From libcxx-commits at lists.llvm.org  Wed Jul  1 00:00:52 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 01 Jul 2026 00:00:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a44bb24.b7ecdeec.190680.5ce0@mx.google.com>


================
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
----------------
Zingam wrote:

I got the opportunity to update the issue.

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Wed Jul  1 00:01:32 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 00:01:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix behavior for
 `get_temporary_buffer` with non-positive size (PR #206871)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206871@github.com>
Message-ID: <6a44bb4c.9f5b7ae6.1381eb.0a1a@mx.google.com>


================
@@ -27,6 +27,11 @@
 #include <memory>
 #include <utility>
 
+#include "test_macros.h"
+
+// This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
----------------
philnik777 wrote:

Can we just use `// ADDITIONAL_COMPILE_FLAGS`?

https://github.com/llvm/llvm-project/pull/206871

From libcxx-commits at lists.llvm.org  Wed Jul  1 00:02:46 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 00:02:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix behavior for
 `get_temporary_buffer` with non-positive size (PR #206871)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206871@github.com>
Message-ID: <6a44bb96.e08f5978.1b3bc6.eba0@mx.google.com>


================
@@ -27,6 +27,11 @@
 #include <memory>
 #include <utility>
 
+#include "test_macros.h"
+
+// This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
----------------
philnik777 wrote:

Or actually push/pop the diagnostic so it's clear which part of the test is affected.

https://github.com/llvm/llvm-project/pull/206871

From libcxx-commits at lists.llvm.org  Wed Jul  1 00:37:38 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 00:37:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix behavior for
 `get_temporary_buffer` with non-positive size (PR #206871)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206871@github.com>
Message-ID: <6a44c3c2.e9629475.ff7f2.509e@mx.google.com>


================
@@ -27,6 +27,11 @@
 #include <memory>
 #include <utility>
 
+#include "test_macros.h"
+
+// This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
----------------
frederick-vs-ja wrote:

> Can we just use `// ADDITIONAL_COMPILE_FLAGS`?

I tried adding `// ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-alloc-size-larger-than`, but that seemed to be wrong, looks like that `// ADDITIONAL_COMPILE_FLAGS(gcc): -Wno-alloc-size-larger-than` should be used.

> Or actually push/pop the diagnostic so it's clear which part of the test is affected.

Thanks! I'll try this approach.

https://github.com/llvm/llvm-project/pull/206871

From libcxx-commits at lists.llvm.org  Wed Jul  1 00:38:27 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Wed, 01 Jul 2026 00:38:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3662
 basic_string::append/assign(NTBS, pos, n) suboptimal (PR #206320)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206320@github.com>
Message-ID: <6a44c3f3.2020f6c2.2129d2.6d80@mx.google.com>


================
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// constexpr basic_string& assign(const charT* s, size_type pos, size_type n);
+
+#include <cassert>
+#include <stdexcept>
+#include <string>
+
+#include "asan_testing.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void
+test(S s, const typename S::value_type* str, typename S::size_type pos, typename S::size_type n, S expected) {
+  typename S::size_type str_len = S::traits_type::length(str);
+  if (pos <= str_len) {
+    s.assign(str, pos, n);
+    LIBCPP_ASSERT(s.__invariants());
+    assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
+  }
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  else if (!TEST_IS_CONSTANT_EVALUATED) {
+    try {
+      s.assign(str, pos, n);
+      assert(false);
+    } catch (std::out_of_range&) {
+      assert(pos > str_len);
+    }
+  }
+#endif
+}
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void test_string() {
+  test(S(), "", 0, 0, S());
+  test(S(), "", 1, 0, S("not happening"));
+  test(S(), "12345", 0, 3, S("123"));
+  test(S(), "12345", 1, 4, S("2345"));
+  test(S(), "12345", 3, 15, S("45"));
+  test(S(), "12345", 5, 15, S(""));
+  test(S(), "12345", 6, 15, S("not happening"));
+  test(S(), "12345678901234567890", 0, 0, S());
+  test(S(), "12345678901234567890", 1, 1, S("2"));
+  test(S(), "12345678901234567890", 2, 3, S("345"));
+  test(S(), "12345678901234567890", 12, 13, S("34567890"));
+  test(S(), "12345678901234567890", 21, 13, S("not happening"));
+
+  test(S("12345"), "", 0, 0, S());
+  test(S("12345"), "12345", 2, 2, S("34"));
+  test(S("12345"), "1234567890", 0, 100, S("1234567890"));
+
+  test(S("12345678901234567890"), "", 0, 0, S());
+  test(S("12345678901234567890"), "12345", 1, 3, S("234"));
+  test(S("12345678901234567890"),
+       "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
+       5,
+       10,
+       S("6789012345"));
+}
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  test_string<std::string>();
+#if TEST_STD_VER >= 11
+  test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, limited_allocator<char, 30>>>();
----------------
c8ef wrote:

Not at all. Could you tell me the numbers that work for MSVC STL for string assign/append respectively? I don't have the dev environment to check it myself.

https://github.com/llvm/llvm-project/pull/206320

From libcxx-commits at lists.llvm.org  Wed Jul  1 00:40:49 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 00:40:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix behavior for
 `get_temporary_buffer` with non-positive size (PR #206871)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206871@github.com>
Message-ID: <6a44c481.450946cd.24bc49.3070@mx.google.com>


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/206871

>From 92bae65367bbcc5caa6601e70e37715d1bec9e5b Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 1 Jul 2026 10:12:44 +0800
Subject: [PATCH 1/3] [libc++] Fix behavior for `get_temporary_buffer` with
 non-positive size

Per C++17 [depr.temporary.buffer]/4, `get_temporary_buffer` is required
to return `{nullptr, 0}` when the size argument is zero or negative.
libc++ used to correctly handle this, but the refactoring in
94e7c0b051c79fd56205f115771980f2e7812306 got this wrong.
---
 libcxx/include/__memory/temporary_buffer.h         |  3 +++
 .../memory/temporary.buffer/overaligned.pass.cpp   | 14 ++++++++++++++
 .../temporary.buffer/temporary_buffer.pass.cpp     | 14 ++++++++++++++
 3 files changed, 31 insertions(+)

diff --git a/libcxx/include/__memory/temporary_buffer.h b/libcxx/include/__memory/temporary_buffer.h
index d18717f52d1cd..3c572e43db641 100644
--- a/libcxx/include/__memory/temporary_buffer.h
+++ b/libcxx/include/__memory/temporary_buffer.h
@@ -26,6 +26,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Tp>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_CFI _LIBCPP_DEPRECATED_IN_CXX17 pair<_Tp*, ptrdiff_t>
 get_temporary_buffer(ptrdiff_t __n) _NOEXCEPT {
+  if (__n <= 0)
+    return pair<_Tp*, ptrdiff_t>();
+
   __unique_temporary_buffer<_Tp> __unique_buf = std::__allocate_unique_temporary_buffer<_Tp>(__n);
   pair<_Tp*, ptrdiff_t> __result(__unique_buf.get(), __unique_buf.get_deleter().__count_);
   __unique_buf.release();
diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
index 3fa9355270866..678e4e3d342fb 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
@@ -38,5 +38,19 @@ int main(int, char**)
     assert(reinterpret_cast<std::uintptr_t>(ip.first) % alignof(A) == 0);
     std::return_temporary_buffer(ip.first);
 
+    // C++17 [depr.temporary.buffer]/4
+    // Returns: If n <= 0 or if no storage could be obtained,
+    // returns a pair P such that P.first is a null pointer value and P.second == 0;
+    {
+      std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(0);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+    {
+      std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(-5);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+
   return 0;
 }
diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
index 39a4767d874e3..50ca6b21ae46c 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
@@ -31,5 +31,19 @@ int main(int, char**)
     assert(ip.second == 5);
     std::return_temporary_buffer(ip.first);
 
+    // C++17 [depr.temporary.buffer]/4
+    // Returns: If n <= 0 or if no storage could be obtained,
+    // returns a pair P such that P.first is a null pointer value and P.second == 0;
+    {
+      std::pair<int*, std::ptrdiff_t> ret = std::get_temporary_buffer<int>(0);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+    {
+      std::pair<int*, std::ptrdiff_t> ret = std::get_temporary_buffer<int>(-5);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+
   return 0;
 }

>From 90d750fbdc0f9a9fcb01c7b5cfa3ed36d99cd163 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 1 Jul 2026 13:35:25 +0800
Subject: [PATCH 2/3] `-Wno-alloc-size-larger-than`

---
 .../utilities/memory/temporary.buffer/overaligned.pass.cpp   | 5 +++++
 .../memory/temporary.buffer/temporary_buffer.pass.cpp        | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
index 678e4e3d342fb..0f119ae90b82e 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
@@ -27,6 +27,11 @@
 #include <memory>
 #include <utility>
 
+#include "test_macros.h"
+
+// This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
+
 struct alignas(32) A {
     int field;
 };
diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
index 50ca6b21ae46c..729cb14b9c5e9 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
@@ -24,6 +24,11 @@
 #include <memory>
 #include <utility>
 
+#include "test_macros.h"
+
+// This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
+
 int main(int, char**)
 {
     std::pair<int*, std::ptrdiff_t> ip = std::get_temporary_buffer<int>(5);

>From 3590c72e1e0cb065b9297b2060c73344869f6f41 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 1 Jul 2026 15:40:16 +0800
Subject: [PATCH 3/3] Locally suppress the warning

---
 .../utilities/memory/temporary.buffer/overaligned.pass.cpp | 7 ++++---
 .../memory/temporary.buffer/temporary_buffer.pass.cpp      | 7 ++++---
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
index 0f119ae90b82e..2b4b6586f5563 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
@@ -29,9 +29,6 @@
 
 #include "test_macros.h"
 
-// This warning is coupled with completeness of control flow analysis which is affected by optimizations.
-TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
-
 struct alignas(32) A {
     int field;
 };
@@ -52,7 +49,11 @@ int main(int, char**)
       assert(ret.second == 0);
     }
     {
+      TEST_DIAGNOSTIC_PUSH
+      // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
       std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(-5);
+      TEST_DIAGNOSTIC_POP
       assert(ret.first == NULL);
       assert(ret.second == 0);
     }
diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
index 729cb14b9c5e9..6935e8a9d83d5 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
@@ -26,9 +26,6 @@
 
 #include "test_macros.h"
 
-// This warning is coupled with completeness of control flow analysis which is affected by optimizations.
-TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
-
 int main(int, char**)
 {
     std::pair<int*, std::ptrdiff_t> ip = std::get_temporary_buffer<int>(5);
@@ -45,7 +42,11 @@ int main(int, char**)
       assert(ret.second == 0);
     }
     {
+      TEST_DIAGNOSTIC_PUSH
+      // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
       std::pair<int*, std::ptrdiff_t> ret = std::get_temporary_buffer<int>(-5);
+      TEST_DIAGNOSTIC_POP
       assert(ret.first == NULL);
       assert(ret.second == 0);
     }


From libcxx-commits at lists.llvm.org  Wed Jul  1 00:41:31 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 00:41:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Granularize `<optional>` (PR
	#206644)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206644@github.com>
Message-ID: <6a44c4ab.bd99a6db.5fc6a.ef62@mx.google.com>


================
@@ -330,1367 +296,13 @@ namespace std {
 _LIBCPP_PUSH_MACROS
 #  include <__undef_macros>
 
-namespace std // purposefully not using versioning namespace
-{
-
-class _LIBCPP_EXPORTED_FROM_ABI bad_optional_access : public exception {
-public:
-  _LIBCPP_HIDE_FROM_ABI bad_optional_access() _NOEXCEPT                                      = default;
-  _LIBCPP_HIDE_FROM_ABI bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
-  _LIBCPP_HIDE_FROM_ABI bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
-  // Get the key function ~bad_optional_access() into the dylib
-  ~bad_optional_access() _NOEXCEPT override;
-  [[__nodiscard__]] const char* what() const _NOEXCEPT override;
-};
-
-} // namespace std
-
 #  if _LIBCPP_STD_VER >= 17
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-[[noreturn]] inline _LIBCPP_HIDE_FROM_ABI void __throw_bad_optional_access() {
-#    if _LIBCPP_HAS_EXCEPTIONS
-  throw bad_optional_access();
-#    else
-  _LIBCPP_VERBOSE_ABORT("bad_optional_access was thrown in -fno-exceptions mode");
-#    endif
-}
-
-struct nullopt_t {
-  struct __secret_tag {
-    explicit __secret_tag() = default;
-  };
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit nullopt_t(__secret_tag, __secret_tag) noexcept {}
-};
-
-inline constexpr nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secret_tag{}};
-
-struct __optional_construct_from_invoke_tag {};
-
-template <class _Tp, bool = is_trivially_destructible<_Tp>::value>
-struct __optional_destruct_base;
-
-template <class _Tp>
-struct __optional_destruct_base<_Tp, false> {
-  typedef _Tp value_type;
-  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
-  union {
-    char __null_state_;
-    remove_cv_t<value_type> __val_;
-  };
-  bool __engaged_;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__optional_destruct_base() {
-    if (__engaged_)
-      __val_.~value_type();
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
-      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Fp, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(
-      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
-      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
-    if (__engaged_) {
-      __val_.~value_type();
-      __engaged_ = false;
-    }
-  }
-};
-
-template <class _Tp>
-struct __optional_destruct_base<_Tp, true> {
-  typedef _Tp value_type;
-  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
-  union {
-    char __null_state_;
-    remove_cv_t<value_type> __val_;
-  };
-  bool __engaged_;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
-      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Fp, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
-      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
-      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
-    if (__engaged_) {
-      __engaged_ = false;
-    }
-  }
-};
-
-template <class _Tp, bool = is_reference<_Tp>::value>
-struct __optional_storage_base : __optional_destruct_base<_Tp> {
-  using __base _LIBCPP_NODEBUG = __optional_destruct_base<_Tp>;
-  using value_type             = _Tp;
-  using __base::__base;
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() & noexcept { return this->__val_; }
-  _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get() const& noexcept { return this->__val_; }
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type&& __get() && noexcept { return std::move(this->__val_); }
-  _LIBCPP_HIDE_FROM_ABI constexpr const value_type&& __get() const&& noexcept { return std::move(this->__val_); }
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_Args&&... __args) {
-    _LIBCPP_ASSERT_INTERNAL(!has_value(), "__construct called for engaged __optional_storage");
-    std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
-    this->__engaged_ = true;
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
-    if (__opt.has_value())
-      __construct(std::forward<_That>(__opt).__get());
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
-    if (this->__engaged_ == __opt.has_value()) {
-      if (this->__engaged_)
-        static_cast<_Tp&>(this->__val_) = std::forward<_That>(__opt).__get();
-    } else {
-      if (this->__engaged_)
-        this->reset();
-      else
-        __construct(std::forward<_That>(__opt).__get());
-    }
-  }
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-  __swap(__optional_storage_base& __rhs) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) {
-    using std::swap;
-    if (this->has_value() == __rhs.has_value()) {
-      if (this->has_value())
-        swap(this->__get(), __rhs.__get());
-    } else {
-      if (this->has_value()) {
-        __rhs.__construct(std::move(this->__get()));
-        this->reset();
-      } else {
-        this->__construct(std::move(__rhs.__get()));
-        __rhs.reset();
-      }
-    }
-  }
-
-  // [optional.observe]
-  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp const> operator->() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return std::move(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return std::move(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return std::move(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return std::move(this->__get());
-  }
-};
-
-template <class _Tp>
-struct __optional_storage_base<_Tp, true> {
-  using value_type                 = _Tp;
-  using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
-  __raw_type* __value_;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
-
-  template <class _Up>
-  _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
-    _Tp& __r(std::forward<_Up>(__val));
-    __value_ = std::addressof(__r);
-  }
-
-  template <class _UArg>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) {
-    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
-                  "Attempted to construct a reference element in optional from a "
-                  "possible temporary");
-    __convert_init_ref_val(std::forward<_UArg>(__uarg));
-  }
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Fp, class... _Args>
-  constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
-    __convert_init_ref_val(std::forward<invoke_result_t<_Fp, _Args...>>(
-        std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)));
-  }
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept { __value_ = nullptr; }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const noexcept { return *__value_; }
-
-  template <class _UArg>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_UArg&& __val) {
-    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
-                  "Attempted to construct a reference element in tuple from a "
-                  "possible temporary");
-    __convert_init_ref_val(std::forward<_UArg>(__val));
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
-    if (__opt.has_value())
-      __construct(std::forward<_That>(__opt).__get());
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
-    if (has_value() == __opt.has_value()) {
-      if (has_value())
-        *__value_ = std::forward<_That>(__opt).__get();
-    } else {
-      if (has_value())
-        reset();
-      else
-        __construct(std::forward<_That>(__opt).__get());
-    }
-  }
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept {
-    std::swap(__value_, __rhs.__value_);
-  }
-
-  // [optional.ref.observe]
-  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() const {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return this->__get();
-  }
-};
-
-template <class _Tp, bool = is_trivially_copy_constructible_v<_Tp>>
-struct __optional_copy_base : __optional_storage_base<_Tp> {
-  using __optional_storage_base<_Tp>::__optional_storage_base;
-};
-
-template <class _Tp>
-struct __optional_copy_base<_Tp, false> : __optional_storage_base<_Tp> {
-  using __optional_storage_base<_Tp>::__optional_storage_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base() = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_base(const __optional_copy_base& __opt) {
-    this->__construct_from(__opt);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base(__optional_copy_base&&)                 = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(const __optional_copy_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(__optional_copy_base&&)      = default;
-};
-
-template <class _Tp, bool = is_trivially_move_constructible_v<_Tp>>
-struct __optional_move_base : __optional_copy_base<_Tp> {
-  using __optional_copy_base<_Tp>::__optional_copy_base;
-};
-
-template <class _Tp>
-struct __optional_move_base<_Tp, false> : __optional_copy_base<_Tp> {
-  using value_type = _Tp;
-  using __optional_copy_base<_Tp>::__optional_copy_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base()                            = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base(const __optional_move_base&) = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
-  __optional_move_base(__optional_move_base&& __opt) noexcept(is_nothrow_move_constructible_v<value_type>) {
-    this->__construct_from(std::move(__opt));
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(const __optional_move_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(__optional_move_base&&)      = default;
-};
-
-template <class _Tp,
-          bool = (is_trivially_destructible_v<_Tp> && is_trivially_copy_constructible_v<_Tp> &&
-                  is_trivially_copy_assignable_v<_Tp>)>
-struct __optional_copy_assign_base : __optional_move_base<_Tp> {
-  using __optional_move_base<_Tp>::__optional_move_base;
-};
-
-template <class _Tp>
-struct __optional_copy_assign_base<_Tp, false> : __optional_move_base<_Tp> {
-  using __optional_move_base<_Tp>::__optional_move_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base()                                   = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(const __optional_copy_assign_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(__optional_copy_assign_base&&)      = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_assign_base&
-  operator=(const __optional_copy_assign_base& __opt) {
-    this->__assign_from(__opt);
-    return *this;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base& operator=(__optional_copy_assign_base&&) = default;
-};
-
-template <class _Tp,
-          bool = (is_trivially_destructible_v<_Tp> && is_trivially_move_constructible_v<_Tp> &&
-                  is_trivially_move_assignable_v<_Tp>)>
-struct __optional_move_assign_base : __optional_copy_assign_base<_Tp> {
-  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
-};
-
-template <class _Tp>
-struct __optional_move_assign_base<_Tp, false> : __optional_copy_assign_base<_Tp> {
-  using value_type = _Tp;
-  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base()                                              = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(const __optional_move_assign_base& __opt)      = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(__optional_move_assign_base&&)                 = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base& operator=(const __optional_move_assign_base&) = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_move_assign_base&
-  operator=(__optional_move_assign_base&& __opt) noexcept(
-      is_nothrow_move_assignable_v<value_type> && is_nothrow_move_constructible_v<value_type>) {
-    this->__assign_from(std::move(__opt));
-    return *this;
-  }
-};
-
-template <class _Tp>
-using __optional_sfinae_ctor_base_t _LIBCPP_NODEBUG =
-    __sfinae_ctor_base< is_copy_constructible<_Tp>::value, is_move_constructible<_Tp>::value >;
-
-template <class _Tp>
-using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
-    __sfinae_assign_base< (is_copy_constructible_v<_Tp> && is_copy_assignable_v<_Tp>),
-                          (is_move_constructible_v<_Tp> && is_move_assignable_v<_Tp>)>;
-
-template <class _Tp>
-class optional;
-
-#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-template <class _Tp>
-constexpr bool ranges::enable_view<optional<_Tp>> = true;
-
-template <class _Tp>
-constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
-
-template <class _Tp>
-constexpr bool ranges::enable_borrowed_range<optional<_Tp&>> = true;
-
-#    endif
-
-#    if _LIBCPP_STD_VER >= 20
-
-template <class _Tp>
-concept __is_derived_from_optional = requires(const _Tp& __t) { []<class _Up>(const optional<_Up>&) {}(__t); };
-
-#    endif // _LIBCPP_STD_VER >= 20
-
-template <class _Tp>
-struct __is_std_optional : false_type {};
-template <class _Tp>
-struct __is_std_optional<optional<_Tp>> : true_type {};
-
-#    if _LIBCPP_STD_VER < 26
-template <class _Tp>
-inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
-#    else
-template <class _Tp>
-inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp> || is_lvalue_reference_v<_Tp>;
-#    endif
-
-template <class _Tp>
-struct __optional_iterator_base : __optional_move_assign_base<_Tp> {
-  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
-};
-
-#    if _LIBCPP_STD_VER >= 26
-template <class _Tp>
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
-};
-
-#      if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-
-template <class _Tp>
-  requires is_object_v<_Tp>
-struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
-private:
-  using __pointer _LIBCPP_NODEBUG       = add_pointer_t<_Tp>;
-  using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const _Tp>;
-
-public:
-  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-  using iterator       = __bounded_iter<__pointer>;
-  using const_iterator = __bounded_iter<__const_pointer>;
-#        else
-  using iterator       = __capacity_aware_iterator<__pointer, optional<_Tp>, 1>;
-  using const_iterator = __capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>;
-#        endif
-
-  // [optional.iterators], iterator support
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
-    auto* __ptr = std::addressof(this->__get());
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
-    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp>, 1>(__ptr);
-#        endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
-    auto* __ptr = std::addressof(this->__get());
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
-    return std::__make_capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>(__ptr);
-#        endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-};
-
-template <class _Tp>
-  requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
-private:
-  using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
-
-public:
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-  using iterator = __bounded_iter<__pointer>;
-#        else
-  using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
-#        endif
-
-  // [optional.ref.iterators], iterator support
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
-    auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
-    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
-#        endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-};
-
-#      endif // _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-#    endif   // _LIBCPP_STD_VER >= 26
-
-template <class _Tp>
-class _LIBCPP_DECLSPEC_EMPTY_BASES optional
-    : public __optional_iterator_base<_Tp>,
-      private __optional_sfinae_ctor_base_t<_Tp>,
-      private __optional_sfinae_assign_base_t<_Tp> {
-  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp>;
-
-public:
-  using value_type = __libcpp_remove_reference_t<_Tp>;
-
-  using __trivially_relocatable _LIBCPP_NODEBUG =
-      conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
-
-private:
-  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
-  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-#    if _LIBCPP_STD_VER >= 26
-  static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
-#    else
-  static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
-#    endif
-  static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
-  static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
-
-  // LWG2756: conditionally explicit conversion from _Up
-  struct _CheckOptionalArgsConstructor {
-    template <class _Up>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
-      return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>;
-    }
-
-    template <class _Up>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
-      return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>;
-    }
-  };
-  template <class _Up>
-  using _CheckOptionalArgsCtor _LIBCPP_NODEBUG =
-      _If< _IsNotSame<__remove_cvref_t<_Up>, in_place_t>::value && _IsNotSame<__remove_cvref_t<_Up>, optional>::value &&
-               (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_optional<__remove_cvref_t<_Up>>::value),
-           _CheckOptionalArgsConstructor,
-           __check_tuple_constructor_fail >;
-  template <class _QualUp>
-  struct _CheckOptionalLikeConstructor {
-    template <class _Up, class _Opt = optional<_Up>>
-    using __check_constructible_from_opt _LIBCPP_NODEBUG =
-        _Or< is_constructible<_Tp, _Opt&>,
-             is_constructible<_Tp, _Opt const&>,
-             is_constructible<_Tp, _Opt&&>,
-             is_constructible<_Tp, _Opt const&&>,
-             is_convertible<_Opt&, _Tp>,
-             is_convertible<_Opt const&, _Tp>,
-             is_convertible<_Opt&&, _Tp>,
-             is_convertible<_Opt const&&, _Tp> >;
-    template <class _Up, class _Opt = optional<_Up>>
-    using __check_assignable_from_opt _LIBCPP_NODEBUG =
-        _Or< is_assignable<_Tp&, _Opt&>,
-             is_assignable<_Tp&, _Opt const&>,
-             is_assignable<_Tp&, _Opt&&>,
-             is_assignable<_Tp&, _Opt const&&> >;
-    template <class _Up, class _QUp = _QualUp>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
-      return is_convertible<_QUp, _Tp>::value &&
-             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
-    }
-    template <class _Up, class _QUp = _QualUp>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
-      return !is_convertible<_QUp, _Tp>::value &&
-             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
-    }
-    template <class _Up, class _QUp = _QualUp>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_assign() {
-      // Construction and assignability of _QUp to _Tp has already been
-      // checked.
-      return !__check_constructible_from_opt<_Up>::value && !__check_assignable_from_opt<_Up>::value;
-    }
-  };
-
-  template <class _Up, class _QualUp>
-  using _CheckOptionalLikeCtor _LIBCPP_NODEBUG =
-      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp> >::value,
-           _CheckOptionalLikeConstructor<_QualUp>,
-           __check_tuple_constructor_fail >;
-  template <class _Up, class _QualUp>
-  using _CheckOptionalLikeAssign _LIBCPP_NODEBUG =
-      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>, is_assignable<_Tp&, _QualUp> >::value,
-           _CheckOptionalLikeConstructor<_QualUp>,
-           __check_tuple_constructor_fail >;
-
-public:
-  _LIBCPP_HIDE_FROM_ABI constexpr optional() noexcept {}
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(const optional&) = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(optional&&)      = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(nullopt_t) noexcept {}
-
-  template <class _InPlaceT,
-            class... _Args,
-            enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, is_constructible<_Tp, _Args...>>::value, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_InPlaceT, _Args&&... __args)
-      : __base(in_place, std::forward<_Args>(__args)...) {}
-
-  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
-      : __base(in_place, __il, std::forward<_Args>(__args)...) {}
-
-  template <class _Up = _Tp, enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
-      : __base(in_place, std::forward<_Up>(__v)) {}
-
-  template <class _Up                                                                        = remove_cv_t<_Tp>,
-            enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) noexcept(
-      is_nothrow_constructible_v<_Tp, _Up>) // strengthened
-      : __base(in_place, std::forward<_Up>(__v)) {}
-
-  // LWG2756: conditionally explicit conversion from const optional<_Up>&
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_implicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) {
-    this->__construct_from(__v);
-  }
-
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_explicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) {
-    this->__construct_from(__v);
-  }
-
-  // LWG2756: conditionally explicit conversion from optional<_Up>&&
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_implicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) {
-    this->__construct_from(std::move(__v));
-  }
-
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_explicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) {
-    this->__construct_from(std::move(__v));
-  }
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Tag,
-            class _Fp,
-            class... _Args,
-            enable_if_t<_IsSame<_Tag, __optional_construct_from_invoke_tag>::value, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
-      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(nullopt_t) noexcept {
-    reset();
-    return *this;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(const optional&) = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(optional&&)      = default;
-
-  // LWG2756
-  template <class _Up        = remove_cv_t<_Tp>,
-            enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>,
-                             _Or<_IsNotSame<__remove_cvref_t<_Up>, _Tp>, _Not<is_scalar<_Tp>>>,
-                             is_constructible<_Tp, _Up>,
-                             is_assignable<_Tp&, _Up>>::value,
-                        int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(_Up&& __v) {
-    if (this->has_value())
-      this->__get() = std::forward<_Up>(__v);
-    else
-      this->__construct(std::forward<_Up>(__v));
-    return *this;
-  }
-
-  // LWG2756
-  template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>::template __enable_assign<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(const optional<_Up>& __v) {
-    this->__assign_from(__v);
-    return *this;
-  }
-
-  // LWG2756
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_assign<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(optional<_Up>&& __v) {
-    this->__assign_from(std::move(__v));
-    return *this;
-  }
-
-  template <class... _Args, enable_if_t<is_constructible_v<_Tp, _Args...>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) {
-    reset();
-    this->__construct(std::forward<_Args>(__args)...);
-    return this->__get();
-  }
-
-  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) {
-    reset();
-    this->__construct(__il, std::forward<_Args>(__args)...);
-    return this->__get();
-  }
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-  swap(optional& __opt) noexcept((is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>)) {
-    this->__swap(__opt);
-  }
-
-  using __base::operator*;
-  using __base::operator->;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return has_value(); }
-
-  using __base::__get;
-  using __base::has_value;
-  using __base::value;
-
-  template <class _Up = remove_cv_t<_Tp>>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
-    static_assert(is_copy_constructible_v<_Tp>, "optional<T>::value_or: T must be copy constructible");
-    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
-    return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v));
-  }
-
-  template <class _Up = remove_cv_t<_Tp>>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
-    static_assert(is_move_constructible_v<_Tp>, "optional<T>::value_or: T must be move constructible");
-    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
-    return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v));
-  }
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
-    using _Up = invoke_result_t<_Func, _Tp&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), value());
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
-    using _Up = invoke_result_t<_Func, const _Tp&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), value());
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
-    using _Up = invoke_result_t<_Func, _Tp&&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(std::move(value())) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), std::move(value()));
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
-    using _Up = invoke_result_t<_Func, const _Tp&&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(std::move(value())) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), std::move(value()));
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & {
-    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
-    return optional<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& {
-    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
-    return optional<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && {
-    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
-    return optional<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& {
-    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
-    return optional<_Up>();
-  }
-
-  template <invocable _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const&
-    requires is_copy_constructible_v<_Tp>
-  {
-    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
-                  "Result of f() should be the same type as this optional");
-    if (*this)
-      return *this;
-    return std::forward<_Func>(__f)();
-  }
-
-  template <invocable _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) &&
-    requires is_move_constructible_v<_Tp>
-  {
-    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
-                  "Result of f() should be the same type as this optional");
-    if (*this)
-      return std::move(*this);
-    return std::forward<_Func>(__f)();
-  }
-#    endif // _LIBCPP_STD_VER >= 23
-
-  using __base::reset;
-};
-
-#    if _LIBCPP_STD_VER >= 26
-template <class _Tp>
-class optional<_Tp&> : public __optional_iterator_base<_Tp&> {
-  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp&>;
-
-  template <class _Up, class _QualUp>
-  static constexpr bool __check_optionalU_ctor =
-      !is_same_v<remove_cv_t<_Tp>, optional<_Up>> && !is_same_v<_Tp&, _Up> && is_constructible_v<_Tp&, _QualUp>;
-  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
-  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-
-public:
-  using value_type = _Tp;
-
-  constexpr optional() noexcept = default;
-  constexpr optional(nullopt_t) noexcept {}
-  constexpr optional(const optional&) noexcept = default;
-
-  template <class _Arg>
-    requires(is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>)
-  constexpr explicit optional(in_place_t, _Arg&& __arg) : __base(in_place, std::forward<_Arg>(__arg)) {}
-
-  template <class _Up>
-    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
-             is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>) optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>)
-      : __base(in_place, std::forward<_Up>(__v)) {}
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up&> && !reference_constructs_from_temporary_v<_Tp&, _Up&>)
-  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
-      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) {
-    this->__construct_from(__rhs);
-  }
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up&> && !reference_constructs_from_temporary_v<_Tp&, const _Up&>)
-  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
-      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) {
-    this->__construct_from(__rhs);
-  }
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
-      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
-    this->__construct_from(std::move(__rhs));
-  }
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up> && !reference_constructs_from_temporary_v<_Tp&, const _Up>)
-  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
-      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) {
-    this->__construct_from(std::move(__rhs));
-  }
-
-  template <class _Tag, class _Fp, class... _Args>
-    requires(is_same_v<_Tag, __optional_construct_from_invoke_tag>)
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
-      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
-
-  // deleted overloads
-
-  template <class _Up>
-    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
-             is_constructible_v<_Tp&, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
-      optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up&> && reference_constructs_from_temporary_v<_Tp&, _Up&>)
-  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
-      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up&> && reference_constructs_from_temporary_v<_Tp&, const _Up&>)
-  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
-      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
-      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up> && reference_constructs_from_temporary_v<_Tp&, const _Up>)
-  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
-      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) = delete;
-
-  constexpr ~optional() = default;
-
-  using __base::__get;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(nullopt_t) noexcept {
-    reset();
-    return *this;
-  }
-
-  constexpr optional& operator=(const optional&) noexcept = default;
-
-  template <class _Up>
-    requires(is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr _Tp& emplace(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
-    this->__construct(std::forward<_Up>(__u));
-
-    return this->__get();
-  }
-
-  constexpr void swap(optional& __rhs) noexcept { this->__swap(__rhs); }
-
-  using __base::operator->;
-  using __base::operator*;
-
-  constexpr explicit operator bool() const noexcept { return has_value(); }
-
-  using __base::has_value;
-  using __base::value;
-
-  template <class _Up = remove_cv_t<_Tp>>
-    requires(!is_array_v<_Tp> && is_object_v<_Tp>)
-  [[nodiscard]] constexpr decay_t<_Tp> value_or(_Up&& __v) const {
-    using _XTp = remove_cv_t<_Tp>;
-    static_assert(is_constructible_v<_XTp, _Tp&>, "optional<T&>::value_or: remove_cv_t<T> must be constructible");
-    static_assert(is_convertible_v<_Up, _XTp>, "optional<T&>::value_or: U must be convertible to remove_cv_t<T>");
-    return this->has_value() ? this->__get() : static_cast<_XTp>(std::forward<_Up>(__v));
-  }
-
-  template <class _Func>
-  [[nodiscard]] constexpr auto and_then(_Func&& __f) const {
-    using _Up = invoke_result_t<_Func, _Tp&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), value());
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] constexpr optional<remove_cv_t<invoke_result_t<_Func, _Tp&>>> transform(_Func&& __f) const {
-    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
-    return optional<_Up>();
-  }
-
-  template <invocable _Func>
-  [[nodiscard]] constexpr optional or_else(_Func&& __f) const {
-    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
-                  "Result of f() should be the same type as this optional");
-    if (*this)
-      return *this;
-    return std::forward<_Func>(__f)();
-  }
-
-  using __base::reset;
-};
-#    endif
-
 template <class _Tp>
 optional(_Tp) -> optional<_Tp>;
 
-// [optional.relops] Relational operators
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (static_cast<bool>(__x) != static_cast<bool>(__y))
-    return false;
-  if (!static_cast<bool>(__x))
-    return true;
-  return *__x == *__y;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (static_cast<bool>(__x) != static_cast<bool>(__y))
-    return true;
-  if (!static_cast<bool>(__x))
-    return false;
-  return *__x != *__y;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__y))
-    return false;
-  if (!static_cast<bool>(__x))
-    return true;
-  return *__x < *__y;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__x))
-    return false;
-  if (!static_cast<bool>(__y))
-    return true;
-  return *__x > *__y;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__x))
-    return true;
-  if (!static_cast<bool>(__y))
-    return false;
-  return *__x <= *__y;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__y))
-    return true;
-  if (!static_cast<bool>(__x))
-    return false;
-  return *__x >= *__y;
-}
-
-#    if _LIBCPP_STD_VER >= 20
-
-template <class _Tp, three_way_comparable_with<_Tp> _Up>
-_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
-operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (__x && __y)
-    return *__x <=> *__y;
-  return __x.has_value() <=> __y.has_value();
-}
-
-#    endif // _LIBCPP_STD_VER >= 20
-
-// [optional.nullops] Comparison with nullopt
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-#    if _LIBCPP_STD_VER <= 17
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>&, nullopt_t) noexcept {
-  return false;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(nullopt_t, const optional<_Tp>&) noexcept {
-  return true;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(nullopt_t, const optional<_Tp>&) noexcept {
-  return false;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>&, nullopt_t) noexcept {
-  return true;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-#    else // _LIBCPP_STD_VER <= 17
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return __x.has_value() <=> false;
-}
-
-#    endif // _LIBCPP_STD_VER <= 17
-
-// [optional.comp.with.t] Comparison with T
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x == __v;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v == *__x;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x != __v;
-  return true;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v != *__x;
-  return true;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x < __v;
-  return true;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v < *__x;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x <= __v;
-  return true;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v <= *__x;
-  return false;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x > __v;
-  return false;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v > *__x;
-  return true;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x >= __v;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v >= *__x;
-  return true;
-}
-
-#    if _LIBCPP_STD_VER >= 20
-
-template <class _Tp, class _Up>
-  requires(!__is_derived_from_optional<_Up>) && three_way_comparable_with<_Tp, _Up>
-_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
-operator<=>(const optional<_Tp>& __x, const _Up& __v) {
-  return __x.has_value() ? *__x <=> __v : strong_ordering::less;
-}
-
-#    endif // _LIBCPP_STD_VER >= 20
-
-template <class _Tp,
-          enable_if_t<(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>)
-#    if _LIBCPP_STD_VER >= 26
-                          || is_reference_v<_Tp>
-#    endif
-                      ,
-                      int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) {
-  __x.swap(__y);
-}
-
 struct __make_optional_barrier_tag {
----------------
philnik777 wrote:

We granularize everything in other places we don't use it from anywhere else. I don't see how this is different. I also don't see why you'd need to provide `optional<T&>` - as long as it's forward-declared you'll get a diagnostic about using an undefined class.

https://github.com/llvm/llvm-project/pull/206644

From libcxx-commits at lists.llvm.org  Wed Jul  1 00:43:48 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 00:43:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3662
 basic_string::append/assign(NTBS, pos, n) suboptimal (PR #206320)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206320@github.com>
Message-ID: <6a44c534.fdc477d6.2b77d8.d844@mx.google.com>


================
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// constexpr basic_string& assign(const charT* s, size_type pos, size_type n);
+
+#include <cassert>
+#include <stdexcept>
+#include <string>
+
+#include "asan_testing.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void
+test(S s, const typename S::value_type* str, typename S::size_type pos, typename S::size_type n, S expected) {
+  typename S::size_type str_len = S::traits_type::length(str);
+  if (pos <= str_len) {
+    s.assign(str, pos, n);
+    LIBCPP_ASSERT(s.__invariants());
+    assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
+  }
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  else if (!TEST_IS_CONSTANT_EVALUATED) {
+    try {
+      s.assign(str, pos, n);
+      assert(false);
+    } catch (std::out_of_range&) {
+      assert(pos > str_len);
+    }
+  }
+#endif
+}
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void test_string() {
+  test(S(), "", 0, 0, S());
+  test(S(), "", 1, 0, S("not happening"));
+  test(S(), "12345", 0, 3, S("123"));
+  test(S(), "12345", 1, 4, S("2345"));
+  test(S(), "12345", 3, 15, S("45"));
+  test(S(), "12345", 5, 15, S(""));
+  test(S(), "12345", 6, 15, S("not happening"));
+  test(S(), "12345678901234567890", 0, 0, S());
+  test(S(), "12345678901234567890", 1, 1, S("2"));
+  test(S(), "12345678901234567890", 2, 3, S("345"));
+  test(S(), "12345678901234567890", 12, 13, S("34567890"));
+  test(S(), "12345678901234567890", 21, 13, S("not happening"));
+
+  test(S("12345"), "", 0, 0, S());
+  test(S("12345"), "12345", 2, 2, S("34"));
+  test(S("12345"), "1234567890", 0, 100, S("1234567890"));
+
+  test(S("12345678901234567890"), "", 0, 0, S());
+  test(S("12345678901234567890"), "12345", 1, 3, S("234"));
+  test(S("12345678901234567890"),
+       "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
+       5,
+       10,
+       S("6789012345"));
+}
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  test_string<std::string>();
+#if TEST_STD_VER >= 11
+  test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, limited_allocator<char, 30>>>();
----------------
frederick-vs-ja wrote:

For `assign` that will be 91 (IIRC) and the current 96 certainly works. For `append` that limit will be 33, as posted in the suggestion.

https://github.com/llvm/llvm-project/pull/206320

From libcxx-commits at lists.llvm.org  Wed Jul  1 00:49:55 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Wed, 01 Jul 2026 00:49:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3662
 basic_string::append/assign(NTBS, pos, n) suboptimal (PR #206320)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206320@github.com>
Message-ID: <6a44c6a3.c56fb102.1c15b6.6367@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/206320

>From f6a0112e8e9878962c62e8291d05e84fbbf622f3 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 28 Jun 2026 17:36:41 +0800
Subject: [PATCH 1/4] [libc++] Implement lwg3662
 basic_string::append/assign(NTBS, pos, n) suboptimal

---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/string                         | 20 ++++
 .../strings/basic.string/nonnull.verify.cpp   |  4 +
 .../string_append/pointer_size_size.pass.cpp  | 93 +++++++++++++++++++
 .../string_assign/pointer_size_size.pass.cpp  | 84 +++++++++++++++++
 5 files changed, 202 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp
 create mode 100644 libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..ffb89cbc8d19c 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -264,7 +264,7 @@
 "`LWG2746 <https://wg21.link/LWG2746>`__","Inconsistency between requirements for ``emplace`` between ``optional`` and ``variant``","2026-03 (Croydon)","","","`#189806 <https://github.com/llvm/llvm-project/issues/189806>`__",""
 "`LWG3504 <https://wg21.link/LWG3504>`__","``condition_variable::wait_for`` is overspecified","2026-03 (Croydon)","","","`#189807 <https://github.com/llvm/llvm-project/issues/189807>`__",""
 "`LWG3599 <https://wg21.link/LWG3599>`__","The ``const`` overload of ``lazy_split_view::begin`` should be constrained by ``const Pattern``","2026-03 (Croydon)","","","`#189808 <https://github.com/llvm/llvm-project/issues/189808>`__",""
-"`LWG3662 <https://wg21.link/LWG3662>`__","``basic_string::append/assign(NTBS, pos, n)`` suboptimal","2026-03 (Croydon)","","","`#189809 <https://github.com/llvm/llvm-project/issues/189809>`__",""
+"`LWG3662 <https://wg21.link/LWG3662>`__","``basic_string::append/assign(NTBS, pos, n)`` suboptimal","2026-03 (Croydon)","|Complete|","23","`#189809 <https://github.com/llvm/llvm-project/issues/189809>`__",""
 "`LWG3777 <https://wg21.link/LWG3777>`__","Common ``cartesian_product_view`` produces an invalid range if the first range is input and one of the ranges is empty","2026-03 (Croydon)","","","`#189810 <https://github.com/llvm/llvm-project/issues/189810>`__",""
 "`LWG3797 <https://wg21.link/LWG3797>`__","``elements_view`` insufficiently constrained","2026-03 (Croydon)","","","`#189811 <https://github.com/llvm/llvm-project/issues/189811>`__",""
 "`LWG3831 <https://wg21.link/LWG3831>`__","Two-digit formatting of negative ``year`` is ambiguous","2026-03 (Croydon)","","","`#189812 <https://github.com/llvm/llvm-project/issues/189812>`__",""
diff --git a/libcxx/include/string b/libcxx/include/string
index e5bc854cd34f3..6516d26e288a9 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -201,6 +201,7 @@ public:
         basic_string& append(const T& t, size_type pos, size_type n=npos);                      // C++17, constexpr since C++20
     basic_string& append(const value_type* s, size_type n);                                     // constexpr since C++20
     basic_string& append(const value_type* s);                                                  // constexpr since C++20
+    basic_string& append(const value_type* s, size_type pos, size_type n);                      // constexpr since C++20
     basic_string& append(size_type n, value_type c);                                            // constexpr since C++20
     template<class InputIterator>
         basic_string& append(InputIterator first, InputIterator last);                          // constexpr since C++20
@@ -224,6 +225,7 @@ public:
         basic_string& assign(const T& t, size_type pos, size_type n=npos);                      // C++17, constexpr since C++20
     basic_string& assign(const value_type* s, size_type n);                                     // constexpr since C++20
     basic_string& assign(const value_type* s);                                                  // constexpr since C++20
+    basic_string& assign(const value_type* s, size_type pos, size_type n);                      // constexpr since C++20
     basic_string& assign(size_type n, value_type c);                                            // constexpr since C++20
     template<class InputIterator>
         basic_string& assign(InputIterator first, InputIterator last);                          // constexpr since C++20
@@ -1342,6 +1344,8 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s, size_type __n)
       _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero");
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s, size_type __pos, size_type __n)
+      _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero");
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(size_type __n, value_type __c);
 
   template <class _InputIterator, __enable_if_t<__has_exactly_input_iterator_category<_InputIterator>::value, int> = 0>
@@ -1468,6 +1472,8 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(const value_type* __s, size_type __n)
       _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero");
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(const value_type* __s, size_type __pos, size_type __n)
+      _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero");
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(size_type __n, value_type __c);
 
   template <class _InputIterator, __enable_if_t<__has_exactly_input_iterator_category<_InputIterator>::value, int> = 0>
@@ -2757,6 +2763,13 @@ basic_string<_CharT, _Traits, _Allocator>::assign(const value_type* __s, size_ty
   return (__builtin_constant_p(__n) && __fits_in_sso(__n)) ? __assign_short(__s, __n) : __assign_external(__s, __n);
 }
 
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string<_CharT, _Traits, _Allocator>&
+basic_string<_CharT, _Traits, _Allocator>::assign(const value_type* __s, size_type __pos, size_type __n) {
+  _LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string::assign received nullptr");
+  return assign(__self_view(__s).substr(__pos, __n));
+}
+
 template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string<_CharT, _Traits, _Allocator>&
 basic_string<_CharT, _Traits, _Allocator>::assign(size_type __n, value_type __c) {
@@ -3024,6 +3037,13 @@ basic_string<_CharT, _Traits, _Allocator>::append(const value_type* __s) {
   return append(__s, traits_type::length(__s));
 }
 
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string<_CharT, _Traits, _Allocator>&
+basic_string<_CharT, _Traits, _Allocator>::append(const value_type* __s, size_type __pos, size_type __n) {
+  _LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string::append received nullptr");
+  return append(__self_view(__s).substr(__pos, __n));
+}
+
 // insert
 
 template <class _CharT, class _Traits, class _Allocator>
diff --git a/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp b/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp
index d51ce1fcf2ba6..bdb3189b7fd35 100644
--- a/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp
@@ -25,8 +25,12 @@ void func() {
   std::string str2(np, std::allocator<char>{}); // expected-warning {{null passed}}
   str2 = np;                                    // expected-warning {{null passed}}
   str2 += np;                                   // expected-warning {{null passed}}
+  str2.assign(np, 1);                           // expected-warning {{null passed}}
   str2.assign(np);                              // expected-warning {{null passed}}
+  str2.assign(np, 0, 1);                        // expected-warning {{null passed}}
+  str2.append(np, 1);                           // expected-warning {{null passed}}
   str2.append(np);                              // expected-warning {{null passed}}
+  str2.append(np, 0, 1);                        // expected-warning {{null passed}}
   str2.insert(0, np);                           // expected-warning {{null passed}}
   str2.find(np);                                // expected-warning {{null passed}}
   str2.rfind(np);                               // expected-warning {{null passed}}
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp
new file mode 100644
index 0000000000000..d3d082dd285ca
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// constexpr basic_string& append(const charT* s, size_type pos, size_type n);
+
+#include <string>
+#include <stdexcept>
+#include <cassert>
+
+#include "test_macros.h"
+#include "min_allocator.h"
+#include "asan_testing.h"
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void
+test(S s, const typename S::value_type* str, typename S::size_type pos, typename S::size_type n, S expected) {
+  typename S::size_type str_len = S::traits_type::length(str);
+  if (pos <= str_len) {
+    s.append(str, pos, n);
+    LIBCPP_ASSERT(s.__invariants());
+    assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
+  }
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  else if (!TEST_IS_CONSTANT_EVALUATED) {
+    try {
+      s.append(str, pos, n);
+      assert(false);
+    } catch (std::out_of_range&) {
+      assert(pos > str_len);
+    }
+  }
+#endif
+}
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void test_string() {
+  test(S(), "", 0, 0, S());
+  test(S(), "", 1, 0, S("not happening"));
+  test(S(), "12345", 0, 3, S("123"));
+  test(S(), "12345", 1, 4, S("2345"));
+  test(S(), "12345", 3, 15, S("45"));
+  test(S(), "12345", 5, 15, S(""));
+  test(S(), "12345", 6, 15, S("not happening"));
+  test(S(), "12345678901234567890", 0, 0, S());
+  test(S(), "12345678901234567890", 1, 1, S("2"));
+  test(S(), "12345678901234567890", 2, 3, S("345"));
+  test(S(), "12345678901234567890", 12, 13, S("34567890"));
+  test(S(), "12345678901234567890", 21, 13, S("not happening"));
+
+  test(S("12345"), "", 0, 0, S("12345"));
+  test(S("12345"), "12345", 2, 2, S("1234534"));
+  test(S("12345"), "1234567890", 0, 100, S("123451234567890"));
+
+  test(S("12345678901234567890"), "", 0, 0, S("12345678901234567890"));
+  test(S("12345678901234567890"), "12345", 1, 3, S("12345678901234567890234"));
+  test(S("12345678901234567890"), "12345678901234567890", 5, 10, S("123456789012345678906789012345"));
+
+  // Starting from long string (no SSO)
+  test(S("123456789012345678901234567890"), "", 0, 0, S("123456789012345678901234567890"));
+  test(S("123456789012345678901234567890"), "12345", 1, 3, S("123456789012345678901234567890234"));
+  test(S("123456789012345678901234567890"),
+       "12345678901234567890",
+       5,
+       10,
+       S("1234567890123456789012345678906789012345"));
+}
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  test_string<std::string>();
+#if TEST_STD_VER >= 11
+  test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
+#endif
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER > 17
+  static_assert(test());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
new file mode 100644
index 0000000000000..9f1dbe8df07d1
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// constexpr basic_string& assign(const charT* s, size_type pos, size_type n);
+
+#include <string>
+#include <stdexcept>
+#include <cassert>
+
+#include "test_macros.h"
+#include "min_allocator.h"
+#include "asan_testing.h"
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void
+test(S s, const typename S::value_type* str, typename S::size_type pos, typename S::size_type n, S expected) {
+  typename S::size_type str_len = S::traits_type::length(str);
+  if (pos <= str_len) {
+    s.assign(str, pos, n);
+    LIBCPP_ASSERT(s.__invariants());
+    assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
+  }
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  else if (!TEST_IS_CONSTANT_EVALUATED) {
+    try {
+      s.assign(str, pos, n);
+      assert(false);
+    } catch (std::out_of_range&) {
+      assert(pos > str_len);
+    }
+  }
+#endif
+}
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void test_string() {
+  test(S(), "", 0, 0, S());
+  test(S(), "", 1, 0, S("not happening"));
+  test(S(), "12345", 0, 3, S("123"));
+  test(S(), "12345", 1, 4, S("2345"));
+  test(S(), "12345", 3, 15, S("45"));
+  test(S(), "12345", 5, 15, S(""));
+  test(S(), "12345", 6, 15, S("not happening"));
+  test(S(), "12345678901234567890", 0, 0, S());
+  test(S(), "12345678901234567890", 1, 1, S("2"));
+  test(S(), "12345678901234567890", 2, 3, S("345"));
+  test(S(), "12345678901234567890", 12, 13, S("34567890"));
+  test(S(), "12345678901234567890", 21, 13, S("not happening"));
+
+  test(S("12345"), "", 0, 0, S());
+  test(S("12345"), "12345", 2, 2, S("34"));
+  test(S("12345"), "1234567890", 0, 100, S("1234567890"));
+
+  test(S("12345678901234567890"), "", 0, 0, S());
+  test(S("12345678901234567890"), "12345", 1, 3, S("234"));
+  test(S("12345678901234567890"), "12345678901234567890", 5, 10, S("6789012345"));
+}
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  test_string<std::string>();
+#if TEST_STD_VER >= 11
+  test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
+#endif
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER > 17
+  static_assert(test());
+#endif
+
+  return 0;
+}

>From 1029f520172bc4d57c0c72090a45de8a980427f5 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 29 Jun 2026 21:47:59 +0800
Subject: [PATCH 2/4] limited allocator test

---
 .../string_append/pointer_size_size.pass.cpp     | 12 +++++++-----
 .../string_assign/pointer_size_size.pass.cpp     | 16 +++++++++++-----
 2 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp
index d3d082dd285ca..ad74eb3541d01 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp
@@ -10,13 +10,14 @@
 
 // constexpr basic_string& append(const charT* s, size_type pos, size_type n);
 
-#include <string>
-#include <stdexcept>
 #include <cassert>
+#include <stdexcept>
+#include <string>
 
-#include "test_macros.h"
-#include "min_allocator.h"
 #include "asan_testing.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void
@@ -67,7 +68,7 @@ TEST_CONSTEXPR_CXX20 void test_string() {
   test(S("123456789012345678901234567890"), "", 0, 0, S("123456789012345678901234567890"));
   test(S("123456789012345678901234567890"), "12345", 1, 3, S("123456789012345678901234567890234"));
   test(S("123456789012345678901234567890"),
-       "12345678901234567890",
+       "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
        5,
        10,
        S("1234567890123456789012345678906789012345"));
@@ -78,6 +79,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
   test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, limited_allocator<char, 96>>>();
 #endif
 
   return true;
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
index 9f1dbe8df07d1..4c511c171f23c 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
@@ -10,13 +10,14 @@
 
 // constexpr basic_string& assign(const charT* s, size_type pos, size_type n);
 
-#include <string>
-#include <stdexcept>
 #include <cassert>
+#include <stdexcept>
+#include <string>
 
-#include "test_macros.h"
-#include "min_allocator.h"
 #include "asan_testing.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
 
 template <class S>
 TEST_CONSTEXPR_CXX20 void
@@ -61,7 +62,11 @@ TEST_CONSTEXPR_CXX20 void test_string() {
 
   test(S("12345678901234567890"), "", 0, 0, S());
   test(S("12345678901234567890"), "12345", 1, 3, S("234"));
-  test(S("12345678901234567890"), "12345678901234567890", 5, 10, S("6789012345"));
+  test(S("12345678901234567890"),
+       "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
+       5,
+       10,
+       S("6789012345"));
 }
 
 TEST_CONSTEXPR_CXX20 bool test() {
@@ -69,6 +74,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
   test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, limited_allocator<char, 30>>>();
 #endif
 
   return true;

>From 6f99d2e2e5439d83bdb214bbda7d522ec43396de Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 29 Jun 2026 21:49:11 +0800
Subject: [PATCH 3/4] >=

---
 .../string.modifiers/string_append/pointer_size_size.pass.cpp   | 2 +-
 .../string.modifiers/string_assign/pointer_size_size.pass.cpp   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp
index ad74eb3541d01..e70a11fced589 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp
@@ -87,7 +87,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
 
 int main(int, char**) {
   test();
-#if TEST_STD_VER > 17
+#if TEST_STD_VER >= 20
   static_assert(test());
 #endif
 
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
index 4c511c171f23c..912861f69b7f8 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
@@ -82,7 +82,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
 
 int main(int, char**) {
   test();
-#if TEST_STD_VER > 17
+#if TEST_STD_VER >= 20
   static_assert(test());
 #endif
 

>From fce96fa664e8dbcc09baa48d7d360958bcbc1eb7 Mon Sep 17 00:00:00 2001
From: Connector Switch <c8ef at outlook.com>
Date: Wed, 1 Jul 2026 15:49:44 +0800
Subject: [PATCH 4/4] Update
 libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../string.modifiers/string_assign/pointer_size_size.pass.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
index 912861f69b7f8..a6ba75653d1a4 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
@@ -74,7 +74,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
 #if TEST_STD_VER >= 11
   test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
   test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
-  test_string<std::basic_string<char, std::char_traits<char>, limited_allocator<char, 30>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, limited_allocator<char, 33>>>();
 #endif
 
   return true;


From libcxx-commits at lists.llvm.org  Wed Jul  1 00:52:13 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 00:52:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3662
 basic_string::append/assign(NTBS, pos, n) suboptimal (PR #206320)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206320@github.com>
Message-ID: <6a44c72d.faaf2ab7.e6ce6.66a6@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.

LGTM with CI becoming green enough (except for failures on FreeBSD which are being fixed).

https://github.com/llvm/llvm-project/pull/206320

From libcxx-commits at lists.llvm.org  Wed Jul  1 00:55:06 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 00:55:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix behavior for
 `get_temporary_buffer` with non-positive size (PR #206871)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206871@github.com>
Message-ID: <6a44c7da.27b3b003.9dfd8.0443@mx.google.com>


================
@@ -38,5 +40,23 @@ int main(int, char**)
     assert(reinterpret_cast<std::uintptr_t>(ip.first) % alignof(A) == 0);
     std::return_temporary_buffer(ip.first);
 
+    // C++17 [depr.temporary.buffer]/4
+    // Returns: If n <= 0 or if no storage could be obtained,
+    // returns a pair P such that P.first is a null pointer value and P.second == 0;
+    {
+      std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(0);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+    {
+      TEST_DIAGNOSTIC_PUSH
+      // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
----------------
philnik777 wrote:

```suggestion
      TEST_GCC_DIAGNOSTIC_IGNORED("-Walloc-size-larger-than")
```
I don't know whether the `no-` version also works, but this is definitely the convention in libc++.

https://github.com/llvm/llvm-project/pull/206871

From libcxx-commits at lists.llvm.org  Wed Jul  1 00:55:07 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 00:55:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix behavior for
 `get_temporary_buffer` with non-positive size (PR #206871)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206871@github.com>
Message-ID: <6a44c7db.bf38ae28.12214b.b720@mx.google.com>


================
@@ -38,5 +40,23 @@ int main(int, char**)
     assert(reinterpret_cast<std::uintptr_t>(ip.first) % alignof(A) == 0);
     std::return_temporary_buffer(ip.first);
 
+    // C++17 [depr.temporary.buffer]/4
+    // Returns: If n <= 0 or if no storage could be obtained,
+    // returns a pair P such that P.first is a null pointer value and P.second == 0;
+    {
+      std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(0);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+    {
+      TEST_DIAGNOSTIC_PUSH
+      // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
+      std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(-5);
----------------
philnik777 wrote:

Maybe also test `numeric_limits<ptrdiff_t>::min()`?

https://github.com/llvm/llvm-project/pull/206871

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:04:04 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 01:04:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix behavior for
 `get_temporary_buffer` with non-positive size (PR #206871)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206871@github.com>
Message-ID: <6a44c9f4.3157459c.1bf8d3.50e8@mx.google.com>


================
@@ -38,5 +40,23 @@ int main(int, char**)
     assert(reinterpret_cast<std::uintptr_t>(ip.first) % alignof(A) == 0);
     std::return_temporary_buffer(ip.first);
 
+    // C++17 [depr.temporary.buffer]/4
+    // Returns: If n <= 0 or if no storage could be obtained,
+    // returns a pair P such that P.first is a null pointer value and P.second == 0;
+    {
+      std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(0);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+    {
+      TEST_DIAGNOSTIC_PUSH
+      // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
----------------
frederick-vs-ja wrote:

Simple `-Walloc-size-larger-than` (without `=`) doesn't work. `-Walloc-size-larger-than=` (with no argument) and `-Walloc-size-larger-than=42` (where `42` can be replaced with any valid number) work. All working forms seem a bit weird...

https://github.com/llvm/llvm-project/pull/206871

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:05:52 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 01:05:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++] Optimize fstream::read"
	(PR #206453)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206453@github.com>
Message-ID: <6a44ca60.a53a8e96.2789ec.1a37@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/206453

>From 011112d4dc7d4cc065cb3e171de5bf02fb35fde9 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 29 Jun 2026 12:22:36 +0200
Subject: [PATCH] Reapply "[libc++] Optimize fstream::read"

This was reverted due to causing crashes if an ifstream wasn't opened.
This patch addresses this issue by simply checking whether the `__file_`
handle is null, and if it is simply fall back to the generic
implementation.

Fixes #168628
Fixes #205845

This reverts commit 347512ff38748ac6ebfacbfda172edb5cf1edbe2.
---
 libcxx/docs/ReleaseNotes/23.rst               |  2 ++
 libcxx/include/fstream                        | 19 ++++++++++++++++
 .../{ofstream.bench.cpp => fstream.bench.cpp} | 22 +++++++++++++++++--
 .../filebuf/traits_mismatch.verify.cpp        |  2 +-
 .../fstreams/traits_mismatch.verify.cpp       |  2 +-
 .../fstreams/ifstream.members/xsgetn.pass.cpp | 19 ++++++++++++++++
 6 files changed, 62 insertions(+), 4 deletions(-)
 rename libcxx/test/benchmarks/streams/{ofstream.bench.cpp => fstream.bench.cpp} (52%)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 37aa972d22ea3..1852351cedc4e 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -69,6 +69,8 @@ Improvements and New Features
   ``"std::visit: variant is valueless"``, ``"std::get: variant is valueless"``, or
   ``"std::get: wrong alternative for variant"``. The standard only requires ``what()`` to return an
   unspecified non-null string, so user code that does not match on the exact message remains correct.
+- ``ifstream::read`` has been optimized to pass through large reads to system calls directly instead of copying them in
+  chunks.
 
 Deprecations and Removals
 -------------------------
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 0c34c9149de0d..7b84bf6609086 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -309,6 +309,25 @@ protected:
     return basic_streambuf<_CharT, _Traits>::xsputn(__str, __len);
   }
 
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL streamsize xsgetn(char_type* __str, streamsize __len) override {
+    if (__file_ && __always_noconv_) {
+      const streamsize __n = std::min(this->egptr() - this->gptr(), __len);
+      if (__n != 0) {
+        traits_type::copy(__str, this->gptr(), __n);
+        this->__gbump_ptrdiff(__n);
+      }
+      const streamsize __remainder    = __len - __n;
+      const streamsize __buffer_space = this->egptr() - this->eback();
+
+      if (__remainder >= __buffer_space)
+        return std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
+      else if (__remainder > 0)
+        return basic_streambuf<_CharT, _Traits>::xsgetn(__str + __n, __remainder) + __n;
+      return __n;
+    }
+    return basic_streambuf<_CharT, _Traits>::xsgetn(__str, __len);
+  }
+
 private:
   char* __extbuf_;
   const char* __extbufnext_;
diff --git a/libcxx/test/benchmarks/streams/ofstream.bench.cpp b/libcxx/test/benchmarks/streams/fstream.bench.cpp
similarity index 52%
rename from libcxx/test/benchmarks/streams/ofstream.bench.cpp
rename to libcxx/test/benchmarks/streams/fstream.bench.cpp
index 80827d59cdf01..2e16ae58749e6 100644
--- a/libcxx/test/benchmarks/streams/ofstream.bench.cpp
+++ b/libcxx/test/benchmarks/streams/fstream.bench.cpp
@@ -13,7 +13,7 @@
 
 #include <benchmark/benchmark.h>
 
-static void bm_write(benchmark::State& state) {
+static void bm_ofstream_write(benchmark::State& state) {
   std::vector<char> buffer;
   buffer.resize(16384);
 
@@ -22,6 +22,24 @@ static void bm_write(benchmark::State& state) {
   for (auto _ : state)
     stream.write(buffer.data(), buffer.size());
 }
-BENCHMARK(bm_write)->Name("std::ofstream::write(char*, size)");
+BENCHMARK(bm_ofstream_write)->Name("std::ofstream::write(char*, size)");
+
+static void bm_ifstream_read(benchmark::State& state) {
+  std::vector<char> buffer;
+  buffer.resize(16384);
+
+  std::ofstream gen_testfile("testfile");
+  gen_testfile.write(buffer.data(), buffer.size());
+
+  std::ifstream stream("testfile");
+  assert(stream);
+
+  for (auto _ : state) {
+    stream.read(buffer.data(), buffer.size());
+    benchmark::DoNotOptimize(buffer);
+    stream.seekg(0);
+  }
+}
+BENCHMARK(bm_ifstream_read)->Name("std::ifstream::read(char*, size)");
 
 BENCHMARK_MAIN();
diff --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
index 283adbc057d1e..30e7b66d42325 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
@@ -19,4 +19,4 @@
 
 std::basic_filebuf<char, std::char_traits<wchar_t> > f;
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
-// expected-error@*:* 10 {{only virtual member functions can be marked 'override'}}
+// expected-error@*:* 11 {{only virtual member functions can be marked 'override'}}
diff --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
index ba6f3c31d3e34..daafb36f9151a 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
@@ -21,7 +21,7 @@ std::basic_fstream<char, std::char_traits<wchar_t> > f;
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
 
-// expected-error@*:* 12 {{only virtual member functions can be marked 'override'}}
+// expected-error@*:* 13 {{only virtual member functions can be marked 'override'}}
 
 // FIXME: As of commit r324062 Clang incorrectly generates a diagnostic about mismatching
 // exception specifications for types which are already invalid for one reason or another.
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
index de17cd4d8e9d3..17615a4812d7d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
@@ -68,6 +68,25 @@ int main(int, char**) {
     test_buffer.resize(24);
     assert(std::string(test_buffer.data(), 24) == "to test buffer behaviour");
   }
+  { // Ensure that read fails gracefully with an unopened ifstream
+    // See https://llvm.org/PR168628
+    char buf[10];
+    std::ifstream ifs;
+
+    assert(!ifs.is_open());
+    assert(!ifs.bad());
+    assert(!ifs.fail());
+    assert(!ifs.eof());
+    assert(ifs.good());
+
+    ifs.read(buf, sizeof(buf));
+
+    assert(!ifs.is_open());
+    assert(!ifs.bad());
+    assert(ifs.fail());
+    assert(ifs.eof());
+    assert(!ifs.good());
+  }
 
   return 0;
 }


From libcxx-commits at lists.llvm.org  Wed Jul  1 01:06:49 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 01:06:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a44ca99.e9d484b9.34b6c.a52e@mx.google.com>


https://github.com/philnik777 ready_for_review https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:07:34 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 01:07:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a44cac6.d5797644.130832.62d4@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

Instead of updating the status array every time `operator()` is called, we can instead update it once every time the entire array has been read. That allows the compile to vectorize the updating code, improving performance.

Apple M4:
```
Benchmark                         old    new    Difference    % Difference
---------------------------  --------  -----  ------------  --------------
std::mt19937::operator()         1.39   0.65         -0.74         -53.21%
std::mt19937_64::operator()      1.46   1.00         -0.46         -31.72%
```

Fixes #<!-- -->197221

---

Patch is 51.61 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/206423.diff


5 Files Affected:

- (modified) libcxx/include/__configuration/abi.h (+1) 
- (modified) libcxx/include/__random/mersenne_twister_engine.h (+36-7) 
- (modified) libcxx/test/benchmarks/random.bench.cpp (+11) 
- (modified) libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp (+160-29) 
- (modified) libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp (+261-128) 


``````````diff
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 51c82d99eec30..7a4c479fd1636 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -77,6 +77,7 @@
 #  define _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION
 #  define _LIBCPP_ABI_TRIVIALLY_COPYABLE_BIT_ITERATOR
 #  define _LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE
+#  define _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
 
 #elif _LIBCPP_ABI_VERSION == 1
 // Feature macros for disabling pre ABI v1 features. All of these options
diff --git a/libcxx/include/__random/mersenne_twister_engine.h b/libcxx/include/__random/mersenne_twister_engine.h
index ce82b3387dbd2..f1d92a1ede932 100644
--- a/libcxx/include/__random/mersenne_twister_engine.h
+++ b/libcxx/include/__random/mersenne_twister_engine.h
@@ -181,6 +181,9 @@ class mersenne_twister_engine {
     for (size_t __i = 1; __i < __n; ++__i)
       __x_[__i] = (__f * (__x_[__i - 1] ^ __rshift<__w - 2>(__x_[__i - 1])) + __i) & _Max;
     __i_ = 0;
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    __update_all_states();
+#endif
   }
   template <class _Sseq, __enable_if_t<__is_seed_sequence_v<_Sseq, mersenne_twister_engine>, int> = 0>
   _LIBCPP_HIDE_FROM_ABI void seed(_Sseq& __q) {
@@ -203,17 +206,43 @@ class mersenne_twister_engine {
           return;
       __x_[0] = result_type(1) << (__w - 1);
     }
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    __update_all_states();
+#endif
+  }
+
+  void __update_state(size_t __i, size_t __k) {
+    const size_t __j         = (__i + 1) % __n;
+    const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
+    const result_type __yp   = (__x_[__i] & ~__mask) | (__x_[__j] & __mask);
+    __x_[__i]                = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
+  }
+
+  void __update_state(size_t __i) { __update_state(__i, (__i + __m) % __n); }
+
+  void __update_all_states() {
+    size_t __i = 0;
+    // This is split into two loops to help the compiler vectorize the code
+    for (; __i != (__n - __m); ++__i)
+      __update_state(__i);
+    for (size_t __j = 0; __i != __n; ++__i, ++__j)
+      __update_state(__i, __j);
   }
 
   // generating functions
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI result_type operator()() {
-    const size_t __j         = (__i_ + 1) % __n;
-    const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
-    const result_type __yp   = (__x_[__i_] & ~__mask) | (__x_[__j] & __mask);
-    const size_t __k         = (__i_ + __m) % __n;
-    __x_[__i_]               = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
-    result_type __z          = __x_[__i_] ^ (__rshift<__u>(__x_[__i_]) & __d);
-    __i_                     = __j;
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    result_type __val = __x_[__i_];
+    if (++__i_ == __n) [[__unlikely__]] {
+      __update_all_states();
+      __i_ = 0;
+    }
+#else
+    __update_state(__i_);
+    result_type __val = __x_[__i_];
+    __i_         = (__i_ + 1) % __n;
+#endif
+    result_type __z = __val ^ (__rshift<__u>(__val) & __d);
     __z ^= __lshift<__s>(__z) & __b;
     __z ^= __lshift<__t>(__z) & __c;
     return __z ^ __rshift<__l>(__z);
diff --git a/libcxx/test/benchmarks/random.bench.cpp b/libcxx/test/benchmarks/random.bench.cpp
index e6af4c3e26eaf..3f73504949f62 100644
--- a/libcxx/test/benchmarks/random.bench.cpp
+++ b/libcxx/test/benchmarks/random.bench.cpp
@@ -34,4 +34,15 @@ static void BM_SeedSeq_Generate(benchmark::State& state) {
 }
 BENCHMARK(BM_SeedSeq_Generate)->Ranges({{1, MAX_SEED_LEN}, {1, MAX_BUFFER_LEN}});
 
+template <class Engine>
+static void BM_engine(benchmark::State& state) {
+  Engine engine;
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(engine());
+  }
+}
+BENCHMARK(BM_engine<std::mt19937_64>)->Name("std::mt19937_64::operator()");
+BENCHMARK(BM_engine<std::mt19937>)->Name("std::mt19937::operator()");
+
 BENCHMARK_MAIN();
diff --git a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
index 02df0345525cb..b261f37450a81 100644
--- a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
@@ -26,23 +26,21 @@
 
 #include "test_macros.h"
 #if TEST_STD_VER >= 11
-#include "make_implicit.h"
-#include "test_convertible.h"
+#  include "make_implicit.h"
+#  include "test_convertible.h"
 #endif
 
 template <class T>
-std::string
-to_string(T const &e)
-{
-    std::ostringstream os;
-    os << e;
-    return os.str();
+std::string to_string(T const& e) {
+  std::ostringstream os;
+  os << e;
+  return os.str();
 }
 
-void
-test1()
-{
-    const char* a = "0 1 1812433255 1900727105 1208447044 2481403966 4042607538 337614300 "
+void test1() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    "0 1 1812433255 1900727105 1208447044 2481403966 4042607538 337614300 "
     "3232553940 1018809052 3202401494 1775180719 3192392114 594215549 184016991 "
     "829906058 610491522 3879932251 3139825610 297902587 4075895579 2943625357 "
     "3530655617 1423771745 2135928312 2891506774 1066338622 135451537 933040465 "
@@ -137,15 +135,82 @@ test1()
     "2361884409 2860936803 683833250 3291277128 1686857206 1112632275 "
     "1200680507 3342928196 2677058150 939442136 3407104669 2906783932 "
     "3668048733 2030009470 1910839172 1234925283 3575831445 123595418 "
-    "2362440495 3048484911 1796872496";
-    std::mt19937 e1(0);
-    assert(to_string(e1) == a);
+    "2362440495 3048484911 1796872496"
+#else
+      "2443250962 1093594115 1878467924 2709361018 1101979660 3904844661 676747479 2085143622 1056793272 3812477442 "
+      "2168787041 275552121 2696932952 3432054210 1657102335 3518946594 962584079 1051271004 3806145045 1414436097 "
+      "2032348584 1661738718 1116708477 2562755208 3176189976 696824676 2399811678 3992505346 569184356 2626558620 "
+      "136797809 4273176064 296167901 3430730584 331909803 1908676996 1950065095 604298543 3615988338 1570232852 "
+      "1028209748 1511467721 2411887154 4210753555 3096762720 423429618 659966766 2937509307 2222847265 378636552 "
+      "1142109618 2509241601 1521729757 888533219 250885260 2455816244 4046047811 1947467789 1395351953 2388948566 "
+      "934627940 194642258 1429256273 2139959677 1543740405 1569613451 4061840539 2075690423 824532376 844152077 "
+      "3218002536 897315311 823414659 1007534698 3139313867 2332794462 3675156583 722594429 3715123011 2656840676 "
+      "2373277715 1516144243 1538161999 1399719297 1448820876 3180842436 2020192709 3279498509 1048107540 3094290376 "
+      "3706920703 3810051683 4003871775 1069557309 151535004 667695756 861767313 1643171871 3649507728 3785763588 "
+      "737899497 2332908276 4082123854 1584539673 3929877280 1046086041 1304062606 2311324008 1190424879 538647603 "
+      "1728806718 148948582 659721465 2351006009 185647941 1825934330 2126619619 2230539295 3878421575 965937340 "
+      "2835563987 419858879 1527311104 2042333483 2745134265 3540517373 1100062165 2308613697 2690739856 2048293545 "
+      "2742876486 2408502115 53398423 3089771461 1398529613 2132477261 504134301 1535108421 1968507173 3412472770 "
+      "4048517196 3509143071 857063211 1429875570 4120479871 2786574360 2470799871 1644104875 2326863162 2033900387 "
+      "274888909 2629973384 1154405937 1839988197 2163830694 268733679 2928953965 387153042 3790900490 3202349717 "
+      "3799209681 2524871464 3801447311 3543081470 1801633977 3870370950 1581678156 358283485 4133385582 4052311650 "
+      "249498956 1712028623 3618329357 3395106946 2704820515 26653208 2437509555 4192186371 3639946838 2615917799 "
+      "294624699 463886267 2477203673 2514051526 4162882381 2937629358 3180524454 1388392394 2637132139 1999482082 "
+      "1870663339 2852309250 3489430700 1116063402 1673637188 1941955790 4081919937 2322920953 3500518887 2722575905 "
+      "405031829 3728964889 3464559963 665516555 1626835870 2523729468 238289561 497556937 2581429740 3456619478 "
+      "2601389837 820426192 268141532 2654829421 349692193 2426957585 1465734488 85095426 1080895536 2823366942 "
+      "379322515 2938986739 1561328720 2940081697 607513620 2724557130 2841377034 14123440 1085631349 1977859413 "
+      "1325586902 1690398943 2516154740 4144373551 555899232 1472707937 3318086852 719779833 3545481907 1384350857 "
+      "191709822 2924586997 3905397527 2528674757 2377804096 2742818576 4162971050 1916415976 2229076768 132706400 "
+      "1337415646 251604474 4168783505 819499074 1213291699 2098930479 1009044052 1060792037 2169940733 2973281031 "
+      "636656529 1946890813 367099469 4076624283 338339529 561343735 3309733580 3895891229 516195845 3075613489 "
+      "3076388119 4088758753 3895582245 1192445752 2923797073 2272605437 2964193665 943979628 3263904824 1852145108 "
+      "1911050628 1596880711 479795791 212699077 2172697748 1764175804 3951845064 2182587171 2396848133 2718837214 "
+      "2934177572 3638054152 914795167 1028977354 1744985339 1161407853 2069746822 3109934340 3666150054 729399111 "
+      "2911998815 152809615 417267263 1064343325 2751707923 1833270972 886939906 4054231201 1410366452 976142646 "
+      "1452795786 1904234879 4231894541 1505392175 3498767279 833704604 2100592828 688268099 1752675602 1975556664 "
+      "2102210733 698665709 1232104968 4051336323 1740516649 3131721670 3755143232 3433756601 138325540 1566724819 "
+      "3146312914 3262098346 1400757929 3821885720 356740291 4236900846 3962771215 3383537419 3432281817 3700484231 "
+      "1124512300 4040103150 2152814065 357241942 3507144572 3156778000 4029389048 1495464238 2710816120 618937484 "
+      "923913256 3233161411 3113338135 2380534800 2989134055 792458413 2073679910 2733527896 1078384210 2832175364 "
+      "501385130 1554930792 2339252603 535487294 2983664186 924228889 2127502206 2847205678 836935943 1902192892 "
+      "4094716961 3296730761 2307406435 281523156 728701372 3943302480 183005637 2660706047 2320033913 2011717602 "
+      "219970222 890345073 2479236311 653839655 38538414 3266308455 3726363269 1256762826 712114913 3499059198 "
+      "2847394904 3246075282 1299462737 830090526 369065487 3899451086 1825910875 2697294244 2553197317 2501134241 "
+      "930821716 2841281141 3920808923 3209045349 3381621736 2145077949 1958091404 371971179 3064957112 3151429184 "
+      "2833651995 1089438243 2146134351 1426568997 3528669710 1043897757 3052263589 3205925012 4076215371 2622476950 "
+      "2385717100 2179892838 3820135605 1574628644 177604240 1712062317 1192081671 1107476027 837376039 2818244460 "
+      "1382379731 2749207910 2644203474 1508860349 3006844699 3157252375 3789683778 1985542020 2309789126 1624089624 "
+      "3660683301 1432444043 1172735898 3271587635 841387671 3614623669 3002115850 1078807738 1095345284 1707086713 "
+      "4263485053 445506874 3148772337 2716917408 3836812085 1026228926 491069724 3983218285 2410582206 3821759029 "
+      "3789305873 1223256738 1677180485 1481771733 1670286271 2852111711 1628943665 2289772949 3483580787 2129860019 "
+      "3955695109 28974017 2289227173 1725243773 483357125 111587742 347631238 1805029491 839978009 339030914 "
+      "2442782449 1701726526 1481742442 3703534067 3796147861 235078546 3195450129 1420184214 998981944 163541155 "
+      "2087693215 3827554748 3003331872 1967967170 160190917 544473278 3884602428 4180513186 1603446876 2191744556 "
+      "1938251010 291242497 1764195107 4076051069 3860380798 806452940 2631256196 949838208 4096577802 1519225265 "
+      "2296800559 1015848400 2864792481 88004533 195132439 966918321 1451347413 2322994051 3585658769 1771598947 "
+      "3308395968 950148946 3846061568 564475171 1345892193 2439581032 2907206869 1297626422 3779454571 818255727 "
+      "705738065 3915384184 4133726754 2724859327 3773743817 326325901 2766390048 2562130418 1885210076 2622324981 "
+      "1727396790 1742653294 3133131942 3790857080 197042473 2868410679 1122976862 2264474389 3607393378 744386048 "
+      "3122128592 1101489146 4064675687 2818166901 2282076661 370818870 3053423257 2272652338 4077081984 4284213290 "
+      "1728304803 3902867896 1643530236 306212760 144705843 498541197 3103269575 4116304893 1646288275 2110757680 "
+      "4092919505 3390929578 1828116798 1847914177 1156754878 2985404552 3970881126 3035874635 2189824678 2637114092 "
+      "2588332748 3917818803 3302695646 1758418726 1934759438 3871576937 1360769189 1051808374 1881390541 757367126 "
+      "3253778407 1685049720 805311189 934898036 95682794 2052240235 3475933669 2213474191 3341273357 2443286051 "
+      "4272536794 696819230 878775027 3332634155 3405126958 2824772573 95284505 3040640920 2412859827 2959515765 "
+      "1764792340 1504924482 2116910326 2293943003 3199262907 1443740894 3900566588 343066614 1927475335 249220892 "
+      "2920636502 3593328327 2648828502 1678096082"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  std::mt19937 e1(0);
+  LIBCPP_ASSERT(to_string(e1) == a);
+  (void)a;
 }
 
-void
-test2()
-{
-    const char* a = "0 1 6364136223846793007 13885033948157127961 "
+void test2() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    "0 1 6364136223846793007 13885033948157127961 "
     "15324573939901584278 12737837167382305846 15195339788985155882 "
     "6554113247712070460 17235932740818599105 13007415075556305955 "
     "6585479514541334743 8274505865835507625 1718218088692873364 "
@@ -248,9 +313,76 @@ test2()
     "14091690436120485477 15763282477731738396 16394237160547425954 "
     "5066318118328746621 13140493775100916989 6371148952471982853 "
     "15150289760867914983 4931341382074091848 12635920082410445322 "
-    "8498109357807439006 14836776625250834986";
-    std::mt19937_64 e1(0);
-    assert(to_string(e1) == a);
+    "8498109357807439006 14836776625250834986"
+#else
+      "4640967165370225016 475580846709610041 992132072322742490 2879266425597847059 9859212410995834590 "
+      "13110412116137766394 2786888605138474240 12527462973726820895 5232191189150589434 8148228587345305383 "
+      "15985190208517110588 17155057006866468333 5040233137627218077 12716217438209898279 7628670991140263350 "
+      "9633431818170735083 8029874679197696152 2546188705250129595 13105544608186384869 6197138528956131705 "
+      "2500767142257833113 4057403796030124452 4801410225133580521 12712510024708253355 1478853541812684571 "
+      "16899998798183024166 763564441705181102 13204668179160915419 6975570844831914277 12029362619687152075 "
+      "17592155920476593974 1391138604394782251 13557582389860889375 6018426101890403504 7401881601334221625 "
+      "281124061034155254 2261645520472923927 10254468173793190351 4336071085409420785 7075410027066751424 "
+      "7438312290102887001 15139389032212795768 12949861808476919158 5221320262711473213 18444574859163569422 "
+      "3327484732560520915 10371833210785813703 494705948341138486 313693460050824934 801700386546917424 "
+      "3333797513889190239 11734932229126290067 4001689285737041461 17077402128101531244 17161815342609821634 "
+      "4393649844836338524 17495132932599391165 16521868517739667677 5661222119201994079 12972222767581865814 "
+      "11571421678172213790 390646047509318948 11495394588801409501 6525248087626393555 8573656613713326328 "
+      "8142990904973493114 13660896775289126601 17864485770152980871 17445884582967639919 5024202147275854142 "
+      "671342157422491897 8265009237858722295 8608118008319642631 9219145998498351877 4556268366257199356 "
+      "6140599343918232709 1607043993756685523 16576680458922020446 2464344868820317239 11814083511457927881 "
+      "17322648412109813175 1813592025330691328 11477642670415214162 17070997401037634101 2303972879274470462 "
+      "11243326672568121379 3445882696291328363 12839802107382893721 12104481080282265364 2596243342696322386 "
+      "17790946661248112185 16074938231864255959 2956506024687822818 5581797792339843057 16395798806003821452 "
+      "1430185758107085903 18323996060167045894 9286586749315539711 11359324205220105656 13078829860258641519 "
+      "5216000878301612113 9168569275644144847 4001438364296785628 2620979336382597902 17733020247211834335 "
+      "9313982434870058041 14715751745255862275 2010565534563004422 3178715725792479308 1903043159754162676 "
+      "14115336913931799326 2521027868575181436 16014439786100458068 12367605516710589904 209714080394415764 "
+      "1624794657804038156 9935768631398762231 4151749392286673312 7107972358174928160 5660032753352365076 "
+      "18036755596310855736 10390782681895899933 1046990263302077358 7362677844682408358 9462769113228112518 "
+      "16297726431682095630 320526314299713717 3778064933596859787 10885323884409002007 2236973268193149682 "
+      "10878852310456567407 2570883140239125692 2789272264811335056 13059628436710418572 2738898868861677604 "
+      "13893534347610447431 5822583231449316688 6492049503818767408 6244748132635487608 8670290879871155016 "
+      "9029174311949188652 14279734151305645897 15912641926116463991 5242647656726280598 18355120933288718928 "
+      "6020122330537384578 14237586109975592522 4056743947737351776 11111921204484673848 2182608451873446153 "
+      "166740030032355275 6707804438883761942 946217821621517312 17920751901125513000 11238439424546078979 "
+      "9206370960702334859 10367106562255294642 16885700982121403407 5166729472772734774 12763662184890206104 "
+      "5517776110990532992 14067054651980895669 91103470530233291 13189720418381102366 13302941329281487681 "
+      "10033391968256035918 5361508361658563078 9633125940775192275 4070378705292966499 2578880557354159711 "
+      "8213829012818869727 14609024865720281973 5764529456700294955 4441867238869537141 4877335405025028278 "
+      "3719605268964114254 6912476600417191412 12213543493427784533 9772185674504838078 849905513166852628 "
+      "2288907677236569364 13249989292558739703 17027351938809896108 621729935073174643 11783397793000418128 "
+      "6535353235698302382 17474699882318514854 11002055139008150918 18416122529605421577 6901480652243894157 "
+      "10382683051257874235 4029870255315506009 2965992838295436717 6235535328826140656 12567736520515051239 "
+      "1747508188587454664 6183192989050178616 15953675909489391368 10361870655666472156 13992646052760132872 "
+      "18278663149537015721 2341145555999432211 6926699433818498172 13807760559474580665 14452983890116009870 "
+      "11892982856082687091 17809698859762328768 2068107465420962246 7113569128111994893 4401076180719847553 "
+      "11644868941271347491 13124811241626639928 13749911628127226656 17349042196015114201 17667020131618919593 "
+      "5882998107995308535 3994906988813734883 12471577435602039626 6541516905696908495 12651354739990156236 "
+      "2656551093809687944 8349751746116271717 16003262134315245189 16966957802441281611 9846484220369869695 "
+      "289040869684008784 12984844645788078807 15184274406289320733 8585588724744493207 17783251231435661237 "
+      "8433051731677026430 4615759437146530251 7622401495291460413 11623553053949964326 5539054352599742054 "
+      "8346732818944267068 2539102865177431249 12328004780676882137 14682995638339538937 17618899063173726898 "
+      "3560029508967023860 203715062184427611 4331882803273086182 11747937041982829018 14988651560527263167 "
+      "12178986862382858651 3296725696957964696 13476561205033500550 5411341409364572097 6178165960265458773 "
+      "6637587084784255540 2852138145128301044 285104174921779935 3890210696591283091 15859573696692819698 "
+      "4183398296224968094 169433174359854381 11697218950892752694 13577878976046486116 9712904827015064754 "
+      "7617616830643388997 12751433537134599555 12319041734951133946 10239102328951141680 7290886622313676141 "
+      "4739372139955959505 9070795802053402609 10714415902002968511 14446814812660765483 8195026527484180858 "
+      "10331449076861906227 11969763657717422129 11061711610463627412 3682504726309317968 186477037840564317 "
+      "10307292328882458385 11683537113360307954 11300920371008006928 12769285684201107347 14545522978223964713 "
+      "5045546601922369224 202539708050129846 9727300421085682838 10660376569580918468 18521046132102800...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:08:18 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 01:08:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix behavior for
 `get_temporary_buffer` with non-positive size (PR #206871)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206871@github.com>
Message-ID: <6a44caf2.8f0cbdd2.1ed5bb.c720@mx.google.com>


================
@@ -38,5 +40,23 @@ int main(int, char**)
     assert(reinterpret_cast<std::uintptr_t>(ip.first) % alignof(A) == 0);
     std::return_temporary_buffer(ip.first);
 
+    // C++17 [depr.temporary.buffer]/4
+    // Returns: If n <= 0 or if no storage could be obtained,
+    // returns a pair P such that P.first is a null pointer value and P.second == 0;
+    {
+      std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(0);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+    {
+      TEST_DIAGNOSTIC_PUSH
+      // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
----------------
philnik777 wrote:

That's a bit unfortunate. I think I'd go for the `=` version without any number. (Also, maybe a GCC bug report?)

https://github.com/llvm/llvm-project/pull/206871

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:09:19 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 01:09:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a44cb2f.976e2a4f.30a67f.aafc@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:10:33 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 01:10:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++] Optimize fstream::read"
	(PR #206453)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206453@github.com>
Message-ID: <6a44cb79.68efef3a.241cb2.41a4@mx.google.com>


https://github.com/philnik777 ready_for_review https://github.com/llvm/llvm-project/pull/206453

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:11:13 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 01:11:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++] Optimize fstream::read"
	(PR #206453)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206453@github.com>
Message-ID: <6a44cba1.560f397e.180e47.1a0b@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

This was reverted due to causing crashes if an ifstream wasn't opened.
This patch addresses this issue by simply checking whether the `__file_`
handle is null, and if it is simply fall back to the generic
implementation.

Fixes #<!-- -->168628
Fixes #<!-- -->205845

This reverts commit 347512ff38748ac6ebfacbfda172edb5cf1edbe2.


---
Full diff: https://github.com/llvm/llvm-project/pull/206453.diff


6 Files Affected:

- (modified) libcxx/docs/ReleaseNotes/23.rst (+2) 
- (modified) libcxx/include/fstream (+19) 
- (renamed) libcxx/test/benchmarks/streams/fstream.bench.cpp (+20-2) 
- (modified) libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp (+1-1) 
- (modified) libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp (+1-1) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp (+19) 


``````````diff
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 37aa972d22ea3..1852351cedc4e 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -69,6 +69,8 @@ Improvements and New Features
   ``"std::visit: variant is valueless"``, ``"std::get: variant is valueless"``, or
   ``"std::get: wrong alternative for variant"``. The standard only requires ``what()`` to return an
   unspecified non-null string, so user code that does not match on the exact message remains correct.
+- ``ifstream::read`` has been optimized to pass through large reads to system calls directly instead of copying them in
+  chunks.
 
 Deprecations and Removals
 -------------------------
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 0c34c9149de0d..7b84bf6609086 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -309,6 +309,25 @@ protected:
     return basic_streambuf<_CharT, _Traits>::xsputn(__str, __len);
   }
 
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL streamsize xsgetn(char_type* __str, streamsize __len) override {
+    if (__file_ && __always_noconv_) {
+      const streamsize __n = std::min(this->egptr() - this->gptr(), __len);
+      if (__n != 0) {
+        traits_type::copy(__str, this->gptr(), __n);
+        this->__gbump_ptrdiff(__n);
+      }
+      const streamsize __remainder    = __len - __n;
+      const streamsize __buffer_space = this->egptr() - this->eback();
+
+      if (__remainder >= __buffer_space)
+        return std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
+      else if (__remainder > 0)
+        return basic_streambuf<_CharT, _Traits>::xsgetn(__str + __n, __remainder) + __n;
+      return __n;
+    }
+    return basic_streambuf<_CharT, _Traits>::xsgetn(__str, __len);
+  }
+
 private:
   char* __extbuf_;
   const char* __extbufnext_;
diff --git a/libcxx/test/benchmarks/streams/ofstream.bench.cpp b/libcxx/test/benchmarks/streams/fstream.bench.cpp
similarity index 52%
rename from libcxx/test/benchmarks/streams/ofstream.bench.cpp
rename to libcxx/test/benchmarks/streams/fstream.bench.cpp
index 80827d59cdf01..2e16ae58749e6 100644
--- a/libcxx/test/benchmarks/streams/ofstream.bench.cpp
+++ b/libcxx/test/benchmarks/streams/fstream.bench.cpp
@@ -13,7 +13,7 @@
 
 #include <benchmark/benchmark.h>
 
-static void bm_write(benchmark::State& state) {
+static void bm_ofstream_write(benchmark::State& state) {
   std::vector<char> buffer;
   buffer.resize(16384);
 
@@ -22,6 +22,24 @@ static void bm_write(benchmark::State& state) {
   for (auto _ : state)
     stream.write(buffer.data(), buffer.size());
 }
-BENCHMARK(bm_write)->Name("std::ofstream::write(char*, size)");
+BENCHMARK(bm_ofstream_write)->Name("std::ofstream::write(char*, size)");
+
+static void bm_ifstream_read(benchmark::State& state) {
+  std::vector<char> buffer;
+  buffer.resize(16384);
+
+  std::ofstream gen_testfile("testfile");
+  gen_testfile.write(buffer.data(), buffer.size());
+
+  std::ifstream stream("testfile");
+  assert(stream);
+
+  for (auto _ : state) {
+    stream.read(buffer.data(), buffer.size());
+    benchmark::DoNotOptimize(buffer);
+    stream.seekg(0);
+  }
+}
+BENCHMARK(bm_ifstream_read)->Name("std::ifstream::read(char*, size)");
 
 BENCHMARK_MAIN();
diff --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
index 283adbc057d1e..30e7b66d42325 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
@@ -19,4 +19,4 @@
 
 std::basic_filebuf<char, std::char_traits<wchar_t> > f;
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
-// expected-error@*:* 10 {{only virtual member functions can be marked 'override'}}
+// expected-error@*:* 11 {{only virtual member functions can be marked 'override'}}
diff --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
index ba6f3c31d3e34..daafb36f9151a 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
@@ -21,7 +21,7 @@ std::basic_fstream<char, std::char_traits<wchar_t> > f;
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
 
-// expected-error@*:* 12 {{only virtual member functions can be marked 'override'}}
+// expected-error@*:* 13 {{only virtual member functions can be marked 'override'}}
 
 // FIXME: As of commit r324062 Clang incorrectly generates a diagnostic about mismatching
 // exception specifications for types which are already invalid for one reason or another.
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
index de17cd4d8e9d3..17615a4812d7d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
@@ -68,6 +68,25 @@ int main(int, char**) {
     test_buffer.resize(24);
     assert(std::string(test_buffer.data(), 24) == "to test buffer behaviour");
   }
+  { // Ensure that read fails gracefully with an unopened ifstream
+    // See https://llvm.org/PR168628
+    char buf[10];
+    std::ifstream ifs;
+
+    assert(!ifs.is_open());
+    assert(!ifs.bad());
+    assert(!ifs.fail());
+    assert(!ifs.eof());
+    assert(ifs.good());
+
+    ifs.read(buf, sizeof(buf));
+
+    assert(!ifs.is_open());
+    assert(!ifs.bad());
+    assert(ifs.fail());
+    assert(ifs.eof());
+    assert(!ifs.good());
+  }
 
   return 0;
 }

``````````

</details>


https://github.com/llvm/llvm-project/pull/206453

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:14:39 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 01:14:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Base string's alignment on
 __STDCPP_DEFAULT_NEW_ALIGNMENT__ (PR #171785)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171785@github.com>
Message-ID: <6a44cc6f.481afadf.29a24b.2867@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/171785

>From 799f3b107ce4ce681a6f16998548fb8c86d7883f Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 11 Dec 2025 10:14:52 +0100
Subject: [PATCH] [libc++] Base string's alignment on
 __STDCPP_DEFAULT_NEW_ALIGNMENT__

---
 libcxx/include/__cxx03/string                 | 13 +++++++-
 libcxx/include/string                         | 12 +++++++-
 .../string.capacity/allocation_size.pass.cpp  |  7 +++--
 .../string.capacity/max_size.pass.cpp         |  7 +++--
 .../strings/basic.string/new_alignment.sh.cpp | 30 +++++++++++++++++++
 .../string.capacity/allocation_size.pass.cpp  | 21 +++++++------
 .../string.capacity/max_size.pass.cpp         |  5 +++-
 .../string.capacity/max_size.pass.cpp         |  4 +++
 8 files changed, 83 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp

diff --git a/libcxx/include/__cxx03/string b/libcxx/include/__cxx03/string
index a919a59e55d57..e5330fb01c04f 100644
--- a/libcxx/include/__cxx03/string
+++ b/libcxx/include/__cxx03/string
@@ -1623,7 +1623,18 @@ private:
   static _LIBCPP_HIDE_FROM_ABI size_type __align_it(size_type __s) _NOEXCEPT {
     return (__s + (__a - 1)) & ~(__a - 1);
   }
-  enum { __alignment = 8 };
+
+  // GCC currently doesn't define __STDCPP_DEFAULT_NEW_ALIGNMENT__ unconditionally
+  // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123872
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+  static inline const size_t
+      __alignment = __is_default_allocator<allocator_type>::value && __STDCPP_DEFAULT_NEW_ALIGNMENT__ > sizeof(void*)
+                      ? __STDCPP_DEFAULT_NEW_ALIGNMENT__
+                      : sizeof(void*);
+#else
+  static inline const size_t __alignment = sizeof(void*);
+#endif
+
   static _LIBCPP_HIDE_FROM_ABI size_type __recommend(size_type __s) _NOEXCEPT {
     if (__s < __min_cap) {
       return static_cast<size_type>(__min_cap) - 1;
diff --git a/libcxx/include/string b/libcxx/include/string
index e5bc854cd34f3..76c5b0e477a59 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -2270,7 +2270,17 @@ private:
   static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __align_it(size_type __s) _NOEXCEPT {
     return (__s + (__a - 1)) & ~(__a - 1);
   }
-  enum { __alignment = 8 };
+
+  // GCC currently doesn't define __STDCPP_DEFAULT_NEW_ALIGNMENT__ unconditionally
+  // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123872
+#  ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+  static inline const size_t
+      __alignment = __is_std_allocator_v<allocator_type> && __STDCPP_DEFAULT_NEW_ALIGNMENT__ > sizeof(void*)
+                      ? __STDCPP_DEFAULT_NEW_ALIGNMENT__
+                      : sizeof(void*);
+#  else
+  static inline const size_t __alignment = sizeof(void*);
+#  endif
 
   // This makes sure that we're using a capacity with some extra alignment, since allocators almost always over-align
   // the allocations anyways, improving memory usage. More importantly, this ensures that the lowest bit is never set
diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
index 77da29225957b..367a58897cec0 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
@@ -15,8 +15,11 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
-const std::size_t alignment = 8;
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const std::size_t alginment = 8;
+#endif
 
 int main(int, char**) {
   std::string input_string;
diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
index 73825ef4845f2..15736e4a1ceed 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -17,8 +17,11 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
-static const std::size_t alignment = 8;
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const std::size_t alginment = 8;
+#endif
 
 template <class = int>
 TEST_CONSTEXPR_CXX20 void full_size() {
diff --git a/libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp b/libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp
new file mode 100644
index 0000000000000..f8906c9fb9827
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Check that std::string bases its alignment on __STDCPP_DEFAULT_NEW_ALIGNMENT__
+
+// UNSUPPORTED: c++03
+
+// RUN: %{build} -faligned-new=32 -DALIGN=32
+// RUN: %{run}
+// RUN: %{build} -faligned-new=16 -DALIGN=16
+// RUN: %{run}
+// RUN: %{build} -faligned-new=8 -DALIGN=8
+// RUN: %{run}
+
+#include <cassert>
+#include <string>
+
+int main(int, char**) {
+  std::string str;
+  str.reserve(25);
+
+  assert(str.capacity() % ALIGN == ALIGN - 1);
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
index 77da29225957b..d9afd7d199c6f 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
@@ -8,6 +8,10 @@
 
 // <string>
 
+// This test is sensitive what __STDCPP_DEFAULT_NEW_ALIGNMENT__ is set to. Enable aligned-new for GCC so that GCC
+// defines the macro
+// ADDITIONAL_COMPILE_FLAGS(gcc): -faligned-new
+
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
@@ -15,22 +19,21 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
-const std::size_t alignment = 8;
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const std::size_t alignment = 8;
+#endif
 
 int main(int, char**) {
-  std::string input_string;
-  input_string.resize(64, 'a');
-
-  // Call a constructor which selects its size using __recommend.
-  std::string test_string(input_string.data());
+  std::string str(64, 'a');
   const std::size_t expected_align8_size = 71;
 
   // Demonstrate the lesser capacity/allocation size when the alignment requirement is 8.
   if (alignment == 8) {
-    assert(test_string.capacity() == expected_align8_size);
+    assert(str.capacity() == expected_align8_size);
   } else {
-    assert(test_string.capacity() == expected_align8_size + 8);
+    assert(str.capacity() == expected_align8_size + 8);
   }
 
   return 0;
diff --git a/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
index 6bfcb5d4bfcd8..11bd1a5928d4a 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -17,8 +17,11 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
 static const std::size_t alignment = 8;
+#endif
 
 template <class = int>
 TEST_CONSTEXPR_CXX20 void full_size() {
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
index 45a52acab464e..b35bed39f2bed 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -8,6 +8,10 @@
 
 // UNSUPPORTED: no-exceptions
 
+// This test is sensitive what __STDCPP_DEFAULT_NEW_ALIGNMENT__ is set to. Enable aligned-new for GCC so that GCC
+// defines the macro
+// ADDITIONAL_COMPILE_FLAGS(gcc): -faligned-new
+
 // <string>
 
 // size_type max_size() const; // constexpr since C++20


From libcxx-commits at lists.llvm.org  Wed Jul  1 01:16:10 2026
From: libcxx-commits at lists.llvm.org (Heejin Ahn via libcxx-commits)
Date: Wed, 01 Jul 2026 01:16:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [llvm] [WebAssembly]
 Directly call EH personality function (PR #175202)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/175202@github.com>
Message-ID: <6a44ccca.ac469dc7.22d7c7.6141@mx.google.com>


aheejin wrote:

I see. Yeah, the current implementation only focuses on C++ support and we didn't really consider other languages or interops with C++ much when doing this.

I think giving each function a choice of personality function depending on the language is a good idea. We may be able to configure the exception tag (`__cpp_exception` for C++) based on the personality function.

Given that `_Unwind_CallPersonality` is mostly a wrapper from our libunwind, making it a personality function within the bitcode signature seems a little awkward though. As I said the benefit of having it is we can save some instructions per catch site. Maybe we should rename it. Or getting rid of it entirely may not the end of world, because C++'s exception usage is dominated by destructors and the number of try-catches is usually small.

Are you planning to submit patches for this?

https://github.com/llvm/llvm-project/pull/175202

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:22:01 2026
From: libcxx-commits at lists.llvm.org (Demetrius Kanios via libcxx-commits)
Date: Wed, 01 Jul 2026 01:22:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [llvm] [WebAssembly]
 Directly call EH personality function (PR #175202)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/175202@github.com>
Message-ID: <6a44ce29.49a3bc26.2606cf.6f28@mx.google.com>


QuantumSegfault wrote:

> Are you planning to submit patches for this?

I'd be fine with doing the PR, once we figure out the direction (i.e. like this PR was not satisfactory)

> Given that _Unwind_CallPersonality is mostly a wrapper from our libunwind, making it a personality function within the bitcode signature seems a little awkward though. As I said the benefit of having it is we can save some instructions per catch site.

Which is why I'm saying why not merge `_Unwind_CallPersonality` down into `__gxx_wasm_personality_v0`. Make it so personality functions on Wasm take a single pointer to the EH object, and are expected to initialize the landing pad context. We keep the small code size, at the cost of breaking/changing the ABI?

https://github.com/llvm/llvm-project/pull/175202

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:26:08 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 01:26:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a44cf20.510f576d.1df92b.9779@mx.google.com>


================
@@ -6,602 +6,487 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
-#include "test_macros.h"
-
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
+#include "make_string.h"
+
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
 
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
-  }
-}
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
 
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::basic_string()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-    newS->~S();
+  });
+
+  bench("std::basic_string::basic_string(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  bench(
+      "std::basic_string::basic_string(const basic_string&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> str(state.range(), 'a');
----------------
philnik777 wrote:

I'm not quite sure that this comment applied to. I've only done it for the copy constructor benchmark for now.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:28:33 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 01:28:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a44cfb1.f06f4994.2ae0bf.0a04@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/185397

>From e3319709150b99dce85caf10629cf427da35eae8 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 12 Feb 2026 15:08:00 +0100
Subject: [PATCH] [libc++] Refactor the string benchmarks

---
 .../benchmarks/containers/string.bench.cpp    | 1014 ++++++++---------
 1 file changed, 453 insertions(+), 561 deletions(-)

diff --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index 98216d22d0144..ea23b713fc5f0 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -6,602 +6,494 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
-#include "test_macros.h"
-
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+#include "make_string.h"
+
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
 
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
 
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
 
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
 }
 
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
-}
-
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Huge does not give us anything that Large doesn't have. Skip it.
-    return Length() == ::Length::Huge;
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  std::string name() const { return "BM_StringRead" + Temperature::name() + Depth::name() + Length::name(); }
-};
-
-void sanityCheckGeneratedStrings() {
-  for (auto Lhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
-    const auto LhsString = makeString(Lhs);
-    for (auto Rhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
-      if (Lhs > Rhs)
-        continue;
-      const auto RhsString = makeString(Rhs);
-
-      // The smaller one must be a prefix of the larger one.
-      if (RhsString.find(LhsString) != 0) {
-        fprintf(
-            stderr, "Invalid autogenerated strings for sizes (%d,%d).\n", static_cast<int>(Lhs), static_cast<int>(Rhs));
-        std::abort();
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string.compare(src));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
-  // Verify the autogenerated diffs
-  for (auto L : {Length::Small, Length::Large, Length::Huge}) {
-    const auto Control = makeString(L);
-    const auto Verify  = [&](std::string Exp, size_t Pos) {
-      // Only change on the Pos char.
-      if (Control[Pos] != Exp[Pos]) {
-        Exp[Pos] = Control[Pos];
-        if (Control == Exp)
-          return;
-      }
-      fprintf(stderr, "Invalid autogenerated diff with size %d\n", static_cast<int>(L));
-      std::abort();
-    };
-    Verify(makeString(L, DiffType::ChangeFirst), 0);
-    Verify(makeString(L, DiffType::ChangeMiddle), Control.size() / 2);
-    Verify(makeString(L, DiffType::ChangeLast), Control.size() - 1);
-  }
-}
 
-int main(int argc, char** argv) {
   benchmark::Initialize(&argc, argv);
-  if (benchmark::ReportUnrecognizedArguments(argc, argv))
-    return 1;
-
-  sanityCheckGeneratedStrings();
-
-  makeCartesianProductBenchmark<StringConstructDestroyCStr, AllLengths, AllOpacity>();
-
-  makeCartesianProductBenchmark<StringAssignStr, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringAssignAsciiz, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringAssignAsciizMix, AllOpacity>();
-
-  makeCartesianProductBenchmark<StringCopy, AllLengths>();
-  makeCartesianProductBenchmark<StringMove, AllLengths>();
-  makeCartesianProductBenchmark<StringDestroy, AllLengths>();
-  makeCartesianProductBenchmark<StringEraseToEnd, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringEraseWithMove, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringRelational, AllRelations, AllLengths, AllLengths, AllDiffTypes>();
-  makeCartesianProductBenchmark<StringRelationalLiteral, AllRelations, AllLengths, AllLengths, AllDiffTypes>();
-  makeCartesianProductBenchmark<StringRead, AllTemperatures, AllDepths, AllLengths>();
   benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
 }


From libcxx-commits at lists.llvm.org  Wed Jul  1 01:29:21 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 01:29:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a44cfe1.f06f4994.2ae0bf.2cee@mx.google.com>


https://github.com/philnik777 ready_for_review https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:29:56 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 01:29:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a44d004.4a342cbc.22496e.0fd3@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

Fixes #<!-- -->179696


---

Patch is 38.13 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/185397.diff


1 Files Affected:

- (modified) libcxx/test/benchmarks/containers/string.bench.cpp (+453-561) 


``````````diff
diff --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index 98216d22d0144..ea23b713fc5f0 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -6,602 +6,494 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
-#include "test_macros.h"
-
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+#include "make_string.h"
+
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
 
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
 
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
 
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
 }
 
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
-}
-
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static c...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:41:09 2026
From: libcxx-commits at lists.llvm.org (Heejin Ahn via libcxx-commits)
Date: Wed, 01 Jul 2026 01:41:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [llvm] [WebAssembly]
 Directly call EH personality function (PR #175202)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/175202@github.com>
Message-ID: <6a44d2a5.58bdd12c.2e0810.c9a3@mx.google.com>


aheejin wrote:

If we merge them, do you mean we just put it in libcxxabi? That might work. We still have a few functions in libunwind, but if other languages don't need them, that should be fine.

https://github.com/llvm/llvm-project/pull/175202

From libcxx-commits at lists.llvm.org  Wed Jul  1 01:46:09 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 01:46:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix behavior for
 `get_temporary_buffer` with non-positive size (PR #206871)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206871@github.com>
Message-ID: <6a44d3d1.92ea0e2a.99846.15cd@mx.google.com>


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/206871

>From 92bae65367bbcc5caa6601e70e37715d1bec9e5b Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 1 Jul 2026 10:12:44 +0800
Subject: [PATCH 1/4] [libc++] Fix behavior for `get_temporary_buffer` with
 non-positive size

Per C++17 [depr.temporary.buffer]/4, `get_temporary_buffer` is required
to return `{nullptr, 0}` when the size argument is zero or negative.
libc++ used to correctly handle this, but the refactoring in
94e7c0b051c79fd56205f115771980f2e7812306 got this wrong.
---
 libcxx/include/__memory/temporary_buffer.h         |  3 +++
 .../memory/temporary.buffer/overaligned.pass.cpp   | 14 ++++++++++++++
 .../temporary.buffer/temporary_buffer.pass.cpp     | 14 ++++++++++++++
 3 files changed, 31 insertions(+)

diff --git a/libcxx/include/__memory/temporary_buffer.h b/libcxx/include/__memory/temporary_buffer.h
index d18717f52d1cd..3c572e43db641 100644
--- a/libcxx/include/__memory/temporary_buffer.h
+++ b/libcxx/include/__memory/temporary_buffer.h
@@ -26,6 +26,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Tp>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_CFI _LIBCPP_DEPRECATED_IN_CXX17 pair<_Tp*, ptrdiff_t>
 get_temporary_buffer(ptrdiff_t __n) _NOEXCEPT {
+  if (__n <= 0)
+    return pair<_Tp*, ptrdiff_t>();
+
   __unique_temporary_buffer<_Tp> __unique_buf = std::__allocate_unique_temporary_buffer<_Tp>(__n);
   pair<_Tp*, ptrdiff_t> __result(__unique_buf.get(), __unique_buf.get_deleter().__count_);
   __unique_buf.release();
diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
index 3fa9355270866..678e4e3d342fb 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
@@ -38,5 +38,19 @@ int main(int, char**)
     assert(reinterpret_cast<std::uintptr_t>(ip.first) % alignof(A) == 0);
     std::return_temporary_buffer(ip.first);
 
+    // C++17 [depr.temporary.buffer]/4
+    // Returns: If n <= 0 or if no storage could be obtained,
+    // returns a pair P such that P.first is a null pointer value and P.second == 0;
+    {
+      std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(0);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+    {
+      std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(-5);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+
   return 0;
 }
diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
index 39a4767d874e3..50ca6b21ae46c 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
@@ -31,5 +31,19 @@ int main(int, char**)
     assert(ip.second == 5);
     std::return_temporary_buffer(ip.first);
 
+    // C++17 [depr.temporary.buffer]/4
+    // Returns: If n <= 0 or if no storage could be obtained,
+    // returns a pair P such that P.first is a null pointer value and P.second == 0;
+    {
+      std::pair<int*, std::ptrdiff_t> ret = std::get_temporary_buffer<int>(0);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+    {
+      std::pair<int*, std::ptrdiff_t> ret = std::get_temporary_buffer<int>(-5);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+
   return 0;
 }

>From 90d750fbdc0f9a9fcb01c7b5cfa3ed36d99cd163 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 1 Jul 2026 13:35:25 +0800
Subject: [PATCH 2/4] `-Wno-alloc-size-larger-than`

---
 .../utilities/memory/temporary.buffer/overaligned.pass.cpp   | 5 +++++
 .../memory/temporary.buffer/temporary_buffer.pass.cpp        | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
index 678e4e3d342fb..0f119ae90b82e 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
@@ -27,6 +27,11 @@
 #include <memory>
 #include <utility>
 
+#include "test_macros.h"
+
+// This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
+
 struct alignas(32) A {
     int field;
 };
diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
index 50ca6b21ae46c..729cb14b9c5e9 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
@@ -24,6 +24,11 @@
 #include <memory>
 #include <utility>
 
+#include "test_macros.h"
+
+// This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
+
 int main(int, char**)
 {
     std::pair<int*, std::ptrdiff_t> ip = std::get_temporary_buffer<int>(5);

>From 3590c72e1e0cb065b9297b2060c73344869f6f41 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 1 Jul 2026 15:40:16 +0800
Subject: [PATCH 3/4] Locally suppress the warning

---
 .../utilities/memory/temporary.buffer/overaligned.pass.cpp | 7 ++++---
 .../memory/temporary.buffer/temporary_buffer.pass.cpp      | 7 ++++---
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
index 0f119ae90b82e..2b4b6586f5563 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
@@ -29,9 +29,6 @@
 
 #include "test_macros.h"
 
-// This warning is coupled with completeness of control flow analysis which is affected by optimizations.
-TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
-
 struct alignas(32) A {
     int field;
 };
@@ -52,7 +49,11 @@ int main(int, char**)
       assert(ret.second == 0);
     }
     {
+      TEST_DIAGNOSTIC_PUSH
+      // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
       std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(-5);
+      TEST_DIAGNOSTIC_POP
       assert(ret.first == NULL);
       assert(ret.second == 0);
     }
diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
index 729cb14b9c5e9..6935e8a9d83d5 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
@@ -26,9 +26,6 @@
 
 #include "test_macros.h"
 
-// This warning is coupled with completeness of control flow analysis which is affected by optimizations.
-TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
-
 int main(int, char**)
 {
     std::pair<int*, std::ptrdiff_t> ip = std::get_temporary_buffer<int>(5);
@@ -45,7 +42,11 @@ int main(int, char**)
       assert(ret.second == 0);
     }
     {
+      TEST_DIAGNOSTIC_PUSH
+      // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
       std::pair<int*, std::ptrdiff_t> ret = std::get_temporary_buffer<int>(-5);
+      TEST_DIAGNOSTIC_POP
       assert(ret.first == NULL);
       assert(ret.second == 0);
     }

>From c95c07613f6cebd619601ad85fc4d209cb8bfe4e Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 1 Jul 2026 16:45:46 +0800
Subject: [PATCH 4/4] Tweak the suppressing style and add test cases

---
 .../memory/temporary.buffer/overaligned.pass.cpp     |  5 +++--
 .../temporary.buffer/temporary_buffer.pass.cpp       | 12 +++++++++++-
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
index 2b4b6586f5563..c55e488af53b1 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
@@ -24,6 +24,7 @@
 #include <cassert>
 #include <cstddef>
 #include <cstdint>
+#include <limits>
 #include <memory>
 #include <utility>
 
@@ -51,8 +52,8 @@ int main(int, char**)
     {
       TEST_DIAGNOSTIC_PUSH
       // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
-      TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
-      std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(-5);
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Walloc-size-larger-than=")
+      std::pair<A*, std::ptrdiff_t> ret = std::get_temporary_buffer<A>(std::numeric_limits<std::ptrdiff_t>::min());
       TEST_DIAGNOSTIC_POP
       assert(ret.first == NULL);
       assert(ret.second == 0);
diff --git a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
index 6935e8a9d83d5..9bd49edbb0d02 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
@@ -21,6 +21,7 @@
 
 #include <cassert>
 #include <cstddef>
+#include <limits>
 #include <memory>
 #include <utility>
 
@@ -44,12 +45,21 @@ int main(int, char**)
     {
       TEST_DIAGNOSTIC_PUSH
       // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
-      TEST_GCC_DIAGNOSTIC_IGNORED("-Wno-alloc-size-larger-than")
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Walloc-size-larger-than=")
       std::pair<int*, std::ptrdiff_t> ret = std::get_temporary_buffer<int>(-5);
       TEST_DIAGNOSTIC_POP
       assert(ret.first == NULL);
       assert(ret.second == 0);
     }
+    {
+      TEST_DIAGNOSTIC_PUSH
+      // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Walloc-size-larger-than=")
+      std::pair<int*, std::ptrdiff_t> ret = std::get_temporary_buffer<int>(std::numeric_limits<std::ptrdiff_t>::min());
+      TEST_DIAGNOSTIC_POP
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
 
   return 0;
 }


From libcxx-commits at lists.llvm.org  Wed Jul  1 02:17:22 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 02:17:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix behavior for
 `get_temporary_buffer` with non-positive size (PR #206871)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206871@github.com>
Message-ID: <6a44db22.ac469dc7.22d7c7.2d98@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/206871

From libcxx-commits at lists.llvm.org  Wed Jul  1 02:52:56 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 02:52:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Assume that <wchar.h> in
 llvm-libc has const-overloads. (PR #206805)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206805@github.com>
Message-ID: <6a44e378.f14cf1a5.9019b.d90b@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/206805

From libcxx-commits at lists.llvm.org  Wed Jul  1 03:50:34 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 03:50:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 6a909d6 - [runtimes][NFC] Re-indent
	shared library blocks (#205523)
Message-ID: <6a44f0fa.d5797644.130832.a69c@mx.google.com>


Author: Matt Arsenault
Date: 2026-07-01T12:50:29+02:00
New Revision: 6a909d68627ff6027777186699ff8fd54ae6e668

URL: https://github.com/llvm/llvm-project/commit/6a909d68627ff6027777186699ff8fd54ae6e668
DIFF: https://github.com/llvm/llvm-project/commit/6a909d68627ff6027777186699ff8fd54ae6e668.diff

LOG: [runtimes][NFC] Re-indent shared library blocks (#205523)

Re-indent the shared library target blocks that were wrapped in
if(<runtime>_SUPPORTS_SHARED_LIBRARY) in the previous commit. This is a
whitespace-only change split out from the functional change to keep that
diff minimal and reviewable.

Co-authored-by: Claude (Opus 4.8) <noreply at anthropic.com>

Added: 
    

Modified: 
    libcxx/src/CMakeLists.txt
    libcxxabi/src/CMakeLists.txt
    libunwind/src/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index d60a62a7ef5a3..e029f9f29d5ea 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -174,89 +174,89 @@ include(FindLibcCommonUtils)
 
 # Build the shared library.
 if (LIBCXX_SUPPORTS_SHARED_LIBRARY)
-add_library(cxx_shared SHARED ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
-target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(cxx_shared PUBLIC cxx-headers runtimes-libc-shared
-                                  PRIVATE ${LIBCXX_LIBRARIES}
-                                  PRIVATE llvm-libc-common-utilities)
-set_target_properties(cxx_shared
-  PROPERTIES
-    EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBCXX_ENABLE_SHARED}>,FALSE,TRUE>"
-    COMPILE_FLAGS "${LIBCXX_COMPILE_FLAGS}"
-    LINK_FLAGS    "${LIBCXX_LINK_FLAGS}"
-    OUTPUT_NAME   "${LIBCXX_SHARED_OUTPUT_NAME}"
-    VERSION       "${LIBCXX_LIBRARY_VERSION}"
-    SOVERSION     "${LIBCXX_ABI_VERSION}"
-    DEFINE_SYMBOL ""
-)
-cxx_add_common_build_flags(cxx_shared)
-
-if(ZOS)
-  add_custom_command(TARGET cxx_shared POST_BUILD
-    COMMAND
-      ${LIBCXX_SOURCE_DIR}/utils/zos_rename_dll_side_deck.sh
-      $<TARGET_LINKER_FILE_NAME:cxx_shared> $<TARGET_FILE_NAME:cxx_shared> "${LIBCXX_DLL_NAME}"
-    COMMENT "Rename dll name inside the side deck file"
-    WORKING_DIRECTORY $<TARGET_FILE_DIR:cxx_shared>
+  add_library(cxx_shared SHARED ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
+  target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+  target_link_libraries(cxx_shared PUBLIC cxx-headers runtimes-libc-shared
+                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                   PRIVATE llvm-libc-common-utilities)
+  set_target_properties(cxx_shared
+    PROPERTIES
+      EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBCXX_ENABLE_SHARED}>,FALSE,TRUE>"
+      COMPILE_FLAGS "${LIBCXX_COMPILE_FLAGS}"
+      LINK_FLAGS    "${LIBCXX_LINK_FLAGS}"
+      OUTPUT_NAME   "${LIBCXX_SHARED_OUTPUT_NAME}"
+      VERSION       "${LIBCXX_LIBRARY_VERSION}"
+      SOVERSION     "${LIBCXX_ABI_VERSION}"
+      DEFINE_SYMBOL ""
   )
-endif()
+  cxx_add_common_build_flags(cxx_shared)
+
+  if(ZOS)
+    add_custom_command(TARGET cxx_shared POST_BUILD
+      COMMAND
+        ${LIBCXX_SOURCE_DIR}/utils/zos_rename_dll_side_deck.sh
+        $<TARGET_LINKER_FILE_NAME:cxx_shared> $<TARGET_FILE_NAME:cxx_shared> "${LIBCXX_DLL_NAME}"
+      COMMENT "Rename dll name inside the side deck file"
+      WORKING_DIRECTORY $<TARGET_FILE_DIR:cxx_shared>
+    )
+  endif()
 
-# Link against libc++abi
-if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY)
-  target_link_libraries(cxx_shared PRIVATE libcxx-abi-shared-objects)
-else()
-  target_link_libraries(cxx_shared PUBLIC libcxx-abi-shared)
-endif()
+  # Link against libc++abi
+  if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY)
+    target_link_libraries(cxx_shared PRIVATE libcxx-abi-shared-objects)
+  else()
+    target_link_libraries(cxx_shared PUBLIC libcxx-abi-shared)
+  endif()
 
-# Maybe force some symbols to be weak, not weak or not exported.
-# TODO: This shouldn't depend on the platform, and ideally it should be done in the sources.
-if (APPLE AND LIBCXX_CXX_ABI MATCHES "libcxxabi$"
-          AND NOT LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY)
-  target_link_libraries(cxx_shared PRIVATE
-    "-Wl,-force_symbols_not_weak_list,${CMAKE_CURRENT_SOURCE_DIR}/../lib/notweak.exp"
-    "-Wl,-force_symbols_weak_list,${CMAKE_CURRENT_SOURCE_DIR}/../lib/weak.exp")
-endif()
+  # Maybe force some symbols to be weak, not weak or not exported.
+  # TODO: This shouldn't depend on the platform, and ideally it should be done in the sources.
+  if (APPLE AND LIBCXX_CXX_ABI MATCHES "libcxxabi$"
+            AND NOT LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY)
+    target_link_libraries(cxx_shared PRIVATE
+      "-Wl,-force_symbols_not_weak_list,${CMAKE_CURRENT_SOURCE_DIR}/../lib/notweak.exp"
+      "-Wl,-force_symbols_weak_list,${CMAKE_CURRENT_SOURCE_DIR}/../lib/weak.exp")
+  endif()
 
-# Generate a linker script in place of a libc++.so symlink.
-if (LIBCXX_ENABLE_ABI_LINKER_SCRIPT)
-  set(link_libraries)
+  # Generate a linker script in place of a libc++.so symlink.
+  if (LIBCXX_ENABLE_ABI_LINKER_SCRIPT)
+    set(link_libraries)
+
+    set(imported_libname "$<TARGET_PROPERTY:libcxx-abi-shared,IMPORTED_LIBNAME>")
+    set(output_name "$<TARGET_PROPERTY:libcxx-abi-shared,OUTPUT_NAME>")
+    string(APPEND link_libraries "${CMAKE_LINK_LIBRARY_FLAG}$<IF:$<BOOL:${imported_libname}>,${imported_libname},${output_name}>")
+
+    # TODO: Move to the same approach as above for the unwind library
+    if (LIBCXXABI_USE_LLVM_UNWINDER)
+      if (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY)
+        # libunwind is already included in libc++abi
+      elseif (TARGET unwind_shared OR HAVE_LIBUNWIND)
+        string(APPEND link_libraries " ${CMAKE_LINK_LIBRARY_FLAG}$<TARGET_PROPERTY:unwind_shared,OUTPUT_NAME>")
+      else()
+        string(APPEND link_libraries " ${CMAKE_LINK_LIBRARY_FLAG}unwind")
+      endif()
+    endif()
 
-  set(imported_libname "$<TARGET_PROPERTY:libcxx-abi-shared,IMPORTED_LIBNAME>")
-  set(output_name "$<TARGET_PROPERTY:libcxx-abi-shared,OUTPUT_NAME>")
-  string(APPEND link_libraries "${CMAKE_LINK_LIBRARY_FLAG}$<IF:$<BOOL:${imported_libname}>,${imported_libname},${output_name}>")
+    set(linker_script "INPUT($<TARGET_SONAME_FILE_NAME:cxx_shared> ${link_libraries})")
+    add_custom_command(TARGET cxx_shared POST_BUILD
+      COMMAND "${CMAKE_COMMAND}" -E remove "$<TARGET_LINKER_FILE:cxx_shared>"
+      COMMAND "${CMAKE_COMMAND}" -E echo "${linker_script}" > "$<TARGET_LINKER_FILE:cxx_shared>"
+      COMMENT "Generating linker script: '${linker_script}' as file $<TARGET_LINKER_FILE:cxx_shared>"
+      VERBATIM
+    )
+  endif()
 
-  # TODO: Move to the same approach as above for the unwind library
-  if (LIBCXXABI_USE_LLVM_UNWINDER)
-    if (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY)
-      # libunwind is already included in libc++abi
-    elseif (TARGET unwind_shared OR HAVE_LIBUNWIND)
-      string(APPEND link_libraries " ${CMAKE_LINK_LIBRARY_FLAG}$<TARGET_PROPERTY:unwind_shared,OUTPUT_NAME>")
+  if(WIN32 AND NOT MINGW AND NOT "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
+    # Since we most likely do not have a mt.exe replacement, disable the
+    # manifest bundling.  This allows a normal cmake invocation to pass which
+    # will attempt to use the manifest tool to generate the bundled manifest
+    if (${CMAKE_CXX_COMPILER_FRONTEND_VARIANT} STREQUAL "MSVC")
+      set_target_properties(cxx_shared PROPERTIES
+                            APPEND_STRING PROPERTY LINK_FLAGS " /MANIFEST:NO")
     else()
-      string(APPEND link_libraries " ${CMAKE_LINK_LIBRARY_FLAG}unwind")
+      set_target_properties(cxx_shared PROPERTIES
+                            APPEND_STRING PROPERTY LINK_FLAGS " -Xlinker /MANIFEST:NO")
     endif()
   endif()
-
-  set(linker_script "INPUT($<TARGET_SONAME_FILE_NAME:cxx_shared> ${link_libraries})")
-  add_custom_command(TARGET cxx_shared POST_BUILD
-    COMMAND "${CMAKE_COMMAND}" -E remove "$<TARGET_LINKER_FILE:cxx_shared>"
-    COMMAND "${CMAKE_COMMAND}" -E echo "${linker_script}" > "$<TARGET_LINKER_FILE:cxx_shared>"
-    COMMENT "Generating linker script: '${linker_script}' as file $<TARGET_LINKER_FILE:cxx_shared>"
-    VERBATIM
-  )
-endif()
-
-if(WIN32 AND NOT MINGW AND NOT "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
-  # Since we most likely do not have a mt.exe replacement, disable the
-  # manifest bundling.  This allows a normal cmake invocation to pass which
-  # will attempt to use the manifest tool to generate the bundled manifest
-  if (${CMAKE_CXX_COMPILER_FRONTEND_VARIANT} STREQUAL "MSVC")
-    set_target_properties(cxx_shared PROPERTIES
-                          APPEND_STRING PROPERTY LINK_FLAGS " /MANIFEST:NO")
-  else()
-    set_target_properties(cxx_shared PROPERTIES
-                          APPEND_STRING PROPERTY LINK_FLAGS " -Xlinker /MANIFEST:NO")
-  endif()
-endif()
 endif(LIBCXX_SUPPORTS_SHARED_LIBRARY)
 
 set(CMAKE_STATIC_LIBRARY_PREFIX "lib")

diff  --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index c6652a831a4ad..1889d7d5a82de 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -162,105 +162,105 @@ include(WarningFlags)
 
 # Build the shared library.
 if (LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
-add_library(cxxabi_shared_objects OBJECT EXCLUDE_FROM_ALL ${LIBCXXABI_SOURCES} ${LIBCXXABI_HEADERS})
-cxx_add_warning_flags(cxxabi_shared_objects ${LIBCXXABI_ENABLE_WERROR} ${LIBCXXABI_ENABLE_PEDANTIC})
-if (LIBCXXABI_USE_LLVM_UNWINDER)
-  if (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY OR
-      (DEFINED LIBUNWIND_ENABLE_SHARED AND NOT LIBUNWIND_ENABLE_SHARED))
-    target_link_libraries(cxxabi_shared_objects PUBLIC unwind_shared_objects) # propagate usage requirements
-    target_sources(cxxabi_shared_objects PUBLIC $<TARGET_OBJECTS:unwind_shared_objects>)
-  else()
-    target_link_libraries(cxxabi_shared_objects PUBLIC unwind_shared)
+  add_library(cxxabi_shared_objects OBJECT EXCLUDE_FROM_ALL ${LIBCXXABI_SOURCES} ${LIBCXXABI_HEADERS})
+  cxx_add_warning_flags(cxxabi_shared_objects ${LIBCXXABI_ENABLE_WERROR} ${LIBCXXABI_ENABLE_PEDANTIC})
+  if (LIBCXXABI_USE_LLVM_UNWINDER)
+    if (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY OR
+        (DEFINED LIBUNWIND_ENABLE_SHARED AND NOT LIBUNWIND_ENABLE_SHARED))
+      target_link_libraries(cxxabi_shared_objects PUBLIC unwind_shared_objects) # propagate usage requirements
+      target_sources(cxxabi_shared_objects PUBLIC $<TARGET_OBJECTS:unwind_shared_objects>)
+    else()
+      target_link_libraries(cxxabi_shared_objects PUBLIC unwind_shared)
+    endif()
   endif()
-endif()
-target_link_libraries(cxxabi_shared_objects
-  PUBLIC cxxabi-headers
-  PRIVATE cxx-headers runtimes-libc-headers ${LIBCXXABI_LIBRARIES})
-if (NOT CXX_SUPPORTS_NOSTDLIBXX_FLAG)
-  target_link_libraries(cxxabi_shared_objects PRIVATE ${LIBCXXABI_BUILTINS_LIBRARY})
-endif()
-set_target_properties(cxxabi_shared_objects
-  PROPERTIES
-    CXX_EXTENSIONS OFF
-    CXX_STANDARD 23
-    CXX_STANDARD_REQUIRED OFF # TODO: Make this REQUIRED once we don't need to accommodate the LLVM documentation builders using an ancient CMake
-    COMPILE_FLAGS "${LIBCXXABI_COMPILE_FLAGS}"
-    DEFINE_SYMBOL ""
-)
-if (CMAKE_POSITION_INDEPENDENT_CODE OR NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
-  set_target_properties(cxxabi_shared_objects PROPERTIES POSITION_INDEPENDENT_CODE ON) # must set manually because it's an object library
-endif()
-target_compile_options(cxxabi_shared_objects PRIVATE "${LIBCXXABI_ADDITIONAL_COMPILE_FLAGS}")
-
-# Build with -fsized-deallocation, which is default in recent versions of Clang.
-# TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
-target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
-
-add_library(cxxabi_shared SHARED)
-set_target_properties(cxxabi_shared
-  PROPERTIES
-    EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBCXXABI_ENABLE_SHARED}>,FALSE,TRUE>"
-    LINK_FLAGS "${LIBCXXABI_LINK_FLAGS}"
-    OUTPUT_NAME "${LIBCXXABI_SHARED_OUTPUT_NAME}"
-    SOVERSION "1"
-    VERSION "${LIBCXXABI_LIBRARY_VERSION}"
-)
-
-if (ZOS)
-  add_custom_command(TARGET cxxabi_shared POST_BUILD
-    COMMAND
-      ${LIBCXXABI_LIBCXX_PATH}/utils/zos_rename_dll_side_deck.sh
-      $<TARGET_LINKER_FILE_NAME:cxxabi_shared> $<TARGET_FILE_NAME:cxxabi_shared> "${LIBCXXABI_DLL_NAME}"
-    COMMENT "Rename dll name inside the side deck file"
-    WORKING_DIRECTORY $<TARGET_FILE_DIR:cxxabi_shared>
+  target_link_libraries(cxxabi_shared_objects
+    PUBLIC cxxabi-headers
+    PRIVATE cxx-headers runtimes-libc-headers ${LIBCXXABI_LIBRARIES})
+  if (NOT CXX_SUPPORTS_NOSTDLIBXX_FLAG)
+    target_link_libraries(cxxabi_shared_objects PRIVATE ${LIBCXXABI_BUILTINS_LIBRARY})
+  endif()
+  set_target_properties(cxxabi_shared_objects
+    PROPERTIES
+      CXX_EXTENSIONS OFF
+      CXX_STANDARD 23
+      CXX_STANDARD_REQUIRED OFF # TODO: Make this REQUIRED once we don't need to accommodate the LLVM documentation builders using an ancient CMake
+      COMPILE_FLAGS "${LIBCXXABI_COMPILE_FLAGS}"
+      DEFINE_SYMBOL ""
   )
-endif ()
-
-target_link_libraries(cxxabi_shared
-  PUBLIC cxxabi_shared_objects runtimes-libc-shared
-  PRIVATE ${LIBCXXABI_LIBRARIES})
-
-# TODO: Move this to libc++'s HandleLibCXXABI.cmake since this is effectively trying to control
-#       what libc++ re-exports.
-add_library(cxxabi-reexports INTERFACE)
-function(export_symbols file)
-  # -exported_symbols_list is only available on Apple platforms
-  if (APPLE)
-    target_link_libraries(cxxabi_shared PRIVATE "-Wl,-exported_symbols_list,${file}")
+  if (CMAKE_POSITION_INDEPENDENT_CODE OR NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
+    set_target_properties(cxxabi_shared_objects PROPERTIES POSITION_INDEPENDENT_CODE ON) # must set manually because it's an object library
   endif()
-endfunction()
+  target_compile_options(cxxabi_shared_objects PRIVATE "${LIBCXXABI_ADDITIONAL_COMPILE_FLAGS}")
+
+  # Build with -fsized-deallocation, which is default in recent versions of Clang.
+  # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
+  target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
+
+  add_library(cxxabi_shared SHARED)
+  set_target_properties(cxxabi_shared
+    PROPERTIES
+      EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBCXXABI_ENABLE_SHARED}>,FALSE,TRUE>"
+      LINK_FLAGS "${LIBCXXABI_LINK_FLAGS}"
+      OUTPUT_NAME "${LIBCXXABI_SHARED_OUTPUT_NAME}"
+      SOVERSION "1"
+      VERSION "${LIBCXXABI_LIBRARY_VERSION}"
+  )
 
-function(reexport_symbols file)
-  export_symbols("${file}")
-  # -reexported_symbols_list is only available on Apple platforms
-  if (APPLE)
-    target_link_libraries(cxxabi-reexports INTERFACE "-Wl,-reexported_symbols_list,${file}")
-  endif()
-endfunction()
+  if (ZOS)
+    add_custom_command(TARGET cxxabi_shared POST_BUILD
+      COMMAND
+        ${LIBCXXABI_LIBCXX_PATH}/utils/zos_rename_dll_side_deck.sh
+        $<TARGET_LINKER_FILE_NAME:cxxabi_shared> $<TARGET_FILE_NAME:cxxabi_shared> "${LIBCXXABI_DLL_NAME}"
+      COMMENT "Rename dll name inside the side deck file"
+      WORKING_DIRECTORY $<TARGET_FILE_DIR:cxxabi_shared>
+    )
+  endif ()
+
+  target_link_libraries(cxxabi_shared
+    PUBLIC cxxabi_shared_objects runtimes-libc-shared
+    PRIVATE ${LIBCXXABI_LIBRARIES})
+
+  # TODO: Move this to libc++'s HandleLibCXXABI.cmake since this is effectively trying to control
+  #       what libc++ re-exports.
+  add_library(cxxabi-reexports INTERFACE)
+  function(export_symbols file)
+    # -exported_symbols_list is only available on Apple platforms
+    if (APPLE)
+      target_link_libraries(cxxabi_shared PRIVATE "-Wl,-exported_symbols_list,${file}")
+    endif()
+  endfunction()
 
-export_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/symbols-not-reexported.exp")
-reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/cxxabiv1.exp")
-reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/fundamental-types.exp")
-reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/itanium-base.exp")
-reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/std-misc.exp")
+  function(reexport_symbols file)
+    export_symbols("${file}")
+    # -reexported_symbols_list is only available on Apple platforms
+    if (APPLE)
+      target_link_libraries(cxxabi-reexports INTERFACE "-Wl,-reexported_symbols_list,${file}")
+    endif()
+  endfunction()
 
-if (LIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS)
-  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/new-delete.exp")
-endif()
+  export_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/symbols-not-reexported.exp")
+  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/cxxabiv1.exp")
+  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/fundamental-types.exp")
+  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/itanium-base.exp")
+  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/std-misc.exp")
 
-# Note that std:: exception types are always defined by the library regardless of
-# whether the exception runtime machinery is provided.
-reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/std-exceptions.exp")
+  if (LIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS)
+    reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/new-delete.exp")
+  endif()
 
-if (LIBCXXABI_ENABLE_EXCEPTIONS)
-  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/itanium-exceptions.exp")
+  # Note that std:: exception types are always defined by the library regardless of
+  # whether the exception runtime machinery is provided.
+  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/std-exceptions.exp")
 
-  if ("${CMAKE_OSX_ARCHITECTURES}" MATCHES "^(armv6|armv7|armv7s)$")
-    reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/personality-sjlj.exp")
-  else()
-    reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/personality-v0.exp")
+  if (LIBCXXABI_ENABLE_EXCEPTIONS)
+    reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/itanium-exceptions.exp")
+
+    if ("${CMAKE_OSX_ARCHITECTURES}" MATCHES "^(armv6|armv7|armv7s)$")
+      reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/personality-sjlj.exp")
+    else()
+      reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/personality-v0.exp")
+    endif()
   endif()
-endif()
 endif(LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
 
 # Build the static library.

diff  --git a/libunwind/src/CMakeLists.txt b/libunwind/src/CMakeLists.txt
index 57e780afb90b2..a9d9a2ef089d6 100644
--- a/libunwind/src/CMakeLists.txt
+++ b/libunwind/src/CMakeLists.txt
@@ -126,39 +126,39 @@ include(WarningFlags)
 
 # Build the shared library.
 if (LIBUNWIND_SUPPORTS_SHARED_LIBRARY)
-add_library(unwind_shared_objects OBJECT EXCLUDE_FROM_ALL ${LIBUNWIND_SOURCES} ${LIBUNWIND_HEADERS})
-cxx_add_warning_flags(unwind_shared_objects ${LIBUNWIND_ENABLE_WERROR} ${LIBUNWIND_ENABLE_PEDANTIC})
-if(CMAKE_C_COMPILER_ID STREQUAL MSVC)
-  target_compile_options(unwind_shared_objects PRIVATE /GR-)
-else()
-  target_compile_options(unwind_shared_objects PRIVATE -fno-rtti)
-endif()
-target_compile_options(unwind_shared_objects PUBLIC "${LIBUNWIND_ADDITIONAL_COMPILE_FLAGS}")
-target_link_libraries(unwind_shared_objects
-  PUBLIC "${LIBUNWIND_ADDITIONAL_LIBRARIES}"
-  PRIVATE unwind-headers runtimes-libc-headers ${LIBUNWIND_LIBRARIES})
-set_target_properties(unwind_shared_objects
-  PROPERTIES
-    CXX_EXTENSIONS OFF
-    CXX_STANDARD 17
-    CXX_STANDARD_REQUIRED ON
-    COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}"
-)
-if (CMAKE_POSITION_INDEPENDENT_CODE OR NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
-  set_target_properties(unwind_shared_objects PROPERTIES POSITION_INDEPENDENT_CODE ON) # must set manually because it's an object library
-endif()
+  add_library(unwind_shared_objects OBJECT EXCLUDE_FROM_ALL ${LIBUNWIND_SOURCES} ${LIBUNWIND_HEADERS})
+  cxx_add_warning_flags(unwind_shared_objects ${LIBUNWIND_ENABLE_WERROR} ${LIBUNWIND_ENABLE_PEDANTIC})
+  if(CMAKE_C_COMPILER_ID STREQUAL MSVC)
+    target_compile_options(unwind_shared_objects PRIVATE /GR-)
+  else()
+    target_compile_options(unwind_shared_objects PRIVATE -fno-rtti)
+  endif()
+  target_compile_options(unwind_shared_objects PUBLIC "${LIBUNWIND_ADDITIONAL_COMPILE_FLAGS}")
+  target_link_libraries(unwind_shared_objects
+    PUBLIC "${LIBUNWIND_ADDITIONAL_LIBRARIES}"
+    PRIVATE unwind-headers runtimes-libc-headers ${LIBUNWIND_LIBRARIES})
+  set_target_properties(unwind_shared_objects
+    PROPERTIES
+      CXX_EXTENSIONS OFF
+      CXX_STANDARD 17
+      CXX_STANDARD_REQUIRED ON
+      COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}"
+  )
+  if (CMAKE_POSITION_INDEPENDENT_CODE OR NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
+    set_target_properties(unwind_shared_objects PROPERTIES POSITION_INDEPENDENT_CODE ON) # must set manually because it's an object library
+  endif()
 
-add_library(unwind_shared SHARED)
-target_link_libraries(unwind_shared PUBLIC unwind_shared_objects runtimes-libc-shared)
-set_target_properties(unwind_shared
-  PROPERTIES
-    EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBUNWIND_ENABLE_SHARED}>,FALSE,TRUE>"
-    LINK_FLAGS "${LIBUNWIND_LINK_FLAGS}"
-    LINKER_LANGUAGE C
-    OUTPUT_NAME "${LIBUNWIND_SHARED_OUTPUT_NAME}"
-    VERSION     "${LIBUNWIND_LIBRARY_VERSION}"
-    SOVERSION   "1"
-)
+  add_library(unwind_shared SHARED)
+  target_link_libraries(unwind_shared PUBLIC unwind_shared_objects runtimes-libc-shared)
+  set_target_properties(unwind_shared
+    PROPERTIES
+      EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBUNWIND_ENABLE_SHARED}>,FALSE,TRUE>"
+      LINK_FLAGS "${LIBUNWIND_LINK_FLAGS}"
+      LINKER_LANGUAGE C
+      OUTPUT_NAME "${LIBUNWIND_SHARED_OUTPUT_NAME}"
+      VERSION     "${LIBUNWIND_LIBRARY_VERSION}"
+      SOVERSION   "1"
+  )
 endif(LIBUNWIND_SUPPORTS_SHARED_LIBRARY)
 
 # Build the static library.


        

From libcxx-commits at lists.llvm.org  Wed Jul  1 03:50:37 2026
From: libcxx-commits at lists.llvm.org (Matt Arsenault via libcxx-commits)
Date: Wed, 01 Jul 2026 03:50:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libunwind] [runtimes][NFC]
 Re-indent shared library blocks (PR #205523)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205523@github.com>
Message-ID: <6a44f0fd.7d41f3a1.2f50d3.7bdd@mx.google.com>


https://github.com/arsenm closed https://github.com/llvm/llvm-project/pull/205523

From libcxx-commits at lists.llvm.org  Wed Jul  1 03:50:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 03:50:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] 6a909d6 - [runtimes][NFC] Re-indent
 shared library blocks (#205523)
Message-ID: <6a44f0fd.14d9b9dc.7e5e3.7cd8@mx.google.com>


Author: Matt Arsenault
Date: 2026-07-01T12:50:29+02:00
New Revision: 6a909d68627ff6027777186699ff8fd54ae6e668

URL: https://github.com/llvm/llvm-project/commit/6a909d68627ff6027777186699ff8fd54ae6e668
DIFF: https://github.com/llvm/llvm-project/commit/6a909d68627ff6027777186699ff8fd54ae6e668.diff

LOG: [runtimes][NFC] Re-indent shared library blocks (#205523)

Re-indent the shared library target blocks that were wrapped in
if(<runtime>_SUPPORTS_SHARED_LIBRARY) in the previous commit. This is a
whitespace-only change split out from the functional change to keep that
diff minimal and reviewable.

Co-authored-by: Claude (Opus 4.8) <noreply at anthropic.com>

Added: 
    

Modified: 
    libcxx/src/CMakeLists.txt
    libcxxabi/src/CMakeLists.txt
    libunwind/src/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index d60a62a7ef5a3..e029f9f29d5ea 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -174,89 +174,89 @@ include(FindLibcCommonUtils)
 
 # Build the shared library.
 if (LIBCXX_SUPPORTS_SHARED_LIBRARY)
-add_library(cxx_shared SHARED ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
-target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(cxx_shared PUBLIC cxx-headers runtimes-libc-shared
-                                  PRIVATE ${LIBCXX_LIBRARIES}
-                                  PRIVATE llvm-libc-common-utilities)
-set_target_properties(cxx_shared
-  PROPERTIES
-    EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBCXX_ENABLE_SHARED}>,FALSE,TRUE>"
-    COMPILE_FLAGS "${LIBCXX_COMPILE_FLAGS}"
-    LINK_FLAGS    "${LIBCXX_LINK_FLAGS}"
-    OUTPUT_NAME   "${LIBCXX_SHARED_OUTPUT_NAME}"
-    VERSION       "${LIBCXX_LIBRARY_VERSION}"
-    SOVERSION     "${LIBCXX_ABI_VERSION}"
-    DEFINE_SYMBOL ""
-)
-cxx_add_common_build_flags(cxx_shared)
-
-if(ZOS)
-  add_custom_command(TARGET cxx_shared POST_BUILD
-    COMMAND
-      ${LIBCXX_SOURCE_DIR}/utils/zos_rename_dll_side_deck.sh
-      $<TARGET_LINKER_FILE_NAME:cxx_shared> $<TARGET_FILE_NAME:cxx_shared> "${LIBCXX_DLL_NAME}"
-    COMMENT "Rename dll name inside the side deck file"
-    WORKING_DIRECTORY $<TARGET_FILE_DIR:cxx_shared>
+  add_library(cxx_shared SHARED ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
+  target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+  target_link_libraries(cxx_shared PUBLIC cxx-headers runtimes-libc-shared
+                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                   PRIVATE llvm-libc-common-utilities)
+  set_target_properties(cxx_shared
+    PROPERTIES
+      EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBCXX_ENABLE_SHARED}>,FALSE,TRUE>"
+      COMPILE_FLAGS "${LIBCXX_COMPILE_FLAGS}"
+      LINK_FLAGS    "${LIBCXX_LINK_FLAGS}"
+      OUTPUT_NAME   "${LIBCXX_SHARED_OUTPUT_NAME}"
+      VERSION       "${LIBCXX_LIBRARY_VERSION}"
+      SOVERSION     "${LIBCXX_ABI_VERSION}"
+      DEFINE_SYMBOL ""
   )
-endif()
+  cxx_add_common_build_flags(cxx_shared)
+
+  if(ZOS)
+    add_custom_command(TARGET cxx_shared POST_BUILD
+      COMMAND
+        ${LIBCXX_SOURCE_DIR}/utils/zos_rename_dll_side_deck.sh
+        $<TARGET_LINKER_FILE_NAME:cxx_shared> $<TARGET_FILE_NAME:cxx_shared> "${LIBCXX_DLL_NAME}"
+      COMMENT "Rename dll name inside the side deck file"
+      WORKING_DIRECTORY $<TARGET_FILE_DIR:cxx_shared>
+    )
+  endif()
 
-# Link against libc++abi
-if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY)
-  target_link_libraries(cxx_shared PRIVATE libcxx-abi-shared-objects)
-else()
-  target_link_libraries(cxx_shared PUBLIC libcxx-abi-shared)
-endif()
+  # Link against libc++abi
+  if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY)
+    target_link_libraries(cxx_shared PRIVATE libcxx-abi-shared-objects)
+  else()
+    target_link_libraries(cxx_shared PUBLIC libcxx-abi-shared)
+  endif()
 
-# Maybe force some symbols to be weak, not weak or not exported.
-# TODO: This shouldn't depend on the platform, and ideally it should be done in the sources.
-if (APPLE AND LIBCXX_CXX_ABI MATCHES "libcxxabi$"
-          AND NOT LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY)
-  target_link_libraries(cxx_shared PRIVATE
-    "-Wl,-force_symbols_not_weak_list,${CMAKE_CURRENT_SOURCE_DIR}/../lib/notweak.exp"
-    "-Wl,-force_symbols_weak_list,${CMAKE_CURRENT_SOURCE_DIR}/../lib/weak.exp")
-endif()
+  # Maybe force some symbols to be weak, not weak or not exported.
+  # TODO: This shouldn't depend on the platform, and ideally it should be done in the sources.
+  if (APPLE AND LIBCXX_CXX_ABI MATCHES "libcxxabi$"
+            AND NOT LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY)
+    target_link_libraries(cxx_shared PRIVATE
+      "-Wl,-force_symbols_not_weak_list,${CMAKE_CURRENT_SOURCE_DIR}/../lib/notweak.exp"
+      "-Wl,-force_symbols_weak_list,${CMAKE_CURRENT_SOURCE_DIR}/../lib/weak.exp")
+  endif()
 
-# Generate a linker script in place of a libc++.so symlink.
-if (LIBCXX_ENABLE_ABI_LINKER_SCRIPT)
-  set(link_libraries)
+  # Generate a linker script in place of a libc++.so symlink.
+  if (LIBCXX_ENABLE_ABI_LINKER_SCRIPT)
+    set(link_libraries)
+
+    set(imported_libname "$<TARGET_PROPERTY:libcxx-abi-shared,IMPORTED_LIBNAME>")
+    set(output_name "$<TARGET_PROPERTY:libcxx-abi-shared,OUTPUT_NAME>")
+    string(APPEND link_libraries "${CMAKE_LINK_LIBRARY_FLAG}$<IF:$<BOOL:${imported_libname}>,${imported_libname},${output_name}>")
+
+    # TODO: Move to the same approach as above for the unwind library
+    if (LIBCXXABI_USE_LLVM_UNWINDER)
+      if (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY)
+        # libunwind is already included in libc++abi
+      elseif (TARGET unwind_shared OR HAVE_LIBUNWIND)
+        string(APPEND link_libraries " ${CMAKE_LINK_LIBRARY_FLAG}$<TARGET_PROPERTY:unwind_shared,OUTPUT_NAME>")
+      else()
+        string(APPEND link_libraries " ${CMAKE_LINK_LIBRARY_FLAG}unwind")
+      endif()
+    endif()
 
-  set(imported_libname "$<TARGET_PROPERTY:libcxx-abi-shared,IMPORTED_LIBNAME>")
-  set(output_name "$<TARGET_PROPERTY:libcxx-abi-shared,OUTPUT_NAME>")
-  string(APPEND link_libraries "${CMAKE_LINK_LIBRARY_FLAG}$<IF:$<BOOL:${imported_libname}>,${imported_libname},${output_name}>")
+    set(linker_script "INPUT($<TARGET_SONAME_FILE_NAME:cxx_shared> ${link_libraries})")
+    add_custom_command(TARGET cxx_shared POST_BUILD
+      COMMAND "${CMAKE_COMMAND}" -E remove "$<TARGET_LINKER_FILE:cxx_shared>"
+      COMMAND "${CMAKE_COMMAND}" -E echo "${linker_script}" > "$<TARGET_LINKER_FILE:cxx_shared>"
+      COMMENT "Generating linker script: '${linker_script}' as file $<TARGET_LINKER_FILE:cxx_shared>"
+      VERBATIM
+    )
+  endif()
 
-  # TODO: Move to the same approach as above for the unwind library
-  if (LIBCXXABI_USE_LLVM_UNWINDER)
-    if (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY)
-      # libunwind is already included in libc++abi
-    elseif (TARGET unwind_shared OR HAVE_LIBUNWIND)
-      string(APPEND link_libraries " ${CMAKE_LINK_LIBRARY_FLAG}$<TARGET_PROPERTY:unwind_shared,OUTPUT_NAME>")
+  if(WIN32 AND NOT MINGW AND NOT "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
+    # Since we most likely do not have a mt.exe replacement, disable the
+    # manifest bundling.  This allows a normal cmake invocation to pass which
+    # will attempt to use the manifest tool to generate the bundled manifest
+    if (${CMAKE_CXX_COMPILER_FRONTEND_VARIANT} STREQUAL "MSVC")
+      set_target_properties(cxx_shared PROPERTIES
+                            APPEND_STRING PROPERTY LINK_FLAGS " /MANIFEST:NO")
     else()
-      string(APPEND link_libraries " ${CMAKE_LINK_LIBRARY_FLAG}unwind")
+      set_target_properties(cxx_shared PROPERTIES
+                            APPEND_STRING PROPERTY LINK_FLAGS " -Xlinker /MANIFEST:NO")
     endif()
   endif()
-
-  set(linker_script "INPUT($<TARGET_SONAME_FILE_NAME:cxx_shared> ${link_libraries})")
-  add_custom_command(TARGET cxx_shared POST_BUILD
-    COMMAND "${CMAKE_COMMAND}" -E remove "$<TARGET_LINKER_FILE:cxx_shared>"
-    COMMAND "${CMAKE_COMMAND}" -E echo "${linker_script}" > "$<TARGET_LINKER_FILE:cxx_shared>"
-    COMMENT "Generating linker script: '${linker_script}' as file $<TARGET_LINKER_FILE:cxx_shared>"
-    VERBATIM
-  )
-endif()
-
-if(WIN32 AND NOT MINGW AND NOT "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
-  # Since we most likely do not have a mt.exe replacement, disable the
-  # manifest bundling.  This allows a normal cmake invocation to pass which
-  # will attempt to use the manifest tool to generate the bundled manifest
-  if (${CMAKE_CXX_COMPILER_FRONTEND_VARIANT} STREQUAL "MSVC")
-    set_target_properties(cxx_shared PROPERTIES
-                          APPEND_STRING PROPERTY LINK_FLAGS " /MANIFEST:NO")
-  else()
-    set_target_properties(cxx_shared PROPERTIES
-                          APPEND_STRING PROPERTY LINK_FLAGS " -Xlinker /MANIFEST:NO")
-  endif()
-endif()
 endif(LIBCXX_SUPPORTS_SHARED_LIBRARY)
 
 set(CMAKE_STATIC_LIBRARY_PREFIX "lib")

diff  --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index c6652a831a4ad..1889d7d5a82de 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -162,105 +162,105 @@ include(WarningFlags)
 
 # Build the shared library.
 if (LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
-add_library(cxxabi_shared_objects OBJECT EXCLUDE_FROM_ALL ${LIBCXXABI_SOURCES} ${LIBCXXABI_HEADERS})
-cxx_add_warning_flags(cxxabi_shared_objects ${LIBCXXABI_ENABLE_WERROR} ${LIBCXXABI_ENABLE_PEDANTIC})
-if (LIBCXXABI_USE_LLVM_UNWINDER)
-  if (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY OR
-      (DEFINED LIBUNWIND_ENABLE_SHARED AND NOT LIBUNWIND_ENABLE_SHARED))
-    target_link_libraries(cxxabi_shared_objects PUBLIC unwind_shared_objects) # propagate usage requirements
-    target_sources(cxxabi_shared_objects PUBLIC $<TARGET_OBJECTS:unwind_shared_objects>)
-  else()
-    target_link_libraries(cxxabi_shared_objects PUBLIC unwind_shared)
+  add_library(cxxabi_shared_objects OBJECT EXCLUDE_FROM_ALL ${LIBCXXABI_SOURCES} ${LIBCXXABI_HEADERS})
+  cxx_add_warning_flags(cxxabi_shared_objects ${LIBCXXABI_ENABLE_WERROR} ${LIBCXXABI_ENABLE_PEDANTIC})
+  if (LIBCXXABI_USE_LLVM_UNWINDER)
+    if (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY OR
+        (DEFINED LIBUNWIND_ENABLE_SHARED AND NOT LIBUNWIND_ENABLE_SHARED))
+      target_link_libraries(cxxabi_shared_objects PUBLIC unwind_shared_objects) # propagate usage requirements
+      target_sources(cxxabi_shared_objects PUBLIC $<TARGET_OBJECTS:unwind_shared_objects>)
+    else()
+      target_link_libraries(cxxabi_shared_objects PUBLIC unwind_shared)
+    endif()
   endif()
-endif()
-target_link_libraries(cxxabi_shared_objects
-  PUBLIC cxxabi-headers
-  PRIVATE cxx-headers runtimes-libc-headers ${LIBCXXABI_LIBRARIES})
-if (NOT CXX_SUPPORTS_NOSTDLIBXX_FLAG)
-  target_link_libraries(cxxabi_shared_objects PRIVATE ${LIBCXXABI_BUILTINS_LIBRARY})
-endif()
-set_target_properties(cxxabi_shared_objects
-  PROPERTIES
-    CXX_EXTENSIONS OFF
-    CXX_STANDARD 23
-    CXX_STANDARD_REQUIRED OFF # TODO: Make this REQUIRED once we don't need to accommodate the LLVM documentation builders using an ancient CMake
-    COMPILE_FLAGS "${LIBCXXABI_COMPILE_FLAGS}"
-    DEFINE_SYMBOL ""
-)
-if (CMAKE_POSITION_INDEPENDENT_CODE OR NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
-  set_target_properties(cxxabi_shared_objects PROPERTIES POSITION_INDEPENDENT_CODE ON) # must set manually because it's an object library
-endif()
-target_compile_options(cxxabi_shared_objects PRIVATE "${LIBCXXABI_ADDITIONAL_COMPILE_FLAGS}")
-
-# Build with -fsized-deallocation, which is default in recent versions of Clang.
-# TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
-target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
-
-add_library(cxxabi_shared SHARED)
-set_target_properties(cxxabi_shared
-  PROPERTIES
-    EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBCXXABI_ENABLE_SHARED}>,FALSE,TRUE>"
-    LINK_FLAGS "${LIBCXXABI_LINK_FLAGS}"
-    OUTPUT_NAME "${LIBCXXABI_SHARED_OUTPUT_NAME}"
-    SOVERSION "1"
-    VERSION "${LIBCXXABI_LIBRARY_VERSION}"
-)
-
-if (ZOS)
-  add_custom_command(TARGET cxxabi_shared POST_BUILD
-    COMMAND
-      ${LIBCXXABI_LIBCXX_PATH}/utils/zos_rename_dll_side_deck.sh
-      $<TARGET_LINKER_FILE_NAME:cxxabi_shared> $<TARGET_FILE_NAME:cxxabi_shared> "${LIBCXXABI_DLL_NAME}"
-    COMMENT "Rename dll name inside the side deck file"
-    WORKING_DIRECTORY $<TARGET_FILE_DIR:cxxabi_shared>
+  target_link_libraries(cxxabi_shared_objects
+    PUBLIC cxxabi-headers
+    PRIVATE cxx-headers runtimes-libc-headers ${LIBCXXABI_LIBRARIES})
+  if (NOT CXX_SUPPORTS_NOSTDLIBXX_FLAG)
+    target_link_libraries(cxxabi_shared_objects PRIVATE ${LIBCXXABI_BUILTINS_LIBRARY})
+  endif()
+  set_target_properties(cxxabi_shared_objects
+    PROPERTIES
+      CXX_EXTENSIONS OFF
+      CXX_STANDARD 23
+      CXX_STANDARD_REQUIRED OFF # TODO: Make this REQUIRED once we don't need to accommodate the LLVM documentation builders using an ancient CMake
+      COMPILE_FLAGS "${LIBCXXABI_COMPILE_FLAGS}"
+      DEFINE_SYMBOL ""
   )
-endif ()
-
-target_link_libraries(cxxabi_shared
-  PUBLIC cxxabi_shared_objects runtimes-libc-shared
-  PRIVATE ${LIBCXXABI_LIBRARIES})
-
-# TODO: Move this to libc++'s HandleLibCXXABI.cmake since this is effectively trying to control
-#       what libc++ re-exports.
-add_library(cxxabi-reexports INTERFACE)
-function(export_symbols file)
-  # -exported_symbols_list is only available on Apple platforms
-  if (APPLE)
-    target_link_libraries(cxxabi_shared PRIVATE "-Wl,-exported_symbols_list,${file}")
+  if (CMAKE_POSITION_INDEPENDENT_CODE OR NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
+    set_target_properties(cxxabi_shared_objects PROPERTIES POSITION_INDEPENDENT_CODE ON) # must set manually because it's an object library
   endif()
-endfunction()
+  target_compile_options(cxxabi_shared_objects PRIVATE "${LIBCXXABI_ADDITIONAL_COMPILE_FLAGS}")
+
+  # Build with -fsized-deallocation, which is default in recent versions of Clang.
+  # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
+  target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
+
+  add_library(cxxabi_shared SHARED)
+  set_target_properties(cxxabi_shared
+    PROPERTIES
+      EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBCXXABI_ENABLE_SHARED}>,FALSE,TRUE>"
+      LINK_FLAGS "${LIBCXXABI_LINK_FLAGS}"
+      OUTPUT_NAME "${LIBCXXABI_SHARED_OUTPUT_NAME}"
+      SOVERSION "1"
+      VERSION "${LIBCXXABI_LIBRARY_VERSION}"
+  )
 
-function(reexport_symbols file)
-  export_symbols("${file}")
-  # -reexported_symbols_list is only available on Apple platforms
-  if (APPLE)
-    target_link_libraries(cxxabi-reexports INTERFACE "-Wl,-reexported_symbols_list,${file}")
-  endif()
-endfunction()
+  if (ZOS)
+    add_custom_command(TARGET cxxabi_shared POST_BUILD
+      COMMAND
+        ${LIBCXXABI_LIBCXX_PATH}/utils/zos_rename_dll_side_deck.sh
+        $<TARGET_LINKER_FILE_NAME:cxxabi_shared> $<TARGET_FILE_NAME:cxxabi_shared> "${LIBCXXABI_DLL_NAME}"
+      COMMENT "Rename dll name inside the side deck file"
+      WORKING_DIRECTORY $<TARGET_FILE_DIR:cxxabi_shared>
+    )
+  endif ()
+
+  target_link_libraries(cxxabi_shared
+    PUBLIC cxxabi_shared_objects runtimes-libc-shared
+    PRIVATE ${LIBCXXABI_LIBRARIES})
+
+  # TODO: Move this to libc++'s HandleLibCXXABI.cmake since this is effectively trying to control
+  #       what libc++ re-exports.
+  add_library(cxxabi-reexports INTERFACE)
+  function(export_symbols file)
+    # -exported_symbols_list is only available on Apple platforms
+    if (APPLE)
+      target_link_libraries(cxxabi_shared PRIVATE "-Wl,-exported_symbols_list,${file}")
+    endif()
+  endfunction()
 
-export_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/symbols-not-reexported.exp")
-reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/cxxabiv1.exp")
-reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/fundamental-types.exp")
-reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/itanium-base.exp")
-reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/std-misc.exp")
+  function(reexport_symbols file)
+    export_symbols("${file}")
+    # -reexported_symbols_list is only available on Apple platforms
+    if (APPLE)
+      target_link_libraries(cxxabi-reexports INTERFACE "-Wl,-reexported_symbols_list,${file}")
+    endif()
+  endfunction()
 
-if (LIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS)
-  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/new-delete.exp")
-endif()
+  export_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/symbols-not-reexported.exp")
+  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/cxxabiv1.exp")
+  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/fundamental-types.exp")
+  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/itanium-base.exp")
+  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/std-misc.exp")
 
-# Note that std:: exception types are always defined by the library regardless of
-# whether the exception runtime machinery is provided.
-reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/std-exceptions.exp")
+  if (LIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS)
+    reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/new-delete.exp")
+  endif()
 
-if (LIBCXXABI_ENABLE_EXCEPTIONS)
-  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/itanium-exceptions.exp")
+  # Note that std:: exception types are always defined by the library regardless of
+  # whether the exception runtime machinery is provided.
+  reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/std-exceptions.exp")
 
-  if ("${CMAKE_OSX_ARCHITECTURES}" MATCHES "^(armv6|armv7|armv7s)$")
-    reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/personality-sjlj.exp")
-  else()
-    reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/personality-v0.exp")
+  if (LIBCXXABI_ENABLE_EXCEPTIONS)
+    reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/itanium-exceptions.exp")
+
+    if ("${CMAKE_OSX_ARCHITECTURES}" MATCHES "^(armv6|armv7|armv7s)$")
+      reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/personality-sjlj.exp")
+    else()
+      reexport_symbols("${CMAKE_CURRENT_SOURCE_DIR}/../lib/personality-v0.exp")
+    endif()
   endif()
-endif()
 endif(LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
 
 # Build the static library.

diff  --git a/libunwind/src/CMakeLists.txt b/libunwind/src/CMakeLists.txt
index 57e780afb90b2..a9d9a2ef089d6 100644
--- a/libunwind/src/CMakeLists.txt
+++ b/libunwind/src/CMakeLists.txt
@@ -126,39 +126,39 @@ include(WarningFlags)
 
 # Build the shared library.
 if (LIBUNWIND_SUPPORTS_SHARED_LIBRARY)
-add_library(unwind_shared_objects OBJECT EXCLUDE_FROM_ALL ${LIBUNWIND_SOURCES} ${LIBUNWIND_HEADERS})
-cxx_add_warning_flags(unwind_shared_objects ${LIBUNWIND_ENABLE_WERROR} ${LIBUNWIND_ENABLE_PEDANTIC})
-if(CMAKE_C_COMPILER_ID STREQUAL MSVC)
-  target_compile_options(unwind_shared_objects PRIVATE /GR-)
-else()
-  target_compile_options(unwind_shared_objects PRIVATE -fno-rtti)
-endif()
-target_compile_options(unwind_shared_objects PUBLIC "${LIBUNWIND_ADDITIONAL_COMPILE_FLAGS}")
-target_link_libraries(unwind_shared_objects
-  PUBLIC "${LIBUNWIND_ADDITIONAL_LIBRARIES}"
-  PRIVATE unwind-headers runtimes-libc-headers ${LIBUNWIND_LIBRARIES})
-set_target_properties(unwind_shared_objects
-  PROPERTIES
-    CXX_EXTENSIONS OFF
-    CXX_STANDARD 17
-    CXX_STANDARD_REQUIRED ON
-    COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}"
-)
-if (CMAKE_POSITION_INDEPENDENT_CODE OR NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
-  set_target_properties(unwind_shared_objects PROPERTIES POSITION_INDEPENDENT_CODE ON) # must set manually because it's an object library
-endif()
+  add_library(unwind_shared_objects OBJECT EXCLUDE_FROM_ALL ${LIBUNWIND_SOURCES} ${LIBUNWIND_HEADERS})
+  cxx_add_warning_flags(unwind_shared_objects ${LIBUNWIND_ENABLE_WERROR} ${LIBUNWIND_ENABLE_PEDANTIC})
+  if(CMAKE_C_COMPILER_ID STREQUAL MSVC)
+    target_compile_options(unwind_shared_objects PRIVATE /GR-)
+  else()
+    target_compile_options(unwind_shared_objects PRIVATE -fno-rtti)
+  endif()
+  target_compile_options(unwind_shared_objects PUBLIC "${LIBUNWIND_ADDITIONAL_COMPILE_FLAGS}")
+  target_link_libraries(unwind_shared_objects
+    PUBLIC "${LIBUNWIND_ADDITIONAL_LIBRARIES}"
+    PRIVATE unwind-headers runtimes-libc-headers ${LIBUNWIND_LIBRARIES})
+  set_target_properties(unwind_shared_objects
+    PROPERTIES
+      CXX_EXTENSIONS OFF
+      CXX_STANDARD 17
+      CXX_STANDARD_REQUIRED ON
+      COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}"
+  )
+  if (CMAKE_POSITION_INDEPENDENT_CODE OR NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
+    set_target_properties(unwind_shared_objects PROPERTIES POSITION_INDEPENDENT_CODE ON) # must set manually because it's an object library
+  endif()
 
-add_library(unwind_shared SHARED)
-target_link_libraries(unwind_shared PUBLIC unwind_shared_objects runtimes-libc-shared)
-set_target_properties(unwind_shared
-  PROPERTIES
-    EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBUNWIND_ENABLE_SHARED}>,FALSE,TRUE>"
-    LINK_FLAGS "${LIBUNWIND_LINK_FLAGS}"
-    LINKER_LANGUAGE C
-    OUTPUT_NAME "${LIBUNWIND_SHARED_OUTPUT_NAME}"
-    VERSION     "${LIBUNWIND_LIBRARY_VERSION}"
-    SOVERSION   "1"
-)
+  add_library(unwind_shared SHARED)
+  target_link_libraries(unwind_shared PUBLIC unwind_shared_objects runtimes-libc-shared)
+  set_target_properties(unwind_shared
+    PROPERTIES
+      EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBUNWIND_ENABLE_SHARED}>,FALSE,TRUE>"
+      LINK_FLAGS "${LIBUNWIND_LINK_FLAGS}"
+      LINKER_LANGUAGE C
+      OUTPUT_NAME "${LIBUNWIND_SHARED_OUTPUT_NAME}"
+      VERSION     "${LIBUNWIND_LIBRARY_VERSION}"
+      SOVERSION   "1"
+  )
 endif(LIBUNWIND_SUPPORTS_SHARED_LIBRARY)
 
 # Build the static library.


        

From libcxx-commits at lists.llvm.org  Wed Jul  1 04:20:30 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 04:20:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] a5c06c0 - [libc++] Implement LWG3662
 basic_string::append/assign(NTBS, pos, n) suboptimal (#206320)
Message-ID: <6a44f7fe.ef0e1708.2770f7.3669@mx.google.com>


Author: Connector Switch
Date: 2026-07-01T19:20:26+08:00
New Revision: a5c06c0b4c7d713ef7ea63cd43554cc674dd167d

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

LOG: [libc++] Implement LWG3662 basic_string::append/assign(NTBS, pos, n) suboptimal (#206320)

Closes https://github.com/llvm/llvm-project/issues/189809

This patch implements LWG 3662 and adds the related functional and
nonnull tests.

---------

Co-authored-by: A. Jiang <de34 at live.cn>

Added: 
    libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp
    libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp

Modified: 
    libcxx/docs/Status/Cxx26Issues.csv
    libcxx/include/string
    libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 10cad5d52e2f1..3f2a446f7e1f3 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -264,7 +264,7 @@
 "`LWG2746 <https://wg21.link/LWG2746>`__","Inconsistency between requirements for ``emplace`` between ``optional`` and ``variant``","2026-03 (Croydon)","","","`#189806 <https://github.com/llvm/llvm-project/issues/189806>`__",""
 "`LWG3504 <https://wg21.link/LWG3504>`__","``condition_variable::wait_for`` is overspecified","2026-03 (Croydon)","","","`#189807 <https://github.com/llvm/llvm-project/issues/189807>`__",""
 "`LWG3599 <https://wg21.link/LWG3599>`__","The ``const`` overload of ``lazy_split_view::begin`` should be constrained by ``const Pattern``","2026-03 (Croydon)","","","`#189808 <https://github.com/llvm/llvm-project/issues/189808>`__",""
-"`LWG3662 <https://wg21.link/LWG3662>`__","``basic_string::append/assign(NTBS, pos, n)`` suboptimal","2026-03 (Croydon)","","","`#189809 <https://github.com/llvm/llvm-project/issues/189809>`__",""
+"`LWG3662 <https://wg21.link/LWG3662>`__","``basic_string::append/assign(NTBS, pos, n)`` suboptimal","2026-03 (Croydon)","|Complete|","23","`#189809 <https://github.com/llvm/llvm-project/issues/189809>`__",""
 "`LWG3777 <https://wg21.link/LWG3777>`__","Common ``cartesian_product_view`` produces an invalid range if the first range is input and one of the ranges is empty","2026-03 (Croydon)","","","`#189810 <https://github.com/llvm/llvm-project/issues/189810>`__",""
 "`LWG3797 <https://wg21.link/LWG3797>`__","``elements_view`` insufficiently constrained","2026-03 (Croydon)","","","`#189811 <https://github.com/llvm/llvm-project/issues/189811>`__",""
 "`LWG3831 <https://wg21.link/LWG3831>`__","Two-digit formatting of negative ``year`` is ambiguous","2026-03 (Croydon)","","","`#189812 <https://github.com/llvm/llvm-project/issues/189812>`__",""

diff  --git a/libcxx/include/string b/libcxx/include/string
index e5bc854cd34f3..6516d26e288a9 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -201,6 +201,7 @@ public:
         basic_string& append(const T& t, size_type pos, size_type n=npos);                      // C++17, constexpr since C++20
     basic_string& append(const value_type* s, size_type n);                                     // constexpr since C++20
     basic_string& append(const value_type* s);                                                  // constexpr since C++20
+    basic_string& append(const value_type* s, size_type pos, size_type n);                      // constexpr since C++20
     basic_string& append(size_type n, value_type c);                                            // constexpr since C++20
     template<class InputIterator>
         basic_string& append(InputIterator first, InputIterator last);                          // constexpr since C++20
@@ -224,6 +225,7 @@ public:
         basic_string& assign(const T& t, size_type pos, size_type n=npos);                      // C++17, constexpr since C++20
     basic_string& assign(const value_type* s, size_type n);                                     // constexpr since C++20
     basic_string& assign(const value_type* s);                                                  // constexpr since C++20
+    basic_string& assign(const value_type* s, size_type pos, size_type n);                      // constexpr since C++20
     basic_string& assign(size_type n, value_type c);                                            // constexpr since C++20
     template<class InputIterator>
         basic_string& assign(InputIterator first, InputIterator last);                          // constexpr since C++20
@@ -1342,6 +1344,8 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s, size_type __n)
       _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero");
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s, size_type __pos, size_type __n)
+      _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero");
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(size_type __n, value_type __c);
 
   template <class _InputIterator, __enable_if_t<__has_exactly_input_iterator_category<_InputIterator>::value, int> = 0>
@@ -1468,6 +1472,8 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(const value_type* __s, size_type __n)
       _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero");
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(const value_type* __s, size_type __pos, size_type __n)
+      _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero");
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(size_type __n, value_type __c);
 
   template <class _InputIterator, __enable_if_t<__has_exactly_input_iterator_category<_InputIterator>::value, int> = 0>
@@ -2757,6 +2763,13 @@ basic_string<_CharT, _Traits, _Allocator>::assign(const value_type* __s, size_ty
   return (__builtin_constant_p(__n) && __fits_in_sso(__n)) ? __assign_short(__s, __n) : __assign_external(__s, __n);
 }
 
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string<_CharT, _Traits, _Allocator>&
+basic_string<_CharT, _Traits, _Allocator>::assign(const value_type* __s, size_type __pos, size_type __n) {
+  _LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string::assign received nullptr");
+  return assign(__self_view(__s).substr(__pos, __n));
+}
+
 template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string<_CharT, _Traits, _Allocator>&
 basic_string<_CharT, _Traits, _Allocator>::assign(size_type __n, value_type __c) {
@@ -3024,6 +3037,13 @@ basic_string<_CharT, _Traits, _Allocator>::append(const value_type* __s) {
   return append(__s, traits_type::length(__s));
 }
 
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string<_CharT, _Traits, _Allocator>&
+basic_string<_CharT, _Traits, _Allocator>::append(const value_type* __s, size_type __pos, size_type __n) {
+  _LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string::append received nullptr");
+  return append(__self_view(__s).substr(__pos, __n));
+}
+
 // insert
 
 template <class _CharT, class _Traits, class _Allocator>

diff  --git a/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp b/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp
index d51ce1fcf2ba6..bdb3189b7fd35 100644
--- a/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp
@@ -25,8 +25,12 @@ void func() {
   std::string str2(np, std::allocator<char>{}); // expected-warning {{null passed}}
   str2 = np;                                    // expected-warning {{null passed}}
   str2 += np;                                   // expected-warning {{null passed}}
+  str2.assign(np, 1);                           // expected-warning {{null passed}}
   str2.assign(np);                              // expected-warning {{null passed}}
+  str2.assign(np, 0, 1);                        // expected-warning {{null passed}}
+  str2.append(np, 1);                           // expected-warning {{null passed}}
   str2.append(np);                              // expected-warning {{null passed}}
+  str2.append(np, 0, 1);                        // expected-warning {{null passed}}
   str2.insert(0, np);                           // expected-warning {{null passed}}
   str2.find(np);                                // expected-warning {{null passed}}
   str2.rfind(np);                               // expected-warning {{null passed}}

diff  --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp
new file mode 100644
index 0000000000000..e70a11fced589
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/pointer_size_size.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// constexpr basic_string& append(const charT* s, size_type pos, size_type n);
+
+#include <cassert>
+#include <stdexcept>
+#include <string>
+
+#include "asan_testing.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void
+test(S s, const typename S::value_type* str, typename S::size_type pos, typename S::size_type n, S expected) {
+  typename S::size_type str_len = S::traits_type::length(str);
+  if (pos <= str_len) {
+    s.append(str, pos, n);
+    LIBCPP_ASSERT(s.__invariants());
+    assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
+  }
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  else if (!TEST_IS_CONSTANT_EVALUATED) {
+    try {
+      s.append(str, pos, n);
+      assert(false);
+    } catch (std::out_of_range&) {
+      assert(pos > str_len);
+    }
+  }
+#endif
+}
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void test_string() {
+  test(S(), "", 0, 0, S());
+  test(S(), "", 1, 0, S("not happening"));
+  test(S(), "12345", 0, 3, S("123"));
+  test(S(), "12345", 1, 4, S("2345"));
+  test(S(), "12345", 3, 15, S("45"));
+  test(S(), "12345", 5, 15, S(""));
+  test(S(), "12345", 6, 15, S("not happening"));
+  test(S(), "12345678901234567890", 0, 0, S());
+  test(S(), "12345678901234567890", 1, 1, S("2"));
+  test(S(), "12345678901234567890", 2, 3, S("345"));
+  test(S(), "12345678901234567890", 12, 13, S("34567890"));
+  test(S(), "12345678901234567890", 21, 13, S("not happening"));
+
+  test(S("12345"), "", 0, 0, S("12345"));
+  test(S("12345"), "12345", 2, 2, S("1234534"));
+  test(S("12345"), "1234567890", 0, 100, S("123451234567890"));
+
+  test(S("12345678901234567890"), "", 0, 0, S("12345678901234567890"));
+  test(S("12345678901234567890"), "12345", 1, 3, S("12345678901234567890234"));
+  test(S("12345678901234567890"), "12345678901234567890", 5, 10, S("123456789012345678906789012345"));
+
+  // Starting from long string (no SSO)
+  test(S("123456789012345678901234567890"), "", 0, 0, S("123456789012345678901234567890"));
+  test(S("123456789012345678901234567890"), "12345", 1, 3, S("123456789012345678901234567890234"));
+  test(S("123456789012345678901234567890"),
+       "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
+       5,
+       10,
+       S("1234567890123456789012345678906789012345"));
+}
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  test_string<std::string>();
+#if TEST_STD_VER >= 11
+  test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, limited_allocator<char, 96>>>();
+#endif
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 20
+  static_assert(test());
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
new file mode 100644
index 0000000000000..a6ba75653d1a4
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/pointer_size_size.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// constexpr basic_string& assign(const charT* s, size_type pos, size_type n);
+
+#include <cassert>
+#include <stdexcept>
+#include <string>
+
+#include "asan_testing.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void
+test(S s, const typename S::value_type* str, typename S::size_type pos, typename S::size_type n, S expected) {
+  typename S::size_type str_len = S::traits_type::length(str);
+  if (pos <= str_len) {
+    s.assign(str, pos, n);
+    LIBCPP_ASSERT(s.__invariants());
+    assert(s == expected);
+    LIBCPP_ASSERT(is_string_asan_correct(s));
+  }
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  else if (!TEST_IS_CONSTANT_EVALUATED) {
+    try {
+      s.assign(str, pos, n);
+      assert(false);
+    } catch (std::out_of_range&) {
+      assert(pos > str_len);
+    }
+  }
+#endif
+}
+
+template <class S>
+TEST_CONSTEXPR_CXX20 void test_string() {
+  test(S(), "", 0, 0, S());
+  test(S(), "", 1, 0, S("not happening"));
+  test(S(), "12345", 0, 3, S("123"));
+  test(S(), "12345", 1, 4, S("2345"));
+  test(S(), "12345", 3, 15, S("45"));
+  test(S(), "12345", 5, 15, S(""));
+  test(S(), "12345", 6, 15, S("not happening"));
+  test(S(), "12345678901234567890", 0, 0, S());
+  test(S(), "12345678901234567890", 1, 1, S("2"));
+  test(S(), "12345678901234567890", 2, 3, S("345"));
+  test(S(), "12345678901234567890", 12, 13, S("34567890"));
+  test(S(), "12345678901234567890", 21, 13, S("not happening"));
+
+  test(S("12345"), "", 0, 0, S());
+  test(S("12345"), "12345", 2, 2, S("34"));
+  test(S("12345"), "1234567890", 0, 100, S("1234567890"));
+
+  test(S("12345678901234567890"), "", 0, 0, S());
+  test(S("12345678901234567890"), "12345", 1, 3, S("234"));
+  test(S("12345678901234567890"),
+       "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
+       5,
+       10,
+       S("6789012345"));
+}
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  test_string<std::string>();
+#if TEST_STD_VER >= 11
+  test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, safe_allocator<char>>>();
+  test_string<std::basic_string<char, std::char_traits<char>, limited_allocator<char, 33>>>();
+#endif
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 20
+  static_assert(test());
+#endif
+
+  return 0;
+}


        

From libcxx-commits at lists.llvm.org  Wed Jul  1 04:20:35 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Wed, 01 Jul 2026 04:20:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3662
 basic_string::append/assign(NTBS, pos, n) suboptimal (PR #206320)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206320@github.com>
Message-ID: <6a44f803.62e6fbbf.207660.2f64@mx.google.com>


https://github.com/c8ef closed https://github.com/llvm/llvm-project/pull/206320

From libcxx-commits at lists.llvm.org  Wed Jul  1 05:21:02 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 05:21:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
 `elements_view` (PR #206589)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206589@github.com>
Message-ID: <6a45062e.67c1d1c3.1e52d3.f9d8@mx.google.com>


================
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Test the libc++ extension that std::ranges::transform_view and std::views::transform are marked as [[nodiscard]].
----------------
frederick-vs-ja wrote:

The current comment is wrong.
```suggestion
// Test the libc++ extension that std::ranges::elements_view and std::views::elements are marked as [[nodiscard]].
```

https://github.com/llvm/llvm-project/pull/206589

From libcxx-commits at lists.llvm.org  Wed Jul  1 05:25:43 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 05:25:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] Cover byteswap _BitInt
 padding on every ABI with width 72 (PR #206360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206360@github.com>
Message-ID: <6a450747.7f17e68b.39a92.1690@mx.google.com>


================
@@ -103,23 +103,21 @@ void test_unsigned_56() {
 
 // Same dispatch-availability guard as test_unsigned_65 above.
 #  if TEST_HAS_BUILTIN(__builtin_bswapg) || !defined(TEST_HAS_NO_INT128)
-#    if __BITINT_MAXWIDTH__ >= 80
-void test_unsigned_80() {
-  // sizeof(_BitInt(80)) == 16 on x86_64; 48 padding bits. Width 80 is also
-  // a multiple of 16, so bswapg would accept it without the static_assert.
-  unsigned _BitInt(80) v = 0;
+// 72 value bits leave padding on every ABI, unlike _BitInt(96), which is
+// exactly 12 bytes with no padding on 32-bit x86 (#205295).
----------------
frederick-vs-ja wrote:

Mostly LGTM. If we don't test `_BitInt(96)`, we can just avoid mentioning it in comments.
```suggestion
// 72 value bits leave padding on every ABI.
```

https://github.com/llvm/llvm-project/pull/206360

From libcxx-commits at lists.llvm.org  Wed Jul  1 05:38:17 2026
From: libcxx-commits at lists.llvm.org (Xavier Roche via libcxx-commits)
Date: Wed, 01 Jul 2026 05:38:17 -0700 (PDT)
Subject: [libcxx-commits] [clang] [compiler-rt] [libcxx] [Clang] Support
 atomic operations on _BitInt(N) (PR #204815)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204815@github.com>
Message-ID: <6a450a39.30759430.1a950e.b404@mx.google.com>


https://github.com/xroche updated https://github.com/llvm/llvm-project/pull/204815

>From 5c7fc853303e60e1c39d6ea49d5fa6ad445abc16 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Fri, 19 Jun 2026 13:59:45 +0200
Subject: [PATCH 1/9] [Clang][POC] Atomic operations on _BitInt(N)

_BitInt(N) was rejected by every atomic path: the _Atomic(...) type
specifier, the __c11_atomic_*/__atomic_* builtins, and transitively
std::atomic. Two blanket isBitIntType() checks disabled it, dating to the
type's introduction (the __atomic builtin half is D84049). __int128, the
closest analogue, was allowed at both sites.

Lift both rejections so _BitInt flows through the normal integer path.
load, store, exchange, compare-exchange, and bitwise read-modify-write are
then correct at every width through the existing canonicalizing store and
the libcall fallback.

Arithmetic read-modify-write needs more care. A single atomicrmw on the
padded memory integer of a non-byte-aligned width (e.g. _BitInt(37) in an
i64) carries into the padding bits, leaving a non-canonical value that
breaks a later compare-exchange and gives wrong signed min/max. A wide
arithmetic fetch (e.g. _BitInt(256)) hit an llvm_unreachable in the libcall
path, a compiler crash. Both are fixed by emitting a compare-exchange loop
that computes the new value at width N via llvm::buildAtomicRMWValue and
writes back a canonical representation, reusing the existing
EmitAtomicCompareExchange helper, which selects the inline cmpxchg or the
__atomic_compare_exchange libcall by size. No-padding inline widths (64,
128) keep the direct atomicrmw fast path.

The libc++ bit-int.verify.cpp test only asserted that Clang rejection, so it
is removed as obsolete. It is not replaced with a positive test here: the
libc++ premerge matrix builds tests with a pinned clang that predates this
change. Whether libc++ should expose atomic _BitInt is a separate design
question for the P3666R4 discussion.

Verified against gcc-14: identical size and alignment for all widths, and
cross-compiler compare-exchange interop in both directions, confirming the
padding canonicalization matches.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/docs/LanguageExtensions.rst             |   9 +
 clang/docs/ReleaseNotes.rst                   |   6 +
 clang/lib/CodeGen/CGAtomic.cpp                | 211 ++++++++++++++++++
 clang/lib/Sema/SemaChecking.cpp               |   5 -
 clang/lib/Sema/SemaType.cpp                   |   2 -
 clang/test/CodeGen/atomic-bitint.c            |  90 ++++++++
 clang/test/Sema/builtins.c                    |   4 +-
 clang/test/SemaCXX/ext-int.cpp                |  10 +-
 libcxx/test/libcxx/atomics/bit-int.verify.cpp |  22 --
 9 files changed, 322 insertions(+), 37 deletions(-)
 create mode 100644 clang/test/CodeGen/atomic-bitint.c
 delete mode 100644 libcxx/test/libcxx/atomics/bit-int.verify.cpp

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index d79d82a175c68..5ff076d3e48ad 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -451,6 +451,15 @@ favor of the standard type.
 Note: the ABI for ``_BitInt(N)`` is still in the process of being stabilized,
 so this type should not yet be used in interfaces that require ABI stability.
 
+``_BitInt(N)`` may be used as an atomic type: ``_Atomic(_BitInt(N))``, the
+``__c11_atomic_*`` and ``__atomic_*`` builtins, and ``std::atomic`` all accept
+it for any width. Widths the target cannot operate on inline are lowered to the
+``__atomic_*`` libcalls. For a width whose representation has padding bits (``N``
+not a multiple of the type's alignment, e.g. ``_BitInt(37)``), arithmetic
+read-modify-write operations are emitted as a compare-exchange loop that computes
+at width ``N``, so the result wraps modulo ``2**N`` and the padding bits stay
+canonical.
+
 C keywords supported in all language modes
 ------------------------------------------
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7f056abfbbe24..8692da8830dff 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -265,6 +265,12 @@ Non-comprehensive list of changes in this release
 - Added support for floating point and pointer values in most ``__atomic_``
   builtins.
 
+- Atomic operations on ``_BitInt(N)`` are now supported, including
+  ``_Atomic(_BitInt(N))``, the ``__c11_atomic_*`` / ``__atomic_*`` builtins, and
+  ``std::atomic``. Widths the target cannot operate on inline use the
+  ``__atomic_*`` libcalls; arithmetic read-modify-write on a width with padding
+  bits is emitted as a compare-exchange loop computing at the value width.
+
 - Added ``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right``
   for bit rotation of unsigned integers including ``_BitInt`` types. Rotation
   counts are normalized modulo the bit-width and support negative values.
diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 270965b109943..66c059fd40e26 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Intrinsics.h"
+#include "llvm/Transforms/Utils/LowerAtomic.h"
 
 using namespace clang;
 using namespace CodeGen;
@@ -558,6 +559,195 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
   return Builder.CreateSelect(Cmp, OldVal, RHS, "newval");
 }
 
+/// Classify an atomic op as an arithmetic/bitwise read-modify-write (one that
+/// normally lowers to a single `atomicrmw`), mapping it to the matching
+/// `AtomicRMWInst::BinOp` and reporting whether the builtin returns the new
+/// value (`<op>_fetch`) rather than the old value (`fetch_<op>`). \p IsSigned
+/// selects signed vs unsigned min/max. Returns false for exchange, load, store,
+/// compare-exchange, and any non-RMW op, none of which need the _BitInt loop.
+static bool classifyBitIntRMW(AtomicExpr::AtomicOp Op, bool IsSigned,
+                              llvm::AtomicRMWInst::BinOp &BinOp,
+                              bool &ReturnsNew) {
+  using RMW = llvm::AtomicRMWInst;
+  switch (Op) {
+  case AtomicExpr::AO__c11_atomic_fetch_add:
+  case AtomicExpr::AO__hip_atomic_fetch_add:
+  case AtomicExpr::AO__opencl_atomic_fetch_add:
+  case AtomicExpr::AO__atomic_fetch_add:
+  case AtomicExpr::AO__scoped_atomic_fetch_add:
+    BinOp = RMW::Add, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_add_fetch:
+  case AtomicExpr::AO__scoped_atomic_add_fetch:
+    BinOp = RMW::Add, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_sub:
+  case AtomicExpr::AO__hip_atomic_fetch_sub:
+  case AtomicExpr::AO__opencl_atomic_fetch_sub:
+  case AtomicExpr::AO__atomic_fetch_sub:
+  case AtomicExpr::AO__scoped_atomic_fetch_sub:
+    BinOp = RMW::Sub, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_sub_fetch:
+  case AtomicExpr::AO__scoped_atomic_sub_fetch:
+    BinOp = RMW::Sub, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_and:
+  case AtomicExpr::AO__hip_atomic_fetch_and:
+  case AtomicExpr::AO__opencl_atomic_fetch_and:
+  case AtomicExpr::AO__atomic_fetch_and:
+  case AtomicExpr::AO__scoped_atomic_fetch_and:
+    BinOp = RMW::And, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_and_fetch:
+  case AtomicExpr::AO__scoped_atomic_and_fetch:
+    BinOp = RMW::And, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_or:
+  case AtomicExpr::AO__hip_atomic_fetch_or:
+  case AtomicExpr::AO__opencl_atomic_fetch_or:
+  case AtomicExpr::AO__atomic_fetch_or:
+  case AtomicExpr::AO__scoped_atomic_fetch_or:
+    BinOp = RMW::Or, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_or_fetch:
+  case AtomicExpr::AO__scoped_atomic_or_fetch:
+    BinOp = RMW::Or, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_xor:
+  case AtomicExpr::AO__hip_atomic_fetch_xor:
+  case AtomicExpr::AO__opencl_atomic_fetch_xor:
+  case AtomicExpr::AO__atomic_fetch_xor:
+  case AtomicExpr::AO__scoped_atomic_fetch_xor:
+    BinOp = RMW::Xor, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_xor_fetch:
+  case AtomicExpr::AO__scoped_atomic_xor_fetch:
+    BinOp = RMW::Xor, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_nand:
+  case AtomicExpr::AO__atomic_fetch_nand:
+  case AtomicExpr::AO__scoped_atomic_fetch_nand:
+    BinOp = RMW::Nand, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_nand_fetch:
+  case AtomicExpr::AO__scoped_atomic_nand_fetch:
+    BinOp = RMW::Nand, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_min:
+  case AtomicExpr::AO__hip_atomic_fetch_min:
+  case AtomicExpr::AO__opencl_atomic_fetch_min:
+  case AtomicExpr::AO__atomic_fetch_min:
+  case AtomicExpr::AO__scoped_atomic_fetch_min:
+    BinOp = IsSigned ? RMW::Min : RMW::UMin, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_min_fetch:
+  case AtomicExpr::AO__scoped_atomic_min_fetch:
+    BinOp = IsSigned ? RMW::Min : RMW::UMin, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_max:
+  case AtomicExpr::AO__hip_atomic_fetch_max:
+  case AtomicExpr::AO__opencl_atomic_fetch_max:
+  case AtomicExpr::AO__atomic_fetch_max:
+  case AtomicExpr::AO__scoped_atomic_fetch_max:
+    BinOp = IsSigned ? RMW::Max : RMW::UMax, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_max_fetch:
+  case AtomicExpr::AO__scoped_atomic_max_fetch:
+    BinOp = IsSigned ? RMW::Max : RMW::UMax, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__atomic_fetch_uinc:
+  case AtomicExpr::AO__scoped_atomic_fetch_uinc:
+    BinOp = RMW::UIncWrap, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_fetch_udec:
+  case AtomicExpr::AO__scoped_atomic_fetch_udec:
+    BinOp = RMW::UDecWrap, ReturnsNew = false;
+    return true;
+  default:
+    return false;
+  }
+}
+
+/// True for a `_BitInt(N)` whose value width N differs from its in-memory width
+/// (e.g. `_BitInt(37)` occupies 64 bits), so the high bits are padding.
+static bool hasBitIntPadding(QualType T, const ASTContext &C) {
+  if (const auto *BIT = T->getAs<BitIntType>())
+    return BIT->getNumBits() != C.getTypeSize(T);
+  return false;
+}
+
+/// Map a constant C ABI memory order to an llvm ordering. A non-constant order
+/// is handled conservatively with the strongest ordering.
+static llvm::AtomicOrdering atomicOrderOrSeqCst(llvm::Value *Order) {
+  auto *C = dyn_cast<llvm::ConstantInt>(Order);
+  if (!C || !llvm::isValidAtomicOrderingCABI(C->getZExtValue()))
+    return llvm::AtomicOrdering::SequentiallyConsistent;
+  switch ((llvm::AtomicOrderingCABI)C->getZExtValue()) {
+  case llvm::AtomicOrderingCABI::relaxed:
+    return llvm::AtomicOrdering::Monotonic;
+  case llvm::AtomicOrderingCABI::consume:
+  case llvm::AtomicOrderingCABI::acquire:
+    return llvm::AtomicOrdering::Acquire;
+  case llvm::AtomicOrderingCABI::release:
+    return llvm::AtomicOrdering::Release;
+  case llvm::AtomicOrderingCABI::acq_rel:
+    return llvm::AtomicOrdering::AcquireRelease;
+  case llvm::AtomicOrderingCABI::seq_cst:
+    return llvm::AtomicOrdering::SequentiallyConsistent;
+  }
+  llvm_unreachable("invalid CABI ordering");
+}
+
+/// Emit a `_BitInt(N)` atomic read-modify-write as a compare-exchange loop. A
+/// single `atomicrmw` on the padded memory integer would carry into / compare
+/// the padding bits, and no arbitrary-width `__atomic_fetch_*` libcall exists
+/// for wide widths. The loop computes the new value at width N and writes back
+/// a canonical (extended) representation via the existing cmpxchg helper, which
+/// also picks the inline-vs-libcall form by size.
+static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
+                                      Address Ptr, Address Val1,
+                                      QualType AtomicTy,
+                                      llvm::AtomicRMWInst::BinOp BinOp,
+                                      bool ReturnsNew, llvm::Value *Order) {
+  QualType ValTy = E->getValueType();
+  llvm::AtomicOrdering AO = atomicOrderOrSeqCst(Order);
+  llvm::AtomicOrdering Failure =
+      llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO);
+
+  LValue AtomicLVal = CGF.MakeAddrLValue(Ptr, AtomicTy);
+  AtomicInfo Atomics(CGF, AtomicLVal);
+
+  llvm::Value *RHS =
+      CGF.EmitLoadOfScalar(CGF.MakeAddrLValue(Val1, ValTy), E->getExprLoc());
+
+  RValue OldRV = Atomics.EmitAtomicLoad(
+      AggValueSlot::ignored(), E->getExprLoc(),
+      /*AsValue=*/true, llvm::AtomicOrdering::Monotonic, E->isVolatile());
+  llvm::Value *Init = OldRV.getScalarVal();
+
+  llvm::BasicBlock *StartBB = CGF.Builder.GetInsertBlock();
+  llvm::BasicBlock *LoopBB = CGF.createBasicBlock("atomicrmw.start", CGF.CurFn);
+  llvm::BasicBlock *EndBB = CGF.createBasicBlock("atomicrmw.end", CGF.CurFn);
+  CGF.Builder.CreateBr(LoopBB);
+  CGF.Builder.SetInsertPoint(LoopBB);
+
+  llvm::PHINode *Old = CGF.Builder.CreatePHI(Init->getType(), 2);
+  Old->addIncoming(Init, StartBB);
+
+  // Compute at the value width via the canonical RMW lowering, so the result
+  // wraps mod 2^N and never touches the padding bits.
+  llvm::Value *New = llvm::buildAtomicRMWValue(BinOp, CGF.Builder, Old, RHS);
+
+  auto Res = Atomics.EmitAtomicCompareExchange(
+      RValue::get(Old), RValue::get(New), AO, Failure, /*IsWeak=*/true);
+  Old->addIncoming(Res.first.getScalarVal(), CGF.Builder.GetInsertBlock());
+  CGF.Builder.CreateCondBr(Res.second, EndBB, LoopBB);
+
+  CGF.Builder.SetInsertPoint(EndBB);
+  return RValue::get(ReturnsNew ? New : static_cast<llvm::Value *>(Old));
+}
+
 static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
                          Address Ptr, Address Val1, Address Val2,
                          Address ExpectedResult, llvm::Value *IsWeak,
@@ -1109,6 +1299,27 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
   LValue AtomicVal = MakeAddrLValue(Ptr, AtomicTy);
   AtomicInfo Atomics(*this, AtomicVal);
 
+  // A `_BitInt(N)` read-modify-write whose value width has padding bits, or
+  // whose size forces a libcall, cannot use a single atomicrmw: the op would
+  // carry into / compare the padding bits, and no arbitrary-width
+  // __atomic_fetch_* libcall exists. Emit a compare-exchange loop instead.
+  // Bitwise and/or/xor are exact even with padding, so only the wide case needs
+  // the loop for them. load/store/exchange/compare_exchange keep their paths.
+  if (MemTy->isBitIntType()) {
+    llvm::AtomicRMWInst::BinOp BinOp;
+    bool RMWReturnsNew;
+    if (classifyBitIntRMW(E->getOp(), MemTy->isSignedIntegerType(), BinOp,
+                          RMWReturnsNew)) {
+      bool WideOrNonPow2 = (Size & (Size - 1)) != 0 || Size > 16;
+      bool Bitwise = BinOp == llvm::AtomicRMWInst::And ||
+                     BinOp == llvm::AtomicRMWInst::Or ||
+                     BinOp == llvm::AtomicRMWInst::Xor;
+      if (WideOrNonPow2 || (hasBitIntPadding(MemTy, getContext()) && !Bitwise))
+        return emitBitIntAtomicRMWLoop(*this, E, Ptr, Val1, AtomicTy, BinOp,
+                                       RMWReturnsNew, Order);
+    }
+  }
+
   Address OriginalVal1 = Val1;
   if (ShouldCastToIntPtrTy) {
     Ptr = Atomics.castToAtomicIntPointer(Ptr);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index b8a3f48a32f24..874ce2bf1ce3a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -5460,11 +5460,6 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
                 ? 0
                 : 1);
 
-  if (ValType->isBitIntType()) {
-    Diag(Ptr->getExprLoc(), diag::err_atomic_builtin_bit_int_prohibit);
-    return ExprError();
-  }
-
   return AE;
 }
 
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index d2bb312feadc1..4a3506c281acf 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -10412,8 +10412,6 @@ QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) {
     else if (!T.isTriviallyCopyableType(Context) && getLangOpts().CPlusPlus)
       // Some other non-trivially-copyable type (probably a C++ class)
       DisallowedKind = 7;
-    else if (T->isBitIntType())
-      DisallowedKind = 8;
     else if (getLangOpts().C23 && T->isUndeducedAutoType())
       // _Atomic auto is prohibited in C23
       DisallowedKind = 9;
diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
new file mode 100644
index 0000000000000..358b530e8a792
--- /dev/null
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -0,0 +1,90 @@
+// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
+//
+// Atomic operations on _BitInt(N). load/store/exchange/compare-exchange and
+// bitwise RMW lower directly; arithmetic RMW on a padded width and any RMW on a
+// width too wide for an inline atomicrmw lower to a compare-exchange loop that
+// computes at the value width.
+
+typedef _BitInt(37)          S37;
+typedef unsigned _BitInt(37) U37;
+typedef _BitInt(64)          S64;
+typedef _BitInt(128)         S128;
+typedef _BitInt(256)         S256;
+
+// CHECK-LABEL: @ld37(
+// CHECK: load atomic i64
+S37 ld37(_Atomic(S37) *p) { return __c11_atomic_load(p, __ATOMIC_SEQ_CST); }
+
+// CHECK-LABEL: @st37(
+// CHECK: store atomic i64
+void st37(_Atomic(S37) *p, S37 v) { __c11_atomic_store(p, v, __ATOMIC_SEQ_CST); }
+
+// CHECK-LABEL: @xchg37(
+// CHECK: atomicrmw xchg ptr {{.*}} i64
+S37 xchg37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_exchange(p, v, __ATOMIC_SEQ_CST);
+}
+
+// CHECK-LABEL: @cas37(
+// CHECK: cmpxchg ptr {{.*}} i64
+_Bool cas37(_Atomic(S37) *p, S37 *e, S37 d) {
+  return __c11_atomic_compare_exchange_strong(p, e, d, __ATOMIC_SEQ_CST,
+                                              __ATOMIC_SEQ_CST);
+}
+
+// Bitwise RMW on a padded width keeps the direct atomicrmw: it is exact.
+// CHECK-LABEL: @and37(
+// CHECK: atomicrmw and ptr {{.*}} i64
+// CHECK-NOT: cmpxchg
+S37 and37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Arithmetic RMW on a padded width becomes a compare-exchange loop, not a bare
+// atomicrmw that would carry into the padding bits.
+// CHECK-LABEL: @add37(
+// CHECK: atomicrmw.start:
+// CHECK: cmpxchg weak ptr {{.*}} i64
+// CHECK-NOT: atomicrmw add
+S37 add37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Signed min is computed at the value width, so the sign bit is at bit N-1.
+// CHECK-LABEL: @min37(
+// CHECK: icmp sle i37
+// CHECK: select i1
+// CHECK: cmpxchg weak ptr {{.*}} i64
+U37 min37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
+}
+
+// No padding: direct atomicrmw, no loop.
+// CHECK-LABEL: @add64(
+// CHECK: atomicrmw add ptr {{.*}} i64
+// CHECK-NOT: cmpxchg
+S64 add64(_Atomic(S64) *p, S64 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// CHECK-LABEL: @add128(
+// CHECK: atomicrmw add ptr {{.*}} i128
+S128 add128(_Atomic(S128) *p, S128 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Wide: no inline atomicrmw and no arbitrary-width __atomic_fetch_add libcall,
+// so the loop calls __atomic_compare_exchange.
+// CHECK-LABEL: @add256(
+// CHECK: call {{.*}}@__atomic_compare_exchange
+// CHECK-NOT: cmpxchg
+S256 add256(_Atomic(S256) *p, S256 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Wide bitwise also needs the loop: the wide path has no inline atomicrmw.
+// CHECK-LABEL: @or256(
+// CHECK: call {{.*}}@__atomic_compare_exchange
+S256 or256(_Atomic(S256) *p, S256 v) {
+  return __c11_atomic_fetch_or(p, v, __ATOMIC_SEQ_CST);
+}
diff --git a/clang/test/Sema/builtins.c b/clang/test/Sema/builtins.c
index b669ee68cdd95..57e0eefdb772b 100644
--- a/clang/test/Sema/builtins.c
+++ b/clang/test/Sema/builtins.c
@@ -281,7 +281,7 @@ void test_ei_i42i(_BitInt(42) *ptr, int value) {
   // expected-warning at +1 {{the semantics of this intrinsic changed with GCC version 4.4 - the newer semantics are provided here}}
   __sync_nand_and_fetch(ptr, value); // expected-error {{atomic memory operand must have a power-of-two size}}
 
-  __atomic_fetch_add(ptr, 1, 0); // expected-error {{argument to atomic builtin of type '_BitInt' is not supported}}
+  __atomic_fetch_add(ptr, 1, 0); // expect success: the GNU atomic builtins support _BitInt
 }
 
 void test_ei_i64i(_BitInt(64) *ptr, int value) {
@@ -289,7 +289,7 @@ void test_ei_i64i(_BitInt(64) *ptr, int value) {
   // expected-warning at +1 {{the semantics of this intrinsic changed with GCC version 4.4 - the newer semantics are provided here}}
   __sync_nand_and_fetch(ptr, value); // expect success
 
-  __atomic_fetch_add(ptr, 1, 0); // expected-error {{argument to atomic builtin of type '_BitInt' is not supported}}
+  __atomic_fetch_add(ptr, 1, 0); // expect success
 }
 
 void test_ei_ii42(int *ptr, _BitInt(42) value) {
diff --git a/clang/test/SemaCXX/ext-int.cpp b/clang/test/SemaCXX/ext-int.cpp
index 281ae3d3c1779..f62a07a84200e 100644
--- a/clang/test/SemaCXX/ext-int.cpp
+++ b/clang/test/SemaCXX/ext-int.cpp
@@ -121,13 +121,11 @@ _Complex _BitInt(3) Cmplx;
 // expected-error at +1{{'_Complex _BitInt' is invalid}}
 typedef _Complex _BitInt(3) Cmp;
 
-// Reject cases of _Atomic:
-// expected-error at +1{{_Atomic cannot be applied to integer type '_BitInt(4)'}}
-_Atomic _BitInt(4) TooSmallAtomic;
-// expected-error at +1{{_Atomic cannot be applied to integer type '_BitInt(9)'}}
+// _Atomic accepts any _BitInt width: small and non-power-of-2 included.
+// Sizes the target cannot lower inline use the __atomic_* libcalls.
+_Atomic _BitInt(4) SmallAtomic;
 _Atomic _BitInt(9) NotPow2Atomic;
-// expected-error at +1{{_Atomic cannot be applied to integer type '_BitInt(128)'}}
-_Atomic _BitInt(128) JustRightAtomic;
+_Atomic _BitInt(128) WideAtomic;
 
 // Test result types of Unary/Bitwise/Binary Operations:
 void Ops() {
diff --git a/libcxx/test/libcxx/atomics/bit-int.verify.cpp b/libcxx/test/libcxx/atomics/bit-int.verify.cpp
deleted file mode 100644
index 03880a1b6215c..0000000000000
--- a/libcxx/test/libcxx/atomics/bit-int.verify.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <atomic>
-
-// Make sure that `std::atomic` doesn't work with `_BitInt`. The intent is to
-// disable them for now until their behavior can be designed better later.
-// See https://reviews.llvm.org/D84049 for details.
-
-// UNSUPPORTED: c++03
-
-#include <atomic>
-
-void f() {
-  // expected-error@*:*1 {{_Atomic cannot be applied to integer type '_BitInt(32)'}}
-  std::atomic<_BitInt(32)> x(42);
-}

>From 92e21eaa61bf0f26c3ba825545845f531d79c4d6 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Tue, 23 Jun 2026 11:53:49 +0200
Subject: [PATCH 2/9] [Clang][POC] Add C23/C2y Sema tests for atomic _BitInt(N)

C23 requires the type-generic atomic interfaces to accept _BitInt(N), so
_Atomic(_BitInt(N)) is well-formed at every width. Add a Sema acceptance
test covering the _Atomic specifier and the __c11_atomic_*/__atomic_*
builtins in C23 and C2y modes, and a -std=c2y run of the codegen test.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/test/CodeGen/atomic-bitint.c |  1 +
 clang/test/Sema/atomic-bitint.c    | 40 ++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+)
 create mode 100644 clang/test/Sema/atomic-bitint.c

diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
index 358b530e8a792..9fa259776bf62 100644
--- a/clang/test/CodeGen/atomic-bitint.c
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c2y -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
 //
 // Atomic operations on _BitInt(N). load/store/exchange/compare-exchange and
 // bitwise RMW lower directly; arithmetic RMW on a padded width and any RMW on a
diff --git a/clang/test/Sema/atomic-bitint.c b/clang/test/Sema/atomic-bitint.c
new file mode 100644
index 0000000000000..1466412bae732
--- /dev/null
+++ b/clang/test/Sema/atomic-bitint.c
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify -triple x86_64-unknown-linux-gnu -std=c23
+// RUN: %clang_cc1 %s -fsyntax-only -verify -triple x86_64-unknown-linux-gnu -std=c2y
+//
+// C23 requires the type-generic atomic interfaces to accept _BitInt(N) for
+// every N, so _Atomic(_BitInt(N)) is well-formed at every width. Widths past
+// 128 are x86-only.
+
+// expected-no-diagnostics
+
+_Atomic(_BitInt(4))   a4;    // small
+_Atomic(_BitInt(9))   a9;    // non-power-of-two
+_Atomic(_BitInt(37))  a37;   // padded
+_Atomic(_BitInt(64))  a64;
+_Atomic(_BitInt(128)) a128;
+_Atomic(_BitInt(256)) a256;  // wider than any inline atomic
+
+// The _Atomic qualifier spelling is equally valid.
+_Atomic _BitInt(9) q9;
+
+static_assert(sizeof(_Atomic(_BitInt(37))) == 8);
+static_assert(sizeof(_Atomic(_BitInt(128))) == 16);
+static_assert(sizeof(_Atomic(_BitInt(256))) == 32);
+
+void c11_builtins(_Atomic(_BitInt(37)) *p, _BitInt(37) v, _BitInt(37) *e) {
+  (void)__c11_atomic_load(p, __ATOMIC_SEQ_CST);
+  __c11_atomic_store(p, v, __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_exchange(p, v, __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_compare_exchange_strong(p, e, v, __ATOMIC_SEQ_CST,
+                                             __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
+}
+
+// The GNU __atomic_* builtins take a plain _BitInt pointer.
+void gnu_builtins(_BitInt(37) *p, _BitInt(37) v) {
+  (void)__atomic_load_n(p, __ATOMIC_SEQ_CST);
+  __atomic_store_n(p, v, __ATOMIC_SEQ_CST);
+  (void)__atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}

>From c0e011fb61086a5a0cae13c54abe21ab312e85c7 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Tue, 23 Jun 2026 13:51:48 +0200
Subject: [PATCH 3/9] [Clang][POC] Extend atomic _BitInt Sema test: width 4096
 + RISC-V

Add a _BitInt(4096) acceptance case and a riscv64 RUN line. The atomic
code imposes no width cap of its own, so the only limit is the target's
getMaxBitIntWidth(); x86 and RISC-V allow widths past 128, others cap at
128. Correct the comment that claimed wide widths were x86-only.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/test/Sema/atomic-bitint.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/clang/test/Sema/atomic-bitint.c b/clang/test/Sema/atomic-bitint.c
index 1466412bae732..fbb4c518438fb 100644
--- a/clang/test/Sema/atomic-bitint.c
+++ b/clang/test/Sema/atomic-bitint.c
@@ -1,18 +1,21 @@
 // RUN: %clang_cc1 %s -fsyntax-only -verify -triple x86_64-unknown-linux-gnu -std=c23
 // RUN: %clang_cc1 %s -fsyntax-only -verify -triple x86_64-unknown-linux-gnu -std=c2y
+// RUN: %clang_cc1 %s -fsyntax-only -verify -triple riscv64-unknown-linux-gnu -std=c23
 //
 // C23 requires the type-generic atomic interfaces to accept _BitInt(N) for
-// every N, so _Atomic(_BitInt(N)) is well-formed at every width. Widths past
-// 128 are x86-only.
+// every N, so _Atomic(_BitInt(N)) is well-formed at every width. The atomic
+// code imposes no width cap of its own; widths past 128 are available wherever
+// the target accepts _BitInt > 128 (x86 and RISC-V today).
 
 // expected-no-diagnostics
 
-_Atomic(_BitInt(4))   a4;    // small
-_Atomic(_BitInt(9))   a9;    // non-power-of-two
-_Atomic(_BitInt(37))  a37;   // padded
-_Atomic(_BitInt(64))  a64;
-_Atomic(_BitInt(128)) a128;
-_Atomic(_BitInt(256)) a256;  // wider than any inline atomic
+_Atomic(_BitInt(4))    a4;     // small
+_Atomic(_BitInt(9))    a9;     // non-power-of-two
+_Atomic(_BitInt(37))   a37;    // padded
+_Atomic(_BitInt(64))   a64;
+_Atomic(_BitInt(128))  a128;
+_Atomic(_BitInt(256))  a256;   // wider than any inline atomic
+_Atomic(_BitInt(4096)) a4096;  // far past the inline range
 
 // The _Atomic qualifier spelling is equally valid.
 _Atomic _BitInt(9) q9;

>From 92e2068b4837befaa51c040c0c882ea7f10cd57f Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Fri, 26 Jun 2026 21:21:00 +0200
Subject: [PATCH 4/9] [Clang][NFC] Address review: atomic _BitInt diagnostic
 list and cast

Drop the now-unreachable _BitInt ("integer") option from
err_atomic_specifier_bad_type and renumber the _Atomic-auto/C23 case to
fill the gap; the rendered text is unchanged. Use static_cast instead of
a C-style cast in atomicOrderOrSeqCst.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ++--
 clang/lib/CodeGen/CGAtomic.cpp                   | 2 +-
 clang/lib/Sema/SemaType.cpp                      | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index cde99dfb16ec5..414357c5a7c73 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7475,8 +7475,8 @@ def err_func_def_incomplete_result : Error<
 def err_atomic_specifier_bad_type
     : Error<"_Atomic cannot be applied to "
             "%select{incomplete |array |function |reference |atomic |qualified "
-            "|sizeless ||integer |}0type "
-            "%1 %select{|||||||which is not trivially copyable||in C23}0">;
+            "|sizeless ||}0type "
+            "%1 %select{|||||||which is not trivially copyable|in C23}0">;
 def warn_atomic_member_access : Warning<
   "accessing a member of an atomic structure or union is undefined behavior">,
   InGroup<DiagGroup<"atomic-access">>, DefaultError;
diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 66c059fd40e26..820849f5974c0 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -683,7 +683,7 @@ static llvm::AtomicOrdering atomicOrderOrSeqCst(llvm::Value *Order) {
   auto *C = dyn_cast<llvm::ConstantInt>(Order);
   if (!C || !llvm::isValidAtomicOrderingCABI(C->getZExtValue()))
     return llvm::AtomicOrdering::SequentiallyConsistent;
-  switch ((llvm::AtomicOrderingCABI)C->getZExtValue()) {
+  switch (static_cast<llvm::AtomicOrderingCABI>(C->getZExtValue())) {
   case llvm::AtomicOrderingCABI::relaxed:
     return llvm::AtomicOrdering::Monotonic;
   case llvm::AtomicOrderingCABI::consume:
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 4a3506c281acf..f76244d5f2871 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -10414,7 +10414,7 @@ QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) {
       DisallowedKind = 7;
     else if (getLangOpts().C23 && T->isUndeducedAutoType())
       // _Atomic auto is prohibited in C23
-      DisallowedKind = 9;
+      DisallowedKind = 8;
 
     if (DisallowedKind != -1) {
       Diag(Loc, diag::err_atomic_specifier_bad_type) << DisallowedKind << T;

>From ece57436ed0217f4c1ad4ca92f10182793ec109f Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Fri, 26 Jun 2026 21:21:02 +0200
Subject: [PATCH 5/9] [Clang][NFC] Regenerate atomic-bitint.c checks with
 update_cc_test_checks

The prior spot-checks did not show the lowering. Regenerate complete
check lines so the compare-exchange loop, the width-N arithmetic, and the
sext/zext memory canonicalization are all visible.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/test/CodeGen/atomic-bitint.c | 350 ++++++++++++++++++++++++++---
 1 file changed, 321 insertions(+), 29 deletions(-)

diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
index 9fa259776bf62..6476c26a0f0dd 100644
--- a/clang/test/CodeGen/atomic-bitint.c
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -1,3 +1,4 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
 // RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
 // RUN: %clang_cc1 -std=c2y -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
 //
@@ -12,80 +13,371 @@ typedef _BitInt(64)          S64;
 typedef _BitInt(128)         S128;
 typedef _BitInt(256)         S256;
 
-// CHECK-LABEL: @ld37(
-// CHECK: load atomic i64
+// CHECK-LABEL: define dso_local i64 @ld37(
+// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load atomic i64, ptr [[TMP0]] seq_cst, align 8
+// CHECK-NEXT:    store i64 [[TMP1]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    store i37 [[LOADEDV]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP3]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 S37 ld37(_Atomic(S37) *p) { return __c11_atomic_load(p, __ATOMIC_SEQ_CST); }
 
-// CHECK-LABEL: @st37(
-// CHECK: store atomic i64
+// CHECK-LABEL: define dso_local void @st37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    store atomic i64 [[TMP3]], ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    ret void
+//
 void st37(_Atomic(S37) *p, S37 v) { __c11_atomic_store(p, v, __ATOMIC_SEQ_CST); }
 
-// CHECK-LABEL: @xchg37(
-// CHECK: atomicrmw xchg ptr {{.*}} i64
+// CHECK-LABEL: define dso_local i64 @xchg37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = atomicrmw xchg ptr [[TMP1]], i64 [[TMP3]] seq_cst, align 8
+// CHECK-NEXT:    store i64 [[TMP4]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP5]] to i37
+// CHECK-NEXT:    store i37 [[LOADEDV3]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP6]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 S37 xchg37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_exchange(p, v, __ATOMIC_SEQ_CST);
 }
 
-// CHECK-LABEL: @cas37(
-// CHECK: cmpxchg ptr {{.*}} i64
+// CHECK-LABEL: define dso_local zeroext i1 @cas37(
+// CHECK-SAME: ptr noundef [[P:%.*]], ptr noundef [[E:%.*]], i64 noundef [[D_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[D:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[E_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[D_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[CMPXCHG_BOOL:%.*]] = alloca i8, align 1
+// CHECK-NEXT:    store i64 [[D_COERCE]], ptr [[D]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[D]], align 8
+// CHECK-NEXT:    [[D1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[E]], ptr [[E_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[D1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[D_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[E_ADDR]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[D_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = load i64, ptr [[TMP2]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP5]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP6]], 0
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
+// CHECK-NEXT:    br i1 [[TMP8]], label %[[CMPXCHG_CONTINUE:.*]], label %[[CMPXCHG_STORE_EXPECTED:.*]]
+// CHECK:       [[CMPXCHG_STORE_EXPECTED]]:
+// CHECK-NEXT:    store i64 [[TMP7]], ptr [[TMP2]], align 8
+// CHECK-NEXT:    br label %[[CMPXCHG_CONTINUE]]
+// CHECK:       [[CMPXCHG_CONTINUE]]:
+// CHECK-NEXT:    [[STOREDV3:%.*]] = zext i1 [[TMP8]] to i8
+// CHECK-NEXT:    store i8 [[STOREDV3]], ptr [[CMPXCHG_BOOL]], align 1
+// CHECK-NEXT:    [[TMP9:%.*]] = load i8, ptr [[CMPXCHG_BOOL]], align 1
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = icmp ne i8 [[TMP9]], 0
+// CHECK-NEXT:    ret i1 [[LOADEDV4]]
+//
 _Bool cas37(_Atomic(S37) *p, S37 *e, S37 d) {
   return __c11_atomic_compare_exchange_strong(p, e, d, __ATOMIC_SEQ_CST,
                                               __ATOMIC_SEQ_CST);
 }
 
 // Bitwise RMW on a padded width keeps the direct atomicrmw: it is exact.
-// CHECK-LABEL: @and37(
-// CHECK: atomicrmw and ptr {{.*}} i64
-// CHECK-NOT: cmpxchg
+// CHECK-LABEL: define dso_local i64 @and37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = atomicrmw and ptr [[TMP1]], i64 [[TMP3]] seq_cst, align 8
+// CHECK-NEXT:    store i64 [[TMP4]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP5]] to i37
+// CHECK-NEXT:    store i37 [[LOADEDV3]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP6]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 S37 and37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
 }
 
 // Arithmetic RMW on a padded width becomes a compare-exchange loop, not a bare
 // atomicrmw that would carry into the padding bits.
-// CHECK-LABEL: @add37(
-// CHECK: atomicrmw.start:
-// CHECK: cmpxchg weak ptr {{.*}} i64
-// CHECK-NOT: atomicrmw add
+// CHECK-LABEL: define dso_local i64 @add37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] monotonic, align 8
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[ATOMIC_LOAD]] to i37
+// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
+// CHECK:       [[ATOMICRMW_START]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i37 [ [[LOADEDV4]], %[[ENTRY]] ], [ [[LOADEDV7:%.*]], %[[ATOMICRMW_START]] ]
+// CHECK-NEXT:    [[NEW:%.*]] = add i37 [[TMP4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[TMP4]] to i64
+// CHECK-NEXT:    [[STOREDV6:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    [[TMP5:%.*]] = cmpxchg weak ptr [[TMP1]], i64 [[STOREDV5]], i64 [[STOREDV6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = extractvalue { i64, i1 } [[TMP5]], 0
+// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP5]], 1
+// CHECK-NEXT:    [[LOADEDV7]] = trunc i64 [[TMP6]] to i37
+// CHECK-NEXT:    br i1 [[TMP7]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
+// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    store i37 [[TMP4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP8:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP8]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 S37 add37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
 }
 
 // Signed min is computed at the value width, so the sign bit is at bit N-1.
-// CHECK-LABEL: @min37(
-// CHECK: icmp sle i37
-// CHECK: select i1
-// CHECK: cmpxchg weak ptr {{.*}} i64
+// CHECK-LABEL: define dso_local i64 @min37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] monotonic, align 8
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[ATOMIC_LOAD]] to i37
+// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
+// CHECK:       [[ATOMICRMW_START]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i37 [ [[LOADEDV4]], %[[ENTRY]] ], [ [[LOADEDV7:%.*]], %[[ATOMICRMW_START]] ]
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp sle i37 [[TMP4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[TMP4]], i37 [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[TMP4]] to i64
+// CHECK-NEXT:    [[STOREDV6:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg weak ptr [[TMP1]], i64 [[STOREDV5]], i64 [[STOREDV6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP6]], 0
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
+// CHECK-NEXT:    [[LOADEDV7]] = trunc i64 [[TMP7]] to i37
+// CHECK-NEXT:    br i1 [[TMP8]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
+// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    store i37 [[TMP4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP9:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP9]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 U37 min37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
 }
 
 // No padding: direct atomicrmw, no loop.
-// CHECK-LABEL: @add64(
-// CHECK: atomicrmw add ptr {{.*}} i64
-// CHECK-NOT: cmpxchg
+// CHECK-LABEL: define dso_local i64 @add64(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[TMP1]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = atomicrmw add ptr [[TMP0]], i64 [[TMP2]] seq_cst, align 8
+// CHECK-NEXT:    store i64 [[TMP3]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    ret i64 [[TMP4]]
+//
 S64 add64(_Atomic(S64) *p, S64 v) {
   return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
 }
 
-// CHECK-LABEL: @add128(
-// CHECK: atomicrmw add ptr {{.*}} i128
+// CHECK-LABEL: define dso_local i128 @add128(
+// CHECK-SAME: ptr noundef [[P:%.*]], i128 noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i128, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i128, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i128, align 16
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store i128 [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load i128, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    store i128 [[TMP1]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i128, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = atomicrmw add ptr [[TMP0]], i128 [[TMP2]] seq_cst, align 16
+// CHECK-NEXT:    store i128 [[TMP3]], ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT:    [[TMP4:%.*]] = load i128, ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT:    ret i128 [[TMP4]]
+//
 S128 add128(_Atomic(S128) *p, S128 v) {
   return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
 }
 
 // Wide: no inline atomicrmw and no arbitrary-width __atomic_fetch_add libcall,
 // so the loop calls __atomic_compare_exchange.
-// CHECK-LABEL: @add256(
-// CHECK: call {{.*}}@__atomic_compare_exchange
-// CHECK-NOT: cmpxchg
+// CHECK-LABEL: define dso_local void @add256(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(i256) align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(i256) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP1:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP2:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[V:%.*]] = load i256, ptr [[TMP0]], align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store i256 [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i256, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    store i256 [[TMP2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i256, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 0)
+// CHECK-NEXT:    [[TMP4:%.*]] = load i256, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
+// CHECK:       [[ATOMICRMW_START]]:
+// CHECK-NEXT:    [[TMP5:%.*]] = phi i256 [ [[TMP4]], %[[ENTRY]] ], [ [[TMP6:%.*]], %[[ATOMICRMW_START]] ]
+// CHECK-NEXT:    [[NEW:%.*]] = add i256 [[TMP5]], [[TMP3]]
+// CHECK-NEXT:    store i256 [[TMP5]], ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    store i256 [[NEW]], ptr [[ATOMIC_TEMP2]], align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
+// CHECK-NEXT:    [[TMP6]] = load i256, ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
+// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    store i256 [[TMP5]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    store i256 [[TMP7]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    ret void
+//
 S256 add256(_Atomic(S256) *p, S256 v) {
   return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
 }
 
 // Wide bitwise also needs the loop: the wide path has no inline atomicrmw.
-// CHECK-LABEL: @or256(
-// CHECK: call {{.*}}@__atomic_compare_exchange
+// CHECK-LABEL: define dso_local void @or256(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(i256) align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(i256) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP1:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP2:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[V:%.*]] = load i256, ptr [[TMP0]], align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store i256 [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i256, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    store i256 [[TMP2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i256, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 0)
+// CHECK-NEXT:    [[TMP4:%.*]] = load i256, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
+// CHECK:       [[ATOMICRMW_START]]:
+// CHECK-NEXT:    [[TMP5:%.*]] = phi i256 [ [[TMP4]], %[[ENTRY]] ], [ [[TMP6:%.*]], %[[ATOMICRMW_START]] ]
+// CHECK-NEXT:    [[NEW:%.*]] = or i256 [[TMP5]], [[TMP3]]
+// CHECK-NEXT:    store i256 [[TMP5]], ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    store i256 [[NEW]], ptr [[ATOMIC_TEMP2]], align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
+// CHECK-NEXT:    [[TMP6]] = load i256, ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
+// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    store i256 [[TMP5]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    store i256 [[TMP7]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    ret void
+//
 S256 or256(_Atomic(S256) *p, S256 v) {
   return __c11_atomic_fetch_or(p, v, __ATOMIC_SEQ_CST);
 }

>From 1c87c9994199bd82766b4d6d6f5edf392c225ba2 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sat, 27 Jun 2026 09:23:04 +0200
Subject: [PATCH 6/9] [Clang] Carry the raw representation in the _BitInt
 atomic RMW loop

The compare-exchange loop for padded and wide _BitInt atomics formed its
cmpxchg expected by re-canonicalizing the loaded value (sign/zero-extending
the truncated old). An object whose padding bits were non-canonical, e.g.
written through a union, then never matched that expected, so the cmpxchg
failed every iteration and the read-modify-write spun forever.

Reuse the existing EmitAtomicUpdate loop, which carries the raw loaded
representation as the expected and writes back a canonical desired computed
at value width N. The object converges on the first iteration regardless of
its padding, and the value it stores is canonical. See P0528.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/lib/CodeGen/CGAtomic.cpp     |  48 +++++-------
 clang/test/CodeGen/atomic-bitint.c | 120 ++++++++++++++---------------
 2 files changed, 77 insertions(+), 91 deletions(-)

diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 820849f5974c0..0043c79b398ee 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -702,9 +702,13 @@ static llvm::AtomicOrdering atomicOrderOrSeqCst(llvm::Value *Order) {
 /// Emit a `_BitInt(N)` atomic read-modify-write as a compare-exchange loop. A
 /// single `atomicrmw` on the padded memory integer would carry into / compare
 /// the padding bits, and no arbitrary-width `__atomic_fetch_*` libcall exists
-/// for wide widths. The loop computes the new value at width N and writes back
-/// a canonical (extended) representation via the existing cmpxchg helper, which
-/// also picks the inline-vs-libcall form by size.
+/// for wide widths.
+///
+/// The update computes at value width N (so the result wraps mod 2^N and is
+/// independent of padding). EmitAtomicUpdate carries the raw loaded
+/// representation as the cmpxchg expected, so an object with non-canonical
+/// padding (e.g. written through a union) still converges instead of spinning
+/// forever; the desired it writes back is canonical. See P0528.
 static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
                                       Address Ptr, Address Val1,
                                       QualType AtomicTy,
@@ -712,8 +716,6 @@ static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
                                       bool ReturnsNew, llvm::Value *Order) {
   QualType ValTy = E->getValueType();
   llvm::AtomicOrdering AO = atomicOrderOrSeqCst(Order);
-  llvm::AtomicOrdering Failure =
-      llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO);
 
   LValue AtomicLVal = CGF.MakeAddrLValue(Ptr, AtomicTy);
   AtomicInfo Atomics(CGF, AtomicLVal);
@@ -721,31 +723,17 @@ static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
   llvm::Value *RHS =
       CGF.EmitLoadOfScalar(CGF.MakeAddrLValue(Val1, ValTy), E->getExprLoc());
 
-  RValue OldRV = Atomics.EmitAtomicLoad(
-      AggValueSlot::ignored(), E->getExprLoc(),
-      /*AsValue=*/true, llvm::AtomicOrdering::Monotonic, E->isVolatile());
-  llvm::Value *Init = OldRV.getScalarVal();
-
-  llvm::BasicBlock *StartBB = CGF.Builder.GetInsertBlock();
-  llvm::BasicBlock *LoopBB = CGF.createBasicBlock("atomicrmw.start", CGF.CurFn);
-  llvm::BasicBlock *EndBB = CGF.createBasicBlock("atomicrmw.end", CGF.CurFn);
-  CGF.Builder.CreateBr(LoopBB);
-  CGF.Builder.SetInsertPoint(LoopBB);
-
-  llvm::PHINode *Old = CGF.Builder.CreatePHI(Init->getType(), 2);
-  Old->addIncoming(Init, StartBB);
-
-  // Compute at the value width via the canonical RMW lowering, so the result
-  // wraps mod 2^N and never touches the padding bits.
-  llvm::Value *New = llvm::buildAtomicRMWValue(BinOp, CGF.Builder, Old, RHS);
-
-  auto Res = Atomics.EmitAtomicCompareExchange(
-      RValue::get(Old), RValue::get(New), AO, Failure, /*IsWeak=*/true);
-  Old->addIncoming(Res.first.getScalarVal(), CGF.Builder.GetInsertBlock());
-  CGF.Builder.CreateCondBr(Res.second, EndBB, LoopBB);
-
-  CGF.Builder.SetInsertPoint(EndBB);
-  return RValue::get(ReturnsNew ? New : static_cast<llvm::Value *>(Old));
+  llvm::Value *Old = nullptr, *New = nullptr;
+  Atomics.EmitAtomicUpdate(
+      AO,
+      [&](RValue OldRV) {
+        Old = OldRV.getScalarVal();
+        New = llvm::buildAtomicRMWValue(BinOp, CGF.Builder, Old, RHS);
+        return RValue::get(New);
+      },
+      E->isVolatile());
+
+  return RValue::get(ReturnsNew ? New : Old);
 }
 
 static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
index 6476c26a0f0dd..bc1e165fd90e3 100644
--- a/clang/test/CodeGen/atomic-bitint.c
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -178,6 +178,7 @@ S37 and37(_Atomic(S37) *p, S37 v) {
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
 // CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
 // CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
 // CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
@@ -191,23 +192,23 @@ S37 and37(_Atomic(S37) *p, S37 v) {
 // CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
-// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] monotonic, align 8
-// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[ATOMIC_LOAD]] to i37
-// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
-// CHECK:       [[ATOMICRMW_START]]:
-// CHECK-NEXT:    [[TMP4:%.*]] = phi i37 [ [[LOADEDV4]], %[[ENTRY]] ], [ [[LOADEDV7:%.*]], %[[ATOMICRMW_START]] ]
-// CHECK-NEXT:    [[NEW:%.*]] = add i37 [[TMP4]], [[LOADEDV3]]
-// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[TMP4]] to i64
-// CHECK-NEXT:    [[STOREDV6:%.*]] = sext i37 [[NEW]] to i64
-// CHECK-NEXT:    [[TMP5:%.*]] = cmpxchg weak ptr [[TMP1]], i64 [[STOREDV5]], i64 [[STOREDV6]] seq_cst seq_cst, align 8
-// CHECK-NEXT:    [[TMP6:%.*]] = extractvalue { i64, i1 } [[TMP5]], 0
-// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP5]], 1
-// CHECK-NEXT:    [[LOADEDV7]] = trunc i64 [[TMP6]] to i37
-// CHECK-NEXT:    br i1 [[TMP7]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
-// CHECK:       [[ATOMICRMW_END]]:
-// CHECK-NEXT:    store i37 [[TMP4]], ptr [[RETVAL]], align 8
-// CHECK-NEXT:    [[TMP8:%.*]] = load i37, ptr [[RETVAL]], align 8
-// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP8]] to i64
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP7:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[NEW:%.*]] = add i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP5]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP7]] = extractvalue { i64, i1 } [[TMP6]], 0
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
+// CHECK-NEXT:    br i1 [[TMP8]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP9:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP9]] to i64
 // CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
 //
 S37 add37(_Atomic(S37) *p, S37 v) {
@@ -223,6 +224,7 @@ S37 add37(_Atomic(S37) *p, S37 v) {
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
 // CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
 // CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
 // CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
@@ -236,24 +238,24 @@ S37 add37(_Atomic(S37) *p, S37 v) {
 // CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
-// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] monotonic, align 8
-// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[ATOMIC_LOAD]] to i37
-// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
-// CHECK:       [[ATOMICRMW_START]]:
-// CHECK-NEXT:    [[TMP4:%.*]] = phi i37 [ [[LOADEDV4]], %[[ENTRY]] ], [ [[LOADEDV7:%.*]], %[[ATOMICRMW_START]] ]
-// CHECK-NEXT:    [[TMP5:%.*]] = icmp sle i37 [[TMP4]], [[LOADEDV3]]
-// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[TMP4]], i37 [[LOADEDV3]]
-// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[TMP4]] to i64
-// CHECK-NEXT:    [[STOREDV6:%.*]] = sext i37 [[NEW]] to i64
-// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg weak ptr [[TMP1]], i64 [[STOREDV5]], i64 [[STOREDV6]] seq_cst seq_cst, align 8
-// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP6]], 0
-// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
-// CHECK-NEXT:    [[LOADEDV7]] = trunc i64 [[TMP7]] to i37
-// CHECK-NEXT:    br i1 [[TMP8]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
-// CHECK:       [[ATOMICRMW_END]]:
-// CHECK-NEXT:    store i37 [[TMP4]], ptr [[RETVAL]], align 8
-// CHECK-NEXT:    [[TMP9:%.*]] = load i37, ptr [[RETVAL]], align 8
-// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP9]] to i64
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP8:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp sle i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[LOADEDV4]], i37 [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP8]] = extractvalue { i64, i1 } [[TMP7]], 0
+// CHECK-NEXT:    [[TMP9:%.*]] = extractvalue { i64, i1 } [[TMP7]], 1
+// CHECK-NEXT:    br i1 [[TMP9]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP10:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP10]] to i64
 // CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
 //
 U37 min37(_Atomic(S37) *p, S37 v) {
@@ -309,7 +311,7 @@ S128 add128(_Atomic(S128) *p, S128 v) {
 // so the loop calls __atomic_compare_exchange.
 // CHECK-LABEL: define dso_local void @add256(
 // CHECK-SAME: ptr dead_on_unwind noalias writable sret(i256) align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(i256) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:  [[ENTRY:.*:]]
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i256, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i256, align 8
@@ -323,21 +325,19 @@ S128 add128(_Atomic(S128) *p, S128 v) {
 // CHECK-NEXT:    [[TMP2:%.*]] = load i256, ptr [[V_ADDR]], align 8
 // CHECK-NEXT:    store i256 [[TMP2]], ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load i256, ptr [[DOTATOMICTMP]], align 8
-// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 0)
+// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 5)
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
 // CHECK-NEXT:    [[TMP4:%.*]] = load i256, ptr [[ATOMIC_TEMP]], align 8
-// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
-// CHECK:       [[ATOMICRMW_START]]:
-// CHECK-NEXT:    [[TMP5:%.*]] = phi i256 [ [[TMP4]], %[[ENTRY]] ], [ [[TMP6:%.*]], %[[ATOMICRMW_START]] ]
-// CHECK-NEXT:    [[NEW:%.*]] = add i256 [[TMP5]], [[TMP3]]
-// CHECK-NEXT:    store i256 [[TMP5]], ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    [[NEW:%.*]] = add i256 [[TMP4]], [[TMP3]]
 // CHECK-NEXT:    store i256 [[NEW]], ptr [[ATOMIC_TEMP2]], align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
-// CHECK-NEXT:    [[TMP6]] = load i256, ptr [[ATOMIC_TEMP1]], align 8
-// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
-// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    call void @__atomic_store(i64 noundef 32, ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5)
+// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], ptr noundef [[ATOMIC_TEMP1]], i32 noundef 5, i32 noundef 5)
+// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i256 [[TMP4]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
 // CHECK-NEXT:    store i256 [[TMP5]], ptr [[AGG_RESULT]], align 8
-// CHECK-NEXT:    [[TMP7:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
-// CHECK-NEXT:    store i256 [[TMP7]], ptr [[AGG_RESULT]], align 8
 // CHECK-NEXT:    ret void
 //
 S256 add256(_Atomic(S256) *p, S256 v) {
@@ -347,7 +347,7 @@ S256 add256(_Atomic(S256) *p, S256 v) {
 // Wide bitwise also needs the loop: the wide path has no inline atomicrmw.
 // CHECK-LABEL: define dso_local void @or256(
 // CHECK-SAME: ptr dead_on_unwind noalias writable sret(i256) align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(i256) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:  [[ENTRY:.*:]]
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i256, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i256, align 8
@@ -361,21 +361,19 @@ S256 add256(_Atomic(S256) *p, S256 v) {
 // CHECK-NEXT:    [[TMP2:%.*]] = load i256, ptr [[V_ADDR]], align 8
 // CHECK-NEXT:    store i256 [[TMP2]], ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load i256, ptr [[DOTATOMICTMP]], align 8
-// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 0)
+// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 5)
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
 // CHECK-NEXT:    [[TMP4:%.*]] = load i256, ptr [[ATOMIC_TEMP]], align 8
-// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
-// CHECK:       [[ATOMICRMW_START]]:
-// CHECK-NEXT:    [[TMP5:%.*]] = phi i256 [ [[TMP4]], %[[ENTRY]] ], [ [[TMP6:%.*]], %[[ATOMICRMW_START]] ]
-// CHECK-NEXT:    [[NEW:%.*]] = or i256 [[TMP5]], [[TMP3]]
-// CHECK-NEXT:    store i256 [[TMP5]], ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    [[NEW:%.*]] = or i256 [[TMP4]], [[TMP3]]
 // CHECK-NEXT:    store i256 [[NEW]], ptr [[ATOMIC_TEMP2]], align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
-// CHECK-NEXT:    [[TMP6]] = load i256, ptr [[ATOMIC_TEMP1]], align 8
-// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
-// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    call void @__atomic_store(i64 noundef 32, ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5)
+// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], ptr noundef [[ATOMIC_TEMP1]], i32 noundef 5, i32 noundef 5)
+// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i256 [[TMP4]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
 // CHECK-NEXT:    store i256 [[TMP5]], ptr [[AGG_RESULT]], align 8
-// CHECK-NEXT:    [[TMP7:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
-// CHECK-NEXT:    store i256 [[TMP7]], ptr [[AGG_RESULT]], align 8
 // CHECK-NEXT:    ret void
 //
 S256 or256(_Atomic(S256) *p, S256 v) {

>From e79f0f4e3fbec4c77fcae7f42f2d9dc5f3e85c5a Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sat, 27 Jun 2026 09:34:49 +0200
Subject: [PATCH 7/9] [compiler-rt] Add runtime test for atomic _BitInt(N)

Single-threaded execution test for _Atomic(_BitInt(N)): per-op value
correctness on a padded inline width and on wide libcall widths, plus
dirty-padding convergence. An object with non-canonical padding (written
through a union) must not spin forever in the read-modify-write
compare-exchange loop. The IR-shape checks in
clang/test/CodeGen/atomic-bitint.c cannot witness non-termination.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 .../test/builtins/Unit/atomic_bitint_test.c   | 91 +++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100644 compiler-rt/test/builtins/Unit/atomic_bitint_test.c

diff --git a/compiler-rt/test/builtins/Unit/atomic_bitint_test.c b/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
new file mode 100644
index 0000000000000..33a745348a6f0
--- /dev/null
+++ b/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
@@ -0,0 +1,91 @@
+// RUN: %clang_builtins -std=c23 %s %librt -o %t && %run %t
+// REQUIRES: librt_has_atomic
+//===-- atomic_bitint_test.c - Test atomic ops on _BitInt -----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Runtime checks for atomic read-modify-write on _BitInt(N). A padded width
+// (37) exercises the inline compare-exchange loop; a wide width (256) exercises
+// the __atomic_compare_exchange libcall loop. Each op is cross-checked against
+// the same operation done non-atomically, and the dirty-padding cases confirm
+// the loop converges (a re-canonicalized expected would spin forever).
+//
+//===----------------------------------------------------------------------===//
+
+#include <assert.h>
+#include <stdio.h>
+
+typedef signed _BitInt(37) S37;
+typedef unsigned _BitInt(37) U37;
+typedef signed _BitInt(256) S256; // no padding (exactly 32 bytes)
+typedef signed _BitInt(200) S200; // padded: 200 value bits in 32-byte storage
+
+// Each macro runs the atomic op and asserts the returned old value and the
+// resulting object both match the non-atomic computation at width N.
+#define CHECK_FETCH(T, init, op, rhs, expr)                                    \
+  do {                                                                         \
+    _Atomic(T) a = (init);                                                     \
+    T old = __c11_atomic_fetch_##op(&a, (rhs), __ATOMIC_SEQ_CST);              \
+    assert(old == (T)(init));                                                  \
+    assert((T)a == (T)(expr));                                                 \
+  } while (0)
+
+static void test_ops(void) {
+  CHECK_FETCH(S37, 100, add, 5, 105);
+  CHECK_FETCH(S37, 100, sub, 40, 60);
+  CHECK_FETCH(S37, -3, add, 1, -2);
+  CHECK_FETCH(U37, 7, add, 9, 16);
+  CHECK_FETCH(S37, 0x15, and, 0x13, 0x11);
+  CHECK_FETCH(S37, 0x10, or, 5, 0x15);
+  CHECK_FETCH(S37, 0x1F, xor, 0x15, 0x0A);
+  CHECK_FETCH(S37, -5, min, -7, -7);    // signed: -7 < -5
+  CHECK_FETCH(U37, 5, min, (U37)-1, 5); // unsigned: 5 < 2^37-1
+  CHECK_FETCH(S37, 3, max, 9, 9);
+  CHECK_FETCH(S37, 0x15, nand, 0x13, (S37) ~(0x15 & 0x13));
+  // Wide widths: the libcall loop (no padding, and padded).
+  CHECK_FETCH(S256, 100, add, 5, 105);
+  CHECK_FETCH(S256, 1, or, 0xFE, 0xFF);
+  CHECK_FETCH(S200, 100, add, 5, 105);
+}
+
+// Seed non-canonical padding through a union, then RMW. A loop that carried a
+// re-canonicalized expected would never match memory and hang here.
+static void test_dirty_padding(void) {
+  union {
+    _Atomic(S37) a;
+    unsigned long b;
+  } s;
+  s.b = ((unsigned long)1 << 40) | 5u; // value bits 5, padding bit 40 set
+  S37 old = __c11_atomic_fetch_add(&s.a, 1, __ATOMIC_SEQ_CST);
+  assert(old == 5 && (S37)s.a == 6);
+
+  union {
+    _Atomic(U37) a;
+    unsigned long b;
+  } u;
+  u.b = ((unsigned long)3 << 50) | 7u;
+  U37 uold = __c11_atomic_fetch_add(&u.a, 1, __ATOMIC_SEQ_CST);
+  assert(uold == 7 && (U37)u.a == 8);
+
+  // Wide padded width (libcall loop): _BitInt(200) has 56 padding bits in its
+  // 32-byte storage. Set the overlay at value level (endian-independent): low
+  // 200 bits = 5, a padding bit (240) dirtied.
+  union {
+    _Atomic(S200) a;
+    unsigned _BitInt(256) full;
+  } w;
+  w.full = (unsigned _BitInt(256))5 | ((unsigned _BitInt(256))0xAA << 240);
+  S200 wold = __c11_atomic_fetch_add(&w.a, 1, __ATOMIC_SEQ_CST);
+  assert(wold == 5 && (S200)w.a == 6);
+}
+
+int main(void) {
+  test_ops();
+  test_dirty_padding();
+  printf("PASS\n");
+  return 0;
+}

>From 88f329b57018f8646b65484f0d36e92c9e54fd27 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sat, 27 Jun 2026 09:56:57 +0200
Subject: [PATCH 8/9] [Clang][test] Expand _BitInt atomic Sema and CodeGen
 coverage

Sema: add reject cases (non-_Atomic pointer, wrong arity, atomic _BitInt
bit-field) so lifting the _BitInt rejection does not silently drop the
atomic-specific checks, plus an __atomic_add_fetch (returns-new) accept.
CodeGen: add an unsigned arithmetic RMW (zero-extended desired) and signed
max / unsigned min, exercising the zext path and the icmp sgt/ule predicates
the previous functions did not.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/test/CodeGen/atomic-bitint.c | 140 +++++++++++++++++++++++++++++
 clang/test/Sema/atomic-bitint.c    |  15 +++-
 2 files changed, 152 insertions(+), 3 deletions(-)

diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
index bc1e165fd90e3..dda8f644f3fec 100644
--- a/clang/test/CodeGen/atomic-bitint.c
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -262,6 +262,146 @@ U37 min37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
 }
 
+// Unsigned arithmetic RMW: the desired is zero-extended, not sign-extended.
+// CHECK-LABEL: define dso_local i64 @uadd37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = zext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = zext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP7:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[NEW:%.*]] = add i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = zext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP5]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP7]] = extractvalue { i64, i1 } [[TMP6]], 0
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
+// CHECK-NEXT:    br i1 [[TMP8]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP9:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP9]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
+U37 uadd37(_Atomic(U37) *p, U37 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Signed max computes at the value width with a signed compare.
+// CHECK-LABEL: define dso_local i64 @max37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP8:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp sgt i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[LOADEDV4]], i37 [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP8]] = extractvalue { i64, i1 } [[TMP7]], 0
+// CHECK-NEXT:    [[TMP9:%.*]] = extractvalue { i64, i1 } [[TMP7]], 1
+// CHECK-NEXT:    br i1 [[TMP9]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP10:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP10]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
+S37 max37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_fetch_max(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Unsigned min computes at the value width with an unsigned compare.
+// CHECK-LABEL: define dso_local i64 @umin37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = zext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = zext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP8:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp ule i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[LOADEDV4]], i37 [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = zext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP8]] = extractvalue { i64, i1 } [[TMP7]], 0
+// CHECK-NEXT:    [[TMP9:%.*]] = extractvalue { i64, i1 } [[TMP7]], 1
+// CHECK-NEXT:    br i1 [[TMP9]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP10:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP10]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
+U37 umin37(_Atomic(U37) *p, U37 v) {
+  return __c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
+}
+
 // No padding: direct atomicrmw, no loop.
 // CHECK-LABEL: define dso_local i64 @add64(
 // CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V:%.*]]) #[[ATTR0]] {
diff --git a/clang/test/Sema/atomic-bitint.c b/clang/test/Sema/atomic-bitint.c
index fbb4c518438fb..3bd4faf22e7be 100644
--- a/clang/test/Sema/atomic-bitint.c
+++ b/clang/test/Sema/atomic-bitint.c
@@ -7,8 +7,6 @@
 // code imposes no width cap of its own; widths past 128 are available wherever
 // the target accepts _BitInt > 128 (x86 and RISC-V today).
 
-// expected-no-diagnostics
-
 _Atomic(_BitInt(4))    a4;     // small
 _Atomic(_BitInt(9))    a9;     // non-power-of-two
 _Atomic(_BitInt(37))   a37;    // padded
@@ -35,9 +33,20 @@ void c11_builtins(_Atomic(_BitInt(37)) *p, _BitInt(37) v, _BitInt(37) *e) {
   (void)__c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
 }
 
-// The GNU __atomic_* builtins take a plain _BitInt pointer.
+// The GNU __atomic_* builtins take a plain _BitInt pointer; the _fetch forms
+// return the new value.
 void gnu_builtins(_BitInt(37) *p, _BitInt(37) v) {
   (void)__atomic_load_n(p, __ATOMIC_SEQ_CST);
   __atomic_store_n(p, v, __ATOMIC_SEQ_CST);
   (void)__atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+  (void)__atomic_add_fetch(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Lifting the _BitInt rejection must not lose the atomic-specific checks.
+void rejects(_Atomic(_BitInt(37)) *ap, _BitInt(37) *p, _BitInt(37) v) {
+  (void)__c11_atomic_load(ap); // expected-error {{too few arguments to function call}}
+  (void)__c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST); // expected-error {{must be a pointer to _Atomic}}
 }
+struct WithAtomicBitIntField {
+  _Atomic(_BitInt(5)) f : 3; // expected-error {{bit-field 'f' has non-integral type}}
+};

>From 9d349acf9dc9f5c74c4103c3ed60252ca95ff53e Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sat, 27 Jun 2026 09:56:58 +0200
Subject: [PATCH 9/9] [compiler-rt] Harden the _BitInt atomic runtime test

Use uint64_t for the dirty-padding overlay (unsigned long is 32-bit on
LLP64, where the padding-bit shift was undefined). Read the storage back
after a converged RMW to confirm the padding is canonicalized, and add
returns-new (__atomic_*_fetch) and non-seq_cst ordering coverage.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 .../test/builtins/Unit/atomic_bitint_test.c   | 37 +++++++++++++++++--
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/compiler-rt/test/builtins/Unit/atomic_bitint_test.c b/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
index 33a745348a6f0..e0cc3aef61bc2 100644
--- a/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
+++ b/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
@@ -17,6 +17,7 @@
 //===----------------------------------------------------------------------===//
 
 #include <assert.h>
+#include <stdint.h>
 #include <stdio.h>
 
 typedef signed _BitInt(37) S37;
@@ -55,21 +56,25 @@ static void test_ops(void) {
 // Seed non-canonical padding through a union, then RMW. A loop that carried a
 // re-canonicalized expected would never match memory and hang here.
 static void test_dirty_padding(void) {
+  // uint64_t (not unsigned long, which is 32-bit on LLP64) so the padding bit
+  // is representable and the overlay matches the 8-byte atomic.
   union {
     _Atomic(S37) a;
-    unsigned long b;
+    uint64_t b;
   } s;
-  s.b = ((unsigned long)1 << 40) | 5u; // value bits 5, padding bit 40 set
+  s.b = ((uint64_t)1 << 40) | 5u; // value bits 5, padding bit 40 set
   S37 old = __c11_atomic_fetch_add(&s.a, 1, __ATOMIC_SEQ_CST);
   assert(old == 5 && (S37)s.a == 6);
+  assert((s.b >> 37) == 0); // padding canonicalized (positive value)
 
   union {
     _Atomic(U37) a;
-    unsigned long b;
+    uint64_t b;
   } u;
-  u.b = ((unsigned long)3 << 50) | 7u;
+  u.b = ((uint64_t)3 << 50) | 7u;
   U37 uold = __c11_atomic_fetch_add(&u.a, 1, __ATOMIC_SEQ_CST);
   assert(uold == 7 && (U37)u.a == 8);
+  assert((u.b >> 37) == 0); // padding canonicalized (zero-extended)
 
   // Wide padded width (libcall loop): _BitInt(200) has 56 padding bits in its
   // 32-byte storage. Set the overlay at value level (endian-independent): low
@@ -81,11 +86,35 @@ static void test_dirty_padding(void) {
   w.full = (unsigned _BitInt(256))5 | ((unsigned _BitInt(256))0xAA << 240);
   S200 wold = __c11_atomic_fetch_add(&w.a, 1, __ATOMIC_SEQ_CST);
   assert(wold == 5 && (S200)w.a == 6);
+  assert((w.full >> 200) == 0); // padding canonicalized (positive value)
+}
+
+// The _fetch builtins return the new value, not the old one.
+static void test_returns_new(void) {
+  S37 a = 100;
+  assert(__atomic_add_fetch(&a, 5, __ATOMIC_SEQ_CST) == 105);
+  assert(__atomic_sub_fetch(&a, 10, __ATOMIC_SEQ_CST) == 95);
+  U37 u = 0;
+  assert(__atomic_or_fetch(&u, 0xF, __ATOMIC_SEQ_CST) == 0xF);
+  S200 w = 100;
+  assert(__atomic_add_fetch(&w, 5, __ATOMIC_SEQ_CST) == 105);
+}
+
+// Each non-seq_cst ordering drives the loop's load/cmpxchg ordering.
+static void test_orderings(void) {
+  _Atomic(S37) a = 10;
+  (void)__c11_atomic_fetch_add(&a, 1, __ATOMIC_RELAXED);
+  (void)__c11_atomic_fetch_add(&a, 1, __ATOMIC_ACQUIRE);
+  (void)__c11_atomic_fetch_add(&a, 1, __ATOMIC_RELEASE);
+  (void)__c11_atomic_fetch_add(&a, 1, __ATOMIC_ACQ_REL);
+  assert((S37)a == 14);
 }
 
 int main(void) {
   test_ops();
   test_dirty_padding();
+  test_returns_new();
+  test_orderings();
   printf("PASS\n");
   return 0;
 }


From libcxx-commits at lists.llvm.org  Wed Jul  1 05:41:39 2026
From: libcxx-commits at lists.llvm.org (Xavier Roche via libcxx-commits)
Date: Wed, 01 Jul 2026 05:41:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] Cover byteswap _BitInt
 padding on every ABI with width 72 (PR #206360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206360@github.com>
Message-ID: <6a450b03.e9d484b9.34b6c.a678@mx.google.com>


https://github.com/xroche updated https://github.com/llvm/llvm-project/pull/206360

>From 9416d0d71902f65c93271943d01eddd09be707ac Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sun, 28 Jun 2026 21:10:43 +0200
Subject: [PATCH 1/2] [libc++][test] Cover byteswap _BitInt padding on every
 ABI with width 72

The byteswap padding-bit test added in #196512 used unsigned _BitInt(96),
which #205295 had to skip on 32-bit x86: the i386 System V ABI aligns
_BitInt to 4 bytes and packs _BitInt(96) into exactly 12 bytes with no
padding, so std::byteswap accepts it and the expected diagnostic never
fires. The skip used !defined(__i386__) as a stand-in because the object
layout that drives the result is not visible to the preprocessor.

Use unsigned _BitInt(72) instead. Its 9 value bytes round up to 12 on
32-bit x86 and 16 everywhere else, so the type always carries padding and
std::byteswap always rejects it. The case now runs on every target,
including 32-bit x86, with no target check.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 .../test/std/numerics/bit/byteswap.verify.cpp | 22 +++++++++----------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/numerics/bit/byteswap.verify.cpp b/libcxx/test/std/numerics/bit/byteswap.verify.cpp
index f9b6954f525da..a8026b41c0f18 100644
--- a/libcxx/test/std/numerics/bit/byteswap.verify.cpp
+++ b/libcxx/test/std/numerics/bit/byteswap.verify.cpp
@@ -103,23 +103,21 @@ void test_unsigned_56() {
 
 // Same dispatch-availability guard as test_unsigned_65 above.
 #  if TEST_HAS_BUILTIN(__builtin_bswapg) || !defined(TEST_HAS_NO_INT128)
-#    if __BITINT_MAXWIDTH__ >= 80
-void test_unsigned_80() {
-  // sizeof(_BitInt(80)) == 16 on x86_64; 48 padding bits. Width 80 is also
-  // a multiple of 16, so bswapg would accept it without the static_assert.
-  unsigned _BitInt(80) v = 0;
+// 72 value bits leave padding on every ABI, unlike _BitInt(96), which is
+// exactly 12 bytes with no padding on 32-bit x86 (#205295).
+#    if __BITINT_MAXWIDTH__ >= 72
+void test_unsigned_72() {
+  unsigned _BitInt(72) v = 0;
   // expected-error-re@*:* {{{{(std::byteswap requires T to have no padding bits|byteswap is unimplemented for integral types of this size)}}}}
   (void)std::byteswap(v);
 }
 #    endif
 
-// 32-bit x86 packs _BitInt(96) into 12 bytes (no padding), so std::byteswap
-// accepts it there; gate the case out. sizeof isn't available to the
-// preprocessor, so this is a target check rather than a has-padding predicate.
-#    if __BITINT_MAXWIDTH__ >= 96 && !defined(__i386__)
-void test_unsigned_96() {
-  // sizeof(_BitInt(96)) == 16 here; 32 padding bits.
-  unsigned _BitInt(96) v = 0;
+#    if __BITINT_MAXWIDTH__ >= 80
+void test_unsigned_80() {
+  // sizeof(_BitInt(80)) == 16 on x86_64; 48 padding bits. Width 80 is also
+  // a multiple of 16, so bswapg would accept it without the static_assert.
+  unsigned _BitInt(80) v = 0;
   // expected-error-re@*:* {{{{(std::byteswap requires T to have no padding bits|byteswap is unimplemented for integral types of this size)}}}}
   (void)std::byteswap(v);
 }

>From 0964ab51bb05685eae6002d32136d0ea071a1307 Mon Sep 17 00:00:00 2001
From: Xavier Roche <roche at httrack.com>
Date: Wed, 1 Jul 2026 14:41:30 +0200
Subject: [PATCH 2/2] Update libcxx/test/std/numerics/bit/byteswap.verify.cpp

Co-authored-by: A. Jiang <de34 at live.cn>
---
 libcxx/test/std/numerics/bit/byteswap.verify.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/bit/byteswap.verify.cpp b/libcxx/test/std/numerics/bit/byteswap.verify.cpp
index a8026b41c0f18..bbd3312a2457a 100644
--- a/libcxx/test/std/numerics/bit/byteswap.verify.cpp
+++ b/libcxx/test/std/numerics/bit/byteswap.verify.cpp
@@ -103,8 +103,7 @@ void test_unsigned_56() {
 
 // Same dispatch-availability guard as test_unsigned_65 above.
 #  if TEST_HAS_BUILTIN(__builtin_bswapg) || !defined(TEST_HAS_NO_INT128)
-// 72 value bits leave padding on every ABI, unlike _BitInt(96), which is
-// exactly 12 bytes with no padding on 32-bit x86 (#205295).
+// 72 value bits leave padding on every ABI.
 #    if __BITINT_MAXWIDTH__ >= 72
 void test_unsigned_72() {
   unsigned _BitInt(72) v = 0;


From libcxx-commits at lists.llvm.org  Wed Jul  1 05:41:56 2026
From: libcxx-commits at lists.llvm.org (Xavier Roche via libcxx-commits)
Date: Wed, 01 Jul 2026 05:41:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] Cover byteswap _BitInt
 padding on every ABI with width 72 (PR #206360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206360@github.com>
Message-ID: <6a450b14.eea6ab6e.29c4b3.50ed@mx.google.com>


https://github.com/xroche updated https://github.com/llvm/llvm-project/pull/206360

>From 9416d0d71902f65c93271943d01eddd09be707ac Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sun, 28 Jun 2026 21:10:43 +0200
Subject: [PATCH 1/2] [libc++][test] Cover byteswap _BitInt padding on every
 ABI with width 72

The byteswap padding-bit test added in #196512 used unsigned _BitInt(96),
which #205295 had to skip on 32-bit x86: the i386 System V ABI aligns
_BitInt to 4 bytes and packs _BitInt(96) into exactly 12 bytes with no
padding, so std::byteswap accepts it and the expected diagnostic never
fires. The skip used !defined(__i386__) as a stand-in because the object
layout that drives the result is not visible to the preprocessor.

Use unsigned _BitInt(72) instead. Its 9 value bytes round up to 12 on
32-bit x86 and 16 everywhere else, so the type always carries padding and
std::byteswap always rejects it. The case now runs on every target,
including 32-bit x86, with no target check.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 .../test/std/numerics/bit/byteswap.verify.cpp | 22 +++++++++----------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/numerics/bit/byteswap.verify.cpp b/libcxx/test/std/numerics/bit/byteswap.verify.cpp
index f9b6954f525da..a8026b41c0f18 100644
--- a/libcxx/test/std/numerics/bit/byteswap.verify.cpp
+++ b/libcxx/test/std/numerics/bit/byteswap.verify.cpp
@@ -103,23 +103,21 @@ void test_unsigned_56() {
 
 // Same dispatch-availability guard as test_unsigned_65 above.
 #  if TEST_HAS_BUILTIN(__builtin_bswapg) || !defined(TEST_HAS_NO_INT128)
-#    if __BITINT_MAXWIDTH__ >= 80
-void test_unsigned_80() {
-  // sizeof(_BitInt(80)) == 16 on x86_64; 48 padding bits. Width 80 is also
-  // a multiple of 16, so bswapg would accept it without the static_assert.
-  unsigned _BitInt(80) v = 0;
+// 72 value bits leave padding on every ABI, unlike _BitInt(96), which is
+// exactly 12 bytes with no padding on 32-bit x86 (#205295).
+#    if __BITINT_MAXWIDTH__ >= 72
+void test_unsigned_72() {
+  unsigned _BitInt(72) v = 0;
   // expected-error-re@*:* {{{{(std::byteswap requires T to have no padding bits|byteswap is unimplemented for integral types of this size)}}}}
   (void)std::byteswap(v);
 }
 #    endif
 
-// 32-bit x86 packs _BitInt(96) into 12 bytes (no padding), so std::byteswap
-// accepts it there; gate the case out. sizeof isn't available to the
-// preprocessor, so this is a target check rather than a has-padding predicate.
-#    if __BITINT_MAXWIDTH__ >= 96 && !defined(__i386__)
-void test_unsigned_96() {
-  // sizeof(_BitInt(96)) == 16 here; 32 padding bits.
-  unsigned _BitInt(96) v = 0;
+#    if __BITINT_MAXWIDTH__ >= 80
+void test_unsigned_80() {
+  // sizeof(_BitInt(80)) == 16 on x86_64; 48 padding bits. Width 80 is also
+  // a multiple of 16, so bswapg would accept it without the static_assert.
+  unsigned _BitInt(80) v = 0;
   // expected-error-re@*:* {{{{(std::byteswap requires T to have no padding bits|byteswap is unimplemented for integral types of this size)}}}}
   (void)std::byteswap(v);
 }

>From 0964ab51bb05685eae6002d32136d0ea071a1307 Mon Sep 17 00:00:00 2001
From: Xavier Roche <roche at httrack.com>
Date: Wed, 1 Jul 2026 14:41:30 +0200
Subject: [PATCH 2/2] Update libcxx/test/std/numerics/bit/byteswap.verify.cpp

Co-authored-by: A. Jiang <de34 at live.cn>
---
 libcxx/test/std/numerics/bit/byteswap.verify.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/bit/byteswap.verify.cpp b/libcxx/test/std/numerics/bit/byteswap.verify.cpp
index a8026b41c0f18..bbd3312a2457a 100644
--- a/libcxx/test/std/numerics/bit/byteswap.verify.cpp
+++ b/libcxx/test/std/numerics/bit/byteswap.verify.cpp
@@ -103,8 +103,7 @@ void test_unsigned_56() {
 
 // Same dispatch-availability guard as test_unsigned_65 above.
 #  if TEST_HAS_BUILTIN(__builtin_bswapg) || !defined(TEST_HAS_NO_INT128)
-// 72 value bits leave padding on every ABI, unlike _BitInt(96), which is
-// exactly 12 bytes with no padding on 32-bit x86 (#205295).
+// 72 value bits leave padding on every ABI.
 #    if __BITINT_MAXWIDTH__ >= 72
 void test_unsigned_72() {
   unsigned _BitInt(72) v = 0;


From libcxx-commits at lists.llvm.org  Wed Jul  1 05:46:33 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 05:46:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] Cover byteswap _BitInt
 padding on every ABI with width 72 (PR #206360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206360@github.com>
Message-ID: <6a450c29.d5797644.130832.9d61@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/206360

From libcxx-commits at lists.llvm.org  Wed Jul  1 05:50:36 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 05:50:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't #define __unused with
 glibc in system_reserved_names.gen.py (PR #206971)
Message-ID: <llvm.org/llvm/llvm-project/pull/206971@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/206971

Newer glibc versions use the identifier `__unused`. To run the test successufly again disable the `__unused` check with glibc.


>From 0e7a566f7a27663a25f83154e8fd8f3d11763186 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 1 Jul 2026 14:48:13 +0200
Subject: [PATCH] [libc++] Don't #define __unused with glibc in
 system_reserved_names.gen.py

---
 libcxx/test/libcxx/system_reserved_names.gen.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/system_reserved_names.gen.py b/libcxx/test/libcxx/system_reserved_names.gen.py
index aaaa385409be4..a0364eeed3256 100644
--- a/libcxx/test/libcxx/system_reserved_names.gen.py
+++ b/libcxx/test/libcxx/system_reserved_names.gen.py
@@ -122,7 +122,7 @@
 #define __release SYSTEM_RESERVED_NAME
 
 // Android and FreeBSD use this for __attribute__((__unused__))
-#if !defined(__FreeBSD__)  && !defined(__ANDROID__)
+#if !defined(__FreeBSD__)  && !defined(__ANDROID__) && !defined(__GLIBC__)
 #define __unused SYSTEM_RESERVED_NAME
 #endif
 


From libcxx-commits at lists.llvm.org  Wed Jul  1 05:51:15 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 05:51:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't #define __unused with
 glibc in system_reserved_names.gen.py (PR #206971)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206971@github.com>
Message-ID: <6a450d43.4a342cbc.22496e.67be@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

Newer glibc versions use the identifier `__unused`. To run the test successufly again disable the `__unused` check with glibc.


---
Full diff: https://github.com/llvm/llvm-project/pull/206971.diff


1 Files Affected:

- (modified) libcxx/test/libcxx/system_reserved_names.gen.py (+1-1) 


``````````diff
diff --git a/libcxx/test/libcxx/system_reserved_names.gen.py b/libcxx/test/libcxx/system_reserved_names.gen.py
index aaaa385409be4..a0364eeed3256 100644
--- a/libcxx/test/libcxx/system_reserved_names.gen.py
+++ b/libcxx/test/libcxx/system_reserved_names.gen.py
@@ -122,7 +122,7 @@
 #define __release SYSTEM_RESERVED_NAME
 
 // Android and FreeBSD use this for __attribute__((__unused__))
-#if !defined(__FreeBSD__)  && !defined(__ANDROID__)
+#if !defined(__FreeBSD__)  && !defined(__ANDROID__) && !defined(__GLIBC__)
 #define __unused SYSTEM_RESERVED_NAME
 #endif
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/206971

From libcxx-commits at lists.llvm.org  Wed Jul  1 05:51:24 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 05:51:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 3c16a8b - [libc++] Don't #define __unused
 with glibc in system_reserved_names.gen.py (#206971)
Message-ID: <6a450d4c.1a4df1a5.3108a4.b2dc@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-01T14:51:19+02:00
New Revision: 3c16a8b183693e8df2e2173764e10c33c33ab06a

URL: https://github.com/llvm/llvm-project/commit/3c16a8b183693e8df2e2173764e10c33c33ab06a
DIFF: https://github.com/llvm/llvm-project/commit/3c16a8b183693e8df2e2173764e10c33c33ab06a.diff

LOG: [libc++] Don't #define __unused with glibc in system_reserved_names.gen.py (#206971)

Newer glibc versions use the identifier `__unused`. To run the test
successufly again disable the `__unused` check with glibc.

Added: 
    

Modified: 
    libcxx/test/libcxx/system_reserved_names.gen.py

Removed: 
    


################################################################################
diff  --git a/libcxx/test/libcxx/system_reserved_names.gen.py b/libcxx/test/libcxx/system_reserved_names.gen.py
index aaaa385409be4..a0364eeed3256 100644
--- a/libcxx/test/libcxx/system_reserved_names.gen.py
+++ b/libcxx/test/libcxx/system_reserved_names.gen.py
@@ -122,7 +122,7 @@
 #define __release SYSTEM_RESERVED_NAME
 
 // Android and FreeBSD use this for __attribute__((__unused__))
-#if !defined(__FreeBSD__)  && !defined(__ANDROID__)
+#if !defined(__FreeBSD__)  && !defined(__ANDROID__) && !defined(__GLIBC__)
 #define __unused SYSTEM_RESERVED_NAME
 #endif
 


        

From libcxx-commits at lists.llvm.org  Wed Jul  1 05:51:26 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 05:51:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't #define __unused with
 glibc in system_reserved_names.gen.py (PR #206971)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206971@github.com>
Message-ID: <6a450d4e.dd2b4bd3.10c1cf.ee0e@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/206971

From libcxx-commits at lists.llvm.org  Wed Jul  1 05:51:37 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 05:51:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't make libc++ headers system
 headers in the modulemap (PR #205509)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205509@github.com>
Message-ID: <6a450d59.cb30c3e4.37526.7be9@mx.google.com>


================
@@ -122,7 +122,7 @@
 #define __release SYSTEM_RESERVED_NAME
 
 // Android and FreeBSD use this for __attribute__((__unused__))
-#if !defined(__FreeBSD__)  && !defined(__ANDROID__)
+#if !defined(__FreeBSD__)  && !defined(__ANDROID__) && !defined(__GLIBC__)
----------------
philnik777 wrote:

Done in #206971.

https://github.com/llvm/llvm-project/pull/205509

From libcxx-commits at lists.llvm.org  Wed Jul  1 05:52:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 05:52:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 28f08a6 - [libc++] Don't make libc++
 headers system headers in the modulemap (#205509)
Message-ID: <6a450da7.58bdd12c.2e0810.38f3@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-01T14:52:50+02:00
New Revision: 28f08a6f64a7677ee5e1af6729103cf35df366f9

URL: https://github.com/llvm/llvm-project/commit/28f08a6f64a7677ee5e1af6729103cf35df366f9
DIFF: https://github.com/llvm/llvm-project/commit/28f08a6f64a7677ee5e1af6729103cf35df366f9.diff

LOG: [libc++] Don't make libc++ headers system headers in the modulemap (#205509)

We currently have `[system]` attributes in the modulemap, which
automatically makes the headers inside the module system headers. This
disables warnings when running the tests, which we generally don't want.
We also already have `#pragma GCC system_header` everywhere and use
`-isystem`, so there isn't much need for the tag in the modulemap as
well.

Added: 
    

Modified: 
    libcxx/include/__locale
    libcxx/include/module.modulemap.in
    libcxx/include/syncstream
    libcxx/test/std/depr/depr.cpp.headers/ccomplex.verify.cpp
    libcxx/test/std/depr/depr.cpp.headers/ciso646.verify.cpp
    libcxx/test/std/depr/depr.cpp.headers/cstdalign.verify.cpp
    libcxx/test/std/depr/depr.cpp.headers/cstdbool.verify.cpp
    libcxx/test/std/depr/depr.cpp.headers/ctgmath.verify.cpp
    libcxx/test/std/library/description/conventions/expos.only.func/synth_three_way.pass.cpp
    libcxx/test/std/ranges/range.factories/range.iota.view/ctor.value.bound.pass.cpp
    libcxx/test/std/ranges/range.factories/range.iota.view/end.pass.cpp
    libcxx/test/std/ranges/range.factories/range.iota.view/iterator/star.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/three_way.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__locale b/libcxx/include/__locale
index 99d29c167f29a..bb8f8e7bbf5d9 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -334,6 +334,8 @@ public:
 #    endif // defined(__BIONIC__)
 #  elif defined(__GLIBC__)
   typedef unsigned short mask;
+  _LIBCPP_DIAGNOSTIC_PUSH
+  _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmodules-ambiguous-internal-linkage")
   static const mask space  = _ISspace;
   static const mask print  = _ISprint;
   static const mask cntrl  = _IScntrl;
@@ -344,6 +346,7 @@ public:
   static const mask punct  = _ISpunct;
   static const mask xdigit = _ISxdigit;
   static const mask blank  = _ISblank;
+  _LIBCPP_DIAGNOSTIC_POP
 #    if defined(__mips__) || (BYTE_ORDER == BIG_ENDIAN)
   static const mask __regex_word = static_cast<mask>(_ISbit(15));
 #    else

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b9ddfd5bbf4cd..316785ddfe9b3 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1,6 +1,6 @@
 // This module contains headers related to the configuration of the library. These headers
 // are free of any dependency on the rest of libc++.
-module std_config [system] {
+module std_config {
   @LIBCXX_CONFIG_SITE_MODULE_ENTRY@ // generated via CMake
   textual header "__config"
   textual header "__configuration/abi.h"
@@ -18,7 +18,7 @@ module std_config [system] {
   textual header "version"
 }
 
-module std_core [system] {
+module std_core {
   module cstddef {
     module byte         { header "__cstddef/byte.h" }
     module max_align_t  {
@@ -380,7 +380,7 @@ module std_core [system] {
   }
 } // module std_core
 
-module std [system] {
+module std {
   module algorithm {
     module adjacent_find                          { header "__algorithm/adjacent_find.h" }
     module all_of                                 { header "__algorithm/all_of.h" }
@@ -1625,7 +1625,7 @@ module std [system] {
   }
 
   // TODO: Understand why this needs to live in its own module
-  module locale_base [system] {
+  module locale_base {
     header "__locale"
     export *
   }
@@ -2451,57 +2451,57 @@ module std [system] {
 // These modules need to be their own top-level modules because they depend on the system-provided
 // headers (via include_next), which are then free to include other C headers provided by libc++.
 // If we group these headers in a single module, we would end up with circular dependencies.
-module std_complex_h [system] {
+module std_complex_h {
   header "complex.h"
   export *
 }
-module std_errno_h [system] {
+module std_errno_h {
   header "errno.h"
   export *
 }
-module std_math_h [system] {
+module std_math_h {
   header "math.h"
   export *
 }
-module std_stdatomic_h [system] {
+module std_stdatomic_h {
   header "stdatomic.h"
   export *
 }
-module std_stddef_h [system] {
+module std_stddef_h {
   // <stddef.h> supports being included multiple times with 
diff erent pre-defined macros
   textual header "stddef.h"
 }
-module std_stdio_h [system] {
+module std_stdio_h {
   // <stdio.h> supports being included multiple times with 
diff erent pre-defined macros
   textual header "stdio.h"
 }
-module std_stdlib_h [system] {
+module std_stdlib_h {
   // <stdlib.h> supports being included multiple times with 
diff erent pre-defined macros
   textual header "stdlib.h"
 }
-module std_string_h [system] {
+module std_string_h {
   header "string.h"
   export *
 }
-module std_tgmath_h [system] {
+module std_tgmath_h {
   header "tgmath.h"
   export *
 }
-module std_uchar_h [system] {
+module std_uchar_h {
   header "uchar.h"
   export *
 }
-module std_wchar_h [system] {
+module std_wchar_h {
   // <wchar.h> supports being included multiple times with 
diff erent pre-defined macros
   textual header "wchar.h"
 }
-module std_wctype_h [system] {
+module std_wctype_h {
   header "wctype.h"
   export *
 }
 
 // This header is used by other C compatibility headers so it needs to be in its own module.
-module std_private_mbstate_t [system] {
+module std_private_mbstate_t {
   header "__mbstate_t.h"
   export *
 }

diff  --git a/libcxx/include/syncstream b/libcxx/include/syncstream
index 03e18ddbdfd22..ca1a89e6bfe0d 100644
--- a/libcxx/include/syncstream
+++ b/libcxx/include/syncstream
@@ -196,7 +196,8 @@ public:
   // calling __inc_reference.
   //
   // pre: __ptr is in __lut_
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI lock_guard<mutex> __get_lock([[maybe_unused]] void* __ptr) noexcept {
+  _LIBCPP_NO_THREAD_SAFETY_ANALYSIS [[nodiscard]] _LIBCPP_HIDE_FROM_ABI lock_guard<mutex>
+  __get_lock([[maybe_unused]] void* __ptr) noexcept {
     shared_lock __lock{__mutex_};
     return lock_guard{__get_it(__ptr)->second.__mutex};
   }

diff  --git a/libcxx/test/std/depr/depr.cpp.headers/ccomplex.verify.cpp b/libcxx/test/std/depr/depr.cpp.headers/ccomplex.verify.cpp
index 900ca0e5e1c5e..3be5117503d59 100644
--- a/libcxx/test/std/depr/depr.cpp.headers/ccomplex.verify.cpp
+++ b/libcxx/test/std/depr/depr.cpp.headers/ccomplex.verify.cpp
@@ -12,7 +12,6 @@
 // When built with modules, <ccomplex> should be omitted.
 
 // UNSUPPORTED: c++03, c++11, c++14
-// UNSUPPORTED: clang-modules-build
 
 // FIXME: using `#warning` causes diagnostics from system headers which include deprecated headers. This can only be
 // enabled again once https://github.com/llvm/llvm-project/pull/168041 (or a similar feature) has landed, since that

diff  --git a/libcxx/test/std/depr/depr.cpp.headers/ciso646.verify.cpp b/libcxx/test/std/depr/depr.cpp.headers/ciso646.verify.cpp
index a1ca842bc62ab..09ad85bab996d 100644
--- a/libcxx/test/std/depr/depr.cpp.headers/ciso646.verify.cpp
+++ b/libcxx/test/std/depr/depr.cpp.headers/ciso646.verify.cpp
@@ -12,7 +12,6 @@
 // When built with modules, <ciso646> should be omitted.
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: clang-modules-build
 
 // FIXME: using `#warning` causes diagnostics from system headers which include deprecated headers. This can only be
 // enabled again once https://github.com/llvm/llvm-project/pull/168041 (or a similar feature) has landed, since that

diff  --git a/libcxx/test/std/depr/depr.cpp.headers/cstdalign.verify.cpp b/libcxx/test/std/depr/depr.cpp.headers/cstdalign.verify.cpp
index 503a87658ac02..be0074a03709a 100644
--- a/libcxx/test/std/depr/depr.cpp.headers/cstdalign.verify.cpp
+++ b/libcxx/test/std/depr/depr.cpp.headers/cstdalign.verify.cpp
@@ -12,7 +12,6 @@
 // When built with modules, <cstdalign> should be omitted.
 
 // UNSUPPORTED: c++03, c++11, c++14
-// UNSUPPORTED: clang-modules-build
 
 // FIXME: using `#warning` causes diagnostics from system headers which include deprecated headers. This can only be
 // enabled again once https://github.com/llvm/llvm-project/pull/168041 (or a similar feature) has landed, since that

diff  --git a/libcxx/test/std/depr/depr.cpp.headers/cstdbool.verify.cpp b/libcxx/test/std/depr/depr.cpp.headers/cstdbool.verify.cpp
index 80025c5ab72d2..90fa5c0ac9b3f 100644
--- a/libcxx/test/std/depr/depr.cpp.headers/cstdbool.verify.cpp
+++ b/libcxx/test/std/depr/depr.cpp.headers/cstdbool.verify.cpp
@@ -12,7 +12,6 @@
 // When built with modules, <cstdbool> should be omitted.
 
 // UNSUPPORTED: c++03, c++11, c++14
-// UNSUPPORTED: clang-modules-build
 
 // FIXME: using `#warning` causes diagnostics from system headers which include deprecated headers. This can only be
 // enabled again once https://github.com/llvm/llvm-project/pull/168041 (or a similar feature) has landed, since that

diff  --git a/libcxx/test/std/depr/depr.cpp.headers/ctgmath.verify.cpp b/libcxx/test/std/depr/depr.cpp.headers/ctgmath.verify.cpp
index 07bdd29648a68..875a9f9bfb81a 100644
--- a/libcxx/test/std/depr/depr.cpp.headers/ctgmath.verify.cpp
+++ b/libcxx/test/std/depr/depr.cpp.headers/ctgmath.verify.cpp
@@ -12,7 +12,6 @@
 // When built with modules, <ctgmath> should be omitted.
 
 // UNSUPPORTED: c++03, c++11, c++14
-// UNSUPPORTED: clang-modules-build
 
 // FIXME: using `#warning` causes diagnostics from system headers which include deprecated headers. This can only be
 // enabled again once https://github.com/llvm/llvm-project/pull/168041 (or a similar feature) has landed, since that

diff  --git a/libcxx/test/std/library/description/conventions/expos.only.func/synth_three_way.pass.cpp b/libcxx/test/std/library/description/conventions/expos.only.func/synth_three_way.pass.cpp
index 158ea46ff2c06..780abf39c577f 100644
--- a/libcxx/test/std/library/description/conventions/expos.only.func/synth_three_way.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/expos.only.func/synth_three_way.pass.cpp
@@ -11,11 +11,8 @@
 // constexpr auto synth-three-way = ...;
 //   via std::tuple<T>(t) <=> std::tuple<U>(u), which exposes its behavior most directly
 
-#include "test_macros.h"
-
-TEST_CLANG_DIAGNOSTIC_IGNORED("-Wsign-compare")
-TEST_GCC_DIAGNOSTIC_IGNORED("-Wsign-compare")
-TEST_MSVC_DIAGNOSTIC_IGNORED(4242 4244) // Various truncation warnings
+// ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-sign-compare
+// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4242 /wd4244
 
 #include <cassert>
 #include <compare>
@@ -24,6 +21,8 @@ TEST_MSVC_DIAGNOSTIC_IGNORED(4242 4244) // Various truncation warnings
 #include <type_traits>
 #include <utility> // declval
 
+#include "test_macros.h"
+
 template <typename T, typename U = T>
 concept can_synth_three_way = requires(T t, U u) { std::tuple<T>(t) <=> std::tuple<U>(u); };
 

diff  --git a/libcxx/test/std/ranges/range.factories/range.iota.view/ctor.value.bound.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/ctor.value.bound.pass.cpp
index b6f4295498644..102b5be558663 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/ctor.value.bound.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/ctor.value.bound.pass.cpp
@@ -8,11 +8,10 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-#include "test_macros.h"
+// ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-sign-compare
 
-TEST_CLANG_DIAGNOSTIC_IGNORED("-Wsign-compare")
-TEST_GCC_DIAGNOSTIC_IGNORED("-Wsign-compare")
-TEST_MSVC_DIAGNOSTIC_IGNORED(4018 4389) // various "signed/unsigned mismatch"
+// various "signed/unsigned mismatch"
+// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4018 /wd4389
 
 // constexpr explicit iota_view(type_identity_t<W> value, type_identity_t<Bound> bound); // explicit since C++23
 

diff  --git a/libcxx/test/std/ranges/range.factories/range.iota.view/end.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/end.pass.cpp
index d12079fdb5eec..800af01d775c1 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/end.pass.cpp
@@ -8,11 +8,10 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-#include "test_macros.h"
+// ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-sign-compare
 
-TEST_CLANG_DIAGNOSTIC_IGNORED("-Wsign-compare")
-TEST_GCC_DIAGNOSTIC_IGNORED("-Wsign-compare")
-TEST_MSVC_DIAGNOSTIC_IGNORED(4018 4389) // various "signed/unsigned mismatch"
+// various "signed/unsigned mismatch"
+// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4018 /wd4389
 
 // constexpr auto end() const;
 // constexpr iterator end() const requires same_as<W, Bound>;
@@ -21,6 +20,7 @@ TEST_MSVC_DIAGNOSTIC_IGNORED(4018 4389) // various "signed/unsigned mismatch"
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "types.h"
 
 template<class T, class U>

diff  --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/star.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/star.pass.cpp
index b776d90c5e5e3..e6f10a4d69c36 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/star.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/star.pass.cpp
@@ -10,16 +10,16 @@
 
 // constexpr W operator*() const noexcept(is_nothrow_copy_constructible_v<W>);
 
-#include "test_macros.h"
+// ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-sign-compare
 
-TEST_CLANG_DIAGNOSTIC_IGNORED("-Wsign-compare")
-TEST_GCC_DIAGNOSTIC_IGNORED("-Wsign-compare")
-TEST_MSVC_DIAGNOSTIC_IGNORED(4018) // various "signed/unsigned mismatch"
+// various "signed/unsigned mismatch"
+// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4018
 
 #include <ranges>
 #include <cassert>
 
 #include "../types.h"
+#include "test_macros.h"
 
 struct NotNoexceptCopy {
   using 
diff erence_type = int;

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/three_way.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/three_way.pass.cpp
index 697d0c03eefe2..f10b1736f1dc8 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/three_way.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/three_way.pass.cpp
@@ -18,11 +18,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-#include "test_macros.h"
-
-TEST_CLANG_DIAGNOSTIC_IGNORED("-Wsign-compare")
-TEST_GCC_DIAGNOSTIC_IGNORED("-Wsign-compare")
-TEST_MSVC_DIAGNOSTIC_IGNORED(4242 4244)
+// ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-sign-compare
+// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4242 /wd4244
 
 #include <array>
 #include <cassert>
@@ -34,6 +31,8 @@ TEST_MSVC_DIAGNOSTIC_IGNORED(4242 4244)
 #include <type_traits> // std::is_constant_evaluated
 #include <utility>
 
+#include "test_macros.h"
+
 // A custom three-way result type
 struct CustomEquality {
   friend constexpr bool operator==(const CustomEquality&, int) noexcept { return true; }


        

From libcxx-commits at lists.llvm.org  Wed Jul  1 05:52:56 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 05:52:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't make libc++ headers system
 headers in the modulemap (PR #205509)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205509@github.com>
Message-ID: <6a450da8.492e23e4.6e404.4850@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/205509

From libcxx-commits at lists.llvm.org  Wed Jul  1 05:59:30 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 05:59:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][FreeBSD] Enable std module on
	FreeBSD (PR #206269)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206269@github.com>
Message-ID: <6a450f32.331483e7.17c361.f3c3@mx.google.com>


https://github.com/frederick-vs-ja ready_for_review https://github.com/llvm/llvm-project/pull/206269

From libcxx-commits at lists.llvm.org  Wed Jul  1 06:00:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 06:00:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][FreeBSD] Enable std module on
	FreeBSD (PR #206269)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206269@github.com>
Message-ID: <6a450f59.4a342cbc.22496e.c097@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: aokblast

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/206269.diff


1 Files Affected:

- (modified) libcxx/utils/libcxx/test/features/misc.py (-1) 


``````````diff
diff --git a/libcxx/utils/libcxx/test/features/misc.py b/libcxx/utils/libcxx/test/features/misc.py
index 92b6dc352d2e1..464d2aaaad79c 100644
--- a/libcxx/utils/libcxx/test/features/misc.py
+++ b/libcxx/utils/libcxx/test/features/misc.py
@@ -217,7 +217,6 @@ def _mingwSupportsModules(cfg):
         # This is not allowed per C11 7.1.2 Standard headers/6
         #  Any declaration of a library function shall have external linkage.
         when=lambda cfg: "__ANDROID__" in compilerMacros(cfg)
-        or "__FreeBSD__" in compilerMacros(cfg)
         or ("_WIN32" in compilerMacros(cfg) and not _mingwSupportsModules(cfg))
         or platform.system().lower().startswith("aix")
         # Avoid building on platforms that don't support modules properly.

``````````

</details>


https://github.com/llvm/llvm-project/pull/206269

From libcxx-commits at lists.llvm.org  Wed Jul  1 06:00:47 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 06:00:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][FreeBSD] Enable std module on
	FreeBSD (PR #206269)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206269@github.com>
Message-ID: <6a450f7f.294e96f5.91a50.264f@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.

LGTM. The remaining failure is unrelated and should probably be handled in another PR.
```
Unexpectedly Passed Tests (1):
  llvm-libc++abi-shared.cfg.in :: thread_local_destruction_order.pass.cpp
```

https://github.com/llvm/llvm-project/pull/206269

From libcxx-commits at lists.llvm.org  Wed Jul  1 06:03:12 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 06:03:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][FreeBSD] Enable std module on
	FreeBSD (PR #206269)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206269@github.com>
Message-ID: <6a451010.2730d81b.11a91c.dc08@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/206269

From libcxx-commits at lists.llvm.org  Wed Jul  1 06:13:54 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 06:13:54 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_=5Blibcxxabi=5D=5BFree?=
 =?utf-8?q?BSD=5D_Re-enable_=60thread=5Flocal=5Fdestruction=5Forder=2Epass?=
 =?utf-8?b?LmNwcOKAjmAuIChQUiAjMTg2MTI5KQ==?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186129@github.com>
Message-ID: <6a451292.1a4df1a5.3108a4.16a1@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/186129

From libcxx-commits at lists.llvm.org  Wed Jul  1 06:15:21 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 06:15:21 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_=5Blibcxxabi=5D=5BFree?=
 =?utf-8?q?BSD=5D_Re-enable_=60thread=5Flocal=5Fdestruction=5Forder=2Epass?=
 =?utf-8?b?LmNwcOKAjmAuIChQUiAjMTg2MTI5KQ==?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186129@github.com>
Message-ID: <6a4512e9.1a4df1a5.3108a4.526c@mx.google.com>


frederick-vs-ja wrote:

I modified the PR title to clarify the meaning of "failed test". Please double-check the changes.
Let's update this PR branch after merging #206269.

https://github.com/llvm/llvm-project/pull/186129

From libcxx-commits at lists.llvm.org  Wed Jul  1 06:30:09 2026
From: libcxx-commits at lists.llvm.org (Jonathan Wakely via libcxx-commits)
Date: Wed, 01 Jul 2026 06:30:09 -0700 (PDT)
Subject: [libcxx-commits] [clang] [compiler-rt] [libcxx] [Clang] Support
 atomic operations on _BitInt(N) (PR #204815)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204815@github.com>
Message-ID: <6a451661.c56fb102.1c15b6.7c73@mx.google.com>


jwakely wrote:

> Also, is GCC going to do the same? @jwakely might want to chime in.

I expect we'll treat this the same way we treat other types with padding bits, i.e. our `__builtin_clear_padding`.

https://github.com/llvm/llvm-project/pull/204815

From libcxx-commits at lists.llvm.org  Wed Jul  1 06:37:15 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 06:37:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Enable std module on FreeBSD (PR
	#206269)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206269@github.com>
Message-ID: <6a45180b.bf38ae28.12214b.e30b@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/206269

From libcxx-commits at lists.llvm.org  Wed Jul  1 06:54:05 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 06:54:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't add #pragma clang
	attribute with GCC (PR #206989)
Message-ID: <llvm.org/llvm/llvm-project/pull/206989@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/206989

GCC doesn't support `#pragma clang attribute`, which causes a bunch of diagnostics. Since we have it behind a macro anyways we can simply define the macro as empty with GCC.


>From d13270a9c78f2cecc7a9d30a42bf07a59ad75601 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 1 Jul 2026 15:48:52 +0200
Subject: [PATCH] [libc++] Don't add #pragma clang attribute with GCC

---
 libcxx/include/__configuration/namespace.h | 23 +++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__configuration/namespace.h b/libcxx/include/__configuration/namespace.h
index 6b733e3076581..1f4b8ce247b13 100644
--- a/libcxx/include/__configuration/namespace.h
+++ b/libcxx/include/__configuration/namespace.h
@@ -46,15 +46,20 @@
 #  define _LIBCPP_POP_ABI_PRAGMA_DIAGNOSTICS
 #endif
 
-#define _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS                                                                           \
-  _LIBCPP_PUSH_ABI_PRAGMA_DIAGNOSTICS                                                                                  \
-  _Pragma(_LIBCPP_TOSTRING(clang attribute _LibcxxExplicitABIAnnotations.push(                                         \
-      __attribute__((__exclude_from_explicit_instantiation__,                                                          \
-                     __visibility__("hidden"),                                                                         \
-                     __abi_tag__(_LIBCPP_TOSTRING(_LIBCPP_ODR_SIGNATURE)))),                                           \
-      apply_to = function))) _LIBCPP_POP_ABI_PRAGMA_DIAGNOSTICS
-
-#define _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS _Pragma("clang attribute _LibcxxExplicitABIAnnotations.pop")
+#ifdef _LIBCPP_COMPILER_CLANG_BASED
+#  define _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS                                                                         \
+    _LIBCPP_PUSH_ABI_PRAGMA_DIAGNOSTICS                                                                                \
+    _Pragma(_LIBCPP_TOSTRING(clang attribute _LibcxxExplicitABIAnnotations.push(                                       \
+        __attribute__((__exclude_from_explicit_instantiation__,                                                        \
+                       __visibility__("hidden"),                                                                       \
+                       __abi_tag__(_LIBCPP_TOSTRING(_LIBCPP_ODR_SIGNATURE)))),                                         \
+        apply_to = function))) _LIBCPP_POP_ABI_PRAGMA_DIAGNOSTICS
+
+#  define _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS _Pragma("clang attribute _LibcxxExplicitABIAnnotations.pop")
+#else
+#  define _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+#  define _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+#endif
 
 // clang-format off
 


From libcxx-commits at lists.llvm.org  Wed Jul  1 07:01:59 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 07:01:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a451dd7.6e01836d.75c1d.3a5d@mx.google.com>


================
@@ -11,28 +11,50 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_reference.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 2300
+
+// A non-reference type can never bind to a temporary, so the result is always `false` for such a
+// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
+// eagerly instantiates the construction of `_Up` (including the element's constructor exception
+// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
+//
+// https://godbolt.org/z/4xz1ozKev
+//
+// TODO: Clang 23 fix this builtin, remove this guard once all supported clang versions include this fix.
+template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
+inline const bool __reference_constructs_from_temporary_v = false;
+
+template <class _Tp, class _Up>
+inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
+    __reference_constructs_from_temporary(_Tp, _Up);
----------------
Kim-J-Smith wrote:

How about this:

```cpp
template <class _Tp>
concept __complete_or_unbounded =
    requires { sizeof(_Tp); } && is_object_v<remove_reference_t<_Tp>>
    || is_void_v<_Tp> || is_unbounded_array_v<_Tp>;

template <class _Tp, class _Up, bool = !is_reference_v<_Tp>
                                       && __complete_or_unbounded<_Tp>
                                       && __complete_or_unbounded<_Up>>
inline const bool __reference_constructs_from_temporary_v = false;

template <class _Tp, class _Up>
inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, false> =
    __reference_constructs_from_temporary(_Tp, _Up);
```

See https://godbolt.org/z/Tnb7eT1jf

https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Wed Jul  1 07:20:55 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 07:20:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a452247.876e2a4f.17c5e7.d93d@mx.google.com>


================
@@ -34,4 +34,15 @@ static void BM_SeedSeq_Generate(benchmark::State& state) {
 }
 BENCHMARK(BM_SeedSeq_Generate)->Ranges({{1, MAX_SEED_LEN}, {1, MAX_BUFFER_LEN}});
 
+template <class Engine>
+static void BM_engine(benchmark::State& state) {
----------------
ldionne wrote:

Let's also add a benchmark where we call it a hundred times (known statically) to allow the compiler to vectorize further.

https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Wed Jul  1 07:20:55 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 07:20:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a452247.6ddb6d48.25823b.ff09@mx.google.com>


================
@@ -181,6 +181,9 @@ class mersenne_twister_engine {
     for (size_t __i = 1; __i < __n; ++__i)
----------------
ldionne wrote:

Not attached to this line: please run SPEC before/after.

https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Wed Jul  1 07:20:55 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 07:20:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a452247.c5c88a87.1f9f6a.dc55@mx.google.com>


================

----------------
ldionne wrote:

We probably need `XFAIL: FIXME-FROZEN-CXX03 && libcpp-abi-mersenne-twister-whatever`.

https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Wed Jul  1 07:20:55 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 07:20:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a452247.60da1a74.17bb2b.ecdd@mx.google.com>


================
@@ -203,17 +206,43 @@ class mersenne_twister_engine {
           return;
       __x_[0] = result_type(1) << (__w - 1);
     }
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    __update_all_states();
+#endif
+  }
+
+  void __update_state(size_t __i, size_t __k) {
+    const size_t __j         = (__i + 1) % __n;
+    const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
+    const result_type __yp   = (__x_[__i] & ~__mask) | (__x_[__j] & __mask);
+    __x_[__i]                = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
+  }
+
+  void __update_state(size_t __i) { __update_state(__i, (__i + __m) % __n); }
+
+  void __update_all_states() {
+    size_t __i = 0;
+    // This is split into two loops to help the compiler vectorize the code
+    for (; __i != (__n - __m); ++__i)
+      __update_state(__i);
+    for (size_t __j = 0; __i != __n; ++__i, ++__j)
+      __update_state(__i, __j);
   }
 
   // generating functions
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI result_type operator()() {
-    const size_t __j         = (__i_ + 1) % __n;
-    const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
-    const result_type __yp   = (__x_[__i_] & ~__mask) | (__x_[__j] & __mask);
-    const size_t __k         = (__i_ + __m) % __n;
-    __x_[__i_]               = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
-    result_type __z          = __x_[__i_] ^ (__rshift<__u>(__x_[__i_]) & __d);
-    __i_                     = __j;
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
----------------
ldionne wrote:

This is technically an ABI break, but it's very pedantic. We'd need a mersenne twister to be passed through an ABI boundary compiled with a different version of libc++, and the `operator()` would have to be called on both sides of the ABI boundary.

I think introducing as an ABI break is the right call, but we should discuss whether this is something we can take unconditionally (as a separate patch).

https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Wed Jul  1 07:22:39 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 07:22:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++] Optimize fstream::read"
	(PR #206453)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206453@github.com>
Message-ID: <6a4522af.1ded2248.1f8e85.c917@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/206453

From libcxx-commits at lists.llvm.org  Wed Jul  1 07:23:31 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 07:23:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++] Optimize fstream::read"
	(PR #206453)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206453@github.com>
Message-ID: <6a4522e3.67c1d1c3.1e52d3.7a7c@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/206453

From libcxx-commits at lists.llvm.org  Wed Jul  1 07:23:31 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 07:23:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++] Optimize fstream::read"
	(PR #206453)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206453@github.com>
Message-ID: <6a4522e3.cfe63a49.24297d.c413@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM w/ test move.

https://github.com/llvm/llvm-project/pull/206453

From libcxx-commits at lists.llvm.org  Wed Jul  1 07:23:31 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 07:23:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++] Optimize fstream::read"
	(PR #206453)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206453@github.com>
Message-ID: <6a4522e3.db2926be.331a35.c879@mx.google.com>


================
@@ -68,6 +68,25 @@ int main(int, char**) {
     test_buffer.resize(24);
     assert(std::string(test_buffer.data(), 24) == "to test buffer behaviour");
   }
+  { // Ensure that read fails gracefully with an unopened ifstream
+    // See https://llvm.org/PR168628
+    char buf[10];
+    std::ifstream ifs;
+
+    assert(!ifs.is_open());
+    assert(!ifs.bad());
+    assert(!ifs.fail());
+    assert(!ifs.eof());
+    assert(ifs.good());
+
+    ifs.read(buf, sizeof(buf));
----------------
ldionne wrote:

It feels like this should be tested in the test file for `ifstream::read` instead.

https://github.com/llvm/llvm-project/pull/206453

From libcxx-commits at lists.llvm.org  Wed Jul  1 07:29:24 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 07:29:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] bb6868a - [libc++] Fix behavior for
 `get_temporary_buffer` with non-positive size (#206871)
Message-ID: <6a452444.a53a8e96.2789ec.ce9c@mx.google.com>


Author: A. Jiang
Date: 2026-07-01T22:29:19+08:00
New Revision: bb6868abb8dd6f8ef7130ed2c81cf538793547c6

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

LOG: [libc++] Fix behavior for `get_temporary_buffer` with non-positive size (#206871)

Per C++17 [depr.temporary.buffer]/4, `get_temporary_buffer` is required
to return `{nullptr, 0}` when the size argument is zero or negative.
libc++ used to correctly handle this, but the refactoring in
94e7c0b051c79fd56205f115771980f2e7812306 got this wrong.

GCC generally warns on negative size due to `-Walloc-size-larger-than=`,
which is false positive due to incomplete control flow analysis. The
warning is coupled with optimizations, and this patch make tests
suppress it instead.

Added: 
    

Modified: 
    libcxx/include/__memory/temporary_buffer.h
    libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
    libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__memory/temporary_buffer.h b/libcxx/include/__memory/temporary_buffer.h
index d18717f52d1cd..3c572e43db641 100644
--- a/libcxx/include/__memory/temporary_buffer.h
+++ b/libcxx/include/__memory/temporary_buffer.h
@@ -26,6 +26,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Tp>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_CFI _LIBCPP_DEPRECATED_IN_CXX17 pair<_Tp*, ptr
diff _t>
 get_temporary_buffer(ptr
diff _t __n) _NOEXCEPT {
+  if (__n <= 0)
+    return pair<_Tp*, ptr
diff _t>();
+
   __unique_temporary_buffer<_Tp> __unique_buf = std::__allocate_unique_temporary_buffer<_Tp>(__n);
   pair<_Tp*, ptr
diff _t> __result(__unique_buf.get(), __unique_buf.get_deleter().__count_);
   __unique_buf.release();

diff  --git a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
index 3fa9355270866..c55e488af53b1 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/overaligned.pass.cpp
@@ -24,9 +24,12 @@
 #include <cassert>
 #include <cstddef>
 #include <cstdint>
+#include <limits>
 #include <memory>
 #include <utility>
 
+#include "test_macros.h"
+
 struct alignas(32) A {
     int field;
 };
@@ -38,5 +41,23 @@ int main(int, char**)
     assert(reinterpret_cast<std::uintptr_t>(ip.first) % alignof(A) == 0);
     std::return_temporary_buffer(ip.first);
 
+    // C++17 [depr.temporary.buffer]/4
+    // Returns: If n <= 0 or if no storage could be obtained,
+    // returns a pair P such that P.first is a null pointer value and P.second == 0;
+    {
+      std::pair<A*, std::ptr
diff _t> ret = std::get_temporary_buffer<A>(0);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+    {
+      TEST_DIAGNOSTIC_PUSH
+      // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Walloc-size-larger-than=")
+      std::pair<A*, std::ptr
diff _t> ret = std::get_temporary_buffer<A>(std::numeric_limits<std::ptr
diff _t>::min());
+      TEST_DIAGNOSTIC_POP
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
index 39a4767d874e3..9bd49edbb0d02 100644
--- a/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
+++ b/libcxx/test/std/utilities/memory/temporary.buffer/temporary_buffer.pass.cpp
@@ -21,9 +21,12 @@
 
 #include <cassert>
 #include <cstddef>
+#include <limits>
 #include <memory>
 #include <utility>
 
+#include "test_macros.h"
+
 int main(int, char**)
 {
     std::pair<int*, std::ptr
diff _t> ip = std::get_temporary_buffer<int>(5);
@@ -31,5 +34,32 @@ int main(int, char**)
     assert(ip.second == 5);
     std::return_temporary_buffer(ip.first);
 
+    // C++17 [depr.temporary.buffer]/4
+    // Returns: If n <= 0 or if no storage could be obtained,
+    // returns a pair P such that P.first is a null pointer value and P.second == 0;
+    {
+      std::pair<int*, std::ptr
diff _t> ret = std::get_temporary_buffer<int>(0);
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+    {
+      TEST_DIAGNOSTIC_PUSH
+      // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Walloc-size-larger-than=")
+      std::pair<int*, std::ptr
diff _t> ret = std::get_temporary_buffer<int>(-5);
+      TEST_DIAGNOSTIC_POP
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+    {
+      TEST_DIAGNOSTIC_PUSH
+      // This warning is coupled with completeness of control flow analysis which is affected by optimizations.
+      TEST_GCC_DIAGNOSTIC_IGNORED("-Walloc-size-larger-than=")
+      std::pair<int*, std::ptr
diff _t> ret = std::get_temporary_buffer<int>(std::numeric_limits<std::ptr
diff _t>::min());
+      TEST_DIAGNOSTIC_POP
+      assert(ret.first == NULL);
+      assert(ret.second == 0);
+    }
+
   return 0;
 }


        

From libcxx-commits at lists.llvm.org  Wed Jul  1 07:29:29 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 01 Jul 2026 07:29:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix behavior for
 `get_temporary_buffer` with non-positive size (PR #206871)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206871@github.com>
Message-ID: <6a452449.8ff50da0.252024.2c9c@mx.google.com>


https://github.com/frederick-vs-ja closed https://github.com/llvm/llvm-project/pull/206871

From libcxx-commits at lists.llvm.org  Wed Jul  1 08:09:58 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 08:09:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 5f282ce - [libc++] Assume that <wchar.h>
 in llvm-libc has const-overloads. (#206805)
Message-ID: <6a452dc6.560f397e.2caf60.994c@mx.google.com>


Author: Alexey Samsonov
Date: 2026-07-01T08:09:53-07:00
New Revision: 5f282ce09f961d33414bb6c42a57b907efcb6aec

URL: https://github.com/llvm/llvm-project/commit/5f282ce09f961d33414bb6c42a57b907efcb6aec
DIFF: https://github.com/llvm/llvm-project/commit/5f282ce09f961d33414bb6c42a57b907efcb6aec.diff

LOG: [libc++] Assume that <wchar.h> in llvm-libc has const-overloads. (#206805)

See discussion in https://github.com/llvm/llvm-project/pull/201236 -
we're close to enabling wide characters in libc++ when it's built on top
of llvm-libc.

llvm-libc headers use `const wchar_t*` return type for selected wchar
functions, so we need to configure libc++ to assume that const-overloads
for these functions are available in this case, and the implementations
in libc++ wrapper around `<wchar.h>` are not needed.

Added: 
    

Modified: 
    libcxx/include/wchar.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index a932dd266b862..7ff03870bdfdb 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -133,6 +133,8 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 #      if defined(_CRT_CONST_CORRECT_OVERLOADS)
 #        define _LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS 1
 #      endif
+#    elif _LIBCPP_LIBC_LLVM_LIBC
+#      define _LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS 1
 #    endif
 
 #    if _LIBCPP_HAS_WIDE_CHARACTERS


        

From libcxx-commits at lists.llvm.org  Wed Jul  1 08:10:01 2026
From: libcxx-commits at lists.llvm.org (Alexey Samsonov via libcxx-commits)
Date: Wed, 01 Jul 2026 08:10:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Assume that <wchar.h> in
 llvm-libc has const-overloads. (PR #206805)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206805@github.com>
Message-ID: <6a452dc9.400acb00.363995.3cd4@mx.google.com>


https://github.com/vonosmas closed https://github.com/llvm/llvm-project/pull/206805

From libcxx-commits at lists.llvm.org  Wed Jul  1 08:10:38 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 08:10:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't add #pragma clang
	attribute with GCC (PR #206989)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206989@github.com>
Message-ID: <6a452dee.8a854648.2c44ff.9d07@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

GCC doesn't support `#pragma clang attribute`, which causes a bunch of diagnostics. Since we have it behind a macro anyways we can simply define the macro as empty with GCC.


---
Full diff: https://github.com/llvm/llvm-project/pull/206989.diff


1 Files Affected:

- (modified) libcxx/include/__configuration/namespace.h (+14-9) 


``````````diff
diff --git a/libcxx/include/__configuration/namespace.h b/libcxx/include/__configuration/namespace.h
index 6b733e3076581..1f4b8ce247b13 100644
--- a/libcxx/include/__configuration/namespace.h
+++ b/libcxx/include/__configuration/namespace.h
@@ -46,15 +46,20 @@
 #  define _LIBCPP_POP_ABI_PRAGMA_DIAGNOSTICS
 #endif
 
-#define _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS                                                                           \
-  _LIBCPP_PUSH_ABI_PRAGMA_DIAGNOSTICS                                                                                  \
-  _Pragma(_LIBCPP_TOSTRING(clang attribute _LibcxxExplicitABIAnnotations.push(                                         \
-      __attribute__((__exclude_from_explicit_instantiation__,                                                          \
-                     __visibility__("hidden"),                                                                         \
-                     __abi_tag__(_LIBCPP_TOSTRING(_LIBCPP_ODR_SIGNATURE)))),                                           \
-      apply_to = function))) _LIBCPP_POP_ABI_PRAGMA_DIAGNOSTICS
-
-#define _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS _Pragma("clang attribute _LibcxxExplicitABIAnnotations.pop")
+#ifdef _LIBCPP_COMPILER_CLANG_BASED
+#  define _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS                                                                         \
+    _LIBCPP_PUSH_ABI_PRAGMA_DIAGNOSTICS                                                                                \
+    _Pragma(_LIBCPP_TOSTRING(clang attribute _LibcxxExplicitABIAnnotations.push(                                       \
+        __attribute__((__exclude_from_explicit_instantiation__,                                                        \
+                       __visibility__("hidden"),                                                                       \
+                       __abi_tag__(_LIBCPP_TOSTRING(_LIBCPP_ODR_SIGNATURE)))),                                         \
+        apply_to = function))) _LIBCPP_POP_ABI_PRAGMA_DIAGNOSTICS
+
+#  define _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS _Pragma("clang attribute _LibcxxExplicitABIAnnotations.pop")
+#else
+#  define _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+#  define _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+#endif
 
 // clang-format off
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/206989

From libcxx-commits at lists.llvm.org  Wed Jul  1 08:16:33 2026
From: libcxx-commits at lists.llvm.org (Alexey Samsonov via libcxx-commits)
Date: Wed, 01 Jul 2026 08:16:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't add #pragma clang
	attribute with GCC (PR #206989)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206989@github.com>
Message-ID: <6a452f51.f732ad81.1d6a4d.6ff4@mx.google.com>


vonosmas wrote:

How will implicit ABI annotations work in non-Clang-based compilers? Do you plan to use some other machinery to add hidden visibility, ODR ABI tags, etc?

https://github.com/llvm/llvm-project/pull/206989

From libcxx-commits at lists.llvm.org  Wed Jul  1 08:45:59 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 08:45:59 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_=5Blibcxxabi=5D=5BFree?=
 =?utf-8?q?BSD=5D_Re-enable_=60thread=5Flocal=5Fdestruction=5Forder=2Epass?=
 =?utf-8?b?LmNwcOKAjmAuIChQUiAjMTg2MTI5KQ==?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186129@github.com>
Message-ID: <6a453637.da8bafd3.13e3eb.0277@mx.google.com>


https://github.com/aokblast updated https://github.com/llvm/llvm-project/pull/186129

>From a9c068680c961fcde50bc3fcb69ba5679e28a901 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Thu, 12 Mar 2026 22:42:03 +0800
Subject: [PATCH] [libcxxabi][FreeBSD] Re-enable failed test.

---
 libcxxabi/test/thread_local_destruction_order.pass.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxxabi/test/thread_local_destruction_order.pass.cpp b/libcxxabi/test/thread_local_destruction_order.pass.cpp
index c7e7717d6f04f..775b417832a1f 100644
--- a/libcxxabi/test/thread_local_destruction_order.pass.cpp
+++ b/libcxxabi/test/thread_local_destruction_order.pass.cpp
@@ -9,8 +9,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-threads
 
-// XFAIL: LIBCXX-FREEBSD-FIXME
-
 // TODO: This test does start working with newer updates of the mingw-w64
 // toolchain, when it includes the following commit:
 // https://github.com/mingw-w64/mingw-w64/commit/71eddccd746c56d9cde28bb5620d027d49259de9


From libcxx-commits at lists.llvm.org  Wed Jul  1 09:47:56 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 09:47:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::find_first_of (PR #206328)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206328@github.com>
Message-ID: <6a4544bc.cb30c3e4.37526.fef2@mx.google.com>


================
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator1 find_first_of(ExecutionPolicy&& exec,
+//                                  ForwardIterator1 first1,
+//                                  ForwardIterator1 last1,
+//                                  ForwardIterator2 first2,
+//                                  ForwardIterator2 last2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]     = {0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a)) == Iter(a));
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a)) == Iter(a + sa));
+    }
+    {
+      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
+      unsigned sa = sizeof(ia) / sizeof(ia[0]);
+      int ib[]    = {1, 3, 5, 7};
+      unsigned sb = sizeof(ib) / sizeof(ib[0]);
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
+      int ic[] = {7};
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
+    }
+    {
+      int a[8192];
----------------
ldionne wrote:

Yeah, I looked at the existing tests for `find_first_of` and they're not great, really. Adding just this should be possible without messing with C++03 though?

```c++
// in  main()
{
      using Iter = forward_iterator<const int*>;
      int a[8192];
      std::iota(begin(a), end(a), 1);
      a[1023] = -1;
      a[2048] = -1;
      a[3071] = -1;
      int b[] = {-1, 999999};
      assert(std::find_first_of(policy, Iter(begin(a)), Iter(end(a)), Iter(begin(b)), Iter(end(b))) ==
                  Iter(begin(a) + 1023));
}
```

That's not addressing the whole gap in coverage, but it captures the added "runtime" coverage here and makes it likely that we'd pick this up when refactoring that test. WDYT?

https://github.com/llvm/llvm-project/pull/206328

From libcxx-commits at lists.llvm.org  Wed Jul  1 09:47:56 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 09:47:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::find_first_of (PR #206328)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206328@github.com>
Message-ID: <6a4544bc.27b3b003.9dfd8.5df0@mx.google.com>


================
@@ -0,0 +1,113 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator1 find_first_of(ExecutionPolicy&& exec,
+//                                  ForwardIterator1 first1,
+//                                  ForwardIterator1 last1,
+//                                  ForwardIterator2 first2,
+//                                  ForwardIterator2 last2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(find_first_of);
+
+static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable&& callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    using std::begin;
----------------
ldionne wrote:

We don't use `using std::foo;` anywhere else in the test suite. I'd prefer fully qualifying for consistency.

https://github.com/llvm/llvm-project/pull/206328

From libcxx-commits at lists.llvm.org  Wed Jul  1 09:48:14 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 09:48:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::find_first_of (PR #206328)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206328@github.com>
Message-ID: <6a4544ce.c56fb102.1c15b6.6ad8@mx.google.com>


================
@@ -0,0 +1,113 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator1 find_first_of(ExecutionPolicy&& exec,
+//                                  ForwardIterator1 first1,
+//                                  ForwardIterator1 last1,
+//                                  ForwardIterator2 first2,
+//                                  ForwardIterator2 last2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(find_first_of);
+
+static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable&& callable) {
----------------
ldionne wrote:

```suggestion
void runway_sample(size_t size, Callable callable) {
```

Unless there's a reason we might want to forward the callable (or avoid making a copy), I'd keep the most naive/simplest way to pass this. Otherwise, one wonders if there's a deeper reason for taking by ref, when in reality there's none (AFAICT).

https://github.com/llvm/llvm-project/pull/206328

From libcxx-commits at lists.llvm.org  Wed Jul  1 09:52:56 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 09:52:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4545e8.2020f6c2.2129d2.ff3c@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/206498

>From 6fc88c1fba7be7c7eb5dcb0dddd2ec12e4a75ec8 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 18:53:17 +0530
Subject: [PATCH 1/3] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. Omit the noreplace mode strings in
 __make_mdstring() on AIX, causing open() to return nullptr for these modes.
 Update the test to expect failure on AIX for all noreplace cases, since the
 platform cannot provide atomic exclusive-open semantics.

---
 libcxx/include/fstream                        | 10 ++++++++
 .../filebuf.members/open_pointer.pass.cpp     | 24 +++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b5fb65820628a..2ec5374ce463e 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -634,6 +634,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -644,6 +653,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..f7ab0239a1b18 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -75,7 +73,10 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -83,7 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 
@@ -94,7 +101,10 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -102,7 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 #  endif

>From 0715d9900e3781e298cca572f5955eb259d297ac Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 12:40:23 -0400
Subject: [PATCH 2/3] fix format and add warning

---
 libcxx/include/fstream                               |  3 +++
 .../fstreams/filebuf.members/open_pointer.pass.cpp   | 12 ++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 2ec5374ce463e..a5f1e80960054 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,6 +642,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if defined(_AIX) || defined(__TOS_AIX__)
+#        warning 'exclusive mode is unsupported on this target'
+#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index f7ab0239a1b18..627a65cac3f2f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -84,13 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#  else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#  endif
           }
         }
 
@@ -112,13 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#    else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#    endif
           }
         }
 #  endif

>From 072a5b2b327fe402617bd7b5ccf081931e685289 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 12:52:06 -0400
Subject: [PATCH 3/3] fix warnings treated as errors

---
 libcxx/include/fstream | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index a5f1e80960054..54f1555c15cbe 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -643,7 +643,10 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
 #      if defined(_AIX) || defined(__TOS_AIX__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic warning "-Wcpp"
 #        warning 'exclusive mode is unsupported on this target'
+#        pragma GCC diagnostic pop
 #      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:


From libcxx-commits at lists.llvm.org  Wed Jul  1 09:53:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 09:53:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a454606.13be6df8.2dd5b3.79e4@mx.google.com>


================
@@ -11,28 +11,50 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_reference.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 2300
+
+// A non-reference type can never bind to a temporary, so the result is always `false` for such a
+// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
+// eagerly instantiates the construction of `_Up` (including the element's constructor exception
+// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
+//
+// https://godbolt.org/z/4xz1ozKev
+//
+// TODO: Clang 23 fix this builtin, remove this guard once all supported clang versions include this fix.
+template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
+inline const bool __reference_constructs_from_temporary_v = false;
+
+template <class _Tp, class _Up>
+inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
+    __reference_constructs_from_temporary(_Tp, _Up);
----------------
Kim-J-Smith wrote:

A correction is hereby made:

```cpp
template <class _Tp, class _Up = remove_reference_t<_Tp>>
concept __complete = is_function_v<_Up> || requires { sizeof(_Tp); } && is_object_v<_Up>;

template <class _Tp>
concept __complete_or_unbounded = __complete<_Tp> || is_void_v<_Tp> || is_unbounded_array_v<_Tp>;
```

https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Wed Jul  1 09:54:08 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 09:54:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add MSVC's implementation of
 `exception_ptr` for Windows (PR #94977)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/94977@github.com>
Message-ID: <6a454630.ac469dc7.22d7c7.4118@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/94977

From libcxx-commits at lists.llvm.org  Wed Jul  1 09:54:08 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 09:54:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add MSVC's implementation of
 `exception_ptr` for Windows (PR #94977)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/94977@github.com>
Message-ID: <6a454630.f06f4994.2ae0bf.51b5@mx.google.com>


================
@@ -62,8 +62,6 @@ _LIBCPP_OVERRIDABLE_FUNC_VIS __cxa_exception* __cxa_init_primary_exception(
 _LIBCPP_BEGIN_UNVERSIONED_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#ifndef _LIBCPP_ABI_MICROSOFT
----------------
ldionne wrote:

I really like that this patch gives us a single implementation of the public class.

https://github.com/llvm/llvm-project/pull/94977

From libcxx-commits at lists.llvm.org  Wed Jul  1 09:54:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 09:54:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add MSVC's implementation of
 `exception_ptr` for Windows (PR #94977)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/94977@github.com>
Message-ID: <6a454631.1a4df1a5.3108a4.2d80@mx.google.com>


https://github.com/ldionne approved this pull request.

Minor comments, but LGTM. I didn't review the implementation (`exception_pointer_msvc.ipp`) in detail since that part is coming from MSVC itself.

https://github.com/llvm/llvm-project/pull/94977

From libcxx-commits at lists.llvm.org  Wed Jul  1 09:54:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 09:54:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add MSVC's implementation of
 `exception_ptr` for Windows (PR #94977)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/94977@github.com>
Message-ID: <6a454631.ac469dc7.22d7c7.4165@mx.google.com>


================
@@ -180,34 +182,7 @@ _LIBCPP_HIDE_FROM_ABI exception_ptr make_exception_ptr(_Ep) _NOEXCEPT {
 
 #else // defined(_LIBCPP_ABI_MICROSOFT)
 
-class _LIBCPP_EXPORTED_FROM_ABI exception_ptr {
----------------
ldionne wrote:

We should release-note this change, since IIUC it's ABI-breaking on Windows (since `exception_ptr` changed layout, right?).

https://github.com/llvm/llvm-project/pull/94977

From libcxx-commits at lists.llvm.org  Wed Jul  1 10:06:11 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 10:06:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::find_first_of (PR #206328)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206328@github.com>
Message-ID: <6a454903.576b3af0.3bb11f.b8a4@mx.google.com>


================
@@ -181,6 +183,25 @@ struct __is_partitioned<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    using _FindIf = __dispatch<__find_if, __current_configuration, _ExecutionPolicy>;
+    using _Ref1   = __iterator_reference<_ForwardIterator1>;
+    using _Ref2   = __iterator_reference<_ForwardIterator2>;
+    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
+      return std::any_of(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); });
+    });
----------------
ldionne wrote:

I think I just clarified what I was trying to say yesterday when I opened #206784 . What would be nice is for this code:

```c++
return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
  return std::any_of(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); });
});
```

to basically desugar to

```c++
return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
  return std::find(__first2, __last2, __element);
});
```

when the predicate is `equal_to`. Then we'd allow for vectorization. I see two ways of achieving this:

1. Here, branch off on `desugars_to<__equal_tag` and call `std::find` directly, or
2. Write this as `std::any_of(__first2, __last2, __partial(__equal_to{}, __element))`. Since `any_of` uses `find_if` under the hood, if `find_if` gets `__partial(OP, ...)` where `OP` desugars to `equal_to`, we can actually call `std::find` under the hood.

(2) is more complicated but more general. (1) is more pragmatic and directly applicable in this patch.

Thoughts? CC @philnik777 

https://github.com/llvm/llvm-project/pull/206328

From libcxx-commits at lists.llvm.org  Wed Jul  1 10:06:56 2026
From: libcxx-commits at lists.llvm.org (Scott Linder via libcxx-commits)
Date: Wed, 01 Jul 2026 10:06:56 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libc]
 [libcxx] [libunwind] [lld] [lldb] [llvm] [openmp] [polly] [3/4]: [docs]
 Create utils/docs (PR #203962)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/203962@github.com>
Message-ID: <6a454930.ac469dc7.22d7c7.97c3@mx.google.com>


slinder1 wrote:

> FWIW, I've started seeing this failure locally when trying to build documentation on Windows:
> 
> ```
> F:\source\llvm-project\clang\docs>make html
> Running Sphinx v7.2.6
> 
> Configuration error:
> There is a programmable error in your configuration file:
> 
> Traceback (most recent call last):
>   File "C:\Users\aballman\AppData\Roaming\Python\Python310\site-packages\sphinx\config.py", line 358, in eval_config_file
>     exec(code, namespace)  # NoQA: S102
>   File "F:\source\llvm-project\clang\docs\conf.py", line 18, in <module>
>     from llvm_sphinx import *  # see llvm-project/utils/docs/README.md
> ModuleNotFoundError: No module named 'llvm_sphinx'
> ```
> 
> am I holding something wrong?

Do you have any more info on your setup that might help me reproduce the issue?

On windows using the default generator for me (`MSBuild`) I don't see any issues:

```
cmake -B build -S llvm -DLLVM_ENABLE_SPHINX=ON
cmake --build build --parallel 8 -t docs-llvm-html
  1>Generating html Sphinx documentation for llvm into "C:/Users/slinder1/llvm-project/build/docs/html"
```

The build succeeds without any other messages, and the HTML generated seems fine.

It looks like you're using `make`, so maybe I'm not testing with the right setup?

https://github.com/llvm/llvm-project/pull/203962

From libcxx-commits at lists.llvm.org  Wed Jul  1 10:50:27 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 10:50:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a455363.ff037943.3bf0e2.7f65@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/206498

>From 6fc88c1fba7be7c7eb5dcb0dddd2ec12e4a75ec8 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 18:53:17 +0530
Subject: [PATCH 1/4] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. Omit the noreplace mode strings in
 __make_mdstring() on AIX, causing open() to return nullptr for these modes.
 Update the test to expect failure on AIX for all noreplace cases, since the
 platform cannot provide atomic exclusive-open semantics.

---
 libcxx/include/fstream                        | 10 ++++++++
 .../filebuf.members/open_pointer.pass.cpp     | 24 +++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b5fb65820628a..2ec5374ce463e 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -634,6 +634,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -644,6 +653,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..f7ab0239a1b18 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -75,7 +73,10 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -83,7 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 
@@ -94,7 +101,10 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -102,7 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 #  endif

>From 0715d9900e3781e298cca572f5955eb259d297ac Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 12:40:23 -0400
Subject: [PATCH 2/4] fix format and add warning

---
 libcxx/include/fstream                               |  3 +++
 .../fstreams/filebuf.members/open_pointer.pass.cpp   | 12 ++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 2ec5374ce463e..a5f1e80960054 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,6 +642,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if defined(_AIX) || defined(__TOS_AIX__)
+#        warning 'exclusive mode is unsupported on this target'
+#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index f7ab0239a1b18..627a65cac3f2f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -84,13 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#  else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#  endif
           }
         }
 
@@ -112,13 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#    else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#    endif
           }
         }
 #  endif

>From 072a5b2b327fe402617bd7b5ccf081931e685289 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 12:52:06 -0400
Subject: [PATCH 3/4] fix warnings treated as errors

---
 libcxx/include/fstream | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index a5f1e80960054..54f1555c15cbe 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -643,7 +643,10 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
 #      if defined(_AIX) || defined(__TOS_AIX__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic warning "-Wcpp"
 #        warning 'exclusive mode is unsupported on this target'
+#        pragma GCC diagnostic pop
 #      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:

>From 9c99f50050c182905f10cd7a5022b32600d0e7b2 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 13:49:31 -0400
Subject: [PATCH 4/4] add warning as fprintf

---
 libcxx/include/fstream | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 54f1555c15cbe..7c7f7c6b0db52 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,12 +642,6 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if defined(_AIX) || defined(__TOS_AIX__)
-#        pragma GCC diagnostic push
-#        pragma GCC diagnostic warning "-Wcpp"
-#        warning 'exclusive mode is unsupported on this target'
-#        pragma GCC diagnostic pop
-#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -750,9 +744,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr)
+  if (!__mdstr){
+#     if defined(_AIX) || defined(__TOS_AIX__)
+    if (__mode & ios_base::noreplace)
+      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
+#     endif
     return nullptr;
-
+}
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif


From libcxx-commits at lists.llvm.org  Wed Jul  1 11:49:24 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 11:49:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 32f14d7 - [libc++] Move threading and
 random device config into <__configuration/platform.h> (#206262)
Message-ID: <6a456134.f06f4994.1e3a60.59b5@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-01T20:49:20+02:00
New Revision: 32f14d7cd9991c0431e0c2721313614a4b05fd2d

URL: https://github.com/llvm/llvm-project/commit/32f14d7cd9991c0431e0c2721313614a4b05fd2d
DIFF: https://github.com/llvm/llvm-project/commit/32f14d7cd9991c0431e0c2721313614a4b05fd2d.diff

LOG: [libc++] Move threading and random device config into <__configuration/platform.h> (#206262)

These are platform-specific configuration options, so they should live
`<__configuration/platform.h>`.

Added: 
    

Modified: 
    libcxx/include/__config
    libcxx/include/__configuration/platform.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..edafe59278821 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -62,50 +62,6 @@
 #    define _LIBCPP_SHORT_WCHAR 1
 #  endif
 
-// Libc++ supports various implementations of std::random_device.
-//
-// _LIBCPP_USING_DEV_RANDOM
-//      Read entropy from the given file, by default `/dev/urandom`.
-//      If a token is provided, it is assumed to be the path to a file
-//      to read entropy from. This is the default behavior if nothing
-//      else is specified. This implementation requires storing state
-//      inside `std::random_device`.
-//
-// _LIBCPP_USING_ARC4_RANDOM
-//      Use arc4random(). This allows obtaining random data even when
-//      using sandboxing mechanisms. On some platforms like Apple, this
-//      is the recommended source of entropy for user-space programs.
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-//
-// _LIBCPP_USING_GETENTROPY
-//      Use getentropy().
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-//
-// _LIBCPP_USING_FUCHSIA_CPRNG
-//      Use Fuchsia's zx_cprng_draw() system call, which is specified to
-//      deliver high-quality entropy and cannot fail.
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-//
-// _LIBCPP_USING_WIN32_RANDOM
-//      Use rand_s(), for use on Windows.
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-#  if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) ||                     \
-      defined(__DragonFly__)
-#    define _LIBCPP_USING_ARC4_RANDOM
-#  elif defined(__wasi__) || defined(__EMSCRIPTEN__)
-#    define _LIBCPP_USING_GETENTROPY
-#  elif defined(__Fuchsia__)
-#    define _LIBCPP_USING_FUCHSIA_CPRNG
-#  elif defined(_LIBCPP_WIN32API)
-#    define _LIBCPP_USING_WIN32_RANDOM
-#  else
-#    define _LIBCPP_USING_DEV_RANDOM
-#  endif
-
 #  ifndef _LIBCPP_CXX03_LANG
 
 #    define _LIBCPP_ALIGNOF(...) alignof(__VA_ARGS__)
@@ -201,107 +157,6 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_DIAGNOSE_DEPRECATED_HEADERS 0
 #  endif
 
-// Thread API
-// clang-format off
-#  if _LIBCPP_HAS_THREADS &&                                                                                           \
-      !_LIBCPP_HAS_THREAD_API_PTHREAD &&                                                                               \
-      !_LIBCPP_HAS_THREAD_API_WIN32 &&                                                                                 \
-      !_LIBCPP_HAS_THREAD_API_EXTERNAL &&                                                                              \
-      !_LIBCPP_HAS_THREAD_API_C11
-
-#    if defined(__FreeBSD__) ||                                                                                        \
-        defined(__wasi__) ||                                                                                           \
-        defined(__NetBSD__) ||                                                                                         \
-        defined(__OpenBSD__) ||                                                                                        \
-        defined(__NuttX__) ||                                                                                          \
-        defined(__linux__) ||                                                                                          \
-        defined(__GNU__) ||                                                                                            \
-        defined(__APPLE__) ||                                                                                          \
-        defined(__MVS__) ||                                                                                            \
-        defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
-// clang-format on
-#      undef _LIBCPP_HAS_THREAD_API_PTHREAD
-#      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#    elif defined(__Fuchsia__)
-// TODO(44575): Switch to C11 thread API when possible.
-#      undef _LIBCPP_HAS_THREAD_API_PTHREAD
-#      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#    elif defined(_LIBCPP_WIN32API)
-#      undef _LIBCPP_HAS_THREAD_API_WIN32
-#      define _LIBCPP_HAS_THREAD_API_WIN32 1
-#    else
-#      error "No thread API"
-#    endif // _LIBCPP_HAS_THREAD_API
-#  endif   // _LIBCPP_HAS_THREADS
-
-#  if !_LIBCPP_HAS_THREAD_API_PTHREAD
-#    define _LIBCPP_HAS_COND_CLOCKWAIT 0
-#  elif (defined(__ANDROID__) && __ANDROID_API__ >= 30) || _LIBCPP_GLIBC_PREREQ(2, 30)
-#    define _LIBCPP_HAS_COND_CLOCKWAIT 1
-#  else
-#    define _LIBCPP_HAS_COND_CLOCKWAIT 0
-#  endif
-
-#  if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_PTHREAD
-#    error _LIBCPP_HAS_THREAD_API_PTHREAD may only be true when _LIBCPP_HAS_THREADS is true.
-#  endif
-
-#  if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_EXTERNAL
-#    error _LIBCPP_HAS_THREAD_API_EXTERNAL may only be true when _LIBCPP_HAS_THREADS is true.
-#  endif
-
-#  if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_C11
-#    error _LIBCPP_HAS_THREAD_API_C11 may only be true when _LIBCPP_HAS_THREADS is true.
-#  endif
-
-#  if !_LIBCPP_HAS_MONOTONIC_CLOCK && _LIBCPP_HAS_THREADS
-#    error _LIBCPP_HAS_MONOTONIC_CLOCK may only be false when _LIBCPP_HAS_THREADS is false.
-#  endif
-
-#  if _LIBCPP_HAS_THREADS && !defined(__STDCPP_THREADS__)
-#    define __STDCPP_THREADS__ 1
-#  endif
-
-// The glibc and Bionic implementation of pthreads implements
-// pthread_mutex_destroy as nop for regular mutexes. Additionally, Win32
-// mutexes have no destroy mechanism.
-//
-// This optimization can't be performed on Apple platforms, where
-// pthread_mutex_destroy can allow the kernel to release resources.
-// See https://llvm.org/D64298 for details.
-//
-// TODO(EricWF): Enable this optimization on Bionic after speaking to their
-//               respective stakeholders.
-// clang-format off
-#  if (_LIBCPP_HAS_THREAD_API_PTHREAD && defined(__GLIBC__)) ||                                                        \
-      (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) ||                                                          \
-       _LIBCPP_HAS_THREAD_API_WIN32
-// clang-format on
-#    define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 1
-#  else
-#    define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 0
-#  endif
-
-// Destroying a condvar is a nop on Windows.
-//
-// This optimization can't be performed on Apple platforms, where
-// pthread_cond_destroy can allow the kernel to release resources.
-// See https://llvm.org/D64298 for details.
-//
-// TODO(EricWF): This is potentially true for some pthread implementations
-// as well.
-#  if (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) || _LIBCPP_HAS_THREAD_API_WIN32
-#    define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 1
-#  else
-#    define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 0
-#  endif
-
-#  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
-#    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
-#  endif
-
 // We often repeat things just for handling wide characters in the library.
 // When wide characters are disabled, it can be useful to have a quick way of
 // disabling it without having to resort to #if-#endif, which has a larger

diff  --git a/libcxx/include/__configuration/platform.h b/libcxx/include/__configuration/platform.h
index ebd638e663215..5b3bcfa4a8989 100644
--- a/libcxx/include/__configuration/platform.h
+++ b/libcxx/include/__configuration/platform.h
@@ -57,4 +57,148 @@
 #  define _LIBCPP_BIG_ENDIAN
 #endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
 
+// Libc++ supports various implementations of std::random_device.
+//
+// _LIBCPP_USING_DEV_RANDOM
+//      Read entropy from the given file, by default `/dev/urandom`.
+//      If a token is provided, it is assumed to be the path to a file
+//      to read entropy from. This is the default behavior if nothing
+//      else is specified. This implementation requires storing state
+//      inside `std::random_device`.
+//
+// _LIBCPP_USING_ARC4_RANDOM
+//      Use arc4random(). This allows obtaining random data even when
+//      using sandboxing mechanisms. On some platforms like Apple, this
+//      is the recommended source of entropy for user-space programs.
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+//
+// _LIBCPP_USING_GETENTROPY
+//      Use getentropy().
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+//
+// _LIBCPP_USING_FUCHSIA_CPRNG
+//      Use Fuchsia's zx_cprng_draw() system call, which is specified to
+//      deliver high-quality entropy and cannot fail.
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+//
+// _LIBCPP_USING_WIN32_RANDOM
+//      Use rand_s(), for use on Windows.
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
+#  define _LIBCPP_USING_ARC4_RANDOM
+#elif defined(__wasi__) || defined(__EMSCRIPTEN__)
+#  define _LIBCPP_USING_GETENTROPY
+#elif defined(__Fuchsia__)
+#  define _LIBCPP_USING_FUCHSIA_CPRNG
+#elif defined(_LIBCPP_WIN32API)
+#  define _LIBCPP_USING_WIN32_RANDOM
+#else
+#  define _LIBCPP_USING_DEV_RANDOM
+#endif
+
+// Thread API
+// clang-format off
+#if _LIBCPP_HAS_THREADS &&                                                                                             \
+    !_LIBCPP_HAS_THREAD_API_PTHREAD &&                                                                                 \
+    !_LIBCPP_HAS_THREAD_API_WIN32 &&                                                                                   \
+    !_LIBCPP_HAS_THREAD_API_EXTERNAL &&                                                                                \
+    !_LIBCPP_HAS_THREAD_API_C11
+
+#  if defined(__FreeBSD__) ||                                                                                          \
+      defined(__wasi__) ||                                                                                             \
+      defined(__NetBSD__) ||                                                                                           \
+      defined(__OpenBSD__) ||                                                                                          \
+      defined(__NuttX__) ||                                                                                            \
+      defined(__linux__) ||                                                                                            \
+      defined(__GNU__) ||                                                                                              \
+      defined(__APPLE__) ||                                                                                            \
+      defined(__MVS__) ||                                                                                              \
+      defined(_AIX) ||                                                                                                 \
+      defined(__EMSCRIPTEN__)
+// clang-format on
+#    undef _LIBCPP_HAS_THREAD_API_PTHREAD
+#    define _LIBCPP_HAS_THREAD_API_PTHREAD 1
+#  elif defined(__Fuchsia__)
+// TODO(44575): Switch to C11 thread API when possible.
+#    undef _LIBCPP_HAS_THREAD_API_PTHREAD
+#    define _LIBCPP_HAS_THREAD_API_PTHREAD 1
+#  elif defined(_LIBCPP_WIN32API)
+#    undef _LIBCPP_HAS_THREAD_API_WIN32
+#    define _LIBCPP_HAS_THREAD_API_WIN32 1
+#  else
+#    error "No thread API"
+#  endif // _LIBCPP_HAS_THREAD_API
+#endif   // _LIBCPP_HAS_THREADS
+
+#if !_LIBCPP_HAS_THREAD_API_PTHREAD
+#  define _LIBCPP_HAS_COND_CLOCKWAIT 0
+#elif (defined(__ANDROID__) && __ANDROID_API__ >= 30) || _LIBCPP_GLIBC_PREREQ(2, 30)
+#  define _LIBCPP_HAS_COND_CLOCKWAIT 1
+#else
+#  define _LIBCPP_HAS_COND_CLOCKWAIT 0
+#endif
+
+#if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_PTHREAD
+#  error _LIBCPP_HAS_THREAD_API_PTHREAD may only be true when _LIBCPP_HAS_THREADS is true.
+#endif
+
+#if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_EXTERNAL
+#  error _LIBCPP_HAS_THREAD_API_EXTERNAL may only be true when _LIBCPP_HAS_THREADS is true.
+#endif
+
+#if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_C11
+#  error _LIBCPP_HAS_THREAD_API_C11 may only be true when _LIBCPP_HAS_THREADS is true.
+#endif
+
+#if !_LIBCPP_HAS_MONOTONIC_CLOCK && _LIBCPP_HAS_THREADS
+#  error _LIBCPP_HAS_MONOTONIC_CLOCK may only be false when _LIBCPP_HAS_THREADS is false.
+#endif
+
+#if _LIBCPP_HAS_THREADS && !defined(__STDCPP_THREADS__)
+#  define __STDCPP_THREADS__ 1
+#endif
+
+// The glibc and Bionic implementation of pthreads implements
+// pthread_mutex_destroy as nop for regular mutexes. Additionally, Win32
+// mutexes have no destroy mechanism.
+//
+// This optimization can't be performed on Apple platforms, where
+// pthread_mutex_destroy can allow the kernel to release resources.
+// See https://llvm.org/D64298 for details.
+//
+// TODO(EricWF): Enable this optimization on Bionic after speaking to their
+//               respective stakeholders.
+// clang-format off
+#  if (_LIBCPP_HAS_THREAD_API_PTHREAD && defined(__GLIBC__)) ||                                                        \
+      (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) ||                                                          \
+       _LIBCPP_HAS_THREAD_API_WIN32
+// clang-format on
+#  define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 1
+#else
+#  define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 0
+#endif
+
+// Destroying a condvar is a nop on Windows.
+//
+// This optimization can't be performed on Apple platforms, where
+// pthread_cond_destroy can allow the kernel to release resources.
+// See https://llvm.org/D64298 for details.
+//
+// TODO(EricWF): This is potentially true for some pthread implementations
+// as well.
+#if (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) || _LIBCPP_HAS_THREAD_API_WIN32
+#  define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 1
+#else
+#  define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 0
+#endif
+
+#if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) || _LIBCPP_HAS_MUSL_LIBC || \
+    defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+#  define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
+#endif
+
 #endif // _LIBCPP___CONFIGURATION_PLATFORM_H


        

From libcxx-commits at lists.llvm.org  Wed Jul  1 11:49:26 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 01 Jul 2026 11:49:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Move threading and random device
 config into <__configuration/platform.h> (PR #206262)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206262@github.com>
Message-ID: <6a456136.1a4df1a5.118deb.2a34@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/206262

From libcxx-commits at lists.llvm.org  Wed Jul  1 11:49:54 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 11:49:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove projection from
	__lower_bound_onesided (PR #206676)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206676@github.com>
Message-ID: <6a456152.c56fb102.11841f.7013@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/206676

From libcxx-commits at lists.llvm.org  Wed Jul  1 11:51:02 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 11:51:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++] Mark LWG4098 as resolved (PR
	#206295)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206295@github.com>
Message-ID: <6a456196.1a4df1a5.118deb.5c67@mx.google.com>


https://github.com/ldionne ready_for_review https://github.com/llvm/llvm-project/pull/206295

From libcxx-commits at lists.llvm.org  Wed Jul  1 11:51:23 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 11:51:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++] Mark LWG4098 as resolved (PR
	#206295)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206295@github.com>
Message-ID: <6a4561ab.af6ba3ed.2c2f30.7d8e@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/206295

From libcxx-commits at lists.llvm.org  Wed Jul  1 11:53:23 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 11:53:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++] Mark LWG4098 as resolved (PR
	#206295)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206295@github.com>
Message-ID: <6a456223.6cfa33d2.19518c.c8fd@mx.google.com>


ldionne wrote:

Do we have test coverage for this? https://cplusplus.github.io/LWG/issue4098 says:

> `((void)E, auto(views::empty<tuple<>>))` if `N` is equal to `0` and `decltype((E))` models `forward_range`,

Do we have a test that exercises that (i.e. if `N == 0` but `decltype((E))` does not model `forward_range`)?

https://github.com/llvm/llvm-project/pull/206295

From libcxx-commits at lists.llvm.org  Wed Jul  1 11:53:39 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 11:53:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG4098 as resolved (PR
	#206295)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206295@github.com>
Message-ID: <6a456233.510f576d.185953.644b@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/206295

From libcxx-commits at lists.llvm.org  Wed Jul  1 11:56:48 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 11:56:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG4098 as resolved (PR
	#206295)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206295@github.com>
Message-ID: <6a4562f0.c56fb102.11841f.a2d8@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Lucas Mellone (lknknm)

<details>
<summary>Changes</summary>

Already implemented in the scope of the full implementation for `adjacent_view`  (#<!-- -->165089/ https://github.com/llvm/llvm-project/commit/1e15dbe311eb08462e7a68fcb8b5850632e24aff)

Closes #<!-- -->105353

---
Full diff: https://github.com/llvm/llvm-project/pull/206295.diff


1 Files Affected:

- (modified) libcxx/docs/Status/Cxx26Issues.csv (+1-1) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..40bf1cc72f6ee 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -72,7 +72,7 @@
 "`LWG4082 <https://wg21.link/LWG4082>`__","``views::concat(r)`` is well-formed when ``r`` is an ``output_range``","2024-06 (St. Louis)","|Complete|","23","`#105349 <https://github.com/llvm/llvm-project/issues/105349>`__",""
 "`LWG4083 <https://wg21.link/LWG4083>`__","``views::as_rvalue`` should reject non-input ranges","2024-06 (St. Louis)","|Complete|","22","`#105351 <https://github.com/llvm/llvm-project/issues/105351>`__",""
 "`LWG4096 <https://wg21.link/LWG4096>`__","``views::iota(views::iota(0))`` should be rejected","2024-06 (St. Louis)","|Complete|","22","`#105352 <https://github.com/llvm/llvm-project/issues/105352>`__",""
-"`LWG4098 <https://wg21.link/LWG4098>`__","``views::adjacent<0>`` should reject non-forward ranges","2024-06 (St. Louis)","","","`#105353 <https://github.com/llvm/llvm-project/issues/105353>`__",""
+"`LWG4098 <https://wg21.link/LWG4098>`__","``views::adjacent<0>`` should reject non-forward ranges","2024-06 (St. Louis)","|Complete|","22","`#105353 <https://github.com/llvm/llvm-project/issues/105353>`__",""
 "`LWG4105 <https://wg21.link/LWG4105>`__","``ranges::ends_with``\`s Returns misses difference casting","2024-06 (St. Louis)","","","`#105354 <https://github.com/llvm/llvm-project/issues/105354>`__",""
 "`LWG4106 <https://wg21.link/LWG4106>`__","``basic_format_args`` should not be default-constructible","2024-06 (St. Louis)","|Complete|","19","`#105355 <https://github.com/llvm/llvm-project/issues/105355>`__",""
 "","","","","","",""

``````````

</details>


https://github.com/llvm/llvm-project/pull/206295

From libcxx-commits at lists.llvm.org  Wed Jul  1 11:58:52 2026
From: libcxx-commits at lists.llvm.org (Benjamin Luke via libcxx-commits)
Date: Wed, 01 Jul 2026 11:58:52 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libc] [libcxx] [clang][X86] Emit AVX
 level mismatch psABI warnings on function definitions (PR #199091)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/199091@github.com>
Message-ID: <6a45636c.331483e7.5bc1e.7167@mx.google.com>


https://github.com/freaknbigpanda updated https://github.com/llvm/llvm-project/pull/199091

>From 5c390db7fb4181395dbd1435d513751df242dc45 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Wed, 20 May 2026 11:53:38 -0700
Subject: [PATCH 01/12] [clang][X86] Emit AVX level mismatch psABI warnings on
 function definitions

Emit -WpsABI for x86_64 function definitions whose return type or
parameter type uses a vector wider than 128 bits without the required ABI
feature enabled. 256-bit vectors require avx, and 512-bit vectors require
avx512f.

Previously this diagnostic was only emitted at call sites, so definitions
with wide vector signatures could be introduced without a warning until
they were called. Use the function feature map so
attribute(target("avx/avx512f")) definitions are accepted, and emit no
warnings for prototype-only declarations.
---
 clang/lib/CodeGen/Targets/X86.cpp             | 51 +++++++++++++++++++
 clang/test/CodeGen/X86/mmx-inline-asm-error.c |  2 +
 clang/test/CodeGen/target-builtin-error-3.c   |  6 +++
 clang/test/CodeGen/target-features-error-2.c  |  1 +
 clang/test/CodeGenCXX/target-avx-abi-diag.cpp | 37 ++++++++++++++
 libc/src/mathvec/generic/CMakeLists.txt       |  2 +
 libcxx/utils/libcxx/test/params.py            |  3 ++
 7 files changed, 102 insertions(+)
 create mode 100644 clang/test/CodeGenCXX/target-avx-abi-diag.cpp

diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 4e99ebab6ba34..37c643f14e39f 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -9,6 +9,7 @@
 #include "ABIInfoImpl.h"
 #include "TargetInfo.h"
 #include "clang/Basic/DiagnosticFrontend.h"
+#include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/SmallBitVector.h"
 
 using namespace clang;
@@ -1509,6 +1510,9 @@ class X86_64TargetCodeGenInfo : public TargetCodeGenInfo {
                             const FunctionDecl *Caller,
                             const FunctionDecl *Callee, const CallArgList &Args,
                             QualType ReturnType) const override;
+
+  void checkFunctionABI(CodeGenModule &CGM,
+                        const FunctionDecl *FD) const override;
 };
 } // namespace
 
@@ -1571,6 +1575,53 @@ static bool checkAVXParam(DiagnosticsEngine &Diag, ASTContext &Ctx,
   return false;
 }
 
+void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
+                                               const FunctionDecl *FD) const {
+  auto GetReturnTypeLoc = [](const FunctionDecl *FD) {
+    if (const TypeSourceInfo *TSI = FD->getTypeSourceInfo()) {
+      TypeLoc TL = TSI->getTypeLoc();
+
+      if (auto FTL = TL.IgnoreParens().getAs<FunctionTypeLoc>())
+        return FTL.getReturnLoc().getBeginLoc();
+    }
+
+    return FD->getLocation();
+  };
+
+  auto Check = [&](QualType Ty, SourceLocation Loc, bool IsReturn) {
+    if (!Ty->isVectorType())
+      return false;
+    if (CGM.getContext().getTypeSize(Ty) <= 128)
+      return false;
+
+    StringRef Feature =
+        CGM.getContext().getTypeSize(Ty) > 256 ? "avx512f" : "avx";
+
+    llvm::StringMap<bool> FeatureMap;
+    CGM.getContext().getFunctionFeatureMap(FeatureMap, FD);
+    if (!FeatureMap.lookup(Feature)) {
+      CGM.getDiags().Report(Loc, diag::warn_avx_calling_convention)
+          << !IsReturn << Ty << Feature;
+      return true;
+    }
+
+    return false;
+  };
+
+  // First check the return type and emit diagnostic if required
+  Check(FD->getReturnType(), GetReturnTypeLoc(FD), true);
+
+  // Go through the parameters and emit a warning for the first vector found
+  // without the matching function AVX level attribute
+  for (const ParmVarDecl *P : FD->parameters()) {
+    SourceLocation Loc = P->getLocation();
+    if (Loc.isInvalid())
+      Loc = P->getBeginLoc();
+    if (Check(P->getType(), Loc, false))
+      return;
+  }
+}
+
 void X86_64TargetCodeGenInfo::checkFunctionCallABI(CodeGenModule &CGM,
                                                    SourceLocation CallLoc,
                                                    const FunctionDecl *Caller,
diff --git a/clang/test/CodeGen/X86/mmx-inline-asm-error.c b/clang/test/CodeGen/X86/mmx-inline-asm-error.c
index 8a2f991a537a2..7f7f53a553057 100644
--- a/clang/test/CodeGen/X86/mmx-inline-asm-error.c
+++ b/clang/test/CodeGen/X86/mmx-inline-asm-error.c
@@ -2,6 +2,8 @@
 // RUN: %clang_cc1 -verify=omp -triple x86_64-unknown-unknown -emit-llvm-only -fopenmp %s
 typedef int vec256 __attribute__((ext_vector_type(8)));
 
+// omp-warning at +2 {{AVX vector return of type 'vec256' (vector of 8 'int' values) without 'avx' enabled changes the ABI}}
+// omp-warning at +1 {{AVX vector argument of type 'vec256' (vector of 8 'int' values) without 'avx' enabled changes the ABI}}
 vec256 foo(vec256 in) {
   vec256 out;
 
diff --git a/clang/test/CodeGen/target-builtin-error-3.c b/clang/test/CodeGen/target-builtin-error-3.c
index 056dc940f7a93..840ca7534905d 100644
--- a/clang/test/CodeGen/target-builtin-error-3.c
+++ b/clang/test/CodeGen/target-builtin-error-3.c
@@ -17,32 +17,38 @@ typedef __attribute__ ((ext_vector_type(16),__aligned__( 64))) float float16;
 static inline half8 __attribute__((__overloadable__)) convert_half( float8 a ) {
   return __extension__ ({ __m256 __a = (a); (__m128i)__builtin_ia32_vcvtps2ph256((__v8sf)__a, (0x00)); }); // expected-error {{'__builtin_ia32_vcvtps2ph256' needs target feature f16c}}
 }
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 static inline half16 __attribute__((__overloadable__)) convert_half( float16 a ) {
   half16 r;
   r.lo = convert_half(a.lo);
   return r;
 }
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 void avx_test( uint16_t *destData, float16 argbF)
 {
   ((half16U *)destData)[0] = convert_half(argbF);
 }
 
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 half16 test( float16 a ) {
   half16 r;
   r.lo = convert_half(a.lo);
   return r;
 }
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 void avx_test2( uint16_t *destData, float16 argbF)
 {
   // expected-warning at +1{{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
   ((half16U *)destData)[0] = test(argbF);
 }
 
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 __attribute__((always_inline)) half16 test2( float16 a ) {
   half16 r;
   r.lo = convert_half(a.lo);
   return r;
 }
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 void avx_test3( uint16_t *destData, float16 argbF)
 {
   ((half16U *)destData)[0] = test2(argbF);
diff --git a/clang/test/CodeGen/target-features-error-2.c b/clang/test/CodeGen/target-features-error-2.c
index 1599b0135b747..8f2ef35b8a931 100644
--- a/clang/test/CodeGen/target-features-error-2.c
+++ b/clang/test/CodeGen/target-features-error-2.c
@@ -8,6 +8,7 @@
 #include <x86intrin.h>
 
 #if NEED_AVX_1
+// expected-warning at +1 {{AVX vector argument of type '__m256i' (vector of 4 'long long' values) without 'avx' enabled changes the ABI}}
 int baz(__m256i a) {
   return _mm256_extract_epi32(a, 3); // expected-error {{'__builtin_ia32_vec_ext_v8si' needs target feature avx}}
 }
diff --git a/clang/test/CodeGenCXX/target-avx-abi-diag.cpp b/clang/test/CodeGenCXX/target-avx-abi-diag.cpp
new file mode 100644
index 0000000000000..5048cb092e67f
--- /dev/null
+++ b/clang/test/CodeGenCXX/target-avx-abi-diag.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -emit-llvm -disable-llvm-passes -verify=noavx,noavx512 -o - %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -target-feature +avx -emit-llvm -disable-llvm-passes -verify=noavx512 -o - %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -target-feature +avx512f -emit-llvm -disable-llvm-passes -verify=both -o - %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -Wno-psabi -emit-llvm -disable-llvm-passes -verify=suppressed -o - %s
+// REQUIRES: x86-registered-target
+
+// both-no-diagnostics
+// suppressed-no-diagnostics
+
+typedef float v8f __attribute__((vector_size(32)));
+typedef float v16f __attribute__((vector_size(64)));
+
+// Prototype-only declarations do not warn.
+v8f proto256(v8f);
+v16f proto512(v16f);
+
+// noavx-warning at +2 {{AVX vector return of type 'v8f' (vector of 8 'float' values) without 'avx' enabled changes the ABI}}
+// noavx-warning at +1 {{AVX vector argument of type 'v8f' (vector of 8 'float' values) without 'avx' enabled changes the ABI}}
+v8f def256(v8f x) {
+  return x;
+}
+
+// noavx512-warning at +2 {{AVX vector return of type 'v16f' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
+// noavx512-warning at +1 {{AVX vector argument of type 'v16f' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
+v16f def512(v16f x) {
+  return x;
+}
+
+__attribute__((target("avx")))
+v8f def256_avx(v8f x) {
+  return x;
+}
+
+__attribute__((target("avx512f")))
+v16f def512_avx512(v16f x) {
+  return x;
+}
diff --git a/libc/src/mathvec/generic/CMakeLists.txt b/libc/src/mathvec/generic/CMakeLists.txt
index d5d94d5ed56b5..0592ec1258cbf 100644
--- a/libc/src/mathvec/generic/CMakeLists.txt
+++ b/libc/src/mathvec/generic/CMakeLists.txt
@@ -7,6 +7,8 @@ add_entrypoint_object(
   DEPENDS
     libc.src.__support.macros.properties.cpu_features
     libc.src.__support.mathvec.expf
+  COMPILE_OPTIONS
+    -Wno-psabi
   FLAGS
     ROUND_OPT
     FMA_OPT
diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py
index d0bb38ab84df4..2c78a32b1449b 100644
--- a/libcxx/utils/libcxx/test/params.py
+++ b/libcxx/utils/libcxx/test/params.py
@@ -76,6 +76,9 @@
     # We're not annotating all the APIs, since that's a lot of annotations compared to how many we actually care about
     "-Wno-nullability-completeness",
 
+    # Adding this for now to get the build to pass but needs more detailed review
+    "-Wno-psabi",
+
     # Technically not a warning flag, but might as well be:
     "-flax-vector-conversions=none",
 ]

>From b7ff7d2f1bcfaa2dbfda0b3cb0c111395a12b1ff Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Thu, 21 May 2026 11:57:54 -0700
Subject: [PATCH 02/12] darker format

---
 libcxx/utils/libcxx/test/params.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py
index 2c78a32b1449b..c9e6f767679fa 100644
--- a/libcxx/utils/libcxx/test/params.py
+++ b/libcxx/utils/libcxx/test/params.py
@@ -75,7 +75,6 @@
 
     # We're not annotating all the APIs, since that's a lot of annotations compared to how many we actually care about
     "-Wno-nullability-completeness",
-
     # Adding this for now to get the build to pass but needs more detailed review
     "-Wno-psabi",
 

>From 7bca183e89c48af4ae6f7b6b04bb1fe3f4bf1dac Mon Sep 17 00:00:00 2001
From: freaknbigpanda <benjamin.luke at sony.com>
Date: Wed, 27 May 2026 17:10:57 -0700
Subject: [PATCH 03/12] Restricted warnings to externally visible definitions,
 for internal definitions ABI mismatch will be diagnosed at the call site.
 Added test to make sure we don't emit anything for internal definitions.

Improved the warning by returning valid source locations for functions that
don't have a valid FunctionTypeLoc, previously we were returning warnings
with no location. Added test to cover this case for lambda's with deduced
return type.
---
 clang/lib/CodeGen/Targets/X86.cpp             | 18 +++++++++++++++---
 clang/test/CodeGenCXX/target-avx-abi-diag.cpp | 18 ++++++++++++++++++
 libc/src/mathvec/generic/CMakeLists.txt       |  2 --
 libcxx/include/__algorithm/simd_utils.h       |  6 ++++--
 libcxx/utils/libcxx/test/params.py            |  2 --
 5 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 37c643f14e39f..15bfe6fc1b681 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1581,11 +1581,18 @@ void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
     if (const TypeSourceInfo *TSI = FD->getTypeSourceInfo()) {
       TypeLoc TL = TSI->getTypeLoc();
 
-      if (auto FTL = TL.IgnoreParens().getAs<FunctionTypeLoc>())
-        return FTL.getReturnLoc().getBeginLoc();
+      if (auto FTL = TL.IgnoreParens().getAs<FunctionTypeLoc>()) {
+        SourceLocation Loc = FTL.getReturnLoc().getBeginLoc();
+        if (Loc.isValid())
+          return Loc;
+      }
     }
 
-    return FD->getLocation();
+    SourceLocation Loc = FD->getLocation();
+    if (Loc.isValid())
+      return Loc;
+
+    return FD->getBeginLoc();
   };
 
   auto Check = [&](QualType Ty, SourceLocation Loc, bool IsReturn) {
@@ -1608,6 +1615,11 @@ void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
     return false;
   };
 
+  // psABI warnings & errors for function definitions that are only visible 
+  // in this translation unit are handled at call site by checkFunctionCallABI
+  if (!FD->isExternallyVisible())
+    return;
+
   // First check the return type and emit diagnostic if required
   Check(FD->getReturnType(), GetReturnTypeLoc(FD), true);
 
diff --git a/clang/test/CodeGenCXX/target-avx-abi-diag.cpp b/clang/test/CodeGenCXX/target-avx-abi-diag.cpp
index 5048cb092e67f..66312d513058e 100644
--- a/clang/test/CodeGenCXX/target-avx-abi-diag.cpp
+++ b/clang/test/CodeGenCXX/target-avx-abi-diag.cpp
@@ -26,6 +26,11 @@ v16f def512(v16f x) {
   return x;
 }
 
+// Internal definitions do not warn
+static v8f internal_def256(v8f x) {
+  return x;
+}
+
 __attribute__((target("avx")))
 v8f def256_avx(v8f x) {
   return x;
@@ -35,3 +40,16 @@ __attribute__((target("avx512f")))
 v16f def512_avx512(v16f x) {
   return x;
 }
+
+// Check to make sure deduced return lambdas still have valid source location which allows these pragmas to work
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpsabi"
+template <class T>
+T template_lambda_return() {
+  return []() { return T{}; }();
+}
+
+v8f use_template_lambda_return() {
+  return template_lambda_return<v8f>();
+}
+#pragma clang diagnostic pop
diff --git a/libc/src/mathvec/generic/CMakeLists.txt b/libc/src/mathvec/generic/CMakeLists.txt
index 0592ec1258cbf..d5d94d5ed56b5 100644
--- a/libc/src/mathvec/generic/CMakeLists.txt
+++ b/libc/src/mathvec/generic/CMakeLists.txt
@@ -7,8 +7,6 @@ add_entrypoint_object(
   DEPENDS
     libc.src.__support.macros.properties.cpu_features
     libc.src.__support.mathvec.expf
-  COMPILE_OPTIONS
-    -Wno-psabi
   FLAGS
     ROUND_OPT
     FMA_OPT
diff --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index c47f79ac7f1dd..5b87f13e5bb01 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -90,6 +90,9 @@ inline constexpr size_t __native_vector_size = 1;
 template <class _ArithmeticT, size_t _Np>
 using __simd_vector __attribute__((__ext_vector_type__(_Np))) _LIBCPP_NODEBUG = _ArithmeticT;
 
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
+
 template <class _VecT>
 inline constexpr size_t __simd_vector_size_v = []<bool _False = false>() -> size_t {
   static_assert(_False, "Not a vector!");
@@ -115,8 +118,6 @@ template <class _VecT, class _Iter>
 }
 
 // Load the first _Np elements, zero the rest
-_LIBCPP_DIAGNOSTIC_PUSH
-_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
 template <class _VecT, size_t _Np, class _Iter>
 [[__nodiscard__]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _VecT __partial_load(_Iter __iter) noexcept {
   return [=]<size_t... _LoadIndices, size_t... _ZeroIndices>(
@@ -174,6 +175,7 @@ template <class _Tp, size_t _Np>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t __find_first_not_set(__simd_vector<_Tp, _Np> __vec) noexcept {
   return std::__find_first_set(~__vec);
 }
+_LIBCPP_DIAGNOSTIC_POP
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py
index c9e6f767679fa..d0bb38ab84df4 100644
--- a/libcxx/utils/libcxx/test/params.py
+++ b/libcxx/utils/libcxx/test/params.py
@@ -75,8 +75,6 @@
 
     # We're not annotating all the APIs, since that's a lot of annotations compared to how many we actually care about
     "-Wno-nullability-completeness",
-    # Adding this for now to get the build to pass but needs more detailed review
-    "-Wno-psabi",
 
     # Technically not a warning flag, but might as well be:
     "-flax-vector-conversions=none",

>From 157557bc6a28888461fec6e8bbc1b5e5d8b57732 Mon Sep 17 00:00:00 2001
From: freaknbigpanda <benjamin.luke at sony.com>
Date: Wed, 27 May 2026 18:12:38 -0700
Subject: [PATCH 04/12] fix test

---
 clang/test/CodeGen/target-builtin-error-3.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/CodeGen/target-builtin-error-3.c b/clang/test/CodeGen/target-builtin-error-3.c
index 840ca7534905d..75754f40ac44d 100644
--- a/clang/test/CodeGen/target-builtin-error-3.c
+++ b/clang/test/CodeGen/target-builtin-error-3.c
@@ -17,7 +17,7 @@ typedef __attribute__ ((ext_vector_type(16),__aligned__( 64))) float float16;
 static inline half8 __attribute__((__overloadable__)) convert_half( float8 a ) {
   return __extension__ ({ __m256 __a = (a); (__m128i)__builtin_ia32_vcvtps2ph256((__v8sf)__a, (0x00)); }); // expected-error {{'__builtin_ia32_vcvtps2ph256' needs target feature f16c}}
 }
-// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
+// Internal definitions do not warn.
 static inline half16 __attribute__((__overloadable__)) convert_half( float16 a ) {
   half16 r;
   r.lo = convert_half(a.lo);

>From 2e9d22e0d9231061d9dc28f7690b49929386c379 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Wed, 27 May 2026 18:15:16 -0700
Subject: [PATCH 05/12] clang format

---
 clang/lib/CodeGen/Targets/X86.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 15bfe6fc1b681..ce98a7d44540e 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1615,7 +1615,7 @@ void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
     return false;
   };
 
-  // psABI warnings & errors for function definitions that are only visible 
+  // psABI warnings & errors for function definitions that are only visible
   // in this translation unit are handled at call site by checkFunctionCallABI
   if (!FD->isExternallyVisible())
     return;

>From f314588a1bc9c79d0fc1affc3ed5744e554a27ee Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Thu, 25 Jun 2026 08:25:18 -0700
Subject: [PATCH 06/12] Update clang/lib/CodeGen/Targets/X86.cpp

Co-authored-by: Aaron Ballman <aaron at aaronballman.com>
---
 clang/lib/CodeGen/Targets/X86.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index ce98a7d44540e..b2be5770ead6d 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1616,7 +1616,7 @@ void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
   };
 
   // psABI warnings & errors for function definitions that are only visible
-  // in this translation unit are handled at call site by checkFunctionCallABI
+  // in this translation unit are handled at call site by checkFunctionCallABI.
   if (!FD->isExternallyVisible())
     return;
 

>From 34d35433ac6e2f9eb6b256406b2d21d991e2c347 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Thu, 25 Jun 2026 08:25:45 -0700
Subject: [PATCH 07/12] Update clang/lib/CodeGen/Targets/X86.cpp

Co-authored-by: Aaron Ballman <aaron at aaronballman.com>
---
 clang/lib/CodeGen/Targets/X86.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index b2be5770ead6d..f5472319c9925 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1620,7 +1620,7 @@ void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
   if (!FD->isExternallyVisible())
     return;
 
-  // First check the return type and emit diagnostic if required
+  // First check the return type and emit diagnostic if required.
   Check(FD->getReturnType(), GetReturnTypeLoc(FD), true);
 
   // Go through the parameters and emit a warning for the first vector found

>From 65ccbb6975b9f25a69ef1bc1eafbe2e8eb2a6f53 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Thu, 25 Jun 2026 08:25:55 -0700
Subject: [PATCH 08/12] Update clang/lib/CodeGen/Targets/X86.cpp

Co-authored-by: Aaron Ballman <aaron at aaronballman.com>
---
 clang/lib/CodeGen/Targets/X86.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index f5472319c9925..807d372c3c8fd 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1624,7 +1624,7 @@ void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
   Check(FD->getReturnType(), GetReturnTypeLoc(FD), true);
 
   // Go through the parameters and emit a warning for the first vector found
-  // without the matching function AVX level attribute
+  // without the matching function AVX level attribute.
   for (const ParmVarDecl *P : FD->parameters()) {
     SourceLocation Loc = P->getLocation();
     if (Loc.isInvalid())

>From d2c2dbf68151f46ef74c317958857a9cf6e57b73 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Thu, 25 Jun 2026 08:37:57 -0700
Subject: [PATCH 09/12] Added release note

---
 clang/docs/ReleaseNotes.rst | 915 ++++++++++++++++++++++++++++++++++++
 1 file changed, 915 insertions(+)
 create mode 100644 clang/docs/ReleaseNotes.rst

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
new file mode 100644
index 0000000000000..80b7880bdc85a
--- /dev/null
+++ b/clang/docs/ReleaseNotes.rst
@@ -0,0 +1,915 @@
+.. If you want to modify sections/contents permanently, you should modify both
+   ReleaseNotes.rst and ReleaseNotesTemplate.txt.
+
+===========================================
+Clang |release| |ReleaseNotesTitle|
+===========================================
+
+.. contents::
+   :local:
+   :depth: 2
+
+Written by the `LLVM Team <https://llvm.org/>`_
+
+.. only:: PreRelease
+
+  .. warning::
+     These are in-progress notes for the upcoming Clang |version| release.
+     Release notes for previous releases can be found on
+     `the Releases Page <https://llvm.org/releases/>`_.
+
+Introduction
+============
+
+This document contains the release notes for the Clang C/C++/Objective-C
+frontend, part of the LLVM Compiler Infrastructure, release |release|. Here we
+describe the status of Clang in some detail, including major
+improvements from the previous release and new feature work. For the
+general LLVM release notes, see `the LLVM
+documentation <https://llvm.org/docs/ReleaseNotes.html>`_. For the libc++ release notes,
+see `this page <https://libcxx.llvm.org/ReleaseNotes.html>`_. All LLVM releases
+may be downloaded from the `LLVM releases web site <https://llvm.org/releases/>`_.
+
+For more information about Clang or LLVM, including information about the
+latest release, please see the `Clang Web Site <https://clang.llvm.org>`_ or the
+`LLVM Web Site <https://llvm.org>`_.
+
+Potentially Breaking Changes
+============================
+
+C/C++ Language Potentially Breaking Changes
+-------------------------------------------
+
+- Clang now makes it ill-formed to try to ``break`` out of or ``continue`` a loop inside its own condition,
+  increment, or init-statement in all C and C++ language modes. This means that code such as
+
+  .. code-block:: c++
+
+    while (({ break; })) {}
+
+  is now ill-formed. An outer loop can still be broken out of or continued so long as the inner loop is
+  in the body of the outer loop:
+
+  .. code-block:: c++
+
+    // Ok, this breaks out of the 'for' loop.
+    for (;;) {
+      while (({ break; true; })) {}
+    }
+
+    // Error: can't break out of the 'for' loop from within its own increment.
+    for (;;({ while (({ break; true; })) {} })) {}
+
+  This also resolves a divergence from GCC: in a construct such as
+
+  .. code-block:: c++
+
+    for (;;) {
+      while (({ break; true; })) {}
+    }
+
+  Clang would previously ``break`` out of the ``while`` loop, whereas GCC (since version 9) would
+  ``break`` out of the ``for`` loop here. Now, Clang and GCC both break out of the ``for`` loop.
+
+C++ Specific Potentially Breaking Changes
+-----------------------------------------
+
+- Clang now more aggressively optimizes away stores to objects after they are
+  dead. This behavior can be disabled with ``-fno-lifetime-dse``.
+
+- Clang now correctly rejects ``export`` declarations in module implementation
+  partitions. (#GH107602)
+
+- Template argument deduction now treats the ``N`` in ``_BitInt(N)``
+  as being of type ``std::size_t`` instead of ``int``,
+  matching the deduction of array sizes from ``int(&)[N]``.
+  This is a breaking change for code that depended on the previously deduced type. (#GH195033)
+
+- Clang now rejects nested local classes defined in a different
+  block scope than their parent class. (#GH193472)
+
+ABI Changes in This Version
+---------------------------
+
+- Fixed incorrect struct layout for ``_BitInt`` bitfields wider than 255 bits
+  on MSVC targets. Internal bitfield tracking fields were changed from
+  ``unsigned char`` to ``uint64_t`` to prevent overflow. This might be an ABI
+  break for such structs compared to earlier Clang versions.
+- Fixed a number of issues with the ``__regcall`` calling convention for passing
+  structs on non-Windows x86-64 targets, including a crash when handling empty
+  struct arguments. This changes how structs that contain arrays, floating point
+  types, or ``_Complex float`` types are passed, and may introduce
+  incompatibilities with code compiled by earlier versions of Clang that uses
+  the ``__regcall`` calling convention on these targets. (#GH62999) (#GH98635)
+- Fixed Itanium mangling for lambdas in instantiated non-static data member
+  initializers by preserving the field-name closure-prefix. This changes the
+  mangled names for affected lambdas. (#GH190555)
+
+AST Dumping Potentially Breaking Changes
+----------------------------------------
+
+- The JSON AST dump now includes all fields from ``AvailabilityAttr``: ``platform``,
+  ``introduced``, ``deprecated``, ``obsoleted``, ``unavailable``, ``message``,
+  ``strict``, ``replacement``, ``priority``, and ``environment``. Previously, these
+  fields were missing from the JSON output.
+- Colons that appear at the end of a ParamCommentCommand name are not serialized
+  as part of the name.
+
+Clang Frontend Potentially Breaking Changes
+-------------------------------------------
+
+- HIPSPV toolchain: `--offload-targets=spirv{32,64}` option is
+  deprecated and will be removed when the new offload driver becomes
+  default. The replacement for the option is
+  `--offload-targets=spirv{32,64}-unknown-chipstar` when using the new
+  offload driver (`--offload-new-driver`).
+- The new driver (`--offload-new-driver`) is now default for all offloading
+  compilations. This changes the ABI for relocatable device code. Currently,
+  libraries will need to be recompiled, or used with
+  (`--no-offload-new-driver`). This option will be removed in the next release.
+
+- Clang no longer defines the ``__cpp_impl_coroutine`` feature test macro under the 32-bit x86 Microsoft ABI,
+  as support for coroutines on this target is incomplete.
+  When using coroutines on this target a warning is emmitted to indicate the lack of full support.
+  That warning can be disabled with ``-Wno-coroutines-unsupported-target``. (see #GH59382)
+
+Clang Python Bindings Potentially Breaking Changes
+--------------------------------------------------
+- Remove ``CompletionString.Availability``. No libclang interfaces returned instances of it.
+- ``CompletionString.availability`` now returns instances of ``CompletionString.AvailabilityKindCompat``.
+
+  Instances of ``AvailabilityKindCompat`` have the same ``__str__`` representation
+  as the previous ``CompletionChunk.Kind`` and are equality-comparable with
+  the existing ``AvailabilityKind`` enum. It will be replaced by ``AvailabilityKind``
+  in a future release. When this happens, the return type of ``CompletionString.availability``
+  will change to ``AvailabilityKind``, so it is recommended to use ``AvailabilityKind``
+  to compare with the return values of ``CompletionString.availability``.
+- Remove ``availabilityKinds``. In this release, uses of ``availabilityKinds``
+  need to be replaced by ``CompletionString.AvailabilityKind``.
+- ``CompletionChunk.kind`` now returns instances of ``CompletionChunkKind``.
+
+  Instances of ``CompletionChunkKind`` have the same ``__str__`` representation
+  as the previous ``CompletionChunk.Kind`` for compatibility.
+  These representations will be changed in a future release to match other enums.
+- Remove ``completionChunkKindMap``. In this release, uses of ``completionChunkKindMap``
+  need to be replaced by ``CompletionChunkKind``.
+- Move ``SPELLING_CACHE`` into ``CompletionChunk`` and change it to use
+  ``CompletionChunkKind`` instances as keys, instead of the enum values.
+  An alias is kept in the form of a ``SPELLING_CACHE`` variable, but it only supports
+  ``__getitem__`` and ``__contains__``. It will be removed in a future release.
+  Please migrate to using ``CompletionChunk.SPELLING_CACHE`` instead.
+- ``SourceLocation`` and ``SourceRange`` now use ``NotImplemented`` to delegate
+  equality checks (``__eq__``) to the other object they are compared with when
+  they are of different classes. They previously returned ``False`` when compared
+  with objects of other classes.
+- ``TranslationUnit.get_tokens`` now throws an error if both the ``extent`` and
+  ``locations`` argument are passed. Previousy, ``locations`` took precedence.
+- ``_CXUnsavedFile`` will be renamed to ``UnsavedFile`` for consistency.
+  ``UnsavedFile`` is already available to use and existing uses should
+  be adapted to refer to it instead. ``_CXUnsavedFile`` will be removed in a
+  future release.
+
+OpenCL Potentially Breaking Changes
+-----------------------------------
+- Clang now diagnoses zero-length arrays as errors in OpenCL. OpenCL C 3.0
+  section 6.11.d states that "Variable length arrays and structures with
+  flexible (or unsized) arrays are not supported."
+
+What's New in Clang |release|?
+==============================
+
+C++ Language Changes
+--------------------
+
+- ``__is_trivially_equality_comparable`` no longer returns false for all enum types. (#GH132672)
+
+C++2c Feature Support
+^^^^^^^^^^^^^^^^^^^^^
+
+C++23 Feature Support
+^^^^^^^^^^^^^^^^^^^^^
+
+- Partially implement Itanium mangling for pack indexing. Partially substituted packs are not yet supported. (#GH112003)
+
+C++20 Feature Support
+^^^^^^^^^^^^^^^^^^^^^
+
+- Clang now supports `P1857R3 <https://wg21.link/p1857r3>`_ Modules Dependency Discovery. (#GH54047)
+
+C++17 Feature Support
+^^^^^^^^^^^^^^^^^^^^^
+
+Resolutions to C++ Defect Reports
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Implemented `CWG1780 Explicit instantiation/specialization of generic lambda
+  operator() <https://cplusplus.github.io/CWG/issues/1780.html>`_
+
+- Clang now allows omitting ``typename`` before a template name in a
+  conversion operator, implementing `CWG2413 <https://wg21.link/cwg2413>`_.
+
+- Clang now uses non-reference types for structured bindings whose initializer
+  returns a prvalue. This resolves `CWG3135 <https://wg21.link/cwg3135>`_.
+
+C Language Changes
+------------------
+
+C2y Feature Support
+^^^^^^^^^^^^^^^^^^^
+
+- Implemented the type-specific C2y ``<stdbit.h>`` rotate functions with constexpr
+  evaluation support:
+  ``stdc_rotate_left_{uc,us,ui,ul,ull}`` and
+  ``stdc_rotate_right_{uc,us,ui,ul,ull}``.
+
+C23 Feature Support
+^^^^^^^^^^^^^^^^^^^
+- Clang now allows C23 ``constexpr`` struct member access through the dot operator in constant expressions. (#GH178349)
+
+Objective-C Language Changes
+-----------------------------
+
+- Clang now emits Objective-C number, array, and dictionary literals as
+  compile-time constant data structures rather than runtime ``objc_msgSend``
+  calls on targets whose runtime supports constant literal classes. The
+  feature can be disabled altogether with ``-fno-objc-constant-literals``,
+  or selectively per literal kind with ``-fno-constant-nsnumber-literals``,
+  ``-fno-constant-nsarray-literals``, and
+  ``-fno-constant-nsdictionary-literals``.
+
+Non-comprehensive list of changes in this release
+-------------------------------------------------
+
+- Added support for floating point and pointer values in most ``__atomic_``
+  builtins.
+
+- Added ``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right``
+  for bit rotation of unsigned integers including ``_BitInt`` types. Rotation
+  counts are normalized modulo the bit-width and support negative values.
+  Usable in constant expressions. Implicit conversion is supported for
+  class/struct types with conversion operators.
+- Implemented the following C23 ``<stdbit.h>`` builtins with ``_BitInt``
+  support and constexpr evaluation:
+  ``__builtin_stdc_leading_zeros``, ``__builtin_stdc_leading_ones``,
+  ``__builtin_stdc_trailing_zeros``, ``__builtin_stdc_trailing_ones``,
+  ``__builtin_stdc_first_leading_zero``, ``__builtin_stdc_first_leading_one``,
+  ``__builtin_stdc_first_trailing_zero``, ``__builtin_stdc_first_trailing_one``,
+  ``__builtin_stdc_count_zeros``, ``__builtin_stdc_count_ones``,
+  ``__builtin_stdc_has_single_bit``, ``__builtin_stdc_bit_width``,
+  ``__builtin_stdc_bit_floor``, and ``__builtin_stdc_bit_ceil``.
+
+- Implemented the type-specific C23 ``<stdbit.h>`` functions with constexpr
+  evaluation support:
+  ``stdc_leading_zeros_{uc,us,ui,ul,ull}``,
+  ``stdc_leading_ones_{uc,us,ui,ul,ull}``,
+  ``stdc_trailing_zeros_{uc,us,ui,ul,ull}``,
+  ``stdc_trailing_ones_{uc,us,ui,ul,ull}``,
+  ``stdc_first_leading_zero_{uc,us,ui,ul,ull}``,
+  ``stdc_first_leading_one_{uc,us,ui,ul,ull}``,
+  ``stdc_first_trailing_zero_{uc,us,ui,ul,ull}``,
+  ``stdc_first_trailing_one_{uc,us,ui,ul,ull}``,
+  ``stdc_count_zeros_{uc,us,ui,ul,ull}``,
+  ``stdc_count_ones_{uc,us,ui,ul,ull}``,
+  ``stdc_has_single_bit_{uc,us,ui,ul,ull}``,
+  ``stdc_bit_width_{uc,us,ui,ul,ull}``,
+  ``stdc_bit_floor_{uc,us,ui,ul,ull}``, and
+  ``stdc_bit_ceil_{uc,us,ui,ul,ull}``.
+
+- A new generic bit-reverse builtin function ``__builtin_bitreverseg`` that
+  extends bit-reversal support to all standard integers type, including
+  ``_BitInt``
+
+- Deprecated float types support from ``__builtin_elementwise_max`` and
+  ``__builtin_elementwise_min``.
+
+- Added header ``endian.h`` which contains byte order helpers specified in POSIX
+
+- Added #pragma loop licm(disable) for llvm.loop.licm.disable metadata
+
+- Added a new ``ExplicitInstantiationDecl`` AST node to represent explicit
+  template instantiations (e.g., ``template void foo<int>();`` or
+  ``extern template class S<int>;``). Previously, source location information
+  for explicit instantiation statements was discarded after parsing. The new
+  node preserves the full source range including the ``extern`` and ``template``
+  keywords, qualifiers, template arguments as written, and the declared type,
+  enabling tools such as language servers and refactoring engines to accurately
+  map source locations back to explicit instantiation sites.
+
+- ``typeid`` on references and pointers of ``final`` types no longer emits a
+  vtable lookup at runtime.
+
+- Updated support for Unicode from 15.1 to 18.0.
+
+New Compiler Flags
+------------------
+- New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added
+  to enable or disable Microsoft's anonymous struct/union extension without
+  enabling other ``-fms-extensions`` features (#GH177607).
+- New option ``--precompile-reduced-bmi`` allows build system to generate a
+  reduced BMI only for a C++20 importable module unit. Previously the users
+  can only generate the reduced BMI as a by-product, e.g, an object files or
+  a full BMI.
+
+- New ``-cc1`` option ``-fexperimental-overflow-behavior-types`` added to
+  enable parsing of the experimental ``overflow_behavior`` type attribute and
+  type specifiers.
+
+- New ``-cl`` option ``/d2guardnochecks`` added to match MSVC. When Windows
+  Control Flow Guard (CFG) is enabled by other options, it will instruct Clang
+  to emit the CFG metadata, but disable adding checks.
+
+- New option ``-fdiagnostics-show-inlining-chain`` added to show inlining chain
+  notes for ``[[gnu::warning]]`` and ``[[gnu::error]]`` diagnostics. When a
+  function with these attributes is called from an inlined context, Clang can
+  now show which functions were inlined to reach the call. When debug info is
+  available (``-gline-directives-only`` (implicitly enabled at ``-g1``) or
+  higher), accurate source locations are used; otherwise, a heuristic fallback
+  is used with a note suggesting how to enable debug info for better accuracy.
+
+- New option ``-fwin-cfg-mechanism=`` added to control the mechanism used by
+  Control Flow Guard on Windows. Accepted values are ``automatic`` (default),
+  ``dispatch``, and ``check``. The ``dispatch`` mechanism uses the dispatch
+  function to perform indirect call checks and can improve performance, while
+  ``check`` uses the traditional check mechanism.
+- New ``-cl`` option ``/d2guardcfgdispatch`` added to match MSVC. This acts as a
+  shorthand for ``-fwin-cfg-mechanism=dispatch``.
+- New ``-cl`` option ``/d2guardcfgdispatch-`` added to match MSVC. This acts as a
+  shorthand for ``-fwin-cfg-mechanism=check``.
+
+- New option ``-f[no-]strict-bool`` added to control whether Clang can assume
+  that ``bool`` values loaded from memory cannot have a bit pattern other
+  than 0 or 1.
+
+Deprecated Compiler Flags
+-------------------------
+
+Modified Compiler Flags
+-----------------------
+- The `-mno-outline` and `-moutline` compiler flags are now allowed on RISC-V and X86, which both support the machine outliner.
+- The `-mno-outline` flag will now add the `nooutline` IR attribute, so that
+  `-mno-outline` and `-moutline` objects can be mixed correctly during LTO.
+
+Removed Compiler Flags
+----------------------
+
+Attribute Changes in Clang
+--------------------------
+
+- Added new attribute ``stack_protector_ignore`` to opt specific local variables out of
+  the analysis which determines if a function should get a stack protector.  A function
+  will still generate a stack protector if other local variables or command line flags
+  require it.
+
+- Added a new attribute, ``[[clang::no_outline]]`` to suppress outlining from
+  annotated functions. This uses the LLVM `nooutline` attribute.
+
+- Introduced a new type attribute ``__attribute__((overflow_behavior))`` which
+  currently accepts either ``wrap`` or ``trap`` as an argument, enabling
+  type-level control over overflow behavior. There is also an accompanying type
+  specifier for each behavior kind via `__ob_wrap` and `__ob_trap`.
+
+- Introduced a new function attribute ``__attribute__((__personality__(...)))``
+  to explicitly specify the personality routine for exception handling. THis is
+  meant to be a low level tool for language runtime authors to associate a
+  foreign language personality with a given function. Note that this does not
+  perform any ABI validation for the personality routine.
+
+- :doc:`ThreadSafetyAnalysis` attributes now correctly handle implicit member
+  accesses in C, and parameter attributes in C++. This improves diagnostic
+  precision and fixes false positives.
+
+- The :doc:`ThreadSafetyAnalysis` attributes ``guarded_by`` and
+  ``pt_guarded_by`` now accept multiple capability arguments with refined
+  access semantics: *writing* requires all listed capabilities to be held
+  exclusively, while *reading* requires at least one to be held.  This is
+  sound because any writer must hold all capabilities, so holding any one
+  prevents concurrent writes.
+
+- :doc:`ThreadSafetyAnalysis` attributes like ``acquire_capability``,
+  ``release_capability``, ``requires_capability``, ``locks_excluded``,
+  ``try_acquire_capability``, and ``assert_capability`` can now be applied to
+  function pointer variables and fields.  The analysis checks calls through
+  annotated function pointers the same way it checks direct function calls.
+  Only plain function pointers are supported; pointers-to-member functions,
+  blocks, or wrappers (e.g. ``std::function``) are not yet supported.
+
+- The ``[[clang::unsafe_buffer_usage]]`` attribute is now supported in API
+  notes. For example:
+  
+  .. code-block:: yaml
+
+    Functions:
+      - Name: myUnsafeFunction
+        UnsafeBufferUsage: true
+
+- Added support for ``[[msvc::forceinline]]`` for functions and
+  ``[[msvc::forceinline_calls]]`` for statements. Both are aliases to
+  ``[[clang::always_inline]]`` with additional checks to ensure that they
+  are only accepted in places where MSVC also does.
+
+- The AMDGPU ``amdgpu_num_sgpr`` and ``amdgpu_num_vgpr`` attributes are now
+  deprecated. Clang emits a ``-Wdeprecated-declarations`` warning when they
+  are used. Use ``amdgpu_waves_per_eu`` instead to control SGPR and VGPR
+  usage.
+
+- Clang now allows GNU attributes between a member declarator and bit-field width. (#GH184954)
+
+Improvements to Clang's diagnostics
+-----------------------------------
+- Fixed bug in ``-Wdocumentation`` so that it correctly handles explicit
+  function template instantiations (#64087).
+
+- ``-Wunused-but-set-variable`` now diagnoses file-scope variables with
+  internal linkage (``static`` storage class) that are assigned but never used.
+  This new coverage is added under the subgroup ``-Wunused-but-set-global``,
+  allowing it to be disabled independently with ``-Wno-unused-but-set-global``.
+  (#GH148361)
+
+- Added ``-Wlifetime-safety`` to enable lifetime safety analysis,
+  a CFG-based intra-procedural analysis that detects use-after-free and related
+  temporal safety bugs. See the
+  `RFC <https://discourse.llvm.org/t/rfc-intra-procedural-lifetime-analysis-in-clang/86291>`_
+  for more details. By design, this warning is enabled in ``-Weverything``. To disable
+  the analysis, use ``-Wno-lifetime-safety`` or ``-fno-lifetime-safety``.
+
+- Added ``-Wlifetime-safety-suggestions`` to enable lifetime annotation suggestions.
+  This provides suggestions for function parameters that
+  should be marked ``[[clang::lifetimebound]]`` based on lifetime analysis. For
+  example, for the following function:
+
+  .. code-block:: c++
+
+    int* p(int *in) { return in; }
+
+  Clang will suggest:
+
+  .. code-block:: c++
+
+    warning: parameter in intra-TU function should be marked [[clang::lifetimebound]]
+    int* p(int *in) { return in; }
+           ^~~~~~~
+                   [[clang::lifetimebound]]
+    note: param returned here
+    int* p(int *in) { return in; }
+                             ^~
+
+- Added ``-Wlifetime-safety-noescape`` to detect misuse of ``[[clang::noescape]]``
+  annotation where the parameter escapes through return. For example:
+
+  .. code-block:: c++
+
+    int* p(int *in [[clang::noescape]]) { return in; }
+
+  Clang will warn:
+
+  .. code-block:: c++
+
+    warning: parameter is marked [[clang::noescape]] but escapes
+    int* p(int *in [[clang::noescape]]) { return in; }
+           ^~~~~~~
+    note: returned here
+    int* p(int *in [[clang::noescape]]) { return in; }
+                                                 ^~
+
+- Added ``-Wlifetime-safety-dangling-field`` to detect dangling field references
+  when stack memory escapes to class fields. This is part of ``-Wlifetime-safety``
+  and detects cases where local variables or parameters are stored in fields but
+  outlive their scope. For example:
+
+  .. code-block:: c++
+
+    struct DanglingView {
+      std::string_view view;
+      DanglingView(std::string s) : view(s) {}  // warning: address of stack memory escapes to a field
+    };
+
+- Improved ``-Wassign-enum`` performance by caching enum enumerator values. (#GH176454)
+
+- Clang now emits ``-Wpsabi`` diagnostics for externally visible x86-64
+  function definitions that return or take AVX or AVX-512 vector types without
+  enabling the corresponding target feature.
+
+- Fixed a false negative in ``-Warray-bounds`` where the warning was suppressed
+  when accessing a member function on a past-the-end array element.
+  (#GH179128)
+
+- Added a missing space to the FixIt for the ``implicit-int`` group of diagnostics and
+  made sure that only one such diagnostic and FixIt is emitted per declaration group. (#GH179354)
+
+- Fixed the Fix-It insertion point for ``expected ';' after alias declaration``
+  when parsing alias declarations involving a token-split ``>>`` sequence
+  (for example, ``using A = X<int>>;``). (#GH184425)
+
+- Fixed incorrect ``implicitly deleted`` diagnostic for explicitly deleted
+  candidate function. (#GH185693)
+
+- The ``-Wloop-analysis`` warning has been extended to catch more cases of
+  variable modification inside lambda expressions (#GH132038).
+
+- Clang now emits ``-Wsizeof-pointer-memaccess`` when snprintf/vsnprintf use the sizeof
+  the destination buffer(dynamically allocated) in the len parameter(#GH162366)
+
+- Added ``-Wmodule-map-path-outside-directory`` (off by default) to warn on
+  header and umbrella directory paths that use ``..`` to refer outside the module
+  directory in module maps found via implicit search
+  (``-fimplicit-module-maps``). This does not affect module maps specified
+  explicitly via ``-fmodule-map-file=``.
+
+- Honour ``[[maybe_unused]]`` attribute on private fields.
+  ``-Wunused-private-field`` no longer emits a warning for annotated private
+  fields.
+
+- Improved ``-Wgnu-zero-variadic-macro-arguments`` to suggest using
+  ``__VA_OPT__`` if the current language version supports it(#GH188624)
+
+- Clang now emits an error when implicitly casting a complex type to a built-in vector type. (#GH186805)
+
+- Added ``-Wnonportable-include-path-separator`` (off by default) to catch
+  #include directives that use backslashes as a path separator. The warning
+  includes a FixIt to change all the backslashes to forward slashes, so that the
+  code can automatically be made portable to other host platforms that don't
+  support backslashes.
+
+- Clang now explains why template deduction fails for explicit template arguments.
+
+- No longer emitting a ``-Wpre-c2y-compat`` or extension diagnostic about use
+  of octal literals with a ``0o`` prefix, and no longer emitting a
+  ``-Wdeprecated-octal-literals`` diagnostic for use of octal literals without
+  a ``0o`` prefix, when the literal is expanded from a macro defined in a
+  system header. (#GH192389)
+
+- Improved error recovery for missing semicolons after class members. Clang now avoids
+  skipping subsequent valid declarations when their previous decl is missing semicolon.
+
+- Removed the body of lambdas from some diagnostic messages.
+
+- Fixed false positive host-device mismatch errors in discarded `if constexpr` branches for CUDA/HIP;
+  such calls are now correctly skipped.
+
+- Clang now errors when a function declaration aliases a variable or vice versa. (#GH195550)
+
+- Added ``-Wattribute-alias`` to diagnose type mismatches between an alias and its aliased function. (#GH195550)
+
+- The diagnostics around ``__block`` now explain why a variable cannot be marked ``__block``. (#GH197213)
+
+- Extended ``-Wnonportable-include-path`` to warn about trailing whitespace and dots in ``#include`` paths. (#GH190610)
+
+- Clang now emits error when attribute is missing closing ``]]`` followed by ``;;``. (#GH187223)
+
+Improvements to Clang's time-trace
+----------------------------------
+
+Improvements to Coverage Mapping
+--------------------------------
+
+- [MC/DC] Nested expressions are handled as individual MC/DC expressions.
+- "Single byte coverage" now supports branch coverage and can be used
+  together with ``-fcoverage-mcdc``.
+- Consteval member functions are no longer emitted in coverage mappings,
+  matching the existing behavior for free consteval functions. (#GH164448)
+
+Bug Fixes in This Version
+-------------------------
+
+- Fixed atomic boolean compound assignment; the conversion back to atomic bool would be miscompiled. (#GH33210)
+- Correctly handle default template argument when establishing subsumption. (#GH188640)
+- Fixed a failed assertion in the preprocessor when ``__has_embed`` parameters are missing parentheses. (#GH175088)
+- Fix lifetime extension of temporaries in for-range-initializers in templates. (#GH165182)
+- Fixed a preprocessor crash in ``__has_cpp_attribute`` on incomplete scoped attributes. (#GH178098)
+- Fixes an assertion failure when evaluating ``__underlying_type`` on enum redeclarations. (#GH177943)
+- Fixed an assertion failure caused by nested macro expansion during header-name lexing (``__has_embed(__has_include)``). (#GH178635)
+- Clang now outputs relative paths of embeds for dependency output. (#GH161950)
+- Fix the result type of a binary operation where both operands are 'void' l-values. (#GH111300)
+- Fixed an assertion failure when evaluating ``_Countof`` on invalid ``void``-typed operands. (#GH180893)
+- Fixed an assertion failure in the serialized diagnostic printer when it is destroyed without calling ``finish()``. (#GH140433)
+- Fixed an assertion failure caused by error recovery while extending a nested name specifier with results from ordinary lookup. (#GH181470)
+- Fixed a crash when parsing ``#pragma clang attribute`` arguments for attributes that forbid arguments. (#GH182122)
+- Fixed a bug with multiple-include optimization (MIOpt) state not being preserved in some cases during lexing, which could suppress header-guard mismatch diagnostics and interfere with include-guard optimization. (#GH180155)
+- Fixed a crash when normalizing constraints involving concept template parameters whose index coincided with non-concept template parameters in the same parameter mapping.
+- Fixed a crash caused by accessing dependent diagnostics of a non-dependent context.
+- Fixed a crash when substituting into a non-type template parameter that has a type containing an undeduced placeholder type.
+- Fixed several crashes and improved diagnostics when a multidimensional subscript operator is applied to a built-in type. (#GH187800)
+- Correctly diagnosing and no longer crashing when ``export module foo``
+  (without a semicolon) are the final tokens in a module file. (#GH187771)
+- Fixed a crash in duplicate attribute checking caused by comparing constant arguments with different integer signedness. (#GH188259)
+- Clang now emits an error when returning an initializer list from a lambda
+  with an explicit return type of void. The diagnostic now correctly refers
+  to "lambda" instead of "block". (#GH188661)
+- Fixed a crash on _BitInt(N) arrays where 129 ≤ N ≤ 192 due to incorrect array filler lowering. (#GH189643)
+- Fixed the behavior in C23 of ``auto``, by emitting an error when an array type is specified for a ``char *``. (#GH162694)
+- Fixed an issue where an assert was thrown instead of an error if no vulkan env was specified with ``--triple spirv``. (#GH189964)
+- Fixed incorrect rejection of ``auto`` with reordered declaration specifiers in C23. (#GH164121)
+- Fixed a crash where constexpr evaluation encountered invalid overrides. (#GH183290)
+- Fixed a bug where Clang fails to find instantiation of Decls in constraint checking. (#GH173086)
+- Fixed a crash when assigning to an element of an ``ext_vector_type`` with ``bool`` element type. (#GH189260)
+- Fixed a crash caused by declaring multiple ``ownership_returns`` attributes with mismatched or missing arguments. (#GH188733)
+- Clang now emits an error for friend declarations of lambda members. (#GH26540)
+- Fixed a crash caused by lambda capture handling in delayed default arguments. (#GH176534)
+- Fixed a crash when parsing invalid ``static_assert`` declarations with string-literal messages (#GH187690).
+- Fixed a potential stack-use-after-return issue in Clang when copy-initializing
+  an array via an element-at-a-time copy loop (#GH192026)
+- Fixed an issue where certain designated initializers would be rejected for constexpr variables. (#GH193373)
+- Fixed a crash when ``#embed`` is used with C++ modules (#GH195350)
+
+Bug Fixes to Compiler Builtins
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Fix a crash when passing an unresolved overload set to ``__builtin_classify_type``. (#GH175589)
+- Fixed a crash when calling `__builtin_allow_sanitize_check` with no arguments. (#GH183927)
+- ``__annotation`` is now diagnosed as unsupported on non-Windows/UEFI targets, fixing a
+  crash when using it with ``-fms-extensions`` on other platforms. (#GH184318)
+
+Bug Fixes to Attribute Support
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed a behavioral discrepancy between deleted functions and private members when checking the ``enable_if`` attribute. (#GH175895)
+- Fixed ``init_priority`` attribute by delaying type checks until after the type is deduced.
+
+Bug Fixes to C++ Support
+^^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed a preprocessor assertion failure triggered when parsing an invalid template-id starting with ``::template operator``. (#GH186582)
+- Fixed a crash when a function template is defined as a non-template friend with a global scope qualifier. (#GH185341)
+- Clang now rejects constant template parameters with block pointer types, since these are not implemented anyway and would lead to crashes. (#GH189247)
+- Fixed a crash on error recovery when dealing with invalid templates. (#GH183075)
+- Fixed a crash when instantiating ``requires`` expressions involving substitution failures in C++ concepts. (#GH176402)
+- Concepts appearing in the require-clause of a member function no longer have access to non-public members of that class,
+  or to a current class object. (#GH115838) (#GH194803) (#GH197067)
+- We no longer caches invalid variable specializations. (#GH132592)
+- Fixed an incorrect template argument deduction when matching packs of template
+  template parameters when one of its parameters is also a pack. (#GH181166)
+- Clang no longer errors on overloads with different ref-qualifiers and constraints. (#GH120812)
+- Fixed a crash when a default argument is passed to an explicit object parameter. (#GH176639)
+- Fixed an alias template CTAD crash.
+- Correctly diagnose uses of ``co_await`` / ``co_yield`` in the default argument of nested function declarations. (#GH98923)
+- Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741)
+- Clang incorrectly instantiated variable specializations outside of the immediate context. (#GH54439)
+- Fixed a crash when pack expansions are used as arguments for non-pack parameters of built-in templates. (#GH180307)
+- Fix a problem where a substitution failure when evaluating a type requirement
+  could directly make the program ill-formed.
+- Fix a problem where pack index expressions where incorrectly being regarded as equivalent.
+- Fixed a bug where captured variables in non-mutable lambdas were incorrectly treated as mutable
+  when used inside decltype in the return type. (#GH180460)
+- Fixed a crash when evaluating uninitialized GCC vector/ext_vector_type vectors in ``constexpr``. (#GH180044)
+- Fixed a crash when `explicit(bool)` is used with an incomplete enumeration. (#GH183887)
+- Fixed a crash on ``typeid`` of incomplete local types during template instantiation. (#GH63242), (#GH176397)
+- Fixed spurious diagnostics produced when checking if constraints are equivalent for redeclarations,
+  which could make the program mistakenly ill-formed.
+- Fixed a crash when an immediate-invoked ``consteval`` lambda is used as an invalid initializer. (#GH185270)
+- Fixed an assertion failure when using a global destructor with a target with a non-default program address space. (#GH186484)
+- Fixed a crash when instantiating an invalid out-of-line static data member definition in a local class. (#GH176152)
+- Inherited constructors in ``dllexport`` classes are now exported for ABI-compatible cases, matching
+  MSVC behavior. Constructors with variadic arguments or callee-cleanup parameters are not yet supported
+  and produce a warning. (#GH162640)
+- Correctly diagnose invalid non-dependent calls in dependent contexts. (#GH135694)
+- Fix initialization of GRO when GRO-return type mismatches, as part of CWG2563. (#GH98744)
+- Fix an error using an initializer list with array new for a type that is not default-constructible. (#GH81157)
+- We no longer consider conversion operators when copy-initializing from the same type. This was non
+  conforming and could lead to recursive constraint satisfaction checking. (#GH149443)
+- Fixed a crash in Itanium C++ name mangling for a lambda in a local class field initializer inside a constructor/destructor. (#GH176395)
+- Fixed crashes in Itanium C++ name mangling for lambdas with trailing requires-clauses involving requires-expressions. (#GH100774) (#GH123854)
+- Fixed an invalid rejection and assertion failure while generating ``operator=`` for fields with the ``__restrict`` qualifier. (#GH37979)
+- Fixed a use-after-free bug when parsing default arguments containing lambdas in declarations with template-id declarators. (#GH196725)
+- Fixed a crash in constant evaluation using placement new on an array which was later initialized. (#GH196450)
+- Fixed an issue where Clang incorrectly accepted invalid unqualified uses of local nested class names outside their declaring scope. (#GH184622)
+
+Bug Fixes to AST Handling
+^^^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed a bug where explicit nullability property attributes were not stored in AST nodes in Objective-C. (#GH179703)
+- Fixed a bug where alias CTAD, or an invalid template template parameter, could create a template with an empty template
+  parameter list. This also adds asserts to prevent this from happening again.
+- Fixed a crash when parsing Doxygen ``@param`` commands attached to invalid declarations or non-function entities. (#GH182737)
+- Fixed the SourceLocation and SourceRange of reversed rewritten CXXOperatorCallExpr. (#GH192467)
+- Fixed a assertion when ``__block`` is used on global variables in C mode. (#GH183974)
+
+Miscellaneous Bug Fixes
+^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed the arguments of the format attribute on ``__builtin_os_log_format``.  Previously, they were off by 1.
+
+Miscellaneous Clang Crashes Fixed
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Fixed a crash when attempting to jump over initialization of a variable with variably modified type. (#GH175540)
+- Fixed a crash when using loop hint with a value dependent argument inside a
+  generic lambda. (#GH172289)
+- Fixed a crash in C++ overload resolution with ``_Atomic``-qualified argument types. (#GH170433)
+- Fixed a crash related to missing source locations (#GH186655)
+- Fixed a crash when casting a parenthesized unresolved template-id or array section. (#GH183505)
+- Fixed a crash when initializing a ``constexpr`` pointer with a floating-point literal in C23. (#GH180313)
+- Fixed a lack of diagnostic for substitution failures in base classes when using `std::void_t`-like types.
+- Fixed a crash when emitting debug info for base classes with instantiation-dependent-only types (#GH193932)
+- Fixed an assertion when diagnosing address-space qualified ``new``/``delete`` in language-defined address spaces such as OpenCL ``__local``. (#GH178319)
+- Fixed an assertion failure in ObjC++ ARC when binding a rvalue reference to reference with different lifetimes (#GH178524)
+- Fixed a crash when subscripting a vector type with large unsigned integer values. (#GH180563)
+- Fixed a crash when attempting to diagnose incompatible conversions involving function types (#GH182534)
+- Fixed a crash when evaluating ``__is_bitwise_cloneable`` on invalid record types. (#GH183707)
+- Fixed an assertion failure when casting a function pointer with a target with a non-default program address space. (#GH186210)
+- Fixed a crash when ``decltype(__builtin_FUNCTION())`` is used as a template type argument. (#GH167433)
+- Fixed an assertion failure when parsing an invalid ``decltype`` specifier with missing parentheses or extra semicolons. (#GH188014)
+- Fixed a crash when explicitly casting a complex type to or from an atomic complex type. (#GH172208)
+- Fixed a crash when explicitly casting a scalar to an atomic complex. (#GH114885)
+- Fixed an assertion failure when parsing an invalid out-of-line enum definition with template parameters. (#GH187909)
+- Fixed an assertion failure on invalid template template parameter during typo correction. (#GH183983)
+- Fixed an assertion failure in ``isAtEndOfMacroExpansion`` on macro expansions crossing the boundary of two fileIDs. (#GH115007), (#GH21755)
+
+OpenACC Specific Changes
+------------------------
+
+Target Specific Changes
+-----------------------
+
+AMDGPU Support
+^^^^^^^^^^^^^^
+- Introduced a new target specific builtin ``__builtin_amdgcn_processor_is``,
+  a late / deferred query for the current target processor.
+- Introduced a new target specific builtin ``__builtin_amdgcn_is_invocable``,
+  a late / deferred query for the availability of target specific builtins.
+- Initial support for gfx1310
+- The ``amdgpu_num_sgpr`` and ``amdgpu_num_vgpr`` function attributes are now
+  deprecated. Using them produces a ``-Wdeprecated-declarations`` warning. Use
+  ``amdgpu_waves_per_eu`` instead.
+
+NVPTX Support
+^^^^^^^^^^^^^^
+
+X86 Support
+^^^^^^^^^^^
+- ``march=znver6`` is now supported.
+
+Arm and AArch64 Support
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Android Support
+^^^^^^^^^^^^^^^
+
+Windows Support
+^^^^^^^^^^^^^^^
+
+- Clang now defines the ``_MSVC_TRADITIONAL`` macro as ``1`` when emulating MSVC
+  19.15 (Visual Studio 2017 version 15.8) and later. (#GH47114)
+- ``-fmacro-prefix-map=`` (``-ffile-prefix-map=``) now affects an anonymous namespace hash generation
+  for the MSVC targets and allows deterministic symbol mangling for reproducible builds.
+
+LoongArch Support
+^^^^^^^^^^^^^^^^^
+
+- DWARF fission is now compatible with linker relaxations, allowing `-gsplit-dwarf` and `-mrelax`
+  to be used together when building for the LoongArch platform.
+
+RISC-V Support
+^^^^^^^^^^^^^^
+
+- Tenstorrent Ascalon D8 was renamed to Ascalon X. Use `tt-ascalon-x` with `-mcpu` or `-mtune`.
+- Intrinsics were added for the 'Zvabd` (RISC-V Integer Vector Absolute Difference) extension.
+- Intrinsics were added for the 'Zvzip` (Reordering Structured Data in Vector Registers) extension.
+
+CUDA/HIP Language Changes
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- The new offloading driver is now the default for HIP. Use
+  `--no-oflfoad-new-driver` to return to the old behavior.
+
+CUDA Support
+^^^^^^^^^^^^
+
+AIX Support
+^^^^^^^^^^^
+
+- The driver default for the linker flag `-bcdtors` now defaults to `mbr`
+  (instead of `all`) which only extracts static init from archive members which
+  would otherwise be referenced.
+  (See https://www.ibm.com/docs/en/aix/7.2.0?topic=l-ld-command for details).
+
+NetBSD Support
+^^^^^^^^^^^^^^
+
+WebAssembly Support
+^^^^^^^^^^^^^^^^^^^
+
+- Fixed a crash when ``__funcref`` is applied to a non-function pointer type.
+  (#GH118233)
+
+AVR Support
+^^^^^^^^^^^
+
+SystemZ Support
+^^^^^^^^^^^^^^^
+
+- Add support for `#pragma export` for z/OS.  This is a pragma used to export functions and variables
+  with external linkage from shared libraries.  It provides compatibility with the IBM XL C/C++
+  compiler.
+
+DWARF Support in Clang
+----------------------
+
+Floating Point Support in Clang
+-------------------------------
+
+Fixed Point Support in Clang
+----------------------------
+
+AST Matchers
+------------
+- Add ``functionTypeLoc`` matcher for matching ``FunctionTypeLoc``.
+- Add missing support for ``TraversalKind`` in some ``addMatcher()`` overloads.
+
+clang-format
+------------
+- Add ``ObjCSpaceAfterMethodDeclarationPrefix`` option to control space between the
+  '-'/'+' and the return type in Objective-C method declarations
+- Deprecate the ``BinPackParameters`` and ``BinPackArguments`` options and replace 
+  them with the ``PackParameters`` and ``PackArguments`` structs (respectively) to 
+  unify packing behavior. Add the ``BreakAfter`` option to the structs, allowing 
+  parameter and argument lists to be formatted with one parameter/argument on each 
+  line if they exceed the specified count.
+- Add ``AfterComma`` value to ``BreakConstructorInitializers`` to allow breaking
+  constructor initializers after commas, keeping the colon on the same line.
+- Extend ``BreakBinaryOperations`` to accept a structured configuration with
+  per-operator break rules and minimum chain length gating via ``PerOperator``.
+- Add ``AllowShortRecordOnASingleLine`` option and set it to ``EmptyAndAttached`` for LLVM style.
+- Add ``BreakFunctionDeclarationParameters`` option to always break before function
+  declaration parameters.
+- Add ``EnumAssignments`` option to ``AlignConsecutiveAssignments`` for aligning
+  enum assignments without affecting other assignments.
+- Add ``BreakBeforeReturnType`` option to break before the function return
+  type.
+
+libclang
+--------
+- Visit constraints of `auto` type to properly visit concept usages (#GH166580)
+- Visit switch initializer statements (https://bugs.kde.org/show_bug.cgi?id=415537#c2)
+- Fix crash in clang_getBinaryOperatorKindSpelling and clang_getUnaryOperatorKindSpelling
+- The clang_Module_getASTFile API is deprecated and now always returns nullptr
+
+Code Completion
+---------------
+
+- Fixed a crash in code completion when using a C-Style cast with a parenthesized
+  operand in Objective-C++ mode. (#GH180125)
+
+Static Analyzer
+---------------
+
+Crash and bug fixes
+^^^^^^^^^^^^^^^^^^^
+
+- Fixed ``security.VAList`` checker producing false positives when analyzing
+  C23 code where ``va_start`` expands to ``__builtin_c23_va_start``.
+
+.. comment:
+  This is for the Static Analyzer.
+  Using the caret `^^^` underlining for subsections:
+    - Crash and bug fixes
+    - New checkers and features
+    - Improvements
+    - Moved checkers
+
+.. _release-notes-sanitizers:
+
+Sanitizers
+----------
+- UndefinedBehaviorSanitizer now supports ``__ubsan_default_suppressions``.
+
+Python Binding Changes
+----------------------
+- Add deprecation warnings to ``CompletionChunk.isKind...`` methods.
+  These will be removed in a future release. Existing uses should be adapted
+  to directly compare equality of the ``CompletionChunk`` kind with
+  the corresponding ``CompletionChunkKind`` variant.
+
+  Affected methods: ``isKindOptional``, ``isKindTypedText``, ``isKindPlaceHolder``,
+  ``isKindInformative`` and ``isKindResultType``.
+- Add a deprecation warning to ``CodeCompletionResults.results``.
+  This property will become an implementation detail with changed behavior in a
+  future release and should not be used directly.. Existing uses of
+  ``CodeCompletionResults.results`` should be changed to directly use
+  ``CodeCompletionResults``: it nows supports ``__len__`` and ``__getitem__``,
+  so it can be used the same as ``CodeCompletionResults.results``.
+- Added a new helper method ``get_clang_version`` to the class ``Config`` to
+  read the version string of the libclang in use.
+
+OpenMP Support
+--------------
+- Added support for ``transparent`` clause in task and taskloop directives.
+- Added support for ``use_device_ptr`` clause to accept an optional
+  ``fallback`` modifier (``fb_nullify`` or ``fb_preserve``) with OpenMP >= 61.
+- Added support for ``local`` clause with declare_target directive when
+  OpenMP >= 60.
+
+Improvements
+^^^^^^^^^^^^
+- Improved substitution performance in concept checking. (#GH172266)
+
+- Clang now preserves the left-hand side of a binary expression (such as an
+  assignment or comparison) in a ``RecoveryExpr`` when the right-hand side fails
+  to parse. This improves IDE features like go-to-definition in ``clangd``.
+
+Additional Information
+======================
+
+A wide variety of additional information is available on the `Clang web
+page <https://clang.llvm.org/>`_. The web page contains versions of the
+API documentation which are up-to-date with the Git version of
+the source code. You can access versions of these documents specific to
+this release by going into the "``clang/docs/``" directory in the Clang
+tree.
+
+If you have any questions or comments about Clang, please feel free to
+contact us on the `Discourse forums (Clang Frontend category)
+<https://discourse.llvm.org/c/clang/6>`_.

>From 5f93257f497c37181fa0f14fd3138c7994ae5c77 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Thu, 25 Jun 2026 10:52:16 -0700
Subject: [PATCH 10/12] Add test to ensure that warnings are generated on K&R
 style function definitions without a prototype

---
 clang/test/CodeGen/target-avx-abi-diag-knr.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100644 clang/test/CodeGen/target-avx-abi-diag-knr.c

diff --git a/clang/test/CodeGen/target-avx-abi-diag-knr.c b/clang/test/CodeGen/target-avx-abi-diag-knr.c
new file mode 100644
index 0000000000000..c9fca7d39e042
--- /dev/null
+++ b/clang/test/CodeGen/target-avx-abi-diag-knr.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -std=c17 -verify=no256,knr -o - -S
+// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -std=c17 -target-feature +avx -verify=knr -o - -S
+// REQUIRES: x86-registered-target
+
+typedef short avx256Type __attribute__((vector_size(32)));
+
+// knr-warning at +3 {{a function definition without a prototype is deprecated in all versions of C and is not supported in C23}}
+// no256-warning at +2 {{AVX vector return of type 'avx256Type' (vector of 16 'short' values) without 'avx' enabled changes the ABI}}
+// no256-warning at +2 {{AVX vector argument of type 'avx256Type' (vector of 16 'short' values) without 'avx' enabled changes the ABI}}
+avx256Type knr_def(x)
+  avx256Type x;
+{
+  return x;
+}

>From 2c4de7de501e9d7fbd48d930534d6a181f66101b Mon Sep 17 00:00:00 2001
From: freaknbigpanda <benjamin.luke at sony.com>
Date: Wed, 1 Jul 2026 11:37:14 -0700
Subject: [PATCH 11/12] fixed conflict with upstream

---
 clang/docs/ReleaseNotes.md  |   4 +
 clang/docs/ReleaseNotes.rst | 915 ------------------------------------
 2 files changed, 4 insertions(+), 915 deletions(-)
 delete mode 100644 clang/docs/ReleaseNotes.rst

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index c8b940836a67f..a084024b7a4e4 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -598,6 +598,10 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
 
 - Improved `-Wassign-enum` performance by caching enum enumerator values. (#GH176454)
 
+- Clang now emits `-Wpsabi` diagnostics for externally visible x86-64
+  function definitions that return or take AVX or AVX-512 vector types without
+  enabling the corresponding target feature.
+
 - Fixed a false negative in `-Warray-bounds` where the warning was suppressed
   when accessing a member function on a past-the-end array element.
   (#GH179128)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
deleted file mode 100644
index 80b7880bdc85a..0000000000000
--- a/clang/docs/ReleaseNotes.rst
+++ /dev/null
@@ -1,915 +0,0 @@
-.. If you want to modify sections/contents permanently, you should modify both
-   ReleaseNotes.rst and ReleaseNotesTemplate.txt.
-
-===========================================
-Clang |release| |ReleaseNotesTitle|
-===========================================
-
-.. contents::
-   :local:
-   :depth: 2
-
-Written by the `LLVM Team <https://llvm.org/>`_
-
-.. only:: PreRelease
-
-  .. warning::
-     These are in-progress notes for the upcoming Clang |version| release.
-     Release notes for previous releases can be found on
-     `the Releases Page <https://llvm.org/releases/>`_.
-
-Introduction
-============
-
-This document contains the release notes for the Clang C/C++/Objective-C
-frontend, part of the LLVM Compiler Infrastructure, release |release|. Here we
-describe the status of Clang in some detail, including major
-improvements from the previous release and new feature work. For the
-general LLVM release notes, see `the LLVM
-documentation <https://llvm.org/docs/ReleaseNotes.html>`_. For the libc++ release notes,
-see `this page <https://libcxx.llvm.org/ReleaseNotes.html>`_. All LLVM releases
-may be downloaded from the `LLVM releases web site <https://llvm.org/releases/>`_.
-
-For more information about Clang or LLVM, including information about the
-latest release, please see the `Clang Web Site <https://clang.llvm.org>`_ or the
-`LLVM Web Site <https://llvm.org>`_.
-
-Potentially Breaking Changes
-============================
-
-C/C++ Language Potentially Breaking Changes
--------------------------------------------
-
-- Clang now makes it ill-formed to try to ``break`` out of or ``continue`` a loop inside its own condition,
-  increment, or init-statement in all C and C++ language modes. This means that code such as
-
-  .. code-block:: c++
-
-    while (({ break; })) {}
-
-  is now ill-formed. An outer loop can still be broken out of or continued so long as the inner loop is
-  in the body of the outer loop:
-
-  .. code-block:: c++
-
-    // Ok, this breaks out of the 'for' loop.
-    for (;;) {
-      while (({ break; true; })) {}
-    }
-
-    // Error: can't break out of the 'for' loop from within its own increment.
-    for (;;({ while (({ break; true; })) {} })) {}
-
-  This also resolves a divergence from GCC: in a construct such as
-
-  .. code-block:: c++
-
-    for (;;) {
-      while (({ break; true; })) {}
-    }
-
-  Clang would previously ``break`` out of the ``while`` loop, whereas GCC (since version 9) would
-  ``break`` out of the ``for`` loop here. Now, Clang and GCC both break out of the ``for`` loop.
-
-C++ Specific Potentially Breaking Changes
------------------------------------------
-
-- Clang now more aggressively optimizes away stores to objects after they are
-  dead. This behavior can be disabled with ``-fno-lifetime-dse``.
-
-- Clang now correctly rejects ``export`` declarations in module implementation
-  partitions. (#GH107602)
-
-- Template argument deduction now treats the ``N`` in ``_BitInt(N)``
-  as being of type ``std::size_t`` instead of ``int``,
-  matching the deduction of array sizes from ``int(&)[N]``.
-  This is a breaking change for code that depended on the previously deduced type. (#GH195033)
-
-- Clang now rejects nested local classes defined in a different
-  block scope than their parent class. (#GH193472)
-
-ABI Changes in This Version
----------------------------
-
-- Fixed incorrect struct layout for ``_BitInt`` bitfields wider than 255 bits
-  on MSVC targets. Internal bitfield tracking fields were changed from
-  ``unsigned char`` to ``uint64_t`` to prevent overflow. This might be an ABI
-  break for such structs compared to earlier Clang versions.
-- Fixed a number of issues with the ``__regcall`` calling convention for passing
-  structs on non-Windows x86-64 targets, including a crash when handling empty
-  struct arguments. This changes how structs that contain arrays, floating point
-  types, or ``_Complex float`` types are passed, and may introduce
-  incompatibilities with code compiled by earlier versions of Clang that uses
-  the ``__regcall`` calling convention on these targets. (#GH62999) (#GH98635)
-- Fixed Itanium mangling for lambdas in instantiated non-static data member
-  initializers by preserving the field-name closure-prefix. This changes the
-  mangled names for affected lambdas. (#GH190555)
-
-AST Dumping Potentially Breaking Changes
-----------------------------------------
-
-- The JSON AST dump now includes all fields from ``AvailabilityAttr``: ``platform``,
-  ``introduced``, ``deprecated``, ``obsoleted``, ``unavailable``, ``message``,
-  ``strict``, ``replacement``, ``priority``, and ``environment``. Previously, these
-  fields were missing from the JSON output.
-- Colons that appear at the end of a ParamCommentCommand name are not serialized
-  as part of the name.
-
-Clang Frontend Potentially Breaking Changes
--------------------------------------------
-
-- HIPSPV toolchain: `--offload-targets=spirv{32,64}` option is
-  deprecated and will be removed when the new offload driver becomes
-  default. The replacement for the option is
-  `--offload-targets=spirv{32,64}-unknown-chipstar` when using the new
-  offload driver (`--offload-new-driver`).
-- The new driver (`--offload-new-driver`) is now default for all offloading
-  compilations. This changes the ABI for relocatable device code. Currently,
-  libraries will need to be recompiled, or used with
-  (`--no-offload-new-driver`). This option will be removed in the next release.
-
-- Clang no longer defines the ``__cpp_impl_coroutine`` feature test macro under the 32-bit x86 Microsoft ABI,
-  as support for coroutines on this target is incomplete.
-  When using coroutines on this target a warning is emmitted to indicate the lack of full support.
-  That warning can be disabled with ``-Wno-coroutines-unsupported-target``. (see #GH59382)
-
-Clang Python Bindings Potentially Breaking Changes
---------------------------------------------------
-- Remove ``CompletionString.Availability``. No libclang interfaces returned instances of it.
-- ``CompletionString.availability`` now returns instances of ``CompletionString.AvailabilityKindCompat``.
-
-  Instances of ``AvailabilityKindCompat`` have the same ``__str__`` representation
-  as the previous ``CompletionChunk.Kind`` and are equality-comparable with
-  the existing ``AvailabilityKind`` enum. It will be replaced by ``AvailabilityKind``
-  in a future release. When this happens, the return type of ``CompletionString.availability``
-  will change to ``AvailabilityKind``, so it is recommended to use ``AvailabilityKind``
-  to compare with the return values of ``CompletionString.availability``.
-- Remove ``availabilityKinds``. In this release, uses of ``availabilityKinds``
-  need to be replaced by ``CompletionString.AvailabilityKind``.
-- ``CompletionChunk.kind`` now returns instances of ``CompletionChunkKind``.
-
-  Instances of ``CompletionChunkKind`` have the same ``__str__`` representation
-  as the previous ``CompletionChunk.Kind`` for compatibility.
-  These representations will be changed in a future release to match other enums.
-- Remove ``completionChunkKindMap``. In this release, uses of ``completionChunkKindMap``
-  need to be replaced by ``CompletionChunkKind``.
-- Move ``SPELLING_CACHE`` into ``CompletionChunk`` and change it to use
-  ``CompletionChunkKind`` instances as keys, instead of the enum values.
-  An alias is kept in the form of a ``SPELLING_CACHE`` variable, but it only supports
-  ``__getitem__`` and ``__contains__``. It will be removed in a future release.
-  Please migrate to using ``CompletionChunk.SPELLING_CACHE`` instead.
-- ``SourceLocation`` and ``SourceRange`` now use ``NotImplemented`` to delegate
-  equality checks (``__eq__``) to the other object they are compared with when
-  they are of different classes. They previously returned ``False`` when compared
-  with objects of other classes.
-- ``TranslationUnit.get_tokens`` now throws an error if both the ``extent`` and
-  ``locations`` argument are passed. Previousy, ``locations`` took precedence.
-- ``_CXUnsavedFile`` will be renamed to ``UnsavedFile`` for consistency.
-  ``UnsavedFile`` is already available to use and existing uses should
-  be adapted to refer to it instead. ``_CXUnsavedFile`` will be removed in a
-  future release.
-
-OpenCL Potentially Breaking Changes
------------------------------------
-- Clang now diagnoses zero-length arrays as errors in OpenCL. OpenCL C 3.0
-  section 6.11.d states that "Variable length arrays and structures with
-  flexible (or unsized) arrays are not supported."
-
-What's New in Clang |release|?
-==============================
-
-C++ Language Changes
---------------------
-
-- ``__is_trivially_equality_comparable`` no longer returns false for all enum types. (#GH132672)
-
-C++2c Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-C++23 Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-- Partially implement Itanium mangling for pack indexing. Partially substituted packs are not yet supported. (#GH112003)
-
-C++20 Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-- Clang now supports `P1857R3 <https://wg21.link/p1857r3>`_ Modules Dependency Discovery. (#GH54047)
-
-C++17 Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-Resolutions to C++ Defect Reports
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Implemented `CWG1780 Explicit instantiation/specialization of generic lambda
-  operator() <https://cplusplus.github.io/CWG/issues/1780.html>`_
-
-- Clang now allows omitting ``typename`` before a template name in a
-  conversion operator, implementing `CWG2413 <https://wg21.link/cwg2413>`_.
-
-- Clang now uses non-reference types for structured bindings whose initializer
-  returns a prvalue. This resolves `CWG3135 <https://wg21.link/cwg3135>`_.
-
-C Language Changes
-------------------
-
-C2y Feature Support
-^^^^^^^^^^^^^^^^^^^
-
-- Implemented the type-specific C2y ``<stdbit.h>`` rotate functions with constexpr
-  evaluation support:
-  ``stdc_rotate_left_{uc,us,ui,ul,ull}`` and
-  ``stdc_rotate_right_{uc,us,ui,ul,ull}``.
-
-C23 Feature Support
-^^^^^^^^^^^^^^^^^^^
-- Clang now allows C23 ``constexpr`` struct member access through the dot operator in constant expressions. (#GH178349)
-
-Objective-C Language Changes
------------------------------
-
-- Clang now emits Objective-C number, array, and dictionary literals as
-  compile-time constant data structures rather than runtime ``objc_msgSend``
-  calls on targets whose runtime supports constant literal classes. The
-  feature can be disabled altogether with ``-fno-objc-constant-literals``,
-  or selectively per literal kind with ``-fno-constant-nsnumber-literals``,
-  ``-fno-constant-nsarray-literals``, and
-  ``-fno-constant-nsdictionary-literals``.
-
-Non-comprehensive list of changes in this release
--------------------------------------------------
-
-- Added support for floating point and pointer values in most ``__atomic_``
-  builtins.
-
-- Added ``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right``
-  for bit rotation of unsigned integers including ``_BitInt`` types. Rotation
-  counts are normalized modulo the bit-width and support negative values.
-  Usable in constant expressions. Implicit conversion is supported for
-  class/struct types with conversion operators.
-- Implemented the following C23 ``<stdbit.h>`` builtins with ``_BitInt``
-  support and constexpr evaluation:
-  ``__builtin_stdc_leading_zeros``, ``__builtin_stdc_leading_ones``,
-  ``__builtin_stdc_trailing_zeros``, ``__builtin_stdc_trailing_ones``,
-  ``__builtin_stdc_first_leading_zero``, ``__builtin_stdc_first_leading_one``,
-  ``__builtin_stdc_first_trailing_zero``, ``__builtin_stdc_first_trailing_one``,
-  ``__builtin_stdc_count_zeros``, ``__builtin_stdc_count_ones``,
-  ``__builtin_stdc_has_single_bit``, ``__builtin_stdc_bit_width``,
-  ``__builtin_stdc_bit_floor``, and ``__builtin_stdc_bit_ceil``.
-
-- Implemented the type-specific C23 ``<stdbit.h>`` functions with constexpr
-  evaluation support:
-  ``stdc_leading_zeros_{uc,us,ui,ul,ull}``,
-  ``stdc_leading_ones_{uc,us,ui,ul,ull}``,
-  ``stdc_trailing_zeros_{uc,us,ui,ul,ull}``,
-  ``stdc_trailing_ones_{uc,us,ui,ul,ull}``,
-  ``stdc_first_leading_zero_{uc,us,ui,ul,ull}``,
-  ``stdc_first_leading_one_{uc,us,ui,ul,ull}``,
-  ``stdc_first_trailing_zero_{uc,us,ui,ul,ull}``,
-  ``stdc_first_trailing_one_{uc,us,ui,ul,ull}``,
-  ``stdc_count_zeros_{uc,us,ui,ul,ull}``,
-  ``stdc_count_ones_{uc,us,ui,ul,ull}``,
-  ``stdc_has_single_bit_{uc,us,ui,ul,ull}``,
-  ``stdc_bit_width_{uc,us,ui,ul,ull}``,
-  ``stdc_bit_floor_{uc,us,ui,ul,ull}``, and
-  ``stdc_bit_ceil_{uc,us,ui,ul,ull}``.
-
-- A new generic bit-reverse builtin function ``__builtin_bitreverseg`` that
-  extends bit-reversal support to all standard integers type, including
-  ``_BitInt``
-
-- Deprecated float types support from ``__builtin_elementwise_max`` and
-  ``__builtin_elementwise_min``.
-
-- Added header ``endian.h`` which contains byte order helpers specified in POSIX
-
-- Added #pragma loop licm(disable) for llvm.loop.licm.disable metadata
-
-- Added a new ``ExplicitInstantiationDecl`` AST node to represent explicit
-  template instantiations (e.g., ``template void foo<int>();`` or
-  ``extern template class S<int>;``). Previously, source location information
-  for explicit instantiation statements was discarded after parsing. The new
-  node preserves the full source range including the ``extern`` and ``template``
-  keywords, qualifiers, template arguments as written, and the declared type,
-  enabling tools such as language servers and refactoring engines to accurately
-  map source locations back to explicit instantiation sites.
-
-- ``typeid`` on references and pointers of ``final`` types no longer emits a
-  vtable lookup at runtime.
-
-- Updated support for Unicode from 15.1 to 18.0.
-
-New Compiler Flags
-------------------
-- New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added
-  to enable or disable Microsoft's anonymous struct/union extension without
-  enabling other ``-fms-extensions`` features (#GH177607).
-- New option ``--precompile-reduced-bmi`` allows build system to generate a
-  reduced BMI only for a C++20 importable module unit. Previously the users
-  can only generate the reduced BMI as a by-product, e.g, an object files or
-  a full BMI.
-
-- New ``-cc1`` option ``-fexperimental-overflow-behavior-types`` added to
-  enable parsing of the experimental ``overflow_behavior`` type attribute and
-  type specifiers.
-
-- New ``-cl`` option ``/d2guardnochecks`` added to match MSVC. When Windows
-  Control Flow Guard (CFG) is enabled by other options, it will instruct Clang
-  to emit the CFG metadata, but disable adding checks.
-
-- New option ``-fdiagnostics-show-inlining-chain`` added to show inlining chain
-  notes for ``[[gnu::warning]]`` and ``[[gnu::error]]`` diagnostics. When a
-  function with these attributes is called from an inlined context, Clang can
-  now show which functions were inlined to reach the call. When debug info is
-  available (``-gline-directives-only`` (implicitly enabled at ``-g1``) or
-  higher), accurate source locations are used; otherwise, a heuristic fallback
-  is used with a note suggesting how to enable debug info for better accuracy.
-
-- New option ``-fwin-cfg-mechanism=`` added to control the mechanism used by
-  Control Flow Guard on Windows. Accepted values are ``automatic`` (default),
-  ``dispatch``, and ``check``. The ``dispatch`` mechanism uses the dispatch
-  function to perform indirect call checks and can improve performance, while
-  ``check`` uses the traditional check mechanism.
-- New ``-cl`` option ``/d2guardcfgdispatch`` added to match MSVC. This acts as a
-  shorthand for ``-fwin-cfg-mechanism=dispatch``.
-- New ``-cl`` option ``/d2guardcfgdispatch-`` added to match MSVC. This acts as a
-  shorthand for ``-fwin-cfg-mechanism=check``.
-
-- New option ``-f[no-]strict-bool`` added to control whether Clang can assume
-  that ``bool`` values loaded from memory cannot have a bit pattern other
-  than 0 or 1.
-
-Deprecated Compiler Flags
--------------------------
-
-Modified Compiler Flags
------------------------
-- The `-mno-outline` and `-moutline` compiler flags are now allowed on RISC-V and X86, which both support the machine outliner.
-- The `-mno-outline` flag will now add the `nooutline` IR attribute, so that
-  `-mno-outline` and `-moutline` objects can be mixed correctly during LTO.
-
-Removed Compiler Flags
-----------------------
-
-Attribute Changes in Clang
---------------------------
-
-- Added new attribute ``stack_protector_ignore`` to opt specific local variables out of
-  the analysis which determines if a function should get a stack protector.  A function
-  will still generate a stack protector if other local variables or command line flags
-  require it.
-
-- Added a new attribute, ``[[clang::no_outline]]`` to suppress outlining from
-  annotated functions. This uses the LLVM `nooutline` attribute.
-
-- Introduced a new type attribute ``__attribute__((overflow_behavior))`` which
-  currently accepts either ``wrap`` or ``trap`` as an argument, enabling
-  type-level control over overflow behavior. There is also an accompanying type
-  specifier for each behavior kind via `__ob_wrap` and `__ob_trap`.
-
-- Introduced a new function attribute ``__attribute__((__personality__(...)))``
-  to explicitly specify the personality routine for exception handling. THis is
-  meant to be a low level tool for language runtime authors to associate a
-  foreign language personality with a given function. Note that this does not
-  perform any ABI validation for the personality routine.
-
-- :doc:`ThreadSafetyAnalysis` attributes now correctly handle implicit member
-  accesses in C, and parameter attributes in C++. This improves diagnostic
-  precision and fixes false positives.
-
-- The :doc:`ThreadSafetyAnalysis` attributes ``guarded_by`` and
-  ``pt_guarded_by`` now accept multiple capability arguments with refined
-  access semantics: *writing* requires all listed capabilities to be held
-  exclusively, while *reading* requires at least one to be held.  This is
-  sound because any writer must hold all capabilities, so holding any one
-  prevents concurrent writes.
-
-- :doc:`ThreadSafetyAnalysis` attributes like ``acquire_capability``,
-  ``release_capability``, ``requires_capability``, ``locks_excluded``,
-  ``try_acquire_capability``, and ``assert_capability`` can now be applied to
-  function pointer variables and fields.  The analysis checks calls through
-  annotated function pointers the same way it checks direct function calls.
-  Only plain function pointers are supported; pointers-to-member functions,
-  blocks, or wrappers (e.g. ``std::function``) are not yet supported.
-
-- The ``[[clang::unsafe_buffer_usage]]`` attribute is now supported in API
-  notes. For example:
-  
-  .. code-block:: yaml
-
-    Functions:
-      - Name: myUnsafeFunction
-        UnsafeBufferUsage: true
-
-- Added support for ``[[msvc::forceinline]]`` for functions and
-  ``[[msvc::forceinline_calls]]`` for statements. Both are aliases to
-  ``[[clang::always_inline]]`` with additional checks to ensure that they
-  are only accepted in places where MSVC also does.
-
-- The AMDGPU ``amdgpu_num_sgpr`` and ``amdgpu_num_vgpr`` attributes are now
-  deprecated. Clang emits a ``-Wdeprecated-declarations`` warning when they
-  are used. Use ``amdgpu_waves_per_eu`` instead to control SGPR and VGPR
-  usage.
-
-- Clang now allows GNU attributes between a member declarator and bit-field width. (#GH184954)
-
-Improvements to Clang's diagnostics
------------------------------------
-- Fixed bug in ``-Wdocumentation`` so that it correctly handles explicit
-  function template instantiations (#64087).
-
-- ``-Wunused-but-set-variable`` now diagnoses file-scope variables with
-  internal linkage (``static`` storage class) that are assigned but never used.
-  This new coverage is added under the subgroup ``-Wunused-but-set-global``,
-  allowing it to be disabled independently with ``-Wno-unused-but-set-global``.
-  (#GH148361)
-
-- Added ``-Wlifetime-safety`` to enable lifetime safety analysis,
-  a CFG-based intra-procedural analysis that detects use-after-free and related
-  temporal safety bugs. See the
-  `RFC <https://discourse.llvm.org/t/rfc-intra-procedural-lifetime-analysis-in-clang/86291>`_
-  for more details. By design, this warning is enabled in ``-Weverything``. To disable
-  the analysis, use ``-Wno-lifetime-safety`` or ``-fno-lifetime-safety``.
-
-- Added ``-Wlifetime-safety-suggestions`` to enable lifetime annotation suggestions.
-  This provides suggestions for function parameters that
-  should be marked ``[[clang::lifetimebound]]`` based on lifetime analysis. For
-  example, for the following function:
-
-  .. code-block:: c++
-
-    int* p(int *in) { return in; }
-
-  Clang will suggest:
-
-  .. code-block:: c++
-
-    warning: parameter in intra-TU function should be marked [[clang::lifetimebound]]
-    int* p(int *in) { return in; }
-           ^~~~~~~
-                   [[clang::lifetimebound]]
-    note: param returned here
-    int* p(int *in) { return in; }
-                             ^~
-
-- Added ``-Wlifetime-safety-noescape`` to detect misuse of ``[[clang::noescape]]``
-  annotation where the parameter escapes through return. For example:
-
-  .. code-block:: c++
-
-    int* p(int *in [[clang::noescape]]) { return in; }
-
-  Clang will warn:
-
-  .. code-block:: c++
-
-    warning: parameter is marked [[clang::noescape]] but escapes
-    int* p(int *in [[clang::noescape]]) { return in; }
-           ^~~~~~~
-    note: returned here
-    int* p(int *in [[clang::noescape]]) { return in; }
-                                                 ^~
-
-- Added ``-Wlifetime-safety-dangling-field`` to detect dangling field references
-  when stack memory escapes to class fields. This is part of ``-Wlifetime-safety``
-  and detects cases where local variables or parameters are stored in fields but
-  outlive their scope. For example:
-
-  .. code-block:: c++
-
-    struct DanglingView {
-      std::string_view view;
-      DanglingView(std::string s) : view(s) {}  // warning: address of stack memory escapes to a field
-    };
-
-- Improved ``-Wassign-enum`` performance by caching enum enumerator values. (#GH176454)
-
-- Clang now emits ``-Wpsabi`` diagnostics for externally visible x86-64
-  function definitions that return or take AVX or AVX-512 vector types without
-  enabling the corresponding target feature.
-
-- Fixed a false negative in ``-Warray-bounds`` where the warning was suppressed
-  when accessing a member function on a past-the-end array element.
-  (#GH179128)
-
-- Added a missing space to the FixIt for the ``implicit-int`` group of diagnostics and
-  made sure that only one such diagnostic and FixIt is emitted per declaration group. (#GH179354)
-
-- Fixed the Fix-It insertion point for ``expected ';' after alias declaration``
-  when parsing alias declarations involving a token-split ``>>`` sequence
-  (for example, ``using A = X<int>>;``). (#GH184425)
-
-- Fixed incorrect ``implicitly deleted`` diagnostic for explicitly deleted
-  candidate function. (#GH185693)
-
-- The ``-Wloop-analysis`` warning has been extended to catch more cases of
-  variable modification inside lambda expressions (#GH132038).
-
-- Clang now emits ``-Wsizeof-pointer-memaccess`` when snprintf/vsnprintf use the sizeof
-  the destination buffer(dynamically allocated) in the len parameter(#GH162366)
-
-- Added ``-Wmodule-map-path-outside-directory`` (off by default) to warn on
-  header and umbrella directory paths that use ``..`` to refer outside the module
-  directory in module maps found via implicit search
-  (``-fimplicit-module-maps``). This does not affect module maps specified
-  explicitly via ``-fmodule-map-file=``.
-
-- Honour ``[[maybe_unused]]`` attribute on private fields.
-  ``-Wunused-private-field`` no longer emits a warning for annotated private
-  fields.
-
-- Improved ``-Wgnu-zero-variadic-macro-arguments`` to suggest using
-  ``__VA_OPT__`` if the current language version supports it(#GH188624)
-
-- Clang now emits an error when implicitly casting a complex type to a built-in vector type. (#GH186805)
-
-- Added ``-Wnonportable-include-path-separator`` (off by default) to catch
-  #include directives that use backslashes as a path separator. The warning
-  includes a FixIt to change all the backslashes to forward slashes, so that the
-  code can automatically be made portable to other host platforms that don't
-  support backslashes.
-
-- Clang now explains why template deduction fails for explicit template arguments.
-
-- No longer emitting a ``-Wpre-c2y-compat`` or extension diagnostic about use
-  of octal literals with a ``0o`` prefix, and no longer emitting a
-  ``-Wdeprecated-octal-literals`` diagnostic for use of octal literals without
-  a ``0o`` prefix, when the literal is expanded from a macro defined in a
-  system header. (#GH192389)
-
-- Improved error recovery for missing semicolons after class members. Clang now avoids
-  skipping subsequent valid declarations when their previous decl is missing semicolon.
-
-- Removed the body of lambdas from some diagnostic messages.
-
-- Fixed false positive host-device mismatch errors in discarded `if constexpr` branches for CUDA/HIP;
-  such calls are now correctly skipped.
-
-- Clang now errors when a function declaration aliases a variable or vice versa. (#GH195550)
-
-- Added ``-Wattribute-alias`` to diagnose type mismatches between an alias and its aliased function. (#GH195550)
-
-- The diagnostics around ``__block`` now explain why a variable cannot be marked ``__block``. (#GH197213)
-
-- Extended ``-Wnonportable-include-path`` to warn about trailing whitespace and dots in ``#include`` paths. (#GH190610)
-
-- Clang now emits error when attribute is missing closing ``]]`` followed by ``;;``. (#GH187223)
-
-Improvements to Clang's time-trace
-----------------------------------
-
-Improvements to Coverage Mapping
---------------------------------
-
-- [MC/DC] Nested expressions are handled as individual MC/DC expressions.
-- "Single byte coverage" now supports branch coverage and can be used
-  together with ``-fcoverage-mcdc``.
-- Consteval member functions are no longer emitted in coverage mappings,
-  matching the existing behavior for free consteval functions. (#GH164448)
-
-Bug Fixes in This Version
--------------------------
-
-- Fixed atomic boolean compound assignment; the conversion back to atomic bool would be miscompiled. (#GH33210)
-- Correctly handle default template argument when establishing subsumption. (#GH188640)
-- Fixed a failed assertion in the preprocessor when ``__has_embed`` parameters are missing parentheses. (#GH175088)
-- Fix lifetime extension of temporaries in for-range-initializers in templates. (#GH165182)
-- Fixed a preprocessor crash in ``__has_cpp_attribute`` on incomplete scoped attributes. (#GH178098)
-- Fixes an assertion failure when evaluating ``__underlying_type`` on enum redeclarations. (#GH177943)
-- Fixed an assertion failure caused by nested macro expansion during header-name lexing (``__has_embed(__has_include)``). (#GH178635)
-- Clang now outputs relative paths of embeds for dependency output. (#GH161950)
-- Fix the result type of a binary operation where both operands are 'void' l-values. (#GH111300)
-- Fixed an assertion failure when evaluating ``_Countof`` on invalid ``void``-typed operands. (#GH180893)
-- Fixed an assertion failure in the serialized diagnostic printer when it is destroyed without calling ``finish()``. (#GH140433)
-- Fixed an assertion failure caused by error recovery while extending a nested name specifier with results from ordinary lookup. (#GH181470)
-- Fixed a crash when parsing ``#pragma clang attribute`` arguments for attributes that forbid arguments. (#GH182122)
-- Fixed a bug with multiple-include optimization (MIOpt) state not being preserved in some cases during lexing, which could suppress header-guard mismatch diagnostics and interfere with include-guard optimization. (#GH180155)
-- Fixed a crash when normalizing constraints involving concept template parameters whose index coincided with non-concept template parameters in the same parameter mapping.
-- Fixed a crash caused by accessing dependent diagnostics of a non-dependent context.
-- Fixed a crash when substituting into a non-type template parameter that has a type containing an undeduced placeholder type.
-- Fixed several crashes and improved diagnostics when a multidimensional subscript operator is applied to a built-in type. (#GH187800)
-- Correctly diagnosing and no longer crashing when ``export module foo``
-  (without a semicolon) are the final tokens in a module file. (#GH187771)
-- Fixed a crash in duplicate attribute checking caused by comparing constant arguments with different integer signedness. (#GH188259)
-- Clang now emits an error when returning an initializer list from a lambda
-  with an explicit return type of void. The diagnostic now correctly refers
-  to "lambda" instead of "block". (#GH188661)
-- Fixed a crash on _BitInt(N) arrays where 129 ≤ N ≤ 192 due to incorrect array filler lowering. (#GH189643)
-- Fixed the behavior in C23 of ``auto``, by emitting an error when an array type is specified for a ``char *``. (#GH162694)
-- Fixed an issue where an assert was thrown instead of an error if no vulkan env was specified with ``--triple spirv``. (#GH189964)
-- Fixed incorrect rejection of ``auto`` with reordered declaration specifiers in C23. (#GH164121)
-- Fixed a crash where constexpr evaluation encountered invalid overrides. (#GH183290)
-- Fixed a bug where Clang fails to find instantiation of Decls in constraint checking. (#GH173086)
-- Fixed a crash when assigning to an element of an ``ext_vector_type`` with ``bool`` element type. (#GH189260)
-- Fixed a crash caused by declaring multiple ``ownership_returns`` attributes with mismatched or missing arguments. (#GH188733)
-- Clang now emits an error for friend declarations of lambda members. (#GH26540)
-- Fixed a crash caused by lambda capture handling in delayed default arguments. (#GH176534)
-- Fixed a crash when parsing invalid ``static_assert`` declarations with string-literal messages (#GH187690).
-- Fixed a potential stack-use-after-return issue in Clang when copy-initializing
-  an array via an element-at-a-time copy loop (#GH192026)
-- Fixed an issue where certain designated initializers would be rejected for constexpr variables. (#GH193373)
-- Fixed a crash when ``#embed`` is used with C++ modules (#GH195350)
-
-Bug Fixes to Compiler Builtins
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Fix a crash when passing an unresolved overload set to ``__builtin_classify_type``. (#GH175589)
-- Fixed a crash when calling `__builtin_allow_sanitize_check` with no arguments. (#GH183927)
-- ``__annotation`` is now diagnosed as unsupported on non-Windows/UEFI targets, fixing a
-  crash when using it with ``-fms-extensions`` on other platforms. (#GH184318)
-
-Bug Fixes to Attribute Support
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Fixed a behavioral discrepancy between deleted functions and private members when checking the ``enable_if`` attribute. (#GH175895)
-- Fixed ``init_priority`` attribute by delaying type checks until after the type is deduced.
-
-Bug Fixes to C++ Support
-^^^^^^^^^^^^^^^^^^^^^^^^
-- Fixed a preprocessor assertion failure triggered when parsing an invalid template-id starting with ``::template operator``. (#GH186582)
-- Fixed a crash when a function template is defined as a non-template friend with a global scope qualifier. (#GH185341)
-- Clang now rejects constant template parameters with block pointer types, since these are not implemented anyway and would lead to crashes. (#GH189247)
-- Fixed a crash on error recovery when dealing with invalid templates. (#GH183075)
-- Fixed a crash when instantiating ``requires`` expressions involving substitution failures in C++ concepts. (#GH176402)
-- Concepts appearing in the require-clause of a member function no longer have access to non-public members of that class,
-  or to a current class object. (#GH115838) (#GH194803) (#GH197067)
-- We no longer caches invalid variable specializations. (#GH132592)
-- Fixed an incorrect template argument deduction when matching packs of template
-  template parameters when one of its parameters is also a pack. (#GH181166)
-- Clang no longer errors on overloads with different ref-qualifiers and constraints. (#GH120812)
-- Fixed a crash when a default argument is passed to an explicit object parameter. (#GH176639)
-- Fixed an alias template CTAD crash.
-- Correctly diagnose uses of ``co_await`` / ``co_yield`` in the default argument of nested function declarations. (#GH98923)
-- Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741)
-- Clang incorrectly instantiated variable specializations outside of the immediate context. (#GH54439)
-- Fixed a crash when pack expansions are used as arguments for non-pack parameters of built-in templates. (#GH180307)
-- Fix a problem where a substitution failure when evaluating a type requirement
-  could directly make the program ill-formed.
-- Fix a problem where pack index expressions where incorrectly being regarded as equivalent.
-- Fixed a bug where captured variables in non-mutable lambdas were incorrectly treated as mutable
-  when used inside decltype in the return type. (#GH180460)
-- Fixed a crash when evaluating uninitialized GCC vector/ext_vector_type vectors in ``constexpr``. (#GH180044)
-- Fixed a crash when `explicit(bool)` is used with an incomplete enumeration. (#GH183887)
-- Fixed a crash on ``typeid`` of incomplete local types during template instantiation. (#GH63242), (#GH176397)
-- Fixed spurious diagnostics produced when checking if constraints are equivalent for redeclarations,
-  which could make the program mistakenly ill-formed.
-- Fixed a crash when an immediate-invoked ``consteval`` lambda is used as an invalid initializer. (#GH185270)
-- Fixed an assertion failure when using a global destructor with a target with a non-default program address space. (#GH186484)
-- Fixed a crash when instantiating an invalid out-of-line static data member definition in a local class. (#GH176152)
-- Inherited constructors in ``dllexport`` classes are now exported for ABI-compatible cases, matching
-  MSVC behavior. Constructors with variadic arguments or callee-cleanup parameters are not yet supported
-  and produce a warning. (#GH162640)
-- Correctly diagnose invalid non-dependent calls in dependent contexts. (#GH135694)
-- Fix initialization of GRO when GRO-return type mismatches, as part of CWG2563. (#GH98744)
-- Fix an error using an initializer list with array new for a type that is not default-constructible. (#GH81157)
-- We no longer consider conversion operators when copy-initializing from the same type. This was non
-  conforming and could lead to recursive constraint satisfaction checking. (#GH149443)
-- Fixed a crash in Itanium C++ name mangling for a lambda in a local class field initializer inside a constructor/destructor. (#GH176395)
-- Fixed crashes in Itanium C++ name mangling for lambdas with trailing requires-clauses involving requires-expressions. (#GH100774) (#GH123854)
-- Fixed an invalid rejection and assertion failure while generating ``operator=`` for fields with the ``__restrict`` qualifier. (#GH37979)
-- Fixed a use-after-free bug when parsing default arguments containing lambdas in declarations with template-id declarators. (#GH196725)
-- Fixed a crash in constant evaluation using placement new on an array which was later initialized. (#GH196450)
-- Fixed an issue where Clang incorrectly accepted invalid unqualified uses of local nested class names outside their declaring scope. (#GH184622)
-
-Bug Fixes to AST Handling
-^^^^^^^^^^^^^^^^^^^^^^^^^
-- Fixed a bug where explicit nullability property attributes were not stored in AST nodes in Objective-C. (#GH179703)
-- Fixed a bug where alias CTAD, or an invalid template template parameter, could create a template with an empty template
-  parameter list. This also adds asserts to prevent this from happening again.
-- Fixed a crash when parsing Doxygen ``@param`` commands attached to invalid declarations or non-function entities. (#GH182737)
-- Fixed the SourceLocation and SourceRange of reversed rewritten CXXOperatorCallExpr. (#GH192467)
-- Fixed a assertion when ``__block`` is used on global variables in C mode. (#GH183974)
-
-Miscellaneous Bug Fixes
-^^^^^^^^^^^^^^^^^^^^^^^
-- Fixed the arguments of the format attribute on ``__builtin_os_log_format``.  Previously, they were off by 1.
-
-Miscellaneous Clang Crashes Fixed
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-- Fixed a crash when attempting to jump over initialization of a variable with variably modified type. (#GH175540)
-- Fixed a crash when using loop hint with a value dependent argument inside a
-  generic lambda. (#GH172289)
-- Fixed a crash in C++ overload resolution with ``_Atomic``-qualified argument types. (#GH170433)
-- Fixed a crash related to missing source locations (#GH186655)
-- Fixed a crash when casting a parenthesized unresolved template-id or array section. (#GH183505)
-- Fixed a crash when initializing a ``constexpr`` pointer with a floating-point literal in C23. (#GH180313)
-- Fixed a lack of diagnostic for substitution failures in base classes when using `std::void_t`-like types.
-- Fixed a crash when emitting debug info for base classes with instantiation-dependent-only types (#GH193932)
-- Fixed an assertion when diagnosing address-space qualified ``new``/``delete`` in language-defined address spaces such as OpenCL ``__local``. (#GH178319)
-- Fixed an assertion failure in ObjC++ ARC when binding a rvalue reference to reference with different lifetimes (#GH178524)
-- Fixed a crash when subscripting a vector type with large unsigned integer values. (#GH180563)
-- Fixed a crash when attempting to diagnose incompatible conversions involving function types (#GH182534)
-- Fixed a crash when evaluating ``__is_bitwise_cloneable`` on invalid record types. (#GH183707)
-- Fixed an assertion failure when casting a function pointer with a target with a non-default program address space. (#GH186210)
-- Fixed a crash when ``decltype(__builtin_FUNCTION())`` is used as a template type argument. (#GH167433)
-- Fixed an assertion failure when parsing an invalid ``decltype`` specifier with missing parentheses or extra semicolons. (#GH188014)
-- Fixed a crash when explicitly casting a complex type to or from an atomic complex type. (#GH172208)
-- Fixed a crash when explicitly casting a scalar to an atomic complex. (#GH114885)
-- Fixed an assertion failure when parsing an invalid out-of-line enum definition with template parameters. (#GH187909)
-- Fixed an assertion failure on invalid template template parameter during typo correction. (#GH183983)
-- Fixed an assertion failure in ``isAtEndOfMacroExpansion`` on macro expansions crossing the boundary of two fileIDs. (#GH115007), (#GH21755)
-
-OpenACC Specific Changes
-------------------------
-
-Target Specific Changes
------------------------
-
-AMDGPU Support
-^^^^^^^^^^^^^^
-- Introduced a new target specific builtin ``__builtin_amdgcn_processor_is``,
-  a late / deferred query for the current target processor.
-- Introduced a new target specific builtin ``__builtin_amdgcn_is_invocable``,
-  a late / deferred query for the availability of target specific builtins.
-- Initial support for gfx1310
-- The ``amdgpu_num_sgpr`` and ``amdgpu_num_vgpr`` function attributes are now
-  deprecated. Using them produces a ``-Wdeprecated-declarations`` warning. Use
-  ``amdgpu_waves_per_eu`` instead.
-
-NVPTX Support
-^^^^^^^^^^^^^^
-
-X86 Support
-^^^^^^^^^^^
-- ``march=znver6`` is now supported.
-
-Arm and AArch64 Support
-^^^^^^^^^^^^^^^^^^^^^^^
-
-Android Support
-^^^^^^^^^^^^^^^
-
-Windows Support
-^^^^^^^^^^^^^^^
-
-- Clang now defines the ``_MSVC_TRADITIONAL`` macro as ``1`` when emulating MSVC
-  19.15 (Visual Studio 2017 version 15.8) and later. (#GH47114)
-- ``-fmacro-prefix-map=`` (``-ffile-prefix-map=``) now affects an anonymous namespace hash generation
-  for the MSVC targets and allows deterministic symbol mangling for reproducible builds.
-
-LoongArch Support
-^^^^^^^^^^^^^^^^^
-
-- DWARF fission is now compatible with linker relaxations, allowing `-gsplit-dwarf` and `-mrelax`
-  to be used together when building for the LoongArch platform.
-
-RISC-V Support
-^^^^^^^^^^^^^^
-
-- Tenstorrent Ascalon D8 was renamed to Ascalon X. Use `tt-ascalon-x` with `-mcpu` or `-mtune`.
-- Intrinsics were added for the 'Zvabd` (RISC-V Integer Vector Absolute Difference) extension.
-- Intrinsics were added for the 'Zvzip` (Reordering Structured Data in Vector Registers) extension.
-
-CUDA/HIP Language Changes
-^^^^^^^^^^^^^^^^^^^^^^^^^
-
-- The new offloading driver is now the default for HIP. Use
-  `--no-oflfoad-new-driver` to return to the old behavior.
-
-CUDA Support
-^^^^^^^^^^^^
-
-AIX Support
-^^^^^^^^^^^
-
-- The driver default for the linker flag `-bcdtors` now defaults to `mbr`
-  (instead of `all`) which only extracts static init from archive members which
-  would otherwise be referenced.
-  (See https://www.ibm.com/docs/en/aix/7.2.0?topic=l-ld-command for details).
-
-NetBSD Support
-^^^^^^^^^^^^^^
-
-WebAssembly Support
-^^^^^^^^^^^^^^^^^^^
-
-- Fixed a crash when ``__funcref`` is applied to a non-function pointer type.
-  (#GH118233)
-
-AVR Support
-^^^^^^^^^^^
-
-SystemZ Support
-^^^^^^^^^^^^^^^
-
-- Add support for `#pragma export` for z/OS.  This is a pragma used to export functions and variables
-  with external linkage from shared libraries.  It provides compatibility with the IBM XL C/C++
-  compiler.
-
-DWARF Support in Clang
-----------------------
-
-Floating Point Support in Clang
--------------------------------
-
-Fixed Point Support in Clang
-----------------------------
-
-AST Matchers
-------------
-- Add ``functionTypeLoc`` matcher for matching ``FunctionTypeLoc``.
-- Add missing support for ``TraversalKind`` in some ``addMatcher()`` overloads.
-
-clang-format
-------------
-- Add ``ObjCSpaceAfterMethodDeclarationPrefix`` option to control space between the
-  '-'/'+' and the return type in Objective-C method declarations
-- Deprecate the ``BinPackParameters`` and ``BinPackArguments`` options and replace 
-  them with the ``PackParameters`` and ``PackArguments`` structs (respectively) to 
-  unify packing behavior. Add the ``BreakAfter`` option to the structs, allowing 
-  parameter and argument lists to be formatted with one parameter/argument on each 
-  line if they exceed the specified count.
-- Add ``AfterComma`` value to ``BreakConstructorInitializers`` to allow breaking
-  constructor initializers after commas, keeping the colon on the same line.
-- Extend ``BreakBinaryOperations`` to accept a structured configuration with
-  per-operator break rules and minimum chain length gating via ``PerOperator``.
-- Add ``AllowShortRecordOnASingleLine`` option and set it to ``EmptyAndAttached`` for LLVM style.
-- Add ``BreakFunctionDeclarationParameters`` option to always break before function
-  declaration parameters.
-- Add ``EnumAssignments`` option to ``AlignConsecutiveAssignments`` for aligning
-  enum assignments without affecting other assignments.
-- Add ``BreakBeforeReturnType`` option to break before the function return
-  type.
-
-libclang
---------
-- Visit constraints of `auto` type to properly visit concept usages (#GH166580)
-- Visit switch initializer statements (https://bugs.kde.org/show_bug.cgi?id=415537#c2)
-- Fix crash in clang_getBinaryOperatorKindSpelling and clang_getUnaryOperatorKindSpelling
-- The clang_Module_getASTFile API is deprecated and now always returns nullptr
-
-Code Completion
----------------
-
-- Fixed a crash in code completion when using a C-Style cast with a parenthesized
-  operand in Objective-C++ mode. (#GH180125)
-
-Static Analyzer
----------------
-
-Crash and bug fixes
-^^^^^^^^^^^^^^^^^^^
-
-- Fixed ``security.VAList`` checker producing false positives when analyzing
-  C23 code where ``va_start`` expands to ``__builtin_c23_va_start``.
-
-.. comment:
-  This is for the Static Analyzer.
-  Using the caret `^^^` underlining for subsections:
-    - Crash and bug fixes
-    - New checkers and features
-    - Improvements
-    - Moved checkers
-
-.. _release-notes-sanitizers:
-
-Sanitizers
-----------
-- UndefinedBehaviorSanitizer now supports ``__ubsan_default_suppressions``.
-
-Python Binding Changes
-----------------------
-- Add deprecation warnings to ``CompletionChunk.isKind...`` methods.
-  These will be removed in a future release. Existing uses should be adapted
-  to directly compare equality of the ``CompletionChunk`` kind with
-  the corresponding ``CompletionChunkKind`` variant.
-
-  Affected methods: ``isKindOptional``, ``isKindTypedText``, ``isKindPlaceHolder``,
-  ``isKindInformative`` and ``isKindResultType``.
-- Add a deprecation warning to ``CodeCompletionResults.results``.
-  This property will become an implementation detail with changed behavior in a
-  future release and should not be used directly.. Existing uses of
-  ``CodeCompletionResults.results`` should be changed to directly use
-  ``CodeCompletionResults``: it nows supports ``__len__`` and ``__getitem__``,
-  so it can be used the same as ``CodeCompletionResults.results``.
-- Added a new helper method ``get_clang_version`` to the class ``Config`` to
-  read the version string of the libclang in use.
-
-OpenMP Support
---------------
-- Added support for ``transparent`` clause in task and taskloop directives.
-- Added support for ``use_device_ptr`` clause to accept an optional
-  ``fallback`` modifier (``fb_nullify`` or ``fb_preserve``) with OpenMP >= 61.
-- Added support for ``local`` clause with declare_target directive when
-  OpenMP >= 60.
-
-Improvements
-^^^^^^^^^^^^
-- Improved substitution performance in concept checking. (#GH172266)
-
-- Clang now preserves the left-hand side of a binary expression (such as an
-  assignment or comparison) in a ``RecoveryExpr`` when the right-hand side fails
-  to parse. This improves IDE features like go-to-definition in ``clangd``.
-
-Additional Information
-======================
-
-A wide variety of additional information is available on the `Clang web
-page <https://clang.llvm.org/>`_. The web page contains versions of the
-API documentation which are up-to-date with the Git version of
-the source code. You can access versions of these documents specific to
-this release by going into the "``clang/docs/``" directory in the Clang
-tree.
-
-If you have any questions or comments about Clang, please feel free to
-contact us on the `Discourse forums (Clang Frontend category)
-<https://discourse.llvm.org/c/clang/6>`_.

>From a8e78f29173a4c762d7a9134297fa1bc6e25243e Mon Sep 17 00:00:00 2001
From: freaknbigpanda <benjamin.luke at sony.com>
Date: Wed, 1 Jul 2026 11:58:32 -0700
Subject: [PATCH 12/12] fixed bad merge

---
 libcxx/include/__algorithm/simd_utils.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index 5b87f13e5bb01..495aed4e21216 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -125,7 +125,6 @@ template <class _VecT, size_t _Np, class _Iter>
     return _VecT{__iter[_LoadIndices]..., ((void)_ZeroIndices, 0)...};
   }(make_index_sequence<_Np>{}, make_index_sequence<__simd_vector_size_v<_VecT> - _Np>{});
 }
-_LIBCPP_DIAGNOSTIC_POP
 
 template <class _Tp, size_t _Np>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __any_of(__simd_vector<_Tp, _Np> __vec) noexcept {


From libcxx-commits at lists.llvm.org  Wed Jul  1 12:50:10 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 01 Jul 2026 12:50:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::replace_copy and std::replace_copy_if (PR #207070)
Message-ID: <llvm.org/llvm/llvm-project/pull/207070@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/207070

Assisted by Claude

>From 59b8ac362ede7e106203ef43c453dc877e276369 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 14:18:54 -0400
Subject: [PATCH] [libc++] Add benchmarks for std::replace_copy and
 std::replace_copy_if

Assisted by Claude
---
 .../modifying/replace_copy.bench.cpp          | 129 ++++++++++++++++++
 1 file changed, 129 insertions(+)
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp

diff --git a/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp
new file mode 100644
index 0000000000000..97117fc7ffec2
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_replace_copy = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
+    return std::replace_copy(first, last, out, old_value, new_value);
+  };
+  auto std_replace_copy_if = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
+    return std::replace_copy_if(
+        first,
+        last,
+        out,
+        [&](auto element) {
+          benchmark::DoNotOptimize(element);
+          return element == old_value;
+        },
+        new_value);
+  };
+
+  // Benchmark {std,ranges}::{replace_copy,replace_copy_if} on a sequence of the form xxxxxxxxxxyyyyyyyyyy
+  // where we replace the prefix of x's with z's.
+  {
+    auto bm = []<class Container>(std::string name, auto replace_copy) {
+      benchmark::RegisterBenchmark(
+          name,
+          [replace_copy](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            Container c;
+            ValueType x = Generate<ValueType>::random();
+            ValueType y = random_different_from({x});
+            ValueType z = random_different_from({x, y});
+            std::fill_n(std::back_inserter(c), size / 2, x);
+            std::fill_n(std::back_inserter(c), size / 2, y);
+
+            std::vector<ValueType> out(size);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(out);
+              benchmark::DoNotOptimize(x);
+              benchmark::DoNotOptimize(z);
+              auto result = replace_copy(c.begin(), c.end(), out.begin(), x, z);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(32)
+          ->Arg(50) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::replace_copy
+    bm.operator()<std::vector<int>>("std::replace_copy(vector<int>) (prefix)", std_replace_copy);
+    bm.operator()<std::deque<int>>("std::replace_copy(deque<int>) (prefix)", std_replace_copy);
+    bm.operator()<std::list<int>>("std::replace_copy(list<int>) (prefix)", std_replace_copy);
+
+    // {std,ranges}::replace_copy_if
+    bm.operator()<std::vector<int>>("std::replace_copy_if(vector<int>) (prefix)", std_replace_copy_if);
+    bm.operator()<std::deque<int>>("std::replace_copy_if(deque<int>) (prefix)", std_replace_copy_if);
+    bm.operator()<std::list<int>>("std::replace_copy_if(list<int>) (prefix)", std_replace_copy_if);
+  }
+
+  // Benchmark {std,ranges}::{replace_copy,replace_copy_if} on a sequence of the form xyxyxyxyxyxyxyxyxyxy
+  // where we replace the x's with z's.
+  {
+    auto bm = []<class Container>(std::string name, auto replace_copy) {
+      benchmark::RegisterBenchmark(
+          name,
+          [replace_copy](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            Container c;
+            ValueType x = Generate<ValueType>::random();
+            ValueType y = random_different_from({x});
+            ValueType z = random_different_from({x, y});
+            for (std::size_t i = 0; i != size; ++i) {
+              c.push_back(i % 2 == 0 ? x : y);
+            }
+
+            std::vector<ValueType> out(size);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(out);
+              benchmark::DoNotOptimize(x);
+              benchmark::DoNotOptimize(z);
+              auto result = replace_copy(c.begin(), c.end(), out.begin(), x, z);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(32)
+          ->Arg(50) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::replace_copy
+    bm.operator()<std::vector<int>>("std::replace_copy(vector<int>) (sprinkled)", std_replace_copy);
+    bm.operator()<std::deque<int>>("std::replace_copy(deque<int>) (sprinkled)", std_replace_copy);
+    bm.operator()<std::list<int>>("std::replace_copy(list<int>) (sprinkled)", std_replace_copy);
+
+    // {std,ranges}::replace_copy_if
+    bm.operator()<std::vector<int>>("std::replace_copy_if(vector<int>) (sprinkled)", std_replace_copy_if);
+    bm.operator()<std::deque<int>>("std::replace_copy_if(deque<int>) (sprinkled)", std_replace_copy_if);
+    bm.operator()<std::list<int>>("std::replace_copy_if(list<int>) (sprinkled)", std_replace_copy_if);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Wed Jul  1 12:50:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 12:50:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::replace_copy and std::replace_copy_if (PR #207070)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207070@github.com>
Message-ID: <6a456f97.bf38ae28.21951a.f7cc@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

Assisted by Claude

---
Full diff: https://github.com/llvm/llvm-project/pull/207070.diff


1 Files Affected:

- (added) libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp (+129) 


``````````diff
diff --git a/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp
new file mode 100644
index 0000000000000..97117fc7ffec2
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_replace_copy = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
+    return std::replace_copy(first, last, out, old_value, new_value);
+  };
+  auto std_replace_copy_if = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
+    return std::replace_copy_if(
+        first,
+        last,
+        out,
+        [&](auto element) {
+          benchmark::DoNotOptimize(element);
+          return element == old_value;
+        },
+        new_value);
+  };
+
+  // Benchmark {std,ranges}::{replace_copy,replace_copy_if} on a sequence of the form xxxxxxxxxxyyyyyyyyyy
+  // where we replace the prefix of x's with z's.
+  {
+    auto bm = []<class Container>(std::string name, auto replace_copy) {
+      benchmark::RegisterBenchmark(
+          name,
+          [replace_copy](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            Container c;
+            ValueType x = Generate<ValueType>::random();
+            ValueType y = random_different_from({x});
+            ValueType z = random_different_from({x, y});
+            std::fill_n(std::back_inserter(c), size / 2, x);
+            std::fill_n(std::back_inserter(c), size / 2, y);
+
+            std::vector<ValueType> out(size);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(out);
+              benchmark::DoNotOptimize(x);
+              benchmark::DoNotOptimize(z);
+              auto result = replace_copy(c.begin(), c.end(), out.begin(), x, z);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(32)
+          ->Arg(50) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::replace_copy
+    bm.operator()<std::vector<int>>("std::replace_copy(vector<int>) (prefix)", std_replace_copy);
+    bm.operator()<std::deque<int>>("std::replace_copy(deque<int>) (prefix)", std_replace_copy);
+    bm.operator()<std::list<int>>("std::replace_copy(list<int>) (prefix)", std_replace_copy);
+
+    // {std,ranges}::replace_copy_if
+    bm.operator()<std::vector<int>>("std::replace_copy_if(vector<int>) (prefix)", std_replace_copy_if);
+    bm.operator()<std::deque<int>>("std::replace_copy_if(deque<int>) (prefix)", std_replace_copy_if);
+    bm.operator()<std::list<int>>("std::replace_copy_if(list<int>) (prefix)", std_replace_copy_if);
+  }
+
+  // Benchmark {std,ranges}::{replace_copy,replace_copy_if} on a sequence of the form xyxyxyxyxyxyxyxyxyxy
+  // where we replace the x's with z's.
+  {
+    auto bm = []<class Container>(std::string name, auto replace_copy) {
+      benchmark::RegisterBenchmark(
+          name,
+          [replace_copy](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            Container c;
+            ValueType x = Generate<ValueType>::random();
+            ValueType y = random_different_from({x});
+            ValueType z = random_different_from({x, y});
+            for (std::size_t i = 0; i != size; ++i) {
+              c.push_back(i % 2 == 0 ? x : y);
+            }
+
+            std::vector<ValueType> out(size);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(out);
+              benchmark::DoNotOptimize(x);
+              benchmark::DoNotOptimize(z);
+              auto result = replace_copy(c.begin(), c.end(), out.begin(), x, z);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(32)
+          ->Arg(50) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::replace_copy
+    bm.operator()<std::vector<int>>("std::replace_copy(vector<int>) (sprinkled)", std_replace_copy);
+    bm.operator()<std::deque<int>>("std::replace_copy(deque<int>) (sprinkled)", std_replace_copy);
+    bm.operator()<std::list<int>>("std::replace_copy(list<int>) (sprinkled)", std_replace_copy);
+
+    // {std,ranges}::replace_copy_if
+    bm.operator()<std::vector<int>>("std::replace_copy_if(vector<int>) (sprinkled)", std_replace_copy_if);
+    bm.operator()<std::deque<int>>("std::replace_copy_if(deque<int>) (sprinkled)", std_replace_copy_if);
+    bm.operator()<std::list<int>>("std::replace_copy_if(list<int>) (sprinkled)", std_replace_copy_if);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/207070

From libcxx-commits at lists.llvm.org  Wed Jul  1 14:49:39 2026
From: libcxx-commits at lists.llvm.org (Alexey Samsonov via libcxx-commits)
Date: Wed, 01 Jul 2026 14:49:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][libc] update LLVM-libc test
	status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a458b73.abb10101.208f3e.dbe1@mx.google.com>


================
@@ -32,22 +32,11 @@ set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
 
 # Not yet fully implemented.
----------------
vonosmas wrote:

Remove the comment

https://github.com/llvm/llvm-project/pull/201236

From libcxx-commits at lists.llvm.org  Wed Jul  1 14:49:39 2026
From: libcxx-commits at lists.llvm.org (Alexey Samsonov via libcxx-commits)
Date: Wed, 01 Jul 2026 14:49:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][libc] update LLVM-libc test
	status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a458b73.d5797644.1b5bca.27b1@mx.google.com>


================
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include-dir} -I %{target-include-dir} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx-dir}/test/support -idirafter @LIBC_KERNEL_HEADERS@'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a'
+    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -latomic -Wl,--end-group -Wl,--defsym,__dso_handle=0'
----------------
vonosmas wrote:

```
  # | /usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/13/libatomic.a(gstore.o): in function `libat_store':
  # | (.text+0x127): undefined reference to `__memcpy_chk'
  # | clang++-23: error: linker command failed with exit code 1 (use -v to see invocation)
```

Looks like there are some issues from using GCC version of libatomic (I suspect, pulled by `-latomic`). I wonder why do you need this, instead of using atomic functionality from compiler-rt builtins? Here you link with the builtins for x86 explicitly, but wouldn't smth like `--rtlib=compiler-rt` to Clang driver achieve the same result?

https://github.com/llvm/llvm-project/pull/201236

From libcxx-commits at lists.llvm.org  Wed Jul  1 15:56:51 2026
From: libcxx-commits at lists.llvm.org (Benjamin Luke via libcxx-commits)
Date: Wed, 01 Jul 2026 15:56:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Move threading and random device
 config into <__configuration/platform.h> (PR #206262)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206262@github.com>
Message-ID: <6a459b33.331483e7.5bc1e.7fa8@mx.google.com>


freaknbigpanda wrote:

I think this caused a bunch of failures on windows builds see my PR which is rebased ontop of this here: https://github.com/llvm/llvm-project/pull/199091

https://github.com/llvm/llvm-project/pull/206262

From libcxx-commits at lists.llvm.org  Wed Jul  1 16:46:39 2026
From: libcxx-commits at lists.llvm.org (Alexey Samsonov via libcxx-commits)
Date: Wed, 01 Jul 2026 16:46:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][libc] update LLVM-libc test
	status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a45a6df.13be6df8.3d8759.ad40@mx.google.com>


vonosmas wrote:

(to expand on the last comment - I'm pretty sure we should somehow avoid adding `-latomic` to linker invocations for llvm-libc config, as we shouldn't depend / pull-in this GCC library . I don't know if there's an easy way to force-disable this workaround feature that would add `-latomic` explicitly).

https://github.com/llvm/llvm-project/pull/201236

From libcxx-commits at lists.llvm.org  Wed Jul  1 17:03:49 2026
From: libcxx-commits at lists.llvm.org (Michael Jones via libcxx-commits)
Date: Wed, 01 Jul 2026 17:03:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][libc] update LLVM-libc test
	status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a45aae5.7d41f3a1.d1cf7.20d4@mx.google.com>


michaelrj-google wrote:

> Please elaborate? Do you mean that if you _don't_ explicitly add `-latomic` then: (a) `has-1024-bit-atomics` feature check fails because of issue with the Clang driver not linking atomics by default (#73361)? (b) libc++ thinks `has-1024-bit-atomics` is False, and expects tests with 1024-bit atomics to fail. (c) when we actually run such tests, `add-latomic-workaround` feature is activated, adds `-latomic` to the linker flags, and the test works.
> 
> If that's the case, can we just `["-latomic"]` linker flag addition to the `has-64-bit-atomics` and `has-1024-bit-atomics` checks in the test config?
> 
> Why is it related to llvm-libc setup, and doesn't fire elsewhere?

That's correct as far as I understand. We could add `["-latomic"]` to the other checks, as one possible solution.

I'd guess it doesn't cause problems elsewhere because systems either have libatomic available and always want it, or don't have it at all.

https://github.com/llvm/llvm-project/pull/201236

From libcxx-commits at lists.llvm.org  Wed Jul  1 17:58:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 17:58:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a45b7af.ff037943.3bf0e2.bfad@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/19] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/19] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/19] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/19] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/19] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/19] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/19] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/19] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/19] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/19] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/19] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/19] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/19] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/19] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/19] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/19] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/19] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/19] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/19] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();


From libcxx-commits at lists.llvm.org  Wed Jul  1 17:59:02 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 17:59:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a45b7d6.ac469dc7.e3aa6.932c@mx.google.com>


================
@@ -0,0 +1,103 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
----------------
inquisitivecrystal wrote:

Done!

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Wed Jul  1 17:59:13 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 17:59:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a45b7e1.cbc3f03a.3be201.8062@mx.google.com>


================
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
----------------
inquisitivecrystal wrote:

You're all good! I'm still learning my way around the codebase and its conventions, so having something like this pointed out is very helpful. I hadn't paid a lot of attention to those comments previously. This did end up identifying some cases where I was inconsistent with the local conventions of the subdirectories, so I'd say it was pretty useful.

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Wed Jul  1 18:04:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 18:04:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a45b90a.70920dee.3514b2.7274@mx.google.com>


================
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
----------------
inquisitivecrystal wrote:

I've added `[[nodiscard]]` tests for ranges that have them for other member functions. `adjacent_view`, `adjacent_transform_view`, and `elements_view` don't have any `[[nodiscard]]` tests. That seems like an oversight, but adding a set is probably out of scope.

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Wed Jul  1 18:06:31 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 18:06:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a45b997.92ea0e2a.21f97.6c2f@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Wed Jul  1 20:30:32 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 01 Jul 2026 20:30:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a45db58.dd2b4bd3.35cd5e.1214@mx.google.com>


================
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
----------------
Zingam wrote:

We are working on adding `[[nodiscard]]`  to the library. There few views in `<ranges>` that have not been done yet. 
https://github.com/llvm/llvm-project/issues/172124
 Feel free to add test just for reserve_hint.
The adjacent_view patch was merged and the others should follow soon.

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Wed Jul  1 20:30:53 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 01 Jul 2026 20:30:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a45db6d.abb10101.208f3e.bf36@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Wed Jul  1 20:33:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 20:33:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a45dc11.eb165e5c.113c2a.f664@mx.google.com>


https://github.com/Kim-J-Smith edited https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Wed Jul  1 21:18:35 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 01 Jul 2026 21:18:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a45e69b.492e23e4.e71ca.eb56@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Wed Jul  1 22:04:54 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 01 Jul 2026 22:04:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a45f176.d5797644.1b5bca.a1b2@mx.google.com>


Zingam wrote:

It looks like you need to enable this CPO test: https://github.com/llvm/llvm-project/blob/631cb35f6eefbea40b450ddeb480eb114dbc1b9e/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp#L85

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Wed Jul  1 22:16:51 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 01 Jul 2026 22:16:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
Message-ID: <llvm.org/llvm/llvm-project/pull/207120@github.com>


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

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.zip.transform

Towards #172124

>From b000ef2650ce3656f562bf8663fd9deef15a3201 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 2 Jul 2026 08:16:28 +0300
Subject: [PATCH] [libc++][ranges] Applied `[[nodiscard]]` to
 `views::zip_transform`

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.zip.transform

Towards #172124
---
 libcxx/include/__ranges/zip_transform_view.h  | 32 +++----
 .../range.zip.transform/nodiscard.verify.cpp  | 88 +++++++++++++++++++
 2 files changed, 104 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
index c56632a2608a1..71849105d40f6 100644
--- a/libcxx/include/__ranges/zip_transform_view.h
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -79,15 +79,15 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views)
       : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     return __iterator<true>(*this, __zip_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __zip_.end());
     } else {
@@ -95,7 +95,7 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -105,13 +105,13 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __zip_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __zip_.size();
@@ -184,7 +184,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) {
     return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_));
   }
@@ -233,7 +233,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -255,25 +255,25 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -307,14 +307,14 @@ class zip_transform_view<_Fn, _Views...>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -327,14 +327,14 @@ struct __fn {
   template <class _Fn>
     requires(move_constructible<decay_t<_Fn>> && regular_invocable<decay_t<_Fn>&> &&
              is_object_v<invoke_result_t<decay_t<_Fn>&>>)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
       noexcept(noexcept(auto(views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>))) {
     return views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>;
   }
 
   template <class _Fn, class... _Ranges>
     requires(sizeof...(_Ranges) > 0)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
       noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)))
           -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) {
     return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..9c8673c60b1dd
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.zip_transform.sentinel]
+
+  auto st = ztv.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // [range.zip_transform.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform(Invocable<>{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform([](int x) { return x; }, range);
+}
\ No newline at end of file


From libcxx-commits at lists.llvm.org  Wed Jul  1 22:18:31 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 22:18:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a45f4a7.331483e7.5bc1e.b42a@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions cpp,h -- libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp libcxx/include/__ranges/zip_transform_view.h --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
index 71849105d..ffa3e9f9c 100644
--- a/libcxx/include/__ranges/zip_transform_view.h
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -273,7 +273,8 @@ public:
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 9c8673c60..7d15d027f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -52,7 +52,7 @@ void test() {
 
   // [range.zip_transform.iterator]
 
-  auto it = ztv.begin();
+  auto it  = ztv.begin();
   auto cIt = std::as_const(ztv).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}

``````````

</details>


https://github.com/llvm/llvm-project/pull/207120

From libcxx-commits at lists.llvm.org  Wed Jul  1 22:59:11 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 01 Jul 2026 22:59:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Enable CPO compile tests
	(PR #207123)
Message-ID: <llvm.org/llvm/llvm-project/pull/207123@github.com>


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

None

>From 9c0786b1bf196f332c01675bbb89349169c9b666 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 2 Jul 2026 08:57:55 +0300
Subject: [PATCH] [libc++][ranges] Enable CPO compile tests

`adjacent_transform_view` and `stride_view` were implemented but the test cases were omitted.
---
 .../customization.point.object/cpo.compile.pass.cpp           | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..71a0b6ebb804c 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -120,7 +120,7 @@ static_assert(test(std::views::transform, a, [](int x) { return x + 1; }));
 static_assert(test(std::views::values, pairs));
 
 #if TEST_STD_VER >= 23
-// static_assert(test(std::views::adjacent_transform<2>, [](int x, int y) { return x + y; }, a));
+static_assert(test(std::views::adjacent_transform<2>, a, [](int x, int y) { return x + y; }));
 static_assert(test(std::views::adjacent<2>, a));
 // static_assert(test(std::views::as_const, a));
 static_assert(test(std::views::as_rvalue, a));
@@ -130,7 +130,7 @@ static_assert(test(std::views::chunk_by, a, [](int x, int y) { return x < y; }))
 static_assert(test(std::views::enumerate, a));
 static_assert(test(std::views::join_with, 1));
 static_assert(test(std::views::pairwise, a));
-// static_assert(test(std::views::stride, a, 1));
+static_assert(test(std::views::stride, a, 1));
 static_assert(test(std::views::zip_transform, [](int x, int y) { return x + y; }, a, a));
 static_assert(test(std::views::zip, a, a));
 #endif


From libcxx-commits at lists.llvm.org  Wed Jul  1 22:59:49 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 01 Jul 2026 22:59:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Enable CPO compile tests
	(PR #207123)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207123@github.com>
Message-ID: <6a45fe55.6cfa33d2.19518c.d9ce@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/207123.diff


1 Files Affected:

- (modified) libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp (+2-2) 


``````````diff
diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..71a0b6ebb804c 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -120,7 +120,7 @@ static_assert(test(std::views::transform, a, [](int x) { return x + 1; }));
 static_assert(test(std::views::values, pairs));
 
 #if TEST_STD_VER >= 23
-// static_assert(test(std::views::adjacent_transform<2>, [](int x, int y) { return x + y; }, a));
+static_assert(test(std::views::adjacent_transform<2>, a, [](int x, int y) { return x + y; }));
 static_assert(test(std::views::adjacent<2>, a));
 // static_assert(test(std::views::as_const, a));
 static_assert(test(std::views::as_rvalue, a));
@@ -130,7 +130,7 @@ static_assert(test(std::views::chunk_by, a, [](int x, int y) { return x < y; }))
 static_assert(test(std::views::enumerate, a));
 static_assert(test(std::views::join_with, 1));
 static_assert(test(std::views::pairwise, a));
-// static_assert(test(std::views::stride, a, 1));
+static_assert(test(std::views::stride, a, 1));
 static_assert(test(std::views::zip_transform, [](int x, int y) { return x + y; }, a, a));
 static_assert(test(std::views::zip, a, a));
 #endif

``````````

</details>


https://github.com/llvm/llvm-project/pull/207123

From libcxx-commits at lists.llvm.org  Wed Jul  1 23:00:03 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 01 Jul 2026 23:00:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Enable CPO compile tests
	(PR #207123)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207123@github.com>
Message-ID: <6a45fe63.e9d484b9.11ad58.f964@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/207123

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:05:52 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 00:05:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove _LIBCPP_COMPRESSED_PAIR
 from packaged_task (PR #200642)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/200642@github.com>
Message-ID: <6a460dd0.da8bafd3.1eab2b.247f@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/200642

>From 9034abd33ffa79a52a25ab9f1e2ef745c9a05e44 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 31 May 2026 09:59:17 +0200
Subject: [PATCH] [libc++] Remove _LIBCPP_COMPRESSED_PAIR from packaged_task

---
 libcxx/include/future                         | 11 ++--
 .../futures.task/destruction_order.pass.cpp   | 58 +++++++++++++++++++
 2 files changed, 64 insertions(+), 5 deletions(-)
 create mode 100644 libcxx/test/std/thread/futures/futures.task/destruction_order.pass.cpp

diff --git a/libcxx/include/future b/libcxx/include/future
index c169c6c182d83..30ceb97785811 100644
--- a/libcxx/include/future
+++ b/libcxx/include/future
@@ -379,7 +379,6 @@ template <class R, class Alloc> struct uses_allocator<packaged_task<R>, Alloc>;
 #    include <__memory/allocator_arg_t.h>
 #    include <__memory/allocator_destructor.h>
 #    include <__memory/allocator_traits.h>
-#    include <__memory/compressed_pair.h>
 #    include <__memory/pointer_traits.h>
 #    include <__memory/shared_count.h>
 #    include <__memory/unique_ptr.h>
@@ -1413,8 +1412,10 @@ template <class _FD, class _Alloc, class _FB>
 class __packaged_task_func;
 
 template <class _Fp, class _Alloc, class _Rp, class... _ArgTypes>
-class __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)> : public __packaged_task_base<_Rp(_ArgTypes...)> {
-  _LIBCPP_COMPRESSED_PAIR(_Fp, __func_, _Alloc, __alloc_);
+class _LIBCPP_HIDE_STRUCT_FROM_ABI
+    __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)> : public __packaged_task_base<_Rp(_ArgTypes...)> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _Fp __func_;
+  _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_;
 
 public:
   _LIBCPP_HIDE_FROM_ABI explicit __packaged_task_func(const _Fp& __f) : __func_(__f) {}
@@ -1435,8 +1436,8 @@ void __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::__move_to(
 
 template <class _Fp, class _Alloc, class _Rp, class... _ArgTypes>
 void __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::destroy() {
-  __func_.~_Fp();
   __alloc_.~_Alloc();
+  __func_.~_Fp();
 }
 
 template <class _Fp, class _Alloc, class _Rp, class... _ArgTypes>
@@ -1445,8 +1446,8 @@ void __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::destroy_deallocate()
   typedef allocator_traits<_Ap> _ATraits;
   typedef pointer_traits<typename _ATraits::pointer> _PTraits;
   _Ap __a(__alloc_);
-  __func_.~_Fp();
   __alloc_.~_Alloc();
+  __func_.~_Fp();
   __a.deallocate(_PTraits::pointer_to(*this), 1);
 }
 
diff --git a/libcxx/test/std/thread/futures/futures.task/destruction_order.pass.cpp b/libcxx/test/std/thread/futures/futures.task/destruction_order.pass.cpp
new file mode 100644
index 0000000000000..ebe821045ad57
--- /dev/null
+++ b/libcxx/test/std/thread/futures/futures.task/destruction_order.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
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+//
+// REQUIRES: c++11 || c++14
+//
+// <future>
+//
+// Ensure that packaged_task destroys its members in the correct order.
+
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+#include <future>
+
+#include "test_macros.h"
+
+template <int Align, int BufferSize>
+struct alignas(Align) CallableWithPadding {
+  CallableWithPadding() {} // Allow putting object into the tail padding
+  CallableWithPadding(const CallableWithPadding&) {}
+  CallableWithPadding& operator=(const CallableWithPadding&) { return *this; }
+  ~CallableWithPadding() { std::memset(static_cast<void*>(this), 0, sizeof(*this)); }
+
+  void operator()() {}
+
+  char buffer[BufferSize];
+};
+
+template <class T>
+struct AllocatorWithData {
+  using value_type = T;
+
+  AllocatorWithData() : data(123) {}
+  AllocatorWithData(const AllocatorWithData& other) : data(other.data) {}
+  AllocatorWithData& operator=(const AllocatorWithData& other) { data = other.data; }
+  ~AllocatorWithData() { assert(data == 123); }
+
+  template <class U>
+  AllocatorWithData(const AllocatorWithData<U>& other) : data(other.data) {}
+
+  uint64_t data;
+
+  T* allocate(size_t n) { return std::allocator<T>().allocate(n); }
+  void deallocate(T* ptr, size_t n) { std::allocator<T>().deallocate(ptr, n); }
+};
+
+int main(int, char**) {
+  std::packaged_task<void()> heap(std::allocator_arg, AllocatorWithData<int>{}, CallableWithPadding<32, 24>{});
+  std::packaged_task<void()> stack(std::allocator_arg, AllocatorWithData<int>{}, CallableWithPadding<16, 8>{});
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Thu Jul  2 00:07:10 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 00:07:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove _LIBCPP_COMPRESSED_PAIR
 from packaged_task (PR #200642)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/200642@github.com>
Message-ID: <6a460e1e.bf38ae28.21951a.79a7@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/200642

>From fd92caf1090c9b8d1a6b3ee2f4d4dae8bf31ce17 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 31 May 2026 09:59:17 +0200
Subject: [PATCH] [libc++] Remove _LIBCPP_COMPRESSED_PAIR from packaged_task

---
 libcxx/include/future                         | 11 ++--
 .../futures.task/destruction_order.pass.cpp   | 58 +++++++++++++++++++
 2 files changed, 64 insertions(+), 5 deletions(-)
 create mode 100644 libcxx/test/std/thread/futures/futures.task/destruction_order.pass.cpp

diff --git a/libcxx/include/future b/libcxx/include/future
index c169c6c182d83..30ceb97785811 100644
--- a/libcxx/include/future
+++ b/libcxx/include/future
@@ -379,7 +379,6 @@ template <class R, class Alloc> struct uses_allocator<packaged_task<R>, Alloc>;
 #    include <__memory/allocator_arg_t.h>
 #    include <__memory/allocator_destructor.h>
 #    include <__memory/allocator_traits.h>
-#    include <__memory/compressed_pair.h>
 #    include <__memory/pointer_traits.h>
 #    include <__memory/shared_count.h>
 #    include <__memory/unique_ptr.h>
@@ -1413,8 +1412,10 @@ template <class _FD, class _Alloc, class _FB>
 class __packaged_task_func;
 
 template <class _Fp, class _Alloc, class _Rp, class... _ArgTypes>
-class __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)> : public __packaged_task_base<_Rp(_ArgTypes...)> {
-  _LIBCPP_COMPRESSED_PAIR(_Fp, __func_, _Alloc, __alloc_);
+class _LIBCPP_HIDE_STRUCT_FROM_ABI
+    __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)> : public __packaged_task_base<_Rp(_ArgTypes...)> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _Fp __func_;
+  _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_;
 
 public:
   _LIBCPP_HIDE_FROM_ABI explicit __packaged_task_func(const _Fp& __f) : __func_(__f) {}
@@ -1435,8 +1436,8 @@ void __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::__move_to(
 
 template <class _Fp, class _Alloc, class _Rp, class... _ArgTypes>
 void __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::destroy() {
-  __func_.~_Fp();
   __alloc_.~_Alloc();
+  __func_.~_Fp();
 }
 
 template <class _Fp, class _Alloc, class _Rp, class... _ArgTypes>
@@ -1445,8 +1446,8 @@ void __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::destroy_deallocate()
   typedef allocator_traits<_Ap> _ATraits;
   typedef pointer_traits<typename _ATraits::pointer> _PTraits;
   _Ap __a(__alloc_);
-  __func_.~_Fp();
   __alloc_.~_Alloc();
+  __func_.~_Fp();
   __a.deallocate(_PTraits::pointer_to(*this), 1);
 }
 
diff --git a/libcxx/test/std/thread/futures/futures.task/destruction_order.pass.cpp b/libcxx/test/std/thread/futures/futures.task/destruction_order.pass.cpp
new file mode 100644
index 0000000000000..93941e3691297
--- /dev/null
+++ b/libcxx/test/std/thread/futures/futures.task/destruction_order.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
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+//
+// REQUIRES: c++03 || c++11 || c++14
+//
+// <future>
+//
+// Ensure that packaged_task destroys its members in the correct order.
+
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+#include <future>
+
+#include "test_macros.h"
+
+template <int Align, int BufferSize>
+struct TEST_ALIGNAS(Align) CallableWithPadding {
+  CallableWithPadding() {} // Allow putting object into the tail padding
+  CallableWithPadding(const CallableWithPadding&) {}
+  CallableWithPadding& operator=(const CallableWithPadding&) { return *this; }
+  ~CallableWithPadding() { std::memset(static_cast<void*>(this), 0, sizeof(*this)); }
+
+  void operator()() {}
+
+  char buffer[BufferSize];
+};
+
+template <class T>
+struct AllocatorWithData {
+  using value_type = T;
+
+  AllocatorWithData() : data(123) {}
+  AllocatorWithData(const AllocatorWithData& other) : data(other.data) {}
+  AllocatorWithData& operator=(const AllocatorWithData& other) { data = other.data; }
+  ~AllocatorWithData() { assert(data == 123); }
+
+  template <class U>
+  AllocatorWithData(const AllocatorWithData<U>& other) : data(other.data) {}
+
+  uint64_t data;
+
+  T* allocate(size_t n) { return std::allocator<T>().allocate(n); }
+  void deallocate(T* ptr, size_t n) { std::allocator<T>().deallocate(ptr, n); }
+};
+
+int main(int, char**) {
+  std::packaged_task<void()> heap((std::allocator_arg_t(), AllocatorWithData<int>(), CallableWithPadding<32, 24>()));
+  std::packaged_task<void()> stack((std::allocator_arg_t(), AllocatorWithData<int>(), CallableWithPadding<16, 8>()));
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Thu Jul  2 00:14:57 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Thu, 02 Jul 2026 00:14:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a460ff1.eb24c6ea.1f2e26.ce73@mx.google.com>


https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/206679

>From 3a7a5558f04b56dff65ed4c5447f434d82df9b12 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 01:22:27 -0700
Subject: [PATCH 1/3] [libc++] Make std::reference_constructs_from_temporary
 SFINAE friendly when the 1st template argument is not a reference type

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/docs/ReleaseNotes/23.rst               |  2 ++
 .../reference_constructs_from_temporary.h     | 32 ++++++++++++++++---
 ...ference_constructs_from_temporary.pass.cpp |  7 ++++
 3 files changed, 36 insertions(+), 5 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 37aa972d22ea3..5c3280746b9d1 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -69,6 +69,8 @@ Improvements and New Features
   ``"std::visit: variant is valueless"``, ``"std::get: variant is valueless"``, or
   ``"std::get: wrong alternative for variant"``. The standard only requires ``what()`` to return an
   unspecified non-null string, so user code that does not match on the exact message remains correct.
+- The ``std::reference_constructs_from_temporary<_Tp, _Up>`` don't cause hard error when the ``_Tp`` template
+  argument is not a reference type.
 
 Deprecations and Removals
 -------------------------
diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index a8325620414ea..1050952bccf80 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -11,6 +11,7 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_reference.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -18,21 +19,42 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 2300
+
+// A non-reference type can never bind to a temporary, so the result is always `false` for such a
+// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
+// eagerly instantiates the construction of `_Up` (including the element's constructor exception
+// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
+//
+// https://godbolt.org/z/4xz1ozKev
+//
+// TODO: Clang 23 fix this builtin, remove this guard once all supported clang versions include this fix.
+template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
+inline const bool __reference_constructs_from_temporary_v = false;
+
+template <class _Tp, class _Up>
+inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
+    __reference_constructs_from_temporary(_Tp, _Up);
+
+#else
+
+template <class _Tp, class _Up>
+inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
+
+#endif // defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 2300
+
 #if _LIBCPP_STD_VER >= 23
 
 template <class _Tp, class _Up>
 struct _LIBCPP_NO_SPECIALIZATIONS reference_constructs_from_temporary
-    : public bool_constant<__reference_constructs_from_temporary(_Tp, _Up)> {};
+    : public bool_constant<__reference_constructs_from_temporary_v<_Tp, _Up>> {};
 
 template <class _Tp, class _Up>
 _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_temporary_v =
-    __reference_constructs_from_temporary(_Tp, _Up);
+    __reference_constructs_from_temporary_v<_Tp, _Up>;
 
 #endif
 
-template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
-
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___TYPE_TRAITS_REFERENCE_CONSTRUCTS_FROM_TEMPORARY_H
diff --git a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp
index 84fe7cfb02208..f20632b840703 100644
--- a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp
+++ b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp
@@ -22,6 +22,9 @@
 #include "common.h"
 #include "test_macros.h"
 
+struct NoConv {};
+struct Bad { template<class T> Bad(T v) noexcept(noexcept(member_ = v)) {} int member_; };
+
 template <typename T, typename U, bool Expected>
 constexpr void test_reference_constructs_from_temporary() {
   assert((std::reference_constructs_from_temporary<T, U>::value == Expected));
@@ -76,6 +79,10 @@ constexpr bool test() {
   test_reference_constructs_from_temporary<const int&, ExplicitConversionRef, false>();
   test_reference_constructs_from_temporary<int&&, ExplicitConversionRvalueRef, false>();
 
+  // Make sure we don't emit "assigning to 'int' from incompatible type 'NoConv'" in SFINAE context.
+  // https://godbolt.org/z/er6e4Ejs1
+  test_reference_constructs_from_temporary<Bad, NoConv&&, false>();
+
   return true;
 }
 

>From aaac050224cb2820eb5984a880a649f427e40cdd Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 01:39:55 -0700
Subject: [PATCH 2/3] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../reference_constructs_from_temporary.pass.cpp            | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp
index f20632b840703..e83f507f5bda3 100644
--- a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp
+++ b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp
@@ -23,7 +23,11 @@
 #include "test_macros.h"
 
 struct NoConv {};
-struct Bad { template<class T> Bad(T v) noexcept(noexcept(member_ = v)) {} int member_; };
+struct Bad {
+  template <class T>
+  Bad(T v) noexcept(noexcept(member_ = v)) {}
+  int member_;
+};
 
 template <typename T, typename U, bool Expected>
 constexpr void test_reference_constructs_from_temporary() {

>From d75213697182d2b8a261e66285ec1039442a32f1 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 2 Jul 2026 00:14:23 -0700
Subject: [PATCH 3/3] Check T and U are complete types

Co-authored-by: Kim-J-Smith
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../reference_constructs_from_temporary.h     | 16 +++++--
 ...rence_constructs_from_temporary.verify.cpp | 42 +++++++++++++++++++
 2 files changed, 54 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.verify.cpp

diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index 1050952bccf80..bb1ec19e4aaef 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -19,8 +19,6 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 2300
-
 // A non-reference type can never bind to a temporary, so the result is always `false` for such a
 // `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
 // eagerly instantiates the construction of `_Up` (including the element's constructor exception
@@ -29,11 +27,21 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 // https://godbolt.org/z/4xz1ozKev
 //
 // TODO: Clang 23 fix this builtin, remove this guard once all supported clang versions include this fix.
-template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
+#if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 2300
+
+template <class _Tp>
+concept __complete =
+    is_reference_v<_Tp> || is_function_v<_Tp> || requires { sizeof(_Tp); } && is_object_v<_Tp>;
+
+template <class _Tp>
+concept __complete_or_unbounded = __complete<_Tp> || is_void_v<_Tp> || is_unbounded_array_v<_Tp>;
+
+template <class _Tp, class _Up,
+    bool = !is_reference_v<_Tp> && __complete_or_unbounded<_Tp> && __complete_or_unbounded<_Up>>
 inline const bool __reference_constructs_from_temporary_v = false;
 
 template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
+inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, false> =
     __reference_constructs_from_temporary(_Tp, _Up);
 
 #else
diff --git a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.verify.cpp b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.verify.cpp
new file mode 100644
index 0000000000000..0b17f9b52d2d7
--- /dev/null
+++ b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.verify.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <type_traits>
+
+// template<class T, class U> struct reference_constructs_from_temporary;
+
+// template<class T, class U>
+// constexpr bool reference_constructs_from_temporary_v
+//   = reference_constructs_from_temporary<T, U>::value;
+
+// expected-error@*:* 2 {{incomplete type 'IncompleteType' used in type trait expression}}
+
+#include <type_traits>
+
+struct NoConv {};
+struct Bad {
+  template <class T>
+  Bad(T v) noexcept(noexcept(member_ = v)) {}
+  int member_;
+};
+struct IncompleteType;
+
+constexpr bool test() {
+  static_assert(!std::reference_constructs_from_temporary_v<IncompleteType, NoConv&&>);
+  static_assert(!std::reference_constructs_from_temporary_v<Bad, IncompleteType>);
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Thu Jul  2 00:17:17 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Thu, 02 Jul 2026 00:17:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a46107d.1a4df1a5.118deb.9b0b@mx.google.com>


================
@@ -11,28 +11,50 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_reference.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 2300
+
+// A non-reference type can never bind to a temporary, so the result is always `false` for such a
+// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
+// eagerly instantiates the construction of `_Up` (including the element's constructor exception
+// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
+//
+// https://godbolt.org/z/4xz1ozKev
+//
+// TODO: Clang 23 fix this builtin, remove this guard once all supported clang versions include this fix.
+template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
+inline const bool __reference_constructs_from_temporary_v = false;
+
+template <class _Tp, class _Up>
+inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
+    __reference_constructs_from_temporary(_Tp, _Up);
----------------
yronglin wrote:

Thanks, this looks good to me! I have applied this change and added more test.

BTW, what's you email, then I can add you as a co-author.

https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:18:05 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Thu, 02 Jul 2026 00:18:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a4610ad.f732ad81.f30e7.e57d@mx.google.com>


yronglin wrote:

This PR depends on https://github.com/llvm/llvm-project/pull/206703.

https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:18:34 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Thu, 02 Jul 2026 00:18:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a4610ca.cbc3f03a.3be201.098a@mx.google.com>


================
@@ -11,28 +11,50 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_reference.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 2300
+
+// A non-reference type can never bind to a temporary, so the result is always `false` for such a
+// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
+// eagerly instantiates the construction of `_Up` (including the element's constructor exception
+// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
+//
+// https://godbolt.org/z/4xz1ozKev
+//
+// TODO: Clang 23 fix this builtin, remove this guard once all supported clang versions include this fix.
+template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
+inline const bool __reference_constructs_from_temporary_v = false;
+
+template <class _Tp, class _Up>
+inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
+    __reference_constructs_from_temporary(_Tp, _Up);
----------------
yronglin wrote:

@philnik777 Could you help review? Many thanks!

https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:29:04 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Thu, 02 Jul 2026 00:29:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a461340.13be6df8.3d8759.a676@mx.google.com>


yronglin wrote:

TODO: Need to add more test.

https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:35:57 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 00:35:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Move threading and random device
 config into <__configuration/platform.h> (PR #206262)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206262@github.com>
Message-ID: <6a4614dd.30759430.1908d3.37b2@mx.google.com>


philnik777 wrote:

Sorry, I missed that there were CI failures when merging. Reverting for now.

https://github.com/llvm/llvm-project/pull/206262

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:36:08 2026
From: libcxx-commits at lists.llvm.org (Vinay Deshmukh via libcxx-commits)
Date: Thu, 02 Jul 2026 00:36:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make `<set>` `std::multiset`
 constexpr as part of P3372R3 (PR #206257)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206257@github.com>
Message-ID: <6a4614e8.6c8668f2.74a35.058d@mx.google.com>


https://github.com/vinay-deshmukh updated https://github.com/llvm/llvm-project/pull/206257

>From c7046abfa4d3aaaf0bce4f8589a50221a46934b1 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 14:46:05 -0500
Subject: [PATCH 01/21] Initial patch from PR#134330

---
 libcxx/include/__functional/hash.h            |   9 +
 .../include/__iterator/erase_if_container.h   |   3 +-
 libcxx/include/__node_handle                  |  52 +-
 .../include/__numeric/saturation_arithmetic.h |  10 +-
 libcxx/include/__tree                         | 912 +++++++++++-------
 .../include/__type_traits/make_transparent.h  |   4 +-
 .../__utility/default_three_way_comparator.h  |   2 +-
 .../lazy_synth_three_way_comparator.h         |  33 +-
 libcxx/include/__utility/try_key_extraction.h |  12 +-
 libcxx/test/support/is_transparent.h          |  13 +-
 libcxx/test/support/poisoned_hash_helper.h    |  10 +-
 libcxx/test/support/private_constructor.h     |  18 +-
 12 files changed, 638 insertions(+), 440 deletions(-)

diff --git a/libcxx/include/__functional/hash.h b/libcxx/include/__functional/hash.h
index f74f25fa6e84b..83bbf1b5e26c3 100644
--- a/libcxx/include/__functional/hash.h
+++ b/libcxx/include/__functional/hash.h
@@ -433,10 +433,13 @@ struct __hash_impl<long double> : __scalar_hash<long double> {
 template <class _Tp>
 struct hash : public __hash_impl<_Tp> {};
 
+#if _LIBCPP_STD_VER >= 17
+
 template <>
 struct hash<nullptr_t> : public __unary_function<nullptr_t, size_t> {
   _LIBCPP_HIDE_FROM_ABI size_t operator()(nullptr_t) const _NOEXCEPT { return 662607004ull; }
 };
+#endif
 
 #ifndef _LIBCPP_CXX03_LANG
 template <class _Key, class _Hash>
@@ -449,12 +452,18 @@ template <class _Key, class _Hash = hash<_Key> >
 using __has_enabled_hash _LIBCPP_NODEBUG =
     integral_constant<bool, __check_hash_requirements<_Key, _Hash>::value && is_default_constructible<_Hash>::value >;
 
+#  if _LIBCPP_STD_VER >= 17
 template <class _Type, class>
 using __enable_hash_helper_imp _LIBCPP_NODEBUG = _Type;
 
 template <class _Type, class... _Keys>
 using __enable_hash_helper _LIBCPP_NODEBUG =
     __enable_hash_helper_imp<_Type, __enable_if_t<__all<__has_enabled_hash<_Keys>::value...>::value> >;
+#  else
+template <class _Type, class...>
+using __enable_hash_helper _LIBCPP_NODEBUG = _Type;
+#  endif
+
 #endif // !_LIBCPP_CXX03_LANG
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__iterator/erase_if_container.h b/libcxx/include/__iterator/erase_if_container.h
index 0f87f50cd1c16..8d92d3f1b9dbe 100644
--- a/libcxx/include/__iterator/erase_if_container.h
+++ b/libcxx/include/__iterator/erase_if_container.h
@@ -22,7 +22,8 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Container, class _Predicate>
-_LIBCPP_HIDE_FROM_ABI typename _Container::size_type __libcpp_erase_if_container(_Container& __c, _Predicate& __pred) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename _Container::size_type
+__libcpp_erase_if_container(_Container& __c, _Predicate& __pred) {
   typename _Container::size_type __old_size = __c.size();
 
   const typename _Container::iterator __last = __c.end();
diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index b20b0c73a0518..58be5b9d4ddd5 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -31,29 +31,29 @@ private:
 public:
   // [container.node.cons], constructors, copy, and assignment
   constexpr node-handle() noexcept : ptr_(), alloc_() {}
-  node-handle(node-handle&&) noexcept;
-  node-handle& operator=(node-handle&&);
+  constexpr node-handle(node-handle&&) noexcept;         // constexpr since C++26
+  constexpr node-handle& operator=(node-handle&&);       // constexpr since C++26
 
   // [container.node.dtor], destructor
-  ~node-handle();
+  constexpr ~node-handle(); // constexpr since C++26
 
   // [container.node.observers], observers
-  value_type& value() const;            // not present for map containers
-  key_type& key() const;                // not present for set containers
-  mapped_type& mapped() const;          // not present for set containers
+  value_type& value() const;             // not present for map containers
+  key_type& key() const;                 // not present for set containers
+  constexpr mapped_type& mapped() const; // not present for set containers, constexpr since C++26
 
-  allocator_type get_allocator() const;
-  explicit operator bool() const noexcept;
-  [[nodiscard]] bool empty() const noexcept; // nodiscard since C++20
+  constexpr allocator_type get_allocator() const;      // constexpr since C++26
+  constexpr explicit operator bool() const noexcept;   // constexpr since C++26
+  [[nodiscard]] constexpr bool empty() const noexcept; // nodiscard since C++20, constexpr since C++26
 
   // [container.node.modifiers], modifiers
-  void swap(node-handle&)
+  constexpr void swap(node-handle&)
     noexcept(ator_traits::propagate_on_container_swap::value ||
-             ator_traits::is_always_equal::value);
+             ator_traits::is_always_equal::value); // constexpr since C++26
 
-  friend void swap(node-handle& x, node-handle& y) noexcept(noexcept(x.swap(y))) {
+  constexpr friend void swap(node-handle& x, node-handle& y) noexcept(noexcept(x.swap(y))) {
     x.swap(y);
-  }
+  } // constexpr since C++26
 };
 
 */
@@ -99,12 +99,12 @@ private:
   __node_pointer_type __ptr_ = nullptr;
   optional<allocator_type> __alloc_;
 
-  _LIBCPP_HIDE_FROM_ABI void __release_ptr() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __release_ptr() {
     __ptr_   = nullptr;
     __alloc_ = std::nullopt;
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __destroy_node_pointer() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __destroy_node_pointer() {
     if (__ptr_ != nullptr) {
       typedef typename __allocator_traits_rebind< allocator_type, _NodeType>::type __node_alloc_type;
       __node_alloc_type __alloc(*__alloc_);
@@ -113,19 +113,20 @@ private:
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI __basic_node_handle(__node_pointer_type __ptr, allocator_type const& __alloc)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  __basic_node_handle(__node_pointer_type __ptr, allocator_type const& __alloc)
       : __ptr_(__ptr), __alloc_(__alloc) {}
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __basic_node_handle() = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __basic_node_handle() = default;
 
-  _LIBCPP_HIDE_FROM_ABI __basic_node_handle(__basic_node_handle&& __other) noexcept
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __basic_node_handle(__basic_node_handle&& __other) noexcept
       : __ptr_(__other.__ptr_), __alloc_(std::move(__other.__alloc_)) {
     __other.__ptr_   = nullptr;
     __other.__alloc_ = std::nullopt;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __basic_node_handle& operator=(__basic_node_handle&& __other) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __basic_node_handle& operator=(__basic_node_handle&& __other) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __alloc_ == std::nullopt || __alloc_traits::propagate_on_container_move_assignment::value ||
             __alloc_ == __other.__alloc_,
@@ -144,13 +145,13 @@ public:
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const { return *__alloc_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type get_allocator() const { return *__alloc_; }
 
-  _LIBCPP_HIDE_FROM_ABI explicit operator bool() const { return __ptr_ != nullptr; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit operator bool() const { return __ptr_ != nullptr; }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool empty() const { return __ptr_ == nullptr; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool empty() const { return __ptr_ == nullptr; }
 
-  _LIBCPP_HIDE_FROM_ABI void swap(__basic_node_handle& __other) noexcept(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(__basic_node_handle& __other) noexcept(
       __alloc_traits::propagate_on_container_swap::value || __alloc_traits::is_always_equal::value) {
     using std::swap;
     swap(__ptr_, __other.__ptr_);
@@ -159,12 +160,12 @@ public:
       swap(__alloc_, __other.__alloc_);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend void
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend void
   swap(__basic_node_handle& __a, __basic_node_handle& __b) noexcept(noexcept(__a.swap(__b))) {
     __a.swap(__b);
   }
 
-  _LIBCPP_HIDE_FROM_ABI ~__basic_node_handle() { __destroy_node_pointer(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~__basic_node_handle() { __destroy_node_pointer(); }
 };
 
 template <class _NodeType, class _Derived>
@@ -179,6 +180,7 @@ struct __map_node_handle_specifics {
   using key_type    = __remove_const_t<typename _NodeType::__node_value_type::first_type>;
   using mapped_type = typename _NodeType::__node_value_type::second_type;
 
+  // This method is not constexpr as per the standard.
   _LIBCPP_HIDE_FROM_ABI key_type& key() const {
     return const_cast<key_type&>(static_cast<_Derived const*>(this)->__ptr_->__get_value().first);
   }
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 4491bab2b1479..7a7410b5dea08 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -121,27 +121,27 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Rp __saturate_cast(_Tp __x) noexcept {
 #if _LIBCPP_STD_VER >= 26
 
 template <__signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
   return std::__add_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
   return std::__sub_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
   return std::__mul_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
   return std::__div_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Rp, __signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
   return std::__saturate_cast<_Rp>(__x);
 }
 
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 694796922c914..5398ff911ec57 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -28,6 +28,8 @@
 #include <__type_traits/copy_cvref.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_constructible.h>
 #include <__type_traits/is_nothrow_assignable.h>
 #include <__type_traits/is_nothrow_constructible.h>
@@ -111,7 +113,7 @@ __root, have a non-null __parent_ field.
 // Returns:  true if __x is a left child of its parent, else false
 // Precondition:  __x != nullptr.
 template <class _NodePtr>
-inline _LIBCPP_HIDE_FROM_ABI bool __tree_is_left_child(_NodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __tree_is_left_child(_NodePtr __x) _NOEXCEPT {
   return __x == __x->__parent_->__left_;
 }
 
@@ -119,7 +121,7 @@ inline _LIBCPP_HIDE_FROM_ABI bool __tree_is_left_child(_NodePtr __x) _NOEXCEPT {
 //    __x is a proper subtree, returns the black height (null counts as 1).  If
 //    __x is an improper subtree, returns 0.
 template <class _NodePtr>
-unsigned __tree_sub_invariant(_NodePtr __x) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 unsigned __tree_sub_invariant(_NodePtr __x) {
   if (__x == nullptr)
     return 1;
   // parent consistency checked by caller
@@ -151,7 +153,7 @@ unsigned __tree_sub_invariant(_NodePtr __x) {
 //    __root == nullptr is a proper tree.  Returns true if __root is a proper
 //    red black tree, else returns false.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI bool __tree_invariant(_NodePtr __root) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __tree_invariant(_NodePtr __root) {
   if (__root == nullptr)
     return true;
   // check __x->__parent_ consistency
@@ -168,7 +170,7 @@ _LIBCPP_HIDE_FROM_ABI bool __tree_invariant(_NodePtr __root) {
 
 // Returns:  pointer to the left-most node under __x.
 template <class _NodePtr>
-inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_min(_NodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_min(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Root node shouldn't be null");
   while (__x->__left_ != nullptr)
     __x = __x->__left_;
@@ -177,7 +179,7 @@ inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_min(_NodePtr __x) _NOEXCEPT {
 
 // Returns:  pointer to the right-most node under __x.
 template <class _NodePtr>
-inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_max(_NodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_max(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Root node shouldn't be null");
   while (__x->__right_ != nullptr)
     __x = __x->__right_;
@@ -186,7 +188,7 @@ inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_max(_NodePtr __x) _NOEXCEPT {
 
 // Returns:  pointer to the next in-order node after __x.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI _NodePtr __tree_next(_NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_next(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   if (__x->__right_ != nullptr)
     return std::__tree_min(__x->__right_);
@@ -201,23 +203,23 @@ _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_next(_NodePtr __x) _NOEXCEPT {
 // to the actual root of the tree through a __left_ pointer. Incrementing the end() pointer is UB, so we can assume that
 // never happens.
 template <class _EndNodePtr, class _NodePtr>
-inline _LIBCPP_HIDE_FROM_ABI _EndNodePtr __tree_next_iter(_NodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _EndNodePtr __tree_next_iter(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   if (__x->__right_ != nullptr)
-    return static_cast<_EndNodePtr>(std::__tree_min(__x->__right_));
+    return std::__static_fancy_pointer_cast<_EndNodePtr>(std::__tree_min(__x->__right_));
   while (!std::__tree_is_left_child(__x))
     __x = __x->__parent_unsafe();
-  return static_cast<_EndNodePtr>(__x->__parent_);
+  return std::__static_fancy_pointer_cast<_EndNodePtr>(__x->__parent_);
 }
 
 // Returns:  pointer to the previous in-order node before __x.
 // Note: __x may be the end node.
 template <class _NodePtr, class _EndNodePtr>
-inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_prev_iter(_EndNodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_prev_iter(_EndNodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   if (__x->__left_ != nullptr)
     return std::__tree_max(__x->__left_);
-  _NodePtr __xx = static_cast<_NodePtr>(__x);
+  _NodePtr __xx = std::__static_fancy_pointer_cast<_NodePtr>(__x);
   while (std::__tree_is_left_child(__xx))
     __xx = __xx->__parent_unsafe();
   return __xx->__parent_unsafe();
@@ -225,7 +227,7 @@ inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_prev_iter(_EndNodePtr __x) _NOEXCEP
 
 // Returns:  pointer to a node which has no children
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI _NodePtr __tree_leaf(_NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_leaf(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   while (true) {
     if (__x->__left_ != nullptr) {
@@ -244,7 +246,7 @@ _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_leaf(_NodePtr __x) _NOEXCEPT {
 // Effects:  Makes __x->__right_ the subtree root with __x as its left child
 //           while preserving in-order order.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI void __tree_left_rotate(_NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree_left_rotate(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   _LIBCPP_ASSERT_INTERNAL(__x->__right_ != nullptr, "node should have a right child");
   _NodePtr __y  = __x->__right_;
@@ -263,7 +265,7 @@ _LIBCPP_HIDE_FROM_ABI void __tree_left_rotate(_NodePtr __x) _NOEXCEPT {
 // Effects:  Makes __x->__left_ the subtree root with __x as its right child
 //           while preserving in-order order.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI void __tree_right_rotate(_NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree_right_rotate(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   _LIBCPP_ASSERT_INTERNAL(__x->__left_ != nullptr, "node should have a left child");
   _NodePtr __y = __x->__left_;
@@ -287,7 +289,8 @@ _LIBCPP_HIDE_FROM_ABI void __tree_right_rotate(_NodePtr __x) _NOEXCEPT {
 // Postcondition: __tree_invariant(end_node->__left_) == true.  end_node->__left_
 //                may be different than the value passed in as __root.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI void __tree_balance_after_insert(_NodePtr __root, _NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+__tree_balance_after_insert(_NodePtr __root, _NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__root != nullptr, "Root of the tree shouldn't be null");
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Can't attach null node to a leaf");
   __x->__is_black_ = __x == __root;
@@ -343,7 +346,7 @@ _LIBCPP_HIDE_FROM_ABI void __tree_balance_after_insert(_NodePtr __root, _NodePtr
 //                nor any of its children refer to __z.  end_node->__left_
 //                may be different than the value passed in as __root.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI void __tree_remove(_NodePtr __root, _NodePtr __z) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree_remove(_NodePtr __root, _NodePtr __z) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__root != nullptr, "Root node should not be null");
   _LIBCPP_ASSERT_INTERNAL(__z != nullptr, "The node to remove should not be null");
   _LIBCPP_ASSERT_INTERNAL(std::__tree_invariant(__root), "The tree invariants should hold");
@@ -558,7 +561,7 @@ public:
   using pointer = _Pointer;
   pointer __left_;
 
-  _LIBCPP_HIDE_FROM_ABI __tree_end_node() _NOEXCEPT : __left_() {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_end_node() _NOEXCEPT : __left_(nullptr) {}
 };
 
 template <class _VoidPtr>
@@ -571,11 +574,15 @@ public:
   __end_node_pointer __parent_;
   bool __is_black_;
 
-  _LIBCPP_HIDE_FROM_ABI pointer __parent_unsafe() const { return static_cast<pointer>(__parent_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pointer __parent_unsafe() const {
+    return std::__static_fancy_pointer_cast<pointer>(__parent_);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI void __set_parent(pointer __p) { __parent_ = static_cast<__end_node_pointer>(__p); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __set_parent(pointer __p) {
+    __parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__p);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_node_base()             = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_node_base() = default;
   __tree_node_base(__tree_node_base const&)            = delete;
   __tree_node_base& operator=(__tree_node_base const&) = delete;
 };
@@ -596,7 +603,7 @@ private:
   };
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __node_value_type& __get_value() { return __value_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_value_type& __get_value() { return __value_; }
 #else
 
 private:
@@ -607,10 +614,24 @@ public:
 #endif
 
   template <class _Alloc, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_node(_Alloc& __na, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_node(_Alloc& __na, _Args&&... __args) {
     allocator_traits<_Alloc>::construct(__na, std::addressof(__get_value()), std::forward<_Args>(__args)...);
   }
+
+// This line fails with:
+// libcxx/test-suite-install/include/c++/v1/__memory/construct_at.h:38:49: note: non-literal type
+// `'std::__tree_node<std::__value_type<Key, int>, void *>' cannot be used in a constant expression`
+// during constant evaluation as part of P3372
+// on:
+// 1. AppleClang, which is not yet using clang >= 20.x, which has "fixed" this issue
+// FIXME: when AppleClang is based off of clang >= 20.x
+// FIXME: when Clang-based CI is using clang >= 20.x
+#if (defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 2000) ||                                                        \
+    (defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 2000)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~__tree_node() {}
+#else
   ~__tree_node()                             = delete;
+#endif
   __tree_node(__tree_node const&)            = delete;
   __tree_node& operator=(__tree_node const&) = delete;
 };
@@ -629,14 +650,15 @@ private:
 public:
   bool __value_constructed;
 
-  _LIBCPP_HIDE_FROM_ABI __tree_node_destructor(const __tree_node_destructor&) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_node_destructor(const __tree_node_destructor&) = default;
   __tree_node_destructor& operator=(const __tree_node_destructor&)            = delete;
 
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_node_destructor(allocator_type& __na, bool __val = false) _NOEXCEPT
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_node_destructor(allocator_type& __na, bool __val = false) _NOEXCEPT
       : __na_(__na),
         __value_constructed(__val) {}
 
-  _LIBCPP_HIDE_FROM_ABI void operator()(pointer __p) _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void operator()(pointer __p) _NOEXCEPT {
     if (__value_constructed)
       __alloc_traits::destroy(__na_, std::addressof(__p->__get_value()));
     if (__p)
@@ -673,44 +695,50 @@ public:
   using reference         = value_type&;
   using pointer           = __rebind_pointer_t<_NodePtr, value_type>;
 
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator() _NOEXCEPT : __ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator() _NOEXCEPT : __ptr_(nullptr) {}
 
-  _LIBCPP_HIDE_FROM_ABI reference operator*() const { return __get_np()->__get_value(); }
-  _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reference operator*() const { return __get_np()->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pointer operator->() const {
     return pointer_traits<pointer>::pointer_to(__get_np()->__get_value());
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator& operator++() {
-    __ptr_ = std::__tree_next_iter<__end_node_pointer>(static_cast<__node_base_pointer>(__ptr_));
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator& operator++() {
+    __ptr_ = std::__tree_next_iter<__end_node_pointer>(std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr_));
     return *this;
   }
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator operator++(int) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator operator++(int) {
     __tree_iterator __t(*this);
     ++(*this);
     return __t;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator& operator--() {
-    __ptr_ = static_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator& operator--() {
+    __ptr_ = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
     return *this;
   }
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator operator--(int) {
     __tree_iterator __t(*this);
     --(*this);
     return __t;
   }
 
-  friend _LIBCPP_HIDE_FROM_ABI bool operator==(const __tree_iterator& __x, const __tree_iterator& __y) {
+  friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
+  operator==(const __tree_iterator& __x, const __tree_iterator& __y) {
     return __x.__ptr_ == __y.__ptr_;
   }
-  friend _LIBCPP_HIDE_FROM_ABI bool operator!=(const __tree_iterator& __x, const __tree_iterator& __y) {
+  friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
+  operator!=(const __tree_iterator& __x, const __tree_iterator& __y) {
     return !(__x == __y);
   }
 
 private:
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_iterator(__node_pointer __p) _NOEXCEPT : __ptr_(__p) {}
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_iterator(__end_node_pointer __p) _NOEXCEPT : __ptr_(__p) {}
-  _LIBCPP_HIDE_FROM_ABI __node_pointer __get_np() const { return static_cast<__node_pointer>(__ptr_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_iterator(__node_pointer __p) _NOEXCEPT
+      : __ptr_(std::__static_fancy_pointer_cast<__end_node_pointer>(__p)) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_iterator(__end_node_pointer __p) _NOEXCEPT
+      : __ptr_(__p) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __get_np() const {
+    return std::__static_fancy_pointer_cast<__node_pointer>(__ptr_);
+  }
   template <class, class, class>
   friend class __tree;
   template <class, class, class>
@@ -735,48 +763,55 @@ public:
   using pointer                              = __rebind_pointer_t<_NodePtr, const value_type>;
   using __non_const_iterator _LIBCPP_NODEBUG = __tree_iterator<_Tp, __node_pointer, difference_type>;
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator() _NOEXCEPT : __ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator() _NOEXCEPT : __ptr_(nullptr) {}
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator(__non_const_iterator __p) _NOEXCEPT : __ptr_(__p.__ptr_) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator(__non_const_iterator __p) _NOEXCEPT
+      : __ptr_(__p.__ptr_) {}
 
-  _LIBCPP_HIDE_FROM_ABI reference operator*() const { return __get_np()->__get_value(); }
-  _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reference operator*() const { return __get_np()->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pointer operator->() const {
     return pointer_traits<pointer>::pointer_to(__get_np()->__get_value());
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator& operator++() {
-    __ptr_ = std::__tree_next_iter<__end_node_pointer>(static_cast<__node_base_pointer>(__ptr_));
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator& operator++() {
+    __ptr_ = std::__tree_next_iter<__end_node_pointer>(std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr_));
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator operator++(int) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator operator++(int) {
     __tree_const_iterator __t(*this);
     ++(*this);
     return __t;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator& operator--() {
-    __ptr_ = static_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator& operator--() {
+    __ptr_ = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator operator--(int) {
     __tree_const_iterator __t(*this);
     --(*this);
     return __t;
   }
 
-  friend _LIBCPP_HIDE_FROM_ABI bool operator==(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
+  friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
+  operator==(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
     return __x.__ptr_ == __y.__ptr_;
   }
-  friend _LIBCPP_HIDE_FROM_ABI bool operator!=(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
+  friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
+  operator!=(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
     return !(__x == __y);
   }
 
 private:
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_const_iterator(__node_pointer __p) _NOEXCEPT : __ptr_(__p) {}
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_const_iterator(__end_node_pointer __p) _NOEXCEPT : __ptr_(__p) {}
-  _LIBCPP_HIDE_FROM_ABI __node_pointer __get_np() const { return static_cast<__node_pointer>(__ptr_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_const_iterator(__node_pointer __p) _NOEXCEPT
+      : __ptr_(std::__static_fancy_pointer_cast<__end_node_pointer>(__p)) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_const_iterator(__end_node_pointer __p) _NOEXCEPT
+      : __ptr_(std::__static_fancy_pointer_cast<__end_node_pointer>(__p)) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __get_np() const {
+    return std::__static_fancy_pointer_cast<__node_pointer>(__ptr_);
+  }
 
   template <class, class, class>
   friend class __tree;
@@ -840,63 +875,73 @@ private:
   _LIBCPP_COMPRESSED_PAIR(size_type, __size_, value_compare, __value_comp_);
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __end_node_pointer __end_node() _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __end_node_pointer __end_node() _NOEXCEPT {
     return pointer_traits<__end_node_pointer>::pointer_to(__end_node_);
   }
-  _LIBCPP_HIDE_FROM_ABI __end_node_pointer __end_node() const _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __end_node_pointer __end_node() const _NOEXCEPT {
     return pointer_traits<__end_node_pointer>::pointer_to(const_cast<__end_node_t&>(__end_node_));
   }
-  _LIBCPP_HIDE_FROM_ABI __node_allocator& __node_alloc() _NOEXCEPT { return __node_alloc_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_allocator& __node_alloc() _NOEXCEPT {
+    return __node_alloc_;
+  }
 
 private:
-  _LIBCPP_HIDE_FROM_ABI const __node_allocator& __node_alloc() const _NOEXCEPT { return __node_alloc_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const __node_allocator& __node_alloc() const _NOEXCEPT {
+    return __node_alloc_;
+  }
 
 public:
-  _LIBCPP_HIDE_FROM_ABI allocator_type __alloc() const _NOEXCEPT { return allocator_type(__node_alloc()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type __alloc() const _NOEXCEPT {
+    return allocator_type(__node_alloc());
+  }
 
-  _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __size_; }
-  _LIBCPP_HIDE_FROM_ABI value_compare& value_comp() _NOEXCEPT { return __value_comp_; }
-  _LIBCPP_HIDE_FROM_ABI const value_compare& value_comp() const _NOEXCEPT { return __value_comp_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type size() const _NOEXCEPT { return __size_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_compare& value_comp() _NOEXCEPT { return __value_comp_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const value_compare& value_comp() const _NOEXCEPT {
+    return __value_comp_;
+  }
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __node_pointer __root() const _NOEXCEPT {
-    return static_cast<__node_pointer>(__end_node()->__left_);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __root() const _NOEXCEPT {
+    return std::__static_fancy_pointer_cast<__node_pointer>(__end_node()->__left_);
   }
 
-  _LIBCPP_HIDE_FROM_ABI __node_base_pointer* __root_ptr() const _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer* __root_ptr() const _NOEXCEPT {
     return std::addressof(__end_node()->__left_);
   }
 
   using iterator       = __tree_iterator<_Tp, __node_pointer, difference_type>;
   using const_iterator = __tree_const_iterator<_Tp, __node_pointer, difference_type>;
 
-  _LIBCPP_HIDE_FROM_ABI explicit __tree(const value_compare& __comp) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree(const value_compare& __comp) _NOEXCEPT_(
       is_nothrow_default_constructible<__node_allocator>::value&& is_nothrow_copy_constructible<value_compare>::value)
       : __size_(0), __value_comp_(__comp) {
     __begin_node_ = __end_node();
   }
 
-  _LIBCPP_HIDE_FROM_ABI explicit __tree(const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree(const allocator_type& __a)
       : __begin_node_(), __node_alloc_(__node_allocator(__a)), __size_(0) {
     __begin_node_ = __end_node();
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree(const value_compare& __comp, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(const value_compare& __comp, const allocator_type& __a)
       : __begin_node_(), __node_alloc_(__node_allocator(__a)), __size_(0), __value_comp_(__comp) {
     __begin_node_ = __end_node();
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree(const __tree& __t);
-  _LIBCPP_HIDE_FROM_ABI __tree& operator=(const __tree& __t);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(const __tree& __t);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree& operator=(const __tree& __t);
   template <class _ForwardIterator>
-  _LIBCPP_HIDE_FROM_ABI void __assign_unique(_ForwardIterator __first, _ForwardIterator __last);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_unique(_ForwardIterator __first, _ForwardIterator __last);
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void __assign_multi(_InputIterator __first, _InputIterator __last);
-  _LIBCPP_HIDE_FROM_ABI __tree(__tree&& __t) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_multi(_InputIterator __first, _InputIterator __last);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t) _NOEXCEPT_(
       is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value);
-  _LIBCPP_HIDE_FROM_ABI __tree(__tree&& __t, const allocator_type& __a);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t, const allocator_type& __a);
 
-  _LIBCPP_HIDE_FROM_ABI __tree& operator=(__tree&& __t)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree& operator=(__tree&& __t)
       _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value &&
                  ((__node_traits::propagate_on_container_move_assignment::value &&
                    is_nothrow_move_assignable<__node_allocator>::value) ||
@@ -905,23 +950,27 @@ public:
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI ~__tree() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~__tree() {
     static_assert(is_copy_constructible<value_compare>::value, "Comparator must be copy-constructible.");
     destroy(__root());
   }
 
-  _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return iterator(__begin_node_); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return const_iterator(__begin_node_); }
-  _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return iterator(__end_node()); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return const_iterator(__end_node()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT { return iterator(__begin_node_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator begin() const _NOEXCEPT {
+    return const_iterator(__begin_node_);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator end() _NOEXCEPT { return iterator(__end_node()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator end() const _NOEXCEPT {
+    return const_iterator(__end_node());
+  }
 
-  _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type max_size() const _NOEXCEPT {
     return std::min<size_type>(__node_traits::max_size(__node_alloc()), numeric_limits<difference_type >::max());
   }
 
-  _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT;
 
-  _LIBCPP_HIDE_FROM_ABI void swap(__tree& __t)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(__tree& __t)
 #if _LIBCPP_STD_VER <= 11
       _NOEXCEPT_(__is_nothrow_swappable_v<value_compare> &&
                  (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>));
@@ -930,21 +979,22 @@ public:
 #endif
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator __emplace_multi(_Args&&... __args);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __emplace_multi(_Args&&... __args);
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator __emplace_hint_multi(const_iterator __p, _Args&&... __args);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
+  __emplace_hint_multi(const_iterator __p, _Args&&... __args);
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __emplace_unique(_Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> __emplace_unique(_Args&&... __args) {
     return std::__try_key_extraction<key_type>(
         [this](const key_type& __key, _Args&&... __args2) {
           auto [__parent, __child] = __find_equal(__key);
-          __node_pointer __r       = static_cast<__node_pointer>(__child);
+          __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
           bool __inserted          = false;
           if (__child == nullptr) {
             __node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
-            __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
+            __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
             __r        = __h.release();
             __inserted = true;
           }
@@ -953,10 +1003,10 @@ public:
         [this](_Args&&... __args2) {
           __node_holder __h        = __construct_node(std::forward<_Args>(__args2)...);
           auto [__parent, __child] = __find_equal(__h->__get_value());
-          __node_pointer __r       = static_cast<__node_pointer>(__child);
+          __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
           bool __inserted          = false;
           if (__child == nullptr) {
-            __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
+            __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
             __r        = __h.release();
             __inserted = true;
           }
@@ -966,16 +1016,17 @@ public:
   }
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __emplace_hint_unique(const_iterator __p, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
+  __emplace_hint_unique(const_iterator __p, _Args&&... __args) {
     return std::__try_key_extraction<key_type>(
         [this, __p](const key_type& __key, _Args&&... __args2) {
           __node_base_pointer __dummy;
           auto [__parent, __child] = __find_equal(__p, __dummy, __key);
-          __node_pointer __r       = static_cast<__node_pointer>(__child);
+          __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
           bool __inserted          = false;
           if (__child == nullptr) {
             __node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
-            __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
+            __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
             __r        = __h.release();
             __inserted = true;
           }
@@ -985,9 +1036,9 @@ public:
           __node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
           __node_base_pointer __dummy;
           auto [__parent, __child] = __find_equal(__p, __dummy, __h->__get_value());
-          __node_pointer __r       = static_cast<__node_pointer>(__child);
+          __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
           if (__child == nullptr) {
-            __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
+            __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
             __r = __h.release();
           }
           return pair<iterator, bool>(iterator(__r), __child == nullptr);
@@ -995,73 +1046,112 @@ public:
         std::forward<_Args>(__args)...);
   }
 
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
+  __emplace_hint_unique__sfinae(true_type, const_iterator __p, _Args&&... __args) {
+    return __emplace_hint_unique(__p, __args...);
+  }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
+  __emplace_hint_unique__sfinae(false_type, const_iterator, _Args&&...) {
+    // This method body should never be run. It only exists to allow for compilation. See note in
+    // __insert_unique_from_orphaned_node for more information.
+    return pair<iterator, bool>(iterator(__node_pointer()), false);
+  }
+
   template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI void
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   __insert_unique_from_orphaned_node(const_iterator __p, __get_node_value_type_t<_Tp>&& __value) {
-    __emplace_hint_unique(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
+#if _LIBCPP_STD_VER >= 26
+    if (std::is_constant_evaluated() && std::is_copy_constructible_v<decltype(__value.first)>) {
+      // We create a sfinae wrapper method here, because if the __emplace_hint_unique method gets template instantiated
+      // within __insert_unique_from_orphaned_node, the code will fail to compile when the value is not
+      // copy_constructible (even for runtime execution); unless we use `if constexpr`. Given the copy-constructible
+      // code path will be a performance regression, we want to restrict it to only execute during constant evaluation
+      // , hence, we need to delay the template instantiation.
+      __emplace_hint_unique__sfinae(
+          integral_constant< bool, std::is_copy_constructible_v<decltype(__value.first)> >(),
+          __p,
+          __value.first,
+          std::move(__value.second));
+    } else
+#endif
+    {
+      __emplace_hint_unique(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
+    }
   }
 
   template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI void __insert_unique_from_orphaned_node(const_iterator __p, _Tp&& __value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __insert_unique_from_orphaned_node(const_iterator __p, _Tp&& __value) {
     __emplace_hint_unique(__p, std::move(__value));
   }
 
   template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(const_iterator __p, value_type&& __value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __insert_multi_from_orphaned_node(const_iterator __p, value_type&& __value) {
     __emplace_hint_multi(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
   }
 
   template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(const_iterator __p, _Tp&& __value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __insert_multi_from_orphaned_node(const_iterator __p, _Tp&& __value) {
     __emplace_hint_multi(__p, std::move(__value));
   }
 
   template <class _InIter, class _Sent>
-  _LIBCPP_HIDE_FROM_ABI void __insert_range_multi(_InIter __first, _Sent __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __insert_range_multi(_InIter __first, _Sent __last) {
     if (__first == __last)
       return;
 
     if (__root() == nullptr) { // Make sure we always have a root node
-      __insert_node_at(
-          __end_node(), __end_node()->__left_, static_cast<__node_base_pointer>(__construct_node(*__first).release()));
+      __insert_node_at(__end_node(),
+                       __end_node()->__left_,
+                       std::__static_fancy_pointer_cast<__node_base_pointer>(__construct_node(*__first).release()));
       ++__first;
     }
 
-    auto __max_node = static_cast<__node_pointer>(std::__tree_max(static_cast<__node_base_pointer>(__root())));
+    auto __max_node = std::__static_fancy_pointer_cast<__node_pointer>(
+        std::__tree_max(std::__static_fancy_pointer_cast<__node_base_pointer>(__root())));
 
     for (; __first != __last; ++__first) {
       __node_holder __nd = __construct_node(*__first);
       // Always check the max node first. This optimizes for sorted ranges inserted at the end.
       if (!value_comp()(__nd->__get_value(), __max_node->__get_value())) { // __node >= __max_val
-        __insert_node_at(static_cast<__end_node_pointer>(__max_node),
+        __insert_node_at(std::__static_fancy_pointer_cast<__end_node_pointer>(__max_node),
                          __max_node->__right_,
-                         static_cast<__node_base_pointer>(__nd.get()));
+                         std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.get()));
         __max_node = __nd.release();
       } else {
         __end_node_pointer __parent;
         __node_base_pointer& __child = __find_leaf_high(__parent, __nd->__get_value());
-        __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd.release()));
+        __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.release()));
       }
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __node_assign_unique(const value_type& __v, __node_pointer __dest);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
+  __node_assign_unique(const value_type& __v, __node_pointer __dest);
 
-  _LIBCPP_HIDE_FROM_ABI iterator __node_insert_multi(__node_pointer __nd);
-  _LIBCPP_HIDE_FROM_ABI iterator __node_insert_multi(const_iterator __p, __node_pointer __nd);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __node_insert_multi(__node_pointer __nd);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
+  __node_insert_multi(const_iterator __p, __node_pointer __nd);
 
   template <class _InIter, class _Sent>
-  _LIBCPP_HIDE_FROM_ABI void __insert_range_unique(_InIter __first, _Sent __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __insert_range_unique(_InIter __first, _Sent __last) {
     if (__first == __last)
       return;
 
     if (__root() == nullptr) {
-      __insert_node_at(
-          __end_node(), __end_node()->__left_, static_cast<__node_base_pointer>(__construct_node(*__first).release()));
+      __insert_node_at(__end_node(),
+                       __end_node()->__left_,
+                       std::__static_fancy_pointer_cast<__node_base_pointer>(__construct_node(*__first).release()));
       ++__first;
     }
 
-    auto __max_node = static_cast<__node_pointer>(std::__tree_max(static_cast<__node_base_pointer>(__root())));
+    auto __max_node = std::__static_fancy_pointer_cast<__node_pointer>(
+        std::__tree_max(std::__static_fancy_pointer_cast<__node_base_pointer>(__root())));
 
     using __reference = decltype(*__first);
 
@@ -1070,29 +1160,31 @@ public:
           [this, &__max_node](const key_type& __key, __reference&& __val) {
             if (value_comp()(__max_node->__get_value(), __key)) { // __key > __max_node
               __node_holder __nd = __construct_node(std::forward<__reference>(__val));
-              __insert_node_at(static_cast<__end_node_pointer>(__max_node),
+              __insert_node_at(std::__static_fancy_pointer_cast<__end_node_pointer>(__max_node),
                                __max_node->__right_,
-                               static_cast<__node_base_pointer>(__nd.get()));
+                               std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.get()));
               __max_node = __nd.release();
             } else {
               auto [__parent, __child] = __find_equal(__key);
               if (__child == nullptr) {
                 __node_holder __nd = __construct_node(std::forward<__reference>(__val));
-                __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd.release()));
+                __insert_node_at(
+                    __parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.release()));
               }
             }
           },
           [this, &__max_node](__reference&& __val) {
             __node_holder __nd = __construct_node(std::forward<__reference>(__val));
             if (value_comp()(__max_node->__get_value(), __nd->__get_value())) { // __node > __max_node
-              __insert_node_at(static_cast<__end_node_pointer>(__max_node),
+              __insert_node_at(std::__static_fancy_pointer_cast<__end_node_pointer>(__max_node),
                                __max_node->__right_,
-                               static_cast<__node_base_pointer>(__nd.get()));
+                               std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.get()));
               __max_node = __nd.release();
             } else {
               auto [__parent, __child] = __find_equal(__nd->__get_value());
               if (__child == nullptr) {
-                __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd.release()));
+                __insert_node_at(
+                    __parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.release()));
               }
             }
           },
@@ -1100,62 +1192,67 @@ public:
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI iterator __remove_node_pointer(__node_pointer) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __remove_node_pointer(__node_pointer) _NOEXCEPT;
 
 #if _LIBCPP_STD_VER >= 17
   template <class _NodeHandle, class _InsertReturnType>
-  _LIBCPP_HIDE_FROM_ABI _InsertReturnType __node_handle_insert_unique(_NodeHandle&&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _InsertReturnType __node_handle_insert_unique(_NodeHandle&&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI iterator __node_handle_insert_unique(const_iterator, _NodeHandle&&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
+  __node_handle_insert_unique(const_iterator, _NodeHandle&&);
   template <class _Comp2>
-  _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source);
 
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI iterator __node_handle_insert_multi(_NodeHandle&&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __node_handle_insert_multi(_NodeHandle&&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI iterator __node_handle_insert_multi(const_iterator, _NodeHandle&&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
+  __node_handle_insert_multi(const_iterator, _NodeHandle&&);
   template <class _Comp2>
-  _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source);
 
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(key_type const&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(key_type const&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const_iterator);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const_iterator);
 #endif
 
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p);
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l);
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI size_type __erase_unique(const _Key& __k);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __erase_unique(const _Key& __k);
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI size_type __erase_multi(const _Key& __k);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __erase_multi(const _Key& __k);
 
-  _LIBCPP_HIDE_FROM_ABI void
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   __insert_node_at(__end_node_pointer __parent, __node_base_pointer& __child, __node_base_pointer __new_node) _NOEXCEPT;
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator find(const _Key& __key) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const _Key& __key) {
     auto [__, __match] = __find_equal(__key);
     if (__match == nullptr)
       return end();
-    return iterator(static_cast<__node_pointer>(__match));
+    return iterator(std::__static_fancy_pointer_cast<__node_pointer>(__match));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator find(const _Key& __key) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const _Key& __key) const {
     auto [__, __match] = __find_equal(__key);
     if (__match == nullptr)
       return end();
-    return const_iterator(static_cast<__node_pointer>(__match));
+    return const_iterator(std::__static_fancy_pointer_cast<__node_pointer>(__match));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI size_type __count_unique(const _Key& __k) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __count_unique(const _Key& __k) const;
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI size_type __count_multi(const _Key& __k) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __count_multi(const _Key& __k) const;
 
   template <bool _LowerBound, class _Key>
-  _LIBCPP_HIDE_FROM_ABI __end_node_pointer __lower_upper_bound_unique_impl(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __end_node_pointer
+  __lower_upper_bound_unique_impl(const _Key& __v) const {
     auto __rt     = __root();
     auto __result = __end_node();
     auto __comp   = __lazy_synth_three_way_comparator<_Compare, _Key, value_type>(value_comp());
@@ -1163,156 +1260,210 @@ public:
       auto __comp_res = __comp(__v, __rt->__get_value());
 
       if (__comp_res.__less()) {
-        __result = static_cast<__end_node_pointer>(__rt);
-        __rt     = static_cast<__node_pointer>(__rt->__left_);
+        __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+        __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
       } else if (__comp_res.__greater()) {
-        __rt = static_cast<__node_pointer>(__rt->__right_);
+        __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
       } else if _LIBCPP_CONSTEXPR (_LowerBound) {
-        return static_cast<__end_node_pointer>(__rt);
+        return std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
       } else {
-        return __rt->__right_ ? static_cast<__end_node_pointer>(std::__tree_min(__rt->__right_)) : __result;
+        return __rt->__right_ ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
+                              : __result;
       }
     }
     return __result;
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator __lower_bound_unique(const _Key& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __lower_bound_unique(const _Key& __v) {
     return iterator(__lower_upper_bound_unique_impl<true>(__v));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator __lower_bound_unique(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __lower_bound_unique(const _Key& __v) const {
     return const_iterator(__lower_upper_bound_unique_impl<true>(__v));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator __upper_bound_unique(const _Key& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __upper_bound_unique(const _Key& __v) {
     return iterator(__lower_upper_bound_unique_impl<false>(__v));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator __upper_bound_unique(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __upper_bound_unique(const _Key& __v) const {
     return iterator(__lower_upper_bound_unique_impl<false>(__v));
   }
 
 private:
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
   __lower_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result);
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
   __lower_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result) const;
 
 public:
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator __lower_bound_multi(const _Key& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __lower_bound_multi(const _Key& __v) {
     return __lower_bound_multi(__v, __root(), __end_node());
   }
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator __lower_bound_multi(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __lower_bound_multi(const _Key& __v) const {
     return __lower_bound_multi(__v, __root(), __end_node());
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator __upper_bound_multi(const _Key& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __upper_bound_multi(const _Key& __v) {
     return __upper_bound_multi(__v, __root(), __end_node());
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator __upper_bound_multi(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __upper_bound_multi(const _Key& __v) const {
     return __upper_bound_multi(__v, __root(), __end_node());
   }
 
 private:
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
   __upper_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result);
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
   __upper_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result) const;
 
 public:
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> __equal_range_unique(const _Key& __k);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> __equal_range_unique(const _Key& __k);
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> __equal_range_unique(const _Key& __k) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  __equal_range_unique(const _Key& __k) const;
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> __equal_range_multi(const _Key& __k);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> __equal_range_multi(const _Key& __k);
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> __equal_range_multi(const _Key& __k) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  __equal_range_multi(const _Key& __k) const;
 
   using _Dp _LIBCPP_NODEBUG           = __tree_node_destructor<__node_allocator>;
   using __node_holder _LIBCPP_NODEBUG = unique_ptr<__node, _Dp>;
 
-  _LIBCPP_HIDE_FROM_ABI __node_holder remove(const_iterator __p) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_holder remove(const_iterator __p) _NOEXCEPT;
 
   // FIXME: Make this function const qualified. Unfortunately doing so
   // breaks existing code which uses non-const callable comparators.
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<__end_node_pointer, __node_base_pointer&> __find_equal(const _Key& __v);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<__end_node_pointer, __node_base_pointer&>
+  __find_equal(const _Key& __v);
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<__end_node_pointer, __node_base_pointer&> __find_equal(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<__end_node_pointer, __node_base_pointer&>
+  __find_equal(const _Key& __v) const {
     return const_cast<__tree*>(this)->__find_equal(__v);
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<__end_node_pointer, __node_base_pointer&>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<__end_node_pointer, __node_base_pointer&>
   __find_equal(const_iterator __hint, __node_base_pointer& __dummy, const _Key& __v);
 
-  _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __tree& __t) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __copy_assign_alloc(const __tree& __t) {
     __copy_assign_alloc(__t, integral_constant<bool, __node_traits::propagate_on_container_copy_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __tree& __t, true_type) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __copy_assign_alloc(const __tree& __t, true_type) {
     if (__node_alloc() != __t.__node_alloc())
       clear();
     __node_alloc() = __t.__node_alloc();
   }
-  _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __tree&, false_type) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __copy_assign_alloc(const __tree&, false_type) {}
 
 private:
-  _LIBCPP_HIDE_FROM_ABI __node_base_pointer& __find_leaf_low(__end_node_pointer& __parent, const value_type& __v);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer&
+  __find_leaf_low(__end_node_pointer& __parent, const value_type& __v);
 
-  _LIBCPP_HIDE_FROM_ABI __node_base_pointer& __find_leaf_high(__end_node_pointer& __parent, const value_type& __v);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer&
+  __find_leaf_high(__end_node_pointer& __parent, const value_type& __v);
 
-  _LIBCPP_HIDE_FROM_ABI __node_base_pointer&
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer&
   __find_leaf(const_iterator __hint, __end_node_pointer& __parent, const value_type& __v);
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI __node_holder __construct_node(_Args&&... __args);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_holder __construct_node(_Args&&... __args);
 
   // TODO: Make this _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_HIDDEN void destroy(__node_pointer __nd) _NOEXCEPT { (__tree_deleter(__node_alloc_))(__nd); }
+  _LIBCPP_HIDDEN _LIBCPP_CONSTEXPR_SINCE_CXX26 void destroy(__node_pointer __nd) _NOEXCEPT {
+    (__tree_deleter(__node_alloc_))(__nd);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign(__tree& __t, false_type);
-  _LIBCPP_HIDE_FROM_ABI void __move_assign(__tree& __t, true_type) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, false_type);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, true_type) _NOEXCEPT_(
       is_nothrow_move_assignable<value_compare>::value&& is_nothrow_move_assignable<__node_allocator>::value);
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__tree& __t)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t)
       _NOEXCEPT_(!__node_traits::propagate_on_container_move_assignment::value ||
                  is_nothrow_move_assignable<__node_allocator>::value) {
     __move_assign_alloc(__t, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__tree& __t, true_type)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t, true_type)
       _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value) {
     __node_alloc() = std::move(__t.__node_alloc());
   }
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__tree&, false_type) _NOEXCEPT {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree&, false_type) _NOEXCEPT {}
+
+  template <class _From, class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_value__sfinae(true_type, __get_node_value_type_t<value_type>& __lhs, _From&& __rhs) {
+    __node_allocator& __na = __node_alloc();
+    __node_traits::destroy(__na, std::addressof(__lhs));
+    __node_traits::construct(__na, std::addressof(__lhs), __rhs.first, __rhs.second);
+  }
+
+  template <class _From, class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_value__sfinae(false_type, __get_node_value_type_t<value_type>&, _From&&) {
+    // This method body should never be run. It only exists to allow for compilation. See note in __assign_value for
+    // more information.
+  }
 
   template <class _From, class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI static void __assign_value(__get_node_value_type_t<value_type>& __lhs, _From&& __rhs) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_value(__get_node_value_type_t<value_type>& __lhs, _From&& __rhs) {
     using __key_type = __remove_const_t<typename value_type::first_type>;
 
-    // This is technically UB, since the object was constructed as `const`.
-    // Clang doesn't optimize on this currently though.
-    const_cast<__key_type&>(__lhs.first) = const_cast<__copy_cvref_t<_From, __key_type>&&>(__rhs.first);
-    __lhs.second                         = std::forward<_From>(__rhs).second;
+#if _LIBCPP_STD_VER >= 26
+
+    if (std::is_constant_evaluated() && std::is_copy_constructible_v<decltype(__rhs.first)>) {
+      // We use copy, and not "move" as the constraint here because we can NOT move
+      // from `const key_type`, which is how `value_type` is defined for `std::map`
+      // `typedef pair<const key_type, mapped_type> value_type;`
+      // so we must copy it to not perform undefined behavior which
+      // is disallowed during constant evaluation.
+
+      // Furthermore, `const_cast` is not allowed during constant evaluation.
+      // We get around this by deleting `__lhs` and creating a new node in-place
+      // to avoid the `const_cast` when attempting to assign to `__lhs.first`.
+
+      // We create a sfinae wrapper method here, because if the body of the `true_type` overload for
+      // `__assign_value__sfinae()` gets template instantiated within `__assign_value`,
+      // the code will fail to compile when
+      // the value is not copy_constructible (even for runtime execution); unless we use `if constexpr`.
+      // Given that the copy-constructible code path will be a performance regression,
+      // we want to restrict it to only execute during constant evaluation
+      //, we need to delay the template instantiation.
+
+      __assign_value__sfinae(std::integral_constant<bool, std::is_copy_constructible_v<decltype(__rhs.first)>>(),
+                             std::forward<decltype(__lhs)>(__lhs),
+                             std::forward<decltype(__rhs)>(__rhs));
+
+    } else
+#endif
+    {
+      // This is technically UB, since the object was constructed as `const`.
+      // Clang doesn't optimize on this currently though.
+      const_cast<__key_type&>(__lhs.first) = const_cast<__copy_cvref_t<_From, __key_type>&&>(__rhs.first);
+      __lhs.second                         = std::forward<_From>(__rhs).second;
+    }
   }
 
   template <class _To, class _From, class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
@@ -1321,26 +1472,26 @@ private:
   }
 
   struct _DetachedTreeCache {
-    _LIBCPP_HIDE_FROM_ABI explicit _DetachedTreeCache(__tree* __t) _NOEXCEPT
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit _DetachedTreeCache(__tree* __t) _NOEXCEPT
         : __t_(__t),
           __cache_root_(__detach_from_tree(__t)) {
       __advance();
     }
 
-    _LIBCPP_HIDE_FROM_ABI __node_pointer __get() const _NOEXCEPT { return __cache_elem_; }
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __get() const _NOEXCEPT { return __cache_elem_; }
 
-    _LIBCPP_HIDE_FROM_ABI void __advance() _NOEXCEPT {
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __advance() _NOEXCEPT {
       __cache_elem_ = __cache_root_;
       if (__cache_root_) {
         __cache_root_ = __detach_next(__cache_root_);
       }
     }
 
-    _LIBCPP_HIDE_FROM_ABI ~_DetachedTreeCache() {
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~_DetachedTreeCache() {
       __t_->destroy(__cache_elem_);
       if (__cache_root_) {
         while (__cache_root_->__parent_ != nullptr)
-          __cache_root_ = static_cast<__node_pointer>(__cache_root_->__parent_);
+          __cache_root_ = std::__static_fancy_pointer_cast<__node_pointer>(__cache_root_->__parent_);
         __t_->destroy(__cache_root_);
       }
     }
@@ -1349,8 +1500,8 @@ private:
     _DetachedTreeCache& operator=(_DetachedTreeCache const&) = delete;
 
   private:
-    _LIBCPP_HIDE_FROM_ABI static __node_pointer __detach_from_tree(__tree* __t) _NOEXCEPT;
-    _LIBCPP_HIDE_FROM_ABI static __node_pointer __detach_next(__node_pointer) _NOEXCEPT;
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static __node_pointer __detach_from_tree(__tree* __t) _NOEXCEPT;
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static __node_pointer __detach_next(__node_pointer) _NOEXCEPT;
 
     __tree* __t_;
     __node_pointer __cache_root_;
@@ -1363,24 +1514,23 @@ private:
   public:
     using pointer = __node_pointer;
 
-    _LIBCPP_HIDE_FROM_ABI __tree_deleter(__node_allocator& __alloc) : __alloc_(__alloc) {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_deleter(__node_allocator& __alloc) : __alloc_(__alloc) {}
 
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
     _LIBCPP_HIDE_FROM_ABI
 #endif
-    void
-    operator()(__node_pointer __ptr) {
+    _LIBCPP_CONSTEXPR_SINCE_CXX26 void operator()(__node_pointer __ptr) {
       if (!__ptr)
         return;
 
-      (*this)(static_cast<__node_pointer>(__ptr->__left_));
+      (*this)(std::__static_fancy_pointer_cast<__node_pointer>(__ptr->__left_));
 
       auto __right = __ptr->__right_;
 
       __node_traits::destroy(__alloc_, std::addressof(__ptr->__get_value()));
       __node_traits::deallocate(__alloc_, __ptr, 1);
 
-      (*this)(static_cast<__node_pointer>(__right));
+      (*this)(std::__static_fancy_pointer_cast<__node_pointer>(__right));
     }
   };
 
@@ -1391,26 +1541,25 @@ private:
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
   _LIBCPP_HIDE_FROM_ABI
 #endif
-  __node_pointer
-  __copy_construct_tree(__node_pointer __src) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __copy_construct_tree(__node_pointer __src) {
     if (!__src)
       return nullptr;
 
     __node_holder __new_node = __construct_node(__src->__get_value());
 
     unique_ptr<__node, __tree_deleter> __left(
-        __copy_construct_tree(static_cast<__node_pointer>(__src->__left_)), __node_alloc_);
-    __node_pointer __right = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
+        __copy_construct_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__left_)), __node_alloc_);
+    __node_pointer __right = __copy_construct_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__right_));
 
     __node_pointer __new_node_ptr = __new_node.release();
 
     __new_node_ptr->__is_black_ = __src->__is_black_;
-    __new_node_ptr->__left_     = static_cast<__node_base_pointer>(__left.release());
-    __new_node_ptr->__right_    = static_cast<__node_base_pointer>(__right);
+    __new_node_ptr->__left_     = std::__static_fancy_pointer_cast<__node_base_pointer>(__left.release());
+    __new_node_ptr->__right_    = std::__static_fancy_pointer_cast<__node_base_pointer>(__right);
     if (__new_node_ptr->__left_)
-      __new_node_ptr->__left_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
+      __new_node_ptr->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__new_node_ptr);
     if (__new_node_ptr->__right_)
-      __new_node_ptr->__right_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
+      __new_node_ptr->__right_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__new_node_ptr);
     return __new_node_ptr;
   }
 
@@ -1420,8 +1569,7 @@ private:
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
   _LIBCPP_HIDE_FROM_ABI
 #endif
-  __node_pointer
-  __copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
     if (!__src) {
       destroy(__dest);
       return nullptr;
@@ -1432,24 +1580,26 @@ private:
 
     // If we already have a left node in the destination tree, reuse it and copy-assign recursively
     if (__dest->__left_) {
-      __dest->__left_ = static_cast<__node_base_pointer>(__copy_assign_tree(
-          static_cast<__node_pointer>(__dest->__left_), static_cast<__node_pointer>(__src->__left_)));
+      __dest->__left_ = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_assign_tree(
+          std::__static_fancy_pointer_cast<__node_pointer>(__dest->__left_),
+          std::__static_fancy_pointer_cast<__node_pointer>(__src->__left_)));
 
       // Otherwise, we must create new nodes; copy-construct from here on
     } else if (__src->__left_) {
-      auto __new_left       = __copy_construct_tree(static_cast<__node_pointer>(__src->__left_));
-      __dest->__left_       = static_cast<__node_base_pointer>(__new_left);
-      __new_left->__parent_ = static_cast<__end_node_pointer>(__dest);
+      auto __new_left       = __copy_construct_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__left_));
+      __dest->__left_       = std::__static_fancy_pointer_cast<__node_base_pointer>(__new_left);
+      __new_left->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__dest);
     }
 
     // Identical to the left case above, just for the right nodes
     if (__dest->__right_) {
-      __dest->__right_ = static_cast<__node_base_pointer>(__copy_assign_tree(
-          static_cast<__node_pointer>(__dest->__right_), static_cast<__node_pointer>(__src->__right_)));
+      __dest->__right_ = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_assign_tree(
+          std::__static_fancy_pointer_cast<__node_pointer>(__dest->__right_),
+          std::__static_fancy_pointer_cast<__node_pointer>(__src->__right_)));
     } else if (__src->__right_) {
-      auto __new_right       = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
-      __dest->__right_       = static_cast<__node_base_pointer>(__new_right);
-      __new_right->__parent_ = static_cast<__end_node_pointer>(__dest);
+      auto __new_right       = __copy_construct_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__right_));
+      __dest->__right_       = std::__static_fancy_pointer_cast<__node_base_pointer>(__new_right);
+      __new_right->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__dest);
     }
 
     return __dest;
@@ -1458,16 +1608,16 @@ private:
 
 // Precondition:  __size_ != 0
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_pointer
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_pointer
 __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_from_tree(__tree* __t) _NOEXCEPT {
-  __node_pointer __cache                = static_cast<__node_pointer>(__t->__begin_node_);
+  __node_pointer __cache                = std::__static_fancy_pointer_cast<__node_pointer>(__t->__begin_node_);
   __t->__begin_node_                    = __t->__end_node();
   __t->__end_node()->__left_->__parent_ = nullptr;
   __t->__end_node()->__left_            = nullptr;
   __t->__size_                          = 0;
   // __cache->__left_ == nullptr
   if (__cache->__right_ != nullptr)
-    __cache = static_cast<__node_pointer>(__cache->__right_);
+    __cache = std::__static_fancy_pointer_cast<__node_pointer>(__cache->__right_);
   // __cache->__left_ == nullptr
   // __cache->__right_ == nullptr
   return __cache;
@@ -1478,27 +1628,28 @@ __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_from_tree(__tree
 //    __cache->right_ == nullptr
 //    This is no longer a red-black tree
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_pointer
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_pointer
 __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_next(__node_pointer __cache) _NOEXCEPT {
   if (__cache->__parent_ == nullptr)
     return nullptr;
-  if (std::__tree_is_left_child(static_cast<__node_base_pointer>(__cache))) {
+  if (std::__tree_is_left_child(std::__static_fancy_pointer_cast<__node_base_pointer>(__cache))) {
     __cache->__parent_->__left_ = nullptr;
-    __cache                     = static_cast<__node_pointer>(__cache->__parent_);
+    __cache                     = std::__static_fancy_pointer_cast<__node_pointer>(__cache->__parent_);
     if (__cache->__right_ == nullptr)
       return __cache;
-    return static_cast<__node_pointer>(std::__tree_leaf(__cache->__right_));
+    return std::__static_fancy_pointer_cast<__node_pointer>(std::__tree_leaf(__cache->__right_));
   }
   // __cache is right child
   __cache->__parent_unsafe()->__right_ = nullptr;
-  __cache                              = static_cast<__node_pointer>(__cache->__parent_);
+  __cache                              = std::__static_fancy_pointer_cast<__node_pointer>(__cache->__parent_);
   if (__cache->__left_ == nullptr)
     return __cache;
-  return static_cast<__node_pointer>(std::__tree_leaf(__cache->__left_));
+  return std::__static_fancy_pointer_cast<__node_pointer>(std::__tree_leaf(__cache->__left_));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-__tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(const __tree& __t) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>&
+__tree<_Tp, _Compare, _Allocator>::operator=(const __tree& __t) {
   if (this == std::addressof(__t))
     return *this;
 
@@ -1506,14 +1657,15 @@ __tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(
   __copy_assign_alloc(__t);
 
   if (__size_ != 0) {
-    *__root_ptr() = static_cast<__node_base_pointer>(__copy_assign_tree(__root(), __t.__root()));
+    *__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_assign_tree(__root(), __t.__root()));
   } else {
-    *__root_ptr() = static_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
+    *__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
     if (__root())
       __root()->__parent_ = __end_node();
   }
-  __begin_node_ =
-      __end_node()->__left_ ? static_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_)) : __end_node();
+  __begin_node_ = __end_node()->__left_
+                    ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_))
+                    : __end_node();
   __size_ = __t.size();
 
   return *this;
@@ -1521,7 +1673,8 @@ __tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _ForwardIterator>
-void __tree<_Tp, _Compare, _Allocator>::__assign_unique(_ForwardIterator __first, _ForwardIterator __last) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void
+__tree<_Tp, _Compare, _Allocator>::__assign_unique(_ForwardIterator __first, _ForwardIterator __last) {
   using _ITraits     = iterator_traits<_ForwardIterator>;
   using _ItValueType = typename _ITraits::value_type;
   static_assert(
@@ -1541,7 +1694,8 @@ void __tree<_Tp, _Compare, _Allocator>::__assign_unique(_ForwardIterator __first
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _InputIterator>
-void __tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _InputIterator __last) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void
+__tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _InputIterator __last) {
   using _ITraits     = iterator_traits<_InputIterator>;
   using _ItValueType = typename _ITraits::value_type;
   static_assert(
@@ -1560,7 +1714,7 @@ void __tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-__tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
     : __begin_node_(__end_node()),
       __node_alloc_(__node_traits::select_on_container_copy_construction(__t.__node_alloc())),
       __size_(0),
@@ -1568,14 +1722,14 @@ __tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
   if (__t.size() == 0)
     return;
 
-  *__root_ptr()       = static_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
+  *__root_ptr()       = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
   __root()->__parent_ = __end_node();
-  __begin_node_       = static_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
+  __begin_node_       = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
   __size_             = __t.size();
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-__tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
     is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value)
     : __begin_node_(std::move(__t.__begin_node_)),
       __end_node_(std::move(__t.__end_node_)),
@@ -1585,7 +1739,7 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
   if (__size_ == 0)
     __begin_node_ = __end_node();
   else {
-    __end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
+    __end_node()->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__end_node());
     __t.__begin_node_                = __t.__end_node();
     __t.__end_node()->__left_        = nullptr;
     __t.__size_                      = 0;
@@ -1593,7 +1747,7 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-__tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __a)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __a)
     : __node_alloc_(__node_allocator(__a)), __size_(0), __value_comp_(std::move(__t.value_comp())) {
   if (__a == __t.__alloc()) {
     if (__t.__size_ == 0)
@@ -1601,7 +1755,7 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __
     else {
       __begin_node_                    = __t.__begin_node_;
       __end_node()->__left_            = __t.__end_node()->__left_;
-      __end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
+      __end_node()->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__end_node());
       __size_                          = __t.__size_;
       __t.__begin_node_                = __t.__end_node();
       __t.__end_node()->__left_        = nullptr;
@@ -1613,9 +1767,9 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
     _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value&& is_nothrow_move_assignable<__node_allocator>::value) {
-  destroy(static_cast<__node_pointer>(__end_node()->__left_));
+  destroy(std::__static_fancy_pointer_cast<__node_pointer>(__end_node()->__left_));
   __begin_node_ = __t.__begin_node_;
   __end_node_   = __t.__end_node_;
   __move_assign_alloc(__t);
@@ -1624,7 +1778,7 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
   if (__size_ == 0)
     __begin_node_ = __end_node();
   else {
-    __end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
+    __end_node()->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__end_node());
     __t.__begin_node_                = __t.__end_node();
     __t.__end_node()->__left_        = nullptr;
     __t.__size_                      = 0;
@@ -1632,7 +1786,7 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
   if (__node_alloc() == __t.__node_alloc())
     __move_assign(__t, true_type());
   else {
@@ -1653,7 +1807,7 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t)
 #if _LIBCPP_STD_VER <= 11
     _NOEXCEPT_(__is_nothrow_swappable_v<value_compare> &&
                (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>))
@@ -1678,7 +1832,7 @@ void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t)
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::clear() _NOEXCEPT {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::clear() _NOEXCEPT {
   destroy(__root());
   __size_               = 0;
   __begin_node_         = __end_node();
@@ -1689,23 +1843,23 @@ void __tree<_Tp, _Compare, _Allocator>::clear() _NOEXCEPT {
 // Set __parent to parent of null leaf
 // Return reference to null leaf
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
 __tree<_Tp, _Compare, _Allocator>::__find_leaf_low(__end_node_pointer& __parent, const value_type& __v) {
   __node_pointer __nd = __root();
   if (__nd != nullptr) {
     while (true) {
       if (value_comp()(__nd->__get_value(), __v)) {
         if (__nd->__right_ != nullptr)
-          __nd = static_cast<__node_pointer>(__nd->__right_);
+          __nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__right_);
         else {
-          __parent = static_cast<__end_node_pointer>(__nd);
+          __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
           return __nd->__right_;
         }
       } else {
         if (__nd->__left_ != nullptr)
-          __nd = static_cast<__node_pointer>(__nd->__left_);
+          __nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__left_);
         else {
-          __parent = static_cast<__end_node_pointer>(__nd);
+          __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
           return __parent->__left_;
         }
       }
@@ -1719,23 +1873,23 @@ __tree<_Tp, _Compare, _Allocator>::__find_leaf_low(__end_node_pointer& __parent,
 // Set __parent to parent of null leaf
 // Return reference to null leaf
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
 __tree<_Tp, _Compare, _Allocator>::__find_leaf_high(__end_node_pointer& __parent, const value_type& __v) {
   __node_pointer __nd = __root();
   if (__nd != nullptr) {
     while (true) {
       if (value_comp()(__v, __nd->__get_value())) {
         if (__nd->__left_ != nullptr)
-          __nd = static_cast<__node_pointer>(__nd->__left_);
+          __nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__left_);
         else {
-          __parent = static_cast<__end_node_pointer>(__nd);
+          __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
           return __parent->__left_;
         }
       } else {
         if (__nd->__right_ != nullptr)
-          __nd = static_cast<__node_pointer>(__nd->__right_);
+          __nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__right_);
         else {
-          __parent = static_cast<__end_node_pointer>(__nd);
+          __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
           return __nd->__right_;
         }
       }
@@ -1752,7 +1906,8 @@ __tree<_Tp, _Compare, _Allocator>::__find_leaf_high(__end_node_pointer& __parent
 // Set __parent to parent of null leaf
 // Return reference to null leaf
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer& __tree<_Tp, _Compare, _Allocator>::__find_leaf(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
+__tree<_Tp, _Compare, _Allocator>::__find_leaf(
     const_iterator __hint, __end_node_pointer& __parent, const value_type& __v) {
   if (__hint == end() || !value_comp()(*__hint, __v)) // check before
   {
@@ -1761,11 +1916,11 @@ typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer& __tree<_Tp, _Co
     if (__prior == begin() || !value_comp()(__v, *--__prior)) {
       // *prev(__hint) <= __v <= *__hint
       if (__hint.__ptr_->__left_ == nullptr) {
-        __parent = static_cast<__end_node_pointer>(__hint.__ptr_);
+        __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__hint.__ptr_);
         return __parent->__left_;
       } else {
-        __parent = static_cast<__end_node_pointer>(__prior.__ptr_);
-        return static_cast<__node_base_pointer>(__prior.__ptr_)->__right_;
+        __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__prior.__ptr_);
+        return std::__static_fancy_pointer_cast<__node_base_pointer>(__prior.__ptr_)->__right_;
       }
     }
     // __v < *prev(__hint)
@@ -1780,8 +1935,9 @@ typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer& __tree<_Tp, _Co
 // If __v doesn't exist, return the parent of the null leaf and a reference to the pointer to the null leaf.
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-_LIBCPP_HIDE_FROM_ABI pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
-                           typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
+_LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
+                                   typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
 __tree<_Tp, _Compare, _Allocator>::__find_equal(const _Key& __v) {
   using _Pair = pair<__end_node_pointer, __node_base_pointer&>;
 
@@ -1801,18 +1957,18 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const _Key& __v) {
 
     if (__comp_res.__less()) {
       if (__nd->__left_ == nullptr)
-        return _Pair(static_cast<__end_node_pointer>(__nd), __nd->__left_);
+        return _Pair(std::__static_fancy_pointer_cast<__end_node_pointer>(__nd), __nd->__left_);
 
       __node_ptr = std::addressof(__nd->__left_);
-      __nd       = static_cast<__node_pointer>(__nd->__left_);
+      __nd       = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__left_);
     } else if (__comp_res.__greater()) {
       if (__nd->__right_ == nullptr)
-        return _Pair(static_cast<__end_node_pointer>(__nd), __nd->__right_);
+        return _Pair(std::__static_fancy_pointer_cast<__end_node_pointer>(__nd), __nd->__right_);
 
       __node_ptr = std::addressof(__nd->__right_);
-      __nd       = static_cast<__node_pointer>(__nd->__right_);
+      __nd       = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__right_);
     } else {
-      return _Pair(static_cast<__end_node_pointer>(__nd), *__node_ptr);
+      return _Pair(std::__static_fancy_pointer_cast<__end_node_pointer>(__nd), *__node_ptr);
     }
   }
 }
@@ -1825,8 +1981,9 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const _Key& __v) {
 // If __v doesn't exist, return the parent of the null leaf and a reference to the pointer to the null leaf.
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-_LIBCPP_HIDE_FROM_ABI pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
-                           typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
+_LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
+                                   typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
 __tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_base_pointer& __dummy, const _Key& __v) {
   using _Pair = pair<__end_node_pointer, __node_base_pointer&>;
 
@@ -1837,7 +1994,7 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_ba
       // *prev(__hint) < __v < *__hint
       if (__hint.__ptr_->__left_ == nullptr)
         return _Pair(__hint.__ptr_, __hint.__ptr_->__left_);
-      return _Pair(__prior.__ptr_, static_cast<__node_pointer>(__prior.__ptr_)->__right_);
+      return _Pair(__prior.__ptr_, std::__static_fancy_pointer_cast<__node_pointer>(__prior.__ptr_)->__right_);
     }
     // __v <= *prev(__hint)
     return __find_equal(__v);
@@ -1849,7 +2006,7 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_ba
     if (__next == end() || value_comp()(__v, *__next)) {
       // *__hint < __v < *std::next(__hint)
       if (__hint.__get_np()->__right_ == nullptr)
-        return _Pair(__hint.__ptr_, static_cast<__node_pointer>(__hint.__ptr_)->__right_);
+        return _Pair(__hint.__ptr_, std::__static_fancy_pointer_cast<__node_pointer>(__hint.__ptr_)->__right_);
       return _Pair(__next.__ptr_, __next.__ptr_->__left_);
     }
     // *next(__hint) <= __v
@@ -1857,12 +2014,12 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_ba
   }
 
   // else __v == *__hint
-  __dummy = static_cast<__node_base_pointer>(__hint.__ptr_);
+  __dummy = std::__static_fancy_pointer_cast<__node_base_pointer>(__hint.__ptr_);
   return _Pair(__hint.__ptr_, __dummy);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::__insert_node_at(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__insert_node_at(
     __end_node_pointer __parent, __node_base_pointer& __child, __node_base_pointer __new_node) _NOEXCEPT {
   __new_node->__left_   = nullptr;
   __new_node->__right_  = nullptr;
@@ -1870,14 +2027,14 @@ void __tree<_Tp, _Compare, _Allocator>::__insert_node_at(
   // __new_node->__is_black_ is initialized in __tree_balance_after_insert
   __child = __new_node;
   if (__begin_node_->__left_ != nullptr)
-    __begin_node_ = static_cast<__end_node_pointer>(__begin_node_->__left_);
+    __begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__begin_node_->__left_);
   std::__tree_balance_after_insert(__end_node()->__left_, __child);
   ++__size_;
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class... _Args>
-typename __tree<_Tp, _Compare, _Allocator>::__node_holder
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_holder
 __tree<_Tp, _Compare, _Allocator>::__construct_node(_Args&&... __args) {
   __node_allocator& __na = __node_alloc();
   __node_holder __h(__node_traits::allocate(__na, 1), _Dp(__na));
@@ -1888,35 +2045,35 @@ __tree<_Tp, _Compare, _Allocator>::__construct_node(_Args&&... __args) {
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class... _Args>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+typename __tree<_Tp, _Compare, _Allocator>::iterator _LIBCPP_CONSTEXPR_SINCE_CXX26
 __tree<_Tp, _Compare, _Allocator>::__emplace_multi(_Args&&... __args) {
   __node_holder __h = __construct_node(std::forward<_Args>(__args)...);
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf_high(__parent, __h->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
-  return iterator(static_cast<__node_pointer>(__h.release()));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
+  return iterator(std::__static_fancy_pointer_cast<__node_pointer>(__h.release()));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class... _Args>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+typename __tree<_Tp, _Compare, _Allocator>::iterator _LIBCPP_CONSTEXPR_SINCE_CXX26
 __tree<_Tp, _Compare, _Allocator>::__emplace_hint_multi(const_iterator __p, _Args&&... __args) {
   __node_holder __h = __construct_node(std::forward<_Args>(__args)...);
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf(__p, __parent, __h->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
-  return iterator(static_cast<__node_pointer>(__h.release()));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
+  return iterator(std::__static_fancy_pointer_cast<__node_pointer>(__h.release()));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, bool>
+pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, bool> _LIBCPP_CONSTEXPR_SINCE_CXX26
 __tree<_Tp, _Compare, _Allocator>::__node_assign_unique(const value_type& __v, __node_pointer __nd) {
   auto [__parent, __child] = __find_equal(__v);
-  __node_pointer __r       = static_cast<__node_pointer>(__child);
+  __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
   bool __inserted          = false;
   if (__child == nullptr) {
     __assign_value(__nd->__get_value(), __v);
-    __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd));
+    __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd));
     __r        = __nd;
     __inserted = true;
   }
@@ -1924,39 +2081,39 @@ __tree<_Tp, _Compare, _Allocator>::__node_assign_unique(const value_type& __v, _
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_insert_multi(__node_pointer __nd) {
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf_high(__parent, __nd->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd));
   return iterator(__nd);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_insert_multi(const_iterator __p, __node_pointer __nd) {
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf(__p, __parent, __nd->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd));
   return iterator(__nd);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__remove_node_pointer(__node_pointer __ptr) _NOEXCEPT {
   iterator __r(__ptr);
   ++__r;
   if (__begin_node_ == __ptr)
     __begin_node_ = __r.__ptr_;
   --__size_;
-  std::__tree_remove(__end_node()->__left_, static_cast<__node_base_pointer>(__ptr));
+  std::__tree_remove(__end_node()->__left_, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
   return __r;
 }
 
 #if _LIBCPP_STD_VER >= 17
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle, class _InsertReturnType>
-_LIBCPP_HIDE_FROM_ABI _InsertReturnType
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _InsertReturnType
 __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __nh) {
   if (__nh.empty())
     return _InsertReturnType{end(), false, _NodeHandle()};
@@ -1964,16 +2121,17 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
   __node_pointer __ptr = __nh.__ptr_;
   auto [__parent, __child] = __find_equal(__ptr->__get_value());
   if (__child != nullptr)
-    return _InsertReturnType{iterator(static_cast<__node_pointer>(__child)), false, std::move(__nh)};
+    return _InsertReturnType{
+        iterator(std::__static_fancy_pointer_cast<__node_pointer>(__child)), false, std::move(__nh)};
 
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__ptr));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
   __nh.__release_ptr();
   return _InsertReturnType{iterator(__ptr), true, _NodeHandle()};
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __hint, _NodeHandle&& __nh) {
   if (__nh.empty())
     return end();
@@ -1981,9 +2139,9 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
   __node_pointer __ptr = __nh.__ptr_;
   __node_base_pointer __dummy;
   auto [__parent, __child] = __find_equal(__hint, __dummy, __ptr->__get_value());
-  __node_pointer __r       = static_cast<__node_pointer>(__child);
+  __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
   if (__child == nullptr) {
-    __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__ptr));
+    __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
     __r = __ptr;
     __nh.__release_ptr();
   }
@@ -1992,7 +2150,8 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(key_type const& __key) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(key_type const& __key) {
   iterator __it = find(__key);
   if (__it == end())
     return _NodeHandle();
@@ -2001,7 +2160,8 @@ _LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_hand
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const_iterator __p) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const_iterator __p) {
   __node_pointer __np = __p.__get_np();
   __remove_node_pointer(__np);
   return _NodeHandle(__np, __alloc());
@@ -2009,7 +2169,7 @@ _LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_hand
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Comp2>
-_LIBCPP_HIDE_FROM_ABI void
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
 __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source) {
   for (iterator __i = __source.begin(); __i != __source.end();) {
     __node_pointer __src_ptr = __i.__get_np();
@@ -2018,27 +2178,27 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_unique(__tree<_Tp, _Comp2
     if (__child != nullptr)
       continue;
     __source.__remove_node_pointer(__src_ptr);
-    __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__src_ptr));
+    __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__src_ptr));
   }
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_multi(_NodeHandle&& __nh) {
   if (__nh.empty())
     return end();
   __node_pointer __ptr = __nh.__ptr_;
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf_high(__parent, __ptr->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__ptr));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
   __nh.__release_ptr();
   return iterator(__ptr);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_multi(const_iterator __hint, _NodeHandle&& __nh) {
   if (__nh.empty())
     return end();
@@ -2046,14 +2206,14 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_multi(const_iterator __h
   __node_pointer __ptr = __nh.__ptr_;
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf(__hint, __parent, __ptr->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__ptr));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
   __nh.__release_ptr();
   return iterator(__ptr);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Comp2>
-_LIBCPP_HIDE_FROM_ABI void
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
 __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source) {
   for (iterator __i = __source.begin(); __i != __source.end();) {
     __node_pointer __src_ptr = __i.__get_np();
@@ -2061,14 +2221,15 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_multi(__tree<_Tp, _Comp2,
     __node_base_pointer& __child = __find_leaf_high(__parent, __src_ptr->__get_value());
     ++__i;
     __source.__remove_node_pointer(__src_ptr);
-    __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__src_ptr));
+    __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__src_ptr));
   }
 }
 
 #endif // _LIBCPP_STD_VER >= 17
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __p) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26
+    typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __p) {
   __node_pointer __np    = __p.__get_np();
   iterator __r           = __remove_node_pointer(__np);
   __node_allocator& __na = __node_alloc();
@@ -2078,7 +2239,7 @@ typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allo
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __f, const_iterator __l) {
   while (__f != __l)
     __f = erase(__f);
@@ -2087,7 +2248,7 @@ __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __f, const_iterator __l)
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::size_type
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
 __tree<_Tp, _Compare, _Allocator>::__erase_unique(const _Key& __k) {
   iterator __i = find(__k);
   if (__i == end())
@@ -2098,7 +2259,7 @@ __tree<_Tp, _Compare, _Allocator>::__erase_unique(const _Key& __k) {
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::size_type
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
 __tree<_Tp, _Compare, _Allocator>::__erase_multi(const _Key& __k) {
   pair<iterator, iterator> __p = __equal_range_multi(__k);
   size_type __r                = 0;
@@ -2109,16 +2270,16 @@ __tree<_Tp, _Compare, _Allocator>::__erase_multi(const _Key& __k) {
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::size_type
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
 __tree<_Tp, _Compare, _Allocator>::__count_unique(const _Key& __k) const {
   __node_pointer __rt = __root();
   auto __comp         = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp());
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __rt = static_cast<__node_pointer>(__rt->__left_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
       return 1;
   }
@@ -2127,7 +2288,7 @@ __tree<_Tp, _Compare, _Allocator>::__count_unique(const _Key& __k) const {
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::size_type
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
 __tree<_Tp, _Compare, _Allocator>::__count_multi(const _Key& __k) const {
   __end_node_pointer __result = __end_node();
   __node_pointer __rt         = __root();
@@ -2135,78 +2296,85 @@ __tree<_Tp, _Compare, _Allocator>::__count_multi(const _Key& __k) const {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
       return std::distance(
-          __lower_bound_multi(__k, static_cast<__node_pointer>(__rt->__left_), static_cast<__end_node_pointer>(__rt)),
-          __upper_bound_multi(__k, static_cast<__node_pointer>(__rt->__right_), __result));
+          __lower_bound_multi(__k,
+                              std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_),
+                              std::__static_fancy_pointer_cast<__end_node_pointer>(__rt)),
+          __upper_bound_multi(__k, std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_), __result));
   }
   return 0;
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
+__tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
     const _Key& __v, __node_pointer __root, __end_node_pointer __result) {
   while (__root != nullptr) {
     if (!value_comp()(__root->__get_value(), __v)) {
-      __result = static_cast<__end_node_pointer>(__root);
-      __root   = static_cast<__node_pointer>(__root->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
+      __root   = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
     } else
-      __root = static_cast<__node_pointer>(__root->__right_);
+      __root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
   }
   return iterator(__result);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::const_iterator __tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::const_iterator
+__tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
     const _Key& __v, __node_pointer __root, __end_node_pointer __result) const {
   while (__root != nullptr) {
     if (!value_comp()(__root->__get_value(), __v)) {
-      __result = static_cast<__end_node_pointer>(__root);
-      __root   = static_cast<__node_pointer>(__root->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
+      __root   = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
     } else
-      __root = static_cast<__node_pointer>(__root->__right_);
+      __root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
   }
   return const_iterator(__result);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
+__tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
     const _Key& __v, __node_pointer __root, __end_node_pointer __result) {
   while (__root != nullptr) {
     if (value_comp()(__v, __root->__get_value())) {
-      __result = static_cast<__end_node_pointer>(__root);
-      __root   = static_cast<__node_pointer>(__root->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
+      __root   = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
     } else
-      __root = static_cast<__node_pointer>(__root->__right_);
+      __root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
   }
   return iterator(__result);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::const_iterator __tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::const_iterator
+__tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
     const _Key& __v, __node_pointer __root, __end_node_pointer __result) const {
   while (__root != nullptr) {
     if (value_comp()(__v, __root->__get_value())) {
-      __result = static_cast<__end_node_pointer>(__root);
-      __root   = static_cast<__node_pointer>(__root->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
+      __root   = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
     } else
-      __root = static_cast<__node_pointer>(__root->__right_);
+      __root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
   }
   return const_iterator(__result);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
-__tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26
+    pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
+    __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) {
   using _Pp                   = pair<iterator, iterator>;
   __end_node_pointer __result = __end_node();
   __node_pointer __rt         = __root();
@@ -2214,22 +2382,23 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
       return _Pp(iterator(__rt),
-                 iterator(__rt->__right_ != nullptr ? static_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
-                                                    : __result));
+                 iterator(__rt->__right_ != nullptr
+                              ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
+                              : __result));
   }
   return _Pp(iterator(__result), iterator(__result));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
-     typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
+_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
+                                   typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
 __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) const {
   using _Pp                   = pair<const_iterator, const_iterator>;
   __end_node_pointer __result = __end_node();
@@ -2238,23 +2407,25 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) const {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
       return _Pp(
           const_iterator(__rt),
-          const_iterator(
-              __rt->__right_ != nullptr ? static_cast<__end_node_pointer>(std::__tree_min(__rt->__right_)) : __result));
+          const_iterator(__rt->__right_ != nullptr
+                             ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
+                             : __result));
   }
   return _Pp(const_iterator(__result), const_iterator(__result));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
-__tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26
+    pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
+    __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) {
   using _Pp                   = pair<iterator, iterator>;
   __end_node_pointer __result = __end_node();
   __node_pointer __rt         = __root();
@@ -2262,22 +2433,23 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
-      return _Pp(
-          __lower_bound_multi(__k, static_cast<__node_pointer>(__rt->__left_), static_cast<__end_node_pointer>(__rt)),
-          __upper_bound_multi(__k, static_cast<__node_pointer>(__rt->__right_), __result));
+      return _Pp(__lower_bound_multi(__k,
+                                     std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_),
+                                     std::__static_fancy_pointer_cast<__end_node_pointer>(__rt)),
+                 __upper_bound_multi(__k, std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_), __result));
   }
   return _Pp(iterator(__result), iterator(__result));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
-     typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
+_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
+                                   typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
 __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) const {
   using _Pp                   = pair<const_iterator, const_iterator>;
   __end_node_pointer __result = __end_node();
@@ -2286,35 +2458,37 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) const {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
-      return _Pp(
-          __lower_bound_multi(__k, static_cast<__node_pointer>(__rt->__left_), static_cast<__end_node_pointer>(__rt)),
-          __upper_bound_multi(__k, static_cast<__node_pointer>(__rt->__right_), __result));
+      return _Pp(__lower_bound_multi(__k,
+                                     std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_),
+                                     std::__static_fancy_pointer_cast<__end_node_pointer>(__rt)),
+                 __upper_bound_multi(__k, std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_), __result));
   }
   return _Pp(const_iterator(__result), const_iterator(__result));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_holder
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_holder
 __tree<_Tp, _Compare, _Allocator>::remove(const_iterator __p) _NOEXCEPT {
   __node_pointer __np = __p.__get_np();
   if (__begin_node_ == __p.__ptr_) {
     if (__np->__right_ != nullptr)
-      __begin_node_ = static_cast<__end_node_pointer>(__np->__right_);
+      __begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__np->__right_);
     else
-      __begin_node_ = static_cast<__end_node_pointer>(__np->__parent_);
+      __begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__np->__parent_);
   }
   --__size_;
-  std::__tree_remove(__end_node()->__left_, static_cast<__node_base_pointer>(__np));
+  std::__tree_remove(__end_node()->__left_, std::__static_fancy_pointer_cast<__node_base_pointer>(__np));
   return __node_holder(__np, _Dp(__node_alloc(), true));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI void swap(__tree<_Tp, _Compare, _Allocator>& __x, __tree<_Tp, _Compare, _Allocator>& __y)
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+swap(__tree<_Tp, _Compare, _Allocator>& __x, __tree<_Tp, _Compare, _Allocator>& __y)
     _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
   __x.swap(__y);
 }
diff --git a/libcxx/include/__type_traits/make_transparent.h b/libcxx/include/__type_traits/make_transparent.h
index 4d3207a807fa7..efcce0011f60d 100644
--- a/libcxx/include/__type_traits/make_transparent.h
+++ b/libcxx/include/__type_traits/make_transparent.h
@@ -33,12 +33,12 @@ template <class _Comparator>
 using __make_transparent_t _LIBCPP_NODEBUG = typename __make_transparent<_Comparator>::type;
 
 template <class _Comparator, __enable_if_t<is_same<_Comparator, __make_transparent_t<_Comparator> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Comparator& __as_transparent(_Comparator& __comp) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Comparator& __as_transparent(_Comparator& __comp) {
   return __comp;
 }
 
 template <class _Comparator, __enable_if_t<!is_same<_Comparator, __make_transparent_t<_Comparator> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI __make_transparent_t<_Comparator> __as_transparent(_Comparator&) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __make_transparent_t<_Comparator> __as_transparent(_Comparator&) {
   static_assert(is_empty<_Comparator>::value);
   return __make_transparent_t<_Comparator>();
 }
diff --git a/libcxx/include/__utility/default_three_way_comparator.h b/libcxx/include/__utility/default_three_way_comparator.h
index 92cdce6aae117..3f6bb1d7455b6 100644
--- a/libcxx/include/__utility/default_three_way_comparator.h
+++ b/libcxx/include/__utility/default_three_way_comparator.h
@@ -31,7 +31,7 @@ template <class _LHS, class _RHS>
 struct __default_three_way_comparator<_LHS,
                                       _RHS,
                                       __enable_if_t<is_arithmetic<_LHS>::value && is_arithmetic<_RHS>::value> > {
-  _LIBCPP_HIDE_FROM_ABI static int operator()(_LHS __lhs, _RHS __rhs) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static int operator()(_LHS __lhs, _RHS __rhs) {
     if (__lhs < __rhs)
       return -1;
     if (__lhs > __rhs)
diff --git a/libcxx/include/__utility/lazy_synth_three_way_comparator.h b/libcxx/include/__utility/lazy_synth_three_way_comparator.h
index 8c78742ccb4e3..3b2c7aa4de83f 100644
--- a/libcxx/include/__utility/lazy_synth_three_way_comparator.h
+++ b/libcxx/include/__utility/lazy_synth_three_way_comparator.h
@@ -34,14 +34,14 @@ struct __lazy_compare_result {
   const _LHS& __lhs_;
   const _RHS& __rhs_;
 
-  _LIBCPP_HIDE_FROM_ABI
-  __lazy_compare_result(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp,
-                        _LIBCPP_CTOR_LIFETIMEBOUND const _LHS& __lhs,
-                        _LIBCPP_CTOR_LIFETIMEBOUND const _RHS& __rhs)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __lazy_compare_result(
+      _LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp,
+      _LIBCPP_CTOR_LIFETIMEBOUND const _LHS& __lhs,
+      _LIBCPP_CTOR_LIFETIMEBOUND const _RHS& __rhs)
       : __comp_(__comp), __lhs_(__lhs), __rhs_(__rhs) {}
 
-  _LIBCPP_HIDE_FROM_ABI bool __less() const { return __comp_(__lhs_, __rhs_); }
-  _LIBCPP_HIDE_FROM_ABI bool __greater() const { return __comp_(__rhs_, __lhs_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __less() const { return __comp_(__lhs_, __rhs_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __greater() const { return __comp_(__rhs_, __lhs_); }
 };
 
 // This class provides three way comparison between _LHS and _RHS as efficiently as possible. This can be specialized if
@@ -51,10 +51,11 @@ template <class _Comparator, class _LHS, class _RHS, class = void>
 struct __lazy_synth_three_way_comparator {
   const _Comparator& __comp_;
 
-  _LIBCPP_HIDE_FROM_ABI __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp)
       : __comp_(__comp) {}
 
-  _LIBCPP_HIDE_FROM_ABI __lazy_compare_result<_Comparator, _LHS, _RHS>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __lazy_compare_result<_Comparator, _LHS, _RHS>
   operator()(_LIBCPP_LIFETIMEBOUND const _LHS& __lhs, _LIBCPP_LIFETIMEBOUND const _RHS& __rhs) const {
     return __lazy_compare_result<_Comparator, _LHS, _RHS>(__comp_, __lhs, __rhs);
   }
@@ -63,10 +64,10 @@ struct __lazy_synth_three_way_comparator {
 struct __eager_compare_result {
   int __res_;
 
-  _LIBCPP_HIDE_FROM_ABI explicit __eager_compare_result(int __res) : __res_(__res) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __eager_compare_result(int __res) : __res_(__res) {}
 
-  _LIBCPP_HIDE_FROM_ABI bool __less() const { return __res_ < 0; }
-  _LIBCPP_HIDE_FROM_ABI bool __greater() const { return __res_ > 0; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __less() const { return __res_ < 0; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __greater() const { return __res_ > 0; }
 };
 
 template <class _Comparator, class _LHS, class _RHS>
@@ -77,10 +78,11 @@ struct __lazy_synth_three_way_comparator<_Comparator,
                                                             __has_default_three_way_comparator<_LHS, _RHS> >::value> > {
   // This lifetimebound annotation is technically incorrect, but other specializations actually capture the lifetime of
   // the comparator.
-  _LIBCPP_HIDE_FROM_ABI __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}
 
   // Same comment as above.
-  _LIBCPP_HIDE_FROM_ABI static __eager_compare_result
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static __eager_compare_result
   operator()(_LIBCPP_LIFETIMEBOUND const _LHS& __lhs, _LIBCPP_LIFETIMEBOUND const _RHS& __rhs) {
     return __eager_compare_result(__default_three_way_comparator<_LHS, _RHS>()(__lhs, __rhs));
   }
@@ -94,10 +96,11 @@ struct __lazy_synth_three_way_comparator<_Comparator,
                                                             __has_default_three_way_comparator<_LHS, _RHS> >::value> > {
   // This lifetimebound annotation is technically incorrect, but other specializations actually capture the lifetime of
   // the comparator.
-  _LIBCPP_HIDE_FROM_ABI __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}
 
   // Same comment as above.
-  _LIBCPP_HIDE_FROM_ABI static __eager_compare_result
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static __eager_compare_result
   operator()(_LIBCPP_LIFETIMEBOUND const _LHS& __lhs, _LIBCPP_LIFETIMEBOUND const _RHS& __rhs) {
     return __eager_compare_result(-__default_three_way_comparator<_LHS, _RHS>()(__lhs, __rhs));
   }
diff --git a/libcxx/include/__utility/try_key_extraction.h b/libcxx/include/__utility/try_key_extraction.h
index 755c08214019f..3423d746dee7f 100644
--- a/libcxx/include/__utility/try_key_extraction.h
+++ b/libcxx/include/__utility/try_key_extraction.h
@@ -28,7 +28,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _KeyT, class _Ret, class _WithKey, class _WithoutKey, class... _Args>
-_LIBCPP_HIDE_FROM_ABI _Ret
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
 __try_key_extraction_impl(__priority_tag<0>, _WithKey, _WithoutKey __without_key, _Args&&... __args) {
   return __without_key(std::forward<_Args>(__args)...);
 }
@@ -39,7 +39,7 @@ template <class _KeyT,
           class _WithoutKey,
           class _Arg,
           __enable_if_t<is_same<_KeyT, __remove_const_ref_t<_Arg> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Ret
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
 __try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _Arg&& __arg) {
   return __with_key(__arg, std::forward<_Arg>(__arg));
 }
@@ -52,7 +52,7 @@ template <class _KeyT,
           __enable_if_t<__is_pair_v<__remove_const_ref_t<_Arg> > &&
                             is_same<__remove_const_t<typename __remove_const_ref_t<_Arg>::first_type>, _KeyT>::value,
                         int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Ret
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
 __try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _Arg&& __arg) {
   return __with_key(__arg.first, std::forward<_Arg>(__arg));
 }
@@ -64,7 +64,7 @@ template <class _KeyT,
           class _Arg1,
           class _Arg2,
           __enable_if_t<is_same<_KeyT, __remove_const_ref_t<_Arg1> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Ret
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
 __try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _Arg1&& __arg1, _Arg2&& __arg2) {
   return __with_key(__arg1, std::forward<_Arg1>(__arg1), std::forward<_Arg2>(__arg2));
 }
@@ -81,7 +81,7 @@ template <class _KeyT,
                             __is_tuple_v<_Tuple1> && tuple_size<_Tuple1>::value == 1 &&
                             is_same<__remove_const_ref_t<typename tuple_element<0, _Tuple1>::type>, _KeyT>::value,
                         int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Ret __try_key_extraction_impl(
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret __try_key_extraction_impl(
     __priority_tag<1>,
     _WithKey __with_key,
     _WithoutKey,
@@ -102,7 +102,7 @@ _LIBCPP_HIDE_FROM_ABI _Ret __try_key_extraction_impl(
 //
 // Both `__with_key` and `__without_key` must take all arguments by reference.
 template <class _KeyT, class _WithKey, class _WithoutKey, class... _Args>
-_LIBCPP_HIDE_FROM_ABI decltype(std::declval<_WithoutKey>()(std::declval<_Args>()...))
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 decltype(std::declval<_WithoutKey>()(std::declval<_Args>()...))
 __try_key_extraction(_WithKey __with_key, _WithoutKey __without_key, _Args&&... __args) {
   using _Ret = decltype(__without_key(std::forward<_Args>(__args)...));
   return std::__try_key_extraction_impl<_KeyT, _Ret>(
diff --git a/libcxx/test/support/is_transparent.h b/libcxx/test/support/is_transparent.h
index 9d3791d68a865..846f8fa4d06dd 100644
--- a/libcxx/test/support/is_transparent.h
+++ b/libcxx/test/support/is_transparent.h
@@ -81,16 +81,17 @@ struct transparent_less_not_a_type
 };
 
 struct C2Int { // comparable to int
-    C2Int() : i_(0) {}
-    C2Int(int i): i_(i) {}
-    int get () const { return i_; }
+  TEST_CONSTEXPR_CXX26 C2Int() : i_(0) {}
+  TEST_CONSTEXPR_CXX26 C2Int(int i) : i_(i) {}
+  TEST_CONSTEXPR_CXX26 int get() const { return i_; }
+
 private:
     int i_;
     };
 
-bool operator <(int          rhs,   const C2Int& lhs) { return rhs       < lhs.get(); }
-bool operator <(const C2Int& rhs,   const C2Int& lhs) { return rhs.get() < lhs.get(); }
-bool operator <(const C2Int& rhs,            int lhs) { return rhs.get() < lhs; }
+    TEST_CONSTEXPR_CXX26 bool operator<(int rhs, const C2Int& lhs) { return rhs < lhs.get(); }
+    TEST_CONSTEXPR_CXX26 bool operator<(const C2Int& rhs, const C2Int& lhs) { return rhs.get() < lhs.get(); }
+    TEST_CONSTEXPR_CXX26 bool operator<(const C2Int& rhs, int lhs) { return rhs.get() < lhs; }
 
 #endif // TEST_STD_VER > 11
 
diff --git a/libcxx/test/support/poisoned_hash_helper.h b/libcxx/test/support/poisoned_hash_helper.h
index cd71cd70d6a84..93b579d2dfde3 100644
--- a/libcxx/test/support/poisoned_hash_helper.h
+++ b/libcxx/test/support/poisoned_hash_helper.h
@@ -123,9 +123,13 @@ struct Class {};
 // Each header that declares the std::hash template provides enabled
 // specializations of std::hash for std::nullptr_t and all cv-unqualified
 // arithmetic, enumeration, and pointer types.
-using LibraryHashTypes =
-    types::concatenate_t<types::arithmetic_types,
-                         types::type_list<Enum, EnumClass, void*, void const*, Class*, std::nullptr_t>>;
+#if TEST_STD_VER >= 17
+using MaybeNullptr = types::type_list<std::nullptr_t>;
+#else
+using MaybeNullptr = types::type_list<>;
+#endif
+using LibraryHashTypes = types::
+    concatenate_t<types::arithmetic_types, types::type_list<Enum, EnumClass, void*, void const*, Class*>, MaybeNullptr>;
 
 struct TestHashEnabled {
   template <class T>
diff --git a/libcxx/test/support/private_constructor.h b/libcxx/test/support/private_constructor.h
index 24f540c6a7fdc..c7ed288c4032e 100644
--- a/libcxx/test/support/private_constructor.h
+++ b/libcxx/test/support/private_constructor.h
@@ -9,18 +9,22 @@
 #ifndef TEST_SUPPORT_PRIVATE_CONSTRUCTOR_H
 #define TEST_SUPPORT_PRIVATE_CONSTRUCTOR_H
 
+#include "test_macros.h"
+
 struct PrivateConstructor {
+  TEST_CONSTEXPR_CXX26 PrivateConstructor static make(int v) { return PrivateConstructor(v); }
+  TEST_CONSTEXPR_CXX26 int get() const { return val; }
 
-    PrivateConstructor static make ( int v ) { return PrivateConstructor(v); }
-    int get () const { return val; }
 private:
-    PrivateConstructor ( int v ) : val(v) {}
-    int val;
+  TEST_CONSTEXPR_CXX26 PrivateConstructor(int v) : val(v) {}
+  int val;
     };
 
-bool operator < ( const PrivateConstructor &lhs, const PrivateConstructor &rhs ) { return lhs.get() < rhs.get(); }
+    TEST_CONSTEXPR_CXX26 bool operator<(const PrivateConstructor& lhs, const PrivateConstructor& rhs) {
+      return lhs.get() < rhs.get();
+    }
 
-bool operator < ( const PrivateConstructor &lhs, int rhs ) { return lhs.get() < rhs; }
-bool operator < ( int lhs, const PrivateConstructor &rhs ) { return lhs < rhs.get(); }
+    TEST_CONSTEXPR_CXX26 bool operator<(const PrivateConstructor& lhs, int rhs) { return lhs.get() < rhs; }
+    TEST_CONSTEXPR_CXX26 bool operator<(int lhs, const PrivateConstructor& rhs) { return lhs < rhs.get(); }
 
 #endif // TEST_SUPPORT_PRIVATE_CONSTRUCTOR_H

>From 49d74e1a7cec61788ad5ca73ebbf040685e38cd9 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 15:22:15 -0500
Subject: [PATCH 02/21] Add test boilerplate for constexpr testing

---
 .../associative/iterator_types.pass.cpp       |  9 ++++-
 .../containers/associative/set/clear.pass.cpp | 11 +++++--
 .../associative/set/contains.pass.cpp         | 11 +++++--
 .../set/contains_transparent.pass.cpp         | 11 +++++--
 .../containers/associative/set/count.pass.cpp | 11 +++++--
 .../set/count_transparent.pass.cpp            | 11 +++++--
 .../associative/set/emplace.pass.cpp          | 11 +++++--
 .../associative/set/emplace_hint.pass.cpp     | 11 +++++--
 .../containers/associative/set/empty.pass.cpp | 11 +++++--
 .../associative/set/equal_range.pass.cpp      | 13 ++++++--
 .../set/equal_range_transparent.pass.cpp      | 17 +++++++---
 .../associative/set/erase_iter.pass.cpp       | 11 +++++--
 .../associative/set/erase_iter_iter.pass.cpp  | 11 +++++--
 .../associative/set/erase_key.pass.cpp        | 11 +++++--
 .../associative/set/extract_iterator.pass.cpp | 11 +++++--
 .../associative/set/extract_key.pass.cpp      | 11 +++++--
 .../containers/associative/set/find.pass.cpp  | 13 ++++++--
 .../associative/set/get_allocator.pass.cpp    | 11 +++++--
 .../associative/set/incomplete_type.pass.cpp  | 11 +++++--
 ...nd_emplace_allocator_requirements.pass.cpp | 15 ++++++---
 .../associative/set/insert_cv.pass.cpp        | 11 +++++--
 .../set/insert_initializer_list.pass.cpp      | 11 +++++--
 .../associative/set/insert_iter_cv.pass.cpp   | 11 +++++--
 .../associative/set/insert_iter_iter.pass.cpp | 11 +++++--
 .../associative/set/insert_iter_rv.pass.cpp   | 11 +++++--
 .../associative/set/insert_node_type.pass.cpp | 11 +++++--
 .../set/insert_node_type_hint.pass.cpp        | 11 +++++--
 .../associative/set/insert_range.pass.cpp     | 11 +++++--
 .../associative/set/insert_rv.pass.cpp        | 11 +++++--
 .../associative/set/iterator.pass.cpp         | 33 +++++++++++--------
 .../associative/set/lower_bound.pass.cpp      | 13 ++++++--
 .../associative/set/max_size.pass.cpp         | 11 +++++--
 .../containers/associative/set/merge.pass.cpp | 17 +++++++---
 .../associative/set/set.cons/alloc.pass.cpp   | 11 +++++--
 .../set.cons/assign_initializer_list.pass.cpp | 11 +++++--
 .../associative/set/set.cons/compare.pass.cpp | 11 +++++--
 .../set/set.cons/compare_alloc.pass.cpp       | 11 +++++--
 .../associative/set/set.cons/copy.pass.cpp    | 11 +++++--
 .../set/set.cons/copy_alloc.pass.cpp          | 11 +++++--
 .../set/set.cons/copy_assign.pass.cpp         | 11 +++++--
 .../associative/set/set.cons/deduct.pass.cpp  | 11 ++++++-
 .../associative/set/set.cons/default.pass.cpp | 11 +++++--
 .../set/set.cons/default_noexcept.pass.cpp    | 11 +++++--
 .../set/set.cons/dtor_noexcept.pass.cpp       | 11 +++++--
 .../set/set.cons/from_range.pass.cpp          | 10 +++++-
 .../set/set.cons/initializer_list.pass.cpp    | 11 +++++--
 .../initializer_list_compare.pass.cpp         | 11 +++++--
 .../initializer_list_compare_alloc.pass.cpp   | 13 ++++++--
 .../set/set.cons/iter_iter.pass.cpp           | 11 +++++--
 .../set/set.cons/iter_iter_alloc.pass.cpp     | 17 +++++++---
 .../set/set.cons/iter_iter_comp.pass.cpp      | 11 +++++--
 .../associative/set/set.cons/move.pass.cpp    | 11 +++++--
 .../set/set.cons/move_alloc.pass.cpp          | 11 +++++--
 .../set/set.cons/move_assign.pass.cpp         | 11 +++++--
 .../set/set.cons/move_noexcept.pass.cpp       | 13 ++++++--
 .../set/set.erasure/erase_if.pass.cpp         | 11 +++++--
 .../set.nonmember/compare.three_way.pass.cpp  | 13 ++++++--
 .../set/set.nonmember/op_compare.pass.cpp     | 33 +++++++++++--------
 .../set/set.observers/comp.pass.cpp           | 13 ++++++--
 .../set/set.special/member_swap.pass.cpp      | 11 +++++--
 .../set/set.special/non_member_swap.pass.cpp  | 11 +++++--
 .../set/set.special/swap_noexcept.pass.cpp    | 11 +++++--
 .../containers/associative/set/size.pass.cpp  | 11 +++++--
 .../containers/associative/set/types.pass.cpp |  9 ++++-
 .../associative/set/upper_bound.pass.cpp      | 13 ++++++--
 65 files changed, 625 insertions(+), 167 deletions(-)

diff --git a/libcxx/test/std/containers/associative/iterator_types.pass.cpp b/libcxx/test/std/containers/associative/iterator_types.pass.cpp
index 2800549905f1b..f394b000615d8 100644
--- a/libcxx/test/std/containers/associative/iterator_types.pass.cpp
+++ b/libcxx/test/std/containers/associative/iterator_types.pass.cpp
@@ -46,7 +46,7 @@ void testSet() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::map<int, int> Map;
     typedef std::pair<const int, int> ValueTp;
@@ -124,5 +124,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/clear.pass.cpp b/libcxx/test/std/containers/associative/set/clear.pass.cpp
index a53b6a86a660e..fbcde898950c0 100644
--- a/libcxx/test/std/containers/associative/set/clear.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/clear.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// void clear() noexcept;
+// constexpr void clear() noexcept; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef int V;
@@ -42,5 +42,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/contains.pass.cpp b/libcxx/test/std/containers/associative/set/contains.pass.cpp
index 93e6ff0a28885..20b1eeb839414 100644
--- a/libcxx/test/std/containers/associative/set/contains.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/contains.pass.cpp
@@ -13,7 +13,7 @@
 
 // <set>
 
-// bool contains(const key_type& x) const;
+// constexpr bool contains(const key_type& x) const; // constexpr since C++26
 
 template <typename T, typename V, typename B, typename... Vals>
 void test(B bad, Vals... args) {
@@ -34,7 +34,7 @@ struct E {
   char c   = 1;
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     test<std::set<int>, int>(14, 10, 11, 12, 13);
     test<std::set<char>, char>('e', 'a', 'b', 'c', 'd');
@@ -44,5 +44,12 @@ int main(int, char**) {
     test<std::multiset<char>, char>('e', 'a', 'b', 'c', 'd');
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
index c5d6b293e8a4b..acd03e649095f 100644
--- a/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// template<typename K> bool contains(const K& x) const; // C++20
+// template<typename K> constexpr bool contains(const K& x) const; // C++20 // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -36,9 +36,16 @@ void test() {
   assert(!s.contains(-1));
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test<std::set<std::pair<int, int>, Comp> >();
   test<std::multiset<std::pair<int, int>, Comp> >();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/count.pass.cpp b/libcxx/test/std/containers/associative/set/count.pass.cpp
index abd1e98f1a4ee..486220f1004c7 100644
--- a/libcxx/test/std/containers/associative/set/count.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/count.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// size_type count(const key_type& k) const;
+// constexpr size_type count(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -136,5 +136,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
index 4e0b610292460..9147b38e5d336 100644
--- a/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class set
 
 //    template<typename K>
-//        size_type count(const K& x) const;        // C++14
+//        constexpr size_type count(const K& x) const;        // C++14 // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -29,11 +29,18 @@ struct Comp {
   bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::set<std::pair<int, int>, Comp> s{{2, 1}, {1, 2}, {1, 3}, {1, 4}, {2, 2}};
 
   auto cnt = s.count(1);
   assert(cnt == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/emplace.pass.cpp b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
index e5a9f9590aad0..fecd9ef2fe7f9 100644
--- a/libcxx/test/std/containers/associative/set/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
@@ -13,7 +13,7 @@
 // class set
 
 // template <class... Args>
-//   pair<iterator, bool> emplace(Args&&... args);
+//   constexpr pair<iterator, bool> emplace(Args&&... args); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -23,7 +23,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<DefaultOnly> M;
     typedef std::pair<M::iterator, bool> R;
@@ -92,5 +92,12 @@ int main(int, char**) {
     assert(set.begin() == std::get<0>(res));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
index 9f0d5fbd2c5e5..5c68000ffbab9 100644
--- a/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
@@ -13,7 +13,7 @@
 // class set
 
 // template <class... Args>
-//   iterator emplace_hint(const_iterator position, Args&&... args);
+//   constexpr iterator emplace_hint(const_iterator position, Args&&... args); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "DefaultOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<DefaultOnly> M;
     typedef M::iterator R;
@@ -78,5 +78,12 @@ int main(int, char**) {
     assert(*r == 2);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/empty.pass.cpp b/libcxx/test/std/containers/associative/set/empty.pass.cpp
index aaefade5cff7b..e6bcf2a58d103 100644
--- a/libcxx/test/std/containers/associative/set/empty.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/empty.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// bool empty() const;
+// constexpr bool empty() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     M m;
@@ -40,5 +40,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/equal_range.pass.cpp b/libcxx/test/std/containers/associative/set/equal_range.pass.cpp
index 239aa3e878426..d59cfe2daee87 100644
--- a/libcxx/test/std/containers/associative/set/equal_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/equal_range.pass.cpp
@@ -10,8 +10,8 @@
 
 // class set
 
-// pair<iterator,iterator>             equal_range(const key_type& k);
-// pair<const_iterator,const_iterator> equal_range(const key_type& k) const;
+// constexpr pair<iterator,iterator>             equal_range(const key_type& k); // constexpr since C++26
+// constexpr pair<const_iterator,const_iterator> equal_range(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -327,5 +327,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
index a44fcf4b74a02..92c1c64be658c 100644
--- a/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
@@ -13,11 +13,11 @@
 // class set
 
 // template<typename K>
-//     pair<iterator,iterator>             equal_range(const K& x);        //
-//     C++14
+//     constexpr pair<iterator,iterator>  equal_range(const K& x);        //
+//     C++14, constexpr since C++26
 // template<typename K>
-//     pair<const_iterator,const_iterator> equal_range(const K& x) const;  //
-//     C++14
+//     constexpr pair<const_iterator,const_iterator> equal_range(const K& x) const;  //
+//     C++14, constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -33,7 +33,7 @@ struct Comp {
   bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::set<std::pair<int, int>, Comp> s{{2, 1}, {1, 2}, {1, 3}, {1, 4}, {2, 2}};
 
   auto er   = s.equal_range(1);
@@ -46,5 +46,12 @@ int main(int, char**) {
 
   assert(nels == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
index 73a4cc4266ec9..7682a81dc7407 100644
--- a/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// iterator erase(const_iterator position);
+// constexpr iterator erase(const_iterator position); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -25,7 +25,7 @@ struct TemplateConstructor {
 
 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef int V;
@@ -179,5 +179,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/erase_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/erase_iter_iter.pass.cpp
index 20c97c28fce56..9c3ba0abc990c 100644
--- a/libcxx/test/std/containers/associative/set/erase_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/erase_iter_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// iterator erase(const_iterator first, const_iterator last);
+// constexpr iterator erase(const_iterator first, const_iterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef int V;
@@ -118,5 +118,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/erase_key.pass.cpp b/libcxx/test/std/containers/associative/set/erase_key.pass.cpp
index 318c35d21cd87..abf82be8a1bcf 100644
--- a/libcxx/test/std/containers/associative/set/erase_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/erase_key.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// size_type erase(const key_type& k);
+// constexpr size_type erase(const key_type& k); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef int V;
@@ -180,5 +180,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
index 2d7b6e521d961..1511b08079d03 100644
--- a/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// node_type extract(const_iterator);
+// constexpr node_type extract(const_iterator); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -35,7 +35,7 @@ void test(Container& c) {
   assert(c.size() == 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     using set_type = std::set<int>;
     set_type m     = {1, 2, 3, 4, 5, 6};
@@ -55,5 +55,12 @@ int main(int, char**) {
     test(m);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/extract_key.pass.cpp b/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
index 0802387f2c9b3..07f6d193179a8 100644
--- a/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// node_type extract(key_type const&);
+// constexpr node_type extract(key_type const&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -41,7 +41,7 @@ void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::set<int> m = {1, 2, 3, 4, 5, 6};
     int keys[]      = {1, 2, 3, 4, 5, 6};
@@ -65,5 +65,12 @@ int main(int, char**) {
     test(m, std::begin(keys), std::end(keys));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/find.pass.cpp b/libcxx/test/std/containers/associative/set/find.pass.cpp
index deb193c17bfa9..e88441ed8927d 100644
--- a/libcxx/test/std/containers/associative/set/find.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/find.pass.cpp
@@ -10,8 +10,8 @@
 
 // class set
 
-//       iterator find(const key_type& k);
-// const_iterator find(const key_type& k) const;
+// constexpr       iterator find(const key_type& k); // constexpr since C++26
+// constexpr const_iterator find(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -202,5 +202,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/get_allocator.pass.cpp b/libcxx/test/std/containers/associative/set/get_allocator.pass.cpp
index 0270c9e82ab0e..a2bca7af5bd41 100644
--- a/libcxx/test/std/containers/associative/set/get_allocator.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/get_allocator.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// allocator_type get_allocator() const
+// constexpr allocator_type get_allocator() const // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::allocator<int> alloc;
     const std::set<int> s(alloc);
@@ -29,5 +29,12 @@ int main(int, char**) {
     const std::set<int, std::less<int>, other_allocator<int> > s(alloc);
     assert(s.get_allocator() == alloc);
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/incomplete_type.pass.cpp b/libcxx/test/std/containers/associative/set/incomplete_type.pass.cpp
index acbda713619bd..e9db3b31f14a4 100644
--- a/libcxx/test/std/containers/associative/set/incomplete_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/incomplete_type.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// <set>
+// <set> // constexpr since C++26
 
 // Check that std::set and its iterators can be instantiated with an incomplete
 // type.
@@ -25,8 +25,15 @@ struct A {
 
 inline bool operator==(A const& L, A const& R) { return &L == &R; }
 inline bool operator<(A const& L, A const& R) { return L.data < R.data; }
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   A a;
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
index 4711473de4dad..ace31eb66ea8d 100644
--- a/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
@@ -10,9 +10,9 @@
 
 // class set
 
-// insert(...)
-// emplace(...)
-// emplace_hint(...)
+// constexpr insert(...) // constexpr since C++26
+// constexpr emplace(...) // constexpr since C++26
+// constexpr emplace_hint(...) // constexpr since C++26
 
 // UNSUPPORTED: c++03
 
@@ -21,10 +21,17 @@
 #include "container_test_types.h"
 #include "../../set_allocator_requirement_test_templates.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   testSetInsert<TCT::set<> >();
   testSetEmplace<TCT::set<> >();
   testSetEmplaceHint<TCT::set<> >();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
index 2683f2924e760..ef278224f0229 100644
--- a/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// pair<iterator, bool> insert(const value_type& v);
+// constexpr pair<iterator, bool> insert(const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -53,7 +53,7 @@ void do_insert_cv_test() {
   assert(*r.first == 3);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   do_insert_cv_test<std::set<int> >();
 #if TEST_STD_VER >= 11
   {
@@ -62,5 +62,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/set/insert_initializer_list.pass.cpp
index f3b69a59987e8..9ebf723e2af44 100644
--- a/libcxx/test/std/containers/associative/set/insert_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// void insert(initializer_list<value_type> il);
+// constexpr void insert(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> C;
     typedef C::value_type V;
@@ -57,5 +57,12 @@ int main(int, char**) {
     assert(*++i == V(10));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_iter_cv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_cv.pass.cpp
index 159487ee0e479..506e06d23bc43 100644
--- a/libcxx/test/std/containers/associative/set/insert_iter_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_iter_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// iterator insert(const_iterator position, const value_type& v);
+// constexpr iterator insert(const_iterator position, const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef M::iterator R;
@@ -70,5 +70,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
index 8576c63a72de9..6d6bd185afa04 100644
--- a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
@@ -11,7 +11,7 @@
 // class set
 
 // template <class InputIterator>
-//   void insert(InputIterator first, InputIterator last);
+//   constexpr void insert(InputIterator first, InputIterator last); // constexpr since C++26
 
 #include <array>
 #include <cassert>
@@ -183,8 +183,15 @@ void test() {
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_iter_rv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_rv.pass.cpp
index 21358f3c40c9c..73031dfae5fd7 100644
--- a/libcxx/test/std/containers/associative/set/insert_iter_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_iter_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// iterator insert(const_iterator position, value_type&& v);
+// constexpr iterator insert(const_iterator position, value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<MoveOnly> M;
     typedef M::iterator R;
@@ -72,5 +72,12 @@ int main(int, char**) {
     assert(*r == 3);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
index 9794b4da989d9..935a90afedb2b 100644
--- a/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// insert_return_type insert(node_type&&);
+// constexpr insert_return_type insert(node_type&&); // constexpr since C++26
 
 #include <memory>
 #include <set>
@@ -94,11 +94,18 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::set<int> m;
   test(m);
   std::set<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
index 1449be0030fe5..f14263595ac6a 100644
--- a/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// iterator insert(const_iterator hint, node_type&&);
+// constexpr iterator insert(const_iterator hint, node_type&&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -47,11 +47,18 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::set<int> m;
   test(m);
   std::set<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
index e6c54d84d6e40..654d4d425774a 100644
--- a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
@@ -13,14 +13,14 @@
 // <set>
 
 // template<container-compatible-range<value_type> R>
-//   void insert_range(R&& rg); // C++23
+//   constexpr void insert_range(R&& rg); // C++23, constexpr since C++26
 
 #include <set>
 
 #include "../../insert_range_maps_sets.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
     test_map_set_insert_range<std::set<int, test_less<int>, Alloc>, int, Iter, Sent>();
   });
@@ -32,5 +32,12 @@ int main(int, char**) {
   test_set_insert_range_exception_safety_throwing_copy<std::set>();
   test_assoc_set_insert_range_exception_safety_throwing_allocator<std::set, int>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_rv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_rv.pass.cpp
index ec18c4379b58d..9b01722faadaa 100644
--- a/libcxx/test/std/containers/associative/set/insert_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// pair<iterator, bool> insert(value_type&& v);
+// constexpr pair<iterator, bool> insert(value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<MoveOnly> M;
     typedef std::pair<M::iterator, bool> R;
@@ -79,5 +79,12 @@ int main(int, char**) {
     assert(*r.first == 3);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/iterator.pass.cpp b/libcxx/test/std/containers/associative/set/iterator.pass.cpp
index 1a18cbc426478..467d88e291a02 100644
--- a/libcxx/test/std/containers/associative/set/iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/iterator.pass.cpp
@@ -10,20 +10,20 @@
 
 // class set
 
-//       iterator begin();
-// const_iterator begin() const;
-//       iterator end();
-// const_iterator end()   const;
+//       constexpr iterator begin(); // constexpr since C++26
+// constexpr const_iterator begin() const; // constexpr since C++26
+//       constexpr iterator end(); // constexpr since C++26
+// constexpr const_iterator end()   const; // constexpr since C++26
 //
-//       reverse_iterator rbegin();
-// const_reverse_iterator rbegin() const;
-//       reverse_iterator rend();
-// const_reverse_iterator rend()   const;
+//       constexpr reverse_iterator rbegin(); // constexpr since C++26
+// constexpr const_reverse_iterator rbegin() const; // constexpr since C++26
+//       constexpr reverse_iterator rend(); // constexpr since C++26
+// constexpr const_reverse_iterator rend()   const; // constexpr since C++26
 //
-// const_iterator         cbegin()  const;
-// const_iterator         cend()    const;
-// const_reverse_iterator crbegin() const;
-// const_reverse_iterator crend()   const;
+// constexpr const_iterator         cbegin()  const; // constexpr since C++26
+// constexpr const_iterator         cend()    const; // constexpr since C++26
+// constexpr const_reverse_iterator crbegin() const; // constexpr since C++26
+// constexpr const_reverse_iterator crend()   const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -32,7 +32,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8};
@@ -129,5 +129,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/lower_bound.pass.cpp b/libcxx/test/std/containers/associative/set/lower_bound.pass.cpp
index 2f114685fb84d..dcaa3b17c8fcc 100644
--- a/libcxx/test/std/containers/associative/set/lower_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/lower_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class set
 
-//       iterator lower_bound(const key_type& k);
-// const_iterator lower_bound(const key_type& k) const;
+//       constexpr iterator lower_bound(const key_type& k); // constexpr since C++26
+// constexpr const_iterator lower_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -283,5 +283,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/max_size.pass.cpp b/libcxx/test/std/containers/associative/set/max_size.pass.cpp
index b00d316c47b35..f11eda16312e5 100644
--- a/libcxx/test/std/containers/associative/set/max_size.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/max_size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// size_type max_size() const;
+// constexpr size_type max_size() const; // constexpr since C++26
 
 #include <cassert>
 #include <limits>
@@ -20,7 +20,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef limited_allocator<int, 10> A;
     typedef std::set<int, std::less<int>, A> C;
@@ -44,5 +44,12 @@ int main(int, char**) {
     assert(c.max_size() <= alloc_max_size(c.get_allocator()));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/merge.pass.cpp b/libcxx/test/std/containers/associative/set/merge.pass.cpp
index 50141d5444b1f..4c76c59ec5de5 100644
--- a/libcxx/test/std/containers/associative/set/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/merge.pass.cpp
@@ -13,13 +13,13 @@
 // class set
 
 // template <class C2>
-//   void merge(set<key_type, C2, allocator_type>& source);
+//   constexpr void merge(set<key_type, C2, allocator_type>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(set<key_type, C2, allocator_type>&& source);
+//   constexpr void merge(set<key_type, C2, allocator_type>&& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<key_type, C2, allocator_type>& source);
+//   constexpr void merge(multiset<key_type, C2, allocator_type>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<key_type, C2, allocator_type>&& source);
+//   constexpr void merge(multiset<key_type, C2, allocator_type>&& source); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -46,7 +46,7 @@ struct throw_comparator {
 };
 #endif
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::set<int> src{1, 3, 5};
     std::set<int> dst{2, 4, 5};
@@ -134,5 +134,12 @@ int main(int, char**) {
       first.merge(std::move(second));
     }
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/alloc.pass.cpp
index 4321666044a3b..539d6e11e6b6f 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set(const allocator_type& a);
+// constexpr set(const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::less<int> C;
   typedef test_allocator<int> A;
   std::set<int, C, A> m(A(5));
@@ -26,5 +26,12 @@ int main(int, char**) {
   assert(m.begin() == m.end());
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
index d674669fd2559..1e0de1434aeb9 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set& operator=(initializer_list<value_type> il);
+// constexpr set& operator=(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -69,9 +69,16 @@ void duplicate_keys_test() {
   LIBCPP_ASSERT(alloc_stats.alloc_count == 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   basic_test();
   duplicate_keys_test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/compare.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/compare.pass.cpp
index 03452142f5b85..5b3abfe1fcb3b 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/compare.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// explicit set(const key_compare& comp) const;
+// constexpr explicit set(const key_compare& comp) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   const std::set<int, C> m(C(3));
   assert(m.empty());
@@ -26,5 +26,12 @@ int main(int, char**) {
   assert(m.key_comp() == C(3));
   assert(m.value_comp() == C(3));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/compare_alloc.pass.cpp
index 2ae0547054537..ba358f1c95412 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/compare_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set(const value_compare& comp, const allocator_type& a);
+// constexpr set(const value_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   typedef test_allocator<int> A;
   std::set<int, C, A> m(C(4), A(5));
@@ -28,5 +28,12 @@ int main(int, char**) {
   assert(m.key_comp() == C(4));
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
index 8fac335175c80..f2116c019d3b6 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set(const set& m);
+// constexpr set(const set& m); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -127,8 +127,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
index 2784af65b0a0f..200bf71b5f3dd 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set(const set& m, const allocator_type& a);
+// constexpr set(const set& m, const allocator_type& a); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -99,8 +99,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
index dd1dbc86190f8..7c301d62cb586 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set& operator=(const set& s);
+// constexpr set& operator=(const set& s); // constexpr since C++26
 
 #include <algorithm>
 #include <cassert>
@@ -277,8 +277,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
index e26df4503ed41..56540fbf35c6a 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
@@ -37,6 +37,8 @@
 //   set(from_range_t, R&&, Allocator)
 //     -> set<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>; // C++23
 
+// constexpr since C++26
+
 #include <algorithm> // std::equal
 #include <array>
 #include <cassert>
@@ -52,7 +54,7 @@ struct NotAnAllocator {
   friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     const int arr[] = {1, 2, 1, INT_MAX, 3};
     std::set s(std::begin(arr), std::end(arr));
@@ -212,5 +214,12 @@ int main(int, char**) {
 
   AssociativeContainerDeductionGuidesSfinaeAway<std::set, std::set<int>>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/default.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/default.pass.cpp
index c41d7c3b9d3bd..6ae5d003fef15 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/default.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/default.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set();
+// constexpr set(); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::set<int> m;
     assert(m.empty());
@@ -51,5 +51,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/default_noexcept.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/default_noexcept.pass.cpp
index c01ac3b2fad0b..4b2ddd174519d 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/default_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/default_noexcept.pass.cpp
@@ -8,7 +8,7 @@
 
 // <set>
 
-// set()
+// constexpr set() // constexpr since C++26
 //    noexcept(
 //        is_nothrow_default_constructible<allocator_type>::value &&
 //        is_nothrow_default_constructible<key_compare>::value &&
@@ -32,7 +32,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::set<MoveOnly> C;
@@ -52,5 +52,12 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_default_constructible<C>::value, "");
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/dtor_noexcept.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/dtor_noexcept.pass.cpp
index 63c0433477414..15f3c257af272 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/dtor_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/dtor_noexcept.pass.cpp
@@ -8,7 +8,7 @@
 
 // <set>
 
-// ~set() // implied noexcept;
+// constexpr ~set() // implied noexcept; // constexpr since C++26
 
 // UNSUPPORTED: c++03
 
@@ -26,7 +26,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<MoveOnly> C;
     static_assert(std::is_nothrow_destructible<C>::value, "");
@@ -46,5 +46,12 @@ int main(int, char**) {
   }
 #endif // _LIBCPP_VERSION
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
index 3590b75065ad9..23a5136aafcf2 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
@@ -14,6 +14,7 @@
 // template<container-compatible-range<value_type> R>
 //   set(from_range_t, R&& rg, const Allocator& a))
 //     : set(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+// constexpr since C++26
 
 #include <array>
 #include <set>
@@ -30,7 +31,7 @@ void test_duplicates() {
   assert(std::ranges::is_permutation(expected, c));
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
     test_associative_set<std::set, int, Iter, Sent, test_less<int>, Alloc>();
   });
@@ -42,5 +43,12 @@ int main(int, char**) {
   test_set_exception_safety_throwing_copy<std::set>();
   test_set_exception_safety_throwing_allocator<std::set, int>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/initializer_list.pass.cpp
index 48495712425be..974d2d96b02d7 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// constexpr set(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> C;
     typedef C::value_type V;
@@ -50,5 +50,12 @@ int main(int, char**) {
     assert(*++i == V(6));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare.pass.cpp
index 25684966078d6..3572465b9118e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// constexpr set(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> Cmp;
   typedef std::set<int, Cmp> C;
   typedef C::value_type V;
@@ -37,5 +37,12 @@ int main(int, char**) {
   assert(*++i == V(6));
   assert(m.key_comp() == Cmp(10));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare_alloc.pass.cpp
index 48649c3b281f4..737e96bfad9f5 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare_alloc.pass.cpp
@@ -12,8 +12,8 @@
 
 // class set
 
-// set(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a);
-// set(initializer_list<value_type> il, const allocator_type& a);
+// constexpr set(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a); // constexpr since C++26
+// constexpr set(initializer_list<value_type> il, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef test_less<int> Cmp;
     typedef test_allocator<int> A;
@@ -60,5 +60,12 @@ int main(int, char**) {
     assert(m.get_allocator() == A(4));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/iter_iter.pass.cpp
index 27a4e6a5f527d..4e49265811b19 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/iter_iter.pass.cpp
@@ -11,7 +11,7 @@
 // class set
 
 // template <class InputIterator>
-//     set(InputIterator first, InputIterator last);
+//     constexpr set(InputIterator first, InputIterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_iterators.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -46,5 +46,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
index dac553073f177..5a4c9d156a3b6 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
@@ -11,12 +11,12 @@
 // class set
 
 // template <class InputIterator>
-//     set(InputIterator first, InputIterator last,
-//         const value_compare& comp, const allocator_type& a);
+//     constexpr set(InputIterator first, InputIterator last,
+//         const value_compare& comp, const allocator_type& a); // constexpr since C++26
 //
 // template <class InputIterator>
-//     set(InputIterator first, InputIterator last,
-//         const allocator_type& a);
+//     constexpr set(InputIterator first, InputIterator last,
+//         const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -26,7 +26,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -63,5 +63,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_comp.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_comp.pass.cpp
index 573bbd316e073..204c86a62590e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_comp.pass.cpp
@@ -11,7 +11,7 @@
 // class set
 
 // template <class InputIterator>
-//     set(InputIterator first, InputIterator last, const value_compare& comp);
+//     constexpr set(InputIterator first, InputIterator last, const value_compare& comp); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_iterators.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
   typedef test_less<V> C;
@@ -33,5 +33,12 @@ int main(int, char**) {
   assert(*std::next(m.begin()) == 2);
   assert(*std::next(m.begin(), 2) == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move.pass.cpp
index 2c1ec22ab257d..a4e68ff583d01 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set(set&& s);
+// constexpr set(set&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef test_less<int> C;
@@ -82,5 +82,12 @@ int main(int, char**) {
     assert(std::distance(mo.begin(), mo.end()) == 0);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
index c85463bd391ca..4f15202f2ca83 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set(set&& s, const allocator_type& a);
+// constexpr set(set&& s, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -24,7 +24,7 @@
 #include "test_allocator.h"
 #include "Counter.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -111,5 +111,12 @@ int main(int, char**) {
     assert(Counter_base::gConstructed == 0);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move_assign.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move_assign.pass.cpp
index bacd58c172a12..418c4d06ca114 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move_assign.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set& operator=(set&& s);
+// constexpr set& operator=(set&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -93,5 +93,12 @@ int main(int, char**) {
     assert(m1.empty());
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move_noexcept.pass.cpp
index 9325b8d1f40de..fa315e392ce0c 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move_noexcept.pass.cpp
@@ -8,9 +8,9 @@
 
 // <set>
 
-// set(set&&)
+// constexpr set(set&&)
 //        noexcept(is_nothrow_move_constructible<allocator_type>::value &&
-//                 is_nothrow_move_constructible<key_compare>::value);
+//                 is_nothrow_move_constructible<key_compare>::value); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -30,7 +30,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::set<MoveOnly> C;
@@ -50,5 +50,12 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_move_constructible<C>::value, "");
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
index b076b31059610..978e80821eb49 100644
--- a/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
@@ -11,7 +11,7 @@
 
 // template <class T, class Compare, class Allocator, class Predicate>
 //   typename set<T, Compare, Allocator>::size_type
-//   erase_if(set<T, Compare, Allocator>& c, Predicate pred);
+//   constexpr erase_if(set<T, Compare, Allocator>& c, Predicate pred); // constexpr since C++26
 
 #include <set>
 
@@ -53,7 +53,7 @@ void test() {
   test0(S({1, 2, 3}), False, S({1, 2, 3}), 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test<std::set<int>>();
   test<std::set<int, std::less<int>, min_allocator<int>>>();
   test<std::set<int, std::less<int>, test_allocator<int>>>();
@@ -61,5 +61,12 @@ int main(int, char**) {
   test<std::set<long>>();
   test<std::set<double>>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
index dc513d96e5ba8..563a8c12db828 100644
--- a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
@@ -12,16 +12,23 @@
 // class set
 
 // template<class Key, class Compare, class Allocator>
-//   synth-three-way-result<Key> operator<=>(const set<Key, Compare, Allocator>& x,
-//                                           const set<Key, Compare, Allocator>& y);
+//   constexpr synth-three-way-result<Key> operator<=>(const set<Key, Compare, Allocator>& x,
+//                                           const set<Key, Compare, Allocator>& y); // constexpr since C++26
 
 #include <cassert>
 #include <set>
 
 #include "test_container_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   assert((test_ordered_set_container_spaceship<std::set>()));
   // `std::set` is not constexpr, so no `static_assert` test here.
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.nonmember/op_compare.pass.cpp b/libcxx/test/std/containers/associative/set/set.nonmember/op_compare.pass.cpp
index 1be07ce885298..547cc59c51880 100644
--- a/libcxx/test/std/containers/associative/set/set.nonmember/op_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.nonmember/op_compare.pass.cpp
@@ -9,28 +9,28 @@
 // <set>
 
 // template<class Key, class Compare, class Alloc>
-// bool operator==(const std::set<Key, Compare, Alloc>& lhs,
-//                 const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator==(const std::set<Key, Compare, Alloc>& lhs,
+//                 const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator!=(const std::set<Key, Compare, Alloc>& lhs,
-//                 const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator!=(const std::set<Key, Compare, Alloc>& lhs,
+//                 const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator<(const std::set<Key, Compare, Alloc>& lhs,
-//                const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator<(const std::set<Key, Compare, Alloc>& lhs,
+//                const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator>(const std::set<Key, Compare, Alloc>& lhs,
-//                const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator>(const std::set<Key, Compare, Alloc>& lhs,
+//                const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator<=(const std::set<Key, Compare, Alloc>& lhs,
-//                 const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator<=(const std::set<Key, Compare, Alloc>& lhs,
+//                 const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator>=(const std::set<Key, Compare, Alloc>& lhs,
-//                 const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator>=(const std::set<Key, Compare, Alloc>& lhs,
+//                 const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -38,7 +38,7 @@
 
 #include "test_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::set<int> s1, s2;
     s1.insert(1);
@@ -61,5 +61,12 @@ int main(int, char**) {
     const std::set<int>&cs1 = s1, cs2 = s2;
     assert(testComparisons(cs1, cs2, false, true));
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
index 019dae3955acb..9aef8c7cce41b 100644
--- a/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
@@ -8,13 +8,13 @@
 
 // <set>
 
-// key_compare key_comp() const;
-// value_compare value_comp() const;
+// constexpr key_compare key_comp() const; // constexpr since C++26
+// constexpr value_compare value_comp() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::set<int> set_type;
 
   set_type s;
@@ -29,5 +29,12 @@ int main(int, char**) {
   assert(cs.value_comp()(*p1.first, *p2.first));
   assert(!cs.value_comp()(*p2.first, *p1.first));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.special/member_swap.pass.cpp b/libcxx/test/std/containers/associative/set/set.special/member_swap.pass.cpp
index db3dc9831b3dd..5a5a5156ce652 100644
--- a/libcxx/test/std/containers/associative/set/set.special/member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.special/member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// void swap(set& m);
+// constexpr void swap(set& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -110,5 +110,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.special/non_member_swap.pass.cpp b/libcxx/test/std/containers/associative/set/set.special/non_member_swap.pass.cpp
index e663a67035ff5..24fb007ef266e 100644
--- a/libcxx/test/std/containers/associative/set/set.special/non_member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.special/non_member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// void swap(set& m);
+// constexpr void swap(set& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   {
     typedef std::set<int> M;
@@ -100,5 +100,12 @@ int main(int, char**) {
     assert(m2.get_allocator() == A(1));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/associative/set/set.special/swap_noexcept.pass.cpp
index 36ab989b76043..4c4bf48adf241 100644
--- a/libcxx/test/std/containers/associative/set/set.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.special/swap_noexcept.pass.cpp
@@ -10,7 +10,7 @@
 
 // <set>
 
-// void swap(set& c)
+// constexpr void swap(set& c) // constexpr since C++26
 //     noexcept(!allocator_type::propagate_on_container_swap::value ||
 //              __is_nothrow_swappable<allocator_type>::value);
 //
@@ -86,7 +86,7 @@ struct some_alloc3 {
   typedef std::false_type is_always_equal;
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<MoveOnly> C;
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
@@ -131,5 +131,12 @@ int main(int, char**) {
 #  endif // _LIBCPP_VERSION
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/size.pass.cpp b/libcxx/test/std/containers/associative/set/size.pass.cpp
index 1672e1f055c6c..58084634e8dde 100644
--- a/libcxx/test/std/containers/associative/set/size.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// size_type size() const;
+// constexpr size_type size() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     M m;
@@ -56,5 +56,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/types.pass.cpp b/libcxx/test/std/containers/associative/set/types.pass.cpp
index a7c30244a540b..af3915dccf1fa 100644
--- a/libcxx/test/std/containers/associative/set/types.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/types.pass.cpp
@@ -34,7 +34,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> C;
     static_assert((std::is_same<C::key_type, int>::value), "");
@@ -67,5 +67,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/upper_bound.pass.cpp b/libcxx/test/std/containers/associative/set/upper_bound.pass.cpp
index 3543879e07940..f5825c30519e6 100644
--- a/libcxx/test/std/containers/associative/set/upper_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/upper_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class set
 
-//       iterator upper_bound(const key_type& k);
-// const_iterator upper_bound(const key_type& k) const;
+//       constexpr iterator upper_bound(const key_type& k); // constexpr since C++26
+// constexpr const_iterator upper_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -283,5 +283,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

>From 594dff9a2ed3b45b1140c5df09a4e149d8afac7e Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 15:56:56 -0500
Subject: [PATCH 03/21] Get ready for set.cons

---
 libcxx/include/__tree |   2 +-
 libcxx/include/set    | 234 ++++++++++++++++++++++++++----------------
 2 files changed, 144 insertions(+), 92 deletions(-)

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 5398ff911ec57..5f4c317d62291 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1467,7 +1467,7 @@ private:
   }
 
   template <class _To, class _From, class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI static void __assign_value(_To& __lhs, _From&& __rhs) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static void __assign_value(_To& __lhs, _From&& __rhs) {
     __lhs = std::forward<_From>(__rhs);
   }
 
diff --git a/libcxx/include/set b/libcxx/include/set
index d58b6e96b061d..d66a54e964a01 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -10,6 +10,8 @@
 #ifndef _LIBCPP_SET
 #define _LIBCPP_SET
 
+// TODO: add constexpr everywhere
+
 /*
 
     set synopsis
@@ -614,24 +616,27 @@ public:
   template <class _Key2, class _Compare2, class _Alloc2>
   friend class multiset;
 
-  _LIBCPP_HIDE_FROM_ABI set() _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set() _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_default_constructible<key_compare>::value&&
           is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(value_compare()) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit set(const value_compare& __comp) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit set(const value_compare& __comp) _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(__comp) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit set(const value_compare& __comp, const allocator_type& __a) : __tree_(__comp, __a) {}
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit set(const value_compare& __comp, const allocator_type& __a)
+      : __tree_(__comp, __a) {}
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI set(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  set(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__f, __l);
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   set(_InputIterator __f, _InputIterator __l, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__f, __l);
@@ -639,7 +644,7 @@ public:
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   set(from_range_t,
       _Range&& __range,
       const key_compare& __comp = key_compare(),
@@ -651,205 +656,247 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI set(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 set(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : set(__f, __l, key_compare(), __a) {}
 #  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI set(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(from_range_t, _Range&& __range, const allocator_type& __a)
       : set(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI set(const set& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(const set& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI set& operator=(const set& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set& operator=(const set& __s) = default;
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI set(set&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(set&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI explicit set(const allocator_type& __a) : __tree_(__a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit set(const allocator_type& __a) : __tree_(__a) {}
 
-  _LIBCPP_HIDE_FROM_ABI set(const set& __s, const allocator_type& __a) : __tree_(__s.__tree_.value_comp(), __a) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(const set& __s, const allocator_type& __a)
+      : __tree_(__s.__tree_.value_comp(), __a) {
     insert(__s.begin(), __s.end());
   }
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI set(set&& __s, const allocator_type& __a);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(set&& __s, const allocator_type& __a);
 
-  _LIBCPP_HIDE_FROM_ABI set(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  set(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__il.begin(), __il.end());
   }
 
-  _LIBCPP_HIDE_FROM_ABI set(initializer_list<value_type> __il, const value_compare& __comp, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  set(initializer_list<value_type> __il, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__il.begin(), __il.end());
   }
 
 #    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI set(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(initializer_list<value_type> __il, const allocator_type& __a)
       : set(__il, key_compare(), __a) {}
 #    endif
 
-  _LIBCPP_HIDE_FROM_ABI set& operator=(initializer_list<value_type> __il) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set& operator=(initializer_list<value_type> __il) {
     __tree_.__assign_unique(__il.begin(), __il.end());
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI set& operator=(set&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set& operator=(set&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI ~set() { static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), ""); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~set() {
+    static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
+  }
 
-  _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return __tree_.begin(); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return __tree_.begin(); }
-  _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return __tree_.end(); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return __tree_.end(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT { return __tree_.begin(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator begin() const _NOEXCEPT { return __tree_.begin(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator end() _NOEXCEPT { return __tree_.end(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator end() const _NOEXCEPT { return __tree_.end(); }
 
-  _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT { return reverse_iterator(end()); }
-  _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rbegin() const _NOEXCEPT { return const_reverse_iterator(end()); }
-  _LIBCPP_HIDE_FROM_ABI reverse_iterator rend() _NOEXCEPT { return reverse_iterator(begin()); }
-  _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const _NOEXCEPT { return const_reverse_iterator(begin()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rbegin() _NOEXCEPT {
+    return reverse_iterator(end());
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator rbegin() const _NOEXCEPT {
+    return const_reverse_iterator(end());
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rend() _NOEXCEPT {
+    return reverse_iterator(begin());
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator rend() const _NOEXCEPT {
+    return const_reverse_iterator(begin());
+  }
 
-  _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const _NOEXCEPT { return begin(); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator cend() const _NOEXCEPT { return end(); }
-  _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crbegin() const _NOEXCEPT { return rbegin(); }
-  _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const _NOEXCEPT { return rend(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cbegin() const _NOEXCEPT { return begin(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cend() const _NOEXCEPT { return end(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator crbegin() const _NOEXCEPT {
+    return rbegin();
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator crend() const _NOEXCEPT { return rend(); }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT { return __tree_.size() == 0; }
-  _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __tree_.size(); }
-  _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT { return __tree_.max_size(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool empty() const _NOEXCEPT {
+    return __tree_.size() == 0;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type size() const _NOEXCEPT { return __tree_.size(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type max_size() const _NOEXCEPT {
+    return __tree_.max_size();
+  }
 
   // modifiers:
 #  ifndef _LIBCPP_CXX03_LANG
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> emplace(_Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> emplace(_Args&&... __args) {
     return __tree_.__emplace_unique(std::forward<_Args>(__args)...);
   }
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace_hint(const_iterator __p, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace_hint(const_iterator __p, _Args&&... __args) {
     return __tree_.__emplace_hint_unique(__p, std::forward<_Args>(__args)...).first;
   }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> insert(const value_type& __v) { return __tree_.__emplace_unique(__v); }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, const value_type& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> insert(const value_type& __v) {
+    return __tree_.__emplace_unique(__v);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, const value_type& __v) {
     return __tree_.__emplace_hint_unique(__p, __v).first;
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __first, _InputIterator __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(_InputIterator __first, _InputIterator __last) {
     __tree_.__insert_range_unique(__first, __last);
   }
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert_range(_Range&& __range) {
     __tree_.__insert_range_unique(ranges::begin(__range), ranges::end(__range));
   }
 #  endif
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> insert(value_type&& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> insert(value_type&& __v) {
     return __tree_.__emplace_unique(std::move(__v));
   }
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, value_type&& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, value_type&& __v) {
     return __tree_.__emplace_hint_unique(__p, std::move(__v)).first;
   }
 
-  _LIBCPP_HIDE_FROM_ABI void insert(initializer_list<value_type> __il) { insert(__il.begin(), __il.end()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(initializer_list<value_type> __il) {
+    insert(__il.begin(), __il.end());
+  }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
-  _LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_unique(__k); }
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
-  _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p) { return __tree_.erase(__p); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(const key_type& __k) {
+    return __tree_.__erase_unique(__k);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
+    return __tree_.erase(__f, __l);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
-  _LIBCPP_HIDE_FROM_ABI insert_return_type insert(node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 insert_return_type insert(node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to set::insert()");
     return __tree_.template __node_handle_insert_unique< node_type, insert_return_type>(std::move(__nh));
   }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __hint, node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __hint, node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to set::insert()");
     return __tree_.template __node_handle_insert_unique<node_type>(__hint, std::move(__nh));
   }
-  _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract<node_type>(__it);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_unique(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_unique(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_unique(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_unique(__source.__tree_);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI void swap(set& __s) _NOEXCEPT_(__is_nothrow_swappable_v<__base>) { __tree_.swap(__s.__tree_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(set& __s) _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
+    __tree_.swap(__s.__tree_);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const _NOEXCEPT { return __tree_.__alloc(); }
-  _LIBCPP_HIDE_FROM_ABI key_compare key_comp() const { return __tree_.value_comp(); }
-  _LIBCPP_HIDE_FROM_ABI value_compare value_comp() const { return __tree_.value_comp(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type get_allocator() const _NOEXCEPT {
+    return __tree_.__alloc();
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_compare key_comp() const { return __tree_.value_comp(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_compare value_comp() const { return __tree_.value_comp(); }
 
   // set operations:
-  _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __tree_.find(__k); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __tree_.find(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const key_type& __k) { return __tree_.find(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const key_type& __k) const {
+    return __tree_.find(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const _K2& __k) {
     return __tree_.find(__k);
   }
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const _K2& __k) const {
     return __tree_.find(__k);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI size_type count(const key_type& __k) const { return __tree_.__count_unique(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const key_type& __k) const {
+    return __tree_.__count_unique(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const _K2& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  endif
 
 #  if _LIBCPP_STD_VER >= 20
-  _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const key_type& __k) const {
+    return find(__k) != end();
+  }
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
 #  endif // _LIBCPP_STD_VER >= 20
 
-  _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const key_type& __k) { return __tree_.__lower_bound_unique(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const key_type& __k) {
+    return __tree_.__lower_bound_unique(__k);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const key_type& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator lower_bound(const key_type& __k) const {
     return __tree_.__lower_bound_unique(__k);
   }
 
@@ -857,46 +904,50 @@ public:
   // match multiple elements.
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const _K2& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const _K2& __k) {
     return __tree_.__lower_bound_multi(__k);
   }
 
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator lower_bound(const _K2& __k) const {
     return __tree_.__lower_bound_multi(__k);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const key_type& __k) { return __tree_.__upper_bound_unique(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const key_type& __k) {
+    return __tree_.__upper_bound_unique(__k);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const key_type& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator upper_bound(const key_type& __k) const {
     return __tree_.__upper_bound_unique(__k);
   }
 
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const _K2& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const _K2& __k) {
     return __tree_.__upper_bound_multi(__k);
   }
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator upper_bound(const _K2& __k) const {
     return __tree_.__upper_bound_multi(__k);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const key_type& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const key_type& __k) {
     return __tree_.__equal_range_unique(__k);
   }
-  _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const key_type& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const key_type& __k) const {
     return __tree_.__equal_range_unique(__k);
   }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const _K2& __k) {
     return __tree_.__equal_range_multi(__k);
   }
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const _K2& __k) const {
     return __tree_.__equal_range_multi(__k);
   }
 #  endif
@@ -949,7 +1000,8 @@ set(initializer_list<_Key>, _Allocator) -> set<_Key, less<_Key>, _Allocator>;
 #  ifndef _LIBCPP_CXX03_LANG
 
 template <class _Key, class _Compare, class _Allocator>
-set<_Key, _Compare, _Allocator>::set(set&& __s, const allocator_type& __a) : __tree_(std::move(__s.__tree_), __a) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 set<_Key, _Compare, _Allocator>::set(set&& __s, const allocator_type& __a)
+    : __tree_(std::move(__s.__tree_), __a) {
   if (__a != __s.get_allocator()) {
     const_iterator __e = cend();
     while (!__s.empty())
@@ -960,7 +1012,7 @@ set<_Key, _Compare, _Allocator>::set(set&& __s, const allocator_type& __a) : __t
 #  endif // _LIBCPP_CXX03_LANG
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator==(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return __x.size() == __y.size() && std::equal(__x.begin(), __x.end(), __y.begin());
 }
@@ -968,31 +1020,31 @@ operator==(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare,
 #  if _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare(__x.begin(), __x.end(), __y.begin(), __y.end());
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator!=(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return !(__x == __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return __y < __x;
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>=(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return !(__x < __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<=(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return !(__y < __x);
 }
@@ -1000,7 +1052,7 @@ operator<=(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare,
 #  else // _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI __synth_three_way_result<_Key>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __synth_three_way_result<_Key>
 operator<=>(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare_three_way(__x.begin(), __x.end(), __y.begin(), __y.end(), std::__synth_three_way);
 }
@@ -1009,14 +1061,14 @@ operator<=>(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare
 
 // specialized algorithms:
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI void swap(set<_Key, _Compare, _Allocator>& __x, set<_Key, _Compare, _Allocator>& __y)
-    _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+swap(set<_Key, _Compare, _Allocator>& __x, set<_Key, _Compare, _Allocator>& __y) _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
   __x.swap(__y);
 }
 
 #  if _LIBCPP_STD_VER >= 20
 template <class _Key, class _Compare, class _Allocator, class _Predicate>
-inline _LIBCPP_HIDE_FROM_ABI typename set<_Key, _Compare, _Allocator>::size_type
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename set<_Key, _Compare, _Allocator>::size_type
 erase_if(set<_Key, _Compare, _Allocator>& __c, _Predicate __pred) {
   return std::__libcpp_erase_if_container(__c, __pred);
 }

>From f29268adce636381be9409c5c01c92281c537865 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 15:58:10 -0500
Subject: [PATCH 04/21] Test set.cons

---
 .../from_range_associative_containers.h       | 14 ++++-----
 .../set.cons/assign_initializer_list.pass.cpp |  4 +--
 .../associative/set/set.cons/copy.pass.cpp    |  8 ++---
 .../set/set.cons/copy_alloc.pass.cpp          |  8 ++---
 .../set/set.cons/copy_assign.pass.cpp         | 31 +++++++++----------
 .../associative/set/set.cons/deduct.pass.cpp  |  2 +-
 .../set/set.cons/from_range.pass.cpp          |  3 +-
 .../set/set.cons/move_alloc.pass.cpp          |  2 +-
 .../test/std/containers/from_range_helpers.h  |  4 +--
 9 files changed, 33 insertions(+), 43 deletions(-)

diff --git a/libcxx/test/std/containers/associative/from_range_associative_containers.h b/libcxx/test/std/containers/associative/from_range_associative_containers.h
index cb3646d738968..2a391b50fc06a 100644
--- a/libcxx/test/std/containers/associative/from_range_associative_containers.h
+++ b/libcxx/test/std/containers/associative/from_range_associative_containers.h
@@ -189,7 +189,7 @@ constexpr bool test_set_constraints() {
 }
 
 template <template <class...> class Container, class T, class Iter, class Sent, class Comp, class Alloc>
-void test_associative_set_with_input(std::vector<T>&& input) {
+TEST_CONSTEXPR_CXX26 void test_associative_set_with_input(std::vector<T>&& input) {
   { // (range)
     std::ranges::subrange in(Iter(input.data()), Sent(Iter(input.data() + input.size())));
     Container<T> c(std::from_range, in);
@@ -232,7 +232,7 @@ void test_associative_set_with_input(std::vector<T>&& input) {
 }
 
 template <template <class...> class Container, class T, class Iter, class Sent, class Comp, class Alloc>
-void test_associative_set() {
+TEST_CONSTEXPR_CXX26 void test_associative_set() {
   auto test_with_input = &test_associative_set_with_input<Container, T, Iter, Sent, Comp, Alloc>;
 
   // Normal input.
@@ -244,7 +244,7 @@ void test_associative_set() {
 }
 
 template <template <class...> class Container>
-void test_associative_set_move_only() {
+TEST_CONSTEXPR_CXX26 void test_associative_set_move_only() {
   MoveOnly input[5];
   std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
 
@@ -252,8 +252,8 @@ void test_associative_set_move_only() {
 }
 
 template <template <class...> class Container>
-void test_set_exception_safety_throwing_copy() {
-#if !defined(TEST_HAS_NO_EXCEPTIONS)
+TEST_CONSTEXPR_CXX26 void test_set_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_IS_CONSTANT_EVALUATED)
   using T = ThrowingCopy<3>;
   T::reset();
   T in[5] = {{1}, {2}, {3}, {4}, {5}};
@@ -270,8 +270,8 @@ void test_set_exception_safety_throwing_copy() {
 }
 
 template <template <class...> class Container, class T>
-void test_set_exception_safety_throwing_allocator() {
-#if !defined(TEST_HAS_NO_EXCEPTIONS)
+TEST_CONSTEXPR_CXX26 void test_set_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_IS_CONSTANT_EVALUATED)
   T in[] = {1, 2};
 
   try {
diff --git a/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
index 1e0de1434aeb9..c37e2f400db3e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
@@ -21,7 +21,7 @@
 #include "min_allocator.h"
 #include "test_allocator.h"
 
-void basic_test() {
+TEST_CONSTEXPR_CXX26 void basic_test() {
   {
     typedef std::set<int> C;
     typedef C::value_type V;
@@ -54,7 +54,7 @@ void basic_test() {
   }
 }
 
-void duplicate_keys_test() {
+TEST_CONSTEXPR_CXX26 void duplicate_keys_test() {
   test_allocator_statistics alloc_stats;
   typedef std::set<int, std::less<int>, test_allocator<int> > Set;
   {
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
index f2116c019d3b6..ea4a34940bacd 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
@@ -20,7 +20,7 @@
 #include "test_allocator.h"
 
 template <template <class> class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   { // Simple check
     using Set = std::set<int, std::less<int>, Alloc<int> >;
 
@@ -79,7 +79,7 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator>();
   test_alloc<min_allocator>(); // Make sure that fancy pointers work
 
@@ -125,10 +125,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.get_allocator() == other_allocator<int>(10));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
index 200bf71b5f3dd..bb1d251777904 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
@@ -21,7 +21,7 @@
 #include "test_allocator.h"
 
 template <class Alloc>
-void test_alloc(const Alloc& new_alloc) {
+TEST_CONSTEXPR_CXX26 void test_alloc(const Alloc& new_alloc) {
   { // Simple check
     using Set = std::set<int, std::less<int>, Alloc>;
 
@@ -81,7 +81,7 @@ void test_alloc(const Alloc& new_alloc) {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc(std::allocator<int>());
   test_alloc(test_allocator<int>(25)); // Make sure that the new allocator is actually used
   test_alloc(min_allocator<int>());    // Make sure that fancy pointers work
@@ -97,10 +97,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
index 7c301d62cb586..2e47297ccf28e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
@@ -35,41 +35,42 @@ class tracking_allocator {
   using value_type                             = T;
   using propagate_on_container_copy_assignment = std::true_type;
 
-  tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
 
   template <class U>
-  tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
 
-  T* allocate(std::size_t n) {
+  TEST_CONSTEXPR_CXX26 T* allocate(std::size_t n) {
     T* allocation = std::allocator<T>().allocate(n);
     allocs_->push_back(allocation);
     return allocation;
   }
 
-  void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
+  TEST_CONSTEXPR_CXX26 void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
     auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
     assert(res != allocs_->end() && "Trying to deallocate memory from different allocator?");
     allocs_->erase(res);
     std::allocator<T>().deallocate(ptr, n);
   }
 
-  friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  TEST_CONSTEXPR_CXX26 friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ == rhs.allocs_;
   }
 
-  friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  TEST_CONSTEXPR_CXX26 friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ != rhs.allocs_;
   }
 };
 
 struct NoOp {
-  void operator()() {}
+  TEST_CONSTEXPR_CXX26 void operator()() {}
 };
 
 template <class Alloc, class AllocatorInvariant = NoOp>
-void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
-                const Alloc& rhs_alloc                   = Alloc(),
-                AllocatorInvariant check_alloc_invariant = NoOp()) {
+TEST_CONSTEXPR_CXX26 void
+test_alloc(const Alloc& lhs_alloc                   = Alloc(),
+           const Alloc& rhs_alloc                   = Alloc(),
+           AllocatorInvariant check_alloc_invariant = NoOp()) {
   {   // Test empty/non-empty set combinations
     { // assign from a non-empty container into an empty one
       using Set = std::set<int, std::less<int>, Alloc>;
@@ -236,7 +237,7 @@ void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator<int> >();
 #if TEST_STD_VER >= 11
   test_alloc<min_allocator<int> >();
@@ -248,10 +249,10 @@ void test() {
       std::vector<void*>* rhs_allocs_;
 
     public:
-      AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
+      TEST_CONSTEXPR_CXX26 AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
           : lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
 
-      void operator()() {
+      TEST_CONSTEXPR_CXX26 void operator()() {
         assert(lhs_allocs_->empty());
         assert(rhs_allocs_->empty());
       }
@@ -275,10 +276,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
index 56540fbf35c6a..6de41997d424d 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
@@ -51,7 +51,7 @@
 #include "test_allocator.h"
 
 struct NotAnAllocator {
-  friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
+  TEST_CONSTEXPR_CXX26 friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
index 23a5136aafcf2..d24cc95d69469 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2147483647
 
 // template<container-compatible-range<value_type> R>
 //   set(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
@@ -22,7 +23,7 @@
 #include "../../from_range_associative_containers.h"
 #include "test_macros.h"
 
-void test_duplicates() {
+TEST_CONSTEXPR_CXX26 void test_duplicates() {
   using T = KeyValue;
 
   std::array input    = {T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}};
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
index 4f15202f2ca83..948cc85e7552d 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
@@ -73,7 +73,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(m3.key_comp() == C(5));
     LIBCPP_ASSERT(m1.empty());
   }
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef Counter<int> V;
     typedef std::less<V> C;
     typedef test_allocator<V> A;
diff --git a/libcxx/test/std/containers/from_range_helpers.h b/libcxx/test/std/containers/from_range_helpers.h
index edf1d6ce528d7..13bd74d15cf14 100644
--- a/libcxx/test/std/containers/from_range_helpers.h
+++ b/libcxx/test/std/containers/from_range_helpers.h
@@ -54,8 +54,8 @@ struct KeyValue {
   int key;    // Only the key is considered for equality comparison.
   char value; // Allows distinguishing equivalent instances.
 
-  bool operator<(const KeyValue& other) const { return key < other.key; }
-  bool operator==(const KeyValue& other) const { return key == other.key; }
+  TEST_CONSTEXPR_CXX26 bool operator<(const KeyValue& other) const { return key < other.key; }
+  TEST_CONSTEXPR_CXX26 bool operator==(const KeyValue& other) const { return key == other.key; }
 };
 
 template <>

>From b2e088aa09088fd8d263a92547797a297a103917 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 16:15:59 -0500
Subject: [PATCH 05/21] Test set.erasure

---
 .../containers/associative/set/set.erasure/erase_if.pass.cpp  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
index 978e80821eb49..a5b2c741e02e4 100644
--- a/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
@@ -20,14 +20,14 @@
 #include "min_allocator.h"
 
 template <class S, class Pred>
-void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
+TEST_CONSTEXPR_CXX26 void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
   ASSERT_SAME_TYPE(typename S::size_type, decltype(std::erase_if(s, p)));
   assert(expected_erased_count == std::erase_if(s, p));
   assert(s == expected);
 }
 
 template <typename S>
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   auto is1   = [](auto v) { return v == 1; };
   auto is2   = [](auto v) { return v == 2; };
   auto is3   = [](auto v) { return v == 3; };

>From 42f2950a3143df5907a575323230e284e3cf3834 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 16:18:08 -0500
Subject: [PATCH 06/21] Test set.observers

---
 .../std/containers/associative/set/set.observers/comp.pass.cpp  | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
index 9aef8c7cce41b..dcea523540247 100644
--- a/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
@@ -14,6 +14,8 @@
 #include <set>
 #include <cassert>
 
+#include "test_macros.h"
+
 TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::set<int> set_type;
 

>From b30297eee8b6ed88b907c41663b9203003a3bac7 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 17:48:30 -0500
Subject: [PATCH 07/21] hash correct version from main

---
 libcxx/include/__functional/hash.h         |  9 ---------
 libcxx/test/support/poisoned_hash_helper.h | 10 +++-------
 2 files changed, 3 insertions(+), 16 deletions(-)

diff --git a/libcxx/include/__functional/hash.h b/libcxx/include/__functional/hash.h
index 83bbf1b5e26c3..f74f25fa6e84b 100644
--- a/libcxx/include/__functional/hash.h
+++ b/libcxx/include/__functional/hash.h
@@ -433,13 +433,10 @@ struct __hash_impl<long double> : __scalar_hash<long double> {
 template <class _Tp>
 struct hash : public __hash_impl<_Tp> {};
 
-#if _LIBCPP_STD_VER >= 17
-
 template <>
 struct hash<nullptr_t> : public __unary_function<nullptr_t, size_t> {
   _LIBCPP_HIDE_FROM_ABI size_t operator()(nullptr_t) const _NOEXCEPT { return 662607004ull; }
 };
-#endif
 
 #ifndef _LIBCPP_CXX03_LANG
 template <class _Key, class _Hash>
@@ -452,18 +449,12 @@ template <class _Key, class _Hash = hash<_Key> >
 using __has_enabled_hash _LIBCPP_NODEBUG =
     integral_constant<bool, __check_hash_requirements<_Key, _Hash>::value && is_default_constructible<_Hash>::value >;
 
-#  if _LIBCPP_STD_VER >= 17
 template <class _Type, class>
 using __enable_hash_helper_imp _LIBCPP_NODEBUG = _Type;
 
 template <class _Type, class... _Keys>
 using __enable_hash_helper _LIBCPP_NODEBUG =
     __enable_hash_helper_imp<_Type, __enable_if_t<__all<__has_enabled_hash<_Keys>::value...>::value> >;
-#  else
-template <class _Type, class...>
-using __enable_hash_helper _LIBCPP_NODEBUG = _Type;
-#  endif
-
 #endif // !_LIBCPP_CXX03_LANG
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/support/poisoned_hash_helper.h b/libcxx/test/support/poisoned_hash_helper.h
index 93b579d2dfde3..cd71cd70d6a84 100644
--- a/libcxx/test/support/poisoned_hash_helper.h
+++ b/libcxx/test/support/poisoned_hash_helper.h
@@ -123,13 +123,9 @@ struct Class {};
 // Each header that declares the std::hash template provides enabled
 // specializations of std::hash for std::nullptr_t and all cv-unqualified
 // arithmetic, enumeration, and pointer types.
-#if TEST_STD_VER >= 17
-using MaybeNullptr = types::type_list<std::nullptr_t>;
-#else
-using MaybeNullptr = types::type_list<>;
-#endif
-using LibraryHashTypes = types::
-    concatenate_t<types::arithmetic_types, types::type_list<Enum, EnumClass, void*, void const*, Class*>, MaybeNullptr>;
+using LibraryHashTypes =
+    types::concatenate_t<types::arithmetic_types,
+                         types::type_list<Enum, EnumClass, void*, void const*, Class*, std::nullptr_t>>;
 
 struct TestHashEnabled {
   template <class T>

>From df77e29f261fd166ef1a56908df8519d941b59fb Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 17:57:58 -0500
Subject: [PATCH 08/21] saturation correct version from main

---
 libcxx/include/__numeric/saturation_arithmetic.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 7a7410b5dea08..4491bab2b1479 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -121,27 +121,27 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Rp __saturate_cast(_Tp __x) noexcept {
 #if _LIBCPP_STD_VER >= 26
 
 template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
   return std::__add_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
   return std::__sub_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
   return std::__mul_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
   return std::__div_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Rp, __signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
   return std::__saturate_cast<_Rp>(__x);
 }
 

>From 7ce7dcdba3724e6109ca2b925077771d7f2d79ca Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 21:10:14 -0500
Subject: [PATCH 09/21] Fix set/ final tests

---
 libcxx/include/__node_handle                      |  4 +++-
 .../containers/associative/set/contains.pass.cpp  |  6 ++++--
 .../associative/set/contains_transparent.pass.cpp | 15 ++++++++++-----
 .../associative/set/count_transparent.pass.cpp    | 10 +++++++---
 .../containers/associative/set/emplace.pass.cpp   |  5 +++--
 .../associative/set/emplace_hint.pass.cpp         |  5 +++--
 .../set/equal_range_transparent.pass.cpp          | 10 +++++++---
 .../associative/set/erase_iter.pass.cpp           |  4 ++--
 .../associative/set/extract_iterator.pass.cpp     |  4 ++--
 .../associative/set/extract_key.pass.cpp          |  4 ++--
 ...rt_and_emplace_allocator_requirements.pass.cpp | 15 ++++-----------
 .../containers/associative/set/insert_cv.pass.cpp |  2 +-
 .../associative/set/insert_iter_iter.pass.cpp     |  8 ++------
 .../associative/set/insert_node_type.pass.cpp     | 13 +++++++------
 .../set/insert_node_type_hint.pass.cpp            |  9 +++++----
 .../associative/set/insert_range.pass.cpp         |  1 +
 .../std/containers/associative/set/merge.pass.cpp | 13 +++++++------
 .../test/std/containers/insert_range_maps_sets.h  | 12 ++++++------
 .../set_allocator_requirement_test_templates.h    |  2 +-
 19 files changed, 77 insertions(+), 65 deletions(-)

diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index 58be5b9d4ddd5..6d253cb4eaa98 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -172,7 +172,9 @@ template <class _NodeType, class _Derived>
 struct __set_node_handle_specifics {
   typedef typename _NodeType::__node_value_type value_type;
 
-  _LIBCPP_HIDE_FROM_ABI value_type& value() const { return static_cast<_Derived const*>(this)->__ptr_->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_type& value() const {
+    return static_cast<_Derived const*>(this)->__ptr_->__get_value();
+  }
 };
 
 template <class _NodeType, class _Derived>
diff --git a/libcxx/test/std/containers/associative/set/contains.pass.cpp b/libcxx/test/std/containers/associative/set/contains.pass.cpp
index 20b1eeb839414..c147b01d73c10 100644
--- a/libcxx/test/std/containers/associative/set/contains.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/contains.pass.cpp
@@ -11,12 +11,14 @@
 #include <cassert>
 #include <set>
 
+#include "test_macros.h"
+
 // <set>
 
 // constexpr bool contains(const key_type& x) const; // constexpr since C++26
 
 template <typename T, typename V, typename B, typename... Vals>
-void test(B bad, Vals... args) {
+TEST_CONSTEXPR_CXX26 void test(B bad, Vals... args) {
   T set;
   V vals[] = {args...};
 
@@ -39,7 +41,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     test<std::set<int>, int>(14, 10, 11, 12, 13);
     test<std::set<char>, char>('e', 'a', 'b', 'c', 'd');
   }
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     test<std::multiset<int>, int>(14, 10, 11, 12, 13);
     test<std::multiset<char>, char>('e', 'a', 'b', 'c', 'd');
   }
diff --git a/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
index acd03e649095f..0b7e032414425 100644
--- a/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
@@ -18,18 +18,22 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 template <typename Container>
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   Container s{{2, 1}, {1, 2}, {1, 3}, {1, 4}, {2, 2}};
 
   assert(s.contains(1));
@@ -38,7 +42,8 @@ void test() {
 
 TEST_CONSTEXPR_CXX26 bool test() {
   test<std::set<std::pair<int, int>, Comp> >();
-  test<std::multiset<std::pair<int, int>, Comp> >();
+  if (!TEST_IS_CONSTANT_EVALUATED)
+    test<std::multiset<std::pair<int, int>, Comp> >();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
index 9147b38e5d336..a14f4efe8c48d 100644
--- a/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
@@ -19,14 +19,18 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/set/emplace.pass.cpp b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
index fecd9ef2fe7f9..6c93d99356c12 100644
--- a/libcxx/test/std/containers/associative/set/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
@@ -24,7 +24,7 @@
 #include "min_allocator.h"
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::set<DefaultOnly> M;
     typedef std::pair<M::iterator, bool> R;
     M m;
@@ -43,7 +43,8 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 1);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED)
+    assert(DefaultOnly::count == 0);
   {
     typedef std::set<Emplaceable> M;
     typedef std::pair<M::iterator, bool> R;
diff --git a/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
index 5c68000ffbab9..5282f4295ec8e 100644
--- a/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
@@ -24,7 +24,7 @@
 #include "min_allocator.h"
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::set<DefaultOnly> M;
     typedef M::iterator R;
     M m;
@@ -41,7 +41,8 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 1);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED)
+    assert(DefaultOnly::count == 0);
   {
     typedef std::set<Emplaceable> M;
     typedef M::iterator R;
diff --git a/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
index 92c1c64be658c..9f2606412a5d2 100644
--- a/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
@@ -23,14 +23,18 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
index 7682a81dc7407..952714a3720b0 100644
--- a/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
@@ -20,10 +20,10 @@
 
 struct TemplateConstructor {
   template <typename T>
-  TemplateConstructor(const T&) {}
+  TEST_CONSTEXPR_CXX26 TemplateConstructor(const T&) {}
 };
 
-bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
+TEST_CONSTEXPR_CXX26 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
 TEST_CONSTEXPR_CXX26 bool test() {
   {
diff --git a/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
index 1511b08079d03..59a83338da06f 100644
--- a/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   std::size_t sz = c.size();
 
   for (auto first = c.cbegin(); first != c.cend();) {
@@ -42,7 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     test(m);
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::set<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     assert(Counter_base::gConstructed == 6);
     test(m);
diff --git a/libcxx/test/std/containers/associative/set/extract_key.pass.cpp b/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
index 07f6d193179a8..432bfbdb1b8fb 100644
--- a/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container, class KeyTypeIter>
-void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
+TEST_CONSTEXPR_CXX26 void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   std::size_t sz = c.size();
   assert((std::size_t)std::distance(first, last) == sz);
 
@@ -48,7 +48,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     test(m, std::begin(keys), std::end(keys));
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::set<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     {
       Counter<int> keys[] = {1, 2, 3, 4, 5, 6};
diff --git a/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
index ace31eb66ea8d..4711473de4dad 100644
--- a/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
@@ -10,9 +10,9 @@
 
 // class set
 
-// constexpr insert(...) // constexpr since C++26
-// constexpr emplace(...) // constexpr since C++26
-// constexpr emplace_hint(...) // constexpr since C++26
+// insert(...)
+// emplace(...)
+// emplace_hint(...)
 
 // UNSUPPORTED: c++03
 
@@ -21,17 +21,10 @@
 #include "container_test_types.h"
 #include "../../set_allocator_requirement_test_templates.h"
 
-TEST_CONSTEXPR_CXX26 bool test() {
+int main(int, char**) {
   testSetInsert<TCT::set<> >();
   testSetEmplace<TCT::set<> >();
   testSetEmplaceHint<TCT::set<> >();
 
-  return true;
-}
-int main(int, char**) {
-  test();
-#if TEST_STD_VER >= 26
-  static_assert(test());
-#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
index ef278224f0229..3dc33a80fe2ad 100644
--- a/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 
 template <class Container>
-void do_insert_cv_test() {
+TEST_CONSTEXPR_CXX26 void do_insert_cv_test() {
   typedef Container M;
   typedef std::pair<typename M::iterator, bool> R;
   typedef typename M::value_type VT;
diff --git a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
index 6d6bd185afa04..ca0180af82897 100644
--- a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
@@ -21,7 +21,7 @@
 #include "test_iterators.h"
 
 template <class Iter, class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   {   // Check that an empty range works correctly
     { // Without elements in the container
       using Map = std::set<int, std::less<int>, Alloc>;
@@ -178,13 +178,9 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<cpp17_input_iterator<int*>, std::allocator<int> >();
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
index 935a90afedb2b..1512debd4e0bd 100644
--- a/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
@@ -21,7 +21,7 @@
 #include "min_allocator.h"
 
 template <class Container, class T>
-void verify_insert_return_type(T&& t) {
+TEST_CONSTEXPR_CXX26 void verify_insert_return_type(T&& t) {
   using verified_type = std::remove_cv_t<std::remove_reference_t<T>>;
   static_assert(std::is_aggregate_v<verified_type>);
   static_assert(std::is_same_v<verified_type, typename Container::insert_return_type>);
@@ -42,19 +42,20 @@ void verify_insert_return_type(T&& t) {
 }
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto p = c.insert(key);
   assert(p.second);
   return c.extract(p.first);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
 
+  Container c2;
+
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c2, i);
     assert(!node.empty());
     typename Container::insert_return_type irt = c.insert(std::move(node));
     assert(node.empty());
@@ -77,7 +78,7 @@ void test(Container& c) {
   }
 
   { // Insert duplicate node.
-    typename Container::node_type dupl = nf(0);
+    typename Container::node_type dupl = nf(c2, 0);
     auto irt                           = c.insert(std::move(dupl));
     assert(dupl.empty());
     assert(!irt.inserted);
diff --git a/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
index f14263595ac6a..a1df9de53ae8a 100644
--- a/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
@@ -19,19 +19,20 @@
 #include "min_allocator.h"
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto p = c.insert(key);
   assert(p.second);
   return c.extract(p.first);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
 
+  Container c2;
+
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c2, i);
     assert(!node.empty());
     std::size_t prev = c.size();
     auto it          = c.insert(c.end(), std::move(node));
diff --git a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
index 654d4d425774a..00cead676709a 100644
--- a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
@@ -9,6 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
 // ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-missing-field-initializers
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2147483647
 
 // <set>
 
diff --git a/libcxx/test/std/containers/associative/set/merge.pass.cpp b/libcxx/test/std/containers/associative/set/merge.pass.cpp
index 4c76c59ec5de5..bcaf13d4a7e1b 100644
--- a/libcxx/test/std/containers/associative/set/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/merge.pass.cpp
@@ -27,7 +27,7 @@
 #include "Counter.h"
 
 template <class Set>
-bool set_equal(const Set& set, Set other) {
+TEST_CONSTEXPR_CXX26 bool set_equal(const Set& set, Set other) {
   return set == other;
 }
 
@@ -55,8 +55,8 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(set_equal(dst, {1, 2, 3, 4, 5}));
   }
 
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     bool do_throw = false;
     typedef std::set<Counter<int>, throw_comparator> set_type;
     set_type src({1, 3, 5}, throw_comparator(do_throw));
@@ -75,13 +75,14 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(set_equal(dst, set_type({2, 4, 5}, throw_comparator(do_throw))));
   }
 #endif
-  assert(Counter_base::gConstructed == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED)
+    assert(Counter_base::gConstructed == 0);
   struct comparator {
     comparator() = default;
 
     bool operator()(const Counter<int>& lhs, const Counter<int>& rhs) const { return lhs < rhs; }
   };
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::set<Counter<int>, std::less<Counter<int>>> first_set_type;
     typedef std::set<Counter<int>, comparator> second_set_type;
     typedef std::multiset<Counter<int>, comparator> third_set_type;
@@ -128,7 +129,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
       first.merge(second);
       first.merge(std::move(second));
     }
-    {
+    if (!TEST_IS_CONSTANT_EVALUATED) {
       std::multiset<int> second;
       first.merge(second);
       first.merge(std::move(second));
diff --git a/libcxx/test/std/containers/insert_range_maps_sets.h b/libcxx/test/std/containers/insert_range_maps_sets.h
index 6e344d8ad3b0f..8563b7b43b934 100644
--- a/libcxx/test/std/containers/insert_range_maps_sets.h
+++ b/libcxx/test/std/containers/insert_range_maps_sets.h
@@ -200,7 +200,7 @@ TestCaseMapSet<std::pair<K, V>> constexpr NElementsContainer_RangeWithDuplicates
         {16, 'D'}}};
 
 template <class Container, class T, class Iter, class Sent>
-void test_map_set_insert_range(bool allow_duplicates = false) {
+TEST_CONSTEXPR_CXX26 void test_map_set_insert_range(bool allow_duplicates = false) {
   auto test = [&](const TestCaseMapSet<T>& test_case, bool check_multi = false) {
     Container c(test_case.initial.begin(), test_case.initial.end());
     auto in = wrap_input<Iter, Sent>(test_case.input);
@@ -250,7 +250,7 @@ void test_map_set_insert_range(bool allow_duplicates = false) {
 // Move-only types.
 
 template <template <class...> class Container>
-void test_set_insert_range_move_only() {
+TEST_CONSTEXPR_CXX26 void test_set_insert_range_move_only() {
   MoveOnly input[5];
   std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
 
@@ -271,8 +271,8 @@ void test_map_insert_range_move_only() {
 // Exception safety.
 
 template <template <class...> class Container>
-void test_set_insert_range_exception_safety_throwing_copy() {
-#if !defined(TEST_HAS_NO_EXCEPTIONS)
+TEST_CONSTEXPR_CXX26 void test_set_insert_range_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_IS_CONSTANT_EVALUATED)
   using T = ThrowingCopy<3>;
   T::reset();
   T in[5] = {{1}, {2}, {3}, {4}, {5}};
@@ -313,8 +313,8 @@ void test_map_insert_range_exception_safety_throwing_copy() {
 }
 
 template <template <class...> class Container, class T>
-void test_assoc_set_insert_range_exception_safety_throwing_allocator() {
-#if !defined(TEST_HAS_NO_EXCEPTIONS)
+TEST_CONSTEXPR_CXX26 void test_assoc_set_insert_range_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_IS_CONSTANT_EVALUATED)
   T in[] = {1, 2};
 
   try {
diff --git a/libcxx/test/std/containers/set_allocator_requirement_test_templates.h b/libcxx/test/std/containers/set_allocator_requirement_test_templates.h
index 10552a5b9ee21..f6bda0eddfbb4 100644
--- a/libcxx/test/std/containers/set_allocator_requirement_test_templates.h
+++ b/libcxx/test/std/containers/set_allocator_requirement_test_templates.h
@@ -27,7 +27,7 @@
 #include "container_test_types.h"
 
 template <class Container>
-void testSetInsert() {
+TEST_CONSTEXPR_CXX26 void testSetInsert() {
   typedef typename Container::value_type ValueTp;
   ConstructController* cc = getConstructController();
   cc->reset();

>From 5764d913c2906ac9010f806395aa2a8b073ce660 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Mon, 10 Nov 2025 07:19:09 -0500
Subject: [PATCH 10/21] restore
 libcxx/test/std/containers/associative/iterator_types.pass.cpp

---
 .../std/containers/associative/iterator_types.pass.cpp   | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/libcxx/test/std/containers/associative/iterator_types.pass.cpp b/libcxx/test/std/containers/associative/iterator_types.pass.cpp
index f394b000615d8..2800549905f1b 100644
--- a/libcxx/test/std/containers/associative/iterator_types.pass.cpp
+++ b/libcxx/test/std/containers/associative/iterator_types.pass.cpp
@@ -46,7 +46,7 @@ void testSet() {
   }
 }
 
-TEST_CONSTEXPR_CXX26 bool test() {
+int main(int, char**) {
   {
     typedef std::map<int, int> Map;
     typedef std::pair<const int, int> ValueTp;
@@ -124,12 +124,5 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
 #endif
 
-  return true;
-}
-int main(int, char**) {
-  test();
-#if TEST_STD_VER >= 26
-  static_assert(test());
-#endif
   return 0;
 }

>From 238229f3cc3d76a3ba038e55a827a4a48197cea0 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 20 Jun 2026 10:56:56 +0100
Subject: [PATCH 11/21] Add feature flag for set

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 ++
 libcxx/include/version                        |  2 ++
 .../set.version.compile.pass.cpp              | 27 +++++++++++++++++++
 .../version.version.compile.pass.cpp          | 27 +++++++++++++++++++
 .../generate_feature_test_macro_components.py |  5 ++++
 5 files changed, 63 insertions(+)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 8e26bdcd860c1..a656bd80497e6 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -450,6 +450,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_constexpr_queue``                              ``202502L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_constexpr_set``                                ``202502L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_constrained_equality``                         ``202411L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_copyable_function``                            *unimplemented*
diff --git a/libcxx/include/version b/libcxx/include/version
index 7f2dc9e4b72ab..711ba498ac101 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -84,6 +84,7 @@ __cpp_lib_constexpr_memory                              202202L <memory>
 __cpp_lib_constexpr_new                                 202406L <new>
 __cpp_lib_constexpr_numeric                             201911L <numeric>
 __cpp_lib_constexpr_queue                               202502L <queue>
+__cpp_lib_constexpr_set                                 202502L <set>
 __cpp_lib_constexpr_string                              201907L <string>
 __cpp_lib_constexpr_string_view                         201811L <string_view>
 __cpp_lib_constexpr_tuple                               201811L <tuple>
@@ -575,6 +576,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #   define __cpp_lib_constexpr_new                      202406L
 # endif
 # define __cpp_lib_constexpr_queue                      202502L
+# define __cpp_lib_constexpr_set                        202502L
 # define __cpp_lib_constrained_equality                 202411L
 // # define __cpp_lib_copyable_function                    202306L
 // # define __cpp_lib_debugging                            202311L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
index 5dc69f29d0ecd..d1a0570557246 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
@@ -32,6 +32,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should not be defined before c++23"
 #  endif
@@ -66,6 +70,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should not be defined before c++23"
 #  endif
@@ -106,6 +114,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should not be defined before c++23"
 #  endif
@@ -152,6 +164,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should not be defined before c++23"
 #  endif
@@ -210,6 +226,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should be defined in c++23"
 #  endif
@@ -280,6 +300,13 @@
 #    endif
 #  endif
 
+#  ifndef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should be defined in c++26"
+#  endif
+#  if __cpp_lib_constexpr_set != 202502L
+#    error "__cpp_lib_constexpr_set should have the value 202502L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should be defined in c++26"
 #  endif
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 d9c78b73f7e23..a88ec24e4c483 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
@@ -252,6 +252,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should not be defined before c++20"
 #  endif
@@ -1188,6 +1192,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should not be defined before c++20"
 #  endif
@@ -2226,6 +2234,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should not be defined before c++20"
 #  endif
@@ -3510,6 +3522,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should be defined in c++20"
 #  endif
@@ -5004,6 +5020,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should be defined in c++23"
 #  endif
@@ -6744,6 +6764,13 @@
 #    error "__cpp_lib_constexpr_queue should have the value 202502L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should be defined in c++26"
+#  endif
+#  if __cpp_lib_constexpr_set != 202502L
+#    error "__cpp_lib_constexpr_set should have the value 202502L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should be defined in c++26"
 #  endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d764a1f677ba6..078e3b815742c 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -432,6 +432,11 @@ def add_version_header(tc):
             "values": {"c++26": 202502},
             "headers": ["queue"],
         },
+        {
+            "name": "__cpp_lib_constexpr_set",
+            "values": {"c++26": 202502},
+            "headers": ["set"],
+        },
         {
             "name": "__cpp_lib_constexpr_string",
             "values": {"c++20": 201907},

>From d559bade444c4925d7ffa5355fbef9f413a12f33 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 21 Jun 2026 22:14:01 +0100
Subject: [PATCH 12/21] add set back to constexpr

---
 .../alg.foreach/ranges.for_each.associative.pass.cpp      | 8 ++------
 .../set/set.nonmember/compare.three_way.pass.cpp          | 1 -
 .../std/containers/container.node/node_handle.pass.cpp    | 4 ++--
 3 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
index e0186654e4bbc..1ee485b1aef0d 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
@@ -253,23 +253,19 @@ TEST_CONSTEXPR_CXX26 void test_invoke_map_like() {
 
 TEST_CONSTEXPR_CXX26 bool test() {
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when set is made constexpr
-    test_node_container<std::set<int> >([](int i) { return i; });
-
     // FIXME: remove when multiset is made constexpr
     test_node_container<std::multiset<int> >([](int i) { return i; });
   }
+  test_node_container<std::set<int> >([](int i) { return i; });
   test_node_container<std::map<int, int> >([](int i) { return std::make_pair(i, i); });
   test_node_container<std::multimap<int, int> >([](int i) { return std::make_pair(i, i); });
 
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when set is made constexpr
-    test_invoke_set_like<std::set>();
-
     // FIXME: remove when multiset is made constexpr
     test_invoke_set_like<std::multiset>();
   }
 
+  test_invoke_set_like<std::set>();
   test_invoke_map_like<std::map>();
   test_invoke_map_like<std::multimap>();
 
diff --git a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
index 563a8c12db828..ce34b7d159dd4 100644
--- a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
@@ -22,7 +22,6 @@
 
 TEST_CONSTEXPR_CXX26 bool test() {
   assert((test_ordered_set_container_spaceship<std::set>()));
-  // `std::set` is not constexpr, so no `static_assert` test here.
   return true;
 }
 int main(int, char**) {
diff --git a/libcxx/test/std/containers/container.node/node_handle.pass.cpp b/libcxx/test/std/containers/container.node/node_handle.pass.cpp
index 38c1f60a31c49..a6737c9037893 100644
--- a/libcxx/test/std/containers/container.node/node_handle.pass.cpp
+++ b/libcxx/test/std/containers/container.node/node_handle.pass.cpp
@@ -150,10 +150,10 @@ TEST_CONSTEXPR_CXX26 bool test_insert_return_type() {
 TEST_CONSTEXPR_CXX26 bool test() {
   test_node_handle_operations<std::map<int, int>>();
   test_node_handle_operations_multi<std::multimap<int, int>>();
+  test_node_handle_operations<std::set<int>>();
 
   // FIXME: update when other containers are made constexpr
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    test_node_handle_operations<std::set<int>>();
     test_node_handle_operations_multi<std::multiset<int>>();
     test_node_handle_operations<std::unordered_map<int, int>>();
     test_node_handle_operations_multi<std::unordered_multimap<int, int>>();
@@ -162,10 +162,10 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
 
   test_insert_return_type<std::map<int, int>>();
+  test_insert_return_type<std::set<int>>();
 
   // FIXME: update when other containers are made constexpr
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    test_insert_return_type<std::set<int>>();
     test_insert_return_type<std::unordered_map<int, int>>();
     test_insert_return_type<std::unordered_set<int>>();
   }

>From ee147e46e940c79ef37111bb04d381a95e510cd7 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Thu, 25 Jun 2026 08:34:28 +0100
Subject: [PATCH 13/21] missing constexpr

---
 .../alg.foreach/ranges.for_each.associative.pass.cpp      | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
index 1ee485b1aef0d..02698819d71df 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
@@ -189,23 +189,23 @@ TEST_CONSTEXPR_CXX26 void test_node_container(Converter conv) {
 }
 
 template <template <class> class Container>
-void test_invoke_set_like() {
+TEST_CONSTEXPR_CXX26 void test_invoke_set_like() {
   { // check that std::invoke is used
     struct T {
       mutable int i = 3;
 
-      void zero() const { i = 0; }
+      TEST_CONSTEXPR_CXX26 void zero() const { i = 0; }
     };
 
     class S {
       int val_;
 
     public:
-      S(int val) : val_(val) {}
+      TEST_CONSTEXPR_CXX26 S(int val) : val_(val) {}
 
       T j;
 
-      bool operator<(const S& rhs) const { return val_ < rhs.val_; }
+      TEST_CONSTEXPR_CXX26 bool operator<(const S& rhs) const { return val_ < rhs.val_; }
     };
 
     { // Iterator overload

>From 330720d49f57abc087a9c7e9aa4df5fedca03eb9 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Thu, 25 Jun 2026 08:38:06 +0100
Subject: [PATCH 14/21] cleanup

---
 libcxx/include/set | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/include/set b/libcxx/include/set
index 565778a836fd1..a8e245b22ae51 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -10,8 +10,6 @@
 #ifndef _LIBCPP_SET
 #define _LIBCPP_SET
 
-// TODO: add constexpr everywhere
-
 /*
 
     set synopsis

>From 6c70a6075dcb19eccb6e2da3205aa1b97c8f37f0 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 27 Jun 2026 13:54:32 +0100
Subject: [PATCH 15/21] Initial constexpr

---
 libcxx/include/set                            | 249 +++++++++++-------
 .../associative/multiset/clear.pass.cpp       |  11 +-
 .../associative/multiset/count.pass.cpp       |  11 +-
 .../multiset/count_transparent.pass.cpp       |  11 +-
 .../associative/multiset/emplace.pass.cpp     |  11 +-
 .../multiset/emplace_hint.pass.cpp            |  11 +-
 .../associative/multiset/empty.pass.cpp       |  11 +-
 .../associative/multiset/equal_range.pass.cpp |  13 +-
 .../multiset/equal_range_transparent.pass.cpp |  13 +-
 .../associative/multiset/erase_iter.pass.cpp  |  11 +-
 .../multiset/erase_iter_iter.pass.cpp         |  11 +-
 .../associative/multiset/erase_key.pass.cpp   |  11 +-
 .../multiset/extract_iterator.pass.cpp        |  11 +-
 .../associative/multiset/extract_key.pass.cpp |  11 +-
 .../associative/multiset/find.pass.cpp        |  13 +-
 .../multiset/get_allocator.pass.cpp           |  11 +-
 .../multiset/incomplete_type.pass.cpp         |  13 +-
 .../associative/multiset/insert_cv.pass.cpp   |  11 +-
 ...rt_emplace_allocator_requirements.pass.cpp |   9 +-
 .../multiset/insert_initializer_list.pass.cpp |  11 +-
 .../multiset/insert_iter_cv.pass.cpp          |  11 +-
 .../multiset/insert_iter_iter.pass.cpp        |   7 +-
 .../multiset/insert_iter_rv.pass.cpp          |  11 +-
 .../multiset/insert_node_type.pass.cpp        |  11 +-
 .../multiset/insert_node_type_hint.pass.cpp   |  11 +-
 .../multiset/insert_range.pass.cpp            |  11 +-
 .../associative/multiset/insert_rv.pass.cpp   |  11 +-
 .../associative/multiset/iterator.pass.cpp    |  33 ++-
 .../associative/multiset/lower_bound.pass.cpp |  13 +-
 .../associative/multiset/max_size.pass.cpp    |  11 +-
 .../associative/multiset/merge.pass.cpp       |  17 +-
 .../multiset/multiset.cons/alloc.pass.cpp     |  11 +-
 .../assign_initializer_list.pass.cpp          |  11 +-
 .../multiset/multiset.cons/compare.pass.cpp   |  11 +-
 .../multiset.cons/compare_alloc.pass.cpp      |  11 +-
 .../multiset/multiset.cons/copy.pass.cpp      |  11 +-
 .../multiset.cons/copy_alloc.pass.cpp         |  11 +-
 .../multiset.cons/copy_assign.pass.cpp        |  11 +-
 .../multiset/multiset.cons/deduct.pass.cpp    |   9 +-
 .../multiset/multiset.cons/default.pass.cpp   |  11 +-
 .../multiset.cons/default_noexcept.pass.cpp   |  11 +-
 .../multiset.cons/dtor_noexcept.pass.cpp      |  11 +-
 .../multiset.cons/from_range.pass.cpp         |  13 +-
 .../multiset.cons/initializer_list.pass.cpp   |  11 +-
 .../initializer_list_compare.pass.cpp         |  11 +-
 .../initializer_list_compare_alloc.pass.cpp   |  11 +-
 .../multiset/multiset.cons/iter_iter.pass.cpp |  11 +-
 .../multiset.cons/iter_iter_alloc.pass.cpp    |  11 +-
 .../multiset.cons/iter_iter_comp.pass.cpp     |  11 +-
 .../multiset/multiset.cons/move.pass.cpp      |  11 +-
 .../multiset.cons/move_alloc.pass.cpp         |  11 +-
 .../multiset.cons/move_assign.pass.cpp        |  11 +-
 .../multiset.cons/move_noexcept.pass.cpp      |  11 +-
 .../multiset.erasure/erase_if.pass.cpp        |  11 +-
 .../compare.three_way.pass.cpp                |  11 +-
 .../multiset.nonmember/op_compare.pass.cpp    |   9 +-
 .../multiset/multiset.observers/comp.pass.cpp |  13 +-
 .../multiset.special/member_swap.pass.cpp     |  11 +-
 .../multiset.special/non_member_swap.pass.cpp |  11 +-
 .../multiset.special/swap_noexcept.pass.cpp   |  13 +-
 .../associative/multiset/size.pass.cpp        |  10 +-
 .../associative/multiset/upper_bound.pass.cpp |  13 +-
 62 files changed, 723 insertions(+), 232 deletions(-)

diff --git a/libcxx/include/set b/libcxx/include/set
index a8e245b22ae51..0409131ac6981 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -1176,31 +1176,34 @@ public:
   friend class multiset;
 
   // construct/copy/destroy:
-  _LIBCPP_HIDE_FROM_ABI multiset() _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset() _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_default_constructible<key_compare>::value&&
           is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(value_compare()) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const value_compare& __comp) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const value_compare& __comp) _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(__comp) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const value_compare& __comp, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {}
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__f, __l);
   }
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multiset(__f, __l, key_compare(), __a) {}
 #  endif
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__f, __l);
@@ -1208,7 +1211,7 @@ public:
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(from_range_t,
            _Range&& __range,
            const key_compare& __comp = key_compare(),
@@ -1218,213 +1221,272 @@ public:
   }
 
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI multiset(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(from_range_t, _Range&& __range, const allocator_type& __a)
       : multiset(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(const multiset& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(const multiset& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(const multiset& __s) = default;
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(multiset&& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s, const allocator_type& __a) : __tree_(std::move(__s.__tree_), __a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(multiset&& __s, const allocator_type& __a)
+      : __tree_(std::move(__s.__tree_), __a) {}
 #  endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const allocator_type& __a) : __tree_(__a) {}
-  _LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s, const allocator_type& __a) : __tree_(__s.__tree_, __a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const allocator_type& __a) : __tree_(__a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(const multiset& __s, const allocator_type& __a)
+      : __tree_(__s.__tree_, __a) {}
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__il.begin(), __il.end());
   }
 
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(initializer_list<value_type> __il, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__il.begin(), __il.end());
   }
 
 #    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(initializer_list<value_type> __il, const allocator_type& __a)
       : multiset(__il, key_compare(), __a) {}
 #    endif
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(initializer_list<value_type> __il) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(initializer_list<value_type> __il) {
     clear();
     insert(__il.begin(), __il.end());
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(multiset&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(multiset&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI ~multiset() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~multiset() {
     static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return __tree_.begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return __tree_.begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return __tree_.end(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return __tree_.end(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT {
+    return __tree_.begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator begin() const _NOEXCEPT {
+    return __tree_.begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator end() _NOEXCEPT {
+    return __tree_.end();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator end() const _NOEXCEPT {
+    return __tree_.end();
+  }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT { return reverse_iterator(end()); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rbegin() const _NOEXCEPT {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rbegin() _NOEXCEPT {
+    return reverse_iterator(end());
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator
+  rbegin() const _NOEXCEPT {
     return const_reverse_iterator(end());
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI reverse_iterator rend() _NOEXCEPT { return reverse_iterator(begin()); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const _NOEXCEPT {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rend() _NOEXCEPT {
+    return reverse_iterator(begin());
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator rend() const _NOEXCEPT {
     return const_reverse_iterator(begin());
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const _NOEXCEPT { return begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator cend() const _NOEXCEPT { return end(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crbegin() const _NOEXCEPT { return rbegin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const _NOEXCEPT { return rend(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cbegin() const _NOEXCEPT {
+    return begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cend() const _NOEXCEPT {
+    return end();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator
+  crbegin() const _NOEXCEPT {
+    return rbegin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator crend() const _NOEXCEPT {
+    return rend();
+  }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT { return __tree_.size() == 0; }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __tree_.size(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT { return __tree_.max_size(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool empty() const _NOEXCEPT {
+    return __tree_.size() == 0;
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type size() const _NOEXCEPT {
+    return __tree_.size();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type max_size() const _NOEXCEPT {
+    return __tree_.max_size();
+  }
 
   // modifiers:
 #  ifndef _LIBCPP_CXX03_LANG
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace(_Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace(_Args&&... __args) {
     return __tree_.__emplace_multi(std::forward<_Args>(__args)...);
   }
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace_hint(const_iterator __p, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace_hint(const_iterator __p, _Args&&... __args) {
     return __tree_.__emplace_hint_multi(__p, std::forward<_Args>(__args)...);
   }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const value_type& __v) { return __tree_.__emplace_multi(__v); }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, const value_type& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const value_type& __v) {
+    return __tree_.__emplace_multi(__v);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, const value_type& __v) {
     return __tree_.__emplace_hint_multi(__p, __v);
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __first, _InputIterator __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(_InputIterator __first, _InputIterator __last) {
     __tree_.__insert_range_multi(__first, __last);
   }
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert_range(_Range&& __range) {
     __tree_.__insert_range_multi(ranges::begin(__range), ranges::end(__range));
   }
 #  endif
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI iterator insert(value_type&& __v) { return __tree_.__emplace_multi(std::move(__v)); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(value_type&& __v) {
+    return __tree_.__emplace_multi(std::move(__v));
+  }
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, value_type&& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, value_type&& __v) {
     return __tree_.__emplace_hint_multi(__p, std::move(__v));
   }
 
-  _LIBCPP_HIDE_FROM_ABI void insert(initializer_list<value_type> __il) { insert(__il.begin(), __il.end()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(initializer_list<value_type> __il) {
+    insert(__il.begin(), __il.end());
+  }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
-  _LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_multi(__k); }
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
-  _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p) { return __tree_.erase(__p); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(const key_type& __k) {
+    return __tree_.__erase_multi(__k);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
+    return __tree_.erase(__f, __l);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
-  _LIBCPP_HIDE_FROM_ABI iterator insert(node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to multiset::insert()");
     return __tree_.template __node_handle_insert_multi<node_type>(std::move(__nh));
   }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __hint, node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __hint, node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to multiset::insert()");
     return __tree_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract<node_type>(__it);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI void swap(multiset& __s) _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(multiset& __s)
+      _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
     __tree_.swap(__s.__tree_);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const _NOEXCEPT { return __tree_.__alloc(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI key_compare key_comp() const { return __tree_.value_comp(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI value_compare value_comp() const { return __tree_.value_comp(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type get_allocator() const _NOEXCEPT {
+    return __tree_.__alloc();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_compare key_comp() const {
+    return __tree_.value_comp();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_compare value_comp() const {
+    return __tree_.value_comp();
+  }
 
   // set operations:
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __tree_.find(__k); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __tree_.find(__k); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const key_type& __k) {
+    return __tree_.find(__k);
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const key_type& __k) const {
+    return __tree_.find(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const _K2& __k) {
     return __tree_.find(__k);
   }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const _K2& __k) const {
     return __tree_.find(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const key_type& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const _K2& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  endif
 
 #  if _LIBCPP_STD_VER >= 20
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const key_type& __k) const {
+    return find(__k) != end();
+  }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
 #  endif // _LIBCPP_STD_VER >= 20
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const key_type& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const key_type& __k) {
     return __tree_.__lower_bound_multi(__k);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  lower_bound(const key_type& __k) const {
     return __tree_.__lower_bound_multi(__k);
   }
 
@@ -1432,23 +1494,25 @@ public:
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const _K2& __k) {
     return __tree_.__lower_bound_multi(__k);
   }
 
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  lower_bound(const _K2& __k) const {
     return __tree_.__lower_bound_multi(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const key_type& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const key_type& __k) {
     return __tree_.__upper_bound_multi(__k);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  upper_bound(const key_type& __k) const {
     return __tree_.__upper_bound_multi(__k);
   }
 
@@ -1456,30 +1520,35 @@ public:
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const _K2& __k) {
     return __tree_.__upper_bound_multi(__k);
   }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  upper_bound(const _K2& __k) const {
     return __tree_.__upper_bound_multi(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const key_type& __k) {
+  [[__nodiscard__]]
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const key_type& __k) {
     return __tree_.__equal_range_multi(__k);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const key_type& __k) const {
     return __tree_.__equal_range_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
+  [[__nodiscard__]]
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const _K2& __k) {
     return __tree_.__equal_range_multi(__k);
   }
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const _K2& __k) const {
     return __tree_.__equal_range_multi(__k);
   }
 #  endif
@@ -1543,7 +1612,7 @@ struct __specialized_algorithm<_Alg, __single_range<multiset<_Key, _Compare, _Al
 
   // set's begin() and end() are identical with and without const qualification
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI static auto operator()(const __set& __set, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static auto operator()(const __set& __set, _Args&&... __args) {
     return __specialized_algorithm<_Alg, __single_range<typename __set::__base>>()(
         __set.__tree_, std::forward<_Args>(__args)...);
   }
@@ -1551,7 +1620,7 @@ struct __specialized_algorithm<_Alg, __single_range<multiset<_Key, _Compare, _Al
 #  endif
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator==(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return __x.size() == __y.size() && std::equal(__x.begin(), __x.end(), __y.begin());
 }
@@ -1559,31 +1628,31 @@ operator==(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key,
 #  if _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare(__x.begin(), __x.end(), __y.begin(), __y.end());
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator!=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return !(__x == __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return __y < __x;
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return !(__x < __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return !(__y < __x);
 }
@@ -1591,7 +1660,7 @@ operator<=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key,
 #  else // _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI __synth_three_way_result<_Key>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __synth_three_way_result<_Key>
 operator<=>(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare_three_way(__x.begin(), __x.end(), __y.begin(), __y.end(), __synth_three_way);
 }
@@ -1599,7 +1668,7 @@ operator<=>(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key
 #  endif // _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI void
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
 swap(multiset<_Key, _Compare, _Allocator>& __x, multiset<_Key, _Compare, _Allocator>& __y)
     _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
   __x.swap(__y);
@@ -1607,7 +1676,7 @@ swap(multiset<_Key, _Compare, _Allocator>& __x, multiset<_Key, _Compare, _Alloca
 
 #  if _LIBCPP_STD_VER >= 20
 template <class _Key, class _Compare, class _Allocator, class _Predicate>
-inline _LIBCPP_HIDE_FROM_ABI typename multiset<_Key, _Compare, _Allocator>::size_type
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename multiset<_Key, _Compare, _Allocator>::size_type
 erase_if(multiset<_Key, _Compare, _Allocator>& __c, _Predicate __pred) {
   return std::__libcpp_erase_if_container(__c, __pred);
 }
diff --git a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
index 61a5f25eac608..1c9d7798b81bf 100644
--- a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// void clear() noexcept;
+// void clear() noexcept; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -42,5 +42,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/count.pass.cpp b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
index 6ad96b232083c..ad7b9be77c36a 100644
--- a/libcxx/test/std/containers/associative/multiset/count.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type count(const key_type& k) const;
+// size_type count(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -124,5 +124,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
index aea63384887d4..33eea24eb0476 100644
--- a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class multiset
 
 // template<typename K>
-//     size_type count(const K& x) const;        // C++14
+//     size_type count(const K& x) const;        // C++14, constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -29,11 +29,18 @@ struct Comp {
   bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<std::pair<int, int>, Comp> s{{2, 1}, {1, 1}, {1, 1}, {1, 1}, {2, 2}};
 
   auto cnt = s.count(1);
   assert(cnt == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
index 97cae1c565216..051522f9a55f2 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
@@ -13,7 +13,7 @@
 // class multiset
 
 // template <class... Args>
-//   iterator emplace(Args&&... args);
+//   iterator emplace(Args&&... args); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -23,7 +23,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
@@ -84,5 +84,12 @@ int main(int, char**) {
     assert(set.begin() == iter);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
index d8e9d1cd4d1c6..7c77dda795a71 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
@@ -13,7 +13,7 @@
 // class multiset
 
 // template <class... Args>
-//   iterator emplace_hint(const_iterator position, Args&&... args);
+//   iterator emplace_hint(const_iterator position, Args&&... args); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "DefaultOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
@@ -78,5 +78,12 @@ int main(int, char**) {
     assert(*r == 2);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
index 944f610a0c507..d730d69dc4e09 100644
--- a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// bool empty() const;
+// bool empty() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     M m;
@@ -40,5 +40,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
index e5c1d2b1eab98..03d469c9c5b0a 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-// pair<iterator,iterator>             equal_range(const key_type& k);
-// pair<const_iterator,const_iterator> equal_range(const key_type& k) const;
+// pair<iterator,iterator>             equal_range(const key_type& k); // constexpr since C++26
+// pair<const_iterator,const_iterator> equal_range(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -205,5 +205,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
index 6402488a44f22..6da1813338c3b 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
@@ -14,10 +14,10 @@
 
 // template<typename K>
 //     pair<iterator,iterator>             equal_range(const K& x);        //
-//     C++14
+//     C++14, constexpr since C++26
 // template<typename K>
 //     pair<const_iterator,const_iterator> equal_range(const K& x) const;  //
-//     C++14
+//     C++14, constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -33,7 +33,7 @@ struct Comp {
   bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<std::pair<int, int>, Comp> s{{2, 1}, {1, 1}, {1, 1}, {1, 1}, {2, 2}};
 
   auto er   = s.equal_range(1);
@@ -46,5 +46,12 @@ int main(int, char**) {
 
   assert(nels == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
index 31f00feda2977..3cc984c619490 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator erase(const_iterator position);
+// iterator erase(const_iterator position); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -25,7 +25,7 @@ struct TemplateConstructor {
 
 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -179,5 +179,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
index a3937d13bbb4b..6a6b2831fb515 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator erase(const_iterator first, const_iterator last);
+// iterator erase(const_iterator first, const_iterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -118,5 +118,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
index ba04a238e44b5..3503e3de4ffb7 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type erase(const key_type& k);
+// size_type erase(const key_type& k); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -104,5 +104,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
index 7f32fa82c46ab..f384b3ee95a67 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// node_type extract(const_iterator);
+// node_type extract(const_iterator); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -35,7 +35,7 @@ void test(Container& c) {
   assert(c.size() == 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     using set_type = std::multiset<int>;
     set_type m     = {1, 2, 3, 4, 5, 6};
@@ -55,5 +55,12 @@ int main(int, char**) {
     test(m);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
index ec75a6c5d835c..55896faa61ad3 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// node_type extract(key_type const&);
+// node_type extract(key_type const&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -41,7 +41,7 @@ void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> m = {1, 2, 3, 4, 5, 6};
     int keys[]           = {1, 2, 3, 4, 5, 6};
@@ -78,5 +78,12 @@ int main(int, char**) {
     test(m, std::begin(keys), std::end(keys));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/find.pass.cpp b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
index 866de0da5ea93..b7cb95a5d1a2b 100644
--- a/libcxx/test/std/containers/associative/multiset/find.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-//       iterator find(const key_type& k);
-// const_iterator find(const key_type& k) const;
+//       iterator find(const key_type& k); // constexpr since C++26
+// const_iterator find(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -202,5 +202,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
index 55bbd567d4360..f07d83e2fa18d 100644
--- a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// allocator_type get_allocator() const
+// allocator_type get_allocator() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::allocator<int> alloc;
     const std::multiset<int> s(alloc);
@@ -29,5 +29,12 @@ int main(int, char**) {
     const std::multiset<int, std::less<int>, other_allocator<int> > s(alloc);
     assert(s.get_allocator() == alloc);
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
index 31cb96ec33e76..3313ab39c0934 100644
--- a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
@@ -23,10 +23,17 @@ struct A {
   Set::const_iterator cit;
 };
 
-inline bool operator==(A const& L, A const& R) { return &L == &R; }
-inline bool operator<(A const& L, A const& R) { return L.data < R.data; }
-int main(int, char**) {
+inline TEST_CONSTEXPR_CXX26 bool operator==(A const& L, A const& R) { return &L == &R; }
+inline TEST_CONSTEXPR_CXX26 bool operator<(A const& L, A const& R) { return L.data < R.data; }
+TEST_CONSTEXPR_CXX26 bool test() {
   A a;
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
index 237cf9bb6fac2..22540133feb6d 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator insert(const value_type& v);
+// iterator insert(const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -48,7 +48,7 @@ void do_insert_cv_test() {
   assert(*r == 3);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   do_insert_cv_test<std::multiset<int> >();
 #if TEST_STD_VER >= 11
   {
@@ -57,5 +57,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
index 8085ac7cbb2f1..8f3d61009410a 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
@@ -20,9 +20,16 @@
 #include "container_test_types.h"
 #include "../../set_allocator_requirement_test_templates.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   testMultisetInsert<TCT::multiset<> >();
   testMultisetEmplace<TCT::multiset<> >();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
index 8f4f165252d36..7a25a2fdc96c1 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// void insert(initializer_list<value_type> il);
+// void insert(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> C;
     typedef C::value_type V;
@@ -57,5 +57,12 @@ int main(int, char**) {
     assert(*++i == V(10));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
index 97c03be6cc57b..e271b078ab317 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator insert(const_iterator position, const value_type& v);
+// iterator insert(const_iterator position, const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef M::iterator R;
@@ -70,5 +70,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
index 56483714cd50c..f4d4007b08d09 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
@@ -21,7 +21,7 @@
 #include "test_iterators.h"
 
 template <class Iter, class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   {   // Check that an empty range works correctly
     { // Without elements in the container
       using Map = std::multiset<int, std::less<int>, Alloc>;
@@ -184,13 +184,16 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   test_alloc<cpp17_input_iterator<int*>, std::allocator<int> >();
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
 }
 
 int main(int, char**) {
   test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
 
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
index d01f6b9b2c306..99bcba0ed97f8 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(const_iterator position, value_type&& v);
+// iterator insert(const_iterator position, value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> M;
     typedef M::iterator R;
@@ -71,5 +71,12 @@ int main(int, char**) {
     assert(*r == 3);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
index 6971d6b6e5eae..10f0d730a4444 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(node_type&&);
+// iterator insert(node_type&&); // constexpr since C++26
 
 #include <set>
 #include <type_traits>
@@ -63,11 +63,18 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<int> m;
   test(m);
   std::multiset<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
index b0392088beb6b..3224a73c9abbf 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(const_iterator hint, node_type&&);
+// iterator insert(const_iterator hint, node_type&&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -45,11 +45,18 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<int> m;
   test(m);
   std::multiset<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
index d57e14f4b59c8..e7ae071487b06 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -13,14 +13,14 @@
 // <set>
 
 // template<container-compatible-range<value_type> R>
-//   void insert_range(R&& rg); // C++23
+//   void insert_range(R&& rg); // C++23, constexpr since C++26
 
 #include <set>
 
 #include "../../insert_range_maps_sets.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
     test_map_set_insert_range<std::multiset<int, test_less<int>, Alloc>, int, Iter, Sent>(/*allow_duplicates=*/true);
   });
@@ -32,5 +32,12 @@ int main(int, char**) {
   test_set_insert_range_exception_safety_throwing_copy<std::multiset>();
   test_assoc_set_insert_range_exception_safety_throwing_allocator<std::multiset, int>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
index 88155572f1680..2e8de11823690 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(value_type&& v);
+// iterator insert(value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> M;
     typedef M::iterator R;
@@ -71,5 +71,12 @@ int main(int, char**) {
     assert(*r == 3);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
index fee8bf9d3eb30..9b510af6deefc 100644
--- a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
@@ -10,20 +10,20 @@
 
 // class multiset
 
-//       iterator begin();
-// const_iterator begin() const;
-//       iterator end();
-// const_iterator end()   const;
+//       iterator begin(); // constexpr since C++26
+// const_iterator begin() const; // constexpr since C++26
+//       iterator end(); // constexpr since C++26
+// const_iterator end()   const; // constexpr since C++26
 //
-//       reverse_iterator rbegin();
-// const_reverse_iterator rbegin() const;
-//       reverse_iterator rend();
-// const_reverse_iterator rend()   const;
+//       reverse_iterator rbegin(); // constexpr since C++26
+// const_reverse_iterator rbegin() const; // constexpr since C++26
+//       reverse_iterator rend(); // constexpr since C++26
+// const_reverse_iterator rend()   const; // constexpr since C++26
 //
-// const_iterator         cbegin()  const;
-// const_iterator         cend()    const;
-// const_reverse_iterator crbegin() const;
-// const_reverse_iterator crend()   const;
+// const_iterator         cbegin()  const; // constexpr since C++26
+// const_iterator         cend()    const; // constexpr since C++26
+// const_reverse_iterator crbegin() const; // constexpr since C++26
+// const_reverse_iterator crend()   const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -32,7 +32,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8};
@@ -137,5 +137,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
index ddd39d8fc0348..4f88cec396700 100644
--- a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-//       iterator lower_bound(const key_type& k);
-// const_iterator lower_bound(const key_type& k) const;
+//       iterator lower_bound(const key_type& k); // constexpr since C++26
+// const_iterator lower_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -165,5 +165,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
index a6cff58b090bd..5df2970223608 100644
--- a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type max_size() const;
+// size_type max_size() const; // constexpr since C++26
 
 #include <cassert>
 #include <limits>
@@ -20,7 +20,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef limited_allocator<int, 10> A;
     typedef std::multiset<int, std::less<int>, A> C;
@@ -44,5 +44,12 @@ int main(int, char**) {
     assert(c.max_size() <= alloc_max_size(c.get_allocator()));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
index 90eaeb2adbfcb..9962fa55bb199 100644
--- a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
@@ -13,13 +13,13 @@
 // class multiset
 
 // template <class C2>
-//   void merge(set<Key, C2, Allocator>& source);
+//   void merge(set<Key, C2, Allocator>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(set<Key, C2, Allocator>&& source);
+//   void merge(set<Key, C2, Allocator>&& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<Key, C2, Allocator>& source);
+//   void merge(multiset<Key, C2, Allocator>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<Key, C2, Allocator>&& source);
+//   void merge(multiset<Key, C2, Allocator>&& source); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -46,7 +46,7 @@ struct throw_comparator {
 };
 #endif
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> src{1, 3, 5};
     std::multiset<int> dst{2, 4, 5};
@@ -134,5 +134,12 @@ int main(int, char**) {
       first.merge(std::move(second));
     }
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
index 1f05201d706b1..d854ef97fce0e 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const allocator_type& a);
+// multiset(const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::less<int> C;
   typedef test_allocator<int> A;
   std::multiset<int, C, A> m(A(5));
@@ -26,5 +26,12 @@ int main(int, char**) {
   assert(m.begin() == m.end());
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
index 7234e61d28bfa..096202b3de35a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset& operator=(initializer_list<value_type> il);
+// multiset& operator=(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> C;
     typedef C::value_type V;
@@ -52,5 +52,12 @@ int main(int, char**) {
     assert(*++i == V(6));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
index b7da7f66b020d..07066563cc459 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// explicit multiset(const key_compare& comp);
+// explicit multiset(const key_compare& comp); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   const std::multiset<int, C> m(C(3));
   assert(m.empty());
@@ -26,5 +26,12 @@ int main(int, char**) {
   assert(m.key_comp() == C(3));
   assert(m.value_comp() == C(3));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
index be8a451fdc405..88e12d3d93f82 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const value_compare& comp, const allocator_type& a);
+// multiset(const value_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   typedef test_allocator<int> A;
   std::multiset<int, C, A> m(C(4), A(5));
@@ -28,5 +28,12 @@ int main(int, char**) {
   assert(m.key_comp() == C(4));
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
index c747828aa01cc..b9be5da01f053 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const multiset& m);
+// multiset(const multiset& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -129,8 +129,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
index 868072ef15288..d6bfd5ca78b22 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const multiset& m, const allocator_type& a);
+// multiset(const multiset& m, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -100,8 +100,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
index 0a5fe62eed69e..05c709e45af8d 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset& operator=(const multiset& s);
+// multiset& operator=(const multiset& s); // constexpr since C++26
 
 #include <algorithm>
 #include <cassert>
@@ -275,8 +275,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
index f9797ca571c47..e25c08e7a81e6 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
@@ -52,7 +52,7 @@ struct NotAnAllocator {
   friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     const int arr[] = {1, 2, 1, INT_MAX, 3};
     std::multiset s(std::begin(arr), std::end(arr));
@@ -212,5 +212,12 @@ int main(int, char**) {
 
   AssociativeContainerDeductionGuidesSfinaeAway<std::multiset, std::multiset<int>>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
index 4f7dabfea2c68..cd2e63d171e21 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset();
+// multiset(); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> m;
     assert(m.empty());
@@ -51,5 +51,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
index 052b032d6e798..58fcb5fb5d337 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
@@ -12,7 +12,7 @@
 //    noexcept(
 //        is_nothrow_default_constructible<allocator_type>::value &&
 //        is_nothrow_default_constructible<key_compare>::value &&
-//        is_nothrow_copy_constructible<key_compare>::value);
+//        is_nothrow_copy_constructible<key_compare>::value); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -32,7 +32,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::multiset<MoveOnly> C;
@@ -52,5 +52,12 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_default_constructible<C>::value, "");
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
index bf5d256e19a0f..cf0d87f879dde 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
@@ -8,7 +8,7 @@
 
 // <set>
 
-// ~multiset() // implied noexcept;
+// ~multiset() // implied noexcept; // constexpr since C++26
 
 // UNSUPPORTED: c++03
 
@@ -27,7 +27,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> C;
     static_assert(std::is_nothrow_destructible<C>::value, "");
@@ -47,5 +47,12 @@ int main(int, char**) {
   }
 #endif // _LIBCPP_VERSION
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
index 15275781a8aac..1b4af3e503bbe 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
@@ -9,11 +9,11 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 // template<container-compatible-range<value_type> R>
-//   multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
+//   multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23, constexpr since C++26
 //
 // template<container-compatible-range<value_type> R>
 //   multiset(from_range_t, R&& rg, const Allocator& a))
-//     : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+//     : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23, constexpr since C++26
 
 #include <algorithm>
 #include <array>
@@ -28,7 +28,7 @@ void test_duplicates() {
   assert(std::ranges::is_permutation(input, c));
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
     test_associative_set<std::multiset, int, Iter, Sent, test_less<int>, Alloc>();
   });
@@ -40,5 +40,12 @@ int main(int, char**) {
   test_set_exception_safety_throwing_copy<std::multiset>();
   test_set_exception_safety_throwing_allocator<std::multiset, int>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
index c860f17bb4196..5369116cb44b9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> C;
     typedef C::value_type V;
@@ -66,5 +66,12 @@ int main(int, char**) {
     assert(m.get_allocator() == a);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
index 5bb400cb82be9..cd2da63ac9c1d 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> Cmp;
   typedef std::multiset<int, Cmp> C;
   typedef C::value_type V;
@@ -37,5 +37,12 @@ int main(int, char**) {
   assert(*++i == V(6));
   assert(m.key_comp() == Cmp(10));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
index a12e68c1e582b..900d0a4546d1a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a);
+// multiset(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> Cmp;
   typedef test_allocator<int> A;
   typedef std::multiset<int, Cmp, A> C;
@@ -40,5 +40,12 @@ int main(int, char**) {
   assert(m.key_comp() == Cmp(10));
   assert(m.get_allocator() == A(4));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
index 6b98ceb1c0df7..192f2c4eacc65 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
@@ -11,7 +11,7 @@
 // class multiset
 
 // template <class InputIterator>
-//     multiset(InputIterator first, InputIterator last);
+//     multiset(InputIterator first, InputIterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_iterators.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -58,5 +58,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
index 50f8fda2a676b..f8db804e4c80a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // template <class InputIterator>
 //     multiset(InputIterator first, InputIterator last,
-//         const value_compare& comp, const allocator_type& a);
+//         const value_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -72,5 +72,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
index bed8d02f3e681..9e80b83b244d5 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
@@ -11,7 +11,7 @@
 // class multiset
 
 // template <class InputIterator>
-//     multiset(InputIterator first, InputIterator last, const value_compare& comp);
+//     multiset(InputIterator first, InputIterator last, const value_compare& comp); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -21,7 +21,7 @@
 #include "test_iterators.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
   typedef test_less<V> C;
@@ -40,5 +40,12 @@ int main(int, char**) {
   assert(*std::next(m.begin(), 7) == 3);
   assert(*std::next(m.begin(), 8) == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
index 2d71f21e182c9..70d9eb2307827 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(multiset&& s);
+// multiset(multiset&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef test_less<int> C;
@@ -94,5 +94,12 @@ int main(int, char**) {
     assert(std::distance(mo.begin(), mo.end()) == 0);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
index 771a7038bf633..8cd8b7727917a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(multiset&& s, const allocator_type& a);
+// multiset(multiset&& s, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -24,7 +24,7 @@
 #include "test_allocator.h"
 #include "Counter.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -111,5 +111,12 @@ int main(int, char**) {
     assert(Counter_base::gConstructed == 0);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
index 5cdd4ec67b8e0..cb83f4386eff0 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset& operator=(multiset&& s);
+// multiset& operator=(multiset&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -93,5 +93,12 @@ int main(int, char**) {
     assert(m1.empty());
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
index 73b4be5005e1c..89e4f4ab1d2ae 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
@@ -10,7 +10,7 @@
 
 // multiset(multiset&&)
 //        noexcept(is_nothrow_move_constructible<allocator_type>::value &&
-//                 is_nothrow_move_constructible<key_compare>::value);
+//                 is_nothrow_move_constructible<key_compare>::value); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -30,7 +30,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::multiset<MoveOnly> C;
@@ -50,5 +50,12 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_move_constructible<C>::value, "");
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
index cfb304ee3be39..cf132ddde4717 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
@@ -11,7 +11,7 @@
 
 // template <class T, class Compare, class Allocator, class Predicate>
 //   typename multiset<T, Compare, Allocator>::size_type
-//   erase_if(multiset<T, Compare, Allocator>& c, Predicate pred);
+//   erase_if(multiset<T, Compare, Allocator>& c, Predicate pred); // constexpr since C++26
 
 #include <set>
 
@@ -64,7 +64,7 @@ void test() {
   test0(S({1, 2, 3}), False, S({1, 2, 3}), 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test<std::multiset<int>>();
   test<std::multiset<int, std::less<int>, min_allocator<int>>>();
   test<std::multiset<int, std::less<int>, test_allocator<int>>>();
@@ -72,5 +72,12 @@ int main(int, char**) {
   test<std::multiset<long>>();
   test<std::multiset<double>>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
index fb575c880dd2e..c146a3da3e272 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
@@ -13,15 +13,22 @@
 
 // template<class Key, class Compare, class Allocator>
 //   synth-three-way-result<Key> operator<=>(const multiset<Key, Compare, Allocator>& x,
-//                                           const multiset<Key, Compare, Allocator>& y);
+//                                           const multiset<Key, Compare, Allocator>& y); // constexpr since C++26
 
 #include <cassert>
 #include <set>
 
 #include "test_container_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   assert(test_ordered_set_container_spaceship<std::multiset>());
   // `std::multiset` is not constexpr, so no `static_assert` test here.
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
index 9ffb5dc3593c5..b78fe1d518d95 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
@@ -38,7 +38,7 @@
 
 #include "test_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> s1, s2;
     s1.insert(1);
@@ -70,5 +70,12 @@ int main(int, char**) {
     const std::multiset<int>&cs1 = s1, cs2 = s2;
     assert(testComparisons(cs1, cs2, false, true));
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
index 63647a052c041..e4754ceea5e25 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
@@ -8,13 +8,13 @@
 
 // <set>
 
-// key_compare key_comp() const;
-// value_compare value_comp() const;
+// key_compare key_comp() const; // constexpr since C++26
+// value_compare value_comp() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::multiset<int> set_type;
 
   set_type s;
@@ -29,5 +29,12 @@ int main(int, char**) {
   assert(cs.value_comp()(*i1, *i2));
   assert(!cs.value_comp()(*i2, *i1));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
index af32e1882093d..7ddeedde1a2ab 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// void swap(multiset& m);
+// void swap(multiset& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -110,5 +110,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
index 53630b3ca0a01..5e08875e08119 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// void swap(multiset& m);
+// void swap(multiset& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   {
     typedef std::multiset<int> M;
@@ -100,5 +100,12 @@ int main(int, char**) {
     assert(m2.get_allocator() == A(1));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
index 7a7ccf87e9de9..047cb03264317 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
@@ -12,11 +12,11 @@
 
 // void swap(multiset& c)
 //     noexcept(!allocator_type::propagate_on_container_swap::value ||
-//              __is_nothrow_swappable<allocator_type>::value);
+//              __is_nothrow_swappable<allocator_type>::value); // constexpr since C++26
 //
 //  In C++17, the standard says that swap shall have:
 //     noexcept(allocator_traits<Allocator>::is_always_equal::value &&
-//              noexcept(swap(declval<Compare&>(), declval<Compare&>())));
+//              noexcept(swap(declval<Compare&>(), declval<Compare&>()))); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -86,7 +86,7 @@ struct some_alloc3 {
   typedef std::false_type is_always_equal;
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> C;
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
@@ -131,5 +131,12 @@ int main(int, char**) {
 #  endif // _LIBCPP_VERSION
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/size.pass.cpp b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
index 405437211b8ca..626f50f57734a 100644
--- a/libcxx/test/std/containers/associative/multiset/size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type size() const;
+// size_type size() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     M m;
@@ -55,6 +55,12 @@ int main(int, char**) {
     assert(m.size() == 0);
   }
 #endif
+}
 
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp b/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp
index c61859b1feffb..4e580d4318a71 100644
--- a/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-//       iterator upper_bound(const key_type& k);
-// const_iterator upper_bound(const key_type& k) const;
+//       iterator upper_bound(const key_type& k); // constexpr since C++26
+// const_iterator upper_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -163,6 +163,13 @@ int main(int, char**) {
     assert(r == std::next(m.begin(), 9));
   }
 #endif
+  return true;
+}
 
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

>From fda9890ab7f494bcf82005dd3ad88faf8fb27a39 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 27 Jun 2026 15:41:58 +0100
Subject: [PATCH 16/21] libcxx/test/std/containers/associative/multiset/

---
 .../multiset/count_transparent.pass.cpp       | 10 ++++--
 .../associative/multiset/emplace.pass.cpp     |  6 ++--
 .../multiset/emplace_hint.pass.cpp            |  6 ++--
 .../multiset/equal_range_transparent.pass.cpp | 10 ++++--
 .../associative/multiset/erase_iter.pass.cpp  |  4 +--
 .../multiset/extract_iterator.pass.cpp        |  4 +--
 .../associative/multiset/extract_key.pass.cpp |  4 +--
 .../associative/multiset/insert_cv.pass.cpp   |  2 +-
 ...rt_emplace_allocator_requirements.pass.cpp |  9 +-----
 .../multiset/insert_iter_iter.pass.cpp        |  3 +-
 .../multiset/insert_node_type.pass.cpp        | 10 +++---
 .../multiset/insert_node_type_hint.pass.cpp   |  9 +++---
 .../multiset/insert_range.pass.cpp            |  1 +
 .../associative/multiset/merge.pass.cpp       | 15 +++++----
 .../multiset/multiset.cons/copy.pass.cpp      |  8 ++---
 .../multiset.cons/copy_alloc.pass.cpp         |  8 ++---
 .../multiset.cons/copy_assign.pass.cpp        | 31 +++++++++----------
 .../multiset/multiset.cons/deduct.pass.cpp    |  2 +-
 .../multiset.cons/from_range.pass.cpp         |  3 +-
 .../multiset.cons/move_alloc.pass.cpp         |  2 +-
 .../associative/multiset/size.pass.cpp        |  1 +
 21 files changed, 75 insertions(+), 73 deletions(-)

diff --git a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
index 33eea24eb0476..c21da4a98fce3 100644
--- a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
@@ -19,14 +19,18 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
index 051522f9a55f2..06a566b293c5a 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
@@ -24,7 +24,7 @@
 #include "min_allocator.h"
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
     M m;
@@ -41,7 +41,9 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 2);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    assert(DefaultOnly::count == 0);
+  }
   {
     typedef std::multiset<Emplaceable> M;
     typedef M::iterator R;
diff --git a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
index 7c77dda795a71..ed686e8a10b66 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
@@ -24,7 +24,7 @@
 #include "min_allocator.h"
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
     M m;
@@ -41,7 +41,9 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 2);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    assert(DefaultOnly::count == 0);
+  }
   {
     typedef std::multiset<Emplaceable> M;
     typedef M::iterator R;
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
index 6da1813338c3b..6fd1df482f673 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
@@ -23,14 +23,18 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
index 3cc984c619490..e2285e69ea07c 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
@@ -20,10 +20,10 @@
 
 struct TemplateConstructor {
   template <typename T>
-  TemplateConstructor(const T&) {}
+  TEST_CONSTEXPR_CXX26 TemplateConstructor(const T&) {}
 };
 
-bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
+TEST_CONSTEXPR_CXX26 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
 TEST_CONSTEXPR_CXX26 bool test() {
   {
diff --git a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
index f384b3ee95a67..7723e7ea7e113 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   std::size_t sz = c.size();
 
   for (auto first = c.cbegin(); first != c.cend();) {
@@ -42,7 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     test(m);
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::multiset<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     assert(Counter_base::gConstructed == 6);
     test(m);
diff --git a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
index 55896faa61ad3..c930cee804e1a 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container, class KeyTypeIter>
-void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
+TEST_CONSTEXPR_CXX26 void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   std::size_t sz = c.size();
   assert((std::size_t)std::distance(first, last) == sz);
 
@@ -61,7 +61,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(!res);
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::multiset<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     {
       Counter<int> keys[] = {1, 2, 3, 4, 5, 6};
diff --git a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
index 22540133feb6d..6d28aab5b0214 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 
 template <class Container>
-void do_insert_cv_test() {
+TEST_CONSTEXPR_CXX26 void do_insert_cv_test() {
   typedef Container M;
   typedef typename M::iterator R;
   typedef typename M::value_type VT;
diff --git a/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
index 8f3d61009410a..8085ac7cbb2f1 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
@@ -20,16 +20,9 @@
 #include "container_test_types.h"
 #include "../../set_allocator_requirement_test_templates.h"
 
-TEST_CONSTEXPR_CXX26 bool test() {
+int main(int, char**) {
   testMultisetInsert<TCT::multiset<> >();
   testMultisetEmplace<TCT::multiset<> >();
 
-  return true;
-}
-int main(int, char**) {
-  test();
-#if TEST_STD_VER >= 26
-  static_assert(test());
-#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
index f4d4007b08d09..858ca520a304a 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
@@ -184,9 +184,10 @@ TEST_CONSTEXPR_CXX26 void test_alloc() {
   }
 }
 
-TEST_CONSTEXPR_CXX26 void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<cpp17_input_iterator<int*>, std::allocator<int> >();
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
+  return true;
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
index 10f0d730a4444..70889de77c1e5 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
@@ -20,18 +20,18 @@
 #include "min_allocator.h"
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto it = c.insert(key);
   return c.extract(it);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
+  Container c2;
 
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c, i);
     assert(!node.empty());
     typename Container::iterator it = c.insert(std::move(node));
     assert(node.empty());
@@ -50,7 +50,7 @@ void test(Container& c) {
   }
 
   { // Insert duplicate node.
-    typename Container::node_type dupl = nf(0);
+    typename Container::node_type dupl = nf(c2, 0);
     auto it                            = c.insert(std::move(dupl));
     assert(*it == 0);
   }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
index 3224a73c9abbf..3610c96ae788a 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
@@ -19,18 +19,19 @@
 #include "min_allocator.h"
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto it = c.insert(key);
   return c.extract(it);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
 
+  Container c2;
+
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c2, i);
     assert(!node.empty());
     std::size_t prev = c.size();
     auto it          = c.insert(c.end(), std::move(node));
diff --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
index e7ae071487b06..f89c783d5f568 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -9,6 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
 // ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-missing-field-initializers
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2147483647
 
 // <set>
 
diff --git a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
index 9962fa55bb199..5ddfa8b4e0b29 100644
--- a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
@@ -27,7 +27,7 @@
 #include "Counter.h"
 
 template <class Set>
-bool set_equal(const Set& set, Set other) {
+TEST_CONSTEXPR_CXX26 bool set_equal(const Set& set, Set other) {
   return set == other;
 }
 
@@ -35,10 +35,10 @@ bool set_equal(const Set& set, Set other) {
 struct throw_comparator {
   bool& should_throw_;
 
-  throw_comparator(bool& should_throw) : should_throw_(should_throw) {}
+  TEST_CONSTEXPR_CXX26 throw_comparator(bool& should_throw) : should_throw_(should_throw) {}
 
   template <class T>
-  bool operator()(const T& lhs, const T& rhs) const {
+  TEST_CONSTEXPR_CXX26 bool operator()(const T& lhs, const T& rhs) const {
     if (should_throw_)
       throw 0;
     return lhs < rhs;
@@ -56,7 +56,8 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
-  {
+
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     bool do_throw = false;
     typedef std::multiset<Counter<int>, throw_comparator> set_type;
     set_type src({1, 3, 5}, throw_comparator(do_throw));
@@ -75,13 +76,15 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(set_equal(dst, set_type({2, 4, 5}, throw_comparator(do_throw))));
   }
 #endif
-  assert(Counter_base::gConstructed == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    assert(Counter_base::gConstructed == 0);
+  }
   struct comparator {
     comparator() = default;
 
     bool operator()(const Counter<int>& lhs, const Counter<int>& rhs) const { return lhs < rhs; }
   };
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::multiset<Counter<int>, std::less<Counter<int>>> first_set_type;
     typedef std::multiset<Counter<int>, comparator> second_set_type;
     typedef std::set<Counter<int>, comparator> third_set_type;
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
index b9be5da01f053..ff0951bdbe06f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 
 template <template <class> class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   { // Simple check
     using Set = std::multiset<int, std::less<int>, Alloc<int> >;
 
@@ -81,7 +81,7 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator>();
   test_alloc<min_allocator>(); // Make sure that fancy pointers work
 
@@ -127,10 +127,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.get_allocator() == other_allocator<int>(10));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
index d6bfd5ca78b22..26881348de9d1 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 
 template <class Alloc>
-void test_alloc(const Alloc& new_alloc) {
+TEST_CONSTEXPR_CXX26 void test_alloc(const Alloc& new_alloc) {
   { // Simple check
     using Set = std::multiset<int, std::less<int>, Alloc>;
 
@@ -82,7 +82,7 @@ void test_alloc(const Alloc& new_alloc) {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc(std::allocator<int>());
   test_alloc(test_allocator<int>(25)); // Make sure that the new allocator is actually used
   test_alloc(min_allocator<int>());    // Make sure that fancy pointers work
@@ -98,10 +98,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
index 05c709e45af8d..c21e8a37b9ae1 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
@@ -33,41 +33,42 @@ class tracking_allocator {
   using value_type                             = T;
   using propagate_on_container_copy_assignment = std::true_type;
 
-  tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
 
   template <class U>
-  tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
 
-  T* allocate(std::size_t n) {
+  TEST_CONSTEXPR_CXX26 T* allocate(std::size_t n) {
     T* allocation = std::allocator<T>().allocate(n);
     allocs_->push_back(allocation);
     return allocation;
   }
 
-  void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
+  TEST_CONSTEXPR_CXX26 void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
     auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
     assert(res != allocs_->end() && "Trying to deallocate memory from different allocator?");
     allocs_->erase(res);
     std::allocator<T>().deallocate(ptr, n);
   }
 
-  friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  friend TEST_CONSTEXPR_CXX26 bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ == rhs.allocs_;
   }
 
-  friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  friend TEST_CONSTEXPR_CXX26 bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ != rhs.allocs_;
   }
 };
 
 struct NoOp {
-  void operator()() {}
+  TEST_CONSTEXPR_CXX26 void operator()() {}
 };
 
 template <class Alloc, class AllocatorInvariant = NoOp>
-void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
-                const Alloc& rhs_alloc                   = Alloc(),
-                AllocatorInvariant check_alloc_invariant = NoOp()) {
+TEST_CONSTEXPR_CXX26 void
+test_alloc(const Alloc& lhs_alloc                   = Alloc(),
+           const Alloc& rhs_alloc                   = Alloc(),
+           AllocatorInvariant check_alloc_invariant = NoOp()) {
   {   // Test empty/non-empty multiset combinations
     { // assign from a non-empty container into an empty one
       using Set = std::multiset<int, std::less<int>, Alloc>;
@@ -234,7 +235,7 @@ void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator<int> >();
 #if TEST_STD_VER >= 11
   test_alloc<min_allocator<int> >();
@@ -246,10 +247,10 @@ void test() {
       std::vector<void*>* rhs_allocs_;
 
     public:
-      AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
+      TEST_CONSTEXPR_CXX26 AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
           : lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
 
-      void operator()() {
+      TEST_CONSTEXPR_CXX26 void operator()() {
         assert(lhs_allocs_->empty());
         assert(rhs_allocs_->empty());
       }
@@ -273,10 +274,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
index e25c08e7a81e6..e3fead570e958 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
@@ -49,7 +49,7 @@
 #include "test_allocator.h"
 
 struct NotAnAllocator {
-  friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
+  friend TEST_CONSTEXPR_CXX26 bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
index 1b4af3e503bbe..338769516782a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2000000
 
 // template<container-compatible-range<value_type> R>
 //   multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23, constexpr since C++26
@@ -22,7 +23,7 @@
 #include "../../from_range_associative_containers.h"
 #include "test_macros.h"
 
-void test_duplicates() {
+TEST_CONSTEXPR_CXX26 void test_duplicates() {
   std::array input = {1, 2, 3, 3, 3, 4, 2, 1, 2};
   auto c           = std::multiset<int>(std::from_range, input);
   assert(std::ranges::is_permutation(input, c));
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
index 8cd8b7727917a..eece56da5aff4 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
@@ -73,7 +73,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(m3.key_comp() == C(5));
     LIBCPP_ASSERT(m1.empty());
   }
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef Counter<int> V;
     typedef std::less<V> C;
     typedef test_allocator<V> A;
diff --git a/libcxx/test/std/containers/associative/multiset/size.pass.cpp b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
index 626f50f57734a..4fe326b013fd4 100644
--- a/libcxx/test/std/containers/associative/multiset/size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
@@ -55,6 +55,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(m.size() == 0);
   }
 #endif
+  return true;
 }
 
 int main(int, char**) {

>From dd8615210cc23bb45756b3ec50848c5b074e37dc Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Mon, 29 Jun 2026 23:12:14 +0100
Subject: [PATCH 17/21] fix pending tests

---
 .../alg.foreach/for_each.associative.pass.cpp         |  8 ++------
 .../alg.foreach/ranges.for_each.associative.pass.cpp  | 11 ++---------
 .../multiset.nonmember/compare.three_way.pass.cpp     |  1 -
 3 files changed, 4 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
index 645e1e4af792e..7b0e5b93dc618 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
@@ -71,12 +71,8 @@ TEST_CONSTEXPR_CXX26 void test_node_container(Converter conv) {
 }
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  // FIXME: remove when set is made constexpr
-  if (!TEST_IS_CONSTANT_EVALUATED)
-    test_node_container<std::set<int> >([](int i) { return i; });
-  // FIXME: remove when multiset is made constexpr
-  if (!TEST_IS_CONSTANT_EVALUATED)
-    test_node_container<std::multiset<int> >([](int i) { return i; });
+  test_node_container<std::set<int> >([](int i) { return i; });
+  test_node_container<std::multiset<int> >([](int i) { return i; });
   test_node_container<std::map<int, int> >([](int i) { return std::make_pair(i, i); });
   test_node_container<std::multimap<int, int> >([](int i) { return std::make_pair(i, i); });
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
index 02698819d71df..72aa40a7f7e0a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
@@ -252,20 +252,13 @@ TEST_CONSTEXPR_CXX26 void test_invoke_map_like() {
 }
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when multiset is made constexpr
-    test_node_container<std::multiset<int> >([](int i) { return i; });
-  }
   test_node_container<std::set<int> >([](int i) { return i; });
+  test_node_container<std::multiset<int> >([](int i) { return i; });
   test_node_container<std::map<int, int> >([](int i) { return std::make_pair(i, i); });
   test_node_container<std::multimap<int, int> >([](int i) { return std::make_pair(i, i); });
 
-  if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when multiset is made constexpr
-    test_invoke_set_like<std::multiset>();
-  }
-
   test_invoke_set_like<std::set>();
+  test_invoke_set_like<std::multiset>();
   test_invoke_map_like<std::map>();
   test_invoke_map_like<std::multimap>();
 
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
index c146a3da3e272..9eaedb730745f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
@@ -22,7 +22,6 @@
 
 TEST_CONSTEXPR_CXX26 bool test() {
   assert(test_ordered_set_container_spaceship<std::multiset>());
-  // `std::multiset` is not constexpr, so no `static_assert` test here.
   return true;
 }
 int main(int, char**) {

>From 83f1ad1ea88dee3d78d35a3aa443c0712f037812 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Wed, 1 Jul 2026 21:23:50 +0100
Subject: [PATCH 18/21] missing include

---
 .../associative/multiset/multiset.observers/comp.pass.cpp       | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
index e4754ceea5e25..1bafc70e87af5 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
@@ -14,6 +14,8 @@
 #include <set>
 #include <cassert>
 
+#include "test_macros.h"
+
 TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::multiset<int> set_type;
 

>From 4c16e99618539bf2a1471d3e8a23a861a91f6ec6 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Wed, 1 Jul 2026 22:13:05 +0100
Subject: [PATCH 19/21] add \n

---
 libcxx/test/std/containers/associative/multiset/clear.pass.cpp   | 1 +
 libcxx/test/std/containers/associative/multiset/count.pass.cpp   | 1 +
 .../containers/associative/multiset/count_transparent.pass.cpp   | 1 +
 libcxx/test/std/containers/associative/multiset/emplace.pass.cpp | 1 +
 .../std/containers/associative/multiset/emplace_hint.pass.cpp    | 1 +
 libcxx/test/std/containers/associative/multiset/empty.pass.cpp   | 1 +
 .../std/containers/associative/multiset/equal_range.pass.cpp     | 1 +
 .../associative/multiset/equal_range_transparent.pass.cpp        | 1 +
 .../test/std/containers/associative/multiset/erase_iter.pass.cpp | 1 +
 .../std/containers/associative/multiset/erase_iter_iter.pass.cpp | 1 +
 .../test/std/containers/associative/multiset/erase_key.pass.cpp  | 1 +
 .../containers/associative/multiset/extract_iterator.pass.cpp    | 1 +
 .../std/containers/associative/multiset/extract_key.pass.cpp     | 1 +
 libcxx/test/std/containers/associative/multiset/find.pass.cpp    | 1 +
 .../std/containers/associative/multiset/get_allocator.pass.cpp   | 1 +
 .../std/containers/associative/multiset/incomplete_type.pass.cpp | 1 +
 .../test/std/containers/associative/multiset/insert_cv.pass.cpp  | 1 +
 .../associative/multiset/insert_initializer_list.pass.cpp        | 1 +
 .../std/containers/associative/multiset/insert_iter_cv.pass.cpp  | 1 +
 .../std/containers/associative/multiset/insert_iter_rv.pass.cpp  | 1 +
 .../containers/associative/multiset/insert_node_type.pass.cpp    | 1 +
 .../associative/multiset/insert_node_type_hint.pass.cpp          | 1 +
 .../std/containers/associative/multiset/insert_range.pass.cpp    | 1 +
 .../test/std/containers/associative/multiset/insert_rv.pass.cpp  | 1 +
 .../test/std/containers/associative/multiset/iterator.pass.cpp   | 1 +
 .../std/containers/associative/multiset/lower_bound.pass.cpp     | 1 +
 .../test/std/containers/associative/multiset/max_size.pass.cpp   | 1 +
 libcxx/test/std/containers/associative/multiset/merge.pass.cpp   | 1 +
 .../containers/associative/multiset/multiset.cons/alloc.pass.cpp | 1 +
 .../multiset/multiset.cons/assign_initializer_list.pass.cpp      | 1 +
 .../associative/multiset/multiset.cons/compare.pass.cpp          | 1 +
 .../associative/multiset/multiset.cons/compare_alloc.pass.cpp    | 1 +
 .../containers/associative/multiset/multiset.cons/copy.pass.cpp  | 1 +
 .../associative/multiset/multiset.cons/copy_alloc.pass.cpp       | 1 +
 .../associative/multiset/multiset.cons/copy_assign.pass.cpp      | 1 +
 .../associative/multiset/multiset.cons/deduct.pass.cpp           | 1 +
 .../associative/multiset/multiset.cons/default.pass.cpp          | 1 +
 .../associative/multiset/multiset.cons/default_noexcept.pass.cpp | 1 +
 .../associative/multiset/multiset.cons/dtor_noexcept.pass.cpp    | 1 +
 .../associative/multiset/multiset.cons/from_range.pass.cpp       | 1 +
 .../associative/multiset/multiset.cons/initializer_list.pass.cpp | 1 +
 .../multiset/multiset.cons/initializer_list_compare.pass.cpp     | 1 +
 .../multiset.cons/initializer_list_compare_alloc.pass.cpp        | 1 +
 .../associative/multiset/multiset.cons/iter_iter.pass.cpp        | 1 +
 .../associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp  | 1 +
 .../associative/multiset/multiset.cons/iter_iter_comp.pass.cpp   | 1 +
 .../containers/associative/multiset/multiset.cons/move.pass.cpp  | 1 +
 .../associative/multiset/multiset.cons/move_alloc.pass.cpp       | 1 +
 .../associative/multiset/multiset.cons/move_assign.pass.cpp      | 1 +
 .../associative/multiset/multiset.cons/move_noexcept.pass.cpp    | 1 +
 .../associative/multiset/multiset.erasure/erase_if.pass.cpp      | 1 +
 .../multiset/multiset.nonmember/compare.three_way.pass.cpp       | 1 +
 .../associative/multiset/multiset.nonmember/op_compare.pass.cpp  | 1 +
 .../associative/multiset/multiset.observers/comp.pass.cpp        | 1 +
 .../associative/multiset/multiset.special/member_swap.pass.cpp   | 1 +
 .../multiset/multiset.special/non_member_swap.pass.cpp           | 1 +
 .../associative/multiset/multiset.special/swap_noexcept.pass.cpp | 1 +
 57 files changed, 57 insertions(+)

diff --git a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
index 1c9d7798b81bf..b8110efe4a4ff 100644
--- a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
@@ -44,6 +44,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/count.pass.cpp b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
index ad7b9be77c36a..6bfcdc66aada9 100644
--- a/libcxx/test/std/containers/associative/multiset/count.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
@@ -126,6 +126,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
index c21da4a98fce3..815260f67c231 100644
--- a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
@@ -41,6 +41,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
index 06a566b293c5a..193926b6dfc87 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
@@ -88,6 +88,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
index ed686e8a10b66..5650b27d19a05 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
@@ -82,6 +82,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
index d730d69dc4e09..08027484fadb6 100644
--- a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
@@ -42,6 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
index 03d469c9c5b0a..7d623036c8e34 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
@@ -207,6 +207,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
index 6fd1df482f673..385b05c8f26b1 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
@@ -52,6 +52,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
index e2285e69ea07c..a7da30fcd2a96 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
@@ -181,6 +181,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
index 6a6b2831fb515..942b076c702ea 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
@@ -120,6 +120,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
index 3503e3de4ffb7..c6fe8f0563370 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
@@ -106,6 +106,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
index 7723e7ea7e113..c07a0d246fcf7 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
@@ -57,6 +57,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
index c930cee804e1a..52adc8ade796c 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
@@ -80,6 +80,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/find.pass.cpp b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
index b7cb95a5d1a2b..bb5be9a28502b 100644
--- a/libcxx/test/std/containers/associative/multiset/find.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
@@ -204,6 +204,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
index f07d83e2fa18d..3def31b6e0c84 100644
--- a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
@@ -31,6 +31,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
index 3313ab39c0934..63fd68a747c32 100644
--- a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
@@ -30,6 +30,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
index 6d28aab5b0214..556f29496dda2 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
@@ -59,6 +59,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
index 7a25a2fdc96c1..b561b5864c771 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
@@ -59,6 +59,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
index e271b078ab317..eb63f2f838891 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
@@ -72,6 +72,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
index 99bcba0ed97f8..874e560f6a533 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
@@ -73,6 +73,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
index 70889de77c1e5..4a20c2792c76e 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
@@ -71,6 +71,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
index 3610c96ae788a..b5c2aae45f01f 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
@@ -54,6 +54,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
index f89c783d5f568..590af11578f22 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -35,6 +35,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
index 2e8de11823690..387a99c042f7d 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
@@ -73,6 +73,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
index 9b510af6deefc..542ba871960ee 100644
--- a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
@@ -139,6 +139,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
index 4f88cec396700..c2883e039c8a9 100644
--- a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
@@ -167,6 +167,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
index 5df2970223608..b7b6d28d002ac 100644
--- a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
@@ -46,6 +46,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
index 5ddfa8b4e0b29..0397f16c52d41 100644
--- a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
@@ -139,6 +139,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
index d854ef97fce0e..fafcf50fa15ad 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
@@ -28,6 +28,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
index 096202b3de35a..914c07ed9d6f7 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
@@ -54,6 +54,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
index 07066563cc459..635b5999dbcd7 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
@@ -28,6 +28,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
index 88e12d3d93f82..e8138c0501263 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
@@ -30,6 +30,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
index ff0951bdbe06f..7d56ea93a9a45 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
@@ -130,6 +130,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
index 26881348de9d1..f79ae3713540b 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
@@ -101,6 +101,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
index c21e8a37b9ae1..86b86f92f3338 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
@@ -277,6 +277,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
index e3fead570e958..7a2cc45ae9cdc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
@@ -214,6 +214,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
index cd2e63d171e21..256b6f41dc3db 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
@@ -53,6 +53,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
index 58fcb5fb5d337..532a98066e51a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
@@ -54,6 +54,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
index cf0d87f879dde..2f81f954c8757 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
@@ -49,6 +49,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
index 338769516782a..ca1d7c20869b9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
@@ -43,6 +43,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
index 5369116cb44b9..bde073eeee9d7 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
@@ -68,6 +68,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
index cd2da63ac9c1d..f144c05352f09 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
@@ -39,6 +39,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
index 900d0a4546d1a..63d8199ea434e 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
@@ -42,6 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
index 192f2c4eacc65..bb41b35870444 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
@@ -60,6 +60,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
index f8db804e4c80a..22ac9bd799e45 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
@@ -74,6 +74,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
index 9e80b83b244d5..353f3bc84ad9f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
@@ -42,6 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
index 70d9eb2307827..a85823e464cbc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
@@ -96,6 +96,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
index eece56da5aff4..a3eaa419fbbab 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
@@ -113,6 +113,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
index cb83f4386eff0..60dc3a5bbc62f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
@@ -95,6 +95,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
index 89e4f4ab1d2ae..52519aaceab3f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
@@ -52,6 +52,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
index cf132ddde4717..147211766e693 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
@@ -74,6 +74,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
index 9eaedb730745f..ae3581104677c 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
@@ -24,6 +24,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
   assert(test_ordered_set_container_spaceship<std::multiset>());
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
index b78fe1d518d95..1d5df25bebfdc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
@@ -72,6 +72,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
index 1bafc70e87af5..1908991e40eed 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
@@ -33,6 +33,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
index 7ddeedde1a2ab..997fe1d818b53 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
@@ -112,6 +112,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
index 5e08875e08119..3ab2e1fbf55ce 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
@@ -102,6 +102,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
index 047cb03264317..37bdae66ee8e9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
@@ -133,6 +133,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26

>From ee60174486b206f0c625d988f4753b0532d270ce Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Wed, 1 Jul 2026 23:23:37 +0100
Subject: [PATCH 20/21] macro

---
 .../associative/multiset/multiset.erasure/erase_if.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
index 147211766e693..e0df9210c7da9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
@@ -20,14 +20,14 @@
 #include "min_allocator.h"
 
 template <class S, class Pred>
-void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
+TEST_CONSTEXPR_CXX26 void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
   ASSERT_SAME_TYPE(typename S::size_type, decltype(std::erase_if(s, p)));
   assert(expected_erased_count == std::erase_if(s, p));
   assert(s == expected);
 }
 
 template <typename S>
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   auto is1   = [](auto v) { return v == 1; };
   auto is2   = [](auto v) { return v == 2; };
   auto is3   = [](auto v) { return v == 3; };

>From dbe235f80d8d00bf17e01955df3dd42ee9b83566 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Thu, 2 Jul 2026 08:35:35 +0100
Subject: [PATCH 21/21] trigger build



From libcxx-commits at lists.llvm.org  Thu Jul  2 00:36:21 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 00:36:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Revert "[libc++] Move threading and
 random device config into <__configuration/platform.h>" (PR #207134)
Message-ID: <llvm.org/llvm/llvm-project/pull/207134@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207134

There were CI failures that I missed when merging.

Reverts llvm/llvm-project#206262


>From c05dc7a341c41be7821d5124e0b6f5c7dd73bc4f Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 2 Jul 2026 09:35:59 +0200
Subject: [PATCH] =?UTF-8?q?Revert=20"[libc++]=20Move=20threading=20and=20r?=
 =?UTF-8?q?andom=20device=20config=20into=20<=5F=5Fconfiguratio=E2=80=A6"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This reverts commit 32f14d7cd9991c0431e0c2721313614a4b05fd2d.
---
 libcxx/include/__config                   | 145 ++++++++++++++++++++++
 libcxx/include/__configuration/platform.h | 144 ---------------------
 2 files changed, 145 insertions(+), 144 deletions(-)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index edafe59278821..3f271ff504767 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -62,6 +62,50 @@
 #    define _LIBCPP_SHORT_WCHAR 1
 #  endif
 
+// Libc++ supports various implementations of std::random_device.
+//
+// _LIBCPP_USING_DEV_RANDOM
+//      Read entropy from the given file, by default `/dev/urandom`.
+//      If a token is provided, it is assumed to be the path to a file
+//      to read entropy from. This is the default behavior if nothing
+//      else is specified. This implementation requires storing state
+//      inside `std::random_device`.
+//
+// _LIBCPP_USING_ARC4_RANDOM
+//      Use arc4random(). This allows obtaining random data even when
+//      using sandboxing mechanisms. On some platforms like Apple, this
+//      is the recommended source of entropy for user-space programs.
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+//
+// _LIBCPP_USING_GETENTROPY
+//      Use getentropy().
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+//
+// _LIBCPP_USING_FUCHSIA_CPRNG
+//      Use Fuchsia's zx_cprng_draw() system call, which is specified to
+//      deliver high-quality entropy and cannot fail.
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+//
+// _LIBCPP_USING_WIN32_RANDOM
+//      Use rand_s(), for use on Windows.
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+#  if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) ||                     \
+      defined(__DragonFly__)
+#    define _LIBCPP_USING_ARC4_RANDOM
+#  elif defined(__wasi__) || defined(__EMSCRIPTEN__)
+#    define _LIBCPP_USING_GETENTROPY
+#  elif defined(__Fuchsia__)
+#    define _LIBCPP_USING_FUCHSIA_CPRNG
+#  elif defined(_LIBCPP_WIN32API)
+#    define _LIBCPP_USING_WIN32_RANDOM
+#  else
+#    define _LIBCPP_USING_DEV_RANDOM
+#  endif
+
 #  ifndef _LIBCPP_CXX03_LANG
 
 #    define _LIBCPP_ALIGNOF(...) alignof(__VA_ARGS__)
@@ -157,6 +201,107 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_DIAGNOSE_DEPRECATED_HEADERS 0
 #  endif
 
+// Thread API
+// clang-format off
+#  if _LIBCPP_HAS_THREADS &&                                                                                           \
+      !_LIBCPP_HAS_THREAD_API_PTHREAD &&                                                                               \
+      !_LIBCPP_HAS_THREAD_API_WIN32 &&                                                                                 \
+      !_LIBCPP_HAS_THREAD_API_EXTERNAL &&                                                                              \
+      !_LIBCPP_HAS_THREAD_API_C11
+
+#    if defined(__FreeBSD__) ||                                                                                        \
+        defined(__wasi__) ||                                                                                           \
+        defined(__NetBSD__) ||                                                                                         \
+        defined(__OpenBSD__) ||                                                                                        \
+        defined(__NuttX__) ||                                                                                          \
+        defined(__linux__) ||                                                                                          \
+        defined(__GNU__) ||                                                                                            \
+        defined(__APPLE__) ||                                                                                          \
+        defined(__MVS__) ||                                                                                            \
+        defined(_AIX) ||                                                                                               \
+        defined(__EMSCRIPTEN__)
+// clang-format on
+#      undef _LIBCPP_HAS_THREAD_API_PTHREAD
+#      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
+#    elif defined(__Fuchsia__)
+// TODO(44575): Switch to C11 thread API when possible.
+#      undef _LIBCPP_HAS_THREAD_API_PTHREAD
+#      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
+#    elif defined(_LIBCPP_WIN32API)
+#      undef _LIBCPP_HAS_THREAD_API_WIN32
+#      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    else
+#      error "No thread API"
+#    endif // _LIBCPP_HAS_THREAD_API
+#  endif   // _LIBCPP_HAS_THREADS
+
+#  if !_LIBCPP_HAS_THREAD_API_PTHREAD
+#    define _LIBCPP_HAS_COND_CLOCKWAIT 0
+#  elif (defined(__ANDROID__) && __ANDROID_API__ >= 30) || _LIBCPP_GLIBC_PREREQ(2, 30)
+#    define _LIBCPP_HAS_COND_CLOCKWAIT 1
+#  else
+#    define _LIBCPP_HAS_COND_CLOCKWAIT 0
+#  endif
+
+#  if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_PTHREAD
+#    error _LIBCPP_HAS_THREAD_API_PTHREAD may only be true when _LIBCPP_HAS_THREADS is true.
+#  endif
+
+#  if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_EXTERNAL
+#    error _LIBCPP_HAS_THREAD_API_EXTERNAL may only be true when _LIBCPP_HAS_THREADS is true.
+#  endif
+
+#  if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_C11
+#    error _LIBCPP_HAS_THREAD_API_C11 may only be true when _LIBCPP_HAS_THREADS is true.
+#  endif
+
+#  if !_LIBCPP_HAS_MONOTONIC_CLOCK && _LIBCPP_HAS_THREADS
+#    error _LIBCPP_HAS_MONOTONIC_CLOCK may only be false when _LIBCPP_HAS_THREADS is false.
+#  endif
+
+#  if _LIBCPP_HAS_THREADS && !defined(__STDCPP_THREADS__)
+#    define __STDCPP_THREADS__ 1
+#  endif
+
+// The glibc and Bionic implementation of pthreads implements
+// pthread_mutex_destroy as nop for regular mutexes. Additionally, Win32
+// mutexes have no destroy mechanism.
+//
+// This optimization can't be performed on Apple platforms, where
+// pthread_mutex_destroy can allow the kernel to release resources.
+// See https://llvm.org/D64298 for details.
+//
+// TODO(EricWF): Enable this optimization on Bionic after speaking to their
+//               respective stakeholders.
+// clang-format off
+#  if (_LIBCPP_HAS_THREAD_API_PTHREAD && defined(__GLIBC__)) ||                                                        \
+      (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) ||                                                          \
+       _LIBCPP_HAS_THREAD_API_WIN32
+// clang-format on
+#    define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 1
+#  else
+#    define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 0
+#  endif
+
+// Destroying a condvar is a nop on Windows.
+//
+// This optimization can't be performed on Apple platforms, where
+// pthread_cond_destroy can allow the kernel to release resources.
+// See https://llvm.org/D64298 for details.
+//
+// TODO(EricWF): This is potentially true for some pthread implementations
+// as well.
+#  if (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) || _LIBCPP_HAS_THREAD_API_WIN32
+#    define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 1
+#  else
+#    define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 0
+#  endif
+
+#  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+#    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
+#  endif
+
 // We often repeat things just for handling wide characters in the library.
 // When wide characters are disabled, it can be useful to have a quick way of
 // disabling it without having to resort to #if-#endif, which has a larger
diff --git a/libcxx/include/__configuration/platform.h b/libcxx/include/__configuration/platform.h
index 5b3bcfa4a8989..ebd638e663215 100644
--- a/libcxx/include/__configuration/platform.h
+++ b/libcxx/include/__configuration/platform.h
@@ -57,148 +57,4 @@
 #  define _LIBCPP_BIG_ENDIAN
 #endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
 
-// Libc++ supports various implementations of std::random_device.
-//
-// _LIBCPP_USING_DEV_RANDOM
-//      Read entropy from the given file, by default `/dev/urandom`.
-//      If a token is provided, it is assumed to be the path to a file
-//      to read entropy from. This is the default behavior if nothing
-//      else is specified. This implementation requires storing state
-//      inside `std::random_device`.
-//
-// _LIBCPP_USING_ARC4_RANDOM
-//      Use arc4random(). This allows obtaining random data even when
-//      using sandboxing mechanisms. On some platforms like Apple, this
-//      is the recommended source of entropy for user-space programs.
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-//
-// _LIBCPP_USING_GETENTROPY
-//      Use getentropy().
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-//
-// _LIBCPP_USING_FUCHSIA_CPRNG
-//      Use Fuchsia's zx_cprng_draw() system call, which is specified to
-//      deliver high-quality entropy and cannot fail.
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-//
-// _LIBCPP_USING_WIN32_RANDOM
-//      Use rand_s(), for use on Windows.
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
-#  define _LIBCPP_USING_ARC4_RANDOM
-#elif defined(__wasi__) || defined(__EMSCRIPTEN__)
-#  define _LIBCPP_USING_GETENTROPY
-#elif defined(__Fuchsia__)
-#  define _LIBCPP_USING_FUCHSIA_CPRNG
-#elif defined(_LIBCPP_WIN32API)
-#  define _LIBCPP_USING_WIN32_RANDOM
-#else
-#  define _LIBCPP_USING_DEV_RANDOM
-#endif
-
-// Thread API
-// clang-format off
-#if _LIBCPP_HAS_THREADS &&                                                                                             \
-    !_LIBCPP_HAS_THREAD_API_PTHREAD &&                                                                                 \
-    !_LIBCPP_HAS_THREAD_API_WIN32 &&                                                                                   \
-    !_LIBCPP_HAS_THREAD_API_EXTERNAL &&                                                                                \
-    !_LIBCPP_HAS_THREAD_API_C11
-
-#  if defined(__FreeBSD__) ||                                                                                          \
-      defined(__wasi__) ||                                                                                             \
-      defined(__NetBSD__) ||                                                                                           \
-      defined(__OpenBSD__) ||                                                                                          \
-      defined(__NuttX__) ||                                                                                            \
-      defined(__linux__) ||                                                                                            \
-      defined(__GNU__) ||                                                                                              \
-      defined(__APPLE__) ||                                                                                            \
-      defined(__MVS__) ||                                                                                              \
-      defined(_AIX) ||                                                                                                 \
-      defined(__EMSCRIPTEN__)
-// clang-format on
-#    undef _LIBCPP_HAS_THREAD_API_PTHREAD
-#    define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#  elif defined(__Fuchsia__)
-// TODO(44575): Switch to C11 thread API when possible.
-#    undef _LIBCPP_HAS_THREAD_API_PTHREAD
-#    define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#  elif defined(_LIBCPP_WIN32API)
-#    undef _LIBCPP_HAS_THREAD_API_WIN32
-#    define _LIBCPP_HAS_THREAD_API_WIN32 1
-#  else
-#    error "No thread API"
-#  endif // _LIBCPP_HAS_THREAD_API
-#endif   // _LIBCPP_HAS_THREADS
-
-#if !_LIBCPP_HAS_THREAD_API_PTHREAD
-#  define _LIBCPP_HAS_COND_CLOCKWAIT 0
-#elif (defined(__ANDROID__) && __ANDROID_API__ >= 30) || _LIBCPP_GLIBC_PREREQ(2, 30)
-#  define _LIBCPP_HAS_COND_CLOCKWAIT 1
-#else
-#  define _LIBCPP_HAS_COND_CLOCKWAIT 0
-#endif
-
-#if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_PTHREAD
-#  error _LIBCPP_HAS_THREAD_API_PTHREAD may only be true when _LIBCPP_HAS_THREADS is true.
-#endif
-
-#if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_EXTERNAL
-#  error _LIBCPP_HAS_THREAD_API_EXTERNAL may only be true when _LIBCPP_HAS_THREADS is true.
-#endif
-
-#if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_C11
-#  error _LIBCPP_HAS_THREAD_API_C11 may only be true when _LIBCPP_HAS_THREADS is true.
-#endif
-
-#if !_LIBCPP_HAS_MONOTONIC_CLOCK && _LIBCPP_HAS_THREADS
-#  error _LIBCPP_HAS_MONOTONIC_CLOCK may only be false when _LIBCPP_HAS_THREADS is false.
-#endif
-
-#if _LIBCPP_HAS_THREADS && !defined(__STDCPP_THREADS__)
-#  define __STDCPP_THREADS__ 1
-#endif
-
-// The glibc and Bionic implementation of pthreads implements
-// pthread_mutex_destroy as nop for regular mutexes. Additionally, Win32
-// mutexes have no destroy mechanism.
-//
-// This optimization can't be performed on Apple platforms, where
-// pthread_mutex_destroy can allow the kernel to release resources.
-// See https://llvm.org/D64298 for details.
-//
-// TODO(EricWF): Enable this optimization on Bionic after speaking to their
-//               respective stakeholders.
-// clang-format off
-#  if (_LIBCPP_HAS_THREAD_API_PTHREAD && defined(__GLIBC__)) ||                                                        \
-      (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) ||                                                          \
-       _LIBCPP_HAS_THREAD_API_WIN32
-// clang-format on
-#  define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 1
-#else
-#  define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 0
-#endif
-
-// Destroying a condvar is a nop on Windows.
-//
-// This optimization can't be performed on Apple platforms, where
-// pthread_cond_destroy can allow the kernel to release resources.
-// See https://llvm.org/D64298 for details.
-//
-// TODO(EricWF): This is potentially true for some pthread implementations
-// as well.
-#if (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) || _LIBCPP_HAS_THREAD_API_WIN32
-#  define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 1
-#else
-#  define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 0
-#endif
-
-#if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) || _LIBCPP_HAS_MUSL_LIBC || \
-    defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
-#  define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
-#endif
-
 #endif // _LIBCPP___CONFIGURATION_PLATFORM_H


From libcxx-commits at lists.llvm.org  Thu Jul  2 00:37:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 00:37:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Revert "[libc++] Move threading and
 random device config into <__configuration/platform.h>" (PR #207134)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207134@github.com>
Message-ID: <6a46151d.576b3af0.2cb9b7.2676@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

There were CI failures that I missed when merging.

Reverts llvm/llvm-project#<!-- -->206262


---
Full diff: https://github.com/llvm/llvm-project/pull/207134.diff


2 Files Affected:

- (modified) libcxx/include/__config (+145) 
- (modified) libcxx/include/__configuration/platform.h (-144) 


``````````diff
diff --git a/libcxx/include/__config b/libcxx/include/__config
index edafe59278821..3f271ff504767 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -62,6 +62,50 @@
 #    define _LIBCPP_SHORT_WCHAR 1
 #  endif
 
+// Libc++ supports various implementations of std::random_device.
+//
+// _LIBCPP_USING_DEV_RANDOM
+//      Read entropy from the given file, by default `/dev/urandom`.
+//      If a token is provided, it is assumed to be the path to a file
+//      to read entropy from. This is the default behavior if nothing
+//      else is specified. This implementation requires storing state
+//      inside `std::random_device`.
+//
+// _LIBCPP_USING_ARC4_RANDOM
+//      Use arc4random(). This allows obtaining random data even when
+//      using sandboxing mechanisms. On some platforms like Apple, this
+//      is the recommended source of entropy for user-space programs.
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+//
+// _LIBCPP_USING_GETENTROPY
+//      Use getentropy().
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+//
+// _LIBCPP_USING_FUCHSIA_CPRNG
+//      Use Fuchsia's zx_cprng_draw() system call, which is specified to
+//      deliver high-quality entropy and cannot fail.
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+//
+// _LIBCPP_USING_WIN32_RANDOM
+//      Use rand_s(), for use on Windows.
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+#  if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) ||                     \
+      defined(__DragonFly__)
+#    define _LIBCPP_USING_ARC4_RANDOM
+#  elif defined(__wasi__) || defined(__EMSCRIPTEN__)
+#    define _LIBCPP_USING_GETENTROPY
+#  elif defined(__Fuchsia__)
+#    define _LIBCPP_USING_FUCHSIA_CPRNG
+#  elif defined(_LIBCPP_WIN32API)
+#    define _LIBCPP_USING_WIN32_RANDOM
+#  else
+#    define _LIBCPP_USING_DEV_RANDOM
+#  endif
+
 #  ifndef _LIBCPP_CXX03_LANG
 
 #    define _LIBCPP_ALIGNOF(...) alignof(__VA_ARGS__)
@@ -157,6 +201,107 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_DIAGNOSE_DEPRECATED_HEADERS 0
 #  endif
 
+// Thread API
+// clang-format off
+#  if _LIBCPP_HAS_THREADS &&                                                                                           \
+      !_LIBCPP_HAS_THREAD_API_PTHREAD &&                                                                               \
+      !_LIBCPP_HAS_THREAD_API_WIN32 &&                                                                                 \
+      !_LIBCPP_HAS_THREAD_API_EXTERNAL &&                                                                              \
+      !_LIBCPP_HAS_THREAD_API_C11
+
+#    if defined(__FreeBSD__) ||                                                                                        \
+        defined(__wasi__) ||                                                                                           \
+        defined(__NetBSD__) ||                                                                                         \
+        defined(__OpenBSD__) ||                                                                                        \
+        defined(__NuttX__) ||                                                                                          \
+        defined(__linux__) ||                                                                                          \
+        defined(__GNU__) ||                                                                                            \
+        defined(__APPLE__) ||                                                                                          \
+        defined(__MVS__) ||                                                                                            \
+        defined(_AIX) ||                                                                                               \
+        defined(__EMSCRIPTEN__)
+// clang-format on
+#      undef _LIBCPP_HAS_THREAD_API_PTHREAD
+#      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
+#    elif defined(__Fuchsia__)
+// TODO(44575): Switch to C11 thread API when possible.
+#      undef _LIBCPP_HAS_THREAD_API_PTHREAD
+#      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
+#    elif defined(_LIBCPP_WIN32API)
+#      undef _LIBCPP_HAS_THREAD_API_WIN32
+#      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    else
+#      error "No thread API"
+#    endif // _LIBCPP_HAS_THREAD_API
+#  endif   // _LIBCPP_HAS_THREADS
+
+#  if !_LIBCPP_HAS_THREAD_API_PTHREAD
+#    define _LIBCPP_HAS_COND_CLOCKWAIT 0
+#  elif (defined(__ANDROID__) && __ANDROID_API__ >= 30) || _LIBCPP_GLIBC_PREREQ(2, 30)
+#    define _LIBCPP_HAS_COND_CLOCKWAIT 1
+#  else
+#    define _LIBCPP_HAS_COND_CLOCKWAIT 0
+#  endif
+
+#  if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_PTHREAD
+#    error _LIBCPP_HAS_THREAD_API_PTHREAD may only be true when _LIBCPP_HAS_THREADS is true.
+#  endif
+
+#  if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_EXTERNAL
+#    error _LIBCPP_HAS_THREAD_API_EXTERNAL may only be true when _LIBCPP_HAS_THREADS is true.
+#  endif
+
+#  if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_C11
+#    error _LIBCPP_HAS_THREAD_API_C11 may only be true when _LIBCPP_HAS_THREADS is true.
+#  endif
+
+#  if !_LIBCPP_HAS_MONOTONIC_CLOCK && _LIBCPP_HAS_THREADS
+#    error _LIBCPP_HAS_MONOTONIC_CLOCK may only be false when _LIBCPP_HAS_THREADS is false.
+#  endif
+
+#  if _LIBCPP_HAS_THREADS && !defined(__STDCPP_THREADS__)
+#    define __STDCPP_THREADS__ 1
+#  endif
+
+// The glibc and Bionic implementation of pthreads implements
+// pthread_mutex_destroy as nop for regular mutexes. Additionally, Win32
+// mutexes have no destroy mechanism.
+//
+// This optimization can't be performed on Apple platforms, where
+// pthread_mutex_destroy can allow the kernel to release resources.
+// See https://llvm.org/D64298 for details.
+//
+// TODO(EricWF): Enable this optimization on Bionic after speaking to their
+//               respective stakeholders.
+// clang-format off
+#  if (_LIBCPP_HAS_THREAD_API_PTHREAD && defined(__GLIBC__)) ||                                                        \
+      (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) ||                                                          \
+       _LIBCPP_HAS_THREAD_API_WIN32
+// clang-format on
+#    define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 1
+#  else
+#    define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 0
+#  endif
+
+// Destroying a condvar is a nop on Windows.
+//
+// This optimization can't be performed on Apple platforms, where
+// pthread_cond_destroy can allow the kernel to release resources.
+// See https://llvm.org/D64298 for details.
+//
+// TODO(EricWF): This is potentially true for some pthread implementations
+// as well.
+#  if (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) || _LIBCPP_HAS_THREAD_API_WIN32
+#    define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 1
+#  else
+#    define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 0
+#  endif
+
+#  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+#    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
+#  endif
+
 // We often repeat things just for handling wide characters in the library.
 // When wide characters are disabled, it can be useful to have a quick way of
 // disabling it without having to resort to #if-#endif, which has a larger
diff --git a/libcxx/include/__configuration/platform.h b/libcxx/include/__configuration/platform.h
index 5b3bcfa4a8989..ebd638e663215 100644
--- a/libcxx/include/__configuration/platform.h
+++ b/libcxx/include/__configuration/platform.h
@@ -57,148 +57,4 @@
 #  define _LIBCPP_BIG_ENDIAN
 #endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
 
-// Libc++ supports various implementations of std::random_device.
-//
-// _LIBCPP_USING_DEV_RANDOM
-//      Read entropy from the given file, by default `/dev/urandom`.
-//      If a token is provided, it is assumed to be the path to a file
-//      to read entropy from. This is the default behavior if nothing
-//      else is specified. This implementation requires storing state
-//      inside `std::random_device`.
-//
-// _LIBCPP_USING_ARC4_RANDOM
-//      Use arc4random(). This allows obtaining random data even when
-//      using sandboxing mechanisms. On some platforms like Apple, this
-//      is the recommended source of entropy for user-space programs.
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-//
-// _LIBCPP_USING_GETENTROPY
-//      Use getentropy().
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-//
-// _LIBCPP_USING_FUCHSIA_CPRNG
-//      Use Fuchsia's zx_cprng_draw() system call, which is specified to
-//      deliver high-quality entropy and cannot fail.
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-//
-// _LIBCPP_USING_WIN32_RANDOM
-//      Use rand_s(), for use on Windows.
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
-#  define _LIBCPP_USING_ARC4_RANDOM
-#elif defined(__wasi__) || defined(__EMSCRIPTEN__)
-#  define _LIBCPP_USING_GETENTROPY
-#elif defined(__Fuchsia__)
-#  define _LIBCPP_USING_FUCHSIA_CPRNG
-#elif defined(_LIBCPP_WIN32API)
-#  define _LIBCPP_USING_WIN32_RANDOM
-#else
-#  define _LIBCPP_USING_DEV_RANDOM
-#endif
-
-// Thread API
-// clang-format off
-#if _LIBCPP_HAS_THREADS &&                                                                                             \
-    !_LIBCPP_HAS_THREAD_API_PTHREAD &&                                                                                 \
-    !_LIBCPP_HAS_THREAD_API_WIN32 &&                                                                                   \
-    !_LIBCPP_HAS_THREAD_API_EXTERNAL &&                                                                                \
-    !_LIBCPP_HAS_THREAD_API_C11
-
-#  if defined(__FreeBSD__) ||                                                                                          \
-      defined(__wasi__) ||                                                                                             \
-      defined(__NetBSD__) ||                                                                                           \
-      defined(__OpenBSD__) ||                                                                                          \
-      defined(__NuttX__) ||                                                                                            \
-      defined(__linux__) ||                                                                                            \
-      defined(__GNU__) ||                                                                                              \
-      defined(__APPLE__) ||                                                                                            \
-      defined(__MVS__) ||                                                                                              \
-      defined(_AIX) ||                                                                                                 \
-      defined(__EMSCRIPTEN__)
-// clang-format on
-#    undef _LIBCPP_HAS_THREAD_API_PTHREAD
-#    define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#  elif defined(__Fuchsia__)
-// TODO(44575): Switch to C11 thread API when possible.
-#    undef _LIBCPP_HAS_THREAD_API_PTHREAD
-#    define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#  elif defined(_LIBCPP_WIN32API)
-#    undef _LIBCPP_HAS_THREAD_API_WIN32
-#    define _LIBCPP_HAS_THREAD_API_WIN32 1
-#  else
-#    error "No thread API"
-#  endif // _LIBCPP_HAS_THREAD_API
-#endif   // _LIBCPP_HAS_THREADS
-
-#if !_LIBCPP_HAS_THREAD_API_PTHREAD
-#  define _LIBCPP_HAS_COND_CLOCKWAIT 0
-#elif (defined(__ANDROID__) && __ANDROID_API__ >= 30) || _LIBCPP_GLIBC_PREREQ(2, 30)
-#  define _LIBCPP_HAS_COND_CLOCKWAIT 1
-#else
-#  define _LIBCPP_HAS_COND_CLOCKWAIT 0
-#endif
-
-#if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_PTHREAD
-#  error _LIBCPP_HAS_THREAD_API_PTHREAD may only be true when _LIBCPP_HAS_THREADS is true.
-#endif
-
-#if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_EXTERNAL
-#  error _LIBCPP_HAS_THREAD_API_EXTERNAL may only be true when _LIBCPP_HAS_THREADS is true.
-#endif
-
-#if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_C11
-#  error _LIBCPP_HAS_THREAD_API_C11 may only be true when _LIBCPP_HAS_THREADS is true.
-#endif
-
-#if !_LIBCPP_HAS_MONOTONIC_CLOCK && _LIBCPP_HAS_THREADS
-#  error _LIBCPP_HAS_MONOTONIC_CLOCK may only be false when _LIBCPP_HAS_THREADS is false.
-#endif
-
-#if _LIBCPP_HAS_THREADS && !defined(__STDCPP_THREADS__)
-#  define __STDCPP_THREADS__ 1
-#endif
-
-// The glibc and Bionic implementation of pthreads implements
-// pthread_mutex_destroy as nop for regular mutexes. Additionally, Win32
-// mutexes have no destroy mechanism.
-//
-// This optimization can't be performed on Apple platforms, where
-// pthread_mutex_destroy can allow the kernel to release resources.
-// See https://llvm.org/D64298 for details.
-//
-// TODO(EricWF): Enable this optimization on Bionic after speaking to their
-//               respective stakeholders.
-// clang-format off
-#  if (_LIBCPP_HAS_THREAD_API_PTHREAD && defined(__GLIBC__)) ||                                                        \
-      (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) ||                                                          \
-       _LIBCPP_HAS_THREAD_API_WIN32
-// clang-format on
-#  define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 1
-#else
-#  define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 0
-#endif
-
-// Destroying a condvar is a nop on Windows.
-//
-// This optimization can't be performed on Apple platforms, where
-// pthread_cond_destroy can allow the kernel to release resources.
-// See https://llvm.org/D64298 for details.
-//
-// TODO(EricWF): This is potentially true for some pthread implementations
-// as well.
-#if (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) || _LIBCPP_HAS_THREAD_API_WIN32
-#  define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 1
-#else
-#  define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 0
-#endif
-
-#if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) || _LIBCPP_HAS_MUSL_LIBC || \
-    defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
-#  define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
-#endif
-
 #endif // _LIBCPP___CONFIGURATION_PLATFORM_H

``````````

</details>


https://github.com/llvm/llvm-project/pull/207134

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:37:13 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 00:37:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] a5114ab - Revert "[libc++] Move threading
 and random device config into <__configuration/platform.h>" (#207134)
Message-ID: <6a461529.cbc3f03a.3be201.15f1@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-02T09:37:09+02:00
New Revision: a5114abd0c022729fdc659dd0dfe14542b25aa85

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

LOG: Revert "[libc++] Move threading and random device config into <__configuration/platform.h>" (#207134)

There were CI failures that I missed when merging.

Reverts llvm/llvm-project#206262

Added: 
    

Modified: 
    libcxx/include/__config
    libcxx/include/__configuration/platform.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__config b/libcxx/include/__config
index edafe59278821..3f271ff504767 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -62,6 +62,50 @@
 #    define _LIBCPP_SHORT_WCHAR 1
 #  endif
 
+// Libc++ supports various implementations of std::random_device.
+//
+// _LIBCPP_USING_DEV_RANDOM
+//      Read entropy from the given file, by default `/dev/urandom`.
+//      If a token is provided, it is assumed to be the path to a file
+//      to read entropy from. This is the default behavior if nothing
+//      else is specified. This implementation requires storing state
+//      inside `std::random_device`.
+//
+// _LIBCPP_USING_ARC4_RANDOM
+//      Use arc4random(). This allows obtaining random data even when
+//      using sandboxing mechanisms. On some platforms like Apple, this
+//      is the recommended source of entropy for user-space programs.
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+//
+// _LIBCPP_USING_GETENTROPY
+//      Use getentropy().
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+//
+// _LIBCPP_USING_FUCHSIA_CPRNG
+//      Use Fuchsia's zx_cprng_draw() system call, which is specified to
+//      deliver high-quality entropy and cannot fail.
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+//
+// _LIBCPP_USING_WIN32_RANDOM
+//      Use rand_s(), for use on Windows.
+//      When this option is used, the token passed to `std::random_device`'s
+//      constructor *must* be "/dev/urandom" -- anything else is an error.
+#  if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) ||                     \
+      defined(__DragonFly__)
+#    define _LIBCPP_USING_ARC4_RANDOM
+#  elif defined(__wasi__) || defined(__EMSCRIPTEN__)
+#    define _LIBCPP_USING_GETENTROPY
+#  elif defined(__Fuchsia__)
+#    define _LIBCPP_USING_FUCHSIA_CPRNG
+#  elif defined(_LIBCPP_WIN32API)
+#    define _LIBCPP_USING_WIN32_RANDOM
+#  else
+#    define _LIBCPP_USING_DEV_RANDOM
+#  endif
+
 #  ifndef _LIBCPP_CXX03_LANG
 
 #    define _LIBCPP_ALIGNOF(...) alignof(__VA_ARGS__)
@@ -157,6 +201,107 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_DIAGNOSE_DEPRECATED_HEADERS 0
 #  endif
 
+// Thread API
+// clang-format off
+#  if _LIBCPP_HAS_THREADS &&                                                                                           \
+      !_LIBCPP_HAS_THREAD_API_PTHREAD &&                                                                               \
+      !_LIBCPP_HAS_THREAD_API_WIN32 &&                                                                                 \
+      !_LIBCPP_HAS_THREAD_API_EXTERNAL &&                                                                              \
+      !_LIBCPP_HAS_THREAD_API_C11
+
+#    if defined(__FreeBSD__) ||                                                                                        \
+        defined(__wasi__) ||                                                                                           \
+        defined(__NetBSD__) ||                                                                                         \
+        defined(__OpenBSD__) ||                                                                                        \
+        defined(__NuttX__) ||                                                                                          \
+        defined(__linux__) ||                                                                                          \
+        defined(__GNU__) ||                                                                                            \
+        defined(__APPLE__) ||                                                                                          \
+        defined(__MVS__) ||                                                                                            \
+        defined(_AIX) ||                                                                                               \
+        defined(__EMSCRIPTEN__)
+// clang-format on
+#      undef _LIBCPP_HAS_THREAD_API_PTHREAD
+#      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
+#    elif defined(__Fuchsia__)
+// TODO(44575): Switch to C11 thread API when possible.
+#      undef _LIBCPP_HAS_THREAD_API_PTHREAD
+#      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
+#    elif defined(_LIBCPP_WIN32API)
+#      undef _LIBCPP_HAS_THREAD_API_WIN32
+#      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    else
+#      error "No thread API"
+#    endif // _LIBCPP_HAS_THREAD_API
+#  endif   // _LIBCPP_HAS_THREADS
+
+#  if !_LIBCPP_HAS_THREAD_API_PTHREAD
+#    define _LIBCPP_HAS_COND_CLOCKWAIT 0
+#  elif (defined(__ANDROID__) && __ANDROID_API__ >= 30) || _LIBCPP_GLIBC_PREREQ(2, 30)
+#    define _LIBCPP_HAS_COND_CLOCKWAIT 1
+#  else
+#    define _LIBCPP_HAS_COND_CLOCKWAIT 0
+#  endif
+
+#  if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_PTHREAD
+#    error _LIBCPP_HAS_THREAD_API_PTHREAD may only be true when _LIBCPP_HAS_THREADS is true.
+#  endif
+
+#  if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_EXTERNAL
+#    error _LIBCPP_HAS_THREAD_API_EXTERNAL may only be true when _LIBCPP_HAS_THREADS is true.
+#  endif
+
+#  if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_C11
+#    error _LIBCPP_HAS_THREAD_API_C11 may only be true when _LIBCPP_HAS_THREADS is true.
+#  endif
+
+#  if !_LIBCPP_HAS_MONOTONIC_CLOCK && _LIBCPP_HAS_THREADS
+#    error _LIBCPP_HAS_MONOTONIC_CLOCK may only be false when _LIBCPP_HAS_THREADS is false.
+#  endif
+
+#  if _LIBCPP_HAS_THREADS && !defined(__STDCPP_THREADS__)
+#    define __STDCPP_THREADS__ 1
+#  endif
+
+// The glibc and Bionic implementation of pthreads implements
+// pthread_mutex_destroy as nop for regular mutexes. Additionally, Win32
+// mutexes have no destroy mechanism.
+//
+// This optimization can't be performed on Apple platforms, where
+// pthread_mutex_destroy can allow the kernel to release resources.
+// See https://llvm.org/D64298 for details.
+//
+// TODO(EricWF): Enable this optimization on Bionic after speaking to their
+//               respective stakeholders.
+// clang-format off
+#  if (_LIBCPP_HAS_THREAD_API_PTHREAD && defined(__GLIBC__)) ||                                                        \
+      (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) ||                                                          \
+       _LIBCPP_HAS_THREAD_API_WIN32
+// clang-format on
+#    define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 1
+#  else
+#    define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 0
+#  endif
+
+// Destroying a condvar is a nop on Windows.
+//
+// This optimization can't be performed on Apple platforms, where
+// pthread_cond_destroy can allow the kernel to release resources.
+// See https://llvm.org/D64298 for details.
+//
+// TODO(EricWF): This is potentially true for some pthread implementations
+// as well.
+#  if (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) || _LIBCPP_HAS_THREAD_API_WIN32
+#    define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 1
+#  else
+#    define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 0
+#  endif
+
+#  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+#    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
+#  endif
+
 // We often repeat things just for handling wide characters in the library.
 // When wide characters are disabled, it can be useful to have a quick way of
 // disabling it without having to resort to #if-#endif, which has a larger

diff  --git a/libcxx/include/__configuration/platform.h b/libcxx/include/__configuration/platform.h
index 5b3bcfa4a8989..ebd638e663215 100644
--- a/libcxx/include/__configuration/platform.h
+++ b/libcxx/include/__configuration/platform.h
@@ -57,148 +57,4 @@
 #  define _LIBCPP_BIG_ENDIAN
 #endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
 
-// Libc++ supports various implementations of std::random_device.
-//
-// _LIBCPP_USING_DEV_RANDOM
-//      Read entropy from the given file, by default `/dev/urandom`.
-//      If a token is provided, it is assumed to be the path to a file
-//      to read entropy from. This is the default behavior if nothing
-//      else is specified. This implementation requires storing state
-//      inside `std::random_device`.
-//
-// _LIBCPP_USING_ARC4_RANDOM
-//      Use arc4random(). This allows obtaining random data even when
-//      using sandboxing mechanisms. On some platforms like Apple, this
-//      is the recommended source of entropy for user-space programs.
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-//
-// _LIBCPP_USING_GETENTROPY
-//      Use getentropy().
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-//
-// _LIBCPP_USING_FUCHSIA_CPRNG
-//      Use Fuchsia's zx_cprng_draw() system call, which is specified to
-//      deliver high-quality entropy and cannot fail.
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-//
-// _LIBCPP_USING_WIN32_RANDOM
-//      Use rand_s(), for use on Windows.
-//      When this option is used, the token passed to `std::random_device`'s
-//      constructor *must* be "/dev/urandom" -- anything else is an error.
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
-#  define _LIBCPP_USING_ARC4_RANDOM
-#elif defined(__wasi__) || defined(__EMSCRIPTEN__)
-#  define _LIBCPP_USING_GETENTROPY
-#elif defined(__Fuchsia__)
-#  define _LIBCPP_USING_FUCHSIA_CPRNG
-#elif defined(_LIBCPP_WIN32API)
-#  define _LIBCPP_USING_WIN32_RANDOM
-#else
-#  define _LIBCPP_USING_DEV_RANDOM
-#endif
-
-// Thread API
-// clang-format off
-#if _LIBCPP_HAS_THREADS &&                                                                                             \
-    !_LIBCPP_HAS_THREAD_API_PTHREAD &&                                                                                 \
-    !_LIBCPP_HAS_THREAD_API_WIN32 &&                                                                                   \
-    !_LIBCPP_HAS_THREAD_API_EXTERNAL &&                                                                                \
-    !_LIBCPP_HAS_THREAD_API_C11
-
-#  if defined(__FreeBSD__) ||                                                                                          \
-      defined(__wasi__) ||                                                                                             \
-      defined(__NetBSD__) ||                                                                                           \
-      defined(__OpenBSD__) ||                                                                                          \
-      defined(__NuttX__) ||                                                                                            \
-      defined(__linux__) ||                                                                                            \
-      defined(__GNU__) ||                                                                                              \
-      defined(__APPLE__) ||                                                                                            \
-      defined(__MVS__) ||                                                                                              \
-      defined(_AIX) ||                                                                                                 \
-      defined(__EMSCRIPTEN__)
-// clang-format on
-#    undef _LIBCPP_HAS_THREAD_API_PTHREAD
-#    define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#  elif defined(__Fuchsia__)
-// TODO(44575): Switch to C11 thread API when possible.
-#    undef _LIBCPP_HAS_THREAD_API_PTHREAD
-#    define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#  elif defined(_LIBCPP_WIN32API)
-#    undef _LIBCPP_HAS_THREAD_API_WIN32
-#    define _LIBCPP_HAS_THREAD_API_WIN32 1
-#  else
-#    error "No thread API"
-#  endif // _LIBCPP_HAS_THREAD_API
-#endif   // _LIBCPP_HAS_THREADS
-
-#if !_LIBCPP_HAS_THREAD_API_PTHREAD
-#  define _LIBCPP_HAS_COND_CLOCKWAIT 0
-#elif (defined(__ANDROID__) && __ANDROID_API__ >= 30) || _LIBCPP_GLIBC_PREREQ(2, 30)
-#  define _LIBCPP_HAS_COND_CLOCKWAIT 1
-#else
-#  define _LIBCPP_HAS_COND_CLOCKWAIT 0
-#endif
-
-#if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_PTHREAD
-#  error _LIBCPP_HAS_THREAD_API_PTHREAD may only be true when _LIBCPP_HAS_THREADS is true.
-#endif
-
-#if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_EXTERNAL
-#  error _LIBCPP_HAS_THREAD_API_EXTERNAL may only be true when _LIBCPP_HAS_THREADS is true.
-#endif
-
-#if !_LIBCPP_HAS_THREADS && _LIBCPP_HAS_THREAD_API_C11
-#  error _LIBCPP_HAS_THREAD_API_C11 may only be true when _LIBCPP_HAS_THREADS is true.
-#endif
-
-#if !_LIBCPP_HAS_MONOTONIC_CLOCK && _LIBCPP_HAS_THREADS
-#  error _LIBCPP_HAS_MONOTONIC_CLOCK may only be false when _LIBCPP_HAS_THREADS is false.
-#endif
-
-#if _LIBCPP_HAS_THREADS && !defined(__STDCPP_THREADS__)
-#  define __STDCPP_THREADS__ 1
-#endif
-
-// The glibc and Bionic implementation of pthreads implements
-// pthread_mutex_destroy as nop for regular mutexes. Additionally, Win32
-// mutexes have no destroy mechanism.
-//
-// This optimization can't be performed on Apple platforms, where
-// pthread_mutex_destroy can allow the kernel to release resources.
-// See https://llvm.org/D64298 for details.
-//
-// TODO(EricWF): Enable this optimization on Bionic after speaking to their
-//               respective stakeholders.
-// clang-format off
-#  if (_LIBCPP_HAS_THREAD_API_PTHREAD && defined(__GLIBC__)) ||                                                        \
-      (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) ||                                                          \
-       _LIBCPP_HAS_THREAD_API_WIN32
-// clang-format on
-#  define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 1
-#else
-#  define _LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION 0
-#endif
-
-// Destroying a condvar is a nop on Windows.
-//
-// This optimization can't be performed on Apple platforms, where
-// pthread_cond_destroy can allow the kernel to release resources.
-// See https://llvm.org/D64298 for details.
-//
-// TODO(EricWF): This is potentially true for some pthread implementations
-// as well.
-#if (_LIBCPP_HAS_THREAD_API_C11 && defined(__Fuchsia__)) || _LIBCPP_HAS_THREAD_API_WIN32
-#  define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 1
-#else
-#  define _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION 0
-#endif
-
-#if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) || _LIBCPP_HAS_MUSL_LIBC || \
-    defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
-#  define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
-#endif
-
 #endif // _LIBCPP___CONFIGURATION_PLATFORM_H


        

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:37:16 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 00:37:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Revert "[libc++] Move threading and
 random device config into <__configuration/platform.h>" (PR #207134)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207134@github.com>
Message-ID: <6a46152c.ff037943.3bf0e2.f878@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/207134

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:42:50 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 00:42:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a46167a.481afadf.3c64c3.1a55@mx.google.com>


================
@@ -63,6 +64,7 @@ __for_each_n(_InputIterator __first, _Size __orig_n, _Func&& __f, _Proj& __proj)
 template <class _InputIterator, class _Size, class _Func>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
 for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__orig_n >= 0, "for_each_n requires a non-negative count");
----------------
philnik777 wrote:

Should there be an LWG issue about this inconsistency?

https://github.com/llvm/llvm-project/pull/207086

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:42:50 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 00:42:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a46167a.510f576d.185953.c539@mx.google.com>


================
@@ -63,14 +63,15 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted > 0) {
+  if (__converted < 0) [[__unlikely__]]
----------------
philnik777 wrote:

```suggestion
  if (__converted < 0)
```
Or does this actually make a difference somewhere? Same throughout.

https://github.com/llvm/llvm-project/pull/207086

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:46:43 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 00:46:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Enable CPO compile tests
	(PR #207123)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207123@github.com>
Message-ID: <6a461763.4a342cbc.134cb.87e4@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/207123

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:49:56 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 00:49:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a461824.c56fb102.11841f.003f@mx.google.com>


https://github.com/philnik777 requested changes to this pull request.

I'm not convinced this is worth the complexity. This adds a workaround for older compilers, so this will be obsolete in a year anyways, and for that it's way too complicated of a fix IMO.

https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:52:30 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 00:52:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a4618be.8e75e0cb.2e0812.8ce2@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Thu Jul  2 00:53:29 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 00:53:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a4618f9.492e23e4.e71ca.40d0@mx.google.com>


================
@@ -34,4 +34,15 @@ static void BM_SeedSeq_Generate(benchmark::State& state) {
 }
 BENCHMARK(BM_SeedSeq_Generate)->Ranges({{1, MAX_SEED_LEN}, {1, MAX_BUFFER_LEN}});
 
+template <class Engine>
+static void BM_engine(benchmark::State& state) {
----------------
philnik777 wrote:

Looks like it doesn't actually manage to do more vectorization for some reason. I'll have to look into that a bit more.

https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Thu Jul  2 01:05:42 2026
From: libcxx-commits at lists.llvm.org (Vinay Deshmukh via libcxx-commits)
Date: Thu, 02 Jul 2026 01:05:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make `<set>` `std::multiset`
 constexpr as part of P3372R3 (PR #206257)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206257@github.com>
Message-ID: <6a461bd6.8e75e0cb.2e0812.a821@mx.google.com>


https://github.com/vinay-deshmukh ready_for_review https://github.com/llvm/llvm-project/pull/206257

From libcxx-commits at lists.llvm.org  Thu Jul  2 01:05:55 2026
From: libcxx-commits at lists.llvm.org (Vinay Deshmukh via libcxx-commits)
Date: Thu, 02 Jul 2026 01:05:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make `<set>` `std::multiset`
 constexpr as part of P3372R3 (PR #206257)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206257@github.com>
Message-ID: <6a461be3.f14cf1a5.2c3aaa.13bb@mx.google.com>


https://github.com/vinay-deshmukh edited https://github.com/llvm/llvm-project/pull/206257

From libcxx-commits at lists.llvm.org  Thu Jul  2 01:12:12 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Thu, 02 Jul 2026 01:12:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a461d5c.7d41f3a1.d1cf7.edef@mx.google.com>


yronglin wrote:

> I'm not convinced this is worth the complexity. This adds a workaround for older compilers, so this will be obsolete in a year anyways, and for that it's way too complicated of a fix IMO.

I found this issue when I working on https://github.com/llvm/llvm-project/pull/205379, If we don't want this workaround, I'd like to disable the failed tests for old compilers in https://github.com/llvm/llvm-project/pull/205379. WDYT?

https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Thu Jul  2 01:32:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 01:32:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make `<set>` `std::multiset`
 constexpr as part of P3372R3 (PR #206257)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206257@github.com>
Message-ID: <6a462200.f14cf1a5.2c3aaa.8cb8@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Vinay Deshmukh (vinay-deshmukh)

<details>
<summary>Changes</summary>

Fixes #<!-- -->128664 

---

Patch is 106.62 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/206257.diff


63 Files Affected:

- (modified) libcxx/include/set (+159-90) 
- (modified) libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp (+2-6) 
- (modified) libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp (+2-9) 
- (modified) libcxx/test/std/containers/associative/multiset/clear.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/count.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp (+17-5) 
- (modified) libcxx/test/std/containers/associative/multiset/emplace.pass.cpp (+14-4) 
- (modified) libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp (+14-4) 
- (modified) libcxx/test/std/containers/associative/multiset/empty.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp (+11-3) 
- (modified) libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp (+18-6) 
- (modified) libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp (+12-4) 
- (modified) libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp (+12-4) 
- (modified) libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp (+12-4) 
- (modified) libcxx/test/std/containers/associative/multiset/find.pass.cpp (+11-3) 
- (modified) libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp (+11-3) 
- (modified) libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp (+11-3) 
- (modified) libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp (+6-2) 
- (modified) libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp (+15-7) 
- (modified) libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp (+15-6) 
- (modified) libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp (+11-2) 
- (modified) libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/iterator.pass.cpp (+21-13) 
- (modified) libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp (+11-3) 
- (modified) libcxx/test/std/containers/associative/multiset/max_size.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/merge.pass.cpp (+22-11) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp (+8-4) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp (+8-4) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp (+20-15) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp (+13-4) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp (+11-3) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp (+12-4) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp (+10-3) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp (+9-1) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp (+13-3) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp (+10-2) 
- (modified) libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp (+11-3) 
- (modified) libcxx/test/std/containers/associative/multiset/size.pass.cpp (+9-2) 
- (modified) libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp (+10-3) 


``````````diff
diff --git a/libcxx/include/set b/libcxx/include/set
index a8e245b22ae51..0409131ac6981 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -1176,31 +1176,34 @@ public:
   friend class multiset;
 
   // construct/copy/destroy:
-  _LIBCPP_HIDE_FROM_ABI multiset() _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset() _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_default_constructible<key_compare>::value&&
           is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(value_compare()) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const value_compare& __comp) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const value_compare& __comp) _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(__comp) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const value_compare& __comp, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {}
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__f, __l);
   }
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multiset(__f, __l, key_compare(), __a) {}
 #  endif
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__f, __l);
@@ -1208,7 +1211,7 @@ public:
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(from_range_t,
            _Range&& __range,
            const key_compare& __comp = key_compare(),
@@ -1218,213 +1221,272 @@ public:
   }
 
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI multiset(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(from_range_t, _Range&& __range, const allocator_type& __a)
       : multiset(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(const multiset& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(const multiset& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(const multiset& __s) = default;
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(multiset&& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s, const allocator_type& __a) : __tree_(std::move(__s.__tree_), __a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(multiset&& __s, const allocator_type& __a)
+      : __tree_(std::move(__s.__tree_), __a) {}
 #  endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const allocator_type& __a) : __tree_(__a) {}
-  _LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s, const allocator_type& __a) : __tree_(__s.__tree_, __a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const allocator_type& __a) : __tree_(__a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(const multiset& __s, const allocator_type& __a)
+      : __tree_(__s.__tree_, __a) {}
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__il.begin(), __il.end());
   }
 
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(initializer_list<value_type> __il, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__il.begin(), __il.end());
   }
 
 #    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(initializer_list<value_type> __il, const allocator_type& __a)
       : multiset(__il, key_compare(), __a) {}
 #    endif
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(initializer_list<value_type> __il) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(initializer_list<value_type> __il) {
     clear();
     insert(__il.begin(), __il.end());
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(multiset&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(multiset&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI ~multiset() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~multiset() {
     static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return __tree_.begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return __tree_.begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return __tree_.end(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return __tree_.end(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT {
+    return __tree_.begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator begin() const _NOEXCEPT {
+    return __tree_.begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator end() _NOEXCEPT {
+    return __tree_.end();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator end() const _NOEXCEPT {
+    return __tree_.end();
+  }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT { return reverse_iterator(end()); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rbegin() const _NOEXCEPT {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rbegin() _NOEXCEPT {
+    return reverse_iterator(end());
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator
+  rbegin() const _NOEXCEPT {
     return const_reverse_iterator(end());
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI reverse_iterator rend() _NOEXCEPT { return reverse_iterator(begin()); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const _NOEXCEPT {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rend() _NOEXCEPT {
+    return reverse_iterator(begin());
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator rend() const _NOEXCEPT {
     return const_reverse_iterator(begin());
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const _NOEXCEPT { return begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator cend() const _NOEXCEPT { return end(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crbegin() const _NOEXCEPT { return rbegin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const _NOEXCEPT { return rend(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cbegin() const _NOEXCEPT {
+    return begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cend() const _NOEXCEPT {
+    return end();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator
+  crbegin() const _NOEXCEPT {
+    return rbegin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator crend() const _NOEXCEPT {
+    return rend();
+  }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT { return __tree_.size() == 0; }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __tree_.size(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT { return __tree_.max_size(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool empty() const _NOEXCEPT {
+    return __tree_.size() == 0;
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type size() const _NOEXCEPT {
+    return __tree_.size();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type max_size() const _NOEXCEPT {
+    return __tree_.max_size();
+  }
 
   // modifiers:
 #  ifndef _LIBCPP_CXX03_LANG
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace(_Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace(_Args&&... __args) {
     return __tree_.__emplace_multi(std::forward<_Args>(__args)...);
   }
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace_hint(const_iterator __p, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace_hint(const_iterator __p, _Args&&... __args) {
     return __tree_.__emplace_hint_multi(__p, std::forward<_Args>(__args)...);
   }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const value_type& __v) { return __tree_.__emplace_multi(__v); }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, const value_type& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const value_type& __v) {
+    return __tree_.__emplace_multi(__v);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, const value_type& __v) {
     return __tree_.__emplace_hint_multi(__p, __v);
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __first, _InputIterator __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(_InputIterator __first, _InputIterator __last) {
     __tree_.__insert_range_multi(__first, __last);
   }
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert_range(_Range&& __range) {
     __tree_.__insert_range_multi(ranges::begin(__range), ranges::end(__range));
   }
 #  endif
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI iterator insert(value_type&& __v) { return __tree_.__emplace_multi(std::move(__v)); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(value_type&& __v) {
+    return __tree_.__emplace_multi(std::move(__v));
+  }
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, value_type&& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, value_type&& __v) {
     return __tree_.__emplace_hint_multi(__p, std::move(__v));
   }
 
-  _LIBCPP_HIDE_FROM_ABI void insert(initializer_list<value_type> __il) { insert(__il.begin(), __il.end()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(initializer_list<value_type> __il) {
+    insert(__il.begin(), __il.end());
+  }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
-  _LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_multi(__k); }
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
-  _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p) { return __tree_.erase(__p); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(const key_type& __k) {
+    return __tree_.__erase_multi(__k);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
+    return __tree_.erase(__f, __l);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
-  _LIBCPP_HIDE_FROM_ABI iterator insert(node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to multiset::insert()");
     return __tree_.template __node_handle_insert_multi<node_type>(std::move(__nh));
   }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __hint, node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __hint, node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to multiset::insert()");
     return __tree_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract<node_type>(__it);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI void swap(multiset& __s) _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(multiset& __s)
+      _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
     __tree_.swap(__s.__tree_);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const _NOEXCEPT { return __tree_.__alloc(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI key_compare key_comp() const { return __tree_.value_comp(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI value_compare value_comp() const { return __tree_.value_comp(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type get_allocator() const _NOEXCEPT {
+    return __tree_.__alloc();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_compare key_comp() const {
+    return __tree_.value_comp();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_compare value_comp() const {
+    return __tree_.value_comp();
+  }
 
   // set operations:
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __tree_.find(__k); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __tree_.find(__k); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const key_type& __k) {
+    return __tree_.find(__k);
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const key_type& __k) const {
+    return __tree_.find(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const _K2& __k) {
     return __tree_.find(__k);
   }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const _K2& __k) const {
     return __tree_.find(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const key_type& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const _K2& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  endif
 
 #  if _LIBCPP_STD_VER >= 20
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const key_type& __k) const {
+    return find(__k) != end();
+  }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
 #  endif // _LIBCPP_STD_VER >= 20
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator l...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/206257

From libcxx-commits at lists.llvm.org  Thu Jul  2 01:41:20 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 01:41:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a462430.f8b79514.3b441.3ced@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/206423

>From a521c654c3a7ac6e20c67f47f2c739c317e850a7 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 29 Jun 2026 10:00:42 +0200
Subject: [PATCH] [libc++] Add an ABI flag to optimize mersenne_twister_engine

---
 libcxx/include/__configuration/abi.h          |   1 +
 .../__random/mersenne_twister_engine.h        |  43 +-
 libcxx/test/benchmarks/random.bench.cpp       |  25 +
 .../rand.eng.mers/ctor_result_type.pass.cpp   | 583 +++++++++------
 .../rand.eng/rand.eng.mers/ctor_sseq.pass.cpp | 697 +++++++++++-------
 .../libcxx/test/features/libcxx_macros.py     |   1 +
 6 files changed, 836 insertions(+), 514 deletions(-)

diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 51c82d99eec30..7a4c479fd1636 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -77,6 +77,7 @@
 #  define _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION
 #  define _LIBCPP_ABI_TRIVIALLY_COPYABLE_BIT_ITERATOR
 #  define _LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE
+#  define _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
 
 #elif _LIBCPP_ABI_VERSION == 1
 // Feature macros for disabling pre ABI v1 features. All of these options
diff --git a/libcxx/include/__random/mersenne_twister_engine.h b/libcxx/include/__random/mersenne_twister_engine.h
index ce82b3387dbd2..8e49b3b267aa8 100644
--- a/libcxx/include/__random/mersenne_twister_engine.h
+++ b/libcxx/include/__random/mersenne_twister_engine.h
@@ -181,6 +181,9 @@ class mersenne_twister_engine {
     for (size_t __i = 1; __i < __n; ++__i)
       __x_[__i] = (__f * (__x_[__i - 1] ^ __rshift<__w - 2>(__x_[__i - 1])) + __i) & _Max;
     __i_ = 0;
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    __update_all_states();
+#endif
   }
   template <class _Sseq, __enable_if_t<__is_seed_sequence_v<_Sseq, mersenne_twister_engine>, int> = 0>
   _LIBCPP_HIDE_FROM_ABI void seed(_Sseq& __q) {
@@ -203,17 +206,43 @@ class mersenne_twister_engine {
           return;
       __x_[0] = result_type(1) << (__w - 1);
     }
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    __update_all_states();
+#endif
+  }
+
+  void __update_state(size_t __i, size_t __k) {
+    const size_t __j         = (__i + 1) % __n;
+    const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
+    const result_type __yp   = (__x_[__i] & ~__mask) | (__x_[__j] & __mask);
+    __x_[__i]                = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
+  }
+
+  void __update_state(size_t __i) { __update_state(__i, (__i + __m) % __n); }
+
+  void __update_all_states() {
+    size_t __i = 0;
+    // This is split into two loops to help the compiler vectorize the code
+    for (; __i != (__n - __m); ++__i)
+      __update_state(__i);
+    for (size_t __j = 0; __i != __n; ++__i, ++__j)
+      __update_state(__i, __j);
   }
 
   // generating functions
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI result_type operator()() {
-    const size_t __j         = (__i_ + 1) % __n;
-    const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
-    const result_type __yp   = (__x_[__i_] & ~__mask) | (__x_[__j] & __mask);
-    const size_t __k         = (__i_ + __m) % __n;
-    __x_[__i_]               = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
-    result_type __z          = __x_[__i_] ^ (__rshift<__u>(__x_[__i_]) & __d);
-    __i_                     = __j;
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    result_type __val = __x_[__i_];
+    if (++__i_ == __n) [[__unlikely__]] {
+      __update_all_states();
+      __i_ = 0;
+    }
+#else
+    __update_state(__i_);
+    result_type __val = __x_[__i_];
+    __i_              = (__i_ + 1) % __n;
+#endif
+    result_type __z = __val ^ (__rshift<__u>(__val) & __d);
     __z ^= __lshift<__s>(__z) & __b;
     __z ^= __lshift<__t>(__z) & __c;
     return __z ^ __rshift<__l>(__z);
diff --git a/libcxx/test/benchmarks/random.bench.cpp b/libcxx/test/benchmarks/random.bench.cpp
index e6af4c3e26eaf..15e16b988a29f 100644
--- a/libcxx/test/benchmarks/random.bench.cpp
+++ b/libcxx/test/benchmarks/random.bench.cpp
@@ -34,4 +34,29 @@ static void BM_SeedSeq_Generate(benchmark::State& state) {
 }
 BENCHMARK(BM_SeedSeq_Generate)->Ranges({{1, MAX_SEED_LEN}, {1, MAX_BUFFER_LEN}});
 
+template <class Engine>
+static void BM_engine(benchmark::State& state) {
+  Engine engine;
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(engine());
+  }
+}
+BENCHMARK(BM_engine<std::mt19937_64>)->Name("std::mt19937_64::operator()");
+BENCHMARK(BM_engine<std::mt19937>)->Name("std::mt19937::operator()");
+
+template <class Engine>
+static void BM_engine_array(benchmark::State& state) {
+  Engine engine;
+
+  typename Engine::result_type buffer[128];
+
+  for (auto _ : state) {
+    std::generate(std::begin(buffer), std::end(buffer), std::ref(engine));
+    benchmark::DoNotOptimize(buffer);
+  }
+}
+BENCHMARK(BM_engine_array<std::mt19937_64>)->Name("std::mt19937_64::operator() (into array)");
+BENCHMARK(BM_engine_array<std::mt19937>)->Name("std::mt19937::operator() (into array)");
+
 BENCHMARK_MAIN();
diff --git a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
index 02df0345525cb..b2ae5141edb06 100644
--- a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
@@ -20,237 +20,371 @@
 // Serializing/deserializing the state of the RNG requires iostreams
 // UNSUPPORTED: no-localization
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME && libcpp-abi-vectorized-mersenne-twister-engine
+
 #include <random>
 #include <sstream>
 #include <cassert>
 
 #include "test_macros.h"
 #if TEST_STD_VER >= 11
-#include "make_implicit.h"
-#include "test_convertible.h"
+#  include "make_implicit.h"
+#  include "test_convertible.h"
 #endif
 
 template <class T>
-std::string
-to_string(T const &e)
-{
-    std::ostringstream os;
-    os << e;
-    return os.str();
+std::string to_string(T const& e) {
+  std::ostringstream os;
+  os << e;
+  return os.str();
 }
 
-void
-test1()
-{
-    const char* a = "0 1 1812433255 1900727105 1208447044 2481403966 4042607538 337614300 "
-    "3232553940 1018809052 3202401494 1775180719 3192392114 594215549 184016991 "
-    "829906058 610491522 3879932251 3139825610 297902587 4075895579 2943625357 "
-    "3530655617 1423771745 2135928312 2891506774 1066338622 135451537 933040465 "
-    "2759011858 2273819758 3545703099 2516396728 1272276355 3172048492 "
-    "3267256201 2332199830 1975469449 392443598 1132453229 2900699076 "
-    "1998300999 3847713992 512669506 1227792182 1629110240 112303347 2142631694 "
-    "3647635483 1715036585 2508091258 1355887243 1884998310 3906360088 "
-    "952450269 3647883368 3962623343 3077504981 2023096077 3791588343 "
-    "3937487744 3455116780 1218485897 1374508007 2815569918 1367263917 "
-    "472908318 2263147545 1461547499 4126813079 2383504810 64750479 2963140275 "
-    "1709368606 4143643781 835933993 1881494649 674663333 2076403047 858036109 "
-    "1667579889 1706666497 607785554 1995775149 1941986352 3448871082 "
-    "2109910019 1474883361 1623095288 1831376534 2612738285 81681830 2204289242 "
-    "1365038485 251164610 4268495337 1805601714 1262528768 1442526919 "
-    "1675006593 965627108 646339161 499795587 840887574 380522518 3023789847 "
-    "1457635507 1947093157 2600365344 2729853143 1550618999 1390905853 "
-    "3021294812 882647559 838872117 1663880796 4222103589 2754172275 3844026123 "
-    "3199260319 4176064873 3591027019 2690294242 2978135515 3172796441 "
-    "3263669796 1451257057 1427035359 4174826006 2171992010 1537002090 "
-    "3122405306 4162452508 3271954368 3794310005 3240514581 1270412086 "
-    "3030475836 2281945856 2644171349 3109139423 4253563838 1289926431 "
-    "1396919653 733220100 2753316645 1196225013 3699575255 3569440056 "
-    "2675979228 2624079148 3463113149 863430286 623703199 2113837653 2656425919 "
-    "175981357 4271478366 4238022735 1665483419 86880610 2963435083 1830392943 "
-    "847801865 3237296945 332143967 3973606945 2671879697 2236330279 2360127810 "
-    "3283955434 203240344 4048139172 13189264 2263058814 247241371 1566765783 "
-    "3084408095 3719371299 1958375251 1985924622 1712739232 1861691451 "
-    "2644502937 2337807839 784993770 2962208780 2190810177 1523122731 "
-    "714888527 578678761 3698481324 1801168075 534650483 3390213921 3923356461 "
-    "3586009066 2059432114 52511333 1969897376 3630122061 524661135 3513619765 "
-    "563070233 501359785 477489274 658768624 938973567 1548584683 1345287459 "
-    "2488691004 3441144905 3849305094 2430000078 855172178 614463281 2092744749 "
-    "176381493 1655802051 2273888101 2474494847 3471978030 2138918303 575352373 "
-    "1658230985 1675972553 2946663114 915579339 284981499 53939948 3022598146 "
-    "1861218535 3403620774 4203516930 2360471119 3134536268 1383448498 "
-    "1307602316 3847663247 3027225131 3597251613 3186237127 725127595 "
-    "1928526954 1843386923 3560410503 54688266 1791983849 2519860352 4256389699 "
-    "2328812602 486464275 3578698363 301279829 1303654791 4181868765 971794070 "
-    "1933885487 3996807464 2144053754 4079903755 3775774765 3481760044 "
-    "1212862354 1067356423 3764189132 1609862325 2209601551 2565747501 "
-    "161962392 4045451782 2605574664 2520953090 3490240017 1082791980 44474324 "
-    "101811128 4268650669 4171338684 772375154 3920460306 2319139534 599033750 "
-    "2950874441 3373922995 1496848525 4095253594 1271943484 1498723121 "
-    "3097453329 3698082465 281869581 3148270661 3591477288 747441437 2809508504 "
-    "3896107498 303747862 2368081624 1844217645 886825352 287949781 1444561207 "
-    "2512101757 2062331723 741720931 1383797313 3876746355 2041045348 "
-    "2627599118 1124169970 200524822 3484820454 55883666 1135054804 669498692 "
-    "2677215504 3097911127 1509628615 617580381 2229022193 85601568 3243896546 "
-    "3715672328 912168347 2359163500 1180347564 4243175048 2092067103 880183327 "
-    "4000664709 2045044777 3500474644 1515175520 1862207123 186628841 "
-    "3337252925 708933575 4015964629 3136815297 3314919747 2891909013 "
-    "3316567785 3944275369 3608506218 2884839110 3054055598 2707439927 "
-    "1381111877 3275487281 4292456216 2639563270 3327301876 3576924628 "
-    "721056309 2002808140 748967365 52380958 2200261692 763456477 1708381337 "
-    "2038446433 2682979402 1526413779 2211263302 3879771969 75966584 3645059271 "
-    "2985763524 4085690255 82390958 1883631385 1647521260 1598026998 3038041577 "
-    "2501913134 3279302868 1738888524 805035483 756399074 3863810982 1097797270 "
-    "1505792529 898904527 583561003 717152376 3333867738 1099456544 1663473545 "
-    "1242141229 3828627682 1966201676 1713552361 3852160017 1584965284 21695908 "
-    "1013262144 145341901 3995441263 3462066219 2239637848 1214086163 "
-    "2428868268 1650037305 1545513388 1621198806 4232947817 1823092073 "
-    "256414624 1745018809 1357102386 2055139770 3280958307 2482431613 "
-    "1664870585 859130423 4097751123 3079768369 2470211009 2984880786 "
-    "2808568948 2877071923 1984903163 302768457 1866396789 869566317 3746415787 "
-    "4169433075 3025005404 3980733379 3539207278 3953071536 876960847 "
-    "2548872156 800507464 1865466907 1273317878 3754712872 1757188269 "
-    "3229950355 3731640200 2283390608 2204990292 411873449 447423849 1852437802 "
-    "472825525 3044219944 2913114194 1859709265 4053786194 574820536 2104496732 "
-    "865469814 2438352724 4208743605 4215067542 1364015250 4139974345 "
-    "3838747005 1818502786 2914274940 1402365828 1751123528 2302578077 "
-    "2463168652 1968705496 1730700144 3023943273 1139096844 2658667767 "
-    "2063547264 705791165 1444775274 2415454225 1575664730 921044163 648101324 "
-    "1212387162 4191962054 1787702169 1888718041 1518218010 3398792842 "
-    "4079359729 149721439 750400353 2661036076 3802767886 520152586 951852508 "
-    "2939585975 1375969109 385733137 3523607459 1902438415 4250996086 "
-    "2712727066 484493674 3932107461 1428488210 1764242548 3424801055 "
-    "4004904451 2226862072 2393366939 3609584727 3614444319 317349896 "
-    "3826527525 204023804 981902443 3356042039 3051207045 1869902661 561831895 "
-    "3706675415 1527687593 1227610446 2596341042 3191717368 3269246891 "
-    "557877074 4062070629 3052520266 3772487029 400039836 3195205275 4085394797 "
-    "1655557239 1345770144 2864727192 449281238 73189507 528365765 2727400656 "
-    "247880434 2408277395 777039183 2210179398 1088433648 2124356402 1555630141 "
-    "604790219 195012151 3312518356 923728373 3999251660 3313059535 3478133921 "
-    "3395026960 383464614 3425869222 2446885186 4032184426 157195416 3158909476 "
-    "1663750443 2046427584 1658453076 1784483001 3146546889 1238739785 "
-    "2297306523 3472330897 2953326031 2421672215 1221694592 1588568605 "
-    "2546987845 3375168573 2137961649 3056565164 330165219 235900365 1000384800 "
-    "2697255904 579122283 3050664825 73426122 1232986102 2940571064 3076486824 "
-    "1708182873 2796363264 292154131 4280019913 1102652157 1185393592 "
-    "1494991690 4270076389 2384840717 425785147 2385321880 317514772 3926962743 "
-    "392176856 3465421709 1878853468 122662664 2958252160 1858961315 2244939588 "
-    "2361884409 2860936803 683833250 3291277128 1686857206 1112632275 "
-    "1200680507 3342928196 2677058150 939442136 3407104669 2906783932 "
-    "3668048733 2030009470 1910839172 1234925283 3575831445 123595418 "
-    "2362440495 3048484911 1796872496";
-    std::mt19937 e1(0);
-    assert(to_string(e1) == a);
+void test1() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "0 1 1812433255 1900727105 1208447044 2481403966 4042607538 337614300 "
+      "3232553940 1018809052 3202401494 1775180719 3192392114 594215549 184016991 "
+      "829906058 610491522 3879932251 3139825610 297902587 4075895579 2943625357 "
+      "3530655617 1423771745 2135928312 2891506774 1066338622 135451537 933040465 "
+      "2759011858 2273819758 3545703099 2516396728 1272276355 3172048492 "
+      "3267256201 2332199830 1975469449 392443598 1132453229 2900699076 "
+      "1998300999 3847713992 512669506 1227792182 1629110240 112303347 2142631694 "
+      "3647635483 1715036585 2508091258 1355887243 1884998310 3906360088 "
+      "952450269 3647883368 3962623343 3077504981 2023096077 3791588343 "
+      "3937487744 3455116780 1218485897 1374508007 2815569918 1367263917 "
+      "472908318 2263147545 1461547499 4126813079 2383504810 64750479 2963140275 "
+      "1709368606 4143643781 835933993 1881494649 674663333 2076403047 858036109 "
+      "1667579889 1706666497 607785554 1995775149 1941986352 3448871082 "
+      "2109910019 1474883361 1623095288 1831376534 2612738285 81681830 2204289242 "
+      "1365038485 251164610 4268495337 1805601714 1262528768 1442526919 "
+      "1675006593 965627108 646339161 499795587 840887574 380522518 3023789847 "
+      "1457635507 1947093157 2600365344 2729853143 1550618999 1390905853 "
+      "3021294812 882647559 838872117 1663880796 4222103589 2754172275 3844026123 "
+      "3199260319 4176064873 3591027019 2690294242 2978135515 3172796441 "
+      "3263669796 1451257057 1427035359 4174826006 2171992010 1537002090 "
+      "3122405306 4162452508 3271954368 3794310005 3240514581 1270412086 "
+      "3030475836 2281945856 2644171349 3109139423 4253563838 1289926431 "
+      "1396919653 733220100 2753316645 1196225013 3699575255 3569440056 "
+      "2675979228 2624079148 3463113149 863430286 623703199 2113837653 2656425919 "
+      "175981357 4271478366 4238022735 1665483419 86880610 2963435083 1830392943 "
+      "847801865 3237296945 332143967 3973606945 2671879697 2236330279 2360127810 "
+      "3283955434 203240344 4048139172 13189264 2263058814 247241371 1566765783 "
+      "3084408095 3719371299 1958375251 1985924622 1712739232 1861691451 "
+      "2644502937 2337807839 784993770 2962208780 2190810177 1523122731 "
+      "714888527 578678761 3698481324 1801168075 534650483 3390213921 3923356461 "
+      "3586009066 2059432114 52511333 1969897376 3630122061 524661135 3513619765 "
+      "563070233 501359785 477489274 658768624 938973567 1548584683 1345287459 "
+      "2488691004 3441144905 3849305094 2430000078 855172178 614463281 2092744749 "
+      "176381493 1655802051 2273888101 2474494847 3471978030 2138918303 575352373 "
+      "1658230985 1675972553 2946663114 915579339 284981499 53939948 3022598146 "
+      "1861218535 3403620774 4203516930 2360471119 3134536268 1383448498 "
+      "1307602316 3847663247 3027225131 3597251613 3186237127 725127595 "
+      "1928526954 1843386923 3560410503 54688266 1791983849 2519860352 4256389699 "
+      "2328812602 486464275 3578698363 301279829 1303654791 4181868765 971794070 "
+      "1933885487 3996807464 2144053754 4079903755 3775774765 3481760044 "
+      "1212862354 1067356423 3764189132 1609862325 2209601551 2565747501 "
+      "161962392 4045451782 2605574664 2520953090 3490240017 1082791980 44474324 "
+      "101811128 4268650669 4171338684 772375154 3920460306 2319139534 599033750 "
+      "2950874441 3373922995 1496848525 4095253594 1271943484 1498723121 "
+      "3097453329 3698082465 281869581 3148270661 3591477288 747441437 2809508504 "
+      "3896107498 303747862 2368081624 1844217645 886825352 287949781 1444561207 "
+      "2512101757 2062331723 741720931 1383797313 3876746355 2041045348 "
+      "2627599118 1124169970 200524822 3484820454 55883666 1135054804 669498692 "
+      "2677215504 3097911127 1509628615 617580381 2229022193 85601568 3243896546 "
+      "3715672328 912168347 2359163500 1180347564 4243175048 2092067103 880183327 "
+      "4000664709 2045044777 3500474644 1515175520 1862207123 186628841 "
+      "3337252925 708933575 4015964629 3136815297 3314919747 2891909013 "
+      "3316567785 3944275369 3608506218 2884839110 3054055598 2707439927 "
+      "1381111877 3275487281 4292456216 2639563270 3327301876 3576924628 "
+      "721056309 2002808140 748967365 52380958 2200261692 763456477 1708381337 "
+      "2038446433 2682979402 1526413779 2211263302 3879771969 75966584 3645059271 "
+      "2985763524 4085690255 82390958 1883631385 1647521260 1598026998 3038041577 "
+      "2501913134 3279302868 1738888524 805035483 756399074 3863810982 1097797270 "
+      "1505792529 898904527 583561003 717152376 3333867738 1099456544 1663473545 "
+      "1242141229 3828627682 1966201676 1713552361 3852160017 1584965284 21695908 "
+      "1013262144 145341901 3995441263 3462066219 2239637848 1214086163 "
+      "2428868268 1650037305 1545513388 1621198806 4232947817 1823092073 "
+      "256414624 1745018809 1357102386 2055139770 3280958307 2482431613 "
+      "1664870585 859130423 4097751123 3079768369 2470211009 2984880786 "
+      "2808568948 2877071923 1984903163 302768457 1866396789 869566317 3746415787 "
+      "4169433075 3025005404 3980733379 3539207278 3953071536 876960847 "
+      "2548872156 800507464 1865466907 1273317878 3754712872 1757188269 "
+      "3229950355 3731640200 2283390608 2204990292 411873449 447423849 1852437802 "
+      "472825525 3044219944 2913114194 1859709265 4053786194 574820536 2104496732 "
+      "865469814 2438352724 4208743605 4215067542 1364015250 4139974345 "
+      "3838747005 1818502786 2914274940 1402365828 1751123528 2302578077 "
+      "2463168652 1968705496 1730700144 3023943273 1139096844 2658667767 "
+      "2063547264 705791165 1444775274 2415454225 1575664730 921044163 648101324 "
+      "1212387162 4191962054 1787702169 1888718041 1518218010 3398792842 "
+      "4079359729 149721439 750400353 2661036076 3802767886 520152586 951852508 "
+      "2939585975 1375969109 385733137 3523607459 1902438415 4250996086 "
+      "2712727066 484493674 3932107461 1428488210 1764242548 3424801055 "
+      "4004904451 2226862072 2393366939 3609584727 3614444319 317349896 "
+      "3826527525 204023804 981902443 3356042039 3051207045 1869902661 561831895 "
+      "3706675415 1527687593 1227610446 2596341042 3191717368 3269246891 "
+      "557877074 4062070629 3052520266 3772487029 400039836 3195205275 4085394797 "
+      "1655557239 1345770144 2864727192 449281238 73189507 528365765 2727400656 "
+      "247880434 2408277395 777039183 2210179398 1088433648 2124356402 1555630141 "
+      "604790219 195012151 3312518356 923728373 3999251660 3313059535 3478133921 "
+      "3395026960 383464614 3425869222 2446885186 4032184426 157195416 3158909476 "
+      "1663750443 2046427584 1658453076 1784483001 3146546889 1238739785 "
+      "2297306523 3472330897 2953326031 2421672215 1221694592 1588568605 "
+      "2546987845 3375168573 2137961649 3056565164 330165219 235900365 1000384800 "
+      "2697255904 579122283 3050664825 73426122 1232986102 2940571064 3076486824 "
+      "1708182873 2796363264 292154131 4280019913 1102652157 1185393592 "
+      "1494991690 4270076389 2384840717 425785147 2385321880 317514772 3926962743 "
+      "392176856 3465421709 1878853468 122662664 2958252160 1858961315 2244939588 "
+      "2361884409 2860936803 683833250 3291277128 1686857206 1112632275 "
+      "1200680507 3342928196 2677058150 939442136 3407104669 2906783932 "
+      "3668048733 2030009470 1910839172 1234925283 3575831445 123595418 "
+      "2362440495 3048484911 1796872496"
+#else
+      "2443250962 1093594115 1878467924 2709361018 1101979660 3904844661 676747479 2085143622 1056793272 3812477442 "
+      "2168787041 275552121 2696932952 3432054210 1657102335 3518946594 962584079 1051271004 3806145045 1414436097 "
+      "2032348584 1661738718 1116708477 2562755208 3176189976 696824676 2399811678 3992505346 569184356 2626558620 "
+      "136797809 4273176064 296167901 3430730584 331909803 1908676996 1950065095 604298543 3615988338 1570232852 "
+      "1028209748 1511467721 2411887154 4210753555 3096762720 423429618 659966766 2937509307 2222847265 378636552 "
+      "1142109618 2509241601 1521729757 888533219 250885260 2455816244 4046047811 1947467789 1395351953 2388948566 "
+      "934627940 194642258 1429256273 2139959677 1543740405 1569613451 4061840539 2075690423 824532376 844152077 "
+      "3218002536 897315311 823414659 1007534698 3139313867 2332794462 3675156583 722594429 3715123011 2656840676 "
+      "2373277715 1516144243 1538161999 1399719297 1448820876 3180842436 2020192709 3279498509 1048107540 3094290376 "
+      "3706920703 3810051683 4003871775 1069557309 151535004 667695756 861767313 1643171871 3649507728 3785763588 "
+      "737899497 2332908276 4082123854 1584539673 3929877280 1046086041 1304062606 2311324008 1190424879 538647603 "
+      "1728806718 148948582 659721465 2351006009 185647941 1825934330 2126619619 2230539295 3878421575 965937340 "
+      "2835563987 419858879 1527311104 2042333483 2745134265 3540517373 1100062165 2308613697 2690739856 2048293545 "
+      "2742876486 2408502115 53398423 3089771461 1398529613 2132477261 504134301 1535108421 1968507173 3412472770 "
+      "4048517196 3509143071 857063211 1429875570 4120479871 2786574360 2470799871 1644104875 2326863162 2033900387 "
+      "274888909 2629973384 1154405937 1839988197 2163830694 268733679 2928953965 387153042 3790900490 3202349717 "
+      "3799209681 2524871464 3801447311 3543081470 1801633977 3870370950 1581678156 358283485 4133385582 4052311650 "
+      "249498956 1712028623 3618329357 3395106946 2704820515 26653208 2437509555 4192186371 3639946838 2615917799 "
+      "294624699 463886267 2477203673 2514051526 4162882381 2937629358 3180524454 1388392394 2637132139 1999482082 "
+      "1870663339 2852309250 3489430700 1116063402 1673637188 1941955790 4081919937 2322920953 3500518887 2722575905 "
+      "405031829 3728964889 3464559963 665516555 1626835870 2523729468 238289561 497556937 2581429740 3456619478 "
+      "2601389837 820426192 268141532 2654829421 349692193 2426957585 1465734488 85095426 1080895536 2823366942 "
+      "379322515 2938986739 1561328720 2940081697 607513620 2724557130 2841377034 14123440 1085631349 1977859413 "
+      "1325586902 1690398943 2516154740 4144373551 555899232 1472707937 3318086852 719779833 3545481907 1384350857 "
+      "191709822 2924586997 3905397527 2528674757 2377804096 2742818576 4162971050 1916415976 2229076768 132706400 "
+      "1337415646 251604474 4168783505 819499074 1213291699 2098930479 1009044052 1060792037 2169940733 2973281031 "
+      "636656529 1946890813 367099469 4076624283 338339529 561343735 3309733580 3895891229 516195845 3075613489 "
+      "3076388119 4088758753 3895582245 1192445752 2923797073 2272605437 2964193665 943979628 3263904824 1852145108 "
+      "1911050628 1596880711 479795791 212699077 2172697748 1764175804 3951845064 2182587171 2396848133 2718837214 "
+      "2934177572 3638054152 914795167 1028977354 1744985339 1161407853 2069746822 3109934340 3666150054 729399111 "
+      "2911998815 152809615 417267263 1064343325 2751707923 1833270972 886939906 4054231201 1410366452 976142646 "
+      "1452795786 1904234879 4231894541 1505392175 3498767279 833704604 2100592828 688268099 1752675602 1975556664 "
+      "2102210733 698665709 1232104968 4051336323 1740516649 3131721670 3755143232 3433756601 138325540 1566724819 "
+      "3146312914 3262098346 1400757929 3821885720 356740291 4236900846 3962771215 3383537419 3432281817 3700484231 "
+      "1124512300 4040103150 2152814065 357241942 3507144572 3156778000 4029389048 1495464238 2710816120 618937484 "
+      "923913256 3233161411 3113338135 2380534800 2989134055 792458413 2073679910 2733527896 1078384210 2832175364 "
+      "501385130 1554930792 2339252603 535487294 2983664186 924228889 2127502206 2847205678 836935943 1902192892 "
+      "4094716961 3296730761 2307406435 281523156 728701372 3943302480 183005637 2660706047 2320033913 2011717602 "
+      "219970222 890345073 2479236311 653839655 38538414 3266308455 3726363269 1256762826 712114913 3499059198 "
+      "2847394904 3246075282 1299462737 830090526 369065487 3899451086 1825910875 2697294244 2553197317 2501134241 "
+      "930821716 2841281141 3920808923 3209045349 3381621736 2145077949 1958091404 371971179 3064957112 3151429184 "
+      "2833651995 1089438243 2146134351 1426568997 3528669710 1043897757 3052263589 3205925012 4076215371 2622476950 "
+      "2385717100 2179892838 3820135605 1574628644 177604240 1712062317 1192081671 1107476027 837376039 2818244460 "
+      "1382379731 2749207910 2644203474 1508860349 3006844699 3157252375 3789683778 1985542020 2309789126 1624089624 "
+      "3660683301 1432444043 1172735898 3271587635 841387671 3614623669 3002115850 1078807738 1095345284 1707086713 "
+      "4263485053 445506874 3148772337 2716917408 3836812085 1026228926 491069724 3983218285 2410582206 3821759029 "
+      "3789305873 1223256738 1677180485 1481771733 1670286271 2852111711 1628943665 2289772949 3483580787 2129860019 "
+      "3955695109 28974017 2289227173 1725243773 483357125 111587742 347631238 1805029491 839978009 339030914 "
+      "2442782449 1701726526 1481742442 3703534067 3796147861 235078546 3195450129 1420184214 998981944 163541155 "
+      "2087693215 3827554748 3003331872 1967967170 160190917 544473278 3884602428 4180513186 1603446876 2191744556 "
+      "1938251010 291242497 1764195107 4076051069 3860380798 806452940 2631256196 949838208 4096577802 1519225265 "
+      "2296800559 1015848400 2864792481 88004533 195132439 966918321 1451347413 2322994051 3585658769 1771598947 "
+      "3308395968 950148946 3846061568 564475171 1345892193 2439581032 2907206869 1297626422 3779454571 818255727 "
+      "705738065 3915384184 4133726754 2724859327 3773743817 326325901 2766390048 2562130418 1885210076 2622324981 "
+      "1727396790 1742653294 3133131942 3790857080 197042473 2868410679 1122976862 2264474389 3607393378 744386048 "
+      "3122128592 1101489146 4064675687 2818166901 2282076661 370818870 3053423257 2272652338 4077081984 4284213290 "
+      "1728304803 3902867896 1643530236 306212760 144705843 498541197 3103269575 4116304893 1646288275 2110757680 "
+      "4092919505 3390929578 1828116798 1847914177 1156754878 2985404552 3970881126 3035874635 2189824678 2637114092 "
+      "2588332748 3917818803 3302695646 1758418726 1934759438 3871576937 1360769189 1051808374 1881390541 757367126 "
+      "3253778407 1685049720 805311189 934898036 95682794 2052240235 3475933669 2213474191 3341273357 2443286051 "
+      "4272536794 696819230 878775027 3332634155 3405126958 2824772573 95284505 3040640920 2412859827 2959515765 "
+      "1764792340 1504924482 2116910326 2293943003 3199262907 1443740894 3900566588 343066614 1927475335 249220892 "
+      "2920636502 3593328327 2648828502 1678096082"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  std::mt19937 e1(0);
+  LIBCPP_ASSERT(to_string(e1) == a);
+  (void)a;
 }
 
-void
-test2()
-{
-    const char* a = "0 1 6364136223846793007 13885033948157127961 "
-    "15324573939901584278 12737837167382305846 15195339788985155882 "
-    "6554113247712070460 17235932740818599105 13007415075556305955 "
-    "6585479514541334743 8274505865835507625 1718218088692873364 "
-    "10390651247454232081 12911994993614796389 3986900029987203370 "
-    "6673827651897561714 4426752746717694792 7419158062930293690 "
-    "5800047417539173618 15710773105226458059 16164512590413496893 "
-    "3438015953120274172 3483801391287623267 293704481016263807 "
-    "11580856846363212652 3489109114147636336 3391036861618673611 "
-    "8265793309278544843 7557898467821912223 11008748280761875940 "
-    "15929443707841919885 8545695347411085846 10810459396490399532 "
-    "12233244910455127352 15556950738631379285 16711543556686614082 "
-    "12362193084052127890 16520645558585805174 5163125267185202360 "
-    "405552980610370477 17567412011316060306 18195950784827697319 "
-    "7893142112162906367 11294475722810961618 7284845498332539581 "
-    "8406882439401998138 4375387785957411470 9627875716250684710 "
-    "8860968026642934661 9743109216691708518 152611520104818631 "
-    "5897430410700879663 5351672954305365323 16325991383734641720 "
-    "9695181037355459478 15420132328343498044 17772146581546890572 "
-    "12095930435311226909 3066005052193896110 11579395203346116306 "
-    "9168946227698330317 18318927644793076250 16096433325093805476 "
-    "14945900876820434843 16826760579960858105 17664119339058295604 "
-    "17844797344364136942 1071414400549507565 16688779616725465582 "
-    "3684635020921274863 12774052823168116810 17270284502989966576 "
-    "1081012692742984704 4377021575203177546 18341292555997099853 "
-    "13297939683513494274 15065725504474304333 10796297883750572804 "
-    "15233335271871291997 8767977593699151062 3360856014170688284 "
-    "7828232912764786750 15167717223619970150 9622174963375022357 "
-    "18262792478991268448 1196631425707106493 5368342740538672272 "
-    "10381091599850241237 12108437846306626340 6150860188928778248 "
-    "3342980288459577584 12715439159457051276 17996971042887275859 "
-    "9749679821487730542 17763727344608586331 16024467606290078168 "
-    "7763401887585513696 4448278918811662063 16947956613780322662 "
-    "15144807360840708645 3878082612940188435 10709780449699561405 "
-    "1649555943517500922 3206645931693769562 12562913950237146427 "
-    "237213339742767727 12987800257476421358 1653669854585203688 "
-    "3485900643226898485 13961759114404652223 5243794832751327611 "
-    "10337687908642742498 16946139522050041809 16716562961992396380 "
-    "4275124606042261542 4055100795824867618 6424268654905981295 "
-    "3424516503413156556 2670380025813203539 10750762735193959951 "
-    "8790031149370411970 4021216986392972993 12076090355041998696 "
-    "14407920322903159838 10653597737935867030 15483225617438352002 "
-    "2497775263858626604 12295882369431088188 14256043521530136935 "
-    "2687322778627883798 3419797801078863201 8786888481486602641 "
-    "445698423634900693 9597067954623467255 7101345576557603992 "
-    "1498579197046783597 10403325187679734962 2464586980321053562 "
-    "2022012026329844477 10802281218030350853 6628929099856200904 "
-    "6828177972863192803 8589868113309334601 5245595233272009016 "
-    "5335692004673212054 4515133017699498525 15966447436053813932 "
-    "15199779177078162007 4190689609934804313 13003438276435994683 "
-    "8406046831313066396 10564320513686955057 12668913223662201488 "
-    "13130110932487580228 1030848205404711145 17684061609212954769 "
-    "12942207438298787911 10731611242140874687 5165052527778107352 "
-    "16323046249518133445 17119162873327029615 5754858052433703070 "
-    "3864761150247579030 9945988334920003074 11409854727071782565 "
-    "5000838138362434817 15526574143469400487 18094554078711846524 "
-    "5576294272011007484 3478525338408894755 11392694223389544658 "
-    "4692963068671452476 4459301637730340710 9699395817392066460 "
-    "14644636990626292085 18065377773424192622 5217202490849387226 "
-    "16175595974171756081 2109372019589047677 1624752883142646445 "
-    "13462209973053735966 12082930933973802402 1568864426788967895 "
-    "17047994306870001795 10556833209957537593 955604103878351641 "
-    "9062985603395234592 9757612676622840969 1767246562613391916 "
-    "9752598821733361274 7499745701633625047 7824811626141302622 "
-    "15819064077972391284 5660565551854829485 17645390577243129343 "
-    "7343780801046581776 2233358068547689666 8716657172695403744 "
-    "9129027798969787220 334709674395230649 2063182499026924878 "
-    "13089071159640936832 1765917316143960741 17552378408917656269 "
-    "3917018959478722819 15626740210483166037 1645962609209923821 "
-    "12277169606472643961 14545894350924442736 11485249378718653961 "
-    "9205208816702766530 10967561305613932827 3105992977398681914 "
-    "2125140299311648264 11619505070220308543 5030167448920096401 "
-    "4248170446421798953 16184577688118775567 9240607582885304823 "
-    "11838996733938359277 415426114101983968 14340734742548675134 "
-    "4124085748228276376 17686494750190224280 9472996569628985376 "
-    "1207013222233148636 3031046462562068367 45068538181330439 "
-    "8678647417835301152 10693327126492781235 3058899219097846020 "
-    "18377730418355022492 10269941972656742364 15986476992758938864 "
-    "14575856764093007010 14749682846448398393 1042926396621439263 "
-    "12184905641567868001 3518848236485931862 6718580865438347534 "
-    "6319395993260741012 2855168874111910691 2482146419106261786 "
-    "17290238774162848390 8071397865265268054 15873003794708011585 "
-    "14422764926380465297 14140979091525022882 3573480287238168646 "
-    "1525896111244666696 7537826952717918371 10482908122538761078 "
-    "17003233041653857 9473838740924911883 8438240966750123668 "
-    "10697754962581554225 15048771265786776312 9067877678399943713 "
-    "3399555692325948067 6150260207049997483 7165140289246675175 "
-    "14816202987105583988 4753550992948864498 10549400354582510015 "
-    "13212062554023586370 1585477630313819722 476999696494664205 "
-    "3208739183359199317 16011681780347380478 8149150693959772807 "
-    "803412833228911773 2360961455092949929 1517204230639934662 "
-    "13863717544358808032 16792122738584967930 12742474971940259289 "
-    "1859755681395355028 1540431035241590810 3883807554140904361 "
-    "16189061625447625933 12376367376041900879 8006563585771266211 "
-    "2682617767291542421 8593924529704142157 9070391845133329273 "
-    "3557484410564396342 9301398051805853085 12632514768298643219 "
-    "227653509634201118 7247795074312488552 4939136716843120792 "
-    "6533591052147596041 1308401457054431629 17488144120120152559 "
-    "14075631579093810083 4015705597302725704 6833920126528811473 "
-    "5095302940809114298 8312250184258072842 15770605014574863643 "
-    "14091690436120485477 15763282477731738396 16394237160547425954 "
-    "5066318118328746621 13140493775100916989 6371148952471982853 "
-    "15150289760867914983 4931341382074091848 12635920082410445322 "
-    "8498109357807439006 14836776625250834986";
-    std::mt19937_64 e1(0);
-    assert(to_string(e1) == a);
+void test2() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "0 1 6364136223846793007 13885033948157127961 "
+      "15324573939901584278 12737837167382305846 15195339788985155882 "
+      "6554113247712070460 17235932740818599105 13007415075556305955 "
+      "6585479514541334743 8274505865835507625 1718218088692873364 "
+      "10390651247454232081 12911994993614796389 3986900029987203370 "
+      "6673827651897561714 4426752746717694792 7419158062930293690 "
+      "5800047417539173618 15710773105226458059 16164512590413496893 "
+      "3438015953120274172 3483801391287623267 293704481016263807 "
+      "11580856846363212652 3489109114147636336 3391036861618673611 "
+      "8265793309278544843 7557898467821912223 11008748280761875940 "
+      "15929443707841919885 8545695347411085846 10810459396490399532 "
+      "12233244910455127352 15556950738631379285 16711543556686614082 "
+      "12362193084052127890 16520645558585805174 5163125267185202360 "
+      "405552980610370477 17567412011316060306 18195950784827697319 "
+      "7893142112162906367 11294475722810961618 7284845498332539581 "
+      "8406882439401998138 4375387785957411470 9627875716250684710 "
+      "8860968026642934661 9743109216691708518 152611520104818631 "
+      "5897430410700879663 5351672954305365323 16325991383734641720 "
+      "9695181037355459478 15420132328343498044 17772146581546890572 "
+      "12095930435311226909 3066005052193896110 11579395203346116306 "
+      "9168946227698330317 18318927644793076250 16096433325093805476 "
+      "14945900876820434843 16826760579960858105 17664119339058295604 "
+      "17844797344364136942 1071414400549507565 16688779616725465582 "
+      "3684635020921274863 12774052823168116810 17270284502989966576 "
+      "1081012692742984704 4377021575203177546 18341292555997099853 "
+      "13297939683513494274 15065725504474304333 10796297883750572804 "
+      "15233335271871291997 8767977593699151062 3360856014170688284 "
+      "7828232912764786750 15167717223619970150 9622174963375022357 "
+      "18262792478991268448 1196631425707106493 5368342740538672272 "
+      "10381091599850241237 12108437846306626340 6150860188928778248 "
+      "3342980288459577584 12715439159457051276 17996971042887275859 "
+      "9749679821487730542 17763727344608586331 16024467606290078168 "
+      "7763401887585513696 4448278918811662063 16947956613780322662 "
+      "15144807360840708645 3878082612940188435 10709780449699561405 "
+      "1649555943517500922 3206645931693769562 12562913950237146427 "
+      "237213339742767727 12987800257476421358 1653669854585203688 "
+      "3485900643226898485 13961759114404652223 5243794832751327611 "
+      "10337687908642742498 16946139522050041809 16716562961992396380 "
+      "4275124606042261542 4055100795824867618 6424268654905981295 "
+      "3424516503413156556 2670380025813203539 10750762735193959951 "
+      "8790031149370411970 4021216986392972993 12076090355041998696 "
+      "14407920322903159838 10653597737935867030 15483225617438352002 "
+      "2497775263858626604 12295882369431088188 14256043521530136935 "
+      "2687322778627883798 3419797801078863201 8786888481486602641 "
+      "445698423634900693 9597067954623467255 7101345576557603992 "
+      "1498579197046783597 10403325187679734962 2464586980321053562 "
+      "2022012026329844477 10802281218030350853 6628929099856200904 "
+      "6828177972863192803 8589868113309334601 5245595233272009016 "
+      "5335692004673212054 4515133017699498525 15966447436053813932 "
+      "15199779177078162007 4190689609934804313 13003438276435994683 "
+      "8406046831313066396 10564320513686955057 12668913223662201488 "
+      "13130110932487580228 1030848205404711145 17684061609212954769 "
+      "12942207438298787911 10731611242140874687 5165052527778107352 "
+      "16323046249518133445 17119162873327029615 5754858052433703070 "
+      "3864761150247579030 9945988334920003074 11409854727071782565 "
+      "5000838138362434817 15526574143469400487 18094554078711846524 "
+      "5576294272011007484 3478525338408894755 11392694223389544658 "
+      "4692963068671452476 4459301637730340710 9699395817392066460 "
+      "14644636990626292085 18065377773424192622 5217202490849387226 "
+      "16175595974171756081 2109372019589047677 1624752883142646445 "
+      "13462209973053735966 12082930933973802402 1568864426788967895 "
+      "17047994306870001795 10556833209957537593 955604103878351641 "
+      "9062985603395234592 9757612676622840969 1767246562613391916 "
+      "9752598821733361274 7499745701633625047 7824811626141302622 "
+      "15819064077972391284 5660565551854829485 17645390577243129343 "
+      "7343780801046581776 2233358068547689666 8716657172695403744 "
+      "9129027798969787220 334709674395230649 2063182499026924878 "
+      "13089071159640936832 1765917316143960741 17552378408917656269 "
+      "3917018959478722819 15626740210483166037 1645962609209923821 "
+      "12277169606472643961 14545894350924442736 11485249378718653961 "
+      "9205208816702766530 10967561305613932827 3105992977398681914 "
+      "2125140299311648264 11619505070220308543 5030167448920096401 "
+      "4248170446421798953 16184577688118775567 9240607582885304823 "
+      "11838996733938359277 415426114101983968 14340734742548675134 "
+      "4124085748228276376 17686494750190224280 9472996569628985376 "
+      "1207013222233148636 3031046462562068367 45068538181330439 "
+      "8678647417835301152 10693327126492781235 3058899219097846020 "
+      "18377730418355022492 10269941972656742364 15986476992758938864 "
+      "14575856764093007010 14749682846448398393 1042926396621439263 "
+      "12184905641567868001 3518848236485931862 6718580865438347534 "
+      "6319395993260741012 2855168874111910691 2482146419106261786 "
+      "17290238774162848390 8071397865265268054 15873003794708011585 "
+      "14422764926380465297 14140979091525022882 3573480287238168646 "
+      "1525896111244666696 7537826952717918371 10482908122538761078 "
+      "17003233041653857 9473838740924911883 8438240966750123668 "
+      "10697754962581554225 15048771265786776312 9067877678399943713 "
+      "3399555692325948067 6150260207049997483 7165140289246675175 "
+      "14816202987105583988 4753550992948864498 10549400354582510015 "
+      "13212062554023586370 1585477630313819722 476999696494664205 "
+      "3208739183359199317 16011681780347380478 8149150693959772807 "
+      "803412833228911773 2360961455092949929 1517204230639934662 "
+      "13863717544358808032 16792122738584967930 12742474971940259289 "
+      "1859755681395355028 1540431035241590810 3883807554140904361 "
+      "16189061625447625933 12376367376041900879 8006563585771266211 "
+      "2682617767291542421 8593924529704142157 9070391845133329273 "
+      "3557484410564396342 9301398051805853085 12632514768298643219 "
+      "227653509634201118 7247795074312488552 4939136716843120792 "
+      "6533591052147596041 1308401457054431629 17488144120120152559 "
+      "14075631579093810083 4015705597302725704 6833920126528811473 "
+      "5095302940809114298 8312250184258072842 15770605014574863643 "
+      "14091690436120485477 15763282477731738396 16394237160547425954 "
+      "5066318118328746621 13140493775100916989 6371148952471982853 "
+      "15150289760867914983 4931341382074091848 12635920082410445322 "
+      "8498109357807439006 14836776625250834986"
+#else
+      "4640967165370225016 475580846709610041 992132072322742490 2879266425597847059 9859212410995834590 "
+      "13110412116137766394 2786888605138474240 12527462973726820895 5232191189150589434 8148228587345305383 "
+      "15985190208517110588 17155057006866468333 5040233137627218077 12716217438209898279 7628670991140263350 "
+      "9633431818170735083 8029874679197696152 2546188705250129595 13105544608186384869 6197138528956131705 "
+      "2500767142257833113 4057403796030124452 4801410225133580521 12712510024708253355 1478853541812684571 "
+      "16899998798183024166 763564441705181102 13204668179160915419 6975570844831914277 12029362619687152075 "
+      "17592155920476593974 1391138604394782251 13557582389860889375 6018426101890403504 7401881601334221625 "
+      "281124061034155254 2261645520472923927 10254468173793190351 4336071085409420785 7075410027066751424 "
+      "7438312290102887001 15139389032212795768 12949861808476919158 5221320262711473213 18444574859163569422 "
+      "3327484732560520915 10371833210785813703 494705948341138486 313693460050824934 801700386546917424 "
+      "3333797513889190239 11734932229126290067 4001689285737041461 17077402128101531244 17161815342609821634 "
+      "4393649844836338524 17495132932599391165 16521868517739667677 5661222119201994079 12972222767581865814 "
+      "11571421678172213790 390646047509318948 11495394588801409501 6525248087626393555 8573656613713326328 "
+      "8142990904973493114 13660896775289126601 17864485770152980871 17445884582967639919 5024202147275854142 "
+      "671342157422491897 8265009237858722295 8608118008319642631 9219145998498351877 4556268366257199356 "
+      "6140599343918232709 1607043993756685523 16576680458922020446 2464344868820317239 11814083511457927881 "
+      "17322648412109813175 1813592025330691328 11477642670415214162 17070997401037634101 2303972879274470462 "
+      "11243326672568121379 3445882696291328363 12839802107382893721 12104481080282265364 2596243342696322386 "
+      "17790946661248112185 16074938231864255959 2956506024687822818 5581797792339843057 16395798806003821452 "
+      "1430185758107085903 18323996060167045894 9286586749315539711 11359324205220105656 13078829860258641519 "
+      "5216000878301612113 9168569275644144847 4001438364296785628 2620979336382597902 17733020247211834335 "
+      "9313982434870058041 14715751745255862275 2010565534563004422 3178715725792479308 1903043159754162676 "
+      "14115336913931799326 2521027868575181436 16014439786100458068 12367605516710589904 209714080394415764 "
+      "1624794657804038156 9935768631398762231 4151749392286673312 7107972358174928160 5660032753352365076 "
+      "18036755596310855736 10390782681895899933 1046990263302077358 7362677844682408358 9462769113228112518 "
+      "16297726431682095630 320526314299713717 3778064933596859787 10885323884409002007 2236973268193149682 "
+      "10878852310456567407 2570883140239125692 2789272264811335056 13059628436710418572 2738898868861677604 "
+      "13893534347610447431 5822583231449316688 6492049503818767408 6244748132635487608 8670290879871155016 "
+      "9029174311949188652 14279734151305645897 15912641926116463991 5242647656726280598 18355120933288718928 "
+      "6020122330537384578 14237586109975592522 4056743947737351776 11111921204484673848 2182608451873446153 "
+      "166740030032355275 6707804438883761942 946217821621517312 17920751901125513000 11238439424546078979 "
+      "9206370960702334859 10367106562255294642 16885700982121403407 5166729472772734774 12763662184890206104 "
+      "5517776110990532992 14067054651980895669 91103470530233291 13189720418381102366 13302941329281487681 "
+      "10033391968256035918 5361508361658563078 9633125940775192275 4070378705292966499 2578880557354159711 "
+      "8213829012818869727 14609024865720281973 5764529456700294955 4441867238869537141 4877335405025028278 "
+      "3719605268964114254 6912476600417191412 12213543493427784533 9772185674504838078 849905513166852628 "
+      "2288907677236569364 13249989292558739703 17027351938809896108 621729935073174643 11783397793000418128 "
+      "6535353235698302382 17474699882318514854 11002055139008150918 18416122529605421577 6901480652243894157 "
+      "10382683051257874235 4029870255315506009 2965992838295436717 6235535328826140656 12567736520515051239 "
+      "1747508188587454664 6183192989050178616 15953675909489391368 10361870655666472156 13992646052760132872 "
+      "18278663149537015721 2341145555999432211 6926699433818498172 13807760559474580665 14452983890116009870 "
+      "11892982856082687091 17809698859762328768 2068107465420962246 7113569128111994893 4401076180719847553 "
+      "11644868941271347491 13124811241626639928 13749911628127226656 17349042196015114201 17667020131618919593 "
+      "5882998107995308535 3994906988813734883 12471577435602039626 6541516905696908495 12651354739990156236 "
+      "2656551093809687944 8349751746116271717 16003262134315245189 16966957802441281611 9846484220369869695 "
+      "289040869684008784 12984844645788078807 15184274406289320733 8585588724744493207 17783251231435661237 "
+      "8433051731677026430 4615759437146530251 7622401495291460413 11623553053949964326 5539054352599742054 "
+      "8346732818944267068 2539102865177431249 12328004780676882137 14682995638339538937 17618899063173726898 "
+      "3560029508967023860 203715062184427611 4331882803273086182 11747937041982829018 14988651560527263167 "
+      "12178986862382858651 3296725696957964696 13476561205033500550 5411341409364572097 6178165960265458773 "
+      "6637587084784255540 2852138145128301044 285104174921779935 3890210696591283091 15859573696692819698 "
+      "4183398296224968094 169433174359854381 11697218950892752694 13577878976046486116 9712904827015064754 "
+      "7617616830643388997 12751433537134599555 12319041734951133946 10239102328951141680 7290886622313676141 "
+      "4739372139955959505 9070795802053402609 10714415902002968511 14446814812660765483 8195026527484180858 "
+      "10331449076861906227 11969763657717422129 11061711610463627412 3682504726309317968 186477037840564317 "
+      "10307292328882458385 11683537113360307954 11300920371008006928 12769285684201107347 14545522978223964713 "
+      "5045546601922369224 202539708050129846 9727300421085682838 10660376569580918468 1852104613210280002 "
+      "2448572796841074793 4275113756623070536 15430856097668330204 8147758262819931807 13018070382604356536 "
+      "1461468238838418536 6307427532280072746 14454983576432818303 16577253122736704814 11165141699850119488 "
+      "1871342067236738412 15245016873857982253 16807633505182897684 18414686740244106738 14154332998791555826 "
+      "2798471212441347211 3624302277914305292 12175243894993809691 18197954905920110263 869336096513266351 "
+      "17322661882612443490 11198026121992866897 3762399852280847282 3393029619669184773 12618712086407186791 "
+      "11601652706361609238 1815215914966060855"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  std::mt19937_64 e1(0);
+  LIBCPP_ASSERT(to_string(e1) == a);
+  (void)a;
 }
 
 template <class E, typename Arg>
@@ -262,13 +396,12 @@ void test_implicit() {
 #endif
 }
 
-int main(int, char**)
-{
-    test1();
-    test2();
+int main(int, char**) {
+  test1();
+  test2();
 
-    test_implicit<std::mt19937, std::uint_fast32_t>();
-    test_implicit<std::mt19937_64, std::uint_fast64_t>();
+  test_implicit<std::mt19937, std::uint_fast32_t>();
+  test_implicit<std::mt19937_64, std::uint_fast64_t>();
 
-    return 0;
+  return 0;
 }
diff --git a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
index ff028929747a6..24832330720e4 100644
--- a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
@@ -24,292 +24,425 @@
 
 #include "test_macros.h"
 
-void
-test1()
-{
-    const char* a = "358595400 4166558815 2016177162 3414480257 "
-    "4027494649 3722317195 1190532340 3212207148 "
-    "3537847251 389019999 1098708832 3277907415 "
-    "1946784350 3608286140 2091419822 2227407035 "
-    "2229110723 1825348377 1276269279 314337202 "
-    "3182935337 1313150029 3118776508 3707918501 "
-    "1900972958 4054820954 3973178248 906260237 "
-    "1403942218 3139931556 2807126524 3940936448 "
-    "1316345796 631296613 2268418920 2914000794 "
-    "3760588399 3226216036 880155129 4183611084 "
-    "211541083 3755352858 1331383234 3036493096 "
-    "937478630 2092170412 777784402 93392729 "
-    "3644029210 1681392086 2427001226 3143870332 "
-    "3703581502 2017505388 1706274541 1049329728 "
-    "2452031492 3437261233 2581204087 1700889875 "
-    "1652573881 2127047692 3778506964 1960741508 "
-    "2739602360 3395905609 2123355622 3041272975 "
-    "784200748 3558951522 1002787860 4063320888 "
-    "1587315560 4042698976 659183308 3082256417 "
-    "2808969567 2361418535 3468698782 750700970 "
-    "2991209851 3581521382 962022878 2518967363 "
-    "1476525873 3865977235 2128790058 2380326689 "
-    "1396773405 312559410 1370621899 1154499924 "
-    "2963101919 2182689761 2071851902 1661288848 "
-    "2411351341 1362764020 1289894483 1951662807 "
-    "701821506 552267185 2356648449 3949188503 "
-    "1748307081 87795201 3718396254 4112205936 "
-    "2819888864 73923733 2800033151 839258139 "
-    "3801779069 3105962436 2111266436 1772784466 "
-    "3692264298 4148810953 3147390749 3537518553 "
-    "1695044978 1430225842 1252346204 3465285434 "
-    "3970017763 2920658411 2805151132 290569815 "
-    "3802301355 1493420394 1943029276 1667143611 "
-    "1049665988 1710824905 220168517 3997946231 "
-    "1014582791 4244598752 1147604069 2533886627 "
-    "598679964 761521020 431779255 3745982038 "
-    "768658283 3598262505 1765664789 279538641 "
-    "715144305 2371628432 2655860083 1759010423 "
-    "3568452003 1910107098 2801429529 3924547532 "
-    "3862672436 3933725740 1764550618 130893617 "
-    "1460692387 4135312761 2075529299 2880227367 "
-    "944557368 4166665482 2627749235 3732013815 "
-    "1595900818 1553312393 3529311831 3531462424 "
-    "2431328342 4075369692 1609967709 3704537555 "
-    "2067297464 397140475 920618678 2840795964 "
-    "4202512837 1286017648 7035910 1057207826 "
-    "2325188262 191593698 3697383848 3029712831 "
-    "2073681914 163454681 1329637200 290077398 "
-    "287239431 4205081522 1233889024 167173087 "
-    "3267660257 3406068803 2382354609 1680046927 "
-    "125183503 3559536309 3208900974 2912148541 "
-    "2882879316 1937001086 2919729069 892928802 "
-    "4141691387 2507406586 855548593 3418647837 "
-    "4035646154 2410275591 248715645 3180757482 "
-    "1880770722 362912336 2964920095 2319904154 "
-    "1493655850 4240733030 1834485846 1696040454 "
-    "3329457927 1865824694 847759208 1587231623 "
-    "3757294772 1161601118 3630323833 3007722125 "
-    "3726418007 2124238171 1205345 172659797 "
-    "3040354211 885213338 1857049013 447922412 "
-    "719906299 1370059380 1922204800 3960090489 "
-    "1658822644 1529626863 1565927273 3537718771 "
-    "2733237258 2180221377 921910745 2144937687 "
-    "1727603895 1315635304 4023867791 2401834107 "
-    "808854185 2408824497 343935326 185237544 "
-    "746732759 2641236122 4283215329 743609415 "
-    "1134726665 3892851319 1302851263 3473445597 "
-    "1326817414 2702766508 1943179285 4025685468 "
-    "932896770 199392138 2787362875 3450501893 "
-    "3351567147 2461286528 2227605848 2993751114 "
-    "3988215720 1320573368 2866560199 4153194990 "
-    "3007120042 3260751955 3171763740 2111121243 "
-    "3962825228 102681859 3368179132 802089147 "
-    "4029273561 424939445 4178414761 2592125109 "
-    "1960801088 2967746406 310701990 2364200202 "
-    "1320507009 3474372480 784693947 2952246664 "
-    "1891935330 2048385105 3530082191 3238151038 "
-    "3293189141 1316053288 2087004409 740799958 "
-    "1187748554 3607767334 1190185990 1408429481 "
-    "657134359 221834425 3907725865 1068016389 "
-    "1402423875 2598612116 2046886300 2345022518 "
-    "1196081924 357783981 4013683598 463491626 "
-    "3269206482 3332444286 886491955 2257342866 "
-    "475911113 833576299 2893727564 2866985145 "
-    "1413365115 2995166393 1486060436 161205225 "
-    "3181728373 3056027137 2040371876 2182305146 "
-    "3028448628 2214316977 1266227021 876938740 "
-    "276477469 752158077 2182179045 1381698878 "
-    "3424557652 666674427 968327842 2534296575 "
-    "265105940 961112540 2641188117 2319139814 "
-    "1750453329 3450138343 678025317 1477566458 "
-    "3773796420 2933993832 3326042905 4084805260 "
-    "444182455 255333481 785163068 2321290820 "
-    "2893603234 3005520266 541104079 1383277090 "
-    "2770755666 3764627833 583371929 2864949033 "
-    "1487681116 1811788361 240329486 3094213377 "
-    "958509875 2564379085 1636995945 2070894127 "
-    "2139004232 1747850055 3841512327 3325011872 "
-    "1161622604 639182193 3533652535 4022667522 "
-    "761048999 3337743670 254221568 2784956233 "
-    "2990252814 4207922787 275707208 261819597 "
-    "2071467265 4034945770 1999813410 3038921100 "
-    "2200194573 1328217451 2440612380 3862293692 "
-    "2733976812 2750523058 2920082515 3809044908 "
-    "4285231753 3131963297 3481602724 1396460739 "
-    "2011767965 2481047764 2958452120 3044188618 "
-    "2217236658 3448605566 757716104 1818279145 "
-    "2641228144 1312649639 1194087684 3845229729 "
-    "1747658356 874418803 1956637337 268670179 "
-    "2083040240 2577671381 3375334655 2587828868 "
-    "1383012799 3583445685 2594576715 3282337104 "
-    "4257972751 3440488071 3129180313 1830891395 "
-    "1594931092 2680778339 3984026324 1102770400 "
-    "2315820258 1263467048 1133254110 2400676748 "
-    "2251795328 1036154092 3313541051 2277356560 "
-    "1477696003 1417117088 3968537402 1404882202 "
-    "2011058180 4114080985 1727459502 4100235708 "
-    "2334509310 2829432554 377936301 1519324520 "
-    "3252826644 1193335837 1929125820 2165344238 "
-    "4160556243 223340988 670907625 1485396519 "
-    "936389509 3813712964 2706450987 3132506320 "
-    "875886515 557088991 2854916639 2955496008 "
-    "2881696287 265169077 3239923698 3649366121 "
-    "4072165960 1233904959 225406526 1767368993 "
-    "1894882500 2296582180 339255168 83200939 "
-    "2958376148 4100205346 1991250823 3806183082 "
-    "2691709980 2642354997 3024056146 1681065839 "
-    "3438299684 1638853652 362567001 2307868899 "
-    "988801086 1342833399 2303298376 1500039911 "
-    "765489391 4080464497 4155444368 980472018 "
-    "2026981853 3460406995 391970367 667377014 "
-    "4177754853 2657468948 3560690175 3030464357 "
-    "2948380657 1208800977 2316451404 4001932203 "
-    "1977856863 4265848271 3116200050 3037586215 "
-    "1335232764 930230766 1026089249 2482219415 "
-    "2613853154 1854543497 2909555107 3862874043 "
-    "2609355500 907364682 383900687 358164223 "
-    "232347546 2536276737 3118482806 1254103998 "
-    "2357334077 1204777304 1996643329 4046232717 "
-    "2570520290 3173323380 1201411457 2361883023 "
-    "806087062 2984143714 2355127569 864220085 "
-    "1787696713 1182652984 4200065581 100722519 "
-    "2380458669 2429592313 2618338302 1236529564 "
-    "1747130196 3711661325 1114068102 510380433 "
-    "93703089 2277774664 3220741441 1577998569 "
-    "2816701900 4206763045 2495239107 4080390459 "
-    "1307072677 20360728 1468385549 96049834 "
-    "3630657447 2809517346 3396111678 3043831060 "
-    "673178359 4256729562 1755211210 1969834535 "
-    "498315110 3717726302 1544859987 2239930949 "
-    "1595372585 294525219 3961637067 3591840665 "
-    "3324896933 2300077772 721255886 4197934760 "
-    "1468866696 2184812884 628246683 3385113037 "
-    "3041166140 3948531843 1176600829 228286131 "
-    "2447397608 712235937 3332826819 2676980703 "
-    "4019468871 1952389952 1202638254 3625447051";
-    unsigned as[] = {3, 5, 7};
-    std::seed_seq sseq(as, as+3);
-    std::mt19937 e1(sseq);
-    std::ostringstream os;
-    os << e1;
-    assert(os.str() == a);
+void test1() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "358595400 4166558815 2016177162 3414480257 "
+      "4027494649 3722317195 1190532340 3212207148 "
+      "3537847251 389019999 1098708832 3277907415 "
+      "1946784350 3608286140 2091419822 2227407035 "
+      "2229110723 1825348377 1276269279 314337202 "
+      "3182935337 1313150029 3118776508 3707918501 "
+      "1900972958 4054820954 3973178248 906260237 "
+      "1403942218 3139931556 2807126524 3940936448 "
+      "1316345796 631296613 2268418920 2914000794 "
+      "3760588399 3226216036 880155129 4183611084 "
+      "211541083 3755352858 1331383234 3036493096 "
+      "937478630 2092170412 777784402 93392729 "
+      "3644029210 1681392086 2427001226 3143870332 "
+      "3703581502 2017505388 1706274541 1049329728 "
+      "2452031492 3437261233 2581204087 1700889875 "
+      "1652573881 2127047692 3778506964 1960741508 "
+      "2739602360 3395905609 2123355622 3041272975 "
+      "784200748 3558951522 1002787860 4063320888 "
+      "1587315560 4042698976 659183308 3082256417 "
+      "2808969567 2361418535 3468698782 750700970 "
+      "2991209851 3581521382 962022878 2518967363 "
+      "1476525873 3865977235 2128790058 2380326689 "
+      "1396773405 312559410 1370621899 1154499924 "
+      "2963101919 2182689761 2071851902 1661288848 "
+      "2411351341 1362764020 1289894483 1951662807 "
+      "701821506 552267185 2356648449 3949188503 "
+      "1748307081 87795201 3718396254 4112205936 "
+      "2819888864 73923733 2800033151 839258139 "
+      "3801779069 3105962436 2111266436 1772784466 "
+      "3692264298 4148810953 3147390749 3537518553 "
+      "1695044978 1430225842 1252346204 3465285434 "
+      "3970017763 2920658411 2805151132 290569815 "
+      "3802301355 1493420394 1943029276 1667143611 "
+      "1049665988 1710824905 220168517 3997946231 "
+      "1014582791 4244598752 1147604069 2533886627 "
+      "598679964 761521020 431779255 3745982038 "
+      "768658283 3598262505 1765664789 279538641 "
+      "715144305 2371628432 2655860083 1759010423 "
+      "3568452003 1910107098 2801429529 3924547532 "
+      "3862672436 3933725740 1764550618 130893617 "
+      "1460692387 4135312761 2075529299 2880227367 "
+      "944557368 4166665482 2627749235 3732013815 "
+      "1595900818 1553312393 3529311831 3531462424 "
+      "2431328342 4075369692 1609967709 3704537555 "
+      "2067297464 397140475 920618678 2840795964 "
+      "4202512837 1286017648 7035910 1057207826 "
+      "2325188262 191593698 3697383848 3029712831 "
+      "2073681914 163454681 1329637200 290077398 "
+      "287239431 4205081522 1233889024 167173087 "
+      "3267660257 3406068803 2382354609 1680046927 "
+      "125183503 3559536309 3208900974 2912148541 "
+      "2882879316 1937001086 2919729069 892928802 "
+      "4141691387 2507406586 855548593 3418647837 "
+      "4035646154 2410275591 248715645 3180757482 "
+      "1880770722 362912336 2964920095 2319904154 "
+      "1493655850 4240733030 1834485846 1696040454 "
+      "3329457927 1865824694 847759208 1587231623 "
+      "3757294772 1161601118 3630323833 3007722125 "
+      "3726418007 2124238171 1205345 172659797 "
+      "3040354211 885213338 1857049013 447922412 "
+      "719906299 1370059380 1922204800 3960090489 "
+      "1658822644 1529626863 1565927273 3537718771 "
+      "2733237258 2180221377 921910745 2144937687 "
+      "1727603895 1315635304 4023867791 2401834107 "
+      "808854185 2408824497 343935326 185237544 "
+      "746732759 2641236122 4283215329 743609415 "
+      "1134726665 3892851319 1302851263 3473445597 "
+      "1326817414 2702766508 1943179285 4025685468 "
+      "932896770 199392138 2787362875 3450501893 "
+      "3351567147 2461286528 2227605848 2993751114 "
+      "3988215720 1320573368 2866560199 4153194990 "
+      "3007120042 3260751955 3171763740 2111121243 "
+      "3962825228 102681859 3368179132 802089147 "
+      "4029273561 424939445 4178414761 2592125109 "
+      "1960801088 2967746406 310701990 2364200202 "
+      "1320507009 3474372480 784693947 2952246664 "
+      "1891935330 2048385105 3530082191 3238151038 "
+      "3293189141 1316053288 2087004409 740799958 "
+      "1187748554 3607767334 1190185990 1408429481 "
+      "657134359 221834425 3907725865 1068016389 "
+      "1402423875 2598612116 2046886300 2345022518 "
+      "1196081924 357783981 4013683598 463491626 "
+      "3269206482 3332444286 886491955 2257342866 "
+      "475911113 833576299 2893727564 2866985145 "
+      "1413365115 2995166393 1486060436 161205225 "
+      "3181728373 3056027137 2040371876 2182305146 "
+      "3028448628 2214316977 1266227021 876938740 "
+      "276477469 752158077 2182179045 1381698878 "
+      "3424557652 666674427 968327842 2534296575 "
+      "265105940 961112540 2641188117 2319139814 "
+      "1750453329 3450138343 678025317 1477566458 "
+      "3773796420 2933993832 3326042905 4084805260 "
+      "444182455 255333481 785163068 2321290820 "
+      "2893603234 3005520266 541104079 1383277090 "
+      "2770755666 3764627833 583371929 2864949033 "
+      "1487681116 1811788361 240329486 3094213377 "
+      "958509875 2564379085 1636995945 2070894127 "
+      "2139004232 1747850055 3841512327 3325011872 "
+      "1161622604 639182193 3533652535 4022667522 "
+      "761048999 3337743670 254221568 2784956233 "
+      "2990252814 4207922787 275707208 261819597 "
+      "2071467265 4034945770 1999813410 3038921100 "
+      "2200194573 1328217451 2440612380 3862293692 "
+      "2733976812 2750523058 2920082515 3809044908 "
+      "4285231753 3131963297 3481602724 1396460739 "
+      "2011767965 2481047764 2958452120 3044188618 "
+      "2217236658 3448605566 757716104 1818279145 "
+      "2641228144 1312649639 1194087684 3845229729 "
+      "1747658356 874418803 1956637337 268670179 "
+      "2083040240 2577671381 3375334655 2587828868 "
+      "1383012799 3583445685 2594576715 3282337104 "
+      "4257972751 3440488071 3129180313 1830891395 "
+      "1594931092 2680778339 3984026324 1102770400 "
+      "2315820258 1263467048 1133254110 2400676748 "
+      "2251795328 1036154092 3313541051 2277356560 "
+      "1477696003 1417117088 3968537402 1404882202 "
+      "2011058180 4114080985 1727459502 4100235708 "
+      "2334509310 2829432554 377936301 1519324520 "
+      "3252826644 1193335837 1929125820 2165344238 "
+      "4160556243 223340988 670907625 1485396519 "
+      "936389509 3813712964 2706450987 3132506320 "
+      "875886515 557088991 2854916639 2955496008 "
+      "2881696287 265169077 3239923698 3649366121 "
+      "4072165960 1233904959 225406526 1767368993 "
+      "1894882500 2296582180 339255168 83200939 "
+      "2958376148 4100205346 1991250823 3806183082 "
+      "2691709980 2642354997 3024056146 1681065839 "
+      "3438299684 1638853652 362567001 2307868899 "
+      "988801086 1342833399 2303298376 1500039911 "
+      "765489391 4080464497 4155444368 980472018 "
+      "2026981853 3460406995 391970367 667377014 "
+      "4177754853 2657468948 3560690175 3030464357 "
+      "2948380657 1208800977 2316451404 4001932203 "
+      "1977856863 4265848271 3116200050 3037586215 "
+      "1335232764 930230766 1026089249 2482219415 "
+      "2613853154 1854543497 2909555107 3862874043 "
+      "2609355500 907364682 383900687 358164223 "
+      "232347546 2536276737 3118482806 1254103998 "
+      "2357334077 1204777304 1996643329 4046232717 "
+      "2570520290 3173323380 1201411457 2361883023 "
+      "806087062 2984143714 2355127569 864220085 "
+      "1787696713 1182652984 4200065581 100722519 "
+      "2380458669 2429592313 2618338302 1236529564 "
+      "1747130196 3711661325 1114068102 510380433 "
+      "93703089 2277774664 3220741441 1577998569 "
+      "2816701900 4206763045 2495239107 4080390459 "
+      "1307072677 20360728 1468385549 96049834 "
+      "3630657447 2809517346 3396111678 3043831060 "
+      "673178359 4256729562 1755211210 1969834535 "
+      "498315110 3717726302 1544859987 2239930949 "
+      "1595372585 294525219 3961637067 3591840665 "
+      "3324896933 2300077772 721255886 4197934760 "
+      "1468866696 2184812884 628246683 3385113037 "
+      "3041166140 3948531843 1176600829 228286131 "
+      "2447397608 712235937 3332826819 2676980703 "
+      "4019468871 1952389952 1202638254 3625447051"
+#else
+      "358595400 4166558815 2016177162 3414480257 4027494649 3722317195 1190532340 3212207148 3537847251 389019999 "
+      "1098708832 3277907415 1946784350 3608286140 2091419822 2227407035 2229110723 1825348377 1276269279 314337202 "
+      "3182935337 1313150029 3118776508 3707918501 1900972958 4054820954 3973178248 906260237 1403942218 3139931556 "
+      "2807126524 3940936448 1316345796 631296613 2268418920 2914000794 3760588399 3226216036 880155129 4183611084 "
+      "211541083 3755352858 1331383234 3036493096 937478630 2092170412 777784402 93392729 3644029210 1681392086 "
+      "2427001226 3143870332 3703581502 2017505388 1706274541 1049329728 2452031492 3437261233 2581204087 1700889875 "
+      "1652573881 2127047692 3778506964 1960741508 2739602360 3395905609 2123355622 3041272975 784200748 3558951522 "
+      "1002787860 4063320888 1587315560 4042698976 659183308 3082256417 2808969567 2361418535 3468698782 750700970 "
+      "2991209851 3581521382 962022878 2518967363 1476525873 3865977235 2128790058 2380326689 1396773405 312559410 "
+      "1370621899 1154499924 2963101919 2182689761 2071851902 1661288848 2411351341 1362764020 1289894483 1951662807 "
+      "701821506 552267185 2356648449 3949188503 1748307081 87795201 3718396254 4112205936 2819888864 73923733 "
+      "2800033151 839258139 3801779069 3105962436 2111266436 1772784466 3692264298 4148810953 3147390749 3537518553 "
+      "1695044978 1430225842 1252346204 3465285434 3970017763 2920658411 2805151132 290569815 3802301355 1493420394 "
+      "1943029276 1667143611 1049665988 1710824905 220168517 3997946231 1014582791 4244598752 1147604069 2533886627 "
+      "598679964 761521020 431779255 3745982038 768658283 3598262505 1765664789 279538641 715144305 2371628432 "
+      "2655860083 1759010423 3568452003 1910107098 2801429529 3924547532 3862672436 3933725740 1764550618 130893617 "
+      "1460692387 4135312761 2075529299 2880227367 944557368 4166665482 2627749235 3732013815 1595900818 1553312393 "
+      "3529311831 3531462424 2431328342 4075369692 1609967709 3704537555 2067297464 397140475 920618678 2840795964 "
+      "4202512837 1286017648 7035910 1057207826 2325188262 191593698 3697383848 3029712831 2073681914 163454681 "
+      "1329637200 290077398 287239431 4205081522 1233889024 167173087 3267660257 3406068803 2382354609 1680046927 "
+      "125183503 3559536309 3208900974 2912148541 2882879316 1937001086 2919729069 892928802 4141691387 2507406586 "
+      "855548593 3418647837 4035646154 2410275591 248715645 3180757482 1880770722 362912336 2964920095 2319904154 "
+      "1493655850 4240733030 1834485846 1696040454 3329457927 1865824694 847759208 1587231623 3757294772 1161601118 "
+      "3630323833 3007722125 3726418007 2124238171 1205345 172659797 3040354211 885213338 1857049013 447922412 "
+      "719906299 1370059380 1922204800 3960090489 1658822644 1529626863 1565927273 3537718771 2733237258 2180221377 "
+      "921910745 2144937687 1727603895 1315635304 4023867791 2401834107 808854185 2408824497 343935326 185237544 "
+      "746732759 2641236122 4283215329 743609415 1134726665 3892851319 1302851263 3473445597 1326817414 2702766508 "
+      "1943179285 4025685468 932896770 199392138 2787362875 3450501893 3351567147 2461286528 2227605848 2993751114 "
+      "3988215720 1320573368 2866560199 4153194990 3007120042 3260751955 3171763740 2111121243 3962825228 102681859 "
+      "3368179132 802089147 4029273561 424939445 4178414761 2592125109 1960801088 2967746406 310701990 2364200202 "
+      "1320507009 3474372480 784693947 2952246664 1891935330 2048385105 3530082191 3238151038 3293189141 1316053288 "
+      "2087004409 740799958 1187748554 3607767334 1190185990 1408429481 657134359 221834425 3907725865 1068016389 "
+      "1402423875 2598612116 2046886300 2345022518 1196081924 357783981 4013683598 463491626 3269206482 3332444286 "
+      "886491955 2257342866 475911113 833576299 2893727564 2866985145 1413365115 2995166393 1486060436 161205225 "
+      "3181728373 3056027137 2040371876 2182305146 3028448628 2214316977 1266227021 876938740 276477469 752158077 "
+      "2182179045 1381698878 3424557652 666674427 968327842 2534296575 265105940 961112540 2641188117 2319139814 "
+      "1750453329 3450138343 678025317 1477566458 3773796420 2933993832 3326042905 4084805260 444182455 255333481 "
+      "785163068 2321290820 2893603234 3005520266 541104079 1383277090 2770755666 3764627833 583371929 2864949033 "
+      "1487681116 1811788361 240329486 3094213377 958509875 2564379085 1636995945 2070894127 2139004232 1747850055 "
+      "3841512327 3325011872 1161622604 639182193 3533652535 4022667522 761048999 3337743670 254221568 2784956233 "
+      "2990252814 4207922787 275707208 261819597 2071467265 4034945770 1999813410 3038921100 2200194573 1328217451 "
+      "2440612380 3862293692 2733976812 2750523058 2920082515 3809044908 4285231753 3131963297 3481602724 1396460739 "
+      "2011767965 2481047764 2958452120 3044188618 2217236658 3448605566 757716104 1818279145 2641228144 1312649639 "
+      "1194087684 3845229729 1747658356 874418803 1956637337 268670179 2083040240 2577671381 3375334655 2587828868 "
+      "1383012799 3583445685 2594576715 3282337104 4257972751 3440488071 3129180313 1830891395 1594931092 2680778339 "
+      "3984026324 1102770400 2315820258 1263467048 1133254110 2400676748 2251795328 1036154092 3313541051 2277356560 "
+      "1477696003 1417117088 3968537402 1404882202 2011058180 4114080985 1727459502 4100235708 2334509310 2829432554 "
+      "377936301 1519324520 3252826644 1193335837 1929125820 2165344238 4160556243 223340988 670907625 1485396519 "
+      "936389509 3813712964 2706450987 3132506320 875886515 557088991 2854916639 2955496008 2881696287 265169077 "
+      "3239923698 3649366121 4072165960 1233904959 225406526 1767368993 1894882500 2296582180 339255168 83200939 "
+      "2958376148 4100205346 1991250823 3806183082 2691709980 2642354997 3024056146 1681065839 3438299684 1638853652 "
+      "362567001 2307868899 988801086 1342833399 2303298376 1500039911 765489391 4080464497 4155444368 980472018 "
+      "2026981853 3460406995 391970367 667377014 4177754853 2657468948 3560690175 3030464357 2948380657 1208800977 "
+      "2316451404 4001932203 1977856863 4265848271 3116200050 3037586215 1335232764 930230766 1026089249 2482219415 "
+      "2613853154 1854543497 2909555107 3862874043 2609355500 907364682 383900687 358164223 232347546 2536276737 "
+      "3118482806 1254103998 2357334077 1204777304 1996643329 4046232717 2570520290 3173323380 1201411457 2361883023 "
+      "806087062 2984143714 2355127569 864220085 1787696713 1182652984 4200065581 100722519 2380458669 2429592313 "
+      "2618338302 1236529564 1747130196 3711661325 1114068102 510380433 93703089 2277774664 3220741441 1577998569 "
+      "2816701900 4206763045 2495239107 4080390459 1307072677 20360728 1468385549 96049834 3630657447 2809517346 "
+      "3396111678 3043831060 673178359 4256729562 1755211210 1969834535 498315110 3717726302 1544859987 2239930949 "
+      "1595372585 294525219 3961637067 3591840665 3324896933 2300077772 721255886 4197934760 1468866696 2184812884 "
+      "628246683 3385113037 3041166140 3948531843 1176600829 228286131 2447397608 712235937 3332826819 2676980703 "
+      "4019468871 1952389952 1202638254 3625447051"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  unsigned as[] = {3, 5, 7};
+  std::seed_seq sseq(as, as + 3);
+  std::mt19937 e1(sseq);
+  std::ostringstream os;
+  os << e1;
+  LIBCPP_ASSERT(os.str() == a);
+  (void)a;
 }
 
-void
-test2()
-{
-    const char* a = "17895233847644109640 14665081038668852234 15987230621890949369 "
-    "13796324649827964148 1670828176732799955 14078505147839608672 "
-    "15497470967856861790 9566640372296747182 7839811585250789315 "
-    "1350068003782415071 5639936432479386921 15925388701147119804 "
-    "17415323390466493342 3892358083553387400 13485903346102334794 "
-    "16926193162581531132 2711398308226914244 12515538113016451944 "
-    "13856492368211347055 17968472785843263993 16129117710261673051 "
-    "13041638543181171650 8985803498136324582 401118717516975186 "
-    "7221524024767248666 13502820261231663498 8665119664667372350 "
-    "4506836866186850029 14762924585995667460 7305266389803732087 "
-    "9135600275824854713 8421320656548229332 14585303533697565624 "
-    "13062167967956981222 15285580395823625260 17451830328116466708 "
-    "17363259891080004456 13238190509560321740 10142215382802200927 "
-    "3224236118694175902 15382517208605932923 10818882444738383326 "
-    "16604245792882032433 10223425285179753002 1342432445403828765 "
-    "4958539418185107403 9374581143772158175 7135181273441366910 "
-    "5853026900476841261 8382327930174454355 2371969498930803266 "
-    "16961635468480846337 377077518789053577 17661790013255465310 "
-    "317500018453124832 3604586262706855295 13340007089026272125 "
-    "7614051306438090372 17819007364113857386 15193526497275633437 "
-    "6142773218979108210 14883287611587512668 12544132362002344419 "
-    "1247987855434921372 6414191755211735979 7160327288923375132 "
-    "7347937017206972868 17171048313531629893 18230412825496997383 "
-    "10882960195884354661 3270707876715241884 16088870345045208503 "
-    "15454419782166694763 1200609322828949525 10186066554418904177 "
-    "7554892242763986291 8203847521335919011 16855803304338943001 "
-    "16895223408596071476 562183806034700250 17761033068687156643 "
-    "12370482348384718931 17895691979506634040 16028877286272943475 "
-    "6671425930002400146 15167515621662197335 17503579548680921174 "
-    "15910867647138768989 1705705354110203064 12201125760909412022 "
-    "5523403744441352645 4540673037752294406 822888669354888870 "
-    "13012517529113958824 702032511346794490 1245872939048413008 "
-    "18060687614291143943 718002942670251776 14628954120078526945 "
-    "7215746609592654001 15288092036204733967 12507582747898016110 "
-    "8319356319569362772 3835100005166188461 10769229288786702843 "
-    "14682980657311687345 10352054841727718090 13661249361946024317 "
-    "1558696616315734178 9963912474249467679 18213809676410642730 "
-    "7284438284457478230 8013656044128665351 6817107912809760616 "
-    "4989038816564331700 12918068165960947833 9123533477086273623 "
-    "741568181450204257 3801962339733348259 1923812112542486965 "
-    "5884360231397942779 17008459141377852544 6569697353326895092 "
-    "15194386425456240489 9363979514988323850 9212437218544795097 "
-    "5650610605870621879 10315798944006232463 10345822437227504297 "
-    "795589193815296350 11344022765750598871 3193778122705907169 "
-    "16719669104430190089 14918335244853046975 11608293761910939782 "
-    "17290187430985633813 856382712722415618 14819792788008454203 "
-    "10571145147196955435 12858063129221173592 5671819431516788648 "
-    "17837836658827607239 14004823010100183722 9067196699747632668 "
-    "441015230260308492 3444946658209715644 1825101023084664281 "
-    "11133092574473850025 12746373758552339264 10154162549097295782 "
-    "14922316177042921089 12679802872389794491 8797747037480461410 "
-    "13907752811248535439 5652405835046458389 3181711594575177977 "
-    "15495242712294857418 6049158560807439366 952771601159099159 "
-    "4587095466254740009 11160954054611782211 10071795025240457628 "
-    "1536670498623767300 1990681379653546894 14312739227381277138 "
-    "9695213786215402291 3580182943401628617 12313607438786545484 "
-    "12864141705426648443 692371170805382036 13125536612285239925 "
-    "9372929234002877092 9510419002221032820 3766423210161674061 "
-    "3230494342413727261 5934351496112072933 2863344864469097044 "
-    "10884720908958139042 4127946927340597780 9960629658622711061 "
-    "14818231351611083857 6346099615454582885 12601407558879514692 "
-    "17544105005554819865 1096648950913019831 9969868157190185788 "
-    "12908611252828823970 5941129863397152719 16168953427117105234 "
-    "12304862402025196697 7781571759256122972 13289545261301048078 "
-    "11013924305579914035 8894422550580466537 7506958826675805512 "
-    "14280817252893250439 2745266616282182732 17277225453205013047 "
-    "14335499905842065319 11961295941780577536 18072890757248426766 "
-    "1124506606842606920 17329960125355005185 13052066741624159010 "
-    "5704650516221677069 16588425097127709212 11813406583737887980 "
-    "16359723311775411283 13451679937172566665 5997753207634594468 "
-    "10656019008205694109 13074690560123889048 14811648124990806194 "
-    "7809449463531558024 5637787273252434288 16515135932856030468 "
-    "3755600163640125044 1153929634172103321 11071014283313196016 "
-    "11114640359080035583 15390782025450330559 14097530518721927499 "
-    "14776783751481098767 7863618667181998233 11513855295425132436 "
-    "4736362806980864724 5426549653049482466 10310828122060887518 "
-    "4450247941008370560 9781171949844602811 6086471549040450051 "
-    "6033923116291003194 17669843285681524740 17610378273478865070 "
-    "12152320288002263294 6525449125788834221 5125338396312613396 "
-    "9300082688721166268 959242243476884691 6379729471368150249 "
-    "16379772457647614853 13454012201619761707 2392678998182524851 "
-    "12693758700673471007 1138892516507202079 15673908144065302514 "
-    "5299581449349386824 7590792025124859454 9863745357571267780 "
-    "357345312340746112 17610247870912740564 16347431861769737095 "
-    "11348828299228888092 7220122803951857490 7038822841708464676 "
-    "9912221445023094105 5767425533670320190 6442622362743049032 "
-    "17525461567869579503 4211095256108567696 14862334876401617373 "
-    "2866362449624104511 11413742225973279461 13015745308569358847 "
-    "5191760666536228849 17188167935010684492 18321678815621002079 "
-    "13046333455321624690 3995310719038261500 10661051209947341089 "
-    "7965203671238327266 16590917686161852835 3897101637344795372 "
-    "1538303624766151695 10893225639252940698 5386335660311332214 "
-    "5174479122000384061 17378437193516866561 13629320139302700770 "
-    "10144210341964027265 12816799659000064406 3711797003976467729 "
-    "5079455890584507977 432599929275804205 10435019529328454317 "
-    "5310854040535477246 15941464006450157396 2192067269367387270 "
-    "9782967689631091633 6777452250210540865 18067909703113078220 "
-    "17525143578810667971 87448662189824165 412530897284614413 "
-    "12066785122245373863 13073154860645125438 18282514257379582711 "
-    "8460374908111578570 15967512883067334502 9620430172798103891 "
-    "1264976185047610409 15426838192579528907 9878758812321441445 "
-    "18029992505662864846 9383699886128308360 14538949787806484635 "
-    "16958815135940772668 980481467951972605 3059030058898313960 "
-    "11497544574740915907 8385450996898478663 15571176518627282350";
-    unsigned as[] = {3, 5, 7};
-    std::seed_seq sseq(as, as+3);
-    std::mt19937_64 e1(sseq);
-    std::ostringstream os;
-    os << e1;
-    assert(os.str() == a);
+void test2() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "17895233847644109640 14665081038668852234 15987230621890949369 "
+      "13796324649827964148 1670828176732799955 14078505147839608672 "
+      "15497470967856861790 9566640372296747182 7839811585250789315 "
+      "1350068003782415071 5639936432479386921 15925388701147119804 "
+      "17415323390466493342 3892358083553387400 13485903346102334794 "
+      "16926193162581531132 2711398308226914244 12515538113016451944 "
+      "13856492368211347055 17968472785843263993 16129117710261673051 "
+      "13041638543181171650 8985803498136324582 401118717516975186 "
+      "7221524024767248666 13502820261231663498 8665119664667372350 "
+      "4506836866186850029 14762924585995667460 7305266389803732087 "
+      "9135600275824854713 8421320656548229332 14585303533697565624 "
+      "13062167967956981222 15285580395823625260 17451830328116466708 "
+      "17363259891080004456 13238190509560321740 10142215382802200927 "
+      "3224236118694175902 15382517208605932923 10818882444738383326 "
+      "16604245792882032433 10223425285179753002 1342432445403828765 "
+      "4958539418185107403 9374581143772158175 7135181273441366910 "
+      "5853026900476841261 8382327930174454355 2371969498930803266 "
+      "16961635468480846337 377077518789053577 17661790013255465310 "
+      "317500018453124832 3604586262706855295 13340007089026272125 "
+      "7614051306438090372 17819007364113857386 15193526497275633437 "
+      "6142773218979108210 14883287611587512668 12544132362002344419 "
+      "1247987855434921372 6414191755211735979 7160327288923375132 "
+      "7347937017206972868 17171048313531629893 18230412825496997383 "
+      "10882960195884354661 3270707876715241884 16088870345045208503 "
+      "15454419782166694763 1200609322828949525 10186066554418904177 "
+      "7554892242763986291 8203847521335919011 16855803304338943001 "
+      "16895223408596071476 562183806034700250 17761033068687156643 "
+      "12370482348384718931 17895691979506634040 16028877286272943475 "
+      "6671425930002400146 15167515621662197335 17503579548680921174 "
+      "15910867647138768989 1705705354110203064 12201125760909412022 "
+      "5523403744441352645 4540673037752294406 822888669354888870 "
+      "13012517529113958824 702032511346794490 1245872939048413008 "
+      "18060687614291143943 718002942670251776 14628954120078526945 "
+      "7215746609592654001 15288092036204733967 12507582747898016110 "
+      "8319356319569362772 3835100005166188461 10769229288786702843 "
+      "14682980657311687345 10352054841727718090 13661249361946024317 "
+      "1558696616315734178 9963912474249467679 18213809676410642730 "
+      "7284438284457478230 8013656044128665351 6817107912809760616 "
+      "4989038816564331700 12918068165960947833 9123533477086273623 "
+      "741568181450204257 3801962339733348259 1923812112542486965 "
+      "5884360231397942779 17008459141377852544 6569697353326895092 "
+      "15194386425456240489 9363979514988323850 9212437218544795097 "
+      "5650610605870621879 10315798944006232463 10345822437227504297 "
+      "795589193815296350 11344022765750598871 3193778122705907169 "
+      "16719669104430190089 14918335244853046975 11608293761910939782 "
+      "17290187430985633813 856382712722415618 14819792788008454203 "
+      "10571145147196955435 12858063129221173592 5671819431516788648 "
+      "17837836658827607239 14004823010100183722 9067196699747632668 "
+      "441015230260308492 3444946658209715644 1825101023084664281 "
+      "11133092574473850025 12746373758552339264 10154162549097295782 "
+      "14922316177042921089 12679802872389794491 8797747037480461410 "
+      "13907752811248535439 5652405835046458389 3181711594575177977 "
+      "15495242712294857418 6049158560807439366 952771601159099159 "
+      "4587095466254740009 11160954054611782211 10071795025240457628 "
+      "1536670498623767300 1990681379653546894 14312739227381277138 "
+      "9695213786215402291 3580182943401628617 12313607438786545484 "
+      "12864141705426648443 692371170805382036 13125536612285239925 "
+      "9372929234002877092 9510419002221032820 3766423210161674061 "
+      "3230494342413727261 5934351496112072933 2863344864469097044 "
+      "10884720908958139042 4127946927340597780 9960629658622711061 "
+      "14818231351611083857 6346099615454582885 12601407558879514692 "
+      "17544105005554819865 1096648950913019831 9969868157190185788 "
+      "12908611252828823970 5941129863397152719 16168953427117105234 "
+      "12304862402025196697 7781571759256122972 13289545261301048078 "
+      "11013924305579914035 8894422550580466537 7506958826675805512 "
+      "14280817252893250439 2745266616282182732 17277225453205013047 "
+      "14335499905842065319 11961295941780577536 18072890757248426766 "
+      "1124506606842606920 17329960125355005185 13052066741624159010 "
+      "5704650516221677069 16588425097127709212 11813406583737887980 "
+      "16359723311775411283 13451679937172566665 5997753207634594468 "
+      "10656019008205694109 13074690560123889048 14811648124990806194 "
+      "7809449463531558024 5637787273252434288 16515135932856030468 "
+      "3755600163640125044 1153929634172103321 11071014283313196016 "
+      "11114640359080035583 15390782025450330559 14097530518721927499 "
+      "14776783751481098767 7863618667181998233 11513855295425132436 "
+      "4736362806980864724 5426549653049482466 10310828122060887518 "
+      "4450247941008370560 9781171949844602811 6086471549040450051 "
+      "6033923116291003194 17669843285681524740 17610378273478865070 "
+      "12152320288002263294 6525449125788834221 5125338396312613396 "
+      "9300082688721166268 959242243476884691 6379729471368150249 "
+      "16379772457647614853 13454012201619761707 2392678998182524851 "
+      "12693758700673471007 1138892516507202079 15673908144065302514 "
+      "5299581449349386824 7590792025124859454 9863745357571267780 "
+      "357345312340746112 17610247870912740564 16347431861769737095 "
+      "11348828299228888092 7220122803951857490 7038822841708464676 "
+      "9912221445023094105 5767425533670320190 6442622362743049032 "
+      "17525461567869579503 4211095256108567696 14862334876401617373 "
+      "2866362449624104511 11413742225973279461 13015745308569358847 "
+      "5191760666536228849 17188167935010684492 18321678815621002079 "
+      "13046333455321624690 3995310719038261500 10661051209947341089 "
+      "7965203671238327266 16590917686161852835 3897101637344795372 "
+      "1538303624766151695 10893225639252940698 5386335660311332214 "
+      "5174479122000384061 17378437193516866561 13629320139302700770 "
+      "10144210341964027265 12816799659000064406 3711797003976467729 "
+      "5079455890584507977 432599929275804205 10435019529328454317 "
+      "5310854040535477246 15941464006450157396 2192067269367387270 "
+      "9782967689631091633 6777452250210540865 18067909703113078220 "
+      "17525143578810667971 87448662189824165 412530897284614413 "
+      "12066785122245373863 13073154860645125438 18282514257379582711 "
+      "8460374908111578570 15967512883067334502 9620430172798103891 "
+      "1264976185047610409 15426838192579528907 9878758812321441445 "
+      "18029992505662864846 9383699886128308360 14538949787806484635 "
+      "16958815135940772668 980481467951972605 3059030058898313960 "
+      "11497544574740915907 8385450996898478663 15571176518627282350"
+#else
+      "12332679912394435791 9453873875014225299 7194476751338084717 15353332195928478249 10481090450137100787 "
+      "16894768970350315699 9140706850585347923 17060753788370102662 5027480258542244180 4237901618226072654 "
+      "1625191769495810967 14151318192590258819 14578513270917221567 1340379920765042993 16984341741367590539 "
+      "17825705796225691974 10460633243946900672 15537248717777931667 18010309519743623944 11238461300079763233 "
+      "5212834616069799861 14803562233233663569 509073377271690301 9868447031443646872 18426739611230355604 "
+      "405337453835701754 2865140403182526171 17022743117531244315 15873574515363721317 1003946026159741321 "
+      "10106235397909731784 7501984743217763859 9608180616803748513 17321043653964822159 138792968844647510 "
+      "13940152250553688762 16186510718789785685 10788084171130680879 3336642670408666887 7310094463387030739 "
+      "5523088258140426915 1285026150975382598 13098435840364629682 6200633715587124711 5088226041080887554 "
+      "11020661496106860750 12793876732801641662 3576577317235426397 15183184978662083277 15861657920389428029 "
+      "470273426362791941 2574629887817053182 13272148377140358694 3004438593777107412 2652718879104414667 "
+      "1834027854782609871 10456539872813643760 6392289299447966013 9267269600134252311 10114120825136775101 "
+      "2198130084042867418 13999435202207229825 14893120434100842814 2852347522408045763 17948795049429383857 "
+      "17441093961240168873 5399621587997711172 12611952947737738867 6103704372959848527 738297531584281114 "
+      "16788685814043292624 6176291056095750530 16428159208694695523 4245608719980075114 12095265841295350099 "
+      "15191360055442418178 8708130367787597793 9264267659253066420 15969277182114773779 17035922951690056341 "
+      "9880719443672114132 15332192661202958624 14150490775662311299 4012807722045209888 8653700056821051719 "
+      "15242621354372282880 17096356791700814452 16018787795979380803 296068005088784708 4048466329605633785 "
+      "8059310313495723083 8563086889008277869 10184519287956394000 6827268421235011709 17346498307575607164 "
+      "6880036350466817261 16153597041588957084 15405131912393208395 12767350280822570901 1052885868672540599 "
+      "4188094412071243913 1126222035238982882 9191486289101564624 10803640833835240836 3601208394977378668 "
+      "4766334496475281242 7832706214999682994 16891444816728487342 17860841416138565015 12370215439434878681 "
+      "9232823972512380788 3640121802277180952 65633375613217608 13600365462550545275 17948579528827407927 "
+      "765525796436836449 13572020505370210613 11924972776661436983 4057541052165744297 17481588995966250594 "
+      "8006213088863539837 9739468715064136699 2716054689781779647 16549004007254321028 4910543003771498131 "
+      "13361071256636157603 15297097731894649703 17624276599277544080 15499557723143597570 17957222608348359804 "
+      "2778177927288936525 13637160153035836011 5098746756051510535 4146145678876364034 2247309035465090095 "
+      "9569124281048572866 12809922376030463313 15451507763993230705 17163206508771813131 17029710194125776618 "
+      "8061947308014036349 1058089520886506655 13597933493965622376 13504098358025602133 1333487301584903927 "
+      "8426860107195956174 3508319831781619992 13220104443771667566 15729810053761292123 4229931263400540722 "
+      "4166080451531777928 6512350709932085964 11707745856776186054 5335698181984744736 16601891716487755730 "
+      "14845047860332527819 13881061187353168588 2290627681380867569 15008794942145533328 9207539692861337313 "
+      "15854225885502222653 12652120737459513649 8391328319785893524 16218535624802384239 10635204123630136612 "
+      "14741489403103018051 1034273171946262577 2598761621738522071 10597540332677591413 11769725249744073186 "
+      "12729182262319592409 13143766812621949948 7337533679729790607 8700823377064447860 6528412901068836499 "
+      "12887771189408551435 6596057164621086436 9725510839601037147 12589333640740308830 8713351353100408409 "
+      "3200078174512985423 3001899537338861016 14245433087720289982 2342459702085298473 15840929965350904315 "
+      "5242384259319523672 6976265022106569670 4694429153464034874 4640016838764614916 11889897715595570593 "
+      "3752569812323601873 2900819842622530250 1859087250745968648 12106047926770169995 12635768141780418349 "
+      "460915272050913013 16909625031804250705 15219281218948426364 15473817896328511538 356510218273447008 "
+      "4322019207571293094 3044055123444388775 14541219878139834671 16010790916272798386 17663395908622954435 "
+      "12611516608169911627 16319922669204576197 16662253955344225107 16530145161795660660 13055268744196885107 "
+      "7863493504777224455 4884882190835979670 17860099797924912564 7963955360230183301 12071138257120882069 "
+      "18357891564140620935 12793730149183942271 14575396984169788025 3998777626616457896 16107243833817385461 "
+      "2800961891557584893 2799945392029798727 11053512580047143649 11067103105271293369 1968075203485277477 "
+      "3090469630341592171 14797167942499219775 1314244307340344386 5197117610141487191 14715472132315901058 "
+      "10223376637313339598 18090093248597656064 162846477809223606 18060308085200244427 4381704277003259436 "
+      "13919578743352454047 12290099036217748490 2399520646070724256 8630374525153030174 12607432526554525963 "
+      "13596968856308825531 4310477479909840944 5249608228653410962 3699954346213058725 287328857418258109 "
+      "6119686667148845021 5412656704791399252 4791204075127203599 14494596012449800144 6684909344487566871 "
+      "4578857374794350422 3320300604108137187 12579270049709840437 16704843247071205209 3817825700702948560 "
+      "5357944831944862632 1304382350048285997 10815164501630203260 438468935207234712 4455248787965292675 "
+      "16418065713664649114 16484487561423541185 10845743900807077540 376195122763577279 15264985897394715313 "
+      "7633016332459855519 18374759716909813581 7494762658573963878 12561201922274984497 17674342236666928778 "
+      "8885969173395223311 8755229097686715671 1319918912921290459 12669348064964076026 8347713925484594194 "
+      "7131861330014060437 18013870281775146388 18428665316372779146 14853358900946804630 11802355386662327119 "
+      "12234197935566505458 1562495104086441070 4789216534718205848 4797493321171570223 11489013455481650173 "
+      "15991479755025557462 5193680184864207374 538012454739572314 12691714223537549902 1625023157851387876 "
+      "15523181006631146535 5212032641878267513 314342477903124542 7039243305239188299 13660860177517273492 "
+      "279600177346432632 1242305966848399256 3754792367662117676 7456833993243708712 5524526481950173096 "
+      "12629250495102362491 12320658947410481525 8375636660933855231 14586189041466889258 3338611085276113663 "
+      "6796460652093029292 17977915623844806175 6678041303167794104 162629150799512142 12738967623757853162 "
+      "15873580300066345221 1661173500922285893"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  unsigned as[] = {3, 5, 7};
+  std::seed_seq sseq(as, as + 3);
+  std::mt19937_64 e1(sseq);
+  std::ostringstream os;
+  os << e1;
+  LIBCPP_ASSERT(os.str() == a);
+  (void)a;
 }
 
-int main(int, char**)
-{
-    test1();
-    test2();
+int main(int, char**) {
+  test1();
+  test2();
 
   return 0;
 }
diff --git a/libcxx/utils/libcxx/test/features/libcxx_macros.py b/libcxx/utils/libcxx/test/features/libcxx_macros.py
index a3b32a9c88051..b8c0e13a622c9 100644
--- a/libcxx/utils/libcxx/test/features/libcxx_macros.py
+++ b/libcxx/utils/libcxx/test/features/libcxx_macros.py
@@ -35,6 +35,7 @@
     "_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING": "libcpp-abi-no-compressed-pair-padding",
     "_LIBCPP_PSTL_BACKEND_LIBDISPATCH": "libcpp-pstl-backend-libdispatch",
     "_LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE": "libcpp-abi-use-small-deque-block-size",
+    "_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine"
 }
 for macro, feature in macros.items():
     features.append(


From libcxx-commits at lists.llvm.org  Thu Jul  2 01:42:58 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 01:42:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a462492.eea6ab6e.1256b6.b86c@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {darker}-->


:warning: Python code formatter, darker found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
darker --check --diff -r origin/main...HEAD libcxx/utils/libcxx/test/features/libcxx_macros.py
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from darker here.
</summary>

``````````diff
--- libcxx_macros.py	2026-07-02 08:41:08.000000 +0000
+++ libcxx_macros.py	2026-07-02 08:42:26.069709 +0000
@@ -33,11 +33,11 @@
     "_LIBCPP_ABI_FIX_UNORDERED_CONTAINER_SIZE_TYPE": "libcpp-has-abi-fix-unordered-container-size-type",
     "_LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR": "libcpp-deprecated-abi-disable-pair-trivial-copy-ctor",
     "_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING": "libcpp-abi-no-compressed-pair-padding",
     "_LIBCPP_PSTL_BACKEND_LIBDISPATCH": "libcpp-pstl-backend-libdispatch",
     "_LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE": "libcpp-abi-use-small-deque-block-size",
-    "_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine"
+    "_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine",
 }
 for macro, feature in macros.items():
     features.append(
         Feature(
             name=lambda cfg, m=macro, f=feature: f + ("={}".format(compilerMacros(cfg)[m]) if compilerMacros(cfg)[m] else ""),

``````````

</details>


https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Thu Jul  2 01:47:13 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 01:47:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++] Optimize fstream::read"
	(PR #206453)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206453@github.com>
Message-ID: <6a462591.bb839515.23e6d3.ddc6@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/206453

>From 1c9c584325ae01933d2e3245e8805aa452e3ee2f Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 29 Jun 2026 12:22:36 +0200
Subject: [PATCH] Reapply "[libc++] Optimize fstream::read"

This was reverted due to causing crashes if an ifstream wasn't opened.
This patch addresses this issue by simply checking whether the `__file_`
handle is null, and if it is simply fall back to the generic
implementation.

Fixes #168628
Fixes #205845

This reverts commit 347512ff38748ac6ebfacbfda172edb5cf1edbe2.
---
 libcxx/docs/ReleaseNotes/23.rst               |  2 ++
 libcxx/include/fstream                        | 19 ++++++++++++++++
 .../{ofstream.bench.cpp => fstream.bench.cpp} | 22 +++++++++++++++++--
 .../filebuf/traits_mismatch.verify.cpp        |  2 +-
 .../fstreams/traits_mismatch.verify.cpp       |  2 +-
 .../fstreams/ifstream.members/xsgetn.pass.cpp | 10 +++++++++
 6 files changed, 53 insertions(+), 4 deletions(-)
 rename libcxx/test/benchmarks/streams/{ofstream.bench.cpp => fstream.bench.cpp} (52%)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 37aa972d22ea3..1852351cedc4e 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -69,6 +69,8 @@ Improvements and New Features
   ``"std::visit: variant is valueless"``, ``"std::get: variant is valueless"``, or
   ``"std::get: wrong alternative for variant"``. The standard only requires ``what()`` to return an
   unspecified non-null string, so user code that does not match on the exact message remains correct.
+- ``ifstream::read`` has been optimized to pass through large reads to system calls directly instead of copying them in
+  chunks.
 
 Deprecations and Removals
 -------------------------
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 0c34c9149de0d..7b84bf6609086 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -309,6 +309,25 @@ protected:
     return basic_streambuf<_CharT, _Traits>::xsputn(__str, __len);
   }
 
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL streamsize xsgetn(char_type* __str, streamsize __len) override {
+    if (__file_ && __always_noconv_) {
+      const streamsize __n = std::min(this->egptr() - this->gptr(), __len);
+      if (__n != 0) {
+        traits_type::copy(__str, this->gptr(), __n);
+        this->__gbump_ptrdiff(__n);
+      }
+      const streamsize __remainder    = __len - __n;
+      const streamsize __buffer_space = this->egptr() - this->eback();
+
+      if (__remainder >= __buffer_space)
+        return std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
+      else if (__remainder > 0)
+        return basic_streambuf<_CharT, _Traits>::xsgetn(__str + __n, __remainder) + __n;
+      return __n;
+    }
+    return basic_streambuf<_CharT, _Traits>::xsgetn(__str, __len);
+  }
+
 private:
   char* __extbuf_;
   const char* __extbufnext_;
diff --git a/libcxx/test/benchmarks/streams/ofstream.bench.cpp b/libcxx/test/benchmarks/streams/fstream.bench.cpp
similarity index 52%
rename from libcxx/test/benchmarks/streams/ofstream.bench.cpp
rename to libcxx/test/benchmarks/streams/fstream.bench.cpp
index 80827d59cdf01..2e16ae58749e6 100644
--- a/libcxx/test/benchmarks/streams/ofstream.bench.cpp
+++ b/libcxx/test/benchmarks/streams/fstream.bench.cpp
@@ -13,7 +13,7 @@
 
 #include <benchmark/benchmark.h>
 
-static void bm_write(benchmark::State& state) {
+static void bm_ofstream_write(benchmark::State& state) {
   std::vector<char> buffer;
   buffer.resize(16384);
 
@@ -22,6 +22,24 @@ static void bm_write(benchmark::State& state) {
   for (auto _ : state)
     stream.write(buffer.data(), buffer.size());
 }
-BENCHMARK(bm_write)->Name("std::ofstream::write(char*, size)");
+BENCHMARK(bm_ofstream_write)->Name("std::ofstream::write(char*, size)");
+
+static void bm_ifstream_read(benchmark::State& state) {
+  std::vector<char> buffer;
+  buffer.resize(16384);
+
+  std::ofstream gen_testfile("testfile");
+  gen_testfile.write(buffer.data(), buffer.size());
+
+  std::ifstream stream("testfile");
+  assert(stream);
+
+  for (auto _ : state) {
+    stream.read(buffer.data(), buffer.size());
+    benchmark::DoNotOptimize(buffer);
+    stream.seekg(0);
+  }
+}
+BENCHMARK(bm_ifstream_read)->Name("std::ifstream::read(char*, size)");
 
 BENCHMARK_MAIN();
diff --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
index 283adbc057d1e..30e7b66d42325 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
@@ -19,4 +19,4 @@
 
 std::basic_filebuf<char, std::char_traits<wchar_t> > f;
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
-// expected-error@*:* 10 {{only virtual member functions can be marked 'override'}}
+// expected-error@*:* 11 {{only virtual member functions can be marked 'override'}}
diff --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
index ba6f3c31d3e34..daafb36f9151a 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
@@ -21,7 +21,7 @@ std::basic_fstream<char, std::char_traits<wchar_t> > f;
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
 
-// expected-error@*:* 12 {{only virtual member functions can be marked 'override'}}
+// expected-error@*:* 13 {{only virtual member functions can be marked 'override'}}
 
 // FIXME: As of commit r324062 Clang incorrectly generates a diagnostic about mismatching
 // exception specifications for types which are already invalid for one reason or another.
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
index de17cd4d8e9d3..685a5f4a6e5dc 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
@@ -68,6 +68,16 @@ int main(int, char**) {
     test_buffer.resize(24);
     assert(std::string(test_buffer.data(), 24) == "to test buffer behaviour");
   }
+  { // Ensure that read fails gracefully with an unopened ifstream
+    // See https://llvm.org/PR168628
+    char buf[10];
+    std::ifstream ifs;
+    std::filebuf* filebuf = ifs.rdbuf();
+
+    assert(!filebuf->is_open());
+    assert(filebuf->sgetn(buf, sizeof(buf)) == 0);
+    assert(!filebuf->is_open());
+  }
 
   return 0;
 }


From libcxx-commits at lists.llvm.org  Thu Jul  2 01:47:56 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 01:47:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++] Optimize fstream::read"
	(PR #206453)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206453@github.com>
Message-ID: <6a4625bc.d1496011.3d49cb.a5af@mx.google.com>


================
@@ -68,6 +68,25 @@ int main(int, char**) {
     test_buffer.resize(24);
     assert(std::string(test_buffer.data(), 24) == "to test buffer behaviour");
   }
+  { // Ensure that read fails gracefully with an unopened ifstream
+    // See https://llvm.org/PR168628
+    char buf[10];
+    std::ifstream ifs;
+
+    assert(!ifs.is_open());
+    assert(!ifs.bad());
+    assert(!ifs.fail());
+    assert(!ifs.eof());
+    assert(ifs.good());
+
+    ifs.read(buf, sizeof(buf));
----------------
philnik777 wrote:

I've decided to use the streambuf interface instead, since that's what this test actually cares about.

https://github.com/llvm/llvm-project/pull/206453

From libcxx-commits at lists.llvm.org  Thu Jul  2 01:48:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 01:48:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++] Optimize fstream::read"
	(PR #206453)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206453@github.com>
Message-ID: <6a4625ce.fdc477d6.118e5e.608f@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/206453

>From 95773ccd75bef4cbfdc558ec00f2c260fa76598f Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 29 Jun 2026 12:22:36 +0200
Subject: [PATCH] Reapply "[libc++] Optimize fstream::read"

This was reverted due to causing crashes if an ifstream wasn't opened.
This patch addresses this issue by simply checking whether the `__file_`
handle is null, and if it is simply fall back to the generic
implementation.

Fixes #168628
Fixes #205845

This reverts commit 347512ff38748ac6ebfacbfda172edb5cf1edbe2.
---
 libcxx/docs/ReleaseNotes/23.rst               |  2 ++
 libcxx/include/fstream                        | 19 ++++++++++++++++
 .../{ofstream.bench.cpp => fstream.bench.cpp} | 22 +++++++++++++++++--
 .../filebuf/traits_mismatch.verify.cpp        |  2 +-
 .../fstreams/traits_mismatch.verify.cpp       |  2 +-
 .../fstreams/ifstream.members/xsgetn.pass.cpp | 10 +++++++++
 6 files changed, 53 insertions(+), 4 deletions(-)
 rename libcxx/test/benchmarks/streams/{ofstream.bench.cpp => fstream.bench.cpp} (52%)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 37aa972d22ea3..1852351cedc4e 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -69,6 +69,8 @@ Improvements and New Features
   ``"std::visit: variant is valueless"``, ``"std::get: variant is valueless"``, or
   ``"std::get: wrong alternative for variant"``. The standard only requires ``what()`` to return an
   unspecified non-null string, so user code that does not match on the exact message remains correct.
+- ``ifstream::read`` has been optimized to pass through large reads to system calls directly instead of copying them in
+  chunks.
 
 Deprecations and Removals
 -------------------------
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 0c34c9149de0d..7b84bf6609086 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -309,6 +309,25 @@ protected:
     return basic_streambuf<_CharT, _Traits>::xsputn(__str, __len);
   }
 
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL streamsize xsgetn(char_type* __str, streamsize __len) override {
+    if (__file_ && __always_noconv_) {
+      const streamsize __n = std::min(this->egptr() - this->gptr(), __len);
+      if (__n != 0) {
+        traits_type::copy(__str, this->gptr(), __n);
+        this->__gbump_ptrdiff(__n);
+      }
+      const streamsize __remainder    = __len - __n;
+      const streamsize __buffer_space = this->egptr() - this->eback();
+
+      if (__remainder >= __buffer_space)
+        return std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
+      else if (__remainder > 0)
+        return basic_streambuf<_CharT, _Traits>::xsgetn(__str + __n, __remainder) + __n;
+      return __n;
+    }
+    return basic_streambuf<_CharT, _Traits>::xsgetn(__str, __len);
+  }
+
 private:
   char* __extbuf_;
   const char* __extbufnext_;
diff --git a/libcxx/test/benchmarks/streams/ofstream.bench.cpp b/libcxx/test/benchmarks/streams/fstream.bench.cpp
similarity index 52%
rename from libcxx/test/benchmarks/streams/ofstream.bench.cpp
rename to libcxx/test/benchmarks/streams/fstream.bench.cpp
index 80827d59cdf01..2e16ae58749e6 100644
--- a/libcxx/test/benchmarks/streams/ofstream.bench.cpp
+++ b/libcxx/test/benchmarks/streams/fstream.bench.cpp
@@ -13,7 +13,7 @@
 
 #include <benchmark/benchmark.h>
 
-static void bm_write(benchmark::State& state) {
+static void bm_ofstream_write(benchmark::State& state) {
   std::vector<char> buffer;
   buffer.resize(16384);
 
@@ -22,6 +22,24 @@ static void bm_write(benchmark::State& state) {
   for (auto _ : state)
     stream.write(buffer.data(), buffer.size());
 }
-BENCHMARK(bm_write)->Name("std::ofstream::write(char*, size)");
+BENCHMARK(bm_ofstream_write)->Name("std::ofstream::write(char*, size)");
+
+static void bm_ifstream_read(benchmark::State& state) {
+  std::vector<char> buffer;
+  buffer.resize(16384);
+
+  std::ofstream gen_testfile("testfile");
+  gen_testfile.write(buffer.data(), buffer.size());
+
+  std::ifstream stream("testfile");
+  assert(stream);
+
+  for (auto _ : state) {
+    stream.read(buffer.data(), buffer.size());
+    benchmark::DoNotOptimize(buffer);
+    stream.seekg(0);
+  }
+}
+BENCHMARK(bm_ifstream_read)->Name("std::ifstream::read(char*, size)");
 
 BENCHMARK_MAIN();
diff --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
index 283adbc057d1e..30e7b66d42325 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
@@ -19,4 +19,4 @@
 
 std::basic_filebuf<char, std::char_traits<wchar_t> > f;
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
-// expected-error@*:* 10 {{only virtual member functions can be marked 'override'}}
+// expected-error@*:* 11 {{only virtual member functions can be marked 'override'}}
diff --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
index ba6f3c31d3e34..daafb36f9151a 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
@@ -21,7 +21,7 @@ std::basic_fstream<char, std::char_traits<wchar_t> > f;
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
 
-// expected-error@*:* 12 {{only virtual member functions can be marked 'override'}}
+// expected-error@*:* 13 {{only virtual member functions can be marked 'override'}}
 
 // FIXME: As of commit r324062 Clang incorrectly generates a diagnostic about mismatching
 // exception specifications for types which are already invalid for one reason or another.
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
index de17cd4d8e9d3..d9ccc2fe62914 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
@@ -68,6 +68,16 @@ int main(int, char**) {
     test_buffer.resize(24);
     assert(std::string(test_buffer.data(), 24) == "to test buffer behaviour");
   }
+  { // Ensure that the read fails gracefully with an unopened ifstream
+    // See https://llvm.org/PR168628
+    char buf[10];
+    std::ifstream ifs;
+    std::filebuf* filebuf = ifs.rdbuf();
+
+    assert(!filebuf->is_open());
+    assert(filebuf->sgetn(buf, sizeof(buf)) == 0);
+    assert(!filebuf->is_open());
+  }
 
   return 0;
 }


From libcxx-commits at lists.llvm.org  Thu Jul  2 01:48:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 01:48:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 88bf280 - Reapply "[libc++] Optimize
	fstream::read" (#206453)
Message-ID: <6a4625dc.f732ad81.f30e7.0f6e@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-02T10:48:23+02:00
New Revision: 88bf28094ec315d164f975e59e9b96641d801af8

URL: https://github.com/llvm/llvm-project/commit/88bf28094ec315d164f975e59e9b96641d801af8
DIFF: https://github.com/llvm/llvm-project/commit/88bf28094ec315d164f975e59e9b96641d801af8.diff

LOG: Reapply "[libc++] Optimize fstream::read" (#206453)

This was reverted due to causing crashes if an ifstream wasn't opened.
This patch addresses this issue by simply checking whether the `__file_`
handle is null, and if it is simply fall back to the generic
implementation.

Fixes #168628
Fixes #205845

This reverts commit 347512ff38748ac6ebfacbfda172edb5cf1edbe2.

Added: 
    libcxx/test/benchmarks/streams/fstream.bench.cpp

Modified: 
    libcxx/docs/ReleaseNotes/23.rst
    libcxx/include/fstream
    libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
    libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
    libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp

Removed: 
    libcxx/test/benchmarks/streams/ofstream.bench.cpp


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 37aa972d22ea3..1852351cedc4e 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -69,6 +69,8 @@ Improvements and New Features
   ``"std::visit: variant is valueless"``, ``"std::get: variant is valueless"``, or
   ``"std::get: wrong alternative for variant"``. The standard only requires ``what()`` to return an
   unspecified non-null string, so user code that does not match on the exact message remains correct.
+- ``ifstream::read`` has been optimized to pass through large reads to system calls directly instead of copying them in
+  chunks.
 
 Deprecations and Removals
 -------------------------

diff  --git a/libcxx/include/fstream b/libcxx/include/fstream
index 0c34c9149de0d..7b84bf6609086 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -309,6 +309,25 @@ protected:
     return basic_streambuf<_CharT, _Traits>::xsputn(__str, __len);
   }
 
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL streamsize xsgetn(char_type* __str, streamsize __len) override {
+    if (__file_ && __always_noconv_) {
+      const streamsize __n = std::min(this->egptr() - this->gptr(), __len);
+      if (__n != 0) {
+        traits_type::copy(__str, this->gptr(), __n);
+        this->__gbump_ptr
diff (__n);
+      }
+      const streamsize __remainder    = __len - __n;
+      const streamsize __buffer_space = this->egptr() - this->eback();
+
+      if (__remainder >= __buffer_space)
+        return std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
+      else if (__remainder > 0)
+        return basic_streambuf<_CharT, _Traits>::xsgetn(__str + __n, __remainder) + __n;
+      return __n;
+    }
+    return basic_streambuf<_CharT, _Traits>::xsgetn(__str, __len);
+  }
+
 private:
   char* __extbuf_;
   const char* __extbufnext_;

diff  --git a/libcxx/test/benchmarks/streams/ofstream.bench.cpp b/libcxx/test/benchmarks/streams/fstream.bench.cpp
similarity index 52%
rename from libcxx/test/benchmarks/streams/ofstream.bench.cpp
rename to libcxx/test/benchmarks/streams/fstream.bench.cpp
index 80827d59cdf01..2e16ae58749e6 100644
--- a/libcxx/test/benchmarks/streams/ofstream.bench.cpp
+++ b/libcxx/test/benchmarks/streams/fstream.bench.cpp
@@ -13,7 +13,7 @@
 
 #include <benchmark/benchmark.h>
 
-static void bm_write(benchmark::State& state) {
+static void bm_ofstream_write(benchmark::State& state) {
   std::vector<char> buffer;
   buffer.resize(16384);
 
@@ -22,6 +22,24 @@ static void bm_write(benchmark::State& state) {
   for (auto _ : state)
     stream.write(buffer.data(), buffer.size());
 }
-BENCHMARK(bm_write)->Name("std::ofstream::write(char*, size)");
+BENCHMARK(bm_ofstream_write)->Name("std::ofstream::write(char*, size)");
+
+static void bm_ifstream_read(benchmark::State& state) {
+  std::vector<char> buffer;
+  buffer.resize(16384);
+
+  std::ofstream gen_testfile("testfile");
+  gen_testfile.write(buffer.data(), buffer.size());
+
+  std::ifstream stream("testfile");
+  assert(stream);
+
+  for (auto _ : state) {
+    stream.read(buffer.data(), buffer.size());
+    benchmark::DoNotOptimize(buffer);
+    stream.seekg(0);
+  }
+}
+BENCHMARK(bm_ifstream_read)->Name("std::ifstream::read(char*, size)");
 
 BENCHMARK_MAIN();

diff  --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
index 283adbc057d1e..30e7b66d42325 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
@@ -19,4 +19,4 @@
 
 std::basic_filebuf<char, std::char_traits<wchar_t> > f;
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
-// expected-error@*:* 10 {{only virtual member functions can be marked 'override'}}
+// expected-error@*:* 11 {{only virtual member functions can be marked 'override'}}

diff  --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
index ba6f3c31d3e34..daafb36f9151a 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
@@ -21,7 +21,7 @@ std::basic_fstream<char, std::char_traits<wchar_t> > f;
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
 
-// expected-error@*:* 12 {{only virtual member functions can be marked 'override'}}
+// expected-error@*:* 13 {{only virtual member functions can be marked 'override'}}
 
 // FIXME: As of commit r324062 Clang incorrectly generates a diagnostic about mismatching
 // exception specifications for types which are already invalid for one reason or another.

diff  --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
index de17cd4d8e9d3..d9ccc2fe62914 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
@@ -68,6 +68,16 @@ int main(int, char**) {
     test_buffer.resize(24);
     assert(std::string(test_buffer.data(), 24) == "to test buffer behaviour");
   }
+  { // Ensure that the read fails gracefully with an unopened ifstream
+    // See https://llvm.org/PR168628
+    char buf[10];
+    std::ifstream ifs;
+    std::filebuf* filebuf = ifs.rdbuf();
+
+    assert(!filebuf->is_open());
+    assert(filebuf->sgetn(buf, sizeof(buf)) == 0);
+    assert(!filebuf->is_open());
+  }
 
   return 0;
 }


        

From libcxx-commits at lists.llvm.org  Thu Jul  2 01:48:29 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 01:48:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++] Optimize fstream::read"
	(PR #206453)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206453@github.com>
Message-ID: <6a4625dd.13be6df8.3d8759.4c03@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/206453

From libcxx-commits at lists.llvm.org  Thu Jul  2 01:50:27 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 01:50:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 6a3359f - [libc++] Remove
 _LIBCPP_COMPRESSED_PAIR from packaged_task (#200642)
Message-ID: <6a462653.dd2b4bd3.35cd5e.1da9@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-02T10:50:23+02:00
New Revision: 6a3359fefb8443af1b897e32f0f18dc5c00aac39

URL: https://github.com/llvm/llvm-project/commit/6a3359fefb8443af1b897e32f0f18dc5c00aac39
DIFF: https://github.com/llvm/llvm-project/commit/6a3359fefb8443af1b897e32f0f18dc5c00aac39.diff

LOG: [libc++] Remove _LIBCPP_COMPRESSED_PAIR from packaged_task (#200642)

Added: 
    libcxx/test/std/thread/futures/futures.task/destruction_order.pass.cpp

Modified: 
    libcxx/include/future

Removed: 
    


################################################################################
diff  --git a/libcxx/include/future b/libcxx/include/future
index c169c6c182d83..30ceb97785811 100644
--- a/libcxx/include/future
+++ b/libcxx/include/future
@@ -379,7 +379,6 @@ template <class R, class Alloc> struct uses_allocator<packaged_task<R>, Alloc>;
 #    include <__memory/allocator_arg_t.h>
 #    include <__memory/allocator_destructor.h>
 #    include <__memory/allocator_traits.h>
-#    include <__memory/compressed_pair.h>
 #    include <__memory/pointer_traits.h>
 #    include <__memory/shared_count.h>
 #    include <__memory/unique_ptr.h>
@@ -1413,8 +1412,10 @@ template <class _FD, class _Alloc, class _FB>
 class __packaged_task_func;
 
 template <class _Fp, class _Alloc, class _Rp, class... _ArgTypes>
-class __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)> : public __packaged_task_base<_Rp(_ArgTypes...)> {
-  _LIBCPP_COMPRESSED_PAIR(_Fp, __func_, _Alloc, __alloc_);
+class _LIBCPP_HIDE_STRUCT_FROM_ABI
+    __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)> : public __packaged_task_base<_Rp(_ArgTypes...)> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _Fp __func_;
+  _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_;
 
 public:
   _LIBCPP_HIDE_FROM_ABI explicit __packaged_task_func(const _Fp& __f) : __func_(__f) {}
@@ -1435,8 +1436,8 @@ void __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::__move_to(
 
 template <class _Fp, class _Alloc, class _Rp, class... _ArgTypes>
 void __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::destroy() {
-  __func_.~_Fp();
   __alloc_.~_Alloc();
+  __func_.~_Fp();
 }
 
 template <class _Fp, class _Alloc, class _Rp, class... _ArgTypes>
@@ -1445,8 +1446,8 @@ void __packaged_task_func<_Fp, _Alloc, _Rp(_ArgTypes...)>::destroy_deallocate()
   typedef allocator_traits<_Ap> _ATraits;
   typedef pointer_traits<typename _ATraits::pointer> _PTraits;
   _Ap __a(__alloc_);
-  __func_.~_Fp();
   __alloc_.~_Alloc();
+  __func_.~_Fp();
   __a.deallocate(_PTraits::pointer_to(*this), 1);
 }
 

diff  --git a/libcxx/test/std/thread/futures/futures.task/destruction_order.pass.cpp b/libcxx/test/std/thread/futures/futures.task/destruction_order.pass.cpp
new file mode 100644
index 0000000000000..93941e3691297
--- /dev/null
+++ b/libcxx/test/std/thread/futures/futures.task/destruction_order.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
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+//
+// REQUIRES: c++03 || c++11 || c++14
+//
+// <future>
+//
+// Ensure that packaged_task destroys its members in the correct order.
+
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+#include <future>
+
+#include "test_macros.h"
+
+template <int Align, int BufferSize>
+struct TEST_ALIGNAS(Align) CallableWithPadding {
+  CallableWithPadding() {} // Allow putting object into the tail padding
+  CallableWithPadding(const CallableWithPadding&) {}
+  CallableWithPadding& operator=(const CallableWithPadding&) { return *this; }
+  ~CallableWithPadding() { std::memset(static_cast<void*>(this), 0, sizeof(*this)); }
+
+  void operator()() {}
+
+  char buffer[BufferSize];
+};
+
+template <class T>
+struct AllocatorWithData {
+  using value_type = T;
+
+  AllocatorWithData() : data(123) {}
+  AllocatorWithData(const AllocatorWithData& other) : data(other.data) {}
+  AllocatorWithData& operator=(const AllocatorWithData& other) { data = other.data; }
+  ~AllocatorWithData() { assert(data == 123); }
+
+  template <class U>
+  AllocatorWithData(const AllocatorWithData<U>& other) : data(other.data) {}
+
+  uint64_t data;
+
+  T* allocate(size_t n) { return std::allocator<T>().allocate(n); }
+  void deallocate(T* ptr, size_t n) { std::allocator<T>().deallocate(ptr, n); }
+};
+
+int main(int, char**) {
+  std::packaged_task<void()> heap((std::allocator_arg_t(), AllocatorWithData<int>(), CallableWithPadding<32, 24>()));
+  std::packaged_task<void()> stack((std::allocator_arg_t(), AllocatorWithData<int>(), CallableWithPadding<16, 8>()));
+  return 0;
+}


        

From libcxx-commits at lists.llvm.org  Thu Jul  2 01:50:30 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 01:50:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove _LIBCPP_COMPRESSED_PAIR
 from packaged_task (PR #200642)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/200642@github.com>
Message-ID: <6a462656.331483e7.5bc1e.7fbc@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/200642

From libcxx-commits at lists.llvm.org  Thu Jul  2 01:52:52 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 01:52:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a4626e4.510f576d.185953.ea53@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/185397

>From 6965698b91d90ffe301f9bc1a561c81dae6e1349 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 12 Feb 2026 15:08:00 +0100
Subject: [PATCH] [libc++] Refactor the string benchmarks

---
 .../benchmarks/containers/string.bench.cpp    | 1013 ++++++++---------
 1 file changed, 454 insertions(+), 559 deletions(-)

diff --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index 98216d22d0144..3493c62195b94 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -6,602 +6,497 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
 
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
 
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Huge does not give us anything that Large doesn't have. Skip it.
-    return Length() == ::Length::Huge;
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  std::string name() const { return "BM_StringRead" + Temperature::name() + Depth::name() + Length::name(); }
-};
-
-void sanityCheckGeneratedStrings() {
-  for (auto Lhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
-    const auto LhsString = makeString(Lhs);
-    for (auto Rhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
-      if (Lhs > Rhs)
-        continue;
-      const auto RhsString = makeString(Rhs);
-
-      // The smaller one must be a prefix of the larger one.
-      if (RhsString.find(LhsString) != 0) {
-        fprintf(
-            stderr, "Invalid autogenerated strings for sizes (%d,%d).\n", static_cast<int>(Lhs), static_cast<int>(Rhs));
-        std::abort();
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string.compare(src));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
-  // Verify the autogenerated diffs
-  for (auto L : {Length::Small, Length::Large, Length::Huge}) {
-    const auto Control = makeString(L);
-    const auto Verify  = [&](std::string Exp, size_t Pos) {
-      // Only change on the Pos char.
-      if (Control[Pos] != Exp[Pos]) {
-        Exp[Pos] = Control[Pos];
-        if (Control == Exp)
-          return;
-      }
-      fprintf(stderr, "Invalid autogenerated diff with size %d\n", static_cast<int>(L));
-      std::abort();
-    };
-    Verify(makeString(L, DiffType::ChangeFirst), 0);
-    Verify(makeString(L, DiffType::ChangeMiddle), Control.size() / 2);
-    Verify(makeString(L, DiffType::ChangeLast), Control.size() - 1);
-  }
-}
 
-int main(int argc, char** argv) {
   benchmark::Initialize(&argc, argv);
-  if (benchmark::ReportUnrecognizedArguments(argc, argv))
-    return 1;
-
-  sanityCheckGeneratedStrings();
-
-  makeCartesianProductBenchmark<StringConstructDestroyCStr, AllLengths, AllOpacity>();
-
-  makeCartesianProductBenchmark<StringAssignStr, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringAssignAsciiz, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringAssignAsciizMix, AllOpacity>();
-
-  makeCartesianProductBenchmark<StringCopy, AllLengths>();
-  makeCartesianProductBenchmark<StringMove, AllLengths>();
-  makeCartesianProductBenchmark<StringDestroy, AllLengths>();
-  makeCartesianProductBenchmark<StringEraseToEnd, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringEraseWithMove, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringRelational, AllRelations, AllLengths, AllLengths, AllDiffTypes>();
-  makeCartesianProductBenchmark<StringRelationalLiteral, AllRelations, AllLengths, AllLengths, AllDiffTypes>();
-  makeCartesianProductBenchmark<StringRead, AllTemperatures, AllDepths, AllLengths>();
   benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
 }


From libcxx-commits at lists.llvm.org  Thu Jul  2 02:08:31 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 02:08:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Base string's alignment on
 __STDCPP_DEFAULT_NEW_ALIGNMENT__ (PR #171785)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171785@github.com>
Message-ID: <6a462a8f.f14cf1a5.2c3aaa.7012@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/171785

>From 247ad49dc2cde3ae96d572c7ff7f6a6a02412149 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 11 Dec 2025 10:14:52 +0100
Subject: [PATCH] [libc++] Base string's alignment on
 __STDCPP_DEFAULT_NEW_ALIGNMENT__

---
 libcxx/include/__cxx03/string                 | 13 +++++++-
 libcxx/include/string                         | 12 +++++++-
 .../string.capacity/allocation_size.pass.cpp  |  7 +++--
 .../string.capacity/max_size.pass.cpp         |  7 +++--
 .../strings/basic.string/new_alignment.sh.cpp | 30 +++++++++++++++++++
 .../string.capacity/allocation_size.pass.cpp  | 24 +++++++++------
 .../string.capacity/max_size.pass.cpp         |  5 +++-
 .../string.capacity/max_size.pass.cpp         |  4 +++
 .../string.capacity/over_max_size.pass.cpp    |  9 ++----
 9 files changed, 88 insertions(+), 23 deletions(-)
 create mode 100644 libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp

diff --git a/libcxx/include/__cxx03/string b/libcxx/include/__cxx03/string
index a919a59e55d57..e5330fb01c04f 100644
--- a/libcxx/include/__cxx03/string
+++ b/libcxx/include/__cxx03/string
@@ -1623,7 +1623,18 @@ private:
   static _LIBCPP_HIDE_FROM_ABI size_type __align_it(size_type __s) _NOEXCEPT {
     return (__s + (__a - 1)) & ~(__a - 1);
   }
-  enum { __alignment = 8 };
+
+  // GCC currently doesn't define __STDCPP_DEFAULT_NEW_ALIGNMENT__ unconditionally
+  // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123872
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+  static inline const size_t
+      __alignment = __is_default_allocator<allocator_type>::value && __STDCPP_DEFAULT_NEW_ALIGNMENT__ > sizeof(void*)
+                      ? __STDCPP_DEFAULT_NEW_ALIGNMENT__
+                      : sizeof(void*);
+#else
+  static inline const size_t __alignment = sizeof(void*);
+#endif
+
   static _LIBCPP_HIDE_FROM_ABI size_type __recommend(size_type __s) _NOEXCEPT {
     if (__s < __min_cap) {
       return static_cast<size_type>(__min_cap) - 1;
diff --git a/libcxx/include/string b/libcxx/include/string
index e5bc854cd34f3..76c5b0e477a59 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -2270,7 +2270,17 @@ private:
   static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __align_it(size_type __s) _NOEXCEPT {
     return (__s + (__a - 1)) & ~(__a - 1);
   }
-  enum { __alignment = 8 };
+
+  // GCC currently doesn't define __STDCPP_DEFAULT_NEW_ALIGNMENT__ unconditionally
+  // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123872
+#  ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+  static inline const size_t
+      __alignment = __is_std_allocator_v<allocator_type> && __STDCPP_DEFAULT_NEW_ALIGNMENT__ > sizeof(void*)
+                      ? __STDCPP_DEFAULT_NEW_ALIGNMENT__
+                      : sizeof(void*);
+#  else
+  static inline const size_t __alignment = sizeof(void*);
+#  endif
 
   // This makes sure that we're using a capacity with some extra alignment, since allocators almost always over-align
   // the allocations anyways, improving memory usage. More importantly, this ensures that the lowest bit is never set
diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
index 77da29225957b..367a58897cec0 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
@@ -15,8 +15,11 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
-const std::size_t alignment = 8;
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const std::size_t alginment = 8;
+#endif
 
 int main(int, char**) {
   std::string input_string;
diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
index 73825ef4845f2..15736e4a1ceed 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -17,8 +17,11 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
-static const std::size_t alignment = 8;
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const std::size_t alginment = 8;
+#endif
 
 template <class = int>
 TEST_CONSTEXPR_CXX20 void full_size() {
diff --git a/libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp b/libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp
new file mode 100644
index 0000000000000..f8906c9fb9827
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Check that std::string bases its alignment on __STDCPP_DEFAULT_NEW_ALIGNMENT__
+
+// UNSUPPORTED: c++03
+
+// RUN: %{build} -faligned-new=32 -DALIGN=32
+// RUN: %{run}
+// RUN: %{build} -faligned-new=16 -DALIGN=16
+// RUN: %{run}
+// RUN: %{build} -faligned-new=8 -DALIGN=8
+// RUN: %{run}
+
+#include <cassert>
+#include <string>
+
+int main(int, char**) {
+  std::string str;
+  str.reserve(25);
+
+  assert(str.capacity() % ALIGN == ALIGN - 1);
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
index 77da29225957b..3c16701cbf860 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
@@ -8,6 +8,13 @@
 
 // <string>
 
+// This test is sensitive what __STDCPP_DEFAULT_NEW_ALIGNMENT__ is set to. Enable aligned-new for GCC so that GCC
+// defines the macro
+// ADDITIONAL_COMPILE_FLAGS(gcc): -faligned-new
+
+// The allocations are done in the dylib, so we need to use an up-to-date one
+// XFAIL: using-built-library-before-llvm-23
+
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
@@ -15,22 +22,21 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
-const std::size_t alignment = 8;
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const std::size_t alignment = 8;
+#endif
 
 int main(int, char**) {
-  std::string input_string;
-  input_string.resize(64, 'a');
-
-  // Call a constructor which selects its size using __recommend.
-  std::string test_string(input_string.data());
+  std::string str(64, 'a');
   const std::size_t expected_align8_size = 71;
 
   // Demonstrate the lesser capacity/allocation size when the alignment requirement is 8.
   if (alignment == 8) {
-    assert(test_string.capacity() == expected_align8_size);
+    assert(str.capacity() == expected_align8_size);
   } else {
-    assert(test_string.capacity() == expected_align8_size + 8);
+    assert(str.capacity() == expected_align8_size + 8);
   }
 
   return 0;
diff --git a/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
index 6bfcb5d4bfcd8..11bd1a5928d4a 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -17,8 +17,11 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
 static const std::size_t alignment = 8;
+#endif
 
 template <class = int>
 TEST_CONSTEXPR_CXX20 void full_size() {
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
index 45a52acab464e..b35bed39f2bed 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -8,6 +8,10 @@
 
 // UNSUPPORTED: no-exceptions
 
+// This test is sensitive what __STDCPP_DEFAULT_NEW_ALIGNMENT__ is set to. Enable aligned-new for GCC so that GCC
+// defines the macro
+// ADDITIONAL_COMPILE_FLAGS(gcc): -faligned-new
+
 // <string>
 
 // size_type max_size() const; // constexpr since C++20
diff --git a/libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp
index 8e5919539d94e..5e6cb68fd9a42 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp
@@ -8,13 +8,8 @@
 
 // UNSUPPORTED: no-exceptions
 
-// This test fails when using a built library that does not contain
-// 15860446a8c3, which changed the return value of max_size(). Without
-// that change, the built library believes the max size to be one greater
-// than it really is, and we fail to throw `length_error` from `string::resize()`,
-// which is explicitly instantiated in the built library.
-//
-// XFAIL: using-built-library-before-llvm-21
+// The allocations are done in the dylib, so we need to use an up-to-date one
+// XFAIL: using-built-library-before-llvm-23
 
 // <string>
 


From libcxx-commits at lists.llvm.org  Thu Jul  2 02:09:44 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 02:09:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 6f9ac38 - [libc++][test] Cover byteswap
 _BitInt padding on every ABI with width 72 (#206360)
Message-ID: <6a462ad8.c56fb102.11841f.cfd1@mx.google.com>


Author: Xavier Roche
Date: 2026-07-02T17:09:39+08:00
New Revision: 6f9ac38b7c1934fbce49683e32c732c73e5e1ab4

URL: https://github.com/llvm/llvm-project/commit/6f9ac38b7c1934fbce49683e32c732c73e5e1ab4
DIFF: https://github.com/llvm/llvm-project/commit/6f9ac38b7c1934fbce49683e32c732c73e5e1ab4.diff

LOG: [libc++][test] Cover byteswap _BitInt padding on every ABI with width 72 (#206360)

`std::byteswap` is required to reject integer types that have padding
bits, and one test case checks that it does. The case used `unsigned
_BitInt(96)`, but #205295 had to skip it on 32-bit x86. On the i386
System V ABI, `_BitInt(96)` is exactly 12 bytes with no padding, so
`std::byteswap` accepts it and the expected error never fires. The skip
used `!defined(__i386__)`, a target name that stands in for an object
layout the preprocessor cannot see.

This change uses `unsigned _BitInt(72)` instead. Its 9 value bytes round
up to 12 bytes on 32-bit x86 and 16 bytes everywhere else, so the type
always has padding and `std::byteswap` always rejects it. The case now
runs on every target, 32-bit x86 included, with no target guard.

Follows up on #205295.

Assisted-by: Claude (Anthropic)

---------

Co-authored-by: Claude Opus 4.6 <noreply at anthropic.com>
Co-authored-by: A. Jiang <de34 at live.cn>

Added: 
    

Modified: 
    libcxx/test/std/numerics/bit/byteswap.verify.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/std/numerics/bit/byteswap.verify.cpp b/libcxx/test/std/numerics/bit/byteswap.verify.cpp
index f9b6954f525da..bbd3312a2457a 100644
--- a/libcxx/test/std/numerics/bit/byteswap.verify.cpp
+++ b/libcxx/test/std/numerics/bit/byteswap.verify.cpp
@@ -103,23 +103,20 @@ void test_unsigned_56() {
 
 // Same dispatch-availability guard as test_unsigned_65 above.
 #  if TEST_HAS_BUILTIN(__builtin_bswapg) || !defined(TEST_HAS_NO_INT128)
-#    if __BITINT_MAXWIDTH__ >= 80
-void test_unsigned_80() {
-  // sizeof(_BitInt(80)) == 16 on x86_64; 48 padding bits. Width 80 is also
-  // a multiple of 16, so bswapg would accept it without the static_assert.
-  unsigned _BitInt(80) v = 0;
+// 72 value bits leave padding on every ABI.
+#    if __BITINT_MAXWIDTH__ >= 72
+void test_unsigned_72() {
+  unsigned _BitInt(72) v = 0;
   // expected-error-re@*:* {{{{(std::byteswap requires T to have no padding bits|byteswap is unimplemented for integral types of this size)}}}}
   (void)std::byteswap(v);
 }
 #    endif
 
-// 32-bit x86 packs _BitInt(96) into 12 bytes (no padding), so std::byteswap
-// accepts it there; gate the case out. sizeof isn't available to the
-// preprocessor, so this is a target check rather than a has-padding predicate.
-#    if __BITINT_MAXWIDTH__ >= 96 && !defined(__i386__)
-void test_unsigned_96() {
-  // sizeof(_BitInt(96)) == 16 here; 32 padding bits.
-  unsigned _BitInt(96) v = 0;
+#    if __BITINT_MAXWIDTH__ >= 80
+void test_unsigned_80() {
+  // sizeof(_BitInt(80)) == 16 on x86_64; 48 padding bits. Width 80 is also
+  // a multiple of 16, so bswapg would accept it without the static_assert.
+  unsigned _BitInt(80) v = 0;
   // expected-error-re@*:* {{{{(std::byteswap requires T to have no padding bits|byteswap is unimplemented for integral types of this size)}}}}
   (void)std::byteswap(v);
 }


        

From libcxx-commits at lists.llvm.org  Thu Jul  2 02:09:51 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Thu, 02 Jul 2026 02:09:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] Cover byteswap _BitInt
 padding on every ABI with width 72 (PR #206360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206360@github.com>
Message-ID: <6a462adf.30759430.1908d3.a9ce@mx.google.com>


https://github.com/frederick-vs-ja closed https://github.com/llvm/llvm-project/pull/206360

From libcxx-commits at lists.llvm.org  Thu Jul  2 02:13:19 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 02:13:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use std::to_chars to format
 thread::id and canonicalize the representation across platforms (PR
 #181624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/181624@github.com>
Message-ID: <6a462baf.3157459c.24b6e5.dd62@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/181624

>From 24246971271bb9651ea80b7bf72ab8623f3b2067 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 16 Feb 2026 11:40:25 +0100
Subject: [PATCH] [libc++] Use std::to_string to format thread::id

---
 libcxx/docs/ReleaseNotes/23.rst               |  3 +
 libcxx/include/__thread/formatter.h           |  9 +-
 libcxx/include/__thread/thread.h              | 28 +++---
 libcxx/include/future                         |  5 +
 libcxx/include/module.modulemap.in            |  5 +-
 libcxx/include/thread                         |  1 +
 .../format.functions.pass.cpp                 | 94 +++++++++++++++++++
 .../test/libcxx/transitive_includes/cxx26.csv | 13 ---
 .../format.functions.format.pass.cpp          | 57 -----------
 .../thread.thread.id/format.functions.tests.h | 83 ----------------
 .../format.functions.vformat.pass.cpp         | 48 +++++++---
 .../thread.thread.id/format.pass.cpp          | 16 ++--
 .../thread.thread.id/stream.pass.cpp          |  1 +
 13 files changed, 170 insertions(+), 193 deletions(-)
 create mode 100644 libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp
 delete mode 100644 libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
 delete mode 100644 libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 37aa972d22ea3..f01f38d1bf27e 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -90,6 +90,9 @@ Potentially breaking changes
   including the correct header. To ease the transition ``_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23`` can be defined, which
   includes the removed headers again. This macro will be removed in LLVM 24.
 
+- The output of ``ostream << std::thread::id`` and ``std::format("...", std::thread:id)`` changes on some platforms from
+  hexadecimal to decimal. This is done to have consistent formatting across all platforms libc++ supports.
+
 Announcements About Future Releases
 -----------------------------------
 
diff --git a/libcxx/include/__thread/formatter.h b/libcxx/include/__thread/formatter.h
index 826607d47b469..9682798e1df41 100644
--- a/libcxx/include/__thread/formatter.h
+++ b/libcxx/include/__thread/formatter.h
@@ -51,20 +51,13 @@ struct formatter<__thread_id, _CharT> {
     // On Linux systems pthread_t is an unsigned long long.
     // On Apple systems pthread_t is a pointer type.
     //
-    // Note the output should match what the stream operator does. Since
-    // the ostream operator has been shipped years before this formatter
-    // was added to the Standard, this formatter does what the stream
-    // operator does. This may require platform specific changes.
+    // Note the output should match what the stream operator does.
 
     using _Tp = decltype(__get_underlying_id(__id));
     using _Cp = conditional_t<integral<_Tp>, _Tp, conditional_t<is_pointer_v<_Tp>, uintptr_t, void>>;
     static_assert(!is_same_v<_Cp, void>, "unsupported thread::id type, please file a bug report");
 
     __format_spec::__parsed_specifications<_CharT> __specs = __parser_.__get_parsed_std_specifications(__ctx);
-    if constexpr (is_pointer_v<_Tp>) {
-      __specs.__std_.__alternate_form_ = true;
-      __specs.__std_.__type_           = __format_spec::__type::__hexadecimal_lower_case;
-    }
     return __formatter::__format_integer(reinterpret_cast<_Cp>(__get_underlying_id(__id)), __ctx, __specs);
   }
 
diff --git a/libcxx/include/__thread/thread.h b/libcxx/include/__thread/thread.h
index ee7a65212f31d..24f62bfe5899e 100644
--- a/libcxx/include/__thread/thread.h
+++ b/libcxx/include/__thread/thread.h
@@ -11,12 +11,13 @@
 #define _LIBCPP___THREAD_THREAD_H
 
 #include <__assert>
+#include <__charconv/to_chars_integral.h>
 #include <__condition_variable/condition_variable.h>
 #include <__config>
 #include <__exception/terminate.h>
 #include <__functional/hash.h>
 #include <__functional/unary_function.h>
-#include <__locale>
+#include <__fwd/ostream.h>
 #include <__memory/addressof.h>
 #include <__memory/unique_ptr.h>
 #include <__mutex/mutex.h>
@@ -27,16 +28,15 @@
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_constructible.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/exchange.h>
 #include <__utility/forward.h>
+#include <cstdint>
+#include <limits>
 #include <tuple>
 
-#if _LIBCPP_HAS_LOCALIZATION
-#  include <sstream>
-#endif
-
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
@@ -146,13 +146,19 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, __thread_id __id) {
   //
   // Since various flags in the output stream can affect how the
   // thread id is represented (e.g. numpunct or showbase), we
-  // use a temporary stream instead and just output the thread
-  // id representation as a string.
+  // use `to_chars` directly and output the id as a string.
+
+  static_assert(is_pointer<__libcpp_thread_id>::value || is_integral<__libcpp_thread_id>::value);
+
+  using __int_type = __conditional_t<is_pointer<__libcpp_thread_id>::value, uintptr_t, __libcpp_thread_id>;
 
-  basic_ostringstream<_CharT, _Traits> __sstr;
-  __sstr.imbue(locale::classic());
-  __sstr << __id.__id_;
-  return __os << __sstr.str();
+  static const size_t __buffer_size = numeric_limits<__int_type>::digits10 + 2;
+  char __buffer[__buffer_size];
+  auto __ret =
+      std::__to_chars_integral(__buffer, __buffer + __buffer_size - 1, reinterpret_cast<__int_type>(__id.__id_), 10);
+  _LIBCPP_ASSERT_INTERNAL(__ret.__ec == std::errc(), "to_chars failed!");
+  *__ret.__ptr = '\0';
+  return __os << __buffer;
 }
 #  endif // _LIBCPP_HAS_LOCALIZATION
 
diff --git a/libcxx/include/future b/libcxx/include/future
index c169c6c182d83..57ee783662377 100644
--- a/libcxx/include/future
+++ b/libcxx/include/future
@@ -2077,6 +2077,11 @@ _LIBCPP_POP_MACROS
 #    include <system_error>
 #    include <thread>
 #  endif
+
+#  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 23
+#    include <sstream>
+#  endif
+
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 
 #endif // _LIBCPP_FUTURE
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b9ddfd5bbf4cd..81a07258345e8 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -948,7 +948,10 @@ module std [system] {
       header "__charconv/to_chars_integral.h"
       export std.charconv.to_chars_result
     }
-    module to_chars_result            { header "__charconv/to_chars_result.h" }
+    module to_chars_result            {
+      header "__charconv/to_chars_result.h"
+      export std.system_error.errc
+    }
     module traits                     { header "__charconv/traits.h" }
 
     header "charconv"
diff --git a/libcxx/include/thread b/libcxx/include/thread
index fa50d048330c6..d79db3dcd7791 100644
--- a/libcxx/include/thread
+++ b/libcxx/include/thread
@@ -131,6 +131,7 @@ void sleep_for(const chrono::duration<Rep, Period>& rel_time);
 
 #  if defined(_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23) && _LIBCPP_STD_VER <= 23
 #    include <atomic>
+#    include <sstream>
 #  endif
 
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
diff --git a/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp
new file mode 100644
index 0000000000000..6719ec6b783e6
--- /dev/null
+++ b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-threads
+
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// <thread>
+
+// template<class charT>
+// struct formatter<thread::id, charT>;
+
+// template<class... Args>
+//   string format(format_string<Args...> fmt, Args&&... args);
+// template<class... Args>
+//   wstring format(wformat_string<Args...> fmt, Args&&... args);
+
+#include <cassert>
+#include <format>
+#include <thread>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "format.functions.common.h"
+#include "test_format_string.h"
+#include "test_macros.h"
+
+template <class CharT, class TestFunction>
+void format_tests(TestFunction check) {
+  // Note the output of std::thread::id is unspecified. The output text is the
+  // same as the stream operator. Since that format is already released this
+  // test follows the practice on existing systems.
+  std::thread::id input{};
+
+  /***** Test the type specific part *****/
+  check(SV("0"), SV("{}"), input);
+  check(SV("0^42"), SV("{}^42"), input);
+  check(SV("0^42"), SV("{:}^42"), input);
+
+  // *** align-fill & width ***
+  check(SV("    0"), SV("{:5}"), input);
+  check(SV("0****"), SV("{:*<5}"), input);
+  check(SV("__0__"), SV("{:_^5}"), input);
+  check(SV("::::0"), SV("{::>5}"), input); // This is not a range, so : is allowed as fill character.
+
+  check(SV("    0"), SV("{:{}}"), input, 5);
+  check(SV("0****"), SV("{:*<{}}"), input, 5);
+  check(SV("__0__"), SV("{:_^{}}"), input, 5);
+  check(SV("####0"), SV("{:#>{}}"), input, 5);
+}
+
+auto test = []<class CharT, class... Args>(
+                std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
+  {
+    std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
+  {
+    std::basic_string<CharT> out = std::vformat(fmt.get(), std::make_format_args<context_t<CharT>>(args...));
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
+};
+
+int main(int, char**) {
+  format_tests<char>(test);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  format_tests<wchar_t>(test);
+#endif
+
+  { // Check that we print the ID correctly (i.e. library-internal round-tripping works)
+    auto thread_id = std::this_thread::get_id();
+    auto result    = std::format("{}", thread_id);
+    auto num       = std::stoull(result);
+    using int_t =
+        std::conditional_t<std::is_pointer<std::__libcpp_thread_id>::value, uintptr_t, std::__libcpp_thread_id>;
+    assert(reinterpret_cast<int_t>(__get_underlying_id(std::this_thread::get_id())) == static_cast<int_t>(num));
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index cf1154a251f23..f678ef7a22134 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -423,29 +423,21 @@ functional tuple
 functional typeinfo
 functional unordered_map
 functional version
-future bitset
 future cctype
 future cerrno
 future climits
-future clocale
 future compare
-future cstddef
 future cstdint
 future cstdio
-future cstdlib
 future cstring
 future ctime
 future cwchar
 future cwctype
 future initializer_list
-future ios
 future iosfwd
-future istream
 future limits
 future ratio
-future sstream
 future stdexcept
-future streambuf
 future string
 future string_view
 future text_encoding
@@ -1043,7 +1035,6 @@ text_encoding stdexcept
 text_encoding string_view
 text_encoding version
 thread array
-thread bitset
 thread cctype
 thread cerrno
 thread climits
@@ -1058,14 +1049,10 @@ thread ctime
 thread cwchar
 thread cwctype
 thread initializer_list
-thread ios
 thread iosfwd
-thread istream
 thread limits
 thread ratio
-thread sstream
 thread stdexcept
-thread streambuf
 thread string
 thread string_view
 thread text_encoding
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
deleted file mode 100644
index 5618836d89e80..0000000000000
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-threads
-
-// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
-
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
-// <thread>
-
-// template<class charT>
-// struct formatter<thread::id, charT>;
-
-// template<class... Args>
-//   string format(format_string<Args...> fmt, Args&&... args);
-// template<class... Args>
-//   wstring format(wformat_string<Args...> fmt, Args&&... args);
-
-#include <format>
-#include <cassert>
-
-#include "assert_macros.h"
-#include "concat_macros.h"
-#include "format.functions.tests.h"
-#include "test_format_string.h"
-#include "test_macros.h"
-
-auto test = []<class CharT, class... Args>(
-                std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
-  std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
-  TEST_REQUIRE(out == expected,
-               TEST_WRITE_CONCATENATED(
-                   "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
-};
-
-auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, Args&&...) {
-  // After P2216 most exceptions thrown by std::format become ill-formed.
-  // Therefore this tests does nothing.
-};
-
-int main(int, char**) {
-  format_tests<char>(test, test_exception);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  format_tests<wchar_t>(test, test_exception);
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
deleted file mode 100644
index f55f0e2af8cb2..0000000000000
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
+++ /dev/null
@@ -1,83 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
-#define TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
-
-#include <thread>
-
-#include "format.functions.common.h"
-#include "test_macros.h"
-
-template <class CharT, class TestFunction, class ExceptionTest>
-void format_tests(TestFunction check, ExceptionTest check_exception) {
-  // Note the output of std::thread::id is unspecified. The output text is the
-  // same as the stream operator. Since that format is already released this
-  // test follows the practice on existing systems.
-  std::thread::id input{};
-
-  /***** Test the type specific part *****/
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
-  check(SV("0"), SV("{}"), input);
-  check(SV("0^42"), SV("{}^42"), input);
-  check(SV("0^42"), SV("{:}^42"), input);
-
-  // *** align-fill & width ***
-  check(SV("    0"), SV("{:5}"), input);
-  check(SV("0****"), SV("{:*<5}"), input);
-  check(SV("__0__"), SV("{:_^5}"), input);
-  check(SV("::::0"), SV("{::>5}"), input); // This is not a range, so : is allowed as fill character.
-
-  check(SV("    0"), SV("{:{}}"), input, 5);
-  check(SV("0****"), SV("{:*<{}}"), input, 5);
-  check(SV("__0__"), SV("{:_^{}}"), input, 5);
-  check(SV("####0"), SV("{:#>{}}"), input, 5);
-#else  // !defined(__APPLE__) && !defined(__FreeBSD__)
-  check(SV("0x0"), SV("{}"), input);
-  check(SV("0x0^42"), SV("{}^42"), input);
-  check(SV("0x0^42"), SV("{:}^42"), input);
-
-  // *** align-fill & width ***
-  check(SV("    0x0"), SV("{:7}"), input);
-  check(SV("0x0****"), SV("{:*<7}"), input);
-  check(SV("__0x0__"), SV("{:_^7}"), input);
-  check(SV("::::0x0"), SV("{::>7}"), input); // This is not a range, so : is allowed as fill character.
-
-  check(SV("    0x0"), SV("{:{}}"), input, 7);
-  check(SV("0x0****"), SV("{:*<{}}"), input, 7);
-  check(SV("__0x0__"), SV("{:_^{}}"), input, 7);
-  check(SV("####0x0"), SV("{:#>{}}"), input, 7);
-#endif // !defined(__APPLE__) && !defined(__FreeBSD__)
-
-  /***** Test the type generic part *****/
-  check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
-  check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
-
-  // *** sign ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:-}"), input);
-  check_exception("The replacement field misses a terminating '}'", SV("{:+}"), input);
-  check_exception("The replacement field misses a terminating '}'", SV("{: }"), input);
-
-  // *** alternate form ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:#}"), input);
-
-  // *** zero-padding ***
-  check_exception("The width option should not have a leading zero", SV("{:0}"), input);
-
-  // *** precision ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:.}"), input);
-
-  // *** locale-specific form ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:L}"), input);
-
-  // *** type ***
-  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>(""))
-    check_exception("The replacement field misses a terminating '}'", fmt, input);
-}
-
-#endif // TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
index 8555ebd742616..ffe1330fa9c7a 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
@@ -22,21 +22,47 @@
 // string vformat(string_view fmt, format_args args);
 // wstring vformat(wstring_view fmt, wformat_args args);
 
-#include <format>
 #include <cassert>
+#include <format>
+#include <thread>
 
 #include "assert_macros.h"
 #include "concat_macros.h"
-#include "format.functions.tests.h"
+#include "format.functions.common.h"
 #include "test_macros.h"
 
-auto test = []<class CharT, class... Args>(
-                std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) {
-  std::basic_string<CharT> out = std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
-  TEST_REQUIRE(out == expected,
-               TEST_WRITE_CONCATENATED(
-                   "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
-};
+template <class CharT, class ExceptionTest>
+void format_tests(ExceptionTest check_exception) {
+  // Note the output of std::thread::id is unspecified. The output text is the
+  // same as the stream operator. Since that format is already released this
+  // test follows the practice on existing systems.
+  std::thread::id input{};
+
+  /***** Test the type generic part *****/
+  check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
+  check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
+
+  // *** sign ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:-}"), input);
+  check_exception("The replacement field misses a terminating '}'", SV("{:+}"), input);
+  check_exception("The replacement field misses a terminating '}'", SV("{: }"), input);
+
+  // *** alternate form ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:#}"), input);
+
+  // *** zero-padding ***
+  check_exception("The width option should not have a leading zero", SV("{:0}"), input);
+
+  // *** precision ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:L}"), input);
+
+  // *** type ***
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>(""))
+    check_exception("The replacement field misses a terminating '}'", fmt, input);
+}
 
 auto test_exception =
     []<class CharT, class... Args>(
@@ -55,10 +81,10 @@ auto test_exception =
     };
 
 int main(int, char**) {
-  format_tests<char>(test, test_exception);
+  format_tests<char>(test_exception);
 
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  format_tests<wchar_t>(test, test_exception);
+  format_tests<wchar_t>(test_exception);
 #endif
 
   return 0;
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
index 39a257592809d..d3fc6e4b016d6 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
@@ -34,9 +34,8 @@
 
 #define SV(S) MAKE_STRING_VIEW(CharT, S)
 
-template <class StringViewT>
-void test_format(StringViewT expected, std::thread::id arg) {
-  using CharT      = typename StringViewT::value_type;
+template <class CharT>
+std::basic_string<CharT> test_format(std::thread::id arg) {
   using String     = std::basic_string<CharT>;
   using OutIt      = std::back_insert_iterator<String>;
   using FormatCtxT = std::basic_format_context<OutIt, CharT>;
@@ -47,16 +46,15 @@ void test_format(StringViewT expected, std::thread::id arg) {
   OutIt out             = std::back_inserter(result);
   FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>(arg));
   formatter.format(arg, format_ctx);
-  assert(result == expected);
+  assert(!result.empty());
+  return result;
 }
 
 template <class CharT>
 void test_fmt() {
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
-  test_format(SV("0"), std::thread::id());
-#else
-  test_format(SV("0x0"), std::thread::id());
-#endif
+  auto def  = test_format<CharT>(std::thread::id());
+  auto self = test_format<CharT>(std::this_thread::get_id());
+  assert(def != self);
 }
 
 void test() {
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
index 97e797f9a8fbb..ae4df5f5f55a5 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
@@ -27,6 +27,7 @@
 #include <format>
 #include <locale>
 #include <sstream>
+#include <string>
 #include <thread>
 
 #include "make_string.h"


From libcxx-commits at lists.llvm.org  Thu Jul  2 02:24:45 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 02:24:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a462e5d.da8bafd3.1eab2b.f74b@mx.google.com>


philnik777 wrote:

> > I'm not convinced this is worth the complexity. This adds a workaround for older compilers, so this will be obsolete in a year anyways, and for that it's way too complicated of a fix IMO.
> 
> I found this issue when I working on #205379, If we don't want this workaround, I'd like to disable the failed tests for old compilers in #205379. WDYT?

IMO that's a perfectly fine outcome.

https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Thu Jul  2 02:34:19 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 02:34:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Ues _LIBCPP_NO_UNIQUE_ADDRESS
 for the new vetor layout (PR #207149)
Message-ID: <llvm.org/llvm/llvm-project/pull/207149@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207149

We use `_LIBCPP_NO_UNIQUE_ADDRESS`, since a plain `[[no_unique_address]]` doesn't work on Windows.


>From 1d328f9eb40ca18bc8002cfc132eae8aa498fc4a Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 2 Jul 2026 11:33:21 +0200
Subject: [PATCH] [libc++] Ues _LIBCPP_NO_UNIQUE_ADDRESS for the new vetor
 layout

---
 libcxx/include/__vector/layout.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__vector/layout.h b/libcxx/include/__vector/layout.h
index 65d11aa238df0..910078b1e760e 100644
--- a/libcxx/include/__vector/layout.h
+++ b/libcxx/include/__vector/layout.h
@@ -217,7 +217,7 @@ class __vector_layout {
 #ifdef _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED
   size_type __size_     = 0;
   size_type __capacity_ = 0;
-  [[no_unique_address]] allocator_type __alloc_;
+  _LIBCPP_NO_UNIQUE_ADDRESS allocator_type __alloc_;
 #else
   pointer __end_ = nullptr;
   _LIBCPP_COMPRESSED_PAIR(pointer, __capacity_ = nullptr, allocator_type, __alloc_);


From libcxx-commits at lists.llvm.org  Thu Jul  2 02:34:53 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 02:34:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Ues _LIBCPP_NO_UNIQUE_ADDRESS
 for the new vetor layout (PR #207149)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207149@github.com>
Message-ID: <6a4630bd.58bdd12c.21857.7040@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

We use `_LIBCPP_NO_UNIQUE_ADDRESS`, since a plain `[[no_unique_address]]` doesn't work on Windows.


---
Full diff: https://github.com/llvm/llvm-project/pull/207149.diff


1 Files Affected:

- (modified) libcxx/include/__vector/layout.h (+1-1) 


``````````diff
diff --git a/libcxx/include/__vector/layout.h b/libcxx/include/__vector/layout.h
index 65d11aa238df0..910078b1e760e 100644
--- a/libcxx/include/__vector/layout.h
+++ b/libcxx/include/__vector/layout.h
@@ -217,7 +217,7 @@ class __vector_layout {
 #ifdef _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED
   size_type __size_     = 0;
   size_type __capacity_ = 0;
-  [[no_unique_address]] allocator_type __alloc_;
+  _LIBCPP_NO_UNIQUE_ADDRESS allocator_type __alloc_;
 #else
   pointer __end_ = nullptr;
   _LIBCPP_COMPRESSED_PAIR(pointer, __capacity_ = nullptr, allocator_type, __alloc_);

``````````

</details>


https://github.com/llvm/llvm-project/pull/207149

From libcxx-commits at lists.llvm.org  Thu Jul  2 02:40:29 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 02:40:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use _LIBCPP_NO_UNIQUE_ADDRESS
 for the new vetor layout (PR #207149)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207149@github.com>
Message-ID: <6a46320d.8e75e0cb.2e0812.cf4e@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/207149

From libcxx-commits at lists.llvm.org  Thu Jul  2 02:40:39 2026
From: libcxx-commits at lists.llvm.org (Yanzuo Liu via libcxx-commits)
Date: Thu, 02 Jul 2026 02:40:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use _LIBCPP_NO_UNIQUE_ADDRESS
 for the new vector layout (PR #207149)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207149@github.com>
Message-ID: <6a463217.bb839515.23e6d3.d545@mx.google.com>


https://github.com/zwuis edited https://github.com/llvm/llvm-project/pull/207149

From libcxx-commits at lists.llvm.org  Thu Jul  2 02:46:45 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Thu, 02 Jul 2026 02:46:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a463385.af6ba3ed.2c2f30.348c@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Thu Jul  2 02:55:08 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 02:55:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some unnecessary
 functions from __vector_layout (PR #207152)
Message-ID: <llvm.org/llvm/llvm-project/pull/207152@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207152

This removes functions which produce identical IR after the first InstCombine pass after inlining compared to their replacements.


>From 51bd6648ade55f87de0fcef85ecc9a3e7c77efc9 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 2 Jul 2026 11:53:16 +0200
Subject: [PATCH] [libc++] Remove some unnecessary functions from
 __vector_layout

---
 libcxx/include/__vector/layout.h | 46 --------------------------------
 libcxx/include/__vector/vector.h | 16 +++++------
 2 files changed, 8 insertions(+), 54 deletions(-)

diff --git a/libcxx/include/__vector/layout.h b/libcxx/include/__vector/layout.h
index 65d11aa238df0..6be670381b244 100644
--- a/libcxx/include/__vector/layout.h
+++ b/libcxx/include/__vector/layout.h
@@ -179,9 +179,6 @@ class __vector_layout {
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type
   __remaining_capacity() const _NOEXCEPT;
 
-  /// Determines if a reallocation is necessary.
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __is_full() const _NOEXCEPT;
-
   /// Sets the member pointing to the first element in the vector to `__new_begin`, the member used
   /// to obtain the vector's bound to the equivalent of `__new_size`, and the member that represents
   /// the vector's capacity to the equivalent to `__new_capacity`.
@@ -202,9 +199,6 @@ class __vector_layout {
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __size() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __capacity() const _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __empty() const _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI _Tp& __back() _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI _Tp const& __back() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __end_ptr() _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer __end_ptr() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_ptr() _NOEXCEPT;
@@ -252,11 +246,6 @@ __vector_layout<_Tp, _Alloc>::__remaining_capacity() const _NOEXCEPT {
   return __capacity_ - __size_;
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__is_full() const _NOEXCEPT {
-  return __size_ == __capacity_;
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __vector_layout<_Tp, _Alloc>::__set_layout(
     pointer __new_begin, size_type __new_size, size_type __new_capacity) _NOEXCEPT {
@@ -324,21 +313,6 @@ __vector_layout<_Tp, _Alloc>::__capacity() const _NOEXCEPT {
   return __capacity_;
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__empty() const _NOEXCEPT {
-  return __size_ == 0;
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __vector_layout<_Tp, _Alloc>::__back() _NOEXCEPT {
-  return __begin_[__size_ - 1];
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp const& __vector_layout<_Tp, _Alloc>::__back() const _NOEXCEPT {
-  return __begin_[__size_ - 1];
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __vector_layout<_Tp, _Alloc>::pointer
 __vector_layout<_Tp, _Alloc>::__end_ptr() _NOEXCEPT {
@@ -384,11 +358,6 @@ __vector_layout<_Tp, _Alloc>::__remaining_capacity() const _NOEXCEPT {
   return __capacity_ - __end_;
 }
 
-template <class _Tp, class _Alloc>
-[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__is_full() const _NOEXCEPT {
-  return __end_ == __capacity_;
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __vector_layout<_Tp, _Alloc>::__set_layout(
     pointer __new_begin, size_type __new_size, size_type __new_capacity) _NOEXCEPT {
@@ -456,21 +425,6 @@ __vector_layout<_Tp, _Alloc>::__capacity() const _NOEXCEPT {
   return static_cast<size_type>(__capacity_ - __begin_);
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__empty() const _NOEXCEPT {
-  return __begin_ == __end_;
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __vector_layout<_Tp, _Alloc>::__back() _NOEXCEPT {
-  return __end_[-1];
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp const& __vector_layout<_Tp, _Alloc>::__back() const _NOEXCEPT {
-  return __end_[-1];
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __vector_layout<_Tp, _Alloc>::pointer
 __vector_layout<_Tp, _Alloc>::__end_ptr() _NOEXCEPT {
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..ae55dfbd877c1 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -400,7 +400,7 @@ class vector {
     return __layout_.__capacity();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
-    return __layout_.__empty();
+    return size() == 0;
   }
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {
@@ -442,11 +442,11 @@ class vector {
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() _NOEXCEPT {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "back() called on an empty vector");
-    return __layout_.__back();
+    return end()[-1];
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const _NOEXCEPT {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "back() called on an empty vector");
-    return __layout_.__back();
+    return end()[-1];
   }
 
   //
@@ -1080,7 +1080,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Alloc>
 vector<_Tp, _Alloc>::emplace_back(_Args&&... __args) {
   pointer __end = __layout_.__end_ptr();
   std::__if_likely_else(
-      !__layout_.__is_full(),
+      size() != capacity(),
       [&] {
         __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
         ++__end;
@@ -1134,7 +1134,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(__x);
@@ -1157,7 +1157,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(std::move(__x));
@@ -1178,7 +1178,7 @@ template <class... _Args>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
@@ -1232,7 +1232,7 @@ vector<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __position, _Inpu
   difference_type __off = __position - begin();
   pointer __p           = this->__layout_.__begin_ptr() + __off;
   pointer __old_last    = __layout_.__end_ptr();
-  for (; !__layout_.__is_full() && __first != __last; ++__first)
+  for (; size() != capacity() && __first != __last; ++__first)
     __emplace_back_assume_capacity(*__first);
 
   if (__first == __last)


From libcxx-commits at lists.llvm.org  Thu Jul  2 03:04:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 03:04:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some unnecessary
 functions from __vector_layout (PR #207152)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207152@github.com>
Message-ID: <6a4637c3.65dfdb71.1ca534.de0a@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

This removes functions which produce identical IR after the first InstCombine pass after inlining compared to their replacements.


---
Full diff: https://github.com/llvm/llvm-project/pull/207152.diff


2 Files Affected:

- (modified) libcxx/include/__vector/layout.h (-46) 
- (modified) libcxx/include/__vector/vector.h (+8-8) 


``````````diff
diff --git a/libcxx/include/__vector/layout.h b/libcxx/include/__vector/layout.h
index 65d11aa238df0..6be670381b244 100644
--- a/libcxx/include/__vector/layout.h
+++ b/libcxx/include/__vector/layout.h
@@ -179,9 +179,6 @@ class __vector_layout {
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type
   __remaining_capacity() const _NOEXCEPT;
 
-  /// Determines if a reallocation is necessary.
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __is_full() const _NOEXCEPT;
-
   /// Sets the member pointing to the first element in the vector to `__new_begin`, the member used
   /// to obtain the vector's bound to the equivalent of `__new_size`, and the member that represents
   /// the vector's capacity to the equivalent to `__new_capacity`.
@@ -202,9 +199,6 @@ class __vector_layout {
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __size() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __capacity() const _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __empty() const _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI _Tp& __back() _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI _Tp const& __back() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __end_ptr() _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer __end_ptr() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_ptr() _NOEXCEPT;
@@ -252,11 +246,6 @@ __vector_layout<_Tp, _Alloc>::__remaining_capacity() const _NOEXCEPT {
   return __capacity_ - __size_;
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__is_full() const _NOEXCEPT {
-  return __size_ == __capacity_;
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __vector_layout<_Tp, _Alloc>::__set_layout(
     pointer __new_begin, size_type __new_size, size_type __new_capacity) _NOEXCEPT {
@@ -324,21 +313,6 @@ __vector_layout<_Tp, _Alloc>::__capacity() const _NOEXCEPT {
   return __capacity_;
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__empty() const _NOEXCEPT {
-  return __size_ == 0;
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __vector_layout<_Tp, _Alloc>::__back() _NOEXCEPT {
-  return __begin_[__size_ - 1];
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp const& __vector_layout<_Tp, _Alloc>::__back() const _NOEXCEPT {
-  return __begin_[__size_ - 1];
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __vector_layout<_Tp, _Alloc>::pointer
 __vector_layout<_Tp, _Alloc>::__end_ptr() _NOEXCEPT {
@@ -384,11 +358,6 @@ __vector_layout<_Tp, _Alloc>::__remaining_capacity() const _NOEXCEPT {
   return __capacity_ - __end_;
 }
 
-template <class _Tp, class _Alloc>
-[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__is_full() const _NOEXCEPT {
-  return __end_ == __capacity_;
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __vector_layout<_Tp, _Alloc>::__set_layout(
     pointer __new_begin, size_type __new_size, size_type __new_capacity) _NOEXCEPT {
@@ -456,21 +425,6 @@ __vector_layout<_Tp, _Alloc>::__capacity() const _NOEXCEPT {
   return static_cast<size_type>(__capacity_ - __begin_);
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__empty() const _NOEXCEPT {
-  return __begin_ == __end_;
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __vector_layout<_Tp, _Alloc>::__back() _NOEXCEPT {
-  return __end_[-1];
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp const& __vector_layout<_Tp, _Alloc>::__back() const _NOEXCEPT {
-  return __end_[-1];
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __vector_layout<_Tp, _Alloc>::pointer
 __vector_layout<_Tp, _Alloc>::__end_ptr() _NOEXCEPT {
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..ae55dfbd877c1 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -400,7 +400,7 @@ class vector {
     return __layout_.__capacity();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
-    return __layout_.__empty();
+    return size() == 0;
   }
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {
@@ -442,11 +442,11 @@ class vector {
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() _NOEXCEPT {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "back() called on an empty vector");
-    return __layout_.__back();
+    return end()[-1];
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const _NOEXCEPT {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "back() called on an empty vector");
-    return __layout_.__back();
+    return end()[-1];
   }
 
   //
@@ -1080,7 +1080,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Alloc>
 vector<_Tp, _Alloc>::emplace_back(_Args&&... __args) {
   pointer __end = __layout_.__end_ptr();
   std::__if_likely_else(
-      !__layout_.__is_full(),
+      size() != capacity(),
       [&] {
         __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
         ++__end;
@@ -1134,7 +1134,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(__x);
@@ -1157,7 +1157,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(std::move(__x));
@@ -1178,7 +1178,7 @@ template <class... _Args>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
@@ -1232,7 +1232,7 @@ vector<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __position, _Inpu
   difference_type __off = __position - begin();
   pointer __p           = this->__layout_.__begin_ptr() + __off;
   pointer __old_last    = __layout_.__end_ptr();
-  for (; !__layout_.__is_full() && __first != __last; ++__first)
+  for (; size() != capacity() && __first != __last; ++__first)
     __emplace_back_assume_capacity(*__first);
 
   if (__first == __last)

``````````

</details>


https://github.com/llvm/llvm-project/pull/207152

From libcxx-commits at lists.llvm.org  Thu Jul  2 03:06:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 03:06:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a463837.ff037943.3bf0e2.c8df@mx.google.com>


Kim-J-Smith wrote:

> I'm not convinced this is worth the complexity. This adds a workaround for older compilers, so this will be obsolete in a year anyways, and for that it's way too complicated of a fix IMO.

I agree that introducing `__complete` and `__complete_or_unbounded` solely as a workaround for older compiler bugs is not ideal—the added complexity is hard to justify for a temporary fix.

That said, I believe these traits could have broader value in libc++ development. They are analogous to `__is_complete_or_unbounded`, which is used extensively in libstdc++ to produce readable static assertions and to help maintainers write robust, self-documenting code. A few examples:

- In `std::copyable_function`:  
  https://gcc.gnu.org/git?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/include/bits/funcref_impl.h;h=9fcab570803fd7ab25ceb2556764005243f83aa4;hb=HEAD#l71

- In `std::function_ref`:  
  https://gcc.gnu.org/git?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/include/bits/funcref_impl.h;h=8a00fde1b8e54ef20555efac9a9f1406559c9648;hb=HEAD#l72

- In `__pass_by_value`:  
  https://gcc.gnu.org/git?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/include/bits/funcwrap.h;h=670efa63b4a242d83953eaf4a832c4998cdac696;hb=HEAD#l220

@philnik777  Do you think it would be worthwhile to open a separate discussion about adding `__complete` and `__complete_or_unbounded` to `__type_traits` to explore these long-term benefits? I’d be happy to file a new issue if that seems reasonable.

https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Thu Jul  2 03:15:40 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Thu, 02 Jul 2026 03:15:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P3391R2: `constexpr` integral
 overloads of `to_(w)string` (PR #205025)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205025@github.com>
Message-ID: <6a463a4c.805085c5.29ef89.c23b@mx.google.com>


================
@@ -3798,17 +3804,88 @@ stoull(const wstring& __str, size_t* __idx = nullptr, int __base = 10);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI double stod(const wstring& __str, size_t* __idx = nullptr);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI long double stold(const wstring& __str, size_t* __idx = nullptr);
 
+#    if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_BUILDING_LIBRARY)
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(int __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long long __val);
+#    endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_BUILDING_LIBRARY)
+
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(float __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(double __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long double __val);
 #  endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
+#  if _LIBCPP_STD_VER >= 26
+
+template <class _String, class _Integer>
+_LIBCPP_HIDE_FROM_ABI constexpr _String __integer_to_string(_Integer __val) {
+  // numeric_limits::digits10 returns value less on 1 than desired for unsigned numbers.
+  // For example, for 1-byte unsigned value digits10 is 2 (999 can not be represented), so we need +1 here.
+  constexpr size_t __buf_size = numeric_limits<_Integer>::digits10 + 2; // +1 for minus, +1 for digits10
+  char __buf[__buf_size];
+  const auto [__ptr, __ec] = std::to_chars(__buf, __buf + __buf_size, __val);
+  _LIBCPP_ASSERT_INTERNAL(__ec == errc(), "__buf_size must be large enough to accommodate the value");
+  return _String(__buf, __ptr);
+}
+
+#    if !defined(_LIBCPP_BUILDING_LIBRARY)
+
+[[nodiscard]] [[__gnu__::__gnu_inline__, __gnu__::__noinline__]] extern inline constexpr string to_string(int __val) {
----------------
frederick-vs-ja wrote:

> Do we really need the `extern`?

It turns out that `extern` seems to be a usable mark. With `__gnu_inline__`, the `constexpr` version should be neither `_LIBCPP_EXPORTED_FROM_ABI` (due to ABI stability on AIX) nor `_LIBCPP_HIDE_FROM_ABI` (due to symbol correspondence), and `extern` can make some checks happy.

https://github.com/llvm/llvm-project/pull/205025

From libcxx-commits at lists.llvm.org  Thu Jul  2 03:15:48 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Thu, 02 Jul 2026 03:15:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P3391R2: `constexpr` integral
 overloads of `to_(w)string` (PR #205025)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205025@github.com>
Message-ID: <6a463a54.a2448446.1ed93b.bb59@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/205025

From libcxx-commits at lists.llvm.org  Thu Jul  2 03:16:13 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 03:16:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a463a6d.c84e7546.46e94.80a8@mx.google.com>


philnik777 wrote:

> > I'm not convinced this is worth the complexity. This adds a workaround for older compilers, so this will be obsolete in a year anyways, and for that it's way too complicated of a fix IMO.
> 
> I agree that introducing `__complete` and `__complete_or_unbounded` solely as a workaround for older compiler bugs is not ideal—the added complexity is hard to justify for a temporary fix.
> 
> That said, I believe these traits could have broader value in libc++ development. They are analogous to `__is_complete_or_unbounded`, which is used extensively in libstdc++ to produce readable static assertions and to help maintainers write robust, self-documenting code. A few examples:
> 
>     * In `std::copyable_function`:
>       https://gcc.gnu.org/git?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/include/bits/funcref_impl.h;h=9fcab570803fd7ab25ceb2556764005243f83aa4;hb=HEAD#l71
> 
>     * In `std::function_ref`:
>       https://gcc.gnu.org/git?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/include/bits/funcref_impl.h;h=8a00fde1b8e54ef20555efac9a9f1406559c9648;hb=HEAD#l72
> 
>     * In `__pass_by_value`:
>       https://gcc.gnu.org/git?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/include/bits/funcwrap.h;h=670efa63b4a242d83953eaf4a832c4998cdac696;hb=HEAD#l220
> 
> 
> @philnik777 Do you think it would be worthwhile to open a separate discussion about adding `__complete` and `__complete_or_unbounded` to `__type_traits` to explore these long-term benefits? I’d be happy to file a new issue if that seems reasonable.

You can, but I'm not convinced they're worth adding. All the type traits are already diagnosed by the compiler, so the application is _very_ limited.

https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Thu Jul  2 03:22:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 03:22:37 -0700 (PDT)
Subject: [libcxx-commits] [clang] [compiler-rt] [libcxx] [Clang] Support
 atomic operations on _BitInt(N) (PR #204815)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204815@github.com>
Message-ID: <6a463bed.c56fb102.11841f.fdc3@mx.google.com>


================
@@ -557,6 +558,183 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
   return Builder.CreateSelect(Cmp, OldVal, RHS, "newval");
 }
 
+/// Classify an atomic op as an arithmetic/bitwise read-modify-write (one that
+/// normally lowers to a single `atomicrmw`), mapping it to the matching
+/// `AtomicRMWInst::BinOp` and reporting whether the builtin returns the new
+/// value (`<op>_fetch`) rather than the old value (`fetch_<op>`). \p IsSigned
+/// selects signed vs unsigned min/max. Returns false for exchange, load, store,
+/// compare-exchange, and any non-RMW op, none of which need the _BitInt loop.
+static bool classifyBitIntRMW(AtomicExpr::AtomicOp Op, bool IsSigned,
+                              llvm::AtomicRMWInst::BinOp &BinOp,
+                              bool &ReturnsNew) {
+  using RMW = llvm::AtomicRMWInst;
+  switch (Op) {
+  case AtomicExpr::AO__c11_atomic_fetch_add:
+  case AtomicExpr::AO__hip_atomic_fetch_add:
+  case AtomicExpr::AO__opencl_atomic_fetch_add:
+  case AtomicExpr::AO__atomic_fetch_add:
+  case AtomicExpr::AO__scoped_atomic_fetch_add:
+    BinOp = RMW::Add, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_add_fetch:
+  case AtomicExpr::AO__scoped_atomic_add_fetch:
+    BinOp = RMW::Add, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_sub:
+  case AtomicExpr::AO__hip_atomic_fetch_sub:
+  case AtomicExpr::AO__opencl_atomic_fetch_sub:
+  case AtomicExpr::AO__atomic_fetch_sub:
+  case AtomicExpr::AO__scoped_atomic_fetch_sub:
+    BinOp = RMW::Sub, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_sub_fetch:
+  case AtomicExpr::AO__scoped_atomic_sub_fetch:
+    BinOp = RMW::Sub, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_and:
+  case AtomicExpr::AO__hip_atomic_fetch_and:
+  case AtomicExpr::AO__opencl_atomic_fetch_and:
+  case AtomicExpr::AO__atomic_fetch_and:
+  case AtomicExpr::AO__scoped_atomic_fetch_and:
+    BinOp = RMW::And, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_and_fetch:
+  case AtomicExpr::AO__scoped_atomic_and_fetch:
+    BinOp = RMW::And, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_or:
+  case AtomicExpr::AO__hip_atomic_fetch_or:
+  case AtomicExpr::AO__opencl_atomic_fetch_or:
+  case AtomicExpr::AO__atomic_fetch_or:
+  case AtomicExpr::AO__scoped_atomic_fetch_or:
+    BinOp = RMW::Or, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_or_fetch:
+  case AtomicExpr::AO__scoped_atomic_or_fetch:
+    BinOp = RMW::Or, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_xor:
+  case AtomicExpr::AO__hip_atomic_fetch_xor:
+  case AtomicExpr::AO__opencl_atomic_fetch_xor:
+  case AtomicExpr::AO__atomic_fetch_xor:
+  case AtomicExpr::AO__scoped_atomic_fetch_xor:
+    BinOp = RMW::Xor, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_xor_fetch:
+  case AtomicExpr::AO__scoped_atomic_xor_fetch:
+    BinOp = RMW::Xor, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_nand:
+  case AtomicExpr::AO__atomic_fetch_nand:
+  case AtomicExpr::AO__scoped_atomic_fetch_nand:
+    BinOp = RMW::Nand, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_nand_fetch:
+  case AtomicExpr::AO__scoped_atomic_nand_fetch:
+    BinOp = RMW::Nand, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_min:
+  case AtomicExpr::AO__hip_atomic_fetch_min:
+  case AtomicExpr::AO__opencl_atomic_fetch_min:
+  case AtomicExpr::AO__atomic_fetch_min:
+  case AtomicExpr::AO__scoped_atomic_fetch_min:
+    BinOp = IsSigned ? RMW::Min : RMW::UMin, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_min_fetch:
+  case AtomicExpr::AO__scoped_atomic_min_fetch:
+    BinOp = IsSigned ? RMW::Min : RMW::UMin, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_max:
+  case AtomicExpr::AO__hip_atomic_fetch_max:
+  case AtomicExpr::AO__opencl_atomic_fetch_max:
+  case AtomicExpr::AO__atomic_fetch_max:
+  case AtomicExpr::AO__scoped_atomic_fetch_max:
+    BinOp = IsSigned ? RMW::Max : RMW::UMax, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_max_fetch:
+  case AtomicExpr::AO__scoped_atomic_max_fetch:
+    BinOp = IsSigned ? RMW::Max : RMW::UMax, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__atomic_fetch_uinc:
+  case AtomicExpr::AO__scoped_atomic_fetch_uinc:
+    BinOp = RMW::UIncWrap, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_fetch_udec:
+  case AtomicExpr::AO__scoped_atomic_fetch_udec:
+    BinOp = RMW::UDecWrap, ReturnsNew = false;
+    return true;
+  default:
+    return false;
+  }
+}
+
+/// True for a `_BitInt(N)` whose value width N differs from its in-memory width
+/// (e.g. `_BitInt(37)` occupies 64 bits), so the high bits are padding.
+static bool hasBitIntPadding(QualType T, const ASTContext &C) {
+  if (const auto *BIT = T->getAs<BitIntType>())
+    return BIT->getNumBits() != C.getTypeSize(T);
+  return false;
+}
+
+/// Map a constant C ABI memory order to an llvm ordering. A non-constant order
+/// is handled conservatively with the strongest ordering.
+static llvm::AtomicOrdering atomicOrderOrSeqCst(llvm::Value *Order) {
+  auto *C = dyn_cast<llvm::ConstantInt>(Order);
+  if (!C || !llvm::isValidAtomicOrderingCABI(C->getZExtValue()))
+    return llvm::AtomicOrdering::SequentiallyConsistent;
+  switch (static_cast<llvm::AtomicOrderingCABI>(C->getZExtValue())) {
+  case llvm::AtomicOrderingCABI::relaxed:
+    return llvm::AtomicOrdering::Monotonic;
+  case llvm::AtomicOrderingCABI::consume:
+  case llvm::AtomicOrderingCABI::acquire:
+    return llvm::AtomicOrdering::Acquire;
+  case llvm::AtomicOrderingCABI::release:
+    return llvm::AtomicOrdering::Release;
+  case llvm::AtomicOrderingCABI::acq_rel:
+    return llvm::AtomicOrdering::AcquireRelease;
+  case llvm::AtomicOrderingCABI::seq_cst:
+    return llvm::AtomicOrdering::SequentiallyConsistent;
+  }
+  llvm_unreachable("invalid CABI ordering");
+}
+
+/// Emit a `_BitInt(N)` atomic read-modify-write as a compare-exchange loop. A
+/// single `atomicrmw` on the padded memory integer would carry into / compare
+/// the padding bits, and no arbitrary-width `__atomic_fetch_*` libcall exists
+/// for wide widths.
+///
+/// The update computes at value width N (so the result wraps mod 2^N and is
+/// independent of padding). EmitAtomicUpdate carries the raw loaded
+/// representation as the cmpxchg expected, so an object with non-canonical
+/// padding (e.g. written through a union) still converges instead of spinning
+/// forever; the desired it writes back is canonical. See P0528.
+static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
+                                      Address Ptr, Address Val1,
+                                      QualType AtomicTy,
+                                      llvm::AtomicRMWInst::BinOp BinOp,
+                                      bool ReturnsNew, llvm::Value *Order) {
+  QualType ValTy = E->getValueType();
+  llvm::AtomicOrdering AO = atomicOrderOrSeqCst(Order);
+
+  LValue AtomicLVal = CGF.MakeAddrLValue(Ptr, AtomicTy);
+  AtomicInfo Atomics(CGF, AtomicLVal);
+
+  llvm::Value *RHS =
+      CGF.EmitLoadOfScalar(CGF.MakeAddrLValue(Val1, ValTy), E->getExprLoc());
+
+  llvm::Value *Old = nullptr, *New = nullptr;
+  Atomics.EmitAtomicUpdate(
+      AO,
+      [&](RValue OldRV) {
+        Old = OldRV.getScalarVal();
+        New = llvm::buildAtomicRMWValue(BinOp, CGF.Builder, Old, RHS);
+        return RValue::get(New);
+      },
+      E->isVolatile());
+
+  return RValue::get(ReturnsNew ? New : Old);
+}
----------------
gonzalobg wrote:

Instead of requiring potentially every frontend to implement this, wouldn't it be better to just extend `atomicrmw` with support for non-power-of-two integers?

https://github.com/llvm/llvm-project/pull/204815

From libcxx-commits at lists.llvm.org  Thu Jul  2 04:22:40 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Thu, 02 Jul 2026 04:22:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Add deleted tuple
 constructor overloads (PR #205379)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205379@github.com>
Message-ID: <6a464a00.eea6ab6e.1256b6.5141@mx.google.com>


https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/205379

>From 865c5a96e4f7699d8ecc5a6317aba888d8795269 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 24 Jun 2026 00:51:43 +0800
Subject: [PATCH 01/12] [libc++] P2255R2: Add deleted tuple constructor
 overloads

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/tuple                          | 247 ++++++++++++++++--
 ...5_tuple_ref_binding_diagnostics.verify.cpp |  24 +-
 .../ref_constructs_from_temporary.verify.cpp  |  74 ++++++
 4 files changed, 309 insertions(+), 38 deletions(-)
 create mode 100644 libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp

diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 739f0ecab37e7..e2e7c679be7b7 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -43,7 +43,7 @@
 "`P0627R6 <https://wg21.link/P0627R6>`__","Function to mark unreachable code","2022-02 (Virtual)","|Complete|","15","`#105175 <https://github.com/llvm/llvm-project/issues/105175>`__",""
 "`P1206R7 <https://wg21.link/P1206R7>`__","``ranges::to``: A function to convert any range to a container","2022-02 (Virtual)","|Complete|","17","`#105176 <https://github.com/llvm/llvm-project/issues/105176>`__",""
 "`P1413R3 <https://wg21.link/P1413R3>`__","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","2022-02 (Virtual)","|Complete|","","`#105177 <https://github.com/llvm/llvm-project/issues/105177>`__","``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but clang doesn't issue a diagnostic for deprecated using template declarations."
-"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","`#105180 <https://github.com/llvm/llvm-project/issues/105180>`__","Implemented the type traits only."
+"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","`#105180 <https://github.com/llvm/llvm-project/issues/105180>`__","Implemented the type traits and changes to tuple constructors only."
 "`P2273R3 <https://wg21.link/P2273R3>`__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16","`#105182 <https://github.com/llvm/llvm-project/issues/105182>`__",""
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 8f43006521dda..912b49c2b97de 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -616,21 +616,55 @@ public:
               _Not<_IsThisTuple<_Up...> >, // extension to allow mis-behaved user constructors
               is_constructible<_Tp, _Up>... > {};
 
-  template <class... _Up,
-            __enable_if_t< _And< _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> >::value,
-                           int> = 0>
+  template <class... _Up>
+  struct _EnableUTypesCtorBase
+      : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
+
+  template <bool, class... _Up>
+  struct _EnableUTypesCtorNoDangling : false_type {};
+
+#    if _LIBCPP_STD_VER >= 23
+  template <bool, class... _Up>
+  struct _CtorCreatesDanglingReference : false_type {};
+
+  template <class... _Up>
+  struct _CtorCreatesDanglingReference<true, _Up...>
+      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, _Up&&> >...> {};
+
+  template <class... _Up>
+  struct _EnableUTypesCtorNoDangling<true, _Up...> : _Not<_CtorCreatesDanglingReference<true, _Up...> > {};
+
+  template <class... _Up>
+  struct _EnableDanglingUTypesCtor : _CtorCreatesDanglingReference<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+#    else
+  template <class... _Up>
+  struct _EnableUTypesCtorNoDangling<true, _Up...> : true_type {};
+#    endif // _LIBCPP_STD_VER >= 23
+
+  template <class... _Up>
+  struct _EnableNonDanglingUTypesCtor
+      : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+
+  template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&... __u) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__forward_args{}, std::forward<_Up>(__u)...) {}
 
-  template <class _Alloc,
-            class... _Up,
-            __enable_if_t< _And< _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> >::value,
-                           int> = 0>
+  template <class _Alloc, class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
       : __base_(allocator_arg_t(), __a, __forward_args{}, std::forward<_Up>(__u)...) {}
 
+#    if _LIBCPP_STD_VER >= 23
+  template <class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
+
+  template <class _Alloc, class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
+#    endif // _LIBCPP_STD_VER >= 23
+
   // Copy and move constructors (including the allocator_arg_t variants)
   tuple(const tuple&) = default;
   tuple(tuple&&)      = default;
@@ -672,58 +706,134 @@ public:
                      _Not<is_convertible<_OtherTuple, _Tp> >...,
                      _Not<is_constructible<_Tp, _OtherTuple> >... > > > {};
 
-  template <class... _Up, __enable_if_t< _And< _EnableCtorFromUTypesTuple<const tuple<_Up...>&> >::value, int> = 0>
+  template <bool, class _OtherTuple>
+  struct _EnableCtorFromUTypesTupleNoDangling : false_type {};
+
+#    if _LIBCPP_STD_VER >= 23
+  template <bool, class _OtherTuple, class _DecayedOtherTuple = __remove_cvref_t<_OtherTuple>, class = void>
+  struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
+
+  template <class _OtherTuple, class... _Up>
+  struct _CtorFromUTypesTupleCreatesDanglingReference<
+      true,
+      _OtherTuple,
+      tuple<_Up...>,
+      __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
+      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
+
+  template <class _OtherTuple>
+  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple>
+      : _Not<_CtorFromUTypesTupleCreatesDanglingReference<true, _OtherTuple> > {};
+
+  template <class _OtherTuple>
+  struct _EnableDanglingCtorFromUTypesTuple
+      : _CtorFromUTypesTupleCreatesDanglingReference<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+#    else
+  template <class _OtherTuple>
+  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple> : true_type {};
+#    endif // _LIBCPP_STD_VER >= 23
+
+  template <class _OtherTuple>
+  struct _EnableNonDanglingCtorFromUTypesTuple
+      : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+
+  template <class... _Up,
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
       : __base_(__from_tuple(), __t) {}
 
   template <class... _Up,
             class _Alloc,
-            __enable_if_t< _And< _EnableCtorFromUTypesTuple<const tuple<_Up...>&> >::value, int> = 0>
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class... _Up,
+            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+      tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
+
+  template <class... _Up,
+            class _Alloc,
+            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
+
   // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>& __t)
       : __base_(__from_tuple(), __t) {}
 
-  template <class _Alloc, class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
+
+  template <class _Alloc,
+            class... _Up,
+            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __t) {}
+
+  template <class _Alloc,
+            class... _Up,
+            enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
-  template <class... _Up, __enable_if_t< _And< _EnableCtorFromUTypesTuple<tuple<_Up...>&&> >::value, int> = 0>
+  template <class... _Up,
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__from_tuple(), std::move(__t)) {}
 
   template <class _Alloc,
             class... _Up,
-            __enable_if_t< _And< _EnableCtorFromUTypesTuple<tuple<_Up...>&&> >::value, int> = 0>
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__t)) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
+
+  template <class _Alloc,
+            class... _Up,
+            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
+
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up,
+            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&& __t)
       : __base_(__from_tuple(), std::move(__t)) {}
 
+  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+      tuple(const tuple<_Up...>&&) = delete;
+
   template <class _Alloc,
             class... _Up,
-            enable_if_t< _EnableCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__t)) {}
+
+  template <class _Alloc,
+            class... _Up,
+            enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(const pair<U1, U2>&) constructors (including allocator_arg_t variants)
@@ -741,6 +851,34 @@ public:
   template <class _Pair>
   struct _EnableCtorFromPair : _CtorPredicateFromPair<is_constructible, _Pair> {};
 
+  template <bool, class _Pair>
+  struct _EnableCtorFromPairNoDangling : false_type {};
+
+#    if _LIBCPP_STD_VER >= 23
+  template <bool, class _Pair, class _DecayedPair = __remove_cvref_t<_Pair>, class _Tuple = tuple>
+  struct _CtorFromPairCreatesDanglingReference : false_type {};
+
+  template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
+  struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
+      : _Or<
+            _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
+            _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
+
+  template <class _Pair>
+  struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
+
+  template <class _Pair>
+  struct _EnableDanglingCtorFromPair
+      : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+#    else
+  template <class _Pair>
+  struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
+#    endif // _LIBCPP_STD_VER >= 23
+
+  template <class _Pair>
+  struct _EnableNonDanglingCtorFromPair
+      : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+
   template <class _Pair>
   struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
 
@@ -754,7 +892,7 @@ public:
   template <class _Up1,
             class _Up2,
             template <class...> class _And                                                   = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>& __p) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value)
@@ -764,35 +902,64 @@ public:
             class _Up1,
             class _Up2,
             template <class...> class _And                                                   = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, const pair<_Up1, _Up2>& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __p) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class _Up1,
+            class _Up2,
+            template <class...> class _And                                                    = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+      tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) =
+          delete;
+
+  template <class _Alloc,
+            class _Up1,
+            class _Up2,
+            template <class...> class _And                                                    = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+      tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
+
   // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
 
-  template <class _U1, class _U2, enable_if_t< _EnableCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _U1, class _U2, enable_if_t< _EnableNonDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(pair<_U1, _U2>& __p)
       : __base_(__from_tuple(), __p) {}
 
+  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+      tuple(pair<_U1, _U2>&) = delete;
+
   template <class _Alloc,
             class _U1,
             class _U2,
-            enable_if_t< _EnableCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+            enable_if_t< _EnableNonDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, pair<_U1, _U2>& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __p) {}
-#    endif
+
+  template <class _Alloc,
+            class _U1,
+            class _U2,
+            enable_if_t< _EnableDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+      tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
+#    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(pair<U1, U2>&&) constructors (including allocator_arg_t variants)
 
   template <class _Up1,
             class _Up2,
             template <class...> class _And                                              = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&& __p) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value)
@@ -802,27 +969,59 @@ public:
             class _Up1,
             class _Up2,
             template <class...> class _And                                              = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, pair<_Up1, _Up2>&& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__p)) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class _Up1,
+            class _Up2,
+            template <class...> class _And                                               = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+      tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
+
+  template <class _Alloc,
+            class _Up1,
+            class _Up2,
+            template <class...> class _And                                               = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+      tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
+
   // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
 
-  template <class _U1, class _U2, enable_if_t< _EnableCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1,
+            class _U2,
+            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&& __p)
       : __base_(__from_tuple(), std::move(__p)) {}
 
+  template <class _U1,
+            class _U2,
+            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+      tuple(const pair<_U1, _U2>&&) = delete;
+
   template <class _Alloc,
             class _U1,
             class _U2,
-            enable_if_t< _EnableCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const pair<_U1, _U2>&& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__p)) {}
+
+  template <class _Alloc,
+            class _U1,
+            class _U2,
+            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+      tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // [tuple.assign]
diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index a1a80483c4487..a44a38e3e17ed 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03
+// REQUIRES: std-at-least-c++23
 
 // <tuple>
 
@@ -39,36 +40,33 @@ template <class ...Args>
 void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
 
 void f() {
-  // Test that we emit our diagnostic from the library.
-  // expected-error at tuple:* 8 {{Attempted construction of reference element binds to a temporary whose lifetime has ended}}
-
-  // Good news everybody! Clang now diagnoses this for us!
-  // expected-error at tuple:* 0+ {{reference member '__value_' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+  // Test that the public constructors are deleted when constructing a reference
+  // element would bind to a temporary.
 
   {
-    F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-note 1 {{requested here}}
+    F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-error {{deleted}}
   }
   {
-    std::tuple<int, const std::string&> t(1, "a"); // expected-note 1 {{requested here}}
+    std::tuple<int, const std::string&> t(1, "a"); // expected-error {{deleted}}
   }
   {
-    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-note 1 {{requested here}}
+    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-error {{deleted}}
   }
   {
     ConvertsTo<int&> ct;
-    std::tuple<const long&, int> t(ct, 42); // expected-note {{requested here}}
+    std::tuple<const long&, int> t(ct, 42); // expected-error {{deleted}}
   }
   {
     ConvertsTo<int> ct;
-    std::tuple<int const&, void*> t(ct, nullptr); // expected-note {{requested here}}
+    std::tuple<int const&, void*> t(ct, nullptr); // expected-error {{deleted}}
   }
   {
     ConvertsTo<Derived> ct;
-    std::tuple<Base const&, int> t(ct, 42); // expected-note {{requested here}}
+    std::tuple<Base const&, int> t(ct, 42); // expected-error {{deleted}}
   }
   {
     std::allocator<int> alloc;
-    std::tuple<std::string &&> t2("hello"); // expected-note {{requested here}}
-    std::tuple<std::string &&> t3(std::allocator_arg, alloc, "hello"); // expected-note {{requested here}}
+    std::tuple<std::string&&> t2("hello"); // expected-error {{deleted}}
+    std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
   }
 }
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp
new file mode 100644
index 0000000000000..c7a5f4e247f3b
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <tuple>
+
+// tuple(UTypes&&... u);
+// tuple(tuple<UTypes...>& u);
+// tuple(const tuple<UTypes...>& u);
+// tuple(tuple<UTypes...>&& u);
+// tuple(const tuple<UTypes...>&& u);
+// tuple(pair<U1, U2>& u);
+// tuple(const pair<U1, U2>& u);
+// tuple(pair<U1, U2>&& u);
+// tuple(const pair<U1, U2>&& u);
+//
+// The constructors above are defined as deleted if reference_constructs_from_temporary_v
+// is true for one of the corresponding tuple elements.
+
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+struct X {
+  X(int);
+};
+
+static_assert(std::is_constructible_v<std::tuple<const X&>, X&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, int>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, std::tuple<int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, const std::tuple<int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, std::tuple<int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, const std::tuple<int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, std::pair<int, int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, const std::pair<int, int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, std::pair<int, int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, const std::pair<int, int>&&>);
+
+void test() {
+  std::allocator<int> alloc;
+  std::tuple<int> t(1);
+  const std::tuple<int> ct(1);
+  std::pair<int, int> p(1, 2);
+  const std::pair<int, int> cp(1, 2);
+
+  // expected-error-re@*:* 18 {{call to deleted constructor of 'std::tuple<{{.*}}>'}}
+  std::tuple<const X&> utypes(1);
+  std::tuple<const X&> alloc_utypes(std::allocator_arg, alloc, 1);
+
+  std::tuple<const X&> tuple_lvalue(t);
+  std::tuple<const X&> alloc_tuple_lvalue(std::allocator_arg, alloc, t);
+  std::tuple<const X&> const_tuple_lvalue(ct);
+  std::tuple<const X&> alloc_const_tuple_lvalue(std::allocator_arg, alloc, ct);
+  std::tuple<const X&> tuple_rvalue(std::tuple<int>{1});
+  std::tuple<const X&> alloc_tuple_rvalue(std::allocator_arg, alloc, std::tuple<int>{1});
+  std::tuple<const X&> const_tuple_rvalue(std::move(ct));
+  std::tuple<const X&> alloc_const_tuple_rvalue(std::allocator_arg, alloc, std::move(ct));
+
+  std::tuple<const X&, int> pair_lvalue(p);
+  std::tuple<const X&, int> alloc_pair_lvalue(std::allocator_arg, alloc, p);
+  std::tuple<const X&, int> const_pair_lvalue(cp);
+  std::tuple<const X&, int> alloc_const_pair_lvalue(std::allocator_arg, alloc, cp);
+  std::tuple<const X&, int> pair_rvalue(std::pair<int, int>{1, 2});
+  std::tuple<const X&, int> alloc_pair_rvalue(std::allocator_arg, alloc, std::pair<int, int>{1, 2});
+  std::tuple<const X&, int> const_pair_rvalue(std::move(cp));
+  std::tuple<const X&, int> alloc_const_pair_rvalue(std::allocator_arg, alloc, std::move(cp));
+}

>From 9d2d66b76aba200e90d0c67b487c13abb81f585e Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 24 Jun 2026 00:56:58 +0800
Subject: [PATCH 02/12] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple                          | 62 ++++++++-----------
 ...5_tuple_ref_binding_diagnostics.verify.cpp |  2 +-
 2 files changed, 26 insertions(+), 38 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 912b49c2b97de..edeb97aa9de80 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -617,8 +617,7 @@ public:
               is_constructible<_Tp, _Up>... > {};
 
   template <class... _Up>
-  struct _EnableUTypesCtorBase
-      : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
+  struct _EnableUTypesCtorBase : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
 
   template <bool, class... _Up>
   struct _EnableUTypesCtorNoDangling : false_type {};
@@ -642,8 +641,7 @@ public:
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class... _Up>
-  struct _EnableNonDanglingUTypesCtor
-      : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+  struct _EnableNonDanglingUTypesCtor : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
 
   template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
@@ -714,11 +712,10 @@ public:
   struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
 
   template <class _OtherTuple, class... _Up>
-  struct _CtorFromUTypesTupleCreatesDanglingReference<
-      true,
-      _OtherTuple,
-      tuple<_Up...>,
-      __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
+  struct _CtorFromUTypesTupleCreatesDanglingReference< true,
+                                                       _OtherTuple,
+                                                       tuple<_Up...>,
+                                                       __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
       : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
 
   template <class _OtherTuple>
@@ -737,8 +734,7 @@ public:
   struct _EnableNonDanglingCtorFromUTypesTuple
       : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
 
-  template <class... _Up,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
       : __base_(__from_tuple(), __t) {}
@@ -751,8 +747,7 @@ public:
       : __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up,
-            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
 
@@ -786,8 +781,7 @@ public:
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
-  template <class... _Up,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__from_tuple(), std::move(__t)) {}
@@ -812,8 +806,7 @@ public:
 
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up,
-            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&& __t)
       : __base_(__from_tuple(), std::move(__t)) {}
@@ -860,24 +853,22 @@ public:
 
   template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
   struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
-      : _Or<
-            _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
-            _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
+      : _Or< _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
+             _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
 
   template <class _Pair>
   struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
 
   template <class _Pair>
-  struct _EnableDanglingCtorFromPair
-      : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+  struct _EnableDanglingCtorFromPair : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {
+  };
 #    else
   template <class _Pair>
   struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _Pair>
-  struct _EnableNonDanglingCtorFromPair
-      : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+  struct _EnableNonDanglingCtorFromPair : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
 
   template <class _Pair>
   struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
@@ -891,7 +882,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                   = _And,
+            template <class...> class _And                                                              = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -901,7 +892,7 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                   = _And,
+            template <class...> class _And                                                              = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -911,17 +902,16 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                    = _And,
+            template <class...> class _And                                                            = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
-      tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) =
-          delete;
+      tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
 
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                    = _And,
+            template <class...> class _And                                                            = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -958,7 +948,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                              = _And,
+            template <class...> class _And                                                         = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -968,7 +958,7 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                              = _And,
+            template <class...> class _And                                                         = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -978,7 +968,7 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                               = _And,
+            template <class...> class _And                                                       = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -987,7 +977,7 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                               = _And,
+            template <class...> class _And                                                       = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -1002,9 +992,7 @@ public:
       tuple(const pair<_U1, _U2>&& __p)
       : __base_(__from_tuple(), std::move(__p)) {}
 
-  template <class _U1,
-            class _U2,
-            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&&) = delete;
 
diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index a44a38e3e17ed..15d032fbb3a7a 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -66,7 +66,7 @@ void f() {
   }
   {
     std::allocator<int> alloc;
-    std::tuple<std::string&&> t2("hello"); // expected-error {{deleted}}
+    std::tuple<std::string&&> t2("hello");                            // expected-error {{deleted}}
     std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
   }
 }

>From 8520af98734cd4100ebad837b70a7f69985fb937 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 09:41:49 -0700
Subject: [PATCH 03/12] [libc++] Refine the implementation use template
 variable and make reference_constructs_from_temporary SFINAE friendly

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../reference_constructs_from_temporary.h     |  15 +-
 libcxx/include/tuple                          | 240 ++++++++----------
 2 files changed, 126 insertions(+), 129 deletions(-)

diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index a8325620414ea..c18723fb77d81 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -11,6 +11,7 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_reference.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -30,8 +31,20 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_tempo
 
 #endif
 
+// A non-reference type can never bind to a temporary, so the result is always `false` for such a
+// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
+// eagerly instantiates the construction of `_Up` (including the element's constructor exception
+// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
+//
+// https://godbolt.org/z/WMTK5cMa4
+//
+// TODO: remove this guard once the Clang builtin short-circuits on a non-reference first operand.
+template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
+inline const bool __reference_constructs_from_temporary_v = false;
+
 template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
+inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
+    __reference_constructs_from_temporary(_Tp, _Up);
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index edeb97aa9de80..609cdf1f93dc4 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -623,42 +623,60 @@ public:
   struct _EnableUTypesCtorNoDangling : false_type {};
 
 #    if _LIBCPP_STD_VER >= 23
-  template <bool, class... _Up>
-  struct _CtorCreatesDanglingReference : false_type {};
-
   template <class... _Up>
-  struct _CtorCreatesDanglingReference<true, _Up...>
-      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, _Up&&> >...> {};
+  static constexpr bool __has_danglings() {
+    // The fold expands `_Tp` and `_Up` in lockstep, so it is only well-formed when the packs have
+    // equal length. The discarded branch is not instantiated when the lengths differ.
+    if constexpr (sizeof...(_Up) == sizeof...(_Tp))
+      return (__reference_constructs_from_temporary_v<_Tp, _Up&&> || ...);
+    else
+      return false;
+  }
 
-  template <class... _Up>
-  struct _EnableUTypesCtorNoDangling<true, _Up...> : _Not<_CtorCreatesDanglingReference<true, _Up...> > {};
+  template <class _Tuple,
+            class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+  static inline constexpr bool __has_danglings_from_tuple_like = false;
+
+  template <class _Tuple, size_t... _Idx>
+  static inline constexpr bool __has_danglings_from_tuple_like<_Tuple, index_sequence<_Idx...>,
+    enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
 
   template <class... _Up>
-  struct _EnableDanglingUTypesCtor : _CtorCreatesDanglingReference<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
-#    else
+  static inline constexpr bool __has_danglings_from_utypes =
+      _EnableUTypesCtorBase<_Up...>::value && __has_danglings<_Up...>();
+
   template <class... _Up>
-  struct _EnableUTypesCtorNoDangling<true, _Up...> : true_type {};
+  static inline constexpr bool __has_no_danglings_from_utypes =
+      _EnableUTypesCtorBase<_Up...>::value && !__has_danglings<_Up...>();
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class... _Up>
-  struct _EnableNonDanglingUTypesCtor : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+  static inline constexpr bool __enable_utypes_constructor_no_dangling() {
+#    if _LIBCPP_STD_VER >= 23
+    return __has_no_danglings_from_utypes<_Up...>;
+#    else
+    return _EnableUTypesCtorBase<_Up...>::value;
+#    endif // _LIBCPP_STD_VER >= 23
+  }
 
-  template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
+  template <class... _Up, __enable_if_t< __enable_utypes_constructor_no_dangling<_Up...>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&... __u) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__forward_args{}, std::forward<_Up>(__u)...) {}
 
-  template <class _Alloc, class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
+  template <class _Alloc, class... _Up, __enable_if_t< __enable_utypes_constructor_no_dangling<_Up...>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
       : __base_(allocator_arg_t(), __a, __forward_args{}, std::forward<_Up>(__u)...) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  template <class... _Up>
+    requires __has_danglings_from_utypes<_Up...>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
-  template <class _Alloc, class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes<_Up...>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
@@ -708,123 +726,116 @@ public:
   struct _EnableCtorFromUTypesTupleNoDangling : false_type {};
 
 #    if _LIBCPP_STD_VER >= 23
-  template <bool, class _OtherTuple, class _DecayedOtherTuple = __remove_cvref_t<_OtherTuple>, class = void>
-  struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
-
-  template <class _OtherTuple, class... _Up>
-  struct _CtorFromUTypesTupleCreatesDanglingReference< true,
-                                                       _OtherTuple,
-                                                       tuple<_Up...>,
-                                                       __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
-      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
-
   template <class _OtherTuple>
-  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple>
-      : _Not<_CtorFromUTypesTupleCreatesDanglingReference<true, _OtherTuple> > {};
+  static inline constexpr bool __has_danglings_from_utypes_tuple =
+      _EnableCtorFromUTypesTuple<_OtherTuple>::value && __has_danglings_from_tuple_like<_OtherTuple>;
 
   template <class _OtherTuple>
-  struct _EnableDanglingCtorFromUTypesTuple
-      : _CtorFromUTypesTupleCreatesDanglingReference<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
-#    else
-  template <class _OtherTuple>
-  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple> : true_type {};
+  static inline constexpr bool __has_no_danglings_from_utypes_tuple =
+      _EnableCtorFromUTypesTuple<_OtherTuple>::value && !__has_danglings_from_tuple_like<_OtherTuple>;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _OtherTuple>
-  struct _EnableNonDanglingCtorFromUTypesTuple
-      : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+  static inline constexpr bool __enable_utypes_tuple_constructor_no_dangling() {
+#    if _LIBCPP_STD_VER >= 23
+    return __has_no_danglings_from_utypes_tuple<_OtherTuple>;
+#    else
+    return _EnableCtorFromUTypesTuple<_OtherTuple>::value;
+#    endif // _LIBCPP_STD_VER >= 23
+  }
 
-  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+  template <class... _Up,
+            __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<const tuple<_Up...>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
       : __base_(__from_tuple(), __t) {}
 
   template <class... _Up,
             class _Alloc,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+            __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<const tuple<_Up...>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
 
-  template <class... _Up,
-            class _Alloc,
-            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  template <class... _Up, class _Alloc>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
 
   // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>& __t)
       : __base_(__from_tuple(), __t) {}
 
-  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __t) {}
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
-  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+  template <class... _Up, __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<tuple<_Up...>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__from_tuple(), std::move(__t)) {}
 
   template <class _Alloc,
             class... _Up,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+            __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<tuple<_Up...>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__t)) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
-  template <class _Alloc,
-            class... _Up,
-            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
 
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&& __t)
       : __base_(__from_tuple(), std::move(__t)) {}
 
-  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&&) = delete;
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__t)) {}
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
@@ -844,31 +855,24 @@ public:
   template <class _Pair>
   struct _EnableCtorFromPair : _CtorPredicateFromPair<is_constructible, _Pair> {};
 
-  template <bool, class _Pair>
-  struct _EnableCtorFromPairNoDangling : false_type {};
-
 #    if _LIBCPP_STD_VER >= 23
-  template <bool, class _Pair, class _DecayedPair = __remove_cvref_t<_Pair>, class _Tuple = tuple>
-  struct _CtorFromPairCreatesDanglingReference : false_type {};
-
-  template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
-  struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
-      : _Or< _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
-             _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
-
   template <class _Pair>
-  struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
+  static inline constexpr bool __has_danglings_from_utypes_pair =
+      _EnableCtorFromPair<_Pair>::value && __has_danglings_from_tuple_like<_Pair>;
 
   template <class _Pair>
-  struct _EnableDanglingCtorFromPair : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {
-  };
-#    else
-  template <class _Pair>
-  struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
+  static inline constexpr bool __has_no_danglings_from_utypes_pair =
+      _EnableCtorFromPair<_Pair>::value && !__has_danglings_from_tuple_like<_Pair>;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _Pair>
-  struct _EnableNonDanglingCtorFromPair : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+  static inline constexpr bool __enable_utypes_pair_constructor_no_dangling() {
+#    if _LIBCPP_STD_VER >= 23
+    return _EnableCtorFromPair<_Pair>::value && __has_no_danglings_from_utypes_pair<_Pair>;
+#    else
+    return _EnableCtorFromPair<_Pair>::value;
+#    endif // _LIBCPP_STD_VER >= 23
+  }
 
   template <class _Pair>
   struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
@@ -882,8 +886,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                              = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t<__enable_utypes_pair_constructor_no_dangling<const pair<_Up1, _Up2>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>& __p) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value)
@@ -892,54 +895,46 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                              = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t<__enable_utypes_pair_constructor_no_dangling<const pair<_Up1, _Up2>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, const pair<_Up1, _Up2>& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __p) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class _Up1,
-            class _Up2,
-            template <class...> class _And                                                            = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  template <class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
 
-  template <class _Alloc,
-            class _Up1,
-            class _Up2,
-            template <class...> class _And                                                            = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  template <class _Alloc, class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
 
   // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
 
-  template <class _U1, class _U2, enable_if_t< _EnableNonDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(pair<_U1, _U2>& __p)
       : __base_(__from_tuple(), __p) {}
 
-  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(pair<_U1, _U2>&) = delete;
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableNonDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, pair<_U1, _U2>& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __p) {}
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
@@ -948,8 +943,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                         = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< __enable_utypes_pair_constructor_no_dangling<pair<_Up1, _Up2>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&& __p) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value)
@@ -958,56 +952,46 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                         = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< __enable_utypes_pair_constructor_no_dangling<pair<_Up1, _Up2>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, pair<_Up1, _Up2>&& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__p)) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class _Up1,
-            class _Up2,
-            template <class...> class _And                                                       = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  template <class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
 
-  template <class _Alloc,
-            class _Up1,
-            class _Up2,
-            template <class...> class _And                                                       = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  template <class _Alloc, class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
 
   // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
 
-  template <class _U1,
-            class _U2,
-            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&& __p)
       : __base_(__from_tuple(), std::move(__p)) {}
 
-  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&&) = delete;
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const pair<_U1, _U2>&& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__p)) {}
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23

>From f8ba2c9c885db176fef5158cd1602b9beaa293ac Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 09:46:03 -0700
Subject: [PATCH 04/12] [libc++] Enable test for all language modes

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index 15d032fbb3a7a..0e9992194f03c 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03
-// REQUIRES: std-at-least-c++23
 
 // <tuple>
 

>From 3d87818fe02261235ef78f2f638b5a6dba300edb Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:06:24 -0700
Subject: [PATCH 05/12] Remove unnecessary for deleted ctors

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 46 +++++++++++++++++---------------------------
 1 file changed, 18 insertions(+), 28 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 609cdf1f93dc4..687192b6ffead 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -672,13 +672,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
     requires __has_danglings_from_utypes<_Up...>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes<_Up...>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
-      tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // Copy and move constructors (including the allocator_arg_t variants)
@@ -760,12 +759,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+  explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
 
   template <class... _Up, class _Alloc>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+  explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
 
   // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
@@ -777,7 +776,7 @@ public:
 
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
+  explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
@@ -787,8 +786,7 @@ public:
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
-      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
+  explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
@@ -807,13 +805,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
-      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
 
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
@@ -825,8 +822,7 @@ public:
 
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
-      tuple(const tuple<_Up...>&&) = delete;
+  explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value) tuple(const tuple<_Up...>&&) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
@@ -836,7 +832,7 @@ public:
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+  explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
@@ -904,14 +900,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
 
   template <class _Alloc, class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
 
   // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
@@ -924,8 +918,7 @@ public:
 
   template <class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<pair<_U1, _U2>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
-      tuple(pair<_U1, _U2>&) = delete;
+  explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value) tuple(pair<_U1, _U2>&) = delete;
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_no_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
@@ -935,7 +928,7 @@ public:
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+  explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
@@ -961,14 +954,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
 
   template <class _Alloc, class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
 
   // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
@@ -981,8 +972,7 @@ public:
 
   template <class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
-      tuple(const pair<_U1, _U2>&&) = delete;
+  explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value) tuple(const pair<_U1, _U2>&&) = delete;
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
@@ -992,7 +982,7 @@ public:
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+  explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 

>From 8ff04dd78e88eafd4876196dd6c88a02b061da0f Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:13:12 -0700
Subject: [PATCH 06/12] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 687192b6ffead..7f444a5333f75 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -633,13 +633,14 @@ public:
       return false;
   }
 
-  template <class _Tuple,
-            class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+  template <class _Tuple, class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
   static inline constexpr bool __has_danglings_from_tuple_like = false;
 
   template <class _Tuple, size_t... _Idx>
-  static inline constexpr bool __has_danglings_from_tuple_like<_Tuple, index_sequence<_Idx...>,
-    enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
+  static inline constexpr bool __has_danglings_from_tuple_like<
+      _Tuple,
+      index_sequence<_Idx...>,
+      enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
 
   template <class... _Up>
   static inline constexpr bool __has_danglings_from_utypes =

>From bb0fa0f1cbee157e39dd52b254da55b2bb59fa5d Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:20:32 -0700
Subject: [PATCH 07/12] Remove unnecessary comments

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 7f444a5333f75..22641af8f494c 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -625,8 +625,6 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
   static constexpr bool __has_danglings() {
-    // The fold expands `_Tp` and `_Up` in lockstep, so it is only well-formed when the packs have
-    // equal length. The discarded branch is not instantiated when the lengths differ.
     if constexpr (sizeof...(_Up) == sizeof...(_Tp))
       return (__reference_constructs_from_temporary_v<_Tp, _Up&&> || ...);
     else

>From 70175b01fa43cb47b44ef3974124e5d3e8366476 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 00:06:59 -0700
Subject: [PATCH 08/12] [libc++] Fix hard error in deleted dangling ctor
 constraints

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 22641af8f494c..0a95aa635663d 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -640,13 +640,25 @@ public:
       index_sequence<_Idx...>,
       enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
 
+  template <bool __dangles, class _CtorEnabled>
+  static inline constexpr bool __check_danglings_from_constructor = false;
+
+  template <class _CtorEnabled>
+  static inline constexpr bool __check_danglings_from_constructor<true, _CtorEnabled> = _CtorEnabled::value;
+
+  template <bool __dangles, class _CtorEnabled>
+  static inline constexpr bool __check_no_danglings_from_constructor = _CtorEnabled::value;
+
+  template <class _CtorEnabled>
+  static inline constexpr bool __check_no_danglings_from_constructor<true, _CtorEnabled> = false;
+
   template <class... _Up>
   static inline constexpr bool __has_danglings_from_utypes =
-      _EnableUTypesCtorBase<_Up...>::value && __has_danglings<_Up...>();
+      __check_danglings_from_constructor<__has_danglings<_Up...>(), _EnableUTypesCtorBase<_Up...> >;
 
   template <class... _Up>
   static inline constexpr bool __has_no_danglings_from_utypes =
-      _EnableUTypesCtorBase<_Up...>::value && !__has_danglings<_Up...>();
+      __check_no_danglings_from_constructor<__has_danglings<_Up...>(), _EnableUTypesCtorBase<_Up...> >;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class... _Up>
@@ -726,11 +738,13 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _OtherTuple>
   static inline constexpr bool __has_danglings_from_utypes_tuple =
-      _EnableCtorFromUTypesTuple<_OtherTuple>::value && __has_danglings_from_tuple_like<_OtherTuple>;
+      __check_danglings_from_constructor<__has_danglings_from_tuple_like<_OtherTuple>,
+                                         _EnableCtorFromUTypesTuple<_OtherTuple> >;
 
   template <class _OtherTuple>
   static inline constexpr bool __has_no_danglings_from_utypes_tuple =
-      _EnableCtorFromUTypesTuple<_OtherTuple>::value && !__has_danglings_from_tuple_like<_OtherTuple>;
+      __check_no_danglings_from_constructor<__has_danglings_from_tuple_like<_OtherTuple>,
+                                            _EnableCtorFromUTypesTuple<_OtherTuple> >;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _OtherTuple>
@@ -853,11 +867,11 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Pair>
   static inline constexpr bool __has_danglings_from_utypes_pair =
-      _EnableCtorFromPair<_Pair>::value && __has_danglings_from_tuple_like<_Pair>;
+      __check_danglings_from_constructor<__has_danglings_from_tuple_like<_Pair>, _EnableCtorFromPair<_Pair> >;
 
   template <class _Pair>
   static inline constexpr bool __has_no_danglings_from_utypes_pair =
-      _EnableCtorFromPair<_Pair>::value && !__has_danglings_from_tuple_like<_Pair>;
+      __check_no_danglings_from_constructor<__has_danglings_from_tuple_like<_Pair>, _EnableCtorFromPair<_Pair> >;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _Pair>

>From d59c4503c1096aac398768060bb6fda5809325cf Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 00:57:48 -0700
Subject: [PATCH 09/12] Fix modules build and don't depends tuple_size_v

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 0a95aa635663d..a2a72cc352f86 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -631,14 +631,16 @@ public:
       return false;
   }
 
-  template <class _Tuple, class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+  template <class _Source, class _DecayedSource = __remove_cvref_t<_Source> >
   static inline constexpr bool __has_danglings_from_tuple_like = false;
 
-  template <class _Tuple, size_t... _Idx>
-  static inline constexpr bool __has_danglings_from_tuple_like<
-      _Tuple,
-      index_sequence<_Idx...>,
-      enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
+  template <class _Source, class... _Up>
+  static inline constexpr bool __has_danglings_from_tuple_like<_Source, tuple<_Up...> > =
+      __has_danglings<__copy_cvref_t<_Source, _Up>...>();
+
+  template <class _Source, class _U1, class _U2>
+  static inline constexpr bool __has_danglings_from_tuple_like<_Source, pair<_U1, _U2> > =
+      __has_danglings<__copy_cvref_t<_Source, _U1>, __copy_cvref_t<_Source, _U2> >();
 
   template <bool __dangles, class _CtorEnabled>
   static inline constexpr bool __check_danglings_from_constructor = false;

>From 958f25ad57ee72b3ef853b69856e04be8cba2477 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 02:36:45 -0700
Subject: [PATCH 10/12] Fix test

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 ...5_tuple_ref_binding_diagnostics.verify.cpp | 34 ++++++++++---------
 1 file changed, 18 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index 0e9992194f03c..3c2cee9f16153 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -39,33 +39,35 @@ template <class ...Args>
 void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
 
 void f() {
-  // Test that the public constructors are deleted when constructing a reference
-  // element would bind to a temporary.
+  // Constructing a reference element that would bind to a temporary is rejected. Since C++23
+  // ([tuple.cnstr]) the offending constructors are deleted, so the error is reported at the call;
+  // before C++23 the bind is caught by a static_assert in the library (with Clang additionally
+  // diagnosing the dangling reference member).
+#if TEST_STD_VER >= 23
+  // expected-error@*:* 8 {{deleted}}
+#else
+  // expected-error at tuple:* 8 {{Attempted construction of reference element binds to a temporary whose lifetime has ended}}
+  // expected-error at tuple:* 0+ {{reference member '__value_' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#endif
 
-  {
-    F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-error {{deleted}}
-  }
-  {
-    std::tuple<int, const std::string&> t(1, "a"); // expected-error {{deleted}}
-  }
-  {
-    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-error {{deleted}}
-  }
+  { F<int, const std::string&>(std::make_tuple(1, "abc")); }
+  { std::tuple<int, const std::string&> t(1, "a"); }
+  { F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); }
   {
     ConvertsTo<int&> ct;
-    std::tuple<const long&, int> t(ct, 42); // expected-error {{deleted}}
+    std::tuple<const long&, int> t(ct, 42);
   }
   {
     ConvertsTo<int> ct;
-    std::tuple<int const&, void*> t(ct, nullptr); // expected-error {{deleted}}
+    std::tuple<int const&, void*> t(ct, nullptr);
   }
   {
     ConvertsTo<Derived> ct;
-    std::tuple<Base const&, int> t(ct, 42); // expected-error {{deleted}}
+    std::tuple<Base const&, int> t(ct, 42);
   }
   {
     std::allocator<int> alloc;
-    std::tuple<std::string&&> t2("hello");                            // expected-error {{deleted}}
-    std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
+    std::tuple<std::string&&> t2("hello");
+    std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello");
   }
 }

>From 2979a29eea2fdbe5591a618ace2c28b81b97cc75 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 03:38:32 -0700
Subject: [PATCH 11/12] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../PR20855_tuple_ref_binding_diagnostics.verify.cpp | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index 3c2cee9f16153..26fc99ae7bbac 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -50,9 +50,15 @@ void f() {
   // expected-error at tuple:* 0+ {{reference member '__value_' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
 #endif
 
-  { F<int, const std::string&>(std::make_tuple(1, "abc")); }
-  { std::tuple<int, const std::string&> t(1, "a"); }
-  { F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); }
+  {
+    F<int, const std::string&>(std::make_tuple(1, "abc"));
+  }
+  {
+    std::tuple<int, const std::string&> t(1, "a");
+  }
+  {
+    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc"));
+  }
   {
     ConvertsTo<int&> ct;
     std::tuple<const long&, int> t(ct, 42);

>From d3a513dc07967195de656085104a942d5e84db31 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 2 Jul 2026 04:22:09 -0700
Subject: [PATCH 12/12] Avoid workaround for
 reference_constructs_from_temporary builtin, it's fixed in clang-23; we will
 skip the related test for older version clang

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../reference_constructs_from_temporary.h         | 15 +--------------
 .../PR23256_constrain_UTypes_ctor.pass.cpp        |  5 +++++
 .../tuple.cnstr/convert_const_move.pass.cpp       |  7 ++++++-
 .../tuple/tuple.tuple/tuple.cnstr/move.pass.cpp   |  4 ++++
 4 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index c18723fb77d81..a8325620414ea 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -11,7 +11,6 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
-#include <__type_traits/is_reference.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -31,20 +30,8 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_tempo
 
 #endif
 
-// A non-reference type can never bind to a temporary, so the result is always `false` for such a
-// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
-// eagerly instantiates the construction of `_Up` (including the element's constructor exception
-// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
-//
-// https://godbolt.org/z/WMTK5cMa4
-//
-// TODO: remove this guard once the Clang builtin short-circuits on a non-reference first operand.
-template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
-inline const bool __reference_constructs_from_temporary_v = false;
-
 template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
-    __reference_constructs_from_temporary(_Tp, _Up);
+inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
index ebc31ce01cfc7..893987ce5f587 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
@@ -64,6 +64,9 @@ struct ExplicitUnconstrainedCtor {
 int main(int, char**) {
     typedef UnconstrainedCtor A;
     typedef ExplicitUnconstrainedCtor ExplicitA;
+
+// Remove this guard when compiler versions older than clang 23 are no longer supported.
+#if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
     {
         static_assert(std::is_copy_constructible<std::tuple<A>>::value, "");
         static_assert(std::is_move_constructible<std::tuple<A>>::value, "");
@@ -92,6 +95,8 @@ int main(int, char**) {
             std::tuple<ExplicitA> &&
         >::value, "");
     }
+#endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+
     {
         std::tuple<A&&> t(std::forward_as_tuple(A{}));
         ((void)t);
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
index 8f778f3fcf3a4..a8ed156a2fc33 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
@@ -113,6 +113,9 @@ constexpr bool test() {
 
   // These two test points cause gcc to ICE
 #if !defined(TEST_COMPILER_GCC)
+
+// Remove this guard when compiler versions older than clang 23 are no longer supported.
+#if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
   // sizeof...(Types) == 1 && is_convertible_v<decltype(u), T>
   {
     const std::tuple<CvtFromConstTupleRefRef> t1{};
@@ -126,7 +129,9 @@ constexpr bool test() {
     std::tuple<ConvertibleFrom<ExplicitCtrFromConstTupleRefRef>> t2{std::move(t1)};
     assert(!constMoveCtrCalled(std::get<0>(t2).v));
   }
-#endif
+#endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+
+#endif // !defined(TEST_COMPILER_GCC)
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
index d6c192d2e0543..b3982795bf6c8 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
@@ -111,6 +111,8 @@ int main(int, char**)
         assert(std::get<1>(t) == 1);
         assert(std::get<2>(t) == 2);
     }
+// Remove this guard when compiler versions older than clang 23 are no longer supported.
+#if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
     // A bug in tuple caused __tuple_leaf to use its explicit converting constructor
     //  as its move constructor. This tests that ConstructsWithTupleLeaf is not called
     // (w/ __tuple_leaf)
@@ -119,6 +121,8 @@ int main(int, char**)
         d_t d((ConstructsWithTupleLeaf()));
         d_t d2(static_cast<d_t &&>(d));
     }
+#endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+
     {
         test_sfinae<move_only_ebo>();
         test_sfinae<move_only_large>();


From libcxx-commits at lists.llvm.org  Thu Jul  2 04:27:06 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Thu, 02 Jul 2026 04:27:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Add deleted tuple
 constructor overloads (PR #205379)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205379@github.com>
Message-ID: <6a464b0a.d7b890e2.14a009.0176@mx.google.com>


https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/205379

>From 865c5a96e4f7699d8ecc5a6317aba888d8795269 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 24 Jun 2026 00:51:43 +0800
Subject: [PATCH 01/13] [libc++] P2255R2: Add deleted tuple constructor
 overloads

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/tuple                          | 247 ++++++++++++++++--
 ...5_tuple_ref_binding_diagnostics.verify.cpp |  24 +-
 .../ref_constructs_from_temporary.verify.cpp  |  74 ++++++
 4 files changed, 309 insertions(+), 38 deletions(-)
 create mode 100644 libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp

diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 739f0ecab37e7..e2e7c679be7b7 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -43,7 +43,7 @@
 "`P0627R6 <https://wg21.link/P0627R6>`__","Function to mark unreachable code","2022-02 (Virtual)","|Complete|","15","`#105175 <https://github.com/llvm/llvm-project/issues/105175>`__",""
 "`P1206R7 <https://wg21.link/P1206R7>`__","``ranges::to``: A function to convert any range to a container","2022-02 (Virtual)","|Complete|","17","`#105176 <https://github.com/llvm/llvm-project/issues/105176>`__",""
 "`P1413R3 <https://wg21.link/P1413R3>`__","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","2022-02 (Virtual)","|Complete|","","`#105177 <https://github.com/llvm/llvm-project/issues/105177>`__","``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but clang doesn't issue a diagnostic for deprecated using template declarations."
-"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","`#105180 <https://github.com/llvm/llvm-project/issues/105180>`__","Implemented the type traits only."
+"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","`#105180 <https://github.com/llvm/llvm-project/issues/105180>`__","Implemented the type traits and changes to tuple constructors only."
 "`P2273R3 <https://wg21.link/P2273R3>`__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16","`#105182 <https://github.com/llvm/llvm-project/issues/105182>`__",""
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 8f43006521dda..912b49c2b97de 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -616,21 +616,55 @@ public:
               _Not<_IsThisTuple<_Up...> >, // extension to allow mis-behaved user constructors
               is_constructible<_Tp, _Up>... > {};
 
-  template <class... _Up,
-            __enable_if_t< _And< _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> >::value,
-                           int> = 0>
+  template <class... _Up>
+  struct _EnableUTypesCtorBase
+      : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
+
+  template <bool, class... _Up>
+  struct _EnableUTypesCtorNoDangling : false_type {};
+
+#    if _LIBCPP_STD_VER >= 23
+  template <bool, class... _Up>
+  struct _CtorCreatesDanglingReference : false_type {};
+
+  template <class... _Up>
+  struct _CtorCreatesDanglingReference<true, _Up...>
+      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, _Up&&> >...> {};
+
+  template <class... _Up>
+  struct _EnableUTypesCtorNoDangling<true, _Up...> : _Not<_CtorCreatesDanglingReference<true, _Up...> > {};
+
+  template <class... _Up>
+  struct _EnableDanglingUTypesCtor : _CtorCreatesDanglingReference<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+#    else
+  template <class... _Up>
+  struct _EnableUTypesCtorNoDangling<true, _Up...> : true_type {};
+#    endif // _LIBCPP_STD_VER >= 23
+
+  template <class... _Up>
+  struct _EnableNonDanglingUTypesCtor
+      : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+
+  template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&... __u) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__forward_args{}, std::forward<_Up>(__u)...) {}
 
-  template <class _Alloc,
-            class... _Up,
-            __enable_if_t< _And< _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> >::value,
-                           int> = 0>
+  template <class _Alloc, class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
       : __base_(allocator_arg_t(), __a, __forward_args{}, std::forward<_Up>(__u)...) {}
 
+#    if _LIBCPP_STD_VER >= 23
+  template <class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
+
+  template <class _Alloc, class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
+#    endif // _LIBCPP_STD_VER >= 23
+
   // Copy and move constructors (including the allocator_arg_t variants)
   tuple(const tuple&) = default;
   tuple(tuple&&)      = default;
@@ -672,58 +706,134 @@ public:
                      _Not<is_convertible<_OtherTuple, _Tp> >...,
                      _Not<is_constructible<_Tp, _OtherTuple> >... > > > {};
 
-  template <class... _Up, __enable_if_t< _And< _EnableCtorFromUTypesTuple<const tuple<_Up...>&> >::value, int> = 0>
+  template <bool, class _OtherTuple>
+  struct _EnableCtorFromUTypesTupleNoDangling : false_type {};
+
+#    if _LIBCPP_STD_VER >= 23
+  template <bool, class _OtherTuple, class _DecayedOtherTuple = __remove_cvref_t<_OtherTuple>, class = void>
+  struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
+
+  template <class _OtherTuple, class... _Up>
+  struct _CtorFromUTypesTupleCreatesDanglingReference<
+      true,
+      _OtherTuple,
+      tuple<_Up...>,
+      __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
+      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
+
+  template <class _OtherTuple>
+  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple>
+      : _Not<_CtorFromUTypesTupleCreatesDanglingReference<true, _OtherTuple> > {};
+
+  template <class _OtherTuple>
+  struct _EnableDanglingCtorFromUTypesTuple
+      : _CtorFromUTypesTupleCreatesDanglingReference<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+#    else
+  template <class _OtherTuple>
+  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple> : true_type {};
+#    endif // _LIBCPP_STD_VER >= 23
+
+  template <class _OtherTuple>
+  struct _EnableNonDanglingCtorFromUTypesTuple
+      : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+
+  template <class... _Up,
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
       : __base_(__from_tuple(), __t) {}
 
   template <class... _Up,
             class _Alloc,
-            __enable_if_t< _And< _EnableCtorFromUTypesTuple<const tuple<_Up...>&> >::value, int> = 0>
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class... _Up,
+            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+      tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
+
+  template <class... _Up,
+            class _Alloc,
+            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
+
   // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>& __t)
       : __base_(__from_tuple(), __t) {}
 
-  template <class _Alloc, class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
+
+  template <class _Alloc,
+            class... _Up,
+            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __t) {}
+
+  template <class _Alloc,
+            class... _Up,
+            enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
-  template <class... _Up, __enable_if_t< _And< _EnableCtorFromUTypesTuple<tuple<_Up...>&&> >::value, int> = 0>
+  template <class... _Up,
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__from_tuple(), std::move(__t)) {}
 
   template <class _Alloc,
             class... _Up,
-            __enable_if_t< _And< _EnableCtorFromUTypesTuple<tuple<_Up...>&&> >::value, int> = 0>
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__t)) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
+
+  template <class _Alloc,
+            class... _Up,
+            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
+
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up,
+            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&& __t)
       : __base_(__from_tuple(), std::move(__t)) {}
 
+  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+      tuple(const tuple<_Up...>&&) = delete;
+
   template <class _Alloc,
             class... _Up,
-            enable_if_t< _EnableCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__t)) {}
+
+  template <class _Alloc,
+            class... _Up,
+            enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(const pair<U1, U2>&) constructors (including allocator_arg_t variants)
@@ -741,6 +851,34 @@ public:
   template <class _Pair>
   struct _EnableCtorFromPair : _CtorPredicateFromPair<is_constructible, _Pair> {};
 
+  template <bool, class _Pair>
+  struct _EnableCtorFromPairNoDangling : false_type {};
+
+#    if _LIBCPP_STD_VER >= 23
+  template <bool, class _Pair, class _DecayedPair = __remove_cvref_t<_Pair>, class _Tuple = tuple>
+  struct _CtorFromPairCreatesDanglingReference : false_type {};
+
+  template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
+  struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
+      : _Or<
+            _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
+            _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
+
+  template <class _Pair>
+  struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
+
+  template <class _Pair>
+  struct _EnableDanglingCtorFromPair
+      : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+#    else
+  template <class _Pair>
+  struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
+#    endif // _LIBCPP_STD_VER >= 23
+
+  template <class _Pair>
+  struct _EnableNonDanglingCtorFromPair
+      : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+
   template <class _Pair>
   struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
 
@@ -754,7 +892,7 @@ public:
   template <class _Up1,
             class _Up2,
             template <class...> class _And                                                   = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>& __p) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value)
@@ -764,35 +902,64 @@ public:
             class _Up1,
             class _Up2,
             template <class...> class _And                                                   = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, const pair<_Up1, _Up2>& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __p) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class _Up1,
+            class _Up2,
+            template <class...> class _And                                                    = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+      tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) =
+          delete;
+
+  template <class _Alloc,
+            class _Up1,
+            class _Up2,
+            template <class...> class _And                                                    = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+      tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
+
   // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
 
-  template <class _U1, class _U2, enable_if_t< _EnableCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _U1, class _U2, enable_if_t< _EnableNonDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(pair<_U1, _U2>& __p)
       : __base_(__from_tuple(), __p) {}
 
+  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+      tuple(pair<_U1, _U2>&) = delete;
+
   template <class _Alloc,
             class _U1,
             class _U2,
-            enable_if_t< _EnableCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+            enable_if_t< _EnableNonDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, pair<_U1, _U2>& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __p) {}
-#    endif
+
+  template <class _Alloc,
+            class _U1,
+            class _U2,
+            enable_if_t< _EnableDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+      tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
+#    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(pair<U1, U2>&&) constructors (including allocator_arg_t variants)
 
   template <class _Up1,
             class _Up2,
             template <class...> class _And                                              = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&& __p) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value)
@@ -802,27 +969,59 @@ public:
             class _Up1,
             class _Up2,
             template <class...> class _And                                              = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, pair<_Up1, _Up2>&& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__p)) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class _Up1,
+            class _Up2,
+            template <class...> class _And                                               = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+      tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
+
+  template <class _Alloc,
+            class _Up1,
+            class _Up2,
+            template <class...> class _And                                               = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+      tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
+
   // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
 
-  template <class _U1, class _U2, enable_if_t< _EnableCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1,
+            class _U2,
+            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&& __p)
       : __base_(__from_tuple(), std::move(__p)) {}
 
+  template <class _U1,
+            class _U2,
+            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+      tuple(const pair<_U1, _U2>&&) = delete;
+
   template <class _Alloc,
             class _U1,
             class _U2,
-            enable_if_t< _EnableCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const pair<_U1, _U2>&& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__p)) {}
+
+  template <class _Alloc,
+            class _U1,
+            class _U2,
+            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+      tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // [tuple.assign]
diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index a1a80483c4487..a44a38e3e17ed 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03
+// REQUIRES: std-at-least-c++23
 
 // <tuple>
 
@@ -39,36 +40,33 @@ template <class ...Args>
 void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
 
 void f() {
-  // Test that we emit our diagnostic from the library.
-  // expected-error at tuple:* 8 {{Attempted construction of reference element binds to a temporary whose lifetime has ended}}
-
-  // Good news everybody! Clang now diagnoses this for us!
-  // expected-error at tuple:* 0+ {{reference member '__value_' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+  // Test that the public constructors are deleted when constructing a reference
+  // element would bind to a temporary.
 
   {
-    F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-note 1 {{requested here}}
+    F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-error {{deleted}}
   }
   {
-    std::tuple<int, const std::string&> t(1, "a"); // expected-note 1 {{requested here}}
+    std::tuple<int, const std::string&> t(1, "a"); // expected-error {{deleted}}
   }
   {
-    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-note 1 {{requested here}}
+    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-error {{deleted}}
   }
   {
     ConvertsTo<int&> ct;
-    std::tuple<const long&, int> t(ct, 42); // expected-note {{requested here}}
+    std::tuple<const long&, int> t(ct, 42); // expected-error {{deleted}}
   }
   {
     ConvertsTo<int> ct;
-    std::tuple<int const&, void*> t(ct, nullptr); // expected-note {{requested here}}
+    std::tuple<int const&, void*> t(ct, nullptr); // expected-error {{deleted}}
   }
   {
     ConvertsTo<Derived> ct;
-    std::tuple<Base const&, int> t(ct, 42); // expected-note {{requested here}}
+    std::tuple<Base const&, int> t(ct, 42); // expected-error {{deleted}}
   }
   {
     std::allocator<int> alloc;
-    std::tuple<std::string &&> t2("hello"); // expected-note {{requested here}}
-    std::tuple<std::string &&> t3(std::allocator_arg, alloc, "hello"); // expected-note {{requested here}}
+    std::tuple<std::string&&> t2("hello"); // expected-error {{deleted}}
+    std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
   }
 }
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp
new file mode 100644
index 0000000000000..c7a5f4e247f3b
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <tuple>
+
+// tuple(UTypes&&... u);
+// tuple(tuple<UTypes...>& u);
+// tuple(const tuple<UTypes...>& u);
+// tuple(tuple<UTypes...>&& u);
+// tuple(const tuple<UTypes...>&& u);
+// tuple(pair<U1, U2>& u);
+// tuple(const pair<U1, U2>& u);
+// tuple(pair<U1, U2>&& u);
+// tuple(const pair<U1, U2>&& u);
+//
+// The constructors above are defined as deleted if reference_constructs_from_temporary_v
+// is true for one of the corresponding tuple elements.
+
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+struct X {
+  X(int);
+};
+
+static_assert(std::is_constructible_v<std::tuple<const X&>, X&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, int>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, std::tuple<int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, const std::tuple<int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, std::tuple<int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, const std::tuple<int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, std::pair<int, int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, const std::pair<int, int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, std::pair<int, int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, const std::pair<int, int>&&>);
+
+void test() {
+  std::allocator<int> alloc;
+  std::tuple<int> t(1);
+  const std::tuple<int> ct(1);
+  std::pair<int, int> p(1, 2);
+  const std::pair<int, int> cp(1, 2);
+
+  // expected-error-re@*:* 18 {{call to deleted constructor of 'std::tuple<{{.*}}>'}}
+  std::tuple<const X&> utypes(1);
+  std::tuple<const X&> alloc_utypes(std::allocator_arg, alloc, 1);
+
+  std::tuple<const X&> tuple_lvalue(t);
+  std::tuple<const X&> alloc_tuple_lvalue(std::allocator_arg, alloc, t);
+  std::tuple<const X&> const_tuple_lvalue(ct);
+  std::tuple<const X&> alloc_const_tuple_lvalue(std::allocator_arg, alloc, ct);
+  std::tuple<const X&> tuple_rvalue(std::tuple<int>{1});
+  std::tuple<const X&> alloc_tuple_rvalue(std::allocator_arg, alloc, std::tuple<int>{1});
+  std::tuple<const X&> const_tuple_rvalue(std::move(ct));
+  std::tuple<const X&> alloc_const_tuple_rvalue(std::allocator_arg, alloc, std::move(ct));
+
+  std::tuple<const X&, int> pair_lvalue(p);
+  std::tuple<const X&, int> alloc_pair_lvalue(std::allocator_arg, alloc, p);
+  std::tuple<const X&, int> const_pair_lvalue(cp);
+  std::tuple<const X&, int> alloc_const_pair_lvalue(std::allocator_arg, alloc, cp);
+  std::tuple<const X&, int> pair_rvalue(std::pair<int, int>{1, 2});
+  std::tuple<const X&, int> alloc_pair_rvalue(std::allocator_arg, alloc, std::pair<int, int>{1, 2});
+  std::tuple<const X&, int> const_pair_rvalue(std::move(cp));
+  std::tuple<const X&, int> alloc_const_pair_rvalue(std::allocator_arg, alloc, std::move(cp));
+}

>From 9d2d66b76aba200e90d0c67b487c13abb81f585e Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 24 Jun 2026 00:56:58 +0800
Subject: [PATCH 02/13] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple                          | 62 ++++++++-----------
 ...5_tuple_ref_binding_diagnostics.verify.cpp |  2 +-
 2 files changed, 26 insertions(+), 38 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 912b49c2b97de..edeb97aa9de80 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -617,8 +617,7 @@ public:
               is_constructible<_Tp, _Up>... > {};
 
   template <class... _Up>
-  struct _EnableUTypesCtorBase
-      : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
+  struct _EnableUTypesCtorBase : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
 
   template <bool, class... _Up>
   struct _EnableUTypesCtorNoDangling : false_type {};
@@ -642,8 +641,7 @@ public:
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class... _Up>
-  struct _EnableNonDanglingUTypesCtor
-      : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+  struct _EnableNonDanglingUTypesCtor : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
 
   template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
@@ -714,11 +712,10 @@ public:
   struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
 
   template <class _OtherTuple, class... _Up>
-  struct _CtorFromUTypesTupleCreatesDanglingReference<
-      true,
-      _OtherTuple,
-      tuple<_Up...>,
-      __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
+  struct _CtorFromUTypesTupleCreatesDanglingReference< true,
+                                                       _OtherTuple,
+                                                       tuple<_Up...>,
+                                                       __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
       : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
 
   template <class _OtherTuple>
@@ -737,8 +734,7 @@ public:
   struct _EnableNonDanglingCtorFromUTypesTuple
       : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
 
-  template <class... _Up,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
       : __base_(__from_tuple(), __t) {}
@@ -751,8 +747,7 @@ public:
       : __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up,
-            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
 
@@ -786,8 +781,7 @@ public:
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
-  template <class... _Up,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__from_tuple(), std::move(__t)) {}
@@ -812,8 +806,7 @@ public:
 
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up,
-            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&& __t)
       : __base_(__from_tuple(), std::move(__t)) {}
@@ -860,24 +853,22 @@ public:
 
   template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
   struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
-      : _Or<
-            _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
-            _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
+      : _Or< _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
+             _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
 
   template <class _Pair>
   struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
 
   template <class _Pair>
-  struct _EnableDanglingCtorFromPair
-      : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+  struct _EnableDanglingCtorFromPair : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {
+  };
 #    else
   template <class _Pair>
   struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _Pair>
-  struct _EnableNonDanglingCtorFromPair
-      : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+  struct _EnableNonDanglingCtorFromPair : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
 
   template <class _Pair>
   struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
@@ -891,7 +882,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                   = _And,
+            template <class...> class _And                                                              = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -901,7 +892,7 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                   = _And,
+            template <class...> class _And                                                              = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -911,17 +902,16 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                    = _And,
+            template <class...> class _And                                                            = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
-      tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) =
-          delete;
+      tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
 
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                    = _And,
+            template <class...> class _And                                                            = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -958,7 +948,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                              = _And,
+            template <class...> class _And                                                         = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -968,7 +958,7 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                              = _And,
+            template <class...> class _And                                                         = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -978,7 +968,7 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                               = _And,
+            template <class...> class _And                                                       = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -987,7 +977,7 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                               = _And,
+            template <class...> class _And                                                       = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -1002,9 +992,7 @@ public:
       tuple(const pair<_U1, _U2>&& __p)
       : __base_(__from_tuple(), std::move(__p)) {}
 
-  template <class _U1,
-            class _U2,
-            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&&) = delete;
 
diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index a44a38e3e17ed..15d032fbb3a7a 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -66,7 +66,7 @@ void f() {
   }
   {
     std::allocator<int> alloc;
-    std::tuple<std::string&&> t2("hello"); // expected-error {{deleted}}
+    std::tuple<std::string&&> t2("hello");                            // expected-error {{deleted}}
     std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
   }
 }

>From 8520af98734cd4100ebad837b70a7f69985fb937 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 09:41:49 -0700
Subject: [PATCH 03/13] [libc++] Refine the implementation use template
 variable and make reference_constructs_from_temporary SFINAE friendly

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../reference_constructs_from_temporary.h     |  15 +-
 libcxx/include/tuple                          | 240 ++++++++----------
 2 files changed, 126 insertions(+), 129 deletions(-)

diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index a8325620414ea..c18723fb77d81 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -11,6 +11,7 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_reference.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -30,8 +31,20 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_tempo
 
 #endif
 
+// A non-reference type can never bind to a temporary, so the result is always `false` for such a
+// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
+// eagerly instantiates the construction of `_Up` (including the element's constructor exception
+// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
+//
+// https://godbolt.org/z/WMTK5cMa4
+//
+// TODO: remove this guard once the Clang builtin short-circuits on a non-reference first operand.
+template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
+inline const bool __reference_constructs_from_temporary_v = false;
+
 template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
+inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
+    __reference_constructs_from_temporary(_Tp, _Up);
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index edeb97aa9de80..609cdf1f93dc4 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -623,42 +623,60 @@ public:
   struct _EnableUTypesCtorNoDangling : false_type {};
 
 #    if _LIBCPP_STD_VER >= 23
-  template <bool, class... _Up>
-  struct _CtorCreatesDanglingReference : false_type {};
-
   template <class... _Up>
-  struct _CtorCreatesDanglingReference<true, _Up...>
-      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, _Up&&> >...> {};
+  static constexpr bool __has_danglings() {
+    // The fold expands `_Tp` and `_Up` in lockstep, so it is only well-formed when the packs have
+    // equal length. The discarded branch is not instantiated when the lengths differ.
+    if constexpr (sizeof...(_Up) == sizeof...(_Tp))
+      return (__reference_constructs_from_temporary_v<_Tp, _Up&&> || ...);
+    else
+      return false;
+  }
 
-  template <class... _Up>
-  struct _EnableUTypesCtorNoDangling<true, _Up...> : _Not<_CtorCreatesDanglingReference<true, _Up...> > {};
+  template <class _Tuple,
+            class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+  static inline constexpr bool __has_danglings_from_tuple_like = false;
+
+  template <class _Tuple, size_t... _Idx>
+  static inline constexpr bool __has_danglings_from_tuple_like<_Tuple, index_sequence<_Idx...>,
+    enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
 
   template <class... _Up>
-  struct _EnableDanglingUTypesCtor : _CtorCreatesDanglingReference<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
-#    else
+  static inline constexpr bool __has_danglings_from_utypes =
+      _EnableUTypesCtorBase<_Up...>::value && __has_danglings<_Up...>();
+
   template <class... _Up>
-  struct _EnableUTypesCtorNoDangling<true, _Up...> : true_type {};
+  static inline constexpr bool __has_no_danglings_from_utypes =
+      _EnableUTypesCtorBase<_Up...>::value && !__has_danglings<_Up...>();
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class... _Up>
-  struct _EnableNonDanglingUTypesCtor : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+  static inline constexpr bool __enable_utypes_constructor_no_dangling() {
+#    if _LIBCPP_STD_VER >= 23
+    return __has_no_danglings_from_utypes<_Up...>;
+#    else
+    return _EnableUTypesCtorBase<_Up...>::value;
+#    endif // _LIBCPP_STD_VER >= 23
+  }
 
-  template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
+  template <class... _Up, __enable_if_t< __enable_utypes_constructor_no_dangling<_Up...>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&... __u) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__forward_args{}, std::forward<_Up>(__u)...) {}
 
-  template <class _Alloc, class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
+  template <class _Alloc, class... _Up, __enable_if_t< __enable_utypes_constructor_no_dangling<_Up...>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
       : __base_(allocator_arg_t(), __a, __forward_args{}, std::forward<_Up>(__u)...) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  template <class... _Up>
+    requires __has_danglings_from_utypes<_Up...>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
-  template <class _Alloc, class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes<_Up...>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
@@ -708,123 +726,116 @@ public:
   struct _EnableCtorFromUTypesTupleNoDangling : false_type {};
 
 #    if _LIBCPP_STD_VER >= 23
-  template <bool, class _OtherTuple, class _DecayedOtherTuple = __remove_cvref_t<_OtherTuple>, class = void>
-  struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
-
-  template <class _OtherTuple, class... _Up>
-  struct _CtorFromUTypesTupleCreatesDanglingReference< true,
-                                                       _OtherTuple,
-                                                       tuple<_Up...>,
-                                                       __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
-      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
-
   template <class _OtherTuple>
-  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple>
-      : _Not<_CtorFromUTypesTupleCreatesDanglingReference<true, _OtherTuple> > {};
+  static inline constexpr bool __has_danglings_from_utypes_tuple =
+      _EnableCtorFromUTypesTuple<_OtherTuple>::value && __has_danglings_from_tuple_like<_OtherTuple>;
 
   template <class _OtherTuple>
-  struct _EnableDanglingCtorFromUTypesTuple
-      : _CtorFromUTypesTupleCreatesDanglingReference<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
-#    else
-  template <class _OtherTuple>
-  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple> : true_type {};
+  static inline constexpr bool __has_no_danglings_from_utypes_tuple =
+      _EnableCtorFromUTypesTuple<_OtherTuple>::value && !__has_danglings_from_tuple_like<_OtherTuple>;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _OtherTuple>
-  struct _EnableNonDanglingCtorFromUTypesTuple
-      : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+  static inline constexpr bool __enable_utypes_tuple_constructor_no_dangling() {
+#    if _LIBCPP_STD_VER >= 23
+    return __has_no_danglings_from_utypes_tuple<_OtherTuple>;
+#    else
+    return _EnableCtorFromUTypesTuple<_OtherTuple>::value;
+#    endif // _LIBCPP_STD_VER >= 23
+  }
 
-  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+  template <class... _Up,
+            __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<const tuple<_Up...>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
       : __base_(__from_tuple(), __t) {}
 
   template <class... _Up,
             class _Alloc,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+            __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<const tuple<_Up...>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
 
-  template <class... _Up,
-            class _Alloc,
-            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  template <class... _Up, class _Alloc>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
 
   // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>& __t)
       : __base_(__from_tuple(), __t) {}
 
-  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __t) {}
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
-  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+  template <class... _Up, __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<tuple<_Up...>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__from_tuple(), std::move(__t)) {}
 
   template <class _Alloc,
             class... _Up,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+            __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<tuple<_Up...>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__t)) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
-  template <class _Alloc,
-            class... _Up,
-            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
 
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&& __t)
       : __base_(__from_tuple(), std::move(__t)) {}
 
-  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&&) = delete;
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__t)) {}
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
@@ -844,31 +855,24 @@ public:
   template <class _Pair>
   struct _EnableCtorFromPair : _CtorPredicateFromPair<is_constructible, _Pair> {};
 
-  template <bool, class _Pair>
-  struct _EnableCtorFromPairNoDangling : false_type {};
-
 #    if _LIBCPP_STD_VER >= 23
-  template <bool, class _Pair, class _DecayedPair = __remove_cvref_t<_Pair>, class _Tuple = tuple>
-  struct _CtorFromPairCreatesDanglingReference : false_type {};
-
-  template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
-  struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
-      : _Or< _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
-             _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
-
   template <class _Pair>
-  struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
+  static inline constexpr bool __has_danglings_from_utypes_pair =
+      _EnableCtorFromPair<_Pair>::value && __has_danglings_from_tuple_like<_Pair>;
 
   template <class _Pair>
-  struct _EnableDanglingCtorFromPair : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {
-  };
-#    else
-  template <class _Pair>
-  struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
+  static inline constexpr bool __has_no_danglings_from_utypes_pair =
+      _EnableCtorFromPair<_Pair>::value && !__has_danglings_from_tuple_like<_Pair>;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _Pair>
-  struct _EnableNonDanglingCtorFromPair : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+  static inline constexpr bool __enable_utypes_pair_constructor_no_dangling() {
+#    if _LIBCPP_STD_VER >= 23
+    return _EnableCtorFromPair<_Pair>::value && __has_no_danglings_from_utypes_pair<_Pair>;
+#    else
+    return _EnableCtorFromPair<_Pair>::value;
+#    endif // _LIBCPP_STD_VER >= 23
+  }
 
   template <class _Pair>
   struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
@@ -882,8 +886,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                              = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t<__enable_utypes_pair_constructor_no_dangling<const pair<_Up1, _Up2>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>& __p) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value)
@@ -892,54 +895,46 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                              = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t<__enable_utypes_pair_constructor_no_dangling<const pair<_Up1, _Up2>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, const pair<_Up1, _Up2>& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __p) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class _Up1,
-            class _Up2,
-            template <class...> class _And                                                            = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  template <class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
 
-  template <class _Alloc,
-            class _Up1,
-            class _Up2,
-            template <class...> class _And                                                            = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  template <class _Alloc, class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
 
   // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
 
-  template <class _U1, class _U2, enable_if_t< _EnableNonDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(pair<_U1, _U2>& __p)
       : __base_(__from_tuple(), __p) {}
 
-  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(pair<_U1, _U2>&) = delete;
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableNonDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, pair<_U1, _U2>& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __p) {}
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
@@ -948,8 +943,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                         = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< __enable_utypes_pair_constructor_no_dangling<pair<_Up1, _Up2>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&& __p) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value)
@@ -958,56 +952,46 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                         = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< __enable_utypes_pair_constructor_no_dangling<pair<_Up1, _Up2>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, pair<_Up1, _Up2>&& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__p)) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class _Up1,
-            class _Up2,
-            template <class...> class _And                                                       = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  template <class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
 
-  template <class _Alloc,
-            class _Up1,
-            class _Up2,
-            template <class...> class _And                                                       = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  template <class _Alloc, class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
 
   // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
 
-  template <class _U1,
-            class _U2,
-            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&& __p)
       : __base_(__from_tuple(), std::move(__p)) {}
 
-  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&&) = delete;
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const pair<_U1, _U2>&& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__p)) {}
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23

>From f8ba2c9c885db176fef5158cd1602b9beaa293ac Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 09:46:03 -0700
Subject: [PATCH 04/13] [libc++] Enable test for all language modes

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index 15d032fbb3a7a..0e9992194f03c 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03
-// REQUIRES: std-at-least-c++23
 
 // <tuple>
 

>From 3d87818fe02261235ef78f2f638b5a6dba300edb Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:06:24 -0700
Subject: [PATCH 05/13] Remove unnecessary for deleted ctors

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 46 +++++++++++++++++---------------------------
 1 file changed, 18 insertions(+), 28 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 609cdf1f93dc4..687192b6ffead 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -672,13 +672,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
     requires __has_danglings_from_utypes<_Up...>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes<_Up...>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
-      tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // Copy and move constructors (including the allocator_arg_t variants)
@@ -760,12 +759,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+  explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
 
   template <class... _Up, class _Alloc>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+  explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
 
   // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
@@ -777,7 +776,7 @@ public:
 
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
+  explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
@@ -787,8 +786,7 @@ public:
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
-      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
+  explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
@@ -807,13 +805,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
-      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
 
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
@@ -825,8 +822,7 @@ public:
 
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
-      tuple(const tuple<_Up...>&&) = delete;
+  explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value) tuple(const tuple<_Up...>&&) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
@@ -836,7 +832,7 @@ public:
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+  explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
@@ -904,14 +900,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
 
   template <class _Alloc, class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
 
   // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
@@ -924,8 +918,7 @@ public:
 
   template <class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<pair<_U1, _U2>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
-      tuple(pair<_U1, _U2>&) = delete;
+  explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value) tuple(pair<_U1, _U2>&) = delete;
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_no_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
@@ -935,7 +928,7 @@ public:
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+  explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
@@ -961,14 +954,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
 
   template <class _Alloc, class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
 
   // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
@@ -981,8 +972,7 @@ public:
 
   template <class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
-      tuple(const pair<_U1, _U2>&&) = delete;
+  explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value) tuple(const pair<_U1, _U2>&&) = delete;
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
@@ -992,7 +982,7 @@ public:
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+  explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 

>From 8ff04dd78e88eafd4876196dd6c88a02b061da0f Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:13:12 -0700
Subject: [PATCH 06/13] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 687192b6ffead..7f444a5333f75 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -633,13 +633,14 @@ public:
       return false;
   }
 
-  template <class _Tuple,
-            class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+  template <class _Tuple, class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
   static inline constexpr bool __has_danglings_from_tuple_like = false;
 
   template <class _Tuple, size_t... _Idx>
-  static inline constexpr bool __has_danglings_from_tuple_like<_Tuple, index_sequence<_Idx...>,
-    enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
+  static inline constexpr bool __has_danglings_from_tuple_like<
+      _Tuple,
+      index_sequence<_Idx...>,
+      enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
 
   template <class... _Up>
   static inline constexpr bool __has_danglings_from_utypes =

>From bb0fa0f1cbee157e39dd52b254da55b2bb59fa5d Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:20:32 -0700
Subject: [PATCH 07/13] Remove unnecessary comments

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 7f444a5333f75..22641af8f494c 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -625,8 +625,6 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
   static constexpr bool __has_danglings() {
-    // The fold expands `_Tp` and `_Up` in lockstep, so it is only well-formed when the packs have
-    // equal length. The discarded branch is not instantiated when the lengths differ.
     if constexpr (sizeof...(_Up) == sizeof...(_Tp))
       return (__reference_constructs_from_temporary_v<_Tp, _Up&&> || ...);
     else

>From 70175b01fa43cb47b44ef3974124e5d3e8366476 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 00:06:59 -0700
Subject: [PATCH 08/13] [libc++] Fix hard error in deleted dangling ctor
 constraints

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 22641af8f494c..0a95aa635663d 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -640,13 +640,25 @@ public:
       index_sequence<_Idx...>,
       enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
 
+  template <bool __dangles, class _CtorEnabled>
+  static inline constexpr bool __check_danglings_from_constructor = false;
+
+  template <class _CtorEnabled>
+  static inline constexpr bool __check_danglings_from_constructor<true, _CtorEnabled> = _CtorEnabled::value;
+
+  template <bool __dangles, class _CtorEnabled>
+  static inline constexpr bool __check_no_danglings_from_constructor = _CtorEnabled::value;
+
+  template <class _CtorEnabled>
+  static inline constexpr bool __check_no_danglings_from_constructor<true, _CtorEnabled> = false;
+
   template <class... _Up>
   static inline constexpr bool __has_danglings_from_utypes =
-      _EnableUTypesCtorBase<_Up...>::value && __has_danglings<_Up...>();
+      __check_danglings_from_constructor<__has_danglings<_Up...>(), _EnableUTypesCtorBase<_Up...> >;
 
   template <class... _Up>
   static inline constexpr bool __has_no_danglings_from_utypes =
-      _EnableUTypesCtorBase<_Up...>::value && !__has_danglings<_Up...>();
+      __check_no_danglings_from_constructor<__has_danglings<_Up...>(), _EnableUTypesCtorBase<_Up...> >;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class... _Up>
@@ -726,11 +738,13 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _OtherTuple>
   static inline constexpr bool __has_danglings_from_utypes_tuple =
-      _EnableCtorFromUTypesTuple<_OtherTuple>::value && __has_danglings_from_tuple_like<_OtherTuple>;
+      __check_danglings_from_constructor<__has_danglings_from_tuple_like<_OtherTuple>,
+                                         _EnableCtorFromUTypesTuple<_OtherTuple> >;
 
   template <class _OtherTuple>
   static inline constexpr bool __has_no_danglings_from_utypes_tuple =
-      _EnableCtorFromUTypesTuple<_OtherTuple>::value && !__has_danglings_from_tuple_like<_OtherTuple>;
+      __check_no_danglings_from_constructor<__has_danglings_from_tuple_like<_OtherTuple>,
+                                            _EnableCtorFromUTypesTuple<_OtherTuple> >;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _OtherTuple>
@@ -853,11 +867,11 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Pair>
   static inline constexpr bool __has_danglings_from_utypes_pair =
-      _EnableCtorFromPair<_Pair>::value && __has_danglings_from_tuple_like<_Pair>;
+      __check_danglings_from_constructor<__has_danglings_from_tuple_like<_Pair>, _EnableCtorFromPair<_Pair> >;
 
   template <class _Pair>
   static inline constexpr bool __has_no_danglings_from_utypes_pair =
-      _EnableCtorFromPair<_Pair>::value && !__has_danglings_from_tuple_like<_Pair>;
+      __check_no_danglings_from_constructor<__has_danglings_from_tuple_like<_Pair>, _EnableCtorFromPair<_Pair> >;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _Pair>

>From d59c4503c1096aac398768060bb6fda5809325cf Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 00:57:48 -0700
Subject: [PATCH 09/13] Fix modules build and don't depends tuple_size_v

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 0a95aa635663d..a2a72cc352f86 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -631,14 +631,16 @@ public:
       return false;
   }
 
-  template <class _Tuple, class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+  template <class _Source, class _DecayedSource = __remove_cvref_t<_Source> >
   static inline constexpr bool __has_danglings_from_tuple_like = false;
 
-  template <class _Tuple, size_t... _Idx>
-  static inline constexpr bool __has_danglings_from_tuple_like<
-      _Tuple,
-      index_sequence<_Idx...>,
-      enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
+  template <class _Source, class... _Up>
+  static inline constexpr bool __has_danglings_from_tuple_like<_Source, tuple<_Up...> > =
+      __has_danglings<__copy_cvref_t<_Source, _Up>...>();
+
+  template <class _Source, class _U1, class _U2>
+  static inline constexpr bool __has_danglings_from_tuple_like<_Source, pair<_U1, _U2> > =
+      __has_danglings<__copy_cvref_t<_Source, _U1>, __copy_cvref_t<_Source, _U2> >();
 
   template <bool __dangles, class _CtorEnabled>
   static inline constexpr bool __check_danglings_from_constructor = false;

>From 958f25ad57ee72b3ef853b69856e04be8cba2477 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 02:36:45 -0700
Subject: [PATCH 10/13] Fix test

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 ...5_tuple_ref_binding_diagnostics.verify.cpp | 34 ++++++++++---------
 1 file changed, 18 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index 0e9992194f03c..3c2cee9f16153 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -39,33 +39,35 @@ template <class ...Args>
 void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
 
 void f() {
-  // Test that the public constructors are deleted when constructing a reference
-  // element would bind to a temporary.
+  // Constructing a reference element that would bind to a temporary is rejected. Since C++23
+  // ([tuple.cnstr]) the offending constructors are deleted, so the error is reported at the call;
+  // before C++23 the bind is caught by a static_assert in the library (with Clang additionally
+  // diagnosing the dangling reference member).
+#if TEST_STD_VER >= 23
+  // expected-error@*:* 8 {{deleted}}
+#else
+  // expected-error at tuple:* 8 {{Attempted construction of reference element binds to a temporary whose lifetime has ended}}
+  // expected-error at tuple:* 0+ {{reference member '__value_' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#endif
 
-  {
-    F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-error {{deleted}}
-  }
-  {
-    std::tuple<int, const std::string&> t(1, "a"); // expected-error {{deleted}}
-  }
-  {
-    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-error {{deleted}}
-  }
+  { F<int, const std::string&>(std::make_tuple(1, "abc")); }
+  { std::tuple<int, const std::string&> t(1, "a"); }
+  { F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); }
   {
     ConvertsTo<int&> ct;
-    std::tuple<const long&, int> t(ct, 42); // expected-error {{deleted}}
+    std::tuple<const long&, int> t(ct, 42);
   }
   {
     ConvertsTo<int> ct;
-    std::tuple<int const&, void*> t(ct, nullptr); // expected-error {{deleted}}
+    std::tuple<int const&, void*> t(ct, nullptr);
   }
   {
     ConvertsTo<Derived> ct;
-    std::tuple<Base const&, int> t(ct, 42); // expected-error {{deleted}}
+    std::tuple<Base const&, int> t(ct, 42);
   }
   {
     std::allocator<int> alloc;
-    std::tuple<std::string&&> t2("hello");                            // expected-error {{deleted}}
-    std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
+    std::tuple<std::string&&> t2("hello");
+    std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello");
   }
 }

>From 2979a29eea2fdbe5591a618ace2c28b81b97cc75 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 03:38:32 -0700
Subject: [PATCH 11/13] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../PR20855_tuple_ref_binding_diagnostics.verify.cpp | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index 3c2cee9f16153..26fc99ae7bbac 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -50,9 +50,15 @@ void f() {
   // expected-error at tuple:* 0+ {{reference member '__value_' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
 #endif
 
-  { F<int, const std::string&>(std::make_tuple(1, "abc")); }
-  { std::tuple<int, const std::string&> t(1, "a"); }
-  { F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); }
+  {
+    F<int, const std::string&>(std::make_tuple(1, "abc"));
+  }
+  {
+    std::tuple<int, const std::string&> t(1, "a");
+  }
+  {
+    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc"));
+  }
   {
     ConvertsTo<int&> ct;
     std::tuple<const long&, int> t(ct, 42);

>From d3a513dc07967195de656085104a942d5e84db31 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 2 Jul 2026 04:22:09 -0700
Subject: [PATCH 12/13] Avoid workaround for
 reference_constructs_from_temporary builtin, it's fixed in clang-23; we will
 skip the related test for older version clang

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../reference_constructs_from_temporary.h         | 15 +--------------
 .../PR23256_constrain_UTypes_ctor.pass.cpp        |  5 +++++
 .../tuple.cnstr/convert_const_move.pass.cpp       |  7 ++++++-
 .../tuple/tuple.tuple/tuple.cnstr/move.pass.cpp   |  4 ++++
 4 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index c18723fb77d81..a8325620414ea 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -11,7 +11,6 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
-#include <__type_traits/is_reference.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -31,20 +30,8 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_tempo
 
 #endif
 
-// A non-reference type can never bind to a temporary, so the result is always `false` for such a
-// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
-// eagerly instantiates the construction of `_Up` (including the element's constructor exception
-// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
-//
-// https://godbolt.org/z/WMTK5cMa4
-//
-// TODO: remove this guard once the Clang builtin short-circuits on a non-reference first operand.
-template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
-inline const bool __reference_constructs_from_temporary_v = false;
-
 template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
-    __reference_constructs_from_temporary(_Tp, _Up);
+inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
index ebc31ce01cfc7..893987ce5f587 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
@@ -64,6 +64,9 @@ struct ExplicitUnconstrainedCtor {
 int main(int, char**) {
     typedef UnconstrainedCtor A;
     typedef ExplicitUnconstrainedCtor ExplicitA;
+
+// Remove this guard when compiler versions older than clang 23 are no longer supported.
+#if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
     {
         static_assert(std::is_copy_constructible<std::tuple<A>>::value, "");
         static_assert(std::is_move_constructible<std::tuple<A>>::value, "");
@@ -92,6 +95,8 @@ int main(int, char**) {
             std::tuple<ExplicitA> &&
         >::value, "");
     }
+#endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+
     {
         std::tuple<A&&> t(std::forward_as_tuple(A{}));
         ((void)t);
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
index 8f778f3fcf3a4..a8ed156a2fc33 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
@@ -113,6 +113,9 @@ constexpr bool test() {
 
   // These two test points cause gcc to ICE
 #if !defined(TEST_COMPILER_GCC)
+
+// Remove this guard when compiler versions older than clang 23 are no longer supported.
+#if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
   // sizeof...(Types) == 1 && is_convertible_v<decltype(u), T>
   {
     const std::tuple<CvtFromConstTupleRefRef> t1{};
@@ -126,7 +129,9 @@ constexpr bool test() {
     std::tuple<ConvertibleFrom<ExplicitCtrFromConstTupleRefRef>> t2{std::move(t1)};
     assert(!constMoveCtrCalled(std::get<0>(t2).v));
   }
-#endif
+#endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+
+#endif // !defined(TEST_COMPILER_GCC)
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
index d6c192d2e0543..b3982795bf6c8 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
@@ -111,6 +111,8 @@ int main(int, char**)
         assert(std::get<1>(t) == 1);
         assert(std::get<2>(t) == 2);
     }
+// Remove this guard when compiler versions older than clang 23 are no longer supported.
+#if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
     // A bug in tuple caused __tuple_leaf to use its explicit converting constructor
     //  as its move constructor. This tests that ConstructsWithTupleLeaf is not called
     // (w/ __tuple_leaf)
@@ -119,6 +121,8 @@ int main(int, char**)
         d_t d((ConstructsWithTupleLeaf()));
         d_t d2(static_cast<d_t &&>(d));
     }
+#endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+
     {
         test_sfinae<move_only_ebo>();
         test_sfinae<move_only_large>();

>From cc1c1669478fbcf815cfa48b9f94ccef44f51743 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 2 Jul 2026 04:26:44 -0700
Subject: [PATCH 13/13] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../PR23256_constrain_UTypes_ctor.pass.cpp    | 63 +++++++++----------
 .../tuple.cnstr/convert_const_move.pass.cpp   |  4 +-
 .../tuple.tuple/tuple.cnstr/move.pass.cpp     | 24 +++----
 3 files changed, 43 insertions(+), 48 deletions(-)

diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
index 893987ce5f587..a7f15e1612157 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
@@ -67,42 +67,37 @@ int main(int, char**) {
 
 // Remove this guard when compiler versions older than clang 23 are no longer supported.
 #if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
-    {
-        static_assert(std::is_copy_constructible<std::tuple<A>>::value, "");
-        static_assert(std::is_move_constructible<std::tuple<A>>::value, "");
-        static_assert(std::is_copy_constructible<std::tuple<ExplicitA>>::value, "");
-        static_assert(std::is_move_constructible<std::tuple<ExplicitA>>::value, "");
-    }
-    {
-        static_assert(std::is_constructible<
-            std::tuple<A>,
-            std::allocator_arg_t, std::allocator<int>,
-            std::tuple<A> const&
-        >::value, "");
-        static_assert(std::is_constructible<
-            std::tuple<A>,
-            std::allocator_arg_t, std::allocator<int>,
-            std::tuple<A> &&
-        >::value, "");
-        static_assert(std::is_constructible<
-            std::tuple<ExplicitA>,
-            std::allocator_arg_t, std::allocator<int>,
-            std::tuple<ExplicitA> const&
-        >::value, "");
-        static_assert(std::is_constructible<
-            std::tuple<ExplicitA>,
-            std::allocator_arg_t, std::allocator<int>,
-            std::tuple<ExplicitA> &&
-        >::value, "");
-    }
+  {
+    static_assert(std::is_copy_constructible<std::tuple<A>>::value, "");
+    static_assert(std::is_move_constructible<std::tuple<A>>::value, "");
+    static_assert(std::is_copy_constructible<std::tuple<ExplicitA>>::value, "");
+    static_assert(std::is_move_constructible<std::tuple<ExplicitA>>::value, "");
+  }
+  {
+    static_assert(
+        std::is_constructible< std::tuple<A>, std::allocator_arg_t, std::allocator<int>, std::tuple<A> const& >::value,
+        "");
+    static_assert(
+        std::is_constructible< std::tuple<A>, std::allocator_arg_t, std::allocator<int>, std::tuple<A>&& >::value, "");
+    static_assert(std::is_constructible< std::tuple<ExplicitA>,
+                                         std::allocator_arg_t,
+                                         std::allocator<int>,
+                                         std::tuple<ExplicitA> const& >::value,
+                  "");
+    static_assert(std::is_constructible< std::tuple<ExplicitA>,
+                                         std::allocator_arg_t,
+                                         std::allocator<int>,
+                                         std::tuple<ExplicitA>&& >::value,
+                  "");
+  }
 #endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
 
-    {
-        std::tuple<A&&> t(std::forward_as_tuple(A{}));
-        ((void)t);
-        std::tuple<ExplicitA&&> t2(std::forward_as_tuple(ExplicitA{}));
-        ((void)t2);
-    }
+  {
+    std::tuple<A&&> t(std::forward_as_tuple(A{}));
+    ((void)t);
+    std::tuple<ExplicitA&&> t2(std::forward_as_tuple(ExplicitA{}));
+    ((void)t2);
+  }
 
   return 0;
 }
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
index a8ed156a2fc33..383627f965137 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
@@ -115,7 +115,7 @@ constexpr bool test() {
 #if !defined(TEST_COMPILER_GCC)
 
 // Remove this guard when compiler versions older than clang 23 are no longer supported.
-#if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+#  if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
   // sizeof...(Types) == 1 && is_convertible_v<decltype(u), T>
   {
     const std::tuple<CvtFromConstTupleRefRef> t1{};
@@ -129,7 +129,7 @@ constexpr bool test() {
     std::tuple<ConvertibleFrom<ExplicitCtrFromConstTupleRefRef>> t2{std::move(t1)};
     assert(!constMoveCtrCalled(std::get<0>(t2).v));
   }
-#endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+#  endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
 
 #endif // !defined(TEST_COMPILER_GCC)
 
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
index b3982795bf6c8..99fe11a085c99 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
@@ -113,20 +113,20 @@ int main(int, char**)
     }
 // Remove this guard when compiler versions older than clang 23 are no longer supported.
 #if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
-    // A bug in tuple caused __tuple_leaf to use its explicit converting constructor
-    //  as its move constructor. This tests that ConstructsWithTupleLeaf is not called
-    // (w/ __tuple_leaf)
-    {
-        typedef std::tuple<ConstructsWithTupleLeaf> d_t;
-        d_t d((ConstructsWithTupleLeaf()));
-        d_t d2(static_cast<d_t &&>(d));
-    }
+  // A bug in tuple caused __tuple_leaf to use its explicit converting constructor
+  //  as its move constructor. This tests that ConstructsWithTupleLeaf is not called
+  // (w/ __tuple_leaf)
+  {
+    typedef std::tuple<ConstructsWithTupleLeaf> d_t;
+    d_t d((ConstructsWithTupleLeaf()));
+    d_t d2(static_cast<d_t&&>(d));
+  }
 #endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
 
-    {
-        test_sfinae<move_only_ebo>();
-        test_sfinae<move_only_large>();
-    }
+  {
+    test_sfinae<move_only_ebo>();
+    test_sfinae<move_only_large>();
+  }
 
   return 0;
 }


From libcxx-commits at lists.llvm.org  Thu Jul  2 04:27:50 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Thu, 02 Jul 2026 04:27:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a464b36.f06f4994.1e3a60.44a6@mx.google.com>


https://github.com/yronglin closed https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Thu Jul  2 04:27:51 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Thu, 02 Jul 2026 04:27:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make
 `std::reference_constructs_from_temporary` SFINAE friendly when the 1st
 template argument is not a reference type (PR #206679)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206679@github.com>
Message-ID: <6a464b37.492e23e4.e71ca.a700@mx.google.com>


yronglin wrote:

Close this PR, because we don't plan to add workaround for old compiler.

https://github.com/llvm/llvm-project/pull/206679

From libcxx-commits at lists.llvm.org  Thu Jul  2 04:29:36 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Thu, 02 Jul 2026 04:29:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Add deleted tuple
 constructor overloads (PR #205379)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205379@github.com>
Message-ID: <6a464ba0.e59e62a9.df662.0c18@mx.google.com>


yronglin wrote:

> Clang's `__reference_meows_from_temporary builtins` implementations not SFINAE friendly, I added a workaround in libc++ and create a separate PR to refine the implementation. #206527.
> 
> But I'm not sure we should also add this workaround for `std::reference_constructs_from_temporary` in this PR.

We don't plan to add workaround for old compilers(see discussions in https://github.com/llvm/llvm-project/pull/206679); instead, disable related failed test for old compiler.

https://github.com/llvm/llvm-project/pull/205379

From libcxx-commits at lists.llvm.org  Thu Jul  2 05:16:37 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Thu, 02 Jul 2026 05:16:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add assert test for string
	assign/append (PR #207164)
Message-ID: <llvm.org/llvm/llvm-project/pull/207164@github.com>


https://github.com/c8ef created https://github.com/llvm/llvm-project/pull/207164

None

>From f3ac7f40e624c78e66041665997897e90b376c86 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Thu, 2 Jul 2026 20:15:54 +0800
Subject: [PATCH] [libc++] Add assert test for string assign/append

---
 .../string.modifiers/assert.append.pass.cpp   | 39 +++++++++++++++++++
 .../string.modifiers/assert.assign.pass.cpp   | 39 +++++++++++++++++++
 2 files changed, 78 insertions(+)
 create mode 100644 libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
 create mode 100644 libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp

diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
new file mode 100644
index 0000000000000..bbf99ffa8d488
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// basic_string& append(const value_type* s, size_type n);
+// basic_string& append(const value_type* s);
+// basic_string& append(const value_type* s, size_type pos, size_type n);
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <string>
+
+#include "check_assertion.h"
+#include "min_allocator.h"
+
+template <class S>
+void test() {
+  S l1("123");
+  const char* np = nullptr;
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np, 1), "string::append received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np), "string::append received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np, 0, 1), "string::append received nullptr");
+}
+
+int main(int, char**) {
+  test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp
new file mode 100644
index 0000000000000..d5a458a3bd0eb
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// basic_string& assign(const value_type* s, size_type n);
+// basic_string& assign(const value_type* s);
+// basic_string& assign(const value_type* s, size_type pos, size_type n);
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <string>
+
+#include "check_assertion.h"
+#include "min_allocator.h"
+
+template <class S>
+void test() {
+  S l1("123");
+  const char* np = nullptr;
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np, 1), "string::assign received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np), "string::assign received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np, 0, 1), "string::assign received nullptr");
+}
+
+int main(int, char**) {
+  test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Thu Jul  2 05:19:12 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Thu, 02 Jul 2026 05:19:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add assert test for string
	assign/append (PR #207164)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207164@github.com>
Message-ID: <6a465740.6c8668f2.74a35.6657@mx.google.com>


https://github.com/c8ef edited https://github.com/llvm/llvm-project/pull/207164

From libcxx-commits at lists.llvm.org  Thu Jul  2 05:20:55 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Thu, 02 Jul 2026 05:20:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add assert test for string
	assign/append (PR #207164)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207164@github.com>
Message-ID: <6a4657a7.eea6ab6e.1256b6.1acf@mx.google.com>


c8ef wrote:

BTW, I put all three overloads in a single file. I'm not sure if it would be better to split them into separate files, like the debug tests in the same directory.

https://github.com/llvm/llvm-project/pull/207164

From libcxx-commits at lists.llvm.org  Thu Jul  2 05:22:16 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Thu, 02 Jul 2026 05:22:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add assert test for string
	assign/append (PR #207164)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207164@github.com>
Message-ID: <6a4657f8.3157459c.24b6e5.e043@mx.google.com>


https://github.com/c8ef ready_for_review https://github.com/llvm/llvm-project/pull/207164

From libcxx-commits at lists.llvm.org  Thu Jul  2 05:22:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 05:22:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add assert test for string
	assign/append (PR #207164)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207164@github.com>
Message-ID: <6a46581f.6c8668f2.74a35.f013@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Connector Switch (c8ef)

<details>
<summary>Changes</summary>

Fixes https://github.com/llvm/llvm-project/pull/206320#discussion_r3507502638.

---
Full diff: https://github.com/llvm/llvm-project/pull/207164.diff


2 Files Affected:

- (added) libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp (+39) 
- (added) libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp (+39) 


``````````diff
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
new file mode 100644
index 0000000000000..bbf99ffa8d488
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// basic_string& append(const value_type* s, size_type n);
+// basic_string& append(const value_type* s);
+// basic_string& append(const value_type* s, size_type pos, size_type n);
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <string>
+
+#include "check_assertion.h"
+#include "min_allocator.h"
+
+template <class S>
+void test() {
+  S l1("123");
+  const char* np = nullptr;
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np, 1), "string::append received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np), "string::append received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np, 0, 1), "string::append received nullptr");
+}
+
+int main(int, char**) {
+  test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp
new file mode 100644
index 0000000000000..d5a458a3bd0eb
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// basic_string& assign(const value_type* s, size_type n);
+// basic_string& assign(const value_type* s);
+// basic_string& assign(const value_type* s, size_type pos, size_type n);
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <string>
+
+#include "check_assertion.h"
+#include "min_allocator.h"
+
+template <class S>
+void test() {
+  S l1("123");
+  const char* np = nullptr;
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np, 1), "string::assign received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np), "string::assign received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np, 0, 1), "string::assign received nullptr");
+}
+
+int main(int, char**) {
+  test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+
+  return 0;
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/207164

From libcxx-commits at lists.llvm.org  Thu Jul  2 07:00:25 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 07:00:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a466ef9.00bedb31.45ae9.391c@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/206498

>From 6fc88c1fba7be7c7eb5dcb0dddd2ec12e4a75ec8 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 18:53:17 +0530
Subject: [PATCH 1/5] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. Omit the noreplace mode strings in
 __make_mdstring() on AIX, causing open() to return nullptr for these modes.
 Update the test to expect failure on AIX for all noreplace cases, since the
 platform cannot provide atomic exclusive-open semantics.

---
 libcxx/include/fstream                        | 10 ++++++++
 .../filebuf.members/open_pointer.pass.cpp     | 24 +++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b5fb65820628a..2ec5374ce463e 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -634,6 +634,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -644,6 +653,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..f7ab0239a1b18 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -75,7 +73,10 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -83,7 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 
@@ -94,7 +101,10 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -102,7 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 #  endif

>From 0715d9900e3781e298cca572f5955eb259d297ac Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 12:40:23 -0400
Subject: [PATCH 2/5] fix format and add warning

---
 libcxx/include/fstream                               |  3 +++
 .../fstreams/filebuf.members/open_pointer.pass.cpp   | 12 ++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 2ec5374ce463e..a5f1e80960054 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,6 +642,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if defined(_AIX) || defined(__TOS_AIX__)
+#        warning 'exclusive mode is unsupported on this target'
+#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index f7ab0239a1b18..627a65cac3f2f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -84,13 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#  else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#  endif
           }
         }
 
@@ -112,13 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#    else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#    endif
           }
         }
 #  endif

>From 072a5b2b327fe402617bd7b5ccf081931e685289 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 12:52:06 -0400
Subject: [PATCH 3/5] fix warnings treated as errors

---
 libcxx/include/fstream | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index a5f1e80960054..54f1555c15cbe 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -643,7 +643,10 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
 #      if defined(_AIX) || defined(__TOS_AIX__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic warning "-Wcpp"
 #        warning 'exclusive mode is unsupported on this target'
+#        pragma GCC diagnostic pop
 #      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:

>From 9c99f50050c182905f10cd7a5022b32600d0e7b2 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 13:49:31 -0400
Subject: [PATCH 4/5] add warning as fprintf

---
 libcxx/include/fstream | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 54f1555c15cbe..7c7f7c6b0db52 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,12 +642,6 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if defined(_AIX) || defined(__TOS_AIX__)
-#        pragma GCC diagnostic push
-#        pragma GCC diagnostic warning "-Wcpp"
-#        warning 'exclusive mode is unsupported on this target'
-#        pragma GCC diagnostic pop
-#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -750,9 +744,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr)
+  if (!__mdstr){
+#     if defined(_AIX) || defined(__TOS_AIX__)
+    if (__mode & ios_base::noreplace)
+      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
+#     endif
     return nullptr;
-
+}
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From d91a5ff2deca3f6f1c737ad0889afcbc198dca74 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:30:10 +0530
Subject: [PATCH 5/5] fix formatting

---
 libcxx/include/fstream | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7c7f7c6b0db52..9b176ddb5a469 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -744,13 +744,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr){
-#     if defined(_AIX) || defined(__TOS_AIX__)
+  if (!__mdstr) {
+#      if defined(_AIX) || defined(__TOS_AIX__)
     if (__mode & ios_base::noreplace)
       fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#     endif
+#      endif
     return nullptr;
-}
+  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif


From libcxx-commits at lists.llvm.org  Thu Jul  2 07:08:08 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 07:08:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4670c8.abb10101.208f3e.2450@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/206498

>From 6fc88c1fba7be7c7eb5dcb0dddd2ec12e4a75ec8 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 18:53:17 +0530
Subject: [PATCH 1/6] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. Omit the noreplace mode strings in
 __make_mdstring() on AIX, causing open() to return nullptr for these modes.
 Update the test to expect failure on AIX for all noreplace cases, since the
 platform cannot provide atomic exclusive-open semantics.

---
 libcxx/include/fstream                        | 10 ++++++++
 .../filebuf.members/open_pointer.pass.cpp     | 24 +++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b5fb65820628a..2ec5374ce463e 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -634,6 +634,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -644,6 +653,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..f7ab0239a1b18 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -75,7 +73,10 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -83,7 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 
@@ -94,7 +101,10 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -102,7 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 #  endif

>From 0715d9900e3781e298cca572f5955eb259d297ac Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 12:40:23 -0400
Subject: [PATCH 2/6] fix format and add warning

---
 libcxx/include/fstream                               |  3 +++
 .../fstreams/filebuf.members/open_pointer.pass.cpp   | 12 ++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 2ec5374ce463e..a5f1e80960054 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,6 +642,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if defined(_AIX) || defined(__TOS_AIX__)
+#        warning 'exclusive mode is unsupported on this target'
+#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index f7ab0239a1b18..627a65cac3f2f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -84,13 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#  else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#  endif
           }
         }
 
@@ -112,13 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#    else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#    endif
           }
         }
 #  endif

>From 072a5b2b327fe402617bd7b5ccf081931e685289 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 12:52:06 -0400
Subject: [PATCH 3/6] fix warnings treated as errors

---
 libcxx/include/fstream | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index a5f1e80960054..54f1555c15cbe 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -643,7 +643,10 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
 #      if defined(_AIX) || defined(__TOS_AIX__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic warning "-Wcpp"
 #        warning 'exclusive mode is unsupported on this target'
+#        pragma GCC diagnostic pop
 #      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:

>From 9c99f50050c182905f10cd7a5022b32600d0e7b2 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 13:49:31 -0400
Subject: [PATCH 4/6] add warning as fprintf

---
 libcxx/include/fstream | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 54f1555c15cbe..7c7f7c6b0db52 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,12 +642,6 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if defined(_AIX) || defined(__TOS_AIX__)
-#        pragma GCC diagnostic push
-#        pragma GCC diagnostic warning "-Wcpp"
-#        warning 'exclusive mode is unsupported on this target'
-#        pragma GCC diagnostic pop
-#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -750,9 +744,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr)
+  if (!__mdstr){
+#     if defined(_AIX) || defined(__TOS_AIX__)
+    if (__mode & ios_base::noreplace)
+      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
+#     endif
     return nullptr;
-
+}
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From d91a5ff2deca3f6f1c737ad0889afcbc198dca74 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:30:10 +0530
Subject: [PATCH 5/6] fix formatting

---
 libcxx/include/fstream | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7c7f7c6b0db52..9b176ddb5a469 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -744,13 +744,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr){
-#     if defined(_AIX) || defined(__TOS_AIX__)
+  if (!__mdstr) {
+#      if defined(_AIX) || defined(__TOS_AIX__)
     if (__mode & ios_base::noreplace)
       fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#     endif
+#      endif
     return nullptr;
-}
+  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From f3d25942e836d9a90b689f74747d241e2b40596b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:37:48 +0530
Subject: [PATCH 6/6] add the remaining test files

---
 .../fstreams/fstream.cons/pointer.pass.cpp    | 24 +++++++++++++++---
 .../fstream.members/open_pointer.pass.cpp     | 24 +++++++++++++++---
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 24 +++++++++++++++---
 .../ofstream.members/open_pointer.pass.cpp    | 25 ++++++++++++++++---
 4 files changed, 81 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..a677b16e76479 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
@@ -91,14 +89,23 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -108,14 +115,23 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..92c0cf1aa0c00 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -74,7 +72,10 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -82,7 +83,13 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -93,7 +100,10 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -101,7 +111,13 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..4fae51f38adbd 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
@@ -145,14 +143,23 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -162,14 +169,23 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..493af1bda9c98 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -82,7 +80,10 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -90,7 +91,13 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -101,14 +108,24 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
+
           {
             std::remove(tmp.c_str());
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif


From libcxx-commits at lists.llvm.org  Thu Jul  2 07:08:59 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 07:08:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ThreadSafety] Add thread safety
 annotations for variadic std::scoped_lock (PR #204462)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204462@github.com>
Message-ID: <6a4670fb.58bdd12c.21857.0864@mx.google.com>


https://github.com/AnthonyCalandraGeotab updated https://github.com/llvm/llvm-project/pull/204462

>From 59ff421a4fdb2dd8ccf1835de838ff1110eade7d Mon Sep 17 00:00:00 2001
From: Anthony Calandra <anthony at anthony-calandra.com>
Date: Wed, 17 Jun 2026 17:05:52 -0400
Subject: [PATCH] [libc++][ThreadSafety] Add thread safety annotations for
 variadic std::scoped_lock

The thread safety annotations on std::scoped_lock were previously only
applied to the empty and single-mutex specializations. The general
variadic specialization carried no annotations, so -Wthread-safety
considered none of the mutexes held inside a multi-mutex scoped_lock
block and reported the guarded data accessed there as unprotected.

Now that Clang supports pack expansion inside thread safety attributes
(landed for Clang 21), annotate the variadic scoped_lock with
acquire_capability/requires_capability on its constructors,
release_capability on its destructor, and scoped_lockable on the class.

Fixes #42000.
---
 libcxx/docs/ReleaseNotes/23.rst               |  3 +
 libcxx/include/__configuration/attributes.h   |  6 +-
 libcxx/include/__mutex/lock_guard.h           |  2 +-
 libcxx/include/__mutex/mutex.h                |  2 +-
 libcxx/include/mutex                          | 18 +++--
 libcxx/include/shared_mutex                   |  4 +-
 .../thread_safety_scoped_lock.pass.cpp        | 69 +++++++++++++++++++
 .../thread_safety_scoped_lock.verify.cpp      | 59 ++++++++++++++++
 8 files changed, 150 insertions(+), 13 deletions(-)
 create mode 100644 libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.pass.cpp
 create mode 100644 libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.verify.cpp

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1852351cedc4e..af4d29dce41ee 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -71,6 +71,9 @@ Improvements and New Features
   unspecified non-null string, so user code that does not match on the exact message remains correct.
 - ``ifstream::read`` has been optimized to pass through large reads to system calls directly instead of copying them in
   chunks.
+- ``std::scoped_lock`` now carries Clang thread safety annotations when locking multiple mutexes, so
+  ``-Wthread-safety`` no longer incorrectly reports the guarded data accessed inside the block as
+  unprotected. This requires Clang 21 or later.
 
 Deprecations and Removals
 -------------------------
diff --git a/libcxx/include/__configuration/attributes.h b/libcxx/include/__configuration/attributes.h
index c543ba9cb9e25..a49bedd1b874a 100644
--- a/libcxx/include/__configuration/attributes.h
+++ b/libcxx/include/__configuration/attributes.h
@@ -342,10 +342,10 @@
 #  define _LIBCPP_TRY_ACQUIRE_SHARED_CAPABILITY(...)
 #endif
 
-#if __has_cpp_attribute(_Clang::__release_capability__)
-#  define _LIBCPP_RELEASE_CAPABILITY [[_Clang::__release_capability__]]
+#if __has_attribute(__release_capability__)
+#  define _LIBCPP_RELEASE_CAPABILITY(...) __attribute__((__release_capability__(__VA_ARGS__)))
 #else
-#  define _LIBCPP_RELEASE_CAPABILITY
+#  define _LIBCPP_RELEASE_CAPABILITY(...)
 #endif
 
 #if __has_cpp_attribute(_Clang::__release_shared_capability__)
diff --git a/libcxx/include/__mutex/lock_guard.h b/libcxx/include/__mutex/lock_guard.h
index 67a25ed2f8a89..29022e8c23ece 100644
--- a/libcxx/include/__mutex/lock_guard.h
+++ b/libcxx/include/__mutex/lock_guard.h
@@ -34,7 +34,7 @@ class _LIBCPP_SCOPED_LOCKABLE lock_guard {
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI lock_guard(mutex_type& __m, adopt_lock_t) _LIBCPP_REQUIRES_CAPABILITY(__m)
       : __m_(__m) {}
-  _LIBCPP_RELEASE_CAPABILITY _LIBCPP_HIDE_FROM_ABI ~lock_guard() { __m_.unlock(); }
+  _LIBCPP_RELEASE_CAPABILITY() _LIBCPP_HIDE_FROM_ABI ~lock_guard() { __m_.unlock(); }
 
   lock_guard(lock_guard const&)            = delete;
   lock_guard& operator=(lock_guard const&) = delete;
diff --git a/libcxx/include/__mutex/mutex.h b/libcxx/include/__mutex/mutex.h
index d0addacc2893f..f71cba03cd4dc 100644
--- a/libcxx/include/__mutex/mutex.h
+++ b/libcxx/include/__mutex/mutex.h
@@ -39,7 +39,7 @@ class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_CAPABILITY("mutex") mutex {
 
   _LIBCPP_ACQUIRE_CAPABILITY() void lock();
   [[__nodiscard__]] _LIBCPP_TRY_ACQUIRE_CAPABILITY(true) bool try_lock() _NOEXCEPT;
-  _LIBCPP_RELEASE_CAPABILITY void unlock() _NOEXCEPT;
+  _LIBCPP_RELEASE_CAPABILITY() void unlock() _NOEXCEPT;
 
   typedef __libcpp_mutex_t* native_handle_type;
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__m_; }
diff --git a/libcxx/include/mutex b/libcxx/include/mutex
index 75b080f8cd088..26ff343a21c1d 100644
--- a/libcxx/include/mutex
+++ b/libcxx/include/mutex
@@ -458,7 +458,7 @@ public:
     __m_.lock();
   }
 
-  _LIBCPP_RELEASE_CAPABILITY _LIBCPP_HIDE_FROM_ABI ~scoped_lock() { __m_.unlock(); }
+  _LIBCPP_RELEASE_CAPABILITY() _LIBCPP_HIDE_FROM_ABI ~scoped_lock() { __m_.unlock(); }
 
   [[nodiscard]]
   _LIBCPP_HIDE_FROM_ABI explicit scoped_lock(adopt_lock_t, mutex_type& __m) _LIBCPP_REQUIRES_CAPABILITY(__m)
@@ -469,25 +469,31 @@ public:
 };
 
 template <class... _MArgs>
-class scoped_lock {
+class _LIBCPP_SCOPED_LOCKABLE scoped_lock {
   static_assert(sizeof...(_MArgs) > 1, "At least 2 lock types required");
   typedef tuple<_MArgs&...> _MutexTuple;
 
 public:
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit scoped_lock(_MArgs&... __margs) : __t_(__margs...) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit scoped_lock(_MArgs&... __margs) _LIBCPP_ACQUIRE_CAPABILITY(__margs...)
+      : __t_(__margs...) {
     std::lock(__margs...);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI scoped_lock(adopt_lock_t, _MArgs&... __margs) : __t_(__margs...) {}
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI scoped_lock(adopt_lock_t, _MArgs&... __margs)
+      _LIBCPP_REQUIRES_CAPABILITY(__margs...)
+      : __t_(__margs...) {}
 
-  _LIBCPP_HIDE_FROM_ABI ~scoped_lock() { __unlock_unpack(make_index_sequence<sizeof...(_MArgs)>(), __t_); }
+  _LIBCPP_RELEASE_CAPABILITY() _LIBCPP_HIDE_FROM_ABI ~scoped_lock() {
+    __unlock_unpack(make_index_sequence<sizeof...(_MArgs)>(), __t_);
+  }
 
   scoped_lock(scoped_lock const&)            = delete;
   scoped_lock& operator=(scoped_lock const&) = delete;
 
 private:
   template <size_t... _Indx>
-  _LIBCPP_HIDE_FROM_ABI static void __unlock_unpack(index_sequence<_Indx...>, _MutexTuple& __mt) {
+  _LIBCPP_HIDE_FROM_ABI static void __unlock_unpack(index_sequence<_Indx...>, _MutexTuple& __mt)
+      _LIBCPP_RELEASE_CAPABILITY(std::get<_Indx>(__mt)...) {
     (std::get<_Indx>(__mt).unlock(), ...);
   }
 
diff --git a/libcxx/include/shared_mutex b/libcxx/include/shared_mutex
index 9479f0aa616f8..8fc89fe1cb97c 100644
--- a/libcxx/include/shared_mutex
+++ b/libcxx/include/shared_mutex
@@ -200,7 +200,7 @@ public:
   [[nodiscard]] _LIBCPP_TRY_ACQUIRE_CAPABILITY(true) _LIBCPP_HIDE_FROM_ABI bool try_lock() {
     return __base_.try_lock();
   }
-  _LIBCPP_RELEASE_CAPABILITY _LIBCPP_HIDE_FROM_ABI void unlock() { return __base_.unlock(); }
+  _LIBCPP_RELEASE_CAPABILITY() _LIBCPP_HIDE_FROM_ABI void unlock() { return __base_.unlock(); }
 
   // Shared ownership
   _LIBCPP_ACQUIRE_SHARED_CAPABILITY _LIBCPP_HIDE_FROM_ABI void lock_shared() { return __base_.lock_shared(); }
@@ -262,7 +262,7 @@ public:
     return true;
   }
 
-  _LIBCPP_RELEASE_CAPABILITY void unlock();
+  _LIBCPP_RELEASE_CAPABILITY() void unlock();
 
   // Shared ownership
   _LIBCPP_ACQUIRE_SHARED_CAPABILITY void lock_shared();
diff --git a/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.pass.cpp b/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.pass.cpp
new file mode 100644
index 0000000000000..06751f9d49215
--- /dev/null
+++ b/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: no-threads, c++03, c++11, c++14
+
+// <mutex>
+
+// GCC doesn't have thread safety attributes
+// UNSUPPORTED: gcc
+
+// ADDITIONAL_COMPILE_FLAGS: -Wthread-safety
+
+#include <mutex>
+
+std::mutex m1;
+std::mutex m2;
+int data1 __attribute__((guarded_by(m1)));
+int data2 __attribute__((guarded_by(m2)));
+int data3;
+
+static void no_mutex() {
+  std::scoped_lock<> lock;
+  data3++;
+}
+
+static void one_mutex() {
+  std::scoped_lock<std::mutex> lock(m1);
+  data1++;
+}
+
+static void two_mutexes() {
+  std::scoped_lock<std::mutex, std::mutex> lock(m1, m2);
+  data1++;
+  data2++;
+}
+
+static void adopt_none() {
+  std::scoped_lock<> lock(std::adopt_lock);
+  data3++;
+}
+
+static void adopt_one() {
+  m1.lock();
+  std::scoped_lock<std::mutex> lock(std::adopt_lock, m1);
+  data1++;
+}
+
+static void adopt_two() {
+  m1.lock();
+  m2.lock();
+  std::scoped_lock<std::mutex, std::mutex> lock(std::adopt_lock, m1, m2);
+  data1++;
+  data2++;
+}
+
+int main(int, char**) {
+  no_mutex();
+  one_mutex();
+  two_mutexes();
+  adopt_none();
+  adopt_one();
+  adopt_two();
+  return 0;
+}
diff --git a/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.verify.cpp b/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.verify.cpp
new file mode 100644
index 0000000000000..3f9dd62c91f52
--- /dev/null
+++ b/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.verify.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: no-threads, c++03, c++11, c++14
+
+// <mutex>
+
+// GCC doesn't have thread safety attributes
+// UNSUPPORTED: gcc
+
+// ADDITIONAL_COMPILE_FLAGS: -Wthread-safety
+
+#include <mutex>
+
+std::mutex m1;
+std::mutex m2;
+int data1 __attribute__((guarded_by(m1)));
+int data2 __attribute__((guarded_by(m2)));
+
+void no_lock_write() {
+  data1++; // expected-warning {{writing variable 'data1' requires holding mutex 'm1' exclusively}}
+}
+
+void no_lock_write2() {
+  std::scoped_lock<> lock;
+  data1++; // expected-warning {{writing variable 'data1' requires holding mutex 'm1' exclusively}}
+}
+
+void wrong_lock_write() {
+  std::scoped_lock<std::mutex> lock(m1);
+  data2++; // expected-warning {{writing variable 'data2' requires holding mutex 'm2' exclusively}}
+}
+
+void adopt_without_lock() {
+  // expected-warning at +1 {{calling function 'scoped_lock' requires holding mutex 'm1' exclusively}}
+  std::scoped_lock<std::mutex> lock(std::adopt_lock, m1);
+  data1++; // expected-warning {{writing variable 'data1' requires holding mutex 'm1' exclusively}}
+}
+
+void adopt_two_only_one_locked() {
+  m1.lock();
+  // expected-warning at +1 {{calling function 'scoped_lock' requires holding mutex 'm2' exclusively}}
+  std::scoped_lock<std::mutex, std::mutex> lock(std::adopt_lock, m1, m2);
+  data1++;
+  data2++; // expected-warning {{writing variable 'data2' requires holding mutex 'm2' exclusively}}
+}
+
+void after_scoped_lock_scope() {
+  {
+    std::scoped_lock<std::mutex> lock(m1);
+    data1++;
+  }
+  data1++; // expected-warning {{writing variable 'data1' requires holding mutex 'm1' exclusively}}
+}


From libcxx-commits at lists.llvm.org  Thu Jul  2 07:13:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 07:13:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a467206.dd2b4bd3.35cd5e.7452@mx.google.com>


https://github.com/Himadhith edited https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Thu Jul  2 07:13:40 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 07:13:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a467214.c56fb102.11841f.8ef0@mx.google.com>


https://github.com/Himadhith edited https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Thu Jul  2 07:18:49 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Thu, 02 Jul 2026 07:18:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add assert test for string
	assign/append (PR #207164)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207164@github.com>
Message-ID: <6a467349.d1496011.3d49cb.1db6@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/207164

>From f3ac7f40e624c78e66041665997897e90b376c86 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Thu, 2 Jul 2026 20:15:54 +0800
Subject: [PATCH 1/2] [libc++] Add assert test for string assign/append

---
 .../string.modifiers/assert.append.pass.cpp   | 39 +++++++++++++++++++
 .../string.modifiers/assert.assign.pass.cpp   | 39 +++++++++++++++++++
 2 files changed, 78 insertions(+)
 create mode 100644 libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
 create mode 100644 libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp

diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
new file mode 100644
index 0000000000000..bbf99ffa8d488
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// basic_string& append(const value_type* s, size_type n);
+// basic_string& append(const value_type* s);
+// basic_string& append(const value_type* s, size_type pos, size_type n);
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <string>
+
+#include "check_assertion.h"
+#include "min_allocator.h"
+
+template <class S>
+void test() {
+  S l1("123");
+  const char* np = nullptr;
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np, 1), "string::append received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np), "string::append received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np, 0, 1), "string::append received nullptr");
+}
+
+int main(int, char**) {
+  test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp
new file mode 100644
index 0000000000000..d5a458a3bd0eb
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// basic_string& assign(const value_type* s, size_type n);
+// basic_string& assign(const value_type* s);
+// basic_string& assign(const value_type* s, size_type pos, size_type n);
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <string>
+
+#include "check_assertion.h"
+#include "min_allocator.h"
+
+template <class S>
+void test() {
+  S l1("123");
+  const char* np = nullptr;
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np, 1), "string::assign received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np), "string::assign received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np, 0, 1), "string::assign received nullptr");
+}
+
+int main(int, char**) {
+  test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+
+  return 0;
+}

>From 3295f500a0f660eb51f790897c4f17cf1c403418 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Thu, 2 Jul 2026 22:18:25 +0800
Subject: [PATCH 2/2] try fixing ci

---
 .../basic.string/string.modifiers/assert.append.pass.cpp       | 3 ++-
 .../basic.string/string.modifiers/assert.assign.pass.cpp       | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
index bbf99ffa8d488..c3745ca0a762d 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
@@ -14,7 +14,8 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: libcpp-hardening-mode=none
+// UNSUPPORTED: libcpp-hardening-mode={{none|fast}}
+// UNSUPPORTED: libcpp-assertion-semantic={{ignore|observe}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 #include <string>
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp
index d5a458a3bd0eb..80abd15929735 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp
@@ -14,7 +14,8 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: libcpp-hardening-mode=none
+// UNSUPPORTED: libcpp-hardening-mode={{none|fast}}
+// UNSUPPORTED: libcpp-assertion-semantic={{ignore|observe}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 #include <string>


From libcxx-commits at lists.llvm.org  Thu Jul  2 07:52:06 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 07:52:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a467b16.51dfe084.2934a9.8d72@mx.google.com>


================
@@ -63,6 +64,7 @@ __for_each_n(_InputIterator __first, _Size __orig_n, _Func&& __f, _Proj& __proj)
 template <class _InputIterator, class _Size, class _Func>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
 for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__orig_n >= 0, "for_each_n requires a non-negative count");
----------------
ldionne wrote:

I think changing that would be a paper since it's technically a design change. I sent this email to LWG to check the temperature and CC'd you:

> 
> Hi,
> 
> While fixing [a libc++ bug](https://github.com/llvm/llvm-project/issues/193613) where copy_n, fill_n and generate_n crashed on negative counts, we noticed an inconsistency in how the standard treats a negative count:
> 
> - copy_n, fill_n and generate_n all define their effects in terms of max(0, n), so a negative n is well-defined and simply a no-op. The design intent of supporting negative counts is pretty clear from the wording.
> - for_each_n instead states "Preconditions: n >= 0 is true", making a negative n undefined behavior.
> 
> Is this divergence intentional? If not, I could write a (very) short paper to propose supporting negative counts in for_each_n, for consistency. I would have preferred a precondition everywhere instead, but the design intent in copy_n & friends is clear from the wording, so I have no plans to relitigate that.
> 
> Thanks,
> Louis

For now I think we should assume this is the intended design, and implement that.

https://github.com/llvm/llvm-project/pull/207086

From libcxx-commits at lists.llvm.org  Thu Jul  2 07:52:28 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 07:52:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a467b2c.b195937d.19ab94.2afc@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/207086

From libcxx-commits at lists.llvm.org  Thu Jul  2 08:52:41 2026
From: libcxx-commits at lists.llvm.org (Xavier Roche via libcxx-commits)
Date: Thu, 02 Jul 2026 08:52:41 -0700 (PDT)
Subject: [libcxx-commits] [clang] [compiler-rt] [libcxx] [Clang] Support
 atomic operations on _BitInt(N) (PR #204815)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204815@github.com>
Message-ID: <6a468949.27b3b003.1610ae.d606@mx.google.com>


================
@@ -557,6 +558,183 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
   return Builder.CreateSelect(Cmp, OldVal, RHS, "newval");
 }
 
+/// Classify an atomic op as an arithmetic/bitwise read-modify-write (one that
+/// normally lowers to a single `atomicrmw`), mapping it to the matching
+/// `AtomicRMWInst::BinOp` and reporting whether the builtin returns the new
+/// value (`<op>_fetch`) rather than the old value (`fetch_<op>`). \p IsSigned
+/// selects signed vs unsigned min/max. Returns false for exchange, load, store,
+/// compare-exchange, and any non-RMW op, none of which need the _BitInt loop.
+static bool classifyBitIntRMW(AtomicExpr::AtomicOp Op, bool IsSigned,
+                              llvm::AtomicRMWInst::BinOp &BinOp,
+                              bool &ReturnsNew) {
+  using RMW = llvm::AtomicRMWInst;
+  switch (Op) {
+  case AtomicExpr::AO__c11_atomic_fetch_add:
+  case AtomicExpr::AO__hip_atomic_fetch_add:
+  case AtomicExpr::AO__opencl_atomic_fetch_add:
+  case AtomicExpr::AO__atomic_fetch_add:
+  case AtomicExpr::AO__scoped_atomic_fetch_add:
+    BinOp = RMW::Add, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_add_fetch:
+  case AtomicExpr::AO__scoped_atomic_add_fetch:
+    BinOp = RMW::Add, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_sub:
+  case AtomicExpr::AO__hip_atomic_fetch_sub:
+  case AtomicExpr::AO__opencl_atomic_fetch_sub:
+  case AtomicExpr::AO__atomic_fetch_sub:
+  case AtomicExpr::AO__scoped_atomic_fetch_sub:
+    BinOp = RMW::Sub, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_sub_fetch:
+  case AtomicExpr::AO__scoped_atomic_sub_fetch:
+    BinOp = RMW::Sub, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_and:
+  case AtomicExpr::AO__hip_atomic_fetch_and:
+  case AtomicExpr::AO__opencl_atomic_fetch_and:
+  case AtomicExpr::AO__atomic_fetch_and:
+  case AtomicExpr::AO__scoped_atomic_fetch_and:
+    BinOp = RMW::And, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_and_fetch:
+  case AtomicExpr::AO__scoped_atomic_and_fetch:
+    BinOp = RMW::And, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_or:
+  case AtomicExpr::AO__hip_atomic_fetch_or:
+  case AtomicExpr::AO__opencl_atomic_fetch_or:
+  case AtomicExpr::AO__atomic_fetch_or:
+  case AtomicExpr::AO__scoped_atomic_fetch_or:
+    BinOp = RMW::Or, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_or_fetch:
+  case AtomicExpr::AO__scoped_atomic_or_fetch:
+    BinOp = RMW::Or, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_xor:
+  case AtomicExpr::AO__hip_atomic_fetch_xor:
+  case AtomicExpr::AO__opencl_atomic_fetch_xor:
+  case AtomicExpr::AO__atomic_fetch_xor:
+  case AtomicExpr::AO__scoped_atomic_fetch_xor:
+    BinOp = RMW::Xor, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_xor_fetch:
+  case AtomicExpr::AO__scoped_atomic_xor_fetch:
+    BinOp = RMW::Xor, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_nand:
+  case AtomicExpr::AO__atomic_fetch_nand:
+  case AtomicExpr::AO__scoped_atomic_fetch_nand:
+    BinOp = RMW::Nand, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_nand_fetch:
+  case AtomicExpr::AO__scoped_atomic_nand_fetch:
+    BinOp = RMW::Nand, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_min:
+  case AtomicExpr::AO__hip_atomic_fetch_min:
+  case AtomicExpr::AO__opencl_atomic_fetch_min:
+  case AtomicExpr::AO__atomic_fetch_min:
+  case AtomicExpr::AO__scoped_atomic_fetch_min:
+    BinOp = IsSigned ? RMW::Min : RMW::UMin, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_min_fetch:
+  case AtomicExpr::AO__scoped_atomic_min_fetch:
+    BinOp = IsSigned ? RMW::Min : RMW::UMin, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_max:
+  case AtomicExpr::AO__hip_atomic_fetch_max:
+  case AtomicExpr::AO__opencl_atomic_fetch_max:
+  case AtomicExpr::AO__atomic_fetch_max:
+  case AtomicExpr::AO__scoped_atomic_fetch_max:
+    BinOp = IsSigned ? RMW::Max : RMW::UMax, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_max_fetch:
+  case AtomicExpr::AO__scoped_atomic_max_fetch:
+    BinOp = IsSigned ? RMW::Max : RMW::UMax, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__atomic_fetch_uinc:
+  case AtomicExpr::AO__scoped_atomic_fetch_uinc:
+    BinOp = RMW::UIncWrap, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_fetch_udec:
+  case AtomicExpr::AO__scoped_atomic_fetch_udec:
+    BinOp = RMW::UDecWrap, ReturnsNew = false;
+    return true;
+  default:
+    return false;
+  }
+}
+
+/// True for a `_BitInt(N)` whose value width N differs from its in-memory width
+/// (e.g. `_BitInt(37)` occupies 64 bits), so the high bits are padding.
+static bool hasBitIntPadding(QualType T, const ASTContext &C) {
+  if (const auto *BIT = T->getAs<BitIntType>())
+    return BIT->getNumBits() != C.getTypeSize(T);
+  return false;
+}
+
+/// Map a constant C ABI memory order to an llvm ordering. A non-constant order
+/// is handled conservatively with the strongest ordering.
+static llvm::AtomicOrdering atomicOrderOrSeqCst(llvm::Value *Order) {
+  auto *C = dyn_cast<llvm::ConstantInt>(Order);
+  if (!C || !llvm::isValidAtomicOrderingCABI(C->getZExtValue()))
+    return llvm::AtomicOrdering::SequentiallyConsistent;
+  switch (static_cast<llvm::AtomicOrderingCABI>(C->getZExtValue())) {
+  case llvm::AtomicOrderingCABI::relaxed:
+    return llvm::AtomicOrdering::Monotonic;
+  case llvm::AtomicOrderingCABI::consume:
+  case llvm::AtomicOrderingCABI::acquire:
+    return llvm::AtomicOrdering::Acquire;
+  case llvm::AtomicOrderingCABI::release:
+    return llvm::AtomicOrdering::Release;
+  case llvm::AtomicOrderingCABI::acq_rel:
+    return llvm::AtomicOrdering::AcquireRelease;
+  case llvm::AtomicOrderingCABI::seq_cst:
+    return llvm::AtomicOrdering::SequentiallyConsistent;
+  }
+  llvm_unreachable("invalid CABI ordering");
+}
+
+/// Emit a `_BitInt(N)` atomic read-modify-write as a compare-exchange loop. A
+/// single `atomicrmw` on the padded memory integer would carry into / compare
+/// the padding bits, and no arbitrary-width `__atomic_fetch_*` libcall exists
+/// for wide widths.
+///
+/// The update computes at value width N (so the result wraps mod 2^N and is
+/// independent of padding). EmitAtomicUpdate carries the raw loaded
+/// representation as the cmpxchg expected, so an object with non-canonical
+/// padding (e.g. written through a union) still converges instead of spinning
+/// forever; the desired it writes back is canonical. See P0528.
+static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
+                                      Address Ptr, Address Val1,
+                                      QualType AtomicTy,
+                                      llvm::AtomicRMWInst::BinOp BinOp,
+                                      bool ReturnsNew, llvm::Value *Order) {
+  QualType ValTy = E->getValueType();
+  llvm::AtomicOrdering AO = atomicOrderOrSeqCst(Order);
+
+  LValue AtomicLVal = CGF.MakeAddrLValue(Ptr, AtomicTy);
+  AtomicInfo Atomics(CGF, AtomicLVal);
+
+  llvm::Value *RHS =
+      CGF.EmitLoadOfScalar(CGF.MakeAddrLValue(Val1, ValTy), E->getExprLoc());
+
+  llvm::Value *Old = nullptr, *New = nullptr;
+  Atomics.EmitAtomicUpdate(
+      AO,
+      [&](RValue OldRV) {
+        Old = OldRV.getScalarVal();
+        New = llvm::buildAtomicRMWValue(BinOp, CGF.Builder, Old, RHS);
+        return RValue::get(New);
+      },
+      E->isVolatile());
+
+  return RValue::get(ReturnsNew ? New : Old);
+}
----------------
xroche wrote:

Agreed that non-power-of-two `atomicrmw` in IR is the better long-term home: it covers every frontend at once and lets the backend pick a better sequence than a generic loop. My worry is the large scope: that change touches the verifier, `AtomicExpandPass`, type legalization, and the backends, and it still owes the same padding-canonicalization decision we're sorting out above, just moved into LangRef.

So I'd treat this frontend patch as the near-term unblock for #60904 and pursue the IR extension as the follow-up, reusing the same padding contract. Happy to file an issue/RFC for the IR path. Do you see the IR change as a prerequisite, or is landing the frontend version first acceptable?

https://github.com/llvm/llvm-project/pull/204815

From libcxx-commits at lists.llvm.org  Thu Jul  2 09:03:10 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 09:03:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Move the
 lower_bound/upper_bound benchmark to the sorting directory (PR #207215)
Message-ID: <llvm.org/llvm/llvm-project/pull/207215@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/207215

These algorithms are not under [nonmodifying], they are under [sorting] in the Standard.

>From df40598bdff32f5436a43b38077d463110e3e61b Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 10:41:25 -0400
Subject: [PATCH] [libc++][NFC] Move the lower_bound/upper_bound benchmark to
 the sorting directory

These algorithms are not under [nonmodifying], they are under [sorting]
in the Standard.
---
 .../{nonmodifying => sorting}/lower_upper_bound.bench.cpp         | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename libcxx/test/benchmarks/algorithms/{nonmodifying => sorting}/lower_upper_bound.bench.cpp (100%)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
similarity index 100%
rename from libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp
rename to libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp


From libcxx-commits at lists.llvm.org  Thu Jul  2 09:04:08 2026
From: libcxx-commits at lists.llvm.org (Benjamin Luke via libcxx-commits)
Date: Thu, 02 Jul 2026 09:04:08 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libc] [libcxx] [clang][X86] Emit AVX
 level mismatch psABI warnings on function definitions (PR #199091)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/199091@github.com>
Message-ID: <6a468bf8.13be6df8.3d8759.5755@mx.google.com>


https://github.com/freaknbigpanda updated https://github.com/llvm/llvm-project/pull/199091

>From 7dbd9c7b715e951ce7bf26970a2b7eac011c56b7 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Wed, 20 May 2026 11:53:38 -0700
Subject: [PATCH 01/12] [clang][X86] Emit AVX level mismatch psABI warnings on
 function definitions

Emit -WpsABI for x86_64 function definitions whose return type or
parameter type uses a vector wider than 128 bits without the required ABI
feature enabled. 256-bit vectors require avx, and 512-bit vectors require
avx512f.

Previously this diagnostic was only emitted at call sites, so definitions
with wide vector signatures could be introduced without a warning until
they were called. Use the function feature map so
attribute(target("avx/avx512f")) definitions are accepted, and emit no
warnings for prototype-only declarations.
---
 clang/lib/CodeGen/Targets/X86.cpp             | 51 +++++++++++++++++++
 clang/test/CodeGen/X86/mmx-inline-asm-error.c |  2 +
 clang/test/CodeGen/target-builtin-error-3.c   |  6 +++
 clang/test/CodeGen/target-features-error-2.c  |  1 +
 clang/test/CodeGenCXX/target-avx-abi-diag.cpp | 37 ++++++++++++++
 libc/src/mathvec/generic/CMakeLists.txt       |  2 +
 libcxx/utils/libcxx/test/params.py            |  3 ++
 7 files changed, 102 insertions(+)
 create mode 100644 clang/test/CodeGenCXX/target-avx-abi-diag.cpp

diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 4e99ebab6ba34..37c643f14e39f 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -9,6 +9,7 @@
 #include "ABIInfoImpl.h"
 #include "TargetInfo.h"
 #include "clang/Basic/DiagnosticFrontend.h"
+#include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/SmallBitVector.h"
 
 using namespace clang;
@@ -1509,6 +1510,9 @@ class X86_64TargetCodeGenInfo : public TargetCodeGenInfo {
                             const FunctionDecl *Caller,
                             const FunctionDecl *Callee, const CallArgList &Args,
                             QualType ReturnType) const override;
+
+  void checkFunctionABI(CodeGenModule &CGM,
+                        const FunctionDecl *FD) const override;
 };
 } // namespace
 
@@ -1571,6 +1575,53 @@ static bool checkAVXParam(DiagnosticsEngine &Diag, ASTContext &Ctx,
   return false;
 }
 
+void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
+                                               const FunctionDecl *FD) const {
+  auto GetReturnTypeLoc = [](const FunctionDecl *FD) {
+    if (const TypeSourceInfo *TSI = FD->getTypeSourceInfo()) {
+      TypeLoc TL = TSI->getTypeLoc();
+
+      if (auto FTL = TL.IgnoreParens().getAs<FunctionTypeLoc>())
+        return FTL.getReturnLoc().getBeginLoc();
+    }
+
+    return FD->getLocation();
+  };
+
+  auto Check = [&](QualType Ty, SourceLocation Loc, bool IsReturn) {
+    if (!Ty->isVectorType())
+      return false;
+    if (CGM.getContext().getTypeSize(Ty) <= 128)
+      return false;
+
+    StringRef Feature =
+        CGM.getContext().getTypeSize(Ty) > 256 ? "avx512f" : "avx";
+
+    llvm::StringMap<bool> FeatureMap;
+    CGM.getContext().getFunctionFeatureMap(FeatureMap, FD);
+    if (!FeatureMap.lookup(Feature)) {
+      CGM.getDiags().Report(Loc, diag::warn_avx_calling_convention)
+          << !IsReturn << Ty << Feature;
+      return true;
+    }
+
+    return false;
+  };
+
+  // First check the return type and emit diagnostic if required
+  Check(FD->getReturnType(), GetReturnTypeLoc(FD), true);
+
+  // Go through the parameters and emit a warning for the first vector found
+  // without the matching function AVX level attribute
+  for (const ParmVarDecl *P : FD->parameters()) {
+    SourceLocation Loc = P->getLocation();
+    if (Loc.isInvalid())
+      Loc = P->getBeginLoc();
+    if (Check(P->getType(), Loc, false))
+      return;
+  }
+}
+
 void X86_64TargetCodeGenInfo::checkFunctionCallABI(CodeGenModule &CGM,
                                                    SourceLocation CallLoc,
                                                    const FunctionDecl *Caller,
diff --git a/clang/test/CodeGen/X86/mmx-inline-asm-error.c b/clang/test/CodeGen/X86/mmx-inline-asm-error.c
index 8a2f991a537a2..7f7f53a553057 100644
--- a/clang/test/CodeGen/X86/mmx-inline-asm-error.c
+++ b/clang/test/CodeGen/X86/mmx-inline-asm-error.c
@@ -2,6 +2,8 @@
 // RUN: %clang_cc1 -verify=omp -triple x86_64-unknown-unknown -emit-llvm-only -fopenmp %s
 typedef int vec256 __attribute__((ext_vector_type(8)));
 
+// omp-warning at +2 {{AVX vector return of type 'vec256' (vector of 8 'int' values) without 'avx' enabled changes the ABI}}
+// omp-warning at +1 {{AVX vector argument of type 'vec256' (vector of 8 'int' values) without 'avx' enabled changes the ABI}}
 vec256 foo(vec256 in) {
   vec256 out;
 
diff --git a/clang/test/CodeGen/target-builtin-error-3.c b/clang/test/CodeGen/target-builtin-error-3.c
index 056dc940f7a93..840ca7534905d 100644
--- a/clang/test/CodeGen/target-builtin-error-3.c
+++ b/clang/test/CodeGen/target-builtin-error-3.c
@@ -17,32 +17,38 @@ typedef __attribute__ ((ext_vector_type(16),__aligned__( 64))) float float16;
 static inline half8 __attribute__((__overloadable__)) convert_half( float8 a ) {
   return __extension__ ({ __m256 __a = (a); (__m128i)__builtin_ia32_vcvtps2ph256((__v8sf)__a, (0x00)); }); // expected-error {{'__builtin_ia32_vcvtps2ph256' needs target feature f16c}}
 }
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 static inline half16 __attribute__((__overloadable__)) convert_half( float16 a ) {
   half16 r;
   r.lo = convert_half(a.lo);
   return r;
 }
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 void avx_test( uint16_t *destData, float16 argbF)
 {
   ((half16U *)destData)[0] = convert_half(argbF);
 }
 
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 half16 test( float16 a ) {
   half16 r;
   r.lo = convert_half(a.lo);
   return r;
 }
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 void avx_test2( uint16_t *destData, float16 argbF)
 {
   // expected-warning at +1{{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
   ((half16U *)destData)[0] = test(argbF);
 }
 
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 __attribute__((always_inline)) half16 test2( float16 a ) {
   half16 r;
   r.lo = convert_half(a.lo);
   return r;
 }
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 void avx_test3( uint16_t *destData, float16 argbF)
 {
   ((half16U *)destData)[0] = test2(argbF);
diff --git a/clang/test/CodeGen/target-features-error-2.c b/clang/test/CodeGen/target-features-error-2.c
index 1599b0135b747..8f2ef35b8a931 100644
--- a/clang/test/CodeGen/target-features-error-2.c
+++ b/clang/test/CodeGen/target-features-error-2.c
@@ -8,6 +8,7 @@
 #include <x86intrin.h>
 
 #if NEED_AVX_1
+// expected-warning at +1 {{AVX vector argument of type '__m256i' (vector of 4 'long long' values) without 'avx' enabled changes the ABI}}
 int baz(__m256i a) {
   return _mm256_extract_epi32(a, 3); // expected-error {{'__builtin_ia32_vec_ext_v8si' needs target feature avx}}
 }
diff --git a/clang/test/CodeGenCXX/target-avx-abi-diag.cpp b/clang/test/CodeGenCXX/target-avx-abi-diag.cpp
new file mode 100644
index 0000000000000..5048cb092e67f
--- /dev/null
+++ b/clang/test/CodeGenCXX/target-avx-abi-diag.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -emit-llvm -disable-llvm-passes -verify=noavx,noavx512 -o - %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -target-feature +avx -emit-llvm -disable-llvm-passes -verify=noavx512 -o - %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -target-feature +avx512f -emit-llvm -disable-llvm-passes -verify=both -o - %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -Wno-psabi -emit-llvm -disable-llvm-passes -verify=suppressed -o - %s
+// REQUIRES: x86-registered-target
+
+// both-no-diagnostics
+// suppressed-no-diagnostics
+
+typedef float v8f __attribute__((vector_size(32)));
+typedef float v16f __attribute__((vector_size(64)));
+
+// Prototype-only declarations do not warn.
+v8f proto256(v8f);
+v16f proto512(v16f);
+
+// noavx-warning at +2 {{AVX vector return of type 'v8f' (vector of 8 'float' values) without 'avx' enabled changes the ABI}}
+// noavx-warning at +1 {{AVX vector argument of type 'v8f' (vector of 8 'float' values) without 'avx' enabled changes the ABI}}
+v8f def256(v8f x) {
+  return x;
+}
+
+// noavx512-warning at +2 {{AVX vector return of type 'v16f' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
+// noavx512-warning at +1 {{AVX vector argument of type 'v16f' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
+v16f def512(v16f x) {
+  return x;
+}
+
+__attribute__((target("avx")))
+v8f def256_avx(v8f x) {
+  return x;
+}
+
+__attribute__((target("avx512f")))
+v16f def512_avx512(v16f x) {
+  return x;
+}
diff --git a/libc/src/mathvec/generic/CMakeLists.txt b/libc/src/mathvec/generic/CMakeLists.txt
index d5d94d5ed56b5..0592ec1258cbf 100644
--- a/libc/src/mathvec/generic/CMakeLists.txt
+++ b/libc/src/mathvec/generic/CMakeLists.txt
@@ -7,6 +7,8 @@ add_entrypoint_object(
   DEPENDS
     libc.src.__support.macros.properties.cpu_features
     libc.src.__support.mathvec.expf
+  COMPILE_OPTIONS
+    -Wno-psabi
   FLAGS
     ROUND_OPT
     FMA_OPT
diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py
index d0bb38ab84df4..2c78a32b1449b 100644
--- a/libcxx/utils/libcxx/test/params.py
+++ b/libcxx/utils/libcxx/test/params.py
@@ -76,6 +76,9 @@
     # We're not annotating all the APIs, since that's a lot of annotations compared to how many we actually care about
     "-Wno-nullability-completeness",
 
+    # Adding this for now to get the build to pass but needs more detailed review
+    "-Wno-psabi",
+
     # Technically not a warning flag, but might as well be:
     "-flax-vector-conversions=none",
 ]

>From 2a2b4ef56384b4956e15a20ba5f9a77765e582f1 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Thu, 21 May 2026 11:57:54 -0700
Subject: [PATCH 02/12] darker format

---
 libcxx/utils/libcxx/test/params.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py
index 2c78a32b1449b..c9e6f767679fa 100644
--- a/libcxx/utils/libcxx/test/params.py
+++ b/libcxx/utils/libcxx/test/params.py
@@ -75,7 +75,6 @@
 
     # We're not annotating all the APIs, since that's a lot of annotations compared to how many we actually care about
     "-Wno-nullability-completeness",
-
     # Adding this for now to get the build to pass but needs more detailed review
     "-Wno-psabi",
 

>From 1ec9239563b3122fde47579dbc4d9e3fc6b148e6 Mon Sep 17 00:00:00 2001
From: freaknbigpanda <benjamin.luke at sony.com>
Date: Wed, 27 May 2026 17:10:57 -0700
Subject: [PATCH 03/12] Restricted warnings to externally visible definitions,
 for internal definitions ABI mismatch will be diagnosed at the call site.
 Added test to make sure we don't emit anything for internal definitions.

Improved the warning by returning valid source locations for functions that
don't have a valid FunctionTypeLoc, previously we were returning warnings
with no location. Added test to cover this case for lambda's with deduced
return type.
---
 clang/lib/CodeGen/Targets/X86.cpp             | 18 +++++++++++++++---
 clang/test/CodeGenCXX/target-avx-abi-diag.cpp | 18 ++++++++++++++++++
 libc/src/mathvec/generic/CMakeLists.txt       |  2 --
 libcxx/include/__algorithm/simd_utils.h       |  6 ++++--
 libcxx/utils/libcxx/test/params.py            |  2 --
 5 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 37c643f14e39f..15bfe6fc1b681 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1581,11 +1581,18 @@ void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
     if (const TypeSourceInfo *TSI = FD->getTypeSourceInfo()) {
       TypeLoc TL = TSI->getTypeLoc();
 
-      if (auto FTL = TL.IgnoreParens().getAs<FunctionTypeLoc>())
-        return FTL.getReturnLoc().getBeginLoc();
+      if (auto FTL = TL.IgnoreParens().getAs<FunctionTypeLoc>()) {
+        SourceLocation Loc = FTL.getReturnLoc().getBeginLoc();
+        if (Loc.isValid())
+          return Loc;
+      }
     }
 
-    return FD->getLocation();
+    SourceLocation Loc = FD->getLocation();
+    if (Loc.isValid())
+      return Loc;
+
+    return FD->getBeginLoc();
   };
 
   auto Check = [&](QualType Ty, SourceLocation Loc, bool IsReturn) {
@@ -1608,6 +1615,11 @@ void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
     return false;
   };
 
+  // psABI warnings & errors for function definitions that are only visible 
+  // in this translation unit are handled at call site by checkFunctionCallABI
+  if (!FD->isExternallyVisible())
+    return;
+
   // First check the return type and emit diagnostic if required
   Check(FD->getReturnType(), GetReturnTypeLoc(FD), true);
 
diff --git a/clang/test/CodeGenCXX/target-avx-abi-diag.cpp b/clang/test/CodeGenCXX/target-avx-abi-diag.cpp
index 5048cb092e67f..66312d513058e 100644
--- a/clang/test/CodeGenCXX/target-avx-abi-diag.cpp
+++ b/clang/test/CodeGenCXX/target-avx-abi-diag.cpp
@@ -26,6 +26,11 @@ v16f def512(v16f x) {
   return x;
 }
 
+// Internal definitions do not warn
+static v8f internal_def256(v8f x) {
+  return x;
+}
+
 __attribute__((target("avx")))
 v8f def256_avx(v8f x) {
   return x;
@@ -35,3 +40,16 @@ __attribute__((target("avx512f")))
 v16f def512_avx512(v16f x) {
   return x;
 }
+
+// Check to make sure deduced return lambdas still have valid source location which allows these pragmas to work
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpsabi"
+template <class T>
+T template_lambda_return() {
+  return []() { return T{}; }();
+}
+
+v8f use_template_lambda_return() {
+  return template_lambda_return<v8f>();
+}
+#pragma clang diagnostic pop
diff --git a/libc/src/mathvec/generic/CMakeLists.txt b/libc/src/mathvec/generic/CMakeLists.txt
index 0592ec1258cbf..d5d94d5ed56b5 100644
--- a/libc/src/mathvec/generic/CMakeLists.txt
+++ b/libc/src/mathvec/generic/CMakeLists.txt
@@ -7,8 +7,6 @@ add_entrypoint_object(
   DEPENDS
     libc.src.__support.macros.properties.cpu_features
     libc.src.__support.mathvec.expf
-  COMPILE_OPTIONS
-    -Wno-psabi
   FLAGS
     ROUND_OPT
     FMA_OPT
diff --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index c47f79ac7f1dd..5b87f13e5bb01 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -90,6 +90,9 @@ inline constexpr size_t __native_vector_size = 1;
 template <class _ArithmeticT, size_t _Np>
 using __simd_vector __attribute__((__ext_vector_type__(_Np))) _LIBCPP_NODEBUG = _ArithmeticT;
 
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
+
 template <class _VecT>
 inline constexpr size_t __simd_vector_size_v = []<bool _False = false>() -> size_t {
   static_assert(_False, "Not a vector!");
@@ -115,8 +118,6 @@ template <class _VecT, class _Iter>
 }
 
 // Load the first _Np elements, zero the rest
-_LIBCPP_DIAGNOSTIC_PUSH
-_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
 template <class _VecT, size_t _Np, class _Iter>
 [[__nodiscard__]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _VecT __partial_load(_Iter __iter) noexcept {
   return [=]<size_t... _LoadIndices, size_t... _ZeroIndices>(
@@ -174,6 +175,7 @@ template <class _Tp, size_t _Np>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t __find_first_not_set(__simd_vector<_Tp, _Np> __vec) noexcept {
   return std::__find_first_set(~__vec);
 }
+_LIBCPP_DIAGNOSTIC_POP
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py
index c9e6f767679fa..d0bb38ab84df4 100644
--- a/libcxx/utils/libcxx/test/params.py
+++ b/libcxx/utils/libcxx/test/params.py
@@ -75,8 +75,6 @@
 
     # We're not annotating all the APIs, since that's a lot of annotations compared to how many we actually care about
     "-Wno-nullability-completeness",
-    # Adding this for now to get the build to pass but needs more detailed review
-    "-Wno-psabi",
 
     # Technically not a warning flag, but might as well be:
     "-flax-vector-conversions=none",

>From ec93252b059a1ea75cf463cf8c15f8c1a0f631c7 Mon Sep 17 00:00:00 2001
From: freaknbigpanda <benjamin.luke at sony.com>
Date: Wed, 27 May 2026 18:12:38 -0700
Subject: [PATCH 04/12] fix test

---
 clang/test/CodeGen/target-builtin-error-3.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/CodeGen/target-builtin-error-3.c b/clang/test/CodeGen/target-builtin-error-3.c
index 840ca7534905d..75754f40ac44d 100644
--- a/clang/test/CodeGen/target-builtin-error-3.c
+++ b/clang/test/CodeGen/target-builtin-error-3.c
@@ -17,7 +17,7 @@ typedef __attribute__ ((ext_vector_type(16),__aligned__( 64))) float float16;
 static inline half8 __attribute__((__overloadable__)) convert_half( float8 a ) {
   return __extension__ ({ __m256 __a = (a); (__m128i)__builtin_ia32_vcvtps2ph256((__v8sf)__a, (0x00)); }); // expected-error {{'__builtin_ia32_vcvtps2ph256' needs target feature f16c}}
 }
-// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
+// Internal definitions do not warn.
 static inline half16 __attribute__((__overloadable__)) convert_half( float16 a ) {
   half16 r;
   r.lo = convert_half(a.lo);

>From f9cf10e72032bebf45cf7065c07806c320d24f8e Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Wed, 27 May 2026 18:15:16 -0700
Subject: [PATCH 05/12] clang format

---
 clang/lib/CodeGen/Targets/X86.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 15bfe6fc1b681..ce98a7d44540e 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1615,7 +1615,7 @@ void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
     return false;
   };
 
-  // psABI warnings & errors for function definitions that are only visible 
+  // psABI warnings & errors for function definitions that are only visible
   // in this translation unit are handled at call site by checkFunctionCallABI
   if (!FD->isExternallyVisible())
     return;

>From ed6abf0be979c9b583f2dd3f3cf01d45b2cc1c50 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Thu, 25 Jun 2026 08:25:18 -0700
Subject: [PATCH 06/12] Update clang/lib/CodeGen/Targets/X86.cpp

Co-authored-by: Aaron Ballman <aaron at aaronballman.com>
---
 clang/lib/CodeGen/Targets/X86.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index ce98a7d44540e..b2be5770ead6d 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1616,7 +1616,7 @@ void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
   };
 
   // psABI warnings & errors for function definitions that are only visible
-  // in this translation unit are handled at call site by checkFunctionCallABI
+  // in this translation unit are handled at call site by checkFunctionCallABI.
   if (!FD->isExternallyVisible())
     return;
 

>From cae36f9be370fbeda2f031dd5c78e9b69bd00046 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Thu, 25 Jun 2026 08:25:45 -0700
Subject: [PATCH 07/12] Update clang/lib/CodeGen/Targets/X86.cpp

Co-authored-by: Aaron Ballman <aaron at aaronballman.com>
---
 clang/lib/CodeGen/Targets/X86.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index b2be5770ead6d..f5472319c9925 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1620,7 +1620,7 @@ void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
   if (!FD->isExternallyVisible())
     return;
 
-  // First check the return type and emit diagnostic if required
+  // First check the return type and emit diagnostic if required.
   Check(FD->getReturnType(), GetReturnTypeLoc(FD), true);
 
   // Go through the parameters and emit a warning for the first vector found

>From 0b64064566de45af163ab9a92b4ea5a87bf3e29a Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Thu, 25 Jun 2026 08:25:55 -0700
Subject: [PATCH 08/12] Update clang/lib/CodeGen/Targets/X86.cpp

Co-authored-by: Aaron Ballman <aaron at aaronballman.com>
---
 clang/lib/CodeGen/Targets/X86.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index f5472319c9925..807d372c3c8fd 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1624,7 +1624,7 @@ void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
   Check(FD->getReturnType(), GetReturnTypeLoc(FD), true);
 
   // Go through the parameters and emit a warning for the first vector found
-  // without the matching function AVX level attribute
+  // without the matching function AVX level attribute.
   for (const ParmVarDecl *P : FD->parameters()) {
     SourceLocation Loc = P->getLocation();
     if (Loc.isInvalid())

>From 3520042d4ab158b20952ae95d0e8dffdd0746c0d Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Thu, 25 Jun 2026 08:37:57 -0700
Subject: [PATCH 09/12] Added release note

---
 clang/docs/ReleaseNotes.rst | 915 ++++++++++++++++++++++++++++++++++++
 1 file changed, 915 insertions(+)
 create mode 100644 clang/docs/ReleaseNotes.rst

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
new file mode 100644
index 0000000000000..80b7880bdc85a
--- /dev/null
+++ b/clang/docs/ReleaseNotes.rst
@@ -0,0 +1,915 @@
+.. If you want to modify sections/contents permanently, you should modify both
+   ReleaseNotes.rst and ReleaseNotesTemplate.txt.
+
+===========================================
+Clang |release| |ReleaseNotesTitle|
+===========================================
+
+.. contents::
+   :local:
+   :depth: 2
+
+Written by the `LLVM Team <https://llvm.org/>`_
+
+.. only:: PreRelease
+
+  .. warning::
+     These are in-progress notes for the upcoming Clang |version| release.
+     Release notes for previous releases can be found on
+     `the Releases Page <https://llvm.org/releases/>`_.
+
+Introduction
+============
+
+This document contains the release notes for the Clang C/C++/Objective-C
+frontend, part of the LLVM Compiler Infrastructure, release |release|. Here we
+describe the status of Clang in some detail, including major
+improvements from the previous release and new feature work. For the
+general LLVM release notes, see `the LLVM
+documentation <https://llvm.org/docs/ReleaseNotes.html>`_. For the libc++ release notes,
+see `this page <https://libcxx.llvm.org/ReleaseNotes.html>`_. All LLVM releases
+may be downloaded from the `LLVM releases web site <https://llvm.org/releases/>`_.
+
+For more information about Clang or LLVM, including information about the
+latest release, please see the `Clang Web Site <https://clang.llvm.org>`_ or the
+`LLVM Web Site <https://llvm.org>`_.
+
+Potentially Breaking Changes
+============================
+
+C/C++ Language Potentially Breaking Changes
+-------------------------------------------
+
+- Clang now makes it ill-formed to try to ``break`` out of or ``continue`` a loop inside its own condition,
+  increment, or init-statement in all C and C++ language modes. This means that code such as
+
+  .. code-block:: c++
+
+    while (({ break; })) {}
+
+  is now ill-formed. An outer loop can still be broken out of or continued so long as the inner loop is
+  in the body of the outer loop:
+
+  .. code-block:: c++
+
+    // Ok, this breaks out of the 'for' loop.
+    for (;;) {
+      while (({ break; true; })) {}
+    }
+
+    // Error: can't break out of the 'for' loop from within its own increment.
+    for (;;({ while (({ break; true; })) {} })) {}
+
+  This also resolves a divergence from GCC: in a construct such as
+
+  .. code-block:: c++
+
+    for (;;) {
+      while (({ break; true; })) {}
+    }
+
+  Clang would previously ``break`` out of the ``while`` loop, whereas GCC (since version 9) would
+  ``break`` out of the ``for`` loop here. Now, Clang and GCC both break out of the ``for`` loop.
+
+C++ Specific Potentially Breaking Changes
+-----------------------------------------
+
+- Clang now more aggressively optimizes away stores to objects after they are
+  dead. This behavior can be disabled with ``-fno-lifetime-dse``.
+
+- Clang now correctly rejects ``export`` declarations in module implementation
+  partitions. (#GH107602)
+
+- Template argument deduction now treats the ``N`` in ``_BitInt(N)``
+  as being of type ``std::size_t`` instead of ``int``,
+  matching the deduction of array sizes from ``int(&)[N]``.
+  This is a breaking change for code that depended on the previously deduced type. (#GH195033)
+
+- Clang now rejects nested local classes defined in a different
+  block scope than their parent class. (#GH193472)
+
+ABI Changes in This Version
+---------------------------
+
+- Fixed incorrect struct layout for ``_BitInt`` bitfields wider than 255 bits
+  on MSVC targets. Internal bitfield tracking fields were changed from
+  ``unsigned char`` to ``uint64_t`` to prevent overflow. This might be an ABI
+  break for such structs compared to earlier Clang versions.
+- Fixed a number of issues with the ``__regcall`` calling convention for passing
+  structs on non-Windows x86-64 targets, including a crash when handling empty
+  struct arguments. This changes how structs that contain arrays, floating point
+  types, or ``_Complex float`` types are passed, and may introduce
+  incompatibilities with code compiled by earlier versions of Clang that uses
+  the ``__regcall`` calling convention on these targets. (#GH62999) (#GH98635)
+- Fixed Itanium mangling for lambdas in instantiated non-static data member
+  initializers by preserving the field-name closure-prefix. This changes the
+  mangled names for affected lambdas. (#GH190555)
+
+AST Dumping Potentially Breaking Changes
+----------------------------------------
+
+- The JSON AST dump now includes all fields from ``AvailabilityAttr``: ``platform``,
+  ``introduced``, ``deprecated``, ``obsoleted``, ``unavailable``, ``message``,
+  ``strict``, ``replacement``, ``priority``, and ``environment``. Previously, these
+  fields were missing from the JSON output.
+- Colons that appear at the end of a ParamCommentCommand name are not serialized
+  as part of the name.
+
+Clang Frontend Potentially Breaking Changes
+-------------------------------------------
+
+- HIPSPV toolchain: `--offload-targets=spirv{32,64}` option is
+  deprecated and will be removed when the new offload driver becomes
+  default. The replacement for the option is
+  `--offload-targets=spirv{32,64}-unknown-chipstar` when using the new
+  offload driver (`--offload-new-driver`).
+- The new driver (`--offload-new-driver`) is now default for all offloading
+  compilations. This changes the ABI for relocatable device code. Currently,
+  libraries will need to be recompiled, or used with
+  (`--no-offload-new-driver`). This option will be removed in the next release.
+
+- Clang no longer defines the ``__cpp_impl_coroutine`` feature test macro under the 32-bit x86 Microsoft ABI,
+  as support for coroutines on this target is incomplete.
+  When using coroutines on this target a warning is emmitted to indicate the lack of full support.
+  That warning can be disabled with ``-Wno-coroutines-unsupported-target``. (see #GH59382)
+
+Clang Python Bindings Potentially Breaking Changes
+--------------------------------------------------
+- Remove ``CompletionString.Availability``. No libclang interfaces returned instances of it.
+- ``CompletionString.availability`` now returns instances of ``CompletionString.AvailabilityKindCompat``.
+
+  Instances of ``AvailabilityKindCompat`` have the same ``__str__`` representation
+  as the previous ``CompletionChunk.Kind`` and are equality-comparable with
+  the existing ``AvailabilityKind`` enum. It will be replaced by ``AvailabilityKind``
+  in a future release. When this happens, the return type of ``CompletionString.availability``
+  will change to ``AvailabilityKind``, so it is recommended to use ``AvailabilityKind``
+  to compare with the return values of ``CompletionString.availability``.
+- Remove ``availabilityKinds``. In this release, uses of ``availabilityKinds``
+  need to be replaced by ``CompletionString.AvailabilityKind``.
+- ``CompletionChunk.kind`` now returns instances of ``CompletionChunkKind``.
+
+  Instances of ``CompletionChunkKind`` have the same ``__str__`` representation
+  as the previous ``CompletionChunk.Kind`` for compatibility.
+  These representations will be changed in a future release to match other enums.
+- Remove ``completionChunkKindMap``. In this release, uses of ``completionChunkKindMap``
+  need to be replaced by ``CompletionChunkKind``.
+- Move ``SPELLING_CACHE`` into ``CompletionChunk`` and change it to use
+  ``CompletionChunkKind`` instances as keys, instead of the enum values.
+  An alias is kept in the form of a ``SPELLING_CACHE`` variable, but it only supports
+  ``__getitem__`` and ``__contains__``. It will be removed in a future release.
+  Please migrate to using ``CompletionChunk.SPELLING_CACHE`` instead.
+- ``SourceLocation`` and ``SourceRange`` now use ``NotImplemented`` to delegate
+  equality checks (``__eq__``) to the other object they are compared with when
+  they are of different classes. They previously returned ``False`` when compared
+  with objects of other classes.
+- ``TranslationUnit.get_tokens`` now throws an error if both the ``extent`` and
+  ``locations`` argument are passed. Previousy, ``locations`` took precedence.
+- ``_CXUnsavedFile`` will be renamed to ``UnsavedFile`` for consistency.
+  ``UnsavedFile`` is already available to use and existing uses should
+  be adapted to refer to it instead. ``_CXUnsavedFile`` will be removed in a
+  future release.
+
+OpenCL Potentially Breaking Changes
+-----------------------------------
+- Clang now diagnoses zero-length arrays as errors in OpenCL. OpenCL C 3.0
+  section 6.11.d states that "Variable length arrays and structures with
+  flexible (or unsized) arrays are not supported."
+
+What's New in Clang |release|?
+==============================
+
+C++ Language Changes
+--------------------
+
+- ``__is_trivially_equality_comparable`` no longer returns false for all enum types. (#GH132672)
+
+C++2c Feature Support
+^^^^^^^^^^^^^^^^^^^^^
+
+C++23 Feature Support
+^^^^^^^^^^^^^^^^^^^^^
+
+- Partially implement Itanium mangling for pack indexing. Partially substituted packs are not yet supported. (#GH112003)
+
+C++20 Feature Support
+^^^^^^^^^^^^^^^^^^^^^
+
+- Clang now supports `P1857R3 <https://wg21.link/p1857r3>`_ Modules Dependency Discovery. (#GH54047)
+
+C++17 Feature Support
+^^^^^^^^^^^^^^^^^^^^^
+
+Resolutions to C++ Defect Reports
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Implemented `CWG1780 Explicit instantiation/specialization of generic lambda
+  operator() <https://cplusplus.github.io/CWG/issues/1780.html>`_
+
+- Clang now allows omitting ``typename`` before a template name in a
+  conversion operator, implementing `CWG2413 <https://wg21.link/cwg2413>`_.
+
+- Clang now uses non-reference types for structured bindings whose initializer
+  returns a prvalue. This resolves `CWG3135 <https://wg21.link/cwg3135>`_.
+
+C Language Changes
+------------------
+
+C2y Feature Support
+^^^^^^^^^^^^^^^^^^^
+
+- Implemented the type-specific C2y ``<stdbit.h>`` rotate functions with constexpr
+  evaluation support:
+  ``stdc_rotate_left_{uc,us,ui,ul,ull}`` and
+  ``stdc_rotate_right_{uc,us,ui,ul,ull}``.
+
+C23 Feature Support
+^^^^^^^^^^^^^^^^^^^
+- Clang now allows C23 ``constexpr`` struct member access through the dot operator in constant expressions. (#GH178349)
+
+Objective-C Language Changes
+-----------------------------
+
+- Clang now emits Objective-C number, array, and dictionary literals as
+  compile-time constant data structures rather than runtime ``objc_msgSend``
+  calls on targets whose runtime supports constant literal classes. The
+  feature can be disabled altogether with ``-fno-objc-constant-literals``,
+  or selectively per literal kind with ``-fno-constant-nsnumber-literals``,
+  ``-fno-constant-nsarray-literals``, and
+  ``-fno-constant-nsdictionary-literals``.
+
+Non-comprehensive list of changes in this release
+-------------------------------------------------
+
+- Added support for floating point and pointer values in most ``__atomic_``
+  builtins.
+
+- Added ``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right``
+  for bit rotation of unsigned integers including ``_BitInt`` types. Rotation
+  counts are normalized modulo the bit-width and support negative values.
+  Usable in constant expressions. Implicit conversion is supported for
+  class/struct types with conversion operators.
+- Implemented the following C23 ``<stdbit.h>`` builtins with ``_BitInt``
+  support and constexpr evaluation:
+  ``__builtin_stdc_leading_zeros``, ``__builtin_stdc_leading_ones``,
+  ``__builtin_stdc_trailing_zeros``, ``__builtin_stdc_trailing_ones``,
+  ``__builtin_stdc_first_leading_zero``, ``__builtin_stdc_first_leading_one``,
+  ``__builtin_stdc_first_trailing_zero``, ``__builtin_stdc_first_trailing_one``,
+  ``__builtin_stdc_count_zeros``, ``__builtin_stdc_count_ones``,
+  ``__builtin_stdc_has_single_bit``, ``__builtin_stdc_bit_width``,
+  ``__builtin_stdc_bit_floor``, and ``__builtin_stdc_bit_ceil``.
+
+- Implemented the type-specific C23 ``<stdbit.h>`` functions with constexpr
+  evaluation support:
+  ``stdc_leading_zeros_{uc,us,ui,ul,ull}``,
+  ``stdc_leading_ones_{uc,us,ui,ul,ull}``,
+  ``stdc_trailing_zeros_{uc,us,ui,ul,ull}``,
+  ``stdc_trailing_ones_{uc,us,ui,ul,ull}``,
+  ``stdc_first_leading_zero_{uc,us,ui,ul,ull}``,
+  ``stdc_first_leading_one_{uc,us,ui,ul,ull}``,
+  ``stdc_first_trailing_zero_{uc,us,ui,ul,ull}``,
+  ``stdc_first_trailing_one_{uc,us,ui,ul,ull}``,
+  ``stdc_count_zeros_{uc,us,ui,ul,ull}``,
+  ``stdc_count_ones_{uc,us,ui,ul,ull}``,
+  ``stdc_has_single_bit_{uc,us,ui,ul,ull}``,
+  ``stdc_bit_width_{uc,us,ui,ul,ull}``,
+  ``stdc_bit_floor_{uc,us,ui,ul,ull}``, and
+  ``stdc_bit_ceil_{uc,us,ui,ul,ull}``.
+
+- A new generic bit-reverse builtin function ``__builtin_bitreverseg`` that
+  extends bit-reversal support to all standard integers type, including
+  ``_BitInt``
+
+- Deprecated float types support from ``__builtin_elementwise_max`` and
+  ``__builtin_elementwise_min``.
+
+- Added header ``endian.h`` which contains byte order helpers specified in POSIX
+
+- Added #pragma loop licm(disable) for llvm.loop.licm.disable metadata
+
+- Added a new ``ExplicitInstantiationDecl`` AST node to represent explicit
+  template instantiations (e.g., ``template void foo<int>();`` or
+  ``extern template class S<int>;``). Previously, source location information
+  for explicit instantiation statements was discarded after parsing. The new
+  node preserves the full source range including the ``extern`` and ``template``
+  keywords, qualifiers, template arguments as written, and the declared type,
+  enabling tools such as language servers and refactoring engines to accurately
+  map source locations back to explicit instantiation sites.
+
+- ``typeid`` on references and pointers of ``final`` types no longer emits a
+  vtable lookup at runtime.
+
+- Updated support for Unicode from 15.1 to 18.0.
+
+New Compiler Flags
+------------------
+- New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added
+  to enable or disable Microsoft's anonymous struct/union extension without
+  enabling other ``-fms-extensions`` features (#GH177607).
+- New option ``--precompile-reduced-bmi`` allows build system to generate a
+  reduced BMI only for a C++20 importable module unit. Previously the users
+  can only generate the reduced BMI as a by-product, e.g, an object files or
+  a full BMI.
+
+- New ``-cc1`` option ``-fexperimental-overflow-behavior-types`` added to
+  enable parsing of the experimental ``overflow_behavior`` type attribute and
+  type specifiers.
+
+- New ``-cl`` option ``/d2guardnochecks`` added to match MSVC. When Windows
+  Control Flow Guard (CFG) is enabled by other options, it will instruct Clang
+  to emit the CFG metadata, but disable adding checks.
+
+- New option ``-fdiagnostics-show-inlining-chain`` added to show inlining chain
+  notes for ``[[gnu::warning]]`` and ``[[gnu::error]]`` diagnostics. When a
+  function with these attributes is called from an inlined context, Clang can
+  now show which functions were inlined to reach the call. When debug info is
+  available (``-gline-directives-only`` (implicitly enabled at ``-g1``) or
+  higher), accurate source locations are used; otherwise, a heuristic fallback
+  is used with a note suggesting how to enable debug info for better accuracy.
+
+- New option ``-fwin-cfg-mechanism=`` added to control the mechanism used by
+  Control Flow Guard on Windows. Accepted values are ``automatic`` (default),
+  ``dispatch``, and ``check``. The ``dispatch`` mechanism uses the dispatch
+  function to perform indirect call checks and can improve performance, while
+  ``check`` uses the traditional check mechanism.
+- New ``-cl`` option ``/d2guardcfgdispatch`` added to match MSVC. This acts as a
+  shorthand for ``-fwin-cfg-mechanism=dispatch``.
+- New ``-cl`` option ``/d2guardcfgdispatch-`` added to match MSVC. This acts as a
+  shorthand for ``-fwin-cfg-mechanism=check``.
+
+- New option ``-f[no-]strict-bool`` added to control whether Clang can assume
+  that ``bool`` values loaded from memory cannot have a bit pattern other
+  than 0 or 1.
+
+Deprecated Compiler Flags
+-------------------------
+
+Modified Compiler Flags
+-----------------------
+- The `-mno-outline` and `-moutline` compiler flags are now allowed on RISC-V and X86, which both support the machine outliner.
+- The `-mno-outline` flag will now add the `nooutline` IR attribute, so that
+  `-mno-outline` and `-moutline` objects can be mixed correctly during LTO.
+
+Removed Compiler Flags
+----------------------
+
+Attribute Changes in Clang
+--------------------------
+
+- Added new attribute ``stack_protector_ignore`` to opt specific local variables out of
+  the analysis which determines if a function should get a stack protector.  A function
+  will still generate a stack protector if other local variables or command line flags
+  require it.
+
+- Added a new attribute, ``[[clang::no_outline]]`` to suppress outlining from
+  annotated functions. This uses the LLVM `nooutline` attribute.
+
+- Introduced a new type attribute ``__attribute__((overflow_behavior))`` which
+  currently accepts either ``wrap`` or ``trap`` as an argument, enabling
+  type-level control over overflow behavior. There is also an accompanying type
+  specifier for each behavior kind via `__ob_wrap` and `__ob_trap`.
+
+- Introduced a new function attribute ``__attribute__((__personality__(...)))``
+  to explicitly specify the personality routine for exception handling. THis is
+  meant to be a low level tool for language runtime authors to associate a
+  foreign language personality with a given function. Note that this does not
+  perform any ABI validation for the personality routine.
+
+- :doc:`ThreadSafetyAnalysis` attributes now correctly handle implicit member
+  accesses in C, and parameter attributes in C++. This improves diagnostic
+  precision and fixes false positives.
+
+- The :doc:`ThreadSafetyAnalysis` attributes ``guarded_by`` and
+  ``pt_guarded_by`` now accept multiple capability arguments with refined
+  access semantics: *writing* requires all listed capabilities to be held
+  exclusively, while *reading* requires at least one to be held.  This is
+  sound because any writer must hold all capabilities, so holding any one
+  prevents concurrent writes.
+
+- :doc:`ThreadSafetyAnalysis` attributes like ``acquire_capability``,
+  ``release_capability``, ``requires_capability``, ``locks_excluded``,
+  ``try_acquire_capability``, and ``assert_capability`` can now be applied to
+  function pointer variables and fields.  The analysis checks calls through
+  annotated function pointers the same way it checks direct function calls.
+  Only plain function pointers are supported; pointers-to-member functions,
+  blocks, or wrappers (e.g. ``std::function``) are not yet supported.
+
+- The ``[[clang::unsafe_buffer_usage]]`` attribute is now supported in API
+  notes. For example:
+  
+  .. code-block:: yaml
+
+    Functions:
+      - Name: myUnsafeFunction
+        UnsafeBufferUsage: true
+
+- Added support for ``[[msvc::forceinline]]`` for functions and
+  ``[[msvc::forceinline_calls]]`` for statements. Both are aliases to
+  ``[[clang::always_inline]]`` with additional checks to ensure that they
+  are only accepted in places where MSVC also does.
+
+- The AMDGPU ``amdgpu_num_sgpr`` and ``amdgpu_num_vgpr`` attributes are now
+  deprecated. Clang emits a ``-Wdeprecated-declarations`` warning when they
+  are used. Use ``amdgpu_waves_per_eu`` instead to control SGPR and VGPR
+  usage.
+
+- Clang now allows GNU attributes between a member declarator and bit-field width. (#GH184954)
+
+Improvements to Clang's diagnostics
+-----------------------------------
+- Fixed bug in ``-Wdocumentation`` so that it correctly handles explicit
+  function template instantiations (#64087).
+
+- ``-Wunused-but-set-variable`` now diagnoses file-scope variables with
+  internal linkage (``static`` storage class) that are assigned but never used.
+  This new coverage is added under the subgroup ``-Wunused-but-set-global``,
+  allowing it to be disabled independently with ``-Wno-unused-but-set-global``.
+  (#GH148361)
+
+- Added ``-Wlifetime-safety`` to enable lifetime safety analysis,
+  a CFG-based intra-procedural analysis that detects use-after-free and related
+  temporal safety bugs. See the
+  `RFC <https://discourse.llvm.org/t/rfc-intra-procedural-lifetime-analysis-in-clang/86291>`_
+  for more details. By design, this warning is enabled in ``-Weverything``. To disable
+  the analysis, use ``-Wno-lifetime-safety`` or ``-fno-lifetime-safety``.
+
+- Added ``-Wlifetime-safety-suggestions`` to enable lifetime annotation suggestions.
+  This provides suggestions for function parameters that
+  should be marked ``[[clang::lifetimebound]]`` based on lifetime analysis. For
+  example, for the following function:
+
+  .. code-block:: c++
+
+    int* p(int *in) { return in; }
+
+  Clang will suggest:
+
+  .. code-block:: c++
+
+    warning: parameter in intra-TU function should be marked [[clang::lifetimebound]]
+    int* p(int *in) { return in; }
+           ^~~~~~~
+                   [[clang::lifetimebound]]
+    note: param returned here
+    int* p(int *in) { return in; }
+                             ^~
+
+- Added ``-Wlifetime-safety-noescape`` to detect misuse of ``[[clang::noescape]]``
+  annotation where the parameter escapes through return. For example:
+
+  .. code-block:: c++
+
+    int* p(int *in [[clang::noescape]]) { return in; }
+
+  Clang will warn:
+
+  .. code-block:: c++
+
+    warning: parameter is marked [[clang::noescape]] but escapes
+    int* p(int *in [[clang::noescape]]) { return in; }
+           ^~~~~~~
+    note: returned here
+    int* p(int *in [[clang::noescape]]) { return in; }
+                                                 ^~
+
+- Added ``-Wlifetime-safety-dangling-field`` to detect dangling field references
+  when stack memory escapes to class fields. This is part of ``-Wlifetime-safety``
+  and detects cases where local variables or parameters are stored in fields but
+  outlive their scope. For example:
+
+  .. code-block:: c++
+
+    struct DanglingView {
+      std::string_view view;
+      DanglingView(std::string s) : view(s) {}  // warning: address of stack memory escapes to a field
+    };
+
+- Improved ``-Wassign-enum`` performance by caching enum enumerator values. (#GH176454)
+
+- Clang now emits ``-Wpsabi`` diagnostics for externally visible x86-64
+  function definitions that return or take AVX or AVX-512 vector types without
+  enabling the corresponding target feature.
+
+- Fixed a false negative in ``-Warray-bounds`` where the warning was suppressed
+  when accessing a member function on a past-the-end array element.
+  (#GH179128)
+
+- Added a missing space to the FixIt for the ``implicit-int`` group of diagnostics and
+  made sure that only one such diagnostic and FixIt is emitted per declaration group. (#GH179354)
+
+- Fixed the Fix-It insertion point for ``expected ';' after alias declaration``
+  when parsing alias declarations involving a token-split ``>>`` sequence
+  (for example, ``using A = X<int>>;``). (#GH184425)
+
+- Fixed incorrect ``implicitly deleted`` diagnostic for explicitly deleted
+  candidate function. (#GH185693)
+
+- The ``-Wloop-analysis`` warning has been extended to catch more cases of
+  variable modification inside lambda expressions (#GH132038).
+
+- Clang now emits ``-Wsizeof-pointer-memaccess`` when snprintf/vsnprintf use the sizeof
+  the destination buffer(dynamically allocated) in the len parameter(#GH162366)
+
+- Added ``-Wmodule-map-path-outside-directory`` (off by default) to warn on
+  header and umbrella directory paths that use ``..`` to refer outside the module
+  directory in module maps found via implicit search
+  (``-fimplicit-module-maps``). This does not affect module maps specified
+  explicitly via ``-fmodule-map-file=``.
+
+- Honour ``[[maybe_unused]]`` attribute on private fields.
+  ``-Wunused-private-field`` no longer emits a warning for annotated private
+  fields.
+
+- Improved ``-Wgnu-zero-variadic-macro-arguments`` to suggest using
+  ``__VA_OPT__`` if the current language version supports it(#GH188624)
+
+- Clang now emits an error when implicitly casting a complex type to a built-in vector type. (#GH186805)
+
+- Added ``-Wnonportable-include-path-separator`` (off by default) to catch
+  #include directives that use backslashes as a path separator. The warning
+  includes a FixIt to change all the backslashes to forward slashes, so that the
+  code can automatically be made portable to other host platforms that don't
+  support backslashes.
+
+- Clang now explains why template deduction fails for explicit template arguments.
+
+- No longer emitting a ``-Wpre-c2y-compat`` or extension diagnostic about use
+  of octal literals with a ``0o`` prefix, and no longer emitting a
+  ``-Wdeprecated-octal-literals`` diagnostic for use of octal literals without
+  a ``0o`` prefix, when the literal is expanded from a macro defined in a
+  system header. (#GH192389)
+
+- Improved error recovery for missing semicolons after class members. Clang now avoids
+  skipping subsequent valid declarations when their previous decl is missing semicolon.
+
+- Removed the body of lambdas from some diagnostic messages.
+
+- Fixed false positive host-device mismatch errors in discarded `if constexpr` branches for CUDA/HIP;
+  such calls are now correctly skipped.
+
+- Clang now errors when a function declaration aliases a variable or vice versa. (#GH195550)
+
+- Added ``-Wattribute-alias`` to diagnose type mismatches between an alias and its aliased function. (#GH195550)
+
+- The diagnostics around ``__block`` now explain why a variable cannot be marked ``__block``. (#GH197213)
+
+- Extended ``-Wnonportable-include-path`` to warn about trailing whitespace and dots in ``#include`` paths. (#GH190610)
+
+- Clang now emits error when attribute is missing closing ``]]`` followed by ``;;``. (#GH187223)
+
+Improvements to Clang's time-trace
+----------------------------------
+
+Improvements to Coverage Mapping
+--------------------------------
+
+- [MC/DC] Nested expressions are handled as individual MC/DC expressions.
+- "Single byte coverage" now supports branch coverage and can be used
+  together with ``-fcoverage-mcdc``.
+- Consteval member functions are no longer emitted in coverage mappings,
+  matching the existing behavior for free consteval functions. (#GH164448)
+
+Bug Fixes in This Version
+-------------------------
+
+- Fixed atomic boolean compound assignment; the conversion back to atomic bool would be miscompiled. (#GH33210)
+- Correctly handle default template argument when establishing subsumption. (#GH188640)
+- Fixed a failed assertion in the preprocessor when ``__has_embed`` parameters are missing parentheses. (#GH175088)
+- Fix lifetime extension of temporaries in for-range-initializers in templates. (#GH165182)
+- Fixed a preprocessor crash in ``__has_cpp_attribute`` on incomplete scoped attributes. (#GH178098)
+- Fixes an assertion failure when evaluating ``__underlying_type`` on enum redeclarations. (#GH177943)
+- Fixed an assertion failure caused by nested macro expansion during header-name lexing (``__has_embed(__has_include)``). (#GH178635)
+- Clang now outputs relative paths of embeds for dependency output. (#GH161950)
+- Fix the result type of a binary operation where both operands are 'void' l-values. (#GH111300)
+- Fixed an assertion failure when evaluating ``_Countof`` on invalid ``void``-typed operands. (#GH180893)
+- Fixed an assertion failure in the serialized diagnostic printer when it is destroyed without calling ``finish()``. (#GH140433)
+- Fixed an assertion failure caused by error recovery while extending a nested name specifier with results from ordinary lookup. (#GH181470)
+- Fixed a crash when parsing ``#pragma clang attribute`` arguments for attributes that forbid arguments. (#GH182122)
+- Fixed a bug with multiple-include optimization (MIOpt) state not being preserved in some cases during lexing, which could suppress header-guard mismatch diagnostics and interfere with include-guard optimization. (#GH180155)
+- Fixed a crash when normalizing constraints involving concept template parameters whose index coincided with non-concept template parameters in the same parameter mapping.
+- Fixed a crash caused by accessing dependent diagnostics of a non-dependent context.
+- Fixed a crash when substituting into a non-type template parameter that has a type containing an undeduced placeholder type.
+- Fixed several crashes and improved diagnostics when a multidimensional subscript operator is applied to a built-in type. (#GH187800)
+- Correctly diagnosing and no longer crashing when ``export module foo``
+  (without a semicolon) are the final tokens in a module file. (#GH187771)
+- Fixed a crash in duplicate attribute checking caused by comparing constant arguments with different integer signedness. (#GH188259)
+- Clang now emits an error when returning an initializer list from a lambda
+  with an explicit return type of void. The diagnostic now correctly refers
+  to "lambda" instead of "block". (#GH188661)
+- Fixed a crash on _BitInt(N) arrays where 129 ≤ N ≤ 192 due to incorrect array filler lowering. (#GH189643)
+- Fixed the behavior in C23 of ``auto``, by emitting an error when an array type is specified for a ``char *``. (#GH162694)
+- Fixed an issue where an assert was thrown instead of an error if no vulkan env was specified with ``--triple spirv``. (#GH189964)
+- Fixed incorrect rejection of ``auto`` with reordered declaration specifiers in C23. (#GH164121)
+- Fixed a crash where constexpr evaluation encountered invalid overrides. (#GH183290)
+- Fixed a bug where Clang fails to find instantiation of Decls in constraint checking. (#GH173086)
+- Fixed a crash when assigning to an element of an ``ext_vector_type`` with ``bool`` element type. (#GH189260)
+- Fixed a crash caused by declaring multiple ``ownership_returns`` attributes with mismatched or missing arguments. (#GH188733)
+- Clang now emits an error for friend declarations of lambda members. (#GH26540)
+- Fixed a crash caused by lambda capture handling in delayed default arguments. (#GH176534)
+- Fixed a crash when parsing invalid ``static_assert`` declarations with string-literal messages (#GH187690).
+- Fixed a potential stack-use-after-return issue in Clang when copy-initializing
+  an array via an element-at-a-time copy loop (#GH192026)
+- Fixed an issue where certain designated initializers would be rejected for constexpr variables. (#GH193373)
+- Fixed a crash when ``#embed`` is used with C++ modules (#GH195350)
+
+Bug Fixes to Compiler Builtins
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Fix a crash when passing an unresolved overload set to ``__builtin_classify_type``. (#GH175589)
+- Fixed a crash when calling `__builtin_allow_sanitize_check` with no arguments. (#GH183927)
+- ``__annotation`` is now diagnosed as unsupported on non-Windows/UEFI targets, fixing a
+  crash when using it with ``-fms-extensions`` on other platforms. (#GH184318)
+
+Bug Fixes to Attribute Support
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed a behavioral discrepancy between deleted functions and private members when checking the ``enable_if`` attribute. (#GH175895)
+- Fixed ``init_priority`` attribute by delaying type checks until after the type is deduced.
+
+Bug Fixes to C++ Support
+^^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed a preprocessor assertion failure triggered when parsing an invalid template-id starting with ``::template operator``. (#GH186582)
+- Fixed a crash when a function template is defined as a non-template friend with a global scope qualifier. (#GH185341)
+- Clang now rejects constant template parameters with block pointer types, since these are not implemented anyway and would lead to crashes. (#GH189247)
+- Fixed a crash on error recovery when dealing with invalid templates. (#GH183075)
+- Fixed a crash when instantiating ``requires`` expressions involving substitution failures in C++ concepts. (#GH176402)
+- Concepts appearing in the require-clause of a member function no longer have access to non-public members of that class,
+  or to a current class object. (#GH115838) (#GH194803) (#GH197067)
+- We no longer caches invalid variable specializations. (#GH132592)
+- Fixed an incorrect template argument deduction when matching packs of template
+  template parameters when one of its parameters is also a pack. (#GH181166)
+- Clang no longer errors on overloads with different ref-qualifiers and constraints. (#GH120812)
+- Fixed a crash when a default argument is passed to an explicit object parameter. (#GH176639)
+- Fixed an alias template CTAD crash.
+- Correctly diagnose uses of ``co_await`` / ``co_yield`` in the default argument of nested function declarations. (#GH98923)
+- Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741)
+- Clang incorrectly instantiated variable specializations outside of the immediate context. (#GH54439)
+- Fixed a crash when pack expansions are used as arguments for non-pack parameters of built-in templates. (#GH180307)
+- Fix a problem where a substitution failure when evaluating a type requirement
+  could directly make the program ill-formed.
+- Fix a problem where pack index expressions where incorrectly being regarded as equivalent.
+- Fixed a bug where captured variables in non-mutable lambdas were incorrectly treated as mutable
+  when used inside decltype in the return type. (#GH180460)
+- Fixed a crash when evaluating uninitialized GCC vector/ext_vector_type vectors in ``constexpr``. (#GH180044)
+- Fixed a crash when `explicit(bool)` is used with an incomplete enumeration. (#GH183887)
+- Fixed a crash on ``typeid`` of incomplete local types during template instantiation. (#GH63242), (#GH176397)
+- Fixed spurious diagnostics produced when checking if constraints are equivalent for redeclarations,
+  which could make the program mistakenly ill-formed.
+- Fixed a crash when an immediate-invoked ``consteval`` lambda is used as an invalid initializer. (#GH185270)
+- Fixed an assertion failure when using a global destructor with a target with a non-default program address space. (#GH186484)
+- Fixed a crash when instantiating an invalid out-of-line static data member definition in a local class. (#GH176152)
+- Inherited constructors in ``dllexport`` classes are now exported for ABI-compatible cases, matching
+  MSVC behavior. Constructors with variadic arguments or callee-cleanup parameters are not yet supported
+  and produce a warning. (#GH162640)
+- Correctly diagnose invalid non-dependent calls in dependent contexts. (#GH135694)
+- Fix initialization of GRO when GRO-return type mismatches, as part of CWG2563. (#GH98744)
+- Fix an error using an initializer list with array new for a type that is not default-constructible. (#GH81157)
+- We no longer consider conversion operators when copy-initializing from the same type. This was non
+  conforming and could lead to recursive constraint satisfaction checking. (#GH149443)
+- Fixed a crash in Itanium C++ name mangling for a lambda in a local class field initializer inside a constructor/destructor. (#GH176395)
+- Fixed crashes in Itanium C++ name mangling for lambdas with trailing requires-clauses involving requires-expressions. (#GH100774) (#GH123854)
+- Fixed an invalid rejection and assertion failure while generating ``operator=`` for fields with the ``__restrict`` qualifier. (#GH37979)
+- Fixed a use-after-free bug when parsing default arguments containing lambdas in declarations with template-id declarators. (#GH196725)
+- Fixed a crash in constant evaluation using placement new on an array which was later initialized. (#GH196450)
+- Fixed an issue where Clang incorrectly accepted invalid unqualified uses of local nested class names outside their declaring scope. (#GH184622)
+
+Bug Fixes to AST Handling
+^^^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed a bug where explicit nullability property attributes were not stored in AST nodes in Objective-C. (#GH179703)
+- Fixed a bug where alias CTAD, or an invalid template template parameter, could create a template with an empty template
+  parameter list. This also adds asserts to prevent this from happening again.
+- Fixed a crash when parsing Doxygen ``@param`` commands attached to invalid declarations or non-function entities. (#GH182737)
+- Fixed the SourceLocation and SourceRange of reversed rewritten CXXOperatorCallExpr. (#GH192467)
+- Fixed a assertion when ``__block`` is used on global variables in C mode. (#GH183974)
+
+Miscellaneous Bug Fixes
+^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed the arguments of the format attribute on ``__builtin_os_log_format``.  Previously, they were off by 1.
+
+Miscellaneous Clang Crashes Fixed
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Fixed a crash when attempting to jump over initialization of a variable with variably modified type. (#GH175540)
+- Fixed a crash when using loop hint with a value dependent argument inside a
+  generic lambda. (#GH172289)
+- Fixed a crash in C++ overload resolution with ``_Atomic``-qualified argument types. (#GH170433)
+- Fixed a crash related to missing source locations (#GH186655)
+- Fixed a crash when casting a parenthesized unresolved template-id or array section. (#GH183505)
+- Fixed a crash when initializing a ``constexpr`` pointer with a floating-point literal in C23. (#GH180313)
+- Fixed a lack of diagnostic for substitution failures in base classes when using `std::void_t`-like types.
+- Fixed a crash when emitting debug info for base classes with instantiation-dependent-only types (#GH193932)
+- Fixed an assertion when diagnosing address-space qualified ``new``/``delete`` in language-defined address spaces such as OpenCL ``__local``. (#GH178319)
+- Fixed an assertion failure in ObjC++ ARC when binding a rvalue reference to reference with different lifetimes (#GH178524)
+- Fixed a crash when subscripting a vector type with large unsigned integer values. (#GH180563)
+- Fixed a crash when attempting to diagnose incompatible conversions involving function types (#GH182534)
+- Fixed a crash when evaluating ``__is_bitwise_cloneable`` on invalid record types. (#GH183707)
+- Fixed an assertion failure when casting a function pointer with a target with a non-default program address space. (#GH186210)
+- Fixed a crash when ``decltype(__builtin_FUNCTION())`` is used as a template type argument. (#GH167433)
+- Fixed an assertion failure when parsing an invalid ``decltype`` specifier with missing parentheses or extra semicolons. (#GH188014)
+- Fixed a crash when explicitly casting a complex type to or from an atomic complex type. (#GH172208)
+- Fixed a crash when explicitly casting a scalar to an atomic complex. (#GH114885)
+- Fixed an assertion failure when parsing an invalid out-of-line enum definition with template parameters. (#GH187909)
+- Fixed an assertion failure on invalid template template parameter during typo correction. (#GH183983)
+- Fixed an assertion failure in ``isAtEndOfMacroExpansion`` on macro expansions crossing the boundary of two fileIDs. (#GH115007), (#GH21755)
+
+OpenACC Specific Changes
+------------------------
+
+Target Specific Changes
+-----------------------
+
+AMDGPU Support
+^^^^^^^^^^^^^^
+- Introduced a new target specific builtin ``__builtin_amdgcn_processor_is``,
+  a late / deferred query for the current target processor.
+- Introduced a new target specific builtin ``__builtin_amdgcn_is_invocable``,
+  a late / deferred query for the availability of target specific builtins.
+- Initial support for gfx1310
+- The ``amdgpu_num_sgpr`` and ``amdgpu_num_vgpr`` function attributes are now
+  deprecated. Using them produces a ``-Wdeprecated-declarations`` warning. Use
+  ``amdgpu_waves_per_eu`` instead.
+
+NVPTX Support
+^^^^^^^^^^^^^^
+
+X86 Support
+^^^^^^^^^^^
+- ``march=znver6`` is now supported.
+
+Arm and AArch64 Support
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Android Support
+^^^^^^^^^^^^^^^
+
+Windows Support
+^^^^^^^^^^^^^^^
+
+- Clang now defines the ``_MSVC_TRADITIONAL`` macro as ``1`` when emulating MSVC
+  19.15 (Visual Studio 2017 version 15.8) and later. (#GH47114)
+- ``-fmacro-prefix-map=`` (``-ffile-prefix-map=``) now affects an anonymous namespace hash generation
+  for the MSVC targets and allows deterministic symbol mangling for reproducible builds.
+
+LoongArch Support
+^^^^^^^^^^^^^^^^^
+
+- DWARF fission is now compatible with linker relaxations, allowing `-gsplit-dwarf` and `-mrelax`
+  to be used together when building for the LoongArch platform.
+
+RISC-V Support
+^^^^^^^^^^^^^^
+
+- Tenstorrent Ascalon D8 was renamed to Ascalon X. Use `tt-ascalon-x` with `-mcpu` or `-mtune`.
+- Intrinsics were added for the 'Zvabd` (RISC-V Integer Vector Absolute Difference) extension.
+- Intrinsics were added for the 'Zvzip` (Reordering Structured Data in Vector Registers) extension.
+
+CUDA/HIP Language Changes
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- The new offloading driver is now the default for HIP. Use
+  `--no-oflfoad-new-driver` to return to the old behavior.
+
+CUDA Support
+^^^^^^^^^^^^
+
+AIX Support
+^^^^^^^^^^^
+
+- The driver default for the linker flag `-bcdtors` now defaults to `mbr`
+  (instead of `all`) which only extracts static init from archive members which
+  would otherwise be referenced.
+  (See https://www.ibm.com/docs/en/aix/7.2.0?topic=l-ld-command for details).
+
+NetBSD Support
+^^^^^^^^^^^^^^
+
+WebAssembly Support
+^^^^^^^^^^^^^^^^^^^
+
+- Fixed a crash when ``__funcref`` is applied to a non-function pointer type.
+  (#GH118233)
+
+AVR Support
+^^^^^^^^^^^
+
+SystemZ Support
+^^^^^^^^^^^^^^^
+
+- Add support for `#pragma export` for z/OS.  This is a pragma used to export functions and variables
+  with external linkage from shared libraries.  It provides compatibility with the IBM XL C/C++
+  compiler.
+
+DWARF Support in Clang
+----------------------
+
+Floating Point Support in Clang
+-------------------------------
+
+Fixed Point Support in Clang
+----------------------------
+
+AST Matchers
+------------
+- Add ``functionTypeLoc`` matcher for matching ``FunctionTypeLoc``.
+- Add missing support for ``TraversalKind`` in some ``addMatcher()`` overloads.
+
+clang-format
+------------
+- Add ``ObjCSpaceAfterMethodDeclarationPrefix`` option to control space between the
+  '-'/'+' and the return type in Objective-C method declarations
+- Deprecate the ``BinPackParameters`` and ``BinPackArguments`` options and replace 
+  them with the ``PackParameters`` and ``PackArguments`` structs (respectively) to 
+  unify packing behavior. Add the ``BreakAfter`` option to the structs, allowing 
+  parameter and argument lists to be formatted with one parameter/argument on each 
+  line if they exceed the specified count.
+- Add ``AfterComma`` value to ``BreakConstructorInitializers`` to allow breaking
+  constructor initializers after commas, keeping the colon on the same line.
+- Extend ``BreakBinaryOperations`` to accept a structured configuration with
+  per-operator break rules and minimum chain length gating via ``PerOperator``.
+- Add ``AllowShortRecordOnASingleLine`` option and set it to ``EmptyAndAttached`` for LLVM style.
+- Add ``BreakFunctionDeclarationParameters`` option to always break before function
+  declaration parameters.
+- Add ``EnumAssignments`` option to ``AlignConsecutiveAssignments`` for aligning
+  enum assignments without affecting other assignments.
+- Add ``BreakBeforeReturnType`` option to break before the function return
+  type.
+
+libclang
+--------
+- Visit constraints of `auto` type to properly visit concept usages (#GH166580)
+- Visit switch initializer statements (https://bugs.kde.org/show_bug.cgi?id=415537#c2)
+- Fix crash in clang_getBinaryOperatorKindSpelling and clang_getUnaryOperatorKindSpelling
+- The clang_Module_getASTFile API is deprecated and now always returns nullptr
+
+Code Completion
+---------------
+
+- Fixed a crash in code completion when using a C-Style cast with a parenthesized
+  operand in Objective-C++ mode. (#GH180125)
+
+Static Analyzer
+---------------
+
+Crash and bug fixes
+^^^^^^^^^^^^^^^^^^^
+
+- Fixed ``security.VAList`` checker producing false positives when analyzing
+  C23 code where ``va_start`` expands to ``__builtin_c23_va_start``.
+
+.. comment:
+  This is for the Static Analyzer.
+  Using the caret `^^^` underlining for subsections:
+    - Crash and bug fixes
+    - New checkers and features
+    - Improvements
+    - Moved checkers
+
+.. _release-notes-sanitizers:
+
+Sanitizers
+----------
+- UndefinedBehaviorSanitizer now supports ``__ubsan_default_suppressions``.
+
+Python Binding Changes
+----------------------
+- Add deprecation warnings to ``CompletionChunk.isKind...`` methods.
+  These will be removed in a future release. Existing uses should be adapted
+  to directly compare equality of the ``CompletionChunk`` kind with
+  the corresponding ``CompletionChunkKind`` variant.
+
+  Affected methods: ``isKindOptional``, ``isKindTypedText``, ``isKindPlaceHolder``,
+  ``isKindInformative`` and ``isKindResultType``.
+- Add a deprecation warning to ``CodeCompletionResults.results``.
+  This property will become an implementation detail with changed behavior in a
+  future release and should not be used directly.. Existing uses of
+  ``CodeCompletionResults.results`` should be changed to directly use
+  ``CodeCompletionResults``: it nows supports ``__len__`` and ``__getitem__``,
+  so it can be used the same as ``CodeCompletionResults.results``.
+- Added a new helper method ``get_clang_version`` to the class ``Config`` to
+  read the version string of the libclang in use.
+
+OpenMP Support
+--------------
+- Added support for ``transparent`` clause in task and taskloop directives.
+- Added support for ``use_device_ptr`` clause to accept an optional
+  ``fallback`` modifier (``fb_nullify`` or ``fb_preserve``) with OpenMP >= 61.
+- Added support for ``local`` clause with declare_target directive when
+  OpenMP >= 60.
+
+Improvements
+^^^^^^^^^^^^
+- Improved substitution performance in concept checking. (#GH172266)
+
+- Clang now preserves the left-hand side of a binary expression (such as an
+  assignment or comparison) in a ``RecoveryExpr`` when the right-hand side fails
+  to parse. This improves IDE features like go-to-definition in ``clangd``.
+
+Additional Information
+======================
+
+A wide variety of additional information is available on the `Clang web
+page <https://clang.llvm.org/>`_. The web page contains versions of the
+API documentation which are up-to-date with the Git version of
+the source code. You can access versions of these documents specific to
+this release by going into the "``clang/docs/``" directory in the Clang
+tree.
+
+If you have any questions or comments about Clang, please feel free to
+contact us on the `Discourse forums (Clang Frontend category)
+<https://discourse.llvm.org/c/clang/6>`_.

>From 93c9222108590ae4740869f11a994790c4e88562 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Thu, 25 Jun 2026 10:52:16 -0700
Subject: [PATCH 10/12] Add test to ensure that warnings are generated on K&R
 style function definitions without a prototype

---
 clang/test/CodeGen/target-avx-abi-diag-knr.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100644 clang/test/CodeGen/target-avx-abi-diag-knr.c

diff --git a/clang/test/CodeGen/target-avx-abi-diag-knr.c b/clang/test/CodeGen/target-avx-abi-diag-knr.c
new file mode 100644
index 0000000000000..c9fca7d39e042
--- /dev/null
+++ b/clang/test/CodeGen/target-avx-abi-diag-knr.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -std=c17 -verify=no256,knr -o - -S
+// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -std=c17 -target-feature +avx -verify=knr -o - -S
+// REQUIRES: x86-registered-target
+
+typedef short avx256Type __attribute__((vector_size(32)));
+
+// knr-warning at +3 {{a function definition without a prototype is deprecated in all versions of C and is not supported in C23}}
+// no256-warning at +2 {{AVX vector return of type 'avx256Type' (vector of 16 'short' values) without 'avx' enabled changes the ABI}}
+// no256-warning at +2 {{AVX vector argument of type 'avx256Type' (vector of 16 'short' values) without 'avx' enabled changes the ABI}}
+avx256Type knr_def(x)
+  avx256Type x;
+{
+  return x;
+}

>From 31bf36d04609fa18c6701db2c62e5bbe96b264c8 Mon Sep 17 00:00:00 2001
From: freaknbigpanda <benjamin.luke at sony.com>
Date: Wed, 1 Jul 2026 11:37:14 -0700
Subject: [PATCH 11/12] fixed conflict with upstream

---
 clang/docs/ReleaseNotes.md  |   4 +
 clang/docs/ReleaseNotes.rst | 915 ------------------------------------
 2 files changed, 4 insertions(+), 915 deletions(-)
 delete mode 100644 clang/docs/ReleaseNotes.rst

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 6788763d0687e..dd75652e3b541 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -603,6 +603,10 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
 
 - Improved `-Wassign-enum` performance by caching enum enumerator values. (#GH176454)
 
+- Clang now emits `-Wpsabi` diagnostics for externally visible x86-64
+  function definitions that return or take AVX or AVX-512 vector types without
+  enabling the corresponding target feature.
+
 - Fixed a false negative in `-Warray-bounds` where the warning was suppressed
   when accessing a member function on a past-the-end array element.
   (#GH179128)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
deleted file mode 100644
index 80b7880bdc85a..0000000000000
--- a/clang/docs/ReleaseNotes.rst
+++ /dev/null
@@ -1,915 +0,0 @@
-.. If you want to modify sections/contents permanently, you should modify both
-   ReleaseNotes.rst and ReleaseNotesTemplate.txt.
-
-===========================================
-Clang |release| |ReleaseNotesTitle|
-===========================================
-
-.. contents::
-   :local:
-   :depth: 2
-
-Written by the `LLVM Team <https://llvm.org/>`_
-
-.. only:: PreRelease
-
-  .. warning::
-     These are in-progress notes for the upcoming Clang |version| release.
-     Release notes for previous releases can be found on
-     `the Releases Page <https://llvm.org/releases/>`_.
-
-Introduction
-============
-
-This document contains the release notes for the Clang C/C++/Objective-C
-frontend, part of the LLVM Compiler Infrastructure, release |release|. Here we
-describe the status of Clang in some detail, including major
-improvements from the previous release and new feature work. For the
-general LLVM release notes, see `the LLVM
-documentation <https://llvm.org/docs/ReleaseNotes.html>`_. For the libc++ release notes,
-see `this page <https://libcxx.llvm.org/ReleaseNotes.html>`_. All LLVM releases
-may be downloaded from the `LLVM releases web site <https://llvm.org/releases/>`_.
-
-For more information about Clang or LLVM, including information about the
-latest release, please see the `Clang Web Site <https://clang.llvm.org>`_ or the
-`LLVM Web Site <https://llvm.org>`_.
-
-Potentially Breaking Changes
-============================
-
-C/C++ Language Potentially Breaking Changes
--------------------------------------------
-
-- Clang now makes it ill-formed to try to ``break`` out of or ``continue`` a loop inside its own condition,
-  increment, or init-statement in all C and C++ language modes. This means that code such as
-
-  .. code-block:: c++
-
-    while (({ break; })) {}
-
-  is now ill-formed. An outer loop can still be broken out of or continued so long as the inner loop is
-  in the body of the outer loop:
-
-  .. code-block:: c++
-
-    // Ok, this breaks out of the 'for' loop.
-    for (;;) {
-      while (({ break; true; })) {}
-    }
-
-    // Error: can't break out of the 'for' loop from within its own increment.
-    for (;;({ while (({ break; true; })) {} })) {}
-
-  This also resolves a divergence from GCC: in a construct such as
-
-  .. code-block:: c++
-
-    for (;;) {
-      while (({ break; true; })) {}
-    }
-
-  Clang would previously ``break`` out of the ``while`` loop, whereas GCC (since version 9) would
-  ``break`` out of the ``for`` loop here. Now, Clang and GCC both break out of the ``for`` loop.
-
-C++ Specific Potentially Breaking Changes
------------------------------------------
-
-- Clang now more aggressively optimizes away stores to objects after they are
-  dead. This behavior can be disabled with ``-fno-lifetime-dse``.
-
-- Clang now correctly rejects ``export`` declarations in module implementation
-  partitions. (#GH107602)
-
-- Template argument deduction now treats the ``N`` in ``_BitInt(N)``
-  as being of type ``std::size_t`` instead of ``int``,
-  matching the deduction of array sizes from ``int(&)[N]``.
-  This is a breaking change for code that depended on the previously deduced type. (#GH195033)
-
-- Clang now rejects nested local classes defined in a different
-  block scope than their parent class. (#GH193472)
-
-ABI Changes in This Version
----------------------------
-
-- Fixed incorrect struct layout for ``_BitInt`` bitfields wider than 255 bits
-  on MSVC targets. Internal bitfield tracking fields were changed from
-  ``unsigned char`` to ``uint64_t`` to prevent overflow. This might be an ABI
-  break for such structs compared to earlier Clang versions.
-- Fixed a number of issues with the ``__regcall`` calling convention for passing
-  structs on non-Windows x86-64 targets, including a crash when handling empty
-  struct arguments. This changes how structs that contain arrays, floating point
-  types, or ``_Complex float`` types are passed, and may introduce
-  incompatibilities with code compiled by earlier versions of Clang that uses
-  the ``__regcall`` calling convention on these targets. (#GH62999) (#GH98635)
-- Fixed Itanium mangling for lambdas in instantiated non-static data member
-  initializers by preserving the field-name closure-prefix. This changes the
-  mangled names for affected lambdas. (#GH190555)
-
-AST Dumping Potentially Breaking Changes
-----------------------------------------
-
-- The JSON AST dump now includes all fields from ``AvailabilityAttr``: ``platform``,
-  ``introduced``, ``deprecated``, ``obsoleted``, ``unavailable``, ``message``,
-  ``strict``, ``replacement``, ``priority``, and ``environment``. Previously, these
-  fields were missing from the JSON output.
-- Colons that appear at the end of a ParamCommentCommand name are not serialized
-  as part of the name.
-
-Clang Frontend Potentially Breaking Changes
--------------------------------------------
-
-- HIPSPV toolchain: `--offload-targets=spirv{32,64}` option is
-  deprecated and will be removed when the new offload driver becomes
-  default. The replacement for the option is
-  `--offload-targets=spirv{32,64}-unknown-chipstar` when using the new
-  offload driver (`--offload-new-driver`).
-- The new driver (`--offload-new-driver`) is now default for all offloading
-  compilations. This changes the ABI for relocatable device code. Currently,
-  libraries will need to be recompiled, or used with
-  (`--no-offload-new-driver`). This option will be removed in the next release.
-
-- Clang no longer defines the ``__cpp_impl_coroutine`` feature test macro under the 32-bit x86 Microsoft ABI,
-  as support for coroutines on this target is incomplete.
-  When using coroutines on this target a warning is emmitted to indicate the lack of full support.
-  That warning can be disabled with ``-Wno-coroutines-unsupported-target``. (see #GH59382)
-
-Clang Python Bindings Potentially Breaking Changes
---------------------------------------------------
-- Remove ``CompletionString.Availability``. No libclang interfaces returned instances of it.
-- ``CompletionString.availability`` now returns instances of ``CompletionString.AvailabilityKindCompat``.
-
-  Instances of ``AvailabilityKindCompat`` have the same ``__str__`` representation
-  as the previous ``CompletionChunk.Kind`` and are equality-comparable with
-  the existing ``AvailabilityKind`` enum. It will be replaced by ``AvailabilityKind``
-  in a future release. When this happens, the return type of ``CompletionString.availability``
-  will change to ``AvailabilityKind``, so it is recommended to use ``AvailabilityKind``
-  to compare with the return values of ``CompletionString.availability``.
-- Remove ``availabilityKinds``. In this release, uses of ``availabilityKinds``
-  need to be replaced by ``CompletionString.AvailabilityKind``.
-- ``CompletionChunk.kind`` now returns instances of ``CompletionChunkKind``.
-
-  Instances of ``CompletionChunkKind`` have the same ``__str__`` representation
-  as the previous ``CompletionChunk.Kind`` for compatibility.
-  These representations will be changed in a future release to match other enums.
-- Remove ``completionChunkKindMap``. In this release, uses of ``completionChunkKindMap``
-  need to be replaced by ``CompletionChunkKind``.
-- Move ``SPELLING_CACHE`` into ``CompletionChunk`` and change it to use
-  ``CompletionChunkKind`` instances as keys, instead of the enum values.
-  An alias is kept in the form of a ``SPELLING_CACHE`` variable, but it only supports
-  ``__getitem__`` and ``__contains__``. It will be removed in a future release.
-  Please migrate to using ``CompletionChunk.SPELLING_CACHE`` instead.
-- ``SourceLocation`` and ``SourceRange`` now use ``NotImplemented`` to delegate
-  equality checks (``__eq__``) to the other object they are compared with when
-  they are of different classes. They previously returned ``False`` when compared
-  with objects of other classes.
-- ``TranslationUnit.get_tokens`` now throws an error if both the ``extent`` and
-  ``locations`` argument are passed. Previousy, ``locations`` took precedence.
-- ``_CXUnsavedFile`` will be renamed to ``UnsavedFile`` for consistency.
-  ``UnsavedFile`` is already available to use and existing uses should
-  be adapted to refer to it instead. ``_CXUnsavedFile`` will be removed in a
-  future release.
-
-OpenCL Potentially Breaking Changes
------------------------------------
-- Clang now diagnoses zero-length arrays as errors in OpenCL. OpenCL C 3.0
-  section 6.11.d states that "Variable length arrays and structures with
-  flexible (or unsized) arrays are not supported."
-
-What's New in Clang |release|?
-==============================
-
-C++ Language Changes
---------------------
-
-- ``__is_trivially_equality_comparable`` no longer returns false for all enum types. (#GH132672)
-
-C++2c Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-C++23 Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-- Partially implement Itanium mangling for pack indexing. Partially substituted packs are not yet supported. (#GH112003)
-
-C++20 Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-- Clang now supports `P1857R3 <https://wg21.link/p1857r3>`_ Modules Dependency Discovery. (#GH54047)
-
-C++17 Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-Resolutions to C++ Defect Reports
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Implemented `CWG1780 Explicit instantiation/specialization of generic lambda
-  operator() <https://cplusplus.github.io/CWG/issues/1780.html>`_
-
-- Clang now allows omitting ``typename`` before a template name in a
-  conversion operator, implementing `CWG2413 <https://wg21.link/cwg2413>`_.
-
-- Clang now uses non-reference types for structured bindings whose initializer
-  returns a prvalue. This resolves `CWG3135 <https://wg21.link/cwg3135>`_.
-
-C Language Changes
-------------------
-
-C2y Feature Support
-^^^^^^^^^^^^^^^^^^^
-
-- Implemented the type-specific C2y ``<stdbit.h>`` rotate functions with constexpr
-  evaluation support:
-  ``stdc_rotate_left_{uc,us,ui,ul,ull}`` and
-  ``stdc_rotate_right_{uc,us,ui,ul,ull}``.
-
-C23 Feature Support
-^^^^^^^^^^^^^^^^^^^
-- Clang now allows C23 ``constexpr`` struct member access through the dot operator in constant expressions. (#GH178349)
-
-Objective-C Language Changes
------------------------------
-
-- Clang now emits Objective-C number, array, and dictionary literals as
-  compile-time constant data structures rather than runtime ``objc_msgSend``
-  calls on targets whose runtime supports constant literal classes. The
-  feature can be disabled altogether with ``-fno-objc-constant-literals``,
-  or selectively per literal kind with ``-fno-constant-nsnumber-literals``,
-  ``-fno-constant-nsarray-literals``, and
-  ``-fno-constant-nsdictionary-literals``.
-
-Non-comprehensive list of changes in this release
--------------------------------------------------
-
-- Added support for floating point and pointer values in most ``__atomic_``
-  builtins.
-
-- Added ``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right``
-  for bit rotation of unsigned integers including ``_BitInt`` types. Rotation
-  counts are normalized modulo the bit-width and support negative values.
-  Usable in constant expressions. Implicit conversion is supported for
-  class/struct types with conversion operators.
-- Implemented the following C23 ``<stdbit.h>`` builtins with ``_BitInt``
-  support and constexpr evaluation:
-  ``__builtin_stdc_leading_zeros``, ``__builtin_stdc_leading_ones``,
-  ``__builtin_stdc_trailing_zeros``, ``__builtin_stdc_trailing_ones``,
-  ``__builtin_stdc_first_leading_zero``, ``__builtin_stdc_first_leading_one``,
-  ``__builtin_stdc_first_trailing_zero``, ``__builtin_stdc_first_trailing_one``,
-  ``__builtin_stdc_count_zeros``, ``__builtin_stdc_count_ones``,
-  ``__builtin_stdc_has_single_bit``, ``__builtin_stdc_bit_width``,
-  ``__builtin_stdc_bit_floor``, and ``__builtin_stdc_bit_ceil``.
-
-- Implemented the type-specific C23 ``<stdbit.h>`` functions with constexpr
-  evaluation support:
-  ``stdc_leading_zeros_{uc,us,ui,ul,ull}``,
-  ``stdc_leading_ones_{uc,us,ui,ul,ull}``,
-  ``stdc_trailing_zeros_{uc,us,ui,ul,ull}``,
-  ``stdc_trailing_ones_{uc,us,ui,ul,ull}``,
-  ``stdc_first_leading_zero_{uc,us,ui,ul,ull}``,
-  ``stdc_first_leading_one_{uc,us,ui,ul,ull}``,
-  ``stdc_first_trailing_zero_{uc,us,ui,ul,ull}``,
-  ``stdc_first_trailing_one_{uc,us,ui,ul,ull}``,
-  ``stdc_count_zeros_{uc,us,ui,ul,ull}``,
-  ``stdc_count_ones_{uc,us,ui,ul,ull}``,
-  ``stdc_has_single_bit_{uc,us,ui,ul,ull}``,
-  ``stdc_bit_width_{uc,us,ui,ul,ull}``,
-  ``stdc_bit_floor_{uc,us,ui,ul,ull}``, and
-  ``stdc_bit_ceil_{uc,us,ui,ul,ull}``.
-
-- A new generic bit-reverse builtin function ``__builtin_bitreverseg`` that
-  extends bit-reversal support to all standard integers type, including
-  ``_BitInt``
-
-- Deprecated float types support from ``__builtin_elementwise_max`` and
-  ``__builtin_elementwise_min``.
-
-- Added header ``endian.h`` which contains byte order helpers specified in POSIX
-
-- Added #pragma loop licm(disable) for llvm.loop.licm.disable metadata
-
-- Added a new ``ExplicitInstantiationDecl`` AST node to represent explicit
-  template instantiations (e.g., ``template void foo<int>();`` or
-  ``extern template class S<int>;``). Previously, source location information
-  for explicit instantiation statements was discarded after parsing. The new
-  node preserves the full source range including the ``extern`` and ``template``
-  keywords, qualifiers, template arguments as written, and the declared type,
-  enabling tools such as language servers and refactoring engines to accurately
-  map source locations back to explicit instantiation sites.
-
-- ``typeid`` on references and pointers of ``final`` types no longer emits a
-  vtable lookup at runtime.
-
-- Updated support for Unicode from 15.1 to 18.0.
-
-New Compiler Flags
-------------------
-- New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added
-  to enable or disable Microsoft's anonymous struct/union extension without
-  enabling other ``-fms-extensions`` features (#GH177607).
-- New option ``--precompile-reduced-bmi`` allows build system to generate a
-  reduced BMI only for a C++20 importable module unit. Previously the users
-  can only generate the reduced BMI as a by-product, e.g, an object files or
-  a full BMI.
-
-- New ``-cc1`` option ``-fexperimental-overflow-behavior-types`` added to
-  enable parsing of the experimental ``overflow_behavior`` type attribute and
-  type specifiers.
-
-- New ``-cl`` option ``/d2guardnochecks`` added to match MSVC. When Windows
-  Control Flow Guard (CFG) is enabled by other options, it will instruct Clang
-  to emit the CFG metadata, but disable adding checks.
-
-- New option ``-fdiagnostics-show-inlining-chain`` added to show inlining chain
-  notes for ``[[gnu::warning]]`` and ``[[gnu::error]]`` diagnostics. When a
-  function with these attributes is called from an inlined context, Clang can
-  now show which functions were inlined to reach the call. When debug info is
-  available (``-gline-directives-only`` (implicitly enabled at ``-g1``) or
-  higher), accurate source locations are used; otherwise, a heuristic fallback
-  is used with a note suggesting how to enable debug info for better accuracy.
-
-- New option ``-fwin-cfg-mechanism=`` added to control the mechanism used by
-  Control Flow Guard on Windows. Accepted values are ``automatic`` (default),
-  ``dispatch``, and ``check``. The ``dispatch`` mechanism uses the dispatch
-  function to perform indirect call checks and can improve performance, while
-  ``check`` uses the traditional check mechanism.
-- New ``-cl`` option ``/d2guardcfgdispatch`` added to match MSVC. This acts as a
-  shorthand for ``-fwin-cfg-mechanism=dispatch``.
-- New ``-cl`` option ``/d2guardcfgdispatch-`` added to match MSVC. This acts as a
-  shorthand for ``-fwin-cfg-mechanism=check``.
-
-- New option ``-f[no-]strict-bool`` added to control whether Clang can assume
-  that ``bool`` values loaded from memory cannot have a bit pattern other
-  than 0 or 1.
-
-Deprecated Compiler Flags
--------------------------
-
-Modified Compiler Flags
------------------------
-- The `-mno-outline` and `-moutline` compiler flags are now allowed on RISC-V and X86, which both support the machine outliner.
-- The `-mno-outline` flag will now add the `nooutline` IR attribute, so that
-  `-mno-outline` and `-moutline` objects can be mixed correctly during LTO.
-
-Removed Compiler Flags
-----------------------
-
-Attribute Changes in Clang
---------------------------
-
-- Added new attribute ``stack_protector_ignore`` to opt specific local variables out of
-  the analysis which determines if a function should get a stack protector.  A function
-  will still generate a stack protector if other local variables or command line flags
-  require it.
-
-- Added a new attribute, ``[[clang::no_outline]]`` to suppress outlining from
-  annotated functions. This uses the LLVM `nooutline` attribute.
-
-- Introduced a new type attribute ``__attribute__((overflow_behavior))`` which
-  currently accepts either ``wrap`` or ``trap`` as an argument, enabling
-  type-level control over overflow behavior. There is also an accompanying type
-  specifier for each behavior kind via `__ob_wrap` and `__ob_trap`.
-
-- Introduced a new function attribute ``__attribute__((__personality__(...)))``
-  to explicitly specify the personality routine for exception handling. THis is
-  meant to be a low level tool for language runtime authors to associate a
-  foreign language personality with a given function. Note that this does not
-  perform any ABI validation for the personality routine.
-
-- :doc:`ThreadSafetyAnalysis` attributes now correctly handle implicit member
-  accesses in C, and parameter attributes in C++. This improves diagnostic
-  precision and fixes false positives.
-
-- The :doc:`ThreadSafetyAnalysis` attributes ``guarded_by`` and
-  ``pt_guarded_by`` now accept multiple capability arguments with refined
-  access semantics: *writing* requires all listed capabilities to be held
-  exclusively, while *reading* requires at least one to be held.  This is
-  sound because any writer must hold all capabilities, so holding any one
-  prevents concurrent writes.
-
-- :doc:`ThreadSafetyAnalysis` attributes like ``acquire_capability``,
-  ``release_capability``, ``requires_capability``, ``locks_excluded``,
-  ``try_acquire_capability``, and ``assert_capability`` can now be applied to
-  function pointer variables and fields.  The analysis checks calls through
-  annotated function pointers the same way it checks direct function calls.
-  Only plain function pointers are supported; pointers-to-member functions,
-  blocks, or wrappers (e.g. ``std::function``) are not yet supported.
-
-- The ``[[clang::unsafe_buffer_usage]]`` attribute is now supported in API
-  notes. For example:
-  
-  .. code-block:: yaml
-
-    Functions:
-      - Name: myUnsafeFunction
-        UnsafeBufferUsage: true
-
-- Added support for ``[[msvc::forceinline]]`` for functions and
-  ``[[msvc::forceinline_calls]]`` for statements. Both are aliases to
-  ``[[clang::always_inline]]`` with additional checks to ensure that they
-  are only accepted in places where MSVC also does.
-
-- The AMDGPU ``amdgpu_num_sgpr`` and ``amdgpu_num_vgpr`` attributes are now
-  deprecated. Clang emits a ``-Wdeprecated-declarations`` warning when they
-  are used. Use ``amdgpu_waves_per_eu`` instead to control SGPR and VGPR
-  usage.
-
-- Clang now allows GNU attributes between a member declarator and bit-field width. (#GH184954)
-
-Improvements to Clang's diagnostics
------------------------------------
-- Fixed bug in ``-Wdocumentation`` so that it correctly handles explicit
-  function template instantiations (#64087).
-
-- ``-Wunused-but-set-variable`` now diagnoses file-scope variables with
-  internal linkage (``static`` storage class) that are assigned but never used.
-  This new coverage is added under the subgroup ``-Wunused-but-set-global``,
-  allowing it to be disabled independently with ``-Wno-unused-but-set-global``.
-  (#GH148361)
-
-- Added ``-Wlifetime-safety`` to enable lifetime safety analysis,
-  a CFG-based intra-procedural analysis that detects use-after-free and related
-  temporal safety bugs. See the
-  `RFC <https://discourse.llvm.org/t/rfc-intra-procedural-lifetime-analysis-in-clang/86291>`_
-  for more details. By design, this warning is enabled in ``-Weverything``. To disable
-  the analysis, use ``-Wno-lifetime-safety`` or ``-fno-lifetime-safety``.
-
-- Added ``-Wlifetime-safety-suggestions`` to enable lifetime annotation suggestions.
-  This provides suggestions for function parameters that
-  should be marked ``[[clang::lifetimebound]]`` based on lifetime analysis. For
-  example, for the following function:
-
-  .. code-block:: c++
-
-    int* p(int *in) { return in; }
-
-  Clang will suggest:
-
-  .. code-block:: c++
-
-    warning: parameter in intra-TU function should be marked [[clang::lifetimebound]]
-    int* p(int *in) { return in; }
-           ^~~~~~~
-                   [[clang::lifetimebound]]
-    note: param returned here
-    int* p(int *in) { return in; }
-                             ^~
-
-- Added ``-Wlifetime-safety-noescape`` to detect misuse of ``[[clang::noescape]]``
-  annotation where the parameter escapes through return. For example:
-
-  .. code-block:: c++
-
-    int* p(int *in [[clang::noescape]]) { return in; }
-
-  Clang will warn:
-
-  .. code-block:: c++
-
-    warning: parameter is marked [[clang::noescape]] but escapes
-    int* p(int *in [[clang::noescape]]) { return in; }
-           ^~~~~~~
-    note: returned here
-    int* p(int *in [[clang::noescape]]) { return in; }
-                                                 ^~
-
-- Added ``-Wlifetime-safety-dangling-field`` to detect dangling field references
-  when stack memory escapes to class fields. This is part of ``-Wlifetime-safety``
-  and detects cases where local variables or parameters are stored in fields but
-  outlive their scope. For example:
-
-  .. code-block:: c++
-
-    struct DanglingView {
-      std::string_view view;
-      DanglingView(std::string s) : view(s) {}  // warning: address of stack memory escapes to a field
-    };
-
-- Improved ``-Wassign-enum`` performance by caching enum enumerator values. (#GH176454)
-
-- Clang now emits ``-Wpsabi`` diagnostics for externally visible x86-64
-  function definitions that return or take AVX or AVX-512 vector types without
-  enabling the corresponding target feature.
-
-- Fixed a false negative in ``-Warray-bounds`` where the warning was suppressed
-  when accessing a member function on a past-the-end array element.
-  (#GH179128)
-
-- Added a missing space to the FixIt for the ``implicit-int`` group of diagnostics and
-  made sure that only one such diagnostic and FixIt is emitted per declaration group. (#GH179354)
-
-- Fixed the Fix-It insertion point for ``expected ';' after alias declaration``
-  when parsing alias declarations involving a token-split ``>>`` sequence
-  (for example, ``using A = X<int>>;``). (#GH184425)
-
-- Fixed incorrect ``implicitly deleted`` diagnostic for explicitly deleted
-  candidate function. (#GH185693)
-
-- The ``-Wloop-analysis`` warning has been extended to catch more cases of
-  variable modification inside lambda expressions (#GH132038).
-
-- Clang now emits ``-Wsizeof-pointer-memaccess`` when snprintf/vsnprintf use the sizeof
-  the destination buffer(dynamically allocated) in the len parameter(#GH162366)
-
-- Added ``-Wmodule-map-path-outside-directory`` (off by default) to warn on
-  header and umbrella directory paths that use ``..`` to refer outside the module
-  directory in module maps found via implicit search
-  (``-fimplicit-module-maps``). This does not affect module maps specified
-  explicitly via ``-fmodule-map-file=``.
-
-- Honour ``[[maybe_unused]]`` attribute on private fields.
-  ``-Wunused-private-field`` no longer emits a warning for annotated private
-  fields.
-
-- Improved ``-Wgnu-zero-variadic-macro-arguments`` to suggest using
-  ``__VA_OPT__`` if the current language version supports it(#GH188624)
-
-- Clang now emits an error when implicitly casting a complex type to a built-in vector type. (#GH186805)
-
-- Added ``-Wnonportable-include-path-separator`` (off by default) to catch
-  #include directives that use backslashes as a path separator. The warning
-  includes a FixIt to change all the backslashes to forward slashes, so that the
-  code can automatically be made portable to other host platforms that don't
-  support backslashes.
-
-- Clang now explains why template deduction fails for explicit template arguments.
-
-- No longer emitting a ``-Wpre-c2y-compat`` or extension diagnostic about use
-  of octal literals with a ``0o`` prefix, and no longer emitting a
-  ``-Wdeprecated-octal-literals`` diagnostic for use of octal literals without
-  a ``0o`` prefix, when the literal is expanded from a macro defined in a
-  system header. (#GH192389)
-
-- Improved error recovery for missing semicolons after class members. Clang now avoids
-  skipping subsequent valid declarations when their previous decl is missing semicolon.
-
-- Removed the body of lambdas from some diagnostic messages.
-
-- Fixed false positive host-device mismatch errors in discarded `if constexpr` branches for CUDA/HIP;
-  such calls are now correctly skipped.
-
-- Clang now errors when a function declaration aliases a variable or vice versa. (#GH195550)
-
-- Added ``-Wattribute-alias`` to diagnose type mismatches between an alias and its aliased function. (#GH195550)
-
-- The diagnostics around ``__block`` now explain why a variable cannot be marked ``__block``. (#GH197213)
-
-- Extended ``-Wnonportable-include-path`` to warn about trailing whitespace and dots in ``#include`` paths. (#GH190610)
-
-- Clang now emits error when attribute is missing closing ``]]`` followed by ``;;``. (#GH187223)
-
-Improvements to Clang's time-trace
-----------------------------------
-
-Improvements to Coverage Mapping
---------------------------------
-
-- [MC/DC] Nested expressions are handled as individual MC/DC expressions.
-- "Single byte coverage" now supports branch coverage and can be used
-  together with ``-fcoverage-mcdc``.
-- Consteval member functions are no longer emitted in coverage mappings,
-  matching the existing behavior for free consteval functions. (#GH164448)
-
-Bug Fixes in This Version
--------------------------
-
-- Fixed atomic boolean compound assignment; the conversion back to atomic bool would be miscompiled. (#GH33210)
-- Correctly handle default template argument when establishing subsumption. (#GH188640)
-- Fixed a failed assertion in the preprocessor when ``__has_embed`` parameters are missing parentheses. (#GH175088)
-- Fix lifetime extension of temporaries in for-range-initializers in templates. (#GH165182)
-- Fixed a preprocessor crash in ``__has_cpp_attribute`` on incomplete scoped attributes. (#GH178098)
-- Fixes an assertion failure when evaluating ``__underlying_type`` on enum redeclarations. (#GH177943)
-- Fixed an assertion failure caused by nested macro expansion during header-name lexing (``__has_embed(__has_include)``). (#GH178635)
-- Clang now outputs relative paths of embeds for dependency output. (#GH161950)
-- Fix the result type of a binary operation where both operands are 'void' l-values. (#GH111300)
-- Fixed an assertion failure when evaluating ``_Countof`` on invalid ``void``-typed operands. (#GH180893)
-- Fixed an assertion failure in the serialized diagnostic printer when it is destroyed without calling ``finish()``. (#GH140433)
-- Fixed an assertion failure caused by error recovery while extending a nested name specifier with results from ordinary lookup. (#GH181470)
-- Fixed a crash when parsing ``#pragma clang attribute`` arguments for attributes that forbid arguments. (#GH182122)
-- Fixed a bug with multiple-include optimization (MIOpt) state not being preserved in some cases during lexing, which could suppress header-guard mismatch diagnostics and interfere with include-guard optimization. (#GH180155)
-- Fixed a crash when normalizing constraints involving concept template parameters whose index coincided with non-concept template parameters in the same parameter mapping.
-- Fixed a crash caused by accessing dependent diagnostics of a non-dependent context.
-- Fixed a crash when substituting into a non-type template parameter that has a type containing an undeduced placeholder type.
-- Fixed several crashes and improved diagnostics when a multidimensional subscript operator is applied to a built-in type. (#GH187800)
-- Correctly diagnosing and no longer crashing when ``export module foo``
-  (without a semicolon) are the final tokens in a module file. (#GH187771)
-- Fixed a crash in duplicate attribute checking caused by comparing constant arguments with different integer signedness. (#GH188259)
-- Clang now emits an error when returning an initializer list from a lambda
-  with an explicit return type of void. The diagnostic now correctly refers
-  to "lambda" instead of "block". (#GH188661)
-- Fixed a crash on _BitInt(N) arrays where 129 ≤ N ≤ 192 due to incorrect array filler lowering. (#GH189643)
-- Fixed the behavior in C23 of ``auto``, by emitting an error when an array type is specified for a ``char *``. (#GH162694)
-- Fixed an issue where an assert was thrown instead of an error if no vulkan env was specified with ``--triple spirv``. (#GH189964)
-- Fixed incorrect rejection of ``auto`` with reordered declaration specifiers in C23. (#GH164121)
-- Fixed a crash where constexpr evaluation encountered invalid overrides. (#GH183290)
-- Fixed a bug where Clang fails to find instantiation of Decls in constraint checking. (#GH173086)
-- Fixed a crash when assigning to an element of an ``ext_vector_type`` with ``bool`` element type. (#GH189260)
-- Fixed a crash caused by declaring multiple ``ownership_returns`` attributes with mismatched or missing arguments. (#GH188733)
-- Clang now emits an error for friend declarations of lambda members. (#GH26540)
-- Fixed a crash caused by lambda capture handling in delayed default arguments. (#GH176534)
-- Fixed a crash when parsing invalid ``static_assert`` declarations with string-literal messages (#GH187690).
-- Fixed a potential stack-use-after-return issue in Clang when copy-initializing
-  an array via an element-at-a-time copy loop (#GH192026)
-- Fixed an issue where certain designated initializers would be rejected for constexpr variables. (#GH193373)
-- Fixed a crash when ``#embed`` is used with C++ modules (#GH195350)
-
-Bug Fixes to Compiler Builtins
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Fix a crash when passing an unresolved overload set to ``__builtin_classify_type``. (#GH175589)
-- Fixed a crash when calling `__builtin_allow_sanitize_check` with no arguments. (#GH183927)
-- ``__annotation`` is now diagnosed as unsupported on non-Windows/UEFI targets, fixing a
-  crash when using it with ``-fms-extensions`` on other platforms. (#GH184318)
-
-Bug Fixes to Attribute Support
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Fixed a behavioral discrepancy between deleted functions and private members when checking the ``enable_if`` attribute. (#GH175895)
-- Fixed ``init_priority`` attribute by delaying type checks until after the type is deduced.
-
-Bug Fixes to C++ Support
-^^^^^^^^^^^^^^^^^^^^^^^^
-- Fixed a preprocessor assertion failure triggered when parsing an invalid template-id starting with ``::template operator``. (#GH186582)
-- Fixed a crash when a function template is defined as a non-template friend with a global scope qualifier. (#GH185341)
-- Clang now rejects constant template parameters with block pointer types, since these are not implemented anyway and would lead to crashes. (#GH189247)
-- Fixed a crash on error recovery when dealing with invalid templates. (#GH183075)
-- Fixed a crash when instantiating ``requires`` expressions involving substitution failures in C++ concepts. (#GH176402)
-- Concepts appearing in the require-clause of a member function no longer have access to non-public members of that class,
-  or to a current class object. (#GH115838) (#GH194803) (#GH197067)
-- We no longer caches invalid variable specializations. (#GH132592)
-- Fixed an incorrect template argument deduction when matching packs of template
-  template parameters when one of its parameters is also a pack. (#GH181166)
-- Clang no longer errors on overloads with different ref-qualifiers and constraints. (#GH120812)
-- Fixed a crash when a default argument is passed to an explicit object parameter. (#GH176639)
-- Fixed an alias template CTAD crash.
-- Correctly diagnose uses of ``co_await`` / ``co_yield`` in the default argument of nested function declarations. (#GH98923)
-- Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741)
-- Clang incorrectly instantiated variable specializations outside of the immediate context. (#GH54439)
-- Fixed a crash when pack expansions are used as arguments for non-pack parameters of built-in templates. (#GH180307)
-- Fix a problem where a substitution failure when evaluating a type requirement
-  could directly make the program ill-formed.
-- Fix a problem where pack index expressions where incorrectly being regarded as equivalent.
-- Fixed a bug where captured variables in non-mutable lambdas were incorrectly treated as mutable
-  when used inside decltype in the return type. (#GH180460)
-- Fixed a crash when evaluating uninitialized GCC vector/ext_vector_type vectors in ``constexpr``. (#GH180044)
-- Fixed a crash when `explicit(bool)` is used with an incomplete enumeration. (#GH183887)
-- Fixed a crash on ``typeid`` of incomplete local types during template instantiation. (#GH63242), (#GH176397)
-- Fixed spurious diagnostics produced when checking if constraints are equivalent for redeclarations,
-  which could make the program mistakenly ill-formed.
-- Fixed a crash when an immediate-invoked ``consteval`` lambda is used as an invalid initializer. (#GH185270)
-- Fixed an assertion failure when using a global destructor with a target with a non-default program address space. (#GH186484)
-- Fixed a crash when instantiating an invalid out-of-line static data member definition in a local class. (#GH176152)
-- Inherited constructors in ``dllexport`` classes are now exported for ABI-compatible cases, matching
-  MSVC behavior. Constructors with variadic arguments or callee-cleanup parameters are not yet supported
-  and produce a warning. (#GH162640)
-- Correctly diagnose invalid non-dependent calls in dependent contexts. (#GH135694)
-- Fix initialization of GRO when GRO-return type mismatches, as part of CWG2563. (#GH98744)
-- Fix an error using an initializer list with array new for a type that is not default-constructible. (#GH81157)
-- We no longer consider conversion operators when copy-initializing from the same type. This was non
-  conforming and could lead to recursive constraint satisfaction checking. (#GH149443)
-- Fixed a crash in Itanium C++ name mangling for a lambda in a local class field initializer inside a constructor/destructor. (#GH176395)
-- Fixed crashes in Itanium C++ name mangling for lambdas with trailing requires-clauses involving requires-expressions. (#GH100774) (#GH123854)
-- Fixed an invalid rejection and assertion failure while generating ``operator=`` for fields with the ``__restrict`` qualifier. (#GH37979)
-- Fixed a use-after-free bug when parsing default arguments containing lambdas in declarations with template-id declarators. (#GH196725)
-- Fixed a crash in constant evaluation using placement new on an array which was later initialized. (#GH196450)
-- Fixed an issue where Clang incorrectly accepted invalid unqualified uses of local nested class names outside their declaring scope. (#GH184622)
-
-Bug Fixes to AST Handling
-^^^^^^^^^^^^^^^^^^^^^^^^^
-- Fixed a bug where explicit nullability property attributes were not stored in AST nodes in Objective-C. (#GH179703)
-- Fixed a bug where alias CTAD, or an invalid template template parameter, could create a template with an empty template
-  parameter list. This also adds asserts to prevent this from happening again.
-- Fixed a crash when parsing Doxygen ``@param`` commands attached to invalid declarations or non-function entities. (#GH182737)
-- Fixed the SourceLocation and SourceRange of reversed rewritten CXXOperatorCallExpr. (#GH192467)
-- Fixed a assertion when ``__block`` is used on global variables in C mode. (#GH183974)
-
-Miscellaneous Bug Fixes
-^^^^^^^^^^^^^^^^^^^^^^^
-- Fixed the arguments of the format attribute on ``__builtin_os_log_format``.  Previously, they were off by 1.
-
-Miscellaneous Clang Crashes Fixed
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-- Fixed a crash when attempting to jump over initialization of a variable with variably modified type. (#GH175540)
-- Fixed a crash when using loop hint with a value dependent argument inside a
-  generic lambda. (#GH172289)
-- Fixed a crash in C++ overload resolution with ``_Atomic``-qualified argument types. (#GH170433)
-- Fixed a crash related to missing source locations (#GH186655)
-- Fixed a crash when casting a parenthesized unresolved template-id or array section. (#GH183505)
-- Fixed a crash when initializing a ``constexpr`` pointer with a floating-point literal in C23. (#GH180313)
-- Fixed a lack of diagnostic for substitution failures in base classes when using `std::void_t`-like types.
-- Fixed a crash when emitting debug info for base classes with instantiation-dependent-only types (#GH193932)
-- Fixed an assertion when diagnosing address-space qualified ``new``/``delete`` in language-defined address spaces such as OpenCL ``__local``. (#GH178319)
-- Fixed an assertion failure in ObjC++ ARC when binding a rvalue reference to reference with different lifetimes (#GH178524)
-- Fixed a crash when subscripting a vector type with large unsigned integer values. (#GH180563)
-- Fixed a crash when attempting to diagnose incompatible conversions involving function types (#GH182534)
-- Fixed a crash when evaluating ``__is_bitwise_cloneable`` on invalid record types. (#GH183707)
-- Fixed an assertion failure when casting a function pointer with a target with a non-default program address space. (#GH186210)
-- Fixed a crash when ``decltype(__builtin_FUNCTION())`` is used as a template type argument. (#GH167433)
-- Fixed an assertion failure when parsing an invalid ``decltype`` specifier with missing parentheses or extra semicolons. (#GH188014)
-- Fixed a crash when explicitly casting a complex type to or from an atomic complex type. (#GH172208)
-- Fixed a crash when explicitly casting a scalar to an atomic complex. (#GH114885)
-- Fixed an assertion failure when parsing an invalid out-of-line enum definition with template parameters. (#GH187909)
-- Fixed an assertion failure on invalid template template parameter during typo correction. (#GH183983)
-- Fixed an assertion failure in ``isAtEndOfMacroExpansion`` on macro expansions crossing the boundary of two fileIDs. (#GH115007), (#GH21755)
-
-OpenACC Specific Changes
-------------------------
-
-Target Specific Changes
------------------------
-
-AMDGPU Support
-^^^^^^^^^^^^^^
-- Introduced a new target specific builtin ``__builtin_amdgcn_processor_is``,
-  a late / deferred query for the current target processor.
-- Introduced a new target specific builtin ``__builtin_amdgcn_is_invocable``,
-  a late / deferred query for the availability of target specific builtins.
-- Initial support for gfx1310
-- The ``amdgpu_num_sgpr`` and ``amdgpu_num_vgpr`` function attributes are now
-  deprecated. Using them produces a ``-Wdeprecated-declarations`` warning. Use
-  ``amdgpu_waves_per_eu`` instead.
-
-NVPTX Support
-^^^^^^^^^^^^^^
-
-X86 Support
-^^^^^^^^^^^
-- ``march=znver6`` is now supported.
-
-Arm and AArch64 Support
-^^^^^^^^^^^^^^^^^^^^^^^
-
-Android Support
-^^^^^^^^^^^^^^^
-
-Windows Support
-^^^^^^^^^^^^^^^
-
-- Clang now defines the ``_MSVC_TRADITIONAL`` macro as ``1`` when emulating MSVC
-  19.15 (Visual Studio 2017 version 15.8) and later. (#GH47114)
-- ``-fmacro-prefix-map=`` (``-ffile-prefix-map=``) now affects an anonymous namespace hash generation
-  for the MSVC targets and allows deterministic symbol mangling for reproducible builds.
-
-LoongArch Support
-^^^^^^^^^^^^^^^^^
-
-- DWARF fission is now compatible with linker relaxations, allowing `-gsplit-dwarf` and `-mrelax`
-  to be used together when building for the LoongArch platform.
-
-RISC-V Support
-^^^^^^^^^^^^^^
-
-- Tenstorrent Ascalon D8 was renamed to Ascalon X. Use `tt-ascalon-x` with `-mcpu` or `-mtune`.
-- Intrinsics were added for the 'Zvabd` (RISC-V Integer Vector Absolute Difference) extension.
-- Intrinsics were added for the 'Zvzip` (Reordering Structured Data in Vector Registers) extension.
-
-CUDA/HIP Language Changes
-^^^^^^^^^^^^^^^^^^^^^^^^^
-
-- The new offloading driver is now the default for HIP. Use
-  `--no-oflfoad-new-driver` to return to the old behavior.
-
-CUDA Support
-^^^^^^^^^^^^
-
-AIX Support
-^^^^^^^^^^^
-
-- The driver default for the linker flag `-bcdtors` now defaults to `mbr`
-  (instead of `all`) which only extracts static init from archive members which
-  would otherwise be referenced.
-  (See https://www.ibm.com/docs/en/aix/7.2.0?topic=l-ld-command for details).
-
-NetBSD Support
-^^^^^^^^^^^^^^
-
-WebAssembly Support
-^^^^^^^^^^^^^^^^^^^
-
-- Fixed a crash when ``__funcref`` is applied to a non-function pointer type.
-  (#GH118233)
-
-AVR Support
-^^^^^^^^^^^
-
-SystemZ Support
-^^^^^^^^^^^^^^^
-
-- Add support for `#pragma export` for z/OS.  This is a pragma used to export functions and variables
-  with external linkage from shared libraries.  It provides compatibility with the IBM XL C/C++
-  compiler.
-
-DWARF Support in Clang
-----------------------
-
-Floating Point Support in Clang
--------------------------------
-
-Fixed Point Support in Clang
-----------------------------
-
-AST Matchers
-------------
-- Add ``functionTypeLoc`` matcher for matching ``FunctionTypeLoc``.
-- Add missing support for ``TraversalKind`` in some ``addMatcher()`` overloads.
-
-clang-format
-------------
-- Add ``ObjCSpaceAfterMethodDeclarationPrefix`` option to control space between the
-  '-'/'+' and the return type in Objective-C method declarations
-- Deprecate the ``BinPackParameters`` and ``BinPackArguments`` options and replace 
-  them with the ``PackParameters`` and ``PackArguments`` structs (respectively) to 
-  unify packing behavior. Add the ``BreakAfter`` option to the structs, allowing 
-  parameter and argument lists to be formatted with one parameter/argument on each 
-  line if they exceed the specified count.
-- Add ``AfterComma`` value to ``BreakConstructorInitializers`` to allow breaking
-  constructor initializers after commas, keeping the colon on the same line.
-- Extend ``BreakBinaryOperations`` to accept a structured configuration with
-  per-operator break rules and minimum chain length gating via ``PerOperator``.
-- Add ``AllowShortRecordOnASingleLine`` option and set it to ``EmptyAndAttached`` for LLVM style.
-- Add ``BreakFunctionDeclarationParameters`` option to always break before function
-  declaration parameters.
-- Add ``EnumAssignments`` option to ``AlignConsecutiveAssignments`` for aligning
-  enum assignments without affecting other assignments.
-- Add ``BreakBeforeReturnType`` option to break before the function return
-  type.
-
-libclang
---------
-- Visit constraints of `auto` type to properly visit concept usages (#GH166580)
-- Visit switch initializer statements (https://bugs.kde.org/show_bug.cgi?id=415537#c2)
-- Fix crash in clang_getBinaryOperatorKindSpelling and clang_getUnaryOperatorKindSpelling
-- The clang_Module_getASTFile API is deprecated and now always returns nullptr
-
-Code Completion
----------------
-
-- Fixed a crash in code completion when using a C-Style cast with a parenthesized
-  operand in Objective-C++ mode. (#GH180125)
-
-Static Analyzer
----------------
-
-Crash and bug fixes
-^^^^^^^^^^^^^^^^^^^
-
-- Fixed ``security.VAList`` checker producing false positives when analyzing
-  C23 code where ``va_start`` expands to ``__builtin_c23_va_start``.
-
-.. comment:
-  This is for the Static Analyzer.
-  Using the caret `^^^` underlining for subsections:
-    - Crash and bug fixes
-    - New checkers and features
-    - Improvements
-    - Moved checkers
-
-.. _release-notes-sanitizers:
-
-Sanitizers
-----------
-- UndefinedBehaviorSanitizer now supports ``__ubsan_default_suppressions``.
-
-Python Binding Changes
-----------------------
-- Add deprecation warnings to ``CompletionChunk.isKind...`` methods.
-  These will be removed in a future release. Existing uses should be adapted
-  to directly compare equality of the ``CompletionChunk`` kind with
-  the corresponding ``CompletionChunkKind`` variant.
-
-  Affected methods: ``isKindOptional``, ``isKindTypedText``, ``isKindPlaceHolder``,
-  ``isKindInformative`` and ``isKindResultType``.
-- Add a deprecation warning to ``CodeCompletionResults.results``.
-  This property will become an implementation detail with changed behavior in a
-  future release and should not be used directly.. Existing uses of
-  ``CodeCompletionResults.results`` should be changed to directly use
-  ``CodeCompletionResults``: it nows supports ``__len__`` and ``__getitem__``,
-  so it can be used the same as ``CodeCompletionResults.results``.
-- Added a new helper method ``get_clang_version`` to the class ``Config`` to
-  read the version string of the libclang in use.
-
-OpenMP Support
---------------
-- Added support for ``transparent`` clause in task and taskloop directives.
-- Added support for ``use_device_ptr`` clause to accept an optional
-  ``fallback`` modifier (``fb_nullify`` or ``fb_preserve``) with OpenMP >= 61.
-- Added support for ``local`` clause with declare_target directive when
-  OpenMP >= 60.
-
-Improvements
-^^^^^^^^^^^^
-- Improved substitution performance in concept checking. (#GH172266)
-
-- Clang now preserves the left-hand side of a binary expression (such as an
-  assignment or comparison) in a ``RecoveryExpr`` when the right-hand side fails
-  to parse. This improves IDE features like go-to-definition in ``clangd``.
-
-Additional Information
-======================
-
-A wide variety of additional information is available on the `Clang web
-page <https://clang.llvm.org/>`_. The web page contains versions of the
-API documentation which are up-to-date with the Git version of
-the source code. You can access versions of these documents specific to
-this release by going into the "``clang/docs/``" directory in the Clang
-tree.
-
-If you have any questions or comments about Clang, please feel free to
-contact us on the `Discourse forums (Clang Frontend category)
-<https://discourse.llvm.org/c/clang/6>`_.

>From 1528401164ef53719b97f15bc9ca71fb2e975fb4 Mon Sep 17 00:00:00 2001
From: freaknbigpanda <benjamin.luke at sony.com>
Date: Wed, 1 Jul 2026 11:58:32 -0700
Subject: [PATCH 12/12] fixed bad merge

---
 libcxx/include/__algorithm/simd_utils.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index 5b87f13e5bb01..495aed4e21216 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -125,7 +125,6 @@ template <class _VecT, size_t _Np, class _Iter>
     return _VecT{__iter[_LoadIndices]..., ((void)_ZeroIndices, 0)...};
   }(make_index_sequence<_Np>{}, make_index_sequence<__simd_vector_size_v<_VecT> - _Np>{});
 }
-_LIBCPP_DIAGNOSTIC_POP
 
 template <class _Tp, size_t _Np>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __any_of(__simd_vector<_Tp, _Np> __vec) noexcept {


From libcxx-commits at lists.llvm.org  Thu Jul  2 09:06:35 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 09:06:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Move the
 lower_bound/upper_bound benchmark to the sorting directory (PR #207215)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207215@github.com>
Message-ID: <6a468c8b.7d41f3a1.d1cf7.4101@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

These algorithms are not under [nonmodifying], they are under [sorting] in the Standard.

---
Full diff: https://github.com/llvm/llvm-project/pull/207215.diff


1 Files Affected:

- (renamed) libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp () 


``````````diff
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
similarity index 100%
rename from libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp
rename to libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp

``````````

</details>


https://github.com/llvm/llvm-project/pull/207215

From libcxx-commits at lists.llvm.org  Thu Jul  2 09:16:42 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Thu, 02 Jul 2026 09:16:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add assert test for string
	assign/append (PR #207164)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207164@github.com>
Message-ID: <6a468eea.331483e7.5bc1e.1f77@mx.google.com>


c8ef wrote:

Now the hardening tests passed. Not sure if my test options look good, IIUC the nullptr check does not work in the fast mode.

The generic-llvm-libc test failed because kernel.org release 404.

https://github.com/llvm/llvm-project/pull/207164

From libcxx-commits at lists.llvm.org  Thu Jul  2 09:21:31 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 09:21:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a46900b.1a4df1a5.118deb.b91c@mx.google.com>


================
@@ -63,14 +63,15 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted > 0) {
+  if (__converted < 0) [[__unlikely__]]
----------------
ldionne wrote:

It seems to make a minor difference: https://godbolt.org/z/dxhGzT6ME.

I used Claude to help me analyze the diff and it seems to mostly do two things:
1. For `copy_n` for input iterators, it moves the cold block to the tail of the function. Possible hot/cold improvement here for the common codepath.
2. For `fill_n(vector<bool>)`, it actually ends up removing a "shrink wrapping" optimization. I didn't know about this optimization and IDK if you do, so let me paste this here:

    > Here the function has a heavy prologue: 5 stp pairs spilling 10 callee-saved registers (x19–x26, x29/x30), because the body calls memset/bzero and needs lots of live registers.
    >
    >  Without [[unlikely]] — LLVM shrink-wraps: it hoists the n <= 0 test above the prologue:
    >
    >      cmp  x1, #0
    >      b.le LBB4_4          ; n <= 0: bail out before saving anything
    >      stp  x26, x25, [sp, #-80]!   ; prologue only runs when n > 0
    >      ...
    >      LBB4_4:                  ; cheap early return — no reg restores, no sp adjust
    >      ldr  x9, [x0]; str x9, [x8]; ldr w9, [x0,# 8]; str w9, [x8,# 8]
    >      ret
    >
    >  With [[unlikely]] — the shrink-wrap cost model sees the n <= 0 exit as cold, decides it's not worth it, and emits the full prologue first, then the check:
    >
    >      stp  x26, x25, [sp, #-80]!   ; save all 10 regs up front
    >      ... (4 more stp) ...
    >      add  x29, sp, #64
    >      mov  x21, x0
    >      mov  x19, x8
    >      cmp  x1, #0
    >      b.le LBB4_19         ; and this cold exit now has to *undo* the prologue:
    >      LBB4_19:
    >      ldr x8,[x21]; str x8,[x19]; ldr w8,[x21,# 8]
    >      b   LBB4_18          ; -> ldp x29/x30, x20/x19, x22/x21, x24/x23, x26/x25; ret
    >
    >  The knock-on effect is slightly different register allocation in the body (a few extra/rearranged movs saving values across the memset/bzero calls), because the two CFG shapes give the allocator different freedom.

Interestingly, we actually seem to pay more cost in the cold case for `fill_n`.

Bottom line: I'd definitely remove it in `fill_n(vector<bool>)`, and I'm neutral about removing it from the other places.

https://github.com/llvm/llvm-project/pull/207086

From libcxx-commits at lists.llvm.org  Thu Jul  2 09:27:32 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 09:27:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a469174.d5797644.146403.0b6f@mx.google.com>


================
@@ -63,14 +63,15 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted > 0) {
+  if (__converted < 0) [[__unlikely__]]
----------------
philnik777 wrote:

Unless there is a clear benefit I'd remove it.  This is the sort of check I'd expect to be removed in most cases anyways, and it looks to me like it's just as likely to hinder optimizations as it helps in the few cases it won't be removed.

https://github.com/llvm/llvm-project/pull/207086

From libcxx-commits at lists.llvm.org  Thu Jul  2 09:29:36 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 09:29:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a4691f0.eb24c6ea.1f2e26.deaa@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/207086

From libcxx-commits at lists.llvm.org  Thu Jul  2 09:29:39 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 09:29:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a4691f3.af6ba3ed.2c2f30.c587@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207086

>From 64b22d4e38f896e39762ccfd1f011f0dd16dfddb Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 29 Jun 2026 14:57:40 -0400
Subject: [PATCH 1/4] [libc++] Treat negative counts in copy_n & friends as
 no-ops

The standard specifies copy_n, fill_n and generate_n (both std and
ranges) to be no-ops when passed a negative count. This patch fixes
libc++ to abide by that requirement, with tests.

It's worth noting that std::for_each_n makes n > 0 a precondition
instead: we add the check and tests for it.

Fixes #193613
---
 libcxx/include/__algorithm/copy_n.h           | 17 +++++----
 libcxx/include/__algorithm/for_each_n.h       |  2 +
 libcxx/include/__algorithm/ranges_copy_n.h    |  2 +
 .../include/__algorithm/ranges_for_each_n.h   |  2 +
 libcxx/include/__bit_reference                | 17 +++++----
 .../alg.foreach/assert.for_each_n.pass.cpp    | 35 ++++++++++++++++++
 .../assert.ranges.for_each_n.pass.cpp         | 37 +++++++++++++++++++
 .../alg.copy/copy_n.pass.cpp                  | 16 ++++++++
 .../alg.copy/ranges.copy_n.pass.cpp           | 17 +++++++++
 .../alg.fill/fill_n.pass.cpp                  | 15 ++++++++
 .../alg.fill/ranges.fill_n.pass.cpp           | 15 ++++++++
 .../alg.generate/generate_n.pass.cpp          |  7 ++++
 .../alg.generate/ranges_generate_n.pass.cpp   | 15 +++++++-
 13 files changed, 181 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
 create mode 100644 libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index ac52b2b686c28..a0ea02121b70b 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -42,7 +42,7 @@ template <class _AlgPolicy,
           __enable_if_t<!__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
-  while (__n != 0) {
+  while (__n > 0) {
     *__result = *__first;
     ++__first;
     ++__result;
@@ -63,14 +63,15 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted > 0) {
+  if (__converted < 0) [[__unlikely__]]
+    return __result;
+
+  *__result = *__first;
+  ++__result;
+  for (--__converted; __converted > 0; --__converted) {
+    ++__first;
     *__result = *__first;
     ++__result;
-    for (--__converted; __converted > 0; --__converted) {
-      ++__first;
-      *__result = *__first;
-      ++__result;
-    }
   }
   return __result;
 }
@@ -83,6 +84,8 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
+  if (__converted < 0) [[__unlikely__]]
+    return __result;
   return std::__copy_n<_ClassicAlgPolicy>(__first, __iterator_difference_type<_InputIterator>(__converted), __result)
       .__out_;
 }
diff --git a/libcxx/include/__algorithm/for_each_n.h b/libcxx/include/__algorithm/for_each_n.h
index 7ff3f9b157c02..9b393b307f323 100644
--- a/libcxx/include/__algorithm/for_each_n.h
+++ b/libcxx/include/__algorithm/for_each_n.h
@@ -12,6 +12,7 @@
 
 #include <__algorithm/for_each.h>
 #include <__algorithm/for_each_n_segment.h>
+#include <__assert>
 #include <__config>
 #include <__functional/identity.h>
 #include <__iterator/iterator_traits.h>
@@ -63,6 +64,7 @@ __for_each_n(_InputIterator __first, _Size __orig_n, _Func&& __f, _Proj& __proj)
 template <class _InputIterator, class _Size, class _Func>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
 for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__orig_n >= 0, "for_each_n requires a non-negative count");
   __identity __proj;
   return std::__for_each_n(__first, __orig_n, __f, __proj);
 }
diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
index 1720ac358fa4d..c153e74009801 100644
--- a/libcxx/include/__algorithm/ranges_copy_n.h
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -38,6 +38,8 @@ struct __copy_n {
     requires indirectly_copyable<_Ip, _Op>
   _LIBCPP_HIDE_FROM_ABI constexpr copy_n_result<_Ip, _Op>
   operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const {
+    if (__n < 0) [[__unlikely__]]
+      return {std::move(__first), std::move(__result)};
     return std::__copy_n<_RangeAlgPolicy>(std::move(__first), __n, std::move(__result));
   }
 };
diff --git a/libcxx/include/__algorithm/ranges_for_each_n.h b/libcxx/include/__algorithm/ranges_for_each_n.h
index 3aab1b79c10a1..126dc481ec723 100644
--- a/libcxx/include/__algorithm/ranges_for_each_n.h
+++ b/libcxx/include/__algorithm/ranges_for_each_n.h
@@ -11,6 +11,7 @@
 
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/in_fun_result.h>
+#include <__assert>
 #include <__config>
 #include <__functional/identity.h>
 #include <__iterator/concepts.h>
@@ -40,6 +41,7 @@ struct __for_each_n {
   template <input_iterator _Iter, class _Proj = identity, indirectly_unary_invocable<projected<_Iter, _Proj>> _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr for_each_n_result<_Iter, _Func>
   operator()(_Iter __first, iter_difference_t<_Iter> __count, _Func __func, _Proj __proj = {}) const {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count >= 0, "for_each_n requires a non-negative count");
     auto __last = std::__for_each_n(std::move(__first), __count, __func, __proj);
     return {std::move(__last), std::move(__func)};
   }
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 692686f1584d0..0083714b2cafe 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -424,8 +424,8 @@ public:
   }
 
 #if _LIBCPP_STD_VER <= 17
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool
-  operator!=(const __bit_iterator& __x, const __bit_iterator& __y) {
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool operator!=(const __bit_iterator& __x, const __bit_iterator& __y) {
     return !(__x == __y);
   }
 
@@ -573,12 +573,13 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n > 0) {
-      if (__value)
-        __impl<true>(__first, __n);
-      else
-        __impl<false>(__first, __n);
-    }
+    if (__n < 0) [[__unlikely__]]
+      return __first;
+
+    if (__value)
+      __impl<true>(__first, __n);
+    else
+      __impl<false>(__first, __n);
     return __first + __n;
   }
 };
diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
new file mode 100644
index 0000000000000..53e59401430fa
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<class InputIterator, class Size, class Function>
+//    constexpr InputIterator
+//    for_each_n(InputIterator first, Size n, Function f);
+//
+// [alg.foreach] requires `n >= 0`; passing a negative count is a precondition violation.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <algorithm>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  int a[] = {1, 2, 3};
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp
new file mode 100644
index 0000000000000..e57f342e760a6
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<input_iterator I, class Proj = identity,
+//          indirectly_unary_invocable<projected<I, Proj>> Fun>
+//   constexpr ranges::for_each_n_result<I, Fun>
+//     ranges::for_each_n(I first, iter_difference_t<I> n, Fun f, Proj proj = {});
+//
+// [alg.foreach] requires `n >= 0`; passing a negative count is a precondition violation.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <algorithm>
+#include <array>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  std::array a = {1, 2, 3};
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::ranges::for_each_n(a.begin(), -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::ranges::for_each_n(a.begin(), -10000000, [](int) {}), "for_each_n requires a non-negative count");
+
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 183f38c1ce117..52d99f018110b 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -61,6 +61,15 @@ struct TestIterators {
       assert(base(r) == ib + N / 2);
       for (unsigned i = 0; i < N / 2; ++i)
         assert(ia[i] == ib[i]);
+
+      { // A negative count is a no-op that returns the unchanged output iterator.
+        // Regression test for https://llvm.org/PR193613.
+        int source[]  = {1, 2, 3};
+        int dest[]    = {-1, -2, -3};
+        OutIter ret   = std::copy_n(InIter(source), -5, OutIter(dest));
+        assert(base(ret) == dest);
+        assert(dest[0] == -1 && dest[1] == -2 && dest[2] == -3);
+      }
     }
   };
 };
@@ -81,6 +90,13 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
     for (std::size_t i = 0; i < N; ++i)
       assert(out[i + 4] == in[i]);
   }
+  { // Negative count test
+    std::vector<bool> source(N, true);
+    std::vector<bool> dest(N, false);
+    std::vector<bool>::iterator r = std::copy_n(source.begin(), -5, dest.begin());
+    assert(r == dest.begin());
+    assert(dest == std::vector<bool>(N, false));
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
index 132993dba76b9..5e073026ed8db 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
@@ -60,6 +60,16 @@ constexpr void test_iterators() {
     assert(base(ret.in) == in.data());
     assert(base(ret.out) == out.data());
   }
+
+  { // A negative count is a no-op that returns the unchanged iterators.
+    // Regression test for https://llvm.org/PR193613.
+    std::array in{1, 2, 3};
+    std::array out{-1, -2, -3};
+    std::same_as<std::ranges::in_out_result<In, Out>> auto ret = std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
+    assert(base(ret.in) == in.data());
+    assert(base(ret.out) == out.data());
+    assert((out == std::array{-1, -2, -3}));
+  }
 }
 
 #if TEST_STD_VER >= 23
@@ -79,6 +89,13 @@ constexpr bool test_vector_bool(std::size_t N) {
     for (std::size_t i = 0; i < N; ++i)
       assert(out[i + 4] == in[i]);
   }
+  { // Negative count test
+    std::vector<bool> source(N, true);
+    std::vector<bool> dest(N, false);
+    auto ret = std::ranges::copy_n(source.begin(), -5, dest.begin());
+    assert(ret.out == dest.begin());
+    assert(dest == std::vector<bool>(N, false));
+  }
 
   return true;
 };
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
index 6d7ba98a33b65..cc9aaeb04d7e8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
@@ -52,6 +52,14 @@ struct Test {
       std::array<T, 4> expected = {1, 5, 5, 4};
       test<Iter>(in, 1, 2, 5, expected);
     }
+    { // A negative count is a no-op that returns the unchanged iterator.
+      // Regression test for https://llvm.org/PR193613.
+      std::array<T, 4> in       = {1, 2, 3, 4};
+      std::array<T, 4> expected = {1, 2, 3, 4};
+      Iter it                   = std::fill_n(Iter(in.data()), -5, T(9));
+      assert(base(it) == in.data());
+      assert(in == expected);
+    }
   }
 };
 
@@ -126,6 +134,13 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
     }
   }
 
+  { // Negative count test
+    std::vector<bool> v(N, false);
+    std::vector<bool>::iterator r = std::fill_n(v.begin(), -5, true);
+    assert(r == v.begin());
+    assert(v == std::vector<bool>(N, false));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
index 0de6f4a625136..a8eb6c4eb9737 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
@@ -51,6 +51,14 @@ constexpr void test_iterators() {
     auto ret = std::ranges::fill_n(It(a.data()), 0, 1);
     assert(base(ret) == a.data());
   }
+
+  { // A negative count is a no-op that returns the unchanged iterator.
+    // Regression test for https://llvm.org/PR193613.
+    int a[3]                            = {-1, -2, -3};
+    std::same_as<It> decltype(auto) ret = std::ranges::fill_n(It(a), -5, 1);
+    assert(base(ret) == a);
+    assert(a[0] == -1 && a[1] == -2 && a[2] == -3);
+  }
 }
 
 // The `ranges::{fill, fill_n}` algorithms require `vector<bool, Alloc>::iterator` to satisfy
@@ -99,6 +107,13 @@ constexpr bool test_vector_bool(std::size_t N) {
     }
   }
 
+  { // Negative count test
+    std::vector<bool> v(N, false);
+    auto r = std::ranges::fill_n(v.begin(), -5, true);
+    assert(r == v.begin());
+    assert(v == std::vector<bool>(N, false));
+  }
+
   return true;
 }
 #endif
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
index 525737ceacfbf..0e38a89613aa8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
@@ -70,6 +70,13 @@ test()
     test2<Iter, float>();
     test2<Iter, double>();  // this is PR#35498
     test2<Iter, long double>();
+
+    { // A negative count is a no-op that returns the unchanged iterator.
+      // Regression test for https://llvm.org/PR193613.
+      int ia[] = {1, 2, 3};
+      assert(std::generate_n(Iter(ia), -5, gen_test()) == Iter(ia));
+      assert(ia[0] == 1 && ia[1] == 2 && ia[2] == 3);
+    }
 }
 
 void deque_test() {
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
index 33d40a39cf385..6154ab240c3d6 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
@@ -92,6 +92,20 @@ constexpr void test_iter() {
   test_one<Iter>(std::array{-10, -20, -30, -40, -50}, 3, gen, {1, 2, 3, -40, -50});
   // Longer sequence, n = 5.
   test_one<Iter>(std::array<int, 5>{}, 5, gen, {1, 2, 3, 4, 5});
+
+  { // A negative count is a no-op that returns the unchanged iterator.
+    // Regression test for https://llvm.org/PR193613.
+    std::array in{-10, -20, -30};
+    int called        = 0;
+    auto counting_gen = [&called] {
+      ++called;
+      return 42;
+    };
+    std::same_as<Iter> decltype(auto) result = std::ranges::generate_n(Iter(in.data()), -5, counting_gen);
+    assert(base(result) == in.data());
+    assert(called == 0);
+    assert((in == std::array{-10, -20, -30}));
+  }
 }
 
 constexpr void test_iterators() {
@@ -131,7 +145,6 @@ constexpr bool test() {
     assert(gen_invocations == N2);
   }
 
-
   return true;
 }
 

>From 892279c90e066c4af5e0ef600fe47df0ba9dbf9e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 17:31:39 -0400
Subject: [PATCH 2/4] Fix check

---
 libcxx/include/__bit_reference | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 0083714b2cafe..b07c7ddccd5a0 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -573,7 +573,7 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n < 0) [[__unlikely__]]
+    if (__n <= 0) [[__unlikely__]]
       return __first;
 
     if (__value)

>From 87de9ba731d496c0900154847a077591dde9c906 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 17:36:19 -0400
Subject: [PATCH 3/4] Revert to != 0

---
 libcxx/include/__algorithm/copy_n.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index a0ea02121b70b..7ce0ec53b0c53 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -42,7 +42,7 @@ template <class _AlgPolicy,
           __enable_if_t<!__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
-  while (__n > 0) {
+  while (__n != 0) {
     *__result = *__first;
     ++__first;
     ++__result;

>From 86c6915b6d53b0ffde7b3a2ccb197b05f28c85ee Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 12:28:02 -0400
Subject: [PATCH 4/4] Drop [[__unlikely__]]

---
 libcxx/include/__algorithm/copy_n.h        | 4 ++--
 libcxx/include/__algorithm/ranges_copy_n.h | 2 +-
 libcxx/include/__bit_reference             | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index 7ce0ec53b0c53..7f9a109a763ce 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -63,7 +63,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0) [[__unlikely__]]
+  if (__converted < 0)
     return __result;
 
   *__result = *__first;
@@ -84,7 +84,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0) [[__unlikely__]]
+  if (__converted < 0)
     return __result;
   return std::__copy_n<_ClassicAlgPolicy>(__first, __iterator_difference_type<_InputIterator>(__converted), __result)
       .__out_;
diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
index c153e74009801..7baf1932bb3d8 100644
--- a/libcxx/include/__algorithm/ranges_copy_n.h
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -38,7 +38,7 @@ struct __copy_n {
     requires indirectly_copyable<_Ip, _Op>
   _LIBCPP_HIDE_FROM_ABI constexpr copy_n_result<_Ip, _Op>
   operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const {
-    if (__n < 0) [[__unlikely__]]
+    if (__n < 0)
       return {std::move(__first), std::move(__result)};
     return std::__copy_n<_RangeAlgPolicy>(std::move(__first), __n, std::move(__result));
   }
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index b07c7ddccd5a0..23ff6eaf56684 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -573,7 +573,7 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n <= 0) [[__unlikely__]]
+    if (__n <= 0)
       return __first;
 
     if (__value)


From libcxx-commits at lists.llvm.org  Thu Jul  2 09:31:51 2026
From: libcxx-commits at lists.llvm.org (David Meng via libcxx-commits)
Date: Thu, 02 Jul 2026 09:31:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Apply the predicate exactly N
 times in stable_partition (PR #203949)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/203949@github.com>
Message-ID: <6a469277.c56fb102.11841f.d0f6@mx.google.com>


https://github.com/davidmenggx updated https://github.com/llvm/llvm-project/pull/203949

>From 8c42de6e2a3ad0e86577965e1f9fde96875663c4 Mon Sep 17 00:00:00 2001
From: David Meng <davidmenggx at gmail.com>
Date: Mon, 15 Jun 2026 09:58:09 -0700
Subject: [PATCH 1/8] [libc++] Apply the predicate exactly N times in
 stable_partition

[alg.partitions] requires stable_partition to apply the predicate exactly
N = last - first times. On the in-place path (taken when no temporary buffer
can be allocated), the bidirectional implementation exceeded this.

The first-half backward scan evaluated the predicate in its loop condition
(`while (!__pred(*--__m1))`) before checking whether it had reached `__first`.
When the left sub-range is entirely false, the scan walks down to `__first`
and re-applies the predicate to `*__first`, an element already known to be
false.

Fixes #203643
---
 libcxx/include/__algorithm/stable_partition.h |   6 +-
 ...anges_stable_partition.complexity.pass.cpp | 165 ++++++++++++++++++
 .../stable_partition.complexity.pass.cpp      | 144 +++++++++++++++
 3 files changed, 313 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp

diff --git a/libcxx/include/__algorithm/stable_partition.h b/libcxx/include/__algorithm/stable_partition.h
index b389ae2508c6e..e0955e78bbb40 100644
--- a/libcxx/include/__algorithm/stable_partition.h
+++ b/libcxx/include/__algorithm/stable_partition.h
@@ -215,9 +215,11 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 _BidirectionalIterator __stable_partition_impl(
   _BidirectionalIterator __m1          = __m;
   _BidirectionalIterator __first_false = __first;
   _Distance __len_half                 = __len2;
-  while (!__pred(*--__m1)) {
-    if (__m1 == __first)
+  while (true) {
+    if (--__m1 == __first)
       goto __first_half_done;
+    if (__pred(*__m1))
+      break;
     --__len_half;
   }
   // F???TFFF?????????T
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
new file mode 100644
index 0000000000000..850e01e447a1a
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
@@ -0,0 +1,165 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <algorithm>
+
+// template<bidirectional_iterator I, sentinel_for<I> S, class Proj = identity,
+//          indirect_unary_predicate<projected<I, Proj>> Pred>
+//   requires permutable<I>
+//   constexpr subrange<I>                                                         // constexpr since C++26
+//     stable_partition(I first, S last, Pred pred, Proj proj = {});               // Since C++20
+//
+// template<bidirectional_range R, class Proj = identity,
+//          indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
+//   requires permutable<iterator_t<R>>
+//   constexpr borrowed_subrange_t<R>                                              // constexpr since C++26
+//     stable_partition(R&& r, Pred pred, Proj proj = {});                         // Since C++20
+
+// [alg.partitions] requires stable_partition (without ExecutionPolicy) to apply the
+// predicate exactly N = last - first times.
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <new>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// Forces stable_partition onto its in-place (no temporary buffer) path. libc++
+// allocates the buffer with ::operator new(..., nothrow), so returning nullptr
+// makes the allocation fail. pair<int, int> is not over-aligned, so only the
+// plain nothrow operator new needs to be replaced.
+static bool g_nothrow_new_fails = false;
+
+void* operator new(std::size_t n, const std::nothrow_t&) TEST_NOEXCEPT {
+  if (g_nothrow_new_fails)
+    return nullptr;
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  try {
+    return ::operator new(n);
+  } catch (...) {
+    return nullptr;
+  }
+#else
+  return ::operator new(n);
+#endif
+}
+
+void operator delete(void* p, const std::nothrow_t&) TEST_NOEXCEPT { ::operator delete(p); }
+
+typedef std::pair<int, int> P; // (value, original index)
+
+// stable_partition takes its predicate by value, so the count must
+// exist outside the predicate object.
+struct counting_proj {
+  int* count_;
+  int operator()(const P& p) const {
+    ++*count_;
+    return p.first;
+  }
+};
+
+struct counting_is_even {
+  int* count_;
+  bool operator()(int v) const {
+    ++*count_;
+    return (v % 2) == 0;
+  }
+};
+
+// Generate expected stable partition result for sanity check.
+static std::vector<P> stable_reference(const std::vector<P>& in) {
+  std::vector<P> out;
+  out.reserve(in.size());
+  for (std::size_t i = 0; i < in.size(); ++i)
+    if (in[i].first % 2 == 0)
+      out.push_back(in[i]);
+  for (std::size_t i = 0; i < in.size(); ++i)
+    if (in[i].first % 2 != 0)
+      out.push_back(in[i]);
+  return out;
+}
+
+// General case: deterministic pseudo-random keys.
+static std::vector<P> make_shuffled(std::size_t n) {
+  std::vector<P> data;
+  data.reserve(n);
+  std::uint64_t s = 0x10110010111001ULL;
+  for (std::size_t i = 0; i < n; ++i) {
+    s ^= s << 13;
+    s ^= s >> 7;
+    s ^= s << 17;
+    data.push_back(P(static_cast<int>(s & 0xFF), static_cast<int>(i)));
+  }
+  return data;
+}
+
+// Worst case: every key is odd (predicate false) except the last, which is even
+// (predicate true). An implementation that evaluates the predicate before checking
+// for __first re-applies it to *__first, an element already known to be false,
+// once per node along the right segment, exceeding the required N applications.
+static std::vector<P> make_all_false_but_last(std::size_t n) {
+  std::vector<P> data;
+  data.reserve(n);
+  for (std::size_t i = 0; i + 1 < n; ++i)
+    data.push_back(P(1, static_cast<int>(i)));
+  if (n > 0)
+    data.push_back(P(2, static_cast<int>(n - 1)));
+  return data;
+}
+
+template <class Iter>
+void run_case(std::vector<P> data) {
+  const std::size_t n           = data.size();
+  const std::vector<P> expected = stable_reference(data);
+
+  int pred_count        = 0;
+  int proj_count        = 0;
+  counting_is_even pred = {&pred_count};
+  counting_proj proj    = {&proj_count};
+
+  g_nothrow_new_fails = true;
+  auto r              = std::ranges::stable_partition(Iter(data.data()), Iter(data.data() + n), pred, proj);
+  g_nothrow_new_fails = false;
+
+  // [alg.partitions]: exactly N applications of the predicate and projection.
+  assert(static_cast<std::size_t>(pred_count) == n);
+  assert(static_cast<std::size_t>(proj_count) == n);
+
+  // Sanity check that the result is correctly and stably partitioned, and the returned iterator
+  // points at the first element for which the predicate is false.
+  assert(data == expected);
+  std::size_t num_true = 0;
+  while (num_true < n && data[num_true].first % 2 == 0)
+    ++num_true;
+  assert(base(r.begin()) == data.data() + num_true);
+  assert(base(r.end()) == data.data() + n);
+}
+
+template <class Iter>
+void test() {
+  const std::size_t sizes[] = {1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 17, 63, 64, 65, 256, 1000, 4096};
+  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i) {
+    run_case<Iter>(make_shuffled(sizes[i]));
+    run_case<Iter>(make_all_false_but_last(sizes[i]));
+  }
+}
+
+int main(int, char**) {
+  test<P*>();
+  test<bidirectional_iterator<P*> >();
+  test<random_access_iterator<P*> >();
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
new file mode 100644
index 0000000000000..0988b3ef0bb04
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
@@ -0,0 +1,144 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<BidirectionalIterator Iter, Predicate<auto, Iter::value_type> Pred>
+//   requires ShuffleIterator<Iter>
+//         && CopyConstructible<Pred>
+//   constexpr Iter                                                               // constexpr since C++26
+//   stable_partition(Iter first, Iter last, Pred pred);
+
+// [alg.partitions] requires stable_partition (without ExecutionPolicy) to apply the
+// predicate exactly N = last - first times.
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// Forces stable_partition in-place (no temporary buffer). libc++ allocates
+// the buffer with ::operator new(..., nothrow), so returning nullptr from
+// it makes the allocation fail. pair<int, int> is not over-aligned, so only
+// the plain nothrow operator new needs to be replaced.
+static bool g_nothrow_new_fails = false;
+
+void* operator new(std::size_t n, const std::nothrow_t&) TEST_NOEXCEPT {
+  if (g_nothrow_new_fails)
+    return nullptr;
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  try {
+    return ::operator new(n);
+  } catch (...) {
+    return nullptr;
+  }
+#else
+  return ::operator new(n);
+#endif
+}
+
+void operator delete(void* p, const std::nothrow_t&) TEST_NOEXCEPT { ::operator delete(p); }
+
+typedef std::pair<int, int> P; // (value, original index)
+
+// stable_partition takes its predicate by value, so the count must
+// exist outside the predicate object.
+struct counting_is_even {
+  int* count_;
+  bool operator()(const P& p) const {
+    ++*count_;
+    return (p.first % 2) == 0;
+  }
+};
+
+// Generate expected stable partition result for sanity check.
+static std::vector<P> stable_reference(const std::vector<P>& in) {
+  std::vector<P> out;
+  out.reserve(in.size());
+  for (std::size_t i = 0; i < in.size(); ++i)
+    if (in[i].first % 2 == 0)
+      out.push_back(in[i]);
+  for (std::size_t i = 0; i < in.size(); ++i)
+    if (in[i].first % 2 != 0)
+      out.push_back(in[i]);
+  return out;
+}
+
+// General case: deterministic pseudo-random keys.
+static std::vector<P> make_shuffled(std::size_t n) {
+  std::vector<P> data;
+  data.reserve(n);
+  std::uint64_t s = 0x10110010111001ULL;
+  for (std::size_t i = 0; i < n; ++i) {
+    s ^= s << 13;
+    s ^= s >> 7;
+    s ^= s << 17;
+    data.push_back(P(static_cast<int>(s & 0xFF), static_cast<int>(i)));
+  }
+  return data;
+}
+
+// Worst case: every key is odd (predicate false) except the last, which is even
+// (predicate true). An implementation that evaluates the predicate before checking
+// for __first re-applies it to *__first, an element already known to be false,
+// once per node along the right segment, exceeding the required N applications.
+static std::vector<P> make_all_false_but_last(std::size_t n) {
+  std::vector<P> data;
+  data.reserve(n);
+  for (std::size_t i = 0; i + 1 < n; ++i)
+    data.push_back(P(1, static_cast<int>(i)));
+  if (n > 0)
+    data.push_back(P(2, static_cast<int>(n - 1)));
+  return data;
+}
+
+template <class Iter>
+void run_case(std::vector<P> data) {
+  const std::size_t n           = data.size();
+  const std::vector<P> expected = stable_reference(data);
+
+  int count             = 0;
+  counting_is_even pred = {&count};
+
+  g_nothrow_new_fails = true;
+  Iter r              = std::stable_partition(Iter(data.data()), Iter(data.data() + n), pred);
+  g_nothrow_new_fails = false;
+
+  // [alg.partitions]: exactly N applications of the predicate.
+  assert(static_cast<std::size_t>(count) == n);
+
+  // Sanity check that the result is correctly and stably partitioned, and the returned iterator
+  // points at the first element for which the predicate is false.
+  assert(data == expected);
+  std::size_t num_true = 0;
+  while (num_true < n && data[num_true].first % 2 == 0)
+    ++num_true;
+  assert(base(r) == data.data() + num_true);
+}
+
+template <class Iter>
+void test() {
+  const std::size_t sizes[] = {1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 17, 63, 64, 65, 256, 1000, 4096};
+  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i) {
+    run_case<Iter>(make_shuffled(sizes[i]));
+    run_case<Iter>(make_all_false_but_last(sizes[i]));
+  }
+}
+
+int main(int, char**) {
+  test<P*>();
+  test<bidirectional_iterator<P*> >();
+  test<random_access_iterator<P*> >();
+  return 0;
+}

>From 547ebc49ae2bf99956531bdca80857a3396fd937 Mon Sep 17 00:00:00 2001
From: David Meng <davidmenggx at gmail.com>
Date: Wed, 17 Jun 2026 08:03:23 -0700
Subject: [PATCH 2/8] Expected failure for cxx03

---
 .../alg.partitions/ranges_stable_partition.complexity.pass.cpp  | 2 ++
 .../alg.partitions/stable_partition.complexity.pass.cpp         | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
index 850e01e447a1a..dc7bd8b8acf47 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
@@ -8,6 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 // <algorithm>
 
 // template<bidirectional_iterator I, sentinel_for<I> S, class Proj = identity,
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
index 0988b3ef0bb04..a844fa9231a4b 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
@@ -6,6 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 // <algorithm>
 
 // template<BidirectionalIterator Iter, Predicate<auto, Iter::value_type> Pred>

>From c36e0bb2b172085ae30f133b3717aa273637bfdc Mon Sep 17 00:00:00 2001
From: David Meng <davidmenggx at gmail.com>
Date: Wed, 17 Jun 2026 10:19:50 -0700
Subject: [PATCH 3/8] Do not support sanitizer new-delete

---
 .../alg.partitions/ranges_stable_partition.complexity.pass.cpp | 3 +--
 .../alg.partitions/stable_partition.complexity.pass.cpp        | 1 +
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
index dc7bd8b8acf47..023743b49f5fa 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
@@ -7,8 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+// UNSUPPORTED: sanitizer-new-delete
 
 // <algorithm>
 
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
index a844fa9231a4b..c93d05f53d338 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// UNSUPPORTED: sanitizer-new-delete
 // XFAIL: FROZEN-CXX03-HEADERS-FIXME
 
 // <algorithm>

>From 9a7a62f2ffeb280b9067d4e11ce078fbbb88cfae Mon Sep 17 00:00:00 2001
From: David Meng <davidmenggx at gmail.com>
Date: Thu, 18 Jun 2026 13:47:36 -0700
Subject: [PATCH 4/8] Simplify test case

---
 ...anges_stable_partition.complexity.pass.cpp | 83 ++++---------------
 .../stable_partition.complexity.pass.cpp      | 82 ++++--------------
 2 files changed, 32 insertions(+), 133 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
index 023743b49f5fa..df5dcc4afbb27 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
@@ -29,7 +29,6 @@
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
-#include <cstdint>
 #include <new>
 #include <ranges>
 #include <utility>
@@ -45,19 +44,8 @@
 static bool g_nothrow_new_fails = false;
 
 void* operator new(std::size_t n, const std::nothrow_t&) TEST_NOEXCEPT {
-  if (g_nothrow_new_fails)
-    return nullptr;
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  try {
-    return ::operator new(n);
-  } catch (...) {
-    return nullptr;
-  }
-#else
-  return ::operator new(n);
-#endif
+  return g_nothrow_new_fails ? nullptr : ::operator new(n);
 }
-
 void operator delete(void* p, const std::nothrow_t&) TEST_NOEXCEPT { ::operator delete(p); }
 
 typedef std::pair<int, int> P; // (value, original index)
@@ -71,7 +59,6 @@ struct counting_proj {
     return p.first;
   }
 };
-
 struct counting_is_even {
   int* count_;
   bool operator()(int v) const {
@@ -80,51 +67,17 @@ struct counting_is_even {
   }
 };
 
-// Generate expected stable partition result for sanity check.
-static std::vector<P> stable_reference(const std::vector<P>& in) {
-  std::vector<P> out;
-  out.reserve(in.size());
-  for (std::size_t i = 0; i < in.size(); ++i)
-    if (in[i].first % 2 == 0)
-      out.push_back(in[i]);
-  for (std::size_t i = 0; i < in.size(); ++i)
-    if (in[i].first % 2 != 0)
-      out.push_back(in[i]);
-  return out;
-}
-
-// General case: deterministic pseudo-random keys.
-static std::vector<P> make_shuffled(std::size_t n) {
-  std::vector<P> data;
-  data.reserve(n);
-  std::uint64_t s = 0x10110010111001ULL;
-  for (std::size_t i = 0; i < n; ++i) {
-    s ^= s << 13;
-    s ^= s >> 7;
-    s ^= s << 17;
-    data.push_back(P(static_cast<int>(s & 0xFF), static_cast<int>(i)));
-  }
-  return data;
-}
-
-// Worst case: every key is odd (predicate false) except the last, which is even
-// (predicate true). An implementation that evaluates the predicate before checking
-// for __first re-applies it to *__first, an element already known to be false,
-// once per node along the right segment, exceeding the required N applications.
-static std::vector<P> make_all_false_but_last(std::size_t n) {
+template <class Iter>
+void test(std::size_t n) {
+  // Worst case: every key is odd (predicate false) except the last, which is even
+  // (predicate true). An implementation that evaluates the predicate before checking
+  // for __first re-applies it to *__first, an element already known to be false,
+  // once per node along the right segment, exceeding the required N applications.
   std::vector<P> data;
   data.reserve(n);
   for (std::size_t i = 0; i + 1 < n; ++i)
     data.push_back(P(1, static_cast<int>(i)));
-  if (n > 0)
-    data.push_back(P(2, static_cast<int>(n - 1)));
-  return data;
-}
-
-template <class Iter>
-void run_case(std::vector<P> data) {
-  const std::size_t n           = data.size();
-  const std::vector<P> expected = stable_reference(data);
+  data.push_back(P(2, static_cast<int>(n - 1)));
 
   int pred_count        = 0;
   int proj_count        = 0;
@@ -139,23 +92,19 @@ void run_case(std::vector<P> data) {
   assert(static_cast<std::size_t>(pred_count) == n);
   assert(static_cast<std::size_t>(proj_count) == n);
 
-  // Sanity check that the result is correctly and stably partitioned, and the returned iterator
-  // points at the first element for which the predicate is false.
-  assert(data == expected);
-  std::size_t num_true = 0;
-  while (num_true < n && data[num_true].first % 2 == 0)
-    ++num_true;
-  assert(base(r.begin()) == data.data() + num_true);
+  // Sanity check that the result is correctly and stably partitioned.
+  assert(base(r.begin()) == data.data() + 1);
   assert(base(r.end()) == data.data() + n);
+  assert(data[0].first % 2 == 0);
+  for (std::size_t i = 1; i < n; ++i)
+    assert(data[i].first % 2 != 0 && data[i].second == static_cast<int>(i - 1));
 }
 
 template <class Iter>
 void test() {
-  const std::size_t sizes[] = {1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 17, 63, 64, 65, 256, 1000, 4096};
-  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i) {
-    run_case<Iter>(make_shuffled(sizes[i]));
-    run_case<Iter>(make_all_false_but_last(sizes[i]));
-  }
+  const std::size_t sizes[] = {1, 2, 3, 4, 5, 16, 17, 1000};
+  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i)
+    test<Iter>(sizes[i]);
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
index c93d05f53d338..a7745791dafa0 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
@@ -23,7 +23,6 @@
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
-#include <cstdint>
 #include <new>
 #include <utility>
 #include <vector>
@@ -38,19 +37,8 @@
 static bool g_nothrow_new_fails = false;
 
 void* operator new(std::size_t n, const std::nothrow_t&) TEST_NOEXCEPT {
-  if (g_nothrow_new_fails)
-    return nullptr;
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  try {
-    return ::operator new(n);
-  } catch (...) {
-    return nullptr;
-  }
-#else
-  return ::operator new(n);
-#endif
+  return g_nothrow_new_fails ? nullptr : ::operator new(n);
 }
-
 void operator delete(void* p, const std::nothrow_t&) TEST_NOEXCEPT { ::operator delete(p); }
 
 typedef std::pair<int, int> P; // (value, original index)
@@ -65,51 +53,17 @@ struct counting_is_even {
   }
 };
 
-// Generate expected stable partition result for sanity check.
-static std::vector<P> stable_reference(const std::vector<P>& in) {
-  std::vector<P> out;
-  out.reserve(in.size());
-  for (std::size_t i = 0; i < in.size(); ++i)
-    if (in[i].first % 2 == 0)
-      out.push_back(in[i]);
-  for (std::size_t i = 0; i < in.size(); ++i)
-    if (in[i].first % 2 != 0)
-      out.push_back(in[i]);
-  return out;
-}
-
-// General case: deterministic pseudo-random keys.
-static std::vector<P> make_shuffled(std::size_t n) {
-  std::vector<P> data;
-  data.reserve(n);
-  std::uint64_t s = 0x10110010111001ULL;
-  for (std::size_t i = 0; i < n; ++i) {
-    s ^= s << 13;
-    s ^= s >> 7;
-    s ^= s << 17;
-    data.push_back(P(static_cast<int>(s & 0xFF), static_cast<int>(i)));
-  }
-  return data;
-}
-
-// Worst case: every key is odd (predicate false) except the last, which is even
-// (predicate true). An implementation that evaluates the predicate before checking
-// for __first re-applies it to *__first, an element already known to be false,
-// once per node along the right segment, exceeding the required N applications.
-static std::vector<P> make_all_false_but_last(std::size_t n) {
+template <class Iter>
+void test(std::size_t n) {
+  // Worst case: every key is odd (predicate false) except the last, which is even
+  // (predicate true). An implementation that evaluates the predicate before checking
+  // for __first re-applies it to *__first, an element already known to be false,
+  // once per node along the right segment, exceeding the required N applications.
   std::vector<P> data;
   data.reserve(n);
   for (std::size_t i = 0; i + 1 < n; ++i)
     data.push_back(P(1, static_cast<int>(i)));
-  if (n > 0)
-    data.push_back(P(2, static_cast<int>(n - 1)));
-  return data;
-}
-
-template <class Iter>
-void run_case(std::vector<P> data) {
-  const std::size_t n           = data.size();
-  const std::vector<P> expected = stable_reference(data);
+  data.push_back(P(2, static_cast<int>(n - 1)));
 
   int count             = 0;
   counting_is_even pred = {&count};
@@ -121,22 +75,18 @@ void run_case(std::vector<P> data) {
   // [alg.partitions]: exactly N applications of the predicate.
   assert(static_cast<std::size_t>(count) == n);
 
-  // Sanity check that the result is correctly and stably partitioned, and the returned iterator
-  // points at the first element for which the predicate is false.
-  assert(data == expected);
-  std::size_t num_true = 0;
-  while (num_true < n && data[num_true].first % 2 == 0)
-    ++num_true;
-  assert(base(r) == data.data() + num_true);
+  // Sanity check that the result is correctly and stably partitioned.
+  assert(base(r) == data.data() + 1);
+  assert(data[0].first % 2 == 0);
+  for (std::size_t i = 1; i < n; ++i)
+    assert(data[i].first % 2 != 0 && data[i].second == static_cast<int>(i - 1));
 }
 
 template <class Iter>
 void test() {
-  const std::size_t sizes[] = {1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 17, 63, 64, 65, 256, 1000, 4096};
-  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i) {
-    run_case<Iter>(make_shuffled(sizes[i]));
-    run_case<Iter>(make_all_false_but_last(sizes[i]));
-  }
+  const std::size_t sizes[] = {1, 2, 3, 4, 5, 16, 17, 1000};
+  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i)
+    test<Iter>(sizes[i]);
 }
 
 int main(int, char**) {

>From c763a5f0dfc683e4c8a8859e4405a7b67ca2c526 Mon Sep 17 00:00:00 2001
From: David Meng <davidmenggx at gmail.com>
Date: Wed, 24 Jun 2026 17:59:51 -0700
Subject: [PATCH 5/8] Revert "Simplify test case"

This reverts commit 9a7a62f2ffeb280b9067d4e11ce078fbbb88cfae.
---
 ...anges_stable_partition.complexity.pass.cpp | 83 +++++++++++++++----
 .../stable_partition.complexity.pass.cpp      | 82 ++++++++++++++----
 2 files changed, 133 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
index df5dcc4afbb27..023743b49f5fa 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
@@ -29,6 +29,7 @@
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
+#include <cstdint>
 #include <new>
 #include <ranges>
 #include <utility>
@@ -44,8 +45,19 @@
 static bool g_nothrow_new_fails = false;
 
 void* operator new(std::size_t n, const std::nothrow_t&) TEST_NOEXCEPT {
-  return g_nothrow_new_fails ? nullptr : ::operator new(n);
+  if (g_nothrow_new_fails)
+    return nullptr;
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  try {
+    return ::operator new(n);
+  } catch (...) {
+    return nullptr;
+  }
+#else
+  return ::operator new(n);
+#endif
 }
+
 void operator delete(void* p, const std::nothrow_t&) TEST_NOEXCEPT { ::operator delete(p); }
 
 typedef std::pair<int, int> P; // (value, original index)
@@ -59,6 +71,7 @@ struct counting_proj {
     return p.first;
   }
 };
+
 struct counting_is_even {
   int* count_;
   bool operator()(int v) const {
@@ -67,17 +80,51 @@ struct counting_is_even {
   }
 };
 
-template <class Iter>
-void test(std::size_t n) {
-  // Worst case: every key is odd (predicate false) except the last, which is even
-  // (predicate true). An implementation that evaluates the predicate before checking
-  // for __first re-applies it to *__first, an element already known to be false,
-  // once per node along the right segment, exceeding the required N applications.
+// Generate expected stable partition result for sanity check.
+static std::vector<P> stable_reference(const std::vector<P>& in) {
+  std::vector<P> out;
+  out.reserve(in.size());
+  for (std::size_t i = 0; i < in.size(); ++i)
+    if (in[i].first % 2 == 0)
+      out.push_back(in[i]);
+  for (std::size_t i = 0; i < in.size(); ++i)
+    if (in[i].first % 2 != 0)
+      out.push_back(in[i]);
+  return out;
+}
+
+// General case: deterministic pseudo-random keys.
+static std::vector<P> make_shuffled(std::size_t n) {
+  std::vector<P> data;
+  data.reserve(n);
+  std::uint64_t s = 0x10110010111001ULL;
+  for (std::size_t i = 0; i < n; ++i) {
+    s ^= s << 13;
+    s ^= s >> 7;
+    s ^= s << 17;
+    data.push_back(P(static_cast<int>(s & 0xFF), static_cast<int>(i)));
+  }
+  return data;
+}
+
+// Worst case: every key is odd (predicate false) except the last, which is even
+// (predicate true). An implementation that evaluates the predicate before checking
+// for __first re-applies it to *__first, an element already known to be false,
+// once per node along the right segment, exceeding the required N applications.
+static std::vector<P> make_all_false_but_last(std::size_t n) {
   std::vector<P> data;
   data.reserve(n);
   for (std::size_t i = 0; i + 1 < n; ++i)
     data.push_back(P(1, static_cast<int>(i)));
-  data.push_back(P(2, static_cast<int>(n - 1)));
+  if (n > 0)
+    data.push_back(P(2, static_cast<int>(n - 1)));
+  return data;
+}
+
+template <class Iter>
+void run_case(std::vector<P> data) {
+  const std::size_t n           = data.size();
+  const std::vector<P> expected = stable_reference(data);
 
   int pred_count        = 0;
   int proj_count        = 0;
@@ -92,19 +139,23 @@ void test(std::size_t n) {
   assert(static_cast<std::size_t>(pred_count) == n);
   assert(static_cast<std::size_t>(proj_count) == n);
 
-  // Sanity check that the result is correctly and stably partitioned.
-  assert(base(r.begin()) == data.data() + 1);
+  // Sanity check that the result is correctly and stably partitioned, and the returned iterator
+  // points at the first element for which the predicate is false.
+  assert(data == expected);
+  std::size_t num_true = 0;
+  while (num_true < n && data[num_true].first % 2 == 0)
+    ++num_true;
+  assert(base(r.begin()) == data.data() + num_true);
   assert(base(r.end()) == data.data() + n);
-  assert(data[0].first % 2 == 0);
-  for (std::size_t i = 1; i < n; ++i)
-    assert(data[i].first % 2 != 0 && data[i].second == static_cast<int>(i - 1));
 }
 
 template <class Iter>
 void test() {
-  const std::size_t sizes[] = {1, 2, 3, 4, 5, 16, 17, 1000};
-  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i)
-    test<Iter>(sizes[i]);
+  const std::size_t sizes[] = {1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 17, 63, 64, 65, 256, 1000, 4096};
+  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i) {
+    run_case<Iter>(make_shuffled(sizes[i]));
+    run_case<Iter>(make_all_false_but_last(sizes[i]));
+  }
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
index a7745791dafa0..c93d05f53d338 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
@@ -23,6 +23,7 @@
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
+#include <cstdint>
 #include <new>
 #include <utility>
 #include <vector>
@@ -37,8 +38,19 @@
 static bool g_nothrow_new_fails = false;
 
 void* operator new(std::size_t n, const std::nothrow_t&) TEST_NOEXCEPT {
-  return g_nothrow_new_fails ? nullptr : ::operator new(n);
+  if (g_nothrow_new_fails)
+    return nullptr;
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  try {
+    return ::operator new(n);
+  } catch (...) {
+    return nullptr;
+  }
+#else
+  return ::operator new(n);
+#endif
 }
+
 void operator delete(void* p, const std::nothrow_t&) TEST_NOEXCEPT { ::operator delete(p); }
 
 typedef std::pair<int, int> P; // (value, original index)
@@ -53,17 +65,51 @@ struct counting_is_even {
   }
 };
 
-template <class Iter>
-void test(std::size_t n) {
-  // Worst case: every key is odd (predicate false) except the last, which is even
-  // (predicate true). An implementation that evaluates the predicate before checking
-  // for __first re-applies it to *__first, an element already known to be false,
-  // once per node along the right segment, exceeding the required N applications.
+// Generate expected stable partition result for sanity check.
+static std::vector<P> stable_reference(const std::vector<P>& in) {
+  std::vector<P> out;
+  out.reserve(in.size());
+  for (std::size_t i = 0; i < in.size(); ++i)
+    if (in[i].first % 2 == 0)
+      out.push_back(in[i]);
+  for (std::size_t i = 0; i < in.size(); ++i)
+    if (in[i].first % 2 != 0)
+      out.push_back(in[i]);
+  return out;
+}
+
+// General case: deterministic pseudo-random keys.
+static std::vector<P> make_shuffled(std::size_t n) {
+  std::vector<P> data;
+  data.reserve(n);
+  std::uint64_t s = 0x10110010111001ULL;
+  for (std::size_t i = 0; i < n; ++i) {
+    s ^= s << 13;
+    s ^= s >> 7;
+    s ^= s << 17;
+    data.push_back(P(static_cast<int>(s & 0xFF), static_cast<int>(i)));
+  }
+  return data;
+}
+
+// Worst case: every key is odd (predicate false) except the last, which is even
+// (predicate true). An implementation that evaluates the predicate before checking
+// for __first re-applies it to *__first, an element already known to be false,
+// once per node along the right segment, exceeding the required N applications.
+static std::vector<P> make_all_false_but_last(std::size_t n) {
   std::vector<P> data;
   data.reserve(n);
   for (std::size_t i = 0; i + 1 < n; ++i)
     data.push_back(P(1, static_cast<int>(i)));
-  data.push_back(P(2, static_cast<int>(n - 1)));
+  if (n > 0)
+    data.push_back(P(2, static_cast<int>(n - 1)));
+  return data;
+}
+
+template <class Iter>
+void run_case(std::vector<P> data) {
+  const std::size_t n           = data.size();
+  const std::vector<P> expected = stable_reference(data);
 
   int count             = 0;
   counting_is_even pred = {&count};
@@ -75,18 +121,22 @@ void test(std::size_t n) {
   // [alg.partitions]: exactly N applications of the predicate.
   assert(static_cast<std::size_t>(count) == n);
 
-  // Sanity check that the result is correctly and stably partitioned.
-  assert(base(r) == data.data() + 1);
-  assert(data[0].first % 2 == 0);
-  for (std::size_t i = 1; i < n; ++i)
-    assert(data[i].first % 2 != 0 && data[i].second == static_cast<int>(i - 1));
+  // Sanity check that the result is correctly and stably partitioned, and the returned iterator
+  // points at the first element for which the predicate is false.
+  assert(data == expected);
+  std::size_t num_true = 0;
+  while (num_true < n && data[num_true].first % 2 == 0)
+    ++num_true;
+  assert(base(r) == data.data() + num_true);
 }
 
 template <class Iter>
 void test() {
-  const std::size_t sizes[] = {1, 2, 3, 4, 5, 16, 17, 1000};
-  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i)
-    test<Iter>(sizes[i]);
+  const std::size_t sizes[] = {1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 17, 63, 64, 65, 256, 1000, 4096};
+  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i) {
+    run_case<Iter>(make_shuffled(sizes[i]));
+    run_case<Iter>(make_all_false_but_last(sizes[i]));
+  }
 }
 
 int main(int, char**) {

>From 24ac41a90e7c784de5227eb9d7cf6ac8e0767c00 Mon Sep 17 00:00:00 2001
From: David Meng <davidmenggx at gmail.com>
Date: Wed, 24 Jun 2026 18:03:31 -0700
Subject: [PATCH 6/8] Reapply "Simplify test case"

This reverts commit c763a5f0dfc683e4c8a8859e4405a7b67ca2c526.
---
 ...anges_stable_partition.complexity.pass.cpp | 83 ++++---------------
 .../stable_partition.complexity.pass.cpp      | 82 ++++--------------
 2 files changed, 32 insertions(+), 133 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
index 023743b49f5fa..df5dcc4afbb27 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
@@ -29,7 +29,6 @@
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
-#include <cstdint>
 #include <new>
 #include <ranges>
 #include <utility>
@@ -45,19 +44,8 @@
 static bool g_nothrow_new_fails = false;
 
 void* operator new(std::size_t n, const std::nothrow_t&) TEST_NOEXCEPT {
-  if (g_nothrow_new_fails)
-    return nullptr;
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  try {
-    return ::operator new(n);
-  } catch (...) {
-    return nullptr;
-  }
-#else
-  return ::operator new(n);
-#endif
+  return g_nothrow_new_fails ? nullptr : ::operator new(n);
 }
-
 void operator delete(void* p, const std::nothrow_t&) TEST_NOEXCEPT { ::operator delete(p); }
 
 typedef std::pair<int, int> P; // (value, original index)
@@ -71,7 +59,6 @@ struct counting_proj {
     return p.first;
   }
 };
-
 struct counting_is_even {
   int* count_;
   bool operator()(int v) const {
@@ -80,51 +67,17 @@ struct counting_is_even {
   }
 };
 
-// Generate expected stable partition result for sanity check.
-static std::vector<P> stable_reference(const std::vector<P>& in) {
-  std::vector<P> out;
-  out.reserve(in.size());
-  for (std::size_t i = 0; i < in.size(); ++i)
-    if (in[i].first % 2 == 0)
-      out.push_back(in[i]);
-  for (std::size_t i = 0; i < in.size(); ++i)
-    if (in[i].first % 2 != 0)
-      out.push_back(in[i]);
-  return out;
-}
-
-// General case: deterministic pseudo-random keys.
-static std::vector<P> make_shuffled(std::size_t n) {
-  std::vector<P> data;
-  data.reserve(n);
-  std::uint64_t s = 0x10110010111001ULL;
-  for (std::size_t i = 0; i < n; ++i) {
-    s ^= s << 13;
-    s ^= s >> 7;
-    s ^= s << 17;
-    data.push_back(P(static_cast<int>(s & 0xFF), static_cast<int>(i)));
-  }
-  return data;
-}
-
-// Worst case: every key is odd (predicate false) except the last, which is even
-// (predicate true). An implementation that evaluates the predicate before checking
-// for __first re-applies it to *__first, an element already known to be false,
-// once per node along the right segment, exceeding the required N applications.
-static std::vector<P> make_all_false_but_last(std::size_t n) {
+template <class Iter>
+void test(std::size_t n) {
+  // Worst case: every key is odd (predicate false) except the last, which is even
+  // (predicate true). An implementation that evaluates the predicate before checking
+  // for __first re-applies it to *__first, an element already known to be false,
+  // once per node along the right segment, exceeding the required N applications.
   std::vector<P> data;
   data.reserve(n);
   for (std::size_t i = 0; i + 1 < n; ++i)
     data.push_back(P(1, static_cast<int>(i)));
-  if (n > 0)
-    data.push_back(P(2, static_cast<int>(n - 1)));
-  return data;
-}
-
-template <class Iter>
-void run_case(std::vector<P> data) {
-  const std::size_t n           = data.size();
-  const std::vector<P> expected = stable_reference(data);
+  data.push_back(P(2, static_cast<int>(n - 1)));
 
   int pred_count        = 0;
   int proj_count        = 0;
@@ -139,23 +92,19 @@ void run_case(std::vector<P> data) {
   assert(static_cast<std::size_t>(pred_count) == n);
   assert(static_cast<std::size_t>(proj_count) == n);
 
-  // Sanity check that the result is correctly and stably partitioned, and the returned iterator
-  // points at the first element for which the predicate is false.
-  assert(data == expected);
-  std::size_t num_true = 0;
-  while (num_true < n && data[num_true].first % 2 == 0)
-    ++num_true;
-  assert(base(r.begin()) == data.data() + num_true);
+  // Sanity check that the result is correctly and stably partitioned.
+  assert(base(r.begin()) == data.data() + 1);
   assert(base(r.end()) == data.data() + n);
+  assert(data[0].first % 2 == 0);
+  for (std::size_t i = 1; i < n; ++i)
+    assert(data[i].first % 2 != 0 && data[i].second == static_cast<int>(i - 1));
 }
 
 template <class Iter>
 void test() {
-  const std::size_t sizes[] = {1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 17, 63, 64, 65, 256, 1000, 4096};
-  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i) {
-    run_case<Iter>(make_shuffled(sizes[i]));
-    run_case<Iter>(make_all_false_but_last(sizes[i]));
-  }
+  const std::size_t sizes[] = {1, 2, 3, 4, 5, 16, 17, 1000};
+  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i)
+    test<Iter>(sizes[i]);
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
index c93d05f53d338..a7745791dafa0 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
@@ -23,7 +23,6 @@
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
-#include <cstdint>
 #include <new>
 #include <utility>
 #include <vector>
@@ -38,19 +37,8 @@
 static bool g_nothrow_new_fails = false;
 
 void* operator new(std::size_t n, const std::nothrow_t&) TEST_NOEXCEPT {
-  if (g_nothrow_new_fails)
-    return nullptr;
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  try {
-    return ::operator new(n);
-  } catch (...) {
-    return nullptr;
-  }
-#else
-  return ::operator new(n);
-#endif
+  return g_nothrow_new_fails ? nullptr : ::operator new(n);
 }
-
 void operator delete(void* p, const std::nothrow_t&) TEST_NOEXCEPT { ::operator delete(p); }
 
 typedef std::pair<int, int> P; // (value, original index)
@@ -65,51 +53,17 @@ struct counting_is_even {
   }
 };
 
-// Generate expected stable partition result for sanity check.
-static std::vector<P> stable_reference(const std::vector<P>& in) {
-  std::vector<P> out;
-  out.reserve(in.size());
-  for (std::size_t i = 0; i < in.size(); ++i)
-    if (in[i].first % 2 == 0)
-      out.push_back(in[i]);
-  for (std::size_t i = 0; i < in.size(); ++i)
-    if (in[i].first % 2 != 0)
-      out.push_back(in[i]);
-  return out;
-}
-
-// General case: deterministic pseudo-random keys.
-static std::vector<P> make_shuffled(std::size_t n) {
-  std::vector<P> data;
-  data.reserve(n);
-  std::uint64_t s = 0x10110010111001ULL;
-  for (std::size_t i = 0; i < n; ++i) {
-    s ^= s << 13;
-    s ^= s >> 7;
-    s ^= s << 17;
-    data.push_back(P(static_cast<int>(s & 0xFF), static_cast<int>(i)));
-  }
-  return data;
-}
-
-// Worst case: every key is odd (predicate false) except the last, which is even
-// (predicate true). An implementation that evaluates the predicate before checking
-// for __first re-applies it to *__first, an element already known to be false,
-// once per node along the right segment, exceeding the required N applications.
-static std::vector<P> make_all_false_but_last(std::size_t n) {
+template <class Iter>
+void test(std::size_t n) {
+  // Worst case: every key is odd (predicate false) except the last, which is even
+  // (predicate true). An implementation that evaluates the predicate before checking
+  // for __first re-applies it to *__first, an element already known to be false,
+  // once per node along the right segment, exceeding the required N applications.
   std::vector<P> data;
   data.reserve(n);
   for (std::size_t i = 0; i + 1 < n; ++i)
     data.push_back(P(1, static_cast<int>(i)));
-  if (n > 0)
-    data.push_back(P(2, static_cast<int>(n - 1)));
-  return data;
-}
-
-template <class Iter>
-void run_case(std::vector<P> data) {
-  const std::size_t n           = data.size();
-  const std::vector<P> expected = stable_reference(data);
+  data.push_back(P(2, static_cast<int>(n - 1)));
 
   int count             = 0;
   counting_is_even pred = {&count};
@@ -121,22 +75,18 @@ void run_case(std::vector<P> data) {
   // [alg.partitions]: exactly N applications of the predicate.
   assert(static_cast<std::size_t>(count) == n);
 
-  // Sanity check that the result is correctly and stably partitioned, and the returned iterator
-  // points at the first element for which the predicate is false.
-  assert(data == expected);
-  std::size_t num_true = 0;
-  while (num_true < n && data[num_true].first % 2 == 0)
-    ++num_true;
-  assert(base(r) == data.data() + num_true);
+  // Sanity check that the result is correctly and stably partitioned.
+  assert(base(r) == data.data() + 1);
+  assert(data[0].first % 2 == 0);
+  for (std::size_t i = 1; i < n; ++i)
+    assert(data[i].first % 2 != 0 && data[i].second == static_cast<int>(i - 1));
 }
 
 template <class Iter>
 void test() {
-  const std::size_t sizes[] = {1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 17, 63, 64, 65, 256, 1000, 4096};
-  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i) {
-    run_case<Iter>(make_shuffled(sizes[i]));
-    run_case<Iter>(make_all_false_but_last(sizes[i]));
-  }
+  const std::size_t sizes[] = {1, 2, 3, 4, 5, 16, 17, 1000};
+  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i)
+    test<Iter>(sizes[i]);
 }
 
 int main(int, char**) {

>From 1e91fd8c679222f9ae098afecfec5a0bbe7630cf Mon Sep 17 00:00:00 2001
From: David Meng <davidmenggx at gmail.com>
Date: Wed, 24 Jun 2026 20:21:28 -0700
Subject: [PATCH 7/8] Simplify test case and combine with existing file

---
 ...anges_stable_partition.complexity.pass.cpp | 115 ------------------
 .../ranges_stable_partition.pass.cpp          |  47 +++++++
 .../stable_partition.complexity.pass.cpp      |  97 ---------------
 .../alg.partitions/stable_partition.pass.cpp  |  38 ++++++
 4 files changed, 85 insertions(+), 212 deletions(-)
 delete mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
 delete mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp

diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
deleted file mode 100644
index df5dcc4afbb27..0000000000000
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.complexity.pass.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: sanitizer-new-delete
-
-// <algorithm>
-
-// template<bidirectional_iterator I, sentinel_for<I> S, class Proj = identity,
-//          indirect_unary_predicate<projected<I, Proj>> Pred>
-//   requires permutable<I>
-//   constexpr subrange<I>                                                         // constexpr since C++26
-//     stable_partition(I first, S last, Pred pred, Proj proj = {});               // Since C++20
-//
-// template<bidirectional_range R, class Proj = identity,
-//          indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
-//   requires permutable<iterator_t<R>>
-//   constexpr borrowed_subrange_t<R>                                              // constexpr since C++26
-//     stable_partition(R&& r, Pred pred, Proj proj = {});                         // Since C++20
-
-// [alg.partitions] requires stable_partition (without ExecutionPolicy) to apply the
-// predicate exactly N = last - first times.
-
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <new>
-#include <ranges>
-#include <utility>
-#include <vector>
-
-#include "test_iterators.h"
-#include "test_macros.h"
-
-// Forces stable_partition onto its in-place (no temporary buffer) path. libc++
-// allocates the buffer with ::operator new(..., nothrow), so returning nullptr
-// makes the allocation fail. pair<int, int> is not over-aligned, so only the
-// plain nothrow operator new needs to be replaced.
-static bool g_nothrow_new_fails = false;
-
-void* operator new(std::size_t n, const std::nothrow_t&) TEST_NOEXCEPT {
-  return g_nothrow_new_fails ? nullptr : ::operator new(n);
-}
-void operator delete(void* p, const std::nothrow_t&) TEST_NOEXCEPT { ::operator delete(p); }
-
-typedef std::pair<int, int> P; // (value, original index)
-
-// stable_partition takes its predicate by value, so the count must
-// exist outside the predicate object.
-struct counting_proj {
-  int* count_;
-  int operator()(const P& p) const {
-    ++*count_;
-    return p.first;
-  }
-};
-struct counting_is_even {
-  int* count_;
-  bool operator()(int v) const {
-    ++*count_;
-    return (v % 2) == 0;
-  }
-};
-
-template <class Iter>
-void test(std::size_t n) {
-  // Worst case: every key is odd (predicate false) except the last, which is even
-  // (predicate true). An implementation that evaluates the predicate before checking
-  // for __first re-applies it to *__first, an element already known to be false,
-  // once per node along the right segment, exceeding the required N applications.
-  std::vector<P> data;
-  data.reserve(n);
-  for (std::size_t i = 0; i + 1 < n; ++i)
-    data.push_back(P(1, static_cast<int>(i)));
-  data.push_back(P(2, static_cast<int>(n - 1)));
-
-  int pred_count        = 0;
-  int proj_count        = 0;
-  counting_is_even pred = {&pred_count};
-  counting_proj proj    = {&proj_count};
-
-  g_nothrow_new_fails = true;
-  auto r              = std::ranges::stable_partition(Iter(data.data()), Iter(data.data() + n), pred, proj);
-  g_nothrow_new_fails = false;
-
-  // [alg.partitions]: exactly N applications of the predicate and projection.
-  assert(static_cast<std::size_t>(pred_count) == n);
-  assert(static_cast<std::size_t>(proj_count) == n);
-
-  // Sanity check that the result is correctly and stably partitioned.
-  assert(base(r.begin()) == data.data() + 1);
-  assert(base(r.end()) == data.data() + n);
-  assert(data[0].first % 2 == 0);
-  for (std::size_t i = 1; i < n; ++i)
-    assert(data[i].first % 2 != 0 && data[i].second == static_cast<int>(i - 1));
-}
-
-template <class Iter>
-void test() {
-  const std::size_t sizes[] = {1, 2, 3, 4, 5, 16, 17, 1000};
-  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i)
-    test<Iter>(sizes[i]);
-}
-
-int main(int, char**) {
-  test<P*>();
-  test<bidirectional_iterator<P*> >();
-  test<random_access_iterator<P*> >();
-  return 0;
-}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.pass.cpp
index 28fda6b56446c..a1d86b47da320 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.pass.cpp
@@ -26,10 +26,14 @@
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <cstddef>
 #include <functional>
 #include <ranges>
+#include <utility>
+#include <vector>
 
 #include "almost_satisfies_types.h"
+#include "count_new.h"
 #include "test_iterators.h"
 #include "test_macros.h"
 
@@ -247,8 +251,51 @@ TEST_CONSTEXPR_CXX26 bool test() {
   return true;
 }
 
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_COMPILER_GCC)
+// [alg.partitions]: Apply the predicate N = last - first times.
+void test_predicate_complexity() {
+  using P                   = std::pair<int, int>;
+  const std::size_t sizes[] = {4, 5, 16, 17, 1000};
+  for (std::size_t n : sizes) {
+    std::vector<P> v;
+    v.reserve(n);
+    for (std::size_t i = 0; i + 1 < n; ++i)
+      v.push_back(P(1, static_cast<int>(i)));
+    v.push_back(P(2, static_cast<int>(n - 1)));
+
+    int pred_count = 0;
+    int proj_count = 0;
+    auto pred      = [&pred_count](int x) {
+      ++pred_count;
+      return x % 2 == 0;
+    };
+    auto proj = [&proj_count](const P& p) {
+      ++proj_count;
+      return p.first;
+    };
+
+    getGlobalMemCounter()->throw_after = 0;
+    auto r                             = std::ranges::stable_partition(v.data(), v.data() + n, pred, proj);
+    getGlobalMemCounter()->reset();
+
+    assert(static_cast<std::size_t>(pred_count) == n); // Exactly N predicate applications.
+    assert(static_cast<std::size_t>(proj_count) == n); // Exactly N projection applications.
+
+    // Result is correctly partitioned and subrange is at correct location.
+    assert(r.begin() == v.data() + 1);
+    assert(r.end() == v.data() + n);
+    assert(v[0].first % 2 == 0);
+    for (std::size_t i = 1; i < n; ++i)
+      assert(v[i].first % 2 != 0 && v[i].second == static_cast<int>(i - 1));
+  }
+}
+#endif // !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_COMPILER_GCC)
+
 int main(int, char**) {
   test();
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_COMPILER_GCC)
+  test_predicate_complexity();
+#endif
 #if TEST_STD_VER >= 26
   static_assert(test());
 #endif
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
deleted file mode 100644
index a7745791dafa0..0000000000000
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.complexity.pass.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: sanitizer-new-delete
-// XFAIL: FROZEN-CXX03-HEADERS-FIXME
-
-// <algorithm>
-
-// template<BidirectionalIterator Iter, Predicate<auto, Iter::value_type> Pred>
-//   requires ShuffleIterator<Iter>
-//         && CopyConstructible<Pred>
-//   constexpr Iter                                                               // constexpr since C++26
-//   stable_partition(Iter first, Iter last, Pred pred);
-
-// [alg.partitions] requires stable_partition (without ExecutionPolicy) to apply the
-// predicate exactly N = last - first times.
-
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <new>
-#include <utility>
-#include <vector>
-
-#include "test_iterators.h"
-#include "test_macros.h"
-
-// Forces stable_partition in-place (no temporary buffer). libc++ allocates
-// the buffer with ::operator new(..., nothrow), so returning nullptr from
-// it makes the allocation fail. pair<int, int> is not over-aligned, so only
-// the plain nothrow operator new needs to be replaced.
-static bool g_nothrow_new_fails = false;
-
-void* operator new(std::size_t n, const std::nothrow_t&) TEST_NOEXCEPT {
-  return g_nothrow_new_fails ? nullptr : ::operator new(n);
-}
-void operator delete(void* p, const std::nothrow_t&) TEST_NOEXCEPT { ::operator delete(p); }
-
-typedef std::pair<int, int> P; // (value, original index)
-
-// stable_partition takes its predicate by value, so the count must
-// exist outside the predicate object.
-struct counting_is_even {
-  int* count_;
-  bool operator()(const P& p) const {
-    ++*count_;
-    return (p.first % 2) == 0;
-  }
-};
-
-template <class Iter>
-void test(std::size_t n) {
-  // Worst case: every key is odd (predicate false) except the last, which is even
-  // (predicate true). An implementation that evaluates the predicate before checking
-  // for __first re-applies it to *__first, an element already known to be false,
-  // once per node along the right segment, exceeding the required N applications.
-  std::vector<P> data;
-  data.reserve(n);
-  for (std::size_t i = 0; i + 1 < n; ++i)
-    data.push_back(P(1, static_cast<int>(i)));
-  data.push_back(P(2, static_cast<int>(n - 1)));
-
-  int count             = 0;
-  counting_is_even pred = {&count};
-
-  g_nothrow_new_fails = true;
-  Iter r              = std::stable_partition(Iter(data.data()), Iter(data.data() + n), pred);
-  g_nothrow_new_fails = false;
-
-  // [alg.partitions]: exactly N applications of the predicate.
-  assert(static_cast<std::size_t>(count) == n);
-
-  // Sanity check that the result is correctly and stably partitioned.
-  assert(base(r) == data.data() + 1);
-  assert(data[0].first % 2 == 0);
-  for (std::size_t i = 1; i < n; ++i)
-    assert(data[i].first % 2 != 0 && data[i].second == static_cast<int>(i - 1));
-}
-
-template <class Iter>
-void test() {
-  const std::size_t sizes[] = {1, 2, 3, 4, 5, 16, 17, 1000};
-  for (std::size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i)
-    test<Iter>(sizes[i]);
-}
-
-int main(int, char**) {
-  test<P*>();
-  test<bidirectional_iterator<P*> >();
-  test<random_access_iterator<P*> >();
-  return 0;
-}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.pass.cpp
index 7e8100b7949e7..a2cfc87ffb968 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.pass.cpp
@@ -16,7 +16,9 @@
 
 #include <algorithm>
 #include <cassert>
+#include <cstddef>
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "count_new.h"
@@ -330,8 +332,44 @@ TEST_CONSTEXPR_CXX26 bool test() {
   return true;
 }
 
+#if TEST_STD_VER >= 11 && !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_COMPILER_GCC)
+// [alg.partitions]: Apply the predicate N = last - first times.
+void test_predicate_complexity() {
+  typedef std::pair<int, int> P;
+  const std::size_t sizes[] = {4, 5, 16, 17, 1000};
+  for (std::size_t n : sizes) {
+    std::vector<P> v;
+    v.reserve(n);
+    for (std::size_t i = 0; i + 1 < n; ++i)
+      v.push_back(P(1, static_cast<int>(i)));
+    v.push_back(P(2, static_cast<int>(n - 1)));
+
+    int count = 0;
+    auto pred = [&count](const P& p) {
+      ++count;
+      return p.first % 2 == 0;
+    };
+
+    getGlobalMemCounter()->throw_after = 0;
+    P* r                               = std::stable_partition(v.data(), v.data() + n, pred);
+    getGlobalMemCounter()->reset();
+
+    assert(static_cast<std::size_t>(count) == n); // Exactly N predicate applications.
+
+    // Result is correctly partitioned and iterator is at correct location.
+    assert(r == v.data() + 1);
+    assert(v[0].first % 2 == 0);
+    for (std::size_t i = 1; i < n; ++i)
+      assert(v[i].first % 2 != 0 && v[i].second == static_cast<int>(i - 1));
+  }
+}
+#endif // TEST_STD_VER >= 11 && !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_COMPILER_GCC)
+
 int main(int, char**) {
   test();
+#if TEST_STD_VER >= 11 && !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_COMPILER_GCC)
+  test_predicate_complexity();
+#endif
 #if TEST_STD_VER >= 26
   static_assert(test());
 #endif

>From 2db5e62999c4b6ff54de2e418dda7785d230f8ba Mon Sep 17 00:00:00 2001
From: David Meng <davidmenggx at gmail.com>
Date: Thu, 2 Jul 2026 09:31:34 -0700
Subject: [PATCH 8/8] Re-run tests



From libcxx-commits at lists.llvm.org  Thu Jul  2 09:33:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 09:33:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a4692d7.27b3b003.1610ae.927f@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207086

>From 64b22d4e38f896e39762ccfd1f011f0dd16dfddb Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 29 Jun 2026 14:57:40 -0400
Subject: [PATCH 1/5] [libc++] Treat negative counts in copy_n & friends as
 no-ops

The standard specifies copy_n, fill_n and generate_n (both std and
ranges) to be no-ops when passed a negative count. This patch fixes
libc++ to abide by that requirement, with tests.

It's worth noting that std::for_each_n makes n > 0 a precondition
instead: we add the check and tests for it.

Fixes #193613
---
 libcxx/include/__algorithm/copy_n.h           | 17 +++++----
 libcxx/include/__algorithm/for_each_n.h       |  2 +
 libcxx/include/__algorithm/ranges_copy_n.h    |  2 +
 .../include/__algorithm/ranges_for_each_n.h   |  2 +
 libcxx/include/__bit_reference                | 17 +++++----
 .../alg.foreach/assert.for_each_n.pass.cpp    | 35 ++++++++++++++++++
 .../assert.ranges.for_each_n.pass.cpp         | 37 +++++++++++++++++++
 .../alg.copy/copy_n.pass.cpp                  | 16 ++++++++
 .../alg.copy/ranges.copy_n.pass.cpp           | 17 +++++++++
 .../alg.fill/fill_n.pass.cpp                  | 15 ++++++++
 .../alg.fill/ranges.fill_n.pass.cpp           | 15 ++++++++
 .../alg.generate/generate_n.pass.cpp          |  7 ++++
 .../alg.generate/ranges_generate_n.pass.cpp   | 15 +++++++-
 13 files changed, 181 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
 create mode 100644 libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index ac52b2b686c28..a0ea02121b70b 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -42,7 +42,7 @@ template <class _AlgPolicy,
           __enable_if_t<!__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
-  while (__n != 0) {
+  while (__n > 0) {
     *__result = *__first;
     ++__first;
     ++__result;
@@ -63,14 +63,15 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted > 0) {
+  if (__converted < 0) [[__unlikely__]]
+    return __result;
+
+  *__result = *__first;
+  ++__result;
+  for (--__converted; __converted > 0; --__converted) {
+    ++__first;
     *__result = *__first;
     ++__result;
-    for (--__converted; __converted > 0; --__converted) {
-      ++__first;
-      *__result = *__first;
-      ++__result;
-    }
   }
   return __result;
 }
@@ -83,6 +84,8 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
+  if (__converted < 0) [[__unlikely__]]
+    return __result;
   return std::__copy_n<_ClassicAlgPolicy>(__first, __iterator_difference_type<_InputIterator>(__converted), __result)
       .__out_;
 }
diff --git a/libcxx/include/__algorithm/for_each_n.h b/libcxx/include/__algorithm/for_each_n.h
index 7ff3f9b157c02..9b393b307f323 100644
--- a/libcxx/include/__algorithm/for_each_n.h
+++ b/libcxx/include/__algorithm/for_each_n.h
@@ -12,6 +12,7 @@
 
 #include <__algorithm/for_each.h>
 #include <__algorithm/for_each_n_segment.h>
+#include <__assert>
 #include <__config>
 #include <__functional/identity.h>
 #include <__iterator/iterator_traits.h>
@@ -63,6 +64,7 @@ __for_each_n(_InputIterator __first, _Size __orig_n, _Func&& __f, _Proj& __proj)
 template <class _InputIterator, class _Size, class _Func>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
 for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__orig_n >= 0, "for_each_n requires a non-negative count");
   __identity __proj;
   return std::__for_each_n(__first, __orig_n, __f, __proj);
 }
diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
index 1720ac358fa4d..c153e74009801 100644
--- a/libcxx/include/__algorithm/ranges_copy_n.h
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -38,6 +38,8 @@ struct __copy_n {
     requires indirectly_copyable<_Ip, _Op>
   _LIBCPP_HIDE_FROM_ABI constexpr copy_n_result<_Ip, _Op>
   operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const {
+    if (__n < 0) [[__unlikely__]]
+      return {std::move(__first), std::move(__result)};
     return std::__copy_n<_RangeAlgPolicy>(std::move(__first), __n, std::move(__result));
   }
 };
diff --git a/libcxx/include/__algorithm/ranges_for_each_n.h b/libcxx/include/__algorithm/ranges_for_each_n.h
index 3aab1b79c10a1..126dc481ec723 100644
--- a/libcxx/include/__algorithm/ranges_for_each_n.h
+++ b/libcxx/include/__algorithm/ranges_for_each_n.h
@@ -11,6 +11,7 @@
 
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/in_fun_result.h>
+#include <__assert>
 #include <__config>
 #include <__functional/identity.h>
 #include <__iterator/concepts.h>
@@ -40,6 +41,7 @@ struct __for_each_n {
   template <input_iterator _Iter, class _Proj = identity, indirectly_unary_invocable<projected<_Iter, _Proj>> _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr for_each_n_result<_Iter, _Func>
   operator()(_Iter __first, iter_difference_t<_Iter> __count, _Func __func, _Proj __proj = {}) const {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count >= 0, "for_each_n requires a non-negative count");
     auto __last = std::__for_each_n(std::move(__first), __count, __func, __proj);
     return {std::move(__last), std::move(__func)};
   }
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 692686f1584d0..0083714b2cafe 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -424,8 +424,8 @@ public:
   }
 
 #if _LIBCPP_STD_VER <= 17
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool
-  operator!=(const __bit_iterator& __x, const __bit_iterator& __y) {
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool operator!=(const __bit_iterator& __x, const __bit_iterator& __y) {
     return !(__x == __y);
   }
 
@@ -573,12 +573,13 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n > 0) {
-      if (__value)
-        __impl<true>(__first, __n);
-      else
-        __impl<false>(__first, __n);
-    }
+    if (__n < 0) [[__unlikely__]]
+      return __first;
+
+    if (__value)
+      __impl<true>(__first, __n);
+    else
+      __impl<false>(__first, __n);
     return __first + __n;
   }
 };
diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
new file mode 100644
index 0000000000000..53e59401430fa
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<class InputIterator, class Size, class Function>
+//    constexpr InputIterator
+//    for_each_n(InputIterator first, Size n, Function f);
+//
+// [alg.foreach] requires `n >= 0`; passing a negative count is a precondition violation.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <algorithm>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  int a[] = {1, 2, 3};
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp
new file mode 100644
index 0000000000000..e57f342e760a6
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<input_iterator I, class Proj = identity,
+//          indirectly_unary_invocable<projected<I, Proj>> Fun>
+//   constexpr ranges::for_each_n_result<I, Fun>
+//     ranges::for_each_n(I first, iter_difference_t<I> n, Fun f, Proj proj = {});
+//
+// [alg.foreach] requires `n >= 0`; passing a negative count is a precondition violation.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <algorithm>
+#include <array>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  std::array a = {1, 2, 3};
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::ranges::for_each_n(a.begin(), -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::ranges::for_each_n(a.begin(), -10000000, [](int) {}), "for_each_n requires a non-negative count");
+
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 183f38c1ce117..52d99f018110b 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -61,6 +61,15 @@ struct TestIterators {
       assert(base(r) == ib + N / 2);
       for (unsigned i = 0; i < N / 2; ++i)
         assert(ia[i] == ib[i]);
+
+      { // A negative count is a no-op that returns the unchanged output iterator.
+        // Regression test for https://llvm.org/PR193613.
+        int source[]  = {1, 2, 3};
+        int dest[]    = {-1, -2, -3};
+        OutIter ret   = std::copy_n(InIter(source), -5, OutIter(dest));
+        assert(base(ret) == dest);
+        assert(dest[0] == -1 && dest[1] == -2 && dest[2] == -3);
+      }
     }
   };
 };
@@ -81,6 +90,13 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
     for (std::size_t i = 0; i < N; ++i)
       assert(out[i + 4] == in[i]);
   }
+  { // Negative count test
+    std::vector<bool> source(N, true);
+    std::vector<bool> dest(N, false);
+    std::vector<bool>::iterator r = std::copy_n(source.begin(), -5, dest.begin());
+    assert(r == dest.begin());
+    assert(dest == std::vector<bool>(N, false));
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
index 132993dba76b9..5e073026ed8db 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
@@ -60,6 +60,16 @@ constexpr void test_iterators() {
     assert(base(ret.in) == in.data());
     assert(base(ret.out) == out.data());
   }
+
+  { // A negative count is a no-op that returns the unchanged iterators.
+    // Regression test for https://llvm.org/PR193613.
+    std::array in{1, 2, 3};
+    std::array out{-1, -2, -3};
+    std::same_as<std::ranges::in_out_result<In, Out>> auto ret = std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
+    assert(base(ret.in) == in.data());
+    assert(base(ret.out) == out.data());
+    assert((out == std::array{-1, -2, -3}));
+  }
 }
 
 #if TEST_STD_VER >= 23
@@ -79,6 +89,13 @@ constexpr bool test_vector_bool(std::size_t N) {
     for (std::size_t i = 0; i < N; ++i)
       assert(out[i + 4] == in[i]);
   }
+  { // Negative count test
+    std::vector<bool> source(N, true);
+    std::vector<bool> dest(N, false);
+    auto ret = std::ranges::copy_n(source.begin(), -5, dest.begin());
+    assert(ret.out == dest.begin());
+    assert(dest == std::vector<bool>(N, false));
+  }
 
   return true;
 };
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
index 6d7ba98a33b65..cc9aaeb04d7e8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
@@ -52,6 +52,14 @@ struct Test {
       std::array<T, 4> expected = {1, 5, 5, 4};
       test<Iter>(in, 1, 2, 5, expected);
     }
+    { // A negative count is a no-op that returns the unchanged iterator.
+      // Regression test for https://llvm.org/PR193613.
+      std::array<T, 4> in       = {1, 2, 3, 4};
+      std::array<T, 4> expected = {1, 2, 3, 4};
+      Iter it                   = std::fill_n(Iter(in.data()), -5, T(9));
+      assert(base(it) == in.data());
+      assert(in == expected);
+    }
   }
 };
 
@@ -126,6 +134,13 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
     }
   }
 
+  { // Negative count test
+    std::vector<bool> v(N, false);
+    std::vector<bool>::iterator r = std::fill_n(v.begin(), -5, true);
+    assert(r == v.begin());
+    assert(v == std::vector<bool>(N, false));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
index 0de6f4a625136..a8eb6c4eb9737 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
@@ -51,6 +51,14 @@ constexpr void test_iterators() {
     auto ret = std::ranges::fill_n(It(a.data()), 0, 1);
     assert(base(ret) == a.data());
   }
+
+  { // A negative count is a no-op that returns the unchanged iterator.
+    // Regression test for https://llvm.org/PR193613.
+    int a[3]                            = {-1, -2, -3};
+    std::same_as<It> decltype(auto) ret = std::ranges::fill_n(It(a), -5, 1);
+    assert(base(ret) == a);
+    assert(a[0] == -1 && a[1] == -2 && a[2] == -3);
+  }
 }
 
 // The `ranges::{fill, fill_n}` algorithms require `vector<bool, Alloc>::iterator` to satisfy
@@ -99,6 +107,13 @@ constexpr bool test_vector_bool(std::size_t N) {
     }
   }
 
+  { // Negative count test
+    std::vector<bool> v(N, false);
+    auto r = std::ranges::fill_n(v.begin(), -5, true);
+    assert(r == v.begin());
+    assert(v == std::vector<bool>(N, false));
+  }
+
   return true;
 }
 #endif
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
index 525737ceacfbf..0e38a89613aa8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
@@ -70,6 +70,13 @@ test()
     test2<Iter, float>();
     test2<Iter, double>();  // this is PR#35498
     test2<Iter, long double>();
+
+    { // A negative count is a no-op that returns the unchanged iterator.
+      // Regression test for https://llvm.org/PR193613.
+      int ia[] = {1, 2, 3};
+      assert(std::generate_n(Iter(ia), -5, gen_test()) == Iter(ia));
+      assert(ia[0] == 1 && ia[1] == 2 && ia[2] == 3);
+    }
 }
 
 void deque_test() {
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
index 33d40a39cf385..6154ab240c3d6 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
@@ -92,6 +92,20 @@ constexpr void test_iter() {
   test_one<Iter>(std::array{-10, -20, -30, -40, -50}, 3, gen, {1, 2, 3, -40, -50});
   // Longer sequence, n = 5.
   test_one<Iter>(std::array<int, 5>{}, 5, gen, {1, 2, 3, 4, 5});
+
+  { // A negative count is a no-op that returns the unchanged iterator.
+    // Regression test for https://llvm.org/PR193613.
+    std::array in{-10, -20, -30};
+    int called        = 0;
+    auto counting_gen = [&called] {
+      ++called;
+      return 42;
+    };
+    std::same_as<Iter> decltype(auto) result = std::ranges::generate_n(Iter(in.data()), -5, counting_gen);
+    assert(base(result) == in.data());
+    assert(called == 0);
+    assert((in == std::array{-10, -20, -30}));
+  }
 }
 
 constexpr void test_iterators() {
@@ -131,7 +145,6 @@ constexpr bool test() {
     assert(gen_invocations == N2);
   }
 
-
   return true;
 }
 

>From 892279c90e066c4af5e0ef600fe47df0ba9dbf9e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 17:31:39 -0400
Subject: [PATCH 2/5] Fix check

---
 libcxx/include/__bit_reference | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 0083714b2cafe..b07c7ddccd5a0 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -573,7 +573,7 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n < 0) [[__unlikely__]]
+    if (__n <= 0) [[__unlikely__]]
       return __first;
 
     if (__value)

>From 87de9ba731d496c0900154847a077591dde9c906 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 17:36:19 -0400
Subject: [PATCH 3/5] Revert to != 0

---
 libcxx/include/__algorithm/copy_n.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index a0ea02121b70b..7ce0ec53b0c53 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -42,7 +42,7 @@ template <class _AlgPolicy,
           __enable_if_t<!__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
-  while (__n > 0) {
+  while (__n != 0) {
     *__result = *__first;
     ++__first;
     ++__result;

>From 86c6915b6d53b0ffde7b3a2ccb197b05f28c85ee Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 12:28:02 -0400
Subject: [PATCH 4/5] Drop [[__unlikely__]]

---
 libcxx/include/__algorithm/copy_n.h        | 4 ++--
 libcxx/include/__algorithm/ranges_copy_n.h | 2 +-
 libcxx/include/__bit_reference             | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index 7ce0ec53b0c53..7f9a109a763ce 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -63,7 +63,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0) [[__unlikely__]]
+  if (__converted < 0)
     return __result;
 
   *__result = *__first;
@@ -84,7 +84,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0) [[__unlikely__]]
+  if (__converted < 0)
     return __result;
   return std::__copy_n<_ClassicAlgPolicy>(__first, __iterator_difference_type<_InputIterator>(__converted), __result)
       .__out_;
diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
index c153e74009801..7baf1932bb3d8 100644
--- a/libcxx/include/__algorithm/ranges_copy_n.h
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -38,7 +38,7 @@ struct __copy_n {
     requires indirectly_copyable<_Ip, _Op>
   _LIBCPP_HIDE_FROM_ABI constexpr copy_n_result<_Ip, _Op>
   operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const {
-    if (__n < 0) [[__unlikely__]]
+    if (__n < 0)
       return {std::move(__first), std::move(__result)};
     return std::__copy_n<_RangeAlgPolicy>(std::move(__first), __n, std::move(__result));
   }
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index b07c7ddccd5a0..23ff6eaf56684 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -573,7 +573,7 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n <= 0) [[__unlikely__]]
+    if (__n <= 0)
       return __first;
 
     if (__value)

>From 6088d6dfa8ef53749309e627c85783bedb3ab248 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 12:33:12 -0400
Subject: [PATCH 5/5] Format

---
 .../alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp | 6 ++----
 .../alg.modifying.operations/alg.copy/copy_n.pass.cpp       | 6 +++---
 .../alg.copy/ranges.copy_n.pass.cpp                         | 3 ++-
 3 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
index 53e59401430fa..5ae0dbc865b46 100644
--- a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
@@ -26,10 +26,8 @@
 int main(int, char**) {
   int a[] = {1, 2, 3};
 
-  TEST_LIBCPP_ASSERT_FAILURE(
-      std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
-  TEST_LIBCPP_ASSERT_FAILURE(
-      std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
 
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 52d99f018110b..3348fcf358580 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -64,9 +64,9 @@ struct TestIterators {
 
       { // A negative count is a no-op that returns the unchanged output iterator.
         // Regression test for https://llvm.org/PR193613.
-        int source[]  = {1, 2, 3};
-        int dest[]    = {-1, -2, -3};
-        OutIter ret   = std::copy_n(InIter(source), -5, OutIter(dest));
+        int source[] = {1, 2, 3};
+        int dest[]   = {-1, -2, -3};
+        OutIter ret  = std::copy_n(InIter(source), -5, OutIter(dest));
         assert(base(ret) == dest);
         assert(dest[0] == -1 && dest[1] == -2 && dest[2] == -3);
       }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
index 5e073026ed8db..c354076b63363 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
@@ -65,7 +65,8 @@ constexpr void test_iterators() {
     // Regression test for https://llvm.org/PR193613.
     std::array in{1, 2, 3};
     std::array out{-1, -2, -3};
-    std::same_as<std::ranges::in_out_result<In, Out>> auto ret = std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
+    std::same_as<std::ranges::in_out_result<In, Out>> auto ret =
+        std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
     assert(base(ret.in) == in.data());
     assert(base(ret.out) == out.data());
     assert((out == std::array{-1, -2, -3}));


From libcxx-commits at lists.llvm.org  Thu Jul  2 10:19:50 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 10:19:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a469db6.6c8668f2.74a35.c11a@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207086

>From 64b22d4e38f896e39762ccfd1f011f0dd16dfddb Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 29 Jun 2026 14:57:40 -0400
Subject: [PATCH 1/6] [libc++] Treat negative counts in copy_n & friends as
 no-ops

The standard specifies copy_n, fill_n and generate_n (both std and
ranges) to be no-ops when passed a negative count. This patch fixes
libc++ to abide by that requirement, with tests.

It's worth noting that std::for_each_n makes n > 0 a precondition
instead: we add the check and tests for it.

Fixes #193613
---
 libcxx/include/__algorithm/copy_n.h           | 17 +++++----
 libcxx/include/__algorithm/for_each_n.h       |  2 +
 libcxx/include/__algorithm/ranges_copy_n.h    |  2 +
 .../include/__algorithm/ranges_for_each_n.h   |  2 +
 libcxx/include/__bit_reference                | 17 +++++----
 .../alg.foreach/assert.for_each_n.pass.cpp    | 35 ++++++++++++++++++
 .../assert.ranges.for_each_n.pass.cpp         | 37 +++++++++++++++++++
 .../alg.copy/copy_n.pass.cpp                  | 16 ++++++++
 .../alg.copy/ranges.copy_n.pass.cpp           | 17 +++++++++
 .../alg.fill/fill_n.pass.cpp                  | 15 ++++++++
 .../alg.fill/ranges.fill_n.pass.cpp           | 15 ++++++++
 .../alg.generate/generate_n.pass.cpp          |  7 ++++
 .../alg.generate/ranges_generate_n.pass.cpp   | 15 +++++++-
 13 files changed, 181 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
 create mode 100644 libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index ac52b2b686c28..a0ea02121b70b 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -42,7 +42,7 @@ template <class _AlgPolicy,
           __enable_if_t<!__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
-  while (__n != 0) {
+  while (__n > 0) {
     *__result = *__first;
     ++__first;
     ++__result;
@@ -63,14 +63,15 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted > 0) {
+  if (__converted < 0) [[__unlikely__]]
+    return __result;
+
+  *__result = *__first;
+  ++__result;
+  for (--__converted; __converted > 0; --__converted) {
+    ++__first;
     *__result = *__first;
     ++__result;
-    for (--__converted; __converted > 0; --__converted) {
-      ++__first;
-      *__result = *__first;
-      ++__result;
-    }
   }
   return __result;
 }
@@ -83,6 +84,8 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
+  if (__converted < 0) [[__unlikely__]]
+    return __result;
   return std::__copy_n<_ClassicAlgPolicy>(__first, __iterator_difference_type<_InputIterator>(__converted), __result)
       .__out_;
 }
diff --git a/libcxx/include/__algorithm/for_each_n.h b/libcxx/include/__algorithm/for_each_n.h
index 7ff3f9b157c02..9b393b307f323 100644
--- a/libcxx/include/__algorithm/for_each_n.h
+++ b/libcxx/include/__algorithm/for_each_n.h
@@ -12,6 +12,7 @@
 
 #include <__algorithm/for_each.h>
 #include <__algorithm/for_each_n_segment.h>
+#include <__assert>
 #include <__config>
 #include <__functional/identity.h>
 #include <__iterator/iterator_traits.h>
@@ -63,6 +64,7 @@ __for_each_n(_InputIterator __first, _Size __orig_n, _Func&& __f, _Proj& __proj)
 template <class _InputIterator, class _Size, class _Func>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
 for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__orig_n >= 0, "for_each_n requires a non-negative count");
   __identity __proj;
   return std::__for_each_n(__first, __orig_n, __f, __proj);
 }
diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
index 1720ac358fa4d..c153e74009801 100644
--- a/libcxx/include/__algorithm/ranges_copy_n.h
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -38,6 +38,8 @@ struct __copy_n {
     requires indirectly_copyable<_Ip, _Op>
   _LIBCPP_HIDE_FROM_ABI constexpr copy_n_result<_Ip, _Op>
   operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const {
+    if (__n < 0) [[__unlikely__]]
+      return {std::move(__first), std::move(__result)};
     return std::__copy_n<_RangeAlgPolicy>(std::move(__first), __n, std::move(__result));
   }
 };
diff --git a/libcxx/include/__algorithm/ranges_for_each_n.h b/libcxx/include/__algorithm/ranges_for_each_n.h
index 3aab1b79c10a1..126dc481ec723 100644
--- a/libcxx/include/__algorithm/ranges_for_each_n.h
+++ b/libcxx/include/__algorithm/ranges_for_each_n.h
@@ -11,6 +11,7 @@
 
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/in_fun_result.h>
+#include <__assert>
 #include <__config>
 #include <__functional/identity.h>
 #include <__iterator/concepts.h>
@@ -40,6 +41,7 @@ struct __for_each_n {
   template <input_iterator _Iter, class _Proj = identity, indirectly_unary_invocable<projected<_Iter, _Proj>> _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr for_each_n_result<_Iter, _Func>
   operator()(_Iter __first, iter_difference_t<_Iter> __count, _Func __func, _Proj __proj = {}) const {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count >= 0, "for_each_n requires a non-negative count");
     auto __last = std::__for_each_n(std::move(__first), __count, __func, __proj);
     return {std::move(__last), std::move(__func)};
   }
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 692686f1584d0..0083714b2cafe 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -424,8 +424,8 @@ public:
   }
 
 #if _LIBCPP_STD_VER <= 17
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool
-  operator!=(const __bit_iterator& __x, const __bit_iterator& __y) {
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool operator!=(const __bit_iterator& __x, const __bit_iterator& __y) {
     return !(__x == __y);
   }
 
@@ -573,12 +573,13 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n > 0) {
-      if (__value)
-        __impl<true>(__first, __n);
-      else
-        __impl<false>(__first, __n);
-    }
+    if (__n < 0) [[__unlikely__]]
+      return __first;
+
+    if (__value)
+      __impl<true>(__first, __n);
+    else
+      __impl<false>(__first, __n);
     return __first + __n;
   }
 };
diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
new file mode 100644
index 0000000000000..53e59401430fa
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<class InputIterator, class Size, class Function>
+//    constexpr InputIterator
+//    for_each_n(InputIterator first, Size n, Function f);
+//
+// [alg.foreach] requires `n >= 0`; passing a negative count is a precondition violation.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <algorithm>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  int a[] = {1, 2, 3};
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp
new file mode 100644
index 0000000000000..e57f342e760a6
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<input_iterator I, class Proj = identity,
+//          indirectly_unary_invocable<projected<I, Proj>> Fun>
+//   constexpr ranges::for_each_n_result<I, Fun>
+//     ranges::for_each_n(I first, iter_difference_t<I> n, Fun f, Proj proj = {});
+//
+// [alg.foreach] requires `n >= 0`; passing a negative count is a precondition violation.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <algorithm>
+#include <array>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  std::array a = {1, 2, 3};
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::ranges::for_each_n(a.begin(), -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::ranges::for_each_n(a.begin(), -10000000, [](int) {}), "for_each_n requires a non-negative count");
+
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 183f38c1ce117..52d99f018110b 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -61,6 +61,15 @@ struct TestIterators {
       assert(base(r) == ib + N / 2);
       for (unsigned i = 0; i < N / 2; ++i)
         assert(ia[i] == ib[i]);
+
+      { // A negative count is a no-op that returns the unchanged output iterator.
+        // Regression test for https://llvm.org/PR193613.
+        int source[]  = {1, 2, 3};
+        int dest[]    = {-1, -2, -3};
+        OutIter ret   = std::copy_n(InIter(source), -5, OutIter(dest));
+        assert(base(ret) == dest);
+        assert(dest[0] == -1 && dest[1] == -2 && dest[2] == -3);
+      }
     }
   };
 };
@@ -81,6 +90,13 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
     for (std::size_t i = 0; i < N; ++i)
       assert(out[i + 4] == in[i]);
   }
+  { // Negative count test
+    std::vector<bool> source(N, true);
+    std::vector<bool> dest(N, false);
+    std::vector<bool>::iterator r = std::copy_n(source.begin(), -5, dest.begin());
+    assert(r == dest.begin());
+    assert(dest == std::vector<bool>(N, false));
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
index 132993dba76b9..5e073026ed8db 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
@@ -60,6 +60,16 @@ constexpr void test_iterators() {
     assert(base(ret.in) == in.data());
     assert(base(ret.out) == out.data());
   }
+
+  { // A negative count is a no-op that returns the unchanged iterators.
+    // Regression test for https://llvm.org/PR193613.
+    std::array in{1, 2, 3};
+    std::array out{-1, -2, -3};
+    std::same_as<std::ranges::in_out_result<In, Out>> auto ret = std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
+    assert(base(ret.in) == in.data());
+    assert(base(ret.out) == out.data());
+    assert((out == std::array{-1, -2, -3}));
+  }
 }
 
 #if TEST_STD_VER >= 23
@@ -79,6 +89,13 @@ constexpr bool test_vector_bool(std::size_t N) {
     for (std::size_t i = 0; i < N; ++i)
       assert(out[i + 4] == in[i]);
   }
+  { // Negative count test
+    std::vector<bool> source(N, true);
+    std::vector<bool> dest(N, false);
+    auto ret = std::ranges::copy_n(source.begin(), -5, dest.begin());
+    assert(ret.out == dest.begin());
+    assert(dest == std::vector<bool>(N, false));
+  }
 
   return true;
 };
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
index 6d7ba98a33b65..cc9aaeb04d7e8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
@@ -52,6 +52,14 @@ struct Test {
       std::array<T, 4> expected = {1, 5, 5, 4};
       test<Iter>(in, 1, 2, 5, expected);
     }
+    { // A negative count is a no-op that returns the unchanged iterator.
+      // Regression test for https://llvm.org/PR193613.
+      std::array<T, 4> in       = {1, 2, 3, 4};
+      std::array<T, 4> expected = {1, 2, 3, 4};
+      Iter it                   = std::fill_n(Iter(in.data()), -5, T(9));
+      assert(base(it) == in.data());
+      assert(in == expected);
+    }
   }
 };
 
@@ -126,6 +134,13 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
     }
   }
 
+  { // Negative count test
+    std::vector<bool> v(N, false);
+    std::vector<bool>::iterator r = std::fill_n(v.begin(), -5, true);
+    assert(r == v.begin());
+    assert(v == std::vector<bool>(N, false));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
index 0de6f4a625136..a8eb6c4eb9737 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
@@ -51,6 +51,14 @@ constexpr void test_iterators() {
     auto ret = std::ranges::fill_n(It(a.data()), 0, 1);
     assert(base(ret) == a.data());
   }
+
+  { // A negative count is a no-op that returns the unchanged iterator.
+    // Regression test for https://llvm.org/PR193613.
+    int a[3]                            = {-1, -2, -3};
+    std::same_as<It> decltype(auto) ret = std::ranges::fill_n(It(a), -5, 1);
+    assert(base(ret) == a);
+    assert(a[0] == -1 && a[1] == -2 && a[2] == -3);
+  }
 }
 
 // The `ranges::{fill, fill_n}` algorithms require `vector<bool, Alloc>::iterator` to satisfy
@@ -99,6 +107,13 @@ constexpr bool test_vector_bool(std::size_t N) {
     }
   }
 
+  { // Negative count test
+    std::vector<bool> v(N, false);
+    auto r = std::ranges::fill_n(v.begin(), -5, true);
+    assert(r == v.begin());
+    assert(v == std::vector<bool>(N, false));
+  }
+
   return true;
 }
 #endif
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
index 525737ceacfbf..0e38a89613aa8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
@@ -70,6 +70,13 @@ test()
     test2<Iter, float>();
     test2<Iter, double>();  // this is PR#35498
     test2<Iter, long double>();
+
+    { // A negative count is a no-op that returns the unchanged iterator.
+      // Regression test for https://llvm.org/PR193613.
+      int ia[] = {1, 2, 3};
+      assert(std::generate_n(Iter(ia), -5, gen_test()) == Iter(ia));
+      assert(ia[0] == 1 && ia[1] == 2 && ia[2] == 3);
+    }
 }
 
 void deque_test() {
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
index 33d40a39cf385..6154ab240c3d6 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
@@ -92,6 +92,20 @@ constexpr void test_iter() {
   test_one<Iter>(std::array{-10, -20, -30, -40, -50}, 3, gen, {1, 2, 3, -40, -50});
   // Longer sequence, n = 5.
   test_one<Iter>(std::array<int, 5>{}, 5, gen, {1, 2, 3, 4, 5});
+
+  { // A negative count is a no-op that returns the unchanged iterator.
+    // Regression test for https://llvm.org/PR193613.
+    std::array in{-10, -20, -30};
+    int called        = 0;
+    auto counting_gen = [&called] {
+      ++called;
+      return 42;
+    };
+    std::same_as<Iter> decltype(auto) result = std::ranges::generate_n(Iter(in.data()), -5, counting_gen);
+    assert(base(result) == in.data());
+    assert(called == 0);
+    assert((in == std::array{-10, -20, -30}));
+  }
 }
 
 constexpr void test_iterators() {
@@ -131,7 +145,6 @@ constexpr bool test() {
     assert(gen_invocations == N2);
   }
 
-
   return true;
 }
 

>From 892279c90e066c4af5e0ef600fe47df0ba9dbf9e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 17:31:39 -0400
Subject: [PATCH 2/6] Fix check

---
 libcxx/include/__bit_reference | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 0083714b2cafe..b07c7ddccd5a0 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -573,7 +573,7 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n < 0) [[__unlikely__]]
+    if (__n <= 0) [[__unlikely__]]
       return __first;
 
     if (__value)

>From 87de9ba731d496c0900154847a077591dde9c906 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 17:36:19 -0400
Subject: [PATCH 3/6] Revert to != 0

---
 libcxx/include/__algorithm/copy_n.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index a0ea02121b70b..7ce0ec53b0c53 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -42,7 +42,7 @@ template <class _AlgPolicy,
           __enable_if_t<!__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
-  while (__n > 0) {
+  while (__n != 0) {
     *__result = *__first;
     ++__first;
     ++__result;

>From 86c6915b6d53b0ffde7b3a2ccb197b05f28c85ee Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 12:28:02 -0400
Subject: [PATCH 4/6] Drop [[__unlikely__]]

---
 libcxx/include/__algorithm/copy_n.h        | 4 ++--
 libcxx/include/__algorithm/ranges_copy_n.h | 2 +-
 libcxx/include/__bit_reference             | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index 7ce0ec53b0c53..7f9a109a763ce 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -63,7 +63,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0) [[__unlikely__]]
+  if (__converted < 0)
     return __result;
 
   *__result = *__first;
@@ -84,7 +84,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0) [[__unlikely__]]
+  if (__converted < 0)
     return __result;
   return std::__copy_n<_ClassicAlgPolicy>(__first, __iterator_difference_type<_InputIterator>(__converted), __result)
       .__out_;
diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
index c153e74009801..7baf1932bb3d8 100644
--- a/libcxx/include/__algorithm/ranges_copy_n.h
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -38,7 +38,7 @@ struct __copy_n {
     requires indirectly_copyable<_Ip, _Op>
   _LIBCPP_HIDE_FROM_ABI constexpr copy_n_result<_Ip, _Op>
   operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const {
-    if (__n < 0) [[__unlikely__]]
+    if (__n < 0)
       return {std::move(__first), std::move(__result)};
     return std::__copy_n<_RangeAlgPolicy>(std::move(__first), __n, std::move(__result));
   }
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index b07c7ddccd5a0..23ff6eaf56684 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -573,7 +573,7 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n <= 0) [[__unlikely__]]
+    if (__n <= 0)
       return __first;
 
     if (__value)

>From 6088d6dfa8ef53749309e627c85783bedb3ab248 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 12:33:12 -0400
Subject: [PATCH 5/6] Format

---
 .../alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp | 6 ++----
 .../alg.modifying.operations/alg.copy/copy_n.pass.cpp       | 6 +++---
 .../alg.copy/ranges.copy_n.pass.cpp                         | 3 ++-
 3 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
index 53e59401430fa..5ae0dbc865b46 100644
--- a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
@@ -26,10 +26,8 @@
 int main(int, char**) {
   int a[] = {1, 2, 3};
 
-  TEST_LIBCPP_ASSERT_FAILURE(
-      std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
-  TEST_LIBCPP_ASSERT_FAILURE(
-      std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
 
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 52d99f018110b..3348fcf358580 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -64,9 +64,9 @@ struct TestIterators {
 
       { // A negative count is a no-op that returns the unchanged output iterator.
         // Regression test for https://llvm.org/PR193613.
-        int source[]  = {1, 2, 3};
-        int dest[]    = {-1, -2, -3};
-        OutIter ret   = std::copy_n(InIter(source), -5, OutIter(dest));
+        int source[] = {1, 2, 3};
+        int dest[]   = {-1, -2, -3};
+        OutIter ret  = std::copy_n(InIter(source), -5, OutIter(dest));
         assert(base(ret) == dest);
         assert(dest[0] == -1 && dest[1] == -2 && dest[2] == -3);
       }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
index 5e073026ed8db..c354076b63363 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
@@ -65,7 +65,8 @@ constexpr void test_iterators() {
     // Regression test for https://llvm.org/PR193613.
     std::array in{1, 2, 3};
     std::array out{-1, -2, -3};
-    std::same_as<std::ranges::in_out_result<In, Out>> auto ret = std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
+    std::same_as<std::ranges::in_out_result<In, Out>> auto ret =
+        std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
     assert(base(ret.in) == in.data());
     assert(base(ret.out) == out.data());
     assert((out == std::array{-1, -2, -3}));

>From d29c394231476873ae920ceaa26d498a3a96eca5 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 12:57:25 -0400
Subject: [PATCH 6/6] [libc++] Fix bug in std::copy_n for n == 0

---
 libcxx/include/__algorithm/copy_n.h           |  2 +-
 .../alg.copy/copy_n.pass.cpp                  | 50 +++++++++++++++++++
 .../alg.fill/fill_n.pass.cpp                  |  2 +
 3 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index 7f9a109a763ce..efd5624d94f67 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -63,7 +63,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0)
+  if (__converted <= 0)
     return __result;
 
   *__result = *__first;
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 3348fcf358580..3e673cee21f69 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -12,9 +12,12 @@
 //   constexpr OutIter   // constexpr after C++17
 //   copy_n(InIter first, InIter::difference_type n, OutIter result);
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 #include <algorithm>
 #include <cassert>
 #include <cstdint>
+#include <iterator>
 #include <vector>
 
 #include "test_macros.h"
@@ -24,6 +27,31 @@
 
 typedef UserDefinedIntegral<unsigned> UDI;
 
+// A minimal single-pass input iterator that counts how many times it is advanced. Used to verify that
+// copy_n reads exactly n elements (advancing the iterator only n - 1 times).
+struct CountingInputIterator {
+  using iterator_category = std::input_iterator_tag;
+  using value_type        = int;
+  using difference_type   = long;
+  using pointer           = const int*;
+  using reference         = const int&;
+
+  const int* base_;
+  int* increments_;
+
+  TEST_CONSTEXPR_CXX14 reference operator*() const { return *base_; }
+  TEST_CONSTEXPR_CXX14 CountingInputIterator& operator++() {
+    ++base_;
+    ++*increments_;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX14 CountingInputIterator operator++(int) {
+    CountingInputIterator __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+};
+
 class PaddedBase {
 public:
   TEST_CONSTEXPR PaddedBase(std::int16_t a, std::int8_t b) : a_(a), b_(b) {}
@@ -130,6 +158,28 @@ TEST_CONSTEXPR_CXX20 bool test() {
     assert(test_vector_bool(256));
   }
 
+  { // For a single-pass input iterator, copy_n reads exactly n elements -- advancing the iterator only
+    // n - 1 times. For n == 0, it shouldn't advance the iterator. See 99847d2bf132.
+    int in[] = {1, 2, 3, 4, 5};
+
+    { // n == 0 is a no-op
+      int out[3]     = {-1, -2, -3};
+      int increments = 0;
+      int* r         = std::copy_n(CountingInputIterator{in, &increments}, 0, out);
+      assert(r == out);
+      assert(increments == 0);
+      assert(out[0] == -1 && out[1] == -2 && out[2] == -3);
+    }
+    { // n > 0 advances the iterator exactly n - 1 times
+      int out[3]     = {0, 0, 0};
+      int increments = 0;
+      int* r         = std::copy_n(CountingInputIterator{in, &increments}, 3, out);
+      assert(r == out + 3);
+      assert(increments == 2);
+      assert(out[0] == 1 && out[1] == 2 && out[2] == 3);
+    }
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
index cc9aaeb04d7e8..5a9214923577a 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
@@ -13,6 +13,8 @@
 //   constexpr OutputIterator      // constexpr after C++17
 //   fill_n(Iter first, Size n, const T& value);
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 #include <algorithm>
 #include <array>
 #include <cassert>


From libcxx-commits at lists.llvm.org  Thu Jul  2 12:11:31 2026
From: libcxx-commits at lists.llvm.org (Benjamin Luke via libcxx-commits)
Date: Thu, 02 Jul 2026 12:11:31 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libc] [libcxx] [clang][X86] Emit AVX
 level mismatch psABI warnings on function definitions (PR #199091)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/199091@github.com>
Message-ID: <6a46b7e3.1a4df1a5.118deb.5f7d@mx.google.com>


freaknbigpanda wrote:

Note that the current failures on my PR are due to libc++ issues on main, I'll rebase and make sure all tests are green before merging

https://github.com/llvm/llvm-project/pull/199091

From libcxx-commits at lists.llvm.org  Thu Jul  2 12:11:46 2026
From: libcxx-commits at lists.llvm.org (Benjamin Luke via libcxx-commits)
Date: Thu, 02 Jul 2026 12:11:46 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libc] [libcxx] [clang][X86] Emit AVX
 level mismatch psABI warnings on function definitions (PR #199091)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/199091@github.com>
Message-ID: <6a46b7f2.af6ba3ed.2c2f30.6973@mx.google.com>


freaknbigpanda wrote:

@AaronBallman Can you help to finish this review

https://github.com/llvm/llvm-project/pull/199091

From libcxx-commits at lists.llvm.org  Thu Jul  2 12:47:32 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 12:47:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add assert test for string
	assign/append (PR #207164)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207164@github.com>
Message-ID: <6a46c054.3157459c.24b6e5.0b57@mx.google.com>


https://github.com/ldionne approved this pull request.

This LGTM, thanks for following up!

https://github.com/llvm/llvm-project/pull/207164

From libcxx-commits at lists.llvm.org  Thu Jul  2 12:59:32 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 12:59:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use _LIBCPP_NO_UNIQUE_ADDRESS
 for the new vector layout (PR #207149)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207149@github.com>
Message-ID: <6a46c324.7d41f3a1.d1cf7.2933@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/207149

From libcxx-commits at lists.llvm.org  Thu Jul  2 13:02:59 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 13:02:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some unnecessary
 functions from __vector_layout (PR #207152)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207152@github.com>
Message-ID: <6a46c3f3.cbc3f03a.3be201.c3eb@mx.google.com>


https://github.com/ldionne commented:

I tend to like that: minimizing the number of functions in the layout classes is a good thing. I had been under the impression that this actually made a difference codegen wise, but I guess it makes sense that it doesn't.

I'd like to look again with green CI and to chat about this during a live review to make sure there's no chance of this regressing perf.

https://github.com/llvm/llvm-project/pull/207152

From libcxx-commits at lists.llvm.org  Thu Jul  2 13:09:14 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 13:09:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::find_first_of (PR #206328)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206328@github.com>
Message-ID: <6a46c56a.af6ba3ed.2c2f30.9579@mx.google.com>


================
@@ -181,6 +183,25 @@ struct __is_partitioned<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    using _FindIf = __dispatch<__find_if, __current_configuration, _ExecutionPolicy>;
+    using _Ref1   = __iterator_reference<_ForwardIterator1>;
+    using _Ref2   = __iterator_reference<_ForwardIterator2>;
+    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
+      return std::any_of(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); });
+    });
----------------
ldionne wrote:

Yeah, I think that's the best approach for now.

For some reason I thought `any_of` was built on top of `find_if`, that's why I've been talking about these vectorization opportunities in the first place. Oh well, we should implement `any_of` as `find_if` anyway.

https://github.com/llvm/llvm-project/pull/206328

From libcxx-commits at lists.llvm.org  Thu Jul  2 13:47:34 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 13:47:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::find_first_of (PR #206328)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206328@github.com>
Message-ID: <6a46ce66.14d9b9dc.140829.3ce1@mx.google.com>


================
@@ -23,6 +25,8 @@
 #include <__iterator/next.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
+#include <__type_traits/desugars_to.h>
+#include <__type_traits/remove_cvref.h>
----------------
ldionne wrote:

```suggestion
```

https://github.com/llvm/llvm-project/pull/206328

From libcxx-commits at lists.llvm.org  Thu Jul  2 13:47:43 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 13:47:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::find_first_of (PR #206328)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206328@github.com>
Message-ID: <6a46ce6f.eea6ab6e.1256b6.09c7@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/206328

>From 80420b031a2664ffaaac079b74864f6f035d0810 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 28 Jun 2026 11:21:56 +0100
Subject: [PATCH 01/12] Added implementation of find_first_of on top of
 __find_if

---
 libcxx/include/__algorithm/pstl.h             | 22 +++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++
 libcxx/include/__pstl/backends/default.h      | 19 ++++
 .../pstl.find_first_of.pass.cpp               | 87 +++++++++++++++++++
 4 files changed, 135 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 10625ea3f8e3d..7d5b20ad96c1d 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -679,6 +679,28 @@ is_sorted(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterato
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a52e6db954d0c..cf64e190ad4ed 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -109,6 +109,13 @@ struct __is_partitioned;
 // optional<bool>
 // operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __find_first_of;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2>
+// optional<_ForwardIterator1>
+// operator()(_Policy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __for_each;
 // template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index be90715af13b5..4fcd9b55e5066 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -12,6 +12,7 @@
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
+#include <__algorithm/find.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
 #include <__config>
@@ -57,6 +58,7 @@ namespace __pstl {
 // - all_of
 // - none_of
 // - is_partitioned
+// - find_first_of
 //
 // for_each family
 // ---------------
@@ -181,6 +183,23 @@ struct __is_partitioned<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2) const noexcept {
+    using _FindIf = __dispatch<__find_if, __current_configuration, _ExecutionPolicy>;
+    using _Ref    = __iterator_reference<_ForwardIterator1>;
+    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref __element) {
+      return std::find(__first2, __last2, __element) != __last2;
+    });
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
new file mode 100644
index 0000000000000..c4093daa71ba7
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator1 find_first_of(ExecutionPolicy&& exec,
+//                                  ForwardIterator1 first1,
+//                                  ForwardIterator1 last1,
+//                                  ForwardIterator2 first2,
+//                                  ForwardIterator2 last2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]     = {0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a)) == Iter(a));
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a)) == Iter(a + sa));
+    }
+    {
+      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
+      unsigned sa = sizeof(ia) / sizeof(ia[0]);
+      int ib[]    = {1, 3, 5, 7};
+      unsigned sb = sizeof(ib) / sizeof(ib[0]);
+      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
+      int ic[] = {7};
+      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
+      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
+      assert(std::find_first_of(Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
+    }
+    {
+      int a[8192];
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      std::iota(a, a + sa, 1);
+      a[1023]     = -1;
+      a[2048]     = -1;
+      a[3071]     = -1;
+      int b[]     = {-1, 999999};
+      unsigned sb = sizeof(b) / sizeof(b[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb)) == Iter(a + 1023));
+    }
+    {
+      int a[1073];
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      std::iota(a, a + sa, 0);
+      int b[]     = {1070, 1071, 1072, -1};
+      unsigned sb = sizeof(b) / sizeof(b[0]);
+      for (unsigned i = 0; i < sa; i = i <= 16 ? i + 1 : unsigned(3.1415 * i)) {
+        a[i] = -1;
+        assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb)) == Iter(a + i));
+        a[i] = i;
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
+                                       types::bidirectional_iterator_list<int*>,
+                                       types::random_access_iterator_list<int*>>{},
+                  TestIteratorWithPolicies<Test>{});
+
+  return 0;
+}

>From c4185be74a100792d2e08987b3991397446a39d1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 28 Jun 2026 12:08:03 +0100
Subject: [PATCH 02/12] Added a predicate version of find_first_of

---
 libcxx/include/__algorithm/pstl.h             | 28 +++++-
 libcxx/include/__pstl/backend_fwd.h           |  4 +-
 libcxx/include/__pstl/backends/default.h      | 14 +--
 .../pstl.find_first_of_pred.pass.cpp          | 95 +++++++++++++++++++
 4 files changed, 132 insertions(+), 9 deletions(-)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 7d5b20ad96c1d..84418222def52 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -698,7 +698,33 @@ template <class _ExecutionPolicy,
       std::move(__first1),
       std::move(__last1),
       std::move(__first2),
-      std::move(__last2));
+      std::move(__last2),
+      std::equal_to<>{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index cf64e190ad4ed..1ea121367fbc7 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -111,10 +111,10 @@ struct __is_partitioned;
 
 template <class _Backend, class _ExecutionPolicy>
 struct __find_first_of;
-// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2>
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
 // optional<_ForwardIterator1>
 // operator()(_Policy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
-//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2) const noexcept;
+//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2, _Predicate __pred) const noexcept;
 
 template <class _Backend, class _ExecutionPolicy>
 struct __for_each;
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 4fcd9b55e5066..6306b051f9dc4 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -9,10 +9,10 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 #define _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 
+#include <__algorithm/any_of.h>
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
-#include <__algorithm/find.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
 #include <__config>
@@ -185,17 +185,19 @@ struct __is_partitioned<__default_backend_tag, _ExecutionPolicy> {
 
 template <class _ExecutionPolicy>
 struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
-  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2>
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
   operator()(_Policy&& __policy,
              _ForwardIterator1 __first1,
              _ForwardIterator1 __last1,
              _ForwardIterator2 __first2,
-             _ForwardIterator2 __last2) const noexcept {
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
     using _FindIf = __dispatch<__find_if, __current_configuration, _ExecutionPolicy>;
-    using _Ref    = __iterator_reference<_ForwardIterator1>;
-    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref __element) {
-      return std::find(__first2, __last2, __element) != __last2;
+    using _Ref1   = __iterator_reference<_ForwardIterator1>;
+    using _Ref2   = __iterator_reference<_ForwardIterator2>;
+    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
+      return std::any_of(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); });
     });
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
new file mode 100644
index 0000000000000..00cb0f149a399
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   ForwardIterator1 find_first_of(ExecutionPolicy&& exec,
+//                                  ForwardIterator1 first1,
+//                                  ForwardIterator1 last1,
+//                                  ForwardIterator2 first2,
+//                                  ForwardIterator2 last2,
+//                                  BinaryPredicate p);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct Pred {
+  bool operator()(int l, int r) const {
+    return l + 1 == r; // ensures that the predicate is not equivalent to std::equal_to
+  }
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]     = {0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a), Pred{}) == Iter(a));
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a), Pred{}) == Iter(a + sa));
+    }
+    {
+      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
+      unsigned sa = sizeof(ia) / sizeof(ia[0]);
+      int ib[]    = {2, 4, 6, 8};
+      unsigned sb = sizeof(ib) / sizeof(ib[0]);
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb), Pred{}) == Iter(ia + 1));
+      int ic[] = {7};
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1), Pred{}) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic), Pred{}) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1), Pred{}) == Iter(ia));
+    }
+    {
+      int a[8192];
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      std::iota(a, a + sa, 1);
+      a[1023]     = -2;
+      a[2048]     = -2;
+      a[3071]     = -2;
+      int b[]     = {-1, 999999};
+      unsigned sb = sizeof(b) / sizeof(b[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb), Pred{}) == Iter(a + 1023));
+    }
+    {
+      int a[1073];
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      std::iota(a, a + sa, 0);
+      int b[]     = {1070, 1071, 1072, -1};
+      unsigned sb = sizeof(b) / sizeof(b[0]);
+      for (unsigned i = 0; i < sa; i = i <= 16 ? i + 1 : unsigned(3.1415 * i)) {
+        a[i] = -2;
+        assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb), Pred{}) == Iter(a + i));
+        a[i] = i;
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
+                                       types::bidirectional_iterator_list<int*>,
+                                       types::random_access_iterator_list<int*>>{},
+                  TestIteratorWithPolicies<Test>{});
+
+  return 0;
+}

>From 92e67cdebae5293ae478ecc9d385585e1f9b8d55 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 28 Jun 2026 12:30:10 +0100
Subject: [PATCH 03/12] Added verification for iterator types, nodiscard and
 noexcept

---
 .../pstl.iterator-requirements.verify.cpp         |  7 +++++++
 .../libcxx/algorithms/pstl.nodiscard.verify.cpp   |  4 ++++
 .../algorithms/pstl.exception_handling.pass.cpp   | 15 +++++++++++++++
 3 files changed, 26 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e5bd7e764c59b..1abaf5cc7b193 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -89,6 +89,13 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::find_if_not(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_if_not}}
   }
 
+  {
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_first_of}}
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
+  }
+
   {
     (void)std::for_each(pol, non_fwd, non_fwd, func); // expected-error@*:* {{static assertion failed: for_each}}
     (void)std::for_each_n(pol, non_fwd, n, func);     // expected-error@*:* {{static assertion failed: for_each_n}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index b6554cef6f32a..747d62bc66837 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -47,6 +47,10 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::find(std::execution::par, std::begin(a), std::end(a), 1);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::find_first_of(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::find_first_of(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_partitioned(std::execution::par, std::begin(a), std::end(a), pred);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a3db159289393..9e6c8f5b8ce40 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -175,6 +175,21 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; });
+
+        // find_first_of(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::find_first_of(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // find_first_of(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::find_first_of(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), pred);
+        });
+      }
+
       {
         auto func = maybe_throw(tokens[5], [](int) {});
 

>From 65aa1ad5d9dd25a930e0cca5ba0c1d59fb3e8aa4 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 28 Jun 2026 13:09:11 +0100
Subject: [PATCH 04/12] Added a missing policy in the unit test

---
 .../alg.find.first.of/pstl.find_first_of.pass.cpp         | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
index c4093daa71ba7..5768a8cf1f3c0 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -45,11 +45,11 @@ struct Test {
       unsigned sa = sizeof(ia) / sizeof(ia[0]);
       int ib[]    = {1, 3, 5, 7};
       unsigned sb = sizeof(ib) / sizeof(ib[0]);
-      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
       int ic[] = {7};
-      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
-      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
-      assert(std::find_first_of(Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
     }
     {
       int a[8192];

>From 18881aea45556d321505c64ef3ebcbab78c331ec Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 30 Jun 2026 22:10:58 +0100
Subject: [PATCH 05/12] Addressed the feedback

---
 libcxx/include/__algorithm/pstl.h             | 96 +++++++++----------
 .../pstl.iterator-requirements.verify.cpp     |  8 +-
 .../pstl.find_first_of.pass.cpp               | 89 ++++++++++-------
 .../pstl.find_first_of_pred.pass.cpp          | 89 ++++++++++-------
 4 files changed, 168 insertions(+), 114 deletions(-)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 84418222def52..3530b3207f59e 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -293,6 +293,54 @@ find(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __l
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::equal_to<>{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator,
           class _Function,
@@ -679,54 +727,6 @@ is_sorted(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterato
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
 }
 
-template <class _ExecutionPolicy,
-          class _ForwardIterator1,
-          class _ForwardIterator2,
-          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
-          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
-    _ExecutionPolicy&& __policy,
-    _ForwardIterator1 __first1,
-    _ForwardIterator1 __last1,
-    _ForwardIterator2 __first2,
-    _ForwardIterator2 __last2) {
-  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
-  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
-  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
-  return __pstl::__handle_exception<_Implementation>(
-      std::forward<_ExecutionPolicy>(__policy),
-      std::move(__first1),
-      std::move(__last1),
-      std::move(__first2),
-      std::move(__last2),
-      std::equal_to<>{});
-}
-
-template <class _ExecutionPolicy,
-          class _ForwardIterator1,
-          class _ForwardIterator2,
-          class _BinaryPredicate,
-          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
-          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
-    _ExecutionPolicy&& __policy,
-    _ForwardIterator1 __first1,
-    _ForwardIterator1 __last1,
-    _ForwardIterator2 __first2,
-    _ForwardIterator2 __last2,
-    _BinaryPredicate __pred) {
-  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
-  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
-  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
-  return __pstl::__handle_exception<_Implementation>(
-      std::forward<_ExecutionPolicy>(__policy),
-      std::move(__first1),
-      std::move(__last1),
-      std::move(__first2),
-      std::move(__last2),
-      std::move(__pred));
-}
-
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 1abaf5cc7b193..aee3f97019071 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -91,9 +91,13 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
 
   {
     (void)std::find_first_of(
-        pol, non_fwd, non_fwd, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_first_of}}
+        pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_first_of}}
     (void)std::find_first_of(
-        pol, non_fwd, non_fwd, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: find_first_of}}
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
   }
 
   {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
index 5768a8cf1f3c0..ea2fdc6eebdef 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -30,58 +30,83 @@
 #include "test_macros.h"
 #include "type_algorithms.h"
 
-template <class Iter>
+EXECUTION_POLICY_SFINAE_TEST(find_first_of);
+
+static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable&& callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    using std::begin;
+    using std::end;
     {
-      int a[]     = {0};
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a)) == Iter(a));
-      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a)) == Iter(a + sa));
+      int a[] = {0};
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(begin(a)), Iter2(begin(a)), Iter2(begin(a))) ==
+             Iter1(begin(a)));
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(a)), Iter2(begin(a))) ==
+             Iter1(end(a)));
     }
     {
-      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
-      unsigned sa = sizeof(ia) / sizeof(ia[0]);
-      int ib[]    = {1, 3, 5, 7};
-      unsigned sb = sizeof(ib) / sizeof(ib[0]);
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
+      int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int ib[] = {1, 3, 5, 7};
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ib)), Iter2(end(ib))) ==
+             Iter1(begin(ia) + 1));
       int ic[] = {7};
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(end(ic))) ==
+             Iter1(end(ia)));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(begin(ic))) ==
+             Iter1(end(ia)));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(begin(ia)), Iter2(begin(ic)), Iter2(end(ic))) ==
+             Iter1(begin(ia)));
     }
     {
       int a[8192];
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      std::iota(a, a + sa, 1);
-      a[1023]     = -1;
-      a[2048]     = -1;
-      a[3071]     = -1;
-      int b[]     = {-1, 999999};
-      unsigned sb = sizeof(b) / sizeof(b[0]);
-      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb)) == Iter(a + 1023));
+      std::iota(begin(a), end(a), 1);
+      a[1023] = -1;
+      a[2048] = -1;
+      a[3071] = -1;
+      int b[] = {-1, 999999};
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b))) ==
+             Iter1(begin(a) + 1023));
     }
     {
       int a[1073];
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      std::iota(a, a + sa, 0);
-      int b[]     = {1070, 1071, 1072, -1};
-      unsigned sb = sizeof(b) / sizeof(b[0]);
-      for (unsigned i = 0; i < sa; i = i <= 16 ? i + 1 : unsigned(3.1415 * i)) {
+      std::iota(begin(a), end(a), 0);
+      int b[] = {1073, 1074, 1075, -1};
+      runway_sample(std::size(a), [&](size_t i) {
         a[i] = -1;
-        assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb)) == Iter(a + i));
+        assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b))) ==
+               Iter1(begin(a) + i));
         a[i] = i;
-      }
+      });
     }
   }
 };
 
 int main(int, char**) {
-  types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
-                                       types::bidirectional_iterator_list<int*>,
-                                       types::random_access_iterator_list<int*>>{},
-                  TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies< types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
 
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
index 00cb0f149a399..770d0109c4f4a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -32,64 +32,89 @@
 #include "test_macros.h"
 #include "type_algorithms.h"
 
+EXECUTION_POLICY_SFINAE_TEST(find_first_of);
+
+static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
 struct Pred {
   bool operator()(int l, int r) const {
     return l + 1 == r; // ensures that the predicate is not equivalent to std::equal_to
   }
 };
 
-template <class Iter>
+template <class Callable>
+void runway_sample(size_t size, Callable&& callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    using std::begin;
+    using std::end;
     {
-      int a[]     = {0};
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a), Pred{}) == Iter(a));
-      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a), Pred{}) == Iter(a + sa));
+      int a[] = {0};
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(begin(a)), Iter2(begin(a)), Iter2(begin(a)), Pred{}) ==
+             Iter1(begin(a)));
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(a)), Iter2(begin(a)), Pred{}) ==
+             Iter1(end(a)));
     }
     {
-      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
-      unsigned sa = sizeof(ia) / sizeof(ia[0]);
-      int ib[]    = {2, 4, 6, 8};
-      unsigned sb = sizeof(ib) / sizeof(ib[0]);
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb), Pred{}) == Iter(ia + 1));
+      int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int ib[] = {2, 4, 6, 8};
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ib)), Iter2(end(ib)), Pred{}) ==
+             Iter1(begin(ia) + 1));
       int ic[] = {7};
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1), Pred{}) == Iter(ia + sa));
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic), Pred{}) == Iter(ia + sa));
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1), Pred{}) == Iter(ia));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(end(ic)), Pred{}) ==
+             Iter1(end(ia)));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(begin(ic)), Pred{}) ==
+             Iter1(end(ia)));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(begin(ia)), Iter2(begin(ic)), Iter2(end(ic)), Pred{}) ==
+             Iter1(begin(ia)));
     }
     {
       int a[8192];
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      std::iota(a, a + sa, 1);
-      a[1023]     = -2;
-      a[2048]     = -2;
-      a[3071]     = -2;
-      int b[]     = {-1, 999999};
-      unsigned sb = sizeof(b) / sizeof(b[0]);
-      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb), Pred{}) == Iter(a + 1023));
+      std::iota(begin(a), end(a), 1);
+      a[1023] = -2;
+      a[2048] = -2;
+      a[3071] = -2;
+      int b[] = {-1, 999999};
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b)), Pred{}) ==
+             Iter1(begin(a) + 1023));
     }
     {
       int a[1073];
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      std::iota(a, a + sa, 0);
-      int b[]     = {1070, 1071, 1072, -1};
-      unsigned sb = sizeof(b) / sizeof(b[0]);
-      for (unsigned i = 0; i < sa; i = i <= 16 ? i + 1 : unsigned(3.1415 * i)) {
+      std::iota(begin(a), end(a), 0);
+      int b[] = {1074, 1075, 1076, -1};
+      runway_sample(std::size(a), [&](size_t i) {
         a[i] = -2;
-        assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb), Pred{}) == Iter(a + i));
+        assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b)), Pred{}) ==
+               Iter1(begin(a) + i));
         a[i] = i;
-      }
+      });
     }
   }
 };
 
 int main(int, char**) {
-  types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
-                                       types::bidirectional_iterator_list<int*>,
-                                       types::random_access_iterator_list<int*>>{},
-                  TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies< types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
 
   return 0;
 }

>From fa3700e14b9c5f311df716d57f018a94a3438b2e Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 30 Jun 2026 22:42:34 +0100
Subject: [PATCH 06/12] Missing include

---
 .../alg.find.first.of/pstl.find_first_of.pass.cpp                | 1 +
 .../alg.find.first.of/pstl.find_first_of_pred.pass.cpp           | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
index ea2fdc6eebdef..0ad0bbfdd700a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -22,6 +22,7 @@
 #include <algorithm>
 #include <cassert>
 #include <functional>
+#include <iterator>
 #include <limits>
 #include <numeric>
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
index 770d0109c4f4a..5532ed2f8e075 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -24,6 +24,7 @@
 #include <algorithm>
 #include <cassert>
 #include <functional>
+#include <iterator>
 #include <limits>
 #include <numeric>
 

>From 8dd415ea0a27cb125486a9b32f4350d5b45c18f1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 1 Jul 2026 21:21:11 +0100
Subject: [PATCH 07/12] Branch off to std::find() if the predicate is equality,
 call into more general std::find_if() otherwise.

---
 libcxx/include/__pstl/backends/default.h      | 12 +++-
 .../pstl.find_first_of.pass.cpp               | 48 ++++++++-------
 .../pstl.find_first_of_pred.pass.cpp          | 61 ++++++++++++-------
 3 files changed, 77 insertions(+), 44 deletions(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 6306b051f9dc4..a5cd1d1b09912 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -9,10 +9,11 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 #define _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 
-#include <__algorithm/any_of.h>
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
+#include <__algorithm/find.h>
+#include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
 #include <__config>
@@ -24,6 +25,8 @@
 #include <__iterator/next.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
+#include <__type_traits/desugars_to.h>
+#include <__type_traits/remove_cvref.h>
 #include <__utility/empty.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -197,7 +200,12 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
     using _Ref1   = __iterator_reference<_ForwardIterator1>;
     using _Ref2   = __iterator_reference<_ForwardIterator2>;
     return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
-      return std::any_of(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); });
+      if constexpr (__desugars_to_v<__equal_tag, _Predicate, __remove_cvref_t<_Ref1>, __remove_cvref_t<_Ref2>>) {
+        // bypass an equality predicate and call directly to std::find() to allow more vectorization
+        return std::find(__first2, __last2, __element) != __last2;
+      } else {
+        return std::find_if(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); }) != __last2;
+      }
     });
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
index 0ad0bbfdd700a..118b34da0cb6c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -37,7 +37,7 @@ static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*>);
 
 template <class Callable>
-void runway_sample(size_t size, Callable&& callable) {
+void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
   // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
   for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
@@ -55,46 +55,52 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
-    using std::begin;
-    using std::end;
     {
       int a[] = {0};
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(begin(a)), Iter2(begin(a)), Iter2(begin(a))) ==
-             Iter1(begin(a)));
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(a)), Iter2(begin(a))) ==
-             Iter1(end(a)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)), Iter2(std::begin(a))) ==
+             Iter1(std::begin(a)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), Iter2(std::begin(a))) ==
+             Iter1(std::end(a)));
     }
     {
       int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
       int ib[] = {1, 3, 5, 7};
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ib)), Iter2(end(ib))) ==
-             Iter1(begin(ia) + 1));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Iter2(std::end(ib))) ==
+             Iter1(std::begin(ia) + 1));
       int ic[] = {7};
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(end(ic))) ==
-             Iter1(end(ia)));
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(begin(ic))) ==
-             Iter1(end(ia)));
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(begin(ia)), Iter2(begin(ic)), Iter2(end(ic))) ==
-             Iter1(begin(ia)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ic)), Iter2(std::end(ic))) ==
+             Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ic)), Iter2(std::begin(ic))) ==
+             Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ic)), Iter2(std::end(ic))) ==
+             Iter1(std::begin(ia)));
     }
     {
       int a[8192];
-      std::iota(begin(a), end(a), 1);
+      std::iota(std::begin(a), std::end(a), 1);
       a[1023] = -1;
       a[2048] = -1;
       a[3071] = -1;
       int b[] = {-1, 999999};
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b))) ==
-             Iter1(begin(a) + 1023));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))) ==
+             Iter1(std::begin(a) + 1023));
     }
     {
       int a[1073];
-      std::iota(begin(a), end(a), 0);
+      std::iota(std::begin(a), std::end(a), 0);
       int b[] = {1073, 1074, 1075, -1};
       runway_sample(std::size(a), [&](size_t i) {
         a[i] = -1;
-        assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b))) ==
-               Iter1(begin(a) + i));
+        assert(std::find_first_of(
+                   policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))) ==
+               Iter1(std::begin(a) + i));
         a[i] = i;
       });
     }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
index 5532ed2f8e075..1033b52e4621f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -45,7 +45,7 @@ struct Pred {
 };
 
 template <class Callable>
-void runway_sample(size_t size, Callable&& callable) {
+void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
   // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
   for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
@@ -63,46 +63,65 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
-    using std::begin;
-    using std::end;
     {
       int a[] = {0};
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(begin(a)), Iter2(begin(a)), Iter2(begin(a)), Pred{}) ==
-             Iter1(begin(a)));
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(a)), Iter2(begin(a)), Pred{}) ==
-             Iter1(end(a)));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)), Iter2(std::begin(a)), Pred{}) ==
+          Iter1(std::begin(a)));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), Iter2(std::begin(a)), Pred{}) ==
+          Iter1(std::end(a)));
     }
     {
       int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
       int ib[] = {2, 4, 6, 8};
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ib)), Iter2(end(ib)), Pred{}) ==
-             Iter1(begin(ia) + 1));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Iter2(std::end(ib)), Pred{}) ==
+          Iter1(std::begin(ia) + 1));
       int ic[] = {7};
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(end(ic)), Pred{}) ==
-             Iter1(end(ia)));
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(begin(ic)), Pred{}) ==
-             Iter1(end(ia)));
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(begin(ia)), Iter2(begin(ic)), Iter2(end(ic)), Pred{}) ==
-             Iter1(begin(ia)));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ic)), Iter2(std::end(ic)), Pred{}) ==
+          Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy,
+                 Iter1(std::begin(ia)),
+                 Iter1(std::end(ia)),
+                 Iter2(std::begin(ic)),
+                 Iter2(std::begin(ic)),
+                 Pred{}) == Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy,
+                 Iter1(std::begin(ia)),
+                 Iter1(std::begin(ia)),
+                 Iter2(std::begin(ic)),
+                 Iter2(std::end(ic)),
+                 Pred{}) == Iter1(std::begin(ia)));
     }
     {
       int a[8192];
-      std::iota(begin(a), end(a), 1);
+      std::iota(std::begin(a), std::end(a), 1);
       a[1023] = -2;
       a[2048] = -2;
       a[3071] = -2;
       int b[] = {-1, 999999};
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b)), Pred{}) ==
-             Iter1(begin(a) + 1023));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{}) ==
+             Iter1(std::begin(a) + 1023));
     }
     {
       int a[1073];
-      std::iota(begin(a), end(a), 0);
+      std::iota(std::begin(a), std::end(a), 0);
       int b[] = {1074, 1075, 1076, -1};
       runway_sample(std::size(a), [&](size_t i) {
         a[i] = -2;
-        assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b)), Pred{}) ==
-               Iter1(begin(a) + i));
+        assert(
+            std::find_first_of(
+                policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{}) ==
+            Iter1(std::begin(a) + i));
         a[i] = i;
       });
     }

>From f850d3d6b613a8ed59102a4fb3add7863766412f Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 1 Jul 2026 22:55:54 +0100
Subject: [PATCH 08/12] <numeric> pulls in more headers via
 <__algorithm/find.h>

---
 libcxx/test/libcxx/transitive_includes/cxx23.csv | 3 +++
 libcxx/test/libcxx/transitive_includes/cxx26.csv | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 8385b37c07822..53ae6c9b4e591 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -704,11 +704,14 @@ mutex typeinfo
 mutex version
 new version
 numbers version
+numeric cctype
 numeric climits
 numeric compare
 numeric cstdint
 numeric cstring
 numeric ctime
+numeric cwchar
+numeric cwctype
 numeric initializer_list
 numeric limits
 numeric optional
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index cf1154a251f23..849c9b0ce932b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -664,11 +664,14 @@ mutex tuple
 mutex version
 new version
 numbers version
+numeric cctype
 numeric climits
 numeric compare
 numeric cstdint
 numeric cstring
 numeric ctime
+numeric cwchar
+numeric cwctype
 numeric initializer_list
 numeric limits
 numeric optional

>From 645820c13db62c99a27d253902acd441488156e0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 1 Jul 2026 23:09:04 +0100
Subject: [PATCH 09/12] Expanded tests cases of the serial versions a bit,
 supporting down to C++03

---
 .../alg.find.first.of/find_first_of.pass.cpp  |  60 ++++++----
 .../find_first_of_pred.pass.cpp               | 104 +++++++++++-------
 2 files changed, 105 insertions(+), 59 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
index efa54bbc8fd66..4740029d4c22b 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
@@ -16,18 +16,19 @@
 #include <algorithm>
 #include <cassert>
 #include <iterator>
+#include <numeric>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
 #if TEST_STD_VER > 17
 TEST_CONSTEXPR bool test_constexpr() {
-    int ia[] = {1, 2, 3};
-    int ib[] = {7, 8, 9};
-    int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
+  int ia[] = {1, 2, 3};
+  int ib[] = {7, 8, 9};
+  int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
     typedef forward_iterator<int*>       FI;
-    typedef bidirectional_iterator<int*> BI;
-    typedef random_access_iterator<int*> RI;
+  typedef bidirectional_iterator<int*> BI;
+  typedef random_access_iterator<int*> RI;
 
     return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia))) == FI(ic+1))
            && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib))) == FI(std::end(ic)))
@@ -36,39 +37,56 @@ TEST_CONSTEXPR bool test_constexpr() {
            && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia))) == RI(ic+1))
            && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib))) == RI(std::end(ic)))
            ;
-    }
+}
 #endif
 
-int main(int, char**)
-{
-    int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
-    const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-    int ib[] = {1, 3, 5, 7};
-    const unsigned sb = sizeof(ib)/sizeof(ib[0]);
+int main(int, char**) {
+  {
+    int ia[]          = {0, 1, 2, 3, 0, 1, 2, 3};
+    const unsigned sa = sizeof(ia) / sizeof(ia[0]);
+    int ib[]          = {1, 3, 5, 7};
+    const unsigned sb = sizeof(ib) / sizeof(ib[0]);
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia + sa),
                               forward_iterator<const int*>(ib),
-                              forward_iterator<const int*>(ib + sb)) ==
-                              cpp17_input_iterator<const int*>(ia+1));
+                              forward_iterator<const int*>(ib + sb)) == cpp17_input_iterator<const int*>(ia + 1));
     int ic[] = {7};
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia + sa),
                               forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic + 1)) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
+                              forward_iterator<const int*>(ic + 1)) == cpp17_input_iterator<const int*>(ia + sa));
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia + sa),
                               forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic)) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
+                              forward_iterator<const int*>(ic)) == cpp17_input_iterator<const int*>(ia + sa));
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia),
                               forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic+1)) ==
-                              cpp17_input_iterator<const int*>(ia));
+                              forward_iterator<const int*>(ic + 1)) == cpp17_input_iterator<const int*>(ia));
+  }
+  {
+    int a[8192];
+    std::iota(std::begin(a), std::end(a), 1);
+    a[1023] = -1;
+    a[2048] = -1;
+    a[3071] = -1;
+    int b[] = {-1, 999999};
+    assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b)) == std::begin(a) + 1023);
+  }
+  {
+    int a[1073];
+    const unsigned sa = sizeof(a) / sizeof(a[0]);
+    std::iota(std::begin(a), std::end(a), 0);
+    int b[] = {1073, 1074, 1075, -1};
+    for (unsigned i = 0; i < sa; i = i < 16 ? i + 1 : unsigned(3.1415 * i)) {
+      a[i] = -1;
+      assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b)) == std::begin(a) + i);
+      a[i] = i;
+    }
+  }
 
 #if TEST_STD_VER > 17
-    static_assert(test_constexpr());
+  static_assert(test_constexpr());
 #endif
 
   return 0;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
index fd2e4d426fbf9..25699f4b5c216 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
@@ -18,19 +18,26 @@
 #include <cassert>
 #include <functional>
 #include <iterator>
+#include <numeric>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
+struct Pred {
+  bool operator()(int l, int r) const {
+    return l + 1 == r; // ensures that the predicate is not equivalent to std::equal_to
+  }
+};
+
 #if TEST_STD_VER > 17
 constexpr bool test_constexpr() {
-    int ia[] = {1, 2, 3};
-    int ib[] = {7, 8, 9};
-    int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
+  int ia[] = {1, 2, 3};
+  int ib[] = {7, 8, 9};
+  int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
     typedef forward_iterator<int*>       FI;
-    typedef bidirectional_iterator<int*> BI;
-    typedef random_access_iterator<int*> RI;
-    std::equal_to<int> eq{};
+  typedef bidirectional_iterator<int*> BI;
+  typedef random_access_iterator<int*> RI;
+  std::equal_to<int> eq{};
     return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia)), eq) == FI(ic+1))
            && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib)), eq) == FI(std::end(ic)))
            && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia)), eq) == BI(ic+1))
@@ -38,43 +45,64 @@ constexpr bool test_constexpr() {
            && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia)), eq) == RI(ic+1))
            && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib)), eq) == RI(std::end(ic)))
            ;
-    }
+}
 #endif
 
-int main(int, char**)
-{
-    int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
-    const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-    int ib[] = {1, 3, 5, 7};
-    const unsigned sb = sizeof(ib)/sizeof(ib[0]);
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia + sa),
-                              forward_iterator<const int*>(ib),
-                              forward_iterator<const int*>(ib + sb),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia+1));
+int main(int, char**) {
+  {
+    int ia[]          = {0, 1, 2, 3, 0, 1, 2, 3};
+    const unsigned sa = sizeof(ia) / sizeof(ia[0]);
+    int ib[]          = {1, 3, 5, 7};
+    const unsigned sb = sizeof(ib) / sizeof(ib[0]);
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia + sa),
+               forward_iterator<const int*>(ib),
+               forward_iterator<const int*>(ib + sb),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia + 1));
     int ic[] = {7};
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia + sa),
-                              forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic + 1),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia + sa),
-                              forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia),
-                              forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic+1),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia));
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia + sa),
+               forward_iterator<const int*>(ic),
+               forward_iterator<const int*>(ic + 1),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia + sa));
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia + sa),
+               forward_iterator<const int*>(ic),
+               forward_iterator<const int*>(ic),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia + sa));
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia),
+               forward_iterator<const int*>(ic),
+               forward_iterator<const int*>(ic + 1),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia));
+  }
+  {
+    int a[8192];
+    std::iota(std::begin(a), std::end(a), 1);
+    a[1023] = -2;
+    a[2048] = -2;
+    a[3071] = -2;
+    int b[] = {-1, 999999};
+    assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b), Pred()) == std::begin(a) + 1023);
+  }
+  {
+    int a[1073];
+    const unsigned sa = sizeof(a) / sizeof(a[0]);
+    std::iota(std::begin(a), std::end(a), 0);
+    int b[] = {1074, 1075, 1076, -1};
+    for (unsigned i = 0; i < sa; i = i < 16 ? i + 1 : unsigned(3.1415 * i)) {
+      a[i] = -2;
+      assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b), Pred()) == std::begin(a) + i);
+      a[i] = i;
+    };
+  }
 
 #if TEST_STD_VER > 17
-    static_assert(test_constexpr());
+  static_assert(test_constexpr());
 #endif
 
   return 0;

>From 12ba4c956bfba72044d2423496800660da679a0f Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 1 Jul 2026 23:12:37 +0100
Subject: [PATCH 10/12] clang-format the serial tests fully

---
 .../alg.find.first.of/find_first_of.pass.cpp  | 21 ++++++++++++-------
 .../find_first_of_pred.pass.cpp               | 21 ++++++++++++-------
 2 files changed, 26 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
index 4740029d4c22b..25a223e274706 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
@@ -26,17 +26,22 @@ TEST_CONSTEXPR bool test_constexpr() {
   int ia[] = {1, 2, 3};
   int ib[] = {7, 8, 9};
   int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
-    typedef forward_iterator<int*>       FI;
+  typedef forward_iterator<int*> FI;
   typedef bidirectional_iterator<int*> BI;
   typedef random_access_iterator<int*> RI;
 
-    return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia))) == FI(ic+1))
-           && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib))) == FI(std::end(ic)))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia))) == BI(ic+1))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib))) == BI(std::end(ic)))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia))) == RI(ic+1))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib))) == RI(std::end(ic)))
-           ;
+  return (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia))) ==
+          FI(ic + 1)) &&
+         (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib))) ==
+          FI(std::end(ic))) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia))) ==
+          BI(ic + 1)) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib))) ==
+          BI(std::end(ic))) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia))) ==
+          RI(ic + 1)) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib))) ==
+          RI(std::end(ic)));
 }
 #endif
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
index 25699f4b5c216..9fdc4081b0141 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
@@ -34,17 +34,22 @@ constexpr bool test_constexpr() {
   int ia[] = {1, 2, 3};
   int ib[] = {7, 8, 9};
   int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
-    typedef forward_iterator<int*>       FI;
+  typedef forward_iterator<int*> FI;
   typedef bidirectional_iterator<int*> BI;
   typedef random_access_iterator<int*> RI;
   std::equal_to<int> eq{};
-    return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia)), eq) == FI(ic+1))
-           && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib)), eq) == FI(std::end(ic)))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia)), eq) == BI(ic+1))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib)), eq) == BI(std::end(ic)))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia)), eq) == RI(ic+1))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib)), eq) == RI(std::end(ic)))
-           ;
+  return (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia)), eq) ==
+          FI(ic + 1)) &&
+         (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib)), eq) ==
+          FI(std::end(ic))) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia)), eq) ==
+          BI(ic + 1)) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib)), eq) ==
+          BI(std::end(ic))) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia)), eq) ==
+          RI(ic + 1)) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib)), eq) ==
+          RI(std::end(ic)));
 }
 #endif
 

>From 12be042391977e8ba12419b3371e5650d90b2a9e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 16:15:40 -0400
Subject: [PATCH 11/12] Update libcxx/include/__pstl/backends/default.h

---
 libcxx/include/__pstl/backends/default.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index a5cd1d1b09912..86f3899dc8ab1 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -200,7 +200,7 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
     using _Ref1   = __iterator_reference<_ForwardIterator1>;
     using _Ref2   = __iterator_reference<_ForwardIterator2>;
     return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
-      if constexpr (__desugars_to_v<__equal_tag, _Predicate, __remove_cvref_t<_Ref1>, __remove_cvref_t<_Ref2>>) {
+      if constexpr (__desugars_to_v<__equal_tag, _Predicate, _Ref1, _Ref2>) {
         // bypass an equality predicate and call directly to std::find() to allow more vectorization
         return std::find(__first2, __last2, __element) != __last2;
       } else {

>From de6f3e6097065c937b4b2aa6f8efc417e11d6a8d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 16:47:28 -0400
Subject: [PATCH 12/12] Update libcxx/include/__pstl/backends/default.h

---
 libcxx/include/__pstl/backends/default.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 86f3899dc8ab1..cb963a130f743 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -26,7 +26,6 @@
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
 #include <__type_traits/desugars_to.h>
-#include <__type_traits/remove_cvref.h>
 #include <__utility/empty.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>


From libcxx-commits at lists.llvm.org  Thu Jul  2 13:48:50 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 13:48:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [WIP][libc++] Implement find_first_of in
 terms of find_if or find (PR #207276)
Message-ID: <llvm.org/llvm/llvm-project/pull/207276@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/207276

This allows taking advantage of optimizations in find_if and find in that algorithm.

Closes #129319

>From b636d2308702c846194c588d4040e0c0635fbd1d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 16:47:46 -0400
Subject: [PATCH] [WIP][libc++] Implement find_first_of in terms of find_if or
 find

This allows taking advantage of optimizations in find_if and find
in that algorithm.

Closes #129319
---
 libcxx/include/__algorithm/find_first_of.h    | 36 +++++++++++++++----
 .../__algorithm/ranges_find_first_of.h        |  1 +
 2 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__algorithm/find_first_of.h b/libcxx/include/__algorithm/find_first_of.h
index 45ec133154371..c6f4bdb0874a0 100644
--- a/libcxx/include/__algorithm/find_first_of.h
+++ b/libcxx/include/__algorithm/find_first_of.h
@@ -11,7 +11,11 @@
 #define _LIBCPP___ALGORITHM_FIND_FIRST_OF_H
 
 #include <__algorithm/comp.h>
+#include <__algorithm/find.h>
+#include <__algorithm/find_if.h>
 #include <__config>
+#include <__iterator/iterator_traits.h>
+#include <__type_traits/desugars_to.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -20,16 +24,36 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _ForwardIterator1, class _ForwardIterator2, class _BinaryPredicate>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _ForwardIterator1 __find_first_of_ce(
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _ForwardIterator1 __find_first_of(
     _ForwardIterator1 __first1,
     _ForwardIterator1 __last1,
     _ForwardIterator2 __first2,
     _ForwardIterator2 __last2,
     _BinaryPredicate&& __pred) {
-  for (; __first1 != __last1; ++__first1)
-    for (_ForwardIterator2 __j = __first2; __j != __last2; ++__j)
-      if (__pred(*__first1, *__j))
+  for (; __first1 != __last1; ++__first1) {
+#ifndef _LIBCPP_CXX03_LANG
+
+    using _Ref1 = typename iterator_traits<_ForwardIterator1>::reference;
+    using _Ref2 = typename iterator_traits<_ForwardIterator2>::reference;
+
+    if constexpr (__desugars_to<__equal_tag, _BinaryPredicate, _Ref1, _Ref2>) {
+      _ForwardIterator2 __found = std::find(__first2, __last2, *__first1);
+      if (__found != __last2)
         return __first1;
+    } else {
+      _ForwardIterator2 __found = std::find_if(__first2, __last2, [&](auto&& __x) { return __pred(*__first1, __x); });
+      if (__found != __last2)
+        return __first1;
+    }
+
+#else
+
+    _ForwardIterator2 __found = std::find_if(__first2, __last2, [&](auto&& __x) { return __pred(*__first1, __x); });
+    if (__found != __last2)
+      return __first1;
+
+#endif
+  }
   return __last1;
 }
 
@@ -40,13 +64,13 @@ template <class _ForwardIterator1, class _ForwardIterator2, class _BinaryPredica
     _ForwardIterator2 __first2,
     _ForwardIterator2 __last2,
     _BinaryPredicate __pred) {
-  return std::__find_first_of_ce(__first1, __last1, __first2, __last2, __pred);
+  return std::__find_first_of(__first1, __last1, __first2, __last2, __pred);
 }
 
 template <class _ForwardIterator1, class _ForwardIterator2>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _ForwardIterator1 find_first_of(
     _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2, _ForwardIterator2 __last2) {
-  return std::__find_first_of_ce(__first1, __last1, __first2, __last2, __equal_to());
+  return std::__find_first_of(__first1, __last1, __first2, __last2, __equal_to());
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__algorithm/ranges_find_first_of.h b/libcxx/include/__algorithm/ranges_find_first_of.h
index 102e16dd7a55b..62e4fbbff6738 100644
--- a/libcxx/include/__algorithm/ranges_find_first_of.h
+++ b/libcxx/include/__algorithm/ranges_find_first_of.h
@@ -42,6 +42,7 @@ struct __find_first_of {
       _Pred& __pred,
       _Proj1& __proj1,
       _Proj2& __proj2) {
+    // TODO: also implement this via __find_first_of (requires projection support?)
     for (; __first1 != __last1; ++__first1) {
       for (auto __j = __first2; __j != __last2; ++__j) {
         if (std::invoke(__pred, std::invoke(__proj1, *__first1), std::invoke(__proj2, *__j)))


From libcxx-commits at lists.llvm.org  Thu Jul  2 13:50:03 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 02 Jul 2026 13:50:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [WIP][libc++] Implement find_first_of in
 terms of find_if or find (PR #207276)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207276@github.com>
Message-ID: <6a46cefb.331483e7.5bc1e.9b92@mx.google.com>


ldionne wrote:

CC @mikekazakov @philnik777 

https://github.com/llvm/llvm-project/pull/207276

From libcxx-commits at lists.llvm.org  Thu Jul  2 15:47:14 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Thu, 02 Jul 2026 15:47:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
 `adjacent_transform_view` (PR #205900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205900@github.com>
Message-ID: <6a46ea72.f732ad81.1d8ca.aaf8@mx.google.com>


================

----------------
Zingam wrote:

```suggestion
  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
```

https://github.com/llvm/llvm-project/pull/205900

From libcxx-commits at lists.llvm.org  Thu Jul  2 15:47:14 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Thu, 02 Jul 2026 15:47:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
 `adjacent_transform_view` (PR #205900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205900@github.com>
Message-ID: <6a46ea72.bf38ae28.21951a.2aad@mx.google.com>


================
@@ -0,0 +1,94 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Test the libc++ extension that std::ranges::adjacent_transform_view and std::views::adjacent_transform are marked as [[nodiscard]].
+
+#include <functional>
+#include <ranges>
+#include <utility>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+void test() {
+  auto v = View{} | std::views::adjacent_transform<2>(std::multiplies());
+
+  // [range.adjacent.transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).size();
+
+  // [range.adjacent.transform.iterator]
+
+  auto it   = v.begin();
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it[0];
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  0 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.adjacent.tranform.sentinel]
+
+  auto st = v.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
+  // [range.adjacent.overview]
+
+  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<0>(range);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<2>(range, std::multiplies());
----------------
Zingam wrote:

```suggestion
  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  static_assert(std::ranges::forward_range<decltype(range)>);

  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
  std::views::adjacent_transform<0>(range, Fn{});

  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
  std::views::adjacent_transform<2>(range, std::multiplies());

  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
  std::views::adjacent_transform<2>(std::multiplies());
```

There are three operators. Take `Fn` from `adaptor.pass.cpp`:
```c++
  struct Fn {
    int operator()(auto...) const;
  };
```

https://github.com/llvm/llvm-project/pull/205900

From libcxx-commits at lists.llvm.org  Thu Jul  2 15:53:46 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Thu, 02 Jul 2026 15:53:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Enable CPO compile tests
	(PR #207123)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207123@github.com>
Message-ID: <6a46ebfa.576b3af0.2cb9b7.945c@mx.google.com>


https://github.com/Zingam updated https://github.com/llvm/llvm-project/pull/207123

>From 9c0786b1bf196f332c01675bbb89349169c9b666 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 2 Jul 2026 08:57:55 +0300
Subject: [PATCH] [libc++][ranges] Enable CPO compile tests

`adjacent_transform_view` and `stride_view` were implemented but the test cases were omitted.
---
 .../customization.point.object/cpo.compile.pass.cpp           | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..71a0b6ebb804c 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -120,7 +120,7 @@ static_assert(test(std::views::transform, a, [](int x) { return x + 1; }));
 static_assert(test(std::views::values, pairs));
 
 #if TEST_STD_VER >= 23
-// static_assert(test(std::views::adjacent_transform<2>, [](int x, int y) { return x + y; }, a));
+static_assert(test(std::views::adjacent_transform<2>, a, [](int x, int y) { return x + y; }));
 static_assert(test(std::views::adjacent<2>, a));
 // static_assert(test(std::views::as_const, a));
 static_assert(test(std::views::as_rvalue, a));
@@ -130,7 +130,7 @@ static_assert(test(std::views::chunk_by, a, [](int x, int y) { return x < y; }))
 static_assert(test(std::views::enumerate, a));
 static_assert(test(std::views::join_with, 1));
 static_assert(test(std::views::pairwise, a));
-// static_assert(test(std::views::stride, a, 1));
+static_assert(test(std::views::stride, a, 1));
 static_assert(test(std::views::zip_transform, [](int x, int y) { return x + y; }, a, a));
 static_assert(test(std::views::zip, a, a));
 #endif


From libcxx-commits at lists.llvm.org  Thu Jul  2 19:03:17 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Thu, 02 Jul 2026 19:03:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add assert test for string
	assign/append (PR #207164)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207164@github.com>
Message-ID: <6a471865.e55dc341.29c9b8.4ec0@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/207164

>From f3ac7f40e624c78e66041665997897e90b376c86 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Thu, 2 Jul 2026 20:15:54 +0800
Subject: [PATCH 1/2] [libc++] Add assert test for string assign/append

---
 .../string.modifiers/assert.append.pass.cpp   | 39 +++++++++++++++++++
 .../string.modifiers/assert.assign.pass.cpp   | 39 +++++++++++++++++++
 2 files changed, 78 insertions(+)
 create mode 100644 libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
 create mode 100644 libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp

diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
new file mode 100644
index 0000000000000..bbf99ffa8d488
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// basic_string& append(const value_type* s, size_type n);
+// basic_string& append(const value_type* s);
+// basic_string& append(const value_type* s, size_type pos, size_type n);
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <string>
+
+#include "check_assertion.h"
+#include "min_allocator.h"
+
+template <class S>
+void test() {
+  S l1("123");
+  const char* np = nullptr;
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np, 1), "string::append received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np), "string::append received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np, 0, 1), "string::append received nullptr");
+}
+
+int main(int, char**) {
+  test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp
new file mode 100644
index 0000000000000..d5a458a3bd0eb
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// basic_string& assign(const value_type* s, size_type n);
+// basic_string& assign(const value_type* s);
+// basic_string& assign(const value_type* s, size_type pos, size_type n);
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <string>
+
+#include "check_assertion.h"
+#include "min_allocator.h"
+
+template <class S>
+void test() {
+  S l1("123");
+  const char* np = nullptr;
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np, 1), "string::assign received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np), "string::assign received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np, 0, 1), "string::assign received nullptr");
+}
+
+int main(int, char**) {
+  test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+
+  return 0;
+}

>From 3295f500a0f660eb51f790897c4f17cf1c403418 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Thu, 2 Jul 2026 22:18:25 +0800
Subject: [PATCH 2/2] try fixing ci

---
 .../basic.string/string.modifiers/assert.append.pass.cpp       | 3 ++-
 .../basic.string/string.modifiers/assert.assign.pass.cpp       | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
index bbf99ffa8d488..c3745ca0a762d 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
@@ -14,7 +14,8 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: libcpp-hardening-mode=none
+// UNSUPPORTED: libcpp-hardening-mode={{none|fast}}
+// UNSUPPORTED: libcpp-assertion-semantic={{ignore|observe}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 #include <string>
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp
index d5a458a3bd0eb..80abd15929735 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp
@@ -14,7 +14,8 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03
-// UNSUPPORTED: libcpp-hardening-mode=none
+// UNSUPPORTED: libcpp-hardening-mode={{none|fast}}
+// UNSUPPORTED: libcpp-assertion-semantic={{ignore|observe}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 #include <string>


From libcxx-commits at lists.llvm.org  Thu Jul  2 19:22:50 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 19:22:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 4fad93b - [libc++] Use
 _LIBCPP_NO_UNIQUE_ADDRESS for the new vector layout (#207149)
Message-ID: <6a471cfa.92ea0e2a.21f97.7528@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-03T04:22:45+02:00
New Revision: 4fad93b912c954720de1b2b63a0f00679dc96550

URL: https://github.com/llvm/llvm-project/commit/4fad93b912c954720de1b2b63a0f00679dc96550
DIFF: https://github.com/llvm/llvm-project/commit/4fad93b912c954720de1b2b63a0f00679dc96550.diff

LOG: [libc++] Use _LIBCPP_NO_UNIQUE_ADDRESS for the new vector layout (#207149)

We use `_LIBCPP_NO_UNIQUE_ADDRESS`, since a plain
`[[no_unique_address]]` doesn't work on Windows.

Added: 
    

Modified: 
    libcxx/include/__vector/layout.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__vector/layout.h b/libcxx/include/__vector/layout.h
index 65d11aa238df0..910078b1e760e 100644
--- a/libcxx/include/__vector/layout.h
+++ b/libcxx/include/__vector/layout.h
@@ -217,7 +217,7 @@ class __vector_layout {
 #ifdef _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED
   size_type __size_     = 0;
   size_type __capacity_ = 0;
-  [[no_unique_address]] allocator_type __alloc_;
+  _LIBCPP_NO_UNIQUE_ADDRESS allocator_type __alloc_;
 #else
   pointer __end_ = nullptr;
   _LIBCPP_COMPRESSED_PAIR(pointer, __capacity_ = nullptr, allocator_type, __alloc_);


        

From libcxx-commits at lists.llvm.org  Thu Jul  2 19:22:52 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 19:22:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use _LIBCPP_NO_UNIQUE_ADDRESS
 for the new vector layout (PR #207149)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207149@github.com>
Message-ID: <6a471cfc.eea6ab6e.1256b6.d139@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/207149

From libcxx-commits at lists.llvm.org  Thu Jul  2 19:51:17 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 19:51:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a4723a5.481afadf.3c64c3.30be@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/207086

From libcxx-commits at lists.llvm.org  Thu Jul  2 20:02:58 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 20:02:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some unnecessary
 functions from __vector_layout (PR #207152)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207152@github.com>
Message-ID: <6a472662.6cfa33d2.19518c.31b4@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207152

>From b307228e45dca7ae9f4d8cd8678ec15bc672d68f Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 2 Jul 2026 11:53:16 +0200
Subject: [PATCH] [libc++] Remove some unnecessary functions from
 __vector_layout

---
 libcxx/include/__vector/layout.h | 46 --------------------------------
 libcxx/include/__vector/vector.h | 18 ++++++-------
 2 files changed, 9 insertions(+), 55 deletions(-)

diff --git a/libcxx/include/__vector/layout.h b/libcxx/include/__vector/layout.h
index 65d11aa238df0..6be670381b244 100644
--- a/libcxx/include/__vector/layout.h
+++ b/libcxx/include/__vector/layout.h
@@ -179,9 +179,6 @@ class __vector_layout {
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type
   __remaining_capacity() const _NOEXCEPT;
 
-  /// Determines if a reallocation is necessary.
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __is_full() const _NOEXCEPT;
-
   /// Sets the member pointing to the first element in the vector to `__new_begin`, the member used
   /// to obtain the vector's bound to the equivalent of `__new_size`, and the member that represents
   /// the vector's capacity to the equivalent to `__new_capacity`.
@@ -202,9 +199,6 @@ class __vector_layout {
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __size() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __capacity() const _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __empty() const _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI _Tp& __back() _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI _Tp const& __back() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __end_ptr() _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer __end_ptr() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_ptr() _NOEXCEPT;
@@ -252,11 +246,6 @@ __vector_layout<_Tp, _Alloc>::__remaining_capacity() const _NOEXCEPT {
   return __capacity_ - __size_;
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__is_full() const _NOEXCEPT {
-  return __size_ == __capacity_;
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __vector_layout<_Tp, _Alloc>::__set_layout(
     pointer __new_begin, size_type __new_size, size_type __new_capacity) _NOEXCEPT {
@@ -324,21 +313,6 @@ __vector_layout<_Tp, _Alloc>::__capacity() const _NOEXCEPT {
   return __capacity_;
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__empty() const _NOEXCEPT {
-  return __size_ == 0;
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __vector_layout<_Tp, _Alloc>::__back() _NOEXCEPT {
-  return __begin_[__size_ - 1];
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp const& __vector_layout<_Tp, _Alloc>::__back() const _NOEXCEPT {
-  return __begin_[__size_ - 1];
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __vector_layout<_Tp, _Alloc>::pointer
 __vector_layout<_Tp, _Alloc>::__end_ptr() _NOEXCEPT {
@@ -384,11 +358,6 @@ __vector_layout<_Tp, _Alloc>::__remaining_capacity() const _NOEXCEPT {
   return __capacity_ - __end_;
 }
 
-template <class _Tp, class _Alloc>
-[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__is_full() const _NOEXCEPT {
-  return __end_ == __capacity_;
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __vector_layout<_Tp, _Alloc>::__set_layout(
     pointer __new_begin, size_type __new_size, size_type __new_capacity) _NOEXCEPT {
@@ -456,21 +425,6 @@ __vector_layout<_Tp, _Alloc>::__capacity() const _NOEXCEPT {
   return static_cast<size_type>(__capacity_ - __begin_);
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__empty() const _NOEXCEPT {
-  return __begin_ == __end_;
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __vector_layout<_Tp, _Alloc>::__back() _NOEXCEPT {
-  return __end_[-1];
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp const& __vector_layout<_Tp, _Alloc>::__back() const _NOEXCEPT {
-  return __end_[-1];
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __vector_layout<_Tp, _Alloc>::pointer
 __vector_layout<_Tp, _Alloc>::__end_ptr() _NOEXCEPT {
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..422c98c300a1d 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -400,7 +400,7 @@ class vector {
     return __layout_.__capacity();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
-    return __layout_.__empty();
+    return size() == 0;
   }
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {
@@ -442,11 +442,11 @@ class vector {
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() _NOEXCEPT {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "back() called on an empty vector");
-    return __layout_.__back();
+    return end()[-1];
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const _NOEXCEPT {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "back() called on an empty vector");
-    return __layout_.__back();
+    return end()[-1];
   }
 
   //
@@ -595,7 +595,7 @@ class vector {
         "vector::__vallocate can only be called on a vector that hasn't allocated memory. This vector either already "
         "owns a buffer, or a deallocation function didn't reset the layout's begin pointer.");
     _LIBCPP_ASSERT_INTERNAL(
-        __layout_.__empty(),
+        size() == 0,
         "vector::__vallocate can only be called on a vector that hasn't allocated memory. This vector either already "
         "owns a buffer, or a deallocation function didn't reset the layout's size.");
     _LIBCPP_ASSERT_INTERNAL(
@@ -1080,7 +1080,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Alloc>
 vector<_Tp, _Alloc>::emplace_back(_Args&&... __args) {
   pointer __end = __layout_.__end_ptr();
   std::__if_likely_else(
-      !__layout_.__is_full(),
+      size() != capacity(),
       [&] {
         __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
         ++__end;
@@ -1134,7 +1134,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(__x);
@@ -1157,7 +1157,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(std::move(__x));
@@ -1178,7 +1178,7 @@ template <class... _Args>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
@@ -1232,7 +1232,7 @@ vector<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __position, _Inpu
   difference_type __off = __position - begin();
   pointer __p           = this->__layout_.__begin_ptr() + __off;
   pointer __old_last    = __layout_.__end_ptr();
-  for (; !__layout_.__is_full() && __first != __last; ++__first)
+  for (; size() != capacity() && __first != __last; ++__first)
     __emplace_back_assume_capacity(*__first);
 
   if (__first == __last)


From libcxx-commits at lists.llvm.org  Thu Jul  2 21:36:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 21:36:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a473c31.d1496011.3d49cb.27dc@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/21] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/21] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/21] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/21] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/21] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/21] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/21] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/21] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/21] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/21] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/21] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/21] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/21] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/21] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/21] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/21] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/21] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/21] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/21] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/21] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/21] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}


From libcxx-commits at lists.llvm.org  Thu Jul  2 21:36:05 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 21:36:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a473c35.af6ba3ed.2c2f30.04cf@mx.google.com>


inquisitivecrystal wrote:

> It looks like you need to enable this CPO test:
> 
> https://github.com/llvm/llvm-project/blob/631cb35f6eefbea40b450ddeb480eb114dbc1b9e/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp#L85

Done!

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Thu Jul  2 22:03:34 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 22:03:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a4742a6.cbc3f03a.3be201.5022@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/21] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/21] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/21] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/21] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/21] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/21] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/21] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/21] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/21] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/21] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/21] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/21] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/21] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/21] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/21] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/21] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/21] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/21] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/21] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/21] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/21] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}


From libcxx-commits at lists.llvm.org  Thu Jul  2 22:03:38 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 22:03:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a4742aa.a87eb5dc.2cf3a1.3950@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Thu Jul  2 22:10:04 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 22:10:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47442c.492e23e4.e71ca.aa44@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/185397

>From 075e4dea22ed41b9012ee103b0b3f2dcd786b764 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 12 Feb 2026 15:08:00 +0100
Subject: [PATCH] [libc++] Refactor the string benchmarks

---
 .../benchmarks/containers/string.bench.cpp    | 1019 ++++++++---------
 1 file changed, 458 insertions(+), 561 deletions(-)

diff --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index 98216d22d0144..e9366293a047b 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Huge does not give us anything that Large doesn't have. Skip it.
-    return Length() == ::Length::Huge;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string.compare(src));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  std::string name() const { return "BM_StringRead" + Temperature::name() + Depth::name() + Length::name(); }
-};
-
-void sanityCheckGeneratedStrings() {
-  for (auto Lhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
-    const auto LhsString = makeString(Lhs);
-    for (auto Rhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
-      if (Lhs > Rhs)
-        continue;
-      const auto RhsString = makeString(Rhs);
-
-      // The smaller one must be a prefix of the larger one.
-      if (RhsString.find(LhsString) != 0) {
-        fprintf(
-            stderr, "Invalid autogenerated strings for sizes (%d,%d).\n", static_cast<int>(Lhs), static_cast<int>(Rhs));
-        std::abort();
-      }
-    }
-  }
-  // Verify the autogenerated diffs
-  for (auto L : {Length::Small, Length::Large, Length::Huge}) {
-    const auto Control = makeString(L);
-    const auto Verify  = [&](std::string Exp, size_t Pos) {
-      // Only change on the Pos char.
-      if (Control[Pos] != Exp[Pos]) {
-        Exp[Pos] = Control[Pos];
-        if (Control == Exp)
-          return;
-      }
-      fprintf(stderr, "Invalid autogenerated diff with size %d\n", static_cast<int>(L));
-      std::abort();
-    };
-    Verify(makeString(L, DiffType::ChangeFirst), 0);
-    Verify(makeString(L, DiffType::ChangeMiddle), Control.size() / 2);
-    Verify(makeString(L, DiffType::ChangeLast), Control.size() - 1);
-  }
-}
-
-int main(int argc, char** argv) {
   benchmark::Initialize(&argc, argv);
-  if (benchmark::ReportUnrecognizedArguments(argc, argv))
-    return 1;
-
-  sanityCheckGeneratedStrings();
-
-  makeCartesianProductBenchmark<StringConstructDestroyCStr, AllLengths, AllOpacity>();
-
-  makeCartesianProductBenchmark<StringAssignStr, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringAssignAsciiz, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringAssignAsciizMix, AllOpacity>();
-
-  makeCartesianProductBenchmark<StringCopy, AllLengths>();
-  makeCartesianProductBenchmark<StringMove, AllLengths>();
-  makeCartesianProductBenchmark<StringDestroy, AllLengths>();
-  makeCartesianProductBenchmark<StringEraseToEnd, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringEraseWithMove, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringRelational, AllRelations, AllLengths, AllLengths, AllDiffTypes>();
-  makeCartesianProductBenchmark<StringRelationalLiteral, AllRelations, AllLengths, AllLengths, AllDiffTypes>();
-  makeCartesianProductBenchmark<StringRead, AllTemperatures, AllDepths, AllLengths>();
   benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
 }


From libcxx-commits at lists.llvm.org  Thu Jul  2 22:11:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 22:11:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] e49c8a0 - [libc++] Base string's
 alignment on __STDCPP_DEFAULT_NEW_ALIGNMENT__ (#171785)
Message-ID: <6a47446d.510f576d.185953.82be@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-03T07:11:04+02:00
New Revision: e49c8a0363267c7b7235aabb3d36ac50e4281c26

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

LOG: [libc++] Base string's alignment on __STDCPP_DEFAULT_NEW_ALIGNMENT__ (#171785)

This allows users to influence how much we overalign `string`s
allocations and tune it to the new/delete implementation via
`-fnew-alignment`. If we don't have `__STDCPP_DEFAULT_NEW_ALGINMENT__`
or we're not using `std::allocator`, we default to an alignment of
`sizeof(void*)`.

Added: 
    libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp

Modified: 
    libcxx/include/__cxx03/string
    libcxx/include/string
    libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
    libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
    libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
    libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
    libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
    libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__cxx03/string b/libcxx/include/__cxx03/string
index a919a59e55d57..e5330fb01c04f 100644
--- a/libcxx/include/__cxx03/string
+++ b/libcxx/include/__cxx03/string
@@ -1623,7 +1623,18 @@ private:
   static _LIBCPP_HIDE_FROM_ABI size_type __align_it(size_type __s) _NOEXCEPT {
     return (__s + (__a - 1)) & ~(__a - 1);
   }
-  enum { __alignment = 8 };
+
+  // GCC currently doesn't define __STDCPP_DEFAULT_NEW_ALIGNMENT__ unconditionally
+  // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123872
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+  static inline const size_t
+      __alignment = __is_default_allocator<allocator_type>::value && __STDCPP_DEFAULT_NEW_ALIGNMENT__ > sizeof(void*)
+                      ? __STDCPP_DEFAULT_NEW_ALIGNMENT__
+                      : sizeof(void*);
+#else
+  static inline const size_t __alignment = sizeof(void*);
+#endif
+
   static _LIBCPP_HIDE_FROM_ABI size_type __recommend(size_type __s) _NOEXCEPT {
     if (__s < __min_cap) {
       return static_cast<size_type>(__min_cap) - 1;

diff  --git a/libcxx/include/string b/libcxx/include/string
index 6516d26e288a9..a297a05813cee 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -2276,7 +2276,17 @@ private:
   static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __align_it(size_type __s) _NOEXCEPT {
     return (__s + (__a - 1)) & ~(__a - 1);
   }
-  enum { __alignment = 8 };
+
+  // GCC currently doesn't define __STDCPP_DEFAULT_NEW_ALIGNMENT__ unconditionally
+  // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123872
+#  ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+  static inline const size_t
+      __alignment = __is_std_allocator_v<allocator_type> && __STDCPP_DEFAULT_NEW_ALIGNMENT__ > sizeof(void*)
+                      ? __STDCPP_DEFAULT_NEW_ALIGNMENT__
+                      : sizeof(void*);
+#  else
+  static inline const size_t __alignment = sizeof(void*);
+#  endif
 
   // This makes sure that we're using a capacity with some extra alignment, since allocators almost always over-align
   // the allocations anyways, improving memory usage. More importantly, this ensures that the lowest bit is never set

diff  --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
index 77da29225957b..367a58897cec0 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
@@ -15,8 +15,11 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
-const std::size_t alignment = 8;
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const std::size_t alginment = 8;
+#endif
 
 int main(int, char**) {
   std::string input_string;

diff  --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
index 73825ef4845f2..15736e4a1ceed 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -17,8 +17,11 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
-static const std::size_t alignment = 8;
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const std::size_t alginment = 8;
+#endif
 
 template <class = int>
 TEST_CONSTEXPR_CXX20 void full_size() {

diff  --git a/libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp b/libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp
new file mode 100644
index 0000000000000..f8906c9fb9827
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Check that std::string bases its alignment on __STDCPP_DEFAULT_NEW_ALIGNMENT__
+
+// UNSUPPORTED: c++03
+
+// RUN: %{build} -faligned-new=32 -DALIGN=32
+// RUN: %{run}
+// RUN: %{build} -faligned-new=16 -DALIGN=16
+// RUN: %{run}
+// RUN: %{build} -faligned-new=8 -DALIGN=8
+// RUN: %{run}
+
+#include <cassert>
+#include <string>
+
+int main(int, char**) {
+  std::string str;
+  str.reserve(25);
+
+  assert(str.capacity() % ALIGN == ALIGN - 1);
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
index 77da29225957b..3c16701cbf860 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
@@ -8,6 +8,13 @@
 
 // <string>
 
+// This test is sensitive what __STDCPP_DEFAULT_NEW_ALIGNMENT__ is set to. Enable aligned-new for GCC so that GCC
+// defines the macro
+// ADDITIONAL_COMPILE_FLAGS(gcc): -faligned-new
+
+// The allocations are done in the dylib, so we need to use an up-to-date one
+// XFAIL: using-built-library-before-llvm-23
+
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
@@ -15,22 +22,21 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
-const std::size_t alignment = 8;
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const std::size_t alignment = 8;
+#endif
 
 int main(int, char**) {
-  std::string input_string;
-  input_string.resize(64, 'a');
-
-  // Call a constructor which selects its size using __recommend.
-  std::string test_string(input_string.data());
+  std::string str(64, 'a');
   const std::size_t expected_align8_size = 71;
 
   // Demonstrate the lesser capacity/allocation size when the alignment requirement is 8.
   if (alignment == 8) {
-    assert(test_string.capacity() == expected_align8_size);
+    assert(str.capacity() == expected_align8_size);
   } else {
-    assert(test_string.capacity() == expected_align8_size + 8);
+    assert(str.capacity() == expected_align8_size + 8);
   }
 
   return 0;

diff  --git a/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
index 6bfcb5d4bfcd8..11bd1a5928d4a 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -17,8 +17,11 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
 static const std::size_t alignment = 8;
+#endif
 
 template <class = int>
 TEST_CONSTEXPR_CXX20 void full_size() {

diff  --git a/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
index 45a52acab464e..b35bed39f2bed 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -8,6 +8,10 @@
 
 // UNSUPPORTED: no-exceptions
 
+// This test is sensitive what __STDCPP_DEFAULT_NEW_ALIGNMENT__ is set to. Enable aligned-new for GCC so that GCC
+// defines the macro
+// ADDITIONAL_COMPILE_FLAGS(gcc): -faligned-new
+
 // <string>
 
 // size_type max_size() const; // constexpr since C++20

diff  --git a/libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp
index 8e5919539d94e..5e6cb68fd9a42 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp
@@ -8,13 +8,8 @@
 
 // UNSUPPORTED: no-exceptions
 
-// This test fails when using a built library that does not contain
-// 15860446a8c3, which changed the return value of max_size(). Without
-// that change, the built library believes the max size to be one greater
-// than it really is, and we fail to throw `length_error` from `string::resize()`,
-// which is explicitly instantiated in the built library.
-//
-// XFAIL: using-built-library-before-llvm-21
+// The allocations are done in the dylib, so we need to use an up-to-date one
+// XFAIL: using-built-library-before-llvm-23
 
 // <string>
 


        

From libcxx-commits at lists.llvm.org  Thu Jul  2 22:11:11 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 22:11:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Base string's alignment on
 __STDCPP_DEFAULT_NEW_ALIGNMENT__ (PR #171785)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171785@github.com>
Message-ID: <6a47446f.fdc477d6.118e5e.d322@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/171785

From libcxx-commits at lists.llvm.org  Thu Jul  2 22:21:04 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 22:21:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Add a clang-tidy
	config (PR #207324)
Message-ID: <llvm.org/llvm/llvm-project/pull/207324@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207324

This essentially copies the `.clang-tidy` file from libc++ into libc++abi, except that the naming conventions are updated to not add underscores.

This isn't enforced for now, but that should be done at some point.


>From be0c0a781b21b8abaa2c617350dc1d41d6da0622 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 07:19:18 +0200
Subject: [PATCH] [libc++abi][NFC] Add a clang-tidy config

---
 libcxxabi/.clang-tidy | 54 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 libcxxabi/.clang-tidy

diff --git a/libcxxabi/.clang-tidy b/libcxxabi/.clang-tidy
new file mode 100644
index 0000000000000..766a0ca125b05
--- /dev/null
+++ b/libcxxabi/.clang-tidy
@@ -0,0 +1,54 @@
+Checks: >
+  bugprone-copy-constructor-init,
+  bugprone-dangling-handle,
+  bugprone-infinite-loop,
+  bugprone-stringview-nullptr,
+  bugprone-use-after-move,
+
+  libcpp-*,
+
+  llvm-include-order,
+  llvm-namespace-comment,
+
+  misc-definitions-in-headers,
+  misc-misplaced-const,
+  misc-non-copyable-objects,
+  misc-uniqueptr-reset-release,
+
+  modernize-loop-convert,
+  modernize-redundant-void-arg,
+  modernize-use-nullptr,
+  modernize-use-override,
+  modernize-use-equals-delete,
+
+  readability-duplicate-include,
+  readability-identifier-naming,
+  readability-function-cognitive-complexity,
+  readability-function-size,
+  readability-misplaced-array-index,
+  readability-redundant-control-flow,
+  readability-redundant-function-ptr-dereference,
+  readability-redundant-preprocessor,
+  readability-simplify-boolean-expr,
+  readability-simplify-subscript-expr,
+  readability-uniqueptr-delete-release,
+
+CheckOptions:
+  - key:   readability-function-cognitive-complexity.Threshold
+    value: 143 # TODO: bring that number down
+  - key:   readability-function-size.LineThreshold
+    value: 194 # TODO: bring that number down
+  - key:   readability-identifier-naming.GetConfigPerFile
+    value: false
+  - key:   readability-identifier-naming.ParameterCase
+    value: lower_case
+  - key:   readability-identifier-naming.PrivateMemberCase
+    value: lower_case
+  - key:   readability-identifier-naming.LocalVariableCase
+    value: lower_case
+  - key:   readability-identifier-naming.TemplateParameterCase
+    value: CamelCase
+  - key:   readability-identifier-naming.ValueTemplateParameterCase
+    value: CamelCase
+  - key:   readability-identifier-naming.ValueTemplateParameterIgnoredRegexp
+    value: (__[a-z_]|_[A-Z]).* # TODO: Converge on a single style for value template parameters


From libcxx-commits at lists.llvm.org  Thu Jul  2 22:21:41 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 22:21:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Add a clang-tidy
	config (PR #207324)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207324@github.com>
Message-ID: <6a4746e5.abb10101.208f3e.2dfb@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxxabi

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

This essentially copies the `.clang-tidy` file from libc++ into libc++abi, except that the naming conventions are updated to not add underscores.

This isn't enforced for now, but that should be done at some point.


---
Full diff: https://github.com/llvm/llvm-project/pull/207324.diff


1 Files Affected:

- (added) libcxxabi/.clang-tidy (+54) 


``````````diff
diff --git a/libcxxabi/.clang-tidy b/libcxxabi/.clang-tidy
new file mode 100644
index 0000000000000..766a0ca125b05
--- /dev/null
+++ b/libcxxabi/.clang-tidy
@@ -0,0 +1,54 @@
+Checks: >
+  bugprone-copy-constructor-init,
+  bugprone-dangling-handle,
+  bugprone-infinite-loop,
+  bugprone-stringview-nullptr,
+  bugprone-use-after-move,
+
+  libcpp-*,
+
+  llvm-include-order,
+  llvm-namespace-comment,
+
+  misc-definitions-in-headers,
+  misc-misplaced-const,
+  misc-non-copyable-objects,
+  misc-uniqueptr-reset-release,
+
+  modernize-loop-convert,
+  modernize-redundant-void-arg,
+  modernize-use-nullptr,
+  modernize-use-override,
+  modernize-use-equals-delete,
+
+  readability-duplicate-include,
+  readability-identifier-naming,
+  readability-function-cognitive-complexity,
+  readability-function-size,
+  readability-misplaced-array-index,
+  readability-redundant-control-flow,
+  readability-redundant-function-ptr-dereference,
+  readability-redundant-preprocessor,
+  readability-simplify-boolean-expr,
+  readability-simplify-subscript-expr,
+  readability-uniqueptr-delete-release,
+
+CheckOptions:
+  - key:   readability-function-cognitive-complexity.Threshold
+    value: 143 # TODO: bring that number down
+  - key:   readability-function-size.LineThreshold
+    value: 194 # TODO: bring that number down
+  - key:   readability-identifier-naming.GetConfigPerFile
+    value: false
+  - key:   readability-identifier-naming.ParameterCase
+    value: lower_case
+  - key:   readability-identifier-naming.PrivateMemberCase
+    value: lower_case
+  - key:   readability-identifier-naming.LocalVariableCase
+    value: lower_case
+  - key:   readability-identifier-naming.TemplateParameterCase
+    value: CamelCase
+  - key:   readability-identifier-naming.ValueTemplateParameterCase
+    value: CamelCase
+  - key:   readability-identifier-naming.ValueTemplateParameterIgnoredRegexp
+    value: (__[a-z_]|_[A-Z]).* # TODO: Converge on a single style for value template parameters

``````````

</details>


https://github.com/llvm/llvm-project/pull/207324

From libcxx-commits at lists.llvm.org  Thu Jul  2 22:39:48 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 22:39:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Return
 derived_object_info from dyn_cast_get_derived_info (PR #207326)
Message-ID: <llvm.org/llvm/llvm-project/pull/207326@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207326

Returning the object is much more idiomatic than having an out parameter.


>From 945b5c2cce2332301e39a4938d051583b3ed69ce Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 07:38:55 +0200
Subject: [PATCH] [libc++abi][NFC] Return derived_object_info from
 dyn_cast_get_derived_info

---
 libcxxabi/src/private_typeinfo.cpp | 38 ++++++++++++++----------------
 1 file changed, 18 insertions(+), 20 deletions(-)

diff --git a/libcxxabi/src/private_typeinfo.cpp b/libcxxabi/src/private_typeinfo.cpp
index d185f2618a7ea..ba3da519238b7 100644
--- a/libcxxabi/src/private_typeinfo.cpp
+++ b/libcxxabi/src/private_typeinfo.cpp
@@ -99,28 +99,27 @@ struct derived_object_info {
 };
 
 /// A helper function that gets (dynamic_ptr, dynamic_type, offset_to_derived) from static_ptr.
-void dyn_cast_get_derived_info(derived_object_info* info, const void* static_ptr)
-{
+derived_object_info dyn_cast_get_derived_info(const void* static_ptr) {
+  derived_object_info info;
 #if __has_feature(cxx_abi_relative_vtable)
-    // The vtable address will point to the first virtual function, which is 8
-    // bytes after the start of the vtable (4 for the offset from top + 4 for
-    // the typeinfo component).
-    const int32_t* vtable =
-        *reinterpret_cast<const int32_t* const*>(static_ptr);
-    info->offset_to_derived = static_cast<std::ptrdiff_t>(vtable[-2]);
-    info->dynamic_ptr = static_cast<const char*>(static_ptr) + info->offset_to_derived;
-
-    // The typeinfo component is now a relative offset to a proxy.
-    int32_t offset_to_ti_proxy = vtable[-1];
-    const uint8_t* ptr_to_ti_proxy =
-        reinterpret_cast<const uint8_t*>(vtable) + offset_to_ti_proxy;
-    info->dynamic_type = *(reinterpret_cast<const __class_type_info* const*>(ptr_to_ti_proxy));
+  // The vtable address will point to the first virtual function, which is 8
+  // bytes after the start of the vtable (4 for the offset from top + 4 for
+  // the typeinfo component).
+  const int32_t* vtable = *reinterpret_cast<const int32_t* const*>(static_ptr);
+  info.offset_to_derived = static_cast<std::ptrdiff_t>(vtable[-2]);
+  info.dynamic_ptr = static_cast<const char*>(static_ptr) + info.offset_to_derived;
+
+  // The typeinfo component is now a relative offset to a proxy.
+  int32_t offset_to_ti_proxy = vtable[-1];
+  const uint8_t* ptr_to_ti_proxy = reinterpret_cast<const uint8_t*>(vtable) + offset_to_ti_proxy;
+  info.dynamic_type = *(reinterpret_cast<const __class_type_info* const*>(ptr_to_ti_proxy));
 #else
   void** vtable = strip_vtable(*static_cast<void** const*>(static_ptr));
-  info->offset_to_derived = reinterpret_cast<ptrdiff_t>(vtable[-2]);
-  info->dynamic_ptr = static_cast<const char*>(static_ptr) + info->offset_to_derived;
-  info->dynamic_type = static_cast<const __class_type_info*>(vtable[-1]);
+  info.offset_to_derived = reinterpret_cast<ptrdiff_t>(vtable[-2]);
+  info.dynamic_ptr = static_cast<const char*>(static_ptr) + info.offset_to_derived;
+  info.dynamic_type = static_cast<const __class_type_info*>(vtable[-1]);
 #endif
+  return info;
 }
 
 /// A helper function for __dynamic_cast that casts a base sub-object pointer
@@ -915,8 +914,7 @@ __dynamic_cast(const void *static_ptr, const __class_type_info *static_type,
                const __class_type_info *dst_type,
                std::ptrdiff_t src2dst_offset) {
     // Get (dynamic_ptr, dynamic_type) from static_ptr
-    derived_object_info derived_info;
-    dyn_cast_get_derived_info(&derived_info, static_ptr);
+    derived_object_info derived_info = dyn_cast_get_derived_info(static_ptr);
 
     // Initialize answer to nullptr.  This will be changed from the search
     //    results if a non-null answer is found.  Regardless, this is what will


From libcxx-commits at lists.llvm.org  Thu Jul  2 22:40:20 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 02 Jul 2026 22:40:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Avoid out parameter
 in dyn_cast_get_derived_info (PR #207326)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207326@github.com>
Message-ID: <6a474b44.8e75e0cb.2e0812.a932@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/207326

From libcxx-commits at lists.llvm.org  Thu Jul  2 22:40:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 02 Jul 2026 22:40:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Avoid out parameter
 in dyn_cast_get_derived_info (PR #207326)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207326@github.com>
Message-ID: <6a474b4a.2020f6c2.1fdf61.b17c@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxxabi

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

Returning the object is much more idiomatic than having an out parameter.


---
Full diff: https://github.com/llvm/llvm-project/pull/207326.diff


1 Files Affected:

- (modified) libcxxabi/src/private_typeinfo.cpp (+18-20) 


``````````diff
diff --git a/libcxxabi/src/private_typeinfo.cpp b/libcxxabi/src/private_typeinfo.cpp
index d185f2618a7ea..ba3da519238b7 100644
--- a/libcxxabi/src/private_typeinfo.cpp
+++ b/libcxxabi/src/private_typeinfo.cpp
@@ -99,28 +99,27 @@ struct derived_object_info {
 };
 
 /// A helper function that gets (dynamic_ptr, dynamic_type, offset_to_derived) from static_ptr.
-void dyn_cast_get_derived_info(derived_object_info* info, const void* static_ptr)
-{
+derived_object_info dyn_cast_get_derived_info(const void* static_ptr) {
+  derived_object_info info;
 #if __has_feature(cxx_abi_relative_vtable)
-    // The vtable address will point to the first virtual function, which is 8
-    // bytes after the start of the vtable (4 for the offset from top + 4 for
-    // the typeinfo component).
-    const int32_t* vtable =
-        *reinterpret_cast<const int32_t* const*>(static_ptr);
-    info->offset_to_derived = static_cast<std::ptrdiff_t>(vtable[-2]);
-    info->dynamic_ptr = static_cast<const char*>(static_ptr) + info->offset_to_derived;
-
-    // The typeinfo component is now a relative offset to a proxy.
-    int32_t offset_to_ti_proxy = vtable[-1];
-    const uint8_t* ptr_to_ti_proxy =
-        reinterpret_cast<const uint8_t*>(vtable) + offset_to_ti_proxy;
-    info->dynamic_type = *(reinterpret_cast<const __class_type_info* const*>(ptr_to_ti_proxy));
+  // The vtable address will point to the first virtual function, which is 8
+  // bytes after the start of the vtable (4 for the offset from top + 4 for
+  // the typeinfo component).
+  const int32_t* vtable = *reinterpret_cast<const int32_t* const*>(static_ptr);
+  info.offset_to_derived = static_cast<std::ptrdiff_t>(vtable[-2]);
+  info.dynamic_ptr = static_cast<const char*>(static_ptr) + info.offset_to_derived;
+
+  // The typeinfo component is now a relative offset to a proxy.
+  int32_t offset_to_ti_proxy = vtable[-1];
+  const uint8_t* ptr_to_ti_proxy = reinterpret_cast<const uint8_t*>(vtable) + offset_to_ti_proxy;
+  info.dynamic_type = *(reinterpret_cast<const __class_type_info* const*>(ptr_to_ti_proxy));
 #else
   void** vtable = strip_vtable(*static_cast<void** const*>(static_ptr));
-  info->offset_to_derived = reinterpret_cast<ptrdiff_t>(vtable[-2]);
-  info->dynamic_ptr = static_cast<const char*>(static_ptr) + info->offset_to_derived;
-  info->dynamic_type = static_cast<const __class_type_info*>(vtable[-1]);
+  info.offset_to_derived = reinterpret_cast<ptrdiff_t>(vtable[-2]);
+  info.dynamic_ptr = static_cast<const char*>(static_ptr) + info.offset_to_derived;
+  info.dynamic_type = static_cast<const __class_type_info*>(vtable[-1]);
 #endif
+  return info;
 }
 
 /// A helper function for __dynamic_cast that casts a base sub-object pointer
@@ -915,8 +914,7 @@ __dynamic_cast(const void *static_ptr, const __class_type_info *static_type,
                const __class_type_info *dst_type,
                std::ptrdiff_t src2dst_offset) {
     // Get (dynamic_ptr, dynamic_type) from static_ptr
-    derived_object_info derived_info;
-    dyn_cast_get_derived_info(&derived_info, static_ptr);
+    derived_object_info derived_info = dyn_cast_get_derived_info(static_ptr);
 
     // Initialize answer to nullptr.  This will be changed from the search
     //    results if a non-null answer is found.  Regardless, this is what will

``````````

</details>


https://github.com/llvm/llvm-project/pull/207326

From libcxx-commits at lists.llvm.org  Fri Jul  3 00:08:12 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 03 Jul 2026 00:08:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Always enable
	-fvisibility=hidden (PR #207333)
Message-ID: <llvm.org/llvm/llvm-project/pull/207333@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207333

Building with `-fvisibility=hidden` is generally a good idea, since it avoids unnecessary relocations when loading a dylib. For libc++abi this should be a no-op, since most things are annotated explicitly. By enabling `-fvisibility=hidden` we can remove these explicit annotations however.


>From 9608f114949d4982f59dc4005aa976f243e6c295 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 09:03:45 +0200
Subject: [PATCH] [libc++abi] Always enable -fvisibility=hidden

---
 libcxxabi/src/CMakeLists.txt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index 1889d7d5a82de..fc66ce7187170 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -195,6 +195,7 @@ if (LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
   # Build with -fsized-deallocation, which is default in recent versions of Clang.
   # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
   target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
+  target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
 
   add_library(cxxabi_shared SHARED)
   set_target_properties(cxxabi_shared
@@ -285,9 +286,9 @@ target_compile_options(cxxabi_static_objects PRIVATE "${LIBCXXABI_ADDITIONAL_COM
 # Build with -fsized-deallocation, which is default in recent versions of Clang.
 # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
 target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fsized-deallocation)
+target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
 
 if(LIBCXXABI_HERMETIC_STATIC_LIBRARY)
-  target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
   # If the hermetic library doesn't define the operator new/delete functions
   # then its code shouldn't declare them with hidden visibility.  They might
   # actually be provided by a shared library at link time.


From libcxx-commits at lists.llvm.org  Fri Jul  3 00:08:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 00:08:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Always enable
	-fvisibility=hidden (PR #207333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207333@github.com>
Message-ID: <6a475fff.70920dee.3514b2.bcba@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxxabi

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

Building with `-fvisibility=hidden` is generally a good idea, since it avoids unnecessary relocations when loading a dylib. For libc++abi this should be a no-op, since most things are annotated explicitly. By enabling `-fvisibility=hidden` we can remove these explicit annotations however.


---
Full diff: https://github.com/llvm/llvm-project/pull/207333.diff


1 Files Affected:

- (modified) libcxxabi/src/CMakeLists.txt (+2-1) 


``````````diff
diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index 1889d7d5a82de..fc66ce7187170 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -195,6 +195,7 @@ if (LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
   # Build with -fsized-deallocation, which is default in recent versions of Clang.
   # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
   target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
+  target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
 
   add_library(cxxabi_shared SHARED)
   set_target_properties(cxxabi_shared
@@ -285,9 +286,9 @@ target_compile_options(cxxabi_static_objects PRIVATE "${LIBCXXABI_ADDITIONAL_COM
 # Build with -fsized-deallocation, which is default in recent versions of Clang.
 # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
 target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fsized-deallocation)
+target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
 
 if(LIBCXXABI_HERMETIC_STATIC_LIBRARY)
-  target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
   # If the hermetic library doesn't define the operator new/delete functions
   # then its code shouldn't declare them with hidden visibility.  They might
   # actually be provided by a shared library at link time.

``````````

</details>


https://github.com/llvm/llvm-project/pull/207333

From libcxx-commits at lists.llvm.org  Fri Jul  3 00:10:17 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 03 Jul 2026 00:10:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use std::to_chars to format
 thread::id and canonicalize the representation across platforms (PR
 #181624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/181624@github.com>
Message-ID: <6a476059.f06f4994.1e3a60.3e51@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/181624

>From 2e58b7e47b434ce2ed43c7f04eeff8dd4875c3a6 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 16 Feb 2026 11:40:25 +0100
Subject: [PATCH] [libc++] Use std::to_string to format thread::id

---
 libcxx/docs/ReleaseNotes/23.rst               |  3 +
 libcxx/include/__thread/formatter.h           |  9 +-
 libcxx/include/__thread/thread.h              | 28 +++---
 libcxx/include/future                         |  5 +
 libcxx/include/module.modulemap.in            |  5 +-
 libcxx/include/thread                         |  1 +
 .../format.functions.pass.cpp                 | 94 +++++++++++++++++++
 .../test/libcxx/transitive_includes/cxx26.csv | 14 ---
 .../format.functions.format.pass.cpp          | 57 -----------
 .../thread.thread.id/format.functions.tests.h | 83 ----------------
 .../format.functions.vformat.pass.cpp         | 48 +++++++---
 .../thread.thread.id/format.pass.cpp          | 16 ++--
 .../thread.thread.id/stream.pass.cpp          |  1 +
 13 files changed, 170 insertions(+), 194 deletions(-)
 create mode 100644 libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp
 delete mode 100644 libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
 delete mode 100644 libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 37aa972d22ea3..f01f38d1bf27e 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -90,6 +90,9 @@ Potentially breaking changes
   including the correct header. To ease the transition ``_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23`` can be defined, which
   includes the removed headers again. This macro will be removed in LLVM 24.
 
+- The output of ``ostream << std::thread::id`` and ``std::format("...", std::thread:id)`` changes on some platforms from
+  hexadecimal to decimal. This is done to have consistent formatting across all platforms libc++ supports.
+
 Announcements About Future Releases
 -----------------------------------
 
diff --git a/libcxx/include/__thread/formatter.h b/libcxx/include/__thread/formatter.h
index 826607d47b469..9682798e1df41 100644
--- a/libcxx/include/__thread/formatter.h
+++ b/libcxx/include/__thread/formatter.h
@@ -51,20 +51,13 @@ struct formatter<__thread_id, _CharT> {
     // On Linux systems pthread_t is an unsigned long long.
     // On Apple systems pthread_t is a pointer type.
     //
-    // Note the output should match what the stream operator does. Since
-    // the ostream operator has been shipped years before this formatter
-    // was added to the Standard, this formatter does what the stream
-    // operator does. This may require platform specific changes.
+    // Note the output should match what the stream operator does.
 
     using _Tp = decltype(__get_underlying_id(__id));
     using _Cp = conditional_t<integral<_Tp>, _Tp, conditional_t<is_pointer_v<_Tp>, uintptr_t, void>>;
     static_assert(!is_same_v<_Cp, void>, "unsupported thread::id type, please file a bug report");
 
     __format_spec::__parsed_specifications<_CharT> __specs = __parser_.__get_parsed_std_specifications(__ctx);
-    if constexpr (is_pointer_v<_Tp>) {
-      __specs.__std_.__alternate_form_ = true;
-      __specs.__std_.__type_           = __format_spec::__type::__hexadecimal_lower_case;
-    }
     return __formatter::__format_integer(reinterpret_cast<_Cp>(__get_underlying_id(__id)), __ctx, __specs);
   }
 
diff --git a/libcxx/include/__thread/thread.h b/libcxx/include/__thread/thread.h
index ee7a65212f31d..24f62bfe5899e 100644
--- a/libcxx/include/__thread/thread.h
+++ b/libcxx/include/__thread/thread.h
@@ -11,12 +11,13 @@
 #define _LIBCPP___THREAD_THREAD_H
 
 #include <__assert>
+#include <__charconv/to_chars_integral.h>
 #include <__condition_variable/condition_variable.h>
 #include <__config>
 #include <__exception/terminate.h>
 #include <__functional/hash.h>
 #include <__functional/unary_function.h>
-#include <__locale>
+#include <__fwd/ostream.h>
 #include <__memory/addressof.h>
 #include <__memory/unique_ptr.h>
 #include <__mutex/mutex.h>
@@ -27,16 +28,15 @@
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_constructible.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/exchange.h>
 #include <__utility/forward.h>
+#include <cstdint>
+#include <limits>
 #include <tuple>
 
-#if _LIBCPP_HAS_LOCALIZATION
-#  include <sstream>
-#endif
-
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
@@ -146,13 +146,19 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, __thread_id __id) {
   //
   // Since various flags in the output stream can affect how the
   // thread id is represented (e.g. numpunct or showbase), we
-  // use a temporary stream instead and just output the thread
-  // id representation as a string.
+  // use `to_chars` directly and output the id as a string.
+
+  static_assert(is_pointer<__libcpp_thread_id>::value || is_integral<__libcpp_thread_id>::value);
+
+  using __int_type = __conditional_t<is_pointer<__libcpp_thread_id>::value, uintptr_t, __libcpp_thread_id>;
 
-  basic_ostringstream<_CharT, _Traits> __sstr;
-  __sstr.imbue(locale::classic());
-  __sstr << __id.__id_;
-  return __os << __sstr.str();
+  static const size_t __buffer_size = numeric_limits<__int_type>::digits10 + 2;
+  char __buffer[__buffer_size];
+  auto __ret =
+      std::__to_chars_integral(__buffer, __buffer + __buffer_size - 1, reinterpret_cast<__int_type>(__id.__id_), 10);
+  _LIBCPP_ASSERT_INTERNAL(__ret.__ec == std::errc(), "to_chars failed!");
+  *__ret.__ptr = '\0';
+  return __os << __buffer;
 }
 #  endif // _LIBCPP_HAS_LOCALIZATION
 
diff --git a/libcxx/include/future b/libcxx/include/future
index c169c6c182d83..57ee783662377 100644
--- a/libcxx/include/future
+++ b/libcxx/include/future
@@ -2077,6 +2077,11 @@ _LIBCPP_POP_MACROS
 #    include <system_error>
 #    include <thread>
 #  endif
+
+#  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 23
+#    include <sstream>
+#  endif
+
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 
 #endif // _LIBCPP_FUTURE
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b9ddfd5bbf4cd..81a07258345e8 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -948,7 +948,10 @@ module std [system] {
       header "__charconv/to_chars_integral.h"
       export std.charconv.to_chars_result
     }
-    module to_chars_result            { header "__charconv/to_chars_result.h" }
+    module to_chars_result            {
+      header "__charconv/to_chars_result.h"
+      export std.system_error.errc
+    }
     module traits                     { header "__charconv/traits.h" }
 
     header "charconv"
diff --git a/libcxx/include/thread b/libcxx/include/thread
index fa50d048330c6..d79db3dcd7791 100644
--- a/libcxx/include/thread
+++ b/libcxx/include/thread
@@ -131,6 +131,7 @@ void sleep_for(const chrono::duration<Rep, Period>& rel_time);
 
 #  if defined(_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23) && _LIBCPP_STD_VER <= 23
 #    include <atomic>
+#    include <sstream>
 #  endif
 
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
diff --git a/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp
new file mode 100644
index 0000000000000..6719ec6b783e6
--- /dev/null
+++ b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-threads
+
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// <thread>
+
+// template<class charT>
+// struct formatter<thread::id, charT>;
+
+// template<class... Args>
+//   string format(format_string<Args...> fmt, Args&&... args);
+// template<class... Args>
+//   wstring format(wformat_string<Args...> fmt, Args&&... args);
+
+#include <cassert>
+#include <format>
+#include <thread>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "format.functions.common.h"
+#include "test_format_string.h"
+#include "test_macros.h"
+
+template <class CharT, class TestFunction>
+void format_tests(TestFunction check) {
+  // Note the output of std::thread::id is unspecified. The output text is the
+  // same as the stream operator. Since that format is already released this
+  // test follows the practice on existing systems.
+  std::thread::id input{};
+
+  /***** Test the type specific part *****/
+  check(SV("0"), SV("{}"), input);
+  check(SV("0^42"), SV("{}^42"), input);
+  check(SV("0^42"), SV("{:}^42"), input);
+
+  // *** align-fill & width ***
+  check(SV("    0"), SV("{:5}"), input);
+  check(SV("0****"), SV("{:*<5}"), input);
+  check(SV("__0__"), SV("{:_^5}"), input);
+  check(SV("::::0"), SV("{::>5}"), input); // This is not a range, so : is allowed as fill character.
+
+  check(SV("    0"), SV("{:{}}"), input, 5);
+  check(SV("0****"), SV("{:*<{}}"), input, 5);
+  check(SV("__0__"), SV("{:_^{}}"), input, 5);
+  check(SV("####0"), SV("{:#>{}}"), input, 5);
+}
+
+auto test = []<class CharT, class... Args>(
+                std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
+  {
+    std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
+  {
+    std::basic_string<CharT> out = std::vformat(fmt.get(), std::make_format_args<context_t<CharT>>(args...));
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
+};
+
+int main(int, char**) {
+  format_tests<char>(test);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  format_tests<wchar_t>(test);
+#endif
+
+  { // Check that we print the ID correctly (i.e. library-internal round-tripping works)
+    auto thread_id = std::this_thread::get_id();
+    auto result    = std::format("{}", thread_id);
+    auto num       = std::stoull(result);
+    using int_t =
+        std::conditional_t<std::is_pointer<std::__libcpp_thread_id>::value, uintptr_t, std::__libcpp_thread_id>;
+    assert(reinterpret_cast<int_t>(__get_underlying_id(std::this_thread::get_id())) == static_cast<int_t>(num));
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index cf1154a251f23..6abceca182a95 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -423,32 +423,23 @@ functional tuple
 functional typeinfo
 functional unordered_map
 functional version
-future bitset
 future cctype
 future cerrno
 future climits
-future clocale
 future compare
-future cstddef
 future cstdint
 future cstdio
-future cstdlib
 future cstring
 future ctime
 future cwchar
 future cwctype
 future initializer_list
-future ios
 future iosfwd
-future istream
 future limits
 future ratio
-future sstream
 future stdexcept
-future streambuf
 future string
 future string_view
-future text_encoding
 future tuple
 future typeinfo
 future version
@@ -1043,7 +1034,6 @@ text_encoding stdexcept
 text_encoding string_view
 text_encoding version
 thread array
-thread bitset
 thread cctype
 thread cerrno
 thread climits
@@ -1058,14 +1048,10 @@ thread ctime
 thread cwchar
 thread cwctype
 thread initializer_list
-thread ios
 thread iosfwd
-thread istream
 thread limits
 thread ratio
-thread sstream
 thread stdexcept
-thread streambuf
 thread string
 thread string_view
 thread text_encoding
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
deleted file mode 100644
index 5618836d89e80..0000000000000
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-threads
-
-// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
-
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
-// <thread>
-
-// template<class charT>
-// struct formatter<thread::id, charT>;
-
-// template<class... Args>
-//   string format(format_string<Args...> fmt, Args&&... args);
-// template<class... Args>
-//   wstring format(wformat_string<Args...> fmt, Args&&... args);
-
-#include <format>
-#include <cassert>
-
-#include "assert_macros.h"
-#include "concat_macros.h"
-#include "format.functions.tests.h"
-#include "test_format_string.h"
-#include "test_macros.h"
-
-auto test = []<class CharT, class... Args>(
-                std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
-  std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
-  TEST_REQUIRE(out == expected,
-               TEST_WRITE_CONCATENATED(
-                   "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
-};
-
-auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, Args&&...) {
-  // After P2216 most exceptions thrown by std::format become ill-formed.
-  // Therefore this tests does nothing.
-};
-
-int main(int, char**) {
-  format_tests<char>(test, test_exception);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  format_tests<wchar_t>(test, test_exception);
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
deleted file mode 100644
index f55f0e2af8cb2..0000000000000
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
+++ /dev/null
@@ -1,83 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
-#define TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
-
-#include <thread>
-
-#include "format.functions.common.h"
-#include "test_macros.h"
-
-template <class CharT, class TestFunction, class ExceptionTest>
-void format_tests(TestFunction check, ExceptionTest check_exception) {
-  // Note the output of std::thread::id is unspecified. The output text is the
-  // same as the stream operator. Since that format is already released this
-  // test follows the practice on existing systems.
-  std::thread::id input{};
-
-  /***** Test the type specific part *****/
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
-  check(SV("0"), SV("{}"), input);
-  check(SV("0^42"), SV("{}^42"), input);
-  check(SV("0^42"), SV("{:}^42"), input);
-
-  // *** align-fill & width ***
-  check(SV("    0"), SV("{:5}"), input);
-  check(SV("0****"), SV("{:*<5}"), input);
-  check(SV("__0__"), SV("{:_^5}"), input);
-  check(SV("::::0"), SV("{::>5}"), input); // This is not a range, so : is allowed as fill character.
-
-  check(SV("    0"), SV("{:{}}"), input, 5);
-  check(SV("0****"), SV("{:*<{}}"), input, 5);
-  check(SV("__0__"), SV("{:_^{}}"), input, 5);
-  check(SV("####0"), SV("{:#>{}}"), input, 5);
-#else  // !defined(__APPLE__) && !defined(__FreeBSD__)
-  check(SV("0x0"), SV("{}"), input);
-  check(SV("0x0^42"), SV("{}^42"), input);
-  check(SV("0x0^42"), SV("{:}^42"), input);
-
-  // *** align-fill & width ***
-  check(SV("    0x0"), SV("{:7}"), input);
-  check(SV("0x0****"), SV("{:*<7}"), input);
-  check(SV("__0x0__"), SV("{:_^7}"), input);
-  check(SV("::::0x0"), SV("{::>7}"), input); // This is not a range, so : is allowed as fill character.
-
-  check(SV("    0x0"), SV("{:{}}"), input, 7);
-  check(SV("0x0****"), SV("{:*<{}}"), input, 7);
-  check(SV("__0x0__"), SV("{:_^{}}"), input, 7);
-  check(SV("####0x0"), SV("{:#>{}}"), input, 7);
-#endif // !defined(__APPLE__) && !defined(__FreeBSD__)
-
-  /***** Test the type generic part *****/
-  check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
-  check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
-
-  // *** sign ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:-}"), input);
-  check_exception("The replacement field misses a terminating '}'", SV("{:+}"), input);
-  check_exception("The replacement field misses a terminating '}'", SV("{: }"), input);
-
-  // *** alternate form ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:#}"), input);
-
-  // *** zero-padding ***
-  check_exception("The width option should not have a leading zero", SV("{:0}"), input);
-
-  // *** precision ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:.}"), input);
-
-  // *** locale-specific form ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:L}"), input);
-
-  // *** type ***
-  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>(""))
-    check_exception("The replacement field misses a terminating '}'", fmt, input);
-}
-
-#endif // TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
index 8555ebd742616..ffe1330fa9c7a 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
@@ -22,21 +22,47 @@
 // string vformat(string_view fmt, format_args args);
 // wstring vformat(wstring_view fmt, wformat_args args);
 
-#include <format>
 #include <cassert>
+#include <format>
+#include <thread>
 
 #include "assert_macros.h"
 #include "concat_macros.h"
-#include "format.functions.tests.h"
+#include "format.functions.common.h"
 #include "test_macros.h"
 
-auto test = []<class CharT, class... Args>(
-                std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) {
-  std::basic_string<CharT> out = std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
-  TEST_REQUIRE(out == expected,
-               TEST_WRITE_CONCATENATED(
-                   "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
-};
+template <class CharT, class ExceptionTest>
+void format_tests(ExceptionTest check_exception) {
+  // Note the output of std::thread::id is unspecified. The output text is the
+  // same as the stream operator. Since that format is already released this
+  // test follows the practice on existing systems.
+  std::thread::id input{};
+
+  /***** Test the type generic part *****/
+  check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
+  check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
+
+  // *** sign ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:-}"), input);
+  check_exception("The replacement field misses a terminating '}'", SV("{:+}"), input);
+  check_exception("The replacement field misses a terminating '}'", SV("{: }"), input);
+
+  // *** alternate form ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:#}"), input);
+
+  // *** zero-padding ***
+  check_exception("The width option should not have a leading zero", SV("{:0}"), input);
+
+  // *** precision ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:L}"), input);
+
+  // *** type ***
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>(""))
+    check_exception("The replacement field misses a terminating '}'", fmt, input);
+}
 
 auto test_exception =
     []<class CharT, class... Args>(
@@ -55,10 +81,10 @@ auto test_exception =
     };
 
 int main(int, char**) {
-  format_tests<char>(test, test_exception);
+  format_tests<char>(test_exception);
 
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  format_tests<wchar_t>(test, test_exception);
+  format_tests<wchar_t>(test_exception);
 #endif
 
   return 0;
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
index 39a257592809d..d3fc6e4b016d6 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
@@ -34,9 +34,8 @@
 
 #define SV(S) MAKE_STRING_VIEW(CharT, S)
 
-template <class StringViewT>
-void test_format(StringViewT expected, std::thread::id arg) {
-  using CharT      = typename StringViewT::value_type;
+template <class CharT>
+std::basic_string<CharT> test_format(std::thread::id arg) {
   using String     = std::basic_string<CharT>;
   using OutIt      = std::back_insert_iterator<String>;
   using FormatCtxT = std::basic_format_context<OutIt, CharT>;
@@ -47,16 +46,15 @@ void test_format(StringViewT expected, std::thread::id arg) {
   OutIt out             = std::back_inserter(result);
   FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>(arg));
   formatter.format(arg, format_ctx);
-  assert(result == expected);
+  assert(!result.empty());
+  return result;
 }
 
 template <class CharT>
 void test_fmt() {
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
-  test_format(SV("0"), std::thread::id());
-#else
-  test_format(SV("0x0"), std::thread::id());
-#endif
+  auto def  = test_format<CharT>(std::thread::id());
+  auto self = test_format<CharT>(std::this_thread::get_id());
+  assert(def != self);
 }
 
 void test() {
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
index 97e797f9a8fbb..ae4df5f5f55a5 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
@@ -27,6 +27,7 @@
 #include <format>
 #include <locale>
 #include <sstream>
+#include <string>
 #include <thread>
 
 #include "make_string.h"


From libcxx-commits at lists.llvm.org  Fri Jul  3 00:54:45 2026
From: libcxx-commits at lists.llvm.org (Vinay Deshmukh via libcxx-commits)
Date: Fri, 03 Jul 2026 00:54:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make `<set>` `std::multiset`
 constexpr as part of P3372R3 (PR #206257)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206257@github.com>
Message-ID: <6a476ac5.dd2b4bd3.35cd5e.e52f@mx.google.com>


https://github.com/vinay-deshmukh updated https://github.com/llvm/llvm-project/pull/206257

>From c7046abfa4d3aaaf0bce4f8589a50221a46934b1 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 14:46:05 -0500
Subject: [PATCH 01/21] Initial patch from PR#134330

---
 libcxx/include/__functional/hash.h            |   9 +
 .../include/__iterator/erase_if_container.h   |   3 +-
 libcxx/include/__node_handle                  |  52 +-
 .../include/__numeric/saturation_arithmetic.h |  10 +-
 libcxx/include/__tree                         | 912 +++++++++++-------
 .../include/__type_traits/make_transparent.h  |   4 +-
 .../__utility/default_three_way_comparator.h  |   2 +-
 .../lazy_synth_three_way_comparator.h         |  33 +-
 libcxx/include/__utility/try_key_extraction.h |  12 +-
 libcxx/test/support/is_transparent.h          |  13 +-
 libcxx/test/support/poisoned_hash_helper.h    |  10 +-
 libcxx/test/support/private_constructor.h     |  18 +-
 12 files changed, 638 insertions(+), 440 deletions(-)

diff --git a/libcxx/include/__functional/hash.h b/libcxx/include/__functional/hash.h
index f74f25fa6e84b..83bbf1b5e26c3 100644
--- a/libcxx/include/__functional/hash.h
+++ b/libcxx/include/__functional/hash.h
@@ -433,10 +433,13 @@ struct __hash_impl<long double> : __scalar_hash<long double> {
 template <class _Tp>
 struct hash : public __hash_impl<_Tp> {};
 
+#if _LIBCPP_STD_VER >= 17
+
 template <>
 struct hash<nullptr_t> : public __unary_function<nullptr_t, size_t> {
   _LIBCPP_HIDE_FROM_ABI size_t operator()(nullptr_t) const _NOEXCEPT { return 662607004ull; }
 };
+#endif
 
 #ifndef _LIBCPP_CXX03_LANG
 template <class _Key, class _Hash>
@@ -449,12 +452,18 @@ template <class _Key, class _Hash = hash<_Key> >
 using __has_enabled_hash _LIBCPP_NODEBUG =
     integral_constant<bool, __check_hash_requirements<_Key, _Hash>::value && is_default_constructible<_Hash>::value >;
 
+#  if _LIBCPP_STD_VER >= 17
 template <class _Type, class>
 using __enable_hash_helper_imp _LIBCPP_NODEBUG = _Type;
 
 template <class _Type, class... _Keys>
 using __enable_hash_helper _LIBCPP_NODEBUG =
     __enable_hash_helper_imp<_Type, __enable_if_t<__all<__has_enabled_hash<_Keys>::value...>::value> >;
+#  else
+template <class _Type, class...>
+using __enable_hash_helper _LIBCPP_NODEBUG = _Type;
+#  endif
+
 #endif // !_LIBCPP_CXX03_LANG
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__iterator/erase_if_container.h b/libcxx/include/__iterator/erase_if_container.h
index 0f87f50cd1c16..8d92d3f1b9dbe 100644
--- a/libcxx/include/__iterator/erase_if_container.h
+++ b/libcxx/include/__iterator/erase_if_container.h
@@ -22,7 +22,8 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Container, class _Predicate>
-_LIBCPP_HIDE_FROM_ABI typename _Container::size_type __libcpp_erase_if_container(_Container& __c, _Predicate& __pred) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename _Container::size_type
+__libcpp_erase_if_container(_Container& __c, _Predicate& __pred) {
   typename _Container::size_type __old_size = __c.size();
 
   const typename _Container::iterator __last = __c.end();
diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index b20b0c73a0518..58be5b9d4ddd5 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -31,29 +31,29 @@ private:
 public:
   // [container.node.cons], constructors, copy, and assignment
   constexpr node-handle() noexcept : ptr_(), alloc_() {}
-  node-handle(node-handle&&) noexcept;
-  node-handle& operator=(node-handle&&);
+  constexpr node-handle(node-handle&&) noexcept;         // constexpr since C++26
+  constexpr node-handle& operator=(node-handle&&);       // constexpr since C++26
 
   // [container.node.dtor], destructor
-  ~node-handle();
+  constexpr ~node-handle(); // constexpr since C++26
 
   // [container.node.observers], observers
-  value_type& value() const;            // not present for map containers
-  key_type& key() const;                // not present for set containers
-  mapped_type& mapped() const;          // not present for set containers
+  value_type& value() const;             // not present for map containers
+  key_type& key() const;                 // not present for set containers
+  constexpr mapped_type& mapped() const; // not present for set containers, constexpr since C++26
 
-  allocator_type get_allocator() const;
-  explicit operator bool() const noexcept;
-  [[nodiscard]] bool empty() const noexcept; // nodiscard since C++20
+  constexpr allocator_type get_allocator() const;      // constexpr since C++26
+  constexpr explicit operator bool() const noexcept;   // constexpr since C++26
+  [[nodiscard]] constexpr bool empty() const noexcept; // nodiscard since C++20, constexpr since C++26
 
   // [container.node.modifiers], modifiers
-  void swap(node-handle&)
+  constexpr void swap(node-handle&)
     noexcept(ator_traits::propagate_on_container_swap::value ||
-             ator_traits::is_always_equal::value);
+             ator_traits::is_always_equal::value); // constexpr since C++26
 
-  friend void swap(node-handle& x, node-handle& y) noexcept(noexcept(x.swap(y))) {
+  constexpr friend void swap(node-handle& x, node-handle& y) noexcept(noexcept(x.swap(y))) {
     x.swap(y);
-  }
+  } // constexpr since C++26
 };
 
 */
@@ -99,12 +99,12 @@ private:
   __node_pointer_type __ptr_ = nullptr;
   optional<allocator_type> __alloc_;
 
-  _LIBCPP_HIDE_FROM_ABI void __release_ptr() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __release_ptr() {
     __ptr_   = nullptr;
     __alloc_ = std::nullopt;
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __destroy_node_pointer() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __destroy_node_pointer() {
     if (__ptr_ != nullptr) {
       typedef typename __allocator_traits_rebind< allocator_type, _NodeType>::type __node_alloc_type;
       __node_alloc_type __alloc(*__alloc_);
@@ -113,19 +113,20 @@ private:
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI __basic_node_handle(__node_pointer_type __ptr, allocator_type const& __alloc)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  __basic_node_handle(__node_pointer_type __ptr, allocator_type const& __alloc)
       : __ptr_(__ptr), __alloc_(__alloc) {}
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __basic_node_handle() = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __basic_node_handle() = default;
 
-  _LIBCPP_HIDE_FROM_ABI __basic_node_handle(__basic_node_handle&& __other) noexcept
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __basic_node_handle(__basic_node_handle&& __other) noexcept
       : __ptr_(__other.__ptr_), __alloc_(std::move(__other.__alloc_)) {
     __other.__ptr_   = nullptr;
     __other.__alloc_ = std::nullopt;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __basic_node_handle& operator=(__basic_node_handle&& __other) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __basic_node_handle& operator=(__basic_node_handle&& __other) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __alloc_ == std::nullopt || __alloc_traits::propagate_on_container_move_assignment::value ||
             __alloc_ == __other.__alloc_,
@@ -144,13 +145,13 @@ public:
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const { return *__alloc_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type get_allocator() const { return *__alloc_; }
 
-  _LIBCPP_HIDE_FROM_ABI explicit operator bool() const { return __ptr_ != nullptr; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit operator bool() const { return __ptr_ != nullptr; }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool empty() const { return __ptr_ == nullptr; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool empty() const { return __ptr_ == nullptr; }
 
-  _LIBCPP_HIDE_FROM_ABI void swap(__basic_node_handle& __other) noexcept(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(__basic_node_handle& __other) noexcept(
       __alloc_traits::propagate_on_container_swap::value || __alloc_traits::is_always_equal::value) {
     using std::swap;
     swap(__ptr_, __other.__ptr_);
@@ -159,12 +160,12 @@ public:
       swap(__alloc_, __other.__alloc_);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend void
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend void
   swap(__basic_node_handle& __a, __basic_node_handle& __b) noexcept(noexcept(__a.swap(__b))) {
     __a.swap(__b);
   }
 
-  _LIBCPP_HIDE_FROM_ABI ~__basic_node_handle() { __destroy_node_pointer(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~__basic_node_handle() { __destroy_node_pointer(); }
 };
 
 template <class _NodeType, class _Derived>
@@ -179,6 +180,7 @@ struct __map_node_handle_specifics {
   using key_type    = __remove_const_t<typename _NodeType::__node_value_type::first_type>;
   using mapped_type = typename _NodeType::__node_value_type::second_type;
 
+  // This method is not constexpr as per the standard.
   _LIBCPP_HIDE_FROM_ABI key_type& key() const {
     return const_cast<key_type&>(static_cast<_Derived const*>(this)->__ptr_->__get_value().first);
   }
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 4491bab2b1479..7a7410b5dea08 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -121,27 +121,27 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Rp __saturate_cast(_Tp __x) noexcept {
 #if _LIBCPP_STD_VER >= 26
 
 template <__signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
   return std::__add_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
   return std::__sub_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
   return std::__mul_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
   return std::__div_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Rp, __signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
   return std::__saturate_cast<_Rp>(__x);
 }
 
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 694796922c914..5398ff911ec57 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -28,6 +28,8 @@
 #include <__type_traits/copy_cvref.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_constructible.h>
 #include <__type_traits/is_nothrow_assignable.h>
 #include <__type_traits/is_nothrow_constructible.h>
@@ -111,7 +113,7 @@ __root, have a non-null __parent_ field.
 // Returns:  true if __x is a left child of its parent, else false
 // Precondition:  __x != nullptr.
 template <class _NodePtr>
-inline _LIBCPP_HIDE_FROM_ABI bool __tree_is_left_child(_NodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __tree_is_left_child(_NodePtr __x) _NOEXCEPT {
   return __x == __x->__parent_->__left_;
 }
 
@@ -119,7 +121,7 @@ inline _LIBCPP_HIDE_FROM_ABI bool __tree_is_left_child(_NodePtr __x) _NOEXCEPT {
 //    __x is a proper subtree, returns the black height (null counts as 1).  If
 //    __x is an improper subtree, returns 0.
 template <class _NodePtr>
-unsigned __tree_sub_invariant(_NodePtr __x) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 unsigned __tree_sub_invariant(_NodePtr __x) {
   if (__x == nullptr)
     return 1;
   // parent consistency checked by caller
@@ -151,7 +153,7 @@ unsigned __tree_sub_invariant(_NodePtr __x) {
 //    __root == nullptr is a proper tree.  Returns true if __root is a proper
 //    red black tree, else returns false.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI bool __tree_invariant(_NodePtr __root) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __tree_invariant(_NodePtr __root) {
   if (__root == nullptr)
     return true;
   // check __x->__parent_ consistency
@@ -168,7 +170,7 @@ _LIBCPP_HIDE_FROM_ABI bool __tree_invariant(_NodePtr __root) {
 
 // Returns:  pointer to the left-most node under __x.
 template <class _NodePtr>
-inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_min(_NodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_min(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Root node shouldn't be null");
   while (__x->__left_ != nullptr)
     __x = __x->__left_;
@@ -177,7 +179,7 @@ inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_min(_NodePtr __x) _NOEXCEPT {
 
 // Returns:  pointer to the right-most node under __x.
 template <class _NodePtr>
-inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_max(_NodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_max(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Root node shouldn't be null");
   while (__x->__right_ != nullptr)
     __x = __x->__right_;
@@ -186,7 +188,7 @@ inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_max(_NodePtr __x) _NOEXCEPT {
 
 // Returns:  pointer to the next in-order node after __x.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI _NodePtr __tree_next(_NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_next(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   if (__x->__right_ != nullptr)
     return std::__tree_min(__x->__right_);
@@ -201,23 +203,23 @@ _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_next(_NodePtr __x) _NOEXCEPT {
 // to the actual root of the tree through a __left_ pointer. Incrementing the end() pointer is UB, so we can assume that
 // never happens.
 template <class _EndNodePtr, class _NodePtr>
-inline _LIBCPP_HIDE_FROM_ABI _EndNodePtr __tree_next_iter(_NodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _EndNodePtr __tree_next_iter(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   if (__x->__right_ != nullptr)
-    return static_cast<_EndNodePtr>(std::__tree_min(__x->__right_));
+    return std::__static_fancy_pointer_cast<_EndNodePtr>(std::__tree_min(__x->__right_));
   while (!std::__tree_is_left_child(__x))
     __x = __x->__parent_unsafe();
-  return static_cast<_EndNodePtr>(__x->__parent_);
+  return std::__static_fancy_pointer_cast<_EndNodePtr>(__x->__parent_);
 }
 
 // Returns:  pointer to the previous in-order node before __x.
 // Note: __x may be the end node.
 template <class _NodePtr, class _EndNodePtr>
-inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_prev_iter(_EndNodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_prev_iter(_EndNodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   if (__x->__left_ != nullptr)
     return std::__tree_max(__x->__left_);
-  _NodePtr __xx = static_cast<_NodePtr>(__x);
+  _NodePtr __xx = std::__static_fancy_pointer_cast<_NodePtr>(__x);
   while (std::__tree_is_left_child(__xx))
     __xx = __xx->__parent_unsafe();
   return __xx->__parent_unsafe();
@@ -225,7 +227,7 @@ inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_prev_iter(_EndNodePtr __x) _NOEXCEP
 
 // Returns:  pointer to a node which has no children
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI _NodePtr __tree_leaf(_NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_leaf(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   while (true) {
     if (__x->__left_ != nullptr) {
@@ -244,7 +246,7 @@ _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_leaf(_NodePtr __x) _NOEXCEPT {
 // Effects:  Makes __x->__right_ the subtree root with __x as its left child
 //           while preserving in-order order.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI void __tree_left_rotate(_NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree_left_rotate(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   _LIBCPP_ASSERT_INTERNAL(__x->__right_ != nullptr, "node should have a right child");
   _NodePtr __y  = __x->__right_;
@@ -263,7 +265,7 @@ _LIBCPP_HIDE_FROM_ABI void __tree_left_rotate(_NodePtr __x) _NOEXCEPT {
 // Effects:  Makes __x->__left_ the subtree root with __x as its right child
 //           while preserving in-order order.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI void __tree_right_rotate(_NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree_right_rotate(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   _LIBCPP_ASSERT_INTERNAL(__x->__left_ != nullptr, "node should have a left child");
   _NodePtr __y = __x->__left_;
@@ -287,7 +289,8 @@ _LIBCPP_HIDE_FROM_ABI void __tree_right_rotate(_NodePtr __x) _NOEXCEPT {
 // Postcondition: __tree_invariant(end_node->__left_) == true.  end_node->__left_
 //                may be different than the value passed in as __root.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI void __tree_balance_after_insert(_NodePtr __root, _NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+__tree_balance_after_insert(_NodePtr __root, _NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__root != nullptr, "Root of the tree shouldn't be null");
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Can't attach null node to a leaf");
   __x->__is_black_ = __x == __root;
@@ -343,7 +346,7 @@ _LIBCPP_HIDE_FROM_ABI void __tree_balance_after_insert(_NodePtr __root, _NodePtr
 //                nor any of its children refer to __z.  end_node->__left_
 //                may be different than the value passed in as __root.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI void __tree_remove(_NodePtr __root, _NodePtr __z) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree_remove(_NodePtr __root, _NodePtr __z) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__root != nullptr, "Root node should not be null");
   _LIBCPP_ASSERT_INTERNAL(__z != nullptr, "The node to remove should not be null");
   _LIBCPP_ASSERT_INTERNAL(std::__tree_invariant(__root), "The tree invariants should hold");
@@ -558,7 +561,7 @@ public:
   using pointer = _Pointer;
   pointer __left_;
 
-  _LIBCPP_HIDE_FROM_ABI __tree_end_node() _NOEXCEPT : __left_() {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_end_node() _NOEXCEPT : __left_(nullptr) {}
 };
 
 template <class _VoidPtr>
@@ -571,11 +574,15 @@ public:
   __end_node_pointer __parent_;
   bool __is_black_;
 
-  _LIBCPP_HIDE_FROM_ABI pointer __parent_unsafe() const { return static_cast<pointer>(__parent_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pointer __parent_unsafe() const {
+    return std::__static_fancy_pointer_cast<pointer>(__parent_);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI void __set_parent(pointer __p) { __parent_ = static_cast<__end_node_pointer>(__p); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __set_parent(pointer __p) {
+    __parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__p);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_node_base()             = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_node_base() = default;
   __tree_node_base(__tree_node_base const&)            = delete;
   __tree_node_base& operator=(__tree_node_base const&) = delete;
 };
@@ -596,7 +603,7 @@ private:
   };
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __node_value_type& __get_value() { return __value_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_value_type& __get_value() { return __value_; }
 #else
 
 private:
@@ -607,10 +614,24 @@ public:
 #endif
 
   template <class _Alloc, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_node(_Alloc& __na, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_node(_Alloc& __na, _Args&&... __args) {
     allocator_traits<_Alloc>::construct(__na, std::addressof(__get_value()), std::forward<_Args>(__args)...);
   }
+
+// This line fails with:
+// libcxx/test-suite-install/include/c++/v1/__memory/construct_at.h:38:49: note: non-literal type
+// `'std::__tree_node<std::__value_type<Key, int>, void *>' cannot be used in a constant expression`
+// during constant evaluation as part of P3372
+// on:
+// 1. AppleClang, which is not yet using clang >= 20.x, which has "fixed" this issue
+// FIXME: when AppleClang is based off of clang >= 20.x
+// FIXME: when Clang-based CI is using clang >= 20.x
+#if (defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 2000) ||                                                        \
+    (defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 2000)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~__tree_node() {}
+#else
   ~__tree_node()                             = delete;
+#endif
   __tree_node(__tree_node const&)            = delete;
   __tree_node& operator=(__tree_node const&) = delete;
 };
@@ -629,14 +650,15 @@ private:
 public:
   bool __value_constructed;
 
-  _LIBCPP_HIDE_FROM_ABI __tree_node_destructor(const __tree_node_destructor&) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_node_destructor(const __tree_node_destructor&) = default;
   __tree_node_destructor& operator=(const __tree_node_destructor&)            = delete;
 
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_node_destructor(allocator_type& __na, bool __val = false) _NOEXCEPT
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_node_destructor(allocator_type& __na, bool __val = false) _NOEXCEPT
       : __na_(__na),
         __value_constructed(__val) {}
 
-  _LIBCPP_HIDE_FROM_ABI void operator()(pointer __p) _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void operator()(pointer __p) _NOEXCEPT {
     if (__value_constructed)
       __alloc_traits::destroy(__na_, std::addressof(__p->__get_value()));
     if (__p)
@@ -673,44 +695,50 @@ public:
   using reference         = value_type&;
   using pointer           = __rebind_pointer_t<_NodePtr, value_type>;
 
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator() _NOEXCEPT : __ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator() _NOEXCEPT : __ptr_(nullptr) {}
 
-  _LIBCPP_HIDE_FROM_ABI reference operator*() const { return __get_np()->__get_value(); }
-  _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reference operator*() const { return __get_np()->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pointer operator->() const {
     return pointer_traits<pointer>::pointer_to(__get_np()->__get_value());
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator& operator++() {
-    __ptr_ = std::__tree_next_iter<__end_node_pointer>(static_cast<__node_base_pointer>(__ptr_));
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator& operator++() {
+    __ptr_ = std::__tree_next_iter<__end_node_pointer>(std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr_));
     return *this;
   }
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator operator++(int) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator operator++(int) {
     __tree_iterator __t(*this);
     ++(*this);
     return __t;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator& operator--() {
-    __ptr_ = static_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator& operator--() {
+    __ptr_ = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
     return *this;
   }
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator operator--(int) {
     __tree_iterator __t(*this);
     --(*this);
     return __t;
   }
 
-  friend _LIBCPP_HIDE_FROM_ABI bool operator==(const __tree_iterator& __x, const __tree_iterator& __y) {
+  friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
+  operator==(const __tree_iterator& __x, const __tree_iterator& __y) {
     return __x.__ptr_ == __y.__ptr_;
   }
-  friend _LIBCPP_HIDE_FROM_ABI bool operator!=(const __tree_iterator& __x, const __tree_iterator& __y) {
+  friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
+  operator!=(const __tree_iterator& __x, const __tree_iterator& __y) {
     return !(__x == __y);
   }
 
 private:
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_iterator(__node_pointer __p) _NOEXCEPT : __ptr_(__p) {}
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_iterator(__end_node_pointer __p) _NOEXCEPT : __ptr_(__p) {}
-  _LIBCPP_HIDE_FROM_ABI __node_pointer __get_np() const { return static_cast<__node_pointer>(__ptr_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_iterator(__node_pointer __p) _NOEXCEPT
+      : __ptr_(std::__static_fancy_pointer_cast<__end_node_pointer>(__p)) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_iterator(__end_node_pointer __p) _NOEXCEPT
+      : __ptr_(__p) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __get_np() const {
+    return std::__static_fancy_pointer_cast<__node_pointer>(__ptr_);
+  }
   template <class, class, class>
   friend class __tree;
   template <class, class, class>
@@ -735,48 +763,55 @@ public:
   using pointer                              = __rebind_pointer_t<_NodePtr, const value_type>;
   using __non_const_iterator _LIBCPP_NODEBUG = __tree_iterator<_Tp, __node_pointer, difference_type>;
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator() _NOEXCEPT : __ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator() _NOEXCEPT : __ptr_(nullptr) {}
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator(__non_const_iterator __p) _NOEXCEPT : __ptr_(__p.__ptr_) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator(__non_const_iterator __p) _NOEXCEPT
+      : __ptr_(__p.__ptr_) {}
 
-  _LIBCPP_HIDE_FROM_ABI reference operator*() const { return __get_np()->__get_value(); }
-  _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reference operator*() const { return __get_np()->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pointer operator->() const {
     return pointer_traits<pointer>::pointer_to(__get_np()->__get_value());
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator& operator++() {
-    __ptr_ = std::__tree_next_iter<__end_node_pointer>(static_cast<__node_base_pointer>(__ptr_));
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator& operator++() {
+    __ptr_ = std::__tree_next_iter<__end_node_pointer>(std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr_));
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator operator++(int) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator operator++(int) {
     __tree_const_iterator __t(*this);
     ++(*this);
     return __t;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator& operator--() {
-    __ptr_ = static_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator& operator--() {
+    __ptr_ = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator operator--(int) {
     __tree_const_iterator __t(*this);
     --(*this);
     return __t;
   }
 
-  friend _LIBCPP_HIDE_FROM_ABI bool operator==(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
+  friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
+  operator==(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
     return __x.__ptr_ == __y.__ptr_;
   }
-  friend _LIBCPP_HIDE_FROM_ABI bool operator!=(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
+  friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
+  operator!=(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
     return !(__x == __y);
   }
 
 private:
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_const_iterator(__node_pointer __p) _NOEXCEPT : __ptr_(__p) {}
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_const_iterator(__end_node_pointer __p) _NOEXCEPT : __ptr_(__p) {}
-  _LIBCPP_HIDE_FROM_ABI __node_pointer __get_np() const { return static_cast<__node_pointer>(__ptr_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_const_iterator(__node_pointer __p) _NOEXCEPT
+      : __ptr_(std::__static_fancy_pointer_cast<__end_node_pointer>(__p)) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_const_iterator(__end_node_pointer __p) _NOEXCEPT
+      : __ptr_(std::__static_fancy_pointer_cast<__end_node_pointer>(__p)) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __get_np() const {
+    return std::__static_fancy_pointer_cast<__node_pointer>(__ptr_);
+  }
 
   template <class, class, class>
   friend class __tree;
@@ -840,63 +875,73 @@ private:
   _LIBCPP_COMPRESSED_PAIR(size_type, __size_, value_compare, __value_comp_);
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __end_node_pointer __end_node() _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __end_node_pointer __end_node() _NOEXCEPT {
     return pointer_traits<__end_node_pointer>::pointer_to(__end_node_);
   }
-  _LIBCPP_HIDE_FROM_ABI __end_node_pointer __end_node() const _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __end_node_pointer __end_node() const _NOEXCEPT {
     return pointer_traits<__end_node_pointer>::pointer_to(const_cast<__end_node_t&>(__end_node_));
   }
-  _LIBCPP_HIDE_FROM_ABI __node_allocator& __node_alloc() _NOEXCEPT { return __node_alloc_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_allocator& __node_alloc() _NOEXCEPT {
+    return __node_alloc_;
+  }
 
 private:
-  _LIBCPP_HIDE_FROM_ABI const __node_allocator& __node_alloc() const _NOEXCEPT { return __node_alloc_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const __node_allocator& __node_alloc() const _NOEXCEPT {
+    return __node_alloc_;
+  }
 
 public:
-  _LIBCPP_HIDE_FROM_ABI allocator_type __alloc() const _NOEXCEPT { return allocator_type(__node_alloc()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type __alloc() const _NOEXCEPT {
+    return allocator_type(__node_alloc());
+  }
 
-  _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __size_; }
-  _LIBCPP_HIDE_FROM_ABI value_compare& value_comp() _NOEXCEPT { return __value_comp_; }
-  _LIBCPP_HIDE_FROM_ABI const value_compare& value_comp() const _NOEXCEPT { return __value_comp_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type size() const _NOEXCEPT { return __size_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_compare& value_comp() _NOEXCEPT { return __value_comp_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const value_compare& value_comp() const _NOEXCEPT {
+    return __value_comp_;
+  }
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __node_pointer __root() const _NOEXCEPT {
-    return static_cast<__node_pointer>(__end_node()->__left_);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __root() const _NOEXCEPT {
+    return std::__static_fancy_pointer_cast<__node_pointer>(__end_node()->__left_);
   }
 
-  _LIBCPP_HIDE_FROM_ABI __node_base_pointer* __root_ptr() const _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer* __root_ptr() const _NOEXCEPT {
     return std::addressof(__end_node()->__left_);
   }
 
   using iterator       = __tree_iterator<_Tp, __node_pointer, difference_type>;
   using const_iterator = __tree_const_iterator<_Tp, __node_pointer, difference_type>;
 
-  _LIBCPP_HIDE_FROM_ABI explicit __tree(const value_compare& __comp) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree(const value_compare& __comp) _NOEXCEPT_(
       is_nothrow_default_constructible<__node_allocator>::value&& is_nothrow_copy_constructible<value_compare>::value)
       : __size_(0), __value_comp_(__comp) {
     __begin_node_ = __end_node();
   }
 
-  _LIBCPP_HIDE_FROM_ABI explicit __tree(const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree(const allocator_type& __a)
       : __begin_node_(), __node_alloc_(__node_allocator(__a)), __size_(0) {
     __begin_node_ = __end_node();
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree(const value_compare& __comp, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(const value_compare& __comp, const allocator_type& __a)
       : __begin_node_(), __node_alloc_(__node_allocator(__a)), __size_(0), __value_comp_(__comp) {
     __begin_node_ = __end_node();
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree(const __tree& __t);
-  _LIBCPP_HIDE_FROM_ABI __tree& operator=(const __tree& __t);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(const __tree& __t);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree& operator=(const __tree& __t);
   template <class _ForwardIterator>
-  _LIBCPP_HIDE_FROM_ABI void __assign_unique(_ForwardIterator __first, _ForwardIterator __last);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_unique(_ForwardIterator __first, _ForwardIterator __last);
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void __assign_multi(_InputIterator __first, _InputIterator __last);
-  _LIBCPP_HIDE_FROM_ABI __tree(__tree&& __t) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_multi(_InputIterator __first, _InputIterator __last);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t) _NOEXCEPT_(
       is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value);
-  _LIBCPP_HIDE_FROM_ABI __tree(__tree&& __t, const allocator_type& __a);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t, const allocator_type& __a);
 
-  _LIBCPP_HIDE_FROM_ABI __tree& operator=(__tree&& __t)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree& operator=(__tree&& __t)
       _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value &&
                  ((__node_traits::propagate_on_container_move_assignment::value &&
                    is_nothrow_move_assignable<__node_allocator>::value) ||
@@ -905,23 +950,27 @@ public:
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI ~__tree() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~__tree() {
     static_assert(is_copy_constructible<value_compare>::value, "Comparator must be copy-constructible.");
     destroy(__root());
   }
 
-  _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return iterator(__begin_node_); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return const_iterator(__begin_node_); }
-  _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return iterator(__end_node()); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return const_iterator(__end_node()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT { return iterator(__begin_node_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator begin() const _NOEXCEPT {
+    return const_iterator(__begin_node_);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator end() _NOEXCEPT { return iterator(__end_node()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator end() const _NOEXCEPT {
+    return const_iterator(__end_node());
+  }
 
-  _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type max_size() const _NOEXCEPT {
     return std::min<size_type>(__node_traits::max_size(__node_alloc()), numeric_limits<difference_type >::max());
   }
 
-  _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT;
 
-  _LIBCPP_HIDE_FROM_ABI void swap(__tree& __t)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(__tree& __t)
 #if _LIBCPP_STD_VER <= 11
       _NOEXCEPT_(__is_nothrow_swappable_v<value_compare> &&
                  (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>));
@@ -930,21 +979,22 @@ public:
 #endif
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator __emplace_multi(_Args&&... __args);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __emplace_multi(_Args&&... __args);
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator __emplace_hint_multi(const_iterator __p, _Args&&... __args);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
+  __emplace_hint_multi(const_iterator __p, _Args&&... __args);
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __emplace_unique(_Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> __emplace_unique(_Args&&... __args) {
     return std::__try_key_extraction<key_type>(
         [this](const key_type& __key, _Args&&... __args2) {
           auto [__parent, __child] = __find_equal(__key);
-          __node_pointer __r       = static_cast<__node_pointer>(__child);
+          __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
           bool __inserted          = false;
           if (__child == nullptr) {
             __node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
-            __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
+            __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
             __r        = __h.release();
             __inserted = true;
           }
@@ -953,10 +1003,10 @@ public:
         [this](_Args&&... __args2) {
           __node_holder __h        = __construct_node(std::forward<_Args>(__args2)...);
           auto [__parent, __child] = __find_equal(__h->__get_value());
-          __node_pointer __r       = static_cast<__node_pointer>(__child);
+          __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
           bool __inserted          = false;
           if (__child == nullptr) {
-            __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
+            __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
             __r        = __h.release();
             __inserted = true;
           }
@@ -966,16 +1016,17 @@ public:
   }
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __emplace_hint_unique(const_iterator __p, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
+  __emplace_hint_unique(const_iterator __p, _Args&&... __args) {
     return std::__try_key_extraction<key_type>(
         [this, __p](const key_type& __key, _Args&&... __args2) {
           __node_base_pointer __dummy;
           auto [__parent, __child] = __find_equal(__p, __dummy, __key);
-          __node_pointer __r       = static_cast<__node_pointer>(__child);
+          __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
           bool __inserted          = false;
           if (__child == nullptr) {
             __node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
-            __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
+            __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
             __r        = __h.release();
             __inserted = true;
           }
@@ -985,9 +1036,9 @@ public:
           __node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
           __node_base_pointer __dummy;
           auto [__parent, __child] = __find_equal(__p, __dummy, __h->__get_value());
-          __node_pointer __r       = static_cast<__node_pointer>(__child);
+          __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
           if (__child == nullptr) {
-            __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
+            __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
             __r = __h.release();
           }
           return pair<iterator, bool>(iterator(__r), __child == nullptr);
@@ -995,73 +1046,112 @@ public:
         std::forward<_Args>(__args)...);
   }
 
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
+  __emplace_hint_unique__sfinae(true_type, const_iterator __p, _Args&&... __args) {
+    return __emplace_hint_unique(__p, __args...);
+  }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
+  __emplace_hint_unique__sfinae(false_type, const_iterator, _Args&&...) {
+    // This method body should never be run. It only exists to allow for compilation. See note in
+    // __insert_unique_from_orphaned_node for more information.
+    return pair<iterator, bool>(iterator(__node_pointer()), false);
+  }
+
   template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI void
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   __insert_unique_from_orphaned_node(const_iterator __p, __get_node_value_type_t<_Tp>&& __value) {
-    __emplace_hint_unique(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
+#if _LIBCPP_STD_VER >= 26
+    if (std::is_constant_evaluated() && std::is_copy_constructible_v<decltype(__value.first)>) {
+      // We create a sfinae wrapper method here, because if the __emplace_hint_unique method gets template instantiated
+      // within __insert_unique_from_orphaned_node, the code will fail to compile when the value is not
+      // copy_constructible (even for runtime execution); unless we use `if constexpr`. Given the copy-constructible
+      // code path will be a performance regression, we want to restrict it to only execute during constant evaluation
+      // , hence, we need to delay the template instantiation.
+      __emplace_hint_unique__sfinae(
+          integral_constant< bool, std::is_copy_constructible_v<decltype(__value.first)> >(),
+          __p,
+          __value.first,
+          std::move(__value.second));
+    } else
+#endif
+    {
+      __emplace_hint_unique(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
+    }
   }
 
   template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI void __insert_unique_from_orphaned_node(const_iterator __p, _Tp&& __value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __insert_unique_from_orphaned_node(const_iterator __p, _Tp&& __value) {
     __emplace_hint_unique(__p, std::move(__value));
   }
 
   template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(const_iterator __p, value_type&& __value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __insert_multi_from_orphaned_node(const_iterator __p, value_type&& __value) {
     __emplace_hint_multi(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
   }
 
   template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(const_iterator __p, _Tp&& __value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __insert_multi_from_orphaned_node(const_iterator __p, _Tp&& __value) {
     __emplace_hint_multi(__p, std::move(__value));
   }
 
   template <class _InIter, class _Sent>
-  _LIBCPP_HIDE_FROM_ABI void __insert_range_multi(_InIter __first, _Sent __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __insert_range_multi(_InIter __first, _Sent __last) {
     if (__first == __last)
       return;
 
     if (__root() == nullptr) { // Make sure we always have a root node
-      __insert_node_at(
-          __end_node(), __end_node()->__left_, static_cast<__node_base_pointer>(__construct_node(*__first).release()));
+      __insert_node_at(__end_node(),
+                       __end_node()->__left_,
+                       std::__static_fancy_pointer_cast<__node_base_pointer>(__construct_node(*__first).release()));
       ++__first;
     }
 
-    auto __max_node = static_cast<__node_pointer>(std::__tree_max(static_cast<__node_base_pointer>(__root())));
+    auto __max_node = std::__static_fancy_pointer_cast<__node_pointer>(
+        std::__tree_max(std::__static_fancy_pointer_cast<__node_base_pointer>(__root())));
 
     for (; __first != __last; ++__first) {
       __node_holder __nd = __construct_node(*__first);
       // Always check the max node first. This optimizes for sorted ranges inserted at the end.
       if (!value_comp()(__nd->__get_value(), __max_node->__get_value())) { // __node >= __max_val
-        __insert_node_at(static_cast<__end_node_pointer>(__max_node),
+        __insert_node_at(std::__static_fancy_pointer_cast<__end_node_pointer>(__max_node),
                          __max_node->__right_,
-                         static_cast<__node_base_pointer>(__nd.get()));
+                         std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.get()));
         __max_node = __nd.release();
       } else {
         __end_node_pointer __parent;
         __node_base_pointer& __child = __find_leaf_high(__parent, __nd->__get_value());
-        __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd.release()));
+        __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.release()));
       }
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __node_assign_unique(const value_type& __v, __node_pointer __dest);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
+  __node_assign_unique(const value_type& __v, __node_pointer __dest);
 
-  _LIBCPP_HIDE_FROM_ABI iterator __node_insert_multi(__node_pointer __nd);
-  _LIBCPP_HIDE_FROM_ABI iterator __node_insert_multi(const_iterator __p, __node_pointer __nd);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __node_insert_multi(__node_pointer __nd);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
+  __node_insert_multi(const_iterator __p, __node_pointer __nd);
 
   template <class _InIter, class _Sent>
-  _LIBCPP_HIDE_FROM_ABI void __insert_range_unique(_InIter __first, _Sent __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __insert_range_unique(_InIter __first, _Sent __last) {
     if (__first == __last)
       return;
 
     if (__root() == nullptr) {
-      __insert_node_at(
-          __end_node(), __end_node()->__left_, static_cast<__node_base_pointer>(__construct_node(*__first).release()));
+      __insert_node_at(__end_node(),
+                       __end_node()->__left_,
+                       std::__static_fancy_pointer_cast<__node_base_pointer>(__construct_node(*__first).release()));
       ++__first;
     }
 
-    auto __max_node = static_cast<__node_pointer>(std::__tree_max(static_cast<__node_base_pointer>(__root())));
+    auto __max_node = std::__static_fancy_pointer_cast<__node_pointer>(
+        std::__tree_max(std::__static_fancy_pointer_cast<__node_base_pointer>(__root())));
 
     using __reference = decltype(*__first);
 
@@ -1070,29 +1160,31 @@ public:
           [this, &__max_node](const key_type& __key, __reference&& __val) {
             if (value_comp()(__max_node->__get_value(), __key)) { // __key > __max_node
               __node_holder __nd = __construct_node(std::forward<__reference>(__val));
-              __insert_node_at(static_cast<__end_node_pointer>(__max_node),
+              __insert_node_at(std::__static_fancy_pointer_cast<__end_node_pointer>(__max_node),
                                __max_node->__right_,
-                               static_cast<__node_base_pointer>(__nd.get()));
+                               std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.get()));
               __max_node = __nd.release();
             } else {
               auto [__parent, __child] = __find_equal(__key);
               if (__child == nullptr) {
                 __node_holder __nd = __construct_node(std::forward<__reference>(__val));
-                __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd.release()));
+                __insert_node_at(
+                    __parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.release()));
               }
             }
           },
           [this, &__max_node](__reference&& __val) {
             __node_holder __nd = __construct_node(std::forward<__reference>(__val));
             if (value_comp()(__max_node->__get_value(), __nd->__get_value())) { // __node > __max_node
-              __insert_node_at(static_cast<__end_node_pointer>(__max_node),
+              __insert_node_at(std::__static_fancy_pointer_cast<__end_node_pointer>(__max_node),
                                __max_node->__right_,
-                               static_cast<__node_base_pointer>(__nd.get()));
+                               std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.get()));
               __max_node = __nd.release();
             } else {
               auto [__parent, __child] = __find_equal(__nd->__get_value());
               if (__child == nullptr) {
-                __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd.release()));
+                __insert_node_at(
+                    __parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.release()));
               }
             }
           },
@@ -1100,62 +1192,67 @@ public:
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI iterator __remove_node_pointer(__node_pointer) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __remove_node_pointer(__node_pointer) _NOEXCEPT;
 
 #if _LIBCPP_STD_VER >= 17
   template <class _NodeHandle, class _InsertReturnType>
-  _LIBCPP_HIDE_FROM_ABI _InsertReturnType __node_handle_insert_unique(_NodeHandle&&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _InsertReturnType __node_handle_insert_unique(_NodeHandle&&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI iterator __node_handle_insert_unique(const_iterator, _NodeHandle&&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
+  __node_handle_insert_unique(const_iterator, _NodeHandle&&);
   template <class _Comp2>
-  _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source);
 
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI iterator __node_handle_insert_multi(_NodeHandle&&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __node_handle_insert_multi(_NodeHandle&&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI iterator __node_handle_insert_multi(const_iterator, _NodeHandle&&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
+  __node_handle_insert_multi(const_iterator, _NodeHandle&&);
   template <class _Comp2>
-  _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source);
 
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(key_type const&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(key_type const&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const_iterator);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const_iterator);
 #endif
 
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p);
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l);
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI size_type __erase_unique(const _Key& __k);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __erase_unique(const _Key& __k);
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI size_type __erase_multi(const _Key& __k);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __erase_multi(const _Key& __k);
 
-  _LIBCPP_HIDE_FROM_ABI void
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   __insert_node_at(__end_node_pointer __parent, __node_base_pointer& __child, __node_base_pointer __new_node) _NOEXCEPT;
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator find(const _Key& __key) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const _Key& __key) {
     auto [__, __match] = __find_equal(__key);
     if (__match == nullptr)
       return end();
-    return iterator(static_cast<__node_pointer>(__match));
+    return iterator(std::__static_fancy_pointer_cast<__node_pointer>(__match));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator find(const _Key& __key) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const _Key& __key) const {
     auto [__, __match] = __find_equal(__key);
     if (__match == nullptr)
       return end();
-    return const_iterator(static_cast<__node_pointer>(__match));
+    return const_iterator(std::__static_fancy_pointer_cast<__node_pointer>(__match));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI size_type __count_unique(const _Key& __k) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __count_unique(const _Key& __k) const;
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI size_type __count_multi(const _Key& __k) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __count_multi(const _Key& __k) const;
 
   template <bool _LowerBound, class _Key>
-  _LIBCPP_HIDE_FROM_ABI __end_node_pointer __lower_upper_bound_unique_impl(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __end_node_pointer
+  __lower_upper_bound_unique_impl(const _Key& __v) const {
     auto __rt     = __root();
     auto __result = __end_node();
     auto __comp   = __lazy_synth_three_way_comparator<_Compare, _Key, value_type>(value_comp());
@@ -1163,156 +1260,210 @@ public:
       auto __comp_res = __comp(__v, __rt->__get_value());
 
       if (__comp_res.__less()) {
-        __result = static_cast<__end_node_pointer>(__rt);
-        __rt     = static_cast<__node_pointer>(__rt->__left_);
+        __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+        __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
       } else if (__comp_res.__greater()) {
-        __rt = static_cast<__node_pointer>(__rt->__right_);
+        __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
       } else if _LIBCPP_CONSTEXPR (_LowerBound) {
-        return static_cast<__end_node_pointer>(__rt);
+        return std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
       } else {
-        return __rt->__right_ ? static_cast<__end_node_pointer>(std::__tree_min(__rt->__right_)) : __result;
+        return __rt->__right_ ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
+                              : __result;
       }
     }
     return __result;
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator __lower_bound_unique(const _Key& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __lower_bound_unique(const _Key& __v) {
     return iterator(__lower_upper_bound_unique_impl<true>(__v));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator __lower_bound_unique(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __lower_bound_unique(const _Key& __v) const {
     return const_iterator(__lower_upper_bound_unique_impl<true>(__v));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator __upper_bound_unique(const _Key& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __upper_bound_unique(const _Key& __v) {
     return iterator(__lower_upper_bound_unique_impl<false>(__v));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator __upper_bound_unique(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __upper_bound_unique(const _Key& __v) const {
     return iterator(__lower_upper_bound_unique_impl<false>(__v));
   }
 
 private:
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
   __lower_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result);
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
   __lower_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result) const;
 
 public:
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator __lower_bound_multi(const _Key& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __lower_bound_multi(const _Key& __v) {
     return __lower_bound_multi(__v, __root(), __end_node());
   }
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator __lower_bound_multi(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __lower_bound_multi(const _Key& __v) const {
     return __lower_bound_multi(__v, __root(), __end_node());
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator __upper_bound_multi(const _Key& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __upper_bound_multi(const _Key& __v) {
     return __upper_bound_multi(__v, __root(), __end_node());
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator __upper_bound_multi(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __upper_bound_multi(const _Key& __v) const {
     return __upper_bound_multi(__v, __root(), __end_node());
   }
 
 private:
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
   __upper_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result);
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
   __upper_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result) const;
 
 public:
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> __equal_range_unique(const _Key& __k);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> __equal_range_unique(const _Key& __k);
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> __equal_range_unique(const _Key& __k) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  __equal_range_unique(const _Key& __k) const;
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> __equal_range_multi(const _Key& __k);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> __equal_range_multi(const _Key& __k);
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> __equal_range_multi(const _Key& __k) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  __equal_range_multi(const _Key& __k) const;
 
   using _Dp _LIBCPP_NODEBUG           = __tree_node_destructor<__node_allocator>;
   using __node_holder _LIBCPP_NODEBUG = unique_ptr<__node, _Dp>;
 
-  _LIBCPP_HIDE_FROM_ABI __node_holder remove(const_iterator __p) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_holder remove(const_iterator __p) _NOEXCEPT;
 
   // FIXME: Make this function const qualified. Unfortunately doing so
   // breaks existing code which uses non-const callable comparators.
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<__end_node_pointer, __node_base_pointer&> __find_equal(const _Key& __v);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<__end_node_pointer, __node_base_pointer&>
+  __find_equal(const _Key& __v);
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<__end_node_pointer, __node_base_pointer&> __find_equal(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<__end_node_pointer, __node_base_pointer&>
+  __find_equal(const _Key& __v) const {
     return const_cast<__tree*>(this)->__find_equal(__v);
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<__end_node_pointer, __node_base_pointer&>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<__end_node_pointer, __node_base_pointer&>
   __find_equal(const_iterator __hint, __node_base_pointer& __dummy, const _Key& __v);
 
-  _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __tree& __t) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __copy_assign_alloc(const __tree& __t) {
     __copy_assign_alloc(__t, integral_constant<bool, __node_traits::propagate_on_container_copy_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __tree& __t, true_type) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __copy_assign_alloc(const __tree& __t, true_type) {
     if (__node_alloc() != __t.__node_alloc())
       clear();
     __node_alloc() = __t.__node_alloc();
   }
-  _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __tree&, false_type) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __copy_assign_alloc(const __tree&, false_type) {}
 
 private:
-  _LIBCPP_HIDE_FROM_ABI __node_base_pointer& __find_leaf_low(__end_node_pointer& __parent, const value_type& __v);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer&
+  __find_leaf_low(__end_node_pointer& __parent, const value_type& __v);
 
-  _LIBCPP_HIDE_FROM_ABI __node_base_pointer& __find_leaf_high(__end_node_pointer& __parent, const value_type& __v);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer&
+  __find_leaf_high(__end_node_pointer& __parent, const value_type& __v);
 
-  _LIBCPP_HIDE_FROM_ABI __node_base_pointer&
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer&
   __find_leaf(const_iterator __hint, __end_node_pointer& __parent, const value_type& __v);
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI __node_holder __construct_node(_Args&&... __args);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_holder __construct_node(_Args&&... __args);
 
   // TODO: Make this _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_HIDDEN void destroy(__node_pointer __nd) _NOEXCEPT { (__tree_deleter(__node_alloc_))(__nd); }
+  _LIBCPP_HIDDEN _LIBCPP_CONSTEXPR_SINCE_CXX26 void destroy(__node_pointer __nd) _NOEXCEPT {
+    (__tree_deleter(__node_alloc_))(__nd);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign(__tree& __t, false_type);
-  _LIBCPP_HIDE_FROM_ABI void __move_assign(__tree& __t, true_type) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, false_type);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, true_type) _NOEXCEPT_(
       is_nothrow_move_assignable<value_compare>::value&& is_nothrow_move_assignable<__node_allocator>::value);
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__tree& __t)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t)
       _NOEXCEPT_(!__node_traits::propagate_on_container_move_assignment::value ||
                  is_nothrow_move_assignable<__node_allocator>::value) {
     __move_assign_alloc(__t, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__tree& __t, true_type)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t, true_type)
       _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value) {
     __node_alloc() = std::move(__t.__node_alloc());
   }
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__tree&, false_type) _NOEXCEPT {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree&, false_type) _NOEXCEPT {}
+
+  template <class _From, class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_value__sfinae(true_type, __get_node_value_type_t<value_type>& __lhs, _From&& __rhs) {
+    __node_allocator& __na = __node_alloc();
+    __node_traits::destroy(__na, std::addressof(__lhs));
+    __node_traits::construct(__na, std::addressof(__lhs), __rhs.first, __rhs.second);
+  }
+
+  template <class _From, class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_value__sfinae(false_type, __get_node_value_type_t<value_type>&, _From&&) {
+    // This method body should never be run. It only exists to allow for compilation. See note in __assign_value for
+    // more information.
+  }
 
   template <class _From, class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI static void __assign_value(__get_node_value_type_t<value_type>& __lhs, _From&& __rhs) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_value(__get_node_value_type_t<value_type>& __lhs, _From&& __rhs) {
     using __key_type = __remove_const_t<typename value_type::first_type>;
 
-    // This is technically UB, since the object was constructed as `const`.
-    // Clang doesn't optimize on this currently though.
-    const_cast<__key_type&>(__lhs.first) = const_cast<__copy_cvref_t<_From, __key_type>&&>(__rhs.first);
-    __lhs.second                         = std::forward<_From>(__rhs).second;
+#if _LIBCPP_STD_VER >= 26
+
+    if (std::is_constant_evaluated() && std::is_copy_constructible_v<decltype(__rhs.first)>) {
+      // We use copy, and not "move" as the constraint here because we can NOT move
+      // from `const key_type`, which is how `value_type` is defined for `std::map`
+      // `typedef pair<const key_type, mapped_type> value_type;`
+      // so we must copy it to not perform undefined behavior which
+      // is disallowed during constant evaluation.
+
+      // Furthermore, `const_cast` is not allowed during constant evaluation.
+      // We get around this by deleting `__lhs` and creating a new node in-place
+      // to avoid the `const_cast` when attempting to assign to `__lhs.first`.
+
+      // We create a sfinae wrapper method here, because if the body of the `true_type` overload for
+      // `__assign_value__sfinae()` gets template instantiated within `__assign_value`,
+      // the code will fail to compile when
+      // the value is not copy_constructible (even for runtime execution); unless we use `if constexpr`.
+      // Given that the copy-constructible code path will be a performance regression,
+      // we want to restrict it to only execute during constant evaluation
+      //, we need to delay the template instantiation.
+
+      __assign_value__sfinae(std::integral_constant<bool, std::is_copy_constructible_v<decltype(__rhs.first)>>(),
+                             std::forward<decltype(__lhs)>(__lhs),
+                             std::forward<decltype(__rhs)>(__rhs));
+
+    } else
+#endif
+    {
+      // This is technically UB, since the object was constructed as `const`.
+      // Clang doesn't optimize on this currently though.
+      const_cast<__key_type&>(__lhs.first) = const_cast<__copy_cvref_t<_From, __key_type>&&>(__rhs.first);
+      __lhs.second                         = std::forward<_From>(__rhs).second;
+    }
   }
 
   template <class _To, class _From, class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
@@ -1321,26 +1472,26 @@ private:
   }
 
   struct _DetachedTreeCache {
-    _LIBCPP_HIDE_FROM_ABI explicit _DetachedTreeCache(__tree* __t) _NOEXCEPT
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit _DetachedTreeCache(__tree* __t) _NOEXCEPT
         : __t_(__t),
           __cache_root_(__detach_from_tree(__t)) {
       __advance();
     }
 
-    _LIBCPP_HIDE_FROM_ABI __node_pointer __get() const _NOEXCEPT { return __cache_elem_; }
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __get() const _NOEXCEPT { return __cache_elem_; }
 
-    _LIBCPP_HIDE_FROM_ABI void __advance() _NOEXCEPT {
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __advance() _NOEXCEPT {
       __cache_elem_ = __cache_root_;
       if (__cache_root_) {
         __cache_root_ = __detach_next(__cache_root_);
       }
     }
 
-    _LIBCPP_HIDE_FROM_ABI ~_DetachedTreeCache() {
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~_DetachedTreeCache() {
       __t_->destroy(__cache_elem_);
       if (__cache_root_) {
         while (__cache_root_->__parent_ != nullptr)
-          __cache_root_ = static_cast<__node_pointer>(__cache_root_->__parent_);
+          __cache_root_ = std::__static_fancy_pointer_cast<__node_pointer>(__cache_root_->__parent_);
         __t_->destroy(__cache_root_);
       }
     }
@@ -1349,8 +1500,8 @@ private:
     _DetachedTreeCache& operator=(_DetachedTreeCache const&) = delete;
 
   private:
-    _LIBCPP_HIDE_FROM_ABI static __node_pointer __detach_from_tree(__tree* __t) _NOEXCEPT;
-    _LIBCPP_HIDE_FROM_ABI static __node_pointer __detach_next(__node_pointer) _NOEXCEPT;
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static __node_pointer __detach_from_tree(__tree* __t) _NOEXCEPT;
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static __node_pointer __detach_next(__node_pointer) _NOEXCEPT;
 
     __tree* __t_;
     __node_pointer __cache_root_;
@@ -1363,24 +1514,23 @@ private:
   public:
     using pointer = __node_pointer;
 
-    _LIBCPP_HIDE_FROM_ABI __tree_deleter(__node_allocator& __alloc) : __alloc_(__alloc) {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_deleter(__node_allocator& __alloc) : __alloc_(__alloc) {}
 
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
     _LIBCPP_HIDE_FROM_ABI
 #endif
-    void
-    operator()(__node_pointer __ptr) {
+    _LIBCPP_CONSTEXPR_SINCE_CXX26 void operator()(__node_pointer __ptr) {
       if (!__ptr)
         return;
 
-      (*this)(static_cast<__node_pointer>(__ptr->__left_));
+      (*this)(std::__static_fancy_pointer_cast<__node_pointer>(__ptr->__left_));
 
       auto __right = __ptr->__right_;
 
       __node_traits::destroy(__alloc_, std::addressof(__ptr->__get_value()));
       __node_traits::deallocate(__alloc_, __ptr, 1);
 
-      (*this)(static_cast<__node_pointer>(__right));
+      (*this)(std::__static_fancy_pointer_cast<__node_pointer>(__right));
     }
   };
 
@@ -1391,26 +1541,25 @@ private:
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
   _LIBCPP_HIDE_FROM_ABI
 #endif
-  __node_pointer
-  __copy_construct_tree(__node_pointer __src) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __copy_construct_tree(__node_pointer __src) {
     if (!__src)
       return nullptr;
 
     __node_holder __new_node = __construct_node(__src->__get_value());
 
     unique_ptr<__node, __tree_deleter> __left(
-        __copy_construct_tree(static_cast<__node_pointer>(__src->__left_)), __node_alloc_);
-    __node_pointer __right = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
+        __copy_construct_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__left_)), __node_alloc_);
+    __node_pointer __right = __copy_construct_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__right_));
 
     __node_pointer __new_node_ptr = __new_node.release();
 
     __new_node_ptr->__is_black_ = __src->__is_black_;
-    __new_node_ptr->__left_     = static_cast<__node_base_pointer>(__left.release());
-    __new_node_ptr->__right_    = static_cast<__node_base_pointer>(__right);
+    __new_node_ptr->__left_     = std::__static_fancy_pointer_cast<__node_base_pointer>(__left.release());
+    __new_node_ptr->__right_    = std::__static_fancy_pointer_cast<__node_base_pointer>(__right);
     if (__new_node_ptr->__left_)
-      __new_node_ptr->__left_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
+      __new_node_ptr->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__new_node_ptr);
     if (__new_node_ptr->__right_)
-      __new_node_ptr->__right_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
+      __new_node_ptr->__right_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__new_node_ptr);
     return __new_node_ptr;
   }
 
@@ -1420,8 +1569,7 @@ private:
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
   _LIBCPP_HIDE_FROM_ABI
 #endif
-  __node_pointer
-  __copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
     if (!__src) {
       destroy(__dest);
       return nullptr;
@@ -1432,24 +1580,26 @@ private:
 
     // If we already have a left node in the destination tree, reuse it and copy-assign recursively
     if (__dest->__left_) {
-      __dest->__left_ = static_cast<__node_base_pointer>(__copy_assign_tree(
-          static_cast<__node_pointer>(__dest->__left_), static_cast<__node_pointer>(__src->__left_)));
+      __dest->__left_ = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_assign_tree(
+          std::__static_fancy_pointer_cast<__node_pointer>(__dest->__left_),
+          std::__static_fancy_pointer_cast<__node_pointer>(__src->__left_)));
 
       // Otherwise, we must create new nodes; copy-construct from here on
     } else if (__src->__left_) {
-      auto __new_left       = __copy_construct_tree(static_cast<__node_pointer>(__src->__left_));
-      __dest->__left_       = static_cast<__node_base_pointer>(__new_left);
-      __new_left->__parent_ = static_cast<__end_node_pointer>(__dest);
+      auto __new_left       = __copy_construct_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__left_));
+      __dest->__left_       = std::__static_fancy_pointer_cast<__node_base_pointer>(__new_left);
+      __new_left->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__dest);
     }
 
     // Identical to the left case above, just for the right nodes
     if (__dest->__right_) {
-      __dest->__right_ = static_cast<__node_base_pointer>(__copy_assign_tree(
-          static_cast<__node_pointer>(__dest->__right_), static_cast<__node_pointer>(__src->__right_)));
+      __dest->__right_ = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_assign_tree(
+          std::__static_fancy_pointer_cast<__node_pointer>(__dest->__right_),
+          std::__static_fancy_pointer_cast<__node_pointer>(__src->__right_)));
     } else if (__src->__right_) {
-      auto __new_right       = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
-      __dest->__right_       = static_cast<__node_base_pointer>(__new_right);
-      __new_right->__parent_ = static_cast<__end_node_pointer>(__dest);
+      auto __new_right       = __copy_construct_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__right_));
+      __dest->__right_       = std::__static_fancy_pointer_cast<__node_base_pointer>(__new_right);
+      __new_right->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__dest);
     }
 
     return __dest;
@@ -1458,16 +1608,16 @@ private:
 
 // Precondition:  __size_ != 0
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_pointer
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_pointer
 __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_from_tree(__tree* __t) _NOEXCEPT {
-  __node_pointer __cache                = static_cast<__node_pointer>(__t->__begin_node_);
+  __node_pointer __cache                = std::__static_fancy_pointer_cast<__node_pointer>(__t->__begin_node_);
   __t->__begin_node_                    = __t->__end_node();
   __t->__end_node()->__left_->__parent_ = nullptr;
   __t->__end_node()->__left_            = nullptr;
   __t->__size_                          = 0;
   // __cache->__left_ == nullptr
   if (__cache->__right_ != nullptr)
-    __cache = static_cast<__node_pointer>(__cache->__right_);
+    __cache = std::__static_fancy_pointer_cast<__node_pointer>(__cache->__right_);
   // __cache->__left_ == nullptr
   // __cache->__right_ == nullptr
   return __cache;
@@ -1478,27 +1628,28 @@ __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_from_tree(__tree
 //    __cache->right_ == nullptr
 //    This is no longer a red-black tree
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_pointer
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_pointer
 __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_next(__node_pointer __cache) _NOEXCEPT {
   if (__cache->__parent_ == nullptr)
     return nullptr;
-  if (std::__tree_is_left_child(static_cast<__node_base_pointer>(__cache))) {
+  if (std::__tree_is_left_child(std::__static_fancy_pointer_cast<__node_base_pointer>(__cache))) {
     __cache->__parent_->__left_ = nullptr;
-    __cache                     = static_cast<__node_pointer>(__cache->__parent_);
+    __cache                     = std::__static_fancy_pointer_cast<__node_pointer>(__cache->__parent_);
     if (__cache->__right_ == nullptr)
       return __cache;
-    return static_cast<__node_pointer>(std::__tree_leaf(__cache->__right_));
+    return std::__static_fancy_pointer_cast<__node_pointer>(std::__tree_leaf(__cache->__right_));
   }
   // __cache is right child
   __cache->__parent_unsafe()->__right_ = nullptr;
-  __cache                              = static_cast<__node_pointer>(__cache->__parent_);
+  __cache                              = std::__static_fancy_pointer_cast<__node_pointer>(__cache->__parent_);
   if (__cache->__left_ == nullptr)
     return __cache;
-  return static_cast<__node_pointer>(std::__tree_leaf(__cache->__left_));
+  return std::__static_fancy_pointer_cast<__node_pointer>(std::__tree_leaf(__cache->__left_));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-__tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(const __tree& __t) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>&
+__tree<_Tp, _Compare, _Allocator>::operator=(const __tree& __t) {
   if (this == std::addressof(__t))
     return *this;
 
@@ -1506,14 +1657,15 @@ __tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(
   __copy_assign_alloc(__t);
 
   if (__size_ != 0) {
-    *__root_ptr() = static_cast<__node_base_pointer>(__copy_assign_tree(__root(), __t.__root()));
+    *__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_assign_tree(__root(), __t.__root()));
   } else {
-    *__root_ptr() = static_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
+    *__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
     if (__root())
       __root()->__parent_ = __end_node();
   }
-  __begin_node_ =
-      __end_node()->__left_ ? static_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_)) : __end_node();
+  __begin_node_ = __end_node()->__left_
+                    ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_))
+                    : __end_node();
   __size_ = __t.size();
 
   return *this;
@@ -1521,7 +1673,8 @@ __tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _ForwardIterator>
-void __tree<_Tp, _Compare, _Allocator>::__assign_unique(_ForwardIterator __first, _ForwardIterator __last) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void
+__tree<_Tp, _Compare, _Allocator>::__assign_unique(_ForwardIterator __first, _ForwardIterator __last) {
   using _ITraits     = iterator_traits<_ForwardIterator>;
   using _ItValueType = typename _ITraits::value_type;
   static_assert(
@@ -1541,7 +1694,8 @@ void __tree<_Tp, _Compare, _Allocator>::__assign_unique(_ForwardIterator __first
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _InputIterator>
-void __tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _InputIterator __last) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void
+__tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _InputIterator __last) {
   using _ITraits     = iterator_traits<_InputIterator>;
   using _ItValueType = typename _ITraits::value_type;
   static_assert(
@@ -1560,7 +1714,7 @@ void __tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-__tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
     : __begin_node_(__end_node()),
       __node_alloc_(__node_traits::select_on_container_copy_construction(__t.__node_alloc())),
       __size_(0),
@@ -1568,14 +1722,14 @@ __tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
   if (__t.size() == 0)
     return;
 
-  *__root_ptr()       = static_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
+  *__root_ptr()       = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
   __root()->__parent_ = __end_node();
-  __begin_node_       = static_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
+  __begin_node_       = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
   __size_             = __t.size();
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-__tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
     is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value)
     : __begin_node_(std::move(__t.__begin_node_)),
       __end_node_(std::move(__t.__end_node_)),
@@ -1585,7 +1739,7 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
   if (__size_ == 0)
     __begin_node_ = __end_node();
   else {
-    __end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
+    __end_node()->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__end_node());
     __t.__begin_node_                = __t.__end_node();
     __t.__end_node()->__left_        = nullptr;
     __t.__size_                      = 0;
@@ -1593,7 +1747,7 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-__tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __a)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __a)
     : __node_alloc_(__node_allocator(__a)), __size_(0), __value_comp_(std::move(__t.value_comp())) {
   if (__a == __t.__alloc()) {
     if (__t.__size_ == 0)
@@ -1601,7 +1755,7 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __
     else {
       __begin_node_                    = __t.__begin_node_;
       __end_node()->__left_            = __t.__end_node()->__left_;
-      __end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
+      __end_node()->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__end_node());
       __size_                          = __t.__size_;
       __t.__begin_node_                = __t.__end_node();
       __t.__end_node()->__left_        = nullptr;
@@ -1613,9 +1767,9 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
     _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value&& is_nothrow_move_assignable<__node_allocator>::value) {
-  destroy(static_cast<__node_pointer>(__end_node()->__left_));
+  destroy(std::__static_fancy_pointer_cast<__node_pointer>(__end_node()->__left_));
   __begin_node_ = __t.__begin_node_;
   __end_node_   = __t.__end_node_;
   __move_assign_alloc(__t);
@@ -1624,7 +1778,7 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
   if (__size_ == 0)
     __begin_node_ = __end_node();
   else {
-    __end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
+    __end_node()->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__end_node());
     __t.__begin_node_                = __t.__end_node();
     __t.__end_node()->__left_        = nullptr;
     __t.__size_                      = 0;
@@ -1632,7 +1786,7 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
   if (__node_alloc() == __t.__node_alloc())
     __move_assign(__t, true_type());
   else {
@@ -1653,7 +1807,7 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t)
 #if _LIBCPP_STD_VER <= 11
     _NOEXCEPT_(__is_nothrow_swappable_v<value_compare> &&
                (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>))
@@ -1678,7 +1832,7 @@ void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t)
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::clear() _NOEXCEPT {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::clear() _NOEXCEPT {
   destroy(__root());
   __size_               = 0;
   __begin_node_         = __end_node();
@@ -1689,23 +1843,23 @@ void __tree<_Tp, _Compare, _Allocator>::clear() _NOEXCEPT {
 // Set __parent to parent of null leaf
 // Return reference to null leaf
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
 __tree<_Tp, _Compare, _Allocator>::__find_leaf_low(__end_node_pointer& __parent, const value_type& __v) {
   __node_pointer __nd = __root();
   if (__nd != nullptr) {
     while (true) {
       if (value_comp()(__nd->__get_value(), __v)) {
         if (__nd->__right_ != nullptr)
-          __nd = static_cast<__node_pointer>(__nd->__right_);
+          __nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__right_);
         else {
-          __parent = static_cast<__end_node_pointer>(__nd);
+          __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
           return __nd->__right_;
         }
       } else {
         if (__nd->__left_ != nullptr)
-          __nd = static_cast<__node_pointer>(__nd->__left_);
+          __nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__left_);
         else {
-          __parent = static_cast<__end_node_pointer>(__nd);
+          __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
           return __parent->__left_;
         }
       }
@@ -1719,23 +1873,23 @@ __tree<_Tp, _Compare, _Allocator>::__find_leaf_low(__end_node_pointer& __parent,
 // Set __parent to parent of null leaf
 // Return reference to null leaf
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
 __tree<_Tp, _Compare, _Allocator>::__find_leaf_high(__end_node_pointer& __parent, const value_type& __v) {
   __node_pointer __nd = __root();
   if (__nd != nullptr) {
     while (true) {
       if (value_comp()(__v, __nd->__get_value())) {
         if (__nd->__left_ != nullptr)
-          __nd = static_cast<__node_pointer>(__nd->__left_);
+          __nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__left_);
         else {
-          __parent = static_cast<__end_node_pointer>(__nd);
+          __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
           return __parent->__left_;
         }
       } else {
         if (__nd->__right_ != nullptr)
-          __nd = static_cast<__node_pointer>(__nd->__right_);
+          __nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__right_);
         else {
-          __parent = static_cast<__end_node_pointer>(__nd);
+          __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
           return __nd->__right_;
         }
       }
@@ -1752,7 +1906,8 @@ __tree<_Tp, _Compare, _Allocator>::__find_leaf_high(__end_node_pointer& __parent
 // Set __parent to parent of null leaf
 // Return reference to null leaf
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer& __tree<_Tp, _Compare, _Allocator>::__find_leaf(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
+__tree<_Tp, _Compare, _Allocator>::__find_leaf(
     const_iterator __hint, __end_node_pointer& __parent, const value_type& __v) {
   if (__hint == end() || !value_comp()(*__hint, __v)) // check before
   {
@@ -1761,11 +1916,11 @@ typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer& __tree<_Tp, _Co
     if (__prior == begin() || !value_comp()(__v, *--__prior)) {
       // *prev(__hint) <= __v <= *__hint
       if (__hint.__ptr_->__left_ == nullptr) {
-        __parent = static_cast<__end_node_pointer>(__hint.__ptr_);
+        __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__hint.__ptr_);
         return __parent->__left_;
       } else {
-        __parent = static_cast<__end_node_pointer>(__prior.__ptr_);
-        return static_cast<__node_base_pointer>(__prior.__ptr_)->__right_;
+        __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__prior.__ptr_);
+        return std::__static_fancy_pointer_cast<__node_base_pointer>(__prior.__ptr_)->__right_;
       }
     }
     // __v < *prev(__hint)
@@ -1780,8 +1935,9 @@ typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer& __tree<_Tp, _Co
 // If __v doesn't exist, return the parent of the null leaf and a reference to the pointer to the null leaf.
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-_LIBCPP_HIDE_FROM_ABI pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
-                           typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
+_LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
+                                   typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
 __tree<_Tp, _Compare, _Allocator>::__find_equal(const _Key& __v) {
   using _Pair = pair<__end_node_pointer, __node_base_pointer&>;
 
@@ -1801,18 +1957,18 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const _Key& __v) {
 
     if (__comp_res.__less()) {
       if (__nd->__left_ == nullptr)
-        return _Pair(static_cast<__end_node_pointer>(__nd), __nd->__left_);
+        return _Pair(std::__static_fancy_pointer_cast<__end_node_pointer>(__nd), __nd->__left_);
 
       __node_ptr = std::addressof(__nd->__left_);
-      __nd       = static_cast<__node_pointer>(__nd->__left_);
+      __nd       = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__left_);
     } else if (__comp_res.__greater()) {
       if (__nd->__right_ == nullptr)
-        return _Pair(static_cast<__end_node_pointer>(__nd), __nd->__right_);
+        return _Pair(std::__static_fancy_pointer_cast<__end_node_pointer>(__nd), __nd->__right_);
 
       __node_ptr = std::addressof(__nd->__right_);
-      __nd       = static_cast<__node_pointer>(__nd->__right_);
+      __nd       = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__right_);
     } else {
-      return _Pair(static_cast<__end_node_pointer>(__nd), *__node_ptr);
+      return _Pair(std::__static_fancy_pointer_cast<__end_node_pointer>(__nd), *__node_ptr);
     }
   }
 }
@@ -1825,8 +1981,9 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const _Key& __v) {
 // If __v doesn't exist, return the parent of the null leaf and a reference to the pointer to the null leaf.
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-_LIBCPP_HIDE_FROM_ABI pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
-                           typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
+_LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
+                                   typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
 __tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_base_pointer& __dummy, const _Key& __v) {
   using _Pair = pair<__end_node_pointer, __node_base_pointer&>;
 
@@ -1837,7 +1994,7 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_ba
       // *prev(__hint) < __v < *__hint
       if (__hint.__ptr_->__left_ == nullptr)
         return _Pair(__hint.__ptr_, __hint.__ptr_->__left_);
-      return _Pair(__prior.__ptr_, static_cast<__node_pointer>(__prior.__ptr_)->__right_);
+      return _Pair(__prior.__ptr_, std::__static_fancy_pointer_cast<__node_pointer>(__prior.__ptr_)->__right_);
     }
     // __v <= *prev(__hint)
     return __find_equal(__v);
@@ -1849,7 +2006,7 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_ba
     if (__next == end() || value_comp()(__v, *__next)) {
       // *__hint < __v < *std::next(__hint)
       if (__hint.__get_np()->__right_ == nullptr)
-        return _Pair(__hint.__ptr_, static_cast<__node_pointer>(__hint.__ptr_)->__right_);
+        return _Pair(__hint.__ptr_, std::__static_fancy_pointer_cast<__node_pointer>(__hint.__ptr_)->__right_);
       return _Pair(__next.__ptr_, __next.__ptr_->__left_);
     }
     // *next(__hint) <= __v
@@ -1857,12 +2014,12 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_ba
   }
 
   // else __v == *__hint
-  __dummy = static_cast<__node_base_pointer>(__hint.__ptr_);
+  __dummy = std::__static_fancy_pointer_cast<__node_base_pointer>(__hint.__ptr_);
   return _Pair(__hint.__ptr_, __dummy);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::__insert_node_at(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__insert_node_at(
     __end_node_pointer __parent, __node_base_pointer& __child, __node_base_pointer __new_node) _NOEXCEPT {
   __new_node->__left_   = nullptr;
   __new_node->__right_  = nullptr;
@@ -1870,14 +2027,14 @@ void __tree<_Tp, _Compare, _Allocator>::__insert_node_at(
   // __new_node->__is_black_ is initialized in __tree_balance_after_insert
   __child = __new_node;
   if (__begin_node_->__left_ != nullptr)
-    __begin_node_ = static_cast<__end_node_pointer>(__begin_node_->__left_);
+    __begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__begin_node_->__left_);
   std::__tree_balance_after_insert(__end_node()->__left_, __child);
   ++__size_;
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class... _Args>
-typename __tree<_Tp, _Compare, _Allocator>::__node_holder
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_holder
 __tree<_Tp, _Compare, _Allocator>::__construct_node(_Args&&... __args) {
   __node_allocator& __na = __node_alloc();
   __node_holder __h(__node_traits::allocate(__na, 1), _Dp(__na));
@@ -1888,35 +2045,35 @@ __tree<_Tp, _Compare, _Allocator>::__construct_node(_Args&&... __args) {
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class... _Args>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+typename __tree<_Tp, _Compare, _Allocator>::iterator _LIBCPP_CONSTEXPR_SINCE_CXX26
 __tree<_Tp, _Compare, _Allocator>::__emplace_multi(_Args&&... __args) {
   __node_holder __h = __construct_node(std::forward<_Args>(__args)...);
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf_high(__parent, __h->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
-  return iterator(static_cast<__node_pointer>(__h.release()));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
+  return iterator(std::__static_fancy_pointer_cast<__node_pointer>(__h.release()));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class... _Args>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+typename __tree<_Tp, _Compare, _Allocator>::iterator _LIBCPP_CONSTEXPR_SINCE_CXX26
 __tree<_Tp, _Compare, _Allocator>::__emplace_hint_multi(const_iterator __p, _Args&&... __args) {
   __node_holder __h = __construct_node(std::forward<_Args>(__args)...);
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf(__p, __parent, __h->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
-  return iterator(static_cast<__node_pointer>(__h.release()));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
+  return iterator(std::__static_fancy_pointer_cast<__node_pointer>(__h.release()));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, bool>
+pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, bool> _LIBCPP_CONSTEXPR_SINCE_CXX26
 __tree<_Tp, _Compare, _Allocator>::__node_assign_unique(const value_type& __v, __node_pointer __nd) {
   auto [__parent, __child] = __find_equal(__v);
-  __node_pointer __r       = static_cast<__node_pointer>(__child);
+  __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
   bool __inserted          = false;
   if (__child == nullptr) {
     __assign_value(__nd->__get_value(), __v);
-    __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd));
+    __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd));
     __r        = __nd;
     __inserted = true;
   }
@@ -1924,39 +2081,39 @@ __tree<_Tp, _Compare, _Allocator>::__node_assign_unique(const value_type& __v, _
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_insert_multi(__node_pointer __nd) {
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf_high(__parent, __nd->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd));
   return iterator(__nd);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_insert_multi(const_iterator __p, __node_pointer __nd) {
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf(__p, __parent, __nd->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd));
   return iterator(__nd);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__remove_node_pointer(__node_pointer __ptr) _NOEXCEPT {
   iterator __r(__ptr);
   ++__r;
   if (__begin_node_ == __ptr)
     __begin_node_ = __r.__ptr_;
   --__size_;
-  std::__tree_remove(__end_node()->__left_, static_cast<__node_base_pointer>(__ptr));
+  std::__tree_remove(__end_node()->__left_, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
   return __r;
 }
 
 #if _LIBCPP_STD_VER >= 17
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle, class _InsertReturnType>
-_LIBCPP_HIDE_FROM_ABI _InsertReturnType
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _InsertReturnType
 __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __nh) {
   if (__nh.empty())
     return _InsertReturnType{end(), false, _NodeHandle()};
@@ -1964,16 +2121,17 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
   __node_pointer __ptr = __nh.__ptr_;
   auto [__parent, __child] = __find_equal(__ptr->__get_value());
   if (__child != nullptr)
-    return _InsertReturnType{iterator(static_cast<__node_pointer>(__child)), false, std::move(__nh)};
+    return _InsertReturnType{
+        iterator(std::__static_fancy_pointer_cast<__node_pointer>(__child)), false, std::move(__nh)};
 
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__ptr));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
   __nh.__release_ptr();
   return _InsertReturnType{iterator(__ptr), true, _NodeHandle()};
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __hint, _NodeHandle&& __nh) {
   if (__nh.empty())
     return end();
@@ -1981,9 +2139,9 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
   __node_pointer __ptr = __nh.__ptr_;
   __node_base_pointer __dummy;
   auto [__parent, __child] = __find_equal(__hint, __dummy, __ptr->__get_value());
-  __node_pointer __r       = static_cast<__node_pointer>(__child);
+  __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
   if (__child == nullptr) {
-    __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__ptr));
+    __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
     __r = __ptr;
     __nh.__release_ptr();
   }
@@ -1992,7 +2150,8 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(key_type const& __key) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(key_type const& __key) {
   iterator __it = find(__key);
   if (__it == end())
     return _NodeHandle();
@@ -2001,7 +2160,8 @@ _LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_hand
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const_iterator __p) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const_iterator __p) {
   __node_pointer __np = __p.__get_np();
   __remove_node_pointer(__np);
   return _NodeHandle(__np, __alloc());
@@ -2009,7 +2169,7 @@ _LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_hand
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Comp2>
-_LIBCPP_HIDE_FROM_ABI void
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
 __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source) {
   for (iterator __i = __source.begin(); __i != __source.end();) {
     __node_pointer __src_ptr = __i.__get_np();
@@ -2018,27 +2178,27 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_unique(__tree<_Tp, _Comp2
     if (__child != nullptr)
       continue;
     __source.__remove_node_pointer(__src_ptr);
-    __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__src_ptr));
+    __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__src_ptr));
   }
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_multi(_NodeHandle&& __nh) {
   if (__nh.empty())
     return end();
   __node_pointer __ptr = __nh.__ptr_;
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf_high(__parent, __ptr->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__ptr));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
   __nh.__release_ptr();
   return iterator(__ptr);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_multi(const_iterator __hint, _NodeHandle&& __nh) {
   if (__nh.empty())
     return end();
@@ -2046,14 +2206,14 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_multi(const_iterator __h
   __node_pointer __ptr = __nh.__ptr_;
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf(__hint, __parent, __ptr->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__ptr));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
   __nh.__release_ptr();
   return iterator(__ptr);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Comp2>
-_LIBCPP_HIDE_FROM_ABI void
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
 __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source) {
   for (iterator __i = __source.begin(); __i != __source.end();) {
     __node_pointer __src_ptr = __i.__get_np();
@@ -2061,14 +2221,15 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_multi(__tree<_Tp, _Comp2,
     __node_base_pointer& __child = __find_leaf_high(__parent, __src_ptr->__get_value());
     ++__i;
     __source.__remove_node_pointer(__src_ptr);
-    __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__src_ptr));
+    __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__src_ptr));
   }
 }
 
 #endif // _LIBCPP_STD_VER >= 17
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __p) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26
+    typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __p) {
   __node_pointer __np    = __p.__get_np();
   iterator __r           = __remove_node_pointer(__np);
   __node_allocator& __na = __node_alloc();
@@ -2078,7 +2239,7 @@ typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allo
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __f, const_iterator __l) {
   while (__f != __l)
     __f = erase(__f);
@@ -2087,7 +2248,7 @@ __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __f, const_iterator __l)
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::size_type
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
 __tree<_Tp, _Compare, _Allocator>::__erase_unique(const _Key& __k) {
   iterator __i = find(__k);
   if (__i == end())
@@ -2098,7 +2259,7 @@ __tree<_Tp, _Compare, _Allocator>::__erase_unique(const _Key& __k) {
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::size_type
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
 __tree<_Tp, _Compare, _Allocator>::__erase_multi(const _Key& __k) {
   pair<iterator, iterator> __p = __equal_range_multi(__k);
   size_type __r                = 0;
@@ -2109,16 +2270,16 @@ __tree<_Tp, _Compare, _Allocator>::__erase_multi(const _Key& __k) {
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::size_type
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
 __tree<_Tp, _Compare, _Allocator>::__count_unique(const _Key& __k) const {
   __node_pointer __rt = __root();
   auto __comp         = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp());
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __rt = static_cast<__node_pointer>(__rt->__left_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
       return 1;
   }
@@ -2127,7 +2288,7 @@ __tree<_Tp, _Compare, _Allocator>::__count_unique(const _Key& __k) const {
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::size_type
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
 __tree<_Tp, _Compare, _Allocator>::__count_multi(const _Key& __k) const {
   __end_node_pointer __result = __end_node();
   __node_pointer __rt         = __root();
@@ -2135,78 +2296,85 @@ __tree<_Tp, _Compare, _Allocator>::__count_multi(const _Key& __k) const {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
       return std::distance(
-          __lower_bound_multi(__k, static_cast<__node_pointer>(__rt->__left_), static_cast<__end_node_pointer>(__rt)),
-          __upper_bound_multi(__k, static_cast<__node_pointer>(__rt->__right_), __result));
+          __lower_bound_multi(__k,
+                              std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_),
+                              std::__static_fancy_pointer_cast<__end_node_pointer>(__rt)),
+          __upper_bound_multi(__k, std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_), __result));
   }
   return 0;
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
+__tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
     const _Key& __v, __node_pointer __root, __end_node_pointer __result) {
   while (__root != nullptr) {
     if (!value_comp()(__root->__get_value(), __v)) {
-      __result = static_cast<__end_node_pointer>(__root);
-      __root   = static_cast<__node_pointer>(__root->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
+      __root   = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
     } else
-      __root = static_cast<__node_pointer>(__root->__right_);
+      __root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
   }
   return iterator(__result);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::const_iterator __tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::const_iterator
+__tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
     const _Key& __v, __node_pointer __root, __end_node_pointer __result) const {
   while (__root != nullptr) {
     if (!value_comp()(__root->__get_value(), __v)) {
-      __result = static_cast<__end_node_pointer>(__root);
-      __root   = static_cast<__node_pointer>(__root->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
+      __root   = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
     } else
-      __root = static_cast<__node_pointer>(__root->__right_);
+      __root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
   }
   return const_iterator(__result);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
+__tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
     const _Key& __v, __node_pointer __root, __end_node_pointer __result) {
   while (__root != nullptr) {
     if (value_comp()(__v, __root->__get_value())) {
-      __result = static_cast<__end_node_pointer>(__root);
-      __root   = static_cast<__node_pointer>(__root->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
+      __root   = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
     } else
-      __root = static_cast<__node_pointer>(__root->__right_);
+      __root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
   }
   return iterator(__result);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::const_iterator __tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::const_iterator
+__tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
     const _Key& __v, __node_pointer __root, __end_node_pointer __result) const {
   while (__root != nullptr) {
     if (value_comp()(__v, __root->__get_value())) {
-      __result = static_cast<__end_node_pointer>(__root);
-      __root   = static_cast<__node_pointer>(__root->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
+      __root   = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
     } else
-      __root = static_cast<__node_pointer>(__root->__right_);
+      __root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
   }
   return const_iterator(__result);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
-__tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26
+    pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
+    __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) {
   using _Pp                   = pair<iterator, iterator>;
   __end_node_pointer __result = __end_node();
   __node_pointer __rt         = __root();
@@ -2214,22 +2382,23 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
       return _Pp(iterator(__rt),
-                 iterator(__rt->__right_ != nullptr ? static_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
-                                                    : __result));
+                 iterator(__rt->__right_ != nullptr
+                              ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
+                              : __result));
   }
   return _Pp(iterator(__result), iterator(__result));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
-     typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
+_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
+                                   typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
 __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) const {
   using _Pp                   = pair<const_iterator, const_iterator>;
   __end_node_pointer __result = __end_node();
@@ -2238,23 +2407,25 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) const {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
       return _Pp(
           const_iterator(__rt),
-          const_iterator(
-              __rt->__right_ != nullptr ? static_cast<__end_node_pointer>(std::__tree_min(__rt->__right_)) : __result));
+          const_iterator(__rt->__right_ != nullptr
+                             ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
+                             : __result));
   }
   return _Pp(const_iterator(__result), const_iterator(__result));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
-__tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26
+    pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
+    __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) {
   using _Pp                   = pair<iterator, iterator>;
   __end_node_pointer __result = __end_node();
   __node_pointer __rt         = __root();
@@ -2262,22 +2433,23 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
-      return _Pp(
-          __lower_bound_multi(__k, static_cast<__node_pointer>(__rt->__left_), static_cast<__end_node_pointer>(__rt)),
-          __upper_bound_multi(__k, static_cast<__node_pointer>(__rt->__right_), __result));
+      return _Pp(__lower_bound_multi(__k,
+                                     std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_),
+                                     std::__static_fancy_pointer_cast<__end_node_pointer>(__rt)),
+                 __upper_bound_multi(__k, std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_), __result));
   }
   return _Pp(iterator(__result), iterator(__result));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
-     typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
+_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
+                                   typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
 __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) const {
   using _Pp                   = pair<const_iterator, const_iterator>;
   __end_node_pointer __result = __end_node();
@@ -2286,35 +2458,37 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) const {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
-      return _Pp(
-          __lower_bound_multi(__k, static_cast<__node_pointer>(__rt->__left_), static_cast<__end_node_pointer>(__rt)),
-          __upper_bound_multi(__k, static_cast<__node_pointer>(__rt->__right_), __result));
+      return _Pp(__lower_bound_multi(__k,
+                                     std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_),
+                                     std::__static_fancy_pointer_cast<__end_node_pointer>(__rt)),
+                 __upper_bound_multi(__k, std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_), __result));
   }
   return _Pp(const_iterator(__result), const_iterator(__result));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_holder
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_holder
 __tree<_Tp, _Compare, _Allocator>::remove(const_iterator __p) _NOEXCEPT {
   __node_pointer __np = __p.__get_np();
   if (__begin_node_ == __p.__ptr_) {
     if (__np->__right_ != nullptr)
-      __begin_node_ = static_cast<__end_node_pointer>(__np->__right_);
+      __begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__np->__right_);
     else
-      __begin_node_ = static_cast<__end_node_pointer>(__np->__parent_);
+      __begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__np->__parent_);
   }
   --__size_;
-  std::__tree_remove(__end_node()->__left_, static_cast<__node_base_pointer>(__np));
+  std::__tree_remove(__end_node()->__left_, std::__static_fancy_pointer_cast<__node_base_pointer>(__np));
   return __node_holder(__np, _Dp(__node_alloc(), true));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI void swap(__tree<_Tp, _Compare, _Allocator>& __x, __tree<_Tp, _Compare, _Allocator>& __y)
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+swap(__tree<_Tp, _Compare, _Allocator>& __x, __tree<_Tp, _Compare, _Allocator>& __y)
     _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
   __x.swap(__y);
 }
diff --git a/libcxx/include/__type_traits/make_transparent.h b/libcxx/include/__type_traits/make_transparent.h
index 4d3207a807fa7..efcce0011f60d 100644
--- a/libcxx/include/__type_traits/make_transparent.h
+++ b/libcxx/include/__type_traits/make_transparent.h
@@ -33,12 +33,12 @@ template <class _Comparator>
 using __make_transparent_t _LIBCPP_NODEBUG = typename __make_transparent<_Comparator>::type;
 
 template <class _Comparator, __enable_if_t<is_same<_Comparator, __make_transparent_t<_Comparator> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Comparator& __as_transparent(_Comparator& __comp) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Comparator& __as_transparent(_Comparator& __comp) {
   return __comp;
 }
 
 template <class _Comparator, __enable_if_t<!is_same<_Comparator, __make_transparent_t<_Comparator> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI __make_transparent_t<_Comparator> __as_transparent(_Comparator&) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __make_transparent_t<_Comparator> __as_transparent(_Comparator&) {
   static_assert(is_empty<_Comparator>::value);
   return __make_transparent_t<_Comparator>();
 }
diff --git a/libcxx/include/__utility/default_three_way_comparator.h b/libcxx/include/__utility/default_three_way_comparator.h
index 92cdce6aae117..3f6bb1d7455b6 100644
--- a/libcxx/include/__utility/default_three_way_comparator.h
+++ b/libcxx/include/__utility/default_three_way_comparator.h
@@ -31,7 +31,7 @@ template <class _LHS, class _RHS>
 struct __default_three_way_comparator<_LHS,
                                       _RHS,
                                       __enable_if_t<is_arithmetic<_LHS>::value && is_arithmetic<_RHS>::value> > {
-  _LIBCPP_HIDE_FROM_ABI static int operator()(_LHS __lhs, _RHS __rhs) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static int operator()(_LHS __lhs, _RHS __rhs) {
     if (__lhs < __rhs)
       return -1;
     if (__lhs > __rhs)
diff --git a/libcxx/include/__utility/lazy_synth_three_way_comparator.h b/libcxx/include/__utility/lazy_synth_three_way_comparator.h
index 8c78742ccb4e3..3b2c7aa4de83f 100644
--- a/libcxx/include/__utility/lazy_synth_three_way_comparator.h
+++ b/libcxx/include/__utility/lazy_synth_three_way_comparator.h
@@ -34,14 +34,14 @@ struct __lazy_compare_result {
   const _LHS& __lhs_;
   const _RHS& __rhs_;
 
-  _LIBCPP_HIDE_FROM_ABI
-  __lazy_compare_result(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp,
-                        _LIBCPP_CTOR_LIFETIMEBOUND const _LHS& __lhs,
-                        _LIBCPP_CTOR_LIFETIMEBOUND const _RHS& __rhs)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __lazy_compare_result(
+      _LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp,
+      _LIBCPP_CTOR_LIFETIMEBOUND const _LHS& __lhs,
+      _LIBCPP_CTOR_LIFETIMEBOUND const _RHS& __rhs)
       : __comp_(__comp), __lhs_(__lhs), __rhs_(__rhs) {}
 
-  _LIBCPP_HIDE_FROM_ABI bool __less() const { return __comp_(__lhs_, __rhs_); }
-  _LIBCPP_HIDE_FROM_ABI bool __greater() const { return __comp_(__rhs_, __lhs_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __less() const { return __comp_(__lhs_, __rhs_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __greater() const { return __comp_(__rhs_, __lhs_); }
 };
 
 // This class provides three way comparison between _LHS and _RHS as efficiently as possible. This can be specialized if
@@ -51,10 +51,11 @@ template <class _Comparator, class _LHS, class _RHS, class = void>
 struct __lazy_synth_three_way_comparator {
   const _Comparator& __comp_;
 
-  _LIBCPP_HIDE_FROM_ABI __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp)
       : __comp_(__comp) {}
 
-  _LIBCPP_HIDE_FROM_ABI __lazy_compare_result<_Comparator, _LHS, _RHS>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __lazy_compare_result<_Comparator, _LHS, _RHS>
   operator()(_LIBCPP_LIFETIMEBOUND const _LHS& __lhs, _LIBCPP_LIFETIMEBOUND const _RHS& __rhs) const {
     return __lazy_compare_result<_Comparator, _LHS, _RHS>(__comp_, __lhs, __rhs);
   }
@@ -63,10 +64,10 @@ struct __lazy_synth_three_way_comparator {
 struct __eager_compare_result {
   int __res_;
 
-  _LIBCPP_HIDE_FROM_ABI explicit __eager_compare_result(int __res) : __res_(__res) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __eager_compare_result(int __res) : __res_(__res) {}
 
-  _LIBCPP_HIDE_FROM_ABI bool __less() const { return __res_ < 0; }
-  _LIBCPP_HIDE_FROM_ABI bool __greater() const { return __res_ > 0; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __less() const { return __res_ < 0; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __greater() const { return __res_ > 0; }
 };
 
 template <class _Comparator, class _LHS, class _RHS>
@@ -77,10 +78,11 @@ struct __lazy_synth_three_way_comparator<_Comparator,
                                                             __has_default_three_way_comparator<_LHS, _RHS> >::value> > {
   // This lifetimebound annotation is technically incorrect, but other specializations actually capture the lifetime of
   // the comparator.
-  _LIBCPP_HIDE_FROM_ABI __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}
 
   // Same comment as above.
-  _LIBCPP_HIDE_FROM_ABI static __eager_compare_result
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static __eager_compare_result
   operator()(_LIBCPP_LIFETIMEBOUND const _LHS& __lhs, _LIBCPP_LIFETIMEBOUND const _RHS& __rhs) {
     return __eager_compare_result(__default_three_way_comparator<_LHS, _RHS>()(__lhs, __rhs));
   }
@@ -94,10 +96,11 @@ struct __lazy_synth_three_way_comparator<_Comparator,
                                                             __has_default_three_way_comparator<_LHS, _RHS> >::value> > {
   // This lifetimebound annotation is technically incorrect, but other specializations actually capture the lifetime of
   // the comparator.
-  _LIBCPP_HIDE_FROM_ABI __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}
 
   // Same comment as above.
-  _LIBCPP_HIDE_FROM_ABI static __eager_compare_result
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static __eager_compare_result
   operator()(_LIBCPP_LIFETIMEBOUND const _LHS& __lhs, _LIBCPP_LIFETIMEBOUND const _RHS& __rhs) {
     return __eager_compare_result(-__default_three_way_comparator<_LHS, _RHS>()(__lhs, __rhs));
   }
diff --git a/libcxx/include/__utility/try_key_extraction.h b/libcxx/include/__utility/try_key_extraction.h
index 755c08214019f..3423d746dee7f 100644
--- a/libcxx/include/__utility/try_key_extraction.h
+++ b/libcxx/include/__utility/try_key_extraction.h
@@ -28,7 +28,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _KeyT, class _Ret, class _WithKey, class _WithoutKey, class... _Args>
-_LIBCPP_HIDE_FROM_ABI _Ret
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
 __try_key_extraction_impl(__priority_tag<0>, _WithKey, _WithoutKey __without_key, _Args&&... __args) {
   return __without_key(std::forward<_Args>(__args)...);
 }
@@ -39,7 +39,7 @@ template <class _KeyT,
           class _WithoutKey,
           class _Arg,
           __enable_if_t<is_same<_KeyT, __remove_const_ref_t<_Arg> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Ret
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
 __try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _Arg&& __arg) {
   return __with_key(__arg, std::forward<_Arg>(__arg));
 }
@@ -52,7 +52,7 @@ template <class _KeyT,
           __enable_if_t<__is_pair_v<__remove_const_ref_t<_Arg> > &&
                             is_same<__remove_const_t<typename __remove_const_ref_t<_Arg>::first_type>, _KeyT>::value,
                         int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Ret
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
 __try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _Arg&& __arg) {
   return __with_key(__arg.first, std::forward<_Arg>(__arg));
 }
@@ -64,7 +64,7 @@ template <class _KeyT,
           class _Arg1,
           class _Arg2,
           __enable_if_t<is_same<_KeyT, __remove_const_ref_t<_Arg1> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Ret
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
 __try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _Arg1&& __arg1, _Arg2&& __arg2) {
   return __with_key(__arg1, std::forward<_Arg1>(__arg1), std::forward<_Arg2>(__arg2));
 }
@@ -81,7 +81,7 @@ template <class _KeyT,
                             __is_tuple_v<_Tuple1> && tuple_size<_Tuple1>::value == 1 &&
                             is_same<__remove_const_ref_t<typename tuple_element<0, _Tuple1>::type>, _KeyT>::value,
                         int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Ret __try_key_extraction_impl(
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret __try_key_extraction_impl(
     __priority_tag<1>,
     _WithKey __with_key,
     _WithoutKey,
@@ -102,7 +102,7 @@ _LIBCPP_HIDE_FROM_ABI _Ret __try_key_extraction_impl(
 //
 // Both `__with_key` and `__without_key` must take all arguments by reference.
 template <class _KeyT, class _WithKey, class _WithoutKey, class... _Args>
-_LIBCPP_HIDE_FROM_ABI decltype(std::declval<_WithoutKey>()(std::declval<_Args>()...))
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 decltype(std::declval<_WithoutKey>()(std::declval<_Args>()...))
 __try_key_extraction(_WithKey __with_key, _WithoutKey __without_key, _Args&&... __args) {
   using _Ret = decltype(__without_key(std::forward<_Args>(__args)...));
   return std::__try_key_extraction_impl<_KeyT, _Ret>(
diff --git a/libcxx/test/support/is_transparent.h b/libcxx/test/support/is_transparent.h
index 9d3791d68a865..846f8fa4d06dd 100644
--- a/libcxx/test/support/is_transparent.h
+++ b/libcxx/test/support/is_transparent.h
@@ -81,16 +81,17 @@ struct transparent_less_not_a_type
 };
 
 struct C2Int { // comparable to int
-    C2Int() : i_(0) {}
-    C2Int(int i): i_(i) {}
-    int get () const { return i_; }
+  TEST_CONSTEXPR_CXX26 C2Int() : i_(0) {}
+  TEST_CONSTEXPR_CXX26 C2Int(int i) : i_(i) {}
+  TEST_CONSTEXPR_CXX26 int get() const { return i_; }
+
 private:
     int i_;
     };
 
-bool operator <(int          rhs,   const C2Int& lhs) { return rhs       < lhs.get(); }
-bool operator <(const C2Int& rhs,   const C2Int& lhs) { return rhs.get() < lhs.get(); }
-bool operator <(const C2Int& rhs,            int lhs) { return rhs.get() < lhs; }
+    TEST_CONSTEXPR_CXX26 bool operator<(int rhs, const C2Int& lhs) { return rhs < lhs.get(); }
+    TEST_CONSTEXPR_CXX26 bool operator<(const C2Int& rhs, const C2Int& lhs) { return rhs.get() < lhs.get(); }
+    TEST_CONSTEXPR_CXX26 bool operator<(const C2Int& rhs, int lhs) { return rhs.get() < lhs; }
 
 #endif // TEST_STD_VER > 11
 
diff --git a/libcxx/test/support/poisoned_hash_helper.h b/libcxx/test/support/poisoned_hash_helper.h
index cd71cd70d6a84..93b579d2dfde3 100644
--- a/libcxx/test/support/poisoned_hash_helper.h
+++ b/libcxx/test/support/poisoned_hash_helper.h
@@ -123,9 +123,13 @@ struct Class {};
 // Each header that declares the std::hash template provides enabled
 // specializations of std::hash for std::nullptr_t and all cv-unqualified
 // arithmetic, enumeration, and pointer types.
-using LibraryHashTypes =
-    types::concatenate_t<types::arithmetic_types,
-                         types::type_list<Enum, EnumClass, void*, void const*, Class*, std::nullptr_t>>;
+#if TEST_STD_VER >= 17
+using MaybeNullptr = types::type_list<std::nullptr_t>;
+#else
+using MaybeNullptr = types::type_list<>;
+#endif
+using LibraryHashTypes = types::
+    concatenate_t<types::arithmetic_types, types::type_list<Enum, EnumClass, void*, void const*, Class*>, MaybeNullptr>;
 
 struct TestHashEnabled {
   template <class T>
diff --git a/libcxx/test/support/private_constructor.h b/libcxx/test/support/private_constructor.h
index 24f540c6a7fdc..c7ed288c4032e 100644
--- a/libcxx/test/support/private_constructor.h
+++ b/libcxx/test/support/private_constructor.h
@@ -9,18 +9,22 @@
 #ifndef TEST_SUPPORT_PRIVATE_CONSTRUCTOR_H
 #define TEST_SUPPORT_PRIVATE_CONSTRUCTOR_H
 
+#include "test_macros.h"
+
 struct PrivateConstructor {
+  TEST_CONSTEXPR_CXX26 PrivateConstructor static make(int v) { return PrivateConstructor(v); }
+  TEST_CONSTEXPR_CXX26 int get() const { return val; }
 
-    PrivateConstructor static make ( int v ) { return PrivateConstructor(v); }
-    int get () const { return val; }
 private:
-    PrivateConstructor ( int v ) : val(v) {}
-    int val;
+  TEST_CONSTEXPR_CXX26 PrivateConstructor(int v) : val(v) {}
+  int val;
     };
 
-bool operator < ( const PrivateConstructor &lhs, const PrivateConstructor &rhs ) { return lhs.get() < rhs.get(); }
+    TEST_CONSTEXPR_CXX26 bool operator<(const PrivateConstructor& lhs, const PrivateConstructor& rhs) {
+      return lhs.get() < rhs.get();
+    }
 
-bool operator < ( const PrivateConstructor &lhs, int rhs ) { return lhs.get() < rhs; }
-bool operator < ( int lhs, const PrivateConstructor &rhs ) { return lhs < rhs.get(); }
+    TEST_CONSTEXPR_CXX26 bool operator<(const PrivateConstructor& lhs, int rhs) { return lhs.get() < rhs; }
+    TEST_CONSTEXPR_CXX26 bool operator<(int lhs, const PrivateConstructor& rhs) { return lhs < rhs.get(); }
 
 #endif // TEST_SUPPORT_PRIVATE_CONSTRUCTOR_H

>From 49d74e1a7cec61788ad5ca73ebbf040685e38cd9 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 15:22:15 -0500
Subject: [PATCH 02/21] Add test boilerplate for constexpr testing

---
 .../associative/iterator_types.pass.cpp       |  9 ++++-
 .../containers/associative/set/clear.pass.cpp | 11 +++++--
 .../associative/set/contains.pass.cpp         | 11 +++++--
 .../set/contains_transparent.pass.cpp         | 11 +++++--
 .../containers/associative/set/count.pass.cpp | 11 +++++--
 .../set/count_transparent.pass.cpp            | 11 +++++--
 .../associative/set/emplace.pass.cpp          | 11 +++++--
 .../associative/set/emplace_hint.pass.cpp     | 11 +++++--
 .../containers/associative/set/empty.pass.cpp | 11 +++++--
 .../associative/set/equal_range.pass.cpp      | 13 ++++++--
 .../set/equal_range_transparent.pass.cpp      | 17 +++++++---
 .../associative/set/erase_iter.pass.cpp       | 11 +++++--
 .../associative/set/erase_iter_iter.pass.cpp  | 11 +++++--
 .../associative/set/erase_key.pass.cpp        | 11 +++++--
 .../associative/set/extract_iterator.pass.cpp | 11 +++++--
 .../associative/set/extract_key.pass.cpp      | 11 +++++--
 .../containers/associative/set/find.pass.cpp  | 13 ++++++--
 .../associative/set/get_allocator.pass.cpp    | 11 +++++--
 .../associative/set/incomplete_type.pass.cpp  | 11 +++++--
 ...nd_emplace_allocator_requirements.pass.cpp | 15 ++++++---
 .../associative/set/insert_cv.pass.cpp        | 11 +++++--
 .../set/insert_initializer_list.pass.cpp      | 11 +++++--
 .../associative/set/insert_iter_cv.pass.cpp   | 11 +++++--
 .../associative/set/insert_iter_iter.pass.cpp | 11 +++++--
 .../associative/set/insert_iter_rv.pass.cpp   | 11 +++++--
 .../associative/set/insert_node_type.pass.cpp | 11 +++++--
 .../set/insert_node_type_hint.pass.cpp        | 11 +++++--
 .../associative/set/insert_range.pass.cpp     | 11 +++++--
 .../associative/set/insert_rv.pass.cpp        | 11 +++++--
 .../associative/set/iterator.pass.cpp         | 33 +++++++++++--------
 .../associative/set/lower_bound.pass.cpp      | 13 ++++++--
 .../associative/set/max_size.pass.cpp         | 11 +++++--
 .../containers/associative/set/merge.pass.cpp | 17 +++++++---
 .../associative/set/set.cons/alloc.pass.cpp   | 11 +++++--
 .../set.cons/assign_initializer_list.pass.cpp | 11 +++++--
 .../associative/set/set.cons/compare.pass.cpp | 11 +++++--
 .../set/set.cons/compare_alloc.pass.cpp       | 11 +++++--
 .../associative/set/set.cons/copy.pass.cpp    | 11 +++++--
 .../set/set.cons/copy_alloc.pass.cpp          | 11 +++++--
 .../set/set.cons/copy_assign.pass.cpp         | 11 +++++--
 .../associative/set/set.cons/deduct.pass.cpp  | 11 ++++++-
 .../associative/set/set.cons/default.pass.cpp | 11 +++++--
 .../set/set.cons/default_noexcept.pass.cpp    | 11 +++++--
 .../set/set.cons/dtor_noexcept.pass.cpp       | 11 +++++--
 .../set/set.cons/from_range.pass.cpp          | 10 +++++-
 .../set/set.cons/initializer_list.pass.cpp    | 11 +++++--
 .../initializer_list_compare.pass.cpp         | 11 +++++--
 .../initializer_list_compare_alloc.pass.cpp   | 13 ++++++--
 .../set/set.cons/iter_iter.pass.cpp           | 11 +++++--
 .../set/set.cons/iter_iter_alloc.pass.cpp     | 17 +++++++---
 .../set/set.cons/iter_iter_comp.pass.cpp      | 11 +++++--
 .../associative/set/set.cons/move.pass.cpp    | 11 +++++--
 .../set/set.cons/move_alloc.pass.cpp          | 11 +++++--
 .../set/set.cons/move_assign.pass.cpp         | 11 +++++--
 .../set/set.cons/move_noexcept.pass.cpp       | 13 ++++++--
 .../set/set.erasure/erase_if.pass.cpp         | 11 +++++--
 .../set.nonmember/compare.three_way.pass.cpp  | 13 ++++++--
 .../set/set.nonmember/op_compare.pass.cpp     | 33 +++++++++++--------
 .../set/set.observers/comp.pass.cpp           | 13 ++++++--
 .../set/set.special/member_swap.pass.cpp      | 11 +++++--
 .../set/set.special/non_member_swap.pass.cpp  | 11 +++++--
 .../set/set.special/swap_noexcept.pass.cpp    | 11 +++++--
 .../containers/associative/set/size.pass.cpp  | 11 +++++--
 .../containers/associative/set/types.pass.cpp |  9 ++++-
 .../associative/set/upper_bound.pass.cpp      | 13 ++++++--
 65 files changed, 625 insertions(+), 167 deletions(-)

diff --git a/libcxx/test/std/containers/associative/iterator_types.pass.cpp b/libcxx/test/std/containers/associative/iterator_types.pass.cpp
index 2800549905f1b..f394b000615d8 100644
--- a/libcxx/test/std/containers/associative/iterator_types.pass.cpp
+++ b/libcxx/test/std/containers/associative/iterator_types.pass.cpp
@@ -46,7 +46,7 @@ void testSet() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::map<int, int> Map;
     typedef std::pair<const int, int> ValueTp;
@@ -124,5 +124,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/clear.pass.cpp b/libcxx/test/std/containers/associative/set/clear.pass.cpp
index a53b6a86a660e..fbcde898950c0 100644
--- a/libcxx/test/std/containers/associative/set/clear.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/clear.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// void clear() noexcept;
+// constexpr void clear() noexcept; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef int V;
@@ -42,5 +42,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/contains.pass.cpp b/libcxx/test/std/containers/associative/set/contains.pass.cpp
index 93e6ff0a28885..20b1eeb839414 100644
--- a/libcxx/test/std/containers/associative/set/contains.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/contains.pass.cpp
@@ -13,7 +13,7 @@
 
 // <set>
 
-// bool contains(const key_type& x) const;
+// constexpr bool contains(const key_type& x) const; // constexpr since C++26
 
 template <typename T, typename V, typename B, typename... Vals>
 void test(B bad, Vals... args) {
@@ -34,7 +34,7 @@ struct E {
   char c   = 1;
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     test<std::set<int>, int>(14, 10, 11, 12, 13);
     test<std::set<char>, char>('e', 'a', 'b', 'c', 'd');
@@ -44,5 +44,12 @@ int main(int, char**) {
     test<std::multiset<char>, char>('e', 'a', 'b', 'c', 'd');
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
index c5d6b293e8a4b..acd03e649095f 100644
--- a/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// template<typename K> bool contains(const K& x) const; // C++20
+// template<typename K> constexpr bool contains(const K& x) const; // C++20 // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -36,9 +36,16 @@ void test() {
   assert(!s.contains(-1));
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test<std::set<std::pair<int, int>, Comp> >();
   test<std::multiset<std::pair<int, int>, Comp> >();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/count.pass.cpp b/libcxx/test/std/containers/associative/set/count.pass.cpp
index abd1e98f1a4ee..486220f1004c7 100644
--- a/libcxx/test/std/containers/associative/set/count.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/count.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// size_type count(const key_type& k) const;
+// constexpr size_type count(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -136,5 +136,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
index 4e0b610292460..9147b38e5d336 100644
--- a/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class set
 
 //    template<typename K>
-//        size_type count(const K& x) const;        // C++14
+//        constexpr size_type count(const K& x) const;        // C++14 // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -29,11 +29,18 @@ struct Comp {
   bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::set<std::pair<int, int>, Comp> s{{2, 1}, {1, 2}, {1, 3}, {1, 4}, {2, 2}};
 
   auto cnt = s.count(1);
   assert(cnt == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/emplace.pass.cpp b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
index e5a9f9590aad0..fecd9ef2fe7f9 100644
--- a/libcxx/test/std/containers/associative/set/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
@@ -13,7 +13,7 @@
 // class set
 
 // template <class... Args>
-//   pair<iterator, bool> emplace(Args&&... args);
+//   constexpr pair<iterator, bool> emplace(Args&&... args); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -23,7 +23,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<DefaultOnly> M;
     typedef std::pair<M::iterator, bool> R;
@@ -92,5 +92,12 @@ int main(int, char**) {
     assert(set.begin() == std::get<0>(res));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
index 9f0d5fbd2c5e5..5c68000ffbab9 100644
--- a/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
@@ -13,7 +13,7 @@
 // class set
 
 // template <class... Args>
-//   iterator emplace_hint(const_iterator position, Args&&... args);
+//   constexpr iterator emplace_hint(const_iterator position, Args&&... args); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "DefaultOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<DefaultOnly> M;
     typedef M::iterator R;
@@ -78,5 +78,12 @@ int main(int, char**) {
     assert(*r == 2);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/empty.pass.cpp b/libcxx/test/std/containers/associative/set/empty.pass.cpp
index aaefade5cff7b..e6bcf2a58d103 100644
--- a/libcxx/test/std/containers/associative/set/empty.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/empty.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// bool empty() const;
+// constexpr bool empty() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     M m;
@@ -40,5 +40,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/equal_range.pass.cpp b/libcxx/test/std/containers/associative/set/equal_range.pass.cpp
index 239aa3e878426..d59cfe2daee87 100644
--- a/libcxx/test/std/containers/associative/set/equal_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/equal_range.pass.cpp
@@ -10,8 +10,8 @@
 
 // class set
 
-// pair<iterator,iterator>             equal_range(const key_type& k);
-// pair<const_iterator,const_iterator> equal_range(const key_type& k) const;
+// constexpr pair<iterator,iterator>             equal_range(const key_type& k); // constexpr since C++26
+// constexpr pair<const_iterator,const_iterator> equal_range(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -327,5 +327,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
index a44fcf4b74a02..92c1c64be658c 100644
--- a/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
@@ -13,11 +13,11 @@
 // class set
 
 // template<typename K>
-//     pair<iterator,iterator>             equal_range(const K& x);        //
-//     C++14
+//     constexpr pair<iterator,iterator>  equal_range(const K& x);        //
+//     C++14, constexpr since C++26
 // template<typename K>
-//     pair<const_iterator,const_iterator> equal_range(const K& x) const;  //
-//     C++14
+//     constexpr pair<const_iterator,const_iterator> equal_range(const K& x) const;  //
+//     C++14, constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -33,7 +33,7 @@ struct Comp {
   bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::set<std::pair<int, int>, Comp> s{{2, 1}, {1, 2}, {1, 3}, {1, 4}, {2, 2}};
 
   auto er   = s.equal_range(1);
@@ -46,5 +46,12 @@ int main(int, char**) {
 
   assert(nels == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
index 73a4cc4266ec9..7682a81dc7407 100644
--- a/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// iterator erase(const_iterator position);
+// constexpr iterator erase(const_iterator position); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -25,7 +25,7 @@ struct TemplateConstructor {
 
 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef int V;
@@ -179,5 +179,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/erase_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/erase_iter_iter.pass.cpp
index 20c97c28fce56..9c3ba0abc990c 100644
--- a/libcxx/test/std/containers/associative/set/erase_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/erase_iter_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// iterator erase(const_iterator first, const_iterator last);
+// constexpr iterator erase(const_iterator first, const_iterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef int V;
@@ -118,5 +118,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/erase_key.pass.cpp b/libcxx/test/std/containers/associative/set/erase_key.pass.cpp
index 318c35d21cd87..abf82be8a1bcf 100644
--- a/libcxx/test/std/containers/associative/set/erase_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/erase_key.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// size_type erase(const key_type& k);
+// constexpr size_type erase(const key_type& k); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef int V;
@@ -180,5 +180,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
index 2d7b6e521d961..1511b08079d03 100644
--- a/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// node_type extract(const_iterator);
+// constexpr node_type extract(const_iterator); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -35,7 +35,7 @@ void test(Container& c) {
   assert(c.size() == 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     using set_type = std::set<int>;
     set_type m     = {1, 2, 3, 4, 5, 6};
@@ -55,5 +55,12 @@ int main(int, char**) {
     test(m);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/extract_key.pass.cpp b/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
index 0802387f2c9b3..07f6d193179a8 100644
--- a/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// node_type extract(key_type const&);
+// constexpr node_type extract(key_type const&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -41,7 +41,7 @@ void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::set<int> m = {1, 2, 3, 4, 5, 6};
     int keys[]      = {1, 2, 3, 4, 5, 6};
@@ -65,5 +65,12 @@ int main(int, char**) {
     test(m, std::begin(keys), std::end(keys));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/find.pass.cpp b/libcxx/test/std/containers/associative/set/find.pass.cpp
index deb193c17bfa9..e88441ed8927d 100644
--- a/libcxx/test/std/containers/associative/set/find.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/find.pass.cpp
@@ -10,8 +10,8 @@
 
 // class set
 
-//       iterator find(const key_type& k);
-// const_iterator find(const key_type& k) const;
+// constexpr       iterator find(const key_type& k); // constexpr since C++26
+// constexpr const_iterator find(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -202,5 +202,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/get_allocator.pass.cpp b/libcxx/test/std/containers/associative/set/get_allocator.pass.cpp
index 0270c9e82ab0e..a2bca7af5bd41 100644
--- a/libcxx/test/std/containers/associative/set/get_allocator.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/get_allocator.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// allocator_type get_allocator() const
+// constexpr allocator_type get_allocator() const // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::allocator<int> alloc;
     const std::set<int> s(alloc);
@@ -29,5 +29,12 @@ int main(int, char**) {
     const std::set<int, std::less<int>, other_allocator<int> > s(alloc);
     assert(s.get_allocator() == alloc);
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/incomplete_type.pass.cpp b/libcxx/test/std/containers/associative/set/incomplete_type.pass.cpp
index acbda713619bd..e9db3b31f14a4 100644
--- a/libcxx/test/std/containers/associative/set/incomplete_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/incomplete_type.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// <set>
+// <set> // constexpr since C++26
 
 // Check that std::set and its iterators can be instantiated with an incomplete
 // type.
@@ -25,8 +25,15 @@ struct A {
 
 inline bool operator==(A const& L, A const& R) { return &L == &R; }
 inline bool operator<(A const& L, A const& R) { return L.data < R.data; }
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   A a;
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
index 4711473de4dad..ace31eb66ea8d 100644
--- a/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
@@ -10,9 +10,9 @@
 
 // class set
 
-// insert(...)
-// emplace(...)
-// emplace_hint(...)
+// constexpr insert(...) // constexpr since C++26
+// constexpr emplace(...) // constexpr since C++26
+// constexpr emplace_hint(...) // constexpr since C++26
 
 // UNSUPPORTED: c++03
 
@@ -21,10 +21,17 @@
 #include "container_test_types.h"
 #include "../../set_allocator_requirement_test_templates.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   testSetInsert<TCT::set<> >();
   testSetEmplace<TCT::set<> >();
   testSetEmplaceHint<TCT::set<> >();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
index 2683f2924e760..ef278224f0229 100644
--- a/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// pair<iterator, bool> insert(const value_type& v);
+// constexpr pair<iterator, bool> insert(const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -53,7 +53,7 @@ void do_insert_cv_test() {
   assert(*r.first == 3);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   do_insert_cv_test<std::set<int> >();
 #if TEST_STD_VER >= 11
   {
@@ -62,5 +62,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/set/insert_initializer_list.pass.cpp
index f3b69a59987e8..9ebf723e2af44 100644
--- a/libcxx/test/std/containers/associative/set/insert_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// void insert(initializer_list<value_type> il);
+// constexpr void insert(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> C;
     typedef C::value_type V;
@@ -57,5 +57,12 @@ int main(int, char**) {
     assert(*++i == V(10));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_iter_cv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_cv.pass.cpp
index 159487ee0e479..506e06d23bc43 100644
--- a/libcxx/test/std/containers/associative/set/insert_iter_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_iter_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// iterator insert(const_iterator position, const value_type& v);
+// constexpr iterator insert(const_iterator position, const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef M::iterator R;
@@ -70,5 +70,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
index 8576c63a72de9..6d6bd185afa04 100644
--- a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
@@ -11,7 +11,7 @@
 // class set
 
 // template <class InputIterator>
-//   void insert(InputIterator first, InputIterator last);
+//   constexpr void insert(InputIterator first, InputIterator last); // constexpr since C++26
 
 #include <array>
 #include <cassert>
@@ -183,8 +183,15 @@ void test() {
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_iter_rv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_rv.pass.cpp
index 21358f3c40c9c..73031dfae5fd7 100644
--- a/libcxx/test/std/containers/associative/set/insert_iter_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_iter_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// iterator insert(const_iterator position, value_type&& v);
+// constexpr iterator insert(const_iterator position, value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<MoveOnly> M;
     typedef M::iterator R;
@@ -72,5 +72,12 @@ int main(int, char**) {
     assert(*r == 3);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
index 9794b4da989d9..935a90afedb2b 100644
--- a/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// insert_return_type insert(node_type&&);
+// constexpr insert_return_type insert(node_type&&); // constexpr since C++26
 
 #include <memory>
 #include <set>
@@ -94,11 +94,18 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::set<int> m;
   test(m);
   std::set<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
index 1449be0030fe5..f14263595ac6a 100644
--- a/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// iterator insert(const_iterator hint, node_type&&);
+// constexpr iterator insert(const_iterator hint, node_type&&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -47,11 +47,18 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::set<int> m;
   test(m);
   std::set<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
index e6c54d84d6e40..654d4d425774a 100644
--- a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
@@ -13,14 +13,14 @@
 // <set>
 
 // template<container-compatible-range<value_type> R>
-//   void insert_range(R&& rg); // C++23
+//   constexpr void insert_range(R&& rg); // C++23, constexpr since C++26
 
 #include <set>
 
 #include "../../insert_range_maps_sets.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
     test_map_set_insert_range<std::set<int, test_less<int>, Alloc>, int, Iter, Sent>();
   });
@@ -32,5 +32,12 @@ int main(int, char**) {
   test_set_insert_range_exception_safety_throwing_copy<std::set>();
   test_assoc_set_insert_range_exception_safety_throwing_allocator<std::set, int>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_rv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_rv.pass.cpp
index ec18c4379b58d..9b01722faadaa 100644
--- a/libcxx/test/std/containers/associative/set/insert_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// pair<iterator, bool> insert(value_type&& v);
+// constexpr pair<iterator, bool> insert(value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<MoveOnly> M;
     typedef std::pair<M::iterator, bool> R;
@@ -79,5 +79,12 @@ int main(int, char**) {
     assert(*r.first == 3);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/iterator.pass.cpp b/libcxx/test/std/containers/associative/set/iterator.pass.cpp
index 1a18cbc426478..467d88e291a02 100644
--- a/libcxx/test/std/containers/associative/set/iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/iterator.pass.cpp
@@ -10,20 +10,20 @@
 
 // class set
 
-//       iterator begin();
-// const_iterator begin() const;
-//       iterator end();
-// const_iterator end()   const;
+//       constexpr iterator begin(); // constexpr since C++26
+// constexpr const_iterator begin() const; // constexpr since C++26
+//       constexpr iterator end(); // constexpr since C++26
+// constexpr const_iterator end()   const; // constexpr since C++26
 //
-//       reverse_iterator rbegin();
-// const_reverse_iterator rbegin() const;
-//       reverse_iterator rend();
-// const_reverse_iterator rend()   const;
+//       constexpr reverse_iterator rbegin(); // constexpr since C++26
+// constexpr const_reverse_iterator rbegin() const; // constexpr since C++26
+//       constexpr reverse_iterator rend(); // constexpr since C++26
+// constexpr const_reverse_iterator rend()   const; // constexpr since C++26
 //
-// const_iterator         cbegin()  const;
-// const_iterator         cend()    const;
-// const_reverse_iterator crbegin() const;
-// const_reverse_iterator crend()   const;
+// constexpr const_iterator         cbegin()  const; // constexpr since C++26
+// constexpr const_iterator         cend()    const; // constexpr since C++26
+// constexpr const_reverse_iterator crbegin() const; // constexpr since C++26
+// constexpr const_reverse_iterator crend()   const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -32,7 +32,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8};
@@ -129,5 +129,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/lower_bound.pass.cpp b/libcxx/test/std/containers/associative/set/lower_bound.pass.cpp
index 2f114685fb84d..dcaa3b17c8fcc 100644
--- a/libcxx/test/std/containers/associative/set/lower_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/lower_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class set
 
-//       iterator lower_bound(const key_type& k);
-// const_iterator lower_bound(const key_type& k) const;
+//       constexpr iterator lower_bound(const key_type& k); // constexpr since C++26
+// constexpr const_iterator lower_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -283,5 +283,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/max_size.pass.cpp b/libcxx/test/std/containers/associative/set/max_size.pass.cpp
index b00d316c47b35..f11eda16312e5 100644
--- a/libcxx/test/std/containers/associative/set/max_size.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/max_size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// size_type max_size() const;
+// constexpr size_type max_size() const; // constexpr since C++26
 
 #include <cassert>
 #include <limits>
@@ -20,7 +20,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef limited_allocator<int, 10> A;
     typedef std::set<int, std::less<int>, A> C;
@@ -44,5 +44,12 @@ int main(int, char**) {
     assert(c.max_size() <= alloc_max_size(c.get_allocator()));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/merge.pass.cpp b/libcxx/test/std/containers/associative/set/merge.pass.cpp
index 50141d5444b1f..4c76c59ec5de5 100644
--- a/libcxx/test/std/containers/associative/set/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/merge.pass.cpp
@@ -13,13 +13,13 @@
 // class set
 
 // template <class C2>
-//   void merge(set<key_type, C2, allocator_type>& source);
+//   constexpr void merge(set<key_type, C2, allocator_type>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(set<key_type, C2, allocator_type>&& source);
+//   constexpr void merge(set<key_type, C2, allocator_type>&& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<key_type, C2, allocator_type>& source);
+//   constexpr void merge(multiset<key_type, C2, allocator_type>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<key_type, C2, allocator_type>&& source);
+//   constexpr void merge(multiset<key_type, C2, allocator_type>&& source); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -46,7 +46,7 @@ struct throw_comparator {
 };
 #endif
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::set<int> src{1, 3, 5};
     std::set<int> dst{2, 4, 5};
@@ -134,5 +134,12 @@ int main(int, char**) {
       first.merge(std::move(second));
     }
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/alloc.pass.cpp
index 4321666044a3b..539d6e11e6b6f 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set(const allocator_type& a);
+// constexpr set(const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::less<int> C;
   typedef test_allocator<int> A;
   std::set<int, C, A> m(A(5));
@@ -26,5 +26,12 @@ int main(int, char**) {
   assert(m.begin() == m.end());
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
index d674669fd2559..1e0de1434aeb9 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set& operator=(initializer_list<value_type> il);
+// constexpr set& operator=(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -69,9 +69,16 @@ void duplicate_keys_test() {
   LIBCPP_ASSERT(alloc_stats.alloc_count == 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   basic_test();
   duplicate_keys_test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/compare.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/compare.pass.cpp
index 03452142f5b85..5b3abfe1fcb3b 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/compare.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// explicit set(const key_compare& comp) const;
+// constexpr explicit set(const key_compare& comp) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   const std::set<int, C> m(C(3));
   assert(m.empty());
@@ -26,5 +26,12 @@ int main(int, char**) {
   assert(m.key_comp() == C(3));
   assert(m.value_comp() == C(3));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/compare_alloc.pass.cpp
index 2ae0547054537..ba358f1c95412 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/compare_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set(const value_compare& comp, const allocator_type& a);
+// constexpr set(const value_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   typedef test_allocator<int> A;
   std::set<int, C, A> m(C(4), A(5));
@@ -28,5 +28,12 @@ int main(int, char**) {
   assert(m.key_comp() == C(4));
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
index 8fac335175c80..f2116c019d3b6 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set(const set& m);
+// constexpr set(const set& m); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -127,8 +127,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
index 2784af65b0a0f..200bf71b5f3dd 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set(const set& m, const allocator_type& a);
+// constexpr set(const set& m, const allocator_type& a); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -99,8 +99,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
index dd1dbc86190f8..7c301d62cb586 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set& operator=(const set& s);
+// constexpr set& operator=(const set& s); // constexpr since C++26
 
 #include <algorithm>
 #include <cassert>
@@ -277,8 +277,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
index e26df4503ed41..56540fbf35c6a 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
@@ -37,6 +37,8 @@
 //   set(from_range_t, R&&, Allocator)
 //     -> set<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>; // C++23
 
+// constexpr since C++26
+
 #include <algorithm> // std::equal
 #include <array>
 #include <cassert>
@@ -52,7 +54,7 @@ struct NotAnAllocator {
   friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     const int arr[] = {1, 2, 1, INT_MAX, 3};
     std::set s(std::begin(arr), std::end(arr));
@@ -212,5 +214,12 @@ int main(int, char**) {
 
   AssociativeContainerDeductionGuidesSfinaeAway<std::set, std::set<int>>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/default.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/default.pass.cpp
index c41d7c3b9d3bd..6ae5d003fef15 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/default.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/default.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set();
+// constexpr set(); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::set<int> m;
     assert(m.empty());
@@ -51,5 +51,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/default_noexcept.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/default_noexcept.pass.cpp
index c01ac3b2fad0b..4b2ddd174519d 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/default_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/default_noexcept.pass.cpp
@@ -8,7 +8,7 @@
 
 // <set>
 
-// set()
+// constexpr set() // constexpr since C++26
 //    noexcept(
 //        is_nothrow_default_constructible<allocator_type>::value &&
 //        is_nothrow_default_constructible<key_compare>::value &&
@@ -32,7 +32,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::set<MoveOnly> C;
@@ -52,5 +52,12 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_default_constructible<C>::value, "");
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/dtor_noexcept.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/dtor_noexcept.pass.cpp
index 63c0433477414..15f3c257af272 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/dtor_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/dtor_noexcept.pass.cpp
@@ -8,7 +8,7 @@
 
 // <set>
 
-// ~set() // implied noexcept;
+// constexpr ~set() // implied noexcept; // constexpr since C++26
 
 // UNSUPPORTED: c++03
 
@@ -26,7 +26,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<MoveOnly> C;
     static_assert(std::is_nothrow_destructible<C>::value, "");
@@ -46,5 +46,12 @@ int main(int, char**) {
   }
 #endif // _LIBCPP_VERSION
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
index 3590b75065ad9..23a5136aafcf2 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
@@ -14,6 +14,7 @@
 // template<container-compatible-range<value_type> R>
 //   set(from_range_t, R&& rg, const Allocator& a))
 //     : set(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+// constexpr since C++26
 
 #include <array>
 #include <set>
@@ -30,7 +31,7 @@ void test_duplicates() {
   assert(std::ranges::is_permutation(expected, c));
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
     test_associative_set<std::set, int, Iter, Sent, test_less<int>, Alloc>();
   });
@@ -42,5 +43,12 @@ int main(int, char**) {
   test_set_exception_safety_throwing_copy<std::set>();
   test_set_exception_safety_throwing_allocator<std::set, int>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/initializer_list.pass.cpp
index 48495712425be..974d2d96b02d7 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// constexpr set(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> C;
     typedef C::value_type V;
@@ -50,5 +50,12 @@ int main(int, char**) {
     assert(*++i == V(6));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare.pass.cpp
index 25684966078d6..3572465b9118e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// constexpr set(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> Cmp;
   typedef std::set<int, Cmp> C;
   typedef C::value_type V;
@@ -37,5 +37,12 @@ int main(int, char**) {
   assert(*++i == V(6));
   assert(m.key_comp() == Cmp(10));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare_alloc.pass.cpp
index 48649c3b281f4..737e96bfad9f5 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare_alloc.pass.cpp
@@ -12,8 +12,8 @@
 
 // class set
 
-// set(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a);
-// set(initializer_list<value_type> il, const allocator_type& a);
+// constexpr set(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a); // constexpr since C++26
+// constexpr set(initializer_list<value_type> il, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef test_less<int> Cmp;
     typedef test_allocator<int> A;
@@ -60,5 +60,12 @@ int main(int, char**) {
     assert(m.get_allocator() == A(4));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/iter_iter.pass.cpp
index 27a4e6a5f527d..4e49265811b19 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/iter_iter.pass.cpp
@@ -11,7 +11,7 @@
 // class set
 
 // template <class InputIterator>
-//     set(InputIterator first, InputIterator last);
+//     constexpr set(InputIterator first, InputIterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_iterators.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -46,5 +46,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
index dac553073f177..5a4c9d156a3b6 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
@@ -11,12 +11,12 @@
 // class set
 
 // template <class InputIterator>
-//     set(InputIterator first, InputIterator last,
-//         const value_compare& comp, const allocator_type& a);
+//     constexpr set(InputIterator first, InputIterator last,
+//         const value_compare& comp, const allocator_type& a); // constexpr since C++26
 //
 // template <class InputIterator>
-//     set(InputIterator first, InputIterator last,
-//         const allocator_type& a);
+//     constexpr set(InputIterator first, InputIterator last,
+//         const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -26,7 +26,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -63,5 +63,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_comp.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_comp.pass.cpp
index 573bbd316e073..204c86a62590e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_comp.pass.cpp
@@ -11,7 +11,7 @@
 // class set
 
 // template <class InputIterator>
-//     set(InputIterator first, InputIterator last, const value_compare& comp);
+//     constexpr set(InputIterator first, InputIterator last, const value_compare& comp); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_iterators.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
   typedef test_less<V> C;
@@ -33,5 +33,12 @@ int main(int, char**) {
   assert(*std::next(m.begin()) == 2);
   assert(*std::next(m.begin(), 2) == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move.pass.cpp
index 2c1ec22ab257d..a4e68ff583d01 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set(set&& s);
+// constexpr set(set&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef test_less<int> C;
@@ -82,5 +82,12 @@ int main(int, char**) {
     assert(std::distance(mo.begin(), mo.end()) == 0);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
index c85463bd391ca..4f15202f2ca83 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set(set&& s, const allocator_type& a);
+// constexpr set(set&& s, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -24,7 +24,7 @@
 #include "test_allocator.h"
 #include "Counter.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -111,5 +111,12 @@ int main(int, char**) {
     assert(Counter_base::gConstructed == 0);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move_assign.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move_assign.pass.cpp
index bacd58c172a12..418c4d06ca114 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move_assign.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set& operator=(set&& s);
+// constexpr set& operator=(set&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -93,5 +93,12 @@ int main(int, char**) {
     assert(m1.empty());
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move_noexcept.pass.cpp
index 9325b8d1f40de..fa315e392ce0c 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move_noexcept.pass.cpp
@@ -8,9 +8,9 @@
 
 // <set>
 
-// set(set&&)
+// constexpr set(set&&)
 //        noexcept(is_nothrow_move_constructible<allocator_type>::value &&
-//                 is_nothrow_move_constructible<key_compare>::value);
+//                 is_nothrow_move_constructible<key_compare>::value); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -30,7 +30,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::set<MoveOnly> C;
@@ -50,5 +50,12 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_move_constructible<C>::value, "");
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
index b076b31059610..978e80821eb49 100644
--- a/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
@@ -11,7 +11,7 @@
 
 // template <class T, class Compare, class Allocator, class Predicate>
 //   typename set<T, Compare, Allocator>::size_type
-//   erase_if(set<T, Compare, Allocator>& c, Predicate pred);
+//   constexpr erase_if(set<T, Compare, Allocator>& c, Predicate pred); // constexpr since C++26
 
 #include <set>
 
@@ -53,7 +53,7 @@ void test() {
   test0(S({1, 2, 3}), False, S({1, 2, 3}), 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test<std::set<int>>();
   test<std::set<int, std::less<int>, min_allocator<int>>>();
   test<std::set<int, std::less<int>, test_allocator<int>>>();
@@ -61,5 +61,12 @@ int main(int, char**) {
   test<std::set<long>>();
   test<std::set<double>>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
index dc513d96e5ba8..563a8c12db828 100644
--- a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
@@ -12,16 +12,23 @@
 // class set
 
 // template<class Key, class Compare, class Allocator>
-//   synth-three-way-result<Key> operator<=>(const set<Key, Compare, Allocator>& x,
-//                                           const set<Key, Compare, Allocator>& y);
+//   constexpr synth-three-way-result<Key> operator<=>(const set<Key, Compare, Allocator>& x,
+//                                           const set<Key, Compare, Allocator>& y); // constexpr since C++26
 
 #include <cassert>
 #include <set>
 
 #include "test_container_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   assert((test_ordered_set_container_spaceship<std::set>()));
   // `std::set` is not constexpr, so no `static_assert` test here.
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.nonmember/op_compare.pass.cpp b/libcxx/test/std/containers/associative/set/set.nonmember/op_compare.pass.cpp
index 1be07ce885298..547cc59c51880 100644
--- a/libcxx/test/std/containers/associative/set/set.nonmember/op_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.nonmember/op_compare.pass.cpp
@@ -9,28 +9,28 @@
 // <set>
 
 // template<class Key, class Compare, class Alloc>
-// bool operator==(const std::set<Key, Compare, Alloc>& lhs,
-//                 const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator==(const std::set<Key, Compare, Alloc>& lhs,
+//                 const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator!=(const std::set<Key, Compare, Alloc>& lhs,
-//                 const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator!=(const std::set<Key, Compare, Alloc>& lhs,
+//                 const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator<(const std::set<Key, Compare, Alloc>& lhs,
-//                const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator<(const std::set<Key, Compare, Alloc>& lhs,
+//                const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator>(const std::set<Key, Compare, Alloc>& lhs,
-//                const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator>(const std::set<Key, Compare, Alloc>& lhs,
+//                const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator<=(const std::set<Key, Compare, Alloc>& lhs,
-//                 const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator<=(const std::set<Key, Compare, Alloc>& lhs,
+//                 const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator>=(const std::set<Key, Compare, Alloc>& lhs,
-//                 const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator>=(const std::set<Key, Compare, Alloc>& lhs,
+//                 const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -38,7 +38,7 @@
 
 #include "test_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::set<int> s1, s2;
     s1.insert(1);
@@ -61,5 +61,12 @@ int main(int, char**) {
     const std::set<int>&cs1 = s1, cs2 = s2;
     assert(testComparisons(cs1, cs2, false, true));
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
index 019dae3955acb..9aef8c7cce41b 100644
--- a/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
@@ -8,13 +8,13 @@
 
 // <set>
 
-// key_compare key_comp() const;
-// value_compare value_comp() const;
+// constexpr key_compare key_comp() const; // constexpr since C++26
+// constexpr value_compare value_comp() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::set<int> set_type;
 
   set_type s;
@@ -29,5 +29,12 @@ int main(int, char**) {
   assert(cs.value_comp()(*p1.first, *p2.first));
   assert(!cs.value_comp()(*p2.first, *p1.first));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.special/member_swap.pass.cpp b/libcxx/test/std/containers/associative/set/set.special/member_swap.pass.cpp
index db3dc9831b3dd..5a5a5156ce652 100644
--- a/libcxx/test/std/containers/associative/set/set.special/member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.special/member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// void swap(set& m);
+// constexpr void swap(set& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -110,5 +110,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.special/non_member_swap.pass.cpp b/libcxx/test/std/containers/associative/set/set.special/non_member_swap.pass.cpp
index e663a67035ff5..24fb007ef266e 100644
--- a/libcxx/test/std/containers/associative/set/set.special/non_member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.special/non_member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// void swap(set& m);
+// constexpr void swap(set& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   {
     typedef std::set<int> M;
@@ -100,5 +100,12 @@ int main(int, char**) {
     assert(m2.get_allocator() == A(1));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/associative/set/set.special/swap_noexcept.pass.cpp
index 36ab989b76043..4c4bf48adf241 100644
--- a/libcxx/test/std/containers/associative/set/set.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.special/swap_noexcept.pass.cpp
@@ -10,7 +10,7 @@
 
 // <set>
 
-// void swap(set& c)
+// constexpr void swap(set& c) // constexpr since C++26
 //     noexcept(!allocator_type::propagate_on_container_swap::value ||
 //              __is_nothrow_swappable<allocator_type>::value);
 //
@@ -86,7 +86,7 @@ struct some_alloc3 {
   typedef std::false_type is_always_equal;
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<MoveOnly> C;
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
@@ -131,5 +131,12 @@ int main(int, char**) {
 #  endif // _LIBCPP_VERSION
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/size.pass.cpp b/libcxx/test/std/containers/associative/set/size.pass.cpp
index 1672e1f055c6c..58084634e8dde 100644
--- a/libcxx/test/std/containers/associative/set/size.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// size_type size() const;
+// constexpr size_type size() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     M m;
@@ -56,5 +56,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/types.pass.cpp b/libcxx/test/std/containers/associative/set/types.pass.cpp
index a7c30244a540b..af3915dccf1fa 100644
--- a/libcxx/test/std/containers/associative/set/types.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/types.pass.cpp
@@ -34,7 +34,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> C;
     static_assert((std::is_same<C::key_type, int>::value), "");
@@ -67,5 +67,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/upper_bound.pass.cpp b/libcxx/test/std/containers/associative/set/upper_bound.pass.cpp
index 3543879e07940..f5825c30519e6 100644
--- a/libcxx/test/std/containers/associative/set/upper_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/upper_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class set
 
-//       iterator upper_bound(const key_type& k);
-// const_iterator upper_bound(const key_type& k) const;
+//       constexpr iterator upper_bound(const key_type& k); // constexpr since C++26
+// constexpr const_iterator upper_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -283,5 +283,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

>From 594dff9a2ed3b45b1140c5df09a4e149d8afac7e Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 15:56:56 -0500
Subject: [PATCH 03/21] Get ready for set.cons

---
 libcxx/include/__tree |   2 +-
 libcxx/include/set    | 234 ++++++++++++++++++++++++++----------------
 2 files changed, 144 insertions(+), 92 deletions(-)

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 5398ff911ec57..5f4c317d62291 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1467,7 +1467,7 @@ private:
   }
 
   template <class _To, class _From, class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI static void __assign_value(_To& __lhs, _From&& __rhs) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static void __assign_value(_To& __lhs, _From&& __rhs) {
     __lhs = std::forward<_From>(__rhs);
   }
 
diff --git a/libcxx/include/set b/libcxx/include/set
index d58b6e96b061d..d66a54e964a01 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -10,6 +10,8 @@
 #ifndef _LIBCPP_SET
 #define _LIBCPP_SET
 
+// TODO: add constexpr everywhere
+
 /*
 
     set synopsis
@@ -614,24 +616,27 @@ public:
   template <class _Key2, class _Compare2, class _Alloc2>
   friend class multiset;
 
-  _LIBCPP_HIDE_FROM_ABI set() _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set() _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_default_constructible<key_compare>::value&&
           is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(value_compare()) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit set(const value_compare& __comp) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit set(const value_compare& __comp) _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(__comp) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit set(const value_compare& __comp, const allocator_type& __a) : __tree_(__comp, __a) {}
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit set(const value_compare& __comp, const allocator_type& __a)
+      : __tree_(__comp, __a) {}
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI set(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  set(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__f, __l);
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   set(_InputIterator __f, _InputIterator __l, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__f, __l);
@@ -639,7 +644,7 @@ public:
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   set(from_range_t,
       _Range&& __range,
       const key_compare& __comp = key_compare(),
@@ -651,205 +656,247 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI set(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 set(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : set(__f, __l, key_compare(), __a) {}
 #  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI set(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(from_range_t, _Range&& __range, const allocator_type& __a)
       : set(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI set(const set& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(const set& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI set& operator=(const set& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set& operator=(const set& __s) = default;
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI set(set&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(set&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI explicit set(const allocator_type& __a) : __tree_(__a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit set(const allocator_type& __a) : __tree_(__a) {}
 
-  _LIBCPP_HIDE_FROM_ABI set(const set& __s, const allocator_type& __a) : __tree_(__s.__tree_.value_comp(), __a) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(const set& __s, const allocator_type& __a)
+      : __tree_(__s.__tree_.value_comp(), __a) {
     insert(__s.begin(), __s.end());
   }
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI set(set&& __s, const allocator_type& __a);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(set&& __s, const allocator_type& __a);
 
-  _LIBCPP_HIDE_FROM_ABI set(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  set(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__il.begin(), __il.end());
   }
 
-  _LIBCPP_HIDE_FROM_ABI set(initializer_list<value_type> __il, const value_compare& __comp, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  set(initializer_list<value_type> __il, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__il.begin(), __il.end());
   }
 
 #    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI set(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(initializer_list<value_type> __il, const allocator_type& __a)
       : set(__il, key_compare(), __a) {}
 #    endif
 
-  _LIBCPP_HIDE_FROM_ABI set& operator=(initializer_list<value_type> __il) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set& operator=(initializer_list<value_type> __il) {
     __tree_.__assign_unique(__il.begin(), __il.end());
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI set& operator=(set&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set& operator=(set&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI ~set() { static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), ""); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~set() {
+    static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
+  }
 
-  _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return __tree_.begin(); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return __tree_.begin(); }
-  _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return __tree_.end(); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return __tree_.end(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT { return __tree_.begin(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator begin() const _NOEXCEPT { return __tree_.begin(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator end() _NOEXCEPT { return __tree_.end(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator end() const _NOEXCEPT { return __tree_.end(); }
 
-  _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT { return reverse_iterator(end()); }
-  _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rbegin() const _NOEXCEPT { return const_reverse_iterator(end()); }
-  _LIBCPP_HIDE_FROM_ABI reverse_iterator rend() _NOEXCEPT { return reverse_iterator(begin()); }
-  _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const _NOEXCEPT { return const_reverse_iterator(begin()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rbegin() _NOEXCEPT {
+    return reverse_iterator(end());
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator rbegin() const _NOEXCEPT {
+    return const_reverse_iterator(end());
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rend() _NOEXCEPT {
+    return reverse_iterator(begin());
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator rend() const _NOEXCEPT {
+    return const_reverse_iterator(begin());
+  }
 
-  _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const _NOEXCEPT { return begin(); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator cend() const _NOEXCEPT { return end(); }
-  _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crbegin() const _NOEXCEPT { return rbegin(); }
-  _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const _NOEXCEPT { return rend(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cbegin() const _NOEXCEPT { return begin(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cend() const _NOEXCEPT { return end(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator crbegin() const _NOEXCEPT {
+    return rbegin();
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator crend() const _NOEXCEPT { return rend(); }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT { return __tree_.size() == 0; }
-  _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __tree_.size(); }
-  _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT { return __tree_.max_size(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool empty() const _NOEXCEPT {
+    return __tree_.size() == 0;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type size() const _NOEXCEPT { return __tree_.size(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type max_size() const _NOEXCEPT {
+    return __tree_.max_size();
+  }
 
   // modifiers:
 #  ifndef _LIBCPP_CXX03_LANG
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> emplace(_Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> emplace(_Args&&... __args) {
     return __tree_.__emplace_unique(std::forward<_Args>(__args)...);
   }
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace_hint(const_iterator __p, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace_hint(const_iterator __p, _Args&&... __args) {
     return __tree_.__emplace_hint_unique(__p, std::forward<_Args>(__args)...).first;
   }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> insert(const value_type& __v) { return __tree_.__emplace_unique(__v); }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, const value_type& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> insert(const value_type& __v) {
+    return __tree_.__emplace_unique(__v);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, const value_type& __v) {
     return __tree_.__emplace_hint_unique(__p, __v).first;
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __first, _InputIterator __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(_InputIterator __first, _InputIterator __last) {
     __tree_.__insert_range_unique(__first, __last);
   }
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert_range(_Range&& __range) {
     __tree_.__insert_range_unique(ranges::begin(__range), ranges::end(__range));
   }
 #  endif
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> insert(value_type&& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> insert(value_type&& __v) {
     return __tree_.__emplace_unique(std::move(__v));
   }
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, value_type&& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, value_type&& __v) {
     return __tree_.__emplace_hint_unique(__p, std::move(__v)).first;
   }
 
-  _LIBCPP_HIDE_FROM_ABI void insert(initializer_list<value_type> __il) { insert(__il.begin(), __il.end()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(initializer_list<value_type> __il) {
+    insert(__il.begin(), __il.end());
+  }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
-  _LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_unique(__k); }
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
-  _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p) { return __tree_.erase(__p); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(const key_type& __k) {
+    return __tree_.__erase_unique(__k);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
+    return __tree_.erase(__f, __l);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
-  _LIBCPP_HIDE_FROM_ABI insert_return_type insert(node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 insert_return_type insert(node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to set::insert()");
     return __tree_.template __node_handle_insert_unique< node_type, insert_return_type>(std::move(__nh));
   }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __hint, node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __hint, node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to set::insert()");
     return __tree_.template __node_handle_insert_unique<node_type>(__hint, std::move(__nh));
   }
-  _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract<node_type>(__it);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_unique(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_unique(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_unique(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_unique(__source.__tree_);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI void swap(set& __s) _NOEXCEPT_(__is_nothrow_swappable_v<__base>) { __tree_.swap(__s.__tree_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(set& __s) _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
+    __tree_.swap(__s.__tree_);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const _NOEXCEPT { return __tree_.__alloc(); }
-  _LIBCPP_HIDE_FROM_ABI key_compare key_comp() const { return __tree_.value_comp(); }
-  _LIBCPP_HIDE_FROM_ABI value_compare value_comp() const { return __tree_.value_comp(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type get_allocator() const _NOEXCEPT {
+    return __tree_.__alloc();
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_compare key_comp() const { return __tree_.value_comp(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_compare value_comp() const { return __tree_.value_comp(); }
 
   // set operations:
-  _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __tree_.find(__k); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __tree_.find(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const key_type& __k) { return __tree_.find(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const key_type& __k) const {
+    return __tree_.find(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const _K2& __k) {
     return __tree_.find(__k);
   }
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const _K2& __k) const {
     return __tree_.find(__k);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI size_type count(const key_type& __k) const { return __tree_.__count_unique(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const key_type& __k) const {
+    return __tree_.__count_unique(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const _K2& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  endif
 
 #  if _LIBCPP_STD_VER >= 20
-  _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const key_type& __k) const {
+    return find(__k) != end();
+  }
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
 #  endif // _LIBCPP_STD_VER >= 20
 
-  _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const key_type& __k) { return __tree_.__lower_bound_unique(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const key_type& __k) {
+    return __tree_.__lower_bound_unique(__k);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const key_type& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator lower_bound(const key_type& __k) const {
     return __tree_.__lower_bound_unique(__k);
   }
 
@@ -857,46 +904,50 @@ public:
   // match multiple elements.
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const _K2& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const _K2& __k) {
     return __tree_.__lower_bound_multi(__k);
   }
 
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator lower_bound(const _K2& __k) const {
     return __tree_.__lower_bound_multi(__k);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const key_type& __k) { return __tree_.__upper_bound_unique(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const key_type& __k) {
+    return __tree_.__upper_bound_unique(__k);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const key_type& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator upper_bound(const key_type& __k) const {
     return __tree_.__upper_bound_unique(__k);
   }
 
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const _K2& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const _K2& __k) {
     return __tree_.__upper_bound_multi(__k);
   }
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator upper_bound(const _K2& __k) const {
     return __tree_.__upper_bound_multi(__k);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const key_type& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const key_type& __k) {
     return __tree_.__equal_range_unique(__k);
   }
-  _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const key_type& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const key_type& __k) const {
     return __tree_.__equal_range_unique(__k);
   }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const _K2& __k) {
     return __tree_.__equal_range_multi(__k);
   }
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const _K2& __k) const {
     return __tree_.__equal_range_multi(__k);
   }
 #  endif
@@ -949,7 +1000,8 @@ set(initializer_list<_Key>, _Allocator) -> set<_Key, less<_Key>, _Allocator>;
 #  ifndef _LIBCPP_CXX03_LANG
 
 template <class _Key, class _Compare, class _Allocator>
-set<_Key, _Compare, _Allocator>::set(set&& __s, const allocator_type& __a) : __tree_(std::move(__s.__tree_), __a) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 set<_Key, _Compare, _Allocator>::set(set&& __s, const allocator_type& __a)
+    : __tree_(std::move(__s.__tree_), __a) {
   if (__a != __s.get_allocator()) {
     const_iterator __e = cend();
     while (!__s.empty())
@@ -960,7 +1012,7 @@ set<_Key, _Compare, _Allocator>::set(set&& __s, const allocator_type& __a) : __t
 #  endif // _LIBCPP_CXX03_LANG
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator==(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return __x.size() == __y.size() && std::equal(__x.begin(), __x.end(), __y.begin());
 }
@@ -968,31 +1020,31 @@ operator==(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare,
 #  if _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare(__x.begin(), __x.end(), __y.begin(), __y.end());
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator!=(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return !(__x == __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return __y < __x;
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>=(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return !(__x < __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<=(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return !(__y < __x);
 }
@@ -1000,7 +1052,7 @@ operator<=(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare,
 #  else // _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI __synth_three_way_result<_Key>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __synth_three_way_result<_Key>
 operator<=>(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare_three_way(__x.begin(), __x.end(), __y.begin(), __y.end(), std::__synth_three_way);
 }
@@ -1009,14 +1061,14 @@ operator<=>(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare
 
 // specialized algorithms:
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI void swap(set<_Key, _Compare, _Allocator>& __x, set<_Key, _Compare, _Allocator>& __y)
-    _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+swap(set<_Key, _Compare, _Allocator>& __x, set<_Key, _Compare, _Allocator>& __y) _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
   __x.swap(__y);
 }
 
 #  if _LIBCPP_STD_VER >= 20
 template <class _Key, class _Compare, class _Allocator, class _Predicate>
-inline _LIBCPP_HIDE_FROM_ABI typename set<_Key, _Compare, _Allocator>::size_type
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename set<_Key, _Compare, _Allocator>::size_type
 erase_if(set<_Key, _Compare, _Allocator>& __c, _Predicate __pred) {
   return std::__libcpp_erase_if_container(__c, __pred);
 }

>From f29268adce636381be9409c5c01c92281c537865 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 15:58:10 -0500
Subject: [PATCH 04/21] Test set.cons

---
 .../from_range_associative_containers.h       | 14 ++++-----
 .../set.cons/assign_initializer_list.pass.cpp |  4 +--
 .../associative/set/set.cons/copy.pass.cpp    |  8 ++---
 .../set/set.cons/copy_alloc.pass.cpp          |  8 ++---
 .../set/set.cons/copy_assign.pass.cpp         | 31 +++++++++----------
 .../associative/set/set.cons/deduct.pass.cpp  |  2 +-
 .../set/set.cons/from_range.pass.cpp          |  3 +-
 .../set/set.cons/move_alloc.pass.cpp          |  2 +-
 .../test/std/containers/from_range_helpers.h  |  4 +--
 9 files changed, 33 insertions(+), 43 deletions(-)

diff --git a/libcxx/test/std/containers/associative/from_range_associative_containers.h b/libcxx/test/std/containers/associative/from_range_associative_containers.h
index cb3646d738968..2a391b50fc06a 100644
--- a/libcxx/test/std/containers/associative/from_range_associative_containers.h
+++ b/libcxx/test/std/containers/associative/from_range_associative_containers.h
@@ -189,7 +189,7 @@ constexpr bool test_set_constraints() {
 }
 
 template <template <class...> class Container, class T, class Iter, class Sent, class Comp, class Alloc>
-void test_associative_set_with_input(std::vector<T>&& input) {
+TEST_CONSTEXPR_CXX26 void test_associative_set_with_input(std::vector<T>&& input) {
   { // (range)
     std::ranges::subrange in(Iter(input.data()), Sent(Iter(input.data() + input.size())));
     Container<T> c(std::from_range, in);
@@ -232,7 +232,7 @@ void test_associative_set_with_input(std::vector<T>&& input) {
 }
 
 template <template <class...> class Container, class T, class Iter, class Sent, class Comp, class Alloc>
-void test_associative_set() {
+TEST_CONSTEXPR_CXX26 void test_associative_set() {
   auto test_with_input = &test_associative_set_with_input<Container, T, Iter, Sent, Comp, Alloc>;
 
   // Normal input.
@@ -244,7 +244,7 @@ void test_associative_set() {
 }
 
 template <template <class...> class Container>
-void test_associative_set_move_only() {
+TEST_CONSTEXPR_CXX26 void test_associative_set_move_only() {
   MoveOnly input[5];
   std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
 
@@ -252,8 +252,8 @@ void test_associative_set_move_only() {
 }
 
 template <template <class...> class Container>
-void test_set_exception_safety_throwing_copy() {
-#if !defined(TEST_HAS_NO_EXCEPTIONS)
+TEST_CONSTEXPR_CXX26 void test_set_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_IS_CONSTANT_EVALUATED)
   using T = ThrowingCopy<3>;
   T::reset();
   T in[5] = {{1}, {2}, {3}, {4}, {5}};
@@ -270,8 +270,8 @@ void test_set_exception_safety_throwing_copy() {
 }
 
 template <template <class...> class Container, class T>
-void test_set_exception_safety_throwing_allocator() {
-#if !defined(TEST_HAS_NO_EXCEPTIONS)
+TEST_CONSTEXPR_CXX26 void test_set_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_IS_CONSTANT_EVALUATED)
   T in[] = {1, 2};
 
   try {
diff --git a/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
index 1e0de1434aeb9..c37e2f400db3e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
@@ -21,7 +21,7 @@
 #include "min_allocator.h"
 #include "test_allocator.h"
 
-void basic_test() {
+TEST_CONSTEXPR_CXX26 void basic_test() {
   {
     typedef std::set<int> C;
     typedef C::value_type V;
@@ -54,7 +54,7 @@ void basic_test() {
   }
 }
 
-void duplicate_keys_test() {
+TEST_CONSTEXPR_CXX26 void duplicate_keys_test() {
   test_allocator_statistics alloc_stats;
   typedef std::set<int, std::less<int>, test_allocator<int> > Set;
   {
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
index f2116c019d3b6..ea4a34940bacd 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
@@ -20,7 +20,7 @@
 #include "test_allocator.h"
 
 template <template <class> class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   { // Simple check
     using Set = std::set<int, std::less<int>, Alloc<int> >;
 
@@ -79,7 +79,7 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator>();
   test_alloc<min_allocator>(); // Make sure that fancy pointers work
 
@@ -125,10 +125,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.get_allocator() == other_allocator<int>(10));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
index 200bf71b5f3dd..bb1d251777904 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
@@ -21,7 +21,7 @@
 #include "test_allocator.h"
 
 template <class Alloc>
-void test_alloc(const Alloc& new_alloc) {
+TEST_CONSTEXPR_CXX26 void test_alloc(const Alloc& new_alloc) {
   { // Simple check
     using Set = std::set<int, std::less<int>, Alloc>;
 
@@ -81,7 +81,7 @@ void test_alloc(const Alloc& new_alloc) {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc(std::allocator<int>());
   test_alloc(test_allocator<int>(25)); // Make sure that the new allocator is actually used
   test_alloc(min_allocator<int>());    // Make sure that fancy pointers work
@@ -97,10 +97,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
index 7c301d62cb586..2e47297ccf28e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
@@ -35,41 +35,42 @@ class tracking_allocator {
   using value_type                             = T;
   using propagate_on_container_copy_assignment = std::true_type;
 
-  tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
 
   template <class U>
-  tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
 
-  T* allocate(std::size_t n) {
+  TEST_CONSTEXPR_CXX26 T* allocate(std::size_t n) {
     T* allocation = std::allocator<T>().allocate(n);
     allocs_->push_back(allocation);
     return allocation;
   }
 
-  void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
+  TEST_CONSTEXPR_CXX26 void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
     auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
     assert(res != allocs_->end() && "Trying to deallocate memory from different allocator?");
     allocs_->erase(res);
     std::allocator<T>().deallocate(ptr, n);
   }
 
-  friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  TEST_CONSTEXPR_CXX26 friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ == rhs.allocs_;
   }
 
-  friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  TEST_CONSTEXPR_CXX26 friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ != rhs.allocs_;
   }
 };
 
 struct NoOp {
-  void operator()() {}
+  TEST_CONSTEXPR_CXX26 void operator()() {}
 };
 
 template <class Alloc, class AllocatorInvariant = NoOp>
-void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
-                const Alloc& rhs_alloc                   = Alloc(),
-                AllocatorInvariant check_alloc_invariant = NoOp()) {
+TEST_CONSTEXPR_CXX26 void
+test_alloc(const Alloc& lhs_alloc                   = Alloc(),
+           const Alloc& rhs_alloc                   = Alloc(),
+           AllocatorInvariant check_alloc_invariant = NoOp()) {
   {   // Test empty/non-empty set combinations
     { // assign from a non-empty container into an empty one
       using Set = std::set<int, std::less<int>, Alloc>;
@@ -236,7 +237,7 @@ void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator<int> >();
 #if TEST_STD_VER >= 11
   test_alloc<min_allocator<int> >();
@@ -248,10 +249,10 @@ void test() {
       std::vector<void*>* rhs_allocs_;
 
     public:
-      AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
+      TEST_CONSTEXPR_CXX26 AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
           : lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
 
-      void operator()() {
+      TEST_CONSTEXPR_CXX26 void operator()() {
         assert(lhs_allocs_->empty());
         assert(rhs_allocs_->empty());
       }
@@ -275,10 +276,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
index 56540fbf35c6a..6de41997d424d 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
@@ -51,7 +51,7 @@
 #include "test_allocator.h"
 
 struct NotAnAllocator {
-  friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
+  TEST_CONSTEXPR_CXX26 friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
index 23a5136aafcf2..d24cc95d69469 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2147483647
 
 // template<container-compatible-range<value_type> R>
 //   set(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
@@ -22,7 +23,7 @@
 #include "../../from_range_associative_containers.h"
 #include "test_macros.h"
 
-void test_duplicates() {
+TEST_CONSTEXPR_CXX26 void test_duplicates() {
   using T = KeyValue;
 
   std::array input    = {T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}};
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
index 4f15202f2ca83..948cc85e7552d 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
@@ -73,7 +73,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(m3.key_comp() == C(5));
     LIBCPP_ASSERT(m1.empty());
   }
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef Counter<int> V;
     typedef std::less<V> C;
     typedef test_allocator<V> A;
diff --git a/libcxx/test/std/containers/from_range_helpers.h b/libcxx/test/std/containers/from_range_helpers.h
index edf1d6ce528d7..13bd74d15cf14 100644
--- a/libcxx/test/std/containers/from_range_helpers.h
+++ b/libcxx/test/std/containers/from_range_helpers.h
@@ -54,8 +54,8 @@ struct KeyValue {
   int key;    // Only the key is considered for equality comparison.
   char value; // Allows distinguishing equivalent instances.
 
-  bool operator<(const KeyValue& other) const { return key < other.key; }
-  bool operator==(const KeyValue& other) const { return key == other.key; }
+  TEST_CONSTEXPR_CXX26 bool operator<(const KeyValue& other) const { return key < other.key; }
+  TEST_CONSTEXPR_CXX26 bool operator==(const KeyValue& other) const { return key == other.key; }
 };
 
 template <>

>From b2e088aa09088fd8d263a92547797a297a103917 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 16:15:59 -0500
Subject: [PATCH 05/21] Test set.erasure

---
 .../containers/associative/set/set.erasure/erase_if.pass.cpp  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
index 978e80821eb49..a5b2c741e02e4 100644
--- a/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
@@ -20,14 +20,14 @@
 #include "min_allocator.h"
 
 template <class S, class Pred>
-void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
+TEST_CONSTEXPR_CXX26 void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
   ASSERT_SAME_TYPE(typename S::size_type, decltype(std::erase_if(s, p)));
   assert(expected_erased_count == std::erase_if(s, p));
   assert(s == expected);
 }
 
 template <typename S>
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   auto is1   = [](auto v) { return v == 1; };
   auto is2   = [](auto v) { return v == 2; };
   auto is3   = [](auto v) { return v == 3; };

>From 42f2950a3143df5907a575323230e284e3cf3834 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 16:18:08 -0500
Subject: [PATCH 06/21] Test set.observers

---
 .../std/containers/associative/set/set.observers/comp.pass.cpp  | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
index 9aef8c7cce41b..dcea523540247 100644
--- a/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
@@ -14,6 +14,8 @@
 #include <set>
 #include <cassert>
 
+#include "test_macros.h"
+
 TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::set<int> set_type;
 

>From b30297eee8b6ed88b907c41663b9203003a3bac7 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 17:48:30 -0500
Subject: [PATCH 07/21] hash correct version from main

---
 libcxx/include/__functional/hash.h         |  9 ---------
 libcxx/test/support/poisoned_hash_helper.h | 10 +++-------
 2 files changed, 3 insertions(+), 16 deletions(-)

diff --git a/libcxx/include/__functional/hash.h b/libcxx/include/__functional/hash.h
index 83bbf1b5e26c3..f74f25fa6e84b 100644
--- a/libcxx/include/__functional/hash.h
+++ b/libcxx/include/__functional/hash.h
@@ -433,13 +433,10 @@ struct __hash_impl<long double> : __scalar_hash<long double> {
 template <class _Tp>
 struct hash : public __hash_impl<_Tp> {};
 
-#if _LIBCPP_STD_VER >= 17
-
 template <>
 struct hash<nullptr_t> : public __unary_function<nullptr_t, size_t> {
   _LIBCPP_HIDE_FROM_ABI size_t operator()(nullptr_t) const _NOEXCEPT { return 662607004ull; }
 };
-#endif
 
 #ifndef _LIBCPP_CXX03_LANG
 template <class _Key, class _Hash>
@@ -452,18 +449,12 @@ template <class _Key, class _Hash = hash<_Key> >
 using __has_enabled_hash _LIBCPP_NODEBUG =
     integral_constant<bool, __check_hash_requirements<_Key, _Hash>::value && is_default_constructible<_Hash>::value >;
 
-#  if _LIBCPP_STD_VER >= 17
 template <class _Type, class>
 using __enable_hash_helper_imp _LIBCPP_NODEBUG = _Type;
 
 template <class _Type, class... _Keys>
 using __enable_hash_helper _LIBCPP_NODEBUG =
     __enable_hash_helper_imp<_Type, __enable_if_t<__all<__has_enabled_hash<_Keys>::value...>::value> >;
-#  else
-template <class _Type, class...>
-using __enable_hash_helper _LIBCPP_NODEBUG = _Type;
-#  endif
-
 #endif // !_LIBCPP_CXX03_LANG
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/support/poisoned_hash_helper.h b/libcxx/test/support/poisoned_hash_helper.h
index 93b579d2dfde3..cd71cd70d6a84 100644
--- a/libcxx/test/support/poisoned_hash_helper.h
+++ b/libcxx/test/support/poisoned_hash_helper.h
@@ -123,13 +123,9 @@ struct Class {};
 // Each header that declares the std::hash template provides enabled
 // specializations of std::hash for std::nullptr_t and all cv-unqualified
 // arithmetic, enumeration, and pointer types.
-#if TEST_STD_VER >= 17
-using MaybeNullptr = types::type_list<std::nullptr_t>;
-#else
-using MaybeNullptr = types::type_list<>;
-#endif
-using LibraryHashTypes = types::
-    concatenate_t<types::arithmetic_types, types::type_list<Enum, EnumClass, void*, void const*, Class*>, MaybeNullptr>;
+using LibraryHashTypes =
+    types::concatenate_t<types::arithmetic_types,
+                         types::type_list<Enum, EnumClass, void*, void const*, Class*, std::nullptr_t>>;
 
 struct TestHashEnabled {
   template <class T>

>From df77e29f261fd166ef1a56908df8519d941b59fb Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 17:57:58 -0500
Subject: [PATCH 08/21] saturation correct version from main

---
 libcxx/include/__numeric/saturation_arithmetic.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 7a7410b5dea08..4491bab2b1479 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -121,27 +121,27 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Rp __saturate_cast(_Tp __x) noexcept {
 #if _LIBCPP_STD_VER >= 26
 
 template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
   return std::__add_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
   return std::__sub_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
   return std::__mul_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
   return std::__div_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Rp, __signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
   return std::__saturate_cast<_Rp>(__x);
 }
 

>From 7ce7dcdba3724e6109ca2b925077771d7f2d79ca Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 21:10:14 -0500
Subject: [PATCH 09/21] Fix set/ final tests

---
 libcxx/include/__node_handle                      |  4 +++-
 .../containers/associative/set/contains.pass.cpp  |  6 ++++--
 .../associative/set/contains_transparent.pass.cpp | 15 ++++++++++-----
 .../associative/set/count_transparent.pass.cpp    | 10 +++++++---
 .../containers/associative/set/emplace.pass.cpp   |  5 +++--
 .../associative/set/emplace_hint.pass.cpp         |  5 +++--
 .../set/equal_range_transparent.pass.cpp          | 10 +++++++---
 .../associative/set/erase_iter.pass.cpp           |  4 ++--
 .../associative/set/extract_iterator.pass.cpp     |  4 ++--
 .../associative/set/extract_key.pass.cpp          |  4 ++--
 ...rt_and_emplace_allocator_requirements.pass.cpp | 15 ++++-----------
 .../containers/associative/set/insert_cv.pass.cpp |  2 +-
 .../associative/set/insert_iter_iter.pass.cpp     |  8 ++------
 .../associative/set/insert_node_type.pass.cpp     | 13 +++++++------
 .../set/insert_node_type_hint.pass.cpp            |  9 +++++----
 .../associative/set/insert_range.pass.cpp         |  1 +
 .../std/containers/associative/set/merge.pass.cpp | 13 +++++++------
 .../test/std/containers/insert_range_maps_sets.h  | 12 ++++++------
 .../set_allocator_requirement_test_templates.h    |  2 +-
 19 files changed, 77 insertions(+), 65 deletions(-)

diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index 58be5b9d4ddd5..6d253cb4eaa98 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -172,7 +172,9 @@ template <class _NodeType, class _Derived>
 struct __set_node_handle_specifics {
   typedef typename _NodeType::__node_value_type value_type;
 
-  _LIBCPP_HIDE_FROM_ABI value_type& value() const { return static_cast<_Derived const*>(this)->__ptr_->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_type& value() const {
+    return static_cast<_Derived const*>(this)->__ptr_->__get_value();
+  }
 };
 
 template <class _NodeType, class _Derived>
diff --git a/libcxx/test/std/containers/associative/set/contains.pass.cpp b/libcxx/test/std/containers/associative/set/contains.pass.cpp
index 20b1eeb839414..c147b01d73c10 100644
--- a/libcxx/test/std/containers/associative/set/contains.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/contains.pass.cpp
@@ -11,12 +11,14 @@
 #include <cassert>
 #include <set>
 
+#include "test_macros.h"
+
 // <set>
 
 // constexpr bool contains(const key_type& x) const; // constexpr since C++26
 
 template <typename T, typename V, typename B, typename... Vals>
-void test(B bad, Vals... args) {
+TEST_CONSTEXPR_CXX26 void test(B bad, Vals... args) {
   T set;
   V vals[] = {args...};
 
@@ -39,7 +41,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     test<std::set<int>, int>(14, 10, 11, 12, 13);
     test<std::set<char>, char>('e', 'a', 'b', 'c', 'd');
   }
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     test<std::multiset<int>, int>(14, 10, 11, 12, 13);
     test<std::multiset<char>, char>('e', 'a', 'b', 'c', 'd');
   }
diff --git a/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
index acd03e649095f..0b7e032414425 100644
--- a/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
@@ -18,18 +18,22 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 template <typename Container>
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   Container s{{2, 1}, {1, 2}, {1, 3}, {1, 4}, {2, 2}};
 
   assert(s.contains(1));
@@ -38,7 +42,8 @@ void test() {
 
 TEST_CONSTEXPR_CXX26 bool test() {
   test<std::set<std::pair<int, int>, Comp> >();
-  test<std::multiset<std::pair<int, int>, Comp> >();
+  if (!TEST_IS_CONSTANT_EVALUATED)
+    test<std::multiset<std::pair<int, int>, Comp> >();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
index 9147b38e5d336..a14f4efe8c48d 100644
--- a/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
@@ -19,14 +19,18 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/set/emplace.pass.cpp b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
index fecd9ef2fe7f9..6c93d99356c12 100644
--- a/libcxx/test/std/containers/associative/set/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
@@ -24,7 +24,7 @@
 #include "min_allocator.h"
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::set<DefaultOnly> M;
     typedef std::pair<M::iterator, bool> R;
     M m;
@@ -43,7 +43,8 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 1);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED)
+    assert(DefaultOnly::count == 0);
   {
     typedef std::set<Emplaceable> M;
     typedef std::pair<M::iterator, bool> R;
diff --git a/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
index 5c68000ffbab9..5282f4295ec8e 100644
--- a/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
@@ -24,7 +24,7 @@
 #include "min_allocator.h"
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::set<DefaultOnly> M;
     typedef M::iterator R;
     M m;
@@ -41,7 +41,8 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 1);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED)
+    assert(DefaultOnly::count == 0);
   {
     typedef std::set<Emplaceable> M;
     typedef M::iterator R;
diff --git a/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
index 92c1c64be658c..9f2606412a5d2 100644
--- a/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
@@ -23,14 +23,18 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
index 7682a81dc7407..952714a3720b0 100644
--- a/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
@@ -20,10 +20,10 @@
 
 struct TemplateConstructor {
   template <typename T>
-  TemplateConstructor(const T&) {}
+  TEST_CONSTEXPR_CXX26 TemplateConstructor(const T&) {}
 };
 
-bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
+TEST_CONSTEXPR_CXX26 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
 TEST_CONSTEXPR_CXX26 bool test() {
   {
diff --git a/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
index 1511b08079d03..59a83338da06f 100644
--- a/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   std::size_t sz = c.size();
 
   for (auto first = c.cbegin(); first != c.cend();) {
@@ -42,7 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     test(m);
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::set<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     assert(Counter_base::gConstructed == 6);
     test(m);
diff --git a/libcxx/test/std/containers/associative/set/extract_key.pass.cpp b/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
index 07f6d193179a8..432bfbdb1b8fb 100644
--- a/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container, class KeyTypeIter>
-void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
+TEST_CONSTEXPR_CXX26 void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   std::size_t sz = c.size();
   assert((std::size_t)std::distance(first, last) == sz);
 
@@ -48,7 +48,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     test(m, std::begin(keys), std::end(keys));
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::set<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     {
       Counter<int> keys[] = {1, 2, 3, 4, 5, 6};
diff --git a/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
index ace31eb66ea8d..4711473de4dad 100644
--- a/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
@@ -10,9 +10,9 @@
 
 // class set
 
-// constexpr insert(...) // constexpr since C++26
-// constexpr emplace(...) // constexpr since C++26
-// constexpr emplace_hint(...) // constexpr since C++26
+// insert(...)
+// emplace(...)
+// emplace_hint(...)
 
 // UNSUPPORTED: c++03
 
@@ -21,17 +21,10 @@
 #include "container_test_types.h"
 #include "../../set_allocator_requirement_test_templates.h"
 
-TEST_CONSTEXPR_CXX26 bool test() {
+int main(int, char**) {
   testSetInsert<TCT::set<> >();
   testSetEmplace<TCT::set<> >();
   testSetEmplaceHint<TCT::set<> >();
 
-  return true;
-}
-int main(int, char**) {
-  test();
-#if TEST_STD_VER >= 26
-  static_assert(test());
-#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
index ef278224f0229..3dc33a80fe2ad 100644
--- a/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 
 template <class Container>
-void do_insert_cv_test() {
+TEST_CONSTEXPR_CXX26 void do_insert_cv_test() {
   typedef Container M;
   typedef std::pair<typename M::iterator, bool> R;
   typedef typename M::value_type VT;
diff --git a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
index 6d6bd185afa04..ca0180af82897 100644
--- a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
@@ -21,7 +21,7 @@
 #include "test_iterators.h"
 
 template <class Iter, class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   {   // Check that an empty range works correctly
     { // Without elements in the container
       using Map = std::set<int, std::less<int>, Alloc>;
@@ -178,13 +178,9 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<cpp17_input_iterator<int*>, std::allocator<int> >();
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
index 935a90afedb2b..1512debd4e0bd 100644
--- a/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
@@ -21,7 +21,7 @@
 #include "min_allocator.h"
 
 template <class Container, class T>
-void verify_insert_return_type(T&& t) {
+TEST_CONSTEXPR_CXX26 void verify_insert_return_type(T&& t) {
   using verified_type = std::remove_cv_t<std::remove_reference_t<T>>;
   static_assert(std::is_aggregate_v<verified_type>);
   static_assert(std::is_same_v<verified_type, typename Container::insert_return_type>);
@@ -42,19 +42,20 @@ void verify_insert_return_type(T&& t) {
 }
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto p = c.insert(key);
   assert(p.second);
   return c.extract(p.first);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
 
+  Container c2;
+
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c2, i);
     assert(!node.empty());
     typename Container::insert_return_type irt = c.insert(std::move(node));
     assert(node.empty());
@@ -77,7 +78,7 @@ void test(Container& c) {
   }
 
   { // Insert duplicate node.
-    typename Container::node_type dupl = nf(0);
+    typename Container::node_type dupl = nf(c2, 0);
     auto irt                           = c.insert(std::move(dupl));
     assert(dupl.empty());
     assert(!irt.inserted);
diff --git a/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
index f14263595ac6a..a1df9de53ae8a 100644
--- a/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
@@ -19,19 +19,20 @@
 #include "min_allocator.h"
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto p = c.insert(key);
   assert(p.second);
   return c.extract(p.first);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
 
+  Container c2;
+
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c2, i);
     assert(!node.empty());
     std::size_t prev = c.size();
     auto it          = c.insert(c.end(), std::move(node));
diff --git a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
index 654d4d425774a..00cead676709a 100644
--- a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
@@ -9,6 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
 // ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-missing-field-initializers
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2147483647
 
 // <set>
 
diff --git a/libcxx/test/std/containers/associative/set/merge.pass.cpp b/libcxx/test/std/containers/associative/set/merge.pass.cpp
index 4c76c59ec5de5..bcaf13d4a7e1b 100644
--- a/libcxx/test/std/containers/associative/set/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/merge.pass.cpp
@@ -27,7 +27,7 @@
 #include "Counter.h"
 
 template <class Set>
-bool set_equal(const Set& set, Set other) {
+TEST_CONSTEXPR_CXX26 bool set_equal(const Set& set, Set other) {
   return set == other;
 }
 
@@ -55,8 +55,8 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(set_equal(dst, {1, 2, 3, 4, 5}));
   }
 
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     bool do_throw = false;
     typedef std::set<Counter<int>, throw_comparator> set_type;
     set_type src({1, 3, 5}, throw_comparator(do_throw));
@@ -75,13 +75,14 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(set_equal(dst, set_type({2, 4, 5}, throw_comparator(do_throw))));
   }
 #endif
-  assert(Counter_base::gConstructed == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED)
+    assert(Counter_base::gConstructed == 0);
   struct comparator {
     comparator() = default;
 
     bool operator()(const Counter<int>& lhs, const Counter<int>& rhs) const { return lhs < rhs; }
   };
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::set<Counter<int>, std::less<Counter<int>>> first_set_type;
     typedef std::set<Counter<int>, comparator> second_set_type;
     typedef std::multiset<Counter<int>, comparator> third_set_type;
@@ -128,7 +129,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
       first.merge(second);
       first.merge(std::move(second));
     }
-    {
+    if (!TEST_IS_CONSTANT_EVALUATED) {
       std::multiset<int> second;
       first.merge(second);
       first.merge(std::move(second));
diff --git a/libcxx/test/std/containers/insert_range_maps_sets.h b/libcxx/test/std/containers/insert_range_maps_sets.h
index 6e344d8ad3b0f..8563b7b43b934 100644
--- a/libcxx/test/std/containers/insert_range_maps_sets.h
+++ b/libcxx/test/std/containers/insert_range_maps_sets.h
@@ -200,7 +200,7 @@ TestCaseMapSet<std::pair<K, V>> constexpr NElementsContainer_RangeWithDuplicates
         {16, 'D'}}};
 
 template <class Container, class T, class Iter, class Sent>
-void test_map_set_insert_range(bool allow_duplicates = false) {
+TEST_CONSTEXPR_CXX26 void test_map_set_insert_range(bool allow_duplicates = false) {
   auto test = [&](const TestCaseMapSet<T>& test_case, bool check_multi = false) {
     Container c(test_case.initial.begin(), test_case.initial.end());
     auto in = wrap_input<Iter, Sent>(test_case.input);
@@ -250,7 +250,7 @@ void test_map_set_insert_range(bool allow_duplicates = false) {
 // Move-only types.
 
 template <template <class...> class Container>
-void test_set_insert_range_move_only() {
+TEST_CONSTEXPR_CXX26 void test_set_insert_range_move_only() {
   MoveOnly input[5];
   std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
 
@@ -271,8 +271,8 @@ void test_map_insert_range_move_only() {
 // Exception safety.
 
 template <template <class...> class Container>
-void test_set_insert_range_exception_safety_throwing_copy() {
-#if !defined(TEST_HAS_NO_EXCEPTIONS)
+TEST_CONSTEXPR_CXX26 void test_set_insert_range_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_IS_CONSTANT_EVALUATED)
   using T = ThrowingCopy<3>;
   T::reset();
   T in[5] = {{1}, {2}, {3}, {4}, {5}};
@@ -313,8 +313,8 @@ void test_map_insert_range_exception_safety_throwing_copy() {
 }
 
 template <template <class...> class Container, class T>
-void test_assoc_set_insert_range_exception_safety_throwing_allocator() {
-#if !defined(TEST_HAS_NO_EXCEPTIONS)
+TEST_CONSTEXPR_CXX26 void test_assoc_set_insert_range_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_IS_CONSTANT_EVALUATED)
   T in[] = {1, 2};
 
   try {
diff --git a/libcxx/test/std/containers/set_allocator_requirement_test_templates.h b/libcxx/test/std/containers/set_allocator_requirement_test_templates.h
index 10552a5b9ee21..f6bda0eddfbb4 100644
--- a/libcxx/test/std/containers/set_allocator_requirement_test_templates.h
+++ b/libcxx/test/std/containers/set_allocator_requirement_test_templates.h
@@ -27,7 +27,7 @@
 #include "container_test_types.h"
 
 template <class Container>
-void testSetInsert() {
+TEST_CONSTEXPR_CXX26 void testSetInsert() {
   typedef typename Container::value_type ValueTp;
   ConstructController* cc = getConstructController();
   cc->reset();

>From 5764d913c2906ac9010f806395aa2a8b073ce660 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Mon, 10 Nov 2025 07:19:09 -0500
Subject: [PATCH 10/21] restore
 libcxx/test/std/containers/associative/iterator_types.pass.cpp

---
 .../std/containers/associative/iterator_types.pass.cpp   | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/libcxx/test/std/containers/associative/iterator_types.pass.cpp b/libcxx/test/std/containers/associative/iterator_types.pass.cpp
index f394b000615d8..2800549905f1b 100644
--- a/libcxx/test/std/containers/associative/iterator_types.pass.cpp
+++ b/libcxx/test/std/containers/associative/iterator_types.pass.cpp
@@ -46,7 +46,7 @@ void testSet() {
   }
 }
 
-TEST_CONSTEXPR_CXX26 bool test() {
+int main(int, char**) {
   {
     typedef std::map<int, int> Map;
     typedef std::pair<const int, int> ValueTp;
@@ -124,12 +124,5 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
 #endif
 
-  return true;
-}
-int main(int, char**) {
-  test();
-#if TEST_STD_VER >= 26
-  static_assert(test());
-#endif
   return 0;
 }

>From 238229f3cc3d76a3ba038e55a827a4a48197cea0 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 20 Jun 2026 10:56:56 +0100
Subject: [PATCH 11/21] Add feature flag for set

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 ++
 libcxx/include/version                        |  2 ++
 .../set.version.compile.pass.cpp              | 27 +++++++++++++++++++
 .../version.version.compile.pass.cpp          | 27 +++++++++++++++++++
 .../generate_feature_test_macro_components.py |  5 ++++
 5 files changed, 63 insertions(+)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 8e26bdcd860c1..a656bd80497e6 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -450,6 +450,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_constexpr_queue``                              ``202502L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_constexpr_set``                                ``202502L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_constrained_equality``                         ``202411L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_copyable_function``                            *unimplemented*
diff --git a/libcxx/include/version b/libcxx/include/version
index 7f2dc9e4b72ab..711ba498ac101 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -84,6 +84,7 @@ __cpp_lib_constexpr_memory                              202202L <memory>
 __cpp_lib_constexpr_new                                 202406L <new>
 __cpp_lib_constexpr_numeric                             201911L <numeric>
 __cpp_lib_constexpr_queue                               202502L <queue>
+__cpp_lib_constexpr_set                                 202502L <set>
 __cpp_lib_constexpr_string                              201907L <string>
 __cpp_lib_constexpr_string_view                         201811L <string_view>
 __cpp_lib_constexpr_tuple                               201811L <tuple>
@@ -575,6 +576,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #   define __cpp_lib_constexpr_new                      202406L
 # endif
 # define __cpp_lib_constexpr_queue                      202502L
+# define __cpp_lib_constexpr_set                        202502L
 # define __cpp_lib_constrained_equality                 202411L
 // # define __cpp_lib_copyable_function                    202306L
 // # define __cpp_lib_debugging                            202311L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
index 5dc69f29d0ecd..d1a0570557246 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
@@ -32,6 +32,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should not be defined before c++23"
 #  endif
@@ -66,6 +70,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should not be defined before c++23"
 #  endif
@@ -106,6 +114,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should not be defined before c++23"
 #  endif
@@ -152,6 +164,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should not be defined before c++23"
 #  endif
@@ -210,6 +226,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should be defined in c++23"
 #  endif
@@ -280,6 +300,13 @@
 #    endif
 #  endif
 
+#  ifndef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should be defined in c++26"
+#  endif
+#  if __cpp_lib_constexpr_set != 202502L
+#    error "__cpp_lib_constexpr_set should have the value 202502L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should be defined in c++26"
 #  endif
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 d9c78b73f7e23..a88ec24e4c483 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
@@ -252,6 +252,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should not be defined before c++20"
 #  endif
@@ -1188,6 +1192,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should not be defined before c++20"
 #  endif
@@ -2226,6 +2234,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should not be defined before c++20"
 #  endif
@@ -3510,6 +3522,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should be defined in c++20"
 #  endif
@@ -5004,6 +5020,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should be defined in c++23"
 #  endif
@@ -6744,6 +6764,13 @@
 #    error "__cpp_lib_constexpr_queue should have the value 202502L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should be defined in c++26"
+#  endif
+#  if __cpp_lib_constexpr_set != 202502L
+#    error "__cpp_lib_constexpr_set should have the value 202502L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should be defined in c++26"
 #  endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d764a1f677ba6..078e3b815742c 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -432,6 +432,11 @@ def add_version_header(tc):
             "values": {"c++26": 202502},
             "headers": ["queue"],
         },
+        {
+            "name": "__cpp_lib_constexpr_set",
+            "values": {"c++26": 202502},
+            "headers": ["set"],
+        },
         {
             "name": "__cpp_lib_constexpr_string",
             "values": {"c++20": 201907},

>From d559bade444c4925d7ffa5355fbef9f413a12f33 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 21 Jun 2026 22:14:01 +0100
Subject: [PATCH 12/21] add set back to constexpr

---
 .../alg.foreach/ranges.for_each.associative.pass.cpp      | 8 ++------
 .../set/set.nonmember/compare.three_way.pass.cpp          | 1 -
 .../std/containers/container.node/node_handle.pass.cpp    | 4 ++--
 3 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
index e0186654e4bbc..1ee485b1aef0d 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
@@ -253,23 +253,19 @@ TEST_CONSTEXPR_CXX26 void test_invoke_map_like() {
 
 TEST_CONSTEXPR_CXX26 bool test() {
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when set is made constexpr
-    test_node_container<std::set<int> >([](int i) { return i; });
-
     // FIXME: remove when multiset is made constexpr
     test_node_container<std::multiset<int> >([](int i) { return i; });
   }
+  test_node_container<std::set<int> >([](int i) { return i; });
   test_node_container<std::map<int, int> >([](int i) { return std::make_pair(i, i); });
   test_node_container<std::multimap<int, int> >([](int i) { return std::make_pair(i, i); });
 
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when set is made constexpr
-    test_invoke_set_like<std::set>();
-
     // FIXME: remove when multiset is made constexpr
     test_invoke_set_like<std::multiset>();
   }
 
+  test_invoke_set_like<std::set>();
   test_invoke_map_like<std::map>();
   test_invoke_map_like<std::multimap>();
 
diff --git a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
index 563a8c12db828..ce34b7d159dd4 100644
--- a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
@@ -22,7 +22,6 @@
 
 TEST_CONSTEXPR_CXX26 bool test() {
   assert((test_ordered_set_container_spaceship<std::set>()));
-  // `std::set` is not constexpr, so no `static_assert` test here.
   return true;
 }
 int main(int, char**) {
diff --git a/libcxx/test/std/containers/container.node/node_handle.pass.cpp b/libcxx/test/std/containers/container.node/node_handle.pass.cpp
index 38c1f60a31c49..a6737c9037893 100644
--- a/libcxx/test/std/containers/container.node/node_handle.pass.cpp
+++ b/libcxx/test/std/containers/container.node/node_handle.pass.cpp
@@ -150,10 +150,10 @@ TEST_CONSTEXPR_CXX26 bool test_insert_return_type() {
 TEST_CONSTEXPR_CXX26 bool test() {
   test_node_handle_operations<std::map<int, int>>();
   test_node_handle_operations_multi<std::multimap<int, int>>();
+  test_node_handle_operations<std::set<int>>();
 
   // FIXME: update when other containers are made constexpr
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    test_node_handle_operations<std::set<int>>();
     test_node_handle_operations_multi<std::multiset<int>>();
     test_node_handle_operations<std::unordered_map<int, int>>();
     test_node_handle_operations_multi<std::unordered_multimap<int, int>>();
@@ -162,10 +162,10 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
 
   test_insert_return_type<std::map<int, int>>();
+  test_insert_return_type<std::set<int>>();
 
   // FIXME: update when other containers are made constexpr
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    test_insert_return_type<std::set<int>>();
     test_insert_return_type<std::unordered_map<int, int>>();
     test_insert_return_type<std::unordered_set<int>>();
   }

>From ee147e46e940c79ef37111bb04d381a95e510cd7 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Thu, 25 Jun 2026 08:34:28 +0100
Subject: [PATCH 13/21] missing constexpr

---
 .../alg.foreach/ranges.for_each.associative.pass.cpp      | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
index 1ee485b1aef0d..02698819d71df 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
@@ -189,23 +189,23 @@ TEST_CONSTEXPR_CXX26 void test_node_container(Converter conv) {
 }
 
 template <template <class> class Container>
-void test_invoke_set_like() {
+TEST_CONSTEXPR_CXX26 void test_invoke_set_like() {
   { // check that std::invoke is used
     struct T {
       mutable int i = 3;
 
-      void zero() const { i = 0; }
+      TEST_CONSTEXPR_CXX26 void zero() const { i = 0; }
     };
 
     class S {
       int val_;
 
     public:
-      S(int val) : val_(val) {}
+      TEST_CONSTEXPR_CXX26 S(int val) : val_(val) {}
 
       T j;
 
-      bool operator<(const S& rhs) const { return val_ < rhs.val_; }
+      TEST_CONSTEXPR_CXX26 bool operator<(const S& rhs) const { return val_ < rhs.val_; }
     };
 
     { // Iterator overload

>From 330720d49f57abc087a9c7e9aa4df5fedca03eb9 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Thu, 25 Jun 2026 08:38:06 +0100
Subject: [PATCH 14/21] cleanup

---
 libcxx/include/set | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/include/set b/libcxx/include/set
index 565778a836fd1..a8e245b22ae51 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -10,8 +10,6 @@
 #ifndef _LIBCPP_SET
 #define _LIBCPP_SET
 
-// TODO: add constexpr everywhere
-
 /*
 
     set synopsis

>From 6c70a6075dcb19eccb6e2da3205aa1b97c8f37f0 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 27 Jun 2026 13:54:32 +0100
Subject: [PATCH 15/21] Initial constexpr

---
 libcxx/include/set                            | 249 +++++++++++-------
 .../associative/multiset/clear.pass.cpp       |  11 +-
 .../associative/multiset/count.pass.cpp       |  11 +-
 .../multiset/count_transparent.pass.cpp       |  11 +-
 .../associative/multiset/emplace.pass.cpp     |  11 +-
 .../multiset/emplace_hint.pass.cpp            |  11 +-
 .../associative/multiset/empty.pass.cpp       |  11 +-
 .../associative/multiset/equal_range.pass.cpp |  13 +-
 .../multiset/equal_range_transparent.pass.cpp |  13 +-
 .../associative/multiset/erase_iter.pass.cpp  |  11 +-
 .../multiset/erase_iter_iter.pass.cpp         |  11 +-
 .../associative/multiset/erase_key.pass.cpp   |  11 +-
 .../multiset/extract_iterator.pass.cpp        |  11 +-
 .../associative/multiset/extract_key.pass.cpp |  11 +-
 .../associative/multiset/find.pass.cpp        |  13 +-
 .../multiset/get_allocator.pass.cpp           |  11 +-
 .../multiset/incomplete_type.pass.cpp         |  13 +-
 .../associative/multiset/insert_cv.pass.cpp   |  11 +-
 ...rt_emplace_allocator_requirements.pass.cpp |   9 +-
 .../multiset/insert_initializer_list.pass.cpp |  11 +-
 .../multiset/insert_iter_cv.pass.cpp          |  11 +-
 .../multiset/insert_iter_iter.pass.cpp        |   7 +-
 .../multiset/insert_iter_rv.pass.cpp          |  11 +-
 .../multiset/insert_node_type.pass.cpp        |  11 +-
 .../multiset/insert_node_type_hint.pass.cpp   |  11 +-
 .../multiset/insert_range.pass.cpp            |  11 +-
 .../associative/multiset/insert_rv.pass.cpp   |  11 +-
 .../associative/multiset/iterator.pass.cpp    |  33 ++-
 .../associative/multiset/lower_bound.pass.cpp |  13 +-
 .../associative/multiset/max_size.pass.cpp    |  11 +-
 .../associative/multiset/merge.pass.cpp       |  17 +-
 .../multiset/multiset.cons/alloc.pass.cpp     |  11 +-
 .../assign_initializer_list.pass.cpp          |  11 +-
 .../multiset/multiset.cons/compare.pass.cpp   |  11 +-
 .../multiset.cons/compare_alloc.pass.cpp      |  11 +-
 .../multiset/multiset.cons/copy.pass.cpp      |  11 +-
 .../multiset.cons/copy_alloc.pass.cpp         |  11 +-
 .../multiset.cons/copy_assign.pass.cpp        |  11 +-
 .../multiset/multiset.cons/deduct.pass.cpp    |   9 +-
 .../multiset/multiset.cons/default.pass.cpp   |  11 +-
 .../multiset.cons/default_noexcept.pass.cpp   |  11 +-
 .../multiset.cons/dtor_noexcept.pass.cpp      |  11 +-
 .../multiset.cons/from_range.pass.cpp         |  13 +-
 .../multiset.cons/initializer_list.pass.cpp   |  11 +-
 .../initializer_list_compare.pass.cpp         |  11 +-
 .../initializer_list_compare_alloc.pass.cpp   |  11 +-
 .../multiset/multiset.cons/iter_iter.pass.cpp |  11 +-
 .../multiset.cons/iter_iter_alloc.pass.cpp    |  11 +-
 .../multiset.cons/iter_iter_comp.pass.cpp     |  11 +-
 .../multiset/multiset.cons/move.pass.cpp      |  11 +-
 .../multiset.cons/move_alloc.pass.cpp         |  11 +-
 .../multiset.cons/move_assign.pass.cpp        |  11 +-
 .../multiset.cons/move_noexcept.pass.cpp      |  11 +-
 .../multiset.erasure/erase_if.pass.cpp        |  11 +-
 .../compare.three_way.pass.cpp                |  11 +-
 .../multiset.nonmember/op_compare.pass.cpp    |   9 +-
 .../multiset/multiset.observers/comp.pass.cpp |  13 +-
 .../multiset.special/member_swap.pass.cpp     |  11 +-
 .../multiset.special/non_member_swap.pass.cpp |  11 +-
 .../multiset.special/swap_noexcept.pass.cpp   |  13 +-
 .../associative/multiset/size.pass.cpp        |  10 +-
 .../associative/multiset/upper_bound.pass.cpp |  13 +-
 62 files changed, 723 insertions(+), 232 deletions(-)

diff --git a/libcxx/include/set b/libcxx/include/set
index a8e245b22ae51..0409131ac6981 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -1176,31 +1176,34 @@ public:
   friend class multiset;
 
   // construct/copy/destroy:
-  _LIBCPP_HIDE_FROM_ABI multiset() _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset() _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_default_constructible<key_compare>::value&&
           is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(value_compare()) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const value_compare& __comp) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const value_compare& __comp) _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(__comp) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const value_compare& __comp, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {}
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__f, __l);
   }
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multiset(__f, __l, key_compare(), __a) {}
 #  endif
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__f, __l);
@@ -1208,7 +1211,7 @@ public:
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(from_range_t,
            _Range&& __range,
            const key_compare& __comp = key_compare(),
@@ -1218,213 +1221,272 @@ public:
   }
 
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI multiset(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(from_range_t, _Range&& __range, const allocator_type& __a)
       : multiset(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(const multiset& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(const multiset& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(const multiset& __s) = default;
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(multiset&& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s, const allocator_type& __a) : __tree_(std::move(__s.__tree_), __a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(multiset&& __s, const allocator_type& __a)
+      : __tree_(std::move(__s.__tree_), __a) {}
 #  endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const allocator_type& __a) : __tree_(__a) {}
-  _LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s, const allocator_type& __a) : __tree_(__s.__tree_, __a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const allocator_type& __a) : __tree_(__a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(const multiset& __s, const allocator_type& __a)
+      : __tree_(__s.__tree_, __a) {}
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__il.begin(), __il.end());
   }
 
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(initializer_list<value_type> __il, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__il.begin(), __il.end());
   }
 
 #    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(initializer_list<value_type> __il, const allocator_type& __a)
       : multiset(__il, key_compare(), __a) {}
 #    endif
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(initializer_list<value_type> __il) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(initializer_list<value_type> __il) {
     clear();
     insert(__il.begin(), __il.end());
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(multiset&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(multiset&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI ~multiset() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~multiset() {
     static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return __tree_.begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return __tree_.begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return __tree_.end(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return __tree_.end(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT {
+    return __tree_.begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator begin() const _NOEXCEPT {
+    return __tree_.begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator end() _NOEXCEPT {
+    return __tree_.end();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator end() const _NOEXCEPT {
+    return __tree_.end();
+  }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT { return reverse_iterator(end()); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rbegin() const _NOEXCEPT {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rbegin() _NOEXCEPT {
+    return reverse_iterator(end());
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator
+  rbegin() const _NOEXCEPT {
     return const_reverse_iterator(end());
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI reverse_iterator rend() _NOEXCEPT { return reverse_iterator(begin()); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const _NOEXCEPT {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rend() _NOEXCEPT {
+    return reverse_iterator(begin());
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator rend() const _NOEXCEPT {
     return const_reverse_iterator(begin());
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const _NOEXCEPT { return begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator cend() const _NOEXCEPT { return end(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crbegin() const _NOEXCEPT { return rbegin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const _NOEXCEPT { return rend(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cbegin() const _NOEXCEPT {
+    return begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cend() const _NOEXCEPT {
+    return end();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator
+  crbegin() const _NOEXCEPT {
+    return rbegin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator crend() const _NOEXCEPT {
+    return rend();
+  }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT { return __tree_.size() == 0; }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __tree_.size(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT { return __tree_.max_size(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool empty() const _NOEXCEPT {
+    return __tree_.size() == 0;
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type size() const _NOEXCEPT {
+    return __tree_.size();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type max_size() const _NOEXCEPT {
+    return __tree_.max_size();
+  }
 
   // modifiers:
 #  ifndef _LIBCPP_CXX03_LANG
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace(_Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace(_Args&&... __args) {
     return __tree_.__emplace_multi(std::forward<_Args>(__args)...);
   }
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace_hint(const_iterator __p, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace_hint(const_iterator __p, _Args&&... __args) {
     return __tree_.__emplace_hint_multi(__p, std::forward<_Args>(__args)...);
   }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const value_type& __v) { return __tree_.__emplace_multi(__v); }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, const value_type& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const value_type& __v) {
+    return __tree_.__emplace_multi(__v);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, const value_type& __v) {
     return __tree_.__emplace_hint_multi(__p, __v);
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __first, _InputIterator __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(_InputIterator __first, _InputIterator __last) {
     __tree_.__insert_range_multi(__first, __last);
   }
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert_range(_Range&& __range) {
     __tree_.__insert_range_multi(ranges::begin(__range), ranges::end(__range));
   }
 #  endif
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI iterator insert(value_type&& __v) { return __tree_.__emplace_multi(std::move(__v)); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(value_type&& __v) {
+    return __tree_.__emplace_multi(std::move(__v));
+  }
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, value_type&& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, value_type&& __v) {
     return __tree_.__emplace_hint_multi(__p, std::move(__v));
   }
 
-  _LIBCPP_HIDE_FROM_ABI void insert(initializer_list<value_type> __il) { insert(__il.begin(), __il.end()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(initializer_list<value_type> __il) {
+    insert(__il.begin(), __il.end());
+  }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
-  _LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_multi(__k); }
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
-  _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p) { return __tree_.erase(__p); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(const key_type& __k) {
+    return __tree_.__erase_multi(__k);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
+    return __tree_.erase(__f, __l);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
-  _LIBCPP_HIDE_FROM_ABI iterator insert(node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to multiset::insert()");
     return __tree_.template __node_handle_insert_multi<node_type>(std::move(__nh));
   }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __hint, node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __hint, node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to multiset::insert()");
     return __tree_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract<node_type>(__it);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI void swap(multiset& __s) _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(multiset& __s)
+      _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
     __tree_.swap(__s.__tree_);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const _NOEXCEPT { return __tree_.__alloc(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI key_compare key_comp() const { return __tree_.value_comp(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI value_compare value_comp() const { return __tree_.value_comp(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type get_allocator() const _NOEXCEPT {
+    return __tree_.__alloc();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_compare key_comp() const {
+    return __tree_.value_comp();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_compare value_comp() const {
+    return __tree_.value_comp();
+  }
 
   // set operations:
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __tree_.find(__k); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __tree_.find(__k); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const key_type& __k) {
+    return __tree_.find(__k);
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const key_type& __k) const {
+    return __tree_.find(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const _K2& __k) {
     return __tree_.find(__k);
   }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const _K2& __k) const {
     return __tree_.find(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const key_type& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const _K2& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  endif
 
 #  if _LIBCPP_STD_VER >= 20
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const key_type& __k) const {
+    return find(__k) != end();
+  }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
 #  endif // _LIBCPP_STD_VER >= 20
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const key_type& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const key_type& __k) {
     return __tree_.__lower_bound_multi(__k);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  lower_bound(const key_type& __k) const {
     return __tree_.__lower_bound_multi(__k);
   }
 
@@ -1432,23 +1494,25 @@ public:
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const _K2& __k) {
     return __tree_.__lower_bound_multi(__k);
   }
 
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  lower_bound(const _K2& __k) const {
     return __tree_.__lower_bound_multi(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const key_type& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const key_type& __k) {
     return __tree_.__upper_bound_multi(__k);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  upper_bound(const key_type& __k) const {
     return __tree_.__upper_bound_multi(__k);
   }
 
@@ -1456,30 +1520,35 @@ public:
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const _K2& __k) {
     return __tree_.__upper_bound_multi(__k);
   }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  upper_bound(const _K2& __k) const {
     return __tree_.__upper_bound_multi(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const key_type& __k) {
+  [[__nodiscard__]]
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const key_type& __k) {
     return __tree_.__equal_range_multi(__k);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const key_type& __k) const {
     return __tree_.__equal_range_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
+  [[__nodiscard__]]
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const _K2& __k) {
     return __tree_.__equal_range_multi(__k);
   }
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const _K2& __k) const {
     return __tree_.__equal_range_multi(__k);
   }
 #  endif
@@ -1543,7 +1612,7 @@ struct __specialized_algorithm<_Alg, __single_range<multiset<_Key, _Compare, _Al
 
   // set's begin() and end() are identical with and without const qualification
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI static auto operator()(const __set& __set, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static auto operator()(const __set& __set, _Args&&... __args) {
     return __specialized_algorithm<_Alg, __single_range<typename __set::__base>>()(
         __set.__tree_, std::forward<_Args>(__args)...);
   }
@@ -1551,7 +1620,7 @@ struct __specialized_algorithm<_Alg, __single_range<multiset<_Key, _Compare, _Al
 #  endif
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator==(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return __x.size() == __y.size() && std::equal(__x.begin(), __x.end(), __y.begin());
 }
@@ -1559,31 +1628,31 @@ operator==(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key,
 #  if _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare(__x.begin(), __x.end(), __y.begin(), __y.end());
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator!=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return !(__x == __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return __y < __x;
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return !(__x < __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return !(__y < __x);
 }
@@ -1591,7 +1660,7 @@ operator<=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key,
 #  else // _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI __synth_three_way_result<_Key>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __synth_three_way_result<_Key>
 operator<=>(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare_three_way(__x.begin(), __x.end(), __y.begin(), __y.end(), __synth_three_way);
 }
@@ -1599,7 +1668,7 @@ operator<=>(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key
 #  endif // _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI void
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
 swap(multiset<_Key, _Compare, _Allocator>& __x, multiset<_Key, _Compare, _Allocator>& __y)
     _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
   __x.swap(__y);
@@ -1607,7 +1676,7 @@ swap(multiset<_Key, _Compare, _Allocator>& __x, multiset<_Key, _Compare, _Alloca
 
 #  if _LIBCPP_STD_VER >= 20
 template <class _Key, class _Compare, class _Allocator, class _Predicate>
-inline _LIBCPP_HIDE_FROM_ABI typename multiset<_Key, _Compare, _Allocator>::size_type
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename multiset<_Key, _Compare, _Allocator>::size_type
 erase_if(multiset<_Key, _Compare, _Allocator>& __c, _Predicate __pred) {
   return std::__libcpp_erase_if_container(__c, __pred);
 }
diff --git a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
index 61a5f25eac608..1c9d7798b81bf 100644
--- a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// void clear() noexcept;
+// void clear() noexcept; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -42,5 +42,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/count.pass.cpp b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
index 6ad96b232083c..ad7b9be77c36a 100644
--- a/libcxx/test/std/containers/associative/multiset/count.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type count(const key_type& k) const;
+// size_type count(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -124,5 +124,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
index aea63384887d4..33eea24eb0476 100644
--- a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class multiset
 
 // template<typename K>
-//     size_type count(const K& x) const;        // C++14
+//     size_type count(const K& x) const;        // C++14, constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -29,11 +29,18 @@ struct Comp {
   bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<std::pair<int, int>, Comp> s{{2, 1}, {1, 1}, {1, 1}, {1, 1}, {2, 2}};
 
   auto cnt = s.count(1);
   assert(cnt == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
index 97cae1c565216..051522f9a55f2 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
@@ -13,7 +13,7 @@
 // class multiset
 
 // template <class... Args>
-//   iterator emplace(Args&&... args);
+//   iterator emplace(Args&&... args); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -23,7 +23,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
@@ -84,5 +84,12 @@ int main(int, char**) {
     assert(set.begin() == iter);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
index d8e9d1cd4d1c6..7c77dda795a71 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
@@ -13,7 +13,7 @@
 // class multiset
 
 // template <class... Args>
-//   iterator emplace_hint(const_iterator position, Args&&... args);
+//   iterator emplace_hint(const_iterator position, Args&&... args); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "DefaultOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
@@ -78,5 +78,12 @@ int main(int, char**) {
     assert(*r == 2);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
index 944f610a0c507..d730d69dc4e09 100644
--- a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// bool empty() const;
+// bool empty() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     M m;
@@ -40,5 +40,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
index e5c1d2b1eab98..03d469c9c5b0a 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-// pair<iterator,iterator>             equal_range(const key_type& k);
-// pair<const_iterator,const_iterator> equal_range(const key_type& k) const;
+// pair<iterator,iterator>             equal_range(const key_type& k); // constexpr since C++26
+// pair<const_iterator,const_iterator> equal_range(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -205,5 +205,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
index 6402488a44f22..6da1813338c3b 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
@@ -14,10 +14,10 @@
 
 // template<typename K>
 //     pair<iterator,iterator>             equal_range(const K& x);        //
-//     C++14
+//     C++14, constexpr since C++26
 // template<typename K>
 //     pair<const_iterator,const_iterator> equal_range(const K& x) const;  //
-//     C++14
+//     C++14, constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -33,7 +33,7 @@ struct Comp {
   bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<std::pair<int, int>, Comp> s{{2, 1}, {1, 1}, {1, 1}, {1, 1}, {2, 2}};
 
   auto er   = s.equal_range(1);
@@ -46,5 +46,12 @@ int main(int, char**) {
 
   assert(nels == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
index 31f00feda2977..3cc984c619490 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator erase(const_iterator position);
+// iterator erase(const_iterator position); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -25,7 +25,7 @@ struct TemplateConstructor {
 
 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -179,5 +179,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
index a3937d13bbb4b..6a6b2831fb515 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator erase(const_iterator first, const_iterator last);
+// iterator erase(const_iterator first, const_iterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -118,5 +118,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
index ba04a238e44b5..3503e3de4ffb7 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type erase(const key_type& k);
+// size_type erase(const key_type& k); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -104,5 +104,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
index 7f32fa82c46ab..f384b3ee95a67 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// node_type extract(const_iterator);
+// node_type extract(const_iterator); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -35,7 +35,7 @@ void test(Container& c) {
   assert(c.size() == 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     using set_type = std::multiset<int>;
     set_type m     = {1, 2, 3, 4, 5, 6};
@@ -55,5 +55,12 @@ int main(int, char**) {
     test(m);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
index ec75a6c5d835c..55896faa61ad3 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// node_type extract(key_type const&);
+// node_type extract(key_type const&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -41,7 +41,7 @@ void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> m = {1, 2, 3, 4, 5, 6};
     int keys[]           = {1, 2, 3, 4, 5, 6};
@@ -78,5 +78,12 @@ int main(int, char**) {
     test(m, std::begin(keys), std::end(keys));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/find.pass.cpp b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
index 866de0da5ea93..b7cb95a5d1a2b 100644
--- a/libcxx/test/std/containers/associative/multiset/find.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-//       iterator find(const key_type& k);
-// const_iterator find(const key_type& k) const;
+//       iterator find(const key_type& k); // constexpr since C++26
+// const_iterator find(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -202,5 +202,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
index 55bbd567d4360..f07d83e2fa18d 100644
--- a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// allocator_type get_allocator() const
+// allocator_type get_allocator() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::allocator<int> alloc;
     const std::multiset<int> s(alloc);
@@ -29,5 +29,12 @@ int main(int, char**) {
     const std::multiset<int, std::less<int>, other_allocator<int> > s(alloc);
     assert(s.get_allocator() == alloc);
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
index 31cb96ec33e76..3313ab39c0934 100644
--- a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
@@ -23,10 +23,17 @@ struct A {
   Set::const_iterator cit;
 };
 
-inline bool operator==(A const& L, A const& R) { return &L == &R; }
-inline bool operator<(A const& L, A const& R) { return L.data < R.data; }
-int main(int, char**) {
+inline TEST_CONSTEXPR_CXX26 bool operator==(A const& L, A const& R) { return &L == &R; }
+inline TEST_CONSTEXPR_CXX26 bool operator<(A const& L, A const& R) { return L.data < R.data; }
+TEST_CONSTEXPR_CXX26 bool test() {
   A a;
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
index 237cf9bb6fac2..22540133feb6d 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator insert(const value_type& v);
+// iterator insert(const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -48,7 +48,7 @@ void do_insert_cv_test() {
   assert(*r == 3);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   do_insert_cv_test<std::multiset<int> >();
 #if TEST_STD_VER >= 11
   {
@@ -57,5 +57,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
index 8085ac7cbb2f1..8f3d61009410a 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
@@ -20,9 +20,16 @@
 #include "container_test_types.h"
 #include "../../set_allocator_requirement_test_templates.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   testMultisetInsert<TCT::multiset<> >();
   testMultisetEmplace<TCT::multiset<> >();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
index 8f4f165252d36..7a25a2fdc96c1 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// void insert(initializer_list<value_type> il);
+// void insert(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> C;
     typedef C::value_type V;
@@ -57,5 +57,12 @@ int main(int, char**) {
     assert(*++i == V(10));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
index 97c03be6cc57b..e271b078ab317 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator insert(const_iterator position, const value_type& v);
+// iterator insert(const_iterator position, const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef M::iterator R;
@@ -70,5 +70,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
index 56483714cd50c..f4d4007b08d09 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
@@ -21,7 +21,7 @@
 #include "test_iterators.h"
 
 template <class Iter, class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   {   // Check that an empty range works correctly
     { // Without elements in the container
       using Map = std::multiset<int, std::less<int>, Alloc>;
@@ -184,13 +184,16 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   test_alloc<cpp17_input_iterator<int*>, std::allocator<int> >();
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
 }
 
 int main(int, char**) {
   test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
 
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
index d01f6b9b2c306..99bcba0ed97f8 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(const_iterator position, value_type&& v);
+// iterator insert(const_iterator position, value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> M;
     typedef M::iterator R;
@@ -71,5 +71,12 @@ int main(int, char**) {
     assert(*r == 3);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
index 6971d6b6e5eae..10f0d730a4444 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(node_type&&);
+// iterator insert(node_type&&); // constexpr since C++26
 
 #include <set>
 #include <type_traits>
@@ -63,11 +63,18 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<int> m;
   test(m);
   std::multiset<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
index b0392088beb6b..3224a73c9abbf 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(const_iterator hint, node_type&&);
+// iterator insert(const_iterator hint, node_type&&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -45,11 +45,18 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<int> m;
   test(m);
   std::multiset<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
index d57e14f4b59c8..e7ae071487b06 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -13,14 +13,14 @@
 // <set>
 
 // template<container-compatible-range<value_type> R>
-//   void insert_range(R&& rg); // C++23
+//   void insert_range(R&& rg); // C++23, constexpr since C++26
 
 #include <set>
 
 #include "../../insert_range_maps_sets.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
     test_map_set_insert_range<std::multiset<int, test_less<int>, Alloc>, int, Iter, Sent>(/*allow_duplicates=*/true);
   });
@@ -32,5 +32,12 @@ int main(int, char**) {
   test_set_insert_range_exception_safety_throwing_copy<std::multiset>();
   test_assoc_set_insert_range_exception_safety_throwing_allocator<std::multiset, int>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
index 88155572f1680..2e8de11823690 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(value_type&& v);
+// iterator insert(value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> M;
     typedef M::iterator R;
@@ -71,5 +71,12 @@ int main(int, char**) {
     assert(*r == 3);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
index fee8bf9d3eb30..9b510af6deefc 100644
--- a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
@@ -10,20 +10,20 @@
 
 // class multiset
 
-//       iterator begin();
-// const_iterator begin() const;
-//       iterator end();
-// const_iterator end()   const;
+//       iterator begin(); // constexpr since C++26
+// const_iterator begin() const; // constexpr since C++26
+//       iterator end(); // constexpr since C++26
+// const_iterator end()   const; // constexpr since C++26
 //
-//       reverse_iterator rbegin();
-// const_reverse_iterator rbegin() const;
-//       reverse_iterator rend();
-// const_reverse_iterator rend()   const;
+//       reverse_iterator rbegin(); // constexpr since C++26
+// const_reverse_iterator rbegin() const; // constexpr since C++26
+//       reverse_iterator rend(); // constexpr since C++26
+// const_reverse_iterator rend()   const; // constexpr since C++26
 //
-// const_iterator         cbegin()  const;
-// const_iterator         cend()    const;
-// const_reverse_iterator crbegin() const;
-// const_reverse_iterator crend()   const;
+// const_iterator         cbegin()  const; // constexpr since C++26
+// const_iterator         cend()    const; // constexpr since C++26
+// const_reverse_iterator crbegin() const; // constexpr since C++26
+// const_reverse_iterator crend()   const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -32,7 +32,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8};
@@ -137,5 +137,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
index ddd39d8fc0348..4f88cec396700 100644
--- a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-//       iterator lower_bound(const key_type& k);
-// const_iterator lower_bound(const key_type& k) const;
+//       iterator lower_bound(const key_type& k); // constexpr since C++26
+// const_iterator lower_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -165,5 +165,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
index a6cff58b090bd..5df2970223608 100644
--- a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type max_size() const;
+// size_type max_size() const; // constexpr since C++26
 
 #include <cassert>
 #include <limits>
@@ -20,7 +20,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef limited_allocator<int, 10> A;
     typedef std::multiset<int, std::less<int>, A> C;
@@ -44,5 +44,12 @@ int main(int, char**) {
     assert(c.max_size() <= alloc_max_size(c.get_allocator()));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
index 90eaeb2adbfcb..9962fa55bb199 100644
--- a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
@@ -13,13 +13,13 @@
 // class multiset
 
 // template <class C2>
-//   void merge(set<Key, C2, Allocator>& source);
+//   void merge(set<Key, C2, Allocator>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(set<Key, C2, Allocator>&& source);
+//   void merge(set<Key, C2, Allocator>&& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<Key, C2, Allocator>& source);
+//   void merge(multiset<Key, C2, Allocator>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<Key, C2, Allocator>&& source);
+//   void merge(multiset<Key, C2, Allocator>&& source); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -46,7 +46,7 @@ struct throw_comparator {
 };
 #endif
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> src{1, 3, 5};
     std::multiset<int> dst{2, 4, 5};
@@ -134,5 +134,12 @@ int main(int, char**) {
       first.merge(std::move(second));
     }
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
index 1f05201d706b1..d854ef97fce0e 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const allocator_type& a);
+// multiset(const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::less<int> C;
   typedef test_allocator<int> A;
   std::multiset<int, C, A> m(A(5));
@@ -26,5 +26,12 @@ int main(int, char**) {
   assert(m.begin() == m.end());
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
index 7234e61d28bfa..096202b3de35a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset& operator=(initializer_list<value_type> il);
+// multiset& operator=(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> C;
     typedef C::value_type V;
@@ -52,5 +52,12 @@ int main(int, char**) {
     assert(*++i == V(6));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
index b7da7f66b020d..07066563cc459 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// explicit multiset(const key_compare& comp);
+// explicit multiset(const key_compare& comp); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   const std::multiset<int, C> m(C(3));
   assert(m.empty());
@@ -26,5 +26,12 @@ int main(int, char**) {
   assert(m.key_comp() == C(3));
   assert(m.value_comp() == C(3));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
index be8a451fdc405..88e12d3d93f82 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const value_compare& comp, const allocator_type& a);
+// multiset(const value_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   typedef test_allocator<int> A;
   std::multiset<int, C, A> m(C(4), A(5));
@@ -28,5 +28,12 @@ int main(int, char**) {
   assert(m.key_comp() == C(4));
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
index c747828aa01cc..b9be5da01f053 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const multiset& m);
+// multiset(const multiset& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -129,8 +129,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
index 868072ef15288..d6bfd5ca78b22 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const multiset& m, const allocator_type& a);
+// multiset(const multiset& m, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -100,8 +100,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
index 0a5fe62eed69e..05c709e45af8d 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset& operator=(const multiset& s);
+// multiset& operator=(const multiset& s); // constexpr since C++26
 
 #include <algorithm>
 #include <cassert>
@@ -275,8 +275,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
index f9797ca571c47..e25c08e7a81e6 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
@@ -52,7 +52,7 @@ struct NotAnAllocator {
   friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     const int arr[] = {1, 2, 1, INT_MAX, 3};
     std::multiset s(std::begin(arr), std::end(arr));
@@ -212,5 +212,12 @@ int main(int, char**) {
 
   AssociativeContainerDeductionGuidesSfinaeAway<std::multiset, std::multiset<int>>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
index 4f7dabfea2c68..cd2e63d171e21 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset();
+// multiset(); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> m;
     assert(m.empty());
@@ -51,5 +51,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
index 052b032d6e798..58fcb5fb5d337 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
@@ -12,7 +12,7 @@
 //    noexcept(
 //        is_nothrow_default_constructible<allocator_type>::value &&
 //        is_nothrow_default_constructible<key_compare>::value &&
-//        is_nothrow_copy_constructible<key_compare>::value);
+//        is_nothrow_copy_constructible<key_compare>::value); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -32,7 +32,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::multiset<MoveOnly> C;
@@ -52,5 +52,12 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_default_constructible<C>::value, "");
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
index bf5d256e19a0f..cf0d87f879dde 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
@@ -8,7 +8,7 @@
 
 // <set>
 
-// ~multiset() // implied noexcept;
+// ~multiset() // implied noexcept; // constexpr since C++26
 
 // UNSUPPORTED: c++03
 
@@ -27,7 +27,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> C;
     static_assert(std::is_nothrow_destructible<C>::value, "");
@@ -47,5 +47,12 @@ int main(int, char**) {
   }
 #endif // _LIBCPP_VERSION
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
index 15275781a8aac..1b4af3e503bbe 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
@@ -9,11 +9,11 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 // template<container-compatible-range<value_type> R>
-//   multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
+//   multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23, constexpr since C++26
 //
 // template<container-compatible-range<value_type> R>
 //   multiset(from_range_t, R&& rg, const Allocator& a))
-//     : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+//     : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23, constexpr since C++26
 
 #include <algorithm>
 #include <array>
@@ -28,7 +28,7 @@ void test_duplicates() {
   assert(std::ranges::is_permutation(input, c));
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
     test_associative_set<std::multiset, int, Iter, Sent, test_less<int>, Alloc>();
   });
@@ -40,5 +40,12 @@ int main(int, char**) {
   test_set_exception_safety_throwing_copy<std::multiset>();
   test_set_exception_safety_throwing_allocator<std::multiset, int>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
index c860f17bb4196..5369116cb44b9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> C;
     typedef C::value_type V;
@@ -66,5 +66,12 @@ int main(int, char**) {
     assert(m.get_allocator() == a);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
index 5bb400cb82be9..cd2da63ac9c1d 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> Cmp;
   typedef std::multiset<int, Cmp> C;
   typedef C::value_type V;
@@ -37,5 +37,12 @@ int main(int, char**) {
   assert(*++i == V(6));
   assert(m.key_comp() == Cmp(10));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
index a12e68c1e582b..900d0a4546d1a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a);
+// multiset(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> Cmp;
   typedef test_allocator<int> A;
   typedef std::multiset<int, Cmp, A> C;
@@ -40,5 +40,12 @@ int main(int, char**) {
   assert(m.key_comp() == Cmp(10));
   assert(m.get_allocator() == A(4));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
index 6b98ceb1c0df7..192f2c4eacc65 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
@@ -11,7 +11,7 @@
 // class multiset
 
 // template <class InputIterator>
-//     multiset(InputIterator first, InputIterator last);
+//     multiset(InputIterator first, InputIterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_iterators.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -58,5 +58,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
index 50f8fda2a676b..f8db804e4c80a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // template <class InputIterator>
 //     multiset(InputIterator first, InputIterator last,
-//         const value_compare& comp, const allocator_type& a);
+//         const value_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -72,5 +72,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
index bed8d02f3e681..9e80b83b244d5 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
@@ -11,7 +11,7 @@
 // class multiset
 
 // template <class InputIterator>
-//     multiset(InputIterator first, InputIterator last, const value_compare& comp);
+//     multiset(InputIterator first, InputIterator last, const value_compare& comp); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -21,7 +21,7 @@
 #include "test_iterators.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
   typedef test_less<V> C;
@@ -40,5 +40,12 @@ int main(int, char**) {
   assert(*std::next(m.begin(), 7) == 3);
   assert(*std::next(m.begin(), 8) == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
index 2d71f21e182c9..70d9eb2307827 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(multiset&& s);
+// multiset(multiset&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef test_less<int> C;
@@ -94,5 +94,12 @@ int main(int, char**) {
     assert(std::distance(mo.begin(), mo.end()) == 0);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
index 771a7038bf633..8cd8b7727917a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(multiset&& s, const allocator_type& a);
+// multiset(multiset&& s, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -24,7 +24,7 @@
 #include "test_allocator.h"
 #include "Counter.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -111,5 +111,12 @@ int main(int, char**) {
     assert(Counter_base::gConstructed == 0);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
index 5cdd4ec67b8e0..cb83f4386eff0 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset& operator=(multiset&& s);
+// multiset& operator=(multiset&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -93,5 +93,12 @@ int main(int, char**) {
     assert(m1.empty());
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
index 73b4be5005e1c..89e4f4ab1d2ae 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
@@ -10,7 +10,7 @@
 
 // multiset(multiset&&)
 //        noexcept(is_nothrow_move_constructible<allocator_type>::value &&
-//                 is_nothrow_move_constructible<key_compare>::value);
+//                 is_nothrow_move_constructible<key_compare>::value); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -30,7 +30,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::multiset<MoveOnly> C;
@@ -50,5 +50,12 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_move_constructible<C>::value, "");
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
index cfb304ee3be39..cf132ddde4717 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
@@ -11,7 +11,7 @@
 
 // template <class T, class Compare, class Allocator, class Predicate>
 //   typename multiset<T, Compare, Allocator>::size_type
-//   erase_if(multiset<T, Compare, Allocator>& c, Predicate pred);
+//   erase_if(multiset<T, Compare, Allocator>& c, Predicate pred); // constexpr since C++26
 
 #include <set>
 
@@ -64,7 +64,7 @@ void test() {
   test0(S({1, 2, 3}), False, S({1, 2, 3}), 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test<std::multiset<int>>();
   test<std::multiset<int, std::less<int>, min_allocator<int>>>();
   test<std::multiset<int, std::less<int>, test_allocator<int>>>();
@@ -72,5 +72,12 @@ int main(int, char**) {
   test<std::multiset<long>>();
   test<std::multiset<double>>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
index fb575c880dd2e..c146a3da3e272 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
@@ -13,15 +13,22 @@
 
 // template<class Key, class Compare, class Allocator>
 //   synth-three-way-result<Key> operator<=>(const multiset<Key, Compare, Allocator>& x,
-//                                           const multiset<Key, Compare, Allocator>& y);
+//                                           const multiset<Key, Compare, Allocator>& y); // constexpr since C++26
 
 #include <cassert>
 #include <set>
 
 #include "test_container_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   assert(test_ordered_set_container_spaceship<std::multiset>());
   // `std::multiset` is not constexpr, so no `static_assert` test here.
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
index 9ffb5dc3593c5..b78fe1d518d95 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
@@ -38,7 +38,7 @@
 
 #include "test_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> s1, s2;
     s1.insert(1);
@@ -70,5 +70,12 @@ int main(int, char**) {
     const std::multiset<int>&cs1 = s1, cs2 = s2;
     assert(testComparisons(cs1, cs2, false, true));
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
index 63647a052c041..e4754ceea5e25 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
@@ -8,13 +8,13 @@
 
 // <set>
 
-// key_compare key_comp() const;
-// value_compare value_comp() const;
+// key_compare key_comp() const; // constexpr since C++26
+// value_compare value_comp() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::multiset<int> set_type;
 
   set_type s;
@@ -29,5 +29,12 @@ int main(int, char**) {
   assert(cs.value_comp()(*i1, *i2));
   assert(!cs.value_comp()(*i2, *i1));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
index af32e1882093d..7ddeedde1a2ab 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// void swap(multiset& m);
+// void swap(multiset& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -110,5 +110,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
index 53630b3ca0a01..5e08875e08119 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// void swap(multiset& m);
+// void swap(multiset& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   {
     typedef std::multiset<int> M;
@@ -100,5 +100,12 @@ int main(int, char**) {
     assert(m2.get_allocator() == A(1));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
index 7a7ccf87e9de9..047cb03264317 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
@@ -12,11 +12,11 @@
 
 // void swap(multiset& c)
 //     noexcept(!allocator_type::propagate_on_container_swap::value ||
-//              __is_nothrow_swappable<allocator_type>::value);
+//              __is_nothrow_swappable<allocator_type>::value); // constexpr since C++26
 //
 //  In C++17, the standard says that swap shall have:
 //     noexcept(allocator_traits<Allocator>::is_always_equal::value &&
-//              noexcept(swap(declval<Compare&>(), declval<Compare&>())));
+//              noexcept(swap(declval<Compare&>(), declval<Compare&>()))); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -86,7 +86,7 @@ struct some_alloc3 {
   typedef std::false_type is_always_equal;
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> C;
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
@@ -131,5 +131,12 @@ int main(int, char**) {
 #  endif // _LIBCPP_VERSION
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/size.pass.cpp b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
index 405437211b8ca..626f50f57734a 100644
--- a/libcxx/test/std/containers/associative/multiset/size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type size() const;
+// size_type size() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     M m;
@@ -55,6 +55,12 @@ int main(int, char**) {
     assert(m.size() == 0);
   }
 #endif
+}
 
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp b/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp
index c61859b1feffb..4e580d4318a71 100644
--- a/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-//       iterator upper_bound(const key_type& k);
-// const_iterator upper_bound(const key_type& k) const;
+//       iterator upper_bound(const key_type& k); // constexpr since C++26
+// const_iterator upper_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -163,6 +163,13 @@ int main(int, char**) {
     assert(r == std::next(m.begin(), 9));
   }
 #endif
+  return true;
+}
 
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

>From fda9890ab7f494bcf82005dd3ad88faf8fb27a39 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 27 Jun 2026 15:41:58 +0100
Subject: [PATCH 16/21] libcxx/test/std/containers/associative/multiset/

---
 .../multiset/count_transparent.pass.cpp       | 10 ++++--
 .../associative/multiset/emplace.pass.cpp     |  6 ++--
 .../multiset/emplace_hint.pass.cpp            |  6 ++--
 .../multiset/equal_range_transparent.pass.cpp | 10 ++++--
 .../associative/multiset/erase_iter.pass.cpp  |  4 +--
 .../multiset/extract_iterator.pass.cpp        |  4 +--
 .../associative/multiset/extract_key.pass.cpp |  4 +--
 .../associative/multiset/insert_cv.pass.cpp   |  2 +-
 ...rt_emplace_allocator_requirements.pass.cpp |  9 +-----
 .../multiset/insert_iter_iter.pass.cpp        |  3 +-
 .../multiset/insert_node_type.pass.cpp        | 10 +++---
 .../multiset/insert_node_type_hint.pass.cpp   |  9 +++---
 .../multiset/insert_range.pass.cpp            |  1 +
 .../associative/multiset/merge.pass.cpp       | 15 +++++----
 .../multiset/multiset.cons/copy.pass.cpp      |  8 ++---
 .../multiset.cons/copy_alloc.pass.cpp         |  8 ++---
 .../multiset.cons/copy_assign.pass.cpp        | 31 +++++++++----------
 .../multiset/multiset.cons/deduct.pass.cpp    |  2 +-
 .../multiset.cons/from_range.pass.cpp         |  3 +-
 .../multiset.cons/move_alloc.pass.cpp         |  2 +-
 .../associative/multiset/size.pass.cpp        |  1 +
 21 files changed, 75 insertions(+), 73 deletions(-)

diff --git a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
index 33eea24eb0476..c21da4a98fce3 100644
--- a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
@@ -19,14 +19,18 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
index 051522f9a55f2..06a566b293c5a 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
@@ -24,7 +24,7 @@
 #include "min_allocator.h"
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
     M m;
@@ -41,7 +41,9 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 2);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    assert(DefaultOnly::count == 0);
+  }
   {
     typedef std::multiset<Emplaceable> M;
     typedef M::iterator R;
diff --git a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
index 7c77dda795a71..ed686e8a10b66 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
@@ -24,7 +24,7 @@
 #include "min_allocator.h"
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
     M m;
@@ -41,7 +41,9 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 2);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    assert(DefaultOnly::count == 0);
+  }
   {
     typedef std::multiset<Emplaceable> M;
     typedef M::iterator R;
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
index 6da1813338c3b..6fd1df482f673 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
@@ -23,14 +23,18 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
index 3cc984c619490..e2285e69ea07c 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
@@ -20,10 +20,10 @@
 
 struct TemplateConstructor {
   template <typename T>
-  TemplateConstructor(const T&) {}
+  TEST_CONSTEXPR_CXX26 TemplateConstructor(const T&) {}
 };
 
-bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
+TEST_CONSTEXPR_CXX26 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
 TEST_CONSTEXPR_CXX26 bool test() {
   {
diff --git a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
index f384b3ee95a67..7723e7ea7e113 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   std::size_t sz = c.size();
 
   for (auto first = c.cbegin(); first != c.cend();) {
@@ -42,7 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     test(m);
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::multiset<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     assert(Counter_base::gConstructed == 6);
     test(m);
diff --git a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
index 55896faa61ad3..c930cee804e1a 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container, class KeyTypeIter>
-void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
+TEST_CONSTEXPR_CXX26 void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   std::size_t sz = c.size();
   assert((std::size_t)std::distance(first, last) == sz);
 
@@ -61,7 +61,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(!res);
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::multiset<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     {
       Counter<int> keys[] = {1, 2, 3, 4, 5, 6};
diff --git a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
index 22540133feb6d..6d28aab5b0214 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 
 template <class Container>
-void do_insert_cv_test() {
+TEST_CONSTEXPR_CXX26 void do_insert_cv_test() {
   typedef Container M;
   typedef typename M::iterator R;
   typedef typename M::value_type VT;
diff --git a/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
index 8f3d61009410a..8085ac7cbb2f1 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
@@ -20,16 +20,9 @@
 #include "container_test_types.h"
 #include "../../set_allocator_requirement_test_templates.h"
 
-TEST_CONSTEXPR_CXX26 bool test() {
+int main(int, char**) {
   testMultisetInsert<TCT::multiset<> >();
   testMultisetEmplace<TCT::multiset<> >();
 
-  return true;
-}
-int main(int, char**) {
-  test();
-#if TEST_STD_VER >= 26
-  static_assert(test());
-#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
index f4d4007b08d09..858ca520a304a 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
@@ -184,9 +184,10 @@ TEST_CONSTEXPR_CXX26 void test_alloc() {
   }
 }
 
-TEST_CONSTEXPR_CXX26 void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<cpp17_input_iterator<int*>, std::allocator<int> >();
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
+  return true;
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
index 10f0d730a4444..70889de77c1e5 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
@@ -20,18 +20,18 @@
 #include "min_allocator.h"
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto it = c.insert(key);
   return c.extract(it);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
+  Container c2;
 
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c, i);
     assert(!node.empty());
     typename Container::iterator it = c.insert(std::move(node));
     assert(node.empty());
@@ -50,7 +50,7 @@ void test(Container& c) {
   }
 
   { // Insert duplicate node.
-    typename Container::node_type dupl = nf(0);
+    typename Container::node_type dupl = nf(c2, 0);
     auto it                            = c.insert(std::move(dupl));
     assert(*it == 0);
   }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
index 3224a73c9abbf..3610c96ae788a 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
@@ -19,18 +19,19 @@
 #include "min_allocator.h"
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto it = c.insert(key);
   return c.extract(it);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
 
+  Container c2;
+
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c2, i);
     assert(!node.empty());
     std::size_t prev = c.size();
     auto it          = c.insert(c.end(), std::move(node));
diff --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
index e7ae071487b06..f89c783d5f568 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -9,6 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
 // ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-missing-field-initializers
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2147483647
 
 // <set>
 
diff --git a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
index 9962fa55bb199..5ddfa8b4e0b29 100644
--- a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
@@ -27,7 +27,7 @@
 #include "Counter.h"
 
 template <class Set>
-bool set_equal(const Set& set, Set other) {
+TEST_CONSTEXPR_CXX26 bool set_equal(const Set& set, Set other) {
   return set == other;
 }
 
@@ -35,10 +35,10 @@ bool set_equal(const Set& set, Set other) {
 struct throw_comparator {
   bool& should_throw_;
 
-  throw_comparator(bool& should_throw) : should_throw_(should_throw) {}
+  TEST_CONSTEXPR_CXX26 throw_comparator(bool& should_throw) : should_throw_(should_throw) {}
 
   template <class T>
-  bool operator()(const T& lhs, const T& rhs) const {
+  TEST_CONSTEXPR_CXX26 bool operator()(const T& lhs, const T& rhs) const {
     if (should_throw_)
       throw 0;
     return lhs < rhs;
@@ -56,7 +56,8 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
-  {
+
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     bool do_throw = false;
     typedef std::multiset<Counter<int>, throw_comparator> set_type;
     set_type src({1, 3, 5}, throw_comparator(do_throw));
@@ -75,13 +76,15 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(set_equal(dst, set_type({2, 4, 5}, throw_comparator(do_throw))));
   }
 #endif
-  assert(Counter_base::gConstructed == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    assert(Counter_base::gConstructed == 0);
+  }
   struct comparator {
     comparator() = default;
 
     bool operator()(const Counter<int>& lhs, const Counter<int>& rhs) const { return lhs < rhs; }
   };
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::multiset<Counter<int>, std::less<Counter<int>>> first_set_type;
     typedef std::multiset<Counter<int>, comparator> second_set_type;
     typedef std::set<Counter<int>, comparator> third_set_type;
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
index b9be5da01f053..ff0951bdbe06f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 
 template <template <class> class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   { // Simple check
     using Set = std::multiset<int, std::less<int>, Alloc<int> >;
 
@@ -81,7 +81,7 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator>();
   test_alloc<min_allocator>(); // Make sure that fancy pointers work
 
@@ -127,10 +127,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.get_allocator() == other_allocator<int>(10));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
index d6bfd5ca78b22..26881348de9d1 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 
 template <class Alloc>
-void test_alloc(const Alloc& new_alloc) {
+TEST_CONSTEXPR_CXX26 void test_alloc(const Alloc& new_alloc) {
   { // Simple check
     using Set = std::multiset<int, std::less<int>, Alloc>;
 
@@ -82,7 +82,7 @@ void test_alloc(const Alloc& new_alloc) {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc(std::allocator<int>());
   test_alloc(test_allocator<int>(25)); // Make sure that the new allocator is actually used
   test_alloc(min_allocator<int>());    // Make sure that fancy pointers work
@@ -98,10 +98,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
index 05c709e45af8d..c21e8a37b9ae1 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
@@ -33,41 +33,42 @@ class tracking_allocator {
   using value_type                             = T;
   using propagate_on_container_copy_assignment = std::true_type;
 
-  tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
 
   template <class U>
-  tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
 
-  T* allocate(std::size_t n) {
+  TEST_CONSTEXPR_CXX26 T* allocate(std::size_t n) {
     T* allocation = std::allocator<T>().allocate(n);
     allocs_->push_back(allocation);
     return allocation;
   }
 
-  void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
+  TEST_CONSTEXPR_CXX26 void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
     auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
     assert(res != allocs_->end() && "Trying to deallocate memory from different allocator?");
     allocs_->erase(res);
     std::allocator<T>().deallocate(ptr, n);
   }
 
-  friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  friend TEST_CONSTEXPR_CXX26 bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ == rhs.allocs_;
   }
 
-  friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  friend TEST_CONSTEXPR_CXX26 bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ != rhs.allocs_;
   }
 };
 
 struct NoOp {
-  void operator()() {}
+  TEST_CONSTEXPR_CXX26 void operator()() {}
 };
 
 template <class Alloc, class AllocatorInvariant = NoOp>
-void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
-                const Alloc& rhs_alloc                   = Alloc(),
-                AllocatorInvariant check_alloc_invariant = NoOp()) {
+TEST_CONSTEXPR_CXX26 void
+test_alloc(const Alloc& lhs_alloc                   = Alloc(),
+           const Alloc& rhs_alloc                   = Alloc(),
+           AllocatorInvariant check_alloc_invariant = NoOp()) {
   {   // Test empty/non-empty multiset combinations
     { // assign from a non-empty container into an empty one
       using Set = std::multiset<int, std::less<int>, Alloc>;
@@ -234,7 +235,7 @@ void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator<int> >();
 #if TEST_STD_VER >= 11
   test_alloc<min_allocator<int> >();
@@ -246,10 +247,10 @@ void test() {
       std::vector<void*>* rhs_allocs_;
 
     public:
-      AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
+      TEST_CONSTEXPR_CXX26 AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
           : lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
 
-      void operator()() {
+      TEST_CONSTEXPR_CXX26 void operator()() {
         assert(lhs_allocs_->empty());
         assert(rhs_allocs_->empty());
       }
@@ -273,10 +274,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
index e25c08e7a81e6..e3fead570e958 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
@@ -49,7 +49,7 @@
 #include "test_allocator.h"
 
 struct NotAnAllocator {
-  friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
+  friend TEST_CONSTEXPR_CXX26 bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
index 1b4af3e503bbe..338769516782a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2000000
 
 // template<container-compatible-range<value_type> R>
 //   multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23, constexpr since C++26
@@ -22,7 +23,7 @@
 #include "../../from_range_associative_containers.h"
 #include "test_macros.h"
 
-void test_duplicates() {
+TEST_CONSTEXPR_CXX26 void test_duplicates() {
   std::array input = {1, 2, 3, 3, 3, 4, 2, 1, 2};
   auto c           = std::multiset<int>(std::from_range, input);
   assert(std::ranges::is_permutation(input, c));
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
index 8cd8b7727917a..eece56da5aff4 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
@@ -73,7 +73,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(m3.key_comp() == C(5));
     LIBCPP_ASSERT(m1.empty());
   }
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef Counter<int> V;
     typedef std::less<V> C;
     typedef test_allocator<V> A;
diff --git a/libcxx/test/std/containers/associative/multiset/size.pass.cpp b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
index 626f50f57734a..4fe326b013fd4 100644
--- a/libcxx/test/std/containers/associative/multiset/size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
@@ -55,6 +55,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(m.size() == 0);
   }
 #endif
+  return true;
 }
 
 int main(int, char**) {

>From dd8615210cc23bb45756b3ec50848c5b074e37dc Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Mon, 29 Jun 2026 23:12:14 +0100
Subject: [PATCH 17/21] fix pending tests

---
 .../alg.foreach/for_each.associative.pass.cpp         |  8 ++------
 .../alg.foreach/ranges.for_each.associative.pass.cpp  | 11 ++---------
 .../multiset.nonmember/compare.three_way.pass.cpp     |  1 -
 3 files changed, 4 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
index 645e1e4af792e..7b0e5b93dc618 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
@@ -71,12 +71,8 @@ TEST_CONSTEXPR_CXX26 void test_node_container(Converter conv) {
 }
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  // FIXME: remove when set is made constexpr
-  if (!TEST_IS_CONSTANT_EVALUATED)
-    test_node_container<std::set<int> >([](int i) { return i; });
-  // FIXME: remove when multiset is made constexpr
-  if (!TEST_IS_CONSTANT_EVALUATED)
-    test_node_container<std::multiset<int> >([](int i) { return i; });
+  test_node_container<std::set<int> >([](int i) { return i; });
+  test_node_container<std::multiset<int> >([](int i) { return i; });
   test_node_container<std::map<int, int> >([](int i) { return std::make_pair(i, i); });
   test_node_container<std::multimap<int, int> >([](int i) { return std::make_pair(i, i); });
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
index 02698819d71df..72aa40a7f7e0a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
@@ -252,20 +252,13 @@ TEST_CONSTEXPR_CXX26 void test_invoke_map_like() {
 }
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when multiset is made constexpr
-    test_node_container<std::multiset<int> >([](int i) { return i; });
-  }
   test_node_container<std::set<int> >([](int i) { return i; });
+  test_node_container<std::multiset<int> >([](int i) { return i; });
   test_node_container<std::map<int, int> >([](int i) { return std::make_pair(i, i); });
   test_node_container<std::multimap<int, int> >([](int i) { return std::make_pair(i, i); });
 
-  if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when multiset is made constexpr
-    test_invoke_set_like<std::multiset>();
-  }
-
   test_invoke_set_like<std::set>();
+  test_invoke_set_like<std::multiset>();
   test_invoke_map_like<std::map>();
   test_invoke_map_like<std::multimap>();
 
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
index c146a3da3e272..9eaedb730745f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
@@ -22,7 +22,6 @@
 
 TEST_CONSTEXPR_CXX26 bool test() {
   assert(test_ordered_set_container_spaceship<std::multiset>());
-  // `std::multiset` is not constexpr, so no `static_assert` test here.
   return true;
 }
 int main(int, char**) {

>From 83f1ad1ea88dee3d78d35a3aa443c0712f037812 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Wed, 1 Jul 2026 21:23:50 +0100
Subject: [PATCH 18/21] missing include

---
 .../associative/multiset/multiset.observers/comp.pass.cpp       | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
index e4754ceea5e25..1bafc70e87af5 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
@@ -14,6 +14,8 @@
 #include <set>
 #include <cassert>
 
+#include "test_macros.h"
+
 TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::multiset<int> set_type;
 

>From 4c16e99618539bf2a1471d3e8a23a861a91f6ec6 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Wed, 1 Jul 2026 22:13:05 +0100
Subject: [PATCH 19/21] add \n

---
 libcxx/test/std/containers/associative/multiset/clear.pass.cpp   | 1 +
 libcxx/test/std/containers/associative/multiset/count.pass.cpp   | 1 +
 .../containers/associative/multiset/count_transparent.pass.cpp   | 1 +
 libcxx/test/std/containers/associative/multiset/emplace.pass.cpp | 1 +
 .../std/containers/associative/multiset/emplace_hint.pass.cpp    | 1 +
 libcxx/test/std/containers/associative/multiset/empty.pass.cpp   | 1 +
 .../std/containers/associative/multiset/equal_range.pass.cpp     | 1 +
 .../associative/multiset/equal_range_transparent.pass.cpp        | 1 +
 .../test/std/containers/associative/multiset/erase_iter.pass.cpp | 1 +
 .../std/containers/associative/multiset/erase_iter_iter.pass.cpp | 1 +
 .../test/std/containers/associative/multiset/erase_key.pass.cpp  | 1 +
 .../containers/associative/multiset/extract_iterator.pass.cpp    | 1 +
 .../std/containers/associative/multiset/extract_key.pass.cpp     | 1 +
 libcxx/test/std/containers/associative/multiset/find.pass.cpp    | 1 +
 .../std/containers/associative/multiset/get_allocator.pass.cpp   | 1 +
 .../std/containers/associative/multiset/incomplete_type.pass.cpp | 1 +
 .../test/std/containers/associative/multiset/insert_cv.pass.cpp  | 1 +
 .../associative/multiset/insert_initializer_list.pass.cpp        | 1 +
 .../std/containers/associative/multiset/insert_iter_cv.pass.cpp  | 1 +
 .../std/containers/associative/multiset/insert_iter_rv.pass.cpp  | 1 +
 .../containers/associative/multiset/insert_node_type.pass.cpp    | 1 +
 .../associative/multiset/insert_node_type_hint.pass.cpp          | 1 +
 .../std/containers/associative/multiset/insert_range.pass.cpp    | 1 +
 .../test/std/containers/associative/multiset/insert_rv.pass.cpp  | 1 +
 .../test/std/containers/associative/multiset/iterator.pass.cpp   | 1 +
 .../std/containers/associative/multiset/lower_bound.pass.cpp     | 1 +
 .../test/std/containers/associative/multiset/max_size.pass.cpp   | 1 +
 libcxx/test/std/containers/associative/multiset/merge.pass.cpp   | 1 +
 .../containers/associative/multiset/multiset.cons/alloc.pass.cpp | 1 +
 .../multiset/multiset.cons/assign_initializer_list.pass.cpp      | 1 +
 .../associative/multiset/multiset.cons/compare.pass.cpp          | 1 +
 .../associative/multiset/multiset.cons/compare_alloc.pass.cpp    | 1 +
 .../containers/associative/multiset/multiset.cons/copy.pass.cpp  | 1 +
 .../associative/multiset/multiset.cons/copy_alloc.pass.cpp       | 1 +
 .../associative/multiset/multiset.cons/copy_assign.pass.cpp      | 1 +
 .../associative/multiset/multiset.cons/deduct.pass.cpp           | 1 +
 .../associative/multiset/multiset.cons/default.pass.cpp          | 1 +
 .../associative/multiset/multiset.cons/default_noexcept.pass.cpp | 1 +
 .../associative/multiset/multiset.cons/dtor_noexcept.pass.cpp    | 1 +
 .../associative/multiset/multiset.cons/from_range.pass.cpp       | 1 +
 .../associative/multiset/multiset.cons/initializer_list.pass.cpp | 1 +
 .../multiset/multiset.cons/initializer_list_compare.pass.cpp     | 1 +
 .../multiset.cons/initializer_list_compare_alloc.pass.cpp        | 1 +
 .../associative/multiset/multiset.cons/iter_iter.pass.cpp        | 1 +
 .../associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp  | 1 +
 .../associative/multiset/multiset.cons/iter_iter_comp.pass.cpp   | 1 +
 .../containers/associative/multiset/multiset.cons/move.pass.cpp  | 1 +
 .../associative/multiset/multiset.cons/move_alloc.pass.cpp       | 1 +
 .../associative/multiset/multiset.cons/move_assign.pass.cpp      | 1 +
 .../associative/multiset/multiset.cons/move_noexcept.pass.cpp    | 1 +
 .../associative/multiset/multiset.erasure/erase_if.pass.cpp      | 1 +
 .../multiset/multiset.nonmember/compare.three_way.pass.cpp       | 1 +
 .../associative/multiset/multiset.nonmember/op_compare.pass.cpp  | 1 +
 .../associative/multiset/multiset.observers/comp.pass.cpp        | 1 +
 .../associative/multiset/multiset.special/member_swap.pass.cpp   | 1 +
 .../multiset/multiset.special/non_member_swap.pass.cpp           | 1 +
 .../associative/multiset/multiset.special/swap_noexcept.pass.cpp | 1 +
 57 files changed, 57 insertions(+)

diff --git a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
index 1c9d7798b81bf..b8110efe4a4ff 100644
--- a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
@@ -44,6 +44,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/count.pass.cpp b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
index ad7b9be77c36a..6bfcdc66aada9 100644
--- a/libcxx/test/std/containers/associative/multiset/count.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
@@ -126,6 +126,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
index c21da4a98fce3..815260f67c231 100644
--- a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
@@ -41,6 +41,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
index 06a566b293c5a..193926b6dfc87 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
@@ -88,6 +88,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
index ed686e8a10b66..5650b27d19a05 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
@@ -82,6 +82,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
index d730d69dc4e09..08027484fadb6 100644
--- a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
@@ -42,6 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
index 03d469c9c5b0a..7d623036c8e34 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
@@ -207,6 +207,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
index 6fd1df482f673..385b05c8f26b1 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
@@ -52,6 +52,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
index e2285e69ea07c..a7da30fcd2a96 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
@@ -181,6 +181,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
index 6a6b2831fb515..942b076c702ea 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
@@ -120,6 +120,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
index 3503e3de4ffb7..c6fe8f0563370 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
@@ -106,6 +106,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
index 7723e7ea7e113..c07a0d246fcf7 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
@@ -57,6 +57,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
index c930cee804e1a..52adc8ade796c 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
@@ -80,6 +80,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/find.pass.cpp b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
index b7cb95a5d1a2b..bb5be9a28502b 100644
--- a/libcxx/test/std/containers/associative/multiset/find.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
@@ -204,6 +204,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
index f07d83e2fa18d..3def31b6e0c84 100644
--- a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
@@ -31,6 +31,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
index 3313ab39c0934..63fd68a747c32 100644
--- a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
@@ -30,6 +30,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
index 6d28aab5b0214..556f29496dda2 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
@@ -59,6 +59,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
index 7a25a2fdc96c1..b561b5864c771 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
@@ -59,6 +59,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
index e271b078ab317..eb63f2f838891 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
@@ -72,6 +72,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
index 99bcba0ed97f8..874e560f6a533 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
@@ -73,6 +73,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
index 70889de77c1e5..4a20c2792c76e 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
@@ -71,6 +71,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
index 3610c96ae788a..b5c2aae45f01f 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
@@ -54,6 +54,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
index f89c783d5f568..590af11578f22 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -35,6 +35,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
index 2e8de11823690..387a99c042f7d 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
@@ -73,6 +73,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
index 9b510af6deefc..542ba871960ee 100644
--- a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
@@ -139,6 +139,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
index 4f88cec396700..c2883e039c8a9 100644
--- a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
@@ -167,6 +167,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
index 5df2970223608..b7b6d28d002ac 100644
--- a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
@@ -46,6 +46,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
index 5ddfa8b4e0b29..0397f16c52d41 100644
--- a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
@@ -139,6 +139,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
index d854ef97fce0e..fafcf50fa15ad 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
@@ -28,6 +28,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
index 096202b3de35a..914c07ed9d6f7 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
@@ -54,6 +54,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
index 07066563cc459..635b5999dbcd7 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
@@ -28,6 +28,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
index 88e12d3d93f82..e8138c0501263 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
@@ -30,6 +30,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
index ff0951bdbe06f..7d56ea93a9a45 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
@@ -130,6 +130,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
index 26881348de9d1..f79ae3713540b 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
@@ -101,6 +101,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
index c21e8a37b9ae1..86b86f92f3338 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
@@ -277,6 +277,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
index e3fead570e958..7a2cc45ae9cdc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
@@ -214,6 +214,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
index cd2e63d171e21..256b6f41dc3db 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
@@ -53,6 +53,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
index 58fcb5fb5d337..532a98066e51a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
@@ -54,6 +54,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
index cf0d87f879dde..2f81f954c8757 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
@@ -49,6 +49,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
index 338769516782a..ca1d7c20869b9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
@@ -43,6 +43,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
index 5369116cb44b9..bde073eeee9d7 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
@@ -68,6 +68,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
index cd2da63ac9c1d..f144c05352f09 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
@@ -39,6 +39,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
index 900d0a4546d1a..63d8199ea434e 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
@@ -42,6 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
index 192f2c4eacc65..bb41b35870444 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
@@ -60,6 +60,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
index f8db804e4c80a..22ac9bd799e45 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
@@ -74,6 +74,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
index 9e80b83b244d5..353f3bc84ad9f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
@@ -42,6 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
index 70d9eb2307827..a85823e464cbc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
@@ -96,6 +96,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
index eece56da5aff4..a3eaa419fbbab 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
@@ -113,6 +113,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
index cb83f4386eff0..60dc3a5bbc62f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
@@ -95,6 +95,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
index 89e4f4ab1d2ae..52519aaceab3f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
@@ -52,6 +52,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
index cf132ddde4717..147211766e693 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
@@ -74,6 +74,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
index 9eaedb730745f..ae3581104677c 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
@@ -24,6 +24,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
   assert(test_ordered_set_container_spaceship<std::multiset>());
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
index b78fe1d518d95..1d5df25bebfdc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
@@ -72,6 +72,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
index 1bafc70e87af5..1908991e40eed 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
@@ -33,6 +33,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
index 7ddeedde1a2ab..997fe1d818b53 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
@@ -112,6 +112,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
index 5e08875e08119..3ab2e1fbf55ce 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
@@ -102,6 +102,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
index 047cb03264317..37bdae66ee8e9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
@@ -133,6 +133,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26

>From ee60174486b206f0c625d988f4753b0532d270ce Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Wed, 1 Jul 2026 23:23:37 +0100
Subject: [PATCH 20/21] macro

---
 .../associative/multiset/multiset.erasure/erase_if.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
index 147211766e693..e0df9210c7da9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
@@ -20,14 +20,14 @@
 #include "min_allocator.h"
 
 template <class S, class Pred>
-void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
+TEST_CONSTEXPR_CXX26 void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
   ASSERT_SAME_TYPE(typename S::size_type, decltype(std::erase_if(s, p)));
   assert(expected_erased_count == std::erase_if(s, p));
   assert(s == expected);
 }
 
 template <typename S>
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   auto is1   = [](auto v) { return v == 1; };
   auto is2   = [](auto v) { return v == 2; };
   auto is3   = [](auto v) { return v == 3; };

>From dbe235f80d8d00bf17e01955df3dd42ee9b83566 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Thu, 2 Jul 2026 08:35:35 +0100
Subject: [PATCH 21/21] trigger build



From libcxx-commits at lists.llvm.org  Fri Jul  3 01:03:44 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 03 Jul 2026 01:03:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Use C++11 spelling of
	[[noreturn]] (PR #207344)
Message-ID: <llvm.org/llvm/llvm-project/pull/207344@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207344

We already use the C++11 spelling for libc++, and there isn't much of a reason not to use it. Except for the changes in `cxxabi.h` this is even fully standard code.


>From 1751d0bdf4ac60bf42f341026de9b4d274d0326c Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 10:02:01 +0200
Subject: [PATCH] [libc++abi][NFC] Use C++11 spelling of [[noreturn]]

---
 libcxxabi/include/cxxabi.h             | 17 ++++++++---------
 libcxxabi/src/abort_message.h          |  2 +-
 libcxxabi/src/cxa_aux_runtime.cpp      |  6 +++---
 libcxxabi/src/cxa_default_handlers.cpp |  9 +++------
 libcxxabi/src/cxa_exception.cpp        |  2 +-
 libcxxabi/src/cxa_handlers.cpp         |  6 ++----
 libcxxabi/src/cxa_handlers.h           |  4 ++--
 libcxxabi/src/cxa_personality.cpp      |  3 +--
 libcxxabi/src/cxa_vector.cpp           |  3 +--
 libcxxabi/src/cxa_virtual.cpp          |  4 ++--
 10 files changed, 24 insertions(+), 32 deletions(-)

diff --git a/libcxxabi/include/cxxabi.h b/libcxxabi/include/cxxabi.h
index ff0d5d2aef47c..c65e353d68daf 100644
--- a/libcxxabi/include/cxxabi.h
+++ b/libcxxabi/include/cxxabi.h
@@ -20,7 +20,6 @@
 #include <__cxxabi_config.h>
 
 #define _LIBCPPABI_VERSION 230000
-#define _LIBCXXABI_NORETURN  __attribute__((noreturn))
 #define _LIBCXXABI_ALWAYS_COLD __attribute__((cold))
 
 #ifdef __cplusplus
@@ -56,7 +55,7 @@ __cxa_init_primary_exception(void* object, std::type_info* tinfo, void(_LIBCXXAB
 #endif
 
 // 2.4.3 Throwing the Exception Object
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void
 __cxa_throw(void *thrown_exception, std::type_info *tinfo,
 #ifdef __wasm__
             void *(_LIBCXXABI_DTOR_FUNC *dest)(void *));
@@ -80,22 +79,22 @@ extern _LIBCXXABI_FUNC_VIS std::type_info *__cxa_current_exception_type();
 // GNU extension
 // Calls `terminate` with the current exception being caught. This function is used by GCC when a `noexcept` function
 // throws an exception inside a try/catch block and doesn't catch it.
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_call_terminate(void*) _LIBCXXABI_NOEXCEPT;
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_call_terminate(void*) _LIBCXXABI_NOEXCEPT;
 
 // 2.5.4 Rethrowing Exceptions
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_rethrow();
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_rethrow();
 
 // 2.6 Auxiliary Runtime APIs
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void);
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void);
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_bad_cast(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_bad_typeid(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void
 __cxa_throw_bad_array_new_length(void);
 
 // 3.2.6 Pure Virtual Function API
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_pure_virtual(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_pure_virtual(void);
 
 // 3.2.7 Deleted Virtual Function API
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_deleted_virtual(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_deleted_virtual(void);
 
 // 3.3.2 One-time Construction API
 #if defined(_LIBCXXABI_GUARD_ABI_ARM)
diff --git a/libcxxabi/src/abort_message.h b/libcxxabi/src/abort_message.h
index 2c12c429569d3..885dd38372791 100644
--- a/libcxxabi/src/abort_message.h
+++ b/libcxxabi/src/abort_message.h
@@ -11,7 +11,7 @@
 
 #include "cxxabi.h"
 
-extern "C" _LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN void
+extern "C" [[noreturn]] _LIBCXXABI_HIDDEN void
 __abort_message(const char *format, ...) __attribute__((format(printf, 1, 2)));
 
 #ifndef _LIBCXXABI_ASSERT
diff --git a/libcxxabi/src/cxa_aux_runtime.cpp b/libcxxabi/src/cxa_aux_runtime.cpp
index 5e6040d75a262..e6bac221d8be8 100644
--- a/libcxxabi/src/cxa_aux_runtime.cpp
+++ b/libcxxabi/src/cxa_aux_runtime.cpp
@@ -16,7 +16,7 @@
 
 namespace __cxxabiv1 {
 extern "C" {
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void) {
+[[noreturn]] _LIBCXXABI_FUNC_VIS void __cxa_bad_cast(void) {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_cast();
 #else
@@ -24,7 +24,7 @@ _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void) {
 #endif
 }
 
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void) {
+[[noreturn]] _LIBCXXABI_FUNC_VIS void __cxa_bad_typeid(void) {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_typeid();
 #else
@@ -32,7 +32,7 @@ _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void) {
 #endif
 }
 
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void
+[[noreturn]] _LIBCXXABI_FUNC_VIS void
 __cxa_throw_bad_array_new_length(void) {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_array_new_length();
diff --git a/libcxxabi/src/cxa_default_handlers.cpp b/libcxxabi/src/cxa_default_handlers.cpp
index 092e66692ecb1..5daf78b34ee54 100644
--- a/libcxxabi/src/cxa_default_handlers.cpp
+++ b/libcxxabi/src/cxa_default_handlers.cpp
@@ -24,8 +24,7 @@
 static constinit const char* cause = "uncaught";
 
 #  ifndef _LIBCXXABI_NO_EXCEPTIONS
-__attribute__((noreturn))
-static void demangling_terminate_handler()
+[[noreturn]] static void demangling_terminate_handler()
 {
     using namespace __cxxabiv1;
     __cxa_eh_globals* globals = __cxa_get_globals_fast();
@@ -78,16 +77,14 @@ static void demangling_terminate_handler()
     }
 }
 #else // !_LIBCXXABI_NO_EXCEPTIONS
-__attribute__((noreturn))
-static void demangling_terminate_handler()
+[[noreturn]] static void demangling_terminate_handler()
 {
     (void)cause;
     __abort_message("terminating");
 }
 #endif // !_LIBCXXABI_NO_EXCEPTIONS
 
-__attribute__((noreturn))
-static void demangling_unexpected_handler()
+[[noreturn]] static void demangling_unexpected_handler()
 {
     cause = "unexpected";
     std::terminate();
diff --git a/libcxxabi/src/cxa_exception.cpp b/libcxxabi/src/cxa_exception.cpp
index 5d7edae697d67..f56ecc248eefb 100644
--- a/libcxxabi/src/cxa_exception.cpp
+++ b/libcxxabi/src/cxa_exception.cpp
@@ -140,7 +140,7 @@ exception_cleanup_func(_Unwind_Reason_Code reason, _Unwind_Exception* unwind_exc
     __cxa_decrement_exception_refcount(unwind_exception + 1);
 }
 
-static _LIBCXXABI_NORETURN void failed_throw(__cxa_exception* exception_header) {
+[[noreturn]] static void failed_throw(__cxa_exception* exception_header) {
 //  Section 2.5.3 says:
 //      * For purposes of this ABI, several things are considered exception handlers:
 //      ** A terminate() call due to a throw.
diff --git a/libcxxabi/src/cxa_handlers.cpp b/libcxxabi/src/cxa_handlers.cpp
index f879ff0d8ff18..0abdc740486b8 100644
--- a/libcxxabi/src/cxa_handlers.cpp
+++ b/libcxxabi/src/cxa_handlers.cpp
@@ -36,8 +36,7 @@ __unexpected(unexpected_handler func)
     __abort_message("unexpected_handler unexpectedly returned");
 }
 
-__attribute__((noreturn))
-void
+[[noreturn]] void
 unexpected()
 {
     __unexpected(get_unexpected());
@@ -69,8 +68,7 @@ __terminate(terminate_handler func) noexcept
 #endif // _LIBCXXABI_NO_EXCEPTIONS
 }
 
-__attribute__((noreturn))
-void
+[[noreturn]] void
 terminate() noexcept
 {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
diff --git a/libcxxabi/src/cxa_handlers.h b/libcxxabi/src/cxa_handlers.h
index 3d8dc6b2de95e..072b85ed89f85 100644
--- a/libcxxabi/src/cxa_handlers.h
+++ b/libcxxabi/src/cxa_handlers.h
@@ -19,11 +19,11 @@
 namespace std
 {
 
-_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN
+[[noreturn]] _LIBCXXABI_HIDDEN
 void
 __unexpected(unexpected_handler func);
 
-_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN
+[[noreturn]] _LIBCXXABI_HIDDEN
 void
 __terminate(terminate_handler func) noexcept;
 
diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index 6599f89740113..c5050e46c0e85 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1272,8 +1272,7 @@ __gxx_personality_v0(_Unwind_State state,
 #endif
 
 
-__attribute__((noreturn))
-_LIBCXXABI_FUNC_VIS void
+[[noreturn]] _LIBCXXABI_FUNC_VIS void
 __cxa_call_unexpected(void* arg)
 {
     _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(arg);
diff --git a/libcxxabi/src/cxa_vector.cpp b/libcxxabi/src/cxa_vector.cpp
index 857ee27d065c3..5eac7a8dc9729 100644
--- a/libcxxabi/src/cxa_vector.cpp
+++ b/libcxxabi/src/cxa_vector.cpp
@@ -116,8 +116,7 @@ namespace {
 //
 
 namespace {
-_LIBCXXABI_NORETURN
-void throw_bad_array_new_length() {
+[[noreturn]] void throw_bad_array_new_length() {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_array_new_length();
 #else
diff --git a/libcxxabi/src/cxa_virtual.cpp b/libcxxabi/src/cxa_virtual.cpp
index 8f4fdd0919f0e..1d3f4c63cf2de 100644
--- a/libcxxabi/src/cxa_virtual.cpp
+++ b/libcxxabi/src/cxa_virtual.cpp
@@ -11,12 +11,12 @@
 
 namespace __cxxabiv1 {
 extern "C" {
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN
+[[noreturn]] _LIBCXXABI_FUNC_VIS
 void __cxa_pure_virtual(void) {
   __abort_message("Pure virtual function called!");
 }
 
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN
+[[noreturn]] _LIBCXXABI_FUNC_VIS
 void __cxa_deleted_virtual(void) {
   __abort_message("Deleted virtual function called!");
 }


From libcxx-commits at lists.llvm.org  Fri Jul  3 01:05:16 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 01:05:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Use C++11 spelling of
	[[noreturn]] (PR #207344)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207344@github.com>
Message-ID: <6a476d3c.a87eb5dc.2cf3a1.678b@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions cpp,h -- libcxxabi/include/cxxabi.h libcxxabi/src/abort_message.h libcxxabi/src/cxa_aux_runtime.cpp libcxxabi/src/cxa_default_handlers.cpp libcxxabi/src/cxa_exception.cpp libcxxabi/src/cxa_handlers.cpp libcxxabi/src/cxa_handlers.h libcxxabi/src/cxa_personality.cpp libcxxabi/src/cxa_vector.cpp libcxxabi/src/cxa_virtual.cpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxxabi/include/cxxabi.h b/libcxxabi/include/cxxabi.h
index c65e353d6..5cb72f6d1 100644
--- a/libcxxabi/include/cxxabi.h
+++ b/libcxxabi/include/cxxabi.h
@@ -55,13 +55,12 @@ __cxa_init_primary_exception(void* object, std::type_info* tinfo, void(_LIBCXXAB
 #endif
 
 // 2.4.3 Throwing the Exception Object
-[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void
-__cxa_throw(void *thrown_exception, std::type_info *tinfo,
-#ifdef __wasm__
-            void *(_LIBCXXABI_DTOR_FUNC *dest)(void *));
-#else
-            void (_LIBCXXABI_DTOR_FUNC *dest)(void *));
-#endif
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_throw(void* thrown_exception, std::type_info* tinfo,
+#  ifdef __wasm__
+                                                             void*(_LIBCXXABI_DTOR_FUNC* dest)(void*));
+#  else
+                                                             void(_LIBCXXABI_DTOR_FUNC* dest)(void*));
+#  endif
 
 // 2.5.3 Exception Handlers
 extern _LIBCXXABI_FUNC_VIS void *
@@ -87,8 +86,7 @@ extern _LIBCXXABI_FUNC_VIS std::type_info *__cxa_current_exception_type();
 // 2.6 Auxiliary Runtime APIs
 [[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_bad_cast(void);
 [[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_bad_typeid(void);
-[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void
-__cxa_throw_bad_array_new_length(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_throw_bad_array_new_length(void);
 
 // 3.2.6 Pure Virtual Function API
 [[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_pure_virtual(void);
diff --git a/libcxxabi/src/abort_message.h b/libcxxabi/src/abort_message.h
index 885dd3837..f52ae0082 100644
--- a/libcxxabi/src/abort_message.h
+++ b/libcxxabi/src/abort_message.h
@@ -11,8 +11,8 @@
 
 #include "cxxabi.h"
 
-extern "C" [[noreturn]] _LIBCXXABI_HIDDEN void
-__abort_message(const char *format, ...) __attribute__((format(printf, 1, 2)));
+extern "C" [[noreturn]] _LIBCXXABI_HIDDEN void __abort_message(const char* format, ...)
+    __attribute__((format(printf, 1, 2)));
 
 #ifndef _LIBCXXABI_ASSERT
 #  define _LIBCXXABI_ASSERT(expr, msg)                                                                                 \
diff --git a/libcxxabi/src/cxa_aux_runtime.cpp b/libcxxabi/src/cxa_aux_runtime.cpp
index e6bac221d..92b8b4cb7 100644
--- a/libcxxabi/src/cxa_aux_runtime.cpp
+++ b/libcxxabi/src/cxa_aux_runtime.cpp
@@ -32,8 +32,7 @@ extern "C" {
 #endif
 }
 
-[[noreturn]] _LIBCXXABI_FUNC_VIS void
-__cxa_throw_bad_array_new_length(void) {
+[[noreturn]] _LIBCXXABI_FUNC_VIS void __cxa_throw_bad_array_new_length(void) {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_array_new_length();
 #else
diff --git a/libcxxabi/src/cxa_default_handlers.cpp b/libcxxabi/src/cxa_default_handlers.cpp
index 5daf78b34..9bfd79926 100644
--- a/libcxxabi/src/cxa_default_handlers.cpp
+++ b/libcxxabi/src/cxa_default_handlers.cpp
@@ -24,70 +24,60 @@
 static constinit const char* cause = "uncaught";
 
 #  ifndef _LIBCXXABI_NO_EXCEPTIONS
-[[noreturn]] static void demangling_terminate_handler()
-{
-    using namespace __cxxabiv1;
-    __cxa_eh_globals* globals = __cxa_get_globals_fast();
+[[noreturn]] static void demangling_terminate_handler() {
+  using namespace __cxxabiv1;
+  __cxa_eh_globals* globals = __cxa_get_globals_fast();
 
-    // If there is no uncaught exception, just note that we're terminating
-    if (!globals)
-        __abort_message("terminating");
+  // If there is no uncaught exception, just note that we're terminating
+  if (!globals)
+    __abort_message("terminating");
 
-    __cxa_exception* exception_header = globals->caughtExceptions;
-    if (!exception_header)
-        __abort_message("terminating");
+  __cxa_exception* exception_header = globals->caughtExceptions;
+  if (!exception_header)
+    __abort_message("terminating");
 
-    _Unwind_Exception* unwind_exception =
-        reinterpret_cast<_Unwind_Exception*>(exception_header + 1) - 1;
+  _Unwind_Exception* unwind_exception = reinterpret_cast<_Unwind_Exception*>(exception_header + 1) - 1;
 
-    // If we're terminating due to a foreign exception
-    if (!__isOurExceptionClass(unwind_exception))
-        __abort_message("terminating due to %s foreign exception", cause);
+  // If we're terminating due to a foreign exception
+  if (!__isOurExceptionClass(unwind_exception))
+    __abort_message("terminating due to %s foreign exception", cause);
 
-    void* thrown_object =
-        __getExceptionClass(unwind_exception) == kOurDependentExceptionClass ?
-            ((__cxa_dependent_exception*)exception_header)->primaryException :
-            exception_header + 1;
-    const __shim_type_info* thrown_type =
-        static_cast<const __shim_type_info*>(exception_header->exceptionType);
+  void* thrown_object = __getExceptionClass(unwind_exception) == kOurDependentExceptionClass
+                            ? ((__cxa_dependent_exception*)exception_header)->primaryException
+                            : exception_header + 1;
+  const __shim_type_info* thrown_type = static_cast<const __shim_type_info*>(exception_header->exceptionType);
 
-    auto name = [str = thrown_type->name()] {
+  auto name = [str = thrown_type->name()] {
 #    ifndef LIBCXXABI_NON_DEMANGLING_TERMINATE
-      if (const char* result = __cxxabiv1::__cxa_demangle(str, nullptr, nullptr, nullptr))
-        // We're about to abort(), this memory can never be freed; so it's fine
-        // to just return a raw pointer
-        return result;
+    if (const char* result = __cxxabiv1::__cxa_demangle(str, nullptr, nullptr, nullptr))
+      // We're about to abort(), this memory can never be freed; so it's fine
+      // to just return a raw pointer
+      return result;
 #    endif
-      return str;
-    }();
-
-    // If the uncaught exception can be caught with std::exception&
-    const __shim_type_info* catch_type =
-        static_cast<const __shim_type_info*>(&typeid(std::exception));
-    if (catch_type->can_catch(thrown_type, thrown_object))
-    {
-        // Include the what() message from the exception
-        const std::exception* e = static_cast<const std::exception*>(thrown_object);
-        __abort_message("terminating due to %s exception of type %s: %s", cause, name, e->what());
-    }
-    else
-    {
-        // Else just note that we're terminating due to an exception
-        __abort_message("terminating due to %s exception of type %s", cause, name);
-    }
+    return str;
+  }();
+
+  // If the uncaught exception can be caught with std::exception&
+  const __shim_type_info* catch_type = static_cast<const __shim_type_info*>(&typeid(std::exception));
+  if (catch_type->can_catch(thrown_type, thrown_object)) {
+    // Include the what() message from the exception
+    const std::exception* e = static_cast<const std::exception*>(thrown_object);
+    __abort_message("terminating due to %s exception of type %s: %s", cause, name, e->what());
+  } else {
+    // Else just note that we're terminating due to an exception
+    __abort_message("terminating due to %s exception of type %s", cause, name);
+  }
 }
-#else // !_LIBCXXABI_NO_EXCEPTIONS
-[[noreturn]] static void demangling_terminate_handler()
-{
-    (void)cause;
-    __abort_message("terminating");
+#  else  // !_LIBCXXABI_NO_EXCEPTIONS
+[[noreturn]] static void demangling_terminate_handler() {
+  (void)cause;
+  __abort_message("terminating");
 }
-#endif // !_LIBCXXABI_NO_EXCEPTIONS
+#  endif // !_LIBCXXABI_NO_EXCEPTIONS
 
-[[noreturn]] static void demangling_unexpected_handler()
-{
-    cause = "unexpected";
-    std::terminate();
+[[noreturn]] static void demangling_unexpected_handler() {
+  cause = "unexpected";
+  std::terminate();
 }
 
 static constexpr std::terminate_handler default_terminate_handler = demangling_terminate_handler;
diff --git a/libcxxabi/src/cxa_exception.cpp b/libcxxabi/src/cxa_exception.cpp
index f56ecc248..f8e597315 100644
--- a/libcxxabi/src/cxa_exception.cpp
+++ b/libcxxabi/src/cxa_exception.cpp
@@ -141,15 +141,15 @@ exception_cleanup_func(_Unwind_Reason_Code reason, _Unwind_Exception* unwind_exc
 }
 
 [[noreturn]] static void failed_throw(__cxa_exception* exception_header) {
-//  Section 2.5.3 says:
-//      * For purposes of this ABI, several things are considered exception handlers:
-//      ** A terminate() call due to a throw.
-//  and
-//      * Upon entry, Following initialization of the catch parameter,
-//          a handler must call:
-//      * void *__cxa_begin_catch(void *exceptionObject );
-    (void) __cxa_begin_catch(&exception_header->unwindHeader);
-    std::__terminate(exception_header->terminateHandler);
+  //  Section 2.5.3 says:
+  //      * For purposes of this ABI, several things are considered exception handlers:
+  //      ** A terminate() call due to a throw.
+  //  and
+  //      * Upon entry, Following initialization of the catch parameter,
+  //          a handler must call:
+  //      * void *__cxa_begin_catch(void *exceptionObject );
+  (void)__cxa_begin_catch(&exception_header->unwindHeader);
+  std::__terminate(exception_header->terminateHandler);
 }
 
 // Return the offset of the __cxa_exception header from the start of the
diff --git a/libcxxabi/src/cxa_handlers.cpp b/libcxxabi/src/cxa_handlers.cpp
index 0abdc7404..921898165 100644
--- a/libcxxabi/src/cxa_handlers.cpp
+++ b/libcxxabi/src/cxa_handlers.cpp
@@ -36,11 +36,7 @@ __unexpected(unexpected_handler func)
     __abort_message("unexpected_handler unexpectedly returned");
 }
 
-[[noreturn]] void
-unexpected()
-{
-    __unexpected(get_unexpected());
-}
+[[noreturn]] void unexpected() { __unexpected(get_unexpected()); }
 
 terminate_handler
 get_terminate() noexcept
@@ -68,9 +64,7 @@ __terminate(terminate_handler func) noexcept
 #endif // _LIBCXXABI_NO_EXCEPTIONS
 }
 
-[[noreturn]] void
-terminate() noexcept
-{
+[[noreturn]] void terminate() noexcept {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
     // If there might be an uncaught exception
     using namespace __cxxabiv1;
diff --git a/libcxxabi/src/cxa_handlers.h b/libcxxabi/src/cxa_handlers.h
index 072b85ed8..e97b33da8 100644
--- a/libcxxabi/src/cxa_handlers.h
+++ b/libcxxabi/src/cxa_handlers.h
@@ -19,13 +19,9 @@
 namespace std
 {
 
-[[noreturn]] _LIBCXXABI_HIDDEN
-void
-__unexpected(unexpected_handler func);
+[[noreturn]] _LIBCXXABI_HIDDEN void __unexpected(unexpected_handler func);
 
-[[noreturn]] _LIBCXXABI_HIDDEN
-void
-__terminate(terminate_handler func) noexcept;
+[[noreturn]] _LIBCXXABI_HIDDEN void __terminate(terminate_handler func) noexcept;
 
 }  // std
 
diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index c5050e46c..5a3a0adf8 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1271,29 +1271,25 @@ __gxx_personality_v0(_Unwind_State state,
 }
 #endif
 
-
-[[noreturn]] _LIBCXXABI_FUNC_VIS void
-__cxa_call_unexpected(void* arg)
-{
-    _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(arg);
-    if (unwind_exception == 0)
-        call_terminate(false, unwind_exception);
-    __cxa_begin_catch(unwind_exception);
-    bool native_old_exception = __isOurExceptionClass(unwind_exception);
-    std::unexpected_handler u_handler;
-    std::terminate_handler t_handler;
-    __cxa_exception* old_exception_header = 0;
-    int64_t ttypeIndex;
-    const uint8_t* lsda;
-    uintptr_t base = 0;
-
-    if (native_old_exception)
-    {
-        old_exception_header = (__cxa_exception*)(unwind_exception+1) - 1;
-        t_handler = old_exception_header->terminateHandler;
-        u_handler = old_exception_header->unexpectedHandler;
-        // If std::__unexpected(u_handler) rethrows the same exception,
-        //   these values get overwritten by the rethrow.  So save them now:
+[[noreturn]] _LIBCXXABI_FUNC_VIS void __cxa_call_unexpected(void* arg) {
+  _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(arg);
+  if (unwind_exception == 0)
+    call_terminate(false, unwind_exception);
+  __cxa_begin_catch(unwind_exception);
+  bool native_old_exception = __isOurExceptionClass(unwind_exception);
+  std::unexpected_handler u_handler;
+  std::terminate_handler t_handler;
+  __cxa_exception* old_exception_header = 0;
+  int64_t ttypeIndex;
+  const uint8_t* lsda;
+  uintptr_t base = 0;
+
+  if (native_old_exception) {
+    old_exception_header = (__cxa_exception*)(unwind_exception + 1) - 1;
+    t_handler = old_exception_header->terminateHandler;
+    u_handler = old_exception_header->unexpectedHandler;
+    // If std::__unexpected(u_handler) rethrows the same exception,
+    //   these values get overwritten by the rethrow.  So save them now:
 #if defined(_LIBCXXABI_ARM_EHABI)
         ttypeIndex = (int64_t)(int32_t)unwind_exception->barrier_cache.bitpattern[4];
         lsda = (const uint8_t*)unwind_exception->barrier_cache.bitpattern[2];
diff --git a/libcxxabi/src/cxa_virtual.cpp b/libcxxabi/src/cxa_virtual.cpp
index 1d3f4c63c..375c22df4 100644
--- a/libcxxabi/src/cxa_virtual.cpp
+++ b/libcxxabi/src/cxa_virtual.cpp
@@ -11,13 +11,9 @@
 
 namespace __cxxabiv1 {
 extern "C" {
-[[noreturn]] _LIBCXXABI_FUNC_VIS
-void __cxa_pure_virtual(void) {
-  __abort_message("Pure virtual function called!");
-}
+[[noreturn]] _LIBCXXABI_FUNC_VIS void __cxa_pure_virtual(void) { __abort_message("Pure virtual function called!"); }
 
-[[noreturn]] _LIBCXXABI_FUNC_VIS
-void __cxa_deleted_virtual(void) {
+[[noreturn]] _LIBCXXABI_FUNC_VIS void __cxa_deleted_virtual(void) {
   __abort_message("Deleted virtual function called!");
 }
 } // extern "C"

``````````

</details>


https://github.com/llvm/llvm-project/pull/207344

From libcxx-commits at lists.llvm.org  Fri Jul  3 01:40:22 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 01:40:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] efcfff7 - [libc++] Add assert test for
 string assign/append (#207164)
Message-ID: <6a477576.13be6df8.3d8759.8789@mx.google.com>


Author: Connector Switch
Date: 2026-07-03T16:40:16+08:00
New Revision: efcfff751f856c66c936d2057ed9a8463e070833

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

LOG: [libc++] Add assert test for string assign/append (#207164)

Fixes
https://github.com/llvm/llvm-project/pull/206320#discussion_r3507502638.

Added: 
    libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
    libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp

Modified: 
    

Removed: 
    


################################################################################
diff  --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
new file mode 100644
index 0000000000000..c3745ca0a762d
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.append.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// basic_string& append(const value_type* s, size_type n);
+// basic_string& append(const value_type* s);
+// basic_string& append(const value_type* s, size_type pos, size_type n);
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03
+// UNSUPPORTED: libcpp-hardening-mode={{none|fast}}
+// UNSUPPORTED: libcpp-assertion-semantic={{ignore|observe}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <string>
+
+#include "check_assertion.h"
+#include "min_allocator.h"
+
+template <class S>
+void test() {
+  S l1("123");
+  const char* np = nullptr;
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np, 1), "string::append received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np), "string::append received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.append(np, 0, 1), "string::append received nullptr");
+}
+
+int main(int, char**) {
+  test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp
new file mode 100644
index 0000000000000..80abd15929735
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.assign.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// basic_string& assign(const value_type* s, size_type n);
+// basic_string& assign(const value_type* s);
+// basic_string& assign(const value_type* s, size_type pos, size_type n);
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03
+// UNSUPPORTED: libcpp-hardening-mode={{none|fast}}
+// UNSUPPORTED: libcpp-assertion-semantic={{ignore|observe}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <string>
+
+#include "check_assertion.h"
+#include "min_allocator.h"
+
+template <class S>
+void test() {
+  S l1("123");
+  const char* np = nullptr;
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np, 1), "string::assign received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np), "string::assign received nullptr");
+  TEST_LIBCPP_ASSERT_FAILURE(l1.assign(np, 0, 1), "string::assign received nullptr");
+}
+
+int main(int, char**) {
+  test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+
+  return 0;
+}


        

From libcxx-commits at lists.llvm.org  Fri Jul  3 01:40:24 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Fri, 03 Jul 2026 01:40:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add assert test for string
	assign/append (PR #207164)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207164@github.com>
Message-ID: <6a477578.f732ad81.1d8ca.03ad@mx.google.com>


https://github.com/c8ef closed https://github.com/llvm/llvm-project/pull/207164

From libcxx-commits at lists.llvm.org  Fri Jul  3 02:36:34 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 03 Jul 2026 02:36:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
 `adjacent_transform_view` (PR #205900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205900@github.com>
Message-ID: <6a4782a2.da8bafd3.1eab2b.27f8@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/205900

>From 65046f1524740c0e4a7829b61cd6a7601c34cfa5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 25 Jun 2026 21:47:55 +0200
Subject: [PATCH 01/11] initial: nodiscard adjacent_transform_view

---
 .../include/__ranges/adjacent_transform_view.h  | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..bdae098623977 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -105,22 +105,23 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   _LIBCPP_HIDE_FROM_ABI constexpr explicit adjacent_transform_view(_View __base, _Fn __fun)
       : __inner_(std::move(__base)), __fun_(std::in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __inner_.base();
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {
     return __iterator<true>(*this, __inner_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __inner_.end());
     } else {
@@ -128,7 +129,7 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -138,13 +139,13 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __inner_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __inner_.size();

>From 0b582345030b80efba493370df6595f56abdaf64 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 25 Jun 2026 23:51:42 +0200
Subject: [PATCH 02/11] Update
 libcxx/include/__ranges/adjacent_transform_view.h

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 libcxx/include/__ranges/adjacent_transform_view.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index bdae098623977..5d22042e9c78f 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -114,7 +114,6 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {

>From acc5961454c76807c11297fc8ac970135d47f69f Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 27 Jun 2026 17:50:55 +0200
Subject: [PATCH 03/11] complete: nodiscard to adjacent_transform_view.h

---
 .../include/__ranges/adjacent_transform_view.h | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 5d22042e9c78f..f8c4cd716a8d5 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -202,7 +202,7 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     requires _Const && convertible_to<__inner_iterator<false>, __inner_iterator<true>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(__noexcept_dereference(make_index_sequence<_Np>{})) {
     return std::apply(
         [&](const auto&... __iters) -> decltype(auto) { return std::invoke(*__parent_->__fun_, *__iters...); },
@@ -249,7 +249,7 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -291,25 +291,25 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__inner_iterator<_Const>, __inner_iterator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -344,14 +344,14 @@ class adjacent_transform_view<_View, _Fn, _Np>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -371,7 +371,7 @@ struct __fn : __range_adaptor_closure<__fn<_Np>> {
   }
 
   template <class _Range, class _Fn>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range, _Fn&& __fn) noexcept(
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range, _Fn&& __fn) noexcept(
       noexcept(adjacent_transform_view<views::all_t<_Range&&>, decay_t<_Fn>, _Np>(
           std::forward<_Range>(__range), std::forward<_Fn>(__fn))))
       -> decltype(adjacent_transform_view<views::all_t<_Range&&>, decay_t<_Fn>, _Np>(

>From 15e15ee15ad0e6580dfff6f8fd3da6fbb9255854 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 27 Jun 2026 17:51:04 +0200
Subject: [PATCH 04/11] complete: nodiscard to adjacent_transform_view.h

---
 libcxx/include/__ranges/adjacent_transform_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index f8c4cd716a8d5..10bf812607b3e 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -309,7 +309,8 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__inner_iterator<_Const>, __inner_iterator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;

>From 287a7d06ae3ca47191a279c987f60137bd0e5761 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 27 Jun 2026 18:24:55 +0200
Subject: [PATCH 05/11] add: initial test file

---
 .../nodiscard.verify.cpp                      | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..6a26fa8bfda85
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,20 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Test the libc++ extension that std::ranges::adjacent_transform_view and std::views::adjacent_transform are marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+#include <functional>
+
+void test(){
+
+}

>From 3aee8e6f2e9c2c3f291da0f79993c161c7c3189d Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 28 Jun 2026 02:18:05 +0200
Subject: [PATCH 06/11] add: test view

---
 .../range.adjacent.transform/nodiscard.verify.cpp      | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 6a26fa8bfda85..459b132766ae0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -15,6 +15,16 @@
 #include <utility>
 #include <functional>
 
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
 void test(){
 
 }

>From 5658eb286ecf418576bc84f50273e2ca37d11a10 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 29 Jun 2026 23:09:10 +0200
Subject: [PATCH 07/11] add: tests for adjacent.transform

---
 .../nodiscard.verify.cpp                      | 65 ++++++++++++++++++-
 1 file changed, 64 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 459b132766ae0..9cf0fffb3ba6a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -25,6 +25,69 @@ static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
-void test(){
+void test() {
+  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  auto v      = View{} | std::views::adjacent_transform<2>(std::plus<>{});
 
+  // [range.adjacent.transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).size();
+
+  // [range.adjacent.transform.iterator]
+
+  auto it   = v.begin();
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it[0];
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  0 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.adjacent.tranform.sentinel]
+
+  auto st = v.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
+  // [range.adjacent.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<0>(range);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<2>(range);
 }

>From b6146befe51e731c8b2bd19f57772ec2e3e50c54 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 10:24:32 +0200
Subject: [PATCH 08/11] fix: formatting

---
 libcxx/include/__ranges/adjacent_transform_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 10bf812607b3e..92aaeb24be183 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -110,10 +110,10 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.base();
   }
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {

>From 4aa95f70a6e45139e002c7f51167fdf0affa5312 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 10:45:28 +0200
Subject: [PATCH 09/11] ref: std::plus<> to std::multiplies() as per the
 standard 25.7.28.1.3 example

---
 .../range.adjacent.transform/nodiscard.verify.cpp               | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 9cf0fffb3ba6a..9677ea4f06019 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -27,7 +27,7 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
   int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  auto v      = View{} | std::views::adjacent_transform<2>(std::plus<>{});
+  auto v      = View{} | std::views::adjacent_transform<2>(std::multiplies());
 
   // [range.adjacent.transform.view]
 

>From 4459d77f262cf216acb83805dcffe636399f19f5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 14:24:02 +0200
Subject: [PATCH 10/11] fix: includes order and transform_view<2>(...) test

---
 .../range.adjacent.transform/nodiscard.verify.cpp        | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 9677ea4f06019..4a6d087a9e1f4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -11,9 +11,9 @@
 
 // Test the libc++ extension that std::ranges::adjacent_transform_view and std::views::adjacent_transform are marked as [[nodiscard]].
 
+#include <functional>
 #include <ranges>
 #include <utility>
-#include <functional>
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -26,8 +26,7 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  auto v      = View{} | std::views::adjacent_transform<2>(std::multiplies());
+  auto v = View{} | std::views::adjacent_transform<2>(std::multiplies());
 
   // [range.adjacent.transform.view]
 
@@ -85,9 +84,11 @@ void test() {
 
   // [range.adjacent.overview]
 
+  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::adjacent_transform<0>(range);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::adjacent_transform<2>(range);
+  std::views::adjacent_transform<2>(range, std::multiplies());
 }

>From 29183f98efbac1b381b8ddf6d6ffdb1d160c034b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 3 Jul 2026 11:36:20 +0200
Subject: [PATCH 11/11] Update
 libcxx/include/__ranges/adjacent_transform_view.h

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 libcxx/include/__ranges/adjacent_transform_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 92aaeb24be183..e0c20b7a95513 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -365,7 +365,7 @@ template <size_t _Np>
 struct __fn : __range_adaptor_closure<__fn<_Np>> {
   template <class _Range, class _Fn>
     requires(_Np == 0 && forward_range<_Range &&>)
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
   operator()(_Range&&, _Fn&& __fn) noexcept(noexcept(views::zip_transform(std::forward<_Fn>(__fn))))
       -> decltype(views::zip_transform(std::forward<_Fn>(__fn))) {
     return views::zip_transform(std::forward<_Fn>(__fn));


From libcxx-commits at lists.llvm.org  Fri Jul  3 03:22:15 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 03 Jul 2026 03:22:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a478d57.13be6df8.3d8759.e0a8@mx.google.com>


================
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it  = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.zip_transform.sentinel]
+
+  auto st = ztv.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // [range.zip_transform.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform(Invocable<>{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform([](int x) { return x; }, range);
+}
----------------
frederick-vs-ja wrote:

Let's add the conventional empty ending line. Also, I think this PR is almost ready and there's no related test failure.

https://github.com/llvm/llvm-project/pull/207120

From libcxx-commits at lists.llvm.org  Fri Jul  3 03:37:54 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 03 Jul 2026 03:37:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Use eary returns in some
	vector functions (PR #207367)
Message-ID: <llvm.org/llvm/llvm-project/pull/207367@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207367

None

>From b9a27cc5a19e7f115d26393b7c798897dfc0d0cd Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 12:37:32 +0200
Subject: [PATCH] [libc++][NFC] Use eary returns in some vector functions

---
 libcxx/include/__vector/vector.h | 101 ++++++++++++++++---------------
 1 file changed, 53 insertions(+), 48 deletions(-)

diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..20111c993c5b3 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -1198,29 +1198,30 @@ vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) {
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::insert(const_iterator __position, size_type __n, const_reference __x) {
+  if (__n <= 0)
+    return __position;
+
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (__n > 0) {
-    if (__n <= __layout_.__remaining_capacity()) {
-      size_type __old_n  = __n;
-      pointer __end      = __layout_.__end_ptr();
-      pointer __old_last = __end;
-      if (__n > static_cast<size_type>(__end - __p)) {
-        size_type __cx = __n - (__end - __p);
-        __construct_at_end(__cx, __x);
-        __n -= __cx;
-      }
-      if (__n > 0) {
-        __move_range(__p, __old_last, __p + __old_n);
-        const_pointer __xr = pointer_traits<const_pointer>::pointer_to(__x);
-        if (std::__is_pointer_in_range(std::__to_address(__p), std::__to_address(__end), std::addressof(__x)))
-          __xr += __old_n;
-        std::fill_n(__p, __n, *__xr);
-      }
-    } else {
-      _SplitBuffer __v(__recommend(size() + __n), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
-      __v.__construct_at_end(__n, __x);
-      __p = __layout_.__relocate_with_pivot(__v, __p);
+  if (__n <= __layout_.__remaining_capacity()) {
+    size_type __old_n  = __n;
+    pointer __end      = __layout_.__end_ptr();
+    pointer __old_last = __end;
+    if (__n > static_cast<size_type>(__end - __p)) {
+      size_type __cx = __n - (__end - __p);
+      __construct_at_end(__cx, __x);
+      __n -= __cx;
+    }
+    if (__n > 0) {
+      __move_range(__p, __old_last, __p + __old_n);
+      const_pointer __xr = pointer_traits<const_pointer>::pointer_to(__x);
+      if (std::__is_pointer_in_range(std::__to_address(__p), std::__to_address(__end), std::addressof(__x)))
+        __xr += __old_n;
+      std::fill_n(__p, __n, *__xr);
     }
+  } else {
+    _SplitBuffer __v(__recommend(size() + __n), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
+    __v.__construct_at_end(__n, __x);
+    __p = __layout_.__relocate_with_pivot(__v, __p);
   }
   return __make_iter(__p);
 }
@@ -1271,36 +1272,40 @@ template <class _AlgPolicy, class _Iterator, class _Sentinel>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::__insert_with_size(
     const_iterator __position, _Iterator __first, _Sentinel __last, difference_type __n) {
+  if (__n <= 0)
+    return __position;
+
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (__n > 0) {
-    if (__n <= static_cast<difference_type>(__layout_.__remaining_capacity())) {
-      pointer __end        = __layout_.__end_ptr();
-      pointer __old_last   = __end;
-      difference_type __dx = __end - __p;
-      if (__n > __dx) {
+
+  if (__n > static_cast<difference_type>(__layout_.__remaining_capacity())) {
+    _SplitBuffer __v(__recommend(size() + __n), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
+    __v.__construct_at_end_with_size(std::move(__first), __n);
+    __p = __layout_.__relocate_with_pivot(__v, __p);
+    return __make_iter(__p);
+  }
+
+  pointer __end        = __layout_.__end_ptr();
+  difference_type __dx = __end - __p;
+
+  if (__n <= __dx) {
+    __move_range(__p, __end, __p + __n);
+    __insert_assign_n_unchecked<_AlgPolicy>(std::move(__first), __n, __p);
+    return __make_iter(__p);
+  }
+
+  pointer __old_last = __end;
 #if _LIBCPP_STD_VER >= 23
-        if constexpr (!forward_iterator<_Iterator>) {
-          __construct_at_end(std::move(__first), std::move(__last), __n);
-          std::rotate(__p, __old_last, __end);
-        } else
+  if constexpr (!forward_iterator<_Iterator>) {
+    __construct_at_end(std::move(__first), std::move(__last), __n);
+    std::rotate(__p, __old_last, __end);
+    return __make_iter(__p);
+  }
 #endif
-        {
-          _Iterator __m = std::next(__first, __dx);
-          __construct_at_end(__m, __last, __n - __dx);
-          if (__dx > 0) {
-            __move_range(__p, __old_last, __p + __n);
-            __insert_assign_n_unchecked<_AlgPolicy>(__first, __dx, __p);
-          }
-        }
-      } else {
-        __move_range(__p, __old_last, __p + __n);
-        __insert_assign_n_unchecked<_AlgPolicy>(std::move(__first), __n, __p);
-      }
-    } else {
-      _SplitBuffer __v(__recommend(size() + __n), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
-      __v.__construct_at_end_with_size(std::move(__first), __n);
-      __p = __layout_.__relocate_with_pivot(__v, __p);
-    }
+  _Iterator __m = std::next(__first, __dx);
+  __construct_at_end(__m, __last, __n - __dx);
+  if (__dx > 0) {
+    __move_range(__p, __old_last, __p + __n);
+    __insert_assign_n_unchecked<_AlgPolicy>(__first, __dx, __p);
   }
   return __make_iter(__p);
 }


From libcxx-commits at lists.llvm.org  Fri Jul  3 04:03:03 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 03 Jul 2026 04:03:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Use eary returns in some
	vector functions (PR #207367)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207367@github.com>
Message-ID: <6a4796e7.491d90f5.1f0d58.090e@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207367

>From e4fcf0f0a65a79df81578070fff2db011e415d9c Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 12:37:32 +0200
Subject: [PATCH] [libc++][NFC] Use eary returns in some vector functions

---
 libcxx/include/__vector/vector.h | 102 ++++++++++++++++---------------
 1 file changed, 54 insertions(+), 48 deletions(-)

diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..fdec69b5ad0b1 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -1199,28 +1199,30 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::insert(const_iterator __position, size_type __n, const_reference __x) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (__n > 0) {
-    if (__n <= __layout_.__remaining_capacity()) {
-      size_type __old_n  = __n;
-      pointer __end      = __layout_.__end_ptr();
-      pointer __old_last = __end;
-      if (__n > static_cast<size_type>(__end - __p)) {
-        size_type __cx = __n - (__end - __p);
-        __construct_at_end(__cx, __x);
-        __n -= __cx;
-      }
-      if (__n > 0) {
-        __move_range(__p, __old_last, __p + __old_n);
-        const_pointer __xr = pointer_traits<const_pointer>::pointer_to(__x);
-        if (std::__is_pointer_in_range(std::__to_address(__p), std::__to_address(__end), std::addressof(__x)))
-          __xr += __old_n;
-        std::fill_n(__p, __n, *__xr);
-      }
-    } else {
-      _SplitBuffer __v(__recommend(size() + __n), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
-      __v.__construct_at_end(__n, __x);
-      __p = __layout_.__relocate_with_pivot(__v, __p);
+
+  if (__n <= 0)
+    return __make_iter(__p);
+
+  if (__n <= __layout_.__remaining_capacity()) {
+    size_type __old_n  = __n;
+    pointer __end      = __layout_.__end_ptr();
+    pointer __old_last = __end;
+    if (__n > static_cast<size_type>(__end - __p)) {
+      size_type __cx = __n - (__end - __p);
+      __construct_at_end(__cx, __x);
+      __n -= __cx;
+    }
+    if (__n > 0) {
+      __move_range(__p, __old_last, __p + __old_n);
+      const_pointer __xr = pointer_traits<const_pointer>::pointer_to(__x);
+      if (std::__is_pointer_in_range(std::__to_address(__p), std::__to_address(__end), std::addressof(__x)))
+        __xr += __old_n;
+      std::fill_n(__p, __n, *__xr);
     }
+  } else {
+    _SplitBuffer __v(__recommend(size() + __n), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
+    __v.__construct_at_end(__n, __x);
+    __p = __layout_.__relocate_with_pivot(__v, __p);
   }
   return __make_iter(__p);
 }
@@ -1272,35 +1274,39 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Alloca
 vector<_Tp, _Allocator>::__insert_with_size(
     const_iterator __position, _Iterator __first, _Sentinel __last, difference_type __n) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (__n > 0) {
-    if (__n <= static_cast<difference_type>(__layout_.__remaining_capacity())) {
-      pointer __end        = __layout_.__end_ptr();
-      pointer __old_last   = __end;
-      difference_type __dx = __end - __p;
-      if (__n > __dx) {
+
+  if (__n <= 0)
+    return __make_iter(__p);
+
+  if (__n > static_cast<difference_type>(__layout_.__remaining_capacity())) {
+    _SplitBuffer __v(__recommend(size() + __n), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
+    __v.__construct_at_end_with_size(std::move(__first), __n);
+    __p = __layout_.__relocate_with_pivot(__v, __p);
+    return __make_iter(__p);
+  }
+
+  pointer __end        = __layout_.__end_ptr();
+  difference_type __dx = __end - __p;
+
+  if (__n <= __dx) {
+    __move_range(__p, __end, __p + __n);
+    __insert_assign_n_unchecked<_AlgPolicy>(std::move(__first), __n, __p);
+    return __make_iter(__p);
+  }
+
+  pointer __old_last = __end;
 #if _LIBCPP_STD_VER >= 23
-        if constexpr (!forward_iterator<_Iterator>) {
-          __construct_at_end(std::move(__first), std::move(__last), __n);
-          std::rotate(__p, __old_last, __end);
-        } else
+  if constexpr (!forward_iterator<_Iterator>) {
+    __construct_at_end(std::move(__first), std::move(__last), __n);
+    std::rotate(__p, __old_last, __end);
+    return __make_iter(__p);
+  }
 #endif
-        {
-          _Iterator __m = std::next(__first, __dx);
-          __construct_at_end(__m, __last, __n - __dx);
-          if (__dx > 0) {
-            __move_range(__p, __old_last, __p + __n);
-            __insert_assign_n_unchecked<_AlgPolicy>(__first, __dx, __p);
-          }
-        }
-      } else {
-        __move_range(__p, __old_last, __p + __n);
-        __insert_assign_n_unchecked<_AlgPolicy>(std::move(__first), __n, __p);
-      }
-    } else {
-      _SplitBuffer __v(__recommend(size() + __n), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
-      __v.__construct_at_end_with_size(std::move(__first), __n);
-      __p = __layout_.__relocate_with_pivot(__v, __p);
-    }
+  _Iterator __m = std::next(__first, __dx);
+  __construct_at_end(__m, __last, __n - __dx);
+  if (__dx > 0) {
+    __move_range(__p, __old_last, __p + __n);
+    __insert_assign_n_unchecked<_AlgPolicy>(__first, __dx, __p);
   }
   return __make_iter(__p);
 }


From libcxx-commits at lists.llvm.org  Fri Jul  3 04:07:30 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 03 Jul 2026 04:07:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Enable CPO compile tests
	(PR #207123)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207123@github.com>
Message-ID: <6a4797f2.2020f6c2.1fdf61.5607@mx.google.com>


Zingam wrote:

The libc test failure appears to be unrelated. Shall I merge?

https://github.com/llvm/llvm-project/pull/207123

From libcxx-commits at lists.llvm.org  Fri Jul  3 04:12:41 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 03 Jul 2026 04:12:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Enable CPO compile tests
	(PR #207123)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207123@github.com>
Message-ID: <6a479929.7d41f3a1.d1cf7.2cce@mx.google.com>


frederick-vs-ja wrote:

> The libc test failure appears to be unrelated. Shall I merge?

I think you should merge this now.

https://github.com/llvm/llvm-project/pull/207123

From libcxx-commits at lists.llvm.org  Fri Jul  3 04:16:36 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 04:16:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 4f06fa9 - [libc++][ranges] Enable CPO
	compile tests (#207123)
Message-ID: <6a479a14.3157459c.24b6e5.7069@mx.google.com>


Author: Hristo Hristov
Date: 2026-07-03T14:16:31+03:00
New Revision: 4f06fa92b6cf16cec5bd0bfe7ef3efec17b91e24

URL: https://github.com/llvm/llvm-project/commit/4f06fa92b6cf16cec5bd0bfe7ef3efec17b91e24
DIFF: https://github.com/llvm/llvm-project/commit/4f06fa92b6cf16cec5bd0bfe7ef3efec17b91e24.diff

LOG: [libc++][ranges] Enable CPO compile tests (#207123)

`adjacent_transform_view` and `stride_view` were implemented but the
test cases were omitted.

Co-authored-by: Hristo Hristov <zingam at outlook.com>

Added: 
    

Modified: 
    libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..71a0b6ebb804c 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -120,7 +120,7 @@ static_assert(test(std::views::transform, a, [](int x) { return x + 1; }));
 static_assert(test(std::views::values, pairs));
 
 #if TEST_STD_VER >= 23
-// static_assert(test(std::views::adjacent_transform<2>, [](int x, int y) { return x + y; }, a));
+static_assert(test(std::views::adjacent_transform<2>, a, [](int x, int y) { return x + y; }));
 static_assert(test(std::views::adjacent<2>, a));
 // static_assert(test(std::views::as_const, a));
 static_assert(test(std::views::as_rvalue, a));
@@ -130,7 +130,7 @@ static_assert(test(std::views::chunk_by, a, [](int x, int y) { return x < y; }))
 static_assert(test(std::views::enumerate, a));
 static_assert(test(std::views::join_with, 1));
 static_assert(test(std::views::pairwise, a));
-// static_assert(test(std::views::stride, a, 1));
+static_assert(test(std::views::stride, a, 1));
 static_assert(test(std::views::zip_transform, [](int x, int y) { return x + y; }, a, a));
 static_assert(test(std::views::zip, a, a));
 #endif


        

From libcxx-commits at lists.llvm.org  Fri Jul  3 04:16:37 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 03 Jul 2026 04:16:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Enable CPO compile tests
	(PR #207123)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207123@github.com>
Message-ID: <6a479a15.13be6df8.3d8759.bdd8@mx.google.com>


https://github.com/Zingam closed https://github.com/llvm/llvm-project/pull/207123

From libcxx-commits at lists.llvm.org  Fri Jul  3 04:41:08 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 03 Jul 2026 04:41:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make `<set>` `std::multiset`
 constexpr as part of P3372R3 (PR #206257)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206257@github.com>
Message-ID: <6a479fd4.eb24c6ea.1f2e26.450e@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/206257

From libcxx-commits at lists.llvm.org  Fri Jul  3 04:41:08 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 03 Jul 2026 04:41:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make `<set>` `std::multiset`
 constexpr as part of P3372R3 (PR #206257)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206257@github.com>
Message-ID: <6a479fd4.ac469dc7.e3aa6.ca15@mx.google.com>


================
@@ -9,18 +9,19 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
 // ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-missing-field-initializers
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2147483647
----------------
frederick-vs-ja wrote:

LGTM with nits. "Critical" tests seem all passed, and the failures with LLVM-libc build are unrelated.

2147483647 looks a bit scary to me as it permits extremely long compile time. I roughly verified this on Godbolt and found that 4000000 should be sufficient. (The exact number seems slightly less than 3400000.) Or maybe we can use GCC's default limit which is 33554432.

```suggestion
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=4000000
```

https://github.com/llvm/llvm-project/pull/206257

From libcxx-commits at lists.llvm.org  Fri Jul  3 05:37:37 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 03 Jul 2026 05:37:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
Message-ID: <llvm.org/llvm/llvm-project/pull/207384@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207384

This is a code size rather than a performance optimization.

For example, the number of instructions in `__insert_with_iter` is
almost cut in half.


>From 1799dcbc32e6e5b4957eded2177a80d148e98dbf Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 13:34:50 +0200
Subject: [PATCH] [libc++] Optimize vector::__move_range for trivially copyable
 types

This is a code size rather than a performance optimization.

For example, the number of instructions in `__insert_with_iter` is
almost cut in half.
---
 libcxx/include/__vector/vector.h | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..672513188effb 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -47,6 +47,7 @@
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__split_buffer>
+#include <__string/constexpr_c_functions.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_allocator.h>
@@ -57,6 +58,7 @@
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
+#include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/declval.h>
@@ -1120,14 +1122,23 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointer __to) {
   pointer __old_last  = __layout_.__end_ptr();
   difference_type __n = __old_last - __to;
-  {
-    pointer __i = __from_s + __n;
-    _ConstructTransaction __tx(*this, __from_e - __i);
-    for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
-      __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
+  if constexpr (__allocator_has_trivial_move_construct_v<allocator_type, value_type> &&
+                is_trivially_copyable<value_type>::value) {
+    if (__libcpp_is_constant_evaluated()) // construct the new trailing objects
+      std::uninitialized_fill_n(__old_last, (__from_e - __from_s) - __n, *__from_s);
+    std::__constexpr_memmove(
+        std::__to_address(__to), std::__to_address(__from_s), __element_count(__from_e - __from_s));
+    __layout_.__set_bound_using_pointer(__to + (__from_e - __from_s));
+  } else {
+    {
+      pointer __i = __from_s + __n;
+      _ConstructTransaction __tx(*this, __from_e - __i);
+      for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
+        __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
+      }
     }
+    std::move_backward(__from_s, __from_s + __n, __old_last);
   }
-  std::move_backward(__from_s, __from_s + __n, __old_last);
 }
 
 template <class _Tp, class _Allocator>


From libcxx-commits at lists.llvm.org  Fri Jul  3 06:12:37 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 03 Jul 2026 06:12:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a47b545.6e01836d.318eb9.b059@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207384

>From 39ac08268a6afa5b6bf080d409693f8d595e0f1c Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 13:34:50 +0200
Subject: [PATCH] [libc++] Optimize vector::__move_range for trivially copyable
 types

This is a code size rather than a performance optimization.

For example, the number of instructions in `__insert_with_iter` is
almost cut in half.
---
 libcxx/include/__vector/vector.h | 24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..bbace6225fce3 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -47,6 +47,7 @@
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__split_buffer>
+#include <__string/constexpr_c_functions.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_allocator.h>
@@ -57,6 +58,7 @@
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
+#include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/declval.h>
@@ -1120,14 +1122,26 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointer __to) {
   pointer __old_last  = __layout_.__end_ptr();
   difference_type __n = __old_last - __to;
+#ifndef _LIBCPP_CXX03_LANG
+  if constexpr (__allocator_has_trivial_move_construct_v<allocator_type, value_type> &&
+                is_trivially_copyable<value_type>::value) {
+    if (__libcpp_is_constant_evaluated()) // construct the new trailing objects
+      std::uninitialized_fill_n(__old_last, (__from_e - __from_s) - __n, *__from_s);
+    std::__constexpr_memmove(
+        std::__to_address(__to), std::__to_address(__from_s), __element_count(__from_e - __from_s));
+    __layout_.__set_bound_using_pointer(__to + (__from_e - __from_s));
+  } else
+#endif
   {
-    pointer __i = __from_s + __n;
-    _ConstructTransaction __tx(*this, __from_e - __i);
-    for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
-      __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
+    {
+      pointer __i = __from_s + __n;
+      _ConstructTransaction __tx(*this, __from_e - __i);
+      for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
+        __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
+      }
     }
+    std::move_backward(__from_s, __from_s + __n, __old_last);
   }
-  std::move_backward(__from_s, __from_s + __n, __old_last);
 }
 
 template <class _Tp, class _Allocator>


From libcxx-commits at lists.llvm.org  Fri Jul  3 07:02:46 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 03 Jul 2026 07:02:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a47c106.a87eb5dc.2cf3a1.1d83@mx.google.com>


https://github.com/philnik777 ready_for_review https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Fri Jul  3 07:03:21 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 07:03:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a47c129.ac469dc7.e3aa6.22e9@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

This is a code size rather than a performance optimization.

For example, the number of instructions in `__insert_with_iter` is
almost cut in half.


---
Full diff: https://github.com/llvm/llvm-project/pull/207384.diff


1 Files Affected:

- (modified) libcxx/include/__vector/vector.h (+19-5) 


``````````diff
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..bbace6225fce3 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -47,6 +47,7 @@
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__split_buffer>
+#include <__string/constexpr_c_functions.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_allocator.h>
@@ -57,6 +58,7 @@
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
+#include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/declval.h>
@@ -1120,14 +1122,26 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointer __to) {
   pointer __old_last  = __layout_.__end_ptr();
   difference_type __n = __old_last - __to;
+#ifndef _LIBCPP_CXX03_LANG
+  if constexpr (__allocator_has_trivial_move_construct_v<allocator_type, value_type> &&
+                is_trivially_copyable<value_type>::value) {
+    if (__libcpp_is_constant_evaluated()) // construct the new trailing objects
+      std::uninitialized_fill_n(__old_last, (__from_e - __from_s) - __n, *__from_s);
+    std::__constexpr_memmove(
+        std::__to_address(__to), std::__to_address(__from_s), __element_count(__from_e - __from_s));
+    __layout_.__set_bound_using_pointer(__to + (__from_e - __from_s));
+  } else
+#endif
   {
-    pointer __i = __from_s + __n;
-    _ConstructTransaction __tx(*this, __from_e - __i);
-    for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
-      __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
+    {
+      pointer __i = __from_s + __n;
+      _ConstructTransaction __tx(*this, __from_e - __i);
+      for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
+        __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
+      }
     }
+    std::move_backward(__from_s, __from_s + __n, __old_last);
   }
-  std::move_backward(__from_s, __from_s + __n, __old_last);
 }
 
 template <class _Tp, class _Allocator>

``````````

</details>


https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Fri Jul  3 07:19:17 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 07:19:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a47c4e5.cbc3f03a.3be201.f465@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Fri Jul  3 07:21:31 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 07:21:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a47c56b.13be6df8.3d8759.25a8@mx.google.com>


================
@@ -35,6 +35,7 @@
     "_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING": "libcpp-abi-no-compressed-pair-padding",
     "_LIBCPP_PSTL_BACKEND_LIBDISPATCH": "libcpp-pstl-backend-libdispatch",
     "_LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE": "libcpp-abi-use-small-deque-block-size",
+    "_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine"
----------------
ldionne wrote:

```suggestion
    "_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine",
```

https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Fri Jul  3 07:21:44 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 07:21:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a47c578.8e75e0cb.2e0812.23b8@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/206423

>From a521c654c3a7ac6e20c67f47f2c739c317e850a7 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 29 Jun 2026 10:00:42 +0200
Subject: [PATCH 1/2] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine

---
 libcxx/include/__configuration/abi.h          |   1 +
 .../__random/mersenne_twister_engine.h        |  43 +-
 libcxx/test/benchmarks/random.bench.cpp       |  25 +
 .../rand.eng.mers/ctor_result_type.pass.cpp   | 583 +++++++++------
 .../rand.eng/rand.eng.mers/ctor_sseq.pass.cpp | 697 +++++++++++-------
 .../libcxx/test/features/libcxx_macros.py     |   1 +
 6 files changed, 836 insertions(+), 514 deletions(-)

diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 51c82d99eec30..7a4c479fd1636 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -77,6 +77,7 @@
 #  define _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION
 #  define _LIBCPP_ABI_TRIVIALLY_COPYABLE_BIT_ITERATOR
 #  define _LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE
+#  define _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
 
 #elif _LIBCPP_ABI_VERSION == 1
 // Feature macros for disabling pre ABI v1 features. All of these options
diff --git a/libcxx/include/__random/mersenne_twister_engine.h b/libcxx/include/__random/mersenne_twister_engine.h
index ce82b3387dbd2..8e49b3b267aa8 100644
--- a/libcxx/include/__random/mersenne_twister_engine.h
+++ b/libcxx/include/__random/mersenne_twister_engine.h
@@ -181,6 +181,9 @@ class mersenne_twister_engine {
     for (size_t __i = 1; __i < __n; ++__i)
       __x_[__i] = (__f * (__x_[__i - 1] ^ __rshift<__w - 2>(__x_[__i - 1])) + __i) & _Max;
     __i_ = 0;
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    __update_all_states();
+#endif
   }
   template <class _Sseq, __enable_if_t<__is_seed_sequence_v<_Sseq, mersenne_twister_engine>, int> = 0>
   _LIBCPP_HIDE_FROM_ABI void seed(_Sseq& __q) {
@@ -203,17 +206,43 @@ class mersenne_twister_engine {
           return;
       __x_[0] = result_type(1) << (__w - 1);
     }
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    __update_all_states();
+#endif
+  }
+
+  void __update_state(size_t __i, size_t __k) {
+    const size_t __j         = (__i + 1) % __n;
+    const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
+    const result_type __yp   = (__x_[__i] & ~__mask) | (__x_[__j] & __mask);
+    __x_[__i]                = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
+  }
+
+  void __update_state(size_t __i) { __update_state(__i, (__i + __m) % __n); }
+
+  void __update_all_states() {
+    size_t __i = 0;
+    // This is split into two loops to help the compiler vectorize the code
+    for (; __i != (__n - __m); ++__i)
+      __update_state(__i);
+    for (size_t __j = 0; __i != __n; ++__i, ++__j)
+      __update_state(__i, __j);
   }
 
   // generating functions
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI result_type operator()() {
-    const size_t __j         = (__i_ + 1) % __n;
-    const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
-    const result_type __yp   = (__x_[__i_] & ~__mask) | (__x_[__j] & __mask);
-    const size_t __k         = (__i_ + __m) % __n;
-    __x_[__i_]               = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
-    result_type __z          = __x_[__i_] ^ (__rshift<__u>(__x_[__i_]) & __d);
-    __i_                     = __j;
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    result_type __val = __x_[__i_];
+    if (++__i_ == __n) [[__unlikely__]] {
+      __update_all_states();
+      __i_ = 0;
+    }
+#else
+    __update_state(__i_);
+    result_type __val = __x_[__i_];
+    __i_              = (__i_ + 1) % __n;
+#endif
+    result_type __z = __val ^ (__rshift<__u>(__val) & __d);
     __z ^= __lshift<__s>(__z) & __b;
     __z ^= __lshift<__t>(__z) & __c;
     return __z ^ __rshift<__l>(__z);
diff --git a/libcxx/test/benchmarks/random.bench.cpp b/libcxx/test/benchmarks/random.bench.cpp
index e6af4c3e26eaf..15e16b988a29f 100644
--- a/libcxx/test/benchmarks/random.bench.cpp
+++ b/libcxx/test/benchmarks/random.bench.cpp
@@ -34,4 +34,29 @@ static void BM_SeedSeq_Generate(benchmark::State& state) {
 }
 BENCHMARK(BM_SeedSeq_Generate)->Ranges({{1, MAX_SEED_LEN}, {1, MAX_BUFFER_LEN}});
 
+template <class Engine>
+static void BM_engine(benchmark::State& state) {
+  Engine engine;
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(engine());
+  }
+}
+BENCHMARK(BM_engine<std::mt19937_64>)->Name("std::mt19937_64::operator()");
+BENCHMARK(BM_engine<std::mt19937>)->Name("std::mt19937::operator()");
+
+template <class Engine>
+static void BM_engine_array(benchmark::State& state) {
+  Engine engine;
+
+  typename Engine::result_type buffer[128];
+
+  for (auto _ : state) {
+    std::generate(std::begin(buffer), std::end(buffer), std::ref(engine));
+    benchmark::DoNotOptimize(buffer);
+  }
+}
+BENCHMARK(BM_engine_array<std::mt19937_64>)->Name("std::mt19937_64::operator() (into array)");
+BENCHMARK(BM_engine_array<std::mt19937>)->Name("std::mt19937::operator() (into array)");
+
 BENCHMARK_MAIN();
diff --git a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
index 02df0345525cb..b2ae5141edb06 100644
--- a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
@@ -20,237 +20,371 @@
 // Serializing/deserializing the state of the RNG requires iostreams
 // UNSUPPORTED: no-localization
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME && libcpp-abi-vectorized-mersenne-twister-engine
+
 #include <random>
 #include <sstream>
 #include <cassert>
 
 #include "test_macros.h"
 #if TEST_STD_VER >= 11
-#include "make_implicit.h"
-#include "test_convertible.h"
+#  include "make_implicit.h"
+#  include "test_convertible.h"
 #endif
 
 template <class T>
-std::string
-to_string(T const &e)
-{
-    std::ostringstream os;
-    os << e;
-    return os.str();
+std::string to_string(T const& e) {
+  std::ostringstream os;
+  os << e;
+  return os.str();
 }
 
-void
-test1()
-{
-    const char* a = "0 1 1812433255 1900727105 1208447044 2481403966 4042607538 337614300 "
-    "3232553940 1018809052 3202401494 1775180719 3192392114 594215549 184016991 "
-    "829906058 610491522 3879932251 3139825610 297902587 4075895579 2943625357 "
-    "3530655617 1423771745 2135928312 2891506774 1066338622 135451537 933040465 "
-    "2759011858 2273819758 3545703099 2516396728 1272276355 3172048492 "
-    "3267256201 2332199830 1975469449 392443598 1132453229 2900699076 "
-    "1998300999 3847713992 512669506 1227792182 1629110240 112303347 2142631694 "
-    "3647635483 1715036585 2508091258 1355887243 1884998310 3906360088 "
-    "952450269 3647883368 3962623343 3077504981 2023096077 3791588343 "
-    "3937487744 3455116780 1218485897 1374508007 2815569918 1367263917 "
-    "472908318 2263147545 1461547499 4126813079 2383504810 64750479 2963140275 "
-    "1709368606 4143643781 835933993 1881494649 674663333 2076403047 858036109 "
-    "1667579889 1706666497 607785554 1995775149 1941986352 3448871082 "
-    "2109910019 1474883361 1623095288 1831376534 2612738285 81681830 2204289242 "
-    "1365038485 251164610 4268495337 1805601714 1262528768 1442526919 "
-    "1675006593 965627108 646339161 499795587 840887574 380522518 3023789847 "
-    "1457635507 1947093157 2600365344 2729853143 1550618999 1390905853 "
-    "3021294812 882647559 838872117 1663880796 4222103589 2754172275 3844026123 "
-    "3199260319 4176064873 3591027019 2690294242 2978135515 3172796441 "
-    "3263669796 1451257057 1427035359 4174826006 2171992010 1537002090 "
-    "3122405306 4162452508 3271954368 3794310005 3240514581 1270412086 "
-    "3030475836 2281945856 2644171349 3109139423 4253563838 1289926431 "
-    "1396919653 733220100 2753316645 1196225013 3699575255 3569440056 "
-    "2675979228 2624079148 3463113149 863430286 623703199 2113837653 2656425919 "
-    "175981357 4271478366 4238022735 1665483419 86880610 2963435083 1830392943 "
-    "847801865 3237296945 332143967 3973606945 2671879697 2236330279 2360127810 "
-    "3283955434 203240344 4048139172 13189264 2263058814 247241371 1566765783 "
-    "3084408095 3719371299 1958375251 1985924622 1712739232 1861691451 "
-    "2644502937 2337807839 784993770 2962208780 2190810177 1523122731 "
-    "714888527 578678761 3698481324 1801168075 534650483 3390213921 3923356461 "
-    "3586009066 2059432114 52511333 1969897376 3630122061 524661135 3513619765 "
-    "563070233 501359785 477489274 658768624 938973567 1548584683 1345287459 "
-    "2488691004 3441144905 3849305094 2430000078 855172178 614463281 2092744749 "
-    "176381493 1655802051 2273888101 2474494847 3471978030 2138918303 575352373 "
-    "1658230985 1675972553 2946663114 915579339 284981499 53939948 3022598146 "
-    "1861218535 3403620774 4203516930 2360471119 3134536268 1383448498 "
-    "1307602316 3847663247 3027225131 3597251613 3186237127 725127595 "
-    "1928526954 1843386923 3560410503 54688266 1791983849 2519860352 4256389699 "
-    "2328812602 486464275 3578698363 301279829 1303654791 4181868765 971794070 "
-    "1933885487 3996807464 2144053754 4079903755 3775774765 3481760044 "
-    "1212862354 1067356423 3764189132 1609862325 2209601551 2565747501 "
-    "161962392 4045451782 2605574664 2520953090 3490240017 1082791980 44474324 "
-    "101811128 4268650669 4171338684 772375154 3920460306 2319139534 599033750 "
-    "2950874441 3373922995 1496848525 4095253594 1271943484 1498723121 "
-    "3097453329 3698082465 281869581 3148270661 3591477288 747441437 2809508504 "
-    "3896107498 303747862 2368081624 1844217645 886825352 287949781 1444561207 "
-    "2512101757 2062331723 741720931 1383797313 3876746355 2041045348 "
-    "2627599118 1124169970 200524822 3484820454 55883666 1135054804 669498692 "
-    "2677215504 3097911127 1509628615 617580381 2229022193 85601568 3243896546 "
-    "3715672328 912168347 2359163500 1180347564 4243175048 2092067103 880183327 "
-    "4000664709 2045044777 3500474644 1515175520 1862207123 186628841 "
-    "3337252925 708933575 4015964629 3136815297 3314919747 2891909013 "
-    "3316567785 3944275369 3608506218 2884839110 3054055598 2707439927 "
-    "1381111877 3275487281 4292456216 2639563270 3327301876 3576924628 "
-    "721056309 2002808140 748967365 52380958 2200261692 763456477 1708381337 "
-    "2038446433 2682979402 1526413779 2211263302 3879771969 75966584 3645059271 "
-    "2985763524 4085690255 82390958 1883631385 1647521260 1598026998 3038041577 "
-    "2501913134 3279302868 1738888524 805035483 756399074 3863810982 1097797270 "
-    "1505792529 898904527 583561003 717152376 3333867738 1099456544 1663473545 "
-    "1242141229 3828627682 1966201676 1713552361 3852160017 1584965284 21695908 "
-    "1013262144 145341901 3995441263 3462066219 2239637848 1214086163 "
-    "2428868268 1650037305 1545513388 1621198806 4232947817 1823092073 "
-    "256414624 1745018809 1357102386 2055139770 3280958307 2482431613 "
-    "1664870585 859130423 4097751123 3079768369 2470211009 2984880786 "
-    "2808568948 2877071923 1984903163 302768457 1866396789 869566317 3746415787 "
-    "4169433075 3025005404 3980733379 3539207278 3953071536 876960847 "
-    "2548872156 800507464 1865466907 1273317878 3754712872 1757188269 "
-    "3229950355 3731640200 2283390608 2204990292 411873449 447423849 1852437802 "
-    "472825525 3044219944 2913114194 1859709265 4053786194 574820536 2104496732 "
-    "865469814 2438352724 4208743605 4215067542 1364015250 4139974345 "
-    "3838747005 1818502786 2914274940 1402365828 1751123528 2302578077 "
-    "2463168652 1968705496 1730700144 3023943273 1139096844 2658667767 "
-    "2063547264 705791165 1444775274 2415454225 1575664730 921044163 648101324 "
-    "1212387162 4191962054 1787702169 1888718041 1518218010 3398792842 "
-    "4079359729 149721439 750400353 2661036076 3802767886 520152586 951852508 "
-    "2939585975 1375969109 385733137 3523607459 1902438415 4250996086 "
-    "2712727066 484493674 3932107461 1428488210 1764242548 3424801055 "
-    "4004904451 2226862072 2393366939 3609584727 3614444319 317349896 "
-    "3826527525 204023804 981902443 3356042039 3051207045 1869902661 561831895 "
-    "3706675415 1527687593 1227610446 2596341042 3191717368 3269246891 "
-    "557877074 4062070629 3052520266 3772487029 400039836 3195205275 4085394797 "
-    "1655557239 1345770144 2864727192 449281238 73189507 528365765 2727400656 "
-    "247880434 2408277395 777039183 2210179398 1088433648 2124356402 1555630141 "
-    "604790219 195012151 3312518356 923728373 3999251660 3313059535 3478133921 "
-    "3395026960 383464614 3425869222 2446885186 4032184426 157195416 3158909476 "
-    "1663750443 2046427584 1658453076 1784483001 3146546889 1238739785 "
-    "2297306523 3472330897 2953326031 2421672215 1221694592 1588568605 "
-    "2546987845 3375168573 2137961649 3056565164 330165219 235900365 1000384800 "
-    "2697255904 579122283 3050664825 73426122 1232986102 2940571064 3076486824 "
-    "1708182873 2796363264 292154131 4280019913 1102652157 1185393592 "
-    "1494991690 4270076389 2384840717 425785147 2385321880 317514772 3926962743 "
-    "392176856 3465421709 1878853468 122662664 2958252160 1858961315 2244939588 "
-    "2361884409 2860936803 683833250 3291277128 1686857206 1112632275 "
-    "1200680507 3342928196 2677058150 939442136 3407104669 2906783932 "
-    "3668048733 2030009470 1910839172 1234925283 3575831445 123595418 "
-    "2362440495 3048484911 1796872496";
-    std::mt19937 e1(0);
-    assert(to_string(e1) == a);
+void test1() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "0 1 1812433255 1900727105 1208447044 2481403966 4042607538 337614300 "
+      "3232553940 1018809052 3202401494 1775180719 3192392114 594215549 184016991 "
+      "829906058 610491522 3879932251 3139825610 297902587 4075895579 2943625357 "
+      "3530655617 1423771745 2135928312 2891506774 1066338622 135451537 933040465 "
+      "2759011858 2273819758 3545703099 2516396728 1272276355 3172048492 "
+      "3267256201 2332199830 1975469449 392443598 1132453229 2900699076 "
+      "1998300999 3847713992 512669506 1227792182 1629110240 112303347 2142631694 "
+      "3647635483 1715036585 2508091258 1355887243 1884998310 3906360088 "
+      "952450269 3647883368 3962623343 3077504981 2023096077 3791588343 "
+      "3937487744 3455116780 1218485897 1374508007 2815569918 1367263917 "
+      "472908318 2263147545 1461547499 4126813079 2383504810 64750479 2963140275 "
+      "1709368606 4143643781 835933993 1881494649 674663333 2076403047 858036109 "
+      "1667579889 1706666497 607785554 1995775149 1941986352 3448871082 "
+      "2109910019 1474883361 1623095288 1831376534 2612738285 81681830 2204289242 "
+      "1365038485 251164610 4268495337 1805601714 1262528768 1442526919 "
+      "1675006593 965627108 646339161 499795587 840887574 380522518 3023789847 "
+      "1457635507 1947093157 2600365344 2729853143 1550618999 1390905853 "
+      "3021294812 882647559 838872117 1663880796 4222103589 2754172275 3844026123 "
+      "3199260319 4176064873 3591027019 2690294242 2978135515 3172796441 "
+      "3263669796 1451257057 1427035359 4174826006 2171992010 1537002090 "
+      "3122405306 4162452508 3271954368 3794310005 3240514581 1270412086 "
+      "3030475836 2281945856 2644171349 3109139423 4253563838 1289926431 "
+      "1396919653 733220100 2753316645 1196225013 3699575255 3569440056 "
+      "2675979228 2624079148 3463113149 863430286 623703199 2113837653 2656425919 "
+      "175981357 4271478366 4238022735 1665483419 86880610 2963435083 1830392943 "
+      "847801865 3237296945 332143967 3973606945 2671879697 2236330279 2360127810 "
+      "3283955434 203240344 4048139172 13189264 2263058814 247241371 1566765783 "
+      "3084408095 3719371299 1958375251 1985924622 1712739232 1861691451 "
+      "2644502937 2337807839 784993770 2962208780 2190810177 1523122731 "
+      "714888527 578678761 3698481324 1801168075 534650483 3390213921 3923356461 "
+      "3586009066 2059432114 52511333 1969897376 3630122061 524661135 3513619765 "
+      "563070233 501359785 477489274 658768624 938973567 1548584683 1345287459 "
+      "2488691004 3441144905 3849305094 2430000078 855172178 614463281 2092744749 "
+      "176381493 1655802051 2273888101 2474494847 3471978030 2138918303 575352373 "
+      "1658230985 1675972553 2946663114 915579339 284981499 53939948 3022598146 "
+      "1861218535 3403620774 4203516930 2360471119 3134536268 1383448498 "
+      "1307602316 3847663247 3027225131 3597251613 3186237127 725127595 "
+      "1928526954 1843386923 3560410503 54688266 1791983849 2519860352 4256389699 "
+      "2328812602 486464275 3578698363 301279829 1303654791 4181868765 971794070 "
+      "1933885487 3996807464 2144053754 4079903755 3775774765 3481760044 "
+      "1212862354 1067356423 3764189132 1609862325 2209601551 2565747501 "
+      "161962392 4045451782 2605574664 2520953090 3490240017 1082791980 44474324 "
+      "101811128 4268650669 4171338684 772375154 3920460306 2319139534 599033750 "
+      "2950874441 3373922995 1496848525 4095253594 1271943484 1498723121 "
+      "3097453329 3698082465 281869581 3148270661 3591477288 747441437 2809508504 "
+      "3896107498 303747862 2368081624 1844217645 886825352 287949781 1444561207 "
+      "2512101757 2062331723 741720931 1383797313 3876746355 2041045348 "
+      "2627599118 1124169970 200524822 3484820454 55883666 1135054804 669498692 "
+      "2677215504 3097911127 1509628615 617580381 2229022193 85601568 3243896546 "
+      "3715672328 912168347 2359163500 1180347564 4243175048 2092067103 880183327 "
+      "4000664709 2045044777 3500474644 1515175520 1862207123 186628841 "
+      "3337252925 708933575 4015964629 3136815297 3314919747 2891909013 "
+      "3316567785 3944275369 3608506218 2884839110 3054055598 2707439927 "
+      "1381111877 3275487281 4292456216 2639563270 3327301876 3576924628 "
+      "721056309 2002808140 748967365 52380958 2200261692 763456477 1708381337 "
+      "2038446433 2682979402 1526413779 2211263302 3879771969 75966584 3645059271 "
+      "2985763524 4085690255 82390958 1883631385 1647521260 1598026998 3038041577 "
+      "2501913134 3279302868 1738888524 805035483 756399074 3863810982 1097797270 "
+      "1505792529 898904527 583561003 717152376 3333867738 1099456544 1663473545 "
+      "1242141229 3828627682 1966201676 1713552361 3852160017 1584965284 21695908 "
+      "1013262144 145341901 3995441263 3462066219 2239637848 1214086163 "
+      "2428868268 1650037305 1545513388 1621198806 4232947817 1823092073 "
+      "256414624 1745018809 1357102386 2055139770 3280958307 2482431613 "
+      "1664870585 859130423 4097751123 3079768369 2470211009 2984880786 "
+      "2808568948 2877071923 1984903163 302768457 1866396789 869566317 3746415787 "
+      "4169433075 3025005404 3980733379 3539207278 3953071536 876960847 "
+      "2548872156 800507464 1865466907 1273317878 3754712872 1757188269 "
+      "3229950355 3731640200 2283390608 2204990292 411873449 447423849 1852437802 "
+      "472825525 3044219944 2913114194 1859709265 4053786194 574820536 2104496732 "
+      "865469814 2438352724 4208743605 4215067542 1364015250 4139974345 "
+      "3838747005 1818502786 2914274940 1402365828 1751123528 2302578077 "
+      "2463168652 1968705496 1730700144 3023943273 1139096844 2658667767 "
+      "2063547264 705791165 1444775274 2415454225 1575664730 921044163 648101324 "
+      "1212387162 4191962054 1787702169 1888718041 1518218010 3398792842 "
+      "4079359729 149721439 750400353 2661036076 3802767886 520152586 951852508 "
+      "2939585975 1375969109 385733137 3523607459 1902438415 4250996086 "
+      "2712727066 484493674 3932107461 1428488210 1764242548 3424801055 "
+      "4004904451 2226862072 2393366939 3609584727 3614444319 317349896 "
+      "3826527525 204023804 981902443 3356042039 3051207045 1869902661 561831895 "
+      "3706675415 1527687593 1227610446 2596341042 3191717368 3269246891 "
+      "557877074 4062070629 3052520266 3772487029 400039836 3195205275 4085394797 "
+      "1655557239 1345770144 2864727192 449281238 73189507 528365765 2727400656 "
+      "247880434 2408277395 777039183 2210179398 1088433648 2124356402 1555630141 "
+      "604790219 195012151 3312518356 923728373 3999251660 3313059535 3478133921 "
+      "3395026960 383464614 3425869222 2446885186 4032184426 157195416 3158909476 "
+      "1663750443 2046427584 1658453076 1784483001 3146546889 1238739785 "
+      "2297306523 3472330897 2953326031 2421672215 1221694592 1588568605 "
+      "2546987845 3375168573 2137961649 3056565164 330165219 235900365 1000384800 "
+      "2697255904 579122283 3050664825 73426122 1232986102 2940571064 3076486824 "
+      "1708182873 2796363264 292154131 4280019913 1102652157 1185393592 "
+      "1494991690 4270076389 2384840717 425785147 2385321880 317514772 3926962743 "
+      "392176856 3465421709 1878853468 122662664 2958252160 1858961315 2244939588 "
+      "2361884409 2860936803 683833250 3291277128 1686857206 1112632275 "
+      "1200680507 3342928196 2677058150 939442136 3407104669 2906783932 "
+      "3668048733 2030009470 1910839172 1234925283 3575831445 123595418 "
+      "2362440495 3048484911 1796872496"
+#else
+      "2443250962 1093594115 1878467924 2709361018 1101979660 3904844661 676747479 2085143622 1056793272 3812477442 "
+      "2168787041 275552121 2696932952 3432054210 1657102335 3518946594 962584079 1051271004 3806145045 1414436097 "
+      "2032348584 1661738718 1116708477 2562755208 3176189976 696824676 2399811678 3992505346 569184356 2626558620 "
+      "136797809 4273176064 296167901 3430730584 331909803 1908676996 1950065095 604298543 3615988338 1570232852 "
+      "1028209748 1511467721 2411887154 4210753555 3096762720 423429618 659966766 2937509307 2222847265 378636552 "
+      "1142109618 2509241601 1521729757 888533219 250885260 2455816244 4046047811 1947467789 1395351953 2388948566 "
+      "934627940 194642258 1429256273 2139959677 1543740405 1569613451 4061840539 2075690423 824532376 844152077 "
+      "3218002536 897315311 823414659 1007534698 3139313867 2332794462 3675156583 722594429 3715123011 2656840676 "
+      "2373277715 1516144243 1538161999 1399719297 1448820876 3180842436 2020192709 3279498509 1048107540 3094290376 "
+      "3706920703 3810051683 4003871775 1069557309 151535004 667695756 861767313 1643171871 3649507728 3785763588 "
+      "737899497 2332908276 4082123854 1584539673 3929877280 1046086041 1304062606 2311324008 1190424879 538647603 "
+      "1728806718 148948582 659721465 2351006009 185647941 1825934330 2126619619 2230539295 3878421575 965937340 "
+      "2835563987 419858879 1527311104 2042333483 2745134265 3540517373 1100062165 2308613697 2690739856 2048293545 "
+      "2742876486 2408502115 53398423 3089771461 1398529613 2132477261 504134301 1535108421 1968507173 3412472770 "
+      "4048517196 3509143071 857063211 1429875570 4120479871 2786574360 2470799871 1644104875 2326863162 2033900387 "
+      "274888909 2629973384 1154405937 1839988197 2163830694 268733679 2928953965 387153042 3790900490 3202349717 "
+      "3799209681 2524871464 3801447311 3543081470 1801633977 3870370950 1581678156 358283485 4133385582 4052311650 "
+      "249498956 1712028623 3618329357 3395106946 2704820515 26653208 2437509555 4192186371 3639946838 2615917799 "
+      "294624699 463886267 2477203673 2514051526 4162882381 2937629358 3180524454 1388392394 2637132139 1999482082 "
+      "1870663339 2852309250 3489430700 1116063402 1673637188 1941955790 4081919937 2322920953 3500518887 2722575905 "
+      "405031829 3728964889 3464559963 665516555 1626835870 2523729468 238289561 497556937 2581429740 3456619478 "
+      "2601389837 820426192 268141532 2654829421 349692193 2426957585 1465734488 85095426 1080895536 2823366942 "
+      "379322515 2938986739 1561328720 2940081697 607513620 2724557130 2841377034 14123440 1085631349 1977859413 "
+      "1325586902 1690398943 2516154740 4144373551 555899232 1472707937 3318086852 719779833 3545481907 1384350857 "
+      "191709822 2924586997 3905397527 2528674757 2377804096 2742818576 4162971050 1916415976 2229076768 132706400 "
+      "1337415646 251604474 4168783505 819499074 1213291699 2098930479 1009044052 1060792037 2169940733 2973281031 "
+      "636656529 1946890813 367099469 4076624283 338339529 561343735 3309733580 3895891229 516195845 3075613489 "
+      "3076388119 4088758753 3895582245 1192445752 2923797073 2272605437 2964193665 943979628 3263904824 1852145108 "
+      "1911050628 1596880711 479795791 212699077 2172697748 1764175804 3951845064 2182587171 2396848133 2718837214 "
+      "2934177572 3638054152 914795167 1028977354 1744985339 1161407853 2069746822 3109934340 3666150054 729399111 "
+      "2911998815 152809615 417267263 1064343325 2751707923 1833270972 886939906 4054231201 1410366452 976142646 "
+      "1452795786 1904234879 4231894541 1505392175 3498767279 833704604 2100592828 688268099 1752675602 1975556664 "
+      "2102210733 698665709 1232104968 4051336323 1740516649 3131721670 3755143232 3433756601 138325540 1566724819 "
+      "3146312914 3262098346 1400757929 3821885720 356740291 4236900846 3962771215 3383537419 3432281817 3700484231 "
+      "1124512300 4040103150 2152814065 357241942 3507144572 3156778000 4029389048 1495464238 2710816120 618937484 "
+      "923913256 3233161411 3113338135 2380534800 2989134055 792458413 2073679910 2733527896 1078384210 2832175364 "
+      "501385130 1554930792 2339252603 535487294 2983664186 924228889 2127502206 2847205678 836935943 1902192892 "
+      "4094716961 3296730761 2307406435 281523156 728701372 3943302480 183005637 2660706047 2320033913 2011717602 "
+      "219970222 890345073 2479236311 653839655 38538414 3266308455 3726363269 1256762826 712114913 3499059198 "
+      "2847394904 3246075282 1299462737 830090526 369065487 3899451086 1825910875 2697294244 2553197317 2501134241 "
+      "930821716 2841281141 3920808923 3209045349 3381621736 2145077949 1958091404 371971179 3064957112 3151429184 "
+      "2833651995 1089438243 2146134351 1426568997 3528669710 1043897757 3052263589 3205925012 4076215371 2622476950 "
+      "2385717100 2179892838 3820135605 1574628644 177604240 1712062317 1192081671 1107476027 837376039 2818244460 "
+      "1382379731 2749207910 2644203474 1508860349 3006844699 3157252375 3789683778 1985542020 2309789126 1624089624 "
+      "3660683301 1432444043 1172735898 3271587635 841387671 3614623669 3002115850 1078807738 1095345284 1707086713 "
+      "4263485053 445506874 3148772337 2716917408 3836812085 1026228926 491069724 3983218285 2410582206 3821759029 "
+      "3789305873 1223256738 1677180485 1481771733 1670286271 2852111711 1628943665 2289772949 3483580787 2129860019 "
+      "3955695109 28974017 2289227173 1725243773 483357125 111587742 347631238 1805029491 839978009 339030914 "
+      "2442782449 1701726526 1481742442 3703534067 3796147861 235078546 3195450129 1420184214 998981944 163541155 "
+      "2087693215 3827554748 3003331872 1967967170 160190917 544473278 3884602428 4180513186 1603446876 2191744556 "
+      "1938251010 291242497 1764195107 4076051069 3860380798 806452940 2631256196 949838208 4096577802 1519225265 "
+      "2296800559 1015848400 2864792481 88004533 195132439 966918321 1451347413 2322994051 3585658769 1771598947 "
+      "3308395968 950148946 3846061568 564475171 1345892193 2439581032 2907206869 1297626422 3779454571 818255727 "
+      "705738065 3915384184 4133726754 2724859327 3773743817 326325901 2766390048 2562130418 1885210076 2622324981 "
+      "1727396790 1742653294 3133131942 3790857080 197042473 2868410679 1122976862 2264474389 3607393378 744386048 "
+      "3122128592 1101489146 4064675687 2818166901 2282076661 370818870 3053423257 2272652338 4077081984 4284213290 "
+      "1728304803 3902867896 1643530236 306212760 144705843 498541197 3103269575 4116304893 1646288275 2110757680 "
+      "4092919505 3390929578 1828116798 1847914177 1156754878 2985404552 3970881126 3035874635 2189824678 2637114092 "
+      "2588332748 3917818803 3302695646 1758418726 1934759438 3871576937 1360769189 1051808374 1881390541 757367126 "
+      "3253778407 1685049720 805311189 934898036 95682794 2052240235 3475933669 2213474191 3341273357 2443286051 "
+      "4272536794 696819230 878775027 3332634155 3405126958 2824772573 95284505 3040640920 2412859827 2959515765 "
+      "1764792340 1504924482 2116910326 2293943003 3199262907 1443740894 3900566588 343066614 1927475335 249220892 "
+      "2920636502 3593328327 2648828502 1678096082"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  std::mt19937 e1(0);
+  LIBCPP_ASSERT(to_string(e1) == a);
+  (void)a;
 }
 
-void
-test2()
-{
-    const char* a = "0 1 6364136223846793007 13885033948157127961 "
-    "15324573939901584278 12737837167382305846 15195339788985155882 "
-    "6554113247712070460 17235932740818599105 13007415075556305955 "
-    "6585479514541334743 8274505865835507625 1718218088692873364 "
-    "10390651247454232081 12911994993614796389 3986900029987203370 "
-    "6673827651897561714 4426752746717694792 7419158062930293690 "
-    "5800047417539173618 15710773105226458059 16164512590413496893 "
-    "3438015953120274172 3483801391287623267 293704481016263807 "
-    "11580856846363212652 3489109114147636336 3391036861618673611 "
-    "8265793309278544843 7557898467821912223 11008748280761875940 "
-    "15929443707841919885 8545695347411085846 10810459396490399532 "
-    "12233244910455127352 15556950738631379285 16711543556686614082 "
-    "12362193084052127890 16520645558585805174 5163125267185202360 "
-    "405552980610370477 17567412011316060306 18195950784827697319 "
-    "7893142112162906367 11294475722810961618 7284845498332539581 "
-    "8406882439401998138 4375387785957411470 9627875716250684710 "
-    "8860968026642934661 9743109216691708518 152611520104818631 "
-    "5897430410700879663 5351672954305365323 16325991383734641720 "
-    "9695181037355459478 15420132328343498044 17772146581546890572 "
-    "12095930435311226909 3066005052193896110 11579395203346116306 "
-    "9168946227698330317 18318927644793076250 16096433325093805476 "
-    "14945900876820434843 16826760579960858105 17664119339058295604 "
-    "17844797344364136942 1071414400549507565 16688779616725465582 "
-    "3684635020921274863 12774052823168116810 17270284502989966576 "
-    "1081012692742984704 4377021575203177546 18341292555997099853 "
-    "13297939683513494274 15065725504474304333 10796297883750572804 "
-    "15233335271871291997 8767977593699151062 3360856014170688284 "
-    "7828232912764786750 15167717223619970150 9622174963375022357 "
-    "18262792478991268448 1196631425707106493 5368342740538672272 "
-    "10381091599850241237 12108437846306626340 6150860188928778248 "
-    "3342980288459577584 12715439159457051276 17996971042887275859 "
-    "9749679821487730542 17763727344608586331 16024467606290078168 "
-    "7763401887585513696 4448278918811662063 16947956613780322662 "
-    "15144807360840708645 3878082612940188435 10709780449699561405 "
-    "1649555943517500922 3206645931693769562 12562913950237146427 "
-    "237213339742767727 12987800257476421358 1653669854585203688 "
-    "3485900643226898485 13961759114404652223 5243794832751327611 "
-    "10337687908642742498 16946139522050041809 16716562961992396380 "
-    "4275124606042261542 4055100795824867618 6424268654905981295 "
-    "3424516503413156556 2670380025813203539 10750762735193959951 "
-    "8790031149370411970 4021216986392972993 12076090355041998696 "
-    "14407920322903159838 10653597737935867030 15483225617438352002 "
-    "2497775263858626604 12295882369431088188 14256043521530136935 "
-    "2687322778627883798 3419797801078863201 8786888481486602641 "
-    "445698423634900693 9597067954623467255 7101345576557603992 "
-    "1498579197046783597 10403325187679734962 2464586980321053562 "
-    "2022012026329844477 10802281218030350853 6628929099856200904 "
-    "6828177972863192803 8589868113309334601 5245595233272009016 "
-    "5335692004673212054 4515133017699498525 15966447436053813932 "
-    "15199779177078162007 4190689609934804313 13003438276435994683 "
-    "8406046831313066396 10564320513686955057 12668913223662201488 "
-    "13130110932487580228 1030848205404711145 17684061609212954769 "
-    "12942207438298787911 10731611242140874687 5165052527778107352 "
-    "16323046249518133445 17119162873327029615 5754858052433703070 "
-    "3864761150247579030 9945988334920003074 11409854727071782565 "
-    "5000838138362434817 15526574143469400487 18094554078711846524 "
-    "5576294272011007484 3478525338408894755 11392694223389544658 "
-    "4692963068671452476 4459301637730340710 9699395817392066460 "
-    "14644636990626292085 18065377773424192622 5217202490849387226 "
-    "16175595974171756081 2109372019589047677 1624752883142646445 "
-    "13462209973053735966 12082930933973802402 1568864426788967895 "
-    "17047994306870001795 10556833209957537593 955604103878351641 "
-    "9062985603395234592 9757612676622840969 1767246562613391916 "
-    "9752598821733361274 7499745701633625047 7824811626141302622 "
-    "15819064077972391284 5660565551854829485 17645390577243129343 "
-    "7343780801046581776 2233358068547689666 8716657172695403744 "
-    "9129027798969787220 334709674395230649 2063182499026924878 "
-    "13089071159640936832 1765917316143960741 17552378408917656269 "
-    "3917018959478722819 15626740210483166037 1645962609209923821 "
-    "12277169606472643961 14545894350924442736 11485249378718653961 "
-    "9205208816702766530 10967561305613932827 3105992977398681914 "
-    "2125140299311648264 11619505070220308543 5030167448920096401 "
-    "4248170446421798953 16184577688118775567 9240607582885304823 "
-    "11838996733938359277 415426114101983968 14340734742548675134 "
-    "4124085748228276376 17686494750190224280 9472996569628985376 "
-    "1207013222233148636 3031046462562068367 45068538181330439 "
-    "8678647417835301152 10693327126492781235 3058899219097846020 "
-    "18377730418355022492 10269941972656742364 15986476992758938864 "
-    "14575856764093007010 14749682846448398393 1042926396621439263 "
-    "12184905641567868001 3518848236485931862 6718580865438347534 "
-    "6319395993260741012 2855168874111910691 2482146419106261786 "
-    "17290238774162848390 8071397865265268054 15873003794708011585 "
-    "14422764926380465297 14140979091525022882 3573480287238168646 "
-    "1525896111244666696 7537826952717918371 10482908122538761078 "
-    "17003233041653857 9473838740924911883 8438240966750123668 "
-    "10697754962581554225 15048771265786776312 9067877678399943713 "
-    "3399555692325948067 6150260207049997483 7165140289246675175 "
-    "14816202987105583988 4753550992948864498 10549400354582510015 "
-    "13212062554023586370 1585477630313819722 476999696494664205 "
-    "3208739183359199317 16011681780347380478 8149150693959772807 "
-    "803412833228911773 2360961455092949929 1517204230639934662 "
-    "13863717544358808032 16792122738584967930 12742474971940259289 "
-    "1859755681395355028 1540431035241590810 3883807554140904361 "
-    "16189061625447625933 12376367376041900879 8006563585771266211 "
-    "2682617767291542421 8593924529704142157 9070391845133329273 "
-    "3557484410564396342 9301398051805853085 12632514768298643219 "
-    "227653509634201118 7247795074312488552 4939136716843120792 "
-    "6533591052147596041 1308401457054431629 17488144120120152559 "
-    "14075631579093810083 4015705597302725704 6833920126528811473 "
-    "5095302940809114298 8312250184258072842 15770605014574863643 "
-    "14091690436120485477 15763282477731738396 16394237160547425954 "
-    "5066318118328746621 13140493775100916989 6371148952471982853 "
-    "15150289760867914983 4931341382074091848 12635920082410445322 "
-    "8498109357807439006 14836776625250834986";
-    std::mt19937_64 e1(0);
-    assert(to_string(e1) == a);
+void test2() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "0 1 6364136223846793007 13885033948157127961 "
+      "15324573939901584278 12737837167382305846 15195339788985155882 "
+      "6554113247712070460 17235932740818599105 13007415075556305955 "
+      "6585479514541334743 8274505865835507625 1718218088692873364 "
+      "10390651247454232081 12911994993614796389 3986900029987203370 "
+      "6673827651897561714 4426752746717694792 7419158062930293690 "
+      "5800047417539173618 15710773105226458059 16164512590413496893 "
+      "3438015953120274172 3483801391287623267 293704481016263807 "
+      "11580856846363212652 3489109114147636336 3391036861618673611 "
+      "8265793309278544843 7557898467821912223 11008748280761875940 "
+      "15929443707841919885 8545695347411085846 10810459396490399532 "
+      "12233244910455127352 15556950738631379285 16711543556686614082 "
+      "12362193084052127890 16520645558585805174 5163125267185202360 "
+      "405552980610370477 17567412011316060306 18195950784827697319 "
+      "7893142112162906367 11294475722810961618 7284845498332539581 "
+      "8406882439401998138 4375387785957411470 9627875716250684710 "
+      "8860968026642934661 9743109216691708518 152611520104818631 "
+      "5897430410700879663 5351672954305365323 16325991383734641720 "
+      "9695181037355459478 15420132328343498044 17772146581546890572 "
+      "12095930435311226909 3066005052193896110 11579395203346116306 "
+      "9168946227698330317 18318927644793076250 16096433325093805476 "
+      "14945900876820434843 16826760579960858105 17664119339058295604 "
+      "17844797344364136942 1071414400549507565 16688779616725465582 "
+      "3684635020921274863 12774052823168116810 17270284502989966576 "
+      "1081012692742984704 4377021575203177546 18341292555997099853 "
+      "13297939683513494274 15065725504474304333 10796297883750572804 "
+      "15233335271871291997 8767977593699151062 3360856014170688284 "
+      "7828232912764786750 15167717223619970150 9622174963375022357 "
+      "18262792478991268448 1196631425707106493 5368342740538672272 "
+      "10381091599850241237 12108437846306626340 6150860188928778248 "
+      "3342980288459577584 12715439159457051276 17996971042887275859 "
+      "9749679821487730542 17763727344608586331 16024467606290078168 "
+      "7763401887585513696 4448278918811662063 16947956613780322662 "
+      "15144807360840708645 3878082612940188435 10709780449699561405 "
+      "1649555943517500922 3206645931693769562 12562913950237146427 "
+      "237213339742767727 12987800257476421358 1653669854585203688 "
+      "3485900643226898485 13961759114404652223 5243794832751327611 "
+      "10337687908642742498 16946139522050041809 16716562961992396380 "
+      "4275124606042261542 4055100795824867618 6424268654905981295 "
+      "3424516503413156556 2670380025813203539 10750762735193959951 "
+      "8790031149370411970 4021216986392972993 12076090355041998696 "
+      "14407920322903159838 10653597737935867030 15483225617438352002 "
+      "2497775263858626604 12295882369431088188 14256043521530136935 "
+      "2687322778627883798 3419797801078863201 8786888481486602641 "
+      "445698423634900693 9597067954623467255 7101345576557603992 "
+      "1498579197046783597 10403325187679734962 2464586980321053562 "
+      "2022012026329844477 10802281218030350853 6628929099856200904 "
+      "6828177972863192803 8589868113309334601 5245595233272009016 "
+      "5335692004673212054 4515133017699498525 15966447436053813932 "
+      "15199779177078162007 4190689609934804313 13003438276435994683 "
+      "8406046831313066396 10564320513686955057 12668913223662201488 "
+      "13130110932487580228 1030848205404711145 17684061609212954769 "
+      "12942207438298787911 10731611242140874687 5165052527778107352 "
+      "16323046249518133445 17119162873327029615 5754858052433703070 "
+      "3864761150247579030 9945988334920003074 11409854727071782565 "
+      "5000838138362434817 15526574143469400487 18094554078711846524 "
+      "5576294272011007484 3478525338408894755 11392694223389544658 "
+      "4692963068671452476 4459301637730340710 9699395817392066460 "
+      "14644636990626292085 18065377773424192622 5217202490849387226 "
+      "16175595974171756081 2109372019589047677 1624752883142646445 "
+      "13462209973053735966 12082930933973802402 1568864426788967895 "
+      "17047994306870001795 10556833209957537593 955604103878351641 "
+      "9062985603395234592 9757612676622840969 1767246562613391916 "
+      "9752598821733361274 7499745701633625047 7824811626141302622 "
+      "15819064077972391284 5660565551854829485 17645390577243129343 "
+      "7343780801046581776 2233358068547689666 8716657172695403744 "
+      "9129027798969787220 334709674395230649 2063182499026924878 "
+      "13089071159640936832 1765917316143960741 17552378408917656269 "
+      "3917018959478722819 15626740210483166037 1645962609209923821 "
+      "12277169606472643961 14545894350924442736 11485249378718653961 "
+      "9205208816702766530 10967561305613932827 3105992977398681914 "
+      "2125140299311648264 11619505070220308543 5030167448920096401 "
+      "4248170446421798953 16184577688118775567 9240607582885304823 "
+      "11838996733938359277 415426114101983968 14340734742548675134 "
+      "4124085748228276376 17686494750190224280 9472996569628985376 "
+      "1207013222233148636 3031046462562068367 45068538181330439 "
+      "8678647417835301152 10693327126492781235 3058899219097846020 "
+      "18377730418355022492 10269941972656742364 15986476992758938864 "
+      "14575856764093007010 14749682846448398393 1042926396621439263 "
+      "12184905641567868001 3518848236485931862 6718580865438347534 "
+      "6319395993260741012 2855168874111910691 2482146419106261786 "
+      "17290238774162848390 8071397865265268054 15873003794708011585 "
+      "14422764926380465297 14140979091525022882 3573480287238168646 "
+      "1525896111244666696 7537826952717918371 10482908122538761078 "
+      "17003233041653857 9473838740924911883 8438240966750123668 "
+      "10697754962581554225 15048771265786776312 9067877678399943713 "
+      "3399555692325948067 6150260207049997483 7165140289246675175 "
+      "14816202987105583988 4753550992948864498 10549400354582510015 "
+      "13212062554023586370 1585477630313819722 476999696494664205 "
+      "3208739183359199317 16011681780347380478 8149150693959772807 "
+      "803412833228911773 2360961455092949929 1517204230639934662 "
+      "13863717544358808032 16792122738584967930 12742474971940259289 "
+      "1859755681395355028 1540431035241590810 3883807554140904361 "
+      "16189061625447625933 12376367376041900879 8006563585771266211 "
+      "2682617767291542421 8593924529704142157 9070391845133329273 "
+      "3557484410564396342 9301398051805853085 12632514768298643219 "
+      "227653509634201118 7247795074312488552 4939136716843120792 "
+      "6533591052147596041 1308401457054431629 17488144120120152559 "
+      "14075631579093810083 4015705597302725704 6833920126528811473 "
+      "5095302940809114298 8312250184258072842 15770605014574863643 "
+      "14091690436120485477 15763282477731738396 16394237160547425954 "
+      "5066318118328746621 13140493775100916989 6371148952471982853 "
+      "15150289760867914983 4931341382074091848 12635920082410445322 "
+      "8498109357807439006 14836776625250834986"
+#else
+      "4640967165370225016 475580846709610041 992132072322742490 2879266425597847059 9859212410995834590 "
+      "13110412116137766394 2786888605138474240 12527462973726820895 5232191189150589434 8148228587345305383 "
+      "15985190208517110588 17155057006866468333 5040233137627218077 12716217438209898279 7628670991140263350 "
+      "9633431818170735083 8029874679197696152 2546188705250129595 13105544608186384869 6197138528956131705 "
+      "2500767142257833113 4057403796030124452 4801410225133580521 12712510024708253355 1478853541812684571 "
+      "16899998798183024166 763564441705181102 13204668179160915419 6975570844831914277 12029362619687152075 "
+      "17592155920476593974 1391138604394782251 13557582389860889375 6018426101890403504 7401881601334221625 "
+      "281124061034155254 2261645520472923927 10254468173793190351 4336071085409420785 7075410027066751424 "
+      "7438312290102887001 15139389032212795768 12949861808476919158 5221320262711473213 18444574859163569422 "
+      "3327484732560520915 10371833210785813703 494705948341138486 313693460050824934 801700386546917424 "
+      "3333797513889190239 11734932229126290067 4001689285737041461 17077402128101531244 17161815342609821634 "
+      "4393649844836338524 17495132932599391165 16521868517739667677 5661222119201994079 12972222767581865814 "
+      "11571421678172213790 390646047509318948 11495394588801409501 6525248087626393555 8573656613713326328 "
+      "8142990904973493114 13660896775289126601 17864485770152980871 17445884582967639919 5024202147275854142 "
+      "671342157422491897 8265009237858722295 8608118008319642631 9219145998498351877 4556268366257199356 "
+      "6140599343918232709 1607043993756685523 16576680458922020446 2464344868820317239 11814083511457927881 "
+      "17322648412109813175 1813592025330691328 11477642670415214162 17070997401037634101 2303972879274470462 "
+      "11243326672568121379 3445882696291328363 12839802107382893721 12104481080282265364 2596243342696322386 "
+      "17790946661248112185 16074938231864255959 2956506024687822818 5581797792339843057 16395798806003821452 "
+      "1430185758107085903 18323996060167045894 9286586749315539711 11359324205220105656 13078829860258641519 "
+      "5216000878301612113 9168569275644144847 4001438364296785628 2620979336382597902 17733020247211834335 "
+      "9313982434870058041 14715751745255862275 2010565534563004422 3178715725792479308 1903043159754162676 "
+      "14115336913931799326 2521027868575181436 16014439786100458068 12367605516710589904 209714080394415764 "
+      "1624794657804038156 9935768631398762231 4151749392286673312 7107972358174928160 5660032753352365076 "
+      "18036755596310855736 10390782681895899933 1046990263302077358 7362677844682408358 9462769113228112518 "
+      "16297726431682095630 320526314299713717 3778064933596859787 10885323884409002007 2236973268193149682 "
+      "10878852310456567407 2570883140239125692 2789272264811335056 13059628436710418572 2738898868861677604 "
+      "13893534347610447431 5822583231449316688 6492049503818767408 6244748132635487608 8670290879871155016 "
+      "9029174311949188652 14279734151305645897 15912641926116463991 5242647656726280598 18355120933288718928 "
+      "6020122330537384578 14237586109975592522 4056743947737351776 11111921204484673848 2182608451873446153 "
+      "166740030032355275 6707804438883761942 946217821621517312 17920751901125513000 11238439424546078979 "
+      "9206370960702334859 10367106562255294642 16885700982121403407 5166729472772734774 12763662184890206104 "
+      "5517776110990532992 14067054651980895669 91103470530233291 13189720418381102366 13302941329281487681 "
+      "10033391968256035918 5361508361658563078 9633125940775192275 4070378705292966499 2578880557354159711 "
+      "8213829012818869727 14609024865720281973 5764529456700294955 4441867238869537141 4877335405025028278 "
+      "3719605268964114254 6912476600417191412 12213543493427784533 9772185674504838078 849905513166852628 "
+      "2288907677236569364 13249989292558739703 17027351938809896108 621729935073174643 11783397793000418128 "
+      "6535353235698302382 17474699882318514854 11002055139008150918 18416122529605421577 6901480652243894157 "
+      "10382683051257874235 4029870255315506009 2965992838295436717 6235535328826140656 12567736520515051239 "
+      "1747508188587454664 6183192989050178616 15953675909489391368 10361870655666472156 13992646052760132872 "
+      "18278663149537015721 2341145555999432211 6926699433818498172 13807760559474580665 14452983890116009870 "
+      "11892982856082687091 17809698859762328768 2068107465420962246 7113569128111994893 4401076180719847553 "
+      "11644868941271347491 13124811241626639928 13749911628127226656 17349042196015114201 17667020131618919593 "
+      "5882998107995308535 3994906988813734883 12471577435602039626 6541516905696908495 12651354739990156236 "
+      "2656551093809687944 8349751746116271717 16003262134315245189 16966957802441281611 9846484220369869695 "
+      "289040869684008784 12984844645788078807 15184274406289320733 8585588724744493207 17783251231435661237 "
+      "8433051731677026430 4615759437146530251 7622401495291460413 11623553053949964326 5539054352599742054 "
+      "8346732818944267068 2539102865177431249 12328004780676882137 14682995638339538937 17618899063173726898 "
+      "3560029508967023860 203715062184427611 4331882803273086182 11747937041982829018 14988651560527263167 "
+      "12178986862382858651 3296725696957964696 13476561205033500550 5411341409364572097 6178165960265458773 "
+      "6637587084784255540 2852138145128301044 285104174921779935 3890210696591283091 15859573696692819698 "
+      "4183398296224968094 169433174359854381 11697218950892752694 13577878976046486116 9712904827015064754 "
+      "7617616830643388997 12751433537134599555 12319041734951133946 10239102328951141680 7290886622313676141 "
+      "4739372139955959505 9070795802053402609 10714415902002968511 14446814812660765483 8195026527484180858 "
+      "10331449076861906227 11969763657717422129 11061711610463627412 3682504726309317968 186477037840564317 "
+      "10307292328882458385 11683537113360307954 11300920371008006928 12769285684201107347 14545522978223964713 "
+      "5045546601922369224 202539708050129846 9727300421085682838 10660376569580918468 1852104613210280002 "
+      "2448572796841074793 4275113756623070536 15430856097668330204 8147758262819931807 13018070382604356536 "
+      "1461468238838418536 6307427532280072746 14454983576432818303 16577253122736704814 11165141699850119488 "
+      "1871342067236738412 15245016873857982253 16807633505182897684 18414686740244106738 14154332998791555826 "
+      "2798471212441347211 3624302277914305292 12175243894993809691 18197954905920110263 869336096513266351 "
+      "17322661882612443490 11198026121992866897 3762399852280847282 3393029619669184773 12618712086407186791 "
+      "11601652706361609238 1815215914966060855"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  std::mt19937_64 e1(0);
+  LIBCPP_ASSERT(to_string(e1) == a);
+  (void)a;
 }
 
 template <class E, typename Arg>
@@ -262,13 +396,12 @@ void test_implicit() {
 #endif
 }
 
-int main(int, char**)
-{
-    test1();
-    test2();
+int main(int, char**) {
+  test1();
+  test2();
 
-    test_implicit<std::mt19937, std::uint_fast32_t>();
-    test_implicit<std::mt19937_64, std::uint_fast64_t>();
+  test_implicit<std::mt19937, std::uint_fast32_t>();
+  test_implicit<std::mt19937_64, std::uint_fast64_t>();
 
-    return 0;
+  return 0;
 }
diff --git a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
index ff028929747a6..24832330720e4 100644
--- a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
@@ -24,292 +24,425 @@
 
 #include "test_macros.h"
 
-void
-test1()
-{
-    const char* a = "358595400 4166558815 2016177162 3414480257 "
-    "4027494649 3722317195 1190532340 3212207148 "
-    "3537847251 389019999 1098708832 3277907415 "
-    "1946784350 3608286140 2091419822 2227407035 "
-    "2229110723 1825348377 1276269279 314337202 "
-    "3182935337 1313150029 3118776508 3707918501 "
-    "1900972958 4054820954 3973178248 906260237 "
-    "1403942218 3139931556 2807126524 3940936448 "
-    "1316345796 631296613 2268418920 2914000794 "
-    "3760588399 3226216036 880155129 4183611084 "
-    "211541083 3755352858 1331383234 3036493096 "
-    "937478630 2092170412 777784402 93392729 "
-    "3644029210 1681392086 2427001226 3143870332 "
-    "3703581502 2017505388 1706274541 1049329728 "
-    "2452031492 3437261233 2581204087 1700889875 "
-    "1652573881 2127047692 3778506964 1960741508 "
-    "2739602360 3395905609 2123355622 3041272975 "
-    "784200748 3558951522 1002787860 4063320888 "
-    "1587315560 4042698976 659183308 3082256417 "
-    "2808969567 2361418535 3468698782 750700970 "
-    "2991209851 3581521382 962022878 2518967363 "
-    "1476525873 3865977235 2128790058 2380326689 "
-    "1396773405 312559410 1370621899 1154499924 "
-    "2963101919 2182689761 2071851902 1661288848 "
-    "2411351341 1362764020 1289894483 1951662807 "
-    "701821506 552267185 2356648449 3949188503 "
-    "1748307081 87795201 3718396254 4112205936 "
-    "2819888864 73923733 2800033151 839258139 "
-    "3801779069 3105962436 2111266436 1772784466 "
-    "3692264298 4148810953 3147390749 3537518553 "
-    "1695044978 1430225842 1252346204 3465285434 "
-    "3970017763 2920658411 2805151132 290569815 "
-    "3802301355 1493420394 1943029276 1667143611 "
-    "1049665988 1710824905 220168517 3997946231 "
-    "1014582791 4244598752 1147604069 2533886627 "
-    "598679964 761521020 431779255 3745982038 "
-    "768658283 3598262505 1765664789 279538641 "
-    "715144305 2371628432 2655860083 1759010423 "
-    "3568452003 1910107098 2801429529 3924547532 "
-    "3862672436 3933725740 1764550618 130893617 "
-    "1460692387 4135312761 2075529299 2880227367 "
-    "944557368 4166665482 2627749235 3732013815 "
-    "1595900818 1553312393 3529311831 3531462424 "
-    "2431328342 4075369692 1609967709 3704537555 "
-    "2067297464 397140475 920618678 2840795964 "
-    "4202512837 1286017648 7035910 1057207826 "
-    "2325188262 191593698 3697383848 3029712831 "
-    "2073681914 163454681 1329637200 290077398 "
-    "287239431 4205081522 1233889024 167173087 "
-    "3267660257 3406068803 2382354609 1680046927 "
-    "125183503 3559536309 3208900974 2912148541 "
-    "2882879316 1937001086 2919729069 892928802 "
-    "4141691387 2507406586 855548593 3418647837 "
-    "4035646154 2410275591 248715645 3180757482 "
-    "1880770722 362912336 2964920095 2319904154 "
-    "1493655850 4240733030 1834485846 1696040454 "
-    "3329457927 1865824694 847759208 1587231623 "
-    "3757294772 1161601118 3630323833 3007722125 "
-    "3726418007 2124238171 1205345 172659797 "
-    "3040354211 885213338 1857049013 447922412 "
-    "719906299 1370059380 1922204800 3960090489 "
-    "1658822644 1529626863 1565927273 3537718771 "
-    "2733237258 2180221377 921910745 2144937687 "
-    "1727603895 1315635304 4023867791 2401834107 "
-    "808854185 2408824497 343935326 185237544 "
-    "746732759 2641236122 4283215329 743609415 "
-    "1134726665 3892851319 1302851263 3473445597 "
-    "1326817414 2702766508 1943179285 4025685468 "
-    "932896770 199392138 2787362875 3450501893 "
-    "3351567147 2461286528 2227605848 2993751114 "
-    "3988215720 1320573368 2866560199 4153194990 "
-    "3007120042 3260751955 3171763740 2111121243 "
-    "3962825228 102681859 3368179132 802089147 "
-    "4029273561 424939445 4178414761 2592125109 "
-    "1960801088 2967746406 310701990 2364200202 "
-    "1320507009 3474372480 784693947 2952246664 "
-    "1891935330 2048385105 3530082191 3238151038 "
-    "3293189141 1316053288 2087004409 740799958 "
-    "1187748554 3607767334 1190185990 1408429481 "
-    "657134359 221834425 3907725865 1068016389 "
-    "1402423875 2598612116 2046886300 2345022518 "
-    "1196081924 357783981 4013683598 463491626 "
-    "3269206482 3332444286 886491955 2257342866 "
-    "475911113 833576299 2893727564 2866985145 "
-    "1413365115 2995166393 1486060436 161205225 "
-    "3181728373 3056027137 2040371876 2182305146 "
-    "3028448628 2214316977 1266227021 876938740 "
-    "276477469 752158077 2182179045 1381698878 "
-    "3424557652 666674427 968327842 2534296575 "
-    "265105940 961112540 2641188117 2319139814 "
-    "1750453329 3450138343 678025317 1477566458 "
-    "3773796420 2933993832 3326042905 4084805260 "
-    "444182455 255333481 785163068 2321290820 "
-    "2893603234 3005520266 541104079 1383277090 "
-    "2770755666 3764627833 583371929 2864949033 "
-    "1487681116 1811788361 240329486 3094213377 "
-    "958509875 2564379085 1636995945 2070894127 "
-    "2139004232 1747850055 3841512327 3325011872 "
-    "1161622604 639182193 3533652535 4022667522 "
-    "761048999 3337743670 254221568 2784956233 "
-    "2990252814 4207922787 275707208 261819597 "
-    "2071467265 4034945770 1999813410 3038921100 "
-    "2200194573 1328217451 2440612380 3862293692 "
-    "2733976812 2750523058 2920082515 3809044908 "
-    "4285231753 3131963297 3481602724 1396460739 "
-    "2011767965 2481047764 2958452120 3044188618 "
-    "2217236658 3448605566 757716104 1818279145 "
-    "2641228144 1312649639 1194087684 3845229729 "
-    "1747658356 874418803 1956637337 268670179 "
-    "2083040240 2577671381 3375334655 2587828868 "
-    "1383012799 3583445685 2594576715 3282337104 "
-    "4257972751 3440488071 3129180313 1830891395 "
-    "1594931092 2680778339 3984026324 1102770400 "
-    "2315820258 1263467048 1133254110 2400676748 "
-    "2251795328 1036154092 3313541051 2277356560 "
-    "1477696003 1417117088 3968537402 1404882202 "
-    "2011058180 4114080985 1727459502 4100235708 "
-    "2334509310 2829432554 377936301 1519324520 "
-    "3252826644 1193335837 1929125820 2165344238 "
-    "4160556243 223340988 670907625 1485396519 "
-    "936389509 3813712964 2706450987 3132506320 "
-    "875886515 557088991 2854916639 2955496008 "
-    "2881696287 265169077 3239923698 3649366121 "
-    "4072165960 1233904959 225406526 1767368993 "
-    "1894882500 2296582180 339255168 83200939 "
-    "2958376148 4100205346 1991250823 3806183082 "
-    "2691709980 2642354997 3024056146 1681065839 "
-    "3438299684 1638853652 362567001 2307868899 "
-    "988801086 1342833399 2303298376 1500039911 "
-    "765489391 4080464497 4155444368 980472018 "
-    "2026981853 3460406995 391970367 667377014 "
-    "4177754853 2657468948 3560690175 3030464357 "
-    "2948380657 1208800977 2316451404 4001932203 "
-    "1977856863 4265848271 3116200050 3037586215 "
-    "1335232764 930230766 1026089249 2482219415 "
-    "2613853154 1854543497 2909555107 3862874043 "
-    "2609355500 907364682 383900687 358164223 "
-    "232347546 2536276737 3118482806 1254103998 "
-    "2357334077 1204777304 1996643329 4046232717 "
-    "2570520290 3173323380 1201411457 2361883023 "
-    "806087062 2984143714 2355127569 864220085 "
-    "1787696713 1182652984 4200065581 100722519 "
-    "2380458669 2429592313 2618338302 1236529564 "
-    "1747130196 3711661325 1114068102 510380433 "
-    "93703089 2277774664 3220741441 1577998569 "
-    "2816701900 4206763045 2495239107 4080390459 "
-    "1307072677 20360728 1468385549 96049834 "
-    "3630657447 2809517346 3396111678 3043831060 "
-    "673178359 4256729562 1755211210 1969834535 "
-    "498315110 3717726302 1544859987 2239930949 "
-    "1595372585 294525219 3961637067 3591840665 "
-    "3324896933 2300077772 721255886 4197934760 "
-    "1468866696 2184812884 628246683 3385113037 "
-    "3041166140 3948531843 1176600829 228286131 "
-    "2447397608 712235937 3332826819 2676980703 "
-    "4019468871 1952389952 1202638254 3625447051";
-    unsigned as[] = {3, 5, 7};
-    std::seed_seq sseq(as, as+3);
-    std::mt19937 e1(sseq);
-    std::ostringstream os;
-    os << e1;
-    assert(os.str() == a);
+void test1() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "358595400 4166558815 2016177162 3414480257 "
+      "4027494649 3722317195 1190532340 3212207148 "
+      "3537847251 389019999 1098708832 3277907415 "
+      "1946784350 3608286140 2091419822 2227407035 "
+      "2229110723 1825348377 1276269279 314337202 "
+      "3182935337 1313150029 3118776508 3707918501 "
+      "1900972958 4054820954 3973178248 906260237 "
+      "1403942218 3139931556 2807126524 3940936448 "
+      "1316345796 631296613 2268418920 2914000794 "
+      "3760588399 3226216036 880155129 4183611084 "
+      "211541083 3755352858 1331383234 3036493096 "
+      "937478630 2092170412 777784402 93392729 "
+      "3644029210 1681392086 2427001226 3143870332 "
+      "3703581502 2017505388 1706274541 1049329728 "
+      "2452031492 3437261233 2581204087 1700889875 "
+      "1652573881 2127047692 3778506964 1960741508 "
+      "2739602360 3395905609 2123355622 3041272975 "
+      "784200748 3558951522 1002787860 4063320888 "
+      "1587315560 4042698976 659183308 3082256417 "
+      "2808969567 2361418535 3468698782 750700970 "
+      "2991209851 3581521382 962022878 2518967363 "
+      "1476525873 3865977235 2128790058 2380326689 "
+      "1396773405 312559410 1370621899 1154499924 "
+      "2963101919 2182689761 2071851902 1661288848 "
+      "2411351341 1362764020 1289894483 1951662807 "
+      "701821506 552267185 2356648449 3949188503 "
+      "1748307081 87795201 3718396254 4112205936 "
+      "2819888864 73923733 2800033151 839258139 "
+      "3801779069 3105962436 2111266436 1772784466 "
+      "3692264298 4148810953 3147390749 3537518553 "
+      "1695044978 1430225842 1252346204 3465285434 "
+      "3970017763 2920658411 2805151132 290569815 "
+      "3802301355 1493420394 1943029276 1667143611 "
+      "1049665988 1710824905 220168517 3997946231 "
+      "1014582791 4244598752 1147604069 2533886627 "
+      "598679964 761521020 431779255 3745982038 "
+      "768658283 3598262505 1765664789 279538641 "
+      "715144305 2371628432 2655860083 1759010423 "
+      "3568452003 1910107098 2801429529 3924547532 "
+      "3862672436 3933725740 1764550618 130893617 "
+      "1460692387 4135312761 2075529299 2880227367 "
+      "944557368 4166665482 2627749235 3732013815 "
+      "1595900818 1553312393 3529311831 3531462424 "
+      "2431328342 4075369692 1609967709 3704537555 "
+      "2067297464 397140475 920618678 2840795964 "
+      "4202512837 1286017648 7035910 1057207826 "
+      "2325188262 191593698 3697383848 3029712831 "
+      "2073681914 163454681 1329637200 290077398 "
+      "287239431 4205081522 1233889024 167173087 "
+      "3267660257 3406068803 2382354609 1680046927 "
+      "125183503 3559536309 3208900974 2912148541 "
+      "2882879316 1937001086 2919729069 892928802 "
+      "4141691387 2507406586 855548593 3418647837 "
+      "4035646154 2410275591 248715645 3180757482 "
+      "1880770722 362912336 2964920095 2319904154 "
+      "1493655850 4240733030 1834485846 1696040454 "
+      "3329457927 1865824694 847759208 1587231623 "
+      "3757294772 1161601118 3630323833 3007722125 "
+      "3726418007 2124238171 1205345 172659797 "
+      "3040354211 885213338 1857049013 447922412 "
+      "719906299 1370059380 1922204800 3960090489 "
+      "1658822644 1529626863 1565927273 3537718771 "
+      "2733237258 2180221377 921910745 2144937687 "
+      "1727603895 1315635304 4023867791 2401834107 "
+      "808854185 2408824497 343935326 185237544 "
+      "746732759 2641236122 4283215329 743609415 "
+      "1134726665 3892851319 1302851263 3473445597 "
+      "1326817414 2702766508 1943179285 4025685468 "
+      "932896770 199392138 2787362875 3450501893 "
+      "3351567147 2461286528 2227605848 2993751114 "
+      "3988215720 1320573368 2866560199 4153194990 "
+      "3007120042 3260751955 3171763740 2111121243 "
+      "3962825228 102681859 3368179132 802089147 "
+      "4029273561 424939445 4178414761 2592125109 "
+      "1960801088 2967746406 310701990 2364200202 "
+      "1320507009 3474372480 784693947 2952246664 "
+      "1891935330 2048385105 3530082191 3238151038 "
+      "3293189141 1316053288 2087004409 740799958 "
+      "1187748554 3607767334 1190185990 1408429481 "
+      "657134359 221834425 3907725865 1068016389 "
+      "1402423875 2598612116 2046886300 2345022518 "
+      "1196081924 357783981 4013683598 463491626 "
+      "3269206482 3332444286 886491955 2257342866 "
+      "475911113 833576299 2893727564 2866985145 "
+      "1413365115 2995166393 1486060436 161205225 "
+      "3181728373 3056027137 2040371876 2182305146 "
+      "3028448628 2214316977 1266227021 876938740 "
+      "276477469 752158077 2182179045 1381698878 "
+      "3424557652 666674427 968327842 2534296575 "
+      "265105940 961112540 2641188117 2319139814 "
+      "1750453329 3450138343 678025317 1477566458 "
+      "3773796420 2933993832 3326042905 4084805260 "
+      "444182455 255333481 785163068 2321290820 "
+      "2893603234 3005520266 541104079 1383277090 "
+      "2770755666 3764627833 583371929 2864949033 "
+      "1487681116 1811788361 240329486 3094213377 "
+      "958509875 2564379085 1636995945 2070894127 "
+      "2139004232 1747850055 3841512327 3325011872 "
+      "1161622604 639182193 3533652535 4022667522 "
+      "761048999 3337743670 254221568 2784956233 "
+      "2990252814 4207922787 275707208 261819597 "
+      "2071467265 4034945770 1999813410 3038921100 "
+      "2200194573 1328217451 2440612380 3862293692 "
+      "2733976812 2750523058 2920082515 3809044908 "
+      "4285231753 3131963297 3481602724 1396460739 "
+      "2011767965 2481047764 2958452120 3044188618 "
+      "2217236658 3448605566 757716104 1818279145 "
+      "2641228144 1312649639 1194087684 3845229729 "
+      "1747658356 874418803 1956637337 268670179 "
+      "2083040240 2577671381 3375334655 2587828868 "
+      "1383012799 3583445685 2594576715 3282337104 "
+      "4257972751 3440488071 3129180313 1830891395 "
+      "1594931092 2680778339 3984026324 1102770400 "
+      "2315820258 1263467048 1133254110 2400676748 "
+      "2251795328 1036154092 3313541051 2277356560 "
+      "1477696003 1417117088 3968537402 1404882202 "
+      "2011058180 4114080985 1727459502 4100235708 "
+      "2334509310 2829432554 377936301 1519324520 "
+      "3252826644 1193335837 1929125820 2165344238 "
+      "4160556243 223340988 670907625 1485396519 "
+      "936389509 3813712964 2706450987 3132506320 "
+      "875886515 557088991 2854916639 2955496008 "
+      "2881696287 265169077 3239923698 3649366121 "
+      "4072165960 1233904959 225406526 1767368993 "
+      "1894882500 2296582180 339255168 83200939 "
+      "2958376148 4100205346 1991250823 3806183082 "
+      "2691709980 2642354997 3024056146 1681065839 "
+      "3438299684 1638853652 362567001 2307868899 "
+      "988801086 1342833399 2303298376 1500039911 "
+      "765489391 4080464497 4155444368 980472018 "
+      "2026981853 3460406995 391970367 667377014 "
+      "4177754853 2657468948 3560690175 3030464357 "
+      "2948380657 1208800977 2316451404 4001932203 "
+      "1977856863 4265848271 3116200050 3037586215 "
+      "1335232764 930230766 1026089249 2482219415 "
+      "2613853154 1854543497 2909555107 3862874043 "
+      "2609355500 907364682 383900687 358164223 "
+      "232347546 2536276737 3118482806 1254103998 "
+      "2357334077 1204777304 1996643329 4046232717 "
+      "2570520290 3173323380 1201411457 2361883023 "
+      "806087062 2984143714 2355127569 864220085 "
+      "1787696713 1182652984 4200065581 100722519 "
+      "2380458669 2429592313 2618338302 1236529564 "
+      "1747130196 3711661325 1114068102 510380433 "
+      "93703089 2277774664 3220741441 1577998569 "
+      "2816701900 4206763045 2495239107 4080390459 "
+      "1307072677 20360728 1468385549 96049834 "
+      "3630657447 2809517346 3396111678 3043831060 "
+      "673178359 4256729562 1755211210 1969834535 "
+      "498315110 3717726302 1544859987 2239930949 "
+      "1595372585 294525219 3961637067 3591840665 "
+      "3324896933 2300077772 721255886 4197934760 "
+      "1468866696 2184812884 628246683 3385113037 "
+      "3041166140 3948531843 1176600829 228286131 "
+      "2447397608 712235937 3332826819 2676980703 "
+      "4019468871 1952389952 1202638254 3625447051"
+#else
+      "358595400 4166558815 2016177162 3414480257 4027494649 3722317195 1190532340 3212207148 3537847251 389019999 "
+      "1098708832 3277907415 1946784350 3608286140 2091419822 2227407035 2229110723 1825348377 1276269279 314337202 "
+      "3182935337 1313150029 3118776508 3707918501 1900972958 4054820954 3973178248 906260237 1403942218 3139931556 "
+      "2807126524 3940936448 1316345796 631296613 2268418920 2914000794 3760588399 3226216036 880155129 4183611084 "
+      "211541083 3755352858 1331383234 3036493096 937478630 2092170412 777784402 93392729 3644029210 1681392086 "
+      "2427001226 3143870332 3703581502 2017505388 1706274541 1049329728 2452031492 3437261233 2581204087 1700889875 "
+      "1652573881 2127047692 3778506964 1960741508 2739602360 3395905609 2123355622 3041272975 784200748 3558951522 "
+      "1002787860 4063320888 1587315560 4042698976 659183308 3082256417 2808969567 2361418535 3468698782 750700970 "
+      "2991209851 3581521382 962022878 2518967363 1476525873 3865977235 2128790058 2380326689 1396773405 312559410 "
+      "1370621899 1154499924 2963101919 2182689761 2071851902 1661288848 2411351341 1362764020 1289894483 1951662807 "
+      "701821506 552267185 2356648449 3949188503 1748307081 87795201 3718396254 4112205936 2819888864 73923733 "
+      "2800033151 839258139 3801779069 3105962436 2111266436 1772784466 3692264298 4148810953 3147390749 3537518553 "
+      "1695044978 1430225842 1252346204 3465285434 3970017763 2920658411 2805151132 290569815 3802301355 1493420394 "
+      "1943029276 1667143611 1049665988 1710824905 220168517 3997946231 1014582791 4244598752 1147604069 2533886627 "
+      "598679964 761521020 431779255 3745982038 768658283 3598262505 1765664789 279538641 715144305 2371628432 "
+      "2655860083 1759010423 3568452003 1910107098 2801429529 3924547532 3862672436 3933725740 1764550618 130893617 "
+      "1460692387 4135312761 2075529299 2880227367 944557368 4166665482 2627749235 3732013815 1595900818 1553312393 "
+      "3529311831 3531462424 2431328342 4075369692 1609967709 3704537555 2067297464 397140475 920618678 2840795964 "
+      "4202512837 1286017648 7035910 1057207826 2325188262 191593698 3697383848 3029712831 2073681914 163454681 "
+      "1329637200 290077398 287239431 4205081522 1233889024 167173087 3267660257 3406068803 2382354609 1680046927 "
+      "125183503 3559536309 3208900974 2912148541 2882879316 1937001086 2919729069 892928802 4141691387 2507406586 "
+      "855548593 3418647837 4035646154 2410275591 248715645 3180757482 1880770722 362912336 2964920095 2319904154 "
+      "1493655850 4240733030 1834485846 1696040454 3329457927 1865824694 847759208 1587231623 3757294772 1161601118 "
+      "3630323833 3007722125 3726418007 2124238171 1205345 172659797 3040354211 885213338 1857049013 447922412 "
+      "719906299 1370059380 1922204800 3960090489 1658822644 1529626863 1565927273 3537718771 2733237258 2180221377 "
+      "921910745 2144937687 1727603895 1315635304 4023867791 2401834107 808854185 2408824497 343935326 185237544 "
+      "746732759 2641236122 4283215329 743609415 1134726665 3892851319 1302851263 3473445597 1326817414 2702766508 "
+      "1943179285 4025685468 932896770 199392138 2787362875 3450501893 3351567147 2461286528 2227605848 2993751114 "
+      "3988215720 1320573368 2866560199 4153194990 3007120042 3260751955 3171763740 2111121243 3962825228 102681859 "
+      "3368179132 802089147 4029273561 424939445 4178414761 2592125109 1960801088 2967746406 310701990 2364200202 "
+      "1320507009 3474372480 784693947 2952246664 1891935330 2048385105 3530082191 3238151038 3293189141 1316053288 "
+      "2087004409 740799958 1187748554 3607767334 1190185990 1408429481 657134359 221834425 3907725865 1068016389 "
+      "1402423875 2598612116 2046886300 2345022518 1196081924 357783981 4013683598 463491626 3269206482 3332444286 "
+      "886491955 2257342866 475911113 833576299 2893727564 2866985145 1413365115 2995166393 1486060436 161205225 "
+      "3181728373 3056027137 2040371876 2182305146 3028448628 2214316977 1266227021 876938740 276477469 752158077 "
+      "2182179045 1381698878 3424557652 666674427 968327842 2534296575 265105940 961112540 2641188117 2319139814 "
+      "1750453329 3450138343 678025317 1477566458 3773796420 2933993832 3326042905 4084805260 444182455 255333481 "
+      "785163068 2321290820 2893603234 3005520266 541104079 1383277090 2770755666 3764627833 583371929 2864949033 "
+      "1487681116 1811788361 240329486 3094213377 958509875 2564379085 1636995945 2070894127 2139004232 1747850055 "
+      "3841512327 3325011872 1161622604 639182193 3533652535 4022667522 761048999 3337743670 254221568 2784956233 "
+      "2990252814 4207922787 275707208 261819597 2071467265 4034945770 1999813410 3038921100 2200194573 1328217451 "
+      "2440612380 3862293692 2733976812 2750523058 2920082515 3809044908 4285231753 3131963297 3481602724 1396460739 "
+      "2011767965 2481047764 2958452120 3044188618 2217236658 3448605566 757716104 1818279145 2641228144 1312649639 "
+      "1194087684 3845229729 1747658356 874418803 1956637337 268670179 2083040240 2577671381 3375334655 2587828868 "
+      "1383012799 3583445685 2594576715 3282337104 4257972751 3440488071 3129180313 1830891395 1594931092 2680778339 "
+      "3984026324 1102770400 2315820258 1263467048 1133254110 2400676748 2251795328 1036154092 3313541051 2277356560 "
+      "1477696003 1417117088 3968537402 1404882202 2011058180 4114080985 1727459502 4100235708 2334509310 2829432554 "
+      "377936301 1519324520 3252826644 1193335837 1929125820 2165344238 4160556243 223340988 670907625 1485396519 "
+      "936389509 3813712964 2706450987 3132506320 875886515 557088991 2854916639 2955496008 2881696287 265169077 "
+      "3239923698 3649366121 4072165960 1233904959 225406526 1767368993 1894882500 2296582180 339255168 83200939 "
+      "2958376148 4100205346 1991250823 3806183082 2691709980 2642354997 3024056146 1681065839 3438299684 1638853652 "
+      "362567001 2307868899 988801086 1342833399 2303298376 1500039911 765489391 4080464497 4155444368 980472018 "
+      "2026981853 3460406995 391970367 667377014 4177754853 2657468948 3560690175 3030464357 2948380657 1208800977 "
+      "2316451404 4001932203 1977856863 4265848271 3116200050 3037586215 1335232764 930230766 1026089249 2482219415 "
+      "2613853154 1854543497 2909555107 3862874043 2609355500 907364682 383900687 358164223 232347546 2536276737 "
+      "3118482806 1254103998 2357334077 1204777304 1996643329 4046232717 2570520290 3173323380 1201411457 2361883023 "
+      "806087062 2984143714 2355127569 864220085 1787696713 1182652984 4200065581 100722519 2380458669 2429592313 "
+      "2618338302 1236529564 1747130196 3711661325 1114068102 510380433 93703089 2277774664 3220741441 1577998569 "
+      "2816701900 4206763045 2495239107 4080390459 1307072677 20360728 1468385549 96049834 3630657447 2809517346 "
+      "3396111678 3043831060 673178359 4256729562 1755211210 1969834535 498315110 3717726302 1544859987 2239930949 "
+      "1595372585 294525219 3961637067 3591840665 3324896933 2300077772 721255886 4197934760 1468866696 2184812884 "
+      "628246683 3385113037 3041166140 3948531843 1176600829 228286131 2447397608 712235937 3332826819 2676980703 "
+      "4019468871 1952389952 1202638254 3625447051"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  unsigned as[] = {3, 5, 7};
+  std::seed_seq sseq(as, as + 3);
+  std::mt19937 e1(sseq);
+  std::ostringstream os;
+  os << e1;
+  LIBCPP_ASSERT(os.str() == a);
+  (void)a;
 }
 
-void
-test2()
-{
-    const char* a = "17895233847644109640 14665081038668852234 15987230621890949369 "
-    "13796324649827964148 1670828176732799955 14078505147839608672 "
-    "15497470967856861790 9566640372296747182 7839811585250789315 "
-    "1350068003782415071 5639936432479386921 15925388701147119804 "
-    "17415323390466493342 3892358083553387400 13485903346102334794 "
-    "16926193162581531132 2711398308226914244 12515538113016451944 "
-    "13856492368211347055 17968472785843263993 16129117710261673051 "
-    "13041638543181171650 8985803498136324582 401118717516975186 "
-    "7221524024767248666 13502820261231663498 8665119664667372350 "
-    "4506836866186850029 14762924585995667460 7305266389803732087 "
-    "9135600275824854713 8421320656548229332 14585303533697565624 "
-    "13062167967956981222 15285580395823625260 17451830328116466708 "
-    "17363259891080004456 13238190509560321740 10142215382802200927 "
-    "3224236118694175902 15382517208605932923 10818882444738383326 "
-    "16604245792882032433 10223425285179753002 1342432445403828765 "
-    "4958539418185107403 9374581143772158175 7135181273441366910 "
-    "5853026900476841261 8382327930174454355 2371969498930803266 "
-    "16961635468480846337 377077518789053577 17661790013255465310 "
-    "317500018453124832 3604586262706855295 13340007089026272125 "
-    "7614051306438090372 17819007364113857386 15193526497275633437 "
-    "6142773218979108210 14883287611587512668 12544132362002344419 "
-    "1247987855434921372 6414191755211735979 7160327288923375132 "
-    "7347937017206972868 17171048313531629893 18230412825496997383 "
-    "10882960195884354661 3270707876715241884 16088870345045208503 "
-    "15454419782166694763 1200609322828949525 10186066554418904177 "
-    "7554892242763986291 8203847521335919011 16855803304338943001 "
-    "16895223408596071476 562183806034700250 17761033068687156643 "
-    "12370482348384718931 17895691979506634040 16028877286272943475 "
-    "6671425930002400146 15167515621662197335 17503579548680921174 "
-    "15910867647138768989 1705705354110203064 12201125760909412022 "
-    "5523403744441352645 4540673037752294406 822888669354888870 "
-    "13012517529113958824 702032511346794490 1245872939048413008 "
-    "18060687614291143943 718002942670251776 14628954120078526945 "
-    "7215746609592654001 15288092036204733967 12507582747898016110 "
-    "8319356319569362772 3835100005166188461 10769229288786702843 "
-    "14682980657311687345 10352054841727718090 13661249361946024317 "
-    "1558696616315734178 9963912474249467679 18213809676410642730 "
-    "7284438284457478230 8013656044128665351 6817107912809760616 "
-    "4989038816564331700 12918068165960947833 9123533477086273623 "
-    "741568181450204257 3801962339733348259 1923812112542486965 "
-    "5884360231397942779 17008459141377852544 6569697353326895092 "
-    "15194386425456240489 9363979514988323850 9212437218544795097 "
-    "5650610605870621879 10315798944006232463 10345822437227504297 "
-    "795589193815296350 11344022765750598871 3193778122705907169 "
-    "16719669104430190089 14918335244853046975 11608293761910939782 "
-    "17290187430985633813 856382712722415618 14819792788008454203 "
-    "10571145147196955435 12858063129221173592 5671819431516788648 "
-    "17837836658827607239 14004823010100183722 9067196699747632668 "
-    "441015230260308492 3444946658209715644 1825101023084664281 "
-    "11133092574473850025 12746373758552339264 10154162549097295782 "
-    "14922316177042921089 12679802872389794491 8797747037480461410 "
-    "13907752811248535439 5652405835046458389 3181711594575177977 "
-    "15495242712294857418 6049158560807439366 952771601159099159 "
-    "4587095466254740009 11160954054611782211 10071795025240457628 "
-    "1536670498623767300 1990681379653546894 14312739227381277138 "
-    "9695213786215402291 3580182943401628617 12313607438786545484 "
-    "12864141705426648443 692371170805382036 13125536612285239925 "
-    "9372929234002877092 9510419002221032820 3766423210161674061 "
-    "3230494342413727261 5934351496112072933 2863344864469097044 "
-    "10884720908958139042 4127946927340597780 9960629658622711061 "
-    "14818231351611083857 6346099615454582885 12601407558879514692 "
-    "17544105005554819865 1096648950913019831 9969868157190185788 "
-    "12908611252828823970 5941129863397152719 16168953427117105234 "
-    "12304862402025196697 7781571759256122972 13289545261301048078 "
-    "11013924305579914035 8894422550580466537 7506958826675805512 "
-    "14280817252893250439 2745266616282182732 17277225453205013047 "
-    "14335499905842065319 11961295941780577536 18072890757248426766 "
-    "1124506606842606920 17329960125355005185 13052066741624159010 "
-    "5704650516221677069 16588425097127709212 11813406583737887980 "
-    "16359723311775411283 13451679937172566665 5997753207634594468 "
-    "10656019008205694109 13074690560123889048 14811648124990806194 "
-    "7809449463531558024 5637787273252434288 16515135932856030468 "
-    "3755600163640125044 1153929634172103321 11071014283313196016 "
-    "11114640359080035583 15390782025450330559 14097530518721927499 "
-    "14776783751481098767 7863618667181998233 11513855295425132436 "
-    "4736362806980864724 5426549653049482466 10310828122060887518 "
-    "4450247941008370560 9781171949844602811 6086471549040450051 "
-    "6033923116291003194 17669843285681524740 17610378273478865070 "
-    "12152320288002263294 6525449125788834221 5125338396312613396 "
-    "9300082688721166268 959242243476884691 6379729471368150249 "
-    "16379772457647614853 13454012201619761707 2392678998182524851 "
-    "12693758700673471007 1138892516507202079 15673908144065302514 "
-    "5299581449349386824 7590792025124859454 9863745357571267780 "
-    "357345312340746112 17610247870912740564 16347431861769737095 "
-    "11348828299228888092 7220122803951857490 7038822841708464676 "
-    "9912221445023094105 5767425533670320190 6442622362743049032 "
-    "17525461567869579503 4211095256108567696 14862334876401617373 "
-    "2866362449624104511 11413742225973279461 13015745308569358847 "
-    "5191760666536228849 17188167935010684492 18321678815621002079 "
-    "13046333455321624690 3995310719038261500 10661051209947341089 "
-    "7965203671238327266 16590917686161852835 3897101637344795372 "
-    "1538303624766151695 10893225639252940698 5386335660311332214 "
-    "5174479122000384061 17378437193516866561 13629320139302700770 "
-    "10144210341964027265 12816799659000064406 3711797003976467729 "
-    "5079455890584507977 432599929275804205 10435019529328454317 "
-    "5310854040535477246 15941464006450157396 2192067269367387270 "
-    "9782967689631091633 6777452250210540865 18067909703113078220 "
-    "17525143578810667971 87448662189824165 412530897284614413 "
-    "12066785122245373863 13073154860645125438 18282514257379582711 "
-    "8460374908111578570 15967512883067334502 9620430172798103891 "
-    "1264976185047610409 15426838192579528907 9878758812321441445 "
-    "18029992505662864846 9383699886128308360 14538949787806484635 "
-    "16958815135940772668 980481467951972605 3059030058898313960 "
-    "11497544574740915907 8385450996898478663 15571176518627282350";
-    unsigned as[] = {3, 5, 7};
-    std::seed_seq sseq(as, as+3);
-    std::mt19937_64 e1(sseq);
-    std::ostringstream os;
-    os << e1;
-    assert(os.str() == a);
+void test2() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "17895233847644109640 14665081038668852234 15987230621890949369 "
+      "13796324649827964148 1670828176732799955 14078505147839608672 "
+      "15497470967856861790 9566640372296747182 7839811585250789315 "
+      "1350068003782415071 5639936432479386921 15925388701147119804 "
+      "17415323390466493342 3892358083553387400 13485903346102334794 "
+      "16926193162581531132 2711398308226914244 12515538113016451944 "
+      "13856492368211347055 17968472785843263993 16129117710261673051 "
+      "13041638543181171650 8985803498136324582 401118717516975186 "
+      "7221524024767248666 13502820261231663498 8665119664667372350 "
+      "4506836866186850029 14762924585995667460 7305266389803732087 "
+      "9135600275824854713 8421320656548229332 14585303533697565624 "
+      "13062167967956981222 15285580395823625260 17451830328116466708 "
+      "17363259891080004456 13238190509560321740 10142215382802200927 "
+      "3224236118694175902 15382517208605932923 10818882444738383326 "
+      "16604245792882032433 10223425285179753002 1342432445403828765 "
+      "4958539418185107403 9374581143772158175 7135181273441366910 "
+      "5853026900476841261 8382327930174454355 2371969498930803266 "
+      "16961635468480846337 377077518789053577 17661790013255465310 "
+      "317500018453124832 3604586262706855295 13340007089026272125 "
+      "7614051306438090372 17819007364113857386 15193526497275633437 "
+      "6142773218979108210 14883287611587512668 12544132362002344419 "
+      "1247987855434921372 6414191755211735979 7160327288923375132 "
+      "7347937017206972868 17171048313531629893 18230412825496997383 "
+      "10882960195884354661 3270707876715241884 16088870345045208503 "
+      "15454419782166694763 1200609322828949525 10186066554418904177 "
+      "7554892242763986291 8203847521335919011 16855803304338943001 "
+      "16895223408596071476 562183806034700250 17761033068687156643 "
+      "12370482348384718931 17895691979506634040 16028877286272943475 "
+      "6671425930002400146 15167515621662197335 17503579548680921174 "
+      "15910867647138768989 1705705354110203064 12201125760909412022 "
+      "5523403744441352645 4540673037752294406 822888669354888870 "
+      "13012517529113958824 702032511346794490 1245872939048413008 "
+      "18060687614291143943 718002942670251776 14628954120078526945 "
+      "7215746609592654001 15288092036204733967 12507582747898016110 "
+      "8319356319569362772 3835100005166188461 10769229288786702843 "
+      "14682980657311687345 10352054841727718090 13661249361946024317 "
+      "1558696616315734178 9963912474249467679 18213809676410642730 "
+      "7284438284457478230 8013656044128665351 6817107912809760616 "
+      "4989038816564331700 12918068165960947833 9123533477086273623 "
+      "741568181450204257 3801962339733348259 1923812112542486965 "
+      "5884360231397942779 17008459141377852544 6569697353326895092 "
+      "15194386425456240489 9363979514988323850 9212437218544795097 "
+      "5650610605870621879 10315798944006232463 10345822437227504297 "
+      "795589193815296350 11344022765750598871 3193778122705907169 "
+      "16719669104430190089 14918335244853046975 11608293761910939782 "
+      "17290187430985633813 856382712722415618 14819792788008454203 "
+      "10571145147196955435 12858063129221173592 5671819431516788648 "
+      "17837836658827607239 14004823010100183722 9067196699747632668 "
+      "441015230260308492 3444946658209715644 1825101023084664281 "
+      "11133092574473850025 12746373758552339264 10154162549097295782 "
+      "14922316177042921089 12679802872389794491 8797747037480461410 "
+      "13907752811248535439 5652405835046458389 3181711594575177977 "
+      "15495242712294857418 6049158560807439366 952771601159099159 "
+      "4587095466254740009 11160954054611782211 10071795025240457628 "
+      "1536670498623767300 1990681379653546894 14312739227381277138 "
+      "9695213786215402291 3580182943401628617 12313607438786545484 "
+      "12864141705426648443 692371170805382036 13125536612285239925 "
+      "9372929234002877092 9510419002221032820 3766423210161674061 "
+      "3230494342413727261 5934351496112072933 2863344864469097044 "
+      "10884720908958139042 4127946927340597780 9960629658622711061 "
+      "14818231351611083857 6346099615454582885 12601407558879514692 "
+      "17544105005554819865 1096648950913019831 9969868157190185788 "
+      "12908611252828823970 5941129863397152719 16168953427117105234 "
+      "12304862402025196697 7781571759256122972 13289545261301048078 "
+      "11013924305579914035 8894422550580466537 7506958826675805512 "
+      "14280817252893250439 2745266616282182732 17277225453205013047 "
+      "14335499905842065319 11961295941780577536 18072890757248426766 "
+      "1124506606842606920 17329960125355005185 13052066741624159010 "
+      "5704650516221677069 16588425097127709212 11813406583737887980 "
+      "16359723311775411283 13451679937172566665 5997753207634594468 "
+      "10656019008205694109 13074690560123889048 14811648124990806194 "
+      "7809449463531558024 5637787273252434288 16515135932856030468 "
+      "3755600163640125044 1153929634172103321 11071014283313196016 "
+      "11114640359080035583 15390782025450330559 14097530518721927499 "
+      "14776783751481098767 7863618667181998233 11513855295425132436 "
+      "4736362806980864724 5426549653049482466 10310828122060887518 "
+      "4450247941008370560 9781171949844602811 6086471549040450051 "
+      "6033923116291003194 17669843285681524740 17610378273478865070 "
+      "12152320288002263294 6525449125788834221 5125338396312613396 "
+      "9300082688721166268 959242243476884691 6379729471368150249 "
+      "16379772457647614853 13454012201619761707 2392678998182524851 "
+      "12693758700673471007 1138892516507202079 15673908144065302514 "
+      "5299581449349386824 7590792025124859454 9863745357571267780 "
+      "357345312340746112 17610247870912740564 16347431861769737095 "
+      "11348828299228888092 7220122803951857490 7038822841708464676 "
+      "9912221445023094105 5767425533670320190 6442622362743049032 "
+      "17525461567869579503 4211095256108567696 14862334876401617373 "
+      "2866362449624104511 11413742225973279461 13015745308569358847 "
+      "5191760666536228849 17188167935010684492 18321678815621002079 "
+      "13046333455321624690 3995310719038261500 10661051209947341089 "
+      "7965203671238327266 16590917686161852835 3897101637344795372 "
+      "1538303624766151695 10893225639252940698 5386335660311332214 "
+      "5174479122000384061 17378437193516866561 13629320139302700770 "
+      "10144210341964027265 12816799659000064406 3711797003976467729 "
+      "5079455890584507977 432599929275804205 10435019529328454317 "
+      "5310854040535477246 15941464006450157396 2192067269367387270 "
+      "9782967689631091633 6777452250210540865 18067909703113078220 "
+      "17525143578810667971 87448662189824165 412530897284614413 "
+      "12066785122245373863 13073154860645125438 18282514257379582711 "
+      "8460374908111578570 15967512883067334502 9620430172798103891 "
+      "1264976185047610409 15426838192579528907 9878758812321441445 "
+      "18029992505662864846 9383699886128308360 14538949787806484635 "
+      "16958815135940772668 980481467951972605 3059030058898313960 "
+      "11497544574740915907 8385450996898478663 15571176518627282350"
+#else
+      "12332679912394435791 9453873875014225299 7194476751338084717 15353332195928478249 10481090450137100787 "
+      "16894768970350315699 9140706850585347923 17060753788370102662 5027480258542244180 4237901618226072654 "
+      "1625191769495810967 14151318192590258819 14578513270917221567 1340379920765042993 16984341741367590539 "
+      "17825705796225691974 10460633243946900672 15537248717777931667 18010309519743623944 11238461300079763233 "
+      "5212834616069799861 14803562233233663569 509073377271690301 9868447031443646872 18426739611230355604 "
+      "405337453835701754 2865140403182526171 17022743117531244315 15873574515363721317 1003946026159741321 "
+      "10106235397909731784 7501984743217763859 9608180616803748513 17321043653964822159 138792968844647510 "
+      "13940152250553688762 16186510718789785685 10788084171130680879 3336642670408666887 7310094463387030739 "
+      "5523088258140426915 1285026150975382598 13098435840364629682 6200633715587124711 5088226041080887554 "
+      "11020661496106860750 12793876732801641662 3576577317235426397 15183184978662083277 15861657920389428029 "
+      "470273426362791941 2574629887817053182 13272148377140358694 3004438593777107412 2652718879104414667 "
+      "1834027854782609871 10456539872813643760 6392289299447966013 9267269600134252311 10114120825136775101 "
+      "2198130084042867418 13999435202207229825 14893120434100842814 2852347522408045763 17948795049429383857 "
+      "17441093961240168873 5399621587997711172 12611952947737738867 6103704372959848527 738297531584281114 "
+      "16788685814043292624 6176291056095750530 16428159208694695523 4245608719980075114 12095265841295350099 "
+      "15191360055442418178 8708130367787597793 9264267659253066420 15969277182114773779 17035922951690056341 "
+      "9880719443672114132 15332192661202958624 14150490775662311299 4012807722045209888 8653700056821051719 "
+      "15242621354372282880 17096356791700814452 16018787795979380803 296068005088784708 4048466329605633785 "
+      "8059310313495723083 8563086889008277869 10184519287956394000 6827268421235011709 17346498307575607164 "
+      "6880036350466817261 16153597041588957084 15405131912393208395 12767350280822570901 1052885868672540599 "
+      "4188094412071243913 1126222035238982882 9191486289101564624 10803640833835240836 3601208394977378668 "
+      "4766334496475281242 7832706214999682994 16891444816728487342 17860841416138565015 12370215439434878681 "
+      "9232823972512380788 3640121802277180952 65633375613217608 13600365462550545275 17948579528827407927 "
+      "765525796436836449 13572020505370210613 11924972776661436983 4057541052165744297 17481588995966250594 "
+      "8006213088863539837 9739468715064136699 2716054689781779647 16549004007254321028 4910543003771498131 "
+      "13361071256636157603 15297097731894649703 17624276599277544080 15499557723143597570 17957222608348359804 "
+      "2778177927288936525 13637160153035836011 5098746756051510535 4146145678876364034 2247309035465090095 "
+      "9569124281048572866 12809922376030463313 15451507763993230705 17163206508771813131 17029710194125776618 "
+      "8061947308014036349 1058089520886506655 13597933493965622376 13504098358025602133 1333487301584903927 "
+      "8426860107195956174 3508319831781619992 13220104443771667566 15729810053761292123 4229931263400540722 "
+      "4166080451531777928 6512350709932085964 11707745856776186054 5335698181984744736 16601891716487755730 "
+      "14845047860332527819 13881061187353168588 2290627681380867569 15008794942145533328 9207539692861337313 "
+      "15854225885502222653 12652120737459513649 8391328319785893524 16218535624802384239 10635204123630136612 "
+      "14741489403103018051 1034273171946262577 2598761621738522071 10597540332677591413 11769725249744073186 "
+      "12729182262319592409 13143766812621949948 7337533679729790607 8700823377064447860 6528412901068836499 "
+      "12887771189408551435 6596057164621086436 9725510839601037147 12589333640740308830 8713351353100408409 "
+      "3200078174512985423 3001899537338861016 14245433087720289982 2342459702085298473 15840929965350904315 "
+      "5242384259319523672 6976265022106569670 4694429153464034874 4640016838764614916 11889897715595570593 "
+      "3752569812323601873 2900819842622530250 1859087250745968648 12106047926770169995 12635768141780418349 "
+      "460915272050913013 16909625031804250705 15219281218948426364 15473817896328511538 356510218273447008 "
+      "4322019207571293094 3044055123444388775 14541219878139834671 16010790916272798386 17663395908622954435 "
+      "12611516608169911627 16319922669204576197 16662253955344225107 16530145161795660660 13055268744196885107 "
+      "7863493504777224455 4884882190835979670 17860099797924912564 7963955360230183301 12071138257120882069 "
+      "18357891564140620935 12793730149183942271 14575396984169788025 3998777626616457896 16107243833817385461 "
+      "2800961891557584893 2799945392029798727 11053512580047143649 11067103105271293369 1968075203485277477 "
+      "3090469630341592171 14797167942499219775 1314244307340344386 5197117610141487191 14715472132315901058 "
+      "10223376637313339598 18090093248597656064 162846477809223606 18060308085200244427 4381704277003259436 "
+      "13919578743352454047 12290099036217748490 2399520646070724256 8630374525153030174 12607432526554525963 "
+      "13596968856308825531 4310477479909840944 5249608228653410962 3699954346213058725 287328857418258109 "
+      "6119686667148845021 5412656704791399252 4791204075127203599 14494596012449800144 6684909344487566871 "
+      "4578857374794350422 3320300604108137187 12579270049709840437 16704843247071205209 3817825700702948560 "
+      "5357944831944862632 1304382350048285997 10815164501630203260 438468935207234712 4455248787965292675 "
+      "16418065713664649114 16484487561423541185 10845743900807077540 376195122763577279 15264985897394715313 "
+      "7633016332459855519 18374759716909813581 7494762658573963878 12561201922274984497 17674342236666928778 "
+      "8885969173395223311 8755229097686715671 1319918912921290459 12669348064964076026 8347713925484594194 "
+      "7131861330014060437 18013870281775146388 18428665316372779146 14853358900946804630 11802355386662327119 "
+      "12234197935566505458 1562495104086441070 4789216534718205848 4797493321171570223 11489013455481650173 "
+      "15991479755025557462 5193680184864207374 538012454739572314 12691714223537549902 1625023157851387876 "
+      "15523181006631146535 5212032641878267513 314342477903124542 7039243305239188299 13660860177517273492 "
+      "279600177346432632 1242305966848399256 3754792367662117676 7456833993243708712 5524526481950173096 "
+      "12629250495102362491 12320658947410481525 8375636660933855231 14586189041466889258 3338611085276113663 "
+      "6796460652093029292 17977915623844806175 6678041303167794104 162629150799512142 12738967623757853162 "
+      "15873580300066345221 1661173500922285893"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  unsigned as[] = {3, 5, 7};
+  std::seed_seq sseq(as, as + 3);
+  std::mt19937_64 e1(sseq);
+  std::ostringstream os;
+  os << e1;
+  LIBCPP_ASSERT(os.str() == a);
+  (void)a;
 }
 
-int main(int, char**)
-{
-    test1();
-    test2();
+int main(int, char**) {
+  test1();
+  test2();
 
   return 0;
 }
diff --git a/libcxx/utils/libcxx/test/features/libcxx_macros.py b/libcxx/utils/libcxx/test/features/libcxx_macros.py
index a3b32a9c88051..b8c0e13a622c9 100644
--- a/libcxx/utils/libcxx/test/features/libcxx_macros.py
+++ b/libcxx/utils/libcxx/test/features/libcxx_macros.py
@@ -35,6 +35,7 @@
     "_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING": "libcpp-abi-no-compressed-pair-padding",
     "_LIBCPP_PSTL_BACKEND_LIBDISPATCH": "libcpp-pstl-backend-libdispatch",
     "_LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE": "libcpp-abi-use-small-deque-block-size",
+    "_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine"
 }
 for macro, feature in macros.items():
     features.append(

>From 721e2ea620a480cdc928ccdd5351bea781a6ad43 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 3 Jul 2026 10:21:34 -0400
Subject: [PATCH 2/2] Update libcxx/utils/libcxx/test/features/libcxx_macros.py

---
 libcxx/utils/libcxx/test/features/libcxx_macros.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/utils/libcxx/test/features/libcxx_macros.py b/libcxx/utils/libcxx/test/features/libcxx_macros.py
index b8c0e13a622c9..bcc081e2dc2de 100644
--- a/libcxx/utils/libcxx/test/features/libcxx_macros.py
+++ b/libcxx/utils/libcxx/test/features/libcxx_macros.py
@@ -35,7 +35,7 @@
     "_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING": "libcpp-abi-no-compressed-pair-padding",
     "_LIBCPP_PSTL_BACKEND_LIBDISPATCH": "libcpp-pstl-backend-libdispatch",
     "_LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE": "libcpp-abi-use-small-deque-block-size",
-    "_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine"
+    "_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine",
 }
 for macro, feature in macros.items():
     features.append(


From libcxx-commits at lists.llvm.org  Fri Jul  3 07:22:12 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 07:22:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a47c594.3157459c.24b6e5.8a78@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/206423

>From a521c654c3a7ac6e20c67f47f2c739c317e850a7 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 29 Jun 2026 10:00:42 +0200
Subject: [PATCH 1/2] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine

---
 libcxx/include/__configuration/abi.h          |   1 +
 .../__random/mersenne_twister_engine.h        |  43 +-
 libcxx/test/benchmarks/random.bench.cpp       |  25 +
 .../rand.eng.mers/ctor_result_type.pass.cpp   | 583 +++++++++------
 .../rand.eng/rand.eng.mers/ctor_sseq.pass.cpp | 697 +++++++++++-------
 .../libcxx/test/features/libcxx_macros.py     |   1 +
 6 files changed, 836 insertions(+), 514 deletions(-)

diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 51c82d99eec30..7a4c479fd1636 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -77,6 +77,7 @@
 #  define _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION
 #  define _LIBCPP_ABI_TRIVIALLY_COPYABLE_BIT_ITERATOR
 #  define _LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE
+#  define _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
 
 #elif _LIBCPP_ABI_VERSION == 1
 // Feature macros for disabling pre ABI v1 features. All of these options
diff --git a/libcxx/include/__random/mersenne_twister_engine.h b/libcxx/include/__random/mersenne_twister_engine.h
index ce82b3387dbd2..8e49b3b267aa8 100644
--- a/libcxx/include/__random/mersenne_twister_engine.h
+++ b/libcxx/include/__random/mersenne_twister_engine.h
@@ -181,6 +181,9 @@ class mersenne_twister_engine {
     for (size_t __i = 1; __i < __n; ++__i)
       __x_[__i] = (__f * (__x_[__i - 1] ^ __rshift<__w - 2>(__x_[__i - 1])) + __i) & _Max;
     __i_ = 0;
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    __update_all_states();
+#endif
   }
   template <class _Sseq, __enable_if_t<__is_seed_sequence_v<_Sseq, mersenne_twister_engine>, int> = 0>
   _LIBCPP_HIDE_FROM_ABI void seed(_Sseq& __q) {
@@ -203,17 +206,43 @@ class mersenne_twister_engine {
           return;
       __x_[0] = result_type(1) << (__w - 1);
     }
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    __update_all_states();
+#endif
+  }
+
+  void __update_state(size_t __i, size_t __k) {
+    const size_t __j         = (__i + 1) % __n;
+    const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
+    const result_type __yp   = (__x_[__i] & ~__mask) | (__x_[__j] & __mask);
+    __x_[__i]                = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
+  }
+
+  void __update_state(size_t __i) { __update_state(__i, (__i + __m) % __n); }
+
+  void __update_all_states() {
+    size_t __i = 0;
+    // This is split into two loops to help the compiler vectorize the code
+    for (; __i != (__n - __m); ++__i)
+      __update_state(__i);
+    for (size_t __j = 0; __i != __n; ++__i, ++__j)
+      __update_state(__i, __j);
   }
 
   // generating functions
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI result_type operator()() {
-    const size_t __j         = (__i_ + 1) % __n;
-    const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
-    const result_type __yp   = (__x_[__i_] & ~__mask) | (__x_[__j] & __mask);
-    const size_t __k         = (__i_ + __m) % __n;
-    __x_[__i_]               = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
-    result_type __z          = __x_[__i_] ^ (__rshift<__u>(__x_[__i_]) & __d);
-    __i_                     = __j;
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    result_type __val = __x_[__i_];
+    if (++__i_ == __n) [[__unlikely__]] {
+      __update_all_states();
+      __i_ = 0;
+    }
+#else
+    __update_state(__i_);
+    result_type __val = __x_[__i_];
+    __i_              = (__i_ + 1) % __n;
+#endif
+    result_type __z = __val ^ (__rshift<__u>(__val) & __d);
     __z ^= __lshift<__s>(__z) & __b;
     __z ^= __lshift<__t>(__z) & __c;
     return __z ^ __rshift<__l>(__z);
diff --git a/libcxx/test/benchmarks/random.bench.cpp b/libcxx/test/benchmarks/random.bench.cpp
index e6af4c3e26eaf..15e16b988a29f 100644
--- a/libcxx/test/benchmarks/random.bench.cpp
+++ b/libcxx/test/benchmarks/random.bench.cpp
@@ -34,4 +34,29 @@ static void BM_SeedSeq_Generate(benchmark::State& state) {
 }
 BENCHMARK(BM_SeedSeq_Generate)->Ranges({{1, MAX_SEED_LEN}, {1, MAX_BUFFER_LEN}});
 
+template <class Engine>
+static void BM_engine(benchmark::State& state) {
+  Engine engine;
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(engine());
+  }
+}
+BENCHMARK(BM_engine<std::mt19937_64>)->Name("std::mt19937_64::operator()");
+BENCHMARK(BM_engine<std::mt19937>)->Name("std::mt19937::operator()");
+
+template <class Engine>
+static void BM_engine_array(benchmark::State& state) {
+  Engine engine;
+
+  typename Engine::result_type buffer[128];
+
+  for (auto _ : state) {
+    std::generate(std::begin(buffer), std::end(buffer), std::ref(engine));
+    benchmark::DoNotOptimize(buffer);
+  }
+}
+BENCHMARK(BM_engine_array<std::mt19937_64>)->Name("std::mt19937_64::operator() (into array)");
+BENCHMARK(BM_engine_array<std::mt19937>)->Name("std::mt19937::operator() (into array)");
+
 BENCHMARK_MAIN();
diff --git a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
index 02df0345525cb..b2ae5141edb06 100644
--- a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
@@ -20,237 +20,371 @@
 // Serializing/deserializing the state of the RNG requires iostreams
 // UNSUPPORTED: no-localization
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME && libcpp-abi-vectorized-mersenne-twister-engine
+
 #include <random>
 #include <sstream>
 #include <cassert>
 
 #include "test_macros.h"
 #if TEST_STD_VER >= 11
-#include "make_implicit.h"
-#include "test_convertible.h"
+#  include "make_implicit.h"
+#  include "test_convertible.h"
 #endif
 
 template <class T>
-std::string
-to_string(T const &e)
-{
-    std::ostringstream os;
-    os << e;
-    return os.str();
+std::string to_string(T const& e) {
+  std::ostringstream os;
+  os << e;
+  return os.str();
 }
 
-void
-test1()
-{
-    const char* a = "0 1 1812433255 1900727105 1208447044 2481403966 4042607538 337614300 "
-    "3232553940 1018809052 3202401494 1775180719 3192392114 594215549 184016991 "
-    "829906058 610491522 3879932251 3139825610 297902587 4075895579 2943625357 "
-    "3530655617 1423771745 2135928312 2891506774 1066338622 135451537 933040465 "
-    "2759011858 2273819758 3545703099 2516396728 1272276355 3172048492 "
-    "3267256201 2332199830 1975469449 392443598 1132453229 2900699076 "
-    "1998300999 3847713992 512669506 1227792182 1629110240 112303347 2142631694 "
-    "3647635483 1715036585 2508091258 1355887243 1884998310 3906360088 "
-    "952450269 3647883368 3962623343 3077504981 2023096077 3791588343 "
-    "3937487744 3455116780 1218485897 1374508007 2815569918 1367263917 "
-    "472908318 2263147545 1461547499 4126813079 2383504810 64750479 2963140275 "
-    "1709368606 4143643781 835933993 1881494649 674663333 2076403047 858036109 "
-    "1667579889 1706666497 607785554 1995775149 1941986352 3448871082 "
-    "2109910019 1474883361 1623095288 1831376534 2612738285 81681830 2204289242 "
-    "1365038485 251164610 4268495337 1805601714 1262528768 1442526919 "
-    "1675006593 965627108 646339161 499795587 840887574 380522518 3023789847 "
-    "1457635507 1947093157 2600365344 2729853143 1550618999 1390905853 "
-    "3021294812 882647559 838872117 1663880796 4222103589 2754172275 3844026123 "
-    "3199260319 4176064873 3591027019 2690294242 2978135515 3172796441 "
-    "3263669796 1451257057 1427035359 4174826006 2171992010 1537002090 "
-    "3122405306 4162452508 3271954368 3794310005 3240514581 1270412086 "
-    "3030475836 2281945856 2644171349 3109139423 4253563838 1289926431 "
-    "1396919653 733220100 2753316645 1196225013 3699575255 3569440056 "
-    "2675979228 2624079148 3463113149 863430286 623703199 2113837653 2656425919 "
-    "175981357 4271478366 4238022735 1665483419 86880610 2963435083 1830392943 "
-    "847801865 3237296945 332143967 3973606945 2671879697 2236330279 2360127810 "
-    "3283955434 203240344 4048139172 13189264 2263058814 247241371 1566765783 "
-    "3084408095 3719371299 1958375251 1985924622 1712739232 1861691451 "
-    "2644502937 2337807839 784993770 2962208780 2190810177 1523122731 "
-    "714888527 578678761 3698481324 1801168075 534650483 3390213921 3923356461 "
-    "3586009066 2059432114 52511333 1969897376 3630122061 524661135 3513619765 "
-    "563070233 501359785 477489274 658768624 938973567 1548584683 1345287459 "
-    "2488691004 3441144905 3849305094 2430000078 855172178 614463281 2092744749 "
-    "176381493 1655802051 2273888101 2474494847 3471978030 2138918303 575352373 "
-    "1658230985 1675972553 2946663114 915579339 284981499 53939948 3022598146 "
-    "1861218535 3403620774 4203516930 2360471119 3134536268 1383448498 "
-    "1307602316 3847663247 3027225131 3597251613 3186237127 725127595 "
-    "1928526954 1843386923 3560410503 54688266 1791983849 2519860352 4256389699 "
-    "2328812602 486464275 3578698363 301279829 1303654791 4181868765 971794070 "
-    "1933885487 3996807464 2144053754 4079903755 3775774765 3481760044 "
-    "1212862354 1067356423 3764189132 1609862325 2209601551 2565747501 "
-    "161962392 4045451782 2605574664 2520953090 3490240017 1082791980 44474324 "
-    "101811128 4268650669 4171338684 772375154 3920460306 2319139534 599033750 "
-    "2950874441 3373922995 1496848525 4095253594 1271943484 1498723121 "
-    "3097453329 3698082465 281869581 3148270661 3591477288 747441437 2809508504 "
-    "3896107498 303747862 2368081624 1844217645 886825352 287949781 1444561207 "
-    "2512101757 2062331723 741720931 1383797313 3876746355 2041045348 "
-    "2627599118 1124169970 200524822 3484820454 55883666 1135054804 669498692 "
-    "2677215504 3097911127 1509628615 617580381 2229022193 85601568 3243896546 "
-    "3715672328 912168347 2359163500 1180347564 4243175048 2092067103 880183327 "
-    "4000664709 2045044777 3500474644 1515175520 1862207123 186628841 "
-    "3337252925 708933575 4015964629 3136815297 3314919747 2891909013 "
-    "3316567785 3944275369 3608506218 2884839110 3054055598 2707439927 "
-    "1381111877 3275487281 4292456216 2639563270 3327301876 3576924628 "
-    "721056309 2002808140 748967365 52380958 2200261692 763456477 1708381337 "
-    "2038446433 2682979402 1526413779 2211263302 3879771969 75966584 3645059271 "
-    "2985763524 4085690255 82390958 1883631385 1647521260 1598026998 3038041577 "
-    "2501913134 3279302868 1738888524 805035483 756399074 3863810982 1097797270 "
-    "1505792529 898904527 583561003 717152376 3333867738 1099456544 1663473545 "
-    "1242141229 3828627682 1966201676 1713552361 3852160017 1584965284 21695908 "
-    "1013262144 145341901 3995441263 3462066219 2239637848 1214086163 "
-    "2428868268 1650037305 1545513388 1621198806 4232947817 1823092073 "
-    "256414624 1745018809 1357102386 2055139770 3280958307 2482431613 "
-    "1664870585 859130423 4097751123 3079768369 2470211009 2984880786 "
-    "2808568948 2877071923 1984903163 302768457 1866396789 869566317 3746415787 "
-    "4169433075 3025005404 3980733379 3539207278 3953071536 876960847 "
-    "2548872156 800507464 1865466907 1273317878 3754712872 1757188269 "
-    "3229950355 3731640200 2283390608 2204990292 411873449 447423849 1852437802 "
-    "472825525 3044219944 2913114194 1859709265 4053786194 574820536 2104496732 "
-    "865469814 2438352724 4208743605 4215067542 1364015250 4139974345 "
-    "3838747005 1818502786 2914274940 1402365828 1751123528 2302578077 "
-    "2463168652 1968705496 1730700144 3023943273 1139096844 2658667767 "
-    "2063547264 705791165 1444775274 2415454225 1575664730 921044163 648101324 "
-    "1212387162 4191962054 1787702169 1888718041 1518218010 3398792842 "
-    "4079359729 149721439 750400353 2661036076 3802767886 520152586 951852508 "
-    "2939585975 1375969109 385733137 3523607459 1902438415 4250996086 "
-    "2712727066 484493674 3932107461 1428488210 1764242548 3424801055 "
-    "4004904451 2226862072 2393366939 3609584727 3614444319 317349896 "
-    "3826527525 204023804 981902443 3356042039 3051207045 1869902661 561831895 "
-    "3706675415 1527687593 1227610446 2596341042 3191717368 3269246891 "
-    "557877074 4062070629 3052520266 3772487029 400039836 3195205275 4085394797 "
-    "1655557239 1345770144 2864727192 449281238 73189507 528365765 2727400656 "
-    "247880434 2408277395 777039183 2210179398 1088433648 2124356402 1555630141 "
-    "604790219 195012151 3312518356 923728373 3999251660 3313059535 3478133921 "
-    "3395026960 383464614 3425869222 2446885186 4032184426 157195416 3158909476 "
-    "1663750443 2046427584 1658453076 1784483001 3146546889 1238739785 "
-    "2297306523 3472330897 2953326031 2421672215 1221694592 1588568605 "
-    "2546987845 3375168573 2137961649 3056565164 330165219 235900365 1000384800 "
-    "2697255904 579122283 3050664825 73426122 1232986102 2940571064 3076486824 "
-    "1708182873 2796363264 292154131 4280019913 1102652157 1185393592 "
-    "1494991690 4270076389 2384840717 425785147 2385321880 317514772 3926962743 "
-    "392176856 3465421709 1878853468 122662664 2958252160 1858961315 2244939588 "
-    "2361884409 2860936803 683833250 3291277128 1686857206 1112632275 "
-    "1200680507 3342928196 2677058150 939442136 3407104669 2906783932 "
-    "3668048733 2030009470 1910839172 1234925283 3575831445 123595418 "
-    "2362440495 3048484911 1796872496";
-    std::mt19937 e1(0);
-    assert(to_string(e1) == a);
+void test1() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "0 1 1812433255 1900727105 1208447044 2481403966 4042607538 337614300 "
+      "3232553940 1018809052 3202401494 1775180719 3192392114 594215549 184016991 "
+      "829906058 610491522 3879932251 3139825610 297902587 4075895579 2943625357 "
+      "3530655617 1423771745 2135928312 2891506774 1066338622 135451537 933040465 "
+      "2759011858 2273819758 3545703099 2516396728 1272276355 3172048492 "
+      "3267256201 2332199830 1975469449 392443598 1132453229 2900699076 "
+      "1998300999 3847713992 512669506 1227792182 1629110240 112303347 2142631694 "
+      "3647635483 1715036585 2508091258 1355887243 1884998310 3906360088 "
+      "952450269 3647883368 3962623343 3077504981 2023096077 3791588343 "
+      "3937487744 3455116780 1218485897 1374508007 2815569918 1367263917 "
+      "472908318 2263147545 1461547499 4126813079 2383504810 64750479 2963140275 "
+      "1709368606 4143643781 835933993 1881494649 674663333 2076403047 858036109 "
+      "1667579889 1706666497 607785554 1995775149 1941986352 3448871082 "
+      "2109910019 1474883361 1623095288 1831376534 2612738285 81681830 2204289242 "
+      "1365038485 251164610 4268495337 1805601714 1262528768 1442526919 "
+      "1675006593 965627108 646339161 499795587 840887574 380522518 3023789847 "
+      "1457635507 1947093157 2600365344 2729853143 1550618999 1390905853 "
+      "3021294812 882647559 838872117 1663880796 4222103589 2754172275 3844026123 "
+      "3199260319 4176064873 3591027019 2690294242 2978135515 3172796441 "
+      "3263669796 1451257057 1427035359 4174826006 2171992010 1537002090 "
+      "3122405306 4162452508 3271954368 3794310005 3240514581 1270412086 "
+      "3030475836 2281945856 2644171349 3109139423 4253563838 1289926431 "
+      "1396919653 733220100 2753316645 1196225013 3699575255 3569440056 "
+      "2675979228 2624079148 3463113149 863430286 623703199 2113837653 2656425919 "
+      "175981357 4271478366 4238022735 1665483419 86880610 2963435083 1830392943 "
+      "847801865 3237296945 332143967 3973606945 2671879697 2236330279 2360127810 "
+      "3283955434 203240344 4048139172 13189264 2263058814 247241371 1566765783 "
+      "3084408095 3719371299 1958375251 1985924622 1712739232 1861691451 "
+      "2644502937 2337807839 784993770 2962208780 2190810177 1523122731 "
+      "714888527 578678761 3698481324 1801168075 534650483 3390213921 3923356461 "
+      "3586009066 2059432114 52511333 1969897376 3630122061 524661135 3513619765 "
+      "563070233 501359785 477489274 658768624 938973567 1548584683 1345287459 "
+      "2488691004 3441144905 3849305094 2430000078 855172178 614463281 2092744749 "
+      "176381493 1655802051 2273888101 2474494847 3471978030 2138918303 575352373 "
+      "1658230985 1675972553 2946663114 915579339 284981499 53939948 3022598146 "
+      "1861218535 3403620774 4203516930 2360471119 3134536268 1383448498 "
+      "1307602316 3847663247 3027225131 3597251613 3186237127 725127595 "
+      "1928526954 1843386923 3560410503 54688266 1791983849 2519860352 4256389699 "
+      "2328812602 486464275 3578698363 301279829 1303654791 4181868765 971794070 "
+      "1933885487 3996807464 2144053754 4079903755 3775774765 3481760044 "
+      "1212862354 1067356423 3764189132 1609862325 2209601551 2565747501 "
+      "161962392 4045451782 2605574664 2520953090 3490240017 1082791980 44474324 "
+      "101811128 4268650669 4171338684 772375154 3920460306 2319139534 599033750 "
+      "2950874441 3373922995 1496848525 4095253594 1271943484 1498723121 "
+      "3097453329 3698082465 281869581 3148270661 3591477288 747441437 2809508504 "
+      "3896107498 303747862 2368081624 1844217645 886825352 287949781 1444561207 "
+      "2512101757 2062331723 741720931 1383797313 3876746355 2041045348 "
+      "2627599118 1124169970 200524822 3484820454 55883666 1135054804 669498692 "
+      "2677215504 3097911127 1509628615 617580381 2229022193 85601568 3243896546 "
+      "3715672328 912168347 2359163500 1180347564 4243175048 2092067103 880183327 "
+      "4000664709 2045044777 3500474644 1515175520 1862207123 186628841 "
+      "3337252925 708933575 4015964629 3136815297 3314919747 2891909013 "
+      "3316567785 3944275369 3608506218 2884839110 3054055598 2707439927 "
+      "1381111877 3275487281 4292456216 2639563270 3327301876 3576924628 "
+      "721056309 2002808140 748967365 52380958 2200261692 763456477 1708381337 "
+      "2038446433 2682979402 1526413779 2211263302 3879771969 75966584 3645059271 "
+      "2985763524 4085690255 82390958 1883631385 1647521260 1598026998 3038041577 "
+      "2501913134 3279302868 1738888524 805035483 756399074 3863810982 1097797270 "
+      "1505792529 898904527 583561003 717152376 3333867738 1099456544 1663473545 "
+      "1242141229 3828627682 1966201676 1713552361 3852160017 1584965284 21695908 "
+      "1013262144 145341901 3995441263 3462066219 2239637848 1214086163 "
+      "2428868268 1650037305 1545513388 1621198806 4232947817 1823092073 "
+      "256414624 1745018809 1357102386 2055139770 3280958307 2482431613 "
+      "1664870585 859130423 4097751123 3079768369 2470211009 2984880786 "
+      "2808568948 2877071923 1984903163 302768457 1866396789 869566317 3746415787 "
+      "4169433075 3025005404 3980733379 3539207278 3953071536 876960847 "
+      "2548872156 800507464 1865466907 1273317878 3754712872 1757188269 "
+      "3229950355 3731640200 2283390608 2204990292 411873449 447423849 1852437802 "
+      "472825525 3044219944 2913114194 1859709265 4053786194 574820536 2104496732 "
+      "865469814 2438352724 4208743605 4215067542 1364015250 4139974345 "
+      "3838747005 1818502786 2914274940 1402365828 1751123528 2302578077 "
+      "2463168652 1968705496 1730700144 3023943273 1139096844 2658667767 "
+      "2063547264 705791165 1444775274 2415454225 1575664730 921044163 648101324 "
+      "1212387162 4191962054 1787702169 1888718041 1518218010 3398792842 "
+      "4079359729 149721439 750400353 2661036076 3802767886 520152586 951852508 "
+      "2939585975 1375969109 385733137 3523607459 1902438415 4250996086 "
+      "2712727066 484493674 3932107461 1428488210 1764242548 3424801055 "
+      "4004904451 2226862072 2393366939 3609584727 3614444319 317349896 "
+      "3826527525 204023804 981902443 3356042039 3051207045 1869902661 561831895 "
+      "3706675415 1527687593 1227610446 2596341042 3191717368 3269246891 "
+      "557877074 4062070629 3052520266 3772487029 400039836 3195205275 4085394797 "
+      "1655557239 1345770144 2864727192 449281238 73189507 528365765 2727400656 "
+      "247880434 2408277395 777039183 2210179398 1088433648 2124356402 1555630141 "
+      "604790219 195012151 3312518356 923728373 3999251660 3313059535 3478133921 "
+      "3395026960 383464614 3425869222 2446885186 4032184426 157195416 3158909476 "
+      "1663750443 2046427584 1658453076 1784483001 3146546889 1238739785 "
+      "2297306523 3472330897 2953326031 2421672215 1221694592 1588568605 "
+      "2546987845 3375168573 2137961649 3056565164 330165219 235900365 1000384800 "
+      "2697255904 579122283 3050664825 73426122 1232986102 2940571064 3076486824 "
+      "1708182873 2796363264 292154131 4280019913 1102652157 1185393592 "
+      "1494991690 4270076389 2384840717 425785147 2385321880 317514772 3926962743 "
+      "392176856 3465421709 1878853468 122662664 2958252160 1858961315 2244939588 "
+      "2361884409 2860936803 683833250 3291277128 1686857206 1112632275 "
+      "1200680507 3342928196 2677058150 939442136 3407104669 2906783932 "
+      "3668048733 2030009470 1910839172 1234925283 3575831445 123595418 "
+      "2362440495 3048484911 1796872496"
+#else
+      "2443250962 1093594115 1878467924 2709361018 1101979660 3904844661 676747479 2085143622 1056793272 3812477442 "
+      "2168787041 275552121 2696932952 3432054210 1657102335 3518946594 962584079 1051271004 3806145045 1414436097 "
+      "2032348584 1661738718 1116708477 2562755208 3176189976 696824676 2399811678 3992505346 569184356 2626558620 "
+      "136797809 4273176064 296167901 3430730584 331909803 1908676996 1950065095 604298543 3615988338 1570232852 "
+      "1028209748 1511467721 2411887154 4210753555 3096762720 423429618 659966766 2937509307 2222847265 378636552 "
+      "1142109618 2509241601 1521729757 888533219 250885260 2455816244 4046047811 1947467789 1395351953 2388948566 "
+      "934627940 194642258 1429256273 2139959677 1543740405 1569613451 4061840539 2075690423 824532376 844152077 "
+      "3218002536 897315311 823414659 1007534698 3139313867 2332794462 3675156583 722594429 3715123011 2656840676 "
+      "2373277715 1516144243 1538161999 1399719297 1448820876 3180842436 2020192709 3279498509 1048107540 3094290376 "
+      "3706920703 3810051683 4003871775 1069557309 151535004 667695756 861767313 1643171871 3649507728 3785763588 "
+      "737899497 2332908276 4082123854 1584539673 3929877280 1046086041 1304062606 2311324008 1190424879 538647603 "
+      "1728806718 148948582 659721465 2351006009 185647941 1825934330 2126619619 2230539295 3878421575 965937340 "
+      "2835563987 419858879 1527311104 2042333483 2745134265 3540517373 1100062165 2308613697 2690739856 2048293545 "
+      "2742876486 2408502115 53398423 3089771461 1398529613 2132477261 504134301 1535108421 1968507173 3412472770 "
+      "4048517196 3509143071 857063211 1429875570 4120479871 2786574360 2470799871 1644104875 2326863162 2033900387 "
+      "274888909 2629973384 1154405937 1839988197 2163830694 268733679 2928953965 387153042 3790900490 3202349717 "
+      "3799209681 2524871464 3801447311 3543081470 1801633977 3870370950 1581678156 358283485 4133385582 4052311650 "
+      "249498956 1712028623 3618329357 3395106946 2704820515 26653208 2437509555 4192186371 3639946838 2615917799 "
+      "294624699 463886267 2477203673 2514051526 4162882381 2937629358 3180524454 1388392394 2637132139 1999482082 "
+      "1870663339 2852309250 3489430700 1116063402 1673637188 1941955790 4081919937 2322920953 3500518887 2722575905 "
+      "405031829 3728964889 3464559963 665516555 1626835870 2523729468 238289561 497556937 2581429740 3456619478 "
+      "2601389837 820426192 268141532 2654829421 349692193 2426957585 1465734488 85095426 1080895536 2823366942 "
+      "379322515 2938986739 1561328720 2940081697 607513620 2724557130 2841377034 14123440 1085631349 1977859413 "
+      "1325586902 1690398943 2516154740 4144373551 555899232 1472707937 3318086852 719779833 3545481907 1384350857 "
+      "191709822 2924586997 3905397527 2528674757 2377804096 2742818576 4162971050 1916415976 2229076768 132706400 "
+      "1337415646 251604474 4168783505 819499074 1213291699 2098930479 1009044052 1060792037 2169940733 2973281031 "
+      "636656529 1946890813 367099469 4076624283 338339529 561343735 3309733580 3895891229 516195845 3075613489 "
+      "3076388119 4088758753 3895582245 1192445752 2923797073 2272605437 2964193665 943979628 3263904824 1852145108 "
+      "1911050628 1596880711 479795791 212699077 2172697748 1764175804 3951845064 2182587171 2396848133 2718837214 "
+      "2934177572 3638054152 914795167 1028977354 1744985339 1161407853 2069746822 3109934340 3666150054 729399111 "
+      "2911998815 152809615 417267263 1064343325 2751707923 1833270972 886939906 4054231201 1410366452 976142646 "
+      "1452795786 1904234879 4231894541 1505392175 3498767279 833704604 2100592828 688268099 1752675602 1975556664 "
+      "2102210733 698665709 1232104968 4051336323 1740516649 3131721670 3755143232 3433756601 138325540 1566724819 "
+      "3146312914 3262098346 1400757929 3821885720 356740291 4236900846 3962771215 3383537419 3432281817 3700484231 "
+      "1124512300 4040103150 2152814065 357241942 3507144572 3156778000 4029389048 1495464238 2710816120 618937484 "
+      "923913256 3233161411 3113338135 2380534800 2989134055 792458413 2073679910 2733527896 1078384210 2832175364 "
+      "501385130 1554930792 2339252603 535487294 2983664186 924228889 2127502206 2847205678 836935943 1902192892 "
+      "4094716961 3296730761 2307406435 281523156 728701372 3943302480 183005637 2660706047 2320033913 2011717602 "
+      "219970222 890345073 2479236311 653839655 38538414 3266308455 3726363269 1256762826 712114913 3499059198 "
+      "2847394904 3246075282 1299462737 830090526 369065487 3899451086 1825910875 2697294244 2553197317 2501134241 "
+      "930821716 2841281141 3920808923 3209045349 3381621736 2145077949 1958091404 371971179 3064957112 3151429184 "
+      "2833651995 1089438243 2146134351 1426568997 3528669710 1043897757 3052263589 3205925012 4076215371 2622476950 "
+      "2385717100 2179892838 3820135605 1574628644 177604240 1712062317 1192081671 1107476027 837376039 2818244460 "
+      "1382379731 2749207910 2644203474 1508860349 3006844699 3157252375 3789683778 1985542020 2309789126 1624089624 "
+      "3660683301 1432444043 1172735898 3271587635 841387671 3614623669 3002115850 1078807738 1095345284 1707086713 "
+      "4263485053 445506874 3148772337 2716917408 3836812085 1026228926 491069724 3983218285 2410582206 3821759029 "
+      "3789305873 1223256738 1677180485 1481771733 1670286271 2852111711 1628943665 2289772949 3483580787 2129860019 "
+      "3955695109 28974017 2289227173 1725243773 483357125 111587742 347631238 1805029491 839978009 339030914 "
+      "2442782449 1701726526 1481742442 3703534067 3796147861 235078546 3195450129 1420184214 998981944 163541155 "
+      "2087693215 3827554748 3003331872 1967967170 160190917 544473278 3884602428 4180513186 1603446876 2191744556 "
+      "1938251010 291242497 1764195107 4076051069 3860380798 806452940 2631256196 949838208 4096577802 1519225265 "
+      "2296800559 1015848400 2864792481 88004533 195132439 966918321 1451347413 2322994051 3585658769 1771598947 "
+      "3308395968 950148946 3846061568 564475171 1345892193 2439581032 2907206869 1297626422 3779454571 818255727 "
+      "705738065 3915384184 4133726754 2724859327 3773743817 326325901 2766390048 2562130418 1885210076 2622324981 "
+      "1727396790 1742653294 3133131942 3790857080 197042473 2868410679 1122976862 2264474389 3607393378 744386048 "
+      "3122128592 1101489146 4064675687 2818166901 2282076661 370818870 3053423257 2272652338 4077081984 4284213290 "
+      "1728304803 3902867896 1643530236 306212760 144705843 498541197 3103269575 4116304893 1646288275 2110757680 "
+      "4092919505 3390929578 1828116798 1847914177 1156754878 2985404552 3970881126 3035874635 2189824678 2637114092 "
+      "2588332748 3917818803 3302695646 1758418726 1934759438 3871576937 1360769189 1051808374 1881390541 757367126 "
+      "3253778407 1685049720 805311189 934898036 95682794 2052240235 3475933669 2213474191 3341273357 2443286051 "
+      "4272536794 696819230 878775027 3332634155 3405126958 2824772573 95284505 3040640920 2412859827 2959515765 "
+      "1764792340 1504924482 2116910326 2293943003 3199262907 1443740894 3900566588 343066614 1927475335 249220892 "
+      "2920636502 3593328327 2648828502 1678096082"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  std::mt19937 e1(0);
+  LIBCPP_ASSERT(to_string(e1) == a);
+  (void)a;
 }
 
-void
-test2()
-{
-    const char* a = "0 1 6364136223846793007 13885033948157127961 "
-    "15324573939901584278 12737837167382305846 15195339788985155882 "
-    "6554113247712070460 17235932740818599105 13007415075556305955 "
-    "6585479514541334743 8274505865835507625 1718218088692873364 "
-    "10390651247454232081 12911994993614796389 3986900029987203370 "
-    "6673827651897561714 4426752746717694792 7419158062930293690 "
-    "5800047417539173618 15710773105226458059 16164512590413496893 "
-    "3438015953120274172 3483801391287623267 293704481016263807 "
-    "11580856846363212652 3489109114147636336 3391036861618673611 "
-    "8265793309278544843 7557898467821912223 11008748280761875940 "
-    "15929443707841919885 8545695347411085846 10810459396490399532 "
-    "12233244910455127352 15556950738631379285 16711543556686614082 "
-    "12362193084052127890 16520645558585805174 5163125267185202360 "
-    "405552980610370477 17567412011316060306 18195950784827697319 "
-    "7893142112162906367 11294475722810961618 7284845498332539581 "
-    "8406882439401998138 4375387785957411470 9627875716250684710 "
-    "8860968026642934661 9743109216691708518 152611520104818631 "
-    "5897430410700879663 5351672954305365323 16325991383734641720 "
-    "9695181037355459478 15420132328343498044 17772146581546890572 "
-    "12095930435311226909 3066005052193896110 11579395203346116306 "
-    "9168946227698330317 18318927644793076250 16096433325093805476 "
-    "14945900876820434843 16826760579960858105 17664119339058295604 "
-    "17844797344364136942 1071414400549507565 16688779616725465582 "
-    "3684635020921274863 12774052823168116810 17270284502989966576 "
-    "1081012692742984704 4377021575203177546 18341292555997099853 "
-    "13297939683513494274 15065725504474304333 10796297883750572804 "
-    "15233335271871291997 8767977593699151062 3360856014170688284 "
-    "7828232912764786750 15167717223619970150 9622174963375022357 "
-    "18262792478991268448 1196631425707106493 5368342740538672272 "
-    "10381091599850241237 12108437846306626340 6150860188928778248 "
-    "3342980288459577584 12715439159457051276 17996971042887275859 "
-    "9749679821487730542 17763727344608586331 16024467606290078168 "
-    "7763401887585513696 4448278918811662063 16947956613780322662 "
-    "15144807360840708645 3878082612940188435 10709780449699561405 "
-    "1649555943517500922 3206645931693769562 12562913950237146427 "
-    "237213339742767727 12987800257476421358 1653669854585203688 "
-    "3485900643226898485 13961759114404652223 5243794832751327611 "
-    "10337687908642742498 16946139522050041809 16716562961992396380 "
-    "4275124606042261542 4055100795824867618 6424268654905981295 "
-    "3424516503413156556 2670380025813203539 10750762735193959951 "
-    "8790031149370411970 4021216986392972993 12076090355041998696 "
-    "14407920322903159838 10653597737935867030 15483225617438352002 "
-    "2497775263858626604 12295882369431088188 14256043521530136935 "
-    "2687322778627883798 3419797801078863201 8786888481486602641 "
-    "445698423634900693 9597067954623467255 7101345576557603992 "
-    "1498579197046783597 10403325187679734962 2464586980321053562 "
-    "2022012026329844477 10802281218030350853 6628929099856200904 "
-    "6828177972863192803 8589868113309334601 5245595233272009016 "
-    "5335692004673212054 4515133017699498525 15966447436053813932 "
-    "15199779177078162007 4190689609934804313 13003438276435994683 "
-    "8406046831313066396 10564320513686955057 12668913223662201488 "
-    "13130110932487580228 1030848205404711145 17684061609212954769 "
-    "12942207438298787911 10731611242140874687 5165052527778107352 "
-    "16323046249518133445 17119162873327029615 5754858052433703070 "
-    "3864761150247579030 9945988334920003074 11409854727071782565 "
-    "5000838138362434817 15526574143469400487 18094554078711846524 "
-    "5576294272011007484 3478525338408894755 11392694223389544658 "
-    "4692963068671452476 4459301637730340710 9699395817392066460 "
-    "14644636990626292085 18065377773424192622 5217202490849387226 "
-    "16175595974171756081 2109372019589047677 1624752883142646445 "
-    "13462209973053735966 12082930933973802402 1568864426788967895 "
-    "17047994306870001795 10556833209957537593 955604103878351641 "
-    "9062985603395234592 9757612676622840969 1767246562613391916 "
-    "9752598821733361274 7499745701633625047 7824811626141302622 "
-    "15819064077972391284 5660565551854829485 17645390577243129343 "
-    "7343780801046581776 2233358068547689666 8716657172695403744 "
-    "9129027798969787220 334709674395230649 2063182499026924878 "
-    "13089071159640936832 1765917316143960741 17552378408917656269 "
-    "3917018959478722819 15626740210483166037 1645962609209923821 "
-    "12277169606472643961 14545894350924442736 11485249378718653961 "
-    "9205208816702766530 10967561305613932827 3105992977398681914 "
-    "2125140299311648264 11619505070220308543 5030167448920096401 "
-    "4248170446421798953 16184577688118775567 9240607582885304823 "
-    "11838996733938359277 415426114101983968 14340734742548675134 "
-    "4124085748228276376 17686494750190224280 9472996569628985376 "
-    "1207013222233148636 3031046462562068367 45068538181330439 "
-    "8678647417835301152 10693327126492781235 3058899219097846020 "
-    "18377730418355022492 10269941972656742364 15986476992758938864 "
-    "14575856764093007010 14749682846448398393 1042926396621439263 "
-    "12184905641567868001 3518848236485931862 6718580865438347534 "
-    "6319395993260741012 2855168874111910691 2482146419106261786 "
-    "17290238774162848390 8071397865265268054 15873003794708011585 "
-    "14422764926380465297 14140979091525022882 3573480287238168646 "
-    "1525896111244666696 7537826952717918371 10482908122538761078 "
-    "17003233041653857 9473838740924911883 8438240966750123668 "
-    "10697754962581554225 15048771265786776312 9067877678399943713 "
-    "3399555692325948067 6150260207049997483 7165140289246675175 "
-    "14816202987105583988 4753550992948864498 10549400354582510015 "
-    "13212062554023586370 1585477630313819722 476999696494664205 "
-    "3208739183359199317 16011681780347380478 8149150693959772807 "
-    "803412833228911773 2360961455092949929 1517204230639934662 "
-    "13863717544358808032 16792122738584967930 12742474971940259289 "
-    "1859755681395355028 1540431035241590810 3883807554140904361 "
-    "16189061625447625933 12376367376041900879 8006563585771266211 "
-    "2682617767291542421 8593924529704142157 9070391845133329273 "
-    "3557484410564396342 9301398051805853085 12632514768298643219 "
-    "227653509634201118 7247795074312488552 4939136716843120792 "
-    "6533591052147596041 1308401457054431629 17488144120120152559 "
-    "14075631579093810083 4015705597302725704 6833920126528811473 "
-    "5095302940809114298 8312250184258072842 15770605014574863643 "
-    "14091690436120485477 15763282477731738396 16394237160547425954 "
-    "5066318118328746621 13140493775100916989 6371148952471982853 "
-    "15150289760867914983 4931341382074091848 12635920082410445322 "
-    "8498109357807439006 14836776625250834986";
-    std::mt19937_64 e1(0);
-    assert(to_string(e1) == a);
+void test2() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "0 1 6364136223846793007 13885033948157127961 "
+      "15324573939901584278 12737837167382305846 15195339788985155882 "
+      "6554113247712070460 17235932740818599105 13007415075556305955 "
+      "6585479514541334743 8274505865835507625 1718218088692873364 "
+      "10390651247454232081 12911994993614796389 3986900029987203370 "
+      "6673827651897561714 4426752746717694792 7419158062930293690 "
+      "5800047417539173618 15710773105226458059 16164512590413496893 "
+      "3438015953120274172 3483801391287623267 293704481016263807 "
+      "11580856846363212652 3489109114147636336 3391036861618673611 "
+      "8265793309278544843 7557898467821912223 11008748280761875940 "
+      "15929443707841919885 8545695347411085846 10810459396490399532 "
+      "12233244910455127352 15556950738631379285 16711543556686614082 "
+      "12362193084052127890 16520645558585805174 5163125267185202360 "
+      "405552980610370477 17567412011316060306 18195950784827697319 "
+      "7893142112162906367 11294475722810961618 7284845498332539581 "
+      "8406882439401998138 4375387785957411470 9627875716250684710 "
+      "8860968026642934661 9743109216691708518 152611520104818631 "
+      "5897430410700879663 5351672954305365323 16325991383734641720 "
+      "9695181037355459478 15420132328343498044 17772146581546890572 "
+      "12095930435311226909 3066005052193896110 11579395203346116306 "
+      "9168946227698330317 18318927644793076250 16096433325093805476 "
+      "14945900876820434843 16826760579960858105 17664119339058295604 "
+      "17844797344364136942 1071414400549507565 16688779616725465582 "
+      "3684635020921274863 12774052823168116810 17270284502989966576 "
+      "1081012692742984704 4377021575203177546 18341292555997099853 "
+      "13297939683513494274 15065725504474304333 10796297883750572804 "
+      "15233335271871291997 8767977593699151062 3360856014170688284 "
+      "7828232912764786750 15167717223619970150 9622174963375022357 "
+      "18262792478991268448 1196631425707106493 5368342740538672272 "
+      "10381091599850241237 12108437846306626340 6150860188928778248 "
+      "3342980288459577584 12715439159457051276 17996971042887275859 "
+      "9749679821487730542 17763727344608586331 16024467606290078168 "
+      "7763401887585513696 4448278918811662063 16947956613780322662 "
+      "15144807360840708645 3878082612940188435 10709780449699561405 "
+      "1649555943517500922 3206645931693769562 12562913950237146427 "
+      "237213339742767727 12987800257476421358 1653669854585203688 "
+      "3485900643226898485 13961759114404652223 5243794832751327611 "
+      "10337687908642742498 16946139522050041809 16716562961992396380 "
+      "4275124606042261542 4055100795824867618 6424268654905981295 "
+      "3424516503413156556 2670380025813203539 10750762735193959951 "
+      "8790031149370411970 4021216986392972993 12076090355041998696 "
+      "14407920322903159838 10653597737935867030 15483225617438352002 "
+      "2497775263858626604 12295882369431088188 14256043521530136935 "
+      "2687322778627883798 3419797801078863201 8786888481486602641 "
+      "445698423634900693 9597067954623467255 7101345576557603992 "
+      "1498579197046783597 10403325187679734962 2464586980321053562 "
+      "2022012026329844477 10802281218030350853 6628929099856200904 "
+      "6828177972863192803 8589868113309334601 5245595233272009016 "
+      "5335692004673212054 4515133017699498525 15966447436053813932 "
+      "15199779177078162007 4190689609934804313 13003438276435994683 "
+      "8406046831313066396 10564320513686955057 12668913223662201488 "
+      "13130110932487580228 1030848205404711145 17684061609212954769 "
+      "12942207438298787911 10731611242140874687 5165052527778107352 "
+      "16323046249518133445 17119162873327029615 5754858052433703070 "
+      "3864761150247579030 9945988334920003074 11409854727071782565 "
+      "5000838138362434817 15526574143469400487 18094554078711846524 "
+      "5576294272011007484 3478525338408894755 11392694223389544658 "
+      "4692963068671452476 4459301637730340710 9699395817392066460 "
+      "14644636990626292085 18065377773424192622 5217202490849387226 "
+      "16175595974171756081 2109372019589047677 1624752883142646445 "
+      "13462209973053735966 12082930933973802402 1568864426788967895 "
+      "17047994306870001795 10556833209957537593 955604103878351641 "
+      "9062985603395234592 9757612676622840969 1767246562613391916 "
+      "9752598821733361274 7499745701633625047 7824811626141302622 "
+      "15819064077972391284 5660565551854829485 17645390577243129343 "
+      "7343780801046581776 2233358068547689666 8716657172695403744 "
+      "9129027798969787220 334709674395230649 2063182499026924878 "
+      "13089071159640936832 1765917316143960741 17552378408917656269 "
+      "3917018959478722819 15626740210483166037 1645962609209923821 "
+      "12277169606472643961 14545894350924442736 11485249378718653961 "
+      "9205208816702766530 10967561305613932827 3105992977398681914 "
+      "2125140299311648264 11619505070220308543 5030167448920096401 "
+      "4248170446421798953 16184577688118775567 9240607582885304823 "
+      "11838996733938359277 415426114101983968 14340734742548675134 "
+      "4124085748228276376 17686494750190224280 9472996569628985376 "
+      "1207013222233148636 3031046462562068367 45068538181330439 "
+      "8678647417835301152 10693327126492781235 3058899219097846020 "
+      "18377730418355022492 10269941972656742364 15986476992758938864 "
+      "14575856764093007010 14749682846448398393 1042926396621439263 "
+      "12184905641567868001 3518848236485931862 6718580865438347534 "
+      "6319395993260741012 2855168874111910691 2482146419106261786 "
+      "17290238774162848390 8071397865265268054 15873003794708011585 "
+      "14422764926380465297 14140979091525022882 3573480287238168646 "
+      "1525896111244666696 7537826952717918371 10482908122538761078 "
+      "17003233041653857 9473838740924911883 8438240966750123668 "
+      "10697754962581554225 15048771265786776312 9067877678399943713 "
+      "3399555692325948067 6150260207049997483 7165140289246675175 "
+      "14816202987105583988 4753550992948864498 10549400354582510015 "
+      "13212062554023586370 1585477630313819722 476999696494664205 "
+      "3208739183359199317 16011681780347380478 8149150693959772807 "
+      "803412833228911773 2360961455092949929 1517204230639934662 "
+      "13863717544358808032 16792122738584967930 12742474971940259289 "
+      "1859755681395355028 1540431035241590810 3883807554140904361 "
+      "16189061625447625933 12376367376041900879 8006563585771266211 "
+      "2682617767291542421 8593924529704142157 9070391845133329273 "
+      "3557484410564396342 9301398051805853085 12632514768298643219 "
+      "227653509634201118 7247795074312488552 4939136716843120792 "
+      "6533591052147596041 1308401457054431629 17488144120120152559 "
+      "14075631579093810083 4015705597302725704 6833920126528811473 "
+      "5095302940809114298 8312250184258072842 15770605014574863643 "
+      "14091690436120485477 15763282477731738396 16394237160547425954 "
+      "5066318118328746621 13140493775100916989 6371148952471982853 "
+      "15150289760867914983 4931341382074091848 12635920082410445322 "
+      "8498109357807439006 14836776625250834986"
+#else
+      "4640967165370225016 475580846709610041 992132072322742490 2879266425597847059 9859212410995834590 "
+      "13110412116137766394 2786888605138474240 12527462973726820895 5232191189150589434 8148228587345305383 "
+      "15985190208517110588 17155057006866468333 5040233137627218077 12716217438209898279 7628670991140263350 "
+      "9633431818170735083 8029874679197696152 2546188705250129595 13105544608186384869 6197138528956131705 "
+      "2500767142257833113 4057403796030124452 4801410225133580521 12712510024708253355 1478853541812684571 "
+      "16899998798183024166 763564441705181102 13204668179160915419 6975570844831914277 12029362619687152075 "
+      "17592155920476593974 1391138604394782251 13557582389860889375 6018426101890403504 7401881601334221625 "
+      "281124061034155254 2261645520472923927 10254468173793190351 4336071085409420785 7075410027066751424 "
+      "7438312290102887001 15139389032212795768 12949861808476919158 5221320262711473213 18444574859163569422 "
+      "3327484732560520915 10371833210785813703 494705948341138486 313693460050824934 801700386546917424 "
+      "3333797513889190239 11734932229126290067 4001689285737041461 17077402128101531244 17161815342609821634 "
+      "4393649844836338524 17495132932599391165 16521868517739667677 5661222119201994079 12972222767581865814 "
+      "11571421678172213790 390646047509318948 11495394588801409501 6525248087626393555 8573656613713326328 "
+      "8142990904973493114 13660896775289126601 17864485770152980871 17445884582967639919 5024202147275854142 "
+      "671342157422491897 8265009237858722295 8608118008319642631 9219145998498351877 4556268366257199356 "
+      "6140599343918232709 1607043993756685523 16576680458922020446 2464344868820317239 11814083511457927881 "
+      "17322648412109813175 1813592025330691328 11477642670415214162 17070997401037634101 2303972879274470462 "
+      "11243326672568121379 3445882696291328363 12839802107382893721 12104481080282265364 2596243342696322386 "
+      "17790946661248112185 16074938231864255959 2956506024687822818 5581797792339843057 16395798806003821452 "
+      "1430185758107085903 18323996060167045894 9286586749315539711 11359324205220105656 13078829860258641519 "
+      "5216000878301612113 9168569275644144847 4001438364296785628 2620979336382597902 17733020247211834335 "
+      "9313982434870058041 14715751745255862275 2010565534563004422 3178715725792479308 1903043159754162676 "
+      "14115336913931799326 2521027868575181436 16014439786100458068 12367605516710589904 209714080394415764 "
+      "1624794657804038156 9935768631398762231 4151749392286673312 7107972358174928160 5660032753352365076 "
+      "18036755596310855736 10390782681895899933 1046990263302077358 7362677844682408358 9462769113228112518 "
+      "16297726431682095630 320526314299713717 3778064933596859787 10885323884409002007 2236973268193149682 "
+      "10878852310456567407 2570883140239125692 2789272264811335056 13059628436710418572 2738898868861677604 "
+      "13893534347610447431 5822583231449316688 6492049503818767408 6244748132635487608 8670290879871155016 "
+      "9029174311949188652 14279734151305645897 15912641926116463991 5242647656726280598 18355120933288718928 "
+      "6020122330537384578 14237586109975592522 4056743947737351776 11111921204484673848 2182608451873446153 "
+      "166740030032355275 6707804438883761942 946217821621517312 17920751901125513000 11238439424546078979 "
+      "9206370960702334859 10367106562255294642 16885700982121403407 5166729472772734774 12763662184890206104 "
+      "5517776110990532992 14067054651980895669 91103470530233291 13189720418381102366 13302941329281487681 "
+      "10033391968256035918 5361508361658563078 9633125940775192275 4070378705292966499 2578880557354159711 "
+      "8213829012818869727 14609024865720281973 5764529456700294955 4441867238869537141 4877335405025028278 "
+      "3719605268964114254 6912476600417191412 12213543493427784533 9772185674504838078 849905513166852628 "
+      "2288907677236569364 13249989292558739703 17027351938809896108 621729935073174643 11783397793000418128 "
+      "6535353235698302382 17474699882318514854 11002055139008150918 18416122529605421577 6901480652243894157 "
+      "10382683051257874235 4029870255315506009 2965992838295436717 6235535328826140656 12567736520515051239 "
+      "1747508188587454664 6183192989050178616 15953675909489391368 10361870655666472156 13992646052760132872 "
+      "18278663149537015721 2341145555999432211 6926699433818498172 13807760559474580665 14452983890116009870 "
+      "11892982856082687091 17809698859762328768 2068107465420962246 7113569128111994893 4401076180719847553 "
+      "11644868941271347491 13124811241626639928 13749911628127226656 17349042196015114201 17667020131618919593 "
+      "5882998107995308535 3994906988813734883 12471577435602039626 6541516905696908495 12651354739990156236 "
+      "2656551093809687944 8349751746116271717 16003262134315245189 16966957802441281611 9846484220369869695 "
+      "289040869684008784 12984844645788078807 15184274406289320733 8585588724744493207 17783251231435661237 "
+      "8433051731677026430 4615759437146530251 7622401495291460413 11623553053949964326 5539054352599742054 "
+      "8346732818944267068 2539102865177431249 12328004780676882137 14682995638339538937 17618899063173726898 "
+      "3560029508967023860 203715062184427611 4331882803273086182 11747937041982829018 14988651560527263167 "
+      "12178986862382858651 3296725696957964696 13476561205033500550 5411341409364572097 6178165960265458773 "
+      "6637587084784255540 2852138145128301044 285104174921779935 3890210696591283091 15859573696692819698 "
+      "4183398296224968094 169433174359854381 11697218950892752694 13577878976046486116 9712904827015064754 "
+      "7617616830643388997 12751433537134599555 12319041734951133946 10239102328951141680 7290886622313676141 "
+      "4739372139955959505 9070795802053402609 10714415902002968511 14446814812660765483 8195026527484180858 "
+      "10331449076861906227 11969763657717422129 11061711610463627412 3682504726309317968 186477037840564317 "
+      "10307292328882458385 11683537113360307954 11300920371008006928 12769285684201107347 14545522978223964713 "
+      "5045546601922369224 202539708050129846 9727300421085682838 10660376569580918468 1852104613210280002 "
+      "2448572796841074793 4275113756623070536 15430856097668330204 8147758262819931807 13018070382604356536 "
+      "1461468238838418536 6307427532280072746 14454983576432818303 16577253122736704814 11165141699850119488 "
+      "1871342067236738412 15245016873857982253 16807633505182897684 18414686740244106738 14154332998791555826 "
+      "2798471212441347211 3624302277914305292 12175243894993809691 18197954905920110263 869336096513266351 "
+      "17322661882612443490 11198026121992866897 3762399852280847282 3393029619669184773 12618712086407186791 "
+      "11601652706361609238 1815215914966060855"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  std::mt19937_64 e1(0);
+  LIBCPP_ASSERT(to_string(e1) == a);
+  (void)a;
 }
 
 template <class E, typename Arg>
@@ -262,13 +396,12 @@ void test_implicit() {
 #endif
 }
 
-int main(int, char**)
-{
-    test1();
-    test2();
+int main(int, char**) {
+  test1();
+  test2();
 
-    test_implicit<std::mt19937, std::uint_fast32_t>();
-    test_implicit<std::mt19937_64, std::uint_fast64_t>();
+  test_implicit<std::mt19937, std::uint_fast32_t>();
+  test_implicit<std::mt19937_64, std::uint_fast64_t>();
 
-    return 0;
+  return 0;
 }
diff --git a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
index ff028929747a6..24832330720e4 100644
--- a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
@@ -24,292 +24,425 @@
 
 #include "test_macros.h"
 
-void
-test1()
-{
-    const char* a = "358595400 4166558815 2016177162 3414480257 "
-    "4027494649 3722317195 1190532340 3212207148 "
-    "3537847251 389019999 1098708832 3277907415 "
-    "1946784350 3608286140 2091419822 2227407035 "
-    "2229110723 1825348377 1276269279 314337202 "
-    "3182935337 1313150029 3118776508 3707918501 "
-    "1900972958 4054820954 3973178248 906260237 "
-    "1403942218 3139931556 2807126524 3940936448 "
-    "1316345796 631296613 2268418920 2914000794 "
-    "3760588399 3226216036 880155129 4183611084 "
-    "211541083 3755352858 1331383234 3036493096 "
-    "937478630 2092170412 777784402 93392729 "
-    "3644029210 1681392086 2427001226 3143870332 "
-    "3703581502 2017505388 1706274541 1049329728 "
-    "2452031492 3437261233 2581204087 1700889875 "
-    "1652573881 2127047692 3778506964 1960741508 "
-    "2739602360 3395905609 2123355622 3041272975 "
-    "784200748 3558951522 1002787860 4063320888 "
-    "1587315560 4042698976 659183308 3082256417 "
-    "2808969567 2361418535 3468698782 750700970 "
-    "2991209851 3581521382 962022878 2518967363 "
-    "1476525873 3865977235 2128790058 2380326689 "
-    "1396773405 312559410 1370621899 1154499924 "
-    "2963101919 2182689761 2071851902 1661288848 "
-    "2411351341 1362764020 1289894483 1951662807 "
-    "701821506 552267185 2356648449 3949188503 "
-    "1748307081 87795201 3718396254 4112205936 "
-    "2819888864 73923733 2800033151 839258139 "
-    "3801779069 3105962436 2111266436 1772784466 "
-    "3692264298 4148810953 3147390749 3537518553 "
-    "1695044978 1430225842 1252346204 3465285434 "
-    "3970017763 2920658411 2805151132 290569815 "
-    "3802301355 1493420394 1943029276 1667143611 "
-    "1049665988 1710824905 220168517 3997946231 "
-    "1014582791 4244598752 1147604069 2533886627 "
-    "598679964 761521020 431779255 3745982038 "
-    "768658283 3598262505 1765664789 279538641 "
-    "715144305 2371628432 2655860083 1759010423 "
-    "3568452003 1910107098 2801429529 3924547532 "
-    "3862672436 3933725740 1764550618 130893617 "
-    "1460692387 4135312761 2075529299 2880227367 "
-    "944557368 4166665482 2627749235 3732013815 "
-    "1595900818 1553312393 3529311831 3531462424 "
-    "2431328342 4075369692 1609967709 3704537555 "
-    "2067297464 397140475 920618678 2840795964 "
-    "4202512837 1286017648 7035910 1057207826 "
-    "2325188262 191593698 3697383848 3029712831 "
-    "2073681914 163454681 1329637200 290077398 "
-    "287239431 4205081522 1233889024 167173087 "
-    "3267660257 3406068803 2382354609 1680046927 "
-    "125183503 3559536309 3208900974 2912148541 "
-    "2882879316 1937001086 2919729069 892928802 "
-    "4141691387 2507406586 855548593 3418647837 "
-    "4035646154 2410275591 248715645 3180757482 "
-    "1880770722 362912336 2964920095 2319904154 "
-    "1493655850 4240733030 1834485846 1696040454 "
-    "3329457927 1865824694 847759208 1587231623 "
-    "3757294772 1161601118 3630323833 3007722125 "
-    "3726418007 2124238171 1205345 172659797 "
-    "3040354211 885213338 1857049013 447922412 "
-    "719906299 1370059380 1922204800 3960090489 "
-    "1658822644 1529626863 1565927273 3537718771 "
-    "2733237258 2180221377 921910745 2144937687 "
-    "1727603895 1315635304 4023867791 2401834107 "
-    "808854185 2408824497 343935326 185237544 "
-    "746732759 2641236122 4283215329 743609415 "
-    "1134726665 3892851319 1302851263 3473445597 "
-    "1326817414 2702766508 1943179285 4025685468 "
-    "932896770 199392138 2787362875 3450501893 "
-    "3351567147 2461286528 2227605848 2993751114 "
-    "3988215720 1320573368 2866560199 4153194990 "
-    "3007120042 3260751955 3171763740 2111121243 "
-    "3962825228 102681859 3368179132 802089147 "
-    "4029273561 424939445 4178414761 2592125109 "
-    "1960801088 2967746406 310701990 2364200202 "
-    "1320507009 3474372480 784693947 2952246664 "
-    "1891935330 2048385105 3530082191 3238151038 "
-    "3293189141 1316053288 2087004409 740799958 "
-    "1187748554 3607767334 1190185990 1408429481 "
-    "657134359 221834425 3907725865 1068016389 "
-    "1402423875 2598612116 2046886300 2345022518 "
-    "1196081924 357783981 4013683598 463491626 "
-    "3269206482 3332444286 886491955 2257342866 "
-    "475911113 833576299 2893727564 2866985145 "
-    "1413365115 2995166393 1486060436 161205225 "
-    "3181728373 3056027137 2040371876 2182305146 "
-    "3028448628 2214316977 1266227021 876938740 "
-    "276477469 752158077 2182179045 1381698878 "
-    "3424557652 666674427 968327842 2534296575 "
-    "265105940 961112540 2641188117 2319139814 "
-    "1750453329 3450138343 678025317 1477566458 "
-    "3773796420 2933993832 3326042905 4084805260 "
-    "444182455 255333481 785163068 2321290820 "
-    "2893603234 3005520266 541104079 1383277090 "
-    "2770755666 3764627833 583371929 2864949033 "
-    "1487681116 1811788361 240329486 3094213377 "
-    "958509875 2564379085 1636995945 2070894127 "
-    "2139004232 1747850055 3841512327 3325011872 "
-    "1161622604 639182193 3533652535 4022667522 "
-    "761048999 3337743670 254221568 2784956233 "
-    "2990252814 4207922787 275707208 261819597 "
-    "2071467265 4034945770 1999813410 3038921100 "
-    "2200194573 1328217451 2440612380 3862293692 "
-    "2733976812 2750523058 2920082515 3809044908 "
-    "4285231753 3131963297 3481602724 1396460739 "
-    "2011767965 2481047764 2958452120 3044188618 "
-    "2217236658 3448605566 757716104 1818279145 "
-    "2641228144 1312649639 1194087684 3845229729 "
-    "1747658356 874418803 1956637337 268670179 "
-    "2083040240 2577671381 3375334655 2587828868 "
-    "1383012799 3583445685 2594576715 3282337104 "
-    "4257972751 3440488071 3129180313 1830891395 "
-    "1594931092 2680778339 3984026324 1102770400 "
-    "2315820258 1263467048 1133254110 2400676748 "
-    "2251795328 1036154092 3313541051 2277356560 "
-    "1477696003 1417117088 3968537402 1404882202 "
-    "2011058180 4114080985 1727459502 4100235708 "
-    "2334509310 2829432554 377936301 1519324520 "
-    "3252826644 1193335837 1929125820 2165344238 "
-    "4160556243 223340988 670907625 1485396519 "
-    "936389509 3813712964 2706450987 3132506320 "
-    "875886515 557088991 2854916639 2955496008 "
-    "2881696287 265169077 3239923698 3649366121 "
-    "4072165960 1233904959 225406526 1767368993 "
-    "1894882500 2296582180 339255168 83200939 "
-    "2958376148 4100205346 1991250823 3806183082 "
-    "2691709980 2642354997 3024056146 1681065839 "
-    "3438299684 1638853652 362567001 2307868899 "
-    "988801086 1342833399 2303298376 1500039911 "
-    "765489391 4080464497 4155444368 980472018 "
-    "2026981853 3460406995 391970367 667377014 "
-    "4177754853 2657468948 3560690175 3030464357 "
-    "2948380657 1208800977 2316451404 4001932203 "
-    "1977856863 4265848271 3116200050 3037586215 "
-    "1335232764 930230766 1026089249 2482219415 "
-    "2613853154 1854543497 2909555107 3862874043 "
-    "2609355500 907364682 383900687 358164223 "
-    "232347546 2536276737 3118482806 1254103998 "
-    "2357334077 1204777304 1996643329 4046232717 "
-    "2570520290 3173323380 1201411457 2361883023 "
-    "806087062 2984143714 2355127569 864220085 "
-    "1787696713 1182652984 4200065581 100722519 "
-    "2380458669 2429592313 2618338302 1236529564 "
-    "1747130196 3711661325 1114068102 510380433 "
-    "93703089 2277774664 3220741441 1577998569 "
-    "2816701900 4206763045 2495239107 4080390459 "
-    "1307072677 20360728 1468385549 96049834 "
-    "3630657447 2809517346 3396111678 3043831060 "
-    "673178359 4256729562 1755211210 1969834535 "
-    "498315110 3717726302 1544859987 2239930949 "
-    "1595372585 294525219 3961637067 3591840665 "
-    "3324896933 2300077772 721255886 4197934760 "
-    "1468866696 2184812884 628246683 3385113037 "
-    "3041166140 3948531843 1176600829 228286131 "
-    "2447397608 712235937 3332826819 2676980703 "
-    "4019468871 1952389952 1202638254 3625447051";
-    unsigned as[] = {3, 5, 7};
-    std::seed_seq sseq(as, as+3);
-    std::mt19937 e1(sseq);
-    std::ostringstream os;
-    os << e1;
-    assert(os.str() == a);
+void test1() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "358595400 4166558815 2016177162 3414480257 "
+      "4027494649 3722317195 1190532340 3212207148 "
+      "3537847251 389019999 1098708832 3277907415 "
+      "1946784350 3608286140 2091419822 2227407035 "
+      "2229110723 1825348377 1276269279 314337202 "
+      "3182935337 1313150029 3118776508 3707918501 "
+      "1900972958 4054820954 3973178248 906260237 "
+      "1403942218 3139931556 2807126524 3940936448 "
+      "1316345796 631296613 2268418920 2914000794 "
+      "3760588399 3226216036 880155129 4183611084 "
+      "211541083 3755352858 1331383234 3036493096 "
+      "937478630 2092170412 777784402 93392729 "
+      "3644029210 1681392086 2427001226 3143870332 "
+      "3703581502 2017505388 1706274541 1049329728 "
+      "2452031492 3437261233 2581204087 1700889875 "
+      "1652573881 2127047692 3778506964 1960741508 "
+      "2739602360 3395905609 2123355622 3041272975 "
+      "784200748 3558951522 1002787860 4063320888 "
+      "1587315560 4042698976 659183308 3082256417 "
+      "2808969567 2361418535 3468698782 750700970 "
+      "2991209851 3581521382 962022878 2518967363 "
+      "1476525873 3865977235 2128790058 2380326689 "
+      "1396773405 312559410 1370621899 1154499924 "
+      "2963101919 2182689761 2071851902 1661288848 "
+      "2411351341 1362764020 1289894483 1951662807 "
+      "701821506 552267185 2356648449 3949188503 "
+      "1748307081 87795201 3718396254 4112205936 "
+      "2819888864 73923733 2800033151 839258139 "
+      "3801779069 3105962436 2111266436 1772784466 "
+      "3692264298 4148810953 3147390749 3537518553 "
+      "1695044978 1430225842 1252346204 3465285434 "
+      "3970017763 2920658411 2805151132 290569815 "
+      "3802301355 1493420394 1943029276 1667143611 "
+      "1049665988 1710824905 220168517 3997946231 "
+      "1014582791 4244598752 1147604069 2533886627 "
+      "598679964 761521020 431779255 3745982038 "
+      "768658283 3598262505 1765664789 279538641 "
+      "715144305 2371628432 2655860083 1759010423 "
+      "3568452003 1910107098 2801429529 3924547532 "
+      "3862672436 3933725740 1764550618 130893617 "
+      "1460692387 4135312761 2075529299 2880227367 "
+      "944557368 4166665482 2627749235 3732013815 "
+      "1595900818 1553312393 3529311831 3531462424 "
+      "2431328342 4075369692 1609967709 3704537555 "
+      "2067297464 397140475 920618678 2840795964 "
+      "4202512837 1286017648 7035910 1057207826 "
+      "2325188262 191593698 3697383848 3029712831 "
+      "2073681914 163454681 1329637200 290077398 "
+      "287239431 4205081522 1233889024 167173087 "
+      "3267660257 3406068803 2382354609 1680046927 "
+      "125183503 3559536309 3208900974 2912148541 "
+      "2882879316 1937001086 2919729069 892928802 "
+      "4141691387 2507406586 855548593 3418647837 "
+      "4035646154 2410275591 248715645 3180757482 "
+      "1880770722 362912336 2964920095 2319904154 "
+      "1493655850 4240733030 1834485846 1696040454 "
+      "3329457927 1865824694 847759208 1587231623 "
+      "3757294772 1161601118 3630323833 3007722125 "
+      "3726418007 2124238171 1205345 172659797 "
+      "3040354211 885213338 1857049013 447922412 "
+      "719906299 1370059380 1922204800 3960090489 "
+      "1658822644 1529626863 1565927273 3537718771 "
+      "2733237258 2180221377 921910745 2144937687 "
+      "1727603895 1315635304 4023867791 2401834107 "
+      "808854185 2408824497 343935326 185237544 "
+      "746732759 2641236122 4283215329 743609415 "
+      "1134726665 3892851319 1302851263 3473445597 "
+      "1326817414 2702766508 1943179285 4025685468 "
+      "932896770 199392138 2787362875 3450501893 "
+      "3351567147 2461286528 2227605848 2993751114 "
+      "3988215720 1320573368 2866560199 4153194990 "
+      "3007120042 3260751955 3171763740 2111121243 "
+      "3962825228 102681859 3368179132 802089147 "
+      "4029273561 424939445 4178414761 2592125109 "
+      "1960801088 2967746406 310701990 2364200202 "
+      "1320507009 3474372480 784693947 2952246664 "
+      "1891935330 2048385105 3530082191 3238151038 "
+      "3293189141 1316053288 2087004409 740799958 "
+      "1187748554 3607767334 1190185990 1408429481 "
+      "657134359 221834425 3907725865 1068016389 "
+      "1402423875 2598612116 2046886300 2345022518 "
+      "1196081924 357783981 4013683598 463491626 "
+      "3269206482 3332444286 886491955 2257342866 "
+      "475911113 833576299 2893727564 2866985145 "
+      "1413365115 2995166393 1486060436 161205225 "
+      "3181728373 3056027137 2040371876 2182305146 "
+      "3028448628 2214316977 1266227021 876938740 "
+      "276477469 752158077 2182179045 1381698878 "
+      "3424557652 666674427 968327842 2534296575 "
+      "265105940 961112540 2641188117 2319139814 "
+      "1750453329 3450138343 678025317 1477566458 "
+      "3773796420 2933993832 3326042905 4084805260 "
+      "444182455 255333481 785163068 2321290820 "
+      "2893603234 3005520266 541104079 1383277090 "
+      "2770755666 3764627833 583371929 2864949033 "
+      "1487681116 1811788361 240329486 3094213377 "
+      "958509875 2564379085 1636995945 2070894127 "
+      "2139004232 1747850055 3841512327 3325011872 "
+      "1161622604 639182193 3533652535 4022667522 "
+      "761048999 3337743670 254221568 2784956233 "
+      "2990252814 4207922787 275707208 261819597 "
+      "2071467265 4034945770 1999813410 3038921100 "
+      "2200194573 1328217451 2440612380 3862293692 "
+      "2733976812 2750523058 2920082515 3809044908 "
+      "4285231753 3131963297 3481602724 1396460739 "
+      "2011767965 2481047764 2958452120 3044188618 "
+      "2217236658 3448605566 757716104 1818279145 "
+      "2641228144 1312649639 1194087684 3845229729 "
+      "1747658356 874418803 1956637337 268670179 "
+      "2083040240 2577671381 3375334655 2587828868 "
+      "1383012799 3583445685 2594576715 3282337104 "
+      "4257972751 3440488071 3129180313 1830891395 "
+      "1594931092 2680778339 3984026324 1102770400 "
+      "2315820258 1263467048 1133254110 2400676748 "
+      "2251795328 1036154092 3313541051 2277356560 "
+      "1477696003 1417117088 3968537402 1404882202 "
+      "2011058180 4114080985 1727459502 4100235708 "
+      "2334509310 2829432554 377936301 1519324520 "
+      "3252826644 1193335837 1929125820 2165344238 "
+      "4160556243 223340988 670907625 1485396519 "
+      "936389509 3813712964 2706450987 3132506320 "
+      "875886515 557088991 2854916639 2955496008 "
+      "2881696287 265169077 3239923698 3649366121 "
+      "4072165960 1233904959 225406526 1767368993 "
+      "1894882500 2296582180 339255168 83200939 "
+      "2958376148 4100205346 1991250823 3806183082 "
+      "2691709980 2642354997 3024056146 1681065839 "
+      "3438299684 1638853652 362567001 2307868899 "
+      "988801086 1342833399 2303298376 1500039911 "
+      "765489391 4080464497 4155444368 980472018 "
+      "2026981853 3460406995 391970367 667377014 "
+      "4177754853 2657468948 3560690175 3030464357 "
+      "2948380657 1208800977 2316451404 4001932203 "
+      "1977856863 4265848271 3116200050 3037586215 "
+      "1335232764 930230766 1026089249 2482219415 "
+      "2613853154 1854543497 2909555107 3862874043 "
+      "2609355500 907364682 383900687 358164223 "
+      "232347546 2536276737 3118482806 1254103998 "
+      "2357334077 1204777304 1996643329 4046232717 "
+      "2570520290 3173323380 1201411457 2361883023 "
+      "806087062 2984143714 2355127569 864220085 "
+      "1787696713 1182652984 4200065581 100722519 "
+      "2380458669 2429592313 2618338302 1236529564 "
+      "1747130196 3711661325 1114068102 510380433 "
+      "93703089 2277774664 3220741441 1577998569 "
+      "2816701900 4206763045 2495239107 4080390459 "
+      "1307072677 20360728 1468385549 96049834 "
+      "3630657447 2809517346 3396111678 3043831060 "
+      "673178359 4256729562 1755211210 1969834535 "
+      "498315110 3717726302 1544859987 2239930949 "
+      "1595372585 294525219 3961637067 3591840665 "
+      "3324896933 2300077772 721255886 4197934760 "
+      "1468866696 2184812884 628246683 3385113037 "
+      "3041166140 3948531843 1176600829 228286131 "
+      "2447397608 712235937 3332826819 2676980703 "
+      "4019468871 1952389952 1202638254 3625447051"
+#else
+      "358595400 4166558815 2016177162 3414480257 4027494649 3722317195 1190532340 3212207148 3537847251 389019999 "
+      "1098708832 3277907415 1946784350 3608286140 2091419822 2227407035 2229110723 1825348377 1276269279 314337202 "
+      "3182935337 1313150029 3118776508 3707918501 1900972958 4054820954 3973178248 906260237 1403942218 3139931556 "
+      "2807126524 3940936448 1316345796 631296613 2268418920 2914000794 3760588399 3226216036 880155129 4183611084 "
+      "211541083 3755352858 1331383234 3036493096 937478630 2092170412 777784402 93392729 3644029210 1681392086 "
+      "2427001226 3143870332 3703581502 2017505388 1706274541 1049329728 2452031492 3437261233 2581204087 1700889875 "
+      "1652573881 2127047692 3778506964 1960741508 2739602360 3395905609 2123355622 3041272975 784200748 3558951522 "
+      "1002787860 4063320888 1587315560 4042698976 659183308 3082256417 2808969567 2361418535 3468698782 750700970 "
+      "2991209851 3581521382 962022878 2518967363 1476525873 3865977235 2128790058 2380326689 1396773405 312559410 "
+      "1370621899 1154499924 2963101919 2182689761 2071851902 1661288848 2411351341 1362764020 1289894483 1951662807 "
+      "701821506 552267185 2356648449 3949188503 1748307081 87795201 3718396254 4112205936 2819888864 73923733 "
+      "2800033151 839258139 3801779069 3105962436 2111266436 1772784466 3692264298 4148810953 3147390749 3537518553 "
+      "1695044978 1430225842 1252346204 3465285434 3970017763 2920658411 2805151132 290569815 3802301355 1493420394 "
+      "1943029276 1667143611 1049665988 1710824905 220168517 3997946231 1014582791 4244598752 1147604069 2533886627 "
+      "598679964 761521020 431779255 3745982038 768658283 3598262505 1765664789 279538641 715144305 2371628432 "
+      "2655860083 1759010423 3568452003 1910107098 2801429529 3924547532 3862672436 3933725740 1764550618 130893617 "
+      "1460692387 4135312761 2075529299 2880227367 944557368 4166665482 2627749235 3732013815 1595900818 1553312393 "
+      "3529311831 3531462424 2431328342 4075369692 1609967709 3704537555 2067297464 397140475 920618678 2840795964 "
+      "4202512837 1286017648 7035910 1057207826 2325188262 191593698 3697383848 3029712831 2073681914 163454681 "
+      "1329637200 290077398 287239431 4205081522 1233889024 167173087 3267660257 3406068803 2382354609 1680046927 "
+      "125183503 3559536309 3208900974 2912148541 2882879316 1937001086 2919729069 892928802 4141691387 2507406586 "
+      "855548593 3418647837 4035646154 2410275591 248715645 3180757482 1880770722 362912336 2964920095 2319904154 "
+      "1493655850 4240733030 1834485846 1696040454 3329457927 1865824694 847759208 1587231623 3757294772 1161601118 "
+      "3630323833 3007722125 3726418007 2124238171 1205345 172659797 3040354211 885213338 1857049013 447922412 "
+      "719906299 1370059380 1922204800 3960090489 1658822644 1529626863 1565927273 3537718771 2733237258 2180221377 "
+      "921910745 2144937687 1727603895 1315635304 4023867791 2401834107 808854185 2408824497 343935326 185237544 "
+      "746732759 2641236122 4283215329 743609415 1134726665 3892851319 1302851263 3473445597 1326817414 2702766508 "
+      "1943179285 4025685468 932896770 199392138 2787362875 3450501893 3351567147 2461286528 2227605848 2993751114 "
+      "3988215720 1320573368 2866560199 4153194990 3007120042 3260751955 3171763740 2111121243 3962825228 102681859 "
+      "3368179132 802089147 4029273561 424939445 4178414761 2592125109 1960801088 2967746406 310701990 2364200202 "
+      "1320507009 3474372480 784693947 2952246664 1891935330 2048385105 3530082191 3238151038 3293189141 1316053288 "
+      "2087004409 740799958 1187748554 3607767334 1190185990 1408429481 657134359 221834425 3907725865 1068016389 "
+      "1402423875 2598612116 2046886300 2345022518 1196081924 357783981 4013683598 463491626 3269206482 3332444286 "
+      "886491955 2257342866 475911113 833576299 2893727564 2866985145 1413365115 2995166393 1486060436 161205225 "
+      "3181728373 3056027137 2040371876 2182305146 3028448628 2214316977 1266227021 876938740 276477469 752158077 "
+      "2182179045 1381698878 3424557652 666674427 968327842 2534296575 265105940 961112540 2641188117 2319139814 "
+      "1750453329 3450138343 678025317 1477566458 3773796420 2933993832 3326042905 4084805260 444182455 255333481 "
+      "785163068 2321290820 2893603234 3005520266 541104079 1383277090 2770755666 3764627833 583371929 2864949033 "
+      "1487681116 1811788361 240329486 3094213377 958509875 2564379085 1636995945 2070894127 2139004232 1747850055 "
+      "3841512327 3325011872 1161622604 639182193 3533652535 4022667522 761048999 3337743670 254221568 2784956233 "
+      "2990252814 4207922787 275707208 261819597 2071467265 4034945770 1999813410 3038921100 2200194573 1328217451 "
+      "2440612380 3862293692 2733976812 2750523058 2920082515 3809044908 4285231753 3131963297 3481602724 1396460739 "
+      "2011767965 2481047764 2958452120 3044188618 2217236658 3448605566 757716104 1818279145 2641228144 1312649639 "
+      "1194087684 3845229729 1747658356 874418803 1956637337 268670179 2083040240 2577671381 3375334655 2587828868 "
+      "1383012799 3583445685 2594576715 3282337104 4257972751 3440488071 3129180313 1830891395 1594931092 2680778339 "
+      "3984026324 1102770400 2315820258 1263467048 1133254110 2400676748 2251795328 1036154092 3313541051 2277356560 "
+      "1477696003 1417117088 3968537402 1404882202 2011058180 4114080985 1727459502 4100235708 2334509310 2829432554 "
+      "377936301 1519324520 3252826644 1193335837 1929125820 2165344238 4160556243 223340988 670907625 1485396519 "
+      "936389509 3813712964 2706450987 3132506320 875886515 557088991 2854916639 2955496008 2881696287 265169077 "
+      "3239923698 3649366121 4072165960 1233904959 225406526 1767368993 1894882500 2296582180 339255168 83200939 "
+      "2958376148 4100205346 1991250823 3806183082 2691709980 2642354997 3024056146 1681065839 3438299684 1638853652 "
+      "362567001 2307868899 988801086 1342833399 2303298376 1500039911 765489391 4080464497 4155444368 980472018 "
+      "2026981853 3460406995 391970367 667377014 4177754853 2657468948 3560690175 3030464357 2948380657 1208800977 "
+      "2316451404 4001932203 1977856863 4265848271 3116200050 3037586215 1335232764 930230766 1026089249 2482219415 "
+      "2613853154 1854543497 2909555107 3862874043 2609355500 907364682 383900687 358164223 232347546 2536276737 "
+      "3118482806 1254103998 2357334077 1204777304 1996643329 4046232717 2570520290 3173323380 1201411457 2361883023 "
+      "806087062 2984143714 2355127569 864220085 1787696713 1182652984 4200065581 100722519 2380458669 2429592313 "
+      "2618338302 1236529564 1747130196 3711661325 1114068102 510380433 93703089 2277774664 3220741441 1577998569 "
+      "2816701900 4206763045 2495239107 4080390459 1307072677 20360728 1468385549 96049834 3630657447 2809517346 "
+      "3396111678 3043831060 673178359 4256729562 1755211210 1969834535 498315110 3717726302 1544859987 2239930949 "
+      "1595372585 294525219 3961637067 3591840665 3324896933 2300077772 721255886 4197934760 1468866696 2184812884 "
+      "628246683 3385113037 3041166140 3948531843 1176600829 228286131 2447397608 712235937 3332826819 2676980703 "
+      "4019468871 1952389952 1202638254 3625447051"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  unsigned as[] = {3, 5, 7};
+  std::seed_seq sseq(as, as + 3);
+  std::mt19937 e1(sseq);
+  std::ostringstream os;
+  os << e1;
+  LIBCPP_ASSERT(os.str() == a);
+  (void)a;
 }
 
-void
-test2()
-{
-    const char* a = "17895233847644109640 14665081038668852234 15987230621890949369 "
-    "13796324649827964148 1670828176732799955 14078505147839608672 "
-    "15497470967856861790 9566640372296747182 7839811585250789315 "
-    "1350068003782415071 5639936432479386921 15925388701147119804 "
-    "17415323390466493342 3892358083553387400 13485903346102334794 "
-    "16926193162581531132 2711398308226914244 12515538113016451944 "
-    "13856492368211347055 17968472785843263993 16129117710261673051 "
-    "13041638543181171650 8985803498136324582 401118717516975186 "
-    "7221524024767248666 13502820261231663498 8665119664667372350 "
-    "4506836866186850029 14762924585995667460 7305266389803732087 "
-    "9135600275824854713 8421320656548229332 14585303533697565624 "
-    "13062167967956981222 15285580395823625260 17451830328116466708 "
-    "17363259891080004456 13238190509560321740 10142215382802200927 "
-    "3224236118694175902 15382517208605932923 10818882444738383326 "
-    "16604245792882032433 10223425285179753002 1342432445403828765 "
-    "4958539418185107403 9374581143772158175 7135181273441366910 "
-    "5853026900476841261 8382327930174454355 2371969498930803266 "
-    "16961635468480846337 377077518789053577 17661790013255465310 "
-    "317500018453124832 3604586262706855295 13340007089026272125 "
-    "7614051306438090372 17819007364113857386 15193526497275633437 "
-    "6142773218979108210 14883287611587512668 12544132362002344419 "
-    "1247987855434921372 6414191755211735979 7160327288923375132 "
-    "7347937017206972868 17171048313531629893 18230412825496997383 "
-    "10882960195884354661 3270707876715241884 16088870345045208503 "
-    "15454419782166694763 1200609322828949525 10186066554418904177 "
-    "7554892242763986291 8203847521335919011 16855803304338943001 "
-    "16895223408596071476 562183806034700250 17761033068687156643 "
-    "12370482348384718931 17895691979506634040 16028877286272943475 "
-    "6671425930002400146 15167515621662197335 17503579548680921174 "
-    "15910867647138768989 1705705354110203064 12201125760909412022 "
-    "5523403744441352645 4540673037752294406 822888669354888870 "
-    "13012517529113958824 702032511346794490 1245872939048413008 "
-    "18060687614291143943 718002942670251776 14628954120078526945 "
-    "7215746609592654001 15288092036204733967 12507582747898016110 "
-    "8319356319569362772 3835100005166188461 10769229288786702843 "
-    "14682980657311687345 10352054841727718090 13661249361946024317 "
-    "1558696616315734178 9963912474249467679 18213809676410642730 "
-    "7284438284457478230 8013656044128665351 6817107912809760616 "
-    "4989038816564331700 12918068165960947833 9123533477086273623 "
-    "741568181450204257 3801962339733348259 1923812112542486965 "
-    "5884360231397942779 17008459141377852544 6569697353326895092 "
-    "15194386425456240489 9363979514988323850 9212437218544795097 "
-    "5650610605870621879 10315798944006232463 10345822437227504297 "
-    "795589193815296350 11344022765750598871 3193778122705907169 "
-    "16719669104430190089 14918335244853046975 11608293761910939782 "
-    "17290187430985633813 856382712722415618 14819792788008454203 "
-    "10571145147196955435 12858063129221173592 5671819431516788648 "
-    "17837836658827607239 14004823010100183722 9067196699747632668 "
-    "441015230260308492 3444946658209715644 1825101023084664281 "
-    "11133092574473850025 12746373758552339264 10154162549097295782 "
-    "14922316177042921089 12679802872389794491 8797747037480461410 "
-    "13907752811248535439 5652405835046458389 3181711594575177977 "
-    "15495242712294857418 6049158560807439366 952771601159099159 "
-    "4587095466254740009 11160954054611782211 10071795025240457628 "
-    "1536670498623767300 1990681379653546894 14312739227381277138 "
-    "9695213786215402291 3580182943401628617 12313607438786545484 "
-    "12864141705426648443 692371170805382036 13125536612285239925 "
-    "9372929234002877092 9510419002221032820 3766423210161674061 "
-    "3230494342413727261 5934351496112072933 2863344864469097044 "
-    "10884720908958139042 4127946927340597780 9960629658622711061 "
-    "14818231351611083857 6346099615454582885 12601407558879514692 "
-    "17544105005554819865 1096648950913019831 9969868157190185788 "
-    "12908611252828823970 5941129863397152719 16168953427117105234 "
-    "12304862402025196697 7781571759256122972 13289545261301048078 "
-    "11013924305579914035 8894422550580466537 7506958826675805512 "
-    "14280817252893250439 2745266616282182732 17277225453205013047 "
-    "14335499905842065319 11961295941780577536 18072890757248426766 "
-    "1124506606842606920 17329960125355005185 13052066741624159010 "
-    "5704650516221677069 16588425097127709212 11813406583737887980 "
-    "16359723311775411283 13451679937172566665 5997753207634594468 "
-    "10656019008205694109 13074690560123889048 14811648124990806194 "
-    "7809449463531558024 5637787273252434288 16515135932856030468 "
-    "3755600163640125044 1153929634172103321 11071014283313196016 "
-    "11114640359080035583 15390782025450330559 14097530518721927499 "
-    "14776783751481098767 7863618667181998233 11513855295425132436 "
-    "4736362806980864724 5426549653049482466 10310828122060887518 "
-    "4450247941008370560 9781171949844602811 6086471549040450051 "
-    "6033923116291003194 17669843285681524740 17610378273478865070 "
-    "12152320288002263294 6525449125788834221 5125338396312613396 "
-    "9300082688721166268 959242243476884691 6379729471368150249 "
-    "16379772457647614853 13454012201619761707 2392678998182524851 "
-    "12693758700673471007 1138892516507202079 15673908144065302514 "
-    "5299581449349386824 7590792025124859454 9863745357571267780 "
-    "357345312340746112 17610247870912740564 16347431861769737095 "
-    "11348828299228888092 7220122803951857490 7038822841708464676 "
-    "9912221445023094105 5767425533670320190 6442622362743049032 "
-    "17525461567869579503 4211095256108567696 14862334876401617373 "
-    "2866362449624104511 11413742225973279461 13015745308569358847 "
-    "5191760666536228849 17188167935010684492 18321678815621002079 "
-    "13046333455321624690 3995310719038261500 10661051209947341089 "
-    "7965203671238327266 16590917686161852835 3897101637344795372 "
-    "1538303624766151695 10893225639252940698 5386335660311332214 "
-    "5174479122000384061 17378437193516866561 13629320139302700770 "
-    "10144210341964027265 12816799659000064406 3711797003976467729 "
-    "5079455890584507977 432599929275804205 10435019529328454317 "
-    "5310854040535477246 15941464006450157396 2192067269367387270 "
-    "9782967689631091633 6777452250210540865 18067909703113078220 "
-    "17525143578810667971 87448662189824165 412530897284614413 "
-    "12066785122245373863 13073154860645125438 18282514257379582711 "
-    "8460374908111578570 15967512883067334502 9620430172798103891 "
-    "1264976185047610409 15426838192579528907 9878758812321441445 "
-    "18029992505662864846 9383699886128308360 14538949787806484635 "
-    "16958815135940772668 980481467951972605 3059030058898313960 "
-    "11497544574740915907 8385450996898478663 15571176518627282350";
-    unsigned as[] = {3, 5, 7};
-    std::seed_seq sseq(as, as+3);
-    std::mt19937_64 e1(sseq);
-    std::ostringstream os;
-    os << e1;
-    assert(os.str() == a);
+void test2() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "17895233847644109640 14665081038668852234 15987230621890949369 "
+      "13796324649827964148 1670828176732799955 14078505147839608672 "
+      "15497470967856861790 9566640372296747182 7839811585250789315 "
+      "1350068003782415071 5639936432479386921 15925388701147119804 "
+      "17415323390466493342 3892358083553387400 13485903346102334794 "
+      "16926193162581531132 2711398308226914244 12515538113016451944 "
+      "13856492368211347055 17968472785843263993 16129117710261673051 "
+      "13041638543181171650 8985803498136324582 401118717516975186 "
+      "7221524024767248666 13502820261231663498 8665119664667372350 "
+      "4506836866186850029 14762924585995667460 7305266389803732087 "
+      "9135600275824854713 8421320656548229332 14585303533697565624 "
+      "13062167967956981222 15285580395823625260 17451830328116466708 "
+      "17363259891080004456 13238190509560321740 10142215382802200927 "
+      "3224236118694175902 15382517208605932923 10818882444738383326 "
+      "16604245792882032433 10223425285179753002 1342432445403828765 "
+      "4958539418185107403 9374581143772158175 7135181273441366910 "
+      "5853026900476841261 8382327930174454355 2371969498930803266 "
+      "16961635468480846337 377077518789053577 17661790013255465310 "
+      "317500018453124832 3604586262706855295 13340007089026272125 "
+      "7614051306438090372 17819007364113857386 15193526497275633437 "
+      "6142773218979108210 14883287611587512668 12544132362002344419 "
+      "1247987855434921372 6414191755211735979 7160327288923375132 "
+      "7347937017206972868 17171048313531629893 18230412825496997383 "
+      "10882960195884354661 3270707876715241884 16088870345045208503 "
+      "15454419782166694763 1200609322828949525 10186066554418904177 "
+      "7554892242763986291 8203847521335919011 16855803304338943001 "
+      "16895223408596071476 562183806034700250 17761033068687156643 "
+      "12370482348384718931 17895691979506634040 16028877286272943475 "
+      "6671425930002400146 15167515621662197335 17503579548680921174 "
+      "15910867647138768989 1705705354110203064 12201125760909412022 "
+      "5523403744441352645 4540673037752294406 822888669354888870 "
+      "13012517529113958824 702032511346794490 1245872939048413008 "
+      "18060687614291143943 718002942670251776 14628954120078526945 "
+      "7215746609592654001 15288092036204733967 12507582747898016110 "
+      "8319356319569362772 3835100005166188461 10769229288786702843 "
+      "14682980657311687345 10352054841727718090 13661249361946024317 "
+      "1558696616315734178 9963912474249467679 18213809676410642730 "
+      "7284438284457478230 8013656044128665351 6817107912809760616 "
+      "4989038816564331700 12918068165960947833 9123533477086273623 "
+      "741568181450204257 3801962339733348259 1923812112542486965 "
+      "5884360231397942779 17008459141377852544 6569697353326895092 "
+      "15194386425456240489 9363979514988323850 9212437218544795097 "
+      "5650610605870621879 10315798944006232463 10345822437227504297 "
+      "795589193815296350 11344022765750598871 3193778122705907169 "
+      "16719669104430190089 14918335244853046975 11608293761910939782 "
+      "17290187430985633813 856382712722415618 14819792788008454203 "
+      "10571145147196955435 12858063129221173592 5671819431516788648 "
+      "17837836658827607239 14004823010100183722 9067196699747632668 "
+      "441015230260308492 3444946658209715644 1825101023084664281 "
+      "11133092574473850025 12746373758552339264 10154162549097295782 "
+      "14922316177042921089 12679802872389794491 8797747037480461410 "
+      "13907752811248535439 5652405835046458389 3181711594575177977 "
+      "15495242712294857418 6049158560807439366 952771601159099159 "
+      "4587095466254740009 11160954054611782211 10071795025240457628 "
+      "1536670498623767300 1990681379653546894 14312739227381277138 "
+      "9695213786215402291 3580182943401628617 12313607438786545484 "
+      "12864141705426648443 692371170805382036 13125536612285239925 "
+      "9372929234002877092 9510419002221032820 3766423210161674061 "
+      "3230494342413727261 5934351496112072933 2863344864469097044 "
+      "10884720908958139042 4127946927340597780 9960629658622711061 "
+      "14818231351611083857 6346099615454582885 12601407558879514692 "
+      "17544105005554819865 1096648950913019831 9969868157190185788 "
+      "12908611252828823970 5941129863397152719 16168953427117105234 "
+      "12304862402025196697 7781571759256122972 13289545261301048078 "
+      "11013924305579914035 8894422550580466537 7506958826675805512 "
+      "14280817252893250439 2745266616282182732 17277225453205013047 "
+      "14335499905842065319 11961295941780577536 18072890757248426766 "
+      "1124506606842606920 17329960125355005185 13052066741624159010 "
+      "5704650516221677069 16588425097127709212 11813406583737887980 "
+      "16359723311775411283 13451679937172566665 5997753207634594468 "
+      "10656019008205694109 13074690560123889048 14811648124990806194 "
+      "7809449463531558024 5637787273252434288 16515135932856030468 "
+      "3755600163640125044 1153929634172103321 11071014283313196016 "
+      "11114640359080035583 15390782025450330559 14097530518721927499 "
+      "14776783751481098767 7863618667181998233 11513855295425132436 "
+      "4736362806980864724 5426549653049482466 10310828122060887518 "
+      "4450247941008370560 9781171949844602811 6086471549040450051 "
+      "6033923116291003194 17669843285681524740 17610378273478865070 "
+      "12152320288002263294 6525449125788834221 5125338396312613396 "
+      "9300082688721166268 959242243476884691 6379729471368150249 "
+      "16379772457647614853 13454012201619761707 2392678998182524851 "
+      "12693758700673471007 1138892516507202079 15673908144065302514 "
+      "5299581449349386824 7590792025124859454 9863745357571267780 "
+      "357345312340746112 17610247870912740564 16347431861769737095 "
+      "11348828299228888092 7220122803951857490 7038822841708464676 "
+      "9912221445023094105 5767425533670320190 6442622362743049032 "
+      "17525461567869579503 4211095256108567696 14862334876401617373 "
+      "2866362449624104511 11413742225973279461 13015745308569358847 "
+      "5191760666536228849 17188167935010684492 18321678815621002079 "
+      "13046333455321624690 3995310719038261500 10661051209947341089 "
+      "7965203671238327266 16590917686161852835 3897101637344795372 "
+      "1538303624766151695 10893225639252940698 5386335660311332214 "
+      "5174479122000384061 17378437193516866561 13629320139302700770 "
+      "10144210341964027265 12816799659000064406 3711797003976467729 "
+      "5079455890584507977 432599929275804205 10435019529328454317 "
+      "5310854040535477246 15941464006450157396 2192067269367387270 "
+      "9782967689631091633 6777452250210540865 18067909703113078220 "
+      "17525143578810667971 87448662189824165 412530897284614413 "
+      "12066785122245373863 13073154860645125438 18282514257379582711 "
+      "8460374908111578570 15967512883067334502 9620430172798103891 "
+      "1264976185047610409 15426838192579528907 9878758812321441445 "
+      "18029992505662864846 9383699886128308360 14538949787806484635 "
+      "16958815135940772668 980481467951972605 3059030058898313960 "
+      "11497544574740915907 8385450996898478663 15571176518627282350"
+#else
+      "12332679912394435791 9453873875014225299 7194476751338084717 15353332195928478249 10481090450137100787 "
+      "16894768970350315699 9140706850585347923 17060753788370102662 5027480258542244180 4237901618226072654 "
+      "1625191769495810967 14151318192590258819 14578513270917221567 1340379920765042993 16984341741367590539 "
+      "17825705796225691974 10460633243946900672 15537248717777931667 18010309519743623944 11238461300079763233 "
+      "5212834616069799861 14803562233233663569 509073377271690301 9868447031443646872 18426739611230355604 "
+      "405337453835701754 2865140403182526171 17022743117531244315 15873574515363721317 1003946026159741321 "
+      "10106235397909731784 7501984743217763859 9608180616803748513 17321043653964822159 138792968844647510 "
+      "13940152250553688762 16186510718789785685 10788084171130680879 3336642670408666887 7310094463387030739 "
+      "5523088258140426915 1285026150975382598 13098435840364629682 6200633715587124711 5088226041080887554 "
+      "11020661496106860750 12793876732801641662 3576577317235426397 15183184978662083277 15861657920389428029 "
+      "470273426362791941 2574629887817053182 13272148377140358694 3004438593777107412 2652718879104414667 "
+      "1834027854782609871 10456539872813643760 6392289299447966013 9267269600134252311 10114120825136775101 "
+      "2198130084042867418 13999435202207229825 14893120434100842814 2852347522408045763 17948795049429383857 "
+      "17441093961240168873 5399621587997711172 12611952947737738867 6103704372959848527 738297531584281114 "
+      "16788685814043292624 6176291056095750530 16428159208694695523 4245608719980075114 12095265841295350099 "
+      "15191360055442418178 8708130367787597793 9264267659253066420 15969277182114773779 17035922951690056341 "
+      "9880719443672114132 15332192661202958624 14150490775662311299 4012807722045209888 8653700056821051719 "
+      "15242621354372282880 17096356791700814452 16018787795979380803 296068005088784708 4048466329605633785 "
+      "8059310313495723083 8563086889008277869 10184519287956394000 6827268421235011709 17346498307575607164 "
+      "6880036350466817261 16153597041588957084 15405131912393208395 12767350280822570901 1052885868672540599 "
+      "4188094412071243913 1126222035238982882 9191486289101564624 10803640833835240836 3601208394977378668 "
+      "4766334496475281242 7832706214999682994 16891444816728487342 17860841416138565015 12370215439434878681 "
+      "9232823972512380788 3640121802277180952 65633375613217608 13600365462550545275 17948579528827407927 "
+      "765525796436836449 13572020505370210613 11924972776661436983 4057541052165744297 17481588995966250594 "
+      "8006213088863539837 9739468715064136699 2716054689781779647 16549004007254321028 4910543003771498131 "
+      "13361071256636157603 15297097731894649703 17624276599277544080 15499557723143597570 17957222608348359804 "
+      "2778177927288936525 13637160153035836011 5098746756051510535 4146145678876364034 2247309035465090095 "
+      "9569124281048572866 12809922376030463313 15451507763993230705 17163206508771813131 17029710194125776618 "
+      "8061947308014036349 1058089520886506655 13597933493965622376 13504098358025602133 1333487301584903927 "
+      "8426860107195956174 3508319831781619992 13220104443771667566 15729810053761292123 4229931263400540722 "
+      "4166080451531777928 6512350709932085964 11707745856776186054 5335698181984744736 16601891716487755730 "
+      "14845047860332527819 13881061187353168588 2290627681380867569 15008794942145533328 9207539692861337313 "
+      "15854225885502222653 12652120737459513649 8391328319785893524 16218535624802384239 10635204123630136612 "
+      "14741489403103018051 1034273171946262577 2598761621738522071 10597540332677591413 11769725249744073186 "
+      "12729182262319592409 13143766812621949948 7337533679729790607 8700823377064447860 6528412901068836499 "
+      "12887771189408551435 6596057164621086436 9725510839601037147 12589333640740308830 8713351353100408409 "
+      "3200078174512985423 3001899537338861016 14245433087720289982 2342459702085298473 15840929965350904315 "
+      "5242384259319523672 6976265022106569670 4694429153464034874 4640016838764614916 11889897715595570593 "
+      "3752569812323601873 2900819842622530250 1859087250745968648 12106047926770169995 12635768141780418349 "
+      "460915272050913013 16909625031804250705 15219281218948426364 15473817896328511538 356510218273447008 "
+      "4322019207571293094 3044055123444388775 14541219878139834671 16010790916272798386 17663395908622954435 "
+      "12611516608169911627 16319922669204576197 16662253955344225107 16530145161795660660 13055268744196885107 "
+      "7863493504777224455 4884882190835979670 17860099797924912564 7963955360230183301 12071138257120882069 "
+      "18357891564140620935 12793730149183942271 14575396984169788025 3998777626616457896 16107243833817385461 "
+      "2800961891557584893 2799945392029798727 11053512580047143649 11067103105271293369 1968075203485277477 "
+      "3090469630341592171 14797167942499219775 1314244307340344386 5197117610141487191 14715472132315901058 "
+      "10223376637313339598 18090093248597656064 162846477809223606 18060308085200244427 4381704277003259436 "
+      "13919578743352454047 12290099036217748490 2399520646070724256 8630374525153030174 12607432526554525963 "
+      "13596968856308825531 4310477479909840944 5249608228653410962 3699954346213058725 287328857418258109 "
+      "6119686667148845021 5412656704791399252 4791204075127203599 14494596012449800144 6684909344487566871 "
+      "4578857374794350422 3320300604108137187 12579270049709840437 16704843247071205209 3817825700702948560 "
+      "5357944831944862632 1304382350048285997 10815164501630203260 438468935207234712 4455248787965292675 "
+      "16418065713664649114 16484487561423541185 10845743900807077540 376195122763577279 15264985897394715313 "
+      "7633016332459855519 18374759716909813581 7494762658573963878 12561201922274984497 17674342236666928778 "
+      "8885969173395223311 8755229097686715671 1319918912921290459 12669348064964076026 8347713925484594194 "
+      "7131861330014060437 18013870281775146388 18428665316372779146 14853358900946804630 11802355386662327119 "
+      "12234197935566505458 1562495104086441070 4789216534718205848 4797493321171570223 11489013455481650173 "
+      "15991479755025557462 5193680184864207374 538012454739572314 12691714223537549902 1625023157851387876 "
+      "15523181006631146535 5212032641878267513 314342477903124542 7039243305239188299 13660860177517273492 "
+      "279600177346432632 1242305966848399256 3754792367662117676 7456833993243708712 5524526481950173096 "
+      "12629250495102362491 12320658947410481525 8375636660933855231 14586189041466889258 3338611085276113663 "
+      "6796460652093029292 17977915623844806175 6678041303167794104 162629150799512142 12738967623757853162 "
+      "15873580300066345221 1661173500922285893"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  unsigned as[] = {3, 5, 7};
+  std::seed_seq sseq(as, as + 3);
+  std::mt19937_64 e1(sseq);
+  std::ostringstream os;
+  os << e1;
+  LIBCPP_ASSERT(os.str() == a);
+  (void)a;
 }
 
-int main(int, char**)
-{
-    test1();
-    test2();
+int main(int, char**) {
+  test1();
+  test2();
 
   return 0;
 }
diff --git a/libcxx/utils/libcxx/test/features/libcxx_macros.py b/libcxx/utils/libcxx/test/features/libcxx_macros.py
index a3b32a9c88051..b8c0e13a622c9 100644
--- a/libcxx/utils/libcxx/test/features/libcxx_macros.py
+++ b/libcxx/utils/libcxx/test/features/libcxx_macros.py
@@ -35,6 +35,7 @@
     "_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING": "libcpp-abi-no-compressed-pair-padding",
     "_LIBCPP_PSTL_BACKEND_LIBDISPATCH": "libcpp-pstl-backend-libdispatch",
     "_LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE": "libcpp-abi-use-small-deque-block-size",
+    "_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine"
 }
 for macro, feature in macros.items():
     features.append(

>From 721e2ea620a480cdc928ccdd5351bea781a6ad43 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 3 Jul 2026 10:21:34 -0400
Subject: [PATCH 2/2] Update libcxx/utils/libcxx/test/features/libcxx_macros.py

---
 libcxx/utils/libcxx/test/features/libcxx_macros.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/utils/libcxx/test/features/libcxx_macros.py b/libcxx/utils/libcxx/test/features/libcxx_macros.py
index b8c0e13a622c9..bcc081e2dc2de 100644
--- a/libcxx/utils/libcxx/test/features/libcxx_macros.py
+++ b/libcxx/utils/libcxx/test/features/libcxx_macros.py
@@ -35,7 +35,7 @@
     "_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING": "libcpp-abi-no-compressed-pair-padding",
     "_LIBCPP_PSTL_BACKEND_LIBDISPATCH": "libcpp-pstl-backend-libdispatch",
     "_LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE": "libcpp-abi-use-small-deque-block-size",
-    "_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine"
+    "_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine",
 }
 for macro, feature in macros.items():
     features.append(


From libcxx-commits at lists.llvm.org  Fri Jul  3 07:51:50 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 07:51:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Always enable
	-fvisibility=hidden (PR #207333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207333@github.com>
Message-ID: <6a47cc86.e55dc341.29c9b8.6f98@mx.google.com>


https://github.com/ldionne approved this pull request.

Assuming the output of `nm` is unchanged, I'm fine with this.

Specifically, the reason this *should* be fine is that we're already supposed to annotate all of the functions we export from libc++abi with the right ABI annotations. That's also what we already do for libc++ (where we already build with hidden visibility).

So the only potential breakage here would be if there are symbols that we export from libc++abi but we don't apply the right explicit visibility annotations to, and the `nm` check should catch that.

https://github.com/llvm/llvm-project/pull/207333

From libcxx-commits at lists.llvm.org  Fri Jul  3 07:51:51 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 07:51:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Always enable
	-fvisibility=hidden (PR #207333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207333@github.com>
Message-ID: <6a47cc87.abb10101.208f3e.5581@mx.google.com>


================
@@ -195,6 +195,7 @@ if (LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
   # Build with -fsized-deallocation, which is default in recent versions of Clang.
   # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
   target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
+  target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
----------------
ldionne wrote:

Can you please review the output of `nm` on `libc++abi` to make sure you don't see anything unexpected? My trust in our ABI list mechanism has been shaken recently, see https://github.com/llvm/llvm-project/issues/207409

https://github.com/llvm/llvm-project/pull/207333

From libcxx-commits at lists.llvm.org  Fri Jul  3 07:51:51 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 07:51:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Always enable
	-fvisibility=hidden (PR #207333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207333@github.com>
Message-ID: <6a47cc87.eea6ab6e.1256b6.6b3c@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/207333

From libcxx-commits at lists.llvm.org  Fri Jul  3 08:23:21 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 08:23:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a47d3e9.7d41f3a1.d1cf7.aa96@mx.google.com>


https://github.com/ldionne commented:

All in all, I'd much rather implement this using relocation semantics, which are easier to understand and should generate equivalent if not better code: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3516r2.html#vectoremplace-vectorinsert

However, I think it's reasonable to land this and then improve upon it.

https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Fri Jul  3 08:23:21 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 08:23:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a47d3e9.13be6df8.3d8759.b20e@mx.google.com>


================
@@ -1120,14 +1122,26 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointer __to) {
   pointer __old_last  = __layout_.__end_ptr();
   difference_type __n = __old_last - __to;
+#ifndef _LIBCPP_CXX03_LANG
+  if constexpr (__allocator_has_trivial_move_construct_v<allocator_type, value_type> &&
+                is_trivially_copyable<value_type>::value) {
+    if (__libcpp_is_constant_evaluated()) // construct the new trailing objects
+      std::uninitialized_fill_n(__old_last, (__from_e - __from_s) - __n, *__from_s);
+    std::__constexpr_memmove(
+        std::__to_address(__to), std::__to_address(__from_s), __element_count(__from_e - __from_s));
+    __layout_.__set_bound_using_pointer(__to + (__from_e - __from_s));
+  } else
+#endif
   {
-    pointer __i = __from_s + __n;
-    _ConstructTransaction __tx(*this, __from_e - __i);
-    for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
-      __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
+    {
+      pointer __i = __from_s + __n;
+      _ConstructTransaction __tx(*this, __from_e - __i);
+      for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
+        __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
+      }
----------------
ldionne wrote:

This could be `__uninitialized_allocator_move`, I think.

https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Fri Jul  3 08:23:21 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 08:23:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a47d3e9.f732ad81.1d8ca.dd03@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Fri Jul  3 08:23:22 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 08:23:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a47d3ea.faf16b37.1e0c59.9e49@mx.google.com>


================
@@ -1120,14 +1122,26 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointer __to) {
   pointer __old_last  = __layout_.__end_ptr();
   difference_type __n = __old_last - __to;
+#ifndef _LIBCPP_CXX03_LANG
----------------
ldionne wrote:

Do we have functional tests that exercise this code path with trivially copyable types (and non-trivially copyable types)?

https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Fri Jul  3 08:23:22 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 08:23:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a47d3ea.492e23e4.e71ca.139e@mx.google.com>


================
@@ -1120,14 +1122,26 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointer __to) {
   pointer __old_last  = __layout_.__end_ptr();
   difference_type __n = __old_last - __to;
+#ifndef _LIBCPP_CXX03_LANG
+  if constexpr (__allocator_has_trivial_move_construct_v<allocator_type, value_type> &&
+                is_trivially_copyable<value_type>::value) {
+    if (__libcpp_is_constant_evaluated()) // construct the new trailing objects
+      std::uninitialized_fill_n(__old_last, (__from_e - __from_s) - __n, *__from_s);
----------------
ldionne wrote:

I'm not a huge fan of this, it seems pretty unclean. I'd rather take the slow code path when we're constant evaluating.

https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Fri Jul  3 08:43:36 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 08:43:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Avoid out parameter
 in dyn_cast_get_derived_info (PR #207326)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207326@github.com>
Message-ID: <6a47d8a8.14d9b9dc.140829.a079@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/207326

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e319.2020f6c2.1fdf61.1360@mx.google.com>


https://github.com/ldionne commented:

Looks reasonable overall. I have a bit of concerns about dropping coverage for different string sizes but maybe you can convince me that it's fine?

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e319.cb30c3e4.6f38b.a051@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e319.f1c1923d.3672e2.b6b2@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
----------------
ldionne wrote:

```suggestion
          std::bind_front(bench_impl, std::false_type{}),
```

I think, otherwise we're measuring opaque twice?

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e319.65dfdb71.1ca534.c397@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Huge does not give us anything that Large doesn't have. Skip it.
-    return Length() == ::Length::Huge;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string.compare(src));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
----------------
ldionne wrote:

These say we're benchmarking `compare(char const*)`, but in reality the benchmark is calling them with a `basic_string` IIUC.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e319.3157459c.24b6e5.41e5@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Huge does not give us anything that Large doesn't have. Skip it.
-    return Length() == ::Length::Huge;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
----------------
ldionne wrote:

```suggestion
          using Str = std::basic_string<CharT>;
```

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e319.f732ad81.1d8ca.41dc@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
----------------
ldionne wrote:

`state.range()` is going to be always 0 IIUC since you're not passing `Arg`?

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:11 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e31b.af6ba3ed.2c2f30.2378@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
----------------
ldionne wrote:

What's the before/after time it takes to run these benchmarks?

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e319.bf38ae28.358429.f784@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
----------------
ldionne wrote:

`state.range()` is not used above since you're passing the size statically. So these `->Arg()` are unused IIUC. This also applies to other benchmarks I've flagged below.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:11 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e31b.481afadf.134314.4cf4@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
----------------
ldionne wrote:

I think it would make sense to retain names for the sizes you commonly use. I think you only use 5/30 now, so maybe `small` and `large`?

And do you think we don't need sizes larger than 30?

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:11 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e31b.481afadf.134314.4cf9@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
----------------
ldionne wrote:

Arg ignored here too.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:11 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e31b.01f0a1da.2016e9.07b8@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Huge does not give us anything that Large doesn't have. Skip it.
-    return Length() == ::Length::Huge;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string.compare(src));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
----------------
ldionne wrote:

Arg ignored here too.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:11 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e31b.f8b79514.3b441.e510@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
----------------
ldionne wrote:

Arg ignored here too.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:11 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e31b.cb30c3e4.6f38b.a138@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
----------------
ldionne wrote:

I'd be curious, can you please validate whether the assembly is different for opaque vs non-opaque? No need to check all benchmarks, just check one of them. I know this was pre-existing but I'm curious to know if it's worth even having that axis.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:11 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e31b.f1c1923d.3672e2.b7a4@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (opaque)",
----------------
ldionne wrote:

Is there any reason we've dropped `operator<` coverage? Was that intended?

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:28:11 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:28:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a47e31b.2020f6c2.1fdf61.14b0@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
----------------
ldionne wrote:

Arg ignored here too.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:38:39 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 09:38:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a47e58f.492e23e4.e71ca.6cd2@mx.google.com>


================
@@ -0,0 +1,138 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+struct Small {};
+
+struct Big {
+  char c[128];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
+// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
+template <>
+class std::function_ref<int> {
+  using storage = std::__function_ref_storage;
+  void test() {
+    {
+      // by value small argument
+      // the internal function should pass by value
+      using call = std::function_ref<void(Small)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
+    }
+    {
+      // by value big argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Big)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
+    }
+    {
+      // by value non-trivial small argument
+      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
+      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
+    }
+    {
+      // by lvalue reference argument
+      // the internal function should pass by lvalue reference
+      using call = std::function_ref<void(Small&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
+    }
+    {
+      // by rvalue reference argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Small&&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
+    }
+  }
+};
+
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
+void test() {
----------------
ldionne wrote:

@boomanaiden154 Would that be addressed by https://github.com/llvm/llvm-project/pull/190884 ? IIUC that PR would build FileCheck on macOS so that should solve this problem?

https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:45:00 2026
From: libcxx-commits at lists.llvm.org (Jake Egan via libcxx-commits)
Date: Fri, 03 Jul 2026 09:45:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a47e70c.faf16b37.1e0c59.8076@mx.google.com>


================
@@ -745,9 +744,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr)
+  if (!__mdstr) {
+#      if defined(_AIX) || defined(__TOS_AIX__)
+    if (__mode & ios_base::noreplace)
+      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
----------------
jakeegan wrote:

Can you use `_LIBCPP_DIAGNOSE_WARNING` instead? And then verify the warning in the test.

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:45:00 2026
From: libcxx-commits at lists.llvm.org (Jake Egan via libcxx-commits)
Date: Fri, 03 Jul 2026 09:45:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a47e70c.da8bafd3.1eab2b.e1e1@mx.google.com>


================
@@ -645,6 +634,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
----------------
jakeegan wrote:

`__TOS_AIX__` isn't necessary. Open XL defines `_AIX`. 

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Fri Jul  3 09:45:01 2026
From: libcxx-commits at lists.llvm.org (Jake Egan via libcxx-commits)
Date: Fri, 03 Jul 2026 09:45:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a47e70d.a151105c.30c1b9.ec22@mx.google.com>


================
@@ -75,15 +73,24 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
----------------
jakeegan wrote:

I don't think this comment is necessary.

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Fri Jul  3 10:10:48 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 10:10:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a47ed18.ff037943.3bf0e2.73a4@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Fri Jul  3 10:27:53 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 10:27:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a47f119.331483e7.5bc1e.1164@mx.google.com>


================
@@ -0,0 +1,138 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+struct Small {};
+
+struct Big {
+  char c[128];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
+// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
+template <>
+class std::function_ref<int> {
+  using storage = std::__function_ref_storage;
+  void test() {
+    {
+      // by value small argument
+      // the internal function should pass by value
+      using call = std::function_ref<void(Small)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
+    }
+    {
+      // by value big argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Big)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
+    }
+    {
+      // by value non-trivial small argument
+      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
+      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
+    }
+    {
+      // by lvalue reference argument
+      // the internal function should pass by lvalue reference
+      using call = std::function_ref<void(Small&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
+    }
+    {
+      // by rvalue reference argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Small&&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
+    }
+  }
+};
+
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
+void test() {
----------------
ldionne wrote:

So, after discussing this further, it turns out that `FileCheck` tests that check codegen are a lot more tricky than we expected. Indeed, unlike most Clang tests where TUs are self-contained, libc++ tests require stuff from the platform. That means you either:
1. Fix the target triple, but then you end up cross-compiling most of the time (e.g. you run a Filecheck test baked for linux on a macOS host), or
2. You don't fix the target triple in the FileCheck test, but then testing for the actual codegen is really difficult because codegen depends heavily on the target

So, for the time being, I think it's reasonable just not to test that property. It would have been nice to have a test on it, but it's acceptable not to.

CC @philnik777 since the insight about FileCheck difficulties is IMO interesting -- I assumed it would be easier.

https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Fri Jul  3 10:31:24 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 10:31:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a47f1ec.e55dc341.29c9b8.42b3@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/186692

>From 5e924c5c40412e114d746d5a4a77ead7748dca2d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 12 Apr 2026 09:35:44 +0100
Subject: [PATCH 01/13] [libcxx] Implement `std::constant_wrapper`

ci

ci

review comments

review comments

ci

review comments
---
 .../containers/views/views.span/span.cons/deduct.pass.cpp  | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index 9e41afad2258e..c9e17b3aec49a 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -73,6 +73,13 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
+
+  {
+    std::span s{std::begin(arr), std::cw<3>};
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+    assert(s.size() == std::size(arr));
+    assert(s.data() == std::data(arr));
+  }
 #endif
 }
 

>From f0dea1ecd3912e08d82f133ee4843f620060e9df Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 8 Jun 2024 17:32:42 -0700
Subject: [PATCH 02/13] [libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

rebase + put in experimental + rename nontype to constant_arg

tests

remove experimental

test for constant_arg_ref

test for constant_arg_ptr

remove fun_ptr member

invoke

format

constexpr function ptr

double unwrapping

P3948R1

tests for new papers

format

lwg issue

lwg issues

review comment

remove constexpr

optimise small types

ci

ci

ci

ci
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/23.rst               |   3 +
 libcxx/docs/Status/Cxx26Issues.csv            |   4 +-
 libcxx/docs/Status/Cxx26Papers.csv            |   6 +-
 libcxx/include/CMakeLists.txt                 |   3 +
 libcxx/include/__functional/function_ref.h    |  33 ++
 .../__functional/function_ref_common.h        | 126 ++++++
 .../include/__functional/function_ref_impl.h  | 184 +++++++++
 libcxx/include/functional                     |   9 +
 libcxx/include/module.modulemap.in            |   3 +
 libcxx/include/version                        |   4 +-
 libcxx/modules/std/functional.inc             |   4 +
 .../assert.constant_arg_ptr.pass.cpp          |  31 ++
 .../assert.function_ptr.pass.cpp              |  26 ++
 .../constant_arg.mandates.verify.cpp          |  60 +++
 .../internal_call_signature.pass.cpp          | 136 +++++++
 .../functional.version.compile.pass.cpp       |  16 +-
 .../version.version.compile.pass.cpp          |  16 +-
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     | 107 +++++
 .../func.wrap.ref.ctor/assign.delete.pass.cpp | 115 ++++++
 .../constant_wrapper.pass.cpp                 | 175 ++++++++
 .../constant_wrapper_ptr.pass.cpp             | 359 +++++++++++++++++
 .../constant_wrapper_ref.pass.cpp             | 373 ++++++++++++++++++
 .../func.wrap.ref.ctor/copy.pass.cpp          | 131 ++++++
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   | 158 ++++++++
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  | 149 +++++++
 .../func.wrap.ref.ctor/move.pass.cpp          | 129 ++++++
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 156 ++++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 316 +++++++++++++++
 .../func.wrap.ref.inv/invoke.pass.cpp         | 362 +++++++++++++++++
 .../trivially_copyable.compile.pass.cpp       |  33 ++
 .../generate_feature_test_macro_components.py |   3 +-
 32 files changed, 3200 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/include/__functional/function_ref.h
 create mode 100644 libcxx/include/__functional/function_ref_common.h
 create mode 100644 libcxx/include/__functional/function_ref_impl.h
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 8e26bdcd860c1..a14ae2d9d32f5 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -478,7 +478,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_fstream_native_handle``                        ``202306L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_function_ref``                                 *unimplemented*
+    ``__cpp_lib_function_ref``                                 ``202603L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_generate_random``                              *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e7eb42207db42..609af1452c53d 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -52,6 +52,9 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 419e5755ed4a8..c41a2e6e6bcc4 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","","","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","","","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index a951ac4c6c833..abe5b13373739 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","","","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."
@@ -192,9 +192,9 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","","","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","","","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index cb7ae820bc012..06c4d28e0a7f3 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -437,6 +437,9 @@ set(files
   __functional/compose.h
   __functional/default_searcher.h
   __functional/function.h
+  __functional/function_ref.h
+  __functional/function_ref_common.h
+  __functional/function_ref_impl.h
   __functional/hash.h
   __functional/identity.h
   __functional/invoke.h
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
new file mode 100644
index 0000000000000..b7fd3a4a2c79f
--- /dev/null
+++ b/libcxx/include/__functional/function_ref.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+
+#include <__config>
+#include <__functional/function_ref_common.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26
+// NOLINTBEGIN(readability-duplicate-include)
+
+#  define _LIBCPP_FUNCTION_REF_CV
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+#  define _LIBCPP_FUNCTION_REF_CV const
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+// NOLINTEND(readability-duplicate-include)
+#endif // _LIBCPP_STD_VER >= 26
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
new file mode 100644
index 0000000000000..61979231b56e8
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_COMMON_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+
+#include <__config>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/constant_wrapper.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization : false_type {};
+
+// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+// pointers
+// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
+// supported
+union __storage_func_ref_t {
+  void* __obj_ptr_;
+  void const* __obj_const_ptr_;
+  void (*__fn_ptr_)();
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        __obj_const_ptr_ = __ptr;
+      } else {
+        __obj_ptr_ = __ptr;
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
+    }
+  }
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
+      } else {
+        return static_cast<_Tp*>(__storage.__obj_ptr_);
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
+    }
+  }
+};
+
+template <class _Fp, class _Tp>
+struct __function_ref_bind {};
+
+// F is of the form R(*)(G, A...) noexcept(E) for a type G.
+template <bool _Noexcept, class _Tp, class _Rp, class _Gp, class... _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept(_Noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_Noexcept);
+};
+
+template <class _Tp, class _Mp, class _Gp>
+  requires is_object_v<_Mp>
+struct __function_ref_bind<_Mp _Gp::*, _Tp> {
+  using type _LIBCPP_NODEBUG = invoke_result_t<_Mp _Gp::*, _Tp&>() noexcept;
+};
+
+template <class _Fp, class _Tp>
+using __function_ref_bind_t _LIBCPP_NODEBUG = __function_ref_bind<_Fp, _Tp>::type;
+
+template <class _Fp>
+  requires is_function_v<_Fp>
+function_ref(_Fp*) -> function_ref<_Fp>;
+
+template <auto _Cw, class _Fn>
+  requires is_function_v<remove_pointer_t<_Fn>>
+function_ref(constant_wrapper<_Cw, _Fn>) -> function_ref<remove_pointer_t<_Fn>>;
+
+template <auto _Cw, class _Fn, class _Tp>
+function_ref(constant_wrapper<_Cw, _Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<_Fn, _Tp&>>;
+
+template <class>
+constexpr bool __is_constant_wrapper = false;
+
+template <auto _Value>
+constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
+
+template <class _Arg>
+struct __function_ref_arg_fwd {
+  using type _LIBCPP_NODEBUG = _Arg&&;
+};
+
+template <class _Arg>
+  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
+struct __function_ref_arg_fwd<_Arg> {
+  using type _LIBCPP_NODEBUG = _Arg;
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
new file mode 100644
index 0000000000000..a4c98d3031602
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -0,0 +1,184 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
+// and generates multiple versions of std::function_ref
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#include <__functional/invoke.h>
+#include <__memory/addressof.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/constant_wrapper.h>
+#include <__utility/forward.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#  error This header should only be included from function_ref.h
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
+    _NoExcept1,
+    _Rp,
+    _ArgTypes...>
+    : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
+
+template <class _Rp, class... _ArgTypes, bool __is_noexcept>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
+private:
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using =
+      _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
+          value;
+
+  template <class _Fn>
+  static constexpr bool __is_convertible_from_specialization_v =
+      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+
+  template <class... _Tp>
+  friend class function_ref;
+
+  template <class _Arg>
+  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+
+  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+
+  using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
+
+  __storage_t __storage_;
+  __call_t __call_;
+
+public:
+  template <class _Fp>
+    requires is_function_v<_Fp> && __is_invocable_using<_Fp>
+  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+      : __storage_(__fn_ptr),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+  }
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {}
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && __is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(__obj.__storage_), __call_(__obj.__call_) {}
+
+  template <auto _Cw, class _Fn>
+    requires __is_invocable_using<const _Fn&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
+      : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+    if constexpr (sizeof...(_ArgTypes) > 0 && (__constexpr_param<remove_cvref_t<_ArgTypes>> && ...)) {
+      static_assert(
+          !requires {
+            typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
+          },
+          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
+          "and produces a result with a structural type");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
+    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Tp>
+    requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
+      : __storage_(__obj_ptr_),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+
+    if constexpr (is_member_pointer_v<_Fn>) {
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(const function_ref&) noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+  template <class _Tp>
+    requires(!__is_convertible_from_specialization_v<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_wrapper<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
+
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
+    return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
+  }
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 9d588231d548d..df79d616a57e2 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -487,6 +487,11 @@ template <class  R, class ... ArgTypes>
 template <class  R, class ... ArgTypes>
   void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
 
+// [func.wrap.ref], non-owning wrapper
+template<class... S> class function_ref;                              // freestanding, not defined, since C++26
+template<class R, class... ArgTypes>
+  class function_ref<R(ArgTypes...) cv noexcept(noex)>;               // freestanding, since C++26
+
 template <class T> struct hash;
 
 template <> struct hash<bool>;
@@ -571,6 +576,10 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 #    include <__type_traits/unwrap_ref.h>
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+#    include <__functional/function_ref.h>
+#  endif
+
 #  include <version>
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b11055940a82c..4e14b3864c297 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1434,6 +1434,9 @@ module std [system] {
     }
     module default_searcher             { header "__functional/default_searcher.h" }
     module function                     { header "__functional/function.h" }
+    module function_ref                 { header "__functional/function_ref.h" }
+    module function_ref_common          { header "__functional/function_ref_common.h" }
+    module function_ref_impl            { textual header "__functional/function_ref_impl.h" }
     module hash                         { header "__functional/hash.h" }
     module identity                     { header "__functional/identity.h" }
     module invoke                       { header "__functional/invoke.h" }
diff --git a/libcxx/include/version b/libcxx/include/version
index 7f2dc9e4b72ab..74a5999fa5a68 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -130,7 +130,7 @@ __cpp_lib_freestanding_optional                         202311L <optional>
 __cpp_lib_freestanding_string_view                      202311L <string_view>
 __cpp_lib_freestanding_variant                          202311L <variant>
 __cpp_lib_fstream_native_handle                         202306L <fstream>
-__cpp_lib_function_ref                                  202306L <functional>
+__cpp_lib_function_ref                                  202603L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
@@ -591,7 +591,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 #   define __cpp_lib_fstream_native_handle              202306L
 # endif
-// # define __cpp_lib_function_ref                         202306L
+# define __cpp_lib_function_ref                         202603L
 // # define __cpp_lib_generate_random                      202403L
 // # define __cpp_lib_hazard_pointer                       202306L
 // # define __cpp_lib_inplace_vector                       202406L
diff --git a/libcxx/modules/std/functional.inc b/libcxx/modules/std/functional.inc
index 9ef8f584611fc..be804b19f75f8 100644
--- a/libcxx/modules/std/functional.inc
+++ b/libcxx/modules/std/functional.inc
@@ -95,6 +95,10 @@ export namespace std {
 
   using std::function;
 
+#if _LIBCPP_STD_VER >= 26
+  using std::function_ref;
+#endif // _LIBCPP_STD_VER >= 26
+
   using std::swap;
 
   using std::operator==;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
new file mode 100644
index 0000000000000..9703de0a59c2c
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+// Preconditions: If is_member_pointer_v<F> is true, obj is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+struct A {
+  void f() {}
+};
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(std::function_ref<void()>(std::cw<&A::f>, static_cast<A*>(nullptr)),
+                             "the object pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
new file mode 100644
index 0000000000000..e69776d0e3c90
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<class F> function_ref(F* f) noexcept;
+// Preconditions: f is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::function_ref<void()>(static_cast<void (*)()>(nullptr)), "the function pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
new file mode 100644
index 0000000000000..519957eec6878
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto c, class F>
+//   constexpr function_ref(constant_wrapper<c, F>) noexcept;
+// template<auto c, class F, class U>
+//   constexpr function_ref(constant_wrapper<c, F>, U&& obj) noexcept;
+// template<auto c, class F, class T>
+//   constexpr function_ref(constant_wrapper<c, F>, cv T* obj) noexcept;
+
+// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
+
+// For the first overload,
+// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+
+#include <functional>
+#include <utility>
+
+struct A {
+  void f();
+};
+
+struct B {
+  constexpr int operator()(std::constant_wrapper<42>) const { return 42; }
+  constexpr int operator()(int) const { return 42; }
+};
+
+// clang-format off
+void test() {
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+
+  int i;
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  A a;
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+}
+// clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
new file mode 100644
index 0000000000000..05756cfc3b3b8
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Track number of moves
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+struct Small {};
+
+struct Big {
+  char c[128];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
+// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
+template <>
+class std::function_ref<int> {
+  using storage = std::__storage_func_ref_t;
+  void test() {
+    {
+      // by value small argument
+      // the internal function should pass by value
+      using call = std::function_ref<void(Small)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
+    }
+    {
+      // by value big argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Big)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
+    }
+    {
+      // by value non-trivial small argument
+      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
+      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
+    }
+    {
+      // by lvalue reference argument
+      // the internal function should pass by lvalue reference
+      using call = std::function_ref<void(Small&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
+    }
+    {
+      // by rvalue reference argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Small&&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
+    }
+  }
+};
+
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
+void test() {
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In this case, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index b7b7d0334830a..bcc453880c7e1 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -494,17 +494,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_invoke
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 d9c78b73f7e23..1622aeb770684 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
@@ -7111,17 +7111,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_gcd_lcm
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
new file mode 100644
index 0000000000000..029cb19d54f6a
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+int fn(int, float) { return 42; }
+
+int fn_noexcept(int, float) noexcept { return 42; }
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem(int, float) { return 42; }
+  int fn_mem_ref(int, float) & { return 42; }
+  int fn_mem_const(int, float) const { return 42; }
+  int fn_mem_const_ref(int, float) const& { return 42; }
+  int fn_mem_noexcept(int, float) noexcept { return 42; }
+};
+
+void test() {
+  // template<class F>
+  //  function_ref(F*) -> function_ref<F>;
+  {
+    std::function_ref fn_ref = fn;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = fn_noexcept;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F>
+  //  function_ref(constant_wrapper<c, F>) -> function_ref<...>;
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn_noexcept>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F, class T>
+  //  function_ref(constant_wrapper<c, F>, T&&) -> function_ref<...>;
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
+  }
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn_noexcept>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float) noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&() noexcept>>);
+  }
+  {
+    const S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&() noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_noexcept>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
new file mode 100644
index 0000000000000..b4df4392a2ade
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class T> function_ref& operator=(T) = delete;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - T is not the same type as function_ref,
+// - is_pointer_v<T> is false, and
+// - T is not a specialization of constant_wrapper.
+
+// non const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[] {}>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
+
+// const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[] { return 42; }>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[](int) { return 42; }>>);
+
+// non-const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[] noexcept {} >>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+// const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[] noexcept {}>>);
+static_assert(
+    !std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+int forty_two() { return 42; }
+
+void test() {
+  {
+    std::function_ref<int()> f(std::cw<[] { return 41; }>);
+    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = &forty_two;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const noexcept
+    std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
new file mode 100644
index 0000000000000..4b425ee03e286
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
@@ -0,0 +1,175 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f> constexpr function_ref(constant_wrapper<f>) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&> is true.
+
+auto l1          = [] {};
+auto l1_noexcept = [] noexcept {};
+auto l2          = [](int) {};
+auto l2_noexcept = [](int) noexcept {};
+
+struct NonConstInvocable {
+  void operator()() noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+// const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    std::function_ref<void()> f = std::cw<[] {}>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
new file mode 100644
index 0000000000000..dd6e10e8a3289
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -0,0 +1,359 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&, cv T*> is true.
+
+auto l1                = [](int*) {};
+auto l1_const          = [](const int*) {};
+auto l1_noexcept       = [](int*) noexcept {};
+auto l1_const_noexcept = [](const int*) noexcept {};
+auto l2                = [](int*, double) {};
+auto l2_const          = [](const int*, double) {};
+auto l2_noexcept       = [](int*, double) noexcept {};
+auto l2_const_noexcept = [](const int*, double) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(int*) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, const int*>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2_const>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const noexcept>,
+                                      std::constant_wrapper<l2_const_noexcept>,
+                                      int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_const_noexcept>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<l1_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const noexcept>,
+                                              std::constant_wrapper<l2_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<&A::f_const_noexcept>,
+                                              A*>);
+
+double f1(const int* x, double y) noexcept { return *x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int*) {}>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int*) {}>, &i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int* j, double d) {
+          *j = 5;
+          return *j + d;
+        }>,
+        &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
new file mode 100644
index 0000000000000..485fa95ca2998
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -0,0 +1,373 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class U>
+//   constexpr function_ref(constant_wrapper<f>, U&& obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_rvalue_reference_v<U&&> is false, and
+// - is-invocable-using<const F&, cv T&> is true.
+
+auto l1          = [](int) {};
+auto l1_noexcept = [](int) noexcept {};
+auto l2          = [](int, double) {};
+auto l2_noexcept = [](int, double) noexcept {};
+auto l3          = [](int&) {};
+auto l3_noexcept = [](int&) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(long) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&&>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int) {}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int) {}>, i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int& j, double d) {
+          j = 5;
+          return j + d;
+        }>,
+        i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
new file mode 100644
index 0000000000000..61b70a559d786
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
new file mode 100644
index 0000000000000..1a8b26f86c8be
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
new file mode 100644
index 0000000000000..f142b81367ef1
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> function_ref(F* f) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_function_v<F> is true, and
+// - is-invocable-using<F> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (*)(int)>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A (*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, A)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+
+// const noexcept(true)
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, A) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              void (*)(int, double) noexcept>);
+
+int fn() { return 42; }
+
+int fn_maythrow(int i, A a) { return i - a.i; }
+int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+
+void foo(int) {}
+void bar(int) noexcept {}
+struct Int {
+  int i;
+  Int(int ii) noexcept : i(ii) {}
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+void test() {
+  {
+    // simple case
+    std::function_ref<int()> f(&fn);
+    assert(f() == 42);
+  }
+  {
+    // explicit(false)
+    std::function_ref<int()> f = &fn;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int(int, A)> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // noexcept
+    std::function_ref<int(int, A) noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // const
+    auto l = [](int x, int y, int z) { return x + y - z; };
+    std::function_ref<int(int, int, int) const> f(+l);
+    assert(f(2, 3, 4) == 1);
+  }
+  {
+    // const noexcept
+    std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+
+  {
+    std::function_ref<Int(int, int, int)> f(&needs_conversion);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(&needs_conversion);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(&needs_conversion);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
+    assert(f4(1, 2, 3).i == 6);
+
+    {
+      std::function_ref r1 = foo;
+      std::function_ref r2 = bar;
+      r1                   = r2; // ok
+    }
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
new file mode 100644
index 0000000000000..0f27bba821391
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
new file mode 100644
index 0000000000000..b40dfa9f2cf4b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
new file mode 100644
index 0000000000000..56d6dd3b7109b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -0,0 +1,316 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> constexpr function_ref(F&&) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - remove_cvref_t<F> is not the same type as function_ref,
+// - is_member_pointer_v<T> is false, and
+// - is-invocable-using<cv T&> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+constexpr auto l1 = [] {};
+using L1          = std::remove_cvref_t<decltype(l1)>;
+
+constexpr auto l2 = [] { return A{5}; };
+using L2          = std::remove_cvref_t<decltype(l2)>;
+
+constexpr auto l2_noexcept = [] noexcept { return A{5}; };
+using L2Noexcept           = std::remove_cvref_t<decltype(l2_noexcept)>;
+
+constexpr auto l3 = [](int x, double d) { return x + d; };
+using L3          = std::remove_cvref_t<decltype(l3)>;
+
+constexpr auto l3_noexcept = [](int x, double d) noexcept { return x + d; };
+using L3Noexcept           = std::remove_cvref_t<decltype(l3_noexcept)>;
+
+struct NonConstInvocable {
+  int i;
+
+  int operator()(int x, double y) {
+    ++i;
+    return x + y + i;
+  }
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L2&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2&>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2 const&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const>, std::function_ref<int(int, double) const>>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(int, double)>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double)>, const NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>,
+                                              std::function_ref<int(int, double) const>>);
+
+// const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                      std::function_ref<int(int, double) const noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              std::function_ref<int(int, double) const noexcept>>);
+
+struct F {
+  int i;
+  int operator()(auto&&...) { return 5 + i; }
+
+  int operator()(auto&&...) const { return 6 + i; }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int fn() { return 5; }
+constexpr auto fn_noexcept = []() noexcept { return 6; };
+
+const auto one = []() noexcept { return 1; };
+const auto two = []() noexcept { return 2; };
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(l1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit(false)
+    std::function_ref<void()> f = l1;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<A() noexcept> f(l2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      auto a = f();
+      assert(a.i == 5);
+    }
+  }
+  {
+    // const
+    std::function_ref<double(int, double) const> f(l3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(l3_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // no copies of original callable
+    auto local = [i = 5] mutable { return i++; };
+    std::function_ref<int()> f(local);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 5);
+      assert(local() == 6);
+      assert(f() == 7);
+    }
+  }
+  {
+    // const correctness
+    F f{5};
+
+    std::function_ref<int()> f1(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1() == 10);
+      assert(std::as_const(f1)() == 10);
+    }
+
+    std::function_ref<int() const> f2(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 11);
+      assert(std::as_const(f2)() == 11);
+    }
+  }
+  {
+    // with conversions
+    NeedsConversion c{};
+
+    std::function_ref<Int(int, int, int)> f(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  {
+    // noexcept conversion
+    std::function_ref<int() noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const conversion
+    std::function_ref<int() const> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() noexcept> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to const conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() const> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // P3961R1 Less double indirection in function_ref
+    // double unwrapping
+    std::function_ref<int() const noexcept> f1(std::cw<one>);
+    std::function_ref<int()> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 1 || f2() == 2);
+      LIBCPP_ASSERT(f2() == 1);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
new file mode 100644
index 0000000000000..1cd0c769ce4d3
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -0,0 +1,362 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// R operator()(ArgTypes... args) const noexcept(noex);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "MoveOnly.h"
+
+template <class T, class... Args>
+concept ConstInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) };
+};
+
+template <class T, class... Args>
+concept ConstNoexceptInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) } noexcept;
+};
+
+static_assert(ConstInvocable<std::function_ref<void()>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void()>>);
+
+static_assert(ConstInvocable<std::function_ref<void() noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() noexcept>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void() const>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() const noexcept>>);
+
+struct S {
+  int data = 42;
+  int operator()(int& x) const noexcept { return data + x; }
+  int operator()(const int& x) const noexcept { return data + x + 1; }
+  int operator()(int&& x) const noexcept { return data + x + 2; }
+  int operator()(const int&& x) const noexcept { return data + x + 3; }
+};
+
+struct S2 {
+  double operator()(int x, int y, int z) noexcept { return x + y + z; }
+
+  double operator()(int x, int y, int z) const noexcept { return x + y + z + 1; }
+};
+
+struct Big {
+  char c[128];
+};
+
+struct S3Ref {
+  char operator()(Big& b) const noexcept { return b.c[0]; }
+};
+
+struct S3Value {
+  char operator()(Big b) const noexcept { return b.c[0]; }
+};
+
+void test_default() {
+  {
+    std::function_ref<void()> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&)> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly)> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int)> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&)> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&)> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&)> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&)> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int)> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int)> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&)> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const() {
+  {
+    std::function_ref<void() const> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_noexcept() {
+  {
+    std::function_ref<void() noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const_noexcept() {
+  {
+    std::function_ref<void() const noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test() {
+  test_default();
+  test_const();
+  test_noexcept();
+  test_const_noexcept();
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
new file mode 100644
index 0000000000000..72e26f13868ea
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
+#include <concepts>
+#include <functional>
+#include <type_traits>
+
+struct A {
+  A() {}
+  A(const A&) {}
+  ~A() {}
+};
+
+static_assert(std::copyable<std::function_ref<A(const A&)>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&)>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) noexcept>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const noexcept>>);
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d764a1f677ba6..bf6a698a301d8 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -703,10 +703,9 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_function_ref",
             "values": {
-                "c++26": 202306  # P0792R14 function_ref: a type-erased callable reference
+                "c++26": 202603  # P3948R1 constant_wrapper is the only tool needed for passing constant expressions via function arguments
             },
             "headers": ["functional"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_gcd_lcm",

>From 3ec28c6dea778575fd14b22e7178b73fc9926e0e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 25 Apr 2026 14:20:04 +0100
Subject: [PATCH 03/13] review comments

---
 .../__functional/function_ref_common.h        | 22 ++++++-------------
 .../include/__functional/function_ref_impl.h  |  2 +-
 .../internal_call_signature.pass.cpp          |  2 +-
 .../constant_wrapper_ref.pass.cpp             |  8 +++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 12 ++++++++++
 5 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 61979231b56e8..72ded36d65107 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -16,6 +16,7 @@
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
 
@@ -37,21 +38,16 @@ struct __is_convertible_from_specialization : false_type {};
 // pointers
 // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
 // supported
-union __storage_func_ref_t {
+union __function_ref_storage {
   void* __obj_ptr_;
-  void const* __obj_const_ptr_;
   void (*__fn_ptr_)();
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage() noexcept : __obj_ptr_(nullptr) {}
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage(_Tp* __ptr) noexcept {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        __obj_const_ptr_ = __ptr;
-      } else {
-        __obj_ptr_ = __ptr;
-      }
+      __obj_ptr_ = const_cast<remove_cv_t<_Tp>*>(__ptr);
     } else {
       static_assert(is_function_v<_Tp>);
       __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
@@ -59,13 +55,9 @@ union __storage_func_ref_t {
   }
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__function_ref_storage __storage) {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
-      } else {
-        return static_cast<_Tp*>(__storage.__obj_ptr_);
-      }
+      return static_cast<_Tp*>(__storage.__obj_ptr_);
     } else {
       static_assert(is_function_v<_Tp>);
       return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index a4c98d3031602..36841d6745955 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -72,7 +72,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Arg>
   using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
 
-  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+  using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
   using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 05756cfc3b3b8..1e5df6413daf0 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -31,7 +31,7 @@ struct SmallButNonTrivial {
 // specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
 template <>
 class std::function_ref<int> {
-  using storage = std::__storage_func_ref_t;
+  using storage = std::__function_ref_storage;
   void test() {
     {
       // by value small argument
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index 485fa95ca2998..d3d5f6827193d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -362,6 +362,14 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // volatile objects
+    volatile int i = 5;
+    std::function_ref<int() > f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 56d6dd3b7109b..9ab012b3a04e8 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -161,6 +161,10 @@ constexpr auto fn_noexcept = []() noexcept { return 6; };
 const auto one = []() noexcept { return 1; };
 const auto two = []() noexcept { return 2; };
 
+struct VolatileCallable {
+  int operator()() volatile const { return 42; }
+};
+
 constexpr bool test() {
   {
     std::function_ref<void()> f(l1);
@@ -305,6 +309,14 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // volatile objects
+    const volatile VolatileCallable vc{};
+    std::function_ref<int()> f(vc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
 
   return true;
 }

>From 064166e69f7a3f7e1d1c9720631ff23b44f1a9b4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 26 Apr 2026 09:41:10 +0100
Subject: [PATCH 04/13] remove stale comments

---
 libcxx/include/__functional/function_ref_common.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 72ded36d65107..c5adb8cbc7d52 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -34,10 +34,6 @@ class function_ref;
 template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization : false_type {};
 
-// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
-// pointers
-// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
-// supported
 union __function_ref_storage {
   void* __obj_ptr_;
   void (*__fn_ptr_)();

>From 52b08ca9daed06d59f60023ef7f2cb20e2c7f851 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 14:06:05 +0100
Subject: [PATCH 05/13] fix bug

---
 .../__functional/function_ref_common.h        |  2 +-
 .../include/__functional/function_ref_impl.h  | 19 ++++++++++++-------
 .../internal_call_signature.pass.cpp          |  4 +++-
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  6 +++---
 .../func.wrap.ref.ctor/ref.pass.cpp           | 11 +++++++++++
 5 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index c5adb8cbc7d52..60bc6ef87af4f 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -31,7 +31,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <class _Fn1, class _Fn2>
 struct __is_convertible_from_specialization : false_type {};
 
 union __function_ref_storage {
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 36841d6745955..55755e9804c95 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -46,14 +46,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization<
-    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
-    _NoExcept1,
-    _Rp,
-    _ArgTypes...>
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) const noexcept(_NoExcept2)> >
     : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
 
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) noexcept(_NoExcept2)> >
+    : _And<is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)>,
+           is_convertible<_LIBCPP_FUNCTION_REF_CV int&, int&>> {};
+
 template <class _Rp, class... _ArgTypes, bool __is_noexcept>
 class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
 private:
@@ -62,9 +67,9 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
           value;
 
-  template <class _Fn>
+  template <class _Fn2>
   static constexpr bool __is_convertible_from_specialization_v =
-      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+      __is_convertible_from_specialization<function_ref, _Fn2>::value;
 
   template <class... _Tp>
   friend class function_ref;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 1e5df6413daf0..ebdf683791694 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -8,7 +8,9 @@
 
 // REQUIRES: std-at-least-c++26
 
-// Track number of moves
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
 
 #include <cassert>
 #include <functional>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index b4df4392a2ade..cb84b3a183920 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -35,9 +35,9 @@ static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrap
 static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
 
 // const noexcept(false)
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
@@ -61,7 +61,7 @@ static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::con
 // const noexcept(true)
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 9ab012b3a04e8..6b8b7c71f0d08 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -309,6 +309,17 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // P3961R1 Less double indirection in function_ref
+    // is-convertible-from-specialization<remove_cv_t<T>> is false
+    std::function_ref<int()> f1(std::cw<one>);
+    std::function_ref<int() const> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 2);
+    }
+  }
   {
     // volatile objects
     const volatile VolatileCallable vc{};

>From 73e42bb1776a0208b766cbe09804cd4d951079a0 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 17:28:36 +0100
Subject: [PATCH 06/13] ci

---
 libcxx/include/__functional/function_ref_impl.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 55755e9804c95..454b0ffb4afb3 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -15,6 +15,7 @@
 #include <__functional/invoke.h>
 #include <__memory/addressof.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/conjunction.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>

>From 65770d03e5304cf101fdc580af40f97047a5b7c3 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 10:39:44 +0100
Subject: [PATCH 07/13] static operator

---
 .../include/__functional/function_ref_impl.h  | 23 +++++++++++----
 .../func.wrap.ref.ctor/ref.pass.cpp           | 28 +++++++++++++++++++
 2 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 454b0ffb4afb3..ab65382782fb2 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
@@ -99,12 +100,22 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
-      : __storage_(std::addressof(__obj)),
-        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
-        }) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
+    using _Dn = remove_cv_t<_Tp>;
+    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
+                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+                  }) {
+      __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    } else {
+      __storage_ = __storage_t(std::addressof(__obj)),
+      __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    }
+  }
 
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 6b8b7c71f0d08..2684ec3548f8e 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -328,6 +328,34 @@ constexpr bool test() {
       assert(f() == 42);
     }
   }
+  {
+    // static call operator
+    struct StaticCall {
+      static int operator()(int x, int y) { return x + y; }
+    };
+    StaticCall sc;
+    std::function_ref<int(int, int)> f(sc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2) == 3);
+    }
+  }
+  {
+    // overload set with static and non-static call operator
+    struct OverloadSet {
+      static int operator()(int x) { return x + 1; }
+      int operator()(long x) const { return x + 2; }
+    };
+    OverloadSet os;
+    std::function_ref<int(int)> f1(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1(1) == 2);
+    }
+
+    std::function_ref<int(long)> f2(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1L) == 3);
+    }
+  }
 
   return true;
 }

>From 975474ecf6b4030804fb3e03d61d4a80f34d6cf2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 19:26:38 +0100
Subject: [PATCH 08/13] work around clang 22 bug

---
 libcxx/include/__functional/function_ref_common.h             | 4 ++++
 libcxx/include/__functional/function_ref_impl.h               | 4 +---
 .../func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp   | 1 +
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 60bc6ef87af4f..bfe3d938056f0 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -19,6 +19,7 @@
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
+#include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -107,6 +108,9 @@ struct __function_ref_arg_fwd<_Arg> {
   using type _LIBCPP_NODEBUG = _Arg;
 };
 
+template <class _Fn, class... _Args>
+concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
+
 #endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index ab65382782fb2..7a173dee7fa4f 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -102,9 +102,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
     using _Dn = remove_cv_t<_Tp>;
-    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
-                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
-                  }) {
+    if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
       };
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 2684ec3548f8e..11b993e4a3ec3 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -351,6 +351,7 @@ constexpr bool test() {
       assert(f1(1) == 2);
     }
 
+    LIBCPP_STATIC_ASSERT(!std::__statically_callable<OverloadSet, long>);
     std::function_ref<int(long)> f2(os);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f2(1L) == 3);

>From b06853cfe55ae5e982f7d4320c459bd9d84652df Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:12:34 +0100
Subject: [PATCH 09/13] review comments

---
 libcxx/include/__functional/function_ref_common.h | 15 +++++++--------
 libcxx/include/__functional/function_ref_impl.h   |  6 +++---
 .../constant_arg.mandates.verify.cpp              |  2 +-
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index bfe3d938056f0..0f40a07073cfc 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -15,7 +15,8 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
-#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
@@ -98,15 +99,13 @@ template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
 template <class _Arg>
-struct __function_ref_arg_fwd {
-  using type _LIBCPP_NODEBUG = _Arg&&;
-};
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
 
 template <class _Arg>
-  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
-struct __function_ref_arg_fwd<_Arg> {
-  using type _LIBCPP_NODEBUG = _Arg;
-};
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
 
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a173dee7fa4f..3424a99bdd174 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -77,7 +77,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   friend class function_ref;
 
   template <class _Arg>
-  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+  using __arg_t _LIBCPP_NODEBUG = _If<__register_passable<_Arg>, _Arg, _Arg&&>;
 
   using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
@@ -135,8 +135,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
           !requires {
             typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
           },
-          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
-          "and produces a result with a structural type");
+          "cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref "
+          "constructed from cw would be ambiguous");
     }
   }
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 519957eec6878..63cfbc0e97c9b 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -41,7 +41,7 @@ void test() {
   // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
   std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}

>From 07ab860ed99b15ae5105da7627b4af36ccceae66 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:13:48 +0100
Subject: [PATCH 10/13] use std::forward instead of static_cast

---
 libcxx/include/__functional/function_ref_impl.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 3424a99bdd174..d276c78fd09e6 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -92,7 +92,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -104,13 +104,13 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     using _Dn = remove_cv_t<_Tp>;
     if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return _Dn::operator()(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     } else {
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -125,7 +125,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     requires __is_invocable_using<const _Fn&>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
       : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -146,7 +146,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -160,7 +160,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(__obj_ptr_),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");

>From b6b3b075afff864969631bfb4f7e2e9d81497664 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:14:02 +0100
Subject: [PATCH 11/13] format

---
 .../func.wrap.ref.ctor/constant_arg.mandates.verify.cpp      | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 63cfbc0e97c9b..c84cca2793bac 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -17,8 +17,9 @@
 
 // Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
 
-// For the first overload,
-// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+// For the first overload, f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>...
+// satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)>
+// is not a valid type.
 
 #include <functional>
 #include <utility>

>From 4a560ea7c68de0a73473f0d14460c062447e4068 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 20 Jun 2026 16:28:00 +0100
Subject: [PATCH 12/13] review commnets

---
 libcxx/include/CMakeLists.txt                 |   1 +
 .../__functional/function_ref_common.h        |   9 -
 .../include/__functional/function_ref_impl.h  |   8 +-
 .../include/__type_traits/register_passable.h |  38 ++++
 libcxx/include/module.modulemap.in            |   1 +
 .../constant_arg.mandates.verify.cpp          |  26 +--
 .../internal_call_signature.pass.cpp          | 138 ---------------
 .../internal_call_signature.sh.cpp            |  68 ++++++++
 .../views.span/span.cons/deduct.pass.cpp      |   7 -
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     |   9 +
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  24 ++-
 .../constant_wrapper_ptr.pass.cpp             |  60 ++++++-
 .../constant_wrapper_ref.pass.cpp             |  59 ++++++-
 .../func.wrap.ref.ctor/copy.pass.cpp          |   5 +
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   |  71 ++++++--
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  |   6 -
 .../func.wrap.ref.ctor/move.pass.cpp          |  70 +-------
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 102 ++---------
 .../func.wrap.ref.inv/invoke.pass.cpp         | 162 ++++++++++++++++++
 19 files changed, 506 insertions(+), 358 deletions(-)
 create mode 100644 libcxx/include/__type_traits/register_passable.h
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 06c4d28e0a7f3..3c4b914bbf4bb 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -912,6 +912,7 @@ set(files
   __type_traits/rank.h
   __type_traits/reference_constructs_from_temporary.h
   __type_traits/reference_converts_from_temporary.h
+  __type_traits/register_passable.h
   __type_traits/remove_all_extents.h
   __type_traits/remove_const.h
   __type_traits/remove_const_ref.h
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 0f40a07073cfc..e6fa031432a91 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -98,15 +98,6 @@ constexpr bool __is_constant_wrapper = false;
 template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
-template <class _Arg>
-concept __itanium_trivial_for_calls =
-    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
-    is_trivially_move_constructible_v<_Arg>;
-
-template <class _Arg>
-concept __register_passable =
-    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
-
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
 
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index d276c78fd09e6..7a968fc64de70 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/register_passable.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
@@ -92,7 +93,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
+          _Fp* __fn_ptr1 = __storage_t::template __get<_Fp>(__storage);
+          return std::invoke_r<_Rp>(__fn_ptr1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -110,7 +112,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
+        return std::invoke_r<_Rp>(__obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -145,7 +147,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          auto& __obj1 = static_cast<_LIBCPP_FUNCTION_REF_CV _Tp&>(*__storage_t::template __get<_Tp>(__storage));
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
diff --git a/libcxx/include/__type_traits/register_passable.h b/libcxx/include/__type_traits/register_passable.h
new file mode 100644
index 0000000000000..21e23e62052c1
--- /dev/null
+++ b/libcxx/include/__type_traits/register_passable.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___TYPE_TRAITS_REGISTER_PASSABLE_H
+#define _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
+
+#include <__config>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Arg>
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
+
+template <class _Arg>
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
+
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4e14b3864c297..d323f5da34166 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -350,6 +350,7 @@ module std_core [system] {
       header "__type_traits/reference_converts_from_temporary.h"
       export std_core.type_traits.integral_constant
     }
+    module register_passable                          { header "__type_traits/register_passable.h" }
     module remove_all_extents                         { header "__type_traits/remove_all_extents.h" }
     module remove_const_ref                           { header "__type_traits/remove_const_ref.h" }
     module remove_const                               { header "__type_traits/remove_const.h" }
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index c84cca2793bac..08e8b8e9b8b52 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -35,27 +35,27 @@ struct B {
 
 // clang-format off
 void test() {
-  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
+  // expected-error-re@*:* {{static assertion failed {{.*}}cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
-  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   A a;
-  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 }
 // clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
deleted file mode 100644
index ebdf683791694..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-struct Small {};
-
-struct Big {
-  char c[128];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
-// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
-template <>
-class std::function_ref<int> {
-  using storage = std::__function_ref_storage;
-  void test() {
-    {
-      // by value small argument
-      // the internal function should pass by value
-      using call = std::function_ref<void(Small)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
-    }
-    {
-      // by value big argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Big)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
-    }
-    {
-      // by value non-trivial small argument
-      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
-      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
-    }
-    {
-      // by lvalue reference argument
-      // the internal function should pass by lvalue reference
-      using call = std::function_ref<void(Small&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
-    }
-    {
-      // by rvalue reference argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Small&&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
-    }
-  }
-};
-
-struct TrackCopyMove {
-  mutable int copy_count = 0;
-  int move_count         = 0;
-
-  TrackCopyMove() = default;
-  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
-    ++copy_count;
-    ++other.copy_count;
-  }
-
-  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
-    ++move_count;
-    ++other.move_count;
-  }
-  TrackCopyMove& operator=(const TrackCopyMove& other) {
-    ++copy_count;
-    ++other.copy_count;
-    return *this;
-  }
-  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
-    ++move_count;
-    ++other.move_count;
-    return *this;
-  }
-};
-
-void test() {
-  {
-    // Arg type is an lvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&)> f = lambda;
-    f(t);
-  }
-  {
-    // Arg type is an rvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&&)> f = lambda;
-    f(std::move(t));
-  }
-  {
-    // Arg type is a prvalue, we should move but not copy the object
-    // In this case, where the type is not trivially copyable, the object should be
-    // moved exactly once when passing into the lambda. The internal functions
-    // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) {
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 1);
-    };
-    std::function_ref<void(TrackCopyMove)> f = lambda;
-    f(TrackCopyMove{});
-  }
-}
-
-int main(int, char**) {
-  test();
-
-  return 0;
-}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
new file mode 100644
index 0000000000000..a32a1083b5596
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// REQUIRES: has-filecheck
+
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
+
+struct Small {
+  char c[8];
+};
+
+struct Big {
+  char c[256];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// CHECK: define{{.*}} @_Z10test_small
+// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
+// CHECK: %3 = alloca %struct.Small, align 1
+// CHECK: %6 = alloca %struct.Small, align 1
+// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
+// CHECK: store i64 %1, ptr %7, align 1
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
+// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
+// CHECK: %15 = load i64, ptr %14, align 1
+// CHECK: call void %10(ptr %13, i64 %15)
+//
+// The above has two local copies of Small object. It loads %1 to local copy %3, and then
+// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
+void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
+
+// CHECK: define{{.*}} @_Z8test_big
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
+//
+// The above directly calls the function pointer with the parameter Big object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the Big object by reference
+void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
+
+// CHECK: define{{.*}} @_Z22test_small_non_trivial
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
+//
+// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the SmallButNonTrivial object by reference
+void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index c9e17b3aec49a..9e41afad2258e 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -73,13 +73,6 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
-
-  {
-    std::span s{std::begin(arr), std::cw<3>};
-    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
-    assert(s.size() == std::size(arr));
-    assert(s.data() == std::data(arr));
-  }
 #endif
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
index 029cb19d54f6a..730a07202062d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -8,6 +8,15 @@
 
 // REQUIRES: std-at-least-c++26
 
+// template<class F>
+// function_ref(F*) -> function_ref<F>;
+//
+// template<auto c, class F0>
+// function_ref(constant_wrapper<c, F0>) -> function_ref<see below>;
+//
+// template<auto c, class F, class T>
+// function_ref(constant_wrapper<c, F>, T&&) -> function_ref<see below>;
+
 #include <cassert>
 #include <functional>
 #include <utility>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index cb84b3a183920..5866488be4a94 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -11,6 +11,7 @@
 // template<class T> function_ref& operator=(T) = delete;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -73,38 +74,49 @@ static_assert(
 
 int forty_two() { return 42; }
 
+// These runtime tests are testing that when the constraints are not met,
+// the assignment operator is not deleted and the implicit constructor
+// and the move assignment are called
 void test() {
   {
     std::function_ref<int()> f(std::cw<[] { return 41; }>);
-    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f =
+        std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = &forty_two;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = &forty_two;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // noexcept
     std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() noexcept>&> decltype(auto) result = f = std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const noexcept
     std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() const noexcept>&> decltype(auto) result = f =
+        std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
index dd6e10e8a3289..74f52063b6788 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -94,6 +94,7 @@ static_assert(
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
 static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, const int*>);
 
 static_assert(
     !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
@@ -180,6 +181,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int*) const noexcept { return 4; }
+  constexpr int operator()(const int*) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -231,6 +237,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -248,6 +262,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -263,39 +285,51 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -348,6 +382,20 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, &i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, &i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, &i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index d3d5f6827193d..979da32e60c3d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -199,6 +199,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int&) const noexcept { return 4; }
+  constexpr int operator()(const int&) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -248,6 +253,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -264,6 +277,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -278,37 +299,49 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -370,6 +403,20 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
index 61b70a559d786..7f837149cc6b5 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -22,6 +22,11 @@ static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
 double f1(int x, double y) noexcept { return x + y; }
 
 struct Int {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
index 1a8b26f86c8be..53c79b4097362 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -11,6 +11,7 @@
 // constexpr function_ref& operator=(const function_ref&) noexcept = default;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -22,6 +23,11 @@ static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
@@ -41,7 +47,8 @@ constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = f;
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -51,7 +58,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = f;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
@@ -62,7 +70,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -72,7 +81,8 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -82,32 +92,47 @@ constexpr bool test() {
     // with conversions
     std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
         std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
@@ -117,31 +142,47 @@ constexpr bool test() {
     // with conversions function pointer
     std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
     std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions function pointer
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index f142b81367ef1..aa0ccf8d67993 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -134,12 +134,6 @@ void test() {
 
     std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
     assert(f4(1, 2, 3).i == 6);
-
-    {
-      std::function_ref r1 = foo;
-      std::function_ref r2 = bar;
-      r1                   = r2; // ok
-    }
   }
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
index 0f27bba821391..025df83ad538d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -8,6 +8,8 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
 #include <functional>
 #include <utility>
@@ -20,18 +22,12 @@ static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-double f1(int x, double y) noexcept { return x + y; }
-
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+double f1(int x, double y) noexcept { return x + y; }
 
 constexpr bool test() {
   {
@@ -66,58 +62,6 @@ constexpr bool test() {
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
index b40dfa9f2cf4b..10620f5d48725 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -8,7 +8,10 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -20,26 +23,20 @@ static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
-
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
-int zero(Int, Int, Int) noexcept { return 0; }
-
 constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -49,8 +46,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = std::move(f);
-
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
       assert(f2() == 42);
@@ -60,7 +57,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -70,81 +68,13 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
-        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 1cd0c769ce4d3..0dced07c439d6 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -67,6 +67,32 @@ struct S3Value {
   char operator()(Big b) const noexcept { return b.c[0]; }
 };
 
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
 void test_default() {
   {
     std::function_ref<void()> f = [] {};
@@ -135,6 +161,40 @@ void test_default() {
     std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const() {
@@ -205,6 +265,40 @@ void test_const() {
     std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_noexcept() {
@@ -276,6 +370,40 @@ void test_noexcept() {
     std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const_noexcept() {
@@ -347,6 +475,40 @@ void test_const_noexcept() {
     std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test() {

>From d09c0d3ac69b98a38687a6acda037ec9bb05c64a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:38:35 +0100
Subject: [PATCH 13/13] format

---
 .../func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp       | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 0dced07c439d6..820fed5fc76bc 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -384,7 +384,7 @@ void test_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -397,7 +397,7 @@ void test_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };
@@ -489,7 +489,7 @@ void test_const_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -502,7 +502,7 @@ void test_const_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };


From libcxx-commits at lists.llvm.org  Fri Jul  3 10:34:44 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 10:34:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a47f2b4.4a342cbc.134cb.b043@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Fri Jul  3 10:34:44 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 10:34:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a47f2b4.eea6ab6e.1256b6.0886@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM with FileCheck removed.

Also, as discussed, I'd like to avoid introducing this type as non-experimental right before the LLVM 23 release, to give us at least a bit of baking time in the tree. Therefore, we'll wait until LLVM 23 is branched before we merge this PR.

https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Fri Jul  3 10:34:45 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 10:34:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a47f2b5.d5797644.146403.3179@mx.google.com>


================
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
----------------
ldionne wrote:

```suggestion
"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","24","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
```

https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Fri Jul  3 10:34:45 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 10:34:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a47f2b5.c56fb102.11841f.a364@mx.google.com>


================
@@ -54,6 +54,9 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
----------------
ldionne wrote:

This would move to 24.rst

https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Fri Jul  3 10:34:45 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 10:34:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a47f2b5.1a4df1a5.118deb.c58b@mx.google.com>


================
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","","","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
----------------
ldionne wrote:

```suggestion
"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","24","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
```

https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Fri Jul  3 10:34:45 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 10:34:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a47f2b5.27b3b003.1610ae.a33b@mx.google.com>


================
@@ -192,9 +192,9 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","","","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","","","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
----------------
ldionne wrote:

```suggestion
"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","24","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
```

https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Fri Jul  3 10:34:45 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 10:34:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a47f2b5.bd93e731.b068b.8b24@mx.google.com>


================
@@ -192,9 +192,9 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","","","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
----------------
ldionne wrote:

```suggestion
"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
```

https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Fri Jul  3 10:34:46 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 10:34:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a47f2b6.e55dc341.29c9b8.ba51@mx.google.com>


================
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","","","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
----------------
ldionne wrote:

```suggestion
"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","24","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
```

https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Fri Jul  3 10:35:50 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 10:35:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47f2f6.7d41f3a1.d1cf7.f0ce@mx.google.com>


https://github.com/ldionne unassigned https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:26 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4a.14d9b9dc.140829.0ed6@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:26 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4a.f1c1923d.3672e2.bd16@mx.google.com>


================
@@ -0,0 +1,243 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
----------------
ldionne wrote:

I would like to suggest using nested scopes inside a templated `test<Foo|Bar>` function directly, with plain english comments explaining what each test case is doing.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:26 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4a.7d41f3a1.d1cf7.feea@mx.google.com>


================
@@ -0,0 +1,243 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
----------------
ldionne wrote:

Missing synopsis. Please also add a short comment explaining what this test is about.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.65dfdb71.1ca534.5b18@mx.google.com>


https://github.com/ldionne requested changes to this pull request.

I'm glad to see this making progress! This is a non-trivial patch, I have some comments but it's looking very reasonable.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.331483e7.5bc1e.01cb@mx.google.com>


================
@@ -0,0 +1,243 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
----------------
ldionne wrote:

I think it would make sense to have at least a few different types being tested here. For example, with padding between two members. To make your life easier, perhaps keep limit it to two members:

```
struct Bar {
  char c;
  int i;
};
```

That's not a crazy increase in coverage, but it's a bit better, and it allows making the test at least somewhat generic.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.491d90f5.1f0d58.eb05@mx.google.com>


================
@@ -0,0 +1,243 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
----------------
ldionne wrote:

You can provide overloads of this function for `Foo` and `Bar` that do the same kind of check, but does them differently.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.faf16b37.1e0c59.488e@mx.google.com>


================
@@ -0,0 +1,243 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
----------------
ldionne wrote:

```suggestion
Foo original_expected; // make a copy including padding bits
std::memcpy(&original_expected, expected, sizeof(Foo));
```

Would that work too?

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.ff037943.3bf0e2.a070@mx.google.com>


================
@@ -0,0 +1,243 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
----------------
ldionne wrote:

Why is the padding `0` here? I think this should be a libc++ specific test, but also a comment would be useful.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.af6ba3ed.2c2f30.9adf@mx.google.com>


================
@@ -124,23 +146,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
----------------
ldionne wrote:

```suggestion
  return std::__atomic_cas_with_clear_padding(
```

Also below?

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.eea6ab6e.1256b6.69e0@mx.google.com>


================
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
----------------
ldionne wrote:

```suggestion
_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
```

I would suggest taking by value here. That makes it easier to understand since you're clearing the padding below (otherwise we have to know that the caller of this function already made a copy that they are about to discard, which requires non local reasoning). I suspect it doesn't make a difference in the codegen since they are small functions and are probably inlined anyway.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.65dfdb71.1ca534.5b4c@mx.google.com>


================
@@ -0,0 +1,243 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
+  while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
----------------
ldionne wrote:

Let's use your current approach but leave a `// TODO(LLVM-23): Switch to XFAIL: clang-22`. We prefer tracking test failures using Lit annotations for consistency, and because the test runner surfaces those explicitly.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.ff037943.3bf0e2.a072@mx.google.com>


================
@@ -0,0 +1,243 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
+  while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
----------------
ldionne wrote:

I would suggest that we instead assert that we succeeded within N attempts, with e.g. N = 100. That removes the "timing" aspect of this, which is very brittle given some of our platforms run on emulators, etc.

Technically this test is still flaky but I think it's difficult to do better.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.cb30c3e4.6f38b.1920@mx.google.com>


================
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
----------------
ldionne wrote:

I think I would rather leave this header outside of `__atomic/support`: `__atomic/support` is intended purely for the platform-specific API. Since this isn't that, I'd suggest e.g. `__atomic/clear_padding.h` or anything else that makes sense, just not nested under `/support`.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.27b3b003.1610ae.4216@mx.google.com>


================
@@ -30,11 +31,26 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
----------------
ldionne wrote:

This `#if` is going to hinge on the DR question.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.bf38ae28.358429.b599@mx.google.com>


================
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
----------------
ldionne wrote:

Open question: Should we implement this paper as a DR? In a certain way, this can be seen as a bug fix against the fact that `atomic<TypeWithPading>::compare_exhange` is broken before C++20. I'd be okay either way (DR or not), but as a non power user of atomics, CAS being broken for `atomic<TypeWithPadding>` is definitely not something that jumps at me as being obvious, hence the "bug report" categorization I am suggesting.

CC @frederick-vs-ja 

Also CC @jwakely and @StephanTLavavej : have you folks implemented this? If so, did you DR?

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.70920dee.c0a01.80f6@mx.google.com>


================
@@ -0,0 +1,243 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
----------------
ldionne wrote:

Your comment: you should probably add a test case where expected and the atomic are the same, including their padding.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4b.faf16b37.1e0c59.488f@mx.google.com>


================
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
----------------
ldionne wrote:

You can use `_LIBCPP_CONSTEXPR_SINCE_CXX14` for this.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:28:30 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 11:28:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a47ff4e.01f0a1da.2016e9.f5ee@mx.google.com>


================
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
----------------
ldionne wrote:

I would suggest taking a simple lvalue reference here, since this is totally an out-parameter.

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:41:34 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 11:41:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] cbb277a - [libc++abi][NFC] Avoid out
 parameter in dyn_cast_get_derived_info (#207326)
Message-ID: <6a48025e.f732ad81.1d8ca.579d@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-03T20:41:30+02:00
New Revision: cbb277a718bd9a8ee28df177f42313cda00cb3f6

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

LOG: [libc++abi][NFC] Avoid out parameter in dyn_cast_get_derived_info (#207326)

Returning the object is much more idiomatic than having an out
parameter.

Added: 
    

Modified: 
    libcxxabi/src/private_typeinfo.cpp

Removed: 
    


################################################################################
diff  --git a/libcxxabi/src/private_typeinfo.cpp b/libcxxabi/src/private_typeinfo.cpp
index d185f2618a7ea..ba3da519238b7 100644
--- a/libcxxabi/src/private_typeinfo.cpp
+++ b/libcxxabi/src/private_typeinfo.cpp
@@ -99,28 +99,27 @@ struct derived_object_info {
 };
 
 /// A helper function that gets (dynamic_ptr, dynamic_type, offset_to_derived) from static_ptr.
-void dyn_cast_get_derived_info(derived_object_info* info, const void* static_ptr)
-{
+derived_object_info dyn_cast_get_derived_info(const void* static_ptr) {
+  derived_object_info info;
 #if __has_feature(cxx_abi_relative_vtable)
-    // The vtable address will point to the first virtual function, which is 8
-    // bytes after the start of the vtable (4 for the offset from top + 4 for
-    // the typeinfo component).
-    const int32_t* vtable =
-        *reinterpret_cast<const int32_t* const*>(static_ptr);
-    info->offset_to_derived = static_cast<std::ptr
diff _t>(vtable[-2]);
-    info->dynamic_ptr = static_cast<const char*>(static_ptr) + info->offset_to_derived;
-
-    // The typeinfo component is now a relative offset to a proxy.
-    int32_t offset_to_ti_proxy = vtable[-1];
-    const uint8_t* ptr_to_ti_proxy =
-        reinterpret_cast<const uint8_t*>(vtable) + offset_to_ti_proxy;
-    info->dynamic_type = *(reinterpret_cast<const __class_type_info* const*>(ptr_to_ti_proxy));
+  // The vtable address will point to the first virtual function, which is 8
+  // bytes after the start of the vtable (4 for the offset from top + 4 for
+  // the typeinfo component).
+  const int32_t* vtable = *reinterpret_cast<const int32_t* const*>(static_ptr);
+  info.offset_to_derived = static_cast<std::ptr
diff _t>(vtable[-2]);
+  info.dynamic_ptr = static_cast<const char*>(static_ptr) + info.offset_to_derived;
+
+  // The typeinfo component is now a relative offset to a proxy.
+  int32_t offset_to_ti_proxy = vtable[-1];
+  const uint8_t* ptr_to_ti_proxy = reinterpret_cast<const uint8_t*>(vtable) + offset_to_ti_proxy;
+  info.dynamic_type = *(reinterpret_cast<const __class_type_info* const*>(ptr_to_ti_proxy));
 #else
   void** vtable = strip_vtable(*static_cast<void** const*>(static_ptr));
-  info->offset_to_derived = reinterpret_cast<ptr
diff _t>(vtable[-2]);
-  info->dynamic_ptr = static_cast<const char*>(static_ptr) + info->offset_to_derived;
-  info->dynamic_type = static_cast<const __class_type_info*>(vtable[-1]);
+  info.offset_to_derived = reinterpret_cast<ptr
diff _t>(vtable[-2]);
+  info.dynamic_ptr = static_cast<const char*>(static_ptr) + info.offset_to_derived;
+  info.dynamic_type = static_cast<const __class_type_info*>(vtable[-1]);
 #endif
+  return info;
 }
 
 /// A helper function for __dynamic_cast that casts a base sub-object pointer
@@ -915,8 +914,7 @@ __dynamic_cast(const void *static_ptr, const __class_type_info *static_type,
                const __class_type_info *dst_type,
                std::ptr
diff _t src2dst_offset) {
     // Get (dynamic_ptr, dynamic_type) from static_ptr
-    derived_object_info derived_info;
-    dyn_cast_get_derived_info(&derived_info, static_ptr);
+    derived_object_info derived_info = dyn_cast_get_derived_info(static_ptr);
 
     // Initialize answer to nullptr.  This will be changed from the search
     //    results if a non-null answer is found.  Regardless, this is what will


        

From libcxx-commits at lists.llvm.org  Fri Jul  3 11:41:37 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 03 Jul 2026 11:41:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Avoid out parameter
 in dyn_cast_get_derived_info (PR #207326)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207326@github.com>
Message-ID: <6a480261.491d90f5.1f0d58.1aff@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/207326

From libcxx-commits at lists.llvm.org  Fri Jul  3 12:02:26 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 03 Jul 2026 12:02:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Split the multidimensional
 algorithms out of uninitialized_algorithms.h (PR #207447)
Message-ID: <llvm.org/llvm/llvm-project/pull/207447@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207447

While these algorithms are conceptually related to other `uninitialized_*` algorithms, they're likely only ever going to be used by `shared_ptr`. `uninitialized_algorithms.h` itself is also getting rather large, so it makes sense to split the multidimensional versions into their own header.

Fixes #207417


>From a9408c940b1ba59bc8bc3adcefc6b758feac59b1 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 21:00:01 +0200
Subject: [PATCH] [libc++] Split the multidimensional algorithms out of
 uninitialized_algorithms.h

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__memory/shared_ptr.h          |   1 +
 .../__memory/uninitialized_algorithms.h       | 159 +-------------
 ...ninitialized_multidimensional_algorithms.h | 194 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   3 +
 5 files changed, 200 insertions(+), 158 deletions(-)
 create mode 100644 libcxx/include/__memory/uninitialized_multidimensional_algorithms.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index d65e66e221766..b40f586161e62 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -618,6 +618,7 @@ set(files
   __memory/temp_value.h
   __memory/temporary_buffer.h
   __memory/uninitialized_algorithms.h
+  __memory/uninitialized_multidimensional_algorithms.h
   __memory/unique_ptr.h
   __memory/unique_temporary_buffer.h
   __memory/uses_allocator.h
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 750d253912ab1..ebd542ba14d01 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -33,6 +33,7 @@
 #include <__memory/pointer_traits.h>
 #include <__memory/shared_count.h>
 #include <__memory/uninitialized_algorithms.h>
+#include <__memory/uninitialized_multidimensional_algorithms.h>
 #include <__memory/unique_ptr.h>
 #include <__type_traits/add_reference.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..d4cd49a1deb6e 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -11,11 +11,10 @@
 #define _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
 
 #include <__algorithm/copy.h>
-#include <__algorithm/move.h>
+#include <__algorithm/in_out_result.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__algorithm/unwrap_range.h>
 #include <__config>
-#include <__cstddef/size_t.h>
 #include <__fwd/memory.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/reverse_iterator.h>
@@ -25,15 +24,12 @@
 #include <__memory/destroy.h>
 #include <__memory/pointer_traits.h>
 #include <__type_traits/enable_if.h>
-#include <__type_traits/extent.h>
-#include <__type_traits/is_array.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/remove_const.h>
-#include <__type_traits/remove_extent.h>
 #include <__utility/exception_guard.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
@@ -295,159 +291,6 @@ uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofir
   return {std::move(__result.__in_), std::move(__result.__out_)};
 }
 
-// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
-// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
-// destruction. If elements are themselves C-style arrays, they are recursively destroyed
-// in the same manner.
-//
-// This function assumes that destructors do not throw, and that the allocator is bound to
-// the correct type.
-template <class _Alloc,
-          class _BidirIter,
-          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
-                "The allocator should already be rebound to the correct type");
-
-  if (__first == __last)
-    return;
-
-  if constexpr (is_array_v<_ValueType>) {
-    static_assert(!__is_unbounded_array_v<_ValueType>,
-                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
-
-    using _Element = remove_extent_t<_ValueType>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    do {
-      --__last;
-      decltype(auto) __array = *__last;
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
-    } while (__last != __first);
-  } else {
-    do {
-      --__last;
-      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
-    } while (__last != __first);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method.
-//
-// If the object being constructed is an array, each element of the array is allocator-constructed,
-// recursively. If an exception is thrown during the construction of an array, the initialized
-// elements are destroyed in reverse order of initialization using allocator destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method, passing along
-// the provided argument.
-//
-// If the object being constructed is an array, the argument is also assumed to be an array. Each
-// each element of the array being constructed is allocator-constructed from the corresponding
-// element of the argument array. If an exception is thrown during the construction of an array,
-// the initialized elements are destroyed in reverse order of initialization using allocator
-// destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp, class _Arg>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    static_assert(is_array_v<_Arg>,
-                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
-                  "trying to construct an array.");
-
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
-  }
-}
-
-// Given a range starting at it and containing n elements, initializes each element in the
-// range from left to right using the construct method of the allocator (rebound to the
-// correct type).
-//
-// If an exception is thrown, the initialized elements are destroyed in reverse order of
-// initialization using allocator_traits destruction. If the elements in the range are C-style
-// arrays, they are initialized element-wise using allocator construction, and recursively so.
-template <class _Alloc,
-          class _BidirIter,
-          class _Tp,
-          class _Size = typename iterator_traits<_BidirIter>::difference_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
-  }
-  __guard.__complete();
-}
-
-// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
-// to the allocator's construct method, which results in value initialization.
-template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
-  }
-  __guard.__complete();
-}
-
 #endif // _LIBCPP_STD_VER >= 17
 
 template <class _Alloc, class _Iter>
diff --git a/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
new file mode 100644
index 0000000000000..2496ce6af1e15
--- /dev/null
+++ b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
@@ -0,0 +1,194 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+#define _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_traits.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/extent.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/remove_extent.h>
+#include <__utility/exception_guard.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
+// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
+// destruction. If elements are themselves C-style arrays, they are recursively destroyed
+// in the same manner.
+//
+// This function assumes that destructors do not throw, and that the allocator is bound to
+// the correct type.
+template <class _Alloc,
+          class _BidirIter,
+          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
+                "The allocator should already be rebound to the correct type");
+
+  if (__first == __last)
+    return;
+
+  if constexpr (is_array_v<_ValueType>) {
+    static_assert(!__is_unbounded_array_v<_ValueType>,
+                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
+
+    using _Element = remove_extent_t<_ValueType>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    do {
+      --__last;
+      decltype(auto) __array = *__last;
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
+    } while (__last != __first);
+  } else {
+    do {
+      --__last;
+      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
+    } while (__last != __first);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method.
+//
+// If the object being constructed is an array, each element of the array is allocator-constructed,
+// recursively. If an exception is thrown during the construction of an array, the initialized
+// elements are destroyed in reverse order of initialization using allocator destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method, passing along
+// the provided argument.
+//
+// If the object being constructed is an array, the argument is also assumed to be an array. Each
+// each element of the array being constructed is allocator-constructed from the corresponding
+// element of the argument array. If an exception is thrown during the construction of an array,
+// the initialized elements are destroyed in reverse order of initialization using allocator
+// destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp, class _Arg>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    static_assert(is_array_v<_Arg>,
+                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
+                  "trying to construct an array.");
+
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
+  }
+}
+
+// Given a range starting at it and containing n elements, initializes each element in the
+// range from left to right using the construct method of the allocator (rebound to the
+// correct type).
+//
+// If an exception is thrown, the initialized elements are destroyed in reverse order of
+// initialization using allocator_traits destruction. If the elements in the range are C-style
+// arrays, they are initialized element-wise using allocator construction, and recursively so.
+template <class _Alloc,
+          class _BidirIter,
+          class _Tp,
+          class _Size = typename iterator_traits<_BidirIter>::difference_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
+  }
+  __guard.__complete();
+}
+
+// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
+// to the allocator's construct method, which results in value initialization.
+template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
+  }
+  __guard.__complete();
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3efceae54599d..fc987f7bab93d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1702,6 +1702,9 @@ module std {
       export std.memory.destroy
       export std.utility.pair
     }
+    module uninitialized_multidimensional_algorithms {
+      header "__memory/uninitialized_algorithms.h"
+    }
     module unique_ptr {
       header "__memory/unique_ptr.h"
       export std.functional.hash


From libcxx-commits at lists.llvm.org  Fri Jul  3 12:03:03 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 12:03:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Split the multidimensional
 algorithms out of uninitialized_algorithms.h (PR #207447)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207447@github.com>
Message-ID: <6a480767.f1c1923d.3672e2.16dc@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

While these algorithms are conceptually related to other `uninitialized_*` algorithms, they're likely only ever going to be used by `shared_ptr`. `uninitialized_algorithms.h` itself is also getting rather large, so it makes sense to split the multidimensional versions into their own header.

Fixes #<!-- -->207417


---
Full diff: https://github.com/llvm/llvm-project/pull/207447.diff


5 Files Affected:

- (modified) libcxx/include/CMakeLists.txt (+1) 
- (modified) libcxx/include/__memory/shared_ptr.h (+1) 
- (modified) libcxx/include/__memory/uninitialized_algorithms.h (+1-158) 
- (added) libcxx/include/__memory/uninitialized_multidimensional_algorithms.h (+194) 
- (modified) libcxx/include/module.modulemap.in (+3) 


``````````diff
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index d65e66e221766..b40f586161e62 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -618,6 +618,7 @@ set(files
   __memory/temp_value.h
   __memory/temporary_buffer.h
   __memory/uninitialized_algorithms.h
+  __memory/uninitialized_multidimensional_algorithms.h
   __memory/unique_ptr.h
   __memory/unique_temporary_buffer.h
   __memory/uses_allocator.h
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 750d253912ab1..ebd542ba14d01 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -33,6 +33,7 @@
 #include <__memory/pointer_traits.h>
 #include <__memory/shared_count.h>
 #include <__memory/uninitialized_algorithms.h>
+#include <__memory/uninitialized_multidimensional_algorithms.h>
 #include <__memory/unique_ptr.h>
 #include <__type_traits/add_reference.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..d4cd49a1deb6e 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -11,11 +11,10 @@
 #define _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
 
 #include <__algorithm/copy.h>
-#include <__algorithm/move.h>
+#include <__algorithm/in_out_result.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__algorithm/unwrap_range.h>
 #include <__config>
-#include <__cstddef/size_t.h>
 #include <__fwd/memory.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/reverse_iterator.h>
@@ -25,15 +24,12 @@
 #include <__memory/destroy.h>
 #include <__memory/pointer_traits.h>
 #include <__type_traits/enable_if.h>
-#include <__type_traits/extent.h>
-#include <__type_traits/is_array.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/remove_const.h>
-#include <__type_traits/remove_extent.h>
 #include <__utility/exception_guard.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
@@ -295,159 +291,6 @@ uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofir
   return {std::move(__result.__in_), std::move(__result.__out_)};
 }
 
-// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
-// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
-// destruction. If elements are themselves C-style arrays, they are recursively destroyed
-// in the same manner.
-//
-// This function assumes that destructors do not throw, and that the allocator is bound to
-// the correct type.
-template <class _Alloc,
-          class _BidirIter,
-          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
-                "The allocator should already be rebound to the correct type");
-
-  if (__first == __last)
-    return;
-
-  if constexpr (is_array_v<_ValueType>) {
-    static_assert(!__is_unbounded_array_v<_ValueType>,
-                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
-
-    using _Element = remove_extent_t<_ValueType>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    do {
-      --__last;
-      decltype(auto) __array = *__last;
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
-    } while (__last != __first);
-  } else {
-    do {
-      --__last;
-      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
-    } while (__last != __first);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method.
-//
-// If the object being constructed is an array, each element of the array is allocator-constructed,
-// recursively. If an exception is thrown during the construction of an array, the initialized
-// elements are destroyed in reverse order of initialization using allocator destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method, passing along
-// the provided argument.
-//
-// If the object being constructed is an array, the argument is also assumed to be an array. Each
-// each element of the array being constructed is allocator-constructed from the corresponding
-// element of the argument array. If an exception is thrown during the construction of an array,
-// the initialized elements are destroyed in reverse order of initialization using allocator
-// destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp, class _Arg>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    static_assert(is_array_v<_Arg>,
-                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
-                  "trying to construct an array.");
-
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
-  }
-}
-
-// Given a range starting at it and containing n elements, initializes each element in the
-// range from left to right using the construct method of the allocator (rebound to the
-// correct type).
-//
-// If an exception is thrown, the initialized elements are destroyed in reverse order of
-// initialization using allocator_traits destruction. If the elements in the range are C-style
-// arrays, they are initialized element-wise using allocator construction, and recursively so.
-template <class _Alloc,
-          class _BidirIter,
-          class _Tp,
-          class _Size = typename iterator_traits<_BidirIter>::difference_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
-  }
-  __guard.__complete();
-}
-
-// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
-// to the allocator's construct method, which results in value initialization.
-template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
-  }
-  __guard.__complete();
-}
-
 #endif // _LIBCPP_STD_VER >= 17
 
 template <class _Alloc, class _Iter>
diff --git a/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
new file mode 100644
index 0000000000000..2496ce6af1e15
--- /dev/null
+++ b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
@@ -0,0 +1,194 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+#define _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_traits.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/extent.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/remove_extent.h>
+#include <__utility/exception_guard.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
+// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
+// destruction. If elements are themselves C-style arrays, they are recursively destroyed
+// in the same manner.
+//
+// This function assumes that destructors do not throw, and that the allocator is bound to
+// the correct type.
+template <class _Alloc,
+          class _BidirIter,
+          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
+                "The allocator should already be rebound to the correct type");
+
+  if (__first == __last)
+    return;
+
+  if constexpr (is_array_v<_ValueType>) {
+    static_assert(!__is_unbounded_array_v<_ValueType>,
+                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
+
+    using _Element = remove_extent_t<_ValueType>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    do {
+      --__last;
+      decltype(auto) __array = *__last;
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
+    } while (__last != __first);
+  } else {
+    do {
+      --__last;
+      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
+    } while (__last != __first);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method.
+//
+// If the object being constructed is an array, each element of the array is allocator-constructed,
+// recursively. If an exception is thrown during the construction of an array, the initialized
+// elements are destroyed in reverse order of initialization using allocator destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method, passing along
+// the provided argument.
+//
+// If the object being constructed is an array, the argument is also assumed to be an array. Each
+// each element of the array being constructed is allocator-constructed from the corresponding
+// element of the argument array. If an exception is thrown during the construction of an array,
+// the initialized elements are destroyed in reverse order of initialization using allocator
+// destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp, class _Arg>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    static_assert(is_array_v<_Arg>,
+                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
+                  "trying to construct an array.");
+
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
+  }
+}
+
+// Given a range starting at it and containing n elements, initializes each element in the
+// range from left to right using the construct method of the allocator (rebound to the
+// correct type).
+//
+// If an exception is thrown, the initialized elements are destroyed in reverse order of
+// initialization using allocator_traits destruction. If the elements in the range are C-style
+// arrays, they are initialized element-wise using allocator construction, and recursively so.
+template <class _Alloc,
+          class _BidirIter,
+          class _Tp,
+          class _Size = typename iterator_traits<_BidirIter>::difference_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
+  }
+  __guard.__complete();
+}
+
+// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
+// to the allocator's construct method, which results in value initialization.
+template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
+  }
+  __guard.__complete();
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3efceae54599d..fc987f7bab93d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1702,6 +1702,9 @@ module std {
       export std.memory.destroy
       export std.utility.pair
     }
+    module uninitialized_multidimensional_algorithms {
+      header "__memory/uninitialized_algorithms.h"
+    }
     module unique_ptr {
       header "__memory/unique_ptr.h"
       export std.functional.hash

``````````

</details>


https://github.com/llvm/llvm-project/pull/207447

From libcxx-commits at lists.llvm.org  Fri Jul  3 12:28:26 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 12:28:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix SPEC benchmarks not
 producing a .lnt result file (PR #207450)
Message-ID: <llvm.org/llvm/llvm-project/pull/207450@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/207450

The refactoring in 471e8f7f94e7 removed the output of a .lnt file, which is necessary for interoperation with consolidate-benchmarks.

>From f1e39493342f2ce2030071538e966186babe8dac Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 3 Jul 2026 15:04:10 -0400
Subject: [PATCH] [libc++] Fix SPEC benchmarks not producing a .lnt result file

The refactoring in 471e8f7f94e7 removed the output of a .lnt file, which
is necessary for interoperation with consolidate-benchmarks.
---
 libcxx/test/benchmarks/spec.gen.py |  2 +-
 libcxx/utils/run-spec-benchmark    | 18 ++++++++++++++----
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/benchmarks/spec.gen.py b/libcxx/test/benchmarks/spec.gen.py
index 13e374adf1587..0eeb2192d47f0 100644
--- a/libcxx/test/benchmarks/spec.gen.py
+++ b/libcxx/test/benchmarks/spec.gen.py
@@ -34,4 +34,4 @@
 
 for benchmark in spec_benchmarks:
     print(f'#--- {benchmark}.sh.test')
-    print(f'RUN: %{{python}} %{{libcxx-dir}}/utils/run-spec-benchmark --spec-dir %{{spec_dir}} --temp-dir %{{temp}} --benchmark {benchmark} --clean -- %{{cxx}} %{{compile_flags}} %{{flags}} %{{link_flags}}')
+    print(f'RUN: %{{python}} %{{libcxx-dir}}/utils/run-spec-benchmark --spec-dir %{{spec_dir}} --temp-dir %{{temp}} --benchmark {benchmark} --output %{{temp}}/results.lnt --clean -- %{{cxx}} %{{compile_flags}} %{{flags}} %{{link_flags}}')
diff --git a/libcxx/utils/run-spec-benchmark b/libcxx/utils/run-spec-benchmark
index 7a4fb565d34ef..c9b91598ce3c6 100755
--- a/libcxx/utils/run-spec-benchmark
+++ b/libcxx/utils/run-spec-benchmark
@@ -42,6 +42,12 @@ def main():
         action="store_true",
         help="Clean up build artifacts after running. Useful to save disk space when running multiple benchmarks.",
     )
+    parser.add_argument(
+        "--output",
+        type=str,
+        default=None,
+        help="Path to write the LNT-format results to. Defaults to stdout.",
+    )
     parser.add_argument(
         "compiler_and_flags",
         nargs="+",
@@ -122,16 +128,20 @@ default:
             print(f"SPEC benchmark {args.benchmark} had a compilation or runtime error.", file=sys.stderr)
             sys.exit(1)
 
-    # Parse results into LNT format and print to stdout.
+    # Parse results into LNT format. Write them to the requested output file (or stdout by default).
+    # We only get here after the build and run succeeded, so a failed benchmark produces no output file.
     benchmark_name = args.benchmark.replace(".", "_")
     spec_results = subprocess.run([str(utils_dir / "parse-spec-results"), "--output-format=lnt"] + csv_files,
                                    capture_output=True, text=True, check=True)
-    sys.stdout.write(spec_results.stdout)
-
     time_results = subprocess.run([str(utils_dir / "parse-time-output"), str(time_output), f"--benchmark={benchmark_name}",
                                     "--extract", "instructions", "max_rss", "cycles", "peak_memory"],
                                     capture_output=True, text=True, check=True)
-    sys.stdout.write(time_results.stdout)
+
+    lnt_results = spec_results.stdout + time_results.stdout
+    if args.output:
+        pathlib.Path(args.output).write_text(lnt_results)
+    else:
+        sys.stdout.write(lnt_results)
 
     # Clean up build artifacts since they can be very large.
     if args.clean:


From libcxx-commits at lists.llvm.org  Fri Jul  3 12:29:04 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 12:29:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix SPEC benchmarks not
 producing a .lnt result file (PR #207450)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207450@github.com>
Message-ID: <6a480d80.27b3b003.1610ae.2452@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

The refactoring in 471e8f7f94e7 removed the output of a .lnt file, which is necessary for interoperation with consolidate-benchmarks.

---
Full diff: https://github.com/llvm/llvm-project/pull/207450.diff


2 Files Affected:

- (modified) libcxx/test/benchmarks/spec.gen.py (+1-1) 
- (modified) libcxx/utils/run-spec-benchmark (+14-4) 


``````````diff
diff --git a/libcxx/test/benchmarks/spec.gen.py b/libcxx/test/benchmarks/spec.gen.py
index 13e374adf1587..0eeb2192d47f0 100644
--- a/libcxx/test/benchmarks/spec.gen.py
+++ b/libcxx/test/benchmarks/spec.gen.py
@@ -34,4 +34,4 @@
 
 for benchmark in spec_benchmarks:
     print(f'#--- {benchmark}.sh.test')
-    print(f'RUN: %{{python}} %{{libcxx-dir}}/utils/run-spec-benchmark --spec-dir %{{spec_dir}} --temp-dir %{{temp}} --benchmark {benchmark} --clean -- %{{cxx}} %{{compile_flags}} %{{flags}} %{{link_flags}}')
+    print(f'RUN: %{{python}} %{{libcxx-dir}}/utils/run-spec-benchmark --spec-dir %{{spec_dir}} --temp-dir %{{temp}} --benchmark {benchmark} --output %{{temp}}/results.lnt --clean -- %{{cxx}} %{{compile_flags}} %{{flags}} %{{link_flags}}')
diff --git a/libcxx/utils/run-spec-benchmark b/libcxx/utils/run-spec-benchmark
index 7a4fb565d34ef..c9b91598ce3c6 100755
--- a/libcxx/utils/run-spec-benchmark
+++ b/libcxx/utils/run-spec-benchmark
@@ -42,6 +42,12 @@ def main():
         action="store_true",
         help="Clean up build artifacts after running. Useful to save disk space when running multiple benchmarks.",
     )
+    parser.add_argument(
+        "--output",
+        type=str,
+        default=None,
+        help="Path to write the LNT-format results to. Defaults to stdout.",
+    )
     parser.add_argument(
         "compiler_and_flags",
         nargs="+",
@@ -122,16 +128,20 @@ default:
             print(f"SPEC benchmark {args.benchmark} had a compilation or runtime error.", file=sys.stderr)
             sys.exit(1)
 
-    # Parse results into LNT format and print to stdout.
+    # Parse results into LNT format. Write them to the requested output file (or stdout by default).
+    # We only get here after the build and run succeeded, so a failed benchmark produces no output file.
     benchmark_name = args.benchmark.replace(".", "_")
     spec_results = subprocess.run([str(utils_dir / "parse-spec-results"), "--output-format=lnt"] + csv_files,
                                    capture_output=True, text=True, check=True)
-    sys.stdout.write(spec_results.stdout)
-
     time_results = subprocess.run([str(utils_dir / "parse-time-output"), str(time_output), f"--benchmark={benchmark_name}",
                                     "--extract", "instructions", "max_rss", "cycles", "peak_memory"],
                                     capture_output=True, text=True, check=True)
-    sys.stdout.write(time_results.stdout)
+
+    lnt_results = spec_results.stdout + time_results.stdout
+    if args.output:
+        pathlib.Path(args.output).write_text(lnt_results)
+    else:
+        sys.stdout.write(lnt_results)
 
     # Clean up build artifacts since they can be very large.
     if args.clean:

``````````

</details>


https://github.com/llvm/llvm-project/pull/207450

From libcxx-commits at lists.llvm.org  Fri Jul  3 12:29:59 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 12:29:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix SPEC benchmarks not
 producing a .lnt result file (PR #207450)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207450@github.com>
Message-ID: <6a480db7.331483e7.5bc1e.8daa@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {darker}-->


:warning: Python code formatter, darker found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
darker --check --diff -r origin/main...HEAD libcxx/test/benchmarks/spec.gen.py
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from darker here.
</summary>

``````````diff
--- spec.gen.py	2026-07-03 19:25:02.000000 +0000
+++ spec.gen.py	2026-07-03 19:29:27.585492 +0000
@@ -31,7 +31,9 @@
 with open(spec_dir / 'benchspec' / 'CPU' / 'no_fortran.bset', 'r') as f:
     no_fortran.update(json.load(f)['benchmarks'])
 spec_benchmarks &= no_fortran
 
 for benchmark in spec_benchmarks:
-    print(f'#--- {benchmark}.sh.test')
-    print(f'RUN: %{{python}} %{{libcxx-dir}}/utils/run-spec-benchmark --spec-dir %{{spec_dir}} --temp-dir %{{temp}} --benchmark {benchmark} --output %{{temp}}/results.lnt --clean -- %{{cxx}} %{{compile_flags}} %{{flags}} %{{link_flags}}')
+    print(f"#--- {benchmark}.sh.test")
+    print(
+        f"RUN: %{{python}} %{{libcxx-dir}}/utils/run-spec-benchmark --spec-dir %{{spec_dir}} --temp-dir %{{temp}} --benchmark {benchmark} --output %{{temp}}/results.lnt --clean -- %{{cxx}} %{{compile_flags}} %{{flags}} %{{link_flags}}"
+    )

``````````

</details>


https://github.com/llvm/llvm-project/pull/207450

From libcxx-commits at lists.llvm.org  Fri Jul  3 12:32:06 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 12:32:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix SPEC benchmarks not
 producing a .lnt result file (PR #207450)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207450@github.com>
Message-ID: <6a480e36.cb30c3e4.6f38b.6eff@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207450

>From f1e39493342f2ce2030071538e966186babe8dac Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 3 Jul 2026 15:04:10 -0400
Subject: [PATCH 1/2] [libc++] Fix SPEC benchmarks not producing a .lnt result
 file

The refactoring in 471e8f7f94e7 removed the output of a .lnt file, which
is necessary for interoperation with consolidate-benchmarks.
---
 libcxx/test/benchmarks/spec.gen.py |  2 +-
 libcxx/utils/run-spec-benchmark    | 18 ++++++++++++++----
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/benchmarks/spec.gen.py b/libcxx/test/benchmarks/spec.gen.py
index 13e374adf1587..0eeb2192d47f0 100644
--- a/libcxx/test/benchmarks/spec.gen.py
+++ b/libcxx/test/benchmarks/spec.gen.py
@@ -34,4 +34,4 @@
 
 for benchmark in spec_benchmarks:
     print(f'#--- {benchmark}.sh.test')
-    print(f'RUN: %{{python}} %{{libcxx-dir}}/utils/run-spec-benchmark --spec-dir %{{spec_dir}} --temp-dir %{{temp}} --benchmark {benchmark} --clean -- %{{cxx}} %{{compile_flags}} %{{flags}} %{{link_flags}}')
+    print(f'RUN: %{{python}} %{{libcxx-dir}}/utils/run-spec-benchmark --spec-dir %{{spec_dir}} --temp-dir %{{temp}} --benchmark {benchmark} --output %{{temp}}/results.lnt --clean -- %{{cxx}} %{{compile_flags}} %{{flags}} %{{link_flags}}')
diff --git a/libcxx/utils/run-spec-benchmark b/libcxx/utils/run-spec-benchmark
index 7a4fb565d34ef..c9b91598ce3c6 100755
--- a/libcxx/utils/run-spec-benchmark
+++ b/libcxx/utils/run-spec-benchmark
@@ -42,6 +42,12 @@ def main():
         action="store_true",
         help="Clean up build artifacts after running. Useful to save disk space when running multiple benchmarks.",
     )
+    parser.add_argument(
+        "--output",
+        type=str,
+        default=None,
+        help="Path to write the LNT-format results to. Defaults to stdout.",
+    )
     parser.add_argument(
         "compiler_and_flags",
         nargs="+",
@@ -122,16 +128,20 @@ default:
             print(f"SPEC benchmark {args.benchmark} had a compilation or runtime error.", file=sys.stderr)
             sys.exit(1)
 
-    # Parse results into LNT format and print to stdout.
+    # Parse results into LNT format. Write them to the requested output file (or stdout by default).
+    # We only get here after the build and run succeeded, so a failed benchmark produces no output file.
     benchmark_name = args.benchmark.replace(".", "_")
     spec_results = subprocess.run([str(utils_dir / "parse-spec-results"), "--output-format=lnt"] + csv_files,
                                    capture_output=True, text=True, check=True)
-    sys.stdout.write(spec_results.stdout)
-
     time_results = subprocess.run([str(utils_dir / "parse-time-output"), str(time_output), f"--benchmark={benchmark_name}",
                                     "--extract", "instructions", "max_rss", "cycles", "peak_memory"],
                                     capture_output=True, text=True, check=True)
-    sys.stdout.write(time_results.stdout)
+
+    lnt_results = spec_results.stdout + time_results.stdout
+    if args.output:
+        pathlib.Path(args.output).write_text(lnt_results)
+    else:
+        sys.stdout.write(lnt_results)
 
     # Clean up build artifacts since they can be very large.
     if args.clean:

>From acfe8e1fe25407cf2b93af8f863d6a5458e22b1c Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 3 Jul 2026 15:31:24 -0400
Subject: [PATCH 2/2] Less brittle against missing trailing newlines

---
 libcxx/utils/run-spec-benchmark | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/utils/run-spec-benchmark b/libcxx/utils/run-spec-benchmark
index c9b91598ce3c6..06f17ef02cf12 100755
--- a/libcxx/utils/run-spec-benchmark
+++ b/libcxx/utils/run-spec-benchmark
@@ -137,7 +137,8 @@ default:
                                     "--extract", "instructions", "max_rss", "cycles", "peak_memory"],
                                     capture_output=True, text=True, check=True)
 
-    lnt_results = spec_results.stdout + time_results.stdout
+    lines = spec_results.stdout.splitlines() + time_results.stdout.splitlines()
+    lnt_results = "".join(f"{line}\n" for line in lines)
     if args.output:
         pathlib.Path(args.output).write_text(lnt_results)
     else:


From libcxx-commits at lists.llvm.org  Fri Jul  3 12:33:40 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 12:33:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] e96a42d - [libc++] Fix SPEC benchmarks
 not producing a .lnt result file (#207450)
Message-ID: <6a480e94.6c8668f2.346e76.ec2b@mx.google.com>


Author: Louis Dionne
Date: 2026-07-03T15:33:35-04:00
New Revision: e96a42daca51802041eb795aa7da95e273885c52

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

LOG: [libc++] Fix SPEC benchmarks not producing a .lnt result file (#207450)

The refactoring in 471e8f7f94e7 removed the output of a .lnt file, which
is necessary for interoperation with consolidate-benchmarks.

Added: 
    

Modified: 
    libcxx/test/benchmarks/spec.gen.py
    libcxx/utils/run-spec-benchmark

Removed: 
    


################################################################################
diff  --git a/libcxx/test/benchmarks/spec.gen.py b/libcxx/test/benchmarks/spec.gen.py
index 13e374adf1587..0eeb2192d47f0 100644
--- a/libcxx/test/benchmarks/spec.gen.py
+++ b/libcxx/test/benchmarks/spec.gen.py
@@ -34,4 +34,4 @@
 
 for benchmark in spec_benchmarks:
     print(f'#--- {benchmark}.sh.test')
-    print(f'RUN: %{{python}} %{{libcxx-dir}}/utils/run-spec-benchmark --spec-dir %{{spec_dir}} --temp-dir %{{temp}} --benchmark {benchmark} --clean -- %{{cxx}} %{{compile_flags}} %{{flags}} %{{link_flags}}')
+    print(f'RUN: %{{python}} %{{libcxx-dir}}/utils/run-spec-benchmark --spec-dir %{{spec_dir}} --temp-dir %{{temp}} --benchmark {benchmark} --output %{{temp}}/results.lnt --clean -- %{{cxx}} %{{compile_flags}} %{{flags}} %{{link_flags}}')

diff  --git a/libcxx/utils/run-spec-benchmark b/libcxx/utils/run-spec-benchmark
index 7a4fb565d34ef..06f17ef02cf12 100755
--- a/libcxx/utils/run-spec-benchmark
+++ b/libcxx/utils/run-spec-benchmark
@@ -42,6 +42,12 @@ def main():
         action="store_true",
         help="Clean up build artifacts after running. Useful to save disk space when running multiple benchmarks.",
     )
+    parser.add_argument(
+        "--output",
+        type=str,
+        default=None,
+        help="Path to write the LNT-format results to. Defaults to stdout.",
+    )
     parser.add_argument(
         "compiler_and_flags",
         nargs="+",
@@ -122,16 +128,21 @@ default:
             print(f"SPEC benchmark {args.benchmark} had a compilation or runtime error.", file=sys.stderr)
             sys.exit(1)
 
-    # Parse results into LNT format and print to stdout.
+    # Parse results into LNT format. Write them to the requested output file (or stdout by default).
+    # We only get here after the build and run succeeded, so a failed benchmark produces no output file.
     benchmark_name = args.benchmark.replace(".", "_")
     spec_results = subprocess.run([str(utils_dir / "parse-spec-results"), "--output-format=lnt"] + csv_files,
                                    capture_output=True, text=True, check=True)
-    sys.stdout.write(spec_results.stdout)
-
     time_results = subprocess.run([str(utils_dir / "parse-time-output"), str(time_output), f"--benchmark={benchmark_name}",
                                     "--extract", "instructions", "max_rss", "cycles", "peak_memory"],
                                     capture_output=True, text=True, check=True)
-    sys.stdout.write(time_results.stdout)
+
+    lines = spec_results.stdout.splitlines() + time_results.stdout.splitlines()
+    lnt_results = "".join(f"{line}\n" for line in lines)
+    if args.output:
+        pathlib.Path(args.output).write_text(lnt_results)
+    else:
+        sys.stdout.write(lnt_results)
 
     # Clean up build artifacts since they can be very large.
     if args.clean:


        

From libcxx-commits at lists.llvm.org  Fri Jul  3 12:33:43 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 12:33:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix SPEC benchmarks not
 producing a .lnt result file (PR #207450)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207450@github.com>
Message-ID: <6a480e97.e9d484b9.11ad58.29b4@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/207450

From libcxx-commits at lists.llvm.org  Fri Jul  3 12:34:56 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 12:34:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make atomic contention tables
	constinit (PR #207456)
Message-ID: <llvm.org/llvm/llvm-project/pull/207456@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/207456

None

>From 663e261ad6b059b9d98b66063aab3e370c4d26f8 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 3 Jul 2026 12:03:26 -0400
Subject: [PATCH] [libc++] Make atomic contention tables constinit

---
 libcxx/src/atomic.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index ae79ac7f87965..53aec09dfc2a0 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -422,7 +422,7 @@ struct alignas(
   constexpr __contention_state_native() : __waiter_count(0) {}
 };
 
-static __contention_state_native __contention_table_native[__contention_table_size];
+static constinit __contention_state_native __contention_table_native[__contention_table_size];
 
 static __cxx_atomic_contention_t* __get_native_waiter_count(void const* p) {
   return &__contention_table_native[__contention_hasher(p) & (__contention_table_size - 1)].__waiter_count;
@@ -438,7 +438,7 @@ struct alignas(
   constexpr __contention_state_global() : __waiter_count(0), __platform_state(0) {}
 };
 
-static __contention_state_global __contention_table_global[__contention_table_size];
+static constinit __contention_state_global __contention_table_global[__contention_table_size];
 
 static __contention_state_global* __get_global_contention_state(void const* p) {
   return &__contention_table_global[__contention_hasher(p) & (__contention_table_size - 1)];


From libcxx-commits at lists.llvm.org  Fri Jul  3 12:35:36 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 12:35:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make atomic contention tables
	constinit (PR #207456)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207456@github.com>
Message-ID: <6a480f08.481afadf.134314.6403@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/207456.diff


1 Files Affected:

- (modified) libcxx/src/atomic.cpp (+2-2) 


``````````diff
diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index ae79ac7f87965..53aec09dfc2a0 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -422,7 +422,7 @@ struct alignas(
   constexpr __contention_state_native() : __waiter_count(0) {}
 };
 
-static __contention_state_native __contention_table_native[__contention_table_size];
+static constinit __contention_state_native __contention_table_native[__contention_table_size];
 
 static __cxx_atomic_contention_t* __get_native_waiter_count(void const* p) {
   return &__contention_table_native[__contention_hasher(p) & (__contention_table_size - 1)].__waiter_count;
@@ -438,7 +438,7 @@ struct alignas(
   constexpr __contention_state_global() : __waiter_count(0), __platform_state(0) {}
 };
 
-static __contention_state_global __contention_table_global[__contention_table_size];
+static constinit __contention_state_global __contention_table_global[__contention_table_size];
 
 static __contention_state_global* __get_global_contention_state(void const* p) {
   return &__contention_table_global[__contention_hasher(p) & (__contention_table_size - 1)];

``````````

</details>


https://github.com/llvm/llvm-project/pull/207456

From libcxx-commits at lists.llvm.org  Fri Jul  3 12:37:42 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 12:37:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement any_of in terms of
	find_if (PR #207274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207274@github.com>
Message-ID: <6a480f86.bf38ae28.358429.e877@mx.google.com>


https://github.com/ldionne converted_to_draft https://github.com/llvm/llvm-project/pull/207274

From libcxx-commits at lists.llvm.org  Fri Jul  3 12:40:18 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 12:40:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement any_of in terms of
	find_if (PR #207274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207274@github.com>
Message-ID: <6a481022.4a342cbc.134cb.fe58@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207274

>From 47d00f9d1665b51b2d0728ed6faecd5e7f75750d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 16:39:23 -0400
Subject: [PATCH 1/4] [libc++] Implement any_of in terms of find_if

This way, any optimizations in find_if will be picked up by any_of.

Closes #129310
---
 libcxx/include/__algorithm/any_of.h | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__algorithm/any_of.h b/libcxx/include/__algorithm/any_of.h
index 4b6eb94517286..fed39c8e4dc7c 100644
--- a/libcxx/include/__algorithm/any_of.h
+++ b/libcxx/include/__algorithm/any_of.h
@@ -10,9 +10,11 @@
 #ifndef _LIBCPP___ALGORITHM_ANY_OF_H
 #define _LIBCPP___ALGORITHM_ANY_OF_H
 
+#include <__algorithm/find_if.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__type_traits/invoke.h>
+#include <__utility/forward.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -23,18 +25,15 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Iter, class _Sent, class _Proj, class _Pred>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
 __any_of(_Iter __first, _Sent __last, _Pred& __pred, _Proj& __proj) {
-  for (; __first != __last; ++__first) {
-    if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
-      return true;
-  }
-  return false;
+  auto __found = std::__find_if(std::move(__first), __last, std::forward<_Pred>(__pred), std::forward<_Proj>(__proj));
+  return __found != __last;
 }
 
 template <class _InputIterator, class _Predicate>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
 any_of(_InputIterator __first, _InputIterator __last, _Predicate __pred) {
   __identity __proj;
-  return std::__any_of(__first, __last, __pred, __proj);
+  return std::__any_of(std::move(__first), std::move(__last), __pred, __proj);
 }
 
 _LIBCPP_END_NAMESPACE_STD

>From 2664ffa9c364847bbc04f398302f7144a2e2dee0 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 16:43:29 -0400
Subject: [PATCH 2/4] Clean up include

---
 libcxx/include/__algorithm/any_of.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__algorithm/any_of.h b/libcxx/include/__algorithm/any_of.h
index fed39c8e4dc7c..94a0917899d63 100644
--- a/libcxx/include/__algorithm/any_of.h
+++ b/libcxx/include/__algorithm/any_of.h
@@ -13,7 +13,6 @@
 #include <__algorithm/find_if.h>
 #include <__config>
 #include <__functional/identity.h>
-#include <__type_traits/invoke.h>
 #include <__utility/forward.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)

>From 70e38949da0c4bb9df744d8c47b2208cadd39b23 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 3 Jul 2026 15:39:34 -0400
Subject: [PATCH 3/4] push/pop

---
 libcxx/include/__algorithm/any_of.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/include/__algorithm/any_of.h b/libcxx/include/__algorithm/any_of.h
index 94a0917899d63..abf729c2086ab 100644
--- a/libcxx/include/__algorithm/any_of.h
+++ b/libcxx/include/__algorithm/any_of.h
@@ -19,6 +19,9 @@
 #  pragma GCC system_header
 #endif
 
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Iter, class _Sent, class _Proj, class _Pred>
@@ -37,4 +40,6 @@ any_of(_InputIterator __first, _InputIterator __last, _Predicate __pred) {
 
 _LIBCPP_END_NAMESPACE_STD
 
+_LIBCPP_POP_MACROS
+
 #endif // _LIBCPP___ALGORITHM_ANY_OF_H

>From 9b0c4388b96e271aab114d5ea0550d7dc467afbc Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 3 Jul 2026 15:39:56 -0400
Subject: [PATCH 4/4] forwarding

---
 libcxx/include/__algorithm/any_of.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/any_of.h b/libcxx/include/__algorithm/any_of.h
index abf729c2086ab..5e1166c2803c3 100644
--- a/libcxx/include/__algorithm/any_of.h
+++ b/libcxx/include/__algorithm/any_of.h
@@ -27,7 +27,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Iter, class _Sent, class _Proj, class _Pred>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
 __any_of(_Iter __first, _Sent __last, _Pred& __pred, _Proj& __proj) {
-  auto __found = std::__find_if(std::move(__first), __last, std::forward<_Pred>(__pred), std::forward<_Proj>(__proj));
+  auto __found = std::__find_if(std::move(__first), __last, __pred, __proj);
   return __found != __last;
 }
 


From libcxx-commits at lists.llvm.org  Fri Jul  3 12:40:28 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 03 Jul 2026 12:40:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement any_of in terms of
	find_if (PR #207274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207274@github.com>
Message-ID: <6a48102c.fdc477d6.118e5e.2296@mx.google.com>


ldionne wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp

https://github.com/llvm/llvm-project/pull/207274

From libcxx-commits at lists.llvm.org  Fri Jul  3 12:44:44 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 03 Jul 2026 12:44:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make atomic contention tables
	constinit (PR #207456)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207456@github.com>
Message-ID: <6a48112c.ff037943.3bf0e2.8ed1@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/207456

From libcxx-commits at lists.llvm.org  Fri Jul  3 13:05:32 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 13:05:32 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_=5Blibcxxabi=5D=5BFree?=
 =?utf-8?q?BSD=5D_Re-enable_=60thread=5Flocal=5Fdestruction=5Forder=2Epass?=
 =?utf-8?b?LmNwcOKAjmAuIChQUiAjMTg2MTI5KQ==?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186129@github.com>
Message-ID: <6a48160c.af6ba3ed.2c2f30.7757@mx.google.com>


https://github.com/aokblast ready_for_review https://github.com/llvm/llvm-project/pull/186129

From libcxx-commits at lists.llvm.org  Fri Jul  3 13:05:49 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 13:05:49 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_=5Blibcxxabi=5D=5BFree?=
 =?utf-8?q?BSD=5D_Re-enable_=60thread=5Flocal=5Fdestruction=5Forder=2Epass?=
 =?utf-8?b?LmNwcOKAjmAuIChQUiAjMTg2MTI5KQ==?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186129@github.com>
Message-ID: <6a48161d.491d90f5.1f0d58.a874@mx.google.com>


aokblast wrote:

> I modified the PR title to clarify the meaning of "failed test". Please double-check the changes. Let's update this PR branch after merging #206269.

Thanks! Can we merge this now?

https://github.com/llvm/llvm-project/pull/186129

From libcxx-commits at lists.llvm.org  Fri Jul  3 13:06:14 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 13:06:14 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_=5Blibcxxabi=5D=5BFree?=
 =?utf-8?q?BSD=5D_Re-enable_=60thread=5Flocal=5Fdestruction=5Forder=2Epass?=
 =?utf-8?b?LmNwcOKAjmAuIChQUiAjMTg2MTI5KQ==?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186129@github.com>
Message-ID: <6a481636.eea6ab6e.1256b6.a4a4@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxxabi

Author: aokblast

<details>
<summary>Changes</summary>

Fixed in https://reviews.freebsd.org/D55826

---
Full diff: https://github.com/llvm/llvm-project/pull/186129.diff


1 Files Affected:

- (modified) libcxxabi/test/thread_local_destruction_order.pass.cpp (-2) 


``````````diff
diff --git a/libcxxabi/test/thread_local_destruction_order.pass.cpp b/libcxxabi/test/thread_local_destruction_order.pass.cpp
index c7e7717d6f04f..775b417832a1f 100644
--- a/libcxxabi/test/thread_local_destruction_order.pass.cpp
+++ b/libcxxabi/test/thread_local_destruction_order.pass.cpp
@@ -9,8 +9,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-threads
 
-// XFAIL: LIBCXX-FREEBSD-FIXME
-
 // TODO: This test does start working with newer updates of the mingw-w64
 // toolchain, when it includes the following commit:
 // https://github.com/mingw-w64/mingw-w64/commit/71eddccd746c56d9cde28bb5620d027d49259de9

``````````

</details>


https://github.com/llvm/llvm-project/pull/186129

From libcxx-commits at lists.llvm.org  Fri Jul  3 15:32:33 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Fri, 03 Jul 2026 15:32:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
Message-ID: <llvm.org/llvm/llvm-project/pull/207471@github.com>


https://github.com/smallp-o-p created https://github.com/llvm/llvm-project/pull/207471

- Add count test to verify the exact number of aliases our table contains
- Should reduce compile time cost of gathering that count


>From f1f732efd7ce293a06aea8d331858a686d24cdb6 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 3 Jul 2026 17:38:47 -0400
Subject: [PATCH] Inline text_encoding alias offset table std::count to 883

- Add count test to verify the exact number of aliases our table
  contains
---
 libcxx/include/text_encoding                  |  4 +-
 .../text/text_encoding/aliases_count.pass.cpp | 41 +++++++++++++++++++
 .../text_encoding.ctor/id.pass.cpp            |  2 +-
 .../text_encoding.ctor/string_view.pass.cpp   |  2 +-
 .../text_encoding.eq/equal.id.pass.cpp        |  2 +-
 .../text_encoding.aliases_view/empty.pass.cpp |  2 +-
 .../operator-bool.pass.cpp                    |  2 +-
 .../test_text_encoding.h                      |  1 +
 8 files changed, 49 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
 rename libcxx/test/{std/text/text_encoding => support}/test_text_encoding.h (99%)

diff --git a/libcxx/include/text_encoding b/libcxx/include/text_encoding
index da39be92c3dfa..d6063482be74d 100644
--- a/libcxx/include/text_encoding
+++ b/libcxx/include/text_encoding
@@ -792,8 +792,8 @@ private:
       "11\0CP50220\0csCP50220\0";
 
   struct __offset_table {
-    constexpr static unsigned long long __num_aliases =
-        std::count(__aliases_string, __aliases_string + sizeof(__aliases_string), '\0') + 1;
+    constexpr static unsigned long long __num_aliases = 884;
+    // +2 for id::unknown, id::other sentinels
     unsigned short __table[__num_aliases];
   };
 
diff --git a/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp b/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
new file mode 100644
index 0000000000000..365c9fe3b9ea3
--- /dev/null
+++ b/libcxx/test/libcxx/text/text_encoding/aliases_count.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
+//
+//===----------------------------------------------------------------------===//
+
+// <text_encoding>
+
+// REQUIRES: std-at-least-c++26
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=40000000
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=1000000000
+
+// Our table has 882 aliases exactly, test to make sure that number matches with the total alias count
+// Internally, there are 884 entries with 2 reserved for id::unknown and id::other
+#include <cassert>
+#include <ranges>
+#include <text_encoding>
+
+#include "test_text_encoding.h"
+
+constexpr bool test() {
+  long long sum = 0;
+  for (auto& enc : unique_encoding_data) {
+    std::text_encoding te{std::text_encoding::id(enc.mib)};
+
+    sum += std::ranges::size(te.aliases());
+  }
+
+  assert(sum == 882);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp
index bc3d729e770e3..12c85e6994252 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp
@@ -19,7 +19,7 @@
 #include <text_encoding>
 #include <type_traits>
 
-#include "../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp
index c3202f78642f2..032a4d3c08557 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp
@@ -19,7 +19,7 @@
 #include <text_encoding>
 #include <type_traits>
 
-#include "../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp
index 79de614299fa0..c50f539d1527a 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp
@@ -16,7 +16,7 @@
 #include <text_encoding>
 
 #include "test_macros.h"
-#include "../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp
index 367f9216ef5ff..3008e2d27927f 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp
@@ -16,7 +16,7 @@
 #include <ranges>
 #include <text_encoding>
 
-#include "../../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp
index 9d63ff4cdfccd..f122ee5c88e0b 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp
@@ -16,7 +16,7 @@
 #include <ranges>
 #include <text_encoding>
 
-#include "../../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/test_text_encoding.h b/libcxx/test/support/test_text_encoding.h
similarity index 99%
rename from libcxx/test/std/text/text_encoding/test_text_encoding.h
rename to libcxx/test/support/test_text_encoding.h
index e18bc3aaeea15..5cef8b8ab3d0a 100644
--- a/libcxx/test/std/text/text_encoding/test_text_encoding.h
+++ b/libcxx/test/support/test_text_encoding.h
@@ -295,6 +295,7 @@ constexpr inline enc_data all_encoding_data[] = {
     {3, "csASCII"},
     {3, "iso-ir-6"},
     {3, "us"},
+    {3, "ASCII"}, // extension to match libstdc++
     {4, "ISO-8859-1"},
     {4, "ISO_8859-1:1987"},
     {4, "CP819"},


From libcxx-commits at lists.llvm.org  Fri Jul  3 15:33:07 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 15:33:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a4838a3.f732ad81.1d8ca.1071@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: William Tran-Viet (smallp-o-p)

<details>
<summary>Changes</summary>

- Add count test to verify the exact number of aliases our table contains
- Should reduce compile time cost of gathering that count


---
Full diff: https://github.com/llvm/llvm-project/pull/207471.diff


8 Files Affected:

- (modified) libcxx/include/text_encoding (+2-2) 
- (added) libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp (+41) 
- (modified) libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp (+1-1) 
- (modified) libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp (+1-1) 
- (modified) libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp (+1-1) 
- (modified) libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp (+1-1) 
- (modified) libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp (+1-1) 
- (renamed) libcxx/test/support/test_text_encoding.h (+1) 


``````````diff
diff --git a/libcxx/include/text_encoding b/libcxx/include/text_encoding
index da39be92c3dfa..d6063482be74d 100644
--- a/libcxx/include/text_encoding
+++ b/libcxx/include/text_encoding
@@ -792,8 +792,8 @@ private:
       "11\0CP50220\0csCP50220\0";
 
   struct __offset_table {
-    constexpr static unsigned long long __num_aliases =
-        std::count(__aliases_string, __aliases_string + sizeof(__aliases_string), '\0') + 1;
+    constexpr static unsigned long long __num_aliases = 884;
+    // +2 for id::unknown, id::other sentinels
     unsigned short __table[__num_aliases];
   };
 
diff --git a/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp b/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
new file mode 100644
index 0000000000000..365c9fe3b9ea3
--- /dev/null
+++ b/libcxx/test/libcxx/text/text_encoding/aliases_count.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
+//
+//===----------------------------------------------------------------------===//
+
+// <text_encoding>
+
+// REQUIRES: std-at-least-c++26
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=40000000
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=1000000000
+
+// Our table has 882 aliases exactly, test to make sure that number matches with the total alias count
+// Internally, there are 884 entries with 2 reserved for id::unknown and id::other
+#include <cassert>
+#include <ranges>
+#include <text_encoding>
+
+#include "test_text_encoding.h"
+
+constexpr bool test() {
+  long long sum = 0;
+  for (auto& enc : unique_encoding_data) {
+    std::text_encoding te{std::text_encoding::id(enc.mib)};
+
+    sum += std::ranges::size(te.aliases());
+  }
+
+  assert(sum == 882);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp
index bc3d729e770e3..12c85e6994252 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp
@@ -19,7 +19,7 @@
 #include <text_encoding>
 #include <type_traits>
 
-#include "../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp
index c3202f78642f2..032a4d3c08557 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp
@@ -19,7 +19,7 @@
 #include <text_encoding>
 #include <type_traits>
 
-#include "../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp
index 79de614299fa0..c50f539d1527a 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp
@@ -16,7 +16,7 @@
 #include <text_encoding>
 
 #include "test_macros.h"
-#include "../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp
index 367f9216ef5ff..3008e2d27927f 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp
@@ -16,7 +16,7 @@
 #include <ranges>
 #include <text_encoding>
 
-#include "../../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp
index 9d63ff4cdfccd..f122ee5c88e0b 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp
@@ -16,7 +16,7 @@
 #include <ranges>
 #include <text_encoding>
 
-#include "../../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/test_text_encoding.h b/libcxx/test/support/test_text_encoding.h
similarity index 99%
rename from libcxx/test/std/text/text_encoding/test_text_encoding.h
rename to libcxx/test/support/test_text_encoding.h
index e18bc3aaeea15..5cef8b8ab3d0a 100644
--- a/libcxx/test/std/text/text_encoding/test_text_encoding.h
+++ b/libcxx/test/support/test_text_encoding.h
@@ -295,6 +295,7 @@ constexpr inline enc_data all_encoding_data[] = {
     {3, "csASCII"},
     {3, "iso-ir-6"},
     {3, "us"},
+    {3, "ASCII"}, // extension to match libstdc++
     {4, "ISO-8859-1"},
     {4, "ISO_8859-1:1987"},
     {4, "CP819"},

``````````

</details>


https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Fri Jul  3 15:33:24 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Fri, 03 Jul 2026 15:33:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a4838b4.f1c1923d.3672e2.4308@mx.google.com>


smallp-o-p wrote:

CC @philnik777 

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Fri Jul  3 22:47:50 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Fri, 03 Jul 2026 22:47:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3454 as resolved (PR
	#207487)
Message-ID: <llvm.org/llvm/llvm-project/pull/207487@github.com>


https://github.com/c8ef created https://github.com/llvm/llvm-project/pull/207487

Closes https://github.com/llvm/llvm-project/issues/171307.

LWG3454 has been implemented in https://github.com/llvm/llvm-project/commit/98d3d5b5da66e3cf7807c23a0294280bb796466b.

>From c1bb4994c8cc823a0dc98d0181a8eb8130fb9bb1 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 4 Jul 2026 13:45:59 +0800
Subject: [PATCH] [libc++] Mark LWG3454 as resolved

---
 libcxx/docs/Status/Cxx26Issues.csv                     |  2 +-
 .../pointer.traits.functions/pointer_to.pass.cpp       | 10 +++++++---
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 3f2a446f7e1f3..30042d4465d93 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -149,7 +149,7 @@
 "`LWG2991 <https://wg21.link/LWG2991>`__","``variant copy`` constructor missing ``noexcept(see below)``","2025-11 (Kona)","","","`#171301 <https://github.com/llvm/llvm-project/issues/171301>`__",""
 "`LWG3090 <https://wg21.link/LWG3090>`__","What is §[time.duration.cons]p4's ""no overflow is induced in the conversion"" intended to mean?","2025-11 (Kona)","","","`#171302 <https://github.com/llvm/llvm-project/issues/171302>`__",""
 "`LWG3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","2025-11 (Kona)","|Complete|","16","`#105356 <https://github.com/llvm/llvm-project/issues/105356>`__",""
-"`LWG3454 <https://wg21.link/LWG3454>`__","``pointer_traits::pointer_to`` should be ``constexpr``","2025-11 (Kona)","","","`#171307 <https://github.com/llvm/llvm-project/issues/171307>`__",""
+"`LWG3454 <https://wg21.link/LWG3454>`__","``pointer_traits::pointer_to`` should be ``constexpr``","2025-11 (Kona)","|Complete|","16","`#171307 <https://github.com/llvm/llvm-project/issues/171307>`__",""
 "`LWG3627 <https://wg21.link/LWG3627>`__","Inconsistent specifications for ``std::make_optional`` overloads","2025-11 (Kona)","|Complete|","22","`#171309 <https://github.com/llvm/llvm-project/issues/171309>`__",""
 "`LWG4015 <https://wg21.link/LWG4015>`__","LWG 3973 broke const overloads of ``std::optional`` monadic operations","2025-11 (Kona)","","","`#171310 <https://github.com/llvm/llvm-project/issues/171310>`__",""
 "`LWG4020 <https://wg21.link/LWG4020>`__","``extents::index-cast`` weirdness","2025-11 (Kona)","|Complete|","23","`#171311 <https://github.com/llvm/llvm-project/issues/171311>`__",""
diff --git a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
index e4c62f3d3df5c..bb6bdbd861e4d 100644
--- a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
+++ b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
@@ -30,24 +30,28 @@ struct A {
   typedef T element_type;
   element_type* t_;
 
-  A(element_type* t) : t_(t) {}
+  TEST_CONSTEXPR_CXX20 A(element_type* t) : t_(t) {}
 
-  static A pointer_to(typename std::conditional<std::is_void<element_type>::value, nat, element_type>::type& et) {
+  TEST_CONSTEXPR_CXX20 static A
+  pointer_to(typename std::conditional<std::is_void<element_type>::value, nat, element_type>::type& et) {
     return A(&et);
   }
 };
 
 template <class Pointer>
-void test() {
+TEST_CONSTEXPR_CXX20 bool test() {
   typename Pointer::element_type obj;
   static_assert(std::is_same<Pointer, decltype(std::pointer_traits<Pointer>::pointer_to(obj))>::value, "");
   Pointer p = std::pointer_traits<Pointer>::pointer_to(obj);
   assert(p.t_ == &obj);
+  return true;
 }
 
 int main(int, char**) {
   test<A<int> >();
   test<A<long> >();
+  static_assert(test<A<int> >());
+  static_assert(test<A<long> >());
   { (std::pointer_traits<A<void> >::element_type)0; }
 
   return 0;


From libcxx-commits at lists.llvm.org  Fri Jul  3 22:48:25 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 22:48:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3454 as resolved (PR
	#207487)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207487@github.com>
Message-ID: <6a489ea9.f732ad81.1d8ca.60b6@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Connector Switch (c8ef)

<details>
<summary>Changes</summary>

Closes https://github.com/llvm/llvm-project/issues/171307.

LWG3454 has been implemented in https://github.com/llvm/llvm-project/commit/98d3d5b5da66e3cf7807c23a0294280bb796466b.

---
Full diff: https://github.com/llvm/llvm-project/pull/207487.diff


2 Files Affected:

- (modified) libcxx/docs/Status/Cxx26Issues.csv (+1-1) 
- (modified) libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp (+7-3) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 3f2a446f7e1f3..30042d4465d93 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -149,7 +149,7 @@
 "`LWG2991 <https://wg21.link/LWG2991>`__","``variant copy`` constructor missing ``noexcept(see below)``","2025-11 (Kona)","","","`#171301 <https://github.com/llvm/llvm-project/issues/171301>`__",""
 "`LWG3090 <https://wg21.link/LWG3090>`__","What is §[time.duration.cons]p4's ""no overflow is induced in the conversion"" intended to mean?","2025-11 (Kona)","","","`#171302 <https://github.com/llvm/llvm-project/issues/171302>`__",""
 "`LWG3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","2025-11 (Kona)","|Complete|","16","`#105356 <https://github.com/llvm/llvm-project/issues/105356>`__",""
-"`LWG3454 <https://wg21.link/LWG3454>`__","``pointer_traits::pointer_to`` should be ``constexpr``","2025-11 (Kona)","","","`#171307 <https://github.com/llvm/llvm-project/issues/171307>`__",""
+"`LWG3454 <https://wg21.link/LWG3454>`__","``pointer_traits::pointer_to`` should be ``constexpr``","2025-11 (Kona)","|Complete|","16","`#171307 <https://github.com/llvm/llvm-project/issues/171307>`__",""
 "`LWG3627 <https://wg21.link/LWG3627>`__","Inconsistent specifications for ``std::make_optional`` overloads","2025-11 (Kona)","|Complete|","22","`#171309 <https://github.com/llvm/llvm-project/issues/171309>`__",""
 "`LWG4015 <https://wg21.link/LWG4015>`__","LWG 3973 broke const overloads of ``std::optional`` monadic operations","2025-11 (Kona)","","","`#171310 <https://github.com/llvm/llvm-project/issues/171310>`__",""
 "`LWG4020 <https://wg21.link/LWG4020>`__","``extents::index-cast`` weirdness","2025-11 (Kona)","|Complete|","23","`#171311 <https://github.com/llvm/llvm-project/issues/171311>`__",""
diff --git a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
index e4c62f3d3df5c..bb6bdbd861e4d 100644
--- a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
+++ b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
@@ -30,24 +30,28 @@ struct A {
   typedef T element_type;
   element_type* t_;
 
-  A(element_type* t) : t_(t) {}
+  TEST_CONSTEXPR_CXX20 A(element_type* t) : t_(t) {}
 
-  static A pointer_to(typename std::conditional<std::is_void<element_type>::value, nat, element_type>::type& et) {
+  TEST_CONSTEXPR_CXX20 static A
+  pointer_to(typename std::conditional<std::is_void<element_type>::value, nat, element_type>::type& et) {
     return A(&et);
   }
 };
 
 template <class Pointer>
-void test() {
+TEST_CONSTEXPR_CXX20 bool test() {
   typename Pointer::element_type obj;
   static_assert(std::is_same<Pointer, decltype(std::pointer_traits<Pointer>::pointer_to(obj))>::value, "");
   Pointer p = std::pointer_traits<Pointer>::pointer_to(obj);
   assert(p.t_ == &obj);
+  return true;
 }
 
 int main(int, char**) {
   test<A<int> >();
   test<A<long> >();
+  static_assert(test<A<int> >());
+  static_assert(test<A<long> >());
   { (std::pointer_traits<A<void> >::element_type)0; }
 
   return 0;

``````````

</details>


https://github.com/llvm/llvm-project/pull/207487

From libcxx-commits at lists.llvm.org  Fri Jul  3 23:35:42 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 03 Jul 2026 23:35:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][math][c++17] P0226R1 -
 Mathematical Special Functions: infra + `std::assoc_laguerre` (PR #205649)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205649@github.com>
Message-ID: <6a48a9be.cb30c3e4.6f38b.2f10@mx.google.com>


https://github.com/PaulXiCao updated https://github.com/llvm/llvm-project/pull/205649

>From 7555447d01b3e6bd5127f4ffbfaeeb223cf9ea81 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 20:52:48 +0200
Subject: [PATCH 01/52] link against Boost.Math

---
 libcxx/CMakeLists.txt     | 8 ++++++++
 libcxx/src/CMakeLists.txt | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..845042f15495f 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -899,6 +899,14 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+
+# Header-only Boost.Math (INTERFACE target `boost_math`). Lives outside the
+# libcxx source tree, so a binary dir must be supplied.
+add_subdirectory(
+  ${LLVM_THIRD_PARTY_DIR}/boost-math
+  ${LIBCXX_BINARY_DIR}/third-party/boost-math
+  EXCLUDE_FROM_ALL)
+
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index de7817ad69f26..c882446596a3e 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -176,6 +176,7 @@ add_library(cxx_shared SHARED ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(cxx_shared PUBLIC cxx-headers runtimes-libc-shared
                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                  PRIVATE boost_math
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_shared
   PROPERTIES
@@ -263,6 +264,7 @@ add_library(cxx_static STATIC ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(cxx_static PUBLIC cxx-headers runtimes-libc-static
                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                  PRIVATE boost_math
                                   PRIVATE libcxx-abi-static
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_static

>From fe89c404516cfea93ab67939b48a15c74213cd59 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 20:53:37 +0200
Subject: [PATCH 02/52] add empty src/mathematical_special_functions.cpp

---
 libcxx/src/CMakeLists.txt                     |  1 +
 libcxx/src/mathematical_special_functions.cpp | 17 +++++++++++++++++
 2 files changed, 18 insertions(+)
 create mode 100644 libcxx/src/mathematical_special_functions.cpp

diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index c882446596a3e..d2168decbfdba 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -29,6 +29,7 @@ set(LIBCXX_SOURCES
   include/ryu/ryu.h
   include/to_chars_floating_point.h
   include/from_chars_floating_point.h
+  mathematical_special_functions.cpp
   memory.cpp
   memory_resource.cpp
   new_handler.cpp
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
new file mode 100644
index 0000000000000..db07d61f89319
--- /dev/null
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -0,0 +1,17 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+
+namespace {}
+
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+_LIBCPP_END_NAMESPACE_STD

>From 7fbc20f4bea5a80fdebfb22eadee50cb90698b46 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 21:06:04 +0200
Subject: [PATCH 03/52] add simple test for std::assoc_laguerref (fails atm)

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
new file mode 100644
index 0000000000000..1a12c8f81a16c
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Missing powl
+// XFAIL: LLVM-LIBC-FIXME
+
+// REQUIRES: std-at-least-c++17
+
+// <cmath>
+//
+// [sf.cmath.assoc.laguerre], associated Laguerre polynomials
+// floating-point-type assoc_laguerre(unsigned n, unsigned m, floating-point-type x);
+// float               assoc_laguerref(unsigned n, unsigned m, float x);
+// long double         assoc_laguerrel(unsigned n, unsigned m, long double x);
+
+#include <cmath>
+
+int main(int, char**) {
+  std::assoc_laguerref(0, 0, 0.0f);
+
+  return 0;
+}

>From c7e11e92b9c70fe1f319a8f959ce1c11cbbfa9cc Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 21:52:09 +0200
Subject: [PATCH 04/52] handling MATH_ERRNO, MATH_ERREXCEPT via __sf_result

---
 libcxx/include/__math/special_functions.h | 25 +++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 6b217852e2f8c..86b3ac9cb665e 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -15,7 +15,10 @@
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_integral.h>
+#include <cerrno>
+#include <cfenv>
 #include <limits>
+#include <math.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -25,6 +28,28 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 17
 
+namespace __math {
+template <class _Tp>
+struct __sf_result {
+  bool __domain_error;
+  _Tp __ret;
+
+  operator _Tp() const {
+#  if math_errhandling & MATH_ERRNO
+    if (__domain_error)
+      errno = EDOM;
+#  endif
+
+#  if math_errhandling & MATH_ERREXCEPT
+    if (__domain_error)
+      feraiseexcept(FE_INVALID);
+#  endif
+
+    return __ret;
+  }
+};
+} // namespace __math
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).

>From 23081681878b007cb50f1ed115ec316fa956b4cf Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 22:00:10 +0200
Subject: [PATCH 05/52] impl assoc_laguerref

---
 libcxx/include/__math/special_functions.h     |  9 +++++
 libcxx/src/mathematical_special_functions.cpp | 37 ++++++++++++++++++-
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 86b3ac9cb665e..d0d6e56507e85 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -25,6 +25,7 @@
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 #if _LIBCPP_STD_VER >= 17
 
@@ -48,8 +49,15 @@ struct __sf_result {
     return __ret;
   }
 };
+
+_LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
+
 } // namespace __math
 
+inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).
@@ -104,6 +112,7 @@ _LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, _Integer __x) {
 
 #endif // _LIBCPP_STD_VER >= 17
 
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index db07d61f89319..e18e9c5f2797c 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -7,11 +7,46 @@
 //===----------------------------------------------------------------------===//
 
 #include <__config>
+#include <boost/math/special_functions.hpp>
+#include <cmath>
+#include <optional>
+#include <type_traits>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-namespace {}
+namespace __math {
+namespace {
+template <class _Ret>
+optional<_Ret> __check_nan() {
+  return nullopt;
+}
+
+template <class _Ret, class _Arg, class... _Args>
+optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
+  if constexpr (is_floating_point_v<_Arg>)
+    if (isnan(__arg))
+      return __arg;
+  return __check_nan<_Ret>(__args...);
+}
+
+template <class Func, class... _Args, class _Ret = std::invoke_result_t<Func, _Args...>>
+__sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
+  if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
+    return {.__domain_error = false, .__ret = *__maybe_nan};
+
+  try {
+    return {.__domain_error = false, .__ret = f(__args...)};
+  } catch (...) {
+    return {.__domain_error = true, .__ret = numeric_limits<_Ret>::quiet_NaN()};
+  }
+}
+} // namespace
+
+__sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
+  return invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+} // namespace __math
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From adb174a0e9d29b172b17bc9e0d781db0dff15d5c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 22:34:12 +0200
Subject: [PATCH 06/52] test assoc_laguerref

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 1a12c8f81a16c..fe92daa501c6f 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -6,22 +6,18 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing powl
-// XFAIL: LLVM-LIBC-FIXME
-
 // REQUIRES: std-at-least-c++17
 
 // <cmath>
 //
 // [sf.cmath.assoc.laguerre], associated Laguerre polynomials
-// floating-point-type assoc_laguerre(unsigned n, unsigned m, floating-point-type x);
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
-// long double         assoc_laguerrel(unsigned n, unsigned m, long double x);
 
+#include <cassert>
 #include <cmath>
 
-int main(int, char**) {
-  std::assoc_laguerref(0, 0, 0.0f);
-
-  return 0;
+int main() {
+  // Single value tested against known solution.
+  // Note, underlying Boost.Math is itself well-tested.
+  assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
 }

>From b7bc1fb6247235ce62066d70a30c13608a8672fc Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 18:26:00 +0200
Subject: [PATCH 07/52] test: common.h

---
 .../std/numerics/c.math/sf.cmath/common.h     | 65 +++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/sf.cmath/common.h

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
new file mode 100644
index 0000000000000..867e6af6f6914
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_SF_CMATH_COMMON_H
+#define TEST_SF_CMATH_COMMON_H
+
+#include <cassert>
+#include <cerrno>
+#include <cmath>
+#include <math.h>
+#include <cfenv>
+
+// std::type_identity is C++20 (we need to support C++17 here)
+template <class T>
+struct type_identity {
+  typedef T type;
+};
+template <class T>
+using type_identity_t = typename type_identity<T>::type;
+
+template <class T>
+bool between(type_identity_t<T> lower, T value, type_identity_t<T> upper) {
+  return lower < value && value < upper;
+}
+
+template <class Func>
+void check_no_domain_error(Func f) {
+#if math_errhandling & MATH_ERRNO
+  errno = EACCES;
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  std::feclearexcept(FE_INVALID);
+#endif
+  f();
+#if math_errhandling & MATH_ERRNO
+  assert(errno == EACCES);
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  assert(!std::fetestexcept(FE_INVALID));
+#endif
+}
+
+template <class Func>
+void check_domain_error(Func f) {
+#if math_errhandling & MATH_ERRNO
+  errno = EACCES;
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  std::feclearexcept(FE_INVALID);
+#endif
+  f();
+#if math_errhandling & MATH_ERRNO
+  assert(errno == EDOM);
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  assert(std::fetestexcept(FE_INVALID));
+#endif
+}
+
+#endif // TEST_SF_CMATH_COMMON_H

>From a47f9e902e5c427c84115f4af119eedf8778e182 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 18:26:59 +0200
Subject: [PATCH 08/52] more testing of assoc_laguerref: values, types, domain
 error

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 36 +++++++++++++++++--
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index fe92daa501c6f..eb747efb4c1c6 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,10 +14,40 @@
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
 
 #include <cassert>
+#include <cerrno>
+#include <cfenv>
 #include <cmath>
 
+#include "common.h"
+#include "type_algorithms.h"
+
+struct TestFloatingPoint {
+  template <class T>
+  void operator()() const {
+    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
+
+    assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
+
+    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
+
+    assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
+
+    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
+
+    check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
+  }
+};
+
+struct TestIntegral {
+  template <class T>
+  void operator()() const {
+    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
+
+    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
+  }
+};
+
 int main() {
-  // Single value tested against known solution.
-  // Note, underlying Boost.Math is itself well-tested.
-  assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
+  types::for_each(types::floating_point_types{}, TestFloatingPoint{});
+  types::for_each(types::integral_types{}, TestIntegral{});
 }

>From f8a3b1d891405ff6ab04bfc7a7d615ca2cd54a56 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:15:40 +0200
Subject: [PATCH 09/52] silence boost_math warnings by marking it as `SYSTEM`
 lib

---
 third-party/boost-math/CMakeLists.txt |  2 +-
 third-party/update_boost_math.sh      | 13 +++++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/third-party/boost-math/CMakeLists.txt b/third-party/boost-math/CMakeLists.txt
index a1f6011d95e55..0de1fa2ec01ed 100644
--- a/third-party/boost-math/CMakeLists.txt
+++ b/third-party/boost-math/CMakeLists.txt
@@ -11,7 +11,7 @@ add_library(boost_math INTERFACE)
 
 add_library(Boost::math ALIAS boost_math)
 
-target_include_directories(boost_math INTERFACE include)
+target_include_directories(boost_math SYSTEM INTERFACE include)
 if(NOT CMAKE_VERSION VERSION_LESS "3.19")
   file(GLOB_RECURSE headers include/*.hpp)
   target_sources(boost_math PRIVATE ${headers})
diff --git a/third-party/update_boost_math.sh b/third-party/update_boost_math.sh
index 96cd73ed8c304..27d8816b7cb12 100755
--- a/third-party/update_boost_math.sh
+++ b/third-party/update_boost_math.sh
@@ -27,3 +27,16 @@ echo "****************************************"
 echo "Subsetting Boost.Math ${VERSION}"
 echo "****************************************"
 rm -rf ${SCRIPT_DIR}/boost-math/{.circleci,.drone,.github,build,config,doc,example,meta,reporting,src,test,tools}
+
+echo "****************************************"
+echo "Patching Boost.Math ${VERSION} for libc++"
+echo "****************************************"
+# Mark boost_math include dir as SYSTEM so libc++ consumers don't surface warnings from vendored boost-math code (e.g.
+# -Wdeprecated-redundant-constexpr-static-def). Upstream keeps these for pre-C++17 compat.
+sed -i 's|target_include_directories(boost_math INTERFACE include)|target_include_directories(boost_math SYSTEM INTERFACE include)|' \
+    ${SCRIPT_DIR}/boost-math/CMakeLists.txt
+
+# Verify the patch landed -- fail loudly if upstream renamed the target.
+grep -q 'target_include_directories(boost_math SYSTEM INTERFACE include)' \
+    ${SCRIPT_DIR}/boost-math/CMakeLists.txt \
+    || { echo "ERROR: SYSTEM include patch failed -- upstream CMakeLists.txt structure changed"; exit 1; }

>From 17c19c5c87c5adf5618686cbf1cac445d4bfbfda Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:24:39 +0200
Subject: [PATCH 10/52] add nodiscard test for assoc_laguerref

---
 .../c.math/sf.cmath/nodiscard.verify.cpp      | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7928ebf50681a
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// Check that functions are marked [[nodiscard]]
+
+#include <cmath>
+
+void test() {
+  // clang-format off
+  // assoc_laguerre
+  std::assoc_laguerref(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}

>From 0a7cf75eca52eab4c5d98b4f323f05fbf0187a9c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:27:41 +0200
Subject: [PATCH 11/52] add nodiscard to assoc_laguerref

---
 libcxx/include/__math/special_functions.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index d0d6e56507e85..dee70bec45cd8 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -54,7 +54,8 @@ _LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsi
 
 } // namespace __math
 
-inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 

>From ddfb2c1563bf465cd912f226abe221488a67b0df Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:50:00 +0200
Subject: [PATCH 12/52] insert grouping for assoc_laguerre functions

---
 libcxx/include/__math/special_functions.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index dee70bec45cd8..3100860ec8c70 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -49,9 +49,11 @@ struct __sf_result {
     return __ret;
   }
 };
+} // namespace __math
 
+// assoc_laguerre
+namespace __math {
 _LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
-
 } // namespace __math
 
 [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float

>From 39b98428ea2cf592234512ac3762348f3594087c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 00:09:27 +0200
Subject: [PATCH 13/52] insert grouping for hermite functions

---
 libcxx/include/__math/special_functions.h | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 3100860ec8c70..b8ec0785a3b2b 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -26,7 +26,6 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
-
 #if _LIBCPP_STD_VER >= 17
 
 namespace __math {
@@ -61,6 +60,14 @@ assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+#endif // _LIBCPP_STD_VER >= 17
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+_LIBCPP_END_NAMESPACE_STD
+
+// hermite
+_LIBCPP_BEGIN_NAMESPACE_STD
+#if _LIBCPP_STD_VER >= 17
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).
@@ -114,8 +121,5 @@ _LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, _Integer __x) {
 }
 
 #endif // _LIBCPP_STD_VER >= 17
-
-_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
-
 #endif // _LIBCPP___MATH_SPECIAL_FUNCTIONS_H

>From 145ce5f04732b8f5800f9013411d11ffe97c215f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 00:10:16 +0200
Subject: [PATCH 14/52] add missing ABI annotation

---
 libcxx/include/__math/special_functions.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index b8ec0785a3b2b..e11dc97fd46d4 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -34,7 +34,7 @@ struct __sf_result {
   bool __domain_error;
   _Tp __ret;
 
-  operator _Tp() const {
+  _LIBCPP_HIDE_FROM_ABI operator _Tp() const {
 #  if math_errhandling & MATH_ERRNO
     if (__domain_error)
       errno = EDOM;

>From 2328d31c7918995c6895836c69e1371250675427 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:16:15 +0200
Subject: [PATCH 15/52] TU: guard by C++ >= 17

---
 libcxx/src/mathematical_special_functions.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index e18e9c5f2797c..102acb37e50bc 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -14,6 +14,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+#if _LIBCPP_STD_VER >= 17
 
 namespace __math {
 namespace {
@@ -48,5 +49,6 @@ __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __
 }
 } // namespace __math
 
+#endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From 7b60948e6eb6cb5b9ccea95e76bb02865d314871 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:18:02 +0200
Subject: [PATCH 16/52] TU: qualify std:: functions

---
 libcxx/src/mathematical_special_functions.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 102acb37e50bc..03a90cd3bd982 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -19,14 +19,14 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 namespace __math {
 namespace {
 template <class _Ret>
-optional<_Ret> __check_nan() {
-  return nullopt;
+std::optional<_Ret> __check_nan() {
+  return std::nullopt;
 }
 
 template <class _Ret, class _Arg, class... _Args>
-optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
-  if constexpr (is_floating_point_v<_Arg>)
-    if (isnan(__arg))
+std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
+  if constexpr (std::is_floating_point_v<_Arg>)
+    if (std::isnan(__arg))
       return __arg;
   return __check_nan<_Ret>(__args...);
 }
@@ -39,7 +39,7 @@ __sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
   try {
     return {.__domain_error = false, .__ret = f(__args...)};
   } catch (...) {
-    return {.__domain_error = true, .__ret = numeric_limits<_Ret>::quiet_NaN()};
+    return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
   }
 }
 } // namespace

>From a0f3426cd940ef45a441b4e610efaebf8f3e6655 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:18:46 +0200
Subject: [PATCH 17/52] TU: add missing __uglify

---
 libcxx/src/mathematical_special_functions.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 03a90cd3bd982..fdca91bf5a8b4 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -31,13 +31,13 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
   return __check_nan<_Ret>(__args...);
 }
 
-template <class Func, class... _Args, class _Ret = std::invoke_result_t<Func, _Args...>>
-__sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
+template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
+__sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return {.__domain_error = false, .__ret = *__maybe_nan};
 
   try {
-    return {.__domain_error = false, .__ret = f(__args...)};
+    return {.__domain_error = false, .__ret = __f(__args...)};
   } catch (...) {
     return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
   }
@@ -45,7 +45,7 @@ __sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
 } // namespace
 
 __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+  return __invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 } // namespace __math
 

>From 745c9d9aa7dfc246593851286f70e6494148c0d4 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:07 +0200
Subject: [PATCH 18/52] TU: add missing headers

---
 libcxx/src/mathematical_special_functions.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index fdca91bf5a8b4..9a106c763c776 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -7,8 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include <__config>
+#include <__math/special_functions.h>
 #include <boost/math/special_functions.hpp>
 #include <cmath>
+#include <limits>
 #include <optional>
 #include <type_traits>
 

>From 372f1054b2aa081808ed07b6182a6b8f2e4a21e3 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:42 +0200
Subject: [PATCH 19/52] TU: remove C++20 designated initializers

---
 libcxx/src/mathematical_special_functions.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 9a106c763c776..23d9216d777d2 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -36,12 +36,12 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
 __sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
-    return {.__domain_error = false, .__ret = *__maybe_nan};
+    return {false, *__maybe_nan};
 
   try {
-    return {.__domain_error = false, .__ret = __f(__args...)};
+    return {false, __f(__args...)};
   } catch (...) {
-    return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
+    return {true, std::numeric_limits<_Ret>::quiet_NaN()};
   }
 }
 } // namespace

>From 4e32fcc443bdd93c388e69aad76c0b08d7335d9d Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:51 +0200
Subject: [PATCH 20/52] TU: cleanup

---
 libcxx/src/mathematical_special_functions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 23d9216d777d2..cf88cbef159fa 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -47,7 +47,7 @@ __sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
 } // namespace
 
 __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return __invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 } // namespace __math
 

>From 299419630480cc7ba0fd7c026a14c8797b461aa0 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 11:21:18 +0200
Subject: [PATCH 21/52] break cyclic header deps: move errno logic into TU

---
 libcxx/include/__math/special_functions.h     | 34 +------------------
 libcxx/src/mathematical_special_functions.cpp | 21 ++++++++----
 2 files changed, 15 insertions(+), 40 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index e11dc97fd46d4..89c75cfc04585 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -15,10 +15,7 @@
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_integral.h>
-#include <cerrno>
-#include <cfenv>
 #include <limits>
-#include <math.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -28,37 +25,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
-namespace __math {
-template <class _Tp>
-struct __sf_result {
-  bool __domain_error;
-  _Tp __ret;
-
-  _LIBCPP_HIDE_FROM_ABI operator _Tp() const {
-#  if math_errhandling & MATH_ERRNO
-    if (__domain_error)
-      errno = EDOM;
-#  endif
-
-#  if math_errhandling & MATH_ERREXCEPT
-    if (__domain_error)
-      feraiseexcept(FE_INVALID);
-#  endif
-
-    return __ret;
-  }
-};
-} // namespace __math
-
 // assoc_laguerre
-namespace __math {
-_LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
-} // namespace __math
-
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float
-assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return __math::__assoc_laguerre(__n, __m, __x);
-}
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index cf88cbef159fa..daab723b8f2e3 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -9,8 +9,11 @@
 #include <__config>
 #include <__math/special_functions.h>
 #include <boost/math/special_functions.hpp>
+#include <cerrno>
+#include <cfenv>
 #include <cmath>
 #include <limits>
+#include <math.h>
 #include <optional>
 #include <type_traits>
 
@@ -18,7 +21,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
-namespace __math {
 namespace {
 template <class _Ret>
 std::optional<_Ret> __check_nan() {
@@ -34,22 +36,27 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 }
 
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
-__sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
+_Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
-    return {false, *__maybe_nan};
+    return *__maybe_nan;
 
   try {
-    return {false, __f(__args...)};
+    return __f(__args...);
   } catch (...) {
-    return {true, std::numeric_limits<_Ret>::quiet_NaN()};
+#  if math_errhandling & MATH_ERRNO
+    errno = EDOM;
+#  endif
+#  if math_errhandling & MATH_ERREXCEPT
+    feraiseexcept(FE_INVALID);
+#  endif
+    return std::numeric_limits<_Ret>::quiet_NaN();
   }
 }
 } // namespace
 
-__sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
+float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
-} // namespace __math
 
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS

>From f2580f1f2e853e83db75462c95558cbfeef6508c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 11:29:35 +0200
Subject: [PATCH 22/52] TU: cleanups

---
 libcxx/src/mathematical_special_functions.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index daab723b8f2e3..420e0a1e71cb0 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -13,7 +13,6 @@
 #include <cfenv>
 #include <cmath>
 #include <limits>
-#include <math.h>
 #include <optional>
 #include <type_traits>
 
@@ -47,7 +46,7 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
     errno = EDOM;
 #  endif
 #  if math_errhandling & MATH_ERREXCEPT
-    feraiseexcept(FE_INVALID);
+    std::feraiseexcept(FE_INVALID);
 #  endif
     return std::numeric_limits<_Ret>::quiet_NaN();
   }

>From 80f81ae52610bc7accc38605524fdbf872004e4f Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 16:31:15 +0200
Subject: [PATCH 23/52] use boost errno_on_error policy instead of exceptions

---
 libcxx/src/mathematical_special_functions.cpp | 44 +++++++++++++++----
 1 file changed, 35 insertions(+), 9 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 420e0a1e71cb0..f8fea7ff1b70e 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -8,19 +8,31 @@
 
 #include <__config>
 #include <__math/special_functions.h>
-#include <boost/math/special_functions.hpp>
 #include <cerrno>
 #include <cfenv>
 #include <cmath>
-#include <limits>
 #include <optional>
 #include <type_traits>
 
+#define BOOST_MATH_NO_EXCEPTIONS
+#include <boost/math/special_functions.hpp>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
 namespace {
+// Error policy for all Boost.Math calls: report domain/pole/overflow/evaluation
+// errors via errno (errno_on_error) instead of throwing. Boost sets errno to
+// EDOM (domain/pole/evaluation) or ERANGE (overflow) and returns NaN/inf. The
+// remaining categories (underflow/denorm/indeterminate) default to ignore.
+namespace __bmp = boost::math::policies;
+using __policy =
+    __bmp::policy<__bmp::domain_error<__bmp::errno_on_error>,
+                  __bmp::pole_error<__bmp::errno_on_error>,
+                  __bmp::overflow_error<__bmp::errno_on_error>,
+                  __bmp::evaluation_error<__bmp::errno_on_error>>;
+
 template <class _Ret>
 std::optional<_Ret> __check_nan() {
   return std::nullopt;
@@ -34,22 +46,36 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
   return __check_nan<_Ret>(__args...);
 }
 
+// Shared back-end for the C++17 mathematical special functions ([sf.cmath]).
+// Boost.Math is the compute kernel; this wrapper enforces the standard's
+// error-reporting rules ([sf.cmath.general]):
+//   1. NaN argument -> return NaN, do NOT report a domain error (the
+//      __check_nan pre-filter below).
+//   2. domain/range error -> report per <cmath> math_errhandling: errno = EDOM
+//      (domain/pole) or ERANGE (overflow), done by Boost's errno_on_error
+//      policy; and, when MATH_ERREXCEPT is set, raise the matching <cfenv>
+//      exception (done here, since Boost never touches <cfenv>).
+// Promotion: Boost's default promote_float=true computes float inputs in double
+// and rounds once -- more accurate and overflow-resistant, matching the
+// existing std::hermite(float) approach. We keep it.
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
 _Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return *__maybe_nan;
 
-  try {
-    return __f(__args...);
-  } catch (...) {
-#  if math_errhandling & MATH_ERRNO
-    errno = EDOM;
+#  if math_errhandling & MATH_ERREXCEPT
+  errno = 0;
 #  endif
+  _Ret __ret = __f(__args..., __policy{});
+  // Boost set errno via the policy; mirror it onto <cfenv>:
+  // EDOM (domain/pole) -> FE_INVALID, ERANGE (overflow) -> FE_OVERFLOW.
 #  if math_errhandling & MATH_ERREXCEPT
+  if (errno == EDOM)
     std::feraiseexcept(FE_INVALID);
+  else if (errno == ERANGE)
+    std::feraiseexcept(FE_OVERFLOW);
 #  endif
-    return std::numeric_limits<_Ret>::quiet_NaN();
-  }
+  return __ret;
 }
 } // namespace
 

>From 2c13b0df2188c488e3f2fe235ba22569419e0e5b Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 16:44:35 +0200
Subject: [PATCH 24/52] TU: remove fenv support.

---
 libcxx/src/mathematical_special_functions.cpp | 25 +++++--------------
 1 file changed, 6 insertions(+), 19 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index f8fea7ff1b70e..3c0af96fb0c19 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -8,8 +8,6 @@
 
 #include <__config>
 #include <__math/special_functions.h>
-#include <cerrno>
-#include <cfenv>
 #include <cmath>
 #include <optional>
 #include <type_traits>
@@ -51,10 +49,11 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 // error-reporting rules ([sf.cmath.general]):
 //   1. NaN argument -> return NaN, do NOT report a domain error (the
 //      __check_nan pre-filter below).
-//   2. domain/range error -> report per <cmath> math_errhandling: errno = EDOM
-//      (domain/pole) or ERANGE (overflow), done by Boost's errno_on_error
-//      policy; and, when MATH_ERREXCEPT is set, raise the matching <cfenv>
-//      exception (done here, since Boost never touches <cfenv>).
+//   2. domain/range error -> reported via errno only: Boost's errno_on_error
+//      policy sets errno = EDOM (domain/pole/evaluation) or ERANGE (overflow).
+//      The <cfenv> floating-point-exception side of math_errhandling
+//      (MATH_ERREXCEPT) is intentionally not mirrored, matching the shipped
+//      std::hermite and libstdc++'s special-function implementations.
 // Promotion: Boost's default promote_float=true computes float inputs in double
 // and rounds once -- more accurate and overflow-resistant, matching the
 // existing std::hermite(float) approach. We keep it.
@@ -63,19 +62,7 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return *__maybe_nan;
 
-#  if math_errhandling & MATH_ERREXCEPT
-  errno = 0;
-#  endif
-  _Ret __ret = __f(__args..., __policy{});
-  // Boost set errno via the policy; mirror it onto <cfenv>:
-  // EDOM (domain/pole) -> FE_INVALID, ERANGE (overflow) -> FE_OVERFLOW.
-#  if math_errhandling & MATH_ERREXCEPT
-  if (errno == EDOM)
-    std::feraiseexcept(FE_INVALID);
-  else if (errno == ERANGE)
-    std::feraiseexcept(FE_OVERFLOW);
-#  endif
-  return __ret;
+  return __f(__args..., __policy{});
 }
 } // namespace
 

>From 580076b2ffe777b1f00d844091f88ebd03820402 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:24:36 +0200
Subject: [PATCH 25/52] test: add more tests; fix header includes

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index eb747efb4c1c6..079751321f3b9 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,11 +14,11 @@
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
 
 #include <cassert>
-#include <cerrno>
-#include <cfenv>
 #include <cmath>
+#include <limits>
 
 #include "common.h"
+#include "test_macros.h"
 #include "type_algorithms.h"
 
 struct TestFloatingPoint {
@@ -32,9 +32,19 @@ struct TestFloatingPoint {
 
     assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
 
+    // m == 0 reduces to the ordinary Laguerre polynomial: L_2(2) = (4 - 8 + 2) / 2 = -1.
+    assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
+
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
+    // noexcept is a libc++ extension here ([sf.cmath] does not mandate it), so use the libc++-only assertion.
+    LIBCPP_ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, T(0.)));
+
     check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
+
+    // The associated Laguerre polynomials are defined for all real x: a negative
+    // argument is in-domain and must not report a domain error. L_1^0(-1) = 1 - x = 2.
+    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, -1.f), 2.01f)); });
   }
 };
 

>From 2a6dfd4ae296d0f89e1ea5bc953d7d5f8f536ca9 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:53:16 +0200
Subject: [PATCH 26/52] test: add noexcept.compile.pass test

---
 .../c.math/sf.cmath/noexcept.compile.pass.cpp | 20 +++++++++++++++++++
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   |  3 ---
 2 files changed, 20 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..9994e6a018f8d
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// Check that functions are marked noexcept
+
+#include <cmath>
+
+#include "test_macros.h"
+
+void test() {
+  // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
+}
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 079751321f3b9..068ca81e04b83 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -37,9 +37,6 @@ struct TestFloatingPoint {
 
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
-    // noexcept is a libc++ extension here ([sf.cmath] does not mandate it), so use the libc++-only assertion.
-    LIBCPP_ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, T(0.)));
-
     check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
 
     // The associated Laguerre polynomials are defined for all real x: a negative

>From 7b8204c6a252f2971f288212096aee6641c14d82 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:55:16 +0200
Subject: [PATCH 27/52] test: cleanup

---
 .../test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 7928ebf50681a..a4c471e4b3168 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -15,6 +15,6 @@
 void test() {
   // clang-format off
   // assoc_laguerre
-  std::assoc_laguerref(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   // clang-format on
 }

>From 82d264a4bc5a0a1f6d4c932b97b4a74c11957fac Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 28 Jun 2026 23:37:36 +0200
Subject: [PATCH 28/52] test: remove fenv support

---
 libcxx/test/std/numerics/c.math/sf.cmath/common.h | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
index 867e6af6f6914..723422bf7038d 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/common.h
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -12,8 +12,6 @@
 #include <cassert>
 #include <cerrno>
 #include <cmath>
-#include <math.h>
-#include <cfenv>
 
 // std::type_identity is C++20 (we need to support C++17 here)
 template <class T>
@@ -32,34 +30,22 @@ template <class Func>
 void check_no_domain_error(Func f) {
 #if math_errhandling & MATH_ERRNO
   errno = EACCES;
-#endif
-#if math_errhandling & MATH_ERREXCEPT
-  std::feclearexcept(FE_INVALID);
 #endif
   f();
 #if math_errhandling & MATH_ERRNO
   assert(errno == EACCES);
 #endif
-#if math_errhandling & MATH_ERREXCEPT
-  assert(!std::fetestexcept(FE_INVALID));
-#endif
 }
 
 template <class Func>
 void check_domain_error(Func f) {
 #if math_errhandling & MATH_ERRNO
   errno = EACCES;
-#endif
-#if math_errhandling & MATH_ERREXCEPT
-  std::feclearexcept(FE_INVALID);
 #endif
   f();
 #if math_errhandling & MATH_ERRNO
   assert(errno == EDOM);
 #endif
-#if math_errhandling & MATH_ERREXCEPT
-  assert(std::fetestexcept(FE_INVALID));
-#endif
 }
 
 #endif // TEST_SF_CMATH_COMMON_H

>From 6b65307f26de7474c6b3f6789c8bd49e9b9787d7 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 28 Jun 2026 23:37:54 +0200
Subject: [PATCH 29/52] test: cleanups + more NaN tests

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 28 ++++++++++---------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 068ca81e04b83..fffbb72557a5a 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -18,30 +18,32 @@
 #include <limits>
 
 #include "common.h"
-#include "test_macros.h"
 #include "type_algorithms.h"
 
 struct TestFloatingPoint {
   template <class T>
   void operator()() const {
+    // sample value testing
     assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-
     assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
-
-    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
-
-    assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
-
-    // m == 0 reduces to the ordinary Laguerre polynomial: L_2(2) = (4 - 8 + 2) / 2 = -1.
     assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
+    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
+    assert(between(60.124f, std::assoc_laguerref(2, 10, T(0.5)), 60.126f));
 
+    // return type: float
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
-    check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
-
-    // The associated Laguerre polynomials are defined for all real x: a negative
-    // argument is in-domain and must not report a domain error. L_1^0(-1) = 1 - x = 2.
-    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, -1.f), 2.01f)); });
+    // NaN input -> NaN output (w/o domain error)
+    auto check_nan = [](T nan) {
+      check_no_domain_error([nan] { assert(std::isnan(std::assoc_laguerref(0, 0, nan))); });
+    };
+    if (std::numeric_limits<T>::has_quiet_NaN)
+      check_nan(std::numeric_limits<T>::quiet_NaN());
+    if (std::numeric_limits<T>::has_signaling_NaN)
+      check_nan(std::numeric_limits<T>::signaling_NaN());
+
+    // negative x: no domain error
+    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, T(-1)), 2.01f)); });
   }
 };
 

>From ddd4eef0a09edb02e8e486fb587bf5e64d538b65 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 00:00:33 +0200
Subject: [PATCH 30/52] impl assoc_laguerrel

---
 libcxx/include/__math/special_functions.h     | 1 +
 libcxx/src/mathematical_special_functions.cpp | 5 +++++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 89c75cfc04585..0cfd484955b15 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -27,6 +27,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 // assoc_laguerre
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept;
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 3c0af96fb0c19..980af019748db 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -66,10 +66,15 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
 }
 } // namespace
 
+// assoc_laguerre
 float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept {
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From a35b978e0601bea7a9accf072c175a4c8b182c97 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 00:23:48 +0200
Subject: [PATCH 31/52] test: assoc_laguerrel

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 78 ++++++++-----------
 1 file changed, 34 insertions(+), 44 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index fffbb72557a5a..0a9abaf2f4f24 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -9,54 +9,44 @@
 // REQUIRES: std-at-least-c++17
 
 // <cmath>
-//
-// [sf.cmath.assoc.laguerre], associated Laguerre polynomials
-// float               assoc_laguerref(unsigned n, unsigned m, float x);
+
+// float       assoc_laguerref(unsigned n, unsigned m, float x);
+// long double assoc_laguerrel(unsigned n, unsigned m, long double x);
 
 #include <cassert>
 #include <cmath>
 #include <limits>
 
 #include "common.h"
-#include "type_algorithms.h"
-
-struct TestFloatingPoint {
-  template <class T>
-  void operator()() const {
-    // sample value testing
-    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-    assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
-    assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
-    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
-    assert(between(60.124f, std::assoc_laguerref(2, 10, T(0.5)), 60.126f));
-
-    // return type: float
-    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
-
-    // NaN input -> NaN output (w/o domain error)
-    auto check_nan = [](T nan) {
-      check_no_domain_error([nan] { assert(std::isnan(std::assoc_laguerref(0, 0, nan))); });
-    };
-    if (std::numeric_limits<T>::has_quiet_NaN)
-      check_nan(std::numeric_limits<T>::quiet_NaN());
-    if (std::numeric_limits<T>::has_signaling_NaN)
-      check_nan(std::numeric_limits<T>::signaling_NaN());
-
-    // negative x: no domain error
-    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, T(-1)), 2.01f)); });
-  }
-};
-
-struct TestIntegral {
-  template <class T>
-  void operator()() const {
-    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-
-    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
-  }
-};
-
-int main() {
-  types::for_each(types::floating_point_types{}, TestFloatingPoint{});
-  types::for_each(types::integral_types{}, TestIntegral{});
+
+// Each suffixed overload is fixed to a single floating-point type, so it is
+// tested directly with that type. The unsuffixed std::assoc_laguerre overloads
+// (and the integer-argument overload) are not implemented yet.
+template <class T, class Func>
+void test(Func assoc_laguerre) {
+  // sample values
+  assert(between(0.99, assoc_laguerre(0, 0, T(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, T(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, T(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, T(2)), 0.01));
+  assert(between(60.124, assoc_laguerre(2, 10, T(0.5)), 60.126));
+
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, T(0))), T>);
+
+  // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
+  auto test_nan = [&](T nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
+  if (std::numeric_limits<T>::has_quiet_NaN)
+    test_nan(std::numeric_limits<T>::quiet_NaN());
+  if (std::numeric_limits<T>::has_signaling_NaN)
+    test_nan(std::numeric_limits<T>::signaling_NaN());
+
+  // A negative argument is in the domain (no domain error).
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, T(-1)), 2.01)); });
 }
+
+int main(int, char**) {
+  test<float>([](unsigned __n, unsigned __m, float __x) { return std::assoc_laguerref(__n, __m, __x); });
+  test<long double>([](unsigned __n, unsigned __m, long double __x) { return std::assoc_laguerrel(__n, __m, __x); });
+
+  return 0;
+}
\ No newline at end of file

>From 96e8004c4a6f41343f1257898d04efbbd01f6457 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 21:43:26 +0200
Subject: [PATCH 32/52] re-introduce __math::__assoc_laguerre -> only ABI
 export internal API

---
 libcxx/include/__math/special_functions.h     | 16 ++++++++++++++--
 libcxx/src/mathematical_special_functions.cpp |  7 +++++--
 2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 0cfd484955b15..62ae09594bea3 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
 
 #include <__config>
+#include <__configuration/attributes.h>
 #include <__math/copysign.h>
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
@@ -26,8 +27,19 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
 // assoc_laguerre
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept;
+namespace __math {
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
+} // namespace __math
+
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
+assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 980af019748db..ed8109c7e482c 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -19,6 +19,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
+namespace __math {
 namespace {
 // Error policy for all Boost.Math calls: report domain/pole/overflow/evaluation
 // errors via errno (errno_on_error) instead of throwing. Boost sets errno to
@@ -67,14 +68,16 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
 } // namespace
 
 // assoc_laguerre
-float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+float __assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
-long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept {
+long double __assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+} // namespace __math
+
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From 34d62b1da4598efb45c79363654566b9ca6677aa Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 21:43:57 +0200
Subject: [PATCH 33/52] add exported ABI symbols to abilist

---
 ...-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...nux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...werpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...rpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...nux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...known-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...own-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 4 +++-
 ...n-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 2 ++
 9 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 74283373d1b65..7889109825e10 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1700,6 +1700,8 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 291149608c9c7..3e139e30496a6 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1338,6 +1338,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index da6d608af462c..304817ba0cffb 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -686,6 +686,8 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEyPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock11from_time_tEi', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6ee31613d5320..d5e37635fa511 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -686,6 +686,8 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock11from_time_tEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 92a37db24ac5f..a59b72d695b55 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1690,6 +1690,8 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 7b09d3dd3f75c..abef314dc14f4 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1338,6 +1338,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 725ed669fb561..86bc3dec5bd34 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1350,6 +1350,8 @@
 {'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 160, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 717d4c9bee726..11330e7eaf442 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1349,6 +1349,8 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
@@ -2030,4 +2032,4 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 45db143b4cf4a..460bd21a2fecf 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1320,6 +1320,8 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}

>From 7e1ed83abf9818cb95663ed9077215f102873666 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:10:37 +0200
Subject: [PATCH 34/52] impl std::assoc_laguerre(double)

---
 libcxx/include/__math/special_functions.h     | 6 ++++++
 libcxx/src/mathematical_special_functions.cpp | 4 ++++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 782e441562c00..8fd363919e5cc 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -30,6 +30,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 // assoc_laguerre
 namespace __math {
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI double __assoc_laguerre(unsigned, unsigned, double) noexcept;
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
@@ -37,6 +38,11 @@ namespace __math {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+template <class = int>
+_LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
 assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index ed8109c7e482c..0f6e3a850ad73 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -72,6 +72,10 @@ float __assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+double __assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+
 long double __assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }

>From 8b1f5f6ded44a8cbd009c03310faa645d320bb97 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:20:15 +0200
Subject: [PATCH 35/52] fixup impl std::assoc_laguerre(double)

---
 libcxx/include/__math/special_functions.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 8fd363919e5cc..b48b9ffdb129c 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -39,7 +39,7 @@ namespace __math {
 }
 
 template <class = int>
-_LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 

>From fbbd70784b3e8bc65aa5e3b18870d936ea438e2e Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:43:07 +0200
Subject: [PATCH 36/52] fixup: assoc_laguerre(dbl)

---
 libcxx/include/__math/special_functions.h                   | 3 +--
 .../std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp    | 6 +++---
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index b48b9ffdb129c..20268dd1bd7d6 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -38,8 +38,7 @@ namespace __math {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-template <class = int>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 0a9abaf2f4f24..b9223f6d66a2c 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -19,9 +19,9 @@
 
 #include "common.h"
 
-// Each suffixed overload is fixed to a single floating-point type, so it is
-// tested directly with that type. The unsuffixed std::assoc_laguerre overloads
-// (and the integer-argument overload) are not implemented yet.
+// Each overload is fixed to a single floating-point type, so it is tested
+// directly with that type. The integer-argument overload
+// (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
 template <class T, class Func>
 void test(Func assoc_laguerre) {
   // sample values

>From 7f5e5515ad7c1e551ff1b7cecf57772211f3b376 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:12:00 +0200
Subject: [PATCH 37/52] test: general cleanups

---
 .../std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp    | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index b9223f6d66a2c..d446e017494c4 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -45,8 +45,8 @@ void test(Func assoc_laguerre) {
 }
 
 int main(int, char**) {
-  test<float>([](unsigned __n, unsigned __m, float __x) { return std::assoc_laguerref(__n, __m, __x); });
-  test<long double>([](unsigned __n, unsigned __m, long double __x) { return std::assoc_laguerrel(__n, __m, __x); });
+  test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
   return 0;
-}
\ No newline at end of file
+}

>From 340ed9ca7b9f543fd3ffa8110f29863dc7fcef4f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:14:47 +0200
Subject: [PATCH 38/52] test: std::assoc_laguerre(double)

---
 .../test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp  | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index d446e017494c4..9c2bc2026a348 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -10,6 +10,7 @@
 
 // <cmath>
 
+// double      assoc_laguerre(unsigned n, unsigned m, double x);
 // float       assoc_laguerref(unsigned n, unsigned m, float x);
 // long double assoc_laguerrel(unsigned n, unsigned m, long double x);
 
@@ -45,6 +46,8 @@ void test(Func assoc_laguerre) {
 }
 
 int main(int, char**) {
+  test<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+
   test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
   test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 

>From 589d01b5fce70f06068b3304556145bc1c284ebe Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:20:51 +0200
Subject: [PATCH 39/52] test: nodiscard/noexcept for assoc_laguerre(double),
 assoc_laguerrel

---
 .../test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp  | 3 +++
 .../libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp  | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index a4c471e4b3168..8b8826d03de01 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -15,6 +15,9 @@
 void test() {
   // clang-format off
   // assoc_laguerre
+  std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   // clang-format on
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 9994e6a018f8d..70984fc0c83bf 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -16,5 +16,8 @@
 
 void test() {
   // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0));
+
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
+  ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
 }

>From db297e6b6a155fdd5ea0bb4978f5e267c14a773e Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:42:41 +0200
Subject: [PATCH 40/52] add ABI symbol to abilist for assoc_laguerre(double)

---
 ...m64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 .../powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...owerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ..._64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...nknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...nown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 1 +
 9 files changed, 9 insertions(+)

diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6ab143c0113f3..9f70dd6550fc7 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1701,6 +1701,7 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 08d42d44e4827..4152a583ec5a3 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1339,6 +1339,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 98fd189eaa3f7..861750f606c77 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -687,6 +687,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEyPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 9e1db57fc315b..347d9f9f23d70 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -687,6 +687,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index a59b72d695b55..cd3f3e4fda9a2 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1690,6 +1690,7 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 61bd10d997a1b..421f4839f762e 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1339,6 +1339,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 286ad56406ee2..d59e224c42922 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1351,6 +1351,7 @@
 {'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 160, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 9fd2edf63e6ef..d2b084bace550 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1350,6 +1350,7 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 51c27fea37ce9..7e57931398e85 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1321,6 +1321,7 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}

>From e8984b706592b5b2ff08bdd19020330498423157 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:12:02 +0200
Subject: [PATCH 41/52] impl assoc_laguerre(Int)

---
 libcxx/include/__math/special_functions.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 20268dd1bd7d6..5021e5a54b634 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -47,6 +47,11 @@ assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+template <class _Int, std::enable_if_t<std::is_integral_v<_Int>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 // hermite

>From c8c5eb6a2040040b6a5c67c42128172a4c5661a8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:18:00 +0200
Subject: [PATCH 42/52] test: nodiscard/except assoc_laguerre(Int)

---
 .../numerics/c.math/sf.cmath/nodiscard.verify.cpp      |  1 +
 .../numerics/c.math/sf.cmath/noexcept.compile.pass.cpp | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 8b8826d03de01..b83070f4f984d 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -16,6 +16,7 @@ void test() {
   // clang-format off
   // assoc_laguerre
   std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 70984fc0c83bf..cca9c1e924a8c 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -13,6 +13,14 @@
 #include <cmath>
 
 #include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestIntegral {
+  template <class T>
+  void operator()() const {
+    ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, T{}));
+  }
+};
 
 void test() {
   // assoc_laguerre
@@ -20,4 +28,6 @@ void test() {
 
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
   ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
+
+  types::for_each(types::integral_types{}, TestIntegral{});
 }

>From 5946b7d3c07ea7f4dc898e9750313838fd3a99a8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:19:29 +0200
Subject: [PATCH 43/52] wip: test: assoc_laguerre(Int)

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 48 ++++++++++++-------
 1 file changed, 32 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 9c2bc2026a348..b39e20fe6288a 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -17,39 +17,55 @@
 #include <cassert>
 #include <cmath>
 #include <limits>
+#include <type_traits>
 
 #include "common.h"
+#include "type_algorithms.h"
 
 // Each overload is fixed to a single floating-point type, so it is tested
 // directly with that type. The integer-argument overload
 // (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
-template <class T, class Func>
+template <class ExpectedFuncRet, class FuncArgX, class Func>
 void test(Func assoc_laguerre) {
   // sample values
-  assert(between(0.99, assoc_laguerre(0, 0, T(0)), 1.01));
-  assert(between(0.99, assoc_laguerre(1, 1, T(1)), 1.01));
-  assert(between(-1.01, assoc_laguerre(2, 0, T(2)), -0.99));
-  assert(between(-0.01, assoc_laguerre(2, 2, T(2)), 0.01));
-  assert(between(60.124, assoc_laguerre(2, 10, T(0.5)), 60.126));
+  assert(between(0.99, assoc_laguerre(0, 0, FuncArgX(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, FuncArgX(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, FuncArgX(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, FuncArgX(2)), 0.01));
+  if constexpr (std::is_floating_point_v<FuncArgX>)
+    assert(between(60.124, assoc_laguerre(2, 10, FuncArgX(0.5)), 60.126));
 
-  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, T(0))), T>);
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, FuncArgX(0))), ExpectedFuncRet>);
 
   // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
-  auto test_nan = [&](T nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
-  if (std::numeric_limits<T>::has_quiet_NaN)
-    test_nan(std::numeric_limits<T>::quiet_NaN());
-  if (std::numeric_limits<T>::has_signaling_NaN)
-    test_nan(std::numeric_limits<T>::signaling_NaN());
+  if constexpr (std::is_floating_point_v<FuncArgX>) {
+    auto test_nan = [&](FuncArgX nan) {
+      check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); });
+    };
+    if (std::numeric_limits<FuncArgX>::has_quiet_NaN)
+      test_nan(std::numeric_limits<FuncArgX>::quiet_NaN());
+    if (std::numeric_limits<FuncArgX>::has_signaling_NaN)
+      test_nan(std::numeric_limits<FuncArgX>::signaling_NaN());
+  }
 
   // A negative argument is in the domain (no domain error).
-  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, T(-1)), 2.01)); });
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, FuncArgX(-1)), 2.01)); });
 }
 
+struct TestIntegral {
+  template <class Int>
+  void operator()() const {
+    test<double, Int>([](unsigned n, unsigned m, Int x) { return std::assoc_laguerre(n, m, x); });
+  }
+};
+
 int main(int, char**) {
-  test<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+  test<double, double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+
+  test<float, float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test<long double, long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
-  test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
-  test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
+  // types::for_each(types::integral_types{}, TestIntegral{}); WIP
 
   return 0;
 }

>From 619773ba99f60f0d799ca98488fa71cc246e2c47 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 17:48:09 +0200
Subject: [PATCH 44/52] fix gcc build: gcc defines __STDCPP_FLOATXX_T__ even
 though we dont ship <stdfloat> yet -> breaks Boost

---
 libcxx/src/mathematical_special_functions.cpp | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 0f6e3a850ad73..b8e14967740f1 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -12,6 +12,22 @@
 #include <optional>
 #include <type_traits>
 
+// GCC defines __STDCPP_FLOATnn_T__ whenever the _Floatnn extended types exist at the
+// language level, independent of the standard library. libc++ currently ships no
+// <stdfloat>, so std::floatnn_t is never declared -- but Boost.Math keys its
+// std::floatnn_t overloads off these macros and would reference the missing types.
+// Suppress those overloads while <stdfloat> is unavailable. The __has_include guard is
+// the same condition Boost uses to include <stdfloat>, so this workaround disables itself
+// automatically once libc++ provides the header (the overloads then light up on their own
+// -- no manual re-enable needed here).
+#if !__has_include(<stdfloat>)
+#  undef __STDCPP_FLOAT16_T__
+#  undef __STDCPP_FLOAT32_T__
+#  undef __STDCPP_FLOAT64_T__
+#  undef __STDCPP_FLOAT128_T__
+#  undef __STDCPP_BFLOAT16_T__
+#endif
+
 #define BOOST_MATH_NO_EXCEPTIONS
 #include <boost/math/special_functions.hpp>
 

>From 3c8e6c21dca99ac9b83fe264c70255710d20c21f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 18:01:47 +0200
Subject: [PATCH 45/52] fix android abilist

---
 ...linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 421f4839f762e..08b901a94f394 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1340,8 +1340,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjg', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}

>From e4c48175cfc00429e93c92a30aa81e67201f4866 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 18:02:21 +0200
Subject: [PATCH 46/52] fix boost math thread support

---
 libcxx/src/mathematical_special_functions.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index b8e14967740f1..c37811bdf52ea 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -28,6 +28,14 @@
 #  undef __STDCPP_BFLOAT16_T__
 #endif
 
+// Boost.Math detects thread support via __has_include(<thread>/<mutex>/...), but libc++
+// ships those headers even when threads are disabled (_LIBCPP_HAS_THREADS == 0), so the
+// detection wrongly enables std::mutex use and breaks on no-thread targets (e.g. picolibc).
+// Tell Boost there are no threads; lazy-init tables don't need locking without threads.
+#if !_LIBCPP_HAS_THREADS
+#  define BOOST_MATH_DISABLE_THREADS
+#endif
+
 #define BOOST_MATH_NO_EXCEPTIONS
 #include <boost/math/special_functions.hpp>
 

>From 60635ce92abd69cad3a5161a78a5a2f55281d9b8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 21:47:33 +0200
Subject: [PATCH 47/52] test: fix apple's math_errhandling usage. runtime not
 macro constant

---
 .../std/numerics/c.math/sf.cmath/common.h     | 23 +++++++++----------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
index 723422bf7038d..ca6de9ea868bd 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/common.h
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -26,26 +26,25 @@ bool between(type_identity_t<T> lower, T value, type_identity_t<T> upper) {
   return lower < value && value < upper;
 }
 
+// Note: math_errhandling must be queried with a runtime `if`, not `#if`. On Apple
+// platforms it is defined as a function call (__math_errhandling()), so it is not a
+// preprocessor constant and would break `#if` under -Wundef.
 template <class Func>
 void check_no_domain_error(Func f) {
-#if math_errhandling & MATH_ERRNO
-  errno = EACCES;
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    errno = EACCES;
   f();
-#if math_errhandling & MATH_ERRNO
-  assert(errno == EACCES);
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    assert(errno == EACCES);
 }
 
 template <class Func>
 void check_domain_error(Func f) {
-#if math_errhandling & MATH_ERRNO
-  errno = EACCES;
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    errno = EACCES;
   f();
-#if math_errhandling & MATH_ERRNO
-  assert(errno == EDOM);
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    assert(errno == EDOM);
 }
 
 #endif // TEST_SF_CMATH_COMMON_H

>From 8201618481b60722093b07eff23b8bcb3b274e05 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:16:59 +0200
Subject: [PATCH 48/52] minimze boost includes

---
 libcxx/src/mathematical_special_functions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index c37811bdf52ea..dc046b3a845df 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -37,7 +37,7 @@
 #endif
 
 #define BOOST_MATH_NO_EXCEPTIONS
-#include <boost/math/special_functions.hpp>
+#include <boost/math/special_functions/laguerre.hpp>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS

>From 4d11bdbe96fca5653a71146ff3fea197180f13ef Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:22:46 +0200
Subject: [PATCH 49/52] patch boost: make it compilable w/o localization
 support

---
 third-party/boost-math-libcxx.patch | 47 +++++++++++++++++++++++++++++
 third-party/update_boost_math.sh    | 10 ++++++
 2 files changed, 57 insertions(+)
 create mode 100644 third-party/boost-math-libcxx.patch

diff --git a/third-party/boost-math-libcxx.patch b/third-party/boost-math-libcxx.patch
new file mode 100644
index 0000000000000..f7692a7e54b67
--- /dev/null
+++ b/third-party/boost-math-libcxx.patch
@@ -0,0 +1,47 @@
+# Local libc++ patch for the vendored Boost.Math snapshot.
+#
+# Boost.Math only uses <sstream>/<iomanip> and detail::prec_format to build the
+# what() text of the exceptions it throws. That machinery sits OUTSIDE Boost's own
+# `#ifndef BOOST_MATH_NO_EXCEPTIONS` guard (unlike the adjacent <stdexcept> /
+# throw_exception.hpp includes), so it is compiled even when exceptions are disabled.
+# libc++ builds the Math Special Functions back-end with BOOST_MATH_NO_EXCEPTIONS,
+# and on configurations without localization (e.g. -DLIBCXX_ENABLE_LOCALIZATION=OFF)
+# libc++ ships no functional <sstream>, so prec_format fails to compile.
+#
+# This patch guards the iostream-only message formatting under BOOST_MATH_NO_EXCEPTIONS.
+# Reported upstream: https://github.com/boostorg/math/issues (see PR description).
+# Re-applied by update_boost_math.sh after each snapshot refresh; drop once upstream.
+diff --git a/boost-math/include/boost/math/policies/error_handling.hpp b/boost-math/include/boost/math/policies/error_handling.hpp
+index d7ba694564ba..da167171f650 100644
+--- a/boost-math/include/boost/math/policies/error_handling.hpp
++++ b/boost-math/include/boost/math/policies/error_handling.hpp
+@@ -26,9 +26,9 @@
+ #include <complex>
+ #include <cstdint>
+ #include <cstring>
++#ifndef BOOST_MATH_NO_EXCEPTIONS
+ #include <iomanip>
+ #include <sstream>
+-#ifndef BOOST_MATH_NO_EXCEPTIONS
+ #include <stdexcept>
+ #endif
+ #include <string>
+@@ -95,6 +95,10 @@ T user_indeterminate_result_error(const char* function, const char* message, con
+ namespace detail
+ {
+ 
++// prec_format is only used to build exception what() messages, so it is gated on
++// exceptions being enabled. It relies on <sstream>, which is unavailable in some
++// standard-library configurations (e.g. libc++ built without localization).
++#ifndef BOOST_MATH_NO_EXCEPTIONS
+ template <class T>
+ inline std::string prec_format(const T& val)
+ {
+@@ -125,6 +129,7 @@ inline std::string prec_format<std::float128_t>(const std::float128_t& val)
+ }
+ 
+ #endif
++#endif // BOOST_MATH_NO_EXCEPTIONS
+ 
+ inline void replace_all_in_string(std::string& result, const char* what, const char* with)
+ {
diff --git a/third-party/update_boost_math.sh b/third-party/update_boost_math.sh
index 27d8816b7cb12..a4276cdb74028 100755
--- a/third-party/update_boost_math.sh
+++ b/third-party/update_boost_math.sh
@@ -40,3 +40,13 @@ sed -i 's|target_include_directories(boost_math INTERFACE include)|target_includ
 grep -q 'target_include_directories(boost_math SYSTEM INTERFACE include)' \
     ${SCRIPT_DIR}/boost-math/CMakeLists.txt \
     || { echo "ERROR: SYSTEM include patch failed -- upstream CMakeLists.txt structure changed"; exit 1; }
+
+# Guard Boost.Math's iostream-only exception-message formatting (prec_format / <sstream>)
+# under BOOST_MATH_NO_EXCEPTIONS, so the no-exceptions back-end builds without <sstream>.
+# Needed for libc++ configurations without localization. Drop once fixed upstream.
+patch -p1 -d ${SCRIPT_DIR} < ${SCRIPT_DIR}/boost-math-libcxx.patch
+
+# Verify the patch landed -- fail loudly if upstream restructured error_handling.hpp.
+grep -q 'prec_format is only used to build exception' \
+    ${SCRIPT_DIR}/boost-math/include/boost/math/policies/error_handling.hpp \
+    || { echo "ERROR: no-exceptions iostream patch failed -- upstream error_handling.hpp structure changed"; exit 1; }

>From 276df4abe72a9ba6d4979d5922b466445aa406b9 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:32:27 +0200
Subject: [PATCH 50/52] apply boost patch

---
 .../include/boost/math/policies/error_handling.hpp         | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/third-party/boost-math/include/boost/math/policies/error_handling.hpp b/third-party/boost-math/include/boost/math/policies/error_handling.hpp
index d7ba694564ba4..da167171f650d 100644
--- a/third-party/boost-math/include/boost/math/policies/error_handling.hpp
+++ b/third-party/boost-math/include/boost/math/policies/error_handling.hpp
@@ -26,9 +26,9 @@
 #include <complex>
 #include <cstdint>
 #include <cstring>
+#ifndef BOOST_MATH_NO_EXCEPTIONS
 #include <iomanip>
 #include <sstream>
-#ifndef BOOST_MATH_NO_EXCEPTIONS
 #include <stdexcept>
 #endif
 #include <string>
@@ -95,6 +95,10 @@ T user_indeterminate_result_error(const char* function, const char* message, con
 namespace detail
 {
 
+// prec_format is only used to build exception what() messages, so it is gated on
+// exceptions being enabled. It relies on <sstream>, which is unavailable in some
+// standard-library configurations (e.g. libc++ built without localization).
+#ifndef BOOST_MATH_NO_EXCEPTIONS
 template <class T>
 inline std::string prec_format(const T& val)
 {
@@ -125,6 +129,7 @@ inline std::string prec_format<std::float128_t>(const std::float128_t& val)
 }
 
 #endif
+#endif // BOOST_MATH_NO_EXCEPTIONS
 
 inline void replace_all_in_string(std::string& result, const char* what, const char* with)
 {

>From 6bbed03306814ee7a45418ba8e01e619852d7d94 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:53:16 +0200
Subject: [PATCH 51/52] doc: add boost issue

---
 third-party/boost-math-libcxx.patch | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/third-party/boost-math-libcxx.patch b/third-party/boost-math-libcxx.patch
index f7692a7e54b67..8593b077a2db2 100644
--- a/third-party/boost-math-libcxx.patch
+++ b/third-party/boost-math-libcxx.patch
@@ -9,7 +9,7 @@
 # libc++ ships no functional <sstream>, so prec_format fails to compile.
 #
 # This patch guards the iostream-only message formatting under BOOST_MATH_NO_EXCEPTIONS.
-# Reported upstream: https://github.com/boostorg/math/issues (see PR description).
+# Reported upstream: https://github.com/boostorg/math/issues/1412
 # Re-applied by update_boost_math.sh after each snapshot refresh; drop once upstream.
 diff --git a/boost-math/include/boost/math/policies/error_handling.hpp b/boost-math/include/boost/math/policies/error_handling.hpp
 index d7ba694564ba..da167171f650 100644

>From 441f2f0089823971d515573e8f8074fddd2e32a1 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 08:34:54 +0200
Subject: [PATCH 52/52] guard via
 _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS

---
 libcxx/include/__configuration/availability.h |  5 +++++
 libcxx/include/__math/special_functions.h     | 20 ++++++++++++-------
 .../c.math/sf.cmath/nodiscard.verify.cpp      |  4 ++++
 .../c.math/sf.cmath/noexcept.compile.pass.cpp |  4 ++++
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   |  4 ++++
 .../libcxx/test/features/availability.py      |  8 ++++++++
 6 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 735af5d431cb1..72eb50721338f 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -271,6 +271,11 @@
 #define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20
 #define _LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20_ATTRIBUTE
 
+// This controls the availability of the C++17 mathematical special functions
+// ([sf.cmath]). Their implementation lives in the built library (backed by Boost.Math).
+#define _LIBCPP_AVAILABILITY_HAS_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_INTRODUCED_IN_LLVM_23
+#define _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE
+
 // This controls the availability of the C++20 time zone database.
 // The parser code is built in the library.
 #define _LIBCPP_AVAILABILITY_HAS_TZDB _LIBCPP_INTRODUCED_IN_LLVM_19
diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 5021e5a54b634..546600b72bce0 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -29,26 +29,32 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 // assoc_laguerre
 namespace __math {
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI double __assoc_laguerre(unsigned, unsigned, double) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI float
+__assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI double
+__assoc_laguerre(unsigned, unsigned, double) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI long double
+__assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI double
+assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI long double
 assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
 template <class _Int, std::enable_if_t<std::is_integral_v<_Int>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_HIDE_FROM_ABI double
+assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index b83070f4f984d..598311f20917d 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// The functions carry availability markup, so referencing them fails to compile against a
+// back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // Check that functions are marked [[nodiscard]]
 
 #include <cmath>
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index cca9c1e924a8c..8f9dd87c0ee87 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// The functions carry availability markup, so referencing them fails to compile against a
+// back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // Check that functions are marked noexcept
 
 #include <cmath>
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index b39e20fe6288a..91a7c4a3d2047 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// These functions are implemented in the built library, so a program using them fails to
+// load against a back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // <cmath>
 
 // double      assoc_laguerre(unsigned n, unsigned m, double x);
diff --git a/libcxx/utils/libcxx/test/features/availability.py b/libcxx/utils/libcxx/test/features/availability.py
index c0f2a3d4cdc88..7bb1add0e9e9c 100644
--- a/libcxx/utils/libcxx/test/features/availability.py
+++ b/libcxx/utils/libcxx/test/features/availability.py
@@ -211,4 +211,12 @@
             cfg.available_features,
         ),
     ),
+    # Tests that require the C++17 mathematical special functions in the built library
+    Feature(
+        name="availability-mathematical_special_functions-missing",
+        when=lambda cfg: BooleanExpression.evaluate(
+            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-23)",
+            cfg.available_features,
+        ),
+    ),
 ]


From libcxx-commits at lists.llvm.org  Fri Jul  3 23:50:33 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Fri, 03 Jul 2026 23:50:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3454 as resolved (PR
	#207487)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207487@github.com>
Message-ID: <6a48ad39.d5797644.146403.529e@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/207487

>From c1bb4994c8cc823a0dc98d0181a8eb8130fb9bb1 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 4 Jul 2026 13:45:59 +0800
Subject: [PATCH 1/2] [libc++] Mark LWG3454 as resolved

---
 libcxx/docs/Status/Cxx26Issues.csv                     |  2 +-
 .../pointer.traits.functions/pointer_to.pass.cpp       | 10 +++++++---
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 3f2a446f7e1f3..30042d4465d93 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -149,7 +149,7 @@
 "`LWG2991 <https://wg21.link/LWG2991>`__","``variant copy`` constructor missing ``noexcept(see below)``","2025-11 (Kona)","","","`#171301 <https://github.com/llvm/llvm-project/issues/171301>`__",""
 "`LWG3090 <https://wg21.link/LWG3090>`__","What is §[time.duration.cons]p4's ""no overflow is induced in the conversion"" intended to mean?","2025-11 (Kona)","","","`#171302 <https://github.com/llvm/llvm-project/issues/171302>`__",""
 "`LWG3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","2025-11 (Kona)","|Complete|","16","`#105356 <https://github.com/llvm/llvm-project/issues/105356>`__",""
-"`LWG3454 <https://wg21.link/LWG3454>`__","``pointer_traits::pointer_to`` should be ``constexpr``","2025-11 (Kona)","","","`#171307 <https://github.com/llvm/llvm-project/issues/171307>`__",""
+"`LWG3454 <https://wg21.link/LWG3454>`__","``pointer_traits::pointer_to`` should be ``constexpr``","2025-11 (Kona)","|Complete|","16","`#171307 <https://github.com/llvm/llvm-project/issues/171307>`__",""
 "`LWG3627 <https://wg21.link/LWG3627>`__","Inconsistent specifications for ``std::make_optional`` overloads","2025-11 (Kona)","|Complete|","22","`#171309 <https://github.com/llvm/llvm-project/issues/171309>`__",""
 "`LWG4015 <https://wg21.link/LWG4015>`__","LWG 3973 broke const overloads of ``std::optional`` monadic operations","2025-11 (Kona)","","","`#171310 <https://github.com/llvm/llvm-project/issues/171310>`__",""
 "`LWG4020 <https://wg21.link/LWG4020>`__","``extents::index-cast`` weirdness","2025-11 (Kona)","|Complete|","23","`#171311 <https://github.com/llvm/llvm-project/issues/171311>`__",""
diff --git a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
index e4c62f3d3df5c..bb6bdbd861e4d 100644
--- a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
+++ b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
@@ -30,24 +30,28 @@ struct A {
   typedef T element_type;
   element_type* t_;
 
-  A(element_type* t) : t_(t) {}
+  TEST_CONSTEXPR_CXX20 A(element_type* t) : t_(t) {}
 
-  static A pointer_to(typename std::conditional<std::is_void<element_type>::value, nat, element_type>::type& et) {
+  TEST_CONSTEXPR_CXX20 static A
+  pointer_to(typename std::conditional<std::is_void<element_type>::value, nat, element_type>::type& et) {
     return A(&et);
   }
 };
 
 template <class Pointer>
-void test() {
+TEST_CONSTEXPR_CXX20 bool test() {
   typename Pointer::element_type obj;
   static_assert(std::is_same<Pointer, decltype(std::pointer_traits<Pointer>::pointer_to(obj))>::value, "");
   Pointer p = std::pointer_traits<Pointer>::pointer_to(obj);
   assert(p.t_ == &obj);
+  return true;
 }
 
 int main(int, char**) {
   test<A<int> >();
   test<A<long> >();
+  static_assert(test<A<int> >());
+  static_assert(test<A<long> >());
   { (std::pointer_traits<A<void> >::element_type)0; }
 
   return 0;

>From d8fef5f39e7650a494106da09d2a44e4172a995b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 4 Jul 2026 14:50:18 +0800
Subject: [PATCH 2/2] [libc++] Mark LWG3454 as resolved

---
 .../pointer.traits.functions/pointer_to.pass.cpp              | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
index bb6bdbd861e4d..1715d0dd031fe 100644
--- a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
+++ b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
@@ -50,8 +50,8 @@ TEST_CONSTEXPR_CXX20 bool test() {
 int main(int, char**) {
   test<A<int> >();
   test<A<long> >();
-  static_assert(test<A<int> >());
-  static_assert(test<A<long> >());
+  static_assert(test<A<int> >(), "");
+  static_assert(test<A<long> >(), "");
   { (std::pointer_traits<A<void> >::element_type)0; }
 
   return 0;


From libcxx-commits at lists.llvm.org  Sat Jul  4 00:09:51 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Sat, 04 Jul 2026 00:09:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3454 as resolved (PR
	#207487)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207487@github.com>
Message-ID: <6a48b1bf.a151105c.30c1b9.40e8@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/207487

>From c1bb4994c8cc823a0dc98d0181a8eb8130fb9bb1 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 4 Jul 2026 13:45:59 +0800
Subject: [PATCH 1/3] [libc++] Mark LWG3454 as resolved

---
 libcxx/docs/Status/Cxx26Issues.csv                     |  2 +-
 .../pointer.traits.functions/pointer_to.pass.cpp       | 10 +++++++---
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 3f2a446f7e1f3..30042d4465d93 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -149,7 +149,7 @@
 "`LWG2991 <https://wg21.link/LWG2991>`__","``variant copy`` constructor missing ``noexcept(see below)``","2025-11 (Kona)","","","`#171301 <https://github.com/llvm/llvm-project/issues/171301>`__",""
 "`LWG3090 <https://wg21.link/LWG3090>`__","What is §[time.duration.cons]p4's ""no overflow is induced in the conversion"" intended to mean?","2025-11 (Kona)","","","`#171302 <https://github.com/llvm/llvm-project/issues/171302>`__",""
 "`LWG3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","2025-11 (Kona)","|Complete|","16","`#105356 <https://github.com/llvm/llvm-project/issues/105356>`__",""
-"`LWG3454 <https://wg21.link/LWG3454>`__","``pointer_traits::pointer_to`` should be ``constexpr``","2025-11 (Kona)","","","`#171307 <https://github.com/llvm/llvm-project/issues/171307>`__",""
+"`LWG3454 <https://wg21.link/LWG3454>`__","``pointer_traits::pointer_to`` should be ``constexpr``","2025-11 (Kona)","|Complete|","16","`#171307 <https://github.com/llvm/llvm-project/issues/171307>`__",""
 "`LWG3627 <https://wg21.link/LWG3627>`__","Inconsistent specifications for ``std::make_optional`` overloads","2025-11 (Kona)","|Complete|","22","`#171309 <https://github.com/llvm/llvm-project/issues/171309>`__",""
 "`LWG4015 <https://wg21.link/LWG4015>`__","LWG 3973 broke const overloads of ``std::optional`` monadic operations","2025-11 (Kona)","","","`#171310 <https://github.com/llvm/llvm-project/issues/171310>`__",""
 "`LWG4020 <https://wg21.link/LWG4020>`__","``extents::index-cast`` weirdness","2025-11 (Kona)","|Complete|","23","`#171311 <https://github.com/llvm/llvm-project/issues/171311>`__",""
diff --git a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
index e4c62f3d3df5c..bb6bdbd861e4d 100644
--- a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
+++ b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
@@ -30,24 +30,28 @@ struct A {
   typedef T element_type;
   element_type* t_;
 
-  A(element_type* t) : t_(t) {}
+  TEST_CONSTEXPR_CXX20 A(element_type* t) : t_(t) {}
 
-  static A pointer_to(typename std::conditional<std::is_void<element_type>::value, nat, element_type>::type& et) {
+  TEST_CONSTEXPR_CXX20 static A
+  pointer_to(typename std::conditional<std::is_void<element_type>::value, nat, element_type>::type& et) {
     return A(&et);
   }
 };
 
 template <class Pointer>
-void test() {
+TEST_CONSTEXPR_CXX20 bool test() {
   typename Pointer::element_type obj;
   static_assert(std::is_same<Pointer, decltype(std::pointer_traits<Pointer>::pointer_to(obj))>::value, "");
   Pointer p = std::pointer_traits<Pointer>::pointer_to(obj);
   assert(p.t_ == &obj);
+  return true;
 }
 
 int main(int, char**) {
   test<A<int> >();
   test<A<long> >();
+  static_assert(test<A<int> >());
+  static_assert(test<A<long> >());
   { (std::pointer_traits<A<void> >::element_type)0; }
 
   return 0;

>From d8fef5f39e7650a494106da09d2a44e4172a995b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 4 Jul 2026 14:50:18 +0800
Subject: [PATCH 2/3] [libc++] Mark LWG3454 as resolved

---
 .../pointer.traits.functions/pointer_to.pass.cpp              | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
index bb6bdbd861e4d..1715d0dd031fe 100644
--- a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
+++ b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
@@ -50,8 +50,8 @@ TEST_CONSTEXPR_CXX20 bool test() {
 int main(int, char**) {
   test<A<int> >();
   test<A<long> >();
-  static_assert(test<A<int> >());
-  static_assert(test<A<long> >());
+  static_assert(test<A<int> >(), "");
+  static_assert(test<A<long> >(), "");
   { (std::pointer_traits<A<void> >::element_type)0; }
 
   return 0;

>From c62c21d07b7ed42057ceff95f0bd600e256fdd3f Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 4 Jul 2026 15:09:32 +0800
Subject: [PATCH 3/3] fix ci

---
 .../pointer.traits.functions/pointer_to.pass.cpp           | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
index 1715d0dd031fe..fb3b76822f570 100644
--- a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
+++ b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
@@ -50,9 +50,12 @@ TEST_CONSTEXPR_CXX20 bool test() {
 int main(int, char**) {
   test<A<int> >();
   test<A<long> >();
-  static_assert(test<A<int> >(), "");
-  static_assert(test<A<long> >(), "");
   { (std::pointer_traits<A<void> >::element_type)0; }
 
+#if TEST_STD_VER >= 20
+  static_assert(test<A<int> >());
+  static_assert(test<A<long> >());
+#endif // TEST_STD_VER
+
   return 0;
 }


From libcxx-commits at lists.llvm.org  Sat Jul  4 00:37:28 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 00:37:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't add #pragma clang
	attribute with GCC (PR #206989)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206989@github.com>
Message-ID: <6a48b838.a151105c.30c1b9.7704@mx.google.com>


philnik777 wrote:

> How will implicit ABI annotations work in non-Clang-based compilers?

It won't work. See below.

> Do you plan to use some other machinery to add hidden visibility, ODR ABI tags, etc?

No. As Louis already mentioned, we have some big problems on GCC currently, since the only way to make our ABI guarantees there is to add `[[gnu::always_inline]]` everywhere. This has always been a suboptimal choice, and bigger features like `std::format` are completely unusable to the point where we disable tests because they basically can't be compiled with GCC (unless you're willing to wait minutes and have dozens of gigs of ram per test). The move to implicit annotations on Clang is in part to fix this issue and be able to provide all the guarantees we want on Clang while being able to walk back our guarantees in exchange for actual usability on GCC. If GCC adds support for any features we need to improve our ABI guarantees I'm of course happy to bring them back. In case anybody who reads this and cares, these are some important ones:
- `[[clang::type_visibility]]`: This would allow us to make functions have the configured visibility (i.e. by default `default`, with `-fvisibility=X` X etc.).
- `[[clang::exclude_from_explicit_instantiation]]`: Removes the need for `[[gnu::always_inline]]` on classes exported from the ABI
- `#pragma clang attribute`: Needed to force hidden visibility on everything we want as well as ABI tagging everything automatically.

> However, this makes me realize that by adopting the pragmas approach, we probably regressed that support even more on GCC, since we wouldn't be applying ABI tags anymore to functions. @philnik777 is that the case? I don't remember discussing that when we reviewed #193045.

I'm pretty sure we talked about this. Yes, we're "losing" the ABI tags (we've `always_inline`d everything anyways, so they didn't make any difference whatsoever before). I'm also pretty sure that we concluded that `[[gnu::always_inline]]` everywhere is worse than not ABI tagging everything.

https://github.com/llvm/llvm-project/pull/206989

From libcxx-commits at lists.llvm.org  Sat Jul  4 00:53:28 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 00:53:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a48bbf8.abb10101.208f3e.6bb8@mx.google.com>


================

----------------
philnik777 wrote:

I think you can get rid of the `<__algorithm/count.h>` include as well.

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Sat Jul  4 00:53:28 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 00:53:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a48bbf8.bd93e731.b068b.bb90@mx.google.com>


================
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// <text_encoding>
+
+// REQUIRES: std-at-least-c++26
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=40000000
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=1000000000
+
+// Our table has 882 aliases exactly, test to make sure that number matches with the total alias count
+// Internally, there are 884 entries with 2 reserved for id::unknown and id::other
+#include <cassert>
+#include <ranges>
+#include <text_encoding>
+
+#include "test_text_encoding.h"
+
+constexpr bool test() {
+  long long sum = 0;
+  for (auto& enc : unique_encoding_data) {
+    std::text_encoding te{std::text_encoding::id(enc.mib)};
+
+    sum += std::ranges::size(te.aliases());
+  }
+
+  assert(sum == 882);
----------------
philnik777 wrote:

Can we test directly against `__num_aliases`? That's what we care is correct. That also means we don't have to modify the test if the header content gets updated correctly (unless that's somehow part of the ABI? I hope not?).

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Sat Jul  4 00:53:28 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 00:53:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a48bbf8.481afadf.134314.f731@mx.google.com>


================

----------------
philnik777 wrote:

Why did this change?

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Sat Jul  4 00:53:28 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 00:53:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a48bbf8.30759430.1908d3.4af2@mx.google.com>


================
@@ -792,8 +792,8 @@ private:
       "11\0CP50220\0csCP50220\0";
 
   struct __offset_table {
-    constexpr static unsigned long long __num_aliases =
-        std::count(__aliases_string, __aliases_string + sizeof(__aliases_string), '\0') + 1;
+    constexpr static unsigned long long __num_aliases = 884;
+    // +2 for id::unknown, id::other sentinels
----------------
philnik777 wrote:

I feel like that comment would be better in the test. I don't expect anybody will count the stuff above manually and then wonder why they're off by two.

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Sat Jul  4 00:59:54 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 00:59:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Split the multidimensional
 algorithms out of uninitialized_algorithms.h (PR #207447)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207447@github.com>
Message-ID: <6a48bd7a.30759430.1908d3.b5ca@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207447

>From a9408c940b1ba59bc8bc3adcefc6b758feac59b1 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 21:00:01 +0200
Subject: [PATCH 1/2] [libc++] Split the multidimensional algorithms out of
 uninitialized_algorithms.h

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__memory/shared_ptr.h          |   1 +
 .../__memory/uninitialized_algorithms.h       | 159 +-------------
 ...ninitialized_multidimensional_algorithms.h | 194 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   3 +
 5 files changed, 200 insertions(+), 158 deletions(-)
 create mode 100644 libcxx/include/__memory/uninitialized_multidimensional_algorithms.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index d65e66e221766..b40f586161e62 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -618,6 +618,7 @@ set(files
   __memory/temp_value.h
   __memory/temporary_buffer.h
   __memory/uninitialized_algorithms.h
+  __memory/uninitialized_multidimensional_algorithms.h
   __memory/unique_ptr.h
   __memory/unique_temporary_buffer.h
   __memory/uses_allocator.h
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 750d253912ab1..ebd542ba14d01 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -33,6 +33,7 @@
 #include <__memory/pointer_traits.h>
 #include <__memory/shared_count.h>
 #include <__memory/uninitialized_algorithms.h>
+#include <__memory/uninitialized_multidimensional_algorithms.h>
 #include <__memory/unique_ptr.h>
 #include <__type_traits/add_reference.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..d4cd49a1deb6e 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -11,11 +11,10 @@
 #define _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
 
 #include <__algorithm/copy.h>
-#include <__algorithm/move.h>
+#include <__algorithm/in_out_result.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__algorithm/unwrap_range.h>
 #include <__config>
-#include <__cstddef/size_t.h>
 #include <__fwd/memory.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/reverse_iterator.h>
@@ -25,15 +24,12 @@
 #include <__memory/destroy.h>
 #include <__memory/pointer_traits.h>
 #include <__type_traits/enable_if.h>
-#include <__type_traits/extent.h>
-#include <__type_traits/is_array.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/remove_const.h>
-#include <__type_traits/remove_extent.h>
 #include <__utility/exception_guard.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
@@ -295,159 +291,6 @@ uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofir
   return {std::move(__result.__in_), std::move(__result.__out_)};
 }
 
-// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
-// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
-// destruction. If elements are themselves C-style arrays, they are recursively destroyed
-// in the same manner.
-//
-// This function assumes that destructors do not throw, and that the allocator is bound to
-// the correct type.
-template <class _Alloc,
-          class _BidirIter,
-          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
-                "The allocator should already be rebound to the correct type");
-
-  if (__first == __last)
-    return;
-
-  if constexpr (is_array_v<_ValueType>) {
-    static_assert(!__is_unbounded_array_v<_ValueType>,
-                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
-
-    using _Element = remove_extent_t<_ValueType>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    do {
-      --__last;
-      decltype(auto) __array = *__last;
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
-    } while (__last != __first);
-  } else {
-    do {
-      --__last;
-      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
-    } while (__last != __first);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method.
-//
-// If the object being constructed is an array, each element of the array is allocator-constructed,
-// recursively. If an exception is thrown during the construction of an array, the initialized
-// elements are destroyed in reverse order of initialization using allocator destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method, passing along
-// the provided argument.
-//
-// If the object being constructed is an array, the argument is also assumed to be an array. Each
-// each element of the array being constructed is allocator-constructed from the corresponding
-// element of the argument array. If an exception is thrown during the construction of an array,
-// the initialized elements are destroyed in reverse order of initialization using allocator
-// destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp, class _Arg>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    static_assert(is_array_v<_Arg>,
-                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
-                  "trying to construct an array.");
-
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
-  }
-}
-
-// Given a range starting at it and containing n elements, initializes each element in the
-// range from left to right using the construct method of the allocator (rebound to the
-// correct type).
-//
-// If an exception is thrown, the initialized elements are destroyed in reverse order of
-// initialization using allocator_traits destruction. If the elements in the range are C-style
-// arrays, they are initialized element-wise using allocator construction, and recursively so.
-template <class _Alloc,
-          class _BidirIter,
-          class _Tp,
-          class _Size = typename iterator_traits<_BidirIter>::difference_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
-  }
-  __guard.__complete();
-}
-
-// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
-// to the allocator's construct method, which results in value initialization.
-template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
-  }
-  __guard.__complete();
-}
-
 #endif // _LIBCPP_STD_VER >= 17
 
 template <class _Alloc, class _Iter>
diff --git a/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
new file mode 100644
index 0000000000000..2496ce6af1e15
--- /dev/null
+++ b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
@@ -0,0 +1,194 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+#define _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_traits.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/extent.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/remove_extent.h>
+#include <__utility/exception_guard.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
+// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
+// destruction. If elements are themselves C-style arrays, they are recursively destroyed
+// in the same manner.
+//
+// This function assumes that destructors do not throw, and that the allocator is bound to
+// the correct type.
+template <class _Alloc,
+          class _BidirIter,
+          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
+                "The allocator should already be rebound to the correct type");
+
+  if (__first == __last)
+    return;
+
+  if constexpr (is_array_v<_ValueType>) {
+    static_assert(!__is_unbounded_array_v<_ValueType>,
+                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
+
+    using _Element = remove_extent_t<_ValueType>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    do {
+      --__last;
+      decltype(auto) __array = *__last;
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
+    } while (__last != __first);
+  } else {
+    do {
+      --__last;
+      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
+    } while (__last != __first);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method.
+//
+// If the object being constructed is an array, each element of the array is allocator-constructed,
+// recursively. If an exception is thrown during the construction of an array, the initialized
+// elements are destroyed in reverse order of initialization using allocator destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method, passing along
+// the provided argument.
+//
+// If the object being constructed is an array, the argument is also assumed to be an array. Each
+// each element of the array being constructed is allocator-constructed from the corresponding
+// element of the argument array. If an exception is thrown during the construction of an array,
+// the initialized elements are destroyed in reverse order of initialization using allocator
+// destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp, class _Arg>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    static_assert(is_array_v<_Arg>,
+                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
+                  "trying to construct an array.");
+
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
+  }
+}
+
+// Given a range starting at it and containing n elements, initializes each element in the
+// range from left to right using the construct method of the allocator (rebound to the
+// correct type).
+//
+// If an exception is thrown, the initialized elements are destroyed in reverse order of
+// initialization using allocator_traits destruction. If the elements in the range are C-style
+// arrays, they are initialized element-wise using allocator construction, and recursively so.
+template <class _Alloc,
+          class _BidirIter,
+          class _Tp,
+          class _Size = typename iterator_traits<_BidirIter>::difference_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
+  }
+  __guard.__complete();
+}
+
+// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
+// to the allocator's construct method, which results in value initialization.
+template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
+  }
+  __guard.__complete();
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3efceae54599d..fc987f7bab93d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1702,6 +1702,9 @@ module std {
       export std.memory.destroy
       export std.utility.pair
     }
+    module uninitialized_multidimensional_algorithms {
+      header "__memory/uninitialized_algorithms.h"
+    }
     module unique_ptr {
       header "__memory/unique_ptr.h"
       export std.functional.hash

>From c3a3e713dee8ba2e32a918c86f2a434e6a7f0b57 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 21:56:01 +0200
Subject: [PATCH 2/2] [libc++] Optimize {ranges::,}uninitialized_copy

This essentially moves the `memmove` optimization from
`__uninitialized_allocator_copy` to `__uninitialized_copy` and makes use
of the latter in the allocator version.
---
 .../ranges_uninitialized_algorithms.h         | 19 +++-
 .../__memory/uninitialized_algorithms.h       | 97 +++++++++++--------
 libcxx/include/module.modulemap.in            |  2 +-
 3 files changed, 75 insertions(+), 43 deletions(-)

diff --git a/libcxx/include/__memory/ranges_uninitialized_algorithms.h b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
index 796e6175af632..201d778056679 100644
--- a/libcxx/include/__memory/ranges_uninitialized_algorithms.h
+++ b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___MEMORY_RANGES_UNINITIALIZED_ALGORITHMS_H
 
 #include <__algorithm/in_out_result.h>
+#include <__algorithm/min.h>
 #include <__concepts/constructible.h>
 #include <__config>
 #include <__iterator/concepts.h>
@@ -18,12 +19,14 @@
 #include <__iterator/iter_move.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/readable_traits.h>
+#include <__memory/addressof.h>
 #include <__memory/concepts.h>
 #include <__memory/uninitialized_algorithms.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
 #include <__ranges/dangling.h>
 #include <__type_traits/remove_reference.h>
+#include <__utility/exception_guard.h>
 #include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -171,9 +174,19 @@ struct __uninitialized_copy {
   operator()(_InputIterator __ifirst, _Sentinel1 __ilast, _OutputIterator __ofirst, _Sentinel2 __olast) const {
     using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
 
-    auto __stop_copying = [&__olast](auto&& __out_iter) -> bool { return __out_iter == __olast; };
-    return std::__uninitialized_copy<_ValueType>(
-        std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __stop_copying);
+    if constexpr (sized_sentinel_for<_Sentinel1, _InputIterator> && random_access_iterator<_InputIterator> &&
+                  sized_sentinel_for<_Sentinel2, _OutputIterator>) {
+      auto __len = std::min(__ilast - __ifirst, __olast - __ofirst);
+      return std::__uninitialized_copy<_ValueType>(__ifirst, __ifirst + __len, __ofirst);
+    } else {
+      _OutputIterator __idx = __ofirst;
+      auto __guard          = std::__make_exception_guard([&] { std::__destroy(__ofirst, __idx); });
+      for (; __ifirst != __ilast && __idx != __olast; ++__ifirst, (void)++__idx)
+        ::new (static_cast<void*>(std::addressof(*__idx))) _ValueType(*__ifirst);
+      __guard.__complete();
+
+      return {std::move(__ifirst), std::move(__idx)};
+    }
   }
 
   template <input_range _InputRange, __nothrow_forward_range _OutputRange>
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index d4cd49a1deb6e..f5ebe847f4054 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -10,7 +10,6 @@
 #ifndef _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
 #define _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
 
-#include <__algorithm/copy.h>
 #include <__algorithm/in_out_result.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__algorithm/unwrap_range.h>
@@ -23,12 +22,15 @@
 #include <__memory/construct_at.h>
 #include <__memory/destroy.h>
 #include <__memory/pointer_traits.h>
+#include <__string/constexpr_c_functions.h>
 #include <__type_traits/enable_if.h>
+#include <__type_traits/is_always_bitcastable.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
 #include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/is_volatile.h>
 #include <__type_traits/remove_const.h>
 #include <__utility/exception_guard.h>
 #include <__utility/move.h>
@@ -52,25 +54,47 @@ struct __always_false {
 
 // uninitialized_copy
 
-template <class _ValueType, class _InputIterator, class _Sentinel1, class _ForwardIterator, class _EndPredicate>
+template <class _ValueType, class _InputIterator, class _Sentinel1, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __in_out_result<_InputIterator, _ForwardIterator>
-__uninitialized_copy(
-    _InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst, _EndPredicate __stop_copying) {
+__uninitialized_copy(_InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst) {
   _ForwardIterator __idx = __ofirst;
   auto __guard           = std::__make_exception_guard([&] { std::__destroy(__ofirst, __idx); });
-  for (; __ifirst != __ilast && !__stop_copying(__idx); ++__ifirst, (void)++__idx)
+  for (; __ifirst != __ilast; ++__ifirst, (void)++__idx)
     ::new (static_cast<void*>(std::addressof(*__idx))) _ValueType(*__ifirst);
   __guard.__complete();
 
   return {std::move(__ifirst), std::move(__idx)};
 }
 
+template <
+    class _ValueType,
+    class _In,
+    class _Out,
+    __enable_if_t<is_trivially_constructible<_Out, const _In&>::value && __is_always_bitcastable<_In, _Out>::value &&
+                      !is_volatile<_In>::value && !is_volatile<_Out>::value,
+                  int> = 0>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_In*, _Out*>
+__uninitialized_copy(_In* __first, _In* __last, _Out* __result) {
+  if (__libcpp_is_constant_evaluated()) {
+    while (__first != __last) {
+      std::__construct_at(__result, *__first);
+      ++__first;
+      ++__result;
+    }
+    return {__first, __result};
+  } else {
+    // __constexpr_memmove only supports constant evaluation assignment, not construction
+    auto __count = __last - __first;
+    std::__constexpr_memmove(__result, __first, __element_count(__count));
+    return {__last, __result + __count};
+  }
+}
+
 template <class _InputIterator, class _ForwardIterator>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
 uninitialized_copy(_InputIterator __ifirst, _InputIterator __ilast, _ForwardIterator __ofirst) {
-  typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;
-  auto __result = std::__uninitialized_copy<_ValueType>(
-      std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __always_false());
+  auto __result = std::__uninitialized_copy<__iterator_value_type<_ForwardIterator>>(
+      std::move(__ifirst), std::move(__ilast), std::move(__ofirst));
   return std::move(__result.__out_);
 }
 
@@ -310,16 +334,26 @@ class _AllocatorDestroyRangeReverse {
   _Iter& __last_;
 };
 
+template <class _Alloc, class _Type>
+inline const bool __allocator_has_trivial_copy_construct_v = !__has_construct_v<_Alloc, _Type*, const _Type&>;
+
+template <class _Type>
+inline const bool __allocator_has_trivial_copy_construct_v<allocator<_Type>, _Type> = true;
+
 // Copy-construct [__first1, __last1) in [__first2, __first2 + N), where N is distance(__first1, __last1).
 //
 // The caller has to ensure that __first2 can hold at least N uninitialized elements. If an exception is thrown the
 // already copied elements are destroyed in reverse order of their construction.
-template <class _Alloc, class _Iter1, class _Sent1, class _Iter2>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter2
-__uninitialized_allocator_copy_impl(_Alloc& __alloc, _Iter1 __first1, _Sent1 __last1, _Iter2 __first2) {
+template <class _Alloc,
+          class _InIter,
+          class _Sent,
+          class _OutIter,
+          __enable_if_t<!__allocator_has_trivial_copy_construct_v<_Alloc, __iterator_value_type<_InIter> >, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutIter
+__uninitialized_allocator_copy_impl(_Alloc& __alloc, _InIter __first1, _Sent __last1, _OutIter __first2) {
   auto __destruct_first = __first2;
   auto __guard =
-      std::__make_exception_guard(_AllocatorDestroyRangeReverse<_Alloc, _Iter2>(__alloc, __destruct_first, __first2));
+      std::__make_exception_guard(_AllocatorDestroyRangeReverse<_Alloc, _OutIter>(__alloc, __destruct_first, __first2));
   while (__first1 != __last1) {
     allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__first2), *__first1);
     ++__first1;
@@ -329,36 +363,21 @@ __uninitialized_allocator_copy_impl(_Alloc& __alloc, _Iter1 __first1, _Sent1 __l
   return __first2;
 }
 
-template <class _Alloc, class _Type>
-inline const bool __allocator_has_trivial_copy_construct_v = !__has_construct_v<_Alloc, _Type*, const _Type&>;
-
-template <class _Type>
-inline const bool __allocator_has_trivial_copy_construct_v<allocator<_Type>, _Type> = true;
-
 template <class _Alloc,
-          class _In,
-          class _Out,
-          __enable_if_t<is_trivially_copy_constructible<_In>::value && is_trivially_assignable<_Out&, _In&>::value &&
-                            is_same<__remove_const_t<_In>, __remove_const_t<_Out> >::value &&
-                            __allocator_has_trivial_copy_construct_v<_Alloc, _In>,
-                        int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Out*
-__uninitialized_allocator_copy_impl(_Alloc&, _In* __first1, _In* __last1, _Out* __first2) {
-  if (__libcpp_is_constant_evaluated()) {
-    while (__first1 != __last1) {
-      std::__construct_at(std::__to_address(__first2), *__first1);
-      ++__first1;
-      ++__first2;
-    }
-    return __first2;
-  } else {
-    return std::copy(__first1, __last1, __first2);
-  }
+          class _InIter,
+          class _Sent,
+          class _OutIter,
+          __enable_if_t<__allocator_has_trivial_copy_construct_v<_Alloc, __iterator_value_type<_OutIter> >, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutIter
+__uninitialized_allocator_copy_impl(_Alloc&, _InIter __first, _Sent __last, _OutIter __result) {
+  return std::__uninitialized_copy<__iterator_value_type<_OutIter>>(
+             std::move(__first), std::move(__last), std::move(__result))
+      .__out_;
 }
 
-template <class _Alloc, class _Iter1, class _Sent1, class _Iter2>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter2
-__uninitialized_allocator_copy(_Alloc& __alloc, _Iter1 __first1, _Sent1 __last1, _Iter2 __first2) {
+template <class _Alloc, class _InIter, class _Sent, class _OutIter>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutIter
+__uninitialized_allocator_copy(_Alloc& __alloc, _InIter __first1, _Sent __last1, _OutIter __first2) {
   auto __unwrapped_range = std::__unwrap_range(std::move(__first1), std::move(__last1));
   auto __result          = std::__uninitialized_allocator_copy_impl(
       __alloc, std::move(__unwrapped_range.first), std::move(__unwrapped_range.second), std::__unwrap_iter(__first2));
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index fc987f7bab93d..f4cde849a8773 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1703,7 +1703,7 @@ module std {
       export std.utility.pair
     }
     module uninitialized_multidimensional_algorithms {
-      header "__memory/uninitialized_algorithms.h"
+      header "__memory/uninitialized_multidimensional_algorithms.h"
     }
     module unique_ptr {
       header "__memory/unique_ptr.h"


From libcxx-commits at lists.llvm.org  Sat Jul  4 01:15:35 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 01:15:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Split the multidimensional
 algorithms out of uninitialized_algorithms.h (PR #207447)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207447@github.com>
Message-ID: <6a48c127.c56fb102.11841f.fefc@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207447

>From 34584814a1eb7cbd4987a34d06404b2a12d0cc21 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 21:00:01 +0200
Subject: [PATCH] [libc++] Split the multidimensional algorithms out of
 uninitialized_algorithms.h

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__memory/shared_ptr.h          |   1 +
 .../__memory/uninitialized_algorithms.h       | 159 +-------------
 ...ninitialized_multidimensional_algorithms.h | 194 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   3 +
 5 files changed, 200 insertions(+), 158 deletions(-)
 create mode 100644 libcxx/include/__memory/uninitialized_multidimensional_algorithms.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index d65e66e221766..b40f586161e62 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -618,6 +618,7 @@ set(files
   __memory/temp_value.h
   __memory/temporary_buffer.h
   __memory/uninitialized_algorithms.h
+  __memory/uninitialized_multidimensional_algorithms.h
   __memory/unique_ptr.h
   __memory/unique_temporary_buffer.h
   __memory/uses_allocator.h
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 750d253912ab1..ebd542ba14d01 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -33,6 +33,7 @@
 #include <__memory/pointer_traits.h>
 #include <__memory/shared_count.h>
 #include <__memory/uninitialized_algorithms.h>
+#include <__memory/uninitialized_multidimensional_algorithms.h>
 #include <__memory/unique_ptr.h>
 #include <__type_traits/add_reference.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..d4cd49a1deb6e 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -11,11 +11,10 @@
 #define _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
 
 #include <__algorithm/copy.h>
-#include <__algorithm/move.h>
+#include <__algorithm/in_out_result.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__algorithm/unwrap_range.h>
 #include <__config>
-#include <__cstddef/size_t.h>
 #include <__fwd/memory.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/reverse_iterator.h>
@@ -25,15 +24,12 @@
 #include <__memory/destroy.h>
 #include <__memory/pointer_traits.h>
 #include <__type_traits/enable_if.h>
-#include <__type_traits/extent.h>
-#include <__type_traits/is_array.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/remove_const.h>
-#include <__type_traits/remove_extent.h>
 #include <__utility/exception_guard.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
@@ -295,159 +291,6 @@ uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofir
   return {std::move(__result.__in_), std::move(__result.__out_)};
 }
 
-// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
-// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
-// destruction. If elements are themselves C-style arrays, they are recursively destroyed
-// in the same manner.
-//
-// This function assumes that destructors do not throw, and that the allocator is bound to
-// the correct type.
-template <class _Alloc,
-          class _BidirIter,
-          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
-                "The allocator should already be rebound to the correct type");
-
-  if (__first == __last)
-    return;
-
-  if constexpr (is_array_v<_ValueType>) {
-    static_assert(!__is_unbounded_array_v<_ValueType>,
-                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
-
-    using _Element = remove_extent_t<_ValueType>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    do {
-      --__last;
-      decltype(auto) __array = *__last;
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
-    } while (__last != __first);
-  } else {
-    do {
-      --__last;
-      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
-    } while (__last != __first);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method.
-//
-// If the object being constructed is an array, each element of the array is allocator-constructed,
-// recursively. If an exception is thrown during the construction of an array, the initialized
-// elements are destroyed in reverse order of initialization using allocator destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method, passing along
-// the provided argument.
-//
-// If the object being constructed is an array, the argument is also assumed to be an array. Each
-// each element of the array being constructed is allocator-constructed from the corresponding
-// element of the argument array. If an exception is thrown during the construction of an array,
-// the initialized elements are destroyed in reverse order of initialization using allocator
-// destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp, class _Arg>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    static_assert(is_array_v<_Arg>,
-                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
-                  "trying to construct an array.");
-
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
-  }
-}
-
-// Given a range starting at it and containing n elements, initializes each element in the
-// range from left to right using the construct method of the allocator (rebound to the
-// correct type).
-//
-// If an exception is thrown, the initialized elements are destroyed in reverse order of
-// initialization using allocator_traits destruction. If the elements in the range are C-style
-// arrays, they are initialized element-wise using allocator construction, and recursively so.
-template <class _Alloc,
-          class _BidirIter,
-          class _Tp,
-          class _Size = typename iterator_traits<_BidirIter>::difference_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
-  }
-  __guard.__complete();
-}
-
-// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
-// to the allocator's construct method, which results in value initialization.
-template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
-  }
-  __guard.__complete();
-}
-
 #endif // _LIBCPP_STD_VER >= 17
 
 template <class _Alloc, class _Iter>
diff --git a/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
new file mode 100644
index 0000000000000..2496ce6af1e15
--- /dev/null
+++ b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
@@ -0,0 +1,194 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+#define _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_traits.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/extent.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/remove_extent.h>
+#include <__utility/exception_guard.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
+// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
+// destruction. If elements are themselves C-style arrays, they are recursively destroyed
+// in the same manner.
+//
+// This function assumes that destructors do not throw, and that the allocator is bound to
+// the correct type.
+template <class _Alloc,
+          class _BidirIter,
+          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
+                "The allocator should already be rebound to the correct type");
+
+  if (__first == __last)
+    return;
+
+  if constexpr (is_array_v<_ValueType>) {
+    static_assert(!__is_unbounded_array_v<_ValueType>,
+                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
+
+    using _Element = remove_extent_t<_ValueType>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    do {
+      --__last;
+      decltype(auto) __array = *__last;
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
+    } while (__last != __first);
+  } else {
+    do {
+      --__last;
+      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
+    } while (__last != __first);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method.
+//
+// If the object being constructed is an array, each element of the array is allocator-constructed,
+// recursively. If an exception is thrown during the construction of an array, the initialized
+// elements are destroyed in reverse order of initialization using allocator destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method, passing along
+// the provided argument.
+//
+// If the object being constructed is an array, the argument is also assumed to be an array. Each
+// each element of the array being constructed is allocator-constructed from the corresponding
+// element of the argument array. If an exception is thrown during the construction of an array,
+// the initialized elements are destroyed in reverse order of initialization using allocator
+// destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp, class _Arg>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    static_assert(is_array_v<_Arg>,
+                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
+                  "trying to construct an array.");
+
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
+  }
+}
+
+// Given a range starting at it and containing n elements, initializes each element in the
+// range from left to right using the construct method of the allocator (rebound to the
+// correct type).
+//
+// If an exception is thrown, the initialized elements are destroyed in reverse order of
+// initialization using allocator_traits destruction. If the elements in the range are C-style
+// arrays, they are initialized element-wise using allocator construction, and recursively so.
+template <class _Alloc,
+          class _BidirIter,
+          class _Tp,
+          class _Size = typename iterator_traits<_BidirIter>::difference_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
+  }
+  __guard.__complete();
+}
+
+// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
+// to the allocator's construct method, which results in value initialization.
+template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
+  }
+  __guard.__complete();
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3efceae54599d..fc987f7bab93d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1702,6 +1702,9 @@ module std {
       export std.memory.destroy
       export std.utility.pair
     }
+    module uninitialized_multidimensional_algorithms {
+      header "__memory/uninitialized_algorithms.h"
+    }
     module unique_ptr {
       header "__memory/unique_ptr.h"
       export std.functional.hash


From libcxx-commits at lists.llvm.org  Sat Jul  4 01:26:58 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 01:26:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a48c3d2.bd93e731.b068b.094c@mx.google.com>


================
@@ -1120,14 +1122,26 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointer __to) {
   pointer __old_last  = __layout_.__end_ptr();
   difference_type __n = __old_last - __to;
+#ifndef _LIBCPP_CXX03_LANG
----------------
philnik777 wrote:

We do. Multiple tests are using e.g. `MoveOnly`, and the one for `emplace` even `NotTriviallyRelocatable`.

https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Sat Jul  4 01:27:43 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 01:27:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a48c3ff.30759430.1908d3.c18e@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207384

>From 74c745b11c0a41958ff80dda22ef13cbe411f178 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 13:34:50 +0200
Subject: [PATCH] [libc++] Optimize vector::__move_range for trivially copyable
 types

This is a code size rather than a performance optimization.

For example, the number of instructions in `__insert_with_iter` is
almost cut in half.
---
 libcxx/include/__vector/vector.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..a272659bc2123 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -47,6 +47,7 @@
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__split_buffer>
+#include <__string/constexpr_c_functions.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_allocator.h>
@@ -57,6 +58,7 @@
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
+#include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/declval.h>
@@ -1120,6 +1122,17 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointer __to) {
   pointer __old_last  = __layout_.__end_ptr();
   difference_type __n = __old_last - __to;
+#ifndef _LIBCPP_CXX03_LANG
+  if constexpr (__allocator_has_trivial_move_construct_v<allocator_type, value_type> &&
+                is_trivially_copyable<value_type>::value) {
+    if (!__libcpp_is_constant_evaluated()) {
+      std::__constexpr_memmove(
+          std::__to_address(__to), std::__to_address(__from_s), __element_count(__from_e - __from_s));
+      __layout_.__set_bound_using_pointer(__to + (__from_e - __from_s));
+      return;
+    }
+  }
+#endif
   {
     pointer __i = __from_s + __n;
     _ConstructTransaction __tx(*this, __from_e - __i);


From libcxx-commits at lists.llvm.org  Sat Jul  4 01:33:03 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 01:33:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ThreadSafety] Add thread safety
 annotations for variadic std::scoped_lock (PR #204462)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204462@github.com>
Message-ID: <6a48c53f.27b3b003.1610ae.6c27@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/204462

From libcxx-commits at lists.llvm.org  Sat Jul  4 01:47:41 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 01:47:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a48c8ad.27b3b003.1610ae.af02@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From d93e557c304cea49a5cf654b3bf1710f5cee6855 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 1/5] [libc++] make std::atomic works with types with paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..5e1a7afea06a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From f17e51a372a7a1372fa63a572871939eec329145 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 2/5] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From ac972d414dd906c0e73a3e8b70697abfc537dafa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 3/5] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e1a7afea06a2..4477c821235de 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..b0f40e5a261ed 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -896,6 +896,7 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 33732fee8003a503f62524e1208aef02094b0bc6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 4/5] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From dc33657dd85cca1c1511d5cd7605bbcca05b35f8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 5/5] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);


From libcxx-commits at lists.llvm.org  Sat Jul  4 01:48:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 01:48:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a48c8c9.331483e7.5bc1e.1233@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From d93e557c304cea49a5cf654b3bf1710f5cee6855 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 1/6] [libc++] make std::atomic works with types with paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..5e1a7afea06a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From f17e51a372a7a1372fa63a572871939eec329145 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 2/6] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From ac972d414dd906c0e73a3e8b70697abfc537dafa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 3/6] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e1a7afea06a2..4477c821235de 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..b0f40e5a261ed 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -896,6 +896,7 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 33732fee8003a503f62524e1208aef02094b0bc6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 4/6] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From dc33657dd85cca1c1511d5cd7605bbcca05b35f8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 5/6] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From f133b64bf0da078d6ef4659603f8f92f12e7ec8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 6/6] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),


From libcxx-commits at lists.llvm.org  Sat Jul  4 01:48:32 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 01:48:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a48c8e0.e9d484b9.11ad58.5b63@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From d93e557c304cea49a5cf654b3bf1710f5cee6855 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 1/7] [libc++] make std::atomic works with types with paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..5e1a7afea06a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From f17e51a372a7a1372fa63a572871939eec329145 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 2/7] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From ac972d414dd906c0e73a3e8b70697abfc537dafa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 3/7] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e1a7afea06a2..4477c821235de 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..b0f40e5a261ed 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -896,6 +896,7 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 33732fee8003a503f62524e1208aef02094b0bc6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 4/7] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From dc33657dd85cca1c1511d5cd7605bbcca05b35f8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 5/7] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From f133b64bf0da078d6ef4659603f8f92f12e7ec8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 6/7] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From e943f4968a86c2c73214cdb22ccdf77b122428e5 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 7/7] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {


From libcxx-commits at lists.llvm.org  Sat Jul  4 02:07:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 02:07:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
Message-ID: <llvm.org/llvm/llvm-project/pull/207500@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207500

None

>From 697847f58cc4ac1ef05d5846971e4d715d66d1a6 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 4 Jul 2026 11:06:55 +0200
Subject: [PATCH] [libc++] Add
 __uninitialized_allocator_{fill,value_construct,move} and use them in vector

---
 .../__memory/uninitialized_algorithms.h       | 38 +++++++++++++++++++
 libcxx/include/__vector/vector.h              | 14 ++-----
 2 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..5426dae7ef114 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -450,6 +450,30 @@ __uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _B
 
 #endif // _LIBCPP_STD_VER >= 17
 
+template <class _Alloc, class _Iter, class _Sent, class _Tp>
+_LIBCPP_CONSTEXPR_SINCE_CXX20
+_Iter __uninitialized_allocator_fill(_Alloc& __alloc, _Iter __first, _Sent __last, const _Tp& __value) {
+  auto __iter = __first;
+
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __first, __iter); });
+  for (; __iter != __last; ++__iter)
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__iter), __value);
+  __guard.__complete();
+  return __iter;
+}
+
+template <class _Alloc, class _Iter, class _Sent>
+_LIBCPP_CONSTEXPR_SINCE_CXX20
+_Iter __uninitialized_allocator_value_construct(_Alloc& __alloc, _Iter __first, _Sent __last) {
+  auto __iter = __first;
+
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __first, __iter); });
+  for (; __iter != __last; ++__iter)
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__iter));
+  __guard.__complete();
+  return __iter;
+}
+
 template <class _Alloc, class _Iter>
 class _AllocatorDestroyRangeReverse {
 public:
@@ -528,6 +552,20 @@ inline const bool __allocator_has_trivial_move_construct_v = !__has_construct_v<
 template <class _Type>
 inline const bool __allocator_has_trivial_move_construct_v<allocator<_Type>, _Type> = true;
 
+template <class _Alloc, class _InIter, class _Sent, class _OutIter>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
+__uninitialized_allocator_move(_Alloc& __alloc, _InIter __first, _Sent __last, _OutIter __result) {
+  auto __destruct_first = __result;
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __destruct_first, __result); });
+  while (__first != __last) {
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__result), std::move(*__first));
+    ++__first;
+    ++__result;
+  }
+  __guard.__complete();
+  return {std::move(__first), std::move(__result)};
+}
+
 template <class _Alloc, class _Tp>
 inline const bool __allocator_has_trivial_destroy_v = !__has_destroy_v<_Alloc, _Tp*>;
 
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..603b5676e05fb 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -880,10 +880,7 @@ vector<_Tp, _Allocator>::__recommend(size_type __new_size) const {
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__construct_at_end(size_type __n) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos));
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_value_construct(__layout_.__alloc(), __tx.__pos_, __tx.__new_end_);
 }
 
 //  Copy constructs __n objects starting at __layout_.__end_ptr() from __x
@@ -896,10 +893,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline void
 vector<_Tp, _Allocator>::__construct_at_end(size_type __n, const_reference __x) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), __x);
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_fill(__layout_.__alloc(), __tx.__pos_, __tx.__new_end_, __x);
 }
 
 template <class _Tp, class _Allocator>
@@ -1123,9 +1117,7 @@ vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointe
   {
     pointer __i = __from_s + __n;
     _ConstructTransaction __tx(*this, __from_e - __i);
-    for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
-      __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
-    }
+    __tx.__pos_ = std::__uninitialized_allocator_move(__layout_.__alloc(), __i, __from_e, __tx.__pos_).__out_;
   }
   std::move_backward(__from_s, __from_s + __n, __old_last);
 }


From libcxx-commits at lists.llvm.org  Sat Jul  4 02:08:45 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 02:08:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a48cd9d.a87eb5dc.2cf3a1.467c@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions h -- libcxx/include/__memory/uninitialized_algorithms.h libcxx/include/__vector/vector.h --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 5426dae7e..39064cb97 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -463,8 +463,8 @@ _Iter __uninitialized_allocator_fill(_Alloc& __alloc, _Iter __first, _Sent __las
 }
 
 template <class _Alloc, class _Iter, class _Sent>
-_LIBCPP_CONSTEXPR_SINCE_CXX20
-_Iter __uninitialized_allocator_value_construct(_Alloc& __alloc, _Iter __first, _Sent __last) {
+_LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter
+__uninitialized_allocator_value_construct(_Alloc& __alloc, _Iter __first, _Sent __last) {
   auto __iter = __first;
 
   auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __first, __iter); });

``````````

</details>


https://github.com/llvm/llvm-project/pull/207500

From libcxx-commits at lists.llvm.org  Sat Jul  4 02:08:59 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 02:08:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Split the multidimensional
 algorithms out of uninitialized_algorithms.h (PR #207447)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207447@github.com>
Message-ID: <6a48cdab.faf16b37.1e0c59.72b3@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207447

>From 07d6b7d36a1b3bf9b4cbdf36f7713aea4c317e4d Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 21:00:01 +0200
Subject: [PATCH] [libc++] Split the multidimensional algorithms out of
 uninitialized_algorithms.h

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__memory/shared_ptr.h          |   1 +
 .../__memory/uninitialized_algorithms.h       | 159 +-------------
 ...ninitialized_multidimensional_algorithms.h | 194 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   3 +
 5 files changed, 200 insertions(+), 158 deletions(-)
 create mode 100644 libcxx/include/__memory/uninitialized_multidimensional_algorithms.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index d65e66e221766..b40f586161e62 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -618,6 +618,7 @@ set(files
   __memory/temp_value.h
   __memory/temporary_buffer.h
   __memory/uninitialized_algorithms.h
+  __memory/uninitialized_multidimensional_algorithms.h
   __memory/unique_ptr.h
   __memory/unique_temporary_buffer.h
   __memory/uses_allocator.h
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 750d253912ab1..ebd542ba14d01 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -33,6 +33,7 @@
 #include <__memory/pointer_traits.h>
 #include <__memory/shared_count.h>
 #include <__memory/uninitialized_algorithms.h>
+#include <__memory/uninitialized_multidimensional_algorithms.h>
 #include <__memory/unique_ptr.h>
 #include <__type_traits/add_reference.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..d4cd49a1deb6e 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -11,11 +11,10 @@
 #define _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
 
 #include <__algorithm/copy.h>
-#include <__algorithm/move.h>
+#include <__algorithm/in_out_result.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__algorithm/unwrap_range.h>
 #include <__config>
-#include <__cstddef/size_t.h>
 #include <__fwd/memory.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/reverse_iterator.h>
@@ -25,15 +24,12 @@
 #include <__memory/destroy.h>
 #include <__memory/pointer_traits.h>
 #include <__type_traits/enable_if.h>
-#include <__type_traits/extent.h>
-#include <__type_traits/is_array.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/remove_const.h>
-#include <__type_traits/remove_extent.h>
 #include <__utility/exception_guard.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
@@ -295,159 +291,6 @@ uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofir
   return {std::move(__result.__in_), std::move(__result.__out_)};
 }
 
-// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
-// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
-// destruction. If elements are themselves C-style arrays, they are recursively destroyed
-// in the same manner.
-//
-// This function assumes that destructors do not throw, and that the allocator is bound to
-// the correct type.
-template <class _Alloc,
-          class _BidirIter,
-          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
-                "The allocator should already be rebound to the correct type");
-
-  if (__first == __last)
-    return;
-
-  if constexpr (is_array_v<_ValueType>) {
-    static_assert(!__is_unbounded_array_v<_ValueType>,
-                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
-
-    using _Element = remove_extent_t<_ValueType>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    do {
-      --__last;
-      decltype(auto) __array = *__last;
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
-    } while (__last != __first);
-  } else {
-    do {
-      --__last;
-      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
-    } while (__last != __first);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method.
-//
-// If the object being constructed is an array, each element of the array is allocator-constructed,
-// recursively. If an exception is thrown during the construction of an array, the initialized
-// elements are destroyed in reverse order of initialization using allocator destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method, passing along
-// the provided argument.
-//
-// If the object being constructed is an array, the argument is also assumed to be an array. Each
-// each element of the array being constructed is allocator-constructed from the corresponding
-// element of the argument array. If an exception is thrown during the construction of an array,
-// the initialized elements are destroyed in reverse order of initialization using allocator
-// destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp, class _Arg>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    static_assert(is_array_v<_Arg>,
-                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
-                  "trying to construct an array.");
-
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
-  }
-}
-
-// Given a range starting at it and containing n elements, initializes each element in the
-// range from left to right using the construct method of the allocator (rebound to the
-// correct type).
-//
-// If an exception is thrown, the initialized elements are destroyed in reverse order of
-// initialization using allocator_traits destruction. If the elements in the range are C-style
-// arrays, they are initialized element-wise using allocator construction, and recursively so.
-template <class _Alloc,
-          class _BidirIter,
-          class _Tp,
-          class _Size = typename iterator_traits<_BidirIter>::difference_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
-  }
-  __guard.__complete();
-}
-
-// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
-// to the allocator's construct method, which results in value initialization.
-template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
-  }
-  __guard.__complete();
-}
-
 #endif // _LIBCPP_STD_VER >= 17
 
 template <class _Alloc, class _Iter>
diff --git a/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
new file mode 100644
index 0000000000000..2496ce6af1e15
--- /dev/null
+++ b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
@@ -0,0 +1,194 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+#define _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_traits.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/extent.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/remove_extent.h>
+#include <__utility/exception_guard.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
+// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
+// destruction. If elements are themselves C-style arrays, they are recursively destroyed
+// in the same manner.
+//
+// This function assumes that destructors do not throw, and that the allocator is bound to
+// the correct type.
+template <class _Alloc,
+          class _BidirIter,
+          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
+                "The allocator should already be rebound to the correct type");
+
+  if (__first == __last)
+    return;
+
+  if constexpr (is_array_v<_ValueType>) {
+    static_assert(!__is_unbounded_array_v<_ValueType>,
+                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
+
+    using _Element = remove_extent_t<_ValueType>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    do {
+      --__last;
+      decltype(auto) __array = *__last;
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
+    } while (__last != __first);
+  } else {
+    do {
+      --__last;
+      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
+    } while (__last != __first);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method.
+//
+// If the object being constructed is an array, each element of the array is allocator-constructed,
+// recursively. If an exception is thrown during the construction of an array, the initialized
+// elements are destroyed in reverse order of initialization using allocator destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method, passing along
+// the provided argument.
+//
+// If the object being constructed is an array, the argument is also assumed to be an array. Each
+// each element of the array being constructed is allocator-constructed from the corresponding
+// element of the argument array. If an exception is thrown during the construction of an array,
+// the initialized elements are destroyed in reverse order of initialization using allocator
+// destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp, class _Arg>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    static_assert(is_array_v<_Arg>,
+                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
+                  "trying to construct an array.");
+
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
+  }
+}
+
+// Given a range starting at it and containing n elements, initializes each element in the
+// range from left to right using the construct method of the allocator (rebound to the
+// correct type).
+//
+// If an exception is thrown, the initialized elements are destroyed in reverse order of
+// initialization using allocator_traits destruction. If the elements in the range are C-style
+// arrays, they are initialized element-wise using allocator construction, and recursively so.
+template <class _Alloc,
+          class _BidirIter,
+          class _Tp,
+          class _Size = typename iterator_traits<_BidirIter>::difference_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
+  }
+  __guard.__complete();
+}
+
+// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
+// to the allocator's construct method, which results in value initialization.
+template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
+  }
+  __guard.__complete();
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3efceae54599d..f4cde849a8773 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1702,6 +1702,9 @@ module std {
       export std.memory.destroy
       export std.utility.pair
     }
+    module uninitialized_multidimensional_algorithms {
+      header "__memory/uninitialized_multidimensional_algorithms.h"
+    }
     module unique_ptr {
       header "__memory/unique_ptr.h"
       export std.functional.hash


From libcxx-commits at lists.llvm.org  Sat Jul  4 02:09:04 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 02:09:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a48cdb0.14d9b9dc.140829.af44@mx.google.com>


https://github.com/huixie90 edited https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Sat Jul  4 02:09:31 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 02:09:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Use eary returns in some
	vector functions (PR #207367)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207367@github.com>
Message-ID: <6a48cdcb.e55dc341.29c9b8.aaeb@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/207367

From libcxx-commits at lists.llvm.org  Sat Jul  4 03:23:54 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 04 Jul 2026 03:23:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Use C++11 spelling of
	[[noreturn]] (PR #207344)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207344@github.com>
Message-ID: <6a48df3a.ff037943.3bf0e2.0ed9@mx.google.com>


https://github.com/philnik777 ready_for_review https://github.com/llvm/llvm-project/pull/207344

From libcxx-commits at lists.llvm.org  Sat Jul  4 03:24:27 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 03:24:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Use C++11 spelling of
	[[noreturn]] (PR #207344)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207344@github.com>
Message-ID: <6a48df5b.bd93e731.b068b.00a6@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxxabi

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

We already use the C++11 spelling for libc++, and there isn't much of a reason not to use it. Except for the changes in `cxxabi.h` this is even fully standard code.


---
Full diff: https://github.com/llvm/llvm-project/pull/207344.diff


10 Files Affected:

- (modified) libcxxabi/include/cxxabi.h (+8-9) 
- (modified) libcxxabi/src/abort_message.h (+1-1) 
- (modified) libcxxabi/src/cxa_aux_runtime.cpp (+3-3) 
- (modified) libcxxabi/src/cxa_default_handlers.cpp (+3-6) 
- (modified) libcxxabi/src/cxa_exception.cpp (+1-1) 
- (modified) libcxxabi/src/cxa_handlers.cpp (+2-4) 
- (modified) libcxxabi/src/cxa_handlers.h (+2-2) 
- (modified) libcxxabi/src/cxa_personality.cpp (+1-2) 
- (modified) libcxxabi/src/cxa_vector.cpp (+1-2) 
- (modified) libcxxabi/src/cxa_virtual.cpp (+2-2) 


``````````diff
diff --git a/libcxxabi/include/cxxabi.h b/libcxxabi/include/cxxabi.h
index ff0d5d2aef47c..c65e353d68daf 100644
--- a/libcxxabi/include/cxxabi.h
+++ b/libcxxabi/include/cxxabi.h
@@ -20,7 +20,6 @@
 #include <__cxxabi_config.h>
 
 #define _LIBCPPABI_VERSION 230000
-#define _LIBCXXABI_NORETURN  __attribute__((noreturn))
 #define _LIBCXXABI_ALWAYS_COLD __attribute__((cold))
 
 #ifdef __cplusplus
@@ -56,7 +55,7 @@ __cxa_init_primary_exception(void* object, std::type_info* tinfo, void(_LIBCXXAB
 #endif
 
 // 2.4.3 Throwing the Exception Object
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void
 __cxa_throw(void *thrown_exception, std::type_info *tinfo,
 #ifdef __wasm__
             void *(_LIBCXXABI_DTOR_FUNC *dest)(void *));
@@ -80,22 +79,22 @@ extern _LIBCXXABI_FUNC_VIS std::type_info *__cxa_current_exception_type();
 // GNU extension
 // Calls `terminate` with the current exception being caught. This function is used by GCC when a `noexcept` function
 // throws an exception inside a try/catch block and doesn't catch it.
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_call_terminate(void*) _LIBCXXABI_NOEXCEPT;
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_call_terminate(void*) _LIBCXXABI_NOEXCEPT;
 
 // 2.5.4 Rethrowing Exceptions
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_rethrow();
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_rethrow();
 
 // 2.6 Auxiliary Runtime APIs
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void);
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void);
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_bad_cast(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_bad_typeid(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void
 __cxa_throw_bad_array_new_length(void);
 
 // 3.2.6 Pure Virtual Function API
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_pure_virtual(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_pure_virtual(void);
 
 // 3.2.7 Deleted Virtual Function API
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_deleted_virtual(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_deleted_virtual(void);
 
 // 3.3.2 One-time Construction API
 #if defined(_LIBCXXABI_GUARD_ABI_ARM)
diff --git a/libcxxabi/src/abort_message.h b/libcxxabi/src/abort_message.h
index 2c12c429569d3..885dd38372791 100644
--- a/libcxxabi/src/abort_message.h
+++ b/libcxxabi/src/abort_message.h
@@ -11,7 +11,7 @@
 
 #include "cxxabi.h"
 
-extern "C" _LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN void
+extern "C" [[noreturn]] _LIBCXXABI_HIDDEN void
 __abort_message(const char *format, ...) __attribute__((format(printf, 1, 2)));
 
 #ifndef _LIBCXXABI_ASSERT
diff --git a/libcxxabi/src/cxa_aux_runtime.cpp b/libcxxabi/src/cxa_aux_runtime.cpp
index 5e6040d75a262..e6bac221d8be8 100644
--- a/libcxxabi/src/cxa_aux_runtime.cpp
+++ b/libcxxabi/src/cxa_aux_runtime.cpp
@@ -16,7 +16,7 @@
 
 namespace __cxxabiv1 {
 extern "C" {
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void) {
+[[noreturn]] _LIBCXXABI_FUNC_VIS void __cxa_bad_cast(void) {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_cast();
 #else
@@ -24,7 +24,7 @@ _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void) {
 #endif
 }
 
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void) {
+[[noreturn]] _LIBCXXABI_FUNC_VIS void __cxa_bad_typeid(void) {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_typeid();
 #else
@@ -32,7 +32,7 @@ _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void) {
 #endif
 }
 
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void
+[[noreturn]] _LIBCXXABI_FUNC_VIS void
 __cxa_throw_bad_array_new_length(void) {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_array_new_length();
diff --git a/libcxxabi/src/cxa_default_handlers.cpp b/libcxxabi/src/cxa_default_handlers.cpp
index 092e66692ecb1..5daf78b34ee54 100644
--- a/libcxxabi/src/cxa_default_handlers.cpp
+++ b/libcxxabi/src/cxa_default_handlers.cpp
@@ -24,8 +24,7 @@
 static constinit const char* cause = "uncaught";
 
 #  ifndef _LIBCXXABI_NO_EXCEPTIONS
-__attribute__((noreturn))
-static void demangling_terminate_handler()
+[[noreturn]] static void demangling_terminate_handler()
 {
     using namespace __cxxabiv1;
     __cxa_eh_globals* globals = __cxa_get_globals_fast();
@@ -78,16 +77,14 @@ static void demangling_terminate_handler()
     }
 }
 #else // !_LIBCXXABI_NO_EXCEPTIONS
-__attribute__((noreturn))
-static void demangling_terminate_handler()
+[[noreturn]] static void demangling_terminate_handler()
 {
     (void)cause;
     __abort_message("terminating");
 }
 #endif // !_LIBCXXABI_NO_EXCEPTIONS
 
-__attribute__((noreturn))
-static void demangling_unexpected_handler()
+[[noreturn]] static void demangling_unexpected_handler()
 {
     cause = "unexpected";
     std::terminate();
diff --git a/libcxxabi/src/cxa_exception.cpp b/libcxxabi/src/cxa_exception.cpp
index 5d7edae697d67..f56ecc248eefb 100644
--- a/libcxxabi/src/cxa_exception.cpp
+++ b/libcxxabi/src/cxa_exception.cpp
@@ -140,7 +140,7 @@ exception_cleanup_func(_Unwind_Reason_Code reason, _Unwind_Exception* unwind_exc
     __cxa_decrement_exception_refcount(unwind_exception + 1);
 }
 
-static _LIBCXXABI_NORETURN void failed_throw(__cxa_exception* exception_header) {
+[[noreturn]] static void failed_throw(__cxa_exception* exception_header) {
 //  Section 2.5.3 says:
 //      * For purposes of this ABI, several things are considered exception handlers:
 //      ** A terminate() call due to a throw.
diff --git a/libcxxabi/src/cxa_handlers.cpp b/libcxxabi/src/cxa_handlers.cpp
index f879ff0d8ff18..0abdc740486b8 100644
--- a/libcxxabi/src/cxa_handlers.cpp
+++ b/libcxxabi/src/cxa_handlers.cpp
@@ -36,8 +36,7 @@ __unexpected(unexpected_handler func)
     __abort_message("unexpected_handler unexpectedly returned");
 }
 
-__attribute__((noreturn))
-void
+[[noreturn]] void
 unexpected()
 {
     __unexpected(get_unexpected());
@@ -69,8 +68,7 @@ __terminate(terminate_handler func) noexcept
 #endif // _LIBCXXABI_NO_EXCEPTIONS
 }
 
-__attribute__((noreturn))
-void
+[[noreturn]] void
 terminate() noexcept
 {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
diff --git a/libcxxabi/src/cxa_handlers.h b/libcxxabi/src/cxa_handlers.h
index 3d8dc6b2de95e..072b85ed89f85 100644
--- a/libcxxabi/src/cxa_handlers.h
+++ b/libcxxabi/src/cxa_handlers.h
@@ -19,11 +19,11 @@
 namespace std
 {
 
-_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN
+[[noreturn]] _LIBCXXABI_HIDDEN
 void
 __unexpected(unexpected_handler func);
 
-_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN
+[[noreturn]] _LIBCXXABI_HIDDEN
 void
 __terminate(terminate_handler func) noexcept;
 
diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index 6599f89740113..c5050e46c0e85 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1272,8 +1272,7 @@ __gxx_personality_v0(_Unwind_State state,
 #endif
 
 
-__attribute__((noreturn))
-_LIBCXXABI_FUNC_VIS void
+[[noreturn]] _LIBCXXABI_FUNC_VIS void
 __cxa_call_unexpected(void* arg)
 {
     _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(arg);
diff --git a/libcxxabi/src/cxa_vector.cpp b/libcxxabi/src/cxa_vector.cpp
index 857ee27d065c3..5eac7a8dc9729 100644
--- a/libcxxabi/src/cxa_vector.cpp
+++ b/libcxxabi/src/cxa_vector.cpp
@@ -116,8 +116,7 @@ namespace {
 //
 
 namespace {
-_LIBCXXABI_NORETURN
-void throw_bad_array_new_length() {
+[[noreturn]] void throw_bad_array_new_length() {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_array_new_length();
 #else
diff --git a/libcxxabi/src/cxa_virtual.cpp b/libcxxabi/src/cxa_virtual.cpp
index 8f4fdd0919f0e..1d3f4c63cf2de 100644
--- a/libcxxabi/src/cxa_virtual.cpp
+++ b/libcxxabi/src/cxa_virtual.cpp
@@ -11,12 +11,12 @@
 
 namespace __cxxabiv1 {
 extern "C" {
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN
+[[noreturn]] _LIBCXXABI_FUNC_VIS
 void __cxa_pure_virtual(void) {
   __abort_message("Pure virtual function called!");
 }
 
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN
+[[noreturn]] _LIBCXXABI_FUNC_VIS
 void __cxa_deleted_virtual(void) {
   __abort_message("Deleted virtual function called!");
 }

``````````

</details>


https://github.com/llvm/llvm-project/pull/207344

From libcxx-commits at lists.llvm.org  Sat Jul  4 03:46:57 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 03:46:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][math][c++17] P0226R1 -
 Mathematical Special Functions: infra + `std::assoc_laguerre` (PR #205649)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205649@github.com>
Message-ID: <6a48e4a1.bd93e731.b068b.e75c@mx.google.com>


https://github.com/PaulXiCao updated https://github.com/llvm/llvm-project/pull/205649

>From 7555447d01b3e6bd5127f4ffbfaeeb223cf9ea81 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 20:52:48 +0200
Subject: [PATCH 01/52] link against Boost.Math

---
 libcxx/CMakeLists.txt     | 8 ++++++++
 libcxx/src/CMakeLists.txt | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..845042f15495f 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -899,6 +899,14 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+
+# Header-only Boost.Math (INTERFACE target `boost_math`). Lives outside the
+# libcxx source tree, so a binary dir must be supplied.
+add_subdirectory(
+  ${LLVM_THIRD_PARTY_DIR}/boost-math
+  ${LIBCXX_BINARY_DIR}/third-party/boost-math
+  EXCLUDE_FROM_ALL)
+
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index de7817ad69f26..c882446596a3e 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -176,6 +176,7 @@ add_library(cxx_shared SHARED ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(cxx_shared PUBLIC cxx-headers runtimes-libc-shared
                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                  PRIVATE boost_math
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_shared
   PROPERTIES
@@ -263,6 +264,7 @@ add_library(cxx_static STATIC ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(cxx_static PUBLIC cxx-headers runtimes-libc-static
                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                  PRIVATE boost_math
                                   PRIVATE libcxx-abi-static
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_static

>From fe89c404516cfea93ab67939b48a15c74213cd59 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 20:53:37 +0200
Subject: [PATCH 02/52] add empty src/mathematical_special_functions.cpp

---
 libcxx/src/CMakeLists.txt                     |  1 +
 libcxx/src/mathematical_special_functions.cpp | 17 +++++++++++++++++
 2 files changed, 18 insertions(+)
 create mode 100644 libcxx/src/mathematical_special_functions.cpp

diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index c882446596a3e..d2168decbfdba 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -29,6 +29,7 @@ set(LIBCXX_SOURCES
   include/ryu/ryu.h
   include/to_chars_floating_point.h
   include/from_chars_floating_point.h
+  mathematical_special_functions.cpp
   memory.cpp
   memory_resource.cpp
   new_handler.cpp
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
new file mode 100644
index 0000000000000..db07d61f89319
--- /dev/null
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -0,0 +1,17 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+
+namespace {}
+
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+_LIBCPP_END_NAMESPACE_STD

>From 7fbc20f4bea5a80fdebfb22eadee50cb90698b46 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 21:06:04 +0200
Subject: [PATCH 03/52] add simple test for std::assoc_laguerref (fails atm)

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
new file mode 100644
index 0000000000000..1a12c8f81a16c
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Missing powl
+// XFAIL: LLVM-LIBC-FIXME
+
+// REQUIRES: std-at-least-c++17
+
+// <cmath>
+//
+// [sf.cmath.assoc.laguerre], associated Laguerre polynomials
+// floating-point-type assoc_laguerre(unsigned n, unsigned m, floating-point-type x);
+// float               assoc_laguerref(unsigned n, unsigned m, float x);
+// long double         assoc_laguerrel(unsigned n, unsigned m, long double x);
+
+#include <cmath>
+
+int main(int, char**) {
+  std::assoc_laguerref(0, 0, 0.0f);
+
+  return 0;
+}

>From c7e11e92b9c70fe1f319a8f959ce1c11cbbfa9cc Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 21:52:09 +0200
Subject: [PATCH 04/52] handling MATH_ERRNO, MATH_ERREXCEPT via __sf_result

---
 libcxx/include/__math/special_functions.h | 25 +++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 6b217852e2f8c..86b3ac9cb665e 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -15,7 +15,10 @@
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_integral.h>
+#include <cerrno>
+#include <cfenv>
 #include <limits>
+#include <math.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -25,6 +28,28 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 17
 
+namespace __math {
+template <class _Tp>
+struct __sf_result {
+  bool __domain_error;
+  _Tp __ret;
+
+  operator _Tp() const {
+#  if math_errhandling & MATH_ERRNO
+    if (__domain_error)
+      errno = EDOM;
+#  endif
+
+#  if math_errhandling & MATH_ERREXCEPT
+    if (__domain_error)
+      feraiseexcept(FE_INVALID);
+#  endif
+
+    return __ret;
+  }
+};
+} // namespace __math
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).

>From 23081681878b007cb50f1ed115ec316fa956b4cf Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 22:00:10 +0200
Subject: [PATCH 05/52] impl assoc_laguerref

---
 libcxx/include/__math/special_functions.h     |  9 +++++
 libcxx/src/mathematical_special_functions.cpp | 37 ++++++++++++++++++-
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 86b3ac9cb665e..d0d6e56507e85 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -25,6 +25,7 @@
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 #if _LIBCPP_STD_VER >= 17
 
@@ -48,8 +49,15 @@ struct __sf_result {
     return __ret;
   }
 };
+
+_LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
+
 } // namespace __math
 
+inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).
@@ -104,6 +112,7 @@ _LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, _Integer __x) {
 
 #endif // _LIBCPP_STD_VER >= 17
 
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index db07d61f89319..e18e9c5f2797c 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -7,11 +7,46 @@
 //===----------------------------------------------------------------------===//
 
 #include <__config>
+#include <boost/math/special_functions.hpp>
+#include <cmath>
+#include <optional>
+#include <type_traits>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-namespace {}
+namespace __math {
+namespace {
+template <class _Ret>
+optional<_Ret> __check_nan() {
+  return nullopt;
+}
+
+template <class _Ret, class _Arg, class... _Args>
+optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
+  if constexpr (is_floating_point_v<_Arg>)
+    if (isnan(__arg))
+      return __arg;
+  return __check_nan<_Ret>(__args...);
+}
+
+template <class Func, class... _Args, class _Ret = std::invoke_result_t<Func, _Args...>>
+__sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
+  if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
+    return {.__domain_error = false, .__ret = *__maybe_nan};
+
+  try {
+    return {.__domain_error = false, .__ret = f(__args...)};
+  } catch (...) {
+    return {.__domain_error = true, .__ret = numeric_limits<_Ret>::quiet_NaN()};
+  }
+}
+} // namespace
+
+__sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
+  return invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+} // namespace __math
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From adb174a0e9d29b172b17bc9e0d781db0dff15d5c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 22:34:12 +0200
Subject: [PATCH 06/52] test assoc_laguerref

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 1a12c8f81a16c..fe92daa501c6f 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -6,22 +6,18 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing powl
-// XFAIL: LLVM-LIBC-FIXME
-
 // REQUIRES: std-at-least-c++17
 
 // <cmath>
 //
 // [sf.cmath.assoc.laguerre], associated Laguerre polynomials
-// floating-point-type assoc_laguerre(unsigned n, unsigned m, floating-point-type x);
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
-// long double         assoc_laguerrel(unsigned n, unsigned m, long double x);
 
+#include <cassert>
 #include <cmath>
 
-int main(int, char**) {
-  std::assoc_laguerref(0, 0, 0.0f);
-
-  return 0;
+int main() {
+  // Single value tested against known solution.
+  // Note, underlying Boost.Math is itself well-tested.
+  assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
 }

>From b7bc1fb6247235ce62066d70a30c13608a8672fc Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 18:26:00 +0200
Subject: [PATCH 07/52] test: common.h

---
 .../std/numerics/c.math/sf.cmath/common.h     | 65 +++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/sf.cmath/common.h

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
new file mode 100644
index 0000000000000..867e6af6f6914
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_SF_CMATH_COMMON_H
+#define TEST_SF_CMATH_COMMON_H
+
+#include <cassert>
+#include <cerrno>
+#include <cmath>
+#include <math.h>
+#include <cfenv>
+
+// std::type_identity is C++20 (we need to support C++17 here)
+template <class T>
+struct type_identity {
+  typedef T type;
+};
+template <class T>
+using type_identity_t = typename type_identity<T>::type;
+
+template <class T>
+bool between(type_identity_t<T> lower, T value, type_identity_t<T> upper) {
+  return lower < value && value < upper;
+}
+
+template <class Func>
+void check_no_domain_error(Func f) {
+#if math_errhandling & MATH_ERRNO
+  errno = EACCES;
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  std::feclearexcept(FE_INVALID);
+#endif
+  f();
+#if math_errhandling & MATH_ERRNO
+  assert(errno == EACCES);
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  assert(!std::fetestexcept(FE_INVALID));
+#endif
+}
+
+template <class Func>
+void check_domain_error(Func f) {
+#if math_errhandling & MATH_ERRNO
+  errno = EACCES;
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  std::feclearexcept(FE_INVALID);
+#endif
+  f();
+#if math_errhandling & MATH_ERRNO
+  assert(errno == EDOM);
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  assert(std::fetestexcept(FE_INVALID));
+#endif
+}
+
+#endif // TEST_SF_CMATH_COMMON_H

>From a47f9e902e5c427c84115f4af119eedf8778e182 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 18:26:59 +0200
Subject: [PATCH 08/52] more testing of assoc_laguerref: values, types, domain
 error

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 36 +++++++++++++++++--
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index fe92daa501c6f..eb747efb4c1c6 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,10 +14,40 @@
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
 
 #include <cassert>
+#include <cerrno>
+#include <cfenv>
 #include <cmath>
 
+#include "common.h"
+#include "type_algorithms.h"
+
+struct TestFloatingPoint {
+  template <class T>
+  void operator()() const {
+    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
+
+    assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
+
+    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
+
+    assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
+
+    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
+
+    check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
+  }
+};
+
+struct TestIntegral {
+  template <class T>
+  void operator()() const {
+    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
+
+    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
+  }
+};
+
 int main() {
-  // Single value tested against known solution.
-  // Note, underlying Boost.Math is itself well-tested.
-  assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
+  types::for_each(types::floating_point_types{}, TestFloatingPoint{});
+  types::for_each(types::integral_types{}, TestIntegral{});
 }

>From f8a3b1d891405ff6ab04bfc7a7d615ca2cd54a56 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:15:40 +0200
Subject: [PATCH 09/52] silence boost_math warnings by marking it as `SYSTEM`
 lib

---
 third-party/boost-math/CMakeLists.txt |  2 +-
 third-party/update_boost_math.sh      | 13 +++++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/third-party/boost-math/CMakeLists.txt b/third-party/boost-math/CMakeLists.txt
index a1f6011d95e55..0de1fa2ec01ed 100644
--- a/third-party/boost-math/CMakeLists.txt
+++ b/third-party/boost-math/CMakeLists.txt
@@ -11,7 +11,7 @@ add_library(boost_math INTERFACE)
 
 add_library(Boost::math ALIAS boost_math)
 
-target_include_directories(boost_math INTERFACE include)
+target_include_directories(boost_math SYSTEM INTERFACE include)
 if(NOT CMAKE_VERSION VERSION_LESS "3.19")
   file(GLOB_RECURSE headers include/*.hpp)
   target_sources(boost_math PRIVATE ${headers})
diff --git a/third-party/update_boost_math.sh b/third-party/update_boost_math.sh
index 96cd73ed8c304..27d8816b7cb12 100755
--- a/third-party/update_boost_math.sh
+++ b/third-party/update_boost_math.sh
@@ -27,3 +27,16 @@ echo "****************************************"
 echo "Subsetting Boost.Math ${VERSION}"
 echo "****************************************"
 rm -rf ${SCRIPT_DIR}/boost-math/{.circleci,.drone,.github,build,config,doc,example,meta,reporting,src,test,tools}
+
+echo "****************************************"
+echo "Patching Boost.Math ${VERSION} for libc++"
+echo "****************************************"
+# Mark boost_math include dir as SYSTEM so libc++ consumers don't surface warnings from vendored boost-math code (e.g.
+# -Wdeprecated-redundant-constexpr-static-def). Upstream keeps these for pre-C++17 compat.
+sed -i 's|target_include_directories(boost_math INTERFACE include)|target_include_directories(boost_math SYSTEM INTERFACE include)|' \
+    ${SCRIPT_DIR}/boost-math/CMakeLists.txt
+
+# Verify the patch landed -- fail loudly if upstream renamed the target.
+grep -q 'target_include_directories(boost_math SYSTEM INTERFACE include)' \
+    ${SCRIPT_DIR}/boost-math/CMakeLists.txt \
+    || { echo "ERROR: SYSTEM include patch failed -- upstream CMakeLists.txt structure changed"; exit 1; }

>From 17c19c5c87c5adf5618686cbf1cac445d4bfbfda Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:24:39 +0200
Subject: [PATCH 10/52] add nodiscard test for assoc_laguerref

---
 .../c.math/sf.cmath/nodiscard.verify.cpp      | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7928ebf50681a
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// Check that functions are marked [[nodiscard]]
+
+#include <cmath>
+
+void test() {
+  // clang-format off
+  // assoc_laguerre
+  std::assoc_laguerref(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}

>From 0a7cf75eca52eab4c5d98b4f323f05fbf0187a9c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:27:41 +0200
Subject: [PATCH 11/52] add nodiscard to assoc_laguerref

---
 libcxx/include/__math/special_functions.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index d0d6e56507e85..dee70bec45cd8 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -54,7 +54,8 @@ _LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsi
 
 } // namespace __math
 
-inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 

>From ddfb2c1563bf465cd912f226abe221488a67b0df Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:50:00 +0200
Subject: [PATCH 12/52] insert grouping for assoc_laguerre functions

---
 libcxx/include/__math/special_functions.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index dee70bec45cd8..3100860ec8c70 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -49,9 +49,11 @@ struct __sf_result {
     return __ret;
   }
 };
+} // namespace __math
 
+// assoc_laguerre
+namespace __math {
 _LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
-
 } // namespace __math
 
 [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float

>From 39b98428ea2cf592234512ac3762348f3594087c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 00:09:27 +0200
Subject: [PATCH 13/52] insert grouping for hermite functions

---
 libcxx/include/__math/special_functions.h | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 3100860ec8c70..b8ec0785a3b2b 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -26,7 +26,6 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
-
 #if _LIBCPP_STD_VER >= 17
 
 namespace __math {
@@ -61,6 +60,14 @@ assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+#endif // _LIBCPP_STD_VER >= 17
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+_LIBCPP_END_NAMESPACE_STD
+
+// hermite
+_LIBCPP_BEGIN_NAMESPACE_STD
+#if _LIBCPP_STD_VER >= 17
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).
@@ -114,8 +121,5 @@ _LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, _Integer __x) {
 }
 
 #endif // _LIBCPP_STD_VER >= 17
-
-_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
-
 #endif // _LIBCPP___MATH_SPECIAL_FUNCTIONS_H

>From 145ce5f04732b8f5800f9013411d11ffe97c215f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 00:10:16 +0200
Subject: [PATCH 14/52] add missing ABI annotation

---
 libcxx/include/__math/special_functions.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index b8ec0785a3b2b..e11dc97fd46d4 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -34,7 +34,7 @@ struct __sf_result {
   bool __domain_error;
   _Tp __ret;
 
-  operator _Tp() const {
+  _LIBCPP_HIDE_FROM_ABI operator _Tp() const {
 #  if math_errhandling & MATH_ERRNO
     if (__domain_error)
       errno = EDOM;

>From 2328d31c7918995c6895836c69e1371250675427 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:16:15 +0200
Subject: [PATCH 15/52] TU: guard by C++ >= 17

---
 libcxx/src/mathematical_special_functions.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index e18e9c5f2797c..102acb37e50bc 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -14,6 +14,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+#if _LIBCPP_STD_VER >= 17
 
 namespace __math {
 namespace {
@@ -48,5 +49,6 @@ __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __
 }
 } // namespace __math
 
+#endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From 7b60948e6eb6cb5b9ccea95e76bb02865d314871 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:18:02 +0200
Subject: [PATCH 16/52] TU: qualify std:: functions

---
 libcxx/src/mathematical_special_functions.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 102acb37e50bc..03a90cd3bd982 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -19,14 +19,14 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 namespace __math {
 namespace {
 template <class _Ret>
-optional<_Ret> __check_nan() {
-  return nullopt;
+std::optional<_Ret> __check_nan() {
+  return std::nullopt;
 }
 
 template <class _Ret, class _Arg, class... _Args>
-optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
-  if constexpr (is_floating_point_v<_Arg>)
-    if (isnan(__arg))
+std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
+  if constexpr (std::is_floating_point_v<_Arg>)
+    if (std::isnan(__arg))
       return __arg;
   return __check_nan<_Ret>(__args...);
 }
@@ -39,7 +39,7 @@ __sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
   try {
     return {.__domain_error = false, .__ret = f(__args...)};
   } catch (...) {
-    return {.__domain_error = true, .__ret = numeric_limits<_Ret>::quiet_NaN()};
+    return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
   }
 }
 } // namespace

>From a0f3426cd940ef45a441b4e610efaebf8f3e6655 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:18:46 +0200
Subject: [PATCH 17/52] TU: add missing __uglify

---
 libcxx/src/mathematical_special_functions.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 03a90cd3bd982..fdca91bf5a8b4 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -31,13 +31,13 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
   return __check_nan<_Ret>(__args...);
 }
 
-template <class Func, class... _Args, class _Ret = std::invoke_result_t<Func, _Args...>>
-__sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
+template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
+__sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return {.__domain_error = false, .__ret = *__maybe_nan};
 
   try {
-    return {.__domain_error = false, .__ret = f(__args...)};
+    return {.__domain_error = false, .__ret = __f(__args...)};
   } catch (...) {
     return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
   }
@@ -45,7 +45,7 @@ __sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
 } // namespace
 
 __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+  return __invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 } // namespace __math
 

>From 745c9d9aa7dfc246593851286f70e6494148c0d4 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:07 +0200
Subject: [PATCH 18/52] TU: add missing headers

---
 libcxx/src/mathematical_special_functions.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index fdca91bf5a8b4..9a106c763c776 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -7,8 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include <__config>
+#include <__math/special_functions.h>
 #include <boost/math/special_functions.hpp>
 #include <cmath>
+#include <limits>
 #include <optional>
 #include <type_traits>
 

>From 372f1054b2aa081808ed07b6182a6b8f2e4a21e3 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:42 +0200
Subject: [PATCH 19/52] TU: remove C++20 designated initializers

---
 libcxx/src/mathematical_special_functions.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 9a106c763c776..23d9216d777d2 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -36,12 +36,12 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
 __sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
-    return {.__domain_error = false, .__ret = *__maybe_nan};
+    return {false, *__maybe_nan};
 
   try {
-    return {.__domain_error = false, .__ret = __f(__args...)};
+    return {false, __f(__args...)};
   } catch (...) {
-    return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
+    return {true, std::numeric_limits<_Ret>::quiet_NaN()};
   }
 }
 } // namespace

>From 4e32fcc443bdd93c388e69aad76c0b08d7335d9d Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:51 +0200
Subject: [PATCH 20/52] TU: cleanup

---
 libcxx/src/mathematical_special_functions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 23d9216d777d2..cf88cbef159fa 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -47,7 +47,7 @@ __sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
 } // namespace
 
 __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return __invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 } // namespace __math
 

>From 299419630480cc7ba0fd7c026a14c8797b461aa0 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 11:21:18 +0200
Subject: [PATCH 21/52] break cyclic header deps: move errno logic into TU

---
 libcxx/include/__math/special_functions.h     | 34 +------------------
 libcxx/src/mathematical_special_functions.cpp | 21 ++++++++----
 2 files changed, 15 insertions(+), 40 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index e11dc97fd46d4..89c75cfc04585 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -15,10 +15,7 @@
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_integral.h>
-#include <cerrno>
-#include <cfenv>
 #include <limits>
-#include <math.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -28,37 +25,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
-namespace __math {
-template <class _Tp>
-struct __sf_result {
-  bool __domain_error;
-  _Tp __ret;
-
-  _LIBCPP_HIDE_FROM_ABI operator _Tp() const {
-#  if math_errhandling & MATH_ERRNO
-    if (__domain_error)
-      errno = EDOM;
-#  endif
-
-#  if math_errhandling & MATH_ERREXCEPT
-    if (__domain_error)
-      feraiseexcept(FE_INVALID);
-#  endif
-
-    return __ret;
-  }
-};
-} // namespace __math
-
 // assoc_laguerre
-namespace __math {
-_LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
-} // namespace __math
-
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float
-assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return __math::__assoc_laguerre(__n, __m, __x);
-}
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index cf88cbef159fa..daab723b8f2e3 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -9,8 +9,11 @@
 #include <__config>
 #include <__math/special_functions.h>
 #include <boost/math/special_functions.hpp>
+#include <cerrno>
+#include <cfenv>
 #include <cmath>
 #include <limits>
+#include <math.h>
 #include <optional>
 #include <type_traits>
 
@@ -18,7 +21,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
-namespace __math {
 namespace {
 template <class _Ret>
 std::optional<_Ret> __check_nan() {
@@ -34,22 +36,27 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 }
 
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
-__sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
+_Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
-    return {false, *__maybe_nan};
+    return *__maybe_nan;
 
   try {
-    return {false, __f(__args...)};
+    return __f(__args...);
   } catch (...) {
-    return {true, std::numeric_limits<_Ret>::quiet_NaN()};
+#  if math_errhandling & MATH_ERRNO
+    errno = EDOM;
+#  endif
+#  if math_errhandling & MATH_ERREXCEPT
+    feraiseexcept(FE_INVALID);
+#  endif
+    return std::numeric_limits<_Ret>::quiet_NaN();
   }
 }
 } // namespace
 
-__sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
+float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
-} // namespace __math
 
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS

>From f2580f1f2e853e83db75462c95558cbfeef6508c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 11:29:35 +0200
Subject: [PATCH 22/52] TU: cleanups

---
 libcxx/src/mathematical_special_functions.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index daab723b8f2e3..420e0a1e71cb0 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -13,7 +13,6 @@
 #include <cfenv>
 #include <cmath>
 #include <limits>
-#include <math.h>
 #include <optional>
 #include <type_traits>
 
@@ -47,7 +46,7 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
     errno = EDOM;
 #  endif
 #  if math_errhandling & MATH_ERREXCEPT
-    feraiseexcept(FE_INVALID);
+    std::feraiseexcept(FE_INVALID);
 #  endif
     return std::numeric_limits<_Ret>::quiet_NaN();
   }

>From 80f81ae52610bc7accc38605524fdbf872004e4f Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 16:31:15 +0200
Subject: [PATCH 23/52] use boost errno_on_error policy instead of exceptions

---
 libcxx/src/mathematical_special_functions.cpp | 44 +++++++++++++++----
 1 file changed, 35 insertions(+), 9 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 420e0a1e71cb0..f8fea7ff1b70e 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -8,19 +8,31 @@
 
 #include <__config>
 #include <__math/special_functions.h>
-#include <boost/math/special_functions.hpp>
 #include <cerrno>
 #include <cfenv>
 #include <cmath>
-#include <limits>
 #include <optional>
 #include <type_traits>
 
+#define BOOST_MATH_NO_EXCEPTIONS
+#include <boost/math/special_functions.hpp>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
 namespace {
+// Error policy for all Boost.Math calls: report domain/pole/overflow/evaluation
+// errors via errno (errno_on_error) instead of throwing. Boost sets errno to
+// EDOM (domain/pole/evaluation) or ERANGE (overflow) and returns NaN/inf. The
+// remaining categories (underflow/denorm/indeterminate) default to ignore.
+namespace __bmp = boost::math::policies;
+using __policy =
+    __bmp::policy<__bmp::domain_error<__bmp::errno_on_error>,
+                  __bmp::pole_error<__bmp::errno_on_error>,
+                  __bmp::overflow_error<__bmp::errno_on_error>,
+                  __bmp::evaluation_error<__bmp::errno_on_error>>;
+
 template <class _Ret>
 std::optional<_Ret> __check_nan() {
   return std::nullopt;
@@ -34,22 +46,36 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
   return __check_nan<_Ret>(__args...);
 }
 
+// Shared back-end for the C++17 mathematical special functions ([sf.cmath]).
+// Boost.Math is the compute kernel; this wrapper enforces the standard's
+// error-reporting rules ([sf.cmath.general]):
+//   1. NaN argument -> return NaN, do NOT report a domain error (the
+//      __check_nan pre-filter below).
+//   2. domain/range error -> report per <cmath> math_errhandling: errno = EDOM
+//      (domain/pole) or ERANGE (overflow), done by Boost's errno_on_error
+//      policy; and, when MATH_ERREXCEPT is set, raise the matching <cfenv>
+//      exception (done here, since Boost never touches <cfenv>).
+// Promotion: Boost's default promote_float=true computes float inputs in double
+// and rounds once -- more accurate and overflow-resistant, matching the
+// existing std::hermite(float) approach. We keep it.
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
 _Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return *__maybe_nan;
 
-  try {
-    return __f(__args...);
-  } catch (...) {
-#  if math_errhandling & MATH_ERRNO
-    errno = EDOM;
+#  if math_errhandling & MATH_ERREXCEPT
+  errno = 0;
 #  endif
+  _Ret __ret = __f(__args..., __policy{});
+  // Boost set errno via the policy; mirror it onto <cfenv>:
+  // EDOM (domain/pole) -> FE_INVALID, ERANGE (overflow) -> FE_OVERFLOW.
 #  if math_errhandling & MATH_ERREXCEPT
+  if (errno == EDOM)
     std::feraiseexcept(FE_INVALID);
+  else if (errno == ERANGE)
+    std::feraiseexcept(FE_OVERFLOW);
 #  endif
-    return std::numeric_limits<_Ret>::quiet_NaN();
-  }
+  return __ret;
 }
 } // namespace
 

>From 2c13b0df2188c488e3f2fe235ba22569419e0e5b Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 16:44:35 +0200
Subject: [PATCH 24/52] TU: remove fenv support.

---
 libcxx/src/mathematical_special_functions.cpp | 25 +++++--------------
 1 file changed, 6 insertions(+), 19 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index f8fea7ff1b70e..3c0af96fb0c19 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -8,8 +8,6 @@
 
 #include <__config>
 #include <__math/special_functions.h>
-#include <cerrno>
-#include <cfenv>
 #include <cmath>
 #include <optional>
 #include <type_traits>
@@ -51,10 +49,11 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 // error-reporting rules ([sf.cmath.general]):
 //   1. NaN argument -> return NaN, do NOT report a domain error (the
 //      __check_nan pre-filter below).
-//   2. domain/range error -> report per <cmath> math_errhandling: errno = EDOM
-//      (domain/pole) or ERANGE (overflow), done by Boost's errno_on_error
-//      policy; and, when MATH_ERREXCEPT is set, raise the matching <cfenv>
-//      exception (done here, since Boost never touches <cfenv>).
+//   2. domain/range error -> reported via errno only: Boost's errno_on_error
+//      policy sets errno = EDOM (domain/pole/evaluation) or ERANGE (overflow).
+//      The <cfenv> floating-point-exception side of math_errhandling
+//      (MATH_ERREXCEPT) is intentionally not mirrored, matching the shipped
+//      std::hermite and libstdc++'s special-function implementations.
 // Promotion: Boost's default promote_float=true computes float inputs in double
 // and rounds once -- more accurate and overflow-resistant, matching the
 // existing std::hermite(float) approach. We keep it.
@@ -63,19 +62,7 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return *__maybe_nan;
 
-#  if math_errhandling & MATH_ERREXCEPT
-  errno = 0;
-#  endif
-  _Ret __ret = __f(__args..., __policy{});
-  // Boost set errno via the policy; mirror it onto <cfenv>:
-  // EDOM (domain/pole) -> FE_INVALID, ERANGE (overflow) -> FE_OVERFLOW.
-#  if math_errhandling & MATH_ERREXCEPT
-  if (errno == EDOM)
-    std::feraiseexcept(FE_INVALID);
-  else if (errno == ERANGE)
-    std::feraiseexcept(FE_OVERFLOW);
-#  endif
-  return __ret;
+  return __f(__args..., __policy{});
 }
 } // namespace
 

>From 580076b2ffe777b1f00d844091f88ebd03820402 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:24:36 +0200
Subject: [PATCH 25/52] test: add more tests; fix header includes

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index eb747efb4c1c6..079751321f3b9 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,11 +14,11 @@
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
 
 #include <cassert>
-#include <cerrno>
-#include <cfenv>
 #include <cmath>
+#include <limits>
 
 #include "common.h"
+#include "test_macros.h"
 #include "type_algorithms.h"
 
 struct TestFloatingPoint {
@@ -32,9 +32,19 @@ struct TestFloatingPoint {
 
     assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
 
+    // m == 0 reduces to the ordinary Laguerre polynomial: L_2(2) = (4 - 8 + 2) / 2 = -1.
+    assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
+
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
+    // noexcept is a libc++ extension here ([sf.cmath] does not mandate it), so use the libc++-only assertion.
+    LIBCPP_ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, T(0.)));
+
     check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
+
+    // The associated Laguerre polynomials are defined for all real x: a negative
+    // argument is in-domain and must not report a domain error. L_1^0(-1) = 1 - x = 2.
+    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, -1.f), 2.01f)); });
   }
 };
 

>From 2a6dfd4ae296d0f89e1ea5bc953d7d5f8f536ca9 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:53:16 +0200
Subject: [PATCH 26/52] test: add noexcept.compile.pass test

---
 .../c.math/sf.cmath/noexcept.compile.pass.cpp | 20 +++++++++++++++++++
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   |  3 ---
 2 files changed, 20 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..9994e6a018f8d
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// Check that functions are marked noexcept
+
+#include <cmath>
+
+#include "test_macros.h"
+
+void test() {
+  // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
+}
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 079751321f3b9..068ca81e04b83 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -37,9 +37,6 @@ struct TestFloatingPoint {
 
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
-    // noexcept is a libc++ extension here ([sf.cmath] does not mandate it), so use the libc++-only assertion.
-    LIBCPP_ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, T(0.)));
-
     check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
 
     // The associated Laguerre polynomials are defined for all real x: a negative

>From 7b8204c6a252f2971f288212096aee6641c14d82 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:55:16 +0200
Subject: [PATCH 27/52] test: cleanup

---
 .../test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 7928ebf50681a..a4c471e4b3168 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -15,6 +15,6 @@
 void test() {
   // clang-format off
   // assoc_laguerre
-  std::assoc_laguerref(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   // clang-format on
 }

>From 82d264a4bc5a0a1f6d4c932b97b4a74c11957fac Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 28 Jun 2026 23:37:36 +0200
Subject: [PATCH 28/52] test: remove fenv support

---
 libcxx/test/std/numerics/c.math/sf.cmath/common.h | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
index 867e6af6f6914..723422bf7038d 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/common.h
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -12,8 +12,6 @@
 #include <cassert>
 #include <cerrno>
 #include <cmath>
-#include <math.h>
-#include <cfenv>
 
 // std::type_identity is C++20 (we need to support C++17 here)
 template <class T>
@@ -32,34 +30,22 @@ template <class Func>
 void check_no_domain_error(Func f) {
 #if math_errhandling & MATH_ERRNO
   errno = EACCES;
-#endif
-#if math_errhandling & MATH_ERREXCEPT
-  std::feclearexcept(FE_INVALID);
 #endif
   f();
 #if math_errhandling & MATH_ERRNO
   assert(errno == EACCES);
 #endif
-#if math_errhandling & MATH_ERREXCEPT
-  assert(!std::fetestexcept(FE_INVALID));
-#endif
 }
 
 template <class Func>
 void check_domain_error(Func f) {
 #if math_errhandling & MATH_ERRNO
   errno = EACCES;
-#endif
-#if math_errhandling & MATH_ERREXCEPT
-  std::feclearexcept(FE_INVALID);
 #endif
   f();
 #if math_errhandling & MATH_ERRNO
   assert(errno == EDOM);
 #endif
-#if math_errhandling & MATH_ERREXCEPT
-  assert(std::fetestexcept(FE_INVALID));
-#endif
 }
 
 #endif // TEST_SF_CMATH_COMMON_H

>From 6b65307f26de7474c6b3f6789c8bd49e9b9787d7 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 28 Jun 2026 23:37:54 +0200
Subject: [PATCH 29/52] test: cleanups + more NaN tests

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 28 ++++++++++---------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 068ca81e04b83..fffbb72557a5a 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -18,30 +18,32 @@
 #include <limits>
 
 #include "common.h"
-#include "test_macros.h"
 #include "type_algorithms.h"
 
 struct TestFloatingPoint {
   template <class T>
   void operator()() const {
+    // sample value testing
     assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-
     assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
-
-    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
-
-    assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
-
-    // m == 0 reduces to the ordinary Laguerre polynomial: L_2(2) = (4 - 8 + 2) / 2 = -1.
     assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
+    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
+    assert(between(60.124f, std::assoc_laguerref(2, 10, T(0.5)), 60.126f));
 
+    // return type: float
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
-    check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
-
-    // The associated Laguerre polynomials are defined for all real x: a negative
-    // argument is in-domain and must not report a domain error. L_1^0(-1) = 1 - x = 2.
-    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, -1.f), 2.01f)); });
+    // NaN input -> NaN output (w/o domain error)
+    auto check_nan = [](T nan) {
+      check_no_domain_error([nan] { assert(std::isnan(std::assoc_laguerref(0, 0, nan))); });
+    };
+    if (std::numeric_limits<T>::has_quiet_NaN)
+      check_nan(std::numeric_limits<T>::quiet_NaN());
+    if (std::numeric_limits<T>::has_signaling_NaN)
+      check_nan(std::numeric_limits<T>::signaling_NaN());
+
+    // negative x: no domain error
+    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, T(-1)), 2.01f)); });
   }
 };
 

>From ddd4eef0a09edb02e8e486fb587bf5e64d538b65 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 00:00:33 +0200
Subject: [PATCH 30/52] impl assoc_laguerrel

---
 libcxx/include/__math/special_functions.h     | 1 +
 libcxx/src/mathematical_special_functions.cpp | 5 +++++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 89c75cfc04585..0cfd484955b15 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -27,6 +27,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 // assoc_laguerre
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept;
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 3c0af96fb0c19..980af019748db 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -66,10 +66,15 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
 }
 } // namespace
 
+// assoc_laguerre
 float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept {
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From a35b978e0601bea7a9accf072c175a4c8b182c97 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 00:23:48 +0200
Subject: [PATCH 31/52] test: assoc_laguerrel

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 78 ++++++++-----------
 1 file changed, 34 insertions(+), 44 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index fffbb72557a5a..0a9abaf2f4f24 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -9,54 +9,44 @@
 // REQUIRES: std-at-least-c++17
 
 // <cmath>
-//
-// [sf.cmath.assoc.laguerre], associated Laguerre polynomials
-// float               assoc_laguerref(unsigned n, unsigned m, float x);
+
+// float       assoc_laguerref(unsigned n, unsigned m, float x);
+// long double assoc_laguerrel(unsigned n, unsigned m, long double x);
 
 #include <cassert>
 #include <cmath>
 #include <limits>
 
 #include "common.h"
-#include "type_algorithms.h"
-
-struct TestFloatingPoint {
-  template <class T>
-  void operator()() const {
-    // sample value testing
-    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-    assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
-    assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
-    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
-    assert(between(60.124f, std::assoc_laguerref(2, 10, T(0.5)), 60.126f));
-
-    // return type: float
-    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
-
-    // NaN input -> NaN output (w/o domain error)
-    auto check_nan = [](T nan) {
-      check_no_domain_error([nan] { assert(std::isnan(std::assoc_laguerref(0, 0, nan))); });
-    };
-    if (std::numeric_limits<T>::has_quiet_NaN)
-      check_nan(std::numeric_limits<T>::quiet_NaN());
-    if (std::numeric_limits<T>::has_signaling_NaN)
-      check_nan(std::numeric_limits<T>::signaling_NaN());
-
-    // negative x: no domain error
-    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, T(-1)), 2.01f)); });
-  }
-};
-
-struct TestIntegral {
-  template <class T>
-  void operator()() const {
-    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-
-    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
-  }
-};
-
-int main() {
-  types::for_each(types::floating_point_types{}, TestFloatingPoint{});
-  types::for_each(types::integral_types{}, TestIntegral{});
+
+// Each suffixed overload is fixed to a single floating-point type, so it is
+// tested directly with that type. The unsuffixed std::assoc_laguerre overloads
+// (and the integer-argument overload) are not implemented yet.
+template <class T, class Func>
+void test(Func assoc_laguerre) {
+  // sample values
+  assert(between(0.99, assoc_laguerre(0, 0, T(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, T(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, T(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, T(2)), 0.01));
+  assert(between(60.124, assoc_laguerre(2, 10, T(0.5)), 60.126));
+
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, T(0))), T>);
+
+  // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
+  auto test_nan = [&](T nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
+  if (std::numeric_limits<T>::has_quiet_NaN)
+    test_nan(std::numeric_limits<T>::quiet_NaN());
+  if (std::numeric_limits<T>::has_signaling_NaN)
+    test_nan(std::numeric_limits<T>::signaling_NaN());
+
+  // A negative argument is in the domain (no domain error).
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, T(-1)), 2.01)); });
 }
+
+int main(int, char**) {
+  test<float>([](unsigned __n, unsigned __m, float __x) { return std::assoc_laguerref(__n, __m, __x); });
+  test<long double>([](unsigned __n, unsigned __m, long double __x) { return std::assoc_laguerrel(__n, __m, __x); });
+
+  return 0;
+}
\ No newline at end of file

>From 96e8004c4a6f41343f1257898d04efbbd01f6457 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 21:43:26 +0200
Subject: [PATCH 32/52] re-introduce __math::__assoc_laguerre -> only ABI
 export internal API

---
 libcxx/include/__math/special_functions.h     | 16 ++++++++++++++--
 libcxx/src/mathematical_special_functions.cpp |  7 +++++--
 2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 0cfd484955b15..62ae09594bea3 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
 
 #include <__config>
+#include <__configuration/attributes.h>
 #include <__math/copysign.h>
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
@@ -26,8 +27,19 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
 // assoc_laguerre
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept;
+namespace __math {
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
+} // namespace __math
+
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
+assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 980af019748db..ed8109c7e482c 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -19,6 +19,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
+namespace __math {
 namespace {
 // Error policy for all Boost.Math calls: report domain/pole/overflow/evaluation
 // errors via errno (errno_on_error) instead of throwing. Boost sets errno to
@@ -67,14 +68,16 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
 } // namespace
 
 // assoc_laguerre
-float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+float __assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
-long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept {
+long double __assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+} // namespace __math
+
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From 34d62b1da4598efb45c79363654566b9ca6677aa Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 21:43:57 +0200
Subject: [PATCH 33/52] add exported ABI symbols to abilist

---
 ...-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...nux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...werpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...rpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...nux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...known-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...own-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 4 +++-
 ...n-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 2 ++
 9 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 74283373d1b65..7889109825e10 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1700,6 +1700,8 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 291149608c9c7..3e139e30496a6 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1338,6 +1338,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index da6d608af462c..304817ba0cffb 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -686,6 +686,8 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEyPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock11from_time_tEi', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6ee31613d5320..d5e37635fa511 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -686,6 +686,8 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock11from_time_tEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 92a37db24ac5f..a59b72d695b55 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1690,6 +1690,8 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 7b09d3dd3f75c..abef314dc14f4 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1338,6 +1338,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 725ed669fb561..86bc3dec5bd34 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1350,6 +1350,8 @@
 {'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 160, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 717d4c9bee726..11330e7eaf442 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1349,6 +1349,8 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
@@ -2030,4 +2032,4 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 45db143b4cf4a..460bd21a2fecf 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1320,6 +1320,8 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}

>From 7e1ed83abf9818cb95663ed9077215f102873666 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:10:37 +0200
Subject: [PATCH 34/52] impl std::assoc_laguerre(double)

---
 libcxx/include/__math/special_functions.h     | 6 ++++++
 libcxx/src/mathematical_special_functions.cpp | 4 ++++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 782e441562c00..8fd363919e5cc 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -30,6 +30,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 // assoc_laguerre
 namespace __math {
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI double __assoc_laguerre(unsigned, unsigned, double) noexcept;
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
@@ -37,6 +38,11 @@ namespace __math {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+template <class = int>
+_LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
 assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index ed8109c7e482c..0f6e3a850ad73 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -72,6 +72,10 @@ float __assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+double __assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+
 long double __assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }

>From 8b1f5f6ded44a8cbd009c03310faa645d320bb97 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:20:15 +0200
Subject: [PATCH 35/52] fixup impl std::assoc_laguerre(double)

---
 libcxx/include/__math/special_functions.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 8fd363919e5cc..b48b9ffdb129c 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -39,7 +39,7 @@ namespace __math {
 }
 
 template <class = int>
-_LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 

>From fbbd70784b3e8bc65aa5e3b18870d936ea438e2e Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:43:07 +0200
Subject: [PATCH 36/52] fixup: assoc_laguerre(dbl)

---
 libcxx/include/__math/special_functions.h                   | 3 +--
 .../std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp    | 6 +++---
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index b48b9ffdb129c..20268dd1bd7d6 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -38,8 +38,7 @@ namespace __math {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-template <class = int>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 0a9abaf2f4f24..b9223f6d66a2c 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -19,9 +19,9 @@
 
 #include "common.h"
 
-// Each suffixed overload is fixed to a single floating-point type, so it is
-// tested directly with that type. The unsuffixed std::assoc_laguerre overloads
-// (and the integer-argument overload) are not implemented yet.
+// Each overload is fixed to a single floating-point type, so it is tested
+// directly with that type. The integer-argument overload
+// (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
 template <class T, class Func>
 void test(Func assoc_laguerre) {
   // sample values

>From 7f5e5515ad7c1e551ff1b7cecf57772211f3b376 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:12:00 +0200
Subject: [PATCH 37/52] test: general cleanups

---
 .../std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp    | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index b9223f6d66a2c..d446e017494c4 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -45,8 +45,8 @@ void test(Func assoc_laguerre) {
 }
 
 int main(int, char**) {
-  test<float>([](unsigned __n, unsigned __m, float __x) { return std::assoc_laguerref(__n, __m, __x); });
-  test<long double>([](unsigned __n, unsigned __m, long double __x) { return std::assoc_laguerrel(__n, __m, __x); });
+  test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
   return 0;
-}
\ No newline at end of file
+}

>From 340ed9ca7b9f543fd3ffa8110f29863dc7fcef4f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:14:47 +0200
Subject: [PATCH 38/52] test: std::assoc_laguerre(double)

---
 .../test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp  | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index d446e017494c4..9c2bc2026a348 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -10,6 +10,7 @@
 
 // <cmath>
 
+// double      assoc_laguerre(unsigned n, unsigned m, double x);
 // float       assoc_laguerref(unsigned n, unsigned m, float x);
 // long double assoc_laguerrel(unsigned n, unsigned m, long double x);
 
@@ -45,6 +46,8 @@ void test(Func assoc_laguerre) {
 }
 
 int main(int, char**) {
+  test<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+
   test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
   test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 

>From 589d01b5fce70f06068b3304556145bc1c284ebe Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:20:51 +0200
Subject: [PATCH 39/52] test: nodiscard/noexcept for assoc_laguerre(double),
 assoc_laguerrel

---
 .../test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp  | 3 +++
 .../libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp  | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index a4c471e4b3168..8b8826d03de01 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -15,6 +15,9 @@
 void test() {
   // clang-format off
   // assoc_laguerre
+  std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   // clang-format on
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 9994e6a018f8d..70984fc0c83bf 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -16,5 +16,8 @@
 
 void test() {
   // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0));
+
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
+  ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
 }

>From db297e6b6a155fdd5ea0bb4978f5e267c14a773e Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:42:41 +0200
Subject: [PATCH 40/52] add ABI symbol to abilist for assoc_laguerre(double)

---
 ...m64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 .../powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...owerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ..._64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...nknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...nown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 1 +
 9 files changed, 9 insertions(+)

diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6ab143c0113f3..9f70dd6550fc7 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1701,6 +1701,7 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 08d42d44e4827..4152a583ec5a3 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1339,6 +1339,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 98fd189eaa3f7..861750f606c77 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -687,6 +687,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEyPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 9e1db57fc315b..347d9f9f23d70 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -687,6 +687,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index a59b72d695b55..cd3f3e4fda9a2 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1690,6 +1690,7 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 61bd10d997a1b..421f4839f762e 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1339,6 +1339,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 286ad56406ee2..d59e224c42922 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1351,6 +1351,7 @@
 {'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 160, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 9fd2edf63e6ef..d2b084bace550 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1350,6 +1350,7 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 51c27fea37ce9..7e57931398e85 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1321,6 +1321,7 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}

>From e8984b706592b5b2ff08bdd19020330498423157 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:12:02 +0200
Subject: [PATCH 41/52] impl assoc_laguerre(Int)

---
 libcxx/include/__math/special_functions.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 20268dd1bd7d6..5021e5a54b634 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -47,6 +47,11 @@ assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+template <class _Int, std::enable_if_t<std::is_integral_v<_Int>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 // hermite

>From c8c5eb6a2040040b6a5c67c42128172a4c5661a8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:18:00 +0200
Subject: [PATCH 42/52] test: nodiscard/except assoc_laguerre(Int)

---
 .../numerics/c.math/sf.cmath/nodiscard.verify.cpp      |  1 +
 .../numerics/c.math/sf.cmath/noexcept.compile.pass.cpp | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 8b8826d03de01..b83070f4f984d 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -16,6 +16,7 @@ void test() {
   // clang-format off
   // assoc_laguerre
   std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 70984fc0c83bf..cca9c1e924a8c 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -13,6 +13,14 @@
 #include <cmath>
 
 #include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestIntegral {
+  template <class T>
+  void operator()() const {
+    ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, T{}));
+  }
+};
 
 void test() {
   // assoc_laguerre
@@ -20,4 +28,6 @@ void test() {
 
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
   ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
+
+  types::for_each(types::integral_types{}, TestIntegral{});
 }

>From 5946b7d3c07ea7f4dc898e9750313838fd3a99a8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:19:29 +0200
Subject: [PATCH 43/52] wip: test: assoc_laguerre(Int)

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 48 ++++++++++++-------
 1 file changed, 32 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 9c2bc2026a348..b39e20fe6288a 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -17,39 +17,55 @@
 #include <cassert>
 #include <cmath>
 #include <limits>
+#include <type_traits>
 
 #include "common.h"
+#include "type_algorithms.h"
 
 // Each overload is fixed to a single floating-point type, so it is tested
 // directly with that type. The integer-argument overload
 // (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
-template <class T, class Func>
+template <class ExpectedFuncRet, class FuncArgX, class Func>
 void test(Func assoc_laguerre) {
   // sample values
-  assert(between(0.99, assoc_laguerre(0, 0, T(0)), 1.01));
-  assert(between(0.99, assoc_laguerre(1, 1, T(1)), 1.01));
-  assert(between(-1.01, assoc_laguerre(2, 0, T(2)), -0.99));
-  assert(between(-0.01, assoc_laguerre(2, 2, T(2)), 0.01));
-  assert(between(60.124, assoc_laguerre(2, 10, T(0.5)), 60.126));
+  assert(between(0.99, assoc_laguerre(0, 0, FuncArgX(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, FuncArgX(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, FuncArgX(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, FuncArgX(2)), 0.01));
+  if constexpr (std::is_floating_point_v<FuncArgX>)
+    assert(between(60.124, assoc_laguerre(2, 10, FuncArgX(0.5)), 60.126));
 
-  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, T(0))), T>);
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, FuncArgX(0))), ExpectedFuncRet>);
 
   // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
-  auto test_nan = [&](T nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
-  if (std::numeric_limits<T>::has_quiet_NaN)
-    test_nan(std::numeric_limits<T>::quiet_NaN());
-  if (std::numeric_limits<T>::has_signaling_NaN)
-    test_nan(std::numeric_limits<T>::signaling_NaN());
+  if constexpr (std::is_floating_point_v<FuncArgX>) {
+    auto test_nan = [&](FuncArgX nan) {
+      check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); });
+    };
+    if (std::numeric_limits<FuncArgX>::has_quiet_NaN)
+      test_nan(std::numeric_limits<FuncArgX>::quiet_NaN());
+    if (std::numeric_limits<FuncArgX>::has_signaling_NaN)
+      test_nan(std::numeric_limits<FuncArgX>::signaling_NaN());
+  }
 
   // A negative argument is in the domain (no domain error).
-  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, T(-1)), 2.01)); });
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, FuncArgX(-1)), 2.01)); });
 }
 
+struct TestIntegral {
+  template <class Int>
+  void operator()() const {
+    test<double, Int>([](unsigned n, unsigned m, Int x) { return std::assoc_laguerre(n, m, x); });
+  }
+};
+
 int main(int, char**) {
-  test<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+  test<double, double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+
+  test<float, float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test<long double, long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
-  test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
-  test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
+  // types::for_each(types::integral_types{}, TestIntegral{}); WIP
 
   return 0;
 }

>From 619773ba99f60f0d799ca98488fa71cc246e2c47 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 17:48:09 +0200
Subject: [PATCH 44/52] fix gcc build: gcc defines __STDCPP_FLOATXX_T__ even
 though we dont ship <stdfloat> yet -> breaks Boost

---
 libcxx/src/mathematical_special_functions.cpp | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 0f6e3a850ad73..b8e14967740f1 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -12,6 +12,22 @@
 #include <optional>
 #include <type_traits>
 
+// GCC defines __STDCPP_FLOATnn_T__ whenever the _Floatnn extended types exist at the
+// language level, independent of the standard library. libc++ currently ships no
+// <stdfloat>, so std::floatnn_t is never declared -- but Boost.Math keys its
+// std::floatnn_t overloads off these macros and would reference the missing types.
+// Suppress those overloads while <stdfloat> is unavailable. The __has_include guard is
+// the same condition Boost uses to include <stdfloat>, so this workaround disables itself
+// automatically once libc++ provides the header (the overloads then light up on their own
+// -- no manual re-enable needed here).
+#if !__has_include(<stdfloat>)
+#  undef __STDCPP_FLOAT16_T__
+#  undef __STDCPP_FLOAT32_T__
+#  undef __STDCPP_FLOAT64_T__
+#  undef __STDCPP_FLOAT128_T__
+#  undef __STDCPP_BFLOAT16_T__
+#endif
+
 #define BOOST_MATH_NO_EXCEPTIONS
 #include <boost/math/special_functions.hpp>
 

>From 3c8e6c21dca99ac9b83fe264c70255710d20c21f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 18:01:47 +0200
Subject: [PATCH 45/52] fix android abilist

---
 ...linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 421f4839f762e..08b901a94f394 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1340,8 +1340,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjg', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}

>From e4c48175cfc00429e93c92a30aa81e67201f4866 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 18:02:21 +0200
Subject: [PATCH 46/52] fix boost math thread support

---
 libcxx/src/mathematical_special_functions.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index b8e14967740f1..c37811bdf52ea 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -28,6 +28,14 @@
 #  undef __STDCPP_BFLOAT16_T__
 #endif
 
+// Boost.Math detects thread support via __has_include(<thread>/<mutex>/...), but libc++
+// ships those headers even when threads are disabled (_LIBCPP_HAS_THREADS == 0), so the
+// detection wrongly enables std::mutex use and breaks on no-thread targets (e.g. picolibc).
+// Tell Boost there are no threads; lazy-init tables don't need locking without threads.
+#if !_LIBCPP_HAS_THREADS
+#  define BOOST_MATH_DISABLE_THREADS
+#endif
+
 #define BOOST_MATH_NO_EXCEPTIONS
 #include <boost/math/special_functions.hpp>
 

>From 60635ce92abd69cad3a5161a78a5a2f55281d9b8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 21:47:33 +0200
Subject: [PATCH 47/52] test: fix apple's math_errhandling usage. runtime not
 macro constant

---
 .../std/numerics/c.math/sf.cmath/common.h     | 23 +++++++++----------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
index 723422bf7038d..ca6de9ea868bd 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/common.h
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -26,26 +26,25 @@ bool between(type_identity_t<T> lower, T value, type_identity_t<T> upper) {
   return lower < value && value < upper;
 }
 
+// Note: math_errhandling must be queried with a runtime `if`, not `#if`. On Apple
+// platforms it is defined as a function call (__math_errhandling()), so it is not a
+// preprocessor constant and would break `#if` under -Wundef.
 template <class Func>
 void check_no_domain_error(Func f) {
-#if math_errhandling & MATH_ERRNO
-  errno = EACCES;
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    errno = EACCES;
   f();
-#if math_errhandling & MATH_ERRNO
-  assert(errno == EACCES);
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    assert(errno == EACCES);
 }
 
 template <class Func>
 void check_domain_error(Func f) {
-#if math_errhandling & MATH_ERRNO
-  errno = EACCES;
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    errno = EACCES;
   f();
-#if math_errhandling & MATH_ERRNO
-  assert(errno == EDOM);
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    assert(errno == EDOM);
 }
 
 #endif // TEST_SF_CMATH_COMMON_H

>From 8201618481b60722093b07eff23b8bcb3b274e05 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:16:59 +0200
Subject: [PATCH 48/52] minimze boost includes

---
 libcxx/src/mathematical_special_functions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index c37811bdf52ea..dc046b3a845df 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -37,7 +37,7 @@
 #endif
 
 #define BOOST_MATH_NO_EXCEPTIONS
-#include <boost/math/special_functions.hpp>
+#include <boost/math/special_functions/laguerre.hpp>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS

>From 4d11bdbe96fca5653a71146ff3fea197180f13ef Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:22:46 +0200
Subject: [PATCH 49/52] patch boost: make it compilable w/o localization
 support

---
 third-party/boost-math-libcxx.patch | 47 +++++++++++++++++++++++++++++
 third-party/update_boost_math.sh    | 10 ++++++
 2 files changed, 57 insertions(+)
 create mode 100644 third-party/boost-math-libcxx.patch

diff --git a/third-party/boost-math-libcxx.patch b/third-party/boost-math-libcxx.patch
new file mode 100644
index 0000000000000..f7692a7e54b67
--- /dev/null
+++ b/third-party/boost-math-libcxx.patch
@@ -0,0 +1,47 @@
+# Local libc++ patch for the vendored Boost.Math snapshot.
+#
+# Boost.Math only uses <sstream>/<iomanip> and detail::prec_format to build the
+# what() text of the exceptions it throws. That machinery sits OUTSIDE Boost's own
+# `#ifndef BOOST_MATH_NO_EXCEPTIONS` guard (unlike the adjacent <stdexcept> /
+# throw_exception.hpp includes), so it is compiled even when exceptions are disabled.
+# libc++ builds the Math Special Functions back-end with BOOST_MATH_NO_EXCEPTIONS,
+# and on configurations without localization (e.g. -DLIBCXX_ENABLE_LOCALIZATION=OFF)
+# libc++ ships no functional <sstream>, so prec_format fails to compile.
+#
+# This patch guards the iostream-only message formatting under BOOST_MATH_NO_EXCEPTIONS.
+# Reported upstream: https://github.com/boostorg/math/issues (see PR description).
+# Re-applied by update_boost_math.sh after each snapshot refresh; drop once upstream.
+diff --git a/boost-math/include/boost/math/policies/error_handling.hpp b/boost-math/include/boost/math/policies/error_handling.hpp
+index d7ba694564ba..da167171f650 100644
+--- a/boost-math/include/boost/math/policies/error_handling.hpp
++++ b/boost-math/include/boost/math/policies/error_handling.hpp
+@@ -26,9 +26,9 @@
+ #include <complex>
+ #include <cstdint>
+ #include <cstring>
++#ifndef BOOST_MATH_NO_EXCEPTIONS
+ #include <iomanip>
+ #include <sstream>
+-#ifndef BOOST_MATH_NO_EXCEPTIONS
+ #include <stdexcept>
+ #endif
+ #include <string>
+@@ -95,6 +95,10 @@ T user_indeterminate_result_error(const char* function, const char* message, con
+ namespace detail
+ {
+ 
++// prec_format is only used to build exception what() messages, so it is gated on
++// exceptions being enabled. It relies on <sstream>, which is unavailable in some
++// standard-library configurations (e.g. libc++ built without localization).
++#ifndef BOOST_MATH_NO_EXCEPTIONS
+ template <class T>
+ inline std::string prec_format(const T& val)
+ {
+@@ -125,6 +129,7 @@ inline std::string prec_format<std::float128_t>(const std::float128_t& val)
+ }
+ 
+ #endif
++#endif // BOOST_MATH_NO_EXCEPTIONS
+ 
+ inline void replace_all_in_string(std::string& result, const char* what, const char* with)
+ {
diff --git a/third-party/update_boost_math.sh b/third-party/update_boost_math.sh
index 27d8816b7cb12..a4276cdb74028 100755
--- a/third-party/update_boost_math.sh
+++ b/third-party/update_boost_math.sh
@@ -40,3 +40,13 @@ sed -i 's|target_include_directories(boost_math INTERFACE include)|target_includ
 grep -q 'target_include_directories(boost_math SYSTEM INTERFACE include)' \
     ${SCRIPT_DIR}/boost-math/CMakeLists.txt \
     || { echo "ERROR: SYSTEM include patch failed -- upstream CMakeLists.txt structure changed"; exit 1; }
+
+# Guard Boost.Math's iostream-only exception-message formatting (prec_format / <sstream>)
+# under BOOST_MATH_NO_EXCEPTIONS, so the no-exceptions back-end builds without <sstream>.
+# Needed for libc++ configurations without localization. Drop once fixed upstream.
+patch -p1 -d ${SCRIPT_DIR} < ${SCRIPT_DIR}/boost-math-libcxx.patch
+
+# Verify the patch landed -- fail loudly if upstream restructured error_handling.hpp.
+grep -q 'prec_format is only used to build exception' \
+    ${SCRIPT_DIR}/boost-math/include/boost/math/policies/error_handling.hpp \
+    || { echo "ERROR: no-exceptions iostream patch failed -- upstream error_handling.hpp structure changed"; exit 1; }

>From 276df4abe72a9ba6d4979d5922b466445aa406b9 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:32:27 +0200
Subject: [PATCH 50/52] apply boost patch

---
 .../include/boost/math/policies/error_handling.hpp         | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/third-party/boost-math/include/boost/math/policies/error_handling.hpp b/third-party/boost-math/include/boost/math/policies/error_handling.hpp
index d7ba694564ba4..da167171f650d 100644
--- a/third-party/boost-math/include/boost/math/policies/error_handling.hpp
+++ b/third-party/boost-math/include/boost/math/policies/error_handling.hpp
@@ -26,9 +26,9 @@
 #include <complex>
 #include <cstdint>
 #include <cstring>
+#ifndef BOOST_MATH_NO_EXCEPTIONS
 #include <iomanip>
 #include <sstream>
-#ifndef BOOST_MATH_NO_EXCEPTIONS
 #include <stdexcept>
 #endif
 #include <string>
@@ -95,6 +95,10 @@ T user_indeterminate_result_error(const char* function, const char* message, con
 namespace detail
 {
 
+// prec_format is only used to build exception what() messages, so it is gated on
+// exceptions being enabled. It relies on <sstream>, which is unavailable in some
+// standard-library configurations (e.g. libc++ built without localization).
+#ifndef BOOST_MATH_NO_EXCEPTIONS
 template <class T>
 inline std::string prec_format(const T& val)
 {
@@ -125,6 +129,7 @@ inline std::string prec_format<std::float128_t>(const std::float128_t& val)
 }
 
 #endif
+#endif // BOOST_MATH_NO_EXCEPTIONS
 
 inline void replace_all_in_string(std::string& result, const char* what, const char* with)
 {

>From 6bbed03306814ee7a45418ba8e01e619852d7d94 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:53:16 +0200
Subject: [PATCH 51/52] doc: add boost issue

---
 third-party/boost-math-libcxx.patch | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/third-party/boost-math-libcxx.patch b/third-party/boost-math-libcxx.patch
index f7692a7e54b67..8593b077a2db2 100644
--- a/third-party/boost-math-libcxx.patch
+++ b/third-party/boost-math-libcxx.patch
@@ -9,7 +9,7 @@
 # libc++ ships no functional <sstream>, so prec_format fails to compile.
 #
 # This patch guards the iostream-only message formatting under BOOST_MATH_NO_EXCEPTIONS.
-# Reported upstream: https://github.com/boostorg/math/issues (see PR description).
+# Reported upstream: https://github.com/boostorg/math/issues/1412
 # Re-applied by update_boost_math.sh after each snapshot refresh; drop once upstream.
 diff --git a/boost-math/include/boost/math/policies/error_handling.hpp b/boost-math/include/boost/math/policies/error_handling.hpp
 index d7ba694564ba..da167171f650 100644

>From 441f2f0089823971d515573e8f8074fddd2e32a1 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 08:34:54 +0200
Subject: [PATCH 52/52] guard via
 _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS

---
 libcxx/include/__configuration/availability.h |  5 +++++
 libcxx/include/__math/special_functions.h     | 20 ++++++++++++-------
 .../c.math/sf.cmath/nodiscard.verify.cpp      |  4 ++++
 .../c.math/sf.cmath/noexcept.compile.pass.cpp |  4 ++++
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   |  4 ++++
 .../libcxx/test/features/availability.py      |  8 ++++++++
 6 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 735af5d431cb1..72eb50721338f 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -271,6 +271,11 @@
 #define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20
 #define _LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20_ATTRIBUTE
 
+// This controls the availability of the C++17 mathematical special functions
+// ([sf.cmath]). Their implementation lives in the built library (backed by Boost.Math).
+#define _LIBCPP_AVAILABILITY_HAS_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_INTRODUCED_IN_LLVM_23
+#define _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE
+
 // This controls the availability of the C++20 time zone database.
 // The parser code is built in the library.
 #define _LIBCPP_AVAILABILITY_HAS_TZDB _LIBCPP_INTRODUCED_IN_LLVM_19
diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 5021e5a54b634..546600b72bce0 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -29,26 +29,32 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 // assoc_laguerre
 namespace __math {
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI double __assoc_laguerre(unsigned, unsigned, double) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI float
+__assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI double
+__assoc_laguerre(unsigned, unsigned, double) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI long double
+__assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI double
+assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI long double
 assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
 template <class _Int, std::enable_if_t<std::is_integral_v<_Int>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_HIDE_FROM_ABI double
+assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index b83070f4f984d..598311f20917d 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// The functions carry availability markup, so referencing them fails to compile against a
+// back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // Check that functions are marked [[nodiscard]]
 
 #include <cmath>
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index cca9c1e924a8c..8f9dd87c0ee87 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// The functions carry availability markup, so referencing them fails to compile against a
+// back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // Check that functions are marked noexcept
 
 #include <cmath>
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index b39e20fe6288a..91a7c4a3d2047 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// These functions are implemented in the built library, so a program using them fails to
+// load against a back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // <cmath>
 
 // double      assoc_laguerre(unsigned n, unsigned m, double x);
diff --git a/libcxx/utils/libcxx/test/features/availability.py b/libcxx/utils/libcxx/test/features/availability.py
index c0f2a3d4cdc88..7bb1add0e9e9c 100644
--- a/libcxx/utils/libcxx/test/features/availability.py
+++ b/libcxx/utils/libcxx/test/features/availability.py
@@ -211,4 +211,12 @@
             cfg.available_features,
         ),
     ),
+    # Tests that require the C++17 mathematical special functions in the built library
+    Feature(
+        name="availability-mathematical_special_functions-missing",
+        when=lambda cfg: BooleanExpression.evaluate(
+            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-23)",
+            cfg.available_features,
+        ),
+    ),
 ]


From libcxx-commits at lists.llvm.org  Sat Jul  4 05:22:03 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 05:22:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][math][c++17] P0226R1 -
 Mathematical Special Functions: infra + `std::assoc_laguerre` (PR #205649)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205649@github.com>
Message-ID: <6a48faeb.6c8668f2.346e76.d6d8@mx.google.com>


https://github.com/PaulXiCao updated https://github.com/llvm/llvm-project/pull/205649

>From 7555447d01b3e6bd5127f4ffbfaeeb223cf9ea81 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 20:52:48 +0200
Subject: [PATCH 01/55] link against Boost.Math

---
 libcxx/CMakeLists.txt     | 8 ++++++++
 libcxx/src/CMakeLists.txt | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..845042f15495f 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -899,6 +899,14 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+
+# Header-only Boost.Math (INTERFACE target `boost_math`). Lives outside the
+# libcxx source tree, so a binary dir must be supplied.
+add_subdirectory(
+  ${LLVM_THIRD_PARTY_DIR}/boost-math
+  ${LIBCXX_BINARY_DIR}/third-party/boost-math
+  EXCLUDE_FROM_ALL)
+
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index de7817ad69f26..c882446596a3e 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -176,6 +176,7 @@ add_library(cxx_shared SHARED ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(cxx_shared PUBLIC cxx-headers runtimes-libc-shared
                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                  PRIVATE boost_math
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_shared
   PROPERTIES
@@ -263,6 +264,7 @@ add_library(cxx_static STATIC ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(cxx_static PUBLIC cxx-headers runtimes-libc-static
                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                  PRIVATE boost_math
                                   PRIVATE libcxx-abi-static
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_static

>From fe89c404516cfea93ab67939b48a15c74213cd59 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 20:53:37 +0200
Subject: [PATCH 02/55] add empty src/mathematical_special_functions.cpp

---
 libcxx/src/CMakeLists.txt                     |  1 +
 libcxx/src/mathematical_special_functions.cpp | 17 +++++++++++++++++
 2 files changed, 18 insertions(+)
 create mode 100644 libcxx/src/mathematical_special_functions.cpp

diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index c882446596a3e..d2168decbfdba 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -29,6 +29,7 @@ set(LIBCXX_SOURCES
   include/ryu/ryu.h
   include/to_chars_floating_point.h
   include/from_chars_floating_point.h
+  mathematical_special_functions.cpp
   memory.cpp
   memory_resource.cpp
   new_handler.cpp
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
new file mode 100644
index 0000000000000..db07d61f89319
--- /dev/null
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -0,0 +1,17 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+
+namespace {}
+
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+_LIBCPP_END_NAMESPACE_STD

>From 7fbc20f4bea5a80fdebfb22eadee50cb90698b46 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 21:06:04 +0200
Subject: [PATCH 03/55] add simple test for std::assoc_laguerref (fails atm)

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
new file mode 100644
index 0000000000000..1a12c8f81a16c
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Missing powl
+// XFAIL: LLVM-LIBC-FIXME
+
+// REQUIRES: std-at-least-c++17
+
+// <cmath>
+//
+// [sf.cmath.assoc.laguerre], associated Laguerre polynomials
+// floating-point-type assoc_laguerre(unsigned n, unsigned m, floating-point-type x);
+// float               assoc_laguerref(unsigned n, unsigned m, float x);
+// long double         assoc_laguerrel(unsigned n, unsigned m, long double x);
+
+#include <cmath>
+
+int main(int, char**) {
+  std::assoc_laguerref(0, 0, 0.0f);
+
+  return 0;
+}

>From c7e11e92b9c70fe1f319a8f959ce1c11cbbfa9cc Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 21:52:09 +0200
Subject: [PATCH 04/55] handling MATH_ERRNO, MATH_ERREXCEPT via __sf_result

---
 libcxx/include/__math/special_functions.h | 25 +++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 6b217852e2f8c..86b3ac9cb665e 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -15,7 +15,10 @@
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_integral.h>
+#include <cerrno>
+#include <cfenv>
 #include <limits>
+#include <math.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -25,6 +28,28 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 17
 
+namespace __math {
+template <class _Tp>
+struct __sf_result {
+  bool __domain_error;
+  _Tp __ret;
+
+  operator _Tp() const {
+#  if math_errhandling & MATH_ERRNO
+    if (__domain_error)
+      errno = EDOM;
+#  endif
+
+#  if math_errhandling & MATH_ERREXCEPT
+    if (__domain_error)
+      feraiseexcept(FE_INVALID);
+#  endif
+
+    return __ret;
+  }
+};
+} // namespace __math
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).

>From 23081681878b007cb50f1ed115ec316fa956b4cf Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 22:00:10 +0200
Subject: [PATCH 05/55] impl assoc_laguerref

---
 libcxx/include/__math/special_functions.h     |  9 +++++
 libcxx/src/mathematical_special_functions.cpp | 37 ++++++++++++++++++-
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 86b3ac9cb665e..d0d6e56507e85 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -25,6 +25,7 @@
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 #if _LIBCPP_STD_VER >= 17
 
@@ -48,8 +49,15 @@ struct __sf_result {
     return __ret;
   }
 };
+
+_LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
+
 } // namespace __math
 
+inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).
@@ -104,6 +112,7 @@ _LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, _Integer __x) {
 
 #endif // _LIBCPP_STD_VER >= 17
 
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index db07d61f89319..e18e9c5f2797c 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -7,11 +7,46 @@
 //===----------------------------------------------------------------------===//
 
 #include <__config>
+#include <boost/math/special_functions.hpp>
+#include <cmath>
+#include <optional>
+#include <type_traits>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-namespace {}
+namespace __math {
+namespace {
+template <class _Ret>
+optional<_Ret> __check_nan() {
+  return nullopt;
+}
+
+template <class _Ret, class _Arg, class... _Args>
+optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
+  if constexpr (is_floating_point_v<_Arg>)
+    if (isnan(__arg))
+      return __arg;
+  return __check_nan<_Ret>(__args...);
+}
+
+template <class Func, class... _Args, class _Ret = std::invoke_result_t<Func, _Args...>>
+__sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
+  if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
+    return {.__domain_error = false, .__ret = *__maybe_nan};
+
+  try {
+    return {.__domain_error = false, .__ret = f(__args...)};
+  } catch (...) {
+    return {.__domain_error = true, .__ret = numeric_limits<_Ret>::quiet_NaN()};
+  }
+}
+} // namespace
+
+__sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
+  return invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+} // namespace __math
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From adb174a0e9d29b172b17bc9e0d781db0dff15d5c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 22:34:12 +0200
Subject: [PATCH 06/55] test assoc_laguerref

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 1a12c8f81a16c..fe92daa501c6f 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -6,22 +6,18 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing powl
-// XFAIL: LLVM-LIBC-FIXME
-
 // REQUIRES: std-at-least-c++17
 
 // <cmath>
 //
 // [sf.cmath.assoc.laguerre], associated Laguerre polynomials
-// floating-point-type assoc_laguerre(unsigned n, unsigned m, floating-point-type x);
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
-// long double         assoc_laguerrel(unsigned n, unsigned m, long double x);
 
+#include <cassert>
 #include <cmath>
 
-int main(int, char**) {
-  std::assoc_laguerref(0, 0, 0.0f);
-
-  return 0;
+int main() {
+  // Single value tested against known solution.
+  // Note, underlying Boost.Math is itself well-tested.
+  assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
 }

>From b7bc1fb6247235ce62066d70a30c13608a8672fc Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 18:26:00 +0200
Subject: [PATCH 07/55] test: common.h

---
 .../std/numerics/c.math/sf.cmath/common.h     | 65 +++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/sf.cmath/common.h

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
new file mode 100644
index 0000000000000..867e6af6f6914
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_SF_CMATH_COMMON_H
+#define TEST_SF_CMATH_COMMON_H
+
+#include <cassert>
+#include <cerrno>
+#include <cmath>
+#include <math.h>
+#include <cfenv>
+
+// std::type_identity is C++20 (we need to support C++17 here)
+template <class T>
+struct type_identity {
+  typedef T type;
+};
+template <class T>
+using type_identity_t = typename type_identity<T>::type;
+
+template <class T>
+bool between(type_identity_t<T> lower, T value, type_identity_t<T> upper) {
+  return lower < value && value < upper;
+}
+
+template <class Func>
+void check_no_domain_error(Func f) {
+#if math_errhandling & MATH_ERRNO
+  errno = EACCES;
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  std::feclearexcept(FE_INVALID);
+#endif
+  f();
+#if math_errhandling & MATH_ERRNO
+  assert(errno == EACCES);
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  assert(!std::fetestexcept(FE_INVALID));
+#endif
+}
+
+template <class Func>
+void check_domain_error(Func f) {
+#if math_errhandling & MATH_ERRNO
+  errno = EACCES;
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  std::feclearexcept(FE_INVALID);
+#endif
+  f();
+#if math_errhandling & MATH_ERRNO
+  assert(errno == EDOM);
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  assert(std::fetestexcept(FE_INVALID));
+#endif
+}
+
+#endif // TEST_SF_CMATH_COMMON_H

>From a47f9e902e5c427c84115f4af119eedf8778e182 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 18:26:59 +0200
Subject: [PATCH 08/55] more testing of assoc_laguerref: values, types, domain
 error

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 36 +++++++++++++++++--
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index fe92daa501c6f..eb747efb4c1c6 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,10 +14,40 @@
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
 
 #include <cassert>
+#include <cerrno>
+#include <cfenv>
 #include <cmath>
 
+#include "common.h"
+#include "type_algorithms.h"
+
+struct TestFloatingPoint {
+  template <class T>
+  void operator()() const {
+    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
+
+    assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
+
+    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
+
+    assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
+
+    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
+
+    check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
+  }
+};
+
+struct TestIntegral {
+  template <class T>
+  void operator()() const {
+    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
+
+    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
+  }
+};
+
 int main() {
-  // Single value tested against known solution.
-  // Note, underlying Boost.Math is itself well-tested.
-  assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
+  types::for_each(types::floating_point_types{}, TestFloatingPoint{});
+  types::for_each(types::integral_types{}, TestIntegral{});
 }

>From f8a3b1d891405ff6ab04bfc7a7d615ca2cd54a56 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:15:40 +0200
Subject: [PATCH 09/55] silence boost_math warnings by marking it as `SYSTEM`
 lib

---
 third-party/boost-math/CMakeLists.txt |  2 +-
 third-party/update_boost_math.sh      | 13 +++++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/third-party/boost-math/CMakeLists.txt b/third-party/boost-math/CMakeLists.txt
index a1f6011d95e55..0de1fa2ec01ed 100644
--- a/third-party/boost-math/CMakeLists.txt
+++ b/third-party/boost-math/CMakeLists.txt
@@ -11,7 +11,7 @@ add_library(boost_math INTERFACE)
 
 add_library(Boost::math ALIAS boost_math)
 
-target_include_directories(boost_math INTERFACE include)
+target_include_directories(boost_math SYSTEM INTERFACE include)
 if(NOT CMAKE_VERSION VERSION_LESS "3.19")
   file(GLOB_RECURSE headers include/*.hpp)
   target_sources(boost_math PRIVATE ${headers})
diff --git a/third-party/update_boost_math.sh b/third-party/update_boost_math.sh
index 96cd73ed8c304..27d8816b7cb12 100755
--- a/third-party/update_boost_math.sh
+++ b/third-party/update_boost_math.sh
@@ -27,3 +27,16 @@ echo "****************************************"
 echo "Subsetting Boost.Math ${VERSION}"
 echo "****************************************"
 rm -rf ${SCRIPT_DIR}/boost-math/{.circleci,.drone,.github,build,config,doc,example,meta,reporting,src,test,tools}
+
+echo "****************************************"
+echo "Patching Boost.Math ${VERSION} for libc++"
+echo "****************************************"
+# Mark boost_math include dir as SYSTEM so libc++ consumers don't surface warnings from vendored boost-math code (e.g.
+# -Wdeprecated-redundant-constexpr-static-def). Upstream keeps these for pre-C++17 compat.
+sed -i 's|target_include_directories(boost_math INTERFACE include)|target_include_directories(boost_math SYSTEM INTERFACE include)|' \
+    ${SCRIPT_DIR}/boost-math/CMakeLists.txt
+
+# Verify the patch landed -- fail loudly if upstream renamed the target.
+grep -q 'target_include_directories(boost_math SYSTEM INTERFACE include)' \
+    ${SCRIPT_DIR}/boost-math/CMakeLists.txt \
+    || { echo "ERROR: SYSTEM include patch failed -- upstream CMakeLists.txt structure changed"; exit 1; }

>From 17c19c5c87c5adf5618686cbf1cac445d4bfbfda Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:24:39 +0200
Subject: [PATCH 10/55] add nodiscard test for assoc_laguerref

---
 .../c.math/sf.cmath/nodiscard.verify.cpp      | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7928ebf50681a
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// Check that functions are marked [[nodiscard]]
+
+#include <cmath>
+
+void test() {
+  // clang-format off
+  // assoc_laguerre
+  std::assoc_laguerref(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}

>From 0a7cf75eca52eab4c5d98b4f323f05fbf0187a9c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:27:41 +0200
Subject: [PATCH 11/55] add nodiscard to assoc_laguerref

---
 libcxx/include/__math/special_functions.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index d0d6e56507e85..dee70bec45cd8 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -54,7 +54,8 @@ _LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsi
 
 } // namespace __math
 
-inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 

>From ddfb2c1563bf465cd912f226abe221488a67b0df Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:50:00 +0200
Subject: [PATCH 12/55] insert grouping for assoc_laguerre functions

---
 libcxx/include/__math/special_functions.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index dee70bec45cd8..3100860ec8c70 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -49,9 +49,11 @@ struct __sf_result {
     return __ret;
   }
 };
+} // namespace __math
 
+// assoc_laguerre
+namespace __math {
 _LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
-
 } // namespace __math
 
 [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float

>From 39b98428ea2cf592234512ac3762348f3594087c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 00:09:27 +0200
Subject: [PATCH 13/55] insert grouping for hermite functions

---
 libcxx/include/__math/special_functions.h | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 3100860ec8c70..b8ec0785a3b2b 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -26,7 +26,6 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
-
 #if _LIBCPP_STD_VER >= 17
 
 namespace __math {
@@ -61,6 +60,14 @@ assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+#endif // _LIBCPP_STD_VER >= 17
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+_LIBCPP_END_NAMESPACE_STD
+
+// hermite
+_LIBCPP_BEGIN_NAMESPACE_STD
+#if _LIBCPP_STD_VER >= 17
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).
@@ -114,8 +121,5 @@ _LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, _Integer __x) {
 }
 
 #endif // _LIBCPP_STD_VER >= 17
-
-_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
-
 #endif // _LIBCPP___MATH_SPECIAL_FUNCTIONS_H

>From 145ce5f04732b8f5800f9013411d11ffe97c215f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 00:10:16 +0200
Subject: [PATCH 14/55] add missing ABI annotation

---
 libcxx/include/__math/special_functions.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index b8ec0785a3b2b..e11dc97fd46d4 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -34,7 +34,7 @@ struct __sf_result {
   bool __domain_error;
   _Tp __ret;
 
-  operator _Tp() const {
+  _LIBCPP_HIDE_FROM_ABI operator _Tp() const {
 #  if math_errhandling & MATH_ERRNO
     if (__domain_error)
       errno = EDOM;

>From 2328d31c7918995c6895836c69e1371250675427 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:16:15 +0200
Subject: [PATCH 15/55] TU: guard by C++ >= 17

---
 libcxx/src/mathematical_special_functions.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index e18e9c5f2797c..102acb37e50bc 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -14,6 +14,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+#if _LIBCPP_STD_VER >= 17
 
 namespace __math {
 namespace {
@@ -48,5 +49,6 @@ __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __
 }
 } // namespace __math
 
+#endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From 7b60948e6eb6cb5b9ccea95e76bb02865d314871 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:18:02 +0200
Subject: [PATCH 16/55] TU: qualify std:: functions

---
 libcxx/src/mathematical_special_functions.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 102acb37e50bc..03a90cd3bd982 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -19,14 +19,14 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 namespace __math {
 namespace {
 template <class _Ret>
-optional<_Ret> __check_nan() {
-  return nullopt;
+std::optional<_Ret> __check_nan() {
+  return std::nullopt;
 }
 
 template <class _Ret, class _Arg, class... _Args>
-optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
-  if constexpr (is_floating_point_v<_Arg>)
-    if (isnan(__arg))
+std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
+  if constexpr (std::is_floating_point_v<_Arg>)
+    if (std::isnan(__arg))
       return __arg;
   return __check_nan<_Ret>(__args...);
 }
@@ -39,7 +39,7 @@ __sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
   try {
     return {.__domain_error = false, .__ret = f(__args...)};
   } catch (...) {
-    return {.__domain_error = true, .__ret = numeric_limits<_Ret>::quiet_NaN()};
+    return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
   }
 }
 } // namespace

>From a0f3426cd940ef45a441b4e610efaebf8f3e6655 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:18:46 +0200
Subject: [PATCH 17/55] TU: add missing __uglify

---
 libcxx/src/mathematical_special_functions.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 03a90cd3bd982..fdca91bf5a8b4 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -31,13 +31,13 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
   return __check_nan<_Ret>(__args...);
 }
 
-template <class Func, class... _Args, class _Ret = std::invoke_result_t<Func, _Args...>>
-__sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
+template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
+__sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return {.__domain_error = false, .__ret = *__maybe_nan};
 
   try {
-    return {.__domain_error = false, .__ret = f(__args...)};
+    return {.__domain_error = false, .__ret = __f(__args...)};
   } catch (...) {
     return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
   }
@@ -45,7 +45,7 @@ __sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
 } // namespace
 
 __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+  return __invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 } // namespace __math
 

>From 745c9d9aa7dfc246593851286f70e6494148c0d4 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:07 +0200
Subject: [PATCH 18/55] TU: add missing headers

---
 libcxx/src/mathematical_special_functions.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index fdca91bf5a8b4..9a106c763c776 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -7,8 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include <__config>
+#include <__math/special_functions.h>
 #include <boost/math/special_functions.hpp>
 #include <cmath>
+#include <limits>
 #include <optional>
 #include <type_traits>
 

>From 372f1054b2aa081808ed07b6182a6b8f2e4a21e3 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:42 +0200
Subject: [PATCH 19/55] TU: remove C++20 designated initializers

---
 libcxx/src/mathematical_special_functions.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 9a106c763c776..23d9216d777d2 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -36,12 +36,12 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
 __sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
-    return {.__domain_error = false, .__ret = *__maybe_nan};
+    return {false, *__maybe_nan};
 
   try {
-    return {.__domain_error = false, .__ret = __f(__args...)};
+    return {false, __f(__args...)};
   } catch (...) {
-    return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
+    return {true, std::numeric_limits<_Ret>::quiet_NaN()};
   }
 }
 } // namespace

>From 4e32fcc443bdd93c388e69aad76c0b08d7335d9d Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:51 +0200
Subject: [PATCH 20/55] TU: cleanup

---
 libcxx/src/mathematical_special_functions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 23d9216d777d2..cf88cbef159fa 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -47,7 +47,7 @@ __sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
 } // namespace
 
 __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return __invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 } // namespace __math
 

>From 299419630480cc7ba0fd7c026a14c8797b461aa0 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 11:21:18 +0200
Subject: [PATCH 21/55] break cyclic header deps: move errno logic into TU

---
 libcxx/include/__math/special_functions.h     | 34 +------------------
 libcxx/src/mathematical_special_functions.cpp | 21 ++++++++----
 2 files changed, 15 insertions(+), 40 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index e11dc97fd46d4..89c75cfc04585 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -15,10 +15,7 @@
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_integral.h>
-#include <cerrno>
-#include <cfenv>
 #include <limits>
-#include <math.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -28,37 +25,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
-namespace __math {
-template <class _Tp>
-struct __sf_result {
-  bool __domain_error;
-  _Tp __ret;
-
-  _LIBCPP_HIDE_FROM_ABI operator _Tp() const {
-#  if math_errhandling & MATH_ERRNO
-    if (__domain_error)
-      errno = EDOM;
-#  endif
-
-#  if math_errhandling & MATH_ERREXCEPT
-    if (__domain_error)
-      feraiseexcept(FE_INVALID);
-#  endif
-
-    return __ret;
-  }
-};
-} // namespace __math
-
 // assoc_laguerre
-namespace __math {
-_LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
-} // namespace __math
-
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float
-assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return __math::__assoc_laguerre(__n, __m, __x);
-}
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index cf88cbef159fa..daab723b8f2e3 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -9,8 +9,11 @@
 #include <__config>
 #include <__math/special_functions.h>
 #include <boost/math/special_functions.hpp>
+#include <cerrno>
+#include <cfenv>
 #include <cmath>
 #include <limits>
+#include <math.h>
 #include <optional>
 #include <type_traits>
 
@@ -18,7 +21,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
-namespace __math {
 namespace {
 template <class _Ret>
 std::optional<_Ret> __check_nan() {
@@ -34,22 +36,27 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 }
 
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
-__sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
+_Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
-    return {false, *__maybe_nan};
+    return *__maybe_nan;
 
   try {
-    return {false, __f(__args...)};
+    return __f(__args...);
   } catch (...) {
-    return {true, std::numeric_limits<_Ret>::quiet_NaN()};
+#  if math_errhandling & MATH_ERRNO
+    errno = EDOM;
+#  endif
+#  if math_errhandling & MATH_ERREXCEPT
+    feraiseexcept(FE_INVALID);
+#  endif
+    return std::numeric_limits<_Ret>::quiet_NaN();
   }
 }
 } // namespace
 
-__sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
+float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
-} // namespace __math
 
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS

>From f2580f1f2e853e83db75462c95558cbfeef6508c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 11:29:35 +0200
Subject: [PATCH 22/55] TU: cleanups

---
 libcxx/src/mathematical_special_functions.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index daab723b8f2e3..420e0a1e71cb0 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -13,7 +13,6 @@
 #include <cfenv>
 #include <cmath>
 #include <limits>
-#include <math.h>
 #include <optional>
 #include <type_traits>
 
@@ -47,7 +46,7 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
     errno = EDOM;
 #  endif
 #  if math_errhandling & MATH_ERREXCEPT
-    feraiseexcept(FE_INVALID);
+    std::feraiseexcept(FE_INVALID);
 #  endif
     return std::numeric_limits<_Ret>::quiet_NaN();
   }

>From 80f81ae52610bc7accc38605524fdbf872004e4f Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 16:31:15 +0200
Subject: [PATCH 23/55] use boost errno_on_error policy instead of exceptions

---
 libcxx/src/mathematical_special_functions.cpp | 44 +++++++++++++++----
 1 file changed, 35 insertions(+), 9 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 420e0a1e71cb0..f8fea7ff1b70e 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -8,19 +8,31 @@
 
 #include <__config>
 #include <__math/special_functions.h>
-#include <boost/math/special_functions.hpp>
 #include <cerrno>
 #include <cfenv>
 #include <cmath>
-#include <limits>
 #include <optional>
 #include <type_traits>
 
+#define BOOST_MATH_NO_EXCEPTIONS
+#include <boost/math/special_functions.hpp>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
 namespace {
+// Error policy for all Boost.Math calls: report domain/pole/overflow/evaluation
+// errors via errno (errno_on_error) instead of throwing. Boost sets errno to
+// EDOM (domain/pole/evaluation) or ERANGE (overflow) and returns NaN/inf. The
+// remaining categories (underflow/denorm/indeterminate) default to ignore.
+namespace __bmp = boost::math::policies;
+using __policy =
+    __bmp::policy<__bmp::domain_error<__bmp::errno_on_error>,
+                  __bmp::pole_error<__bmp::errno_on_error>,
+                  __bmp::overflow_error<__bmp::errno_on_error>,
+                  __bmp::evaluation_error<__bmp::errno_on_error>>;
+
 template <class _Ret>
 std::optional<_Ret> __check_nan() {
   return std::nullopt;
@@ -34,22 +46,36 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
   return __check_nan<_Ret>(__args...);
 }
 
+// Shared back-end for the C++17 mathematical special functions ([sf.cmath]).
+// Boost.Math is the compute kernel; this wrapper enforces the standard's
+// error-reporting rules ([sf.cmath.general]):
+//   1. NaN argument -> return NaN, do NOT report a domain error (the
+//      __check_nan pre-filter below).
+//   2. domain/range error -> report per <cmath> math_errhandling: errno = EDOM
+//      (domain/pole) or ERANGE (overflow), done by Boost's errno_on_error
+//      policy; and, when MATH_ERREXCEPT is set, raise the matching <cfenv>
+//      exception (done here, since Boost never touches <cfenv>).
+// Promotion: Boost's default promote_float=true computes float inputs in double
+// and rounds once -- more accurate and overflow-resistant, matching the
+// existing std::hermite(float) approach. We keep it.
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
 _Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return *__maybe_nan;
 
-  try {
-    return __f(__args...);
-  } catch (...) {
-#  if math_errhandling & MATH_ERRNO
-    errno = EDOM;
+#  if math_errhandling & MATH_ERREXCEPT
+  errno = 0;
 #  endif
+  _Ret __ret = __f(__args..., __policy{});
+  // Boost set errno via the policy; mirror it onto <cfenv>:
+  // EDOM (domain/pole) -> FE_INVALID, ERANGE (overflow) -> FE_OVERFLOW.
 #  if math_errhandling & MATH_ERREXCEPT
+  if (errno == EDOM)
     std::feraiseexcept(FE_INVALID);
+  else if (errno == ERANGE)
+    std::feraiseexcept(FE_OVERFLOW);
 #  endif
-    return std::numeric_limits<_Ret>::quiet_NaN();
-  }
+  return __ret;
 }
 } // namespace
 

>From 2c13b0df2188c488e3f2fe235ba22569419e0e5b Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 16:44:35 +0200
Subject: [PATCH 24/55] TU: remove fenv support.

---
 libcxx/src/mathematical_special_functions.cpp | 25 +++++--------------
 1 file changed, 6 insertions(+), 19 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index f8fea7ff1b70e..3c0af96fb0c19 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -8,8 +8,6 @@
 
 #include <__config>
 #include <__math/special_functions.h>
-#include <cerrno>
-#include <cfenv>
 #include <cmath>
 #include <optional>
 #include <type_traits>
@@ -51,10 +49,11 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 // error-reporting rules ([sf.cmath.general]):
 //   1. NaN argument -> return NaN, do NOT report a domain error (the
 //      __check_nan pre-filter below).
-//   2. domain/range error -> report per <cmath> math_errhandling: errno = EDOM
-//      (domain/pole) or ERANGE (overflow), done by Boost's errno_on_error
-//      policy; and, when MATH_ERREXCEPT is set, raise the matching <cfenv>
-//      exception (done here, since Boost never touches <cfenv>).
+//   2. domain/range error -> reported via errno only: Boost's errno_on_error
+//      policy sets errno = EDOM (domain/pole/evaluation) or ERANGE (overflow).
+//      The <cfenv> floating-point-exception side of math_errhandling
+//      (MATH_ERREXCEPT) is intentionally not mirrored, matching the shipped
+//      std::hermite and libstdc++'s special-function implementations.
 // Promotion: Boost's default promote_float=true computes float inputs in double
 // and rounds once -- more accurate and overflow-resistant, matching the
 // existing std::hermite(float) approach. We keep it.
@@ -63,19 +62,7 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return *__maybe_nan;
 
-#  if math_errhandling & MATH_ERREXCEPT
-  errno = 0;
-#  endif
-  _Ret __ret = __f(__args..., __policy{});
-  // Boost set errno via the policy; mirror it onto <cfenv>:
-  // EDOM (domain/pole) -> FE_INVALID, ERANGE (overflow) -> FE_OVERFLOW.
-#  if math_errhandling & MATH_ERREXCEPT
-  if (errno == EDOM)
-    std::feraiseexcept(FE_INVALID);
-  else if (errno == ERANGE)
-    std::feraiseexcept(FE_OVERFLOW);
-#  endif
-  return __ret;
+  return __f(__args..., __policy{});
 }
 } // namespace
 

>From 580076b2ffe777b1f00d844091f88ebd03820402 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:24:36 +0200
Subject: [PATCH 25/55] test: add more tests; fix header includes

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index eb747efb4c1c6..079751321f3b9 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,11 +14,11 @@
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
 
 #include <cassert>
-#include <cerrno>
-#include <cfenv>
 #include <cmath>
+#include <limits>
 
 #include "common.h"
+#include "test_macros.h"
 #include "type_algorithms.h"
 
 struct TestFloatingPoint {
@@ -32,9 +32,19 @@ struct TestFloatingPoint {
 
     assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
 
+    // m == 0 reduces to the ordinary Laguerre polynomial: L_2(2) = (4 - 8 + 2) / 2 = -1.
+    assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
+
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
+    // noexcept is a libc++ extension here ([sf.cmath] does not mandate it), so use the libc++-only assertion.
+    LIBCPP_ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, T(0.)));
+
     check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
+
+    // The associated Laguerre polynomials are defined for all real x: a negative
+    // argument is in-domain and must not report a domain error. L_1^0(-1) = 1 - x = 2.
+    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, -1.f), 2.01f)); });
   }
 };
 

>From 2a6dfd4ae296d0f89e1ea5bc953d7d5f8f536ca9 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:53:16 +0200
Subject: [PATCH 26/55] test: add noexcept.compile.pass test

---
 .../c.math/sf.cmath/noexcept.compile.pass.cpp | 20 +++++++++++++++++++
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   |  3 ---
 2 files changed, 20 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..9994e6a018f8d
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// Check that functions are marked noexcept
+
+#include <cmath>
+
+#include "test_macros.h"
+
+void test() {
+  // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
+}
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 079751321f3b9..068ca81e04b83 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -37,9 +37,6 @@ struct TestFloatingPoint {
 
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
-    // noexcept is a libc++ extension here ([sf.cmath] does not mandate it), so use the libc++-only assertion.
-    LIBCPP_ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, T(0.)));
-
     check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
 
     // The associated Laguerre polynomials are defined for all real x: a negative

>From 7b8204c6a252f2971f288212096aee6641c14d82 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:55:16 +0200
Subject: [PATCH 27/55] test: cleanup

---
 .../test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 7928ebf50681a..a4c471e4b3168 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -15,6 +15,6 @@
 void test() {
   // clang-format off
   // assoc_laguerre
-  std::assoc_laguerref(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   // clang-format on
 }

>From 82d264a4bc5a0a1f6d4c932b97b4a74c11957fac Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 28 Jun 2026 23:37:36 +0200
Subject: [PATCH 28/55] test: remove fenv support

---
 libcxx/test/std/numerics/c.math/sf.cmath/common.h | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
index 867e6af6f6914..723422bf7038d 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/common.h
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -12,8 +12,6 @@
 #include <cassert>
 #include <cerrno>
 #include <cmath>
-#include <math.h>
-#include <cfenv>
 
 // std::type_identity is C++20 (we need to support C++17 here)
 template <class T>
@@ -32,34 +30,22 @@ template <class Func>
 void check_no_domain_error(Func f) {
 #if math_errhandling & MATH_ERRNO
   errno = EACCES;
-#endif
-#if math_errhandling & MATH_ERREXCEPT
-  std::feclearexcept(FE_INVALID);
 #endif
   f();
 #if math_errhandling & MATH_ERRNO
   assert(errno == EACCES);
 #endif
-#if math_errhandling & MATH_ERREXCEPT
-  assert(!std::fetestexcept(FE_INVALID));
-#endif
 }
 
 template <class Func>
 void check_domain_error(Func f) {
 #if math_errhandling & MATH_ERRNO
   errno = EACCES;
-#endif
-#if math_errhandling & MATH_ERREXCEPT
-  std::feclearexcept(FE_INVALID);
 #endif
   f();
 #if math_errhandling & MATH_ERRNO
   assert(errno == EDOM);
 #endif
-#if math_errhandling & MATH_ERREXCEPT
-  assert(std::fetestexcept(FE_INVALID));
-#endif
 }
 
 #endif // TEST_SF_CMATH_COMMON_H

>From 6b65307f26de7474c6b3f6789c8bd49e9b9787d7 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 28 Jun 2026 23:37:54 +0200
Subject: [PATCH 29/55] test: cleanups + more NaN tests

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 28 ++++++++++---------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 068ca81e04b83..fffbb72557a5a 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -18,30 +18,32 @@
 #include <limits>
 
 #include "common.h"
-#include "test_macros.h"
 #include "type_algorithms.h"
 
 struct TestFloatingPoint {
   template <class T>
   void operator()() const {
+    // sample value testing
     assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-
     assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
-
-    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
-
-    assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
-
-    // m == 0 reduces to the ordinary Laguerre polynomial: L_2(2) = (4 - 8 + 2) / 2 = -1.
     assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
+    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
+    assert(between(60.124f, std::assoc_laguerref(2, 10, T(0.5)), 60.126f));
 
+    // return type: float
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
-    check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
-
-    // The associated Laguerre polynomials are defined for all real x: a negative
-    // argument is in-domain and must not report a domain error. L_1^0(-1) = 1 - x = 2.
-    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, -1.f), 2.01f)); });
+    // NaN input -> NaN output (w/o domain error)
+    auto check_nan = [](T nan) {
+      check_no_domain_error([nan] { assert(std::isnan(std::assoc_laguerref(0, 0, nan))); });
+    };
+    if (std::numeric_limits<T>::has_quiet_NaN)
+      check_nan(std::numeric_limits<T>::quiet_NaN());
+    if (std::numeric_limits<T>::has_signaling_NaN)
+      check_nan(std::numeric_limits<T>::signaling_NaN());
+
+    // negative x: no domain error
+    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, T(-1)), 2.01f)); });
   }
 };
 

>From ddd4eef0a09edb02e8e486fb587bf5e64d538b65 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 00:00:33 +0200
Subject: [PATCH 30/55] impl assoc_laguerrel

---
 libcxx/include/__math/special_functions.h     | 1 +
 libcxx/src/mathematical_special_functions.cpp | 5 +++++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 89c75cfc04585..0cfd484955b15 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -27,6 +27,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 // assoc_laguerre
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept;
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 3c0af96fb0c19..980af019748db 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -66,10 +66,15 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
 }
 } // namespace
 
+// assoc_laguerre
 float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept {
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From a35b978e0601bea7a9accf072c175a4c8b182c97 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 00:23:48 +0200
Subject: [PATCH 31/55] test: assoc_laguerrel

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 78 ++++++++-----------
 1 file changed, 34 insertions(+), 44 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index fffbb72557a5a..0a9abaf2f4f24 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -9,54 +9,44 @@
 // REQUIRES: std-at-least-c++17
 
 // <cmath>
-//
-// [sf.cmath.assoc.laguerre], associated Laguerre polynomials
-// float               assoc_laguerref(unsigned n, unsigned m, float x);
+
+// float       assoc_laguerref(unsigned n, unsigned m, float x);
+// long double assoc_laguerrel(unsigned n, unsigned m, long double x);
 
 #include <cassert>
 #include <cmath>
 #include <limits>
 
 #include "common.h"
-#include "type_algorithms.h"
-
-struct TestFloatingPoint {
-  template <class T>
-  void operator()() const {
-    // sample value testing
-    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-    assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
-    assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
-    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
-    assert(between(60.124f, std::assoc_laguerref(2, 10, T(0.5)), 60.126f));
-
-    // return type: float
-    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
-
-    // NaN input -> NaN output (w/o domain error)
-    auto check_nan = [](T nan) {
-      check_no_domain_error([nan] { assert(std::isnan(std::assoc_laguerref(0, 0, nan))); });
-    };
-    if (std::numeric_limits<T>::has_quiet_NaN)
-      check_nan(std::numeric_limits<T>::quiet_NaN());
-    if (std::numeric_limits<T>::has_signaling_NaN)
-      check_nan(std::numeric_limits<T>::signaling_NaN());
-
-    // negative x: no domain error
-    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, T(-1)), 2.01f)); });
-  }
-};
-
-struct TestIntegral {
-  template <class T>
-  void operator()() const {
-    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-
-    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
-  }
-};
-
-int main() {
-  types::for_each(types::floating_point_types{}, TestFloatingPoint{});
-  types::for_each(types::integral_types{}, TestIntegral{});
+
+// Each suffixed overload is fixed to a single floating-point type, so it is
+// tested directly with that type. The unsuffixed std::assoc_laguerre overloads
+// (and the integer-argument overload) are not implemented yet.
+template <class T, class Func>
+void test(Func assoc_laguerre) {
+  // sample values
+  assert(between(0.99, assoc_laguerre(0, 0, T(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, T(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, T(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, T(2)), 0.01));
+  assert(between(60.124, assoc_laguerre(2, 10, T(0.5)), 60.126));
+
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, T(0))), T>);
+
+  // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
+  auto test_nan = [&](T nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
+  if (std::numeric_limits<T>::has_quiet_NaN)
+    test_nan(std::numeric_limits<T>::quiet_NaN());
+  if (std::numeric_limits<T>::has_signaling_NaN)
+    test_nan(std::numeric_limits<T>::signaling_NaN());
+
+  // A negative argument is in the domain (no domain error).
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, T(-1)), 2.01)); });
 }
+
+int main(int, char**) {
+  test<float>([](unsigned __n, unsigned __m, float __x) { return std::assoc_laguerref(__n, __m, __x); });
+  test<long double>([](unsigned __n, unsigned __m, long double __x) { return std::assoc_laguerrel(__n, __m, __x); });
+
+  return 0;
+}
\ No newline at end of file

>From 96e8004c4a6f41343f1257898d04efbbd01f6457 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 21:43:26 +0200
Subject: [PATCH 32/55] re-introduce __math::__assoc_laguerre -> only ABI
 export internal API

---
 libcxx/include/__math/special_functions.h     | 16 ++++++++++++++--
 libcxx/src/mathematical_special_functions.cpp |  7 +++++--
 2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 0cfd484955b15..62ae09594bea3 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
 
 #include <__config>
+#include <__configuration/attributes.h>
 #include <__math/copysign.h>
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
@@ -26,8 +27,19 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
 // assoc_laguerre
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept;
+namespace __math {
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
+} // namespace __math
+
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
+assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 980af019748db..ed8109c7e482c 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -19,6 +19,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
+namespace __math {
 namespace {
 // Error policy for all Boost.Math calls: report domain/pole/overflow/evaluation
 // errors via errno (errno_on_error) instead of throwing. Boost sets errno to
@@ -67,14 +68,16 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
 } // namespace
 
 // assoc_laguerre
-float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+float __assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
-long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept {
+long double __assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+} // namespace __math
+
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From 34d62b1da4598efb45c79363654566b9ca6677aa Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 21:43:57 +0200
Subject: [PATCH 33/55] add exported ABI symbols to abilist

---
 ...-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...nux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...werpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...rpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...nux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...known-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...own-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 4 +++-
 ...n-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 2 ++
 9 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 74283373d1b65..7889109825e10 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1700,6 +1700,8 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 291149608c9c7..3e139e30496a6 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1338,6 +1338,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index da6d608af462c..304817ba0cffb 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -686,6 +686,8 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEyPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock11from_time_tEi', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6ee31613d5320..d5e37635fa511 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -686,6 +686,8 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock11from_time_tEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 92a37db24ac5f..a59b72d695b55 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1690,6 +1690,8 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 7b09d3dd3f75c..abef314dc14f4 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1338,6 +1338,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 725ed669fb561..86bc3dec5bd34 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1350,6 +1350,8 @@
 {'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 160, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 717d4c9bee726..11330e7eaf442 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1349,6 +1349,8 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
@@ -2030,4 +2032,4 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 45db143b4cf4a..460bd21a2fecf 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1320,6 +1320,8 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}

>From 7e1ed83abf9818cb95663ed9077215f102873666 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:10:37 +0200
Subject: [PATCH 34/55] impl std::assoc_laguerre(double)

---
 libcxx/include/__math/special_functions.h     | 6 ++++++
 libcxx/src/mathematical_special_functions.cpp | 4 ++++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 782e441562c00..8fd363919e5cc 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -30,6 +30,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 // assoc_laguerre
 namespace __math {
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI double __assoc_laguerre(unsigned, unsigned, double) noexcept;
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
@@ -37,6 +38,11 @@ namespace __math {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+template <class = int>
+_LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
 assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index ed8109c7e482c..0f6e3a850ad73 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -72,6 +72,10 @@ float __assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+double __assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+
 long double __assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }

>From 8b1f5f6ded44a8cbd009c03310faa645d320bb97 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:20:15 +0200
Subject: [PATCH 35/55] fixup impl std::assoc_laguerre(double)

---
 libcxx/include/__math/special_functions.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 8fd363919e5cc..b48b9ffdb129c 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -39,7 +39,7 @@ namespace __math {
 }
 
 template <class = int>
-_LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 

>From fbbd70784b3e8bc65aa5e3b18870d936ea438e2e Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:43:07 +0200
Subject: [PATCH 36/55] fixup: assoc_laguerre(dbl)

---
 libcxx/include/__math/special_functions.h                   | 3 +--
 .../std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp    | 6 +++---
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index b48b9ffdb129c..20268dd1bd7d6 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -38,8 +38,7 @@ namespace __math {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-template <class = int>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 0a9abaf2f4f24..b9223f6d66a2c 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -19,9 +19,9 @@
 
 #include "common.h"
 
-// Each suffixed overload is fixed to a single floating-point type, so it is
-// tested directly with that type. The unsuffixed std::assoc_laguerre overloads
-// (and the integer-argument overload) are not implemented yet.
+// Each overload is fixed to a single floating-point type, so it is tested
+// directly with that type. The integer-argument overload
+// (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
 template <class T, class Func>
 void test(Func assoc_laguerre) {
   // sample values

>From 7f5e5515ad7c1e551ff1b7cecf57772211f3b376 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:12:00 +0200
Subject: [PATCH 37/55] test: general cleanups

---
 .../std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp    | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index b9223f6d66a2c..d446e017494c4 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -45,8 +45,8 @@ void test(Func assoc_laguerre) {
 }
 
 int main(int, char**) {
-  test<float>([](unsigned __n, unsigned __m, float __x) { return std::assoc_laguerref(__n, __m, __x); });
-  test<long double>([](unsigned __n, unsigned __m, long double __x) { return std::assoc_laguerrel(__n, __m, __x); });
+  test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
   return 0;
-}
\ No newline at end of file
+}

>From 340ed9ca7b9f543fd3ffa8110f29863dc7fcef4f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:14:47 +0200
Subject: [PATCH 38/55] test: std::assoc_laguerre(double)

---
 .../test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp  | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index d446e017494c4..9c2bc2026a348 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -10,6 +10,7 @@
 
 // <cmath>
 
+// double      assoc_laguerre(unsigned n, unsigned m, double x);
 // float       assoc_laguerref(unsigned n, unsigned m, float x);
 // long double assoc_laguerrel(unsigned n, unsigned m, long double x);
 
@@ -45,6 +46,8 @@ void test(Func assoc_laguerre) {
 }
 
 int main(int, char**) {
+  test<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+
   test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
   test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 

>From 589d01b5fce70f06068b3304556145bc1c284ebe Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:20:51 +0200
Subject: [PATCH 39/55] test: nodiscard/noexcept for assoc_laguerre(double),
 assoc_laguerrel

---
 .../test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp  | 3 +++
 .../libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp  | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index a4c471e4b3168..8b8826d03de01 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -15,6 +15,9 @@
 void test() {
   // clang-format off
   // assoc_laguerre
+  std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   // clang-format on
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 9994e6a018f8d..70984fc0c83bf 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -16,5 +16,8 @@
 
 void test() {
   // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0));
+
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
+  ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
 }

>From db297e6b6a155fdd5ea0bb4978f5e267c14a773e Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:42:41 +0200
Subject: [PATCH 40/55] add ABI symbol to abilist for assoc_laguerre(double)

---
 ...m64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 .../powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...owerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ..._64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...nknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...nown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 1 +
 9 files changed, 9 insertions(+)

diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6ab143c0113f3..9f70dd6550fc7 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1701,6 +1701,7 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 08d42d44e4827..4152a583ec5a3 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1339,6 +1339,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 98fd189eaa3f7..861750f606c77 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -687,6 +687,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEyPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 9e1db57fc315b..347d9f9f23d70 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -687,6 +687,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index a59b72d695b55..cd3f3e4fda9a2 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1690,6 +1690,7 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 61bd10d997a1b..421f4839f762e 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1339,6 +1339,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 286ad56406ee2..d59e224c42922 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1351,6 +1351,7 @@
 {'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 160, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 9fd2edf63e6ef..d2b084bace550 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1350,6 +1350,7 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 51c27fea37ce9..7e57931398e85 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1321,6 +1321,7 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}

>From e8984b706592b5b2ff08bdd19020330498423157 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:12:02 +0200
Subject: [PATCH 41/55] impl assoc_laguerre(Int)

---
 libcxx/include/__math/special_functions.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 20268dd1bd7d6..5021e5a54b634 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -47,6 +47,11 @@ assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+template <class _Int, std::enable_if_t<std::is_integral_v<_Int>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 // hermite

>From c8c5eb6a2040040b6a5c67c42128172a4c5661a8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:18:00 +0200
Subject: [PATCH 42/55] test: nodiscard/except assoc_laguerre(Int)

---
 .../numerics/c.math/sf.cmath/nodiscard.verify.cpp      |  1 +
 .../numerics/c.math/sf.cmath/noexcept.compile.pass.cpp | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 8b8826d03de01..b83070f4f984d 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -16,6 +16,7 @@ void test() {
   // clang-format off
   // assoc_laguerre
   std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 70984fc0c83bf..cca9c1e924a8c 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -13,6 +13,14 @@
 #include <cmath>
 
 #include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestIntegral {
+  template <class T>
+  void operator()() const {
+    ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, T{}));
+  }
+};
 
 void test() {
   // assoc_laguerre
@@ -20,4 +28,6 @@ void test() {
 
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
   ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
+
+  types::for_each(types::integral_types{}, TestIntegral{});
 }

>From 5946b7d3c07ea7f4dc898e9750313838fd3a99a8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:19:29 +0200
Subject: [PATCH 43/55] wip: test: assoc_laguerre(Int)

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 48 ++++++++++++-------
 1 file changed, 32 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 9c2bc2026a348..b39e20fe6288a 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -17,39 +17,55 @@
 #include <cassert>
 #include <cmath>
 #include <limits>
+#include <type_traits>
 
 #include "common.h"
+#include "type_algorithms.h"
 
 // Each overload is fixed to a single floating-point type, so it is tested
 // directly with that type. The integer-argument overload
 // (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
-template <class T, class Func>
+template <class ExpectedFuncRet, class FuncArgX, class Func>
 void test(Func assoc_laguerre) {
   // sample values
-  assert(between(0.99, assoc_laguerre(0, 0, T(0)), 1.01));
-  assert(between(0.99, assoc_laguerre(1, 1, T(1)), 1.01));
-  assert(between(-1.01, assoc_laguerre(2, 0, T(2)), -0.99));
-  assert(between(-0.01, assoc_laguerre(2, 2, T(2)), 0.01));
-  assert(between(60.124, assoc_laguerre(2, 10, T(0.5)), 60.126));
+  assert(between(0.99, assoc_laguerre(0, 0, FuncArgX(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, FuncArgX(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, FuncArgX(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, FuncArgX(2)), 0.01));
+  if constexpr (std::is_floating_point_v<FuncArgX>)
+    assert(between(60.124, assoc_laguerre(2, 10, FuncArgX(0.5)), 60.126));
 
-  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, T(0))), T>);
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, FuncArgX(0))), ExpectedFuncRet>);
 
   // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
-  auto test_nan = [&](T nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
-  if (std::numeric_limits<T>::has_quiet_NaN)
-    test_nan(std::numeric_limits<T>::quiet_NaN());
-  if (std::numeric_limits<T>::has_signaling_NaN)
-    test_nan(std::numeric_limits<T>::signaling_NaN());
+  if constexpr (std::is_floating_point_v<FuncArgX>) {
+    auto test_nan = [&](FuncArgX nan) {
+      check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); });
+    };
+    if (std::numeric_limits<FuncArgX>::has_quiet_NaN)
+      test_nan(std::numeric_limits<FuncArgX>::quiet_NaN());
+    if (std::numeric_limits<FuncArgX>::has_signaling_NaN)
+      test_nan(std::numeric_limits<FuncArgX>::signaling_NaN());
+  }
 
   // A negative argument is in the domain (no domain error).
-  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, T(-1)), 2.01)); });
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, FuncArgX(-1)), 2.01)); });
 }
 
+struct TestIntegral {
+  template <class Int>
+  void operator()() const {
+    test<double, Int>([](unsigned n, unsigned m, Int x) { return std::assoc_laguerre(n, m, x); });
+  }
+};
+
 int main(int, char**) {
-  test<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+  test<double, double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+
+  test<float, float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test<long double, long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
-  test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
-  test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
+  // types::for_each(types::integral_types{}, TestIntegral{}); WIP
 
   return 0;
 }

>From 619773ba99f60f0d799ca98488fa71cc246e2c47 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 17:48:09 +0200
Subject: [PATCH 44/55] fix gcc build: gcc defines __STDCPP_FLOATXX_T__ even
 though we dont ship <stdfloat> yet -> breaks Boost

---
 libcxx/src/mathematical_special_functions.cpp | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 0f6e3a850ad73..b8e14967740f1 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -12,6 +12,22 @@
 #include <optional>
 #include <type_traits>
 
+// GCC defines __STDCPP_FLOATnn_T__ whenever the _Floatnn extended types exist at the
+// language level, independent of the standard library. libc++ currently ships no
+// <stdfloat>, so std::floatnn_t is never declared -- but Boost.Math keys its
+// std::floatnn_t overloads off these macros and would reference the missing types.
+// Suppress those overloads while <stdfloat> is unavailable. The __has_include guard is
+// the same condition Boost uses to include <stdfloat>, so this workaround disables itself
+// automatically once libc++ provides the header (the overloads then light up on their own
+// -- no manual re-enable needed here).
+#if !__has_include(<stdfloat>)
+#  undef __STDCPP_FLOAT16_T__
+#  undef __STDCPP_FLOAT32_T__
+#  undef __STDCPP_FLOAT64_T__
+#  undef __STDCPP_FLOAT128_T__
+#  undef __STDCPP_BFLOAT16_T__
+#endif
+
 #define BOOST_MATH_NO_EXCEPTIONS
 #include <boost/math/special_functions.hpp>
 

>From 3c8e6c21dca99ac9b83fe264c70255710d20c21f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 18:01:47 +0200
Subject: [PATCH 45/55] fix android abilist

---
 ...linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 421f4839f762e..08b901a94f394 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1340,8 +1340,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjg', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}

>From e4c48175cfc00429e93c92a30aa81e67201f4866 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 18:02:21 +0200
Subject: [PATCH 46/55] fix boost math thread support

---
 libcxx/src/mathematical_special_functions.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index b8e14967740f1..c37811bdf52ea 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -28,6 +28,14 @@
 #  undef __STDCPP_BFLOAT16_T__
 #endif
 
+// Boost.Math detects thread support via __has_include(<thread>/<mutex>/...), but libc++
+// ships those headers even when threads are disabled (_LIBCPP_HAS_THREADS == 0), so the
+// detection wrongly enables std::mutex use and breaks on no-thread targets (e.g. picolibc).
+// Tell Boost there are no threads; lazy-init tables don't need locking without threads.
+#if !_LIBCPP_HAS_THREADS
+#  define BOOST_MATH_DISABLE_THREADS
+#endif
+
 #define BOOST_MATH_NO_EXCEPTIONS
 #include <boost/math/special_functions.hpp>
 

>From 60635ce92abd69cad3a5161a78a5a2f55281d9b8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 21:47:33 +0200
Subject: [PATCH 47/55] test: fix apple's math_errhandling usage. runtime not
 macro constant

---
 .../std/numerics/c.math/sf.cmath/common.h     | 23 +++++++++----------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
index 723422bf7038d..ca6de9ea868bd 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/common.h
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -26,26 +26,25 @@ bool between(type_identity_t<T> lower, T value, type_identity_t<T> upper) {
   return lower < value && value < upper;
 }
 
+// Note: math_errhandling must be queried with a runtime `if`, not `#if`. On Apple
+// platforms it is defined as a function call (__math_errhandling()), so it is not a
+// preprocessor constant and would break `#if` under -Wundef.
 template <class Func>
 void check_no_domain_error(Func f) {
-#if math_errhandling & MATH_ERRNO
-  errno = EACCES;
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    errno = EACCES;
   f();
-#if math_errhandling & MATH_ERRNO
-  assert(errno == EACCES);
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    assert(errno == EACCES);
 }
 
 template <class Func>
 void check_domain_error(Func f) {
-#if math_errhandling & MATH_ERRNO
-  errno = EACCES;
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    errno = EACCES;
   f();
-#if math_errhandling & MATH_ERRNO
-  assert(errno == EDOM);
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    assert(errno == EDOM);
 }
 
 #endif // TEST_SF_CMATH_COMMON_H

>From 8201618481b60722093b07eff23b8bcb3b274e05 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:16:59 +0200
Subject: [PATCH 48/55] minimze boost includes

---
 libcxx/src/mathematical_special_functions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index c37811bdf52ea..dc046b3a845df 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -37,7 +37,7 @@
 #endif
 
 #define BOOST_MATH_NO_EXCEPTIONS
-#include <boost/math/special_functions.hpp>
+#include <boost/math/special_functions/laguerre.hpp>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS

>From 4d11bdbe96fca5653a71146ff3fea197180f13ef Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:22:46 +0200
Subject: [PATCH 49/55] patch boost: make it compilable w/o localization
 support

---
 third-party/boost-math-libcxx.patch | 47 +++++++++++++++++++++++++++++
 third-party/update_boost_math.sh    | 10 ++++++
 2 files changed, 57 insertions(+)
 create mode 100644 third-party/boost-math-libcxx.patch

diff --git a/third-party/boost-math-libcxx.patch b/third-party/boost-math-libcxx.patch
new file mode 100644
index 0000000000000..f7692a7e54b67
--- /dev/null
+++ b/third-party/boost-math-libcxx.patch
@@ -0,0 +1,47 @@
+# Local libc++ patch for the vendored Boost.Math snapshot.
+#
+# Boost.Math only uses <sstream>/<iomanip> and detail::prec_format to build the
+# what() text of the exceptions it throws. That machinery sits OUTSIDE Boost's own
+# `#ifndef BOOST_MATH_NO_EXCEPTIONS` guard (unlike the adjacent <stdexcept> /
+# throw_exception.hpp includes), so it is compiled even when exceptions are disabled.
+# libc++ builds the Math Special Functions back-end with BOOST_MATH_NO_EXCEPTIONS,
+# and on configurations without localization (e.g. -DLIBCXX_ENABLE_LOCALIZATION=OFF)
+# libc++ ships no functional <sstream>, so prec_format fails to compile.
+#
+# This patch guards the iostream-only message formatting under BOOST_MATH_NO_EXCEPTIONS.
+# Reported upstream: https://github.com/boostorg/math/issues (see PR description).
+# Re-applied by update_boost_math.sh after each snapshot refresh; drop once upstream.
+diff --git a/boost-math/include/boost/math/policies/error_handling.hpp b/boost-math/include/boost/math/policies/error_handling.hpp
+index d7ba694564ba..da167171f650 100644
+--- a/boost-math/include/boost/math/policies/error_handling.hpp
++++ b/boost-math/include/boost/math/policies/error_handling.hpp
+@@ -26,9 +26,9 @@
+ #include <complex>
+ #include <cstdint>
+ #include <cstring>
++#ifndef BOOST_MATH_NO_EXCEPTIONS
+ #include <iomanip>
+ #include <sstream>
+-#ifndef BOOST_MATH_NO_EXCEPTIONS
+ #include <stdexcept>
+ #endif
+ #include <string>
+@@ -95,6 +95,10 @@ T user_indeterminate_result_error(const char* function, const char* message, con
+ namespace detail
+ {
+ 
++// prec_format is only used to build exception what() messages, so it is gated on
++// exceptions being enabled. It relies on <sstream>, which is unavailable in some
++// standard-library configurations (e.g. libc++ built without localization).
++#ifndef BOOST_MATH_NO_EXCEPTIONS
+ template <class T>
+ inline std::string prec_format(const T& val)
+ {
+@@ -125,6 +129,7 @@ inline std::string prec_format<std::float128_t>(const std::float128_t& val)
+ }
+ 
+ #endif
++#endif // BOOST_MATH_NO_EXCEPTIONS
+ 
+ inline void replace_all_in_string(std::string& result, const char* what, const char* with)
+ {
diff --git a/third-party/update_boost_math.sh b/third-party/update_boost_math.sh
index 27d8816b7cb12..a4276cdb74028 100755
--- a/third-party/update_boost_math.sh
+++ b/third-party/update_boost_math.sh
@@ -40,3 +40,13 @@ sed -i 's|target_include_directories(boost_math INTERFACE include)|target_includ
 grep -q 'target_include_directories(boost_math SYSTEM INTERFACE include)' \
     ${SCRIPT_DIR}/boost-math/CMakeLists.txt \
     || { echo "ERROR: SYSTEM include patch failed -- upstream CMakeLists.txt structure changed"; exit 1; }
+
+# Guard Boost.Math's iostream-only exception-message formatting (prec_format / <sstream>)
+# under BOOST_MATH_NO_EXCEPTIONS, so the no-exceptions back-end builds without <sstream>.
+# Needed for libc++ configurations without localization. Drop once fixed upstream.
+patch -p1 -d ${SCRIPT_DIR} < ${SCRIPT_DIR}/boost-math-libcxx.patch
+
+# Verify the patch landed -- fail loudly if upstream restructured error_handling.hpp.
+grep -q 'prec_format is only used to build exception' \
+    ${SCRIPT_DIR}/boost-math/include/boost/math/policies/error_handling.hpp \
+    || { echo "ERROR: no-exceptions iostream patch failed -- upstream error_handling.hpp structure changed"; exit 1; }

>From 276df4abe72a9ba6d4979d5922b466445aa406b9 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:32:27 +0200
Subject: [PATCH 50/55] apply boost patch

---
 .../include/boost/math/policies/error_handling.hpp         | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/third-party/boost-math/include/boost/math/policies/error_handling.hpp b/third-party/boost-math/include/boost/math/policies/error_handling.hpp
index d7ba694564ba4..da167171f650d 100644
--- a/third-party/boost-math/include/boost/math/policies/error_handling.hpp
+++ b/third-party/boost-math/include/boost/math/policies/error_handling.hpp
@@ -26,9 +26,9 @@
 #include <complex>
 #include <cstdint>
 #include <cstring>
+#ifndef BOOST_MATH_NO_EXCEPTIONS
 #include <iomanip>
 #include <sstream>
-#ifndef BOOST_MATH_NO_EXCEPTIONS
 #include <stdexcept>
 #endif
 #include <string>
@@ -95,6 +95,10 @@ T user_indeterminate_result_error(const char* function, const char* message, con
 namespace detail
 {
 
+// prec_format is only used to build exception what() messages, so it is gated on
+// exceptions being enabled. It relies on <sstream>, which is unavailable in some
+// standard-library configurations (e.g. libc++ built without localization).
+#ifndef BOOST_MATH_NO_EXCEPTIONS
 template <class T>
 inline std::string prec_format(const T& val)
 {
@@ -125,6 +129,7 @@ inline std::string prec_format<std::float128_t>(const std::float128_t& val)
 }
 
 #endif
+#endif // BOOST_MATH_NO_EXCEPTIONS
 
 inline void replace_all_in_string(std::string& result, const char* what, const char* with)
 {

>From 6bbed03306814ee7a45418ba8e01e619852d7d94 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:53:16 +0200
Subject: [PATCH 51/55] doc: add boost issue

---
 third-party/boost-math-libcxx.patch | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/third-party/boost-math-libcxx.patch b/third-party/boost-math-libcxx.patch
index f7692a7e54b67..8593b077a2db2 100644
--- a/third-party/boost-math-libcxx.patch
+++ b/third-party/boost-math-libcxx.patch
@@ -9,7 +9,7 @@
 # libc++ ships no functional <sstream>, so prec_format fails to compile.
 #
 # This patch guards the iostream-only message formatting under BOOST_MATH_NO_EXCEPTIONS.
-# Reported upstream: https://github.com/boostorg/math/issues (see PR description).
+# Reported upstream: https://github.com/boostorg/math/issues/1412
 # Re-applied by update_boost_math.sh after each snapshot refresh; drop once upstream.
 diff --git a/boost-math/include/boost/math/policies/error_handling.hpp b/boost-math/include/boost/math/policies/error_handling.hpp
 index d7ba694564ba..da167171f650 100644

>From 441f2f0089823971d515573e8f8074fddd2e32a1 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 08:34:54 +0200
Subject: [PATCH 52/55] guard via
 _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS

---
 libcxx/include/__configuration/availability.h |  5 +++++
 libcxx/include/__math/special_functions.h     | 20 ++++++++++++-------
 .../c.math/sf.cmath/nodiscard.verify.cpp      |  4 ++++
 .../c.math/sf.cmath/noexcept.compile.pass.cpp |  4 ++++
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   |  4 ++++
 .../libcxx/test/features/availability.py      |  8 ++++++++
 6 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 735af5d431cb1..72eb50721338f 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -271,6 +271,11 @@
 #define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20
 #define _LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20_ATTRIBUTE
 
+// This controls the availability of the C++17 mathematical special functions
+// ([sf.cmath]). Their implementation lives in the built library (backed by Boost.Math).
+#define _LIBCPP_AVAILABILITY_HAS_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_INTRODUCED_IN_LLVM_23
+#define _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE
+
 // This controls the availability of the C++20 time zone database.
 // The parser code is built in the library.
 #define _LIBCPP_AVAILABILITY_HAS_TZDB _LIBCPP_INTRODUCED_IN_LLVM_19
diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 5021e5a54b634..546600b72bce0 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -29,26 +29,32 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 // assoc_laguerre
 namespace __math {
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI double __assoc_laguerre(unsigned, unsigned, double) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI float
+__assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI double
+__assoc_laguerre(unsigned, unsigned, double) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI long double
+__assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI double
+assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI long double
 assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
 template <class _Int, std::enable_if_t<std::is_integral_v<_Int>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_HIDE_FROM_ABI double
+assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index b83070f4f984d..598311f20917d 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// The functions carry availability markup, so referencing them fails to compile against a
+// back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // Check that functions are marked [[nodiscard]]
 
 #include <cmath>
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index cca9c1e924a8c..8f9dd87c0ee87 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// The functions carry availability markup, so referencing them fails to compile against a
+// back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // Check that functions are marked noexcept
 
 #include <cmath>
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index b39e20fe6288a..91a7c4a3d2047 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// These functions are implemented in the built library, so a program using them fails to
+// load against a back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // <cmath>
 
 // double      assoc_laguerre(unsigned n, unsigned m, double x);
diff --git a/libcxx/utils/libcxx/test/features/availability.py b/libcxx/utils/libcxx/test/features/availability.py
index c0f2a3d4cdc88..7bb1add0e9e9c 100644
--- a/libcxx/utils/libcxx/test/features/availability.py
+++ b/libcxx/utils/libcxx/test/features/availability.py
@@ -211,4 +211,12 @@
             cfg.available_features,
         ),
     ),
+    # Tests that require the C++17 mathematical special functions in the built library
+    Feature(
+        name="availability-mathematical_special_functions-missing",
+        when=lambda cfg: BooleanExpression.evaluate(
+            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-23)",
+            cfg.available_features,
+        ),
+    ),
 ]

>From c63f3e0b04b37c5dcd6294473878de37d612a0f7 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 13:31:20 +0200
Subject: [PATCH 53/55] cleanup header includes

---
 libcxx/include/__math/special_functions.h     | 1 -
 libcxx/src/mathematical_special_functions.cpp | 1 +
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 546600b72bce0..9721430ee0c64 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -11,7 +11,6 @@
 #define _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
 
 #include <__config>
-#include <__configuration/attributes.h>
 #include <__math/copysign.h>
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index dc046b3a845df..e9f655d56aa69 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -37,6 +37,7 @@
 #endif
 
 #define BOOST_MATH_NO_EXCEPTIONS
+#include <boost/math/policies/policy.hpp>
 #include <boost/math/special_functions/laguerre.hpp>
 
 _LIBCPP_BEGIN_NAMESPACE_STD

>From 8afb315b5746d25b2c2f915d91fffa49b41720f8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 14:02:44 +0200
Subject: [PATCH 54/55] test: add integral tests; refactor

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 72 +++++++++----------
 1 file changed, 36 insertions(+), 36 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 91a7c4a3d2047..7561cb27b6020 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,62 +14,62 @@
 
 // <cmath>
 
-// double      assoc_laguerre(unsigned n, unsigned m, double x);
-// float       assoc_laguerref(unsigned n, unsigned m, float x);
-// long double assoc_laguerrel(unsigned n, unsigned m, long double x);
+// floating-point-type assoc_laguerre( unsigned n, unsigned m, floating-point-type x);
+// float               assoc_laguerref(unsigned n, unsigned m, float x              );
+// long double         assoc_laguerrel(unsigned n, unsigned m, long double x        );
 
 #include <cassert>
 #include <cmath>
+#include <initializer_list>
 #include <limits>
 #include <type_traits>
 
 #include "common.h"
 #include "type_algorithms.h"
 
-// Each overload is fixed to a single floating-point type, so it is tested
-// directly with that type. The integer-argument overload
-// (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
-template <class ExpectedFuncRet, class FuncArgX, class Func>
-void test(Func assoc_laguerre) {
-  // sample values
-  assert(between(0.99, assoc_laguerre(0, 0, FuncArgX(0)), 1.01));
-  assert(between(0.99, assoc_laguerre(1, 1, FuncArgX(1)), 1.01));
-  assert(between(-1.01, assoc_laguerre(2, 0, FuncArgX(2)), -0.99));
-  assert(between(-0.01, assoc_laguerre(2, 2, FuncArgX(2)), 0.01));
-  if constexpr (std::is_floating_point_v<FuncArgX>)
-    assert(between(60.124, assoc_laguerre(2, 10, FuncArgX(0.5)), 60.126));
+// Tests a fixed-precision overload (assoc_laguerre / assoc_laguerref / assoc_laguerrel),
+// whose argument and return type are both `Float`.
+template <class Float, class Func>
+void test_floating_point(Func assoc_laguerre) {
+  // return type is the argument type
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, Float(0))), Float>);
 
-  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, FuncArgX(0))), ExpectedFuncRet>);
+  // sample values
+  assert(between(0.99, assoc_laguerre(0, 0, Float(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, Float(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, Float(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, Float(2)), 0.01));
+  assert(between(60.124, assoc_laguerre(2, 10, Float(0.5)), 60.126));
 
-  // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
-  if constexpr (std::is_floating_point_v<FuncArgX>) {
-    auto test_nan = [&](FuncArgX nan) {
-      check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); });
-    };
-    if (std::numeric_limits<FuncArgX>::has_quiet_NaN)
-      test_nan(std::numeric_limits<FuncArgX>::quiet_NaN());
-    if (std::numeric_limits<FuncArgX>::has_signaling_NaN)
-      test_nan(std::numeric_limits<FuncArgX>::signaling_NaN());
-  }
+  // a negative argument is in the domain (no domain error)
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, Float(-1)), 2.01)); });
 
-  // A negative argument is in the domain (no domain error).
-  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, FuncArgX(-1)), 2.01)); });
+  // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1)
+  auto test_nan = [&](Float nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
+  if (std::numeric_limits<Float>::has_quiet_NaN)
+    test_nan(std::numeric_limits<Float>::quiet_NaN());
+  if (std::numeric_limits<Float>::has_signaling_NaN)
+    test_nan(std::numeric_limits<Float>::signaling_NaN());
 }
 
-struct TestIntegral {
-  template <class Int>
+// Tests the integer-argument overload: it promotes the argument to double and returns double.
+struct TestInteger {
+  template <class Integer>
   void operator()() const {
-    test<double, Int>([](unsigned n, unsigned m, Int x) { return std::assoc_laguerre(n, m, x); });
+    static_assert(std::is_same_v<decltype(std::assoc_laguerre(0u, 0u, Integer(0))), double>);
+
+    // same result as the double overload with the argument cast to double
+    for (Integer x : {Integer(0), Integer(1), Integer(2)})
+      assert(std::assoc_laguerre(2, 0, x) == std::assoc_laguerre(2, 0, static_cast<double>(x)));
   }
 };
 
 int main(int, char**) {
-  test<double, double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
-
-  test<float, float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
-  test<long double, long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
+  test_floating_point<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test_floating_point<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+  test_floating_point<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
-  // types::for_each(types::integral_types{}, TestIntegral{}); WIP
+  types::for_each(types::integral_types{}, TestInteger{});
 
   return 0;
 }

>From 7236c0aa7861a04227195f8fa88d0f7efde7267c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 14:21:17 +0200
Subject: [PATCH 55/55] impl assoc_laguerre(float/long double) + tests

---
 libcxx/include/__math/special_functions.h          | 14 ++++++++++++--
 .../numerics/c.math/sf.cmath/nodiscard.verify.cpp  |  6 ++++--
 .../c.math/sf.cmath/noexcept.compile.pass.cpp      | 13 +++----------
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        |  2 ++
 4 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 9721430ee0c64..8bb805d721b5f 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -37,7 +37,7 @@ __assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
 [[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI float
-assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
@@ -47,7 +47,7 @@ assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
 }
 
 [[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI long double
-assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
+assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
@@ -57,6 +57,16 @@ assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
 }
 
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI long double
+assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 // hermite
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 598311f20917d..4729dbd23c58e 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -19,8 +19,10 @@
 void test() {
   // clang-format off
   // assoc_laguerre
-  std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::assoc_laguerre(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0.0f);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0.0);   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0.0l);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0);     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 8f9dd87c0ee87..b9993ab6500a7 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -17,21 +17,14 @@
 #include <cmath>
 
 #include "test_macros.h"
-#include "type_algorithms.h"
-
-struct TestIntegral {
-  template <class T>
-  void operator()() const {
-    ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, T{}));
-  }
-};
 
 void test() {
   // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0f));
   ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0));
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0l));
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0));
 
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
   ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
-
-  types::for_each(types::integral_types{}, TestIntegral{});
 }
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 7561cb27b6020..a0dca3cf5ce14 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -66,8 +66,10 @@ struct TestInteger {
 
 int main(int, char**) {
   test_floating_point<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test_floating_point<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerre(n, m, x); });
   test_floating_point<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
   test_floating_point<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
+  test_floating_point<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerre(n, m, x); });
 
   types::for_each(types::integral_types{}, TestInteger{});
 


From libcxx-commits at lists.llvm.org  Sat Jul  4 05:22:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 05:22:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][math][c++17] P0226R1 -
 Mathematical Special Functions: infra + `std::assoc_laguerre` (PR #205649)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205649@github.com>
Message-ID: <6a48fb0d.491d90f5.1f0d58.1973@mx.google.com>


https://github.com/PaulXiCao edited https://github.com/llvm/llvm-project/pull/205649

From libcxx-commits at lists.llvm.org  Sat Jul  4 05:22:38 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 05:22:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][math][c++17] P0226R1 -
 Mathematical Special Functions: infra + `std::assoc_laguerre` (PR #205649)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205649@github.com>
Message-ID: <6a48fb0e.eb24c6ea.1f2e26.c56b@mx.google.com>


https://github.com/PaulXiCao edited https://github.com/llvm/llvm-project/pull/205649

From libcxx-commits at lists.llvm.org  Sat Jul  4 05:22:45 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 05:22:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][math][c++17] P0226R1 -
 Mathematical Special Functions: infra + `std::assoc_laguerre` (PR #205649)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205649@github.com>
Message-ID: <6a48fb15.cb30c3e4.6f38b.d1ec@mx.google.com>


https://github.com/PaulXiCao edited https://github.com/llvm/llvm-project/pull/205649

From libcxx-commits at lists.llvm.org  Sat Jul  4 07:31:38 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 07:31:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a49194a.30759430.1908d3.1cf4@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From d93e557c304cea49a5cf654b3bf1710f5cee6855 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 1/8] [libc++] make std::atomic works with types with paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..5e1a7afea06a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From f17e51a372a7a1372fa63a572871939eec329145 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 2/8] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From ac972d414dd906c0e73a3e8b70697abfc537dafa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 3/8] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e1a7afea06a2..4477c821235de 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..b0f40e5a261ed 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -896,6 +896,7 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 33732fee8003a503f62524e1208aef02094b0bc6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 4/8] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From dc33657dd85cca1c1511d5cd7605bbcca05b35f8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 5/8] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From f133b64bf0da078d6ef4659603f8f92f12e7ec8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 6/8] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From e943f4968a86c2c73214cdb22ccdf77b122428e5 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 7/8] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 6070b97b3d62691fabebe6dbd7b79a3fd5a58c4d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 8/8] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 56cbfdaa5c0ff..8ccbcf9d8d227 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -160,7 +160,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -178,7 +178,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -193,7 +193,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b20b5e25fc9cd..04e12afd21689 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)


From libcxx-commits at lists.llvm.org  Sat Jul  4 10:54:13 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 10:54:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ThreadSafety] Add thread safety
 annotations for variadic std::scoped_lock (PR #204462)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204462@github.com>
Message-ID: <6a4948c5.cb30c3e4.6f38b.ec26@mx.google.com>


https://github.com/AnthonyCalandraGeotab updated https://github.com/llvm/llvm-project/pull/204462

>From 2bbdf8c84976204c9340db64265775754fc4a7ca Mon Sep 17 00:00:00 2001
From: Anthony Calandra <anthony at anthony-calandra.com>
Date: Wed, 17 Jun 2026 17:05:52 -0400
Subject: [PATCH] [libc++][ThreadSafety] Add thread safety annotations for
 variadic std::scoped_lock

The thread safety annotations on std::scoped_lock were previously only
applied to the empty and single-mutex specializations. The general
variadic specialization carried no annotations, so -Wthread-safety
considered none of the mutexes held inside a multi-mutex scoped_lock
block and reported the guarded data accessed there as unprotected.

Now that Clang supports pack expansion inside thread safety attributes
(landed for Clang 21), annotate the variadic scoped_lock with
acquire_capability/requires_capability on its constructors,
release_capability on its destructor, and scoped_lockable on the class.

Fixes #42000.
---
 libcxx/docs/ReleaseNotes/23.rst               |  3 +
 libcxx/include/__configuration/attributes.h   |  6 +-
 libcxx/include/__mutex/lock_guard.h           |  2 +-
 libcxx/include/__mutex/mutex.h                |  2 +-
 libcxx/include/mutex                          | 18 +++--
 libcxx/include/shared_mutex                   |  4 +-
 .../thread_safety_scoped_lock.pass.cpp        | 69 +++++++++++++++++++
 .../thread_safety_scoped_lock.verify.cpp      | 59 ++++++++++++++++
 8 files changed, 150 insertions(+), 13 deletions(-)
 create mode 100644 libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.pass.cpp
 create mode 100644 libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.verify.cpp

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1852351cedc4e..af4d29dce41ee 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -71,6 +71,9 @@ Improvements and New Features
   unspecified non-null string, so user code that does not match on the exact message remains correct.
 - ``ifstream::read`` has been optimized to pass through large reads to system calls directly instead of copying them in
   chunks.
+- ``std::scoped_lock`` now carries Clang thread safety annotations when locking multiple mutexes, so
+  ``-Wthread-safety`` no longer incorrectly reports the guarded data accessed inside the block as
+  unprotected. This requires Clang 21 or later.
 
 Deprecations and Removals
 -------------------------
diff --git a/libcxx/include/__configuration/attributes.h b/libcxx/include/__configuration/attributes.h
index c543ba9cb9e25..a49bedd1b874a 100644
--- a/libcxx/include/__configuration/attributes.h
+++ b/libcxx/include/__configuration/attributes.h
@@ -342,10 +342,10 @@
 #  define _LIBCPP_TRY_ACQUIRE_SHARED_CAPABILITY(...)
 #endif
 
-#if __has_cpp_attribute(_Clang::__release_capability__)
-#  define _LIBCPP_RELEASE_CAPABILITY [[_Clang::__release_capability__]]
+#if __has_attribute(__release_capability__)
+#  define _LIBCPP_RELEASE_CAPABILITY(...) __attribute__((__release_capability__(__VA_ARGS__)))
 #else
-#  define _LIBCPP_RELEASE_CAPABILITY
+#  define _LIBCPP_RELEASE_CAPABILITY(...)
 #endif
 
 #if __has_cpp_attribute(_Clang::__release_shared_capability__)
diff --git a/libcxx/include/__mutex/lock_guard.h b/libcxx/include/__mutex/lock_guard.h
index 67a25ed2f8a89..29022e8c23ece 100644
--- a/libcxx/include/__mutex/lock_guard.h
+++ b/libcxx/include/__mutex/lock_guard.h
@@ -34,7 +34,7 @@ class _LIBCPP_SCOPED_LOCKABLE lock_guard {
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI lock_guard(mutex_type& __m, adopt_lock_t) _LIBCPP_REQUIRES_CAPABILITY(__m)
       : __m_(__m) {}
-  _LIBCPP_RELEASE_CAPABILITY _LIBCPP_HIDE_FROM_ABI ~lock_guard() { __m_.unlock(); }
+  _LIBCPP_RELEASE_CAPABILITY() _LIBCPP_HIDE_FROM_ABI ~lock_guard() { __m_.unlock(); }
 
   lock_guard(lock_guard const&)            = delete;
   lock_guard& operator=(lock_guard const&) = delete;
diff --git a/libcxx/include/__mutex/mutex.h b/libcxx/include/__mutex/mutex.h
index d0addacc2893f..f71cba03cd4dc 100644
--- a/libcxx/include/__mutex/mutex.h
+++ b/libcxx/include/__mutex/mutex.h
@@ -39,7 +39,7 @@ class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_CAPABILITY("mutex") mutex {
 
   _LIBCPP_ACQUIRE_CAPABILITY() void lock();
   [[__nodiscard__]] _LIBCPP_TRY_ACQUIRE_CAPABILITY(true) bool try_lock() _NOEXCEPT;
-  _LIBCPP_RELEASE_CAPABILITY void unlock() _NOEXCEPT;
+  _LIBCPP_RELEASE_CAPABILITY() void unlock() _NOEXCEPT;
 
   typedef __libcpp_mutex_t* native_handle_type;
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__m_; }
diff --git a/libcxx/include/mutex b/libcxx/include/mutex
index 75b080f8cd088..26ff343a21c1d 100644
--- a/libcxx/include/mutex
+++ b/libcxx/include/mutex
@@ -458,7 +458,7 @@ public:
     __m_.lock();
   }
 
-  _LIBCPP_RELEASE_CAPABILITY _LIBCPP_HIDE_FROM_ABI ~scoped_lock() { __m_.unlock(); }
+  _LIBCPP_RELEASE_CAPABILITY() _LIBCPP_HIDE_FROM_ABI ~scoped_lock() { __m_.unlock(); }
 
   [[nodiscard]]
   _LIBCPP_HIDE_FROM_ABI explicit scoped_lock(adopt_lock_t, mutex_type& __m) _LIBCPP_REQUIRES_CAPABILITY(__m)
@@ -469,25 +469,31 @@ public:
 };
 
 template <class... _MArgs>
-class scoped_lock {
+class _LIBCPP_SCOPED_LOCKABLE scoped_lock {
   static_assert(sizeof...(_MArgs) > 1, "At least 2 lock types required");
   typedef tuple<_MArgs&...> _MutexTuple;
 
 public:
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit scoped_lock(_MArgs&... __margs) : __t_(__margs...) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit scoped_lock(_MArgs&... __margs) _LIBCPP_ACQUIRE_CAPABILITY(__margs...)
+      : __t_(__margs...) {
     std::lock(__margs...);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI scoped_lock(adopt_lock_t, _MArgs&... __margs) : __t_(__margs...) {}
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI scoped_lock(adopt_lock_t, _MArgs&... __margs)
+      _LIBCPP_REQUIRES_CAPABILITY(__margs...)
+      : __t_(__margs...) {}
 
-  _LIBCPP_HIDE_FROM_ABI ~scoped_lock() { __unlock_unpack(make_index_sequence<sizeof...(_MArgs)>(), __t_); }
+  _LIBCPP_RELEASE_CAPABILITY() _LIBCPP_HIDE_FROM_ABI ~scoped_lock() {
+    __unlock_unpack(make_index_sequence<sizeof...(_MArgs)>(), __t_);
+  }
 
   scoped_lock(scoped_lock const&)            = delete;
   scoped_lock& operator=(scoped_lock const&) = delete;
 
 private:
   template <size_t... _Indx>
-  _LIBCPP_HIDE_FROM_ABI static void __unlock_unpack(index_sequence<_Indx...>, _MutexTuple& __mt) {
+  _LIBCPP_HIDE_FROM_ABI static void __unlock_unpack(index_sequence<_Indx...>, _MutexTuple& __mt)
+      _LIBCPP_RELEASE_CAPABILITY(std::get<_Indx>(__mt)...) {
     (std::get<_Indx>(__mt).unlock(), ...);
   }
 
diff --git a/libcxx/include/shared_mutex b/libcxx/include/shared_mutex
index 9479f0aa616f8..8fc89fe1cb97c 100644
--- a/libcxx/include/shared_mutex
+++ b/libcxx/include/shared_mutex
@@ -200,7 +200,7 @@ public:
   [[nodiscard]] _LIBCPP_TRY_ACQUIRE_CAPABILITY(true) _LIBCPP_HIDE_FROM_ABI bool try_lock() {
     return __base_.try_lock();
   }
-  _LIBCPP_RELEASE_CAPABILITY _LIBCPP_HIDE_FROM_ABI void unlock() { return __base_.unlock(); }
+  _LIBCPP_RELEASE_CAPABILITY() _LIBCPP_HIDE_FROM_ABI void unlock() { return __base_.unlock(); }
 
   // Shared ownership
   _LIBCPP_ACQUIRE_SHARED_CAPABILITY _LIBCPP_HIDE_FROM_ABI void lock_shared() { return __base_.lock_shared(); }
@@ -262,7 +262,7 @@ public:
     return true;
   }
 
-  _LIBCPP_RELEASE_CAPABILITY void unlock();
+  _LIBCPP_RELEASE_CAPABILITY() void unlock();
 
   // Shared ownership
   _LIBCPP_ACQUIRE_SHARED_CAPABILITY void lock_shared();
diff --git a/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.pass.cpp b/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.pass.cpp
new file mode 100644
index 0000000000000..06751f9d49215
--- /dev/null
+++ b/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: no-threads, c++03, c++11, c++14
+
+// <mutex>
+
+// GCC doesn't have thread safety attributes
+// UNSUPPORTED: gcc
+
+// ADDITIONAL_COMPILE_FLAGS: -Wthread-safety
+
+#include <mutex>
+
+std::mutex m1;
+std::mutex m2;
+int data1 __attribute__((guarded_by(m1)));
+int data2 __attribute__((guarded_by(m2)));
+int data3;
+
+static void no_mutex() {
+  std::scoped_lock<> lock;
+  data3++;
+}
+
+static void one_mutex() {
+  std::scoped_lock<std::mutex> lock(m1);
+  data1++;
+}
+
+static void two_mutexes() {
+  std::scoped_lock<std::mutex, std::mutex> lock(m1, m2);
+  data1++;
+  data2++;
+}
+
+static void adopt_none() {
+  std::scoped_lock<> lock(std::adopt_lock);
+  data3++;
+}
+
+static void adopt_one() {
+  m1.lock();
+  std::scoped_lock<std::mutex> lock(std::adopt_lock, m1);
+  data1++;
+}
+
+static void adopt_two() {
+  m1.lock();
+  m2.lock();
+  std::scoped_lock<std::mutex, std::mutex> lock(std::adopt_lock, m1, m2);
+  data1++;
+  data2++;
+}
+
+int main(int, char**) {
+  no_mutex();
+  one_mutex();
+  two_mutexes();
+  adopt_none();
+  adopt_one();
+  adopt_two();
+  return 0;
+}
diff --git a/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.verify.cpp b/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.verify.cpp
new file mode 100644
index 0000000000000..3f9dd62c91f52
--- /dev/null
+++ b/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.verify.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: no-threads, c++03, c++11, c++14
+
+// <mutex>
+
+// GCC doesn't have thread safety attributes
+// UNSUPPORTED: gcc
+
+// ADDITIONAL_COMPILE_FLAGS: -Wthread-safety
+
+#include <mutex>
+
+std::mutex m1;
+std::mutex m2;
+int data1 __attribute__((guarded_by(m1)));
+int data2 __attribute__((guarded_by(m2)));
+
+void no_lock_write() {
+  data1++; // expected-warning {{writing variable 'data1' requires holding mutex 'm1' exclusively}}
+}
+
+void no_lock_write2() {
+  std::scoped_lock<> lock;
+  data1++; // expected-warning {{writing variable 'data1' requires holding mutex 'm1' exclusively}}
+}
+
+void wrong_lock_write() {
+  std::scoped_lock<std::mutex> lock(m1);
+  data2++; // expected-warning {{writing variable 'data2' requires holding mutex 'm2' exclusively}}
+}
+
+void adopt_without_lock() {
+  // expected-warning at +1 {{calling function 'scoped_lock' requires holding mutex 'm1' exclusively}}
+  std::scoped_lock<std::mutex> lock(std::adopt_lock, m1);
+  data1++; // expected-warning {{writing variable 'data1' requires holding mutex 'm1' exclusively}}
+}
+
+void adopt_two_only_one_locked() {
+  m1.lock();
+  // expected-warning at +1 {{calling function 'scoped_lock' requires holding mutex 'm2' exclusively}}
+  std::scoped_lock<std::mutex, std::mutex> lock(std::adopt_lock, m1, m2);
+  data1++;
+  data2++; // expected-warning {{writing variable 'data2' requires holding mutex 'm2' exclusively}}
+}
+
+void after_scoped_lock_scope() {
+  {
+    std::scoped_lock<std::mutex> lock(m1);
+    data1++;
+  }
+  data1++; // expected-warning {{writing variable 'data1' requires holding mutex 'm1' exclusively}}
+}


From libcxx-commits at lists.llvm.org  Sat Jul  4 12:43:52 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 12:43:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG4098 as resolved (PR
	#206295)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206295@github.com>
Message-ID: <6a496278.f1c1923d.3672e2.9b76@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/206295

>From c98aae7e0a3fe5cca883af52c7d5c1d6bbb37c18 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 28 Jun 2026 02:21:57 +0200
Subject: [PATCH] mark LWG4098 with Complete tag and add clang ver

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..40bf1cc72f6ee 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -72,7 +72,7 @@
 "`LWG4082 <https://wg21.link/LWG4082>`__","``views::concat(r)`` is well-formed when ``r`` is an ``output_range``","2024-06 (St. Louis)","|Complete|","23","`#105349 <https://github.com/llvm/llvm-project/issues/105349>`__",""
 "`LWG4083 <https://wg21.link/LWG4083>`__","``views::as_rvalue`` should reject non-input ranges","2024-06 (St. Louis)","|Complete|","22","`#105351 <https://github.com/llvm/llvm-project/issues/105351>`__",""
 "`LWG4096 <https://wg21.link/LWG4096>`__","``views::iota(views::iota(0))`` should be rejected","2024-06 (St. Louis)","|Complete|","22","`#105352 <https://github.com/llvm/llvm-project/issues/105352>`__",""
-"`LWG4098 <https://wg21.link/LWG4098>`__","``views::adjacent<0>`` should reject non-forward ranges","2024-06 (St. Louis)","","","`#105353 <https://github.com/llvm/llvm-project/issues/105353>`__",""
+"`LWG4098 <https://wg21.link/LWG4098>`__","``views::adjacent<0>`` should reject non-forward ranges","2024-06 (St. Louis)","|Complete|","22","`#105353 <https://github.com/llvm/llvm-project/issues/105353>`__",""
 "`LWG4105 <https://wg21.link/LWG4105>`__","``ranges::ends_with``\`s Returns misses difference casting","2024-06 (St. Louis)","","","`#105354 <https://github.com/llvm/llvm-project/issues/105354>`__",""
 "`LWG4106 <https://wg21.link/LWG4106>`__","``basic_format_args`` should not be default-constructible","2024-06 (St. Louis)","|Complete|","19","`#105355 <https://github.com/llvm/llvm-project/issues/105355>`__",""
 "","","","","","",""


From libcxx-commits at lists.llvm.org  Sat Jul  4 12:44:25 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 12:44:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
 `adjacent_transform_view` (PR #205900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205900@github.com>
Message-ID: <6a496299.492e23e4.e71ca.5824@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/205900

>From 65046f1524740c0e4a7829b61cd6a7601c34cfa5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 25 Jun 2026 21:47:55 +0200
Subject: [PATCH 01/12] initial: nodiscard adjacent_transform_view

---
 .../include/__ranges/adjacent_transform_view.h  | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..bdae098623977 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -105,22 +105,23 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   _LIBCPP_HIDE_FROM_ABI constexpr explicit adjacent_transform_view(_View __base, _Fn __fun)
       : __inner_(std::move(__base)), __fun_(std::in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __inner_.base();
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {
     return __iterator<true>(*this, __inner_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __inner_.end());
     } else {
@@ -128,7 +129,7 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -138,13 +139,13 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __inner_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __inner_.size();

>From 0b582345030b80efba493370df6595f56abdaf64 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 25 Jun 2026 23:51:42 +0200
Subject: [PATCH 02/12] Update
 libcxx/include/__ranges/adjacent_transform_view.h

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 libcxx/include/__ranges/adjacent_transform_view.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index bdae098623977..5d22042e9c78f 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -114,7 +114,6 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {

>From acc5961454c76807c11297fc8ac970135d47f69f Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 27 Jun 2026 17:50:55 +0200
Subject: [PATCH 03/12] complete: nodiscard to adjacent_transform_view.h

---
 .../include/__ranges/adjacent_transform_view.h | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 5d22042e9c78f..f8c4cd716a8d5 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -202,7 +202,7 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     requires _Const && convertible_to<__inner_iterator<false>, __inner_iterator<true>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(__noexcept_dereference(make_index_sequence<_Np>{})) {
     return std::apply(
         [&](const auto&... __iters) -> decltype(auto) { return std::invoke(*__parent_->__fun_, *__iters...); },
@@ -249,7 +249,7 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -291,25 +291,25 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__inner_iterator<_Const>, __inner_iterator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -344,14 +344,14 @@ class adjacent_transform_view<_View, _Fn, _Np>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -371,7 +371,7 @@ struct __fn : __range_adaptor_closure<__fn<_Np>> {
   }
 
   template <class _Range, class _Fn>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range, _Fn&& __fn) noexcept(
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range, _Fn&& __fn) noexcept(
       noexcept(adjacent_transform_view<views::all_t<_Range&&>, decay_t<_Fn>, _Np>(
           std::forward<_Range>(__range), std::forward<_Fn>(__fn))))
       -> decltype(adjacent_transform_view<views::all_t<_Range&&>, decay_t<_Fn>, _Np>(

>From 15e15ee15ad0e6580dfff6f8fd3da6fbb9255854 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 27 Jun 2026 17:51:04 +0200
Subject: [PATCH 04/12] complete: nodiscard to adjacent_transform_view.h

---
 libcxx/include/__ranges/adjacent_transform_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index f8c4cd716a8d5..10bf812607b3e 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -309,7 +309,8 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__inner_iterator<_Const>, __inner_iterator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;

>From 287a7d06ae3ca47191a279c987f60137bd0e5761 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 27 Jun 2026 18:24:55 +0200
Subject: [PATCH 05/12] add: initial test file

---
 .../nodiscard.verify.cpp                      | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..6a26fa8bfda85
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,20 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Test the libc++ extension that std::ranges::adjacent_transform_view and std::views::adjacent_transform are marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+#include <functional>
+
+void test(){
+
+}

>From 3aee8e6f2e9c2c3f291da0f79993c161c7c3189d Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 28 Jun 2026 02:18:05 +0200
Subject: [PATCH 06/12] add: test view

---
 .../range.adjacent.transform/nodiscard.verify.cpp      | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 6a26fa8bfda85..459b132766ae0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -15,6 +15,16 @@
 #include <utility>
 #include <functional>
 
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
 void test(){
 
 }

>From 5658eb286ecf418576bc84f50273e2ca37d11a10 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 29 Jun 2026 23:09:10 +0200
Subject: [PATCH 07/12] add: tests for adjacent.transform

---
 .../nodiscard.verify.cpp                      | 65 ++++++++++++++++++-
 1 file changed, 64 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 459b132766ae0..9cf0fffb3ba6a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -25,6 +25,69 @@ static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
-void test(){
+void test() {
+  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  auto v      = View{} | std::views::adjacent_transform<2>(std::plus<>{});
 
+  // [range.adjacent.transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).size();
+
+  // [range.adjacent.transform.iterator]
+
+  auto it   = v.begin();
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it[0];
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  0 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.adjacent.tranform.sentinel]
+
+  auto st = v.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
+  // [range.adjacent.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<0>(range);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<2>(range);
 }

>From b6146befe51e731c8b2bd19f57772ec2e3e50c54 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 10:24:32 +0200
Subject: [PATCH 08/12] fix: formatting

---
 libcxx/include/__ranges/adjacent_transform_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 10bf812607b3e..92aaeb24be183 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -110,10 +110,10 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.base();
   }
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {

>From 4aa95f70a6e45139e002c7f51167fdf0affa5312 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 10:45:28 +0200
Subject: [PATCH 09/12] ref: std::plus<> to std::multiplies() as per the
 standard 25.7.28.1.3 example

---
 .../range.adjacent.transform/nodiscard.verify.cpp               | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 9cf0fffb3ba6a..9677ea4f06019 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -27,7 +27,7 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
   int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  auto v      = View{} | std::views::adjacent_transform<2>(std::plus<>{});
+  auto v      = View{} | std::views::adjacent_transform<2>(std::multiplies());
 
   // [range.adjacent.transform.view]
 

>From 4459d77f262cf216acb83805dcffe636399f19f5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 14:24:02 +0200
Subject: [PATCH 10/12] fix: includes order and transform_view<2>(...) test

---
 .../range.adjacent.transform/nodiscard.verify.cpp        | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 9677ea4f06019..4a6d087a9e1f4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -11,9 +11,9 @@
 
 // Test the libc++ extension that std::ranges::adjacent_transform_view and std::views::adjacent_transform are marked as [[nodiscard]].
 
+#include <functional>
 #include <ranges>
 #include <utility>
-#include <functional>
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -26,8 +26,7 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  auto v      = View{} | std::views::adjacent_transform<2>(std::multiplies());
+  auto v = View{} | std::views::adjacent_transform<2>(std::multiplies());
 
   // [range.adjacent.transform.view]
 
@@ -85,9 +84,11 @@ void test() {
 
   // [range.adjacent.overview]
 
+  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::adjacent_transform<0>(range);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::adjacent_transform<2>(range);
+  std::views::adjacent_transform<2>(range, std::multiplies());
 }

>From 29183f98efbac1b381b8ddf6d6ffdb1d160c034b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 3 Jul 2026 11:36:20 +0200
Subject: [PATCH 11/12] Update
 libcxx/include/__ranges/adjacent_transform_view.h

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 libcxx/include/__ranges/adjacent_transform_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 92aaeb24be183..e0c20b7a95513 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -365,7 +365,7 @@ template <size_t _Np>
 struct __fn : __range_adaptor_closure<__fn<_Np>> {
   template <class _Range, class _Fn>
     requires(_Np == 0 && forward_range<_Range &&>)
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
   operator()(_Range&&, _Fn&& __fn) noexcept(noexcept(views::zip_transform(std::forward<_Fn>(__fn))))
       -> decltype(views::zip_transform(std::forward<_Fn>(__fn))) {
     return views::zip_transform(std::forward<_Fn>(__fn));

>From 1f670586ceea6efb96928f99221e5bc361654681 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 4 Jul 2026 21:44:10 +0200
Subject: [PATCH 12/12] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../range.adjacent.transform/nodiscard.verify.cpp        | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 4a6d087a9e1f4..017d7e584613d 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -89,6 +89,15 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::adjacent_transform<0>(range);
 
+  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  static_assert(std::ranges::forward_range<decltype(range)>);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<0>(range, Fn{});
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::adjacent_transform<2>(range, std::multiplies());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<2>(std::multiplies());
 }


From libcxx-commits at lists.llvm.org  Sat Jul  4 12:56:06 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 12:56:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
 `adjacent_transform_view` (PR #205900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205900@github.com>
Message-ID: <6a496556.d1496011.3d49cb.9dda@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/205900

>From 65046f1524740c0e4a7829b61cd6a7601c34cfa5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 25 Jun 2026 21:47:55 +0200
Subject: [PATCH 01/13] initial: nodiscard adjacent_transform_view

---
 .../include/__ranges/adjacent_transform_view.h  | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..bdae098623977 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -105,22 +105,23 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   _LIBCPP_HIDE_FROM_ABI constexpr explicit adjacent_transform_view(_View __base, _Fn __fun)
       : __inner_(std::move(__base)), __fun_(std::in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __inner_.base();
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {
     return __iterator<true>(*this, __inner_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __inner_.end());
     } else {
@@ -128,7 +129,7 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -138,13 +139,13 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __inner_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __inner_.size();

>From 0b582345030b80efba493370df6595f56abdaf64 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 25 Jun 2026 23:51:42 +0200
Subject: [PATCH 02/13] Update
 libcxx/include/__ranges/adjacent_transform_view.h

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 libcxx/include/__ranges/adjacent_transform_view.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index bdae098623977..5d22042e9c78f 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -114,7 +114,6 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {

>From acc5961454c76807c11297fc8ac970135d47f69f Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 27 Jun 2026 17:50:55 +0200
Subject: [PATCH 03/13] complete: nodiscard to adjacent_transform_view.h

---
 .../include/__ranges/adjacent_transform_view.h | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 5d22042e9c78f..f8c4cd716a8d5 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -202,7 +202,7 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     requires _Const && convertible_to<__inner_iterator<false>, __inner_iterator<true>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(__noexcept_dereference(make_index_sequence<_Np>{})) {
     return std::apply(
         [&](const auto&... __iters) -> decltype(auto) { return std::invoke(*__parent_->__fun_, *__iters...); },
@@ -249,7 +249,7 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -291,25 +291,25 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__inner_iterator<_Const>, __inner_iterator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -344,14 +344,14 @@ class adjacent_transform_view<_View, _Fn, _Np>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -371,7 +371,7 @@ struct __fn : __range_adaptor_closure<__fn<_Np>> {
   }
 
   template <class _Range, class _Fn>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range, _Fn&& __fn) noexcept(
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range, _Fn&& __fn) noexcept(
       noexcept(adjacent_transform_view<views::all_t<_Range&&>, decay_t<_Fn>, _Np>(
           std::forward<_Range>(__range), std::forward<_Fn>(__fn))))
       -> decltype(adjacent_transform_view<views::all_t<_Range&&>, decay_t<_Fn>, _Np>(

>From 15e15ee15ad0e6580dfff6f8fd3da6fbb9255854 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 27 Jun 2026 17:51:04 +0200
Subject: [PATCH 04/13] complete: nodiscard to adjacent_transform_view.h

---
 libcxx/include/__ranges/adjacent_transform_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index f8c4cd716a8d5..10bf812607b3e 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -309,7 +309,8 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__inner_iterator<_Const>, __inner_iterator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;

>From 287a7d06ae3ca47191a279c987f60137bd0e5761 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 27 Jun 2026 18:24:55 +0200
Subject: [PATCH 05/13] add: initial test file

---
 .../nodiscard.verify.cpp                      | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..6a26fa8bfda85
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,20 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Test the libc++ extension that std::ranges::adjacent_transform_view and std::views::adjacent_transform are marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+#include <functional>
+
+void test(){
+
+}

>From 3aee8e6f2e9c2c3f291da0f79993c161c7c3189d Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 28 Jun 2026 02:18:05 +0200
Subject: [PATCH 06/13] add: test view

---
 .../range.adjacent.transform/nodiscard.verify.cpp      | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 6a26fa8bfda85..459b132766ae0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -15,6 +15,16 @@
 #include <utility>
 #include <functional>
 
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
 void test(){
 
 }

>From 5658eb286ecf418576bc84f50273e2ca37d11a10 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 29 Jun 2026 23:09:10 +0200
Subject: [PATCH 07/13] add: tests for adjacent.transform

---
 .../nodiscard.verify.cpp                      | 65 ++++++++++++++++++-
 1 file changed, 64 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 459b132766ae0..9cf0fffb3ba6a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -25,6 +25,69 @@ static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
-void test(){
+void test() {
+  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  auto v      = View{} | std::views::adjacent_transform<2>(std::plus<>{});
 
+  // [range.adjacent.transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).size();
+
+  // [range.adjacent.transform.iterator]
+
+  auto it   = v.begin();
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it[0];
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  0 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.adjacent.tranform.sentinel]
+
+  auto st = v.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
+  // [range.adjacent.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<0>(range);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<2>(range);
 }

>From b6146befe51e731c8b2bd19f57772ec2e3e50c54 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 10:24:32 +0200
Subject: [PATCH 08/13] fix: formatting

---
 libcxx/include/__ranges/adjacent_transform_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 10bf812607b3e..92aaeb24be183 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -110,10 +110,10 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.base();
   }
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {

>From 4aa95f70a6e45139e002c7f51167fdf0affa5312 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 10:45:28 +0200
Subject: [PATCH 09/13] ref: std::plus<> to std::multiplies() as per the
 standard 25.7.28.1.3 example

---
 .../range.adjacent.transform/nodiscard.verify.cpp               | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 9cf0fffb3ba6a..9677ea4f06019 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -27,7 +27,7 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
   int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  auto v      = View{} | std::views::adjacent_transform<2>(std::plus<>{});
+  auto v      = View{} | std::views::adjacent_transform<2>(std::multiplies());
 
   // [range.adjacent.transform.view]
 

>From 4459d77f262cf216acb83805dcffe636399f19f5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 14:24:02 +0200
Subject: [PATCH 10/13] fix: includes order and transform_view<2>(...) test

---
 .../range.adjacent.transform/nodiscard.verify.cpp        | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 9677ea4f06019..4a6d087a9e1f4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -11,9 +11,9 @@
 
 // Test the libc++ extension that std::ranges::adjacent_transform_view and std::views::adjacent_transform are marked as [[nodiscard]].
 
+#include <functional>
 #include <ranges>
 #include <utility>
-#include <functional>
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -26,8 +26,7 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  auto v      = View{} | std::views::adjacent_transform<2>(std::multiplies());
+  auto v = View{} | std::views::adjacent_transform<2>(std::multiplies());
 
   // [range.adjacent.transform.view]
 
@@ -85,9 +84,11 @@ void test() {
 
   // [range.adjacent.overview]
 
+  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::adjacent_transform<0>(range);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::adjacent_transform<2>(range);
+  std::views::adjacent_transform<2>(range, std::multiplies());
 }

>From 29183f98efbac1b381b8ddf6d6ffdb1d160c034b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 3 Jul 2026 11:36:20 +0200
Subject: [PATCH 11/13] Update
 libcxx/include/__ranges/adjacent_transform_view.h

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 libcxx/include/__ranges/adjacent_transform_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 92aaeb24be183..e0c20b7a95513 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -365,7 +365,7 @@ template <size_t _Np>
 struct __fn : __range_adaptor_closure<__fn<_Np>> {
   template <class _Range, class _Fn>
     requires(_Np == 0 && forward_range<_Range &&>)
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
   operator()(_Range&&, _Fn&& __fn) noexcept(noexcept(views::zip_transform(std::forward<_Fn>(__fn))))
       -> decltype(views::zip_transform(std::forward<_Fn>(__fn))) {
     return views::zip_transform(std::forward<_Fn>(__fn));

>From 1f670586ceea6efb96928f99221e5bc361654681 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 4 Jul 2026 21:44:10 +0200
Subject: [PATCH 12/13] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../range.adjacent.transform/nodiscard.verify.cpp        | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 4a6d087a9e1f4..017d7e584613d 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -89,6 +89,15 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::adjacent_transform<0>(range);
 
+  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  static_assert(std::ranges::forward_range<decltype(range)>);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<0>(range, Fn{});
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::adjacent_transform<2>(range, std::multiplies());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<2>(std::multiplies());
 }

>From 21b26530b40196d35c61d3677afa8543e65a179c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 4 Jul 2026 21:55:44 +0200
Subject: [PATCH 13/13] fix: range variable declaration and minor fixes

---
 .../range.adjacent.transform/nodiscard.verify.cpp      | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 017d7e584613d..88fb7e3c39281 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -1,4 +1,3 @@
-
 //===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -25,6 +24,10 @@ static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
+struct Fn {
+  int operator()(auto...) const;
+};
+
 void test() {
   auto v = View{} | std::views::adjacent_transform<2>(std::multiplies());
 
@@ -84,11 +87,6 @@ void test() {
 
   // [range.adjacent.overview]
 
-  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::adjacent_transform<0>(range);
-
   int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
   static_assert(std::ranges::forward_range<decltype(range)>);
 


From libcxx-commits at lists.llvm.org  Sat Jul  4 13:32:54 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 13:32:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
 `elements_view` (PR #206589)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206589@github.com>
Message-ID: <6a496df6.eb24c6ea.1f2e26.53b3@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/206589

>From fde2218d5a8d799e9c2806a51ddd9758c2efd13e Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 29 Jun 2026 23:30:56 +0200
Subject: [PATCH 1/7] add: nodiscard to elements_view

---
 libcxx/include/__ranges/elements_view.h | 40 ++++++++++++-------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..fc8c46b29b57f 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -77,57 +77,57 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit elements_view(_View __base) : __base_(std::move(__base)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
     requires(!__simple_view<_View>)
   {
     return __iterator</*_Const=*/false>(ranges::begin(__base_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _View>
   {
     return __iterator</*_Const=*/true>(ranges::begin(__base_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!__simple_view<_View> && !common_range<_View>)
   {
     return __sentinel</*_Const=*/false>{ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!__simple_view<_View> && common_range<_View>)
   {
     return __iterator</*_Const=*/false>{ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _View>
   {
     return __sentinel</*_Const=*/true>{ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires common_range<const _View>
   {
     return __iterator</*_Const=*/true>{ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
     return ranges::size(__base_);
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
     return ranges::size(__base_);
@@ -211,11 +211,11 @@ class elements_view<_View, _Np>::__iterator
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return __get_element(__current_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return __get_element(__current_); }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
     ++__current_;
@@ -261,7 +261,7 @@ class elements_view<_View, _Np>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return __get_element(__current_ + __n);
@@ -303,25 +303,25 @@ class elements_view<_View, _Np>::__iterator
     return __x.__current_ <=> __y.__current_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __x, difference_type __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __x, difference_type __y)
     requires random_access_range<_Base>
   {
     return __iterator{__x} += __y;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __x, const __iterator& __y)
     requires random_access_range<_Base>
   {
     return __y + __x;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __x, difference_type __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __x, difference_type __y)
     requires random_access_range<_Base>
   {
     return __iterator{__x} -= __y;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
   {
     return __x.__current_ - __y.__current_;
@@ -365,14 +365,14 @@ class elements_view<_View, _Np>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __get_current(__x) - __y.__end_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__end_ - __get_current(__y);
   }

>From 4c961f7efb3efb1985259cd16b13fae3c23ef871 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 29 Jun 2026 23:36:42 +0200
Subject: [PATCH 2/7] fix: clang format

---
 libcxx/include/__ranges/elements_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index fc8c46b29b57f..33c152bbd03b4 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -321,7 +321,8 @@ class elements_view<_View, _Np>::__iterator
     return __iterator{__x} -= __y;
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
   {
     return __x.__current_ - __y.__current_;

>From 8c1bb7ca3ce494344d17e1f41fd8317efe3809da Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 12:46:41 +0200
Subject: [PATCH 3/7] add: range.elements.overview as per the 25.7.23.1.2.1
 example in the Standard

---
 .../range.elements/nodiscard.verify.cpp       | 44 +++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..16759e6a53569
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Test the libc++ extension that std::ranges::transform_view and std::views::transform are marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+#include <functional>
+#include <map>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+void test() {
+    auto historical_figures = std::map{
+      std::pair{"Lovelace"sv, 1815},
+      {"Turing"sv, 1912},
+      {"Babbage"sv, 1791},
+      {"Hamilton"sv, 1936}
+    };
+
+    // [range.elements.overview]
+    
+   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::elements<0>(historical_figures);
+
+   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::elements<1>(historical_figures);
+}
+

>From 77071141de6b3af76d1e8d846b887be981bbdb5e Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 21:59:05 +0200
Subject: [PATCH 4/7] fix: drop sv

---
 .../range.adaptors/range.elements/nodiscard.verify.cpp   | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
index 16759e6a53569..5951a04e31d91 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 #include <functional>
+#include <string_view>
 #include <map>
 
 struct View : std::ranges::view_interface<View> {
@@ -27,10 +28,10 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
     auto historical_figures = std::map{
-      std::pair{"Lovelace"sv, 1815},
-      {"Turing"sv, 1912},
-      {"Babbage"sv, 1791},
-      {"Hamilton"sv, 1936}
+        std::pair{"Lovelace", 1815},
+        std::pair{"Turing", 1912},
+        std::pair{"Babbage", 1791},
+        std::pair{"Hamilton", 1936}
     };
 
     // [range.elements.overview]

>From 6b4b2c0585ec53caefab4562a91ecbd4dd51b9ad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 22:35:49 +0200
Subject: [PATCH 5/7] add: full tests for range.elements

---
 .../range.elements/nodiscard.verify.cpp       | 99 ++++++++++++++-----
 1 file changed, 77 insertions(+), 22 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
index 5951a04e31d91..0f25b2449cf5f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -10,36 +10,91 @@
 
 // Test the libc++ extension that std::ranges::transform_view and std::views::transform are marked as [[nodiscard]].
 
-#include <ranges>
-#include <utility>
 #include <functional>
-#include <string_view>
 #include <map>
+#include <ranges>
+#include <utility>
 
 struct View : std::ranges::view_interface<View> {
-  int* begin();
-  const int* begin() const;
-  volatile int* end();
-  const volatile int* end() const;
+  std::tuple<int, int>* begin();
+  const std::tuple<int, int>* begin() const;
+  volatile std::tuple<int, int>* end();
+  const volatile std::tuple<int, int>* end() const;
 };
 static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-    auto historical_figures = std::map{
-        std::pair{"Lovelace", 1815},
-        std::pair{"Turing", 1912},
-        std::pair{"Babbage", 1791},
-        std::pair{"Hamilton", 1936}
-    };
-
-    // [range.elements.overview]
-    
-   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-    std::views::elements<0>(historical_figures);
-
-   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-    std::views::elements<1>(historical_figures);
-}
+  auto v = View{} | std::views::elements<1>;
+
+  // [range.elements.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).size();
+
+  // [range.elements.iterator]
 
+  auto it   = v.begin();
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(it).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it[0];
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  0 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.elements.sentinel]
+
+  auto st = v.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
+  // [range.elements.overview]
+
+  auto historical_figures = std::map{
+      std::pair{"Lovelace", 1815}, std::pair{"Turing", 1912}, std::pair{"Babbage", 1791}, std::pair{"Hamilton", 1936}};
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::elements<0>(historical_figures);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::elements<1>(historical_figures);
+}

>From 720ef3917f62f01a6c667c4fb37e1547ef96106b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 1 Jul 2026 20:24:25 +0200
Subject: [PATCH 6/7] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../ranges/range.adaptors/range.elements/nodiscard.verify.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
index 0f25b2449cf5f..84790ffe90875 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: std-at-least-c++20
 
-// Test the libc++ extension that std::ranges::transform_view and std::views::transform are marked as [[nodiscard]].
+// Test the libc++ extension that std::ranges::elements_view and std::views::elements are marked as [[nodiscard]].
 
 #include <functional>
 #include <map>

>From 813282415c33ecf47816cfabfc8a980d4143f4b5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 4 Jul 2026 22:32:36 +0200
Subject: [PATCH 7/7] add: model CommonView

---
 .../range.elements/nodiscard.verify.cpp        | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
index 84790ffe90875..088c1dba5af9d 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -25,8 +25,19 @@ static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
+struct CommonView : std::ranges::view_interface<View> {
+  std::tuple<int, int>* begin();
+  const std::tuple<int, int>* begin() const;
+  std::tuple<int, int>* end();
+  const std::tuple<int, int>* end() const;
+};
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(!std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
+
 void test() {
-  auto v = View{} | std::views::elements<1>;
+  auto v        = View{} | std::views::elements<1>;
+  auto common_v = CommonView{} | std::views::elements<1>;
 
   // [range.elements.view]
 
@@ -45,6 +56,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).end();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  common_v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(common_v).end();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   v.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}


From libcxx-commits at lists.llvm.org  Sat Jul  4 13:36:16 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 13:36:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
 `elements_view` (PR #206589)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206589@github.com>
Message-ID: <6a496ec0.e9d484b9.11ad58.e107@mx.google.com>


================
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Test the libc++ extension that std::ranges::elements_view and std::views::elements are marked as [[nodiscard]].
+
+#include <functional>
+#include <map>
+#include <ranges>
+#include <utility>
+
+struct View : std::ranges::view_interface<View> {
+  std::tuple<int, int>* begin();
+  const std::tuple<int, int>* begin() const;
+  volatile std::tuple<int, int>* end();
+  const volatile std::tuple<int, int>* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+void test() {
+  auto v = View{} | std::views::elements<1>;
+
+  // [range.elements.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
----------------
lknknm wrote:

I think it should be fine now, but I'm not quite sure if I'm correctly testing the `end() const` for common range in:
```cpp
  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
    requires common_range<const _View>
  {
    return __iterator</*_Const=*/true>{ranges::end(__base_)};
  }
```
Could you please double check? Thanks!

https://github.com/llvm/llvm-project/pull/206589

From libcxx-commits at lists.llvm.org  Sat Jul  4 13:38:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 13:38:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][math][c++17] P0226R1 -
 Mathematical Special Functions: infra + `std::assoc_laguerre` (PR #205649)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205649@github.com>
Message-ID: <6a496f57.faf16b37.1e0c59.8a7c@mx.google.com>


https://github.com/PaulXiCao updated https://github.com/llvm/llvm-project/pull/205649

>From 7555447d01b3e6bd5127f4ffbfaeeb223cf9ea81 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 20:52:48 +0200
Subject: [PATCH 01/56] link against Boost.Math

---
 libcxx/CMakeLists.txt     | 8 ++++++++
 libcxx/src/CMakeLists.txt | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..845042f15495f 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -899,6 +899,14 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+
+# Header-only Boost.Math (INTERFACE target `boost_math`). Lives outside the
+# libcxx source tree, so a binary dir must be supplied.
+add_subdirectory(
+  ${LLVM_THIRD_PARTY_DIR}/boost-math
+  ${LIBCXX_BINARY_DIR}/third-party/boost-math
+  EXCLUDE_FROM_ALL)
+
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index de7817ad69f26..c882446596a3e 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -176,6 +176,7 @@ add_library(cxx_shared SHARED ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(cxx_shared PUBLIC cxx-headers runtimes-libc-shared
                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                  PRIVATE boost_math
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_shared
   PROPERTIES
@@ -263,6 +264,7 @@ add_library(cxx_static STATIC ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(cxx_static PUBLIC cxx-headers runtimes-libc-static
                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                  PRIVATE boost_math
                                   PRIVATE libcxx-abi-static
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_static

>From fe89c404516cfea93ab67939b48a15c74213cd59 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 20:53:37 +0200
Subject: [PATCH 02/56] add empty src/mathematical_special_functions.cpp

---
 libcxx/src/CMakeLists.txt                     |  1 +
 libcxx/src/mathematical_special_functions.cpp | 17 +++++++++++++++++
 2 files changed, 18 insertions(+)
 create mode 100644 libcxx/src/mathematical_special_functions.cpp

diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index c882446596a3e..d2168decbfdba 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -29,6 +29,7 @@ set(LIBCXX_SOURCES
   include/ryu/ryu.h
   include/to_chars_floating_point.h
   include/from_chars_floating_point.h
+  mathematical_special_functions.cpp
   memory.cpp
   memory_resource.cpp
   new_handler.cpp
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
new file mode 100644
index 0000000000000..db07d61f89319
--- /dev/null
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -0,0 +1,17 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+
+namespace {}
+
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+_LIBCPP_END_NAMESPACE_STD

>From 7fbc20f4bea5a80fdebfb22eadee50cb90698b46 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 21:06:04 +0200
Subject: [PATCH 03/56] add simple test for std::assoc_laguerref (fails atm)

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
new file mode 100644
index 0000000000000..1a12c8f81a16c
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Missing powl
+// XFAIL: LLVM-LIBC-FIXME
+
+// REQUIRES: std-at-least-c++17
+
+// <cmath>
+//
+// [sf.cmath.assoc.laguerre], associated Laguerre polynomials
+// floating-point-type assoc_laguerre(unsigned n, unsigned m, floating-point-type x);
+// float               assoc_laguerref(unsigned n, unsigned m, float x);
+// long double         assoc_laguerrel(unsigned n, unsigned m, long double x);
+
+#include <cmath>
+
+int main(int, char**) {
+  std::assoc_laguerref(0, 0, 0.0f);
+
+  return 0;
+}

>From c7e11e92b9c70fe1f319a8f959ce1c11cbbfa9cc Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 21:52:09 +0200
Subject: [PATCH 04/56] handling MATH_ERRNO, MATH_ERREXCEPT via __sf_result

---
 libcxx/include/__math/special_functions.h | 25 +++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 6b217852e2f8c..86b3ac9cb665e 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -15,7 +15,10 @@
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_integral.h>
+#include <cerrno>
+#include <cfenv>
 #include <limits>
+#include <math.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -25,6 +28,28 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 17
 
+namespace __math {
+template <class _Tp>
+struct __sf_result {
+  bool __domain_error;
+  _Tp __ret;
+
+  operator _Tp() const {
+#  if math_errhandling & MATH_ERRNO
+    if (__domain_error)
+      errno = EDOM;
+#  endif
+
+#  if math_errhandling & MATH_ERREXCEPT
+    if (__domain_error)
+      feraiseexcept(FE_INVALID);
+#  endif
+
+    return __ret;
+  }
+};
+} // namespace __math
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).

>From 23081681878b007cb50f1ed115ec316fa956b4cf Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 22:00:10 +0200
Subject: [PATCH 05/56] impl assoc_laguerref

---
 libcxx/include/__math/special_functions.h     |  9 +++++
 libcxx/src/mathematical_special_functions.cpp | 37 ++++++++++++++++++-
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 86b3ac9cb665e..d0d6e56507e85 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -25,6 +25,7 @@
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 #if _LIBCPP_STD_VER >= 17
 
@@ -48,8 +49,15 @@ struct __sf_result {
     return __ret;
   }
 };
+
+_LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
+
 } // namespace __math
 
+inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).
@@ -104,6 +112,7 @@ _LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, _Integer __x) {
 
 #endif // _LIBCPP_STD_VER >= 17
 
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index db07d61f89319..e18e9c5f2797c 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -7,11 +7,46 @@
 //===----------------------------------------------------------------------===//
 
 #include <__config>
+#include <boost/math/special_functions.hpp>
+#include <cmath>
+#include <optional>
+#include <type_traits>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-namespace {}
+namespace __math {
+namespace {
+template <class _Ret>
+optional<_Ret> __check_nan() {
+  return nullopt;
+}
+
+template <class _Ret, class _Arg, class... _Args>
+optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
+  if constexpr (is_floating_point_v<_Arg>)
+    if (isnan(__arg))
+      return __arg;
+  return __check_nan<_Ret>(__args...);
+}
+
+template <class Func, class... _Args, class _Ret = std::invoke_result_t<Func, _Args...>>
+__sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
+  if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
+    return {.__domain_error = false, .__ret = *__maybe_nan};
+
+  try {
+    return {.__domain_error = false, .__ret = f(__args...)};
+  } catch (...) {
+    return {.__domain_error = true, .__ret = numeric_limits<_Ret>::quiet_NaN()};
+  }
+}
+} // namespace
+
+__sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
+  return invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+} // namespace __math
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From adb174a0e9d29b172b17bc9e0d781db0dff15d5c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 22:34:12 +0200
Subject: [PATCH 06/56] test assoc_laguerref

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 1a12c8f81a16c..fe92daa501c6f 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -6,22 +6,18 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing powl
-// XFAIL: LLVM-LIBC-FIXME
-
 // REQUIRES: std-at-least-c++17
 
 // <cmath>
 //
 // [sf.cmath.assoc.laguerre], associated Laguerre polynomials
-// floating-point-type assoc_laguerre(unsigned n, unsigned m, floating-point-type x);
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
-// long double         assoc_laguerrel(unsigned n, unsigned m, long double x);
 
+#include <cassert>
 #include <cmath>
 
-int main(int, char**) {
-  std::assoc_laguerref(0, 0, 0.0f);
-
-  return 0;
+int main() {
+  // Single value tested against known solution.
+  // Note, underlying Boost.Math is itself well-tested.
+  assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
 }

>From b7bc1fb6247235ce62066d70a30c13608a8672fc Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 18:26:00 +0200
Subject: [PATCH 07/56] test: common.h

---
 .../std/numerics/c.math/sf.cmath/common.h     | 65 +++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/sf.cmath/common.h

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
new file mode 100644
index 0000000000000..867e6af6f6914
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_SF_CMATH_COMMON_H
+#define TEST_SF_CMATH_COMMON_H
+
+#include <cassert>
+#include <cerrno>
+#include <cmath>
+#include <math.h>
+#include <cfenv>
+
+// std::type_identity is C++20 (we need to support C++17 here)
+template <class T>
+struct type_identity {
+  typedef T type;
+};
+template <class T>
+using type_identity_t = typename type_identity<T>::type;
+
+template <class T>
+bool between(type_identity_t<T> lower, T value, type_identity_t<T> upper) {
+  return lower < value && value < upper;
+}
+
+template <class Func>
+void check_no_domain_error(Func f) {
+#if math_errhandling & MATH_ERRNO
+  errno = EACCES;
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  std::feclearexcept(FE_INVALID);
+#endif
+  f();
+#if math_errhandling & MATH_ERRNO
+  assert(errno == EACCES);
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  assert(!std::fetestexcept(FE_INVALID));
+#endif
+}
+
+template <class Func>
+void check_domain_error(Func f) {
+#if math_errhandling & MATH_ERRNO
+  errno = EACCES;
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  std::feclearexcept(FE_INVALID);
+#endif
+  f();
+#if math_errhandling & MATH_ERRNO
+  assert(errno == EDOM);
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  assert(std::fetestexcept(FE_INVALID));
+#endif
+}
+
+#endif // TEST_SF_CMATH_COMMON_H

>From a47f9e902e5c427c84115f4af119eedf8778e182 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 18:26:59 +0200
Subject: [PATCH 08/56] more testing of assoc_laguerref: values, types, domain
 error

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 36 +++++++++++++++++--
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index fe92daa501c6f..eb747efb4c1c6 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,10 +14,40 @@
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
 
 #include <cassert>
+#include <cerrno>
+#include <cfenv>
 #include <cmath>
 
+#include "common.h"
+#include "type_algorithms.h"
+
+struct TestFloatingPoint {
+  template <class T>
+  void operator()() const {
+    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
+
+    assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
+
+    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
+
+    assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
+
+    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
+
+    check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
+  }
+};
+
+struct TestIntegral {
+  template <class T>
+  void operator()() const {
+    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
+
+    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
+  }
+};
+
 int main() {
-  // Single value tested against known solution.
-  // Note, underlying Boost.Math is itself well-tested.
-  assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
+  types::for_each(types::floating_point_types{}, TestFloatingPoint{});
+  types::for_each(types::integral_types{}, TestIntegral{});
 }

>From f8a3b1d891405ff6ab04bfc7a7d615ca2cd54a56 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:15:40 +0200
Subject: [PATCH 09/56] silence boost_math warnings by marking it as `SYSTEM`
 lib

---
 third-party/boost-math/CMakeLists.txt |  2 +-
 third-party/update_boost_math.sh      | 13 +++++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/third-party/boost-math/CMakeLists.txt b/third-party/boost-math/CMakeLists.txt
index a1f6011d95e55..0de1fa2ec01ed 100644
--- a/third-party/boost-math/CMakeLists.txt
+++ b/third-party/boost-math/CMakeLists.txt
@@ -11,7 +11,7 @@ add_library(boost_math INTERFACE)
 
 add_library(Boost::math ALIAS boost_math)
 
-target_include_directories(boost_math INTERFACE include)
+target_include_directories(boost_math SYSTEM INTERFACE include)
 if(NOT CMAKE_VERSION VERSION_LESS "3.19")
   file(GLOB_RECURSE headers include/*.hpp)
   target_sources(boost_math PRIVATE ${headers})
diff --git a/third-party/update_boost_math.sh b/third-party/update_boost_math.sh
index 96cd73ed8c304..27d8816b7cb12 100755
--- a/third-party/update_boost_math.sh
+++ b/third-party/update_boost_math.sh
@@ -27,3 +27,16 @@ echo "****************************************"
 echo "Subsetting Boost.Math ${VERSION}"
 echo "****************************************"
 rm -rf ${SCRIPT_DIR}/boost-math/{.circleci,.drone,.github,build,config,doc,example,meta,reporting,src,test,tools}
+
+echo "****************************************"
+echo "Patching Boost.Math ${VERSION} for libc++"
+echo "****************************************"
+# Mark boost_math include dir as SYSTEM so libc++ consumers don't surface warnings from vendored boost-math code (e.g.
+# -Wdeprecated-redundant-constexpr-static-def). Upstream keeps these for pre-C++17 compat.
+sed -i 's|target_include_directories(boost_math INTERFACE include)|target_include_directories(boost_math SYSTEM INTERFACE include)|' \
+    ${SCRIPT_DIR}/boost-math/CMakeLists.txt
+
+# Verify the patch landed -- fail loudly if upstream renamed the target.
+grep -q 'target_include_directories(boost_math SYSTEM INTERFACE include)' \
+    ${SCRIPT_DIR}/boost-math/CMakeLists.txt \
+    || { echo "ERROR: SYSTEM include patch failed -- upstream CMakeLists.txt structure changed"; exit 1; }

>From 17c19c5c87c5adf5618686cbf1cac445d4bfbfda Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:24:39 +0200
Subject: [PATCH 10/56] add nodiscard test for assoc_laguerref

---
 .../c.math/sf.cmath/nodiscard.verify.cpp      | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7928ebf50681a
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// Check that functions are marked [[nodiscard]]
+
+#include <cmath>
+
+void test() {
+  // clang-format off
+  // assoc_laguerre
+  std::assoc_laguerref(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}

>From 0a7cf75eca52eab4c5d98b4f323f05fbf0187a9c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:27:41 +0200
Subject: [PATCH 11/56] add nodiscard to assoc_laguerref

---
 libcxx/include/__math/special_functions.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index d0d6e56507e85..dee70bec45cd8 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -54,7 +54,8 @@ _LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsi
 
 } // namespace __math
 
-inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 

>From ddfb2c1563bf465cd912f226abe221488a67b0df Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:50:00 +0200
Subject: [PATCH 12/56] insert grouping for assoc_laguerre functions

---
 libcxx/include/__math/special_functions.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index dee70bec45cd8..3100860ec8c70 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -49,9 +49,11 @@ struct __sf_result {
     return __ret;
   }
 };
+} // namespace __math
 
+// assoc_laguerre
+namespace __math {
 _LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
-
 } // namespace __math
 
 [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float

>From 39b98428ea2cf592234512ac3762348f3594087c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 00:09:27 +0200
Subject: [PATCH 13/56] insert grouping for hermite functions

---
 libcxx/include/__math/special_functions.h | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 3100860ec8c70..b8ec0785a3b2b 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -26,7 +26,6 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
-
 #if _LIBCPP_STD_VER >= 17
 
 namespace __math {
@@ -61,6 +60,14 @@ assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+#endif // _LIBCPP_STD_VER >= 17
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+_LIBCPP_END_NAMESPACE_STD
+
+// hermite
+_LIBCPP_BEGIN_NAMESPACE_STD
+#if _LIBCPP_STD_VER >= 17
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).
@@ -114,8 +121,5 @@ _LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, _Integer __x) {
 }
 
 #endif // _LIBCPP_STD_VER >= 17
-
-_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
-
 #endif // _LIBCPP___MATH_SPECIAL_FUNCTIONS_H

>From 145ce5f04732b8f5800f9013411d11ffe97c215f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 00:10:16 +0200
Subject: [PATCH 14/56] add missing ABI annotation

---
 libcxx/include/__math/special_functions.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index b8ec0785a3b2b..e11dc97fd46d4 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -34,7 +34,7 @@ struct __sf_result {
   bool __domain_error;
   _Tp __ret;
 
-  operator _Tp() const {
+  _LIBCPP_HIDE_FROM_ABI operator _Tp() const {
 #  if math_errhandling & MATH_ERRNO
     if (__domain_error)
       errno = EDOM;

>From 2328d31c7918995c6895836c69e1371250675427 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:16:15 +0200
Subject: [PATCH 15/56] TU: guard by C++ >= 17

---
 libcxx/src/mathematical_special_functions.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index e18e9c5f2797c..102acb37e50bc 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -14,6 +14,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+#if _LIBCPP_STD_VER >= 17
 
 namespace __math {
 namespace {
@@ -48,5 +49,6 @@ __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __
 }
 } // namespace __math
 
+#endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From 7b60948e6eb6cb5b9ccea95e76bb02865d314871 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:18:02 +0200
Subject: [PATCH 16/56] TU: qualify std:: functions

---
 libcxx/src/mathematical_special_functions.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 102acb37e50bc..03a90cd3bd982 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -19,14 +19,14 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 namespace __math {
 namespace {
 template <class _Ret>
-optional<_Ret> __check_nan() {
-  return nullopt;
+std::optional<_Ret> __check_nan() {
+  return std::nullopt;
 }
 
 template <class _Ret, class _Arg, class... _Args>
-optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
-  if constexpr (is_floating_point_v<_Arg>)
-    if (isnan(__arg))
+std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
+  if constexpr (std::is_floating_point_v<_Arg>)
+    if (std::isnan(__arg))
       return __arg;
   return __check_nan<_Ret>(__args...);
 }
@@ -39,7 +39,7 @@ __sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
   try {
     return {.__domain_error = false, .__ret = f(__args...)};
   } catch (...) {
-    return {.__domain_error = true, .__ret = numeric_limits<_Ret>::quiet_NaN()};
+    return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
   }
 }
 } // namespace

>From a0f3426cd940ef45a441b4e610efaebf8f3e6655 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:18:46 +0200
Subject: [PATCH 17/56] TU: add missing __uglify

---
 libcxx/src/mathematical_special_functions.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 03a90cd3bd982..fdca91bf5a8b4 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -31,13 +31,13 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
   return __check_nan<_Ret>(__args...);
 }
 
-template <class Func, class... _Args, class _Ret = std::invoke_result_t<Func, _Args...>>
-__sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
+template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
+__sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return {.__domain_error = false, .__ret = *__maybe_nan};
 
   try {
-    return {.__domain_error = false, .__ret = f(__args...)};
+    return {.__domain_error = false, .__ret = __f(__args...)};
   } catch (...) {
     return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
   }
@@ -45,7 +45,7 @@ __sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
 } // namespace
 
 __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+  return __invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 } // namespace __math
 

>From 745c9d9aa7dfc246593851286f70e6494148c0d4 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:07 +0200
Subject: [PATCH 18/56] TU: add missing headers

---
 libcxx/src/mathematical_special_functions.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index fdca91bf5a8b4..9a106c763c776 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -7,8 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include <__config>
+#include <__math/special_functions.h>
 #include <boost/math/special_functions.hpp>
 #include <cmath>
+#include <limits>
 #include <optional>
 #include <type_traits>
 

>From 372f1054b2aa081808ed07b6182a6b8f2e4a21e3 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:42 +0200
Subject: [PATCH 19/56] TU: remove C++20 designated initializers

---
 libcxx/src/mathematical_special_functions.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 9a106c763c776..23d9216d777d2 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -36,12 +36,12 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
 __sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
-    return {.__domain_error = false, .__ret = *__maybe_nan};
+    return {false, *__maybe_nan};
 
   try {
-    return {.__domain_error = false, .__ret = __f(__args...)};
+    return {false, __f(__args...)};
   } catch (...) {
-    return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
+    return {true, std::numeric_limits<_Ret>::quiet_NaN()};
   }
 }
 } // namespace

>From 4e32fcc443bdd93c388e69aad76c0b08d7335d9d Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:51 +0200
Subject: [PATCH 20/56] TU: cleanup

---
 libcxx/src/mathematical_special_functions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 23d9216d777d2..cf88cbef159fa 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -47,7 +47,7 @@ __sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
 } // namespace
 
 __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return __invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 } // namespace __math
 

>From 299419630480cc7ba0fd7c026a14c8797b461aa0 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 11:21:18 +0200
Subject: [PATCH 21/56] break cyclic header deps: move errno logic into TU

---
 libcxx/include/__math/special_functions.h     | 34 +------------------
 libcxx/src/mathematical_special_functions.cpp | 21 ++++++++----
 2 files changed, 15 insertions(+), 40 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index e11dc97fd46d4..89c75cfc04585 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -15,10 +15,7 @@
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_integral.h>
-#include <cerrno>
-#include <cfenv>
 #include <limits>
-#include <math.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -28,37 +25,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
-namespace __math {
-template <class _Tp>
-struct __sf_result {
-  bool __domain_error;
-  _Tp __ret;
-
-  _LIBCPP_HIDE_FROM_ABI operator _Tp() const {
-#  if math_errhandling & MATH_ERRNO
-    if (__domain_error)
-      errno = EDOM;
-#  endif
-
-#  if math_errhandling & MATH_ERREXCEPT
-    if (__domain_error)
-      feraiseexcept(FE_INVALID);
-#  endif
-
-    return __ret;
-  }
-};
-} // namespace __math
-
 // assoc_laguerre
-namespace __math {
-_LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
-} // namespace __math
-
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float
-assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return __math::__assoc_laguerre(__n, __m, __x);
-}
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index cf88cbef159fa..daab723b8f2e3 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -9,8 +9,11 @@
 #include <__config>
 #include <__math/special_functions.h>
 #include <boost/math/special_functions.hpp>
+#include <cerrno>
+#include <cfenv>
 #include <cmath>
 #include <limits>
+#include <math.h>
 #include <optional>
 #include <type_traits>
 
@@ -18,7 +21,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
-namespace __math {
 namespace {
 template <class _Ret>
 std::optional<_Ret> __check_nan() {
@@ -34,22 +36,27 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 }
 
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
-__sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
+_Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
-    return {false, *__maybe_nan};
+    return *__maybe_nan;
 
   try {
-    return {false, __f(__args...)};
+    return __f(__args...);
   } catch (...) {
-    return {true, std::numeric_limits<_Ret>::quiet_NaN()};
+#  if math_errhandling & MATH_ERRNO
+    errno = EDOM;
+#  endif
+#  if math_errhandling & MATH_ERREXCEPT
+    feraiseexcept(FE_INVALID);
+#  endif
+    return std::numeric_limits<_Ret>::quiet_NaN();
   }
 }
 } // namespace
 
-__sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
+float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
-} // namespace __math
 
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS

>From f2580f1f2e853e83db75462c95558cbfeef6508c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 11:29:35 +0200
Subject: [PATCH 22/56] TU: cleanups

---
 libcxx/src/mathematical_special_functions.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index daab723b8f2e3..420e0a1e71cb0 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -13,7 +13,6 @@
 #include <cfenv>
 #include <cmath>
 #include <limits>
-#include <math.h>
 #include <optional>
 #include <type_traits>
 
@@ -47,7 +46,7 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
     errno = EDOM;
 #  endif
 #  if math_errhandling & MATH_ERREXCEPT
-    feraiseexcept(FE_INVALID);
+    std::feraiseexcept(FE_INVALID);
 #  endif
     return std::numeric_limits<_Ret>::quiet_NaN();
   }

>From 80f81ae52610bc7accc38605524fdbf872004e4f Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 16:31:15 +0200
Subject: [PATCH 23/56] use boost errno_on_error policy instead of exceptions

---
 libcxx/src/mathematical_special_functions.cpp | 44 +++++++++++++++----
 1 file changed, 35 insertions(+), 9 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 420e0a1e71cb0..f8fea7ff1b70e 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -8,19 +8,31 @@
 
 #include <__config>
 #include <__math/special_functions.h>
-#include <boost/math/special_functions.hpp>
 #include <cerrno>
 #include <cfenv>
 #include <cmath>
-#include <limits>
 #include <optional>
 #include <type_traits>
 
+#define BOOST_MATH_NO_EXCEPTIONS
+#include <boost/math/special_functions.hpp>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
 namespace {
+// Error policy for all Boost.Math calls: report domain/pole/overflow/evaluation
+// errors via errno (errno_on_error) instead of throwing. Boost sets errno to
+// EDOM (domain/pole/evaluation) or ERANGE (overflow) and returns NaN/inf. The
+// remaining categories (underflow/denorm/indeterminate) default to ignore.
+namespace __bmp = boost::math::policies;
+using __policy =
+    __bmp::policy<__bmp::domain_error<__bmp::errno_on_error>,
+                  __bmp::pole_error<__bmp::errno_on_error>,
+                  __bmp::overflow_error<__bmp::errno_on_error>,
+                  __bmp::evaluation_error<__bmp::errno_on_error>>;
+
 template <class _Ret>
 std::optional<_Ret> __check_nan() {
   return std::nullopt;
@@ -34,22 +46,36 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
   return __check_nan<_Ret>(__args...);
 }
 
+// Shared back-end for the C++17 mathematical special functions ([sf.cmath]).
+// Boost.Math is the compute kernel; this wrapper enforces the standard's
+// error-reporting rules ([sf.cmath.general]):
+//   1. NaN argument -> return NaN, do NOT report a domain error (the
+//      __check_nan pre-filter below).
+//   2. domain/range error -> report per <cmath> math_errhandling: errno = EDOM
+//      (domain/pole) or ERANGE (overflow), done by Boost's errno_on_error
+//      policy; and, when MATH_ERREXCEPT is set, raise the matching <cfenv>
+//      exception (done here, since Boost never touches <cfenv>).
+// Promotion: Boost's default promote_float=true computes float inputs in double
+// and rounds once -- more accurate and overflow-resistant, matching the
+// existing std::hermite(float) approach. We keep it.
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
 _Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return *__maybe_nan;
 
-  try {
-    return __f(__args...);
-  } catch (...) {
-#  if math_errhandling & MATH_ERRNO
-    errno = EDOM;
+#  if math_errhandling & MATH_ERREXCEPT
+  errno = 0;
 #  endif
+  _Ret __ret = __f(__args..., __policy{});
+  // Boost set errno via the policy; mirror it onto <cfenv>:
+  // EDOM (domain/pole) -> FE_INVALID, ERANGE (overflow) -> FE_OVERFLOW.
 #  if math_errhandling & MATH_ERREXCEPT
+  if (errno == EDOM)
     std::feraiseexcept(FE_INVALID);
+  else if (errno == ERANGE)
+    std::feraiseexcept(FE_OVERFLOW);
 #  endif
-    return std::numeric_limits<_Ret>::quiet_NaN();
-  }
+  return __ret;
 }
 } // namespace
 

>From 2c13b0df2188c488e3f2fe235ba22569419e0e5b Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 16:44:35 +0200
Subject: [PATCH 24/56] TU: remove fenv support.

---
 libcxx/src/mathematical_special_functions.cpp | 25 +++++--------------
 1 file changed, 6 insertions(+), 19 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index f8fea7ff1b70e..3c0af96fb0c19 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -8,8 +8,6 @@
 
 #include <__config>
 #include <__math/special_functions.h>
-#include <cerrno>
-#include <cfenv>
 #include <cmath>
 #include <optional>
 #include <type_traits>
@@ -51,10 +49,11 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 // error-reporting rules ([sf.cmath.general]):
 //   1. NaN argument -> return NaN, do NOT report a domain error (the
 //      __check_nan pre-filter below).
-//   2. domain/range error -> report per <cmath> math_errhandling: errno = EDOM
-//      (domain/pole) or ERANGE (overflow), done by Boost's errno_on_error
-//      policy; and, when MATH_ERREXCEPT is set, raise the matching <cfenv>
-//      exception (done here, since Boost never touches <cfenv>).
+//   2. domain/range error -> reported via errno only: Boost's errno_on_error
+//      policy sets errno = EDOM (domain/pole/evaluation) or ERANGE (overflow).
+//      The <cfenv> floating-point-exception side of math_errhandling
+//      (MATH_ERREXCEPT) is intentionally not mirrored, matching the shipped
+//      std::hermite and libstdc++'s special-function implementations.
 // Promotion: Boost's default promote_float=true computes float inputs in double
 // and rounds once -- more accurate and overflow-resistant, matching the
 // existing std::hermite(float) approach. We keep it.
@@ -63,19 +62,7 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return *__maybe_nan;
 
-#  if math_errhandling & MATH_ERREXCEPT
-  errno = 0;
-#  endif
-  _Ret __ret = __f(__args..., __policy{});
-  // Boost set errno via the policy; mirror it onto <cfenv>:
-  // EDOM (domain/pole) -> FE_INVALID, ERANGE (overflow) -> FE_OVERFLOW.
-#  if math_errhandling & MATH_ERREXCEPT
-  if (errno == EDOM)
-    std::feraiseexcept(FE_INVALID);
-  else if (errno == ERANGE)
-    std::feraiseexcept(FE_OVERFLOW);
-#  endif
-  return __ret;
+  return __f(__args..., __policy{});
 }
 } // namespace
 

>From 580076b2ffe777b1f00d844091f88ebd03820402 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:24:36 +0200
Subject: [PATCH 25/56] test: add more tests; fix header includes

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index eb747efb4c1c6..079751321f3b9 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,11 +14,11 @@
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
 
 #include <cassert>
-#include <cerrno>
-#include <cfenv>
 #include <cmath>
+#include <limits>
 
 #include "common.h"
+#include "test_macros.h"
 #include "type_algorithms.h"
 
 struct TestFloatingPoint {
@@ -32,9 +32,19 @@ struct TestFloatingPoint {
 
     assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
 
+    // m == 0 reduces to the ordinary Laguerre polynomial: L_2(2) = (4 - 8 + 2) / 2 = -1.
+    assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
+
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
+    // noexcept is a libc++ extension here ([sf.cmath] does not mandate it), so use the libc++-only assertion.
+    LIBCPP_ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, T(0.)));
+
     check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
+
+    // The associated Laguerre polynomials are defined for all real x: a negative
+    // argument is in-domain and must not report a domain error. L_1^0(-1) = 1 - x = 2.
+    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, -1.f), 2.01f)); });
   }
 };
 

>From 2a6dfd4ae296d0f89e1ea5bc953d7d5f8f536ca9 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:53:16 +0200
Subject: [PATCH 26/56] test: add noexcept.compile.pass test

---
 .../c.math/sf.cmath/noexcept.compile.pass.cpp | 20 +++++++++++++++++++
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   |  3 ---
 2 files changed, 20 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..9994e6a018f8d
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// Check that functions are marked noexcept
+
+#include <cmath>
+
+#include "test_macros.h"
+
+void test() {
+  // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
+}
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 079751321f3b9..068ca81e04b83 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -37,9 +37,6 @@ struct TestFloatingPoint {
 
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
-    // noexcept is a libc++ extension here ([sf.cmath] does not mandate it), so use the libc++-only assertion.
-    LIBCPP_ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, T(0.)));
-
     check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
 
     // The associated Laguerre polynomials are defined for all real x: a negative

>From 7b8204c6a252f2971f288212096aee6641c14d82 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:55:16 +0200
Subject: [PATCH 27/56] test: cleanup

---
 .../test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 7928ebf50681a..a4c471e4b3168 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -15,6 +15,6 @@
 void test() {
   // clang-format off
   // assoc_laguerre
-  std::assoc_laguerref(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   // clang-format on
 }

>From 82d264a4bc5a0a1f6d4c932b97b4a74c11957fac Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 28 Jun 2026 23:37:36 +0200
Subject: [PATCH 28/56] test: remove fenv support

---
 libcxx/test/std/numerics/c.math/sf.cmath/common.h | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
index 867e6af6f6914..723422bf7038d 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/common.h
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -12,8 +12,6 @@
 #include <cassert>
 #include <cerrno>
 #include <cmath>
-#include <math.h>
-#include <cfenv>
 
 // std::type_identity is C++20 (we need to support C++17 here)
 template <class T>
@@ -32,34 +30,22 @@ template <class Func>
 void check_no_domain_error(Func f) {
 #if math_errhandling & MATH_ERRNO
   errno = EACCES;
-#endif
-#if math_errhandling & MATH_ERREXCEPT
-  std::feclearexcept(FE_INVALID);
 #endif
   f();
 #if math_errhandling & MATH_ERRNO
   assert(errno == EACCES);
 #endif
-#if math_errhandling & MATH_ERREXCEPT
-  assert(!std::fetestexcept(FE_INVALID));
-#endif
 }
 
 template <class Func>
 void check_domain_error(Func f) {
 #if math_errhandling & MATH_ERRNO
   errno = EACCES;
-#endif
-#if math_errhandling & MATH_ERREXCEPT
-  std::feclearexcept(FE_INVALID);
 #endif
   f();
 #if math_errhandling & MATH_ERRNO
   assert(errno == EDOM);
 #endif
-#if math_errhandling & MATH_ERREXCEPT
-  assert(std::fetestexcept(FE_INVALID));
-#endif
 }
 
 #endif // TEST_SF_CMATH_COMMON_H

>From 6b65307f26de7474c6b3f6789c8bd49e9b9787d7 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 28 Jun 2026 23:37:54 +0200
Subject: [PATCH 29/56] test: cleanups + more NaN tests

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 28 ++++++++++---------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 068ca81e04b83..fffbb72557a5a 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -18,30 +18,32 @@
 #include <limits>
 
 #include "common.h"
-#include "test_macros.h"
 #include "type_algorithms.h"
 
 struct TestFloatingPoint {
   template <class T>
   void operator()() const {
+    // sample value testing
     assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-
     assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
-
-    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
-
-    assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
-
-    // m == 0 reduces to the ordinary Laguerre polynomial: L_2(2) = (4 - 8 + 2) / 2 = -1.
     assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
+    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
+    assert(between(60.124f, std::assoc_laguerref(2, 10, T(0.5)), 60.126f));
 
+    // return type: float
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
-    check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
-
-    // The associated Laguerre polynomials are defined for all real x: a negative
-    // argument is in-domain and must not report a domain error. L_1^0(-1) = 1 - x = 2.
-    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, -1.f), 2.01f)); });
+    // NaN input -> NaN output (w/o domain error)
+    auto check_nan = [](T nan) {
+      check_no_domain_error([nan] { assert(std::isnan(std::assoc_laguerref(0, 0, nan))); });
+    };
+    if (std::numeric_limits<T>::has_quiet_NaN)
+      check_nan(std::numeric_limits<T>::quiet_NaN());
+    if (std::numeric_limits<T>::has_signaling_NaN)
+      check_nan(std::numeric_limits<T>::signaling_NaN());
+
+    // negative x: no domain error
+    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, T(-1)), 2.01f)); });
   }
 };
 

>From ddd4eef0a09edb02e8e486fb587bf5e64d538b65 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 00:00:33 +0200
Subject: [PATCH 30/56] impl assoc_laguerrel

---
 libcxx/include/__math/special_functions.h     | 1 +
 libcxx/src/mathematical_special_functions.cpp | 5 +++++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 89c75cfc04585..0cfd484955b15 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -27,6 +27,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 // assoc_laguerre
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept;
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 3c0af96fb0c19..980af019748db 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -66,10 +66,15 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
 }
 } // namespace
 
+// assoc_laguerre
 float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept {
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From a35b978e0601bea7a9accf072c175a4c8b182c97 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 00:23:48 +0200
Subject: [PATCH 31/56] test: assoc_laguerrel

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 78 ++++++++-----------
 1 file changed, 34 insertions(+), 44 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index fffbb72557a5a..0a9abaf2f4f24 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -9,54 +9,44 @@
 // REQUIRES: std-at-least-c++17
 
 // <cmath>
-//
-// [sf.cmath.assoc.laguerre], associated Laguerre polynomials
-// float               assoc_laguerref(unsigned n, unsigned m, float x);
+
+// float       assoc_laguerref(unsigned n, unsigned m, float x);
+// long double assoc_laguerrel(unsigned n, unsigned m, long double x);
 
 #include <cassert>
 #include <cmath>
 #include <limits>
 
 #include "common.h"
-#include "type_algorithms.h"
-
-struct TestFloatingPoint {
-  template <class T>
-  void operator()() const {
-    // sample value testing
-    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-    assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
-    assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
-    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
-    assert(between(60.124f, std::assoc_laguerref(2, 10, T(0.5)), 60.126f));
-
-    // return type: float
-    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
-
-    // NaN input -> NaN output (w/o domain error)
-    auto check_nan = [](T nan) {
-      check_no_domain_error([nan] { assert(std::isnan(std::assoc_laguerref(0, 0, nan))); });
-    };
-    if (std::numeric_limits<T>::has_quiet_NaN)
-      check_nan(std::numeric_limits<T>::quiet_NaN());
-    if (std::numeric_limits<T>::has_signaling_NaN)
-      check_nan(std::numeric_limits<T>::signaling_NaN());
-
-    // negative x: no domain error
-    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, T(-1)), 2.01f)); });
-  }
-};
-
-struct TestIntegral {
-  template <class T>
-  void operator()() const {
-    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-
-    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
-  }
-};
-
-int main() {
-  types::for_each(types::floating_point_types{}, TestFloatingPoint{});
-  types::for_each(types::integral_types{}, TestIntegral{});
+
+// Each suffixed overload is fixed to a single floating-point type, so it is
+// tested directly with that type. The unsuffixed std::assoc_laguerre overloads
+// (and the integer-argument overload) are not implemented yet.
+template <class T, class Func>
+void test(Func assoc_laguerre) {
+  // sample values
+  assert(between(0.99, assoc_laguerre(0, 0, T(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, T(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, T(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, T(2)), 0.01));
+  assert(between(60.124, assoc_laguerre(2, 10, T(0.5)), 60.126));
+
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, T(0))), T>);
+
+  // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
+  auto test_nan = [&](T nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
+  if (std::numeric_limits<T>::has_quiet_NaN)
+    test_nan(std::numeric_limits<T>::quiet_NaN());
+  if (std::numeric_limits<T>::has_signaling_NaN)
+    test_nan(std::numeric_limits<T>::signaling_NaN());
+
+  // A negative argument is in the domain (no domain error).
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, T(-1)), 2.01)); });
 }
+
+int main(int, char**) {
+  test<float>([](unsigned __n, unsigned __m, float __x) { return std::assoc_laguerref(__n, __m, __x); });
+  test<long double>([](unsigned __n, unsigned __m, long double __x) { return std::assoc_laguerrel(__n, __m, __x); });
+
+  return 0;
+}
\ No newline at end of file

>From 96e8004c4a6f41343f1257898d04efbbd01f6457 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 21:43:26 +0200
Subject: [PATCH 32/56] re-introduce __math::__assoc_laguerre -> only ABI
 export internal API

---
 libcxx/include/__math/special_functions.h     | 16 ++++++++++++++--
 libcxx/src/mathematical_special_functions.cpp |  7 +++++--
 2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 0cfd484955b15..62ae09594bea3 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
 
 #include <__config>
+#include <__configuration/attributes.h>
 #include <__math/copysign.h>
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
@@ -26,8 +27,19 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
 // assoc_laguerre
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept;
+namespace __math {
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
+} // namespace __math
+
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
+assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 980af019748db..ed8109c7e482c 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -19,6 +19,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
+namespace __math {
 namespace {
 // Error policy for all Boost.Math calls: report domain/pole/overflow/evaluation
 // errors via errno (errno_on_error) instead of throwing. Boost sets errno to
@@ -67,14 +68,16 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
 } // namespace
 
 // assoc_laguerre
-float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+float __assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
-long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept {
+long double __assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+} // namespace __math
+
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From 34d62b1da4598efb45c79363654566b9ca6677aa Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 21:43:57 +0200
Subject: [PATCH 33/56] add exported ABI symbols to abilist

---
 ...-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...nux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...werpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...rpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...nux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...known-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...own-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 4 +++-
 ...n-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 2 ++
 9 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 74283373d1b65..7889109825e10 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1700,6 +1700,8 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 291149608c9c7..3e139e30496a6 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1338,6 +1338,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index da6d608af462c..304817ba0cffb 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -686,6 +686,8 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEyPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock11from_time_tEi', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6ee31613d5320..d5e37635fa511 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -686,6 +686,8 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock11from_time_tEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 92a37db24ac5f..a59b72d695b55 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1690,6 +1690,8 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 7b09d3dd3f75c..abef314dc14f4 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1338,6 +1338,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 725ed669fb561..86bc3dec5bd34 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1350,6 +1350,8 @@
 {'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 160, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 717d4c9bee726..11330e7eaf442 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1349,6 +1349,8 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
@@ -2030,4 +2032,4 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 45db143b4cf4a..460bd21a2fecf 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1320,6 +1320,8 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}

>From 7e1ed83abf9818cb95663ed9077215f102873666 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:10:37 +0200
Subject: [PATCH 34/56] impl std::assoc_laguerre(double)

---
 libcxx/include/__math/special_functions.h     | 6 ++++++
 libcxx/src/mathematical_special_functions.cpp | 4 ++++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 782e441562c00..8fd363919e5cc 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -30,6 +30,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 // assoc_laguerre
 namespace __math {
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI double __assoc_laguerre(unsigned, unsigned, double) noexcept;
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
@@ -37,6 +38,11 @@ namespace __math {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+template <class = int>
+_LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
 assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index ed8109c7e482c..0f6e3a850ad73 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -72,6 +72,10 @@ float __assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+double __assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+
 long double __assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }

>From 8b1f5f6ded44a8cbd009c03310faa645d320bb97 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:20:15 +0200
Subject: [PATCH 35/56] fixup impl std::assoc_laguerre(double)

---
 libcxx/include/__math/special_functions.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 8fd363919e5cc..b48b9ffdb129c 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -39,7 +39,7 @@ namespace __math {
 }
 
 template <class = int>
-_LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 

>From fbbd70784b3e8bc65aa5e3b18870d936ea438e2e Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:43:07 +0200
Subject: [PATCH 36/56] fixup: assoc_laguerre(dbl)

---
 libcxx/include/__math/special_functions.h                   | 3 +--
 .../std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp    | 6 +++---
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index b48b9ffdb129c..20268dd1bd7d6 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -38,8 +38,7 @@ namespace __math {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-template <class = int>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 0a9abaf2f4f24..b9223f6d66a2c 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -19,9 +19,9 @@
 
 #include "common.h"
 
-// Each suffixed overload is fixed to a single floating-point type, so it is
-// tested directly with that type. The unsuffixed std::assoc_laguerre overloads
-// (and the integer-argument overload) are not implemented yet.
+// Each overload is fixed to a single floating-point type, so it is tested
+// directly with that type. The integer-argument overload
+// (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
 template <class T, class Func>
 void test(Func assoc_laguerre) {
   // sample values

>From 7f5e5515ad7c1e551ff1b7cecf57772211f3b376 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:12:00 +0200
Subject: [PATCH 37/56] test: general cleanups

---
 .../std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp    | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index b9223f6d66a2c..d446e017494c4 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -45,8 +45,8 @@ void test(Func assoc_laguerre) {
 }
 
 int main(int, char**) {
-  test<float>([](unsigned __n, unsigned __m, float __x) { return std::assoc_laguerref(__n, __m, __x); });
-  test<long double>([](unsigned __n, unsigned __m, long double __x) { return std::assoc_laguerrel(__n, __m, __x); });
+  test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
   return 0;
-}
\ No newline at end of file
+}

>From 340ed9ca7b9f543fd3ffa8110f29863dc7fcef4f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:14:47 +0200
Subject: [PATCH 38/56] test: std::assoc_laguerre(double)

---
 .../test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp  | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index d446e017494c4..9c2bc2026a348 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -10,6 +10,7 @@
 
 // <cmath>
 
+// double      assoc_laguerre(unsigned n, unsigned m, double x);
 // float       assoc_laguerref(unsigned n, unsigned m, float x);
 // long double assoc_laguerrel(unsigned n, unsigned m, long double x);
 
@@ -45,6 +46,8 @@ void test(Func assoc_laguerre) {
 }
 
 int main(int, char**) {
+  test<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+
   test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
   test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 

>From 589d01b5fce70f06068b3304556145bc1c284ebe Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:20:51 +0200
Subject: [PATCH 39/56] test: nodiscard/noexcept for assoc_laguerre(double),
 assoc_laguerrel

---
 .../test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp  | 3 +++
 .../libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp  | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index a4c471e4b3168..8b8826d03de01 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -15,6 +15,9 @@
 void test() {
   // clang-format off
   // assoc_laguerre
+  std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   // clang-format on
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 9994e6a018f8d..70984fc0c83bf 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -16,5 +16,8 @@
 
 void test() {
   // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0));
+
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
+  ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
 }

>From db297e6b6a155fdd5ea0bb4978f5e267c14a773e Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:42:41 +0200
Subject: [PATCH 40/56] add ABI symbol to abilist for assoc_laguerre(double)

---
 ...m64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 .../powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...owerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ..._64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...nknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...nown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 1 +
 9 files changed, 9 insertions(+)

diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6ab143c0113f3..9f70dd6550fc7 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1701,6 +1701,7 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 08d42d44e4827..4152a583ec5a3 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1339,6 +1339,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 98fd189eaa3f7..861750f606c77 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -687,6 +687,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEyPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 9e1db57fc315b..347d9f9f23d70 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -687,6 +687,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index a59b72d695b55..cd3f3e4fda9a2 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1690,6 +1690,7 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 61bd10d997a1b..421f4839f762e 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1339,6 +1339,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 286ad56406ee2..d59e224c42922 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1351,6 +1351,7 @@
 {'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 160, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 9fd2edf63e6ef..d2b084bace550 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1350,6 +1350,7 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 51c27fea37ce9..7e57931398e85 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1321,6 +1321,7 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}

>From e8984b706592b5b2ff08bdd19020330498423157 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:12:02 +0200
Subject: [PATCH 41/56] impl assoc_laguerre(Int)

---
 libcxx/include/__math/special_functions.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 20268dd1bd7d6..5021e5a54b634 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -47,6 +47,11 @@ assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+template <class _Int, std::enable_if_t<std::is_integral_v<_Int>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 // hermite

>From c8c5eb6a2040040b6a5c67c42128172a4c5661a8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:18:00 +0200
Subject: [PATCH 42/56] test: nodiscard/except assoc_laguerre(Int)

---
 .../numerics/c.math/sf.cmath/nodiscard.verify.cpp      |  1 +
 .../numerics/c.math/sf.cmath/noexcept.compile.pass.cpp | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 8b8826d03de01..b83070f4f984d 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -16,6 +16,7 @@ void test() {
   // clang-format off
   // assoc_laguerre
   std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 70984fc0c83bf..cca9c1e924a8c 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -13,6 +13,14 @@
 #include <cmath>
 
 #include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestIntegral {
+  template <class T>
+  void operator()() const {
+    ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, T{}));
+  }
+};
 
 void test() {
   // assoc_laguerre
@@ -20,4 +28,6 @@ void test() {
 
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
   ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
+
+  types::for_each(types::integral_types{}, TestIntegral{});
 }

>From 5946b7d3c07ea7f4dc898e9750313838fd3a99a8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:19:29 +0200
Subject: [PATCH 43/56] wip: test: assoc_laguerre(Int)

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 48 ++++++++++++-------
 1 file changed, 32 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 9c2bc2026a348..b39e20fe6288a 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -17,39 +17,55 @@
 #include <cassert>
 #include <cmath>
 #include <limits>
+#include <type_traits>
 
 #include "common.h"
+#include "type_algorithms.h"
 
 // Each overload is fixed to a single floating-point type, so it is tested
 // directly with that type. The integer-argument overload
 // (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
-template <class T, class Func>
+template <class ExpectedFuncRet, class FuncArgX, class Func>
 void test(Func assoc_laguerre) {
   // sample values
-  assert(between(0.99, assoc_laguerre(0, 0, T(0)), 1.01));
-  assert(between(0.99, assoc_laguerre(1, 1, T(1)), 1.01));
-  assert(between(-1.01, assoc_laguerre(2, 0, T(2)), -0.99));
-  assert(between(-0.01, assoc_laguerre(2, 2, T(2)), 0.01));
-  assert(between(60.124, assoc_laguerre(2, 10, T(0.5)), 60.126));
+  assert(between(0.99, assoc_laguerre(0, 0, FuncArgX(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, FuncArgX(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, FuncArgX(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, FuncArgX(2)), 0.01));
+  if constexpr (std::is_floating_point_v<FuncArgX>)
+    assert(between(60.124, assoc_laguerre(2, 10, FuncArgX(0.5)), 60.126));
 
-  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, T(0))), T>);
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, FuncArgX(0))), ExpectedFuncRet>);
 
   // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
-  auto test_nan = [&](T nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
-  if (std::numeric_limits<T>::has_quiet_NaN)
-    test_nan(std::numeric_limits<T>::quiet_NaN());
-  if (std::numeric_limits<T>::has_signaling_NaN)
-    test_nan(std::numeric_limits<T>::signaling_NaN());
+  if constexpr (std::is_floating_point_v<FuncArgX>) {
+    auto test_nan = [&](FuncArgX nan) {
+      check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); });
+    };
+    if (std::numeric_limits<FuncArgX>::has_quiet_NaN)
+      test_nan(std::numeric_limits<FuncArgX>::quiet_NaN());
+    if (std::numeric_limits<FuncArgX>::has_signaling_NaN)
+      test_nan(std::numeric_limits<FuncArgX>::signaling_NaN());
+  }
 
   // A negative argument is in the domain (no domain error).
-  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, T(-1)), 2.01)); });
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, FuncArgX(-1)), 2.01)); });
 }
 
+struct TestIntegral {
+  template <class Int>
+  void operator()() const {
+    test<double, Int>([](unsigned n, unsigned m, Int x) { return std::assoc_laguerre(n, m, x); });
+  }
+};
+
 int main(int, char**) {
-  test<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+  test<double, double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+
+  test<float, float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test<long double, long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
-  test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
-  test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
+  // types::for_each(types::integral_types{}, TestIntegral{}); WIP
 
   return 0;
 }

>From 619773ba99f60f0d799ca98488fa71cc246e2c47 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 17:48:09 +0200
Subject: [PATCH 44/56] fix gcc build: gcc defines __STDCPP_FLOATXX_T__ even
 though we dont ship <stdfloat> yet -> breaks Boost

---
 libcxx/src/mathematical_special_functions.cpp | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 0f6e3a850ad73..b8e14967740f1 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -12,6 +12,22 @@
 #include <optional>
 #include <type_traits>
 
+// GCC defines __STDCPP_FLOATnn_T__ whenever the _Floatnn extended types exist at the
+// language level, independent of the standard library. libc++ currently ships no
+// <stdfloat>, so std::floatnn_t is never declared -- but Boost.Math keys its
+// std::floatnn_t overloads off these macros and would reference the missing types.
+// Suppress those overloads while <stdfloat> is unavailable. The __has_include guard is
+// the same condition Boost uses to include <stdfloat>, so this workaround disables itself
+// automatically once libc++ provides the header (the overloads then light up on their own
+// -- no manual re-enable needed here).
+#if !__has_include(<stdfloat>)
+#  undef __STDCPP_FLOAT16_T__
+#  undef __STDCPP_FLOAT32_T__
+#  undef __STDCPP_FLOAT64_T__
+#  undef __STDCPP_FLOAT128_T__
+#  undef __STDCPP_BFLOAT16_T__
+#endif
+
 #define BOOST_MATH_NO_EXCEPTIONS
 #include <boost/math/special_functions.hpp>
 

>From 3c8e6c21dca99ac9b83fe264c70255710d20c21f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 18:01:47 +0200
Subject: [PATCH 45/56] fix android abilist

---
 ...linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 421f4839f762e..08b901a94f394 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1340,8 +1340,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjg', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}

>From e4c48175cfc00429e93c92a30aa81e67201f4866 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 18:02:21 +0200
Subject: [PATCH 46/56] fix boost math thread support

---
 libcxx/src/mathematical_special_functions.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index b8e14967740f1..c37811bdf52ea 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -28,6 +28,14 @@
 #  undef __STDCPP_BFLOAT16_T__
 #endif
 
+// Boost.Math detects thread support via __has_include(<thread>/<mutex>/...), but libc++
+// ships those headers even when threads are disabled (_LIBCPP_HAS_THREADS == 0), so the
+// detection wrongly enables std::mutex use and breaks on no-thread targets (e.g. picolibc).
+// Tell Boost there are no threads; lazy-init tables don't need locking without threads.
+#if !_LIBCPP_HAS_THREADS
+#  define BOOST_MATH_DISABLE_THREADS
+#endif
+
 #define BOOST_MATH_NO_EXCEPTIONS
 #include <boost/math/special_functions.hpp>
 

>From 60635ce92abd69cad3a5161a78a5a2f55281d9b8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 21:47:33 +0200
Subject: [PATCH 47/56] test: fix apple's math_errhandling usage. runtime not
 macro constant

---
 .../std/numerics/c.math/sf.cmath/common.h     | 23 +++++++++----------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
index 723422bf7038d..ca6de9ea868bd 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/common.h
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -26,26 +26,25 @@ bool between(type_identity_t<T> lower, T value, type_identity_t<T> upper) {
   return lower < value && value < upper;
 }
 
+// Note: math_errhandling must be queried with a runtime `if`, not `#if`. On Apple
+// platforms it is defined as a function call (__math_errhandling()), so it is not a
+// preprocessor constant and would break `#if` under -Wundef.
 template <class Func>
 void check_no_domain_error(Func f) {
-#if math_errhandling & MATH_ERRNO
-  errno = EACCES;
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    errno = EACCES;
   f();
-#if math_errhandling & MATH_ERRNO
-  assert(errno == EACCES);
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    assert(errno == EACCES);
 }
 
 template <class Func>
 void check_domain_error(Func f) {
-#if math_errhandling & MATH_ERRNO
-  errno = EACCES;
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    errno = EACCES;
   f();
-#if math_errhandling & MATH_ERRNO
-  assert(errno == EDOM);
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    assert(errno == EDOM);
 }
 
 #endif // TEST_SF_CMATH_COMMON_H

>From 8201618481b60722093b07eff23b8bcb3b274e05 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:16:59 +0200
Subject: [PATCH 48/56] minimze boost includes

---
 libcxx/src/mathematical_special_functions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index c37811bdf52ea..dc046b3a845df 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -37,7 +37,7 @@
 #endif
 
 #define BOOST_MATH_NO_EXCEPTIONS
-#include <boost/math/special_functions.hpp>
+#include <boost/math/special_functions/laguerre.hpp>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS

>From 4d11bdbe96fca5653a71146ff3fea197180f13ef Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:22:46 +0200
Subject: [PATCH 49/56] patch boost: make it compilable w/o localization
 support

---
 third-party/boost-math-libcxx.patch | 47 +++++++++++++++++++++++++++++
 third-party/update_boost_math.sh    | 10 ++++++
 2 files changed, 57 insertions(+)
 create mode 100644 third-party/boost-math-libcxx.patch

diff --git a/third-party/boost-math-libcxx.patch b/third-party/boost-math-libcxx.patch
new file mode 100644
index 0000000000000..f7692a7e54b67
--- /dev/null
+++ b/third-party/boost-math-libcxx.patch
@@ -0,0 +1,47 @@
+# Local libc++ patch for the vendored Boost.Math snapshot.
+#
+# Boost.Math only uses <sstream>/<iomanip> and detail::prec_format to build the
+# what() text of the exceptions it throws. That machinery sits OUTSIDE Boost's own
+# `#ifndef BOOST_MATH_NO_EXCEPTIONS` guard (unlike the adjacent <stdexcept> /
+# throw_exception.hpp includes), so it is compiled even when exceptions are disabled.
+# libc++ builds the Math Special Functions back-end with BOOST_MATH_NO_EXCEPTIONS,
+# and on configurations without localization (e.g. -DLIBCXX_ENABLE_LOCALIZATION=OFF)
+# libc++ ships no functional <sstream>, so prec_format fails to compile.
+#
+# This patch guards the iostream-only message formatting under BOOST_MATH_NO_EXCEPTIONS.
+# Reported upstream: https://github.com/boostorg/math/issues (see PR description).
+# Re-applied by update_boost_math.sh after each snapshot refresh; drop once upstream.
+diff --git a/boost-math/include/boost/math/policies/error_handling.hpp b/boost-math/include/boost/math/policies/error_handling.hpp
+index d7ba694564ba..da167171f650 100644
+--- a/boost-math/include/boost/math/policies/error_handling.hpp
++++ b/boost-math/include/boost/math/policies/error_handling.hpp
+@@ -26,9 +26,9 @@
+ #include <complex>
+ #include <cstdint>
+ #include <cstring>
++#ifndef BOOST_MATH_NO_EXCEPTIONS
+ #include <iomanip>
+ #include <sstream>
+-#ifndef BOOST_MATH_NO_EXCEPTIONS
+ #include <stdexcept>
+ #endif
+ #include <string>
+@@ -95,6 +95,10 @@ T user_indeterminate_result_error(const char* function, const char* message, con
+ namespace detail
+ {
+ 
++// prec_format is only used to build exception what() messages, so it is gated on
++// exceptions being enabled. It relies on <sstream>, which is unavailable in some
++// standard-library configurations (e.g. libc++ built without localization).
++#ifndef BOOST_MATH_NO_EXCEPTIONS
+ template <class T>
+ inline std::string prec_format(const T& val)
+ {
+@@ -125,6 +129,7 @@ inline std::string prec_format<std::float128_t>(const std::float128_t& val)
+ }
+ 
+ #endif
++#endif // BOOST_MATH_NO_EXCEPTIONS
+ 
+ inline void replace_all_in_string(std::string& result, const char* what, const char* with)
+ {
diff --git a/third-party/update_boost_math.sh b/third-party/update_boost_math.sh
index 27d8816b7cb12..a4276cdb74028 100755
--- a/third-party/update_boost_math.sh
+++ b/third-party/update_boost_math.sh
@@ -40,3 +40,13 @@ sed -i 's|target_include_directories(boost_math INTERFACE include)|target_includ
 grep -q 'target_include_directories(boost_math SYSTEM INTERFACE include)' \
     ${SCRIPT_DIR}/boost-math/CMakeLists.txt \
     || { echo "ERROR: SYSTEM include patch failed -- upstream CMakeLists.txt structure changed"; exit 1; }
+
+# Guard Boost.Math's iostream-only exception-message formatting (prec_format / <sstream>)
+# under BOOST_MATH_NO_EXCEPTIONS, so the no-exceptions back-end builds without <sstream>.
+# Needed for libc++ configurations without localization. Drop once fixed upstream.
+patch -p1 -d ${SCRIPT_DIR} < ${SCRIPT_DIR}/boost-math-libcxx.patch
+
+# Verify the patch landed -- fail loudly if upstream restructured error_handling.hpp.
+grep -q 'prec_format is only used to build exception' \
+    ${SCRIPT_DIR}/boost-math/include/boost/math/policies/error_handling.hpp \
+    || { echo "ERROR: no-exceptions iostream patch failed -- upstream error_handling.hpp structure changed"; exit 1; }

>From 276df4abe72a9ba6d4979d5922b466445aa406b9 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:32:27 +0200
Subject: [PATCH 50/56] apply boost patch

---
 .../include/boost/math/policies/error_handling.hpp         | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/third-party/boost-math/include/boost/math/policies/error_handling.hpp b/third-party/boost-math/include/boost/math/policies/error_handling.hpp
index d7ba694564ba4..da167171f650d 100644
--- a/third-party/boost-math/include/boost/math/policies/error_handling.hpp
+++ b/third-party/boost-math/include/boost/math/policies/error_handling.hpp
@@ -26,9 +26,9 @@
 #include <complex>
 #include <cstdint>
 #include <cstring>
+#ifndef BOOST_MATH_NO_EXCEPTIONS
 #include <iomanip>
 #include <sstream>
-#ifndef BOOST_MATH_NO_EXCEPTIONS
 #include <stdexcept>
 #endif
 #include <string>
@@ -95,6 +95,10 @@ T user_indeterminate_result_error(const char* function, const char* message, con
 namespace detail
 {
 
+// prec_format is only used to build exception what() messages, so it is gated on
+// exceptions being enabled. It relies on <sstream>, which is unavailable in some
+// standard-library configurations (e.g. libc++ built without localization).
+#ifndef BOOST_MATH_NO_EXCEPTIONS
 template <class T>
 inline std::string prec_format(const T& val)
 {
@@ -125,6 +129,7 @@ inline std::string prec_format<std::float128_t>(const std::float128_t& val)
 }
 
 #endif
+#endif // BOOST_MATH_NO_EXCEPTIONS
 
 inline void replace_all_in_string(std::string& result, const char* what, const char* with)
 {

>From 6bbed03306814ee7a45418ba8e01e619852d7d94 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:53:16 +0200
Subject: [PATCH 51/56] doc: add boost issue

---
 third-party/boost-math-libcxx.patch | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/third-party/boost-math-libcxx.patch b/third-party/boost-math-libcxx.patch
index f7692a7e54b67..8593b077a2db2 100644
--- a/third-party/boost-math-libcxx.patch
+++ b/third-party/boost-math-libcxx.patch
@@ -9,7 +9,7 @@
 # libc++ ships no functional <sstream>, so prec_format fails to compile.
 #
 # This patch guards the iostream-only message formatting under BOOST_MATH_NO_EXCEPTIONS.
-# Reported upstream: https://github.com/boostorg/math/issues (see PR description).
+# Reported upstream: https://github.com/boostorg/math/issues/1412
 # Re-applied by update_boost_math.sh after each snapshot refresh; drop once upstream.
 diff --git a/boost-math/include/boost/math/policies/error_handling.hpp b/boost-math/include/boost/math/policies/error_handling.hpp
 index d7ba694564ba..da167171f650 100644

>From 441f2f0089823971d515573e8f8074fddd2e32a1 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 08:34:54 +0200
Subject: [PATCH 52/56] guard via
 _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS

---
 libcxx/include/__configuration/availability.h |  5 +++++
 libcxx/include/__math/special_functions.h     | 20 ++++++++++++-------
 .../c.math/sf.cmath/nodiscard.verify.cpp      |  4 ++++
 .../c.math/sf.cmath/noexcept.compile.pass.cpp |  4 ++++
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   |  4 ++++
 .../libcxx/test/features/availability.py      |  8 ++++++++
 6 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 735af5d431cb1..72eb50721338f 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -271,6 +271,11 @@
 #define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20
 #define _LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20_ATTRIBUTE
 
+// This controls the availability of the C++17 mathematical special functions
+// ([sf.cmath]). Their implementation lives in the built library (backed by Boost.Math).
+#define _LIBCPP_AVAILABILITY_HAS_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_INTRODUCED_IN_LLVM_23
+#define _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE
+
 // This controls the availability of the C++20 time zone database.
 // The parser code is built in the library.
 #define _LIBCPP_AVAILABILITY_HAS_TZDB _LIBCPP_INTRODUCED_IN_LLVM_19
diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 5021e5a54b634..546600b72bce0 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -29,26 +29,32 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 // assoc_laguerre
 namespace __math {
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI double __assoc_laguerre(unsigned, unsigned, double) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI float
+__assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI double
+__assoc_laguerre(unsigned, unsigned, double) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI long double
+__assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI double
+assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI long double
 assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
 template <class _Int, std::enable_if_t<std::is_integral_v<_Int>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_HIDE_FROM_ABI double
+assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index b83070f4f984d..598311f20917d 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// The functions carry availability markup, so referencing them fails to compile against a
+// back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // Check that functions are marked [[nodiscard]]
 
 #include <cmath>
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index cca9c1e924a8c..8f9dd87c0ee87 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// The functions carry availability markup, so referencing them fails to compile against a
+// back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // Check that functions are marked noexcept
 
 #include <cmath>
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index b39e20fe6288a..91a7c4a3d2047 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// These functions are implemented in the built library, so a program using them fails to
+// load against a back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // <cmath>
 
 // double      assoc_laguerre(unsigned n, unsigned m, double x);
diff --git a/libcxx/utils/libcxx/test/features/availability.py b/libcxx/utils/libcxx/test/features/availability.py
index c0f2a3d4cdc88..7bb1add0e9e9c 100644
--- a/libcxx/utils/libcxx/test/features/availability.py
+++ b/libcxx/utils/libcxx/test/features/availability.py
@@ -211,4 +211,12 @@
             cfg.available_features,
         ),
     ),
+    # Tests that require the C++17 mathematical special functions in the built library
+    Feature(
+        name="availability-mathematical_special_functions-missing",
+        when=lambda cfg: BooleanExpression.evaluate(
+            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-23)",
+            cfg.available_features,
+        ),
+    ),
 ]

>From c63f3e0b04b37c5dcd6294473878de37d612a0f7 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 13:31:20 +0200
Subject: [PATCH 53/56] cleanup header includes

---
 libcxx/include/__math/special_functions.h     | 1 -
 libcxx/src/mathematical_special_functions.cpp | 1 +
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 546600b72bce0..9721430ee0c64 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -11,7 +11,6 @@
 #define _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
 
 #include <__config>
-#include <__configuration/attributes.h>
 #include <__math/copysign.h>
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index dc046b3a845df..e9f655d56aa69 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -37,6 +37,7 @@
 #endif
 
 #define BOOST_MATH_NO_EXCEPTIONS
+#include <boost/math/policies/policy.hpp>
 #include <boost/math/special_functions/laguerre.hpp>
 
 _LIBCPP_BEGIN_NAMESPACE_STD

>From 8afb315b5746d25b2c2f915d91fffa49b41720f8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 14:02:44 +0200
Subject: [PATCH 54/56] test: add integral tests; refactor

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 72 +++++++++----------
 1 file changed, 36 insertions(+), 36 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 91a7c4a3d2047..7561cb27b6020 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,62 +14,62 @@
 
 // <cmath>
 
-// double      assoc_laguerre(unsigned n, unsigned m, double x);
-// float       assoc_laguerref(unsigned n, unsigned m, float x);
-// long double assoc_laguerrel(unsigned n, unsigned m, long double x);
+// floating-point-type assoc_laguerre( unsigned n, unsigned m, floating-point-type x);
+// float               assoc_laguerref(unsigned n, unsigned m, float x              );
+// long double         assoc_laguerrel(unsigned n, unsigned m, long double x        );
 
 #include <cassert>
 #include <cmath>
+#include <initializer_list>
 #include <limits>
 #include <type_traits>
 
 #include "common.h"
 #include "type_algorithms.h"
 
-// Each overload is fixed to a single floating-point type, so it is tested
-// directly with that type. The integer-argument overload
-// (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
-template <class ExpectedFuncRet, class FuncArgX, class Func>
-void test(Func assoc_laguerre) {
-  // sample values
-  assert(between(0.99, assoc_laguerre(0, 0, FuncArgX(0)), 1.01));
-  assert(between(0.99, assoc_laguerre(1, 1, FuncArgX(1)), 1.01));
-  assert(between(-1.01, assoc_laguerre(2, 0, FuncArgX(2)), -0.99));
-  assert(between(-0.01, assoc_laguerre(2, 2, FuncArgX(2)), 0.01));
-  if constexpr (std::is_floating_point_v<FuncArgX>)
-    assert(between(60.124, assoc_laguerre(2, 10, FuncArgX(0.5)), 60.126));
+// Tests a fixed-precision overload (assoc_laguerre / assoc_laguerref / assoc_laguerrel),
+// whose argument and return type are both `Float`.
+template <class Float, class Func>
+void test_floating_point(Func assoc_laguerre) {
+  // return type is the argument type
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, Float(0))), Float>);
 
-  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, FuncArgX(0))), ExpectedFuncRet>);
+  // sample values
+  assert(between(0.99, assoc_laguerre(0, 0, Float(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, Float(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, Float(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, Float(2)), 0.01));
+  assert(between(60.124, assoc_laguerre(2, 10, Float(0.5)), 60.126));
 
-  // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
-  if constexpr (std::is_floating_point_v<FuncArgX>) {
-    auto test_nan = [&](FuncArgX nan) {
-      check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); });
-    };
-    if (std::numeric_limits<FuncArgX>::has_quiet_NaN)
-      test_nan(std::numeric_limits<FuncArgX>::quiet_NaN());
-    if (std::numeric_limits<FuncArgX>::has_signaling_NaN)
-      test_nan(std::numeric_limits<FuncArgX>::signaling_NaN());
-  }
+  // a negative argument is in the domain (no domain error)
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, Float(-1)), 2.01)); });
 
-  // A negative argument is in the domain (no domain error).
-  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, FuncArgX(-1)), 2.01)); });
+  // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1)
+  auto test_nan = [&](Float nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
+  if (std::numeric_limits<Float>::has_quiet_NaN)
+    test_nan(std::numeric_limits<Float>::quiet_NaN());
+  if (std::numeric_limits<Float>::has_signaling_NaN)
+    test_nan(std::numeric_limits<Float>::signaling_NaN());
 }
 
-struct TestIntegral {
-  template <class Int>
+// Tests the integer-argument overload: it promotes the argument to double and returns double.
+struct TestInteger {
+  template <class Integer>
   void operator()() const {
-    test<double, Int>([](unsigned n, unsigned m, Int x) { return std::assoc_laguerre(n, m, x); });
+    static_assert(std::is_same_v<decltype(std::assoc_laguerre(0u, 0u, Integer(0))), double>);
+
+    // same result as the double overload with the argument cast to double
+    for (Integer x : {Integer(0), Integer(1), Integer(2)})
+      assert(std::assoc_laguerre(2, 0, x) == std::assoc_laguerre(2, 0, static_cast<double>(x)));
   }
 };
 
 int main(int, char**) {
-  test<double, double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
-
-  test<float, float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
-  test<long double, long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
+  test_floating_point<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test_floating_point<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+  test_floating_point<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
-  // types::for_each(types::integral_types{}, TestIntegral{}); WIP
+  types::for_each(types::integral_types{}, TestInteger{});
 
   return 0;
 }

>From 7236c0aa7861a04227195f8fa88d0f7efde7267c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 14:21:17 +0200
Subject: [PATCH 55/56] impl assoc_laguerre(float/long double) + tests

---
 libcxx/include/__math/special_functions.h          | 14 ++++++++++++--
 .../numerics/c.math/sf.cmath/nodiscard.verify.cpp  |  6 ++++--
 .../c.math/sf.cmath/noexcept.compile.pass.cpp      | 13 +++----------
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        |  2 ++
 4 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 9721430ee0c64..8bb805d721b5f 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -37,7 +37,7 @@ __assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
 [[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI float
-assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
@@ -47,7 +47,7 @@ assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
 }
 
 [[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI long double
-assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
+assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
@@ -57,6 +57,16 @@ assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
 }
 
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI long double
+assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 // hermite
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 598311f20917d..4729dbd23c58e 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -19,8 +19,10 @@
 void test() {
   // clang-format off
   // assoc_laguerre
-  std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::assoc_laguerre(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0.0f);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0.0);   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0.0l);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0);     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 8f9dd87c0ee87..b9993ab6500a7 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -17,21 +17,14 @@
 #include <cmath>
 
 #include "test_macros.h"
-#include "type_algorithms.h"
-
-struct TestIntegral {
-  template <class T>
-  void operator()() const {
-    ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, T{}));
-  }
-};
 
 void test() {
   // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0f));
   ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0));
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0l));
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0));
 
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
   ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
-
-  types::for_each(types::integral_types{}, TestIntegral{});
 }
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 7561cb27b6020..a0dca3cf5ce14 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -66,8 +66,10 @@ struct TestInteger {
 
 int main(int, char**) {
   test_floating_point<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test_floating_point<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerre(n, m, x); });
   test_floating_point<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
   test_floating_point<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
+  test_floating_point<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerre(n, m, x); });
 
   types::for_each(types::integral_types{}, TestInteger{});
 

>From cda53aa4c90bd6477110fc4597d6433e67613eee Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 22:37:56 +0200
Subject: [PATCH 56/56] dummy commit (retrigger pipeline)

---
 .../test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index a0dca3cf5ce14..dd07bae308ca0 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -58,7 +58,7 @@ struct TestInteger {
   void operator()() const {
     static_assert(std::is_same_v<decltype(std::assoc_laguerre(0u, 0u, Integer(0))), double>);
 
-    // same result as the double overload with the argument cast to double
+    // same result as the double overload with the argument cast to double.
     for (Integer x : {Integer(0), Integer(1), Integer(2)})
       assert(std::assoc_laguerre(2, 0, x) == std::assoc_laguerre(2, 0, static_cast<double>(x)));
   }


From libcxx-commits at lists.llvm.org  Sat Jul  4 13:44:00 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 13:44:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
 `elements_view` (PR #206589)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206589@github.com>
Message-ID: <6a497090.dd2b4bd3.13e95e.b512@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/206589

>From fde2218d5a8d799e9c2806a51ddd9758c2efd13e Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 29 Jun 2026 23:30:56 +0200
Subject: [PATCH 1/7] add: nodiscard to elements_view

---
 libcxx/include/__ranges/elements_view.h | 40 ++++++++++++-------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..fc8c46b29b57f 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -77,57 +77,57 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit elements_view(_View __base) : __base_(std::move(__base)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
     requires(!__simple_view<_View>)
   {
     return __iterator</*_Const=*/false>(ranges::begin(__base_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _View>
   {
     return __iterator</*_Const=*/true>(ranges::begin(__base_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!__simple_view<_View> && !common_range<_View>)
   {
     return __sentinel</*_Const=*/false>{ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!__simple_view<_View> && common_range<_View>)
   {
     return __iterator</*_Const=*/false>{ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _View>
   {
     return __sentinel</*_Const=*/true>{ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires common_range<const _View>
   {
     return __iterator</*_Const=*/true>{ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
     return ranges::size(__base_);
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
     return ranges::size(__base_);
@@ -211,11 +211,11 @@ class elements_view<_View, _Np>::__iterator
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return __get_element(__current_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return __get_element(__current_); }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
     ++__current_;
@@ -261,7 +261,7 @@ class elements_view<_View, _Np>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return __get_element(__current_ + __n);
@@ -303,25 +303,25 @@ class elements_view<_View, _Np>::__iterator
     return __x.__current_ <=> __y.__current_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __x, difference_type __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __x, difference_type __y)
     requires random_access_range<_Base>
   {
     return __iterator{__x} += __y;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __x, const __iterator& __y)
     requires random_access_range<_Base>
   {
     return __y + __x;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __x, difference_type __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __x, difference_type __y)
     requires random_access_range<_Base>
   {
     return __iterator{__x} -= __y;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
   {
     return __x.__current_ - __y.__current_;
@@ -365,14 +365,14 @@ class elements_view<_View, _Np>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __get_current(__x) - __y.__end_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__end_ - __get_current(__y);
   }

>From 4c961f7efb3efb1985259cd16b13fae3c23ef871 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 29 Jun 2026 23:36:42 +0200
Subject: [PATCH 2/7] fix: clang format

---
 libcxx/include/__ranges/elements_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index fc8c46b29b57f..33c152bbd03b4 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -321,7 +321,8 @@ class elements_view<_View, _Np>::__iterator
     return __iterator{__x} -= __y;
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
   {
     return __x.__current_ - __y.__current_;

>From 8c1bb7ca3ce494344d17e1f41fd8317efe3809da Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 12:46:41 +0200
Subject: [PATCH 3/7] add: range.elements.overview as per the 25.7.23.1.2.1
 example in the Standard

---
 .../range.elements/nodiscard.verify.cpp       | 44 +++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..16759e6a53569
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Test the libc++ extension that std::ranges::transform_view and std::views::transform are marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+#include <functional>
+#include <map>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+void test() {
+    auto historical_figures = std::map{
+      std::pair{"Lovelace"sv, 1815},
+      {"Turing"sv, 1912},
+      {"Babbage"sv, 1791},
+      {"Hamilton"sv, 1936}
+    };
+
+    // [range.elements.overview]
+    
+   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::elements<0>(historical_figures);
+
+   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::elements<1>(historical_figures);
+}
+

>From 77071141de6b3af76d1e8d846b887be981bbdb5e Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 21:59:05 +0200
Subject: [PATCH 4/7] fix: drop sv

---
 .../range.adaptors/range.elements/nodiscard.verify.cpp   | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
index 16759e6a53569..5951a04e31d91 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 #include <functional>
+#include <string_view>
 #include <map>
 
 struct View : std::ranges::view_interface<View> {
@@ -27,10 +28,10 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
     auto historical_figures = std::map{
-      std::pair{"Lovelace"sv, 1815},
-      {"Turing"sv, 1912},
-      {"Babbage"sv, 1791},
-      {"Hamilton"sv, 1936}
+        std::pair{"Lovelace", 1815},
+        std::pair{"Turing", 1912},
+        std::pair{"Babbage", 1791},
+        std::pair{"Hamilton", 1936}
     };
 
     // [range.elements.overview]

>From 6b4b2c0585ec53caefab4562a91ecbd4dd51b9ad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 22:35:49 +0200
Subject: [PATCH 5/7] add: full tests for range.elements

---
 .../range.elements/nodiscard.verify.cpp       | 99 ++++++++++++++-----
 1 file changed, 77 insertions(+), 22 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
index 5951a04e31d91..0f25b2449cf5f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -10,36 +10,91 @@
 
 // Test the libc++ extension that std::ranges::transform_view and std::views::transform are marked as [[nodiscard]].
 
-#include <ranges>
-#include <utility>
 #include <functional>
-#include <string_view>
 #include <map>
+#include <ranges>
+#include <utility>
 
 struct View : std::ranges::view_interface<View> {
-  int* begin();
-  const int* begin() const;
-  volatile int* end();
-  const volatile int* end() const;
+  std::tuple<int, int>* begin();
+  const std::tuple<int, int>* begin() const;
+  volatile std::tuple<int, int>* end();
+  const volatile std::tuple<int, int>* end() const;
 };
 static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-    auto historical_figures = std::map{
-        std::pair{"Lovelace", 1815},
-        std::pair{"Turing", 1912},
-        std::pair{"Babbage", 1791},
-        std::pair{"Hamilton", 1936}
-    };
-
-    // [range.elements.overview]
-    
-   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-    std::views::elements<0>(historical_figures);
-
-   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-    std::views::elements<1>(historical_figures);
-}
+  auto v = View{} | std::views::elements<1>;
+
+  // [range.elements.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).size();
+
+  // [range.elements.iterator]
 
+  auto it   = v.begin();
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(it).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it[0];
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  0 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.elements.sentinel]
+
+  auto st = v.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
+  // [range.elements.overview]
+
+  auto historical_figures = std::map{
+      std::pair{"Lovelace", 1815}, std::pair{"Turing", 1912}, std::pair{"Babbage", 1791}, std::pair{"Hamilton", 1936}};
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::elements<0>(historical_figures);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::elements<1>(historical_figures);
+}

>From 720ef3917f62f01a6c667c4fb37e1547ef96106b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 1 Jul 2026 20:24:25 +0200
Subject: [PATCH 6/7] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../ranges/range.adaptors/range.elements/nodiscard.verify.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
index 0f25b2449cf5f..84790ffe90875 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: std-at-least-c++20
 
-// Test the libc++ extension that std::ranges::transform_view and std::views::transform are marked as [[nodiscard]].
+// Test the libc++ extension that std::ranges::elements_view and std::views::elements are marked as [[nodiscard]].
 
 #include <functional>
 #include <map>

>From 813282415c33ecf47816cfabfc8a980d4143f4b5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 4 Jul 2026 22:32:36 +0200
Subject: [PATCH 7/7] add: model CommonView

---
 .../range.elements/nodiscard.verify.cpp        | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
index 84790ffe90875..088c1dba5af9d 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -25,8 +25,19 @@ static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
+struct CommonView : std::ranges::view_interface<View> {
+  std::tuple<int, int>* begin();
+  const std::tuple<int, int>* begin() const;
+  std::tuple<int, int>* end();
+  const std::tuple<int, int>* end() const;
+};
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(!std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
+
 void test() {
-  auto v = View{} | std::views::elements<1>;
+  auto v        = View{} | std::views::elements<1>;
+  auto common_v = CommonView{} | std::views::elements<1>;
 
   // [range.elements.view]
 
@@ -45,6 +56,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).end();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  common_v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(common_v).end();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   v.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}


From libcxx-commits at lists.llvm.org  Sat Jul  4 14:00:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 14:00:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][math][c++17] P0226R1 -
 Mathematical Special Functions: infra + `std::assoc_laguerre` (PR #205649)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205649@github.com>
Message-ID: <6a49747f.e55dc341.29c9b8.5250@mx.google.com>


https://github.com/PaulXiCao updated https://github.com/llvm/llvm-project/pull/205649

>From 7555447d01b3e6bd5127f4ffbfaeeb223cf9ea81 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 20:52:48 +0200
Subject: [PATCH 01/56] link against Boost.Math

---
 libcxx/CMakeLists.txt     | 8 ++++++++
 libcxx/src/CMakeLists.txt | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..845042f15495f 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -899,6 +899,14 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+
+# Header-only Boost.Math (INTERFACE target `boost_math`). Lives outside the
+# libcxx source tree, so a binary dir must be supplied.
+add_subdirectory(
+  ${LLVM_THIRD_PARTY_DIR}/boost-math
+  ${LIBCXX_BINARY_DIR}/third-party/boost-math
+  EXCLUDE_FROM_ALL)
+
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index de7817ad69f26..c882446596a3e 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -176,6 +176,7 @@ add_library(cxx_shared SHARED ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(cxx_shared PUBLIC cxx-headers runtimes-libc-shared
                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                  PRIVATE boost_math
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_shared
   PROPERTIES
@@ -263,6 +264,7 @@ add_library(cxx_static STATIC ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(cxx_static PUBLIC cxx-headers runtimes-libc-static
                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                  PRIVATE boost_math
                                   PRIVATE libcxx-abi-static
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_static

>From fe89c404516cfea93ab67939b48a15c74213cd59 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 20:53:37 +0200
Subject: [PATCH 02/56] add empty src/mathematical_special_functions.cpp

---
 libcxx/src/CMakeLists.txt                     |  1 +
 libcxx/src/mathematical_special_functions.cpp | 17 +++++++++++++++++
 2 files changed, 18 insertions(+)
 create mode 100644 libcxx/src/mathematical_special_functions.cpp

diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index c882446596a3e..d2168decbfdba 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -29,6 +29,7 @@ set(LIBCXX_SOURCES
   include/ryu/ryu.h
   include/to_chars_floating_point.h
   include/from_chars_floating_point.h
+  mathematical_special_functions.cpp
   memory.cpp
   memory_resource.cpp
   new_handler.cpp
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
new file mode 100644
index 0000000000000..db07d61f89319
--- /dev/null
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -0,0 +1,17 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+
+namespace {}
+
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+_LIBCPP_END_NAMESPACE_STD

>From 7fbc20f4bea5a80fdebfb22eadee50cb90698b46 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 21:06:04 +0200
Subject: [PATCH 03/56] add simple test for std::assoc_laguerref (fails atm)

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
new file mode 100644
index 0000000000000..1a12c8f81a16c
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Missing powl
+// XFAIL: LLVM-LIBC-FIXME
+
+// REQUIRES: std-at-least-c++17
+
+// <cmath>
+//
+// [sf.cmath.assoc.laguerre], associated Laguerre polynomials
+// floating-point-type assoc_laguerre(unsigned n, unsigned m, floating-point-type x);
+// float               assoc_laguerref(unsigned n, unsigned m, float x);
+// long double         assoc_laguerrel(unsigned n, unsigned m, long double x);
+
+#include <cmath>
+
+int main(int, char**) {
+  std::assoc_laguerref(0, 0, 0.0f);
+
+  return 0;
+}

>From c7e11e92b9c70fe1f319a8f959ce1c11cbbfa9cc Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 21:52:09 +0200
Subject: [PATCH 04/56] handling MATH_ERRNO, MATH_ERREXCEPT via __sf_result

---
 libcxx/include/__math/special_functions.h | 25 +++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 6b217852e2f8c..86b3ac9cb665e 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -15,7 +15,10 @@
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_integral.h>
+#include <cerrno>
+#include <cfenv>
 #include <limits>
+#include <math.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -25,6 +28,28 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 17
 
+namespace __math {
+template <class _Tp>
+struct __sf_result {
+  bool __domain_error;
+  _Tp __ret;
+
+  operator _Tp() const {
+#  if math_errhandling & MATH_ERRNO
+    if (__domain_error)
+      errno = EDOM;
+#  endif
+
+#  if math_errhandling & MATH_ERREXCEPT
+    if (__domain_error)
+      feraiseexcept(FE_INVALID);
+#  endif
+
+    return __ret;
+  }
+};
+} // namespace __math
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).

>From 23081681878b007cb50f1ed115ec316fa956b4cf Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 22:00:10 +0200
Subject: [PATCH 05/56] impl assoc_laguerref

---
 libcxx/include/__math/special_functions.h     |  9 +++++
 libcxx/src/mathematical_special_functions.cpp | 37 ++++++++++++++++++-
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 86b3ac9cb665e..d0d6e56507e85 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -25,6 +25,7 @@
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 #if _LIBCPP_STD_VER >= 17
 
@@ -48,8 +49,15 @@ struct __sf_result {
     return __ret;
   }
 };
+
+_LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
+
 } // namespace __math
 
+inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).
@@ -104,6 +112,7 @@ _LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, _Integer __x) {
 
 #endif // _LIBCPP_STD_VER >= 17
 
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index db07d61f89319..e18e9c5f2797c 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -7,11 +7,46 @@
 //===----------------------------------------------------------------------===//
 
 #include <__config>
+#include <boost/math/special_functions.hpp>
+#include <cmath>
+#include <optional>
+#include <type_traits>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-namespace {}
+namespace __math {
+namespace {
+template <class _Ret>
+optional<_Ret> __check_nan() {
+  return nullopt;
+}
+
+template <class _Ret, class _Arg, class... _Args>
+optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
+  if constexpr (is_floating_point_v<_Arg>)
+    if (isnan(__arg))
+      return __arg;
+  return __check_nan<_Ret>(__args...);
+}
+
+template <class Func, class... _Args, class _Ret = std::invoke_result_t<Func, _Args...>>
+__sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
+  if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
+    return {.__domain_error = false, .__ret = *__maybe_nan};
+
+  try {
+    return {.__domain_error = false, .__ret = f(__args...)};
+  } catch (...) {
+    return {.__domain_error = true, .__ret = numeric_limits<_Ret>::quiet_NaN()};
+  }
+}
+} // namespace
+
+__sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
+  return invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+} // namespace __math
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From adb174a0e9d29b172b17bc9e0d781db0dff15d5c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 22:34:12 +0200
Subject: [PATCH 06/56] test assoc_laguerref

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 1a12c8f81a16c..fe92daa501c6f 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -6,22 +6,18 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing powl
-// XFAIL: LLVM-LIBC-FIXME
-
 // REQUIRES: std-at-least-c++17
 
 // <cmath>
 //
 // [sf.cmath.assoc.laguerre], associated Laguerre polynomials
-// floating-point-type assoc_laguerre(unsigned n, unsigned m, floating-point-type x);
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
-// long double         assoc_laguerrel(unsigned n, unsigned m, long double x);
 
+#include <cassert>
 #include <cmath>
 
-int main(int, char**) {
-  std::assoc_laguerref(0, 0, 0.0f);
-
-  return 0;
+int main() {
+  // Single value tested against known solution.
+  // Note, underlying Boost.Math is itself well-tested.
+  assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
 }

>From b7bc1fb6247235ce62066d70a30c13608a8672fc Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 18:26:00 +0200
Subject: [PATCH 07/56] test: common.h

---
 .../std/numerics/c.math/sf.cmath/common.h     | 65 +++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/sf.cmath/common.h

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
new file mode 100644
index 0000000000000..867e6af6f6914
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_SF_CMATH_COMMON_H
+#define TEST_SF_CMATH_COMMON_H
+
+#include <cassert>
+#include <cerrno>
+#include <cmath>
+#include <math.h>
+#include <cfenv>
+
+// std::type_identity is C++20 (we need to support C++17 here)
+template <class T>
+struct type_identity {
+  typedef T type;
+};
+template <class T>
+using type_identity_t = typename type_identity<T>::type;
+
+template <class T>
+bool between(type_identity_t<T> lower, T value, type_identity_t<T> upper) {
+  return lower < value && value < upper;
+}
+
+template <class Func>
+void check_no_domain_error(Func f) {
+#if math_errhandling & MATH_ERRNO
+  errno = EACCES;
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  std::feclearexcept(FE_INVALID);
+#endif
+  f();
+#if math_errhandling & MATH_ERRNO
+  assert(errno == EACCES);
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  assert(!std::fetestexcept(FE_INVALID));
+#endif
+}
+
+template <class Func>
+void check_domain_error(Func f) {
+#if math_errhandling & MATH_ERRNO
+  errno = EACCES;
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  std::feclearexcept(FE_INVALID);
+#endif
+  f();
+#if math_errhandling & MATH_ERRNO
+  assert(errno == EDOM);
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  assert(std::fetestexcept(FE_INVALID));
+#endif
+}
+
+#endif // TEST_SF_CMATH_COMMON_H

>From a47f9e902e5c427c84115f4af119eedf8778e182 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 18:26:59 +0200
Subject: [PATCH 08/56] more testing of assoc_laguerref: values, types, domain
 error

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 36 +++++++++++++++++--
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index fe92daa501c6f..eb747efb4c1c6 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,10 +14,40 @@
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
 
 #include <cassert>
+#include <cerrno>
+#include <cfenv>
 #include <cmath>
 
+#include "common.h"
+#include "type_algorithms.h"
+
+struct TestFloatingPoint {
+  template <class T>
+  void operator()() const {
+    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
+
+    assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
+
+    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
+
+    assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
+
+    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
+
+    check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
+  }
+};
+
+struct TestIntegral {
+  template <class T>
+  void operator()() const {
+    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
+
+    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
+  }
+};
+
 int main() {
-  // Single value tested against known solution.
-  // Note, underlying Boost.Math is itself well-tested.
-  assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
+  types::for_each(types::floating_point_types{}, TestFloatingPoint{});
+  types::for_each(types::integral_types{}, TestIntegral{});
 }

>From f8a3b1d891405ff6ab04bfc7a7d615ca2cd54a56 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:15:40 +0200
Subject: [PATCH 09/56] silence boost_math warnings by marking it as `SYSTEM`
 lib

---
 third-party/boost-math/CMakeLists.txt |  2 +-
 third-party/update_boost_math.sh      | 13 +++++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/third-party/boost-math/CMakeLists.txt b/third-party/boost-math/CMakeLists.txt
index a1f6011d95e55..0de1fa2ec01ed 100644
--- a/third-party/boost-math/CMakeLists.txt
+++ b/third-party/boost-math/CMakeLists.txt
@@ -11,7 +11,7 @@ add_library(boost_math INTERFACE)
 
 add_library(Boost::math ALIAS boost_math)
 
-target_include_directories(boost_math INTERFACE include)
+target_include_directories(boost_math SYSTEM INTERFACE include)
 if(NOT CMAKE_VERSION VERSION_LESS "3.19")
   file(GLOB_RECURSE headers include/*.hpp)
   target_sources(boost_math PRIVATE ${headers})
diff --git a/third-party/update_boost_math.sh b/third-party/update_boost_math.sh
index 96cd73ed8c304..27d8816b7cb12 100755
--- a/third-party/update_boost_math.sh
+++ b/third-party/update_boost_math.sh
@@ -27,3 +27,16 @@ echo "****************************************"
 echo "Subsetting Boost.Math ${VERSION}"
 echo "****************************************"
 rm -rf ${SCRIPT_DIR}/boost-math/{.circleci,.drone,.github,build,config,doc,example,meta,reporting,src,test,tools}
+
+echo "****************************************"
+echo "Patching Boost.Math ${VERSION} for libc++"
+echo "****************************************"
+# Mark boost_math include dir as SYSTEM so libc++ consumers don't surface warnings from vendored boost-math code (e.g.
+# -Wdeprecated-redundant-constexpr-static-def). Upstream keeps these for pre-C++17 compat.
+sed -i 's|target_include_directories(boost_math INTERFACE include)|target_include_directories(boost_math SYSTEM INTERFACE include)|' \
+    ${SCRIPT_DIR}/boost-math/CMakeLists.txt
+
+# Verify the patch landed -- fail loudly if upstream renamed the target.
+grep -q 'target_include_directories(boost_math SYSTEM INTERFACE include)' \
+    ${SCRIPT_DIR}/boost-math/CMakeLists.txt \
+    || { echo "ERROR: SYSTEM include patch failed -- upstream CMakeLists.txt structure changed"; exit 1; }

>From 17c19c5c87c5adf5618686cbf1cac445d4bfbfda Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:24:39 +0200
Subject: [PATCH 10/56] add nodiscard test for assoc_laguerref

---
 .../c.math/sf.cmath/nodiscard.verify.cpp      | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7928ebf50681a
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// Check that functions are marked [[nodiscard]]
+
+#include <cmath>
+
+void test() {
+  // clang-format off
+  // assoc_laguerre
+  std::assoc_laguerref(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}

>From 0a7cf75eca52eab4c5d98b4f323f05fbf0187a9c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:27:41 +0200
Subject: [PATCH 11/56] add nodiscard to assoc_laguerref

---
 libcxx/include/__math/special_functions.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index d0d6e56507e85..dee70bec45cd8 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -54,7 +54,8 @@ _LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsi
 
 } // namespace __math
 
-inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 

>From ddfb2c1563bf465cd912f226abe221488a67b0df Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 23:50:00 +0200
Subject: [PATCH 12/56] insert grouping for assoc_laguerre functions

---
 libcxx/include/__math/special_functions.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index dee70bec45cd8..3100860ec8c70 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -49,9 +49,11 @@ struct __sf_result {
     return __ret;
   }
 };
+} // namespace __math
 
+// assoc_laguerre
+namespace __math {
 _LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
-
 } // namespace __math
 
 [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float

>From 39b98428ea2cf592234512ac3762348f3594087c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 00:09:27 +0200
Subject: [PATCH 13/56] insert grouping for hermite functions

---
 libcxx/include/__math/special_functions.h | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 3100860ec8c70..b8ec0785a3b2b 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -26,7 +26,6 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
-
 #if _LIBCPP_STD_VER >= 17
 
 namespace __math {
@@ -61,6 +60,14 @@ assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+#endif // _LIBCPP_STD_VER >= 17
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+_LIBCPP_END_NAMESPACE_STD
+
+// hermite
+_LIBCPP_BEGIN_NAMESPACE_STD
+#if _LIBCPP_STD_VER >= 17
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).
@@ -114,8 +121,5 @@ _LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, _Integer __x) {
 }
 
 #endif // _LIBCPP_STD_VER >= 17
-
-_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
-
 #endif // _LIBCPP___MATH_SPECIAL_FUNCTIONS_H

>From 145ce5f04732b8f5800f9013411d11ffe97c215f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 00:10:16 +0200
Subject: [PATCH 14/56] add missing ABI annotation

---
 libcxx/include/__math/special_functions.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index b8ec0785a3b2b..e11dc97fd46d4 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -34,7 +34,7 @@ struct __sf_result {
   bool __domain_error;
   _Tp __ret;
 
-  operator _Tp() const {
+  _LIBCPP_HIDE_FROM_ABI operator _Tp() const {
 #  if math_errhandling & MATH_ERRNO
     if (__domain_error)
       errno = EDOM;

>From 2328d31c7918995c6895836c69e1371250675427 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:16:15 +0200
Subject: [PATCH 15/56] TU: guard by C++ >= 17

---
 libcxx/src/mathematical_special_functions.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index e18e9c5f2797c..102acb37e50bc 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -14,6 +14,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+#if _LIBCPP_STD_VER >= 17
 
 namespace __math {
 namespace {
@@ -48,5 +49,6 @@ __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __
 }
 } // namespace __math
 
+#endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From 7b60948e6eb6cb5b9ccea95e76bb02865d314871 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:18:02 +0200
Subject: [PATCH 16/56] TU: qualify std:: functions

---
 libcxx/src/mathematical_special_functions.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 102acb37e50bc..03a90cd3bd982 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -19,14 +19,14 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 namespace __math {
 namespace {
 template <class _Ret>
-optional<_Ret> __check_nan() {
-  return nullopt;
+std::optional<_Ret> __check_nan() {
+  return std::nullopt;
 }
 
 template <class _Ret, class _Arg, class... _Args>
-optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
-  if constexpr (is_floating_point_v<_Arg>)
-    if (isnan(__arg))
+std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
+  if constexpr (std::is_floating_point_v<_Arg>)
+    if (std::isnan(__arg))
       return __arg;
   return __check_nan<_Ret>(__args...);
 }
@@ -39,7 +39,7 @@ __sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
   try {
     return {.__domain_error = false, .__ret = f(__args...)};
   } catch (...) {
-    return {.__domain_error = true, .__ret = numeric_limits<_Ret>::quiet_NaN()};
+    return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
   }
 }
 } // namespace

>From a0f3426cd940ef45a441b4e610efaebf8f3e6655 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:18:46 +0200
Subject: [PATCH 17/56] TU: add missing __uglify

---
 libcxx/src/mathematical_special_functions.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 03a90cd3bd982..fdca91bf5a8b4 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -31,13 +31,13 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
   return __check_nan<_Ret>(__args...);
 }
 
-template <class Func, class... _Args, class _Ret = std::invoke_result_t<Func, _Args...>>
-__sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
+template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
+__sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return {.__domain_error = false, .__ret = *__maybe_nan};
 
   try {
-    return {.__domain_error = false, .__ret = f(__args...)};
+    return {.__domain_error = false, .__ret = __f(__args...)};
   } catch (...) {
     return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
   }
@@ -45,7 +45,7 @@ __sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
 } // namespace
 
 __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+  return __invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 } // namespace __math
 

>From 745c9d9aa7dfc246593851286f70e6494148c0d4 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:07 +0200
Subject: [PATCH 18/56] TU: add missing headers

---
 libcxx/src/mathematical_special_functions.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index fdca91bf5a8b4..9a106c763c776 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -7,8 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include <__config>
+#include <__math/special_functions.h>
 #include <boost/math/special_functions.hpp>
 #include <cmath>
+#include <limits>
 #include <optional>
 #include <type_traits>
 

>From 372f1054b2aa081808ed07b6182a6b8f2e4a21e3 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:42 +0200
Subject: [PATCH 19/56] TU: remove C++20 designated initializers

---
 libcxx/src/mathematical_special_functions.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 9a106c763c776..23d9216d777d2 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -36,12 +36,12 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
 __sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
-    return {.__domain_error = false, .__ret = *__maybe_nan};
+    return {false, *__maybe_nan};
 
   try {
-    return {.__domain_error = false, .__ret = __f(__args...)};
+    return {false, __f(__args...)};
   } catch (...) {
-    return {.__domain_error = true, .__ret = std::numeric_limits<_Ret>::quiet_NaN()};
+    return {true, std::numeric_limits<_Ret>::quiet_NaN()};
   }
 }
 } // namespace

>From 4e32fcc443bdd93c388e69aad76c0b08d7335d9d Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 10:28:51 +0200
Subject: [PATCH 20/56] TU: cleanup

---
 libcxx/src/mathematical_special_functions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 23d9216d777d2..cf88cbef159fa 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -47,7 +47,7 @@ __sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
 } // namespace
 
 __sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return __invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 } // namespace __math
 

>From 299419630480cc7ba0fd7c026a14c8797b461aa0 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 11:21:18 +0200
Subject: [PATCH 21/56] break cyclic header deps: move errno logic into TU

---
 libcxx/include/__math/special_functions.h     | 34 +------------------
 libcxx/src/mathematical_special_functions.cpp | 21 ++++++++----
 2 files changed, 15 insertions(+), 40 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index e11dc97fd46d4..89c75cfc04585 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -15,10 +15,7 @@
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_integral.h>
-#include <cerrno>
-#include <cfenv>
 #include <limits>
-#include <math.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -28,37 +25,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
-namespace __math {
-template <class _Tp>
-struct __sf_result {
-  bool __domain_error;
-  _Tp __ret;
-
-  _LIBCPP_HIDE_FROM_ABI operator _Tp() const {
-#  if math_errhandling & MATH_ERRNO
-    if (__domain_error)
-      errno = EDOM;
-#  endif
-
-#  if math_errhandling & MATH_ERREXCEPT
-    if (__domain_error)
-      feraiseexcept(FE_INVALID);
-#  endif
-
-    return __ret;
-  }
-};
-} // namespace __math
-
 // assoc_laguerre
-namespace __math {
-_LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
-} // namespace __math
-
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float
-assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
-  return __math::__assoc_laguerre(__n, __m, __x);
-}
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index cf88cbef159fa..daab723b8f2e3 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -9,8 +9,11 @@
 #include <__config>
 #include <__math/special_functions.h>
 #include <boost/math/special_functions.hpp>
+#include <cerrno>
+#include <cfenv>
 #include <cmath>
 #include <limits>
+#include <math.h>
 #include <optional>
 #include <type_traits>
 
@@ -18,7 +21,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
-namespace __math {
 namespace {
 template <class _Ret>
 std::optional<_Ret> __check_nan() {
@@ -34,22 +36,27 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 }
 
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
-__sf_result<_Ret> __invoke_boost_math(_Func __f, _Args... __args) {
+_Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
-    return {false, *__maybe_nan};
+    return *__maybe_nan;
 
   try {
-    return {false, __f(__args...)};
+    return __f(__args...);
   } catch (...) {
-    return {true, std::numeric_limits<_Ret>::quiet_NaN()};
+#  if math_errhandling & MATH_ERRNO
+    errno = EDOM;
+#  endif
+#  if math_errhandling & MATH_ERREXCEPT
+    feraiseexcept(FE_INVALID);
+#  endif
+    return std::numeric_limits<_Ret>::quiet_NaN();
   }
 }
 } // namespace
 
-__sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
+float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
-} // namespace __math
 
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS

>From f2580f1f2e853e83db75462c95558cbfeef6508c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 27 Jun 2026 11:29:35 +0200
Subject: [PATCH 22/56] TU: cleanups

---
 libcxx/src/mathematical_special_functions.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index daab723b8f2e3..420e0a1e71cb0 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -13,7 +13,6 @@
 #include <cfenv>
 #include <cmath>
 #include <limits>
-#include <math.h>
 #include <optional>
 #include <type_traits>
 
@@ -47,7 +46,7 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
     errno = EDOM;
 #  endif
 #  if math_errhandling & MATH_ERREXCEPT
-    feraiseexcept(FE_INVALID);
+    std::feraiseexcept(FE_INVALID);
 #  endif
     return std::numeric_limits<_Ret>::quiet_NaN();
   }

>From 80f81ae52610bc7accc38605524fdbf872004e4f Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 16:31:15 +0200
Subject: [PATCH 23/56] use boost errno_on_error policy instead of exceptions

---
 libcxx/src/mathematical_special_functions.cpp | 44 +++++++++++++++----
 1 file changed, 35 insertions(+), 9 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 420e0a1e71cb0..f8fea7ff1b70e 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -8,19 +8,31 @@
 
 #include <__config>
 #include <__math/special_functions.h>
-#include <boost/math/special_functions.hpp>
 #include <cerrno>
 #include <cfenv>
 #include <cmath>
-#include <limits>
 #include <optional>
 #include <type_traits>
 
+#define BOOST_MATH_NO_EXCEPTIONS
+#include <boost/math/special_functions.hpp>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
 namespace {
+// Error policy for all Boost.Math calls: report domain/pole/overflow/evaluation
+// errors via errno (errno_on_error) instead of throwing. Boost sets errno to
+// EDOM (domain/pole/evaluation) or ERANGE (overflow) and returns NaN/inf. The
+// remaining categories (underflow/denorm/indeterminate) default to ignore.
+namespace __bmp = boost::math::policies;
+using __policy =
+    __bmp::policy<__bmp::domain_error<__bmp::errno_on_error>,
+                  __bmp::pole_error<__bmp::errno_on_error>,
+                  __bmp::overflow_error<__bmp::errno_on_error>,
+                  __bmp::evaluation_error<__bmp::errno_on_error>>;
+
 template <class _Ret>
 std::optional<_Ret> __check_nan() {
   return std::nullopt;
@@ -34,22 +46,36 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
   return __check_nan<_Ret>(__args...);
 }
 
+// Shared back-end for the C++17 mathematical special functions ([sf.cmath]).
+// Boost.Math is the compute kernel; this wrapper enforces the standard's
+// error-reporting rules ([sf.cmath.general]):
+//   1. NaN argument -> return NaN, do NOT report a domain error (the
+//      __check_nan pre-filter below).
+//   2. domain/range error -> report per <cmath> math_errhandling: errno = EDOM
+//      (domain/pole) or ERANGE (overflow), done by Boost's errno_on_error
+//      policy; and, when MATH_ERREXCEPT is set, raise the matching <cfenv>
+//      exception (done here, since Boost never touches <cfenv>).
+// Promotion: Boost's default promote_float=true computes float inputs in double
+// and rounds once -- more accurate and overflow-resistant, matching the
+// existing std::hermite(float) approach. We keep it.
 template <class _Func, class... _Args, class _Ret = std::invoke_result_t<_Func, _Args...>>
 _Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return *__maybe_nan;
 
-  try {
-    return __f(__args...);
-  } catch (...) {
-#  if math_errhandling & MATH_ERRNO
-    errno = EDOM;
+#  if math_errhandling & MATH_ERREXCEPT
+  errno = 0;
 #  endif
+  _Ret __ret = __f(__args..., __policy{});
+  // Boost set errno via the policy; mirror it onto <cfenv>:
+  // EDOM (domain/pole) -> FE_INVALID, ERANGE (overflow) -> FE_OVERFLOW.
 #  if math_errhandling & MATH_ERREXCEPT
+  if (errno == EDOM)
     std::feraiseexcept(FE_INVALID);
+  else if (errno == ERANGE)
+    std::feraiseexcept(FE_OVERFLOW);
 #  endif
-    return std::numeric_limits<_Ret>::quiet_NaN();
-  }
+  return __ret;
 }
 } // namespace
 

>From 2c13b0df2188c488e3f2fe235ba22569419e0e5b Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 16:44:35 +0200
Subject: [PATCH 24/56] TU: remove fenv support.

---
 libcxx/src/mathematical_special_functions.cpp | 25 +++++--------------
 1 file changed, 6 insertions(+), 19 deletions(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index f8fea7ff1b70e..3c0af96fb0c19 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -8,8 +8,6 @@
 
 #include <__config>
 #include <__math/special_functions.h>
-#include <cerrno>
-#include <cfenv>
 #include <cmath>
 #include <optional>
 #include <type_traits>
@@ -51,10 +49,11 @@ std::optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
 // error-reporting rules ([sf.cmath.general]):
 //   1. NaN argument -> return NaN, do NOT report a domain error (the
 //      __check_nan pre-filter below).
-//   2. domain/range error -> report per <cmath> math_errhandling: errno = EDOM
-//      (domain/pole) or ERANGE (overflow), done by Boost's errno_on_error
-//      policy; and, when MATH_ERREXCEPT is set, raise the matching <cfenv>
-//      exception (done here, since Boost never touches <cfenv>).
+//   2. domain/range error -> reported via errno only: Boost's errno_on_error
+//      policy sets errno = EDOM (domain/pole/evaluation) or ERANGE (overflow).
+//      The <cfenv> floating-point-exception side of math_errhandling
+//      (MATH_ERREXCEPT) is intentionally not mirrored, matching the shipped
+//      std::hermite and libstdc++'s special-function implementations.
 // Promotion: Boost's default promote_float=true computes float inputs in double
 // and rounds once -- more accurate and overflow-resistant, matching the
 // existing std::hermite(float) approach. We keep it.
@@ -63,19 +62,7 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
   if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
     return *__maybe_nan;
 
-#  if math_errhandling & MATH_ERREXCEPT
-  errno = 0;
-#  endif
-  _Ret __ret = __f(__args..., __policy{});
-  // Boost set errno via the policy; mirror it onto <cfenv>:
-  // EDOM (domain/pole) -> FE_INVALID, ERANGE (overflow) -> FE_OVERFLOW.
-#  if math_errhandling & MATH_ERREXCEPT
-  if (errno == EDOM)
-    std::feraiseexcept(FE_INVALID);
-  else if (errno == ERANGE)
-    std::feraiseexcept(FE_OVERFLOW);
-#  endif
-  return __ret;
+  return __f(__args..., __policy{});
 }
 } // namespace
 

>From 580076b2ffe777b1f00d844091f88ebd03820402 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:24:36 +0200
Subject: [PATCH 25/56] test: add more tests; fix header includes

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index eb747efb4c1c6..079751321f3b9 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,11 +14,11 @@
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
 
 #include <cassert>
-#include <cerrno>
-#include <cfenv>
 #include <cmath>
+#include <limits>
 
 #include "common.h"
+#include "test_macros.h"
 #include "type_algorithms.h"
 
 struct TestFloatingPoint {
@@ -32,9 +32,19 @@ struct TestFloatingPoint {
 
     assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
 
+    // m == 0 reduces to the ordinary Laguerre polynomial: L_2(2) = (4 - 8 + 2) / 2 = -1.
+    assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
+
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
+    // noexcept is a libc++ extension here ([sf.cmath] does not mandate it), so use the libc++-only assertion.
+    LIBCPP_ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, T(0.)));
+
     check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
+
+    // The associated Laguerre polynomials are defined for all real x: a negative
+    // argument is in-domain and must not report a domain error. L_1^0(-1) = 1 - x = 2.
+    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, -1.f), 2.01f)); });
   }
 };
 

>From 2a6dfd4ae296d0f89e1ea5bc953d7d5f8f536ca9 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:53:16 +0200
Subject: [PATCH 26/56] test: add noexcept.compile.pass test

---
 .../c.math/sf.cmath/noexcept.compile.pass.cpp | 20 +++++++++++++++++++
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   |  3 ---
 2 files changed, 20 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..9994e6a018f8d
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// Check that functions are marked noexcept
+
+#include <cmath>
+
+#include "test_macros.h"
+
+void test() {
+  // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
+}
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 079751321f3b9..068ca81e04b83 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -37,9 +37,6 @@ struct TestFloatingPoint {
 
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
-    // noexcept is a libc++ extension here ([sf.cmath] does not mandate it), so use the libc++-only assertion.
-    LIBCPP_ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, T(0.)));
-
     check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
 
     // The associated Laguerre polynomials are defined for all real x: a negative

>From 7b8204c6a252f2971f288212096aee6641c14d82 Mon Sep 17 00:00:00 2001
From: Paul <a at a.a>
Date: Sun, 28 Jun 2026 17:55:16 +0200
Subject: [PATCH 27/56] test: cleanup

---
 .../test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 7928ebf50681a..a4c471e4b3168 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -15,6 +15,6 @@
 void test() {
   // clang-format off
   // assoc_laguerre
-  std::assoc_laguerref(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   // clang-format on
 }

>From 82d264a4bc5a0a1f6d4c932b97b4a74c11957fac Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 28 Jun 2026 23:37:36 +0200
Subject: [PATCH 28/56] test: remove fenv support

---
 libcxx/test/std/numerics/c.math/sf.cmath/common.h | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
index 867e6af6f6914..723422bf7038d 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/common.h
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -12,8 +12,6 @@
 #include <cassert>
 #include <cerrno>
 #include <cmath>
-#include <math.h>
-#include <cfenv>
 
 // std::type_identity is C++20 (we need to support C++17 here)
 template <class T>
@@ -32,34 +30,22 @@ template <class Func>
 void check_no_domain_error(Func f) {
 #if math_errhandling & MATH_ERRNO
   errno = EACCES;
-#endif
-#if math_errhandling & MATH_ERREXCEPT
-  std::feclearexcept(FE_INVALID);
 #endif
   f();
 #if math_errhandling & MATH_ERRNO
   assert(errno == EACCES);
 #endif
-#if math_errhandling & MATH_ERREXCEPT
-  assert(!std::fetestexcept(FE_INVALID));
-#endif
 }
 
 template <class Func>
 void check_domain_error(Func f) {
 #if math_errhandling & MATH_ERRNO
   errno = EACCES;
-#endif
-#if math_errhandling & MATH_ERREXCEPT
-  std::feclearexcept(FE_INVALID);
 #endif
   f();
 #if math_errhandling & MATH_ERRNO
   assert(errno == EDOM);
 #endif
-#if math_errhandling & MATH_ERREXCEPT
-  assert(std::fetestexcept(FE_INVALID));
-#endif
 }
 
 #endif // TEST_SF_CMATH_COMMON_H

>From 6b65307f26de7474c6b3f6789c8bd49e9b9787d7 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sun, 28 Jun 2026 23:37:54 +0200
Subject: [PATCH 29/56] test: cleanups + more NaN tests

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 28 ++++++++++---------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 068ca81e04b83..fffbb72557a5a 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -18,30 +18,32 @@
 #include <limits>
 
 #include "common.h"
-#include "test_macros.h"
 #include "type_algorithms.h"
 
 struct TestFloatingPoint {
   template <class T>
   void operator()() const {
+    // sample value testing
     assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-
     assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
-
-    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
-
-    assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
-
-    // m == 0 reduces to the ordinary Laguerre polynomial: L_2(2) = (4 - 8 + 2) / 2 = -1.
     assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
+    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
+    assert(between(60.124f, std::assoc_laguerref(2, 10, T(0.5)), 60.126f));
 
+    // return type: float
     static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
 
-    check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
-
-    // The associated Laguerre polynomials are defined for all real x: a negative
-    // argument is in-domain and must not report a domain error. L_1^0(-1) = 1 - x = 2.
-    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, -1.f), 2.01f)); });
+    // NaN input -> NaN output (w/o domain error)
+    auto check_nan = [](T nan) {
+      check_no_domain_error([nan] { assert(std::isnan(std::assoc_laguerref(0, 0, nan))); });
+    };
+    if (std::numeric_limits<T>::has_quiet_NaN)
+      check_nan(std::numeric_limits<T>::quiet_NaN());
+    if (std::numeric_limits<T>::has_signaling_NaN)
+      check_nan(std::numeric_limits<T>::signaling_NaN());
+
+    // negative x: no domain error
+    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, T(-1)), 2.01f)); });
   }
 };
 

>From ddd4eef0a09edb02e8e486fb587bf5e64d538b65 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 00:00:33 +0200
Subject: [PATCH 30/56] impl assoc_laguerrel

---
 libcxx/include/__math/special_functions.h     | 1 +
 libcxx/src/mathematical_special_functions.cpp | 5 +++++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 89c75cfc04585..0cfd484955b15 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -27,6 +27,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 // assoc_laguerre
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept;
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 3c0af96fb0c19..980af019748db 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -66,10 +66,15 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
 }
 } // namespace
 
+// assoc_laguerre
 float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept {
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From a35b978e0601bea7a9accf072c175a4c8b182c97 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 00:23:48 +0200
Subject: [PATCH 31/56] test: assoc_laguerrel

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 78 ++++++++-----------
 1 file changed, 34 insertions(+), 44 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index fffbb72557a5a..0a9abaf2f4f24 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -9,54 +9,44 @@
 // REQUIRES: std-at-least-c++17
 
 // <cmath>
-//
-// [sf.cmath.assoc.laguerre], associated Laguerre polynomials
-// float               assoc_laguerref(unsigned n, unsigned m, float x);
+
+// float       assoc_laguerref(unsigned n, unsigned m, float x);
+// long double assoc_laguerrel(unsigned n, unsigned m, long double x);
 
 #include <cassert>
 #include <cmath>
 #include <limits>
 
 #include "common.h"
-#include "type_algorithms.h"
-
-struct TestFloatingPoint {
-  template <class T>
-  void operator()() const {
-    // sample value testing
-    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-    assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
-    assert(between(-1.01f, std::assoc_laguerref(2, 0, T(2.)), -0.99f));
-    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
-    assert(between(60.124f, std::assoc_laguerref(2, 10, T(0.5)), 60.126f));
-
-    // return type: float
-    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
-
-    // NaN input -> NaN output (w/o domain error)
-    auto check_nan = [](T nan) {
-      check_no_domain_error([nan] { assert(std::isnan(std::assoc_laguerref(0, 0, nan))); });
-    };
-    if (std::numeric_limits<T>::has_quiet_NaN)
-      check_nan(std::numeric_limits<T>::quiet_NaN());
-    if (std::numeric_limits<T>::has_signaling_NaN)
-      check_nan(std::numeric_limits<T>::signaling_NaN());
-
-    // negative x: no domain error
-    check_no_domain_error([] { assert(between(1.99f, std::assoc_laguerref(1, 0, T(-1)), 2.01f)); });
-  }
-};
-
-struct TestIntegral {
-  template <class T>
-  void operator()() const {
-    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
-
-    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
-  }
-};
-
-int main() {
-  types::for_each(types::floating_point_types{}, TestFloatingPoint{});
-  types::for_each(types::integral_types{}, TestIntegral{});
+
+// Each suffixed overload is fixed to a single floating-point type, so it is
+// tested directly with that type. The unsuffixed std::assoc_laguerre overloads
+// (and the integer-argument overload) are not implemented yet.
+template <class T, class Func>
+void test(Func assoc_laguerre) {
+  // sample values
+  assert(between(0.99, assoc_laguerre(0, 0, T(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, T(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, T(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, T(2)), 0.01));
+  assert(between(60.124, assoc_laguerre(2, 10, T(0.5)), 60.126));
+
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, T(0))), T>);
+
+  // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
+  auto test_nan = [&](T nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
+  if (std::numeric_limits<T>::has_quiet_NaN)
+    test_nan(std::numeric_limits<T>::quiet_NaN());
+  if (std::numeric_limits<T>::has_signaling_NaN)
+    test_nan(std::numeric_limits<T>::signaling_NaN());
+
+  // A negative argument is in the domain (no domain error).
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, T(-1)), 2.01)); });
 }
+
+int main(int, char**) {
+  test<float>([](unsigned __n, unsigned __m, float __x) { return std::assoc_laguerref(__n, __m, __x); });
+  test<long double>([](unsigned __n, unsigned __m, long double __x) { return std::assoc_laguerrel(__n, __m, __x); });
+
+  return 0;
+}
\ No newline at end of file

>From 96e8004c4a6f41343f1257898d04efbbd01f6457 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 21:43:26 +0200
Subject: [PATCH 32/56] re-introduce __math::__assoc_laguerre -> only ABI
 export internal API

---
 libcxx/include/__math/special_functions.h     | 16 ++++++++++++++--
 libcxx/src/mathematical_special_functions.cpp |  7 +++++--
 2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 0cfd484955b15..62ae09594bea3 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
 
 #include <__config>
+#include <__configuration/attributes.h>
 #include <__math/copysign.h>
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
@@ -26,8 +27,19 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
 // assoc_laguerre
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept;
+namespace __math {
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
+} // namespace __math
+
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
+assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
 
 #endif // _LIBCPP_STD_VER >= 17
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 980af019748db..ed8109c7e482c 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -19,6 +19,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 #if _LIBCPP_STD_VER >= 17
 
+namespace __math {
 namespace {
 // Error policy for all Boost.Math calls: report domain/pole/overflow/evaluation
 // errors via errno (errno_on_error) instead of throwing. Boost sets errno to
@@ -67,14 +68,16 @@ _Ret __invoke_boost_math(_Func __f, _Args... __args) {
 } // namespace
 
 // assoc_laguerre
-float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+float __assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
-long double assoc_laguerrel(unsigned int __n, unsigned int __m, long double __x) noexcept {
+long double __assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+} // namespace __math
+
 #endif
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From 34d62b1da4598efb45c79363654566b9ca6677aa Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 21:43:57 +0200
Subject: [PATCH 33/56] add exported ABI symbols to abilist

---
 ...-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...nux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...werpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...rpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...nux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...known-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...own-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 4 +++-
 ...n-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 2 ++
 9 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 74283373d1b65..7889109825e10 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1700,6 +1700,8 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 291149608c9c7..3e139e30496a6 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1338,6 +1338,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index da6d608af462c..304817ba0cffb 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -686,6 +686,8 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEyPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock11from_time_tEi', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6ee31613d5320..d5e37635fa511 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -686,6 +686,8 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock11from_time_tEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12system_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 92a37db24ac5f..a59b72d695b55 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1690,6 +1690,8 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 7b09d3dd3f75c..abef314dc14f4 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1338,6 +1338,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 725ed669fb561..86bc3dec5bd34 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1350,6 +1350,8 @@
 {'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 160, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 717d4c9bee726..11330e7eaf442 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1349,6 +1349,8 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}
@@ -2030,4 +2032,4 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 45db143b4cf4a..460bd21a2fecf 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1320,6 +1320,8 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}

>From 7e1ed83abf9818cb95663ed9077215f102873666 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:10:37 +0200
Subject: [PATCH 34/56] impl std::assoc_laguerre(double)

---
 libcxx/include/__math/special_functions.h     | 6 ++++++
 libcxx/src/mathematical_special_functions.cpp | 4 ++++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 782e441562c00..8fd363919e5cc 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -30,6 +30,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 // assoc_laguerre
 namespace __math {
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI double __assoc_laguerre(unsigned, unsigned, double) noexcept;
 [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
@@ -37,6 +38,11 @@ namespace __math {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+template <class = int>
+_LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
 assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index ed8109c7e482c..0f6e3a850ad73 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -72,6 +72,10 @@ float __assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }
 
+double __assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+  return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+
 long double __assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __invoke_boost_math([](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
 }

>From 8b1f5f6ded44a8cbd009c03310faa645d320bb97 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:20:15 +0200
Subject: [PATCH 35/56] fixup impl std::assoc_laguerre(double)

---
 libcxx/include/__math/special_functions.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 8fd363919e5cc..b48b9ffdb129c 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -39,7 +39,7 @@ namespace __math {
 }
 
 template <class = int>
-_LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 

>From fbbd70784b3e8bc65aa5e3b18870d936ea438e2e Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:43:07 +0200
Subject: [PATCH 36/56] fixup: assoc_laguerre(dbl)

---
 libcxx/include/__math/special_functions.h                   | 3 +--
 .../std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp    | 6 +++---
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index b48b9ffdb129c..20268dd1bd7d6 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -38,8 +38,7 @@ namespace __math {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-template <class = int>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 0a9abaf2f4f24..b9223f6d66a2c 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -19,9 +19,9 @@
 
 #include "common.h"
 
-// Each suffixed overload is fixed to a single floating-point type, so it is
-// tested directly with that type. The unsuffixed std::assoc_laguerre overloads
-// (and the integer-argument overload) are not implemented yet.
+// Each overload is fixed to a single floating-point type, so it is tested
+// directly with that type. The integer-argument overload
+// (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
 template <class T, class Func>
 void test(Func assoc_laguerre) {
   // sample values

>From 7f5e5515ad7c1e551ff1b7cecf57772211f3b376 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:12:00 +0200
Subject: [PATCH 37/56] test: general cleanups

---
 .../std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp    | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index b9223f6d66a2c..d446e017494c4 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -45,8 +45,8 @@ void test(Func assoc_laguerre) {
 }
 
 int main(int, char**) {
-  test<float>([](unsigned __n, unsigned __m, float __x) { return std::assoc_laguerref(__n, __m, __x); });
-  test<long double>([](unsigned __n, unsigned __m, long double __x) { return std::assoc_laguerrel(__n, __m, __x); });
+  test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
   return 0;
-}
\ No newline at end of file
+}

>From 340ed9ca7b9f543fd3ffa8110f29863dc7fcef4f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:14:47 +0200
Subject: [PATCH 38/56] test: std::assoc_laguerre(double)

---
 .../test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp  | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index d446e017494c4..9c2bc2026a348 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -10,6 +10,7 @@
 
 // <cmath>
 
+// double      assoc_laguerre(unsigned n, unsigned m, double x);
 // float       assoc_laguerref(unsigned n, unsigned m, float x);
 // long double assoc_laguerrel(unsigned n, unsigned m, long double x);
 
@@ -45,6 +46,8 @@ void test(Func assoc_laguerre) {
 }
 
 int main(int, char**) {
+  test<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+
   test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
   test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 

>From 589d01b5fce70f06068b3304556145bc1c284ebe Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:20:51 +0200
Subject: [PATCH 39/56] test: nodiscard/noexcept for assoc_laguerre(double),
 assoc_laguerrel

---
 .../test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp  | 3 +++
 .../libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp  | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index a4c471e4b3168..8b8826d03de01 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -15,6 +15,9 @@
 void test() {
   // clang-format off
   // assoc_laguerre
+  std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   // clang-format on
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 9994e6a018f8d..70984fc0c83bf 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -16,5 +16,8 @@
 
 void test() {
   // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0));
+
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
+  ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
 }

>From db297e6b6a155fdd5ea0bb4978f5e267c14a773e Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 22:42:41 +0200
Subject: [PATCH 40/56] add ABI symbol to abilist for assoc_laguerre(double)

---
 ...m64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 .../powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...owerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ..._64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...nknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 +
 ...nown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 1 +
 9 files changed, 9 insertions(+)

diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6ab143c0113f3..9f70dd6550fc7 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1701,6 +1701,7 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 08d42d44e4827..4152a583ec5a3 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1339,6 +1339,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 98fd189eaa3f7..861750f606c77 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -687,6 +687,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEyPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 9e1db57fc315b..347d9f9f23d70 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -687,6 +687,7 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16chrono12steady_clock3nowEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index a59b72d695b55..cd3f3e4fda9a2 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1690,6 +1690,7 @@
 {'is_defined': True, 'name': '__ZNSt3__15wcoutE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__itoa8__u64toaEyPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 61bd10d997a1b..421f4839f762e 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1339,6 +1339,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 286ad56406ee2..d59e224c42922 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1351,6 +1351,7 @@
 {'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 160, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 9fd2edf63e6ef..d2b084bace550 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1350,6 +1350,7 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 51c27fea37ce9..7e57931398e85 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1321,6 +1321,7 @@
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}

>From e8984b706592b5b2ff08bdd19020330498423157 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:12:02 +0200
Subject: [PATCH 41/56] impl assoc_laguerre(Int)

---
 libcxx/include/__math/special_functions.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 20268dd1bd7d6..5021e5a54b634 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -47,6 +47,11 @@ assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
+template <class _Int, std::enable_if_t<std::is_integral_v<_Int>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 // hermite

>From c8c5eb6a2040040b6a5c67c42128172a4c5661a8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:18:00 +0200
Subject: [PATCH 42/56] test: nodiscard/except assoc_laguerre(Int)

---
 .../numerics/c.math/sf.cmath/nodiscard.verify.cpp      |  1 +
 .../numerics/c.math/sf.cmath/noexcept.compile.pass.cpp | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 8b8826d03de01..b83070f4f984d 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -16,6 +16,7 @@ void test() {
   // clang-format off
   // assoc_laguerre
   std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 70984fc0c83bf..cca9c1e924a8c 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -13,6 +13,14 @@
 #include <cmath>
 
 #include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestIntegral {
+  template <class T>
+  void operator()() const {
+    ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, T{}));
+  }
+};
 
 void test() {
   // assoc_laguerre
@@ -20,4 +28,6 @@ void test() {
 
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
   ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
+
+  types::for_each(types::integral_types{}, TestIntegral{});
 }

>From 5946b7d3c07ea7f4dc898e9750313838fd3a99a8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Mon, 29 Jun 2026 23:19:29 +0200
Subject: [PATCH 43/56] wip: test: assoc_laguerre(Int)

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 48 ++++++++++++-------
 1 file changed, 32 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 9c2bc2026a348..b39e20fe6288a 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -17,39 +17,55 @@
 #include <cassert>
 #include <cmath>
 #include <limits>
+#include <type_traits>
 
 #include "common.h"
+#include "type_algorithms.h"
 
 // Each overload is fixed to a single floating-point type, so it is tested
 // directly with that type. The integer-argument overload
 // (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
-template <class T, class Func>
+template <class ExpectedFuncRet, class FuncArgX, class Func>
 void test(Func assoc_laguerre) {
   // sample values
-  assert(between(0.99, assoc_laguerre(0, 0, T(0)), 1.01));
-  assert(between(0.99, assoc_laguerre(1, 1, T(1)), 1.01));
-  assert(between(-1.01, assoc_laguerre(2, 0, T(2)), -0.99));
-  assert(between(-0.01, assoc_laguerre(2, 2, T(2)), 0.01));
-  assert(between(60.124, assoc_laguerre(2, 10, T(0.5)), 60.126));
+  assert(between(0.99, assoc_laguerre(0, 0, FuncArgX(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, FuncArgX(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, FuncArgX(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, FuncArgX(2)), 0.01));
+  if constexpr (std::is_floating_point_v<FuncArgX>)
+    assert(between(60.124, assoc_laguerre(2, 10, FuncArgX(0.5)), 60.126));
 
-  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, T(0))), T>);
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, FuncArgX(0))), ExpectedFuncRet>);
 
   // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
-  auto test_nan = [&](T nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
-  if (std::numeric_limits<T>::has_quiet_NaN)
-    test_nan(std::numeric_limits<T>::quiet_NaN());
-  if (std::numeric_limits<T>::has_signaling_NaN)
-    test_nan(std::numeric_limits<T>::signaling_NaN());
+  if constexpr (std::is_floating_point_v<FuncArgX>) {
+    auto test_nan = [&](FuncArgX nan) {
+      check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); });
+    };
+    if (std::numeric_limits<FuncArgX>::has_quiet_NaN)
+      test_nan(std::numeric_limits<FuncArgX>::quiet_NaN());
+    if (std::numeric_limits<FuncArgX>::has_signaling_NaN)
+      test_nan(std::numeric_limits<FuncArgX>::signaling_NaN());
+  }
 
   // A negative argument is in the domain (no domain error).
-  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, T(-1)), 2.01)); });
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, FuncArgX(-1)), 2.01)); });
 }
 
+struct TestIntegral {
+  template <class Int>
+  void operator()() const {
+    test<double, Int>([](unsigned n, unsigned m, Int x) { return std::assoc_laguerre(n, m, x); });
+  }
+};
+
 int main(int, char**) {
-  test<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+  test<double, double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+
+  test<float, float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test<long double, long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
-  test<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
-  test<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
+  // types::for_each(types::integral_types{}, TestIntegral{}); WIP
 
   return 0;
 }

>From 619773ba99f60f0d799ca98488fa71cc246e2c47 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 17:48:09 +0200
Subject: [PATCH 44/56] fix gcc build: gcc defines __STDCPP_FLOATXX_T__ even
 though we dont ship <stdfloat> yet -> breaks Boost

---
 libcxx/src/mathematical_special_functions.cpp | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index 0f6e3a850ad73..b8e14967740f1 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -12,6 +12,22 @@
 #include <optional>
 #include <type_traits>
 
+// GCC defines __STDCPP_FLOATnn_T__ whenever the _Floatnn extended types exist at the
+// language level, independent of the standard library. libc++ currently ships no
+// <stdfloat>, so std::floatnn_t is never declared -- but Boost.Math keys its
+// std::floatnn_t overloads off these macros and would reference the missing types.
+// Suppress those overloads while <stdfloat> is unavailable. The __has_include guard is
+// the same condition Boost uses to include <stdfloat>, so this workaround disables itself
+// automatically once libc++ provides the header (the overloads then light up on their own
+// -- no manual re-enable needed here).
+#if !__has_include(<stdfloat>)
+#  undef __STDCPP_FLOAT16_T__
+#  undef __STDCPP_FLOAT32_T__
+#  undef __STDCPP_FLOAT64_T__
+#  undef __STDCPP_FLOAT128_T__
+#  undef __STDCPP_BFLOAT16_T__
+#endif
+
 #define BOOST_MATH_NO_EXCEPTIONS
 #include <boost/math/special_functions.hpp>
 

>From 3c8e6c21dca99ac9b83fe264c70255710d20c21f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 18:01:47 +0200
Subject: [PATCH 45/56] fix android abilist

---
 ...linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 421f4839f762e..08b901a94f394 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1340,8 +1340,8 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjd', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjje', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjf', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk16__math16__assoc_laguerreEjjg', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_', 'type': 'FUNC'}

>From e4c48175cfc00429e93c92a30aa81e67201f4866 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 18:02:21 +0200
Subject: [PATCH 46/56] fix boost math thread support

---
 libcxx/src/mathematical_special_functions.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index b8e14967740f1..c37811bdf52ea 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -28,6 +28,14 @@
 #  undef __STDCPP_BFLOAT16_T__
 #endif
 
+// Boost.Math detects thread support via __has_include(<thread>/<mutex>/...), but libc++
+// ships those headers even when threads are disabled (_LIBCPP_HAS_THREADS == 0), so the
+// detection wrongly enables std::mutex use and breaks on no-thread targets (e.g. picolibc).
+// Tell Boost there are no threads; lazy-init tables don't need locking without threads.
+#if !_LIBCPP_HAS_THREADS
+#  define BOOST_MATH_DISABLE_THREADS
+#endif
+
 #define BOOST_MATH_NO_EXCEPTIONS
 #include <boost/math/special_functions.hpp>
 

>From 60635ce92abd69cad3a5161a78a5a2f55281d9b8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 21:47:33 +0200
Subject: [PATCH 47/56] test: fix apple's math_errhandling usage. runtime not
 macro constant

---
 .../std/numerics/c.math/sf.cmath/common.h     | 23 +++++++++----------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
index 723422bf7038d..ca6de9ea868bd 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/common.h
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -26,26 +26,25 @@ bool between(type_identity_t<T> lower, T value, type_identity_t<T> upper) {
   return lower < value && value < upper;
 }
 
+// Note: math_errhandling must be queried with a runtime `if`, not `#if`. On Apple
+// platforms it is defined as a function call (__math_errhandling()), so it is not a
+// preprocessor constant and would break `#if` under -Wundef.
 template <class Func>
 void check_no_domain_error(Func f) {
-#if math_errhandling & MATH_ERRNO
-  errno = EACCES;
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    errno = EACCES;
   f();
-#if math_errhandling & MATH_ERRNO
-  assert(errno == EACCES);
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    assert(errno == EACCES);
 }
 
 template <class Func>
 void check_domain_error(Func f) {
-#if math_errhandling & MATH_ERRNO
-  errno = EACCES;
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    errno = EACCES;
   f();
-#if math_errhandling & MATH_ERRNO
-  assert(errno == EDOM);
-#endif
+  if (math_errhandling & MATH_ERRNO)
+    assert(errno == EDOM);
 }
 
 #endif // TEST_SF_CMATH_COMMON_H

>From 8201618481b60722093b07eff23b8bcb3b274e05 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:16:59 +0200
Subject: [PATCH 48/56] minimze boost includes

---
 libcxx/src/mathematical_special_functions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index c37811bdf52ea..dc046b3a845df 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -37,7 +37,7 @@
 #endif
 
 #define BOOST_MATH_NO_EXCEPTIONS
-#include <boost/math/special_functions.hpp>
+#include <boost/math/special_functions/laguerre.hpp>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS

>From 4d11bdbe96fca5653a71146ff3fea197180f13ef Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:22:46 +0200
Subject: [PATCH 49/56] patch boost: make it compilable w/o localization
 support

---
 third-party/boost-math-libcxx.patch | 47 +++++++++++++++++++++++++++++
 third-party/update_boost_math.sh    | 10 ++++++
 2 files changed, 57 insertions(+)
 create mode 100644 third-party/boost-math-libcxx.patch

diff --git a/third-party/boost-math-libcxx.patch b/third-party/boost-math-libcxx.patch
new file mode 100644
index 0000000000000..f7692a7e54b67
--- /dev/null
+++ b/third-party/boost-math-libcxx.patch
@@ -0,0 +1,47 @@
+# Local libc++ patch for the vendored Boost.Math snapshot.
+#
+# Boost.Math only uses <sstream>/<iomanip> and detail::prec_format to build the
+# what() text of the exceptions it throws. That machinery sits OUTSIDE Boost's own
+# `#ifndef BOOST_MATH_NO_EXCEPTIONS` guard (unlike the adjacent <stdexcept> /
+# throw_exception.hpp includes), so it is compiled even when exceptions are disabled.
+# libc++ builds the Math Special Functions back-end with BOOST_MATH_NO_EXCEPTIONS,
+# and on configurations without localization (e.g. -DLIBCXX_ENABLE_LOCALIZATION=OFF)
+# libc++ ships no functional <sstream>, so prec_format fails to compile.
+#
+# This patch guards the iostream-only message formatting under BOOST_MATH_NO_EXCEPTIONS.
+# Reported upstream: https://github.com/boostorg/math/issues (see PR description).
+# Re-applied by update_boost_math.sh after each snapshot refresh; drop once upstream.
+diff --git a/boost-math/include/boost/math/policies/error_handling.hpp b/boost-math/include/boost/math/policies/error_handling.hpp
+index d7ba694564ba..da167171f650 100644
+--- a/boost-math/include/boost/math/policies/error_handling.hpp
++++ b/boost-math/include/boost/math/policies/error_handling.hpp
+@@ -26,9 +26,9 @@
+ #include <complex>
+ #include <cstdint>
+ #include <cstring>
++#ifndef BOOST_MATH_NO_EXCEPTIONS
+ #include <iomanip>
+ #include <sstream>
+-#ifndef BOOST_MATH_NO_EXCEPTIONS
+ #include <stdexcept>
+ #endif
+ #include <string>
+@@ -95,6 +95,10 @@ T user_indeterminate_result_error(const char* function, const char* message, con
+ namespace detail
+ {
+ 
++// prec_format is only used to build exception what() messages, so it is gated on
++// exceptions being enabled. It relies on <sstream>, which is unavailable in some
++// standard-library configurations (e.g. libc++ built without localization).
++#ifndef BOOST_MATH_NO_EXCEPTIONS
+ template <class T>
+ inline std::string prec_format(const T& val)
+ {
+@@ -125,6 +129,7 @@ inline std::string prec_format<std::float128_t>(const std::float128_t& val)
+ }
+ 
+ #endif
++#endif // BOOST_MATH_NO_EXCEPTIONS
+ 
+ inline void replace_all_in_string(std::string& result, const char* what, const char* with)
+ {
diff --git a/third-party/update_boost_math.sh b/third-party/update_boost_math.sh
index 27d8816b7cb12..a4276cdb74028 100755
--- a/third-party/update_boost_math.sh
+++ b/third-party/update_boost_math.sh
@@ -40,3 +40,13 @@ sed -i 's|target_include_directories(boost_math INTERFACE include)|target_includ
 grep -q 'target_include_directories(boost_math SYSTEM INTERFACE include)' \
     ${SCRIPT_DIR}/boost-math/CMakeLists.txt \
     || { echo "ERROR: SYSTEM include patch failed -- upstream CMakeLists.txt structure changed"; exit 1; }
+
+# Guard Boost.Math's iostream-only exception-message formatting (prec_format / <sstream>)
+# under BOOST_MATH_NO_EXCEPTIONS, so the no-exceptions back-end builds without <sstream>.
+# Needed for libc++ configurations without localization. Drop once fixed upstream.
+patch -p1 -d ${SCRIPT_DIR} < ${SCRIPT_DIR}/boost-math-libcxx.patch
+
+# Verify the patch landed -- fail loudly if upstream restructured error_handling.hpp.
+grep -q 'prec_format is only used to build exception' \
+    ${SCRIPT_DIR}/boost-math/include/boost/math/policies/error_handling.hpp \
+    || { echo "ERROR: no-exceptions iostream patch failed -- upstream error_handling.hpp structure changed"; exit 1; }

>From 276df4abe72a9ba6d4979d5922b466445aa406b9 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:32:27 +0200
Subject: [PATCH 50/56] apply boost patch

---
 .../include/boost/math/policies/error_handling.hpp         | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/third-party/boost-math/include/boost/math/policies/error_handling.hpp b/third-party/boost-math/include/boost/math/policies/error_handling.hpp
index d7ba694564ba4..da167171f650d 100644
--- a/third-party/boost-math/include/boost/math/policies/error_handling.hpp
+++ b/third-party/boost-math/include/boost/math/policies/error_handling.hpp
@@ -26,9 +26,9 @@
 #include <complex>
 #include <cstdint>
 #include <cstring>
+#ifndef BOOST_MATH_NO_EXCEPTIONS
 #include <iomanip>
 #include <sstream>
-#ifndef BOOST_MATH_NO_EXCEPTIONS
 #include <stdexcept>
 #endif
 #include <string>
@@ -95,6 +95,10 @@ T user_indeterminate_result_error(const char* function, const char* message, con
 namespace detail
 {
 
+// prec_format is only used to build exception what() messages, so it is gated on
+// exceptions being enabled. It relies on <sstream>, which is unavailable in some
+// standard-library configurations (e.g. libc++ built without localization).
+#ifndef BOOST_MATH_NO_EXCEPTIONS
 template <class T>
 inline std::string prec_format(const T& val)
 {
@@ -125,6 +129,7 @@ inline std::string prec_format<std::float128_t>(const std::float128_t& val)
 }
 
 #endif
+#endif // BOOST_MATH_NO_EXCEPTIONS
 
 inline void replace_all_in_string(std::string& result, const char* what, const char* with)
 {

>From 6bbed03306814ee7a45418ba8e01e619852d7d94 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 30 Jun 2026 22:53:16 +0200
Subject: [PATCH 51/56] doc: add boost issue

---
 third-party/boost-math-libcxx.patch | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/third-party/boost-math-libcxx.patch b/third-party/boost-math-libcxx.patch
index f7692a7e54b67..8593b077a2db2 100644
--- a/third-party/boost-math-libcxx.patch
+++ b/third-party/boost-math-libcxx.patch
@@ -9,7 +9,7 @@
 # libc++ ships no functional <sstream>, so prec_format fails to compile.
 #
 # This patch guards the iostream-only message formatting under BOOST_MATH_NO_EXCEPTIONS.
-# Reported upstream: https://github.com/boostorg/math/issues (see PR description).
+# Reported upstream: https://github.com/boostorg/math/issues/1412
 # Re-applied by update_boost_math.sh after each snapshot refresh; drop once upstream.
 diff --git a/boost-math/include/boost/math/policies/error_handling.hpp b/boost-math/include/boost/math/policies/error_handling.hpp
 index d7ba694564ba..da167171f650 100644

>From 441f2f0089823971d515573e8f8074fddd2e32a1 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 08:34:54 +0200
Subject: [PATCH 52/56] guard via
 _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS

---
 libcxx/include/__configuration/availability.h |  5 +++++
 libcxx/include/__math/special_functions.h     | 20 ++++++++++++-------
 .../c.math/sf.cmath/nodiscard.verify.cpp      |  4 ++++
 .../c.math/sf.cmath/noexcept.compile.pass.cpp |  4 ++++
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   |  4 ++++
 .../libcxx/test/features/availability.py      |  8 ++++++++
 6 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 735af5d431cb1..72eb50721338f 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -271,6 +271,11 @@
 #define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20
 #define _LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20_ATTRIBUTE
 
+// This controls the availability of the C++17 mathematical special functions
+// ([sf.cmath]). Their implementation lives in the built library (backed by Boost.Math).
+#define _LIBCPP_AVAILABILITY_HAS_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_INTRODUCED_IN_LLVM_23
+#define _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE
+
 // This controls the availability of the C++20 time zone database.
 // The parser code is built in the library.
 #define _LIBCPP_AVAILABILITY_HAS_TZDB _LIBCPP_INTRODUCED_IN_LLVM_19
diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 5021e5a54b634..546600b72bce0 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -29,26 +29,32 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 // assoc_laguerre
 namespace __math {
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI float __assoc_laguerre(unsigned, unsigned, float) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI double __assoc_laguerre(unsigned, unsigned, double) noexcept;
-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI long double __assoc_laguerre(unsigned, unsigned, long double) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI float
+__assoc_laguerre(unsigned, unsigned, float) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI double
+__assoc_laguerre(unsigned, unsigned, double) noexcept;
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_EXPORTED_FROM_ABI long double
+__assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI double
+assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI long double
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI long double
 assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
 template <class _Int, std::enable_if_t<std::is_integral_v<_Int>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI double assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS _LIBCPP_HIDE_FROM_ABI double
+assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index b83070f4f984d..598311f20917d 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// The functions carry availability markup, so referencing them fails to compile against a
+// back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // Check that functions are marked [[nodiscard]]
 
 #include <cmath>
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index cca9c1e924a8c..8f9dd87c0ee87 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// The functions carry availability markup, so referencing them fails to compile against a
+// back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // Check that functions are marked noexcept
 
 #include <cmath>
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index b39e20fe6288a..91a7c4a3d2047 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -8,6 +8,10 @@
 
 // REQUIRES: std-at-least-c++17
 
+// These functions are implemented in the built library, so a program using them fails to
+// load against a back-deployment target whose libc++ predates them.
+// XFAIL: availability-mathematical_special_functions-missing
+
 // <cmath>
 
 // double      assoc_laguerre(unsigned n, unsigned m, double x);
diff --git a/libcxx/utils/libcxx/test/features/availability.py b/libcxx/utils/libcxx/test/features/availability.py
index c0f2a3d4cdc88..7bb1add0e9e9c 100644
--- a/libcxx/utils/libcxx/test/features/availability.py
+++ b/libcxx/utils/libcxx/test/features/availability.py
@@ -211,4 +211,12 @@
             cfg.available_features,
         ),
     ),
+    # Tests that require the C++17 mathematical special functions in the built library
+    Feature(
+        name="availability-mathematical_special_functions-missing",
+        when=lambda cfg: BooleanExpression.evaluate(
+            "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-23)",
+            cfg.available_features,
+        ),
+    ),
 ]

>From c63f3e0b04b37c5dcd6294473878de37d612a0f7 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 13:31:20 +0200
Subject: [PATCH 53/56] cleanup header includes

---
 libcxx/include/__math/special_functions.h     | 1 -
 libcxx/src/mathematical_special_functions.cpp | 1 +
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 546600b72bce0..9721430ee0c64 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -11,7 +11,6 @@
 #define _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
 
 #include <__config>
-#include <__configuration/attributes.h>
 #include <__math/copysign.h>
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index dc046b3a845df..e9f655d56aa69 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -37,6 +37,7 @@
 #endif
 
 #define BOOST_MATH_NO_EXCEPTIONS
+#include <boost/math/policies/policy.hpp>
 #include <boost/math/special_functions/laguerre.hpp>
 
 _LIBCPP_BEGIN_NAMESPACE_STD

>From 8afb315b5746d25b2c2f915d91fffa49b41720f8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 14:02:44 +0200
Subject: [PATCH 54/56] test: add integral tests; refactor

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 72 +++++++++----------
 1 file changed, 36 insertions(+), 36 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 91a7c4a3d2047..7561cb27b6020 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,62 +14,62 @@
 
 // <cmath>
 
-// double      assoc_laguerre(unsigned n, unsigned m, double x);
-// float       assoc_laguerref(unsigned n, unsigned m, float x);
-// long double assoc_laguerrel(unsigned n, unsigned m, long double x);
+// floating-point-type assoc_laguerre( unsigned n, unsigned m, floating-point-type x);
+// float               assoc_laguerref(unsigned n, unsigned m, float x              );
+// long double         assoc_laguerrel(unsigned n, unsigned m, long double x        );
 
 #include <cassert>
 #include <cmath>
+#include <initializer_list>
 #include <limits>
 #include <type_traits>
 
 #include "common.h"
 #include "type_algorithms.h"
 
-// Each overload is fixed to a single floating-point type, so it is tested
-// directly with that type. The integer-argument overload
-// (assoc_laguerre(unsigned, unsigned, Integer) -> double) is not implemented yet.
-template <class ExpectedFuncRet, class FuncArgX, class Func>
-void test(Func assoc_laguerre) {
-  // sample values
-  assert(between(0.99, assoc_laguerre(0, 0, FuncArgX(0)), 1.01));
-  assert(between(0.99, assoc_laguerre(1, 1, FuncArgX(1)), 1.01));
-  assert(between(-1.01, assoc_laguerre(2, 0, FuncArgX(2)), -0.99));
-  assert(between(-0.01, assoc_laguerre(2, 2, FuncArgX(2)), 0.01));
-  if constexpr (std::is_floating_point_v<FuncArgX>)
-    assert(between(60.124, assoc_laguerre(2, 10, FuncArgX(0.5)), 60.126));
+// Tests a fixed-precision overload (assoc_laguerre / assoc_laguerref / assoc_laguerrel),
+// whose argument and return type are both `Float`.
+template <class Float, class Func>
+void test_floating_point(Func assoc_laguerre) {
+  // return type is the argument type
+  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, Float(0))), Float>);
 
-  static_assert(std::is_same_v<decltype(assoc_laguerre(0, 0, FuncArgX(0))), ExpectedFuncRet>);
+  // sample values
+  assert(between(0.99, assoc_laguerre(0, 0, Float(0)), 1.01));
+  assert(between(0.99, assoc_laguerre(1, 1, Float(1)), 1.01));
+  assert(between(-1.01, assoc_laguerre(2, 0, Float(2)), -0.99));
+  assert(between(-0.01, assoc_laguerre(2, 2, Float(2)), 0.01));
+  assert(between(60.124, assoc_laguerre(2, 10, Float(0.5)), 60.126));
 
-  // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1).
-  if constexpr (std::is_floating_point_v<FuncArgX>) {
-    auto test_nan = [&](FuncArgX nan) {
-      check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); });
-    };
-    if (std::numeric_limits<FuncArgX>::has_quiet_NaN)
-      test_nan(std::numeric_limits<FuncArgX>::quiet_NaN());
-    if (std::numeric_limits<FuncArgX>::has_signaling_NaN)
-      test_nan(std::numeric_limits<FuncArgX>::signaling_NaN());
-  }
+  // a negative argument is in the domain (no domain error)
+  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, Float(-1)), 2.01)); });
 
-  // A negative argument is in the domain (no domain error).
-  check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, FuncArgX(-1)), 2.01)); });
+  // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1)
+  auto test_nan = [&](Float nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
+  if (std::numeric_limits<Float>::has_quiet_NaN)
+    test_nan(std::numeric_limits<Float>::quiet_NaN());
+  if (std::numeric_limits<Float>::has_signaling_NaN)
+    test_nan(std::numeric_limits<Float>::signaling_NaN());
 }
 
-struct TestIntegral {
-  template <class Int>
+// Tests the integer-argument overload: it promotes the argument to double and returns double.
+struct TestInteger {
+  template <class Integer>
   void operator()() const {
-    test<double, Int>([](unsigned n, unsigned m, Int x) { return std::assoc_laguerre(n, m, x); });
+    static_assert(std::is_same_v<decltype(std::assoc_laguerre(0u, 0u, Integer(0))), double>);
+
+    // same result as the double overload with the argument cast to double
+    for (Integer x : {Integer(0), Integer(1), Integer(2)})
+      assert(std::assoc_laguerre(2, 0, x) == std::assoc_laguerre(2, 0, static_cast<double>(x)));
   }
 };
 
 int main(int, char**) {
-  test<double, double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
-
-  test<float, float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
-  test<long double, long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
+  test_floating_point<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test_floating_point<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
+  test_floating_point<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
 
-  // types::for_each(types::integral_types{}, TestIntegral{}); WIP
+  types::for_each(types::integral_types{}, TestInteger{});
 
   return 0;
 }

>From 7236c0aa7861a04227195f8fa88d0f7efde7267c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 14:21:17 +0200
Subject: [PATCH 55/56] impl assoc_laguerre(float/long double) + tests

---
 libcxx/include/__math/special_functions.h          | 14 ++++++++++++--
 .../numerics/c.math/sf.cmath/nodiscard.verify.cpp  |  6 ++++--
 .../c.math/sf.cmath/noexcept.compile.pass.cpp      | 13 +++----------
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        |  2 ++
 4 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 9721430ee0c64..8bb805d721b5f 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -37,7 +37,7 @@ __assoc_laguerre(unsigned, unsigned, long double) noexcept;
 } // namespace __math
 
 [[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI float
-assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+assoc_laguerre(unsigned __n, unsigned __m, float __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
@@ -47,7 +47,7 @@ assoc_laguerre(unsigned __n, unsigned __m, double __x) noexcept {
 }
 
 [[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI long double
-assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
+assoc_laguerre(unsigned __n, unsigned __m, long double __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, __x);
 }
 
@@ -57,6 +57,16 @@ assoc_laguerre(unsigned __n, unsigned __m, _Int __x) noexcept {
   return __math::__assoc_laguerre(__n, __m, static_cast<double>(__x));
 }
 
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI float
+assoc_laguerref(unsigned __n, unsigned __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
+[[nodiscard]] _LIBCPP_AVAILABILITY_MATHEMATICAL_SPECIAL_FUNCTIONS inline _LIBCPP_HIDE_FROM_ABI long double
+assoc_laguerrel(unsigned __n, unsigned __m, long double __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 // hermite
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
index 598311f20917d..4729dbd23c58e 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/nodiscard.verify.cpp
@@ -19,8 +19,10 @@
 void test() {
   // clang-format off
   // assoc_laguerre
-  std::assoc_laguerre(0, 0, 0.0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::assoc_laguerre(0, 0, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0.0f);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0.0);   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0.0l);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::assoc_laguerre(0, 0, 0);     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
   std::assoc_laguerref(0, 0, 0.0f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::assoc_laguerrel(0, 0, 0.0l); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
index 8f9dd87c0ee87..b9993ab6500a7 100644
--- a/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/sf.cmath/noexcept.compile.pass.cpp
@@ -17,21 +17,14 @@
 #include <cmath>
 
 #include "test_macros.h"
-#include "type_algorithms.h"
-
-struct TestIntegral {
-  template <class T>
-  void operator()() const {
-    ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, T{}));
-  }
-};
 
 void test() {
   // assoc_laguerre
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0f));
   ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0));
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0.0l));
+  ASSERT_NOEXCEPT(std::assoc_laguerre(0, 0, 0));
 
   ASSERT_NOEXCEPT(std::assoc_laguerref(0, 0, 0.0f));
   ASSERT_NOEXCEPT(std::assoc_laguerrel(0, 0, 0.0l));
-
-  types::for_each(types::integral_types{}, TestIntegral{});
 }
diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 7561cb27b6020..a0dca3cf5ce14 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -66,8 +66,10 @@ struct TestInteger {
 
 int main(int, char**) {
   test_floating_point<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerref(n, m, x); });
+  test_floating_point<float>([](unsigned n, unsigned m, float x) { return std::assoc_laguerre(n, m, x); });
   test_floating_point<double>([](unsigned n, unsigned m, double x) { return std::assoc_laguerre(n, m, x); });
   test_floating_point<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerrel(n, m, x); });
+  test_floating_point<long double>([](unsigned n, unsigned m, long double x) { return std::assoc_laguerre(n, m, x); });
 
   types::for_each(types::integral_types{}, TestInteger{});
 

>From 743cc59817c20cb989c244ba6ad9588586777993 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 4 Jul 2026 22:59:48 +0200
Subject: [PATCH 56/56] test: if constexpr on NaN support

---
 .../std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp  | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index a0dca3cf5ce14..dbd8a5d24eaee 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -45,10 +45,12 @@ void test_floating_point(Func assoc_laguerre) {
   check_no_domain_error([&] { assert(between(1.99, assoc_laguerre(1, 0, Float(-1)), 2.01)); });
 
   // NaN argument -> NaN result, without a domain error ([sf.cmath.general]/1)
-  auto test_nan = [&](Float nan) { check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); }); };
-  if (std::numeric_limits<Float>::has_quiet_NaN)
+  [[maybe_unused]] auto test_nan = [&](Float nan) {
+    check_no_domain_error([&] { assert(std::isnan(assoc_laguerre(0, 0, nan))); });
+  };
+  if constexpr (std::numeric_limits<Float>::has_quiet_NaN)
     test_nan(std::numeric_limits<Float>::quiet_NaN());
-  if (std::numeric_limits<Float>::has_signaling_NaN)
+  if constexpr (std::numeric_limits<Float>::has_signaling_NaN)
     test_nan(std::numeric_limits<Float>::signaling_NaN());
 }
 


From libcxx-commits at lists.llvm.org  Sat Jul  4 14:24:36 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 14:24:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `join_view` (PR #206838)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206838@github.com>
Message-ID: <6a497a14.f8b79514.3b441.f596@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/206838

>From 9f551657103ada7b335b878b39f7ecc7517701ef Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 23:26:43 +0200
Subject: [PATCH 1/2] initial: nodiscard to join_view

---
 libcxx/include/__ranges/join_view.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index 364f056d8d2cf..39c1d462695f0 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -100,15 +100,15 @@ class join_view : public view_interface<join_view<_View>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit join_view(_View __base) : __base_(std::move(__base)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       constexpr bool __use_const = __simple_view<_View> && is_reference_v<range_reference_t<_View>>;
       return __iterator<__use_const>{*this, ranges::begin(__base_)};
@@ -119,14 +119,14 @@ class join_view : public view_interface<join_view<_View>> {
   }
 
   template <class _V2 = _View>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<const _V2> && is_reference_v<range_reference_t<const _V2>> &&
              input_range<range_reference_t<const _V2>>
   {
     return __iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (forward_range<_View> && is_reference_v<_InnerRange> && forward_range<_InnerRange> &&
                   common_range<_View> && common_range<_InnerRange>)
       return __iterator<__simple_view<_View>>{*this, ranges::end(__base_)};
@@ -135,7 +135,7 @@ class join_view : public view_interface<join_view<_View>> {
   }
 
   template <class _V2 = _View>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires forward_range<const _V2> && is_reference_v<range_reference_t<const _V2>> &&
              input_range<range_reference_t<const _V2>>
   {

>From ac3a329c6283a20369a12c9e308ac91ceb3d1b23 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 1 Jul 2026 21:29:55 +0200
Subject: [PATCH 2/2] wip: initial tests proposal

---
 libcxx/include/__ranges/join_view.h           |  4 +-
 .../range.join/nodiscard.verify.cpp           | 63 +++++++++++++++++++
 2 files changed, 65 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index 39c1d462695f0..c379b63975e4f 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -276,7 +276,7 @@ struct join_view<_View>::__iterator final : public __join_view_iterator_category
     requires _Const && convertible_to<iterator_t<_View>, _Outer> && convertible_to<iterator_t<_InnerRange>, _Inner>
       : __outer_(std::move(__i.__outer_)), __inner_(std::move(__i.__inner_)), __parent_(__i.__parent_) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return **__inner_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return **__inner_; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Inner operator->() const
     requires __has_arrow<_Inner> && copyable<_Inner>
@@ -340,7 +340,7 @@ struct join_view<_View>::__iterator final : public __join_view_iterator_category
   }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
-  iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__inner_))) {
+  [[nodiscard]] iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__inner_))) {
     return ranges::iter_move(*__i.__inner_);
   }
 
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..6512d45a0b3ad
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
@@ -0,0 +1,63 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Test the libc++ extension that std::ranges::join_view and std::views::view are marked as [[nodiscard]].
+
+#include <functional>
+#include <ranges>
+#include <string>
+#include <utility>
+#include <vector>
+
+struct InnerView : std::ranges::view_interface<InnerView> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+
+struct View : std::ranges::view_interface<View> {
+  InnerView* begin();
+  const InnerView* begin() const;
+  volatile InnerView* end();
+  const volatile InnerView* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+void test() {
+  auto v = View{} | std::views::join;
+
+  // [range.join.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.join.overview]
+
+  std::vector<std::string> ss{"hello", " ", "world", "!"};
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::join(ss);
+}


From libcxx-commits at lists.llvm.org  Sat Jul  4 14:38:09 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 14:38:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `join_view` (PR #206838)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206838@github.com>
Message-ID: <6a497d41.bf38ae28.358429.9afe@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/206838

>From 9f551657103ada7b335b878b39f7ecc7517701ef Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 23:26:43 +0200
Subject: [PATCH 1/3] initial: nodiscard to join_view

---
 libcxx/include/__ranges/join_view.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index 364f056d8d2cf..39c1d462695f0 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -100,15 +100,15 @@ class join_view : public view_interface<join_view<_View>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit join_view(_View __base) : __base_(std::move(__base)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       constexpr bool __use_const = __simple_view<_View> && is_reference_v<range_reference_t<_View>>;
       return __iterator<__use_const>{*this, ranges::begin(__base_)};
@@ -119,14 +119,14 @@ class join_view : public view_interface<join_view<_View>> {
   }
 
   template <class _V2 = _View>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<const _V2> && is_reference_v<range_reference_t<const _V2>> &&
              input_range<range_reference_t<const _V2>>
   {
     return __iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (forward_range<_View> && is_reference_v<_InnerRange> && forward_range<_InnerRange> &&
                   common_range<_View> && common_range<_InnerRange>)
       return __iterator<__simple_view<_View>>{*this, ranges::end(__base_)};
@@ -135,7 +135,7 @@ class join_view : public view_interface<join_view<_View>> {
   }
 
   template <class _V2 = _View>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires forward_range<const _V2> && is_reference_v<range_reference_t<const _V2>> &&
              input_range<range_reference_t<const _V2>>
   {

>From ac3a329c6283a20369a12c9e308ac91ceb3d1b23 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 1 Jul 2026 21:29:55 +0200
Subject: [PATCH 2/3] wip: initial tests proposal

---
 libcxx/include/__ranges/join_view.h           |  4 +-
 .../range.join/nodiscard.verify.cpp           | 63 +++++++++++++++++++
 2 files changed, 65 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index 39c1d462695f0..c379b63975e4f 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -276,7 +276,7 @@ struct join_view<_View>::__iterator final : public __join_view_iterator_category
     requires _Const && convertible_to<iterator_t<_View>, _Outer> && convertible_to<iterator_t<_InnerRange>, _Inner>
       : __outer_(std::move(__i.__outer_)), __inner_(std::move(__i.__inner_)), __parent_(__i.__parent_) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return **__inner_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return **__inner_; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Inner operator->() const
     requires __has_arrow<_Inner> && copyable<_Inner>
@@ -340,7 +340,7 @@ struct join_view<_View>::__iterator final : public __join_view_iterator_category
   }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
-  iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__inner_))) {
+  [[nodiscard]] iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__inner_))) {
     return ranges::iter_move(*__i.__inner_);
   }
 
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..6512d45a0b3ad
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
@@ -0,0 +1,63 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Test the libc++ extension that std::ranges::join_view and std::views::view are marked as [[nodiscard]].
+
+#include <functional>
+#include <ranges>
+#include <string>
+#include <utility>
+#include <vector>
+
+struct InnerView : std::ranges::view_interface<InnerView> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+
+struct View : std::ranges::view_interface<View> {
+  InnerView* begin();
+  const InnerView* begin() const;
+  volatile InnerView* end();
+  const volatile InnerView* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+void test() {
+  auto v = View{} | std::views::join;
+
+  // [range.join.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.join.overview]
+
+  std::vector<std::string> ss{"hello", " ", "world", "!"};
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::join(ss);
+}

>From 0027333c05ad2d482a36d48a181f1b87a34bbcb1 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 4 Jul 2026 23:37:51 +0200
Subject: [PATCH 3/3] add: range.join.iterator nodiscard.verify tests

---
 .../range.adaptors/range.join/nodiscard.verify.cpp     | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
index 6512d45a0b3ad..6a7a93cbe5e81 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
@@ -54,6 +54,16 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).end();
 
+  // [range.join.iterator]
+
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(c_it);
+
   // [range.join.overview]
 
   std::vector<std::string> ss{"hello", " ", "world", "!"};


From libcxx-commits at lists.llvm.org  Sat Jul  4 14:38:26 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 14:38:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`join_view` (PR #206838)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206838@github.com>
Message-ID: <6a497d52.58bdd12c.21857.b13e@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/206838

From libcxx-commits at lists.llvm.org  Sat Jul  4 14:38:49 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 14:38:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`join_view` (PR #206838)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206838@github.com>
Message-ID: <6a497d69.eea6ab6e.1256b6.df81@mx.google.com>


https://github.com/lknknm ready_for_review https://github.com/llvm/llvm-project/pull/206838

From libcxx-commits at lists.llvm.org  Sat Jul  4 14:39:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 14:39:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`join_view` (PR #206838)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206838@github.com>
Message-ID: <6a497d8e.e55dc341.29c9b8.52c2@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Lucas Mellone (lknknm)

<details>
<summary>Changes</summary>

[[nodiscard]] should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.join

Towards https://github.com/llvm/llvm-project/issues/172124

---
Full diff: https://github.com/llvm/llvm-project/pull/206838.diff


2 Files Affected:

- (modified) libcxx/include/__ranges/join_view.h (+8-8) 
- (added) libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp (+73) 


``````````diff
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index 364f056d8d2cf..c379b63975e4f 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -100,15 +100,15 @@ class join_view : public view_interface<join_view<_View>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit join_view(_View __base) : __base_(std::move(__base)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       constexpr bool __use_const = __simple_view<_View> && is_reference_v<range_reference_t<_View>>;
       return __iterator<__use_const>{*this, ranges::begin(__base_)};
@@ -119,14 +119,14 @@ class join_view : public view_interface<join_view<_View>> {
   }
 
   template <class _V2 = _View>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<const _V2> && is_reference_v<range_reference_t<const _V2>> &&
              input_range<range_reference_t<const _V2>>
   {
     return __iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (forward_range<_View> && is_reference_v<_InnerRange> && forward_range<_InnerRange> &&
                   common_range<_View> && common_range<_InnerRange>)
       return __iterator<__simple_view<_View>>{*this, ranges::end(__base_)};
@@ -135,7 +135,7 @@ class join_view : public view_interface<join_view<_View>> {
   }
 
   template <class _V2 = _View>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires forward_range<const _V2> && is_reference_v<range_reference_t<const _V2>> &&
              input_range<range_reference_t<const _V2>>
   {
@@ -276,7 +276,7 @@ struct join_view<_View>::__iterator final : public __join_view_iterator_category
     requires _Const && convertible_to<iterator_t<_View>, _Outer> && convertible_to<iterator_t<_InnerRange>, _Inner>
       : __outer_(std::move(__i.__outer_)), __inner_(std::move(__i.__inner_)), __parent_(__i.__parent_) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return **__inner_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return **__inner_; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Inner operator->() const
     requires __has_arrow<_Inner> && copyable<_Inner>
@@ -340,7 +340,7 @@ struct join_view<_View>::__iterator final : public __join_view_iterator_category
   }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
-  iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__inner_))) {
+  [[nodiscard]] iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__inner_))) {
     return ranges::iter_move(*__i.__inner_);
   }
 
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..6a7a93cbe5e81
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
@@ -0,0 +1,73 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Test the libc++ extension that std::ranges::join_view and std::views::view are marked as [[nodiscard]].
+
+#include <functional>
+#include <ranges>
+#include <string>
+#include <utility>
+#include <vector>
+
+struct InnerView : std::ranges::view_interface<InnerView> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+
+struct View : std::ranges::view_interface<View> {
+  InnerView* begin();
+  const InnerView* begin() const;
+  volatile InnerView* end();
+  const volatile InnerView* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+void test() {
+  auto v = View{} | std::views::join;
+
+  // [range.join.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.join.iterator]
+
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(c_it);
+
+  // [range.join.overview]
+
+  std::vector<std::string> ss{"hello", " ", "world", "!"};
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::join(ss);
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/206838

From libcxx-commits at lists.llvm.org  Sat Jul  4 14:40:32 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 14:40:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`join_view` (PR #206838)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206838@github.com>
Message-ID: <6a497dd0.6c8668f2.346e76.042e@mx.google.com>


lknknm wrote:

I think it's ready for a round of review, however I'd like to confirm:
While looking at the `join_with_view.h` I can see there are internal APIs like `__get_outer()` and so on marked as `nodiscard`. Should I also add them to the `join_view`?



https://github.com/llvm/llvm-project/pull/206838

From libcxx-commits at lists.llvm.org  Sat Jul  4 14:42:57 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 14:42:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`join_view` (PR #206838)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206838@github.com>
Message-ID: <6a497e61.92d2873c.38c951.fc17@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/206838

>From 9f551657103ada7b335b878b39f7ecc7517701ef Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 23:26:43 +0200
Subject: [PATCH 1/4] initial: nodiscard to join_view

---
 libcxx/include/__ranges/join_view.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index 364f056d8d2cf..39c1d462695f0 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -100,15 +100,15 @@ class join_view : public view_interface<join_view<_View>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit join_view(_View __base) : __base_(std::move(__base)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       constexpr bool __use_const = __simple_view<_View> && is_reference_v<range_reference_t<_View>>;
       return __iterator<__use_const>{*this, ranges::begin(__base_)};
@@ -119,14 +119,14 @@ class join_view : public view_interface<join_view<_View>> {
   }
 
   template <class _V2 = _View>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<const _V2> && is_reference_v<range_reference_t<const _V2>> &&
              input_range<range_reference_t<const _V2>>
   {
     return __iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (forward_range<_View> && is_reference_v<_InnerRange> && forward_range<_InnerRange> &&
                   common_range<_View> && common_range<_InnerRange>)
       return __iterator<__simple_view<_View>>{*this, ranges::end(__base_)};
@@ -135,7 +135,7 @@ class join_view : public view_interface<join_view<_View>> {
   }
 
   template <class _V2 = _View>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires forward_range<const _V2> && is_reference_v<range_reference_t<const _V2>> &&
              input_range<range_reference_t<const _V2>>
   {

>From ac3a329c6283a20369a12c9e308ac91ceb3d1b23 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 1 Jul 2026 21:29:55 +0200
Subject: [PATCH 2/4] wip: initial tests proposal

---
 libcxx/include/__ranges/join_view.h           |  4 +-
 .../range.join/nodiscard.verify.cpp           | 63 +++++++++++++++++++
 2 files changed, 65 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index 39c1d462695f0..c379b63975e4f 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -276,7 +276,7 @@ struct join_view<_View>::__iterator final : public __join_view_iterator_category
     requires _Const && convertible_to<iterator_t<_View>, _Outer> && convertible_to<iterator_t<_InnerRange>, _Inner>
       : __outer_(std::move(__i.__outer_)), __inner_(std::move(__i.__inner_)), __parent_(__i.__parent_) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return **__inner_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return **__inner_; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Inner operator->() const
     requires __has_arrow<_Inner> && copyable<_Inner>
@@ -340,7 +340,7 @@ struct join_view<_View>::__iterator final : public __join_view_iterator_category
   }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
-  iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__inner_))) {
+  [[nodiscard]] iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__inner_))) {
     return ranges::iter_move(*__i.__inner_);
   }
 
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..6512d45a0b3ad
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
@@ -0,0 +1,63 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Test the libc++ extension that std::ranges::join_view and std::views::view are marked as [[nodiscard]].
+
+#include <functional>
+#include <ranges>
+#include <string>
+#include <utility>
+#include <vector>
+
+struct InnerView : std::ranges::view_interface<InnerView> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+
+struct View : std::ranges::view_interface<View> {
+  InnerView* begin();
+  const InnerView* begin() const;
+  volatile InnerView* end();
+  const volatile InnerView* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+void test() {
+  auto v = View{} | std::views::join;
+
+  // [range.join.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.join.overview]
+
+  std::vector<std::string> ss{"hello", " ", "world", "!"};
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::join(ss);
+}

>From 0027333c05ad2d482a36d48a181f1b87a34bbcb1 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 4 Jul 2026 23:37:51 +0200
Subject: [PATCH 3/4] add: range.join.iterator nodiscard.verify tests

---
 .../range.adaptors/range.join/nodiscard.verify.cpp     | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
index 6512d45a0b3ad..6a7a93cbe5e81 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
@@ -54,6 +54,16 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).end();
 
+  // [range.join.iterator]
+
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(c_it);
+
   // [range.join.overview]
 
   std::vector<std::string> ss{"hello", " ", "world", "!"};

>From 53193297ec56209e02e4cc128f3e213cfda80a24 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 4 Jul 2026 23:42:42 +0200
Subject: [PATCH 4/4] fix: nodiscard correction

---
 libcxx/include/__ranges/join_view.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index c379b63975e4f..f80b75c839d4a 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -339,8 +339,8 @@ struct join_view<_View>::__iterator final : public __join_view_iterator_category
     return __x.__outer_ == __y.__outer_ && __x.__inner_ == __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
-  [[nodiscard]] iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__inner_))) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
+  iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__inner_))) {
     return ranges::iter_move(*__i.__inner_);
   }
 


From libcxx-commits at lists.llvm.org  Sat Jul  4 14:48:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 14:48:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][math][c++17] P0226R1 -
 Mathematical Special Functions: infra + `std::assoc_laguerre` (PR #205649)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205649@github.com>
Message-ID: <6a497fc3.331483e7.5bc1e.e702@mx.google.com>


https://github.com/PaulXiCao edited https://github.com/llvm/llvm-project/pull/205649

From libcxx-commits at lists.llvm.org  Sat Jul  4 14:55:30 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 14:55:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a498152.27b3b003.1610ae.3c4d@mx.google.com>


================
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
----------------
lknknm wrote:

I'm not sure if we should model also a CommonRange to test the `if constexpr (common_range<_InnerView>)` in:
```cpp
  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
    if constexpr (common_range<_InnerView>) {
      return __iterator<false>(*this, __zip_.end());
    } else {
      return __sentinel<false>(__zip_.end());
    }
  }
```
Does that make sense?

https://github.com/llvm/llvm-project/pull/207120

From libcxx-commits at lists.llvm.org  Sat Jul  4 14:55:30 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 14:55:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a498152.da8bafd3.1eab2b.f147@mx.google.com>


================
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it  = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
----------------
lknknm wrote:

I'm sorry for the nitpick but shall we maintain the snake_case style here? 

```suggestion
  auto c_it = std::as_const(ztv).begin();

  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
  *c_it;
  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
  c_it[0];
```

https://github.com/llvm/llvm-project/pull/207120

From libcxx-commits at lists.llvm.org  Sat Jul  4 14:55:59 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 04 Jul 2026 14:55:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a49816f.8e75e0cb.2e0812.ffc8@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/207120

From libcxx-commits at lists.llvm.org  Sat Jul  4 17:59:22 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 17:59:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ThreadSafety] Add thread safety
 annotations for variadic std::scoped_lock (PR #204462)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204462@github.com>
Message-ID: <6a49ac6a.01f0a1da.2016e9.0fe1@mx.google.com>


AnthonyCalandraGeotab wrote:

Just an FYI, I rebased the branch off latest main; there were a couple of failures in one of the checks the last time. @philnik777 

https://github.com/llvm/llvm-project/pull/204462

From libcxx-commits at lists.llvm.org  Sat Jul  4 22:20:42 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sat, 04 Jul 2026 22:20:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`join_view` (PR #206838)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206838@github.com>
Message-ID: <6a49e9aa.ac469dc7.e3aa6.7532@mx.google.com>


Zingam wrote:

> I think it's ready for a round of review, however I'd like to confirm: While looking at the `join_with_view.h` I can see there are internal APIs like `__get_outer()` and so on marked as `nodiscard`. Should I also mark the equivalent in `join_view` as `[[nodiscard]]` as well?

Applying `[[nodiscard]]` to implementation details is out-of-scope. Only in containers we applied it to iterators such as `__wrap_iter` for a reason.

https://github.com/llvm/llvm-project/pull/206838

From libcxx-commits at lists.llvm.org  Sat Jul  4 22:49:28 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sat, 04 Jul 2026 22:49:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a49f068.f06f4994.168928.99fd@mx.google.com>


================
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it  = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
----------------
Zingam wrote:

The test suit is already inconsistent on snake_case vs CameCase, or whatEverCase including `[[nodiscard]]` tests.
So I am not nitpicking on that.

https://github.com/llvm/llvm-project/pull/207120

From libcxx-commits at lists.llvm.org  Sat Jul  4 22:49:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 22:49:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
Message-ID: <llvm.org/llvm/llvm-project/pull/207558@github.com>


https://github.com/emmett2020 created https://github.com/llvm/llvm-project/pull/207558

None

>From 4e0a007bf696183fbb99d73970ea5fc6b2b415dc Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Sun, 5 Jul 2026 13:38:58 +0800
Subject: [PATCH] Fix LWG issue4481 disallow chrono::duration<const T, P>

---
 libcxx/include/__chrono/duration.h            |  8 +++++
 .../std/time/time.duration/cv_rep.verify.cpp  | 31 +++++++++++++++++++
 2 files changed, 39 insertions(+)
 create mode 100644 libcxx/test/std/time/time.duration/cv_rep.verify.cpp

diff --git a/libcxx/include/__chrono/duration.h b/libcxx/include/__chrono/duration.h
index 92efd82f72145..16fd4b36690d3 100644
--- a/libcxx/include/__chrono/duration.h
+++ b/libcxx/include/__chrono/duration.h
@@ -22,6 +22,11 @@
 #include <limits>
 #include <ratio>
 
+#if _LIBCPP_STD_VER >= 26
+#  include <__type_traits/is_same.h>
+#  include <__type_traits/remove_cv.h>
+#endif
+
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
@@ -164,6 +169,9 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
 
 template <class _Rep, class _Period>
 class duration {
+#if _LIBCPP_STD_VER >= 26
+  static_assert(is_same_v<_Rep, remove_cv_t<_Rep>>, "A duration representation cannot be cv-qualified");
+#endif
   static_assert(!__is_duration_v<_Rep>, "A duration representation can not be a duration");
   static_assert(__is_ratio_v<_Period>, "Second template parameter of duration must be a std::ratio");
   static_assert(_Period::num > 0, "duration period must be positive");
diff --git a/libcxx/test/std/time/time.duration/cv_rep.verify.cpp b/libcxx/test/std/time/time.duration/cv_rep.verify.cpp
new file mode 100644
index 0000000000000..d51181c0c4c6f
--- /dev/null
+++ b/libcxx/test/std/time/time.duration/cv_rep.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
+
+// <chrono>
+
+// LWG 4481
+// A program that instantiates duration with a cv-qualified Rep is ill-formed.
+
+#include <chrono>
+
+void test_const() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<const int>);
+}
+
+void test_volatile() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<volatile int>);
+}
+
+void test_const_volatile() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<const volatile int>);
+}


From libcxx-commits at lists.llvm.org  Sat Jul  4 22:50:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 22:50:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49f088.2020f6c2.1fdf61.b63e@mx.google.com>


github-actions[bot] wrote:

<!--LLVM NEW CONTRIBUTOR COMMENT-->

Hello @emmett2020 :wave:

Thank you for submitting a Pull Request (PR) to the LLVM Project. Since this is your first PR, here are a few useful links covering our main contribution policies and review practices.

* All contributions to LLVM must follow our [LLVM AI Tool Use Policy](https://llvm.org/docs/AIToolPolicy.html). In particular, if you used AI while working on this PR, remember to add a note to the PR description.
* The [LLVM Code-Review Policy and Practices](https://llvm.org/docs/CodeReview.html) document contains practical information about the PR process, including how patches are reviewed and accepted, and who can review a PR.
* Our [LLVM Developer Policy](https://llvm.org/docs/DeveloperPolicy.html) describes our expectations for code quality, commit summaries and contains notes on our CI system.

Please reply to this message to confirm that you have read these policies, especially the LLVM AI Tool Use Policy, and that any AI tool usage has been noted in the PR description.

---

### Frequently asked questions

**How do I add reviewers?**

This PR will be automatically labeled, and the relevant teams will be notified. For some parts of the project, reviewers may also be added automatically.

You can also add reviewers manually using the **Reviewers** section on this page. If you cannot use that section, it is probably because you do not have write permissions for the repository. In that case, you can request a review by tagging reviewers in a comment using `@` followed by their GitHub username.

**What if there are no comments?**

If you have not received any comments on your PR after a week, you can request a review by pinging the PR with a comment such as “Ping”. The common courtesy ping rate is once a week. Please remember that you are asking for volunteer time from other developers.

**Are any special GitHub settings required to contribute to LLVM?**

We only require contributors to have a public email address associated with their GitHub commits, see this [section](https://llvm.org/docs/DeveloperPolicy.html#email-addresses) of LLVM Developer Policy for details.

---

If you have questions, feel free to leave a comment on this PR, or ask on [LLVM Discord](https://discord.com/invite/xS7Z362) or [LLVM Discourse](https://discourse.llvm.org/).

Thank you,
The LLVM Community

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 22:50:20 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sat, 04 Jul 2026 22:50:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a49f09c.af6ba3ed.2c2f30.c3ad@mx.google.com>


================
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it  = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.zip_transform.sentinel]
+
+  auto st = ztv.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // [range.zip_transform.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform(Invocable<>{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform([](int x) { return x; }, range);
+}
----------------
Zingam wrote:

Thanks for noticing. The proposed `View` isn't producing the desired sentinel result for `operator-`, so this PR is not ready yet.

https://github.com/llvm/llvm-project/pull/207120

From libcxx-commits at lists.llvm.org  Sat Jul  4 22:50:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 22:50:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49f0ad.ac469dc7.e3aa6.e09a@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Emmett (emmett2020)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/207558.diff


2 Files Affected:

- (modified) libcxx/include/__chrono/duration.h (+8) 
- (added) libcxx/test/std/time/time.duration/cv_rep.verify.cpp (+31) 


``````````diff
diff --git a/libcxx/include/__chrono/duration.h b/libcxx/include/__chrono/duration.h
index 92efd82f72145..16fd4b36690d3 100644
--- a/libcxx/include/__chrono/duration.h
+++ b/libcxx/include/__chrono/duration.h
@@ -22,6 +22,11 @@
 #include <limits>
 #include <ratio>
 
+#if _LIBCPP_STD_VER >= 26
+#  include <__type_traits/is_same.h>
+#  include <__type_traits/remove_cv.h>
+#endif
+
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
@@ -164,6 +169,9 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
 
 template <class _Rep, class _Period>
 class duration {
+#if _LIBCPP_STD_VER >= 26
+  static_assert(is_same_v<_Rep, remove_cv_t<_Rep>>, "A duration representation cannot be cv-qualified");
+#endif
   static_assert(!__is_duration_v<_Rep>, "A duration representation can not be a duration");
   static_assert(__is_ratio_v<_Period>, "Second template parameter of duration must be a std::ratio");
   static_assert(_Period::num > 0, "duration period must be positive");
diff --git a/libcxx/test/std/time/time.duration/cv_rep.verify.cpp b/libcxx/test/std/time/time.duration/cv_rep.verify.cpp
new file mode 100644
index 0000000000000..d51181c0c4c6f
--- /dev/null
+++ b/libcxx/test/std/time/time.duration/cv_rep.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
+
+// <chrono>
+
+// LWG 4481
+// A program that instantiates duration with a cv-qualified Rep is ill-formed.
+
+#include <chrono>
+
+void test_const() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<const int>);
+}
+
+void test_volatile() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<volatile int>);
+}
+
+void test_const_volatile() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<const volatile int>);
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 22:52:13 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 04 Jul 2026 22:52:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][math][c++17] P0226R1 -
 Mathematical Special Functions: infra + `std::assoc_laguerre` (PR #205649)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205649@github.com>
Message-ID: <6a49f10d.ff037943.3bf0e2.451c@mx.google.com>


https://github.com/PaulXiCao edited https://github.com/llvm/llvm-project/pull/205649

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:02:38 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sat, 04 Jul 2026 23:02:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49f37e.8e75e0cb.2e0812.b21f@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:02:38 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sat, 04 Jul 2026 23:02:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49f37e.da8bafd3.1eab2b.0aad@mx.google.com>


https://github.com/Zingam commented:

Please update the title to include the conventional prefix such as: `[libc++][chrono] Fix LWG...`.
Also associate this PR with the GitHub issue using the GitHub syntax in the commit message (the top comment) "Closes #issue number"

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:02:38 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sat, 04 Jul 2026 23:02:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49f37e.70920dee.c0a01.7a2c@mx.google.com>


================
@@ -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
----------------
Zingam wrote:

```suggestion
// REQUIRES: std-at-least-c++26
```

Using the newer syntax is recommended.

I am also not convinced `cv_rep.verify.cpp` to be a good name. I think `duration.verify.cpp` is more appropriate and consistent. Let's rename the test file.

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:02:39 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sat, 04 Jul 2026 23:02:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49f37f.ff037943.3bf0e2.8c14@mx.google.com>


================
@@ -22,6 +22,11 @@
 #include <limits>
 #include <ratio>
 
+#if _LIBCPP_STD_VER >= 26
+#  include <__type_traits/is_same.h>
+#  include <__type_traits/remove_cv.h>
+#endif
+
----------------
Zingam wrote:

Typically we don't include conditinally.
```suggestion
#  include <__type_traits/is_same.h>
#  include <__type_traits/remove_cv.h>

```

Includes should be sorted.

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:02:39 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sat, 04 Jul 2026 23:02:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49f37f.ff037943.3bf0e2.8bf3@mx.google.com>


================
@@ -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
+
+// <chrono>
+
+// LWG 4481
+// A program that instantiates duration with a cv-qualified Rep is ill-formed.
+
+#include <chrono>
+
+void test_const() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<const int>);
+}
+
+void test_volatile() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<volatile int>);
+}
+
+void test_const_volatile() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<const volatile int>);
+}
----------------
Zingam wrote:

Nit: I see no need for so many "functions":

```suggestion
void test() {
  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
  (void)sizeof(std::chrono::duration<const int>);

  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
  (void)sizeof(std::chrono::duration<volatile int>);

  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
  (void)sizeof(std::chrono::duration<const volatile int>);
}
```

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:28:01 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 04 Jul 2026 23:28:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49f971.c56fb102.11841f.5921@mx.google.com>


https://github.com/frederick-vs-ja requested changes to this pull request.


https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:28:01 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 04 Jul 2026 23:28:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49f971.4a342cbc.134cb.e130@mx.google.com>


================
@@ -164,6 +169,9 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
 
 template <class _Rep, class _Period>
 class duration {
+#if _LIBCPP_STD_VER >= 26
+  static_assert(is_same_v<_Rep, remove_cv_t<_Rep>>, "A duration representation cannot be cv-qualified");
+#endif
----------------
frederick-vs-ja wrote:

Also, please update this line in `Cxx26Issues.csv` as indicated.
https://github.com/llvm/llvm-project/blob/7f449d38cd9d203af2be106210f3ef9c30597ebe/libcxx/docs/Status/Cxx26Issues.csv#L304

```diff
-"`LWG4481 <https://wg21.link/LWG4481>`__","Disallow ``chrono::duration<const T, P>``","2026-03 (Croydon)","","","`#189846 <https://github.com/llvm/llvm-project/issues/189846>`__",""
+"`LWG4481 <https://wg21.link/LWG4481>`__","Disallow ``chrono::duration<const T, P>``","2026-03 (Croydon)","|Complete|","23","`#189846 <https://github.com/llvm/llvm-project/issues/189846>`__",""
```

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:28:01 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 04 Jul 2026 23:28:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49f971.27b3b003.1610ae.a8c1@mx.google.com>


================
@@ -164,6 +169,9 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
 
 template <class _Rep, class _Period>
 class duration {
+#if _LIBCPP_STD_VER >= 26
+  static_assert(is_same_v<_Rep, remove_cv_t<_Rep>>, "A duration representation cannot be cv-qualified");
+#endif
----------------
frederick-vs-ja wrote:

Resolution LWG4481 should be applied to old modes. Also it's better to separately diagnose const- and volatile-qualifications.
```suggestion
  static_assert(!is_const<_Rep, remove_cv_t<_Rep>>::value, "A duration representation cannot be const-qualified");
  static_assert(is_volatile<_Rep, remove_cv_t<_Rep>>::value, "A duration representation cannot be volatile-qualified");
```

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:29:57 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 04 Jul 2026 23:29:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49f9e5.13be6df8.3d8759.0718@mx.google.com>


================
@@ -22,6 +22,11 @@
 #include <limits>
 #include <ratio>
 
+#if _LIBCPP_STD_VER >= 26
+#  include <__type_traits/is_same.h>
+#  include <__type_traits/remove_cv.h>
+#endif
+
----------------
frederick-vs-ja wrote:

```suggestion
#  include <__type_traits/is_const.h>
#  include <__type_traits/is_volatile.h>

```
Let's include `<__type_traits/is_const.h>` and `<__type_traits/is_volatile.h>` instead, as `is_const` and `is_volatile` are a bit more expressive (see my suggested change).

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:32:14 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 04 Jul 2026 23:32:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49fa6e.abb10101.208f3e.58d0@mx.google.com>


================
@@ -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
----------------
frederick-vs-ja wrote:

```suggestion
```

Let's remove this line, because we provide `duration` even in C++03 mode.

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:36:48 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 04 Jul 2026 23:36:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49fb80.30759430.1908d3.af6e@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:37:04 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 04 Jul 2026 23:37:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49fb90.92d2873c.38c951.b4c6@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sat Jul  4 23:39:45 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 04 Jul 2026 23:39:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a49fc31.7270d2fd.23dbb3.5242@mx.google.com>


================
@@ -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
+
+// <chrono>
+
+// LWG 4481
+// A program that instantiates duration with a cv-qualified Rep is ill-formed.
+
+#include <chrono>
+
+void test_const() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<const int>);
+}
+
+void test_volatile() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<volatile int>);
+}
+
+void test_const_volatile() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<const volatile int>);
+}
----------------
frederick-vs-ja wrote:

Correspondingly, let's use
```suggestion
void test() {
  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be const-qualified}}
  (void)sizeof(std::chrono::duration<const int>);

  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be volatile-qualified}}
  (void)sizeof(std::chrono::duration<volatile int>);

  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be const-qualified}}\
  (void)sizeof(std::chrono::duration<const volatile int>);
}
```

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 00:45:05 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 05 Jul 2026 00:45:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3454 as resolved (PR
	#207487)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207487@github.com>
Message-ID: <6a4a0b81.bb985344.17fb3b.f801@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/207487

From libcxx-commits at lists.llvm.org  Sun Jul  5 00:53:49 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 00:53:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] b0a64cf - [libc++] Mark LWG3454 as
	resolved (#207487)
Message-ID: <6a4a0d8d.ac469dc7.e3aa6.3bfa@mx.google.com>


Author: Connector Switch
Date: 2026-07-05T15:53:44+08:00
New Revision: b0a64cf46adcb6da2cef28bac25f910077277923

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

LOG: [libc++] Mark LWG3454 as resolved (#207487)

Closes https://github.com/llvm/llvm-project/issues/171307.

LWG3454 has been implemented in
https://github.com/llvm/llvm-project/commit/98d3d5b5da66e3cf7807c23a0294280bb796466b.

Added: 
    

Modified: 
    libcxx/docs/Status/Cxx26Issues.csv
    libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 3f2a446f7e1f3..30042d4465d93 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -149,7 +149,7 @@
 "`LWG2991 <https://wg21.link/LWG2991>`__","``variant copy`` constructor missing ``noexcept(see below)``","2025-11 (Kona)","","","`#171301 <https://github.com/llvm/llvm-project/issues/171301>`__",""
 "`LWG3090 <https://wg21.link/LWG3090>`__","What is §[time.duration.cons]p4's ""no overflow is induced in the conversion"" intended to mean?","2025-11 (Kona)","","","`#171302 <https://github.com/llvm/llvm-project/issues/171302>`__",""
 "`LWG3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","2025-11 (Kona)","|Complete|","16","`#105356 <https://github.com/llvm/llvm-project/issues/105356>`__",""
-"`LWG3454 <https://wg21.link/LWG3454>`__","``pointer_traits::pointer_to`` should be ``constexpr``","2025-11 (Kona)","","","`#171307 <https://github.com/llvm/llvm-project/issues/171307>`__",""
+"`LWG3454 <https://wg21.link/LWG3454>`__","``pointer_traits::pointer_to`` should be ``constexpr``","2025-11 (Kona)","|Complete|","16","`#171307 <https://github.com/llvm/llvm-project/issues/171307>`__",""
 "`LWG3627 <https://wg21.link/LWG3627>`__","Inconsistent specifications for ``std::make_optional`` overloads","2025-11 (Kona)","|Complete|","22","`#171309 <https://github.com/llvm/llvm-project/issues/171309>`__",""
 "`LWG4015 <https://wg21.link/LWG4015>`__","LWG 3973 broke const overloads of ``std::optional`` monadic operations","2025-11 (Kona)","","","`#171310 <https://github.com/llvm/llvm-project/issues/171310>`__",""
 "`LWG4020 <https://wg21.link/LWG4020>`__","``extents::index-cast`` weirdness","2025-11 (Kona)","|Complete|","23","`#171311 <https://github.com/llvm/llvm-project/issues/171311>`__",""

diff  --git a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
index e4c62f3d3df5c..fb3b76822f570 100644
--- a/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
+++ b/libcxx/test/std/utilities/memory/pointer.traits/pointer.traits.functions/pointer_to.pass.cpp
@@ -30,19 +30,21 @@ struct A {
   typedef T element_type;
   element_type* t_;
 
-  A(element_type* t) : t_(t) {}
+  TEST_CONSTEXPR_CXX20 A(element_type* t) : t_(t) {}
 
-  static A pointer_to(typename std::conditional<std::is_void<element_type>::value, nat, element_type>::type& et) {
+  TEST_CONSTEXPR_CXX20 static A
+  pointer_to(typename std::conditional<std::is_void<element_type>::value, nat, element_type>::type& et) {
     return A(&et);
   }
 };
 
 template <class Pointer>
-void test() {
+TEST_CONSTEXPR_CXX20 bool test() {
   typename Pointer::element_type obj;
   static_assert(std::is_same<Pointer, decltype(std::pointer_traits<Pointer>::pointer_to(obj))>::value, "");
   Pointer p = std::pointer_traits<Pointer>::pointer_to(obj);
   assert(p.t_ == &obj);
+  return true;
 }
 
 int main(int, char**) {
@@ -50,5 +52,10 @@ int main(int, char**) {
   test<A<long> >();
   { (std::pointer_traits<A<void> >::element_type)0; }
 
+#if TEST_STD_VER >= 20
+  static_assert(test<A<int> >());
+  static_assert(test<A<long> >());
+#endif // TEST_STD_VER
+
   return 0;
 }


        

From libcxx-commits at lists.llvm.org  Sun Jul  5 00:53:52 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Sun, 05 Jul 2026 00:53:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3454 as resolved (PR
	#207487)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207487@github.com>
Message-ID: <6a4a0d90.4a342cbc.134cb.cb6c@mx.google.com>


https://github.com/c8ef closed https://github.com/llvm/llvm-project/pull/207487

From libcxx-commits at lists.llvm.org  Sun Jul  5 01:00:02 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 01:00:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove _LIBCPP_SHORT_WCHAR (PR
	#207562)
Message-ID: <llvm.org/llvm/llvm-project/pull/207562@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207562

We can just check for `sizeof(wchar_t)` in most places instead. The only place that seems problematic is `test_macros.h`, where we can use `__SIZEOF_WCHAR_T__` instead.


>From 7b9ae38ec59e488c20498ed00e496992260263fa Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 27 Jun 2026 14:45:50 +0200
Subject: [PATCH] [libc++] Remove _LIBCPP_SHORT_WCHAR

---
 libcxx/include/__config           |   6 -
 libcxx/include/__cxx03/__config   |   6 -
 libcxx/include/print              |  19 +--
 libcxx/src/locale.cpp             | 247 ++++++++++++------------------
 libcxx/test/support/test_macros.h |   2 +-
 5 files changed, 103 insertions(+), 177 deletions(-)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..9c3b569861db7 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -44,7 +44,6 @@
 
 #  if defined(_WIN32)
 #    define _LIBCPP_WIN32API
-#    define _LIBCPP_SHORT_WCHAR 1
 // Both MinGW and native MSVC provide a "MSVC"-like environment
 #    define _LIBCPP_MSVCRT_LIKE
 // If mingw not explicitly detected, assume using MS C runtime only if
@@ -57,11 +56,6 @@
 #    define _LIBCPP_HAS_OPEN_WITH_WCHAR 0
 #  endif // defined(_WIN32)
 
-#  if defined(_AIX) && !defined(__64BIT__)
-// The size of wchar is 2 byte on 32-bit mode on AIX.
-#    define _LIBCPP_SHORT_WCHAR 1
-#  endif
-
 // Libc++ supports various implementations of std::random_device.
 //
 // _LIBCPP_USING_DEV_RANDOM
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 70a4f5cff9138..6679494a1f936 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -221,7 +221,6 @@ _LIBCPP_HARDENING_MODE_DEBUG
 
 #  if defined(_WIN32)
 #    define _LIBCPP_WIN32API
-#    define _LIBCPP_SHORT_WCHAR 1
 // Both MinGW and native MSVC provide a "MSVC"-like environment
 #    define _LIBCPP_MSVCRT_LIKE
 // If mingw not explicitly detected, assume using MS C runtime only if
@@ -232,11 +231,6 @@ _LIBCPP_HARDENING_MODE_DEBUG
 #    define _LIBCPP_HAS_OPEN_WITH_WCHAR
 #  endif // defined(_WIN32)
 
-#  if defined(_AIX) && !defined(__64BIT__)
-// The size of wchar is 2 byte on 32-bit mode on AIX.
-#    define _LIBCPP_SHORT_WCHAR 1
-#  endif
-
 // Libc++ supports various implementations of std::random_device.
 //
 // _LIBCPP_USING_DEV_RANDOM
diff --git a/libcxx/include/print b/libcxx/include/print
index cf62743846f42..d30d0be0130c2 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -89,27 +89,20 @@ namespace __unicode {
 // The names of these concepts are modelled after P2728R0, but the
 // implementation is not. char16_t may contain 32-bits so depending on the
 // number of bits is an issue.
-#      ifdef _LIBCPP_SHORT_WCHAR
 template <class _Tp>
 concept __utf16_code_unit =
     same_as<_Tp, char16_t>
-#        if _LIBCPP_HAS_WIDE_CHARACTERS
-    || same_as<_Tp, wchar_t>
-#        endif
+#      if _LIBCPP_HAS_WIDE_CHARACTERS
+    || (sizeof(wchar_t) == sizeof(char16_t) && same_as<_Tp, wchar_t>)
+#      endif
     ;
 template <class _Tp>
-concept __utf32_code_unit = same_as<_Tp, char32_t>;
-#      else // _LIBCPP_SHORT_WCHAR
-template <class _Tp>
-concept __utf16_code_unit = same_as<_Tp, char16_t>;
-template <class _Tp>
 concept __utf32_code_unit =
     same_as<_Tp, char32_t>
-#        if _LIBCPP_HAS_WIDE_CHARACTERS
-    || same_as<_Tp, wchar_t>
-#        endif
+#      if _LIBCPP_HAS_WIDE_CHARACTERS
+    || (sizeof(wchar_t) == sizeof(char32_t) && same_as<_Tp, wchar_t>)
+#      endif
     ;
-#      endif // _LIBCPP_SHORT_WCHAR
 
 // Pass by reference since an output_iterator may not be copyable.
 template <class _OutIt>
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index b0a69ab74f94c..51548f96ce6f1 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -2913,6 +2913,28 @@ int codecvt<char32_t, char8_t, mbstate_t>::do_max_length() const noexcept { retu
 // __codecvt_utf8<wchar_t>
 
 #if _LIBCPP_HAS_WIDE_CHARACTERS
+
+constexpr bool is_short_wchar = sizeof(wchar_t) == sizeof(uint16_t);
+using underlying_wchar_t = conditional_t<is_short_wchar, uint16_t, uint32_t>;
+static_assert(sizeof(underlying_wchar_t) == sizeof(wchar_t));
+
+template <auto callable>
+struct call {
+  template <class... Args>
+  static auto operator()(Args&&... args) -> decltype(callable(std::forward<Args>(args)...)) {
+    return callable(std::forward<Args>(args)...);
+  }
+};
+
+template <auto... Args, class... Args2>
+auto overload(Args2&&... args) {
+  struct overload_t : call<Args>... {
+    using call<Args>::operator()...;
+  };
+
+  return overload_t{}(std::forward<Args2>(args)...);
+}
+
 __codecvt_utf8<wchar_t>::result __codecvt_utf8<wchar_t>::do_out(
     state_type&,
     const intern_type* frm,
@@ -2921,25 +2943,15 @@ __codecvt_utf8<wchar_t>::result __codecvt_utf8<wchar_t>::do_out(
     extern_type* to,
     extern_type* to_end,
     extern_type*& to_nxt) const {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  const uint16_t* _frm     = reinterpret_cast<const uint16_t*>(frm);
-  const uint16_t* _frm_end = reinterpret_cast<const uint16_t*>(frm_end);
-  const uint16_t* _frm_nxt = _frm;
-#  else
-  const uint32_t* _frm     = reinterpret_cast<const uint32_t*>(frm);
-  const uint32_t* _frm_end = reinterpret_cast<const uint32_t*>(frm_end);
-  const uint32_t* _frm_nxt = _frm;
-#  endif
-  uint8_t* _to     = reinterpret_cast<uint8_t*>(to);
-  uint8_t* _to_end = reinterpret_cast<uint8_t*>(to_end);
-  uint8_t* _to_nxt = _to;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  result r = ucs2_to_utf8(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  result r = ucs4_to_utf8(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
-  frm_nxt = frm + (_frm_nxt - _frm);
-  to_nxt  = to + (_to_nxt - _to);
+  const auto* _frm     = reinterpret_cast<const underlying_wchar_t*>(frm);
+  const auto* _frm_end = reinterpret_cast<const underlying_wchar_t*>(frm_end);
+  const auto* _frm_nxt = _frm;
+  uint8_t* _to         = reinterpret_cast<uint8_t*>(to);
+  uint8_t* _to_end     = reinterpret_cast<uint8_t*>(to_end);
+  uint8_t* _to_nxt     = _to;
+  result r = overload<ucs2_to_utf8, ucs4_to_utf8>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
+  frm_nxt  = frm + (_frm_nxt - _frm);
+  to_nxt   = to + (_to_nxt - _to);
   return r;
 }
 
@@ -2954,19 +2966,12 @@ __codecvt_utf8<wchar_t>::result __codecvt_utf8<wchar_t>::do_in(
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
   const uint8_t* _frm_nxt = _frm;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  uint16_t* _to     = reinterpret_cast<uint16_t*>(to);
-  uint16_t* _to_end = reinterpret_cast<uint16_t*>(to_end);
-  uint16_t* _to_nxt = _to;
-  result r          = utf8_to_ucs2(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  uint32_t* _to     = reinterpret_cast<uint32_t*>(to);
-  uint32_t* _to_end = reinterpret_cast<uint32_t*>(to_end);
-  uint32_t* _to_nxt = _to;
-  result r          = utf8_to_ucs4(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
-  frm_nxt = frm + (_frm_nxt - _frm);
-  to_nxt  = to + (_to_nxt - _to);
+  auto* _to               = reinterpret_cast<underlying_wchar_t*>(to);
+  auto* _to_end           = reinterpret_cast<underlying_wchar_t*>(to_end);
+  auto* _to_nxt           = _to;
+  result r = overload<utf8_to_ucs2, utf8_to_ucs4>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
+  frm_nxt  = frm + (_frm_nxt - _frm);
+  to_nxt   = to + (_to_nxt - _to);
   return r;
 }
 
@@ -2984,24 +2989,18 @@ int __codecvt_utf8<wchar_t>::do_length(
     state_type&, const extern_type* frm, const extern_type* frm_end, size_t mx) const {
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  return utf8_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  else
-  return utf8_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  endif
+  if constexpr (is_short_wchar)
+    return utf8_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
+  else
+    return utf8_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
 }
 
 _LIBCPP_SUPPRESS_DEPRECATED_PUSH
 int __codecvt_utf8<wchar_t>::do_max_length() const noexcept {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  if (__mode_ & consume_header)
-    return 6;
-  return 3;
-#  else
-  if (__mode_ & consume_header)
-    return 7;
-  return 4;
-#  endif
+  if constexpr (is_short_wchar)
+    return (__mode_ & consume_header) ? 6 : 3;
+  else
+    return (__mode_ & consume_header) ? 7 : 4;
 }
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
@@ -3150,23 +3149,14 @@ __codecvt_utf16<wchar_t, false>::result __codecvt_utf16<wchar_t, false>::do_out(
     extern_type* to,
     extern_type* to_end,
     extern_type*& to_nxt) const {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  const uint16_t* _frm     = reinterpret_cast<const uint16_t*>(frm);
-  const uint16_t* _frm_end = reinterpret_cast<const uint16_t*>(frm_end);
-  const uint16_t* _frm_nxt = _frm;
-#  else
-  const uint32_t* _frm     = reinterpret_cast<const uint32_t*>(frm);
-  const uint32_t* _frm_end = reinterpret_cast<const uint32_t*>(frm_end);
-  const uint32_t* _frm_nxt = _frm;
-#  endif
-  uint8_t* _to     = reinterpret_cast<uint8_t*>(to);
-  uint8_t* _to_end = reinterpret_cast<uint8_t*>(to_end);
-  uint8_t* _to_nxt = _to;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  result r = ucs2_to_utf16be(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  result r = ucs4_to_utf16be(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
+  const auto* _frm     = reinterpret_cast<const underlying_wchar_t*>(frm);
+  const auto* _frm_end = reinterpret_cast<const underlying_wchar_t*>(frm_end);
+  const auto* _frm_nxt = _frm;
+  uint8_t* _to         = reinterpret_cast<uint8_t*>(to);
+  uint8_t* _to_end     = reinterpret_cast<uint8_t*>(to_end);
+  uint8_t* _to_nxt     = _to;
+  result r =
+      overload<ucs2_to_utf16be, ucs4_to_utf16be>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
   frm_nxt = frm + (_frm_nxt - _frm);
   to_nxt  = to + (_to_nxt - _to);
   return r;
@@ -3183,17 +3173,11 @@ __codecvt_utf16<wchar_t, false>::result __codecvt_utf16<wchar_t, false>::do_in(
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
   const uint8_t* _frm_nxt = _frm;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  uint16_t* _to     = reinterpret_cast<uint16_t*>(to);
-  uint16_t* _to_end = reinterpret_cast<uint16_t*>(to_end);
-  uint16_t* _to_nxt = _to;
-  result r          = utf16be_to_ucs2(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  uint32_t* _to     = reinterpret_cast<uint32_t*>(to);
-  uint32_t* _to_end = reinterpret_cast<uint32_t*>(to_end);
-  uint32_t* _to_nxt = _to;
-  result r          = utf16be_to_ucs4(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
+  auto* _to               = reinterpret_cast<underlying_wchar_t*>(to);
+  auto* _to_end           = reinterpret_cast<underlying_wchar_t*>(to_end);
+  auto* _to_nxt           = _to;
+  result r =
+      overload<utf16be_to_ucs2, utf16be_to_ucs4>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
   frm_nxt = frm + (_frm_nxt - _frm);
   to_nxt  = to + (_to_nxt - _to);
   return r;
@@ -3213,23 +3197,17 @@ int __codecvt_utf16<wchar_t, false>::do_length(
     state_type&, const extern_type* frm, const extern_type* frm_end, size_t mx) const {
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  return utf16be_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  else
-  return utf16be_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  endif
+  if constexpr (is_short_wchar)
+    return utf16be_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
+  else
+    return utf16be_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
 }
 
 int __codecvt_utf16<wchar_t, false>::do_max_length() const noexcept {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  if (__mode_ & consume_header)
-    return 4;
-  return 2;
-#  else
-  if (__mode_ & consume_header)
-    return 6;
-  return 4;
-#  endif
+  if constexpr (is_short_wchar)
+    return (__mode_ & consume_header) ? 4 : 2;
+  else
+    return (__mode_ & consume_header) ? 6 : 4;
 }
 
 // __codecvt_utf16<wchar_t, true>
@@ -3242,23 +3220,14 @@ __codecvt_utf16<wchar_t, true>::result __codecvt_utf16<wchar_t, true>::do_out(
     extern_type* to,
     extern_type* to_end,
     extern_type*& to_nxt) const {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  const uint16_t* _frm     = reinterpret_cast<const uint16_t*>(frm);
-  const uint16_t* _frm_end = reinterpret_cast<const uint16_t*>(frm_end);
-  const uint16_t* _frm_nxt = _frm;
-#  else
-  const uint32_t* _frm     = reinterpret_cast<const uint32_t*>(frm);
-  const uint32_t* _frm_end = reinterpret_cast<const uint32_t*>(frm_end);
-  const uint32_t* _frm_nxt = _frm;
-#  endif
-  uint8_t* _to     = reinterpret_cast<uint8_t*>(to);
-  uint8_t* _to_end = reinterpret_cast<uint8_t*>(to_end);
-  uint8_t* _to_nxt = _to;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  result r = ucs2_to_utf16le(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  result r = ucs4_to_utf16le(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
+  const auto* _frm     = reinterpret_cast<const underlying_wchar_t*>(frm);
+  const auto* _frm_end = reinterpret_cast<const underlying_wchar_t*>(frm_end);
+  const auto* _frm_nxt = _frm;
+  uint8_t* _to         = reinterpret_cast<uint8_t*>(to);
+  uint8_t* _to_end     = reinterpret_cast<uint8_t*>(to_end);
+  uint8_t* _to_nxt     = _to;
+  result r =
+      overload<ucs2_to_utf16le, ucs4_to_utf16le>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
   frm_nxt = frm + (_frm_nxt - _frm);
   to_nxt  = to + (_to_nxt - _to);
   return r;
@@ -3275,17 +3244,11 @@ __codecvt_utf16<wchar_t, true>::result __codecvt_utf16<wchar_t, true>::do_in(
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
   const uint8_t* _frm_nxt = _frm;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  uint16_t* _to     = reinterpret_cast<uint16_t*>(to);
-  uint16_t* _to_end = reinterpret_cast<uint16_t*>(to_end);
-  uint16_t* _to_nxt = _to;
-  result r          = utf16le_to_ucs2(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  uint32_t* _to     = reinterpret_cast<uint32_t*>(to);
-  uint32_t* _to_end = reinterpret_cast<uint32_t*>(to_end);
-  uint32_t* _to_nxt = _to;
-  result r          = utf16le_to_ucs4(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
+  auto* _to               = reinterpret_cast<underlying_wchar_t*>(to);
+  auto* _to_end           = reinterpret_cast<underlying_wchar_t*>(to_end);
+  auto* _to_nxt           = _to;
+  result r =
+      overload<utf16le_to_ucs2, utf16le_to_ucs4>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
   frm_nxt = frm + (_frm_nxt - _frm);
   to_nxt  = to + (_to_nxt - _to);
   return r;
@@ -3305,23 +3268,17 @@ int __codecvt_utf16<wchar_t, true>::do_length(
     state_type&, const extern_type* frm, const extern_type* frm_end, size_t mx) const {
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  return utf16le_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  else
-  return utf16le_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  endif
+  if constexpr (is_short_wchar)
+    return utf16le_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
+  else
+    return utf16le_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
 }
 
 int __codecvt_utf16<wchar_t, true>::do_max_length() const noexcept {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  if (__mode_ & consume_header)
-    return 4;
-  return 2;
-#  else
-  if (__mode_ & consume_header)
-    return 6;
-  return 4;
-#  endif
+  if constexpr (is_short_wchar)
+    return (__mode_ & consume_header) ? 4 : 2;
+  else
+    return (__mode_ & consume_header) ? 6 : 4;
 }
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
@@ -3604,15 +3561,9 @@ __codecvt_utf8_utf16<wchar_t>::result __codecvt_utf8_utf16<wchar_t>::do_out(
     extern_type* to,
     extern_type* to_end,
     extern_type*& to_nxt) const {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  const uint16_t* _frm     = reinterpret_cast<const uint16_t*>(frm);
-  const uint16_t* _frm_end = reinterpret_cast<const uint16_t*>(frm_end);
-  const uint16_t* _frm_nxt = _frm;
-#  else
-  const uint32_t* _frm     = reinterpret_cast<const uint32_t*>(frm);
-  const uint32_t* _frm_end = reinterpret_cast<const uint32_t*>(frm_end);
-  const uint32_t* _frm_nxt = _frm;
-#  endif
+  const auto* _frm     = reinterpret_cast<const underlying_wchar_t*>(frm);
+  const auto* _frm_end = reinterpret_cast<const underlying_wchar_t*>(frm_end);
+  const auto* _frm_nxt = _frm;
   uint8_t* _to     = reinterpret_cast<uint8_t*>(to);
   uint8_t* _to_end = reinterpret_cast<uint8_t*>(to_end);
   uint8_t* _to_nxt = _to;
@@ -3633,18 +3584,12 @@ __codecvt_utf8_utf16<wchar_t>::result __codecvt_utf8_utf16<wchar_t>::do_in(
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
   const uint8_t* _frm_nxt = _frm;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  uint16_t* _to     = reinterpret_cast<uint16_t*>(to);
-  uint16_t* _to_end = reinterpret_cast<uint16_t*>(to_end);
-  uint16_t* _to_nxt = _to;
-#  else
-  uint32_t* _to     = reinterpret_cast<uint32_t*>(to);
-  uint32_t* _to_end = reinterpret_cast<uint32_t*>(to_end);
-  uint32_t* _to_nxt = _to;
-#  endif
-  result r = utf8_to_utf16(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-  frm_nxt  = frm + (_frm_nxt - _frm);
-  to_nxt   = to + (_to_nxt - _to);
+  auto* _to               = reinterpret_cast<underlying_wchar_t*>(to);
+  auto* _to_end           = reinterpret_cast<underlying_wchar_t*>(to_end);
+  auto* _to_nxt           = _to;
+  result r                = utf8_to_utf16(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
+  frm_nxt                 = frm + (_frm_nxt - _frm);
+  to_nxt                  = to + (_to_nxt - _to);
   return r;
 }
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..2fcaf6582afb0 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -517,7 +517,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NO_UNIQUE_ADDRESS
 #endif
 
-#ifdef _LIBCPP_SHORT_WCHAR
+#if defined(_WIN32) || defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2
 #  define TEST_SHORT_WCHAR
 #endif
 


From libcxx-commits at lists.llvm.org  Sun Jul  5 01:00:38 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 01:00:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove _LIBCPP_SHORT_WCHAR (PR
	#207562)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207562@github.com>
Message-ID: <6a4a0f26.13be6df8.3d8759.77e4@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

We can just check for `sizeof(wchar_t)` in most places instead. The only place that seems problematic is `test_macros.h`, where we can use `__SIZEOF_WCHAR_T__` instead.


---
Full diff: https://github.com/llvm/llvm-project/pull/207562.diff


5 Files Affected:

- (modified) libcxx/include/__config (-6) 
- (modified) libcxx/include/__cxx03/__config (-6) 
- (modified) libcxx/include/print (+6-13) 
- (modified) libcxx/src/locale.cpp (+96-151) 
- (modified) libcxx/test/support/test_macros.h (+1-1) 


``````````diff
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..9c3b569861db7 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -44,7 +44,6 @@
 
 #  if defined(_WIN32)
 #    define _LIBCPP_WIN32API
-#    define _LIBCPP_SHORT_WCHAR 1
 // Both MinGW and native MSVC provide a "MSVC"-like environment
 #    define _LIBCPP_MSVCRT_LIKE
 // If mingw not explicitly detected, assume using MS C runtime only if
@@ -57,11 +56,6 @@
 #    define _LIBCPP_HAS_OPEN_WITH_WCHAR 0
 #  endif // defined(_WIN32)
 
-#  if defined(_AIX) && !defined(__64BIT__)
-// The size of wchar is 2 byte on 32-bit mode on AIX.
-#    define _LIBCPP_SHORT_WCHAR 1
-#  endif
-
 // Libc++ supports various implementations of std::random_device.
 //
 // _LIBCPP_USING_DEV_RANDOM
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 70a4f5cff9138..6679494a1f936 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -221,7 +221,6 @@ _LIBCPP_HARDENING_MODE_DEBUG
 
 #  if defined(_WIN32)
 #    define _LIBCPP_WIN32API
-#    define _LIBCPP_SHORT_WCHAR 1
 // Both MinGW and native MSVC provide a "MSVC"-like environment
 #    define _LIBCPP_MSVCRT_LIKE
 // If mingw not explicitly detected, assume using MS C runtime only if
@@ -232,11 +231,6 @@ _LIBCPP_HARDENING_MODE_DEBUG
 #    define _LIBCPP_HAS_OPEN_WITH_WCHAR
 #  endif // defined(_WIN32)
 
-#  if defined(_AIX) && !defined(__64BIT__)
-// The size of wchar is 2 byte on 32-bit mode on AIX.
-#    define _LIBCPP_SHORT_WCHAR 1
-#  endif
-
 // Libc++ supports various implementations of std::random_device.
 //
 // _LIBCPP_USING_DEV_RANDOM
diff --git a/libcxx/include/print b/libcxx/include/print
index cf62743846f42..d30d0be0130c2 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -89,27 +89,20 @@ namespace __unicode {
 // The names of these concepts are modelled after P2728R0, but the
 // implementation is not. char16_t may contain 32-bits so depending on the
 // number of bits is an issue.
-#      ifdef _LIBCPP_SHORT_WCHAR
 template <class _Tp>
 concept __utf16_code_unit =
     same_as<_Tp, char16_t>
-#        if _LIBCPP_HAS_WIDE_CHARACTERS
-    || same_as<_Tp, wchar_t>
-#        endif
+#      if _LIBCPP_HAS_WIDE_CHARACTERS
+    || (sizeof(wchar_t) == sizeof(char16_t) && same_as<_Tp, wchar_t>)
+#      endif
     ;
 template <class _Tp>
-concept __utf32_code_unit = same_as<_Tp, char32_t>;
-#      else // _LIBCPP_SHORT_WCHAR
-template <class _Tp>
-concept __utf16_code_unit = same_as<_Tp, char16_t>;
-template <class _Tp>
 concept __utf32_code_unit =
     same_as<_Tp, char32_t>
-#        if _LIBCPP_HAS_WIDE_CHARACTERS
-    || same_as<_Tp, wchar_t>
-#        endif
+#      if _LIBCPP_HAS_WIDE_CHARACTERS
+    || (sizeof(wchar_t) == sizeof(char32_t) && same_as<_Tp, wchar_t>)
+#      endif
     ;
-#      endif // _LIBCPP_SHORT_WCHAR
 
 // Pass by reference since an output_iterator may not be copyable.
 template <class _OutIt>
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index b0a69ab74f94c..51548f96ce6f1 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -2913,6 +2913,28 @@ int codecvt<char32_t, char8_t, mbstate_t>::do_max_length() const noexcept { retu
 // __codecvt_utf8<wchar_t>
 
 #if _LIBCPP_HAS_WIDE_CHARACTERS
+
+constexpr bool is_short_wchar = sizeof(wchar_t) == sizeof(uint16_t);
+using underlying_wchar_t = conditional_t<is_short_wchar, uint16_t, uint32_t>;
+static_assert(sizeof(underlying_wchar_t) == sizeof(wchar_t));
+
+template <auto callable>
+struct call {
+  template <class... Args>
+  static auto operator()(Args&&... args) -> decltype(callable(std::forward<Args>(args)...)) {
+    return callable(std::forward<Args>(args)...);
+  }
+};
+
+template <auto... Args, class... Args2>
+auto overload(Args2&&... args) {
+  struct overload_t : call<Args>... {
+    using call<Args>::operator()...;
+  };
+
+  return overload_t{}(std::forward<Args2>(args)...);
+}
+
 __codecvt_utf8<wchar_t>::result __codecvt_utf8<wchar_t>::do_out(
     state_type&,
     const intern_type* frm,
@@ -2921,25 +2943,15 @@ __codecvt_utf8<wchar_t>::result __codecvt_utf8<wchar_t>::do_out(
     extern_type* to,
     extern_type* to_end,
     extern_type*& to_nxt) const {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  const uint16_t* _frm     = reinterpret_cast<const uint16_t*>(frm);
-  const uint16_t* _frm_end = reinterpret_cast<const uint16_t*>(frm_end);
-  const uint16_t* _frm_nxt = _frm;
-#  else
-  const uint32_t* _frm     = reinterpret_cast<const uint32_t*>(frm);
-  const uint32_t* _frm_end = reinterpret_cast<const uint32_t*>(frm_end);
-  const uint32_t* _frm_nxt = _frm;
-#  endif
-  uint8_t* _to     = reinterpret_cast<uint8_t*>(to);
-  uint8_t* _to_end = reinterpret_cast<uint8_t*>(to_end);
-  uint8_t* _to_nxt = _to;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  result r = ucs2_to_utf8(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  result r = ucs4_to_utf8(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
-  frm_nxt = frm + (_frm_nxt - _frm);
-  to_nxt  = to + (_to_nxt - _to);
+  const auto* _frm     = reinterpret_cast<const underlying_wchar_t*>(frm);
+  const auto* _frm_end = reinterpret_cast<const underlying_wchar_t*>(frm_end);
+  const auto* _frm_nxt = _frm;
+  uint8_t* _to         = reinterpret_cast<uint8_t*>(to);
+  uint8_t* _to_end     = reinterpret_cast<uint8_t*>(to_end);
+  uint8_t* _to_nxt     = _to;
+  result r = overload<ucs2_to_utf8, ucs4_to_utf8>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
+  frm_nxt  = frm + (_frm_nxt - _frm);
+  to_nxt   = to + (_to_nxt - _to);
   return r;
 }
 
@@ -2954,19 +2966,12 @@ __codecvt_utf8<wchar_t>::result __codecvt_utf8<wchar_t>::do_in(
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
   const uint8_t* _frm_nxt = _frm;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  uint16_t* _to     = reinterpret_cast<uint16_t*>(to);
-  uint16_t* _to_end = reinterpret_cast<uint16_t*>(to_end);
-  uint16_t* _to_nxt = _to;
-  result r          = utf8_to_ucs2(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  uint32_t* _to     = reinterpret_cast<uint32_t*>(to);
-  uint32_t* _to_end = reinterpret_cast<uint32_t*>(to_end);
-  uint32_t* _to_nxt = _to;
-  result r          = utf8_to_ucs4(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
-  frm_nxt = frm + (_frm_nxt - _frm);
-  to_nxt  = to + (_to_nxt - _to);
+  auto* _to               = reinterpret_cast<underlying_wchar_t*>(to);
+  auto* _to_end           = reinterpret_cast<underlying_wchar_t*>(to_end);
+  auto* _to_nxt           = _to;
+  result r = overload<utf8_to_ucs2, utf8_to_ucs4>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
+  frm_nxt  = frm + (_frm_nxt - _frm);
+  to_nxt   = to + (_to_nxt - _to);
   return r;
 }
 
@@ -2984,24 +2989,18 @@ int __codecvt_utf8<wchar_t>::do_length(
     state_type&, const extern_type* frm, const extern_type* frm_end, size_t mx) const {
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  return utf8_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  else
-  return utf8_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  endif
+  if constexpr (is_short_wchar)
+    return utf8_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
+  else
+    return utf8_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
 }
 
 _LIBCPP_SUPPRESS_DEPRECATED_PUSH
 int __codecvt_utf8<wchar_t>::do_max_length() const noexcept {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  if (__mode_ & consume_header)
-    return 6;
-  return 3;
-#  else
-  if (__mode_ & consume_header)
-    return 7;
-  return 4;
-#  endif
+  if constexpr (is_short_wchar)
+    return (__mode_ & consume_header) ? 6 : 3;
+  else
+    return (__mode_ & consume_header) ? 7 : 4;
 }
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
@@ -3150,23 +3149,14 @@ __codecvt_utf16<wchar_t, false>::result __codecvt_utf16<wchar_t, false>::do_out(
     extern_type* to,
     extern_type* to_end,
     extern_type*& to_nxt) const {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  const uint16_t* _frm     = reinterpret_cast<const uint16_t*>(frm);
-  const uint16_t* _frm_end = reinterpret_cast<const uint16_t*>(frm_end);
-  const uint16_t* _frm_nxt = _frm;
-#  else
-  const uint32_t* _frm     = reinterpret_cast<const uint32_t*>(frm);
-  const uint32_t* _frm_end = reinterpret_cast<const uint32_t*>(frm_end);
-  const uint32_t* _frm_nxt = _frm;
-#  endif
-  uint8_t* _to     = reinterpret_cast<uint8_t*>(to);
-  uint8_t* _to_end = reinterpret_cast<uint8_t*>(to_end);
-  uint8_t* _to_nxt = _to;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  result r = ucs2_to_utf16be(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  result r = ucs4_to_utf16be(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
+  const auto* _frm     = reinterpret_cast<const underlying_wchar_t*>(frm);
+  const auto* _frm_end = reinterpret_cast<const underlying_wchar_t*>(frm_end);
+  const auto* _frm_nxt = _frm;
+  uint8_t* _to         = reinterpret_cast<uint8_t*>(to);
+  uint8_t* _to_end     = reinterpret_cast<uint8_t*>(to_end);
+  uint8_t* _to_nxt     = _to;
+  result r =
+      overload<ucs2_to_utf16be, ucs4_to_utf16be>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
   frm_nxt = frm + (_frm_nxt - _frm);
   to_nxt  = to + (_to_nxt - _to);
   return r;
@@ -3183,17 +3173,11 @@ __codecvt_utf16<wchar_t, false>::result __codecvt_utf16<wchar_t, false>::do_in(
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
   const uint8_t* _frm_nxt = _frm;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  uint16_t* _to     = reinterpret_cast<uint16_t*>(to);
-  uint16_t* _to_end = reinterpret_cast<uint16_t*>(to_end);
-  uint16_t* _to_nxt = _to;
-  result r          = utf16be_to_ucs2(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  uint32_t* _to     = reinterpret_cast<uint32_t*>(to);
-  uint32_t* _to_end = reinterpret_cast<uint32_t*>(to_end);
-  uint32_t* _to_nxt = _to;
-  result r          = utf16be_to_ucs4(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
+  auto* _to               = reinterpret_cast<underlying_wchar_t*>(to);
+  auto* _to_end           = reinterpret_cast<underlying_wchar_t*>(to_end);
+  auto* _to_nxt           = _to;
+  result r =
+      overload<utf16be_to_ucs2, utf16be_to_ucs4>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
   frm_nxt = frm + (_frm_nxt - _frm);
   to_nxt  = to + (_to_nxt - _to);
   return r;
@@ -3213,23 +3197,17 @@ int __codecvt_utf16<wchar_t, false>::do_length(
     state_type&, const extern_type* frm, const extern_type* frm_end, size_t mx) const {
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  return utf16be_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  else
-  return utf16be_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  endif
+  if constexpr (is_short_wchar)
+    return utf16be_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
+  else
+    return utf16be_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
 }
 
 int __codecvt_utf16<wchar_t, false>::do_max_length() const noexcept {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  if (__mode_ & consume_header)
-    return 4;
-  return 2;
-#  else
-  if (__mode_ & consume_header)
-    return 6;
-  return 4;
-#  endif
+  if constexpr (is_short_wchar)
+    return (__mode_ & consume_header) ? 4 : 2;
+  else
+    return (__mode_ & consume_header) ? 6 : 4;
 }
 
 // __codecvt_utf16<wchar_t, true>
@@ -3242,23 +3220,14 @@ __codecvt_utf16<wchar_t, true>::result __codecvt_utf16<wchar_t, true>::do_out(
     extern_type* to,
     extern_type* to_end,
     extern_type*& to_nxt) const {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  const uint16_t* _frm     = reinterpret_cast<const uint16_t*>(frm);
-  const uint16_t* _frm_end = reinterpret_cast<const uint16_t*>(frm_end);
-  const uint16_t* _frm_nxt = _frm;
-#  else
-  const uint32_t* _frm     = reinterpret_cast<const uint32_t*>(frm);
-  const uint32_t* _frm_end = reinterpret_cast<const uint32_t*>(frm_end);
-  const uint32_t* _frm_nxt = _frm;
-#  endif
-  uint8_t* _to     = reinterpret_cast<uint8_t*>(to);
-  uint8_t* _to_end = reinterpret_cast<uint8_t*>(to_end);
-  uint8_t* _to_nxt = _to;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  result r = ucs2_to_utf16le(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  result r = ucs4_to_utf16le(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
+  const auto* _frm     = reinterpret_cast<const underlying_wchar_t*>(frm);
+  const auto* _frm_end = reinterpret_cast<const underlying_wchar_t*>(frm_end);
+  const auto* _frm_nxt = _frm;
+  uint8_t* _to         = reinterpret_cast<uint8_t*>(to);
+  uint8_t* _to_end     = reinterpret_cast<uint8_t*>(to_end);
+  uint8_t* _to_nxt     = _to;
+  result r =
+      overload<ucs2_to_utf16le, ucs4_to_utf16le>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
   frm_nxt = frm + (_frm_nxt - _frm);
   to_nxt  = to + (_to_nxt - _to);
   return r;
@@ -3275,17 +3244,11 @@ __codecvt_utf16<wchar_t, true>::result __codecvt_utf16<wchar_t, true>::do_in(
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
   const uint8_t* _frm_nxt = _frm;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  uint16_t* _to     = reinterpret_cast<uint16_t*>(to);
-  uint16_t* _to_end = reinterpret_cast<uint16_t*>(to_end);
-  uint16_t* _to_nxt = _to;
-  result r          = utf16le_to_ucs2(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  uint32_t* _to     = reinterpret_cast<uint32_t*>(to);
-  uint32_t* _to_end = reinterpret_cast<uint32_t*>(to_end);
-  uint32_t* _to_nxt = _to;
-  result r          = utf16le_to_ucs4(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
+  auto* _to               = reinterpret_cast<underlying_wchar_t*>(to);
+  auto* _to_end           = reinterpret_cast<underlying_wchar_t*>(to_end);
+  auto* _to_nxt           = _to;
+  result r =
+      overload<utf16le_to_ucs2, utf16le_to_ucs4>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
   frm_nxt = frm + (_frm_nxt - _frm);
   to_nxt  = to + (_to_nxt - _to);
   return r;
@@ -3305,23 +3268,17 @@ int __codecvt_utf16<wchar_t, true>::do_length(
     state_type&, const extern_type* frm, const extern_type* frm_end, size_t mx) const {
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  return utf16le_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  else
-  return utf16le_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  endif
+  if constexpr (is_short_wchar)
+    return utf16le_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
+  else
+    return utf16le_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
 }
 
 int __codecvt_utf16<wchar_t, true>::do_max_length() const noexcept {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  if (__mode_ & consume_header)
-    return 4;
-  return 2;
-#  else
-  if (__mode_ & consume_header)
-    return 6;
-  return 4;
-#  endif
+  if constexpr (is_short_wchar)
+    return (__mode_ & consume_header) ? 4 : 2;
+  else
+    return (__mode_ & consume_header) ? 6 : 4;
 }
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
@@ -3604,15 +3561,9 @@ __codecvt_utf8_utf16<wchar_t>::result __codecvt_utf8_utf16<wchar_t>::do_out(
     extern_type* to,
     extern_type* to_end,
     extern_type*& to_nxt) const {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  const uint16_t* _frm     = reinterpret_cast<const uint16_t*>(frm);
-  const uint16_t* _frm_end = reinterpret_cast<const uint16_t*>(frm_end);
-  const uint16_t* _frm_nxt = _frm;
-#  else
-  const uint32_t* _frm     = reinterpret_cast<const uint32_t*>(frm);
-  const uint32_t* _frm_end = reinterpret_cast<const uint32_t*>(frm_end);
-  const uint32_t* _frm_nxt = _frm;
-#  endif
+  const auto* _frm     = reinterpret_cast<const underlying_wchar_t*>(frm);
+  const auto* _frm_end = reinterpret_cast<const underlying_wchar_t*>(frm_end);
+  const auto* _frm_nxt = _frm;
   uint8_t* _to     = reinterpret_cast<uint8_t*>(to);
   uint8_t* _to_end = reinterpret_cast<uint8_t*>(to_end);
   uint8_t* _to_nxt = _to;
@@ -3633,18 +3584,12 @@ __codecvt_utf8_utf16<wchar_t>::result __codecvt_utf8_utf16<wchar_t>::do_in(
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
   const uint8_t* _frm_nxt = _frm;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  uint16_t* _to     = reinterpret_cast<uint16_t*>(to);
-  uint16_t* _to_end = reinterpret_cast<uint16_t*>(to_end);
-  uint16_t* _to_nxt = _to;
-#  else
-  uint32_t* _to     = reinterpret_cast<uint32_t*>(to);
-  uint32_t* _to_end = reinterpret_cast<uint32_t*>(to_end);
-  uint32_t* _to_nxt = _to;
-#  endif
-  result r = utf8_to_utf16(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-  frm_nxt  = frm + (_frm_nxt - _frm);
-  to_nxt   = to + (_to_nxt - _to);
+  auto* _to               = reinterpret_cast<underlying_wchar_t*>(to);
+  auto* _to_end           = reinterpret_cast<underlying_wchar_t*>(to_end);
+  auto* _to_nxt           = _to;
+  result r                = utf8_to_utf16(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
+  frm_nxt                 = frm + (_frm_nxt - _frm);
+  to_nxt                  = to + (_to_nxt - _to);
   return r;
 }
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..2fcaf6582afb0 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -517,7 +517,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NO_UNIQUE_ADDRESS
 #endif
 
-#ifdef _LIBCPP_SHORT_WCHAR
+#if defined(_WIN32) || defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2
 #  define TEST_SHORT_WCHAR
 #endif
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/207562

From libcxx-commits at lists.llvm.org  Sun Jul  5 01:01:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 01:01:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove _LIBCPP_SHORT_WCHAR (PR
	#207562)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207562@github.com>
Message-ID: <6a4a0f6b.491d90f5.1f0d58.b6f3@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions ,h,cpp -- libcxx/include/__config libcxx/include/__cxx03/__config libcxx/include/print libcxx/src/locale.cpp libcxx/test/support/test_macros.h --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index 51548f96c..8393faea3 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -2915,7 +2915,7 @@ int codecvt<char32_t, char8_t, mbstate_t>::do_max_length() const noexcept { retu
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 
 constexpr bool is_short_wchar = sizeof(wchar_t) == sizeof(uint16_t);
-using underlying_wchar_t = conditional_t<is_short_wchar, uint16_t, uint32_t>;
+using underlying_wchar_t      = conditional_t<is_short_wchar, uint16_t, uint32_t>;
 static_assert(sizeof(underlying_wchar_t) == sizeof(wchar_t));
 
 template <auto callable>

``````````

</details>


https://github.com/llvm/llvm-project/pull/207562

From libcxx-commits at lists.llvm.org  Sun Jul  5 01:56:09 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 05 Jul 2026 01:56:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Fix LWG issue4481 disallow
	chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a1c29.92d2873c.38c951.260a@mx.google.com>


================
@@ -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
----------------
Zingam wrote:

And also that: LWGs are treated as Defect Reports and applied unconditionally.

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 02:35:54 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 02:35:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] #199778: locale_base_api.h (PR
	#207569)
Message-ID: <llvm.org/llvm/llvm-project/pull/207569@github.com>


https://github.com/trcrsired created https://github.com/llvm/llvm-project/pull/207569

According to issue #199778
PR#194317 broke the build for wasm since __MVS__ define guard was removed.
Try to add it back.


>From f7c39f158d20e9e0a991a33925a8bad05fbcf0a4 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sun, 5 Jul 2026 17:29:22 +0800
Subject: [PATCH] [libcxx] #199778: locale_base_api.h

According to issue #199778
PR#194317 broke the build for wasm since __MVS__ define guard
was removed.
Try to add it back.
---
 libcxx/include/__locale_dir/locale_base_api.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index f973d95ef244d..79df29c7470d9 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -134,7 +134,9 @@
 //       (by providing global non-reserved names) and the new API. As we move individual platforms
 //       towards the new way of defining the locale base API, this should disappear since each platform
 //       will define those directly.
-#    include <__locale_dir/locale_base_api/ibm.h>
+#    ifdef __MVS__
+#      include <__locale_dir/locale_base_api/ibm.h>
+#    endif
 
 #    include <__locale_dir/locale_base_api/bsd_locale_fallbacks.h>
 


From libcxx-commits at lists.llvm.org  Sun Jul  5 02:36:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 02:36:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] #199778: locale_base_api.h (PR
	#207569)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207569@github.com>
Message-ID: <6a4a259a.14d9b9dc.140829.c43b@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: cqwrteur (trcrsired)

<details>
<summary>Changes</summary>

According to issue #<!-- -->199778
PR#<!-- -->194317 broke the build for wasm since __MVS__ define guard was removed.
Try to add it back.


---
Full diff: https://github.com/llvm/llvm-project/pull/207569.diff


1 Files Affected:

- (modified) libcxx/include/__locale_dir/locale_base_api.h (+3-1) 


``````````diff
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index f973d95ef244d..79df29c7470d9 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -134,7 +134,9 @@
 //       (by providing global non-reserved names) and the new API. As we move individual platforms
 //       towards the new way of defining the locale base API, this should disappear since each platform
 //       will define those directly.
-#    include <__locale_dir/locale_base_api/ibm.h>
+#    ifdef __MVS__
+#      include <__locale_dir/locale_base_api/ibm.h>
+#    endif
 
 #    include <__locale_dir/locale_base_api/bsd_locale_fallbacks.h>
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/207569

From libcxx-commits at lists.llvm.org  Sun Jul  5 02:37:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 02:37:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] #199778: locale_base_api.h (PR
	#207569)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207569@github.com>
Message-ID: <6a4a25eb.92d2873c.38c951.1543@mx.google.com>


trcrsired wrote:

@sylvestre @philnik777 @brad0 

https://github.com/llvm/llvm-project/pull/207569

From libcxx-commits at lists.llvm.org  Sun Jul  5 03:08:33 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 03:08:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Cleanup windows macros (PR
	#207577)
Message-ID: <llvm.org/llvm/llvm-project/pull/207577@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207577

We have multiple macros for different Windows. However, most of them don't actually define different things. We can condense the macros to
- `_WIN32` directly if it only depends on windows, or
- `_LIBCPP_LIBC_MSVCRT` (which is equivalent to the old `_LIBCPP_MSVCRT`)

This patch drops `_LIBCPP_MSVCRT_LIKE` and `_LIBCPP_WIN32API`, since they are equivalent to `_WIN32`.



>From 55c3ce9848402bc748ee8c1e7780c4d30d660e02 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 5 Jul 2026 12:01:53 +0200
Subject: [PATCH] [libc++] Cleanup windows macros

---
 libcxx/include/__config                       | 17 ++++----
 libcxx/include/__filesystem/path.h            | 34 ++++++++--------
 libcxx/include/__filesystem/u8path.h          | 14 +++----
 libcxx/include/__locale                       |  2 +-
 libcxx/include/__locale_dir/locale_base_api.h |  2 +-
 libcxx/include/__locale_dir/support/windows.h |  2 +-
 libcxx/include/__math/gamma.h                 |  2 +-
 libcxx/include/__math/traits.h                |  2 +-
 libcxx/include/__ostream/print.h              |  4 +-
 libcxx/include/fstream                        | 10 ++---
 libcxx/include/math.h                         | 10 ++---
 libcxx/include/print                          | 12 +++---
 libcxx/include/stdlib.h                       |  4 +-
 libcxx/include/string.h                       |  2 +-
 libcxx/include/text_encoding                  |  2 +-
 libcxx/include/wchar.h                        |  6 +--
 libcxx/src/chrono.cpp                         |  8 ++--
 libcxx/src/filesystem/directory_iterator.cpp  |  4 +-
 libcxx/src/filesystem/error.h                 |  4 +-
 libcxx/src/filesystem/file_descriptor.h       | 14 +++----
 libcxx/src/filesystem/filesystem_clock.cpp    |  6 +--
 libcxx/src/filesystem/format_string.h         |  2 +-
 libcxx/src/filesystem/operations.cpp          | 14 +++----
 libcxx/src/filesystem/path.cpp                |  2 +-
 libcxx/src/filesystem/path_parser.h           |  4 +-
 libcxx/src/filesystem/posix_compat.h          |  6 +--
 libcxx/src/filesystem/time_utils.h            | 10 ++---
 libcxx/src/fstream.cpp                        |  4 +-
 libcxx/src/include/aligned_alloc.h            |  4 +-
 libcxx/src/include/config_elast.h             |  4 +-
 libcxx/src/iostream.cpp                       |  2 +-
 libcxx/src/locale.cpp                         | 40 +++++++++----------
 libcxx/src/print.cpp                          |  8 ++--
 libcxx/src/std_stream.h                       |  4 +-
 libcxx/src/string.cpp                         |  2 +-
 libcxx/src/support/win32/locale_win32.cpp     |  6 +--
 libcxx/src/system_error.cpp                   | 10 ++---
 libcxx/src/thread.cpp                         |  4 +-
 libcxx/test/support/count_new.h               |  2 +-
 39 files changed, 143 insertions(+), 146 deletions(-)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..bf2cb865fa0e5 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -43,14 +43,11 @@
 #  endif
 
 #  if defined(_WIN32)
-#    define _LIBCPP_WIN32API
 #    define _LIBCPP_SHORT_WCHAR 1
-// Both MinGW and native MSVC provide a "MSVC"-like environment
-#    define _LIBCPP_MSVCRT_LIKE
-// If mingw not explicitly detected, assume using MS C runtime only if
-// a MS compatibility version is specified.
-#    if defined(_MSC_VER) && !defined(__MINGW32__)
-#      define _LIBCPP_MSVCRT // Using Microsoft's C Runtime library
+#    if defined(__MINGW32__)
+#      define _LIBCPP_LIBC_MINGW
+#    else
+#      define _LIBCPP_LIBC_MSVCRT
 #    endif
 #    define _LIBCPP_HAS_OPEN_WITH_WCHAR 1
 #  else
@@ -100,7 +97,7 @@
 #    define _LIBCPP_USING_GETENTROPY
 #  elif defined(__Fuchsia__)
 #    define _LIBCPP_USING_FUCHSIA_CPRNG
-#  elif defined(_LIBCPP_WIN32API)
+#  elif defined(_WIN32)
 #    define _LIBCPP_USING_WIN32_RANDOM
 #  else
 #    define _LIBCPP_USING_DEV_RANDOM
@@ -170,7 +167,7 @@ typedef __char32_t char32_t;
 
 // If we are getting operator new from the MSVC CRT, then allocation overloads
 // for align_val_t were added in 19.12, aka VS 2017 version 15.3.
-#  if defined(_LIBCPP_MSVCRT) && defined(_MSC_VER) && _MSC_VER < 1912
+#  if defined(_LIBCPP_LIBC_MSVCRT) && defined(_MSC_VER) && _MSC_VER < 1912
 #    define _LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION 0
 #  elif defined(_LIBCPP_ABI_VCRUNTIME) && !defined(__cpp_aligned_new)
 // We're deferring to Microsoft's STL to provide aligned new et al. We don't
@@ -227,7 +224,7 @@ typedef __char32_t char32_t;
 // TODO(44575): Switch to C11 thread API when possible.
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#    elif defined(_LIBCPP_WIN32API)
+#    elif defined(_WIN32)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
 #    else
diff --git a/libcxx/include/__filesystem/path.h b/libcxx/include/__filesystem/path.h
index a63c4ee611ebf..f89c829505d61 100644
--- a/libcxx/include/__filesystem/path.h
+++ b/libcxx/include/__filesystem/path.h
@@ -80,7 +80,7 @@ struct __can_convert_char<char32_t> {
 
 template <class _ECharT, __enable_if_t<__can_convert_char<_ECharT>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI bool __is_separator(_ECharT __e) {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   return __e == _ECharT('/') || __e == _ECharT('\\');
 #  else
   return __e == _ECharT('/');
@@ -181,7 +181,7 @@ struct __is_pathable<_Tp, false, true, false> : __is_pathable_char_array<_Tp> {}
 template <class _Tp>
 struct __is_pathable<_Tp, false, false, true> : __is_pathable_iter<_Tp> {};
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 typedef wstring __path_string;
 typedef wchar_t __path_value;
 #  else
@@ -189,7 +189,7 @@ typedef string __path_string;
 typedef char __path_value;
 #  endif
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 _LIBCPP_EXPORTED_FROM_ABI size_t __wide_to_char(const wstring&, char*, size_t);
 _LIBCPP_EXPORTED_FROM_ABI size_t __char_to_wide(const string&, wchar_t*, size_t);
 #  endif
@@ -203,12 +203,12 @@ struct _PathCVT {
   static_assert(__can_convert_char<_ECharT>::value, "Char type not convertible");
 
   typedef __narrow_to_utf8<sizeof(_ECharT) * __CHAR_BIT__> _Narrower;
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
   typedef __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__> _Widener;
 #    endif
 
   _LIBCPP_HIDE_FROM_ABI static void __append_range(__path_string& __dest, _ECharT const* __b, _ECharT const* __e) {
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __b, __e);
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -223,7 +223,7 @@ struct _PathCVT {
     if (__b == __e)
       return;
     basic_string<_ECharT> __tmp(__b, __e);
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __tmp.data(), __tmp.data() + __tmp.length());
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -241,7 +241,7 @@ struct _PathCVT {
     basic_string<_ECharT> __tmp;
     for (; *__b != __sentinel; ++__b)
       __tmp.push_back(*__b);
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __tmp.data(), __tmp.data() + __tmp.length());
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -284,7 +284,7 @@ struct _PathCVT<__path_value> {
   }
 };
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 template <>
 struct _PathCVT<char> {
   _LIBCPP_HIDE_FROM_ABI static void __append_string(__path_string& __dest, const basic_string<char>& __str) {
@@ -369,7 +369,7 @@ struct _PathExport<char8_t> {
 };
 #      endif // _LIBCPP_HAS_CHAR8_T
 #    endif   // _LIBCPP_HAS_LOCALIZATION
-#  endif     // _LIBCPP_WIN32API
+#  endif     // _WIN32
 
 class _LIBCPP_EXPORTED_FROM_ABI path {
   template <class _SourceOrIter, class _Tp = path&>
@@ -382,7 +382,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   using _SourceCVT _LIBCPP_NODEBUG = _PathCVT<_SourceChar<_Tp> >;
 
 public:
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   typedef wchar_t value_type;
   static constexpr value_type preferred_separator = L'\\';
 #  else
@@ -468,7 +468,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
 
 public:
   // appends
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   _LIBCPP_HIDE_FROM_ABI path& operator/=(const path& __p) {
     auto __p_root_name      = __p.__root_name();
     auto __p_root_name_size = __p_root_name.size();
@@ -602,7 +602,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   _LIBCPP_HIDE_FROM_ABI void clear() noexcept { __pn_.clear(); }
 
   _LIBCPP_HIDE_FROM_ABI path& make_preferred() _LIBCPP_LIFETIMEBOUND {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     std::replace(__pn_.begin(), __pn_.end(), L'/', L'\\');
 #  endif
     return *this;
@@ -667,7 +667,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
 
   _LIBCPP_HIDE_FROM_ABI operator string_type() const { return __pn_; }
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::wstring wstring() const { return __pn_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::wstring generic_wstring() const {
@@ -722,7 +722,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
     return __s;
   }
 #    endif // _LIBCPP_HAS_LOCALIZATION
-#  else    /* _LIBCPP_WIN32API */
+#  else    // _WIN32
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::string string() const { return __pn_; }
 #    if _LIBCPP_HAS_CHAR8_T
@@ -775,7 +775,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::u16string generic_u16string() const { return string<char16_t>(); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::u32string generic_u32string() const { return string<char32_t>(); }
 #    endif // _LIBCPP_HAS_LOCALIZATION
-#  endif   /* !_LIBCPP_WIN32API */
+#  endif   // _WIN32
 
 private:
   int __compare(__string_view) const;
@@ -799,7 +799,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_name() const { return string_type(__root_name()); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_directory() const { return string_type(__root_directory()); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_path() const {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     return string_type(__root_path_raw());
 #  else
     return root_name().append(string_type(__root_directory()));
@@ -824,7 +824,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool has_extension() const { return !__extension().empty(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_absolute() const {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     __string_view __root_name_str = __root_name();
     __string_view __root_dir      = __root_directory();
     if (__root_name_str.size() == 2 && __root_name_str[1] == ':') {
diff --git a/libcxx/include/__filesystem/u8path.h b/libcxx/include/__filesystem/u8path.h
index aabd2bbd3c26c..77e7c0d6653da 100644
--- a/libcxx/include/__filesystem/u8path.h
+++ b/libcxx/include/__filesystem/u8path.h
@@ -24,7 +24,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
-#  if !defined(_LIBCPP_WIN32API) || _LIBCPP_HAS_LOCALIZATION
+#  if !defined(_WIN32) || _LIBCPP_HAS_LOCALIZATION
 template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(_InputIt __f, _InputIt __l) {
   static_assert(
@@ -34,7 +34,7 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
           is_same<typename __is_pathable<_InputIt>::__char_type, char>::value,
       "u8path(Iter, Iter) requires Iter have a value_type of type 'char'"
       " or 'char8_t'");
-#    if defined(_LIBCPP_WIN32API)
+#    if defined(_WIN32)
   string __tmp(__f, __l);
   using _CVT = __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__>;
   std::wstring __w;
@@ -43,11 +43,11 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
   return path(__w);
 #    else
   return path(__f, __l);
-#    endif // defined(_LIBCPP_WIN32API)
+#    endif // defined(_WIN32)
 }
-#  endif // !defined(_LIBCPP_WIN32API) || _LIBCPP_HAS_LOCALIZATION
+#  endif // !defined(_WIN32) || _LIBCPP_HAS_LOCALIZATION
 
-#  if defined(_LIBCPP_WIN32API) && _LIBCPP_HAS_LOCALIZATION
+#  if defined(_WIN32) && _LIBCPP_HAS_LOCALIZATION
 template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(_InputIt __f, _NullSentinel) {
   static_assert(
@@ -67,7 +67,7 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
   _CVT()(back_inserter(__w), __tmp.data(), __tmp.data() + __tmp.size());
   return path(__w);
 }
-#  endif // defined(_LIBCPP_WIN32API) && _LIBCPP_HAS_LOCALIZATION
+#  endif // defined(_WIN32) && _LIBCPP_HAS_LOCALIZATION
 
 template <class _Source, __enable_if_t<__is_pathable<_Source>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(const _Source& __s) {
@@ -78,7 +78,7 @@ template <class _Source, __enable_if_t<__is_pathable<_Source>::value, int> = 0>
           is_same<typename __is_pathable<_Source>::__char_type, char>::value,
       "u8path(Source const&) requires Source have a character type of type "
       "'char' or 'char8_t'");
-#  if defined(_LIBCPP_WIN32API)
+#  if defined(_WIN32)
   using _Traits = __is_pathable<_Source>;
   return u8path(std::__unwrap_iter(_Traits::__range_begin(__s)), std::__unwrap_iter(_Traits::__range_end(__s)));
 #  else
diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index bb8f8e7bbf5d9..e0524cf4abc4c 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -352,7 +352,7 @@ public:
 #    else
   static const mask __regex_word = 0x80;
 #    endif
-#  elif defined(_LIBCPP_MSVCRT_LIKE)
+#  elif defined(_WIN32)
   typedef unsigned short mask;
   static const mask space        = _SPACE;
   static const mask print        = _BLANK | _PUNCT | _ALPHA | _DIGIT;
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index f973d95ef244d..017d971ec72ba 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -116,7 +116,7 @@
 #    include <__locale_dir/support/netbsd.h>
 #  elif defined(__OpenBSD__)
 #    include <__locale_dir/support/openbsd.h>
-#  elif defined(_LIBCPP_MSVCRT_LIKE)
+#  elif defined(_WIN32)
 #    include <__locale_dir/support/windows.h>
 #  elif defined(__Fuchsia__)
 #    include <__locale_dir/support/fuchsia.h>
diff --git a/libcxx/include/__locale_dir/support/windows.h b/libcxx/include/__locale_dir/support/windows.h
index 6675cfb740b29..a20b5226738a6 100644
--- a/libcxx/include/__locale_dir/support/windows.h
+++ b/libcxx/include/__locale_dir/support/windows.h
@@ -172,7 +172,7 @@ _LIBCPP_EXPORTED_FROM_ABI const char* __get_locale_encoding(__locale_t __loc);
 //
 
 // the *_l functions are prefixed on Windows, only available for msvcr80+, VS2005+
-#if defined(_LIBCPP_MSVCRT)
+#if defined(_LIBCPP_LIBC_MSVCRT)
 inline _LIBCPP_HIDE_FROM_ABI float __strtof(const char* __nptr, char** __endptr, __locale_t __loc) {
   return ::_strtof_l(__nptr, __endptr, __loc);
 }
diff --git a/libcxx/include/__math/gamma.h b/libcxx/include/__math/gamma.h
index a742ea42d9009..08e09d7cb7aef 100644
--- a/libcxx/include/__math/gamma.h
+++ b/libcxx/include/__math/gamma.h
@@ -62,7 +62,7 @@ inline _LIBCPP_HIDE_FROM_ABI double tgamma(_A1 __x) _NOEXCEPT {
 // declare it differently in the first place: instead use `asm` to get the compiler to call the right
 // function.
 
-#if defined(_LIBCPP_MSVCRT_LIKE) // reentrant version is not available on Windows
+#ifdef _WIN32 // reentrant version is not available on Windows
 
 inline _LIBCPP_HIDE_FROM_ABI double __lgamma_r(double __d) _NOEXCEPT { return __builtin_lgamma(__d); }
 
diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..5dd489c8b0a36 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -188,7 +188,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // TODO: Remove the workaround once UCRT fixes these functions. Note that this doesn't seem planned as of 2025-07 per
 // https://developercommunity.visualstudio.com/t/10294165.
 
-#if defined(_LIBCPP_MSVCRT) && _LIBCPP_STD_VER >= 20
+#if defined(_LIBCPP_LIBC_MSVCRT) && _LIBCPP_STD_VER >= 20
 namespace __ucrt {
 template <class _A1>
   requires is_integral_v<_A1>
diff --git a/libcxx/include/__ostream/print.h b/libcxx/include/__ostream/print.h
index 5b4ab60a45eea..4a519c177b990 100644
--- a/libcxx/include/__ostream/print.h
+++ b/libcxx/include/__ostream/print.h
@@ -89,7 +89,7 @@ _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 #    if _LIBCPP_HAS_UNICODE
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
-#      ifndef _LIBCPP_WIN32API
+#      ifndef _WIN32
   return std::__vprint_nonunicode(__os, __fmt, __args, __write_nl);
 #      else
   FILE* __file = std::__get_ostream_file(__os);
@@ -123,7 +123,7 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo
     __os.__set_badbit_and_consider_rethrow();
   }
 #        endif // _LIBCPP_HAS_EXCEPTIONS
-#      endif   // _LIBCPP_WIN32API
+#      endif   // _WIN32
 }
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..78fcdf254b493 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -222,7 +222,7 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#    if _LIBCPP_STD_VER >= 23 && defined(_LIBCPP_WIN32API)
+#    if _LIBCPP_STD_VER >= 23 && defined(_WIN32)
 _LIBCPP_EXPORTED_FROM_ABI void* __filebuf_windows_native_handle(FILE* __file) noexcept;
 #    endif
 
@@ -236,7 +236,7 @@ public:
   typedef typename traits_type::off_type off_type;
   typedef typename traits_type::state_type state_type;
 #    if _LIBCPP_STD_VER >= 26
-#      if defined(_LIBCPP_WIN32API)
+#      if defined(_WIN32)
   using native_handle_type = void*; // HANDLE
 #      elif __has_include(<unistd.h>)
   using native_handle_type = int; // POSIX file descriptor
@@ -272,7 +272,7 @@ public:
 #    if _LIBCPP_STD_VER >= 26
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() const noexcept {
     _LIBCPP_ASSERT_UNCATEGORIZED(this->is_open(), "File must be opened");
-#      if defined(_LIBCPP_WIN32API)
+#      if defined(_WIN32)
     return std::__filebuf_windows_native_handle(__file_);
 #      elif __has_include(<unistd.h>)
     return fileno(__file_);
@@ -1020,7 +1020,7 @@ basic_filebuf<_CharT, _Traits>::seekoff(off_type __off, ios_base::seekdir __way,
 
 template <class _CharT, class _Traits>
 int basic_filebuf<_CharT, _Traits>::__fseek(FILE* __file, pos_type __offset, int __whence) {
-#    if defined(_LIBCPP_MSVCRT_LIKE)
+#    ifdef _WIN32
   return _fseeki64(__file, __offset, __whence);
 #    elif _LIBCPP_LIBC_NEWLIB
   return fseek(__file, __offset, __whence);
@@ -1031,7 +1031,7 @@ int basic_filebuf<_CharT, _Traits>::__fseek(FILE* __file, pos_type __offset, int
 
 template <class _CharT, class _Traits>
 typename basic_filebuf<_CharT, _Traits>::pos_type basic_filebuf<_CharT, _Traits>::__ftell(FILE* __file) {
-#    if defined(_LIBCPP_MSVCRT_LIKE)
+#    ifdef _WIN32
   return _ftelli64(__file);
 #    elif _LIBCPP_LIBC_NEWLIB
   return ftell(__file);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..d35d305a682eb 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -416,7 +416,7 @@ using std::__math::fpclassify;
 using std::__math::signbit;
 
 // The MSVC runtime already provides these functions as templates
-#      ifndef _LIBCPP_MSVCRT
+#      ifndef _LIBCPP_LIBC_MSVCRT
 using std::__math::isfinite;
 using std::__math::isgreater;
 using std::__math::isgreaterequal;
@@ -427,9 +427,9 @@ using std::__math::islessgreater;
 using std::__math::isnan;
 using std::__math::isnormal;
 using std::__math::isunordered;
-#      endif // _LIBCPP_MSVCRT
+#      endif // _LIBCPP_LIBC_MSVCRT
 
-#      if defined(_LIBCPP_MSVCRT) && _LIBCPP_STD_VER >= 20
+#      if defined(_LIBCPP_LIBC_MSVCRT) && _LIBCPP_STD_VER >= 20
 // MS UCRT incorrectly defines some functions in a way not working with integer types. Until C++20, this was worked
 // around by -fdelayed-template-parsing. Since C++20, we can use standard feature "requires" instead.
 
@@ -446,7 +446,7 @@ using std::__math::__ucrt::islessgreater;
 using std::__math::__ucrt::isnan;
 using std::__math::__ucrt::isnormal;
 using std::__math::__ucrt::isunordered;
-#      endif // defined(_LIBCPP_MSVCRT) && _LIBCPP_STD_VER >= 20
+#      endif // defined(_LIBCPP_LIBC_MSVCRT) && _LIBCPP_STD_VER >= 20
 
 // We have to provide double overloads for <math.h> to work on platforms that don't provide the full set of math
 // functions. To make the overload set work with multiple functions that take the same arguments, we make our overloads
@@ -527,7 +527,7 @@ using std::__math::trunc;
 //
 // and receive the definitions of mathematical constants, even if <math.h>
 // has previously been included.
-#  if defined(_LIBCPP_MSVCRT) && defined(_USE_MATH_DEFINES)
+#  if defined(_LIBCPP_LIBC_MSVCRT) && defined(_USE_MATH_DEFINES)
 #    include_next <math.h>
 #  endif
 
diff --git a/libcxx/include/print b/libcxx/include/print
index cf62743846f42..a9c4ee686f5f9 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -56,7 +56,7 @@ namespace std {
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#    ifdef _LIBCPP_WIN32API
+#    ifdef _WIN32
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
 
@@ -73,7 +73,7 @@ _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
 _LIBCPP_EXPORTED_FROM_ABI void __write_to_windows_console(FILE* __stream, wstring_view __view);
 #      endif // _LIBCPP_HAS_WIDE_CHARACTERS
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
-#    endif   // _LIBCPP_WIN32API
+#    endif   // _WIN32
 
 #    if _LIBCPP_HAS_UNICODE
 // This is the code to transcode UTF-8 to UTF-16. This is used on
@@ -197,7 +197,7 @@ inline constexpr bool __use_unicode_execution_charset = _MSVC_EXECUTION_CHARACTE
 inline constexpr bool __use_unicode_execution_charset = true;
 #    endif
 
-#    ifdef _LIBCPP_WIN32API
+#    ifdef _WIN32
 _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream) {
   // The macro _LIBCPP_TESTING_PRINT_IS_TERMINAL is used to change
   // the behavior in the test. This is not part of the public API.
@@ -207,7 +207,7 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream)
   return std::__is_windows_terminal(__stream);
 #      endif
 }
-#    endif // _LIBCPP_WIN32API
+#    endif // _WIN32
 
 [[noreturn]] _LIBCPP_HIDE_FROM_ABI inline void __handle_output_error(FILE* __stream) {
   if (std::feof(__stream))
@@ -251,7 +251,7 @@ _LIBCPP_HIDE_FROM_ABI inline void __output_unicode_windows([[maybe_unused]] FILE
   // the behavior in the test. This is not part of the public API.
 #        ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
   _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION(__stream, __view);
-#        elif defined(_LIBCPP_WIN32API)
+#        elif defined(_WIN32)
   std::__write_to_windows_console(__stream, __view);
 #        else
   std::__throw_runtime_error("No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
@@ -282,7 +282,7 @@ _LIBCPP_HIDE_FROM_ABI inline void __output_unicode([[maybe_unused]] FILE* __stre
   // so there the call can be forwarded to the non_unicode API. On
   // Windows there is a different API. This API requires transcoding.
 
-#      ifndef _LIBCPP_WIN32API
+#      ifndef _WIN32
   __print::__output_nonunicode(__stream, __text);
 #      elif _LIBCPP_HAS_WIDE_CHARACTERS
   if (__print::__is_terminal(__stream)) {
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..0eb4a9ed433d2 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -122,12 +122,12 @@ using std::__math::abs;
 #      endif
 
 // MSVCRT already has the correct prototype in <stdlib.h> if __cplusplus is defined
-#      if !defined(_LIBCPP_MSVCRT)
+#      if !defined(_LIBCPP_LIBC_MSVCRT)
 inline _LIBCPP_HIDE_FROM_ABI ldiv_t div(long __x, long __y) _NOEXCEPT { return ::ldiv(__x, __y); }
 #        if !(defined(__FreeBSD__) && !defined(__LONG_LONG_SUPPORTED))
 inline _LIBCPP_HIDE_FROM_ABI lldiv_t div(long long __x, long long __y) _NOEXCEPT { return ::lldiv(__x, __y); }
 #        endif
-#      endif // _LIBCPP_MSVCRT
+#      endif // _LIBCPP_LIBC_MSVCRT
 } // extern "C++"
 #    endif   // __cplusplus
 #  endif     // _LIBCPP_STDLIB_H
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..d494b37d73413 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -69,7 +69,7 @@ size_t strlen(const char* s);
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
 // the right signature.
-#  if defined(__CORRECT_ISO_CPP_STRING_H_PROTO) || defined(_LIBCPP_MSVCRT) ||                                          \
+#  if defined(__CORRECT_ISO_CPP_STRING_H_PROTO) || defined(_LIBCPP_LIBC_MSVCRT) ||                                          \
       defined(_STRING_H_CPLUSPLUS_98_CONFORMANCE_)
 #    define _LIBCPP_STRING_H_HAS_CONST_OVERLOADS
 #  endif
diff --git a/libcxx/include/text_encoding b/libcxx/include/text_encoding
index da39be92c3dfa..c00fe0d278dc0 100644
--- a/libcxx/include/text_encoding
+++ b/libcxx/include/text_encoding
@@ -598,7 +598,7 @@ public:
 
 #    if _LIBCPP_HAS_LOCALIZATION
   [[nodiscard]] _LIBCPP_AVAILABILITY_TEXT_ENCODING_ENVIRONMENT static text_encoding environment() {
-#      if defined(_LIBCPP_WIN32API)
+#      ifdef _WIN32
     return __get_locale_encoding(nullptr);
 #      else
     return __get_locale_encoding("");
diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index 7ff03870bdfdb..c68f284879cb0 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -129,7 +129,7 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 #      if __GLIBC_PREREQ(2, 10)
 #        define _LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS 1
 #      endif
-#    elif defined(_LIBCPP_MSVCRT)
+#    elif defined(_LIBCPP_LIBC_MSVCRT)
 #      if defined(_CRT_CONST_CORRECT_OVERLOADS)
 #        define _LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS 1
 #      endif
@@ -195,14 +195,14 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD wchar_t* wmemchr(wchar_t
 }
 #      endif
 
-#      if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
+#      if defined(__cplusplus) && (defined(_WIN32) || defined(__MVS__))
 extern "C" {
 size_t mbsnrtowcs(
     wchar_t* __restrict __dst, const char** __restrict __src, size_t __nmc, size_t __len, mbstate_t* __restrict __ps);
 size_t wcsnrtombs(
     char* __restrict __dst, const wchar_t** __restrict __src, size_t __nwc, size_t __len, mbstate_t* __restrict __ps);
 } // extern "C"
-#      endif // __cplusplus && (_LIBCPP_MSVCRT || __MVS__)
+#      endif // __cplusplus && (_LIBCPP_LIBC_MSVCRT || __MVS__)
 #    endif   // _LIBCPP_HAS_WIDE_CHARACTERS
 #  endif     // _LIBCPP_WCHAR_H
 
diff --git a/libcxx/src/chrono.cpp b/libcxx/src/chrono.cpp
index af5fe7c01fb8b..30c1a1f119a92 100644
--- a/libcxx/src/chrono.cpp
+++ b/libcxx/src/chrono.cpp
@@ -41,14 +41,14 @@
 #  define _LIBCPP_HAS_CLOCK_GETTIME
 #endif
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define VC_EXTRA_LEAN
 #  include <windows.h>
 #  if _WIN32_WINNT >= _WIN32_WINNT_WIN8
 #    include <winapifamily.h>
 #  endif
-#endif // defined(_LIBCPP_WIN32API)
+#endif // _WIN32
 
 #if defined(__Fuchsia__)
 #  include <zircon/syscalls.h>
@@ -71,7 +71,7 @@ namespace chrono {
 // system_clock
 //
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 #  if _WIN32_WINNT < _WIN32_WINNT_WIN8
 
@@ -184,7 +184,7 @@ static steady_clock::time_point __libcpp_steady_clock_now() {
   return steady_clock::time_point(seconds(tp.tv_sec) + nanoseconds(tp.tv_nsec));
 }
 
-#  elif defined(_LIBCPP_WIN32API)
+#  elif defined(_WIN32)
 
 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx says:
 //    If the function fails, the return value is zero. <snip>
diff --git a/libcxx/src/filesystem/directory_iterator.cpp b/libcxx/src/filesystem/directory_iterator.cpp
index 5d682aee2b839..fd2bd504b221b 100644
--- a/libcxx/src/filesystem/directory_iterator.cpp
+++ b/libcxx/src/filesystem/directory_iterator.cpp
@@ -17,7 +17,7 @@
 #include "error.h"
 #include "file_descriptor.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -30,7 +30,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 using detail::ErrorHandler;
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 class __dir_stream {
 public:
   __dir_stream()                               = delete;
diff --git a/libcxx/src/filesystem/error.h b/libcxx/src/filesystem/error.h
index db5d1ae9a7250..ee1d148e65aa1 100644
--- a/libcxx/src/filesystem/error.h
+++ b/libcxx/src/filesystem/error.h
@@ -23,7 +23,7 @@
 
 #include "format_string.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h> // ERROR_* macros
@@ -42,7 +42,7 @@ inline error_code capture_errno() {
 }
 
 inline error_code get_last_error() {
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   return std::error_code(GetLastError(), std::system_category());
 #else
   return capture_errno();
diff --git a/libcxx/src/filesystem/file_descriptor.h b/libcxx/src/filesystem/file_descriptor.h
index 9c279c451f28c..b095f200912a4 100644
--- a/libcxx/src/filesystem/file_descriptor.h
+++ b/libcxx/src/filesystem/file_descriptor.h
@@ -20,7 +20,7 @@
 #include "posix_compat.h"
 #include "time_utils.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -30,13 +30,13 @@
 #  include <sys/stat.h>
 #  include <sys/statvfs.h>
 #  include <unistd.h>
-#endif // defined(_LIBCPP_WIN32API)
+#endif // _WIN32
 
 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 
-#if !defined(_LIBCPP_WIN32API)
+#ifndef _WIN32
 
 #  if defined(DT_BLK)
 template <class DirEntT, class = decltype(DirEntT::d_type)>
@@ -84,7 +84,7 @@ inline pair<string_view, file_type> posix_readdir(DIR* dir_stream, error_code& e
   }
 }
 
-#else // _LIBCPP_WIN32API
+#else // _WIN32
 
 inline file_type get_file_type(const WIN32_FIND_DATAW& data) {
   if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
@@ -111,7 +111,7 @@ inline perms get_file_perm(const WIN32_FIND_DATAW& data) {
   return static_cast<perms>(st_mode) & perms::mask;
 }
 
-#endif // !_LIBCPP_WIN32API
+#endif // _WIN32
 
 //                       POSIX HELPERS
 
@@ -128,7 +128,7 @@ struct FileDescriptor {
   static FileDescriptor create(const path* p, error_code& ec, Args... args) {
     ec.clear();
     int fd;
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
     // TODO: most of the filesystem implementation uses native Win32 calls
     // (mostly via posix_compat.h). However, here we use the C-runtime APIs to
     // open a file, because we subsequently pass the C-runtime fd to
@@ -171,7 +171,7 @@ struct FileDescriptor {
 
   void close() noexcept {
     if (fd != -1) {
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
       ::_close(fd);
 #else
       ::close(fd);
diff --git a/libcxx/src/filesystem/filesystem_clock.cpp b/libcxx/src/filesystem/filesystem_clock.cpp
index 96a1336d4cbca..0d360c51a7a37 100644
--- a/libcxx/src/filesystem/filesystem_clock.cpp
+++ b/libcxx/src/filesystem/filesystem_clock.cpp
@@ -14,11 +14,11 @@
 #include <ratio>
 #include <time.h>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  include "time_utils.h"
 #endif
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -51,7 +51,7 @@ _LIBCPP_DIAGNOSTIC_POP
 
 _FilesystemClock::time_point _FilesystemClock::now() noexcept {
   typedef chrono::duration<rep> __secs;
-#if defined(_LIBCPP_WIN32API)
+#if defined(_WIN32)
   typedef chrono::duration<rep, nano> __nsecs;
   FILETIME time;
   GetSystemTimeAsFileTime(&time);
diff --git a/libcxx/src/filesystem/format_string.h b/libcxx/src/filesystem/format_string.h
index 8d17b027a6e31..48bdad9a12030 100644
--- a/libcxx/src/filesystem/format_string.h
+++ b/libcxx/src/filesystem/format_string.h
@@ -18,7 +18,7 @@
 #include <cstdio>
 #include <string>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define PATHSTR(x) (L##x)
 #  define PATH_CSTR_FMT "\"%ls\""
 #else
diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp
index 3d358dd55e8b8..d6392ca144262 100644
--- a/libcxx/src/filesystem/operations.cpp
+++ b/libcxx/src/filesystem/operations.cpp
@@ -26,7 +26,7 @@
 #include "posix_compat.h"
 #include "time_utils.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -100,7 +100,7 @@ path __canonical(path const& orig_p, error_code* ec) {
   ErrorHandler<path> err("canonical", ec, &orig_p, &cwd);
 
   path p = __do_absolute(orig_p, &cwd, ec);
-#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API)
+#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_WIN32)
   std::unique_ptr<path::value_type, decltype(&::free)> hold(detail::realpath(p.c_str(), nullptr), &::free);
   if (hold.get() == nullptr)
     return err.report(detail::get_last_error());
@@ -460,7 +460,7 @@ void __copy_symlink(const path& existing_symlink, const path& new_symlink, error
   if (ec && *ec) {
     return;
   }
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   error_code local_ec;
   if (is_directory(real_path, local_ec))
     __create_directory_symlink(real_path, new_symlink, ec);
@@ -564,7 +564,7 @@ void __create_symlink(path const& from, path const& to, error_code* ec) {
 path __current_path(error_code* ec) {
   ErrorHandler<path> err("current_path", ec);
 
-#if defined(_LIBCPP_WIN32API) || defined(__GLIBC__) || defined(__APPLE__)
+#if defined(_WIN32) || defined(__GLIBC__) || defined(__APPLE__)
   // Common extension outside of POSIX getcwd() spec, without needing to
   // preallocate a buffer. Also supported by a number of other POSIX libcs.
   int size              = 0;
@@ -691,7 +691,7 @@ void __last_write_time(const path& p, file_time_type new_time, error_code* ec) {
   using detail::fs_time;
   ErrorHandler<void> err("last_write_time", ec, &p);
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   TimeSpec ts;
   if (!fs_time::convert_to_timespec(ts, new_time))
     return err.report(errc::value_too_large);
@@ -819,7 +819,7 @@ bool __remove(const path& p, error_code* ec) {
 //
 // The second implementation is used on platforms where `openat()` & friends are available,
 // and it threads file descriptors through recursive calls to avoid such race conditions.
-#if defined(_LIBCPP_WIN32API) || defined(__MVS__)
+#if defined(_WIN32) || defined(__MVS__)
 #  define REMOVE_ALL_USE_DIRECTORY_ITERATOR
 #endif
 
@@ -996,7 +996,7 @@ file_status __symlink_status(const path& p, error_code* ec) { return detail::pos
 path __temp_directory_path(error_code* ec) {
   ErrorHandler<path> err("temp_directory_path", ec);
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   wchar_t buf[MAX_PATH];
   DWORD retval = GetTempPathW(MAX_PATH, buf);
   if (!retval)
diff --git a/libcxx/src/filesystem/path.cpp b/libcxx/src/filesystem/path.cpp
index faa7b4f1fbab7..5790039f66426 100644
--- a/libcxx/src/filesystem/path.cpp
+++ b/libcxx/src/filesystem/path.cpp
@@ -417,7 +417,7 @@ path::iterator& path::iterator::__decrement() {
   return *this;
 }
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 ////////////////////////////////////////////////////////////////////////////
 // Windows path conversions
 size_t __wide_to_char(const wstring& str, char* out, size_t outlen) {
diff --git a/libcxx/src/filesystem/path_parser.h b/libcxx/src/filesystem/path_parser.h
index 5968f449235ba..ae91dc7f1cf26 100644
--- a/libcxx/src/filesystem/path_parser.h
+++ b/libcxx/src/filesystem/path_parser.h
@@ -22,7 +22,7 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 inline bool isSeparator(path::value_type C) {
   if (C == '/')
     return true;
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   if (C == '\\')
     return true;
 #endif
@@ -324,7 +324,7 @@ struct PathParser {
   }
 
   PosPtr consumeRootName(PosPtr P, PosPtr End) const noexcept {
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
     if (PosPtr Ret = consumeDriveLetter(P, End))
       return Ret;
     if (PosPtr Ret = consumeNetworkRoot(P, End))
diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h
index ddd99d8aaf206..f5119aebcbefb 100644
--- a/libcxx/src/filesystem/posix_compat.h
+++ b/libcxx/src/filesystem/posix_compat.h
@@ -31,7 +31,7 @@
 #include "error.h"
 #include "time_utils.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <io.h>
@@ -47,7 +47,7 @@
 #include <stdlib.h>
 #include <time.h>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 // This struct isn't defined in the normal Windows SDK, but only in the
 // Windows Driver Kit.
 struct LIBCPP_REPARSE_DATA_BUFFER {
@@ -81,7 +81,7 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 // Various C runtime header sets provide more or less of these. As we
 // provide our own implementation, undef all potential defines from the
diff --git a/libcxx/src/filesystem/time_utils.h b/libcxx/src/filesystem/time_utils.h
index 89352e5bd6abb..2b01ea9542586 100644
--- a/libcxx/src/filesystem/time_utils.h
+++ b/libcxx/src/filesystem/time_utils.h
@@ -22,7 +22,7 @@
 #include "error.h"
 #include "format_string.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -41,7 +41,7 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 // Various C runtime versions (UCRT, or the legacy msvcrt.dll used by
 // some mingw toolchains) provide different stat function implementations,
 // with a number of limitations with respect to what we want from the
@@ -267,7 +267,7 @@ struct time_util : time_util_base<FileTimeT, TimeT> {
   }
 };
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 using fs_time = time_util<file_time_type, int64_t, TimeSpec>;
 #else
 using fs_time = time_util<file_time_type, time_t, TimeSpec>;
@@ -301,7 +301,7 @@ inline TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
 
 #if _LIBCPP_HAS_FILESYSTEM
 
-#  if !defined(_LIBCPP_WIN32API)
+#  ifndef _WIN32
 inline bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS, error_code& ec) {
   TimeVal ConvertedTS[2] = {make_timeval(TS[0]), make_timeval(TS[1])};
   if (::utimes(p.c_str(), ConvertedTS) == -1) {
@@ -329,7 +329,7 @@ inline bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS, err
 #    endif
 }
 
-#  endif // !_LIBCPP_WIN32API
+#  endif // _WIN32
 
 inline file_time_type __extract_last_write_time(const path& p, const StatT& st, error_code* ec) {
   using detail::fs_time;
diff --git a/libcxx/src/fstream.cpp b/libcxx/src/fstream.cpp
index 2975088eb6255..12d4952293972 100644
--- a/libcxx/src/fstream.cpp
+++ b/libcxx/src/fstream.cpp
@@ -10,7 +10,7 @@
 #include <cstdio>
 #include <fstream>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <io.h>
@@ -20,7 +20,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 // Confirm that `HANDLE` is `void*` as implemented in `basic_filebuf`
 static_assert(std::same_as<HANDLE, void*>);
diff --git a/libcxx/src/include/aligned_alloc.h b/libcxx/src/include/aligned_alloc.h
index 24ca26ce04525..2b69a344006a3 100644
--- a/libcxx/src/include/aligned_alloc.h
+++ b/libcxx/src/include/aligned_alloc.h
@@ -27,7 +27,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 //
 // Returns the allocated memory, or `nullptr` on failure.
 inline _LIBCPP_HIDE_FROM_ABI void* __libcpp_aligned_alloc(std::size_t __alignment, std::size_t __size) {
-#  if defined(_LIBCPP_MSVCRT_LIKE)
+#  ifdef _WIN32
   return ::_aligned_malloc(__size, __alignment);
 
 // Android only provides aligned_alloc when targeting API 28 or higher.
@@ -51,7 +51,7 @@ inline _LIBCPP_HIDE_FROM_ABI void* __libcpp_aligned_alloc(std::size_t __alignmen
 }
 
 inline _LIBCPP_HIDE_FROM_ABI void __libcpp_aligned_free(void* __ptr) {
-#  if defined(_LIBCPP_MSVCRT_LIKE)
+#  ifdef _WIN32
   ::_aligned_free(__ptr);
 #  else
   ::free(__ptr);
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..8aa9355c4e7f3 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -11,7 +11,7 @@
 
 #include <__config>
 
-#if defined(_LIBCPP_MSVCRT_LIKE)
+#ifdef _WIN32
 #  include <stdlib.h>
 #else
 #  include <errno.h>
@@ -39,7 +39,7 @@
 // No _LIBCPP_ELAST needed on Apple
 #elif defined(__MVS__)
 #  define _LIBCPP_ELAST 1160
-#elif defined(_LIBCPP_MSVCRT_LIKE)
+#elif defined(_WIN32)
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..884585519af09 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -95,7 +95,7 @@ STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
 // the standard streams, otherwise it may not be alive during program termination
 // when we flush the streams.
 static void force_locale_initialization() {
-#if defined(_LIBCPP_MSVCRT_LIKE)
+#ifdef _WIN32
   static bool once = []() {
     auto loc = __locale::__newlocale(_LIBCPP_ALL_MASK, "C", 0);
     {
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index b0a69ab74f94c..1067a2f2512ce 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -30,7 +30,7 @@
 #  include <sys/localedef.h> // for __lc_ctype_ptr
 #endif
 
-#if defined(_LIBCPP_MSVCRT)
+#if defined(_LIBCPP_LIBC_MSVCRT)
 #  define _CTYPE_DISABLE_MACROS
 #endif
 
@@ -905,7 +905,7 @@ const ctype<char>::mask* ctype<char>::classic_table() noexcept {
   return _C_ctype_tab_ + 1;
 #  elif defined(__GLIBC__)
   return _LIBCPP_GET_C_LOCALE->__ctype_b;
-#  elif defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  elif defined(_WIN32)
   return __pctype_func();
 #  elif defined(__EMSCRIPTEN__)
   return *__ctype_b_loc();
@@ -5360,19 +5360,19 @@ void moneypunct_byname<char, true>::init(const char* nm) {
     __frac_digits_ = lc->int_frac_digits;
   else
     __frac_digits_ = base::do_frac_digits();
-#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#ifdef _WIN32
   if (lc->p_sign_posn == 0)
-#else  // _LIBCPP_MSVCRT
+#else
   if (lc->int_p_sign_posn == 0)
-#endif // !_LIBCPP_MSVCRT
+#endif
     __positive_sign_ = "()";
   else
     __positive_sign_ = lc->positive_sign;
-#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#ifdef _WIN32
   if (lc->n_sign_posn == 0)
-#else  // _LIBCPP_MSVCRT
+#else
   if (lc->int_n_sign_posn == 0)
-#endif // !_LIBCPP_MSVCRT
+#endif
     __negative_sign_ = "()";
   else
     __negative_sign_ = lc->negative_sign;
@@ -5380,10 +5380,10 @@ void moneypunct_byname<char, true>::init(const char* nm) {
   // the same places in curr_symbol since there's no way to
   // represent anything else.
   string_type __dummy_curr_symbol = __curr_symbol_;
-#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#ifdef _WIN32
   __init_pat(__pos_format_, __dummy_curr_symbol, true, lc->p_cs_precedes, lc->p_sep_by_space, lc->p_sign_posn, ' ');
   __init_pat(__neg_format_, __curr_symbol_, true, lc->n_cs_precedes, lc->n_sep_by_space, lc->n_sign_posn, ' ');
-#else  // _LIBCPP_MSVCRT
+#else  // _WIN32
   __init_pat(
       __pos_format_,
       __dummy_curr_symbol,
@@ -5394,7 +5394,7 @@ void moneypunct_byname<char, true>::init(const char* nm) {
       ' ');
   __init_pat(
       __neg_format_, __curr_symbol_, true, lc->int_n_cs_precedes, lc->int_n_sep_by_space, lc->int_n_sign_posn, ' ');
-#endif // !_LIBCPP_MSVCRT
+#endif // _WIN32
 }
 
 #if _LIBCPP_HAS_WIDE_CHARACTERS
@@ -5477,11 +5477,11 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
     __frac_digits_ = lc->int_frac_digits;
   else
     __frac_digits_ = base::do_frac_digits();
-#  if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  ifdef _WIN32
   if (lc->p_sign_posn == 0)
-#  else  // _LIBCPP_MSVCRT
+#  else
   if (lc->int_p_sign_posn == 0)
-#  endif // !_LIBCPP_MSVCRT
+#  endif
     __positive_sign_ = L"()";
   else {
     mb = mbstate_t();
@@ -5492,11 +5492,11 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
     wbe = wbuf + j;
     __positive_sign_.assign(wbuf, wbe);
   }
-#  if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  ifdef _WIN32
   if (lc->n_sign_posn == 0)
-#  else  // _LIBCPP_MSVCRT
+#  else
   if (lc->int_n_sign_posn == 0)
-#  endif // !_LIBCPP_MSVCRT
+#  endif
     __negative_sign_ = L"()";
   else {
     mb = mbstate_t();
@@ -5511,10 +5511,10 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
   // the same places in curr_symbol since there's no way to
   // represent anything else.
   string_type __dummy_curr_symbol = __curr_symbol_;
-#  if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  ifdef _WIN32
   __init_pat(__pos_format_, __dummy_curr_symbol, true, lc->p_cs_precedes, lc->p_sep_by_space, lc->p_sign_posn, L' ');
   __init_pat(__neg_format_, __curr_symbol_, true, lc->n_cs_precedes, lc->n_sep_by_space, lc->n_sign_posn, L' ');
-#  else  // _LIBCPP_MSVCRT
+#  else // _WIN32
   __init_pat(
       __pos_format_,
       __dummy_curr_symbol,
@@ -5525,7 +5525,7 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
       L' ');
   __init_pat(
       __neg_format_, __curr_symbol_, true, lc->int_n_cs_precedes, lc->int_n_sep_by_space, lc->int_n_sign_posn, L' ');
-#  endif // !_LIBCPP_MSVCRT
+#  endif // _WIN32
 }
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
diff --git a/libcxx/src/print.cpp b/libcxx/src/print.cpp
index 21d868e8c6a7a..2eb8a0f31488c 100644
--- a/libcxx/src/print.cpp
+++ b/libcxx/src/print.cpp
@@ -15,7 +15,7 @@
 
 #include "filesystem/error.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <io.h>
@@ -35,7 +35,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream) {
   // Note the Standard does this in one call, but it's unclear whether
@@ -65,13 +65,13 @@ __write_to_windows_console([[maybe_unused]] FILE* __stream, [[maybe_unused]] wst
 }
 #  endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
-#elif defined(HAS_FILENO_AND_ISATTY) && _LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 23 // !_LIBCPP_WIN32API
+#elif defined(HAS_FILENO_AND_ISATTY) && _LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 23
 
 _LIBCPP_DIAGNOSTIC_PUSH
 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmissing-prototypes")
 _LIBCPP_EXPORTED_FROM_ABI bool __is_posix_terminal(FILE* __stream) { return isatty(fileno(__stream)); }
 _LIBCPP_DIAGNOSTIC_POP
-#endif
+#endif // _WIN32
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/std_stream.h b/libcxx/src/std_stream.h
index 4b9d3a34b2441..32f23e78d4e69 100644
--- a/libcxx/src/std_stream.h
+++ b/libcxx/src/std_stream.h
@@ -57,7 +57,7 @@ class _LIBCPP_HIDDEN __stdinbuf : public basic_streambuf<_CharT, char_traits<_Ch
   bool __last_consumed_is_next_;
   bool __always_noconv_;
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   static constexpr bool __is_win32api_wide_char = !is_same_v<_CharT, char>;
 #else
   static constexpr bool __is_win32api_wide_char = false;
@@ -262,7 +262,7 @@ class _LIBCPP_HIDDEN __stdoutbuf : public basic_streambuf<_CharT, char_traits<_C
   state_type* __st_;
   bool __always_noconv_;
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   static constexpr bool __is_win32api_wide_char = !is_same_v<_CharT, char>;
 #else
   static constexpr bool __is_win32api_wide_char = false;
diff --git a/libcxx/src/string.cpp b/libcxx/src/string.cpp
index b55f9ff5c9a43..68ac35c228aeb 100644
--- a/libcxx/src/string.cpp
+++ b/libcxx/src/string.cpp
@@ -320,7 +320,7 @@ struct initial_string<wstring> {
 typedef int (*wide_printf)(wchar_t* __restrict, size_t, const wchar_t* __restrict, ...);
 
 inline wide_printf get_swprintf() {
-#  ifndef _LIBCPP_MSVCRT
+#  ifndef _LIBCPP_LIBC_MSVCRT
   return swprintf;
 #  else
   return static_cast<int(__cdecl*)(wchar_t* __restrict, size_t, const wchar_t* __restrict, ...)>(_snwprintf);
diff --git a/libcxx/src/support/win32/locale_win32.cpp b/libcxx/src/support/win32/locale_win32.cpp
index c5117564438a5..701793ae58354 100644
--- a/libcxx/src/support/win32/locale_win32.cpp
+++ b/libcxx/src/support/win32/locale_win32.cpp
@@ -298,7 +298,7 @@ const char* __get_locale_encoding(__locale_t loc) {
 //
 // Strtonum functions
 //
-#if !defined(_LIBCPP_MSVCRT)
+#if !defined(_LIBCPP_LIBC_MSVCRT)
 float __strtof(const char* nptr, char** endptr, __locale_t loc) {
   __locale_guard __current(loc);
   return std::strtof(nptr, endptr);
@@ -324,7 +324,7 @@ size_t __strftime(char* ret, size_t n, const char* format, const struct tm* tm,
 // Other functions
 //
 decltype(MB_CUR_MAX) __mb_len_max(__locale_t __l) {
-#if defined(_LIBCPP_MSVCRT)
+#if defined(_LIBCPP_LIBC_MSVCRT)
   return ::___mb_cur_max_l_func(__l);
 #else
   __locale_guard __current(__l);
@@ -387,7 +387,7 @@ size_t __mbsrtowcs(
 int __snprintf(char* ret, size_t n, __locale_t loc, const char* format, ...) {
   va_list ap;
   va_start(ap, format);
-#if defined(_LIBCPP_MSVCRT)
+#if defined(_LIBCPP_LIBC_MSVCRT)
   // FIXME: Remove usage of internal CRT function and globals.
   int result = ::__stdio_common_vsprintf(
       _CRT_INTERNAL_LOCAL_PRINTF_OPTIONS | _CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR, ret, n, format, loc, ap);
diff --git a/libcxx/src/system_error.cpp b/libcxx/src/system_error.cpp
index cf3d2c2926861..c6a38b5bacba9 100644
--- a/libcxx/src/system_error.cpp
+++ b/libcxx/src/system_error.cpp
@@ -21,7 +21,7 @@
 
 #include "include/config_elast.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  include <windows.h>
 #  include <winerror.h>
 #endif
@@ -29,7 +29,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 namespace {
 std::optional<errc> __win_err_to_errc(int err) {
@@ -149,7 +149,7 @@ constexpr size_t strerror_buff_size = 1024;
 
 string do_strerror_r(int ev);
 
-#  if defined(_LIBCPP_MSVCRT_LIKE)
+#  ifdef _WIN32
 string do_strerror_r(int ev) {
   char buffer[strerror_buff_size];
   if (::strerror_s(buffer, strerror_buff_size, ev) == 0)
@@ -272,7 +272,7 @@ class _LIBCPP_HIDDEN __system_error_category : public __do_message {
 const char* __system_error_category::name() const noexcept { return "system"; }
 
 string __system_error_category::message(int ev) const {
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
   std::string result;
   char* str               = nullptr;
   unsigned long num_chars = ::FormatMessageA(
@@ -304,7 +304,7 @@ string __system_error_category::message(int ev) const {
 }
 
 error_condition __system_error_category::default_error_condition(int ev) const noexcept {
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
   // Remap windows error codes to generic error codes if possible.
   if (ev == 0)
     return error_condition(0, generic_category());
diff --git a/libcxx/src/thread.cpp b/libcxx/src/thread.cpp
index d2aff1ee0c3fe..7b0890858e9d6 100644
--- a/libcxx/src/thread.cpp
+++ b/libcxx/src/thread.cpp
@@ -24,7 +24,7 @@
 #  pragma weak pthread_create // Do not create libpthread dependency
 #endif
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  include <windows.h>
 #endif
 
@@ -74,7 +74,7 @@ unsigned thread::hardware_concurrency() noexcept {
   if (result < 0)
     return 0;
   return static_cast<unsigned>(result);
-#elif defined(_LIBCPP_WIN32API)
+#elif defined(_WIN32)
   return static_cast<unsigned>(GetActiveProcessorCount(ALL_PROCESSOR_GROUPS));
 #else // defined(CTL_HW) && defined(HW_NCPU)
   // TODO: grovel through /proc or check cpuid on x86 and similar
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..0966e7dace413 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -450,7 +450,7 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
 }
 
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
-#    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
+#    ifdef _WIN32
 #      define USE_ALIGNED_ALLOC
 #    endif
 


From libcxx-commits at lists.llvm.org  Sun Jul  5 03:10:02 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 03:10:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Cleanup windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a4a2d7a.af6ba3ed.2c2f30.5078@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions ,cpp,h -- libcxx/include/__config libcxx/include/__filesystem/path.h libcxx/include/__filesystem/u8path.h libcxx/include/__locale libcxx/include/__locale_dir/locale_base_api.h libcxx/include/__locale_dir/support/windows.h libcxx/include/__math/gamma.h libcxx/include/__math/traits.h libcxx/include/__ostream/print.h libcxx/include/fstream libcxx/include/math.h libcxx/include/print libcxx/include/stdlib.h libcxx/include/string.h libcxx/include/text_encoding libcxx/include/wchar.h libcxx/src/chrono.cpp libcxx/src/filesystem/directory_iterator.cpp libcxx/src/filesystem/error.h libcxx/src/filesystem/file_descriptor.h libcxx/src/filesystem/filesystem_clock.cpp libcxx/src/filesystem/format_string.h libcxx/src/filesystem/operations.cpp libcxx/src/filesystem/path.cpp libcxx/src/filesystem/path_parser.h libcxx/src/filesystem/posix_compat.h libcxx/src/filesystem/time_utils.h libcxx/src/fstream.cpp libcxx/src/include/aligned_alloc.h libcxx/src/include/config_elast.h libcxx/src/iostream.cpp libcxx/src/locale.cpp libcxx/src/print.cpp libcxx/src/std_stream.h libcxx/src/string.cpp libcxx/src/support/win32/locale_win32.cpp libcxx/src/system_error.cpp libcxx/src/thread.cpp libcxx/test/support/count_new.h --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index d494b37d7..417a34147 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -69,7 +69,7 @@ size_t strlen(const char* s);
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
 // the right signature.
-#  if defined(__CORRECT_ISO_CPP_STRING_H_PROTO) || defined(_LIBCPP_LIBC_MSVCRT) ||                                          \
+#  if defined(__CORRECT_ISO_CPP_STRING_H_PROTO) || defined(_LIBCPP_LIBC_MSVCRT) ||                                     \
       defined(_STRING_H_CPLUSPLUS_98_CONFORMANCE_)
 #    define _LIBCPP_STRING_H_HAS_CONST_OVERLOADS
 #  endif
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index 1067a2f25..22ad9aa8f 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -5514,7 +5514,7 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
 #  ifdef _WIN32
   __init_pat(__pos_format_, __dummy_curr_symbol, true, lc->p_cs_precedes, lc->p_sep_by_space, lc->p_sign_posn, L' ');
   __init_pat(__neg_format_, __curr_symbol_, true, lc->n_cs_precedes, lc->n_sep_by_space, lc->n_sign_posn, L' ');
-#  else // _WIN32
+#  else  // _WIN32
   __init_pat(
       __pos_format_,
       __dummy_curr_symbol,

``````````

</details>


https://github.com/llvm/llvm-project/pull/207577

From libcxx-commits at lists.llvm.org  Sun Jul  5 03:44:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 03:44:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Fix LWG issue4481
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a358c.ac469dc7.e3aa6.4de4@mx.google.com>


https://github.com/emmett2020 edited https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 03:48:11 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 03:48:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Fix LWG issue4481
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a366b.a87eb5dc.1d432d.cd17@mx.google.com>


https://github.com/emmett2020 edited https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 04:25:44 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 05 Jul 2026 04:25:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::find_first_of (PR #206328)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206328@github.com>
Message-ID: <6a4a3f38.eb165e5c.113c2a.875b@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/206328

>From 80420b031a2664ffaaac079b74864f6f035d0810 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 28 Jun 2026 11:21:56 +0100
Subject: [PATCH 01/12] Added implementation of find_first_of on top of
 __find_if

---
 libcxx/include/__algorithm/pstl.h             | 22 +++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++
 libcxx/include/__pstl/backends/default.h      | 19 ++++
 .../pstl.find_first_of.pass.cpp               | 87 +++++++++++++++++++
 4 files changed, 135 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 10625ea3f8e3d..7d5b20ad96c1d 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -679,6 +679,28 @@ is_sorted(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterato
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a52e6db954d0c..cf64e190ad4ed 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -109,6 +109,13 @@ struct __is_partitioned;
 // optional<bool>
 // operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __find_first_of;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2>
+// optional<_ForwardIterator1>
+// operator()(_Policy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __for_each;
 // template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index be90715af13b5..4fcd9b55e5066 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -12,6 +12,7 @@
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
+#include <__algorithm/find.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
 #include <__config>
@@ -57,6 +58,7 @@ namespace __pstl {
 // - all_of
 // - none_of
 // - is_partitioned
+// - find_first_of
 //
 // for_each family
 // ---------------
@@ -181,6 +183,23 @@ struct __is_partitioned<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2) const noexcept {
+    using _FindIf = __dispatch<__find_if, __current_configuration, _ExecutionPolicy>;
+    using _Ref    = __iterator_reference<_ForwardIterator1>;
+    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref __element) {
+      return std::find(__first2, __last2, __element) != __last2;
+    });
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
new file mode 100644
index 0000000000000..c4093daa71ba7
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator1 find_first_of(ExecutionPolicy&& exec,
+//                                  ForwardIterator1 first1,
+//                                  ForwardIterator1 last1,
+//                                  ForwardIterator2 first2,
+//                                  ForwardIterator2 last2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]     = {0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a)) == Iter(a));
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a)) == Iter(a + sa));
+    }
+    {
+      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
+      unsigned sa = sizeof(ia) / sizeof(ia[0]);
+      int ib[]    = {1, 3, 5, 7};
+      unsigned sb = sizeof(ib) / sizeof(ib[0]);
+      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
+      int ic[] = {7};
+      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
+      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
+      assert(std::find_first_of(Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
+    }
+    {
+      int a[8192];
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      std::iota(a, a + sa, 1);
+      a[1023]     = -1;
+      a[2048]     = -1;
+      a[3071]     = -1;
+      int b[]     = {-1, 999999};
+      unsigned sb = sizeof(b) / sizeof(b[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb)) == Iter(a + 1023));
+    }
+    {
+      int a[1073];
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      std::iota(a, a + sa, 0);
+      int b[]     = {1070, 1071, 1072, -1};
+      unsigned sb = sizeof(b) / sizeof(b[0]);
+      for (unsigned i = 0; i < sa; i = i <= 16 ? i + 1 : unsigned(3.1415 * i)) {
+        a[i] = -1;
+        assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb)) == Iter(a + i));
+        a[i] = i;
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
+                                       types::bidirectional_iterator_list<int*>,
+                                       types::random_access_iterator_list<int*>>{},
+                  TestIteratorWithPolicies<Test>{});
+
+  return 0;
+}

>From c4185be74a100792d2e08987b3991397446a39d1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 28 Jun 2026 12:08:03 +0100
Subject: [PATCH 02/12] Added a predicate version of find_first_of

---
 libcxx/include/__algorithm/pstl.h             | 28 +++++-
 libcxx/include/__pstl/backend_fwd.h           |  4 +-
 libcxx/include/__pstl/backends/default.h      | 14 +--
 .../pstl.find_first_of_pred.pass.cpp          | 95 +++++++++++++++++++
 4 files changed, 132 insertions(+), 9 deletions(-)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 7d5b20ad96c1d..84418222def52 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -698,7 +698,33 @@ template <class _ExecutionPolicy,
       std::move(__first1),
       std::move(__last1),
       std::move(__first2),
-      std::move(__last2));
+      std::move(__last2),
+      std::equal_to<>{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index cf64e190ad4ed..1ea121367fbc7 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -111,10 +111,10 @@ struct __is_partitioned;
 
 template <class _Backend, class _ExecutionPolicy>
 struct __find_first_of;
-// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2>
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
 // optional<_ForwardIterator1>
 // operator()(_Policy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
-//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2) const noexcept;
+//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2, _Predicate __pred) const noexcept;
 
 template <class _Backend, class _ExecutionPolicy>
 struct __for_each;
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 4fcd9b55e5066..6306b051f9dc4 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -9,10 +9,10 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 #define _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 
+#include <__algorithm/any_of.h>
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
-#include <__algorithm/find.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
 #include <__config>
@@ -185,17 +185,19 @@ struct __is_partitioned<__default_backend_tag, _ExecutionPolicy> {
 
 template <class _ExecutionPolicy>
 struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
-  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2>
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
   operator()(_Policy&& __policy,
              _ForwardIterator1 __first1,
              _ForwardIterator1 __last1,
              _ForwardIterator2 __first2,
-             _ForwardIterator2 __last2) const noexcept {
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
     using _FindIf = __dispatch<__find_if, __current_configuration, _ExecutionPolicy>;
-    using _Ref    = __iterator_reference<_ForwardIterator1>;
-    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref __element) {
-      return std::find(__first2, __last2, __element) != __last2;
+    using _Ref1   = __iterator_reference<_ForwardIterator1>;
+    using _Ref2   = __iterator_reference<_ForwardIterator2>;
+    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
+      return std::any_of(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); });
     });
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
new file mode 100644
index 0000000000000..00cb0f149a399
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   ForwardIterator1 find_first_of(ExecutionPolicy&& exec,
+//                                  ForwardIterator1 first1,
+//                                  ForwardIterator1 last1,
+//                                  ForwardIterator2 first2,
+//                                  ForwardIterator2 last2,
+//                                  BinaryPredicate p);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct Pred {
+  bool operator()(int l, int r) const {
+    return l + 1 == r; // ensures that the predicate is not equivalent to std::equal_to
+  }
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]     = {0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a), Pred{}) == Iter(a));
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a), Pred{}) == Iter(a + sa));
+    }
+    {
+      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
+      unsigned sa = sizeof(ia) / sizeof(ia[0]);
+      int ib[]    = {2, 4, 6, 8};
+      unsigned sb = sizeof(ib) / sizeof(ib[0]);
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb), Pred{}) == Iter(ia + 1));
+      int ic[] = {7};
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1), Pred{}) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic), Pred{}) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1), Pred{}) == Iter(ia));
+    }
+    {
+      int a[8192];
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      std::iota(a, a + sa, 1);
+      a[1023]     = -2;
+      a[2048]     = -2;
+      a[3071]     = -2;
+      int b[]     = {-1, 999999};
+      unsigned sb = sizeof(b) / sizeof(b[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb), Pred{}) == Iter(a + 1023));
+    }
+    {
+      int a[1073];
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      std::iota(a, a + sa, 0);
+      int b[]     = {1070, 1071, 1072, -1};
+      unsigned sb = sizeof(b) / sizeof(b[0]);
+      for (unsigned i = 0; i < sa; i = i <= 16 ? i + 1 : unsigned(3.1415 * i)) {
+        a[i] = -2;
+        assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb), Pred{}) == Iter(a + i));
+        a[i] = i;
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
+                                       types::bidirectional_iterator_list<int*>,
+                                       types::random_access_iterator_list<int*>>{},
+                  TestIteratorWithPolicies<Test>{});
+
+  return 0;
+}

>From 92e67cdebae5293ae478ecc9d385585e1f9b8d55 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 28 Jun 2026 12:30:10 +0100
Subject: [PATCH 03/12] Added verification for iterator types, nodiscard and
 noexcept

---
 .../pstl.iterator-requirements.verify.cpp         |  7 +++++++
 .../libcxx/algorithms/pstl.nodiscard.verify.cpp   |  4 ++++
 .../algorithms/pstl.exception_handling.pass.cpp   | 15 +++++++++++++++
 3 files changed, 26 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e5bd7e764c59b..1abaf5cc7b193 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -89,6 +89,13 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::find_if_not(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_if_not}}
   }
 
+  {
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_first_of}}
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
+  }
+
   {
     (void)std::for_each(pol, non_fwd, non_fwd, func); // expected-error@*:* {{static assertion failed: for_each}}
     (void)std::for_each_n(pol, non_fwd, n, func);     // expected-error@*:* {{static assertion failed: for_each_n}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index b6554cef6f32a..747d62bc66837 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -47,6 +47,10 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::find(std::execution::par, std::begin(a), std::end(a), 1);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::find_first_of(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::find_first_of(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_partitioned(std::execution::par, std::begin(a), std::end(a), pred);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a3db159289393..9e6c8f5b8ce40 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -175,6 +175,21 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; });
+
+        // find_first_of(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::find_first_of(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // find_first_of(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::find_first_of(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), pred);
+        });
+      }
+
       {
         auto func = maybe_throw(tokens[5], [](int) {});
 

>From 65aa1ad5d9dd25a930e0cca5ba0c1d59fb3e8aa4 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 28 Jun 2026 13:09:11 +0100
Subject: [PATCH 04/12] Added a missing policy in the unit test

---
 .../alg.find.first.of/pstl.find_first_of.pass.cpp         | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
index c4093daa71ba7..5768a8cf1f3c0 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -45,11 +45,11 @@ struct Test {
       unsigned sa = sizeof(ia) / sizeof(ia[0]);
       int ib[]    = {1, 3, 5, 7};
       unsigned sb = sizeof(ib) / sizeof(ib[0]);
-      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
       int ic[] = {7};
-      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
-      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
-      assert(std::find_first_of(Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
     }
     {
       int a[8192];

>From 18881aea45556d321505c64ef3ebcbab78c331ec Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 30 Jun 2026 22:10:58 +0100
Subject: [PATCH 05/12] Addressed the feedback

---
 libcxx/include/__algorithm/pstl.h             | 96 +++++++++----------
 .../pstl.iterator-requirements.verify.cpp     |  8 +-
 .../pstl.find_first_of.pass.cpp               | 89 ++++++++++-------
 .../pstl.find_first_of_pred.pass.cpp          | 89 ++++++++++-------
 4 files changed, 168 insertions(+), 114 deletions(-)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 84418222def52..3530b3207f59e 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -293,6 +293,54 @@ find(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __l
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::equal_to<>{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator,
           class _Function,
@@ -679,54 +727,6 @@ is_sorted(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterato
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
 }
 
-template <class _ExecutionPolicy,
-          class _ForwardIterator1,
-          class _ForwardIterator2,
-          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
-          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
-    _ExecutionPolicy&& __policy,
-    _ForwardIterator1 __first1,
-    _ForwardIterator1 __last1,
-    _ForwardIterator2 __first2,
-    _ForwardIterator2 __last2) {
-  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
-  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
-  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
-  return __pstl::__handle_exception<_Implementation>(
-      std::forward<_ExecutionPolicy>(__policy),
-      std::move(__first1),
-      std::move(__last1),
-      std::move(__first2),
-      std::move(__last2),
-      std::equal_to<>{});
-}
-
-template <class _ExecutionPolicy,
-          class _ForwardIterator1,
-          class _ForwardIterator2,
-          class _BinaryPredicate,
-          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
-          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
-    _ExecutionPolicy&& __policy,
-    _ForwardIterator1 __first1,
-    _ForwardIterator1 __last1,
-    _ForwardIterator2 __first2,
-    _ForwardIterator2 __last2,
-    _BinaryPredicate __pred) {
-  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
-  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
-  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
-  return __pstl::__handle_exception<_Implementation>(
-      std::forward<_ExecutionPolicy>(__policy),
-      std::move(__first1),
-      std::move(__last1),
-      std::move(__first2),
-      std::move(__last2),
-      std::move(__pred));
-}
-
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 1abaf5cc7b193..aee3f97019071 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -91,9 +91,13 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
 
   {
     (void)std::find_first_of(
-        pol, non_fwd, non_fwd, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_first_of}}
+        pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_first_of}}
     (void)std::find_first_of(
-        pol, non_fwd, non_fwd, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: find_first_of}}
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
   }
 
   {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
index 5768a8cf1f3c0..ea2fdc6eebdef 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -30,58 +30,83 @@
 #include "test_macros.h"
 #include "type_algorithms.h"
 
-template <class Iter>
+EXECUTION_POLICY_SFINAE_TEST(find_first_of);
+
+static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable&& callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    using std::begin;
+    using std::end;
     {
-      int a[]     = {0};
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a)) == Iter(a));
-      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a)) == Iter(a + sa));
+      int a[] = {0};
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(begin(a)), Iter2(begin(a)), Iter2(begin(a))) ==
+             Iter1(begin(a)));
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(a)), Iter2(begin(a))) ==
+             Iter1(end(a)));
     }
     {
-      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
-      unsigned sa = sizeof(ia) / sizeof(ia[0]);
-      int ib[]    = {1, 3, 5, 7};
-      unsigned sb = sizeof(ib) / sizeof(ib[0]);
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
+      int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int ib[] = {1, 3, 5, 7};
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ib)), Iter2(end(ib))) ==
+             Iter1(begin(ia) + 1));
       int ic[] = {7};
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(end(ic))) ==
+             Iter1(end(ia)));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(begin(ic))) ==
+             Iter1(end(ia)));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(begin(ia)), Iter2(begin(ic)), Iter2(end(ic))) ==
+             Iter1(begin(ia)));
     }
     {
       int a[8192];
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      std::iota(a, a + sa, 1);
-      a[1023]     = -1;
-      a[2048]     = -1;
-      a[3071]     = -1;
-      int b[]     = {-1, 999999};
-      unsigned sb = sizeof(b) / sizeof(b[0]);
-      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb)) == Iter(a + 1023));
+      std::iota(begin(a), end(a), 1);
+      a[1023] = -1;
+      a[2048] = -1;
+      a[3071] = -1;
+      int b[] = {-1, 999999};
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b))) ==
+             Iter1(begin(a) + 1023));
     }
     {
       int a[1073];
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      std::iota(a, a + sa, 0);
-      int b[]     = {1070, 1071, 1072, -1};
-      unsigned sb = sizeof(b) / sizeof(b[0]);
-      for (unsigned i = 0; i < sa; i = i <= 16 ? i + 1 : unsigned(3.1415 * i)) {
+      std::iota(begin(a), end(a), 0);
+      int b[] = {1073, 1074, 1075, -1};
+      runway_sample(std::size(a), [&](size_t i) {
         a[i] = -1;
-        assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb)) == Iter(a + i));
+        assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b))) ==
+               Iter1(begin(a) + i));
         a[i] = i;
-      }
+      });
     }
   }
 };
 
 int main(int, char**) {
-  types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
-                                       types::bidirectional_iterator_list<int*>,
-                                       types::random_access_iterator_list<int*>>{},
-                  TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies< types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
 
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
index 00cb0f149a399..770d0109c4f4a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -32,64 +32,89 @@
 #include "test_macros.h"
 #include "type_algorithms.h"
 
+EXECUTION_POLICY_SFINAE_TEST(find_first_of);
+
+static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
 struct Pred {
   bool operator()(int l, int r) const {
     return l + 1 == r; // ensures that the predicate is not equivalent to std::equal_to
   }
 };
 
-template <class Iter>
+template <class Callable>
+void runway_sample(size_t size, Callable&& callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    using std::begin;
+    using std::end;
     {
-      int a[]     = {0};
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a), Pred{}) == Iter(a));
-      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a), Pred{}) == Iter(a + sa));
+      int a[] = {0};
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(begin(a)), Iter2(begin(a)), Iter2(begin(a)), Pred{}) ==
+             Iter1(begin(a)));
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(a)), Iter2(begin(a)), Pred{}) ==
+             Iter1(end(a)));
     }
     {
-      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
-      unsigned sa = sizeof(ia) / sizeof(ia[0]);
-      int ib[]    = {2, 4, 6, 8};
-      unsigned sb = sizeof(ib) / sizeof(ib[0]);
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb), Pred{}) == Iter(ia + 1));
+      int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int ib[] = {2, 4, 6, 8};
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ib)), Iter2(end(ib)), Pred{}) ==
+             Iter1(begin(ia) + 1));
       int ic[] = {7};
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1), Pred{}) == Iter(ia + sa));
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic), Pred{}) == Iter(ia + sa));
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1), Pred{}) == Iter(ia));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(end(ic)), Pred{}) ==
+             Iter1(end(ia)));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(begin(ic)), Pred{}) ==
+             Iter1(end(ia)));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(begin(ia)), Iter2(begin(ic)), Iter2(end(ic)), Pred{}) ==
+             Iter1(begin(ia)));
     }
     {
       int a[8192];
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      std::iota(a, a + sa, 1);
-      a[1023]     = -2;
-      a[2048]     = -2;
-      a[3071]     = -2;
-      int b[]     = {-1, 999999};
-      unsigned sb = sizeof(b) / sizeof(b[0]);
-      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb), Pred{}) == Iter(a + 1023));
+      std::iota(begin(a), end(a), 1);
+      a[1023] = -2;
+      a[2048] = -2;
+      a[3071] = -2;
+      int b[] = {-1, 999999};
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b)), Pred{}) ==
+             Iter1(begin(a) + 1023));
     }
     {
       int a[1073];
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      std::iota(a, a + sa, 0);
-      int b[]     = {1070, 1071, 1072, -1};
-      unsigned sb = sizeof(b) / sizeof(b[0]);
-      for (unsigned i = 0; i < sa; i = i <= 16 ? i + 1 : unsigned(3.1415 * i)) {
+      std::iota(begin(a), end(a), 0);
+      int b[] = {1074, 1075, 1076, -1};
+      runway_sample(std::size(a), [&](size_t i) {
         a[i] = -2;
-        assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb), Pred{}) == Iter(a + i));
+        assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b)), Pred{}) ==
+               Iter1(begin(a) + i));
         a[i] = i;
-      }
+      });
     }
   }
 };
 
 int main(int, char**) {
-  types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
-                                       types::bidirectional_iterator_list<int*>,
-                                       types::random_access_iterator_list<int*>>{},
-                  TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies< types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
 
   return 0;
 }

>From fa3700e14b9c5f311df716d57f018a94a3438b2e Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 30 Jun 2026 22:42:34 +0100
Subject: [PATCH 06/12] Missing include

---
 .../alg.find.first.of/pstl.find_first_of.pass.cpp                | 1 +
 .../alg.find.first.of/pstl.find_first_of_pred.pass.cpp           | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
index ea2fdc6eebdef..0ad0bbfdd700a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -22,6 +22,7 @@
 #include <algorithm>
 #include <cassert>
 #include <functional>
+#include <iterator>
 #include <limits>
 #include <numeric>
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
index 770d0109c4f4a..5532ed2f8e075 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -24,6 +24,7 @@
 #include <algorithm>
 #include <cassert>
 #include <functional>
+#include <iterator>
 #include <limits>
 #include <numeric>
 

>From 8dd415ea0a27cb125486a9b32f4350d5b45c18f1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 1 Jul 2026 21:21:11 +0100
Subject: [PATCH 07/12] Branch off to std::find() if the predicate is equality,
 call into more general std::find_if() otherwise.

---
 libcxx/include/__pstl/backends/default.h      | 12 +++-
 .../pstl.find_first_of.pass.cpp               | 48 ++++++++-------
 .../pstl.find_first_of_pred.pass.cpp          | 61 ++++++++++++-------
 3 files changed, 77 insertions(+), 44 deletions(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 6306b051f9dc4..a5cd1d1b09912 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -9,10 +9,11 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 #define _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 
-#include <__algorithm/any_of.h>
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
+#include <__algorithm/find.h>
+#include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
 #include <__config>
@@ -24,6 +25,8 @@
 #include <__iterator/next.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
+#include <__type_traits/desugars_to.h>
+#include <__type_traits/remove_cvref.h>
 #include <__utility/empty.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -197,7 +200,12 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
     using _Ref1   = __iterator_reference<_ForwardIterator1>;
     using _Ref2   = __iterator_reference<_ForwardIterator2>;
     return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
-      return std::any_of(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); });
+      if constexpr (__desugars_to_v<__equal_tag, _Predicate, __remove_cvref_t<_Ref1>, __remove_cvref_t<_Ref2>>) {
+        // bypass an equality predicate and call directly to std::find() to allow more vectorization
+        return std::find(__first2, __last2, __element) != __last2;
+      } else {
+        return std::find_if(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); }) != __last2;
+      }
     });
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
index 0ad0bbfdd700a..118b34da0cb6c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -37,7 +37,7 @@ static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*>);
 
 template <class Callable>
-void runway_sample(size_t size, Callable&& callable) {
+void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
   // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
   for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
@@ -55,46 +55,52 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
-    using std::begin;
-    using std::end;
     {
       int a[] = {0};
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(begin(a)), Iter2(begin(a)), Iter2(begin(a))) ==
-             Iter1(begin(a)));
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(a)), Iter2(begin(a))) ==
-             Iter1(end(a)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)), Iter2(std::begin(a))) ==
+             Iter1(std::begin(a)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), Iter2(std::begin(a))) ==
+             Iter1(std::end(a)));
     }
     {
       int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
       int ib[] = {1, 3, 5, 7};
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ib)), Iter2(end(ib))) ==
-             Iter1(begin(ia) + 1));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Iter2(std::end(ib))) ==
+             Iter1(std::begin(ia) + 1));
       int ic[] = {7};
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(end(ic))) ==
-             Iter1(end(ia)));
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(begin(ic))) ==
-             Iter1(end(ia)));
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(begin(ia)), Iter2(begin(ic)), Iter2(end(ic))) ==
-             Iter1(begin(ia)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ic)), Iter2(std::end(ic))) ==
+             Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ic)), Iter2(std::begin(ic))) ==
+             Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ic)), Iter2(std::end(ic))) ==
+             Iter1(std::begin(ia)));
     }
     {
       int a[8192];
-      std::iota(begin(a), end(a), 1);
+      std::iota(std::begin(a), std::end(a), 1);
       a[1023] = -1;
       a[2048] = -1;
       a[3071] = -1;
       int b[] = {-1, 999999};
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b))) ==
-             Iter1(begin(a) + 1023));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))) ==
+             Iter1(std::begin(a) + 1023));
     }
     {
       int a[1073];
-      std::iota(begin(a), end(a), 0);
+      std::iota(std::begin(a), std::end(a), 0);
       int b[] = {1073, 1074, 1075, -1};
       runway_sample(std::size(a), [&](size_t i) {
         a[i] = -1;
-        assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b))) ==
-               Iter1(begin(a) + i));
+        assert(std::find_first_of(
+                   policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))) ==
+               Iter1(std::begin(a) + i));
         a[i] = i;
       });
     }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
index 5532ed2f8e075..1033b52e4621f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -45,7 +45,7 @@ struct Pred {
 };
 
 template <class Callable>
-void runway_sample(size_t size, Callable&& callable) {
+void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
   // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
   for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
@@ -63,46 +63,65 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
-    using std::begin;
-    using std::end;
     {
       int a[] = {0};
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(begin(a)), Iter2(begin(a)), Iter2(begin(a)), Pred{}) ==
-             Iter1(begin(a)));
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(a)), Iter2(begin(a)), Pred{}) ==
-             Iter1(end(a)));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)), Iter2(std::begin(a)), Pred{}) ==
+          Iter1(std::begin(a)));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), Iter2(std::begin(a)), Pred{}) ==
+          Iter1(std::end(a)));
     }
     {
       int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
       int ib[] = {2, 4, 6, 8};
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ib)), Iter2(end(ib)), Pred{}) ==
-             Iter1(begin(ia) + 1));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Iter2(std::end(ib)), Pred{}) ==
+          Iter1(std::begin(ia) + 1));
       int ic[] = {7};
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(end(ic)), Pred{}) ==
-             Iter1(end(ia)));
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(begin(ic)), Pred{}) ==
-             Iter1(end(ia)));
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(begin(ia)), Iter2(begin(ic)), Iter2(end(ic)), Pred{}) ==
-             Iter1(begin(ia)));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ic)), Iter2(std::end(ic)), Pred{}) ==
+          Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy,
+                 Iter1(std::begin(ia)),
+                 Iter1(std::end(ia)),
+                 Iter2(std::begin(ic)),
+                 Iter2(std::begin(ic)),
+                 Pred{}) == Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy,
+                 Iter1(std::begin(ia)),
+                 Iter1(std::begin(ia)),
+                 Iter2(std::begin(ic)),
+                 Iter2(std::end(ic)),
+                 Pred{}) == Iter1(std::begin(ia)));
     }
     {
       int a[8192];
-      std::iota(begin(a), end(a), 1);
+      std::iota(std::begin(a), std::end(a), 1);
       a[1023] = -2;
       a[2048] = -2;
       a[3071] = -2;
       int b[] = {-1, 999999};
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b)), Pred{}) ==
-             Iter1(begin(a) + 1023));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{}) ==
+             Iter1(std::begin(a) + 1023));
     }
     {
       int a[1073];
-      std::iota(begin(a), end(a), 0);
+      std::iota(std::begin(a), std::end(a), 0);
       int b[] = {1074, 1075, 1076, -1};
       runway_sample(std::size(a), [&](size_t i) {
         a[i] = -2;
-        assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b)), Pred{}) ==
-               Iter1(begin(a) + i));
+        assert(
+            std::find_first_of(
+                policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{}) ==
+            Iter1(std::begin(a) + i));
         a[i] = i;
       });
     }

>From f850d3d6b613a8ed59102a4fb3add7863766412f Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 1 Jul 2026 22:55:54 +0100
Subject: [PATCH 08/12] <numeric> pulls in more headers via
 <__algorithm/find.h>

---
 libcxx/test/libcxx/transitive_includes/cxx23.csv | 3 +++
 libcxx/test/libcxx/transitive_includes/cxx26.csv | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 8385b37c07822..53ae6c9b4e591 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -704,11 +704,14 @@ mutex typeinfo
 mutex version
 new version
 numbers version
+numeric cctype
 numeric climits
 numeric compare
 numeric cstdint
 numeric cstring
 numeric ctime
+numeric cwchar
+numeric cwctype
 numeric initializer_list
 numeric limits
 numeric optional
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index cf1154a251f23..849c9b0ce932b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -664,11 +664,14 @@ mutex tuple
 mutex version
 new version
 numbers version
+numeric cctype
 numeric climits
 numeric compare
 numeric cstdint
 numeric cstring
 numeric ctime
+numeric cwchar
+numeric cwctype
 numeric initializer_list
 numeric limits
 numeric optional

>From 645820c13db62c99a27d253902acd441488156e0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 1 Jul 2026 23:09:04 +0100
Subject: [PATCH 09/12] Expanded tests cases of the serial versions a bit,
 supporting down to C++03

---
 .../alg.find.first.of/find_first_of.pass.cpp  |  60 ++++++----
 .../find_first_of_pred.pass.cpp               | 104 +++++++++++-------
 2 files changed, 105 insertions(+), 59 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
index efa54bbc8fd66..4740029d4c22b 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
@@ -16,18 +16,19 @@
 #include <algorithm>
 #include <cassert>
 #include <iterator>
+#include <numeric>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
 #if TEST_STD_VER > 17
 TEST_CONSTEXPR bool test_constexpr() {
-    int ia[] = {1, 2, 3};
-    int ib[] = {7, 8, 9};
-    int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
+  int ia[] = {1, 2, 3};
+  int ib[] = {7, 8, 9};
+  int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
     typedef forward_iterator<int*>       FI;
-    typedef bidirectional_iterator<int*> BI;
-    typedef random_access_iterator<int*> RI;
+  typedef bidirectional_iterator<int*> BI;
+  typedef random_access_iterator<int*> RI;
 
     return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia))) == FI(ic+1))
            && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib))) == FI(std::end(ic)))
@@ -36,39 +37,56 @@ TEST_CONSTEXPR bool test_constexpr() {
            && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia))) == RI(ic+1))
            && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib))) == RI(std::end(ic)))
            ;
-    }
+}
 #endif
 
-int main(int, char**)
-{
-    int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
-    const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-    int ib[] = {1, 3, 5, 7};
-    const unsigned sb = sizeof(ib)/sizeof(ib[0]);
+int main(int, char**) {
+  {
+    int ia[]          = {0, 1, 2, 3, 0, 1, 2, 3};
+    const unsigned sa = sizeof(ia) / sizeof(ia[0]);
+    int ib[]          = {1, 3, 5, 7};
+    const unsigned sb = sizeof(ib) / sizeof(ib[0]);
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia + sa),
                               forward_iterator<const int*>(ib),
-                              forward_iterator<const int*>(ib + sb)) ==
-                              cpp17_input_iterator<const int*>(ia+1));
+                              forward_iterator<const int*>(ib + sb)) == cpp17_input_iterator<const int*>(ia + 1));
     int ic[] = {7};
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia + sa),
                               forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic + 1)) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
+                              forward_iterator<const int*>(ic + 1)) == cpp17_input_iterator<const int*>(ia + sa));
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia + sa),
                               forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic)) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
+                              forward_iterator<const int*>(ic)) == cpp17_input_iterator<const int*>(ia + sa));
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia),
                               forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic+1)) ==
-                              cpp17_input_iterator<const int*>(ia));
+                              forward_iterator<const int*>(ic + 1)) == cpp17_input_iterator<const int*>(ia));
+  }
+  {
+    int a[8192];
+    std::iota(std::begin(a), std::end(a), 1);
+    a[1023] = -1;
+    a[2048] = -1;
+    a[3071] = -1;
+    int b[] = {-1, 999999};
+    assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b)) == std::begin(a) + 1023);
+  }
+  {
+    int a[1073];
+    const unsigned sa = sizeof(a) / sizeof(a[0]);
+    std::iota(std::begin(a), std::end(a), 0);
+    int b[] = {1073, 1074, 1075, -1};
+    for (unsigned i = 0; i < sa; i = i < 16 ? i + 1 : unsigned(3.1415 * i)) {
+      a[i] = -1;
+      assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b)) == std::begin(a) + i);
+      a[i] = i;
+    }
+  }
 
 #if TEST_STD_VER > 17
-    static_assert(test_constexpr());
+  static_assert(test_constexpr());
 #endif
 
   return 0;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
index fd2e4d426fbf9..25699f4b5c216 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
@@ -18,19 +18,26 @@
 #include <cassert>
 #include <functional>
 #include <iterator>
+#include <numeric>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
+struct Pred {
+  bool operator()(int l, int r) const {
+    return l + 1 == r; // ensures that the predicate is not equivalent to std::equal_to
+  }
+};
+
 #if TEST_STD_VER > 17
 constexpr bool test_constexpr() {
-    int ia[] = {1, 2, 3};
-    int ib[] = {7, 8, 9};
-    int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
+  int ia[] = {1, 2, 3};
+  int ib[] = {7, 8, 9};
+  int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
     typedef forward_iterator<int*>       FI;
-    typedef bidirectional_iterator<int*> BI;
-    typedef random_access_iterator<int*> RI;
-    std::equal_to<int> eq{};
+  typedef bidirectional_iterator<int*> BI;
+  typedef random_access_iterator<int*> RI;
+  std::equal_to<int> eq{};
     return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia)), eq) == FI(ic+1))
            && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib)), eq) == FI(std::end(ic)))
            && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia)), eq) == BI(ic+1))
@@ -38,43 +45,64 @@ constexpr bool test_constexpr() {
            && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia)), eq) == RI(ic+1))
            && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib)), eq) == RI(std::end(ic)))
            ;
-    }
+}
 #endif
 
-int main(int, char**)
-{
-    int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
-    const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-    int ib[] = {1, 3, 5, 7};
-    const unsigned sb = sizeof(ib)/sizeof(ib[0]);
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia + sa),
-                              forward_iterator<const int*>(ib),
-                              forward_iterator<const int*>(ib + sb),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia+1));
+int main(int, char**) {
+  {
+    int ia[]          = {0, 1, 2, 3, 0, 1, 2, 3};
+    const unsigned sa = sizeof(ia) / sizeof(ia[0]);
+    int ib[]          = {1, 3, 5, 7};
+    const unsigned sb = sizeof(ib) / sizeof(ib[0]);
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia + sa),
+               forward_iterator<const int*>(ib),
+               forward_iterator<const int*>(ib + sb),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia + 1));
     int ic[] = {7};
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia + sa),
-                              forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic + 1),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia + sa),
-                              forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia),
-                              forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic+1),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia));
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia + sa),
+               forward_iterator<const int*>(ic),
+               forward_iterator<const int*>(ic + 1),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia + sa));
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia + sa),
+               forward_iterator<const int*>(ic),
+               forward_iterator<const int*>(ic),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia + sa));
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia),
+               forward_iterator<const int*>(ic),
+               forward_iterator<const int*>(ic + 1),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia));
+  }
+  {
+    int a[8192];
+    std::iota(std::begin(a), std::end(a), 1);
+    a[1023] = -2;
+    a[2048] = -2;
+    a[3071] = -2;
+    int b[] = {-1, 999999};
+    assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b), Pred()) == std::begin(a) + 1023);
+  }
+  {
+    int a[1073];
+    const unsigned sa = sizeof(a) / sizeof(a[0]);
+    std::iota(std::begin(a), std::end(a), 0);
+    int b[] = {1074, 1075, 1076, -1};
+    for (unsigned i = 0; i < sa; i = i < 16 ? i + 1 : unsigned(3.1415 * i)) {
+      a[i] = -2;
+      assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b), Pred()) == std::begin(a) + i);
+      a[i] = i;
+    };
+  }
 
 #if TEST_STD_VER > 17
-    static_assert(test_constexpr());
+  static_assert(test_constexpr());
 #endif
 
   return 0;

>From 12ba4c956bfba72044d2423496800660da679a0f Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 1 Jul 2026 23:12:37 +0100
Subject: [PATCH 10/12] clang-format the serial tests fully

---
 .../alg.find.first.of/find_first_of.pass.cpp  | 21 ++++++++++++-------
 .../find_first_of_pred.pass.cpp               | 21 ++++++++++++-------
 2 files changed, 26 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
index 4740029d4c22b..25a223e274706 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
@@ -26,17 +26,22 @@ TEST_CONSTEXPR bool test_constexpr() {
   int ia[] = {1, 2, 3};
   int ib[] = {7, 8, 9};
   int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
-    typedef forward_iterator<int*>       FI;
+  typedef forward_iterator<int*> FI;
   typedef bidirectional_iterator<int*> BI;
   typedef random_access_iterator<int*> RI;
 
-    return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia))) == FI(ic+1))
-           && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib))) == FI(std::end(ic)))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia))) == BI(ic+1))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib))) == BI(std::end(ic)))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia))) == RI(ic+1))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib))) == RI(std::end(ic)))
-           ;
+  return (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia))) ==
+          FI(ic + 1)) &&
+         (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib))) ==
+          FI(std::end(ic))) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia))) ==
+          BI(ic + 1)) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib))) ==
+          BI(std::end(ic))) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia))) ==
+          RI(ic + 1)) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib))) ==
+          RI(std::end(ic)));
 }
 #endif
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
index 25699f4b5c216..9fdc4081b0141 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
@@ -34,17 +34,22 @@ constexpr bool test_constexpr() {
   int ia[] = {1, 2, 3};
   int ib[] = {7, 8, 9};
   int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
-    typedef forward_iterator<int*>       FI;
+  typedef forward_iterator<int*> FI;
   typedef bidirectional_iterator<int*> BI;
   typedef random_access_iterator<int*> RI;
   std::equal_to<int> eq{};
-    return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia)), eq) == FI(ic+1))
-           && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib)), eq) == FI(std::end(ic)))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia)), eq) == BI(ic+1))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib)), eq) == BI(std::end(ic)))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia)), eq) == RI(ic+1))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib)), eq) == RI(std::end(ic)))
-           ;
+  return (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia)), eq) ==
+          FI(ic + 1)) &&
+         (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib)), eq) ==
+          FI(std::end(ic))) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia)), eq) ==
+          BI(ic + 1)) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib)), eq) ==
+          BI(std::end(ic))) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia)), eq) ==
+          RI(ic + 1)) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib)), eq) ==
+          RI(std::end(ic)));
 }
 #endif
 

>From 12be042391977e8ba12419b3371e5650d90b2a9e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 16:15:40 -0400
Subject: [PATCH 11/12] Update libcxx/include/__pstl/backends/default.h

---
 libcxx/include/__pstl/backends/default.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index a5cd1d1b09912..86f3899dc8ab1 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -200,7 +200,7 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
     using _Ref1   = __iterator_reference<_ForwardIterator1>;
     using _Ref2   = __iterator_reference<_ForwardIterator2>;
     return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
-      if constexpr (__desugars_to_v<__equal_tag, _Predicate, __remove_cvref_t<_Ref1>, __remove_cvref_t<_Ref2>>) {
+      if constexpr (__desugars_to_v<__equal_tag, _Predicate, _Ref1, _Ref2>) {
         // bypass an equality predicate and call directly to std::find() to allow more vectorization
         return std::find(__first2, __last2, __element) != __last2;
       } else {

>From de6f3e6097065c937b4b2aa6f8efc417e11d6a8d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 16:47:28 -0400
Subject: [PATCH 12/12] Update libcxx/include/__pstl/backends/default.h

---
 libcxx/include/__pstl/backends/default.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 86f3899dc8ab1..cb963a130f743 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -26,7 +26,6 @@
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
 #include <__type_traits/desugars_to.h>
-#include <__type_traits/remove_cvref.h>
 #include <__utility/empty.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>


From libcxx-commits at lists.llvm.org  Sun Jul  5 04:44:51 2026
From: libcxx-commits at lists.llvm.org (Rafail Shakhin ogly via libcxx-commits)
Date: Sun, 05 Jul 2026 04:44:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2077R3: Heterogeneous
 erasure overloads for associative containers (PR #174680)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/174680@github.com>
Message-ID: <6a4a43b3.a87eb5dc.1d432d.158c@mx.google.com>


https://github.com/rsaddatimov updated https://github.com/llvm/llvm-project/pull/174680

>From be7b5f514cca12278e8f3ccc96145dbea971d4be Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 05:11:31 +0300
Subject: [PATCH 01/21] Initial commit: start with set & multiset tests

---
 libcxx/include/__tree                         |  16 +--
 libcxx/include/map                            |  10 +-
 libcxx/include/set                            |  64 ++++++++-
 .../multiset/extract.transparent.pass.cpp     |  27 ++++
 .../erase.transparent.pass.cpp                |  27 ++++
 .../set/erase.transparent.pass.cpp            |  27 ++++
 .../set/extract.transparent.pass.cpp          |  27 ++++
 .../support/test_transparent_associative.hpp  | 124 ++++++++++++++++++
 8 files changed, 307 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/support/test_transparent_associative.hpp

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index dafeff3e2e019..fe54b53ac80b9 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1239,10 +1239,10 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   __node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source);
 
+  template <class _NodeHandle, class _Key>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const _Key&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(key_type const&);
-  template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const_iterator);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract_iterator(const_iterator);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p);
@@ -2089,7 +2089,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
   if (__nh.empty())
     return _InsertReturnType{end(), false, _NodeHandle()};
 
-  __node_pointer __ptr = __nh.__ptr_;
+  __node_pointer __ptr     = __nh.__ptr_;
   auto [__parent, __child] = __find_equal(__ptr->__get_value());
   if (__child != nullptr)
     return _InsertReturnType{
@@ -2120,19 +2120,19 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-template <class _NodeHandle>
+template <class _NodeHandle, class _Key>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(key_type const& __key) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const& _Key __key) {
   iterator __it = __lower_bound_multi(__key);
   if (__it == end() || __value_comp_(__key, *__it))
     return _NodeHandle();
-  return __node_handle_extract<_NodeHandle>(__it);
+  return __node_handle_extract_iterator<_NodeHandle>(__it);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const_iterator __p) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_iterator(const_iterator __p) {
   __node_pointer __np = __p.__get_np();
   __remove_node_pointer(__np);
   return _NodeHandle(__np, __alloc());
diff --git a/libcxx/include/map b/libcxx/include/map
index 8efe18fa3f839..1dda078c4e6c1 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1037,8 +1037,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 #  endif
 
@@ -1353,7 +1353,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
@@ -2022,8 +2022,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it.__i_);
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
diff --git a/libcxx/include/set b/libcxx/include/set
index a8e245b22ae51..6eeec2ee7b6a0 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -125,13 +125,19 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
     iterator  erase(const_iterator position);
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class C2>
@@ -370,6 +376,9 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     iterator insert(node_type&& nh);                                                  // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
@@ -377,6 +386,9 @@ public:
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
     iterator  erase(const_iterator first, const_iterator last);
+    template <typename K>
+      size_type erase(K&& k); // C++23
+
     void clear() noexcept;
 
     template<class C2>
@@ -816,6 +828,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
     return __tree_.erase(__f, __l);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -833,8 +857,20 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
   }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1329,6 +1365,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
   _LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_multi(__k); }
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1346,8 +1394,20 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
diff --git a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..fc04c8d85c267
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class multiset
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
+
+  test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
+
+  test_non_transparent_extract<std::multiset<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..a7252f0aa039c
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class multiset
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
+
+  test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
+
+  test_non_transparent_erase<std::multiset<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..d2ca1fe599374
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class set
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
+
+  test_transparent_erase<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
+
+  test_non_transparent_erase<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..62c72808b83d1
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class set
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
+
+  test_transparent_extract<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
+
+  test_non_transparent_extract<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
new file mode 100644
index 0000000000000..a59f10e426ecc
--- /dev/null
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_TRANSPARENT_ASSOCIATIVE_H
+#define TEST_TRANSPARENT_ASSOCIATIVE_H
+
+#include "test_macros.h"
+
+#include <cassert>
+
+#if TEST_STD_VER > 23
+
+template <typename T>
+struct StoredType;
+
+template <typename T>
+struct SearchedType {
+  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+
+  operator StoredType<T>() const {
+    ++*conversions_;
+    return StoredType<T>{value_};
+  }
+
+  T get_value() const { return value_; }
+
+  auto operator<=>(const SearchedType<T>&) const = default;
+
+private:
+  T value_;
+  int* conversions_;
+};
+
+template <typename T>
+struct StoredType {
+  StoredType() = default;
+  StoredType(T value) : value_(value) {}
+
+  T get_value() const { return value_; }
+
+  auto operator<=>(const StoredType<T>&) const = default;
+
+private:
+  T value_;
+};
+
+struct transparent_comparator_base {
+  using is_transparent = void;
+
+  template <typename T>
+  bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
+    return lhs.get_value() < rhs.get_value();
+  }
+
+  template <typename T>
+  bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
+    return lhs.get_value() < rhs.get_value();
+  }
+
+  template <typename T>
+  bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
+    return lhs < rhs;
+  }
+};
+
+struct transparent_comparator_final final : public transparent_comparator_base {};
+
+template <class Container>
+void test_transparent_erase(Container c) {
+  int conversions = 0;
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+
+  assert(conversions == 0);
+
+  c.erase(c.begin());
+  c.erase(c.cbegin());
+
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_erase(Container c) {
+  int conversions = 0;
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(conversions == 1);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(conversions == 2);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+  assert(conversions == 3);
+}
+
+template <class Container>
+void test_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 0);
+
+  assert(!c.extract(c.cbegin()).empty());
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(conversions == 1);
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(conversions == 2);
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 3);
+}
+
+#endif // TEST_STD_VER > 23
+
+#endif // TEST_TRANSPARENT_ASSOCIATIVE_H

>From a677aa74846301171d0f9bc06f68700dcb76ad4e Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 06:22:38 +0300
Subject: [PATCH 02/21] Fix test & version stuff

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/ReleaseNotes/22.rst               |  1 +
 libcxx/docs/Status/Cxx23Papers.csv            |  2 +-
 libcxx/include/version                        |  2 +-
 .../map.version.compile.pass.cpp              | 32 ++++++-------------
 .../set.version.compile.pass.cpp              | 32 ++++++-------------
 .../unordered_map.version.compile.pass.cpp    | 32 ++++++-------------
 .../unordered_set.version.compile.pass.cpp    | 32 ++++++-------------
 .../version.version.compile.pass.cpp          | 32 ++++++-------------
 .../support/test_transparent_associative.hpp  |  4 +--
 .../generate_feature_test_macro_components.py |  1 -
 11 files changed, 56 insertions(+), 116 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..644d898772749 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -314,7 +314,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_allocate_at_least``                            ``202302L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_associative_heterogeneous_erasure``            *unimplemented*
+    ``__cpp_lib_associative_heterogeneous_erasure``            ``202110L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_bind_back``                                    ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 9f9927095a61f..5b05666cbb419 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 22.0.0?
 Implemented Papers
 ------------------
 
+- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
 - P2592R3: Hashing support for ``std::chrono`` value classes (`Github <https://llvm.org/PR105358>`__)
 - P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__)
 - P2988R12: ``std::optional<T&>`` (`Github <https://llvm.org/PR148131>`__)
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 739f0ecab37e7..d6fa695026543 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -30,7 +30,7 @@
 "`P1147R1 <https://wg21.link/P1147R1>`__","Printing ``volatile`` Pointers","2021-10 (Virtual)","|Complete|","14","`#105161 <https://github.com/llvm/llvm-project/issues/105161>`__",""
 "`P1272R4 <https://wg21.link/P1272R4>`__","Byteswapping for fun&&nuf","2021-10 (Virtual)","|Complete|","14","`#105163 <https://github.com/llvm/llvm-project/issues/105163>`__",""
 "`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","","`#105164 <https://github.com/llvm/llvm-project/issues/105164>`__",""
-"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","","","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
+"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","22","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
 "`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14","`#105166 <https://github.com/llvm/llvm-project/issues/105166>`__",""
 "`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
 "`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|Complete|","22","`#105169 <https://github.com/llvm/llvm-project/issues/105169>`__",""
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..01faecd6e48b3 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -493,7 +493,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #if _LIBCPP_STD_VER >= 23
 # define __cpp_lib_adaptor_iterator_pair_constructor    202106L
 # define __cpp_lib_allocate_at_least                    202302L
-// # define __cpp_lib_associative_heterogeneous_erasure    202110L
+# define __cpp_lib_associative_heterogeneous_erasure    202110L
 # define __cpp_lib_bind_back                            202202L
 # define __cpp_lib_byteswap                             202110L
 # define __cpp_lib_constexpr_bitset                     202207L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
index 794c2ee9ad086..587c90a02fec4 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
@@ -247,17 +247,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -332,17 +326,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
index d1a0570557246..8531a257b64a6 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
@@ -209,17 +209,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -274,17 +268,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
index 221d8aaebc14b..ba777e5434008 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
@@ -225,17 +225,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -306,17 +300,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
index d1c1335df7c80..b168215ea737b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
@@ -187,17 +187,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -248,17 +242,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L 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 f2d74cc99d864..153219940d1d3 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
@@ -4680,17 +4680,11 @@
 #    error "__cpp_lib_as_const should have the value 201510L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -6373,17 +6367,11 @@
 #    error "__cpp_lib_as_const should have the value 201510L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index a59f10e426ecc..6ec13d29000ea 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -13,7 +13,7 @@
 
 #include <cassert>
 
-#if TEST_STD_VER > 23
+#if TEST_STD_VER >= 23
 
 template <typename T>
 struct StoredType;
@@ -119,6 +119,6 @@ void test_non_transparent_extract(Container c) {
   assert(conversions == 3);
 }
 
-#endif // TEST_STD_VER > 23
+#endif // TEST_STD_VER >= 23
 
 #endif // TEST_TRANSPARENT_ASSOCIATIVE_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..0ad09ba56178c 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -152,7 +152,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_associative_heterogeneous_erasure",
             "values": {"c++23": 202110},
             "headers": ["map", "set", "unordered_map", "unordered_set"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_associative_heterogeneous_insertion",

>From 7c4cd977070e62f1a0809542c40f0b0871f15815 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 17:10:53 +0300
Subject: [PATCH 03/21] Don't uglify nodiscard

---
 libcxx/include/set | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/set b/libcxx/include/set
index 6eeec2ee7b6a0..34fec52cb91e1 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -865,7 +865,7 @@ public:
   template <class _Kp>
     requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
              !is_convertible_v<_Kp &&, const_iterator>)
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
     return __tree_.template __node_handle_extract<node_type>(__kp);
   }
 
@@ -1402,7 +1402,7 @@ public:
   template <class _Kp>
     requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
              !is_convertible_v<_Kp &&, const_iterator>)
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
     return __tree_.template __node_handle_extract<node_type>(__kp);
   }
 

>From a7a1f7a7b1f383122a8cffb415710da632b665a2 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 17:29:05 +0300
Subject: [PATCH 04/21] Overloads for map & multimap

---
 libcxx/include/map                            | 59 +++++++++++++++++++
 .../map.erasure/erase.transparent.pass.cpp    | 28 +++++++++
 .../extract.transparent.pass.cpp              | 27 +++++++++
 .../erase.transparent.pass.cpp                | 29 +++++++++
 .../extract.transparent.pass.cpp              | 27 +++++++++
 5 files changed, 170 insertions(+)
 create mode 100644 libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp

diff --git a/libcxx/include/map b/libcxx/include/map
index 1dda078c4e6c1..2266dbaabca9f 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -147,6 +147,9 @@ public:
 
     constexpr node_type extract(const_iterator position);                                       // C++17
     constexpr node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      constexpr node_type extract(K&& k);                                                       // C++23
+
     constexpr insert_return_type insert(node_type&& nh);                                        // C++17
     constexpr iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
@@ -170,7 +173,10 @@ public:
     constexpr iterator  erase(const_iterator position);
     constexpr iterator  erase(iterator position);                         // C++14
     constexpr size_type erase(const key_type& k);
+    template <typename K>
+      constexpr size_type erase(K&& k);                                   // C++23
     constexpr iterator  erase(const_iterator first, const_iterator last);
+
     constexpr void clear() noexcept;
 
     template<class C2>
@@ -426,13 +432,19 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     iterator insert(node_type&& nh);                                                  // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
     iterator  erase(const_iterator position);
     iterator  erase(iterator position); // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class C2>
@@ -1336,6 +1348,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
     return __tree_.erase(__f.__i_, __l.__i_);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1355,6 +1379,18 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   merge(map<key_type, mapped_type, _Compare2, allocator_type>& __source) {
@@ -2008,6 +2044,17 @@ public:
     return __tree_.erase(__f.__i_, __l.__i_);
   }
 
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
 #  if _LIBCPP_STD_VER >= 17
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
@@ -2025,6 +2072,18 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   merge(multimap<key_type, mapped_type, _Compare2, allocator_type>& __source) {
diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..2fe83c20acf67
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class map
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_final>>(
+      {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_non_transparent_erase<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..0dd4d9afdeb9b
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class map
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_non_transparent_extract<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..7e3f625370d73
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class multimap
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
+      {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_final>>(
+      {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_non_transparent_erase<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..9faa74f69e2b0
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class multimap
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_non_transparent_extract<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}

>From 978ed6c38b5ce529040228c67a9dfcf4ee06271f Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 18:12:03 +0300
Subject: [PATCH 05/21] Overloads for unordered_map & unordered_multimap

---
 libcxx/include/__hash_table                   | 16 +--
 libcxx/include/unordered_map                  | 62 +++++++++++-
 libcxx/include/unordered_set                  |  4 +-
 .../unord.map/erase.transparent.pass.cpp      | 56 +++++++++++
 .../extract.transparent.pass.cpp              | 56 +++++++++++
 .../unord.multimap/erase.transparent.pass.cpp | 56 +++++++++++
 .../extract.transparent.pass.cpp              | 56 +++++++++++
 .../test/support/test_transparent_unordered.h | 99 ++++++++++++++-----
 8 files changed, 366 insertions(+), 39 deletions(-)
 create mode 100644 libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp

diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 0ce4cf3edd6fd..ff3d0f1ab7749 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -896,10 +896,10 @@ public:
   template <class _Table>
   _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_multi(_Table& __source);
 
+  template <class _NodeHandle, class _Key>
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const _Key& __key);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(key_type const& __key);
-  template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const_iterator __it);
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_iterator(const_iterator __it);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
@@ -1648,18 +1648,18 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_insert_unique(const_iter
 }
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
-template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle
-__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(key_type const& __key) {
+template <class _NodeHandle, class _Key>
+_LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(const _Key& __key) {
   iterator __i = find(__key);
   if (__i == end())
     return _NodeHandle();
-  return __node_handle_extract<_NodeHandle>(__i);
+  return __node_handle_extract_iterator<_NodeHandle>(__i);
 }
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(const_iterator __p) {
+_LIBCPP_HIDE_FROM_ABI _NodeHandle
+__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_iterator(const_iterator __p) {
   allocator_type __alloc(__node_alloc());
   return _NodeHandle(remove(__p).release(), __alloc);
 }
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 8cefba01fb5f6..2a9bc016a4e9a 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -139,6 +139,9 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      size_type erase(K&& k);                                                         // C++23
+
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator           insert(const_iterator hint, node_type&& nh);                   // C++17
 
@@ -162,7 +165,10 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -425,13 +431,19 @@ public:
 
     node_type extract(const_iterator position);                // C++17
     node_type extract(const key_type& x);                      // C++17
+    template <typename K>
+      node_type extract(K&& k);                                // C++23
+
     iterator insert(node_type&& nh);                           // C++17
     iterator insert(const_iterator hint, node_type&& nh);      // C++17
 
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -1158,6 +1170,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first.__i_, __last.__i_);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1175,9 +1199,20 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
   }
 
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_map<key_type, mapped_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1896,6 +1931,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first.__i_, __last.__i_);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1913,9 +1960,20 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
   }
 
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_multimap<key_type, mapped_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index d54d59403b038..29bd03e6d1c5a 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -805,7 +805,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract<node_type>(__it);
+    return __table_.template __node_handle_extract_iterator<node_type>(__it);
   }
 
   template <class _H2, class _P2>
@@ -1412,7 +1412,7 @@ public:
     return __table_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __position) {
-    return __table_.template __node_handle_extract<node_type>(__position);
+    return __table_.template __node_handle_extract_iterator<node_type>(__position);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
     return __table_.template __node_handle_extract<node_type>(__key);
diff --git a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..2d0138ef6472f
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_map
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..08a2e05e777bc
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_map
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..b15d915a618d9
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_multimap
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..8eefc2b5f689b
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_multimap
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index e2d02402d808b..f77688b42e194 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -46,7 +46,7 @@ struct transparent_equal_final final : std::equal_to<> {};
 
 template <typename T>
 struct SearchedType {
-  explicit SearchedType(T value, int *counter) : value_(value), conversions_(counter) { }
+  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
 
   // Whenever a conversion is performed, increment the counter to keep track
   // of conversions.
@@ -55,48 +55,40 @@ struct SearchedType {
     return StoredType<T>{value_};
   }
 
-  int get_value() const {
-    return value_;
-  }
+  int get_value() const { return value_; }
 
 private:
   T value_;
-  int *conversions_;
+  int* conversions_;
 };
 
 template <typename T>
 struct StoredType {
   StoredType() = default;
-  StoredType(T value) : value_(value) { }
+  StoredType(T value) : value_(value) {}
 
-  friend bool operator==(StoredType const& lhs, StoredType const& rhs) {
-    return lhs.value_ == rhs.value_;
-  }
+  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
 
   // If we're being passed a SearchedType<T> object, avoid the conversion
   // to T. This allows testing that the transparent operations are correctly
   // forwarding the SearchedType all the way to this comparison by checking
   // that we didn't have a conversion when we search for a SearchedType<T>
   // in a container full of StoredType<T>.
-  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
-    return lhs.value_ == rhs.get_value();
-  }
+  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
 
-  int get_value() const {
-    return value_;
-  }
+  int get_value() const { return value_; }
 
 private:
   T value_;
 };
 
-template<template<class...> class UnorderedSet, class Hash, class Equal>
+template <template <class...> class UnorderedSet, class Hash, class Equal>
 using unord_set_type = UnorderedSet<StoredType<int>, Hash, Equal>;
 
-template<template<class...> class UnorderedMap, class Hash, class Equal>
+template <template <class...> class UnorderedMap, class Hash, class Equal>
 using unord_map_type = UnorderedMap<StoredType<int>, int, Hash, Equal>;
 
-template<class Container>
+template <class Container>
 void test_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -105,7 +97,7 @@ void test_transparent_find(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -116,7 +108,7 @@ void test_non_transparent_find(Container c) {
   assert(conversions == 3);
 }
 
-template<class Container>
+template <class Container>
 void test_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -125,7 +117,7 @@ void test_transparent_count(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -136,7 +128,7 @@ void test_non_transparent_count(Container c) {
   assert(conversions == 3);
 }
 
-template<class Container>
+template <class Container>
 void test_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -145,7 +137,7 @@ void test_transparent_contains(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -156,10 +148,10 @@ void test_non_transparent_contains(Container c) {
   assert(conversions == 3);
 }
 
-template<class Container>
+template <class Container>
 void test_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   iters = c.equal_range(SearchedType<int>(2, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
@@ -168,10 +160,10 @@ void test_transparent_equal_range(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   assert(conversions == 1);
   iters = c.equal_range(SearchedType<int>(2, &conversions));
@@ -182,6 +174,59 @@ void test_non_transparent_equal_range(Container c) {
   assert(conversions == 3);
 }
 
+#  if TEST_STD_VER >= 23
+
+template <class Container>
+void test_transparent_erase(Container c) {
+  int conversions = 0;
+
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+  assert(conversions == 0);
+
+  c.erase(c.begin());
+  c.erase(c.cbegin());
+
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_erase(Container c) {
+  int conversions = 0;
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(conversions == 1);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(conversions == 2);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+  assert(conversions == 3);
+}
+
+template <class Container>
+void test_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 0);
+
+  assert(!c.extract(c.cbegin()).empty());
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(conversions == 1);
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(conversions == 2);
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 3);
+}
+
+#  endif // TEST_STD_VER >= 23
+
 #endif // TEST_STD_VER > 17
 
 #endif // TEST_TRANSPARENT_UNORDERED_H

>From 52e260c1608ab53fedbbb1eaea81ed041b6d6e22 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 19:12:42 +0300
Subject: [PATCH 06/21] Overloads for unordered_set & unordered_multiset

---
 libcxx/include/unordered_map                  |  2 +-
 libcxx/include/unordered_set                  | 58 +++++++++++++++++++
 .../unord.multiset/erase.transparent.pass.cpp | 56 ++++++++++++++++++
 .../extract.transparent.pass.cpp              | 56 ++++++++++++++++++
 .../unord.set/erase.transparent.pass.cpp      | 56 ++++++++++++++++++
 .../unord.set/extract.transparent.pass.cpp    | 56 ++++++++++++++++++
 .../test/support/test_transparent_unordered.h |  4 +-
 7 files changed, 285 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp

diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 2a9bc016a4e9a..ca2173def1f71 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -140,7 +140,7 @@ public:
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
     template <typename K>
-      size_type erase(K&& k);                                                         // C++23
+      node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator           insert(const_iterator hint, node_type&& nh);                   // C++17
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 29bd03e6d1c5a..32a10c1be1e95 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -131,13 +131,19 @@ public:
 
     node_type extract(const_iterator position);                       // C++17
     node_type extract(const key_type& x);                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                       // C++23
+
     insert_return_type insert(node_type&& nh);                        // C++17
     iterator           insert(const_iterator hint, node_type&& nh);   // C++17
 
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -379,13 +385,19 @@ public:
 
     node_type extract(const_iterator position);             // C++17
     node_type extract(const key_type& x);                   // C++17
+    template <typename K>
+      node_type extract(K&& k);                             // C++23
+
     iterator insert(node_type&& nh);                        // C++17
     iterator insert(const_iterator hint, node_type&& nh);   // C++17
 
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -788,6 +800,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first, __last);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -808,6 +832,17 @@ public:
     return __table_.template __node_handle_extract_iterator<node_type>(__it);
   }
 
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_set<key_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1418,6 +1453,17 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
 
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_multiset<key_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1449,6 +1495,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first, __last);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
   _LIBCPP_HIDE_FROM_ABI void swap(unordered_multiset& __u) _NOEXCEPT_(__is_nothrow_swappable_v<__table>) {
diff --git a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..41420d41613b1
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_multiset
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..e094201a8cedd
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_multiset
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..efc55a0ea8964
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_set
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..e84facdaabcb0
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_set
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index f77688b42e194..f2439be716d44 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -55,7 +55,7 @@ struct SearchedType {
     return StoredType<T>{value_};
   }
 
-  int get_value() const { return value_; }
+  T get_value() const { return value_; }
 
 private:
   T value_;
@@ -76,7 +76,7 @@ struct StoredType {
   // in a container full of StoredType<T>.
   friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
 
-  int get_value() const { return value_; }
+  T get_value() const { return value_; }
 
 private:
   T value_;

>From 6bfa1e6710ae0e296c6600542e6be9ef1e832191 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 19:22:02 +0300
Subject: [PATCH 07/21] nodiscard test cases

---
 .../containers/associative/map/map.nodiscard.verify.cpp       | 4 ++++
 .../containers/associative/map/multimap.nodiscard.verify.cpp  | 4 ++++
 .../containers/associative/set/multiset.nodiscard.verify.cpp  | 4 ++++
 .../containers/associative/set/set.nodiscard.verify.cpp       | 4 ++++
 .../libcxx/containers/unord/unord.map/nodiscard.verify.cpp    | 4 ++++
 .../containers/unord/unord.multimap/nodiscard.verify.cpp      | 4 ++++
 .../containers/unord/unord.multiset/nodiscard.verify.cpp      | 4 ++++
 .../libcxx/containers/unord/unord.set/nodiscard.verify.cpp    | 4 ++++
 8 files changed, 32 insertions(+)

diff --git a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
index 20218b50e6c60..7a418502a722b 100644
--- a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
@@ -121,4 +121,8 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
index 99e4fbceabf12..49ac8c6a76064 100644
--- a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
@@ -106,4 +106,8 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
index 889b3cc62d191..a90f69fc96af4 100644
--- a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
@@ -104,4 +104,8 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
index 0453541d1b862..02f2634363679 100644
--- a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
@@ -104,4 +104,8 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
index 0747556905200..34df0f1d44335 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
@@ -81,6 +81,10 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   m[key];            // no-warning
   m[std::move(key)]; // no-warning
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
index cdb47d2b514ab..9ce6bb6a5d46c 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
@@ -81,6 +81,10 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   m.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
index ff62874caf01b..dbb10eaa40dd3 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
@@ -83,6 +83,10 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
index 628c31a93099f..d289d0ebfbfd0 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
@@ -82,6 +82,10 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 

>From 194b4e325a6032b37630a9d13aae80c28768cbf2 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 18:55:54 +0300
Subject: [PATCH 08/21] Refactor nodiscard tests

---
 .../associative/map/map.nodiscard.verify.cpp   | 18 +++++++++---------
 .../map/multimap.nodiscard.verify.cpp          | 18 +++++++++---------
 .../set/multiset.nodiscard.verify.cpp          | 18 +++++++++---------
 .../associative/set/set.nodiscard.verify.cpp   | 18 +++++++++---------
 .../unord/unord.map/nodiscard.verify.cpp       | 18 +++++++++---------
 .../unord/unord.multimap/nodiscard.verify.cpp  | 18 +++++++++---------
 .../unord/unord.multiset/nodiscard.verify.cpp  | 18 +++++++++---------
 .../unord/unord.set/nodiscard.verify.cpp       | 17 +++++++++--------
 8 files changed, 72 insertions(+), 71 deletions(-)

diff --git a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
index 7a418502a722b..c21456965190b 100644
--- a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
@@ -74,19 +74,23 @@ void test() {
   m.key_comp();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.value_comp();    // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
+#if TEST_STD_VER >= 14
+  std::map<int, int, TransparentCompare> tm;
+  const std::map<int, int, TransparentCompare> ctm{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(key);      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.cend()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::map<int, int, TransparentCompare> tm;
-  const std::map<int, int, TransparentCompare> ctm{};
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -121,8 +125,4 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
index 49ac8c6a76064..bb38153b22a8e 100644
--- a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
@@ -59,19 +59,23 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 14
+  std::multimap<int, int, TransparentCompare> tm;
+  const std::multimap<int, int, TransparentCompare> ctm{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(key);      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.cend()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::multimap<int, int, TransparentCompare> tm;
-  const std::multimap<int, int, TransparentCompare> ctm{};
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -106,8 +110,4 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
index a90f69fc96af4..3a762b059e89a 100644
--- a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
@@ -53,10 +53,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 14
+  std::multiset<int, TransparentCompare> ts;
+  const std::multiset<int, TransparentCompare> cts{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   s.extract(key);       // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.extract(s.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   s.get_allocator(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.key_comp();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -65,11 +74,6 @@ void test() {
   s.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cs.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::multiset<int, TransparentCompare> ts;
-  const std::multiset<int, TransparentCompare> cts{};
-
-  TransparentKey tkey;
-
   ts.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -104,8 +108,4 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
index 02f2634363679..acee396bfbb99 100644
--- a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
@@ -53,10 +53,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 14
+  std::set<int, TransparentCompare> ts;
+  const std::set<int, TransparentCompare> cts{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   s.extract(key);       // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.extract(s.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   s.get_allocator(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.key_comp();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -65,11 +74,6 @@ void test() {
   s.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cs.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::set<int, TransparentCompare> ts;
-  const std::set<int, TransparentCompare> cts{};
-
-  TransparentKey tkey;
-
   ts.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -104,8 +108,4 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
index 34df0f1d44335..19e6764c1f064 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
+  const std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(0);         // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
-  const std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -81,10 +85,6 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   m[key];            // no-warning
   m[std::move(key)]; // no-warning
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
index 9ce6bb6a5d46c..511d9e2062d48 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
+  const std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(0);         // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
-  const std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -81,10 +85,6 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   m.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
index dbb10eaa40dd3..1f0e76a412017 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
+  const std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   us.extract(key);        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.extract(us.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   us.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
   us.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cus.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
-  const std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
-
-  TransparentKey tkey;
-
   tus.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctus.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -83,10 +87,6 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
index d289d0ebfbfd0..2a3aca497e8c4 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
+  const std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   us.extract(key);        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.extract(us.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   us.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,10 +66,6 @@ void test() {
   us.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cus.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
-  const std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
-  TransparentKey tkey;
-
   tus.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctus.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -82,10 +87,6 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 

>From d2f5bf01bf1dc7813c2435233e645f7916ee2a4e Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 19:02:05 +0300
Subject: [PATCH 09/21] Drop reformatting

---
 libcxx/include/__tree                         |  2 +-
 .../test/support/test_transparent_unordered.h | 46 +++++++++++--------
 2 files changed, 28 insertions(+), 20 deletions(-)

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index fe54b53ac80b9..b25fcbcba5ff7 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -2089,7 +2089,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
   if (__nh.empty())
     return _InsertReturnType{end(), false, _NodeHandle()};
 
-  __node_pointer __ptr     = __nh.__ptr_;
+  __node_pointer __ptr = __nh.__ptr_;
   auto [__parent, __child] = __find_equal(__ptr->__get_value());
   if (__child != nullptr)
     return _InsertReturnType{
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index f2439be716d44..5aee507191351 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -46,7 +46,7 @@ struct transparent_equal_final final : std::equal_to<> {};
 
 template <typename T>
 struct SearchedType {
-  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+  explicit SearchedType(T value, int *counter) : value_(value), conversions_(counter) { }
 
   // Whenever a conversion is performed, increment the counter to keep track
   // of conversions.
@@ -55,40 +55,48 @@ struct SearchedType {
     return StoredType<T>{value_};
   }
 
-  T get_value() const { return value_; }
+  int get_value() const {
+    return value_;
+  }
 
 private:
   T value_;
-  int* conversions_;
+  int *conversions_;
 };
 
 template <typename T>
 struct StoredType {
   StoredType() = default;
-  StoredType(T value) : value_(value) {}
+  StoredType(T value) : value_(value) { }
 
-  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+  friend bool operator==(StoredType const& lhs, StoredType const& rhs) {
+    return lhs.value_ == rhs.value_;
+  }
 
   // If we're being passed a SearchedType<T> object, avoid the conversion
   // to T. This allows testing that the transparent operations are correctly
   // forwarding the SearchedType all the way to this comparison by checking
   // that we didn't have a conversion when we search for a SearchedType<T>
   // in a container full of StoredType<T>.
-  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
+    return lhs.value_ == rhs.get_value();
+  }
 
-  T get_value() const { return value_; }
+  int get_value() const {
+    return value_;
+  }
 
 private:
   T value_;
 };
 
-template <template <class...> class UnorderedSet, class Hash, class Equal>
+template<template<class...> class UnorderedSet, class Hash, class Equal>
 using unord_set_type = UnorderedSet<StoredType<int>, Hash, Equal>;
 
-template <template <class...> class UnorderedMap, class Hash, class Equal>
+template<template<class...> class UnorderedMap, class Hash, class Equal>
 using unord_map_type = UnorderedMap<StoredType<int>, int, Hash, Equal>;
 
-template <class Container>
+template<class Container>
 void test_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -97,7 +105,7 @@ void test_transparent_find(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -108,7 +116,7 @@ void test_non_transparent_find(Container c) {
   assert(conversions == 3);
 }
 
-template <class Container>
+template<class Container>
 void test_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -117,7 +125,7 @@ void test_transparent_count(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -128,7 +136,7 @@ void test_non_transparent_count(Container c) {
   assert(conversions == 3);
 }
 
-template <class Container>
+template<class Container>
 void test_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -137,7 +145,7 @@ void test_transparent_contains(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -148,10 +156,10 @@ void test_non_transparent_contains(Container c) {
   assert(conversions == 3);
 }
 
-template <class Container>
+template<class Container>
 void test_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   iters = c.equal_range(SearchedType<int>(2, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
@@ -160,10 +168,10 @@ void test_transparent_equal_range(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   assert(conversions == 1);
   iters = c.equal_range(SearchedType<int>(2, &conversions));

>From 801bab5272d1bc04913108ec01b3eba61ed7d4cb Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 19:07:29 +0300
Subject: [PATCH 10/21] typename ==> class

---
 libcxx/include/map           | 2 +-
 libcxx/include/set           | 4 ++--
 libcxx/include/unordered_map | 4 ++--
 libcxx/include/unordered_set | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/map b/libcxx/include/map
index 2266dbaabca9f..342d3443051f5 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -432,7 +432,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     iterator insert(node_type&& nh);                                                  // C++17
diff --git a/libcxx/include/set b/libcxx/include/set
index 34fec52cb91e1..8f19680749ce0 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -125,7 +125,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
@@ -376,7 +376,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     iterator insert(node_type&& nh);                                                  // C++17
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index ca2173def1f71..da31c801d3d0c 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -139,7 +139,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
@@ -431,7 +431,7 @@ public:
 
     node_type extract(const_iterator position);                // C++17
     node_type extract(const key_type& x);                      // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                // C++23
 
     iterator insert(node_type&& nh);                           // C++17
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 32a10c1be1e95..2ae6d02556f0d 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -131,7 +131,7 @@ public:
 
     node_type extract(const_iterator position);                       // C++17
     node_type extract(const key_type& x);                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                       // C++23
 
     insert_return_type insert(node_type&& nh);                        // C++17

>From e57e551a7c1d20d8409d2ff7d4668614debca3a7 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 20:11:11 +0300
Subject: [PATCH 11/21] Better extract testing

---
 .../support/test_transparent_associative.hpp  | 56 ++++++++++++++-----
 .../test/support/test_transparent_unordered.h | 50 ++++++++++++-----
 2 files changed, 77 insertions(+), 29 deletions(-)

diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index 6ec13d29000ea..2448f3cd16083 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -41,6 +41,10 @@ struct StoredType {
   StoredType() = default;
   StoredType(T value) : value_(value) {}
 
+  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+
+  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+
   T get_value() const { return value_; }
 
   auto operator<=>(const StoredType<T>&) const = default;
@@ -72,17 +76,17 @@ struct transparent_comparator_final final : public transparent_comparator_base {
 
 template <class Container>
 void test_transparent_erase(Container c) {
+  static_assert(
+      std::same_as<
+          typename Container::size_type,
+          std::invoke_result_t<decltype(&Container::template erase<SearchedType<int>>), Container, SearchedType<int>>>);
+
   int conversions = 0;
+
   assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
-
   assert(conversions == 0);
-
-  c.erase(c.begin());
-  c.erase(c.cbegin());
-
-  assert(c.empty());
 }
 
 template <class Container>
@@ -96,25 +100,47 @@ void test_non_transparent_erase(Container c) {
   assert(conversions == 3);
 }
 
+template <typename NodeHandle>
+concept node_handle_has_key = requires(NodeHandle nh) {
+  { nh.key() };
+};
+
+template <class T, class Container>
+void test_single_extract(SearchedType<T> key, Container& c) {
+  auto node_handle = c.extract(key);
+
+  assert(!node_handle.empty());
+
+  if constexpr (node_handle_has_key<typename Container::node_type>) {
+    assert(node_handle.key() == key);
+  } else {
+    assert(node_handle.value() == key);
+  }
+}
+
 template <class Container>
 void test_transparent_extract(Container c) {
+  static_assert(std::same_as< typename Container::node_type,
+                              std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
+                                                   Container,
+                                                   SearchedType<int>>>);
+
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 0);
-
-  assert(!c.extract(c.cbegin()).empty());
-  assert(c.empty());
 }
 
 template <class Container>
 void test_non_transparent_extract(Container c) {
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(conversions == 1);
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
-  assert(conversions == 2);
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 3);
 }
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index 5aee507191351..1bc6fb07dee6f 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -186,17 +186,17 @@ void test_non_transparent_equal_range(Container c) {
 
 template <class Container>
 void test_transparent_erase(Container c) {
+  static_assert(
+      std::same_as<
+          typename Container::size_type,
+          std::invoke_result_t<decltype(&Container::template erase<SearchedType<int>>), Container, SearchedType<int>>>);
+
   int conversions = 0;
 
   assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
   assert(conversions == 0);
-
-  c.erase(c.begin());
-  c.erase(c.cbegin());
-
-  assert(c.empty());
 }
 
 template <class Container>
@@ -210,25 +210,47 @@ void test_non_transparent_erase(Container c) {
   assert(conversions == 3);
 }
 
+template <typename NodeHandle>
+concept node_handle_has_key = requires(NodeHandle nh) {
+  { nh.key() };
+};
+
+template <class T, class Container>
+void test_single_extract(SearchedType<T> key, Container& c) {
+  auto node_handle = c.extract(key);
+
+  assert(!node_handle.empty());
+
+  if constexpr (node_handle_has_key<typename Container::node_type>) {
+    assert(node_handle.key() == key);
+  } else {
+    assert(node_handle.value() == key);
+  }
+}
+
 template <class Container>
 void test_transparent_extract(Container c) {
+  static_assert(std::same_as< typename Container::node_type,
+                              std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
+                                                   Container,
+                                                   SearchedType<int>>>);
+
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 0);
-
-  assert(!c.extract(c.cbegin()).empty());
-  assert(c.empty());
 }
 
 template <class Container>
 void test_non_transparent_extract(Container c) {
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(conversions == 1);
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
-  assert(conversions == 2);
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 3);
 }

>From fee83fdd349d4a3166ebb57e439b1281cbe9fde3 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 20:43:59 +0300
Subject: [PATCH 12/21] Include concepts in test

---
 libcxx/test/support/test_transparent_associative.hpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index 2448f3cd16083..dae3acc725162 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -15,6 +15,8 @@
 
 #if TEST_STD_VER >= 23
 
+#  include <concepts>
+
 template <typename T>
 struct StoredType;
 

>From 1f1aba10aa26b42f41d2dec74e816d66b0acb3c5 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 21:04:33 +0300
Subject: [PATCH 13/21] Include type_traits in test

---
 libcxx/test/support/test_transparent_associative.hpp | 1 +
 libcxx/test/support/test_transparent_unordered.h     | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index dae3acc725162..e3cac5e8a51cb 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -16,6 +16,7 @@
 #if TEST_STD_VER >= 23
 
 #  include <concepts>
+#  include <type_traits>
 
 template <typename T>
 struct StoredType;
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index 1bc6fb07dee6f..14faea4d8e5b7 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -16,6 +16,9 @@
 
 #if TEST_STD_VER > 17
 
+#  include <concepts>
+#  include <type_traits>
+
 template <typename T>
 struct StoredType;
 

>From 181d598df66f06db00fbeaededa6c86ed70a9b85 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 10 Jan 2026 02:07:55 +0300
Subject: [PATCH 14/21] Correct header suffix

---
 .../associative/map/map.erasure/erase.transparent.pass.cpp    | 4 ++--
 .../map/map.modifiers/extract.transparent.pass.cpp            | 4 ++--
 .../multimap/multimap.erasure/erase.transparent.pass.cpp      | 4 ++--
 .../multimap/multimap.modifiers/extract.transparent.pass.cpp  | 4 ++--
 .../associative/multiset/extract.transparent.pass.cpp         | 4 ++--
 .../multiset/multiset.erasure/erase.transparent.pass.cpp      | 4 ++--
 .../containers/associative/set/extract.transparent.pass.cpp   | 4 ++--
 .../set/{ => set.erasure}/erase.transparent.pass.cpp          | 4 ++--
 .../std/containers/unord/unord.map/erase.transparent.pass.cpp | 2 +-
 .../unord.map.modifiers/extract.transparent.pass.cpp          | 2 +-
 .../unord/unord.multimap/erase.transparent.pass.cpp           | 2 +-
 .../unord.multimap.modifiers/extract.transparent.pass.cpp     | 2 +-
 .../unord/unord.multiset/erase.transparent.pass.cpp           | 2 +-
 .../unord/unord.multiset/extract.transparent.pass.cpp         | 2 +-
 .../std/containers/unord/unord.set/erase.transparent.pass.cpp | 2 +-
 .../containers/unord/unord.set/extract.transparent.pass.cpp   | 2 +-
 ...sparent_associative.hpp => test_transparent_associative.h} | 0
 17 files changed, 24 insertions(+), 24 deletions(-)
 rename libcxx/test/std/containers/associative/set/{ => set.erasure}/erase.transparent.pass.cpp (91%)
 rename libcxx/test/support/{test_transparent_associative.hpp => test_transparent_associative.h} (100%)

diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
index 2fe83c20acf67..c8cfb77069d62 100644
--- a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class map
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
index 0dd4d9afdeb9b..97cdcda5e519e 100644
--- a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class map
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
index 7e3f625370d73..9a257b82b9388 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multimap
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
index 9faa74f69e2b0..f467d28996ed7 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multimap
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
diff --git a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
index fc04c8d85c267..f05905e7483a1 100644
--- a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multiset
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
index a7252f0aa039c..2669b07e8fd8d 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multiset
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
diff --git a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
index 62c72808b83d1..0b036971e119e 100644
--- a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class set
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
diff --git a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
similarity index 91%
rename from libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
rename to libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
index d2ca1fe599374..87e99da2a9bef 100644
--- a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class set
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
diff --git a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
index 2d0138ef6472f..edbaec04d3329 100644
--- a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_map
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
index 08a2e05e777bc..a14dca09159ac 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_map
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
index b15d915a618d9..e09b66da0ea10 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multimap
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
index 8eefc2b5f689b..4af37297ec0ae 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multimap
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
index 41420d41613b1..3fa33f111b1c8 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multiset
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
index e094201a8cedd..9ec51b55d2278 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multiset
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
index efc55a0ea8964..944faf091d5df 100644
--- a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_set
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
index e84facdaabcb0..06ad1deaf05d1 100644
--- a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_set
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.h
similarity index 100%
rename from libcxx/test/support/test_transparent_associative.hpp
rename to libcxx/test/support/test_transparent_associative.h

>From 1ff485c7129ea014892820d5d55b448f08642660 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 10 Jan 2026 02:15:39 +0300
Subject: [PATCH 15/21] typename ==> class

---
 libcxx/include/map           | 2 +-
 libcxx/include/set           | 4 ++--
 libcxx/include/unordered_map | 4 ++--
 libcxx/include/unordered_set | 6 +++---
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/map b/libcxx/include/map
index 342d3443051f5..12a783df8fc4d 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -441,7 +441,7 @@ public:
     iterator  erase(const_iterator position);
     iterator  erase(iterator position); // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
 
diff --git a/libcxx/include/set b/libcxx/include/set
index 8f19680749ce0..6be683fc2b269 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -134,7 +134,7 @@ public:
     iterator  erase(const_iterator position);
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
 
@@ -386,7 +386,7 @@ public:
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
     iterator  erase(const_iterator first, const_iterator last);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
 
     void clear() noexcept;
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index da31c801d3d0c..f81270797228f 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -165,7 +165,7 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
 
@@ -440,7 +440,7 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
 
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 2ae6d02556f0d..c0636fe437698 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -140,7 +140,7 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
 
@@ -385,7 +385,7 @@ public:
 
     node_type extract(const_iterator position);             // C++17
     node_type extract(const key_type& x);                   // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                             // C++23
 
     iterator insert(node_type&& nh);                        // C++17
@@ -394,7 +394,7 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
 

>From 08e9b952756d97859c72776a90d65ca935f8ed40 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 7 Feb 2026 19:04:30 +0300
Subject: [PATCH 16/21] Rename extract method

---
 libcxx/include/__hash_table  | 6 +++---
 libcxx/include/__tree        | 6 +++---
 libcxx/include/map           | 4 ++--
 libcxx/include/set           | 4 ++--
 libcxx/include/unordered_map | 4 ++--
 libcxx/include/unordered_set | 4 ++--
 6 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index ff3d0f1ab7749..19ecb90928c7f 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -899,7 +899,7 @@ public:
   template <class _NodeHandle, class _Key>
   _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const _Key& __key);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_iterator(const_iterator __it);
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_from_iterator(const_iterator __it);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
@@ -1653,13 +1653,13 @@ _LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__no
   iterator __i = find(__key);
   if (__i == end())
     return _NodeHandle();
-  return __node_handle_extract_iterator<_NodeHandle>(__i);
+  return __node_handle_extract_from_iterator<_NodeHandle>(__i);
 }
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 template <class _NodeHandle>
 _LIBCPP_HIDE_FROM_ABI _NodeHandle
-__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_iterator(const_iterator __p) {
+__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_from_iterator(const_iterator __p) {
   allocator_type __alloc(__node_alloc());
   return _NodeHandle(remove(__p).release(), __alloc);
 }
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index b25fcbcba5ff7..f4e0d0b02369c 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1242,7 +1242,7 @@ public:
   template <class _NodeHandle, class _Key>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const _Key&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract_iterator(const_iterator);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract_from_iterator(const_iterator);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p);
@@ -2126,13 +2126,13 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const& _Key __key) {
   iterator __it = __lower_bound_multi(__key);
   if (__it == end() || __value_comp_(__key, *__it))
     return _NodeHandle();
-  return __node_handle_extract_iterator<_NodeHandle>(__it);
+  return __node_handle_extract_from_iterator<_NodeHandle>(__it);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_iterator(const_iterator __p) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_from_iterator(const_iterator __p) {
   __node_pointer __np = __p.__get_np();
   __remove_node_pointer(__np);
   return _NodeHandle(__np, __alloc());
diff --git a/libcxx/include/map b/libcxx/include/map
index 12a783df8fc4d..ab5c0c9b5840d 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1377,7 +1377,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_iterator_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23
@@ -2070,7 +2070,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/set b/libcxx/include/set
index 6be683fc2b269..8c3dac354e1f3 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -857,7 +857,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
+    return __tree_.template __node_handle_extract_from_iterator<node_type>(__it);
   }
 
 #    if _LIBCPP_STD_VER >= 23
@@ -1394,7 +1394,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
+    return __tree_.template __node_handle_extract_from_iterator<node_type>(__it);
   }
 
 #    if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index f81270797228f..41784958719aa 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1199,7 +1199,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23
@@ -1960,7 +1960,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index c0636fe437698..50be2c8c6222d 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -829,7 +829,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract_iterator<node_type>(__it);
+    return __table_.template __node_handle_extract_from_iterator<node_type>(__it);
   }
 
 #    if _LIBCPP_STD_VER >= 23
@@ -1447,7 +1447,7 @@ public:
     return __table_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __position) {
-    return __table_.template __node_handle_extract_iterator<node_type>(__position);
+    return __table_.template __node_handle_extract_from_iterator<node_type>(__position);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
     return __table_.template __node_handle_extract<node_type>(__key);

>From d104290bd82f56ef462e0660eb8f8c0df3111925 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Tue, 16 Jun 2026 04:22:05 +0300
Subject: [PATCH 17/21] fixes after rebase

---
 libcxx/include/__tree | 2 +-
 libcxx/include/map    | 9 +++++----
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index f4e0d0b02369c..ed7d2f306f544 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -2122,7 +2122,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle, class _Key>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const& _Key __key) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const _Key& __key) {
   iterator __it = __lower_bound_multi(__key);
   if (__it == end() || __value_comp_(__key, *__it))
     return _NodeHandle();
diff --git a/libcxx/include/map b/libcxx/include/map
index ab5c0c9b5840d..71f0284a55cee 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -147,7 +147,7 @@ public:
 
     constexpr node_type extract(const_iterator position);                                       // C++17
     constexpr node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       constexpr node_type extract(K&& k);                                                       // C++23
 
     constexpr insert_return_type insert(node_type&& nh);                                        // C++17
@@ -173,7 +173,7 @@ public:
     constexpr iterator  erase(const_iterator position);
     constexpr iterator  erase(iterator position);                         // C++14
     constexpr size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       constexpr size_type erase(K&& k);                                   // C++23
     constexpr iterator  erase(const_iterator first, const_iterator last);
 
@@ -1049,7 +1049,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26
   map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 #  endif
@@ -1377,7 +1378,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator_from_iterator<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23

>From 01cec7c5af424ca29d15e091e0b9f246f11abd75 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Tue, 16 Jun 2026 04:30:02 +0300
Subject: [PATCH 18/21] release 23

---
 libcxx/docs/ReleaseNotes/22.rst    | 1 -
 libcxx/docs/ReleaseNotes/23.rst    | 1 +
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 libcxx/include/map                 | 3 +--
 4 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 5b05666cbb419..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -38,7 +38,6 @@ What's New in Libc++ 22.0.0?
 Implemented Papers
 ------------------
 
-- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
 - P2592R3: Hashing support for ``std::chrono`` value classes (`Github <https://llvm.org/PR105358>`__)
 - P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__)
 - P2988R12: ``std::optional<T&>`` (`Github <https://llvm.org/PR148131>`__)
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1852351cedc4e..f3206cceb3d87 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,7 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index d6fa695026543..fac8f0061e305 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -30,7 +30,7 @@
 "`P1147R1 <https://wg21.link/P1147R1>`__","Printing ``volatile`` Pointers","2021-10 (Virtual)","|Complete|","14","`#105161 <https://github.com/llvm/llvm-project/issues/105161>`__",""
 "`P1272R4 <https://wg21.link/P1272R4>`__","Byteswapping for fun&&nuf","2021-10 (Virtual)","|Complete|","14","`#105163 <https://github.com/llvm/llvm-project/issues/105163>`__",""
 "`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","","`#105164 <https://github.com/llvm/llvm-project/issues/105164>`__",""
-"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","22","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
+"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","23","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
 "`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14","`#105166 <https://github.com/llvm/llvm-project/issues/105166>`__",""
 "`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
 "`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|Complete|","22","`#105169 <https://github.com/llvm/llvm-project/issues/105169>`__",""
diff --git a/libcxx/include/map b/libcxx/include/map
index 71f0284a55cee..3ede95877bcb7 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1050,8 +1050,7 @@ public:
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26
-  map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 #  endif
 

>From aa629905b458823f7766387124800f91f83f9c40 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 26 Jun 2026 22:28:04 +0300
Subject: [PATCH 19/21] add constexpr tests for map and multimap

---
 libcxx/include/__node_handle                  |  4 +--
 libcxx/include/map                            | 18 +++++-----
 .../map.erasure/erase.transparent.pass.cpp    | 14 ++++++--
 .../extract.transparent.pass.cpp              | 14 ++++++--
 .../erase.transparent.pass.cpp                | 14 ++++++--
 .../extract.transparent.pass.cpp              | 14 ++++++--
 .../support/test_transparent_associative.h    | 35 ++++++++++---------
 7 files changed, 77 insertions(+), 36 deletions(-)

diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index 993283cc926da..eb8e57d2b82f7 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -180,11 +180,11 @@ struct __map_node_handle_specifics {
   using mapped_type = typename _NodeType::__node_value_type::second_type;
 
   // This method is not constexpr as per the standard.
-  _LIBCPP_HIDE_FROM_ABI key_type& key() const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_type& key() const {
     return const_cast<key_type&>(static_cast<_Derived const*>(this)->__ptr_->__get_value().first);
   }
 
-  _LIBCPP_HIDE_FROM_ABI mapped_type& mapped() const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 mapped_type& mapped() const {
     return static_cast<_Derived const*>(this)->__ptr_->__get_value().second;
   }
 };
diff --git a/libcxx/include/map b/libcxx/include/map
index 3ede95877bcb7..9b33ae697a001 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1049,8 +1049,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 #  endif
 
@@ -1855,15 +1855,15 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multimap(__f, __l, key_compare(), __a) {}
 #  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multimap(from_range_t, _Range&& __range, const allocator_type& __a)
       : multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
@@ -1893,8 +1893,8 @@ public:
   }
 
 #    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multimap(initializer_list<value_type> __il, const allocator_type& __a)
       : multimap(__il, key_compare(), __a) {}
 #    endif
 
@@ -2069,7 +2069,7 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
index c8cfb77069d62..fbb632847b38f 100644
--- a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -13,16 +13,26 @@
 // class map
 
 //    template<class K>
-//        size_type erase(K&& k) const;        // C++23
+//        constexpr size_type erase(K&& k) const;        // C++23
 
 #include <map>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
 
   test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_final>>(
       {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
 
   test_non_transparent_erase<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
index 97cdcda5e519e..01544585333cc 100644
--- a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -13,15 +13,25 @@
 // class map
 
 //    template<class K>
-//        node_type extract(K&& k) const;        // C++23
+//        constexpr node_type extract(K&& k) const;        // C++23
 
 #include <map>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
 
   test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
 
   test_non_transparent_extract<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
index 9a257b82b9388..bba57e93c397b 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -13,12 +13,12 @@
 // class multimap
 
 //    template<class K>
-//        size_type erase(K&& k) const;        // C++23
+//        constexpr size_type erase(K&& k) const;        // C++23
 
 #include <map>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
       {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
 
@@ -26,4 +26,14 @@ int main(int, char**) {
       {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
 
   test_non_transparent_erase<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
index f467d28996ed7..0c54072aa94cd 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -13,15 +13,25 @@
 // class multimap
 
 //    template<class K>
-//        node_type extract(K&& k) const;        // C++23
+//        constexpr node_type extract(K&& k) const;        // C++23
 
 #include <map>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
 
   test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
 
   test_non_transparent_extract<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/support/test_transparent_associative.h b/libcxx/test/support/test_transparent_associative.h
index e3cac5e8a51cb..e8acb8d986a88 100644
--- a/libcxx/test/support/test_transparent_associative.h
+++ b/libcxx/test/support/test_transparent_associative.h
@@ -23,16 +23,16 @@ struct StoredType;
 
 template <typename T>
 struct SearchedType {
-  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+  constexpr explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
 
-  operator StoredType<T>() const {
+  constexpr operator StoredType<T>() const {
     ++*conversions_;
     return StoredType<T>{value_};
   }
 
-  T get_value() const { return value_; }
+  constexpr T get_value() const { return value_; }
 
-  auto operator<=>(const SearchedType<T>&) const = default;
+  constexpr auto operator<=>(const SearchedType<T>&) const = default;
 
 private:
   T value_;
@@ -42,15 +42,16 @@ struct SearchedType {
 template <typename T>
 struct StoredType {
   StoredType() = default;
-  StoredType(T value) : value_(value) {}
 
-  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+  constexpr StoredType(T value) : value_(value) {}
 
-  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+  constexpr friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
 
-  T get_value() const { return value_; }
+  constexpr friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
 
-  auto operator<=>(const StoredType<T>&) const = default;
+  constexpr T get_value() const { return value_; }
+
+  constexpr auto operator<=>(const StoredType<T>&) const = default;
 
 private:
   T value_;
@@ -60,17 +61,17 @@ struct transparent_comparator_base {
   using is_transparent = void;
 
   template <typename T>
-  bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
+  constexpr bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
     return lhs.get_value() < rhs.get_value();
   }
 
   template <typename T>
-  bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
+  constexpr bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
     return lhs.get_value() < rhs.get_value();
   }
 
   template <typename T>
-  bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
+  constexpr bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
     return lhs < rhs;
   }
 };
@@ -78,7 +79,7 @@ struct transparent_comparator_base {
 struct transparent_comparator_final final : public transparent_comparator_base {};
 
 template <class Container>
-void test_transparent_erase(Container c) {
+constexpr void test_transparent_erase(Container c) {
   static_assert(
       std::same_as<
           typename Container::size_type,
@@ -93,7 +94,7 @@ void test_transparent_erase(Container c) {
 }
 
 template <class Container>
-void test_non_transparent_erase(Container c) {
+constexpr void test_non_transparent_erase(Container c) {
   int conversions = 0;
   assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
   assert(conversions == 1);
@@ -109,7 +110,7 @@ concept node_handle_has_key = requires(NodeHandle nh) {
 };
 
 template <class T, class Container>
-void test_single_extract(SearchedType<T> key, Container& c) {
+constexpr void test_single_extract(SearchedType<T> key, Container& c) {
   auto node_handle = c.extract(key);
 
   assert(!node_handle.empty());
@@ -122,7 +123,7 @@ void test_single_extract(SearchedType<T> key, Container& c) {
 }
 
 template <class Container>
-void test_transparent_extract(Container c) {
+constexpr void test_transparent_extract(Container c) {
   static_assert(std::same_as< typename Container::node_type,
                               std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
                                                    Container,
@@ -138,7 +139,7 @@ void test_transparent_extract(Container c) {
 }
 
 template <class Container>
-void test_non_transparent_extract(Container c) {
+constexpr void test_non_transparent_extract(Container c) {
   int conversions = 0;
 
   test_single_extract(SearchedType<int>(1, &conversions), c);

>From f688d05c39a6437e2d23a4b9c57cac69b30c0ae8 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 26 Jun 2026 22:37:08 +0300
Subject: [PATCH 20/21] fix node_handle case

---
 libcxx/include/__node_handle                       |  4 ++--
 libcxx/test/support/test_transparent_associative.h | 11 -----------
 2 files changed, 2 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index eb8e57d2b82f7..993283cc926da 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -180,11 +180,11 @@ struct __map_node_handle_specifics {
   using mapped_type = typename _NodeType::__node_value_type::second_type;
 
   // This method is not constexpr as per the standard.
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_type& key() const {
+  _LIBCPP_HIDE_FROM_ABI key_type& key() const {
     return const_cast<key_type&>(static_cast<_Derived const*>(this)->__ptr_->__get_value().first);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 mapped_type& mapped() const {
+  _LIBCPP_HIDE_FROM_ABI mapped_type& mapped() const {
     return static_cast<_Derived const*>(this)->__ptr_->__get_value().second;
   }
 };
diff --git a/libcxx/test/support/test_transparent_associative.h b/libcxx/test/support/test_transparent_associative.h
index e8acb8d986a88..90eb2feae4e8e 100644
--- a/libcxx/test/support/test_transparent_associative.h
+++ b/libcxx/test/support/test_transparent_associative.h
@@ -104,22 +104,11 @@ constexpr void test_non_transparent_erase(Container c) {
   assert(conversions == 3);
 }
 
-template <typename NodeHandle>
-concept node_handle_has_key = requires(NodeHandle nh) {
-  { nh.key() };
-};
-
 template <class T, class Container>
 constexpr void test_single_extract(SearchedType<T> key, Container& c) {
   auto node_handle = c.extract(key);
 
   assert(!node_handle.empty());
-
-  if constexpr (node_handle_has_key<typename Container::node_type>) {
-    assert(node_handle.key() == key);
-  } else {
-    assert(node_handle.value() == key);
-  }
 }
 
 template <class Container>

>From 26f71e9462e6620fcc44287a52669930ab9ad1b4 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 26 Jun 2026 22:54:03 +0300
Subject: [PATCH 21/21] fix format

---
 libcxx/include/map                               | 16 ++++++++--------
 .../test/support/test_transparent_associative.h  |  4 +++-
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/map b/libcxx/include/map
index 9b33ae697a001..e431c79479c10 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1049,8 +1049,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
-  map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 #  endif
 
@@ -1855,15 +1855,15 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
-  multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multimap(__f, __l, key_compare(), __a) {}
 #  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
-  multimap(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(from_range_t, _Range&& __range, const allocator_type& __a)
       : multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
@@ -1893,8 +1893,8 @@ public:
   }
 
 #    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
-  multimap(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(initializer_list<value_type> __il, const allocator_type& __a)
       : multimap(__il, key_compare(), __a) {}
 #    endif
 
diff --git a/libcxx/test/support/test_transparent_associative.h b/libcxx/test/support/test_transparent_associative.h
index 90eb2feae4e8e..85c35e3acff8f 100644
--- a/libcxx/test/support/test_transparent_associative.h
+++ b/libcxx/test/support/test_transparent_associative.h
@@ -47,7 +47,9 @@ struct StoredType {
 
   constexpr friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
 
-  constexpr friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+  constexpr friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
+    return lhs.value_ == rhs.get_value();
+  }
 
   constexpr T get_value() const { return value_; }
 


From libcxx-commits at lists.llvm.org  Sun Jul  5 04:59:19 2026
From: libcxx-commits at lists.llvm.org (Rafail Shakhin ogly via libcxx-commits)
Date: Sun, 05 Jul 2026 04:59:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2077R3: Heterogeneous
 erasure overloads for associative containers (PR #174680)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/174680@github.com>
Message-ID: <6a4a4717.a87eb5dc.1d432d.44ad@mx.google.com>


https://github.com/rsaddatimov updated https://github.com/llvm/llvm-project/pull/174680

>From be7b5f514cca12278e8f3ccc96145dbea971d4be Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 05:11:31 +0300
Subject: [PATCH 01/22] Initial commit: start with set & multiset tests

---
 libcxx/include/__tree                         |  16 +--
 libcxx/include/map                            |  10 +-
 libcxx/include/set                            |  64 ++++++++-
 .../multiset/extract.transparent.pass.cpp     |  27 ++++
 .../erase.transparent.pass.cpp                |  27 ++++
 .../set/erase.transparent.pass.cpp            |  27 ++++
 .../set/extract.transparent.pass.cpp          |  27 ++++
 .../support/test_transparent_associative.hpp  | 124 ++++++++++++++++++
 8 files changed, 307 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/support/test_transparent_associative.hpp

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index dafeff3e2e019..fe54b53ac80b9 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1239,10 +1239,10 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   __node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source);
 
+  template <class _NodeHandle, class _Key>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const _Key&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(key_type const&);
-  template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const_iterator);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract_iterator(const_iterator);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p);
@@ -2089,7 +2089,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
   if (__nh.empty())
     return _InsertReturnType{end(), false, _NodeHandle()};
 
-  __node_pointer __ptr = __nh.__ptr_;
+  __node_pointer __ptr     = __nh.__ptr_;
   auto [__parent, __child] = __find_equal(__ptr->__get_value());
   if (__child != nullptr)
     return _InsertReturnType{
@@ -2120,19 +2120,19 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-template <class _NodeHandle>
+template <class _NodeHandle, class _Key>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(key_type const& __key) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const& _Key __key) {
   iterator __it = __lower_bound_multi(__key);
   if (__it == end() || __value_comp_(__key, *__it))
     return _NodeHandle();
-  return __node_handle_extract<_NodeHandle>(__it);
+  return __node_handle_extract_iterator<_NodeHandle>(__it);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const_iterator __p) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_iterator(const_iterator __p) {
   __node_pointer __np = __p.__get_np();
   __remove_node_pointer(__np);
   return _NodeHandle(__np, __alloc());
diff --git a/libcxx/include/map b/libcxx/include/map
index 8efe18fa3f839..1dda078c4e6c1 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1037,8 +1037,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 #  endif
 
@@ -1353,7 +1353,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
@@ -2022,8 +2022,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it.__i_);
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
diff --git a/libcxx/include/set b/libcxx/include/set
index a8e245b22ae51..6eeec2ee7b6a0 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -125,13 +125,19 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
     iterator  erase(const_iterator position);
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class C2>
@@ -370,6 +376,9 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     iterator insert(node_type&& nh);                                                  // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
@@ -377,6 +386,9 @@ public:
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
     iterator  erase(const_iterator first, const_iterator last);
+    template <typename K>
+      size_type erase(K&& k); // C++23
+
     void clear() noexcept;
 
     template<class C2>
@@ -816,6 +828,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
     return __tree_.erase(__f, __l);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -833,8 +857,20 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
   }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1329,6 +1365,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
   _LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_multi(__k); }
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1346,8 +1394,20 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
diff --git a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..fc04c8d85c267
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class multiset
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
+
+  test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
+
+  test_non_transparent_extract<std::multiset<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..a7252f0aa039c
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class multiset
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
+
+  test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
+
+  test_non_transparent_erase<std::multiset<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..d2ca1fe599374
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class set
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
+
+  test_transparent_erase<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
+
+  test_non_transparent_erase<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..62c72808b83d1
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class set
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
+
+  test_transparent_extract<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
+
+  test_non_transparent_extract<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
new file mode 100644
index 0000000000000..a59f10e426ecc
--- /dev/null
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_TRANSPARENT_ASSOCIATIVE_H
+#define TEST_TRANSPARENT_ASSOCIATIVE_H
+
+#include "test_macros.h"
+
+#include <cassert>
+
+#if TEST_STD_VER > 23
+
+template <typename T>
+struct StoredType;
+
+template <typename T>
+struct SearchedType {
+  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+
+  operator StoredType<T>() const {
+    ++*conversions_;
+    return StoredType<T>{value_};
+  }
+
+  T get_value() const { return value_; }
+
+  auto operator<=>(const SearchedType<T>&) const = default;
+
+private:
+  T value_;
+  int* conversions_;
+};
+
+template <typename T>
+struct StoredType {
+  StoredType() = default;
+  StoredType(T value) : value_(value) {}
+
+  T get_value() const { return value_; }
+
+  auto operator<=>(const StoredType<T>&) const = default;
+
+private:
+  T value_;
+};
+
+struct transparent_comparator_base {
+  using is_transparent = void;
+
+  template <typename T>
+  bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
+    return lhs.get_value() < rhs.get_value();
+  }
+
+  template <typename T>
+  bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
+    return lhs.get_value() < rhs.get_value();
+  }
+
+  template <typename T>
+  bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
+    return lhs < rhs;
+  }
+};
+
+struct transparent_comparator_final final : public transparent_comparator_base {};
+
+template <class Container>
+void test_transparent_erase(Container c) {
+  int conversions = 0;
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+
+  assert(conversions == 0);
+
+  c.erase(c.begin());
+  c.erase(c.cbegin());
+
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_erase(Container c) {
+  int conversions = 0;
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(conversions == 1);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(conversions == 2);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+  assert(conversions == 3);
+}
+
+template <class Container>
+void test_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 0);
+
+  assert(!c.extract(c.cbegin()).empty());
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(conversions == 1);
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(conversions == 2);
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 3);
+}
+
+#endif // TEST_STD_VER > 23
+
+#endif // TEST_TRANSPARENT_ASSOCIATIVE_H

>From a677aa74846301171d0f9bc06f68700dcb76ad4e Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 06:22:38 +0300
Subject: [PATCH 02/22] Fix test & version stuff

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/ReleaseNotes/22.rst               |  1 +
 libcxx/docs/Status/Cxx23Papers.csv            |  2 +-
 libcxx/include/version                        |  2 +-
 .../map.version.compile.pass.cpp              | 32 ++++++-------------
 .../set.version.compile.pass.cpp              | 32 ++++++-------------
 .../unordered_map.version.compile.pass.cpp    | 32 ++++++-------------
 .../unordered_set.version.compile.pass.cpp    | 32 ++++++-------------
 .../version.version.compile.pass.cpp          | 32 ++++++-------------
 .../support/test_transparent_associative.hpp  |  4 +--
 .../generate_feature_test_macro_components.py |  1 -
 11 files changed, 56 insertions(+), 116 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..644d898772749 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -314,7 +314,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_allocate_at_least``                            ``202302L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_associative_heterogeneous_erasure``            *unimplemented*
+    ``__cpp_lib_associative_heterogeneous_erasure``            ``202110L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_bind_back``                                    ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 9f9927095a61f..5b05666cbb419 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 22.0.0?
 Implemented Papers
 ------------------
 
+- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
 - P2592R3: Hashing support for ``std::chrono`` value classes (`Github <https://llvm.org/PR105358>`__)
 - P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__)
 - P2988R12: ``std::optional<T&>`` (`Github <https://llvm.org/PR148131>`__)
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 739f0ecab37e7..d6fa695026543 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -30,7 +30,7 @@
 "`P1147R1 <https://wg21.link/P1147R1>`__","Printing ``volatile`` Pointers","2021-10 (Virtual)","|Complete|","14","`#105161 <https://github.com/llvm/llvm-project/issues/105161>`__",""
 "`P1272R4 <https://wg21.link/P1272R4>`__","Byteswapping for fun&&nuf","2021-10 (Virtual)","|Complete|","14","`#105163 <https://github.com/llvm/llvm-project/issues/105163>`__",""
 "`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","","`#105164 <https://github.com/llvm/llvm-project/issues/105164>`__",""
-"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","","","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
+"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","22","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
 "`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14","`#105166 <https://github.com/llvm/llvm-project/issues/105166>`__",""
 "`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
 "`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|Complete|","22","`#105169 <https://github.com/llvm/llvm-project/issues/105169>`__",""
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..01faecd6e48b3 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -493,7 +493,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #if _LIBCPP_STD_VER >= 23
 # define __cpp_lib_adaptor_iterator_pair_constructor    202106L
 # define __cpp_lib_allocate_at_least                    202302L
-// # define __cpp_lib_associative_heterogeneous_erasure    202110L
+# define __cpp_lib_associative_heterogeneous_erasure    202110L
 # define __cpp_lib_bind_back                            202202L
 # define __cpp_lib_byteswap                             202110L
 # define __cpp_lib_constexpr_bitset                     202207L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
index 794c2ee9ad086..587c90a02fec4 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
@@ -247,17 +247,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -332,17 +326,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
index d1a0570557246..8531a257b64a6 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
@@ -209,17 +209,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -274,17 +268,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
index 221d8aaebc14b..ba777e5434008 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
@@ -225,17 +225,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -306,17 +300,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
index d1c1335df7c80..b168215ea737b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
@@ -187,17 +187,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -248,17 +242,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L 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 f2d74cc99d864..153219940d1d3 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
@@ -4680,17 +4680,11 @@
 #    error "__cpp_lib_as_const should have the value 201510L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -6373,17 +6367,11 @@
 #    error "__cpp_lib_as_const should have the value 201510L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index a59f10e426ecc..6ec13d29000ea 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -13,7 +13,7 @@
 
 #include <cassert>
 
-#if TEST_STD_VER > 23
+#if TEST_STD_VER >= 23
 
 template <typename T>
 struct StoredType;
@@ -119,6 +119,6 @@ void test_non_transparent_extract(Container c) {
   assert(conversions == 3);
 }
 
-#endif // TEST_STD_VER > 23
+#endif // TEST_STD_VER >= 23
 
 #endif // TEST_TRANSPARENT_ASSOCIATIVE_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..0ad09ba56178c 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -152,7 +152,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_associative_heterogeneous_erasure",
             "values": {"c++23": 202110},
             "headers": ["map", "set", "unordered_map", "unordered_set"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_associative_heterogeneous_insertion",

>From 7c4cd977070e62f1a0809542c40f0b0871f15815 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 17:10:53 +0300
Subject: [PATCH 03/22] Don't uglify nodiscard

---
 libcxx/include/set | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/set b/libcxx/include/set
index 6eeec2ee7b6a0..34fec52cb91e1 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -865,7 +865,7 @@ public:
   template <class _Kp>
     requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
              !is_convertible_v<_Kp &&, const_iterator>)
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
     return __tree_.template __node_handle_extract<node_type>(__kp);
   }
 
@@ -1402,7 +1402,7 @@ public:
   template <class _Kp>
     requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
              !is_convertible_v<_Kp &&, const_iterator>)
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
     return __tree_.template __node_handle_extract<node_type>(__kp);
   }
 

>From a7a1f7a7b1f383122a8cffb415710da632b665a2 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 17:29:05 +0300
Subject: [PATCH 04/22] Overloads for map & multimap

---
 libcxx/include/map                            | 59 +++++++++++++++++++
 .../map.erasure/erase.transparent.pass.cpp    | 28 +++++++++
 .../extract.transparent.pass.cpp              | 27 +++++++++
 .../erase.transparent.pass.cpp                | 29 +++++++++
 .../extract.transparent.pass.cpp              | 27 +++++++++
 5 files changed, 170 insertions(+)
 create mode 100644 libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp

diff --git a/libcxx/include/map b/libcxx/include/map
index 1dda078c4e6c1..2266dbaabca9f 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -147,6 +147,9 @@ public:
 
     constexpr node_type extract(const_iterator position);                                       // C++17
     constexpr node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      constexpr node_type extract(K&& k);                                                       // C++23
+
     constexpr insert_return_type insert(node_type&& nh);                                        // C++17
     constexpr iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
@@ -170,7 +173,10 @@ public:
     constexpr iterator  erase(const_iterator position);
     constexpr iterator  erase(iterator position);                         // C++14
     constexpr size_type erase(const key_type& k);
+    template <typename K>
+      constexpr size_type erase(K&& k);                                   // C++23
     constexpr iterator  erase(const_iterator first, const_iterator last);
+
     constexpr void clear() noexcept;
 
     template<class C2>
@@ -426,13 +432,19 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     iterator insert(node_type&& nh);                                                  // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
     iterator  erase(const_iterator position);
     iterator  erase(iterator position); // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class C2>
@@ -1336,6 +1348,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
     return __tree_.erase(__f.__i_, __l.__i_);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1355,6 +1379,18 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   merge(map<key_type, mapped_type, _Compare2, allocator_type>& __source) {
@@ -2008,6 +2044,17 @@ public:
     return __tree_.erase(__f.__i_, __l.__i_);
   }
 
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
 #  if _LIBCPP_STD_VER >= 17
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
@@ -2025,6 +2072,18 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   merge(multimap<key_type, mapped_type, _Compare2, allocator_type>& __source) {
diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..2fe83c20acf67
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class map
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_final>>(
+      {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_non_transparent_erase<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..0dd4d9afdeb9b
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class map
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_non_transparent_extract<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..7e3f625370d73
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class multimap
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
+      {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_final>>(
+      {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_non_transparent_erase<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..9faa74f69e2b0
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class multimap
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_non_transparent_extract<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}

>From 978ed6c38b5ce529040228c67a9dfcf4ee06271f Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 18:12:03 +0300
Subject: [PATCH 05/22] Overloads for unordered_map & unordered_multimap

---
 libcxx/include/__hash_table                   | 16 +--
 libcxx/include/unordered_map                  | 62 +++++++++++-
 libcxx/include/unordered_set                  |  4 +-
 .../unord.map/erase.transparent.pass.cpp      | 56 +++++++++++
 .../extract.transparent.pass.cpp              | 56 +++++++++++
 .../unord.multimap/erase.transparent.pass.cpp | 56 +++++++++++
 .../extract.transparent.pass.cpp              | 56 +++++++++++
 .../test/support/test_transparent_unordered.h | 99 ++++++++++++++-----
 8 files changed, 366 insertions(+), 39 deletions(-)
 create mode 100644 libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp

diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 0ce4cf3edd6fd..ff3d0f1ab7749 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -896,10 +896,10 @@ public:
   template <class _Table>
   _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_multi(_Table& __source);
 
+  template <class _NodeHandle, class _Key>
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const _Key& __key);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(key_type const& __key);
-  template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const_iterator __it);
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_iterator(const_iterator __it);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
@@ -1648,18 +1648,18 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_insert_unique(const_iter
 }
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
-template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle
-__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(key_type const& __key) {
+template <class _NodeHandle, class _Key>
+_LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(const _Key& __key) {
   iterator __i = find(__key);
   if (__i == end())
     return _NodeHandle();
-  return __node_handle_extract<_NodeHandle>(__i);
+  return __node_handle_extract_iterator<_NodeHandle>(__i);
 }
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(const_iterator __p) {
+_LIBCPP_HIDE_FROM_ABI _NodeHandle
+__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_iterator(const_iterator __p) {
   allocator_type __alloc(__node_alloc());
   return _NodeHandle(remove(__p).release(), __alloc);
 }
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 8cefba01fb5f6..2a9bc016a4e9a 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -139,6 +139,9 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      size_type erase(K&& k);                                                         // C++23
+
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator           insert(const_iterator hint, node_type&& nh);                   // C++17
 
@@ -162,7 +165,10 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -425,13 +431,19 @@ public:
 
     node_type extract(const_iterator position);                // C++17
     node_type extract(const key_type& x);                      // C++17
+    template <typename K>
+      node_type extract(K&& k);                                // C++23
+
     iterator insert(node_type&& nh);                           // C++17
     iterator insert(const_iterator hint, node_type&& nh);      // C++17
 
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -1158,6 +1170,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first.__i_, __last.__i_);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1175,9 +1199,20 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
   }
 
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_map<key_type, mapped_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1896,6 +1931,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first.__i_, __last.__i_);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1913,9 +1960,20 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
   }
 
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_multimap<key_type, mapped_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index d54d59403b038..29bd03e6d1c5a 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -805,7 +805,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract<node_type>(__it);
+    return __table_.template __node_handle_extract_iterator<node_type>(__it);
   }
 
   template <class _H2, class _P2>
@@ -1412,7 +1412,7 @@ public:
     return __table_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __position) {
-    return __table_.template __node_handle_extract<node_type>(__position);
+    return __table_.template __node_handle_extract_iterator<node_type>(__position);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
     return __table_.template __node_handle_extract<node_type>(__key);
diff --git a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..2d0138ef6472f
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_map
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..08a2e05e777bc
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_map
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..b15d915a618d9
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_multimap
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..8eefc2b5f689b
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_multimap
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index e2d02402d808b..f77688b42e194 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -46,7 +46,7 @@ struct transparent_equal_final final : std::equal_to<> {};
 
 template <typename T>
 struct SearchedType {
-  explicit SearchedType(T value, int *counter) : value_(value), conversions_(counter) { }
+  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
 
   // Whenever a conversion is performed, increment the counter to keep track
   // of conversions.
@@ -55,48 +55,40 @@ struct SearchedType {
     return StoredType<T>{value_};
   }
 
-  int get_value() const {
-    return value_;
-  }
+  int get_value() const { return value_; }
 
 private:
   T value_;
-  int *conversions_;
+  int* conversions_;
 };
 
 template <typename T>
 struct StoredType {
   StoredType() = default;
-  StoredType(T value) : value_(value) { }
+  StoredType(T value) : value_(value) {}
 
-  friend bool operator==(StoredType const& lhs, StoredType const& rhs) {
-    return lhs.value_ == rhs.value_;
-  }
+  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
 
   // If we're being passed a SearchedType<T> object, avoid the conversion
   // to T. This allows testing that the transparent operations are correctly
   // forwarding the SearchedType all the way to this comparison by checking
   // that we didn't have a conversion when we search for a SearchedType<T>
   // in a container full of StoredType<T>.
-  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
-    return lhs.value_ == rhs.get_value();
-  }
+  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
 
-  int get_value() const {
-    return value_;
-  }
+  int get_value() const { return value_; }
 
 private:
   T value_;
 };
 
-template<template<class...> class UnorderedSet, class Hash, class Equal>
+template <template <class...> class UnorderedSet, class Hash, class Equal>
 using unord_set_type = UnorderedSet<StoredType<int>, Hash, Equal>;
 
-template<template<class...> class UnorderedMap, class Hash, class Equal>
+template <template <class...> class UnorderedMap, class Hash, class Equal>
 using unord_map_type = UnorderedMap<StoredType<int>, int, Hash, Equal>;
 
-template<class Container>
+template <class Container>
 void test_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -105,7 +97,7 @@ void test_transparent_find(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -116,7 +108,7 @@ void test_non_transparent_find(Container c) {
   assert(conversions == 3);
 }
 
-template<class Container>
+template <class Container>
 void test_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -125,7 +117,7 @@ void test_transparent_count(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -136,7 +128,7 @@ void test_non_transparent_count(Container c) {
   assert(conversions == 3);
 }
 
-template<class Container>
+template <class Container>
 void test_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -145,7 +137,7 @@ void test_transparent_contains(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -156,10 +148,10 @@ void test_non_transparent_contains(Container c) {
   assert(conversions == 3);
 }
 
-template<class Container>
+template <class Container>
 void test_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   iters = c.equal_range(SearchedType<int>(2, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
@@ -168,10 +160,10 @@ void test_transparent_equal_range(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   assert(conversions == 1);
   iters = c.equal_range(SearchedType<int>(2, &conversions));
@@ -182,6 +174,59 @@ void test_non_transparent_equal_range(Container c) {
   assert(conversions == 3);
 }
 
+#  if TEST_STD_VER >= 23
+
+template <class Container>
+void test_transparent_erase(Container c) {
+  int conversions = 0;
+
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+  assert(conversions == 0);
+
+  c.erase(c.begin());
+  c.erase(c.cbegin());
+
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_erase(Container c) {
+  int conversions = 0;
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(conversions == 1);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(conversions == 2);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+  assert(conversions == 3);
+}
+
+template <class Container>
+void test_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 0);
+
+  assert(!c.extract(c.cbegin()).empty());
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(conversions == 1);
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(conversions == 2);
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 3);
+}
+
+#  endif // TEST_STD_VER >= 23
+
 #endif // TEST_STD_VER > 17
 
 #endif // TEST_TRANSPARENT_UNORDERED_H

>From 52e260c1608ab53fedbbb1eaea81ed041b6d6e22 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 19:12:42 +0300
Subject: [PATCH 06/22] Overloads for unordered_set & unordered_multiset

---
 libcxx/include/unordered_map                  |  2 +-
 libcxx/include/unordered_set                  | 58 +++++++++++++++++++
 .../unord.multiset/erase.transparent.pass.cpp | 56 ++++++++++++++++++
 .../extract.transparent.pass.cpp              | 56 ++++++++++++++++++
 .../unord.set/erase.transparent.pass.cpp      | 56 ++++++++++++++++++
 .../unord.set/extract.transparent.pass.cpp    | 56 ++++++++++++++++++
 .../test/support/test_transparent_unordered.h |  4 +-
 7 files changed, 285 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp

diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 2a9bc016a4e9a..ca2173def1f71 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -140,7 +140,7 @@ public:
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
     template <typename K>
-      size_type erase(K&& k);                                                         // C++23
+      node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator           insert(const_iterator hint, node_type&& nh);                   // C++17
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 29bd03e6d1c5a..32a10c1be1e95 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -131,13 +131,19 @@ public:
 
     node_type extract(const_iterator position);                       // C++17
     node_type extract(const key_type& x);                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                       // C++23
+
     insert_return_type insert(node_type&& nh);                        // C++17
     iterator           insert(const_iterator hint, node_type&& nh);   // C++17
 
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -379,13 +385,19 @@ public:
 
     node_type extract(const_iterator position);             // C++17
     node_type extract(const key_type& x);                   // C++17
+    template <typename K>
+      node_type extract(K&& k);                             // C++23
+
     iterator insert(node_type&& nh);                        // C++17
     iterator insert(const_iterator hint, node_type&& nh);   // C++17
 
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -788,6 +800,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first, __last);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -808,6 +832,17 @@ public:
     return __table_.template __node_handle_extract_iterator<node_type>(__it);
   }
 
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_set<key_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1418,6 +1453,17 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
 
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_multiset<key_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1449,6 +1495,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first, __last);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
   _LIBCPP_HIDE_FROM_ABI void swap(unordered_multiset& __u) _NOEXCEPT_(__is_nothrow_swappable_v<__table>) {
diff --git a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..41420d41613b1
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_multiset
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..e094201a8cedd
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_multiset
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..efc55a0ea8964
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_set
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..e84facdaabcb0
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_set
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index f77688b42e194..f2439be716d44 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -55,7 +55,7 @@ struct SearchedType {
     return StoredType<T>{value_};
   }
 
-  int get_value() const { return value_; }
+  T get_value() const { return value_; }
 
 private:
   T value_;
@@ -76,7 +76,7 @@ struct StoredType {
   // in a container full of StoredType<T>.
   friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
 
-  int get_value() const { return value_; }
+  T get_value() const { return value_; }
 
 private:
   T value_;

>From 6bfa1e6710ae0e296c6600542e6be9ef1e832191 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 19:22:02 +0300
Subject: [PATCH 07/22] nodiscard test cases

---
 .../containers/associative/map/map.nodiscard.verify.cpp       | 4 ++++
 .../containers/associative/map/multimap.nodiscard.verify.cpp  | 4 ++++
 .../containers/associative/set/multiset.nodiscard.verify.cpp  | 4 ++++
 .../containers/associative/set/set.nodiscard.verify.cpp       | 4 ++++
 .../libcxx/containers/unord/unord.map/nodiscard.verify.cpp    | 4 ++++
 .../containers/unord/unord.multimap/nodiscard.verify.cpp      | 4 ++++
 .../containers/unord/unord.multiset/nodiscard.verify.cpp      | 4 ++++
 .../libcxx/containers/unord/unord.set/nodiscard.verify.cpp    | 4 ++++
 8 files changed, 32 insertions(+)

diff --git a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
index 20218b50e6c60..7a418502a722b 100644
--- a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
@@ -121,4 +121,8 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
index 99e4fbceabf12..49ac8c6a76064 100644
--- a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
@@ -106,4 +106,8 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
index 889b3cc62d191..a90f69fc96af4 100644
--- a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
@@ -104,4 +104,8 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
index 0453541d1b862..02f2634363679 100644
--- a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
@@ -104,4 +104,8 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
index 0747556905200..34df0f1d44335 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
@@ -81,6 +81,10 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   m[key];            // no-warning
   m[std::move(key)]; // no-warning
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
index cdb47d2b514ab..9ce6bb6a5d46c 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
@@ -81,6 +81,10 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   m.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
index ff62874caf01b..dbb10eaa40dd3 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
@@ -83,6 +83,10 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
index 628c31a93099f..d289d0ebfbfd0 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
@@ -82,6 +82,10 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 

>From 194b4e325a6032b37630a9d13aae80c28768cbf2 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 18:55:54 +0300
Subject: [PATCH 08/22] Refactor nodiscard tests

---
 .../associative/map/map.nodiscard.verify.cpp   | 18 +++++++++---------
 .../map/multimap.nodiscard.verify.cpp          | 18 +++++++++---------
 .../set/multiset.nodiscard.verify.cpp          | 18 +++++++++---------
 .../associative/set/set.nodiscard.verify.cpp   | 18 +++++++++---------
 .../unord/unord.map/nodiscard.verify.cpp       | 18 +++++++++---------
 .../unord/unord.multimap/nodiscard.verify.cpp  | 18 +++++++++---------
 .../unord/unord.multiset/nodiscard.verify.cpp  | 18 +++++++++---------
 .../unord/unord.set/nodiscard.verify.cpp       | 17 +++++++++--------
 8 files changed, 72 insertions(+), 71 deletions(-)

diff --git a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
index 7a418502a722b..c21456965190b 100644
--- a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
@@ -74,19 +74,23 @@ void test() {
   m.key_comp();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.value_comp();    // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
+#if TEST_STD_VER >= 14
+  std::map<int, int, TransparentCompare> tm;
+  const std::map<int, int, TransparentCompare> ctm{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(key);      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.cend()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::map<int, int, TransparentCompare> tm;
-  const std::map<int, int, TransparentCompare> ctm{};
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -121,8 +125,4 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
index 49ac8c6a76064..bb38153b22a8e 100644
--- a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
@@ -59,19 +59,23 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 14
+  std::multimap<int, int, TransparentCompare> tm;
+  const std::multimap<int, int, TransparentCompare> ctm{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(key);      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.cend()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::multimap<int, int, TransparentCompare> tm;
-  const std::multimap<int, int, TransparentCompare> ctm{};
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -106,8 +110,4 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
index a90f69fc96af4..3a762b059e89a 100644
--- a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
@@ -53,10 +53,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 14
+  std::multiset<int, TransparentCompare> ts;
+  const std::multiset<int, TransparentCompare> cts{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   s.extract(key);       // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.extract(s.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   s.get_allocator(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.key_comp();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -65,11 +74,6 @@ void test() {
   s.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cs.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::multiset<int, TransparentCompare> ts;
-  const std::multiset<int, TransparentCompare> cts{};
-
-  TransparentKey tkey;
-
   ts.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -104,8 +108,4 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
index 02f2634363679..acee396bfbb99 100644
--- a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
@@ -53,10 +53,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 14
+  std::set<int, TransparentCompare> ts;
+  const std::set<int, TransparentCompare> cts{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   s.extract(key);       // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.extract(s.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   s.get_allocator(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.key_comp();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -65,11 +74,6 @@ void test() {
   s.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cs.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::set<int, TransparentCompare> ts;
-  const std::set<int, TransparentCompare> cts{};
-
-  TransparentKey tkey;
-
   ts.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -104,8 +108,4 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
index 34df0f1d44335..19e6764c1f064 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
+  const std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(0);         // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
-  const std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -81,10 +85,6 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   m[key];            // no-warning
   m[std::move(key)]; // no-warning
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
index 9ce6bb6a5d46c..511d9e2062d48 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
+  const std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(0);         // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
-  const std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -81,10 +85,6 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   m.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
index dbb10eaa40dd3..1f0e76a412017 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
+  const std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   us.extract(key);        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.extract(us.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   us.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
   us.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cus.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
-  const std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
-
-  TransparentKey tkey;
-
   tus.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctus.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -83,10 +87,6 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
index d289d0ebfbfd0..2a3aca497e8c4 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
+  const std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   us.extract(key);        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.extract(us.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   us.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,10 +66,6 @@ void test() {
   us.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cus.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
-  const std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
-  TransparentKey tkey;
-
   tus.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctus.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -82,10 +87,6 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 

>From d2f5bf01bf1dc7813c2435233e645f7916ee2a4e Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 19:02:05 +0300
Subject: [PATCH 09/22] Drop reformatting

---
 libcxx/include/__tree                         |  2 +-
 .../test/support/test_transparent_unordered.h | 46 +++++++++++--------
 2 files changed, 28 insertions(+), 20 deletions(-)

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index fe54b53ac80b9..b25fcbcba5ff7 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -2089,7 +2089,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
   if (__nh.empty())
     return _InsertReturnType{end(), false, _NodeHandle()};
 
-  __node_pointer __ptr     = __nh.__ptr_;
+  __node_pointer __ptr = __nh.__ptr_;
   auto [__parent, __child] = __find_equal(__ptr->__get_value());
   if (__child != nullptr)
     return _InsertReturnType{
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index f2439be716d44..5aee507191351 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -46,7 +46,7 @@ struct transparent_equal_final final : std::equal_to<> {};
 
 template <typename T>
 struct SearchedType {
-  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+  explicit SearchedType(T value, int *counter) : value_(value), conversions_(counter) { }
 
   // Whenever a conversion is performed, increment the counter to keep track
   // of conversions.
@@ -55,40 +55,48 @@ struct SearchedType {
     return StoredType<T>{value_};
   }
 
-  T get_value() const { return value_; }
+  int get_value() const {
+    return value_;
+  }
 
 private:
   T value_;
-  int* conversions_;
+  int *conversions_;
 };
 
 template <typename T>
 struct StoredType {
   StoredType() = default;
-  StoredType(T value) : value_(value) {}
+  StoredType(T value) : value_(value) { }
 
-  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+  friend bool operator==(StoredType const& lhs, StoredType const& rhs) {
+    return lhs.value_ == rhs.value_;
+  }
 
   // If we're being passed a SearchedType<T> object, avoid the conversion
   // to T. This allows testing that the transparent operations are correctly
   // forwarding the SearchedType all the way to this comparison by checking
   // that we didn't have a conversion when we search for a SearchedType<T>
   // in a container full of StoredType<T>.
-  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
+    return lhs.value_ == rhs.get_value();
+  }
 
-  T get_value() const { return value_; }
+  int get_value() const {
+    return value_;
+  }
 
 private:
   T value_;
 };
 
-template <template <class...> class UnorderedSet, class Hash, class Equal>
+template<template<class...> class UnorderedSet, class Hash, class Equal>
 using unord_set_type = UnorderedSet<StoredType<int>, Hash, Equal>;
 
-template <template <class...> class UnorderedMap, class Hash, class Equal>
+template<template<class...> class UnorderedMap, class Hash, class Equal>
 using unord_map_type = UnorderedMap<StoredType<int>, int, Hash, Equal>;
 
-template <class Container>
+template<class Container>
 void test_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -97,7 +105,7 @@ void test_transparent_find(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -108,7 +116,7 @@ void test_non_transparent_find(Container c) {
   assert(conversions == 3);
 }
 
-template <class Container>
+template<class Container>
 void test_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -117,7 +125,7 @@ void test_transparent_count(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -128,7 +136,7 @@ void test_non_transparent_count(Container c) {
   assert(conversions == 3);
 }
 
-template <class Container>
+template<class Container>
 void test_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -137,7 +145,7 @@ void test_transparent_contains(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -148,10 +156,10 @@ void test_non_transparent_contains(Container c) {
   assert(conversions == 3);
 }
 
-template <class Container>
+template<class Container>
 void test_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   iters = c.equal_range(SearchedType<int>(2, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
@@ -160,10 +168,10 @@ void test_transparent_equal_range(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   assert(conversions == 1);
   iters = c.equal_range(SearchedType<int>(2, &conversions));

>From 801bab5272d1bc04913108ec01b3eba61ed7d4cb Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 19:07:29 +0300
Subject: [PATCH 10/22] typename ==> class

---
 libcxx/include/map           | 2 +-
 libcxx/include/set           | 4 ++--
 libcxx/include/unordered_map | 4 ++--
 libcxx/include/unordered_set | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/map b/libcxx/include/map
index 2266dbaabca9f..342d3443051f5 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -432,7 +432,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     iterator insert(node_type&& nh);                                                  // C++17
diff --git a/libcxx/include/set b/libcxx/include/set
index 34fec52cb91e1..8f19680749ce0 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -125,7 +125,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
@@ -376,7 +376,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     iterator insert(node_type&& nh);                                                  // C++17
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index ca2173def1f71..da31c801d3d0c 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -139,7 +139,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
@@ -431,7 +431,7 @@ public:
 
     node_type extract(const_iterator position);                // C++17
     node_type extract(const key_type& x);                      // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                // C++23
 
     iterator insert(node_type&& nh);                           // C++17
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 32a10c1be1e95..2ae6d02556f0d 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -131,7 +131,7 @@ public:
 
     node_type extract(const_iterator position);                       // C++17
     node_type extract(const key_type& x);                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                       // C++23
 
     insert_return_type insert(node_type&& nh);                        // C++17

>From e57e551a7c1d20d8409d2ff7d4668614debca3a7 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 20:11:11 +0300
Subject: [PATCH 11/22] Better extract testing

---
 .../support/test_transparent_associative.hpp  | 56 ++++++++++++++-----
 .../test/support/test_transparent_unordered.h | 50 ++++++++++++-----
 2 files changed, 77 insertions(+), 29 deletions(-)

diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index 6ec13d29000ea..2448f3cd16083 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -41,6 +41,10 @@ struct StoredType {
   StoredType() = default;
   StoredType(T value) : value_(value) {}
 
+  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+
+  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+
   T get_value() const { return value_; }
 
   auto operator<=>(const StoredType<T>&) const = default;
@@ -72,17 +76,17 @@ struct transparent_comparator_final final : public transparent_comparator_base {
 
 template <class Container>
 void test_transparent_erase(Container c) {
+  static_assert(
+      std::same_as<
+          typename Container::size_type,
+          std::invoke_result_t<decltype(&Container::template erase<SearchedType<int>>), Container, SearchedType<int>>>);
+
   int conversions = 0;
+
   assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
-
   assert(conversions == 0);
-
-  c.erase(c.begin());
-  c.erase(c.cbegin());
-
-  assert(c.empty());
 }
 
 template <class Container>
@@ -96,25 +100,47 @@ void test_non_transparent_erase(Container c) {
   assert(conversions == 3);
 }
 
+template <typename NodeHandle>
+concept node_handle_has_key = requires(NodeHandle nh) {
+  { nh.key() };
+};
+
+template <class T, class Container>
+void test_single_extract(SearchedType<T> key, Container& c) {
+  auto node_handle = c.extract(key);
+
+  assert(!node_handle.empty());
+
+  if constexpr (node_handle_has_key<typename Container::node_type>) {
+    assert(node_handle.key() == key);
+  } else {
+    assert(node_handle.value() == key);
+  }
+}
+
 template <class Container>
 void test_transparent_extract(Container c) {
+  static_assert(std::same_as< typename Container::node_type,
+                              std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
+                                                   Container,
+                                                   SearchedType<int>>>);
+
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 0);
-
-  assert(!c.extract(c.cbegin()).empty());
-  assert(c.empty());
 }
 
 template <class Container>
 void test_non_transparent_extract(Container c) {
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(conversions == 1);
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
-  assert(conversions == 2);
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 3);
 }
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index 5aee507191351..1bc6fb07dee6f 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -186,17 +186,17 @@ void test_non_transparent_equal_range(Container c) {
 
 template <class Container>
 void test_transparent_erase(Container c) {
+  static_assert(
+      std::same_as<
+          typename Container::size_type,
+          std::invoke_result_t<decltype(&Container::template erase<SearchedType<int>>), Container, SearchedType<int>>>);
+
   int conversions = 0;
 
   assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
   assert(conversions == 0);
-
-  c.erase(c.begin());
-  c.erase(c.cbegin());
-
-  assert(c.empty());
 }
 
 template <class Container>
@@ -210,25 +210,47 @@ void test_non_transparent_erase(Container c) {
   assert(conversions == 3);
 }
 
+template <typename NodeHandle>
+concept node_handle_has_key = requires(NodeHandle nh) {
+  { nh.key() };
+};
+
+template <class T, class Container>
+void test_single_extract(SearchedType<T> key, Container& c) {
+  auto node_handle = c.extract(key);
+
+  assert(!node_handle.empty());
+
+  if constexpr (node_handle_has_key<typename Container::node_type>) {
+    assert(node_handle.key() == key);
+  } else {
+    assert(node_handle.value() == key);
+  }
+}
+
 template <class Container>
 void test_transparent_extract(Container c) {
+  static_assert(std::same_as< typename Container::node_type,
+                              std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
+                                                   Container,
+                                                   SearchedType<int>>>);
+
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 0);
-
-  assert(!c.extract(c.cbegin()).empty());
-  assert(c.empty());
 }
 
 template <class Container>
 void test_non_transparent_extract(Container c) {
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(conversions == 1);
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
-  assert(conversions == 2);
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 3);
 }

>From fee83fdd349d4a3166ebb57e439b1281cbe9fde3 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 20:43:59 +0300
Subject: [PATCH 12/22] Include concepts in test

---
 libcxx/test/support/test_transparent_associative.hpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index 2448f3cd16083..dae3acc725162 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -15,6 +15,8 @@
 
 #if TEST_STD_VER >= 23
 
+#  include <concepts>
+
 template <typename T>
 struct StoredType;
 

>From 1f1aba10aa26b42f41d2dec74e816d66b0acb3c5 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 21:04:33 +0300
Subject: [PATCH 13/22] Include type_traits in test

---
 libcxx/test/support/test_transparent_associative.hpp | 1 +
 libcxx/test/support/test_transparent_unordered.h     | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index dae3acc725162..e3cac5e8a51cb 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -16,6 +16,7 @@
 #if TEST_STD_VER >= 23
 
 #  include <concepts>
+#  include <type_traits>
 
 template <typename T>
 struct StoredType;
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index 1bc6fb07dee6f..14faea4d8e5b7 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -16,6 +16,9 @@
 
 #if TEST_STD_VER > 17
 
+#  include <concepts>
+#  include <type_traits>
+
 template <typename T>
 struct StoredType;
 

>From 181d598df66f06db00fbeaededa6c86ed70a9b85 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 10 Jan 2026 02:07:55 +0300
Subject: [PATCH 14/22] Correct header suffix

---
 .../associative/map/map.erasure/erase.transparent.pass.cpp    | 4 ++--
 .../map/map.modifiers/extract.transparent.pass.cpp            | 4 ++--
 .../multimap/multimap.erasure/erase.transparent.pass.cpp      | 4 ++--
 .../multimap/multimap.modifiers/extract.transparent.pass.cpp  | 4 ++--
 .../associative/multiset/extract.transparent.pass.cpp         | 4 ++--
 .../multiset/multiset.erasure/erase.transparent.pass.cpp      | 4 ++--
 .../containers/associative/set/extract.transparent.pass.cpp   | 4 ++--
 .../set/{ => set.erasure}/erase.transparent.pass.cpp          | 4 ++--
 .../std/containers/unord/unord.map/erase.transparent.pass.cpp | 2 +-
 .../unord.map.modifiers/extract.transparent.pass.cpp          | 2 +-
 .../unord/unord.multimap/erase.transparent.pass.cpp           | 2 +-
 .../unord.multimap.modifiers/extract.transparent.pass.cpp     | 2 +-
 .../unord/unord.multiset/erase.transparent.pass.cpp           | 2 +-
 .../unord/unord.multiset/extract.transparent.pass.cpp         | 2 +-
 .../std/containers/unord/unord.set/erase.transparent.pass.cpp | 2 +-
 .../containers/unord/unord.set/extract.transparent.pass.cpp   | 2 +-
 ...sparent_associative.hpp => test_transparent_associative.h} | 0
 17 files changed, 24 insertions(+), 24 deletions(-)
 rename libcxx/test/std/containers/associative/set/{ => set.erasure}/erase.transparent.pass.cpp (91%)
 rename libcxx/test/support/{test_transparent_associative.hpp => test_transparent_associative.h} (100%)

diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
index 2fe83c20acf67..c8cfb77069d62 100644
--- a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class map
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
index 0dd4d9afdeb9b..97cdcda5e519e 100644
--- a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class map
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
index 7e3f625370d73..9a257b82b9388 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multimap
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
index 9faa74f69e2b0..f467d28996ed7 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multimap
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
diff --git a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
index fc04c8d85c267..f05905e7483a1 100644
--- a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multiset
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
index a7252f0aa039c..2669b07e8fd8d 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multiset
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
diff --git a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
index 62c72808b83d1..0b036971e119e 100644
--- a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class set
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
diff --git a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
similarity index 91%
rename from libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
rename to libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
index d2ca1fe599374..87e99da2a9bef 100644
--- a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class set
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
diff --git a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
index 2d0138ef6472f..edbaec04d3329 100644
--- a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_map
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
index 08a2e05e777bc..a14dca09159ac 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_map
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
index b15d915a618d9..e09b66da0ea10 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multimap
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
index 8eefc2b5f689b..4af37297ec0ae 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multimap
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
index 41420d41613b1..3fa33f111b1c8 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multiset
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
index e094201a8cedd..9ec51b55d2278 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multiset
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
index efc55a0ea8964..944faf091d5df 100644
--- a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_set
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
index e84facdaabcb0..06ad1deaf05d1 100644
--- a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_set
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.h
similarity index 100%
rename from libcxx/test/support/test_transparent_associative.hpp
rename to libcxx/test/support/test_transparent_associative.h

>From 1ff485c7129ea014892820d5d55b448f08642660 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 10 Jan 2026 02:15:39 +0300
Subject: [PATCH 15/22] typename ==> class

---
 libcxx/include/map           | 2 +-
 libcxx/include/set           | 4 ++--
 libcxx/include/unordered_map | 4 ++--
 libcxx/include/unordered_set | 6 +++---
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/map b/libcxx/include/map
index 342d3443051f5..12a783df8fc4d 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -441,7 +441,7 @@ public:
     iterator  erase(const_iterator position);
     iterator  erase(iterator position); // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
 
diff --git a/libcxx/include/set b/libcxx/include/set
index 8f19680749ce0..6be683fc2b269 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -134,7 +134,7 @@ public:
     iterator  erase(const_iterator position);
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
 
@@ -386,7 +386,7 @@ public:
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
     iterator  erase(const_iterator first, const_iterator last);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
 
     void clear() noexcept;
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index da31c801d3d0c..f81270797228f 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -165,7 +165,7 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
 
@@ -440,7 +440,7 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
 
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 2ae6d02556f0d..c0636fe437698 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -140,7 +140,7 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
 
@@ -385,7 +385,7 @@ public:
 
     node_type extract(const_iterator position);             // C++17
     node_type extract(const key_type& x);                   // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                             // C++23
 
     iterator insert(node_type&& nh);                        // C++17
@@ -394,7 +394,7 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
 

>From 08e9b952756d97859c72776a90d65ca935f8ed40 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 7 Feb 2026 19:04:30 +0300
Subject: [PATCH 16/22] Rename extract method

---
 libcxx/include/__hash_table  | 6 +++---
 libcxx/include/__tree        | 6 +++---
 libcxx/include/map           | 4 ++--
 libcxx/include/set           | 4 ++--
 libcxx/include/unordered_map | 4 ++--
 libcxx/include/unordered_set | 4 ++--
 6 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index ff3d0f1ab7749..19ecb90928c7f 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -899,7 +899,7 @@ public:
   template <class _NodeHandle, class _Key>
   _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const _Key& __key);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_iterator(const_iterator __it);
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_from_iterator(const_iterator __it);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
@@ -1653,13 +1653,13 @@ _LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__no
   iterator __i = find(__key);
   if (__i == end())
     return _NodeHandle();
-  return __node_handle_extract_iterator<_NodeHandle>(__i);
+  return __node_handle_extract_from_iterator<_NodeHandle>(__i);
 }
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 template <class _NodeHandle>
 _LIBCPP_HIDE_FROM_ABI _NodeHandle
-__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_iterator(const_iterator __p) {
+__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_from_iterator(const_iterator __p) {
   allocator_type __alloc(__node_alloc());
   return _NodeHandle(remove(__p).release(), __alloc);
 }
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index b25fcbcba5ff7..f4e0d0b02369c 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1242,7 +1242,7 @@ public:
   template <class _NodeHandle, class _Key>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const _Key&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract_iterator(const_iterator);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract_from_iterator(const_iterator);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p);
@@ -2126,13 +2126,13 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const& _Key __key) {
   iterator __it = __lower_bound_multi(__key);
   if (__it == end() || __value_comp_(__key, *__it))
     return _NodeHandle();
-  return __node_handle_extract_iterator<_NodeHandle>(__it);
+  return __node_handle_extract_from_iterator<_NodeHandle>(__it);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_iterator(const_iterator __p) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_from_iterator(const_iterator __p) {
   __node_pointer __np = __p.__get_np();
   __remove_node_pointer(__np);
   return _NodeHandle(__np, __alloc());
diff --git a/libcxx/include/map b/libcxx/include/map
index 12a783df8fc4d..ab5c0c9b5840d 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1377,7 +1377,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_iterator_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23
@@ -2070,7 +2070,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/set b/libcxx/include/set
index 6be683fc2b269..8c3dac354e1f3 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -857,7 +857,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
+    return __tree_.template __node_handle_extract_from_iterator<node_type>(__it);
   }
 
 #    if _LIBCPP_STD_VER >= 23
@@ -1394,7 +1394,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
+    return __tree_.template __node_handle_extract_from_iterator<node_type>(__it);
   }
 
 #    if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index f81270797228f..41784958719aa 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1199,7 +1199,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23
@@ -1960,7 +1960,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index c0636fe437698..50be2c8c6222d 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -829,7 +829,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract_iterator<node_type>(__it);
+    return __table_.template __node_handle_extract_from_iterator<node_type>(__it);
   }
 
 #    if _LIBCPP_STD_VER >= 23
@@ -1447,7 +1447,7 @@ public:
     return __table_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __position) {
-    return __table_.template __node_handle_extract_iterator<node_type>(__position);
+    return __table_.template __node_handle_extract_from_iterator<node_type>(__position);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
     return __table_.template __node_handle_extract<node_type>(__key);

>From d104290bd82f56ef462e0660eb8f8c0df3111925 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Tue, 16 Jun 2026 04:22:05 +0300
Subject: [PATCH 17/22] fixes after rebase

---
 libcxx/include/__tree | 2 +-
 libcxx/include/map    | 9 +++++----
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index f4e0d0b02369c..ed7d2f306f544 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -2122,7 +2122,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle, class _Key>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const& _Key __key) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const _Key& __key) {
   iterator __it = __lower_bound_multi(__key);
   if (__it == end() || __value_comp_(__key, *__it))
     return _NodeHandle();
diff --git a/libcxx/include/map b/libcxx/include/map
index ab5c0c9b5840d..71f0284a55cee 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -147,7 +147,7 @@ public:
 
     constexpr node_type extract(const_iterator position);                                       // C++17
     constexpr node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       constexpr node_type extract(K&& k);                                                       // C++23
 
     constexpr insert_return_type insert(node_type&& nh);                                        // C++17
@@ -173,7 +173,7 @@ public:
     constexpr iterator  erase(const_iterator position);
     constexpr iterator  erase(iterator position);                         // C++14
     constexpr size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       constexpr size_type erase(K&& k);                                   // C++23
     constexpr iterator  erase(const_iterator first, const_iterator last);
 
@@ -1049,7 +1049,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26
   map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 #  endif
@@ -1377,7 +1378,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator_from_iterator<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23

>From 01cec7c5af424ca29d15e091e0b9f246f11abd75 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Tue, 16 Jun 2026 04:30:02 +0300
Subject: [PATCH 18/22] release 23

---
 libcxx/docs/ReleaseNotes/22.rst    | 1 -
 libcxx/docs/ReleaseNotes/23.rst    | 1 +
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 libcxx/include/map                 | 3 +--
 4 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 5b05666cbb419..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -38,7 +38,6 @@ What's New in Libc++ 22.0.0?
 Implemented Papers
 ------------------
 
-- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
 - P2592R3: Hashing support for ``std::chrono`` value classes (`Github <https://llvm.org/PR105358>`__)
 - P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__)
 - P2988R12: ``std::optional<T&>`` (`Github <https://llvm.org/PR148131>`__)
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1852351cedc4e..f3206cceb3d87 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,7 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index d6fa695026543..fac8f0061e305 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -30,7 +30,7 @@
 "`P1147R1 <https://wg21.link/P1147R1>`__","Printing ``volatile`` Pointers","2021-10 (Virtual)","|Complete|","14","`#105161 <https://github.com/llvm/llvm-project/issues/105161>`__",""
 "`P1272R4 <https://wg21.link/P1272R4>`__","Byteswapping for fun&&nuf","2021-10 (Virtual)","|Complete|","14","`#105163 <https://github.com/llvm/llvm-project/issues/105163>`__",""
 "`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","","`#105164 <https://github.com/llvm/llvm-project/issues/105164>`__",""
-"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","22","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
+"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","23","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
 "`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14","`#105166 <https://github.com/llvm/llvm-project/issues/105166>`__",""
 "`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
 "`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|Complete|","22","`#105169 <https://github.com/llvm/llvm-project/issues/105169>`__",""
diff --git a/libcxx/include/map b/libcxx/include/map
index 71f0284a55cee..3ede95877bcb7 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1050,8 +1050,7 @@ public:
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26
-  map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 #  endif
 

>From aa629905b458823f7766387124800f91f83f9c40 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 26 Jun 2026 22:28:04 +0300
Subject: [PATCH 19/22] add constexpr tests for map and multimap

---
 libcxx/include/__node_handle                  |  4 +--
 libcxx/include/map                            | 18 +++++-----
 .../map.erasure/erase.transparent.pass.cpp    | 14 ++++++--
 .../extract.transparent.pass.cpp              | 14 ++++++--
 .../erase.transparent.pass.cpp                | 14 ++++++--
 .../extract.transparent.pass.cpp              | 14 ++++++--
 .../support/test_transparent_associative.h    | 35 ++++++++++---------
 7 files changed, 77 insertions(+), 36 deletions(-)

diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index 993283cc926da..eb8e57d2b82f7 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -180,11 +180,11 @@ struct __map_node_handle_specifics {
   using mapped_type = typename _NodeType::__node_value_type::second_type;
 
   // This method is not constexpr as per the standard.
-  _LIBCPP_HIDE_FROM_ABI key_type& key() const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_type& key() const {
     return const_cast<key_type&>(static_cast<_Derived const*>(this)->__ptr_->__get_value().first);
   }
 
-  _LIBCPP_HIDE_FROM_ABI mapped_type& mapped() const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 mapped_type& mapped() const {
     return static_cast<_Derived const*>(this)->__ptr_->__get_value().second;
   }
 };
diff --git a/libcxx/include/map b/libcxx/include/map
index 3ede95877bcb7..9b33ae697a001 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1049,8 +1049,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 #  endif
 
@@ -1855,15 +1855,15 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multimap(__f, __l, key_compare(), __a) {}
 #  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multimap(from_range_t, _Range&& __range, const allocator_type& __a)
       : multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
@@ -1893,8 +1893,8 @@ public:
   }
 
 #    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multimap(initializer_list<value_type> __il, const allocator_type& __a)
       : multimap(__il, key_compare(), __a) {}
 #    endif
 
@@ -2069,7 +2069,7 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
index c8cfb77069d62..fbb632847b38f 100644
--- a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -13,16 +13,26 @@
 // class map
 
 //    template<class K>
-//        size_type erase(K&& k) const;        // C++23
+//        constexpr size_type erase(K&& k) const;        // C++23
 
 #include <map>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
 
   test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_final>>(
       {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
 
   test_non_transparent_erase<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
index 97cdcda5e519e..01544585333cc 100644
--- a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -13,15 +13,25 @@
 // class map
 
 //    template<class K>
-//        node_type extract(K&& k) const;        // C++23
+//        constexpr node_type extract(K&& k) const;        // C++23
 
 #include <map>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
 
   test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
 
   test_non_transparent_extract<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
index 9a257b82b9388..bba57e93c397b 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -13,12 +13,12 @@
 // class multimap
 
 //    template<class K>
-//        size_type erase(K&& k) const;        // C++23
+//        constexpr size_type erase(K&& k) const;        // C++23
 
 #include <map>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
       {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
 
@@ -26,4 +26,14 @@ int main(int, char**) {
       {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
 
   test_non_transparent_erase<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
index f467d28996ed7..0c54072aa94cd 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -13,15 +13,25 @@
 // class multimap
 
 //    template<class K>
-//        node_type extract(K&& k) const;        // C++23
+//        constexpr node_type extract(K&& k) const;        // C++23
 
 #include <map>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
 
   test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
 
   test_non_transparent_extract<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/support/test_transparent_associative.h b/libcxx/test/support/test_transparent_associative.h
index e3cac5e8a51cb..e8acb8d986a88 100644
--- a/libcxx/test/support/test_transparent_associative.h
+++ b/libcxx/test/support/test_transparent_associative.h
@@ -23,16 +23,16 @@ struct StoredType;
 
 template <typename T>
 struct SearchedType {
-  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+  constexpr explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
 
-  operator StoredType<T>() const {
+  constexpr operator StoredType<T>() const {
     ++*conversions_;
     return StoredType<T>{value_};
   }
 
-  T get_value() const { return value_; }
+  constexpr T get_value() const { return value_; }
 
-  auto operator<=>(const SearchedType<T>&) const = default;
+  constexpr auto operator<=>(const SearchedType<T>&) const = default;
 
 private:
   T value_;
@@ -42,15 +42,16 @@ struct SearchedType {
 template <typename T>
 struct StoredType {
   StoredType() = default;
-  StoredType(T value) : value_(value) {}
 
-  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+  constexpr StoredType(T value) : value_(value) {}
 
-  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+  constexpr friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
 
-  T get_value() const { return value_; }
+  constexpr friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
 
-  auto operator<=>(const StoredType<T>&) const = default;
+  constexpr T get_value() const { return value_; }
+
+  constexpr auto operator<=>(const StoredType<T>&) const = default;
 
 private:
   T value_;
@@ -60,17 +61,17 @@ struct transparent_comparator_base {
   using is_transparent = void;
 
   template <typename T>
-  bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
+  constexpr bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
     return lhs.get_value() < rhs.get_value();
   }
 
   template <typename T>
-  bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
+  constexpr bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
     return lhs.get_value() < rhs.get_value();
   }
 
   template <typename T>
-  bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
+  constexpr bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
     return lhs < rhs;
   }
 };
@@ -78,7 +79,7 @@ struct transparent_comparator_base {
 struct transparent_comparator_final final : public transparent_comparator_base {};
 
 template <class Container>
-void test_transparent_erase(Container c) {
+constexpr void test_transparent_erase(Container c) {
   static_assert(
       std::same_as<
           typename Container::size_type,
@@ -93,7 +94,7 @@ void test_transparent_erase(Container c) {
 }
 
 template <class Container>
-void test_non_transparent_erase(Container c) {
+constexpr void test_non_transparent_erase(Container c) {
   int conversions = 0;
   assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
   assert(conversions == 1);
@@ -109,7 +110,7 @@ concept node_handle_has_key = requires(NodeHandle nh) {
 };
 
 template <class T, class Container>
-void test_single_extract(SearchedType<T> key, Container& c) {
+constexpr void test_single_extract(SearchedType<T> key, Container& c) {
   auto node_handle = c.extract(key);
 
   assert(!node_handle.empty());
@@ -122,7 +123,7 @@ void test_single_extract(SearchedType<T> key, Container& c) {
 }
 
 template <class Container>
-void test_transparent_extract(Container c) {
+constexpr void test_transparent_extract(Container c) {
   static_assert(std::same_as< typename Container::node_type,
                               std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
                                                    Container,
@@ -138,7 +139,7 @@ void test_transparent_extract(Container c) {
 }
 
 template <class Container>
-void test_non_transparent_extract(Container c) {
+constexpr void test_non_transparent_extract(Container c) {
   int conversions = 0;
 
   test_single_extract(SearchedType<int>(1, &conversions), c);

>From f688d05c39a6437e2d23a4b9c57cac69b30c0ae8 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 26 Jun 2026 22:37:08 +0300
Subject: [PATCH 20/22] fix node_handle case

---
 libcxx/include/__node_handle                       |  4 ++--
 libcxx/test/support/test_transparent_associative.h | 11 -----------
 2 files changed, 2 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index eb8e57d2b82f7..993283cc926da 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -180,11 +180,11 @@ struct __map_node_handle_specifics {
   using mapped_type = typename _NodeType::__node_value_type::second_type;
 
   // This method is not constexpr as per the standard.
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_type& key() const {
+  _LIBCPP_HIDE_FROM_ABI key_type& key() const {
     return const_cast<key_type&>(static_cast<_Derived const*>(this)->__ptr_->__get_value().first);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 mapped_type& mapped() const {
+  _LIBCPP_HIDE_FROM_ABI mapped_type& mapped() const {
     return static_cast<_Derived const*>(this)->__ptr_->__get_value().second;
   }
 };
diff --git a/libcxx/test/support/test_transparent_associative.h b/libcxx/test/support/test_transparent_associative.h
index e8acb8d986a88..90eb2feae4e8e 100644
--- a/libcxx/test/support/test_transparent_associative.h
+++ b/libcxx/test/support/test_transparent_associative.h
@@ -104,22 +104,11 @@ constexpr void test_non_transparent_erase(Container c) {
   assert(conversions == 3);
 }
 
-template <typename NodeHandle>
-concept node_handle_has_key = requires(NodeHandle nh) {
-  { nh.key() };
-};
-
 template <class T, class Container>
 constexpr void test_single_extract(SearchedType<T> key, Container& c) {
   auto node_handle = c.extract(key);
 
   assert(!node_handle.empty());
-
-  if constexpr (node_handle_has_key<typename Container::node_type>) {
-    assert(node_handle.key() == key);
-  } else {
-    assert(node_handle.value() == key);
-  }
 }
 
 template <class Container>

>From 26f71e9462e6620fcc44287a52669930ab9ad1b4 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 26 Jun 2026 22:54:03 +0300
Subject: [PATCH 21/22] fix format

---
 libcxx/include/map                               | 16 ++++++++--------
 .../test/support/test_transparent_associative.h  |  4 +++-
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/map b/libcxx/include/map
index 9b33ae697a001..e431c79479c10 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1049,8 +1049,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
-  map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 #  endif
 
@@ -1855,15 +1855,15 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
-  multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multimap(__f, __l, key_compare(), __a) {}
 #  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
-  multimap(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(from_range_t, _Range&& __range, const allocator_type& __a)
       : multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
@@ -1893,8 +1893,8 @@ public:
   }
 
 #    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
-  multimap(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(initializer_list<value_type> __il, const allocator_type& __a)
       : multimap(__il, key_compare(), __a) {}
 #    endif
 
diff --git a/libcxx/test/support/test_transparent_associative.h b/libcxx/test/support/test_transparent_associative.h
index 90eb2feae4e8e..85c35e3acff8f 100644
--- a/libcxx/test/support/test_transparent_associative.h
+++ b/libcxx/test/support/test_transparent_associative.h
@@ -47,7 +47,9 @@ struct StoredType {
 
   constexpr friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
 
-  constexpr friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+  constexpr friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
+    return lhs.value_ == rhs.get_value();
+  }
 
   constexpr T get_value() const { return value_; }
 

>From ee4e4715c8da81842d45fa136f6274e4c71231df Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sun, 5 Jul 2026 14:58:35 +0300
Subject: [PATCH 22/22] make set tests constexpr

---
 .../map/map.erasure/erase.transparent.pass.cpp     |  2 +-
 .../map/map.modifiers/extract.transparent.pass.cpp |  2 +-
 .../multimap.erasure/erase.transparent.pass.cpp    |  2 +-
 .../extract.transparent.pass.cpp                   |  2 +-
 .../associative/set/extract.transparent.pass.cpp   | 14 ++++++++++++--
 .../set/set.erasure/erase.transparent.pass.cpp     | 14 ++++++++++++--
 6 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
index fbb632847b38f..89346b83a287c 100644
--- a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class map
 
 //    template<class K>
-//        constexpr size_type erase(K&& k) const;        // C++23
+//        constexpr size_type erase(K&& k) const;        // C++23, constexpr since C++26
 
 #include <map>
 #include "test_transparent_associative.h"
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
index 01544585333cc..5cd31a54ba4d1 100644
--- a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class map
 
 //    template<class K>
-//        constexpr node_type extract(K&& k) const;        // C++23
+//        constexpr node_type extract(K&& k) const;        // C++23, constexpr since C++26
 
 #include <map>
 #include "test_transparent_associative.h"
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
index bba57e93c397b..2f9a47327b74c 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class multimap
 
 //    template<class K>
-//        constexpr size_type erase(K&& k) const;        // C++23
+//        constexpr size_type erase(K&& k) const;        // C++23, constexpr since C++26
 
 #include <map>
 #include "test_transparent_associative.h"
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
index 0c54072aa94cd..71a0cdaf9e1a7 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class multimap
 
 //    template<class K>
-//        constexpr node_type extract(K&& k) const;        // C++23
+//        constexpr node_type extract(K&& k) const;        // C++23, constexpr since C++26
 
 #include <map>
 #include "test_transparent_associative.h"
diff --git a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
index 0b036971e119e..50f13833cbe3e 100644
--- a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
@@ -13,15 +13,25 @@
 // class set
 
 //    template<class K>
-//        node_type extract(K&& k) const;        // C++23
+//        constexpr node_type extract(K&& k) const;        // C++23, constexpr since C++26
 
 #include <set>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_extract<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
 
   test_transparent_extract<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
 
   test_non_transparent_extract<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
index 87e99da2a9bef..0df59d1eeb132 100644
--- a/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
@@ -13,15 +13,25 @@
 // class set
 
 //    template<class K>
-//        size_type erase(K&& k) const;        // C++23
+//        constexpr size_type erase(K&& k) const;        // C++23, constexpr since C++26
 
 #include <set>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_erase<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
 
   test_transparent_erase<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
 
   test_non_transparent_erase<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }


From libcxx-commits at lists.llvm.org  Sun Jul  5 05:25:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 05:25:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][math][c++17] P0226R1 -
 Mathematical Special Functions: infra + `std::assoc_laguerre` (PR #205649)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205649@github.com>
Message-ID: <6a4a4d41.bb985344.17fb3b.b136@mx.google.com>


https://github.com/PaulXiCao ready_for_review https://github.com/llvm/llvm-project/pull/205649

From libcxx-commits at lists.llvm.org  Sun Jul  5 06:12:49 2026
From: libcxx-commits at lists.llvm.org (Simon Pilgrim via libcxx-commits)
Date: Sun, 05 Jul 2026 06:12:49 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libc] [libcxx] [clang][X86] Emit AVX
 level mismatch psABI warnings on function definitions (PR #199091)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/199091@github.com>
Message-ID: <6a4a5851.2020f6c2.1fdf61.4e61@mx.google.com>


================
@@ -1571,6 +1575,65 @@ static bool checkAVXParam(DiagnosticsEngine &Diag, ASTContext &Ctx,
   return false;
 }
 
+void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
----------------
RKSimon wrote:

+1 for a new tracking issue

https://github.com/llvm/llvm-project/pull/199091

From libcxx-commits at lists.llvm.org  Sun Jul  5 06:12:49 2026
From: libcxx-commits at lists.llvm.org (Simon Pilgrim via libcxx-commits)
Date: Sun, 05 Jul 2026 06:12:49 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libc] [libcxx] [clang][X86] Emit AVX
 level mismatch psABI warnings on function definitions (PR #199091)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/199091@github.com>
Message-ID: <6a4a5851.481afadf.134314.c73c@mx.google.com>


https://github.com/RKSimon edited https://github.com/llvm/llvm-project/pull/199091

From libcxx-commits at lists.llvm.org  Sun Jul  5 06:12:49 2026
From: libcxx-commits at lists.llvm.org (Simon Pilgrim via libcxx-commits)
Date: Sun, 05 Jul 2026 06:12:49 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libc] [libcxx] [clang][X86] Emit AVX
 level mismatch psABI warnings on function definitions (PR #199091)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/199091@github.com>
Message-ID: <6a4a5851.af6ba3ed.2c2f30.4e4d@mx.google.com>


https://github.com/RKSimon commented:

Please can you confirm the CI failures are irrelevant (do they go when you merge against trunk latest)?

https://github.com/llvm/llvm-project/pull/199091

From libcxx-commits at lists.llvm.org  Sun Jul  5 06:18:59 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 05 Jul 2026 06:18:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Implementation of adjacent_difference on
 top of a binary transform (PR #207585)
Message-ID: <llvm.org/llvm/llvm-project/pull/207585@github.com>


https://github.com/mikekazakov created https://github.com/llvm/llvm-project/pull/207585

None

>From 7158ac56340f536d5fe47e9c5449bd8aaf994285 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 5 Jul 2026 14:17:28 +0100
Subject: [PATCH] Implementation of adjacent_difference on top of a binary
 transform

---
 libcxx/include/__numeric/pstl.h               |  39 ++++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  35 +++-
 .../pstl.iterator-requirements.verify.cpp     |  11 ++
 .../algorithms/pstl.nodiscard.verify.cpp      |   5 +
 .../pstl.exception_handling.pass.cpp          |  16 +-
 .../pstl.adjacent_difference.pass.cpp         | 171 +++++++++++++++++
 .../pstl.adjacent_difference_op.pass.cpp      | 181 ++++++++++++++++++
 8 files changed, 461 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp

diff --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h
index ecbc9f5348333..0b85cfb8c2327 100644
--- a/libcxx/include/__numeric/pstl.h
+++ b/libcxx/include/__numeric/pstl.h
@@ -165,6 +165,45 @@ template <class _ExecutionPolicy,
       std::move(__transform));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first1), std::move(__last1), std::move(__first2), minus{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryOperation,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _BinaryOperation __op) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__op));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a52e6db954d0c..e4fd78850ec37 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -303,6 +303,13 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_difference;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+// optional<_ForwardIterator2>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index be90715af13b5..05018c23c37e3 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -87,11 +87,12 @@ namespace __pstl {
 //
 // transform and transform_binary family
 // -------------------------------------
-// - replace_copy_if
-// - replace_copy
-// - move
+// - adjacent_difference
 // - copy
 // - copy_n
+// - move
+// - replace_copy
+// - replace_copy_if
 // - rotate_copy
 //
 
@@ -529,6 +530,34 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _BinaryOperation&& __op) const noexcept {
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    if (__first1 == __last1)
+      return __first2; // edge case: empty input range, just return the output iterator
+    *__first2 = *__first1;
+    ++__first2;
+    _ForwardIterator1 __first_left = std::next(__first1);
+    if (__first_left == __last1)
+      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+    // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
+    return _TransformBinary()(
+        __policy,
+        std::move(__first_left),
+        std::move(__last1),
+        std::move(__first1),
+        std::move(__first2),
+        std::forward<_BinaryOperation>(__op));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e5bd7e764c59b..82615719fba7d 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -190,4 +190,15 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::transform_reduce(
         pol, non_fwd, non_fwd, val, func, func); // expected-error@*:* {{static assertion failed: transform_reduce}}
   }
+
+  {
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+  }
 }
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index b6554cef6f32a..9f486c320067f 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <algorithm>
 #include <execution>
 #include <iterator>
+#include <numeric>
 
 void test() {
   int a[]    = {1};
@@ -52,4 +53,8 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
 }
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a3db159289393..16f4db5c97f1c 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -85,7 +85,7 @@ int main(int, char**) {
       auto last1       = util::throw_on_move_iterator(std::end(a), tokens[1].active() ? 1 : -1);
       auto first2      = util::throw_on_move_iterator(std::begin(b), tokens[2].active() ? 1 : -1);
       auto last2       = util::throw_on_move_iterator(std::end(b), tokens[3].active() ? 1 : -1);
-      auto dest        = util::throw_on_move_iterator(std::end(storage), tokens[4].active() ? 1 : -1);
+      auto dest        = util::throw_on_move_iterator(std::begin(storage), tokens[4].active() ? 1 : -1);
       auto maybe_throw = [](ThrowToken const& token, auto f) {
         return [&token, f](auto... args) {
           if (token.active())
@@ -343,6 +343,20 @@ int main(int, char**) {
           (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction);
         });
       }
+
+      {
+        auto op = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
+
+        // adjacent_difference(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+
+        // adjacent_difference(first, last, dest, op)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest), op);
+        });
+      }
     }
   });
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
new file mode 100644
index 0000000000000..bb98004c1c62a
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+
+  friend X operator-(const X& x, const X& y) { return X(x.i_ - y.i_); }
+
+  friend class Y;
+};
+
+class Y {
+  int i_;
+
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 20};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, -5, -4, -3, -2};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r =
+            std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        assert(std::all_of(std::begin(ib), std::begin(ib) + i, [](int x) { return x == 1; }));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(7)};
+      Y ir[3] = {Y(1), Y(4), Y(2)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
new file mode 100644
index 0000000000000..26d65f9a77c10
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryOperation>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2,
+//                                        BinaryOperation op);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+  friend class Y;
+  friend struct Op;
+};
+
+class Y {
+  int i_;
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+// ensures that the predicate is not equivalent to std::minus
+struct Op {
+  int operator()(int l, int r) const { return l * 2 - r; }
+  X operator()(const X& l, const X& r) const { return X(l.i_ * 2 - r.i_); }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 50};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, 5, 2, 0, -1};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r = std::adjacent_difference(
+            policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)), Op{});
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j)
+          assert(ib[j] == (int)(j == 0 ? 1 : j + 2));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(8)};
+      Y ir[3] = {Y(1), Y(9), Y(11)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Sun Jul  5 06:19:08 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 05 Jul 2026 06:19:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Implementation of adjacent_difference on
 top of a binary transform (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4a59cc.30759430.1908d3.e70a@mx.google.com>


https://github.com/mikekazakov converted_to_draft https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Sun Jul  5 06:19:34 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 06:19:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Implementation of adjacent_difference on
 top of a binary transform (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4a59e6.491d90f5.1f0d58.53b3@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Michael G. Kazakov (mikekazakov)

<details>
<summary>Changes</summary>



---

Patch is 21.48 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/207585.diff


8 Files Affected:

- (modified) libcxx/include/__numeric/pstl.h (+39) 
- (modified) libcxx/include/__pstl/backend_fwd.h (+7) 
- (modified) libcxx/include/__pstl/backends/default.h (+32-3) 
- (modified) libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp (+11) 
- (modified) libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp (+5) 
- (modified) libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp (+15-1) 
- (added) libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp (+171) 
- (added) libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp (+181) 


``````````diff
diff --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h
index ecbc9f5348333..0b85cfb8c2327 100644
--- a/libcxx/include/__numeric/pstl.h
+++ b/libcxx/include/__numeric/pstl.h
@@ -165,6 +165,45 @@ template <class _ExecutionPolicy,
       std::move(__transform));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first1), std::move(__last1), std::move(__first2), minus{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryOperation,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _BinaryOperation __op) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__op));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a52e6db954d0c..e4fd78850ec37 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -303,6 +303,13 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_difference;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+// optional<_ForwardIterator2>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index be90715af13b5..05018c23c37e3 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -87,11 +87,12 @@ namespace __pstl {
 //
 // transform and transform_binary family
 // -------------------------------------
-// - replace_copy_if
-// - replace_copy
-// - move
+// - adjacent_difference
 // - copy
 // - copy_n
+// - move
+// - replace_copy
+// - replace_copy_if
 // - rotate_copy
 //
 
@@ -529,6 +530,34 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _BinaryOperation&& __op) const noexcept {
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    if (__first1 == __last1)
+      return __first2; // edge case: empty input range, just return the output iterator
+    *__first2 = *__first1;
+    ++__first2;
+    _ForwardIterator1 __first_left = std::next(__first1);
+    if (__first_left == __last1)
+      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+    // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
+    return _TransformBinary()(
+        __policy,
+        std::move(__first_left),
+        std::move(__last1),
+        std::move(__first1),
+        std::move(__first2),
+        std::forward<_BinaryOperation>(__op));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e5bd7e764c59b..82615719fba7d 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -190,4 +190,15 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::transform_reduce(
         pol, non_fwd, non_fwd, val, func, func); // expected-error@*:* {{static assertion failed: transform_reduce}}
   }
+
+  {
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+  }
 }
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index b6554cef6f32a..9f486c320067f 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <algorithm>
 #include <execution>
 #include <iterator>
+#include <numeric>
 
 void test() {
   int a[]    = {1};
@@ -52,4 +53,8 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
 }
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a3db159289393..16f4db5c97f1c 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -85,7 +85,7 @@ int main(int, char**) {
       auto last1       = util::throw_on_move_iterator(std::end(a), tokens[1].active() ? 1 : -1);
       auto first2      = util::throw_on_move_iterator(std::begin(b), tokens[2].active() ? 1 : -1);
       auto last2       = util::throw_on_move_iterator(std::end(b), tokens[3].active() ? 1 : -1);
-      auto dest        = util::throw_on_move_iterator(std::end(storage), tokens[4].active() ? 1 : -1);
+      auto dest        = util::throw_on_move_iterator(std::begin(storage), tokens[4].active() ? 1 : -1);
       auto maybe_throw = [](ThrowToken const& token, auto f) {
         return [&token, f](auto... args) {
           if (token.active())
@@ -343,6 +343,20 @@ int main(int, char**) {
           (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction);
         });
       }
+
+      {
+        auto op = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
+
+        // adjacent_difference(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+
+        // adjacent_difference(first, last, dest, op)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest), op);
+        });
+      }
     }
   });
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
new file mode 100644
index 0000000000000..bb98004c1c62a
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+
+  friend X operator-(const X& x, const X& y) { return X(x.i_ - y.i_); }
+
+  friend class Y;
+};
+
+class Y {
+  int i_;
+
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 20};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, -5, -4, -3, -2};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r =
+            std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        assert(std::all_of(std::begin(ib), std::begin(ib) + i, [](int x) { return x == 1; }));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(7)};
+      Y ir[3] = {Y(1), Y(4), Y(2)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
new file mode 100644
index 0000000000000..26d65f9a77c10
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryOperation>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2,
+//                                        BinaryOperation op);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+  friend class Y;
+  friend struct Op;
+};
+
+class Y {
+  int i_;
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+// ensures that the predicate is not equivalent to std::minus
+struct Op {
+  int operator()(int l, int r) const { return l * 2 - r; }
+  X operator()(const X& l, const X& r) const { return X(l.i_ * 2 - r.i_); }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 50};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, 5, 2, 0, -1};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r = std::adjacent_difference(
+            policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)), Op{});
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Sun Jul  5 06:25:58 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 06:25:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a5b66.f06f4994.168928.ed95@mx.google.com>


https://github.com/emmett2020 edited https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 06:28:26 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 05 Jul 2026 06:28:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Implementation of adjacent_difference on
 top of a binary transform (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4a5bfa.da8bafd3.2e1688.b1b6@mx.google.com>


https://github.com/mikekazakov edited https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Sun Jul  5 06:30:22 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 05 Jul 2026 06:30:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 adjacent_difference on top of a binary transform (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4a5c6e.e55dc341.29c9b8.e3bb@mx.google.com>


https://github.com/mikekazakov edited https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Sun Jul  5 06:30:43 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 05 Jul 2026 06:30:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4a5c83.331483e7.5bc1e.e781@mx.google.com>


https://github.com/mikekazakov edited https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Sun Jul  5 06:31:05 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 05 Jul 2026 06:31:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4a5c99.eb24c6ea.1f2e26.e3cb@mx.google.com>


https://github.com/mikekazakov edited https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Sun Jul  5 06:31:31 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 05 Jul 2026 06:31:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4a5cb3.bf38ae28.358429.7935@mx.google.com>


https://github.com/mikekazakov ready_for_review https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Sun Jul  5 06:34:47 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 05 Jul 2026 06:34:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4a5d77.e55dc341.29c9b8.8754@mx.google.com>


https://github.com/mikekazakov edited https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Sun Jul  5 07:39:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 07:39:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] d832bfe - [libc++] Mark LWG4098 as
	resolved (#206295)
Message-ID: <6a4a6c84.a87eb5dc.1d432d.1a02@mx.google.com>


Author: Lucas Mellone
Date: 2026-07-05T22:38:56+08:00
New Revision: d832bfe3c99ac8251258b5a965d4418db503e38c

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

LOG: [libc++] Mark LWG4098 as resolved (#206295)

Already implemented and tested in the scope of the full implementation
for `adjacent_view` (1e15dbe311eb08462e7a68fcb8b5850632e24aff).

Closes #105353

Added: 
    

Modified: 
    libcxx/docs/Status/Cxx26Issues.csv

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 30042d4465d93..23adfde5bd4d7 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -72,7 +72,7 @@
 "`LWG4082 <https://wg21.link/LWG4082>`__","``views::concat(r)`` is well-formed when ``r`` is an ``output_range``","2024-06 (St. Louis)","|Complete|","23","`#105349 <https://github.com/llvm/llvm-project/issues/105349>`__",""
 "`LWG4083 <https://wg21.link/LWG4083>`__","``views::as_rvalue`` should reject non-input ranges","2024-06 (St. Louis)","|Complete|","22","`#105351 <https://github.com/llvm/llvm-project/issues/105351>`__",""
 "`LWG4096 <https://wg21.link/LWG4096>`__","``views::iota(views::iota(0))`` should be rejected","2024-06 (St. Louis)","|Complete|","22","`#105352 <https://github.com/llvm/llvm-project/issues/105352>`__",""
-"`LWG4098 <https://wg21.link/LWG4098>`__","``views::adjacent<0>`` should reject non-forward ranges","2024-06 (St. Louis)","","","`#105353 <https://github.com/llvm/llvm-project/issues/105353>`__",""
+"`LWG4098 <https://wg21.link/LWG4098>`__","``views::adjacent<0>`` should reject non-forward ranges","2024-06 (St. Louis)","|Complete|","22","`#105353 <https://github.com/llvm/llvm-project/issues/105353>`__",""
 "`LWG4105 <https://wg21.link/LWG4105>`__","``ranges::ends_with``\`s Returns misses 
diff erence casting","2024-06 (St. Louis)","","","`#105354 <https://github.com/llvm/llvm-project/issues/105354>`__",""
 "`LWG4106 <https://wg21.link/LWG4106>`__","``basic_format_args`` should not be default-constructible","2024-06 (St. Louis)","|Complete|","19","`#105355 <https://github.com/llvm/llvm-project/issues/105355>`__",""
 "","","","","","",""


        

From libcxx-commits at lists.llvm.org  Sun Jul  5 07:39:03 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 05 Jul 2026 07:39:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG4098 as resolved (PR
	#206295)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206295@github.com>
Message-ID: <6a4a6c87.fdc477d6.1a5bda.1fbc@mx.google.com>


https://github.com/frederick-vs-ja closed https://github.com/llvm/llvm-project/pull/206295

From libcxx-commits at lists.llvm.org  Sun Jul  5 07:55:19 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 05 Jul 2026 07:55:19 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_=5Blibcxxabi=5D_Re-ena?=
 =?utf-8?q?ble_=60thread=5Flocal=5Fdestruction=5Forder=2Epass=2Ecpp?=
 =?utf-8?b?4oCOYCBvbiBGcmVlQlNELiAoUFIgIzE4NjEyOSk=?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186129@github.com>
Message-ID: <6a4a7057.e9d484b9.11ad58.7e5b@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/186129

From libcxx-commits at lists.llvm.org  Sun Jul  5 07:55:33 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 05 Jul 2026 07:55:33 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_=5Blibcxxabi=5D_Re-ena?=
 =?utf-8?q?ble_=60thread=5Flocal=5Fdestruction=5Forder=2Epass=2Ecpp?=
 =?utf-8?b?4oCOYCBvbiBGcmVlQlNELiAoUFIgIzE4NjEyOSk=?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186129@github.com>
Message-ID: <6a4a7065.14d9b9dc.140829.b020@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/186129

From libcxx-commits at lists.llvm.org  Sun Jul  5 07:58:22 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 05 Jul 2026 07:58:22 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_=5Blibcxxabi=5D_Re-ena?=
 =?utf-8?q?ble_=60thread=5Flocal=5Fdestruction=5Forder=2Epass=2Ecpp?=
 =?utf-8?b?4oCOYCBvbiBGcmVlQlNELiAoUFIgIzE4NjEyOSk=?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186129@github.com>
Message-ID: <6a4a710e.13be6df8.3d8759.2030@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.

LGTM. But perhaps an approval from a libc++abi maintainer would be desired. CC @ldionne.

I slightly modified the title (again) and the PR description. Please double check the changes.

https://github.com/llvm/llvm-project/pull/186129

From libcxx-commits at lists.llvm.org  Sun Jul  5 08:04:16 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 08:04:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a7270.92d2873c.38c951.9d74@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/207558

>From f20f20873cde0b8800e0cc5d690f3ee7cb6a4b27 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Sun, 5 Jul 2026 13:38:58 +0800
Subject: [PATCH] [libc++][chrono] Disallow cv-qualified representations in
 duration

Implement LWG 4481 by rejecting chrono::duration specializations whose
Rep type is const- or volatile-qualified.

Fixes #189846
---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__chrono/duration.h            |  4 +++
 libcxx/include/chrono                         |  2 ++
 .../time/time.duration/duration.verify.cpp    | 27 +++++++++++++++++++
 4 files changed, 34 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/time/time.duration/duration.verify.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 23adfde5bd4d7..5de35c96d4c0a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -301,7 +301,7 @@
 "`LWG4477 <https://wg21.link/LWG4477>`__","Placement ``operator delete`` should be constexpr","2026-03 (Croydon)","|Complete|","23","`#189843 <https://github.com/llvm/llvm-project/issues/189843>`__",""
 "`LWG4478 <https://wg21.link/LWG4478>`__","``meta::has_identifier`` is not specified for annotations","2026-03 (Croydon)","","","`#189844 <https://github.com/llvm/llvm-project/issues/189844>`__",""
 "`LWG4480 <https://wg21.link/LWG4480>`__","``<stdatomic.h>`` should provide ``ATOMIC_CHAR8_T_LOCK_FREE``","2026-03 (Croydon)","","","`#189845 <https://github.com/llvm/llvm-project/issues/189845>`__",""
-"`LWG4481 <https://wg21.link/LWG4481>`__","Disallow ``chrono::duration<const T, P>``","2026-03 (Croydon)","","","`#189846 <https://github.com/llvm/llvm-project/issues/189846>`__",""
+"`LWG4481 <https://wg21.link/LWG4481>`__","Disallow ``chrono::duration<const T, P>``","2026-03 (Croydon)","|Complete|","23","`#189846 <https://github.com/llvm/llvm-project/issues/189846>`__",""
 "`LWG4483 <https://wg21.link/LWG4483>`__","Multidimensional arrays are not supported by ``meta::reflect_constant_array`` and related functions","2026-03 (Croydon)","","","`#189847 <https://github.com/llvm/llvm-project/issues/189847>`__",""
 "`LWG4485 <https://wg21.link/LWG4485>`__","Move specification for ``task::stop_token_type``","2026-03 (Croydon)","","","`#189848 <https://github.com/llvm/llvm-project/issues/189848>`__",""
 "`LWG4486 <https://wg21.link/LWG4486>`__","``integral-constant-like`` and ``constexpr-wrapper-like`` exposition-only concept duplication","2026-03 (Croydon)","","","`#189849 <https://github.com/llvm/llvm-project/issues/189849>`__",""
diff --git a/libcxx/include/__chrono/duration.h b/libcxx/include/__chrono/duration.h
index 92efd82f72145..ebff8fd5263fe 100644
--- a/libcxx/include/__chrono/duration.h
+++ b/libcxx/include/__chrono/duration.h
@@ -17,8 +17,10 @@
 #include <__functional/hash.h>
 #include <__type_traits/common_type.h>
 #include <__type_traits/enable_if.h>
+#include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>
 #include <__type_traits/is_floating_point.h>
+#include <__type_traits/is_volatile.h>
 #include <limits>
 #include <ratio>
 
@@ -164,6 +166,8 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
 
 template <class _Rep, class _Period>
 class duration {
+  static_assert(!is_const<_Rep>::value, "A duration representation can not be const-qualified");
+  static_assert(!is_volatile<_Rep>::value, "A duration representation can not be volatile-qualified");
   static_assert(!__is_duration_v<_Rep>, "A duration representation can not be a duration");
   static_assert(__is_ratio_v<_Period>, "Second template parameter of duration must be a std::ratio");
   static_assert(_Period::num > 0, "duration period must be positive");
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index 39822d5e74f79..2f844b8b9a870 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -46,6 +46,8 @@ public:
 template <class Rep, class Period = ratio<1>>
 class duration
 {
+    static_assert(!is_const<Rep>::value, "A duration representation can not be const-qualified");
+    static_assert(!is_volatile<Rep>::value, "A duration representation can not be volatile-qualified");
     static_assert(!__is_duration<Rep>::value, "A duration representation can not be a duration");
     static_assert(__is_ratio<Period>::value, "Second template parameter of duration must be a std::ratio");
     static_assert(Period::num > 0, "duration period must be positive");
diff --git a/libcxx/test/std/time/time.duration/duration.verify.cpp b/libcxx/test/std/time/time.duration/duration.verify.cpp
new file mode 100644
index 0000000000000..3a740e3996ac4
--- /dev/null
+++ b/libcxx/test/std/time/time.duration/duration.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <chrono>
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+// LWG 4481
+// A program that instantiates duration with a cv-qualified Rep is ill-formed.
+
+#include <chrono>
+
+void test() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation can not be const-qualified}}
+  (void)sizeof(std::chrono::duration<const int>);
+
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation can not be volatile-qualified}}
+  (void)sizeof(std::chrono::duration<volatile int>);
+
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation can not be const-qualified}}
+  (void)sizeof(std::chrono::duration<const volatile int>);
+}


From libcxx-commits at lists.llvm.org  Sun Jul  5 08:06:07 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 08:06:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a72df.8b87e04f.199b71.2442@mx.google.com>


================
@@ -22,6 +22,11 @@
 #include <limits>
 #include <ratio>
 
+#if _LIBCPP_STD_VER >= 26
+#  include <__type_traits/is_same.h>
+#  include <__type_traits/remove_cv.h>
+#endif
+
----------------
emmett2020 wrote:

Thanks for your review. I've changed the related headers.

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 08:11:29 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 08:11:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a7421.449bfb44.233a36.2c41@mx.google.com>


================
@@ -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
----------------
emmett2020 wrote:

Thanks. I'm not familiar with the process. I know that DRs should retroactively apply to older versions, but I didn't see this issue marked as a DR from the beginning. After carefully reading the related paper, I removed the C++26 macro guards and refined the test cases too.

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 08:12:12 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 08:12:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a744c.510f576d.185953.5cfa@mx.google.com>


================
@@ -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
+
+// <chrono>
+
+// LWG 4481
+// A program that instantiates duration with a cv-qualified Rep is ill-formed.
+
+#include <chrono>
+
+void test_const() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<const int>);
+}
+
+void test_volatile() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<volatile int>);
+}
+
+void test_const_volatile() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be cv-qualified}}
+  (void)sizeof(std::chrono::duration<const volatile int>);
+}
----------------
emmett2020 wrote:

Thank you! I moved them into one function.

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 08:18:12 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 08:18:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a75b4.ff037943.3bf0e2.aa3c@mx.google.com>


================
@@ -164,6 +169,9 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
 
 template <class _Rep, class _Period>
 class duration {
+#if _LIBCPP_STD_VER >= 26
+  static_assert(is_same_v<_Rep, remove_cv_t<_Rep>>, "A duration representation cannot be cv-qualified");
+#endif
----------------
emmett2020 wrote:

Many thanks. I've updated the code.

- The fix now applies to C++03, C++11, and later versions.
- For the assertion message, I used "can not" instead of "cannot" to match the style of the is_duration_v check — they're similar.
- I've also updated Cxx26Issues.csv. Thanks again for your careful review.

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 08:20:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 08:20:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a7629.ff037943.3bf0e2.e2f6@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/207558

>From f20f20873cde0b8800e0cc5d690f3ee7cb6a4b27 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Sun, 5 Jul 2026 13:38:58 +0800
Subject: [PATCH] [libc++][chrono] Disallow cv-qualified representations in
 duration

Implement LWG 4481 by rejecting chrono::duration specializations whose
Rep type is const- or volatile-qualified.

Fixes #189846
---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__chrono/duration.h            |  4 +++
 libcxx/include/chrono                         |  2 ++
 .../time/time.duration/duration.verify.cpp    | 27 +++++++++++++++++++
 4 files changed, 34 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/time/time.duration/duration.verify.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 23adfde5bd4d7..5de35c96d4c0a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -301,7 +301,7 @@
 "`LWG4477 <https://wg21.link/LWG4477>`__","Placement ``operator delete`` should be constexpr","2026-03 (Croydon)","|Complete|","23","`#189843 <https://github.com/llvm/llvm-project/issues/189843>`__",""
 "`LWG4478 <https://wg21.link/LWG4478>`__","``meta::has_identifier`` is not specified for annotations","2026-03 (Croydon)","","","`#189844 <https://github.com/llvm/llvm-project/issues/189844>`__",""
 "`LWG4480 <https://wg21.link/LWG4480>`__","``<stdatomic.h>`` should provide ``ATOMIC_CHAR8_T_LOCK_FREE``","2026-03 (Croydon)","","","`#189845 <https://github.com/llvm/llvm-project/issues/189845>`__",""
-"`LWG4481 <https://wg21.link/LWG4481>`__","Disallow ``chrono::duration<const T, P>``","2026-03 (Croydon)","","","`#189846 <https://github.com/llvm/llvm-project/issues/189846>`__",""
+"`LWG4481 <https://wg21.link/LWG4481>`__","Disallow ``chrono::duration<const T, P>``","2026-03 (Croydon)","|Complete|","23","`#189846 <https://github.com/llvm/llvm-project/issues/189846>`__",""
 "`LWG4483 <https://wg21.link/LWG4483>`__","Multidimensional arrays are not supported by ``meta::reflect_constant_array`` and related functions","2026-03 (Croydon)","","","`#189847 <https://github.com/llvm/llvm-project/issues/189847>`__",""
 "`LWG4485 <https://wg21.link/LWG4485>`__","Move specification for ``task::stop_token_type``","2026-03 (Croydon)","","","`#189848 <https://github.com/llvm/llvm-project/issues/189848>`__",""
 "`LWG4486 <https://wg21.link/LWG4486>`__","``integral-constant-like`` and ``constexpr-wrapper-like`` exposition-only concept duplication","2026-03 (Croydon)","","","`#189849 <https://github.com/llvm/llvm-project/issues/189849>`__",""
diff --git a/libcxx/include/__chrono/duration.h b/libcxx/include/__chrono/duration.h
index 92efd82f72145..ebff8fd5263fe 100644
--- a/libcxx/include/__chrono/duration.h
+++ b/libcxx/include/__chrono/duration.h
@@ -17,8 +17,10 @@
 #include <__functional/hash.h>
 #include <__type_traits/common_type.h>
 #include <__type_traits/enable_if.h>
+#include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>
 #include <__type_traits/is_floating_point.h>
+#include <__type_traits/is_volatile.h>
 #include <limits>
 #include <ratio>
 
@@ -164,6 +166,8 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
 
 template <class _Rep, class _Period>
 class duration {
+  static_assert(!is_const<_Rep>::value, "A duration representation can not be const-qualified");
+  static_assert(!is_volatile<_Rep>::value, "A duration representation can not be volatile-qualified");
   static_assert(!__is_duration_v<_Rep>, "A duration representation can not be a duration");
   static_assert(__is_ratio_v<_Period>, "Second template parameter of duration must be a std::ratio");
   static_assert(_Period::num > 0, "duration period must be positive");
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index 39822d5e74f79..2f844b8b9a870 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -46,6 +46,8 @@ public:
 template <class Rep, class Period = ratio<1>>
 class duration
 {
+    static_assert(!is_const<Rep>::value, "A duration representation can not be const-qualified");
+    static_assert(!is_volatile<Rep>::value, "A duration representation can not be volatile-qualified");
     static_assert(!__is_duration<Rep>::value, "A duration representation can not be a duration");
     static_assert(__is_ratio<Period>::value, "Second template parameter of duration must be a std::ratio");
     static_assert(Period::num > 0, "duration period must be positive");
diff --git a/libcxx/test/std/time/time.duration/duration.verify.cpp b/libcxx/test/std/time/time.duration/duration.verify.cpp
new file mode 100644
index 0000000000000..3a740e3996ac4
--- /dev/null
+++ b/libcxx/test/std/time/time.duration/duration.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <chrono>
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+// LWG 4481
+// A program that instantiates duration with a cv-qualified Rep is ill-formed.
+
+#include <chrono>
+
+void test() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation can not be const-qualified}}
+  (void)sizeof(std::chrono::duration<const int>);
+
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation can not be volatile-qualified}}
+  (void)sizeof(std::chrono::duration<volatile int>);
+
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation can not be const-qualified}}
+  (void)sizeof(std::chrono::duration<const volatile int>);
+}


From libcxx-commits at lists.llvm.org  Sun Jul  5 08:35:16 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 08:35:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a79b4.8e75e0cb.2e0812.3512@mx.google.com>


https://github.com/emmett2020 edited https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 08:37:16 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 08:37:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] #199778: locale_base_api.h (PR
	#207569)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207569@github.com>
Message-ID: <6a4a7a2c.d1496011.3d49cb.c07c@mx.google.com>


trcrsired wrote:

@EugeneZelenko 

https://github.com/llvm/llvm-project/pull/207569

From libcxx-commits at lists.llvm.org  Sun Jul  5 08:38:27 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 08:38:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a7a73.14d9b9dc.140829.5183@mx.google.com>


emmett2020 wrote:

> Hello @emmett2020 👋
> 
> Thank you for submitting a Pull Request (PR) to the LLVM Project. Since this is your first PR, here are a few useful links covering our main contribution policies and review practices.
> 
> * All contributions to LLVM must follow our [LLVM AI Tool Use Policy](https://llvm.org/docs/AIToolPolicy.html). In particular, if you used AI while working on this PR, remember to add a note to the PR description.
> * The [LLVM Code-Review Policy and Practices](https://llvm.org/docs/CodeReview.html) document contains practical information about the PR process, including how patches are reviewed and accepted, and who can review a PR.
> * Our [LLVM Developer Policy](https://llvm.org/docs/DeveloperPolicy.html) describes our expectations for code quality, commit summaries and contains notes on our CI system.
> 
> Please reply to this message to confirm that you have read these policies, especially the LLVM AI Tool Use Policy, and that any AI tool usage has been noted in the PR description.
> 
> ### Frequently asked questions
> **How do I add reviewers?**
> 
> This PR will be automatically labeled, and the relevant teams will be notified. For some parts of the project, reviewers may also be added automatically.
> 
> You can also add reviewers manually using the **Reviewers** section on this page. If you cannot use that section, it is probably because you do not have write permissions for the repository. In that case, you can request a review by tagging reviewers in a comment using `@` followed by their GitHub username.
> 
> **What if there are no comments?**
> 
> If you have not received any comments on your PR after a week, you can request a review by pinging the PR with a comment such as “Ping”. The common courtesy ping rate is once a week. Please remember that you are asking for volunteer time from other developers.
> 
> **Are any special GitHub settings required to contribute to LLVM?**
> 
> We only require contributors to have a public email address associated with their GitHub commits, see this [section](https://llvm.org/docs/DeveloperPolicy.html#email-addresses) of LLVM Developer Policy for details.
> 
> If you have questions, feel free to leave a comment on this PR, or ask on [LLVM Discord](https://discord.com/invite/xS7Z362) or [LLVM Discourse](https://discourse.llvm.org/).
> 
> Thank you, The LLVM Community

Thank you bot :) I've read all documents above. 

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 10:32:44 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 10:32:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Clean up windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a4a953c.dd2b4bd3.13e95e.0690@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/207577

From libcxx-commits at lists.llvm.org  Sun Jul  5 10:34:34 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 10:34:34 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_=5Blibcxxabi=5D_Re-ena?=
 =?utf-8?q?ble_=60thread=5Flocal=5Fdestruction=5Forder=2Epass=2Ecpp?=
 =?utf-8?b?4oCOYCBvbiBGcmVlQlNELiAoUFIgIzE4NjEyOSk=?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186129@github.com>
Message-ID: <6a4a95aa.eb165e5c.113c2a.932c@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/186129

From libcxx-commits at lists.llvm.org  Sun Jul  5 10:35:02 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 10:35:02 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_=5Blibcxxabi=5D_Re-ena?=
 =?utf-8?q?ble_=60thread=5Flocal=5Fdestruction=5Forder=2Epass=2Ecpp?=
 =?utf-8?b?4oCOYCBvbiBGcmVlQlNEIChQUiAjMTg2MTI5KQ==?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186129@github.com>
Message-ID: <6a4a95c6.2020f6c2.1fdf61.f427@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/186129

From libcxx-commits at lists.llvm.org  Sun Jul  5 10:35:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 10:35:23 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_4a02e11_-_=5Blibcxxabi?=
 =?utf-8?q?=5D_Re-enable_=60thread=5Flocal=5Fdestruction=5Forder=2Epass=2E?=
 =?utf-8?b?Y3Bw4oCOYCBvbiBGcmVlQlNEICgjMTg2MTI5KQ==?=
Message-ID: <6a4a95db.510f576d.185953.6d0b@mx.google.com>


Author: aokblast
Date: 2026-07-05T19:35:18+02:00
New Revision: 4a02e116e58d162335cf18b4f1f28c7d34d6d3ac

URL: https://github.com/llvm/llvm-project/commit/4a02e116e58d162335cf18b4f1f28c7d34d6d3ac
DIFF: https://github.com/llvm/llvm-project/commit/4a02e116e58d162335cf18b4f1f28c7d34d6d3ac.diff

LOG: [libcxxabi] Re-enable `thread_local_destruction_order.pass.cpp‎` on FreeBSD (#186129)

The underlying bug has been fixed in https://reviews.freebsd.org/D55826.

Added: 
    

Modified: 
    libcxxabi/test/thread_local_destruction_order.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxxabi/test/thread_local_destruction_order.pass.cpp b/libcxxabi/test/thread_local_destruction_order.pass.cpp
index c7e7717d6f04f..775b417832a1f 100644
--- a/libcxxabi/test/thread_local_destruction_order.pass.cpp
+++ b/libcxxabi/test/thread_local_destruction_order.pass.cpp
@@ -9,8 +9,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-threads
 
-// XFAIL: LIBCXX-FREEBSD-FIXME
-
 // TODO: This test does start working with newer updates of the mingw-w64
 // toolchain, when it includes the following commit:
 // https://github.com/mingw-w64/mingw-w64/commit/71eddccd746c56d9cde28bb5620d027d49259de9


        

From libcxx-commits at lists.llvm.org  Sun Jul  5 10:35:26 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 10:35:26 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxxabi=5D_=5Blibcxxabi=5D_Re-ena?=
 =?utf-8?q?ble_=60thread=5Flocal=5Fdestruction=5Forder=2Epass=2Ecpp?=
 =?utf-8?b?4oCOYCBvbiBGcmVlQlNEIChQUiAjMTg2MTI5KQ==?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186129@github.com>
Message-ID: <6a4a95de.492e23e4.e71ca.297d@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/186129

From libcxx-commits at lists.llvm.org  Sun Jul  5 10:36:43 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 10:36:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] a68147f - [libc++] Use std::to_chars to
 format thread::id and canonicalize the representation across platforms
 (#181624)
Message-ID: <6a4a962b.f8b79514.3b441.d1e8@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-05T19:36:39+02:00
New Revision: a68147f0f05652d50937ab178f7ac21069562028

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

LOG: [libc++] Use std::to_chars to format thread::id and canonicalize the representation across platforms (#181624)

This makes printing `thread::id` faster, since we avoid a bunch of
boilerplate code that isn't optimized away. It also avoids including
`<sstream>` and `<__locale>`, cutting the time to parse `<thread>` in
half.

This also changes the output when printing the id on a platform that
uses a pointer as the underlying type. I don't think that's a problem,
since the thread id isn't in any way stable. It also makes the output
consistent across all platforms we support.

Added: 
    libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp

Modified: 
    libcxx/docs/ReleaseNotes/23.rst
    libcxx/include/__thread/formatter.h
    libcxx/include/__thread/thread.h
    libcxx/include/future
    libcxx/include/module.modulemap.in
    libcxx/include/thread
    libcxx/test/libcxx/transitive_includes/cxx26.csv
    libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
    libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
    libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp

Removed: 
    libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
    libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1852351cedc4e..312841c1de36a 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -92,6 +92,9 @@ Potentially breaking changes
   including the correct header. To ease the transition ``_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23`` can be defined, which
   includes the removed headers again. This macro will be removed in LLVM 24.
 
+- The output of ``ostream << std::thread::id`` and ``std::format("...", std::thread:id)`` changes on some platforms from
+  hexadecimal to decimal. This is done to have consistent formatting across all platforms libc++ supports.
+
 Announcements About Future Releases
 -----------------------------------
 

diff  --git a/libcxx/include/__thread/formatter.h b/libcxx/include/__thread/formatter.h
index 826607d47b469..9682798e1df41 100644
--- a/libcxx/include/__thread/formatter.h
+++ b/libcxx/include/__thread/formatter.h
@@ -51,20 +51,13 @@ struct formatter<__thread_id, _CharT> {
     // On Linux systems pthread_t is an unsigned long long.
     // On Apple systems pthread_t is a pointer type.
     //
-    // Note the output should match what the stream operator does. Since
-    // the ostream operator has been shipped years before this formatter
-    // was added to the Standard, this formatter does what the stream
-    // operator does. This may require platform specific changes.
+    // Note the output should match what the stream operator does.
 
     using _Tp = decltype(__get_underlying_id(__id));
     using _Cp = conditional_t<integral<_Tp>, _Tp, conditional_t<is_pointer_v<_Tp>, uintptr_t, void>>;
     static_assert(!is_same_v<_Cp, void>, "unsupported thread::id type, please file a bug report");
 
     __format_spec::__parsed_specifications<_CharT> __specs = __parser_.__get_parsed_std_specifications(__ctx);
-    if constexpr (is_pointer_v<_Tp>) {
-      __specs.__std_.__alternate_form_ = true;
-      __specs.__std_.__type_           = __format_spec::__type::__hexadecimal_lower_case;
-    }
     return __formatter::__format_integer(reinterpret_cast<_Cp>(__get_underlying_id(__id)), __ctx, __specs);
   }
 

diff  --git a/libcxx/include/__thread/thread.h b/libcxx/include/__thread/thread.h
index ee7a65212f31d..24f62bfe5899e 100644
--- a/libcxx/include/__thread/thread.h
+++ b/libcxx/include/__thread/thread.h
@@ -11,12 +11,13 @@
 #define _LIBCPP___THREAD_THREAD_H
 
 #include <__assert>
+#include <__charconv/to_chars_integral.h>
 #include <__condition_variable/condition_variable.h>
 #include <__config>
 #include <__exception/terminate.h>
 #include <__functional/hash.h>
 #include <__functional/unary_function.h>
-#include <__locale>
+#include <__fwd/ostream.h>
 #include <__memory/addressof.h>
 #include <__memory/unique_ptr.h>
 #include <__mutex/mutex.h>
@@ -27,16 +28,15 @@
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_constructible.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/exchange.h>
 #include <__utility/forward.h>
+#include <cstdint>
+#include <limits>
 #include <tuple>
 
-#if _LIBCPP_HAS_LOCALIZATION
-#  include <sstream>
-#endif
-
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
@@ -146,13 +146,19 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, __thread_id __id) {
   //
   // Since various flags in the output stream can affect how the
   // thread id is represented (e.g. numpunct or showbase), we
-  // use a temporary stream instead and just output the thread
-  // id representation as a string.
+  // use `to_chars` directly and output the id as a string.
+
+  static_assert(is_pointer<__libcpp_thread_id>::value || is_integral<__libcpp_thread_id>::value);
+
+  using __int_type = __conditional_t<is_pointer<__libcpp_thread_id>::value, uintptr_t, __libcpp_thread_id>;
 
-  basic_ostringstream<_CharT, _Traits> __sstr;
-  __sstr.imbue(locale::classic());
-  __sstr << __id.__id_;
-  return __os << __sstr.str();
+  static const size_t __buffer_size = numeric_limits<__int_type>::digits10 + 2;
+  char __buffer[__buffer_size];
+  auto __ret =
+      std::__to_chars_integral(__buffer, __buffer + __buffer_size - 1, reinterpret_cast<__int_type>(__id.__id_), 10);
+  _LIBCPP_ASSERT_INTERNAL(__ret.__ec == std::errc(), "to_chars failed!");
+  *__ret.__ptr = '\0';
+  return __os << __buffer;
 }
 #  endif // _LIBCPP_HAS_LOCALIZATION
 

diff  --git a/libcxx/include/future b/libcxx/include/future
index 30ceb97785811..498fda99fdafd 100644
--- a/libcxx/include/future
+++ b/libcxx/include/future
@@ -2078,6 +2078,11 @@ _LIBCPP_POP_MACROS
 #    include <system_error>
 #    include <thread>
 #  endif
+
+#  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 23
+#    include <sstream>
+#  endif
+
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 
 #endif // _LIBCPP_FUTURE

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3efceae54599d..2d4ba2b5876a7 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -948,7 +948,10 @@ module std {
       header "__charconv/to_chars_integral.h"
       export std.charconv.to_chars_result
     }
-    module to_chars_result            { header "__charconv/to_chars_result.h" }
+    module to_chars_result            {
+      header "__charconv/to_chars_result.h"
+      export std.system_error.errc
+    }
     module traits                     { header "__charconv/traits.h" }
 
     header "charconv"

diff  --git a/libcxx/include/thread b/libcxx/include/thread
index fa50d048330c6..d79db3dcd7791 100644
--- a/libcxx/include/thread
+++ b/libcxx/include/thread
@@ -131,6 +131,7 @@ void sleep_for(const chrono::duration<Rep, Period>& rel_time);
 
 #  if defined(_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23) && _LIBCPP_STD_VER <= 23
 #    include <atomic>
+#    include <sstream>
 #  endif
 
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)

diff  --git a/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp
new file mode 100644
index 0000000000000..6719ec6b783e6
--- /dev/null
+++ b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-threads
+
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// <thread>
+
+// template<class charT>
+// struct formatter<thread::id, charT>;
+
+// template<class... Args>
+//   string format(format_string<Args...> fmt, Args&&... args);
+// template<class... Args>
+//   wstring format(wformat_string<Args...> fmt, Args&&... args);
+
+#include <cassert>
+#include <format>
+#include <thread>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "format.functions.common.h"
+#include "test_format_string.h"
+#include "test_macros.h"
+
+template <class CharT, class TestFunction>
+void format_tests(TestFunction check) {
+  // Note the output of std::thread::id is unspecified. The output text is the
+  // same as the stream operator. Since that format is already released this
+  // test follows the practice on existing systems.
+  std::thread::id input{};
+
+  /***** Test the type specific part *****/
+  check(SV("0"), SV("{}"), input);
+  check(SV("0^42"), SV("{}^42"), input);
+  check(SV("0^42"), SV("{:}^42"), input);
+
+  // *** align-fill & width ***
+  check(SV("    0"), SV("{:5}"), input);
+  check(SV("0****"), SV("{:*<5}"), input);
+  check(SV("__0__"), SV("{:_^5}"), input);
+  check(SV("::::0"), SV("{::>5}"), input); // This is not a range, so : is allowed as fill character.
+
+  check(SV("    0"), SV("{:{}}"), input, 5);
+  check(SV("0****"), SV("{:*<{}}"), input, 5);
+  check(SV("__0__"), SV("{:_^{}}"), input, 5);
+  check(SV("####0"), SV("{:#>{}}"), input, 5);
+}
+
+auto test = []<class CharT, class... Args>(
+                std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
+  {
+    std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
+  {
+    std::basic_string<CharT> out = std::vformat(fmt.get(), std::make_format_args<context_t<CharT>>(args...));
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
+};
+
+int main(int, char**) {
+  format_tests<char>(test);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  format_tests<wchar_t>(test);
+#endif
+
+  { // Check that we print the ID correctly (i.e. library-internal round-tripping works)
+    auto thread_id = std::this_thread::get_id();
+    auto result    = std::format("{}", thread_id);
+    auto num       = std::stoull(result);
+    using int_t =
+        std::conditional_t<std::is_pointer<std::__libcpp_thread_id>::value, uintptr_t, std::__libcpp_thread_id>;
+    assert(reinterpret_cast<int_t>(__get_underlying_id(std::this_thread::get_id())) == static_cast<int_t>(num));
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index cf1154a251f23..6abceca182a95 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -423,32 +423,23 @@ functional tuple
 functional typeinfo
 functional unordered_map
 functional version
-future bitset
 future cctype
 future cerrno
 future climits
-future clocale
 future compare
-future cstddef
 future cstdint
 future cstdio
-future cstdlib
 future cstring
 future ctime
 future cwchar
 future cwctype
 future initializer_list
-future ios
 future iosfwd
-future istream
 future limits
 future ratio
-future sstream
 future stdexcept
-future streambuf
 future string
 future string_view
-future text_encoding
 future tuple
 future typeinfo
 future version
@@ -1043,7 +1034,6 @@ text_encoding stdexcept
 text_encoding string_view
 text_encoding version
 thread array
-thread bitset
 thread cctype
 thread cerrno
 thread climits
@@ -1058,14 +1048,10 @@ thread ctime
 thread cwchar
 thread cwctype
 thread initializer_list
-thread ios
 thread iosfwd
-thread istream
 thread limits
 thread ratio
-thread sstream
 thread stdexcept
-thread streambuf
 thread string
 thread string_view
 thread text_encoding

diff  --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
deleted file mode 100644
index 5618836d89e80..0000000000000
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-threads
-
-// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
-
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
-// <thread>
-
-// template<class charT>
-// struct formatter<thread::id, charT>;
-
-// template<class... Args>
-//   string format(format_string<Args...> fmt, Args&&... args);
-// template<class... Args>
-//   wstring format(wformat_string<Args...> fmt, Args&&... args);
-
-#include <format>
-#include <cassert>
-
-#include "assert_macros.h"
-#include "concat_macros.h"
-#include "format.functions.tests.h"
-#include "test_format_string.h"
-#include "test_macros.h"
-
-auto test = []<class CharT, class... Args>(
-                std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
-  std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
-  TEST_REQUIRE(out == expected,
-               TEST_WRITE_CONCATENATED(
-                   "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
-};
-
-auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, Args&&...) {
-  // After P2216 most exceptions thrown by std::format become ill-formed.
-  // Therefore this tests does nothing.
-};
-
-int main(int, char**) {
-  format_tests<char>(test, test_exception);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  format_tests<wchar_t>(test, test_exception);
-#endif
-
-  return 0;
-}

diff  --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
deleted file mode 100644
index f55f0e2af8cb2..0000000000000
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
+++ /dev/null
@@ -1,83 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
-#define TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
-
-#include <thread>
-
-#include "format.functions.common.h"
-#include "test_macros.h"
-
-template <class CharT, class TestFunction, class ExceptionTest>
-void format_tests(TestFunction check, ExceptionTest check_exception) {
-  // Note the output of std::thread::id is unspecified. The output text is the
-  // same as the stream operator. Since that format is already released this
-  // test follows the practice on existing systems.
-  std::thread::id input{};
-
-  /***** Test the type specific part *****/
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
-  check(SV("0"), SV("{}"), input);
-  check(SV("0^42"), SV("{}^42"), input);
-  check(SV("0^42"), SV("{:}^42"), input);
-
-  // *** align-fill & width ***
-  check(SV("    0"), SV("{:5}"), input);
-  check(SV("0****"), SV("{:*<5}"), input);
-  check(SV("__0__"), SV("{:_^5}"), input);
-  check(SV("::::0"), SV("{::>5}"), input); // This is not a range, so : is allowed as fill character.
-
-  check(SV("    0"), SV("{:{}}"), input, 5);
-  check(SV("0****"), SV("{:*<{}}"), input, 5);
-  check(SV("__0__"), SV("{:_^{}}"), input, 5);
-  check(SV("####0"), SV("{:#>{}}"), input, 5);
-#else  // !defined(__APPLE__) && !defined(__FreeBSD__)
-  check(SV("0x0"), SV("{}"), input);
-  check(SV("0x0^42"), SV("{}^42"), input);
-  check(SV("0x0^42"), SV("{:}^42"), input);
-
-  // *** align-fill & width ***
-  check(SV("    0x0"), SV("{:7}"), input);
-  check(SV("0x0****"), SV("{:*<7}"), input);
-  check(SV("__0x0__"), SV("{:_^7}"), input);
-  check(SV("::::0x0"), SV("{::>7}"), input); // This is not a range, so : is allowed as fill character.
-
-  check(SV("    0x0"), SV("{:{}}"), input, 7);
-  check(SV("0x0****"), SV("{:*<{}}"), input, 7);
-  check(SV("__0x0__"), SV("{:_^{}}"), input, 7);
-  check(SV("####0x0"), SV("{:#>{}}"), input, 7);
-#endif // !defined(__APPLE__) && !defined(__FreeBSD__)
-
-  /***** Test the type generic part *****/
-  check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
-  check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
-
-  // *** sign ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:-}"), input);
-  check_exception("The replacement field misses a terminating '}'", SV("{:+}"), input);
-  check_exception("The replacement field misses a terminating '}'", SV("{: }"), input);
-
-  // *** alternate form ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:#}"), input);
-
-  // *** zero-padding ***
-  check_exception("The width option should not have a leading zero", SV("{:0}"), input);
-
-  // *** precision ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:.}"), input);
-
-  // *** locale-specific form ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:L}"), input);
-
-  // *** type ***
-  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>(""))
-    check_exception("The replacement field misses a terminating '}'", fmt, input);
-}
-
-#endif // TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H

diff  --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
index 8555ebd742616..ffe1330fa9c7a 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
@@ -22,21 +22,47 @@
 // string vformat(string_view fmt, format_args args);
 // wstring vformat(wstring_view fmt, wformat_args args);
 
-#include <format>
 #include <cassert>
+#include <format>
+#include <thread>
 
 #include "assert_macros.h"
 #include "concat_macros.h"
-#include "format.functions.tests.h"
+#include "format.functions.common.h"
 #include "test_macros.h"
 
-auto test = []<class CharT, class... Args>(
-                std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) {
-  std::basic_string<CharT> out = std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
-  TEST_REQUIRE(out == expected,
-               TEST_WRITE_CONCATENATED(
-                   "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
-};
+template <class CharT, class ExceptionTest>
+void format_tests(ExceptionTest check_exception) {
+  // Note the output of std::thread::id is unspecified. The output text is the
+  // same as the stream operator. Since that format is already released this
+  // test follows the practice on existing systems.
+  std::thread::id input{};
+
+  /***** Test the type generic part *****/
+  check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
+  check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
+
+  // *** sign ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:-}"), input);
+  check_exception("The replacement field misses a terminating '}'", SV("{:+}"), input);
+  check_exception("The replacement field misses a terminating '}'", SV("{: }"), input);
+
+  // *** alternate form ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:#}"), input);
+
+  // *** zero-padding ***
+  check_exception("The width option should not have a leading zero", SV("{:0}"), input);
+
+  // *** precision ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:L}"), input);
+
+  // *** type ***
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>(""))
+    check_exception("The replacement field misses a terminating '}'", fmt, input);
+}
 
 auto test_exception =
     []<class CharT, class... Args>(
@@ -55,10 +81,10 @@ auto test_exception =
     };
 
 int main(int, char**) {
-  format_tests<char>(test, test_exception);
+  format_tests<char>(test_exception);
 
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  format_tests<wchar_t>(test, test_exception);
+  format_tests<wchar_t>(test_exception);
 #endif
 
   return 0;

diff  --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
index 39a257592809d..d3fc6e4b016d6 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
@@ -34,9 +34,8 @@
 
 #define SV(S) MAKE_STRING_VIEW(CharT, S)
 
-template <class StringViewT>
-void test_format(StringViewT expected, std::thread::id arg) {
-  using CharT      = typename StringViewT::value_type;
+template <class CharT>
+std::basic_string<CharT> test_format(std::thread::id arg) {
   using String     = std::basic_string<CharT>;
   using OutIt      = std::back_insert_iterator<String>;
   using FormatCtxT = std::basic_format_context<OutIt, CharT>;
@@ -47,16 +46,15 @@ void test_format(StringViewT expected, std::thread::id arg) {
   OutIt out             = std::back_inserter(result);
   FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>(arg));
   formatter.format(arg, format_ctx);
-  assert(result == expected);
+  assert(!result.empty());
+  return result;
 }
 
 template <class CharT>
 void test_fmt() {
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
-  test_format(SV("0"), std::thread::id());
-#else
-  test_format(SV("0x0"), std::thread::id());
-#endif
+  auto def  = test_format<CharT>(std::thread::id());
+  auto self = test_format<CharT>(std::this_thread::get_id());
+  assert(def != self);
 }
 
 void test() {

diff  --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
index 97e797f9a8fbb..ae4df5f5f55a5 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
@@ -27,6 +27,7 @@
 #include <format>
 #include <locale>
 #include <sstream>
+#include <string>
 #include <thread>
 
 #include "make_string.h"


        

From libcxx-commits at lists.llvm.org  Sun Jul  5 10:36:45 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 10:36:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use std::to_chars to format
 thread::id and canonicalize the representation across platforms (PR
 #181624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/181624@github.com>
Message-ID: <6a4a962d.331483e7.5bc1e.63ea@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/181624

From libcxx-commits at lists.llvm.org  Sun Jul  5 10:40:48 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 10:40:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 925f4f2 - [libc++] Add an ABI flag to
 optimize mersenne_twister_engine (#206423)
Message-ID: <6a4a9720.92ea0e2a.21f97.2dea@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-05T19:40:43+02:00
New Revision: 925f4f27b724446572c644a23d20a8a8ecd5dd90

URL: https://github.com/llvm/llvm-project/commit/925f4f27b724446572c644a23d20a8a8ecd5dd90
DIFF: https://github.com/llvm/llvm-project/commit/925f4f27b724446572c644a23d20a8a8ecd5dd90.diff

LOG: [libc++] Add an ABI flag to optimize mersenne_twister_engine (#206423)

Instead of updating the status array every time `operator()` is called,
we can instead update it once every time the entire array has been read.
That allows the compiler to vectorize the updating code, improving
performance.

Apple M4:
```
Benchmark                                     old      new    Difference    % Difference
----------------------------------------  -------  -------  ------------  --------------
std::mt19937::operator()                     1.39     0.65         -0.74         -53.26%
std::mt19937::operator()_(into_array)      172.92    80.69        -92.23         -53.34%
std::mt19937_64::operator()                  1.46     0.99         -0.47         -31.99%
std::mt19937_64::operator()_(into_array)   187.31   108.10        -79.21         -42.29%
```

Fixes #197221

Added: 
    

Modified: 
    libcxx/include/__configuration/abi.h
    libcxx/include/__random/mersenne_twister_engine.h
    libcxx/test/benchmarks/random.bench.cpp
    libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
    libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
    libcxx/utils/libcxx/test/features/libcxx_macros.py

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 9231271d7e692..8a34ebcc7bb90 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -78,6 +78,7 @@
 #  define _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION
 #  define _LIBCPP_ABI_TRIVIALLY_COPYABLE_BIT_ITERATOR
 #  define _LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE
+#  define _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
 
 #elif _LIBCPP_ABI_VERSION == 1
 // Feature macros for disabling pre ABI v1 features. All of these options

diff  --git a/libcxx/include/__random/mersenne_twister_engine.h b/libcxx/include/__random/mersenne_twister_engine.h
index ce82b3387dbd2..8e49b3b267aa8 100644
--- a/libcxx/include/__random/mersenne_twister_engine.h
+++ b/libcxx/include/__random/mersenne_twister_engine.h
@@ -181,6 +181,9 @@ class mersenne_twister_engine {
     for (size_t __i = 1; __i < __n; ++__i)
       __x_[__i] = (__f * (__x_[__i - 1] ^ __rshift<__w - 2>(__x_[__i - 1])) + __i) & _Max;
     __i_ = 0;
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    __update_all_states();
+#endif
   }
   template <class _Sseq, __enable_if_t<__is_seed_sequence_v<_Sseq, mersenne_twister_engine>, int> = 0>
   _LIBCPP_HIDE_FROM_ABI void seed(_Sseq& __q) {
@@ -203,17 +206,43 @@ class mersenne_twister_engine {
           return;
       __x_[0] = result_type(1) << (__w - 1);
     }
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    __update_all_states();
+#endif
+  }
+
+  void __update_state(size_t __i, size_t __k) {
+    const size_t __j         = (__i + 1) % __n;
+    const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
+    const result_type __yp   = (__x_[__i] & ~__mask) | (__x_[__j] & __mask);
+    __x_[__i]                = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
+  }
+
+  void __update_state(size_t __i) { __update_state(__i, (__i + __m) % __n); }
+
+  void __update_all_states() {
+    size_t __i = 0;
+    // This is split into two loops to help the compiler vectorize the code
+    for (; __i != (__n - __m); ++__i)
+      __update_state(__i);
+    for (size_t __j = 0; __i != __n; ++__i, ++__j)
+      __update_state(__i, __j);
   }
 
   // generating functions
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI result_type operator()() {
-    const size_t __j         = (__i_ + 1) % __n;
-    const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
-    const result_type __yp   = (__x_[__i_] & ~__mask) | (__x_[__j] & __mask);
-    const size_t __k         = (__i_ + __m) % __n;
-    __x_[__i_]               = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
-    result_type __z          = __x_[__i_] ^ (__rshift<__u>(__x_[__i_]) & __d);
-    __i_                     = __j;
+#ifdef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+    result_type __val = __x_[__i_];
+    if (++__i_ == __n) [[__unlikely__]] {
+      __update_all_states();
+      __i_ = 0;
+    }
+#else
+    __update_state(__i_);
+    result_type __val = __x_[__i_];
+    __i_              = (__i_ + 1) % __n;
+#endif
+    result_type __z = __val ^ (__rshift<__u>(__val) & __d);
     __z ^= __lshift<__s>(__z) & __b;
     __z ^= __lshift<__t>(__z) & __c;
     return __z ^ __rshift<__l>(__z);

diff  --git a/libcxx/test/benchmarks/random.bench.cpp b/libcxx/test/benchmarks/random.bench.cpp
index e6af4c3e26eaf..15e16b988a29f 100644
--- a/libcxx/test/benchmarks/random.bench.cpp
+++ b/libcxx/test/benchmarks/random.bench.cpp
@@ -34,4 +34,29 @@ static void BM_SeedSeq_Generate(benchmark::State& state) {
 }
 BENCHMARK(BM_SeedSeq_Generate)->Ranges({{1, MAX_SEED_LEN}, {1, MAX_BUFFER_LEN}});
 
+template <class Engine>
+static void BM_engine(benchmark::State& state) {
+  Engine engine;
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(engine());
+  }
+}
+BENCHMARK(BM_engine<std::mt19937_64>)->Name("std::mt19937_64::operator()");
+BENCHMARK(BM_engine<std::mt19937>)->Name("std::mt19937::operator()");
+
+template <class Engine>
+static void BM_engine_array(benchmark::State& state) {
+  Engine engine;
+
+  typename Engine::result_type buffer[128];
+
+  for (auto _ : state) {
+    std::generate(std::begin(buffer), std::end(buffer), std::ref(engine));
+    benchmark::DoNotOptimize(buffer);
+  }
+}
+BENCHMARK(BM_engine_array<std::mt19937_64>)->Name("std::mt19937_64::operator() (into array)");
+BENCHMARK(BM_engine_array<std::mt19937>)->Name("std::mt19937::operator() (into array)");
+
 BENCHMARK_MAIN();

diff  --git a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
index 02df0345525cb..b2ae5141edb06 100644
--- a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_result_type.pass.cpp
@@ -20,237 +20,371 @@
 // Serializing/deserializing the state of the RNG requires iostreams
 // UNSUPPORTED: no-localization
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME && libcpp-abi-vectorized-mersenne-twister-engine
+
 #include <random>
 #include <sstream>
 #include <cassert>
 
 #include "test_macros.h"
 #if TEST_STD_VER >= 11
-#include "make_implicit.h"
-#include "test_convertible.h"
+#  include "make_implicit.h"
+#  include "test_convertible.h"
 #endif
 
 template <class T>
-std::string
-to_string(T const &e)
-{
-    std::ostringstream os;
-    os << e;
-    return os.str();
+std::string to_string(T const& e) {
+  std::ostringstream os;
+  os << e;
+  return os.str();
 }
 
-void
-test1()
-{
-    const char* a = "0 1 1812433255 1900727105 1208447044 2481403966 4042607538 337614300 "
-    "3232553940 1018809052 3202401494 1775180719 3192392114 594215549 184016991 "
-    "829906058 610491522 3879932251 3139825610 297902587 4075895579 2943625357 "
-    "3530655617 1423771745 2135928312 2891506774 1066338622 135451537 933040465 "
-    "2759011858 2273819758 3545703099 2516396728 1272276355 3172048492 "
-    "3267256201 2332199830 1975469449 392443598 1132453229 2900699076 "
-    "1998300999 3847713992 512669506 1227792182 1629110240 112303347 2142631694 "
-    "3647635483 1715036585 2508091258 1355887243 1884998310 3906360088 "
-    "952450269 3647883368 3962623343 3077504981 2023096077 3791588343 "
-    "3937487744 3455116780 1218485897 1374508007 2815569918 1367263917 "
-    "472908318 2263147545 1461547499 4126813079 2383504810 64750479 2963140275 "
-    "1709368606 4143643781 835933993 1881494649 674663333 2076403047 858036109 "
-    "1667579889 1706666497 607785554 1995775149 1941986352 3448871082 "
-    "2109910019 1474883361 1623095288 1831376534 2612738285 81681830 2204289242 "
-    "1365038485 251164610 4268495337 1805601714 1262528768 1442526919 "
-    "1675006593 965627108 646339161 499795587 840887574 380522518 3023789847 "
-    "1457635507 1947093157 2600365344 2729853143 1550618999 1390905853 "
-    "3021294812 882647559 838872117 1663880796 4222103589 2754172275 3844026123 "
-    "3199260319 4176064873 3591027019 2690294242 2978135515 3172796441 "
-    "3263669796 1451257057 1427035359 4174826006 2171992010 1537002090 "
-    "3122405306 4162452508 3271954368 3794310005 3240514581 1270412086 "
-    "3030475836 2281945856 2644171349 3109139423 4253563838 1289926431 "
-    "1396919653 733220100 2753316645 1196225013 3699575255 3569440056 "
-    "2675979228 2624079148 3463113149 863430286 623703199 2113837653 2656425919 "
-    "175981357 4271478366 4238022735 1665483419 86880610 2963435083 1830392943 "
-    "847801865 3237296945 332143967 3973606945 2671879697 2236330279 2360127810 "
-    "3283955434 203240344 4048139172 13189264 2263058814 247241371 1566765783 "
-    "3084408095 3719371299 1958375251 1985924622 1712739232 1861691451 "
-    "2644502937 2337807839 784993770 2962208780 2190810177 1523122731 "
-    "714888527 578678761 3698481324 1801168075 534650483 3390213921 3923356461 "
-    "3586009066 2059432114 52511333 1969897376 3630122061 524661135 3513619765 "
-    "563070233 501359785 477489274 658768624 938973567 1548584683 1345287459 "
-    "2488691004 3441144905 3849305094 2430000078 855172178 614463281 2092744749 "
-    "176381493 1655802051 2273888101 2474494847 3471978030 2138918303 575352373 "
-    "1658230985 1675972553 2946663114 915579339 284981499 53939948 3022598146 "
-    "1861218535 3403620774 4203516930 2360471119 3134536268 1383448498 "
-    "1307602316 3847663247 3027225131 3597251613 3186237127 725127595 "
-    "1928526954 1843386923 3560410503 54688266 1791983849 2519860352 4256389699 "
-    "2328812602 486464275 3578698363 301279829 1303654791 4181868765 971794070 "
-    "1933885487 3996807464 2144053754 4079903755 3775774765 3481760044 "
-    "1212862354 1067356423 3764189132 1609862325 2209601551 2565747501 "
-    "161962392 4045451782 2605574664 2520953090 3490240017 1082791980 44474324 "
-    "101811128 4268650669 4171338684 772375154 3920460306 2319139534 599033750 "
-    "2950874441 3373922995 1496848525 4095253594 1271943484 1498723121 "
-    "3097453329 3698082465 281869581 3148270661 3591477288 747441437 2809508504 "
-    "3896107498 303747862 2368081624 1844217645 886825352 287949781 1444561207 "
-    "2512101757 2062331723 741720931 1383797313 3876746355 2041045348 "
-    "2627599118 1124169970 200524822 3484820454 55883666 1135054804 669498692 "
-    "2677215504 3097911127 1509628615 617580381 2229022193 85601568 3243896546 "
-    "3715672328 912168347 2359163500 1180347564 4243175048 2092067103 880183327 "
-    "4000664709 2045044777 3500474644 1515175520 1862207123 186628841 "
-    "3337252925 708933575 4015964629 3136815297 3314919747 2891909013 "
-    "3316567785 3944275369 3608506218 2884839110 3054055598 2707439927 "
-    "1381111877 3275487281 4292456216 2639563270 3327301876 3576924628 "
-    "721056309 2002808140 748967365 52380958 2200261692 763456477 1708381337 "
-    "2038446433 2682979402 1526413779 2211263302 3879771969 75966584 3645059271 "
-    "2985763524 4085690255 82390958 1883631385 1647521260 1598026998 3038041577 "
-    "2501913134 3279302868 1738888524 805035483 756399074 3863810982 1097797270 "
-    "1505792529 898904527 583561003 717152376 3333867738 1099456544 1663473545 "
-    "1242141229 3828627682 1966201676 1713552361 3852160017 1584965284 21695908 "
-    "1013262144 145341901 3995441263 3462066219 2239637848 1214086163 "
-    "2428868268 1650037305 1545513388 1621198806 4232947817 1823092073 "
-    "256414624 1745018809 1357102386 2055139770 3280958307 2482431613 "
-    "1664870585 859130423 4097751123 3079768369 2470211009 2984880786 "
-    "2808568948 2877071923 1984903163 302768457 1866396789 869566317 3746415787 "
-    "4169433075 3025005404 3980733379 3539207278 3953071536 876960847 "
-    "2548872156 800507464 1865466907 1273317878 3754712872 1757188269 "
-    "3229950355 3731640200 2283390608 2204990292 411873449 447423849 1852437802 "
-    "472825525 3044219944 2913114194 1859709265 4053786194 574820536 2104496732 "
-    "865469814 2438352724 4208743605 4215067542 1364015250 4139974345 "
-    "3838747005 1818502786 2914274940 1402365828 1751123528 2302578077 "
-    "2463168652 1968705496 1730700144 3023943273 1139096844 2658667767 "
-    "2063547264 705791165 1444775274 2415454225 1575664730 921044163 648101324 "
-    "1212387162 4191962054 1787702169 1888718041 1518218010 3398792842 "
-    "4079359729 149721439 750400353 2661036076 3802767886 520152586 951852508 "
-    "2939585975 1375969109 385733137 3523607459 1902438415 4250996086 "
-    "2712727066 484493674 3932107461 1428488210 1764242548 3424801055 "
-    "4004904451 2226862072 2393366939 3609584727 3614444319 317349896 "
-    "3826527525 204023804 981902443 3356042039 3051207045 1869902661 561831895 "
-    "3706675415 1527687593 1227610446 2596341042 3191717368 3269246891 "
-    "557877074 4062070629 3052520266 3772487029 400039836 3195205275 4085394797 "
-    "1655557239 1345770144 2864727192 449281238 73189507 528365765 2727400656 "
-    "247880434 2408277395 777039183 2210179398 1088433648 2124356402 1555630141 "
-    "604790219 195012151 3312518356 923728373 3999251660 3313059535 3478133921 "
-    "3395026960 383464614 3425869222 2446885186 4032184426 157195416 3158909476 "
-    "1663750443 2046427584 1658453076 1784483001 3146546889 1238739785 "
-    "2297306523 3472330897 2953326031 2421672215 1221694592 1588568605 "
-    "2546987845 3375168573 2137961649 3056565164 330165219 235900365 1000384800 "
-    "2697255904 579122283 3050664825 73426122 1232986102 2940571064 3076486824 "
-    "1708182873 2796363264 292154131 4280019913 1102652157 1185393592 "
-    "1494991690 4270076389 2384840717 425785147 2385321880 317514772 3926962743 "
-    "392176856 3465421709 1878853468 122662664 2958252160 1858961315 2244939588 "
-    "2361884409 2860936803 683833250 3291277128 1686857206 1112632275 "
-    "1200680507 3342928196 2677058150 939442136 3407104669 2906783932 "
-    "3668048733 2030009470 1910839172 1234925283 3575831445 123595418 "
-    "2362440495 3048484911 1796872496";
-    std::mt19937 e1(0);
-    assert(to_string(e1) == a);
+void test1() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "0 1 1812433255 1900727105 1208447044 2481403966 4042607538 337614300 "
+      "3232553940 1018809052 3202401494 1775180719 3192392114 594215549 184016991 "
+      "829906058 610491522 3879932251 3139825610 297902587 4075895579 2943625357 "
+      "3530655617 1423771745 2135928312 2891506774 1066338622 135451537 933040465 "
+      "2759011858 2273819758 3545703099 2516396728 1272276355 3172048492 "
+      "3267256201 2332199830 1975469449 392443598 1132453229 2900699076 "
+      "1998300999 3847713992 512669506 1227792182 1629110240 112303347 2142631694 "
+      "3647635483 1715036585 2508091258 1355887243 1884998310 3906360088 "
+      "952450269 3647883368 3962623343 3077504981 2023096077 3791588343 "
+      "3937487744 3455116780 1218485897 1374508007 2815569918 1367263917 "
+      "472908318 2263147545 1461547499 4126813079 2383504810 64750479 2963140275 "
+      "1709368606 4143643781 835933993 1881494649 674663333 2076403047 858036109 "
+      "1667579889 1706666497 607785554 1995775149 1941986352 3448871082 "
+      "2109910019 1474883361 1623095288 1831376534 2612738285 81681830 2204289242 "
+      "1365038485 251164610 4268495337 1805601714 1262528768 1442526919 "
+      "1675006593 965627108 646339161 499795587 840887574 380522518 3023789847 "
+      "1457635507 1947093157 2600365344 2729853143 1550618999 1390905853 "
+      "3021294812 882647559 838872117 1663880796 4222103589 2754172275 3844026123 "
+      "3199260319 4176064873 3591027019 2690294242 2978135515 3172796441 "
+      "3263669796 1451257057 1427035359 4174826006 2171992010 1537002090 "
+      "3122405306 4162452508 3271954368 3794310005 3240514581 1270412086 "
+      "3030475836 2281945856 2644171349 3109139423 4253563838 1289926431 "
+      "1396919653 733220100 2753316645 1196225013 3699575255 3569440056 "
+      "2675979228 2624079148 3463113149 863430286 623703199 2113837653 2656425919 "
+      "175981357 4271478366 4238022735 1665483419 86880610 2963435083 1830392943 "
+      "847801865 3237296945 332143967 3973606945 2671879697 2236330279 2360127810 "
+      "3283955434 203240344 4048139172 13189264 2263058814 247241371 1566765783 "
+      "3084408095 3719371299 1958375251 1985924622 1712739232 1861691451 "
+      "2644502937 2337807839 784993770 2962208780 2190810177 1523122731 "
+      "714888527 578678761 3698481324 1801168075 534650483 3390213921 3923356461 "
+      "3586009066 2059432114 52511333 1969897376 3630122061 524661135 3513619765 "
+      "563070233 501359785 477489274 658768624 938973567 1548584683 1345287459 "
+      "2488691004 3441144905 3849305094 2430000078 855172178 614463281 2092744749 "
+      "176381493 1655802051 2273888101 2474494847 3471978030 2138918303 575352373 "
+      "1658230985 1675972553 2946663114 915579339 284981499 53939948 3022598146 "
+      "1861218535 3403620774 4203516930 2360471119 3134536268 1383448498 "
+      "1307602316 3847663247 3027225131 3597251613 3186237127 725127595 "
+      "1928526954 1843386923 3560410503 54688266 1791983849 2519860352 4256389699 "
+      "2328812602 486464275 3578698363 301279829 1303654791 4181868765 971794070 "
+      "1933885487 3996807464 2144053754 4079903755 3775774765 3481760044 "
+      "1212862354 1067356423 3764189132 1609862325 2209601551 2565747501 "
+      "161962392 4045451782 2605574664 2520953090 3490240017 1082791980 44474324 "
+      "101811128 4268650669 4171338684 772375154 3920460306 2319139534 599033750 "
+      "2950874441 3373922995 1496848525 4095253594 1271943484 1498723121 "
+      "3097453329 3698082465 281869581 3148270661 3591477288 747441437 2809508504 "
+      "3896107498 303747862 2368081624 1844217645 886825352 287949781 1444561207 "
+      "2512101757 2062331723 741720931 1383797313 3876746355 2041045348 "
+      "2627599118 1124169970 200524822 3484820454 55883666 1135054804 669498692 "
+      "2677215504 3097911127 1509628615 617580381 2229022193 85601568 3243896546 "
+      "3715672328 912168347 2359163500 1180347564 4243175048 2092067103 880183327 "
+      "4000664709 2045044777 3500474644 1515175520 1862207123 186628841 "
+      "3337252925 708933575 4015964629 3136815297 3314919747 2891909013 "
+      "3316567785 3944275369 3608506218 2884839110 3054055598 2707439927 "
+      "1381111877 3275487281 4292456216 2639563270 3327301876 3576924628 "
+      "721056309 2002808140 748967365 52380958 2200261692 763456477 1708381337 "
+      "2038446433 2682979402 1526413779 2211263302 3879771969 75966584 3645059271 "
+      "2985763524 4085690255 82390958 1883631385 1647521260 1598026998 3038041577 "
+      "2501913134 3279302868 1738888524 805035483 756399074 3863810982 1097797270 "
+      "1505792529 898904527 583561003 717152376 3333867738 1099456544 1663473545 "
+      "1242141229 3828627682 1966201676 1713552361 3852160017 1584965284 21695908 "
+      "1013262144 145341901 3995441263 3462066219 2239637848 1214086163 "
+      "2428868268 1650037305 1545513388 1621198806 4232947817 1823092073 "
+      "256414624 1745018809 1357102386 2055139770 3280958307 2482431613 "
+      "1664870585 859130423 4097751123 3079768369 2470211009 2984880786 "
+      "2808568948 2877071923 1984903163 302768457 1866396789 869566317 3746415787 "
+      "4169433075 3025005404 3980733379 3539207278 3953071536 876960847 "
+      "2548872156 800507464 1865466907 1273317878 3754712872 1757188269 "
+      "3229950355 3731640200 2283390608 2204990292 411873449 447423849 1852437802 "
+      "472825525 3044219944 2913114194 1859709265 4053786194 574820536 2104496732 "
+      "865469814 2438352724 4208743605 4215067542 1364015250 4139974345 "
+      "3838747005 1818502786 2914274940 1402365828 1751123528 2302578077 "
+      "2463168652 1968705496 1730700144 3023943273 1139096844 2658667767 "
+      "2063547264 705791165 1444775274 2415454225 1575664730 921044163 648101324 "
+      "1212387162 4191962054 1787702169 1888718041 1518218010 3398792842 "
+      "4079359729 149721439 750400353 2661036076 3802767886 520152586 951852508 "
+      "2939585975 1375969109 385733137 3523607459 1902438415 4250996086 "
+      "2712727066 484493674 3932107461 1428488210 1764242548 3424801055 "
+      "4004904451 2226862072 2393366939 3609584727 3614444319 317349896 "
+      "3826527525 204023804 981902443 3356042039 3051207045 1869902661 561831895 "
+      "3706675415 1527687593 1227610446 2596341042 3191717368 3269246891 "
+      "557877074 4062070629 3052520266 3772487029 400039836 3195205275 4085394797 "
+      "1655557239 1345770144 2864727192 449281238 73189507 528365765 2727400656 "
+      "247880434 2408277395 777039183 2210179398 1088433648 2124356402 1555630141 "
+      "604790219 195012151 3312518356 923728373 3999251660 3313059535 3478133921 "
+      "3395026960 383464614 3425869222 2446885186 4032184426 157195416 3158909476 "
+      "1663750443 2046427584 1658453076 1784483001 3146546889 1238739785 "
+      "2297306523 3472330897 2953326031 2421672215 1221694592 1588568605 "
+      "2546987845 3375168573 2137961649 3056565164 330165219 235900365 1000384800 "
+      "2697255904 579122283 3050664825 73426122 1232986102 2940571064 3076486824 "
+      "1708182873 2796363264 292154131 4280019913 1102652157 1185393592 "
+      "1494991690 4270076389 2384840717 425785147 2385321880 317514772 3926962743 "
+      "392176856 3465421709 1878853468 122662664 2958252160 1858961315 2244939588 "
+      "2361884409 2860936803 683833250 3291277128 1686857206 1112632275 "
+      "1200680507 3342928196 2677058150 939442136 3407104669 2906783932 "
+      "3668048733 2030009470 1910839172 1234925283 3575831445 123595418 "
+      "2362440495 3048484911 1796872496"
+#else
+      "2443250962 1093594115 1878467924 2709361018 1101979660 3904844661 676747479 2085143622 1056793272 3812477442 "
+      "2168787041 275552121 2696932952 3432054210 1657102335 3518946594 962584079 1051271004 3806145045 1414436097 "
+      "2032348584 1661738718 1116708477 2562755208 3176189976 696824676 2399811678 3992505346 569184356 2626558620 "
+      "136797809 4273176064 296167901 3430730584 331909803 1908676996 1950065095 604298543 3615988338 1570232852 "
+      "1028209748 1511467721 2411887154 4210753555 3096762720 423429618 659966766 2937509307 2222847265 378636552 "
+      "1142109618 2509241601 1521729757 888533219 250885260 2455816244 4046047811 1947467789 1395351953 2388948566 "
+      "934627940 194642258 1429256273 2139959677 1543740405 1569613451 4061840539 2075690423 824532376 844152077 "
+      "3218002536 897315311 823414659 1007534698 3139313867 2332794462 3675156583 722594429 3715123011 2656840676 "
+      "2373277715 1516144243 1538161999 1399719297 1448820876 3180842436 2020192709 3279498509 1048107540 3094290376 "
+      "3706920703 3810051683 4003871775 1069557309 151535004 667695756 861767313 1643171871 3649507728 3785763588 "
+      "737899497 2332908276 4082123854 1584539673 3929877280 1046086041 1304062606 2311324008 1190424879 538647603 "
+      "1728806718 148948582 659721465 2351006009 185647941 1825934330 2126619619 2230539295 3878421575 965937340 "
+      "2835563987 419858879 1527311104 2042333483 2745134265 3540517373 1100062165 2308613697 2690739856 2048293545 "
+      "2742876486 2408502115 53398423 3089771461 1398529613 2132477261 504134301 1535108421 1968507173 3412472770 "
+      "4048517196 3509143071 857063211 1429875570 4120479871 2786574360 2470799871 1644104875 2326863162 2033900387 "
+      "274888909 2629973384 1154405937 1839988197 2163830694 268733679 2928953965 387153042 3790900490 3202349717 "
+      "3799209681 2524871464 3801447311 3543081470 1801633977 3870370950 1581678156 358283485 4133385582 4052311650 "
+      "249498956 1712028623 3618329357 3395106946 2704820515 26653208 2437509555 4192186371 3639946838 2615917799 "
+      "294624699 463886267 2477203673 2514051526 4162882381 2937629358 3180524454 1388392394 2637132139 1999482082 "
+      "1870663339 2852309250 3489430700 1116063402 1673637188 1941955790 4081919937 2322920953 3500518887 2722575905 "
+      "405031829 3728964889 3464559963 665516555 1626835870 2523729468 238289561 497556937 2581429740 3456619478 "
+      "2601389837 820426192 268141532 2654829421 349692193 2426957585 1465734488 85095426 1080895536 2823366942 "
+      "379322515 2938986739 1561328720 2940081697 607513620 2724557130 2841377034 14123440 1085631349 1977859413 "
+      "1325586902 1690398943 2516154740 4144373551 555899232 1472707937 3318086852 719779833 3545481907 1384350857 "
+      "191709822 2924586997 3905397527 2528674757 2377804096 2742818576 4162971050 1916415976 2229076768 132706400 "
+      "1337415646 251604474 4168783505 819499074 1213291699 2098930479 1009044052 1060792037 2169940733 2973281031 "
+      "636656529 1946890813 367099469 4076624283 338339529 561343735 3309733580 3895891229 516195845 3075613489 "
+      "3076388119 4088758753 3895582245 1192445752 2923797073 2272605437 2964193665 943979628 3263904824 1852145108 "
+      "1911050628 1596880711 479795791 212699077 2172697748 1764175804 3951845064 2182587171 2396848133 2718837214 "
+      "2934177572 3638054152 914795167 1028977354 1744985339 1161407853 2069746822 3109934340 3666150054 729399111 "
+      "2911998815 152809615 417267263 1064343325 2751707923 1833270972 886939906 4054231201 1410366452 976142646 "
+      "1452795786 1904234879 4231894541 1505392175 3498767279 833704604 2100592828 688268099 1752675602 1975556664 "
+      "2102210733 698665709 1232104968 4051336323 1740516649 3131721670 3755143232 3433756601 138325540 1566724819 "
+      "3146312914 3262098346 1400757929 3821885720 356740291 4236900846 3962771215 3383537419 3432281817 3700484231 "
+      "1124512300 4040103150 2152814065 357241942 3507144572 3156778000 4029389048 1495464238 2710816120 618937484 "
+      "923913256 3233161411 3113338135 2380534800 2989134055 792458413 2073679910 2733527896 1078384210 2832175364 "
+      "501385130 1554930792 2339252603 535487294 2983664186 924228889 2127502206 2847205678 836935943 1902192892 "
+      "4094716961 3296730761 2307406435 281523156 728701372 3943302480 183005637 2660706047 2320033913 2011717602 "
+      "219970222 890345073 2479236311 653839655 38538414 3266308455 3726363269 1256762826 712114913 3499059198 "
+      "2847394904 3246075282 1299462737 830090526 369065487 3899451086 1825910875 2697294244 2553197317 2501134241 "
+      "930821716 2841281141 3920808923 3209045349 3381621736 2145077949 1958091404 371971179 3064957112 3151429184 "
+      "2833651995 1089438243 2146134351 1426568997 3528669710 1043897757 3052263589 3205925012 4076215371 2622476950 "
+      "2385717100 2179892838 3820135605 1574628644 177604240 1712062317 1192081671 1107476027 837376039 2818244460 "
+      "1382379731 2749207910 2644203474 1508860349 3006844699 3157252375 3789683778 1985542020 2309789126 1624089624 "
+      "3660683301 1432444043 1172735898 3271587635 841387671 3614623669 3002115850 1078807738 1095345284 1707086713 "
+      "4263485053 445506874 3148772337 2716917408 3836812085 1026228926 491069724 3983218285 2410582206 3821759029 "
+      "3789305873 1223256738 1677180485 1481771733 1670286271 2852111711 1628943665 2289772949 3483580787 2129860019 "
+      "3955695109 28974017 2289227173 1725243773 483357125 111587742 347631238 1805029491 839978009 339030914 "
+      "2442782449 1701726526 1481742442 3703534067 3796147861 235078546 3195450129 1420184214 998981944 163541155 "
+      "2087693215 3827554748 3003331872 1967967170 160190917 544473278 3884602428 4180513186 1603446876 2191744556 "
+      "1938251010 291242497 1764195107 4076051069 3860380798 806452940 2631256196 949838208 4096577802 1519225265 "
+      "2296800559 1015848400 2864792481 88004533 195132439 966918321 1451347413 2322994051 3585658769 1771598947 "
+      "3308395968 950148946 3846061568 564475171 1345892193 2439581032 2907206869 1297626422 3779454571 818255727 "
+      "705738065 3915384184 4133726754 2724859327 3773743817 326325901 2766390048 2562130418 1885210076 2622324981 "
+      "1727396790 1742653294 3133131942 3790857080 197042473 2868410679 1122976862 2264474389 3607393378 744386048 "
+      "3122128592 1101489146 4064675687 2818166901 2282076661 370818870 3053423257 2272652338 4077081984 4284213290 "
+      "1728304803 3902867896 1643530236 306212760 144705843 498541197 3103269575 4116304893 1646288275 2110757680 "
+      "4092919505 3390929578 1828116798 1847914177 1156754878 2985404552 3970881126 3035874635 2189824678 2637114092 "
+      "2588332748 3917818803 3302695646 1758418726 1934759438 3871576937 1360769189 1051808374 1881390541 757367126 "
+      "3253778407 1685049720 805311189 934898036 95682794 2052240235 3475933669 2213474191 3341273357 2443286051 "
+      "4272536794 696819230 878775027 3332634155 3405126958 2824772573 95284505 3040640920 2412859827 2959515765 "
+      "1764792340 1504924482 2116910326 2293943003 3199262907 1443740894 3900566588 343066614 1927475335 249220892 "
+      "2920636502 3593328327 2648828502 1678096082"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  std::mt19937 e1(0);
+  LIBCPP_ASSERT(to_string(e1) == a);
+  (void)a;
 }
 
-void
-test2()
-{
-    const char* a = "0 1 6364136223846793007 13885033948157127961 "
-    "15324573939901584278 12737837167382305846 15195339788985155882 "
-    "6554113247712070460 17235932740818599105 13007415075556305955 "
-    "6585479514541334743 8274505865835507625 1718218088692873364 "
-    "10390651247454232081 12911994993614796389 3986900029987203370 "
-    "6673827651897561714 4426752746717694792 7419158062930293690 "
-    "5800047417539173618 15710773105226458059 16164512590413496893 "
-    "3438015953120274172 3483801391287623267 293704481016263807 "
-    "11580856846363212652 3489109114147636336 3391036861618673611 "
-    "8265793309278544843 7557898467821912223 11008748280761875940 "
-    "15929443707841919885 8545695347411085846 10810459396490399532 "
-    "12233244910455127352 15556950738631379285 16711543556686614082 "
-    "12362193084052127890 16520645558585805174 5163125267185202360 "
-    "405552980610370477 17567412011316060306 18195950784827697319 "
-    "7893142112162906367 11294475722810961618 7284845498332539581 "
-    "8406882439401998138 4375387785957411470 9627875716250684710 "
-    "8860968026642934661 9743109216691708518 152611520104818631 "
-    "5897430410700879663 5351672954305365323 16325991383734641720 "
-    "9695181037355459478 15420132328343498044 17772146581546890572 "
-    "12095930435311226909 3066005052193896110 11579395203346116306 "
-    "9168946227698330317 18318927644793076250 16096433325093805476 "
-    "14945900876820434843 16826760579960858105 17664119339058295604 "
-    "17844797344364136942 1071414400549507565 16688779616725465582 "
-    "3684635020921274863 12774052823168116810 17270284502989966576 "
-    "1081012692742984704 4377021575203177546 18341292555997099853 "
-    "13297939683513494274 15065725504474304333 10796297883750572804 "
-    "15233335271871291997 8767977593699151062 3360856014170688284 "
-    "7828232912764786750 15167717223619970150 9622174963375022357 "
-    "18262792478991268448 1196631425707106493 5368342740538672272 "
-    "10381091599850241237 12108437846306626340 6150860188928778248 "
-    "3342980288459577584 12715439159457051276 17996971042887275859 "
-    "9749679821487730542 17763727344608586331 16024467606290078168 "
-    "7763401887585513696 4448278918811662063 16947956613780322662 "
-    "15144807360840708645 3878082612940188435 10709780449699561405 "
-    "1649555943517500922 3206645931693769562 12562913950237146427 "
-    "237213339742767727 12987800257476421358 1653669854585203688 "
-    "3485900643226898485 13961759114404652223 5243794832751327611 "
-    "10337687908642742498 16946139522050041809 16716562961992396380 "
-    "4275124606042261542 4055100795824867618 6424268654905981295 "
-    "3424516503413156556 2670380025813203539 10750762735193959951 "
-    "8790031149370411970 4021216986392972993 12076090355041998696 "
-    "14407920322903159838 10653597737935867030 15483225617438352002 "
-    "2497775263858626604 12295882369431088188 14256043521530136935 "
-    "2687322778627883798 3419797801078863201 8786888481486602641 "
-    "445698423634900693 9597067954623467255 7101345576557603992 "
-    "1498579197046783597 10403325187679734962 2464586980321053562 "
-    "2022012026329844477 10802281218030350853 6628929099856200904 "
-    "6828177972863192803 8589868113309334601 5245595233272009016 "
-    "5335692004673212054 4515133017699498525 15966447436053813932 "
-    "15199779177078162007 4190689609934804313 13003438276435994683 "
-    "8406046831313066396 10564320513686955057 12668913223662201488 "
-    "13130110932487580228 1030848205404711145 17684061609212954769 "
-    "12942207438298787911 10731611242140874687 5165052527778107352 "
-    "16323046249518133445 17119162873327029615 5754858052433703070 "
-    "3864761150247579030 9945988334920003074 11409854727071782565 "
-    "5000838138362434817 15526574143469400487 18094554078711846524 "
-    "5576294272011007484 3478525338408894755 11392694223389544658 "
-    "4692963068671452476 4459301637730340710 9699395817392066460 "
-    "14644636990626292085 18065377773424192622 5217202490849387226 "
-    "16175595974171756081 2109372019589047677 1624752883142646445 "
-    "13462209973053735966 12082930933973802402 1568864426788967895 "
-    "17047994306870001795 10556833209957537593 955604103878351641 "
-    "9062985603395234592 9757612676622840969 1767246562613391916 "
-    "9752598821733361274 7499745701633625047 7824811626141302622 "
-    "15819064077972391284 5660565551854829485 17645390577243129343 "
-    "7343780801046581776 2233358068547689666 8716657172695403744 "
-    "9129027798969787220 334709674395230649 2063182499026924878 "
-    "13089071159640936832 1765917316143960741 17552378408917656269 "
-    "3917018959478722819 15626740210483166037 1645962609209923821 "
-    "12277169606472643961 14545894350924442736 11485249378718653961 "
-    "9205208816702766530 10967561305613932827 3105992977398681914 "
-    "2125140299311648264 11619505070220308543 5030167448920096401 "
-    "4248170446421798953 16184577688118775567 9240607582885304823 "
-    "11838996733938359277 415426114101983968 14340734742548675134 "
-    "4124085748228276376 17686494750190224280 9472996569628985376 "
-    "1207013222233148636 3031046462562068367 45068538181330439 "
-    "8678647417835301152 10693327126492781235 3058899219097846020 "
-    "18377730418355022492 10269941972656742364 15986476992758938864 "
-    "14575856764093007010 14749682846448398393 1042926396621439263 "
-    "12184905641567868001 3518848236485931862 6718580865438347534 "
-    "6319395993260741012 2855168874111910691 2482146419106261786 "
-    "17290238774162848390 8071397865265268054 15873003794708011585 "
-    "14422764926380465297 14140979091525022882 3573480287238168646 "
-    "1525896111244666696 7537826952717918371 10482908122538761078 "
-    "17003233041653857 9473838740924911883 8438240966750123668 "
-    "10697754962581554225 15048771265786776312 9067877678399943713 "
-    "3399555692325948067 6150260207049997483 7165140289246675175 "
-    "14816202987105583988 4753550992948864498 10549400354582510015 "
-    "13212062554023586370 1585477630313819722 476999696494664205 "
-    "3208739183359199317 16011681780347380478 8149150693959772807 "
-    "803412833228911773 2360961455092949929 1517204230639934662 "
-    "13863717544358808032 16792122738584967930 12742474971940259289 "
-    "1859755681395355028 1540431035241590810 3883807554140904361 "
-    "16189061625447625933 12376367376041900879 8006563585771266211 "
-    "2682617767291542421 8593924529704142157 9070391845133329273 "
-    "3557484410564396342 9301398051805853085 12632514768298643219 "
-    "227653509634201118 7247795074312488552 4939136716843120792 "
-    "6533591052147596041 1308401457054431629 17488144120120152559 "
-    "14075631579093810083 4015705597302725704 6833920126528811473 "
-    "5095302940809114298 8312250184258072842 15770605014574863643 "
-    "14091690436120485477 15763282477731738396 16394237160547425954 "
-    "5066318118328746621 13140493775100916989 6371148952471982853 "
-    "15150289760867914983 4931341382074091848 12635920082410445322 "
-    "8498109357807439006 14836776625250834986";
-    std::mt19937_64 e1(0);
-    assert(to_string(e1) == a);
+void test2() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "0 1 6364136223846793007 13885033948157127961 "
+      "15324573939901584278 12737837167382305846 15195339788985155882 "
+      "6554113247712070460 17235932740818599105 13007415075556305955 "
+      "6585479514541334743 8274505865835507625 1718218088692873364 "
+      "10390651247454232081 12911994993614796389 3986900029987203370 "
+      "6673827651897561714 4426752746717694792 7419158062930293690 "
+      "5800047417539173618 15710773105226458059 16164512590413496893 "
+      "3438015953120274172 3483801391287623267 293704481016263807 "
+      "11580856846363212652 3489109114147636336 3391036861618673611 "
+      "8265793309278544843 7557898467821912223 11008748280761875940 "
+      "15929443707841919885 8545695347411085846 10810459396490399532 "
+      "12233244910455127352 15556950738631379285 16711543556686614082 "
+      "12362193084052127890 16520645558585805174 5163125267185202360 "
+      "405552980610370477 17567412011316060306 18195950784827697319 "
+      "7893142112162906367 11294475722810961618 7284845498332539581 "
+      "8406882439401998138 4375387785957411470 9627875716250684710 "
+      "8860968026642934661 9743109216691708518 152611520104818631 "
+      "5897430410700879663 5351672954305365323 16325991383734641720 "
+      "9695181037355459478 15420132328343498044 17772146581546890572 "
+      "12095930435311226909 3066005052193896110 11579395203346116306 "
+      "9168946227698330317 18318927644793076250 16096433325093805476 "
+      "14945900876820434843 16826760579960858105 17664119339058295604 "
+      "17844797344364136942 1071414400549507565 16688779616725465582 "
+      "3684635020921274863 12774052823168116810 17270284502989966576 "
+      "1081012692742984704 4377021575203177546 18341292555997099853 "
+      "13297939683513494274 15065725504474304333 10796297883750572804 "
+      "15233335271871291997 8767977593699151062 3360856014170688284 "
+      "7828232912764786750 15167717223619970150 9622174963375022357 "
+      "18262792478991268448 1196631425707106493 5368342740538672272 "
+      "10381091599850241237 12108437846306626340 6150860188928778248 "
+      "3342980288459577584 12715439159457051276 17996971042887275859 "
+      "9749679821487730542 17763727344608586331 16024467606290078168 "
+      "7763401887585513696 4448278918811662063 16947956613780322662 "
+      "15144807360840708645 3878082612940188435 10709780449699561405 "
+      "1649555943517500922 3206645931693769562 12562913950237146427 "
+      "237213339742767727 12987800257476421358 1653669854585203688 "
+      "3485900643226898485 13961759114404652223 5243794832751327611 "
+      "10337687908642742498 16946139522050041809 16716562961992396380 "
+      "4275124606042261542 4055100795824867618 6424268654905981295 "
+      "3424516503413156556 2670380025813203539 10750762735193959951 "
+      "8790031149370411970 4021216986392972993 12076090355041998696 "
+      "14407920322903159838 10653597737935867030 15483225617438352002 "
+      "2497775263858626604 12295882369431088188 14256043521530136935 "
+      "2687322778627883798 3419797801078863201 8786888481486602641 "
+      "445698423634900693 9597067954623467255 7101345576557603992 "
+      "1498579197046783597 10403325187679734962 2464586980321053562 "
+      "2022012026329844477 10802281218030350853 6628929099856200904 "
+      "6828177972863192803 8589868113309334601 5245595233272009016 "
+      "5335692004673212054 4515133017699498525 15966447436053813932 "
+      "15199779177078162007 4190689609934804313 13003438276435994683 "
+      "8406046831313066396 10564320513686955057 12668913223662201488 "
+      "13130110932487580228 1030848205404711145 17684061609212954769 "
+      "12942207438298787911 10731611242140874687 5165052527778107352 "
+      "16323046249518133445 17119162873327029615 5754858052433703070 "
+      "3864761150247579030 9945988334920003074 11409854727071782565 "
+      "5000838138362434817 15526574143469400487 18094554078711846524 "
+      "5576294272011007484 3478525338408894755 11392694223389544658 "
+      "4692963068671452476 4459301637730340710 9699395817392066460 "
+      "14644636990626292085 18065377773424192622 5217202490849387226 "
+      "16175595974171756081 2109372019589047677 1624752883142646445 "
+      "13462209973053735966 12082930933973802402 1568864426788967895 "
+      "17047994306870001795 10556833209957537593 955604103878351641 "
+      "9062985603395234592 9757612676622840969 1767246562613391916 "
+      "9752598821733361274 7499745701633625047 7824811626141302622 "
+      "15819064077972391284 5660565551854829485 17645390577243129343 "
+      "7343780801046581776 2233358068547689666 8716657172695403744 "
+      "9129027798969787220 334709674395230649 2063182499026924878 "
+      "13089071159640936832 1765917316143960741 17552378408917656269 "
+      "3917018959478722819 15626740210483166037 1645962609209923821 "
+      "12277169606472643961 14545894350924442736 11485249378718653961 "
+      "9205208816702766530 10967561305613932827 3105992977398681914 "
+      "2125140299311648264 11619505070220308543 5030167448920096401 "
+      "4248170446421798953 16184577688118775567 9240607582885304823 "
+      "11838996733938359277 415426114101983968 14340734742548675134 "
+      "4124085748228276376 17686494750190224280 9472996569628985376 "
+      "1207013222233148636 3031046462562068367 45068538181330439 "
+      "8678647417835301152 10693327126492781235 3058899219097846020 "
+      "18377730418355022492 10269941972656742364 15986476992758938864 "
+      "14575856764093007010 14749682846448398393 1042926396621439263 "
+      "12184905641567868001 3518848236485931862 6718580865438347534 "
+      "6319395993260741012 2855168874111910691 2482146419106261786 "
+      "17290238774162848390 8071397865265268054 15873003794708011585 "
+      "14422764926380465297 14140979091525022882 3573480287238168646 "
+      "1525896111244666696 7537826952717918371 10482908122538761078 "
+      "17003233041653857 9473838740924911883 8438240966750123668 "
+      "10697754962581554225 15048771265786776312 9067877678399943713 "
+      "3399555692325948067 6150260207049997483 7165140289246675175 "
+      "14816202987105583988 4753550992948864498 10549400354582510015 "
+      "13212062554023586370 1585477630313819722 476999696494664205 "
+      "3208739183359199317 16011681780347380478 8149150693959772807 "
+      "803412833228911773 2360961455092949929 1517204230639934662 "
+      "13863717544358808032 16792122738584967930 12742474971940259289 "
+      "1859755681395355028 1540431035241590810 3883807554140904361 "
+      "16189061625447625933 12376367376041900879 8006563585771266211 "
+      "2682617767291542421 8593924529704142157 9070391845133329273 "
+      "3557484410564396342 9301398051805853085 12632514768298643219 "
+      "227653509634201118 7247795074312488552 4939136716843120792 "
+      "6533591052147596041 1308401457054431629 17488144120120152559 "
+      "14075631579093810083 4015705597302725704 6833920126528811473 "
+      "5095302940809114298 8312250184258072842 15770605014574863643 "
+      "14091690436120485477 15763282477731738396 16394237160547425954 "
+      "5066318118328746621 13140493775100916989 6371148952471982853 "
+      "15150289760867914983 4931341382074091848 12635920082410445322 "
+      "8498109357807439006 14836776625250834986"
+#else
+      "4640967165370225016 475580846709610041 992132072322742490 2879266425597847059 9859212410995834590 "
+      "13110412116137766394 2786888605138474240 12527462973726820895 5232191189150589434 8148228587345305383 "
+      "15985190208517110588 17155057006866468333 5040233137627218077 12716217438209898279 7628670991140263350 "
+      "9633431818170735083 8029874679197696152 2546188705250129595 13105544608186384869 6197138528956131705 "
+      "2500767142257833113 4057403796030124452 4801410225133580521 12712510024708253355 1478853541812684571 "
+      "16899998798183024166 763564441705181102 13204668179160915419 6975570844831914277 12029362619687152075 "
+      "17592155920476593974 1391138604394782251 13557582389860889375 6018426101890403504 7401881601334221625 "
+      "281124061034155254 2261645520472923927 10254468173793190351 4336071085409420785 7075410027066751424 "
+      "7438312290102887001 15139389032212795768 12949861808476919158 5221320262711473213 18444574859163569422 "
+      "3327484732560520915 10371833210785813703 494705948341138486 313693460050824934 801700386546917424 "
+      "3333797513889190239 11734932229126290067 4001689285737041461 17077402128101531244 17161815342609821634 "
+      "4393649844836338524 17495132932599391165 16521868517739667677 5661222119201994079 12972222767581865814 "
+      "11571421678172213790 390646047509318948 11495394588801409501 6525248087626393555 8573656613713326328 "
+      "8142990904973493114 13660896775289126601 17864485770152980871 17445884582967639919 5024202147275854142 "
+      "671342157422491897 8265009237858722295 8608118008319642631 9219145998498351877 4556268366257199356 "
+      "6140599343918232709 1607043993756685523 16576680458922020446 2464344868820317239 11814083511457927881 "
+      "17322648412109813175 1813592025330691328 11477642670415214162 17070997401037634101 2303972879274470462 "
+      "11243326672568121379 3445882696291328363 12839802107382893721 12104481080282265364 2596243342696322386 "
+      "17790946661248112185 16074938231864255959 2956506024687822818 5581797792339843057 16395798806003821452 "
+      "1430185758107085903 18323996060167045894 9286586749315539711 11359324205220105656 13078829860258641519 "
+      "5216000878301612113 9168569275644144847 4001438364296785628 2620979336382597902 17733020247211834335 "
+      "9313982434870058041 14715751745255862275 2010565534563004422 3178715725792479308 1903043159754162676 "
+      "14115336913931799326 2521027868575181436 16014439786100458068 12367605516710589904 209714080394415764 "
+      "1624794657804038156 9935768631398762231 4151749392286673312 7107972358174928160 5660032753352365076 "
+      "18036755596310855736 10390782681895899933 1046990263302077358 7362677844682408358 9462769113228112518 "
+      "16297726431682095630 320526314299713717 3778064933596859787 10885323884409002007 2236973268193149682 "
+      "10878852310456567407 2570883140239125692 2789272264811335056 13059628436710418572 2738898868861677604 "
+      "13893534347610447431 5822583231449316688 6492049503818767408 6244748132635487608 8670290879871155016 "
+      "9029174311949188652 14279734151305645897 15912641926116463991 5242647656726280598 18355120933288718928 "
+      "6020122330537384578 14237586109975592522 4056743947737351776 11111921204484673848 2182608451873446153 "
+      "166740030032355275 6707804438883761942 946217821621517312 17920751901125513000 11238439424546078979 "
+      "9206370960702334859 10367106562255294642 16885700982121403407 5166729472772734774 12763662184890206104 "
+      "5517776110990532992 14067054651980895669 91103470530233291 13189720418381102366 13302941329281487681 "
+      "10033391968256035918 5361508361658563078 9633125940775192275 4070378705292966499 2578880557354159711 "
+      "8213829012818869727 14609024865720281973 5764529456700294955 4441867238869537141 4877335405025028278 "
+      "3719605268964114254 6912476600417191412 12213543493427784533 9772185674504838078 849905513166852628 "
+      "2288907677236569364 13249989292558739703 17027351938809896108 621729935073174643 11783397793000418128 "
+      "6535353235698302382 17474699882318514854 11002055139008150918 18416122529605421577 6901480652243894157 "
+      "10382683051257874235 4029870255315506009 2965992838295436717 6235535328826140656 12567736520515051239 "
+      "1747508188587454664 6183192989050178616 15953675909489391368 10361870655666472156 13992646052760132872 "
+      "18278663149537015721 2341145555999432211 6926699433818498172 13807760559474580665 14452983890116009870 "
+      "11892982856082687091 17809698859762328768 2068107465420962246 7113569128111994893 4401076180719847553 "
+      "11644868941271347491 13124811241626639928 13749911628127226656 17349042196015114201 17667020131618919593 "
+      "5882998107995308535 3994906988813734883 12471577435602039626 6541516905696908495 12651354739990156236 "
+      "2656551093809687944 8349751746116271717 16003262134315245189 16966957802441281611 9846484220369869695 "
+      "289040869684008784 12984844645788078807 15184274406289320733 8585588724744493207 17783251231435661237 "
+      "8433051731677026430 4615759437146530251 7622401495291460413 11623553053949964326 5539054352599742054 "
+      "8346732818944267068 2539102865177431249 12328004780676882137 14682995638339538937 17618899063173726898 "
+      "3560029508967023860 203715062184427611 4331882803273086182 11747937041982829018 14988651560527263167 "
+      "12178986862382858651 3296725696957964696 13476561205033500550 5411341409364572097 6178165960265458773 "
+      "6637587084784255540 2852138145128301044 285104174921779935 3890210696591283091 15859573696692819698 "
+      "4183398296224968094 169433174359854381 11697218950892752694 13577878976046486116 9712904827015064754 "
+      "7617616830643388997 12751433537134599555 12319041734951133946 10239102328951141680 7290886622313676141 "
+      "4739372139955959505 9070795802053402609 10714415902002968511 14446814812660765483 8195026527484180858 "
+      "10331449076861906227 11969763657717422129 11061711610463627412 3682504726309317968 186477037840564317 "
+      "10307292328882458385 11683537113360307954 11300920371008006928 12769285684201107347 14545522978223964713 "
+      "5045546601922369224 202539708050129846 9727300421085682838 10660376569580918468 1852104613210280002 "
+      "2448572796841074793 4275113756623070536 15430856097668330204 8147758262819931807 13018070382604356536 "
+      "1461468238838418536 6307427532280072746 14454983576432818303 16577253122736704814 11165141699850119488 "
+      "1871342067236738412 15245016873857982253 16807633505182897684 18414686740244106738 14154332998791555826 "
+      "2798471212441347211 3624302277914305292 12175243894993809691 18197954905920110263 869336096513266351 "
+      "17322661882612443490 11198026121992866897 3762399852280847282 3393029619669184773 12618712086407186791 "
+      "11601652706361609238 1815215914966060855"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  std::mt19937_64 e1(0);
+  LIBCPP_ASSERT(to_string(e1) == a);
+  (void)a;
 }
 
 template <class E, typename Arg>
@@ -262,13 +396,12 @@ void test_implicit() {
 #endif
 }
 
-int main(int, char**)
-{
-    test1();
-    test2();
+int main(int, char**) {
+  test1();
+  test2();
 
-    test_implicit<std::mt19937, std::uint_fast32_t>();
-    test_implicit<std::mt19937_64, std::uint_fast64_t>();
+  test_implicit<std::mt19937, std::uint_fast32_t>();
+  test_implicit<std::mt19937_64, std::uint_fast64_t>();
 
-    return 0;
+  return 0;
 }

diff  --git a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
index ff028929747a6..24832330720e4 100644
--- a/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.eng/rand.eng.mers/ctor_sseq.pass.cpp
@@ -24,292 +24,425 @@
 
 #include "test_macros.h"
 
-void
-test1()
-{
-    const char* a = "358595400 4166558815 2016177162 3414480257 "
-    "4027494649 3722317195 1190532340 3212207148 "
-    "3537847251 389019999 1098708832 3277907415 "
-    "1946784350 3608286140 2091419822 2227407035 "
-    "2229110723 1825348377 1276269279 314337202 "
-    "3182935337 1313150029 3118776508 3707918501 "
-    "1900972958 4054820954 3973178248 906260237 "
-    "1403942218 3139931556 2807126524 3940936448 "
-    "1316345796 631296613 2268418920 2914000794 "
-    "3760588399 3226216036 880155129 4183611084 "
-    "211541083 3755352858 1331383234 3036493096 "
-    "937478630 2092170412 777784402 93392729 "
-    "3644029210 1681392086 2427001226 3143870332 "
-    "3703581502 2017505388 1706274541 1049329728 "
-    "2452031492 3437261233 2581204087 1700889875 "
-    "1652573881 2127047692 3778506964 1960741508 "
-    "2739602360 3395905609 2123355622 3041272975 "
-    "784200748 3558951522 1002787860 4063320888 "
-    "1587315560 4042698976 659183308 3082256417 "
-    "2808969567 2361418535 3468698782 750700970 "
-    "2991209851 3581521382 962022878 2518967363 "
-    "1476525873 3865977235 2128790058 2380326689 "
-    "1396773405 312559410 1370621899 1154499924 "
-    "2963101919 2182689761 2071851902 1661288848 "
-    "2411351341 1362764020 1289894483 1951662807 "
-    "701821506 552267185 2356648449 3949188503 "
-    "1748307081 87795201 3718396254 4112205936 "
-    "2819888864 73923733 2800033151 839258139 "
-    "3801779069 3105962436 2111266436 1772784466 "
-    "3692264298 4148810953 3147390749 3537518553 "
-    "1695044978 1430225842 1252346204 3465285434 "
-    "3970017763 2920658411 2805151132 290569815 "
-    "3802301355 1493420394 1943029276 1667143611 "
-    "1049665988 1710824905 220168517 3997946231 "
-    "1014582791 4244598752 1147604069 2533886627 "
-    "598679964 761521020 431779255 3745982038 "
-    "768658283 3598262505 1765664789 279538641 "
-    "715144305 2371628432 2655860083 1759010423 "
-    "3568452003 1910107098 2801429529 3924547532 "
-    "3862672436 3933725740 1764550618 130893617 "
-    "1460692387 4135312761 2075529299 2880227367 "
-    "944557368 4166665482 2627749235 3732013815 "
-    "1595900818 1553312393 3529311831 3531462424 "
-    "2431328342 4075369692 1609967709 3704537555 "
-    "2067297464 397140475 920618678 2840795964 "
-    "4202512837 1286017648 7035910 1057207826 "
-    "2325188262 191593698 3697383848 3029712831 "
-    "2073681914 163454681 1329637200 290077398 "
-    "287239431 4205081522 1233889024 167173087 "
-    "3267660257 3406068803 2382354609 1680046927 "
-    "125183503 3559536309 3208900974 2912148541 "
-    "2882879316 1937001086 2919729069 892928802 "
-    "4141691387 2507406586 855548593 3418647837 "
-    "4035646154 2410275591 248715645 3180757482 "
-    "1880770722 362912336 2964920095 2319904154 "
-    "1493655850 4240733030 1834485846 1696040454 "
-    "3329457927 1865824694 847759208 1587231623 "
-    "3757294772 1161601118 3630323833 3007722125 "
-    "3726418007 2124238171 1205345 172659797 "
-    "3040354211 885213338 1857049013 447922412 "
-    "719906299 1370059380 1922204800 3960090489 "
-    "1658822644 1529626863 1565927273 3537718771 "
-    "2733237258 2180221377 921910745 2144937687 "
-    "1727603895 1315635304 4023867791 2401834107 "
-    "808854185 2408824497 343935326 185237544 "
-    "746732759 2641236122 4283215329 743609415 "
-    "1134726665 3892851319 1302851263 3473445597 "
-    "1326817414 2702766508 1943179285 4025685468 "
-    "932896770 199392138 2787362875 3450501893 "
-    "3351567147 2461286528 2227605848 2993751114 "
-    "3988215720 1320573368 2866560199 4153194990 "
-    "3007120042 3260751955 3171763740 2111121243 "
-    "3962825228 102681859 3368179132 802089147 "
-    "4029273561 424939445 4178414761 2592125109 "
-    "1960801088 2967746406 310701990 2364200202 "
-    "1320507009 3474372480 784693947 2952246664 "
-    "1891935330 2048385105 3530082191 3238151038 "
-    "3293189141 1316053288 2087004409 740799958 "
-    "1187748554 3607767334 1190185990 1408429481 "
-    "657134359 221834425 3907725865 1068016389 "
-    "1402423875 2598612116 2046886300 2345022518 "
-    "1196081924 357783981 4013683598 463491626 "
-    "3269206482 3332444286 886491955 2257342866 "
-    "475911113 833576299 2893727564 2866985145 "
-    "1413365115 2995166393 1486060436 161205225 "
-    "3181728373 3056027137 2040371876 2182305146 "
-    "3028448628 2214316977 1266227021 876938740 "
-    "276477469 752158077 2182179045 1381698878 "
-    "3424557652 666674427 968327842 2534296575 "
-    "265105940 961112540 2641188117 2319139814 "
-    "1750453329 3450138343 678025317 1477566458 "
-    "3773796420 2933993832 3326042905 4084805260 "
-    "444182455 255333481 785163068 2321290820 "
-    "2893603234 3005520266 541104079 1383277090 "
-    "2770755666 3764627833 583371929 2864949033 "
-    "1487681116 1811788361 240329486 3094213377 "
-    "958509875 2564379085 1636995945 2070894127 "
-    "2139004232 1747850055 3841512327 3325011872 "
-    "1161622604 639182193 3533652535 4022667522 "
-    "761048999 3337743670 254221568 2784956233 "
-    "2990252814 4207922787 275707208 261819597 "
-    "2071467265 4034945770 1999813410 3038921100 "
-    "2200194573 1328217451 2440612380 3862293692 "
-    "2733976812 2750523058 2920082515 3809044908 "
-    "4285231753 3131963297 3481602724 1396460739 "
-    "2011767965 2481047764 2958452120 3044188618 "
-    "2217236658 3448605566 757716104 1818279145 "
-    "2641228144 1312649639 1194087684 3845229729 "
-    "1747658356 874418803 1956637337 268670179 "
-    "2083040240 2577671381 3375334655 2587828868 "
-    "1383012799 3583445685 2594576715 3282337104 "
-    "4257972751 3440488071 3129180313 1830891395 "
-    "1594931092 2680778339 3984026324 1102770400 "
-    "2315820258 1263467048 1133254110 2400676748 "
-    "2251795328 1036154092 3313541051 2277356560 "
-    "1477696003 1417117088 3968537402 1404882202 "
-    "2011058180 4114080985 1727459502 4100235708 "
-    "2334509310 2829432554 377936301 1519324520 "
-    "3252826644 1193335837 1929125820 2165344238 "
-    "4160556243 223340988 670907625 1485396519 "
-    "936389509 3813712964 2706450987 3132506320 "
-    "875886515 557088991 2854916639 2955496008 "
-    "2881696287 265169077 3239923698 3649366121 "
-    "4072165960 1233904959 225406526 1767368993 "
-    "1894882500 2296582180 339255168 83200939 "
-    "2958376148 4100205346 1991250823 3806183082 "
-    "2691709980 2642354997 3024056146 1681065839 "
-    "3438299684 1638853652 362567001 2307868899 "
-    "988801086 1342833399 2303298376 1500039911 "
-    "765489391 4080464497 4155444368 980472018 "
-    "2026981853 3460406995 391970367 667377014 "
-    "4177754853 2657468948 3560690175 3030464357 "
-    "2948380657 1208800977 2316451404 4001932203 "
-    "1977856863 4265848271 3116200050 3037586215 "
-    "1335232764 930230766 1026089249 2482219415 "
-    "2613853154 1854543497 2909555107 3862874043 "
-    "2609355500 907364682 383900687 358164223 "
-    "232347546 2536276737 3118482806 1254103998 "
-    "2357334077 1204777304 1996643329 4046232717 "
-    "2570520290 3173323380 1201411457 2361883023 "
-    "806087062 2984143714 2355127569 864220085 "
-    "1787696713 1182652984 4200065581 100722519 "
-    "2380458669 2429592313 2618338302 1236529564 "
-    "1747130196 3711661325 1114068102 510380433 "
-    "93703089 2277774664 3220741441 1577998569 "
-    "2816701900 4206763045 2495239107 4080390459 "
-    "1307072677 20360728 1468385549 96049834 "
-    "3630657447 2809517346 3396111678 3043831060 "
-    "673178359 4256729562 1755211210 1969834535 "
-    "498315110 3717726302 1544859987 2239930949 "
-    "1595372585 294525219 3961637067 3591840665 "
-    "3324896933 2300077772 721255886 4197934760 "
-    "1468866696 2184812884 628246683 3385113037 "
-    "3041166140 3948531843 1176600829 228286131 "
-    "2447397608 712235937 3332826819 2676980703 "
-    "4019468871 1952389952 1202638254 3625447051";
-    unsigned as[] = {3, 5, 7};
-    std::seed_seq sseq(as, as+3);
-    std::mt19937 e1(sseq);
-    std::ostringstream os;
-    os << e1;
-    assert(os.str() == a);
+void test1() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "358595400 4166558815 2016177162 3414480257 "
+      "4027494649 3722317195 1190532340 3212207148 "
+      "3537847251 389019999 1098708832 3277907415 "
+      "1946784350 3608286140 2091419822 2227407035 "
+      "2229110723 1825348377 1276269279 314337202 "
+      "3182935337 1313150029 3118776508 3707918501 "
+      "1900972958 4054820954 3973178248 906260237 "
+      "1403942218 3139931556 2807126524 3940936448 "
+      "1316345796 631296613 2268418920 2914000794 "
+      "3760588399 3226216036 880155129 4183611084 "
+      "211541083 3755352858 1331383234 3036493096 "
+      "937478630 2092170412 777784402 93392729 "
+      "3644029210 1681392086 2427001226 3143870332 "
+      "3703581502 2017505388 1706274541 1049329728 "
+      "2452031492 3437261233 2581204087 1700889875 "
+      "1652573881 2127047692 3778506964 1960741508 "
+      "2739602360 3395905609 2123355622 3041272975 "
+      "784200748 3558951522 1002787860 4063320888 "
+      "1587315560 4042698976 659183308 3082256417 "
+      "2808969567 2361418535 3468698782 750700970 "
+      "2991209851 3581521382 962022878 2518967363 "
+      "1476525873 3865977235 2128790058 2380326689 "
+      "1396773405 312559410 1370621899 1154499924 "
+      "2963101919 2182689761 2071851902 1661288848 "
+      "2411351341 1362764020 1289894483 1951662807 "
+      "701821506 552267185 2356648449 3949188503 "
+      "1748307081 87795201 3718396254 4112205936 "
+      "2819888864 73923733 2800033151 839258139 "
+      "3801779069 3105962436 2111266436 1772784466 "
+      "3692264298 4148810953 3147390749 3537518553 "
+      "1695044978 1430225842 1252346204 3465285434 "
+      "3970017763 2920658411 2805151132 290569815 "
+      "3802301355 1493420394 1943029276 1667143611 "
+      "1049665988 1710824905 220168517 3997946231 "
+      "1014582791 4244598752 1147604069 2533886627 "
+      "598679964 761521020 431779255 3745982038 "
+      "768658283 3598262505 1765664789 279538641 "
+      "715144305 2371628432 2655860083 1759010423 "
+      "3568452003 1910107098 2801429529 3924547532 "
+      "3862672436 3933725740 1764550618 130893617 "
+      "1460692387 4135312761 2075529299 2880227367 "
+      "944557368 4166665482 2627749235 3732013815 "
+      "1595900818 1553312393 3529311831 3531462424 "
+      "2431328342 4075369692 1609967709 3704537555 "
+      "2067297464 397140475 920618678 2840795964 "
+      "4202512837 1286017648 7035910 1057207826 "
+      "2325188262 191593698 3697383848 3029712831 "
+      "2073681914 163454681 1329637200 290077398 "
+      "287239431 4205081522 1233889024 167173087 "
+      "3267660257 3406068803 2382354609 1680046927 "
+      "125183503 3559536309 3208900974 2912148541 "
+      "2882879316 1937001086 2919729069 892928802 "
+      "4141691387 2507406586 855548593 3418647837 "
+      "4035646154 2410275591 248715645 3180757482 "
+      "1880770722 362912336 2964920095 2319904154 "
+      "1493655850 4240733030 1834485846 1696040454 "
+      "3329457927 1865824694 847759208 1587231623 "
+      "3757294772 1161601118 3630323833 3007722125 "
+      "3726418007 2124238171 1205345 172659797 "
+      "3040354211 885213338 1857049013 447922412 "
+      "719906299 1370059380 1922204800 3960090489 "
+      "1658822644 1529626863 1565927273 3537718771 "
+      "2733237258 2180221377 921910745 2144937687 "
+      "1727603895 1315635304 4023867791 2401834107 "
+      "808854185 2408824497 343935326 185237544 "
+      "746732759 2641236122 4283215329 743609415 "
+      "1134726665 3892851319 1302851263 3473445597 "
+      "1326817414 2702766508 1943179285 4025685468 "
+      "932896770 199392138 2787362875 3450501893 "
+      "3351567147 2461286528 2227605848 2993751114 "
+      "3988215720 1320573368 2866560199 4153194990 "
+      "3007120042 3260751955 3171763740 2111121243 "
+      "3962825228 102681859 3368179132 802089147 "
+      "4029273561 424939445 4178414761 2592125109 "
+      "1960801088 2967746406 310701990 2364200202 "
+      "1320507009 3474372480 784693947 2952246664 "
+      "1891935330 2048385105 3530082191 3238151038 "
+      "3293189141 1316053288 2087004409 740799958 "
+      "1187748554 3607767334 1190185990 1408429481 "
+      "657134359 221834425 3907725865 1068016389 "
+      "1402423875 2598612116 2046886300 2345022518 "
+      "1196081924 357783981 4013683598 463491626 "
+      "3269206482 3332444286 886491955 2257342866 "
+      "475911113 833576299 2893727564 2866985145 "
+      "1413365115 2995166393 1486060436 161205225 "
+      "3181728373 3056027137 2040371876 2182305146 "
+      "3028448628 2214316977 1266227021 876938740 "
+      "276477469 752158077 2182179045 1381698878 "
+      "3424557652 666674427 968327842 2534296575 "
+      "265105940 961112540 2641188117 2319139814 "
+      "1750453329 3450138343 678025317 1477566458 "
+      "3773796420 2933993832 3326042905 4084805260 "
+      "444182455 255333481 785163068 2321290820 "
+      "2893603234 3005520266 541104079 1383277090 "
+      "2770755666 3764627833 583371929 2864949033 "
+      "1487681116 1811788361 240329486 3094213377 "
+      "958509875 2564379085 1636995945 2070894127 "
+      "2139004232 1747850055 3841512327 3325011872 "
+      "1161622604 639182193 3533652535 4022667522 "
+      "761048999 3337743670 254221568 2784956233 "
+      "2990252814 4207922787 275707208 261819597 "
+      "2071467265 4034945770 1999813410 3038921100 "
+      "2200194573 1328217451 2440612380 3862293692 "
+      "2733976812 2750523058 2920082515 3809044908 "
+      "4285231753 3131963297 3481602724 1396460739 "
+      "2011767965 2481047764 2958452120 3044188618 "
+      "2217236658 3448605566 757716104 1818279145 "
+      "2641228144 1312649639 1194087684 3845229729 "
+      "1747658356 874418803 1956637337 268670179 "
+      "2083040240 2577671381 3375334655 2587828868 "
+      "1383012799 3583445685 2594576715 3282337104 "
+      "4257972751 3440488071 3129180313 1830891395 "
+      "1594931092 2680778339 3984026324 1102770400 "
+      "2315820258 1263467048 1133254110 2400676748 "
+      "2251795328 1036154092 3313541051 2277356560 "
+      "1477696003 1417117088 3968537402 1404882202 "
+      "2011058180 4114080985 1727459502 4100235708 "
+      "2334509310 2829432554 377936301 1519324520 "
+      "3252826644 1193335837 1929125820 2165344238 "
+      "4160556243 223340988 670907625 1485396519 "
+      "936389509 3813712964 2706450987 3132506320 "
+      "875886515 557088991 2854916639 2955496008 "
+      "2881696287 265169077 3239923698 3649366121 "
+      "4072165960 1233904959 225406526 1767368993 "
+      "1894882500 2296582180 339255168 83200939 "
+      "2958376148 4100205346 1991250823 3806183082 "
+      "2691709980 2642354997 3024056146 1681065839 "
+      "3438299684 1638853652 362567001 2307868899 "
+      "988801086 1342833399 2303298376 1500039911 "
+      "765489391 4080464497 4155444368 980472018 "
+      "2026981853 3460406995 391970367 667377014 "
+      "4177754853 2657468948 3560690175 3030464357 "
+      "2948380657 1208800977 2316451404 4001932203 "
+      "1977856863 4265848271 3116200050 3037586215 "
+      "1335232764 930230766 1026089249 2482219415 "
+      "2613853154 1854543497 2909555107 3862874043 "
+      "2609355500 907364682 383900687 358164223 "
+      "232347546 2536276737 3118482806 1254103998 "
+      "2357334077 1204777304 1996643329 4046232717 "
+      "2570520290 3173323380 1201411457 2361883023 "
+      "806087062 2984143714 2355127569 864220085 "
+      "1787696713 1182652984 4200065581 100722519 "
+      "2380458669 2429592313 2618338302 1236529564 "
+      "1747130196 3711661325 1114068102 510380433 "
+      "93703089 2277774664 3220741441 1577998569 "
+      "2816701900 4206763045 2495239107 4080390459 "
+      "1307072677 20360728 1468385549 96049834 "
+      "3630657447 2809517346 3396111678 3043831060 "
+      "673178359 4256729562 1755211210 1969834535 "
+      "498315110 3717726302 1544859987 2239930949 "
+      "1595372585 294525219 3961637067 3591840665 "
+      "3324896933 2300077772 721255886 4197934760 "
+      "1468866696 2184812884 628246683 3385113037 "
+      "3041166140 3948531843 1176600829 228286131 "
+      "2447397608 712235937 3332826819 2676980703 "
+      "4019468871 1952389952 1202638254 3625447051"
+#else
+      "358595400 4166558815 2016177162 3414480257 4027494649 3722317195 1190532340 3212207148 3537847251 389019999 "
+      "1098708832 3277907415 1946784350 3608286140 2091419822 2227407035 2229110723 1825348377 1276269279 314337202 "
+      "3182935337 1313150029 3118776508 3707918501 1900972958 4054820954 3973178248 906260237 1403942218 3139931556 "
+      "2807126524 3940936448 1316345796 631296613 2268418920 2914000794 3760588399 3226216036 880155129 4183611084 "
+      "211541083 3755352858 1331383234 3036493096 937478630 2092170412 777784402 93392729 3644029210 1681392086 "
+      "2427001226 3143870332 3703581502 2017505388 1706274541 1049329728 2452031492 3437261233 2581204087 1700889875 "
+      "1652573881 2127047692 3778506964 1960741508 2739602360 3395905609 2123355622 3041272975 784200748 3558951522 "
+      "1002787860 4063320888 1587315560 4042698976 659183308 3082256417 2808969567 2361418535 3468698782 750700970 "
+      "2991209851 3581521382 962022878 2518967363 1476525873 3865977235 2128790058 2380326689 1396773405 312559410 "
+      "1370621899 1154499924 2963101919 2182689761 2071851902 1661288848 2411351341 1362764020 1289894483 1951662807 "
+      "701821506 552267185 2356648449 3949188503 1748307081 87795201 3718396254 4112205936 2819888864 73923733 "
+      "2800033151 839258139 3801779069 3105962436 2111266436 1772784466 3692264298 4148810953 3147390749 3537518553 "
+      "1695044978 1430225842 1252346204 3465285434 3970017763 2920658411 2805151132 290569815 3802301355 1493420394 "
+      "1943029276 1667143611 1049665988 1710824905 220168517 3997946231 1014582791 4244598752 1147604069 2533886627 "
+      "598679964 761521020 431779255 3745982038 768658283 3598262505 1765664789 279538641 715144305 2371628432 "
+      "2655860083 1759010423 3568452003 1910107098 2801429529 3924547532 3862672436 3933725740 1764550618 130893617 "
+      "1460692387 4135312761 2075529299 2880227367 944557368 4166665482 2627749235 3732013815 1595900818 1553312393 "
+      "3529311831 3531462424 2431328342 4075369692 1609967709 3704537555 2067297464 397140475 920618678 2840795964 "
+      "4202512837 1286017648 7035910 1057207826 2325188262 191593698 3697383848 3029712831 2073681914 163454681 "
+      "1329637200 290077398 287239431 4205081522 1233889024 167173087 3267660257 3406068803 2382354609 1680046927 "
+      "125183503 3559536309 3208900974 2912148541 2882879316 1937001086 2919729069 892928802 4141691387 2507406586 "
+      "855548593 3418647837 4035646154 2410275591 248715645 3180757482 1880770722 362912336 2964920095 2319904154 "
+      "1493655850 4240733030 1834485846 1696040454 3329457927 1865824694 847759208 1587231623 3757294772 1161601118 "
+      "3630323833 3007722125 3726418007 2124238171 1205345 172659797 3040354211 885213338 1857049013 447922412 "
+      "719906299 1370059380 1922204800 3960090489 1658822644 1529626863 1565927273 3537718771 2733237258 2180221377 "
+      "921910745 2144937687 1727603895 1315635304 4023867791 2401834107 808854185 2408824497 343935326 185237544 "
+      "746732759 2641236122 4283215329 743609415 1134726665 3892851319 1302851263 3473445597 1326817414 2702766508 "
+      "1943179285 4025685468 932896770 199392138 2787362875 3450501893 3351567147 2461286528 2227605848 2993751114 "
+      "3988215720 1320573368 2866560199 4153194990 3007120042 3260751955 3171763740 2111121243 3962825228 102681859 "
+      "3368179132 802089147 4029273561 424939445 4178414761 2592125109 1960801088 2967746406 310701990 2364200202 "
+      "1320507009 3474372480 784693947 2952246664 1891935330 2048385105 3530082191 3238151038 3293189141 1316053288 "
+      "2087004409 740799958 1187748554 3607767334 1190185990 1408429481 657134359 221834425 3907725865 1068016389 "
+      "1402423875 2598612116 2046886300 2345022518 1196081924 357783981 4013683598 463491626 3269206482 3332444286 "
+      "886491955 2257342866 475911113 833576299 2893727564 2866985145 1413365115 2995166393 1486060436 161205225 "
+      "3181728373 3056027137 2040371876 2182305146 3028448628 2214316977 1266227021 876938740 276477469 752158077 "
+      "2182179045 1381698878 3424557652 666674427 968327842 2534296575 265105940 961112540 2641188117 2319139814 "
+      "1750453329 3450138343 678025317 1477566458 3773796420 2933993832 3326042905 4084805260 444182455 255333481 "
+      "785163068 2321290820 2893603234 3005520266 541104079 1383277090 2770755666 3764627833 583371929 2864949033 "
+      "1487681116 1811788361 240329486 3094213377 958509875 2564379085 1636995945 2070894127 2139004232 1747850055 "
+      "3841512327 3325011872 1161622604 639182193 3533652535 4022667522 761048999 3337743670 254221568 2784956233 "
+      "2990252814 4207922787 275707208 261819597 2071467265 4034945770 1999813410 3038921100 2200194573 1328217451 "
+      "2440612380 3862293692 2733976812 2750523058 2920082515 3809044908 4285231753 3131963297 3481602724 1396460739 "
+      "2011767965 2481047764 2958452120 3044188618 2217236658 3448605566 757716104 1818279145 2641228144 1312649639 "
+      "1194087684 3845229729 1747658356 874418803 1956637337 268670179 2083040240 2577671381 3375334655 2587828868 "
+      "1383012799 3583445685 2594576715 3282337104 4257972751 3440488071 3129180313 1830891395 1594931092 2680778339 "
+      "3984026324 1102770400 2315820258 1263467048 1133254110 2400676748 2251795328 1036154092 3313541051 2277356560 "
+      "1477696003 1417117088 3968537402 1404882202 2011058180 4114080985 1727459502 4100235708 2334509310 2829432554 "
+      "377936301 1519324520 3252826644 1193335837 1929125820 2165344238 4160556243 223340988 670907625 1485396519 "
+      "936389509 3813712964 2706450987 3132506320 875886515 557088991 2854916639 2955496008 2881696287 265169077 "
+      "3239923698 3649366121 4072165960 1233904959 225406526 1767368993 1894882500 2296582180 339255168 83200939 "
+      "2958376148 4100205346 1991250823 3806183082 2691709980 2642354997 3024056146 1681065839 3438299684 1638853652 "
+      "362567001 2307868899 988801086 1342833399 2303298376 1500039911 765489391 4080464497 4155444368 980472018 "
+      "2026981853 3460406995 391970367 667377014 4177754853 2657468948 3560690175 3030464357 2948380657 1208800977 "
+      "2316451404 4001932203 1977856863 4265848271 3116200050 3037586215 1335232764 930230766 1026089249 2482219415 "
+      "2613853154 1854543497 2909555107 3862874043 2609355500 907364682 383900687 358164223 232347546 2536276737 "
+      "3118482806 1254103998 2357334077 1204777304 1996643329 4046232717 2570520290 3173323380 1201411457 2361883023 "
+      "806087062 2984143714 2355127569 864220085 1787696713 1182652984 4200065581 100722519 2380458669 2429592313 "
+      "2618338302 1236529564 1747130196 3711661325 1114068102 510380433 93703089 2277774664 3220741441 1577998569 "
+      "2816701900 4206763045 2495239107 4080390459 1307072677 20360728 1468385549 96049834 3630657447 2809517346 "
+      "3396111678 3043831060 673178359 4256729562 1755211210 1969834535 498315110 3717726302 1544859987 2239930949 "
+      "1595372585 294525219 3961637067 3591840665 3324896933 2300077772 721255886 4197934760 1468866696 2184812884 "
+      "628246683 3385113037 3041166140 3948531843 1176600829 228286131 2447397608 712235937 3332826819 2676980703 "
+      "4019468871 1952389952 1202638254 3625447051"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  unsigned as[] = {3, 5, 7};
+  std::seed_seq sseq(as, as + 3);
+  std::mt19937 e1(sseq);
+  std::ostringstream os;
+  os << e1;
+  LIBCPP_ASSERT(os.str() == a);
+  (void)a;
 }
 
-void
-test2()
-{
-    const char* a = "17895233847644109640 14665081038668852234 15987230621890949369 "
-    "13796324649827964148 1670828176732799955 14078505147839608672 "
-    "15497470967856861790 9566640372296747182 7839811585250789315 "
-    "1350068003782415071 5639936432479386921 15925388701147119804 "
-    "17415323390466493342 3892358083553387400 13485903346102334794 "
-    "16926193162581531132 2711398308226914244 12515538113016451944 "
-    "13856492368211347055 17968472785843263993 16129117710261673051 "
-    "13041638543181171650 8985803498136324582 401118717516975186 "
-    "7221524024767248666 13502820261231663498 8665119664667372350 "
-    "4506836866186850029 14762924585995667460 7305266389803732087 "
-    "9135600275824854713 8421320656548229332 14585303533697565624 "
-    "13062167967956981222 15285580395823625260 17451830328116466708 "
-    "17363259891080004456 13238190509560321740 10142215382802200927 "
-    "3224236118694175902 15382517208605932923 10818882444738383326 "
-    "16604245792882032433 10223425285179753002 1342432445403828765 "
-    "4958539418185107403 9374581143772158175 7135181273441366910 "
-    "5853026900476841261 8382327930174454355 2371969498930803266 "
-    "16961635468480846337 377077518789053577 17661790013255465310 "
-    "317500018453124832 3604586262706855295 13340007089026272125 "
-    "7614051306438090372 17819007364113857386 15193526497275633437 "
-    "6142773218979108210 14883287611587512668 12544132362002344419 "
-    "1247987855434921372 6414191755211735979 7160327288923375132 "
-    "7347937017206972868 17171048313531629893 18230412825496997383 "
-    "10882960195884354661 3270707876715241884 16088870345045208503 "
-    "15454419782166694763 1200609322828949525 10186066554418904177 "
-    "7554892242763986291 8203847521335919011 16855803304338943001 "
-    "16895223408596071476 562183806034700250 17761033068687156643 "
-    "12370482348384718931 17895691979506634040 16028877286272943475 "
-    "6671425930002400146 15167515621662197335 17503579548680921174 "
-    "15910867647138768989 1705705354110203064 12201125760909412022 "
-    "5523403744441352645 4540673037752294406 822888669354888870 "
-    "13012517529113958824 702032511346794490 1245872939048413008 "
-    "18060687614291143943 718002942670251776 14628954120078526945 "
-    "7215746609592654001 15288092036204733967 12507582747898016110 "
-    "8319356319569362772 3835100005166188461 10769229288786702843 "
-    "14682980657311687345 10352054841727718090 13661249361946024317 "
-    "1558696616315734178 9963912474249467679 18213809676410642730 "
-    "7284438284457478230 8013656044128665351 6817107912809760616 "
-    "4989038816564331700 12918068165960947833 9123533477086273623 "
-    "741568181450204257 3801962339733348259 1923812112542486965 "
-    "5884360231397942779 17008459141377852544 6569697353326895092 "
-    "15194386425456240489 9363979514988323850 9212437218544795097 "
-    "5650610605870621879 10315798944006232463 10345822437227504297 "
-    "795589193815296350 11344022765750598871 3193778122705907169 "
-    "16719669104430190089 14918335244853046975 11608293761910939782 "
-    "17290187430985633813 856382712722415618 14819792788008454203 "
-    "10571145147196955435 12858063129221173592 5671819431516788648 "
-    "17837836658827607239 14004823010100183722 9067196699747632668 "
-    "441015230260308492 3444946658209715644 1825101023084664281 "
-    "11133092574473850025 12746373758552339264 10154162549097295782 "
-    "14922316177042921089 12679802872389794491 8797747037480461410 "
-    "13907752811248535439 5652405835046458389 3181711594575177977 "
-    "15495242712294857418 6049158560807439366 952771601159099159 "
-    "4587095466254740009 11160954054611782211 10071795025240457628 "
-    "1536670498623767300 1990681379653546894 14312739227381277138 "
-    "9695213786215402291 3580182943401628617 12313607438786545484 "
-    "12864141705426648443 692371170805382036 13125536612285239925 "
-    "9372929234002877092 9510419002221032820 3766423210161674061 "
-    "3230494342413727261 5934351496112072933 2863344864469097044 "
-    "10884720908958139042 4127946927340597780 9960629658622711061 "
-    "14818231351611083857 6346099615454582885 12601407558879514692 "
-    "17544105005554819865 1096648950913019831 9969868157190185788 "
-    "12908611252828823970 5941129863397152719 16168953427117105234 "
-    "12304862402025196697 7781571759256122972 13289545261301048078 "
-    "11013924305579914035 8894422550580466537 7506958826675805512 "
-    "14280817252893250439 2745266616282182732 17277225453205013047 "
-    "14335499905842065319 11961295941780577536 18072890757248426766 "
-    "1124506606842606920 17329960125355005185 13052066741624159010 "
-    "5704650516221677069 16588425097127709212 11813406583737887980 "
-    "16359723311775411283 13451679937172566665 5997753207634594468 "
-    "10656019008205694109 13074690560123889048 14811648124990806194 "
-    "7809449463531558024 5637787273252434288 16515135932856030468 "
-    "3755600163640125044 1153929634172103321 11071014283313196016 "
-    "11114640359080035583 15390782025450330559 14097530518721927499 "
-    "14776783751481098767 7863618667181998233 11513855295425132436 "
-    "4736362806980864724 5426549653049482466 10310828122060887518 "
-    "4450247941008370560 9781171949844602811 6086471549040450051 "
-    "6033923116291003194 17669843285681524740 17610378273478865070 "
-    "12152320288002263294 6525449125788834221 5125338396312613396 "
-    "9300082688721166268 959242243476884691 6379729471368150249 "
-    "16379772457647614853 13454012201619761707 2392678998182524851 "
-    "12693758700673471007 1138892516507202079 15673908144065302514 "
-    "5299581449349386824 7590792025124859454 9863745357571267780 "
-    "357345312340746112 17610247870912740564 16347431861769737095 "
-    "11348828299228888092 7220122803951857490 7038822841708464676 "
-    "9912221445023094105 5767425533670320190 6442622362743049032 "
-    "17525461567869579503 4211095256108567696 14862334876401617373 "
-    "2866362449624104511 11413742225973279461 13015745308569358847 "
-    "5191760666536228849 17188167935010684492 18321678815621002079 "
-    "13046333455321624690 3995310719038261500 10661051209947341089 "
-    "7965203671238327266 16590917686161852835 3897101637344795372 "
-    "1538303624766151695 10893225639252940698 5386335660311332214 "
-    "5174479122000384061 17378437193516866561 13629320139302700770 "
-    "10144210341964027265 12816799659000064406 3711797003976467729 "
-    "5079455890584507977 432599929275804205 10435019529328454317 "
-    "5310854040535477246 15941464006450157396 2192067269367387270 "
-    "9782967689631091633 6777452250210540865 18067909703113078220 "
-    "17525143578810667971 87448662189824165 412530897284614413 "
-    "12066785122245373863 13073154860645125438 18282514257379582711 "
-    "8460374908111578570 15967512883067334502 9620430172798103891 "
-    "1264976185047610409 15426838192579528907 9878758812321441445 "
-    "18029992505662864846 9383699886128308360 14538949787806484635 "
-    "16958815135940772668 980481467951972605 3059030058898313960 "
-    "11497544574740915907 8385450996898478663 15571176518627282350";
-    unsigned as[] = {3, 5, 7};
-    std::seed_seq sseq(as, as+3);
-    std::mt19937_64 e1(sseq);
-    std::ostringstream os;
-    os << e1;
-    assert(os.str() == a);
+void test2() {
+  const char* a =
+#ifndef _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      "17895233847644109640 14665081038668852234 15987230621890949369 "
+      "13796324649827964148 1670828176732799955 14078505147839608672 "
+      "15497470967856861790 9566640372296747182 7839811585250789315 "
+      "1350068003782415071 5639936432479386921 15925388701147119804 "
+      "17415323390466493342 3892358083553387400 13485903346102334794 "
+      "16926193162581531132 2711398308226914244 12515538113016451944 "
+      "13856492368211347055 17968472785843263993 16129117710261673051 "
+      "13041638543181171650 8985803498136324582 401118717516975186 "
+      "7221524024767248666 13502820261231663498 8665119664667372350 "
+      "4506836866186850029 14762924585995667460 7305266389803732087 "
+      "9135600275824854713 8421320656548229332 14585303533697565624 "
+      "13062167967956981222 15285580395823625260 17451830328116466708 "
+      "17363259891080004456 13238190509560321740 10142215382802200927 "
+      "3224236118694175902 15382517208605932923 10818882444738383326 "
+      "16604245792882032433 10223425285179753002 1342432445403828765 "
+      "4958539418185107403 9374581143772158175 7135181273441366910 "
+      "5853026900476841261 8382327930174454355 2371969498930803266 "
+      "16961635468480846337 377077518789053577 17661790013255465310 "
+      "317500018453124832 3604586262706855295 13340007089026272125 "
+      "7614051306438090372 17819007364113857386 15193526497275633437 "
+      "6142773218979108210 14883287611587512668 12544132362002344419 "
+      "1247987855434921372 6414191755211735979 7160327288923375132 "
+      "7347937017206972868 17171048313531629893 18230412825496997383 "
+      "10882960195884354661 3270707876715241884 16088870345045208503 "
+      "15454419782166694763 1200609322828949525 10186066554418904177 "
+      "7554892242763986291 8203847521335919011 16855803304338943001 "
+      "16895223408596071476 562183806034700250 17761033068687156643 "
+      "12370482348384718931 17895691979506634040 16028877286272943475 "
+      "6671425930002400146 15167515621662197335 17503579548680921174 "
+      "15910867647138768989 1705705354110203064 12201125760909412022 "
+      "5523403744441352645 4540673037752294406 822888669354888870 "
+      "13012517529113958824 702032511346794490 1245872939048413008 "
+      "18060687614291143943 718002942670251776 14628954120078526945 "
+      "7215746609592654001 15288092036204733967 12507582747898016110 "
+      "8319356319569362772 3835100005166188461 10769229288786702843 "
+      "14682980657311687345 10352054841727718090 13661249361946024317 "
+      "1558696616315734178 9963912474249467679 18213809676410642730 "
+      "7284438284457478230 8013656044128665351 6817107912809760616 "
+      "4989038816564331700 12918068165960947833 9123533477086273623 "
+      "741568181450204257 3801962339733348259 1923812112542486965 "
+      "5884360231397942779 17008459141377852544 6569697353326895092 "
+      "15194386425456240489 9363979514988323850 9212437218544795097 "
+      "5650610605870621879 10315798944006232463 10345822437227504297 "
+      "795589193815296350 11344022765750598871 3193778122705907169 "
+      "16719669104430190089 14918335244853046975 11608293761910939782 "
+      "17290187430985633813 856382712722415618 14819792788008454203 "
+      "10571145147196955435 12858063129221173592 5671819431516788648 "
+      "17837836658827607239 14004823010100183722 9067196699747632668 "
+      "441015230260308492 3444946658209715644 1825101023084664281 "
+      "11133092574473850025 12746373758552339264 10154162549097295782 "
+      "14922316177042921089 12679802872389794491 8797747037480461410 "
+      "13907752811248535439 5652405835046458389 3181711594575177977 "
+      "15495242712294857418 6049158560807439366 952771601159099159 "
+      "4587095466254740009 11160954054611782211 10071795025240457628 "
+      "1536670498623767300 1990681379653546894 14312739227381277138 "
+      "9695213786215402291 3580182943401628617 12313607438786545484 "
+      "12864141705426648443 692371170805382036 13125536612285239925 "
+      "9372929234002877092 9510419002221032820 3766423210161674061 "
+      "3230494342413727261 5934351496112072933 2863344864469097044 "
+      "10884720908958139042 4127946927340597780 9960629658622711061 "
+      "14818231351611083857 6346099615454582885 12601407558879514692 "
+      "17544105005554819865 1096648950913019831 9969868157190185788 "
+      "12908611252828823970 5941129863397152719 16168953427117105234 "
+      "12304862402025196697 7781571759256122972 13289545261301048078 "
+      "11013924305579914035 8894422550580466537 7506958826675805512 "
+      "14280817252893250439 2745266616282182732 17277225453205013047 "
+      "14335499905842065319 11961295941780577536 18072890757248426766 "
+      "1124506606842606920 17329960125355005185 13052066741624159010 "
+      "5704650516221677069 16588425097127709212 11813406583737887980 "
+      "16359723311775411283 13451679937172566665 5997753207634594468 "
+      "10656019008205694109 13074690560123889048 14811648124990806194 "
+      "7809449463531558024 5637787273252434288 16515135932856030468 "
+      "3755600163640125044 1153929634172103321 11071014283313196016 "
+      "11114640359080035583 15390782025450330559 14097530518721927499 "
+      "14776783751481098767 7863618667181998233 11513855295425132436 "
+      "4736362806980864724 5426549653049482466 10310828122060887518 "
+      "4450247941008370560 9781171949844602811 6086471549040450051 "
+      "6033923116291003194 17669843285681524740 17610378273478865070 "
+      "12152320288002263294 6525449125788834221 5125338396312613396 "
+      "9300082688721166268 959242243476884691 6379729471368150249 "
+      "16379772457647614853 13454012201619761707 2392678998182524851 "
+      "12693758700673471007 1138892516507202079 15673908144065302514 "
+      "5299581449349386824 7590792025124859454 9863745357571267780 "
+      "357345312340746112 17610247870912740564 16347431861769737095 "
+      "11348828299228888092 7220122803951857490 7038822841708464676 "
+      "9912221445023094105 5767425533670320190 6442622362743049032 "
+      "17525461567869579503 4211095256108567696 14862334876401617373 "
+      "2866362449624104511 11413742225973279461 13015745308569358847 "
+      "5191760666536228849 17188167935010684492 18321678815621002079 "
+      "13046333455321624690 3995310719038261500 10661051209947341089 "
+      "7965203671238327266 16590917686161852835 3897101637344795372 "
+      "1538303624766151695 10893225639252940698 5386335660311332214 "
+      "5174479122000384061 17378437193516866561 13629320139302700770 "
+      "10144210341964027265 12816799659000064406 3711797003976467729 "
+      "5079455890584507977 432599929275804205 10435019529328454317 "
+      "5310854040535477246 15941464006450157396 2192067269367387270 "
+      "9782967689631091633 6777452250210540865 18067909703113078220 "
+      "17525143578810667971 87448662189824165 412530897284614413 "
+      "12066785122245373863 13073154860645125438 18282514257379582711 "
+      "8460374908111578570 15967512883067334502 9620430172798103891 "
+      "1264976185047610409 15426838192579528907 9878758812321441445 "
+      "18029992505662864846 9383699886128308360 14538949787806484635 "
+      "16958815135940772668 980481467951972605 3059030058898313960 "
+      "11497544574740915907 8385450996898478663 15571176518627282350"
+#else
+      "12332679912394435791 9453873875014225299 7194476751338084717 15353332195928478249 10481090450137100787 "
+      "16894768970350315699 9140706850585347923 17060753788370102662 5027480258542244180 4237901618226072654 "
+      "1625191769495810967 14151318192590258819 14578513270917221567 1340379920765042993 16984341741367590539 "
+      "17825705796225691974 10460633243946900672 15537248717777931667 18010309519743623944 11238461300079763233 "
+      "5212834616069799861 14803562233233663569 509073377271690301 9868447031443646872 18426739611230355604 "
+      "405337453835701754 2865140403182526171 17022743117531244315 15873574515363721317 1003946026159741321 "
+      "10106235397909731784 7501984743217763859 9608180616803748513 17321043653964822159 138792968844647510 "
+      "13940152250553688762 16186510718789785685 10788084171130680879 3336642670408666887 7310094463387030739 "
+      "5523088258140426915 1285026150975382598 13098435840364629682 6200633715587124711 5088226041080887554 "
+      "11020661496106860750 12793876732801641662 3576577317235426397 15183184978662083277 15861657920389428029 "
+      "470273426362791941 2574629887817053182 13272148377140358694 3004438593777107412 2652718879104414667 "
+      "1834027854782609871 10456539872813643760 6392289299447966013 9267269600134252311 10114120825136775101 "
+      "2198130084042867418 13999435202207229825 14893120434100842814 2852347522408045763 17948795049429383857 "
+      "17441093961240168873 5399621587997711172 12611952947737738867 6103704372959848527 738297531584281114 "
+      "16788685814043292624 6176291056095750530 16428159208694695523 4245608719980075114 12095265841295350099 "
+      "15191360055442418178 8708130367787597793 9264267659253066420 15969277182114773779 17035922951690056341 "
+      "9880719443672114132 15332192661202958624 14150490775662311299 4012807722045209888 8653700056821051719 "
+      "15242621354372282880 17096356791700814452 16018787795979380803 296068005088784708 4048466329605633785 "
+      "8059310313495723083 8563086889008277869 10184519287956394000 6827268421235011709 17346498307575607164 "
+      "6880036350466817261 16153597041588957084 15405131912393208395 12767350280822570901 1052885868672540599 "
+      "4188094412071243913 1126222035238982882 9191486289101564624 10803640833835240836 3601208394977378668 "
+      "4766334496475281242 7832706214999682994 16891444816728487342 17860841416138565015 12370215439434878681 "
+      "9232823972512380788 3640121802277180952 65633375613217608 13600365462550545275 17948579528827407927 "
+      "765525796436836449 13572020505370210613 11924972776661436983 4057541052165744297 17481588995966250594 "
+      "8006213088863539837 9739468715064136699 2716054689781779647 16549004007254321028 4910543003771498131 "
+      "13361071256636157603 15297097731894649703 17624276599277544080 15499557723143597570 17957222608348359804 "
+      "2778177927288936525 13637160153035836011 5098746756051510535 4146145678876364034 2247309035465090095 "
+      "9569124281048572866 12809922376030463313 15451507763993230705 17163206508771813131 17029710194125776618 "
+      "8061947308014036349 1058089520886506655 13597933493965622376 13504098358025602133 1333487301584903927 "
+      "8426860107195956174 3508319831781619992 13220104443771667566 15729810053761292123 4229931263400540722 "
+      "4166080451531777928 6512350709932085964 11707745856776186054 5335698181984744736 16601891716487755730 "
+      "14845047860332527819 13881061187353168588 2290627681380867569 15008794942145533328 9207539692861337313 "
+      "15854225885502222653 12652120737459513649 8391328319785893524 16218535624802384239 10635204123630136612 "
+      "14741489403103018051 1034273171946262577 2598761621738522071 10597540332677591413 11769725249744073186 "
+      "12729182262319592409 13143766812621949948 7337533679729790607 8700823377064447860 6528412901068836499 "
+      "12887771189408551435 6596057164621086436 9725510839601037147 12589333640740308830 8713351353100408409 "
+      "3200078174512985423 3001899537338861016 14245433087720289982 2342459702085298473 15840929965350904315 "
+      "5242384259319523672 6976265022106569670 4694429153464034874 4640016838764614916 11889897715595570593 "
+      "3752569812323601873 2900819842622530250 1859087250745968648 12106047926770169995 12635768141780418349 "
+      "460915272050913013 16909625031804250705 15219281218948426364 15473817896328511538 356510218273447008 "
+      "4322019207571293094 3044055123444388775 14541219878139834671 16010790916272798386 17663395908622954435 "
+      "12611516608169911627 16319922669204576197 16662253955344225107 16530145161795660660 13055268744196885107 "
+      "7863493504777224455 4884882190835979670 17860099797924912564 7963955360230183301 12071138257120882069 "
+      "18357891564140620935 12793730149183942271 14575396984169788025 3998777626616457896 16107243833817385461 "
+      "2800961891557584893 2799945392029798727 11053512580047143649 11067103105271293369 1968075203485277477 "
+      "3090469630341592171 14797167942499219775 1314244307340344386 5197117610141487191 14715472132315901058 "
+      "10223376637313339598 18090093248597656064 162846477809223606 18060308085200244427 4381704277003259436 "
+      "13919578743352454047 12290099036217748490 2399520646070724256 8630374525153030174 12607432526554525963 "
+      "13596968856308825531 4310477479909840944 5249608228653410962 3699954346213058725 287328857418258109 "
+      "6119686667148845021 5412656704791399252 4791204075127203599 14494596012449800144 6684909344487566871 "
+      "4578857374794350422 3320300604108137187 12579270049709840437 16704843247071205209 3817825700702948560 "
+      "5357944831944862632 1304382350048285997 10815164501630203260 438468935207234712 4455248787965292675 "
+      "16418065713664649114 16484487561423541185 10845743900807077540 376195122763577279 15264985897394715313 "
+      "7633016332459855519 18374759716909813581 7494762658573963878 12561201922274984497 17674342236666928778 "
+      "8885969173395223311 8755229097686715671 1319918912921290459 12669348064964076026 8347713925484594194 "
+      "7131861330014060437 18013870281775146388 18428665316372779146 14853358900946804630 11802355386662327119 "
+      "12234197935566505458 1562495104086441070 4789216534718205848 4797493321171570223 11489013455481650173 "
+      "15991479755025557462 5193680184864207374 538012454739572314 12691714223537549902 1625023157851387876 "
+      "15523181006631146535 5212032641878267513 314342477903124542 7039243305239188299 13660860177517273492 "
+      "279600177346432632 1242305966848399256 3754792367662117676 7456833993243708712 5524526481950173096 "
+      "12629250495102362491 12320658947410481525 8375636660933855231 14586189041466889258 3338611085276113663 "
+      "6796460652093029292 17977915623844806175 6678041303167794104 162629150799512142 12738967623757853162 "
+      "15873580300066345221 1661173500922285893"
+#endif // _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+      ;
+  unsigned as[] = {3, 5, 7};
+  std::seed_seq sseq(as, as + 3);
+  std::mt19937_64 e1(sseq);
+  std::ostringstream os;
+  os << e1;
+  LIBCPP_ASSERT(os.str() == a);
+  (void)a;
 }
 
-int main(int, char**)
-{
-    test1();
-    test2();
+int main(int, char**) {
+  test1();
+  test2();
 
   return 0;
 }

diff  --git a/libcxx/utils/libcxx/test/features/libcxx_macros.py b/libcxx/utils/libcxx/test/features/libcxx_macros.py
index a3b32a9c88051..bcc081e2dc2de 100644
--- a/libcxx/utils/libcxx/test/features/libcxx_macros.py
+++ b/libcxx/utils/libcxx/test/features/libcxx_macros.py
@@ -35,6 +35,7 @@
     "_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING": "libcpp-abi-no-compressed-pair-padding",
     "_LIBCPP_PSTL_BACKEND_LIBDISPATCH": "libcpp-pstl-backend-libdispatch",
     "_LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE": "libcpp-abi-use-small-deque-block-size",
+    "_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine",
 }
 for macro, feature in macros.items():
     features.append(


        

From libcxx-commits at lists.llvm.org  Sun Jul  5 10:40:51 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 10:40:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a4a9723.01f0a1da.2016e9.3767@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Sun Jul  5 10:48:42 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 10:48:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ThreadSafety] Add thread safety
 annotations for variadic std::scoped_lock (PR #204462)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204462@github.com>
Message-ID: <6a4a98fa.ff037943.3bf0e2.29ad@mx.google.com>


philnik777 wrote:

> Just an FYI, I rebased the branch off latest main; there was a failure in the checks last time: https://github.com/llvm/llvm-project/actions/runs/28596507647 @philnik777

Just ignore it. It's currently failing.

https://github.com/llvm/llvm-project/pull/204462

From libcxx-commits at lists.llvm.org  Sun Jul  5 11:02:11 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 11:02:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4a9c23.a87eb5dc.1d432d.a8c2@mx.google.com>


================
@@ -164,6 +166,8 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
 
 template <class _Rep, class _Period>
 class duration {
+  static_assert(!is_const<_Rep>::value, "A duration representation can not be const-qualified");
----------------
philnik777 wrote:

Can we use `__is_unqualified` instead?

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 11:02:37 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 11:02:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Clean up windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a4a9c3d.92d2873c.38c951.f9d4@mx.google.com>


https://github.com/philnik777 ready_for_review https://github.com/llvm/llvm-project/pull/207577

From libcxx-commits at lists.llvm.org  Sun Jul  5 11:03:14 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 11:03:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Clean up windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a4a9c62.da8bafd3.2e1688.9d1f@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

We have multiple macros for different Windows. However, most of them don't actually define different things. We can condense the macros to
- `_WIN32` directly if it only depends on windows, or
- `_LIBCPP_LIBC_MSVCRT` (which is equivalent to the old `_LIBCPP_MSVCRT`)

This patch drops `_LIBCPP_MSVCRT_LIKE` and `_LIBCPP_WIN32API`, since they are equivalent to `_WIN32`.



---

Patch is 49.54 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/207577.diff


39 Files Affected:

- (modified) libcxx/include/__config (+7-10) 
- (modified) libcxx/include/__filesystem/path.h (+17-17) 
- (modified) libcxx/include/__filesystem/u8path.h (+7-7) 
- (modified) libcxx/include/__locale (+1-1) 
- (modified) libcxx/include/__locale_dir/locale_base_api.h (+1-1) 
- (modified) libcxx/include/__locale_dir/support/windows.h (+1-1) 
- (modified) libcxx/include/__math/gamma.h (+1-1) 
- (modified) libcxx/include/__math/traits.h (+1-1) 
- (modified) libcxx/include/__ostream/print.h (+2-2) 
- (modified) libcxx/include/fstream (+5-5) 
- (modified) libcxx/include/math.h (+5-5) 
- (modified) libcxx/include/print (+6-6) 
- (modified) libcxx/include/stdlib.h (+2-2) 
- (modified) libcxx/include/string.h (+1-1) 
- (modified) libcxx/include/text_encoding (+1-1) 
- (modified) libcxx/include/wchar.h (+3-3) 
- (modified) libcxx/src/chrono.cpp (+4-4) 
- (modified) libcxx/src/filesystem/directory_iterator.cpp (+2-2) 
- (modified) libcxx/src/filesystem/error.h (+2-2) 
- (modified) libcxx/src/filesystem/file_descriptor.h (+7-7) 
- (modified) libcxx/src/filesystem/filesystem_clock.cpp (+3-3) 
- (modified) libcxx/src/filesystem/format_string.h (+1-1) 
- (modified) libcxx/src/filesystem/operations.cpp (+7-7) 
- (modified) libcxx/src/filesystem/path.cpp (+1-1) 
- (modified) libcxx/src/filesystem/path_parser.h (+2-2) 
- (modified) libcxx/src/filesystem/posix_compat.h (+3-3) 
- (modified) libcxx/src/filesystem/time_utils.h (+5-5) 
- (modified) libcxx/src/fstream.cpp (+2-2) 
- (modified) libcxx/src/include/aligned_alloc.h (+2-2) 
- (modified) libcxx/src/include/config_elast.h (+2-2) 
- (modified) libcxx/src/iostream.cpp (+1-1) 
- (modified) libcxx/src/locale.cpp (+20-20) 
- (modified) libcxx/src/print.cpp (+4-4) 
- (modified) libcxx/src/std_stream.h (+2-2) 
- (modified) libcxx/src/string.cpp (+1-1) 
- (modified) libcxx/src/support/win32/locale_win32.cpp (+3-3) 
- (modified) libcxx/src/system_error.cpp (+5-5) 
- (modified) libcxx/src/thread.cpp (+2-2) 
- (modified) libcxx/test/support/count_new.h (+1-1) 


``````````diff
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..bf2cb865fa0e5 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -43,14 +43,11 @@
 #  endif
 
 #  if defined(_WIN32)
-#    define _LIBCPP_WIN32API
 #    define _LIBCPP_SHORT_WCHAR 1
-// Both MinGW and native MSVC provide a "MSVC"-like environment
-#    define _LIBCPP_MSVCRT_LIKE
-// If mingw not explicitly detected, assume using MS C runtime only if
-// a MS compatibility version is specified.
-#    if defined(_MSC_VER) && !defined(__MINGW32__)
-#      define _LIBCPP_MSVCRT // Using Microsoft's C Runtime library
+#    if defined(__MINGW32__)
+#      define _LIBCPP_LIBC_MINGW
+#    else
+#      define _LIBCPP_LIBC_MSVCRT
 #    endif
 #    define _LIBCPP_HAS_OPEN_WITH_WCHAR 1
 #  else
@@ -100,7 +97,7 @@
 #    define _LIBCPP_USING_GETENTROPY
 #  elif defined(__Fuchsia__)
 #    define _LIBCPP_USING_FUCHSIA_CPRNG
-#  elif defined(_LIBCPP_WIN32API)
+#  elif defined(_WIN32)
 #    define _LIBCPP_USING_WIN32_RANDOM
 #  else
 #    define _LIBCPP_USING_DEV_RANDOM
@@ -170,7 +167,7 @@ typedef __char32_t char32_t;
 
 // If we are getting operator new from the MSVC CRT, then allocation overloads
 // for align_val_t were added in 19.12, aka VS 2017 version 15.3.
-#  if defined(_LIBCPP_MSVCRT) && defined(_MSC_VER) && _MSC_VER < 1912
+#  if defined(_LIBCPP_LIBC_MSVCRT) && defined(_MSC_VER) && _MSC_VER < 1912
 #    define _LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION 0
 #  elif defined(_LIBCPP_ABI_VCRUNTIME) && !defined(__cpp_aligned_new)
 // We're deferring to Microsoft's STL to provide aligned new et al. We don't
@@ -227,7 +224,7 @@ typedef __char32_t char32_t;
 // TODO(44575): Switch to C11 thread API when possible.
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#    elif defined(_LIBCPP_WIN32API)
+#    elif defined(_WIN32)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
 #    else
diff --git a/libcxx/include/__filesystem/path.h b/libcxx/include/__filesystem/path.h
index a63c4ee611ebf..f89c829505d61 100644
--- a/libcxx/include/__filesystem/path.h
+++ b/libcxx/include/__filesystem/path.h
@@ -80,7 +80,7 @@ struct __can_convert_char<char32_t> {
 
 template <class _ECharT, __enable_if_t<__can_convert_char<_ECharT>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI bool __is_separator(_ECharT __e) {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   return __e == _ECharT('/') || __e == _ECharT('\\');
 #  else
   return __e == _ECharT('/');
@@ -181,7 +181,7 @@ struct __is_pathable<_Tp, false, true, false> : __is_pathable_char_array<_Tp> {}
 template <class _Tp>
 struct __is_pathable<_Tp, false, false, true> : __is_pathable_iter<_Tp> {};
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 typedef wstring __path_string;
 typedef wchar_t __path_value;
 #  else
@@ -189,7 +189,7 @@ typedef string __path_string;
 typedef char __path_value;
 #  endif
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 _LIBCPP_EXPORTED_FROM_ABI size_t __wide_to_char(const wstring&, char*, size_t);
 _LIBCPP_EXPORTED_FROM_ABI size_t __char_to_wide(const string&, wchar_t*, size_t);
 #  endif
@@ -203,12 +203,12 @@ struct _PathCVT {
   static_assert(__can_convert_char<_ECharT>::value, "Char type not convertible");
 
   typedef __narrow_to_utf8<sizeof(_ECharT) * __CHAR_BIT__> _Narrower;
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
   typedef __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__> _Widener;
 #    endif
 
   _LIBCPP_HIDE_FROM_ABI static void __append_range(__path_string& __dest, _ECharT const* __b, _ECharT const* __e) {
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __b, __e);
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -223,7 +223,7 @@ struct _PathCVT {
     if (__b == __e)
       return;
     basic_string<_ECharT> __tmp(__b, __e);
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __tmp.data(), __tmp.data() + __tmp.length());
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -241,7 +241,7 @@ struct _PathCVT {
     basic_string<_ECharT> __tmp;
     for (; *__b != __sentinel; ++__b)
       __tmp.push_back(*__b);
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __tmp.data(), __tmp.data() + __tmp.length());
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -284,7 +284,7 @@ struct _PathCVT<__path_value> {
   }
 };
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 template <>
 struct _PathCVT<char> {
   _LIBCPP_HIDE_FROM_ABI static void __append_string(__path_string& __dest, const basic_string<char>& __str) {
@@ -369,7 +369,7 @@ struct _PathExport<char8_t> {
 };
 #      endif // _LIBCPP_HAS_CHAR8_T
 #    endif   // _LIBCPP_HAS_LOCALIZATION
-#  endif     // _LIBCPP_WIN32API
+#  endif     // _WIN32
 
 class _LIBCPP_EXPORTED_FROM_ABI path {
   template <class _SourceOrIter, class _Tp = path&>
@@ -382,7 +382,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   using _SourceCVT _LIBCPP_NODEBUG = _PathCVT<_SourceChar<_Tp> >;
 
 public:
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   typedef wchar_t value_type;
   static constexpr value_type preferred_separator = L'\\';
 #  else
@@ -468,7 +468,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
 
 public:
   // appends
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   _LIBCPP_HIDE_FROM_ABI path& operator/=(const path& __p) {
     auto __p_root_name      = __p.__root_name();
     auto __p_root_name_size = __p_root_name.size();
@@ -602,7 +602,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   _LIBCPP_HIDE_FROM_ABI void clear() noexcept { __pn_.clear(); }
 
   _LIBCPP_HIDE_FROM_ABI path& make_preferred() _LIBCPP_LIFETIMEBOUND {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     std::replace(__pn_.begin(), __pn_.end(), L'/', L'\\');
 #  endif
     return *this;
@@ -667,7 +667,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
 
   _LIBCPP_HIDE_FROM_ABI operator string_type() const { return __pn_; }
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::wstring wstring() const { return __pn_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::wstring generic_wstring() const {
@@ -722,7 +722,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
     return __s;
   }
 #    endif // _LIBCPP_HAS_LOCALIZATION
-#  else    /* _LIBCPP_WIN32API */
+#  else    // _WIN32
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::string string() const { return __pn_; }
 #    if _LIBCPP_HAS_CHAR8_T
@@ -775,7 +775,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::u16string generic_u16string() const { return string<char16_t>(); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::u32string generic_u32string() const { return string<char32_t>(); }
 #    endif // _LIBCPP_HAS_LOCALIZATION
-#  endif   /* !_LIBCPP_WIN32API */
+#  endif   // _WIN32
 
 private:
   int __compare(__string_view) const;
@@ -799,7 +799,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_name() const { return string_type(__root_name()); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_directory() const { return string_type(__root_directory()); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_path() const {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     return string_type(__root_path_raw());
 #  else
     return root_name().append(string_type(__root_directory()));
@@ -824,7 +824,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool has_extension() const { return !__extension().empty(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_absolute() const {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     __string_view __root_name_str = __root_name();
     __string_view __root_dir      = __root_directory();
     if (__root_name_str.size() == 2 && __root_name_str[1] == ':') {
diff --git a/libcxx/include/__filesystem/u8path.h b/libcxx/include/__filesystem/u8path.h
index aabd2bbd3c26c..77e7c0d6653da 100644
--- a/libcxx/include/__filesystem/u8path.h
+++ b/libcxx/include/__filesystem/u8path.h
@@ -24,7 +24,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
-#  if !defined(_LIBCPP_WIN32API) || _LIBCPP_HAS_LOCALIZATION
+#  if !defined(_WIN32) || _LIBCPP_HAS_LOCALIZATION
 template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(_InputIt __f, _InputIt __l) {
   static_assert(
@@ -34,7 +34,7 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
           is_same<typename __is_pathable<_InputIt>::__char_type, char>::value,
       "u8path(Iter, Iter) requires Iter have a value_type of type 'char'"
       " or 'char8_t'");
-#    if defined(_LIBCPP_WIN32API)
+#    if defined(_WIN32)
   string __tmp(__f, __l);
   using _CVT = __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__>;
   std::wstring __w;
@@ -43,11 +43,11 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
   return path(__w);
 #    else
   return path(__f, __l);
-#    endif // defined(_LIBCPP_WIN32API)
+#    endif // defined(_WIN32)
 }
-#  endif // !defined(_LIBCPP_WIN32API) || _LIBCPP_HAS_LOCALIZATION
+#  endif // !defined(_WIN32) || _LIBCPP_HAS_LOCALIZATION
 
-#  if defined(_LIBCPP_WIN32API) && _LIBCPP_HAS_LOCALIZATION
+#  if defined(_WIN32) && _LIBCPP_HAS_LOCALIZATION
 template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(_InputIt __f, _NullSentinel) {
   static_assert(
@@ -67,7 +67,7 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
   _CVT()(back_inserter(__w), __tmp.data(), __tmp.data() + __tmp.size());
   return path(__w);
 }
-#  endif // defined(_LIBCPP_WIN32API) && _LIBCPP_HAS_LOCALIZATION
+#  endif // defined(_WIN32) && _LIBCPP_HAS_LOCALIZATION
 
 template <class _Source, __enable_if_t<__is_pathable<_Source>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(const _Source& __s) {
@@ -78,7 +78,7 @@ template <class _Source, __enable_if_t<__is_pathable<_Source>::value, int> = 0>
           is_same<typename __is_pathable<_Source>::__char_type, char>::value,
       "u8path(Source const&) requires Source have a character type of type "
       "'char' or 'char8_t'");
-#  if defined(_LIBCPP_WIN32API)
+#  if defined(_WIN32)
   using _Traits = __is_pathable<_Source>;
   return u8path(std::__unwrap_iter(_Traits::__range_begin(__s)), std::__unwrap_iter(_Traits::__range_end(__s)));
 #  else
diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index bb8f8e7bbf5d9..e0524cf4abc4c 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -352,7 +352,7 @@ public:
 #    else
   static const mask __regex_word = 0x80;
 #    endif
-#  elif defined(_LIBCPP_MSVCRT_LIKE)
+#  elif defined(_WIN32)
   typedef unsigned short mask;
   static const mask space        = _SPACE;
   static const mask print        = _BLANK | _PUNCT | _ALPHA | _DIGIT;
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index f973d95ef244d..017d971ec72ba 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -116,7 +116,7 @@
 #    include <__locale_dir/support/netbsd.h>
 #  elif defined(__OpenBSD__)
 #    include <__locale_dir/support/openbsd.h>
-#  elif defined(_LIBCPP_MSVCRT_LIKE)
+#  elif defined(_WIN32)
 #    include <__locale_dir/support/windows.h>
 #  elif defined(__Fuchsia__)
 #    include <__locale_dir/support/fuchsia.h>
diff --git a/libcxx/include/__locale_dir/support/windows.h b/libcxx/include/__locale_dir/support/windows.h
index 6675cfb740b29..a20b5226738a6 100644
--- a/libcxx/include/__locale_dir/support/windows.h
+++ b/libcxx/include/__locale_dir/support/windows.h
@@ -172,7 +172,7 @@ _LIBCPP_EXPORTED_FROM_ABI const char* __get_locale_encoding(__locale_t __loc);
 //
 
 // the *_l functions are prefixed on Windows, only available for msvcr80+, VS2005+
-#if defined(_LIBCPP_MSVCRT)
+#if defined(_LIBCPP_LIBC_MSVCRT)
 inline _LIBCPP_HIDE_FROM_ABI float __strtof(const char* __nptr, char** __endptr, __locale_t __loc) {
   return ::_strtof_l(__nptr, __endptr, __loc);
 }
diff --git a/libcxx/include/__math/gamma.h b/libcxx/include/__math/gamma.h
index a742ea42d9009..08e09d7cb7aef 100644
--- a/libcxx/include/__math/gamma.h
+++ b/libcxx/include/__math/gamma.h
@@ -62,7 +62,7 @@ inline _LIBCPP_HIDE_FROM_ABI double tgamma(_A1 __x) _NOEXCEPT {
 // declare it differently in the first place: instead use `asm` to get the compiler to call the right
 // function.
 
-#if defined(_LIBCPP_MSVCRT_LIKE) // reentrant version is not available on Windows
+#ifdef _WIN32 // reentrant version is not available on Windows
 
 inline _LIBCPP_HIDE_FROM_ABI double __lgamma_r(double __d) _NOEXCEPT { return __builtin_lgamma(__d); }
 
diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..5dd489c8b0a36 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -188,7 +188,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // TODO: Remove the workaround once UCRT fixes these functions. Note that this doesn't seem planned as of 2025-07 per
 // https://developercommunity.visualstudio.com/t/10294165.
 
-#if defined(_LIBCPP_MSVCRT) && _LIBCPP_STD_VER >= 20
+#if defined(_LIBCPP_LIBC_MSVCRT) && _LIBCPP_STD_VER >= 20
 namespace __ucrt {
 template <class _A1>
   requires is_integral_v<_A1>
diff --git a/libcxx/include/__ostream/print.h b/libcxx/include/__ostream/print.h
index 5b4ab60a45eea..4a519c177b990 100644
--- a/libcxx/include/__ostream/print.h
+++ b/libcxx/include/__ostream/print.h
@@ -89,7 +89,7 @@ _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 #    if _LIBCPP_HAS_UNICODE
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
-#      ifndef _LIBCPP_WIN32API
+#      ifndef _WIN32
   return std::__vprint_nonunicode(__os, __fmt, __args, __write_nl);
 #      else
   FILE* __file = std::__get_ostream_file(__os);
@@ -123,7 +123,7 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo
     __os.__set_badbit_and_consider_rethrow();
   }
 #        endif // _LIBCPP_HAS_EXCEPTIONS
-#      endif   // _LIBCPP_WIN32API
+#      endif   // _WIN32
 }
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..78fcdf254b493 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -222,7 +222,7 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#    if _LIBCPP_STD_VER >= 23 && defined(_LIBCPP_WIN32API)
+#    if _LIBCPP_STD_VER >= 23 && defined(_WIN32)
 _LIBCPP_EXPORTED_FROM_ABI void* __filebuf_windows_native_handle(FILE* __file) noexcept;
 #    endif
 
@@ -236,7 +236,7 @@ public:
   typedef typename traits_type::off_type off_type;
   typedef typename traits_type::state_type state_type;
 #    if _LIBCPP_STD_VER >= 26
-#      if defined(_LIBCPP_WIN32API)
+#      if defined(_WIN32)
   using native_handle_type = void*; // HANDLE
 #      elif __has_include(<unistd.h>)
   using native_handle_type = int; // POSIX file descriptor
@@ -272,7 +272,7 @@ public:
 #    if _LIBCPP_STD_VER >= 26
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() const noexcept {
     _LIBCPP_ASSERT_UNCATEGORIZED(this->is_open(), "File must be opened");
-#      if defined(_LIBCPP_WIN32API)
+#      if defined(_WIN32)
     return std::__filebuf_windows_native_handle(__file_);
 #      elif __has_include(<unistd.h>)
     return fileno(__file_);
@@ -1020,7 +1020,7 @@ basic_filebuf<_CharT, _Traits>::seekoff(off_type __off, ios_base::seekdir __way,
 
 template <class _CharT, class _Traits>
 int basic_filebuf<_CharT, _Traits>::__fseek(FILE* __file, pos_type __offset, int __whence) {
-#    if defined(_LIBCPP_MSVCRT_LIKE)
+#    ifdef _WIN32
   return _fseeki64(__file, __offset, __whence);
 #    elif _LIBCPP_LIBC_NEWLIB
   return fseek(__file, __offset, __whence);
@@ -1031,7 +1031,7 @@ int basic_filebuf<_CharT, _Traits>::__fseek(FILE* __file, pos_type __offset, int
 
 template <class _CharT, class _Traits>
 typename basic_filebuf<_CharT, _Traits>::pos_type basic_filebuf<_CharT, _Traits>::__ftell(FILE* __file) {
-#    if defined(_LIBCPP_MSVCRT_LIKE)
+#    ifdef _WIN32
   return _ftelli64(__file);
 #    elif _LIBCPP_LIBC_NEWLIB
   return ftell(__file);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..d35d305a682eb 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -416,7 +416,7 @@ using std::__math::fpclassify;
 using std::__math::signbit;
 
 // The MSVC runtime already provides these functions as templates
-#      ifndef _LIBCPP_MSVCRT
+#      ifndef _LIBCPP_LIBC_MSVCRT
 using std::__math::isfinite;
 using std::__math::isgreater;
 using std::__math::isgreaterequal;
@@ -427,9 +427,9 @@ using std::__math::islessgreater;
 using std::__math::isnan;
 using std::__math::isnormal;
 using std::__math::isunordered;
-#      endif // _LIBCPP_MSVCRT
+#      endif // _LIBCPP_LIBC_MSVCRT
 
-#      if defined(_LIBCPP_MSVCRT) && _LIBCPP_STD_VER >= 20
+#      if defined(_LIBCPP_LIBC_MSVCRT) && _LIBCPP_STD_VER >= 20
 // MS UCRT incorrectly defines some functions in a way not working with integer types. Until C++20, this was worked
 // around by -fdelayed-template-parsing. Since C++20, we can use standard feature "requires" instead.
 
@@ -446,7 +446,7 @@ using std::__math::__ucrt::islessgreater;
 using std::__math::__ucrt::isnan;
 using std::__math::__ucrt::isnormal;
 using std::__math::__ucrt::isunordered;
-#      endif // defined(_LIBCPP_MSVCRT) && _LIBCPP_STD_VER >= 20
+#      endif // defined(_LIBCPP_LIBC_MSVCRT) && _LIBCPP_STD_VER >= 20
 
 // We have to provide double overloads for <math.h> to work on platforms that don't provide the full set of math
 // functions. To make the overload set work with multiple functions that take the same arguments, we make our overloads
@@ -527,7 +527,7 @@ using std::__math::trunc;
 //
 // and receive the definitions of mathematical constants, even if <math.h>
 // has previously been included.
-#  if defined(_LIBCPP_MSVCRT) && defined(_USE_MATH_DEFINES)
+#  if defined(_LIBCPP_LIBC_MSVCRT) && defined(_USE_MATH_DEFINES)
 #    include_next <math.h>
 #  endif
 
diff --git a/libcxx/include/print b/libcxx/include/print
index cf62743846f42..a9c4ee686f5f9 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -56,7 +56,7 @@ namespace std {
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#    ifdef _LIBCPP_WIN32API
+#    ifdef _WIN32
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
 
@@ -73,7 +73,7 @@ _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
 _LIBCPP_EXPORTED_FROM_ABI void __write_to_windows_console(FILE* __stream, wstring_view __view);
 #      endif // _LIBCPP_HAS_WIDE_CHARACTERS
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
-#    endif   // _LIBCPP_WIN32API
+#    endif   // _WIN32
 
 #    if _LIBCPP_HAS_UNICODE
 // This is the code to transcode UTF-8 to UTF-16. This is used on
@@ -197,7 +197,7 @@ inline constexpr bool __use_unicode_execution_charset = _MSVC_EXECUTION_CHARACTE
 inline constexpr bool __use_unicode_execution_charset = true;
 #    endif
 
-#    ifdef _LIBCPP_WIN32API
+#    ifdef _WIN32
 _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream) {
   // The macro _LIBCPP_TESTING_PRINT_IS_TERMINAL is used to change
   // the behavior in the test. This is not part of the public API.
@@ -207,7 +207,7 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream)
   return std::__is_windows_terminal(__stream);
 #      endif
 }
-#    endif // _LIBCPP_WIN32API
+#    endif // _WIN32
 
 [[noreturn]] _LIBCPP_HIDE_FROM_ABI inline void __handle_out...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/207577

From libcxx-commits at lists.llvm.org  Sun Jul  5 11:46:30 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Sun, 05 Jul 2026 11:46:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a4aa686.92ea0e2a.21f97.9e93@mx.google.com>


================
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// <text_encoding>
+
+// REQUIRES: std-at-least-c++26
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=40000000
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=1000000000
+
+// Our table has 882 aliases exactly, test to make sure that number matches with the total alias count
+// Internally, there are 884 entries with 2 reserved for id::unknown and id::other
+#include <cassert>
+#include <ranges>
+#include <text_encoding>
+
+#include "test_text_encoding.h"
+
+constexpr bool test() {
+  long long sum = 0;
+  for (auto& enc : unique_encoding_data) {
+    std::text_encoding te{std::text_encoding::id(enc.mib)};
+
+    sum += std::ranges::size(te.aliases());
+  }
+
+  assert(sum == 882);
----------------
smallp-o-p wrote:

I assume that `_LIBCPP_HIDE_FROM_ABI` is implicitly (thanks to the change to `_LIBCPP_BEGIN/END_NAMESPACE_STD`) added to the tables in the header, so they shouldn't be a part of the ABI.

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Sun Jul  5 11:46:51 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Sun, 05 Jul 2026 11:46:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a4aa69b.e55dc341.29c9b8.2eff@mx.google.com>


https://github.com/smallp-o-p edited https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Sun Jul  5 11:49:50 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Sun, 05 Jul 2026 11:49:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a4aa74e.92ea0e2a.21f97.e84a@mx.google.com>


https://github.com/smallp-o-p edited https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Sun Jul  5 11:52:58 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Sun, 05 Jul 2026 11:52:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a4aa80a.bb985344.17fb3b.57fb@mx.google.com>


================

----------------
smallp-o-p wrote:

When implementing the test data, I had forgotten that I added `ASCII` as a possible name for `id::ASCII`, which was an extension to match `libstdc++`, and I figured I might as well add it in this change.

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Sun Jul  5 11:54:09 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Sun, 05 Jul 2026 11:54:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a4aa851.fdc477d6.1a5bda.8f08@mx.google.com>


https://github.com/smallp-o-p edited https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Sun Jul  5 11:54:34 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Sun, 05 Jul 2026 11:54:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a4aa86a.27b3b003.1610ae.4907@mx.google.com>


https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/207471

>From f1f732efd7ce293a06aea8d331858a686d24cdb6 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 3 Jul 2026 17:38:47 -0400
Subject: [PATCH 1/2] Inline text_encoding alias offset table std::count to 883

- Add count test to verify the exact number of aliases our table
  contains
---
 libcxx/include/text_encoding                  |  4 +-
 .../text/text_encoding/aliases_count.pass.cpp | 41 +++++++++++++++++++
 .../text_encoding.ctor/id.pass.cpp            |  2 +-
 .../text_encoding.ctor/string_view.pass.cpp   |  2 +-
 .../text_encoding.eq/equal.id.pass.cpp        |  2 +-
 .../text_encoding.aliases_view/empty.pass.cpp |  2 +-
 .../operator-bool.pass.cpp                    |  2 +-
 .../test_text_encoding.h                      |  1 +
 8 files changed, 49 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
 rename libcxx/test/{std/text/text_encoding => support}/test_text_encoding.h (99%)

diff --git a/libcxx/include/text_encoding b/libcxx/include/text_encoding
index da39be92c3dfa..d6063482be74d 100644
--- a/libcxx/include/text_encoding
+++ b/libcxx/include/text_encoding
@@ -792,8 +792,8 @@ private:
       "11\0CP50220\0csCP50220\0";
 
   struct __offset_table {
-    constexpr static unsigned long long __num_aliases =
-        std::count(__aliases_string, __aliases_string + sizeof(__aliases_string), '\0') + 1;
+    constexpr static unsigned long long __num_aliases = 884;
+    // +2 for id::unknown, id::other sentinels
     unsigned short __table[__num_aliases];
   };
 
diff --git a/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp b/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
new file mode 100644
index 0000000000000..365c9fe3b9ea3
--- /dev/null
+++ b/libcxx/test/libcxx/text/text_encoding/aliases_count.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
+//
+//===----------------------------------------------------------------------===//
+
+// <text_encoding>
+
+// REQUIRES: std-at-least-c++26
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=40000000
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=1000000000
+
+// Our table has 882 aliases exactly, test to make sure that number matches with the total alias count
+// Internally, there are 884 entries with 2 reserved for id::unknown and id::other
+#include <cassert>
+#include <ranges>
+#include <text_encoding>
+
+#include "test_text_encoding.h"
+
+constexpr bool test() {
+  long long sum = 0;
+  for (auto& enc : unique_encoding_data) {
+    std::text_encoding te{std::text_encoding::id(enc.mib)};
+
+    sum += std::ranges::size(te.aliases());
+  }
+
+  assert(sum == 882);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp
index bc3d729e770e3..12c85e6994252 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp
@@ -19,7 +19,7 @@
 #include <text_encoding>
 #include <type_traits>
 
-#include "../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp
index c3202f78642f2..032a4d3c08557 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp
@@ -19,7 +19,7 @@
 #include <text_encoding>
 #include <type_traits>
 
-#include "../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp
index 79de614299fa0..c50f539d1527a 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp
@@ -16,7 +16,7 @@
 #include <text_encoding>
 
 #include "test_macros.h"
-#include "../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp
index 367f9216ef5ff..3008e2d27927f 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp
@@ -16,7 +16,7 @@
 #include <ranges>
 #include <text_encoding>
 
-#include "../../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp
index 9d63ff4cdfccd..f122ee5c88e0b 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp
@@ -16,7 +16,7 @@
 #include <ranges>
 #include <text_encoding>
 
-#include "../../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/test_text_encoding.h b/libcxx/test/support/test_text_encoding.h
similarity index 99%
rename from libcxx/test/std/text/text_encoding/test_text_encoding.h
rename to libcxx/test/support/test_text_encoding.h
index e18bc3aaeea15..5cef8b8ab3d0a 100644
--- a/libcxx/test/std/text/text_encoding/test_text_encoding.h
+++ b/libcxx/test/support/test_text_encoding.h
@@ -295,6 +295,7 @@ constexpr inline enc_data all_encoding_data[] = {
     {3, "csASCII"},
     {3, "iso-ir-6"},
     {3, "us"},
+    {3, "ASCII"}, // extension to match libstdc++
     {4, "ISO-8859-1"},
     {4, "ISO_8859-1:1987"},
     {4, "CP819"},

>From 19e8c4af3cb765190b989ba756fbd84eb3830857 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sun, 5 Jul 2026 14:54:20 -0400
Subject: [PATCH 2/2] Address comments

---
 libcxx/include/text_encoding                              | 8 +++-----
 .../test/libcxx/text/text_encoding/aliases_count.pass.cpp | 6 ++++--
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/text_encoding b/libcxx/include/text_encoding
index d6063482be74d..6035927091493 100644
--- a/libcxx/include/text_encoding
+++ b/libcxx/include/text_encoding
@@ -68,7 +68,6 @@ struct text_encoding
 
 #  if _LIBCPP_STD_VER >= 26
 
-#    include <__algorithm/count.h>
 #    include <__algorithm/find_if.h>
 #    include <__algorithm/lower_bound.h>
 #    include <__cstddef/ptrdiff_t.h>
@@ -623,6 +622,8 @@ public:
   static bool environment_is() = delete;
 #    endif
 
+  constexpr static unsigned long long __num_aliases = 884;
+
 private:
   constexpr const __te_data& __get() const { return __entries[__encoding_idx_]; }
 
@@ -792,8 +793,6 @@ private:
       "11\0CP50220\0csCP50220\0";
 
   struct __offset_table {
-    constexpr static unsigned long long __num_aliases = 884;
-    // +2 for id::unknown, id::other sentinels
     unsigned short __table[__num_aliases];
   };
 
@@ -805,8 +804,7 @@ private:
 
     unsigned long long __idx = 3;
 
-    for (unsigned short __pos = 0; __pos < sizeof(__aliases_string) - 1 && __idx < __offset_table::__num_aliases;
-         __pos++) {
+    for (unsigned short __pos = 0; __pos < sizeof(__aliases_string) - 1 && __idx < __num_aliases; __pos++) {
       if (__aliases_string[__pos] == '\0') {
         __aliases.__table[__idx++] = __pos + 1;
       }
diff --git a/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp b/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
index 365c9fe3b9ea3..c5ca1188a0312 100644
--- a/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
+++ b/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
@@ -13,7 +13,9 @@
 // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=1000000000
 
 // Our table has 882 aliases exactly, test to make sure that number matches with the total alias count
-// Internally, there are 884 entries with 2 reserved for id::unknown and id::other
+// Internally, there are a total of 884 entries in our offset table, 882 for the actual aliases,
+// and +2 reserved as sentinels for id::unknown and id::other
+
 #include <cassert>
 #include <ranges>
 #include <text_encoding>
@@ -28,7 +30,7 @@ constexpr bool test() {
     sum += std::ranges::size(te.aliases());
   }
 
-  assert(sum == 882);
+  assert(sum == std::text_encoding::__num_aliases - 2);
 
   return true;
 }


From libcxx-commits at lists.llvm.org  Sun Jul  5 12:26:53 2026
From: libcxx-commits at lists.llvm.org (Vinay Deshmukh via libcxx-commits)
Date: Sun, 05 Jul 2026 12:26:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make `<set>` `std::multiset`
 constexpr as part of P3372R3 (PR #206257)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206257@github.com>
Message-ID: <6a4aaffd.4a342cbc.134cb.7e5b@mx.google.com>


https://github.com/vinay-deshmukh updated https://github.com/llvm/llvm-project/pull/206257

>From c7046abfa4d3aaaf0bce4f8589a50221a46934b1 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 14:46:05 -0500
Subject: [PATCH 01/22] Initial patch from PR#134330

---
 libcxx/include/__functional/hash.h            |   9 +
 .../include/__iterator/erase_if_container.h   |   3 +-
 libcxx/include/__node_handle                  |  52 +-
 .../include/__numeric/saturation_arithmetic.h |  10 +-
 libcxx/include/__tree                         | 912 +++++++++++-------
 .../include/__type_traits/make_transparent.h  |   4 +-
 .../__utility/default_three_way_comparator.h  |   2 +-
 .../lazy_synth_three_way_comparator.h         |  33 +-
 libcxx/include/__utility/try_key_extraction.h |  12 +-
 libcxx/test/support/is_transparent.h          |  13 +-
 libcxx/test/support/poisoned_hash_helper.h    |  10 +-
 libcxx/test/support/private_constructor.h     |  18 +-
 12 files changed, 638 insertions(+), 440 deletions(-)

diff --git a/libcxx/include/__functional/hash.h b/libcxx/include/__functional/hash.h
index f74f25fa6e84b..83bbf1b5e26c3 100644
--- a/libcxx/include/__functional/hash.h
+++ b/libcxx/include/__functional/hash.h
@@ -433,10 +433,13 @@ struct __hash_impl<long double> : __scalar_hash<long double> {
 template <class _Tp>
 struct hash : public __hash_impl<_Tp> {};
 
+#if _LIBCPP_STD_VER >= 17
+
 template <>
 struct hash<nullptr_t> : public __unary_function<nullptr_t, size_t> {
   _LIBCPP_HIDE_FROM_ABI size_t operator()(nullptr_t) const _NOEXCEPT { return 662607004ull; }
 };
+#endif
 
 #ifndef _LIBCPP_CXX03_LANG
 template <class _Key, class _Hash>
@@ -449,12 +452,18 @@ template <class _Key, class _Hash = hash<_Key> >
 using __has_enabled_hash _LIBCPP_NODEBUG =
     integral_constant<bool, __check_hash_requirements<_Key, _Hash>::value && is_default_constructible<_Hash>::value >;
 
+#  if _LIBCPP_STD_VER >= 17
 template <class _Type, class>
 using __enable_hash_helper_imp _LIBCPP_NODEBUG = _Type;
 
 template <class _Type, class... _Keys>
 using __enable_hash_helper _LIBCPP_NODEBUG =
     __enable_hash_helper_imp<_Type, __enable_if_t<__all<__has_enabled_hash<_Keys>::value...>::value> >;
+#  else
+template <class _Type, class...>
+using __enable_hash_helper _LIBCPP_NODEBUG = _Type;
+#  endif
+
 #endif // !_LIBCPP_CXX03_LANG
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__iterator/erase_if_container.h b/libcxx/include/__iterator/erase_if_container.h
index 0f87f50cd1c16..8d92d3f1b9dbe 100644
--- a/libcxx/include/__iterator/erase_if_container.h
+++ b/libcxx/include/__iterator/erase_if_container.h
@@ -22,7 +22,8 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Container, class _Predicate>
-_LIBCPP_HIDE_FROM_ABI typename _Container::size_type __libcpp_erase_if_container(_Container& __c, _Predicate& __pred) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename _Container::size_type
+__libcpp_erase_if_container(_Container& __c, _Predicate& __pred) {
   typename _Container::size_type __old_size = __c.size();
 
   const typename _Container::iterator __last = __c.end();
diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index b20b0c73a0518..58be5b9d4ddd5 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -31,29 +31,29 @@ private:
 public:
   // [container.node.cons], constructors, copy, and assignment
   constexpr node-handle() noexcept : ptr_(), alloc_() {}
-  node-handle(node-handle&&) noexcept;
-  node-handle& operator=(node-handle&&);
+  constexpr node-handle(node-handle&&) noexcept;         // constexpr since C++26
+  constexpr node-handle& operator=(node-handle&&);       // constexpr since C++26
 
   // [container.node.dtor], destructor
-  ~node-handle();
+  constexpr ~node-handle(); // constexpr since C++26
 
   // [container.node.observers], observers
-  value_type& value() const;            // not present for map containers
-  key_type& key() const;                // not present for set containers
-  mapped_type& mapped() const;          // not present for set containers
+  value_type& value() const;             // not present for map containers
+  key_type& key() const;                 // not present for set containers
+  constexpr mapped_type& mapped() const; // not present for set containers, constexpr since C++26
 
-  allocator_type get_allocator() const;
-  explicit operator bool() const noexcept;
-  [[nodiscard]] bool empty() const noexcept; // nodiscard since C++20
+  constexpr allocator_type get_allocator() const;      // constexpr since C++26
+  constexpr explicit operator bool() const noexcept;   // constexpr since C++26
+  [[nodiscard]] constexpr bool empty() const noexcept; // nodiscard since C++20, constexpr since C++26
 
   // [container.node.modifiers], modifiers
-  void swap(node-handle&)
+  constexpr void swap(node-handle&)
     noexcept(ator_traits::propagate_on_container_swap::value ||
-             ator_traits::is_always_equal::value);
+             ator_traits::is_always_equal::value); // constexpr since C++26
 
-  friend void swap(node-handle& x, node-handle& y) noexcept(noexcept(x.swap(y))) {
+  constexpr friend void swap(node-handle& x, node-handle& y) noexcept(noexcept(x.swap(y))) {
     x.swap(y);
-  }
+  } // constexpr since C++26
 };
 
 */
@@ -99,12 +99,12 @@ private:
   __node_pointer_type __ptr_ = nullptr;
   optional<allocator_type> __alloc_;
 
-  _LIBCPP_HIDE_FROM_ABI void __release_ptr() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __release_ptr() {
     __ptr_   = nullptr;
     __alloc_ = std::nullopt;
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __destroy_node_pointer() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __destroy_node_pointer() {
     if (__ptr_ != nullptr) {
       typedef typename __allocator_traits_rebind< allocator_type, _NodeType>::type __node_alloc_type;
       __node_alloc_type __alloc(*__alloc_);
@@ -113,19 +113,20 @@ private:
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI __basic_node_handle(__node_pointer_type __ptr, allocator_type const& __alloc)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  __basic_node_handle(__node_pointer_type __ptr, allocator_type const& __alloc)
       : __ptr_(__ptr), __alloc_(__alloc) {}
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __basic_node_handle() = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __basic_node_handle() = default;
 
-  _LIBCPP_HIDE_FROM_ABI __basic_node_handle(__basic_node_handle&& __other) noexcept
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __basic_node_handle(__basic_node_handle&& __other) noexcept
       : __ptr_(__other.__ptr_), __alloc_(std::move(__other.__alloc_)) {
     __other.__ptr_   = nullptr;
     __other.__alloc_ = std::nullopt;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __basic_node_handle& operator=(__basic_node_handle&& __other) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __basic_node_handle& operator=(__basic_node_handle&& __other) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __alloc_ == std::nullopt || __alloc_traits::propagate_on_container_move_assignment::value ||
             __alloc_ == __other.__alloc_,
@@ -144,13 +145,13 @@ public:
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const { return *__alloc_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type get_allocator() const { return *__alloc_; }
 
-  _LIBCPP_HIDE_FROM_ABI explicit operator bool() const { return __ptr_ != nullptr; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit operator bool() const { return __ptr_ != nullptr; }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool empty() const { return __ptr_ == nullptr; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool empty() const { return __ptr_ == nullptr; }
 
-  _LIBCPP_HIDE_FROM_ABI void swap(__basic_node_handle& __other) noexcept(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(__basic_node_handle& __other) noexcept(
       __alloc_traits::propagate_on_container_swap::value || __alloc_traits::is_always_equal::value) {
     using std::swap;
     swap(__ptr_, __other.__ptr_);
@@ -159,12 +160,12 @@ public:
       swap(__alloc_, __other.__alloc_);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend void
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 friend void
   swap(__basic_node_handle& __a, __basic_node_handle& __b) noexcept(noexcept(__a.swap(__b))) {
     __a.swap(__b);
   }
 
-  _LIBCPP_HIDE_FROM_ABI ~__basic_node_handle() { __destroy_node_pointer(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~__basic_node_handle() { __destroy_node_pointer(); }
 };
 
 template <class _NodeType, class _Derived>
@@ -179,6 +180,7 @@ struct __map_node_handle_specifics {
   using key_type    = __remove_const_t<typename _NodeType::__node_value_type::first_type>;
   using mapped_type = typename _NodeType::__node_value_type::second_type;
 
+  // This method is not constexpr as per the standard.
   _LIBCPP_HIDE_FROM_ABI key_type& key() const {
     return const_cast<key_type&>(static_cast<_Derived const*>(this)->__ptr_->__get_value().first);
   }
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 4491bab2b1479..7a7410b5dea08 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -121,27 +121,27 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Rp __saturate_cast(_Tp __x) noexcept {
 #if _LIBCPP_STD_VER >= 26
 
 template <__signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
   return std::__add_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
   return std::__sub_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
   return std::__mul_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
   return std::__div_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Rp, __signed_or_unsigned_integer _Tp>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
   return std::__saturate_cast<_Rp>(__x);
 }
 
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 694796922c914..5398ff911ec57 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -28,6 +28,8 @@
 #include <__type_traits/copy_cvref.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_constructible.h>
 #include <__type_traits/is_nothrow_assignable.h>
 #include <__type_traits/is_nothrow_constructible.h>
@@ -111,7 +113,7 @@ __root, have a non-null __parent_ field.
 // Returns:  true if __x is a left child of its parent, else false
 // Precondition:  __x != nullptr.
 template <class _NodePtr>
-inline _LIBCPP_HIDE_FROM_ABI bool __tree_is_left_child(_NodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __tree_is_left_child(_NodePtr __x) _NOEXCEPT {
   return __x == __x->__parent_->__left_;
 }
 
@@ -119,7 +121,7 @@ inline _LIBCPP_HIDE_FROM_ABI bool __tree_is_left_child(_NodePtr __x) _NOEXCEPT {
 //    __x is a proper subtree, returns the black height (null counts as 1).  If
 //    __x is an improper subtree, returns 0.
 template <class _NodePtr>
-unsigned __tree_sub_invariant(_NodePtr __x) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 unsigned __tree_sub_invariant(_NodePtr __x) {
   if (__x == nullptr)
     return 1;
   // parent consistency checked by caller
@@ -151,7 +153,7 @@ unsigned __tree_sub_invariant(_NodePtr __x) {
 //    __root == nullptr is a proper tree.  Returns true if __root is a proper
 //    red black tree, else returns false.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI bool __tree_invariant(_NodePtr __root) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __tree_invariant(_NodePtr __root) {
   if (__root == nullptr)
     return true;
   // check __x->__parent_ consistency
@@ -168,7 +170,7 @@ _LIBCPP_HIDE_FROM_ABI bool __tree_invariant(_NodePtr __root) {
 
 // Returns:  pointer to the left-most node under __x.
 template <class _NodePtr>
-inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_min(_NodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_min(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Root node shouldn't be null");
   while (__x->__left_ != nullptr)
     __x = __x->__left_;
@@ -177,7 +179,7 @@ inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_min(_NodePtr __x) _NOEXCEPT {
 
 // Returns:  pointer to the right-most node under __x.
 template <class _NodePtr>
-inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_max(_NodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_max(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Root node shouldn't be null");
   while (__x->__right_ != nullptr)
     __x = __x->__right_;
@@ -186,7 +188,7 @@ inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_max(_NodePtr __x) _NOEXCEPT {
 
 // Returns:  pointer to the next in-order node after __x.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI _NodePtr __tree_next(_NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_next(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   if (__x->__right_ != nullptr)
     return std::__tree_min(__x->__right_);
@@ -201,23 +203,23 @@ _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_next(_NodePtr __x) _NOEXCEPT {
 // to the actual root of the tree through a __left_ pointer. Incrementing the end() pointer is UB, so we can assume that
 // never happens.
 template <class _EndNodePtr, class _NodePtr>
-inline _LIBCPP_HIDE_FROM_ABI _EndNodePtr __tree_next_iter(_NodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _EndNodePtr __tree_next_iter(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   if (__x->__right_ != nullptr)
-    return static_cast<_EndNodePtr>(std::__tree_min(__x->__right_));
+    return std::__static_fancy_pointer_cast<_EndNodePtr>(std::__tree_min(__x->__right_));
   while (!std::__tree_is_left_child(__x))
     __x = __x->__parent_unsafe();
-  return static_cast<_EndNodePtr>(__x->__parent_);
+  return std::__static_fancy_pointer_cast<_EndNodePtr>(__x->__parent_);
 }
 
 // Returns:  pointer to the previous in-order node before __x.
 // Note: __x may be the end node.
 template <class _NodePtr, class _EndNodePtr>
-inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_prev_iter(_EndNodePtr __x) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_prev_iter(_EndNodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   if (__x->__left_ != nullptr)
     return std::__tree_max(__x->__left_);
-  _NodePtr __xx = static_cast<_NodePtr>(__x);
+  _NodePtr __xx = std::__static_fancy_pointer_cast<_NodePtr>(__x);
   while (std::__tree_is_left_child(__xx))
     __xx = __xx->__parent_unsafe();
   return __xx->__parent_unsafe();
@@ -225,7 +227,7 @@ inline _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_prev_iter(_EndNodePtr __x) _NOEXCEP
 
 // Returns:  pointer to a node which has no children
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI _NodePtr __tree_leaf(_NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_leaf(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   while (true) {
     if (__x->__left_ != nullptr) {
@@ -244,7 +246,7 @@ _LIBCPP_HIDE_FROM_ABI _NodePtr __tree_leaf(_NodePtr __x) _NOEXCEPT {
 // Effects:  Makes __x->__right_ the subtree root with __x as its left child
 //           while preserving in-order order.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI void __tree_left_rotate(_NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree_left_rotate(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   _LIBCPP_ASSERT_INTERNAL(__x->__right_ != nullptr, "node should have a right child");
   _NodePtr __y  = __x->__right_;
@@ -263,7 +265,7 @@ _LIBCPP_HIDE_FROM_ABI void __tree_left_rotate(_NodePtr __x) _NOEXCEPT {
 // Effects:  Makes __x->__left_ the subtree root with __x as its right child
 //           while preserving in-order order.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI void __tree_right_rotate(_NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree_right_rotate(_NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
   _LIBCPP_ASSERT_INTERNAL(__x->__left_ != nullptr, "node should have a left child");
   _NodePtr __y = __x->__left_;
@@ -287,7 +289,8 @@ _LIBCPP_HIDE_FROM_ABI void __tree_right_rotate(_NodePtr __x) _NOEXCEPT {
 // Postcondition: __tree_invariant(end_node->__left_) == true.  end_node->__left_
 //                may be different than the value passed in as __root.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI void __tree_balance_after_insert(_NodePtr __root, _NodePtr __x) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+__tree_balance_after_insert(_NodePtr __root, _NodePtr __x) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__root != nullptr, "Root of the tree shouldn't be null");
   _LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Can't attach null node to a leaf");
   __x->__is_black_ = __x == __root;
@@ -343,7 +346,7 @@ _LIBCPP_HIDE_FROM_ABI void __tree_balance_after_insert(_NodePtr __root, _NodePtr
 //                nor any of its children refer to __z.  end_node->__left_
 //                may be different than the value passed in as __root.
 template <class _NodePtr>
-_LIBCPP_HIDE_FROM_ABI void __tree_remove(_NodePtr __root, _NodePtr __z) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree_remove(_NodePtr __root, _NodePtr __z) _NOEXCEPT {
   _LIBCPP_ASSERT_INTERNAL(__root != nullptr, "Root node should not be null");
   _LIBCPP_ASSERT_INTERNAL(__z != nullptr, "The node to remove should not be null");
   _LIBCPP_ASSERT_INTERNAL(std::__tree_invariant(__root), "The tree invariants should hold");
@@ -558,7 +561,7 @@ public:
   using pointer = _Pointer;
   pointer __left_;
 
-  _LIBCPP_HIDE_FROM_ABI __tree_end_node() _NOEXCEPT : __left_() {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_end_node() _NOEXCEPT : __left_(nullptr) {}
 };
 
 template <class _VoidPtr>
@@ -571,11 +574,15 @@ public:
   __end_node_pointer __parent_;
   bool __is_black_;
 
-  _LIBCPP_HIDE_FROM_ABI pointer __parent_unsafe() const { return static_cast<pointer>(__parent_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pointer __parent_unsafe() const {
+    return std::__static_fancy_pointer_cast<pointer>(__parent_);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI void __set_parent(pointer __p) { __parent_ = static_cast<__end_node_pointer>(__p); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __set_parent(pointer __p) {
+    __parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__p);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_node_base()             = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_node_base() = default;
   __tree_node_base(__tree_node_base const&)            = delete;
   __tree_node_base& operator=(__tree_node_base const&) = delete;
 };
@@ -596,7 +603,7 @@ private:
   };
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __node_value_type& __get_value() { return __value_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_value_type& __get_value() { return __value_; }
 #else
 
 private:
@@ -607,10 +614,24 @@ public:
 #endif
 
   template <class _Alloc, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_node(_Alloc& __na, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_node(_Alloc& __na, _Args&&... __args) {
     allocator_traits<_Alloc>::construct(__na, std::addressof(__get_value()), std::forward<_Args>(__args)...);
   }
+
+// This line fails with:
+// libcxx/test-suite-install/include/c++/v1/__memory/construct_at.h:38:49: note: non-literal type
+// `'std::__tree_node<std::__value_type<Key, int>, void *>' cannot be used in a constant expression`
+// during constant evaluation as part of P3372
+// on:
+// 1. AppleClang, which is not yet using clang >= 20.x, which has "fixed" this issue
+// FIXME: when AppleClang is based off of clang >= 20.x
+// FIXME: when Clang-based CI is using clang >= 20.x
+#if (defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 2000) ||                                                        \
+    (defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 2000)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~__tree_node() {}
+#else
   ~__tree_node()                             = delete;
+#endif
   __tree_node(__tree_node const&)            = delete;
   __tree_node& operator=(__tree_node const&) = delete;
 };
@@ -629,14 +650,15 @@ private:
 public:
   bool __value_constructed;
 
-  _LIBCPP_HIDE_FROM_ABI __tree_node_destructor(const __tree_node_destructor&) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_node_destructor(const __tree_node_destructor&) = default;
   __tree_node_destructor& operator=(const __tree_node_destructor&)            = delete;
 
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_node_destructor(allocator_type& __na, bool __val = false) _NOEXCEPT
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_node_destructor(allocator_type& __na, bool __val = false) _NOEXCEPT
       : __na_(__na),
         __value_constructed(__val) {}
 
-  _LIBCPP_HIDE_FROM_ABI void operator()(pointer __p) _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void operator()(pointer __p) _NOEXCEPT {
     if (__value_constructed)
       __alloc_traits::destroy(__na_, std::addressof(__p->__get_value()));
     if (__p)
@@ -673,44 +695,50 @@ public:
   using reference         = value_type&;
   using pointer           = __rebind_pointer_t<_NodePtr, value_type>;
 
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator() _NOEXCEPT : __ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator() _NOEXCEPT : __ptr_(nullptr) {}
 
-  _LIBCPP_HIDE_FROM_ABI reference operator*() const { return __get_np()->__get_value(); }
-  _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reference operator*() const { return __get_np()->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pointer operator->() const {
     return pointer_traits<pointer>::pointer_to(__get_np()->__get_value());
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator& operator++() {
-    __ptr_ = std::__tree_next_iter<__end_node_pointer>(static_cast<__node_base_pointer>(__ptr_));
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator& operator++() {
+    __ptr_ = std::__tree_next_iter<__end_node_pointer>(std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr_));
     return *this;
   }
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator operator++(int) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator operator++(int) {
     __tree_iterator __t(*this);
     ++(*this);
     return __t;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator& operator--() {
-    __ptr_ = static_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator& operator--() {
+    __ptr_ = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
     return *this;
   }
-  _LIBCPP_HIDE_FROM_ABI __tree_iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator operator--(int) {
     __tree_iterator __t(*this);
     --(*this);
     return __t;
   }
 
-  friend _LIBCPP_HIDE_FROM_ABI bool operator==(const __tree_iterator& __x, const __tree_iterator& __y) {
+  friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
+  operator==(const __tree_iterator& __x, const __tree_iterator& __y) {
     return __x.__ptr_ == __y.__ptr_;
   }
-  friend _LIBCPP_HIDE_FROM_ABI bool operator!=(const __tree_iterator& __x, const __tree_iterator& __y) {
+  friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
+  operator!=(const __tree_iterator& __x, const __tree_iterator& __y) {
     return !(__x == __y);
   }
 
 private:
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_iterator(__node_pointer __p) _NOEXCEPT : __ptr_(__p) {}
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_iterator(__end_node_pointer __p) _NOEXCEPT : __ptr_(__p) {}
-  _LIBCPP_HIDE_FROM_ABI __node_pointer __get_np() const { return static_cast<__node_pointer>(__ptr_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_iterator(__node_pointer __p) _NOEXCEPT
+      : __ptr_(std::__static_fancy_pointer_cast<__end_node_pointer>(__p)) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_iterator(__end_node_pointer __p) _NOEXCEPT
+      : __ptr_(__p) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __get_np() const {
+    return std::__static_fancy_pointer_cast<__node_pointer>(__ptr_);
+  }
   template <class, class, class>
   friend class __tree;
   template <class, class, class>
@@ -735,48 +763,55 @@ public:
   using pointer                              = __rebind_pointer_t<_NodePtr, const value_type>;
   using __non_const_iterator _LIBCPP_NODEBUG = __tree_iterator<_Tp, __node_pointer, difference_type>;
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator() _NOEXCEPT : __ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator() _NOEXCEPT : __ptr_(nullptr) {}
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator(__non_const_iterator __p) _NOEXCEPT : __ptr_(__p.__ptr_) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator(__non_const_iterator __p) _NOEXCEPT
+      : __ptr_(__p.__ptr_) {}
 
-  _LIBCPP_HIDE_FROM_ABI reference operator*() const { return __get_np()->__get_value(); }
-  _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reference operator*() const { return __get_np()->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pointer operator->() const {
     return pointer_traits<pointer>::pointer_to(__get_np()->__get_value());
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator& operator++() {
-    __ptr_ = std::__tree_next_iter<__end_node_pointer>(static_cast<__node_base_pointer>(__ptr_));
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator& operator++() {
+    __ptr_ = std::__tree_next_iter<__end_node_pointer>(std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr_));
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator operator++(int) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator operator++(int) {
     __tree_const_iterator __t(*this);
     ++(*this);
     return __t;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator& operator--() {
-    __ptr_ = static_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator& operator--() {
+    __ptr_ = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree_const_iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator operator--(int) {
     __tree_const_iterator __t(*this);
     --(*this);
     return __t;
   }
 
-  friend _LIBCPP_HIDE_FROM_ABI bool operator==(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
+  friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
+  operator==(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
     return __x.__ptr_ == __y.__ptr_;
   }
-  friend _LIBCPP_HIDE_FROM_ABI bool operator!=(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
+  friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
+  operator!=(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
     return !(__x == __y);
   }
 
 private:
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_const_iterator(__node_pointer __p) _NOEXCEPT : __ptr_(__p) {}
-  _LIBCPP_HIDE_FROM_ABI explicit __tree_const_iterator(__end_node_pointer __p) _NOEXCEPT : __ptr_(__p) {}
-  _LIBCPP_HIDE_FROM_ABI __node_pointer __get_np() const { return static_cast<__node_pointer>(__ptr_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_const_iterator(__node_pointer __p) _NOEXCEPT
+      : __ptr_(std::__static_fancy_pointer_cast<__end_node_pointer>(__p)) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_const_iterator(__end_node_pointer __p) _NOEXCEPT
+      : __ptr_(std::__static_fancy_pointer_cast<__end_node_pointer>(__p)) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __get_np() const {
+    return std::__static_fancy_pointer_cast<__node_pointer>(__ptr_);
+  }
 
   template <class, class, class>
   friend class __tree;
@@ -840,63 +875,73 @@ private:
   _LIBCPP_COMPRESSED_PAIR(size_type, __size_, value_compare, __value_comp_);
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __end_node_pointer __end_node() _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __end_node_pointer __end_node() _NOEXCEPT {
     return pointer_traits<__end_node_pointer>::pointer_to(__end_node_);
   }
-  _LIBCPP_HIDE_FROM_ABI __end_node_pointer __end_node() const _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __end_node_pointer __end_node() const _NOEXCEPT {
     return pointer_traits<__end_node_pointer>::pointer_to(const_cast<__end_node_t&>(__end_node_));
   }
-  _LIBCPP_HIDE_FROM_ABI __node_allocator& __node_alloc() _NOEXCEPT { return __node_alloc_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_allocator& __node_alloc() _NOEXCEPT {
+    return __node_alloc_;
+  }
 
 private:
-  _LIBCPP_HIDE_FROM_ABI const __node_allocator& __node_alloc() const _NOEXCEPT { return __node_alloc_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const __node_allocator& __node_alloc() const _NOEXCEPT {
+    return __node_alloc_;
+  }
 
 public:
-  _LIBCPP_HIDE_FROM_ABI allocator_type __alloc() const _NOEXCEPT { return allocator_type(__node_alloc()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type __alloc() const _NOEXCEPT {
+    return allocator_type(__node_alloc());
+  }
 
-  _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __size_; }
-  _LIBCPP_HIDE_FROM_ABI value_compare& value_comp() _NOEXCEPT { return __value_comp_; }
-  _LIBCPP_HIDE_FROM_ABI const value_compare& value_comp() const _NOEXCEPT { return __value_comp_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type size() const _NOEXCEPT { return __size_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_compare& value_comp() _NOEXCEPT { return __value_comp_; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const value_compare& value_comp() const _NOEXCEPT {
+    return __value_comp_;
+  }
 
 public:
-  _LIBCPP_HIDE_FROM_ABI __node_pointer __root() const _NOEXCEPT {
-    return static_cast<__node_pointer>(__end_node()->__left_);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __root() const _NOEXCEPT {
+    return std::__static_fancy_pointer_cast<__node_pointer>(__end_node()->__left_);
   }
 
-  _LIBCPP_HIDE_FROM_ABI __node_base_pointer* __root_ptr() const _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer* __root_ptr() const _NOEXCEPT {
     return std::addressof(__end_node()->__left_);
   }
 
   using iterator       = __tree_iterator<_Tp, __node_pointer, difference_type>;
   using const_iterator = __tree_const_iterator<_Tp, __node_pointer, difference_type>;
 
-  _LIBCPP_HIDE_FROM_ABI explicit __tree(const value_compare& __comp) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree(const value_compare& __comp) _NOEXCEPT_(
       is_nothrow_default_constructible<__node_allocator>::value&& is_nothrow_copy_constructible<value_compare>::value)
       : __size_(0), __value_comp_(__comp) {
     __begin_node_ = __end_node();
   }
 
-  _LIBCPP_HIDE_FROM_ABI explicit __tree(const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree(const allocator_type& __a)
       : __begin_node_(), __node_alloc_(__node_allocator(__a)), __size_(0) {
     __begin_node_ = __end_node();
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree(const value_compare& __comp, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(const value_compare& __comp, const allocator_type& __a)
       : __begin_node_(), __node_alloc_(__node_allocator(__a)), __size_(0), __value_comp_(__comp) {
     __begin_node_ = __end_node();
   }
 
-  _LIBCPP_HIDE_FROM_ABI __tree(const __tree& __t);
-  _LIBCPP_HIDE_FROM_ABI __tree& operator=(const __tree& __t);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(const __tree& __t);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree& operator=(const __tree& __t);
   template <class _ForwardIterator>
-  _LIBCPP_HIDE_FROM_ABI void __assign_unique(_ForwardIterator __first, _ForwardIterator __last);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_unique(_ForwardIterator __first, _ForwardIterator __last);
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void __assign_multi(_InputIterator __first, _InputIterator __last);
-  _LIBCPP_HIDE_FROM_ABI __tree(__tree&& __t) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_multi(_InputIterator __first, _InputIterator __last);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t) _NOEXCEPT_(
       is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value);
-  _LIBCPP_HIDE_FROM_ABI __tree(__tree&& __t, const allocator_type& __a);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t, const allocator_type& __a);
 
-  _LIBCPP_HIDE_FROM_ABI __tree& operator=(__tree&& __t)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree& operator=(__tree&& __t)
       _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value &&
                  ((__node_traits::propagate_on_container_move_assignment::value &&
                    is_nothrow_move_assignable<__node_allocator>::value) ||
@@ -905,23 +950,27 @@ public:
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI ~__tree() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~__tree() {
     static_assert(is_copy_constructible<value_compare>::value, "Comparator must be copy-constructible.");
     destroy(__root());
   }
 
-  _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return iterator(__begin_node_); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return const_iterator(__begin_node_); }
-  _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return iterator(__end_node()); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return const_iterator(__end_node()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT { return iterator(__begin_node_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator begin() const _NOEXCEPT {
+    return const_iterator(__begin_node_);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator end() _NOEXCEPT { return iterator(__end_node()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator end() const _NOEXCEPT {
+    return const_iterator(__end_node());
+  }
 
-  _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type max_size() const _NOEXCEPT {
     return std::min<size_type>(__node_traits::max_size(__node_alloc()), numeric_limits<difference_type >::max());
   }
 
-  _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT;
 
-  _LIBCPP_HIDE_FROM_ABI void swap(__tree& __t)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(__tree& __t)
 #if _LIBCPP_STD_VER <= 11
       _NOEXCEPT_(__is_nothrow_swappable_v<value_compare> &&
                  (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>));
@@ -930,21 +979,22 @@ public:
 #endif
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator __emplace_multi(_Args&&... __args);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __emplace_multi(_Args&&... __args);
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator __emplace_hint_multi(const_iterator __p, _Args&&... __args);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
+  __emplace_hint_multi(const_iterator __p, _Args&&... __args);
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __emplace_unique(_Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> __emplace_unique(_Args&&... __args) {
     return std::__try_key_extraction<key_type>(
         [this](const key_type& __key, _Args&&... __args2) {
           auto [__parent, __child] = __find_equal(__key);
-          __node_pointer __r       = static_cast<__node_pointer>(__child);
+          __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
           bool __inserted          = false;
           if (__child == nullptr) {
             __node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
-            __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
+            __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
             __r        = __h.release();
             __inserted = true;
           }
@@ -953,10 +1003,10 @@ public:
         [this](_Args&&... __args2) {
           __node_holder __h        = __construct_node(std::forward<_Args>(__args2)...);
           auto [__parent, __child] = __find_equal(__h->__get_value());
-          __node_pointer __r       = static_cast<__node_pointer>(__child);
+          __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
           bool __inserted          = false;
           if (__child == nullptr) {
-            __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
+            __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
             __r        = __h.release();
             __inserted = true;
           }
@@ -966,16 +1016,17 @@ public:
   }
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __emplace_hint_unique(const_iterator __p, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
+  __emplace_hint_unique(const_iterator __p, _Args&&... __args) {
     return std::__try_key_extraction<key_type>(
         [this, __p](const key_type& __key, _Args&&... __args2) {
           __node_base_pointer __dummy;
           auto [__parent, __child] = __find_equal(__p, __dummy, __key);
-          __node_pointer __r       = static_cast<__node_pointer>(__child);
+          __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
           bool __inserted          = false;
           if (__child == nullptr) {
             __node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
-            __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
+            __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
             __r        = __h.release();
             __inserted = true;
           }
@@ -985,9 +1036,9 @@ public:
           __node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
           __node_base_pointer __dummy;
           auto [__parent, __child] = __find_equal(__p, __dummy, __h->__get_value());
-          __node_pointer __r       = static_cast<__node_pointer>(__child);
+          __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
           if (__child == nullptr) {
-            __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
+            __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
             __r = __h.release();
           }
           return pair<iterator, bool>(iterator(__r), __child == nullptr);
@@ -995,73 +1046,112 @@ public:
         std::forward<_Args>(__args)...);
   }
 
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
+  __emplace_hint_unique__sfinae(true_type, const_iterator __p, _Args&&... __args) {
+    return __emplace_hint_unique(__p, __args...);
+  }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
+  __emplace_hint_unique__sfinae(false_type, const_iterator, _Args&&...) {
+    // This method body should never be run. It only exists to allow for compilation. See note in
+    // __insert_unique_from_orphaned_node for more information.
+    return pair<iterator, bool>(iterator(__node_pointer()), false);
+  }
+
   template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI void
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   __insert_unique_from_orphaned_node(const_iterator __p, __get_node_value_type_t<_Tp>&& __value) {
-    __emplace_hint_unique(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
+#if _LIBCPP_STD_VER >= 26
+    if (std::is_constant_evaluated() && std::is_copy_constructible_v<decltype(__value.first)>) {
+      // We create a sfinae wrapper method here, because if the __emplace_hint_unique method gets template instantiated
+      // within __insert_unique_from_orphaned_node, the code will fail to compile when the value is not
+      // copy_constructible (even for runtime execution); unless we use `if constexpr`. Given the copy-constructible
+      // code path will be a performance regression, we want to restrict it to only execute during constant evaluation
+      // , hence, we need to delay the template instantiation.
+      __emplace_hint_unique__sfinae(
+          integral_constant< bool, std::is_copy_constructible_v<decltype(__value.first)> >(),
+          __p,
+          __value.first,
+          std::move(__value.second));
+    } else
+#endif
+    {
+      __emplace_hint_unique(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
+    }
   }
 
   template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI void __insert_unique_from_orphaned_node(const_iterator __p, _Tp&& __value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __insert_unique_from_orphaned_node(const_iterator __p, _Tp&& __value) {
     __emplace_hint_unique(__p, std::move(__value));
   }
 
   template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(const_iterator __p, value_type&& __value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __insert_multi_from_orphaned_node(const_iterator __p, value_type&& __value) {
     __emplace_hint_multi(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
   }
 
   template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(const_iterator __p, _Tp&& __value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __insert_multi_from_orphaned_node(const_iterator __p, _Tp&& __value) {
     __emplace_hint_multi(__p, std::move(__value));
   }
 
   template <class _InIter, class _Sent>
-  _LIBCPP_HIDE_FROM_ABI void __insert_range_multi(_InIter __first, _Sent __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __insert_range_multi(_InIter __first, _Sent __last) {
     if (__first == __last)
       return;
 
     if (__root() == nullptr) { // Make sure we always have a root node
-      __insert_node_at(
-          __end_node(), __end_node()->__left_, static_cast<__node_base_pointer>(__construct_node(*__first).release()));
+      __insert_node_at(__end_node(),
+                       __end_node()->__left_,
+                       std::__static_fancy_pointer_cast<__node_base_pointer>(__construct_node(*__first).release()));
       ++__first;
     }
 
-    auto __max_node = static_cast<__node_pointer>(std::__tree_max(static_cast<__node_base_pointer>(__root())));
+    auto __max_node = std::__static_fancy_pointer_cast<__node_pointer>(
+        std::__tree_max(std::__static_fancy_pointer_cast<__node_base_pointer>(__root())));
 
     for (; __first != __last; ++__first) {
       __node_holder __nd = __construct_node(*__first);
       // Always check the max node first. This optimizes for sorted ranges inserted at the end.
       if (!value_comp()(__nd->__get_value(), __max_node->__get_value())) { // __node >= __max_val
-        __insert_node_at(static_cast<__end_node_pointer>(__max_node),
+        __insert_node_at(std::__static_fancy_pointer_cast<__end_node_pointer>(__max_node),
                          __max_node->__right_,
-                         static_cast<__node_base_pointer>(__nd.get()));
+                         std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.get()));
         __max_node = __nd.release();
       } else {
         __end_node_pointer __parent;
         __node_base_pointer& __child = __find_leaf_high(__parent, __nd->__get_value());
-        __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd.release()));
+        __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.release()));
       }
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __node_assign_unique(const value_type& __v, __node_pointer __dest);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
+  __node_assign_unique(const value_type& __v, __node_pointer __dest);
 
-  _LIBCPP_HIDE_FROM_ABI iterator __node_insert_multi(__node_pointer __nd);
-  _LIBCPP_HIDE_FROM_ABI iterator __node_insert_multi(const_iterator __p, __node_pointer __nd);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __node_insert_multi(__node_pointer __nd);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
+  __node_insert_multi(const_iterator __p, __node_pointer __nd);
 
   template <class _InIter, class _Sent>
-  _LIBCPP_HIDE_FROM_ABI void __insert_range_unique(_InIter __first, _Sent __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __insert_range_unique(_InIter __first, _Sent __last) {
     if (__first == __last)
       return;
 
     if (__root() == nullptr) {
-      __insert_node_at(
-          __end_node(), __end_node()->__left_, static_cast<__node_base_pointer>(__construct_node(*__first).release()));
+      __insert_node_at(__end_node(),
+                       __end_node()->__left_,
+                       std::__static_fancy_pointer_cast<__node_base_pointer>(__construct_node(*__first).release()));
       ++__first;
     }
 
-    auto __max_node = static_cast<__node_pointer>(std::__tree_max(static_cast<__node_base_pointer>(__root())));
+    auto __max_node = std::__static_fancy_pointer_cast<__node_pointer>(
+        std::__tree_max(std::__static_fancy_pointer_cast<__node_base_pointer>(__root())));
 
     using __reference = decltype(*__first);
 
@@ -1070,29 +1160,31 @@ public:
           [this, &__max_node](const key_type& __key, __reference&& __val) {
             if (value_comp()(__max_node->__get_value(), __key)) { // __key > __max_node
               __node_holder __nd = __construct_node(std::forward<__reference>(__val));
-              __insert_node_at(static_cast<__end_node_pointer>(__max_node),
+              __insert_node_at(std::__static_fancy_pointer_cast<__end_node_pointer>(__max_node),
                                __max_node->__right_,
-                               static_cast<__node_base_pointer>(__nd.get()));
+                               std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.get()));
               __max_node = __nd.release();
             } else {
               auto [__parent, __child] = __find_equal(__key);
               if (__child == nullptr) {
                 __node_holder __nd = __construct_node(std::forward<__reference>(__val));
-                __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd.release()));
+                __insert_node_at(
+                    __parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.release()));
               }
             }
           },
           [this, &__max_node](__reference&& __val) {
             __node_holder __nd = __construct_node(std::forward<__reference>(__val));
             if (value_comp()(__max_node->__get_value(), __nd->__get_value())) { // __node > __max_node
-              __insert_node_at(static_cast<__end_node_pointer>(__max_node),
+              __insert_node_at(std::__static_fancy_pointer_cast<__end_node_pointer>(__max_node),
                                __max_node->__right_,
-                               static_cast<__node_base_pointer>(__nd.get()));
+                               std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.get()));
               __max_node = __nd.release();
             } else {
               auto [__parent, __child] = __find_equal(__nd->__get_value());
               if (__child == nullptr) {
-                __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd.release()));
+                __insert_node_at(
+                    __parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.release()));
               }
             }
           },
@@ -1100,62 +1192,67 @@ public:
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI iterator __remove_node_pointer(__node_pointer) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __remove_node_pointer(__node_pointer) _NOEXCEPT;
 
 #if _LIBCPP_STD_VER >= 17
   template <class _NodeHandle, class _InsertReturnType>
-  _LIBCPP_HIDE_FROM_ABI _InsertReturnType __node_handle_insert_unique(_NodeHandle&&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _InsertReturnType __node_handle_insert_unique(_NodeHandle&&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI iterator __node_handle_insert_unique(const_iterator, _NodeHandle&&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
+  __node_handle_insert_unique(const_iterator, _NodeHandle&&);
   template <class _Comp2>
-  _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source);
 
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI iterator __node_handle_insert_multi(_NodeHandle&&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __node_handle_insert_multi(_NodeHandle&&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI iterator __node_handle_insert_multi(const_iterator, _NodeHandle&&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
+  __node_handle_insert_multi(const_iterator, _NodeHandle&&);
   template <class _Comp2>
-  _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source);
 
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(key_type const&);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(key_type const&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const_iterator);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const_iterator);
 #endif
 
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p);
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l);
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI size_type __erase_unique(const _Key& __k);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __erase_unique(const _Key& __k);
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI size_type __erase_multi(const _Key& __k);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __erase_multi(const _Key& __k);
 
-  _LIBCPP_HIDE_FROM_ABI void
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   __insert_node_at(__end_node_pointer __parent, __node_base_pointer& __child, __node_base_pointer __new_node) _NOEXCEPT;
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator find(const _Key& __key) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const _Key& __key) {
     auto [__, __match] = __find_equal(__key);
     if (__match == nullptr)
       return end();
-    return iterator(static_cast<__node_pointer>(__match));
+    return iterator(std::__static_fancy_pointer_cast<__node_pointer>(__match));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator find(const _Key& __key) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const _Key& __key) const {
     auto [__, __match] = __find_equal(__key);
     if (__match == nullptr)
       return end();
-    return const_iterator(static_cast<__node_pointer>(__match));
+    return const_iterator(std::__static_fancy_pointer_cast<__node_pointer>(__match));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI size_type __count_unique(const _Key& __k) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __count_unique(const _Key& __k) const;
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI size_type __count_multi(const _Key& __k) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __count_multi(const _Key& __k) const;
 
   template <bool _LowerBound, class _Key>
-  _LIBCPP_HIDE_FROM_ABI __end_node_pointer __lower_upper_bound_unique_impl(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __end_node_pointer
+  __lower_upper_bound_unique_impl(const _Key& __v) const {
     auto __rt     = __root();
     auto __result = __end_node();
     auto __comp   = __lazy_synth_three_way_comparator<_Compare, _Key, value_type>(value_comp());
@@ -1163,156 +1260,210 @@ public:
       auto __comp_res = __comp(__v, __rt->__get_value());
 
       if (__comp_res.__less()) {
-        __result = static_cast<__end_node_pointer>(__rt);
-        __rt     = static_cast<__node_pointer>(__rt->__left_);
+        __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+        __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
       } else if (__comp_res.__greater()) {
-        __rt = static_cast<__node_pointer>(__rt->__right_);
+        __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
       } else if _LIBCPP_CONSTEXPR (_LowerBound) {
-        return static_cast<__end_node_pointer>(__rt);
+        return std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
       } else {
-        return __rt->__right_ ? static_cast<__end_node_pointer>(std::__tree_min(__rt->__right_)) : __result;
+        return __rt->__right_ ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
+                              : __result;
       }
     }
     return __result;
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator __lower_bound_unique(const _Key& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __lower_bound_unique(const _Key& __v) {
     return iterator(__lower_upper_bound_unique_impl<true>(__v));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator __lower_bound_unique(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __lower_bound_unique(const _Key& __v) const {
     return const_iterator(__lower_upper_bound_unique_impl<true>(__v));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator __upper_bound_unique(const _Key& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __upper_bound_unique(const _Key& __v) {
     return iterator(__lower_upper_bound_unique_impl<false>(__v));
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator __upper_bound_unique(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __upper_bound_unique(const _Key& __v) const {
     return iterator(__lower_upper_bound_unique_impl<false>(__v));
   }
 
 private:
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
   __lower_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result);
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
   __lower_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result) const;
 
 public:
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator __lower_bound_multi(const _Key& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __lower_bound_multi(const _Key& __v) {
     return __lower_bound_multi(__v, __root(), __end_node());
   }
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator __lower_bound_multi(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __lower_bound_multi(const _Key& __v) const {
     return __lower_bound_multi(__v, __root(), __end_node());
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator __upper_bound_multi(const _Key& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __upper_bound_multi(const _Key& __v) {
     return __upper_bound_multi(__v, __root(), __end_node());
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator __upper_bound_multi(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __upper_bound_multi(const _Key& __v) const {
     return __upper_bound_multi(__v, __root(), __end_node());
   }
 
 private:
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI iterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
   __upper_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result);
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI const_iterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
   __upper_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result) const;
 
 public:
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> __equal_range_unique(const _Key& __k);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> __equal_range_unique(const _Key& __k);
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> __equal_range_unique(const _Key& __k) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  __equal_range_unique(const _Key& __k) const;
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> __equal_range_multi(const _Key& __k);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> __equal_range_multi(const _Key& __k);
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> __equal_range_multi(const _Key& __k) const;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  __equal_range_multi(const _Key& __k) const;
 
   using _Dp _LIBCPP_NODEBUG           = __tree_node_destructor<__node_allocator>;
   using __node_holder _LIBCPP_NODEBUG = unique_ptr<__node, _Dp>;
 
-  _LIBCPP_HIDE_FROM_ABI __node_holder remove(const_iterator __p) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_holder remove(const_iterator __p) _NOEXCEPT;
 
   // FIXME: Make this function const qualified. Unfortunately doing so
   // breaks existing code which uses non-const callable comparators.
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<__end_node_pointer, __node_base_pointer&> __find_equal(const _Key& __v);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<__end_node_pointer, __node_base_pointer&>
+  __find_equal(const _Key& __v);
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<__end_node_pointer, __node_base_pointer&> __find_equal(const _Key& __v) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<__end_node_pointer, __node_base_pointer&>
+  __find_equal(const _Key& __v) const {
     return const_cast<__tree*>(this)->__find_equal(__v);
   }
 
   template <class _Key>
-  _LIBCPP_HIDE_FROM_ABI pair<__end_node_pointer, __node_base_pointer&>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<__end_node_pointer, __node_base_pointer&>
   __find_equal(const_iterator __hint, __node_base_pointer& __dummy, const _Key& __v);
 
-  _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __tree& __t) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __copy_assign_alloc(const __tree& __t) {
     __copy_assign_alloc(__t, integral_constant<bool, __node_traits::propagate_on_container_copy_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __tree& __t, true_type) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __copy_assign_alloc(const __tree& __t, true_type) {
     if (__node_alloc() != __t.__node_alloc())
       clear();
     __node_alloc() = __t.__node_alloc();
   }
-  _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __tree&, false_type) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __copy_assign_alloc(const __tree&, false_type) {}
 
 private:
-  _LIBCPP_HIDE_FROM_ABI __node_base_pointer& __find_leaf_low(__end_node_pointer& __parent, const value_type& __v);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer&
+  __find_leaf_low(__end_node_pointer& __parent, const value_type& __v);
 
-  _LIBCPP_HIDE_FROM_ABI __node_base_pointer& __find_leaf_high(__end_node_pointer& __parent, const value_type& __v);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer&
+  __find_leaf_high(__end_node_pointer& __parent, const value_type& __v);
 
-  _LIBCPP_HIDE_FROM_ABI __node_base_pointer&
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer&
   __find_leaf(const_iterator __hint, __end_node_pointer& __parent, const value_type& __v);
 
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI __node_holder __construct_node(_Args&&... __args);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_holder __construct_node(_Args&&... __args);
 
   // TODO: Make this _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_HIDDEN void destroy(__node_pointer __nd) _NOEXCEPT { (__tree_deleter(__node_alloc_))(__nd); }
+  _LIBCPP_HIDDEN _LIBCPP_CONSTEXPR_SINCE_CXX26 void destroy(__node_pointer __nd) _NOEXCEPT {
+    (__tree_deleter(__node_alloc_))(__nd);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign(__tree& __t, false_type);
-  _LIBCPP_HIDE_FROM_ABI void __move_assign(__tree& __t, true_type) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, false_type);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, true_type) _NOEXCEPT_(
       is_nothrow_move_assignable<value_compare>::value&& is_nothrow_move_assignable<__node_allocator>::value);
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__tree& __t)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t)
       _NOEXCEPT_(!__node_traits::propagate_on_container_move_assignment::value ||
                  is_nothrow_move_assignable<__node_allocator>::value) {
     __move_assign_alloc(__t, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__tree& __t, true_type)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t, true_type)
       _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value) {
     __node_alloc() = std::move(__t.__node_alloc());
   }
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__tree&, false_type) _NOEXCEPT {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree&, false_type) _NOEXCEPT {}
+
+  template <class _From, class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_value__sfinae(true_type, __get_node_value_type_t<value_type>& __lhs, _From&& __rhs) {
+    __node_allocator& __na = __node_alloc();
+    __node_traits::destroy(__na, std::addressof(__lhs));
+    __node_traits::construct(__na, std::addressof(__lhs), __rhs.first, __rhs.second);
+  }
+
+  template <class _From, class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_value__sfinae(false_type, __get_node_value_type_t<value_type>&, _From&&) {
+    // This method body should never be run. It only exists to allow for compilation. See note in __assign_value for
+    // more information.
+  }
 
   template <class _From, class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI static void __assign_value(__get_node_value_type_t<value_type>& __lhs, _From&& __rhs) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  __assign_value(__get_node_value_type_t<value_type>& __lhs, _From&& __rhs) {
     using __key_type = __remove_const_t<typename value_type::first_type>;
 
-    // This is technically UB, since the object was constructed as `const`.
-    // Clang doesn't optimize on this currently though.
-    const_cast<__key_type&>(__lhs.first) = const_cast<__copy_cvref_t<_From, __key_type>&&>(__rhs.first);
-    __lhs.second                         = std::forward<_From>(__rhs).second;
+#if _LIBCPP_STD_VER >= 26
+
+    if (std::is_constant_evaluated() && std::is_copy_constructible_v<decltype(__rhs.first)>) {
+      // We use copy, and not "move" as the constraint here because we can NOT move
+      // from `const key_type`, which is how `value_type` is defined for `std::map`
+      // `typedef pair<const key_type, mapped_type> value_type;`
+      // so we must copy it to not perform undefined behavior which
+      // is disallowed during constant evaluation.
+
+      // Furthermore, `const_cast` is not allowed during constant evaluation.
+      // We get around this by deleting `__lhs` and creating a new node in-place
+      // to avoid the `const_cast` when attempting to assign to `__lhs.first`.
+
+      // We create a sfinae wrapper method here, because if the body of the `true_type` overload for
+      // `__assign_value__sfinae()` gets template instantiated within `__assign_value`,
+      // the code will fail to compile when
+      // the value is not copy_constructible (even for runtime execution); unless we use `if constexpr`.
+      // Given that the copy-constructible code path will be a performance regression,
+      // we want to restrict it to only execute during constant evaluation
+      //, we need to delay the template instantiation.
+
+      __assign_value__sfinae(std::integral_constant<bool, std::is_copy_constructible_v<decltype(__rhs.first)>>(),
+                             std::forward<decltype(__lhs)>(__lhs),
+                             std::forward<decltype(__rhs)>(__rhs));
+
+    } else
+#endif
+    {
+      // This is technically UB, since the object was constructed as `const`.
+      // Clang doesn't optimize on this currently though.
+      const_cast<__key_type&>(__lhs.first) = const_cast<__copy_cvref_t<_From, __key_type>&&>(__rhs.first);
+      __lhs.second                         = std::forward<_From>(__rhs).second;
+    }
   }
 
   template <class _To, class _From, class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
@@ -1321,26 +1472,26 @@ private:
   }
 
   struct _DetachedTreeCache {
-    _LIBCPP_HIDE_FROM_ABI explicit _DetachedTreeCache(__tree* __t) _NOEXCEPT
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit _DetachedTreeCache(__tree* __t) _NOEXCEPT
         : __t_(__t),
           __cache_root_(__detach_from_tree(__t)) {
       __advance();
     }
 
-    _LIBCPP_HIDE_FROM_ABI __node_pointer __get() const _NOEXCEPT { return __cache_elem_; }
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __get() const _NOEXCEPT { return __cache_elem_; }
 
-    _LIBCPP_HIDE_FROM_ABI void __advance() _NOEXCEPT {
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __advance() _NOEXCEPT {
       __cache_elem_ = __cache_root_;
       if (__cache_root_) {
         __cache_root_ = __detach_next(__cache_root_);
       }
     }
 
-    _LIBCPP_HIDE_FROM_ABI ~_DetachedTreeCache() {
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~_DetachedTreeCache() {
       __t_->destroy(__cache_elem_);
       if (__cache_root_) {
         while (__cache_root_->__parent_ != nullptr)
-          __cache_root_ = static_cast<__node_pointer>(__cache_root_->__parent_);
+          __cache_root_ = std::__static_fancy_pointer_cast<__node_pointer>(__cache_root_->__parent_);
         __t_->destroy(__cache_root_);
       }
     }
@@ -1349,8 +1500,8 @@ private:
     _DetachedTreeCache& operator=(_DetachedTreeCache const&) = delete;
 
   private:
-    _LIBCPP_HIDE_FROM_ABI static __node_pointer __detach_from_tree(__tree* __t) _NOEXCEPT;
-    _LIBCPP_HIDE_FROM_ABI static __node_pointer __detach_next(__node_pointer) _NOEXCEPT;
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static __node_pointer __detach_from_tree(__tree* __t) _NOEXCEPT;
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static __node_pointer __detach_next(__node_pointer) _NOEXCEPT;
 
     __tree* __t_;
     __node_pointer __cache_root_;
@@ -1363,24 +1514,23 @@ private:
   public:
     using pointer = __node_pointer;
 
-    _LIBCPP_HIDE_FROM_ABI __tree_deleter(__node_allocator& __alloc) : __alloc_(__alloc) {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_deleter(__node_allocator& __alloc) : __alloc_(__alloc) {}
 
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
     _LIBCPP_HIDE_FROM_ABI
 #endif
-    void
-    operator()(__node_pointer __ptr) {
+    _LIBCPP_CONSTEXPR_SINCE_CXX26 void operator()(__node_pointer __ptr) {
       if (!__ptr)
         return;
 
-      (*this)(static_cast<__node_pointer>(__ptr->__left_));
+      (*this)(std::__static_fancy_pointer_cast<__node_pointer>(__ptr->__left_));
 
       auto __right = __ptr->__right_;
 
       __node_traits::destroy(__alloc_, std::addressof(__ptr->__get_value()));
       __node_traits::deallocate(__alloc_, __ptr, 1);
 
-      (*this)(static_cast<__node_pointer>(__right));
+      (*this)(std::__static_fancy_pointer_cast<__node_pointer>(__right));
     }
   };
 
@@ -1391,26 +1541,25 @@ private:
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
   _LIBCPP_HIDE_FROM_ABI
 #endif
-  __node_pointer
-  __copy_construct_tree(__node_pointer __src) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __copy_construct_tree(__node_pointer __src) {
     if (!__src)
       return nullptr;
 
     __node_holder __new_node = __construct_node(__src->__get_value());
 
     unique_ptr<__node, __tree_deleter> __left(
-        __copy_construct_tree(static_cast<__node_pointer>(__src->__left_)), __node_alloc_);
-    __node_pointer __right = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
+        __copy_construct_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__left_)), __node_alloc_);
+    __node_pointer __right = __copy_construct_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__right_));
 
     __node_pointer __new_node_ptr = __new_node.release();
 
     __new_node_ptr->__is_black_ = __src->__is_black_;
-    __new_node_ptr->__left_     = static_cast<__node_base_pointer>(__left.release());
-    __new_node_ptr->__right_    = static_cast<__node_base_pointer>(__right);
+    __new_node_ptr->__left_     = std::__static_fancy_pointer_cast<__node_base_pointer>(__left.release());
+    __new_node_ptr->__right_    = std::__static_fancy_pointer_cast<__node_base_pointer>(__right);
     if (__new_node_ptr->__left_)
-      __new_node_ptr->__left_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
+      __new_node_ptr->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__new_node_ptr);
     if (__new_node_ptr->__right_)
-      __new_node_ptr->__right_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
+      __new_node_ptr->__right_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__new_node_ptr);
     return __new_node_ptr;
   }
 
@@ -1420,8 +1569,7 @@ private:
 #ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
   _LIBCPP_HIDE_FROM_ABI
 #endif
-  __node_pointer
-  __copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
     if (!__src) {
       destroy(__dest);
       return nullptr;
@@ -1432,24 +1580,26 @@ private:
 
     // If we already have a left node in the destination tree, reuse it and copy-assign recursively
     if (__dest->__left_) {
-      __dest->__left_ = static_cast<__node_base_pointer>(__copy_assign_tree(
-          static_cast<__node_pointer>(__dest->__left_), static_cast<__node_pointer>(__src->__left_)));
+      __dest->__left_ = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_assign_tree(
+          std::__static_fancy_pointer_cast<__node_pointer>(__dest->__left_),
+          std::__static_fancy_pointer_cast<__node_pointer>(__src->__left_)));
 
       // Otherwise, we must create new nodes; copy-construct from here on
     } else if (__src->__left_) {
-      auto __new_left       = __copy_construct_tree(static_cast<__node_pointer>(__src->__left_));
-      __dest->__left_       = static_cast<__node_base_pointer>(__new_left);
-      __new_left->__parent_ = static_cast<__end_node_pointer>(__dest);
+      auto __new_left       = __copy_construct_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__left_));
+      __dest->__left_       = std::__static_fancy_pointer_cast<__node_base_pointer>(__new_left);
+      __new_left->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__dest);
     }
 
     // Identical to the left case above, just for the right nodes
     if (__dest->__right_) {
-      __dest->__right_ = static_cast<__node_base_pointer>(__copy_assign_tree(
-          static_cast<__node_pointer>(__dest->__right_), static_cast<__node_pointer>(__src->__right_)));
+      __dest->__right_ = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_assign_tree(
+          std::__static_fancy_pointer_cast<__node_pointer>(__dest->__right_),
+          std::__static_fancy_pointer_cast<__node_pointer>(__src->__right_)));
     } else if (__src->__right_) {
-      auto __new_right       = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
-      __dest->__right_       = static_cast<__node_base_pointer>(__new_right);
-      __new_right->__parent_ = static_cast<__end_node_pointer>(__dest);
+      auto __new_right       = __copy_construct_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__right_));
+      __dest->__right_       = std::__static_fancy_pointer_cast<__node_base_pointer>(__new_right);
+      __new_right->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__dest);
     }
 
     return __dest;
@@ -1458,16 +1608,16 @@ private:
 
 // Precondition:  __size_ != 0
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_pointer
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_pointer
 __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_from_tree(__tree* __t) _NOEXCEPT {
-  __node_pointer __cache                = static_cast<__node_pointer>(__t->__begin_node_);
+  __node_pointer __cache                = std::__static_fancy_pointer_cast<__node_pointer>(__t->__begin_node_);
   __t->__begin_node_                    = __t->__end_node();
   __t->__end_node()->__left_->__parent_ = nullptr;
   __t->__end_node()->__left_            = nullptr;
   __t->__size_                          = 0;
   // __cache->__left_ == nullptr
   if (__cache->__right_ != nullptr)
-    __cache = static_cast<__node_pointer>(__cache->__right_);
+    __cache = std::__static_fancy_pointer_cast<__node_pointer>(__cache->__right_);
   // __cache->__left_ == nullptr
   // __cache->__right_ == nullptr
   return __cache;
@@ -1478,27 +1628,28 @@ __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_from_tree(__tree
 //    __cache->right_ == nullptr
 //    This is no longer a red-black tree
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_pointer
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_pointer
 __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_next(__node_pointer __cache) _NOEXCEPT {
   if (__cache->__parent_ == nullptr)
     return nullptr;
-  if (std::__tree_is_left_child(static_cast<__node_base_pointer>(__cache))) {
+  if (std::__tree_is_left_child(std::__static_fancy_pointer_cast<__node_base_pointer>(__cache))) {
     __cache->__parent_->__left_ = nullptr;
-    __cache                     = static_cast<__node_pointer>(__cache->__parent_);
+    __cache                     = std::__static_fancy_pointer_cast<__node_pointer>(__cache->__parent_);
     if (__cache->__right_ == nullptr)
       return __cache;
-    return static_cast<__node_pointer>(std::__tree_leaf(__cache->__right_));
+    return std::__static_fancy_pointer_cast<__node_pointer>(std::__tree_leaf(__cache->__right_));
   }
   // __cache is right child
   __cache->__parent_unsafe()->__right_ = nullptr;
-  __cache                              = static_cast<__node_pointer>(__cache->__parent_);
+  __cache                              = std::__static_fancy_pointer_cast<__node_pointer>(__cache->__parent_);
   if (__cache->__left_ == nullptr)
     return __cache;
-  return static_cast<__node_pointer>(std::__tree_leaf(__cache->__left_));
+  return std::__static_fancy_pointer_cast<__node_pointer>(std::__tree_leaf(__cache->__left_));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-__tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(const __tree& __t) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>&
+__tree<_Tp, _Compare, _Allocator>::operator=(const __tree& __t) {
   if (this == std::addressof(__t))
     return *this;
 
@@ -1506,14 +1657,15 @@ __tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(
   __copy_assign_alloc(__t);
 
   if (__size_ != 0) {
-    *__root_ptr() = static_cast<__node_base_pointer>(__copy_assign_tree(__root(), __t.__root()));
+    *__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_assign_tree(__root(), __t.__root()));
   } else {
-    *__root_ptr() = static_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
+    *__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
     if (__root())
       __root()->__parent_ = __end_node();
   }
-  __begin_node_ =
-      __end_node()->__left_ ? static_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_)) : __end_node();
+  __begin_node_ = __end_node()->__left_
+                    ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_))
+                    : __end_node();
   __size_ = __t.size();
 
   return *this;
@@ -1521,7 +1673,8 @@ __tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _ForwardIterator>
-void __tree<_Tp, _Compare, _Allocator>::__assign_unique(_ForwardIterator __first, _ForwardIterator __last) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void
+__tree<_Tp, _Compare, _Allocator>::__assign_unique(_ForwardIterator __first, _ForwardIterator __last) {
   using _ITraits     = iterator_traits<_ForwardIterator>;
   using _ItValueType = typename _ITraits::value_type;
   static_assert(
@@ -1541,7 +1694,8 @@ void __tree<_Tp, _Compare, _Allocator>::__assign_unique(_ForwardIterator __first
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _InputIterator>
-void __tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _InputIterator __last) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void
+__tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _InputIterator __last) {
   using _ITraits     = iterator_traits<_InputIterator>;
   using _ItValueType = typename _ITraits::value_type;
   static_assert(
@@ -1560,7 +1714,7 @@ void __tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-__tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
     : __begin_node_(__end_node()),
       __node_alloc_(__node_traits::select_on_container_copy_construction(__t.__node_alloc())),
       __size_(0),
@@ -1568,14 +1722,14 @@ __tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
   if (__t.size() == 0)
     return;
 
-  *__root_ptr()       = static_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
+  *__root_ptr()       = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
   __root()->__parent_ = __end_node();
-  __begin_node_       = static_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
+  __begin_node_       = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
   __size_             = __t.size();
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-__tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
     is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value)
     : __begin_node_(std::move(__t.__begin_node_)),
       __end_node_(std::move(__t.__end_node_)),
@@ -1585,7 +1739,7 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
   if (__size_ == 0)
     __begin_node_ = __end_node();
   else {
-    __end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
+    __end_node()->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__end_node());
     __t.__begin_node_                = __t.__end_node();
     __t.__end_node()->__left_        = nullptr;
     __t.__size_                      = 0;
@@ -1593,7 +1747,7 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-__tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __a)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __a)
     : __node_alloc_(__node_allocator(__a)), __size_(0), __value_comp_(std::move(__t.value_comp())) {
   if (__a == __t.__alloc()) {
     if (__t.__size_ == 0)
@@ -1601,7 +1755,7 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __
     else {
       __begin_node_                    = __t.__begin_node_;
       __end_node()->__left_            = __t.__end_node()->__left_;
-      __end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
+      __end_node()->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__end_node());
       __size_                          = __t.__size_;
       __t.__begin_node_                = __t.__end_node();
       __t.__end_node()->__left_        = nullptr;
@@ -1613,9 +1767,9 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
     _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value&& is_nothrow_move_assignable<__node_allocator>::value) {
-  destroy(static_cast<__node_pointer>(__end_node()->__left_));
+  destroy(std::__static_fancy_pointer_cast<__node_pointer>(__end_node()->__left_));
   __begin_node_ = __t.__begin_node_;
   __end_node_   = __t.__end_node_;
   __move_assign_alloc(__t);
@@ -1624,7 +1778,7 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
   if (__size_ == 0)
     __begin_node_ = __end_node();
   else {
-    __end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
+    __end_node()->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__end_node());
     __t.__begin_node_                = __t.__end_node();
     __t.__end_node()->__left_        = nullptr;
     __t.__size_                      = 0;
@@ -1632,7 +1786,7 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
   if (__node_alloc() == __t.__node_alloc())
     __move_assign(__t, true_type());
   else {
@@ -1653,7 +1807,7 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t)
 #if _LIBCPP_STD_VER <= 11
     _NOEXCEPT_(__is_nothrow_swappable_v<value_compare> &&
                (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>))
@@ -1678,7 +1832,7 @@ void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t)
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::clear() _NOEXCEPT {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::clear() _NOEXCEPT {
   destroy(__root());
   __size_               = 0;
   __begin_node_         = __end_node();
@@ -1689,23 +1843,23 @@ void __tree<_Tp, _Compare, _Allocator>::clear() _NOEXCEPT {
 // Set __parent to parent of null leaf
 // Return reference to null leaf
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
 __tree<_Tp, _Compare, _Allocator>::__find_leaf_low(__end_node_pointer& __parent, const value_type& __v) {
   __node_pointer __nd = __root();
   if (__nd != nullptr) {
     while (true) {
       if (value_comp()(__nd->__get_value(), __v)) {
         if (__nd->__right_ != nullptr)
-          __nd = static_cast<__node_pointer>(__nd->__right_);
+          __nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__right_);
         else {
-          __parent = static_cast<__end_node_pointer>(__nd);
+          __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
           return __nd->__right_;
         }
       } else {
         if (__nd->__left_ != nullptr)
-          __nd = static_cast<__node_pointer>(__nd->__left_);
+          __nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__left_);
         else {
-          __parent = static_cast<__end_node_pointer>(__nd);
+          __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
           return __parent->__left_;
         }
       }
@@ -1719,23 +1873,23 @@ __tree<_Tp, _Compare, _Allocator>::__find_leaf_low(__end_node_pointer& __parent,
 // Set __parent to parent of null leaf
 // Return reference to null leaf
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
 __tree<_Tp, _Compare, _Allocator>::__find_leaf_high(__end_node_pointer& __parent, const value_type& __v) {
   __node_pointer __nd = __root();
   if (__nd != nullptr) {
     while (true) {
       if (value_comp()(__v, __nd->__get_value())) {
         if (__nd->__left_ != nullptr)
-          __nd = static_cast<__node_pointer>(__nd->__left_);
+          __nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__left_);
         else {
-          __parent = static_cast<__end_node_pointer>(__nd);
+          __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
           return __parent->__left_;
         }
       } else {
         if (__nd->__right_ != nullptr)
-          __nd = static_cast<__node_pointer>(__nd->__right_);
+          __nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__right_);
         else {
-          __parent = static_cast<__end_node_pointer>(__nd);
+          __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
           return __nd->__right_;
         }
       }
@@ -1752,7 +1906,8 @@ __tree<_Tp, _Compare, _Allocator>::__find_leaf_high(__end_node_pointer& __parent
 // Set __parent to parent of null leaf
 // Return reference to null leaf
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer& __tree<_Tp, _Compare, _Allocator>::__find_leaf(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
+__tree<_Tp, _Compare, _Allocator>::__find_leaf(
     const_iterator __hint, __end_node_pointer& __parent, const value_type& __v) {
   if (__hint == end() || !value_comp()(*__hint, __v)) // check before
   {
@@ -1761,11 +1916,11 @@ typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer& __tree<_Tp, _Co
     if (__prior == begin() || !value_comp()(__v, *--__prior)) {
       // *prev(__hint) <= __v <= *__hint
       if (__hint.__ptr_->__left_ == nullptr) {
-        __parent = static_cast<__end_node_pointer>(__hint.__ptr_);
+        __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__hint.__ptr_);
         return __parent->__left_;
       } else {
-        __parent = static_cast<__end_node_pointer>(__prior.__ptr_);
-        return static_cast<__node_base_pointer>(__prior.__ptr_)->__right_;
+        __parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__prior.__ptr_);
+        return std::__static_fancy_pointer_cast<__node_base_pointer>(__prior.__ptr_)->__right_;
       }
     }
     // __v < *prev(__hint)
@@ -1780,8 +1935,9 @@ typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer& __tree<_Tp, _Co
 // If __v doesn't exist, return the parent of the null leaf and a reference to the pointer to the null leaf.
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-_LIBCPP_HIDE_FROM_ABI pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
-                           typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
+_LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
+                                   typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
 __tree<_Tp, _Compare, _Allocator>::__find_equal(const _Key& __v) {
   using _Pair = pair<__end_node_pointer, __node_base_pointer&>;
 
@@ -1801,18 +1957,18 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const _Key& __v) {
 
     if (__comp_res.__less()) {
       if (__nd->__left_ == nullptr)
-        return _Pair(static_cast<__end_node_pointer>(__nd), __nd->__left_);
+        return _Pair(std::__static_fancy_pointer_cast<__end_node_pointer>(__nd), __nd->__left_);
 
       __node_ptr = std::addressof(__nd->__left_);
-      __nd       = static_cast<__node_pointer>(__nd->__left_);
+      __nd       = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__left_);
     } else if (__comp_res.__greater()) {
       if (__nd->__right_ == nullptr)
-        return _Pair(static_cast<__end_node_pointer>(__nd), __nd->__right_);
+        return _Pair(std::__static_fancy_pointer_cast<__end_node_pointer>(__nd), __nd->__right_);
 
       __node_ptr = std::addressof(__nd->__right_);
-      __nd       = static_cast<__node_pointer>(__nd->__right_);
+      __nd       = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__right_);
     } else {
-      return _Pair(static_cast<__end_node_pointer>(__nd), *__node_ptr);
+      return _Pair(std::__static_fancy_pointer_cast<__end_node_pointer>(__nd), *__node_ptr);
     }
   }
 }
@@ -1825,8 +1981,9 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const _Key& __v) {
 // If __v doesn't exist, return the parent of the null leaf and a reference to the pointer to the null leaf.
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-_LIBCPP_HIDE_FROM_ABI pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
-                           typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
+_LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
+                                   typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
 __tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_base_pointer& __dummy, const _Key& __v) {
   using _Pair = pair<__end_node_pointer, __node_base_pointer&>;
 
@@ -1837,7 +1994,7 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_ba
       // *prev(__hint) < __v < *__hint
       if (__hint.__ptr_->__left_ == nullptr)
         return _Pair(__hint.__ptr_, __hint.__ptr_->__left_);
-      return _Pair(__prior.__ptr_, static_cast<__node_pointer>(__prior.__ptr_)->__right_);
+      return _Pair(__prior.__ptr_, std::__static_fancy_pointer_cast<__node_pointer>(__prior.__ptr_)->__right_);
     }
     // __v <= *prev(__hint)
     return __find_equal(__v);
@@ -1849,7 +2006,7 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_ba
     if (__next == end() || value_comp()(__v, *__next)) {
       // *__hint < __v < *std::next(__hint)
       if (__hint.__get_np()->__right_ == nullptr)
-        return _Pair(__hint.__ptr_, static_cast<__node_pointer>(__hint.__ptr_)->__right_);
+        return _Pair(__hint.__ptr_, std::__static_fancy_pointer_cast<__node_pointer>(__hint.__ptr_)->__right_);
       return _Pair(__next.__ptr_, __next.__ptr_->__left_);
     }
     // *next(__hint) <= __v
@@ -1857,12 +2014,12 @@ __tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_ba
   }
 
   // else __v == *__hint
-  __dummy = static_cast<__node_base_pointer>(__hint.__ptr_);
+  __dummy = std::__static_fancy_pointer_cast<__node_base_pointer>(__hint.__ptr_);
   return _Pair(__hint.__ptr_, __dummy);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-void __tree<_Tp, _Compare, _Allocator>::__insert_node_at(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__insert_node_at(
     __end_node_pointer __parent, __node_base_pointer& __child, __node_base_pointer __new_node) _NOEXCEPT {
   __new_node->__left_   = nullptr;
   __new_node->__right_  = nullptr;
@@ -1870,14 +2027,14 @@ void __tree<_Tp, _Compare, _Allocator>::__insert_node_at(
   // __new_node->__is_black_ is initialized in __tree_balance_after_insert
   __child = __new_node;
   if (__begin_node_->__left_ != nullptr)
-    __begin_node_ = static_cast<__end_node_pointer>(__begin_node_->__left_);
+    __begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__begin_node_->__left_);
   std::__tree_balance_after_insert(__end_node()->__left_, __child);
   ++__size_;
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class... _Args>
-typename __tree<_Tp, _Compare, _Allocator>::__node_holder
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_holder
 __tree<_Tp, _Compare, _Allocator>::__construct_node(_Args&&... __args) {
   __node_allocator& __na = __node_alloc();
   __node_holder __h(__node_traits::allocate(__na, 1), _Dp(__na));
@@ -1888,35 +2045,35 @@ __tree<_Tp, _Compare, _Allocator>::__construct_node(_Args&&... __args) {
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class... _Args>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+typename __tree<_Tp, _Compare, _Allocator>::iterator _LIBCPP_CONSTEXPR_SINCE_CXX26
 __tree<_Tp, _Compare, _Allocator>::__emplace_multi(_Args&&... __args) {
   __node_holder __h = __construct_node(std::forward<_Args>(__args)...);
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf_high(__parent, __h->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
-  return iterator(static_cast<__node_pointer>(__h.release()));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
+  return iterator(std::__static_fancy_pointer_cast<__node_pointer>(__h.release()));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class... _Args>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+typename __tree<_Tp, _Compare, _Allocator>::iterator _LIBCPP_CONSTEXPR_SINCE_CXX26
 __tree<_Tp, _Compare, _Allocator>::__emplace_hint_multi(const_iterator __p, _Args&&... __args) {
   __node_holder __h = __construct_node(std::forward<_Args>(__args)...);
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf(__p, __parent, __h->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
-  return iterator(static_cast<__node_pointer>(__h.release()));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
+  return iterator(std::__static_fancy_pointer_cast<__node_pointer>(__h.release()));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, bool>
+pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, bool> _LIBCPP_CONSTEXPR_SINCE_CXX26
 __tree<_Tp, _Compare, _Allocator>::__node_assign_unique(const value_type& __v, __node_pointer __nd) {
   auto [__parent, __child] = __find_equal(__v);
-  __node_pointer __r       = static_cast<__node_pointer>(__child);
+  __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
   bool __inserted          = false;
   if (__child == nullptr) {
     __assign_value(__nd->__get_value(), __v);
-    __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd));
+    __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd));
     __r        = __nd;
     __inserted = true;
   }
@@ -1924,39 +2081,39 @@ __tree<_Tp, _Compare, _Allocator>::__node_assign_unique(const value_type& __v, _
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_insert_multi(__node_pointer __nd) {
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf_high(__parent, __nd->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd));
   return iterator(__nd);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_insert_multi(const_iterator __p, __node_pointer __nd) {
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf(__p, __parent, __nd->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd));
   return iterator(__nd);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__remove_node_pointer(__node_pointer __ptr) _NOEXCEPT {
   iterator __r(__ptr);
   ++__r;
   if (__begin_node_ == __ptr)
     __begin_node_ = __r.__ptr_;
   --__size_;
-  std::__tree_remove(__end_node()->__left_, static_cast<__node_base_pointer>(__ptr));
+  std::__tree_remove(__end_node()->__left_, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
   return __r;
 }
 
 #if _LIBCPP_STD_VER >= 17
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle, class _InsertReturnType>
-_LIBCPP_HIDE_FROM_ABI _InsertReturnType
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _InsertReturnType
 __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __nh) {
   if (__nh.empty())
     return _InsertReturnType{end(), false, _NodeHandle()};
@@ -1964,16 +2121,17 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
   __node_pointer __ptr = __nh.__ptr_;
   auto [__parent, __child] = __find_equal(__ptr->__get_value());
   if (__child != nullptr)
-    return _InsertReturnType{iterator(static_cast<__node_pointer>(__child)), false, std::move(__nh)};
+    return _InsertReturnType{
+        iterator(std::__static_fancy_pointer_cast<__node_pointer>(__child)), false, std::move(__nh)};
 
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__ptr));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
   __nh.__release_ptr();
   return _InsertReturnType{iterator(__ptr), true, _NodeHandle()};
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __hint, _NodeHandle&& __nh) {
   if (__nh.empty())
     return end();
@@ -1981,9 +2139,9 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
   __node_pointer __ptr = __nh.__ptr_;
   __node_base_pointer __dummy;
   auto [__parent, __child] = __find_equal(__hint, __dummy, __ptr->__get_value());
-  __node_pointer __r       = static_cast<__node_pointer>(__child);
+  __node_pointer __r       = std::__static_fancy_pointer_cast<__node_pointer>(__child);
   if (__child == nullptr) {
-    __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__ptr));
+    __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
     __r = __ptr;
     __nh.__release_ptr();
   }
@@ -1992,7 +2150,8 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(key_type const& __key) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(key_type const& __key) {
   iterator __it = find(__key);
   if (__it == end())
     return _NodeHandle();
@@ -2001,7 +2160,8 @@ _LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_hand
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const_iterator __p) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const_iterator __p) {
   __node_pointer __np = __p.__get_np();
   __remove_node_pointer(__np);
   return _NodeHandle(__np, __alloc());
@@ -2009,7 +2169,7 @@ _LIBCPP_HIDE_FROM_ABI _NodeHandle __tree<_Tp, _Compare, _Allocator>::__node_hand
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Comp2>
-_LIBCPP_HIDE_FROM_ABI void
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
 __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source) {
   for (iterator __i = __source.begin(); __i != __source.end();) {
     __node_pointer __src_ptr = __i.__get_np();
@@ -2018,27 +2178,27 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_unique(__tree<_Tp, _Comp2
     if (__child != nullptr)
       continue;
     __source.__remove_node_pointer(__src_ptr);
-    __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__src_ptr));
+    __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__src_ptr));
   }
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_multi(_NodeHandle&& __nh) {
   if (__nh.empty())
     return end();
   __node_pointer __ptr = __nh.__ptr_;
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf_high(__parent, __ptr->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__ptr));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
   __nh.__release_ptr();
   return iterator(__ptr);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_multi(const_iterator __hint, _NodeHandle&& __nh) {
   if (__nh.empty())
     return end();
@@ -2046,14 +2206,14 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_multi(const_iterator __h
   __node_pointer __ptr = __nh.__ptr_;
   __end_node_pointer __parent;
   __node_base_pointer& __child = __find_leaf(__hint, __parent, __ptr->__get_value());
-  __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__ptr));
+  __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
   __nh.__release_ptr();
   return iterator(__ptr);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Comp2>
-_LIBCPP_HIDE_FROM_ABI void
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
 __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source) {
   for (iterator __i = __source.begin(); __i != __source.end();) {
     __node_pointer __src_ptr = __i.__get_np();
@@ -2061,14 +2221,15 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_merge_multi(__tree<_Tp, _Comp2,
     __node_base_pointer& __child = __find_leaf_high(__parent, __src_ptr->__get_value());
     ++__i;
     __source.__remove_node_pointer(__src_ptr);
-    __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__src_ptr));
+    __insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__src_ptr));
   }
 }
 
 #endif // _LIBCPP_STD_VER >= 17
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __p) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26
+    typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __p) {
   __node_pointer __np    = __p.__get_np();
   iterator __r           = __remove_node_pointer(__np);
   __node_allocator& __na = __node_alloc();
@@ -2078,7 +2239,7 @@ typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allo
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::iterator
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
 __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __f, const_iterator __l) {
   while (__f != __l)
     __f = erase(__f);
@@ -2087,7 +2248,7 @@ __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __f, const_iterator __l)
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::size_type
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
 __tree<_Tp, _Compare, _Allocator>::__erase_unique(const _Key& __k) {
   iterator __i = find(__k);
   if (__i == end())
@@ -2098,7 +2259,7 @@ __tree<_Tp, _Compare, _Allocator>::__erase_unique(const _Key& __k) {
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::size_type
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
 __tree<_Tp, _Compare, _Allocator>::__erase_multi(const _Key& __k) {
   pair<iterator, iterator> __p = __equal_range_multi(__k);
   size_type __r                = 0;
@@ -2109,16 +2270,16 @@ __tree<_Tp, _Compare, _Allocator>::__erase_multi(const _Key& __k) {
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::size_type
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
 __tree<_Tp, _Compare, _Allocator>::__count_unique(const _Key& __k) const {
   __node_pointer __rt = __root();
   auto __comp         = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp());
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __rt = static_cast<__node_pointer>(__rt->__left_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
       return 1;
   }
@@ -2127,7 +2288,7 @@ __tree<_Tp, _Compare, _Allocator>::__count_unique(const _Key& __k) const {
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::size_type
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
 __tree<_Tp, _Compare, _Allocator>::__count_multi(const _Key& __k) const {
   __end_node_pointer __result = __end_node();
   __node_pointer __rt         = __root();
@@ -2135,78 +2296,85 @@ __tree<_Tp, _Compare, _Allocator>::__count_multi(const _Key& __k) const {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
       return std::distance(
-          __lower_bound_multi(__k, static_cast<__node_pointer>(__rt->__left_), static_cast<__end_node_pointer>(__rt)),
-          __upper_bound_multi(__k, static_cast<__node_pointer>(__rt->__right_), __result));
+          __lower_bound_multi(__k,
+                              std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_),
+                              std::__static_fancy_pointer_cast<__end_node_pointer>(__rt)),
+          __upper_bound_multi(__k, std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_), __result));
   }
   return 0;
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
+__tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
     const _Key& __v, __node_pointer __root, __end_node_pointer __result) {
   while (__root != nullptr) {
     if (!value_comp()(__root->__get_value(), __v)) {
-      __result = static_cast<__end_node_pointer>(__root);
-      __root   = static_cast<__node_pointer>(__root->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
+      __root   = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
     } else
-      __root = static_cast<__node_pointer>(__root->__right_);
+      __root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
   }
   return iterator(__result);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::const_iterator __tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::const_iterator
+__tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
     const _Key& __v, __node_pointer __root, __end_node_pointer __result) const {
   while (__root != nullptr) {
     if (!value_comp()(__root->__get_value(), __v)) {
-      __result = static_cast<__end_node_pointer>(__root);
-      __root   = static_cast<__node_pointer>(__root->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
+      __root   = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
     } else
-      __root = static_cast<__node_pointer>(__root->__right_);
+      __root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
   }
   return const_iterator(__result);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
+__tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
     const _Key& __v, __node_pointer __root, __end_node_pointer __result) {
   while (__root != nullptr) {
     if (value_comp()(__v, __root->__get_value())) {
-      __result = static_cast<__end_node_pointer>(__root);
-      __root   = static_cast<__node_pointer>(__root->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
+      __root   = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
     } else
-      __root = static_cast<__node_pointer>(__root->__right_);
+      __root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
   }
   return iterator(__result);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-typename __tree<_Tp, _Compare, _Allocator>::const_iterator __tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::const_iterator
+__tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
     const _Key& __v, __node_pointer __root, __end_node_pointer __result) const {
   while (__root != nullptr) {
     if (value_comp()(__v, __root->__get_value())) {
-      __result = static_cast<__end_node_pointer>(__root);
-      __root   = static_cast<__node_pointer>(__root->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
+      __root   = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
     } else
-      __root = static_cast<__node_pointer>(__root->__right_);
+      __root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
   }
   return const_iterator(__result);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
-__tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26
+    pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
+    __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) {
   using _Pp                   = pair<iterator, iterator>;
   __end_node_pointer __result = __end_node();
   __node_pointer __rt         = __root();
@@ -2214,22 +2382,23 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
       return _Pp(iterator(__rt),
-                 iterator(__rt->__right_ != nullptr ? static_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
-                                                    : __result));
+                 iterator(__rt->__right_ != nullptr
+                              ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
+                              : __result));
   }
   return _Pp(iterator(__result), iterator(__result));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
-     typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
+_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
+                                   typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
 __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) const {
   using _Pp                   = pair<const_iterator, const_iterator>;
   __end_node_pointer __result = __end_node();
@@ -2238,23 +2407,25 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) const {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
       return _Pp(
           const_iterator(__rt),
-          const_iterator(
-              __rt->__right_ != nullptr ? static_cast<__end_node_pointer>(std::__tree_min(__rt->__right_)) : __result));
+          const_iterator(__rt->__right_ != nullptr
+                             ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
+                             : __result));
   }
   return _Pp(const_iterator(__result), const_iterator(__result));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
-__tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26
+    pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
+    __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) {
   using _Pp                   = pair<iterator, iterator>;
   __end_node_pointer __result = __end_node();
   __node_pointer __rt         = __root();
@@ -2262,22 +2433,23 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
-      return _Pp(
-          __lower_bound_multi(__k, static_cast<__node_pointer>(__rt->__left_), static_cast<__end_node_pointer>(__rt)),
-          __upper_bound_multi(__k, static_cast<__node_pointer>(__rt->__right_), __result));
+      return _Pp(__lower_bound_multi(__k,
+                                     std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_),
+                                     std::__static_fancy_pointer_cast<__end_node_pointer>(__rt)),
+                 __upper_bound_multi(__k, std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_), __result));
   }
   return _Pp(iterator(__result), iterator(__result));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _Key>
-pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
-     typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
+_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
+                                   typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
 __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) const {
   using _Pp                   = pair<const_iterator, const_iterator>;
   __end_node_pointer __result = __end_node();
@@ -2286,35 +2458,37 @@ __tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) const {
   while (__rt != nullptr) {
     auto __comp_res = __comp(__k, __rt->__get_value());
     if (__comp_res.__less()) {
-      __result = static_cast<__end_node_pointer>(__rt);
-      __rt     = static_cast<__node_pointer>(__rt->__left_);
+      __result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
+      __rt     = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
     } else if (__comp_res.__greater())
-      __rt = static_cast<__node_pointer>(__rt->__right_);
+      __rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
     else
-      return _Pp(
-          __lower_bound_multi(__k, static_cast<__node_pointer>(__rt->__left_), static_cast<__end_node_pointer>(__rt)),
-          __upper_bound_multi(__k, static_cast<__node_pointer>(__rt->__right_), __result));
+      return _Pp(__lower_bound_multi(__k,
+                                     std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_),
+                                     std::__static_fancy_pointer_cast<__end_node_pointer>(__rt)),
+                 __upper_bound_multi(__k, std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_), __result));
   }
   return _Pp(const_iterator(__result), const_iterator(__result));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-typename __tree<_Tp, _Compare, _Allocator>::__node_holder
+_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_holder
 __tree<_Tp, _Compare, _Allocator>::remove(const_iterator __p) _NOEXCEPT {
   __node_pointer __np = __p.__get_np();
   if (__begin_node_ == __p.__ptr_) {
     if (__np->__right_ != nullptr)
-      __begin_node_ = static_cast<__end_node_pointer>(__np->__right_);
+      __begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__np->__right_);
     else
-      __begin_node_ = static_cast<__end_node_pointer>(__np->__parent_);
+      __begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__np->__parent_);
   }
   --__size_;
-  std::__tree_remove(__end_node()->__left_, static_cast<__node_base_pointer>(__np));
+  std::__tree_remove(__end_node()->__left_, std::__static_fancy_pointer_cast<__node_base_pointer>(__np));
   return __node_holder(__np, _Dp(__node_alloc(), true));
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI void swap(__tree<_Tp, _Compare, _Allocator>& __x, __tree<_Tp, _Compare, _Allocator>& __y)
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+swap(__tree<_Tp, _Compare, _Allocator>& __x, __tree<_Tp, _Compare, _Allocator>& __y)
     _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
   __x.swap(__y);
 }
diff --git a/libcxx/include/__type_traits/make_transparent.h b/libcxx/include/__type_traits/make_transparent.h
index 4d3207a807fa7..efcce0011f60d 100644
--- a/libcxx/include/__type_traits/make_transparent.h
+++ b/libcxx/include/__type_traits/make_transparent.h
@@ -33,12 +33,12 @@ template <class _Comparator>
 using __make_transparent_t _LIBCPP_NODEBUG = typename __make_transparent<_Comparator>::type;
 
 template <class _Comparator, __enable_if_t<is_same<_Comparator, __make_transparent_t<_Comparator> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Comparator& __as_transparent(_Comparator& __comp) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Comparator& __as_transparent(_Comparator& __comp) {
   return __comp;
 }
 
 template <class _Comparator, __enable_if_t<!is_same<_Comparator, __make_transparent_t<_Comparator> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI __make_transparent_t<_Comparator> __as_transparent(_Comparator&) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __make_transparent_t<_Comparator> __as_transparent(_Comparator&) {
   static_assert(is_empty<_Comparator>::value);
   return __make_transparent_t<_Comparator>();
 }
diff --git a/libcxx/include/__utility/default_three_way_comparator.h b/libcxx/include/__utility/default_three_way_comparator.h
index 92cdce6aae117..3f6bb1d7455b6 100644
--- a/libcxx/include/__utility/default_three_way_comparator.h
+++ b/libcxx/include/__utility/default_three_way_comparator.h
@@ -31,7 +31,7 @@ template <class _LHS, class _RHS>
 struct __default_three_way_comparator<_LHS,
                                       _RHS,
                                       __enable_if_t<is_arithmetic<_LHS>::value && is_arithmetic<_RHS>::value> > {
-  _LIBCPP_HIDE_FROM_ABI static int operator()(_LHS __lhs, _RHS __rhs) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static int operator()(_LHS __lhs, _RHS __rhs) {
     if (__lhs < __rhs)
       return -1;
     if (__lhs > __rhs)
diff --git a/libcxx/include/__utility/lazy_synth_three_way_comparator.h b/libcxx/include/__utility/lazy_synth_three_way_comparator.h
index 8c78742ccb4e3..3b2c7aa4de83f 100644
--- a/libcxx/include/__utility/lazy_synth_three_way_comparator.h
+++ b/libcxx/include/__utility/lazy_synth_three_way_comparator.h
@@ -34,14 +34,14 @@ struct __lazy_compare_result {
   const _LHS& __lhs_;
   const _RHS& __rhs_;
 
-  _LIBCPP_HIDE_FROM_ABI
-  __lazy_compare_result(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp,
-                        _LIBCPP_CTOR_LIFETIMEBOUND const _LHS& __lhs,
-                        _LIBCPP_CTOR_LIFETIMEBOUND const _RHS& __rhs)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __lazy_compare_result(
+      _LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp,
+      _LIBCPP_CTOR_LIFETIMEBOUND const _LHS& __lhs,
+      _LIBCPP_CTOR_LIFETIMEBOUND const _RHS& __rhs)
       : __comp_(__comp), __lhs_(__lhs), __rhs_(__rhs) {}
 
-  _LIBCPP_HIDE_FROM_ABI bool __less() const { return __comp_(__lhs_, __rhs_); }
-  _LIBCPP_HIDE_FROM_ABI bool __greater() const { return __comp_(__rhs_, __lhs_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __less() const { return __comp_(__lhs_, __rhs_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __greater() const { return __comp_(__rhs_, __lhs_); }
 };
 
 // This class provides three way comparison between _LHS and _RHS as efficiently as possible. This can be specialized if
@@ -51,10 +51,11 @@ template <class _Comparator, class _LHS, class _RHS, class = void>
 struct __lazy_synth_three_way_comparator {
   const _Comparator& __comp_;
 
-  _LIBCPP_HIDE_FROM_ABI __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator& __comp)
       : __comp_(__comp) {}
 
-  _LIBCPP_HIDE_FROM_ABI __lazy_compare_result<_Comparator, _LHS, _RHS>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __lazy_compare_result<_Comparator, _LHS, _RHS>
   operator()(_LIBCPP_LIFETIMEBOUND const _LHS& __lhs, _LIBCPP_LIFETIMEBOUND const _RHS& __rhs) const {
     return __lazy_compare_result<_Comparator, _LHS, _RHS>(__comp_, __lhs, __rhs);
   }
@@ -63,10 +64,10 @@ struct __lazy_synth_three_way_comparator {
 struct __eager_compare_result {
   int __res_;
 
-  _LIBCPP_HIDE_FROM_ABI explicit __eager_compare_result(int __res) : __res_(__res) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __eager_compare_result(int __res) : __res_(__res) {}
 
-  _LIBCPP_HIDE_FROM_ABI bool __less() const { return __res_ < 0; }
-  _LIBCPP_HIDE_FROM_ABI bool __greater() const { return __res_ > 0; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __less() const { return __res_ < 0; }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __greater() const { return __res_ > 0; }
 };
 
 template <class _Comparator, class _LHS, class _RHS>
@@ -77,10 +78,11 @@ struct __lazy_synth_three_way_comparator<_Comparator,
                                                             __has_default_three_way_comparator<_LHS, _RHS> >::value> > {
   // This lifetimebound annotation is technically incorrect, but other specializations actually capture the lifetime of
   // the comparator.
-  _LIBCPP_HIDE_FROM_ABI __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}
 
   // Same comment as above.
-  _LIBCPP_HIDE_FROM_ABI static __eager_compare_result
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static __eager_compare_result
   operator()(_LIBCPP_LIFETIMEBOUND const _LHS& __lhs, _LIBCPP_LIFETIMEBOUND const _RHS& __rhs) {
     return __eager_compare_result(__default_three_way_comparator<_LHS, _RHS>()(__lhs, __rhs));
   }
@@ -94,10 +96,11 @@ struct __lazy_synth_three_way_comparator<_Comparator,
                                                             __has_default_three_way_comparator<_LHS, _RHS> >::value> > {
   // This lifetimebound annotation is technically incorrect, but other specializations actually capture the lifetime of
   // the comparator.
-  _LIBCPP_HIDE_FROM_ABI __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  __lazy_synth_three_way_comparator(_LIBCPP_CTOR_LIFETIMEBOUND const _Comparator&) {}
 
   // Same comment as above.
-  _LIBCPP_HIDE_FROM_ABI static __eager_compare_result
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static __eager_compare_result
   operator()(_LIBCPP_LIFETIMEBOUND const _LHS& __lhs, _LIBCPP_LIFETIMEBOUND const _RHS& __rhs) {
     return __eager_compare_result(-__default_three_way_comparator<_LHS, _RHS>()(__lhs, __rhs));
   }
diff --git a/libcxx/include/__utility/try_key_extraction.h b/libcxx/include/__utility/try_key_extraction.h
index 755c08214019f..3423d746dee7f 100644
--- a/libcxx/include/__utility/try_key_extraction.h
+++ b/libcxx/include/__utility/try_key_extraction.h
@@ -28,7 +28,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _KeyT, class _Ret, class _WithKey, class _WithoutKey, class... _Args>
-_LIBCPP_HIDE_FROM_ABI _Ret
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
 __try_key_extraction_impl(__priority_tag<0>, _WithKey, _WithoutKey __without_key, _Args&&... __args) {
   return __without_key(std::forward<_Args>(__args)...);
 }
@@ -39,7 +39,7 @@ template <class _KeyT,
           class _WithoutKey,
           class _Arg,
           __enable_if_t<is_same<_KeyT, __remove_const_ref_t<_Arg> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Ret
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
 __try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _Arg&& __arg) {
   return __with_key(__arg, std::forward<_Arg>(__arg));
 }
@@ -52,7 +52,7 @@ template <class _KeyT,
           __enable_if_t<__is_pair_v<__remove_const_ref_t<_Arg> > &&
                             is_same<__remove_const_t<typename __remove_const_ref_t<_Arg>::first_type>, _KeyT>::value,
                         int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Ret
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
 __try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _Arg&& __arg) {
   return __with_key(__arg.first, std::forward<_Arg>(__arg));
 }
@@ -64,7 +64,7 @@ template <class _KeyT,
           class _Arg1,
           class _Arg2,
           __enable_if_t<is_same<_KeyT, __remove_const_ref_t<_Arg1> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Ret
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
 __try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _Arg1&& __arg1, _Arg2&& __arg2) {
   return __with_key(__arg1, std::forward<_Arg1>(__arg1), std::forward<_Arg2>(__arg2));
 }
@@ -81,7 +81,7 @@ template <class _KeyT,
                             __is_tuple_v<_Tuple1> && tuple_size<_Tuple1>::value == 1 &&
                             is_same<__remove_const_ref_t<typename tuple_element<0, _Tuple1>::type>, _KeyT>::value,
                         int> = 0>
-_LIBCPP_HIDE_FROM_ABI _Ret __try_key_extraction_impl(
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret __try_key_extraction_impl(
     __priority_tag<1>,
     _WithKey __with_key,
     _WithoutKey,
@@ -102,7 +102,7 @@ _LIBCPP_HIDE_FROM_ABI _Ret __try_key_extraction_impl(
 //
 // Both `__with_key` and `__without_key` must take all arguments by reference.
 template <class _KeyT, class _WithKey, class _WithoutKey, class... _Args>
-_LIBCPP_HIDE_FROM_ABI decltype(std::declval<_WithoutKey>()(std::declval<_Args>()...))
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 decltype(std::declval<_WithoutKey>()(std::declval<_Args>()...))
 __try_key_extraction(_WithKey __with_key, _WithoutKey __without_key, _Args&&... __args) {
   using _Ret = decltype(__without_key(std::forward<_Args>(__args)...));
   return std::__try_key_extraction_impl<_KeyT, _Ret>(
diff --git a/libcxx/test/support/is_transparent.h b/libcxx/test/support/is_transparent.h
index 9d3791d68a865..846f8fa4d06dd 100644
--- a/libcxx/test/support/is_transparent.h
+++ b/libcxx/test/support/is_transparent.h
@@ -81,16 +81,17 @@ struct transparent_less_not_a_type
 };
 
 struct C2Int { // comparable to int
-    C2Int() : i_(0) {}
-    C2Int(int i): i_(i) {}
-    int get () const { return i_; }
+  TEST_CONSTEXPR_CXX26 C2Int() : i_(0) {}
+  TEST_CONSTEXPR_CXX26 C2Int(int i) : i_(i) {}
+  TEST_CONSTEXPR_CXX26 int get() const { return i_; }
+
 private:
     int i_;
     };
 
-bool operator <(int          rhs,   const C2Int& lhs) { return rhs       < lhs.get(); }
-bool operator <(const C2Int& rhs,   const C2Int& lhs) { return rhs.get() < lhs.get(); }
-bool operator <(const C2Int& rhs,            int lhs) { return rhs.get() < lhs; }
+    TEST_CONSTEXPR_CXX26 bool operator<(int rhs, const C2Int& lhs) { return rhs < lhs.get(); }
+    TEST_CONSTEXPR_CXX26 bool operator<(const C2Int& rhs, const C2Int& lhs) { return rhs.get() < lhs.get(); }
+    TEST_CONSTEXPR_CXX26 bool operator<(const C2Int& rhs, int lhs) { return rhs.get() < lhs; }
 
 #endif // TEST_STD_VER > 11
 
diff --git a/libcxx/test/support/poisoned_hash_helper.h b/libcxx/test/support/poisoned_hash_helper.h
index cd71cd70d6a84..93b579d2dfde3 100644
--- a/libcxx/test/support/poisoned_hash_helper.h
+++ b/libcxx/test/support/poisoned_hash_helper.h
@@ -123,9 +123,13 @@ struct Class {};
 // Each header that declares the std::hash template provides enabled
 // specializations of std::hash for std::nullptr_t and all cv-unqualified
 // arithmetic, enumeration, and pointer types.
-using LibraryHashTypes =
-    types::concatenate_t<types::arithmetic_types,
-                         types::type_list<Enum, EnumClass, void*, void const*, Class*, std::nullptr_t>>;
+#if TEST_STD_VER >= 17
+using MaybeNullptr = types::type_list<std::nullptr_t>;
+#else
+using MaybeNullptr = types::type_list<>;
+#endif
+using LibraryHashTypes = types::
+    concatenate_t<types::arithmetic_types, types::type_list<Enum, EnumClass, void*, void const*, Class*>, MaybeNullptr>;
 
 struct TestHashEnabled {
   template <class T>
diff --git a/libcxx/test/support/private_constructor.h b/libcxx/test/support/private_constructor.h
index 24f540c6a7fdc..c7ed288c4032e 100644
--- a/libcxx/test/support/private_constructor.h
+++ b/libcxx/test/support/private_constructor.h
@@ -9,18 +9,22 @@
 #ifndef TEST_SUPPORT_PRIVATE_CONSTRUCTOR_H
 #define TEST_SUPPORT_PRIVATE_CONSTRUCTOR_H
 
+#include "test_macros.h"
+
 struct PrivateConstructor {
+  TEST_CONSTEXPR_CXX26 PrivateConstructor static make(int v) { return PrivateConstructor(v); }
+  TEST_CONSTEXPR_CXX26 int get() const { return val; }
 
-    PrivateConstructor static make ( int v ) { return PrivateConstructor(v); }
-    int get () const { return val; }
 private:
-    PrivateConstructor ( int v ) : val(v) {}
-    int val;
+  TEST_CONSTEXPR_CXX26 PrivateConstructor(int v) : val(v) {}
+  int val;
     };
 
-bool operator < ( const PrivateConstructor &lhs, const PrivateConstructor &rhs ) { return lhs.get() < rhs.get(); }
+    TEST_CONSTEXPR_CXX26 bool operator<(const PrivateConstructor& lhs, const PrivateConstructor& rhs) {
+      return lhs.get() < rhs.get();
+    }
 
-bool operator < ( const PrivateConstructor &lhs, int rhs ) { return lhs.get() < rhs; }
-bool operator < ( int lhs, const PrivateConstructor &rhs ) { return lhs < rhs.get(); }
+    TEST_CONSTEXPR_CXX26 bool operator<(const PrivateConstructor& lhs, int rhs) { return lhs.get() < rhs; }
+    TEST_CONSTEXPR_CXX26 bool operator<(int lhs, const PrivateConstructor& rhs) { return lhs < rhs.get(); }
 
 #endif // TEST_SUPPORT_PRIVATE_CONSTRUCTOR_H

>From 49d74e1a7cec61788ad5ca73ebbf040685e38cd9 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 15:22:15 -0500
Subject: [PATCH 02/22] Add test boilerplate for constexpr testing

---
 .../associative/iterator_types.pass.cpp       |  9 ++++-
 .../containers/associative/set/clear.pass.cpp | 11 +++++--
 .../associative/set/contains.pass.cpp         | 11 +++++--
 .../set/contains_transparent.pass.cpp         | 11 +++++--
 .../containers/associative/set/count.pass.cpp | 11 +++++--
 .../set/count_transparent.pass.cpp            | 11 +++++--
 .../associative/set/emplace.pass.cpp          | 11 +++++--
 .../associative/set/emplace_hint.pass.cpp     | 11 +++++--
 .../containers/associative/set/empty.pass.cpp | 11 +++++--
 .../associative/set/equal_range.pass.cpp      | 13 ++++++--
 .../set/equal_range_transparent.pass.cpp      | 17 +++++++---
 .../associative/set/erase_iter.pass.cpp       | 11 +++++--
 .../associative/set/erase_iter_iter.pass.cpp  | 11 +++++--
 .../associative/set/erase_key.pass.cpp        | 11 +++++--
 .../associative/set/extract_iterator.pass.cpp | 11 +++++--
 .../associative/set/extract_key.pass.cpp      | 11 +++++--
 .../containers/associative/set/find.pass.cpp  | 13 ++++++--
 .../associative/set/get_allocator.pass.cpp    | 11 +++++--
 .../associative/set/incomplete_type.pass.cpp  | 11 +++++--
 ...nd_emplace_allocator_requirements.pass.cpp | 15 ++++++---
 .../associative/set/insert_cv.pass.cpp        | 11 +++++--
 .../set/insert_initializer_list.pass.cpp      | 11 +++++--
 .../associative/set/insert_iter_cv.pass.cpp   | 11 +++++--
 .../associative/set/insert_iter_iter.pass.cpp | 11 +++++--
 .../associative/set/insert_iter_rv.pass.cpp   | 11 +++++--
 .../associative/set/insert_node_type.pass.cpp | 11 +++++--
 .../set/insert_node_type_hint.pass.cpp        | 11 +++++--
 .../associative/set/insert_range.pass.cpp     | 11 +++++--
 .../associative/set/insert_rv.pass.cpp        | 11 +++++--
 .../associative/set/iterator.pass.cpp         | 33 +++++++++++--------
 .../associative/set/lower_bound.pass.cpp      | 13 ++++++--
 .../associative/set/max_size.pass.cpp         | 11 +++++--
 .../containers/associative/set/merge.pass.cpp | 17 +++++++---
 .../associative/set/set.cons/alloc.pass.cpp   | 11 +++++--
 .../set.cons/assign_initializer_list.pass.cpp | 11 +++++--
 .../associative/set/set.cons/compare.pass.cpp | 11 +++++--
 .../set/set.cons/compare_alloc.pass.cpp       | 11 +++++--
 .../associative/set/set.cons/copy.pass.cpp    | 11 +++++--
 .../set/set.cons/copy_alloc.pass.cpp          | 11 +++++--
 .../set/set.cons/copy_assign.pass.cpp         | 11 +++++--
 .../associative/set/set.cons/deduct.pass.cpp  | 11 ++++++-
 .../associative/set/set.cons/default.pass.cpp | 11 +++++--
 .../set/set.cons/default_noexcept.pass.cpp    | 11 +++++--
 .../set/set.cons/dtor_noexcept.pass.cpp       | 11 +++++--
 .../set/set.cons/from_range.pass.cpp          | 10 +++++-
 .../set/set.cons/initializer_list.pass.cpp    | 11 +++++--
 .../initializer_list_compare.pass.cpp         | 11 +++++--
 .../initializer_list_compare_alloc.pass.cpp   | 13 ++++++--
 .../set/set.cons/iter_iter.pass.cpp           | 11 +++++--
 .../set/set.cons/iter_iter_alloc.pass.cpp     | 17 +++++++---
 .../set/set.cons/iter_iter_comp.pass.cpp      | 11 +++++--
 .../associative/set/set.cons/move.pass.cpp    | 11 +++++--
 .../set/set.cons/move_alloc.pass.cpp          | 11 +++++--
 .../set/set.cons/move_assign.pass.cpp         | 11 +++++--
 .../set/set.cons/move_noexcept.pass.cpp       | 13 ++++++--
 .../set/set.erasure/erase_if.pass.cpp         | 11 +++++--
 .../set.nonmember/compare.three_way.pass.cpp  | 13 ++++++--
 .../set/set.nonmember/op_compare.pass.cpp     | 33 +++++++++++--------
 .../set/set.observers/comp.pass.cpp           | 13 ++++++--
 .../set/set.special/member_swap.pass.cpp      | 11 +++++--
 .../set/set.special/non_member_swap.pass.cpp  | 11 +++++--
 .../set/set.special/swap_noexcept.pass.cpp    | 11 +++++--
 .../containers/associative/set/size.pass.cpp  | 11 +++++--
 .../containers/associative/set/types.pass.cpp |  9 ++++-
 .../associative/set/upper_bound.pass.cpp      | 13 ++++++--
 65 files changed, 625 insertions(+), 167 deletions(-)

diff --git a/libcxx/test/std/containers/associative/iterator_types.pass.cpp b/libcxx/test/std/containers/associative/iterator_types.pass.cpp
index 2800549905f1b..f394b000615d8 100644
--- a/libcxx/test/std/containers/associative/iterator_types.pass.cpp
+++ b/libcxx/test/std/containers/associative/iterator_types.pass.cpp
@@ -46,7 +46,7 @@ void testSet() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::map<int, int> Map;
     typedef std::pair<const int, int> ValueTp;
@@ -124,5 +124,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/clear.pass.cpp b/libcxx/test/std/containers/associative/set/clear.pass.cpp
index a53b6a86a660e..fbcde898950c0 100644
--- a/libcxx/test/std/containers/associative/set/clear.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/clear.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// void clear() noexcept;
+// constexpr void clear() noexcept; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef int V;
@@ -42,5 +42,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/contains.pass.cpp b/libcxx/test/std/containers/associative/set/contains.pass.cpp
index 93e6ff0a28885..20b1eeb839414 100644
--- a/libcxx/test/std/containers/associative/set/contains.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/contains.pass.cpp
@@ -13,7 +13,7 @@
 
 // <set>
 
-// bool contains(const key_type& x) const;
+// constexpr bool contains(const key_type& x) const; // constexpr since C++26
 
 template <typename T, typename V, typename B, typename... Vals>
 void test(B bad, Vals... args) {
@@ -34,7 +34,7 @@ struct E {
   char c   = 1;
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     test<std::set<int>, int>(14, 10, 11, 12, 13);
     test<std::set<char>, char>('e', 'a', 'b', 'c', 'd');
@@ -44,5 +44,12 @@ int main(int, char**) {
     test<std::multiset<char>, char>('e', 'a', 'b', 'c', 'd');
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
index c5d6b293e8a4b..acd03e649095f 100644
--- a/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// template<typename K> bool contains(const K& x) const; // C++20
+// template<typename K> constexpr bool contains(const K& x) const; // C++20 // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -36,9 +36,16 @@ void test() {
   assert(!s.contains(-1));
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test<std::set<std::pair<int, int>, Comp> >();
   test<std::multiset<std::pair<int, int>, Comp> >();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/count.pass.cpp b/libcxx/test/std/containers/associative/set/count.pass.cpp
index abd1e98f1a4ee..486220f1004c7 100644
--- a/libcxx/test/std/containers/associative/set/count.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/count.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// size_type count(const key_type& k) const;
+// constexpr size_type count(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -136,5 +136,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
index 4e0b610292460..9147b38e5d336 100644
--- a/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class set
 
 //    template<typename K>
-//        size_type count(const K& x) const;        // C++14
+//        constexpr size_type count(const K& x) const;        // C++14 // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -29,11 +29,18 @@ struct Comp {
   bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::set<std::pair<int, int>, Comp> s{{2, 1}, {1, 2}, {1, 3}, {1, 4}, {2, 2}};
 
   auto cnt = s.count(1);
   assert(cnt == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/emplace.pass.cpp b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
index e5a9f9590aad0..fecd9ef2fe7f9 100644
--- a/libcxx/test/std/containers/associative/set/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
@@ -13,7 +13,7 @@
 // class set
 
 // template <class... Args>
-//   pair<iterator, bool> emplace(Args&&... args);
+//   constexpr pair<iterator, bool> emplace(Args&&... args); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -23,7 +23,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<DefaultOnly> M;
     typedef std::pair<M::iterator, bool> R;
@@ -92,5 +92,12 @@ int main(int, char**) {
     assert(set.begin() == std::get<0>(res));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
index 9f0d5fbd2c5e5..5c68000ffbab9 100644
--- a/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
@@ -13,7 +13,7 @@
 // class set
 
 // template <class... Args>
-//   iterator emplace_hint(const_iterator position, Args&&... args);
+//   constexpr iterator emplace_hint(const_iterator position, Args&&... args); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "DefaultOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<DefaultOnly> M;
     typedef M::iterator R;
@@ -78,5 +78,12 @@ int main(int, char**) {
     assert(*r == 2);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/empty.pass.cpp b/libcxx/test/std/containers/associative/set/empty.pass.cpp
index aaefade5cff7b..e6bcf2a58d103 100644
--- a/libcxx/test/std/containers/associative/set/empty.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/empty.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// bool empty() const;
+// constexpr bool empty() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     M m;
@@ -40,5 +40,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/equal_range.pass.cpp b/libcxx/test/std/containers/associative/set/equal_range.pass.cpp
index 239aa3e878426..d59cfe2daee87 100644
--- a/libcxx/test/std/containers/associative/set/equal_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/equal_range.pass.cpp
@@ -10,8 +10,8 @@
 
 // class set
 
-// pair<iterator,iterator>             equal_range(const key_type& k);
-// pair<const_iterator,const_iterator> equal_range(const key_type& k) const;
+// constexpr pair<iterator,iterator>             equal_range(const key_type& k); // constexpr since C++26
+// constexpr pair<const_iterator,const_iterator> equal_range(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -327,5 +327,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
index a44fcf4b74a02..92c1c64be658c 100644
--- a/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
@@ -13,11 +13,11 @@
 // class set
 
 // template<typename K>
-//     pair<iterator,iterator>             equal_range(const K& x);        //
-//     C++14
+//     constexpr pair<iterator,iterator>  equal_range(const K& x);        //
+//     C++14, constexpr since C++26
 // template<typename K>
-//     pair<const_iterator,const_iterator> equal_range(const K& x) const;  //
-//     C++14
+//     constexpr pair<const_iterator,const_iterator> equal_range(const K& x) const;  //
+//     C++14, constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -33,7 +33,7 @@ struct Comp {
   bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::set<std::pair<int, int>, Comp> s{{2, 1}, {1, 2}, {1, 3}, {1, 4}, {2, 2}};
 
   auto er   = s.equal_range(1);
@@ -46,5 +46,12 @@ int main(int, char**) {
 
   assert(nels == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
index 73a4cc4266ec9..7682a81dc7407 100644
--- a/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// iterator erase(const_iterator position);
+// constexpr iterator erase(const_iterator position); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -25,7 +25,7 @@ struct TemplateConstructor {
 
 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef int V;
@@ -179,5 +179,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/erase_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/erase_iter_iter.pass.cpp
index 20c97c28fce56..9c3ba0abc990c 100644
--- a/libcxx/test/std/containers/associative/set/erase_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/erase_iter_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// iterator erase(const_iterator first, const_iterator last);
+// constexpr iterator erase(const_iterator first, const_iterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef int V;
@@ -118,5 +118,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/erase_key.pass.cpp b/libcxx/test/std/containers/associative/set/erase_key.pass.cpp
index 318c35d21cd87..abf82be8a1bcf 100644
--- a/libcxx/test/std/containers/associative/set/erase_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/erase_key.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// size_type erase(const key_type& k);
+// constexpr size_type erase(const key_type& k); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef int V;
@@ -180,5 +180,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
index 2d7b6e521d961..1511b08079d03 100644
--- a/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// node_type extract(const_iterator);
+// constexpr node_type extract(const_iterator); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -35,7 +35,7 @@ void test(Container& c) {
   assert(c.size() == 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     using set_type = std::set<int>;
     set_type m     = {1, 2, 3, 4, 5, 6};
@@ -55,5 +55,12 @@ int main(int, char**) {
     test(m);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/extract_key.pass.cpp b/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
index 0802387f2c9b3..07f6d193179a8 100644
--- a/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// node_type extract(key_type const&);
+// constexpr node_type extract(key_type const&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -41,7 +41,7 @@ void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::set<int> m = {1, 2, 3, 4, 5, 6};
     int keys[]      = {1, 2, 3, 4, 5, 6};
@@ -65,5 +65,12 @@ int main(int, char**) {
     test(m, std::begin(keys), std::end(keys));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/find.pass.cpp b/libcxx/test/std/containers/associative/set/find.pass.cpp
index deb193c17bfa9..e88441ed8927d 100644
--- a/libcxx/test/std/containers/associative/set/find.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/find.pass.cpp
@@ -10,8 +10,8 @@
 
 // class set
 
-//       iterator find(const key_type& k);
-// const_iterator find(const key_type& k) const;
+// constexpr       iterator find(const key_type& k); // constexpr since C++26
+// constexpr const_iterator find(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -202,5 +202,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/get_allocator.pass.cpp b/libcxx/test/std/containers/associative/set/get_allocator.pass.cpp
index 0270c9e82ab0e..a2bca7af5bd41 100644
--- a/libcxx/test/std/containers/associative/set/get_allocator.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/get_allocator.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// allocator_type get_allocator() const
+// constexpr allocator_type get_allocator() const // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::allocator<int> alloc;
     const std::set<int> s(alloc);
@@ -29,5 +29,12 @@ int main(int, char**) {
     const std::set<int, std::less<int>, other_allocator<int> > s(alloc);
     assert(s.get_allocator() == alloc);
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/incomplete_type.pass.cpp b/libcxx/test/std/containers/associative/set/incomplete_type.pass.cpp
index acbda713619bd..e9db3b31f14a4 100644
--- a/libcxx/test/std/containers/associative/set/incomplete_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/incomplete_type.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// <set>
+// <set> // constexpr since C++26
 
 // Check that std::set and its iterators can be instantiated with an incomplete
 // type.
@@ -25,8 +25,15 @@ struct A {
 
 inline bool operator==(A const& L, A const& R) { return &L == &R; }
 inline bool operator<(A const& L, A const& R) { return L.data < R.data; }
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   A a;
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
index 4711473de4dad..ace31eb66ea8d 100644
--- a/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
@@ -10,9 +10,9 @@
 
 // class set
 
-// insert(...)
-// emplace(...)
-// emplace_hint(...)
+// constexpr insert(...) // constexpr since C++26
+// constexpr emplace(...) // constexpr since C++26
+// constexpr emplace_hint(...) // constexpr since C++26
 
 // UNSUPPORTED: c++03
 
@@ -21,10 +21,17 @@
 #include "container_test_types.h"
 #include "../../set_allocator_requirement_test_templates.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   testSetInsert<TCT::set<> >();
   testSetEmplace<TCT::set<> >();
   testSetEmplaceHint<TCT::set<> >();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
index 2683f2924e760..ef278224f0229 100644
--- a/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// pair<iterator, bool> insert(const value_type& v);
+// constexpr pair<iterator, bool> insert(const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -53,7 +53,7 @@ void do_insert_cv_test() {
   assert(*r.first == 3);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   do_insert_cv_test<std::set<int> >();
 #if TEST_STD_VER >= 11
   {
@@ -62,5 +62,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/set/insert_initializer_list.pass.cpp
index f3b69a59987e8..9ebf723e2af44 100644
--- a/libcxx/test/std/containers/associative/set/insert_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// void insert(initializer_list<value_type> il);
+// constexpr void insert(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> C;
     typedef C::value_type V;
@@ -57,5 +57,12 @@ int main(int, char**) {
     assert(*++i == V(10));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_iter_cv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_cv.pass.cpp
index 159487ee0e479..506e06d23bc43 100644
--- a/libcxx/test/std/containers/associative/set/insert_iter_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_iter_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// iterator insert(const_iterator position, const value_type& v);
+// constexpr iterator insert(const_iterator position, const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     typedef M::iterator R;
@@ -70,5 +70,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
index 8576c63a72de9..6d6bd185afa04 100644
--- a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
@@ -11,7 +11,7 @@
 // class set
 
 // template <class InputIterator>
-//   void insert(InputIterator first, InputIterator last);
+//   constexpr void insert(InputIterator first, InputIterator last); // constexpr since C++26
 
 #include <array>
 #include <cassert>
@@ -183,8 +183,15 @@ void test() {
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_iter_rv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_rv.pass.cpp
index 21358f3c40c9c..73031dfae5fd7 100644
--- a/libcxx/test/std/containers/associative/set/insert_iter_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_iter_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// iterator insert(const_iterator position, value_type&& v);
+// constexpr iterator insert(const_iterator position, value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<MoveOnly> M;
     typedef M::iterator R;
@@ -72,5 +72,12 @@ int main(int, char**) {
     assert(*r == 3);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
index 9794b4da989d9..935a90afedb2b 100644
--- a/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// insert_return_type insert(node_type&&);
+// constexpr insert_return_type insert(node_type&&); // constexpr since C++26
 
 #include <memory>
 #include <set>
@@ -94,11 +94,18 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::set<int> m;
   test(m);
   std::set<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
index 1449be0030fe5..f14263595ac6a 100644
--- a/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// iterator insert(const_iterator hint, node_type&&);
+// constexpr iterator insert(const_iterator hint, node_type&&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -47,11 +47,18 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::set<int> m;
   test(m);
   std::set<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
index e6c54d84d6e40..654d4d425774a 100644
--- a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
@@ -13,14 +13,14 @@
 // <set>
 
 // template<container-compatible-range<value_type> R>
-//   void insert_range(R&& rg); // C++23
+//   constexpr void insert_range(R&& rg); // C++23, constexpr since C++26
 
 #include <set>
 
 #include "../../insert_range_maps_sets.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
     test_map_set_insert_range<std::set<int, test_less<int>, Alloc>, int, Iter, Sent>();
   });
@@ -32,5 +32,12 @@ int main(int, char**) {
   test_set_insert_range_exception_safety_throwing_copy<std::set>();
   test_assoc_set_insert_range_exception_safety_throwing_allocator<std::set, int>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_rv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_rv.pass.cpp
index ec18c4379b58d..9b01722faadaa 100644
--- a/libcxx/test/std/containers/associative/set/insert_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// pair<iterator, bool> insert(value_type&& v);
+// constexpr pair<iterator, bool> insert(value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<MoveOnly> M;
     typedef std::pair<M::iterator, bool> R;
@@ -79,5 +79,12 @@ int main(int, char**) {
     assert(*r.first == 3);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/iterator.pass.cpp b/libcxx/test/std/containers/associative/set/iterator.pass.cpp
index 1a18cbc426478..467d88e291a02 100644
--- a/libcxx/test/std/containers/associative/set/iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/iterator.pass.cpp
@@ -10,20 +10,20 @@
 
 // class set
 
-//       iterator begin();
-// const_iterator begin() const;
-//       iterator end();
-// const_iterator end()   const;
+//       constexpr iterator begin(); // constexpr since C++26
+// constexpr const_iterator begin() const; // constexpr since C++26
+//       constexpr iterator end(); // constexpr since C++26
+// constexpr const_iterator end()   const; // constexpr since C++26
 //
-//       reverse_iterator rbegin();
-// const_reverse_iterator rbegin() const;
-//       reverse_iterator rend();
-// const_reverse_iterator rend()   const;
+//       constexpr reverse_iterator rbegin(); // constexpr since C++26
+// constexpr const_reverse_iterator rbegin() const; // constexpr since C++26
+//       constexpr reverse_iterator rend(); // constexpr since C++26
+// constexpr const_reverse_iterator rend()   const; // constexpr since C++26
 //
-// const_iterator         cbegin()  const;
-// const_iterator         cend()    const;
-// const_reverse_iterator crbegin() const;
-// const_reverse_iterator crend()   const;
+// constexpr const_iterator         cbegin()  const; // constexpr since C++26
+// constexpr const_iterator         cend()    const; // constexpr since C++26
+// constexpr const_reverse_iterator crbegin() const; // constexpr since C++26
+// constexpr const_reverse_iterator crend()   const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -32,7 +32,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8};
@@ -129,5 +129,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/lower_bound.pass.cpp b/libcxx/test/std/containers/associative/set/lower_bound.pass.cpp
index 2f114685fb84d..dcaa3b17c8fcc 100644
--- a/libcxx/test/std/containers/associative/set/lower_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/lower_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class set
 
-//       iterator lower_bound(const key_type& k);
-// const_iterator lower_bound(const key_type& k) const;
+//       constexpr iterator lower_bound(const key_type& k); // constexpr since C++26
+// constexpr const_iterator lower_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -283,5 +283,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/max_size.pass.cpp b/libcxx/test/std/containers/associative/set/max_size.pass.cpp
index b00d316c47b35..f11eda16312e5 100644
--- a/libcxx/test/std/containers/associative/set/max_size.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/max_size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// size_type max_size() const;
+// constexpr size_type max_size() const; // constexpr since C++26
 
 #include <cassert>
 #include <limits>
@@ -20,7 +20,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef limited_allocator<int, 10> A;
     typedef std::set<int, std::less<int>, A> C;
@@ -44,5 +44,12 @@ int main(int, char**) {
     assert(c.max_size() <= alloc_max_size(c.get_allocator()));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/merge.pass.cpp b/libcxx/test/std/containers/associative/set/merge.pass.cpp
index 50141d5444b1f..4c76c59ec5de5 100644
--- a/libcxx/test/std/containers/associative/set/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/merge.pass.cpp
@@ -13,13 +13,13 @@
 // class set
 
 // template <class C2>
-//   void merge(set<key_type, C2, allocator_type>& source);
+//   constexpr void merge(set<key_type, C2, allocator_type>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(set<key_type, C2, allocator_type>&& source);
+//   constexpr void merge(set<key_type, C2, allocator_type>&& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<key_type, C2, allocator_type>& source);
+//   constexpr void merge(multiset<key_type, C2, allocator_type>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<key_type, C2, allocator_type>&& source);
+//   constexpr void merge(multiset<key_type, C2, allocator_type>&& source); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -46,7 +46,7 @@ struct throw_comparator {
 };
 #endif
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::set<int> src{1, 3, 5};
     std::set<int> dst{2, 4, 5};
@@ -134,5 +134,12 @@ int main(int, char**) {
       first.merge(std::move(second));
     }
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/alloc.pass.cpp
index 4321666044a3b..539d6e11e6b6f 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set(const allocator_type& a);
+// constexpr set(const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::less<int> C;
   typedef test_allocator<int> A;
   std::set<int, C, A> m(A(5));
@@ -26,5 +26,12 @@ int main(int, char**) {
   assert(m.begin() == m.end());
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
index d674669fd2559..1e0de1434aeb9 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set& operator=(initializer_list<value_type> il);
+// constexpr set& operator=(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -69,9 +69,16 @@ void duplicate_keys_test() {
   LIBCPP_ASSERT(alloc_stats.alloc_count == 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   basic_test();
   duplicate_keys_test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/compare.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/compare.pass.cpp
index 03452142f5b85..5b3abfe1fcb3b 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/compare.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// explicit set(const key_compare& comp) const;
+// constexpr explicit set(const key_compare& comp) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   const std::set<int, C> m(C(3));
   assert(m.empty());
@@ -26,5 +26,12 @@ int main(int, char**) {
   assert(m.key_comp() == C(3));
   assert(m.value_comp() == C(3));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/compare_alloc.pass.cpp
index 2ae0547054537..ba358f1c95412 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/compare_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set(const value_compare& comp, const allocator_type& a);
+// constexpr set(const value_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   typedef test_allocator<int> A;
   std::set<int, C, A> m(C(4), A(5));
@@ -28,5 +28,12 @@ int main(int, char**) {
   assert(m.key_comp() == C(4));
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
index 8fac335175c80..f2116c019d3b6 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set(const set& m);
+// constexpr set(const set& m); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -127,8 +127,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
index 2784af65b0a0f..200bf71b5f3dd 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set(const set& m, const allocator_type& a);
+// constexpr set(const set& m, const allocator_type& a); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -99,8 +99,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
index dd1dbc86190f8..7c301d62cb586 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set& operator=(const set& s);
+// constexpr set& operator=(const set& s); // constexpr since C++26
 
 #include <algorithm>
 #include <cassert>
@@ -277,8 +277,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
index e26df4503ed41..56540fbf35c6a 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
@@ -37,6 +37,8 @@
 //   set(from_range_t, R&&, Allocator)
 //     -> set<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>; // C++23
 
+// constexpr since C++26
+
 #include <algorithm> // std::equal
 #include <array>
 #include <cassert>
@@ -52,7 +54,7 @@ struct NotAnAllocator {
   friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     const int arr[] = {1, 2, 1, INT_MAX, 3};
     std::set s(std::begin(arr), std::end(arr));
@@ -212,5 +214,12 @@ int main(int, char**) {
 
   AssociativeContainerDeductionGuidesSfinaeAway<std::set, std::set<int>>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/default.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/default.pass.cpp
index c41d7c3b9d3bd..6ae5d003fef15 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/default.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/default.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// set();
+// constexpr set(); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::set<int> m;
     assert(m.empty());
@@ -51,5 +51,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/default_noexcept.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/default_noexcept.pass.cpp
index c01ac3b2fad0b..4b2ddd174519d 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/default_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/default_noexcept.pass.cpp
@@ -8,7 +8,7 @@
 
 // <set>
 
-// set()
+// constexpr set() // constexpr since C++26
 //    noexcept(
 //        is_nothrow_default_constructible<allocator_type>::value &&
 //        is_nothrow_default_constructible<key_compare>::value &&
@@ -32,7 +32,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::set<MoveOnly> C;
@@ -52,5 +52,12 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_default_constructible<C>::value, "");
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/dtor_noexcept.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/dtor_noexcept.pass.cpp
index 63c0433477414..15f3c257af272 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/dtor_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/dtor_noexcept.pass.cpp
@@ -8,7 +8,7 @@
 
 // <set>
 
-// ~set() // implied noexcept;
+// constexpr ~set() // implied noexcept; // constexpr since C++26
 
 // UNSUPPORTED: c++03
 
@@ -26,7 +26,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<MoveOnly> C;
     static_assert(std::is_nothrow_destructible<C>::value, "");
@@ -46,5 +46,12 @@ int main(int, char**) {
   }
 #endif // _LIBCPP_VERSION
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
index 3590b75065ad9..23a5136aafcf2 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
@@ -14,6 +14,7 @@
 // template<container-compatible-range<value_type> R>
 //   set(from_range_t, R&& rg, const Allocator& a))
 //     : set(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+// constexpr since C++26
 
 #include <array>
 #include <set>
@@ -30,7 +31,7 @@ void test_duplicates() {
   assert(std::ranges::is_permutation(expected, c));
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
     test_associative_set<std::set, int, Iter, Sent, test_less<int>, Alloc>();
   });
@@ -42,5 +43,12 @@ int main(int, char**) {
   test_set_exception_safety_throwing_copy<std::set>();
   test_set_exception_safety_throwing_allocator<std::set, int>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/initializer_list.pass.cpp
index 48495712425be..974d2d96b02d7 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// constexpr set(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> C;
     typedef C::value_type V;
@@ -50,5 +50,12 @@ int main(int, char**) {
     assert(*++i == V(6));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare.pass.cpp
index 25684966078d6..3572465b9118e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// constexpr set(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> Cmp;
   typedef std::set<int, Cmp> C;
   typedef C::value_type V;
@@ -37,5 +37,12 @@ int main(int, char**) {
   assert(*++i == V(6));
   assert(m.key_comp() == Cmp(10));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare_alloc.pass.cpp
index 48649c3b281f4..737e96bfad9f5 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/initializer_list_compare_alloc.pass.cpp
@@ -12,8 +12,8 @@
 
 // class set
 
-// set(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a);
-// set(initializer_list<value_type> il, const allocator_type& a);
+// constexpr set(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a); // constexpr since C++26
+// constexpr set(initializer_list<value_type> il, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef test_less<int> Cmp;
     typedef test_allocator<int> A;
@@ -60,5 +60,12 @@ int main(int, char**) {
     assert(m.get_allocator() == A(4));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/iter_iter.pass.cpp
index 27a4e6a5f527d..4e49265811b19 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/iter_iter.pass.cpp
@@ -11,7 +11,7 @@
 // class set
 
 // template <class InputIterator>
-//     set(InputIterator first, InputIterator last);
+//     constexpr set(InputIterator first, InputIterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_iterators.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -46,5 +46,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
index dac553073f177..5a4c9d156a3b6 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
@@ -11,12 +11,12 @@
 // class set
 
 // template <class InputIterator>
-//     set(InputIterator first, InputIterator last,
-//         const value_compare& comp, const allocator_type& a);
+//     constexpr set(InputIterator first, InputIterator last,
+//         const value_compare& comp, const allocator_type& a); // constexpr since C++26
 //
 // template <class InputIterator>
-//     set(InputIterator first, InputIterator last,
-//         const allocator_type& a);
+//     constexpr set(InputIterator first, InputIterator last,
+//         const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -26,7 +26,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -63,5 +63,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_comp.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_comp.pass.cpp
index 573bbd316e073..204c86a62590e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_comp.pass.cpp
@@ -11,7 +11,7 @@
 // class set
 
 // template <class InputIterator>
-//     set(InputIterator first, InputIterator last, const value_compare& comp);
+//     constexpr set(InputIterator first, InputIterator last, const value_compare& comp); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_iterators.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
   typedef test_less<V> C;
@@ -33,5 +33,12 @@ int main(int, char**) {
   assert(*std::next(m.begin()) == 2);
   assert(*std::next(m.begin(), 2) == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move.pass.cpp
index 2c1ec22ab257d..a4e68ff583d01 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set(set&& s);
+// constexpr set(set&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef test_less<int> C;
@@ -82,5 +82,12 @@ int main(int, char**) {
     assert(std::distance(mo.begin(), mo.end()) == 0);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
index c85463bd391ca..4f15202f2ca83 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set(set&& s, const allocator_type& a);
+// constexpr set(set&& s, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -24,7 +24,7 @@
 #include "test_allocator.h"
 #include "Counter.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -111,5 +111,12 @@ int main(int, char**) {
     assert(Counter_base::gConstructed == 0);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move_assign.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move_assign.pass.cpp
index bacd58c172a12..418c4d06ca114 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move_assign.pass.cpp
@@ -12,7 +12,7 @@
 
 // class set
 
-// set& operator=(set&& s);
+// constexpr set& operator=(set&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -93,5 +93,12 @@ int main(int, char**) {
     assert(m1.empty());
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move_noexcept.pass.cpp
index 9325b8d1f40de..fa315e392ce0c 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move_noexcept.pass.cpp
@@ -8,9 +8,9 @@
 
 // <set>
 
-// set(set&&)
+// constexpr set(set&&)
 //        noexcept(is_nothrow_move_constructible<allocator_type>::value &&
-//                 is_nothrow_move_constructible<key_compare>::value);
+//                 is_nothrow_move_constructible<key_compare>::value); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -30,7 +30,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::set<MoveOnly> C;
@@ -50,5 +50,12 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_move_constructible<C>::value, "");
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
index b076b31059610..978e80821eb49 100644
--- a/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
@@ -11,7 +11,7 @@
 
 // template <class T, class Compare, class Allocator, class Predicate>
 //   typename set<T, Compare, Allocator>::size_type
-//   erase_if(set<T, Compare, Allocator>& c, Predicate pred);
+//   constexpr erase_if(set<T, Compare, Allocator>& c, Predicate pred); // constexpr since C++26
 
 #include <set>
 
@@ -53,7 +53,7 @@ void test() {
   test0(S({1, 2, 3}), False, S({1, 2, 3}), 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test<std::set<int>>();
   test<std::set<int, std::less<int>, min_allocator<int>>>();
   test<std::set<int, std::less<int>, test_allocator<int>>>();
@@ -61,5 +61,12 @@ int main(int, char**) {
   test<std::set<long>>();
   test<std::set<double>>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
index dc513d96e5ba8..563a8c12db828 100644
--- a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
@@ -12,16 +12,23 @@
 // class set
 
 // template<class Key, class Compare, class Allocator>
-//   synth-three-way-result<Key> operator<=>(const set<Key, Compare, Allocator>& x,
-//                                           const set<Key, Compare, Allocator>& y);
+//   constexpr synth-three-way-result<Key> operator<=>(const set<Key, Compare, Allocator>& x,
+//                                           const set<Key, Compare, Allocator>& y); // constexpr since C++26
 
 #include <cassert>
 #include <set>
 
 #include "test_container_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   assert((test_ordered_set_container_spaceship<std::set>()));
   // `std::set` is not constexpr, so no `static_assert` test here.
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.nonmember/op_compare.pass.cpp b/libcxx/test/std/containers/associative/set/set.nonmember/op_compare.pass.cpp
index 1be07ce885298..547cc59c51880 100644
--- a/libcxx/test/std/containers/associative/set/set.nonmember/op_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.nonmember/op_compare.pass.cpp
@@ -9,28 +9,28 @@
 // <set>
 
 // template<class Key, class Compare, class Alloc>
-// bool operator==(const std::set<Key, Compare, Alloc>& lhs,
-//                 const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator==(const std::set<Key, Compare, Alloc>& lhs,
+//                 const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator!=(const std::set<Key, Compare, Alloc>& lhs,
-//                 const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator!=(const std::set<Key, Compare, Alloc>& lhs,
+//                 const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator<(const std::set<Key, Compare, Alloc>& lhs,
-//                const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator<(const std::set<Key, Compare, Alloc>& lhs,
+//                const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator>(const std::set<Key, Compare, Alloc>& lhs,
-//                const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator>(const std::set<Key, Compare, Alloc>& lhs,
+//                const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator<=(const std::set<Key, Compare, Alloc>& lhs,
-//                 const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator<=(const std::set<Key, Compare, Alloc>& lhs,
+//                 const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 //
 // template<class Key, class Compare, class Alloc>
-// bool operator>=(const std::set<Key, Compare, Alloc>& lhs,
-//                 const std::set<Key, Compare, Alloc>& rhs);
+// constexpr bool operator>=(const std::set<Key, Compare, Alloc>& lhs,
+//                 const std::set<Key, Compare, Alloc>& rhs); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -38,7 +38,7 @@
 
 #include "test_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::set<int> s1, s2;
     s1.insert(1);
@@ -61,5 +61,12 @@ int main(int, char**) {
     const std::set<int>&cs1 = s1, cs2 = s2;
     assert(testComparisons(cs1, cs2, false, true));
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
index 019dae3955acb..9aef8c7cce41b 100644
--- a/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
@@ -8,13 +8,13 @@
 
 // <set>
 
-// key_compare key_comp() const;
-// value_compare value_comp() const;
+// constexpr key_compare key_comp() const; // constexpr since C++26
+// constexpr value_compare value_comp() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::set<int> set_type;
 
   set_type s;
@@ -29,5 +29,12 @@ int main(int, char**) {
   assert(cs.value_comp()(*p1.first, *p2.first));
   assert(!cs.value_comp()(*p2.first, *p1.first));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.special/member_swap.pass.cpp b/libcxx/test/std/containers/associative/set/set.special/member_swap.pass.cpp
index db3dc9831b3dd..5a5a5156ce652 100644
--- a/libcxx/test/std/containers/associative/set/set.special/member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.special/member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// void swap(set& m);
+// constexpr void swap(set& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -110,5 +110,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.special/non_member_swap.pass.cpp b/libcxx/test/std/containers/associative/set/set.special/non_member_swap.pass.cpp
index e663a67035ff5..24fb007ef266e 100644
--- a/libcxx/test/std/containers/associative/set/set.special/non_member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.special/non_member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// void swap(set& m);
+// constexpr void swap(set& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   {
     typedef std::set<int> M;
@@ -100,5 +100,12 @@ int main(int, char**) {
     assert(m2.get_allocator() == A(1));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/associative/set/set.special/swap_noexcept.pass.cpp
index 36ab989b76043..4c4bf48adf241 100644
--- a/libcxx/test/std/containers/associative/set/set.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.special/swap_noexcept.pass.cpp
@@ -10,7 +10,7 @@
 
 // <set>
 
-// void swap(set& c)
+// constexpr void swap(set& c) // constexpr since C++26
 //     noexcept(!allocator_type::propagate_on_container_swap::value ||
 //              __is_nothrow_swappable<allocator_type>::value);
 //
@@ -86,7 +86,7 @@ struct some_alloc3 {
   typedef std::false_type is_always_equal;
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<MoveOnly> C;
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
@@ -131,5 +131,12 @@ int main(int, char**) {
 #  endif // _LIBCPP_VERSION
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/size.pass.cpp b/libcxx/test/std/containers/associative/set/size.pass.cpp
index 1672e1f055c6c..58084634e8dde 100644
--- a/libcxx/test/std/containers/associative/set/size.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class set
 
-// size_type size() const;
+// constexpr size_type size() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> M;
     M m;
@@ -56,5 +56,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/types.pass.cpp b/libcxx/test/std/containers/associative/set/types.pass.cpp
index a7c30244a540b..af3915dccf1fa 100644
--- a/libcxx/test/std/containers/associative/set/types.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/types.pass.cpp
@@ -34,7 +34,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::set<int> C;
     static_assert((std::is_same<C::key_type, int>::value), "");
@@ -67,5 +67,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/upper_bound.pass.cpp b/libcxx/test/std/containers/associative/set/upper_bound.pass.cpp
index 3543879e07940..f5825c30519e6 100644
--- a/libcxx/test/std/containers/associative/set/upper_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/upper_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class set
 
-//       iterator upper_bound(const key_type& k);
-// const_iterator upper_bound(const key_type& k) const;
+//       constexpr iterator upper_bound(const key_type& k); // constexpr since C++26
+// constexpr const_iterator upper_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::set<int> M;
@@ -283,5 +283,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

>From 594dff9a2ed3b45b1140c5df09a4e149d8afac7e Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 15:56:56 -0500
Subject: [PATCH 03/22] Get ready for set.cons

---
 libcxx/include/__tree |   2 +-
 libcxx/include/set    | 234 ++++++++++++++++++++++++++----------------
 2 files changed, 144 insertions(+), 92 deletions(-)

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 5398ff911ec57..5f4c317d62291 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1467,7 +1467,7 @@ private:
   }
 
   template <class _To, class _From, class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI static void __assign_value(_To& __lhs, _From&& __rhs) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static void __assign_value(_To& __lhs, _From&& __rhs) {
     __lhs = std::forward<_From>(__rhs);
   }
 
diff --git a/libcxx/include/set b/libcxx/include/set
index d58b6e96b061d..d66a54e964a01 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -10,6 +10,8 @@
 #ifndef _LIBCPP_SET
 #define _LIBCPP_SET
 
+// TODO: add constexpr everywhere
+
 /*
 
     set synopsis
@@ -614,24 +616,27 @@ public:
   template <class _Key2, class _Compare2, class _Alloc2>
   friend class multiset;
 
-  _LIBCPP_HIDE_FROM_ABI set() _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set() _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_default_constructible<key_compare>::value&&
           is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(value_compare()) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit set(const value_compare& __comp) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit set(const value_compare& __comp) _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(__comp) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit set(const value_compare& __comp, const allocator_type& __a) : __tree_(__comp, __a) {}
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit set(const value_compare& __comp, const allocator_type& __a)
+      : __tree_(__comp, __a) {}
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI set(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  set(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__f, __l);
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   set(_InputIterator __f, _InputIterator __l, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__f, __l);
@@ -639,7 +644,7 @@ public:
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   set(from_range_t,
       _Range&& __range,
       const key_compare& __comp = key_compare(),
@@ -651,205 +656,247 @@ public:
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI set(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 set(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : set(__f, __l, key_compare(), __a) {}
 #  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI set(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(from_range_t, _Range&& __range, const allocator_type& __a)
       : set(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI set(const set& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(const set& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI set& operator=(const set& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set& operator=(const set& __s) = default;
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI set(set&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(set&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI explicit set(const allocator_type& __a) : __tree_(__a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit set(const allocator_type& __a) : __tree_(__a) {}
 
-  _LIBCPP_HIDE_FROM_ABI set(const set& __s, const allocator_type& __a) : __tree_(__s.__tree_.value_comp(), __a) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(const set& __s, const allocator_type& __a)
+      : __tree_(__s.__tree_.value_comp(), __a) {
     insert(__s.begin(), __s.end());
   }
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI set(set&& __s, const allocator_type& __a);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(set&& __s, const allocator_type& __a);
 
-  _LIBCPP_HIDE_FROM_ABI set(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  set(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__il.begin(), __il.end());
   }
 
-  _LIBCPP_HIDE_FROM_ABI set(initializer_list<value_type> __il, const value_compare& __comp, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  set(initializer_list<value_type> __il, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__il.begin(), __il.end());
   }
 
 #    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI set(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(initializer_list<value_type> __il, const allocator_type& __a)
       : set(__il, key_compare(), __a) {}
 #    endif
 
-  _LIBCPP_HIDE_FROM_ABI set& operator=(initializer_list<value_type> __il) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set& operator=(initializer_list<value_type> __il) {
     __tree_.__assign_unique(__il.begin(), __il.end());
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI set& operator=(set&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set& operator=(set&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI ~set() { static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), ""); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~set() {
+    static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
+  }
 
-  _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return __tree_.begin(); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return __tree_.begin(); }
-  _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return __tree_.end(); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return __tree_.end(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT { return __tree_.begin(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator begin() const _NOEXCEPT { return __tree_.begin(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator end() _NOEXCEPT { return __tree_.end(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator end() const _NOEXCEPT { return __tree_.end(); }
 
-  _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT { return reverse_iterator(end()); }
-  _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rbegin() const _NOEXCEPT { return const_reverse_iterator(end()); }
-  _LIBCPP_HIDE_FROM_ABI reverse_iterator rend() _NOEXCEPT { return reverse_iterator(begin()); }
-  _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const _NOEXCEPT { return const_reverse_iterator(begin()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rbegin() _NOEXCEPT {
+    return reverse_iterator(end());
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator rbegin() const _NOEXCEPT {
+    return const_reverse_iterator(end());
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rend() _NOEXCEPT {
+    return reverse_iterator(begin());
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator rend() const _NOEXCEPT {
+    return const_reverse_iterator(begin());
+  }
 
-  _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const _NOEXCEPT { return begin(); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator cend() const _NOEXCEPT { return end(); }
-  _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crbegin() const _NOEXCEPT { return rbegin(); }
-  _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const _NOEXCEPT { return rend(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cbegin() const _NOEXCEPT { return begin(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cend() const _NOEXCEPT { return end(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator crbegin() const _NOEXCEPT {
+    return rbegin();
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator crend() const _NOEXCEPT { return rend(); }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT { return __tree_.size() == 0; }
-  _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __tree_.size(); }
-  _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT { return __tree_.max_size(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool empty() const _NOEXCEPT {
+    return __tree_.size() == 0;
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type size() const _NOEXCEPT { return __tree_.size(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type max_size() const _NOEXCEPT {
+    return __tree_.max_size();
+  }
 
   // modifiers:
 #  ifndef _LIBCPP_CXX03_LANG
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> emplace(_Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> emplace(_Args&&... __args) {
     return __tree_.__emplace_unique(std::forward<_Args>(__args)...);
   }
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace_hint(const_iterator __p, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace_hint(const_iterator __p, _Args&&... __args) {
     return __tree_.__emplace_hint_unique(__p, std::forward<_Args>(__args)...).first;
   }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> insert(const value_type& __v) { return __tree_.__emplace_unique(__v); }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, const value_type& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> insert(const value_type& __v) {
+    return __tree_.__emplace_unique(__v);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, const value_type& __v) {
     return __tree_.__emplace_hint_unique(__p, __v).first;
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __first, _InputIterator __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(_InputIterator __first, _InputIterator __last) {
     __tree_.__insert_range_unique(__first, __last);
   }
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert_range(_Range&& __range) {
     __tree_.__insert_range_unique(ranges::begin(__range), ranges::end(__range));
   }
 #  endif
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, bool> insert(value_type&& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> insert(value_type&& __v) {
     return __tree_.__emplace_unique(std::move(__v));
   }
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, value_type&& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, value_type&& __v) {
     return __tree_.__emplace_hint_unique(__p, std::move(__v)).first;
   }
 
-  _LIBCPP_HIDE_FROM_ABI void insert(initializer_list<value_type> __il) { insert(__il.begin(), __il.end()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(initializer_list<value_type> __il) {
+    insert(__il.begin(), __il.end());
+  }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
-  _LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_unique(__k); }
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
-  _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p) { return __tree_.erase(__p); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(const key_type& __k) {
+    return __tree_.__erase_unique(__k);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
+    return __tree_.erase(__f, __l);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
-  _LIBCPP_HIDE_FROM_ABI insert_return_type insert(node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 insert_return_type insert(node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to set::insert()");
     return __tree_.template __node_handle_insert_unique< node_type, insert_return_type>(std::move(__nh));
   }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __hint, node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __hint, node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to set::insert()");
     return __tree_.template __node_handle_insert_unique<node_type>(__hint, std::move(__nh));
   }
-  _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract<node_type>(__it);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_unique(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_unique(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_unique(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_unique(__source.__tree_);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI void swap(set& __s) _NOEXCEPT_(__is_nothrow_swappable_v<__base>) { __tree_.swap(__s.__tree_); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(set& __s) _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
+    __tree_.swap(__s.__tree_);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const _NOEXCEPT { return __tree_.__alloc(); }
-  _LIBCPP_HIDE_FROM_ABI key_compare key_comp() const { return __tree_.value_comp(); }
-  _LIBCPP_HIDE_FROM_ABI value_compare value_comp() const { return __tree_.value_comp(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type get_allocator() const _NOEXCEPT {
+    return __tree_.__alloc();
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_compare key_comp() const { return __tree_.value_comp(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_compare value_comp() const { return __tree_.value_comp(); }
 
   // set operations:
-  _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __tree_.find(__k); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __tree_.find(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const key_type& __k) { return __tree_.find(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const key_type& __k) const {
+    return __tree_.find(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const _K2& __k) {
     return __tree_.find(__k);
   }
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const _K2& __k) const {
     return __tree_.find(__k);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI size_type count(const key_type& __k) const { return __tree_.__count_unique(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const key_type& __k) const {
+    return __tree_.__count_unique(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const _K2& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  endif
 
 #  if _LIBCPP_STD_VER >= 20
-  _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const key_type& __k) const {
+    return find(__k) != end();
+  }
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
 #  endif // _LIBCPP_STD_VER >= 20
 
-  _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const key_type& __k) { return __tree_.__lower_bound_unique(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const key_type& __k) {
+    return __tree_.__lower_bound_unique(__k);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const key_type& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator lower_bound(const key_type& __k) const {
     return __tree_.__lower_bound_unique(__k);
   }
 
@@ -857,46 +904,50 @@ public:
   // match multiple elements.
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const _K2& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const _K2& __k) {
     return __tree_.__lower_bound_multi(__k);
   }
 
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator lower_bound(const _K2& __k) const {
     return __tree_.__lower_bound_multi(__k);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const key_type& __k) { return __tree_.__upper_bound_unique(__k); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const key_type& __k) {
+    return __tree_.__upper_bound_unique(__k);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const key_type& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator upper_bound(const key_type& __k) const {
     return __tree_.__upper_bound_unique(__k);
   }
 
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const _K2& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const _K2& __k) {
     return __tree_.__upper_bound_multi(__k);
   }
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator upper_bound(const _K2& __k) const {
     return __tree_.__upper_bound_multi(__k);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const key_type& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const key_type& __k) {
     return __tree_.__equal_range_unique(__k);
   }
-  _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const key_type& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const key_type& __k) const {
     return __tree_.__equal_range_unique(__k);
   }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const _K2& __k) {
     return __tree_.__equal_range_multi(__k);
   }
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const _K2& __k) const {
     return __tree_.__equal_range_multi(__k);
   }
 #  endif
@@ -949,7 +1000,8 @@ set(initializer_list<_Key>, _Allocator) -> set<_Key, less<_Key>, _Allocator>;
 #  ifndef _LIBCPP_CXX03_LANG
 
 template <class _Key, class _Compare, class _Allocator>
-set<_Key, _Compare, _Allocator>::set(set&& __s, const allocator_type& __a) : __tree_(std::move(__s.__tree_), __a) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 set<_Key, _Compare, _Allocator>::set(set&& __s, const allocator_type& __a)
+    : __tree_(std::move(__s.__tree_), __a) {
   if (__a != __s.get_allocator()) {
     const_iterator __e = cend();
     while (!__s.empty())
@@ -960,7 +1012,7 @@ set<_Key, _Compare, _Allocator>::set(set&& __s, const allocator_type& __a) : __t
 #  endif // _LIBCPP_CXX03_LANG
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator==(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return __x.size() == __y.size() && std::equal(__x.begin(), __x.end(), __y.begin());
 }
@@ -968,31 +1020,31 @@ operator==(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare,
 #  if _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare(__x.begin(), __x.end(), __y.begin(), __y.end());
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator!=(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return !(__x == __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return __y < __x;
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>=(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return !(__x < __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<=(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return !(__y < __x);
 }
@@ -1000,7 +1052,7 @@ operator<=(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare,
 #  else // _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI __synth_three_way_result<_Key>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __synth_three_way_result<_Key>
 operator<=>(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare_three_way(__x.begin(), __x.end(), __y.begin(), __y.end(), std::__synth_three_way);
 }
@@ -1009,14 +1061,14 @@ operator<=>(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare
 
 // specialized algorithms:
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI void swap(set<_Key, _Compare, _Allocator>& __x, set<_Key, _Compare, _Allocator>& __y)
-    _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+swap(set<_Key, _Compare, _Allocator>& __x, set<_Key, _Compare, _Allocator>& __y) _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
   __x.swap(__y);
 }
 
 #  if _LIBCPP_STD_VER >= 20
 template <class _Key, class _Compare, class _Allocator, class _Predicate>
-inline _LIBCPP_HIDE_FROM_ABI typename set<_Key, _Compare, _Allocator>::size_type
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename set<_Key, _Compare, _Allocator>::size_type
 erase_if(set<_Key, _Compare, _Allocator>& __c, _Predicate __pred) {
   return std::__libcpp_erase_if_container(__c, __pred);
 }

>From f29268adce636381be9409c5c01c92281c537865 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 15:58:10 -0500
Subject: [PATCH 04/22] Test set.cons

---
 .../from_range_associative_containers.h       | 14 ++++-----
 .../set.cons/assign_initializer_list.pass.cpp |  4 +--
 .../associative/set/set.cons/copy.pass.cpp    |  8 ++---
 .../set/set.cons/copy_alloc.pass.cpp          |  8 ++---
 .../set/set.cons/copy_assign.pass.cpp         | 31 +++++++++----------
 .../associative/set/set.cons/deduct.pass.cpp  |  2 +-
 .../set/set.cons/from_range.pass.cpp          |  3 +-
 .../set/set.cons/move_alloc.pass.cpp          |  2 +-
 .../test/std/containers/from_range_helpers.h  |  4 +--
 9 files changed, 33 insertions(+), 43 deletions(-)

diff --git a/libcxx/test/std/containers/associative/from_range_associative_containers.h b/libcxx/test/std/containers/associative/from_range_associative_containers.h
index cb3646d738968..2a391b50fc06a 100644
--- a/libcxx/test/std/containers/associative/from_range_associative_containers.h
+++ b/libcxx/test/std/containers/associative/from_range_associative_containers.h
@@ -189,7 +189,7 @@ constexpr bool test_set_constraints() {
 }
 
 template <template <class...> class Container, class T, class Iter, class Sent, class Comp, class Alloc>
-void test_associative_set_with_input(std::vector<T>&& input) {
+TEST_CONSTEXPR_CXX26 void test_associative_set_with_input(std::vector<T>&& input) {
   { // (range)
     std::ranges::subrange in(Iter(input.data()), Sent(Iter(input.data() + input.size())));
     Container<T> c(std::from_range, in);
@@ -232,7 +232,7 @@ void test_associative_set_with_input(std::vector<T>&& input) {
 }
 
 template <template <class...> class Container, class T, class Iter, class Sent, class Comp, class Alloc>
-void test_associative_set() {
+TEST_CONSTEXPR_CXX26 void test_associative_set() {
   auto test_with_input = &test_associative_set_with_input<Container, T, Iter, Sent, Comp, Alloc>;
 
   // Normal input.
@@ -244,7 +244,7 @@ void test_associative_set() {
 }
 
 template <template <class...> class Container>
-void test_associative_set_move_only() {
+TEST_CONSTEXPR_CXX26 void test_associative_set_move_only() {
   MoveOnly input[5];
   std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
 
@@ -252,8 +252,8 @@ void test_associative_set_move_only() {
 }
 
 template <template <class...> class Container>
-void test_set_exception_safety_throwing_copy() {
-#if !defined(TEST_HAS_NO_EXCEPTIONS)
+TEST_CONSTEXPR_CXX26 void test_set_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_IS_CONSTANT_EVALUATED)
   using T = ThrowingCopy<3>;
   T::reset();
   T in[5] = {{1}, {2}, {3}, {4}, {5}};
@@ -270,8 +270,8 @@ void test_set_exception_safety_throwing_copy() {
 }
 
 template <template <class...> class Container, class T>
-void test_set_exception_safety_throwing_allocator() {
-#if !defined(TEST_HAS_NO_EXCEPTIONS)
+TEST_CONSTEXPR_CXX26 void test_set_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_IS_CONSTANT_EVALUATED)
   T in[] = {1, 2};
 
   try {
diff --git a/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
index 1e0de1434aeb9..c37e2f400db3e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/assign_initializer_list.pass.cpp
@@ -21,7 +21,7 @@
 #include "min_allocator.h"
 #include "test_allocator.h"
 
-void basic_test() {
+TEST_CONSTEXPR_CXX26 void basic_test() {
   {
     typedef std::set<int> C;
     typedef C::value_type V;
@@ -54,7 +54,7 @@ void basic_test() {
   }
 }
 
-void duplicate_keys_test() {
+TEST_CONSTEXPR_CXX26 void duplicate_keys_test() {
   test_allocator_statistics alloc_stats;
   typedef std::set<int, std::less<int>, test_allocator<int> > Set;
   {
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
index f2116c019d3b6..ea4a34940bacd 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
@@ -20,7 +20,7 @@
 #include "test_allocator.h"
 
 template <template <class> class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   { // Simple check
     using Set = std::set<int, std::less<int>, Alloc<int> >;
 
@@ -79,7 +79,7 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator>();
   test_alloc<min_allocator>(); // Make sure that fancy pointers work
 
@@ -125,10 +125,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.get_allocator() == other_allocator<int>(10));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
index 200bf71b5f3dd..bb1d251777904 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
@@ -21,7 +21,7 @@
 #include "test_allocator.h"
 
 template <class Alloc>
-void test_alloc(const Alloc& new_alloc) {
+TEST_CONSTEXPR_CXX26 void test_alloc(const Alloc& new_alloc) {
   { // Simple check
     using Set = std::set<int, std::less<int>, Alloc>;
 
@@ -81,7 +81,7 @@ void test_alloc(const Alloc& new_alloc) {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc(std::allocator<int>());
   test_alloc(test_allocator<int>(25)); // Make sure that the new allocator is actually used
   test_alloc(min_allocator<int>());    // Make sure that fancy pointers work
@@ -97,10 +97,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
index 7c301d62cb586..2e47297ccf28e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
@@ -35,41 +35,42 @@ class tracking_allocator {
   using value_type                             = T;
   using propagate_on_container_copy_assignment = std::true_type;
 
-  tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
 
   template <class U>
-  tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
 
-  T* allocate(std::size_t n) {
+  TEST_CONSTEXPR_CXX26 T* allocate(std::size_t n) {
     T* allocation = std::allocator<T>().allocate(n);
     allocs_->push_back(allocation);
     return allocation;
   }
 
-  void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
+  TEST_CONSTEXPR_CXX26 void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
     auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
     assert(res != allocs_->end() && "Trying to deallocate memory from different allocator?");
     allocs_->erase(res);
     std::allocator<T>().deallocate(ptr, n);
   }
 
-  friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  TEST_CONSTEXPR_CXX26 friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ == rhs.allocs_;
   }
 
-  friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  TEST_CONSTEXPR_CXX26 friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ != rhs.allocs_;
   }
 };
 
 struct NoOp {
-  void operator()() {}
+  TEST_CONSTEXPR_CXX26 void operator()() {}
 };
 
 template <class Alloc, class AllocatorInvariant = NoOp>
-void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
-                const Alloc& rhs_alloc                   = Alloc(),
-                AllocatorInvariant check_alloc_invariant = NoOp()) {
+TEST_CONSTEXPR_CXX26 void
+test_alloc(const Alloc& lhs_alloc                   = Alloc(),
+           const Alloc& rhs_alloc                   = Alloc(),
+           AllocatorInvariant check_alloc_invariant = NoOp()) {
   {   // Test empty/non-empty set combinations
     { // assign from a non-empty container into an empty one
       using Set = std::set<int, std::less<int>, Alloc>;
@@ -236,7 +237,7 @@ void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator<int> >();
 #if TEST_STD_VER >= 11
   test_alloc<min_allocator<int> >();
@@ -248,10 +249,10 @@ void test() {
       std::vector<void*>* rhs_allocs_;
 
     public:
-      AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
+      TEST_CONSTEXPR_CXX26 AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
           : lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
 
-      void operator()() {
+      TEST_CONSTEXPR_CXX26 void operator()() {
         assert(lhs_allocs_->empty());
         assert(rhs_allocs_->empty());
       }
@@ -275,10 +276,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
index 56540fbf35c6a..6de41997d424d 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/deduct.pass.cpp
@@ -51,7 +51,7 @@
 #include "test_allocator.h"
 
 struct NotAnAllocator {
-  friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
+  TEST_CONSTEXPR_CXX26 friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
index 23a5136aafcf2..d24cc95d69469 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/from_range.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2147483647
 
 // template<container-compatible-range<value_type> R>
 //   set(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
@@ -22,7 +23,7 @@
 #include "../../from_range_associative_containers.h"
 #include "test_macros.h"
 
-void test_duplicates() {
+TEST_CONSTEXPR_CXX26 void test_duplicates() {
   using T = KeyValue;
 
   std::array input    = {T{1, 'a'}, T{2, 'a'}, T{3, 'a'}, T{3, 'b'}, T{3, 'c'}, T{2, 'b'}, T{4, 'a'}};
diff --git a/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
index 4f15202f2ca83..948cc85e7552d 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/move_alloc.pass.cpp
@@ -73,7 +73,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(m3.key_comp() == C(5));
     LIBCPP_ASSERT(m1.empty());
   }
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef Counter<int> V;
     typedef std::less<V> C;
     typedef test_allocator<V> A;
diff --git a/libcxx/test/std/containers/from_range_helpers.h b/libcxx/test/std/containers/from_range_helpers.h
index edf1d6ce528d7..13bd74d15cf14 100644
--- a/libcxx/test/std/containers/from_range_helpers.h
+++ b/libcxx/test/std/containers/from_range_helpers.h
@@ -54,8 +54,8 @@ struct KeyValue {
   int key;    // Only the key is considered for equality comparison.
   char value; // Allows distinguishing equivalent instances.
 
-  bool operator<(const KeyValue& other) const { return key < other.key; }
-  bool operator==(const KeyValue& other) const { return key == other.key; }
+  TEST_CONSTEXPR_CXX26 bool operator<(const KeyValue& other) const { return key < other.key; }
+  TEST_CONSTEXPR_CXX26 bool operator==(const KeyValue& other) const { return key == other.key; }
 };
 
 template <>

>From b2e088aa09088fd8d263a92547797a297a103917 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 16:15:59 -0500
Subject: [PATCH 05/22] Test set.erasure

---
 .../containers/associative/set/set.erasure/erase_if.pass.cpp  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
index 978e80821eb49..a5b2c741e02e4 100644
--- a/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase_if.pass.cpp
@@ -20,14 +20,14 @@
 #include "min_allocator.h"
 
 template <class S, class Pred>
-void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
+TEST_CONSTEXPR_CXX26 void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
   ASSERT_SAME_TYPE(typename S::size_type, decltype(std::erase_if(s, p)));
   assert(expected_erased_count == std::erase_if(s, p));
   assert(s == expected);
 }
 
 template <typename S>
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   auto is1   = [](auto v) { return v == 1; };
   auto is2   = [](auto v) { return v == 2; };
   auto is3   = [](auto v) { return v == 3; };

>From 42f2950a3143df5907a575323230e284e3cf3834 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 16:18:08 -0500
Subject: [PATCH 06/22] Test set.observers

---
 .../std/containers/associative/set/set.observers/comp.pass.cpp  | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
index 9aef8c7cce41b..dcea523540247 100644
--- a/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.observers/comp.pass.cpp
@@ -14,6 +14,8 @@
 #include <set>
 #include <cassert>
 
+#include "test_macros.h"
+
 TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::set<int> set_type;
 

>From b30297eee8b6ed88b907c41663b9203003a3bac7 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 17:48:30 -0500
Subject: [PATCH 07/22] hash correct version from main

---
 libcxx/include/__functional/hash.h         |  9 ---------
 libcxx/test/support/poisoned_hash_helper.h | 10 +++-------
 2 files changed, 3 insertions(+), 16 deletions(-)

diff --git a/libcxx/include/__functional/hash.h b/libcxx/include/__functional/hash.h
index 83bbf1b5e26c3..f74f25fa6e84b 100644
--- a/libcxx/include/__functional/hash.h
+++ b/libcxx/include/__functional/hash.h
@@ -433,13 +433,10 @@ struct __hash_impl<long double> : __scalar_hash<long double> {
 template <class _Tp>
 struct hash : public __hash_impl<_Tp> {};
 
-#if _LIBCPP_STD_VER >= 17
-
 template <>
 struct hash<nullptr_t> : public __unary_function<nullptr_t, size_t> {
   _LIBCPP_HIDE_FROM_ABI size_t operator()(nullptr_t) const _NOEXCEPT { return 662607004ull; }
 };
-#endif
 
 #ifndef _LIBCPP_CXX03_LANG
 template <class _Key, class _Hash>
@@ -452,18 +449,12 @@ template <class _Key, class _Hash = hash<_Key> >
 using __has_enabled_hash _LIBCPP_NODEBUG =
     integral_constant<bool, __check_hash_requirements<_Key, _Hash>::value && is_default_constructible<_Hash>::value >;
 
-#  if _LIBCPP_STD_VER >= 17
 template <class _Type, class>
 using __enable_hash_helper_imp _LIBCPP_NODEBUG = _Type;
 
 template <class _Type, class... _Keys>
 using __enable_hash_helper _LIBCPP_NODEBUG =
     __enable_hash_helper_imp<_Type, __enable_if_t<__all<__has_enabled_hash<_Keys>::value...>::value> >;
-#  else
-template <class _Type, class...>
-using __enable_hash_helper _LIBCPP_NODEBUG = _Type;
-#  endif
-
 #endif // !_LIBCPP_CXX03_LANG
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/support/poisoned_hash_helper.h b/libcxx/test/support/poisoned_hash_helper.h
index 93b579d2dfde3..cd71cd70d6a84 100644
--- a/libcxx/test/support/poisoned_hash_helper.h
+++ b/libcxx/test/support/poisoned_hash_helper.h
@@ -123,13 +123,9 @@ struct Class {};
 // Each header that declares the std::hash template provides enabled
 // specializations of std::hash for std::nullptr_t and all cv-unqualified
 // arithmetic, enumeration, and pointer types.
-#if TEST_STD_VER >= 17
-using MaybeNullptr = types::type_list<std::nullptr_t>;
-#else
-using MaybeNullptr = types::type_list<>;
-#endif
-using LibraryHashTypes = types::
-    concatenate_t<types::arithmetic_types, types::type_list<Enum, EnumClass, void*, void const*, Class*>, MaybeNullptr>;
+using LibraryHashTypes =
+    types::concatenate_t<types::arithmetic_types,
+                         types::type_list<Enum, EnumClass, void*, void const*, Class*, std::nullptr_t>>;
 
 struct TestHashEnabled {
   template <class T>

>From df77e29f261fd166ef1a56908df8519d941b59fb Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 17:57:58 -0500
Subject: [PATCH 08/22] saturation correct version from main

---
 libcxx/include/__numeric/saturation_arithmetic.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 7a7410b5dea08..4491bab2b1479 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -121,27 +121,27 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Rp __saturate_cast(_Tp __x) noexcept {
 #if _LIBCPP_STD_VER >= 26
 
 template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp add_sat(_Tp __x, _Tp __y) noexcept {
   return std::__add_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp sub_sat(_Tp __x, _Tp __y) noexcept {
   return std::__sub_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp mul_sat(_Tp __x, _Tp __y) noexcept {
   return std::__mul_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp div_sat(_Tp __x, _Tp __y) noexcept {
   return std::__div_sat(__x, __y);
 }
 
 template <__signed_or_unsigned_integer _Rp, __signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Rp saturate_cast(_Tp __x) noexcept {
   return std::__saturate_cast<_Rp>(__x);
 }
 

>From 7ce7dcdba3724e6109ca2b925077771d7f2d79ca Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 9 Nov 2025 21:10:14 -0500
Subject: [PATCH 09/22] Fix set/ final tests

---
 libcxx/include/__node_handle                      |  4 +++-
 .../containers/associative/set/contains.pass.cpp  |  6 ++++--
 .../associative/set/contains_transparent.pass.cpp | 15 ++++++++++-----
 .../associative/set/count_transparent.pass.cpp    | 10 +++++++---
 .../containers/associative/set/emplace.pass.cpp   |  5 +++--
 .../associative/set/emplace_hint.pass.cpp         |  5 +++--
 .../set/equal_range_transparent.pass.cpp          | 10 +++++++---
 .../associative/set/erase_iter.pass.cpp           |  4 ++--
 .../associative/set/extract_iterator.pass.cpp     |  4 ++--
 .../associative/set/extract_key.pass.cpp          |  4 ++--
 ...rt_and_emplace_allocator_requirements.pass.cpp | 15 ++++-----------
 .../containers/associative/set/insert_cv.pass.cpp |  2 +-
 .../associative/set/insert_iter_iter.pass.cpp     |  8 ++------
 .../associative/set/insert_node_type.pass.cpp     | 13 +++++++------
 .../set/insert_node_type_hint.pass.cpp            |  9 +++++----
 .../associative/set/insert_range.pass.cpp         |  1 +
 .../std/containers/associative/set/merge.pass.cpp | 13 +++++++------
 .../test/std/containers/insert_range_maps_sets.h  | 12 ++++++------
 .../set_allocator_requirement_test_templates.h    |  2 +-
 19 files changed, 77 insertions(+), 65 deletions(-)

diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index 58be5b9d4ddd5..6d253cb4eaa98 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -172,7 +172,9 @@ template <class _NodeType, class _Derived>
 struct __set_node_handle_specifics {
   typedef typename _NodeType::__node_value_type value_type;
 
-  _LIBCPP_HIDE_FROM_ABI value_type& value() const { return static_cast<_Derived const*>(this)->__ptr_->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_type& value() const {
+    return static_cast<_Derived const*>(this)->__ptr_->__get_value();
+  }
 };
 
 template <class _NodeType, class _Derived>
diff --git a/libcxx/test/std/containers/associative/set/contains.pass.cpp b/libcxx/test/std/containers/associative/set/contains.pass.cpp
index 20b1eeb839414..c147b01d73c10 100644
--- a/libcxx/test/std/containers/associative/set/contains.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/contains.pass.cpp
@@ -11,12 +11,14 @@
 #include <cassert>
 #include <set>
 
+#include "test_macros.h"
+
 // <set>
 
 // constexpr bool contains(const key_type& x) const; // constexpr since C++26
 
 template <typename T, typename V, typename B, typename... Vals>
-void test(B bad, Vals... args) {
+TEST_CONSTEXPR_CXX26 void test(B bad, Vals... args) {
   T set;
   V vals[] = {args...};
 
@@ -39,7 +41,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     test<std::set<int>, int>(14, 10, 11, 12, 13);
     test<std::set<char>, char>('e', 'a', 'b', 'c', 'd');
   }
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     test<std::multiset<int>, int>(14, 10, 11, 12, 13);
     test<std::multiset<char>, char>('e', 'a', 'b', 'c', 'd');
   }
diff --git a/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
index acd03e649095f..0b7e032414425 100644
--- a/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/contains_transparent.pass.cpp
@@ -18,18 +18,22 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 template <typename Container>
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   Container s{{2, 1}, {1, 2}, {1, 3}, {1, 4}, {2, 2}};
 
   assert(s.contains(1));
@@ -38,7 +42,8 @@ void test() {
 
 TEST_CONSTEXPR_CXX26 bool test() {
   test<std::set<std::pair<int, int>, Comp> >();
-  test<std::multiset<std::pair<int, int>, Comp> >();
+  if (!TEST_IS_CONSTANT_EVALUATED)
+    test<std::multiset<std::pair<int, int>, Comp> >();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
index 9147b38e5d336..a14f4efe8c48d 100644
--- a/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/count_transparent.pass.cpp
@@ -19,14 +19,18 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/set/emplace.pass.cpp b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
index fecd9ef2fe7f9..6c93d99356c12 100644
--- a/libcxx/test/std/containers/associative/set/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
@@ -24,7 +24,7 @@
 #include "min_allocator.h"
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::set<DefaultOnly> M;
     typedef std::pair<M::iterator, bool> R;
     M m;
@@ -43,7 +43,8 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 1);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED)
+    assert(DefaultOnly::count == 0);
   {
     typedef std::set<Emplaceable> M;
     typedef std::pair<M::iterator, bool> R;
diff --git a/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
index 5c68000ffbab9..5282f4295ec8e 100644
--- a/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace_hint.pass.cpp
@@ -24,7 +24,7 @@
 #include "min_allocator.h"
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::set<DefaultOnly> M;
     typedef M::iterator R;
     M m;
@@ -41,7 +41,8 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 1);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED)
+    assert(DefaultOnly::count == 0);
   {
     typedef std::set<Emplaceable> M;
     typedef M::iterator R;
diff --git a/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
index 92c1c64be658c..9f2606412a5d2 100644
--- a/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/equal_range_transparent.pass.cpp
@@ -23,14 +23,18 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
index 7682a81dc7407..952714a3720b0 100644
--- a/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/erase_iter.pass.cpp
@@ -20,10 +20,10 @@
 
 struct TemplateConstructor {
   template <typename T>
-  TemplateConstructor(const T&) {}
+  TEST_CONSTEXPR_CXX26 TemplateConstructor(const T&) {}
 };
 
-bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
+TEST_CONSTEXPR_CXX26 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
 TEST_CONSTEXPR_CXX26 bool test() {
   {
diff --git a/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
index 1511b08079d03..59a83338da06f 100644
--- a/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract_iterator.pass.cpp
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   std::size_t sz = c.size();
 
   for (auto first = c.cbegin(); first != c.cend();) {
@@ -42,7 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     test(m);
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::set<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     assert(Counter_base::gConstructed == 6);
     test(m);
diff --git a/libcxx/test/std/containers/associative/set/extract_key.pass.cpp b/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
index 07f6d193179a8..432bfbdb1b8fb 100644
--- a/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract_key.pass.cpp
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container, class KeyTypeIter>
-void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
+TEST_CONSTEXPR_CXX26 void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   std::size_t sz = c.size();
   assert((std::size_t)std::distance(first, last) == sz);
 
@@ -48,7 +48,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     test(m, std::begin(keys), std::end(keys));
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::set<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     {
       Counter<int> keys[] = {1, 2, 3, 4, 5, 6};
diff --git a/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
index ace31eb66ea8d..4711473de4dad 100644
--- a/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_and_emplace_allocator_requirements.pass.cpp
@@ -10,9 +10,9 @@
 
 // class set
 
-// constexpr insert(...) // constexpr since C++26
-// constexpr emplace(...) // constexpr since C++26
-// constexpr emplace_hint(...) // constexpr since C++26
+// insert(...)
+// emplace(...)
+// emplace_hint(...)
 
 // UNSUPPORTED: c++03
 
@@ -21,17 +21,10 @@
 #include "container_test_types.h"
 #include "../../set_allocator_requirement_test_templates.h"
 
-TEST_CONSTEXPR_CXX26 bool test() {
+int main(int, char**) {
   testSetInsert<TCT::set<> >();
   testSetEmplace<TCT::set<> >();
   testSetEmplaceHint<TCT::set<> >();
 
-  return true;
-}
-int main(int, char**) {
-  test();
-#if TEST_STD_VER >= 26
-  static_assert(test());
-#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
index ef278224f0229..3dc33a80fe2ad 100644
--- a/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_cv.pass.cpp
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 
 template <class Container>
-void do_insert_cv_test() {
+TEST_CONSTEXPR_CXX26 void do_insert_cv_test() {
   typedef Container M;
   typedef std::pair<typename M::iterator, bool> R;
   typedef typename M::value_type VT;
diff --git a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
index 6d6bd185afa04..ca0180af82897 100644
--- a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp
@@ -21,7 +21,7 @@
 #include "test_iterators.h"
 
 template <class Iter, class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   {   // Check that an empty range works correctly
     { // Without elements in the container
       using Map = std::set<int, std::less<int>, Alloc>;
@@ -178,13 +178,9 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<cpp17_input_iterator<int*>, std::allocator<int> >();
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
index 935a90afedb2b..1512debd4e0bd 100644
--- a/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_node_type.pass.cpp
@@ -21,7 +21,7 @@
 #include "min_allocator.h"
 
 template <class Container, class T>
-void verify_insert_return_type(T&& t) {
+TEST_CONSTEXPR_CXX26 void verify_insert_return_type(T&& t) {
   using verified_type = std::remove_cv_t<std::remove_reference_t<T>>;
   static_assert(std::is_aggregate_v<verified_type>);
   static_assert(std::is_same_v<verified_type, typename Container::insert_return_type>);
@@ -42,19 +42,20 @@ void verify_insert_return_type(T&& t) {
 }
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto p = c.insert(key);
   assert(p.second);
   return c.extract(p.first);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
 
+  Container c2;
+
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c2, i);
     assert(!node.empty());
     typename Container::insert_return_type irt = c.insert(std::move(node));
     assert(node.empty());
@@ -77,7 +78,7 @@ void test(Container& c) {
   }
 
   { // Insert duplicate node.
-    typename Container::node_type dupl = nf(0);
+    typename Container::node_type dupl = nf(c2, 0);
     auto irt                           = c.insert(std::move(dupl));
     assert(dupl.empty());
     assert(!irt.inserted);
diff --git a/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
index f14263595ac6a..a1df9de53ae8a 100644
--- a/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_node_type_hint.pass.cpp
@@ -19,19 +19,20 @@
 #include "min_allocator.h"
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto p = c.insert(key);
   assert(p.second);
   return c.extract(p.first);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
 
+  Container c2;
+
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c2, i);
     assert(!node.empty());
     std::size_t prev = c.size();
     auto it          = c.insert(c.end(), std::move(node));
diff --git a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
index 654d4d425774a..00cead676709a 100644
--- a/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/insert_range.pass.cpp
@@ -9,6 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
 // ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-missing-field-initializers
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2147483647
 
 // <set>
 
diff --git a/libcxx/test/std/containers/associative/set/merge.pass.cpp b/libcxx/test/std/containers/associative/set/merge.pass.cpp
index 4c76c59ec5de5..bcaf13d4a7e1b 100644
--- a/libcxx/test/std/containers/associative/set/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/merge.pass.cpp
@@ -27,7 +27,7 @@
 #include "Counter.h"
 
 template <class Set>
-bool set_equal(const Set& set, Set other) {
+TEST_CONSTEXPR_CXX26 bool set_equal(const Set& set, Set other) {
   return set == other;
 }
 
@@ -55,8 +55,8 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(set_equal(dst, {1, 2, 3, 4, 5}));
   }
 
-#ifndef TEST_HAS_NO_EXCEPTIONS
-  {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     bool do_throw = false;
     typedef std::set<Counter<int>, throw_comparator> set_type;
     set_type src({1, 3, 5}, throw_comparator(do_throw));
@@ -75,13 +75,14 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(set_equal(dst, set_type({2, 4, 5}, throw_comparator(do_throw))));
   }
 #endif
-  assert(Counter_base::gConstructed == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED)
+    assert(Counter_base::gConstructed == 0);
   struct comparator {
     comparator() = default;
 
     bool operator()(const Counter<int>& lhs, const Counter<int>& rhs) const { return lhs < rhs; }
   };
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::set<Counter<int>, std::less<Counter<int>>> first_set_type;
     typedef std::set<Counter<int>, comparator> second_set_type;
     typedef std::multiset<Counter<int>, comparator> third_set_type;
@@ -128,7 +129,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
       first.merge(second);
       first.merge(std::move(second));
     }
-    {
+    if (!TEST_IS_CONSTANT_EVALUATED) {
       std::multiset<int> second;
       first.merge(second);
       first.merge(std::move(second));
diff --git a/libcxx/test/std/containers/insert_range_maps_sets.h b/libcxx/test/std/containers/insert_range_maps_sets.h
index 6e344d8ad3b0f..8563b7b43b934 100644
--- a/libcxx/test/std/containers/insert_range_maps_sets.h
+++ b/libcxx/test/std/containers/insert_range_maps_sets.h
@@ -200,7 +200,7 @@ TestCaseMapSet<std::pair<K, V>> constexpr NElementsContainer_RangeWithDuplicates
         {16, 'D'}}};
 
 template <class Container, class T, class Iter, class Sent>
-void test_map_set_insert_range(bool allow_duplicates = false) {
+TEST_CONSTEXPR_CXX26 void test_map_set_insert_range(bool allow_duplicates = false) {
   auto test = [&](const TestCaseMapSet<T>& test_case, bool check_multi = false) {
     Container c(test_case.initial.begin(), test_case.initial.end());
     auto in = wrap_input<Iter, Sent>(test_case.input);
@@ -250,7 +250,7 @@ void test_map_set_insert_range(bool allow_duplicates = false) {
 // Move-only types.
 
 template <template <class...> class Container>
-void test_set_insert_range_move_only() {
+TEST_CONSTEXPR_CXX26 void test_set_insert_range_move_only() {
   MoveOnly input[5];
   std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
 
@@ -271,8 +271,8 @@ void test_map_insert_range_move_only() {
 // Exception safety.
 
 template <template <class...> class Container>
-void test_set_insert_range_exception_safety_throwing_copy() {
-#if !defined(TEST_HAS_NO_EXCEPTIONS)
+TEST_CONSTEXPR_CXX26 void test_set_insert_range_exception_safety_throwing_copy() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_IS_CONSTANT_EVALUATED)
   using T = ThrowingCopy<3>;
   T::reset();
   T in[5] = {{1}, {2}, {3}, {4}, {5}};
@@ -313,8 +313,8 @@ void test_map_insert_range_exception_safety_throwing_copy() {
 }
 
 template <template <class...> class Container, class T>
-void test_assoc_set_insert_range_exception_safety_throwing_allocator() {
-#if !defined(TEST_HAS_NO_EXCEPTIONS)
+TEST_CONSTEXPR_CXX26 void test_assoc_set_insert_range_exception_safety_throwing_allocator() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS) && !defined(TEST_IS_CONSTANT_EVALUATED)
   T in[] = {1, 2};
 
   try {
diff --git a/libcxx/test/std/containers/set_allocator_requirement_test_templates.h b/libcxx/test/std/containers/set_allocator_requirement_test_templates.h
index 10552a5b9ee21..f6bda0eddfbb4 100644
--- a/libcxx/test/std/containers/set_allocator_requirement_test_templates.h
+++ b/libcxx/test/std/containers/set_allocator_requirement_test_templates.h
@@ -27,7 +27,7 @@
 #include "container_test_types.h"
 
 template <class Container>
-void testSetInsert() {
+TEST_CONSTEXPR_CXX26 void testSetInsert() {
   typedef typename Container::value_type ValueTp;
   ConstructController* cc = getConstructController();
   cc->reset();

>From 5764d913c2906ac9010f806395aa2a8b073ce660 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Mon, 10 Nov 2025 07:19:09 -0500
Subject: [PATCH 10/22] restore
 libcxx/test/std/containers/associative/iterator_types.pass.cpp

---
 .../std/containers/associative/iterator_types.pass.cpp   | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/libcxx/test/std/containers/associative/iterator_types.pass.cpp b/libcxx/test/std/containers/associative/iterator_types.pass.cpp
index f394b000615d8..2800549905f1b 100644
--- a/libcxx/test/std/containers/associative/iterator_types.pass.cpp
+++ b/libcxx/test/std/containers/associative/iterator_types.pass.cpp
@@ -46,7 +46,7 @@ void testSet() {
   }
 }
 
-TEST_CONSTEXPR_CXX26 bool test() {
+int main(int, char**) {
   {
     typedef std::map<int, int> Map;
     typedef std::pair<const int, int> ValueTp;
@@ -124,12 +124,5 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
 #endif
 
-  return true;
-}
-int main(int, char**) {
-  test();
-#if TEST_STD_VER >= 26
-  static_assert(test());
-#endif
   return 0;
 }

>From 238229f3cc3d76a3ba038e55a827a4a48197cea0 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 20 Jun 2026 10:56:56 +0100
Subject: [PATCH 11/22] Add feature flag for set

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 ++
 libcxx/include/version                        |  2 ++
 .../set.version.compile.pass.cpp              | 27 +++++++++++++++++++
 .../version.version.compile.pass.cpp          | 27 +++++++++++++++++++
 .../generate_feature_test_macro_components.py |  5 ++++
 5 files changed, 63 insertions(+)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 8e26bdcd860c1..a656bd80497e6 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -450,6 +450,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_constexpr_queue``                              ``202502L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_constexpr_set``                                ``202502L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_constrained_equality``                         ``202411L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_copyable_function``                            *unimplemented*
diff --git a/libcxx/include/version b/libcxx/include/version
index 7f2dc9e4b72ab..711ba498ac101 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -84,6 +84,7 @@ __cpp_lib_constexpr_memory                              202202L <memory>
 __cpp_lib_constexpr_new                                 202406L <new>
 __cpp_lib_constexpr_numeric                             201911L <numeric>
 __cpp_lib_constexpr_queue                               202502L <queue>
+__cpp_lib_constexpr_set                                 202502L <set>
 __cpp_lib_constexpr_string                              201907L <string>
 __cpp_lib_constexpr_string_view                         201811L <string_view>
 __cpp_lib_constexpr_tuple                               201811L <tuple>
@@ -575,6 +576,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #   define __cpp_lib_constexpr_new                      202406L
 # endif
 # define __cpp_lib_constexpr_queue                      202502L
+# define __cpp_lib_constexpr_set                        202502L
 # define __cpp_lib_constrained_equality                 202411L
 // # define __cpp_lib_copyable_function                    202306L
 // # define __cpp_lib_debugging                            202311L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
index 5dc69f29d0ecd..d1a0570557246 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
@@ -32,6 +32,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should not be defined before c++23"
 #  endif
@@ -66,6 +70,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should not be defined before c++23"
 #  endif
@@ -106,6 +114,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should not be defined before c++23"
 #  endif
@@ -152,6 +164,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should not be defined before c++23"
 #  endif
@@ -210,6 +226,10 @@
 #    error "__cpp_lib_associative_heterogeneous_insertion should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should be defined in c++23"
 #  endif
@@ -280,6 +300,13 @@
 #    endif
 #  endif
 
+#  ifndef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should be defined in c++26"
+#  endif
+#  if __cpp_lib_constexpr_set != 202502L
+#    error "__cpp_lib_constexpr_set should have the value 202502L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_containers_ranges
 #    error "__cpp_lib_containers_ranges should be defined in c++26"
 #  endif
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 d9c78b73f7e23..a88ec24e4c483 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
@@ -252,6 +252,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should not be defined before c++20"
 #  endif
@@ -1188,6 +1192,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should not be defined before c++20"
 #  endif
@@ -2226,6 +2234,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should not be defined before c++20"
 #  endif
@@ -3510,6 +3522,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should be defined in c++20"
 #  endif
@@ -5004,6 +5020,10 @@
 #    error "__cpp_lib_constexpr_queue should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should be defined in c++23"
 #  endif
@@ -6744,6 +6764,13 @@
 #    error "__cpp_lib_constexpr_queue should have the value 202502L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_constexpr_set
+#    error "__cpp_lib_constexpr_set should be defined in c++26"
+#  endif
+#  if __cpp_lib_constexpr_set != 202502L
+#    error "__cpp_lib_constexpr_set should have the value 202502L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should be defined in c++26"
 #  endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d764a1f677ba6..078e3b815742c 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -432,6 +432,11 @@ def add_version_header(tc):
             "values": {"c++26": 202502},
             "headers": ["queue"],
         },
+        {
+            "name": "__cpp_lib_constexpr_set",
+            "values": {"c++26": 202502},
+            "headers": ["set"],
+        },
         {
             "name": "__cpp_lib_constexpr_string",
             "values": {"c++20": 201907},

>From d559bade444c4925d7ffa5355fbef9f413a12f33 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 21 Jun 2026 22:14:01 +0100
Subject: [PATCH 12/22] add set back to constexpr

---
 .../alg.foreach/ranges.for_each.associative.pass.cpp      | 8 ++------
 .../set/set.nonmember/compare.three_way.pass.cpp          | 1 -
 .../std/containers/container.node/node_handle.pass.cpp    | 4 ++--
 3 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
index e0186654e4bbc..1ee485b1aef0d 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
@@ -253,23 +253,19 @@ TEST_CONSTEXPR_CXX26 void test_invoke_map_like() {
 
 TEST_CONSTEXPR_CXX26 bool test() {
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when set is made constexpr
-    test_node_container<std::set<int> >([](int i) { return i; });
-
     // FIXME: remove when multiset is made constexpr
     test_node_container<std::multiset<int> >([](int i) { return i; });
   }
+  test_node_container<std::set<int> >([](int i) { return i; });
   test_node_container<std::map<int, int> >([](int i) { return std::make_pair(i, i); });
   test_node_container<std::multimap<int, int> >([](int i) { return std::make_pair(i, i); });
 
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when set is made constexpr
-    test_invoke_set_like<std::set>();
-
     // FIXME: remove when multiset is made constexpr
     test_invoke_set_like<std::multiset>();
   }
 
+  test_invoke_set_like<std::set>();
   test_invoke_map_like<std::map>();
   test_invoke_map_like<std::multimap>();
 
diff --git a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
index 563a8c12db828..ce34b7d159dd4 100644
--- a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
@@ -22,7 +22,6 @@
 
 TEST_CONSTEXPR_CXX26 bool test() {
   assert((test_ordered_set_container_spaceship<std::set>()));
-  // `std::set` is not constexpr, so no `static_assert` test here.
   return true;
 }
 int main(int, char**) {
diff --git a/libcxx/test/std/containers/container.node/node_handle.pass.cpp b/libcxx/test/std/containers/container.node/node_handle.pass.cpp
index 38c1f60a31c49..a6737c9037893 100644
--- a/libcxx/test/std/containers/container.node/node_handle.pass.cpp
+++ b/libcxx/test/std/containers/container.node/node_handle.pass.cpp
@@ -150,10 +150,10 @@ TEST_CONSTEXPR_CXX26 bool test_insert_return_type() {
 TEST_CONSTEXPR_CXX26 bool test() {
   test_node_handle_operations<std::map<int, int>>();
   test_node_handle_operations_multi<std::multimap<int, int>>();
+  test_node_handle_operations<std::set<int>>();
 
   // FIXME: update when other containers are made constexpr
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    test_node_handle_operations<std::set<int>>();
     test_node_handle_operations_multi<std::multiset<int>>();
     test_node_handle_operations<std::unordered_map<int, int>>();
     test_node_handle_operations_multi<std::unordered_multimap<int, int>>();
@@ -162,10 +162,10 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
 
   test_insert_return_type<std::map<int, int>>();
+  test_insert_return_type<std::set<int>>();
 
   // FIXME: update when other containers are made constexpr
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    test_insert_return_type<std::set<int>>();
     test_insert_return_type<std::unordered_map<int, int>>();
     test_insert_return_type<std::unordered_set<int>>();
   }

>From ee147e46e940c79ef37111bb04d381a95e510cd7 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Thu, 25 Jun 2026 08:34:28 +0100
Subject: [PATCH 13/22] missing constexpr

---
 .../alg.foreach/ranges.for_each.associative.pass.cpp      | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
index 1ee485b1aef0d..02698819d71df 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
@@ -189,23 +189,23 @@ TEST_CONSTEXPR_CXX26 void test_node_container(Converter conv) {
 }
 
 template <template <class> class Container>
-void test_invoke_set_like() {
+TEST_CONSTEXPR_CXX26 void test_invoke_set_like() {
   { // check that std::invoke is used
     struct T {
       mutable int i = 3;
 
-      void zero() const { i = 0; }
+      TEST_CONSTEXPR_CXX26 void zero() const { i = 0; }
     };
 
     class S {
       int val_;
 
     public:
-      S(int val) : val_(val) {}
+      TEST_CONSTEXPR_CXX26 S(int val) : val_(val) {}
 
       T j;
 
-      bool operator<(const S& rhs) const { return val_ < rhs.val_; }
+      TEST_CONSTEXPR_CXX26 bool operator<(const S& rhs) const { return val_ < rhs.val_; }
     };
 
     { // Iterator overload

>From 330720d49f57abc087a9c7e9aa4df5fedca03eb9 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Thu, 25 Jun 2026 08:38:06 +0100
Subject: [PATCH 14/22] cleanup

---
 libcxx/include/set | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/include/set b/libcxx/include/set
index 565778a836fd1..a8e245b22ae51 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -10,8 +10,6 @@
 #ifndef _LIBCPP_SET
 #define _LIBCPP_SET
 
-// TODO: add constexpr everywhere
-
 /*
 
     set synopsis

>From 6c70a6075dcb19eccb6e2da3205aa1b97c8f37f0 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 27 Jun 2026 13:54:32 +0100
Subject: [PATCH 15/22] Initial constexpr

---
 libcxx/include/set                            | 249 +++++++++++-------
 .../associative/multiset/clear.pass.cpp       |  11 +-
 .../associative/multiset/count.pass.cpp       |  11 +-
 .../multiset/count_transparent.pass.cpp       |  11 +-
 .../associative/multiset/emplace.pass.cpp     |  11 +-
 .../multiset/emplace_hint.pass.cpp            |  11 +-
 .../associative/multiset/empty.pass.cpp       |  11 +-
 .../associative/multiset/equal_range.pass.cpp |  13 +-
 .../multiset/equal_range_transparent.pass.cpp |  13 +-
 .../associative/multiset/erase_iter.pass.cpp  |  11 +-
 .../multiset/erase_iter_iter.pass.cpp         |  11 +-
 .../associative/multiset/erase_key.pass.cpp   |  11 +-
 .../multiset/extract_iterator.pass.cpp        |  11 +-
 .../associative/multiset/extract_key.pass.cpp |  11 +-
 .../associative/multiset/find.pass.cpp        |  13 +-
 .../multiset/get_allocator.pass.cpp           |  11 +-
 .../multiset/incomplete_type.pass.cpp         |  13 +-
 .../associative/multiset/insert_cv.pass.cpp   |  11 +-
 ...rt_emplace_allocator_requirements.pass.cpp |   9 +-
 .../multiset/insert_initializer_list.pass.cpp |  11 +-
 .../multiset/insert_iter_cv.pass.cpp          |  11 +-
 .../multiset/insert_iter_iter.pass.cpp        |   7 +-
 .../multiset/insert_iter_rv.pass.cpp          |  11 +-
 .../multiset/insert_node_type.pass.cpp        |  11 +-
 .../multiset/insert_node_type_hint.pass.cpp   |  11 +-
 .../multiset/insert_range.pass.cpp            |  11 +-
 .../associative/multiset/insert_rv.pass.cpp   |  11 +-
 .../associative/multiset/iterator.pass.cpp    |  33 ++-
 .../associative/multiset/lower_bound.pass.cpp |  13 +-
 .../associative/multiset/max_size.pass.cpp    |  11 +-
 .../associative/multiset/merge.pass.cpp       |  17 +-
 .../multiset/multiset.cons/alloc.pass.cpp     |  11 +-
 .../assign_initializer_list.pass.cpp          |  11 +-
 .../multiset/multiset.cons/compare.pass.cpp   |  11 +-
 .../multiset.cons/compare_alloc.pass.cpp      |  11 +-
 .../multiset/multiset.cons/copy.pass.cpp      |  11 +-
 .../multiset.cons/copy_alloc.pass.cpp         |  11 +-
 .../multiset.cons/copy_assign.pass.cpp        |  11 +-
 .../multiset/multiset.cons/deduct.pass.cpp    |   9 +-
 .../multiset/multiset.cons/default.pass.cpp   |  11 +-
 .../multiset.cons/default_noexcept.pass.cpp   |  11 +-
 .../multiset.cons/dtor_noexcept.pass.cpp      |  11 +-
 .../multiset.cons/from_range.pass.cpp         |  13 +-
 .../multiset.cons/initializer_list.pass.cpp   |  11 +-
 .../initializer_list_compare.pass.cpp         |  11 +-
 .../initializer_list_compare_alloc.pass.cpp   |  11 +-
 .../multiset/multiset.cons/iter_iter.pass.cpp |  11 +-
 .../multiset.cons/iter_iter_alloc.pass.cpp    |  11 +-
 .../multiset.cons/iter_iter_comp.pass.cpp     |  11 +-
 .../multiset/multiset.cons/move.pass.cpp      |  11 +-
 .../multiset.cons/move_alloc.pass.cpp         |  11 +-
 .../multiset.cons/move_assign.pass.cpp        |  11 +-
 .../multiset.cons/move_noexcept.pass.cpp      |  11 +-
 .../multiset.erasure/erase_if.pass.cpp        |  11 +-
 .../compare.three_way.pass.cpp                |  11 +-
 .../multiset.nonmember/op_compare.pass.cpp    |   9 +-
 .../multiset/multiset.observers/comp.pass.cpp |  13 +-
 .../multiset.special/member_swap.pass.cpp     |  11 +-
 .../multiset.special/non_member_swap.pass.cpp |  11 +-
 .../multiset.special/swap_noexcept.pass.cpp   |  13 +-
 .../associative/multiset/size.pass.cpp        |  10 +-
 .../associative/multiset/upper_bound.pass.cpp |  13 +-
 62 files changed, 723 insertions(+), 232 deletions(-)

diff --git a/libcxx/include/set b/libcxx/include/set
index a8e245b22ae51..0409131ac6981 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -1176,31 +1176,34 @@ public:
   friend class multiset;
 
   // construct/copy/destroy:
-  _LIBCPP_HIDE_FROM_ABI multiset() _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset() _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_default_constructible<key_compare>::value&&
           is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(value_compare()) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const value_compare& __comp) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const value_compare& __comp) _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(__comp) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const value_compare& __comp, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {}
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__f, __l);
   }
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multiset(__f, __l, key_compare(), __a) {}
 #  endif
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__f, __l);
@@ -1208,7 +1211,7 @@ public:
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(from_range_t,
            _Range&& __range,
            const key_compare& __comp = key_compare(),
@@ -1218,213 +1221,272 @@ public:
   }
 
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI multiset(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(from_range_t, _Range&& __range, const allocator_type& __a)
       : multiset(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(const multiset& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(const multiset& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(const multiset& __s) = default;
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(multiset&& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s, const allocator_type& __a) : __tree_(std::move(__s.__tree_), __a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(multiset&& __s, const allocator_type& __a)
+      : __tree_(std::move(__s.__tree_), __a) {}
 #  endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const allocator_type& __a) : __tree_(__a) {}
-  _LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s, const allocator_type& __a) : __tree_(__s.__tree_, __a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const allocator_type& __a) : __tree_(__a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(const multiset& __s, const allocator_type& __a)
+      : __tree_(__s.__tree_, __a) {}
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__il.begin(), __il.end());
   }
 
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(initializer_list<value_type> __il, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__il.begin(), __il.end());
   }
 
 #    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(initializer_list<value_type> __il, const allocator_type& __a)
       : multiset(__il, key_compare(), __a) {}
 #    endif
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(initializer_list<value_type> __il) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(initializer_list<value_type> __il) {
     clear();
     insert(__il.begin(), __il.end());
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(multiset&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(multiset&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI ~multiset() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~multiset() {
     static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return __tree_.begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return __tree_.begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return __tree_.end(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return __tree_.end(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT {
+    return __tree_.begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator begin() const _NOEXCEPT {
+    return __tree_.begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator end() _NOEXCEPT {
+    return __tree_.end();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator end() const _NOEXCEPT {
+    return __tree_.end();
+  }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT { return reverse_iterator(end()); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rbegin() const _NOEXCEPT {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rbegin() _NOEXCEPT {
+    return reverse_iterator(end());
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator
+  rbegin() const _NOEXCEPT {
     return const_reverse_iterator(end());
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI reverse_iterator rend() _NOEXCEPT { return reverse_iterator(begin()); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const _NOEXCEPT {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rend() _NOEXCEPT {
+    return reverse_iterator(begin());
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator rend() const _NOEXCEPT {
     return const_reverse_iterator(begin());
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const _NOEXCEPT { return begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator cend() const _NOEXCEPT { return end(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crbegin() const _NOEXCEPT { return rbegin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const _NOEXCEPT { return rend(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cbegin() const _NOEXCEPT {
+    return begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cend() const _NOEXCEPT {
+    return end();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator
+  crbegin() const _NOEXCEPT {
+    return rbegin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator crend() const _NOEXCEPT {
+    return rend();
+  }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT { return __tree_.size() == 0; }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __tree_.size(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT { return __tree_.max_size(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool empty() const _NOEXCEPT {
+    return __tree_.size() == 0;
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type size() const _NOEXCEPT {
+    return __tree_.size();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type max_size() const _NOEXCEPT {
+    return __tree_.max_size();
+  }
 
   // modifiers:
 #  ifndef _LIBCPP_CXX03_LANG
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace(_Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace(_Args&&... __args) {
     return __tree_.__emplace_multi(std::forward<_Args>(__args)...);
   }
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace_hint(const_iterator __p, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace_hint(const_iterator __p, _Args&&... __args) {
     return __tree_.__emplace_hint_multi(__p, std::forward<_Args>(__args)...);
   }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const value_type& __v) { return __tree_.__emplace_multi(__v); }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, const value_type& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const value_type& __v) {
+    return __tree_.__emplace_multi(__v);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, const value_type& __v) {
     return __tree_.__emplace_hint_multi(__p, __v);
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __first, _InputIterator __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(_InputIterator __first, _InputIterator __last) {
     __tree_.__insert_range_multi(__first, __last);
   }
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert_range(_Range&& __range) {
     __tree_.__insert_range_multi(ranges::begin(__range), ranges::end(__range));
   }
 #  endif
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI iterator insert(value_type&& __v) { return __tree_.__emplace_multi(std::move(__v)); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(value_type&& __v) {
+    return __tree_.__emplace_multi(std::move(__v));
+  }
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, value_type&& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, value_type&& __v) {
     return __tree_.__emplace_hint_multi(__p, std::move(__v));
   }
 
-  _LIBCPP_HIDE_FROM_ABI void insert(initializer_list<value_type> __il) { insert(__il.begin(), __il.end()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(initializer_list<value_type> __il) {
+    insert(__il.begin(), __il.end());
+  }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
-  _LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_multi(__k); }
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
-  _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p) { return __tree_.erase(__p); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(const key_type& __k) {
+    return __tree_.__erase_multi(__k);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
+    return __tree_.erase(__f, __l);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
-  _LIBCPP_HIDE_FROM_ABI iterator insert(node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to multiset::insert()");
     return __tree_.template __node_handle_insert_multi<node_type>(std::move(__nh));
   }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __hint, node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __hint, node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to multiset::insert()");
     return __tree_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract<node_type>(__it);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI void swap(multiset& __s) _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(multiset& __s)
+      _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
     __tree_.swap(__s.__tree_);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const _NOEXCEPT { return __tree_.__alloc(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI key_compare key_comp() const { return __tree_.value_comp(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI value_compare value_comp() const { return __tree_.value_comp(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type get_allocator() const _NOEXCEPT {
+    return __tree_.__alloc();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_compare key_comp() const {
+    return __tree_.value_comp();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_compare value_comp() const {
+    return __tree_.value_comp();
+  }
 
   // set operations:
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __tree_.find(__k); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __tree_.find(__k); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const key_type& __k) {
+    return __tree_.find(__k);
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const key_type& __k) const {
+    return __tree_.find(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const _K2& __k) {
     return __tree_.find(__k);
   }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const _K2& __k) const {
     return __tree_.find(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const key_type& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const _K2& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  endif
 
 #  if _LIBCPP_STD_VER >= 20
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const key_type& __k) const {
+    return find(__k) != end();
+  }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
 #  endif // _LIBCPP_STD_VER >= 20
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const key_type& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const key_type& __k) {
     return __tree_.__lower_bound_multi(__k);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  lower_bound(const key_type& __k) const {
     return __tree_.__lower_bound_multi(__k);
   }
 
@@ -1432,23 +1494,25 @@ public:
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const _K2& __k) {
     return __tree_.__lower_bound_multi(__k);
   }
 
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  lower_bound(const _K2& __k) const {
     return __tree_.__lower_bound_multi(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const key_type& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const key_type& __k) {
     return __tree_.__upper_bound_multi(__k);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  upper_bound(const key_type& __k) const {
     return __tree_.__upper_bound_multi(__k);
   }
 
@@ -1456,30 +1520,35 @@ public:
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const _K2& __k) {
     return __tree_.__upper_bound_multi(__k);
   }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  upper_bound(const _K2& __k) const {
     return __tree_.__upper_bound_multi(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const key_type& __k) {
+  [[__nodiscard__]]
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const key_type& __k) {
     return __tree_.__equal_range_multi(__k);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const key_type& __k) const {
     return __tree_.__equal_range_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
+  [[__nodiscard__]]
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const _K2& __k) {
     return __tree_.__equal_range_multi(__k);
   }
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const _K2& __k) const {
     return __tree_.__equal_range_multi(__k);
   }
 #  endif
@@ -1543,7 +1612,7 @@ struct __specialized_algorithm<_Alg, __single_range<multiset<_Key, _Compare, _Al
 
   // set's begin() and end() are identical with and without const qualification
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI static auto operator()(const __set& __set, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static auto operator()(const __set& __set, _Args&&... __args) {
     return __specialized_algorithm<_Alg, __single_range<typename __set::__base>>()(
         __set.__tree_, std::forward<_Args>(__args)...);
   }
@@ -1551,7 +1620,7 @@ struct __specialized_algorithm<_Alg, __single_range<multiset<_Key, _Compare, _Al
 #  endif
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator==(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return __x.size() == __y.size() && std::equal(__x.begin(), __x.end(), __y.begin());
 }
@@ -1559,31 +1628,31 @@ operator==(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key,
 #  if _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare(__x.begin(), __x.end(), __y.begin(), __y.end());
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator!=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return !(__x == __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return __y < __x;
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return !(__x < __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return !(__y < __x);
 }
@@ -1591,7 +1660,7 @@ operator<=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key,
 #  else // _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI __synth_three_way_result<_Key>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __synth_three_way_result<_Key>
 operator<=>(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare_three_way(__x.begin(), __x.end(), __y.begin(), __y.end(), __synth_three_way);
 }
@@ -1599,7 +1668,7 @@ operator<=>(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key
 #  endif // _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI void
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
 swap(multiset<_Key, _Compare, _Allocator>& __x, multiset<_Key, _Compare, _Allocator>& __y)
     _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
   __x.swap(__y);
@@ -1607,7 +1676,7 @@ swap(multiset<_Key, _Compare, _Allocator>& __x, multiset<_Key, _Compare, _Alloca
 
 #  if _LIBCPP_STD_VER >= 20
 template <class _Key, class _Compare, class _Allocator, class _Predicate>
-inline _LIBCPP_HIDE_FROM_ABI typename multiset<_Key, _Compare, _Allocator>::size_type
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename multiset<_Key, _Compare, _Allocator>::size_type
 erase_if(multiset<_Key, _Compare, _Allocator>& __c, _Predicate __pred) {
   return std::__libcpp_erase_if_container(__c, __pred);
 }
diff --git a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
index 61a5f25eac608..1c9d7798b81bf 100644
--- a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// void clear() noexcept;
+// void clear() noexcept; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -42,5 +42,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/count.pass.cpp b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
index 6ad96b232083c..ad7b9be77c36a 100644
--- a/libcxx/test/std/containers/associative/multiset/count.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type count(const key_type& k) const;
+// size_type count(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -124,5 +124,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
index aea63384887d4..33eea24eb0476 100644
--- a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class multiset
 
 // template<typename K>
-//     size_type count(const K& x) const;        // C++14
+//     size_type count(const K& x) const;        // C++14, constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -29,11 +29,18 @@ struct Comp {
   bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<std::pair<int, int>, Comp> s{{2, 1}, {1, 1}, {1, 1}, {1, 1}, {2, 2}};
 
   auto cnt = s.count(1);
   assert(cnt == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
index 97cae1c565216..051522f9a55f2 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
@@ -13,7 +13,7 @@
 // class multiset
 
 // template <class... Args>
-//   iterator emplace(Args&&... args);
+//   iterator emplace(Args&&... args); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -23,7 +23,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
@@ -84,5 +84,12 @@ int main(int, char**) {
     assert(set.begin() == iter);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
index d8e9d1cd4d1c6..7c77dda795a71 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
@@ -13,7 +13,7 @@
 // class multiset
 
 // template <class... Args>
-//   iterator emplace_hint(const_iterator position, Args&&... args);
+//   iterator emplace_hint(const_iterator position, Args&&... args); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "DefaultOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
@@ -78,5 +78,12 @@ int main(int, char**) {
     assert(*r == 2);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
index 944f610a0c507..d730d69dc4e09 100644
--- a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// bool empty() const;
+// bool empty() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     M m;
@@ -40,5 +40,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
index e5c1d2b1eab98..03d469c9c5b0a 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-// pair<iterator,iterator>             equal_range(const key_type& k);
-// pair<const_iterator,const_iterator> equal_range(const key_type& k) const;
+// pair<iterator,iterator>             equal_range(const key_type& k); // constexpr since C++26
+// pair<const_iterator,const_iterator> equal_range(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -205,5 +205,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
index 6402488a44f22..6da1813338c3b 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
@@ -14,10 +14,10 @@
 
 // template<typename K>
 //     pair<iterator,iterator>             equal_range(const K& x);        //
-//     C++14
+//     C++14, constexpr since C++26
 // template<typename K>
 //     pair<const_iterator,const_iterator> equal_range(const K& x) const;  //
-//     C++14
+//     C++14, constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -33,7 +33,7 @@ struct Comp {
   bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<std::pair<int, int>, Comp> s{{2, 1}, {1, 1}, {1, 1}, {1, 1}, {2, 2}};
 
   auto er   = s.equal_range(1);
@@ -46,5 +46,12 @@ int main(int, char**) {
 
   assert(nels == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
index 31f00feda2977..3cc984c619490 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator erase(const_iterator position);
+// iterator erase(const_iterator position); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -25,7 +25,7 @@ struct TemplateConstructor {
 
 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -179,5 +179,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
index a3937d13bbb4b..6a6b2831fb515 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator erase(const_iterator first, const_iterator last);
+// iterator erase(const_iterator first, const_iterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -118,5 +118,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
index ba04a238e44b5..3503e3de4ffb7 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type erase(const key_type& k);
+// size_type erase(const key_type& k); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -104,5 +104,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
index 7f32fa82c46ab..f384b3ee95a67 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// node_type extract(const_iterator);
+// node_type extract(const_iterator); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -35,7 +35,7 @@ void test(Container& c) {
   assert(c.size() == 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     using set_type = std::multiset<int>;
     set_type m     = {1, 2, 3, 4, 5, 6};
@@ -55,5 +55,12 @@ int main(int, char**) {
     test(m);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
index ec75a6c5d835c..55896faa61ad3 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// node_type extract(key_type const&);
+// node_type extract(key_type const&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -41,7 +41,7 @@ void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> m = {1, 2, 3, 4, 5, 6};
     int keys[]           = {1, 2, 3, 4, 5, 6};
@@ -78,5 +78,12 @@ int main(int, char**) {
     test(m, std::begin(keys), std::end(keys));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/find.pass.cpp b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
index 866de0da5ea93..b7cb95a5d1a2b 100644
--- a/libcxx/test/std/containers/associative/multiset/find.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-//       iterator find(const key_type& k);
-// const_iterator find(const key_type& k) const;
+//       iterator find(const key_type& k); // constexpr since C++26
+// const_iterator find(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -202,5 +202,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
index 55bbd567d4360..f07d83e2fa18d 100644
--- a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// allocator_type get_allocator() const
+// allocator_type get_allocator() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::allocator<int> alloc;
     const std::multiset<int> s(alloc);
@@ -29,5 +29,12 @@ int main(int, char**) {
     const std::multiset<int, std::less<int>, other_allocator<int> > s(alloc);
     assert(s.get_allocator() == alloc);
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
index 31cb96ec33e76..3313ab39c0934 100644
--- a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
@@ -23,10 +23,17 @@ struct A {
   Set::const_iterator cit;
 };
 
-inline bool operator==(A const& L, A const& R) { return &L == &R; }
-inline bool operator<(A const& L, A const& R) { return L.data < R.data; }
-int main(int, char**) {
+inline TEST_CONSTEXPR_CXX26 bool operator==(A const& L, A const& R) { return &L == &R; }
+inline TEST_CONSTEXPR_CXX26 bool operator<(A const& L, A const& R) { return L.data < R.data; }
+TEST_CONSTEXPR_CXX26 bool test() {
   A a;
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
index 237cf9bb6fac2..22540133feb6d 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator insert(const value_type& v);
+// iterator insert(const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -48,7 +48,7 @@ void do_insert_cv_test() {
   assert(*r == 3);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   do_insert_cv_test<std::multiset<int> >();
 #if TEST_STD_VER >= 11
   {
@@ -57,5 +57,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
index 8085ac7cbb2f1..8f3d61009410a 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
@@ -20,9 +20,16 @@
 #include "container_test_types.h"
 #include "../../set_allocator_requirement_test_templates.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   testMultisetInsert<TCT::multiset<> >();
   testMultisetEmplace<TCT::multiset<> >();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
index 8f4f165252d36..7a25a2fdc96c1 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// void insert(initializer_list<value_type> il);
+// void insert(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> C;
     typedef C::value_type V;
@@ -57,5 +57,12 @@ int main(int, char**) {
     assert(*++i == V(10));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
index 97c03be6cc57b..e271b078ab317 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator insert(const_iterator position, const value_type& v);
+// iterator insert(const_iterator position, const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef M::iterator R;
@@ -70,5 +70,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
index 56483714cd50c..f4d4007b08d09 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
@@ -21,7 +21,7 @@
 #include "test_iterators.h"
 
 template <class Iter, class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   {   // Check that an empty range works correctly
     { // Without elements in the container
       using Map = std::multiset<int, std::less<int>, Alloc>;
@@ -184,13 +184,16 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   test_alloc<cpp17_input_iterator<int*>, std::allocator<int> >();
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
 }
 
 int main(int, char**) {
   test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
 
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
index d01f6b9b2c306..99bcba0ed97f8 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(const_iterator position, value_type&& v);
+// iterator insert(const_iterator position, value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> M;
     typedef M::iterator R;
@@ -71,5 +71,12 @@ int main(int, char**) {
     assert(*r == 3);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
index 6971d6b6e5eae..10f0d730a4444 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(node_type&&);
+// iterator insert(node_type&&); // constexpr since C++26
 
 #include <set>
 #include <type_traits>
@@ -63,11 +63,18 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<int> m;
   test(m);
   std::multiset<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
index b0392088beb6b..3224a73c9abbf 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(const_iterator hint, node_type&&);
+// iterator insert(const_iterator hint, node_type&&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -45,11 +45,18 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<int> m;
   test(m);
   std::multiset<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
index d57e14f4b59c8..e7ae071487b06 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -13,14 +13,14 @@
 // <set>
 
 // template<container-compatible-range<value_type> R>
-//   void insert_range(R&& rg); // C++23
+//   void insert_range(R&& rg); // C++23, constexpr since C++26
 
 #include <set>
 
 #include "../../insert_range_maps_sets.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
     test_map_set_insert_range<std::multiset<int, test_less<int>, Alloc>, int, Iter, Sent>(/*allow_duplicates=*/true);
   });
@@ -32,5 +32,12 @@ int main(int, char**) {
   test_set_insert_range_exception_safety_throwing_copy<std::multiset>();
   test_assoc_set_insert_range_exception_safety_throwing_allocator<std::multiset, int>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
index 88155572f1680..2e8de11823690 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(value_type&& v);
+// iterator insert(value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> M;
     typedef M::iterator R;
@@ -71,5 +71,12 @@ int main(int, char**) {
     assert(*r == 3);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
index fee8bf9d3eb30..9b510af6deefc 100644
--- a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
@@ -10,20 +10,20 @@
 
 // class multiset
 
-//       iterator begin();
-// const_iterator begin() const;
-//       iterator end();
-// const_iterator end()   const;
+//       iterator begin(); // constexpr since C++26
+// const_iterator begin() const; // constexpr since C++26
+//       iterator end(); // constexpr since C++26
+// const_iterator end()   const; // constexpr since C++26
 //
-//       reverse_iterator rbegin();
-// const_reverse_iterator rbegin() const;
-//       reverse_iterator rend();
-// const_reverse_iterator rend()   const;
+//       reverse_iterator rbegin(); // constexpr since C++26
+// const_reverse_iterator rbegin() const; // constexpr since C++26
+//       reverse_iterator rend(); // constexpr since C++26
+// const_reverse_iterator rend()   const; // constexpr since C++26
 //
-// const_iterator         cbegin()  const;
-// const_iterator         cend()    const;
-// const_reverse_iterator crbegin() const;
-// const_reverse_iterator crend()   const;
+// const_iterator         cbegin()  const; // constexpr since C++26
+// const_iterator         cend()    const; // constexpr since C++26
+// const_reverse_iterator crbegin() const; // constexpr since C++26
+// const_reverse_iterator crend()   const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -32,7 +32,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8};
@@ -137,5 +137,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
index ddd39d8fc0348..4f88cec396700 100644
--- a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-//       iterator lower_bound(const key_type& k);
-// const_iterator lower_bound(const key_type& k) const;
+//       iterator lower_bound(const key_type& k); // constexpr since C++26
+// const_iterator lower_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -165,5 +165,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
index a6cff58b090bd..5df2970223608 100644
--- a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type max_size() const;
+// size_type max_size() const; // constexpr since C++26
 
 #include <cassert>
 #include <limits>
@@ -20,7 +20,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef limited_allocator<int, 10> A;
     typedef std::multiset<int, std::less<int>, A> C;
@@ -44,5 +44,12 @@ int main(int, char**) {
     assert(c.max_size() <= alloc_max_size(c.get_allocator()));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
index 90eaeb2adbfcb..9962fa55bb199 100644
--- a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
@@ -13,13 +13,13 @@
 // class multiset
 
 // template <class C2>
-//   void merge(set<Key, C2, Allocator>& source);
+//   void merge(set<Key, C2, Allocator>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(set<Key, C2, Allocator>&& source);
+//   void merge(set<Key, C2, Allocator>&& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<Key, C2, Allocator>& source);
+//   void merge(multiset<Key, C2, Allocator>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<Key, C2, Allocator>&& source);
+//   void merge(multiset<Key, C2, Allocator>&& source); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -46,7 +46,7 @@ struct throw_comparator {
 };
 #endif
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> src{1, 3, 5};
     std::multiset<int> dst{2, 4, 5};
@@ -134,5 +134,12 @@ int main(int, char**) {
       first.merge(std::move(second));
     }
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
index 1f05201d706b1..d854ef97fce0e 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const allocator_type& a);
+// multiset(const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::less<int> C;
   typedef test_allocator<int> A;
   std::multiset<int, C, A> m(A(5));
@@ -26,5 +26,12 @@ int main(int, char**) {
   assert(m.begin() == m.end());
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
index 7234e61d28bfa..096202b3de35a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset& operator=(initializer_list<value_type> il);
+// multiset& operator=(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> C;
     typedef C::value_type V;
@@ -52,5 +52,12 @@ int main(int, char**) {
     assert(*++i == V(6));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
index b7da7f66b020d..07066563cc459 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// explicit multiset(const key_compare& comp);
+// explicit multiset(const key_compare& comp); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   const std::multiset<int, C> m(C(3));
   assert(m.empty());
@@ -26,5 +26,12 @@ int main(int, char**) {
   assert(m.key_comp() == C(3));
   assert(m.value_comp() == C(3));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
index be8a451fdc405..88e12d3d93f82 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const value_compare& comp, const allocator_type& a);
+// multiset(const value_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   typedef test_allocator<int> A;
   std::multiset<int, C, A> m(C(4), A(5));
@@ -28,5 +28,12 @@ int main(int, char**) {
   assert(m.key_comp() == C(4));
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
index c747828aa01cc..b9be5da01f053 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const multiset& m);
+// multiset(const multiset& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -129,8 +129,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
index 868072ef15288..d6bfd5ca78b22 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const multiset& m, const allocator_type& a);
+// multiset(const multiset& m, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -100,8 +100,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
index 0a5fe62eed69e..05c709e45af8d 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset& operator=(const multiset& s);
+// multiset& operator=(const multiset& s); // constexpr since C++26
 
 #include <algorithm>
 #include <cassert>
@@ -275,8 +275,15 @@ void test() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
index f9797ca571c47..e25c08e7a81e6 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
@@ -52,7 +52,7 @@ struct NotAnAllocator {
   friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     const int arr[] = {1, 2, 1, INT_MAX, 3};
     std::multiset s(std::begin(arr), std::end(arr));
@@ -212,5 +212,12 @@ int main(int, char**) {
 
   AssociativeContainerDeductionGuidesSfinaeAway<std::multiset, std::multiset<int>>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
index 4f7dabfea2c68..cd2e63d171e21 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset();
+// multiset(); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> m;
     assert(m.empty());
@@ -51,5 +51,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
index 052b032d6e798..58fcb5fb5d337 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
@@ -12,7 +12,7 @@
 //    noexcept(
 //        is_nothrow_default_constructible<allocator_type>::value &&
 //        is_nothrow_default_constructible<key_compare>::value &&
-//        is_nothrow_copy_constructible<key_compare>::value);
+//        is_nothrow_copy_constructible<key_compare>::value); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -32,7 +32,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::multiset<MoveOnly> C;
@@ -52,5 +52,12 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_default_constructible<C>::value, "");
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
index bf5d256e19a0f..cf0d87f879dde 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
@@ -8,7 +8,7 @@
 
 // <set>
 
-// ~multiset() // implied noexcept;
+// ~multiset() // implied noexcept; // constexpr since C++26
 
 // UNSUPPORTED: c++03
 
@@ -27,7 +27,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> C;
     static_assert(std::is_nothrow_destructible<C>::value, "");
@@ -47,5 +47,12 @@ int main(int, char**) {
   }
 #endif // _LIBCPP_VERSION
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
index 15275781a8aac..1b4af3e503bbe 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
@@ -9,11 +9,11 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 // template<container-compatible-range<value_type> R>
-//   multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
+//   multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23, constexpr since C++26
 //
 // template<container-compatible-range<value_type> R>
 //   multiset(from_range_t, R&& rg, const Allocator& a))
-//     : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+//     : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23, constexpr since C++26
 
 #include <algorithm>
 #include <array>
@@ -28,7 +28,7 @@ void test_duplicates() {
   assert(std::ranges::is_permutation(input, c));
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
     test_associative_set<std::multiset, int, Iter, Sent, test_less<int>, Alloc>();
   });
@@ -40,5 +40,12 @@ int main(int, char**) {
   test_set_exception_safety_throwing_copy<std::multiset>();
   test_set_exception_safety_throwing_allocator<std::multiset, int>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
index c860f17bb4196..5369116cb44b9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> C;
     typedef C::value_type V;
@@ -66,5 +66,12 @@ int main(int, char**) {
     assert(m.get_allocator() == a);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
index 5bb400cb82be9..cd2da63ac9c1d 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> Cmp;
   typedef std::multiset<int, Cmp> C;
   typedef C::value_type V;
@@ -37,5 +37,12 @@ int main(int, char**) {
   assert(*++i == V(6));
   assert(m.key_comp() == Cmp(10));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
index a12e68c1e582b..900d0a4546d1a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a);
+// multiset(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> Cmp;
   typedef test_allocator<int> A;
   typedef std::multiset<int, Cmp, A> C;
@@ -40,5 +40,12 @@ int main(int, char**) {
   assert(m.key_comp() == Cmp(10));
   assert(m.get_allocator() == A(4));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
index 6b98ceb1c0df7..192f2c4eacc65 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
@@ -11,7 +11,7 @@
 // class multiset
 
 // template <class InputIterator>
-//     multiset(InputIterator first, InputIterator last);
+//     multiset(InputIterator first, InputIterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_iterators.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -58,5 +58,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
index 50f8fda2a676b..f8db804e4c80a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // template <class InputIterator>
 //     multiset(InputIterator first, InputIterator last,
-//         const value_compare& comp, const allocator_type& a);
+//         const value_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -72,5 +72,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
index bed8d02f3e681..9e80b83b244d5 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
@@ -11,7 +11,7 @@
 // class multiset
 
 // template <class InputIterator>
-//     multiset(InputIterator first, InputIterator last, const value_compare& comp);
+//     multiset(InputIterator first, InputIterator last, const value_compare& comp); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -21,7 +21,7 @@
 #include "test_iterators.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
   typedef test_less<V> C;
@@ -40,5 +40,12 @@ int main(int, char**) {
   assert(*std::next(m.begin(), 7) == 3);
   assert(*std::next(m.begin(), 8) == 3);
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
index 2d71f21e182c9..70d9eb2307827 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(multiset&& s);
+// multiset(multiset&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef test_less<int> C;
@@ -94,5 +94,12 @@ int main(int, char**) {
     assert(std::distance(mo.begin(), mo.end()) == 0);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
index 771a7038bf633..8cd8b7727917a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(multiset&& s, const allocator_type& a);
+// multiset(multiset&& s, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -24,7 +24,7 @@
 #include "test_allocator.h"
 #include "Counter.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -111,5 +111,12 @@ int main(int, char**) {
     assert(Counter_base::gConstructed == 0);
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
index 5cdd4ec67b8e0..cb83f4386eff0 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset& operator=(multiset&& s);
+// multiset& operator=(multiset&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -93,5 +93,12 @@ int main(int, char**) {
     assert(m1.empty());
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
index 73b4be5005e1c..89e4f4ab1d2ae 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
@@ -10,7 +10,7 @@
 
 // multiset(multiset&&)
 //        noexcept(is_nothrow_move_constructible<allocator_type>::value &&
-//                 is_nothrow_move_constructible<key_compare>::value);
+//                 is_nothrow_move_constructible<key_compare>::value); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -30,7 +30,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::multiset<MoveOnly> C;
@@ -50,5 +50,12 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_move_constructible<C>::value, "");
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
index cfb304ee3be39..cf132ddde4717 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
@@ -11,7 +11,7 @@
 
 // template <class T, class Compare, class Allocator, class Predicate>
 //   typename multiset<T, Compare, Allocator>::size_type
-//   erase_if(multiset<T, Compare, Allocator>& c, Predicate pred);
+//   erase_if(multiset<T, Compare, Allocator>& c, Predicate pred); // constexpr since C++26
 
 #include <set>
 
@@ -64,7 +64,7 @@ void test() {
   test0(S({1, 2, 3}), False, S({1, 2, 3}), 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test<std::multiset<int>>();
   test<std::multiset<int, std::less<int>, min_allocator<int>>>();
   test<std::multiset<int, std::less<int>, test_allocator<int>>>();
@@ -72,5 +72,12 @@ int main(int, char**) {
   test<std::multiset<long>>();
   test<std::multiset<double>>();
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
index fb575c880dd2e..c146a3da3e272 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
@@ -13,15 +13,22 @@
 
 // template<class Key, class Compare, class Allocator>
 //   synth-three-way-result<Key> operator<=>(const multiset<Key, Compare, Allocator>& x,
-//                                           const multiset<Key, Compare, Allocator>& y);
+//                                           const multiset<Key, Compare, Allocator>& y); // constexpr since C++26
 
 #include <cassert>
 #include <set>
 
 #include "test_container_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   assert(test_ordered_set_container_spaceship<std::multiset>());
   // `std::multiset` is not constexpr, so no `static_assert` test here.
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
index 9ffb5dc3593c5..b78fe1d518d95 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
@@ -38,7 +38,7 @@
 
 #include "test_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> s1, s2;
     s1.insert(1);
@@ -70,5 +70,12 @@ int main(int, char**) {
     const std::multiset<int>&cs1 = s1, cs2 = s2;
     assert(testComparisons(cs1, cs2, false, true));
   }
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
index 63647a052c041..e4754ceea5e25 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
@@ -8,13 +8,13 @@
 
 // <set>
 
-// key_compare key_comp() const;
-// value_compare value_comp() const;
+// key_compare key_comp() const; // constexpr since C++26
+// value_compare value_comp() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::multiset<int> set_type;
 
   set_type s;
@@ -29,5 +29,12 @@ int main(int, char**) {
   assert(cs.value_comp()(*i1, *i2));
   assert(!cs.value_comp()(*i2, *i1));
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
index af32e1882093d..7ddeedde1a2ab 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// void swap(multiset& m);
+// void swap(multiset& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -110,5 +110,12 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
index 53630b3ca0a01..5e08875e08119 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// void swap(multiset& m);
+// void swap(multiset& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   {
     typedef std::multiset<int> M;
@@ -100,5 +100,12 @@ int main(int, char**) {
     assert(m2.get_allocator() == A(1));
   }
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
index 7a7ccf87e9de9..047cb03264317 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
@@ -12,11 +12,11 @@
 
 // void swap(multiset& c)
 //     noexcept(!allocator_type::propagate_on_container_swap::value ||
-//              __is_nothrow_swappable<allocator_type>::value);
+//              __is_nothrow_swappable<allocator_type>::value); // constexpr since C++26
 //
 //  In C++17, the standard says that swap shall have:
 //     noexcept(allocator_traits<Allocator>::is_always_equal::value &&
-//              noexcept(swap(declval<Compare&>(), declval<Compare&>())));
+//              noexcept(swap(declval<Compare&>(), declval<Compare&>()))); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -86,7 +86,7 @@ struct some_alloc3 {
   typedef std::false_type is_always_equal;
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> C;
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
@@ -131,5 +131,12 @@ int main(int, char**) {
 #  endif // _LIBCPP_VERSION
 #endif
 
+  return true;
+}
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/size.pass.cpp b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
index 405437211b8ca..626f50f57734a 100644
--- a/libcxx/test/std/containers/associative/multiset/size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type size() const;
+// size_type size() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     M m;
@@ -55,6 +55,12 @@ int main(int, char**) {
     assert(m.size() == 0);
   }
 #endif
+}
 
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp b/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp
index c61859b1feffb..4e580d4318a71 100644
--- a/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-//       iterator upper_bound(const key_type& k);
-// const_iterator upper_bound(const key_type& k) const;
+//       iterator upper_bound(const key_type& k); // constexpr since C++26
+// const_iterator upper_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -163,6 +163,13 @@ int main(int, char**) {
     assert(r == std::next(m.begin(), 9));
   }
 #endif
+  return true;
+}
 
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

>From fda9890ab7f494bcf82005dd3ad88faf8fb27a39 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sat, 27 Jun 2026 15:41:58 +0100
Subject: [PATCH 16/22] libcxx/test/std/containers/associative/multiset/

---
 .../multiset/count_transparent.pass.cpp       | 10 ++++--
 .../associative/multiset/emplace.pass.cpp     |  6 ++--
 .../multiset/emplace_hint.pass.cpp            |  6 ++--
 .../multiset/equal_range_transparent.pass.cpp | 10 ++++--
 .../associative/multiset/erase_iter.pass.cpp  |  4 +--
 .../multiset/extract_iterator.pass.cpp        |  4 +--
 .../associative/multiset/extract_key.pass.cpp |  4 +--
 .../associative/multiset/insert_cv.pass.cpp   |  2 +-
 ...rt_emplace_allocator_requirements.pass.cpp |  9 +-----
 .../multiset/insert_iter_iter.pass.cpp        |  3 +-
 .../multiset/insert_node_type.pass.cpp        | 10 +++---
 .../multiset/insert_node_type_hint.pass.cpp   |  9 +++---
 .../multiset/insert_range.pass.cpp            |  1 +
 .../associative/multiset/merge.pass.cpp       | 15 +++++----
 .../multiset/multiset.cons/copy.pass.cpp      |  8 ++---
 .../multiset.cons/copy_alloc.pass.cpp         |  8 ++---
 .../multiset.cons/copy_assign.pass.cpp        | 31 +++++++++----------
 .../multiset/multiset.cons/deduct.pass.cpp    |  2 +-
 .../multiset.cons/from_range.pass.cpp         |  3 +-
 .../multiset.cons/move_alloc.pass.cpp         |  2 +-
 .../associative/multiset/size.pass.cpp        |  1 +
 21 files changed, 75 insertions(+), 73 deletions(-)

diff --git a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
index 33eea24eb0476..c21da4a98fce3 100644
--- a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
@@ -19,14 +19,18 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
index 051522f9a55f2..06a566b293c5a 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
@@ -24,7 +24,7 @@
 #include "min_allocator.h"
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
     M m;
@@ -41,7 +41,9 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 2);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    assert(DefaultOnly::count == 0);
+  }
   {
     typedef std::multiset<Emplaceable> M;
     typedef M::iterator R;
diff --git a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
index 7c77dda795a71..ed686e8a10b66 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
@@ -24,7 +24,7 @@
 #include "min_allocator.h"
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
     M m;
@@ -41,7 +41,9 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 2);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    assert(DefaultOnly::count == 0);
+  }
   {
     typedef std::multiset<Emplaceable> M;
     typedef M::iterator R;
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
index 6da1813338c3b..6fd1df482f673 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
@@ -23,14 +23,18 @@
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
index 3cc984c619490..e2285e69ea07c 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
@@ -20,10 +20,10 @@
 
 struct TemplateConstructor {
   template <typename T>
-  TemplateConstructor(const T&) {}
+  TEST_CONSTEXPR_CXX26 TemplateConstructor(const T&) {}
 };
 
-bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
+TEST_CONSTEXPR_CXX26 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
 TEST_CONSTEXPR_CXX26 bool test() {
   {
diff --git a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
index f384b3ee95a67..7723e7ea7e113 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   std::size_t sz = c.size();
 
   for (auto first = c.cbegin(); first != c.cend();) {
@@ -42,7 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     test(m);
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::multiset<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     assert(Counter_base::gConstructed == 6);
     test(m);
diff --git a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
index 55896faa61ad3..c930cee804e1a 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container, class KeyTypeIter>
-void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
+TEST_CONSTEXPR_CXX26 void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   std::size_t sz = c.size();
   assert((std::size_t)std::distance(first, last) == sz);
 
@@ -61,7 +61,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(!res);
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::multiset<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     {
       Counter<int> keys[] = {1, 2, 3, 4, 5, 6};
diff --git a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
index 22540133feb6d..6d28aab5b0214 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 
 template <class Container>
-void do_insert_cv_test() {
+TEST_CONSTEXPR_CXX26 void do_insert_cv_test() {
   typedef Container M;
   typedef typename M::iterator R;
   typedef typename M::value_type VT;
diff --git a/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
index 8f3d61009410a..8085ac7cbb2f1 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_emplace_allocator_requirements.pass.cpp
@@ -20,16 +20,9 @@
 #include "container_test_types.h"
 #include "../../set_allocator_requirement_test_templates.h"
 
-TEST_CONSTEXPR_CXX26 bool test() {
+int main(int, char**) {
   testMultisetInsert<TCT::multiset<> >();
   testMultisetEmplace<TCT::multiset<> >();
 
-  return true;
-}
-int main(int, char**) {
-  test();
-#if TEST_STD_VER >= 26
-  static_assert(test());
-#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
index f4d4007b08d09..858ca520a304a 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
@@ -184,9 +184,10 @@ TEST_CONSTEXPR_CXX26 void test_alloc() {
   }
 }
 
-TEST_CONSTEXPR_CXX26 void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<cpp17_input_iterator<int*>, std::allocator<int> >();
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
+  return true;
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
index 10f0d730a4444..70889de77c1e5 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
@@ -20,18 +20,18 @@
 #include "min_allocator.h"
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto it = c.insert(key);
   return c.extract(it);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
+  Container c2;
 
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c, i);
     assert(!node.empty());
     typename Container::iterator it = c.insert(std::move(node));
     assert(node.empty());
@@ -50,7 +50,7 @@ void test(Container& c) {
   }
 
   { // Insert duplicate node.
-    typename Container::node_type dupl = nf(0);
+    typename Container::node_type dupl = nf(c2, 0);
     auto it                            = c.insert(std::move(dupl));
     assert(*it == 0);
   }
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
index 3224a73c9abbf..3610c96ae788a 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
@@ -19,18 +19,19 @@
 #include "min_allocator.h"
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto it = c.insert(key);
   return c.extract(it);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
 
+  Container c2;
+
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c2, i);
     assert(!node.empty());
     std::size_t prev = c.size();
     auto it          = c.insert(c.end(), std::move(node));
diff --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
index e7ae071487b06..f89c783d5f568 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -9,6 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
 // ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-missing-field-initializers
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2147483647
 
 // <set>
 
diff --git a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
index 9962fa55bb199..5ddfa8b4e0b29 100644
--- a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
@@ -27,7 +27,7 @@
 #include "Counter.h"
 
 template <class Set>
-bool set_equal(const Set& set, Set other) {
+TEST_CONSTEXPR_CXX26 bool set_equal(const Set& set, Set other) {
   return set == other;
 }
 
@@ -35,10 +35,10 @@ bool set_equal(const Set& set, Set other) {
 struct throw_comparator {
   bool& should_throw_;
 
-  throw_comparator(bool& should_throw) : should_throw_(should_throw) {}
+  TEST_CONSTEXPR_CXX26 throw_comparator(bool& should_throw) : should_throw_(should_throw) {}
 
   template <class T>
-  bool operator()(const T& lhs, const T& rhs) const {
+  TEST_CONSTEXPR_CXX26 bool operator()(const T& lhs, const T& rhs) const {
     if (should_throw_)
       throw 0;
     return lhs < rhs;
@@ -56,7 +56,8 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
-  {
+
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     bool do_throw = false;
     typedef std::multiset<Counter<int>, throw_comparator> set_type;
     set_type src({1, 3, 5}, throw_comparator(do_throw));
@@ -75,13 +76,15 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(set_equal(dst, set_type({2, 4, 5}, throw_comparator(do_throw))));
   }
 #endif
-  assert(Counter_base::gConstructed == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    assert(Counter_base::gConstructed == 0);
+  }
   struct comparator {
     comparator() = default;
 
     bool operator()(const Counter<int>& lhs, const Counter<int>& rhs) const { return lhs < rhs; }
   };
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::multiset<Counter<int>, std::less<Counter<int>>> first_set_type;
     typedef std::multiset<Counter<int>, comparator> second_set_type;
     typedef std::set<Counter<int>, comparator> third_set_type;
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
index b9be5da01f053..ff0951bdbe06f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 
 template <template <class> class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   { // Simple check
     using Set = std::multiset<int, std::less<int>, Alloc<int> >;
 
@@ -81,7 +81,7 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator>();
   test_alloc<min_allocator>(); // Make sure that fancy pointers work
 
@@ -127,10 +127,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.get_allocator() == other_allocator<int>(10));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
index d6bfd5ca78b22..26881348de9d1 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 
 template <class Alloc>
-void test_alloc(const Alloc& new_alloc) {
+TEST_CONSTEXPR_CXX26 void test_alloc(const Alloc& new_alloc) {
   { // Simple check
     using Set = std::multiset<int, std::less<int>, Alloc>;
 
@@ -82,7 +82,7 @@ void test_alloc(const Alloc& new_alloc) {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc(std::allocator<int>());
   test_alloc(test_allocator<int>(25)); // Make sure that the new allocator is actually used
   test_alloc(min_allocator<int>());    // Make sure that fancy pointers work
@@ -98,10 +98,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
index 05c709e45af8d..c21e8a37b9ae1 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
@@ -33,41 +33,42 @@ class tracking_allocator {
   using value_type                             = T;
   using propagate_on_container_copy_assignment = std::true_type;
 
-  tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
 
   template <class U>
-  tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
 
-  T* allocate(std::size_t n) {
+  TEST_CONSTEXPR_CXX26 T* allocate(std::size_t n) {
     T* allocation = std::allocator<T>().allocate(n);
     allocs_->push_back(allocation);
     return allocation;
   }
 
-  void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
+  TEST_CONSTEXPR_CXX26 void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
     auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
     assert(res != allocs_->end() && "Trying to deallocate memory from different allocator?");
     allocs_->erase(res);
     std::allocator<T>().deallocate(ptr, n);
   }
 
-  friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  friend TEST_CONSTEXPR_CXX26 bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ == rhs.allocs_;
   }
 
-  friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  friend TEST_CONSTEXPR_CXX26 bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ != rhs.allocs_;
   }
 };
 
 struct NoOp {
-  void operator()() {}
+  TEST_CONSTEXPR_CXX26 void operator()() {}
 };
 
 template <class Alloc, class AllocatorInvariant = NoOp>
-void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
-                const Alloc& rhs_alloc                   = Alloc(),
-                AllocatorInvariant check_alloc_invariant = NoOp()) {
+TEST_CONSTEXPR_CXX26 void
+test_alloc(const Alloc& lhs_alloc                   = Alloc(),
+           const Alloc& rhs_alloc                   = Alloc(),
+           AllocatorInvariant check_alloc_invariant = NoOp()) {
   {   // Test empty/non-empty multiset combinations
     { // assign from a non-empty container into an empty one
       using Set = std::multiset<int, std::less<int>, Alloc>;
@@ -234,7 +235,7 @@ void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator<int> >();
 #if TEST_STD_VER >= 11
   test_alloc<min_allocator<int> >();
@@ -246,10 +247,10 @@ void test() {
       std::vector<void*>* rhs_allocs_;
 
     public:
-      AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
+      TEST_CONSTEXPR_CXX26 AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
           : lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
 
-      void operator()() {
+      TEST_CONSTEXPR_CXX26 void operator()() {
         assert(lhs_allocs_->empty());
         assert(rhs_allocs_->empty());
       }
@@ -273,10 +274,6 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
-}
-
-TEST_CONSTEXPR_CXX26 bool test() {
-  test();
 
   return true;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
index e25c08e7a81e6..e3fead570e958 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
@@ -49,7 +49,7 @@
 #include "test_allocator.h"
 
 struct NotAnAllocator {
-  friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
+  friend TEST_CONSTEXPR_CXX26 bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
 TEST_CONSTEXPR_CXX26 bool test() {
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
index 1b4af3e503bbe..338769516782a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2000000
 
 // template<container-compatible-range<value_type> R>
 //   multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23, constexpr since C++26
@@ -22,7 +23,7 @@
 #include "../../from_range_associative_containers.h"
 #include "test_macros.h"
 
-void test_duplicates() {
+TEST_CONSTEXPR_CXX26 void test_duplicates() {
   std::array input = {1, 2, 3, 3, 3, 4, 2, 1, 2};
   auto c           = std::multiset<int>(std::from_range, input);
   assert(std::ranges::is_permutation(input, c));
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
index 8cd8b7727917a..eece56da5aff4 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
@@ -73,7 +73,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(m3.key_comp() == C(5));
     LIBCPP_ASSERT(m1.empty());
   }
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef Counter<int> V;
     typedef std::less<V> C;
     typedef test_allocator<V> A;
diff --git a/libcxx/test/std/containers/associative/multiset/size.pass.cpp b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
index 626f50f57734a..4fe326b013fd4 100644
--- a/libcxx/test/std/containers/associative/multiset/size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
@@ -55,6 +55,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(m.size() == 0);
   }
 #endif
+  return true;
 }
 
 int main(int, char**) {

>From dd8615210cc23bb45756b3ec50848c5b074e37dc Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Mon, 29 Jun 2026 23:12:14 +0100
Subject: [PATCH 17/22] fix pending tests

---
 .../alg.foreach/for_each.associative.pass.cpp         |  8 ++------
 .../alg.foreach/ranges.for_each.associative.pass.cpp  | 11 ++---------
 .../multiset.nonmember/compare.three_way.pass.cpp     |  1 -
 3 files changed, 4 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
index 645e1e4af792e..7b0e5b93dc618 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
@@ -71,12 +71,8 @@ TEST_CONSTEXPR_CXX26 void test_node_container(Converter conv) {
 }
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  // FIXME: remove when set is made constexpr
-  if (!TEST_IS_CONSTANT_EVALUATED)
-    test_node_container<std::set<int> >([](int i) { return i; });
-  // FIXME: remove when multiset is made constexpr
-  if (!TEST_IS_CONSTANT_EVALUATED)
-    test_node_container<std::multiset<int> >([](int i) { return i; });
+  test_node_container<std::set<int> >([](int i) { return i; });
+  test_node_container<std::multiset<int> >([](int i) { return i; });
   test_node_container<std::map<int, int> >([](int i) { return std::make_pair(i, i); });
   test_node_container<std::multimap<int, int> >([](int i) { return std::make_pair(i, i); });
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
index 02698819d71df..72aa40a7f7e0a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
@@ -252,20 +252,13 @@ TEST_CONSTEXPR_CXX26 void test_invoke_map_like() {
 }
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when multiset is made constexpr
-    test_node_container<std::multiset<int> >([](int i) { return i; });
-  }
   test_node_container<std::set<int> >([](int i) { return i; });
+  test_node_container<std::multiset<int> >([](int i) { return i; });
   test_node_container<std::map<int, int> >([](int i) { return std::make_pair(i, i); });
   test_node_container<std::multimap<int, int> >([](int i) { return std::make_pair(i, i); });
 
-  if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when multiset is made constexpr
-    test_invoke_set_like<std::multiset>();
-  }
-
   test_invoke_set_like<std::set>();
+  test_invoke_set_like<std::multiset>();
   test_invoke_map_like<std::map>();
   test_invoke_map_like<std::multimap>();
 
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
index c146a3da3e272..9eaedb730745f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
@@ -22,7 +22,6 @@
 
 TEST_CONSTEXPR_CXX26 bool test() {
   assert(test_ordered_set_container_spaceship<std::multiset>());
-  // `std::multiset` is not constexpr, so no `static_assert` test here.
   return true;
 }
 int main(int, char**) {

>From 83f1ad1ea88dee3d78d35a3aa443c0712f037812 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Wed, 1 Jul 2026 21:23:50 +0100
Subject: [PATCH 18/22] missing include

---
 .../associative/multiset/multiset.observers/comp.pass.cpp       | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
index e4754ceea5e25..1bafc70e87af5 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
@@ -14,6 +14,8 @@
 #include <set>
 #include <cassert>
 
+#include "test_macros.h"
+
 TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::multiset<int> set_type;
 

>From 4c16e99618539bf2a1471d3e8a23a861a91f6ec6 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Wed, 1 Jul 2026 22:13:05 +0100
Subject: [PATCH 19/22] add \n

---
 libcxx/test/std/containers/associative/multiset/clear.pass.cpp   | 1 +
 libcxx/test/std/containers/associative/multiset/count.pass.cpp   | 1 +
 .../containers/associative/multiset/count_transparent.pass.cpp   | 1 +
 libcxx/test/std/containers/associative/multiset/emplace.pass.cpp | 1 +
 .../std/containers/associative/multiset/emplace_hint.pass.cpp    | 1 +
 libcxx/test/std/containers/associative/multiset/empty.pass.cpp   | 1 +
 .../std/containers/associative/multiset/equal_range.pass.cpp     | 1 +
 .../associative/multiset/equal_range_transparent.pass.cpp        | 1 +
 .../test/std/containers/associative/multiset/erase_iter.pass.cpp | 1 +
 .../std/containers/associative/multiset/erase_iter_iter.pass.cpp | 1 +
 .../test/std/containers/associative/multiset/erase_key.pass.cpp  | 1 +
 .../containers/associative/multiset/extract_iterator.pass.cpp    | 1 +
 .../std/containers/associative/multiset/extract_key.pass.cpp     | 1 +
 libcxx/test/std/containers/associative/multiset/find.pass.cpp    | 1 +
 .../std/containers/associative/multiset/get_allocator.pass.cpp   | 1 +
 .../std/containers/associative/multiset/incomplete_type.pass.cpp | 1 +
 .../test/std/containers/associative/multiset/insert_cv.pass.cpp  | 1 +
 .../associative/multiset/insert_initializer_list.pass.cpp        | 1 +
 .../std/containers/associative/multiset/insert_iter_cv.pass.cpp  | 1 +
 .../std/containers/associative/multiset/insert_iter_rv.pass.cpp  | 1 +
 .../containers/associative/multiset/insert_node_type.pass.cpp    | 1 +
 .../associative/multiset/insert_node_type_hint.pass.cpp          | 1 +
 .../std/containers/associative/multiset/insert_range.pass.cpp    | 1 +
 .../test/std/containers/associative/multiset/insert_rv.pass.cpp  | 1 +
 .../test/std/containers/associative/multiset/iterator.pass.cpp   | 1 +
 .../std/containers/associative/multiset/lower_bound.pass.cpp     | 1 +
 .../test/std/containers/associative/multiset/max_size.pass.cpp   | 1 +
 libcxx/test/std/containers/associative/multiset/merge.pass.cpp   | 1 +
 .../containers/associative/multiset/multiset.cons/alloc.pass.cpp | 1 +
 .../multiset/multiset.cons/assign_initializer_list.pass.cpp      | 1 +
 .../associative/multiset/multiset.cons/compare.pass.cpp          | 1 +
 .../associative/multiset/multiset.cons/compare_alloc.pass.cpp    | 1 +
 .../containers/associative/multiset/multiset.cons/copy.pass.cpp  | 1 +
 .../associative/multiset/multiset.cons/copy_alloc.pass.cpp       | 1 +
 .../associative/multiset/multiset.cons/copy_assign.pass.cpp      | 1 +
 .../associative/multiset/multiset.cons/deduct.pass.cpp           | 1 +
 .../associative/multiset/multiset.cons/default.pass.cpp          | 1 +
 .../associative/multiset/multiset.cons/default_noexcept.pass.cpp | 1 +
 .../associative/multiset/multiset.cons/dtor_noexcept.pass.cpp    | 1 +
 .../associative/multiset/multiset.cons/from_range.pass.cpp       | 1 +
 .../associative/multiset/multiset.cons/initializer_list.pass.cpp | 1 +
 .../multiset/multiset.cons/initializer_list_compare.pass.cpp     | 1 +
 .../multiset.cons/initializer_list_compare_alloc.pass.cpp        | 1 +
 .../associative/multiset/multiset.cons/iter_iter.pass.cpp        | 1 +
 .../associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp  | 1 +
 .../associative/multiset/multiset.cons/iter_iter_comp.pass.cpp   | 1 +
 .../containers/associative/multiset/multiset.cons/move.pass.cpp  | 1 +
 .../associative/multiset/multiset.cons/move_alloc.pass.cpp       | 1 +
 .../associative/multiset/multiset.cons/move_assign.pass.cpp      | 1 +
 .../associative/multiset/multiset.cons/move_noexcept.pass.cpp    | 1 +
 .../associative/multiset/multiset.erasure/erase_if.pass.cpp      | 1 +
 .../multiset/multiset.nonmember/compare.three_way.pass.cpp       | 1 +
 .../associative/multiset/multiset.nonmember/op_compare.pass.cpp  | 1 +
 .../associative/multiset/multiset.observers/comp.pass.cpp        | 1 +
 .../associative/multiset/multiset.special/member_swap.pass.cpp   | 1 +
 .../multiset/multiset.special/non_member_swap.pass.cpp           | 1 +
 .../associative/multiset/multiset.special/swap_noexcept.pass.cpp | 1 +
 57 files changed, 57 insertions(+)

diff --git a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
index 1c9d7798b81bf..b8110efe4a4ff 100644
--- a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
@@ -44,6 +44,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/count.pass.cpp b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
index ad7b9be77c36a..6bfcdc66aada9 100644
--- a/libcxx/test/std/containers/associative/multiset/count.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
@@ -126,6 +126,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
index c21da4a98fce3..815260f67c231 100644
--- a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
@@ -41,6 +41,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
index 06a566b293c5a..193926b6dfc87 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
@@ -88,6 +88,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
index ed686e8a10b66..5650b27d19a05 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
@@ -82,6 +82,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
index d730d69dc4e09..08027484fadb6 100644
--- a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
@@ -42,6 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
index 03d469c9c5b0a..7d623036c8e34 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
@@ -207,6 +207,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
index 6fd1df482f673..385b05c8f26b1 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
@@ -52,6 +52,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
index e2285e69ea07c..a7da30fcd2a96 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
@@ -181,6 +181,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
index 6a6b2831fb515..942b076c702ea 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
@@ -120,6 +120,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
index 3503e3de4ffb7..c6fe8f0563370 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
@@ -106,6 +106,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
index 7723e7ea7e113..c07a0d246fcf7 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
@@ -57,6 +57,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
index c930cee804e1a..52adc8ade796c 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
@@ -80,6 +80,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/find.pass.cpp b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
index b7cb95a5d1a2b..bb5be9a28502b 100644
--- a/libcxx/test/std/containers/associative/multiset/find.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
@@ -204,6 +204,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
index f07d83e2fa18d..3def31b6e0c84 100644
--- a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
@@ -31,6 +31,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
index 3313ab39c0934..63fd68a747c32 100644
--- a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
@@ -30,6 +30,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
index 6d28aab5b0214..556f29496dda2 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
@@ -59,6 +59,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
index 7a25a2fdc96c1..b561b5864c771 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
@@ -59,6 +59,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
index e271b078ab317..eb63f2f838891 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
@@ -72,6 +72,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
index 99bcba0ed97f8..874e560f6a533 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
@@ -73,6 +73,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
index 70889de77c1e5..4a20c2792c76e 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
@@ -71,6 +71,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
index 3610c96ae788a..b5c2aae45f01f 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
@@ -54,6 +54,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
index f89c783d5f568..590af11578f22 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -35,6 +35,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
index 2e8de11823690..387a99c042f7d 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
@@ -73,6 +73,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
index 9b510af6deefc..542ba871960ee 100644
--- a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
@@ -139,6 +139,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
index 4f88cec396700..c2883e039c8a9 100644
--- a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
@@ -167,6 +167,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
index 5df2970223608..b7b6d28d002ac 100644
--- a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
@@ -46,6 +46,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
index 5ddfa8b4e0b29..0397f16c52d41 100644
--- a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
@@ -139,6 +139,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
index d854ef97fce0e..fafcf50fa15ad 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
@@ -28,6 +28,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
index 096202b3de35a..914c07ed9d6f7 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
@@ -54,6 +54,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
index 07066563cc459..635b5999dbcd7 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
@@ -28,6 +28,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
index 88e12d3d93f82..e8138c0501263 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
@@ -30,6 +30,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
index ff0951bdbe06f..7d56ea93a9a45 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
@@ -130,6 +130,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
index 26881348de9d1..f79ae3713540b 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
@@ -101,6 +101,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
index c21e8a37b9ae1..86b86f92f3338 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
@@ -277,6 +277,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
index e3fead570e958..7a2cc45ae9cdc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
@@ -214,6 +214,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
index cd2e63d171e21..256b6f41dc3db 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
@@ -53,6 +53,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
index 58fcb5fb5d337..532a98066e51a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
@@ -54,6 +54,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
index cf0d87f879dde..2f81f954c8757 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
@@ -49,6 +49,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
index 338769516782a..ca1d7c20869b9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
@@ -43,6 +43,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
index 5369116cb44b9..bde073eeee9d7 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
@@ -68,6 +68,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
index cd2da63ac9c1d..f144c05352f09 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
@@ -39,6 +39,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
index 900d0a4546d1a..63d8199ea434e 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
@@ -42,6 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
index 192f2c4eacc65..bb41b35870444 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
@@ -60,6 +60,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
index f8db804e4c80a..22ac9bd799e45 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
@@ -74,6 +74,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
index 9e80b83b244d5..353f3bc84ad9f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
@@ -42,6 +42,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
index 70d9eb2307827..a85823e464cbc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
@@ -96,6 +96,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
index eece56da5aff4..a3eaa419fbbab 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
@@ -113,6 +113,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
index cb83f4386eff0..60dc3a5bbc62f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
@@ -95,6 +95,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
index 89e4f4ab1d2ae..52519aaceab3f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
@@ -52,6 +52,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
index cf132ddde4717..147211766e693 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
@@ -74,6 +74,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
index 9eaedb730745f..ae3581104677c 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
@@ -24,6 +24,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
   assert(test_ordered_set_container_spaceship<std::multiset>());
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
index b78fe1d518d95..1d5df25bebfdc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
@@ -72,6 +72,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
   }
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
index 1bafc70e87af5..1908991e40eed 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
@@ -33,6 +33,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
index 7ddeedde1a2ab..997fe1d818b53 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
@@ -112,6 +112,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
index 5e08875e08119..3ab2e1fbf55ce 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
@@ -102,6 +102,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
index 047cb03264317..37bdae66ee8e9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
@@ -133,6 +133,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
 
   return true;
 }
+
 int main(int, char**) {
   test();
 #if TEST_STD_VER >= 26

>From ee60174486b206f0c625d988f4753b0532d270ce Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Wed, 1 Jul 2026 23:23:37 +0100
Subject: [PATCH 20/22] macro

---
 .../associative/multiset/multiset.erasure/erase_if.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
index 147211766e693..e0df9210c7da9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
@@ -20,14 +20,14 @@
 #include "min_allocator.h"
 
 template <class S, class Pred>
-void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
+TEST_CONSTEXPR_CXX26 void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
   ASSERT_SAME_TYPE(typename S::size_type, decltype(std::erase_if(s, p)));
   assert(expected_erased_count == std::erase_if(s, p));
   assert(s == expected);
 }
 
 template <typename S>
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   auto is1   = [](auto v) { return v == 1; };
   auto is2   = [](auto v) { return v == 2; };
   auto is3   = [](auto v) { return v == 3; };

>From dbe235f80d8d00bf17e01955df3dd42ee9b83566 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Thu, 2 Jul 2026 08:35:35 +0100
Subject: [PATCH 21/22] trigger build


>From c5cc01c1d8235f866af6c3c5b959563057d10e4f Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <vinay_deshmukh at outlook.com>
Date: Sun, 5 Jul 2026 20:26:19 +0100
Subject: [PATCH 22/22] fix constexpr steps

---
 .../std/containers/associative/multiset/insert_range.pass.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
index 590af11578f22..55edbfd8af079 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
 // ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-missing-field-initializers
-// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2147483647
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=4000000
 
 // <set>
 


From libcxx-commits at lists.llvm.org  Sun Jul  5 12:27:09 2026
From: libcxx-commits at lists.llvm.org (Vinay Deshmukh via libcxx-commits)
Date: Sun, 05 Jul 2026 12:27:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make `<set>` `std::multiset`
 constexpr as part of P3372R3 (PR #206257)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206257@github.com>
Message-ID: <6a4ab00d.13be6df8.3d8759.e9ae@mx.google.com>


================
@@ -9,18 +9,19 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
 // ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-missing-field-initializers
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2147483647
----------------
vinay-deshmukh wrote:

fixed with c5cc01c

https://github.com/llvm/llvm-project/pull/206257

From libcxx-commits at lists.llvm.org  Sun Jul  5 19:25:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 19:25:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4b122b.7d41f3a1.d1cf7.d3a2@mx.google.com>


================
@@ -164,6 +166,8 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
 
 template <class _Rep, class _Period>
 class duration {
+  static_assert(!is_const<_Rep>::value, "A duration representation can not be const-qualified");
----------------
emmett2020 wrote:

Thanks for the reviewing. I think `__is_unqualified_v<T>` has the same functional effect with `!is_const<T> && !is_volatile<T>`. However, we can give users a more concrete and specific error message with separated check. There're also  same usage at https://github.com/llvm/llvm-project/blob/9b2d44a51252891b4612330c485fa11464866455/libcxx/include/__memory/allocator.h#L68. 

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 19:27:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 19:27:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4b1299.7270d2fd.23dbb3.9951@mx.google.com>


https://github.com/emmett2020 edited https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 19:28:35 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 19:28:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4b12d3.cb30c3e4.6f38b.b6fe@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/207558

>From f20f20873cde0b8800e0cc5d690f3ee7cb6a4b27 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Sun, 5 Jul 2026 13:38:58 +0800
Subject: [PATCH] [libc++][chrono] Disallow cv-qualified representations in
 duration

Implement LWG 4481 by rejecting chrono::duration specializations whose
Rep type is const- or volatile-qualified.

Fixes #189846
---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__chrono/duration.h            |  4 +++
 libcxx/include/chrono                         |  2 ++
 .../time/time.duration/duration.verify.cpp    | 27 +++++++++++++++++++
 4 files changed, 34 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/time/time.duration/duration.verify.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 23adfde5bd4d7..5de35c96d4c0a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -301,7 +301,7 @@
 "`LWG4477 <https://wg21.link/LWG4477>`__","Placement ``operator delete`` should be constexpr","2026-03 (Croydon)","|Complete|","23","`#189843 <https://github.com/llvm/llvm-project/issues/189843>`__",""
 "`LWG4478 <https://wg21.link/LWG4478>`__","``meta::has_identifier`` is not specified for annotations","2026-03 (Croydon)","","","`#189844 <https://github.com/llvm/llvm-project/issues/189844>`__",""
 "`LWG4480 <https://wg21.link/LWG4480>`__","``<stdatomic.h>`` should provide ``ATOMIC_CHAR8_T_LOCK_FREE``","2026-03 (Croydon)","","","`#189845 <https://github.com/llvm/llvm-project/issues/189845>`__",""
-"`LWG4481 <https://wg21.link/LWG4481>`__","Disallow ``chrono::duration<const T, P>``","2026-03 (Croydon)","","","`#189846 <https://github.com/llvm/llvm-project/issues/189846>`__",""
+"`LWG4481 <https://wg21.link/LWG4481>`__","Disallow ``chrono::duration<const T, P>``","2026-03 (Croydon)","|Complete|","23","`#189846 <https://github.com/llvm/llvm-project/issues/189846>`__",""
 "`LWG4483 <https://wg21.link/LWG4483>`__","Multidimensional arrays are not supported by ``meta::reflect_constant_array`` and related functions","2026-03 (Croydon)","","","`#189847 <https://github.com/llvm/llvm-project/issues/189847>`__",""
 "`LWG4485 <https://wg21.link/LWG4485>`__","Move specification for ``task::stop_token_type``","2026-03 (Croydon)","","","`#189848 <https://github.com/llvm/llvm-project/issues/189848>`__",""
 "`LWG4486 <https://wg21.link/LWG4486>`__","``integral-constant-like`` and ``constexpr-wrapper-like`` exposition-only concept duplication","2026-03 (Croydon)","","","`#189849 <https://github.com/llvm/llvm-project/issues/189849>`__",""
diff --git a/libcxx/include/__chrono/duration.h b/libcxx/include/__chrono/duration.h
index 92efd82f72145..ebff8fd5263fe 100644
--- a/libcxx/include/__chrono/duration.h
+++ b/libcxx/include/__chrono/duration.h
@@ -17,8 +17,10 @@
 #include <__functional/hash.h>
 #include <__type_traits/common_type.h>
 #include <__type_traits/enable_if.h>
+#include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>
 #include <__type_traits/is_floating_point.h>
+#include <__type_traits/is_volatile.h>
 #include <limits>
 #include <ratio>
 
@@ -164,6 +166,8 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
 
 template <class _Rep, class _Period>
 class duration {
+  static_assert(!is_const<_Rep>::value, "A duration representation can not be const-qualified");
+  static_assert(!is_volatile<_Rep>::value, "A duration representation can not be volatile-qualified");
   static_assert(!__is_duration_v<_Rep>, "A duration representation can not be a duration");
   static_assert(__is_ratio_v<_Period>, "Second template parameter of duration must be a std::ratio");
   static_assert(_Period::num > 0, "duration period must be positive");
diff --git a/libcxx/include/chrono b/libcxx/include/chrono
index 39822d5e74f79..2f844b8b9a870 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -46,6 +46,8 @@ public:
 template <class Rep, class Period = ratio<1>>
 class duration
 {
+    static_assert(!is_const<Rep>::value, "A duration representation can not be const-qualified");
+    static_assert(!is_volatile<Rep>::value, "A duration representation can not be volatile-qualified");
     static_assert(!__is_duration<Rep>::value, "A duration representation can not be a duration");
     static_assert(__is_ratio<Period>::value, "Second template parameter of duration must be a std::ratio");
     static_assert(Period::num > 0, "duration period must be positive");
diff --git a/libcxx/test/std/time/time.duration/duration.verify.cpp b/libcxx/test/std/time/time.duration/duration.verify.cpp
new file mode 100644
index 0000000000000..3a740e3996ac4
--- /dev/null
+++ b/libcxx/test/std/time/time.duration/duration.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <chrono>
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+// LWG 4481
+// A program that instantiates duration with a cv-qualified Rep is ill-formed.
+
+#include <chrono>
+
+void test() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation can not be const-qualified}}
+  (void)sizeof(std::chrono::duration<const int>);
+
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation can not be volatile-qualified}}
+  (void)sizeof(std::chrono::duration<volatile int>);
+
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation can not be const-qualified}}
+  (void)sizeof(std::chrono::duration<const volatile int>);
+}


From libcxx-commits at lists.llvm.org  Sun Jul  5 19:32:52 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 19:32:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 224b9d2 - [libc++] Make `<set>`
 `std::multiset` constexpr as part of P3372R3 (#206257)
Message-ID: <6a4b13d4.da8bafd3.2e1688.8188@mx.google.com>


Author: Vinay Deshmukh
Date: 2026-07-06T10:32:47+08:00
New Revision: 224b9d2ac05d808aa4b08c27ba37149260310e4b

URL: https://github.com/llvm/llvm-project/commit/224b9d2ac05d808aa4b08c27ba37149260310e4b
DIFF: https://github.com/llvm/llvm-project/commit/224b9d2ac05d808aa4b08c27ba37149260310e4b.diff

LOG: [libc++] Make `<set>` `std::multiset` constexpr as part of P3372R3 (#206257)

Added: 
    

Modified: 
    libcxx/include/set
    libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
    libcxx/test/std/containers/associative/multiset/clear.pass.cpp
    libcxx/test/std/containers/associative/multiset/count.pass.cpp
    libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
    libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
    libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
    libcxx/test/std/containers/associative/multiset/empty.pass.cpp
    libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
    libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
    libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
    libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
    libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
    libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
    libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
    libcxx/test/std/containers/associative/multiset/find.pass.cpp
    libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
    libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
    libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
    libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
    libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
    libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
    libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
    libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
    libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
    libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
    libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
    libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
    libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
    libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
    libcxx/test/std/containers/associative/multiset/merge.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
    libcxx/test/std/containers/associative/multiset/size.pass.cpp
    libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/set b/libcxx/include/set
index a8e245b22ae51..0409131ac6981 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -1176,31 +1176,34 @@ public:
   friend class multiset;
 
   // construct/copy/destroy:
-  _LIBCPP_HIDE_FROM_ABI multiset() _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset() _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_default_constructible<key_compare>::value&&
           is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(value_compare()) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const value_compare& __comp) _NOEXCEPT_(
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const value_compare& __comp) _NOEXCEPT_(
       is_nothrow_default_constructible<allocator_type>::value&& is_nothrow_copy_constructible<key_compare>::value)
       : __tree_(__comp) {}
 
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const value_compare& __comp, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {}
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__f, __l);
   }
 
 #  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multiset(__f, __l, key_compare(), __a) {}
 #  endif
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(_InputIterator __f, _InputIterator __l, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__f, __l);
@@ -1208,7 +1211,7 @@ public:
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(from_range_t,
            _Range&& __range,
            const key_compare& __comp = key_compare(),
@@ -1218,213 +1221,272 @@ public:
   }
 
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI multiset(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(from_range_t, _Range&& __range, const allocator_type& __a)
       : multiset(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(const multiset& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(const multiset& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(const multiset& __s) = default;
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(multiset&& __s) = default;
 
-  _LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s, const allocator_type& __a) : __tree_(std::move(__s.__tree_), __a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(multiset&& __s, const allocator_type& __a)
+      : __tree_(std::move(__s.__tree_), __a) {}
 #  endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI explicit multiset(const allocator_type& __a) : __tree_(__a) {}
-  _LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s, const allocator_type& __a) : __tree_(__s.__tree_, __a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit multiset(const allocator_type& __a) : __tree_(__a) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(const multiset& __s, const allocator_type& __a)
+      : __tree_(__s.__tree_, __a) {}
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
       : __tree_(__comp) {
     insert(__il.begin(), __il.end());
   }
 
-  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
   multiset(initializer_list<value_type> __il, const value_compare& __comp, const allocator_type& __a)
       : __tree_(__comp, __a) {
     insert(__il.begin(), __il.end());
   }
 
 #    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(initializer_list<value_type> __il, const allocator_type& __a)
       : multiset(__il, key_compare(), __a) {}
 #    endif
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(initializer_list<value_type> __il) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(initializer_list<value_type> __il) {
     clear();
     insert(__il.begin(), __il.end());
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI multiset& operator=(multiset&& __s) = default;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(multiset&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI ~multiset() {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~multiset() {
     static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return __tree_.begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return __tree_.begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return __tree_.end(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return __tree_.end(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT {
+    return __tree_.begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator begin() const _NOEXCEPT {
+    return __tree_.begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator end() _NOEXCEPT {
+    return __tree_.end();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator end() const _NOEXCEPT {
+    return __tree_.end();
+  }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT { return reverse_iterator(end()); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rbegin() const _NOEXCEPT {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rbegin() _NOEXCEPT {
+    return reverse_iterator(end());
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator
+  rbegin() const _NOEXCEPT {
     return const_reverse_iterator(end());
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI reverse_iterator rend() _NOEXCEPT { return reverse_iterator(begin()); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const _NOEXCEPT {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reverse_iterator rend() _NOEXCEPT {
+    return reverse_iterator(begin());
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator rend() const _NOEXCEPT {
     return const_reverse_iterator(begin());
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const _NOEXCEPT { return begin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator cend() const _NOEXCEPT { return end(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crbegin() const _NOEXCEPT { return rbegin(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const _NOEXCEPT { return rend(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cbegin() const _NOEXCEPT {
+    return begin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator cend() const _NOEXCEPT {
+    return end();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator
+  crbegin() const _NOEXCEPT {
+    return rbegin();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_reverse_iterator crend() const _NOEXCEPT {
+    return rend();
+  }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT { return __tree_.size() == 0; }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __tree_.size(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT { return __tree_.max_size(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool empty() const _NOEXCEPT {
+    return __tree_.size() == 0;
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type size() const _NOEXCEPT {
+    return __tree_.size();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type max_size() const _NOEXCEPT {
+    return __tree_.max_size();
+  }
 
   // modifiers:
 #  ifndef _LIBCPP_CXX03_LANG
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace(_Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace(_Args&&... __args) {
     return __tree_.__emplace_multi(std::forward<_Args>(__args)...);
   }
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI iterator emplace_hint(const_iterator __p, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator emplace_hint(const_iterator __p, _Args&&... __args) {
     return __tree_.__emplace_hint_multi(__p, std::forward<_Args>(__args)...);
   }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const value_type& __v) { return __tree_.__emplace_multi(__v); }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, const value_type& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const value_type& __v) {
+    return __tree_.__emplace_multi(__v);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, const value_type& __v) {
     return __tree_.__emplace_hint_multi(__p, __v);
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __first, _InputIterator __last) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(_InputIterator __first, _InputIterator __last) {
     __tree_.__insert_range_multi(__first, __last);
   }
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert_range(_Range&& __range) {
     __tree_.__insert_range_multi(ranges::begin(__range), ranges::end(__range));
   }
 #  endif
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI iterator insert(value_type&& __v) { return __tree_.__emplace_multi(std::move(__v)); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(value_type&& __v) {
+    return __tree_.__emplace_multi(std::move(__v));
+  }
 
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __p, value_type&& __v) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __p, value_type&& __v) {
     return __tree_.__emplace_hint_multi(__p, std::move(__v));
   }
 
-  _LIBCPP_HIDE_FROM_ABI void insert(initializer_list<value_type> __il) { insert(__il.begin(), __il.end()); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void insert(initializer_list<value_type> __il) {
+    insert(__il.begin(), __il.end());
+  }
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
-  _LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_multi(__k); }
-  _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
-  _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p) { return __tree_.erase(__p); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(const key_type& __k) {
+    return __tree_.__erase_multi(__k);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
+    return __tree_.erase(__f, __l);
+  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
-  _LIBCPP_HIDE_FROM_ABI iterator insert(node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to multiset::insert()");
     return __tree_.template __node_handle_insert_multi<node_type>(std::move(__nh));
   }
-  _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __hint, node_type&& __nh) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(const_iterator __hint, node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
                                         "node_type with incompatible allocator passed to multiset::insert()");
     return __tree_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract<node_type>(__it);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+  merge(multiset<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
   template <class _Compare2>
-  _LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>&& __source) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>&& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
         __source.get_allocator() == get_allocator(), "merging container with incompatible allocator");
     __tree_.__node_handle_merge_multi(__source.__tree_);
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI void swap(multiset& __s) _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(multiset& __s)
+      _NOEXCEPT_(__is_nothrow_swappable_v<__base>) {
     __tree_.swap(__s.__tree_);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const _NOEXCEPT { return __tree_.__alloc(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI key_compare key_comp() const { return __tree_.value_comp(); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI value_compare value_comp() const { return __tree_.value_comp(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type get_allocator() const _NOEXCEPT {
+    return __tree_.__alloc();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_compare key_comp() const {
+    return __tree_.value_comp();
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_compare value_comp() const {
+    return __tree_.value_comp();
+  }
 
   // set operations:
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const key_type& __k) { return __tree_.find(__k); }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const key_type& __k) const { return __tree_.find(__k); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const key_type& __k) {
+    return __tree_.find(__k);
+  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const key_type& __k) const {
+    return __tree_.find(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator find(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const _K2& __k) {
     return __tree_.find(__k);
   }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator find(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const _K2& __k) const {
     return __tree_.find(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const key_type& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_type count(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type count(const _K2& __k) const {
     return __tree_.__count_multi(__k);
   }
 #  endif
 
 #  if _LIBCPP_STD_VER >= 20
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const key_type& __k) const { return find(__k) != end(); }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const key_type& __k) const {
+    return find(__k) != end();
+  }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool contains(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool contains(const _K2& __k) const {
     return find(__k) != end();
   }
 #  endif // _LIBCPP_STD_VER >= 20
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const key_type& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const key_type& __k) {
     return __tree_.__lower_bound_multi(__k);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  lower_bound(const key_type& __k) const {
     return __tree_.__lower_bound_multi(__k);
   }
 
@@ -1432,23 +1494,25 @@ public:
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator lower_bound(const _K2& __k) {
     return __tree_.__lower_bound_multi(__k);
   }
 
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  lower_bound(const _K2& __k) const {
     return __tree_.__lower_bound_multi(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const key_type& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const key_type& __k) {
     return __tree_.__upper_bound_multi(__k);
   }
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  upper_bound(const key_type& __k) const {
     return __tree_.__upper_bound_multi(__k);
   }
 
@@ -1456,30 +1520,35 @@ public:
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const _K2& __k) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator upper_bound(const _K2& __k) {
     return __tree_.__upper_bound_multi(__k);
   }
   template <typename _K2,
             class _Comp = _Compare,
             enable_if_t<__is_transparent_v<_Comp> || __is_transparently_comparable_v<_Comp, key_type, _K2>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
+  upper_bound(const _K2& __k) const {
     return __tree_.__upper_bound_multi(__k);
   }
 #  endif
 
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const key_type& __k) {
+  [[__nodiscard__]]
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const key_type& __k) {
     return __tree_.__equal_range_multi(__k);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const key_type& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const key_type& __k) const {
     return __tree_.__equal_range_multi(__k);
   }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<iterator, iterator> equal_range(const _K2& __k) {
+  [[__nodiscard__]]
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> equal_range(const _K2& __k) {
     return __tree_.__equal_range_multi(__k);
   }
   template <typename _K2, class _Comp = _Compare, enable_if_t<__is_transparent_v<_Comp>, int> = 0>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> equal_range(const _K2& __k) const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
+  equal_range(const _K2& __k) const {
     return __tree_.__equal_range_multi(__k);
   }
 #  endif
@@ -1543,7 +1612,7 @@ struct __specialized_algorithm<_Alg, __single_range<multiset<_Key, _Compare, _Al
 
   // set's begin() and end() are identical with and without const qualification
   template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI static auto operator()(const __set& __set, _Args&&... __args) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static auto operator()(const __set& __set, _Args&&... __args) {
     return __specialized_algorithm<_Alg, __single_range<typename __set::__base>>()(
         __set.__tree_, std::forward<_Args>(__args)...);
   }
@@ -1551,7 +1620,7 @@ struct __specialized_algorithm<_Alg, __single_range<multiset<_Key, _Compare, _Al
 #  endif
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator==(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return __x.size() == __y.size() && std::equal(__x.begin(), __x.end(), __y.begin());
 }
@@ -1559,31 +1628,31 @@ operator==(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key,
 #  if _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare(__x.begin(), __x.end(), __y.begin(), __y.end());
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator!=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return !(__x == __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return __y < __x;
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator>=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return !(__x < __y);
 }
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI bool
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
 operator<=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return !(__y < __x);
 }
@@ -1591,7 +1660,7 @@ operator<=(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key,
 #  else // _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI __synth_three_way_result<_Key>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __synth_three_way_result<_Key>
 operator<=>(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {
   return std::lexicographical_compare_three_way(__x.begin(), __x.end(), __y.begin(), __y.end(), __synth_three_way);
 }
@@ -1599,7 +1668,7 @@ operator<=>(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key
 #  endif // _LIBCPP_STD_VER <= 17
 
 template <class _Key, class _Compare, class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI void
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
 swap(multiset<_Key, _Compare, _Allocator>& __x, multiset<_Key, _Compare, _Allocator>& __y)
     _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
   __x.swap(__y);
@@ -1607,7 +1676,7 @@ swap(multiset<_Key, _Compare, _Allocator>& __x, multiset<_Key, _Compare, _Alloca
 
 #  if _LIBCPP_STD_VER >= 20
 template <class _Key, class _Compare, class _Allocator, class _Predicate>
-inline _LIBCPP_HIDE_FROM_ABI typename multiset<_Key, _Compare, _Allocator>::size_type
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename multiset<_Key, _Compare, _Allocator>::size_type
 erase_if(multiset<_Key, _Compare, _Allocator>& __c, _Predicate __pred) {
   return std::__libcpp_erase_if_container(__c, __pred);
 }

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
index 645e1e4af792e..7b0e5b93dc618 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/for_each.associative.pass.cpp
@@ -71,12 +71,8 @@ TEST_CONSTEXPR_CXX26 void test_node_container(Converter conv) {
 }
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  // FIXME: remove when set is made constexpr
-  if (!TEST_IS_CONSTANT_EVALUATED)
-    test_node_container<std::set<int> >([](int i) { return i; });
-  // FIXME: remove when multiset is made constexpr
-  if (!TEST_IS_CONSTANT_EVALUATED)
-    test_node_container<std::multiset<int> >([](int i) { return i; });
+  test_node_container<std::set<int> >([](int i) { return i; });
+  test_node_container<std::multiset<int> >([](int i) { return i; });
   test_node_container<std::map<int, int> >([](int i) { return std::make_pair(i, i); });
   test_node_container<std::multimap<int, int> >([](int i) { return std::make_pair(i, i); });
 

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
index 02698819d71df..72aa40a7f7e0a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.foreach/ranges.for_each.associative.pass.cpp
@@ -252,20 +252,13 @@ TEST_CONSTEXPR_CXX26 void test_invoke_map_like() {
 }
 
 TEST_CONSTEXPR_CXX26 bool test() {
-  if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when multiset is made constexpr
-    test_node_container<std::multiset<int> >([](int i) { return i; });
-  }
   test_node_container<std::set<int> >([](int i) { return i; });
+  test_node_container<std::multiset<int> >([](int i) { return i; });
   test_node_container<std::map<int, int> >([](int i) { return std::make_pair(i, i); });
   test_node_container<std::multimap<int, int> >([](int i) { return std::make_pair(i, i); });
 
-  if (!TEST_IS_CONSTANT_EVALUATED) {
-    // FIXME: remove when multiset is made constexpr
-    test_invoke_set_like<std::multiset>();
-  }
-
   test_invoke_set_like<std::set>();
+  test_invoke_set_like<std::multiset>();
   test_invoke_map_like<std::map>();
   test_invoke_map_like<std::multimap>();
 

diff  --git a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
index 61a5f25eac608..b8110efe4a4ff 100644
--- a/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/clear.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// void clear() noexcept;
+// void clear() noexcept; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -42,5 +42,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/count.pass.cpp b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
index 6ad96b232083c..6bfcdc66aada9 100644
--- a/libcxx/test/std/containers/associative/multiset/count.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type count(const key_type& k) const;
+// size_type count(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -124,5 +124,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
index aea63384887d4..815260f67c231 100644
--- a/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/count_transparent.pass.cpp
@@ -13,27 +13,39 @@
 // class multiset
 
 // template<typename K>
-//     size_type count(const K& x) const;        // C++14
+//     size_type count(const K& x) const;        // C++14, constexpr since C++26
 
 #include <cassert>
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<std::pair<int, int>, Comp> s{{2, 1}, {1, 1}, {1, 1}, {1, 1}, {2, 2}};
 
   auto cnt = s.count(1);
   assert(cnt == 3);
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
index 97cae1c565216..193926b6dfc87 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace.pass.cpp
@@ -13,7 +13,7 @@
 // class multiset
 
 // template <class... Args>
-//   iterator emplace(Args&&... args);
+//   iterator emplace(Args&&... args); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -23,8 +23,8 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
-  {
+TEST_CONSTEXPR_CXX26 bool test() {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
     M m;
@@ -41,7 +41,9 @@ int main(int, char**) {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 2);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    assert(DefaultOnly::count == 0);
+  }
   {
     typedef std::multiset<Emplaceable> M;
     typedef M::iterator R;
@@ -84,5 +86,13 @@ int main(int, char**) {
     assert(set.begin() == iter);
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
index d8e9d1cd4d1c6..5650b27d19a05 100644
--- a/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/emplace_hint.pass.cpp
@@ -13,7 +13,7 @@
 // class multiset
 
 // template <class... Args>
-//   iterator emplace_hint(const_iterator position, Args&&... args);
+//   iterator emplace_hint(const_iterator position, Args&&... args); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,8 +23,8 @@
 #include "DefaultOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
-  {
+TEST_CONSTEXPR_CXX26 bool test() {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::multiset<DefaultOnly> M;
     typedef M::iterator R;
     M m;
@@ -41,7 +41,9 @@ int main(int, char**) {
     assert(*m.begin() == DefaultOnly());
     assert(DefaultOnly::count == 2);
   }
-  assert(DefaultOnly::count == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    assert(DefaultOnly::count == 0);
+  }
   {
     typedef std::multiset<Emplaceable> M;
     typedef M::iterator R;
@@ -78,5 +80,13 @@ int main(int, char**) {
     assert(*r == 2);
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
index 944f610a0c507..08027484fadb6 100644
--- a/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/empty.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// bool empty() const;
+// bool empty() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     M m;
@@ -40,5 +40,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
index e5c1d2b1eab98..7d623036c8e34 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-// pair<iterator,iterator>             equal_range(const key_type& k);
-// pair<const_iterator,const_iterator> equal_range(const key_type& k) const;
+// pair<iterator,iterator>             equal_range(const key_type& k); // constexpr since C++26
+// pair<const_iterator,const_iterator> equal_range(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -205,5 +205,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
index 6402488a44f22..385b05c8f26b1 100644
--- a/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/equal_range_transparent.pass.cpp
@@ -14,26 +14,30 @@
 
 // template<typename K>
 //     pair<iterator,iterator>             equal_range(const K& x);        //
-//     C++14
+//     C++14, constexpr since C++26
 // template<typename K>
 //     pair<const_iterator,const_iterator> equal_range(const K& x) const;  //
-//     C++14
+//     C++14, constexpr since C++26
 
 #include <cassert>
 #include <set>
 #include <utility>
 
+#include "test_macros.h"
+
 struct Comp {
   using is_transparent = void;
 
-  bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const { return lhs < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const {
+    return lhs < rhs;
+  }
 
-  bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
+  TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& lhs, int rhs) const { return lhs.first < rhs; }
 
-  bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
+  TEST_CONSTEXPR_CXX26 bool operator()(int lhs, const std::pair<int, int>& rhs) const { return lhs < rhs.first; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<std::pair<int, int>, Comp> s{{2, 1}, {1, 1}, {1, 1}, {1, 1}, {2, 2}};
 
   auto er   = s.equal_range(1);
@@ -46,5 +50,13 @@ int main(int, char**) {
 
   assert(nels == 3);
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
index 31f00feda2977..a7da30fcd2a96 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator erase(const_iterator position);
+// iterator erase(const_iterator position); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,12 +20,12 @@
 
 struct TemplateConstructor {
   template <typename T>
-  TemplateConstructor(const T&) {}
+  TEST_CONSTEXPR_CXX26 TemplateConstructor(const T&) {}
 };
 
-bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
+TEST_CONSTEXPR_CXX26 bool operator<(const TemplateConstructor&, const TemplateConstructor&) { return false; }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -179,5 +179,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
index a3937d13bbb4b..942b076c702ea 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_iter_iter.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator erase(const_iterator first, const_iterator last);
+// iterator erase(const_iterator first, const_iterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -118,5 +118,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
index ba04a238e44b5..c6fe8f0563370 100644
--- a/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/erase_key.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type erase(const key_type& k);
+// size_type erase(const key_type& k); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef int V;
@@ -104,5 +104,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
index 7f32fa82c46ab..c07a0d246fcf7 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_iterator.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// node_type extract(const_iterator);
+// node_type extract(const_iterator); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   std::size_t sz = c.size();
 
   for (auto first = c.cbegin(); first != c.cend();) {
@@ -35,14 +35,14 @@ void test(Container& c) {
   assert(c.size() == 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     using set_type = std::multiset<int>;
     set_type m     = {1, 2, 3, 4, 5, 6};
     test(m);
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::multiset<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     assert(Counter_base::gConstructed == 6);
     test(m);
@@ -55,5 +55,13 @@ int main(int, char**) {
     test(m);
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
index ec75a6c5d835c..52adc8ade796c 100644
--- a/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract_key.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// node_type extract(key_type const&);
+// node_type extract(key_type const&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
@@ -20,7 +20,7 @@
 #include "Counter.h"
 
 template <class Container, class KeyTypeIter>
-void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
+TEST_CONSTEXPR_CXX26 void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   std::size_t sz = c.size();
   assert((std::size_t)std::distance(first, last) == sz);
 
@@ -41,7 +41,7 @@ void test(Container& c, KeyTypeIter first, KeyTypeIter last) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> m = {1, 2, 3, 4, 5, 6};
     int keys[]           = {1, 2, 3, 4, 5, 6};
@@ -61,7 +61,7 @@ int main(int, char**) {
     assert(!res);
   }
 
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     std::multiset<Counter<int>> m = {1, 2, 3, 4, 5, 6};
     {
       Counter<int> keys[] = {1, 2, 3, 4, 5, 6};
@@ -78,5 +78,13 @@ int main(int, char**) {
     test(m, std::begin(keys), std::end(keys));
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/find.pass.cpp b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
index 866de0da5ea93..bb5be9a28502b 100644
--- a/libcxx/test/std/containers/associative/multiset/find.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/find.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-//       iterator find(const key_type& k);
-// const_iterator find(const key_type& k) const;
+//       iterator find(const key_type& k); // constexpr since C++26
+// const_iterator find(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -202,5 +202,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
index 55bbd567d4360..3def31b6e0c84 100644
--- a/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/get_allocator.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// allocator_type get_allocator() const
+// allocator_type get_allocator() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::allocator<int> alloc;
     const std::multiset<int> s(alloc);
@@ -29,5 +29,13 @@ int main(int, char**) {
     const std::multiset<int, std::less<int>, other_allocator<int> > s(alloc);
     assert(s.get_allocator() == alloc);
   }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
index 31cb96ec33e76..63fd68a747c32 100644
--- a/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/incomplete_type.pass.cpp
@@ -23,10 +23,18 @@ struct A {
   Set::const_iterator cit;
 };
 
-inline bool operator==(A const& L, A const& R) { return &L == &R; }
-inline bool operator<(A const& L, A const& R) { return L.data < R.data; }
-int main(int, char**) {
+inline TEST_CONSTEXPR_CXX26 bool operator==(A const& L, A const& R) { return &L == &R; }
+inline TEST_CONSTEXPR_CXX26 bool operator<(A const& L, A const& R) { return L.data < R.data; }
+TEST_CONSTEXPR_CXX26 bool test() {
   A a;
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
index 237cf9bb6fac2..556f29496dda2 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator insert(const value_type& v);
+// iterator insert(const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "min_allocator.h"
 
 template <class Container>
-void do_insert_cv_test() {
+TEST_CONSTEXPR_CXX26 void do_insert_cv_test() {
   typedef Container M;
   typedef typename M::iterator R;
   typedef typename M::value_type VT;
@@ -48,7 +48,7 @@ void do_insert_cv_test() {
   assert(*r == 3);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   do_insert_cv_test<std::multiset<int> >();
 #if TEST_STD_VER >= 11
   {
@@ -57,5 +57,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
index 8f4f165252d36..b561b5864c771 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// void insert(initializer_list<value_type> il);
+// void insert(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> C;
     typedef C::value_type V;
@@ -57,5 +57,13 @@ int main(int, char**) {
     assert(*++i == V(10));
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
index 97c03be6cc57b..eb63f2f838891 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_cv.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// iterator insert(const_iterator position, const value_type& v);
+// iterator insert(const_iterator position, const value_type& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     typedef M::iterator R;
@@ -70,5 +70,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
index 56483714cd50c..858ca520a304a 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_iter.pass.cpp
@@ -21,7 +21,7 @@
 #include "test_iterators.h"
 
 template <class Iter, class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   {   // Check that an empty range works correctly
     { // Without elements in the container
       using Map = std::multiset<int, std::less<int>, Alloc>;
@@ -184,13 +184,17 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<cpp17_input_iterator<int*>, std::allocator<int> >();
   test_alloc<cpp17_input_iterator<int*>, min_allocator<int> >();
+  return true;
 }
 
 int main(int, char**) {
   test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
 
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
index d01f6b9b2c306..874e560f6a533 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_iter_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(const_iterator position, value_type&& v);
+// iterator insert(const_iterator position, value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> M;
     typedef M::iterator R;
@@ -71,5 +71,13 @@ int main(int, char**) {
     assert(*r == 3);
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
index 6971d6b6e5eae..4a20c2792c76e 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(node_type&&);
+// iterator insert(node_type&&); // constexpr since C++26
 
 #include <set>
 #include <type_traits>
@@ -20,18 +20,18 @@
 #include "min_allocator.h"
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto it = c.insert(key);
   return c.extract(it);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
+  Container c2;
 
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c, i);
     assert(!node.empty());
     typename Container::iterator it = c.insert(std::move(node));
     assert(node.empty());
@@ -50,7 +50,7 @@ void test(Container& c) {
   }
 
   { // Insert duplicate node.
-    typename Container::node_type dupl = nf(0);
+    typename Container::node_type dupl = nf(c2, 0);
     auto it                            = c.insert(std::move(dupl));
     assert(*it == 0);
   }
@@ -63,11 +63,19 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<int> m;
   test(m);
   std::multiset<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
index b0392088beb6b..b5c2aae45f01f 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_node_type_hint.pass.cpp
@@ -12,25 +12,26 @@
 
 // class multiset
 
-// iterator insert(const_iterator hint, node_type&&);
+// iterator insert(const_iterator hint, node_type&&); // constexpr since C++26
 
 #include <set>
 #include "test_macros.h"
 #include "min_allocator.h"
 
 template <class Container>
-typename Container::node_type node_factory(typename Container::key_type const& key) {
-  static Container c;
+TEST_CONSTEXPR_CXX26 typename Container::node_type node_factory(Container& c, typename Container::key_type const& key) {
   auto it = c.insert(key);
   return c.extract(it);
 }
 
 template <class Container>
-void test(Container& c) {
+TEST_CONSTEXPR_CXX26 void test(Container& c) {
   auto* nf = &node_factory<Container>;
 
+  Container c2;
+
   for (int i = 0; i != 10; ++i) {
-    typename Container::node_type node = nf(i);
+    typename Container::node_type node = nf(c2, i);
     assert(!node.empty());
     std::size_t prev = c.size();
     auto it          = c.insert(c.end(), std::move(node));
@@ -45,11 +46,19 @@ void test(Container& c) {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   std::multiset<int> m;
   test(m);
   std::multiset<int, std::less<int>, min_allocator<int>> m2;
   test(m2);
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
index d57e14f4b59c8..55edbfd8af079 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_range.pass.cpp
@@ -9,18 +9,19 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // Some fields in the test case variables are deliberately not explicitly initialized, this silences a warning on GCC.
 // ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-missing-field-initializers
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=4000000
 
 // <set>
 
 // template<container-compatible-range<value_type> R>
-//   void insert_range(R&& rg); // C++23
+//   void insert_range(R&& rg); // C++23, constexpr since C++26
 
 #include <set>
 
 #include "../../insert_range_maps_sets.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
     test_map_set_insert_range<std::multiset<int, test_less<int>, Alloc>, int, Iter, Sent>(/*allow_duplicates=*/true);
   });
@@ -32,5 +33,13 @@ int main(int, char**) {
   test_set_insert_range_exception_safety_throwing_copy<std::multiset>();
   test_assoc_set_insert_range_exception_safety_throwing_allocator<std::multiset, int>();
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
index 88155572f1680..387a99c042f7d 100644
--- a/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/insert_rv.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// iterator insert(value_type&& v);
+// iterator insert(value_type&& v); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "MoveOnly.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> M;
     typedef M::iterator R;
@@ -71,5 +71,13 @@ int main(int, char**) {
     assert(*r == 3);
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
index fee8bf9d3eb30..542ba871960ee 100644
--- a/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/iterator.pass.cpp
@@ -10,20 +10,20 @@
 
 // class multiset
 
-//       iterator begin();
-// const_iterator begin() const;
-//       iterator end();
-// const_iterator end()   const;
+//       iterator begin(); // constexpr since C++26
+// const_iterator begin() const; // constexpr since C++26
+//       iterator end(); // constexpr since C++26
+// const_iterator end()   const; // constexpr since C++26
 //
-//       reverse_iterator rbegin();
-// const_reverse_iterator rbegin() const;
-//       reverse_iterator rend();
-// const_reverse_iterator rend()   const;
+//       reverse_iterator rbegin(); // constexpr since C++26
+// const_reverse_iterator rbegin() const; // constexpr since C++26
+//       reverse_iterator rend(); // constexpr since C++26
+// const_reverse_iterator rend()   const; // constexpr since C++26
 //
-// const_iterator         cbegin()  const;
-// const_iterator         cend()    const;
-// const_reverse_iterator crbegin() const;
-// const_reverse_iterator crend()   const;
+// const_iterator         cbegin()  const; // constexpr since C++26
+// const_iterator         cend()    const; // constexpr since C++26
+// const_reverse_iterator crbegin() const; // constexpr since C++26
+// const_reverse_iterator crend()   const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -32,7 +32,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8};
@@ -137,5 +137,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
index ddd39d8fc0348..c2883e039c8a9 100644
--- a/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/lower_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-//       iterator lower_bound(const key_type& k);
-// const_iterator lower_bound(const key_type& k) const;
+//       iterator lower_bound(const key_type& k); // constexpr since C++26
+// const_iterator lower_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -165,5 +165,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
index a6cff58b090bd..b7b6d28d002ac 100644
--- a/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/max_size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type max_size() const;
+// size_type max_size() const; // constexpr since C++26
 
 #include <cassert>
 #include <limits>
@@ -20,7 +20,7 @@
 #include "test_allocator.h"
 #include "test_macros.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef limited_allocator<int, 10> A;
     typedef std::multiset<int, std::less<int>, A> C;
@@ -44,5 +44,13 @@ int main(int, char**) {
     assert(c.max_size() <= alloc_max_size(c.get_allocator()));
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
index 90eaeb2adbfcb..0397f16c52d41 100644
--- a/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/merge.pass.cpp
@@ -13,13 +13,13 @@
 // class multiset
 
 // template <class C2>
-//   void merge(set<Key, C2, Allocator>& source);
+//   void merge(set<Key, C2, Allocator>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(set<Key, C2, Allocator>&& source);
+//   void merge(set<Key, C2, Allocator>&& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<Key, C2, Allocator>& source);
+//   void merge(multiset<Key, C2, Allocator>& source); // constexpr since C++26
 // template <class C2>
-//   void merge(multiset<Key, C2, Allocator>&& source);
+//   void merge(multiset<Key, C2, Allocator>&& source); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -27,7 +27,7 @@
 #include "Counter.h"
 
 template <class Set>
-bool set_equal(const Set& set, Set other) {
+TEST_CONSTEXPR_CXX26 bool set_equal(const Set& set, Set other) {
   return set == other;
 }
 
@@ -35,10 +35,10 @@ bool set_equal(const Set& set, Set other) {
 struct throw_comparator {
   bool& should_throw_;
 
-  throw_comparator(bool& should_throw) : should_throw_(should_throw) {}
+  TEST_CONSTEXPR_CXX26 throw_comparator(bool& should_throw) : should_throw_(should_throw) {}
 
   template <class T>
-  bool operator()(const T& lhs, const T& rhs) const {
+  TEST_CONSTEXPR_CXX26 bool operator()(const T& lhs, const T& rhs) const {
     if (should_throw_)
       throw 0;
     return lhs < rhs;
@@ -46,7 +46,7 @@ struct throw_comparator {
 };
 #endif
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> src{1, 3, 5};
     std::multiset<int> dst{2, 4, 5};
@@ -56,7 +56,8 @@ int main(int, char**) {
   }
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
-  {
+
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     bool do_throw = false;
     typedef std::multiset<Counter<int>, throw_comparator> set_type;
     set_type src({1, 3, 5}, throw_comparator(do_throw));
@@ -75,13 +76,15 @@ int main(int, char**) {
     assert(set_equal(dst, set_type({2, 4, 5}, throw_comparator(do_throw))));
   }
 #endif
-  assert(Counter_base::gConstructed == 0);
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    assert(Counter_base::gConstructed == 0);
+  }
   struct comparator {
     comparator() = default;
 
     bool operator()(const Counter<int>& lhs, const Counter<int>& rhs) const { return lhs < rhs; }
   };
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef std::multiset<Counter<int>, std::less<Counter<int>>> first_set_type;
     typedef std::multiset<Counter<int>, comparator> second_set_type;
     typedef std::set<Counter<int>, comparator> third_set_type;
@@ -134,5 +137,13 @@ int main(int, char**) {
       first.merge(std::move(second));
     }
   }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
index 1f05201d706b1..fafcf50fa15ad 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const allocator_type& a);
+// multiset(const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::less<int> C;
   typedef test_allocator<int> A;
   std::multiset<int, C, A> m(A(5));
@@ -26,5 +26,13 @@ int main(int, char**) {
   assert(m.begin() == m.end());
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
index 7234e61d28bfa..914c07ed9d6f7 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/assign_initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset& operator=(initializer_list<value_type> il);
+// multiset& operator=(initializer_list<value_type> il); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> C;
     typedef C::value_type V;
@@ -52,5 +52,13 @@ int main(int, char**) {
     assert(*++i == V(6));
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
index b7da7f66b020d..635b5999dbcd7 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// explicit multiset(const key_compare& comp);
+// explicit multiset(const key_compare& comp); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   const std::multiset<int, C> m(C(3));
   assert(m.empty());
@@ -26,5 +26,13 @@ int main(int, char**) {
   assert(m.key_comp() == C(3));
   assert(m.value_comp() == C(3));
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
index be8a451fdc405..e8138c0501263 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/compare_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const value_compare& comp, const allocator_type& a);
+// multiset(const value_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -19,7 +19,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> C;
   typedef test_allocator<int> A;
   std::multiset<int, C, A> m(C(4), A(5));
@@ -28,5 +28,13 @@ int main(int, char**) {
   assert(m.key_comp() == C(4));
   assert(m.get_allocator() == A(5));
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
index c747828aa01cc..7d56ea93a9a45 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const multiset& m);
+// multiset(const multiset& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 
 template <template <class> class Alloc>
-void test_alloc() {
+TEST_CONSTEXPR_CXX26 void test_alloc() {
   { // Simple check
     using Set = std::multiset<int, std::less<int>, Alloc<int> >;
 
@@ -81,7 +81,7 @@ void test_alloc() {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator>();
   test_alloc<min_allocator>(); // Make sure that fancy pointers work
 
@@ -127,10 +127,14 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.get_allocator() == other_allocator<int>(10));
   }
+
+  return true;
 }
 
 int main(int, char**) {
   test();
-
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
index 868072ef15288..f79ae3713540b 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset(const multiset& m, const allocator_type& a);
+// multiset(const multiset& m, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 
 template <class Alloc>
-void test_alloc(const Alloc& new_alloc) {
+TEST_CONSTEXPR_CXX26 void test_alloc(const Alloc& new_alloc) {
   { // Simple check
     using Set = std::multiset<int, std::less<int>, Alloc>;
 
@@ -82,7 +82,7 @@ void test_alloc(const Alloc& new_alloc) {
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc(std::allocator<int>());
   test_alloc(test_allocator<int>(25)); // Make sure that the new allocator is actually used
   test_alloc(min_allocator<int>());    // Make sure that fancy pointers work
@@ -98,10 +98,14 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
+
+  return true;
 }
 
 int main(int, char**) {
   test();
-
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
index 0a5fe62eed69e..86b86f92f3338 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset& operator=(const multiset& s);
+// multiset& operator=(const multiset& s); // constexpr since C++26
 
 #include <algorithm>
 #include <cassert>
@@ -33,41 +33,42 @@ class tracking_allocator {
   using value_type                             = T;
   using propagate_on_container_copy_assignment = std::true_type;
 
-  tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
 
   template <class U>
-  tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
+  TEST_CONSTEXPR_CXX26 tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
 
-  T* allocate(std::size_t n) {
+  TEST_CONSTEXPR_CXX26 T* allocate(std::size_t n) {
     T* allocation = std::allocator<T>().allocate(n);
     allocs_->push_back(allocation);
     return allocation;
   }
 
-  void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
+  TEST_CONSTEXPR_CXX26 void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
     auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
     assert(res != allocs_->end() && "Trying to deallocate memory from 
diff erent allocator?");
     allocs_->erase(res);
     std::allocator<T>().deallocate(ptr, n);
   }
 
-  friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  friend TEST_CONSTEXPR_CXX26 bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ == rhs.allocs_;
   }
 
-  friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+  friend TEST_CONSTEXPR_CXX26 bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
     return lhs.allocs_ != rhs.allocs_;
   }
 };
 
 struct NoOp {
-  void operator()() {}
+  TEST_CONSTEXPR_CXX26 void operator()() {}
 };
 
 template <class Alloc, class AllocatorInvariant = NoOp>
-void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
-                const Alloc& rhs_alloc                   = Alloc(),
-                AllocatorInvariant check_alloc_invariant = NoOp()) {
+TEST_CONSTEXPR_CXX26 void
+test_alloc(const Alloc& lhs_alloc                   = Alloc(),
+           const Alloc& rhs_alloc                   = Alloc(),
+           AllocatorInvariant check_alloc_invariant = NoOp()) {
   {   // Test empty/non-empty multiset combinations
     { // assign from a non-empty container into an empty one
       using Set = std::multiset<int, std::less<int>, Alloc>;
@@ -234,7 +235,7 @@ void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
   }
 }
 
-void test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_alloc<std::allocator<int> >();
 #if TEST_STD_VER >= 11
   test_alloc<min_allocator<int> >();
@@ -246,10 +247,10 @@ void test() {
       std::vector<void*>* rhs_allocs_;
 
     public:
-      AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
+      TEST_CONSTEXPR_CXX26 AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
           : lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
 
-      void operator()() {
+      TEST_CONSTEXPR_CXX26 void operator()() {
         assert(lhs_allocs_->empty());
         assert(rhs_allocs_->empty());
       }
@@ -273,10 +274,14 @@ void test() {
     assert(orig.size() == 3);
     assert(orig.key_comp() == test_less<int>(3));
   }
+
+  return true;
 }
 
 int main(int, char**) {
   test();
-
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
index f9797ca571c47..7a2cc45ae9cdc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/deduct.pass.cpp
@@ -49,10 +49,10 @@
 #include "test_allocator.h"
 
 struct NotAnAllocator {
-  friend bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
+  friend TEST_CONSTEXPR_CXX26 bool operator<(NotAnAllocator, NotAnAllocator) { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     const int arr[] = {1, 2, 1, INT_MAX, 3};
     std::multiset s(std::begin(arr), std::end(arr));
@@ -212,5 +212,13 @@ int main(int, char**) {
 
   AssociativeContainerDeductionGuidesSfinaeAway<std::multiset, std::multiset<int>>();
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
index 4f7dabfea2c68..256b6f41dc3db 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// multiset();
+// multiset(); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> m;
     assert(m.empty());
@@ -51,5 +51,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
index 052b032d6e798..532a98066e51a 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/default_noexcept.pass.cpp
@@ -12,7 +12,7 @@
 //    noexcept(
 //        is_nothrow_default_constructible<allocator_type>::value &&
 //        is_nothrow_default_constructible<key_compare>::value &&
-//        is_nothrow_copy_constructible<key_compare>::value);
+//        is_nothrow_copy_constructible<key_compare>::value); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -32,7 +32,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::multiset<MoveOnly> C;
@@ -52,5 +52,13 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_default_constructible<C>::value, "");
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
index bf5d256e19a0f..2f81f954c8757 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/dtor_noexcept.pass.cpp
@@ -8,7 +8,7 @@
 
 // <set>
 
-// ~multiset() // implied noexcept;
+// ~multiset() // implied noexcept; // constexpr since C++26
 
 // UNSUPPORTED: c++03
 
@@ -27,7 +27,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> C;
     static_assert(std::is_nothrow_destructible<C>::value, "");
@@ -47,5 +47,13 @@ int main(int, char**) {
   }
 #endif // _LIBCPP_VERSION
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
index 15275781a8aac..ca1d7c20869b9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/from_range.pass.cpp
@@ -7,13 +7,14 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2000000
 
 // template<container-compatible-range<value_type> R>
-//   multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23
+//   multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); // C++23, constexpr since C++26
 //
 // template<container-compatible-range<value_type> R>
 //   multiset(from_range_t, R&& rg, const Allocator& a))
-//     : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
+//     : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23, constexpr since C++26
 
 #include <algorithm>
 #include <array>
@@ -22,13 +23,13 @@
 #include "../../from_range_associative_containers.h"
 #include "test_macros.h"
 
-void test_duplicates() {
+TEST_CONSTEXPR_CXX26 void test_duplicates() {
   std::array input = {1, 2, 3, 3, 3, 4, 2, 1, 2};
   auto c           = std::multiset<int>(std::from_range, input);
   assert(std::ranges::is_permutation(input, c));
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
     test_associative_set<std::multiset, int, Iter, Sent, test_less<int>, Alloc>();
   });
@@ -40,5 +41,13 @@ int main(int, char**) {
   test_set_exception_safety_throwing_copy<std::multiset>();
   test_set_exception_safety_throwing_allocator<std::multiset, int>();
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
index c860f17bb4196..bde073eeee9d7 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> C;
     typedef C::value_type V;
@@ -66,5 +66,13 @@ int main(int, char**) {
     assert(m.get_allocator() == a);
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
index 5bb400cb82be9..f144c05352f09 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare());
+// multiset(initializer_list<value_type> il, const key_compare& comp = key_compare()); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -21,7 +21,7 @@
 #include "test_macros.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> Cmp;
   typedef std::multiset<int, Cmp> C;
   typedef C::value_type V;
@@ -37,5 +37,13 @@ int main(int, char**) {
   assert(*++i == V(6));
   assert(m.key_comp() == Cmp(10));
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
index a12e68c1e582b..63d8199ea434e 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/initializer_list_compare_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a);
+// multiset(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef test_less<int> Cmp;
   typedef test_allocator<int> A;
   typedef std::multiset<int, Cmp, A> C;
@@ -40,5 +40,13 @@ int main(int, char**) {
   assert(m.key_comp() == Cmp(10));
   assert(m.get_allocator() == A(4));
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
index 6b98ceb1c0df7..bb41b35870444 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter.pass.cpp
@@ -11,7 +11,7 @@
 // class multiset
 
 // template <class InputIterator>
-//     multiset(InputIterator first, InputIterator last);
+//     multiset(InputIterator first, InputIterator last); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "test_iterators.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -58,5 +58,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
index 50f8fda2a676b..22ac9bd799e45 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // template <class InputIterator>
 //     multiset(InputIterator first, InputIterator last,
-//         const value_compare& comp, const allocator_type& a);
+//         const value_compare& comp, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
@@ -72,5 +72,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
index bed8d02f3e681..353f3bc84ad9f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_comp.pass.cpp
@@ -11,7 +11,7 @@
 // class multiset
 
 // template <class InputIterator>
-//     multiset(InputIterator first, InputIterator last, const value_compare& comp);
+//     multiset(InputIterator first, InputIterator last, const value_compare& comp); // constexpr since C++26
 
 #include <cassert>
 #include <set>
@@ -21,7 +21,7 @@
 #include "test_iterators.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
   typedef test_less<V> C;
@@ -40,5 +40,13 @@ int main(int, char**) {
   assert(*std::next(m.begin(), 7) == 3);
   assert(*std::next(m.begin(), 8) == 3);
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
index 2d71f21e182c9..a85823e464cbc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(multiset&& s);
+// multiset(multiset&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -22,7 +22,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef test_less<int> C;
@@ -94,5 +94,13 @@ int main(int, char**) {
     assert(std::distance(mo.begin(), mo.end()) == 0);
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
index 771a7038bf633..a3eaa419fbbab 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_alloc.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset(multiset&& s, const allocator_type& a);
+// multiset(multiset&& s, const allocator_type& a); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -24,7 +24,7 @@
 #include "test_allocator.h"
 #include "Counter.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -73,7 +73,7 @@ int main(int, char**) {
     assert(m3.key_comp() == C(5));
     LIBCPP_ASSERT(m1.empty());
   }
-  {
+  if (!TEST_IS_CONSTANT_EVALUATED) {
     typedef Counter<int> V;
     typedef std::less<V> C;
     typedef test_allocator<V> A;
@@ -111,5 +111,13 @@ int main(int, char**) {
     assert(Counter_base::gConstructed == 0);
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
index 5cdd4ec67b8e0..60dc3a5bbc62f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_assign.pass.cpp
@@ -12,7 +12,7 @@
 
 // class multiset
 
-// multiset& operator=(multiset&& s);
+// multiset& operator=(multiset&& s); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -23,7 +23,7 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef MoveOnly V;
     typedef test_less<MoveOnly> C;
@@ -93,5 +93,13 @@ int main(int, char**) {
     assert(m1.empty());
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
index 73b4be5005e1c..52519aaceab3f 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/move_noexcept.pass.cpp
@@ -10,7 +10,7 @@
 
 // multiset(multiset&&)
 //        noexcept(is_nothrow_move_constructible<allocator_type>::value &&
-//                 is_nothrow_move_constructible<key_compare>::value);
+//                 is_nothrow_move_constructible<key_compare>::value); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -30,7 +30,7 @@ struct some_comp {
   bool operator()(const T&, const T&) const { return false; }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
 #if defined(_LIBCPP_VERSION)
   {
     typedef std::multiset<MoveOnly> C;
@@ -50,5 +50,13 @@ int main(int, char**) {
     static_assert(!std::is_nothrow_move_constructible<C>::value, "");
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
index cfb304ee3be39..e0df9210c7da9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase_if.pass.cpp
@@ -11,7 +11,7 @@
 
 // template <class T, class Compare, class Allocator, class Predicate>
 //   typename multiset<T, Compare, Allocator>::size_type
-//   erase_if(multiset<T, Compare, Allocator>& c, Predicate pred);
+//   erase_if(multiset<T, Compare, Allocator>& c, Predicate pred); // constexpr since C++26
 
 #include <set>
 
@@ -20,14 +20,14 @@
 #include "min_allocator.h"
 
 template <class S, class Pred>
-void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
+TEST_CONSTEXPR_CXX26 void test0(S s, Pred p, S expected, std::size_t expected_erased_count) {
   ASSERT_SAME_TYPE(typename S::size_type, decltype(std::erase_if(s, p)));
   assert(expected_erased_count == std::erase_if(s, p));
   assert(s == expected);
 }
 
 template <typename S>
-void test() {
+TEST_CONSTEXPR_CXX26 void test() {
   auto is1   = [](auto v) { return v == 1; };
   auto is2   = [](auto v) { return v == 2; };
   auto is3   = [](auto v) { return v == 3; };
@@ -64,7 +64,7 @@ void test() {
   test0(S({1, 2, 3}), False, S({1, 2, 3}), 0);
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test<std::multiset<int>>();
   test<std::multiset<int, std::less<int>, min_allocator<int>>>();
   test<std::multiset<int, std::less<int>, test_allocator<int>>>();
@@ -72,5 +72,13 @@ int main(int, char**) {
   test<std::multiset<long>>();
   test<std::multiset<double>>();
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
index fb575c880dd2e..ae3581104677c 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
@@ -13,15 +13,22 @@
 
 // template<class Key, class Compare, class Allocator>
 //   synth-three-way-result<Key> operator<=>(const multiset<Key, Compare, Allocator>& x,
-//                                           const multiset<Key, Compare, Allocator>& y);
+//                                           const multiset<Key, Compare, Allocator>& y); // constexpr since C++26
 
 #include <cassert>
 #include <set>
 
 #include "test_container_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   assert(test_ordered_set_container_spaceship<std::multiset>());
-  // `std::multiset` is not constexpr, so no `static_assert` test here.
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
index 9ffb5dc3593c5..1d5df25bebfdc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/op_compare.pass.cpp
@@ -38,7 +38,7 @@
 
 #include "test_comparisons.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     std::multiset<int> s1, s2;
     s1.insert(1);
@@ -70,5 +70,13 @@ int main(int, char**) {
     const std::multiset<int>&cs1 = s1, cs2 = s2;
     assert(testComparisons(cs1, cs2, false, true));
   }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
index 63647a052c041..1908991e40eed 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.observers/comp.pass.cpp
@@ -8,13 +8,15 @@
 
 // <set>
 
-// key_compare key_comp() const;
-// value_compare value_comp() const;
+// key_compare key_comp() const; // constexpr since C++26
+// value_compare value_comp() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
 
-int main(int, char**) {
+#include "test_macros.h"
+
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef std::multiset<int> set_type;
 
   set_type s;
@@ -29,5 +31,13 @@ int main(int, char**) {
   assert(cs.value_comp()(*i1, *i2));
   assert(!cs.value_comp()(*i2, *i1));
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
index af32e1882093d..997fe1d818b53 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// void swap(multiset& m);
+// void swap(multiset& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -110,5 +110,13 @@ int main(int, char**) {
   }
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
index 53630b3ca0a01..3ab2e1fbf55ce 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/non_member_swap.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// void swap(multiset& m);
+// void swap(multiset& m); // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_allocator.h"
 #include "../../../test_compare.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   typedef int V;
   {
     typedef std::multiset<int> M;
@@ -100,5 +100,13 @@ int main(int, char**) {
     assert(m2.get_allocator() == A(1));
   }
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
index 7a7ccf87e9de9..37bdae66ee8e9 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.special/swap_noexcept.pass.cpp
@@ -12,11 +12,11 @@
 
 // void swap(multiset& c)
 //     noexcept(!allocator_type::propagate_on_container_swap::value ||
-//              __is_nothrow_swappable<allocator_type>::value);
+//              __is_nothrow_swappable<allocator_type>::value); // constexpr since C++26
 //
 //  In C++17, the standard says that swap shall have:
 //     noexcept(allocator_traits<Allocator>::is_always_equal::value &&
-//              noexcept(swap(declval<Compare&>(), declval<Compare&>())));
+//              noexcept(swap(declval<Compare&>(), declval<Compare&>()))); // constexpr since C++26
 
 // This tests a conforming extension
 
@@ -86,7 +86,7 @@ struct some_alloc3 {
   typedef std::false_type is_always_equal;
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<MoveOnly> C;
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
@@ -131,5 +131,13 @@ int main(int, char**) {
 #  endif // _LIBCPP_VERSION
 #endif
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/size.pass.cpp b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
index 405437211b8ca..4fe326b013fd4 100644
--- a/libcxx/test/std/containers/associative/multiset/size.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/size.pass.cpp
@@ -10,7 +10,7 @@
 
 // class multiset
 
-// size_type size() const;
+// size_type size() const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef std::multiset<int> M;
     M m;
@@ -55,6 +55,13 @@ int main(int, char**) {
     assert(m.size() == 0);
   }
 #endif
+  return true;
+}
 
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp b/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp
index c61859b1feffb..4e580d4318a71 100644
--- a/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/upper_bound.pass.cpp
@@ -10,8 +10,8 @@
 
 // class multiset
 
-//       iterator upper_bound(const key_type& k);
-// const_iterator upper_bound(const key_type& k) const;
+//       iterator upper_bound(const key_type& k); // constexpr since C++26
+// const_iterator upper_bound(const key_type& k) const; // constexpr since C++26
 
 #include <set>
 #include <cassert>
@@ -20,7 +20,7 @@
 #include "min_allocator.h"
 #include "private_constructor.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   {
     typedef int V;
     typedef std::multiset<int> M;
@@ -163,6 +163,13 @@ int main(int, char**) {
     assert(r == std::next(m.begin(), 9));
   }
 #endif
+  return true;
+}
 
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
   return 0;
 }


        

From libcxx-commits at lists.llvm.org  Sun Jul  5 19:33:07 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 05 Jul 2026 19:33:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make `<set>` `std::multiset`
 constexpr as part of P3372R3 (PR #206257)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206257@github.com>
Message-ID: <6a4b13e3.7270d2fd.23dbb3.0b6e@mx.google.com>


https://github.com/frederick-vs-ja closed https://github.com/llvm/llvm-project/pull/206257

From libcxx-commits at lists.llvm.org  Sun Jul  5 21:36:00 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Sun, 05 Jul 2026 21:36:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Add deleted tuple
 constructor overloads (PR #205379)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205379@github.com>
Message-ID: <6a4b30b0.cbc3f03a.33a4cc.e6e0@mx.google.com>


yronglin wrote:

@frederick-vs-ja @ldionne Hi experts, do you know how to update the clang-23 version in CI? Seems it's currently bumped to 26th April commit: https://github.com/llvm/llvm-project/commit/486e97a19f8fd34ce29e12a9de6e7033e93998d8, but this PR needs https://github.com/llvm/llvm-project/commit/fc1565f89eaff7b4594186793cbb04862660ecff.

https://github.com/llvm/llvm-project/pull/205379

From libcxx-commits at lists.llvm.org  Sun Jul  5 22:27:19 2026
From: libcxx-commits at lists.llvm.org (Michele Scandale via libcxx-commits)
Date: Sun, 05 Jul 2026 22:27:19 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] Fix vector types w.r.t. qualified
	element type. (PR #197624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/197624@github.com>
Message-ID: <6a4b3cb7.dd2b4bd3.13e95e.fa85@mx.google.com>


michele-scandale wrote:

Ping

https://github.com/llvm/llvm-project/pull/197624

From libcxx-commits at lists.llvm.org  Sun Jul  5 22:32:39 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 05 Jul 2026 22:32:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a4b3df7.491d90f5.1f0d58.3b8f@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/207120

>From 83e34236209bc1f131a8b52c2b9c0f36ac183c21 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 2 Jul 2026 08:16:28 +0300
Subject: [PATCH 1/2] [libc++][ranges] Applied `[[nodiscard]]` to
 `views::zip_transform`

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.zip.transform

Towards #172124
---
 libcxx/include/__ranges/zip_transform_view.h  | 33 +++----
 .../range.zip.transform/nodiscard.verify.cpp  | 88 +++++++++++++++++++
 2 files changed, 105 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
index c56632a2608a1..ffa3e9f9c6dd2 100644
--- a/libcxx/include/__ranges/zip_transform_view.h
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -79,15 +79,15 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views)
       : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     return __iterator<true>(*this, __zip_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __zip_.end());
     } else {
@@ -95,7 +95,7 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -105,13 +105,13 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __zip_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __zip_.size();
@@ -184,7 +184,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) {
     return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_));
   }
@@ -233,7 +233,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -255,25 +255,26 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -307,14 +308,14 @@ class zip_transform_view<_Fn, _Views...>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -327,14 +328,14 @@ struct __fn {
   template <class _Fn>
     requires(move_constructible<decay_t<_Fn>> && regular_invocable<decay_t<_Fn>&> &&
              is_object_v<invoke_result_t<decay_t<_Fn>&>>)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
       noexcept(noexcept(auto(views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>))) {
     return views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>;
   }
 
   template <class _Fn, class... _Ranges>
     requires(sizeof...(_Ranges) > 0)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
       noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)))
           -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) {
     return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7d15d027f75be
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it  = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.zip_transform.sentinel]
+
+  auto st = ztv.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // [range.zip_transform.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform(Invocable<>{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform([](int x) { return x; }, range);
+}
\ No newline at end of file

>From 7595d99fcdce5c0f4856027608807187fd2a3713 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:32:19 +0300
Subject: [PATCH 2/2] Try to fix

---
 .../range.zip.transform/nodiscard.verify.cpp  | 124 ++++++++++++------
 .../range.adaptors/range_adaptor_types.h      |  27 ----
 libcxx/test/support/test_iterators.h          |  30 ++++-
 3 files changed, 112 insertions(+), 69 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 7d15d027f75be..0a85c8802011a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -14,17 +14,40 @@
 #include <utility>
 #include <vector>
 
-struct View : std::ranges::view_interface<View> {
+#include "test_iterators.h"
+
+struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
-  const int* begin() const;
-  volatile int* end();
-  const volatile int* end() const;
+  int* begin() const;
+  int* end();
+  int* end() const;
 
   int size() const;
 };
-static_assert(!std::ranges::common_range<View>);
-static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
-static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
+static_assert(std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
+
+struct ForwardSizedNonCommon {
+  int* buffer_      = nullptr;
+  std::size_t size_ = 0;
+
+  template <std::size_t N>
+  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
+
+  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
+
+  using iterator = forward_sized_iterator<int*>;
+  using sentinel = sized_sentinel<iterator>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
+};
+static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
+// static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {
@@ -32,51 +55,70 @@ struct Invocable {
 };
 
 void test() {
-  View range;
-  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+  CommonView commonRange;
+  std::ranges::zip_transform_view commonZtv{[](int x) { return x; }, commonRange};
+  static_assert(std::same_as<decltype(commonZtv.end()), decltype(commonZtv.begin())>);
 
   // [range.zip_transform.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.begin();
+  commonZtv.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).begin();
+  std::as_const(commonZtv).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.end();
+  commonZtv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).end();
+  std::as_const(commonZtv).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.size();
+  commonZtv.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).size();
+  std::as_const(commonZtv).size();
 
   // [range.zip_transform.iterator]
-
-  auto it  = ztv.begin();
-  auto cIt = std::as_const(ztv).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *cIt;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  cIt[0];
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  1 + it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - it;
+  {
+    auto it  = commonZtv.begin();
+    auto cIt = std::as_const(commonZtv).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *cIt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt[0];
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+  }
 
   // [range.zip_transform.sentinel]
-
-  auto st = ztv.end();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - st;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - it;
+  {
+    auto MakeTuple = [](auto&&... args) { return std::tuple(std::forward<decltype(args)>(args)...); };
+    int arr[]      = {1, 2, 3};
+    std::ranges::zip_transform_view nonCommonZtv{MakeTuple, std::views::iota(0, 3), ForwardSizedNonCommon(arr)};
+    static_assert(!std::ranges::common_range<decltype(nonCommonZtv)>);
+    static_assert(!std::same_as<decltype(nonCommonZtv.end()), decltype(nonCommonZtv.begin())>);
+    auto it  = nonCommonZtv.begin();
+    auto cIt = std::as_const(nonCommonZtv).begin();
+    auto st  = nonCommonZtv.end();
+    auto cSt = std::as_const(nonCommonZtv).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - cSt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cSt - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - cIt;
+  }
 
   // [range.zip_transform.overview]
 
@@ -84,5 +126,5 @@ void test() {
   std::views::zip_transform(Invocable<>{});
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::zip_transform([](int x) { return x; }, range);
-}
\ No newline at end of file
+  std::views::zip_transform([](int x) { return x; }, commonRange);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 0b654566e35b8..429c94e254303 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,33 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-template <class Base = int*>
-struct forward_sized_iterator {
-  Base it_ = nullptr;
-
-  using iterator_category = std::forward_iterator_tag;
-  using value_type        = int;
-  using difference_type   = std::intptr_t;
-  using pointer           = Base;
-  using reference         = decltype(*Base{});
-
-  forward_sized_iterator() = default;
-  constexpr forward_sized_iterator(Base it) : it_(it) {}
-
-  constexpr reference operator*() const { return *it_; }
-
-  constexpr forward_sized_iterator& operator++() {
-    ++it_;
-    return *this;
-  }
-  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
-
-  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
-
-  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
-    return x.it_ - y.it_;
-  }
-};
 static_assert(std::forward_iterator<forward_sized_iterator<>>);
 static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 4fc8345c2dcef..6a721a4feaf9f 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,6 +1175,35 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
+
+template <class Base = int*>
+struct forward_sized_iterator {
+  Base it_ = nullptr;
+
+  using iterator_category = std::forward_iterator_tag;
+  using value_type        = int;
+  using difference_type   = std::intptr_t;
+  using pointer           = Base;
+  using reference         = decltype(*Base{});
+
+  forward_sized_iterator() = default;
+  constexpr forward_sized_iterator(Base it) : it_(it) {}
+
+  constexpr reference operator*() const { return *it_; }
+
+  constexpr forward_sized_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
+
+  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
+    return x.it_ - y.it_;
+  }
+};
+
 namespace adl {
 
 class Iterator {
@@ -1880,5 +1909,4 @@ using cpp20_input_iterator_list =
 #endif
 } // namespace types
 
-
 #endif // SUPPORT_TEST_ITERATORS_H


From libcxx-commits at lists.llvm.org  Sun Jul  5 22:33:31 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 05 Jul 2026 22:33:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a4b3e2b.92d2873c.38c951.146d@mx.google.com>


https://github.com/Zingam updated https://github.com/llvm/llvm-project/pull/207120

>From 83e34236209bc1f131a8b52c2b9c0f36ac183c21 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 2 Jul 2026 08:16:28 +0300
Subject: [PATCH 1/2] [libc++][ranges] Applied `[[nodiscard]]` to
 `views::zip_transform`

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.zip.transform

Towards #172124
---
 libcxx/include/__ranges/zip_transform_view.h  | 33 +++----
 .../range.zip.transform/nodiscard.verify.cpp  | 88 +++++++++++++++++++
 2 files changed, 105 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
index c56632a2608a1..ffa3e9f9c6dd2 100644
--- a/libcxx/include/__ranges/zip_transform_view.h
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -79,15 +79,15 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views)
       : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     return __iterator<true>(*this, __zip_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __zip_.end());
     } else {
@@ -95,7 +95,7 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -105,13 +105,13 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __zip_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __zip_.size();
@@ -184,7 +184,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) {
     return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_));
   }
@@ -233,7 +233,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -255,25 +255,26 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -307,14 +308,14 @@ class zip_transform_view<_Fn, _Views...>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -327,14 +328,14 @@ struct __fn {
   template <class _Fn>
     requires(move_constructible<decay_t<_Fn>> && regular_invocable<decay_t<_Fn>&> &&
              is_object_v<invoke_result_t<decay_t<_Fn>&>>)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
       noexcept(noexcept(auto(views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>))) {
     return views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>;
   }
 
   template <class _Fn, class... _Ranges>
     requires(sizeof...(_Ranges) > 0)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
       noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)))
           -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) {
     return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7d15d027f75be
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it  = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.zip_transform.sentinel]
+
+  auto st = ztv.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // [range.zip_transform.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform(Invocable<>{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform([](int x) { return x; }, range);
+}
\ No newline at end of file

>From 7595d99fcdce5c0f4856027608807187fd2a3713 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:32:19 +0300
Subject: [PATCH 2/2] Try to fix

---
 .../range.zip.transform/nodiscard.verify.cpp  | 124 ++++++++++++------
 .../range.adaptors/range_adaptor_types.h      |  27 ----
 libcxx/test/support/test_iterators.h          |  30 ++++-
 3 files changed, 112 insertions(+), 69 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 7d15d027f75be..0a85c8802011a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -14,17 +14,40 @@
 #include <utility>
 #include <vector>
 
-struct View : std::ranges::view_interface<View> {
+#include "test_iterators.h"
+
+struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
-  const int* begin() const;
-  volatile int* end();
-  const volatile int* end() const;
+  int* begin() const;
+  int* end();
+  int* end() const;
 
   int size() const;
 };
-static_assert(!std::ranges::common_range<View>);
-static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
-static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
+static_assert(std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
+
+struct ForwardSizedNonCommon {
+  int* buffer_      = nullptr;
+  std::size_t size_ = 0;
+
+  template <std::size_t N>
+  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
+
+  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
+
+  using iterator = forward_sized_iterator<int*>;
+  using sentinel = sized_sentinel<iterator>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
+};
+static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
+// static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {
@@ -32,51 +55,70 @@ struct Invocable {
 };
 
 void test() {
-  View range;
-  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+  CommonView commonRange;
+  std::ranges::zip_transform_view commonZtv{[](int x) { return x; }, commonRange};
+  static_assert(std::same_as<decltype(commonZtv.end()), decltype(commonZtv.begin())>);
 
   // [range.zip_transform.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.begin();
+  commonZtv.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).begin();
+  std::as_const(commonZtv).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.end();
+  commonZtv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).end();
+  std::as_const(commonZtv).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.size();
+  commonZtv.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).size();
+  std::as_const(commonZtv).size();
 
   // [range.zip_transform.iterator]
-
-  auto it  = ztv.begin();
-  auto cIt = std::as_const(ztv).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *cIt;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  cIt[0];
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  1 + it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - it;
+  {
+    auto it  = commonZtv.begin();
+    auto cIt = std::as_const(commonZtv).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *cIt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt[0];
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+  }
 
   // [range.zip_transform.sentinel]
-
-  auto st = ztv.end();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - st;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - it;
+  {
+    auto MakeTuple = [](auto&&... args) { return std::tuple(std::forward<decltype(args)>(args)...); };
+    int arr[]      = {1, 2, 3};
+    std::ranges::zip_transform_view nonCommonZtv{MakeTuple, std::views::iota(0, 3), ForwardSizedNonCommon(arr)};
+    static_assert(!std::ranges::common_range<decltype(nonCommonZtv)>);
+    static_assert(!std::same_as<decltype(nonCommonZtv.end()), decltype(nonCommonZtv.begin())>);
+    auto it  = nonCommonZtv.begin();
+    auto cIt = std::as_const(nonCommonZtv).begin();
+    auto st  = nonCommonZtv.end();
+    auto cSt = std::as_const(nonCommonZtv).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - cSt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cSt - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - cIt;
+  }
 
   // [range.zip_transform.overview]
 
@@ -84,5 +126,5 @@ void test() {
   std::views::zip_transform(Invocable<>{});
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::zip_transform([](int x) { return x; }, range);
-}
\ No newline at end of file
+  std::views::zip_transform([](int x) { return x; }, commonRange);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 0b654566e35b8..429c94e254303 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,33 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-template <class Base = int*>
-struct forward_sized_iterator {
-  Base it_ = nullptr;
-
-  using iterator_category = std::forward_iterator_tag;
-  using value_type        = int;
-  using difference_type   = std::intptr_t;
-  using pointer           = Base;
-  using reference         = decltype(*Base{});
-
-  forward_sized_iterator() = default;
-  constexpr forward_sized_iterator(Base it) : it_(it) {}
-
-  constexpr reference operator*() const { return *it_; }
-
-  constexpr forward_sized_iterator& operator++() {
-    ++it_;
-    return *this;
-  }
-  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
-
-  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
-
-  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
-    return x.it_ - y.it_;
-  }
-};
 static_assert(std::forward_iterator<forward_sized_iterator<>>);
 static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 4fc8345c2dcef..6a721a4feaf9f 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,6 +1175,35 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
+
+template <class Base = int*>
+struct forward_sized_iterator {
+  Base it_ = nullptr;
+
+  using iterator_category = std::forward_iterator_tag;
+  using value_type        = int;
+  using difference_type   = std::intptr_t;
+  using pointer           = Base;
+  using reference         = decltype(*Base{});
+
+  forward_sized_iterator() = default;
+  constexpr forward_sized_iterator(Base it) : it_(it) {}
+
+  constexpr reference operator*() const { return *it_; }
+
+  constexpr forward_sized_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
+
+  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
+    return x.it_ - y.it_;
+  }
+};
+
 namespace adl {
 
 class Iterator {
@@ -1880,5 +1909,4 @@ using cpp20_input_iterator_list =
 #endif
 } // namespace types
 
-
 #endif // SUPPORT_TEST_ITERATORS_H


From libcxx-commits at lists.llvm.org  Sun Jul  5 22:35:39 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 05 Jul 2026 22:35:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a4b3eab.ac469dc7.ed218.0840@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/207120

>From 83e34236209bc1f131a8b52c2b9c0f36ac183c21 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 2 Jul 2026 08:16:28 +0300
Subject: [PATCH 1/3] [libc++][ranges] Applied `[[nodiscard]]` to
 `views::zip_transform`

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.zip.transform

Towards #172124
---
 libcxx/include/__ranges/zip_transform_view.h  | 33 +++----
 .../range.zip.transform/nodiscard.verify.cpp  | 88 +++++++++++++++++++
 2 files changed, 105 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
index c56632a2608a1..ffa3e9f9c6dd2 100644
--- a/libcxx/include/__ranges/zip_transform_view.h
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -79,15 +79,15 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views)
       : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     return __iterator<true>(*this, __zip_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __zip_.end());
     } else {
@@ -95,7 +95,7 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -105,13 +105,13 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __zip_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __zip_.size();
@@ -184,7 +184,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) {
     return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_));
   }
@@ -233,7 +233,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -255,25 +255,26 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -307,14 +308,14 @@ class zip_transform_view<_Fn, _Views...>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -327,14 +328,14 @@ struct __fn {
   template <class _Fn>
     requires(move_constructible<decay_t<_Fn>> && regular_invocable<decay_t<_Fn>&> &&
              is_object_v<invoke_result_t<decay_t<_Fn>&>>)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
       noexcept(noexcept(auto(views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>))) {
     return views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>;
   }
 
   template <class _Fn, class... _Ranges>
     requires(sizeof...(_Ranges) > 0)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
       noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)))
           -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) {
     return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7d15d027f75be
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it  = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.zip_transform.sentinel]
+
+  auto st = ztv.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // [range.zip_transform.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform(Invocable<>{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform([](int x) { return x; }, range);
+}
\ No newline at end of file

>From 7595d99fcdce5c0f4856027608807187fd2a3713 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:32:19 +0300
Subject: [PATCH 2/3] Try to fix

---
 .../range.zip.transform/nodiscard.verify.cpp  | 124 ++++++++++++------
 .../range.adaptors/range_adaptor_types.h      |  27 ----
 libcxx/test/support/test_iterators.h          |  30 ++++-
 3 files changed, 112 insertions(+), 69 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 7d15d027f75be..0a85c8802011a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -14,17 +14,40 @@
 #include <utility>
 #include <vector>
 
-struct View : std::ranges::view_interface<View> {
+#include "test_iterators.h"
+
+struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
-  const int* begin() const;
-  volatile int* end();
-  const volatile int* end() const;
+  int* begin() const;
+  int* end();
+  int* end() const;
 
   int size() const;
 };
-static_assert(!std::ranges::common_range<View>);
-static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
-static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
+static_assert(std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
+
+struct ForwardSizedNonCommon {
+  int* buffer_      = nullptr;
+  std::size_t size_ = 0;
+
+  template <std::size_t N>
+  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
+
+  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
+
+  using iterator = forward_sized_iterator<int*>;
+  using sentinel = sized_sentinel<iterator>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
+};
+static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
+// static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {
@@ -32,51 +55,70 @@ struct Invocable {
 };
 
 void test() {
-  View range;
-  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+  CommonView commonRange;
+  std::ranges::zip_transform_view commonZtv{[](int x) { return x; }, commonRange};
+  static_assert(std::same_as<decltype(commonZtv.end()), decltype(commonZtv.begin())>);
 
   // [range.zip_transform.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.begin();
+  commonZtv.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).begin();
+  std::as_const(commonZtv).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.end();
+  commonZtv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).end();
+  std::as_const(commonZtv).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.size();
+  commonZtv.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).size();
+  std::as_const(commonZtv).size();
 
   // [range.zip_transform.iterator]
-
-  auto it  = ztv.begin();
-  auto cIt = std::as_const(ztv).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *cIt;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  cIt[0];
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  1 + it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - it;
+  {
+    auto it  = commonZtv.begin();
+    auto cIt = std::as_const(commonZtv).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *cIt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt[0];
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+  }
 
   // [range.zip_transform.sentinel]
-
-  auto st = ztv.end();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - st;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - it;
+  {
+    auto MakeTuple = [](auto&&... args) { return std::tuple(std::forward<decltype(args)>(args)...); };
+    int arr[]      = {1, 2, 3};
+    std::ranges::zip_transform_view nonCommonZtv{MakeTuple, std::views::iota(0, 3), ForwardSizedNonCommon(arr)};
+    static_assert(!std::ranges::common_range<decltype(nonCommonZtv)>);
+    static_assert(!std::same_as<decltype(nonCommonZtv.end()), decltype(nonCommonZtv.begin())>);
+    auto it  = nonCommonZtv.begin();
+    auto cIt = std::as_const(nonCommonZtv).begin();
+    auto st  = nonCommonZtv.end();
+    auto cSt = std::as_const(nonCommonZtv).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - cSt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cSt - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - cIt;
+  }
 
   // [range.zip_transform.overview]
 
@@ -84,5 +126,5 @@ void test() {
   std::views::zip_transform(Invocable<>{});
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::zip_transform([](int x) { return x; }, range);
-}
\ No newline at end of file
+  std::views::zip_transform([](int x) { return x; }, commonRange);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 0b654566e35b8..429c94e254303 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,33 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-template <class Base = int*>
-struct forward_sized_iterator {
-  Base it_ = nullptr;
-
-  using iterator_category = std::forward_iterator_tag;
-  using value_type        = int;
-  using difference_type   = std::intptr_t;
-  using pointer           = Base;
-  using reference         = decltype(*Base{});
-
-  forward_sized_iterator() = default;
-  constexpr forward_sized_iterator(Base it) : it_(it) {}
-
-  constexpr reference operator*() const { return *it_; }
-
-  constexpr forward_sized_iterator& operator++() {
-    ++it_;
-    return *this;
-  }
-  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
-
-  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
-
-  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
-    return x.it_ - y.it_;
-  }
-};
 static_assert(std::forward_iterator<forward_sized_iterator<>>);
 static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 4fc8345c2dcef..6a721a4feaf9f 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,6 +1175,35 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
+
+template <class Base = int*>
+struct forward_sized_iterator {
+  Base it_ = nullptr;
+
+  using iterator_category = std::forward_iterator_tag;
+  using value_type        = int;
+  using difference_type   = std::intptr_t;
+  using pointer           = Base;
+  using reference         = decltype(*Base{});
+
+  forward_sized_iterator() = default;
+  constexpr forward_sized_iterator(Base it) : it_(it) {}
+
+  constexpr reference operator*() const { return *it_; }
+
+  constexpr forward_sized_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
+
+  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
+    return x.it_ - y.it_;
+  }
+};
+
 namespace adl {
 
 class Iterator {
@@ -1880,5 +1909,4 @@ using cpp20_input_iterator_list =
 #endif
 } // namespace types
 
-
 #endif // SUPPORT_TEST_ITERATORS_H

>From b21a63ee3ba7f9571091d219f0ff6b939658eebb Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:34:11 +0300
Subject: [PATCH 3/3] FIx formatting

---
 libcxx/test/support/test_iterators.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 6a721a4feaf9f..a75cb578b2e91 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,7 +1175,6 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
-
 template <class Base = int*>
 struct forward_sized_iterator {
   Base it_ = nullptr;


From libcxx-commits at lists.llvm.org  Sun Jul  5 22:36:11 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 22:36:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4b3ecb.f8b79514.3b441.5c86@mx.google.com>


https://github.com/Himadhith edited https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Sun Jul  5 22:37:28 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 05 Jul 2026 22:37:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a4b3f18.331483e7.5bc1e.ec56@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/207120

>From 83e34236209bc1f131a8b52c2b9c0f36ac183c21 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 2 Jul 2026 08:16:28 +0300
Subject: [PATCH 1/4] [libc++][ranges] Applied `[[nodiscard]]` to
 `views::zip_transform`

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.zip.transform

Towards #172124
---
 libcxx/include/__ranges/zip_transform_view.h  | 33 +++----
 .../range.zip.transform/nodiscard.verify.cpp  | 88 +++++++++++++++++++
 2 files changed, 105 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
index c56632a2608a1..ffa3e9f9c6dd2 100644
--- a/libcxx/include/__ranges/zip_transform_view.h
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -79,15 +79,15 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views)
       : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     return __iterator<true>(*this, __zip_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __zip_.end());
     } else {
@@ -95,7 +95,7 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -105,13 +105,13 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __zip_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __zip_.size();
@@ -184,7 +184,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) {
     return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_));
   }
@@ -233,7 +233,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -255,25 +255,26 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -307,14 +308,14 @@ class zip_transform_view<_Fn, _Views...>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -327,14 +328,14 @@ struct __fn {
   template <class _Fn>
     requires(move_constructible<decay_t<_Fn>> && regular_invocable<decay_t<_Fn>&> &&
              is_object_v<invoke_result_t<decay_t<_Fn>&>>)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
       noexcept(noexcept(auto(views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>))) {
     return views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>;
   }
 
   template <class _Fn, class... _Ranges>
     requires(sizeof...(_Ranges) > 0)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
       noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)))
           -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) {
     return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7d15d027f75be
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it  = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.zip_transform.sentinel]
+
+  auto st = ztv.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // [range.zip_transform.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform(Invocable<>{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform([](int x) { return x; }, range);
+}
\ No newline at end of file

>From 7595d99fcdce5c0f4856027608807187fd2a3713 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:32:19 +0300
Subject: [PATCH 2/4] Try to fix

---
 .../range.zip.transform/nodiscard.verify.cpp  | 124 ++++++++++++------
 .../range.adaptors/range_adaptor_types.h      |  27 ----
 libcxx/test/support/test_iterators.h          |  30 ++++-
 3 files changed, 112 insertions(+), 69 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 7d15d027f75be..0a85c8802011a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -14,17 +14,40 @@
 #include <utility>
 #include <vector>
 
-struct View : std::ranges::view_interface<View> {
+#include "test_iterators.h"
+
+struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
-  const int* begin() const;
-  volatile int* end();
-  const volatile int* end() const;
+  int* begin() const;
+  int* end();
+  int* end() const;
 
   int size() const;
 };
-static_assert(!std::ranges::common_range<View>);
-static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
-static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
+static_assert(std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
+
+struct ForwardSizedNonCommon {
+  int* buffer_      = nullptr;
+  std::size_t size_ = 0;
+
+  template <std::size_t N>
+  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
+
+  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
+
+  using iterator = forward_sized_iterator<int*>;
+  using sentinel = sized_sentinel<iterator>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
+};
+static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
+// static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {
@@ -32,51 +55,70 @@ struct Invocable {
 };
 
 void test() {
-  View range;
-  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+  CommonView commonRange;
+  std::ranges::zip_transform_view commonZtv{[](int x) { return x; }, commonRange};
+  static_assert(std::same_as<decltype(commonZtv.end()), decltype(commonZtv.begin())>);
 
   // [range.zip_transform.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.begin();
+  commonZtv.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).begin();
+  std::as_const(commonZtv).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.end();
+  commonZtv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).end();
+  std::as_const(commonZtv).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.size();
+  commonZtv.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).size();
+  std::as_const(commonZtv).size();
 
   // [range.zip_transform.iterator]
-
-  auto it  = ztv.begin();
-  auto cIt = std::as_const(ztv).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *cIt;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  cIt[0];
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  1 + it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - it;
+  {
+    auto it  = commonZtv.begin();
+    auto cIt = std::as_const(commonZtv).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *cIt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt[0];
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+  }
 
   // [range.zip_transform.sentinel]
-
-  auto st = ztv.end();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - st;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - it;
+  {
+    auto MakeTuple = [](auto&&... args) { return std::tuple(std::forward<decltype(args)>(args)...); };
+    int arr[]      = {1, 2, 3};
+    std::ranges::zip_transform_view nonCommonZtv{MakeTuple, std::views::iota(0, 3), ForwardSizedNonCommon(arr)};
+    static_assert(!std::ranges::common_range<decltype(nonCommonZtv)>);
+    static_assert(!std::same_as<decltype(nonCommonZtv.end()), decltype(nonCommonZtv.begin())>);
+    auto it  = nonCommonZtv.begin();
+    auto cIt = std::as_const(nonCommonZtv).begin();
+    auto st  = nonCommonZtv.end();
+    auto cSt = std::as_const(nonCommonZtv).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - cSt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cSt - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - cIt;
+  }
 
   // [range.zip_transform.overview]
 
@@ -84,5 +126,5 @@ void test() {
   std::views::zip_transform(Invocable<>{});
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::zip_transform([](int x) { return x; }, range);
-}
\ No newline at end of file
+  std::views::zip_transform([](int x) { return x; }, commonRange);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 0b654566e35b8..429c94e254303 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,33 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-template <class Base = int*>
-struct forward_sized_iterator {
-  Base it_ = nullptr;
-
-  using iterator_category = std::forward_iterator_tag;
-  using value_type        = int;
-  using difference_type   = std::intptr_t;
-  using pointer           = Base;
-  using reference         = decltype(*Base{});
-
-  forward_sized_iterator() = default;
-  constexpr forward_sized_iterator(Base it) : it_(it) {}
-
-  constexpr reference operator*() const { return *it_; }
-
-  constexpr forward_sized_iterator& operator++() {
-    ++it_;
-    return *this;
-  }
-  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
-
-  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
-
-  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
-    return x.it_ - y.it_;
-  }
-};
 static_assert(std::forward_iterator<forward_sized_iterator<>>);
 static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 4fc8345c2dcef..6a721a4feaf9f 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,6 +1175,35 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
+
+template <class Base = int*>
+struct forward_sized_iterator {
+  Base it_ = nullptr;
+
+  using iterator_category = std::forward_iterator_tag;
+  using value_type        = int;
+  using difference_type   = std::intptr_t;
+  using pointer           = Base;
+  using reference         = decltype(*Base{});
+
+  forward_sized_iterator() = default;
+  constexpr forward_sized_iterator(Base it) : it_(it) {}
+
+  constexpr reference operator*() const { return *it_; }
+
+  constexpr forward_sized_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
+
+  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
+    return x.it_ - y.it_;
+  }
+};
+
 namespace adl {
 
 class Iterator {
@@ -1880,5 +1909,4 @@ using cpp20_input_iterator_list =
 #endif
 } // namespace types
 
-
 #endif // SUPPORT_TEST_ITERATORS_H

>From b21a63ee3ba7f9571091d219f0ff6b939658eebb Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:34:11 +0300
Subject: [PATCH 3/4] FIx formatting

---
 libcxx/test/support/test_iterators.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 6a721a4feaf9f..a75cb578b2e91 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,7 +1175,6 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
-
 template <class Base = int*>
 struct forward_sized_iterator {
   Base it_ = nullptr;

>From edf859970903a239119d1a4c537c41b1e6a9d7e4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:37:10 +0300
Subject: [PATCH 4/4] Try to fix

---
 libcxx/test/support/test_iterators.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index a75cb578b2e91..f443abcddb3bb 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1202,6 +1202,8 @@ struct forward_sized_iterator {
     return x.it_ - y.it_;
   }
 };
+static_assert(std::forward_iterator<forward_sized_iterator<>>);
+static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
 namespace adl {
 


From libcxx-commits at lists.llvm.org  Sun Jul  5 22:38:28 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 05 Jul 2026 22:38:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a4b3f54.27b3b003.1610ae.3924@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/207120

>From 83e34236209bc1f131a8b52c2b9c0f36ac183c21 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 2 Jul 2026 08:16:28 +0300
Subject: [PATCH 1/5] [libc++][ranges] Applied `[[nodiscard]]` to
 `views::zip_transform`

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.zip.transform

Towards #172124
---
 libcxx/include/__ranges/zip_transform_view.h  | 33 +++----
 .../range.zip.transform/nodiscard.verify.cpp  | 88 +++++++++++++++++++
 2 files changed, 105 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
index c56632a2608a1..ffa3e9f9c6dd2 100644
--- a/libcxx/include/__ranges/zip_transform_view.h
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -79,15 +79,15 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views)
       : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     return __iterator<true>(*this, __zip_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __zip_.end());
     } else {
@@ -95,7 +95,7 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -105,13 +105,13 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __zip_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __zip_.size();
@@ -184,7 +184,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) {
     return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_));
   }
@@ -233,7 +233,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -255,25 +255,26 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -307,14 +308,14 @@ class zip_transform_view<_Fn, _Views...>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -327,14 +328,14 @@ struct __fn {
   template <class _Fn>
     requires(move_constructible<decay_t<_Fn>> && regular_invocable<decay_t<_Fn>&> &&
              is_object_v<invoke_result_t<decay_t<_Fn>&>>)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
       noexcept(noexcept(auto(views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>))) {
     return views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>;
   }
 
   template <class _Fn, class... _Ranges>
     requires(sizeof...(_Ranges) > 0)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
       noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)))
           -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) {
     return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7d15d027f75be
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it  = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.zip_transform.sentinel]
+
+  auto st = ztv.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // [range.zip_transform.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform(Invocable<>{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform([](int x) { return x; }, range);
+}
\ No newline at end of file

>From 7595d99fcdce5c0f4856027608807187fd2a3713 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:32:19 +0300
Subject: [PATCH 2/5] Try to fix

---
 .../range.zip.transform/nodiscard.verify.cpp  | 124 ++++++++++++------
 .../range.adaptors/range_adaptor_types.h      |  27 ----
 libcxx/test/support/test_iterators.h          |  30 ++++-
 3 files changed, 112 insertions(+), 69 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 7d15d027f75be..0a85c8802011a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -14,17 +14,40 @@
 #include <utility>
 #include <vector>
 
-struct View : std::ranges::view_interface<View> {
+#include "test_iterators.h"
+
+struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
-  const int* begin() const;
-  volatile int* end();
-  const volatile int* end() const;
+  int* begin() const;
+  int* end();
+  int* end() const;
 
   int size() const;
 };
-static_assert(!std::ranges::common_range<View>);
-static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
-static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
+static_assert(std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
+
+struct ForwardSizedNonCommon {
+  int* buffer_      = nullptr;
+  std::size_t size_ = 0;
+
+  template <std::size_t N>
+  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
+
+  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
+
+  using iterator = forward_sized_iterator<int*>;
+  using sentinel = sized_sentinel<iterator>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
+};
+static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
+// static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {
@@ -32,51 +55,70 @@ struct Invocable {
 };
 
 void test() {
-  View range;
-  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+  CommonView commonRange;
+  std::ranges::zip_transform_view commonZtv{[](int x) { return x; }, commonRange};
+  static_assert(std::same_as<decltype(commonZtv.end()), decltype(commonZtv.begin())>);
 
   // [range.zip_transform.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.begin();
+  commonZtv.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).begin();
+  std::as_const(commonZtv).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.end();
+  commonZtv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).end();
+  std::as_const(commonZtv).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.size();
+  commonZtv.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).size();
+  std::as_const(commonZtv).size();
 
   // [range.zip_transform.iterator]
-
-  auto it  = ztv.begin();
-  auto cIt = std::as_const(ztv).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *cIt;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  cIt[0];
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  1 + it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - it;
+  {
+    auto it  = commonZtv.begin();
+    auto cIt = std::as_const(commonZtv).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *cIt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt[0];
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+  }
 
   // [range.zip_transform.sentinel]
-
-  auto st = ztv.end();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - st;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - it;
+  {
+    auto MakeTuple = [](auto&&... args) { return std::tuple(std::forward<decltype(args)>(args)...); };
+    int arr[]      = {1, 2, 3};
+    std::ranges::zip_transform_view nonCommonZtv{MakeTuple, std::views::iota(0, 3), ForwardSizedNonCommon(arr)};
+    static_assert(!std::ranges::common_range<decltype(nonCommonZtv)>);
+    static_assert(!std::same_as<decltype(nonCommonZtv.end()), decltype(nonCommonZtv.begin())>);
+    auto it  = nonCommonZtv.begin();
+    auto cIt = std::as_const(nonCommonZtv).begin();
+    auto st  = nonCommonZtv.end();
+    auto cSt = std::as_const(nonCommonZtv).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - cSt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cSt - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - cIt;
+  }
 
   // [range.zip_transform.overview]
 
@@ -84,5 +126,5 @@ void test() {
   std::views::zip_transform(Invocable<>{});
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::zip_transform([](int x) { return x; }, range);
-}
\ No newline at end of file
+  std::views::zip_transform([](int x) { return x; }, commonRange);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 0b654566e35b8..429c94e254303 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,33 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-template <class Base = int*>
-struct forward_sized_iterator {
-  Base it_ = nullptr;
-
-  using iterator_category = std::forward_iterator_tag;
-  using value_type        = int;
-  using difference_type   = std::intptr_t;
-  using pointer           = Base;
-  using reference         = decltype(*Base{});
-
-  forward_sized_iterator() = default;
-  constexpr forward_sized_iterator(Base it) : it_(it) {}
-
-  constexpr reference operator*() const { return *it_; }
-
-  constexpr forward_sized_iterator& operator++() {
-    ++it_;
-    return *this;
-  }
-  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
-
-  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
-
-  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
-    return x.it_ - y.it_;
-  }
-};
 static_assert(std::forward_iterator<forward_sized_iterator<>>);
 static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 4fc8345c2dcef..6a721a4feaf9f 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,6 +1175,35 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
+
+template <class Base = int*>
+struct forward_sized_iterator {
+  Base it_ = nullptr;
+
+  using iterator_category = std::forward_iterator_tag;
+  using value_type        = int;
+  using difference_type   = std::intptr_t;
+  using pointer           = Base;
+  using reference         = decltype(*Base{});
+
+  forward_sized_iterator() = default;
+  constexpr forward_sized_iterator(Base it) : it_(it) {}
+
+  constexpr reference operator*() const { return *it_; }
+
+  constexpr forward_sized_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
+
+  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
+    return x.it_ - y.it_;
+  }
+};
+
 namespace adl {
 
 class Iterator {
@@ -1880,5 +1909,4 @@ using cpp20_input_iterator_list =
 #endif
 } // namespace types
 
-
 #endif // SUPPORT_TEST_ITERATORS_H

>From b21a63ee3ba7f9571091d219f0ff6b939658eebb Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:34:11 +0300
Subject: [PATCH 3/5] FIx formatting

---
 libcxx/test/support/test_iterators.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 6a721a4feaf9f..a75cb578b2e91 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,7 +1175,6 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
-
 template <class Base = int*>
 struct forward_sized_iterator {
   Base it_ = nullptr;

>From edf859970903a239119d1a4c537c41b1e6a9d7e4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:37:10 +0300
Subject: [PATCH 4/5] Try to fix

---
 libcxx/test/support/test_iterators.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index a75cb578b2e91..f443abcddb3bb 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1202,6 +1202,8 @@ struct forward_sized_iterator {
     return x.it_ - y.it_;
   }
 };
+static_assert(std::forward_iterator<forward_sized_iterator<>>);
+static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
 namespace adl {
 

>From 675b4611e20d7f9f482d3622ec31c0bdb23271a8 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:38:10 +0300
Subject: [PATCH 5/5] Fix again

---
 libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 429c94e254303..41ca28fa905e4 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,9 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-static_assert(std::forward_iterator<forward_sized_iterator<>>);
-static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
-
 using ForwardSizedView = BasicView<forward_sized_iterator<>>;
 static_assert(std::ranges::forward_range<ForwardSizedView>);
 static_assert(std::ranges::sized_range<ForwardSizedView>);


From libcxx-commits at lists.llvm.org  Sun Jul  5 22:39:34 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 05 Jul 2026 22:39:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a4b3f96.eb24c6ea.1f2e26.22bb@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/207120

>From 83e34236209bc1f131a8b52c2b9c0f36ac183c21 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 2 Jul 2026 08:16:28 +0300
Subject: [PATCH 1/6] [libc++][ranges] Applied `[[nodiscard]]` to
 `views::zip_transform`

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.zip.transform

Towards #172124
---
 libcxx/include/__ranges/zip_transform_view.h  | 33 +++----
 .../range.zip.transform/nodiscard.verify.cpp  | 88 +++++++++++++++++++
 2 files changed, 105 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
index c56632a2608a1..ffa3e9f9c6dd2 100644
--- a/libcxx/include/__ranges/zip_transform_view.h
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -79,15 +79,15 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views)
       : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     return __iterator<true>(*this, __zip_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __zip_.end());
     } else {
@@ -95,7 +95,7 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -105,13 +105,13 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __zip_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __zip_.size();
@@ -184,7 +184,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) {
     return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_));
   }
@@ -233,7 +233,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -255,25 +255,26 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -307,14 +308,14 @@ class zip_transform_view<_Fn, _Views...>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -327,14 +328,14 @@ struct __fn {
   template <class _Fn>
     requires(move_constructible<decay_t<_Fn>> && regular_invocable<decay_t<_Fn>&> &&
              is_object_v<invoke_result_t<decay_t<_Fn>&>>)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
       noexcept(noexcept(auto(views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>))) {
     return views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>;
   }
 
   template <class _Fn, class... _Ranges>
     requires(sizeof...(_Ranges) > 0)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
       noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)))
           -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) {
     return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7d15d027f75be
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it  = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.zip_transform.sentinel]
+
+  auto st = ztv.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // [range.zip_transform.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform(Invocable<>{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform([](int x) { return x; }, range);
+}
\ No newline at end of file

>From 7595d99fcdce5c0f4856027608807187fd2a3713 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:32:19 +0300
Subject: [PATCH 2/6] Try to fix

---
 .../range.zip.transform/nodiscard.verify.cpp  | 124 ++++++++++++------
 .../range.adaptors/range_adaptor_types.h      |  27 ----
 libcxx/test/support/test_iterators.h          |  30 ++++-
 3 files changed, 112 insertions(+), 69 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 7d15d027f75be..0a85c8802011a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -14,17 +14,40 @@
 #include <utility>
 #include <vector>
 
-struct View : std::ranges::view_interface<View> {
+#include "test_iterators.h"
+
+struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
-  const int* begin() const;
-  volatile int* end();
-  const volatile int* end() const;
+  int* begin() const;
+  int* end();
+  int* end() const;
 
   int size() const;
 };
-static_assert(!std::ranges::common_range<View>);
-static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
-static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
+static_assert(std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
+
+struct ForwardSizedNonCommon {
+  int* buffer_      = nullptr;
+  std::size_t size_ = 0;
+
+  template <std::size_t N>
+  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
+
+  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
+
+  using iterator = forward_sized_iterator<int*>;
+  using sentinel = sized_sentinel<iterator>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
+};
+static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
+// static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {
@@ -32,51 +55,70 @@ struct Invocable {
 };
 
 void test() {
-  View range;
-  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+  CommonView commonRange;
+  std::ranges::zip_transform_view commonZtv{[](int x) { return x; }, commonRange};
+  static_assert(std::same_as<decltype(commonZtv.end()), decltype(commonZtv.begin())>);
 
   // [range.zip_transform.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.begin();
+  commonZtv.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).begin();
+  std::as_const(commonZtv).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.end();
+  commonZtv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).end();
+  std::as_const(commonZtv).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.size();
+  commonZtv.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).size();
+  std::as_const(commonZtv).size();
 
   // [range.zip_transform.iterator]
-
-  auto it  = ztv.begin();
-  auto cIt = std::as_const(ztv).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *cIt;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  cIt[0];
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  1 + it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - it;
+  {
+    auto it  = commonZtv.begin();
+    auto cIt = std::as_const(commonZtv).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *cIt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt[0];
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+  }
 
   // [range.zip_transform.sentinel]
-
-  auto st = ztv.end();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - st;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - it;
+  {
+    auto MakeTuple = [](auto&&... args) { return std::tuple(std::forward<decltype(args)>(args)...); };
+    int arr[]      = {1, 2, 3};
+    std::ranges::zip_transform_view nonCommonZtv{MakeTuple, std::views::iota(0, 3), ForwardSizedNonCommon(arr)};
+    static_assert(!std::ranges::common_range<decltype(nonCommonZtv)>);
+    static_assert(!std::same_as<decltype(nonCommonZtv.end()), decltype(nonCommonZtv.begin())>);
+    auto it  = nonCommonZtv.begin();
+    auto cIt = std::as_const(nonCommonZtv).begin();
+    auto st  = nonCommonZtv.end();
+    auto cSt = std::as_const(nonCommonZtv).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - cSt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cSt - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - cIt;
+  }
 
   // [range.zip_transform.overview]
 
@@ -84,5 +126,5 @@ void test() {
   std::views::zip_transform(Invocable<>{});
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::zip_transform([](int x) { return x; }, range);
-}
\ No newline at end of file
+  std::views::zip_transform([](int x) { return x; }, commonRange);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 0b654566e35b8..429c94e254303 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,33 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-template <class Base = int*>
-struct forward_sized_iterator {
-  Base it_ = nullptr;
-
-  using iterator_category = std::forward_iterator_tag;
-  using value_type        = int;
-  using difference_type   = std::intptr_t;
-  using pointer           = Base;
-  using reference         = decltype(*Base{});
-
-  forward_sized_iterator() = default;
-  constexpr forward_sized_iterator(Base it) : it_(it) {}
-
-  constexpr reference operator*() const { return *it_; }
-
-  constexpr forward_sized_iterator& operator++() {
-    ++it_;
-    return *this;
-  }
-  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
-
-  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
-
-  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
-    return x.it_ - y.it_;
-  }
-};
 static_assert(std::forward_iterator<forward_sized_iterator<>>);
 static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 4fc8345c2dcef..6a721a4feaf9f 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,6 +1175,35 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
+
+template <class Base = int*>
+struct forward_sized_iterator {
+  Base it_ = nullptr;
+
+  using iterator_category = std::forward_iterator_tag;
+  using value_type        = int;
+  using difference_type   = std::intptr_t;
+  using pointer           = Base;
+  using reference         = decltype(*Base{});
+
+  forward_sized_iterator() = default;
+  constexpr forward_sized_iterator(Base it) : it_(it) {}
+
+  constexpr reference operator*() const { return *it_; }
+
+  constexpr forward_sized_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
+
+  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
+    return x.it_ - y.it_;
+  }
+};
+
 namespace adl {
 
 class Iterator {
@@ -1880,5 +1909,4 @@ using cpp20_input_iterator_list =
 #endif
 } // namespace types
 
-
 #endif // SUPPORT_TEST_ITERATORS_H

>From b21a63ee3ba7f9571091d219f0ff6b939658eebb Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:34:11 +0300
Subject: [PATCH 3/6] FIx formatting

---
 libcxx/test/support/test_iterators.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 6a721a4feaf9f..a75cb578b2e91 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,7 +1175,6 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
-
 template <class Base = int*>
 struct forward_sized_iterator {
   Base it_ = nullptr;

>From edf859970903a239119d1a4c537c41b1e6a9d7e4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:37:10 +0300
Subject: [PATCH 4/6] Try to fix

---
 libcxx/test/support/test_iterators.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index a75cb578b2e91..f443abcddb3bb 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1202,6 +1202,8 @@ struct forward_sized_iterator {
     return x.it_ - y.it_;
   }
 };
+static_assert(std::forward_iterator<forward_sized_iterator<>>);
+static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
 namespace adl {
 

>From 675b4611e20d7f9f482d3622ec31c0bdb23271a8 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:38:10 +0300
Subject: [PATCH 5/6] Fix again

---
 libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 429c94e254303..41ca28fa905e4 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,9 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-static_assert(std::forward_iterator<forward_sized_iterator<>>);
-static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
-
 using ForwardSizedView = BasicView<forward_sized_iterator<>>;
 static_assert(std::ranges::forward_range<ForwardSizedView>);
 static_assert(std::ranges::sized_range<ForwardSizedView>);

>From 55976947871a096595e4f623ff833db0ca37e6b1 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:39:15 +0300
Subject: [PATCH 6/6] Fix more

---
 .../range.adaptors/range.zip.transform/nodiscard.verify.cpp    | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 0a85c8802011a..354178b2e581b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <vector>
 
 #include "test_iterators.h"
+#include "test_range.h"
 
 struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
@@ -47,7 +48,7 @@ static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
 static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
 static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
 static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
-// static_assert(simple_view<ForwardSizedNonCommon>);
+static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {


From libcxx-commits at lists.llvm.org  Sun Jul  5 23:21:13 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 23:21:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4b4959.eb165e5c.113c2a.e6ab@mx.google.com>


================
@@ -164,6 +166,8 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
 
 template <class _Rep, class _Period>
 class duration {
+  static_assert(!is_const<_Rep>::value, "A duration representation can not be const-qualified");
----------------
philnik777 wrote:

Is that message worth the cost? I don't think "does not support const types" is fundamentally better than "cannot be qualified". Everybody has to pay for two class instantiations with this version, while `__is_unqualified` is just a single variable template.

https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Sun Jul  5 23:45:57 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 05 Jul 2026 23:45:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4b4f25.e9d484b9.11ad58.a093@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/206498

>From 6fc88c1fba7be7c7eb5dcb0dddd2ec12e4a75ec8 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 18:53:17 +0530
Subject: [PATCH 1/7] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. Omit the noreplace mode strings in
 __make_mdstring() on AIX, causing open() to return nullptr for these modes.
 Update the test to expect failure on AIX for all noreplace cases, since the
 platform cannot provide atomic exclusive-open semantics.

---
 libcxx/include/fstream                        | 10 ++++++++
 .../filebuf.members/open_pointer.pass.cpp     | 24 +++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b5fb65820628a..2ec5374ce463e 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -634,6 +634,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -644,6 +653,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..f7ab0239a1b18 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -75,7 +73,10 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -83,7 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 
@@ -94,7 +101,10 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -102,7 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 #  endif

>From 0715d9900e3781e298cca572f5955eb259d297ac Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 12:40:23 -0400
Subject: [PATCH 2/7] fix format and add warning

---
 libcxx/include/fstream                               |  3 +++
 .../fstreams/filebuf.members/open_pointer.pass.cpp   | 12 ++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 2ec5374ce463e..a5f1e80960054 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,6 +642,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if defined(_AIX) || defined(__TOS_AIX__)
+#        warning 'exclusive mode is unsupported on this target'
+#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index f7ab0239a1b18..627a65cac3f2f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -84,13 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#  else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#  endif
           }
         }
 
@@ -112,13 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#    else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#    endif
           }
         }
 #  endif

>From 072a5b2b327fe402617bd7b5ccf081931e685289 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 12:52:06 -0400
Subject: [PATCH 3/7] fix warnings treated as errors

---
 libcxx/include/fstream | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index a5f1e80960054..54f1555c15cbe 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -643,7 +643,10 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
 #      if defined(_AIX) || defined(__TOS_AIX__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic warning "-Wcpp"
 #        warning 'exclusive mode is unsupported on this target'
+#        pragma GCC diagnostic pop
 #      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:

>From 9c99f50050c182905f10cd7a5022b32600d0e7b2 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 13:49:31 -0400
Subject: [PATCH 4/7] add warning as fprintf

---
 libcxx/include/fstream | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 54f1555c15cbe..7c7f7c6b0db52 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,12 +642,6 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if defined(_AIX) || defined(__TOS_AIX__)
-#        pragma GCC diagnostic push
-#        pragma GCC diagnostic warning "-Wcpp"
-#        warning 'exclusive mode is unsupported on this target'
-#        pragma GCC diagnostic pop
-#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -750,9 +744,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr)
+  if (!__mdstr){
+#     if defined(_AIX) || defined(__TOS_AIX__)
+    if (__mode & ios_base::noreplace)
+      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
+#     endif
     return nullptr;
-
+}
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From d91a5ff2deca3f6f1c737ad0889afcbc198dca74 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:30:10 +0530
Subject: [PATCH 5/7] fix formatting

---
 libcxx/include/fstream | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7c7f7c6b0db52..9b176ddb5a469 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -744,13 +744,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr){
-#     if defined(_AIX) || defined(__TOS_AIX__)
+  if (!__mdstr) {
+#      if defined(_AIX) || defined(__TOS_AIX__)
     if (__mode & ios_base::noreplace)
       fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#     endif
+#      endif
     return nullptr;
-}
+  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From f3d25942e836d9a90b689f74747d241e2b40596b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:37:48 +0530
Subject: [PATCH 6/7] add the remaining test files

---
 .../fstreams/fstream.cons/pointer.pass.cpp    | 24 +++++++++++++++---
 .../fstream.members/open_pointer.pass.cpp     | 24 +++++++++++++++---
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 24 +++++++++++++++---
 .../ofstream.members/open_pointer.pass.cpp    | 25 ++++++++++++++++---
 4 files changed, 81 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..a677b16e76479 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
@@ -91,14 +89,23 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -108,14 +115,23 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..92c0cf1aa0c00 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -74,7 +72,10 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -82,7 +83,13 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -93,7 +100,10 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -101,7 +111,13 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..4fae51f38adbd 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
@@ -145,14 +143,23 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -162,14 +169,23 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..493af1bda9c98 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -82,7 +80,10 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -90,7 +91,13 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -101,14 +108,24 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
+
           {
             std::remove(tmp.c_str());
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif

>From 71a756641b5e5d0aa17687274c66648f42d09558 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 6 Jul 2026 12:15:37 +0530
Subject: [PATCH 7/7] review changes

---
 libcxx/include/fstream                        | 21 +++++++++++--------
 .../filebuf.members/open_pointer.pass.cpp     | 18 ++++++----------
 .../fstreams/fstream.cons/pointer.pass.cpp    | 18 ++++++----------
 .../fstream.members/open_pointer.pass.cpp     | 18 ++++++----------
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 18 ++++++----------
 .../ofstream.members/open_pointer.pass.cpp    | 18 ++++++----------
 6 files changed, 42 insertions(+), 69 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 9b176ddb5a469..9975be836304a 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -258,6 +258,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -641,8 +643,8 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // existence of the file and create it, it shall fail rather than perform a
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
-// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if !defined(_AIX) && !defined(__TOS_AIX__)
+// _AIX is defined on AIX by all supported compilers.
+#      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -653,7 +655,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
-#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
+#      endif // !defined(_AIX)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
@@ -744,13 +746,8 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr) {
-#      if defined(_AIX) || defined(__TOS_AIX__)
-    if (__mode & ios_base::noreplace)
-      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#      endif
+  if (!__mdstr)
     return nullptr;
-  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1192,6 +1189,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1350,6 +1349,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1514,6 +1515,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 627a65cac3f2f..56f238d24dbcd 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -73,10 +73,7 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -84,10 +81,10 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
 #  else
             assert(f.is_open()); // since it doesn't exist
 #  endif
@@ -101,10 +98,7 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -112,10 +106,10 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
 #    else
             assert(f.is_open()); // since it doesn't exist
 #    endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index a677b16e76479..c373643afeac3 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -89,20 +89,17 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
 #  else
             assert(f.is_open()); // since it doesn't exist
 #  endif
@@ -115,20 +112,17 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
 #    else
             assert(f.is_open()); // since it doesn't exist
 #    endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 92c0cf1aa0c00..eeae0a4d72f8a 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -72,10 +72,7 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -83,10 +80,10 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
 #  else
             assert(f.is_open()); // since it doesn't exist
 #  endif
@@ -100,10 +97,7 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -111,10 +105,10 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
 #    else
             assert(f.is_open()); // since it doesn't exist
 #    endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index 4fae51f38adbd..c593f52b3b93b 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -143,20 +143,17 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
 #  else
             assert(f.is_open()); // since it doesn't exist
 #  endif
@@ -169,20 +166,17 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
 #    else
             assert(f.is_open()); // since it doesn't exist
 #    endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 493af1bda9c98..d75636bfa0232 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -80,10 +80,7 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -91,10 +88,10 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
 #  else
             assert(f.is_open()); // since it doesn't exist
 #  endif
@@ -108,10 +105,7 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -119,10 +113,10 @@ int main(int, char**)
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
 #    else
             assert(f.is_open()); // since it doesn't exist
 #    endif


From libcxx-commits at lists.llvm.org  Sun Jul  5 23:58:16 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 05 Jul 2026 23:58:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Clean up windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a4b5208.491d90f5.1f0d58.655d@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207577

>From 0737abee1be634f31e9dd2f708c1cafea07f18cb Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 5 Jul 2026 12:01:53 +0200
Subject: [PATCH] [libc++] Cleanup windows macros

---
 libcxx/include/__config                       | 17 ++++----
 libcxx/include/__filesystem/path.h            | 34 ++++++++--------
 libcxx/include/__filesystem/u8path.h          | 14 +++----
 libcxx/include/__locale                       |  2 +-
 libcxx/include/__locale_dir/locale_base_api.h |  2 +-
 libcxx/include/__locale_dir/support/windows.h |  2 +-
 libcxx/include/__math/gamma.h                 |  2 +-
 libcxx/include/__math/traits.h                |  2 +-
 libcxx/include/__ostream/print.h              |  4 +-
 libcxx/include/fstream                        | 10 ++---
 libcxx/include/math.h                         | 10 ++---
 libcxx/include/print                          | 12 +++---
 libcxx/include/stdlib.h                       |  4 +-
 libcxx/include/string.h                       |  2 +-
 libcxx/include/text_encoding                  |  2 +-
 libcxx/include/wchar.h                        |  6 +--
 libcxx/src/chrono.cpp                         |  8 ++--
 libcxx/src/filesystem/directory_iterator.cpp  |  4 +-
 libcxx/src/filesystem/error.h                 |  4 +-
 libcxx/src/filesystem/file_descriptor.h       | 14 +++----
 libcxx/src/filesystem/filesystem_clock.cpp    |  6 +--
 libcxx/src/filesystem/format_string.h         |  2 +-
 libcxx/src/filesystem/operations.cpp          | 14 +++----
 libcxx/src/filesystem/path.cpp                |  2 +-
 libcxx/src/filesystem/path_parser.h           |  4 +-
 libcxx/src/filesystem/posix_compat.h          |  6 +--
 libcxx/src/filesystem/time_utils.h            | 10 ++---
 libcxx/src/fstream.cpp                        |  4 +-
 libcxx/src/include/aligned_alloc.h            |  4 +-
 libcxx/src/include/config_elast.h             |  4 +-
 libcxx/src/iostream.cpp                       |  2 +-
 libcxx/src/locale.cpp                         | 40 +++++++++----------
 libcxx/src/print.cpp                          |  8 ++--
 libcxx/src/std_stream.h                       |  4 +-
 libcxx/src/string.cpp                         |  2 +-
 libcxx/src/support/win32/locale_win32.cpp     |  6 +--
 libcxx/src/system_error.cpp                   | 10 ++---
 libcxx/src/thread.cpp                         |  4 +-
 libcxx/test/support/count_new.h               |  2 +-
 39 files changed, 143 insertions(+), 146 deletions(-)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..bf2cb865fa0e5 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -43,14 +43,11 @@
 #  endif
 
 #  if defined(_WIN32)
-#    define _LIBCPP_WIN32API
 #    define _LIBCPP_SHORT_WCHAR 1
-// Both MinGW and native MSVC provide a "MSVC"-like environment
-#    define _LIBCPP_MSVCRT_LIKE
-// If mingw not explicitly detected, assume using MS C runtime only if
-// a MS compatibility version is specified.
-#    if defined(_MSC_VER) && !defined(__MINGW32__)
-#      define _LIBCPP_MSVCRT // Using Microsoft's C Runtime library
+#    if defined(__MINGW32__)
+#      define _LIBCPP_LIBC_MINGW
+#    else
+#      define _LIBCPP_LIBC_MSVCRT
 #    endif
 #    define _LIBCPP_HAS_OPEN_WITH_WCHAR 1
 #  else
@@ -100,7 +97,7 @@
 #    define _LIBCPP_USING_GETENTROPY
 #  elif defined(__Fuchsia__)
 #    define _LIBCPP_USING_FUCHSIA_CPRNG
-#  elif defined(_LIBCPP_WIN32API)
+#  elif defined(_WIN32)
 #    define _LIBCPP_USING_WIN32_RANDOM
 #  else
 #    define _LIBCPP_USING_DEV_RANDOM
@@ -170,7 +167,7 @@ typedef __char32_t char32_t;
 
 // If we are getting operator new from the MSVC CRT, then allocation overloads
 // for align_val_t were added in 19.12, aka VS 2017 version 15.3.
-#  if defined(_LIBCPP_MSVCRT) && defined(_MSC_VER) && _MSC_VER < 1912
+#  if defined(_LIBCPP_LIBC_MSVCRT) && defined(_MSC_VER) && _MSC_VER < 1912
 #    define _LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION 0
 #  elif defined(_LIBCPP_ABI_VCRUNTIME) && !defined(__cpp_aligned_new)
 // We're deferring to Microsoft's STL to provide aligned new et al. We don't
@@ -227,7 +224,7 @@ typedef __char32_t char32_t;
 // TODO(44575): Switch to C11 thread API when possible.
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#    elif defined(_LIBCPP_WIN32API)
+#    elif defined(_WIN32)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
 #    else
diff --git a/libcxx/include/__filesystem/path.h b/libcxx/include/__filesystem/path.h
index a63c4ee611ebf..f89c829505d61 100644
--- a/libcxx/include/__filesystem/path.h
+++ b/libcxx/include/__filesystem/path.h
@@ -80,7 +80,7 @@ struct __can_convert_char<char32_t> {
 
 template <class _ECharT, __enable_if_t<__can_convert_char<_ECharT>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI bool __is_separator(_ECharT __e) {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   return __e == _ECharT('/') || __e == _ECharT('\\');
 #  else
   return __e == _ECharT('/');
@@ -181,7 +181,7 @@ struct __is_pathable<_Tp, false, true, false> : __is_pathable_char_array<_Tp> {}
 template <class _Tp>
 struct __is_pathable<_Tp, false, false, true> : __is_pathable_iter<_Tp> {};
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 typedef wstring __path_string;
 typedef wchar_t __path_value;
 #  else
@@ -189,7 +189,7 @@ typedef string __path_string;
 typedef char __path_value;
 #  endif
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 _LIBCPP_EXPORTED_FROM_ABI size_t __wide_to_char(const wstring&, char*, size_t);
 _LIBCPP_EXPORTED_FROM_ABI size_t __char_to_wide(const string&, wchar_t*, size_t);
 #  endif
@@ -203,12 +203,12 @@ struct _PathCVT {
   static_assert(__can_convert_char<_ECharT>::value, "Char type not convertible");
 
   typedef __narrow_to_utf8<sizeof(_ECharT) * __CHAR_BIT__> _Narrower;
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
   typedef __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__> _Widener;
 #    endif
 
   _LIBCPP_HIDE_FROM_ABI static void __append_range(__path_string& __dest, _ECharT const* __b, _ECharT const* __e) {
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __b, __e);
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -223,7 +223,7 @@ struct _PathCVT {
     if (__b == __e)
       return;
     basic_string<_ECharT> __tmp(__b, __e);
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __tmp.data(), __tmp.data() + __tmp.length());
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -241,7 +241,7 @@ struct _PathCVT {
     basic_string<_ECharT> __tmp;
     for (; *__b != __sentinel; ++__b)
       __tmp.push_back(*__b);
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __tmp.data(), __tmp.data() + __tmp.length());
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -284,7 +284,7 @@ struct _PathCVT<__path_value> {
   }
 };
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 template <>
 struct _PathCVT<char> {
   _LIBCPP_HIDE_FROM_ABI static void __append_string(__path_string& __dest, const basic_string<char>& __str) {
@@ -369,7 +369,7 @@ struct _PathExport<char8_t> {
 };
 #      endif // _LIBCPP_HAS_CHAR8_T
 #    endif   // _LIBCPP_HAS_LOCALIZATION
-#  endif     // _LIBCPP_WIN32API
+#  endif     // _WIN32
 
 class _LIBCPP_EXPORTED_FROM_ABI path {
   template <class _SourceOrIter, class _Tp = path&>
@@ -382,7 +382,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   using _SourceCVT _LIBCPP_NODEBUG = _PathCVT<_SourceChar<_Tp> >;
 
 public:
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   typedef wchar_t value_type;
   static constexpr value_type preferred_separator = L'\\';
 #  else
@@ -468,7 +468,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
 
 public:
   // appends
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   _LIBCPP_HIDE_FROM_ABI path& operator/=(const path& __p) {
     auto __p_root_name      = __p.__root_name();
     auto __p_root_name_size = __p_root_name.size();
@@ -602,7 +602,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   _LIBCPP_HIDE_FROM_ABI void clear() noexcept { __pn_.clear(); }
 
   _LIBCPP_HIDE_FROM_ABI path& make_preferred() _LIBCPP_LIFETIMEBOUND {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     std::replace(__pn_.begin(), __pn_.end(), L'/', L'\\');
 #  endif
     return *this;
@@ -667,7 +667,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
 
   _LIBCPP_HIDE_FROM_ABI operator string_type() const { return __pn_; }
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::wstring wstring() const { return __pn_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::wstring generic_wstring() const {
@@ -722,7 +722,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
     return __s;
   }
 #    endif // _LIBCPP_HAS_LOCALIZATION
-#  else    /* _LIBCPP_WIN32API */
+#  else    // _WIN32
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::string string() const { return __pn_; }
 #    if _LIBCPP_HAS_CHAR8_T
@@ -775,7 +775,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::u16string generic_u16string() const { return string<char16_t>(); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::u32string generic_u32string() const { return string<char32_t>(); }
 #    endif // _LIBCPP_HAS_LOCALIZATION
-#  endif   /* !_LIBCPP_WIN32API */
+#  endif   // _WIN32
 
 private:
   int __compare(__string_view) const;
@@ -799,7 +799,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_name() const { return string_type(__root_name()); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_directory() const { return string_type(__root_directory()); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_path() const {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     return string_type(__root_path_raw());
 #  else
     return root_name().append(string_type(__root_directory()));
@@ -824,7 +824,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool has_extension() const { return !__extension().empty(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_absolute() const {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     __string_view __root_name_str = __root_name();
     __string_view __root_dir      = __root_directory();
     if (__root_name_str.size() == 2 && __root_name_str[1] == ':') {
diff --git a/libcxx/include/__filesystem/u8path.h b/libcxx/include/__filesystem/u8path.h
index aabd2bbd3c26c..77e7c0d6653da 100644
--- a/libcxx/include/__filesystem/u8path.h
+++ b/libcxx/include/__filesystem/u8path.h
@@ -24,7 +24,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
-#  if !defined(_LIBCPP_WIN32API) || _LIBCPP_HAS_LOCALIZATION
+#  if !defined(_WIN32) || _LIBCPP_HAS_LOCALIZATION
 template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(_InputIt __f, _InputIt __l) {
   static_assert(
@@ -34,7 +34,7 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
           is_same<typename __is_pathable<_InputIt>::__char_type, char>::value,
       "u8path(Iter, Iter) requires Iter have a value_type of type 'char'"
       " or 'char8_t'");
-#    if defined(_LIBCPP_WIN32API)
+#    if defined(_WIN32)
   string __tmp(__f, __l);
   using _CVT = __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__>;
   std::wstring __w;
@@ -43,11 +43,11 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
   return path(__w);
 #    else
   return path(__f, __l);
-#    endif // defined(_LIBCPP_WIN32API)
+#    endif // defined(_WIN32)
 }
-#  endif // !defined(_LIBCPP_WIN32API) || _LIBCPP_HAS_LOCALIZATION
+#  endif // !defined(_WIN32) || _LIBCPP_HAS_LOCALIZATION
 
-#  if defined(_LIBCPP_WIN32API) && _LIBCPP_HAS_LOCALIZATION
+#  if defined(_WIN32) && _LIBCPP_HAS_LOCALIZATION
 template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(_InputIt __f, _NullSentinel) {
   static_assert(
@@ -67,7 +67,7 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
   _CVT()(back_inserter(__w), __tmp.data(), __tmp.data() + __tmp.size());
   return path(__w);
 }
-#  endif // defined(_LIBCPP_WIN32API) && _LIBCPP_HAS_LOCALIZATION
+#  endif // defined(_WIN32) && _LIBCPP_HAS_LOCALIZATION
 
 template <class _Source, __enable_if_t<__is_pathable<_Source>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(const _Source& __s) {
@@ -78,7 +78,7 @@ template <class _Source, __enable_if_t<__is_pathable<_Source>::value, int> = 0>
           is_same<typename __is_pathable<_Source>::__char_type, char>::value,
       "u8path(Source const&) requires Source have a character type of type "
       "'char' or 'char8_t'");
-#  if defined(_LIBCPP_WIN32API)
+#  if defined(_WIN32)
   using _Traits = __is_pathable<_Source>;
   return u8path(std::__unwrap_iter(_Traits::__range_begin(__s)), std::__unwrap_iter(_Traits::__range_end(__s)));
 #  else
diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index bb8f8e7bbf5d9..e0524cf4abc4c 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -352,7 +352,7 @@ public:
 #    else
   static const mask __regex_word = 0x80;
 #    endif
-#  elif defined(_LIBCPP_MSVCRT_LIKE)
+#  elif defined(_WIN32)
   typedef unsigned short mask;
   static const mask space        = _SPACE;
   static const mask print        = _BLANK | _PUNCT | _ALPHA | _DIGIT;
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index f973d95ef244d..017d971ec72ba 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -116,7 +116,7 @@
 #    include <__locale_dir/support/netbsd.h>
 #  elif defined(__OpenBSD__)
 #    include <__locale_dir/support/openbsd.h>
-#  elif defined(_LIBCPP_MSVCRT_LIKE)
+#  elif defined(_WIN32)
 #    include <__locale_dir/support/windows.h>
 #  elif defined(__Fuchsia__)
 #    include <__locale_dir/support/fuchsia.h>
diff --git a/libcxx/include/__locale_dir/support/windows.h b/libcxx/include/__locale_dir/support/windows.h
index 6675cfb740b29..a20b5226738a6 100644
--- a/libcxx/include/__locale_dir/support/windows.h
+++ b/libcxx/include/__locale_dir/support/windows.h
@@ -172,7 +172,7 @@ _LIBCPP_EXPORTED_FROM_ABI const char* __get_locale_encoding(__locale_t __loc);
 //
 
 // the *_l functions are prefixed on Windows, only available for msvcr80+, VS2005+
-#if defined(_LIBCPP_MSVCRT)
+#if defined(_LIBCPP_LIBC_MSVCRT)
 inline _LIBCPP_HIDE_FROM_ABI float __strtof(const char* __nptr, char** __endptr, __locale_t __loc) {
   return ::_strtof_l(__nptr, __endptr, __loc);
 }
diff --git a/libcxx/include/__math/gamma.h b/libcxx/include/__math/gamma.h
index a742ea42d9009..08e09d7cb7aef 100644
--- a/libcxx/include/__math/gamma.h
+++ b/libcxx/include/__math/gamma.h
@@ -62,7 +62,7 @@ inline _LIBCPP_HIDE_FROM_ABI double tgamma(_A1 __x) _NOEXCEPT {
 // declare it differently in the first place: instead use `asm` to get the compiler to call the right
 // function.
 
-#if defined(_LIBCPP_MSVCRT_LIKE) // reentrant version is not available on Windows
+#ifdef _WIN32 // reentrant version is not available on Windows
 
 inline _LIBCPP_HIDE_FROM_ABI double __lgamma_r(double __d) _NOEXCEPT { return __builtin_lgamma(__d); }
 
diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..5dd489c8b0a36 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -188,7 +188,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // TODO: Remove the workaround once UCRT fixes these functions. Note that this doesn't seem planned as of 2025-07 per
 // https://developercommunity.visualstudio.com/t/10294165.
 
-#if defined(_LIBCPP_MSVCRT) && _LIBCPP_STD_VER >= 20
+#if defined(_LIBCPP_LIBC_MSVCRT) && _LIBCPP_STD_VER >= 20
 namespace __ucrt {
 template <class _A1>
   requires is_integral_v<_A1>
diff --git a/libcxx/include/__ostream/print.h b/libcxx/include/__ostream/print.h
index 5b4ab60a45eea..4a519c177b990 100644
--- a/libcxx/include/__ostream/print.h
+++ b/libcxx/include/__ostream/print.h
@@ -89,7 +89,7 @@ _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 #    if _LIBCPP_HAS_UNICODE
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
-#      ifndef _LIBCPP_WIN32API
+#      ifndef _WIN32
   return std::__vprint_nonunicode(__os, __fmt, __args, __write_nl);
 #      else
   FILE* __file = std::__get_ostream_file(__os);
@@ -123,7 +123,7 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo
     __os.__set_badbit_and_consider_rethrow();
   }
 #        endif // _LIBCPP_HAS_EXCEPTIONS
-#      endif   // _LIBCPP_WIN32API
+#      endif   // _WIN32
 }
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..78fcdf254b493 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -222,7 +222,7 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#    if _LIBCPP_STD_VER >= 23 && defined(_LIBCPP_WIN32API)
+#    if _LIBCPP_STD_VER >= 23 && defined(_WIN32)
 _LIBCPP_EXPORTED_FROM_ABI void* __filebuf_windows_native_handle(FILE* __file) noexcept;
 #    endif
 
@@ -236,7 +236,7 @@ public:
   typedef typename traits_type::off_type off_type;
   typedef typename traits_type::state_type state_type;
 #    if _LIBCPP_STD_VER >= 26
-#      if defined(_LIBCPP_WIN32API)
+#      if defined(_WIN32)
   using native_handle_type = void*; // HANDLE
 #      elif __has_include(<unistd.h>)
   using native_handle_type = int; // POSIX file descriptor
@@ -272,7 +272,7 @@ public:
 #    if _LIBCPP_STD_VER >= 26
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() const noexcept {
     _LIBCPP_ASSERT_UNCATEGORIZED(this->is_open(), "File must be opened");
-#      if defined(_LIBCPP_WIN32API)
+#      if defined(_WIN32)
     return std::__filebuf_windows_native_handle(__file_);
 #      elif __has_include(<unistd.h>)
     return fileno(__file_);
@@ -1020,7 +1020,7 @@ basic_filebuf<_CharT, _Traits>::seekoff(off_type __off, ios_base::seekdir __way,
 
 template <class _CharT, class _Traits>
 int basic_filebuf<_CharT, _Traits>::__fseek(FILE* __file, pos_type __offset, int __whence) {
-#    if defined(_LIBCPP_MSVCRT_LIKE)
+#    ifdef _WIN32
   return _fseeki64(__file, __offset, __whence);
 #    elif _LIBCPP_LIBC_NEWLIB
   return fseek(__file, __offset, __whence);
@@ -1031,7 +1031,7 @@ int basic_filebuf<_CharT, _Traits>::__fseek(FILE* __file, pos_type __offset, int
 
 template <class _CharT, class _Traits>
 typename basic_filebuf<_CharT, _Traits>::pos_type basic_filebuf<_CharT, _Traits>::__ftell(FILE* __file) {
-#    if defined(_LIBCPP_MSVCRT_LIKE)
+#    ifdef _WIN32
   return _ftelli64(__file);
 #    elif _LIBCPP_LIBC_NEWLIB
   return ftell(__file);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..d35d305a682eb 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -416,7 +416,7 @@ using std::__math::fpclassify;
 using std::__math::signbit;
 
 // The MSVC runtime already provides these functions as templates
-#      ifndef _LIBCPP_MSVCRT
+#      ifndef _LIBCPP_LIBC_MSVCRT
 using std::__math::isfinite;
 using std::__math::isgreater;
 using std::__math::isgreaterequal;
@@ -427,9 +427,9 @@ using std::__math::islessgreater;
 using std::__math::isnan;
 using std::__math::isnormal;
 using std::__math::isunordered;
-#      endif // _LIBCPP_MSVCRT
+#      endif // _LIBCPP_LIBC_MSVCRT
 
-#      if defined(_LIBCPP_MSVCRT) && _LIBCPP_STD_VER >= 20
+#      if defined(_LIBCPP_LIBC_MSVCRT) && _LIBCPP_STD_VER >= 20
 // MS UCRT incorrectly defines some functions in a way not working with integer types. Until C++20, this was worked
 // around by -fdelayed-template-parsing. Since C++20, we can use standard feature "requires" instead.
 
@@ -446,7 +446,7 @@ using std::__math::__ucrt::islessgreater;
 using std::__math::__ucrt::isnan;
 using std::__math::__ucrt::isnormal;
 using std::__math::__ucrt::isunordered;
-#      endif // defined(_LIBCPP_MSVCRT) && _LIBCPP_STD_VER >= 20
+#      endif // defined(_LIBCPP_LIBC_MSVCRT) && _LIBCPP_STD_VER >= 20
 
 // We have to provide double overloads for <math.h> to work on platforms that don't provide the full set of math
 // functions. To make the overload set work with multiple functions that take the same arguments, we make our overloads
@@ -527,7 +527,7 @@ using std::__math::trunc;
 //
 // and receive the definitions of mathematical constants, even if <math.h>
 // has previously been included.
-#  if defined(_LIBCPP_MSVCRT) && defined(_USE_MATH_DEFINES)
+#  if defined(_LIBCPP_LIBC_MSVCRT) && defined(_USE_MATH_DEFINES)
 #    include_next <math.h>
 #  endif
 
diff --git a/libcxx/include/print b/libcxx/include/print
index cf62743846f42..a9c4ee686f5f9 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -56,7 +56,7 @@ namespace std {
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#    ifdef _LIBCPP_WIN32API
+#    ifdef _WIN32
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
 
@@ -73,7 +73,7 @@ _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
 _LIBCPP_EXPORTED_FROM_ABI void __write_to_windows_console(FILE* __stream, wstring_view __view);
 #      endif // _LIBCPP_HAS_WIDE_CHARACTERS
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
-#    endif   // _LIBCPP_WIN32API
+#    endif   // _WIN32
 
 #    if _LIBCPP_HAS_UNICODE
 // This is the code to transcode UTF-8 to UTF-16. This is used on
@@ -197,7 +197,7 @@ inline constexpr bool __use_unicode_execution_charset = _MSVC_EXECUTION_CHARACTE
 inline constexpr bool __use_unicode_execution_charset = true;
 #    endif
 
-#    ifdef _LIBCPP_WIN32API
+#    ifdef _WIN32
 _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream) {
   // The macro _LIBCPP_TESTING_PRINT_IS_TERMINAL is used to change
   // the behavior in the test. This is not part of the public API.
@@ -207,7 +207,7 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream)
   return std::__is_windows_terminal(__stream);
 #      endif
 }
-#    endif // _LIBCPP_WIN32API
+#    endif // _WIN32
 
 [[noreturn]] _LIBCPP_HIDE_FROM_ABI inline void __handle_output_error(FILE* __stream) {
   if (std::feof(__stream))
@@ -251,7 +251,7 @@ _LIBCPP_HIDE_FROM_ABI inline void __output_unicode_windows([[maybe_unused]] FILE
   // the behavior in the test. This is not part of the public API.
 #        ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
   _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION(__stream, __view);
-#        elif defined(_LIBCPP_WIN32API)
+#        elif defined(_WIN32)
   std::__write_to_windows_console(__stream, __view);
 #        else
   std::__throw_runtime_error("No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
@@ -282,7 +282,7 @@ _LIBCPP_HIDE_FROM_ABI inline void __output_unicode([[maybe_unused]] FILE* __stre
   // so there the call can be forwarded to the non_unicode API. On
   // Windows there is a different API. This API requires transcoding.
 
-#      ifndef _LIBCPP_WIN32API
+#      ifndef _WIN32
   __print::__output_nonunicode(__stream, __text);
 #      elif _LIBCPP_HAS_WIDE_CHARACTERS
   if (__print::__is_terminal(__stream)) {
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..0eb4a9ed433d2 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -122,12 +122,12 @@ using std::__math::abs;
 #      endif
 
 // MSVCRT already has the correct prototype in <stdlib.h> if __cplusplus is defined
-#      if !defined(_LIBCPP_MSVCRT)
+#      if !defined(_LIBCPP_LIBC_MSVCRT)
 inline _LIBCPP_HIDE_FROM_ABI ldiv_t div(long __x, long __y) _NOEXCEPT { return ::ldiv(__x, __y); }
 #        if !(defined(__FreeBSD__) && !defined(__LONG_LONG_SUPPORTED))
 inline _LIBCPP_HIDE_FROM_ABI lldiv_t div(long long __x, long long __y) _NOEXCEPT { return ::lldiv(__x, __y); }
 #        endif
-#      endif // _LIBCPP_MSVCRT
+#      endif // _LIBCPP_LIBC_MSVCRT
 } // extern "C++"
 #    endif   // __cplusplus
 #  endif     // _LIBCPP_STDLIB_H
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..417a341474f31 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -69,7 +69,7 @@ size_t strlen(const char* s);
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
 // the right signature.
-#  if defined(__CORRECT_ISO_CPP_STRING_H_PROTO) || defined(_LIBCPP_MSVCRT) ||                                          \
+#  if defined(__CORRECT_ISO_CPP_STRING_H_PROTO) || defined(_LIBCPP_LIBC_MSVCRT) ||                                     \
       defined(_STRING_H_CPLUSPLUS_98_CONFORMANCE_)
 #    define _LIBCPP_STRING_H_HAS_CONST_OVERLOADS
 #  endif
diff --git a/libcxx/include/text_encoding b/libcxx/include/text_encoding
index da39be92c3dfa..c00fe0d278dc0 100644
--- a/libcxx/include/text_encoding
+++ b/libcxx/include/text_encoding
@@ -598,7 +598,7 @@ public:
 
 #    if _LIBCPP_HAS_LOCALIZATION
   [[nodiscard]] _LIBCPP_AVAILABILITY_TEXT_ENCODING_ENVIRONMENT static text_encoding environment() {
-#      if defined(_LIBCPP_WIN32API)
+#      ifdef _WIN32
     return __get_locale_encoding(nullptr);
 #      else
     return __get_locale_encoding("");
diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index 7ff03870bdfdb..c68f284879cb0 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -129,7 +129,7 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 #      if __GLIBC_PREREQ(2, 10)
 #        define _LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS 1
 #      endif
-#    elif defined(_LIBCPP_MSVCRT)
+#    elif defined(_LIBCPP_LIBC_MSVCRT)
 #      if defined(_CRT_CONST_CORRECT_OVERLOADS)
 #        define _LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS 1
 #      endif
@@ -195,14 +195,14 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD wchar_t* wmemchr(wchar_t
 }
 #      endif
 
-#      if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
+#      if defined(__cplusplus) && (defined(_WIN32) || defined(__MVS__))
 extern "C" {
 size_t mbsnrtowcs(
     wchar_t* __restrict __dst, const char** __restrict __src, size_t __nmc, size_t __len, mbstate_t* __restrict __ps);
 size_t wcsnrtombs(
     char* __restrict __dst, const wchar_t** __restrict __src, size_t __nwc, size_t __len, mbstate_t* __restrict __ps);
 } // extern "C"
-#      endif // __cplusplus && (_LIBCPP_MSVCRT || __MVS__)
+#      endif // __cplusplus && (_LIBCPP_LIBC_MSVCRT || __MVS__)
 #    endif   // _LIBCPP_HAS_WIDE_CHARACTERS
 #  endif     // _LIBCPP_WCHAR_H
 
diff --git a/libcxx/src/chrono.cpp b/libcxx/src/chrono.cpp
index af5fe7c01fb8b..30c1a1f119a92 100644
--- a/libcxx/src/chrono.cpp
+++ b/libcxx/src/chrono.cpp
@@ -41,14 +41,14 @@
 #  define _LIBCPP_HAS_CLOCK_GETTIME
 #endif
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define VC_EXTRA_LEAN
 #  include <windows.h>
 #  if _WIN32_WINNT >= _WIN32_WINNT_WIN8
 #    include <winapifamily.h>
 #  endif
-#endif // defined(_LIBCPP_WIN32API)
+#endif // _WIN32
 
 #if defined(__Fuchsia__)
 #  include <zircon/syscalls.h>
@@ -71,7 +71,7 @@ namespace chrono {
 // system_clock
 //
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 #  if _WIN32_WINNT < _WIN32_WINNT_WIN8
 
@@ -184,7 +184,7 @@ static steady_clock::time_point __libcpp_steady_clock_now() {
   return steady_clock::time_point(seconds(tp.tv_sec) + nanoseconds(tp.tv_nsec));
 }
 
-#  elif defined(_LIBCPP_WIN32API)
+#  elif defined(_WIN32)
 
 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx says:
 //    If the function fails, the return value is zero. <snip>
diff --git a/libcxx/src/filesystem/directory_iterator.cpp b/libcxx/src/filesystem/directory_iterator.cpp
index 5d682aee2b839..fd2bd504b221b 100644
--- a/libcxx/src/filesystem/directory_iterator.cpp
+++ b/libcxx/src/filesystem/directory_iterator.cpp
@@ -17,7 +17,7 @@
 #include "error.h"
 #include "file_descriptor.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -30,7 +30,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 using detail::ErrorHandler;
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 class __dir_stream {
 public:
   __dir_stream()                               = delete;
diff --git a/libcxx/src/filesystem/error.h b/libcxx/src/filesystem/error.h
index db5d1ae9a7250..ee1d148e65aa1 100644
--- a/libcxx/src/filesystem/error.h
+++ b/libcxx/src/filesystem/error.h
@@ -23,7 +23,7 @@
 
 #include "format_string.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h> // ERROR_* macros
@@ -42,7 +42,7 @@ inline error_code capture_errno() {
 }
 
 inline error_code get_last_error() {
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   return std::error_code(GetLastError(), std::system_category());
 #else
   return capture_errno();
diff --git a/libcxx/src/filesystem/file_descriptor.h b/libcxx/src/filesystem/file_descriptor.h
index 9c279c451f28c..b095f200912a4 100644
--- a/libcxx/src/filesystem/file_descriptor.h
+++ b/libcxx/src/filesystem/file_descriptor.h
@@ -20,7 +20,7 @@
 #include "posix_compat.h"
 #include "time_utils.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -30,13 +30,13 @@
 #  include <sys/stat.h>
 #  include <sys/statvfs.h>
 #  include <unistd.h>
-#endif // defined(_LIBCPP_WIN32API)
+#endif // _WIN32
 
 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 
-#if !defined(_LIBCPP_WIN32API)
+#ifndef _WIN32
 
 #  if defined(DT_BLK)
 template <class DirEntT, class = decltype(DirEntT::d_type)>
@@ -84,7 +84,7 @@ inline pair<string_view, file_type> posix_readdir(DIR* dir_stream, error_code& e
   }
 }
 
-#else // _LIBCPP_WIN32API
+#else // _WIN32
 
 inline file_type get_file_type(const WIN32_FIND_DATAW& data) {
   if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
@@ -111,7 +111,7 @@ inline perms get_file_perm(const WIN32_FIND_DATAW& data) {
   return static_cast<perms>(st_mode) & perms::mask;
 }
 
-#endif // !_LIBCPP_WIN32API
+#endif // _WIN32
 
 //                       POSIX HELPERS
 
@@ -128,7 +128,7 @@ struct FileDescriptor {
   static FileDescriptor create(const path* p, error_code& ec, Args... args) {
     ec.clear();
     int fd;
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
     // TODO: most of the filesystem implementation uses native Win32 calls
     // (mostly via posix_compat.h). However, here we use the C-runtime APIs to
     // open a file, because we subsequently pass the C-runtime fd to
@@ -171,7 +171,7 @@ struct FileDescriptor {
 
   void close() noexcept {
     if (fd != -1) {
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
       ::_close(fd);
 #else
       ::close(fd);
diff --git a/libcxx/src/filesystem/filesystem_clock.cpp b/libcxx/src/filesystem/filesystem_clock.cpp
index 96a1336d4cbca..0d360c51a7a37 100644
--- a/libcxx/src/filesystem/filesystem_clock.cpp
+++ b/libcxx/src/filesystem/filesystem_clock.cpp
@@ -14,11 +14,11 @@
 #include <ratio>
 #include <time.h>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  include "time_utils.h"
 #endif
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -51,7 +51,7 @@ _LIBCPP_DIAGNOSTIC_POP
 
 _FilesystemClock::time_point _FilesystemClock::now() noexcept {
   typedef chrono::duration<rep> __secs;
-#if defined(_LIBCPP_WIN32API)
+#if defined(_WIN32)
   typedef chrono::duration<rep, nano> __nsecs;
   FILETIME time;
   GetSystemTimeAsFileTime(&time);
diff --git a/libcxx/src/filesystem/format_string.h b/libcxx/src/filesystem/format_string.h
index 8d17b027a6e31..48bdad9a12030 100644
--- a/libcxx/src/filesystem/format_string.h
+++ b/libcxx/src/filesystem/format_string.h
@@ -18,7 +18,7 @@
 #include <cstdio>
 #include <string>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define PATHSTR(x) (L##x)
 #  define PATH_CSTR_FMT "\"%ls\""
 #else
diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp
index 3d358dd55e8b8..d6392ca144262 100644
--- a/libcxx/src/filesystem/operations.cpp
+++ b/libcxx/src/filesystem/operations.cpp
@@ -26,7 +26,7 @@
 #include "posix_compat.h"
 #include "time_utils.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -100,7 +100,7 @@ path __canonical(path const& orig_p, error_code* ec) {
   ErrorHandler<path> err("canonical", ec, &orig_p, &cwd);
 
   path p = __do_absolute(orig_p, &cwd, ec);
-#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API)
+#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_WIN32)
   std::unique_ptr<path::value_type, decltype(&::free)> hold(detail::realpath(p.c_str(), nullptr), &::free);
   if (hold.get() == nullptr)
     return err.report(detail::get_last_error());
@@ -460,7 +460,7 @@ void __copy_symlink(const path& existing_symlink, const path& new_symlink, error
   if (ec && *ec) {
     return;
   }
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   error_code local_ec;
   if (is_directory(real_path, local_ec))
     __create_directory_symlink(real_path, new_symlink, ec);
@@ -564,7 +564,7 @@ void __create_symlink(path const& from, path const& to, error_code* ec) {
 path __current_path(error_code* ec) {
   ErrorHandler<path> err("current_path", ec);
 
-#if defined(_LIBCPP_WIN32API) || defined(__GLIBC__) || defined(__APPLE__)
+#if defined(_WIN32) || defined(__GLIBC__) || defined(__APPLE__)
   // Common extension outside of POSIX getcwd() spec, without needing to
   // preallocate a buffer. Also supported by a number of other POSIX libcs.
   int size              = 0;
@@ -691,7 +691,7 @@ void __last_write_time(const path& p, file_time_type new_time, error_code* ec) {
   using detail::fs_time;
   ErrorHandler<void> err("last_write_time", ec, &p);
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   TimeSpec ts;
   if (!fs_time::convert_to_timespec(ts, new_time))
     return err.report(errc::value_too_large);
@@ -819,7 +819,7 @@ bool __remove(const path& p, error_code* ec) {
 //
 // The second implementation is used on platforms where `openat()` & friends are available,
 // and it threads file descriptors through recursive calls to avoid such race conditions.
-#if defined(_LIBCPP_WIN32API) || defined(__MVS__)
+#if defined(_WIN32) || defined(__MVS__)
 #  define REMOVE_ALL_USE_DIRECTORY_ITERATOR
 #endif
 
@@ -996,7 +996,7 @@ file_status __symlink_status(const path& p, error_code* ec) { return detail::pos
 path __temp_directory_path(error_code* ec) {
   ErrorHandler<path> err("temp_directory_path", ec);
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   wchar_t buf[MAX_PATH];
   DWORD retval = GetTempPathW(MAX_PATH, buf);
   if (!retval)
diff --git a/libcxx/src/filesystem/path.cpp b/libcxx/src/filesystem/path.cpp
index faa7b4f1fbab7..5790039f66426 100644
--- a/libcxx/src/filesystem/path.cpp
+++ b/libcxx/src/filesystem/path.cpp
@@ -417,7 +417,7 @@ path::iterator& path::iterator::__decrement() {
   return *this;
 }
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 ////////////////////////////////////////////////////////////////////////////
 // Windows path conversions
 size_t __wide_to_char(const wstring& str, char* out, size_t outlen) {
diff --git a/libcxx/src/filesystem/path_parser.h b/libcxx/src/filesystem/path_parser.h
index 5968f449235ba..ae91dc7f1cf26 100644
--- a/libcxx/src/filesystem/path_parser.h
+++ b/libcxx/src/filesystem/path_parser.h
@@ -22,7 +22,7 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 inline bool isSeparator(path::value_type C) {
   if (C == '/')
     return true;
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   if (C == '\\')
     return true;
 #endif
@@ -324,7 +324,7 @@ struct PathParser {
   }
 
   PosPtr consumeRootName(PosPtr P, PosPtr End) const noexcept {
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
     if (PosPtr Ret = consumeDriveLetter(P, End))
       return Ret;
     if (PosPtr Ret = consumeNetworkRoot(P, End))
diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h
index ddd99d8aaf206..f5119aebcbefb 100644
--- a/libcxx/src/filesystem/posix_compat.h
+++ b/libcxx/src/filesystem/posix_compat.h
@@ -31,7 +31,7 @@
 #include "error.h"
 #include "time_utils.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <io.h>
@@ -47,7 +47,7 @@
 #include <stdlib.h>
 #include <time.h>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 // This struct isn't defined in the normal Windows SDK, but only in the
 // Windows Driver Kit.
 struct LIBCPP_REPARSE_DATA_BUFFER {
@@ -81,7 +81,7 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 // Various C runtime header sets provide more or less of these. As we
 // provide our own implementation, undef all potential defines from the
diff --git a/libcxx/src/filesystem/time_utils.h b/libcxx/src/filesystem/time_utils.h
index 89352e5bd6abb..2b01ea9542586 100644
--- a/libcxx/src/filesystem/time_utils.h
+++ b/libcxx/src/filesystem/time_utils.h
@@ -22,7 +22,7 @@
 #include "error.h"
 #include "format_string.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -41,7 +41,7 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 // Various C runtime versions (UCRT, or the legacy msvcrt.dll used by
 // some mingw toolchains) provide different stat function implementations,
 // with a number of limitations with respect to what we want from the
@@ -267,7 +267,7 @@ struct time_util : time_util_base<FileTimeT, TimeT> {
   }
 };
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 using fs_time = time_util<file_time_type, int64_t, TimeSpec>;
 #else
 using fs_time = time_util<file_time_type, time_t, TimeSpec>;
@@ -301,7 +301,7 @@ inline TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
 
 #if _LIBCPP_HAS_FILESYSTEM
 
-#  if !defined(_LIBCPP_WIN32API)
+#  ifndef _WIN32
 inline bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS, error_code& ec) {
   TimeVal ConvertedTS[2] = {make_timeval(TS[0]), make_timeval(TS[1])};
   if (::utimes(p.c_str(), ConvertedTS) == -1) {
@@ -329,7 +329,7 @@ inline bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS, err
 #    endif
 }
 
-#  endif // !_LIBCPP_WIN32API
+#  endif // _WIN32
 
 inline file_time_type __extract_last_write_time(const path& p, const StatT& st, error_code* ec) {
   using detail::fs_time;
diff --git a/libcxx/src/fstream.cpp b/libcxx/src/fstream.cpp
index 2975088eb6255..12d4952293972 100644
--- a/libcxx/src/fstream.cpp
+++ b/libcxx/src/fstream.cpp
@@ -10,7 +10,7 @@
 #include <cstdio>
 #include <fstream>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <io.h>
@@ -20,7 +20,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 // Confirm that `HANDLE` is `void*` as implemented in `basic_filebuf`
 static_assert(std::same_as<HANDLE, void*>);
diff --git a/libcxx/src/include/aligned_alloc.h b/libcxx/src/include/aligned_alloc.h
index 24ca26ce04525..2b69a344006a3 100644
--- a/libcxx/src/include/aligned_alloc.h
+++ b/libcxx/src/include/aligned_alloc.h
@@ -27,7 +27,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 //
 // Returns the allocated memory, or `nullptr` on failure.
 inline _LIBCPP_HIDE_FROM_ABI void* __libcpp_aligned_alloc(std::size_t __alignment, std::size_t __size) {
-#  if defined(_LIBCPP_MSVCRT_LIKE)
+#  ifdef _WIN32
   return ::_aligned_malloc(__size, __alignment);
 
 // Android only provides aligned_alloc when targeting API 28 or higher.
@@ -51,7 +51,7 @@ inline _LIBCPP_HIDE_FROM_ABI void* __libcpp_aligned_alloc(std::size_t __alignmen
 }
 
 inline _LIBCPP_HIDE_FROM_ABI void __libcpp_aligned_free(void* __ptr) {
-#  if defined(_LIBCPP_MSVCRT_LIKE)
+#  ifdef _WIN32
   ::_aligned_free(__ptr);
 #  else
   ::free(__ptr);
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..8aa9355c4e7f3 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -11,7 +11,7 @@
 
 #include <__config>
 
-#if defined(_LIBCPP_MSVCRT_LIKE)
+#ifdef _WIN32
 #  include <stdlib.h>
 #else
 #  include <errno.h>
@@ -39,7 +39,7 @@
 // No _LIBCPP_ELAST needed on Apple
 #elif defined(__MVS__)
 #  define _LIBCPP_ELAST 1160
-#elif defined(_LIBCPP_MSVCRT_LIKE)
+#elif defined(_WIN32)
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..884585519af09 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -95,7 +95,7 @@ STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
 // the standard streams, otherwise it may not be alive during program termination
 // when we flush the streams.
 static void force_locale_initialization() {
-#if defined(_LIBCPP_MSVCRT_LIKE)
+#ifdef _WIN32
   static bool once = []() {
     auto loc = __locale::__newlocale(_LIBCPP_ALL_MASK, "C", 0);
     {
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index b0a69ab74f94c..22ad9aa8fc1ce 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -30,7 +30,7 @@
 #  include <sys/localedef.h> // for __lc_ctype_ptr
 #endif
 
-#if defined(_LIBCPP_MSVCRT)
+#if defined(_LIBCPP_LIBC_MSVCRT)
 #  define _CTYPE_DISABLE_MACROS
 #endif
 
@@ -905,7 +905,7 @@ const ctype<char>::mask* ctype<char>::classic_table() noexcept {
   return _C_ctype_tab_ + 1;
 #  elif defined(__GLIBC__)
   return _LIBCPP_GET_C_LOCALE->__ctype_b;
-#  elif defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  elif defined(_WIN32)
   return __pctype_func();
 #  elif defined(__EMSCRIPTEN__)
   return *__ctype_b_loc();
@@ -5360,19 +5360,19 @@ void moneypunct_byname<char, true>::init(const char* nm) {
     __frac_digits_ = lc->int_frac_digits;
   else
     __frac_digits_ = base::do_frac_digits();
-#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#ifdef _WIN32
   if (lc->p_sign_posn == 0)
-#else  // _LIBCPP_MSVCRT
+#else
   if (lc->int_p_sign_posn == 0)
-#endif // !_LIBCPP_MSVCRT
+#endif
     __positive_sign_ = "()";
   else
     __positive_sign_ = lc->positive_sign;
-#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#ifdef _WIN32
   if (lc->n_sign_posn == 0)
-#else  // _LIBCPP_MSVCRT
+#else
   if (lc->int_n_sign_posn == 0)
-#endif // !_LIBCPP_MSVCRT
+#endif
     __negative_sign_ = "()";
   else
     __negative_sign_ = lc->negative_sign;
@@ -5380,10 +5380,10 @@ void moneypunct_byname<char, true>::init(const char* nm) {
   // the same places in curr_symbol since there's no way to
   // represent anything else.
   string_type __dummy_curr_symbol = __curr_symbol_;
-#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#ifdef _WIN32
   __init_pat(__pos_format_, __dummy_curr_symbol, true, lc->p_cs_precedes, lc->p_sep_by_space, lc->p_sign_posn, ' ');
   __init_pat(__neg_format_, __curr_symbol_, true, lc->n_cs_precedes, lc->n_sep_by_space, lc->n_sign_posn, ' ');
-#else  // _LIBCPP_MSVCRT
+#else  // _WIN32
   __init_pat(
       __pos_format_,
       __dummy_curr_symbol,
@@ -5394,7 +5394,7 @@ void moneypunct_byname<char, true>::init(const char* nm) {
       ' ');
   __init_pat(
       __neg_format_, __curr_symbol_, true, lc->int_n_cs_precedes, lc->int_n_sep_by_space, lc->int_n_sign_posn, ' ');
-#endif // !_LIBCPP_MSVCRT
+#endif // _WIN32
 }
 
 #if _LIBCPP_HAS_WIDE_CHARACTERS
@@ -5477,11 +5477,11 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
     __frac_digits_ = lc->int_frac_digits;
   else
     __frac_digits_ = base::do_frac_digits();
-#  if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  ifdef _WIN32
   if (lc->p_sign_posn == 0)
-#  else  // _LIBCPP_MSVCRT
+#  else
   if (lc->int_p_sign_posn == 0)
-#  endif // !_LIBCPP_MSVCRT
+#  endif
     __positive_sign_ = L"()";
   else {
     mb = mbstate_t();
@@ -5492,11 +5492,11 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
     wbe = wbuf + j;
     __positive_sign_.assign(wbuf, wbe);
   }
-#  if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  ifdef _WIN32
   if (lc->n_sign_posn == 0)
-#  else  // _LIBCPP_MSVCRT
+#  else
   if (lc->int_n_sign_posn == 0)
-#  endif // !_LIBCPP_MSVCRT
+#  endif
     __negative_sign_ = L"()";
   else {
     mb = mbstate_t();
@@ -5511,10 +5511,10 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
   // the same places in curr_symbol since there's no way to
   // represent anything else.
   string_type __dummy_curr_symbol = __curr_symbol_;
-#  if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  ifdef _WIN32
   __init_pat(__pos_format_, __dummy_curr_symbol, true, lc->p_cs_precedes, lc->p_sep_by_space, lc->p_sign_posn, L' ');
   __init_pat(__neg_format_, __curr_symbol_, true, lc->n_cs_precedes, lc->n_sep_by_space, lc->n_sign_posn, L' ');
-#  else  // _LIBCPP_MSVCRT
+#  else  // _WIN32
   __init_pat(
       __pos_format_,
       __dummy_curr_symbol,
@@ -5525,7 +5525,7 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
       L' ');
   __init_pat(
       __neg_format_, __curr_symbol_, true, lc->int_n_cs_precedes, lc->int_n_sep_by_space, lc->int_n_sign_posn, L' ');
-#  endif // !_LIBCPP_MSVCRT
+#  endif // _WIN32
 }
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
diff --git a/libcxx/src/print.cpp b/libcxx/src/print.cpp
index 21d868e8c6a7a..2eb8a0f31488c 100644
--- a/libcxx/src/print.cpp
+++ b/libcxx/src/print.cpp
@@ -15,7 +15,7 @@
 
 #include "filesystem/error.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <io.h>
@@ -35,7 +35,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream) {
   // Note the Standard does this in one call, but it's unclear whether
@@ -65,13 +65,13 @@ __write_to_windows_console([[maybe_unused]] FILE* __stream, [[maybe_unused]] wst
 }
 #  endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
-#elif defined(HAS_FILENO_AND_ISATTY) && _LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 23 // !_LIBCPP_WIN32API
+#elif defined(HAS_FILENO_AND_ISATTY) && _LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 23
 
 _LIBCPP_DIAGNOSTIC_PUSH
 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmissing-prototypes")
 _LIBCPP_EXPORTED_FROM_ABI bool __is_posix_terminal(FILE* __stream) { return isatty(fileno(__stream)); }
 _LIBCPP_DIAGNOSTIC_POP
-#endif
+#endif // _WIN32
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/std_stream.h b/libcxx/src/std_stream.h
index 4b9d3a34b2441..32f23e78d4e69 100644
--- a/libcxx/src/std_stream.h
+++ b/libcxx/src/std_stream.h
@@ -57,7 +57,7 @@ class _LIBCPP_HIDDEN __stdinbuf : public basic_streambuf<_CharT, char_traits<_Ch
   bool __last_consumed_is_next_;
   bool __always_noconv_;
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   static constexpr bool __is_win32api_wide_char = !is_same_v<_CharT, char>;
 #else
   static constexpr bool __is_win32api_wide_char = false;
@@ -262,7 +262,7 @@ class _LIBCPP_HIDDEN __stdoutbuf : public basic_streambuf<_CharT, char_traits<_C
   state_type* __st_;
   bool __always_noconv_;
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   static constexpr bool __is_win32api_wide_char = !is_same_v<_CharT, char>;
 #else
   static constexpr bool __is_win32api_wide_char = false;
diff --git a/libcxx/src/string.cpp b/libcxx/src/string.cpp
index b55f9ff5c9a43..68ac35c228aeb 100644
--- a/libcxx/src/string.cpp
+++ b/libcxx/src/string.cpp
@@ -320,7 +320,7 @@ struct initial_string<wstring> {
 typedef int (*wide_printf)(wchar_t* __restrict, size_t, const wchar_t* __restrict, ...);
 
 inline wide_printf get_swprintf() {
-#  ifndef _LIBCPP_MSVCRT
+#  ifndef _LIBCPP_LIBC_MSVCRT
   return swprintf;
 #  else
   return static_cast<int(__cdecl*)(wchar_t* __restrict, size_t, const wchar_t* __restrict, ...)>(_snwprintf);
diff --git a/libcxx/src/support/win32/locale_win32.cpp b/libcxx/src/support/win32/locale_win32.cpp
index c5117564438a5..701793ae58354 100644
--- a/libcxx/src/support/win32/locale_win32.cpp
+++ b/libcxx/src/support/win32/locale_win32.cpp
@@ -298,7 +298,7 @@ const char* __get_locale_encoding(__locale_t loc) {
 //
 // Strtonum functions
 //
-#if !defined(_LIBCPP_MSVCRT)
+#if !defined(_LIBCPP_LIBC_MSVCRT)
 float __strtof(const char* nptr, char** endptr, __locale_t loc) {
   __locale_guard __current(loc);
   return std::strtof(nptr, endptr);
@@ -324,7 +324,7 @@ size_t __strftime(char* ret, size_t n, const char* format, const struct tm* tm,
 // Other functions
 //
 decltype(MB_CUR_MAX) __mb_len_max(__locale_t __l) {
-#if defined(_LIBCPP_MSVCRT)
+#if defined(_LIBCPP_LIBC_MSVCRT)
   return ::___mb_cur_max_l_func(__l);
 #else
   __locale_guard __current(__l);
@@ -387,7 +387,7 @@ size_t __mbsrtowcs(
 int __snprintf(char* ret, size_t n, __locale_t loc, const char* format, ...) {
   va_list ap;
   va_start(ap, format);
-#if defined(_LIBCPP_MSVCRT)
+#if defined(_LIBCPP_LIBC_MSVCRT)
   // FIXME: Remove usage of internal CRT function and globals.
   int result = ::__stdio_common_vsprintf(
       _CRT_INTERNAL_LOCAL_PRINTF_OPTIONS | _CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR, ret, n, format, loc, ap);
diff --git a/libcxx/src/system_error.cpp b/libcxx/src/system_error.cpp
index cf3d2c2926861..c6a38b5bacba9 100644
--- a/libcxx/src/system_error.cpp
+++ b/libcxx/src/system_error.cpp
@@ -21,7 +21,7 @@
 
 #include "include/config_elast.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  include <windows.h>
 #  include <winerror.h>
 #endif
@@ -29,7 +29,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 namespace {
 std::optional<errc> __win_err_to_errc(int err) {
@@ -149,7 +149,7 @@ constexpr size_t strerror_buff_size = 1024;
 
 string do_strerror_r(int ev);
 
-#  if defined(_LIBCPP_MSVCRT_LIKE)
+#  ifdef _WIN32
 string do_strerror_r(int ev) {
   char buffer[strerror_buff_size];
   if (::strerror_s(buffer, strerror_buff_size, ev) == 0)
@@ -272,7 +272,7 @@ class _LIBCPP_HIDDEN __system_error_category : public __do_message {
 const char* __system_error_category::name() const noexcept { return "system"; }
 
 string __system_error_category::message(int ev) const {
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
   std::string result;
   char* str               = nullptr;
   unsigned long num_chars = ::FormatMessageA(
@@ -304,7 +304,7 @@ string __system_error_category::message(int ev) const {
 }
 
 error_condition __system_error_category::default_error_condition(int ev) const noexcept {
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
   // Remap windows error codes to generic error codes if possible.
   if (ev == 0)
     return error_condition(0, generic_category());
diff --git a/libcxx/src/thread.cpp b/libcxx/src/thread.cpp
index d2aff1ee0c3fe..7b0890858e9d6 100644
--- a/libcxx/src/thread.cpp
+++ b/libcxx/src/thread.cpp
@@ -24,7 +24,7 @@
 #  pragma weak pthread_create // Do not create libpthread dependency
 #endif
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  include <windows.h>
 #endif
 
@@ -74,7 +74,7 @@ unsigned thread::hardware_concurrency() noexcept {
   if (result < 0)
     return 0;
   return static_cast<unsigned>(result);
-#elif defined(_LIBCPP_WIN32API)
+#elif defined(_WIN32)
   return static_cast<unsigned>(GetActiveProcessorCount(ALL_PROCESSOR_GROUPS));
 #else // defined(CTL_HW) && defined(HW_NCPU)
   // TODO: grovel through /proc or check cpuid on x86 and similar
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..0966e7dace413 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -450,7 +450,7 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
 }
 
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
-#    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
+#    ifdef _WIN32
 #      define USE_ALIGNED_ALLOC
 #    endif
 


From libcxx-commits at lists.llvm.org  Sun Jul  5 23:59:26 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 05 Jul 2026 23:59:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a4b524e.2020f6c2.1fdf61.12a4@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/207120

>From 83e34236209bc1f131a8b52c2b9c0f36ac183c21 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 2 Jul 2026 08:16:28 +0300
Subject: [PATCH 1/7] [libc++][ranges] Applied `[[nodiscard]]` to
 `views::zip_transform`

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.zip.transform

Towards #172124
---
 libcxx/include/__ranges/zip_transform_view.h  | 33 +++----
 .../range.zip.transform/nodiscard.verify.cpp  | 88 +++++++++++++++++++
 2 files changed, 105 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
index c56632a2608a1..ffa3e9f9c6dd2 100644
--- a/libcxx/include/__ranges/zip_transform_view.h
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -79,15 +79,15 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views)
       : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     return __iterator<true>(*this, __zip_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __zip_.end());
     } else {
@@ -95,7 +95,7 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -105,13 +105,13 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __zip_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __zip_.size();
@@ -184,7 +184,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) {
     return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_));
   }
@@ -233,7 +233,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -255,25 +255,26 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -307,14 +308,14 @@ class zip_transform_view<_Fn, _Views...>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -327,14 +328,14 @@ struct __fn {
   template <class _Fn>
     requires(move_constructible<decay_t<_Fn>> && regular_invocable<decay_t<_Fn>&> &&
              is_object_v<invoke_result_t<decay_t<_Fn>&>>)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
       noexcept(noexcept(auto(views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>))) {
     return views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>;
   }
 
   template <class _Fn, class... _Ranges>
     requires(sizeof...(_Ranges) > 0)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
       noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)))
           -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) {
     return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7d15d027f75be
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it  = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.zip_transform.sentinel]
+
+  auto st = ztv.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // [range.zip_transform.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform(Invocable<>{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform([](int x) { return x; }, range);
+}
\ No newline at end of file

>From 7595d99fcdce5c0f4856027608807187fd2a3713 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:32:19 +0300
Subject: [PATCH 2/7] Try to fix

---
 .../range.zip.transform/nodiscard.verify.cpp  | 124 ++++++++++++------
 .../range.adaptors/range_adaptor_types.h      |  27 ----
 libcxx/test/support/test_iterators.h          |  30 ++++-
 3 files changed, 112 insertions(+), 69 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 7d15d027f75be..0a85c8802011a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -14,17 +14,40 @@
 #include <utility>
 #include <vector>
 
-struct View : std::ranges::view_interface<View> {
+#include "test_iterators.h"
+
+struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
-  const int* begin() const;
-  volatile int* end();
-  const volatile int* end() const;
+  int* begin() const;
+  int* end();
+  int* end() const;
 
   int size() const;
 };
-static_assert(!std::ranges::common_range<View>);
-static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
-static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
+static_assert(std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
+
+struct ForwardSizedNonCommon {
+  int* buffer_      = nullptr;
+  std::size_t size_ = 0;
+
+  template <std::size_t N>
+  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
+
+  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
+
+  using iterator = forward_sized_iterator<int*>;
+  using sentinel = sized_sentinel<iterator>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
+};
+static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
+// static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {
@@ -32,51 +55,70 @@ struct Invocable {
 };
 
 void test() {
-  View range;
-  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+  CommonView commonRange;
+  std::ranges::zip_transform_view commonZtv{[](int x) { return x; }, commonRange};
+  static_assert(std::same_as<decltype(commonZtv.end()), decltype(commonZtv.begin())>);
 
   // [range.zip_transform.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.begin();
+  commonZtv.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).begin();
+  std::as_const(commonZtv).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.end();
+  commonZtv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).end();
+  std::as_const(commonZtv).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.size();
+  commonZtv.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).size();
+  std::as_const(commonZtv).size();
 
   // [range.zip_transform.iterator]
-
-  auto it  = ztv.begin();
-  auto cIt = std::as_const(ztv).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *cIt;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  cIt[0];
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  1 + it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - it;
+  {
+    auto it  = commonZtv.begin();
+    auto cIt = std::as_const(commonZtv).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *cIt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt[0];
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+  }
 
   // [range.zip_transform.sentinel]
-
-  auto st = ztv.end();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - st;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - it;
+  {
+    auto MakeTuple = [](auto&&... args) { return std::tuple(std::forward<decltype(args)>(args)...); };
+    int arr[]      = {1, 2, 3};
+    std::ranges::zip_transform_view nonCommonZtv{MakeTuple, std::views::iota(0, 3), ForwardSizedNonCommon(arr)};
+    static_assert(!std::ranges::common_range<decltype(nonCommonZtv)>);
+    static_assert(!std::same_as<decltype(nonCommonZtv.end()), decltype(nonCommonZtv.begin())>);
+    auto it  = nonCommonZtv.begin();
+    auto cIt = std::as_const(nonCommonZtv).begin();
+    auto st  = nonCommonZtv.end();
+    auto cSt = std::as_const(nonCommonZtv).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - cSt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cSt - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - cIt;
+  }
 
   // [range.zip_transform.overview]
 
@@ -84,5 +126,5 @@ void test() {
   std::views::zip_transform(Invocable<>{});
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::zip_transform([](int x) { return x; }, range);
-}
\ No newline at end of file
+  std::views::zip_transform([](int x) { return x; }, commonRange);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 0b654566e35b8..429c94e254303 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,33 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-template <class Base = int*>
-struct forward_sized_iterator {
-  Base it_ = nullptr;
-
-  using iterator_category = std::forward_iterator_tag;
-  using value_type        = int;
-  using difference_type   = std::intptr_t;
-  using pointer           = Base;
-  using reference         = decltype(*Base{});
-
-  forward_sized_iterator() = default;
-  constexpr forward_sized_iterator(Base it) : it_(it) {}
-
-  constexpr reference operator*() const { return *it_; }
-
-  constexpr forward_sized_iterator& operator++() {
-    ++it_;
-    return *this;
-  }
-  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
-
-  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
-
-  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
-    return x.it_ - y.it_;
-  }
-};
 static_assert(std::forward_iterator<forward_sized_iterator<>>);
 static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 4fc8345c2dcef..6a721a4feaf9f 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,6 +1175,35 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
+
+template <class Base = int*>
+struct forward_sized_iterator {
+  Base it_ = nullptr;
+
+  using iterator_category = std::forward_iterator_tag;
+  using value_type        = int;
+  using difference_type   = std::intptr_t;
+  using pointer           = Base;
+  using reference         = decltype(*Base{});
+
+  forward_sized_iterator() = default;
+  constexpr forward_sized_iterator(Base it) : it_(it) {}
+
+  constexpr reference operator*() const { return *it_; }
+
+  constexpr forward_sized_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
+
+  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
+    return x.it_ - y.it_;
+  }
+};
+
 namespace adl {
 
 class Iterator {
@@ -1880,5 +1909,4 @@ using cpp20_input_iterator_list =
 #endif
 } // namespace types
 
-
 #endif // SUPPORT_TEST_ITERATORS_H

>From b21a63ee3ba7f9571091d219f0ff6b939658eebb Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:34:11 +0300
Subject: [PATCH 3/7] FIx formatting

---
 libcxx/test/support/test_iterators.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 6a721a4feaf9f..a75cb578b2e91 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,7 +1175,6 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
-
 template <class Base = int*>
 struct forward_sized_iterator {
   Base it_ = nullptr;

>From edf859970903a239119d1a4c537c41b1e6a9d7e4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:37:10 +0300
Subject: [PATCH 4/7] Try to fix

---
 libcxx/test/support/test_iterators.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index a75cb578b2e91..f443abcddb3bb 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1202,6 +1202,8 @@ struct forward_sized_iterator {
     return x.it_ - y.it_;
   }
 };
+static_assert(std::forward_iterator<forward_sized_iterator<>>);
+static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
 namespace adl {
 

>From 675b4611e20d7f9f482d3622ec31c0bdb23271a8 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:38:10 +0300
Subject: [PATCH 5/7] Fix again

---
 libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 429c94e254303..41ca28fa905e4 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,9 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-static_assert(std::forward_iterator<forward_sized_iterator<>>);
-static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
-
 using ForwardSizedView = BasicView<forward_sized_iterator<>>;
 static_assert(std::ranges::forward_range<ForwardSizedView>);
 static_assert(std::ranges::sized_range<ForwardSizedView>);

>From 55976947871a096595e4f623ff833db0ca37e6b1 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:39:15 +0300
Subject: [PATCH 6/7] Fix more

---
 .../range.adaptors/range.zip.transform/nodiscard.verify.cpp    | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 0a85c8802011a..354178b2e581b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <vector>
 
 #include "test_iterators.h"
+#include "test_range.h"
 
 struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
@@ -47,7 +48,7 @@ static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
 static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
 static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
 static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
-// static_assert(simple_view<ForwardSizedNonCommon>);
+static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {

>From d699200115d369fa910d8be7321753ead6748cbb Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 09:59:05 +0300
Subject: [PATCH 7/7] Fix after fix

---
 .../range.adaptors/range.zip.transform/nodiscard.verify.cpp     | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 354178b2e581b..e681d338dc786 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -15,7 +15,6 @@
 #include <vector>
 
 #include "test_iterators.h"
-#include "test_range.h"
 
 struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
@@ -48,7 +47,6 @@ static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
 static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
 static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
 static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
-static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {


From libcxx-commits at lists.llvm.org  Sun Jul  5 23:59:32 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 05 Jul 2026 23:59:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a4b5254.92d2873c.38c951.e175@mx.google.com>


https://github.com/Zingam updated https://github.com/llvm/llvm-project/pull/207120

>From 83e34236209bc1f131a8b52c2b9c0f36ac183c21 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 2 Jul 2026 08:16:28 +0300
Subject: [PATCH 1/7] [libc++][ranges] Applied `[[nodiscard]]` to
 `views::zip_transform`

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.zip.transform

Towards #172124
---
 libcxx/include/__ranges/zip_transform_view.h  | 33 +++----
 .../range.zip.transform/nodiscard.verify.cpp  | 88 +++++++++++++++++++
 2 files changed, 105 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
index c56632a2608a1..ffa3e9f9c6dd2 100644
--- a/libcxx/include/__ranges/zip_transform_view.h
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -79,15 +79,15 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views)
       : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     return __iterator<true>(*this, __zip_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __zip_.end());
     } else {
@@ -95,7 +95,7 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -105,13 +105,13 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __zip_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __zip_.size();
@@ -184,7 +184,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) {
     return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_));
   }
@@ -233,7 +233,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -255,25 +255,26 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -307,14 +308,14 @@ class zip_transform_view<_Fn, _Views...>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -327,14 +328,14 @@ struct __fn {
   template <class _Fn>
     requires(move_constructible<decay_t<_Fn>> && regular_invocable<decay_t<_Fn>&> &&
              is_object_v<invoke_result_t<decay_t<_Fn>&>>)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
       noexcept(noexcept(auto(views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>))) {
     return views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>;
   }
 
   template <class _Fn, class... _Ranges>
     requires(sizeof...(_Ranges) > 0)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
       noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)))
           -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) {
     return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..7d15d027f75be
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+
+  int size() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  View range;
+  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ztv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ztv).size();
+
+  // [range.zip_transform.iterator]
+
+  auto it  = ztv.begin();
+  auto cIt = std::as_const(ztv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cIt;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cIt[0];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.zip_transform.sentinel]
+
+  auto st = ztv.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // [range.zip_transform.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform(Invocable<>{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform([](int x) { return x; }, range);
+}
\ No newline at end of file

>From 7595d99fcdce5c0f4856027608807187fd2a3713 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:32:19 +0300
Subject: [PATCH 2/7] Try to fix

---
 .../range.zip.transform/nodiscard.verify.cpp  | 124 ++++++++++++------
 .../range.adaptors/range_adaptor_types.h      |  27 ----
 libcxx/test/support/test_iterators.h          |  30 ++++-
 3 files changed, 112 insertions(+), 69 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 7d15d027f75be..0a85c8802011a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -14,17 +14,40 @@
 #include <utility>
 #include <vector>
 
-struct View : std::ranges::view_interface<View> {
+#include "test_iterators.h"
+
+struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
-  const int* begin() const;
-  volatile int* end();
-  const volatile int* end() const;
+  int* begin() const;
+  int* end();
+  int* end() const;
 
   int size() const;
 };
-static_assert(!std::ranges::common_range<View>);
-static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
-static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
+static_assert(std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
+
+struct ForwardSizedNonCommon {
+  int* buffer_      = nullptr;
+  std::size_t size_ = 0;
+
+  template <std::size_t N>
+  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
+
+  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
+
+  using iterator = forward_sized_iterator<int*>;
+  using sentinel = sized_sentinel<iterator>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
+};
+static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
+// static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {
@@ -32,51 +55,70 @@ struct Invocable {
 };
 
 void test() {
-  View range;
-  std::ranges::zip_transform_view ztv{[](int x) { return x; }, range};
+  CommonView commonRange;
+  std::ranges::zip_transform_view commonZtv{[](int x) { return x; }, commonRange};
+  static_assert(std::same_as<decltype(commonZtv.end()), decltype(commonZtv.begin())>);
 
   // [range.zip_transform.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.begin();
+  commonZtv.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).begin();
+  std::as_const(commonZtv).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.end();
+  commonZtv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).end();
+  std::as_const(commonZtv).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ztv.size();
+  commonZtv.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ztv).size();
+  std::as_const(commonZtv).size();
 
   // [range.zip_transform.iterator]
-
-  auto it  = ztv.begin();
-  auto cIt = std::as_const(ztv).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *cIt;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  cIt[0];
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  1 + it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - 1;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - it;
+  {
+    auto it  = commonZtv.begin();
+    auto cIt = std::as_const(commonZtv).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *cIt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt[0];
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+  }
 
   // [range.zip_transform.sentinel]
-
-  auto st = ztv.end();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it - st;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - it;
+  {
+    auto MakeTuple = [](auto&&... args) { return std::tuple(std::forward<decltype(args)>(args)...); };
+    int arr[]      = {1, 2, 3};
+    std::ranges::zip_transform_view nonCommonZtv{MakeTuple, std::views::iota(0, 3), ForwardSizedNonCommon(arr)};
+    static_assert(!std::ranges::common_range<decltype(nonCommonZtv)>);
+    static_assert(!std::same_as<decltype(nonCommonZtv.end()), decltype(nonCommonZtv.begin())>);
+    auto it  = nonCommonZtv.begin();
+    auto cIt = std::as_const(nonCommonZtv).begin();
+    auto st  = nonCommonZtv.end();
+    auto cSt = std::as_const(nonCommonZtv).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - cSt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cSt - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - cIt;
+  }
 
   // [range.zip_transform.overview]
 
@@ -84,5 +126,5 @@ void test() {
   std::views::zip_transform(Invocable<>{});
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::zip_transform([](int x) { return x; }, range);
-}
\ No newline at end of file
+  std::views::zip_transform([](int x) { return x; }, commonRange);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 0b654566e35b8..429c94e254303 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,33 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-template <class Base = int*>
-struct forward_sized_iterator {
-  Base it_ = nullptr;
-
-  using iterator_category = std::forward_iterator_tag;
-  using value_type        = int;
-  using difference_type   = std::intptr_t;
-  using pointer           = Base;
-  using reference         = decltype(*Base{});
-
-  forward_sized_iterator() = default;
-  constexpr forward_sized_iterator(Base it) : it_(it) {}
-
-  constexpr reference operator*() const { return *it_; }
-
-  constexpr forward_sized_iterator& operator++() {
-    ++it_;
-    return *this;
-  }
-  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
-
-  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
-
-  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
-    return x.it_ - y.it_;
-  }
-};
 static_assert(std::forward_iterator<forward_sized_iterator<>>);
 static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 4fc8345c2dcef..6a721a4feaf9f 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,6 +1175,35 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
+
+template <class Base = int*>
+struct forward_sized_iterator {
+  Base it_ = nullptr;
+
+  using iterator_category = std::forward_iterator_tag;
+  using value_type        = int;
+  using difference_type   = std::intptr_t;
+  using pointer           = Base;
+  using reference         = decltype(*Base{});
+
+  forward_sized_iterator() = default;
+  constexpr forward_sized_iterator(Base it) : it_(it) {}
+
+  constexpr reference operator*() const { return *it_; }
+
+  constexpr forward_sized_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
+
+  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
+    return x.it_ - y.it_;
+  }
+};
+
 namespace adl {
 
 class Iterator {
@@ -1880,5 +1909,4 @@ using cpp20_input_iterator_list =
 #endif
 } // namespace types
 
-
 #endif // SUPPORT_TEST_ITERATORS_H

>From b21a63ee3ba7f9571091d219f0ff6b939658eebb Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:34:11 +0300
Subject: [PATCH 3/7] FIx formatting

---
 libcxx/test/support/test_iterators.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 6a721a4feaf9f..a75cb578b2e91 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,7 +1175,6 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
-
 template <class Base = int*>
 struct forward_sized_iterator {
   Base it_ = nullptr;

>From edf859970903a239119d1a4c537c41b1e6a9d7e4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:37:10 +0300
Subject: [PATCH 4/7] Try to fix

---
 libcxx/test/support/test_iterators.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index a75cb578b2e91..f443abcddb3bb 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1202,6 +1202,8 @@ struct forward_sized_iterator {
     return x.it_ - y.it_;
   }
 };
+static_assert(std::forward_iterator<forward_sized_iterator<>>);
+static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
 
 namespace adl {
 

>From 675b4611e20d7f9f482d3622ec31c0bdb23271a8 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:38:10 +0300
Subject: [PATCH 5/7] Fix again

---
 libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 429c94e254303..41ca28fa905e4 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,9 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-static_assert(std::forward_iterator<forward_sized_iterator<>>);
-static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
-
 using ForwardSizedView = BasicView<forward_sized_iterator<>>;
 static_assert(std::ranges::forward_range<ForwardSizedView>);
 static_assert(std::ranges::sized_range<ForwardSizedView>);

>From 55976947871a096595e4f623ff833db0ca37e6b1 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 08:39:15 +0300
Subject: [PATCH 6/7] Fix more

---
 .../range.adaptors/range.zip.transform/nodiscard.verify.cpp    | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 0a85c8802011a..354178b2e581b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <vector>
 
 #include "test_iterators.h"
+#include "test_range.h"
 
 struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
@@ -47,7 +48,7 @@ static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
 static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
 static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
 static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
-// static_assert(simple_view<ForwardSizedNonCommon>);
+static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {

>From d699200115d369fa910d8be7321753ead6748cbb Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Jul 2026 09:59:05 +0300
Subject: [PATCH 7/7] Fix after fix

---
 .../range.adaptors/range.zip.transform/nodiscard.verify.cpp     | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index 354178b2e581b..e681d338dc786 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -15,7 +15,6 @@
 #include <vector>
 
 #include "test_iterators.h"
-#include "test_range.h"
 
 struct CommonView : std::ranges::view_interface<CommonView> {
   int* begin();
@@ -48,7 +47,6 @@ static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
 static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
 static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
 static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
-static_assert(simple_view<ForwardSizedNonCommon>);
 
 template <class... Args>
 struct Invocable {


From libcxx-commits at lists.llvm.org  Sun Jul  5 23:59:35 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 05 Jul 2026 23:59:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a4b5257.bf38ae28.358429.afae@mx.google.com>


https://github.com/Zingam ready_for_review https://github.com/llvm/llvm-project/pull/207120

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:00:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 00:00:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a4b5279.da8bafd3.2e1688.2632@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

<details>
<summary>Changes</summary>

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.zip.transform

Towards #<!-- -->172124

---
Full diff: https://github.com/llvm/llvm-project/pull/207120.diff


4 Files Affected:

- (modified) libcxx/include/__ranges/zip_transform_view.h (+17-16) 
- (added) libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp (+129) 
- (modified) libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h (-30) 
- (modified) libcxx/test/support/test_iterators.h (+30-1) 


``````````diff
diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
index c56632a2608a1..ffa3e9f9c6dd2 100644
--- a/libcxx/include/__ranges/zip_transform_view.h
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -79,15 +79,15 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views)
       : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     return __iterator<true>(*this, __zip_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __zip_.end());
     } else {
@@ -95,7 +95,7 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -105,13 +105,13 @@ class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views.
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __zip_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __zip_.size();
@@ -184,7 +184,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) {
     return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_));
   }
@@ -233,7 +233,7 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -255,25 +255,26 @@ class zip_transform_view<_Fn, _Views...>::__iterator
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -307,14 +308,14 @@ class zip_transform_view<_Fn, _Views...>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -327,14 +328,14 @@ struct __fn {
   template <class _Fn>
     requires(move_constructible<decay_t<_Fn>> && regular_invocable<decay_t<_Fn>&> &&
              is_object_v<invoke_result_t<decay_t<_Fn>&>>)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
       noexcept(noexcept(auto(views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>))) {
     return views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>;
   }
 
   template <class _Fn, class... _Ranges>
     requires(sizeof...(_Ranges) > 0)
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
       noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)))
           -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) {
     return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..e681d338dc786
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_iterators.h"
+
+struct CommonView : std::ranges::view_interface<CommonView> {
+  int* begin();
+  int* begin() const;
+  int* end();
+  int* end() const;
+
+  int size() const;
+};
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
+static_assert(std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
+
+struct ForwardSizedNonCommon {
+  int* buffer_      = nullptr;
+  std::size_t size_ = 0;
+
+  template <std::size_t N>
+  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
+
+  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
+
+  using iterator = forward_sized_iterator<int*>;
+  using sentinel = sized_sentinel<iterator>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
+};
+static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
+
+template <class... Args>
+struct Invocable {
+  int operator()(Args...) const { return 5; }
+};
+
+void test() {
+  CommonView commonRange;
+  std::ranges::zip_transform_view commonZtv{[](int x) { return x; }, commonRange};
+  static_assert(std::same_as<decltype(commonZtv.end()), decltype(commonZtv.begin())>);
+
+  // [range.zip_transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  commonZtv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(commonZtv).begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  commonZtv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(commonZtv).end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  commonZtv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(commonZtv).size();
+
+  // [range.zip_transform.iterator]
+  {
+    auto it  = commonZtv.begin();
+    auto cIt = std::as_const(commonZtv).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *cIt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt[0];
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+  }
+
+  // [range.zip_transform.sentinel]
+  {
+    auto MakeTuple = [](auto&&... args) { return std::tuple(std::forward<decltype(args)>(args)...); };
+    int arr[]      = {1, 2, 3};
+    std::ranges::zip_transform_view nonCommonZtv{MakeTuple, std::views::iota(0, 3), ForwardSizedNonCommon(arr)};
+    static_assert(!std::ranges::common_range<decltype(nonCommonZtv)>);
+    static_assert(!std::same_as<decltype(nonCommonZtv.end()), decltype(nonCommonZtv.begin())>);
+    auto it  = nonCommonZtv.begin();
+    auto cIt = std::as_const(nonCommonZtv).begin();
+    auto st  = nonCommonZtv.end();
+    auto cSt = std::as_const(nonCommonZtv).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - cSt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cSt - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - cIt;
+  }
+
+  // [range.zip_transform.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform(Invocable<>{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::zip_transform([](int x) { return x; }, commonRange);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 0b654566e35b8..41ca28fa905e4 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -208,36 +208,6 @@ struct BasicView : IntBufferView {
   }
 };
 
-template <class Base = int*>
-struct forward_sized_iterator {
-  Base it_ = nullptr;
-
-  using iterator_category = std::forward_iterator_tag;
-  using value_type        = int;
-  using difference_type   = std::intptr_t;
-  using pointer           = Base;
-  using reference         = decltype(*Base{});
-
-  forward_sized_iterator() = default;
-  constexpr forward_sized_iterator(Base it) : it_(it) {}
-
-  constexpr reference operator*() const { return *it_; }
-
-  constexpr forward_sized_iterator& operator++() {
-    ++it_;
-    return *this;
-  }
-  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
-
-  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
-
-  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
-    return x.it_ - y.it_;
-  }
-};
-static_assert(std::forward_iterator<forward_sized_iterator<>>);
-static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
-
 using ForwardSizedView = BasicView<forward_sized_iterator<>>;
 static_assert(std::ranges::forward_range<ForwardSizedView>);
 static_assert(std::ranges::sized_range<ForwardSizedView>);
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 4fc8345c2dcef..f443abcddb3bb 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1175,6 +1175,36 @@ class sized_sentinel {
 template <class It>
 sized_sentinel(It) -> sized_sentinel<It>;
 
+template <class Base = int*>
+struct forward_sized_iterator {
+  Base it_ = nullptr;
+
+  using iterator_category = std::forward_iterator_tag;
+  using value_type        = int;
+  using difference_type   = std::intptr_t;
+  using pointer           = Base;
+  using reference         = decltype(*Base{});
+
+  forward_sized_iterator() = default;
+  constexpr forward_sized_iterator(Base it) : it_(it) {}
+
+  constexpr reference operator*() const { return *it_; }
+
+  constexpr forward_sized_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
+
+  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
+    return x.it_ - y.it_;
+  }
+};
+static_assert(std::forward_iterator<forward_sized_iterator<>>);
+static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
+
 namespace adl {
 
 class Iterator {
@@ -1880,5 +1910,4 @@ using cpp20_input_iterator_list =
 #endif
 } // namespace types
 
-
 #endif // SUPPORT_TEST_ITERATORS_H

``````````

</details>


https://github.com/llvm/llvm-project/pull/207120

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:00:50 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Mon, 06 Jul 2026 00:00:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use std::to_chars to format
 thread::id and canonicalize the representation across platforms (PR
 #181624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/181624@github.com>
Message-ID: <6a4b52a2.8e75e0cb.2e0812.3c53@mx.google.com>


mstorsjo wrote:

This removed some transitive includes, which affected some projects. This isn't a big deal in practice, but if we still have the `_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23` flag, we probably should cover these headers by that as well? (I wouldn't use it myself, but it would seem consistent to have that flag keep these includes as well.)

https://github.com/llvm/llvm-project/pull/181624

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:00:52 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 00:00:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove _LIBCPP_SHORT_WCHAR (PR
	#207562)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207562@github.com>
Message-ID: <6a4b52a4.331483e7.5bc1e.ad58@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207562

>From 599d5dc30220922632fc994c7bd2ce59c0b122f7 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 27 Jun 2026 14:45:50 +0200
Subject: [PATCH] [libc++] Remove _LIBCPP_SHORT_WCHAR

---
 libcxx/include/__config           |   6 -
 libcxx/include/__cxx03/__config   |   6 -
 libcxx/include/print              |  19 +--
 libcxx/src/locale.cpp             | 247 ++++++++++++------------------
 libcxx/test/support/test_macros.h |   2 +-
 5 files changed, 103 insertions(+), 177 deletions(-)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..9c3b569861db7 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -44,7 +44,6 @@
 
 #  if defined(_WIN32)
 #    define _LIBCPP_WIN32API
-#    define _LIBCPP_SHORT_WCHAR 1
 // Both MinGW and native MSVC provide a "MSVC"-like environment
 #    define _LIBCPP_MSVCRT_LIKE
 // If mingw not explicitly detected, assume using MS C runtime only if
@@ -57,11 +56,6 @@
 #    define _LIBCPP_HAS_OPEN_WITH_WCHAR 0
 #  endif // defined(_WIN32)
 
-#  if defined(_AIX) && !defined(__64BIT__)
-// The size of wchar is 2 byte on 32-bit mode on AIX.
-#    define _LIBCPP_SHORT_WCHAR 1
-#  endif
-
 // Libc++ supports various implementations of std::random_device.
 //
 // _LIBCPP_USING_DEV_RANDOM
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 70a4f5cff9138..6679494a1f936 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -221,7 +221,6 @@ _LIBCPP_HARDENING_MODE_DEBUG
 
 #  if defined(_WIN32)
 #    define _LIBCPP_WIN32API
-#    define _LIBCPP_SHORT_WCHAR 1
 // Both MinGW and native MSVC provide a "MSVC"-like environment
 #    define _LIBCPP_MSVCRT_LIKE
 // If mingw not explicitly detected, assume using MS C runtime only if
@@ -232,11 +231,6 @@ _LIBCPP_HARDENING_MODE_DEBUG
 #    define _LIBCPP_HAS_OPEN_WITH_WCHAR
 #  endif // defined(_WIN32)
 
-#  if defined(_AIX) && !defined(__64BIT__)
-// The size of wchar is 2 byte on 32-bit mode on AIX.
-#    define _LIBCPP_SHORT_WCHAR 1
-#  endif
-
 // Libc++ supports various implementations of std::random_device.
 //
 // _LIBCPP_USING_DEV_RANDOM
diff --git a/libcxx/include/print b/libcxx/include/print
index cf62743846f42..d30d0be0130c2 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -89,27 +89,20 @@ namespace __unicode {
 // The names of these concepts are modelled after P2728R0, but the
 // implementation is not. char16_t may contain 32-bits so depending on the
 // number of bits is an issue.
-#      ifdef _LIBCPP_SHORT_WCHAR
 template <class _Tp>
 concept __utf16_code_unit =
     same_as<_Tp, char16_t>
-#        if _LIBCPP_HAS_WIDE_CHARACTERS
-    || same_as<_Tp, wchar_t>
-#        endif
+#      if _LIBCPP_HAS_WIDE_CHARACTERS
+    || (sizeof(wchar_t) == sizeof(char16_t) && same_as<_Tp, wchar_t>)
+#      endif
     ;
 template <class _Tp>
-concept __utf32_code_unit = same_as<_Tp, char32_t>;
-#      else // _LIBCPP_SHORT_WCHAR
-template <class _Tp>
-concept __utf16_code_unit = same_as<_Tp, char16_t>;
-template <class _Tp>
 concept __utf32_code_unit =
     same_as<_Tp, char32_t>
-#        if _LIBCPP_HAS_WIDE_CHARACTERS
-    || same_as<_Tp, wchar_t>
-#        endif
+#      if _LIBCPP_HAS_WIDE_CHARACTERS
+    || (sizeof(wchar_t) == sizeof(char32_t) && same_as<_Tp, wchar_t>)
+#      endif
     ;
-#      endif // _LIBCPP_SHORT_WCHAR
 
 // Pass by reference since an output_iterator may not be copyable.
 template <class _OutIt>
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index b0a69ab74f94c..8393faea3c140 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -2913,6 +2913,28 @@ int codecvt<char32_t, char8_t, mbstate_t>::do_max_length() const noexcept { retu
 // __codecvt_utf8<wchar_t>
 
 #if _LIBCPP_HAS_WIDE_CHARACTERS
+
+constexpr bool is_short_wchar = sizeof(wchar_t) == sizeof(uint16_t);
+using underlying_wchar_t      = conditional_t<is_short_wchar, uint16_t, uint32_t>;
+static_assert(sizeof(underlying_wchar_t) == sizeof(wchar_t));
+
+template <auto callable>
+struct call {
+  template <class... Args>
+  static auto operator()(Args&&... args) -> decltype(callable(std::forward<Args>(args)...)) {
+    return callable(std::forward<Args>(args)...);
+  }
+};
+
+template <auto... Args, class... Args2>
+auto overload(Args2&&... args) {
+  struct overload_t : call<Args>... {
+    using call<Args>::operator()...;
+  };
+
+  return overload_t{}(std::forward<Args2>(args)...);
+}
+
 __codecvt_utf8<wchar_t>::result __codecvt_utf8<wchar_t>::do_out(
     state_type&,
     const intern_type* frm,
@@ -2921,25 +2943,15 @@ __codecvt_utf8<wchar_t>::result __codecvt_utf8<wchar_t>::do_out(
     extern_type* to,
     extern_type* to_end,
     extern_type*& to_nxt) const {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  const uint16_t* _frm     = reinterpret_cast<const uint16_t*>(frm);
-  const uint16_t* _frm_end = reinterpret_cast<const uint16_t*>(frm_end);
-  const uint16_t* _frm_nxt = _frm;
-#  else
-  const uint32_t* _frm     = reinterpret_cast<const uint32_t*>(frm);
-  const uint32_t* _frm_end = reinterpret_cast<const uint32_t*>(frm_end);
-  const uint32_t* _frm_nxt = _frm;
-#  endif
-  uint8_t* _to     = reinterpret_cast<uint8_t*>(to);
-  uint8_t* _to_end = reinterpret_cast<uint8_t*>(to_end);
-  uint8_t* _to_nxt = _to;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  result r = ucs2_to_utf8(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  result r = ucs4_to_utf8(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
-  frm_nxt = frm + (_frm_nxt - _frm);
-  to_nxt  = to + (_to_nxt - _to);
+  const auto* _frm     = reinterpret_cast<const underlying_wchar_t*>(frm);
+  const auto* _frm_end = reinterpret_cast<const underlying_wchar_t*>(frm_end);
+  const auto* _frm_nxt = _frm;
+  uint8_t* _to         = reinterpret_cast<uint8_t*>(to);
+  uint8_t* _to_end     = reinterpret_cast<uint8_t*>(to_end);
+  uint8_t* _to_nxt     = _to;
+  result r = overload<ucs2_to_utf8, ucs4_to_utf8>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
+  frm_nxt  = frm + (_frm_nxt - _frm);
+  to_nxt   = to + (_to_nxt - _to);
   return r;
 }
 
@@ -2954,19 +2966,12 @@ __codecvt_utf8<wchar_t>::result __codecvt_utf8<wchar_t>::do_in(
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
   const uint8_t* _frm_nxt = _frm;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  uint16_t* _to     = reinterpret_cast<uint16_t*>(to);
-  uint16_t* _to_end = reinterpret_cast<uint16_t*>(to_end);
-  uint16_t* _to_nxt = _to;
-  result r          = utf8_to_ucs2(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  uint32_t* _to     = reinterpret_cast<uint32_t*>(to);
-  uint32_t* _to_end = reinterpret_cast<uint32_t*>(to_end);
-  uint32_t* _to_nxt = _to;
-  result r          = utf8_to_ucs4(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
-  frm_nxt = frm + (_frm_nxt - _frm);
-  to_nxt  = to + (_to_nxt - _to);
+  auto* _to               = reinterpret_cast<underlying_wchar_t*>(to);
+  auto* _to_end           = reinterpret_cast<underlying_wchar_t*>(to_end);
+  auto* _to_nxt           = _to;
+  result r = overload<utf8_to_ucs2, utf8_to_ucs4>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
+  frm_nxt  = frm + (_frm_nxt - _frm);
+  to_nxt   = to + (_to_nxt - _to);
   return r;
 }
 
@@ -2984,24 +2989,18 @@ int __codecvt_utf8<wchar_t>::do_length(
     state_type&, const extern_type* frm, const extern_type* frm_end, size_t mx) const {
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  return utf8_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  else
-  return utf8_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  endif
+  if constexpr (is_short_wchar)
+    return utf8_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
+  else
+    return utf8_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
 }
 
 _LIBCPP_SUPPRESS_DEPRECATED_PUSH
 int __codecvt_utf8<wchar_t>::do_max_length() const noexcept {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  if (__mode_ & consume_header)
-    return 6;
-  return 3;
-#  else
-  if (__mode_ & consume_header)
-    return 7;
-  return 4;
-#  endif
+  if constexpr (is_short_wchar)
+    return (__mode_ & consume_header) ? 6 : 3;
+  else
+    return (__mode_ & consume_header) ? 7 : 4;
 }
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
@@ -3150,23 +3149,14 @@ __codecvt_utf16<wchar_t, false>::result __codecvt_utf16<wchar_t, false>::do_out(
     extern_type* to,
     extern_type* to_end,
     extern_type*& to_nxt) const {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  const uint16_t* _frm     = reinterpret_cast<const uint16_t*>(frm);
-  const uint16_t* _frm_end = reinterpret_cast<const uint16_t*>(frm_end);
-  const uint16_t* _frm_nxt = _frm;
-#  else
-  const uint32_t* _frm     = reinterpret_cast<const uint32_t*>(frm);
-  const uint32_t* _frm_end = reinterpret_cast<const uint32_t*>(frm_end);
-  const uint32_t* _frm_nxt = _frm;
-#  endif
-  uint8_t* _to     = reinterpret_cast<uint8_t*>(to);
-  uint8_t* _to_end = reinterpret_cast<uint8_t*>(to_end);
-  uint8_t* _to_nxt = _to;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  result r = ucs2_to_utf16be(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  result r = ucs4_to_utf16be(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
+  const auto* _frm     = reinterpret_cast<const underlying_wchar_t*>(frm);
+  const auto* _frm_end = reinterpret_cast<const underlying_wchar_t*>(frm_end);
+  const auto* _frm_nxt = _frm;
+  uint8_t* _to         = reinterpret_cast<uint8_t*>(to);
+  uint8_t* _to_end     = reinterpret_cast<uint8_t*>(to_end);
+  uint8_t* _to_nxt     = _to;
+  result r =
+      overload<ucs2_to_utf16be, ucs4_to_utf16be>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
   frm_nxt = frm + (_frm_nxt - _frm);
   to_nxt  = to + (_to_nxt - _to);
   return r;
@@ -3183,17 +3173,11 @@ __codecvt_utf16<wchar_t, false>::result __codecvt_utf16<wchar_t, false>::do_in(
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
   const uint8_t* _frm_nxt = _frm;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  uint16_t* _to     = reinterpret_cast<uint16_t*>(to);
-  uint16_t* _to_end = reinterpret_cast<uint16_t*>(to_end);
-  uint16_t* _to_nxt = _to;
-  result r          = utf16be_to_ucs2(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  uint32_t* _to     = reinterpret_cast<uint32_t*>(to);
-  uint32_t* _to_end = reinterpret_cast<uint32_t*>(to_end);
-  uint32_t* _to_nxt = _to;
-  result r          = utf16be_to_ucs4(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
+  auto* _to               = reinterpret_cast<underlying_wchar_t*>(to);
+  auto* _to_end           = reinterpret_cast<underlying_wchar_t*>(to_end);
+  auto* _to_nxt           = _to;
+  result r =
+      overload<utf16be_to_ucs2, utf16be_to_ucs4>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
   frm_nxt = frm + (_frm_nxt - _frm);
   to_nxt  = to + (_to_nxt - _to);
   return r;
@@ -3213,23 +3197,17 @@ int __codecvt_utf16<wchar_t, false>::do_length(
     state_type&, const extern_type* frm, const extern_type* frm_end, size_t mx) const {
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  return utf16be_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  else
-  return utf16be_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  endif
+  if constexpr (is_short_wchar)
+    return utf16be_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
+  else
+    return utf16be_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
 }
 
 int __codecvt_utf16<wchar_t, false>::do_max_length() const noexcept {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  if (__mode_ & consume_header)
-    return 4;
-  return 2;
-#  else
-  if (__mode_ & consume_header)
-    return 6;
-  return 4;
-#  endif
+  if constexpr (is_short_wchar)
+    return (__mode_ & consume_header) ? 4 : 2;
+  else
+    return (__mode_ & consume_header) ? 6 : 4;
 }
 
 // __codecvt_utf16<wchar_t, true>
@@ -3242,23 +3220,14 @@ __codecvt_utf16<wchar_t, true>::result __codecvt_utf16<wchar_t, true>::do_out(
     extern_type* to,
     extern_type* to_end,
     extern_type*& to_nxt) const {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  const uint16_t* _frm     = reinterpret_cast<const uint16_t*>(frm);
-  const uint16_t* _frm_end = reinterpret_cast<const uint16_t*>(frm_end);
-  const uint16_t* _frm_nxt = _frm;
-#  else
-  const uint32_t* _frm     = reinterpret_cast<const uint32_t*>(frm);
-  const uint32_t* _frm_end = reinterpret_cast<const uint32_t*>(frm_end);
-  const uint32_t* _frm_nxt = _frm;
-#  endif
-  uint8_t* _to     = reinterpret_cast<uint8_t*>(to);
-  uint8_t* _to_end = reinterpret_cast<uint8_t*>(to_end);
-  uint8_t* _to_nxt = _to;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  result r = ucs2_to_utf16le(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  result r = ucs4_to_utf16le(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
+  const auto* _frm     = reinterpret_cast<const underlying_wchar_t*>(frm);
+  const auto* _frm_end = reinterpret_cast<const underlying_wchar_t*>(frm_end);
+  const auto* _frm_nxt = _frm;
+  uint8_t* _to         = reinterpret_cast<uint8_t*>(to);
+  uint8_t* _to_end     = reinterpret_cast<uint8_t*>(to_end);
+  uint8_t* _to_nxt     = _to;
+  result r =
+      overload<ucs2_to_utf16le, ucs4_to_utf16le>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
   frm_nxt = frm + (_frm_nxt - _frm);
   to_nxt  = to + (_to_nxt - _to);
   return r;
@@ -3275,17 +3244,11 @@ __codecvt_utf16<wchar_t, true>::result __codecvt_utf16<wchar_t, true>::do_in(
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
   const uint8_t* _frm_nxt = _frm;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  uint16_t* _to     = reinterpret_cast<uint16_t*>(to);
-  uint16_t* _to_end = reinterpret_cast<uint16_t*>(to_end);
-  uint16_t* _to_nxt = _to;
-  result r          = utf16le_to_ucs2(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  else
-  uint32_t* _to     = reinterpret_cast<uint32_t*>(to);
-  uint32_t* _to_end = reinterpret_cast<uint32_t*>(to_end);
-  uint32_t* _to_nxt = _to;
-  result r          = utf16le_to_ucs4(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-#  endif
+  auto* _to               = reinterpret_cast<underlying_wchar_t*>(to);
+  auto* _to_end           = reinterpret_cast<underlying_wchar_t*>(to_end);
+  auto* _to_nxt           = _to;
+  result r =
+      overload<utf16le_to_ucs2, utf16le_to_ucs4>(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
   frm_nxt = frm + (_frm_nxt - _frm);
   to_nxt  = to + (_to_nxt - _to);
   return r;
@@ -3305,23 +3268,17 @@ int __codecvt_utf16<wchar_t, true>::do_length(
     state_type&, const extern_type* frm, const extern_type* frm_end, size_t mx) const {
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  return utf16le_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  else
-  return utf16le_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
-#  endif
+  if constexpr (is_short_wchar)
+    return utf16le_to_ucs2_length(_frm, _frm_end, mx, __maxcode_, __mode_);
+  else
+    return utf16le_to_ucs4_length(_frm, _frm_end, mx, __maxcode_, __mode_);
 }
 
 int __codecvt_utf16<wchar_t, true>::do_max_length() const noexcept {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  if (__mode_ & consume_header)
-    return 4;
-  return 2;
-#  else
-  if (__mode_ & consume_header)
-    return 6;
-  return 4;
-#  endif
+  if constexpr (is_short_wchar)
+    return (__mode_ & consume_header) ? 4 : 2;
+  else
+    return (__mode_ & consume_header) ? 6 : 4;
 }
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
@@ -3604,15 +3561,9 @@ __codecvt_utf8_utf16<wchar_t>::result __codecvt_utf8_utf16<wchar_t>::do_out(
     extern_type* to,
     extern_type* to_end,
     extern_type*& to_nxt) const {
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  const uint16_t* _frm     = reinterpret_cast<const uint16_t*>(frm);
-  const uint16_t* _frm_end = reinterpret_cast<const uint16_t*>(frm_end);
-  const uint16_t* _frm_nxt = _frm;
-#  else
-  const uint32_t* _frm     = reinterpret_cast<const uint32_t*>(frm);
-  const uint32_t* _frm_end = reinterpret_cast<const uint32_t*>(frm_end);
-  const uint32_t* _frm_nxt = _frm;
-#  endif
+  const auto* _frm     = reinterpret_cast<const underlying_wchar_t*>(frm);
+  const auto* _frm_end = reinterpret_cast<const underlying_wchar_t*>(frm_end);
+  const auto* _frm_nxt = _frm;
   uint8_t* _to     = reinterpret_cast<uint8_t*>(to);
   uint8_t* _to_end = reinterpret_cast<uint8_t*>(to_end);
   uint8_t* _to_nxt = _to;
@@ -3633,18 +3584,12 @@ __codecvt_utf8_utf16<wchar_t>::result __codecvt_utf8_utf16<wchar_t>::do_in(
   const uint8_t* _frm     = reinterpret_cast<const uint8_t*>(frm);
   const uint8_t* _frm_end = reinterpret_cast<const uint8_t*>(frm_end);
   const uint8_t* _frm_nxt = _frm;
-#  if defined(_LIBCPP_SHORT_WCHAR)
-  uint16_t* _to     = reinterpret_cast<uint16_t*>(to);
-  uint16_t* _to_end = reinterpret_cast<uint16_t*>(to_end);
-  uint16_t* _to_nxt = _to;
-#  else
-  uint32_t* _to     = reinterpret_cast<uint32_t*>(to);
-  uint32_t* _to_end = reinterpret_cast<uint32_t*>(to_end);
-  uint32_t* _to_nxt = _to;
-#  endif
-  result r = utf8_to_utf16(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
-  frm_nxt  = frm + (_frm_nxt - _frm);
-  to_nxt   = to + (_to_nxt - _to);
+  auto* _to               = reinterpret_cast<underlying_wchar_t*>(to);
+  auto* _to_end           = reinterpret_cast<underlying_wchar_t*>(to_end);
+  auto* _to_nxt           = _to;
+  result r                = utf8_to_utf16(_frm, _frm_end, _frm_nxt, _to, _to_end, _to_nxt, __maxcode_, __mode_);
+  frm_nxt                 = frm + (_frm_nxt - _frm);
+  to_nxt                  = to + (_to_nxt - _to);
   return r;
 }
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..2fcaf6582afb0 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -517,7 +517,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NO_UNIQUE_ADDRESS
 #endif
 
-#ifdef _LIBCPP_SHORT_WCHAR
+#if defined(_WIN32) || defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2
 #  define TEST_SHORT_WCHAR
 #endif
 


From libcxx-commits at lists.llvm.org  Mon Jul  6 00:10:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 00:10:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4b54c8.27b3b003.1610ae.0738@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/206498

>From 6fc88c1fba7be7c7eb5dcb0dddd2ec12e4a75ec8 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 18:53:17 +0530
Subject: [PATCH 1/7] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. Omit the noreplace mode strings in
 __make_mdstring() on AIX, causing open() to return nullptr for these modes.
 Update the test to expect failure on AIX for all noreplace cases, since the
 platform cannot provide atomic exclusive-open semantics.

---
 libcxx/include/fstream                        | 10 ++++++++
 .../filebuf.members/open_pointer.pass.cpp     | 24 +++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b5fb65820628a..2ec5374ce463e 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -634,6 +634,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -644,6 +653,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..f7ab0239a1b18 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -75,7 +73,10 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -83,7 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 
@@ -94,7 +101,10 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -102,7 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 #  endif

>From 0715d9900e3781e298cca572f5955eb259d297ac Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 12:40:23 -0400
Subject: [PATCH 2/7] fix format and add warning

---
 libcxx/include/fstream                               |  3 +++
 .../fstreams/filebuf.members/open_pointer.pass.cpp   | 12 ++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 2ec5374ce463e..a5f1e80960054 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,6 +642,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if defined(_AIX) || defined(__TOS_AIX__)
+#        warning 'exclusive mode is unsupported on this target'
+#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index f7ab0239a1b18..627a65cac3f2f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -84,13 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#  else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#  endif
           }
         }
 
@@ -112,13 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#    else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#    endif
           }
         }
 #  endif

>From 072a5b2b327fe402617bd7b5ccf081931e685289 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 12:52:06 -0400
Subject: [PATCH 3/7] fix warnings treated as errors

---
 libcxx/include/fstream | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index a5f1e80960054..54f1555c15cbe 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -643,7 +643,10 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
 #      if defined(_AIX) || defined(__TOS_AIX__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic warning "-Wcpp"
 #        warning 'exclusive mode is unsupported on this target'
+#        pragma GCC diagnostic pop
 #      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:

>From 9c99f50050c182905f10cd7a5022b32600d0e7b2 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 13:49:31 -0400
Subject: [PATCH 4/7] add warning as fprintf

---
 libcxx/include/fstream | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 54f1555c15cbe..7c7f7c6b0db52 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,12 +642,6 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if defined(_AIX) || defined(__TOS_AIX__)
-#        pragma GCC diagnostic push
-#        pragma GCC diagnostic warning "-Wcpp"
-#        warning 'exclusive mode is unsupported on this target'
-#        pragma GCC diagnostic pop
-#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -750,9 +744,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr)
+  if (!__mdstr){
+#     if defined(_AIX) || defined(__TOS_AIX__)
+    if (__mode & ios_base::noreplace)
+      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
+#     endif
     return nullptr;
-
+}
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From d91a5ff2deca3f6f1c737ad0889afcbc198dca74 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:30:10 +0530
Subject: [PATCH 5/7] fix formatting

---
 libcxx/include/fstream | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7c7f7c6b0db52..9b176ddb5a469 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -744,13 +744,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr){
-#     if defined(_AIX) || defined(__TOS_AIX__)
+  if (!__mdstr) {
+#      if defined(_AIX) || defined(__TOS_AIX__)
     if (__mode & ios_base::noreplace)
       fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#     endif
+#      endif
     return nullptr;
-}
+  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From f3d25942e836d9a90b689f74747d241e2b40596b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:37:48 +0530
Subject: [PATCH 6/7] add the remaining test files

---
 .../fstreams/fstream.cons/pointer.pass.cpp    | 24 +++++++++++++++---
 .../fstream.members/open_pointer.pass.cpp     | 24 +++++++++++++++---
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 24 +++++++++++++++---
 .../ofstream.members/open_pointer.pass.cpp    | 25 ++++++++++++++++---
 4 files changed, 81 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..a677b16e76479 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
@@ -91,14 +89,23 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -108,14 +115,23 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..92c0cf1aa0c00 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -74,7 +72,10 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -82,7 +83,13 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -93,7 +100,10 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -101,7 +111,13 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..4fae51f38adbd 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
@@ -145,14 +143,23 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -162,14 +169,23 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..493af1bda9c98 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -82,7 +80,10 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -90,7 +91,13 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -101,14 +108,24 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
+
           {
             std::remove(tmp.c_str());
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif

>From 87a284bdb6700ecc77606479d7aef417f10c3550 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 6 Jul 2026 12:15:37 +0530
Subject: [PATCH 7/7] review changes

---
 libcxx/include/fstream                        | 21 +++++++++++--------
 .../filebuf.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/fstream.cons/pointer.pass.cpp    | 18 ++++------------
 .../fstream.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 18 ++++------------
 .../ofstream.members/open_pointer.pass.cpp    | 18 ++++------------
 6 files changed, 32 insertions(+), 79 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 9b176ddb5a469..9975be836304a 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -258,6 +258,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -641,8 +643,8 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // existence of the file and create it, it shall fail rather than perform a
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
-// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if !defined(_AIX) && !defined(__TOS_AIX__)
+// _AIX is defined on AIX by all supported compilers.
+#      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -653,7 +655,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
-#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
+#      endif // !defined(_AIX)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
@@ -744,13 +746,8 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr) {
-#      if defined(_AIX) || defined(__TOS_AIX__)
-    if (__mode & ios_base::noreplace)
-      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#      endif
+  if (!__mdstr)
     return nullptr;
-  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1192,6 +1189,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1350,6 +1349,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1514,6 +1515,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 627a65cac3f2f..37cfd985d1c11 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -73,10 +73,7 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -84,9 +81,7 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -101,10 +96,7 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -112,9 +104,7 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index a677b16e76479..2a297ca87903e 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -89,19 +89,14 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -115,19 +110,14 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 92c0cf1aa0c00..9178788b60354 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -72,10 +72,7 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -83,9 +80,7 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -100,10 +95,7 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -111,9 +103,7 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index 4fae51f38adbd..31a944236d1d0 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -143,19 +143,14 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -169,19 +164,14 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 493af1bda9c98..0b77f012b345d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -80,10 +80,7 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -91,9 +88,7 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -108,10 +103,7 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -119,9 +111,7 @@ int main(int, char**)
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist


From libcxx-commits at lists.llvm.org  Mon Jul  6 00:12:55 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 00:12:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a4b5577.eb24c6ea.1f2e26.69c3@mx.google.com>


================

----------------
philnik777 wrote:

Did we ever talk about this extension? If not, this should definitely be separated and consider whether we actually want to add it. That also doesn't explain why the directory changed.

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:12:55 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 00:12:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a4b5577.449bfb44.233a36.9b69@mx.google.com>


================
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// <text_encoding>
+
+// REQUIRES: std-at-least-c++26
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=40000000
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=1000000000
+
+// Our table has 882 aliases exactly, test to make sure that number matches with the total alias count
+// Internally, there are a total of 884 entries in our offset table, 882 for the actual aliases,
+// and +2 reserved as sentinels for id::unknown and id::other
----------------
philnik777 wrote:

Can we just add this as an explantation for the `- 2` in the assert?

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:14:24 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 06 Jul 2026 00:14:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
	to `zip_view` (PR #207667)
Message-ID: <llvm.org/llvm/llvm-project/pull/207667@github.com>


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

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/ranges.zip

Towards #172124

>From 33e16582ed6dcc20864c817ae0e660052916ed65 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 21 Jan 2026 13:44:27 +0200
Subject: [PATCH] [libc++][ranges] Applied `[[nodiscard]]` to `zip_view`

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/ranges.zip

Towards #172124
---
 libcxx/include/__ranges/zip_view.h            | 33 ++++---
 .../range.zip/nodiscard.verify.cpp            | 92 +++++++++++++++++++
 2 files changed, 110 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h
index bea64c4997a9e..aa13bed5f428d 100644
--- a/libcxx/include/__ranges/zip_view.h
+++ b/libcxx/include/__ranges/zip_view.h
@@ -134,19 +134,19 @@ class zip_view : public view_interface<zip_view<_Views...>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_view(_Views... __views) : __views_(std::move(__views)...) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
     requires(!(__simple_view<_Views> && ...))
   {
     return __iterator<false>(std::__tuple_transform(ranges::begin, __views_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires(range<const _Views> && ...)
   {
     return __iterator<true>(std::__tuple_transform(ranges::begin, __views_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!(__simple_view<_Views> && ...))
   {
     if constexpr (!__zip_is_common<_Views...>) {
@@ -158,7 +158,7 @@ class zip_view : public view_interface<zip_view<_Views...>> {
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires(range<const _Views> && ...)
   {
     if constexpr (!__zip_is_common<const _Views...>) {
@@ -170,7 +170,7 @@ class zip_view : public view_interface<zip_view<_Views...>> {
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires(sized_range<_Views> && ...)
   {
     return std::apply(
@@ -181,7 +181,7 @@ class zip_view : public view_interface<zip_view<_Views...>> {
         std::__tuple_transform(ranges::size, __views_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires(sized_range<const _Views> && ...)
   {
     return std::apply(
@@ -267,7 +267,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     requires _Const && (convertible_to<iterator_t<_Views>, iterator_t<__maybe_const<_Const, _Views>>> && ...)
       : __current_(std::move(__i.__current_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const {
     return std::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_);
   }
 
@@ -315,7 +315,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const
     requires __zip_all_random_access<_Const, _Views...>
   {
     return std::__tuple_transform(
@@ -338,7 +338,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     return __x.__current_ <=> __y.__current_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires __zip_all_random_access<_Const, _Views...>
   {
     auto __r = __i;
@@ -346,13 +346,13 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     return __r;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires __zip_all_random_access<_Const, _Views...>
   {
     return __i + __n;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires __zip_all_random_access<_Const, _Views...>
   {
     auto __r = __i;
@@ -360,7 +360,8 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     return __r;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires(sized_sentinel_for<iterator_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_Const, _Views>>> &&
              ...)
   {
@@ -374,7 +375,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
         __diffs);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept(
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept(
       (noexcept(ranges::iter_move(std::declval<const iterator_t<__maybe_const<_Const, _Views>>&>())) && ...) &&
       (is_nothrow_move_constructible_v<range_rvalue_reference_t<__maybe_const<_Const, _Views>>> && ...)) {
     return std::__tuple_transform(ranges::iter_move, __i.__current_);
@@ -426,6 +427,7 @@ class zip_view<_Views...>::__sentinel {
     requires(
         sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> &&
         ...)
+  [[nodiscard]]
   _LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     const auto __diffs = ranges::__tuple_zip_transform(minus<>(), __iter_current(__x), __y.__end_);
@@ -443,6 +445,7 @@ class zip_view<_Views...>::__sentinel {
     requires(
         sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> &&
         ...)
+  [[nodiscard]]
   _LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...>
   operator-(const __sentinel& __y, const __iterator<_OtherConst>& __x) {
     return -(__x - __y);
@@ -456,10 +459,10 @@ namespace views {
 namespace __zip {
 
 struct __fn {
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()() noexcept { return empty_view<tuple<>>{}; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()() noexcept { return empty_view<tuple<>>{}; }
 
   template <class... _Ranges>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
   operator()(_Ranges&&... __rs) noexcept(noexcept(zip_view<all_t<_Ranges&&>...>(std::forward<_Ranges>(__rs)...)))
       -> decltype(zip_view<all_t<_Ranges&&>...>(std::forward<_Ranges>(__rs)...)) {
     return zip_view<all_t<_Ranges>...>(std::forward<_Ranges>(__rs)...);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..59a1518a006ef
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <vector>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+void test() {
+  int arr[]{94, 82, 49};
+  std::vector<int> range;
+
+  std::ranges::zip_view view{range, arr};
+
+  // [range.zip.view]
+  {
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    view.begin();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(view).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    view.end();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(view).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    view.size();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(view).size();
+  }
+
+  // [range.zip.iterator]
+
+  {
+    auto it = view.begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *std::as_const(it);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(it)[0];
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    iter_move(it);
+  }
+
+  // [range.zip.sentinel]
+
+  {
+    auto it = view.begin();
+    auto st = view.end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(it) - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - std::as_const(it);
+  }
+
+  // [range.zip.overview]
+
+  {
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::zip();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::zip(arr, arr);
+  }
+}


From libcxx-commits at lists.llvm.org  Mon Jul  6 00:16:13 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 00:16:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use std::to_chars to format
 thread::id and canonicalize the representation across platforms (PR
 #181624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/181624@github.com>
Message-ID: <6a4b563d.491d90f5.1f0d58.df89@mx.google.com>


philnik777 wrote:

> This removed some transitive includes, which affected some projects. This isn't a big deal in practice, but if we still have the `_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23` flag, we probably should cover these headers by that as well? (I wouldn't use it myself, but it would seem consistent to have that flag keep these includes as well.)

I've added `<sstream>` behind `_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23` to both `<future>` and `<thread>`. Is there some other header that is removed?

https://github.com/llvm/llvm-project/pull/181624

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:17:30 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 00:17:30 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [compiler-rt] [flang]
 [libc] [libcxx] [llvm] [mlir] [polly] [NFC] Fix "is is" typos (PR #206288)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206288@github.com>
Message-ID: <6a4b568a.27b3b003.1610ae.578f@mx.google.com>


https://github.com/zackc6 approved this pull request.


https://github.com/llvm/llvm-project/pull/206288

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:20:05 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 00:20:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use
 _LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23 in <future> (PR #207668)
Message-ID: <llvm.org/llvm/llvm-project/pull/207668@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207668

This was accidentally guarded behind `_LIBCPP_REMOVE_TRANSITIVE_INCLUDES`.


>From d880d4e4c3124b14deb9b998600ddf6b11e817f7 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 6 Jul 2026 09:18:56 +0200
Subject: [PATCH] [libc++] Use _LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23 in
 <future>

---
 libcxx/include/future | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/future b/libcxx/include/future
index 498fda99fdafd..fd265972ab750 100644
--- a/libcxx/include/future
+++ b/libcxx/include/future
@@ -2079,7 +2079,7 @@ _LIBCPP_POP_MACROS
 #    include <thread>
 #  endif
 
-#  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 23
+#  if defined(_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23) && _LIBCPP_STD_VER <= 23
 #    include <sstream>
 #  endif
 


From libcxx-commits at lists.llvm.org  Mon Jul  6 00:20:32 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 00:20:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use
 _LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23 in <future> (PR #207668)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207668@github.com>
Message-ID: <6a4b5740.92ea0e2a.21f97.37b9@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/207668

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:20:46 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 00:20:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use
 _LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23 in <future> (PR #207668)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207668@github.com>
Message-ID: <6a4b574e.eb165e5c.113c2a.3fe6@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

`_LIBCPP_REMOVE_TRANSITIVE_INCLUDES` was used accidentally.


---
Full diff: https://github.com/llvm/llvm-project/pull/207668.diff


1 Files Affected:

- (modified) libcxx/include/future (+1-1) 


``````````diff
diff --git a/libcxx/include/future b/libcxx/include/future
index 498fda99fdafd..fd265972ab750 100644
--- a/libcxx/include/future
+++ b/libcxx/include/future
@@ -2079,7 +2079,7 @@ _LIBCPP_POP_MACROS
 #    include <thread>
 #  endif
 
-#  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 23
+#  if defined(_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23) && _LIBCPP_STD_VER <= 23
 #    include <sstream>
 #  endif
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/207668

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:26:33 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 00:26:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a4b58a9.bb985344.17fb3b.bb29@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207500

>From 70975eb30423b4b3ac4e4d60596d009668067650 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 4 Jul 2026 11:06:55 +0200
Subject: [PATCH] [libc++] Add
 __uninitialized_allocator_{fill,value_construct,move} and use them in vector

---
 .../__memory/uninitialized_algorithms.h       | 38 +++++++++++++++++++
 libcxx/include/__vector/vector.h              | 14 ++-----
 .../sequences/vector/asan_throw.pass.cpp      |  2 +-
 3 files changed, 42 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..5426dae7ef114 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -450,6 +450,30 @@ __uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _B
 
 #endif // _LIBCPP_STD_VER >= 17
 
+template <class _Alloc, class _Iter, class _Sent, class _Tp>
+_LIBCPP_CONSTEXPR_SINCE_CXX20
+_Iter __uninitialized_allocator_fill(_Alloc& __alloc, _Iter __first, _Sent __last, const _Tp& __value) {
+  auto __iter = __first;
+
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __first, __iter); });
+  for (; __iter != __last; ++__iter)
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__iter), __value);
+  __guard.__complete();
+  return __iter;
+}
+
+template <class _Alloc, class _Iter, class _Sent>
+_LIBCPP_CONSTEXPR_SINCE_CXX20
+_Iter __uninitialized_allocator_value_construct(_Alloc& __alloc, _Iter __first, _Sent __last) {
+  auto __iter = __first;
+
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __first, __iter); });
+  for (; __iter != __last; ++__iter)
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__iter));
+  __guard.__complete();
+  return __iter;
+}
+
 template <class _Alloc, class _Iter>
 class _AllocatorDestroyRangeReverse {
 public:
@@ -528,6 +552,20 @@ inline const bool __allocator_has_trivial_move_construct_v = !__has_construct_v<
 template <class _Type>
 inline const bool __allocator_has_trivial_move_construct_v<allocator<_Type>, _Type> = true;
 
+template <class _Alloc, class _InIter, class _Sent, class _OutIter>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
+__uninitialized_allocator_move(_Alloc& __alloc, _InIter __first, _Sent __last, _OutIter __result) {
+  auto __destruct_first = __result;
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __destruct_first, __result); });
+  while (__first != __last) {
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__result), std::move(*__first));
+    ++__first;
+    ++__result;
+  }
+  __guard.__complete();
+  return {std::move(__first), std::move(__result)};
+}
+
 template <class _Alloc, class _Tp>
 inline const bool __allocator_has_trivial_destroy_v = !__has_destroy_v<_Alloc, _Tp*>;
 
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..603b5676e05fb 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -880,10 +880,7 @@ vector<_Tp, _Allocator>::__recommend(size_type __new_size) const {
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__construct_at_end(size_type __n) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos));
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_value_construct(__layout_.__alloc(), __tx.__pos_, __tx.__new_end_);
 }
 
 //  Copy constructs __n objects starting at __layout_.__end_ptr() from __x
@@ -896,10 +893,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline void
 vector<_Tp, _Allocator>::__construct_at_end(size_type __n, const_reference __x) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), __x);
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_fill(__layout_.__alloc(), __tx.__pos_, __tx.__new_end_, __x);
 }
 
 template <class _Tp, class _Allocator>
@@ -1123,9 +1117,7 @@ vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointe
   {
     pointer __i = __from_s + __n;
     _ConstructTransaction __tx(*this, __from_e - __i);
-    for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
-      __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
-    }
+    __tx.__pos_ = std::__uninitialized_allocator_move(__layout_.__alloc(), __i, __from_e, __tx.__pos_).__out_;
   }
   std::move_backward(__from_s, __from_s + __n, __old_last);
 }
diff --git a/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp
index dcfa8029cfc0d..5089cd716266a 100644
--- a/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp
@@ -182,7 +182,7 @@ void test_insert_n2() {
     v.insert(v.cbegin(), 5, ThrowOnCopy());
     assert(0);
   } catch (int e) {
-    assert(v.size() == 11);
+    assert(v.size() == 10);
     assert(is_contiguous_container_asan_correct(v));
     return;
   }


From libcxx-commits at lists.llvm.org  Mon Jul  6 00:27:18 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Mon, 06 Jul 2026 00:27:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use std::to_chars to format
 thread::id and canonicalize the representation across platforms (PR
 #181624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/181624@github.com>
Message-ID: <6a4b58d6.f8b79514.3b441.b311@mx.google.com>


mstorsjo wrote:

> > This removed some transitive includes, which affected some projects. This isn't a big deal in practice, but if we still have the `_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23` flag, we probably should cover these headers by that as well? (I wouldn't use it myself, but it would seem consistent to have that flag keep these includes as well.)
> 
> I've added `<sstream>` behind `_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23` to both `<future>` and `<thread>`. Is there some other header that is removed?

Oh, sorry - I overlooked the detail that you actually did that in the other headers, not the one that it was removed from. Then it's probably all fine! Sorry for the noise.


https://github.com/llvm/llvm-project/pull/181624

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:29:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 00:29:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 1b2efd2 - [libc++] Split the
 multidimensional algorithms out of uninitialized_algorithms.h (#207447)
Message-ID: <6a4b593d.13be6df8.3d8759.646f@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-06T09:28:56+02:00
New Revision: 1b2efd2e079a7fe86888cb7da12a5efa84b131fc

URL: https://github.com/llvm/llvm-project/commit/1b2efd2e079a7fe86888cb7da12a5efa84b131fc
DIFF: https://github.com/llvm/llvm-project/commit/1b2efd2e079a7fe86888cb7da12a5efa84b131fc.diff

LOG: [libc++] Split the multidimensional algorithms out of uninitialized_algorithms.h (#207447)

While these algorithms are conceptually related to other
`uninitialized_*` algorithms, they're likely only ever going to be used
by `shared_ptr`. `uninitialized_algorithms.h` itself is also getting
rather large, so it makes sense to split the multidimensional versions
into their own header.

Fixes #207417

Added: 
    libcxx/include/__memory/uninitialized_multidimensional_algorithms.h

Modified: 
    libcxx/include/CMakeLists.txt
    libcxx/include/__memory/shared_ptr.h
    libcxx/include/__memory/uninitialized_algorithms.h
    libcxx/include/module.modulemap.in

Removed: 
    


################################################################################
diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index d65e66e221766..b40f586161e62 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -618,6 +618,7 @@ set(files
   __memory/temp_value.h
   __memory/temporary_buffer.h
   __memory/uninitialized_algorithms.h
+  __memory/uninitialized_multidimensional_algorithms.h
   __memory/unique_ptr.h
   __memory/unique_temporary_buffer.h
   __memory/uses_allocator.h

diff  --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 750d253912ab1..ebd542ba14d01 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -33,6 +33,7 @@
 #include <__memory/pointer_traits.h>
 #include <__memory/shared_count.h>
 #include <__memory/uninitialized_algorithms.h>
+#include <__memory/uninitialized_multidimensional_algorithms.h>
 #include <__memory/unique_ptr.h>
 #include <__type_traits/add_reference.h>
 #include <__type_traits/conditional.h>

diff  --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..d4cd49a1deb6e 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -11,11 +11,10 @@
 #define _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
 
 #include <__algorithm/copy.h>
-#include <__algorithm/move.h>
+#include <__algorithm/in_out_result.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__algorithm/unwrap_range.h>
 #include <__config>
-#include <__cstddef/size_t.h>
 #include <__fwd/memory.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/reverse_iterator.h>
@@ -25,15 +24,12 @@
 #include <__memory/destroy.h>
 #include <__memory/pointer_traits.h>
 #include <__type_traits/enable_if.h>
-#include <__type_traits/extent.h>
-#include <__type_traits/is_array.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/remove_const.h>
-#include <__type_traits/remove_extent.h>
 #include <__utility/exception_guard.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
@@ -295,159 +291,6 @@ uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofir
   return {std::move(__result.__in_), std::move(__result.__out_)};
 }
 
-// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
-// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
-// destruction. If elements are themselves C-style arrays, they are recursively destroyed
-// in the same manner.
-//
-// This function assumes that destructors do not throw, and that the allocator is bound to
-// the correct type.
-template <class _Alloc,
-          class _BidirIter,
-          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
-                "The allocator should already be rebound to the correct type");
-
-  if (__first == __last)
-    return;
-
-  if constexpr (is_array_v<_ValueType>) {
-    static_assert(!__is_unbounded_array_v<_ValueType>,
-                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
-
-    using _Element = remove_extent_t<_ValueType>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    do {
-      --__last;
-      decltype(auto) __array = *__last;
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
-    } while (__last != __first);
-  } else {
-    do {
-      --__last;
-      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
-    } while (__last != __first);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method.
-//
-// If the object being constructed is an array, each element of the array is allocator-constructed,
-// recursively. If an exception is thrown during the construction of an array, the initialized
-// elements are destroyed in reverse order of initialization using allocator destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method, passing along
-// the provided argument.
-//
-// If the object being constructed is an array, the argument is also assumed to be an array. Each
-// each element of the array being constructed is allocator-constructed from the corresponding
-// element of the argument array. If an exception is thrown during the construction of an array,
-// the initialized elements are destroyed in reverse order of initialization using allocator
-// destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp, class _Arg>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    static_assert(is_array_v<_Arg>,
-                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
-                  "trying to construct an array.");
-
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
-  }
-}
-
-// Given a range starting at it and containing n elements, initializes each element in the
-// range from left to right using the construct method of the allocator (rebound to the
-// correct type).
-//
-// If an exception is thrown, the initialized elements are destroyed in reverse order of
-// initialization using allocator_traits destruction. If the elements in the range are C-style
-// arrays, they are initialized element-wise using allocator construction, and recursively so.
-template <class _Alloc,
-          class _BidirIter,
-          class _Tp,
-          class _Size = typename iterator_traits<_BidirIter>::
diff erence_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
-  }
-  __guard.__complete();
-}
-
-// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
-// to the allocator's construct method, which results in value initialization.
-template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::
diff erence_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
-  }
-  __guard.__complete();
-}
-
 #endif // _LIBCPP_STD_VER >= 17
 
 template <class _Alloc, class _Iter>

diff  --git a/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
new file mode 100644
index 0000000000000..2496ce6af1e15
--- /dev/null
+++ b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
@@ -0,0 +1,194 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+#define _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_traits.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/extent.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/remove_extent.h>
+#include <__utility/exception_guard.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
+// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
+// destruction. If elements are themselves C-style arrays, they are recursively destroyed
+// in the same manner.
+//
+// This function assumes that destructors do not throw, and that the allocator is bound to
+// the correct type.
+template <class _Alloc,
+          class _BidirIter,
+          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
+                "The allocator should already be rebound to the correct type");
+
+  if (__first == __last)
+    return;
+
+  if constexpr (is_array_v<_ValueType>) {
+    static_assert(!__is_unbounded_array_v<_ValueType>,
+                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
+
+    using _Element = remove_extent_t<_ValueType>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    do {
+      --__last;
+      decltype(auto) __array = *__last;
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
+    } while (__last != __first);
+  } else {
+    do {
+      --__last;
+      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
+    } while (__last != __first);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method.
+//
+// If the object being constructed is an array, each element of the array is allocator-constructed,
+// recursively. If an exception is thrown during the construction of an array, the initialized
+// elements are destroyed in reverse order of initialization using allocator destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method, passing along
+// the provided argument.
+//
+// If the object being constructed is an array, the argument is also assumed to be an array. Each
+// each element of the array being constructed is allocator-constructed from the corresponding
+// element of the argument array. If an exception is thrown during the construction of an array,
+// the initialized elements are destroyed in reverse order of initialization using allocator
+// destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp, class _Arg>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    static_assert(is_array_v<_Arg>,
+                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
+                  "trying to construct an array.");
+
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
+  }
+}
+
+// Given a range starting at it and containing n elements, initializes each element in the
+// range from left to right using the construct method of the allocator (rebound to the
+// correct type).
+//
+// If an exception is thrown, the initialized elements are destroyed in reverse order of
+// initialization using allocator_traits destruction. If the elements in the range are C-style
+// arrays, they are initialized element-wise using allocator construction, and recursively so.
+template <class _Alloc,
+          class _BidirIter,
+          class _Tp,
+          class _Size = typename iterator_traits<_BidirIter>::
diff erence_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
+  }
+  __guard.__complete();
+}
+
+// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
+// to the allocator's construct method, which results in value initialization.
+template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::
diff erence_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
+  }
+  __guard.__complete();
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 2d4ba2b5876a7..39b4e0bb986c6 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1705,6 +1705,9 @@ module std {
       export std.memory.destroy
       export std.utility.pair
     }
+    module uninitialized_multidimensional_algorithms {
+      header "__memory/uninitialized_multidimensional_algorithms.h"
+    }
     module unique_ptr {
       header "__memory/unique_ptr.h"
       export std.functional.hash


        

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:29:03 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 00:29:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Split the multidimensional
 algorithms out of uninitialized_algorithms.h (PR #207447)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207447@github.com>
Message-ID: <6a4b593f.a87eb5dc.1d432d.cfe5@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/207447

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:35:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 00:35:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Always enable
	-fvisibility=hidden (PR #207333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207333@github.com>
Message-ID: <6a4b5ab2.dd2b4bd3.13e95e.a461@mx.google.com>


================
@@ -195,6 +195,7 @@ if (LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
   # Build with -fsized-deallocation, which is default in recent versions of Clang.
   # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
   target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
+  target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
----------------
philnik777 wrote:

For the record, I've done
```
nm libc++abi.so.1.0 | cut -c 18- > dump.nm
ninja cxxabi-test-depends
nm libc++abi.so.1.0 | cut -c 18- > dump2.nm
diff dump.nm dump2.nm
```
and the files were identical.

https://github.com/llvm/llvm-project/pull/207333

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:35:16 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 00:35:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] cb942d0 - [libc++abi] Always enable
	-fvisibility=hidden (#207333)
Message-ID: <6a4b5ab4.fdc477d6.1a5bda.ca30@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-06T09:35:11+02:00
New Revision: cb942d009a5ed029ce20d9b75b3604f551f0cb56

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

LOG: [libc++abi] Always enable -fvisibility=hidden (#207333)

Building with `-fvisibility=hidden` is generally a good idea, since it
avoids unnecessary relocations when loading a dylib. For libc++abi this
should be a no-op, since most things are annotated explicitly. By
enabling `-fvisibility=hidden` we can remove these explicit annotations
however.

Added: 
    

Modified: 
    libcxxabi/src/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index 1889d7d5a82de..fc66ce7187170 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -195,6 +195,7 @@ if (LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
   # Build with -fsized-deallocation, which is default in recent versions of Clang.
   # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
   target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
+  target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
 
   add_library(cxxabi_shared SHARED)
   set_target_properties(cxxabi_shared
@@ -285,9 +286,9 @@ target_compile_options(cxxabi_static_objects PRIVATE "${LIBCXXABI_ADDITIONAL_COM
 # Build with -fsized-deallocation, which is default in recent versions of Clang.
 # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
 target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fsized-deallocation)
+target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
 
 if(LIBCXXABI_HERMETIC_STATIC_LIBRARY)
-  target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
   # If the hermetic library doesn't define the operator new/delete functions
   # then its code shouldn't declare them with hidden visibility.  They might
   # actually be provided by a shared library at link time.


        

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:35:18 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 00:35:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Always enable
	-fvisibility=hidden (PR #207333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207333@github.com>
Message-ID: <6a4b5ab6.13be6df8.3d8759.67b3@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/207333

From libcxx-commits at lists.llvm.org  Mon Jul  6 00:48:51 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 00:48:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use std::to_chars to format
 thread::id and canonicalize the representation across platforms (PR
 #181624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/181624@github.com>
Message-ID: <6a4b5de3.510f576d.185953.bbee@mx.google.com>


philnik777 wrote:

> > > This removed some transitive includes, which affected some projects. This isn't a big deal in practice, but if we still have the `_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23` flag, we probably should cover these headers by that as well? (I wouldn't use it myself, but it would seem consistent to have that flag keep these includes as well.)
> > 
> > 
> > I've added `<sstream>` behind `_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23` to both `<future>` and `<thread>`. Is there some other header that is removed?
> 
> Oh, sorry - I overlooked the detail that you actually did that in the other headers, not the one that it was removed from. Then it's probably all fine! Sorry for the noise.

No worries! We always put the unused includes in the top-level headers to avoid polluting new headers that use the detail headers.

https://github.com/llvm/llvm-project/pull/181624

From libcxx-commits at lists.llvm.org  Mon Jul  6 01:53:38 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 01:53:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a4b6d12.58bdd12c.21857.e803@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/185397

>From aea5cc3b53e48633cca05245f5b4be535c62a8d2 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 12 Feb 2026 15:08:00 +0100
Subject: [PATCH] [libc++] Refactor the string benchmarks

---
 .../benchmarks/containers/string.bench.cpp    | 999 ++++++++----------
 1 file changed, 438 insertions(+), 561 deletions(-)

diff --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index 98216d22d0144..915867c143651 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -6,602 +6,479 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench(
+      "std::basic_string::ctor(const value_type*)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> str(state.range(), 'a');
+
+        for (auto _ : state) {
+          auto ptr = str.data();
+          std::basic_string<CharT> copy(ptr);
+          benchmark::DoNotOptimize(copy);
+        }
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}));
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}));
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}));
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}));
   }
 
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}));
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}));
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}));
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}));
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}));
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}));
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}));
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}));
   }
 
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}));
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}));
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}));
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}));
   }
 
-  static bool skip() {
-    // Huge does not give us anything that Large doesn't have. Skip it.
-    return Length() == ::Length::Huge;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> src(size, 'a');
+
+          std::basic_string<CharT> strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string.compare(src));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const std::basic_string&) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}));
+
+    bench("std::basic_string::compare(const std::basic_string&) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}));
+
+    bench("std::basic_string::compare(const std::basic_string&) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}));
+
+    bench("std::basic_string::compare(const std::basic_string&) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}));
   }
 
-  std::string name() const { return "BM_StringRead" + Temperature::name() + Depth::name() + Length::name(); }
-};
-
-void sanityCheckGeneratedStrings() {
-  for (auto Lhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
-    const auto LhsString = makeString(Lhs);
-    for (auto Rhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
-      if (Lhs > Rhs)
-        continue;
-      const auto RhsString = makeString(Rhs);
-
-      // The smaller one must be a prefix of the larger one.
-      if (RhsString.find(LhsString) != 0) {
-        fprintf(
-            stderr, "Invalid autogenerated strings for sizes (%d,%d).\n", static_cast<int>(Lhs), static_cast<int>(Rhs));
-        std::abort();
-      }
-    }
-  }
-  // Verify the autogenerated diffs
-  for (auto L : {Length::Small, Length::Large, Length::Huge}) {
-    const auto Control = makeString(L);
-    const auto Verify  = [&](std::string Exp, size_t Pos) {
-      // Only change on the Pos char.
-      if (Control[Pos] != Exp[Pos]) {
-        Exp[Pos] = Control[Pos];
-        if (Control == Exp)
-          return;
-      }
-      fprintf(stderr, "Invalid autogenerated diff with size %d\n", static_cast<int>(L));
-      std::abort();
-    };
-    Verify(makeString(L, DiffType::ChangeFirst), 0);
-    Verify(makeString(L, DiffType::ChangeMiddle), Control.size() / 2);
-    Verify(makeString(L, DiffType::ChangeLast), Control.size() - 1);
-  }
-}
-
-int main(int argc, char** argv) {
   benchmark::Initialize(&argc, argv);
-  if (benchmark::ReportUnrecognizedArguments(argc, argv))
-    return 1;
-
-  sanityCheckGeneratedStrings();
-
-  makeCartesianProductBenchmark<StringConstructDestroyCStr, AllLengths, AllOpacity>();
-
-  makeCartesianProductBenchmark<StringAssignStr, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringAssignAsciiz, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringAssignAsciizMix, AllOpacity>();
-
-  makeCartesianProductBenchmark<StringCopy, AllLengths>();
-  makeCartesianProductBenchmark<StringMove, AllLengths>();
-  makeCartesianProductBenchmark<StringDestroy, AllLengths>();
-  makeCartesianProductBenchmark<StringEraseToEnd, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringEraseWithMove, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringRelational, AllRelations, AllLengths, AllLengths, AllDiffTypes>();
-  makeCartesianProductBenchmark<StringRelationalLiteral, AllRelations, AllLengths, AllLengths, AllDiffTypes>();
-  makeCartesianProductBenchmark<StringRead, AllTemperatures, AllDepths, AllLengths>();
   benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
 }


From libcxx-commits at lists.llvm.org  Mon Jul  6 01:59:13 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 01:59:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a4b6e61.da8bafd3.2e1688.6ac7@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
----------------
philnik777 wrote:

Strings tend to be relatively small, so I think we should focus on that. I don't think there are many benchmarks here where much bigger values would make a big difference. We're working in contiguous memory and have exclusively linear time operations AFAIK.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Mon Jul  6 01:59:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 01:59:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a4b6e62.f06f4994.168928.5d28@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
----------------
philnik777 wrote:

Indeed. I think I changed this to pass the size as a constant to allow the transparent tests to work properly.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Mon Jul  6 01:59:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 01:59:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a4b6e62.d5797644.146403.c3d7@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
----------------
philnik777 wrote:

These test `__builtin_constant_p` special cases. So yeah, they make a difference.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Mon Jul  6 01:59:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 01:59:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a4b6e62.481afadf.134314.c9da@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
----------------
philnik777 wrote:

Before: 22s compile time + 6:31min runtime
After: 105s compile time + 3:21min runtime

I'm not quite sure why Clang is so slow with the new code. Somehow `std::invoke` uses up ridiculous amounts of resources.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Mon Jul  6 01:59:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 01:59:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a4b6e62.14d9b9dc.140829.0c5e@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (opaque)",
----------------
philnik777 wrote:

I can't remember whether I did that intentionally, but I don't think it adds much value, since `operator<` and `operator==` are basically identical.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Mon Jul  6 02:19:12 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 02:19:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4b7310.6c8668f2.346e76.125c@mx.google.com>


https://github.com/Himadhith ready_for_review https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Mon Jul  6 02:19:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 02:19:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4b7333.ac469dc7.ed218.86a1@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Himadhith

<details>
<summary>Changes</summary>

AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix. 
Return null for all instances of `noreplace` as the check happens non-atomically.
Note that the wording of this mode in C23, Specifically (para 5):

`If the implementation is not capable of performing the check for the existence of the file and the
creation of the file atomically, it shall fail instead of performing a non-atomic check and creation` 
Ref: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf


---
Full diff: https://github.com/llvm/llvm-project/pull/206498.diff


6 Files Affected:

- (modified) libcxx/include/fstream (+18-1) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp (+8-2) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp (+8-2) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp (+8-2) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp (+8-2) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp (+9-2) 


``````````diff
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b5fb65820628a..9975be836304a 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -258,6 +258,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -634,6 +636,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined on AIX by all supported compilers.
+#      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -644,6 +655,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
@@ -736,7 +748,6 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   const wchar_t* __mdstr = __make_mdwstring(__mode);
   if (!__mdstr)
     return nullptr;
-
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1178,6 +1189,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1336,6 +1349,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1500,6 +1515,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..37cfd985d1c11 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -83,7 +81,11 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX)
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -102,7 +104,11 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX)
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..2a297ca87903e 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
@@ -98,7 +96,11 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
+#  if defined(_AIX)
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -115,7 +117,11 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
+#    if defined(_AIX)
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..9178788b60354 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -82,7 +80,11 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX)
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -101,7 +103,11 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX)
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..31a944236d1d0 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
@@ -152,7 +150,11 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
+#  if defined(_AIX)
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -169,7 +171,11 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
+#    if defined(_AIX)
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..0b77f012b345d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -90,7 +88,11 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX)
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -103,12 +105,17 @@ int main(int, char**)
             f.open(tmp.c_str(), mode);
             assert(!f.is_open()); // since it already exists
           }
+
           {
             std::remove(tmp.c_str());
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX)
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif

``````````

</details>


https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Mon Jul  6 02:25:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 02:25:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4b7491.7270d2fd.23dbb3.af6c@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/206498

>From 6fc88c1fba7be7c7eb5dcb0dddd2ec12e4a75ec8 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 18:53:17 +0530
Subject: [PATCH 1/7] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. Omit the noreplace mode strings in
 __make_mdstring() on AIX, causing open() to return nullptr for these modes.
 Update the test to expect failure on AIX for all noreplace cases, since the
 platform cannot provide atomic exclusive-open semantics.

---
 libcxx/include/fstream                        | 10 ++++++++
 .../filebuf.members/open_pointer.pass.cpp     | 24 +++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b5fb65820628a..2ec5374ce463e 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -634,6 +634,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -644,6 +653,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..f7ab0239a1b18 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -75,7 +73,10 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -83,7 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 
@@ -94,7 +101,10 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -102,7 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 #  endif

>From 0715d9900e3781e298cca572f5955eb259d297ac Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 12:40:23 -0400
Subject: [PATCH 2/7] fix format and add warning

---
 libcxx/include/fstream                               |  3 +++
 .../fstreams/filebuf.members/open_pointer.pass.cpp   | 12 ++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 2ec5374ce463e..a5f1e80960054 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,6 +642,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if defined(_AIX) || defined(__TOS_AIX__)
+#        warning 'exclusive mode is unsupported on this target'
+#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index f7ab0239a1b18..627a65cac3f2f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -84,13 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#  else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#  endif
           }
         }
 
@@ -112,13 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#    else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#    endif
           }
         }
 #  endif

>From 072a5b2b327fe402617bd7b5ccf081931e685289 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 12:52:06 -0400
Subject: [PATCH 3/7] fix warnings treated as errors

---
 libcxx/include/fstream | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index a5f1e80960054..54f1555c15cbe 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -643,7 +643,10 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
 #      if defined(_AIX) || defined(__TOS_AIX__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic warning "-Wcpp"
 #        warning 'exclusive mode is unsupported on this target'
+#        pragma GCC diagnostic pop
 #      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:

>From 9c99f50050c182905f10cd7a5022b32600d0e7b2 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 13:49:31 -0400
Subject: [PATCH 4/7] add warning as fprintf

---
 libcxx/include/fstream | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 54f1555c15cbe..7c7f7c6b0db52 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -642,12 +642,6 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if defined(_AIX) || defined(__TOS_AIX__)
-#        pragma GCC diagnostic push
-#        pragma GCC diagnostic warning "-Wcpp"
-#        warning 'exclusive mode is unsupported on this target'
-#        pragma GCC diagnostic pop
-#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -750,9 +744,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr)
+  if (!__mdstr){
+#     if defined(_AIX) || defined(__TOS_AIX__)
+    if (__mode & ios_base::noreplace)
+      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
+#     endif
     return nullptr;
-
+}
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From d91a5ff2deca3f6f1c737ad0889afcbc198dca74 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:30:10 +0530
Subject: [PATCH 5/7] fix formatting

---
 libcxx/include/fstream | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7c7f7c6b0db52..9b176ddb5a469 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -744,13 +744,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr){
-#     if defined(_AIX) || defined(__TOS_AIX__)
+  if (!__mdstr) {
+#      if defined(_AIX) || defined(__TOS_AIX__)
     if (__mode & ios_base::noreplace)
       fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#     endif
+#      endif
     return nullptr;
-}
+  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From f3d25942e836d9a90b689f74747d241e2b40596b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:37:48 +0530
Subject: [PATCH 6/7] add the remaining test files

---
 .../fstreams/fstream.cons/pointer.pass.cpp    | 24 +++++++++++++++---
 .../fstream.members/open_pointer.pass.cpp     | 24 +++++++++++++++---
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 24 +++++++++++++++---
 .../ofstream.members/open_pointer.pass.cpp    | 25 ++++++++++++++++---
 4 files changed, 81 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..a677b16e76479 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
@@ -91,14 +89,23 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -108,14 +115,23 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..92c0cf1aa0c00 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -74,7 +72,10 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -82,7 +83,13 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -93,7 +100,10 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -101,7 +111,13 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..4fae51f38adbd 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
@@ -145,14 +143,23 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -162,14 +169,23 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..493af1bda9c98 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -82,7 +80,10 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -90,7 +91,13 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -101,14 +108,24 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
+
           {
             std::remove(tmp.c_str());
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif

>From b65864e76d5d3222dfe4762a17934a3d1ca808cd Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 6 Jul 2026 12:15:37 +0530
Subject: [PATCH 7/7] review changes

---
 libcxx/include/fstream                        | 21 +++++++++++--------
 .../filebuf.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/fstream.cons/pointer.pass.cpp    | 18 ++++------------
 .../fstream.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 18 ++++------------
 .../ofstream.members/open_pointer.pass.cpp    | 18 ++++------------
 6 files changed, 32 insertions(+), 79 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 9b176ddb5a469..3dbb3e5152e09 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -258,6 +258,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                          "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -641,8 +643,8 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // existence of the file and create it, it shall fail rather than perform a
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
-// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if !defined(_AIX) && !defined(__TOS_AIX__)
+// _AIX is defined on AIX by all supported compilers.
+#      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -653,7 +655,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
-#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
+#      endif // !defined(_AIX)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
@@ -744,13 +746,8 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr) {
-#      if defined(_AIX) || defined(__TOS_AIX__)
-    if (__mode & ios_base::noreplace)
-      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#      endif
+  if (!__mdstr)
     return nullptr;
-  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1192,6 +1189,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                          "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1350,6 +1349,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                          "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1514,6 +1515,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                          "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 627a65cac3f2f..37cfd985d1c11 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -73,10 +73,7 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -84,9 +81,7 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -101,10 +96,7 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -112,9 +104,7 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index a677b16e76479..2a297ca87903e 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -89,19 +89,14 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -115,19 +110,14 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 92c0cf1aa0c00..9178788b60354 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -72,10 +72,7 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -83,9 +80,7 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -100,10 +95,7 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -111,9 +103,7 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index 4fae51f38adbd..31a944236d1d0 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -143,19 +143,14 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -169,19 +164,14 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 493af1bda9c98..0b77f012b345d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -80,10 +80,7 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -91,9 +88,7 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -108,10 +103,7 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -119,9 +111,7 @@ int main(int, char**)
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist


From libcxx-commits at lists.llvm.org  Mon Jul  6 02:26:18 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 02:26:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4b74b9.449bfb44.233a36.b090@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/206498

>From eac8181d96f0ed0ff110d7cf7602551199913a6b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 18:53:17 +0530
Subject: [PATCH 1/7] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. Omit the noreplace mode strings in
 __make_mdstring() on AIX, causing open() to return nullptr for these modes.
 Update the test to expect failure on AIX for all noreplace cases, since the
 platform cannot provide atomic exclusive-open semantics.

---
 libcxx/include/fstream                        | 10 ++++++++
 .../filebuf.members/open_pointer.pass.cpp     | 24 +++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..f979db89db794 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -664,6 +664,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -674,6 +683,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..f7ab0239a1b18 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -75,7 +73,10 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -83,7 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 
@@ -94,7 +101,10 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -102,7 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 #  endif

>From 9c8dfa79291faa95c5a036e5d6b2ac43d199aee1 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 12:40:23 -0400
Subject: [PATCH 2/7] fix format and add warning

---
 libcxx/include/fstream                               |  3 +++
 .../fstreams/filebuf.members/open_pointer.pass.cpp   | 12 ++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index f979db89db794..b71baabff2524 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -672,6 +672,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if defined(_AIX) || defined(__TOS_AIX__)
+#        warning 'exclusive mode is unsupported on this target'
+#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index f7ab0239a1b18..627a65cac3f2f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -84,13 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#  else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#  endif
           }
         }
 
@@ -112,13 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#    else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#    endif
           }
         }
 #  endif

>From d324a675f05f851656c4fb10ac6eaf5120ebb08e Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 12:52:06 -0400
Subject: [PATCH 3/7] fix warnings treated as errors

---
 libcxx/include/fstream | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b71baabff2524..91259a39f4000 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -673,7 +673,10 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
 #      if defined(_AIX) || defined(__TOS_AIX__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic warning "-Wcpp"
 #        warning 'exclusive mode is unsupported on this target'
+#        pragma GCC diagnostic pop
 #      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:

>From 450f6694bddb62f12cc9a666b3c1c300a90f725c Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 13:49:31 -0400
Subject: [PATCH 4/7] add warning as fprintf

---
 libcxx/include/fstream | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 91259a39f4000..92baa4670b363 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -672,12 +672,6 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if defined(_AIX) || defined(__TOS_AIX__)
-#        pragma GCC diagnostic push
-#        pragma GCC diagnostic warning "-Wcpp"
-#        warning 'exclusive mode is unsupported on this target'
-#        pragma GCC diagnostic pop
-#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -780,9 +774,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr)
+  if (!__mdstr){
+#     if defined(_AIX) || defined(__TOS_AIX__)
+    if (__mode & ios_base::noreplace)
+      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
+#     endif
     return nullptr;
-
+}
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From eb0c8a9711dd9ed86e21ec217adb49f8cc7e3ec3 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:30:10 +0530
Subject: [PATCH 5/7] fix formatting

---
 libcxx/include/fstream | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 92baa4670b363..216082cfb8283 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -774,13 +774,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr){
-#     if defined(_AIX) || defined(__TOS_AIX__)
+  if (!__mdstr) {
+#      if defined(_AIX) || defined(__TOS_AIX__)
     if (__mode & ios_base::noreplace)
       fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#     endif
+#      endif
     return nullptr;
-}
+  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From 9f7f1e7bc05bf06350f2846f8f736366133b1b8b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:37:48 +0530
Subject: [PATCH 6/7] add the remaining test files

---
 .../fstreams/fstream.cons/pointer.pass.cpp    | 24 +++++++++++++++---
 .../fstream.members/open_pointer.pass.cpp     | 24 +++++++++++++++---
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 24 +++++++++++++++---
 .../ofstream.members/open_pointer.pass.cpp    | 25 ++++++++++++++++---
 4 files changed, 81 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..a677b16e76479 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
@@ -91,14 +89,23 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -108,14 +115,23 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..92c0cf1aa0c00 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -74,7 +72,10 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -82,7 +83,13 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -93,7 +100,10 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -101,7 +111,13 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..4fae51f38adbd 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
@@ -145,14 +143,23 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -162,14 +169,23 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..493af1bda9c98 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -82,7 +80,10 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -90,7 +91,13 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -101,14 +108,24 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
+
           {
             std::remove(tmp.c_str());
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif

>From fbd3444c18db75dab76c1eb9b8ee8dbcdf421bc0 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 6 Jul 2026 12:15:37 +0530
Subject: [PATCH 7/7] review changes

---
 libcxx/include/fstream                        | 21 +++++++++++--------
 .../filebuf.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/fstream.cons/pointer.pass.cpp    | 18 ++++------------
 .../fstream.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 18 ++++------------
 .../ofstream.members/open_pointer.pass.cpp    | 18 ++++------------
 6 files changed, 32 insertions(+), 79 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 216082cfb8283..cfbdabdbba3f1 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -258,6 +258,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                          "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -671,8 +673,8 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // existence of the file and create it, it shall fail rather than perform a
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
-// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if !defined(_AIX) && !defined(__TOS_AIX__)
+// _AIX is defined on AIX by all supported compilers.
+#      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -683,7 +685,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
-#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
+#      endif // !defined(_AIX)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
@@ -774,13 +776,8 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr) {
-#      if defined(_AIX) || defined(__TOS_AIX__)
-    if (__mode & ios_base::noreplace)
-      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#      endif
+  if (!__mdstr)
     return nullptr;
-  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1222,6 +1219,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                          "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1380,6 +1379,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                          "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1544,6 +1545,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                          "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 627a65cac3f2f..37cfd985d1c11 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -73,10 +73,7 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -84,9 +81,7 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -101,10 +96,7 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -112,9 +104,7 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index a677b16e76479..2a297ca87903e 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -89,19 +89,14 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -115,19 +110,14 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 92c0cf1aa0c00..9178788b60354 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -72,10 +72,7 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -83,9 +80,7 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -100,10 +95,7 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -111,9 +103,7 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index 4fae51f38adbd..31a944236d1d0 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -143,19 +143,14 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -169,19 +164,14 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 493af1bda9c98..0b77f012b345d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -80,10 +80,7 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -91,9 +88,7 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -108,10 +103,7 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -119,9 +111,7 @@ int main(int, char**)
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist


From libcxx-commits at lists.llvm.org  Mon Jul  6 02:33:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 02:33:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4b7666.6c8668f2.346e76.5004@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/206498

>From eac8181d96f0ed0ff110d7cf7602551199913a6b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 18:53:17 +0530
Subject: [PATCH 1/7] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. Omit the noreplace mode strings in
 __make_mdstring() on AIX, causing open() to return nullptr for these modes.
 Update the test to expect failure on AIX for all noreplace cases, since the
 platform cannot provide atomic exclusive-open semantics.

---
 libcxx/include/fstream                        | 10 ++++++++
 .../filebuf.members/open_pointer.pass.cpp     | 24 +++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..f979db89db794 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -664,6 +664,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -674,6 +683,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..f7ab0239a1b18 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -75,7 +73,10 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -83,7 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 
@@ -94,7 +101,10 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -102,7 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 #  endif

>From 9c8dfa79291faa95c5a036e5d6b2ac43d199aee1 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 12:40:23 -0400
Subject: [PATCH 2/7] fix format and add warning

---
 libcxx/include/fstream                               |  3 +++
 .../fstreams/filebuf.members/open_pointer.pass.cpp   | 12 ++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index f979db89db794..b71baabff2524 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -672,6 +672,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if defined(_AIX) || defined(__TOS_AIX__)
+#        warning 'exclusive mode is unsupported on this target'
+#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index f7ab0239a1b18..627a65cac3f2f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -84,13 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#  else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#  endif
           }
         }
 
@@ -112,13 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#    else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#    endif
           }
         }
 #  endif

>From d324a675f05f851656c4fb10ac6eaf5120ebb08e Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 12:52:06 -0400
Subject: [PATCH 3/7] fix warnings treated as errors

---
 libcxx/include/fstream | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b71baabff2524..91259a39f4000 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -673,7 +673,10 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
 #      if defined(_AIX) || defined(__TOS_AIX__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic warning "-Wcpp"
 #        warning 'exclusive mode is unsupported on this target'
+#        pragma GCC diagnostic pop
 #      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:

>From 450f6694bddb62f12cc9a666b3c1c300a90f725c Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 13:49:31 -0400
Subject: [PATCH 4/7] add warning as fprintf

---
 libcxx/include/fstream | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 91259a39f4000..92baa4670b363 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -672,12 +672,6 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if defined(_AIX) || defined(__TOS_AIX__)
-#        pragma GCC diagnostic push
-#        pragma GCC diagnostic warning "-Wcpp"
-#        warning 'exclusive mode is unsupported on this target'
-#        pragma GCC diagnostic pop
-#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -780,9 +774,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr)
+  if (!__mdstr){
+#     if defined(_AIX) || defined(__TOS_AIX__)
+    if (__mode & ios_base::noreplace)
+      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
+#     endif
     return nullptr;
-
+}
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From eb0c8a9711dd9ed86e21ec217adb49f8cc7e3ec3 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:30:10 +0530
Subject: [PATCH 5/7] fix formatting

---
 libcxx/include/fstream | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 92baa4670b363..216082cfb8283 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -774,13 +774,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr){
-#     if defined(_AIX) || defined(__TOS_AIX__)
+  if (!__mdstr) {
+#      if defined(_AIX) || defined(__TOS_AIX__)
     if (__mode & ios_base::noreplace)
       fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#     endif
+#      endif
     return nullptr;
-}
+  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From 9f7f1e7bc05bf06350f2846f8f736366133b1b8b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:37:48 +0530
Subject: [PATCH 6/7] add the remaining test files

---
 .../fstreams/fstream.cons/pointer.pass.cpp    | 24 +++++++++++++++---
 .../fstream.members/open_pointer.pass.cpp     | 24 +++++++++++++++---
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 24 +++++++++++++++---
 .../ofstream.members/open_pointer.pass.cpp    | 25 ++++++++++++++++---
 4 files changed, 81 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..a677b16e76479 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
@@ -91,14 +89,23 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -108,14 +115,23 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..92c0cf1aa0c00 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -74,7 +72,10 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -82,7 +83,13 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -93,7 +100,10 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -101,7 +111,13 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..4fae51f38adbd 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
@@ -145,14 +143,23 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -162,14 +169,23 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..493af1bda9c98 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -82,7 +80,10 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -90,7 +91,13 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -101,14 +108,24 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
+
           {
             std::remove(tmp.c_str());
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif

>From 150ffaacc7f92197986a67f317374c030a7e78cf Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 6 Jul 2026 12:15:37 +0530
Subject: [PATCH 7/7] review changes

---
 libcxx/include/fstream                        | 21 +++++++++++--------
 .../filebuf.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/fstream.cons/pointer.pass.cpp    | 18 ++++------------
 .../fstream.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 18 ++++------------
 .../ofstream.members/open_pointer.pass.cpp    | 18 ++++------------
 6 files changed, 32 insertions(+), 79 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 216082cfb8283..f37522db70b1d 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -258,6 +258,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -671,8 +673,8 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // existence of the file and create it, it shall fail rather than perform a
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
-// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if !defined(_AIX) && !defined(__TOS_AIX__)
+// _AIX is defined on AIX by all supported compilers.
+#      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -683,7 +685,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
-#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
+#      endif // !defined(_AIX)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
@@ -774,13 +776,8 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr) {
-#      if defined(_AIX) || defined(__TOS_AIX__)
-    if (__mode & ios_base::noreplace)
-      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#      endif
+  if (!__mdstr)
     return nullptr;
-  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1222,6 +1219,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1380,6 +1379,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1544,6 +1545,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 627a65cac3f2f..37cfd985d1c11 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -73,10 +73,7 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -84,9 +81,7 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -101,10 +96,7 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -112,9 +104,7 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index a677b16e76479..2a297ca87903e 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -89,19 +89,14 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -115,19 +110,14 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 92c0cf1aa0c00..9178788b60354 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -72,10 +72,7 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -83,9 +80,7 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -100,10 +95,7 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -111,9 +103,7 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index 4fae51f38adbd..31a944236d1d0 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -143,19 +143,14 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -169,19 +164,14 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 493af1bda9c98..0b77f012b345d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -80,10 +80,7 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -91,9 +88,7 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -108,10 +103,7 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -119,9 +111,7 @@ int main(int, char**)
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist


From libcxx-commits at lists.llvm.org  Mon Jul  6 03:12:02 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 03:12:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918:
 `std::uninitialized_move/_n` and guaranteed copy elision (PR #207692)
Message-ID: <llvm.org/llvm/llvm-project/pull/207692@github.com>


https://github.com/inquisitivecrystal created https://github.com/llvm/llvm-project/pull/207692

This implements [LWG3918](wg21.link/LWG3918), which guarantees copy-elision for rvalues in `std::uninitialized_move/_n`. It also implements [LWG4452](wg21.link/LWG4452), a minor correction that makes the helper added by LWG3918 constexpr.

Interestingly, due to an implementation oversight, libc++'s `std::uninitialized_move/_n` didn't work *at all* for prvalues before this change. The previous implementation used a lambda `[](auto&& __iter) -> decltype(auto) { return std::move(*__iter); };`. If I'm understanding correctly, when the iterator's reference type is a prvalue, `*__iter` materializes a temporary and the lambda then returns a dangling reference to that temporary. The standard text doesn't have a lambda, so it would have allowed prvalues even before LWG3918, just with an extra copy. My tests cover this case and show that it now works correctly.

Resolves #118339.
Resolves #171416.


>From 8551a998c0e6da70f2168cd02b1249b28de12adf Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 5 Jul 2026 01:27:35 -0700
Subject: [PATCH 1/3] Ensure copy-elision for uninitialized_move(_n)

This implements the resolution for LWG issues 3918 and 4452.
---
 libcxx/include/__memory/uninitialized_algorithms.h | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..f5035ea2e21b9 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -28,6 +28,7 @@
 #include <__type_traits/extent.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
@@ -145,6 +146,14 @@ uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) {
 
 #if _LIBCPP_STD_VER >= 17
 
+template <class _Iter>
+_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) __deref_move(_Iter& __it) {
+  if constexpr (is_lvalue_reference_v<decltype(*__it)>)
+    return std::move(*__it);
+  else
+    return *__it;
+}
+
 // uninitialized_default_construct
 
 template <class _ValueType, class _ForwardIterator, class _Sentinel>
@@ -257,7 +266,7 @@ template <class _InputIterator, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
 uninitialized_move(_InputIterator __ifirst, _InputIterator __ilast, _ForwardIterator __ofirst) {
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
-  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::move(*__iter); };
+  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::__deref_move(__iter); };
 
   auto __result = std::__uninitialized_move<_ValueType>(
       std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __always_false(), __iter_move);
@@ -288,7 +297,7 @@ template <class _InputIterator, class _Size, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<_InputIterator, _ForwardIterator>
 uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst) {
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
-  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::move(*__iter); };
+  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::__deref_move(__iter); };
 
   auto __result = std::__uninitialized_move_n<_ValueType>(
       std::move(__ifirst), __n, std::move(__ofirst), __always_false(), __iter_move);

>From dad12baeec8d251e68ee97a73f237588da3518df Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 6 Jul 2026 02:38:47 -0700
Subject: [PATCH 2/3] Test changes to uninitialized_move(_n)

This tests that copies are elided for prvalues. It includes a
constexpr test in C++26.
---
 .../uninitialized_move.pass.cpp               | 62 ++++++++++++++++++
 .../uninitialized_move_n.pass.cpp             | 65 +++++++++++++++++++
 2 files changed, 127 insertions(+)

diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
index 13fef7a972e95..a248e25e8d966 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
+
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using difference_type   = std::ptrdiff_t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
+
+  constexpr NoMoveNoCopy operator*() const { return NoMoveNoCopy(*ptr_); }
+
+  constexpr PrvalueIterator& operator++() {
+    ++ptr_;
+    return *this;
+  }
+
+  friend constexpr bool operator==(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ == b.ptr_; }
+  friend constexpr bool operator!=(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ != b.ptr_; }
+
+private:
+  const int* ptr_;
+};
+
 TEST_CONSTEXPR_CXX26 bool test() {
   const int n    = 3;
   MoveOnly in[n] = {1, 2, 3};
@@ -128,9 +162,36 @@ void test_counted()
     assert(Counted::count == 0);
 }
 
+TEST_CONSTEXPR_CXX26 bool test_copy_elision() {
+  using It      = PrvalueIterator;
+  using FIt     = forward_iterator<NoMoveNoCopy*>;
+  const int N   = 5;
+  int values[N] = {1, 2, 3, 4, 5};
+  std::allocator<NoMoveNoCopy> alloc;
+  NoMoveNoCopy* p = alloc.allocate(N);
+  auto ret        = std::uninitialized_move(It(values), It(values + 1), FIt(p));
+  assert(ret == FIt(p + 1));
+  assert(p[0].value == 1);
+  assert(values[0] == 1);
+  ret = std::uninitialized_move(It(values + 1), It(values + N), FIt(p + 1));
+  assert(p[1].value == 2);
+  assert(p[2].value == 3);
+  assert(p[3].value == 4);
+  assert(p[4].value == 5);
+  assert(values[1] == 2);
+  assert(values[2] == 3);
+  assert(values[3] == 4);
+  assert(values[4] == 5);
+  std::destroy(p, p + N);
+  alloc.deallocate(p, N);
+
+  return true;
+}
+
 int main(int, char**) {
     test_counted();
     test_ctor_throws();
+    test_copy_elision();
 
     // Test with an iterator that overloads operator== and operator!= as the input and output iterators
     {
@@ -164,6 +225,7 @@ int main(int, char**) {
     test();
 #if TEST_STD_VER >= 26
     static_assert(test());
+    static_assert(test_copy_elision());
 #endif
 
     return 0;
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
index 785cfb406efd9..ab0b917972963 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
+
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using difference_type   = std::ptrdiff_t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
+
+  constexpr NoMoveNoCopy operator*() const { return NoMoveNoCopy(*ptr_); }
+
+  constexpr PrvalueIterator& operator++() {
+    ++ptr_;
+    return *this;
+  }
+
+  friend constexpr bool operator==(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ == b.ptr_; }
+  friend constexpr bool operator!=(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ != b.ptr_; }
+
+private:
+  const int* ptr_;
+};
+
 TEST_CONSTEXPR_CXX26 bool test() {
   const int n    = 3;
   MoveOnly in[n] = {1, 2, 3};
@@ -99,6 +133,35 @@ void test_ctor_throws()
 #endif
 }
 
+TEST_CONSTEXPR_CXX26 bool test_copy_elision() {
+  using It      = PrvalueIterator;
+  using FIt     = forward_iterator<NoMoveNoCopy*>;
+  const int N   = 5;
+  int values[N] = {1, 2, 3, 4, 5};
+  std::allocator<NoMoveNoCopy> alloc;
+  NoMoveNoCopy* p = alloc.allocate(N);
+  auto ret        = std::uninitialized_move_n(It(values), 1, FIt(p));
+  assert(ret.first == It(values + 1));
+  assert(ret.second == FIt(p + 1));
+  assert(p[0].value == 1);
+  assert(values[0] == 1);
+  ret = std::uninitialized_move_n(It(values + 1), N - 1, FIt(p + 1));
+  assert(ret.first == It(values + N));
+  assert(ret.second == FIt(p + N));
+  assert(p[1].value == 2);
+  assert(p[2].value == 3);
+  assert(p[3].value == 4);
+  assert(p[4].value == 5);
+  assert(values[1] == 2);
+  assert(values[2] == 3);
+  assert(values[3] == 4);
+  assert(values[4] == 5);
+  std::destroy(p, p + N);
+  alloc.deallocate(p, N);
+
+  return true;
+}
+
 void test_counted()
 {
     using It = cpp17_input_iterator<int*>;
@@ -135,6 +198,7 @@ int main(int, char**)
 {
     test_counted();
     test_ctor_throws();
+    test_copy_elision();
 
     // Test with an iterator that overloads operator== and operator!= as the input and output iterators
     {
@@ -168,6 +232,7 @@ int main(int, char**)
     test();
 #if TEST_STD_VER >= 26
     static_assert(test());
+    static_assert(test_copy_elision());
 #endif
 
     return 0;

>From d39772b756600b83cb9409648ceb5bb396ebf20c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 6 Jul 2026 02:47:30 -0700
Subject: [PATCH 3/3] Mark LWG issues 3918 and 4452 complete

---
 libcxx/docs/Status/Cxx26Issues.csv | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 3f2a446f7e1f3..cba4246e05361 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -81,7 +81,7 @@
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
 "`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
 "`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
-"`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
+"`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","|Complete|","23","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
 "`LWG4027 <https://wg21.link/LWG4027>`__","``possibly-const-range`` should prefer returning ``const R&``","2024-11 (Wrocław)","","","`#118342 <https://github.com/llvm/llvm-project/issues/118342>`__",""
@@ -250,7 +250,7 @@
 "`LWG4449 <https://wg21.link/LWG4449>`__","``define_aggregate`` members must be public","2025-11 (Kona)","","","`#171412 <https://github.com/llvm/llvm-project/issues/171412>`__",""
 "`LWG4450 <https://wg21.link/LWG4450>`__","``std::atomic_ref<T>::store_key`` should be disabled for const ``T``","2025-11 (Kona)","","","`#171414 <https://github.com/llvm/llvm-project/issues/171414>`__",""
 "`LWG4451 <https://wg21.link/LWG4451>`__","``make_shared`` should not refer to a type ``U[N]`` for runtime N","2025-11 (Kona)","","","`#171415 <https://github.com/llvm/llvm-project/issues/171415>`__",""
-"`LWG4452 <https://wg21.link/LWG4452>`__","Make *deref-move* constexpr","2025-11 (Kona)","","","`#171416 <https://github.com/llvm/llvm-project/issues/171416>`__",""
+"`LWG4452 <https://wg21.link/LWG4452>`__","Make *deref-move* constexpr","2025-11 (Kona)","|Complete|","23","`#171416 <https://github.com/llvm/llvm-project/issues/171416>`__",""
 "`LWG4455 <https://wg21.link/LWG4455>`__","Add missing constraint to ``basic-sender::get_completion_signatures`` definition","2025-11 (Kona)","","","`#171417 <https://github.com/llvm/llvm-project/issues/171417>`__",""
 "`LWG4456 <https://wg21.link/LWG4456>`__","Decay ``Data`` and ``Child`` in ``make-sender``","2025-11 (Kona)","","","`#171418 <https://github.com/llvm/llvm-project/issues/171418>`__",""
 "`LWG4459 <https://wg21.link/LWG4459>`__","Protect ``get_completion_signatures`` fold expression from overloaded commas","2025-11 (Kona)","","","`#171419 <https://github.com/llvm/llvm-project/issues/171419>`__",""


From libcxx-commits at lists.llvm.org  Mon Jul  6 03:12:20 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 03:12:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918:
 `std::uninitialized_move/_n` and guaranteed copy elision (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4b7f84.bf38ae28.358429.ed80@mx.google.com>


github-actions[bot] wrote:

<!--LLVM NEW CONTRIBUTOR COMMENT-->

Hello @inquisitivecrystal :wave:

Thank you for submitting a Pull Request (PR) to the LLVM Project. Since this is your first PR, here are a few useful links covering our main contribution policies and review practices.

* All contributions to LLVM must follow our [LLVM AI Tool Use Policy](https://llvm.org/docs/AIToolPolicy.html). In particular, if you used AI while working on this PR, remember to add a note to the PR description.
* The [LLVM Code-Review Policy and Practices](https://llvm.org/docs/CodeReview.html) document contains practical information about the PR process, including how patches are reviewed and accepted, and who can review a PR.
* Our [LLVM Developer Policy](https://llvm.org/docs/DeveloperPolicy.html) describes our expectations for code quality, commit summaries and contains notes on our CI system.

Please reply to this message to confirm that you have read these policies, especially the LLVM AI Tool Use Policy, and that any AI tool usage has been noted in the PR description.

---

### Frequently asked questions

**How do I add reviewers?**

This PR will be automatically labeled, and the relevant teams will be notified. For some parts of the project, reviewers may also be added automatically.

You can also add reviewers manually using the **Reviewers** section on this page. If you cannot use that section, it is probably because you do not have write permissions for the repository. In that case, you can request a review by tagging reviewers in a comment using `@` followed by their GitHub username.

**What if there are no comments?**

If you have not received any comments on your PR after a week, you can request a review by pinging the PR with a comment such as “Ping”. The common courtesy ping rate is once a week. Please remember that you are asking for volunteer time from other developers.

**Are any special GitHub settings required to contribute to LLVM?**

We only require contributors to have a public email address associated with their GitHub commits, see this [section](https://llvm.org/docs/DeveloperPolicy.html#email-addresses) of LLVM Developer Policy for details.

---

If you have questions, feel free to leave a comment on this PR, or ask on [LLVM Discord](https://discord.com/invite/xS7Z362) or [LLVM Discourse](https://discourse.llvm.org/).

Thank you,
The LLVM Community

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Mon Jul  6 03:12:54 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 03:12:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918:
 `std::uninitialized_move/_n` and guaranteed copy elision (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4b7fa6.8b87e04f.199b71.35e0@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: inquisitivecrystal (inquisitivecrystal)

<details>
<summary>Changes</summary>

This implements [LWG3918](wg21.link/LWG3918), which guarantees copy-elision for rvalues in `std::uninitialized_move/_n`. It also implements [LWG4452](wg21.link/LWG4452), a minor correction that makes the helper added by LWG3918 constexpr.

Interestingly, due to an implementation oversight, libc++'s `std::uninitialized_move/_n` didn't work *at all* for prvalues before this change. The previous implementation used a lambda `[](auto&amp;&amp; __iter) -&gt; decltype(auto) { return std::move(*__iter); };`. If I'm understanding correctly, when the iterator's reference type is a prvalue, `*__iter` materializes a temporary and the lambda then returns a dangling reference to that temporary. The standard text doesn't have a lambda, so it would have allowed prvalues even before LWG3918, just with an extra copy. My tests cover this case and show that it now works correctly.

Resolves #<!-- -->118339.
Resolves #<!-- -->171416.


---
Full diff: https://github.com/llvm/llvm-project/pull/207692.diff


4 Files Affected:

- (modified) libcxx/docs/Status/Cxx26Issues.csv (+2-2) 
- (modified) libcxx/include/__memory/uninitialized_algorithms.h (+11-2) 
- (modified) libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp (+62) 
- (modified) libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp (+65) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 3f2a446f7e1f3..cba4246e05361 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -81,7 +81,7 @@
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
 "`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
 "`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
-"`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
+"`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","|Complete|","23","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
 "`LWG4027 <https://wg21.link/LWG4027>`__","``possibly-const-range`` should prefer returning ``const R&``","2024-11 (Wrocław)","","","`#118342 <https://github.com/llvm/llvm-project/issues/118342>`__",""
@@ -250,7 +250,7 @@
 "`LWG4449 <https://wg21.link/LWG4449>`__","``define_aggregate`` members must be public","2025-11 (Kona)","","","`#171412 <https://github.com/llvm/llvm-project/issues/171412>`__",""
 "`LWG4450 <https://wg21.link/LWG4450>`__","``std::atomic_ref<T>::store_key`` should be disabled for const ``T``","2025-11 (Kona)","","","`#171414 <https://github.com/llvm/llvm-project/issues/171414>`__",""
 "`LWG4451 <https://wg21.link/LWG4451>`__","``make_shared`` should not refer to a type ``U[N]`` for runtime N","2025-11 (Kona)","","","`#171415 <https://github.com/llvm/llvm-project/issues/171415>`__",""
-"`LWG4452 <https://wg21.link/LWG4452>`__","Make *deref-move* constexpr","2025-11 (Kona)","","","`#171416 <https://github.com/llvm/llvm-project/issues/171416>`__",""
+"`LWG4452 <https://wg21.link/LWG4452>`__","Make *deref-move* constexpr","2025-11 (Kona)","|Complete|","23","`#171416 <https://github.com/llvm/llvm-project/issues/171416>`__",""
 "`LWG4455 <https://wg21.link/LWG4455>`__","Add missing constraint to ``basic-sender::get_completion_signatures`` definition","2025-11 (Kona)","","","`#171417 <https://github.com/llvm/llvm-project/issues/171417>`__",""
 "`LWG4456 <https://wg21.link/LWG4456>`__","Decay ``Data`` and ``Child`` in ``make-sender``","2025-11 (Kona)","","","`#171418 <https://github.com/llvm/llvm-project/issues/171418>`__",""
 "`LWG4459 <https://wg21.link/LWG4459>`__","Protect ``get_completion_signatures`` fold expression from overloaded commas","2025-11 (Kona)","","","`#171419 <https://github.com/llvm/llvm-project/issues/171419>`__",""
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..f5035ea2e21b9 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -28,6 +28,7 @@
 #include <__type_traits/extent.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
@@ -145,6 +146,14 @@ uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) {
 
 #if _LIBCPP_STD_VER >= 17
 
+template <class _Iter>
+_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) __deref_move(_Iter& __it) {
+  if constexpr (is_lvalue_reference_v<decltype(*__it)>)
+    return std::move(*__it);
+  else
+    return *__it;
+}
+
 // uninitialized_default_construct
 
 template <class _ValueType, class _ForwardIterator, class _Sentinel>
@@ -257,7 +266,7 @@ template <class _InputIterator, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
 uninitialized_move(_InputIterator __ifirst, _InputIterator __ilast, _ForwardIterator __ofirst) {
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
-  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::move(*__iter); };
+  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::__deref_move(__iter); };
 
   auto __result = std::__uninitialized_move<_ValueType>(
       std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __always_false(), __iter_move);
@@ -288,7 +297,7 @@ template <class _InputIterator, class _Size, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<_InputIterator, _ForwardIterator>
 uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst) {
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
-  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::move(*__iter); };
+  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::__deref_move(__iter); };
 
   auto __result = std::__uninitialized_move_n<_ValueType>(
       std::move(__ifirst), __n, std::move(__ofirst), __always_false(), __iter_move);
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
index 13fef7a972e95..a248e25e8d966 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
+
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using difference_type   = std::ptrdiff_t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
+
+  constexpr NoMoveNoCopy operator*() const { return NoMoveNoCopy(*ptr_); }
+
+  constexpr PrvalueIterator& operator++() {
+    ++ptr_;
+    return *this;
+  }
+
+  friend constexpr bool operator==(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ == b.ptr_; }
+  friend constexpr bool operator!=(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ != b.ptr_; }
+
+private:
+  const int* ptr_;
+};
+
 TEST_CONSTEXPR_CXX26 bool test() {
   const int n    = 3;
   MoveOnly in[n] = {1, 2, 3};
@@ -128,9 +162,36 @@ void test_counted()
     assert(Counted::count == 0);
 }
 
+TEST_CONSTEXPR_CXX26 bool test_copy_elision() {
+  using It      = PrvalueIterator;
+  using FIt     = forward_iterator<NoMoveNoCopy*>;
+  const int N   = 5;
+  int values[N] = {1, 2, 3, 4, 5};
+  std::allocator<NoMoveNoCopy> alloc;
+  NoMoveNoCopy* p = alloc.allocate(N);
+  auto ret        = std::uninitialized_move(It(values), It(values + 1), FIt(p));
+  assert(ret == FIt(p + 1));
+  assert(p[0].value == 1);
+  assert(values[0] == 1);
+  ret = std::uninitialized_move(It(values + 1), It(values + N), FIt(p + 1));
+  assert(p[1].value == 2);
+  assert(p[2].value == 3);
+  assert(p[3].value == 4);
+  assert(p[4].value == 5);
+  assert(values[1] == 2);
+  assert(values[2] == 3);
+  assert(values[3] == 4);
+  assert(values[4] == 5);
+  std::destroy(p, p + N);
+  alloc.deallocate(p, N);
+
+  return true;
+}
+
 int main(int, char**) {
     test_counted();
     test_ctor_throws();
+    test_copy_elision();
 
     // Test with an iterator that overloads operator== and operator!= as the input and output iterators
     {
@@ -164,6 +225,7 @@ int main(int, char**) {
     test();
 #if TEST_STD_VER >= 26
     static_assert(test());
+    static_assert(test_copy_elision());
 #endif
 
     return 0;
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
index 785cfb406efd9..ab0b917972963 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
+
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using difference_type   = std::ptrdiff_t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
+
+  constexpr NoMoveNoCopy operator*() const { return NoMoveNoCopy(*ptr_); }
+
+  constexpr PrvalueIterator& operator++() {
+    ++ptr_;
+    return *this;
+  }
+
+  friend constexpr bool operator==(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ == b.ptr_; }
+  friend constexpr bool operator!=(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ != b.ptr_; }
+
+private:
+  const int* ptr_;
+};
+
 TEST_CONSTEXPR_CXX26 bool test() {
   const int n    = 3;
   MoveOnly in[n] = {1, 2, 3};
@@ -99,6 +133,35 @@ void test_ctor_throws()
 #endif
 }
 
+TEST_CONSTEXPR_CXX26 bool test_copy_elision() {
+  using It      = PrvalueIterator;
+  using FIt     = forward_iterator<NoMoveNoCopy*>;
+  const int N   = 5;
+  int values[N] = {1, 2, 3, 4, 5};
+  std::allocator<NoMoveNoCopy> alloc;
+  NoMoveNoCopy* p = alloc.allocate(N);
+  auto ret        = std::uninitialized_move_n(It(values), 1, FIt(p));
+  assert(ret.first == It(values + 1));
+  assert(ret.second == FIt(p + 1));
+  assert(p[0].value == 1);
+  assert(values[0] == 1);
+  ret = std::uninitialized_move_n(It(values + 1), N - 1, FIt(p + 1));
+  assert(ret.first == It(values + N));
+  assert(ret.second == FIt(p + N));
+  assert(p[1].value == 2);
+  assert(p[2].value == 3);
+  assert(p[3].value == 4);
+  assert(p[4].value == 5);
+  assert(values[1] == 2);
+  assert(values[2] == 3);
+  assert(values[3] == 4);
+  assert(values[4] == 5);
+  std::destroy(p, p + N);
+  alloc.deallocate(p, N);
+
+  return true;
+}
+
 void test_counted()
 {
     using It = cpp17_input_iterator<int*>;
@@ -135,6 +198,7 @@ int main(int, char**)
 {
     test_counted();
     test_ctor_throws();
+    test_copy_elision();
 
     // Test with an iterator that overloads operator== and operator!= as the input and output iterators
     {
@@ -168,6 +232,7 @@ int main(int, char**)
     test();
 #if TEST_STD_VER >= 26
     static_assert(test());
+    static_assert(test_copy_elision());
 #endif
 
     return 0;

``````````

</details>


https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Mon Jul  6 03:13:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 03:13:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918: copy elision in
 `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4b7fc6.7270d2fd.23dbb3.e83d@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Mon Jul  6 03:17:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 03:17:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918: copy elision in
 `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4b80a5.a87eb5dc.1d432d.5e3d@mx.google.com>


inquisitivecrystal wrote:

> Please reply to this message to confirm that you have read these policies, especially the LLVM AI Tool Use Policy, and that any AI tool usage has been noted in the PR description.

I am familiar with the relevant policies. This PR does not contain any AI generated code.

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Mon Jul  6 03:27:56 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 03:27:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918: copy elision in
 `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4b832c.2020f6c2.1fdf61.e510@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/207692

>From 8551a998c0e6da70f2168cd02b1249b28de12adf Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 5 Jul 2026 01:27:35 -0700
Subject: [PATCH 1/3] Ensure copy-elision for uninitialized_move(_n)

This implements the resolution for LWG issues 3918 and 4452.
---
 libcxx/include/__memory/uninitialized_algorithms.h | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..f5035ea2e21b9 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -28,6 +28,7 @@
 #include <__type_traits/extent.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
@@ -145,6 +146,14 @@ uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) {
 
 #if _LIBCPP_STD_VER >= 17
 
+template <class _Iter>
+_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) __deref_move(_Iter& __it) {
+  if constexpr (is_lvalue_reference_v<decltype(*__it)>)
+    return std::move(*__it);
+  else
+    return *__it;
+}
+
 // uninitialized_default_construct
 
 template <class _ValueType, class _ForwardIterator, class _Sentinel>
@@ -257,7 +266,7 @@ template <class _InputIterator, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
 uninitialized_move(_InputIterator __ifirst, _InputIterator __ilast, _ForwardIterator __ofirst) {
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
-  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::move(*__iter); };
+  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::__deref_move(__iter); };
 
   auto __result = std::__uninitialized_move<_ValueType>(
       std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __always_false(), __iter_move);
@@ -288,7 +297,7 @@ template <class _InputIterator, class _Size, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<_InputIterator, _ForwardIterator>
 uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst) {
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
-  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::move(*__iter); };
+  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::__deref_move(__iter); };
 
   auto __result = std::__uninitialized_move_n<_ValueType>(
       std::move(__ifirst), __n, std::move(__ofirst), __always_false(), __iter_move);

>From dad12baeec8d251e68ee97a73f237588da3518df Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 6 Jul 2026 02:38:47 -0700
Subject: [PATCH 2/3] Test changes to uninitialized_move(_n)

This tests that copies are elided for prvalues. It includes a
constexpr test in C++26.
---
 .../uninitialized_move.pass.cpp               | 62 ++++++++++++++++++
 .../uninitialized_move_n.pass.cpp             | 65 +++++++++++++++++++
 2 files changed, 127 insertions(+)

diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
index 13fef7a972e95..a248e25e8d966 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
+
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using difference_type   = std::ptrdiff_t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
+
+  constexpr NoMoveNoCopy operator*() const { return NoMoveNoCopy(*ptr_); }
+
+  constexpr PrvalueIterator& operator++() {
+    ++ptr_;
+    return *this;
+  }
+
+  friend constexpr bool operator==(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ == b.ptr_; }
+  friend constexpr bool operator!=(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ != b.ptr_; }
+
+private:
+  const int* ptr_;
+};
+
 TEST_CONSTEXPR_CXX26 bool test() {
   const int n    = 3;
   MoveOnly in[n] = {1, 2, 3};
@@ -128,9 +162,36 @@ void test_counted()
     assert(Counted::count == 0);
 }
 
+TEST_CONSTEXPR_CXX26 bool test_copy_elision() {
+  using It      = PrvalueIterator;
+  using FIt     = forward_iterator<NoMoveNoCopy*>;
+  const int N   = 5;
+  int values[N] = {1, 2, 3, 4, 5};
+  std::allocator<NoMoveNoCopy> alloc;
+  NoMoveNoCopy* p = alloc.allocate(N);
+  auto ret        = std::uninitialized_move(It(values), It(values + 1), FIt(p));
+  assert(ret == FIt(p + 1));
+  assert(p[0].value == 1);
+  assert(values[0] == 1);
+  ret = std::uninitialized_move(It(values + 1), It(values + N), FIt(p + 1));
+  assert(p[1].value == 2);
+  assert(p[2].value == 3);
+  assert(p[3].value == 4);
+  assert(p[4].value == 5);
+  assert(values[1] == 2);
+  assert(values[2] == 3);
+  assert(values[3] == 4);
+  assert(values[4] == 5);
+  std::destroy(p, p + N);
+  alloc.deallocate(p, N);
+
+  return true;
+}
+
 int main(int, char**) {
     test_counted();
     test_ctor_throws();
+    test_copy_elision();
 
     // Test with an iterator that overloads operator== and operator!= as the input and output iterators
     {
@@ -164,6 +225,7 @@ int main(int, char**) {
     test();
 #if TEST_STD_VER >= 26
     static_assert(test());
+    static_assert(test_copy_elision());
 #endif
 
     return 0;
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
index 785cfb406efd9..ab0b917972963 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
+
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using difference_type   = std::ptrdiff_t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
+
+  constexpr NoMoveNoCopy operator*() const { return NoMoveNoCopy(*ptr_); }
+
+  constexpr PrvalueIterator& operator++() {
+    ++ptr_;
+    return *this;
+  }
+
+  friend constexpr bool operator==(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ == b.ptr_; }
+  friend constexpr bool operator!=(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ != b.ptr_; }
+
+private:
+  const int* ptr_;
+};
+
 TEST_CONSTEXPR_CXX26 bool test() {
   const int n    = 3;
   MoveOnly in[n] = {1, 2, 3};
@@ -99,6 +133,35 @@ void test_ctor_throws()
 #endif
 }
 
+TEST_CONSTEXPR_CXX26 bool test_copy_elision() {
+  using It      = PrvalueIterator;
+  using FIt     = forward_iterator<NoMoveNoCopy*>;
+  const int N   = 5;
+  int values[N] = {1, 2, 3, 4, 5};
+  std::allocator<NoMoveNoCopy> alloc;
+  NoMoveNoCopy* p = alloc.allocate(N);
+  auto ret        = std::uninitialized_move_n(It(values), 1, FIt(p));
+  assert(ret.first == It(values + 1));
+  assert(ret.second == FIt(p + 1));
+  assert(p[0].value == 1);
+  assert(values[0] == 1);
+  ret = std::uninitialized_move_n(It(values + 1), N - 1, FIt(p + 1));
+  assert(ret.first == It(values + N));
+  assert(ret.second == FIt(p + N));
+  assert(p[1].value == 2);
+  assert(p[2].value == 3);
+  assert(p[3].value == 4);
+  assert(p[4].value == 5);
+  assert(values[1] == 2);
+  assert(values[2] == 3);
+  assert(values[3] == 4);
+  assert(values[4] == 5);
+  std::destroy(p, p + N);
+  alloc.deallocate(p, N);
+
+  return true;
+}
+
 void test_counted()
 {
     using It = cpp17_input_iterator<int*>;
@@ -135,6 +198,7 @@ int main(int, char**)
 {
     test_counted();
     test_ctor_throws();
+    test_copy_elision();
 
     // Test with an iterator that overloads operator== and operator!= as the input and output iterators
     {
@@ -168,6 +232,7 @@ int main(int, char**)
     test();
 #if TEST_STD_VER >= 26
     static_assert(test());
+    static_assert(test_copy_elision());
 #endif
 
     return 0;

>From 0a53270b60cc74d91306e400dee5163777b9e180 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 6 Jul 2026 02:47:30 -0700
Subject: [PATCH 3/3] Mark LWG issues 3918 and 4452 as complete

---
 libcxx/docs/Status/Cxx26Issues.csv | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 3f2a446f7e1f3..cba4246e05361 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -81,7 +81,7 @@
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
 "`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
 "`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
-"`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
+"`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","|Complete|","23","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
 "`LWG4027 <https://wg21.link/LWG4027>`__","``possibly-const-range`` should prefer returning ``const R&``","2024-11 (Wrocław)","","","`#118342 <https://github.com/llvm/llvm-project/issues/118342>`__",""
@@ -250,7 +250,7 @@
 "`LWG4449 <https://wg21.link/LWG4449>`__","``define_aggregate`` members must be public","2025-11 (Kona)","","","`#171412 <https://github.com/llvm/llvm-project/issues/171412>`__",""
 "`LWG4450 <https://wg21.link/LWG4450>`__","``std::atomic_ref<T>::store_key`` should be disabled for const ``T``","2025-11 (Kona)","","","`#171414 <https://github.com/llvm/llvm-project/issues/171414>`__",""
 "`LWG4451 <https://wg21.link/LWG4451>`__","``make_shared`` should not refer to a type ``U[N]`` for runtime N","2025-11 (Kona)","","","`#171415 <https://github.com/llvm/llvm-project/issues/171415>`__",""
-"`LWG4452 <https://wg21.link/LWG4452>`__","Make *deref-move* constexpr","2025-11 (Kona)","","","`#171416 <https://github.com/llvm/llvm-project/issues/171416>`__",""
+"`LWG4452 <https://wg21.link/LWG4452>`__","Make *deref-move* constexpr","2025-11 (Kona)","|Complete|","23","`#171416 <https://github.com/llvm/llvm-project/issues/171416>`__",""
 "`LWG4455 <https://wg21.link/LWG4455>`__","Add missing constraint to ``basic-sender::get_completion_signatures`` definition","2025-11 (Kona)","","","`#171417 <https://github.com/llvm/llvm-project/issues/171417>`__",""
 "`LWG4456 <https://wg21.link/LWG4456>`__","Decay ``Data`` and ``Child`` in ``make-sender``","2025-11 (Kona)","","","`#171418 <https://github.com/llvm/llvm-project/issues/171418>`__",""
 "`LWG4459 <https://wg21.link/LWG4459>`__","Protect ``get_completion_signatures`` fold expression from overloaded commas","2025-11 (Kona)","","","`#171419 <https://github.com/llvm/llvm-project/issues/171419>`__",""


From libcxx-commits at lists.llvm.org  Mon Jul  6 03:36:24 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 03:36:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 18c0f3a - [libc++] Use
 _LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23 in <future> (#207668)
Message-ID: <6a4b8528.2020f6c2.1fdf61.1828@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-06T12:36:19+02:00
New Revision: 18c0f3a20c46b79d5a1023372982bc73b39b6627

URL: https://github.com/llvm/llvm-project/commit/18c0f3a20c46b79d5a1023372982bc73b39b6627
DIFF: https://github.com/llvm/llvm-project/commit/18c0f3a20c46b79d5a1023372982bc73b39b6627.diff

LOG: [libc++] Use _LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23 in <future> (#207668)

`_LIBCPP_REMOVE_TRANSITIVE_INCLUDES` was used accidentally.

Added: 
    

Modified: 
    libcxx/include/future

Removed: 
    


################################################################################
diff  --git a/libcxx/include/future b/libcxx/include/future
index 498fda99fdafd..fd265972ab750 100644
--- a/libcxx/include/future
+++ b/libcxx/include/future
@@ -2079,7 +2079,7 @@ _LIBCPP_POP_MACROS
 #    include <thread>
 #  endif
 
-#  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 23
+#  if defined(_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23) && _LIBCPP_STD_VER <= 23
 #    include <sstream>
 #  endif
 


        

From libcxx-commits at lists.llvm.org  Mon Jul  6 03:36:26 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 03:36:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use
 _LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23 in <future> (PR #207668)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207668@github.com>
Message-ID: <6a4b852a.af6ba3ed.2c2f30.d53f@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/207668

From libcxx-commits at lists.llvm.org  Mon Jul  6 03:41:03 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 03:41:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a4b863f.491d90f5.1f0d58.926c@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Mon Jul  6 03:41:17 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 03:41:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918: copy elision in
 `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4b864d.faf16b37.1e0c59.e76e@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Mon Jul  6 03:43:25 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 03:43:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918: copy elision in
 `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4b86cd.bd93e731.b068b.0d35@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Mon Jul  6 04:20:35 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 04:20:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor vector's ASan
 annotations to only ever delete and add the annotations (PR #152967)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/152967@github.com>
Message-ID: <6a4b8f83.bb839515.23e6d3.15f8@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/152967

>From 83ec440655122b47bbeb4fdb571c344b5ef19b63 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 11 Aug 2025 10:02:01 +0200
Subject: [PATCH] [libc++] Refactor vector's ASan annotations to only ever
 delete and add the annotations

---
 libcxx/include/__utility/scope_guard.h |   3 +
 libcxx/include/__vector/vector.h       | 107 +++++++++++--------------
 2 files changed, 49 insertions(+), 61 deletions(-)

diff --git a/libcxx/include/__utility/scope_guard.h b/libcxx/include/__utility/scope_guard.h
index db4f0e4c73a2a..19da144d5c980 100644
--- a/libcxx/include/__utility/scope_guard.h
+++ b/libcxx/include/__utility/scope_guard.h
@@ -37,7 +37,10 @@ class __scope_guard {
 // C++14 doesn't have mandatory RVO, so we have to provide a declaration even though no compiler will ever generate
 // a call to the move constructor.
 #if _LIBCPP_STD_VER <= 14
+  _LIBCPP_DIAGNOSTIC_PUSH
+  _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wundefined-internal")
   __scope_guard(__scope_guard&&);
+  _LIBCPP_DIAGNOSTIC_POP
 #else
   __scope_guard(__scope_guard&&) = delete;
 #endif
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..c6124b9ba71b2 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -65,6 +65,7 @@
 #include <__utility/is_pointer_in_range.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
+#include <__utility/scope_guard.h>
 #include <__utility/swap.h>
 #include <initializer_list>
 #include <limits>
@@ -474,10 +475,11 @@ class vector {
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __emplace_back_assume_capacity(_Args&&... __args) {
     _LIBCPP_ASSERT_INTERNAL(
         size() < capacity(), "We assume that we have enough space to insert an element at the end of the vector");
-    _ConstructTransaction __tx(*this, 1);
+    __annotate_delete();
+    auto __guard = std::__make_scope_guard(__annotate_new_size(*this));
     __alloc_traits::construct(
-        this->__layout_.__alloc(), std::__to_address(__tx.__pos_), std::forward<_Args>(__args)...);
-    ++__tx.__pos_;
+        __layout_.__alloc(), std::__to_address(__layout_.__end_ptr()), std::forward<_Args>(__args)...);
+    __layout_.__set_bound_using_pointer(__layout_.__end_ptr() + 1);
   }
 
 #if _LIBCPP_STD_VER >= 23
@@ -486,7 +488,7 @@ class vector {
     if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) {
       auto __len = ranges::distance(__range);
       if (__len <= static_cast<difference_type>(__layout_.__remaining_capacity())) {
-        __construct_at_end(ranges::begin(__range), ranges::end(__range), __len);
+        __construct_at_end(ranges::begin(__range), ranges::end(__range));
       } else {
         _SplitBuffer __buffer(__recommend(size() + __len), size(), __layout_.__alloc());
         __buffer.__construct_at_end_with_size(ranges::begin(__range), __len);
@@ -558,9 +560,9 @@ class vector {
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last);
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT {
-    size_type __old_size = size();
-    __base_destruct_at_end(this->__layout_.__begin_ptr());
-    __annotate_shrink(__old_size);
+    __annotate_delete();
+    auto __guard = std::__make_scope_guard(__annotate_new_size(*this));
+    __base_destruct_at_end(__layout_.__begin_ptr());
   }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void resize(size_type __sz);
@@ -623,7 +625,7 @@ class vector {
 
     if (__n > 0) {
       __vallocate(__n);
-      __construct_at_end(std::move(__first), std::move(__last), __n);
+      __construct_at_end(std::move(__first), std::move(__last));
     }
 
     __guard.__complete();
@@ -678,8 +680,7 @@ class vector {
   __insert_with_size(const_iterator __position, _Iterator __first, _Sentinel __last, difference_type __n);
 
   template <class _InputIterator, class _Sentinel>
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
-  __construct_at_end(_InputIterator __first, _Sentinel __last, size_type __n);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __construct_at_end(_InputIterator __first, _Sentinel __last);
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator __make_iter(pointer __p) _NOEXCEPT {
 #ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR
@@ -714,9 +715,9 @@ class vector {
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign(vector& __c, false_type)
       _NOEXCEPT_(__alloc_traits::is_always_equal::value);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __destruct_at_end(pointer __new_last) _NOEXCEPT {
-    size_type __old_size = size();
+    __annotate_delete();
+    auto __guard = std::__make_scope_guard(__annotate_new_size(*this));
     __base_destruct_at_end(__new_last);
-    __annotate_shrink(__old_size);
   }
 
   template <class... _Args>
@@ -743,33 +744,11 @@ class vector {
     __annotate_contiguous_container(data() + size(), data() + capacity());
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __annotate_increase(size_type __n) const _NOEXCEPT {
-    __annotate_contiguous_container(data() + size(), data() + size() + __n);
-  }
-
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __annotate_shrink(size_type __old_size) const _NOEXCEPT {
-    __annotate_contiguous_container(data() + __old_size, data() + size());
-  }
-
-  struct _ConstructTransaction {
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI explicit _ConstructTransaction(vector& __v, size_type __n)
-        : __v_(__v), __pos_(__v.__layout_.__end_ptr()), __new_end_(__pos_ + __n) {
-      __v_.__annotate_increase(__n);
-    }
-
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI ~_ConstructTransaction() {
-      __v_.__layout_.__set_bound_using_pointer(__pos_);
-      if (__pos_ != __new_end_) {
-        __v_.__annotate_shrink(__new_end_ - __v_.__layout_.__begin_ptr());
-      }
-    }
-
-    vector& __v_;
-    pointer __pos_;
-    const_pointer const __new_end_;
+  struct __annotate_new_size {
+    vector& __vec_;
 
-    _ConstructTransaction(_ConstructTransaction const&)            = delete;
-    _ConstructTransaction& operator=(_ConstructTransaction const&) = delete;
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __annotate_new_size(vector& __vec) : __vec_(__vec) {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void operator()() { __vec_.__annotate_new(__vec_.size()); }
   };
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __base_destruct_at_end(pointer __new_last) _NOEXCEPT {
@@ -879,10 +858,12 @@ vector<_Tp, _Allocator>::__recommend(size_type __new_size) const {
 //  Postcondition:  size() == size() + __n
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__construct_at_end(size_type __n) {
-  _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos));
+  __annotate_delete();
+  auto __guard = std::__make_scope_guard(__annotate_new_size(*this));
+
+  for (size_t __i = 0; __i != __n; ++__i) {
+    __alloc_traits::construct(__layout_.__alloc(), std::__to_address(__layout_.__end_ptr()));
+    __layout_.__set_bound_using_pointer(__layout_.__end_ptr() + 1);
   }
 }
 
@@ -895,20 +876,23 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__construct_at_end(s
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline void
 vector<_Tp, _Allocator>::__construct_at_end(size_type __n, const_reference __x) {
-  _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), __x);
+  __annotate_delete();
+  auto __guard = std::__make_scope_guard(__annotate_new_size(*this));
+
+  for (size_t __i = 0; __i != __n; ++__i) {
+    __alloc_traits::construct(__layout_.__alloc(), std::__to_address(__layout_.__end_ptr()), __x);
+    __layout_.__set_bound_using_pointer(__layout_.__end_ptr() + 1);
   }
 }
 
 template <class _Tp, class _Allocator>
 template <class _InputIterator, class _Sentinel>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-vector<_Tp, _Allocator>::__construct_at_end(_InputIterator __first, _Sentinel __last, size_type __n) {
-  _ConstructTransaction __tx(*this, __n);
-  __tx.__pos_ = std::__uninitialized_allocator_copy(
-      this->__layout_.__alloc(), std::move(__first), std::move(__last), __tx.__pos_);
+vector<_Tp, _Allocator>::__construct_at_end(_InputIterator __first, _Sentinel __last) {
+  __annotate_delete();
+  auto __guard = std::__make_scope_guard(__annotate_new_size(*this));
+  __layout_.__set_bound_using_pointer(std::__uninitialized_allocator_copy(
+      __layout_.__alloc(), std::move(__first), std::move(__last), __layout_.__end_ptr()));
 }
 
 template <class _Tp, class _Allocator>
@@ -985,8 +969,8 @@ vector<_Tp, _Allocator>::__assign_with_size(_Iterator __first, _Sentinel __last,
   if (__new_size <= capacity()) {
     auto const __size = size();
     if (__new_size > __size) {
-      auto __mid = std::__copy_n<_AlgPolicy>(std::move(__first), __size, this->__layout_.__begin_ptr()).__in_;
-      __construct_at_end(std::move(__mid), std::move(__last), __new_size - __size);
+      auto __mid = std::__copy_n<_AlgPolicy>(std::move(__first), __size, __layout_.__begin_ptr()).__in_;
+      __construct_at_end(std::move(__mid), std::move(__last));
     } else {
       pointer __m = std::__copy(std::move(__first), __last, this->__layout_.__begin_ptr()).__out_;
       this->__destruct_at_end(__m);
@@ -994,7 +978,7 @@ vector<_Tp, _Allocator>::__assign_with_size(_Iterator __first, _Sentinel __last,
   } else {
     __vdeallocate();
     __vallocate(__recommend(__new_size));
-    __construct_at_end(std::move(__first), std::move(__last), __new_size);
+    __construct_at_end(std::move(__first), std::move(__last));
   }
 }
 
@@ -1118,15 +1102,16 @@ vector<_Tp, _Allocator>::erase(const_iterator __first, const_iterator __last) {
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointer __to) {
+  __annotate_delete();
+  auto __guard        = std::__make_scope_guard(__annotate_new_size(*this));
   pointer __old_last  = __layout_.__end_ptr();
   difference_type __n = __old_last - __to;
-  {
-    pointer __i = __from_s + __n;
-    _ConstructTransaction __tx(*this, __from_e - __i);
-    for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
-      __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
-    }
+
+  for (pointer __i = __from_s + __n; __i != __from_e; ++__i) {
+    __alloc_traits::construct(__layout_.__alloc(), std::__to_address(__layout_.__end_ptr()), std::move(*__i));
+    __layout_.__set_bound_using_pointer(__layout_.__end_ptr() + 1);
   }
+
   std::move_backward(__from_s, __from_s + __n, __old_last);
 }
 
@@ -1280,13 +1265,13 @@ vector<_Tp, _Allocator>::__insert_with_size(
       if (__n > __dx) {
 #if _LIBCPP_STD_VER >= 23
         if constexpr (!forward_iterator<_Iterator>) {
-          __construct_at_end(std::move(__first), std::move(__last), __n);
+          __construct_at_end(std::move(__first), std::move(__last));
           std::rotate(__p, __old_last, __end);
         } else
 #endif
         {
           _Iterator __m = std::next(__first, __dx);
-          __construct_at_end(__m, __last, __n - __dx);
+          __construct_at_end(__m, __last);
           if (__dx > 0) {
             __move_range(__p, __old_last, __p + __n);
             __insert_assign_n_unchecked<_AlgPolicy>(__first, __dx, __p);


From libcxx-commits at lists.llvm.org  Mon Jul  6 04:21:22 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 04:21:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make __is_std_ templates
	variable templates (PR #150590)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/150590@github.com>
Message-ID: <6a4b8fb2.58bdd12c.21857.7497@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/150590

From libcxx-commits at lists.llvm.org  Mon Jul  6 04:54:42 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 04:54:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add warning groups to all our
	diagnose_if uses (PR #128759)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/128759@github.com>
Message-ID: <6a4b9782.6e01836d.318eb9.26b5@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/128759

>From febc806d9ccd72e99f05fc1f3d6098e756084aa4 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 25 Feb 2025 19:52:57 +0100
Subject: [PATCH] [libc++] Add warning groups to diagnose_if when available

---
 libcxx/include/__atomic/check_memory_order.h  | 17 +++++++-------
 libcxx/include/__configuration/attributes.h   |  7 ------
 libcxx/include/__filesystem/path.h            |  2 +-
 libcxx/include/__hash_table                   | 22 ++++++++++++-------
 libcxx/include/__tree                         | 12 +++++-----
 libcxx/include/map                            | 10 ++++-----
 libcxx/include/module.modulemap.in            |  1 +
 libcxx/include/set                            | 10 ++++-----
 libcxx/include/unordered_map                  |  6 +++--
 libcxx/include/unordered_set                  |  6 +++--
 .../non_const_comparator.verify.cpp           | 10 ++++-----
 .../unord/non_const_comparator.verify.cpp     | 19 ++++++++--------
 .../strings/basic.string/nonnull.verify.cpp   |  3 ---
 .../strings/string.view/nonnull.verify.cpp    |  3 ---
 14 files changed, 61 insertions(+), 67 deletions(-)

diff --git a/libcxx/include/__atomic/check_memory_order.h b/libcxx/include/__atomic/check_memory_order.h
index 536f764a61902..da9e3a8ecd1c1 100644
--- a/libcxx/include/__atomic/check_memory_order.h
+++ b/libcxx/include/__atomic/check_memory_order.h
@@ -15,20 +15,21 @@
 #  pragma GCC system_header
 #endif
 
+#define _LIBCPP_DIAGNOSE_ATOMIC_MEMORY_ORDERING_IF(condition)                                                          \
+  _LIBCPP_DIAGNOSE_IF(                                                                                                 \
+      condition, "memory order argument to atomic operation is invalid", "warning", "atomic-memory-ordering")
+
 #define _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m)                                                                          \
-  _LIBCPP_DIAGNOSE_WARNING(__m == memory_order_consume || __m == memory_order_acquire || __m == memory_order_acq_rel,  \
-                           "memory order argument to atomic operation is invalid")
+  _LIBCPP_DIAGNOSE_ATOMIC_MEMORY_ORDERING_IF(                                                                          \
+      __m == memory_order_consume || __m == memory_order_acquire || __m == memory_order_acq_rel)
 
 #define _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m)                                                                           \
-  _LIBCPP_DIAGNOSE_WARNING(__m == memory_order_release || __m == memory_order_acq_rel,                                 \
-                           "memory order argument to atomic operation is invalid")
+  _LIBCPP_DIAGNOSE_ATOMIC_MEMORY_ORDERING_IF(__m == memory_order_release || __m == memory_order_acq_rel)
 
 #define _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__m, __f)                                                                  \
-  _LIBCPP_DIAGNOSE_WARNING(__f == memory_order_release || __f == memory_order_acq_rel,                                 \
-                           "memory order argument to atomic operation is invalid")
+  _LIBCPP_DIAGNOSE_ATOMIC_MEMORY_ORDERING_IF(__f == memory_order_release || __f == memory_order_acq_rel)
 
 #define _LIBCPP_CHECK_WAIT_MEMORY_ORDER(__m)                                                                           \
-  _LIBCPP_DIAGNOSE_WARNING(__m == memory_order_release || __m == memory_order_acq_rel,                                 \
-                           "memory order argument to atomic operation is invalid")
+  _LIBCPP_DIAGNOSE_ATOMIC_MEMORY_ORDERING_IF(__m == memory_order_release || __m == memory_order_acq_rel)
 
 #endif // _LIBCPP___ATOMIC_CHECK_MEMORY_ORDER_H
diff --git a/libcxx/include/__configuration/attributes.h b/libcxx/include/__configuration/attributes.h
index c543ba9cb9e25..a30ad26404508 100644
--- a/libcxx/include/__configuration/attributes.h
+++ b/libcxx/include/__configuration/attributes.h
@@ -255,13 +255,6 @@
 #endif
 
 #if __has_attribute(__diagnose_if__)
-#  define _LIBCPP_DIAGNOSE_WARNING(...) __attribute__((__diagnose_if__(__VA_ARGS__, "warning")))
-#else
-#  define _LIBCPP_DIAGNOSE_WARNING(...)
-#endif
-
-#if __has_attribute(__diagnose_if__) && !defined(_LIBCPP_APPLE_CLANG_VER) &&                                           \
-    (!defined(_LIBCPP_CLANG_VER) || _LIBCPP_CLANG_VER >= 2001)
 #  define _LIBCPP_DIAGNOSE_IF(...) __attribute__((__diagnose_if__(__VA_ARGS__)))
 #else
 #  define _LIBCPP_DIAGNOSE_IF(...)
diff --git a/libcxx/include/__filesystem/path.h b/libcxx/include/__filesystem/path.h
index a63c4ee611ebf..a77439b632f0d 100644
--- a/libcxx/include/__filesystem/path.h
+++ b/libcxx/include/__filesystem/path.h
@@ -513,7 +513,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
     return *this;
   }
 
-  // FIXME: Use _LIBCPP_DIAGNOSE_WARNING to produce a diagnostic when __src
+  // FIXME: Use _LIBCPP_DIAGNOSE_IF to produce a diagnostic when __src
   // is known at compile time to be "/' since the user almost certainly intended
   // to append a separator instead of overwriting the path with "/"
   template <class _Source>
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 0ce4cf3edd6fd..9c3472c75fca2 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -596,15 +596,21 @@ struct __enforce_unordered_container_requirements {
   typedef int type;
 };
 
+#define _LIBCPP_DIAGNOSE_UNORDERED_CONTAINER_REQUIREMENTS(_Key, _Hash, _Equal)                                         \
+  _LIBCPP_DIAGNOSE_IF(                                                                                                 \
+      !__is_invocable_v<_Equal const&, _Key const&, _Key const&>,                                                      \
+      "the specified comparator type does not provide a viable const call operator",                                   \
+      "warning",                                                                                                       \
+      "discard-qual")                                                                                                  \
+  _LIBCPP_DIAGNOSE_IF(                                                                                                 \
+      !__is_invocable_v<_Hash const&, _Key const&>,                                                                    \
+      "the specified hash functor does not provide a viable const call operator",                                      \
+      "warning",                                                                                                       \
+      "discard-qual")
+
 template <class _Key, class _Hash, class _Equal>
-#ifndef _LIBCPP_CXX03_LANG
-_LIBCPP_DIAGNOSE_WARNING(!__is_invocable_v<_Equal const&, _Key const&, _Key const&>,
-                         "the specified comparator type does not provide a viable const call operator")
-_LIBCPP_DIAGNOSE_WARNING(!__is_invocable_v<_Hash const&, _Key const&>,
-                         "the specified hash functor does not provide a viable const call operator")
-#endif
-    typename __enforce_unordered_container_requirements<_Key, _Hash, _Equal>::type
-    __diagnose_unordered_container_requirements(int);
+typename __enforce_unordered_container_requirements<_Key, _Hash, _Equal>::type
+__diagnose_unordered_container_requirements(int);
 
 // This dummy overload is used so that the compiler won't emit a spurious
 // "no matching function for call to __diagnose_unordered_xxx" diagnostic
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index dafeff3e2e019..b46716e87f9e4 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -894,12 +894,12 @@ struct __specialized_algorithm<
 };
 #endif
 
-template <class _Tp, class _Compare>
-#ifndef _LIBCPP_CXX03_LANG
-_LIBCPP_DIAGNOSE_WARNING(!__is_invocable_v<_Compare const&, _Tp const&, _Tp const&>,
-                         "the specified comparator type does not provide a viable const call operator")
-#endif
-int __diagnose_non_const_comparator();
+#define _LIBCPP_DIAGNOSE_NON_CONST_COMPARATOR(_Compare, _Tp)                                                           \
+  _LIBCPP_DIAGNOSE_IF(                                                                                                 \
+      !__is_invocable_v<_Compare const&, _Tp const&, _Tp const&>,                                                      \
+      "the specified comparator type does not provide a viable const call operator",                                   \
+      "warning",                                                                                                       \
+      "discard-qual")
 
 template <class _Tp, class _Compare, class _Allocator>
 class __tree {
diff --git a/libcxx/include/map b/libcxx/include/map
index 8efe18fa3f839..f9c1528d0043c 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1092,9 +1092,8 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 map(const map& __m, const allocator_type& __alloc)
       : __tree_(__m.__tree_, __alloc) {}
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~map() {
-    static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~map()
+      _LIBCPP_DIAGNOSE_NON_CONST_COMPARATOR(key_compare, key_type) {}
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT {
     return __tree_.begin();
@@ -1876,9 +1875,8 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(const multimap& __m, const allocator_type& __a)
       : __tree_(__m.__tree_, __a) {}
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~multimap() {
-    static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~multimap()
+      _LIBCPP_DIAGNOSE_NON_CONST_COMPARATOR(key_compare, key_type) {}
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT {
     return __tree_.begin();
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..ed8b052333021 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2413,6 +2413,7 @@ module std {
   module tree                 {
     header "__tree"
     export std.memory.unique_ptr
+    export std_core.type_traits.invoke
   }
   module std_mbstate_t {
     header "__std_mbstate_t.h"
diff --git a/libcxx/include/set b/libcxx/include/set
index 0409131ac6981..993e854a78a98 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -709,9 +709,8 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set& operator=(set&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~set() {
-    static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~set()
+      _LIBCPP_DIAGNOSE_NON_CONST_COMPARATOR(key_compare, key_type) {}
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT {
     return __tree_.begin();
@@ -1268,9 +1267,8 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(multiset&& __s) = default;
 #  endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~multiset() {
-    static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~multiset()
+      _LIBCPP_DIAGNOSE_NON_CONST_COMPARATOR(key_compare, key_type) {}
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT {
     return __tree_.begin();
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 8cefba01fb5f6..9e39db76b21f2 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1024,7 +1024,8 @@ public:
   unordered_map(initializer_list<value_type> __il, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_map(__il, __n, __hf, key_equal(), __a) {}
 #  endif
-  _LIBCPP_HIDE_FROM_ABI ~unordered_map() {
+  _LIBCPP_HIDE_FROM_ABI ~unordered_map()
+      _LIBCPP_DIAGNOSE_UNORDERED_CONTAINER_REQUIREMENTS(key_type, hasher, key_equal) {
     static_assert(sizeof(std::__diagnose_unordered_container_requirements<_Key, _Hash, _Pred>(0)), "");
   }
 
@@ -1818,7 +1819,8 @@ public:
   unordered_multimap(initializer_list<value_type> __il, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_multimap(__il, __n, __hf, key_equal(), __a) {}
 #  endif
-  _LIBCPP_HIDE_FROM_ABI ~unordered_multimap() {
+  _LIBCPP_HIDE_FROM_ABI ~unordered_multimap()
+      _LIBCPP_DIAGNOSE_UNORDERED_CONTAINER_REQUIREMENTS(key_type, hasher, key_equal) {
     static_assert(sizeof(std::__diagnose_unordered_container_requirements<_Key, _Hash, _Pred>(0)), "");
   }
 
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index d54d59403b038..60b661072ce08 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -726,7 +726,8 @@ public:
       : unordered_set(__il, __n, __hf, key_equal(), __a) {}
 #    endif
 #  endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI ~unordered_set() {
+  _LIBCPP_HIDE_FROM_ABI ~unordered_set()
+      _LIBCPP_DIAGNOSE_UNORDERED_CONTAINER_REQUIREMENTS(key_type, hasher, key_equal) {
     static_assert(sizeof(std::__diagnose_unordered_container_requirements<_Value, _Hash, _Pred>(0)), "");
   }
 
@@ -1340,7 +1341,8 @@ public:
       : unordered_multiset(__il, __n, __hf, key_equal(), __a) {}
 #    endif
 #  endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI ~unordered_multiset() {
+  _LIBCPP_HIDE_FROM_ABI ~unordered_multiset()
+      _LIBCPP_DIAGNOSE_UNORDERED_CONTAINER_REQUIREMENTS(key_type, hasher, key_equal) {
     static_assert(sizeof(std::__diagnose_unordered_container_requirements<_Value, _Hash, _Pred>(0)), "");
   }
 
diff --git a/libcxx/test/libcxx/containers/associative/non_const_comparator.verify.cpp b/libcxx/test/libcxx/containers/associative/non_const_comparator.verify.cpp
index cb7a044abd8c0..8c338be91110e 100644
--- a/libcxx/test/libcxx/containers/associative/non_const_comparator.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/non_const_comparator.verify.cpp
@@ -27,22 +27,20 @@ void f() {
   static_assert(!std::__is_invocable_v<BadCompare const&, int const&, int const&>, "");
   static_assert(std::__is_invocable_v<BadCompare&, int const&, int const&>, "");
 
-  // expected-warning at set:* 2 {{the specified comparator type does not provide a viable const call operator}}
-  // expected-warning at map:* 2 {{the specified comparator type does not provide a viable const call operator}}
   {
     using C = std::set<int, BadCompare>;
-    C s;
+    C s; // expected-warning {{the specified comparator type does not provide a viable const call operator}}
   }
   {
     using C = std::multiset<long, BadCompare>;
-    C s;
+    C s; // expected-warning {{the specified comparator type does not provide a viable const call operator}}
   }
   {
     using C = std::map<int, int, BadCompare>;
-    C s;
+    C s; // expected-warning {{the specified comparator type does not provide a viable const call operator}}
   }
   {
     using C = std::multimap<long, int, BadCompare>;
-    C s;
+    C s; // expected-warning {{the specified comparator type does not provide a viable const call operator}}
   }
 }
diff --git a/libcxx/test/libcxx/containers/unord/non_const_comparator.verify.cpp b/libcxx/test/libcxx/containers/unord/non_const_comparator.verify.cpp
index c3418302d0315..c3a58a5c72299 100644
--- a/libcxx/test/libcxx/containers/unord/non_const_comparator.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/non_const_comparator.verify.cpp
@@ -12,6 +12,8 @@
 // Test that libc++ generates a warning diagnostic when the container is
 // provided a non-const callable comparator or a non-const hasher.
 
+// ADDITIONAL_COMPILE_FLAGS: -Wno-comment
+
 #include <__type_traits/invoke.h>
 #include <unordered_set>
 #include <unordered_map>
@@ -34,25 +36,24 @@ void f() {
   static_assert(!std::__is_invocable_v<BadEqual const&, int const&, int const&>, "");
   static_assert(std::__is_invocable_v<BadEqual&, int const&, int const&>, "");
 
-  // expected-warning at unordered_set:* 2 {{the specified comparator type does not provide a viable const call operator}}
-  // expected-warning at unordered_map:* 2 {{the specified comparator type does not provide a viable const call operator}}
-  // expected-warning at unordered_set:* 2 {{the specified hash functor does not provide a viable const call operator}}
-  // expected-warning at unordered_map:* 2 {{the specified hash functor does not provide a viable const call operator}}
-
   {
     using C = std::unordered_set<int, BadHash, BadEqual>;
-    C s;
+    C s; // expected-warning {{the specified comparator type does not provide a viable const call operator}} \
+            expected-warning {{the specified hash functor does not provide a viable const call operator}}
   }
   {
     using C = std::unordered_multiset<long, BadHash, BadEqual>;
-    C s;
+    C s; // expected-warning {{the specified comparator type does not provide a viable const call operator}} \
+            expected-warning {{the specified hash functor does not provide a viable const call operator}}
   }
   {
     using C = std::unordered_map<int, int, BadHash, BadEqual>;
-    C s;
+    C s; // expected-warning {{the specified comparator type does not provide a viable const call operator}} \
+            expected-warning {{the specified hash functor does not provide a viable const call operator}}
   }
   {
     using C = std::unordered_multimap<long, int, BadHash, BadEqual>;
-    C s;
+    C s; // expected-warning {{the specified comparator type does not provide a viable const call operator}} \
+            expected-warning {{the specified hash functor does not provide a viable const call operator}}
   }
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp b/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp
index bdb3189b7fd35..9db3c0be6392f 100644
--- a/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp
@@ -10,9 +10,6 @@
 
 // Ensure that APIs which take a CharT* are diagnosing passing a nullptr to them
 
-// AppleClang doesn't have diagnose_if with diagnostic flags
-// UNSUPPORTED: apple-clang-21
-
 // ADDITIONAL_COMPILE_FLAGS: -Wno-unused-result
 
 #include <string>
diff --git a/libcxx/test/libcxx/strings/string.view/nonnull.verify.cpp b/libcxx/test/libcxx/strings/string.view/nonnull.verify.cpp
index cac8ca81b1a1b..4f699d9d1b3fc 100644
--- a/libcxx/test/libcxx/strings/string.view/nonnull.verify.cpp
+++ b/libcxx/test/libcxx/strings/string.view/nonnull.verify.cpp
@@ -10,9 +10,6 @@
 
 // Ensure that APIs which take a CharT* are diagnosing passing a nullptr to them
 
-// AppleClang doesn't have diagnose_if with diagnostic flags
-// UNSUPPORTED: apple-clang-21
-
 // ADDITIONAL_COMPILE_FLAGS: -Wno-unused-result
 
 #include <string_view>


From libcxx-commits at lists.llvm.org  Mon Jul  6 04:56:25 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 04:56:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Instantiate integral to_chars
 overloads externally (PR #107071)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/107071@github.com>
Message-ID: <6a4b97e9.8b87e04f.199b71.1877@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/107071

From libcxx-commits at lists.llvm.org  Mon Jul  6 05:04:59 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 05:04:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Reorder shared_ptr
	allocation functions (PR #206031)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206031@github.com>
Message-ID: <6a4b99eb.fe8808c9.119fa8.a99b@mx.google.com>


================
@@ -877,13 +831,28 @@ allocate_shared(const _Alloc& __a, const remove_extent_t<_Tp>& __u) {
   return std::__allocate_shared_bounded_array<_Tp>(__a, __u);
 }
 
-template <class _Tp, class _Alloc, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
-  using _ForOverwriteAllocator = __allocator_traits_rebind_t<_Alloc, __for_overwrite_tag>;
-  _ForOverwriteAllocator __alloc(__a);
-  return std::__allocate_shared_bounded_array<_Tp>(__alloc);
+template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
----------------
philnik777 wrote:

I think interleaving the control blocks and public API is a bit confusing. I'd rather have the overload sets together.

https://github.com/llvm/llvm-project/pull/206031

From libcxx-commits at lists.llvm.org  Mon Jul  6 05:05:10 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 05:05:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Reorder shared_ptr
	allocation functions (PR #206031)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206031@github.com>
Message-ID: <6a4b99f6.1173ae6c.7bab2.d2c3@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/206031

>From af2bde9e085488f80eb1762afbe02ecea46bd5d9 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 26 May 2026 11:06:23 +0200
Subject: [PATCH] [libc++] Reorder shared_ptr allocation functions

This reorders the functions to be together by overload set instead of by object/array created. This makes it significantly easier to understand the overload set.
---
 libcxx/include/__memory/shared_ptr.h | 196 ++++++++++++++-------------
 1 file changed, 99 insertions(+), 97 deletions(-)

diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 76a52c35bdf12..a3c2c22192b0c 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -125,56 +125,6 @@ class _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_pointer final : public __shared_
 // initialization/destruction based on that.
 struct __for_overwrite_tag {};
 
-template <class _Tp, class _Alloc>
-struct _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_emplace : __shared_weak_count {
-  using __alloc_t _LIBCPP_NODEBUG      = __allocator_traits_rebind_t<_Alloc, __shared_ptr_emplace>;
-  using __alloc_traits _LIBCPP_NODEBUG = allocator_traits<__alloc_t>;
-  using __value_type _LIBCPP_NODEBUG   = __remove_cv_t<_Tp>;
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI explicit __shared_ptr_emplace(_Alloc __a, _Args&&... __args) : __alloc_(std::move(__a)) {
-    __alloc_traits::construct(__alloc_, __get_elem(), std::forward<_Args>(__args)...);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __value_type* __get_elem() _NOEXCEPT { return reinterpret_cast<__value_type*>(__buffer_); }
-
-private:
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared() _NOEXCEPT override {
-    __alloc_traits::destroy(__alloc_, __get_elem());
-  }
-
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared_weak() _NOEXCEPT override {
-    __alloc_t __tmp(__alloc_);
-    __alloc_.~__alloc_t();
-    __alloc_traits::deallocate(__tmp, pointer_traits<typename __alloc_traits::pointer>::pointer_to(*this), 1);
-  }
-
-  _LIBCPP_NO_UNIQUE_ADDRESS __alloc_t __alloc_;
-  _ALIGNAS_TYPE(__value_type) char __buffer_[sizeof(__value_type)];
-};
-
-template <class _Tp, class _Alloc>
-struct _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_emplace_for_overwrite : __shared_weak_count {
-  using __alloc_t _LIBCPP_NODEBUG    = __allocator_traits_rebind_t<_Alloc, __shared_ptr_emplace_for_overwrite>;
-  using __value_type _LIBCPP_NODEBUG = __remove_cv_t<_Tp>;
-
-  _LIBCPP_HIDE_FROM_ABI explicit __shared_ptr_emplace_for_overwrite(_Alloc __a) : __alloc_(std::move(__a)) {}
-
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared() _NOEXCEPT override { __value_.~__value_type(); }
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared_weak() _NOEXCEPT override {
-    __alloc_t __tmp(__alloc_);
-    __alloc_.~__alloc_t();
-    using __alloc_traits = allocator_traits<__alloc_t>;
-    __alloc_traits::deallocate(__tmp, pointer_traits<typename __alloc_traits::pointer>::pointer_to(*this), 1);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __value_type* __get_elem() _NOEXCEPT { return std::addressof(__value_); }
-
-private:
-  _LIBCPP_NO_UNIQUE_ADDRESS __alloc_t __alloc_;
-  _LIBCPP_NO_UNIQUE_ADDRESS __value_type __value_;
-};
-
 struct __shared_ptr_dummy_rebind_allocator_type;
 template <>
 class allocator<__shared_ptr_dummy_rebind_allocator_type> {
@@ -640,9 +590,7 @@ template <class _Tp, class _Dp>
 shared_ptr(unique_ptr<_Tp, _Dp>) -> shared_ptr<_Tp>;
 #endif
 
-//
-// std::allocate_shared and std::make_shared
-//
+// allocate_shared
 
 template <class _ControlBlock, class _Tp, class _Alloc, class... _Args>
 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> __allocate_shared_impl(const _Alloc& __alloc, _Args&&... __args) {
@@ -654,32 +602,33 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> __allocate_shared_impl(const _Alloc& __all
       __control_block->__get_elem(), std::__to_address(__control_block));
 }
 
-template <class _Tp, class _Alloc, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, _Args&&... __args) {
-  using _ControlBlock = __shared_ptr_emplace<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp> > >;
-  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a, std::forward<_Args>(__args)...);
-}
+template <class _Tp, class _Alloc>
+struct _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_emplace : __shared_weak_count {
+  using __alloc_t _LIBCPP_NODEBUG      = __allocator_traits_rebind_t<_Alloc, __shared_ptr_emplace>;
+  using __alloc_traits _LIBCPP_NODEBUG = allocator_traits<__alloc_t>;
+  using __value_type _LIBCPP_NODEBUG   = __remove_cv_t<_Tp>;
 
-template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(_Args&&... __args) {
-  return std::allocate_shared<_Tp>(allocator<__remove_cv_t<_Tp> >(), std::forward<_Args>(__args)...);
-}
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI explicit __shared_ptr_emplace(_Alloc __a, _Args&&... __args) : __alloc_(std::move(__a)) {
+    __alloc_traits::construct(__alloc_, __get_elem(), std::forward<_Args>(__args)...);
+  }
 
-#if _LIBCPP_STD_VER >= 20
+  _LIBCPP_HIDE_FROM_ABI __value_type* __get_elem() _NOEXCEPT { return reinterpret_cast<__value_type*>(__buffer_); }
 
-template <class _Tp, class _Alloc, __enable_if_t<!is_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
-  using _ControlBlock =
-      __shared_ptr_emplace_for_overwrite<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp>>>;
-  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a);
-}
+private:
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared() _NOEXCEPT override {
+    __alloc_traits::destroy(__alloc_, __get_elem());
+  }
 
-template <class _Tp, __enable_if_t<!is_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared_for_overwrite() {
-  return std::allocate_shared_for_overwrite<_Tp>(allocator<__remove_cv_t<_Tp>>());
-}
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared_weak() _NOEXCEPT override {
+    __alloc_t __tmp(__alloc_);
+    __alloc_.~__alloc_t();
+    __alloc_traits::deallocate(__tmp, pointer_traits<typename __alloc_traits::pointer>::pointer_to(*this), 1);
+  }
 
-#endif // _LIBCPP_STD_VER >= 20
+  _LIBCPP_NO_UNIQUE_ADDRESS __alloc_t __alloc_;
+  _ALIGNAS_TYPE(__value_type) char __buffer_[sizeof(__value_type)];
+};
 
 #if _LIBCPP_STD_VER >= 17
 
@@ -863,9 +812,14 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Array> __allocate_shared_bounded_array(const _
 
 #endif // _LIBCPP_STD_VER >= 17
 
+template <class _Tp, class _Alloc, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, _Args&&... __args) {
+  using _ControlBlock = __shared_ptr_emplace<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp> > >;
+  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a, std::forward<_Args>(__args)...);
+}
+
 #if _LIBCPP_STD_VER >= 20
 
-// bounded array variants
 template <class _Tp, class _Alloc, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a) {
   return std::__allocate_shared_bounded_array<_Tp>(__a);
@@ -877,13 +831,28 @@ allocate_shared(const _Alloc& __a, const remove_extent_t<_Tp>& __u) {
   return std::__allocate_shared_bounded_array<_Tp>(__a, __u);
 }
 
-template <class _Tp, class _Alloc, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
-  using _ForOverwriteAllocator = __allocator_traits_rebind_t<_Alloc, __for_overwrite_tag>;
-  _ForOverwriteAllocator __alloc(__a);
-  return std::__allocate_shared_bounded_array<_Tp>(__alloc);
+template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n) {
+  return std::__allocate_shared_unbounded_array<_Tp>(__a, __n);
+}
+
+template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
+allocate_shared(const _Alloc& __a, size_t __n, const remove_extent_t<_Tp>& __u) {
+  return std::__allocate_shared_unbounded_array<_Tp>(__a, __n, __u);
 }
 
+#endif // _LIBCPP_STD_VER >= 20
+
+// make_shared
+
+template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(_Args&&... __args) {
+  return std::allocate_shared<_Tp>(allocator<__remove_cv_t<_Tp> >(), std::forward<_Args>(__args)...);
+}
+
+#if _LIBCPP_STD_VER >= 20
+
 template <class _Tp, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared() {
   return std::__allocate_shared_bounded_array<_Tp>(allocator<_Tp>());
@@ -894,21 +863,52 @@ template <class _Tp, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
   return std::__allocate_shared_bounded_array<_Tp>(allocator<_Tp>(), __u);
 }
 
-template <class _Tp, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared_for_overwrite() {
-  return std::__allocate_shared_bounded_array<_Tp>(allocator<__for_overwrite_tag>());
+template <class _Tp, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(size_t __n) {
+  return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n);
 }
 
-// unbounded array variants
-template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n) {
-  return std::__allocate_shared_unbounded_array<_Tp>(__a, __n);
+template <class _Tp, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(size_t __n, const remove_extent_t<_Tp>& __u) {
+  return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n, __u);
 }
 
-template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
-allocate_shared(const _Alloc& __a, size_t __n, const remove_extent_t<_Tp>& __u) {
-  return std::__allocate_shared_unbounded_array<_Tp>(__a, __n, __u);
+// allocate_shared_for_overwrite
+
+template <class _Tp, class _Alloc>
+struct _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_emplace_for_overwrite : __shared_weak_count {
+  using __alloc_t _LIBCPP_NODEBUG    = __allocator_traits_rebind_t<_Alloc, __shared_ptr_emplace_for_overwrite>;
+  using __value_type _LIBCPP_NODEBUG = __remove_cv_t<_Tp>;
+
+  _LIBCPP_HIDE_FROM_ABI explicit __shared_ptr_emplace_for_overwrite(_Alloc __a) : __alloc_(std::move(__a)) {}
+
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared() _NOEXCEPT override { __value_.~__value_type(); }
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared_weak() _NOEXCEPT override {
+    __alloc_t __tmp(__alloc_);
+    __alloc_.~__alloc_t();
+    using __alloc_traits = allocator_traits<__alloc_t>;
+    __alloc_traits::deallocate(__tmp, pointer_traits<typename __alloc_traits::pointer>::pointer_to(*this), 1);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __value_type* __get_elem() _NOEXCEPT { return std::addressof(__value_); }
+
+private:
+  _LIBCPP_NO_UNIQUE_ADDRESS __alloc_t __alloc_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __value_type __value_;
+};
+
+template <class _Tp, class _Alloc, __enable_if_t<!is_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
+  using _ControlBlock =
+      __shared_ptr_emplace_for_overwrite<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp>>>;
+  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a);
+}
+
+template <class _Tp, class _Alloc, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
+  using _ForOverwriteAllocator = __allocator_traits_rebind_t<_Alloc, __for_overwrite_tag>;
+  _ForOverwriteAllocator __alloc(__a);
+  return std::__allocate_shared_bounded_array<_Tp>(__alloc);
 }
 
 template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
@@ -918,14 +918,16 @@ template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value,
   return std::__allocate_shared_unbounded_array<_Tp>(__alloc, __n);
 }
 
-template <class _Tp, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(size_t __n) {
-  return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n);
+// make_shared_for_overwrite
+
+template <class _Tp, __enable_if_t<!is_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared_for_overwrite() {
+  return std::allocate_shared_for_overwrite<_Tp>(allocator<__remove_cv_t<_Tp>>());
 }
 
-template <class _Tp, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(size_t __n, const remove_extent_t<_Tp>& __u) {
-  return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n, __u);
+template <class _Tp, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared_for_overwrite() {
+  return std::__allocate_shared_bounded_array<_Tp>(allocator<__for_overwrite_tag>());
 }
 
 template <class _Tp, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>


From libcxx-commits at lists.llvm.org  Mon Jul  6 05:08:56 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 06 Jul 2026 05:08:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Reorder shared_ptr
	allocation functions (PR #206031)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206031@github.com>
Message-ID: <6a4b9ad8.b47a2ac0.265e67.757a@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/206031

>From 92af481c27135efed84304b69937c605b3535652 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 26 May 2026 11:06:23 +0200
Subject: [PATCH] [libc++] Reorder shared_ptr allocation functions

This reorders the functions to be together by overload set instead of by object/array created. This makes it significantly easier to understand the overload set.
---
 libcxx/include/__memory/shared_ptr.h | 198 ++++++++++++++-------------
 1 file changed, 100 insertions(+), 98 deletions(-)

diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index ebd542ba14d01..f2b14f0566dba 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -126,57 +126,6 @@ class _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_pointer final : public __shared_
 // initialization/destruction based on that.
 struct __for_overwrite_tag {};
 
-template <class _Tp, class _Alloc>
-struct _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_emplace : __shared_weak_count {
-  using __alloc_traits _LIBCPP_NODEBUG = allocator_traits<_Alloc>;
-  using __value_type _LIBCPP_NODEBUG   = __remove_cv_t<_Tp>;
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI explicit __shared_ptr_emplace(_Alloc __a, _Args&&... __args) : __alloc_(std::move(__a)) {
-    allocator_traits<_Alloc>::construct(__alloc_, __get_elem(), std::forward<_Args>(__args)...);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __value_type* __get_elem() _NOEXCEPT { return reinterpret_cast<__value_type*>(__buffer_); }
-
-private:
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared() _NOEXCEPT override {
-    allocator_traits<_Alloc>::destroy(__alloc_, __get_elem());
-  }
-
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared_weak() _NOEXCEPT override {
-    using __alloc_t = __allocator_traits_rebind_t<_Alloc, __shared_ptr_emplace>;
-    __alloc_t __tmp(__alloc_);
-    __alloc_.~_Alloc();
-    allocator_traits<__alloc_t>::deallocate(
-        __tmp, pointer_traits<typename allocator_traits<__alloc_t>::pointer>::pointer_to(*this), 1);
-  }
-
-  _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_;
-  _ALIGNAS_TYPE(__value_type) char __buffer_[sizeof(__value_type)];
-};
-
-template <class _Tp, class _Alloc>
-struct _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_emplace_for_overwrite : __shared_weak_count {
-  using __value_type _LIBCPP_NODEBUG = __remove_cv_t<_Tp>;
-
-  _LIBCPP_HIDE_FROM_ABI explicit __shared_ptr_emplace_for_overwrite(_Alloc __a) : __alloc_(std::move(__a)) {}
-
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared() _NOEXCEPT override { __value_.~__value_type(); }
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared_weak() _NOEXCEPT override {
-    using __alloc_t      = __allocator_traits_rebind_t<_Alloc, __shared_ptr_emplace_for_overwrite>;
-    using __alloc_traits = allocator_traits<__alloc_t>;
-    __alloc_t __tmp(__alloc_);
-    __alloc_.~_Alloc();
-    __alloc_traits::deallocate(__tmp, pointer_traits<typename __alloc_traits::pointer>::pointer_to(*this), 1);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __value_type* __get_elem() _NOEXCEPT { return std::addressof(__value_); }
-
-private:
-  _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_;
-  _LIBCPP_NO_UNIQUE_ADDRESS __value_type __value_;
-};
-
 struct __shared_ptr_dummy_rebind_allocator_type;
 template <>
 class allocator<__shared_ptr_dummy_rebind_allocator_type> {
@@ -642,9 +591,7 @@ template <class _Tp, class _Dp>
 shared_ptr(unique_ptr<_Tp, _Dp>) -> shared_ptr<_Tp>;
 #endif
 
-//
-// std::allocate_shared and std::make_shared
-//
+// allocate_shared
 
 template <class _ControlBlock, class _Tp, class _Alloc, class... _Args>
 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> __allocate_shared_impl(const _Alloc& __alloc, _Args&&... __args) {
@@ -656,32 +603,34 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> __allocate_shared_impl(const _Alloc& __all
       __control_block->__get_elem(), std::__to_address(__control_block));
 }
 
-template <class _Tp, class _Alloc, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, _Args&&... __args) {
-  using _ControlBlock = __shared_ptr_emplace<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp> > >;
-  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a, std::forward<_Args>(__args)...);
-}
+template <class _Tp, class _Alloc>
+struct _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_emplace : __shared_weak_count {
+  using __alloc_traits _LIBCPP_NODEBUG = allocator_traits<_Alloc>;
+  using __value_type _LIBCPP_NODEBUG   = __remove_cv_t<_Tp>;
 
-template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(_Args&&... __args) {
-  return std::allocate_shared<_Tp>(allocator<__remove_cv_t<_Tp> >(), std::forward<_Args>(__args)...);
-}
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI explicit __shared_ptr_emplace(_Alloc __a, _Args&&... __args) : __alloc_(std::move(__a)) {
+    allocator_traits<_Alloc>::construct(__alloc_, __get_elem(), std::forward<_Args>(__args)...);
+  }
 
-#if _LIBCPP_STD_VER >= 20
+  _LIBCPP_HIDE_FROM_ABI __value_type* __get_elem() _NOEXCEPT { return reinterpret_cast<__value_type*>(__buffer_); }
 
-template <class _Tp, class _Alloc, __enable_if_t<!is_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
-  using _ControlBlock =
-      __shared_ptr_emplace_for_overwrite<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp>>>;
-  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a);
-}
+private:
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared() _NOEXCEPT override {
+    allocator_traits<_Alloc>::destroy(__alloc_, __get_elem());
+  }
 
-template <class _Tp, __enable_if_t<!is_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared_for_overwrite() {
-  return std::allocate_shared_for_overwrite<_Tp>(allocator<__remove_cv_t<_Tp>>());
-}
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared_weak() _NOEXCEPT override {
+    using __alloc_t = __allocator_traits_rebind_t<_Alloc, __shared_ptr_emplace>;
+    __alloc_t __tmp(__alloc_);
+    __alloc_.~_Alloc();
+    allocator_traits<__alloc_t>::deallocate(
+        __tmp, pointer_traits<typename allocator_traits<__alloc_t>::pointer>::pointer_to(*this), 1);
+  }
 
-#endif // _LIBCPP_STD_VER >= 20
+  _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_;
+  _ALIGNAS_TYPE(__value_type) char __buffer_[sizeof(__value_type)];
+};
 
 #if _LIBCPP_STD_VER >= 17
 
@@ -865,9 +814,14 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Array> __allocate_shared_bounded_array(const _
 
 #endif // _LIBCPP_STD_VER >= 17
 
+template <class _Tp, class _Alloc, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, _Args&&... __args) {
+  using _ControlBlock = __shared_ptr_emplace<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp> > >;
+  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a, std::forward<_Args>(__args)...);
+}
+
 #if _LIBCPP_STD_VER >= 20
 
-// bounded array variants
 template <class _Tp, class _Alloc, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a) {
   return std::__allocate_shared_bounded_array<_Tp>(__a);
@@ -879,13 +833,28 @@ allocate_shared(const _Alloc& __a, const remove_extent_t<_Tp>& __u) {
   return std::__allocate_shared_bounded_array<_Tp>(__a, __u);
 }
 
-template <class _Tp, class _Alloc, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
-  using _ForOverwriteAllocator = __allocator_traits_rebind_t<_Alloc, __for_overwrite_tag>;
-  _ForOverwriteAllocator __alloc(__a);
-  return std::__allocate_shared_bounded_array<_Tp>(__alloc);
+template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n) {
+  return std::__allocate_shared_unbounded_array<_Tp>(__a, __n);
+}
+
+template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
+allocate_shared(const _Alloc& __a, size_t __n, const remove_extent_t<_Tp>& __u) {
+  return std::__allocate_shared_unbounded_array<_Tp>(__a, __n, __u);
+}
+
+#endif // _LIBCPP_STD_VER >= 20
+
+// make_shared
+
+template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(_Args&&... __args) {
+  return std::allocate_shared<_Tp>(allocator<__remove_cv_t<_Tp> >(), std::forward<_Args>(__args)...);
 }
 
+#if _LIBCPP_STD_VER >= 20
+
 template <class _Tp, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared() {
   return std::__allocate_shared_bounded_array<_Tp>(allocator<_Tp>());
@@ -896,21 +865,52 @@ template <class _Tp, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
   return std::__allocate_shared_bounded_array<_Tp>(allocator<_Tp>(), __u);
 }
 
-template <class _Tp, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared_for_overwrite() {
-  return std::__allocate_shared_bounded_array<_Tp>(allocator<__for_overwrite_tag>());
+template <class _Tp, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(size_t __n) {
+  return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n);
 }
 
-// unbounded array variants
-template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n) {
-  return std::__allocate_shared_unbounded_array<_Tp>(__a, __n);
+template <class _Tp, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(size_t __n, const remove_extent_t<_Tp>& __u) {
+  return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n, __u);
 }
 
-template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
-allocate_shared(const _Alloc& __a, size_t __n, const remove_extent_t<_Tp>& __u) {
-  return std::__allocate_shared_unbounded_array<_Tp>(__a, __n, __u);
+// allocate_shared_for_overwrite
+
+template <class _Tp, class _Alloc>
+struct _LIBCPP_HIDE_STRUCT_FROM_ABI __shared_ptr_emplace_for_overwrite : __shared_weak_count {
+  using __value_type _LIBCPP_NODEBUG = __remove_cv_t<_Tp>;
+
+  _LIBCPP_HIDE_FROM_ABI explicit __shared_ptr_emplace_for_overwrite(_Alloc __a) : __alloc_(std::move(__a)) {}
+
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared() _NOEXCEPT override { __value_.~__value_type(); }
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void __on_zero_shared_weak() _NOEXCEPT override {
+    using __alloc_t      = __allocator_traits_rebind_t<_Alloc, __shared_ptr_emplace_for_overwrite>;
+    using __alloc_traits = allocator_traits<__alloc_t>;
+    __alloc_t __tmp(__alloc_);
+    __alloc_.~_Alloc();
+    __alloc_traits::deallocate(__tmp, pointer_traits<typename __alloc_traits::pointer>::pointer_to(*this), 1);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __value_type* __get_elem() _NOEXCEPT { return std::addressof(__value_); }
+
+private:
+  _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __value_type __value_;
+};
+
+template <class _Tp, class _Alloc, __enable_if_t<!is_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
+  using _ControlBlock =
+      __shared_ptr_emplace_for_overwrite<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp>>>;
+  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a);
+}
+
+template <class _Tp, class _Alloc, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
+  using _ForOverwriteAllocator = __allocator_traits_rebind_t<_Alloc, __for_overwrite_tag>;
+  _ForOverwriteAllocator __alloc(__a);
+  return std::__allocate_shared_bounded_array<_Tp>(__alloc);
 }
 
 template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
@@ -920,14 +920,16 @@ template <class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value,
   return std::__allocate_shared_unbounded_array<_Tp>(__alloc, __n);
 }
 
-template <class _Tp, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(size_t __n) {
-  return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n);
+// make_shared_for_overwrite
+
+template <class _Tp, __enable_if_t<!is_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared_for_overwrite() {
+  return std::allocate_shared_for_overwrite<_Tp>(allocator<__remove_cv_t<_Tp>>());
 }
 
-template <class _Tp, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(size_t __n, const remove_extent_t<_Tp>& __u) {
-  return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n, __u);
+template <class _Tp, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared_for_overwrite() {
+  return std::__allocate_shared_bounded_array<_Tp>(allocator<__for_overwrite_tag>());
 }
 
 template <class _Tp, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0>


From libcxx-commits at lists.llvm.org  Mon Jul  6 05:28:38 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Mon, 06 Jul 2026 05:28:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3585 as resolved (PR
	#207717)
Message-ID: <llvm.org/llvm/llvm-project/pull/207717@github.com>


https://github.com/c8ef created https://github.com/llvm/llvm-project/pull/207717

- Close: https://github.com/llvm/llvm-project/issues/104934.
- Commit: https://github.com/llvm/llvm-project/commit/0d3d8de014f1012dd0b2c2c077099cf6face44e5
- Test: https://github.com/llvm/llvm-project/blame/52271a5c11f6abde1fa1221db304212b5eb8ec7c/libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp#L29-L34

>From 277a4410cab64519dd123500cfb00e43e8eb8c23 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 6 Jul 2026 20:25:30 +0800
Subject: [PATCH] [libc++] Mark LWG3585 as resolved

---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 8c4cf77d42fbb..d0616fb46681c 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -129,7 +129,7 @@
 "`LWG3574 <https://wg21.link/LWG3574>`__","``common_iterator`` should be completely ``constexpr``-able","2021-10 (Virtual)","|Complete|","14","`#104931 <https://github.com/llvm/llvm-project/issues/104931>`__",""
 "`LWG3580 <https://wg21.link/LWG3580>`__","``iota_view``'s ``iterator``'s binary ``operator+`` should be improved","2021-10 (Virtual)","|Complete|","14","`#104932 <https://github.com/llvm/llvm-project/issues/104932>`__",""
 "`LWG3581 <https://wg21.link/LWG3581>`__","The range constructor makes ``basic_string_view`` not trivially move constructible","2021-10 (Virtual)","|Complete|","14","`#104933 <https://github.com/llvm/llvm-project/issues/104933>`__",""
-"`LWG3585 <https://wg21.link/LWG3585>`__","``variant`` converting assignment with immovable alternative","2021-10 (Virtual)","","","`#104934 <https://github.com/llvm/llvm-project/issues/104934>`__",""
+"`LWG3585 <https://wg21.link/LWG3585>`__","``variant`` converting assignment with immovable alternative","2021-10 (Virtual)","|Complete|","4","`#104934 <https://github.com/llvm/llvm-project/issues/104934>`__",""
 "`LWG3589 <https://wg21.link/LWG3589>`__","The ``const`` lvalue reference overload of ``get`` for ``subrange`` does not constrain ``I`` to be ``copyable`` when ``N == 0``","2021-10 (Virtual)","|Complete|","14","`#104935 <https://github.com/llvm/llvm-project/issues/104935>`__",""
 "`LWG3590 <https://wg21.link/LWG3590>`__","``split_view::base() const &`` is overconstrained","2021-10 (Virtual)","|Complete|","16","`#104936 <https://github.com/llvm/llvm-project/issues/104936>`__",""
 "`LWG3591 <https://wg21.link/LWG3591>`__","``lazy_split_view<input_view>::inner-iterator::base() &&`` invalidates outer iterators","2021-10 (Virtual)","","","`#104937 <https://github.com/llvm/llvm-project/issues/104937>`__",""


From libcxx-commits at lists.llvm.org  Mon Jul  6 05:29:20 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 05:29:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3585 as resolved (PR
	#207717)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207717@github.com>
Message-ID: <6a4b9fa0.72a2efdc.13c309.ff2b@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Connector Switch (c8ef)

<details>
<summary>Changes</summary>

- Close: https://github.com/llvm/llvm-project/issues/104934.
- Commit: https://github.com/llvm/llvm-project/commit/0d3d8de014f1012dd0b2c2c077099cf6face44e5
- Test: https://github.com/llvm/llvm-project/blame/52271a5c11f6abde1fa1221db304212b5eb8ec7c/libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp#L29-L34

---
Full diff: https://github.com/llvm/llvm-project/pull/207717.diff


1 Files Affected:

- (modified) libcxx/docs/Status/Cxx23Issues.csv (+1-1) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 8c4cf77d42fbb..d0616fb46681c 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -129,7 +129,7 @@
 "`LWG3574 <https://wg21.link/LWG3574>`__","``common_iterator`` should be completely ``constexpr``-able","2021-10 (Virtual)","|Complete|","14","`#104931 <https://github.com/llvm/llvm-project/issues/104931>`__",""
 "`LWG3580 <https://wg21.link/LWG3580>`__","``iota_view``'s ``iterator``'s binary ``operator+`` should be improved","2021-10 (Virtual)","|Complete|","14","`#104932 <https://github.com/llvm/llvm-project/issues/104932>`__",""
 "`LWG3581 <https://wg21.link/LWG3581>`__","The range constructor makes ``basic_string_view`` not trivially move constructible","2021-10 (Virtual)","|Complete|","14","`#104933 <https://github.com/llvm/llvm-project/issues/104933>`__",""
-"`LWG3585 <https://wg21.link/LWG3585>`__","``variant`` converting assignment with immovable alternative","2021-10 (Virtual)","","","`#104934 <https://github.com/llvm/llvm-project/issues/104934>`__",""
+"`LWG3585 <https://wg21.link/LWG3585>`__","``variant`` converting assignment with immovable alternative","2021-10 (Virtual)","|Complete|","4","`#104934 <https://github.com/llvm/llvm-project/issues/104934>`__",""
 "`LWG3589 <https://wg21.link/LWG3589>`__","The ``const`` lvalue reference overload of ``get`` for ``subrange`` does not constrain ``I`` to be ``copyable`` when ``N == 0``","2021-10 (Virtual)","|Complete|","14","`#104935 <https://github.com/llvm/llvm-project/issues/104935>`__",""
 "`LWG3590 <https://wg21.link/LWG3590>`__","``split_view::base() const &`` is overconstrained","2021-10 (Virtual)","|Complete|","16","`#104936 <https://github.com/llvm/llvm-project/issues/104936>`__",""
 "`LWG3591 <https://wg21.link/LWG3591>`__","``lazy_split_view<input_view>::inner-iterator::base() &&`` invalidates outer iterators","2021-10 (Virtual)","","","`#104937 <https://github.com/llvm/llvm-project/issues/104937>`__",""

``````````

</details>


https://github.com/llvm/llvm-project/pull/207717

From libcxx-commits at lists.llvm.org  Mon Jul  6 05:37:44 2026
From: libcxx-commits at lists.llvm.org (Corentin Jabot via libcxx-commits)
Date: Mon, 06 Jul 2026 05:37:44 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [compiler-rt] [flang]
 [libc] [libcxx] [llvm] [mlir] [polly] [NFC] Fix "is is" typos (PR #206288)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206288@github.com>
Message-ID: <6a4ba198.566b9172.11e4e5.64fe@mx.google.com>


https://github.com/cor3ntin approved this pull request.

Clamg changes LGTM

https://github.com/llvm/llvm-project/pull/206288

From libcxx-commits at lists.llvm.org  Mon Jul  6 06:00:42 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Mon, 06 Jul 2026 06:00:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
 `adjacent_transform_view` (PR #205900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205900@github.com>
Message-ID: <6a4ba6fa.8e75e0cb.2e0812.7140@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/205900

>From 65046f1524740c0e4a7829b61cd6a7601c34cfa5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 25 Jun 2026 21:47:55 +0200
Subject: [PATCH 01/13] initial: nodiscard adjacent_transform_view

---
 .../include/__ranges/adjacent_transform_view.h  | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..bdae098623977 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -105,22 +105,23 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   _LIBCPP_HIDE_FROM_ABI constexpr explicit adjacent_transform_view(_View __base, _Fn __fun)
       : __inner_(std::move(__base)), __fun_(std::in_place, std::move(__fun)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __inner_.base();
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {
     return __iterator<true>(*this, __inner_.begin());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (common_range<_InnerView>) {
       return __iterator<false>(*this, __inner_.end());
     } else {
@@ -128,7 +129,7 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {
     if constexpr (common_range<const _InnerView>) {
@@ -138,13 +139,13 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_InnerView>
   {
     return __inner_.size();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _InnerView>
   {
     return __inner_.size();

>From 0b582345030b80efba493370df6595f56abdaf64 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 25 Jun 2026 23:51:42 +0200
Subject: [PATCH 02/13] Update
 libcxx/include/__ranges/adjacent_transform_view.h

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 libcxx/include/__ranges/adjacent_transform_view.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index bdae098623977..5d22042e9c78f 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -114,7 +114,6 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {

>From acc5961454c76807c11297fc8ac970135d47f69f Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 27 Jun 2026 17:50:55 +0200
Subject: [PATCH 03/13] complete: nodiscard to adjacent_transform_view.h

---
 .../include/__ranges/adjacent_transform_view.h | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 5d22042e9c78f..f8c4cd716a8d5 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -202,7 +202,7 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     requires _Const && convertible_to<__inner_iterator<false>, __inner_iterator<true>>
       : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(__noexcept_dereference(make_index_sequence<_Np>{})) {
     return std::apply(
         [&](const auto&... __iters) -> decltype(auto) { return std::invoke(*__parent_->__fun_, *__iters...); },
@@ -249,7 +249,7 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
     return std::apply(
@@ -291,25 +291,25 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     return __x.__inner_ <=> __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ + __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__inner_iterator<_Const>, __inner_iterator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;
@@ -344,14 +344,14 @@ class adjacent_transform_view<_View, _Fn, _Np>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__inner_ - __y.__inner_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__inner_ - __y.__inner_;
   }
@@ -371,7 +371,7 @@ struct __fn : __range_adaptor_closure<__fn<_Np>> {
   }
 
   template <class _Range, class _Fn>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range, _Fn&& __fn) noexcept(
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range, _Fn&& __fn) noexcept(
       noexcept(adjacent_transform_view<views::all_t<_Range&&>, decay_t<_Fn>, _Np>(
           std::forward<_Range>(__range), std::forward<_Fn>(__fn))))
       -> decltype(adjacent_transform_view<views::all_t<_Range&&>, decay_t<_Fn>, _Np>(

>From 15e15ee15ad0e6580dfff6f8fd3da6fbb9255854 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 27 Jun 2026 17:51:04 +0200
Subject: [PATCH 04/13] complete: nodiscard to adjacent_transform_view.h

---
 libcxx/include/__ranges/adjacent_transform_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index f8c4cd716a8d5..10bf812607b3e 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -309,7 +309,8 @@ class adjacent_transform_view<_View, _Fn, _Np>::__iterator {
     return __iterator(*__i.__parent_, __i.__inner_ - __n);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<__inner_iterator<_Const>, __inner_iterator<_Const>>
   {
     return __x.__inner_ - __y.__inner_;

>From 287a7d06ae3ca47191a279c987f60137bd0e5761 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 27 Jun 2026 18:24:55 +0200
Subject: [PATCH 05/13] add: initial test file

---
 .../nodiscard.verify.cpp                      | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..6a26fa8bfda85
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,20 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Test the libc++ extension that std::ranges::adjacent_transform_view and std::views::adjacent_transform are marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+#include <functional>
+
+void test(){
+
+}

>From 3aee8e6f2e9c2c3f291da0f79993c161c7c3189d Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 28 Jun 2026 02:18:05 +0200
Subject: [PATCH 06/13] add: test view

---
 .../range.adjacent.transform/nodiscard.verify.cpp      | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 6a26fa8bfda85..459b132766ae0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -15,6 +15,16 @@
 #include <utility>
 #include <functional>
 
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
 void test(){
 
 }

>From 5658eb286ecf418576bc84f50273e2ca37d11a10 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 29 Jun 2026 23:09:10 +0200
Subject: [PATCH 07/13] add: tests for adjacent.transform

---
 .../nodiscard.verify.cpp                      | 65 ++++++++++++++++++-
 1 file changed, 64 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 459b132766ae0..9cf0fffb3ba6a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -25,6 +25,69 @@ static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
-void test(){
+void test() {
+  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  auto v      = View{} | std::views::adjacent_transform<2>(std::plus<>{});
 
+  // [range.adjacent.transform.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).size();
+
+  // [range.adjacent.transform.iterator]
+
+  auto it   = v.begin();
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it[0];
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  0 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 0;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // [range.adjacent.tranform.sentinel]
+
+  auto st = v.end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - st;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
+  // [range.adjacent.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<0>(range);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<2>(range);
 }

>From b6146befe51e731c8b2bd19f57772ec2e3e50c54 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 10:24:32 +0200
Subject: [PATCH 08/13] fix: formatting

---
 libcxx/include/__ranges/adjacent_transform_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 10bf812607b3e..92aaeb24be183 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -110,10 +110,10 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.base();
   }
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
   {

>From 4aa95f70a6e45139e002c7f51167fdf0affa5312 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 10:45:28 +0200
Subject: [PATCH 09/13] ref: std::plus<> to std::multiplies() as per the
 standard 25.7.28.1.3 example

---
 .../range.adjacent.transform/nodiscard.verify.cpp               | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 9cf0fffb3ba6a..9677ea4f06019 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -27,7 +27,7 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
   int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  auto v      = View{} | std::views::adjacent_transform<2>(std::plus<>{});
+  auto v      = View{} | std::views::adjacent_transform<2>(std::multiplies());
 
   // [range.adjacent.transform.view]
 

>From 4459d77f262cf216acb83805dcffe636399f19f5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 30 Jun 2026 14:24:02 +0200
Subject: [PATCH 10/13] fix: includes order and transform_view<2>(...) test

---
 .../range.adjacent.transform/nodiscard.verify.cpp        | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 9677ea4f06019..4a6d087a9e1f4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -11,9 +11,9 @@
 
 // Test the libc++ extension that std::ranges::adjacent_transform_view and std::views::adjacent_transform are marked as [[nodiscard]].
 
+#include <functional>
 #include <ranges>
 #include <utility>
-#include <functional>
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -26,8 +26,7 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-  auto v      = View{} | std::views::adjacent_transform<2>(std::multiplies());
+  auto v = View{} | std::views::adjacent_transform<2>(std::multiplies());
 
   // [range.adjacent.transform.view]
 
@@ -85,9 +84,11 @@ void test() {
 
   // [range.adjacent.overview]
 
+  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::adjacent_transform<0>(range);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::adjacent_transform<2>(range);
+  std::views::adjacent_transform<2>(range, std::multiplies());
 }

>From 29183f98efbac1b381b8ddf6d6ffdb1d160c034b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 3 Jul 2026 11:36:20 +0200
Subject: [PATCH 11/13] Update
 libcxx/include/__ranges/adjacent_transform_view.h

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 libcxx/include/__ranges/adjacent_transform_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 92aaeb24be183..e0c20b7a95513 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -365,7 +365,7 @@ template <size_t _Np>
 struct __fn : __range_adaptor_closure<__fn<_Np>> {
   template <class _Range, class _Fn>
     requires(_Np == 0 && forward_range<_Range &&>)
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
   operator()(_Range&&, _Fn&& __fn) noexcept(noexcept(views::zip_transform(std::forward<_Fn>(__fn))))
       -> decltype(views::zip_transform(std::forward<_Fn>(__fn))) {
     return views::zip_transform(std::forward<_Fn>(__fn));

>From 1f670586ceea6efb96928f99221e5bc361654681 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 4 Jul 2026 21:44:10 +0200
Subject: [PATCH 12/13] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../range.adjacent.transform/nodiscard.verify.cpp        | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 4a6d087a9e1f4..017d7e584613d 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -89,6 +89,15 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::adjacent_transform<0>(range);
 
+  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  static_assert(std::ranges::forward_range<decltype(range)>);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<0>(range, Fn{});
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::adjacent_transform<2>(range, std::multiplies());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::adjacent_transform<2>(std::multiplies());
 }

>From 21b26530b40196d35c61d3677afa8543e65a179c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 4 Jul 2026 21:55:44 +0200
Subject: [PATCH 13/13] fix: range variable declaration and minor fixes

---
 .../range.adjacent.transform/nodiscard.verify.cpp      | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
index 017d7e584613d..88fb7e3c39281 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -1,4 +1,3 @@
-
 //===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -25,6 +24,10 @@ static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
+struct Fn {
+  int operator()(auto...) const;
+};
+
 void test() {
   auto v = View{} | std::views::adjacent_transform<2>(std::multiplies());
 
@@ -84,11 +87,6 @@ void test() {
 
   // [range.adjacent.overview]
 
-  int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::adjacent_transform<0>(range);
-
   int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
   static_assert(std::ranges::forward_range<decltype(range)>);
 


From libcxx-commits at lists.llvm.org  Mon Jul  6 06:03:53 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 06:03:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918: copy elision in
 `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4ba7b9.f8b79514.3b441.a5c9@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Mon Jul  6 06:08:57 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 06:08:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918: copy elision in
 `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4ba8e9.eb24c6ea.1f2e26.0b63@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Mon Jul  6 07:23:50 2026
From: libcxx-commits at lists.llvm.org (David Meng via libcxx-commits)
Date: Mon, 06 Jul 2026 07:23:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Apply the predicate exactly N
 times in stable_partition (PR #203949)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/203949@github.com>
Message-ID: <6a4bba76.58bdd12c.21857.1082@mx.google.com>


davidmenggx wrote:

ping @ldionne @philnik777

https://github.com/llvm/llvm-project/pull/203949

From libcxx-commits at lists.llvm.org  Mon Jul  6 07:27:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 07:27:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a4bbb35.d5797644.146403.0532@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From d93e557c304cea49a5cf654b3bf1710f5cee6855 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 1/9] [libc++] make std::atomic works with types with paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..5e1a7afea06a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From f17e51a372a7a1372fa63a572871939eec329145 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 2/9] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From ac972d414dd906c0e73a3e8b70697abfc537dafa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 3/9] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e1a7afea06a2..4477c821235de 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..b0f40e5a261ed 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -896,6 +896,7 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 33732fee8003a503f62524e1208aef02094b0bc6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 4/9] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From dc33657dd85cca1c1511d5cd7605bbcca05b35f8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 5/9] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From f133b64bf0da078d6ef4659603f8f92f12e7ec8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 6/9] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From e943f4968a86c2c73214cdb22ccdf77b122428e5 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 7/9] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 6070b97b3d62691fabebe6dbd7b79a3fd5a58c4d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 8/9] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 56cbfdaa5c0ff..8ccbcf9d8d227 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -160,7 +160,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -178,7 +178,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -193,7 +193,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b20b5e25fc9cd..04e12afd21689 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From d85c6a4fd888729fc38b7b3bae192263875cc4ed Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 9/9] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 4477c821235de..0e214d48c5672 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 8ccbcf9d8d227..04bdd2a6b343f 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -28,29 +28,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 04e12afd21689..3776873d8936d 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b0f40e5a261ed..a15ec1342a56d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -885,6 +885,7 @@ module std [system] {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -896,7 +897,6 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;


From libcxx-commits at lists.llvm.org  Mon Jul  6 07:33:26 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 06 Jul 2026 07:33:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement any_of in terms of
	find_if (PR #207274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207274@github.com>
Message-ID: <6a4bbcb6.f8b79514.3b441.54fc@mx.google.com>


ldionne wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp

https://github.com/llvm/llvm-project/pull/207274

From libcxx-commits at lists.llvm.org  Mon Jul  6 07:43:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 07:43:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] c58675e - [libc++] Make atomic contention
	tables constinit (#207456)
Message-ID: <6a4bbefd.331483e7.5bc1e.912b@mx.google.com>


Author: Louis Dionne
Date: 2026-07-06T10:43:04-04:00
New Revision: c58675e91042e7052870675aeaf2aca963e11376

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

LOG: [libc++] Make atomic contention tables constinit (#207456)

Added: 
    

Modified: 
    libcxx/src/atomic.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index ae79ac7f87965..53aec09dfc2a0 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -422,7 +422,7 @@ struct alignas(
   constexpr __contention_state_native() : __waiter_count(0) {}
 };
 
-static __contention_state_native __contention_table_native[__contention_table_size];
+static constinit __contention_state_native __contention_table_native[__contention_table_size];
 
 static __cxx_atomic_contention_t* __get_native_waiter_count(void const* p) {
   return &__contention_table_native[__contention_hasher(p) & (__contention_table_size - 1)].__waiter_count;
@@ -438,7 +438,7 @@ struct alignas(
   constexpr __contention_state_global() : __waiter_count(0), __platform_state(0) {}
 };
 
-static __contention_state_global __contention_table_global[__contention_table_size];
+static constinit __contention_state_global __contention_table_global[__contention_table_size];
 
 static __contention_state_global* __get_global_contention_state(void const* p) {
   return &__contention_table_global[__contention_hasher(p) & (__contention_table_size - 1)];


        

From libcxx-commits at lists.llvm.org  Mon Jul  6 07:43:11 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 06 Jul 2026 07:43:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Make atomic contention tables
	constinit (PR #207456)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207456@github.com>
Message-ID: <6a4bbeff.27b3b003.1610ae.ebb3@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/207456

From libcxx-commits at lists.llvm.org  Mon Jul  6 09:00:01 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 09:00:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
 to `views::zip_transform` (PR #207120)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207120@github.com>
Message-ID: <6a4bd101.14d9b9dc.140829.d0f9@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/207120

From libcxx-commits at lists.llvm.org  Mon Jul  6 16:50:02 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 16:50:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`join_view` (PR #206838)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206838@github.com>
Message-ID: <6a4c3f2a.92d2873c.1ab170.2300@mx.google.com>


================
@@ -0,0 +1,73 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Test the libc++ extension that std::ranges::join_view and std::views::view are marked as [[nodiscard]].
+
+#include <functional>
+#include <ranges>
+#include <string>
+#include <utility>
+#include <vector>
+
+struct InnerView : std::ranges::view_interface<InnerView> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+
+struct View : std::ranges::view_interface<View> {
+  InnerView* begin();
+  const InnerView* begin() const;
+  volatile InnerView* end();
+  const volatile InnerView* end() const;
+};
----------------
frederick-vs-ja wrote:

I think it was me that suggested such views. IMO it's already simple enough as a non-simple, non-common range.

https://github.com/llvm/llvm-project/pull/206838

From libcxx-commits at lists.llvm.org  Mon Jul  6 16:53:04 2026
From: libcxx-commits at lists.llvm.org (Michael Jones via libcxx-commits)
Date: Mon, 06 Jul 2026 16:53:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][libc] update LLVM-libc test
	status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a4c3fe0.2020f6c2.243e78.8ec0@mx.google.com>


https://github.com/michaelrj-google updated https://github.com/llvm/llvm-project/pull/201236

>From d2a4c762a7b356f93c17c3cd7db7d7beda43c28d Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 2 Jun 2026 23:27:03 +0000
Subject: [PATCH 1/5] [libcxx][libc] update LLVM-libc test status

This update removes some unnecessary carveouts, marks some newly
XFAILing tests, and unmarks some now passing tests.

With this change the status of tests is:
Total Discovered Tests: 11402
  Unsupported      : 1821 (15.97%)
  Passed           : 9409 (82.52%)
  Expectedly Failed:  172 (1.51%)

Depends on #201235

Assisted-by: Automated tooling, human reviewed.
---
 libcxx/cmake/caches/Generic-llvm-libc.cmake           | 11 -----------
 libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in      |  2 +-
 ...nothrow_new_not_overridden_fno_exceptions.pass.cpp |  3 ---
 .../support.dynamic/new_dont_return_nullptr.pass.cpp  |  3 ---
 ...R30202_notify_from_pthread_created_thread.pass.cpp |  2 ++
 .../thread.thread.member/native_handle.pass.cpp       |  3 +++
 .../osyncstream/thread/several_threads.pass.cpp       |  3 +++
 .../complex.member.ops/divide_equal_complex.pass.cpp  |  3 ---
 .../complex.ops/complex_divide_complex.pass.cpp       |  3 ---
 .../complex.ops/scalar_divide_complex.pass.cpp        |  3 ---
 .../rand.dist.bern.bin/eval.PR44847.pass.cpp          |  3 +++
 .../rand.dist.bern/rand.dist.bern.bin/io.pass.cpp     |  3 +++
 .../notify_all_at_thread_exit_lwg3343.pass.cpp        |  3 +++
 .../thread.thread.this/sleep_for.signals.pass.cpp     |  3 +++
 .../std/time/time.hash/time.hash_enabled.pass.cpp     |  3 ---
 15 files changed, 21 insertions(+), 30 deletions(-)

diff --git a/libcxx/cmake/caches/Generic-llvm-libc.cmake b/libcxx/cmake/caches/Generic-llvm-libc.cmake
index 8e8a2699858b3..96bbeab040362 100644
--- a/libcxx/cmake/caches/Generic-llvm-libc.cmake
+++ b/libcxx/cmake/caches/Generic-llvm-libc.cmake
@@ -32,22 +32,11 @@ set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
 
 # Not yet fully implemented.
-set(LIBCXX_ENABLE_LOCALIZATION OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF CACHE BOOL "")
 
 # Still missing some components.
 set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
 
-# Still needs validation before turning on.
-set(LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "")
-
-# Still missing some components.
-set(LIBCXX_ENABLE_MONOTONIC_CLOCK OFF CACHE BOOL "")
-
-# requires LIBCXX_ENABLE_MONOTONIC_CLOCK=ON
-set(LIBCXX_ENABLE_THREADS OFF CACHE BOOL "")
-set(LIBCXXABI_ENABLE_THREADS OFF CACHE BOOL "")
-
 set(LIBCXX_ENABLE_UNICODE ON CACHE BOOL "")
 set(LIBCXX_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
 set(LIBCXXABI_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
diff --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index cae8a8007214e..7d878e9888ce0 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include-dir} -I %{target-include-dir} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx-dir}/test/support -idirafter @LIBC_KERNEL_HEADERS@'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a'
+    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--defsym,__dso_handle=0 -latomic -Wl,--end-group'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '
diff --git a/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp b/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
index 39a51352eadd9..fb26bdf49cc3c 100644
--- a/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
+++ b/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
@@ -29,9 +29,6 @@
 //       spuriously fail.
 // REQUIRES: no-exceptions
 
-// No member named 'regex'.
-// XFAIL: LLVM-LIBC-FIXME
-
 #include <cstddef>
 #include <new>
 
diff --git a/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp b/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
index 339e756b86fc8..02b20a8c98b21 100644
--- a/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
+++ b/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
@@ -22,9 +22,6 @@
 // REQUIRES: no-exceptions
 // UNSUPPORTED: c++03, c++11, c++14
 
-// No member named 'regex'.
-// XFAIL: LLVM-LIBC-FIXME
-
 #include <cstddef>
 #include <limits>
 #include <new>
diff --git a/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp b/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
index 66251648bd50c..1df75885e4347 100644
--- a/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
@@ -20,6 +20,8 @@
 // Test that this function works with threads that were not created by
 // std::thread. See https://llvm.org/PR30202
 
+// Segfault
+// XFAIL: LLVM-LIBC-FIXME
 
 #include <condition_variable>
 #include <mutex>
diff --git a/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
index 96ec3332519bc..5c5c0fed58726 100644
--- a/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
@@ -16,6 +16,9 @@
 
 // native_handle_type native_handle();
 
+// pthread_t and int not comparable.
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <thread>
 #include <new>
 #include <cstdlib>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..82d43c1673d2d 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -21,6 +21,9 @@
 // but the words should be written without interleaving. To increment the
 // change of interleaving words are written one character at a time.
 
+// segmentation fault
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <cassert>
 #include <chrono>
 #include <mutex>
diff --git a/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
index b9467a166e8d5..61e0d01f2ae4d 100644
--- a/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing some math functions that compiler-rt buildins need.
-// XFAIL: LLVM-LIBC-FIXME
-
 // <complex>
 
 // complex& operator/=(const complex& rhs); // constexpr in C++20
diff --git a/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
index 4e00e87f18220..d12dfd994b0ae 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing some math functions that compiler-rt buildins need.
-// XFAIL: LLVM-LIBC-FIXME
-
 // <complex>
 
 // template<class T>
diff --git a/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
index 56d9664b49aea..f32b560c1e864 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing some math functions that compiler-rt buildins need.
-// XFAIL: LLVM-LIBC-FIXME
-
 // <complex>
 
 // template<class T>
diff --git a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
index 8f7c8eb7ccc0f..22319e5b31454 100644
--- a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
@@ -21,6 +21,9 @@
 // Very slow when run in qemu.
 // REQUIRES: long_tests
 
+// missing lgamma_r
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <random>
 #include <cassert>
 #include <cmath>
diff --git a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
index 35f579903a460..a53a5db3be4c0 100644
--- a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
@@ -23,6 +23,9 @@
 // operator>>(basic_istream<charT, traits>& is,
 //            binomial_distribution& x);
 
+// missing lgamma_r
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <random>
 #include <sstream>
 #include <cassert>
diff --git a/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp b/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
index 61094eced4cb2..f1b349017b1a5 100644
--- a/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
+++ b/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
@@ -23,6 +23,9 @@
 //
 // void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);
 
+// segmentation fault
+// XFAIL: LLVM-LIBC-FIXME
+
 #include "make_test_thread.h"
 #include "test_macros.h"
 
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
index 1dba5d8a40976..726b5807d55e3 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
@@ -21,6 +21,9 @@
 // This test ensures that we sleep for the right amount of time even when
 // we get interrupted by a signal, as fixed in 58a0a70fb2f1.
 
+// missing ITIMER_REAL
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <thread>
 #include <cassert>
 #include <chrono>
diff --git a/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp b/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
index e3ac7e53251d5..cac7fbda0fbb3 100644
--- a/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
+++ b/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// No member named `leapsecond'.
-// XFAIL: LLVM-LIBC-FIXME
-
 // REQUIRES: std-at-least-c++26
 
 // <chrono>

>From 63a956a437f635c0aedaca424221573f13a27166 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 3 Jun 2026 23:59:29 +0000
Subject: [PATCH 2/5] enable wchar, fix tests

---
 libcxx/cmake/caches/Generic-llvm-libc.cmake                    | 3 ++-
 .../extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp  | 3 +++
 .../test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp   | 3 +++
 libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp   | 3 +++
 libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp  | 3 +++
 libcxx/test/std/strings/c.strings/cwchar.pass.cpp              | 3 +++
 libcxx/test/std/strings/c.strings/cwctype.pass.cpp             | 3 +++
 libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp | 3 +++
 8 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/libcxx/cmake/caches/Generic-llvm-libc.cmake b/libcxx/cmake/caches/Generic-llvm-libc.cmake
index 96bbeab040362..2a4df69bec105 100644
--- a/libcxx/cmake/caches/Generic-llvm-libc.cmake
+++ b/libcxx/cmake/caches/Generic-llvm-libc.cmake
@@ -32,7 +32,7 @@ set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
 
 # Not yet fully implemented.
-set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF CACHE BOOL "")
+set(LIBCXX_ENABLE_WIDE_CHARACTERS ON CACHE BOOL "")
 
 # Still missing some components.
 set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
@@ -47,5 +47,6 @@ set(CMAKE_BUILD_TYPE "Release" CACHE STRING "")
 set(LLVM_ENABLE_RUNTIMES "compiler-rt;libc;libunwind;libcxxabi;libcxx" CACHE STRING "")
 set(LLVM_LIBC_FULL_BUILD ON CACHE BOOL "")
 set(LLVM_LIBC_INCLUDE_SCUDO ON CACHE BOOL "")
+set(LLVM_LIBC_ENABLE_EXPERIMENTAL_ENTRYPOINTS ON CACHE BOOL "")
 set(COMPILER_RT_BUILD_GWP_ASAN OFF CACHE BOOL "")
 set(COMPILER_RT_BUILD_SCUDO_STANDALONE_WITH_LLVM_LIBC ON CACHE BOOL "")
diff --git a/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp b/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
index dd9847f63253b..4b8297145e35a 100644
--- a/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
+++ b/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
@@ -9,6 +9,9 @@
 // We're building as C, so this test doesn't work when building with modules.
 // ADDITIONAL_COMPILE_FLAGS: -fno-modules
 
+// Incompatible redeclaration of wcschr and wmemchr builtins
+// XFAIL: LLVM-LIBC-FIXME
+
 // GCC complains about unrecognized arguments because we're compiling the
 // file as C, but we're passing C++ flags on the command-line.
 // UNSUPPORTED: gcc
diff --git a/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
index eaae3aa3417e4..a730debab2e9b 100644
--- a/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
@@ -8,6 +8,9 @@
 
 // test <inttypes.h>
 
+// Missing wcstoimax, wcstoumax
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <inttypes.h>
 
 #include "test_macros.h"
diff --git a/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
index 98d028c5181d5..df7bda3baf8af 100644
--- a/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
@@ -8,6 +8,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing fwprintf, fwscanf, swscanf, vfwprintf, vfwscanf, vswprintf, vswscanf, wcsftime, vwscanf, wscanf
+// XFAIL: LLVM-LIBC-FIXME
+
 // <wchar.h>
 
 #include <wchar.h>
diff --git a/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
index 4e2fb319336f1..221c219918614 100644
--- a/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
@@ -8,6 +8,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing WEOF, wctrans_t
+// XFAIL: LLVM-LIBC-FIXME
+
 // towctrans and wctrans were added in Android API 26.
 // TODO: Switch from UNSUPPORTED to XFAIL once the Android CI Docker sysroot is
 // updated.
diff --git a/libcxx/test/std/strings/c.strings/cwchar.pass.cpp b/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
index 0caf4b9913b50..1758a703b3b62 100644
--- a/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
+++ b/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
@@ -10,6 +10,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing fwprintf, fwscanf, swscanf, vfwprintf, vfwscanf, vswprintf, vswscanf, wcsftime, vwscanf, wscanf
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <cwchar>
 #include <ctime>
 #include <cstdarg>
diff --git a/libcxx/test/std/strings/c.strings/cwctype.pass.cpp b/libcxx/test/std/strings/c.strings/cwctype.pass.cpp
index 0deabf51ed59c..3de582714d01d 100644
--- a/libcxx/test/std/strings/c.strings/cwctype.pass.cpp
+++ b/libcxx/test/std/strings/c.strings/cwctype.pass.cpp
@@ -10,6 +10,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing WEOF, towctrans, wctrans_t
+// XFAIL: LLVM-LIBC-FIXME
+
 // towctrans and wctrans were added in Android API 26.
 // TODO: Switch from UNSUPPORTED to XFAIL once the Android CI Docker sysroot is
 // updated.
diff --git a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
index fff5ede848b57..bd4bd47671a81 100644
--- a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
+++ b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
@@ -20,6 +20,9 @@
 // wstring to_wstring(double val);
 // wstring to_wstring(long double val);
 
+// Missing swprintf (currently a stub that infinite loops)
+// UNSUPPORTED: LLVM-LIBC-FIXME
+
 #include <string>
 #include <cassert>
 #include <limits>

>From b79fc3d8ce3e538234bad1f6d23f34ebd32b283a Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 24 Jun 2026 17:52:24 +0000
Subject: [PATCH 3/5] move where `__dso_handle` is defined

---
 libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index 7d878e9888ce0..ed1a63c027c9e 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include-dir} -I %{target-include-dir} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx-dir}/test/support -idirafter @LIBC_KERNEL_HEADERS@'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--defsym,__dso_handle=0 -latomic -Wl,--end-group'
+    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -latomic -Wl,--end-group -Wl,--defsym,__dso_handle=0'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '

>From 93823f1de230489ffa7d0ebb5748d9f89afe2d73 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 1 Jul 2026 20:22:46 +0000
Subject: [PATCH 4/5] update XFAILs with new version

---
 .../extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp  | 3 ---
 .../test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp   | 3 ---
 2 files changed, 6 deletions(-)

diff --git a/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp b/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
index 4b8297145e35a..dd9847f63253b 100644
--- a/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
+++ b/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
@@ -9,9 +9,6 @@
 // We're building as C, so this test doesn't work when building with modules.
 // ADDITIONAL_COMPILE_FLAGS: -fno-modules
 
-// Incompatible redeclaration of wcschr and wmemchr builtins
-// XFAIL: LLVM-LIBC-FIXME
-
 // GCC complains about unrecognized arguments because we're compiling the
 // file as C, but we're passing C++ flags on the command-line.
 // UNSUPPORTED: gcc
diff --git a/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
index a730debab2e9b..eaae3aa3417e4 100644
--- a/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
@@ -8,9 +8,6 @@
 
 // test <inttypes.h>
 
-// Missing wcstoimax, wcstoumax
-// XFAIL: LLVM-LIBC-FIXME
-
 #include <inttypes.h>
 
 #include "test_macros.h"

>From 266f578426e4d2b467271497e6094826ac22bb54 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Mon, 6 Jul 2026 23:52:38 +0000
Subject: [PATCH 5/5] disable threads due to atomic issue

---
 libcxx/cmake/caches/Generic-llvm-libc.cmake      | 7 ++++---
 libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in | 2 +-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/cmake/caches/Generic-llvm-libc.cmake b/libcxx/cmake/caches/Generic-llvm-libc.cmake
index 2a4df69bec105..dce7fb73ff8b3 100644
--- a/libcxx/cmake/caches/Generic-llvm-libc.cmake
+++ b/libcxx/cmake/caches/Generic-llvm-libc.cmake
@@ -31,12 +31,13 @@ set(LIBCXX_ENABLE_STATIC ON CACHE BOOL "")
 set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
 
-# Not yet fully implemented.
-set(LIBCXX_ENABLE_WIDE_CHARACTERS ON CACHE BOOL "")
-
 # Still missing some components.
 set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
 
+# blocked on issues with atomic
+set(LIBCXX_ENABLE_THREADS OFF CACHE BOOL "")
+set(LIBCXXABI_ENABLE_THREADS OFF CACHE BOOL "")
+
 set(LIBCXX_ENABLE_UNICODE ON CACHE BOOL "")
 set(LIBCXX_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
 set(LIBCXXABI_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
diff --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index ed1a63c027c9e..9dbf8a98dd5e4 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include-dir} -I %{target-include-dir} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx-dir}/test/support -idirafter @LIBC_KERNEL_HEADERS@'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -latomic -Wl,--end-group -Wl,--defsym,__dso_handle=0'
+    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--end-group -Wl,--defsym,__dso_handle=0'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '


From libcxx-commits at lists.llvm.org  Mon Jul  6 16:54:04 2026
From: libcxx-commits at lists.llvm.org (Michael Jones via libcxx-commits)
Date: Mon, 06 Jul 2026 16:54:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][libc] update LLVM-libc test
	status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a4c401c.8b87e04f.184605.1ef4@mx.google.com>


michaelrj-google wrote:

Due to the atomic issue I've disabled threads for now, bringing the current test status to:
```
Total Discovered Tests: 11457
  Unsupported      : 2093 (18.27%)
  Passed           : 9251 (80.75%)
  Expectedly Failed:  113 (0.99%)
```
I will update the commit message before merging.

https://github.com/llvm/llvm-project/pull/201236

From libcxx-commits at lists.llvm.org  Mon Jul  6 18:23:15 2026
From: libcxx-commits at lists.llvm.org (Charlie Lin via libcxx-commits)
Date: Mon, 06 Jul 2026 18:23:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] Finish supporting Unicode 18 (PR
	#199111)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/199111@github.com>
Message-ID: <6a4c5503.8e75e0cb.5e108.f75f@mx.google.com>


clin1234 wrote:

Ping @ldionne @fhahn @JDevlieghere @RKSimon @boomanaiden154 @arsenm @nico @alexey-bataev @tgymnich, really would like this merged in

https://github.com/llvm/llvm-project/pull/199111

From libcxx-commits at lists.llvm.org  Mon Jul  6 18:24:33 2026
From: libcxx-commits at lists.llvm.org (Charlie Lin via libcxx-commits)
Date: Mon, 06 Jul 2026 18:24:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] Finish supporting Unicode 18 (PR
	#199111)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/199111@github.com>
Message-ID: <6a4c5551.ff037943.2e959a.e8ae@mx.google.com>


https://github.com/clin1234 updated https://github.com/llvm/llvm-project/pull/199111

>From fd74cb0f54e21076697fa66da40af9ba61498249 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Thu, 21 May 2026 13:52:02 -0500
Subject: [PATCH 01/33] Update Unicode version in comments to 18.0.0 in
 generate_escaped_output_table.py

---
 libcxx/utils/generate_escaped_output_table.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/utils/generate_escaped_output_table.py b/libcxx/utils/generate_escaped_output_table.py
index 30583dd8cdc85..dd7173201b466 100755
--- a/libcxx/utils/generate_escaped_output_table.py
+++ b/libcxx/utils/generate_escaped_output_table.py
@@ -322,7 +322,7 @@ def generate_data_tables() -> str:
 
     # The output table has two large entries at the end, with a small "gap"
     #   E0100..E01EF  ; Grapheme_Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
-    # Based on Unicode 15.1.0:
+    # Based on Unicode 18.0.0:
     # - Encoding all these entries in the table requires 1173 entries.
     # - Manually handling these last two blocks reduces the size to 729 entries.
     # This not only reduces the binary size, but also improves the performance

>From 5669ba13bdcd8853eb7a5ae674422b9b70edfafb Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Thu, 21 May 2026 13:55:08 -0500
Subject: [PATCH 02/33] Update Unicode data URL in
 UnicodeNameMappingGenerator.cpp

---
 llvm/utils/UnicodeData/UnicodeNameMappingGenerator.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/utils/UnicodeData/UnicodeNameMappingGenerator.cpp b/llvm/utils/UnicodeData/UnicodeNameMappingGenerator.cpp
index 84ef0daf0ec7a..a42913fa537ed 100644
--- a/llvm/utils/UnicodeData/UnicodeNameMappingGenerator.cpp
+++ b/llvm/utils/UnicodeData/UnicodeNameMappingGenerator.cpp
@@ -8,7 +8,7 @@
 //
 // This file is used to generate lib/Support/UnicodeNameToCodepointGenerated.cpp
 // using UnicodeData.txt and NameAliases.txt available at
-// https://unicode.org/Public/15.1.0/ucd/
+// https://unicode.org/Public/18.0.0/ucd/
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ADT/DenseMap.h"

>From 966a1c5842af813cec2ab59368923e35b200515f Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Thu, 21 May 2026 14:38:07 -0500
Subject: [PATCH 03/33] Update __entries array size and values for Unicode 18

---
 .../include/__format/escaped_output_table.h   | 141 ++++++++++--------
 1 file changed, 75 insertions(+), 66 deletions(-)

diff --git a/libcxx/include/__format/escaped_output_table.h b/libcxx/include/__format/escaped_output_table.h
index a62c715811a0c..c0d401d9a00c1 100644
--- a/libcxx/include/__format/escaped_output_table.h
+++ b/libcxx/include/__format/escaped_output_table.h
@@ -110,7 +110,7 @@ namespace __escaped_output_table {
 /// - bits [14, 31] The lower bound code point of the range. The upper bound of
 ///   the range is lower bound + size. Note the code expects code units the fit
 ///   into 18 bits, instead of the 21 bits needed for the full Unicode range.
-_LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
+_LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[744] = {
     0x00000020 /* 00000000 - 00000020 [   33] */,
     0x001fc021 /* 0000007f - 000000a0 [   34] */,
     0x002b4000 /* 000000ad - 000000ad [    1] */,
@@ -120,10 +120,9 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x00e34000 /* 0000038d - 0000038d [    1] */,
     0x00e88000 /* 000003a2 - 000003a2 [    1] */,
     0x014c0000 /* 00000530 - 00000530 [    1] */,
-    0x0155c001 /* 00000557 - 00000558 [    2] */,
-    0x0162c001 /* 0000058b - 0000058c [    2] */,
+    0x0155c000 /* 00000557 - 00000557 [    1] */,
     0x01640000 /* 00000590 - 00000590 [    1] */,
-    0x01720007 /* 000005c8 - 000005cf [    8] */,
+    0x01728005 /* 000005ca - 000005cf [    6] */,
     0x017ac003 /* 000005eb - 000005ee [    4] */,
     0x017d4010 /* 000005f5 - 00000605 [   17] */,
     0x01870000 /* 0000061c - 0000061c [    1] */,
@@ -137,7 +136,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x02170001 /* 0000085c - 0000085d [    2] */,
     0x0217c000 /* 0000085f - 0000085f [    1] */,
     0x021ac004 /* 0000086b - 0000086f [    5] */,
-    0x0223c007 /* 0000088f - 00000896 [    8] */,
+    0x02240006 /* 00000890 - 00000896 [    7] */,
     0x02388000 /* 000008e2 - 000008e2 [    1] */,
     0x02610000 /* 00000984 - 00000984 [    1] */,
     0x02634001 /* 0000098d - 0000098e [    2] */,
@@ -192,7 +191,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x02ce8001 /* 00000b3a - 00000b3b [    2] */,
     0x02d14001 /* 00000b45 - 00000b46 [    2] */,
     0x02d24001 /* 00000b49 - 00000b4a [    2] */,
-    0x02d38006 /* 00000b4e - 00000b54 [    7] */,
+    0x02d38004 /* 00000b4e - 00000b52 [    5] */,
     0x02d60003 /* 00000b58 - 00000b5b [    4] */,
     0x02d78000 /* 00000b5e - 00000b5e [    1] */,
     0x02d90001 /* 00000b64 - 00000b65 [    2] */,
@@ -221,7 +220,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x03124000 /* 00000c49 - 00000c49 [    1] */,
     0x03138006 /* 00000c4e - 00000c54 [    7] */,
     0x0315c000 /* 00000c57 - 00000c57 [    1] */,
-    0x0316c001 /* 00000c5b - 00000c5c [    2] */,
+    0x0316c000 /* 00000c5b - 00000c5b [    1] */,
     0x03178001 /* 00000c5e - 00000c5f [    2] */,
     0x03190001 /* 00000c64 - 00000c65 [    2] */,
     0x031c0006 /* 00000c70 - 00000c76 [    7] */,
@@ -233,7 +232,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x03314000 /* 00000cc5 - 00000cc5 [    1] */,
     0x03324000 /* 00000cc9 - 00000cc9 [    1] */,
     0x03338006 /* 00000cce - 00000cd4 [    7] */,
-    0x0335c005 /* 00000cd7 - 00000cdc [    6] */,
+    0x0335c004 /* 00000cd7 - 00000cdb [    5] */,
     0x0337c000 /* 00000cdf - 00000cdf [    1] */,
     0x03390001 /* 00000ce4 - 00000ce5 [    2] */,
     0x033c0000 /* 00000cf0 - 00000cf0 [    1] */,
@@ -331,7 +330,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x06a28005 /* 00001a8a - 00001a8f [    6] */,
     0x06a68005 /* 00001a9a - 00001a9f [    6] */,
     0x06ab8001 /* 00001aae - 00001aaf [    2] */,
-    0x06b3c030 /* 00001acf - 00001aff [   49] */,
+    0x06bc400e /* 00001af1 - 00001aff [   15] */,
     0x06d34000 /* 00001b4d - 00001b4d [    1] */,
     0x06fd0007 /* 00001bf4 - 00001bfb [    8] */,
     0x070e0002 /* 00001c38 - 00001c3a [    3] */,
@@ -359,15 +358,12 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x080a0007 /* 00002028 - 0000202f [    8] */,
     0x0817c010 /* 0000205f - 0000206f [   17] */,
     0x081c8001 /* 00002072 - 00002073 [    2] */,
-    0x0823c000 /* 0000208f - 0000208f [    1] */,
-    0x08274002 /* 0000209d - 0000209f [    3] */,
-    0x0830400e /* 000020c1 - 000020cf [   15] */,
+    0x0831400a /* 000020c5 - 000020cf [   11] */,
     0x083c400e /* 000020f1 - 000020ff [   15] */,
     0x08630003 /* 0000218c - 0000218f [    4] */,
     0x090a8015 /* 0000242a - 0000243f [   22] */,
     0x0912c014 /* 0000244b - 0000245f [   21] */,
     0x0add0001 /* 00002b74 - 00002b75 [    2] */,
-    0x0ae58000 /* 00002b96 - 00002b96 [    1] */,
     0x0b3d0004 /* 00002cf4 - 00002cf8 [    5] */,
     0x0b498000 /* 00002d26 - 00002d26 [    1] */,
     0x0b4a0004 /* 00002d28 - 00002d2c [    5] */,
@@ -383,7 +379,8 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x0b73c000 /* 00002dcf - 00002dcf [    1] */,
     0x0b75c000 /* 00002dd7 - 00002dd7 [    1] */,
     0x0b77c000 /* 00002ddf - 00002ddf [    1] */,
-    0x0b978021 /* 00002e5e - 00002e7f [   34] */,
+    0x0b978001 /* 00002e5e - 00002e5f [    2] */,
+    0x0b99001b /* 00002e64 - 00002e7f [   28] */,
     0x0ba68000 /* 00002e9a - 00002e9a [    1] */,
     0x0bbd000b /* 00002ef4 - 00002eff [   12] */,
     0x0bf58019 /* 00002fd6 - 00002fef [   26] */,
@@ -399,10 +396,8 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x2931c008 /* 0000a4c7 - 0000a4cf [    9] */,
     0x298b0013 /* 0000a62c - 0000a63f [   20] */,
     0x29be0007 /* 0000a6f8 - 0000a6ff [    8] */,
-    0x29f38001 /* 0000a7ce - 0000a7cf [    2] */,
-    0x29f48000 /* 0000a7d2 - 0000a7d2 [    1] */,
-    0x29f50000 /* 0000a7d4 - 0000a7d4 [    1] */,
-    0x29f74014 /* 0000a7dd - 0000a7f1 [   21] */,
+    0x29f78003 /* 0000a7de - 0000a7e1 [    4] */,
+    0x29f8c00d /* 0000a7e3 - 0000a7f0 [   14] */,
     0x2a0b4002 /* 0000a82d - 0000a82f [    3] */,
     0x2a0e8005 /* 0000a83a - 0000a83f [    6] */,
     0x2a1e0007 /* 0000a878 - 0000a87f [    8] */,
@@ -423,7 +418,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x2ac5c008 /* 0000ab17 - 0000ab1f [    9] */,
     0x2ac9c000 /* 0000ab27 - 0000ab27 [    1] */,
     0x2acbc000 /* 0000ab2f - 0000ab2f [    1] */,
-    0x2adb0003 /* 0000ab6c - 0000ab6f [    4] */,
+    0x2adb8001 /* 0000ab6e - 0000ab6f [    2] */,
     0x2afb8001 /* 0000abee - 0000abef [    2] */,
     0x2afe8005 /* 0000abfa - 0000abff [    6] */,
     0x35e9000b /* 0000d7a4 - 0000d7af [   12] */,
@@ -438,9 +433,6 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x3ecfc000 /* 0000fb3f - 0000fb3f [    1] */,
     0x3ed08000 /* 0000fb42 - 0000fb42 [    1] */,
     0x3ed14000 /* 0000fb45 - 0000fb45 [    1] */,
-    0x3ef0c00f /* 0000fbc3 - 0000fbd2 [   16] */,
-    0x3f640001 /* 0000fd90 - 0000fd91 [    2] */,
-    0x3f720006 /* 0000fdc8 - 0000fdce [    7] */,
     0x3f74001f /* 0000fdd0 - 0000fdef [   32] */,
     0x3f868005 /* 0000fe1a - 0000fe1f [    6] */,
     0x3f94c000 /* 0000fe53 - 0000fe53 [    1] */,
@@ -498,7 +490,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x41da0017 /* 00010768 - 0001077f [   24] */,
     0x41e18000 /* 00010786 - 00010786 [    1] */,
     0x41ec4000 /* 000107b1 - 000107b1 [    1] */,
-    0x41eec044 /* 000107bb - 000107ff [   69] */,
+    0x41f0003f /* 000107c0 - 000107ff [   64] */,
     0x42018001 /* 00010806 - 00010807 [    2] */,
     0x42024000 /* 00010809 - 00010809 [    1] */,
     0x420d8000 /* 00010836 - 00010836 [    1] */,
@@ -511,7 +503,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x423d8004 /* 000108f6 - 000108fa [    5] */,
     0x42470002 /* 0001091c - 0001091e [    3] */,
     0x424e8004 /* 0001093a - 0001093e [    5] */,
-    0x4250003f /* 00010940 - 0001097f [   64] */,
+    0x42568025 /* 0001095a - 0001097f [   38] */,
     0x426e0003 /* 000109b8 - 000109bb [    4] */,
     0x42740001 /* 000109d0 - 000109d1 [    2] */,
     0x42810000 /* 00010a04 - 00010a04 [    1] */,
@@ -543,7 +535,8 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x43aa8000 /* 00010eaa - 00010eaa [    1] */,
     0x43ab8001 /* 00010eae - 00010eaf [    2] */,
     0x43ac800f /* 00010eb2 - 00010ec1 [   16] */,
-    0x43b14036 /* 00010ec5 - 00010efb [   55] */,
+    0x43b20000 /* 00010ec8 - 00010ec8 [    1] */,
+    0x43bbc000 /* 00010eef - 00010eef [    1] */,
     0x43ca0007 /* 00010f28 - 00010f2f [    8] */,
     0x43d68015 /* 00010f5a - 00010f6f [   22] */,
     0x43e28025 /* 00010f8a - 00010faf [   38] */,
@@ -626,7 +619,8 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x46920007 /* 00011a48 - 00011a4f [    8] */,
     0x46a8c00c /* 00011aa3 - 00011aaf [   13] */,
     0x46be4006 /* 00011af9 - 00011aff [    7] */,
-    0x46c280b5 /* 00011b0a - 00011bbf [  182] */,
+    0x46c2c054 /* 00011b0b - 00011b5f [   85] */,
+    0x46da0057 /* 00011b68 - 00011bbf [   88] */,
     0x46f8800d /* 00011be2 - 00011bef [   14] */,
     0x46fe8005 /* 00011bfa - 00011bff [    6] */,
     0x47024000 /* 00011c09 - 00011c09 [    1] */,
@@ -648,7 +642,10 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x4763c000 /* 00011d8f - 00011d8f [    1] */,
     0x47648000 /* 00011d92 - 00011d92 [    1] */,
     0x47664006 /* 00011d99 - 00011d9f [    7] */,
-    0x476a8135 /* 00011daa - 00011edf [  310] */,
+    0x476a8005 /* 00011daa - 00011daf [    6] */,
+    0x47770003 /* 00011ddc - 00011ddf [    4] */,
+    0x477a8005 /* 00011dea - 00011def [    6] */,
+    0x477c80ed /* 00011df2 - 00011edf [  238] */,
     0x47be4006 /* 00011ef9 - 00011eff [    7] */,
     0x47c44000 /* 00011f11 - 00011f11 [    1] */,
     0x47cec002 /* 00011f3b - 00011f3d [    3] */,
@@ -656,9 +653,8 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x47ec400e /* 00011fb1 - 00011fbf [   15] */,
     0x47fc800c /* 00011ff2 - 00011ffe [   13] */,
     0x48e68065 /* 0001239a - 000123ff [  102] */,
-    0x491bc000 /* 0001246f - 0001246f [    1] */,
-    0x491d400a /* 00012475 - 0001247f [   11] */,
-    0x49510a4b /* 00012544 - 00012f8f [ 2636] */,
+    0x4951000b /* 00012544 - 0001254f [   12] */,
+    0x49a1c908 /* 00012687 - 00012f8f [ 2313] */,
     0x4bfcc00c /* 00012ff3 - 00012fff [   13] */,
     0x4d0c000f /* 00013430 - 0001343f [   16] */,
     0x4d158009 /* 00013456 - 0001345f [   10] */,
@@ -677,40 +673,48 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x5ad88000 /* 00016b62 - 00016b62 [    1] */,
     0x5ade0004 /* 00016b78 - 00016b7c [    5] */,
     0x5ae401af /* 00016b90 - 00016d3f [  432] */,
-    0x5b5e80c5 /* 00016d7a - 00016e3f [  198] */,
-    0x5ba6c064 /* 00016e9b - 00016eff [  101] */,
+    0x5b5e8005 /* 00016d7a - 00016d7f [    6] */,
+    0x5b678001 /* 00016d9e - 00016d9f [    2] */,
+    0x5b6a8095 /* 00016daa - 00016e3f [  150] */,
+    0x5ba6c004 /* 00016e9b - 00016e9f [    5] */,
+    0x5bae4001 /* 00016eb9 - 00016eba [    2] */,
+    0x5bb5002b /* 00016ed4 - 00016eff [   44] */,
     0x5bd2c003 /* 00016f4b - 00016f4e [    4] */,
     0x5be20006 /* 00016f88 - 00016f8e [    7] */,
     0x5be8003f /* 00016fa0 - 00016fdf [   64] */,
     0x5bf9400a /* 00016fe5 - 00016fef [   11] */,
-    0x5bfc800d /* 00016ff2 - 00016fff [   14] */,
-    0x61fe0007 /* 000187f8 - 000187ff [    8] */,
-    0x63358028 /* 00018cd6 - 00018cfe [   41] */,
-    0x634262e6 /* 00018d09 - 0001afef [ 8935] */,
+    0x5bfdc008 /* 00016ff7 - 00016fff [    9] */,
+    0x6336c023 /* 00018cdb - 00018cfe [   36] */,
+    0x6348405e /* 00018d21 - 00018d7f [   95] */,
+    0x637cc00c /* 00018df3 - 00018dff [   13] */,
+    0x6464800d /* 00019192 - 0001919f [   14] */,
+    0x6474de1c /* 000191d3 - 0001afef [ 7709] */,
     0x6bfd0000 /* 0001aff4 - 0001aff4 [    1] */,
     0x6bff0000 /* 0001affc - 0001affc [    1] */,
     0x6bffc000 /* 0001afff - 0001afff [    1] */,
-    0x6c48c00e /* 0001b123 - 0001b131 [   15] */,
+    0x6c4a4008 /* 0001b129 - 0001b131 [    9] */,
     0x6c4cc01c /* 0001b133 - 0001b14f [   29] */,
     0x6c54c001 /* 0001b153 - 0001b154 [    2] */,
     0x6c55800d /* 0001b156 - 0001b163 [   14] */,
-    0x6c5a0007 /* 0001b168 - 0001b16f [    8] */,
+    0x6c5a4006 /* 0001b169 - 0001b16f [    7] */,
     0x6cbf0903 /* 0001b2fc - 0001bbff [ 2308] */,
     0x6f1ac004 /* 0001bc6b - 0001bc6f [    5] */,
     0x6f1f4002 /* 0001bc7d - 0001bc7f [    3] */,
     0x6f224006 /* 0001bc89 - 0001bc8f [    7] */,
     0x6f268001 /* 0001bc9a - 0001bc9b [    2] */,
     0x6f280f5f /* 0001bca0 - 0001cbff [ 3936] */,
-    0x733e8005 /* 0001ccfa - 0001ccff [    6] */,
-    0x73ad004b /* 0001ceb4 - 0001ceff [   76] */,
+    0x733f4002 /* 0001ccfd - 0001ccff [    3] */,
+    0x73ad0005 /* 0001ceb4 - 0001ceb9 [    6] */,
+    0x73b44000 /* 0001ced1 - 0001ced1 [    1] */,
+    0x73b54007 /* 0001ced5 - 0001cedc [    8] */,
+    0x73bf8001 /* 0001cefe - 0001ceff [    2] */,
     0x73cb8001 /* 0001cf2e - 0001cf2f [    2] */,
     0x73d1c008 /* 0001cf47 - 0001cf4f [    9] */,
     0x73f1003b /* 0001cfc4 - 0001cfff [   60] */,
     0x743d8009 /* 0001d0f6 - 0001d0ff [   10] */,
-    0x7449c001 /* 0001d127 - 0001d128 [    2] */,
     0x745cc007 /* 0001d173 - 0001d17a [    8] */,
-    0x747ac014 /* 0001d1eb - 0001d1ff [   21] */,
-    0x74918079 /* 0001d246 - 0001d2bf [  122] */,
+    0x74918009 /* 0001d246 - 0001d24f [   10] */,
+    0x74a0803d /* 0001d282 - 0001d2bf [   62] */,
     0x74b5000b /* 0001d2d4 - 0001d2df [   12] */,
     0x74bd000b /* 0001d2f4 - 0001d2ff [   12] */,
     0x74d5c008 /* 0001d357 - 0001d35f [    9] */,
@@ -733,13 +737,14 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x75514000 /* 0001d545 - 0001d545 [    1] */,
     0x7551c002 /* 0001d547 - 0001d549 [    3] */,
     0x75544000 /* 0001d551 - 0001d551 [    1] */,
-    0x75a98001 /* 0001d6a6 - 0001d6a7 [    2] */,
+    0x75a9c000 /* 0001d6a7 - 0001d6a7 [    1] */,
     0x75f30001 /* 0001d7cc - 0001d7cd [    2] */,
     0x76a3000e /* 0001da8c - 0001da9a [   15] */,
     0x76a80000 /* 0001daa0 - 0001daa0 [    1] */,
-    0x76ac044f /* 0001dab0 - 0001deff [ 1104] */,
-    0x77c7c005 /* 0001df1f - 0001df24 [    6] */,
-    0x77cac0d4 /* 0001df2b - 0001dfff [  213] */,
+    0x76ac004f /* 0001dab0 - 0001daff [   80] */,
+    0x76c743e2 /* 0001db1d - 0001deff [  995] */,
+    0x77e0800d /* 0001df82 - 0001df8f [   14] */,
+    0x77e5c035 /* 0001df97 - 0001dfcc [   54] */,
     0x7801c000 /* 0001e007 - 0001e007 [    1] */,
     0x78064001 /* 0001e019 - 0001e01a [    2] */,
     0x78088000 /* 0001e022 - 0001e022 [    1] */,
@@ -756,7 +761,10 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x78c001cf /* 0001e300 - 0001e4cf [  464] */,
     0x793e80d5 /* 0001e4fa - 0001e5cf [  214] */,
     0x797ec003 /* 0001e5fb - 0001e5fe [    4] */,
-    0x798001df /* 0001e600 - 0001e7df [  480] */,
+    0x798000bf /* 0001e600 - 0001e6bf [  192] */,
+    0x79b7c000 /* 0001e6df - 0001e6df [    1] */,
+    0x79bd8007 /* 0001e6f6 - 0001e6fd [    8] */,
+    0x79c000df /* 0001e700 - 0001e7df [  224] */,
     0x79f9c000 /* 0001e7e7 - 0001e7e7 [    1] */,
     0x79fb0000 /* 0001e7ec - 0001e7ec [    1] */,
     0x79fbc000 /* 0001e7ef - 0001e7ef [    1] */,
@@ -808,44 +816,45 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
     0x7c300000 /* 0001f0c0 - 0001f0c0 [    1] */,
     0x7c340000 /* 0001f0d0 - 0001f0d0 [    1] */,
     0x7c3d8009 /* 0001f0f6 - 0001f0ff [   10] */,
-    0x7c6b8037 /* 0001f1ae - 0001f1e5 [   56] */,
+    0x7c6bc036 /* 0001f1af - 0001f1e5 [   55] */,
     0x7c80c00c /* 0001f203 - 0001f20f [   13] */,
     0x7c8f0003 /* 0001f23c - 0001f23f [    4] */,
     0x7c924006 /* 0001f249 - 0001f24f [    7] */,
     0x7c94800d /* 0001f252 - 0001f25f [   14] */,
     0x7c998099 /* 0001f266 - 0001f2ff [  154] */,
-    0x7db60003 /* 0001f6d8 - 0001f6db [    4] */,
+    0x7db68001 /* 0001f6da - 0001f6db [    2] */,
     0x7dbb4002 /* 0001f6ed - 0001f6ef [    3] */,
     0x7dbf4002 /* 0001f6fd - 0001f6ff [    3] */,
-    0x7dddc003 /* 0001f777 - 0001f77a [    4] */,
-    0x7df68005 /* 0001f7da - 0001f7df [    6] */,
+    0x7df70003 /* 0001f7dc - 0001f7df [    4] */,
     0x7dfb0003 /* 0001f7ec - 0001f7ef [    4] */,
-    0x7dfc400e /* 0001f7f1 - 0001f7ff [   15] */,
     0x7e030003 /* 0001f80c - 0001f80f [    4] */,
     0x7e120007 /* 0001f848 - 0001f84f [    8] */,
     0x7e168005 /* 0001f85a - 0001f85f [    6] */,
     0x7e220007 /* 0001f888 - 0001f88f [    8] */,
     0x7e2b8001 /* 0001f8ae - 0001f8af [    2] */,
     0x7e2f0003 /* 0001f8bc - 0001f8bf [    4] */,
-    0x7e30803d /* 0001f8c2 - 0001f8ff [   62] */,
-    0x7e95000b /* 0001fa54 - 0001fa5f [   12] */,
+    0x7e30800d /* 0001f8c2 - 0001f8cf [   14] */,
+    0x7e364026 /* 0001f8d9 - 0001f8ff [   39] */,
+    0x7e960007 /* 0001fa58 - 0001fa5f [    8] */,
     0x7e9b8001 /* 0001fa6e - 0001fa6f [    2] */,
     0x7e9f4002 /* 0001fa7d - 0001fa7f [    3] */,
-    0x7ea28004 /* 0001fa8a - 0001fa8e [    5] */,
-    0x7eb1c006 /* 0001fac7 - 0001facd [    7] */,
-    0x7eb74001 /* 0001fadd - 0001fade [    2] */,
-    0x7eba8005 /* 0001faea - 0001faef [    6] */,
-    0x7ebe4006 /* 0001faf9 - 0001faff [    7] */,
+    0x7eb1c000 /* 0001fac7 - 0001fac7 [    1] */,
+    0x7eb24002 /* 0001fac9 - 0001facb [    3] */,
+    0x7eb78000 /* 0001fade - 0001fade [    1] */,
+    0x7ebb0002 /* 0001faec - 0001faee [    3] */,
+    0x7ebec004 /* 0001fafb - 0001faff [    5] */,
     0x7ee4c000 /* 0001fb93 - 0001fb93 [    1] */,
-    0x7efe8405 /* 0001fbfa - 0001ffff [ 1030] */,
+    0x7efec404 /* 0001fbfb - 0001ffff [ 1029] */,
     0xa9b8001f /* 0002a6e0 - 0002a6ff [   32] */,
-    0xadce8005 /* 0002b73a - 0002b73f [    6] */,
-    0xae078001 /* 0002b81e - 0002b81f [    2] */,
-    0xb3a8800d /* 0002cea2 - 0002ceaf [   14] */,
+    0xae07c000 /* 0002b81f - 0002b81f [    1] */,
+    0xb3ab8001 /* 0002ceae - 0002ceaf [    2] */,
     0xbaf8400e /* 0002ebe1 - 0002ebef [   15] */,
     0xbb9789a1 /* 0002ee5e - 0002f7ff [ 2466] */,
     0xbe8785e1 /* 0002fa1e - 0002ffff [ 1506] */,
-    0xc4d2c004 /* 0003134b - 0003134f [    5] */};
+    0xc4d2c004 /* 0003134b - 0003134f [    5] */,
+    0xcd1ebfff /* 0003347a - 00037479 [16384] */,
+    0xdd1ebfff /* 0003747a - 0003b479 [16384] */,
+    0xed1e9b85 /* 0003b47a - 0003cfff [ 7046] */};
 
 /// Returns whether the code unit needs to be escaped.
 ///
@@ -866,7 +875,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[735] = {
      return false;
 
   // The entries at the end.
-  if (__code_point >= 0x000323b0)
+  if (__code_point >= 0x0003fc40)
     return true;
 
   ptrdiff_t __i = std::upper_bound(std::begin(__entries), std::end(__entries), (__code_point << 14) | 0x3fffu) - __entries;

>From 0084a9cd102523343feffdb579c2002fa59f2bee Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Thu, 21 May 2026 15:27:36 -0500
Subject: [PATCH 04/33] Update unicode-case-fold.py

---
 llvm/utils/unicode-case-fold.py | 44 +++++++++++++++++----------------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/llvm/utils/unicode-case-fold.py b/llvm/utils/unicode-case-fold.py
index 4afb41d4060f0..ca0423082b724 100755
--- a/llvm/utils/unicode-case-fold.py
+++ b/llvm/utils/unicode-case-fold.py
@@ -36,7 +36,9 @@ def mappings(f):
         m = expr.match(line)
         if not m:
             continue
-        from_char = int(m.group(1), 16)
+        group_1 = m.group(1)
+        if "b" in group_1: group_1 = group_1[2:]
+        from_char = int(group_1, 16)
         to_char = int(m.group(2), 16)
         from_name = m.group(3)
 
@@ -101,28 +103,28 @@ def dump_block(b):
 
 
 current_block = []
-f = urlopen(sys.argv[1])
-for m in mappings(f):
-    if len(current_block) == 0:
-        current_block.append(m)
-        continue
-
-    if shift(current_block[0]) != shift(m):
-        # Incompatible shift, start a new block.
+with urlopen(sys.argv[1]) as f:
+    g = [str(x) for x in f.readlines()]
+    for m in mappings(g):
+        if len(current_block) == 0:
+            current_block.append(m)
+            continue
+
+        if shift(current_block[0]) != shift(m):
+            # Incompatible shift, start a new block.
+            dump_block(current_block)
+            current_block = [m]
+            continue
+
+        if len(current_block) == 1 or stride(current_block) == stride2(
+            current_block[-1], m
+        ):
+            current_block.append(m)
+            continue
+
+        # Incompatible stride, start a new block.
         dump_block(current_block)
         current_block = [m]
-        continue
-
-    if len(current_block) == 1 or stride(current_block) == stride2(
-        current_block[-1], m
-    ):
-        current_block.append(m)
-        continue
-
-    # Incompatible stride, start a new block.
-    dump_block(current_block)
-    current_block = [m]
-f.close()
 
 dump_block(current_block)
 

>From b4d794503557029086110782c4fbaef7265d33fa Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Thu, 21 May 2026 15:28:40 -0500
Subject: [PATCH 05/33] Update UnicodeCaseFold.cpp

---
 llvm/lib/Support/UnicodeCaseFold.cpp | 285 ++++++++++++++++-----------
 1 file changed, 170 insertions(+), 115 deletions(-)

diff --git a/llvm/lib/Support/UnicodeCaseFold.cpp b/llvm/lib/Support/UnicodeCaseFold.cpp
index abf6df8d1e3c3..007c6261568ca 100644
--- a/llvm/lib/Support/UnicodeCaseFold.cpp
+++ b/llvm/lib/Support/UnicodeCaseFold.cpp
@@ -2,11 +2,11 @@
 //
 // This file was generated by utils/unicode-case-fold.py from the Unicode
 // case folding database at
-//    http://www.unicode.org/Public/15.1.0/ucd/CaseFolding.txt
+//    https://www.unicode.org/Public/draft/ucd/CaseFolding.txt
 //
 // To regenerate this file, run:
 //   utils/unicode-case-fold.py \
-//     "http://www.unicode.org/Public/15.1.0/ucd/CaseFolding.txt" \
+//     "https://www.unicode.org/Public/draft/ucd/CaseFolding.txt" \
 //     > lib/Support/UnicodeCaseFold.cpp
 //
 //===----------------------------------------------------------------------===//
@@ -19,7 +19,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 26 characters
   if (C <= 0x005a)
     return C + 32;
-  // MICRO SIGN
+  // MICRO SIGN\n'
   if (C == 0x00b5)
     return 0x03bc;
   if (C < 0x00c0)
@@ -52,7 +52,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 23 characters
   if (C <= 0x0176)
     return C | 1;
-  // LATIN CAPITAL LETTER Y WITH DIAERESIS
+  // LATIN CAPITAL LETTER Y WITH DIAERESIS\n'
   if (C == 0x0178)
     return 0x00ff;
   if (C < 0x0179)
@@ -60,10 +60,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 3 characters
   if (C <= 0x017d && C % 2 == 1)
     return C + 1;
-  // LATIN SMALL LETTER LONG S
+  // LATIN SMALL LETTER LONG S\n'
   if (C == 0x017f)
     return 0x0073;
-  // LATIN CAPITAL LETTER B WITH HOOK
+  // LATIN CAPITAL LETTER B WITH HOOK\n'
   if (C == 0x0181)
     return 0x0253;
   if (C < 0x0182)
@@ -71,10 +71,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x0184)
     return C | 1;
-  // LATIN CAPITAL LETTER OPEN O
+  // LATIN CAPITAL LETTER OPEN O\n'
   if (C == 0x0186)
     return 0x0254;
-  // LATIN CAPITAL LETTER C WITH HOOK
+  // LATIN CAPITAL LETTER C WITH HOOK\n'
   if (C == 0x0187)
     return 0x0188;
   if (C < 0x0189)
@@ -82,43 +82,43 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x018a)
     return C + 205;
-  // LATIN CAPITAL LETTER D WITH TOPBAR
+  // LATIN CAPITAL LETTER D WITH TOPBAR\n'
   if (C == 0x018b)
     return 0x018c;
-  // LATIN CAPITAL LETTER REVERSED E
+  // LATIN CAPITAL LETTER REVERSED E\n'
   if (C == 0x018e)
     return 0x01dd;
-  // LATIN CAPITAL LETTER SCHWA
+  // LATIN CAPITAL LETTER SCHWA\n'
   if (C == 0x018f)
     return 0x0259;
-  // LATIN CAPITAL LETTER OPEN E
+  // LATIN CAPITAL LETTER OPEN E\n'
   if (C == 0x0190)
     return 0x025b;
-  // LATIN CAPITAL LETTER F WITH HOOK
+  // LATIN CAPITAL LETTER F WITH HOOK\n'
   if (C == 0x0191)
     return 0x0192;
-  // LATIN CAPITAL LETTER G WITH HOOK
+  // LATIN CAPITAL LETTER G WITH HOOK\n'
   if (C == 0x0193)
     return 0x0260;
-  // LATIN CAPITAL LETTER GAMMA
+  // LATIN CAPITAL LETTER GAMMA\n'
   if (C == 0x0194)
     return 0x0263;
-  // LATIN CAPITAL LETTER IOTA
+  // LATIN CAPITAL LETTER IOTA\n'
   if (C == 0x0196)
     return 0x0269;
-  // LATIN CAPITAL LETTER I WITH STROKE
+  // LATIN CAPITAL LETTER I WITH STROKE\n'
   if (C == 0x0197)
     return 0x0268;
-  // LATIN CAPITAL LETTER K WITH HOOK
+  // LATIN CAPITAL LETTER K WITH HOOK\n'
   if (C == 0x0198)
     return 0x0199;
-  // LATIN CAPITAL LETTER TURNED M
+  // LATIN CAPITAL LETTER TURNED M\n'
   if (C == 0x019c)
     return 0x026f;
-  // LATIN CAPITAL LETTER N WITH LEFT HOOK
+  // LATIN CAPITAL LETTER N WITH LEFT HOOK\n'
   if (C == 0x019d)
     return 0x0272;
-  // LATIN CAPITAL LETTER O WITH MIDDLE TILDE
+  // LATIN CAPITAL LETTER O WITH MIDDLE TILDE\n'
   if (C == 0x019f)
     return 0x0275;
   if (C < 0x01a0)
@@ -126,22 +126,22 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 3 characters
   if (C <= 0x01a4)
     return C | 1;
-  // LATIN LETTER YR
+  // LATIN LETTER YR\n'
   if (C == 0x01a6)
     return 0x0280;
-  // LATIN CAPITAL LETTER TONE TWO
+  // LATIN CAPITAL LETTER TONE TWO\n'
   if (C == 0x01a7)
     return 0x01a8;
-  // LATIN CAPITAL LETTER ESH
+  // LATIN CAPITAL LETTER ESH\n'
   if (C == 0x01a9)
     return 0x0283;
-  // LATIN CAPITAL LETTER T WITH HOOK
+  // LATIN CAPITAL LETTER T WITH HOOK\n'
   if (C == 0x01ac)
     return 0x01ad;
-  // LATIN CAPITAL LETTER T WITH RETROFLEX HOOK
+  // LATIN CAPITAL LETTER T WITH RETROFLEX HOOK\n'
   if (C == 0x01ae)
     return 0x0288;
-  // LATIN CAPITAL LETTER U WITH HORN
+  // LATIN CAPITAL LETTER U WITH HORN\n'
   if (C == 0x01af)
     return 0x01b0;
   if (C < 0x01b1)
@@ -154,7 +154,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x01b5 && C % 2 == 1)
     return C + 1;
-  // LATIN CAPITAL LETTER EZH
+  // LATIN CAPITAL LETTER EZH\n'
   if (C == 0x01b7)
     return 0x0292;
   if (C < 0x01b8)
@@ -162,19 +162,19 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x01bc && C % 4 == 0)
     return C + 1;
-  // LATIN CAPITAL LETTER DZ WITH CARON
+  // LATIN CAPITAL LETTER DZ WITH CARON\n'
   if (C == 0x01c4)
     return 0x01c6;
-  // LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON
+  // LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON\n'
   if (C == 0x01c5)
     return 0x01c6;
-  // LATIN CAPITAL LETTER LJ
+  // LATIN CAPITAL LETTER LJ\n'
   if (C == 0x01c7)
     return 0x01c9;
-  // LATIN CAPITAL LETTER L WITH SMALL LETTER J
+  // LATIN CAPITAL LETTER L WITH SMALL LETTER J\n'
   if (C == 0x01c8)
     return 0x01c9;
-  // LATIN CAPITAL LETTER NJ
+  // LATIN CAPITAL LETTER NJ\n'
   if (C == 0x01ca)
     return 0x01cc;
   if (C < 0x01cb)
@@ -187,7 +187,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 9 characters
   if (C <= 0x01ee)
     return C | 1;
-  // LATIN CAPITAL LETTER DZ
+  // LATIN CAPITAL LETTER DZ\n'
   if (C == 0x01f1)
     return 0x01f3;
   if (C < 0x01f2)
@@ -195,10 +195,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x01f4)
     return C | 1;
-  // LATIN CAPITAL LETTER HWAIR
+  // LATIN CAPITAL LETTER HWAIR\n'
   if (C == 0x01f6)
     return 0x0195;
-  // LATIN CAPITAL LETTER WYNN
+  // LATIN CAPITAL LETTER WYNN\n'
   if (C == 0x01f7)
     return 0x01bf;
   if (C < 0x01f8)
@@ -206,7 +206,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 20 characters
   if (C <= 0x021e)
     return C | 1;
-  // LATIN CAPITAL LETTER N WITH LONG RIGHT LEG
+  // LATIN CAPITAL LETTER N WITH LONG RIGHT LEG\n'
   if (C == 0x0220)
     return 0x019e;
   if (C < 0x0222)
@@ -214,28 +214,28 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 9 characters
   if (C <= 0x0232)
     return C | 1;
-  // LATIN CAPITAL LETTER A WITH STROKE
+  // LATIN CAPITAL LETTER A WITH STROKE\n'
   if (C == 0x023a)
     return 0x2c65;
-  // LATIN CAPITAL LETTER C WITH STROKE
+  // LATIN CAPITAL LETTER C WITH STROKE\n'
   if (C == 0x023b)
     return 0x023c;
-  // LATIN CAPITAL LETTER L WITH BAR
+  // LATIN CAPITAL LETTER L WITH BAR\n'
   if (C == 0x023d)
     return 0x019a;
-  // LATIN CAPITAL LETTER T WITH DIAGONAL STROKE
+  // LATIN CAPITAL LETTER T WITH DIAGONAL STROKE\n'
   if (C == 0x023e)
     return 0x2c66;
-  // LATIN CAPITAL LETTER GLOTTAL STOP
+  // LATIN CAPITAL LETTER GLOTTAL STOP\n'
   if (C == 0x0241)
     return 0x0242;
-  // LATIN CAPITAL LETTER B WITH STROKE
+  // LATIN CAPITAL LETTER B WITH STROKE\n'
   if (C == 0x0243)
     return 0x0180;
-  // LATIN CAPITAL LETTER U BAR
+  // LATIN CAPITAL LETTER U BAR\n'
   if (C == 0x0244)
     return 0x0289;
-  // LATIN CAPITAL LETTER TURNED V
+  // LATIN CAPITAL LETTER TURNED V\n'
   if (C == 0x0245)
     return 0x028c;
   if (C < 0x0246)
@@ -243,7 +243,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 5 characters
   if (C <= 0x024e)
     return C | 1;
-  // COMBINING GREEK YPOGEGRAMMENI
+  // COMBINING GREEK YPOGEGRAMMENI\n'
   if (C == 0x0345)
     return 0x03b9;
   if (C < 0x0370)
@@ -251,13 +251,13 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x0372)
     return C | 1;
-  // GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA
+  // GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA\n'
   if (C == 0x0376)
     return 0x0377;
-  // GREEK CAPITAL LETTER YOT
+  // GREEK CAPITAL LETTER YOT\n'
   if (C == 0x037f)
     return 0x03f3;
-  // GREEK CAPITAL LETTER ALPHA WITH TONOS
+  // GREEK CAPITAL LETTER ALPHA WITH TONOS\n'
   if (C == 0x0386)
     return 0x03ac;
   if (C < 0x0388)
@@ -265,7 +265,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 3 characters
   if (C <= 0x038a)
     return C + 37;
-  // GREEK CAPITAL LETTER OMICRON WITH TONOS
+  // GREEK CAPITAL LETTER OMICRON WITH TONOS\n'
   if (C == 0x038c)
     return 0x03cc;
   if (C < 0x038e)
@@ -283,22 +283,22 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 9 characters
   if (C <= 0x03ab)
     return C + 32;
-  // GREEK SMALL LETTER FINAL SIGMA
+  // GREEK SMALL LETTER FINAL SIGMA\n'
   if (C == 0x03c2)
     return 0x03c3;
-  // GREEK CAPITAL KAI SYMBOL
+  // GREEK CAPITAL KAI SYMBOL\n'
   if (C == 0x03cf)
     return 0x03d7;
-  // GREEK BETA SYMBOL
+  // GREEK BETA SYMBOL\n'
   if (C == 0x03d0)
     return 0x03b2;
-  // GREEK THETA SYMBOL
+  // GREEK THETA SYMBOL\n'
   if (C == 0x03d1)
     return 0x03b8;
-  // GREEK PHI SYMBOL
+  // GREEK PHI SYMBOL\n'
   if (C == 0x03d5)
     return 0x03c6;
-  // GREEK PI SYMBOL
+  // GREEK PI SYMBOL\n'
   if (C == 0x03d6)
     return 0x03c0;
   if (C < 0x03d8)
@@ -306,25 +306,25 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 12 characters
   if (C <= 0x03ee)
     return C | 1;
-  // GREEK KAPPA SYMBOL
+  // GREEK KAPPA SYMBOL\n'
   if (C == 0x03f0)
     return 0x03ba;
-  // GREEK RHO SYMBOL
+  // GREEK RHO SYMBOL\n'
   if (C == 0x03f1)
     return 0x03c1;
-  // GREEK CAPITAL THETA SYMBOL
+  // GREEK CAPITAL THETA SYMBOL\n'
   if (C == 0x03f4)
     return 0x03b8;
-  // GREEK LUNATE EPSILON SYMBOL
+  // GREEK LUNATE EPSILON SYMBOL\n'
   if (C == 0x03f5)
     return 0x03b5;
-  // GREEK CAPITAL LETTER SHO
+  // GREEK CAPITAL LETTER SHO\n'
   if (C == 0x03f7)
     return 0x03f8;
-  // GREEK CAPITAL LUNATE SIGMA SYMBOL
+  // GREEK CAPITAL LUNATE SIGMA SYMBOL\n'
   if (C == 0x03f9)
     return 0x03f2;
-  // GREEK CAPITAL LETTER SAN
+  // GREEK CAPITAL LETTER SAN\n'
   if (C == 0x03fa)
     return 0x03fb;
   if (C < 0x03fd)
@@ -352,7 +352,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 27 characters
   if (C <= 0x04be)
     return C | 1;
-  // CYRILLIC LETTER PALOCHKA
+  // CYRILLIC LETTER PALOCHKA\n'
   if (C == 0x04c0)
     return 0x04cf;
   if (C < 0x04c1)
@@ -385,13 +385,13 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 6 characters
   if (C <= 0x13fd)
     return C + -8;
-  // CYRILLIC SMALL LETTER ROUNDED VE
+  // CYRILLIC SMALL LETTER ROUNDED VE\n'
   if (C == 0x1c80)
     return 0x0432;
-  // CYRILLIC SMALL LETTER LONG-LEGGED DE
+  // CYRILLIC SMALL LETTER LONG-LEGGED DE\n'
   if (C == 0x1c81)
     return 0x0434;
-  // CYRILLIC SMALL LETTER NARROW O
+  // CYRILLIC SMALL LETTER NARROW O\n'
   if (C == 0x1c82)
     return 0x043e;
   if (C < 0x1c83)
@@ -399,18 +399,21 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x1c84)
     return C + -6210;
-  // CYRILLIC SMALL LETTER THREE-LEGGED TE
+  // CYRILLIC SMALL LETTER THREE-LEGGED TE\n'
   if (C == 0x1c85)
     return 0x0442;
-  // CYRILLIC SMALL LETTER TALL HARD SIGN
+  // CYRILLIC SMALL LETTER TALL HARD SIGN\n'
   if (C == 0x1c86)
     return 0x044a;
-  // CYRILLIC SMALL LETTER TALL YAT
+  // CYRILLIC SMALL LETTER TALL YAT\n'
   if (C == 0x1c87)
     return 0x0463;
-  // CYRILLIC SMALL LETTER UNBLENDED UK
+  // CYRILLIC SMALL LETTER UNBLENDED UK\n'
   if (C == 0x1c88)
     return 0xa64b;
+  // CYRILLIC CAPITAL LETTER TJE\n'
+  if (C == 0x1c89)
+    return 0x1c8a;
   if (C < 0x1c90)
     return C;
   // 43 characters
@@ -426,10 +429,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 75 characters
   if (C <= 0x1e94)
     return C | 1;
-  // LATIN SMALL LETTER LONG S WITH DOT ABOVE
+  // LATIN SMALL LETTER LONG S WITH DOT ABOVE\n'
   if (C == 0x1e9b)
     return 0x1e61;
-  // LATIN CAPITAL LETTER SHARP S
+  // LATIN CAPITAL LETTER SHARP S\n'
   if (C == 0x1e9e)
     return 0x00df;
   if (C < 0x1ea0)
@@ -497,10 +500,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x1fbb)
     return C + -74;
-  // GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+  // GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI\n'
   if (C == 0x1fbc)
     return 0x1fb3;
-  // GREEK PROSGEGRAMMENI
+  // GREEK PROSGEGRAMMENI\n'
   if (C == 0x1fbe)
     return 0x03b9;
   if (C < 0x1fc8)
@@ -508,10 +511,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 4 characters
   if (C <= 0x1fcb)
     return C + -86;
-  // GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+  // GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI\n'
   if (C == 0x1fcc)
     return 0x1fc3;
-  // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+  // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA\n'
   if (C == 0x1fd3)
     return 0x0390;
   if (C < 0x1fd8)
@@ -524,7 +527,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x1fdb)
     return C + -100;
-  // GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
+  // GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA\n'
   if (C == 0x1fe3)
     return 0x03b0;
   if (C < 0x1fe8)
@@ -537,7 +540,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x1feb)
     return C + -112;
-  // GREEK CAPITAL LETTER RHO WITH DASIA
+  // GREEK CAPITAL LETTER RHO WITH DASIA\n'
   if (C == 0x1fec)
     return 0x1fe5;
   if (C < 0x1ff8)
@@ -550,19 +553,19 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x1ffb)
     return C + -126;
-  // GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+  // GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI\n'
   if (C == 0x1ffc)
     return 0x1ff3;
-  // OHM SIGN
+  // OHM SIGN\n'
   if (C == 0x2126)
     return 0x03c9;
-  // KELVIN SIGN
+  // KELVIN SIGN\n'
   if (C == 0x212a)
     return 0x006b;
-  // ANGSTROM SIGN
+  // ANGSTROM SIGN\n'
   if (C == 0x212b)
     return 0x00e5;
-  // TURNED CAPITAL F
+  // TURNED CAPITAL F\n'
   if (C == 0x2132)
     return 0x214e;
   if (C < 0x2160)
@@ -570,7 +573,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 16 characters
   if (C <= 0x216f)
     return C + 16;
-  // ROMAN NUMERAL REVERSED ONE HUNDRED
+  // ROMAN NUMERAL REVERSED ONE HUNDRED\n'
   if (C == 0x2183)
     return 0x2184;
   if (C < 0x24b6)
@@ -583,16 +586,16 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 48 characters
   if (C <= 0x2c2f)
     return C + 48;
-  // LATIN CAPITAL LETTER L WITH DOUBLE BAR
+  // LATIN CAPITAL LETTER L WITH DOUBLE BAR\n'
   if (C == 0x2c60)
     return 0x2c61;
-  // LATIN CAPITAL LETTER L WITH MIDDLE TILDE
+  // LATIN CAPITAL LETTER L WITH MIDDLE TILDE\n'
   if (C == 0x2c62)
     return 0x026b;
-  // LATIN CAPITAL LETTER P WITH STROKE
+  // LATIN CAPITAL LETTER P WITH STROKE\n'
   if (C == 0x2c63)
     return 0x1d7d;
-  // LATIN CAPITAL LETTER R WITH TAIL
+  // LATIN CAPITAL LETTER R WITH TAIL\n'
   if (C == 0x2c64)
     return 0x027d;
   if (C < 0x2c67)
@@ -600,16 +603,16 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 3 characters
   if (C <= 0x2c6b && C % 2 == 1)
     return C + 1;
-  // LATIN CAPITAL LETTER ALPHA
+  // LATIN CAPITAL LETTER ALPHA\n'
   if (C == 0x2c6d)
     return 0x0251;
-  // LATIN CAPITAL LETTER M WITH HOOK
+  // LATIN CAPITAL LETTER M WITH HOOK\n'
   if (C == 0x2c6e)
     return 0x0271;
-  // LATIN CAPITAL LETTER TURNED A
+  // LATIN CAPITAL LETTER TURNED A\n'
   if (C == 0x2c6f)
     return 0x0250;
-  // LATIN CAPITAL LETTER TURNED ALPHA
+  // LATIN CAPITAL LETTER TURNED ALPHA\n'
   if (C == 0x2c70)
     return 0x0252;
   if (C < 0x2c72)
@@ -662,7 +665,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0xa77b && C % 2 == 1)
     return C + 1;
-  // LATIN CAPITAL LETTER INSULAR G
+  // LATIN CAPITAL LETTER INSULAR G\n'
   if (C == 0xa77d)
     return 0x1d79;
   if (C < 0xa77e)
@@ -670,10 +673,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 5 characters
   if (C <= 0xa786)
     return C | 1;
-  // LATIN CAPITAL LETTER SALTILLO
+  // LATIN CAPITAL LETTER SALTILLO\n'
   if (C == 0xa78b)
     return 0xa78c;
-  // LATIN CAPITAL LETTER TURNED H
+  // LATIN CAPITAL LETTER TURNED H\n'
   if (C == 0xa78d)
     return 0x0265;
   if (C < 0xa790)
@@ -686,31 +689,31 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 10 characters
   if (C <= 0xa7a8)
     return C | 1;
-  // LATIN CAPITAL LETTER H WITH HOOK
+  // LATIN CAPITAL LETTER H WITH HOOK\n'
   if (C == 0xa7aa)
     return 0x0266;
-  // LATIN CAPITAL LETTER REVERSED OPEN E
+  // LATIN CAPITAL LETTER REVERSED OPEN E\n'
   if (C == 0xa7ab)
     return 0x025c;
-  // LATIN CAPITAL LETTER SCRIPT G
+  // LATIN CAPITAL LETTER SCRIPT G\n'
   if (C == 0xa7ac)
     return 0x0261;
-  // LATIN CAPITAL LETTER L WITH BELT
+  // LATIN CAPITAL LETTER L WITH BELT\n'
   if (C == 0xa7ad)
     return 0x026c;
-  // LATIN CAPITAL LETTER SMALL CAPITAL I
+  // LATIN CAPITAL LETTER SMALL CAPITAL I\n'
   if (C == 0xa7ae)
     return 0x026a;
-  // LATIN CAPITAL LETTER TURNED K
+  // LATIN CAPITAL LETTER TURNED K\n'
   if (C == 0xa7b0)
     return 0x029e;
-  // LATIN CAPITAL LETTER TURNED T
+  // LATIN CAPITAL LETTER TURNED T\n'
   if (C == 0xa7b1)
     return 0x0287;
-  // LATIN CAPITAL LETTER J WITH CROSSED-TAIL
+  // LATIN CAPITAL LETTER J WITH CROSSED-TAIL\n'
   if (C == 0xa7b2)
     return 0x029d;
-  // LATIN CAPITAL LETTER CHI
+  // LATIN CAPITAL LETTER CHI\n'
   if (C == 0xa7b3)
     return 0xab53;
   if (C < 0xa7b4)
@@ -718,13 +721,13 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 8 characters
   if (C <= 0xa7c2)
     return C | 1;
-  // LATIN CAPITAL LETTER C WITH PALATAL HOOK
+  // LATIN CAPITAL LETTER C WITH PALATAL HOOK\n'
   if (C == 0xa7c4)
     return 0xa794;
-  // LATIN CAPITAL LETTER S WITH HOOK
+  // LATIN CAPITAL LETTER S WITH HOOK\n'
   if (C == 0xa7c5)
     return 0x0282;
-  // LATIN CAPITAL LETTER Z WITH PALATAL HOOK
+  // LATIN CAPITAL LETTER Z WITH PALATAL HOOK\n'
   if (C == 0xa7c6)
     return 0x1d8e;
   if (C < 0xa7c7)
@@ -732,22 +735,36 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0xa7c9 && C % 2 == 1)
     return C + 1;
-  if (C < 0xa7d0)
+  // LATIN CAPITAL LETTER RAMS HORN\n'
+  if (C == 0xa7cb)
+    return 0x0264;
+  if (C < 0xa7cc)
+    return C;
+  // 8 characters
+  if (C <= 0xa7da)
+    return C | 1;
+  // LATIN CAPITAL LETTER LAMBDA WITH STROKE\n'
+  if (C == 0xa7dc)
+    return 0x019b;
+  if (C < 0xa7dd)
     return C;
   // 2 characters
-  if (C <= 0xa7d6 && C % 6 == 0)
-    return C + 1;
-  if (C < 0xa7d8)
+  if (C <= 0xa7e2 && C % 5 == 3)
+    return C + -42342;
+  // LATIN CAPITAL LETTER REVERSED HALF H\n'
+  if (C == 0xa7f5)
+    return 0xa7f6;
+  if (C < 0xab6c)
     return C;
   // 2 characters
-  if (C <= 0xa7f5 && C % 29 == 19)
-    return C + 1;
+  if (C <= 0xab6d)
+    return C + -33;
   if (C < 0xab70)
     return C;
   // 80 characters
   if (C <= 0xabbf)
     return C + -38864;
-  // LATIN SMALL LIGATURE LONG S T
+  // LATIN SMALL LIGATURE LONG S T\n'
   if (C == 0xfb05)
     return 0xfb06;
   if (C < 0xff21)
@@ -790,6 +807,11 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 51 characters
   if (C <= 0x10cb2)
     return C + 64;
+  if (C < 0x10d50)
+    return C;
+  // 22 characters
+  if (C <= 0x10d65)
+    return C + 32;
   if (C < 0x118a0)
     return C;
   // 32 characters
@@ -800,6 +822,39 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 32 characters
   if (C <= 0x16e5f)
     return C + 32;
+  if (C < 0x16ea0)
+    return C;
+  // 25 characters
+  if (C <= 0x16eb8)
+    return C + 27;
+  if (C < 0x1df40)
+    return C;
+  // 2 characters
+  if (C <= 0x1df48 && C % 8 == 0)
+    return C + 1;
+  if (C < 0x1df4a)
+    return C;
+  // 2 characters
+  if (C <= 0x1df4d && C % 3 == 1)
+    return C + 1;
+  if (C < 0x1df51)
+    return C;
+  // 2 characters
+  if (C <= 0x1df68 && C % 23 == 0)
+    return C + 1;
+  if (C < 0x1df6a)
+    return C;
+  // 3 characters
+  if (C <= 0x1df6e)
+    return C | 1;
+  if (C < 0x1df72)
+    return C;
+  // 7 characters
+  if (C <= 0x1df7e)
+    return C | 1;
+  // LATIN SMALL LIGATURE LONG S WITH DESCENDER S\n'
+  if (C == 0x1df95)
+    return 0x00df;
   if (C < 0x1e900)
     return C;
   // 34 characters

>From 4fe797de550d36083ba6bc2b924808e16f3e68a9 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Thu, 21 May 2026 15:40:45 -0500
Subject: [PATCH 06/33] Update unicode-case-fold.py

---
 llvm/utils/unicode-case-fold.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/llvm/utils/unicode-case-fold.py b/llvm/utils/unicode-case-fold.py
index ca0423082b724..cfa5b279facf3 100755
--- a/llvm/utils/unicode-case-fold.py
+++ b/llvm/utils/unicode-case-fold.py
@@ -40,7 +40,9 @@ def mappings(f):
         if "b" in group_1: group_1 = group_1[2:]
         from_char = int(group_1, 16)
         to_char = int(m.group(2), 16)
-        from_name = m.group(3)
+        group_3 = m.group(3)
+        if "\\n" in group_3: group_3 = group_3[:-3]
+        from_name = group_3
 
         if from_char <= previous_from:
             raise Exception("Duplicate or unsorted characters in input")
@@ -68,7 +70,6 @@ def stride(block):
 # shift and the stride between adjecant mappings (if any) is constant.
 def dump_block(b):
     global body
-
     if len(b) == 1:
         # Special case for handling blocks of length 1. We don't even need to
         # emit the "if (C < X) return C" check below as all characters in this

>From 0b09ce24e1d37ce078f099e10f0a880f2c25f6f6 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Thu, 21 May 2026 15:41:50 -0500
Subject: [PATCH 07/33] Update UnicodeCaseFold.cpp

---
 llvm/lib/Support/UnicodeCaseFold.cpp | 224 +++++++++++++--------------
 1 file changed, 112 insertions(+), 112 deletions(-)

diff --git a/llvm/lib/Support/UnicodeCaseFold.cpp b/llvm/lib/Support/UnicodeCaseFold.cpp
index 007c6261568ca..3e44ac5210f71 100644
--- a/llvm/lib/Support/UnicodeCaseFold.cpp
+++ b/llvm/lib/Support/UnicodeCaseFold.cpp
@@ -19,7 +19,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 26 characters
   if (C <= 0x005a)
     return C + 32;
-  // MICRO SIGN\n'
+  // MICRO SIGN
   if (C == 0x00b5)
     return 0x03bc;
   if (C < 0x00c0)
@@ -52,7 +52,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 23 characters
   if (C <= 0x0176)
     return C | 1;
-  // LATIN CAPITAL LETTER Y WITH DIAERESIS\n'
+  // LATIN CAPITAL LETTER Y WITH DIAERESIS
   if (C == 0x0178)
     return 0x00ff;
   if (C < 0x0179)
@@ -60,10 +60,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 3 characters
   if (C <= 0x017d && C % 2 == 1)
     return C + 1;
-  // LATIN SMALL LETTER LONG S\n'
+  // LATIN SMALL LETTER LONG S
   if (C == 0x017f)
     return 0x0073;
-  // LATIN CAPITAL LETTER B WITH HOOK\n'
+  // LATIN CAPITAL LETTER B WITH HOOK
   if (C == 0x0181)
     return 0x0253;
   if (C < 0x0182)
@@ -71,10 +71,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x0184)
     return C | 1;
-  // LATIN CAPITAL LETTER OPEN O\n'
+  // LATIN CAPITAL LETTER OPEN O
   if (C == 0x0186)
     return 0x0254;
-  // LATIN CAPITAL LETTER C WITH HOOK\n'
+  // LATIN CAPITAL LETTER C WITH HOOK
   if (C == 0x0187)
     return 0x0188;
   if (C < 0x0189)
@@ -82,43 +82,43 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x018a)
     return C + 205;
-  // LATIN CAPITAL LETTER D WITH TOPBAR\n'
+  // LATIN CAPITAL LETTER D WITH TOPBAR
   if (C == 0x018b)
     return 0x018c;
-  // LATIN CAPITAL LETTER REVERSED E\n'
+  // LATIN CAPITAL LETTER REVERSED E
   if (C == 0x018e)
     return 0x01dd;
-  // LATIN CAPITAL LETTER SCHWA\n'
+  // LATIN CAPITAL LETTER SCHWA
   if (C == 0x018f)
     return 0x0259;
-  // LATIN CAPITAL LETTER OPEN E\n'
+  // LATIN CAPITAL LETTER OPEN E
   if (C == 0x0190)
     return 0x025b;
-  // LATIN CAPITAL LETTER F WITH HOOK\n'
+  // LATIN CAPITAL LETTER F WITH HOOK
   if (C == 0x0191)
     return 0x0192;
-  // LATIN CAPITAL LETTER G WITH HOOK\n'
+  // LATIN CAPITAL LETTER G WITH HOOK
   if (C == 0x0193)
     return 0x0260;
-  // LATIN CAPITAL LETTER GAMMA\n'
+  // LATIN CAPITAL LETTER GAMMA
   if (C == 0x0194)
     return 0x0263;
-  // LATIN CAPITAL LETTER IOTA\n'
+  // LATIN CAPITAL LETTER IOTA
   if (C == 0x0196)
     return 0x0269;
-  // LATIN CAPITAL LETTER I WITH STROKE\n'
+  // LATIN CAPITAL LETTER I WITH STROKE
   if (C == 0x0197)
     return 0x0268;
-  // LATIN CAPITAL LETTER K WITH HOOK\n'
+  // LATIN CAPITAL LETTER K WITH HOOK
   if (C == 0x0198)
     return 0x0199;
-  // LATIN CAPITAL LETTER TURNED M\n'
+  // LATIN CAPITAL LETTER TURNED M
   if (C == 0x019c)
     return 0x026f;
-  // LATIN CAPITAL LETTER N WITH LEFT HOOK\n'
+  // LATIN CAPITAL LETTER N WITH LEFT HOOK
   if (C == 0x019d)
     return 0x0272;
-  // LATIN CAPITAL LETTER O WITH MIDDLE TILDE\n'
+  // LATIN CAPITAL LETTER O WITH MIDDLE TILDE
   if (C == 0x019f)
     return 0x0275;
   if (C < 0x01a0)
@@ -126,22 +126,22 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 3 characters
   if (C <= 0x01a4)
     return C | 1;
-  // LATIN LETTER YR\n'
+  // LATIN LETTER YR
   if (C == 0x01a6)
     return 0x0280;
-  // LATIN CAPITAL LETTER TONE TWO\n'
+  // LATIN CAPITAL LETTER TONE TWO
   if (C == 0x01a7)
     return 0x01a8;
-  // LATIN CAPITAL LETTER ESH\n'
+  // LATIN CAPITAL LETTER ESH
   if (C == 0x01a9)
     return 0x0283;
-  // LATIN CAPITAL LETTER T WITH HOOK\n'
+  // LATIN CAPITAL LETTER T WITH HOOK
   if (C == 0x01ac)
     return 0x01ad;
-  // LATIN CAPITAL LETTER T WITH RETROFLEX HOOK\n'
+  // LATIN CAPITAL LETTER T WITH RETROFLEX HOOK
   if (C == 0x01ae)
     return 0x0288;
-  // LATIN CAPITAL LETTER U WITH HORN\n'
+  // LATIN CAPITAL LETTER U WITH HORN
   if (C == 0x01af)
     return 0x01b0;
   if (C < 0x01b1)
@@ -154,7 +154,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x01b5 && C % 2 == 1)
     return C + 1;
-  // LATIN CAPITAL LETTER EZH\n'
+  // LATIN CAPITAL LETTER EZH
   if (C == 0x01b7)
     return 0x0292;
   if (C < 0x01b8)
@@ -162,19 +162,19 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x01bc && C % 4 == 0)
     return C + 1;
-  // LATIN CAPITAL LETTER DZ WITH CARON\n'
+  // LATIN CAPITAL LETTER DZ WITH CARON
   if (C == 0x01c4)
     return 0x01c6;
-  // LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON\n'
+  // LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON
   if (C == 0x01c5)
     return 0x01c6;
-  // LATIN CAPITAL LETTER LJ\n'
+  // LATIN CAPITAL LETTER LJ
   if (C == 0x01c7)
     return 0x01c9;
-  // LATIN CAPITAL LETTER L WITH SMALL LETTER J\n'
+  // LATIN CAPITAL LETTER L WITH SMALL LETTER J
   if (C == 0x01c8)
     return 0x01c9;
-  // LATIN CAPITAL LETTER NJ\n'
+  // LATIN CAPITAL LETTER NJ
   if (C == 0x01ca)
     return 0x01cc;
   if (C < 0x01cb)
@@ -187,7 +187,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 9 characters
   if (C <= 0x01ee)
     return C | 1;
-  // LATIN CAPITAL LETTER DZ\n'
+  // LATIN CAPITAL LETTER DZ
   if (C == 0x01f1)
     return 0x01f3;
   if (C < 0x01f2)
@@ -195,10 +195,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x01f4)
     return C | 1;
-  // LATIN CAPITAL LETTER HWAIR\n'
+  // LATIN CAPITAL LETTER HWAIR
   if (C == 0x01f6)
     return 0x0195;
-  // LATIN CAPITAL LETTER WYNN\n'
+  // LATIN CAPITAL LETTER WYNN
   if (C == 0x01f7)
     return 0x01bf;
   if (C < 0x01f8)
@@ -206,7 +206,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 20 characters
   if (C <= 0x021e)
     return C | 1;
-  // LATIN CAPITAL LETTER N WITH LONG RIGHT LEG\n'
+  // LATIN CAPITAL LETTER N WITH LONG RIGHT LEG
   if (C == 0x0220)
     return 0x019e;
   if (C < 0x0222)
@@ -214,28 +214,28 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 9 characters
   if (C <= 0x0232)
     return C | 1;
-  // LATIN CAPITAL LETTER A WITH STROKE\n'
+  // LATIN CAPITAL LETTER A WITH STROKE
   if (C == 0x023a)
     return 0x2c65;
-  // LATIN CAPITAL LETTER C WITH STROKE\n'
+  // LATIN CAPITAL LETTER C WITH STROKE
   if (C == 0x023b)
     return 0x023c;
-  // LATIN CAPITAL LETTER L WITH BAR\n'
+  // LATIN CAPITAL LETTER L WITH BAR
   if (C == 0x023d)
     return 0x019a;
-  // LATIN CAPITAL LETTER T WITH DIAGONAL STROKE\n'
+  // LATIN CAPITAL LETTER T WITH DIAGONAL STROKE
   if (C == 0x023e)
     return 0x2c66;
-  // LATIN CAPITAL LETTER GLOTTAL STOP\n'
+  // LATIN CAPITAL LETTER GLOTTAL STOP
   if (C == 0x0241)
     return 0x0242;
-  // LATIN CAPITAL LETTER B WITH STROKE\n'
+  // LATIN CAPITAL LETTER B WITH STROKE
   if (C == 0x0243)
     return 0x0180;
-  // LATIN CAPITAL LETTER U BAR\n'
+  // LATIN CAPITAL LETTER U BAR
   if (C == 0x0244)
     return 0x0289;
-  // LATIN CAPITAL LETTER TURNED V\n'
+  // LATIN CAPITAL LETTER TURNED V
   if (C == 0x0245)
     return 0x028c;
   if (C < 0x0246)
@@ -243,7 +243,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 5 characters
   if (C <= 0x024e)
     return C | 1;
-  // COMBINING GREEK YPOGEGRAMMENI\n'
+  // COMBINING GREEK YPOGEGRAMMENI
   if (C == 0x0345)
     return 0x03b9;
   if (C < 0x0370)
@@ -251,13 +251,13 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x0372)
     return C | 1;
-  // GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA\n'
+  // GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA
   if (C == 0x0376)
     return 0x0377;
-  // GREEK CAPITAL LETTER YOT\n'
+  // GREEK CAPITAL LETTER YOT
   if (C == 0x037f)
     return 0x03f3;
-  // GREEK CAPITAL LETTER ALPHA WITH TONOS\n'
+  // GREEK CAPITAL LETTER ALPHA WITH TONOS
   if (C == 0x0386)
     return 0x03ac;
   if (C < 0x0388)
@@ -265,7 +265,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 3 characters
   if (C <= 0x038a)
     return C + 37;
-  // GREEK CAPITAL LETTER OMICRON WITH TONOS\n'
+  // GREEK CAPITAL LETTER OMICRON WITH TONOS
   if (C == 0x038c)
     return 0x03cc;
   if (C < 0x038e)
@@ -283,22 +283,22 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 9 characters
   if (C <= 0x03ab)
     return C + 32;
-  // GREEK SMALL LETTER FINAL SIGMA\n'
+  // GREEK SMALL LETTER FINAL SIGMA
   if (C == 0x03c2)
     return 0x03c3;
-  // GREEK CAPITAL KAI SYMBOL\n'
+  // GREEK CAPITAL KAI SYMBOL
   if (C == 0x03cf)
     return 0x03d7;
-  // GREEK BETA SYMBOL\n'
+  // GREEK BETA SYMBOL
   if (C == 0x03d0)
     return 0x03b2;
-  // GREEK THETA SYMBOL\n'
+  // GREEK THETA SYMBOL
   if (C == 0x03d1)
     return 0x03b8;
-  // GREEK PHI SYMBOL\n'
+  // GREEK PHI SYMBOL
   if (C == 0x03d5)
     return 0x03c6;
-  // GREEK PI SYMBOL\n'
+  // GREEK PI SYMBOL
   if (C == 0x03d6)
     return 0x03c0;
   if (C < 0x03d8)
@@ -306,25 +306,25 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 12 characters
   if (C <= 0x03ee)
     return C | 1;
-  // GREEK KAPPA SYMBOL\n'
+  // GREEK KAPPA SYMBOL
   if (C == 0x03f0)
     return 0x03ba;
-  // GREEK RHO SYMBOL\n'
+  // GREEK RHO SYMBOL
   if (C == 0x03f1)
     return 0x03c1;
-  // GREEK CAPITAL THETA SYMBOL\n'
+  // GREEK CAPITAL THETA SYMBOL
   if (C == 0x03f4)
     return 0x03b8;
-  // GREEK LUNATE EPSILON SYMBOL\n'
+  // GREEK LUNATE EPSILON SYMBOL
   if (C == 0x03f5)
     return 0x03b5;
-  // GREEK CAPITAL LETTER SHO\n'
+  // GREEK CAPITAL LETTER SHO
   if (C == 0x03f7)
     return 0x03f8;
-  // GREEK CAPITAL LUNATE SIGMA SYMBOL\n'
+  // GREEK CAPITAL LUNATE SIGMA SYMBOL
   if (C == 0x03f9)
     return 0x03f2;
-  // GREEK CAPITAL LETTER SAN\n'
+  // GREEK CAPITAL LETTER SAN
   if (C == 0x03fa)
     return 0x03fb;
   if (C < 0x03fd)
@@ -352,7 +352,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 27 characters
   if (C <= 0x04be)
     return C | 1;
-  // CYRILLIC LETTER PALOCHKA\n'
+  // CYRILLIC LETTER PALOCHKA
   if (C == 0x04c0)
     return 0x04cf;
   if (C < 0x04c1)
@@ -385,13 +385,13 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 6 characters
   if (C <= 0x13fd)
     return C + -8;
-  // CYRILLIC SMALL LETTER ROUNDED VE\n'
+  // CYRILLIC SMALL LETTER ROUNDED VE
   if (C == 0x1c80)
     return 0x0432;
-  // CYRILLIC SMALL LETTER LONG-LEGGED DE\n'
+  // CYRILLIC SMALL LETTER LONG-LEGGED DE
   if (C == 0x1c81)
     return 0x0434;
-  // CYRILLIC SMALL LETTER NARROW O\n'
+  // CYRILLIC SMALL LETTER NARROW O
   if (C == 0x1c82)
     return 0x043e;
   if (C < 0x1c83)
@@ -399,19 +399,19 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x1c84)
     return C + -6210;
-  // CYRILLIC SMALL LETTER THREE-LEGGED TE\n'
+  // CYRILLIC SMALL LETTER THREE-LEGGED TE
   if (C == 0x1c85)
     return 0x0442;
-  // CYRILLIC SMALL LETTER TALL HARD SIGN\n'
+  // CYRILLIC SMALL LETTER TALL HARD SIGN
   if (C == 0x1c86)
     return 0x044a;
-  // CYRILLIC SMALL LETTER TALL YAT\n'
+  // CYRILLIC SMALL LETTER TALL YAT
   if (C == 0x1c87)
     return 0x0463;
-  // CYRILLIC SMALL LETTER UNBLENDED UK\n'
+  // CYRILLIC SMALL LETTER UNBLENDED UK
   if (C == 0x1c88)
     return 0xa64b;
-  // CYRILLIC CAPITAL LETTER TJE\n'
+  // CYRILLIC CAPITAL LETTER TJE
   if (C == 0x1c89)
     return 0x1c8a;
   if (C < 0x1c90)
@@ -429,10 +429,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 75 characters
   if (C <= 0x1e94)
     return C | 1;
-  // LATIN SMALL LETTER LONG S WITH DOT ABOVE\n'
+  // LATIN SMALL LETTER LONG S WITH DOT ABOVE
   if (C == 0x1e9b)
     return 0x1e61;
-  // LATIN CAPITAL LETTER SHARP S\n'
+  // LATIN CAPITAL LETTER SHARP S
   if (C == 0x1e9e)
     return 0x00df;
   if (C < 0x1ea0)
@@ -500,10 +500,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x1fbb)
     return C + -74;
-  // GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI\n'
+  // GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
   if (C == 0x1fbc)
     return 0x1fb3;
-  // GREEK PROSGEGRAMMENI\n'
+  // GREEK PROSGEGRAMMENI
   if (C == 0x1fbe)
     return 0x03b9;
   if (C < 0x1fc8)
@@ -511,10 +511,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 4 characters
   if (C <= 0x1fcb)
     return C + -86;
-  // GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI\n'
+  // GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
   if (C == 0x1fcc)
     return 0x1fc3;
-  // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA\n'
+  // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
   if (C == 0x1fd3)
     return 0x0390;
   if (C < 0x1fd8)
@@ -527,7 +527,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x1fdb)
     return C + -100;
-  // GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA\n'
+  // GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
   if (C == 0x1fe3)
     return 0x03b0;
   if (C < 0x1fe8)
@@ -540,7 +540,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x1feb)
     return C + -112;
-  // GREEK CAPITAL LETTER RHO WITH DASIA\n'
+  // GREEK CAPITAL LETTER RHO WITH DASIA
   if (C == 0x1fec)
     return 0x1fe5;
   if (C < 0x1ff8)
@@ -553,19 +553,19 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0x1ffb)
     return C + -126;
-  // GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI\n'
+  // GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
   if (C == 0x1ffc)
     return 0x1ff3;
-  // OHM SIGN\n'
+  // OHM SIGN
   if (C == 0x2126)
     return 0x03c9;
-  // KELVIN SIGN\n'
+  // KELVIN SIGN
   if (C == 0x212a)
     return 0x006b;
-  // ANGSTROM SIGN\n'
+  // ANGSTROM SIGN
   if (C == 0x212b)
     return 0x00e5;
-  // TURNED CAPITAL F\n'
+  // TURNED CAPITAL F
   if (C == 0x2132)
     return 0x214e;
   if (C < 0x2160)
@@ -573,7 +573,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 16 characters
   if (C <= 0x216f)
     return C + 16;
-  // ROMAN NUMERAL REVERSED ONE HUNDRED\n'
+  // ROMAN NUMERAL REVERSED ONE HUNDRED
   if (C == 0x2183)
     return 0x2184;
   if (C < 0x24b6)
@@ -586,16 +586,16 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 48 characters
   if (C <= 0x2c2f)
     return C + 48;
-  // LATIN CAPITAL LETTER L WITH DOUBLE BAR\n'
+  // LATIN CAPITAL LETTER L WITH DOUBLE BAR
   if (C == 0x2c60)
     return 0x2c61;
-  // LATIN CAPITAL LETTER L WITH MIDDLE TILDE\n'
+  // LATIN CAPITAL LETTER L WITH MIDDLE TILDE
   if (C == 0x2c62)
     return 0x026b;
-  // LATIN CAPITAL LETTER P WITH STROKE\n'
+  // LATIN CAPITAL LETTER P WITH STROKE
   if (C == 0x2c63)
     return 0x1d7d;
-  // LATIN CAPITAL LETTER R WITH TAIL\n'
+  // LATIN CAPITAL LETTER R WITH TAIL
   if (C == 0x2c64)
     return 0x027d;
   if (C < 0x2c67)
@@ -603,16 +603,16 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 3 characters
   if (C <= 0x2c6b && C % 2 == 1)
     return C + 1;
-  // LATIN CAPITAL LETTER ALPHA\n'
+  // LATIN CAPITAL LETTER ALPHA
   if (C == 0x2c6d)
     return 0x0251;
-  // LATIN CAPITAL LETTER M WITH HOOK\n'
+  // LATIN CAPITAL LETTER M WITH HOOK
   if (C == 0x2c6e)
     return 0x0271;
-  // LATIN CAPITAL LETTER TURNED A\n'
+  // LATIN CAPITAL LETTER TURNED A
   if (C == 0x2c6f)
     return 0x0250;
-  // LATIN CAPITAL LETTER TURNED ALPHA\n'
+  // LATIN CAPITAL LETTER TURNED ALPHA
   if (C == 0x2c70)
     return 0x0252;
   if (C < 0x2c72)
@@ -665,7 +665,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0xa77b && C % 2 == 1)
     return C + 1;
-  // LATIN CAPITAL LETTER INSULAR G\n'
+  // LATIN CAPITAL LETTER INSULAR G
   if (C == 0xa77d)
     return 0x1d79;
   if (C < 0xa77e)
@@ -673,10 +673,10 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 5 characters
   if (C <= 0xa786)
     return C | 1;
-  // LATIN CAPITAL LETTER SALTILLO\n'
+  // LATIN CAPITAL LETTER SALTILLO
   if (C == 0xa78b)
     return 0xa78c;
-  // LATIN CAPITAL LETTER TURNED H\n'
+  // LATIN CAPITAL LETTER TURNED H
   if (C == 0xa78d)
     return 0x0265;
   if (C < 0xa790)
@@ -689,31 +689,31 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 10 characters
   if (C <= 0xa7a8)
     return C | 1;
-  // LATIN CAPITAL LETTER H WITH HOOK\n'
+  // LATIN CAPITAL LETTER H WITH HOOK
   if (C == 0xa7aa)
     return 0x0266;
-  // LATIN CAPITAL LETTER REVERSED OPEN E\n'
+  // LATIN CAPITAL LETTER REVERSED OPEN E
   if (C == 0xa7ab)
     return 0x025c;
-  // LATIN CAPITAL LETTER SCRIPT G\n'
+  // LATIN CAPITAL LETTER SCRIPT G
   if (C == 0xa7ac)
     return 0x0261;
-  // LATIN CAPITAL LETTER L WITH BELT\n'
+  // LATIN CAPITAL LETTER L WITH BELT
   if (C == 0xa7ad)
     return 0x026c;
-  // LATIN CAPITAL LETTER SMALL CAPITAL I\n'
+  // LATIN CAPITAL LETTER SMALL CAPITAL I
   if (C == 0xa7ae)
     return 0x026a;
-  // LATIN CAPITAL LETTER TURNED K\n'
+  // LATIN CAPITAL LETTER TURNED K
   if (C == 0xa7b0)
     return 0x029e;
-  // LATIN CAPITAL LETTER TURNED T\n'
+  // LATIN CAPITAL LETTER TURNED T
   if (C == 0xa7b1)
     return 0x0287;
-  // LATIN CAPITAL LETTER J WITH CROSSED-TAIL\n'
+  // LATIN CAPITAL LETTER J WITH CROSSED-TAIL
   if (C == 0xa7b2)
     return 0x029d;
-  // LATIN CAPITAL LETTER CHI\n'
+  // LATIN CAPITAL LETTER CHI
   if (C == 0xa7b3)
     return 0xab53;
   if (C < 0xa7b4)
@@ -721,13 +721,13 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 8 characters
   if (C <= 0xa7c2)
     return C | 1;
-  // LATIN CAPITAL LETTER C WITH PALATAL HOOK\n'
+  // LATIN CAPITAL LETTER C WITH PALATAL HOOK
   if (C == 0xa7c4)
     return 0xa794;
-  // LATIN CAPITAL LETTER S WITH HOOK\n'
+  // LATIN CAPITAL LETTER S WITH HOOK
   if (C == 0xa7c5)
     return 0x0282;
-  // LATIN CAPITAL LETTER Z WITH PALATAL HOOK\n'
+  // LATIN CAPITAL LETTER Z WITH PALATAL HOOK
   if (C == 0xa7c6)
     return 0x1d8e;
   if (C < 0xa7c7)
@@ -735,7 +735,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0xa7c9 && C % 2 == 1)
     return C + 1;
-  // LATIN CAPITAL LETTER RAMS HORN\n'
+  // LATIN CAPITAL LETTER RAMS HORN
   if (C == 0xa7cb)
     return 0x0264;
   if (C < 0xa7cc)
@@ -743,7 +743,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 8 characters
   if (C <= 0xa7da)
     return C | 1;
-  // LATIN CAPITAL LETTER LAMBDA WITH STROKE\n'
+  // LATIN CAPITAL LETTER LAMBDA WITH STROKE
   if (C == 0xa7dc)
     return 0x019b;
   if (C < 0xa7dd)
@@ -751,7 +751,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 2 characters
   if (C <= 0xa7e2 && C % 5 == 3)
     return C + -42342;
-  // LATIN CAPITAL LETTER REVERSED HALF H\n'
+  // LATIN CAPITAL LETTER REVERSED HALF H
   if (C == 0xa7f5)
     return 0xa7f6;
   if (C < 0xab6c)
@@ -764,7 +764,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 80 characters
   if (C <= 0xabbf)
     return C + -38864;
-  // LATIN SMALL LIGATURE LONG S T\n'
+  // LATIN SMALL LIGATURE LONG S T
   if (C == 0xfb05)
     return 0xfb06;
   if (C < 0xff21)
@@ -852,7 +852,7 @@ int llvm::sys::unicode::foldCharSimple(int C) {
   // 7 characters
   if (C <= 0x1df7e)
     return C | 1;
-  // LATIN SMALL LIGATURE LONG S WITH DESCENDER S\n'
+  // LATIN SMALL LIGATURE LONG S WITH DESCENDER S
   if (C == 0x1df95)
     return 0x00df;
   if (C < 0x1e900)

>From 3c63d3bbcb4d26226448335896ac733440f101f6 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Thu, 21 May 2026 15:51:48 -0500
Subject: [PATCH 08/33] Update UnicodeNameMappingGenerator.cpp

---
 llvm/utils/UnicodeData/UnicodeNameMappingGenerator.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/utils/UnicodeData/UnicodeNameMappingGenerator.cpp b/llvm/utils/UnicodeData/UnicodeNameMappingGenerator.cpp
index a42913fa537ed..85714a7934c71 100644
--- a/llvm/utils/UnicodeData/UnicodeNameMappingGenerator.cpp
+++ b/llvm/utils/UnicodeData/UnicodeNameMappingGenerator.cpp
@@ -356,9 +356,9 @@ int main(int argc, char **argv) {
          "Usage: %s UnicodeData.txt NameAliases.txt output\n\n",
          argv[0]);
   printf("NameAliases.txt can be found at "
-         "https://unicode.org/Public/15.1.0/ucd/NameAliases.txt\n"
+         "https://unicode.org/Public/18.0.0/ucd/NameAliases.txt\n"
          "UnicodeData.txt can be found at "
-         "https://unicode.org/Public/15.1.0/ucd/UnicodeData.txt\n\n");
+         "https://unicode.org/Public/18.0.0/ucd/UnicodeData.txt\n\n");
 
   if (argc != 4)
     return EXIT_FAILURE;

>From 4b430c7fd930e7ba1291d52d12054faa8f9d39a8 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Fri, 22 May 2026 06:03:06 -0500
Subject: [PATCH 09/33] Update generate_escaped_output_table.py

---
 libcxx/utils/generate_escaped_output_table.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/utils/generate_escaped_output_table.py b/libcxx/utils/generate_escaped_output_table.py
index dd7173201b466..20bf572dc6dbe 100755
--- a/libcxx/utils/generate_escaped_output_table.py
+++ b/libcxx/utils/generate_escaped_output_table.py
@@ -105,7 +105,7 @@ def compactPropertyRanges(input: list[PropertyRange]) -> list[PropertyRange]:
 /// - Unassigned.
 ///
 /// The data is generated from
-/// - https://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedGeneralCategory.txt
+/// - https://www.unicode.org/Public/UCD/18.0.0/ucd/extracted/DerivedGeneralCategory.txt
 ///
 /// The table is similar to the table
 ///  __extended_grapheme_custer_property_boundary::__entries

>From a0ed088af5b5516e6ccbd02f1f1238a982e3caf7 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Fri, 22 May 2026 06:11:56 -0500
Subject: [PATCH 10/33] Update entries of last two blocks for 18.0.0

---
 libcxx/utils/generate_escaped_output_table.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/utils/generate_escaped_output_table.py b/libcxx/utils/generate_escaped_output_table.py
index 20bf572dc6dbe..d043dfb1a681c 100755
--- a/libcxx/utils/generate_escaped_output_table.py
+++ b/libcxx/utils/generate_escaped_output_table.py
@@ -330,10 +330,10 @@ def generate_data_tables() -> str:
     # The exact entries may differ between Unicode versions. When these numbers
     # change the test needs to be updated too.
     #   libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
-    assert (data[-2].lower) == 0x323B0
-    assert (data[-2].upper) == 0xE00FF
-    assert (data[-1].lower) == 0xE01F0
-    assert (data[-1].upper) == 0x10FFFF
+    assert (data[-2].lower) == 0x3FC40, hex(data[-2].lower)
+    assert (data[-2].upper) == 0xE00FF, hex(data[-2].upper)
+    assert (data[-1].lower) == 0xE01F0, hex(data[-1].lower)
+    assert (data[-1].upper) == 0x10FFFF, hex(data[-1].upper)
 
     return "\n".join(
         [

>From 9fb62d6f4a0e3d479656727d4cc9c6e722cd3731 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Fri, 22 May 2026 06:13:33 -0500
Subject: [PATCH 11/33] Update excluded range

---
 .../format.string/format.string.std/escaped_output.pass.cpp     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
index d24db360173b1..39a21fbeb9778 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
@@ -73,7 +73,7 @@ inline constexpr int C  = Cc + Cf + Cs + Co + Cn;
 // [323B0, 10FFFF] use a hand-crafted algorithm.
 //
 // This means a number of entries are omitted
-inline constexpr int excluded = ((0x10FFFF - 0x323B0) + 1) - 240;
+inline constexpr int excluded = ((0x10FFFF - 0x3FC40) + 1) - 240;
 
 inline constexpr int entries = Z + C - excluded;
 

>From 0f8e0494df9c3931549fa03dc13f0a9adfc16ad3 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Fri, 22 May 2026 06:14:43 -0500
Subject: [PATCH 12/33] Update escaped_output.pass.cpp

---
 .../format.string/format.string.std/escaped_output.pass.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
index 39a21fbeb9778..3c5502652866b 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
@@ -66,11 +66,11 @@ inline constexpr int C  = Cc + Cf + Cs + Co + Cn;
 // 100000..10FFFD; Co # [65534] <private-use-100000>..<private-use-10FFFD>
 // 10FFFE..10FFFF; Cn #   [2] <noncharacter-10FFFE>..<noncharacter-10FFFF>
 //
-// It can be observed all entries in the range 323B0..10FFFF are in the
+// It can be observed all entries in the range 3FC40..10FFFF are in the
 // categories Cf, Co, Cn, except a small range with the property Mn.
 // In order to reduce the size of the table only the entires in the range
-// [0000, 323B0) are stored in the table. The entries in the range
-// [323B0, 10FFFF] use a hand-crafted algorithm.
+// [0000, 3FC40) are stored in the table. The entries in the range
+// [3FC40, 10FFFF] use a hand-crafted algorithm.
 //
 // This means a number of entries are omitted
 inline constexpr int excluded = ((0x10FFFF - 0x3FC40) + 1) - 240;

>From c9725f605b452ddd4023273c64ce3df502b48044 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Fri, 22 May 2026 07:47:12 -0500
Subject: [PATCH 13/33] Account for new Unicode property ranges in 18

---
 .../format.string.std/escaped_output.pass.cpp          | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
index 3c5502652866b..ee1fae71f66ea 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
@@ -48,13 +48,15 @@ inline constexpr int Cc = 65;
 inline constexpr int Cf = 170;
 inline constexpr int Cs = 2'048;
 inline constexpr int Co = 137'468;
-inline constexpr int Cn = 819'533;
+inline constexpr int Cn = 801'683;
 inline constexpr int C  = Cc + Cf + Cs + Co + Cn;
 
 // This is the final part of the Unicode properties table:
 //
-// 31350..323AF  ; Lo # [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF
-// 323B0..E0000  ; Cn # [711761] <reserved-323B0>..<reserved-E0000>
+// 31350..33479  ; Lo # [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479
+// 3347A..3CFFF  ; Cn # [39814] <reserved-3347A>..<reserved-3CFFF>
+// 3D000..3FC3F  ; Lo # [11328] SEAL CHARACTER-3D000..SEAL CHARACTER-3FC3F
+// 3FC40..E0000  ; Cn # [656321] <reserved-3FC40>..<reserved-E0000>
 // E0001         ; Cf #       LANGUAGE TAG
 // E0002..E001F  ; Cn #  [30] <reserved-E0002>..<reserved-E001F>
 // E0020..E007F  ; Cf #  [96] TAG SPACE..CANCEL TAG
@@ -73,7 +75,7 @@ inline constexpr int C  = Cc + Cf + Cs + Co + Cn;
 // [3FC40, 10FFFF] use a hand-crafted algorithm.
 //
 // This means a number of entries are omitted
-inline constexpr int excluded = ((0x10FFFF - 0x3FC40) + 1) - 240;
+inline constexpr int excluded = ((0x10FFFF - 0x3347A) + 1) - 240;
 
 inline constexpr int entries = Z + C - excluded;
 

>From 9929e27d56701237731b35e57c80134f558ec891 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Fri, 22 May 2026 08:24:25 -0500
Subject: [PATCH 14/33] Final corrected excluded codepoint is 0x3fc40

---
 .../format.string/format.string.std/escaped_output.pass.cpp     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
index ee1fae71f66ea..9e51b064e6f5a 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
@@ -75,7 +75,7 @@ inline constexpr int C  = Cc + Cf + Cs + Co + Cn;
 // [3FC40, 10FFFF] use a hand-crafted algorithm.
 //
 // This means a number of entries are omitted
-inline constexpr int excluded = ((0x10FFFF - 0x3347A) + 1) - 240;
+inline constexpr int excluded = ((0x10FFFF - 0x3FC40) + 1) - 240;
 
 inline constexpr int entries = Z + C - excluded;
 

>From 56fb5dc177769b287e65f05791691fa9d41d26a0 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Fri, 22 May 2026 08:47:39 -0500
Subject: [PATCH 15/33] Update asserts with correct ranges

---
 .../format.string/format.string.std/escaped_output.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
index 9e51b064e6f5a..5fadf537b50d8 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
@@ -90,10 +90,10 @@ static constexpr int count_entries() {
 static_assert(count_entries() == entries);
 
 int main(int, char**) {
-  for (char32_t c = 0x31350; c <= 0x323AF; ++c) // 31350..323AF  ; Lo # [4192]
+  for (char32_t c = 0x31350; c <= 0x33479; ++c) // 31350..33479  ; Lo # [8490]
     assert(std::__escaped_output_table::__needs_escape(c) == false);
 
-  for (char32_t c = 0x323B0; c <= 0xE00FF; ++c) // 323B0..E00FF ; C
+  for (char32_t c = 0x3347A; c <= 0xE00FF; ++c) // 3347A..E00FF ; C
     assert(std::__escaped_output_table::__needs_escape(c) == true);
 
   for (char32_t c = 0xE0100; c <= 0xE01EF; ++c) // E0100..E01EF  ; Mn # [240]

>From 5d244300f8b05059bd5fc9b60d18efa158a18f6c Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Fri, 22 May 2026 09:07:02 -0500
Subject: [PATCH 16/33] Use 0x3FC40 in an assert

---
 .../format.string/format.string.std/escaped_output.pass.cpp     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
index 5fadf537b50d8..f6b6325992ce0 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
@@ -93,7 +93,7 @@ int main(int, char**) {
   for (char32_t c = 0x31350; c <= 0x33479; ++c) // 31350..33479  ; Lo # [8490]
     assert(std::__escaped_output_table::__needs_escape(c) == false);
 
-  for (char32_t c = 0x3347A; c <= 0xE00FF; ++c) // 3347A..E00FF ; C
+  for (char32_t c = 0x3FC40; c <= 0xE00FF; ++c) // 3347A..E00FF ; C
     assert(std::__escaped_output_table::__needs_escape(c) == true);
 
   for (char32_t c = 0xE0100; c <= 0xE01EF; ++c) // E0100..E01EF  ; Mn # [240]

>From 33ab7072812e23a806f42af1d79e931f53dbbe35 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Fri, 19 Jun 2026 16:07:12 -0400
Subject: [PATCH 17/33] Update URL for Unicode data source

---
 libcxx/utils/generate_escaped_output_table.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/utils/generate_escaped_output_table.py b/libcxx/utils/generate_escaped_output_table.py
index d043dfb1a681c..744835129161d 100755
--- a/libcxx/utils/generate_escaped_output_table.py
+++ b/libcxx/utils/generate_escaped_output_table.py
@@ -105,7 +105,7 @@ def compactPropertyRanges(input: list[PropertyRange]) -> list[PropertyRange]:
 /// - Unassigned.
 ///
 /// The data is generated from
-/// - https://www.unicode.org/Public/UCD/18.0.0/ucd/extracted/DerivedGeneralCategory.txt
+/// - https://www.unicode.org/Public/18.0.0/ucd/extracted/DerivedGeneralCategory.txt
 ///
 /// The table is similar to the table
 ///  __extended_grapheme_custer_property_boundary::__entries

>From 574455c7ef7f96fa84a1cf5108bb1185f854d8d4 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Fri, 19 Jun 2026 16:08:52 -0400
Subject: [PATCH 18/33] Update URL for Unicode case folding database

---
 llvm/lib/Support/UnicodeCaseFold.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Support/UnicodeCaseFold.cpp b/llvm/lib/Support/UnicodeCaseFold.cpp
index 3e44ac5210f71..5f1b1be013bd7 100644
--- a/llvm/lib/Support/UnicodeCaseFold.cpp
+++ b/llvm/lib/Support/UnicodeCaseFold.cpp
@@ -2,11 +2,11 @@
 //
 // This file was generated by utils/unicode-case-fold.py from the Unicode
 // case folding database at
-//    https://www.unicode.org/Public/draft/ucd/CaseFolding.txt
+//    https://www.unicode.org/Public/18.0.0/ucd/CaseFolding.txt
 //
 // To regenerate this file, run:
 //   utils/unicode-case-fold.py \
-//     "https://www.unicode.org/Public/draft/ucd/CaseFolding.txt" \
+//     "https://www.unicode.org/Public/18.0.0/ucd/CaseFolding.txt" \
 //     > lib/Support/UnicodeCaseFold.cpp
 //
 //===----------------------------------------------------------------------===//

>From 30f86e19143d7e47587d1380dcaeff7cafaaa3fc Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Fri, 19 Jun 2026 16:18:25 -0400
Subject: [PATCH 19/33] Refactor conditional statements for readability

---
 llvm/utils/unicode-case-fold.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/llvm/utils/unicode-case-fold.py b/llvm/utils/unicode-case-fold.py
index cfa5b279facf3..61d113b26835e 100755
--- a/llvm/utils/unicode-case-fold.py
+++ b/llvm/utils/unicode-case-fold.py
@@ -37,11 +37,13 @@ def mappings(f):
         if not m:
             continue
         group_1 = m.group(1)
-        if "b" in group_1: group_1 = group_1[2:]
+        if "b" in group_1:
+            group_1 = group_1[2:]
         from_char = int(group_1, 16)
         to_char = int(m.group(2), 16)
         group_3 = m.group(3)
-        if "\\n" in group_3: group_3 = group_3[:-3]
+        if "\\n" in group_3:
+            group_3 = group_3[:-3]
         from_name = group_3
 
         if from_char <= previous_from:

>From 7ffe648f81c20f12a693ef126b22677158d6e7a2 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Fri, 19 Jun 2026 16:20:19 -0400
Subject: [PATCH 20/33] Fix assertion for lower bound in escaped output table

---
 libcxx/utils/generate_escaped_output_table.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/utils/generate_escaped_output_table.py b/libcxx/utils/generate_escaped_output_table.py
index 744835129161d..aa918a6977371 100755
--- a/libcxx/utils/generate_escaped_output_table.py
+++ b/libcxx/utils/generate_escaped_output_table.py
@@ -330,7 +330,7 @@ def generate_data_tables() -> str:
     # The exact entries may differ between Unicode versions. When these numbers
     # change the test needs to be updated too.
     #   libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
-    assert (data[-2].lower) == 0x3FC40, hex(data[-2].lower)
+    assert (data[-2].lower) == 0x323B0, hex(data[-2].lower)
     assert (data[-2].upper) == 0xE00FF, hex(data[-2].upper)
     assert (data[-1].lower) == 0xE01F0, hex(data[-1].lower)
     assert (data[-1].upper) == 0x10FFFF, hex(data[-1].upper)

>From f18105fdd75bd2820466ddd142193514fa9f8f07 Mon Sep 17 00:00:00 2001
From: unknown <lol>
Date: Fri, 19 Jun 2026 19:30:38 -0400
Subject: [PATCH 21/33] Update UCD to 18.0.0

---
 .../data/unicode/DerivedCoreProperties.txt    | 1062 +++++++---
 .../data/unicode/DerivedGeneralCategory.txt   |  366 ++--
 libcxx/utils/data/unicode/EastAsianWidth.txt  |  175 +-
 .../data/unicode/GraphemeBreakProperty.txt    |   40 +-
 .../utils/data/unicode/GraphemeBreakTest.txt  | 1824 +++++++----------
 libcxx/utils/data/unicode/emoji-data.txt      |  173 +-
 6 files changed, 2044 insertions(+), 1596 deletions(-)

diff --git a/libcxx/utils/data/unicode/DerivedCoreProperties.txt b/libcxx/utils/data/unicode/DerivedCoreProperties.txt
index 1075638f1a654..08b1c3b594ac6 100644
--- a/libcxx/utils/data/unicode/DerivedCoreProperties.txt
+++ b/libcxx/utils/data/unicode/DerivedCoreProperties.txt
@@ -1,6 +1,6 @@
-# DerivedCoreProperties-16.0.0.txt
-# Date: 2024-05-31, 18:09:32 GMT
-# © 2024 Unicode®, Inc.
+# DerivedCoreProperties-18.0.0.txt
+# Date: 2026-05-12, 22:09:55 GMT
+# © 2026 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
 # For terms of use and license, see https://www.unicode.org/terms_of_use.html
 #
@@ -178,6 +178,8 @@ FF5E          ; Math # Sm       FULLWIDTH TILDE
 FFE2          ; Math # Sm       FULLWIDTH NOT SIGN
 FFE9..FFEC    ; Math # Sm   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW
 10D8E..10D8F  ; Math # Sm   [2] GARAY PLUS SIGN..GARAY MINUS SIGN
+1CEDD..1CEDF  ; Math # Sm   [3] SQUARE ROOT OF SQUARE ROOT..SQUARE ROOT OF SQUARE ROOT OF SQUARE ROOT OF SQUARE ROOT
+1CEF0..1CEFD  ; Math # Sm  [14] MEDIUM SMALL WHITE CIRCLE WITH HORIZONTAL BAR..HYPERBOLA
 1D400..1D454  ; Math # L&  [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G
 1D456..1D49C  ; Math # L&  [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A
 1D49E..1D49F  ; Math # L&   [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D
@@ -196,7 +198,7 @@ FFE9..FFEC    ; Math # Sm   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A
 1D540..1D544  ; Math # L&   [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
 1D546         ; Math # L&       MATHEMATICAL DOUBLE-STRUCK CAPITAL O
 1D54A..1D550  ; Math # L&   [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
-1D552..1D6A5  ; Math # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D552..1D6A6  ; Math # L& [341] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL LIGATURE LONG S WITH DESCENDER S
 1D6A8..1D6C0  ; Math # L&  [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
 1D6C1         ; Math # Sm       MATHEMATICAL BOLD NABLA
 1D6C2..1D6DA  ; Math # L&  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
@@ -219,6 +221,7 @@ FFE9..FFEC    ; Math # Sm   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A
 1D7C3         ; Math # Sm       MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL
 1D7C4..1D7CB  ; Math # L&   [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA
 1D7CE..1D7FF  ; Math # Nd  [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE
+1DB00..1DB1C  ; Math # Sm  [29] LEIBNIZIAN EQUALS SIGN..FACIT SYMBOL
 1EE00..1EE03  ; Math # Lo   [4] ARABIC MATHEMATICAL ALEF..ARABIC MATHEMATICAL DAL
 1EE05..1EE1F  ; Math # Lo  [27] ARABIC MATHEMATICAL WAW..ARABIC MATHEMATICAL DOTLESS QAF
 1EE21..1EE22  ; Math # Lo   [2] ARABIC MATHEMATICAL INITIAL BEH..ARABIC MATHEMATICAL INITIAL JEEM
@@ -253,8 +256,9 @@ FFE9..FFEC    ; Math # Sm   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A
 1EEA5..1EEA9  ; Math # Lo   [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH
 1EEAB..1EEBB  ; Math # Lo  [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN
 1EEF0..1EEF1  ; Math # Sm   [2] ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL..ARABIC MATHEMATICAL OPERATOR HAH WITH DAL
+1F8D0..1F8D8  ; Math # Sm   [9] LONG RIGHTWARDS ARROW OVER LONG LEFTWARDS ARROW..LONG LEFT RIGHT ARROW WITH DEPENDENT LOBE
 
-# Total code points: 2312
+# Total code points: 2368
 
 # ================================================
 
@@ -273,8 +277,8 @@ FFE9..FFEC    ; Math # Sm   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A
 01BC..01BF    ; Alphabetic # L&   [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN
 01C0..01C3    ; Alphabetic # Lo   [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK
 01C4..0293    ; Alphabetic # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL
-0294          ; Alphabetic # Lo       LATIN LETTER GLOTTAL STOP
-0295..02AF    ; Alphabetic # L&  [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+0294..0295    ; Alphabetic # Lo   [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE
+0296..02AF    ; Alphabetic # L&  [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
 02B0..02C1    ; Alphabetic # Lm  [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP
 02C6..02D1    ; Alphabetic # Lm  [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON
 02E0..02E4    ; Alphabetic # Lm   [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
@@ -296,13 +300,14 @@ FFE9..FFEC    ; Math # Sm   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A
 03F7..0481    ; Alphabetic # L& [139] GREEK CAPITAL LETTER SHO..CYRILLIC SMALL LETTER KOPPA
 048A..052F    ; Alphabetic # L& [166] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER EL WITH DESCENDER
 0531..0556    ; Alphabetic # L&  [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH
-0559          ; Alphabetic # Lm       ARMENIAN MODIFIER LETTER LEFT HALF RING
+0558..0559    ; Alphabetic # Lm   [2] MODIFIER LETTER ARMENIAN SMALL EH..ARMENIAN MODIFIER LETTER LEFT HALF RING
 0560..0588    ; Alphabetic # L&  [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE
+058B..058C    ; Alphabetic # Lm   [2] MODIFIER LETTER ARMENIAN SMALL INI..MODIFIER LETTER ARMENIAN SMALL YI
 05B0..05BD    ; Alphabetic # Mn  [14] HEBREW POINT SHEVA..HEBREW POINT METEG
 05BF          ; Alphabetic # Mn       HEBREW POINT RAFE
 05C1..05C2    ; Alphabetic # Mn   [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
 05C4..05C5    ; Alphabetic # Mn   [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
-05C7          ; Alphabetic # Mn       HEBREW POINT QAMATS QATAN
+05C7..05C9    ; Alphabetic # Mn   [3] HEBREW POINT QAMATS QATAN..HEBREW POINT DAGESH HAZAQ MUDGASH
 05D0..05EA    ; Alphabetic # Lo  [27] HEBREW LETTER ALEF..HEBREW LETTER TAV
 05EF..05F2    ; Alphabetic # Lo   [4] HEBREW YOD TRIANGLE..HEBREW LIGATURE YIDDISH DOUBLE YOD
 0610..061A    ; Alphabetic # Mn  [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
@@ -344,7 +349,7 @@ FFE9..FFEC    ; Math # Sm   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A
 0840..0858    ; Alphabetic # Lo  [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN
 0860..086A    ; Alphabetic # Lo  [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA
 0870..0887    ; Alphabetic # Lo  [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT
-0889..088E    ; Alphabetic # Lo   [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL
+0889..088F    ; Alphabetic # Lo   [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE
 0897          ; Alphabetic # Mn       ARABIC PEPET
 08A0..08C8    ; Alphabetic # Lo  [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF
 08C9          ; Alphabetic # Lm       ARABIC SMALL FARSI YEH
@@ -477,7 +482,7 @@ FFE9..FFEC    ; Math # Sm   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A
 0C4A..0C4C    ; Alphabetic # Mn   [3] TELUGU VOWEL SIGN O..TELUGU VOWEL SIGN AU
 0C55..0C56    ; Alphabetic # Mn   [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
 0C58..0C5A    ; Alphabetic # Lo   [3] TELUGU LETTER TSA..TELUGU LETTER RRRA
-0C5D          ; Alphabetic # Lo       TELUGU LETTER NAKAARA POLLU
+0C5C..0C5D    ; Alphabetic # Lo   [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU
 0C60..0C61    ; Alphabetic # Lo   [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
 0C62..0C63    ; Alphabetic # Mn   [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
 0C80          ; Alphabetic # Lo       KANNADA SIGN SPACING CANDRABINDU
@@ -497,7 +502,7 @@ FFE9..FFEC    ; Math # Sm   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A
 0CCA..0CCB    ; Alphabetic # Mc   [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
 0CCC          ; Alphabetic # Mn       KANNADA VOWEL SIGN AU
 0CD5..0CD6    ; Alphabetic # Mc   [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
-0CDD..0CDE    ; Alphabetic # Lo   [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA
+0CDC..0CDE    ; Alphabetic # Lo   [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA
 0CE0..0CE1    ; Alphabetic # Lo   [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
 0CE2..0CE3    ; Alphabetic # Mn   [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL
 0CF1..0CF2    ; Alphabetic # Lo   [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA
@@ -748,7 +753,7 @@ FFE9..FFEC    ; Math # Sm   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS A
 1FF6..1FFC    ; Alphabetic # L&   [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
 2071          ; Alphabetic # Lm       SUPERSCRIPT LATIN SMALL LETTER I
 207F          ; Alphabetic # Lm       SUPERSCRIPT LATIN SMALL LETTER N
-2090..209C    ; Alphabetic # Lm  [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T
+208F..209F    ; Alphabetic # Lm  [17] MODIFIER LETTER HIGH AND LOW VERTICAL LINE..LATIN SUBSCRIPT SMALL LETTER Z
 2102          ; Alphabetic # L&       DOUBLE-STRUCK CAPITAL C
 2107          ; Alphabetic # L&       EULER CONSTANT
 210A..2113    ; Alphabetic # L&  [10] SCRIPT SMALL G..SCRIPT SMALL L
@@ -833,11 +838,9 @@ A771..A787    ; Alphabetic # L&  [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER
 A788          ; Alphabetic # Lm       MODIFIER LETTER LOW CIRCUMFLEX ACCENT
 A78B..A78E    ; Alphabetic # L&   [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT
 A78F          ; Alphabetic # Lo       LATIN LETTER SINOLOGICAL DOT
-A790..A7CD    ; Alphabetic # L&  [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE
-A7D0..A7D1    ; Alphabetic # L&   [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G
-A7D3          ; Alphabetic # L&       LATIN SMALL LETTER DOUBLE THORN
-A7D5..A7DC    ; Alphabetic # L&   [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE
-A7F2..A7F4    ; Alphabetic # Lm   [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
+A790..A7DD    ; Alphabetic # L&  [78] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2          ; Alphabetic # L&       LATIN CAPITAL LETTER R WITH LONG LEG
+A7F1..A7F4    ; Alphabetic # Lm   [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q
 A7F5..A7F6    ; Alphabetic # L&   [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H
 A7F7          ; Alphabetic # Lo       LATIN EPIGRAPHIC LETTER SIDEWAYS I
 A7F8..A7F9    ; Alphabetic # Lm   [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
@@ -926,6 +929,7 @@ AB30..AB5A    ; Alphabetic # L&  [43] LATIN SMALL LETTER BARRED ALPHA..LATIN SMA
 AB5C..AB5F    ; Alphabetic # Lm   [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK
 AB60..AB68    ; Alphabetic # L&   [9] LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER TURNED R WITH MIDDLE TILDE
 AB69          ; Alphabetic # Lm       MODIFIER LETTER SMALL TURNED W
+AB6C..AB6D    ; Alphabetic # L&   [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 AB70..ABBF    ; Alphabetic # L&  [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
 ABC0..ABE2    ; Alphabetic # Lo  [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM
 ABE3..ABE4    ; Alphabetic # Mc   [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP
@@ -1007,7 +1011,7 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 10760..10767  ; Alphabetic # Lo   [8] LINEAR A SIGN A800..LINEAR A SIGN A807
 10780..10785  ; Alphabetic # Lm   [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK
 10787..107B0  ; Alphabetic # Lm  [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
-107B2..107BA  ; Alphabetic # Lm   [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
+107B2..107BF  ; Alphabetic # Lm  [14] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL ESH WITH DOUBLE BAR
 10800..10805  ; Alphabetic # Lo   [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA
 10808         ; Alphabetic # Lo       CYPRIOT SYLLABLE JO
 1080A..10835  ; Alphabetic # Lo  [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO
@@ -1020,6 +1024,7 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 108F4..108F5  ; Alphabetic # Lo   [2] HATRAN LETTER SHIN..HATRAN LETTER TAW
 10900..10915  ; Alphabetic # Lo  [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU
 10920..10939  ; Alphabetic # Lo  [26] LYDIAN LETTER A..LYDIAN LETTER C
+10940..10959  ; Alphabetic # Lo  [26] SIDETIC LETTER N01..SIDETIC LETTER N26
 10980..109B7  ; Alphabetic # Lo  [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA
 109BE..109BF  ; Alphabetic # Lo   [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN
 10A00         ; Alphabetic # Lo       KHAROSHTHI LETTER A
@@ -1053,7 +1058,14 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 10EAB..10EAC  ; Alphabetic # Mn   [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
 10EB0..10EB1  ; Alphabetic # Lo   [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
 10EC2..10EC4  ; Alphabetic # Lo   [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW
-10EFC         ; Alphabetic # Mn       ARABIC COMBINING ALEF OVERLAY
+10EC5         ; Alphabetic # Lm       ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW
+10EC6..10EC7  ; Alphabetic # Lo   [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW
+10ECB..10ECD  ; Alphabetic # Mn   [3] ARABIC NORTHEAST POINTING ARROWHEAD ABOVE..ARABIC SOUTHWEST POINTING ARROWHEAD BELOW
+10ED9..10EEE  ; Alphabetic # Lo  [22] ARABIC CROWN LETTER BEH..ARABIC CROWN LETTER YEH
+10EF3         ; Alphabetic # Mn       ARABIC SMALL HIGH NOON WITH FATHA
+10EF5         ; Alphabetic # Mn       ARABIC SMALL HIGH NOON WITH DAMMA
+10EF7..10EF8  ; Alphabetic # Mn   [2] ARABIC SMALL HIGH HEH INITIAL FORM..ARABIC SMALL HIGH WORD KABBIR
+10EFA..10EFC  ; Alphabetic # Mn   [3] ARABIC DOUBLE VERTICAL BAR BELOW..ARABIC COMBINING ALEF OVERLAY
 10F00..10F1C  ; Alphabetic # Lo  [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL
 10F27         ; Alphabetic # Lo       OLD SOGDIAN LIGATURE AYIN-DALETH
 10F30..10F45  ; Alphabetic # Lo  [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN
@@ -1239,6 +1251,13 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 11A97         ; Alphabetic # Mc       SOYOMBO SIGN VISARGA
 11A9D         ; Alphabetic # Lo       SOYOMBO MARK PLUTA
 11AB0..11AF8  ; Alphabetic # Lo  [73] CANADIAN SYLLABICS NATTILIK HI..PAU CIN HAU GLOTTAL STOP FINAL
+11B0A         ; Alphabetic # Lo       DEVANAGARI LETTER ALTERNATE DDDA
+11B60         ; Alphabetic # Mn       SHARADA VOWEL SIGN OE
+11B61         ; Alphabetic # Mc       SHARADA VOWEL SIGN OOE
+11B62..11B64  ; Alphabetic # Mn   [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E
+11B65         ; Alphabetic # Mc       SHARADA VOWEL SIGN SHORT O
+11B66         ; Alphabetic # Mn       SHARADA VOWEL SIGN CANDRA E
+11B67         ; Alphabetic # Mc       SHARADA VOWEL SIGN CANDRA O
 11BC0..11BE0  ; Alphabetic # Lo  [33] SUNUWAR LETTER DEVI..SUNUWAR LETTER KLOKO
 11C00..11C08  ; Alphabetic # Lo   [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L
 11C0A..11C2E  ; Alphabetic # Lo  [37] BHAIKSUKI LETTER E..BHAIKSUKI LETTER HA
@@ -1274,6 +1293,11 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 11D95         ; Alphabetic # Mn       GUNJALA GONDI SIGN ANUSVARA
 11D96         ; Alphabetic # Mc       GUNJALA GONDI SIGN VISARGA
 11D98         ; Alphabetic # Lo       GUNJALA GONDI OM
+11DB0..11DD8  ; Alphabetic # Lo  [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH
+11DD9         ; Alphabetic # Lm       TOLONG SIKI SIGN SELA
+11DDA..11DDB  ; Alphabetic # Lo   [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA
+11DF0         ; Alphabetic # Mn       BENGALI SIGN COMBINING ANUSVARA ABOVE
+11DF1         ; Alphabetic # Lo       BENGALI LETTER ALTERNATE BARGIYA BA
 11EE0..11EF2  ; Alphabetic # Lo  [19] MAKASAR LETTER KA..MAKASAR ANGKA
 11EF3..11EF4  ; Alphabetic # Mn   [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
 11EF5..11EF6  ; Alphabetic # Mc   [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
@@ -1288,8 +1312,10 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 11F40         ; Alphabetic # Mn       KAWI VOWEL SIGN EU
 11FB0         ; Alphabetic # Lo       LISU LETTER YHA
 12000..12399  ; Alphabetic # Lo [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U
-12400..1246E  ; Alphabetic # Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM
+12400..1246F  ; Alphabetic # Nl [112] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN SEVEN ASH TENU
+12475..1247F  ; Alphabetic # Nl  [11] CUNEIFORM NUMERIC SIGN EIGHT ASH TENU..CUNEIFORM NUMERIC SIGN ASH TIMES NINE DISH TENU
 12480..12543  ; Alphabetic # Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU
+12550..12686  ; Alphabetic # Nl [311] CUNEIFORM NUMERIC SIGN ONE N01..CUNEIFORM NUMERIC SIGN ONE N36 FLAT
 12F90..12FF0  ; Alphabetic # Lo  [97] CYPRO-MINOAN SIGN CM001..CYPRO-MINOAN SIGN CM114
 13000..1342F  ; Alphabetic # Lo [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D
 13441..13446  ; Alphabetic # Lo   [6] EGYPTIAN HIEROGLYPH FULL BLANK..EGYPTIAN HIEROGLYPH WIDE LOST SIGN
@@ -1310,7 +1336,12 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 16D40..16D42  ; Alphabetic # Lm   [3] KIRAT RAI SIGN ANUSVARA..KIRAT RAI SIGN VISARGA
 16D43..16D6A  ; Alphabetic # Lo  [40] KIRAT RAI LETTER A..KIRAT RAI VOWEL SIGN AU
 16D6B..16D6C  ; Alphabetic # Lm   [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT
+16D80..16D97  ; Alphabetic # Lo  [24] CHISOI LETTER A..CHISOI LETTER PA
+16D98         ; Alphabetic # Mn       CHISOI SIGN ANUSVARA
+16D99..16D9C  ; Alphabetic # Lo   [4] CHISOI LETTER YA..CHISOI LETTER JARAHA
 16E40..16E7F  ; Alphabetic # L&  [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y
+16EA0..16EB8  ; Alphabetic # L&  [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
+16EBB..16ED3  ; Alphabetic # L&  [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
 16F00..16F4A  ; Alphabetic # Lo  [75] MIAO LETTER PA..MIAO LETTER RTE
 16F4F         ; Alphabetic # Mn       MIAO SIGN CONSONANT MODIFIER BAR
 16F50         ; Alphabetic # Lo       MIAO LETTER NASALIZATION
@@ -1320,17 +1351,21 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 16FE0..16FE1  ; Alphabetic # Lm   [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK
 16FE3         ; Alphabetic # Lm       OLD CHINESE ITERATION MARK
 16FF0..16FF1  ; Alphabetic # Mc   [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY
-17000..187F7  ; Alphabetic # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
-18800..18CD5  ; Alphabetic # Lo [1238] TANGUT COMPONENT-001..KHITAN SMALL SCRIPT CHARACTER-18CD5
-18CFF..18D08  ; Alphabetic # Lo  [10] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D08
+16FF2..16FF3  ; Alphabetic # Lm   [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER
+16FF4..16FF6  ; Alphabetic # Nl   [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS
+17000..18CDA  ; Alphabetic # Lo [7387] TANGUT IDEOGRAPH-17000..KHITAN SMALL SCRIPT CHARACTER-18CDA
+18CFF..18D20  ; Alphabetic # Lo  [34] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D20
+18D80..18DF2  ; Alphabetic # Lo [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883
+18E00..19191  ; Alphabetic # Lo [914] JURCHEN CHARACTER-18E00..JURCHEN CHARACTER-19191
+191A0..191D2  ; Alphabetic # Lo  [51] JURCHEN RADICAL-01..JURCHEN RADICAL-51
 1AFF0..1AFF3  ; Alphabetic # Lm   [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5
 1AFF5..1AFFB  ; Alphabetic # Lm   [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5
 1AFFD..1AFFE  ; Alphabetic # Lm   [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8
-1B000..1B122  ; Alphabetic # Lo [291] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ARCHAIC WU
+1B000..1B128  ; Alphabetic # Lo [297] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ALTERNATE WI
 1B132         ; Alphabetic # Lo       HIRAGANA LETTER SMALL KO
 1B150..1B152  ; Alphabetic # Lo   [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO
 1B155         ; Alphabetic # Lo       KATAKANA LETTER SMALL KO
-1B164..1B167  ; Alphabetic # Lo   [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N
+1B164..1B168  ; Alphabetic # Lo   [5] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL ARCHAIC YE
 1B170..1B2FB  ; Alphabetic # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
 1BC00..1BC6A  ; Alphabetic # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M
 1BC70..1BC7C  ; Alphabetic # Lo  [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK
@@ -1355,7 +1390,7 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 1D540..1D544  ; Alphabetic # L&   [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
 1D546         ; Alphabetic # L&       MATHEMATICAL DOUBLE-STRUCK CAPITAL O
 1D54A..1D550  ; Alphabetic # L&   [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
-1D552..1D6A5  ; Alphabetic # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D552..1D6A6  ; Alphabetic # L& [341] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL LIGATURE LONG S WITH DESCENDER S
 1D6A8..1D6C0  ; Alphabetic # L&  [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
 1D6C2..1D6DA  ; Alphabetic # L&  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
 1D6DC..1D6FA  ; Alphabetic # L&  [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA
@@ -1369,8 +1404,10 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 1D7C4..1D7CB  ; Alphabetic # L&   [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA
 1DF00..1DF09  ; Alphabetic # L&  [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
 1DF0A         ; Alphabetic # Lo       LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK
-1DF0B..1DF1E  ; Alphabetic # L&  [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL
-1DF25..1DF2A  ; Alphabetic # L&   [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK
+1DF0B..1DF7F  ; Alphabetic # L& [117] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER REVERSED U
+1DF80..1DF81  ; Alphabetic # Lo   [2] LATIN CAPITAL LETTER A WITH TOPBAR..LATIN CAPITAL LETTER E WITH BENT TOPBAR
+1DF90..1DF96  ; Alphabetic # L&   [7] LATIN SMALL LETTER C WITH LOW SLASH..LATIN SMALL LETTER LONG S WITH TOP LOOP
+1DFCD..1DFFF  ; Alphabetic # Lm  [51] MODIFIER LETTER SMALL TURNED R WITH MID-HEIGHT LEFT HOOK..MODIFIER LETTER SMALL T WITH HOOK AND RETROFLEX HOOK
 1E000..1E006  ; Alphabetic # Mn   [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
 1E008..1E018  ; Alphabetic # Mn  [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
 1E01B..1E021  ; Alphabetic # Mn   [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
@@ -1387,6 +1424,17 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 1E4EB         ; Alphabetic # Lm       NAG MUNDARI SIGN OJOD
 1E5D0..1E5ED  ; Alphabetic # Lo  [30] OL ONAL LETTER O..OL ONAL LETTER EG
 1E5F0         ; Alphabetic # Lo       OL ONAL SIGN HODDOND
+1E6C0..1E6DE  ; Alphabetic # Lo  [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO
+1E6E0..1E6E2  ; Alphabetic # Lo   [3] TAI YO LETTER AA..TAI YO LETTER UE
+1E6E3         ; Alphabetic # Mn       TAI YO SIGN UE
+1E6E4..1E6E5  ; Alphabetic # Lo   [2] TAI YO LETTER U..TAI YO LETTER AE
+1E6E6         ; Alphabetic # Mn       TAI YO SIGN AU
+1E6E7..1E6ED  ; Alphabetic # Lo   [7] TAI YO LETTER O..TAI YO LETTER AUE
+1E6EE..1E6EF  ; Alphabetic # Mn   [2] TAI YO SIGN AY..TAI YO SIGN ANG
+1E6F0..1E6F4  ; Alphabetic # Lo   [5] TAI YO LETTER AN..TAI YO LETTER AP
+1E6F5         ; Alphabetic # Mn       TAI YO SIGN OM
+1E6FE         ; Alphabetic # Lo       TAI YO SYMBOL MUEANG
+1E6FF         ; Alphabetic # Lm       TAI YO XAM LAI
 1E7E0..1E7E6  ; Alphabetic # Lo   [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO
 1E7E8..1E7EB  ; Alphabetic # Lo   [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE
 1E7ED..1E7EE  ; Alphabetic # Lo   [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE
@@ -1432,16 +1480,16 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 1F150..1F169  ; Alphabetic # So  [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z
 1F170..1F189  ; Alphabetic # So  [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z
 20000..2A6DF  ; Alphabetic # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF
-2A700..2B739  ; Alphabetic # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739
-2B740..2B81D  ; Alphabetic # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D
-2B820..2CEA1  ; Alphabetic # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1
+2A700..2B81E  ; Alphabetic # Lo [4383] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81E
+2B820..2CEAD  ; Alphabetic # Lo [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD
 2CEB0..2EBE0  ; Alphabetic # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
 2EBF0..2EE5D  ; Alphabetic # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D
 2F800..2FA1D  ; Alphabetic # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
 30000..3134A  ; Alphabetic # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A
-31350..323AF  ; Alphabetic # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF
+31350..33479  ; Alphabetic # Lo [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479
+3D000..3FC3F  ; Alphabetic # Lo [11328] SEAL CHARACTER-3D000..SEAL CHARACTER-3FC3F
 
-# Total code points: 142759
+# Total code points: 160283
 
 # ================================================
 
@@ -1595,7 +1643,7 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 024B          ; Lowercase # L&       LATIN SMALL LETTER Q WITH HOOK TAIL
 024D          ; Lowercase # L&       LATIN SMALL LETTER R WITH STROKE
 024F..0293    ; Lowercase # L&  [69] LATIN SMALL LETTER Y WITH STROKE..LATIN SMALL LETTER EZH WITH CURL
-0295..02AF    ; Lowercase # L&  [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+0296..02AF    ; Lowercase # L&  [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
 02B0..02B8    ; Lowercase # Lm   [9] MODIFIER LETTER SMALL H..MODIFIER LETTER SMALL Y
 02C0..02C1    ; Lowercase # Lm   [2] MODIFIER LETTER GLOTTAL STOP..MODIFIER LETTER REVERSED GLOTTAL STOP
 02E0..02E4    ; Lowercase # Lm   [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
@@ -1724,7 +1772,9 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 052B          ; Lowercase # L&       CYRILLIC SMALL LETTER DZZHE
 052D          ; Lowercase # L&       CYRILLIC SMALL LETTER DCHE
 052F          ; Lowercase # L&       CYRILLIC SMALL LETTER EL WITH DESCENDER
+0558          ; Lowercase # Lm       MODIFIER LETTER ARMENIAN SMALL EH
 0560..0588    ; Lowercase # L&  [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE
+058B..058C    ; Lowercase # Lm   [2] MODIFIER LETTER ARMENIAN SMALL INI..MODIFIER LETTER ARMENIAN SMALL YI
 10D0..10FA    ; Lowercase # L&  [43] GEORGIAN LETTER AN..GEORGIAN LETTER AIN
 10FC          ; Lowercase # Lm       MODIFIER LETTER GEORGIAN NAR
 10FD..10FF    ; Lowercase # L&   [3] GEORGIAN LETTER AEN..GEORGIAN LETTER LABIAL SIGN
@@ -1883,7 +1933,7 @@ FFDA..FFDC    ; Alphabetic # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANG
 1FF6..1FF7    ; Lowercase # L&   [2] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI
 2071          ; Lowercase # Lm       SUPERSCRIPT LATIN SMALL LETTER I
 207F          ; Lowercase # Lm       SUPERSCRIPT LATIN SMALL LETTER N
-2090..209C    ; Lowercase # Lm  [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T
+2090..209F    ; Lowercase # Lm  [16] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER Z
 210A          ; Lowercase # L&       SCRIPT SMALL G
 210E..210F    ; Lowercase # L&   [2] PLANCK CONSTANT..PLANCK CONSTANT OVER TWO PI
 2113          ; Lowercase # L&       SCRIPT SMALL L
@@ -2073,13 +2123,14 @@ A7C3          ; Lowercase # L&       LATIN SMALL LETTER ANGLICANA W
 A7C8          ; Lowercase # L&       LATIN SMALL LETTER D WITH SHORT STROKE OVERLAY
 A7CA          ; Lowercase # L&       LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY
 A7CD          ; Lowercase # L&       LATIN SMALL LETTER S WITH DIAGONAL STROKE
+A7CF          ; Lowercase # L&       LATIN SMALL LETTER PHARYNGEAL VOICED FRICATIVE
 A7D1          ; Lowercase # L&       LATIN SMALL LETTER CLOSED INSULAR G
 A7D3          ; Lowercase # L&       LATIN SMALL LETTER DOUBLE THORN
 A7D5          ; Lowercase # L&       LATIN SMALL LETTER DOUBLE WYNN
 A7D7          ; Lowercase # L&       LATIN SMALL LETTER MIDDLE SCOTS S
 A7D9          ; Lowercase # L&       LATIN SMALL LETTER SIGMOID S
 A7DB          ; Lowercase # L&       LATIN SMALL LETTER LAMBDA
-A7F2..A7F4    ; Lowercase # Lm   [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
+A7F1..A7F4    ; Lowercase # Lm   [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q
 A7F6          ; Lowercase # L&       LATIN SMALL LETTER REVERSED HALF H
 A7F8..A7F9    ; Lowercase # Lm   [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
 A7FA          ; Lowercase # L&       LATIN LETTER SMALL CAPITAL TURNED M
@@ -2100,11 +2151,12 @@ FF41..FF5A    ; Lowercase # L&  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH L
 10780         ; Lowercase # Lm       MODIFIER LETTER SMALL CAPITAL AA
 10783..10785  ; Lowercase # Lm   [3] MODIFIER LETTER SMALL AE..MODIFIER LETTER SMALL B WITH HOOK
 10787..107B0  ; Lowercase # Lm  [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
-107B2..107BA  ; Lowercase # Lm   [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
+107B2..107BF  ; Lowercase # Lm  [14] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL ESH WITH DOUBLE BAR
 10CC0..10CF2  ; Lowercase # L&  [51] OLD HUNGARIAN SMALL LETTER A..OLD HUNGARIAN SMALL LETTER US
 10D70..10D85  ; Lowercase # L&  [22] GARAY SMALL LETTER A..GARAY SMALL LETTER OLD NA
 118C0..118DF  ; Lowercase # L&  [32] WARANG CITI SMALL LETTER NGAA..WARANG CITI SMALL LETTER VIYO
 16E60..16E7F  ; Lowercase # L&  [32] MEDEFAIDRIN SMALL LETTER M..MEDEFAIDRIN SMALL LETTER Y
+16EBB..16ED3  ; Lowercase # L&  [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
 1D41A..1D433  ; Lowercase # L&  [26] MATHEMATICAL BOLD SMALL A..MATHEMATICAL BOLD SMALL Z
 1D44E..1D454  ; Lowercase # L&   [7] MATHEMATICAL ITALIC SMALL A..MATHEMATICAL ITALIC SMALL G
 1D456..1D467  ; Lowercase # L&  [18] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL ITALIC SMALL Z
@@ -2121,7 +2173,7 @@ FF41..FF5A    ; Lowercase # L&  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH L
 1D5EE..1D607  ; Lowercase # L&  [26] MATHEMATICAL SANS-SERIF BOLD SMALL A..MATHEMATICAL SANS-SERIF BOLD SMALL Z
 1D622..1D63B  ; Lowercase # L&  [26] MATHEMATICAL SANS-SERIF ITALIC SMALL A..MATHEMATICAL SANS-SERIF ITALIC SMALL Z
 1D656..1D66F  ; Lowercase # L&  [26] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z
-1D68A..1D6A5  ; Lowercase # L&  [28] MATHEMATICAL MONOSPACE SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D68A..1D6A6  ; Lowercase # L&  [29] MATHEMATICAL MONOSPACE SMALL A..MATHEMATICAL ITALIC SMALL LIGATURE LONG S WITH DESCENDER S
 1D6C2..1D6DA  ; Lowercase # L&  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
 1D6DC..1D6E1  ; Lowercase # L&   [6] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL BOLD PI SYMBOL
 1D6FC..1D714  ; Lowercase # L&  [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA
@@ -2134,12 +2186,29 @@ FF41..FF5A    ; Lowercase # L&  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH L
 1D7C4..1D7C9  ; Lowercase # L&   [6] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL
 1D7CB         ; Lowercase # L&       MATHEMATICAL BOLD SMALL DIGAMMA
 1DF00..1DF09  ; Lowercase # L&  [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
-1DF0B..1DF1E  ; Lowercase # L&  [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL
-1DF25..1DF2A  ; Lowercase # L&   [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK
+1DF0B..1DF3F  ; Lowercase # L&  [53] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER BARRED W
+1DF41..1DF47  ; Lowercase # L&   [7] LATIN SMALL LETTER BARRED A..LATIN SMALL LETTER BARRED H WITH HOOK
+1DF49         ; Lowercase # L&       LATIN SMALL LETTER BARRED K
+1DF4B..1DF4C  ; Lowercase # L&   [2] LATIN SMALL LETTER BARRED M..LATIN SMALL LETTER BARRED M WITH HOOK
+1DF4E..1DF50  ; Lowercase # L&   [3] LATIN SMALL LETTER BARRED N..LATIN SMALL LETTER TURNED R WITH STROKE
+1DF52..1DF67  ; Lowercase # L&  [22] LATIN SMALL LETTER BARRED V..LATIN SMALL LETTER LEZH WITH CURL
+1DF69         ; Lowercase # L&       LATIN SMALL LETTER PHONOTYPIC A WITH SWASH
+1DF6B         ; Lowercase # L&       LATIN SMALL LETTER PHONOTYPIC ROUNDTOP A
+1DF6D         ; Lowercase # L&       LATIN SMALL LETTER REVERSED SCRUPLE
+1DF6F..1DF71  ; Lowercase # L&   [3] LATIN SMALL LETTER PHONOTYPIC DIPHTHONG AI..LATIN SMALL LETTER STRETCHED I
+1DF73         ; Lowercase # L&       LATIN SMALL LETTER O WITH CURL
+1DF75         ; Lowercase # L&       LATIN SMALL LETTER CLOSED OMEGA WITH LONG STEM
+1DF77         ; Lowercase # L&       LATIN SMALL LETTER TURNED CLOSED OMEGA
+1DF79         ; Lowercase # L&       LATIN SMALL LETTER PHONOTYPIC TH
+1DF7B         ; Lowercase # L&       LATIN SMALL LETTER U WITH HOOK TAIL
+1DF7D         ; Lowercase # L&       LATIN SMALL LETTER U WITH NOTCH AT BOTTOM
+1DF7F         ; Lowercase # L&       LATIN SMALL LETTER REVERSED U
+1DF90..1DF96  ; Lowercase # L&   [7] LATIN SMALL LETTER C WITH LOW SLASH..LATIN SMALL LETTER LONG S WITH TOP LOOP
+1DFCD..1DFFF  ; Lowercase # Lm  [51] MODIFIER LETTER SMALL TURNED R WITH MID-HEIGHT LEFT HOOK..MODIFIER LETTER SMALL T WITH HOOK AND RETROFLEX HOOK
 1E030..1E06D  ; Lowercase # Lm  [62] MODIFIER LETTER CYRILLIC SMALL A..MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE
 1E922..1E943  ; Lowercase # L&  [34] ADLAM SMALL LETTER ALIF..ADLAM SMALL LETTER SHA
 
-# Total code points: 2569
+# Total code points: 2740
 
 # ================================================
 
@@ -2750,12 +2819,17 @@ A7C2          ; Uppercase # L&       LATIN CAPITAL LETTER ANGLICANA W
 A7C4..A7C7    ; Uppercase # L&   [4] LATIN CAPITAL LETTER C WITH PALATAL HOOK..LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY
 A7C9          ; Uppercase # L&       LATIN CAPITAL LETTER S WITH SHORT STROKE OVERLAY
 A7CB..A7CC    ; Uppercase # L&   [2] LATIN CAPITAL LETTER RAMS HORN..LATIN CAPITAL LETTER S WITH DIAGONAL STROKE
+A7CE          ; Uppercase # L&       LATIN CAPITAL LETTER PHARYNGEAL VOICED FRICATIVE
 A7D0          ; Uppercase # L&       LATIN CAPITAL LETTER CLOSED INSULAR G
+A7D2          ; Uppercase # L&       LATIN CAPITAL LETTER DOUBLE THORN
+A7D4          ; Uppercase # L&       LATIN CAPITAL LETTER DOUBLE WYNN
 A7D6          ; Uppercase # L&       LATIN CAPITAL LETTER MIDDLE SCOTS S
 A7D8          ; Uppercase # L&       LATIN CAPITAL LETTER SIGMOID S
 A7DA          ; Uppercase # L&       LATIN CAPITAL LETTER LAMBDA
-A7DC          ; Uppercase # L&       LATIN CAPITAL LETTER LAMBDA WITH STROKE
+A7DC..A7DD    ; Uppercase # L&   [2] LATIN CAPITAL LETTER LAMBDA WITH STROKE..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2          ; Uppercase # L&       LATIN CAPITAL LETTER R WITH LONG LEG
 A7F5          ; Uppercase # L&       LATIN CAPITAL LETTER REVERSED HALF H
+AB6C..AB6D    ; Uppercase # L&   [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 FF21..FF3A    ; Uppercase # L&  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z
 10400..10427  ; Uppercase # L&  [40] DESERET CAPITAL LETTER LONG I..DESERET CAPITAL LETTER EW
 104B0..104D3  ; Uppercase # L&  [36] OSAGE CAPITAL LETTER A..OSAGE CAPITAL LETTER ZHA
@@ -2767,6 +2841,7 @@ FF21..FF3A    ; Uppercase # L&  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH
 10D50..10D65  ; Uppercase # L&  [22] GARAY CAPITAL LETTER A..GARAY CAPITAL LETTER OLD NA
 118A0..118BF  ; Uppercase # L&  [32] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI CAPITAL LETTER VIYO
 16E40..16E5F  ; Uppercase # L&  [32] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN CAPITAL LETTER Y
+16EA0..16EB8  ; Uppercase # L&  [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
 1D400..1D419  ; Uppercase # L&  [26] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL BOLD CAPITAL Z
 1D434..1D44D  ; Uppercase # L&  [26] MATHEMATICAL ITALIC CAPITAL A..MATHEMATICAL ITALIC CAPITAL Z
 1D468..1D481  ; Uppercase # L&  [26] MATHEMATICAL BOLD ITALIC CAPITAL A..MATHEMATICAL BOLD ITALIC CAPITAL Z
@@ -2798,12 +2873,28 @@ FF21..FF3A    ; Uppercase # L&  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH
 1D756..1D76E  ; Uppercase # L&  [25] MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA
 1D790..1D7A8  ; Uppercase # L&  [25] MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA
 1D7CA         ; Uppercase # L&       MATHEMATICAL BOLD CAPITAL DIGAMMA
+1DF40         ; Uppercase # L&       LATIN CAPITAL LETTER BARRED A
+1DF48         ; Uppercase # L&       LATIN CAPITAL LETTER BARRED K
+1DF4A         ; Uppercase # L&       LATIN CAPITAL LETTER BARRED M
+1DF4D         ; Uppercase # L&       LATIN CAPITAL LETTER BARRED N
+1DF51         ; Uppercase # L&       LATIN CAPITAL LETTER BARRED V
+1DF68         ; Uppercase # L&       LATIN CAPITAL LETTER PHONOTYPIC A WITH SWASH
+1DF6A         ; Uppercase # L&       LATIN CAPITAL LETTER PHONOTYPIC ROUNDTOP A
+1DF6C         ; Uppercase # L&       LATIN CAPITAL LETTER REVERSED SCRUPLE
+1DF6E         ; Uppercase # L&       LATIN CAPITAL LETTER PHONOTYPIC DIPHTHONG AI
+1DF72         ; Uppercase # L&       LATIN CAPITAL LETTER O WITH CURL
+1DF74         ; Uppercase # L&       LATIN CAPITAL LETTER CLOSED OMEGA WITH LONG STEM
+1DF76         ; Uppercase # L&       LATIN CAPITAL LETTER TURNED CLOSED OMEGA
+1DF78         ; Uppercase # L&       LATIN CAPITAL LETTER PHONOTYPIC TH
+1DF7A         ; Uppercase # L&       LATIN CAPITAL LETTER U WITH HOOK TAIL
+1DF7C         ; Uppercase # L&       LATIN CAPITAL LETTER U WITH NOTCH AT BOTTOM
+1DF7E         ; Uppercase # L&       LATIN CAPITAL LETTER REVERSED U
 1E900..1E921  ; Uppercase # L&  [34] ADLAM CAPITAL LETTER ALIF..ADLAM CAPITAL LETTER SHA
 1F130..1F149  ; Uppercase # So  [26] SQUARED LATIN CAPITAL LETTER A..SQUARED LATIN CAPITAL LETTER Z
 1F150..1F169  ; Uppercase # So  [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z
 1F170..1F189  ; Uppercase # So  [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z
 
-# Total code points: 1978
+# Total code points: 2026
 
 # ================================================
 
@@ -2821,7 +2912,7 @@ FF21..FF3A    ; Uppercase # L&  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH
 00F8..01BA    ; Cased # L& [195] LATIN SMALL LETTER O WITH STROKE..LATIN SMALL LETTER EZH WITH TAIL
 01BC..01BF    ; Cased # L&   [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN
 01C4..0293    ; Cased # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL
-0295..02AF    ; Cased # L&  [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+0296..02AF    ; Cased # L&  [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
 02B0..02B8    ; Cased # Lm   [9] MODIFIER LETTER SMALL H..MODIFIER LETTER SMALL Y
 02C0..02C1    ; Cased # Lm   [2] MODIFIER LETTER GLOTTAL STOP..MODIFIER LETTER REVERSED GLOTTAL STOP
 02E0..02E4    ; Cased # Lm   [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
@@ -2839,7 +2930,9 @@ FF21..FF3A    ; Uppercase # L&  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH
 03F7..0481    ; Cased # L& [139] GREEK CAPITAL LETTER SHO..CYRILLIC SMALL LETTER KOPPA
 048A..052F    ; Cased # L& [166] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER EL WITH DESCENDER
 0531..0556    ; Cased # L&  [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH
+0558          ; Cased # Lm       MODIFIER LETTER ARMENIAN SMALL EH
 0560..0588    ; Cased # L&  [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE
+058B..058C    ; Cased # Lm   [2] MODIFIER LETTER ARMENIAN SMALL INI..MODIFIER LETTER ARMENIAN SMALL YI
 10A0..10C5    ; Cased # L&  [38] GEORGIAN CAPITAL LETTER AN..GEORGIAN CAPITAL LETTER HOE
 10C7          ; Cased # L&       GEORGIAN CAPITAL LETTER YN
 10CD          ; Cased # L&       GEORGIAN CAPITAL LETTER AEN
@@ -2878,7 +2971,7 @@ FF21..FF3A    ; Uppercase # L&  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH
 1FF6..1FFC    ; Cased # L&   [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
 2071          ; Cased # Lm       SUPERSCRIPT LATIN SMALL LETTER I
 207F          ; Cased # Lm       SUPERSCRIPT LATIN SMALL LETTER N
-2090..209C    ; Cased # Lm  [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T
+2090..209F    ; Cased # Lm  [16] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER Z
 2102          ; Cased # L&       DOUBLE-STRUCK CAPITAL C
 2107          ; Cased # L&       EULER CONSTANT
 210A..2113    ; Cased # L&  [10] SCRIPT SMALL G..SCRIPT SMALL L
@@ -2911,11 +3004,9 @@ A722..A76F    ; Cased # L&  [78] LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF..LATIN
 A770          ; Cased # Lm       MODIFIER LETTER US
 A771..A787    ; Cased # L&  [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER INSULAR T
 A78B..A78E    ; Cased # L&   [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT
-A790..A7CD    ; Cased # L&  [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE
-A7D0..A7D1    ; Cased # L&   [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G
-A7D3          ; Cased # L&       LATIN SMALL LETTER DOUBLE THORN
-A7D5..A7DC    ; Cased # L&   [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE
-A7F2..A7F4    ; Cased # Lm   [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
+A790..A7DD    ; Cased # L&  [78] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2          ; Cased # L&       LATIN CAPITAL LETTER R WITH LONG LEG
+A7F1..A7F4    ; Cased # Lm   [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q
 A7F5..A7F6    ; Cased # L&   [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H
 A7F8..A7F9    ; Cased # Lm   [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
 A7FA          ; Cased # L&       LATIN LETTER SMALL CAPITAL TURNED M
@@ -2923,6 +3014,7 @@ AB30..AB5A    ; Cased # L&  [43] LATIN SMALL LETTER BARRED ALPHA..LATIN SMALL LE
 AB5C..AB5F    ; Cased # Lm   [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK
 AB60..AB68    ; Cased # L&   [9] LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER TURNED R WITH MIDDLE TILDE
 AB69          ; Cased # Lm       MODIFIER LETTER SMALL TURNED W
+AB6C..AB6D    ; Cased # L&   [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 AB70..ABBF    ; Cased # L&  [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
 FB00..FB06    ; Cased # L&   [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
 FB13..FB17    ; Cased # L&   [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH
@@ -2942,13 +3034,15 @@ FF41..FF5A    ; Cased # L&  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN
 10780         ; Cased # Lm       MODIFIER LETTER SMALL CAPITAL AA
 10783..10785  ; Cased # Lm   [3] MODIFIER LETTER SMALL AE..MODIFIER LETTER SMALL B WITH HOOK
 10787..107B0  ; Cased # Lm  [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
-107B2..107BA  ; Cased # Lm   [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
+107B2..107BF  ; Cased # Lm  [14] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL ESH WITH DOUBLE BAR
 10C80..10CB2  ; Cased # L&  [51] OLD HUNGARIAN CAPITAL LETTER A..OLD HUNGARIAN CAPITAL LETTER US
 10CC0..10CF2  ; Cased # L&  [51] OLD HUNGARIAN SMALL LETTER A..OLD HUNGARIAN SMALL LETTER US
 10D50..10D65  ; Cased # L&  [22] GARAY CAPITAL LETTER A..GARAY CAPITAL LETTER OLD NA
 10D70..10D85  ; Cased # L&  [22] GARAY SMALL LETTER A..GARAY SMALL LETTER OLD NA
 118A0..118DF  ; Cased # L&  [64] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI SMALL LETTER VIYO
 16E40..16E7F  ; Cased # L&  [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y
+16EA0..16EB8  ; Cased # L&  [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
+16EBB..16ED3  ; Cased # L&  [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
 1D400..1D454  ; Cased # L&  [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G
 1D456..1D49C  ; Cased # L&  [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A
 1D49E..1D49F  ; Cased # L&   [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D
@@ -2967,7 +3061,7 @@ FF41..FF5A    ; Cased # L&  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN
 1D540..1D544  ; Cased # L&   [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
 1D546         ; Cased # L&       MATHEMATICAL DOUBLE-STRUCK CAPITAL O
 1D54A..1D550  ; Cased # L&   [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
-1D552..1D6A5  ; Cased # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D552..1D6A6  ; Cased # L& [341] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL LIGATURE LONG S WITH DESCENDER S
 1D6A8..1D6C0  ; Cased # L&  [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
 1D6C2..1D6DA  ; Cased # L&  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
 1D6DC..1D6FA  ; Cased # L&  [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA
@@ -2980,15 +3074,16 @@ FF41..FF5A    ; Cased # L&  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN
 1D7AA..1D7C2  ; Cased # L&  [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA
 1D7C4..1D7CB  ; Cased # L&   [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA
 1DF00..1DF09  ; Cased # L&  [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
-1DF0B..1DF1E  ; Cased # L&  [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL
-1DF25..1DF2A  ; Cased # L&   [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK
+1DF0B..1DF7F  ; Cased # L& [117] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER REVERSED U
+1DF90..1DF96  ; Cased # L&   [7] LATIN SMALL LETTER C WITH LOW SLASH..LATIN SMALL LETTER LONG S WITH TOP LOOP
+1DFCD..1DFFF  ; Cased # Lm  [51] MODIFIER LETTER SMALL TURNED R WITH MID-HEIGHT LEFT HOOK..MODIFIER LETTER SMALL T WITH HOOK AND RETROFLEX HOOK
 1E030..1E06D  ; Cased # Lm  [62] MODIFIER LETTER CYRILLIC SMALL A..MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE
 1E900..1E943  ; Cased # L&  [68] ADLAM CAPITAL LETTER ALIF..ADLAM SMALL LETTER SHA
 1F130..1F149  ; Cased # So  [26] SQUARED LATIN CAPITAL LETTER A..SQUARED LATIN CAPITAL LETTER Z
 1F150..1F169  ; Cased # So  [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z
 1F170..1F189  ; Cased # So  [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z
 
-# Total code points: 4578
+# Total code points: 4797
 
 # ================================================
 
@@ -3027,13 +3122,14 @@ FF41..FF5A    ; Cased # L&  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN
 0387          ; Case_Ignorable # Po       GREEK ANO TELEIA
 0483..0487    ; Case_Ignorable # Mn   [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE
 0488..0489    ; Case_Ignorable # Me   [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN
-0559          ; Case_Ignorable # Lm       ARMENIAN MODIFIER LETTER LEFT HALF RING
+0558..0559    ; Case_Ignorable # Lm   [2] MODIFIER LETTER ARMENIAN SMALL EH..ARMENIAN MODIFIER LETTER LEFT HALF RING
 055F          ; Case_Ignorable # Po       ARMENIAN ABBREVIATION MARK
+058B..058C    ; Case_Ignorable # Lm   [2] MODIFIER LETTER ARMENIAN SMALL INI..MODIFIER LETTER ARMENIAN SMALL YI
 0591..05BD    ; Case_Ignorable # Mn  [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
 05BF          ; Case_Ignorable # Mn       HEBREW POINT RAFE
 05C1..05C2    ; Case_Ignorable # Mn   [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
 05C4..05C5    ; Case_Ignorable # Mn   [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
-05C7          ; Case_Ignorable # Mn       HEBREW POINT QAMATS QATAN
+05C7..05C9    ; Case_Ignorable # Mn   [3] HEBREW POINT QAMATS QATAN..HEBREW POINT DAGESH HAZAQ MUDGASH
 05F4          ; Case_Ignorable # Po       HEBREW PUNCTUATION GERSHAYIM
 0600..0605    ; Case_Ignorable # Cf   [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE
 0610..061A    ; Case_Ignorable # Mn  [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
@@ -3103,7 +3199,7 @@ FF41..FF5A    ; Cased # L&  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN
 0B3F          ; Case_Ignorable # Mn       ORIYA VOWEL SIGN I
 0B41..0B44    ; Case_Ignorable # Mn   [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR
 0B4D          ; Case_Ignorable # Mn       ORIYA SIGN VIRAMA
-0B55..0B56    ; Case_Ignorable # Mn   [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK
+0B53..0B56    ; Case_Ignorable # Mn   [4] ORIYA SIGN DOT ABOVE..ORIYA AI LENGTH MARK
 0B62..0B63    ; Case_Ignorable # Mn   [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL
 0B82          ; Case_Ignorable # Mn       TAMIL SIGN ANUSVARA
 0BC0          ; Case_Ignorable # Mn       TAMIL VOWEL SIGN II
@@ -3194,7 +3290,7 @@ FF41..FF5A    ; Cased # L&  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN
 1AA7          ; Case_Ignorable # Lm       TAI THAM SIGN MAI YAMOK
 1AB0..1ABD    ; Case_Ignorable # Mn  [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
 1ABE          ; Case_Ignorable # Me       COMBINING PARENTHESES OVERLAY
-1ABF..1ACE    ; Case_Ignorable # Mn  [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T
+1ABF..1AF0    ; Case_Ignorable # Mn  [50] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOUBLE COMMA ABOVE
 1B00..1B03    ; Case_Ignorable # Mn   [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
 1B34          ; Case_Ignorable # Mn       BALINESE SIGN REREKAN
 1B36..1B3A    ; Case_Ignorable # Mn   [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA
@@ -3238,7 +3334,7 @@ FF41..FF5A    ; Cased # L&  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN
 2066..206F    ; Case_Ignorable # Cf  [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
 2071          ; Case_Ignorable # Lm       SUPERSCRIPT LATIN SMALL LETTER I
 207F          ; Case_Ignorable # Lm       SUPERSCRIPT LATIN SMALL LETTER N
-2090..209C    ; Case_Ignorable # Lm  [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T
+208F..209F    ; Case_Ignorable # Lm  [17] MODIFIER LETTER HIGH AND LOW VERTICAL LINE..LATIN SUBSCRIPT SMALL LETTER Z
 20D0..20DC    ; Case_Ignorable # Mn  [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE
 20DD..20E0    ; Case_Ignorable # Me   [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH
 20E1          ; Case_Ignorable # Mn       COMBINING LEFT RIGHT ARROW ABOVE
@@ -3274,7 +3370,7 @@ A720..A721    ; Case_Ignorable # Sk   [2] MODIFIER LETTER STRESS AND HIGH TONE..
 A770          ; Case_Ignorable # Lm       MODIFIER LETTER US
 A788          ; Case_Ignorable # Lm       MODIFIER LETTER LOW CIRCUMFLEX ACCENT
 A789..A78A    ; Case_Ignorable # Sk   [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
-A7F2..A7F4    ; Case_Ignorable # Lm   [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
+A7F1..A7F4    ; Case_Ignorable # Lm   [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q
 A7F8..A7F9    ; Case_Ignorable # Lm   [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
 A802          ; Case_Ignorable # Mn       SYLOTI NAGRI SIGN DVISVARA
 A806          ; Case_Ignorable # Mn       SYLOTI NAGRI SIGN HASANTA
@@ -3338,7 +3434,7 @@ FFF9..FFFB    ; Case_Ignorable # Cf   [3] INTERLINEAR ANNOTATION ANCHOR..INTERLI
 10376..1037A  ; Case_Ignorable # Mn   [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII
 10780..10785  ; Case_Ignorable # Lm   [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK
 10787..107B0  ; Case_Ignorable # Lm  [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
-107B2..107BA  ; Case_Ignorable # Lm   [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
+107B2..107BF  ; Case_Ignorable # Lm  [14] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL ESH WITH DOUBLE BAR
 10A01..10A03  ; Case_Ignorable # Mn   [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R
 10A05..10A06  ; Case_Ignorable # Mn   [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O
 10A0C..10A0F  ; Case_Ignorable # Mn   [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA
@@ -3350,7 +3446,10 @@ FFF9..FFFB    ; Case_Ignorable # Cf   [3] INTERLINEAR ANNOTATION ANCHOR..INTERLI
 10D69..10D6D  ; Case_Ignorable # Mn   [5] GARAY VOWEL SIGN E..GARAY CONSONANT NASALIZATION MARK
 10D6F         ; Case_Ignorable # Lm       GARAY REDUPLICATION MARK
 10EAB..10EAC  ; Case_Ignorable # Mn   [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
-10EFC..10EFF  ; Case_Ignorable # Mn   [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA
+10EC5         ; Case_Ignorable # Lm       ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW
+10EC9..10ECA  ; Case_Ignorable # Sk   [2] ARABIC SMALL BASELINE FATHA..ARABIC SMALL BASELINE DOTLESS HEAD OF KHAH
+10ECB..10ECF  ; Case_Ignorable # Mn   [5] ARABIC NORTHEAST POINTING ARROWHEAD ABOVE..ARABIC LARGE CIRCLE ABOVE
+10EF0..10EFF  ; Case_Ignorable # Mn  [16] ARABIC SMALL LOW UPRIGHT RECTANGULAR ZERO..ARABIC SMALL LOW WORD MADDA
 10F46..10F50  ; Case_Ignorable # Mn  [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW
 10F82..10F85  ; Case_Ignorable # Mn   [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW
 11001         ; Case_Ignorable # Mn       BRAHMI SIGN ANUSVARA
@@ -3427,6 +3526,9 @@ FFF9..FFFB    ; Case_Ignorable # Cf   [3] INTERLINEAR ANNOTATION ANCHOR..INTERLI
 11A59..11A5B  ; Case_Ignorable # Mn   [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK
 11A8A..11A96  ; Case_Ignorable # Mn  [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA
 11A98..11A99  ; Case_Ignorable # Mn   [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER
+11B60         ; Case_Ignorable # Mn       SHARADA VOWEL SIGN OE
+11B62..11B64  ; Case_Ignorable # Mn   [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E
+11B66         ; Case_Ignorable # Mn       SHARADA VOWEL SIGN CANDRA E
 11C30..11C36  ; Case_Ignorable # Mn   [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L
 11C38..11C3D  ; Case_Ignorable # Mn   [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA
 11C3F         ; Case_Ignorable # Mn       BHAIKSUKI SIGN VIRAMA
@@ -3442,6 +3544,8 @@ FFF9..FFFB    ; Case_Ignorable # Cf   [3] INTERLINEAR ANNOTATION ANCHOR..INTERLI
 11D90..11D91  ; Case_Ignorable # Mn   [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI
 11D95         ; Case_Ignorable # Mn       GUNJALA GONDI SIGN ANUSVARA
 11D97         ; Case_Ignorable # Mn       GUNJALA GONDI VIRAMA
+11DD9         ; Case_Ignorable # Lm       TOLONG SIKI SIGN SELA
+11DF0         ; Case_Ignorable # Mn       BENGALI SIGN COMBINING ANUSVARA ABOVE
 11EF3..11EF4  ; Case_Ignorable # Mn   [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
 11F00..11F01  ; Case_Ignorable # Mn   [2] KAWI SIGN CANDRABINDU..KAWI SIGN ANUSVARA
 11F36..11F3A  ; Case_Ignorable # Mn   [5] KAWI VOWEL SIGN I..KAWI VOWEL SIGN VOCALIC R
@@ -3458,12 +3562,15 @@ FFF9..FFFB    ; Case_Ignorable # Cf   [3] INTERLINEAR ANNOTATION ANCHOR..INTERLI
 16B40..16B43  ; Case_Ignorable # Lm   [4] PAHAWH HMONG SIGN VOS SEEV..PAHAWH HMONG SIGN IB YAM
 16D40..16D42  ; Case_Ignorable # Lm   [3] KIRAT RAI SIGN ANUSVARA..KIRAT RAI SIGN VISARGA
 16D6B..16D6C  ; Case_Ignorable # Lm   [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT
+16D98         ; Case_Ignorable # Mn       CHISOI SIGN ANUSVARA
+16D9D         ; Case_Ignorable # Mn       CHISOI SIGN SISO
 16F4F         ; Case_Ignorable # Mn       MIAO SIGN CONSONANT MODIFIER BAR
 16F8F..16F92  ; Case_Ignorable # Mn   [4] MIAO TONE RIGHT..MIAO TONE BELOW
 16F93..16F9F  ; Case_Ignorable # Lm  [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8
 16FE0..16FE1  ; Case_Ignorable # Lm   [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK
 16FE3         ; Case_Ignorable # Lm       OLD CHINESE ITERATION MARK
 16FE4         ; Case_Ignorable # Mn       KHITAN SMALL SCRIPT FILLER
+16FF2..16FF3  ; Case_Ignorable # Lm   [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER
 1AFF0..1AFF3  ; Case_Ignorable # Lm   [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5
 1AFF5..1AFFB  ; Case_Ignorable # Lm   [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5
 1AFFD..1AFFE  ; Case_Ignorable # Lm   [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8
@@ -3471,18 +3578,21 @@ FFF9..FFFB    ; Case_Ignorable # Cf   [3] INTERLINEAR ANNOTATION ANCHOR..INTERLI
 1BCA0..1BCA3  ; Case_Ignorable # Cf   [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
 1CF00..1CF2D  ; Case_Ignorable # Mn  [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT
 1CF30..1CF46  ; Case_Ignorable # Mn  [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG
+1D127..1D128  ; Case_Ignorable # Mn   [2] MUSICAL SYMBOL COMBINING STRESS..MUSICAL SYMBOL COMBINING UNSTRESS
 1D167..1D169  ; Case_Ignorable # Mn   [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
 1D173..1D17A  ; Case_Ignorable # Cf   [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
 1D17B..1D182  ; Case_Ignorable # Mn   [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE
 1D185..1D18B  ; Case_Ignorable # Mn   [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
 1D1AA..1D1AD  ; Case_Ignorable # Mn   [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
 1D242..1D244  ; Case_Ignorable # Mn   [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+1D25B..1D25C  ; Case_Ignorable # Mn   [2] MUSICAL SYMBOL COMBINING TREMOLO-4..MUSICAL SYMBOL COMBINING TREMOLO-5
 1DA00..1DA36  ; Case_Ignorable # Mn  [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN
 1DA3B..1DA6C  ; Case_Ignorable # Mn  [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT
 1DA75         ; Case_Ignorable # Mn       SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS
 1DA84         ; Case_Ignorable # Mn       SIGNWRITING LOCATION HEAD NECK
 1DA9B..1DA9F  ; Case_Ignorable # Mn   [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6
 1DAA1..1DAAF  ; Case_Ignorable # Mn  [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16
+1DFCD..1DFFF  ; Case_Ignorable # Lm  [51] MODIFIER LETTER SMALL TURNED R WITH MID-HEIGHT LEFT HOOK..MODIFIER LETTER SMALL T WITH HOOK AND RETROFLEX HOOK
 1E000..1E006  ; Case_Ignorable # Mn   [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
 1E008..1E018  ; Case_Ignorable # Mn  [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
 1E01B..1E021  ; Case_Ignorable # Mn   [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
@@ -3497,6 +3607,11 @@ FFF9..FFFB    ; Case_Ignorable # Cf   [3] INTERLINEAR ANNOTATION ANCHOR..INTERLI
 1E4EB         ; Case_Ignorable # Lm       NAG MUNDARI SIGN OJOD
 1E4EC..1E4EF  ; Case_Ignorable # Mn   [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH
 1E5EE..1E5EF  ; Case_Ignorable # Mn   [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR
+1E6E3         ; Case_Ignorable # Mn       TAI YO SIGN UE
+1E6E6         ; Case_Ignorable # Mn       TAI YO SIGN AU
+1E6EE..1E6EF  ; Case_Ignorable # Mn   [2] TAI YO SIGN AY..TAI YO SIGN ANG
+1E6F5         ; Case_Ignorable # Mn       TAI YO SIGN OM
+1E6FF         ; Case_Ignorable # Lm       TAI YO XAM LAI
 1E8D0..1E8D6  ; Case_Ignorable # Mn   [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
 1E944..1E94A  ; Case_Ignorable # Mn   [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
 1E94B         ; Case_Ignorable # Lm       ADLAM NASALIZATION MARK
@@ -3505,13 +3620,14 @@ E0001         ; Case_Ignorable # Cf       LANGUAGE TAG
 E0020..E007F  ; Case_Ignorable # Cf  [96] TAG SPACE..CANCEL TAG
 E0100..E01EF  ; Case_Ignorable # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
 
-# Total code points: 2749
+# Total code points: 2892
 
 # ================================================
 
 # Derived Property:   Changes_When_Lowercased (CWL)
 #  Characters whose normalized forms are not stable under a toLowercase mapping.
-#  For more information, see D139 in Section 3.13, "Default Case Algorithms".
+#  For more information, see the definition of "isLowercase(X)"
+#  in the "Conformance" / "Default Case Algorithms" section of the core specification.
 #  Changes_When_Lowercased(X) is true when toLowercase(toNFD(X)) != toNFD(X)
 
 0041..005A    ; Changes_When_Lowercased # L&  [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z
@@ -4110,12 +4226,17 @@ A7C2          ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER ANGLICAN
 A7C4..A7C7    ; Changes_When_Lowercased # L&   [4] LATIN CAPITAL LETTER C WITH PALATAL HOOK..LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY
 A7C9          ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER S WITH SHORT STROKE OVERLAY
 A7CB..A7CC    ; Changes_When_Lowercased # L&   [2] LATIN CAPITAL LETTER RAMS HORN..LATIN CAPITAL LETTER S WITH DIAGONAL STROKE
+A7CE          ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER PHARYNGEAL VOICED FRICATIVE
 A7D0          ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER CLOSED INSULAR G
+A7D2          ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER DOUBLE THORN
+A7D4          ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER DOUBLE WYNN
 A7D6          ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER MIDDLE SCOTS S
 A7D8          ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER SIGMOID S
 A7DA          ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER LAMBDA
-A7DC          ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER LAMBDA WITH STROKE
+A7DC..A7DD    ; Changes_When_Lowercased # L&   [2] LATIN CAPITAL LETTER LAMBDA WITH STROKE..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2          ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER R WITH LONG LEG
 A7F5          ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER REVERSED HALF H
+AB6C..AB6D    ; Changes_When_Lowercased # L&   [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 FF21..FF3A    ; Changes_When_Lowercased # L&  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z
 10400..10427  ; Changes_When_Lowercased # L&  [40] DESERET CAPITAL LETTER LONG I..DESERET CAPITAL LETTER EW
 104B0..104D3  ; Changes_When_Lowercased # L&  [36] OSAGE CAPITAL LETTER A..OSAGE CAPITAL LETTER ZHA
@@ -4127,15 +4248,33 @@ FF21..FF3A    ; Changes_When_Lowercased # L&  [26] FULLWIDTH LATIN CAPITAL LETTE
 10D50..10D65  ; Changes_When_Lowercased # L&  [22] GARAY CAPITAL LETTER A..GARAY CAPITAL LETTER OLD NA
 118A0..118BF  ; Changes_When_Lowercased # L&  [32] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI CAPITAL LETTER VIYO
 16E40..16E5F  ; Changes_When_Lowercased # L&  [32] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN CAPITAL LETTER Y
+16EA0..16EB8  ; Changes_When_Lowercased # L&  [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
+1DF40         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER BARRED A
+1DF48         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER BARRED K
+1DF4A         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER BARRED M
+1DF4D         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER BARRED N
+1DF51         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER BARRED V
+1DF68         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER PHONOTYPIC A WITH SWASH
+1DF6A         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER PHONOTYPIC ROUNDTOP A
+1DF6C         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER REVERSED SCRUPLE
+1DF6E         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER PHONOTYPIC DIPHTHONG AI
+1DF72         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER O WITH CURL
+1DF74         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER CLOSED OMEGA WITH LONG STEM
+1DF76         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER TURNED CLOSED OMEGA
+1DF78         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER PHONOTYPIC TH
+1DF7A         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER U WITH HOOK TAIL
+1DF7C         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER U WITH NOTCH AT BOTTOM
+1DF7E         ; Changes_When_Lowercased # L&       LATIN CAPITAL LETTER REVERSED U
 1E900..1E921  ; Changes_When_Lowercased # L&  [34] ADLAM CAPITAL LETTER ALIF..ADLAM CAPITAL LETTER SHA
 
-# Total code points: 1460
+# Total code points: 1508
 
 # ================================================
 
 # Derived Property:   Changes_When_Uppercased (CWU)
 #  Characters whose normalized forms are not stable under a toUppercase mapping.
-#  For more information, see D140 in Section 3.13, "Default Case Algorithms".
+#  For more information, see the definition of "isUppercase(X)"
+#  in the "Conformance" / "Default Case Algorithms" section of the core specification.
 #  Changes_When_Uppercased(X) is true when toUppercase(toNFD(X)) != toNFD(X)
 
 0061..007A    ; Changes_When_Uppercased # L&  [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z
@@ -4291,7 +4430,8 @@ FF21..FF3A    ; Changes_When_Lowercased # L&  [26] FULLWIDTH LATIN CAPITAL LETTE
 026F          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER TURNED M
 0271..0272    ; Changes_When_Uppercased # L&   [2] LATIN SMALL LETTER M WITH HOOK..LATIN SMALL LETTER N WITH LEFT HOOK
 0275          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER BARRED O
-027D          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER R WITH TAIL
+0277          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER CLOSED OMEGA
+027C..027D    ; Changes_When_Uppercased # L&   [2] LATIN SMALL LETTER R WITH LONG LEG..LATIN SMALL LETTER R WITH TAIL
 0280          ; Changes_When_Uppercased # L&       LATIN LETTER SMALL CAPITAL R
 0282..0283    ; Changes_When_Uppercased # L&   [2] LATIN SMALL LETTER S WITH HOOK..LATIN SMALL LETTER ESH
 0287..028C    ; Changes_When_Uppercased # L&   [6] LATIN SMALL LETTER TURNED T..LATIN SMALL LETTER TURNED V
@@ -4747,11 +4887,15 @@ A7C3          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER ANGLICANA
 A7C8          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER D WITH SHORT STROKE OVERLAY
 A7CA          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY
 A7CD          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER S WITH DIAGONAL STROKE
+A7CF          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER PHARYNGEAL VOICED FRICATIVE
 A7D1          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER CLOSED INSULAR G
+A7D3          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER DOUBLE THORN
+A7D5          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER DOUBLE WYNN
 A7D7          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER MIDDLE SCOTS S
 A7D9          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER SIGMOID S
 A7DB          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER LAMBDA
 A7F6          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER REVERSED HALF H
+AB4B..AB4C    ; Changes_When_Uppercased # L&   [2] LATIN SMALL LETTER SCRIPT R..LATIN SMALL LETTER SCRIPT R WITH RING
 AB53          ; Changes_When_Uppercased # L&       LATIN SMALL LETTER CHI
 AB70..ABBF    ; Changes_When_Uppercased # L&  [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
 FB00..FB06    ; Changes_When_Uppercased # L&   [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
@@ -4767,15 +4911,34 @@ FF41..FF5A    ; Changes_When_Uppercased # L&  [26] FULLWIDTH LATIN SMALL LETTER
 10D70..10D85  ; Changes_When_Uppercased # L&  [22] GARAY SMALL LETTER A..GARAY SMALL LETTER OLD NA
 118C0..118DF  ; Changes_When_Uppercased # L&  [32] WARANG CITI SMALL LETTER NGAA..WARANG CITI SMALL LETTER VIYO
 16E60..16E7F  ; Changes_When_Uppercased # L&  [32] MEDEFAIDRIN SMALL LETTER M..MEDEFAIDRIN SMALL LETTER Y
+16EBB..16ED3  ; Changes_When_Uppercased # L&  [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
+1DF41         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER BARRED A
+1DF49         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER BARRED K
+1DF4B         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER BARRED M
+1DF4E         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER BARRED N
+1DF52         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER BARRED V
+1DF69         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER PHONOTYPIC A WITH SWASH
+1DF6B         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER PHONOTYPIC ROUNDTOP A
+1DF6D         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER REVERSED SCRUPLE
+1DF6F         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER PHONOTYPIC DIPHTHONG AI
+1DF73         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER O WITH CURL
+1DF75         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER CLOSED OMEGA WITH LONG STEM
+1DF77         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER TURNED CLOSED OMEGA
+1DF79         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER PHONOTYPIC TH
+1DF7B         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER U WITH HOOK TAIL
+1DF7D         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER U WITH NOTCH AT BOTTOM
+1DF7F         ; Changes_When_Uppercased # L&       LATIN SMALL LETTER REVERSED U
+1DF95         ; Changes_When_Uppercased # L&       LATIN SMALL LIGATURE LONG S WITH DESCENDER S
 1E922..1E943  ; Changes_When_Uppercased # L&  [34] ADLAM SMALL LETTER ALIF..ADLAM SMALL LETTER SHA
 
-# Total code points: 1552
+# Total code points: 1601
 
 # ================================================
 
 # Derived Property:   Changes_When_Titlecased (CWT)
 #  Characters whose normalized forms are not stable under a toTitlecase mapping.
-#  For more information, see D141 in Section 3.13, "Default Case Algorithms".
+#  For more information, see the definition of "isTitlecase(X)"
+#  in the "Conformance" / "Default Case Algorithms" section of the core specification.
 #  Changes_When_Titlecased(X) is true when toTitlecase(toNFD(X)) != toNFD(X)
 
 0061..007A    ; Changes_When_Titlecased # L&  [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z
@@ -4932,7 +5095,8 @@ FF41..FF5A    ; Changes_When_Uppercased # L&  [26] FULLWIDTH LATIN SMALL LETTER
 026F          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER TURNED M
 0271..0272    ; Changes_When_Titlecased # L&   [2] LATIN SMALL LETTER M WITH HOOK..LATIN SMALL LETTER N WITH LEFT HOOK
 0275          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER BARRED O
-027D          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER R WITH TAIL
+0277          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER CLOSED OMEGA
+027C..027D    ; Changes_When_Titlecased # L&   [2] LATIN SMALL LETTER R WITH LONG LEG..LATIN SMALL LETTER R WITH TAIL
 0280          ; Changes_When_Titlecased # L&       LATIN LETTER SMALL CAPITAL R
 0282..0283    ; Changes_When_Titlecased # L&   [2] LATIN SMALL LETTER S WITH HOOK..LATIN SMALL LETTER ESH
 0287..028C    ; Changes_When_Titlecased # L&   [6] LATIN SMALL LETTER TURNED T..LATIN SMALL LETTER TURNED V
@@ -5386,11 +5550,15 @@ A7C3          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER ANGLICANA
 A7C8          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER D WITH SHORT STROKE OVERLAY
 A7CA          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY
 A7CD          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER S WITH DIAGONAL STROKE
+A7CF          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER PHARYNGEAL VOICED FRICATIVE
 A7D1          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER CLOSED INSULAR G
+A7D3          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER DOUBLE THORN
+A7D5          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER DOUBLE WYNN
 A7D7          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER MIDDLE SCOTS S
 A7D9          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER SIGMOID S
 A7DB          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER LAMBDA
 A7F6          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER REVERSED HALF H
+AB4B..AB4C    ; Changes_When_Titlecased # L&   [2] LATIN SMALL LETTER SCRIPT R..LATIN SMALL LETTER SCRIPT R WITH RING
 AB53          ; Changes_When_Titlecased # L&       LATIN SMALL LETTER CHI
 AB70..ABBF    ; Changes_When_Titlecased # L&  [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
 FB00..FB06    ; Changes_When_Titlecased # L&   [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
@@ -5406,15 +5574,34 @@ FF41..FF5A    ; Changes_When_Titlecased # L&  [26] FULLWIDTH LATIN SMALL LETTER
 10D70..10D85  ; Changes_When_Titlecased # L&  [22] GARAY SMALL LETTER A..GARAY SMALL LETTER OLD NA
 118C0..118DF  ; Changes_When_Titlecased # L&  [32] WARANG CITI SMALL LETTER NGAA..WARANG CITI SMALL LETTER VIYO
 16E60..16E7F  ; Changes_When_Titlecased # L&  [32] MEDEFAIDRIN SMALL LETTER M..MEDEFAIDRIN SMALL LETTER Y
+16EBB..16ED3  ; Changes_When_Titlecased # L&  [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
+1DF41         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER BARRED A
+1DF49         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER BARRED K
+1DF4B         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER BARRED M
+1DF4E         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER BARRED N
+1DF52         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER BARRED V
+1DF69         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER PHONOTYPIC A WITH SWASH
+1DF6B         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER PHONOTYPIC ROUNDTOP A
+1DF6D         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER REVERSED SCRUPLE
+1DF6F         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER PHONOTYPIC DIPHTHONG AI
+1DF73         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER O WITH CURL
+1DF75         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER CLOSED OMEGA WITH LONG STEM
+1DF77         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER TURNED CLOSED OMEGA
+1DF79         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER PHONOTYPIC TH
+1DF7B         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER U WITH HOOK TAIL
+1DF7D         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER U WITH NOTCH AT BOTTOM
+1DF7F         ; Changes_When_Titlecased # L&       LATIN SMALL LETTER REVERSED U
+1DF95         ; Changes_When_Titlecased # L&       LATIN SMALL LIGATURE LONG S WITH DESCENDER S
 1E922..1E943  ; Changes_When_Titlecased # L&  [34] ADLAM SMALL LETTER ALIF..ADLAM SMALL LETTER SHA
 
-# Total code points: 1479
+# Total code points: 1528
 
 # ================================================
 
 # Derived Property:   Changes_When_Casefolded (CWCF)
 #  Characters whose normalized forms are not stable under case folding.
-#  For more information, see D142 in Section 3.13, "Default Case Algorithms".
+#  For more information, see the definition of "isCasefolded(X)"
+#  in the "Conformance" / "Default Case Algorithms" section of the core specification.
 #  Changes_When_Casefolded(X) is true when toCasefold(toNFD(X)) != toNFD(X)
 
 0041..005A    ; Changes_When_Casefolded # L&  [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z
@@ -6022,12 +6209,17 @@ A7C2          ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER ANGLICAN
 A7C4..A7C7    ; Changes_When_Casefolded # L&   [4] LATIN CAPITAL LETTER C WITH PALATAL HOOK..LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY
 A7C9          ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER S WITH SHORT STROKE OVERLAY
 A7CB..A7CC    ; Changes_When_Casefolded # L&   [2] LATIN CAPITAL LETTER RAMS HORN..LATIN CAPITAL LETTER S WITH DIAGONAL STROKE
+A7CE          ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER PHARYNGEAL VOICED FRICATIVE
 A7D0          ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER CLOSED INSULAR G
+A7D2          ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER DOUBLE THORN
+A7D4          ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER DOUBLE WYNN
 A7D6          ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER MIDDLE SCOTS S
 A7D8          ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER SIGMOID S
 A7DA          ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER LAMBDA
-A7DC          ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER LAMBDA WITH STROKE
+A7DC..A7DD    ; Changes_When_Casefolded # L&   [2] LATIN CAPITAL LETTER LAMBDA WITH STROKE..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2          ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER R WITH LONG LEG
 A7F5          ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER REVERSED HALF H
+AB6C..AB6D    ; Changes_When_Casefolded # L&   [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 AB70..ABBF    ; Changes_When_Casefolded # L&  [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
 FB00..FB06    ; Changes_When_Casefolded # L&   [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
 FB13..FB17    ; Changes_When_Casefolded # L&   [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH
@@ -6042,15 +6234,34 @@ FF21..FF3A    ; Changes_When_Casefolded # L&  [26] FULLWIDTH LATIN CAPITAL LETTE
 10D50..10D65  ; Changes_When_Casefolded # L&  [22] GARAY CAPITAL LETTER A..GARAY CAPITAL LETTER OLD NA
 118A0..118BF  ; Changes_When_Casefolded # L&  [32] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI CAPITAL LETTER VIYO
 16E40..16E5F  ; Changes_When_Casefolded # L&  [32] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN CAPITAL LETTER Y
+16EA0..16EB8  ; Changes_When_Casefolded # L&  [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
+1DF40         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER BARRED A
+1DF48         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER BARRED K
+1DF4A         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER BARRED M
+1DF4D         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER BARRED N
+1DF51         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER BARRED V
+1DF68         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER PHONOTYPIC A WITH SWASH
+1DF6A         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER PHONOTYPIC ROUNDTOP A
+1DF6C         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER REVERSED SCRUPLE
+1DF6E         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER PHONOTYPIC DIPHTHONG AI
+1DF72         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER O WITH CURL
+1DF74         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER CLOSED OMEGA WITH LONG STEM
+1DF76         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER TURNED CLOSED OMEGA
+1DF78         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER PHONOTYPIC TH
+1DF7A         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER U WITH HOOK TAIL
+1DF7C         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER U WITH NOTCH AT BOTTOM
+1DF7E         ; Changes_When_Casefolded # L&       LATIN CAPITAL LETTER REVERSED U
+1DF95         ; Changes_When_Casefolded # L&       LATIN SMALL LIGATURE LONG S WITH DESCENDER S
 1E900..1E921  ; Changes_When_Casefolded # L&  [34] ADLAM CAPITAL LETTER ALIF..ADLAM CAPITAL LETTER SHA
 
-# Total code points: 1533
+# Total code points: 1582
 
 # ================================================
 
 # Derived Property:   Changes_When_Casemapped (CWCM)
 #  Characters whose normalized forms are not stable under case mapping.
-#  For more information, see D143 in Section 3.13, "Default Case Algorithms".
+#  For more information, see the definition of "isCased(X)"
+#  in the "Conformance" / "Default Case Algorithms" section of the core specification.
 #  Changes_When_Casemapped(X) is true when CWL(X), or CWT(X), or CWU(X)
 
 0041..005A    ; Changes_When_Casemapped # L&  [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z
@@ -6076,7 +6287,8 @@ FF21..FF3A    ; Changes_When_Casefolded # L&  [26] FULLWIDTH LATIN CAPITAL LETTE
 026F          ; Changes_When_Casemapped # L&       LATIN SMALL LETTER TURNED M
 0271..0272    ; Changes_When_Casemapped # L&   [2] LATIN SMALL LETTER M WITH HOOK..LATIN SMALL LETTER N WITH LEFT HOOK
 0275          ; Changes_When_Casemapped # L&       LATIN SMALL LETTER BARRED O
-027D          ; Changes_When_Casemapped # L&       LATIN SMALL LETTER R WITH TAIL
+0277          ; Changes_When_Casemapped # L&       LATIN SMALL LETTER CLOSED OMEGA
+027C..027D    ; Changes_When_Casemapped # L&   [2] LATIN SMALL LETTER R WITH LONG LEG..LATIN SMALL LETTER R WITH TAIL
 0280          ; Changes_When_Casemapped # L&       LATIN LETTER SMALL CAPITAL R
 0282..0283    ; Changes_When_Casemapped # L&   [2] LATIN SMALL LETTER S WITH HOOK..LATIN SMALL LETTER ESH
 0287..028C    ; Changes_When_Casemapped # L&   [6] LATIN SMALL LETTER TURNED T..LATIN SMALL LETTER TURNED V
@@ -6156,11 +6368,12 @@ A779..A787    ; Changes_When_Casemapped # L&  [15] LATIN CAPITAL LETTER INSULAR
 A78B..A78D    ; Changes_When_Casemapped # L&   [3] LATIN CAPITAL LETTER SALTILLO..LATIN CAPITAL LETTER TURNED H
 A790..A794    ; Changes_When_Casemapped # L&   [5] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER C WITH PALATAL HOOK
 A796..A7AE    ; Changes_When_Casemapped # L&  [25] LATIN CAPITAL LETTER B WITH FLOURISH..LATIN CAPITAL LETTER SMALL CAPITAL I
-A7B0..A7CD    ; Changes_When_Casemapped # L&  [30] LATIN CAPITAL LETTER TURNED K..LATIN SMALL LETTER S WITH DIAGONAL STROKE
-A7D0..A7D1    ; Changes_When_Casemapped # L&   [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G
-A7D6..A7DC    ; Changes_When_Casemapped # L&   [7] LATIN CAPITAL LETTER MIDDLE SCOTS S..LATIN CAPITAL LETTER LAMBDA WITH STROKE
+A7B0..A7DD    ; Changes_When_Casemapped # L&  [46] LATIN CAPITAL LETTER TURNED K..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2          ; Changes_When_Casemapped # L&       LATIN CAPITAL LETTER R WITH LONG LEG
 A7F5..A7F6    ; Changes_When_Casemapped # L&   [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H
+AB4B..AB4C    ; Changes_When_Casemapped # L&   [2] LATIN SMALL LETTER SCRIPT R..LATIN SMALL LETTER SCRIPT R WITH RING
 AB53          ; Changes_When_Casemapped # L&       LATIN SMALL LETTER CHI
+AB6C..AB6D    ; Changes_When_Casemapped # L&   [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 AB70..ABBF    ; Changes_When_Casemapped # L&  [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
 FB00..FB06    ; Changes_When_Casemapped # L&   [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
 FB13..FB17    ; Changes_When_Casemapped # L&   [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH
@@ -6183,9 +6396,18 @@ FF41..FF5A    ; Changes_When_Casemapped # L&  [26] FULLWIDTH LATIN SMALL LETTER
 10D70..10D85  ; Changes_When_Casemapped # L&  [22] GARAY SMALL LETTER A..GARAY SMALL LETTER OLD NA
 118A0..118DF  ; Changes_When_Casemapped # L&  [64] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI SMALL LETTER VIYO
 16E40..16E7F  ; Changes_When_Casemapped # L&  [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y
+16EA0..16EB8  ; Changes_When_Casemapped # L&  [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
+16EBB..16ED3  ; Changes_When_Casemapped # L&  [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
+1DF40..1DF41  ; Changes_When_Casemapped # L&   [2] LATIN CAPITAL LETTER BARRED A..LATIN SMALL LETTER BARRED A
+1DF48..1DF4B  ; Changes_When_Casemapped # L&   [4] LATIN CAPITAL LETTER BARRED K..LATIN SMALL LETTER BARRED M
+1DF4D..1DF4E  ; Changes_When_Casemapped # L&   [2] LATIN CAPITAL LETTER BARRED N..LATIN SMALL LETTER BARRED N
+1DF51..1DF52  ; Changes_When_Casemapped # L&   [2] LATIN CAPITAL LETTER BARRED V..LATIN SMALL LETTER BARRED V
+1DF68..1DF6F  ; Changes_When_Casemapped # L&   [8] LATIN CAPITAL LETTER PHONOTYPIC A WITH SWASH..LATIN SMALL LETTER PHONOTYPIC DIPHTHONG AI
+1DF72..1DF7F  ; Changes_When_Casemapped # L&  [14] LATIN CAPITAL LETTER O WITH CURL..LATIN SMALL LETTER REVERSED U
+1DF95         ; Changes_When_Casemapped # L&       LATIN SMALL LIGATURE LONG S WITH DESCENDER S
 1E900..1E943  ; Changes_When_Casemapped # L&  [68] ADLAM CAPITAL LETTER ALIF..ADLAM SMALL LETTER SHA
 
-# Total code points: 2981
+# Total code points: 3078
 
 # ================================================
 
@@ -6210,8 +6432,8 @@ FF41..FF5A    ; Changes_When_Casemapped # L&  [26] FULLWIDTH LATIN SMALL LETTER
 01BC..01BF    ; ID_Start # L&   [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN
 01C0..01C3    ; ID_Start # Lo   [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK
 01C4..0293    ; ID_Start # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL
-0294          ; ID_Start # Lo       LATIN LETTER GLOTTAL STOP
-0295..02AF    ; ID_Start # L&  [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+0294..0295    ; ID_Start # Lo   [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE
+0296..02AF    ; ID_Start # L&  [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
 02B0..02C1    ; ID_Start # Lm  [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP
 02C6..02D1    ; ID_Start # Lm  [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON
 02E0..02E4    ; ID_Start # Lm   [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
@@ -6231,8 +6453,9 @@ FF41..FF5A    ; Changes_When_Casemapped # L&  [26] FULLWIDTH LATIN SMALL LETTER
 03F7..0481    ; ID_Start # L& [139] GREEK CAPITAL LETTER SHO..CYRILLIC SMALL LETTER KOPPA
 048A..052F    ; ID_Start # L& [166] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER EL WITH DESCENDER
 0531..0556    ; ID_Start # L&  [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH
-0559          ; ID_Start # Lm       ARMENIAN MODIFIER LETTER LEFT HALF RING
+0558..0559    ; ID_Start # Lm   [2] MODIFIER LETTER ARMENIAN SMALL EH..ARMENIAN MODIFIER LETTER LEFT HALF RING
 0560..0588    ; ID_Start # L&  [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE
+058B..058C    ; ID_Start # Lm   [2] MODIFIER LETTER ARMENIAN SMALL INI..MODIFIER LETTER ARMENIAN SMALL YI
 05D0..05EA    ; ID_Start # Lo  [27] HEBREW LETTER ALEF..HEBREW LETTER TAV
 05EF..05F2    ; ID_Start # Lo   [4] HEBREW YOD TRIANGLE..HEBREW LIGATURE YIDDISH DOUBLE YOD
 0620..063F    ; ID_Start # Lo  [32] ARABIC LETTER KASHMIRI YEH..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE
@@ -6259,7 +6482,7 @@ FF41..FF5A    ; Changes_When_Casemapped # L&  [26] FULLWIDTH LATIN SMALL LETTER
 0840..0858    ; ID_Start # Lo  [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN
 0860..086A    ; ID_Start # Lo  [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA
 0870..0887    ; ID_Start # Lo  [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT
-0889..088E    ; ID_Start # Lo   [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL
+0889..088F    ; ID_Start # Lo   [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE
 08A0..08C8    ; ID_Start # Lo  [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF
 08C9          ; ID_Start # Lm       ARABIC SMALL FARSI YEH
 0904..0939    ; ID_Start # Lo  [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA
@@ -6327,7 +6550,7 @@ FF41..FF5A    ; Changes_When_Casemapped # L&  [26] FULLWIDTH LATIN SMALL LETTER
 0C2A..0C39    ; ID_Start # Lo  [16] TELUGU LETTER PA..TELUGU LETTER HA
 0C3D          ; ID_Start # Lo       TELUGU SIGN AVAGRAHA
 0C58..0C5A    ; ID_Start # Lo   [3] TELUGU LETTER TSA..TELUGU LETTER RRRA
-0C5D          ; ID_Start # Lo       TELUGU LETTER NAKAARA POLLU
+0C5C..0C5D    ; ID_Start # Lo   [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU
 0C60..0C61    ; ID_Start # Lo   [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
 0C80          ; ID_Start # Lo       KANNADA SIGN SPACING CANDRABINDU
 0C85..0C8C    ; ID_Start # Lo   [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L
@@ -6336,7 +6559,7 @@ FF41..FF5A    ; Changes_When_Casemapped # L&  [26] FULLWIDTH LATIN SMALL LETTER
 0CAA..0CB3    ; ID_Start # Lo  [10] KANNADA LETTER PA..KANNADA LETTER LLA
 0CB5..0CB9    ; ID_Start # Lo   [5] KANNADA LETTER VA..KANNADA LETTER HA
 0CBD          ; ID_Start # Lo       KANNADA SIGN AVAGRAHA
-0CDD..0CDE    ; ID_Start # Lo   [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA
+0CDC..0CDE    ; ID_Start # Lo   [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA
 0CE0..0CE1    ; ID_Start # Lo   [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
 0CF1..0CF2    ; ID_Start # Lo   [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA
 0D04..0D0C    ; ID_Start # Lo   [9] MALAYALAM LETTER VEDIC ANUSVARA..MALAYALAM LETTER VOCALIC L
@@ -6478,7 +6701,7 @@ FF41..FF5A    ; Changes_When_Casemapped # L&  [26] FULLWIDTH LATIN SMALL LETTER
 1FF6..1FFC    ; ID_Start # L&   [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
 2071          ; ID_Start # Lm       SUPERSCRIPT LATIN SMALL LETTER I
 207F          ; ID_Start # Lm       SUPERSCRIPT LATIN SMALL LETTER N
-2090..209C    ; ID_Start # Lm  [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T
+208F..209F    ; ID_Start # Lm  [17] MODIFIER LETTER HIGH AND LOW VERTICAL LINE..LATIN SUBSCRIPT SMALL LETTER Z
 2102          ; ID_Start # L&       DOUBLE-STRUCK CAPITAL C
 2107          ; ID_Start # L&       EULER CONSTANT
 210A..2113    ; ID_Start # L&  [10] SCRIPT SMALL G..SCRIPT SMALL L
@@ -6561,11 +6784,9 @@ A771..A787    ; ID_Start # L&  [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER I
 A788          ; ID_Start # Lm       MODIFIER LETTER LOW CIRCUMFLEX ACCENT
 A78B..A78E    ; ID_Start # L&   [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT
 A78F          ; ID_Start # Lo       LATIN LETTER SINOLOGICAL DOT
-A790..A7CD    ; ID_Start # L&  [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE
-A7D0..A7D1    ; ID_Start # L&   [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G
-A7D3          ; ID_Start # L&       LATIN SMALL LETTER DOUBLE THORN
-A7D5..A7DC    ; ID_Start # L&   [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE
-A7F2..A7F4    ; ID_Start # Lm   [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
+A790..A7DD    ; ID_Start # L&  [78] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2          ; ID_Start # L&       LATIN CAPITAL LETTER R WITH LONG LEG
+A7F1..A7F4    ; ID_Start # Lm   [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q
 A7F5..A7F6    ; ID_Start # L&   [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H
 A7F7          ; ID_Start # Lo       LATIN EPIGRAPHIC LETTER SIDEWAYS I
 A7F8..A7F9    ; ID_Start # Lm   [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
@@ -6615,6 +6836,7 @@ AB30..AB5A    ; ID_Start # L&  [43] LATIN SMALL LETTER BARRED ALPHA..LATIN SMALL
 AB5C..AB5F    ; ID_Start # Lm   [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK
 AB60..AB68    ; ID_Start # L&   [9] LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER TURNED R WITH MIDDLE TILDE
 AB69          ; ID_Start # Lm       MODIFIER LETTER SMALL TURNED W
+AB6C..AB6D    ; ID_Start # L&   [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 AB70..ABBF    ; ID_Start # L&  [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
 ABC0..ABE2    ; ID_Start # Lo  [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM
 AC00..D7A3    ; ID_Start # Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH
@@ -6689,7 +6911,7 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 10760..10767  ; ID_Start # Lo   [8] LINEAR A SIGN A800..LINEAR A SIGN A807
 10780..10785  ; ID_Start # Lm   [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK
 10787..107B0  ; ID_Start # Lm  [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
-107B2..107BA  ; ID_Start # Lm   [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
+107B2..107BF  ; ID_Start # Lm  [14] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL ESH WITH DOUBLE BAR
 10800..10805  ; ID_Start # Lo   [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA
 10808         ; ID_Start # Lo       CYPRIOT SYLLABLE JO
 1080A..10835  ; ID_Start # Lo  [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO
@@ -6702,6 +6924,7 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 108F4..108F5  ; ID_Start # Lo   [2] HATRAN LETTER SHIN..HATRAN LETTER TAW
 10900..10915  ; ID_Start # Lo  [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU
 10920..10939  ; ID_Start # Lo  [26] LYDIAN LETTER A..LYDIAN LETTER C
+10940..10959  ; ID_Start # Lo  [26] SIDETIC LETTER N01..SIDETIC LETTER N26
 10980..109B7  ; ID_Start # Lo  [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA
 109BE..109BF  ; ID_Start # Lo   [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN
 10A00         ; ID_Start # Lo       KHAROSHTHI LETTER A
@@ -6729,6 +6952,9 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 10E80..10EA9  ; ID_Start # Lo  [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET
 10EB0..10EB1  ; ID_Start # Lo   [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
 10EC2..10EC4  ; ID_Start # Lo   [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW
+10EC5         ; ID_Start # Lm       ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW
+10EC6..10EC7  ; ID_Start # Lo   [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW
+10ED9..10EEE  ; ID_Start # Lo  [22] ARABIC CROWN LETTER BEH..ARABIC CROWN LETTER YEH
 10F00..10F1C  ; ID_Start # Lo  [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL
 10F27         ; ID_Start # Lo       OLD SOGDIAN LIGATURE AYIN-DALETH
 10F30..10F45  ; ID_Start # Lo  [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN
@@ -6808,6 +7034,7 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 11A5C..11A89  ; ID_Start # Lo  [46] SOYOMBO LETTER KA..SOYOMBO CLUSTER-INITIAL LETTER SA
 11A9D         ; ID_Start # Lo       SOYOMBO MARK PLUTA
 11AB0..11AF8  ; ID_Start # Lo  [73] CANADIAN SYLLABICS NATTILIK HI..PAU CIN HAU GLOTTAL STOP FINAL
+11B0A         ; ID_Start # Lo       DEVANAGARI LETTER ALTERNATE DDDA
 11BC0..11BE0  ; ID_Start # Lo  [33] SUNUWAR LETTER DEVI..SUNUWAR LETTER KLOKO
 11C00..11C08  ; ID_Start # Lo   [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L
 11C0A..11C2E  ; ID_Start # Lo  [37] BHAIKSUKI LETTER E..BHAIKSUKI LETTER HA
@@ -6821,14 +7048,20 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 11D67..11D68  ; ID_Start # Lo   [2] GUNJALA GONDI LETTER EE..GUNJALA GONDI LETTER AI
 11D6A..11D89  ; ID_Start # Lo  [32] GUNJALA GONDI LETTER OO..GUNJALA GONDI LETTER SA
 11D98         ; ID_Start # Lo       GUNJALA GONDI OM
+11DB0..11DD8  ; ID_Start # Lo  [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH
+11DD9         ; ID_Start # Lm       TOLONG SIKI SIGN SELA
+11DDA..11DDB  ; ID_Start # Lo   [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA
+11DF1         ; ID_Start # Lo       BENGALI LETTER ALTERNATE BARGIYA BA
 11EE0..11EF2  ; ID_Start # Lo  [19] MAKASAR LETTER KA..MAKASAR ANGKA
 11F02         ; ID_Start # Lo       KAWI SIGN REPHA
 11F04..11F10  ; ID_Start # Lo  [13] KAWI LETTER A..KAWI LETTER O
 11F12..11F33  ; ID_Start # Lo  [34] KAWI LETTER KA..KAWI LETTER JNYA
 11FB0         ; ID_Start # Lo       LISU LETTER YHA
 12000..12399  ; ID_Start # Lo [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U
-12400..1246E  ; ID_Start # Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM
+12400..1246F  ; ID_Start # Nl [112] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN SEVEN ASH TENU
+12475..1247F  ; ID_Start # Nl  [11] CUNEIFORM NUMERIC SIGN EIGHT ASH TENU..CUNEIFORM NUMERIC SIGN ASH TIMES NINE DISH TENU
 12480..12543  ; ID_Start # Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU
+12550..12686  ; ID_Start # Nl [311] CUNEIFORM NUMERIC SIGN ONE N01..CUNEIFORM NUMERIC SIGN ONE N36 FLAT
 12F90..12FF0  ; ID_Start # Lo  [97] CYPRO-MINOAN SIGN CM001..CYPRO-MINOAN SIGN CM114
 13000..1342F  ; ID_Start # Lo [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D
 13441..13446  ; ID_Start # Lo   [6] EGYPTIAN HIEROGLYPH FULL BLANK..EGYPTIAN HIEROGLYPH WIDE LOST SIGN
@@ -6846,23 +7079,31 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 16D40..16D42  ; ID_Start # Lm   [3] KIRAT RAI SIGN ANUSVARA..KIRAT RAI SIGN VISARGA
 16D43..16D6A  ; ID_Start # Lo  [40] KIRAT RAI LETTER A..KIRAT RAI VOWEL SIGN AU
 16D6B..16D6C  ; ID_Start # Lm   [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT
+16D80..16D97  ; ID_Start # Lo  [24] CHISOI LETTER A..CHISOI LETTER PA
+16D99..16D9C  ; ID_Start # Lo   [4] CHISOI LETTER YA..CHISOI LETTER JARAHA
 16E40..16E7F  ; ID_Start # L&  [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y
+16EA0..16EB8  ; ID_Start # L&  [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
+16EBB..16ED3  ; ID_Start # L&  [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
 16F00..16F4A  ; ID_Start # Lo  [75] MIAO LETTER PA..MIAO LETTER RTE
 16F50         ; ID_Start # Lo       MIAO LETTER NASALIZATION
 16F93..16F9F  ; ID_Start # Lm  [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8
 16FE0..16FE1  ; ID_Start # Lm   [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK
 16FE3         ; ID_Start # Lm       OLD CHINESE ITERATION MARK
-17000..187F7  ; ID_Start # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
-18800..18CD5  ; ID_Start # Lo [1238] TANGUT COMPONENT-001..KHITAN SMALL SCRIPT CHARACTER-18CD5
-18CFF..18D08  ; ID_Start # Lo  [10] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D08
+16FF2..16FF3  ; ID_Start # Lm   [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER
+16FF4..16FF6  ; ID_Start # Nl   [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS
+17000..18CDA  ; ID_Start # Lo [7387] TANGUT IDEOGRAPH-17000..KHITAN SMALL SCRIPT CHARACTER-18CDA
+18CFF..18D20  ; ID_Start # Lo  [34] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D20
+18D80..18DF2  ; ID_Start # Lo [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883
+18E00..19191  ; ID_Start # Lo [914] JURCHEN CHARACTER-18E00..JURCHEN CHARACTER-19191
+191A0..191D2  ; ID_Start # Lo  [51] JURCHEN RADICAL-01..JURCHEN RADICAL-51
 1AFF0..1AFF3  ; ID_Start # Lm   [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5
 1AFF5..1AFFB  ; ID_Start # Lm   [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5
 1AFFD..1AFFE  ; ID_Start # Lm   [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8
-1B000..1B122  ; ID_Start # Lo [291] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ARCHAIC WU
+1B000..1B128  ; ID_Start # Lo [297] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ALTERNATE WI
 1B132         ; ID_Start # Lo       HIRAGANA LETTER SMALL KO
 1B150..1B152  ; ID_Start # Lo   [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO
 1B155         ; ID_Start # Lo       KATAKANA LETTER SMALL KO
-1B164..1B167  ; ID_Start # Lo   [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N
+1B164..1B168  ; ID_Start # Lo   [5] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL ARCHAIC YE
 1B170..1B2FB  ; ID_Start # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
 1BC00..1BC6A  ; ID_Start # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M
 1BC70..1BC7C  ; ID_Start # Lo  [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK
@@ -6886,7 +7127,7 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 1D540..1D544  ; ID_Start # L&   [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
 1D546         ; ID_Start # L&       MATHEMATICAL DOUBLE-STRUCK CAPITAL O
 1D54A..1D550  ; ID_Start # L&   [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
-1D552..1D6A5  ; ID_Start # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D552..1D6A6  ; ID_Start # L& [341] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL LIGATURE LONG S WITH DESCENDER S
 1D6A8..1D6C0  ; ID_Start # L&  [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
 1D6C2..1D6DA  ; ID_Start # L&  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
 1D6DC..1D6FA  ; ID_Start # L&  [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA
@@ -6900,8 +7141,10 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 1D7C4..1D7CB  ; ID_Start # L&   [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA
 1DF00..1DF09  ; ID_Start # L&  [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
 1DF0A         ; ID_Start # Lo       LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK
-1DF0B..1DF1E  ; ID_Start # L&  [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL
-1DF25..1DF2A  ; ID_Start # L&   [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK
+1DF0B..1DF7F  ; ID_Start # L& [117] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER REVERSED U
+1DF80..1DF81  ; ID_Start # Lo   [2] LATIN CAPITAL LETTER A WITH TOPBAR..LATIN CAPITAL LETTER E WITH BENT TOPBAR
+1DF90..1DF96  ; ID_Start # L&   [7] LATIN SMALL LETTER C WITH LOW SLASH..LATIN SMALL LETTER LONG S WITH TOP LOOP
+1DFCD..1DFFF  ; ID_Start # Lm  [51] MODIFIER LETTER SMALL TURNED R WITH MID-HEIGHT LEFT HOOK..MODIFIER LETTER SMALL T WITH HOOK AND RETROFLEX HOOK
 1E030..1E06D  ; ID_Start # Lm  [62] MODIFIER LETTER CYRILLIC SMALL A..MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE
 1E100..1E12C  ; ID_Start # Lo  [45] NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W
 1E137..1E13D  ; ID_Start # Lm   [7] NYIAKENG PUACHUE HMONG SIGN FOR PERSON..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER
@@ -6912,6 +7155,13 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 1E4EB         ; ID_Start # Lm       NAG MUNDARI SIGN OJOD
 1E5D0..1E5ED  ; ID_Start # Lo  [30] OL ONAL LETTER O..OL ONAL LETTER EG
 1E5F0         ; ID_Start # Lo       OL ONAL SIGN HODDOND
+1E6C0..1E6DE  ; ID_Start # Lo  [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO
+1E6E0..1E6E2  ; ID_Start # Lo   [3] TAI YO LETTER AA..TAI YO LETTER UE
+1E6E4..1E6E5  ; ID_Start # Lo   [2] TAI YO LETTER U..TAI YO LETTER AE
+1E6E7..1E6ED  ; ID_Start # Lo   [7] TAI YO LETTER O..TAI YO LETTER AUE
+1E6F0..1E6F4  ; ID_Start # Lo   [5] TAI YO LETTER AN..TAI YO LETTER AP
+1E6FE         ; ID_Start # Lo       TAI YO SYMBOL MUEANG
+1E6FF         ; ID_Start # Lm       TAI YO XAM LAI
 1E7E0..1E7E6  ; ID_Start # Lo   [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO
 1E7E8..1E7EB  ; ID_Start # Lo   [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE
 1E7ED..1E7EE  ; ID_Start # Lo   [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE
@@ -6953,16 +7203,16 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 1EEA5..1EEA9  ; ID_Start # Lo   [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH
 1EEAB..1EEBB  ; ID_Start # Lo  [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN
 20000..2A6DF  ; ID_Start # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF
-2A700..2B739  ; ID_Start # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739
-2B740..2B81D  ; ID_Start # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D
-2B820..2CEA1  ; ID_Start # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1
+2A700..2B81E  ; ID_Start # Lo [4383] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81E
+2B820..2CEAD  ; ID_Start # Lo [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD
 2CEB0..2EBE0  ; ID_Start # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
 2EBF0..2EE5D  ; ID_Start # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D
 2F800..2FA1D  ; ID_Start # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
 30000..3134A  ; ID_Start # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A
-31350..323AF  ; ID_Start # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF
+31350..33479  ; ID_Start # Lo [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479
+3D000..3FC3F  ; ID_Start # Lo [11328] SEAL CHARACTER-3D000..SEAL CHARACTER-3FC3F
 
-# Total code points: 141269
+# Total code points: 158767
 
 # ================================================
 
@@ -6991,8 +7241,8 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 01BC..01BF    ; ID_Continue # L&   [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN
 01C0..01C3    ; ID_Continue # Lo   [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK
 01C4..0293    ; ID_Continue # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL
-0294          ; ID_Continue # Lo       LATIN LETTER GLOTTAL STOP
-0295..02AF    ; ID_Continue # L&  [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+0294..0295    ; ID_Continue # Lo   [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE
+0296..02AF    ; ID_Continue # L&  [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
 02B0..02C1    ; ID_Continue # Lm  [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP
 02C6..02D1    ; ID_Continue # Lm  [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON
 02E0..02E4    ; ID_Continue # Lm   [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
@@ -7015,13 +7265,14 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 0483..0487    ; ID_Continue # Mn   [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE
 048A..052F    ; ID_Continue # L& [166] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER EL WITH DESCENDER
 0531..0556    ; ID_Continue # L&  [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH
-0559          ; ID_Continue # Lm       ARMENIAN MODIFIER LETTER LEFT HALF RING
+0558..0559    ; ID_Continue # Lm   [2] MODIFIER LETTER ARMENIAN SMALL EH..ARMENIAN MODIFIER LETTER LEFT HALF RING
 0560..0588    ; ID_Continue # L&  [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE
+058B..058C    ; ID_Continue # Lm   [2] MODIFIER LETTER ARMENIAN SMALL INI..MODIFIER LETTER ARMENIAN SMALL YI
 0591..05BD    ; ID_Continue # Mn  [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
 05BF          ; ID_Continue # Mn       HEBREW POINT RAFE
 05C1..05C2    ; ID_Continue # Mn   [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
 05C4..05C5    ; ID_Continue # Mn   [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
-05C7          ; ID_Continue # Mn       HEBREW POINT QAMATS QATAN
+05C7..05C9    ; ID_Continue # Mn   [3] HEBREW POINT QAMATS QATAN..HEBREW POINT DAGESH HAZAQ MUDGASH
 05D0..05EA    ; ID_Continue # Lo  [27] HEBREW LETTER ALEF..HEBREW LETTER TAV
 05EF..05F2    ; ID_Continue # Lo   [4] HEBREW YOD TRIANGLE..HEBREW LIGATURE YIDDISH DOUBLE YOD
 0610..061A    ; ID_Continue # Mn  [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
@@ -7068,7 +7319,7 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 0859..085B    ; ID_Continue # Mn   [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK
 0860..086A    ; ID_Continue # Lo  [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA
 0870..0887    ; ID_Continue # Lo  [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT
-0889..088E    ; ID_Continue # Lo   [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL
+0889..088F    ; ID_Continue # Lo   [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE
 0897..089F    ; ID_Continue # Mn   [9] ARABIC PEPET..ARABIC HALF MADDA OVER MADDA
 08A0..08C8    ; ID_Continue # Lo  [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF
 08C9          ; ID_Continue # Lm       ARABIC SMALL FARSI YEH
@@ -7176,7 +7427,7 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 0B47..0B48    ; ID_Continue # Mc   [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
 0B4B..0B4C    ; ID_Continue # Mc   [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
 0B4D          ; ID_Continue # Mn       ORIYA SIGN VIRAMA
-0B55..0B56    ; ID_Continue # Mn   [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK
+0B53..0B56    ; ID_Continue # Mn   [4] ORIYA SIGN DOT ABOVE..ORIYA AI LENGTH MARK
 0B57          ; ID_Continue # Mc       ORIYA AU LENGTH MARK
 0B5C..0B5D    ; ID_Continue # Lo   [2] ORIYA LETTER RRA..ORIYA LETTER RHA
 0B5F..0B61    ; ID_Continue # Lo   [3] ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL
@@ -7218,7 +7469,7 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 0C4A..0C4D    ; ID_Continue # Mn   [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
 0C55..0C56    ; ID_Continue # Mn   [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
 0C58..0C5A    ; ID_Continue # Lo   [3] TELUGU LETTER TSA..TELUGU LETTER RRRA
-0C5D          ; ID_Continue # Lo       TELUGU LETTER NAKAARA POLLU
+0C5C..0C5D    ; ID_Continue # Lo   [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU
 0C60..0C61    ; ID_Continue # Lo   [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
 0C62..0C63    ; ID_Continue # Mn   [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
 0C66..0C6F    ; ID_Continue # Nd  [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE
@@ -7240,7 +7491,7 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 0CCA..0CCB    ; ID_Continue # Mc   [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
 0CCC..0CCD    ; ID_Continue # Mn   [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA
 0CD5..0CD6    ; ID_Continue # Mc   [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
-0CDD..0CDE    ; ID_Continue # Lo   [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA
+0CDC..0CDE    ; ID_Continue # Lo   [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA
 0CE0..0CE1    ; ID_Continue # Lo   [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
 0CE2..0CE3    ; ID_Continue # Mn   [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL
 0CE6..0CEF    ; ID_Continue # Nd  [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE
@@ -7457,7 +7708,7 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 1A90..1A99    ; ID_Continue # Nd  [10] TAI THAM THAM DIGIT ZERO..TAI THAM THAM DIGIT NINE
 1AA7          ; ID_Continue # Lm       TAI THAM SIGN MAI YAMOK
 1AB0..1ABD    ; ID_Continue # Mn  [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
-1ABF..1ACE    ; ID_Continue # Mn  [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T
+1ABF..1AF0    ; ID_Continue # Mn  [50] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOUBLE COMMA ABOVE
 1B00..1B03    ; ID_Continue # Mn   [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
 1B04          ; ID_Continue # Mc       BALINESE SIGN BISAH
 1B05..1B33    ; ID_Continue # Lo  [47] BALINESE LETTER AKARA..BALINESE LETTER HA
@@ -7548,7 +7799,7 @@ FFDA..FFDC    ; ID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
 2054          ; ID_Continue # Pc       INVERTED UNDERTIE
 2071          ; ID_Continue # Lm       SUPERSCRIPT LATIN SMALL LETTER I
 207F          ; ID_Continue # Lm       SUPERSCRIPT LATIN SMALL LETTER N
-2090..209C    ; ID_Continue # Lm  [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T
+208F..209F    ; ID_Continue # Lm  [17] MODIFIER LETTER HIGH AND LOW VERTICAL LINE..LATIN SUBSCRIPT SMALL LETTER Z
 20D0..20DC    ; ID_Continue # Mn  [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE
 20E1          ; ID_Continue # Mn       COMBINING LEFT RIGHT ARROW ABOVE
 20E5..20F0    ; ID_Continue # Mn  [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE
@@ -7646,11 +7897,9 @@ A771..A787    ; ID_Continue # L&  [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTE
 A788          ; ID_Continue # Lm       MODIFIER LETTER LOW CIRCUMFLEX ACCENT
 A78B..A78E    ; ID_Continue # L&   [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT
 A78F          ; ID_Continue # Lo       LATIN LETTER SINOLOGICAL DOT
-A790..A7CD    ; ID_Continue # L&  [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE
-A7D0..A7D1    ; ID_Continue # L&   [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G
-A7D3          ; ID_Continue # L&       LATIN SMALL LETTER DOUBLE THORN
-A7D5..A7DC    ; ID_Continue # L&   [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE
-A7F2..A7F4    ; ID_Continue # Lm   [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
+A790..A7DD    ; ID_Continue # L&  [78] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2          ; ID_Continue # L&       LATIN CAPITAL LETTER R WITH LONG LEG
+A7F1..A7F4    ; ID_Continue # Lm   [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q
 A7F5..A7F6    ; ID_Continue # L&   [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H
 A7F7          ; ID_Continue # Lo       LATIN EPIGRAPHIC LETTER SIDEWAYS I
 A7F8..A7F9    ; ID_Continue # Lm   [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
@@ -7750,6 +7999,7 @@ AB30..AB5A    ; ID_Continue # L&  [43] LATIN SMALL LETTER BARRED ALPHA..LATIN SM
 AB5C..AB5F    ; ID_Continue # Lm   [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK
 AB60..AB68    ; ID_Continue # L&   [9] LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER TURNED R WITH MIDDLE TILDE
 AB69          ; ID_Continue # Lm       MODIFIER LETTER SMALL TURNED W
+AB6C..AB6D    ; ID_Continue # L&   [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 AB70..ABBF    ; ID_Continue # L&  [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
 ABC0..ABE2    ; ID_Continue # Lo  [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM
 ABE3..ABE4    ; ID_Continue # Mc   [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP
@@ -7844,7 +8094,7 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 10760..10767  ; ID_Continue # Lo   [8] LINEAR A SIGN A800..LINEAR A SIGN A807
 10780..10785  ; ID_Continue # Lm   [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK
 10787..107B0  ; ID_Continue # Lm  [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
-107B2..107BA  ; ID_Continue # Lm   [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
+107B2..107BF  ; ID_Continue # Lm  [14] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL ESH WITH DOUBLE BAR
 10800..10805  ; ID_Continue # Lo   [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA
 10808         ; ID_Continue # Lo       CYPRIOT SYLLABLE JO
 1080A..10835  ; ID_Continue # Lo  [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO
@@ -7857,6 +8107,7 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 108F4..108F5  ; ID_Continue # Lo   [2] HATRAN LETTER SHIN..HATRAN LETTER TAW
 10900..10915  ; ID_Continue # Lo  [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU
 10920..10939  ; ID_Continue # Lo  [26] LYDIAN LETTER A..LYDIAN LETTER C
+10940..10959  ; ID_Continue # Lo  [26] SIDETIC LETTER N01..SIDETIC LETTER N26
 10980..109B7  ; ID_Continue # Lo  [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA
 109BE..109BF  ; ID_Continue # Lo   [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN
 10A00         ; ID_Continue # Lo       KHAROSHTHI LETTER A
@@ -7895,7 +8146,11 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 10EAB..10EAC  ; ID_Continue # Mn   [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
 10EB0..10EB1  ; ID_Continue # Lo   [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
 10EC2..10EC4  ; ID_Continue # Lo   [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW
-10EFC..10EFF  ; ID_Continue # Mn   [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA
+10EC5         ; ID_Continue # Lm       ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW
+10EC6..10EC7  ; ID_Continue # Lo   [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW
+10ECB..10ECF  ; ID_Continue # Mn   [5] ARABIC NORTHEAST POINTING ARROWHEAD ABOVE..ARABIC LARGE CIRCLE ABOVE
+10ED9..10EEE  ; ID_Continue # Lo  [22] ARABIC CROWN LETTER BEH..ARABIC CROWN LETTER YEH
+10EF0..10EFF  ; ID_Continue # Mn  [16] ARABIC SMALL LOW UPRIGHT RECTANGULAR ZERO..ARABIC SMALL LOW WORD MADDA
 10F00..10F1C  ; ID_Continue # Lo  [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL
 10F27         ; ID_Continue # Lo       OLD SOGDIAN LIGATURE AYIN-DALETH
 10F30..10F45  ; ID_Continue # Lo  [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN
@@ -8122,6 +8377,13 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 11A98..11A99  ; ID_Continue # Mn   [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER
 11A9D         ; ID_Continue # Lo       SOYOMBO MARK PLUTA
 11AB0..11AF8  ; ID_Continue # Lo  [73] CANADIAN SYLLABICS NATTILIK HI..PAU CIN HAU GLOTTAL STOP FINAL
+11B0A         ; ID_Continue # Lo       DEVANAGARI LETTER ALTERNATE DDDA
+11B60         ; ID_Continue # Mn       SHARADA VOWEL SIGN OE
+11B61         ; ID_Continue # Mc       SHARADA VOWEL SIGN OOE
+11B62..11B64  ; ID_Continue # Mn   [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E
+11B65         ; ID_Continue # Mc       SHARADA VOWEL SIGN SHORT O
+11B66         ; ID_Continue # Mn       SHARADA VOWEL SIGN CANDRA E
+11B67         ; ID_Continue # Mc       SHARADA VOWEL SIGN CANDRA O
 11BC0..11BE0  ; ID_Continue # Lo  [33] SUNUWAR LETTER DEVI..SUNUWAR LETTER KLOKO
 11BF0..11BF9  ; ID_Continue # Nd  [10] SUNUWAR DIGIT ZERO..SUNUWAR DIGIT NINE
 11C00..11C08  ; ID_Continue # Lo   [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L
@@ -8162,6 +8424,12 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 11D97         ; ID_Continue # Mn       GUNJALA GONDI VIRAMA
 11D98         ; ID_Continue # Lo       GUNJALA GONDI OM
 11DA0..11DA9  ; ID_Continue # Nd  [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE
+11DB0..11DD8  ; ID_Continue # Lo  [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH
+11DD9         ; ID_Continue # Lm       TOLONG SIKI SIGN SELA
+11DDA..11DDB  ; ID_Continue # Lo   [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA
+11DE0..11DE9  ; ID_Continue # Nd  [10] TOLONG SIKI DIGIT ZERO..TOLONG SIKI DIGIT NINE
+11DF0         ; ID_Continue # Mn       BENGALI SIGN COMBINING ANUSVARA ABOVE
+11DF1         ; ID_Continue # Lo       BENGALI LETTER ALTERNATE BARGIYA BA
 11EE0..11EF2  ; ID_Continue # Lo  [19] MAKASAR LETTER KA..MAKASAR ANGKA
 11EF3..11EF4  ; ID_Continue # Mn   [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
 11EF5..11EF6  ; ID_Continue # Mc   [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
@@ -8180,8 +8448,10 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 11F5A         ; ID_Continue # Mn       KAWI SIGN NUKTA
 11FB0         ; ID_Continue # Lo       LISU LETTER YHA
 12000..12399  ; ID_Continue # Lo [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U
-12400..1246E  ; ID_Continue # Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM
+12400..1246F  ; ID_Continue # Nl [112] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN SEVEN ASH TENU
+12475..1247F  ; ID_Continue # Nl  [11] CUNEIFORM NUMERIC SIGN EIGHT ASH TENU..CUNEIFORM NUMERIC SIGN ASH TIMES NINE DISH TENU
 12480..12543  ; ID_Continue # Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU
+12550..12686  ; ID_Continue # Nl [311] CUNEIFORM NUMERIC SIGN ONE N01..CUNEIFORM NUMERIC SIGN ONE N36 FLAT
 12F90..12FF0  ; ID_Continue # Lo  [97] CYPRO-MINOAN SIGN CM001..CYPRO-MINOAN SIGN CM114
 13000..1342F  ; ID_Continue # Lo [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D
 13440         ; ID_Continue # Mn       EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY
@@ -8211,7 +8481,14 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 16D43..16D6A  ; ID_Continue # Lo  [40] KIRAT RAI LETTER A..KIRAT RAI VOWEL SIGN AU
 16D6B..16D6C  ; ID_Continue # Lm   [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT
 16D70..16D79  ; ID_Continue # Nd  [10] KIRAT RAI DIGIT ZERO..KIRAT RAI DIGIT NINE
+16D80..16D97  ; ID_Continue # Lo  [24] CHISOI LETTER A..CHISOI LETTER PA
+16D98         ; ID_Continue # Mn       CHISOI SIGN ANUSVARA
+16D99..16D9C  ; ID_Continue # Lo   [4] CHISOI LETTER YA..CHISOI LETTER JARAHA
+16D9D         ; ID_Continue # Mn       CHISOI SIGN SISO
+16DA0..16DA9  ; ID_Continue # Nd  [10] CHISOI DIGIT ZERO..CHISOI DIGIT NINE
 16E40..16E7F  ; ID_Continue # L&  [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y
+16EA0..16EB8  ; ID_Continue # L&  [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
+16EBB..16ED3  ; ID_Continue # L&  [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
 16F00..16F4A  ; ID_Continue # Lo  [75] MIAO LETTER PA..MIAO LETTER RTE
 16F4F         ; ID_Continue # Mn       MIAO SIGN CONSONANT MODIFIER BAR
 16F50         ; ID_Continue # Lo       MIAO LETTER NASALIZATION
@@ -8222,17 +8499,21 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 16FE3         ; ID_Continue # Lm       OLD CHINESE ITERATION MARK
 16FE4         ; ID_Continue # Mn       KHITAN SMALL SCRIPT FILLER
 16FF0..16FF1  ; ID_Continue # Mc   [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY
-17000..187F7  ; ID_Continue # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
-18800..18CD5  ; ID_Continue # Lo [1238] TANGUT COMPONENT-001..KHITAN SMALL SCRIPT CHARACTER-18CD5
-18CFF..18D08  ; ID_Continue # Lo  [10] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D08
+16FF2..16FF3  ; ID_Continue # Lm   [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER
+16FF4..16FF6  ; ID_Continue # Nl   [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS
+17000..18CDA  ; ID_Continue # Lo [7387] TANGUT IDEOGRAPH-17000..KHITAN SMALL SCRIPT CHARACTER-18CDA
+18CFF..18D20  ; ID_Continue # Lo  [34] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D20
+18D80..18DF2  ; ID_Continue # Lo [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883
+18E00..19191  ; ID_Continue # Lo [914] JURCHEN CHARACTER-18E00..JURCHEN CHARACTER-19191
+191A0..191D2  ; ID_Continue # Lo  [51] JURCHEN RADICAL-01..JURCHEN RADICAL-51
 1AFF0..1AFF3  ; ID_Continue # Lm   [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5
 1AFF5..1AFFB  ; ID_Continue # Lm   [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5
 1AFFD..1AFFE  ; ID_Continue # Lm   [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8
-1B000..1B122  ; ID_Continue # Lo [291] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ARCHAIC WU
+1B000..1B128  ; ID_Continue # Lo [297] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ALTERNATE WI
 1B132         ; ID_Continue # Lo       HIRAGANA LETTER SMALL KO
 1B150..1B152  ; ID_Continue # Lo   [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO
 1B155         ; ID_Continue # Lo       KATAKANA LETTER SMALL KO
-1B164..1B167  ; ID_Continue # Lo   [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N
+1B164..1B168  ; ID_Continue # Lo   [5] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL ARCHAIC YE
 1B170..1B2FB  ; ID_Continue # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
 1BC00..1BC6A  ; ID_Continue # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M
 1BC70..1BC7C  ; ID_Continue # Lo  [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK
@@ -8242,6 +8523,7 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 1CCF0..1CCF9  ; ID_Continue # Nd  [10] OUTLINED DIGIT ZERO..OUTLINED DIGIT NINE
 1CF00..1CF2D  ; ID_Continue # Mn  [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT
 1CF30..1CF46  ; ID_Continue # Mn  [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG
+1D127..1D128  ; ID_Continue # Mn   [2] MUSICAL SYMBOL COMBINING STRESS..MUSICAL SYMBOL COMBINING UNSTRESS
 1D165..1D166  ; ID_Continue # Mc   [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
 1D167..1D169  ; ID_Continue # Mn   [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
 1D16D..1D172  ; ID_Continue # Mc   [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5
@@ -8249,6 +8531,10 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 1D185..1D18B  ; ID_Continue # Mn   [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
 1D1AA..1D1AD  ; ID_Continue # Mn   [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
 1D242..1D244  ; ID_Continue # Mn   [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+1D250..1D252  ; ID_Continue # Mc   [3] MUSICAL SYMBOL COMBINING FLAG-6..MUSICAL SYMBOL COMBINING FLAG-8
+1D25B..1D25C  ; ID_Continue # Mn   [2] MUSICAL SYMBOL COMBINING TREMOLO-4..MUSICAL SYMBOL COMBINING TREMOLO-5
+1D25F         ; ID_Continue # Mc       MUSICAL SYMBOL COMBINING BUZZ ROLL STEM
+1D280..1D281  ; ID_Continue # Mc   [2] MUSICAL SYMBOL COMBINING STEM BOW BEHIND BRIDGE..MUSICAL SYMBOL COMBINING STEM BOW ON TOP OF BRIDGE
 1D400..1D454  ; ID_Continue # L&  [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G
 1D456..1D49C  ; ID_Continue # L&  [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A
 1D49E..1D49F  ; ID_Continue # L&   [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D
@@ -8267,7 +8553,7 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 1D540..1D544  ; ID_Continue # L&   [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
 1D546         ; ID_Continue # L&       MATHEMATICAL DOUBLE-STRUCK CAPITAL O
 1D54A..1D550  ; ID_Continue # L&   [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
-1D552..1D6A5  ; ID_Continue # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D552..1D6A6  ; ID_Continue # L& [341] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL LIGATURE LONG S WITH DESCENDER S
 1D6A8..1D6C0  ; ID_Continue # L&  [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
 1D6C2..1D6DA  ; ID_Continue # L&  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
 1D6DC..1D6FA  ; ID_Continue # L&  [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA
@@ -8288,8 +8574,10 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 1DAA1..1DAAF  ; ID_Continue # Mn  [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16
 1DF00..1DF09  ; ID_Continue # L&  [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
 1DF0A         ; ID_Continue # Lo       LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK
-1DF0B..1DF1E  ; ID_Continue # L&  [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL
-1DF25..1DF2A  ; ID_Continue # L&   [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK
+1DF0B..1DF7F  ; ID_Continue # L& [117] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER REVERSED U
+1DF80..1DF81  ; ID_Continue # Lo   [2] LATIN CAPITAL LETTER A WITH TOPBAR..LATIN CAPITAL LETTER E WITH BENT TOPBAR
+1DF90..1DF96  ; ID_Continue # L&   [7] LATIN SMALL LETTER C WITH LOW SLASH..LATIN SMALL LETTER LONG S WITH TOP LOOP
+1DFCD..1DFFF  ; ID_Continue # Lm  [51] MODIFIER LETTER SMALL TURNED R WITH MID-HEIGHT LEFT HOOK..MODIFIER LETTER SMALL T WITH HOOK AND RETROFLEX HOOK
 1E000..1E006  ; ID_Continue # Mn   [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
 1E008..1E018  ; ID_Continue # Mn  [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
 1E01B..1E021  ; ID_Continue # Mn   [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
@@ -8315,6 +8603,17 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 1E5EE..1E5EF  ; ID_Continue # Mn   [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR
 1E5F0         ; ID_Continue # Lo       OL ONAL SIGN HODDOND
 1E5F1..1E5FA  ; ID_Continue # Nd  [10] OL ONAL DIGIT ZERO..OL ONAL DIGIT NINE
+1E6C0..1E6DE  ; ID_Continue # Lo  [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO
+1E6E0..1E6E2  ; ID_Continue # Lo   [3] TAI YO LETTER AA..TAI YO LETTER UE
+1E6E3         ; ID_Continue # Mn       TAI YO SIGN UE
+1E6E4..1E6E5  ; ID_Continue # Lo   [2] TAI YO LETTER U..TAI YO LETTER AE
+1E6E6         ; ID_Continue # Mn       TAI YO SIGN AU
+1E6E7..1E6ED  ; ID_Continue # Lo   [7] TAI YO LETTER O..TAI YO LETTER AUE
+1E6EE..1E6EF  ; ID_Continue # Mn   [2] TAI YO SIGN AY..TAI YO SIGN ANG
+1E6F0..1E6F4  ; ID_Continue # Lo   [5] TAI YO LETTER AN..TAI YO LETTER AP
+1E6F5         ; ID_Continue # Mn       TAI YO SIGN OM
+1E6FE         ; ID_Continue # Lo       TAI YO SYMBOL MUEANG
+1E6FF         ; ID_Continue # Lm       TAI YO XAM LAI
 1E7E0..1E7E6  ; ID_Continue # Lo   [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO
 1E7E8..1E7EB  ; ID_Continue # Lo   [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE
 1E7ED..1E7EE  ; ID_Continue # Lo   [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE
@@ -8360,17 +8659,17 @@ FFDA..FFDC    ; ID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HAN
 1EEAB..1EEBB  ; ID_Continue # Lo  [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN
 1FBF0..1FBF9  ; ID_Continue # Nd  [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE
 20000..2A6DF  ; ID_Continue # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF
-2A700..2B739  ; ID_Continue # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739
-2B740..2B81D  ; ID_Continue # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D
-2B820..2CEA1  ; ID_Continue # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1
+2A700..2B81E  ; ID_Continue # Lo [4383] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81E
+2B820..2CEAD  ; ID_Continue # Lo [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD
 2CEB0..2EBE0  ; ID_Continue # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
 2EBF0..2EE5D  ; ID_Continue # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D
 2F800..2FA1D  ; ID_Continue # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
 30000..3134A  ; ID_Continue # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A
-31350..323AF  ; ID_Continue # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF
+31350..33479  ; ID_Continue # Lo [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479
+3D000..3FC3F  ; ID_Continue # Lo [11328] SEAL CHARACTER-3D000..SEAL CHARACTER-3FC3F
 E0100..E01EF  ; ID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
 
-# Total code points: 144541
+# Total code points: 162140
 
 # ================================================
 
@@ -8393,8 +8692,8 @@ E0100..E01EF  ; ID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR
 01BC..01BF    ; XID_Start # L&   [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN
 01C0..01C3    ; XID_Start # Lo   [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK
 01C4..0293    ; XID_Start # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL
-0294          ; XID_Start # Lo       LATIN LETTER GLOTTAL STOP
-0295..02AF    ; XID_Start # L&  [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+0294..0295    ; XID_Start # Lo   [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE
+0296..02AF    ; XID_Start # L&  [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
 02B0..02C1    ; XID_Start # Lm  [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP
 02C6..02D1    ; XID_Start # Lm  [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON
 02E0..02E4    ; XID_Start # Lm   [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
@@ -8413,8 +8712,9 @@ E0100..E01EF  ; ID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR
 03F7..0481    ; XID_Start # L& [139] GREEK CAPITAL LETTER SHO..CYRILLIC SMALL LETTER KOPPA
 048A..052F    ; XID_Start # L& [166] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER EL WITH DESCENDER
 0531..0556    ; XID_Start # L&  [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH
-0559          ; XID_Start # Lm       ARMENIAN MODIFIER LETTER LEFT HALF RING
+0558..0559    ; XID_Start # Lm   [2] MODIFIER LETTER ARMENIAN SMALL EH..ARMENIAN MODIFIER LETTER LEFT HALF RING
 0560..0588    ; XID_Start # L&  [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE
+058B..058C    ; XID_Start # Lm   [2] MODIFIER LETTER ARMENIAN SMALL INI..MODIFIER LETTER ARMENIAN SMALL YI
 05D0..05EA    ; XID_Start # Lo  [27] HEBREW LETTER ALEF..HEBREW LETTER TAV
 05EF..05F2    ; XID_Start # Lo   [4] HEBREW YOD TRIANGLE..HEBREW LIGATURE YIDDISH DOUBLE YOD
 0620..063F    ; XID_Start # Lo  [32] ARABIC LETTER KASHMIRI YEH..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE
@@ -8441,7 +8741,7 @@ E0100..E01EF  ; ID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR
 0840..0858    ; XID_Start # Lo  [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN
 0860..086A    ; XID_Start # Lo  [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA
 0870..0887    ; XID_Start # Lo  [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT
-0889..088E    ; XID_Start # Lo   [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL
+0889..088F    ; XID_Start # Lo   [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE
 08A0..08C8    ; XID_Start # Lo  [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF
 08C9          ; XID_Start # Lm       ARABIC SMALL FARSI YEH
 0904..0939    ; XID_Start # Lo  [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA
@@ -8509,7 +8809,7 @@ E0100..E01EF  ; ID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR
 0C2A..0C39    ; XID_Start # Lo  [16] TELUGU LETTER PA..TELUGU LETTER HA
 0C3D          ; XID_Start # Lo       TELUGU SIGN AVAGRAHA
 0C58..0C5A    ; XID_Start # Lo   [3] TELUGU LETTER TSA..TELUGU LETTER RRRA
-0C5D          ; XID_Start # Lo       TELUGU LETTER NAKAARA POLLU
+0C5C..0C5D    ; XID_Start # Lo   [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU
 0C60..0C61    ; XID_Start # Lo   [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
 0C80          ; XID_Start # Lo       KANNADA SIGN SPACING CANDRABINDU
 0C85..0C8C    ; XID_Start # Lo   [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L
@@ -8518,7 +8818,7 @@ E0100..E01EF  ; ID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR
 0CAA..0CB3    ; XID_Start # Lo  [10] KANNADA LETTER PA..KANNADA LETTER LLA
 0CB5..0CB9    ; XID_Start # Lo   [5] KANNADA LETTER VA..KANNADA LETTER HA
 0CBD          ; XID_Start # Lo       KANNADA SIGN AVAGRAHA
-0CDD..0CDE    ; XID_Start # Lo   [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA
+0CDC..0CDE    ; XID_Start # Lo   [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA
 0CE0..0CE1    ; XID_Start # Lo   [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
 0CF1..0CF2    ; XID_Start # Lo   [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA
 0D04..0D0C    ; XID_Start # Lo   [9] MALAYALAM LETTER VEDIC ANUSVARA..MALAYALAM LETTER VOCALIC L
@@ -8660,7 +8960,7 @@ E0100..E01EF  ; ID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR
 1FF6..1FFC    ; XID_Start # L&   [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
 2071          ; XID_Start # Lm       SUPERSCRIPT LATIN SMALL LETTER I
 207F          ; XID_Start # Lm       SUPERSCRIPT LATIN SMALL LETTER N
-2090..209C    ; XID_Start # Lm  [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T
+208F..209F    ; XID_Start # Lm  [17] MODIFIER LETTER HIGH AND LOW VERTICAL LINE..LATIN SUBSCRIPT SMALL LETTER Z
 2102          ; XID_Start # L&       DOUBLE-STRUCK CAPITAL C
 2107          ; XID_Start # L&       EULER CONSTANT
 210A..2113    ; XID_Start # L&  [10] SCRIPT SMALL G..SCRIPT SMALL L
@@ -8742,11 +9042,9 @@ A771..A787    ; XID_Start # L&  [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER
 A788          ; XID_Start # Lm       MODIFIER LETTER LOW CIRCUMFLEX ACCENT
 A78B..A78E    ; XID_Start # L&   [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT
 A78F          ; XID_Start # Lo       LATIN LETTER SINOLOGICAL DOT
-A790..A7CD    ; XID_Start # L&  [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE
-A7D0..A7D1    ; XID_Start # L&   [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G
-A7D3          ; XID_Start # L&       LATIN SMALL LETTER DOUBLE THORN
-A7D5..A7DC    ; XID_Start # L&   [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE
-A7F2..A7F4    ; XID_Start # Lm   [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
+A790..A7DD    ; XID_Start # L&  [78] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2          ; XID_Start # L&       LATIN CAPITAL LETTER R WITH LONG LEG
+A7F1..A7F4    ; XID_Start # Lm   [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q
 A7F5..A7F6    ; XID_Start # L&   [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H
 A7F7          ; XID_Start # Lo       LATIN EPIGRAPHIC LETTER SIDEWAYS I
 A7F8..A7F9    ; XID_Start # Lm   [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
@@ -8796,6 +9094,7 @@ AB30..AB5A    ; XID_Start # L&  [43] LATIN SMALL LETTER BARRED ALPHA..LATIN SMAL
 AB5C..AB5F    ; XID_Start # Lm   [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK
 AB60..AB68    ; XID_Start # L&   [9] LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER TURNED R WITH MIDDLE TILDE
 AB69          ; XID_Start # Lm       MODIFIER LETTER SMALL TURNED W
+AB6C..AB6D    ; XID_Start # L&   [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 AB70..ABBF    ; XID_Start # L&  [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
 ABC0..ABE2    ; XID_Start # Lo  [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM
 AC00..D7A3    ; XID_Start # Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH
@@ -8875,7 +9174,7 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 10760..10767  ; XID_Start # Lo   [8] LINEAR A SIGN A800..LINEAR A SIGN A807
 10780..10785  ; XID_Start # Lm   [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK
 10787..107B0  ; XID_Start # Lm  [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
-107B2..107BA  ; XID_Start # Lm   [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
+107B2..107BF  ; XID_Start # Lm  [14] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL ESH WITH DOUBLE BAR
 10800..10805  ; XID_Start # Lo   [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA
 10808         ; XID_Start # Lo       CYPRIOT SYLLABLE JO
 1080A..10835  ; XID_Start # Lo  [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO
@@ -8888,6 +9187,7 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 108F4..108F5  ; XID_Start # Lo   [2] HATRAN LETTER SHIN..HATRAN LETTER TAW
 10900..10915  ; XID_Start # Lo  [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU
 10920..10939  ; XID_Start # Lo  [26] LYDIAN LETTER A..LYDIAN LETTER C
+10940..10959  ; XID_Start # Lo  [26] SIDETIC LETTER N01..SIDETIC LETTER N26
 10980..109B7  ; XID_Start # Lo  [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA
 109BE..109BF  ; XID_Start # Lo   [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN
 10A00         ; XID_Start # Lo       KHAROSHTHI LETTER A
@@ -8915,6 +9215,9 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 10E80..10EA9  ; XID_Start # Lo  [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET
 10EB0..10EB1  ; XID_Start # Lo   [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
 10EC2..10EC4  ; XID_Start # Lo   [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW
+10EC5         ; XID_Start # Lm       ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW
+10EC6..10EC7  ; XID_Start # Lo   [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW
+10ED9..10EEE  ; XID_Start # Lo  [22] ARABIC CROWN LETTER BEH..ARABIC CROWN LETTER YEH
 10F00..10F1C  ; XID_Start # Lo  [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL
 10F27         ; XID_Start # Lo       OLD SOGDIAN LIGATURE AYIN-DALETH
 10F30..10F45  ; XID_Start # Lo  [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN
@@ -8994,6 +9297,7 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 11A5C..11A89  ; XID_Start # Lo  [46] SOYOMBO LETTER KA..SOYOMBO CLUSTER-INITIAL LETTER SA
 11A9D         ; XID_Start # Lo       SOYOMBO MARK PLUTA
 11AB0..11AF8  ; XID_Start # Lo  [73] CANADIAN SYLLABICS NATTILIK HI..PAU CIN HAU GLOTTAL STOP FINAL
+11B0A         ; XID_Start # Lo       DEVANAGARI LETTER ALTERNATE DDDA
 11BC0..11BE0  ; XID_Start # Lo  [33] SUNUWAR LETTER DEVI..SUNUWAR LETTER KLOKO
 11C00..11C08  ; XID_Start # Lo   [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L
 11C0A..11C2E  ; XID_Start # Lo  [37] BHAIKSUKI LETTER E..BHAIKSUKI LETTER HA
@@ -9007,14 +9311,20 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 11D67..11D68  ; XID_Start # Lo   [2] GUNJALA GONDI LETTER EE..GUNJALA GONDI LETTER AI
 11D6A..11D89  ; XID_Start # Lo  [32] GUNJALA GONDI LETTER OO..GUNJALA GONDI LETTER SA
 11D98         ; XID_Start # Lo       GUNJALA GONDI OM
+11DB0..11DD8  ; XID_Start # Lo  [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH
+11DD9         ; XID_Start # Lm       TOLONG SIKI SIGN SELA
+11DDA..11DDB  ; XID_Start # Lo   [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA
+11DF1         ; XID_Start # Lo       BENGALI LETTER ALTERNATE BARGIYA BA
 11EE0..11EF2  ; XID_Start # Lo  [19] MAKASAR LETTER KA..MAKASAR ANGKA
 11F02         ; XID_Start # Lo       KAWI SIGN REPHA
 11F04..11F10  ; XID_Start # Lo  [13] KAWI LETTER A..KAWI LETTER O
 11F12..11F33  ; XID_Start # Lo  [34] KAWI LETTER KA..KAWI LETTER JNYA
 11FB0         ; XID_Start # Lo       LISU LETTER YHA
 12000..12399  ; XID_Start # Lo [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U
-12400..1246E  ; XID_Start # Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM
+12400..1246F  ; XID_Start # Nl [112] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN SEVEN ASH TENU
+12475..1247F  ; XID_Start # Nl  [11] CUNEIFORM NUMERIC SIGN EIGHT ASH TENU..CUNEIFORM NUMERIC SIGN ASH TIMES NINE DISH TENU
 12480..12543  ; XID_Start # Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU
+12550..12686  ; XID_Start # Nl [311] CUNEIFORM NUMERIC SIGN ONE N01..CUNEIFORM NUMERIC SIGN ONE N36 FLAT
 12F90..12FF0  ; XID_Start # Lo  [97] CYPRO-MINOAN SIGN CM001..CYPRO-MINOAN SIGN CM114
 13000..1342F  ; XID_Start # Lo [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D
 13441..13446  ; XID_Start # Lo   [6] EGYPTIAN HIEROGLYPH FULL BLANK..EGYPTIAN HIEROGLYPH WIDE LOST SIGN
@@ -9032,23 +9342,31 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 16D40..16D42  ; XID_Start # Lm   [3] KIRAT RAI SIGN ANUSVARA..KIRAT RAI SIGN VISARGA
 16D43..16D6A  ; XID_Start # Lo  [40] KIRAT RAI LETTER A..KIRAT RAI VOWEL SIGN AU
 16D6B..16D6C  ; XID_Start # Lm   [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT
+16D80..16D97  ; XID_Start # Lo  [24] CHISOI LETTER A..CHISOI LETTER PA
+16D99..16D9C  ; XID_Start # Lo   [4] CHISOI LETTER YA..CHISOI LETTER JARAHA
 16E40..16E7F  ; XID_Start # L&  [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y
+16EA0..16EB8  ; XID_Start # L&  [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
+16EBB..16ED3  ; XID_Start # L&  [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
 16F00..16F4A  ; XID_Start # Lo  [75] MIAO LETTER PA..MIAO LETTER RTE
 16F50         ; XID_Start # Lo       MIAO LETTER NASALIZATION
 16F93..16F9F  ; XID_Start # Lm  [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8
 16FE0..16FE1  ; XID_Start # Lm   [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK
 16FE3         ; XID_Start # Lm       OLD CHINESE ITERATION MARK
-17000..187F7  ; XID_Start # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
-18800..18CD5  ; XID_Start # Lo [1238] TANGUT COMPONENT-001..KHITAN SMALL SCRIPT CHARACTER-18CD5
-18CFF..18D08  ; XID_Start # Lo  [10] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D08
+16FF2..16FF3  ; XID_Start # Lm   [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER
+16FF4..16FF6  ; XID_Start # Nl   [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS
+17000..18CDA  ; XID_Start # Lo [7387] TANGUT IDEOGRAPH-17000..KHITAN SMALL SCRIPT CHARACTER-18CDA
+18CFF..18D20  ; XID_Start # Lo  [34] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D20
+18D80..18DF2  ; XID_Start # Lo [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883
+18E00..19191  ; XID_Start # Lo [914] JURCHEN CHARACTER-18E00..JURCHEN CHARACTER-19191
+191A0..191D2  ; XID_Start # Lo  [51] JURCHEN RADICAL-01..JURCHEN RADICAL-51
 1AFF0..1AFF3  ; XID_Start # Lm   [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5
 1AFF5..1AFFB  ; XID_Start # Lm   [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5
 1AFFD..1AFFE  ; XID_Start # Lm   [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8
-1B000..1B122  ; XID_Start # Lo [291] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ARCHAIC WU
+1B000..1B128  ; XID_Start # Lo [297] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ALTERNATE WI
 1B132         ; XID_Start # Lo       HIRAGANA LETTER SMALL KO
 1B150..1B152  ; XID_Start # Lo   [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO
 1B155         ; XID_Start # Lo       KATAKANA LETTER SMALL KO
-1B164..1B167  ; XID_Start # Lo   [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N
+1B164..1B168  ; XID_Start # Lo   [5] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL ARCHAIC YE
 1B170..1B2FB  ; XID_Start # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
 1BC00..1BC6A  ; XID_Start # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M
 1BC70..1BC7C  ; XID_Start # Lo  [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK
@@ -9072,7 +9390,7 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 1D540..1D544  ; XID_Start # L&   [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
 1D546         ; XID_Start # L&       MATHEMATICAL DOUBLE-STRUCK CAPITAL O
 1D54A..1D550  ; XID_Start # L&   [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
-1D552..1D6A5  ; XID_Start # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D552..1D6A6  ; XID_Start # L& [341] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL LIGATURE LONG S WITH DESCENDER S
 1D6A8..1D6C0  ; XID_Start # L&  [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
 1D6C2..1D6DA  ; XID_Start # L&  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
 1D6DC..1D6FA  ; XID_Start # L&  [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA
@@ -9086,8 +9404,10 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 1D7C4..1D7CB  ; XID_Start # L&   [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA
 1DF00..1DF09  ; XID_Start # L&  [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
 1DF0A         ; XID_Start # Lo       LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK
-1DF0B..1DF1E  ; XID_Start # L&  [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL
-1DF25..1DF2A  ; XID_Start # L&   [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK
+1DF0B..1DF7F  ; XID_Start # L& [117] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER REVERSED U
+1DF80..1DF81  ; XID_Start # Lo   [2] LATIN CAPITAL LETTER A WITH TOPBAR..LATIN CAPITAL LETTER E WITH BENT TOPBAR
+1DF90..1DF96  ; XID_Start # L&   [7] LATIN SMALL LETTER C WITH LOW SLASH..LATIN SMALL LETTER LONG S WITH TOP LOOP
+1DFCD..1DFFF  ; XID_Start # Lm  [51] MODIFIER LETTER SMALL TURNED R WITH MID-HEIGHT LEFT HOOK..MODIFIER LETTER SMALL T WITH HOOK AND RETROFLEX HOOK
 1E030..1E06D  ; XID_Start # Lm  [62] MODIFIER LETTER CYRILLIC SMALL A..MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE
 1E100..1E12C  ; XID_Start # Lo  [45] NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W
 1E137..1E13D  ; XID_Start # Lm   [7] NYIAKENG PUACHUE HMONG SIGN FOR PERSON..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER
@@ -9098,6 +9418,13 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 1E4EB         ; XID_Start # Lm       NAG MUNDARI SIGN OJOD
 1E5D0..1E5ED  ; XID_Start # Lo  [30] OL ONAL LETTER O..OL ONAL LETTER EG
 1E5F0         ; XID_Start # Lo       OL ONAL SIGN HODDOND
+1E6C0..1E6DE  ; XID_Start # Lo  [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO
+1E6E0..1E6E2  ; XID_Start # Lo   [3] TAI YO LETTER AA..TAI YO LETTER UE
+1E6E4..1E6E5  ; XID_Start # Lo   [2] TAI YO LETTER U..TAI YO LETTER AE
+1E6E7..1E6ED  ; XID_Start # Lo   [7] TAI YO LETTER O..TAI YO LETTER AUE
+1E6F0..1E6F4  ; XID_Start # Lo   [5] TAI YO LETTER AN..TAI YO LETTER AP
+1E6FE         ; XID_Start # Lo       TAI YO SYMBOL MUEANG
+1E6FF         ; XID_Start # Lm       TAI YO XAM LAI
 1E7E0..1E7E6  ; XID_Start # Lo   [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO
 1E7E8..1E7EB  ; XID_Start # Lo   [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE
 1E7ED..1E7EE  ; XID_Start # Lo   [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE
@@ -9139,16 +9466,16 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 1EEA5..1EEA9  ; XID_Start # Lo   [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH
 1EEAB..1EEBB  ; XID_Start # Lo  [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN
 20000..2A6DF  ; XID_Start # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF
-2A700..2B739  ; XID_Start # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739
-2B740..2B81D  ; XID_Start # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D
-2B820..2CEA1  ; XID_Start # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1
+2A700..2B81E  ; XID_Start # Lo [4383] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81E
+2B820..2CEAD  ; XID_Start # Lo [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD
 2CEB0..2EBE0  ; XID_Start # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
 2EBF0..2EE5D  ; XID_Start # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D
 2F800..2FA1D  ; XID_Start # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
 30000..3134A  ; XID_Start # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A
-31350..323AF  ; XID_Start # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF
+31350..33479  ; XID_Start # Lo [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479
+3D000..3FC3F  ; XID_Start # Lo [11328] SEAL CHARACTER-3D000..SEAL CHARACTER-3FC3F
 
-# Total code points: 141246
+# Total code points: 158744
 
 # ================================================
 
@@ -9174,8 +9501,8 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 01BC..01BF    ; XID_Continue # L&   [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN
 01C0..01C3    ; XID_Continue # Lo   [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK
 01C4..0293    ; XID_Continue # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL
-0294          ; XID_Continue # Lo       LATIN LETTER GLOTTAL STOP
-0295..02AF    ; XID_Continue # L&  [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+0294..0295    ; XID_Continue # Lo   [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE
+0296..02AF    ; XID_Continue # L&  [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
 02B0..02C1    ; XID_Continue # Lm  [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP
 02C6..02D1    ; XID_Continue # Lm  [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON
 02E0..02E4    ; XID_Continue # Lm   [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
@@ -9197,13 +9524,14 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 0483..0487    ; XID_Continue # Mn   [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE
 048A..052F    ; XID_Continue # L& [166] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER EL WITH DESCENDER
 0531..0556    ; XID_Continue # L&  [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH
-0559          ; XID_Continue # Lm       ARMENIAN MODIFIER LETTER LEFT HALF RING
+0558..0559    ; XID_Continue # Lm   [2] MODIFIER LETTER ARMENIAN SMALL EH..ARMENIAN MODIFIER LETTER LEFT HALF RING
 0560..0588    ; XID_Continue # L&  [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE
+058B..058C    ; XID_Continue # Lm   [2] MODIFIER LETTER ARMENIAN SMALL INI..MODIFIER LETTER ARMENIAN SMALL YI
 0591..05BD    ; XID_Continue # Mn  [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
 05BF          ; XID_Continue # Mn       HEBREW POINT RAFE
 05C1..05C2    ; XID_Continue # Mn   [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
 05C4..05C5    ; XID_Continue # Mn   [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
-05C7          ; XID_Continue # Mn       HEBREW POINT QAMATS QATAN
+05C7..05C9    ; XID_Continue # Mn   [3] HEBREW POINT QAMATS QATAN..HEBREW POINT DAGESH HAZAQ MUDGASH
 05D0..05EA    ; XID_Continue # Lo  [27] HEBREW LETTER ALEF..HEBREW LETTER TAV
 05EF..05F2    ; XID_Continue # Lo   [4] HEBREW YOD TRIANGLE..HEBREW LIGATURE YIDDISH DOUBLE YOD
 0610..061A    ; XID_Continue # Mn  [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
@@ -9250,7 +9578,7 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 0859..085B    ; XID_Continue # Mn   [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK
 0860..086A    ; XID_Continue # Lo  [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA
 0870..0887    ; XID_Continue # Lo  [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT
-0889..088E    ; XID_Continue # Lo   [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL
+0889..088F    ; XID_Continue # Lo   [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE
 0897..089F    ; XID_Continue # Mn   [9] ARABIC PEPET..ARABIC HALF MADDA OVER MADDA
 08A0..08C8    ; XID_Continue # Lo  [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF
 08C9          ; XID_Continue # Lm       ARABIC SMALL FARSI YEH
@@ -9358,7 +9686,7 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 0B47..0B48    ; XID_Continue # Mc   [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
 0B4B..0B4C    ; XID_Continue # Mc   [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
 0B4D          ; XID_Continue # Mn       ORIYA SIGN VIRAMA
-0B55..0B56    ; XID_Continue # Mn   [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK
+0B53..0B56    ; XID_Continue # Mn   [4] ORIYA SIGN DOT ABOVE..ORIYA AI LENGTH MARK
 0B57          ; XID_Continue # Mc       ORIYA AU LENGTH MARK
 0B5C..0B5D    ; XID_Continue # Lo   [2] ORIYA LETTER RRA..ORIYA LETTER RHA
 0B5F..0B61    ; XID_Continue # Lo   [3] ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL
@@ -9400,7 +9728,7 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 0C4A..0C4D    ; XID_Continue # Mn   [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
 0C55..0C56    ; XID_Continue # Mn   [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
 0C58..0C5A    ; XID_Continue # Lo   [3] TELUGU LETTER TSA..TELUGU LETTER RRRA
-0C5D          ; XID_Continue # Lo       TELUGU LETTER NAKAARA POLLU
+0C5C..0C5D    ; XID_Continue # Lo   [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU
 0C60..0C61    ; XID_Continue # Lo   [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
 0C62..0C63    ; XID_Continue # Mn   [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
 0C66..0C6F    ; XID_Continue # Nd  [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE
@@ -9422,7 +9750,7 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 0CCA..0CCB    ; XID_Continue # Mc   [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
 0CCC..0CCD    ; XID_Continue # Mn   [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA
 0CD5..0CD6    ; XID_Continue # Mc   [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
-0CDD..0CDE    ; XID_Continue # Lo   [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA
+0CDC..0CDE    ; XID_Continue # Lo   [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA
 0CE0..0CE1    ; XID_Continue # Lo   [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
 0CE2..0CE3    ; XID_Continue # Mn   [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL
 0CE6..0CEF    ; XID_Continue # Nd  [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE
@@ -9639,7 +9967,7 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 1A90..1A99    ; XID_Continue # Nd  [10] TAI THAM THAM DIGIT ZERO..TAI THAM THAM DIGIT NINE
 1AA7          ; XID_Continue # Lm       TAI THAM SIGN MAI YAMOK
 1AB0..1ABD    ; XID_Continue # Mn  [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
-1ABF..1ACE    ; XID_Continue # Mn  [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T
+1ABF..1AF0    ; XID_Continue # Mn  [50] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOUBLE COMMA ABOVE
 1B00..1B03    ; XID_Continue # Mn   [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
 1B04          ; XID_Continue # Mc       BALINESE SIGN BISAH
 1B05..1B33    ; XID_Continue # Lo  [47] BALINESE LETTER AKARA..BALINESE LETTER HA
@@ -9730,7 +10058,7 @@ FFDA..FFDC    ; XID_Start # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGU
 2054          ; XID_Continue # Pc       INVERTED UNDERTIE
 2071          ; XID_Continue # Lm       SUPERSCRIPT LATIN SMALL LETTER I
 207F          ; XID_Continue # Lm       SUPERSCRIPT LATIN SMALL LETTER N
-2090..209C    ; XID_Continue # Lm  [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T
+208F..209F    ; XID_Continue # Lm  [17] MODIFIER LETTER HIGH AND LOW VERTICAL LINE..LATIN SUBSCRIPT SMALL LETTER Z
 20D0..20DC    ; XID_Continue # Mn  [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE
 20E1          ; XID_Continue # Mn       COMBINING LEFT RIGHT ARROW ABOVE
 20E5..20F0    ; XID_Continue # Mn  [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE
@@ -9827,11 +10155,9 @@ A771..A787    ; XID_Continue # L&  [23] LATIN SMALL LETTER DUM..LATIN SMALL LETT
 A788          ; XID_Continue # Lm       MODIFIER LETTER LOW CIRCUMFLEX ACCENT
 A78B..A78E    ; XID_Continue # L&   [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT
 A78F          ; XID_Continue # Lo       LATIN LETTER SINOLOGICAL DOT
-A790..A7CD    ; XID_Continue # L&  [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE
-A7D0..A7D1    ; XID_Continue # L&   [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G
-A7D3          ; XID_Continue # L&       LATIN SMALL LETTER DOUBLE THORN
-A7D5..A7DC    ; XID_Continue # L&   [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE
-A7F2..A7F4    ; XID_Continue # Lm   [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
+A790..A7DD    ; XID_Continue # L&  [78] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2          ; XID_Continue # L&       LATIN CAPITAL LETTER R WITH LONG LEG
+A7F1..A7F4    ; XID_Continue # Lm   [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q
 A7F5..A7F6    ; XID_Continue # L&   [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H
 A7F7          ; XID_Continue # Lo       LATIN EPIGRAPHIC LETTER SIDEWAYS I
 A7F8..A7F9    ; XID_Continue # Lm   [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
@@ -9931,6 +10257,7 @@ AB30..AB5A    ; XID_Continue # L&  [43] LATIN SMALL LETTER BARRED ALPHA..LATIN S
 AB5C..AB5F    ; XID_Continue # Lm   [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK
 AB60..AB68    ; XID_Continue # L&   [9] LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER TURNED R WITH MIDDLE TILDE
 AB69          ; XID_Continue # Lm       MODIFIER LETTER SMALL TURNED W
+AB6C..AB6D    ; XID_Continue # L&   [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 AB70..ABBF    ; XID_Continue # L&  [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
 ABC0..ABE2    ; XID_Continue # Lo  [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM
 ABE3..ABE4    ; XID_Continue # Mc   [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP
@@ -10031,7 +10358,7 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 10760..10767  ; XID_Continue # Lo   [8] LINEAR A SIGN A800..LINEAR A SIGN A807
 10780..10785  ; XID_Continue # Lm   [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK
 10787..107B0  ; XID_Continue # Lm  [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
-107B2..107BA  ; XID_Continue # Lm   [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
+107B2..107BF  ; XID_Continue # Lm  [14] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL ESH WITH DOUBLE BAR
 10800..10805  ; XID_Continue # Lo   [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA
 10808         ; XID_Continue # Lo       CYPRIOT SYLLABLE JO
 1080A..10835  ; XID_Continue # Lo  [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO
@@ -10044,6 +10371,7 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 108F4..108F5  ; XID_Continue # Lo   [2] HATRAN LETTER SHIN..HATRAN LETTER TAW
 10900..10915  ; XID_Continue # Lo  [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU
 10920..10939  ; XID_Continue # Lo  [26] LYDIAN LETTER A..LYDIAN LETTER C
+10940..10959  ; XID_Continue # Lo  [26] SIDETIC LETTER N01..SIDETIC LETTER N26
 10980..109B7  ; XID_Continue # Lo  [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA
 109BE..109BF  ; XID_Continue # Lo   [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN
 10A00         ; XID_Continue # Lo       KHAROSHTHI LETTER A
@@ -10082,7 +10410,11 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 10EAB..10EAC  ; XID_Continue # Mn   [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
 10EB0..10EB1  ; XID_Continue # Lo   [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
 10EC2..10EC4  ; XID_Continue # Lo   [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW
-10EFC..10EFF  ; XID_Continue # Mn   [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA
+10EC5         ; XID_Continue # Lm       ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW
+10EC6..10EC7  ; XID_Continue # Lo   [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW
+10ECB..10ECF  ; XID_Continue # Mn   [5] ARABIC NORTHEAST POINTING ARROWHEAD ABOVE..ARABIC LARGE CIRCLE ABOVE
+10ED9..10EEE  ; XID_Continue # Lo  [22] ARABIC CROWN LETTER BEH..ARABIC CROWN LETTER YEH
+10EF0..10EFF  ; XID_Continue # Mn  [16] ARABIC SMALL LOW UPRIGHT RECTANGULAR ZERO..ARABIC SMALL LOW WORD MADDA
 10F00..10F1C  ; XID_Continue # Lo  [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL
 10F27         ; XID_Continue # Lo       OLD SOGDIAN LIGATURE AYIN-DALETH
 10F30..10F45  ; XID_Continue # Lo  [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN
@@ -10309,6 +10641,13 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 11A98..11A99  ; XID_Continue # Mn   [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER
 11A9D         ; XID_Continue # Lo       SOYOMBO MARK PLUTA
 11AB0..11AF8  ; XID_Continue # Lo  [73] CANADIAN SYLLABICS NATTILIK HI..PAU CIN HAU GLOTTAL STOP FINAL
+11B0A         ; XID_Continue # Lo       DEVANAGARI LETTER ALTERNATE DDDA
+11B60         ; XID_Continue # Mn       SHARADA VOWEL SIGN OE
+11B61         ; XID_Continue # Mc       SHARADA VOWEL SIGN OOE
+11B62..11B64  ; XID_Continue # Mn   [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E
+11B65         ; XID_Continue # Mc       SHARADA VOWEL SIGN SHORT O
+11B66         ; XID_Continue # Mn       SHARADA VOWEL SIGN CANDRA E
+11B67         ; XID_Continue # Mc       SHARADA VOWEL SIGN CANDRA O
 11BC0..11BE0  ; XID_Continue # Lo  [33] SUNUWAR LETTER DEVI..SUNUWAR LETTER KLOKO
 11BF0..11BF9  ; XID_Continue # Nd  [10] SUNUWAR DIGIT ZERO..SUNUWAR DIGIT NINE
 11C00..11C08  ; XID_Continue # Lo   [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L
@@ -10349,6 +10688,12 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 11D97         ; XID_Continue # Mn       GUNJALA GONDI VIRAMA
 11D98         ; XID_Continue # Lo       GUNJALA GONDI OM
 11DA0..11DA9  ; XID_Continue # Nd  [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE
+11DB0..11DD8  ; XID_Continue # Lo  [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH
+11DD9         ; XID_Continue # Lm       TOLONG SIKI SIGN SELA
+11DDA..11DDB  ; XID_Continue # Lo   [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA
+11DE0..11DE9  ; XID_Continue # Nd  [10] TOLONG SIKI DIGIT ZERO..TOLONG SIKI DIGIT NINE
+11DF0         ; XID_Continue # Mn       BENGALI SIGN COMBINING ANUSVARA ABOVE
+11DF1         ; XID_Continue # Lo       BENGALI LETTER ALTERNATE BARGIYA BA
 11EE0..11EF2  ; XID_Continue # Lo  [19] MAKASAR LETTER KA..MAKASAR ANGKA
 11EF3..11EF4  ; XID_Continue # Mn   [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
 11EF5..11EF6  ; XID_Continue # Mc   [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
@@ -10367,8 +10712,10 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 11F5A         ; XID_Continue # Mn       KAWI SIGN NUKTA
 11FB0         ; XID_Continue # Lo       LISU LETTER YHA
 12000..12399  ; XID_Continue # Lo [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U
-12400..1246E  ; XID_Continue # Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM
+12400..1246F  ; XID_Continue # Nl [112] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN SEVEN ASH TENU
+12475..1247F  ; XID_Continue # Nl  [11] CUNEIFORM NUMERIC SIGN EIGHT ASH TENU..CUNEIFORM NUMERIC SIGN ASH TIMES NINE DISH TENU
 12480..12543  ; XID_Continue # Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU
+12550..12686  ; XID_Continue # Nl [311] CUNEIFORM NUMERIC SIGN ONE N01..CUNEIFORM NUMERIC SIGN ONE N36 FLAT
 12F90..12FF0  ; XID_Continue # Lo  [97] CYPRO-MINOAN SIGN CM001..CYPRO-MINOAN SIGN CM114
 13000..1342F  ; XID_Continue # Lo [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D
 13440         ; XID_Continue # Mn       EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY
@@ -10398,7 +10745,14 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 16D43..16D6A  ; XID_Continue # Lo  [40] KIRAT RAI LETTER A..KIRAT RAI VOWEL SIGN AU
 16D6B..16D6C  ; XID_Continue # Lm   [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT
 16D70..16D79  ; XID_Continue # Nd  [10] KIRAT RAI DIGIT ZERO..KIRAT RAI DIGIT NINE
+16D80..16D97  ; XID_Continue # Lo  [24] CHISOI LETTER A..CHISOI LETTER PA
+16D98         ; XID_Continue # Mn       CHISOI SIGN ANUSVARA
+16D99..16D9C  ; XID_Continue # Lo   [4] CHISOI LETTER YA..CHISOI LETTER JARAHA
+16D9D         ; XID_Continue # Mn       CHISOI SIGN SISO
+16DA0..16DA9  ; XID_Continue # Nd  [10] CHISOI DIGIT ZERO..CHISOI DIGIT NINE
 16E40..16E7F  ; XID_Continue # L&  [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y
+16EA0..16EB8  ; XID_Continue # L&  [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
+16EBB..16ED3  ; XID_Continue # L&  [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
 16F00..16F4A  ; XID_Continue # Lo  [75] MIAO LETTER PA..MIAO LETTER RTE
 16F4F         ; XID_Continue # Mn       MIAO SIGN CONSONANT MODIFIER BAR
 16F50         ; XID_Continue # Lo       MIAO LETTER NASALIZATION
@@ -10409,17 +10763,21 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 16FE3         ; XID_Continue # Lm       OLD CHINESE ITERATION MARK
 16FE4         ; XID_Continue # Mn       KHITAN SMALL SCRIPT FILLER
 16FF0..16FF1  ; XID_Continue # Mc   [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY
-17000..187F7  ; XID_Continue # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
-18800..18CD5  ; XID_Continue # Lo [1238] TANGUT COMPONENT-001..KHITAN SMALL SCRIPT CHARACTER-18CD5
-18CFF..18D08  ; XID_Continue # Lo  [10] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D08
+16FF2..16FF3  ; XID_Continue # Lm   [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER
+16FF4..16FF6  ; XID_Continue # Nl   [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS
+17000..18CDA  ; XID_Continue # Lo [7387] TANGUT IDEOGRAPH-17000..KHITAN SMALL SCRIPT CHARACTER-18CDA
+18CFF..18D20  ; XID_Continue # Lo  [34] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D20
+18D80..18DF2  ; XID_Continue # Lo [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883
+18E00..19191  ; XID_Continue # Lo [914] JURCHEN CHARACTER-18E00..JURCHEN CHARACTER-19191
+191A0..191D2  ; XID_Continue # Lo  [51] JURCHEN RADICAL-01..JURCHEN RADICAL-51
 1AFF0..1AFF3  ; XID_Continue # Lm   [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5
 1AFF5..1AFFB  ; XID_Continue # Lm   [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5
 1AFFD..1AFFE  ; XID_Continue # Lm   [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8
-1B000..1B122  ; XID_Continue # Lo [291] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ARCHAIC WU
+1B000..1B128  ; XID_Continue # Lo [297] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ALTERNATE WI
 1B132         ; XID_Continue # Lo       HIRAGANA LETTER SMALL KO
 1B150..1B152  ; XID_Continue # Lo   [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO
 1B155         ; XID_Continue # Lo       KATAKANA LETTER SMALL KO
-1B164..1B167  ; XID_Continue # Lo   [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N
+1B164..1B168  ; XID_Continue # Lo   [5] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL ARCHAIC YE
 1B170..1B2FB  ; XID_Continue # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
 1BC00..1BC6A  ; XID_Continue # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M
 1BC70..1BC7C  ; XID_Continue # Lo  [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK
@@ -10429,6 +10787,7 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 1CCF0..1CCF9  ; XID_Continue # Nd  [10] OUTLINED DIGIT ZERO..OUTLINED DIGIT NINE
 1CF00..1CF2D  ; XID_Continue # Mn  [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT
 1CF30..1CF46  ; XID_Continue # Mn  [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG
+1D127..1D128  ; XID_Continue # Mn   [2] MUSICAL SYMBOL COMBINING STRESS..MUSICAL SYMBOL COMBINING UNSTRESS
 1D165..1D166  ; XID_Continue # Mc   [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
 1D167..1D169  ; XID_Continue # Mn   [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
 1D16D..1D172  ; XID_Continue # Mc   [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5
@@ -10436,6 +10795,10 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 1D185..1D18B  ; XID_Continue # Mn   [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
 1D1AA..1D1AD  ; XID_Continue # Mn   [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
 1D242..1D244  ; XID_Continue # Mn   [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+1D250..1D252  ; XID_Continue # Mc   [3] MUSICAL SYMBOL COMBINING FLAG-6..MUSICAL SYMBOL COMBINING FLAG-8
+1D25B..1D25C  ; XID_Continue # Mn   [2] MUSICAL SYMBOL COMBINING TREMOLO-4..MUSICAL SYMBOL COMBINING TREMOLO-5
+1D25F         ; XID_Continue # Mc       MUSICAL SYMBOL COMBINING BUZZ ROLL STEM
+1D280..1D281  ; XID_Continue # Mc   [2] MUSICAL SYMBOL COMBINING STEM BOW BEHIND BRIDGE..MUSICAL SYMBOL COMBINING STEM BOW ON TOP OF BRIDGE
 1D400..1D454  ; XID_Continue # L&  [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G
 1D456..1D49C  ; XID_Continue # L&  [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A
 1D49E..1D49F  ; XID_Continue # L&   [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D
@@ -10454,7 +10817,7 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 1D540..1D544  ; XID_Continue # L&   [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
 1D546         ; XID_Continue # L&       MATHEMATICAL DOUBLE-STRUCK CAPITAL O
 1D54A..1D550  ; XID_Continue # L&   [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
-1D552..1D6A5  ; XID_Continue # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D552..1D6A6  ; XID_Continue # L& [341] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL LIGATURE LONG S WITH DESCENDER S
 1D6A8..1D6C0  ; XID_Continue # L&  [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
 1D6C2..1D6DA  ; XID_Continue # L&  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
 1D6DC..1D6FA  ; XID_Continue # L&  [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA
@@ -10475,8 +10838,10 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 1DAA1..1DAAF  ; XID_Continue # Mn  [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16
 1DF00..1DF09  ; XID_Continue # L&  [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
 1DF0A         ; XID_Continue # Lo       LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK
-1DF0B..1DF1E  ; XID_Continue # L&  [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL
-1DF25..1DF2A  ; XID_Continue # L&   [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK
+1DF0B..1DF7F  ; XID_Continue # L& [117] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER REVERSED U
+1DF80..1DF81  ; XID_Continue # Lo   [2] LATIN CAPITAL LETTER A WITH TOPBAR..LATIN CAPITAL LETTER E WITH BENT TOPBAR
+1DF90..1DF96  ; XID_Continue # L&   [7] LATIN SMALL LETTER C WITH LOW SLASH..LATIN SMALL LETTER LONG S WITH TOP LOOP
+1DFCD..1DFFF  ; XID_Continue # Lm  [51] MODIFIER LETTER SMALL TURNED R WITH MID-HEIGHT LEFT HOOK..MODIFIER LETTER SMALL T WITH HOOK AND RETROFLEX HOOK
 1E000..1E006  ; XID_Continue # Mn   [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
 1E008..1E018  ; XID_Continue # Mn  [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
 1E01B..1E021  ; XID_Continue # Mn   [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
@@ -10502,6 +10867,17 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 1E5EE..1E5EF  ; XID_Continue # Mn   [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR
 1E5F0         ; XID_Continue # Lo       OL ONAL SIGN HODDOND
 1E5F1..1E5FA  ; XID_Continue # Nd  [10] OL ONAL DIGIT ZERO..OL ONAL DIGIT NINE
+1E6C0..1E6DE  ; XID_Continue # Lo  [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO
+1E6E0..1E6E2  ; XID_Continue # Lo   [3] TAI YO LETTER AA..TAI YO LETTER UE
+1E6E3         ; XID_Continue # Mn       TAI YO SIGN UE
+1E6E4..1E6E5  ; XID_Continue # Lo   [2] TAI YO LETTER U..TAI YO LETTER AE
+1E6E6         ; XID_Continue # Mn       TAI YO SIGN AU
+1E6E7..1E6ED  ; XID_Continue # Lo   [7] TAI YO LETTER O..TAI YO LETTER AUE
+1E6EE..1E6EF  ; XID_Continue # Mn   [2] TAI YO SIGN AY..TAI YO SIGN ANG
+1E6F0..1E6F4  ; XID_Continue # Lo   [5] TAI YO LETTER AN..TAI YO LETTER AP
+1E6F5         ; XID_Continue # Mn       TAI YO SIGN OM
+1E6FE         ; XID_Continue # Lo       TAI YO SYMBOL MUEANG
+1E6FF         ; XID_Continue # Lm       TAI YO XAM LAI
 1E7E0..1E7E6  ; XID_Continue # Lo   [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO
 1E7E8..1E7EB  ; XID_Continue # Lo   [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE
 1E7ED..1E7EE  ; XID_Continue # Lo   [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE
@@ -10547,17 +10923,17 @@ FFDA..FFDC    ; XID_Continue # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HA
 1EEAB..1EEBB  ; XID_Continue # Lo  [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN
 1FBF0..1FBF9  ; XID_Continue # Nd  [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE
 20000..2A6DF  ; XID_Continue # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF
-2A700..2B739  ; XID_Continue # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739
-2B740..2B81D  ; XID_Continue # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D
-2B820..2CEA1  ; XID_Continue # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1
+2A700..2B81E  ; XID_Continue # Lo [4383] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81E
+2B820..2CEAD  ; XID_Continue # Lo [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD
 2CEB0..2EBE0  ; XID_Continue # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
 2EBF0..2EE5D  ; XID_Continue # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D
 2F800..2FA1D  ; XID_Continue # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
 30000..3134A  ; XID_Continue # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A
-31350..323AF  ; XID_Continue # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF
+31350..33479  ; XID_Continue # Lo [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479
+3D000..3FC3F  ; XID_Continue # Lo [11328] SEAL CHARACTER-3D000..SEAL CHARACTER-3FC3F
 E0100..E01EF  ; XID_Continue # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
 
-# Total code points: 144522
+# Total code points: 162121
 
 # ================================================
 
@@ -10624,7 +11000,7 @@ E01F0..E0FFF  ; Default_Ignorable_Code_Point # Cn [3600] <reserved-E01F0>..<rese
 05BF          ; Grapheme_Extend # Mn       HEBREW POINT RAFE
 05C1..05C2    ; Grapheme_Extend # Mn   [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
 05C4..05C5    ; Grapheme_Extend # Mn   [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
-05C7          ; Grapheme_Extend # Mn       HEBREW POINT QAMATS QATAN
+05C7..05C9    ; Grapheme_Extend # Mn   [3] HEBREW POINT QAMATS QATAN..HEBREW POINT DAGESH HAZAQ MUDGASH
 0610..061A    ; Grapheme_Extend # Mn  [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
 064B..065F    ; Grapheme_Extend # Mn  [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW
 0670          ; Grapheme_Extend # Mn       ARABIC LETTER SUPERSCRIPT ALEF
@@ -10680,7 +11056,7 @@ E01F0..E0FFF  ; Default_Ignorable_Code_Point # Cn [3600] <reserved-E01F0>..<rese
 0B3F          ; Grapheme_Extend # Mn       ORIYA VOWEL SIGN I
 0B41..0B44    ; Grapheme_Extend # Mn   [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR
 0B4D          ; Grapheme_Extend # Mn       ORIYA SIGN VIRAMA
-0B55..0B56    ; Grapheme_Extend # Mn   [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK
+0B53..0B56    ; Grapheme_Extend # Mn   [4] ORIYA SIGN DOT ABOVE..ORIYA AI LENGTH MARK
 0B57          ; Grapheme_Extend # Mc       ORIYA AU LENGTH MARK
 0B62..0B63    ; Grapheme_Extend # Mn   [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL
 0B82          ; Grapheme_Extend # Mn       TAMIL SIGN ANUSVARA
@@ -10778,7 +11154,7 @@ E01F0..E0FFF  ; Default_Ignorable_Code_Point # Cn [3600] <reserved-E01F0>..<rese
 1A7F          ; Grapheme_Extend # Mn       TAI THAM COMBINING CRYPTOGRAMMIC DOT
 1AB0..1ABD    ; Grapheme_Extend # Mn  [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
 1ABE          ; Grapheme_Extend # Me       COMBINING PARENTHESES OVERLAY
-1ABF..1ACE    ; Grapheme_Extend # Mn  [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T
+1ABF..1AF0    ; Grapheme_Extend # Mn  [50] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOUBLE COMMA ABOVE
 1B00..1B03    ; Grapheme_Extend # Mn   [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
 1B34          ; Grapheme_Extend # Mn       BALINESE SIGN REREKAN
 1B35          ; Grapheme_Extend # Mc       BALINESE VOWEL SIGN TEDUNG
@@ -10874,7 +11250,8 @@ FF9E..FF9F    ; Grapheme_Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK.
 10D24..10D27  ; Grapheme_Extend # Mn   [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI
 10D69..10D6D  ; Grapheme_Extend # Mn   [5] GARAY VOWEL SIGN E..GARAY CONSONANT NASALIZATION MARK
 10EAB..10EAC  ; Grapheme_Extend # Mn   [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
-10EFC..10EFF  ; Grapheme_Extend # Mn   [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA
+10ECB..10ECF  ; Grapheme_Extend # Mn   [5] ARABIC NORTHEAST POINTING ARROWHEAD ABOVE..ARABIC LARGE CIRCLE ABOVE
+10EF0..10EFF  ; Grapheme_Extend # Mn  [16] ARABIC SMALL LOW UPRIGHT RECTANGULAR ZERO..ARABIC SMALL LOW WORD MADDA
 10F46..10F50  ; Grapheme_Extend # Mn  [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW
 10F82..10F85  ; Grapheme_Extend # Mn   [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW
 11001         ; Grapheme_Extend # Mn       BRAHMI SIGN ANUSVARA
@@ -10965,6 +11342,9 @@ FF9E..FF9F    ; Grapheme_Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK.
 11A59..11A5B  ; Grapheme_Extend # Mn   [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK
 11A8A..11A96  ; Grapheme_Extend # Mn  [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA
 11A98..11A99  ; Grapheme_Extend # Mn   [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER
+11B60         ; Grapheme_Extend # Mn       SHARADA VOWEL SIGN OE
+11B62..11B64  ; Grapheme_Extend # Mn   [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E
+11B66         ; Grapheme_Extend # Mn       SHARADA VOWEL SIGN CANDRA E
 11C30..11C36  ; Grapheme_Extend # Mn   [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L
 11C38..11C3D  ; Grapheme_Extend # Mn   [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA
 11C3F         ; Grapheme_Extend # Mn       BHAIKSUKI SIGN VIRAMA
@@ -10980,6 +11360,7 @@ FF9E..FF9F    ; Grapheme_Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK.
 11D90..11D91  ; Grapheme_Extend # Mn   [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI
 11D95         ; Grapheme_Extend # Mn       GUNJALA GONDI SIGN ANUSVARA
 11D97         ; Grapheme_Extend # Mn       GUNJALA GONDI VIRAMA
+11DF0         ; Grapheme_Extend # Mn       BENGALI SIGN COMBINING ANUSVARA ABOVE
 11EF3..11EF4  ; Grapheme_Extend # Mn   [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
 11F00..11F01  ; Grapheme_Extend # Mn   [2] KAWI SIGN CANDRABINDU..KAWI SIGN ANUSVARA
 11F36..11F3A  ; Grapheme_Extend # Mn   [5] KAWI VOWEL SIGN I..KAWI VOWEL SIGN VOCALIC R
@@ -10993,6 +11374,8 @@ FF9E..FF9F    ; Grapheme_Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK.
 1612D..1612F  ; Grapheme_Extend # Mn   [3] GURUNG KHEMA SIGN ANUSVARA..GURUNG KHEMA SIGN THOLHOMA
 16AF0..16AF4  ; Grapheme_Extend # Mn   [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
 16B30..16B36  ; Grapheme_Extend # Mn   [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
+16D98         ; Grapheme_Extend # Mn       CHISOI SIGN ANUSVARA
+16D9D         ; Grapheme_Extend # Mn       CHISOI SIGN SISO
 16F4F         ; Grapheme_Extend # Mn       MIAO SIGN CONSONANT MODIFIER BAR
 16F8F..16F92  ; Grapheme_Extend # Mn   [4] MIAO TONE RIGHT..MIAO TONE BELOW
 16FE4         ; Grapheme_Extend # Mn       KHITAN SMALL SCRIPT FILLER
@@ -11000,6 +11383,7 @@ FF9E..FF9F    ; Grapheme_Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK.
 1BC9D..1BC9E  ; Grapheme_Extend # Mn   [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK
 1CF00..1CF2D  ; Grapheme_Extend # Mn  [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT
 1CF30..1CF46  ; Grapheme_Extend # Mn  [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG
+1D127..1D128  ; Grapheme_Extend # Mn   [2] MUSICAL SYMBOL COMBINING STRESS..MUSICAL SYMBOL COMBINING UNSTRESS
 1D165..1D166  ; Grapheme_Extend # Mc   [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
 1D167..1D169  ; Grapheme_Extend # Mn   [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
 1D16D..1D172  ; Grapheme_Extend # Mc   [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5
@@ -11007,6 +11391,10 @@ FF9E..FF9F    ; Grapheme_Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK.
 1D185..1D18B  ; Grapheme_Extend # Mn   [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
 1D1AA..1D1AD  ; Grapheme_Extend # Mn   [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
 1D242..1D244  ; Grapheme_Extend # Mn   [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+1D250..1D252  ; Grapheme_Extend # Mc   [3] MUSICAL SYMBOL COMBINING FLAG-6..MUSICAL SYMBOL COMBINING FLAG-8
+1D25B..1D25C  ; Grapheme_Extend # Mn   [2] MUSICAL SYMBOL COMBINING TREMOLO-4..MUSICAL SYMBOL COMBINING TREMOLO-5
+1D25F         ; Grapheme_Extend # Mc       MUSICAL SYMBOL COMBINING BUZZ ROLL STEM
+1D280..1D281  ; Grapheme_Extend # Mc   [2] MUSICAL SYMBOL COMBINING STEM BOW BEHIND BRIDGE..MUSICAL SYMBOL COMBINING STEM BOW ON TOP OF BRIDGE
 1DA00..1DA36  ; Grapheme_Extend # Mn  [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN
 1DA3B..1DA6C  ; Grapheme_Extend # Mn  [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT
 1DA75         ; Grapheme_Extend # Mn       SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS
@@ -11024,12 +11412,16 @@ FF9E..FF9F    ; Grapheme_Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK.
 1E2EC..1E2EF  ; Grapheme_Extend # Mn   [4] WANCHO TONE TUP..WANCHO TONE KOINI
 1E4EC..1E4EF  ; Grapheme_Extend # Mn   [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH
 1E5EE..1E5EF  ; Grapheme_Extend # Mn   [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR
+1E6E3         ; Grapheme_Extend # Mn       TAI YO SIGN UE
+1E6E6         ; Grapheme_Extend # Mn       TAI YO SIGN AU
+1E6EE..1E6EF  ; Grapheme_Extend # Mn   [2] TAI YO SIGN AY..TAI YO SIGN ANG
+1E6F5         ; Grapheme_Extend # Mn       TAI YO SIGN OM
 1E8D0..1E8D6  ; Grapheme_Extend # Mn   [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
 1E944..1E94A  ; Grapheme_Extend # Mn   [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
 E0020..E007F  ; Grapheme_Extend # Cf  [96] TAG SPACE..CANCEL TAG
 E0100..E01EF  ; Grapheme_Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
 
-# Total code points: 2193
+# Total code points: 2271
 
 # ================================================
 
@@ -11098,8 +11490,8 @@ E0100..E01EF  ; Grapheme_Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELE
 01BC..01BF    ; Grapheme_Base # L&   [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN
 01C0..01C3    ; Grapheme_Base # Lo   [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK
 01C4..0293    ; Grapheme_Base # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL
-0294          ; Grapheme_Base # Lo       LATIN LETTER GLOTTAL STOP
-0295..02AF    ; Grapheme_Base # L&  [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+0294..0295    ; Grapheme_Base # Lo   [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE
+0296..02AF    ; Grapheme_Base # L&  [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
 02B0..02C1    ; Grapheme_Base # Lm  [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP
 02C2..02C5    ; Grapheme_Base # Sk   [4] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER DOWN ARROWHEAD
 02C6..02D1    ; Grapheme_Base # Lm  [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON
@@ -11130,11 +11522,12 @@ E0100..E01EF  ; Grapheme_Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELE
 0482          ; Grapheme_Base # So       CYRILLIC THOUSANDS SIGN
 048A..052F    ; Grapheme_Base # L& [166] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER EL WITH DESCENDER
 0531..0556    ; Grapheme_Base # L&  [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH
-0559          ; Grapheme_Base # Lm       ARMENIAN MODIFIER LETTER LEFT HALF RING
+0558..0559    ; Grapheme_Base # Lm   [2] MODIFIER LETTER ARMENIAN SMALL EH..ARMENIAN MODIFIER LETTER LEFT HALF RING
 055A..055F    ; Grapheme_Base # Po   [6] ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION MARK
 0560..0588    ; Grapheme_Base # L&  [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE
 0589          ; Grapheme_Base # Po       ARMENIAN FULL STOP
 058A          ; Grapheme_Base # Pd       ARMENIAN HYPHEN
+058B..058C    ; Grapheme_Base # Lm   [2] MODIFIER LETTER ARMENIAN SMALL INI..MODIFIER LETTER ARMENIAN SMALL YI
 058D..058E    ; Grapheme_Base # So   [2] RIGHT-FACING ARMENIAN ETERNITY SIGN..LEFT-FACING ARMENIAN ETERNITY SIGN
 058F          ; Grapheme_Base # Sc       ARMENIAN DRAM SIGN
 05BE          ; Grapheme_Base # Pd       HEBREW PUNCTUATION MAQAF
@@ -11190,7 +11583,7 @@ E0100..E01EF  ; Grapheme_Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELE
 0860..086A    ; Grapheme_Base # Lo  [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA
 0870..0887    ; Grapheme_Base # Lo  [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT
 0888          ; Grapheme_Base # Sk       ARABIC RAISED ROUND DOT
-0889..088E    ; Grapheme_Base # Lo   [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL
+0889..088F    ; Grapheme_Base # Lo   [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE
 08A0..08C8    ; Grapheme_Base # Lo  [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF
 08C9          ; Grapheme_Base # Lm       ARABIC SMALL FARSI YEH
 0903          ; Grapheme_Base # Mc       DEVANAGARI SIGN VISARGA
@@ -11305,7 +11698,7 @@ E0100..E01EF  ; Grapheme_Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELE
 0C3D          ; Grapheme_Base # Lo       TELUGU SIGN AVAGRAHA
 0C41..0C44    ; Grapheme_Base # Mc   [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR
 0C58..0C5A    ; Grapheme_Base # Lo   [3] TELUGU LETTER TSA..TELUGU LETTER RRRA
-0C5D          ; Grapheme_Base # Lo       TELUGU LETTER NAKAARA POLLU
+0C5C..0C5D    ; Grapheme_Base # Lo   [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU
 0C60..0C61    ; Grapheme_Base # Lo   [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
 0C66..0C6F    ; Grapheme_Base # Nd  [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE
 0C77          ; Grapheme_Base # Po       TELUGU SIGN SIDDHAM
@@ -11323,7 +11716,7 @@ E0100..E01EF  ; Grapheme_Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELE
 0CBE          ; Grapheme_Base # Mc       KANNADA VOWEL SIGN AA
 0CC1          ; Grapheme_Base # Mc       KANNADA VOWEL SIGN U
 0CC3..0CC4    ; Grapheme_Base # Mc   [2] KANNADA VOWEL SIGN VOCALIC R..KANNADA VOWEL SIGN VOCALIC RR
-0CDD..0CDE    ; Grapheme_Base # Lo   [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA
+0CDC..0CDE    ; Grapheme_Base # Lo   [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA
 0CE0..0CE1    ; Grapheme_Base # Lo   [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
 0CE6..0CEF    ; Grapheme_Base # Nd  [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE
 0CF1..0CF2    ; Grapheme_Base # Lo   [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA
@@ -11638,8 +12031,8 @@ E0100..E01EF  ; Grapheme_Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELE
 208A..208C    ; Grapheme_Base # Sm   [3] SUBSCRIPT PLUS SIGN..SUBSCRIPT EQUALS SIGN
 208D          ; Grapheme_Base # Ps       SUBSCRIPT LEFT PARENTHESIS
 208E          ; Grapheme_Base # Pe       SUBSCRIPT RIGHT PARENTHESIS
-2090..209C    ; Grapheme_Base # Lm  [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T
-20A0..20C0    ; Grapheme_Base # Sc  [33] EURO-CURRENCY SIGN..SOM SIGN
+208F..209F    ; Grapheme_Base # Lm  [17] MODIFIER LETTER HIGH AND LOW VERTICAL LINE..LATIN SUBSCRIPT SMALL LETTER Z
+20A0..20C4    ; Grapheme_Base # Sc  [37] EURO-CURRENCY SIGN..OMANI RIAL SIGN
 2100..2101    ; Grapheme_Base # So   [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT
 2102          ; Grapheme_Base # L&       DOUBLE-STRUCK CAPITAL C
 2103..2106    ; Grapheme_Base # So   [4] DEGREE CELSIUS..CADA UNA
@@ -11796,8 +12189,7 @@ E0100..E01EF  ; Grapheme_Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELE
 2B45..2B46    ; Grapheme_Base # So   [2] LEFTWARDS QUADRUPLE ARROW..RIGHTWARDS QUADRUPLE ARROW
 2B47..2B4C    ; Grapheme_Base # Sm   [6] REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW..RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR
 2B4D..2B73    ; Grapheme_Base # So  [39] DOWNWARDS TRIANGLE-HEADED ZIGZAG ARROW..DOWNWARDS TRIANGLE-HEADED ARROW TO BAR
-2B76..2B95    ; Grapheme_Base # So  [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW
-2B97..2BFF    ; Grapheme_Base # So [105] SYMBOL FOR TYPE A ELECTRONICS..HELLSCHREIBER PAUSE SYMBOL
+2B76..2BFF    ; Grapheme_Base # So [138] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..HELLSCHREIBER PAUSE SYMBOL
 2C00..2C7B    ; Grapheme_Base # L& [124] GLAGOLITIC CAPITAL LETTER AZU..LATIN LETTER SMALL CAPITAL TURNED E
 2C7C..2C7D    ; Grapheme_Base # Lm   [2] LATIN SUBSCRIPT SMALL LETTER J..MODIFIER LETTER CAPITAL V
 2C7E..2CE4    ; Grapheme_Base # L& [103] LATIN CAPITAL LETTER S WITH SWASH TAIL..COPTIC SYMBOL KAI
@@ -11871,6 +12263,9 @@ E0100..E01EF  ; Grapheme_Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELE
 2E5B          ; Grapheme_Base # Ps       BOTTOM HALF LEFT PARENTHESIS
 2E5C          ; Grapheme_Base # Pe       BOTTOM HALF RIGHT PARENTHESIS
 2E5D          ; Grapheme_Base # Pd       OBLIQUE HYPHEN
+2E60..2E61    ; Grapheme_Base # Po   [2] WIGGLY EXCLAMATION MARK..INVERTED WIGGLY EXCLAMATION MARK
+2E62          ; Grapheme_Base # Ps       LEFT PARENTHESIS WITH MIDDLE RING
+2E63          ; Grapheme_Base # Pe       RIGHT PARENTHESIS WITH MIDDLE RING
 2E80..2E99    ; Grapheme_Base # So  [26] CJK RADICAL REPEAT..CJK RADICAL RAP
 2E9B..2EF3    ; Grapheme_Base # So  [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE
 2F00..2FD5    ; Grapheme_Base # So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE
@@ -11977,11 +12372,9 @@ A788          ; Grapheme_Base # Lm       MODIFIER LETTER LOW CIRCUMFLEX ACCENT
 A789..A78A    ; Grapheme_Base # Sk   [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
 A78B..A78E    ; Grapheme_Base # L&   [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT
 A78F          ; Grapheme_Base # Lo       LATIN LETTER SINOLOGICAL DOT
-A790..A7CD    ; Grapheme_Base # L&  [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE
-A7D0..A7D1    ; Grapheme_Base # L&   [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G
-A7D3          ; Grapheme_Base # L&       LATIN SMALL LETTER DOUBLE THORN
-A7D5..A7DC    ; Grapheme_Base # L&   [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE
-A7F2..A7F4    ; Grapheme_Base # Lm   [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
+A790..A7DD    ; Grapheme_Base # L&  [78] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2          ; Grapheme_Base # L&       LATIN CAPITAL LETTER R WITH LONG LEG
+A7F1..A7F4    ; Grapheme_Base # Lm   [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q
 A7F5..A7F6    ; Grapheme_Base # L&   [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H
 A7F7          ; Grapheme_Base # Lo       LATIN EPIGRAPHIC LETTER SIDEWAYS I
 A7F8..A7F9    ; Grapheme_Base # Lm   [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
@@ -12072,6 +12465,7 @@ AB5C..AB5F    ; Grapheme_Base # Lm   [4] MODIFIER LETTER SMALL HENG..MODIFIER LE
 AB60..AB68    ; Grapheme_Base # L&   [9] LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER TURNED R WITH MIDDLE TILDE
 AB69          ; Grapheme_Base # Lm       MODIFIER LETTER SMALL TURNED W
 AB6A..AB6B    ; Grapheme_Base # Sk   [2] MODIFIER LETTER LEFT TACK..MODIFIER LETTER RIGHT TACK
+AB6C..AB6D    ; Grapheme_Base # L&   [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 AB70..ABBF    ; Grapheme_Base # L&  [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
 ABC0..ABE2    ; Grapheme_Base # Lo  [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM
 ABE3..ABE4    ; Grapheme_Base # Mc   [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP
@@ -12097,13 +12491,15 @@ FB40..FB41    ; Grapheme_Base # Lo   [2] HEBREW LETTER NUN WITH DAGESH..HEBREW L
 FB43..FB44    ; Grapheme_Base # Lo   [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
 FB46..FBB1    ; Grapheme_Base # Lo [108] HEBREW LETTER TSADI WITH DAGESH..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
 FBB2..FBC2    ; Grapheme_Base # Sk  [17] ARABIC SYMBOL DOT ABOVE..ARABIC SYMBOL WASLA ABOVE
+FBC3..FBD2    ; Grapheme_Base # So  [16] ARABIC LIGATURE JALLA WA-ALAA..ARABIC LIGATURE ALAYHI AR-RAHMAH
 FBD3..FD3D    ; Grapheme_Base # Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
 FD3E          ; Grapheme_Base # Pe       ORNATE LEFT PARENTHESIS
 FD3F          ; Grapheme_Base # Ps       ORNATE RIGHT PARENTHESIS
 FD40..FD4F    ; Grapheme_Base # So  [16] ARABIC LIGATURE RAHIMAHU ALLAAH..ARABIC LIGATURE RAHIMAHUM ALLAAH
 FD50..FD8F    ; Grapheme_Base # Lo  [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+FD90..FD91    ; Grapheme_Base # So   [2] ARABIC LIGATURE RAHMATU ALLAAHI ALAYH..ARABIC LIGATURE RAHMATU ALLAAHI ALAYHAA
 FD92..FDC7    ; Grapheme_Base # Lo  [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
-FDCF          ; Grapheme_Base # So       ARABIC LIGATURE SALAAMUHU ALAYNAA
+FDC8..FDCF    ; Grapheme_Base # So   [8] ARABIC LIGATURE RAHIMAHU ALLAAH TAAALAA..ARABIC LIGATURE SALAAMUHU ALAYNAA
 FDF0..FDFB    ; Grapheme_Base # Lo  [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU
 FDFC          ; Grapheme_Base # Sc       RIAL SIGN
 FDFD..FDFF    ; Grapheme_Base # So   [3] ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM..ARABIC LIGATURE AZZA WA JALL
@@ -12258,7 +12654,7 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 10760..10767  ; Grapheme_Base # Lo   [8] LINEAR A SIGN A800..LINEAR A SIGN A807
 10780..10785  ; Grapheme_Base # Lm   [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK
 10787..107B0  ; Grapheme_Base # Lm  [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
-107B2..107BA  ; Grapheme_Base # Lm   [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
+107B2..107BF  ; Grapheme_Base # Lm  [14] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL ESH WITH DOUBLE BAR
 10800..10805  ; Grapheme_Base # Lo   [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA
 10808         ; Grapheme_Base # Lo       CYPRIOT SYLLABLE JO
 1080A..10835  ; Grapheme_Base # Lo  [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO
@@ -12280,6 +12676,7 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 1091F         ; Grapheme_Base # Po       PHOENICIAN WORD SEPARATOR
 10920..10939  ; Grapheme_Base # Lo  [26] LYDIAN LETTER A..LYDIAN LETTER C
 1093F         ; Grapheme_Base # Po       LYDIAN TRIANGULAR MARK
+10940..10959  ; Grapheme_Base # Lo  [26] SIDETIC LETTER N01..SIDETIC LETTER N26
 10980..109B7  ; Grapheme_Base # Lo  [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA
 109BC..109BD  ; Grapheme_Base # No   [2] MEROITIC CURSIVE FRACTION ELEVEN TWELFTHS..MEROITIC CURSIVE FRACTION ONE HALF
 109BE..109BF  ; Grapheme_Base # Lo   [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN
@@ -12330,6 +12727,12 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 10EAD         ; Grapheme_Base # Pd       YEZIDI HYPHENATION MARK
 10EB0..10EB1  ; Grapheme_Base # Lo   [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
 10EC2..10EC4  ; Grapheme_Base # Lo   [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW
+10EC5         ; Grapheme_Base # Lm       ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW
+10EC6..10EC7  ; Grapheme_Base # Lo   [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW
+10EC9..10ECA  ; Grapheme_Base # Sk   [2] ARABIC SMALL BASELINE FATHA..ARABIC SMALL BASELINE DOTLESS HEAD OF KHAH
+10ED0         ; Grapheme_Base # Po       ARABIC BIBLICAL END OF VERSE
+10ED1..10ED8  ; Grapheme_Base # So   [8] ARABIC LIGATURE ALAYHAA AS-SALAATU WAS-SALAAM..ARABIC LIGATURE NAWWARA ALLAAHU MARQADAH
+10ED9..10EEE  ; Grapheme_Base # Lo  [22] ARABIC CROWN LETTER BEH..ARABIC CROWN LETTER YEH
 10F00..10F1C  ; Grapheme_Base # Lo  [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL
 10F1D..10F26  ; Grapheme_Base # No  [10] OLD SOGDIAN NUMBER ONE..OLD SOGDIAN FRACTION ONE HALF
 10F27         ; Grapheme_Base # Lo       OLD SOGDIAN LIGATURE AYIN-DALETH
@@ -12515,6 +12918,10 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 11A9E..11AA2  ; Grapheme_Base # Po   [5] SOYOMBO HEAD MARK WITH MOON AND SUN AND TRIPLE FLAME..SOYOMBO TERMINAL MARK-2
 11AB0..11AF8  ; Grapheme_Base # Lo  [73] CANADIAN SYLLABICS NATTILIK HI..PAU CIN HAU GLOTTAL STOP FINAL
 11B00..11B09  ; Grapheme_Base # Po  [10] DEVANAGARI HEAD MARK..DEVANAGARI SIGN MINDU
+11B0A         ; Grapheme_Base # Lo       DEVANAGARI LETTER ALTERNATE DDDA
+11B61         ; Grapheme_Base # Mc       SHARADA VOWEL SIGN OOE
+11B65         ; Grapheme_Base # Mc       SHARADA VOWEL SIGN SHORT O
+11B67         ; Grapheme_Base # Mc       SHARADA VOWEL SIGN CANDRA O
 11BC0..11BE0  ; Grapheme_Base # Lo  [33] SUNUWAR LETTER DEVI..SUNUWAR LETTER KLOKO
 11BE1         ; Grapheme_Base # Po       SUNUWAR SIGN PVO
 11BF0..11BF9  ; Grapheme_Base # Nd  [10] SUNUWAR DIGIT ZERO..SUNUWAR DIGIT NINE
@@ -12544,6 +12951,11 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 11D96         ; Grapheme_Base # Mc       GUNJALA GONDI SIGN VISARGA
 11D98         ; Grapheme_Base # Lo       GUNJALA GONDI OM
 11DA0..11DA9  ; Grapheme_Base # Nd  [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE
+11DB0..11DD8  ; Grapheme_Base # Lo  [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH
+11DD9         ; Grapheme_Base # Lm       TOLONG SIKI SIGN SELA
+11DDA..11DDB  ; Grapheme_Base # Lo   [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA
+11DE0..11DE9  ; Grapheme_Base # Nd  [10] TOLONG SIKI DIGIT ZERO..TOLONG SIKI DIGIT NINE
+11DF1         ; Grapheme_Base # Lo       BENGALI LETTER ALTERNATE BARGIYA BA
 11EE0..11EF2  ; Grapheme_Base # Lo  [19] MAKASAR LETTER KA..MAKASAR ANGKA
 11EF5..11EF6  ; Grapheme_Base # Mc   [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
 11EF7..11EF8  ; Grapheme_Base # Po   [2] MAKASAR PASSIMBANG..MAKASAR END OF SECTION
@@ -12562,9 +12974,11 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 11FE1..11FF1  ; Grapheme_Base # So  [17] TAMIL SIGN PAARAM..TAMIL SIGN VAKAIYARAA
 11FFF         ; Grapheme_Base # Po       TAMIL PUNCTUATION END OF TEXT
 12000..12399  ; Grapheme_Base # Lo [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U
-12400..1246E  ; Grapheme_Base # Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM
+12400..1246F  ; Grapheme_Base # Nl [112] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN SEVEN ASH TENU
 12470..12474  ; Grapheme_Base # Po   [5] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON
+12475..1247F  ; Grapheme_Base # Nl  [11] CUNEIFORM NUMERIC SIGN EIGHT ASH TENU..CUNEIFORM NUMERIC SIGN ASH TIMES NINE DISH TENU
 12480..12543  ; Grapheme_Base # Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU
+12550..12686  ; Grapheme_Base # Nl [311] CUNEIFORM NUMERIC SIGN ONE N01..CUNEIFORM NUMERIC SIGN ONE N36 FLAT
 12F90..12FF0  ; Grapheme_Base # Lo  [97] CYPRO-MINOAN SIGN CM001..CYPRO-MINOAN SIGN CM114
 12FF1..12FF2  ; Grapheme_Base # Po   [2] CYPRO-MINOAN SIGN CM301..CYPRO-MINOAN SIGN CM302
 13000..1342F  ; Grapheme_Base # Lo [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D
@@ -12597,9 +13011,14 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 16D6B..16D6C  ; Grapheme_Base # Lm   [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT
 16D6D..16D6F  ; Grapheme_Base # Po   [3] KIRAT RAI SIGN YUPI..KIRAT RAI DOUBLE DANDA
 16D70..16D79  ; Grapheme_Base # Nd  [10] KIRAT RAI DIGIT ZERO..KIRAT RAI DIGIT NINE
+16D80..16D97  ; Grapheme_Base # Lo  [24] CHISOI LETTER A..CHISOI LETTER PA
+16D99..16D9C  ; Grapheme_Base # Lo   [4] CHISOI LETTER YA..CHISOI LETTER JARAHA
+16DA0..16DA9  ; Grapheme_Base # Nd  [10] CHISOI DIGIT ZERO..CHISOI DIGIT NINE
 16E40..16E7F  ; Grapheme_Base # L&  [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y
 16E80..16E96  ; Grapheme_Base # No  [23] MEDEFAIDRIN DIGIT ZERO..MEDEFAIDRIN DIGIT THREE ALTERNATE FORM
 16E97..16E9A  ; Grapheme_Base # Po   [4] MEDEFAIDRIN COMMA..MEDEFAIDRIN EXCLAMATION OH
+16EA0..16EB8  ; Grapheme_Base # L&  [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
+16EBB..16ED3  ; Grapheme_Base # L&  [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
 16F00..16F4A  ; Grapheme_Base # Lo  [75] MIAO LETTER PA..MIAO LETTER RTE
 16F50         ; Grapheme_Base # Lo       MIAO LETTER NASALIZATION
 16F51..16F87  ; Grapheme_Base # Mc  [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
@@ -12607,17 +13026,21 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 16FE0..16FE1  ; Grapheme_Base # Lm   [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK
 16FE2         ; Grapheme_Base # Po       OLD CHINESE HOOK MARK
 16FE3         ; Grapheme_Base # Lm       OLD CHINESE ITERATION MARK
-17000..187F7  ; Grapheme_Base # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
-18800..18CD5  ; Grapheme_Base # Lo [1238] TANGUT COMPONENT-001..KHITAN SMALL SCRIPT CHARACTER-18CD5
-18CFF..18D08  ; Grapheme_Base # Lo  [10] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D08
+16FF2..16FF3  ; Grapheme_Base # Lm   [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER
+16FF4..16FF6  ; Grapheme_Base # Nl   [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS
+17000..18CDA  ; Grapheme_Base # Lo [7387] TANGUT IDEOGRAPH-17000..KHITAN SMALL SCRIPT CHARACTER-18CDA
+18CFF..18D20  ; Grapheme_Base # Lo  [34] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D20
+18D80..18DF2  ; Grapheme_Base # Lo [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883
+18E00..19191  ; Grapheme_Base # Lo [914] JURCHEN CHARACTER-18E00..JURCHEN CHARACTER-19191
+191A0..191D2  ; Grapheme_Base # Lo  [51] JURCHEN RADICAL-01..JURCHEN RADICAL-51
 1AFF0..1AFF3  ; Grapheme_Base # Lm   [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5
 1AFF5..1AFFB  ; Grapheme_Base # Lm   [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5
 1AFFD..1AFFE  ; Grapheme_Base # Lm   [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8
-1B000..1B122  ; Grapheme_Base # Lo [291] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ARCHAIC WU
+1B000..1B128  ; Grapheme_Base # Lo [297] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ALTERNATE WI
 1B132         ; Grapheme_Base # Lo       HIRAGANA LETTER SMALL KO
 1B150..1B152  ; Grapheme_Base # Lo   [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO
 1B155         ; Grapheme_Base # Lo       KATAKANA LETTER SMALL KO
-1B164..1B167  ; Grapheme_Base # Lo   [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N
+1B164..1B168  ; Grapheme_Base # Lo   [5] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL ARCHAIC YE
 1B170..1B2FB  ; Grapheme_Base # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
 1BC00..1BC6A  ; Grapheme_Base # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M
 1BC70..1BC7C  ; Grapheme_Base # Lo  [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK
@@ -12627,7 +13050,13 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 1BC9F         ; Grapheme_Base # Po       DUPLOYAN PUNCTUATION CHINOOK FULL STOP
 1CC00..1CCEF  ; Grapheme_Base # So [240] UP-POINTING GO-KART..OUTLINED LATIN CAPITAL LETTER Z
 1CCF0..1CCF9  ; Grapheme_Base # Nd  [10] OUTLINED DIGIT ZERO..OUTLINED DIGIT NINE
+1CCFA..1CCFC  ; Grapheme_Base # So   [3] SNAKE SYMBOL..NOSE SYMBOL
 1CD00..1CEB3  ; Grapheme_Base # So [436] BLOCK OCTANT-3..BLACK RIGHT TRIANGLE CARET
+1CEBA..1CED0  ; Grapheme_Base # So  [23] FRAGILE SYMBOL..LEUKOTHEA
+1CED2..1CED4  ; Grapheme_Base # So   [3] ALCHEMICAL SYMBOL FOR OIL INVERTED..ALCHEMICAL SYMBOL FOR SALT OF TARTAR
+1CEDD..1CEDF  ; Grapheme_Base # Sm   [3] SQUARE ROOT OF SQUARE ROOT..SQUARE ROOT OF SQUARE ROOT OF SQUARE ROOT OF SQUARE ROOT
+1CEE0..1CEEF  ; Grapheme_Base # So  [16] GEOMANTIC FIGURE POPULUS..GEOMANTIC FIGURE VIA
+1CEF0..1CEFD  ; Grapheme_Base # Sm  [14] MEDIUM SMALL WHITE CIRCLE WITH HORIZONTAL BAR..HYPERBOLA
 1CF50..1CFC3  ; Grapheme_Base # So [116] ZNAMENNY NEUME KRYUK..ZNAMENNY NEUME PAUK
 1D000..1D0F5  ; Grapheme_Base # So [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO
 1D100..1D126  ; Grapheme_Base # So  [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2
@@ -12635,9 +13064,11 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 1D16A..1D16C  ; Grapheme_Base # So   [3] MUSICAL SYMBOL FINGERED TREMOLO-1..MUSICAL SYMBOL FINGERED TREMOLO-3
 1D183..1D184  ; Grapheme_Base # So   [2] MUSICAL SYMBOL ARPEGGIATO UP..MUSICAL SYMBOL ARPEGGIATO DOWN
 1D18C..1D1A9  ; Grapheme_Base # So  [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL DEGREE SLASH
-1D1AE..1D1EA  ; Grapheme_Base # So  [61] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL KORON
-1D200..1D241  ; Grapheme_Base # So  [66] GREEK VOCAL NOTATION SYMBOL-1..GREEK INSTRUMENTAL NOTATION SYMBOL-54
+1D1AE..1D241  ; Grapheme_Base # So [148] MUSICAL SYMBOL PEDAL MARK..GREEK INSTRUMENTAL NOTATION SYMBOL-54
 1D245         ; Grapheme_Base # So       GREEK MUSICAL LEIMMA
+1D253..1D25A  ; Grapheme_Base # So   [8] MUSICAL SYMBOL TWO HUNDRED FIFTY-SIXTH REST..MUSICAL SYMBOL SHARP WITH STROKE
+1D25D..1D25E  ; Grapheme_Base # So   [2] MUSICAL SYMBOL FINGERED TREMOLO-4..MUSICAL SYMBOL FINGERED TREMOLO-5
+1D260..1D27F  ; Grapheme_Base # So  [32] MUSICAL SYMBOL TRIPLE FLAT..MUSICAL SYMBOL ARPEGGIATO UP WITH ARROW
 1D2C0..1D2D3  ; Grapheme_Base # No  [20] KAKTOVIK NUMERAL ZERO..KAKTOVIK NUMERAL NINETEEN
 1D2E0..1D2F3  ; Grapheme_Base # No  [20] MAYAN NUMERAL ZERO..MAYAN NUMERAL NINETEEN
 1D300..1D356  ; Grapheme_Base # So  [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING
@@ -12660,7 +13091,7 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 1D540..1D544  ; Grapheme_Base # L&   [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
 1D546         ; Grapheme_Base # L&       MATHEMATICAL DOUBLE-STRUCK CAPITAL O
 1D54A..1D550  ; Grapheme_Base # L&   [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
-1D552..1D6A5  ; Grapheme_Base # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D552..1D6A6  ; Grapheme_Base # L& [341] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL LIGATURE LONG S WITH DESCENDER S
 1D6A8..1D6C0  ; Grapheme_Base # L&  [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
 1D6C1         ; Grapheme_Base # Sm       MATHEMATICAL BOLD NABLA
 1D6C2..1D6DA  ; Grapheme_Base # L&  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
@@ -12689,10 +13120,13 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 1DA76..1DA83  ; Grapheme_Base # So  [14] SIGNWRITING LIMB COMBINATION..SIGNWRITING LOCATION DEPTH
 1DA85..1DA86  ; Grapheme_Base # So   [2] SIGNWRITING LOCATION TORSO..SIGNWRITING LOCATION LIMBS DIGITS
 1DA87..1DA8B  ; Grapheme_Base # Po   [5] SIGNWRITING COMMA..SIGNWRITING PARENTHESIS
+1DB00..1DB1C  ; Grapheme_Base # Sm  [29] LEIBNIZIAN EQUALS SIGN..FACIT SYMBOL
 1DF00..1DF09  ; Grapheme_Base # L&  [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
 1DF0A         ; Grapheme_Base # Lo       LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK
-1DF0B..1DF1E  ; Grapheme_Base # L&  [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL
-1DF25..1DF2A  ; Grapheme_Base # L&   [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK
+1DF0B..1DF7F  ; Grapheme_Base # L& [117] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER REVERSED U
+1DF80..1DF81  ; Grapheme_Base # Lo   [2] LATIN CAPITAL LETTER A WITH TOPBAR..LATIN CAPITAL LETTER E WITH BENT TOPBAR
+1DF90..1DF96  ; Grapheme_Base # L&   [7] LATIN SMALL LETTER C WITH LOW SLASH..LATIN SMALL LETTER LONG S WITH TOP LOOP
+1DFCD..1DFFF  ; Grapheme_Base # Lm  [51] MODIFIER LETTER SMALL TURNED R WITH MID-HEIGHT LEFT HOOK..MODIFIER LETTER SMALL T WITH HOOK AND RETROFLEX HOOK
 1E030..1E06D  ; Grapheme_Base # Lm  [62] MODIFIER LETTER CYRILLIC SMALL A..MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE
 1E100..1E12C  ; Grapheme_Base # Lo  [45] NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W
 1E137..1E13D  ; Grapheme_Base # Lm   [7] NYIAKENG PUACHUE HMONG SIGN FOR PERSON..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER
@@ -12710,6 +13144,13 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 1E5F0         ; Grapheme_Base # Lo       OL ONAL SIGN HODDOND
 1E5F1..1E5FA  ; Grapheme_Base # Nd  [10] OL ONAL DIGIT ZERO..OL ONAL DIGIT NINE
 1E5FF         ; Grapheme_Base # Po       OL ONAL ABBREVIATION SIGN
+1E6C0..1E6DE  ; Grapheme_Base # Lo  [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO
+1E6E0..1E6E2  ; Grapheme_Base # Lo   [3] TAI YO LETTER AA..TAI YO LETTER UE
+1E6E4..1E6E5  ; Grapheme_Base # Lo   [2] TAI YO LETTER U..TAI YO LETTER AE
+1E6E7..1E6ED  ; Grapheme_Base # Lo   [7] TAI YO LETTER O..TAI YO LETTER AUE
+1E6F0..1E6F4  ; Grapheme_Base # Lo   [5] TAI YO LETTER AN..TAI YO LETTER AP
+1E6FE         ; Grapheme_Base # Lo       TAI YO SYMBOL MUEANG
+1E6FF         ; Grapheme_Base # Lm       TAI YO XAM LAI
 1E7E0..1E7E6  ; Grapheme_Base # Lo   [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO
 1E7E8..1E7EB  ; Grapheme_Base # Lo   [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE
 1E7ED..1E7EE  ; Grapheme_Base # Lo   [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE
@@ -12769,7 +13210,7 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 1F0C1..1F0CF  ; Grapheme_Base # So  [15] PLAYING CARD ACE OF DIAMONDS..PLAYING CARD BLACK JOKER
 1F0D1..1F0F5  ; Grapheme_Base # So  [37] PLAYING CARD ACE OF CLUBS..PLAYING CARD TRUMP-21
 1F100..1F10C  ; Grapheme_Base # No  [13] DIGIT ZERO FULL STOP..DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ZERO
-1F10D..1F1AD  ; Grapheme_Base # So [161] CIRCLED ZERO WITH SLASH..MASK WORK SYMBOL
+1F10D..1F1AE  ; Grapheme_Base # So [162] CIRCLED ZERO WITH SLASH..TOMOBIKI SYMBOL
 1F1E6..1F202  ; Grapheme_Base # So  [29] REGIONAL INDICATOR SYMBOL LETTER A..SQUARED KATAKANA SA
 1F210..1F23B  ; Grapheme_Base # So  [44] SQUARED CJK UNIFIED IDEOGRAPH-624B..SQUARED CJK UNIFIED IDEOGRAPH-914D
 1F240..1F248  ; Grapheme_Base # So   [9] TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C..TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557
@@ -12777,42 +13218,42 @@ FFFC..FFFD    ; Grapheme_Base # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEME
 1F260..1F265  ; Grapheme_Base # So   [6] ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
 1F300..1F3FA  ; Grapheme_Base # So [251] CYCLONE..AMPHORA
 1F3FB..1F3FF  ; Grapheme_Base # Sk   [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6
-1F400..1F6D7  ; Grapheme_Base # So [728] RAT..ELEVATOR
+1F400..1F6D9  ; Grapheme_Base # So [730] RAT..LIGHTHOUSE
 1F6DC..1F6EC  ; Grapheme_Base # So  [17] WIRELESS..AIRPLANE ARRIVING
 1F6F0..1F6FC  ; Grapheme_Base # So  [13] SATELLITE..ROLLER SKATE
-1F700..1F776  ; Grapheme_Base # So [119] ALCHEMICAL SYMBOL FOR QUINTESSENCE..LUNAR ECLIPSE
-1F77B..1F7D9  ; Grapheme_Base # So  [95] HAUMEA..NINE POINTED WHITE STAR
+1F700..1F7DB  ; Grapheme_Base # So [220] ALCHEMICAL SYMBOL FOR QUINTESSENCE..BULLET IN DOUBLE CIRCLE
 1F7E0..1F7EB  ; Grapheme_Base # So  [12] LARGE ORANGE CIRCLE..LARGE BROWN SQUARE
-1F7F0         ; Grapheme_Base # So       HEAVY EQUALS SIGN
-1F800..1F80B  ; Grapheme_Base # So  [12] LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD
+1F7F0..1F80B  ; Grapheme_Base # So  [28] HEAVY EQUALS SIGN..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD
 1F810..1F847  ; Grapheme_Base # So  [56] LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD..DOWNWARDS HEAVY ARROW
 1F850..1F859  ; Grapheme_Base # So  [10] LEFTWARDS SANS-SERIF ARROW..UP DOWN SANS-SERIF ARROW
 1F860..1F887  ; Grapheme_Base # So  [40] WIDE-HEADED LEFTWARDS LIGHT BARB ARROW..WIDE-HEADED SOUTH WEST VERY HEAVY BARB ARROW
 1F890..1F8AD  ; Grapheme_Base # So  [30] LEFTWARDS TRIANGLE ARROWHEAD..WHITE ARROW SHAFT WIDTH TWO THIRDS
 1F8B0..1F8BB  ; Grapheme_Base # So  [12] ARROW POINTING UPWARDS THEN NORTH WEST..SOUTH WEST ARROW FROM BAR
 1F8C0..1F8C1  ; Grapheme_Base # So   [2] LEFTWARDS ARROW FROM DOWNWARDS ARROW..RIGHTWARDS ARROW FROM DOWNWARDS ARROW
-1F900..1FA53  ; Grapheme_Base # So [340] CIRCLED CROSS FORMEE WITH FOUR DOTS..BLACK CHESS KNIGHT-BISHOP
+1F8D0..1F8D8  ; Grapheme_Base # Sm   [9] LONG RIGHTWARDS ARROW OVER LONG LEFTWARDS ARROW..LONG LEFT RIGHT ARROW WITH DEPENDENT LOBE
+1F900..1FA57  ; Grapheme_Base # So [344] CIRCLED CROSS FORMEE WITH FOUR DOTS..BLACK CHESS ALFIL
 1FA60..1FA6D  ; Grapheme_Base # So  [14] XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
 1FA70..1FA7C  ; Grapheme_Base # So  [13] BALLET SHOES..CRUTCH
-1FA80..1FA89  ; Grapheme_Base # So  [10] YO-YO..HARP
-1FA8F..1FAC6  ; Grapheme_Base # So  [56] SHOVEL..FINGERPRINT
-1FACE..1FADC  ; Grapheme_Base # So  [15] MOOSE..ROOT VEGETABLE
-1FADF..1FAE9  ; Grapheme_Base # So  [11] SPLATTER..FACE WITH BAGS UNDER EYES
-1FAF0..1FAF8  ; Grapheme_Base # So   [9] HAND WITH INDEX FINGER AND THUMB CROSSED..RIGHTWARDS PUSHING HAND
+1FA80..1FAC6  ; Grapheme_Base # So  [71] YO-YO..FINGERPRINT
+1FAC8         ; Grapheme_Base # So       HAIRY CREATURE
+1FACC..1FADD  ; Grapheme_Base # So  [18] MONARCH BUTTERFLY..PICKLE
+1FADF..1FAEB  ; Grapheme_Base # So  [13] SPLATTER..CRACKING FACE
+1FAEF..1FAFA  ; Grapheme_Base # So  [12] FIGHT CLOUD..RIGHTWARDS THUMB SIGN
 1FB00..1FB92  ; Grapheme_Base # So [147] BLOCK SEXTANT-1..UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK
 1FB94..1FBEF  ; Grapheme_Base # So  [92] LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK..TOP LEFT JUSTIFIED LOWER RIGHT QUARTER BLACK CIRCLE
 1FBF0..1FBF9  ; Grapheme_Base # Nd  [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE
+1FBFA         ; Grapheme_Base # So       ALARM BELL SYMBOL
 20000..2A6DF  ; Grapheme_Base # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF
-2A700..2B739  ; Grapheme_Base # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739
-2B740..2B81D  ; Grapheme_Base # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D
-2B820..2CEA1  ; Grapheme_Base # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1
+2A700..2B81E  ; Grapheme_Base # Lo [4383] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81E
+2B820..2CEAD  ; Grapheme_Base # Lo [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD
 2CEB0..2EBE0  ; Grapheme_Base # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
 2EBF0..2EE5D  ; Grapheme_Base # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D
 2F800..2FA1D  ; Grapheme_Base # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
 30000..3134A  ; Grapheme_Base # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A
-31350..323AF  ; Grapheme_Base # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF
+31350..33479  ; Grapheme_Base # Lo [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479
+3D000..3FC3F  ; Grapheme_Base # Lo [11328] SEAL CHARACTER-3D000..SEAL CHARACTER-3FC3F
 
-# Total code points: 152730
+# Total code points: 170502
 
 # ================================================
 
@@ -12884,8 +13325,9 @@ ABED          ; Grapheme_Link # Mn       MEETEI MAYEK APUN IYEK
 11F41         ; Grapheme_Link # Mc       KAWI SIGN KILLER
 11F42         ; Grapheme_Link # Mn       KAWI CONJOINER
 1612F         ; Grapheme_Link # Mn       GURUNG KHEMA SIGN THOLHOMA
+16D9D         ; Grapheme_Link # Mn       CHISOI SIGN SISO
 
-# Total code points: 69
+# Total code points: 70
 
 # ================================================
 
@@ -12909,8 +13351,24 @@ ABED          ; Grapheme_Link # Mn       MEETEI MAYEK APUN IYEK
 0B4D          ; InCB; Linker # Mn       ORIYA SIGN VIRAMA
 0C4D          ; InCB; Linker # Mn       TELUGU SIGN VIRAMA
 0D4D          ; InCB; Linker # Mn       MALAYALAM SIGN VIRAMA
+1039          ; InCB; Linker # Mn       MYANMAR SIGN VIRAMA
+17D2          ; InCB; Linker # Mn       KHMER SIGN COENG
+1A60          ; InCB; Linker # Mn       TAI THAM SIGN SAKOT
+1B44          ; InCB; Linker # Mc       BALINESE ADEG ADEG
+1BAB          ; InCB; Linker # Mn       SUNDANESE SIGN VIRAMA
+1CF5..1CF6    ; InCB; Linker # Lo   [2] VEDIC SIGN JIHVAMULIYA..VEDIC SIGN UPADHMANIYA
+A9C0          ; InCB; Linker # Mc       JAVANESE PANGKON
+AAF6          ; InCB; Linker # Mn       MEETEI MAYEK VIRAMA
+10A3F         ; InCB; Linker # Mn       KHAROSHTHI VIRAMA
+11133         ; InCB; Linker # Mn       CHAKMA VIRAMA
+113D0         ; InCB; Linker # Mn       TULU-TIGALARI CONJOINER
+1193E         ; InCB; Linker # Mn       DIVES AKURU VIRAMA
+11A3A         ; InCB; Linker # Lo       ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA
+11A47         ; InCB; Linker # Mn       ZANABAZAR SQUARE SUBJOINER
+11A99         ; InCB; Linker # Mn       SOYOMBO SUBJOINER
+11F42         ; InCB; Linker # Mn       KAWI CONJOINER
 
-# Total code points: 6
+# Total code points: 23
 
 # ================================================
 
@@ -12942,8 +13400,60 @@ ABED          ; Grapheme_Link # Mn       MEETEI MAYEK APUN IYEK
 0C2A..0C39    ; InCB; Consonant # Lo  [16] TELUGU LETTER PA..TELUGU LETTER HA
 0C58..0C5A    ; InCB; Consonant # Lo   [3] TELUGU LETTER TSA..TELUGU LETTER RRRA
 0D15..0D3A    ; InCB; Consonant # Lo  [38] MALAYALAM LETTER KA..MALAYALAM LETTER TTTA
+1000..102A    ; InCB; Consonant # Lo  [43] MYANMAR LETTER KA..MYANMAR LETTER AU
+103F          ; InCB; Consonant # Lo       MYANMAR LETTER GREAT SA
+1050..1055    ; InCB; Consonant # Lo   [6] MYANMAR LETTER SHA..MYANMAR LETTER VOCALIC LL
+105A..105D    ; InCB; Consonant # Lo   [4] MYANMAR LETTER MON NGA..MYANMAR LETTER MON BBE
+1061          ; InCB; Consonant # Lo       MYANMAR LETTER SGAW KAREN SHA
+1065..1066    ; InCB; Consonant # Lo   [2] MYANMAR LETTER WESTERN PWO KAREN THA..MYANMAR LETTER WESTERN PWO KAREN PWA
+106E..1070    ; InCB; Consonant # Lo   [3] MYANMAR LETTER EASTERN PWO KAREN NNA..MYANMAR LETTER EASTERN PWO KAREN GHWA
+1075..1081    ; InCB; Consonant # Lo  [13] MYANMAR LETTER SHAN KA..MYANMAR LETTER SHAN HA
+108E          ; InCB; Consonant # Lo       MYANMAR LETTER RUMAI PALAUNG FA
+1780..17B3    ; InCB; Consonant # Lo  [52] KHMER LETTER KA..KHMER INDEPENDENT VOWEL QAU
+1A20..1A54    ; InCB; Consonant # Lo  [53] TAI THAM LETTER HIGH KA..TAI THAM LETTER GREAT SA
+1B0B..1B0C    ; InCB; Consonant # Lo   [2] BALINESE LETTER RA REPA..BALINESE LETTER RA REPA TEDUNG
+1B13..1B33    ; InCB; Consonant # Lo  [33] BALINESE LETTER KA..BALINESE LETTER HA
+1B45..1B4C    ; InCB; Consonant # Lo   [8] BALINESE LETTER KAF SASAK..BALINESE LETTER ARCHAIC JNYA
+1B83..1BA0    ; InCB; Consonant # Lo  [30] SUNDANESE LETTER A..SUNDANESE LETTER HA
+1BAE..1BAF    ; InCB; Consonant # Lo   [2] SUNDANESE LETTER KHA..SUNDANESE LETTER SYA
+1BBB..1BBD    ; InCB; Consonant # Lo   [3] SUNDANESE LETTER REU..SUNDANESE LETTER BHA
+A989..A98B    ; InCB; Consonant # Lo   [3] JAVANESE LETTER PA CEREK..JAVANESE LETTER NGA LELET RASWADI
+A98F..A9B2    ; InCB; Consonant # Lo  [36] JAVANESE LETTER KA..JAVANESE LETTER HA
+A9E0..A9E4    ; InCB; Consonant # Lo   [5] MYANMAR LETTER SHAN GHA..MYANMAR LETTER SHAN BHA
+A9E7..A9EF    ; InCB; Consonant # Lo   [9] MYANMAR LETTER TAI LAING NYA..MYANMAR LETTER TAI LAING NNA
+A9FA..A9FE    ; InCB; Consonant # Lo   [5] MYANMAR LETTER TAI LAING LLA..MYANMAR LETTER TAI LAING BHA
+AA60..AA6F    ; InCB; Consonant # Lo  [16] MYANMAR LETTER KHAMTI GA..MYANMAR LETTER KHAMTI FA
+AA71..AA73    ; InCB; Consonant # Lo   [3] MYANMAR LETTER KHAMTI XA..MYANMAR LETTER KHAMTI RA
+AA7A          ; InCB; Consonant # Lo       MYANMAR LETTER AITON RA
+AA7E..AA7F    ; InCB; Consonant # Lo   [2] MYANMAR LETTER SHWE PALAUNG CHA..MYANMAR LETTER SHWE PALAUNG SHA
+AAE0..AAEA    ; InCB; Consonant # Lo  [11] MEETEI MAYEK LETTER E..MEETEI MAYEK LETTER SSA
+ABC0..ABDA    ; InCB; Consonant # Lo  [27] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER BHAM
+10A00         ; InCB; Consonant # Lo       KHAROSHTHI LETTER A
+10A10..10A13  ; InCB; Consonant # Lo   [4] KHAROSHTHI LETTER KA..KHAROSHTHI LETTER GHA
+10A15..10A17  ; InCB; Consonant # Lo   [3] KHAROSHTHI LETTER CA..KHAROSHTHI LETTER JA
+10A19..10A35  ; InCB; Consonant # Lo  [29] KHAROSHTHI LETTER NYA..KHAROSHTHI LETTER VHA
+11103..11126  ; InCB; Consonant # Lo  [36] CHAKMA LETTER AA..CHAKMA LETTER HAA
+11144         ; InCB; Consonant # Lo       CHAKMA LETTER LHAA
+11147         ; InCB; Consonant # Lo       CHAKMA LETTER VAA
+11380..11389  ; InCB; Consonant # Lo  [10] TULU-TIGALARI LETTER A..TULU-TIGALARI LETTER VOCALIC LL
+1138B         ; InCB; Consonant # Lo       TULU-TIGALARI LETTER EE
+1138E         ; InCB; Consonant # Lo       TULU-TIGALARI LETTER AI
+11390..113B5  ; InCB; Consonant # Lo  [38] TULU-TIGALARI LETTER OO..TULU-TIGALARI LETTER LLLA
+11900..11906  ; InCB; Consonant # Lo   [7] DIVES AKURU LETTER A..DIVES AKURU LETTER E
+11909         ; InCB; Consonant # Lo       DIVES AKURU LETTER O
+1190C..11913  ; InCB; Consonant # Lo   [8] DIVES AKURU LETTER KA..DIVES AKURU LETTER JA
+11915..11916  ; InCB; Consonant # Lo   [2] DIVES AKURU LETTER NYA..DIVES AKURU LETTER TTA
+11918..1192F  ; InCB; Consonant # Lo  [24] DIVES AKURU LETTER DDA..DIVES AKURU LETTER ZA
+11A00         ; InCB; Consonant # Lo       ZANABAZAR SQUARE LETTER A
+11A0B..11A32  ; InCB; Consonant # Lo  [40] ZANABAZAR SQUARE LETTER KA..ZANABAZAR SQUARE LETTER KSSA
+11A50         ; InCB; Consonant # Lo       SOYOMBO LETTER A
+11A5C..11A83  ; InCB; Consonant # Lo  [40] SOYOMBO LETTER KA..SOYOMBO LETTER KSSA
+11B0A         ; InCB; Consonant # Lo       DEVANAGARI LETTER ALTERNATE DDDA
+11DF1         ; InCB; Consonant # Lo       BENGALI LETTER ALTERNATE BARGIYA BA
+11F04..11F10  ; InCB; Consonant # Lo  [13] KAWI LETTER A..KAWI LETTER O
+11F12..11F33  ; InCB; Consonant # Lo  [34] KAWI LETTER KA..KAWI LETTER JNYA
 
-# Total code points: 240
+# Total code points: 913
 
 # ================================================
 
@@ -12956,7 +13466,7 @@ ABED          ; Grapheme_Link # Mn       MEETEI MAYEK APUN IYEK
 05BF          ; InCB; Extend # Mn       HEBREW POINT RAFE
 05C1..05C2    ; InCB; Extend # Mn   [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
 05C4..05C5    ; InCB; Extend # Mn   [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
-05C7          ; InCB; Extend # Mn       HEBREW POINT QAMATS QATAN
+05C7..05C9    ; InCB; Extend # Mn   [3] HEBREW POINT QAMATS QATAN..HEBREW POINT DAGESH HAZAQ MUDGASH
 0610..061A    ; InCB; Extend # Mn  [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
 064B..065F    ; InCB; Extend # Mn  [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW
 0670          ; InCB; Extend # Mn       ARABIC LETTER SUPERSCRIPT ALEF
@@ -13008,7 +13518,7 @@ ABED          ; Grapheme_Link # Mn       MEETEI MAYEK APUN IYEK
 0B3E          ; InCB; Extend # Mc       ORIYA VOWEL SIGN AA
 0B3F          ; InCB; Extend # Mn       ORIYA VOWEL SIGN I
 0B41..0B44    ; InCB; Extend # Mn   [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR
-0B55..0B56    ; InCB; Extend # Mn   [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK
+0B53..0B56    ; InCB; Extend # Mn   [4] ORIYA SIGN DOT ABOVE..ORIYA AI LENGTH MARK
 0B57          ; InCB; Extend # Mc       ORIYA AU LENGTH MARK
 0B62..0B63    ; InCB; Extend # Mn   [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL
 0B82          ; InCB; Extend # Mn       TAMIL SIGN ANUSVARA
@@ -13065,7 +13575,7 @@ ABED          ; Grapheme_Link # Mn       MEETEI MAYEK APUN IYEK
 0FC6          ; InCB; Extend # Mn       TIBETAN SYMBOL PADMA GDAN
 102D..1030    ; InCB; Extend # Mn   [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU
 1032..1037    ; InCB; Extend # Mn   [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW
-1039..103A    ; InCB; Extend # Mn   [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT
+103A          ; InCB; Extend # Mn       MYANMAR SIGN ASAT
 103D..103E    ; InCB; Extend # Mn   [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA
 1058..1059    ; InCB; Extend # Mn   [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL
 105E..1060    ; InCB; Extend # Mn   [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA
@@ -13084,7 +13594,8 @@ ABED          ; Grapheme_Link # Mn       MEETEI MAYEK APUN IYEK
 17B4..17B5    ; InCB; Extend # Mn   [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
 17B7..17BD    ; InCB; Extend # Mn   [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA
 17C6          ; InCB; Extend # Mn       KHMER SIGN NIKAHIT
-17C9..17D3    ; InCB; Extend # Mn  [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT
+17C9..17D1    ; InCB; Extend # Mn   [9] KHMER SIGN MUUSIKATOAN..KHMER SIGN VIRIAM
+17D3          ; InCB; Extend # Mn       KHMER SIGN BATHAMASAT
 17DD          ; InCB; Extend # Mn       KHMER SIGN ATTHACAN
 180B..180D    ; InCB; Extend # Mn   [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
 180F          ; InCB; Extend # Mn       MONGOLIAN FREE VARIATION SELECTOR FOUR
@@ -13098,14 +13609,13 @@ ABED          ; Grapheme_Link # Mn       MEETEI MAYEK APUN IYEK
 1A1B          ; InCB; Extend # Mn       BUGINESE VOWEL SIGN AE
 1A56          ; InCB; Extend # Mn       TAI THAM CONSONANT SIGN MEDIAL LA
 1A58..1A5E    ; InCB; Extend # Mn   [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA
-1A60          ; InCB; Extend # Mn       TAI THAM SIGN SAKOT
 1A62          ; InCB; Extend # Mn       TAI THAM VOWEL SIGN MAI SAT
 1A65..1A6C    ; InCB; Extend # Mn   [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW
 1A73..1A7C    ; InCB; Extend # Mn  [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN
 1A7F          ; InCB; Extend # Mn       TAI THAM COMBINING CRYPTOGRAMMIC DOT
 1AB0..1ABD    ; InCB; Extend # Mn  [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
 1ABE          ; InCB; Extend # Me       COMBINING PARENTHESES OVERLAY
-1ABF..1ACE    ; InCB; Extend # Mn  [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T
+1ABF..1AF0    ; InCB; Extend # Mn  [50] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOUBLE COMMA ABOVE
 1B00..1B03    ; InCB; Extend # Mn   [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
 1B34          ; InCB; Extend # Mn       BALINESE SIGN REREKAN
 1B35          ; InCB; Extend # Mc       BALINESE VOWEL SIGN TEDUNG
@@ -13114,13 +13624,13 @@ ABED          ; Grapheme_Link # Mn       MEETEI MAYEK APUN IYEK
 1B3C          ; InCB; Extend # Mn       BALINESE VOWEL SIGN LA LENGA
 1B3D          ; InCB; Extend # Mc       BALINESE VOWEL SIGN LA LENGA TEDUNG
 1B42          ; InCB; Extend # Mn       BALINESE VOWEL SIGN PEPET
-1B43..1B44    ; InCB; Extend # Mc   [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG
+1B43          ; InCB; Extend # Mc       BALINESE VOWEL SIGN PEPET TEDUNG
 1B6B..1B73    ; InCB; Extend # Mn   [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG
 1B80..1B81    ; InCB; Extend # Mn   [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR
 1BA2..1BA5    ; InCB; Extend # Mn   [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU
 1BA8..1BA9    ; InCB; Extend # Mn   [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG
 1BAA          ; InCB; Extend # Mc       SUNDANESE SIGN PAMAAEH
-1BAB..1BAD    ; InCB; Extend # Mn   [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA
+1BAC..1BAD    ; InCB; Extend # Mn   [2] SUNDANESE CONSONANT SIGN PASANGAN MA..SUNDANESE CONSONANT SIGN PASANGAN WA
 1BE6          ; InCB; Extend # Mn       BATAK SIGN TOMPI
 1BE8..1BE9    ; InCB; Extend # Mn   [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE
 1BED          ; InCB; Extend # Mn       BATAK VOWEL SIGN KARO O
@@ -13167,7 +13677,6 @@ A980..A982    ; InCB; Extend # Mn   [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN L
 A9B3          ; InCB; Extend # Mn       JAVANESE SIGN CECAK TELU
 A9B6..A9B9    ; InCB; Extend # Mn   [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT
 A9BC..A9BD    ; InCB; Extend # Mn   [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET
-A9C0          ; InCB; Extend # Mc       JAVANESE PANGKON
 A9E5          ; InCB; Extend # Mn       MYANMAR SIGN SHAN SAW
 AA29..AA2E    ; InCB; Extend # Mn   [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
 AA31..AA32    ; InCB; Extend # Mn   [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
@@ -13181,7 +13690,6 @@ AAB7..AAB8    ; InCB; Extend # Mn   [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA
 AABE..AABF    ; InCB; Extend # Mn   [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK
 AAC1          ; InCB; Extend # Mn       TAI VIET TONE MAI THO
 AAEC..AAED    ; InCB; Extend # Mn   [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI
-AAF6          ; InCB; Extend # Mn       MEETEI MAYEK VIRAMA
 ABE5          ; InCB; Extend # Mn       MEETEI MAYEK VOWEL SIGN ANAP
 ABE8          ; InCB; Extend # Mn       MEETEI MAYEK VOWEL SIGN UNAP
 ABED          ; InCB; Extend # Mn       MEETEI MAYEK APUN IYEK
@@ -13196,12 +13704,12 @@ FF9E..FF9F    ; InCB; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HA
 10A05..10A06  ; InCB; Extend # Mn   [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O
 10A0C..10A0F  ; InCB; Extend # Mn   [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA
 10A38..10A3A  ; InCB; Extend # Mn   [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW
-10A3F         ; InCB; Extend # Mn       KHAROSHTHI VIRAMA
 10AE5..10AE6  ; InCB; Extend # Mn   [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW
 10D24..10D27  ; InCB; Extend # Mn   [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI
 10D69..10D6D  ; InCB; Extend # Mn   [5] GARAY VOWEL SIGN E..GARAY CONSONANT NASALIZATION MARK
 10EAB..10EAC  ; InCB; Extend # Mn   [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
-10EFC..10EFF  ; InCB; Extend # Mn   [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA
+10ECB..10ECF  ; InCB; Extend # Mn   [5] ARABIC NORTHEAST POINTING ARROWHEAD ABOVE..ARABIC LARGE CIRCLE ABOVE
+10EF0..10EFF  ; InCB; Extend # Mn  [16] ARABIC SMALL LOW UPRIGHT RECTANGULAR ZERO..ARABIC SMALL LOW WORD MADDA
 10F46..10F50  ; InCB; Extend # Mn  [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW
 10F82..10F85  ; InCB; Extend # Mn   [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW
 11001         ; InCB; Extend # Mn       BRAHMI SIGN ANUSVARA
@@ -13214,7 +13722,8 @@ FF9E..FF9F    ; InCB; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HA
 110C2         ; InCB; Extend # Mn       KAITHI VOWEL SIGN VOCALIC R
 11100..11102  ; InCB; Extend # Mn   [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA
 11127..1112B  ; InCB; Extend # Mn   [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU
-1112D..11134  ; InCB; Extend # Mn   [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA
+1112D..11132  ; InCB; Extend # Mn   [6] CHAKMA VOWEL SIGN AI..CHAKMA AU MARK
+11134         ; InCB; Extend # Mn       CHAKMA MAAYYAA
 11173         ; InCB; Extend # Mn       MAHAJANI SIGN NUKTA
 11180..11181  ; InCB; Extend # Mn   [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA
 111B6..111BE  ; InCB; Extend # Mn   [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O
@@ -13244,7 +13753,6 @@ FF9E..FF9F    ; InCB; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HA
 113C7..113C9  ; InCB; Extend # Mc   [3] TULU-TIGALARI VOWEL SIGN OO..TULU-TIGALARI AU LENGTH MARK
 113CE         ; InCB; Extend # Mn       TULU-TIGALARI SIGN VIRAMA
 113CF         ; InCB; Extend # Mc       TULU-TIGALARI SIGN LOOPED VIRAMA
-113D0         ; InCB; Extend # Mn       TULU-TIGALARI CONJOINER
 113D2         ; InCB; Extend # Mn       TULU-TIGALARI GEMINATION MARK
 113E1..113E2  ; InCB; Extend # Mn   [2] TULU-TIGALARI VEDIC TONE SVARITA..TULU-TIGALARI VEDIC TONE ANUDATTA
 11438..1143F  ; InCB; Extend # Mn   [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI
@@ -13279,7 +13787,6 @@ FF9E..FF9F    ; InCB; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HA
 11930         ; InCB; Extend # Mc       DIVES AKURU VOWEL SIGN AA
 1193B..1193C  ; InCB; Extend # Mn   [2] DIVES AKURU SIGN ANUSVARA..DIVES AKURU SIGN CANDRABINDU
 1193D         ; InCB; Extend # Mc       DIVES AKURU SIGN HALANTA
-1193E         ; InCB; Extend # Mn       DIVES AKURU VIRAMA
 11943         ; InCB; Extend # Mn       DIVES AKURU SIGN NUKTA
 119D4..119D7  ; InCB; Extend # Mn   [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR
 119DA..119DB  ; InCB; Extend # Mn   [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI
@@ -13287,11 +13794,13 @@ FF9E..FF9F    ; InCB; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HA
 11A01..11A0A  ; InCB; Extend # Mn  [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK
 11A33..11A38  ; InCB; Extend # Mn   [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA
 11A3B..11A3E  ; InCB; Extend # Mn   [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA
-11A47         ; InCB; Extend # Mn       ZANABAZAR SQUARE SUBJOINER
 11A51..11A56  ; InCB; Extend # Mn   [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE
 11A59..11A5B  ; InCB; Extend # Mn   [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK
 11A8A..11A96  ; InCB; Extend # Mn  [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA
-11A98..11A99  ; InCB; Extend # Mn   [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER
+11A98         ; InCB; Extend # Mn       SOYOMBO GEMINATION MARK
+11B60         ; InCB; Extend # Mn       SHARADA VOWEL SIGN OE
+11B62..11B64  ; InCB; Extend # Mn   [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E
+11B66         ; InCB; Extend # Mn       SHARADA VOWEL SIGN CANDRA E
 11C30..11C36  ; InCB; Extend # Mn   [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L
 11C38..11C3D  ; InCB; Extend # Mn   [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA
 11C3F         ; InCB; Extend # Mn       BHAIKSUKI SIGN VIRAMA
@@ -13307,12 +13816,12 @@ FF9E..FF9F    ; InCB; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HA
 11D90..11D91  ; InCB; Extend # Mn   [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI
 11D95         ; InCB; Extend # Mn       GUNJALA GONDI SIGN ANUSVARA
 11D97         ; InCB; Extend # Mn       GUNJALA GONDI VIRAMA
+11DF0         ; InCB; Extend # Mn       BENGALI SIGN COMBINING ANUSVARA ABOVE
 11EF3..11EF4  ; InCB; Extend # Mn   [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
 11F00..11F01  ; InCB; Extend # Mn   [2] KAWI SIGN CANDRABINDU..KAWI SIGN ANUSVARA
 11F36..11F3A  ; InCB; Extend # Mn   [5] KAWI VOWEL SIGN I..KAWI VOWEL SIGN VOCALIC R
 11F40         ; InCB; Extend # Mn       KAWI VOWEL SIGN EU
 11F41         ; InCB; Extend # Mc       KAWI SIGN KILLER
-11F42         ; InCB; Extend # Mn       KAWI CONJOINER
 11F5A         ; InCB; Extend # Mn       KAWI SIGN NUKTA
 13440         ; InCB; Extend # Mn       EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY
 13447..13455  ; InCB; Extend # Mn  [15] EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED
@@ -13320,6 +13829,8 @@ FF9E..FF9F    ; InCB; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HA
 1612D..1612F  ; InCB; Extend # Mn   [3] GURUNG KHEMA SIGN ANUSVARA..GURUNG KHEMA SIGN THOLHOMA
 16AF0..16AF4  ; InCB; Extend # Mn   [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
 16B30..16B36  ; InCB; Extend # Mn   [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
+16D98         ; InCB; Extend # Mn       CHISOI SIGN ANUSVARA
+16D9D         ; InCB; Extend # Mn       CHISOI SIGN SISO
 16F4F         ; InCB; Extend # Mn       MIAO SIGN CONSONANT MODIFIER BAR
 16F8F..16F92  ; InCB; Extend # Mn   [4] MIAO TONE RIGHT..MIAO TONE BELOW
 16FE4         ; InCB; Extend # Mn       KHITAN SMALL SCRIPT FILLER
@@ -13327,6 +13838,7 @@ FF9E..FF9F    ; InCB; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HA
 1BC9D..1BC9E  ; InCB; Extend # Mn   [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK
 1CF00..1CF2D  ; InCB; Extend # Mn  [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT
 1CF30..1CF46  ; InCB; Extend # Mn  [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG
+1D127..1D128  ; InCB; Extend # Mn   [2] MUSICAL SYMBOL COMBINING STRESS..MUSICAL SYMBOL COMBINING UNSTRESS
 1D165..1D166  ; InCB; Extend # Mc   [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
 1D167..1D169  ; InCB; Extend # Mn   [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
 1D16D..1D172  ; InCB; Extend # Mc   [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5
@@ -13334,6 +13846,10 @@ FF9E..FF9F    ; InCB; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HA
 1D185..1D18B  ; InCB; Extend # Mn   [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
 1D1AA..1D1AD  ; InCB; Extend # Mn   [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
 1D242..1D244  ; InCB; Extend # Mn   [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+1D250..1D252  ; InCB; Extend # Mc   [3] MUSICAL SYMBOL COMBINING FLAG-6..MUSICAL SYMBOL COMBINING FLAG-8
+1D25B..1D25C  ; InCB; Extend # Mn   [2] MUSICAL SYMBOL COMBINING TREMOLO-4..MUSICAL SYMBOL COMBINING TREMOLO-5
+1D25F         ; InCB; Extend # Mc       MUSICAL SYMBOL COMBINING BUZZ ROLL STEM
+1D280..1D281  ; InCB; Extend # Mc   [2] MUSICAL SYMBOL COMBINING STEM BOW BEHIND BRIDGE..MUSICAL SYMBOL COMBINING STEM BOW ON TOP OF BRIDGE
 1DA00..1DA36  ; InCB; Extend # Mn  [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN
 1DA3B..1DA6C  ; InCB; Extend # Mn  [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT
 1DA75         ; InCB; Extend # Mn       SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS
@@ -13351,12 +13867,16 @@ FF9E..FF9F    ; InCB; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HA
 1E2EC..1E2EF  ; InCB; Extend # Mn   [4] WANCHO TONE TUP..WANCHO TONE KOINI
 1E4EC..1E4EF  ; InCB; Extend # Mn   [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH
 1E5EE..1E5EF  ; InCB; Extend # Mn   [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR
+1E6E3         ; InCB; Extend # Mn       TAI YO SIGN UE
+1E6E6         ; InCB; Extend # Mn       TAI YO SIGN AU
+1E6EE..1E6EF  ; InCB; Extend # Mn   [2] TAI YO SIGN AY..TAI YO SIGN ANG
+1E6F5         ; InCB; Extend # Mn       TAI YO SIGN OM
 1E8D0..1E8D6  ; InCB; Extend # Mn   [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
 1E944..1E94A  ; InCB; Extend # Mn   [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
 1F3FB..1F3FF  ; InCB; Extend # Sk   [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6
 E0020..E007F  ; InCB; Extend # Cf  [96] TAG SPACE..CANCEL TAG
 E0100..E01EF  ; InCB; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
 
-# Total code points: 2192
+# Total code points: 2256
 
 # EOF
diff --git a/libcxx/utils/data/unicode/DerivedGeneralCategory.txt b/libcxx/utils/data/unicode/DerivedGeneralCategory.txt
index 07bf7bca93d93..74639501f8f62 100644
--- a/libcxx/utils/data/unicode/DerivedGeneralCategory.txt
+++ b/libcxx/utils/data/unicode/DerivedGeneralCategory.txt
@@ -1,6 +1,6 @@
-# DerivedGeneralCategory-16.0.0.txt
-# Date: 2024-04-30, 21:48:17 GMT
-# © 2024 Unicode®, Inc.
+# DerivedGeneralCategory-18.0.0.txt
+# Date: 2026-04-29, 19:44:02 GMT
+# © 2026 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
 # For terms of use and license, see https://www.unicode.org/terms_of_use.html
 #
@@ -21,10 +21,9 @@
 038D          ; Cn #       <reserved-038D>
 03A2          ; Cn #       <reserved-03A2>
 0530          ; Cn #       <reserved-0530>
-0557..0558    ; Cn #   [2] <reserved-0557>..<reserved-0558>
-058B..058C    ; Cn #   [2] <reserved-058B>..<reserved-058C>
+0557          ; Cn #       <reserved-0557>
 0590          ; Cn #       <reserved-0590>
-05C8..05CF    ; Cn #   [8] <reserved-05C8>..<reserved-05CF>
+05CA..05CF    ; Cn #   [6] <reserved-05CA>..<reserved-05CF>
 05EB..05EE    ; Cn #   [4] <reserved-05EB>..<reserved-05EE>
 05F5..05FF    ; Cn #  [11] <reserved-05F5>..<reserved-05FF>
 070E          ; Cn #       <reserved-070E>
@@ -36,7 +35,6 @@
 085C..085D    ; Cn #   [2] <reserved-085C>..<reserved-085D>
 085F          ; Cn #       <reserved-085F>
 086B..086F    ; Cn #   [5] <reserved-086B>..<reserved-086F>
-088F          ; Cn #       <reserved-088F>
 0892..0896    ; Cn #   [5] <reserved-0892>..<reserved-0896>
 0984          ; Cn #       <reserved-0984>
 098D..098E    ; Cn #   [2] <reserved-098D>..<reserved-098E>
@@ -91,7 +89,7 @@
 0B3A..0B3B    ; Cn #   [2] <reserved-0B3A>..<reserved-0B3B>
 0B45..0B46    ; Cn #   [2] <reserved-0B45>..<reserved-0B46>
 0B49..0B4A    ; Cn #   [2] <reserved-0B49>..<reserved-0B4A>
-0B4E..0B54    ; Cn #   [7] <reserved-0B4E>..<reserved-0B54>
+0B4E..0B52    ; Cn #   [5] <reserved-0B4E>..<reserved-0B52>
 0B58..0B5B    ; Cn #   [4] <reserved-0B58>..<reserved-0B5B>
 0B5E          ; Cn #       <reserved-0B5E>
 0B64..0B65    ; Cn #   [2] <reserved-0B64>..<reserved-0B65>
@@ -120,7 +118,7 @@
 0C49          ; Cn #       <reserved-0C49>
 0C4E..0C54    ; Cn #   [7] <reserved-0C4E>..<reserved-0C54>
 0C57          ; Cn #       <reserved-0C57>
-0C5B..0C5C    ; Cn #   [2] <reserved-0C5B>..<reserved-0C5C>
+0C5B          ; Cn #       <reserved-0C5B>
 0C5E..0C5F    ; Cn #   [2] <reserved-0C5E>..<reserved-0C5F>
 0C64..0C65    ; Cn #   [2] <reserved-0C64>..<reserved-0C65>
 0C70..0C76    ; Cn #   [7] <reserved-0C70>..<reserved-0C76>
@@ -132,7 +130,7 @@
 0CC5          ; Cn #       <reserved-0CC5>
 0CC9          ; Cn #       <reserved-0CC9>
 0CCE..0CD4    ; Cn #   [7] <reserved-0CCE>..<reserved-0CD4>
-0CD7..0CDC    ; Cn #   [6] <reserved-0CD7>..<reserved-0CDC>
+0CD7..0CDB    ; Cn #   [5] <reserved-0CD7>..<reserved-0CDB>
 0CDF          ; Cn #       <reserved-0CDF>
 0CE4..0CE5    ; Cn #   [2] <reserved-0CE4>..<reserved-0CE5>
 0CF0          ; Cn #       <reserved-0CF0>
@@ -228,7 +226,7 @@
 1A8A..1A8F    ; Cn #   [6] <reserved-1A8A>..<reserved-1A8F>
 1A9A..1A9F    ; Cn #   [6] <reserved-1A9A>..<reserved-1A9F>
 1AAE..1AAF    ; Cn #   [2] <reserved-1AAE>..<reserved-1AAF>
-1ACF..1AFF    ; Cn #  [49] <reserved-1ACF>..<reserved-1AFF>
+1AF1..1AFF    ; Cn #  [15] <reserved-1AF1>..<reserved-1AFF>
 1B4D          ; Cn #       <reserved-1B4D>
 1BF4..1BFB    ; Cn #   [8] <reserved-1BF4>..<reserved-1BFB>
 1C38..1C3A    ; Cn #   [3] <reserved-1C38>..<reserved-1C3A>
@@ -255,15 +253,12 @@
 1FFF          ; Cn #       <reserved-1FFF>
 2065          ; Cn #       <reserved-2065>
 2072..2073    ; Cn #   [2] <reserved-2072>..<reserved-2073>
-208F          ; Cn #       <reserved-208F>
-209D..209F    ; Cn #   [3] <reserved-209D>..<reserved-209F>
-20C1..20CF    ; Cn #  [15] <reserved-20C1>..<reserved-20CF>
+20C5..20CF    ; Cn #  [11] <reserved-20C5>..<reserved-20CF>
 20F1..20FF    ; Cn #  [15] <reserved-20F1>..<reserved-20FF>
 218C..218F    ; Cn #   [4] <reserved-218C>..<reserved-218F>
 242A..243F    ; Cn #  [22] <reserved-242A>..<reserved-243F>
 244B..245F    ; Cn #  [21] <reserved-244B>..<reserved-245F>
 2B74..2B75    ; Cn #   [2] <reserved-2B74>..<reserved-2B75>
-2B96          ; Cn #       <reserved-2B96>
 2CF4..2CF8    ; Cn #   [5] <reserved-2CF4>..<reserved-2CF8>
 2D26          ; Cn #       <reserved-2D26>
 2D28..2D2C    ; Cn #   [5] <reserved-2D28>..<reserved-2D2C>
@@ -279,7 +274,8 @@
 2DCF          ; Cn #       <reserved-2DCF>
 2DD7          ; Cn #       <reserved-2DD7>
 2DDF          ; Cn #       <reserved-2DDF>
-2E5E..2E7F    ; Cn #  [34] <reserved-2E5E>..<reserved-2E7F>
+2E5E..2E5F    ; Cn #   [2] <reserved-2E5E>..<reserved-2E5F>
+2E64..2E7F    ; Cn #  [28] <reserved-2E64>..<reserved-2E7F>
 2E9A          ; Cn #       <reserved-2E9A>
 2EF4..2EFF    ; Cn #  [12] <reserved-2EF4>..<reserved-2EFF>
 2FD6..2FEF    ; Cn #  [26] <reserved-2FD6>..<reserved-2FEF>
@@ -294,10 +290,8 @@ A48D..A48F    ; Cn #   [3] <reserved-A48D>..<reserved-A48F>
 A4C7..A4CF    ; Cn #   [9] <reserved-A4C7>..<reserved-A4CF>
 A62C..A63F    ; Cn #  [20] <reserved-A62C>..<reserved-A63F>
 A6F8..A6FF    ; Cn #   [8] <reserved-A6F8>..<reserved-A6FF>
-A7CE..A7CF    ; Cn #   [2] <reserved-A7CE>..<reserved-A7CF>
-A7D2          ; Cn #       <reserved-A7D2>
-A7D4          ; Cn #       <reserved-A7D4>
-A7DD..A7F1    ; Cn #  [21] <reserved-A7DD>..<reserved-A7F1>
+A7DE..A7E1    ; Cn #   [4] <reserved-A7DE>..<reserved-A7E1>
+A7E3..A7F0    ; Cn #  [14] <reserved-A7E3>..<reserved-A7F0>
 A82D..A82F    ; Cn #   [3] <reserved-A82D>..<reserved-A82F>
 A83A..A83F    ; Cn #   [6] <reserved-A83A>..<reserved-A83F>
 A878..A87F    ; Cn #   [8] <reserved-A878>..<reserved-A87F>
@@ -318,7 +312,7 @@ AB0F..AB10    ; Cn #   [2] <reserved-AB0F>..<reserved-AB10>
 AB17..AB1F    ; Cn #   [9] <reserved-AB17>..<reserved-AB1F>
 AB27          ; Cn #       <reserved-AB27>
 AB2F          ; Cn #       <reserved-AB2F>
-AB6C..AB6F    ; Cn #   [4] <reserved-AB6C>..<reserved-AB6F>
+AB6E..AB6F    ; Cn #   [2] <reserved-AB6E>..<reserved-AB6F>
 ABEE..ABEF    ; Cn #   [2] <reserved-ABEE>..<reserved-ABEF>
 ABFA..ABFF    ; Cn #   [6] <reserved-ABFA>..<reserved-ABFF>
 D7A4..D7AF    ; Cn #  [12] <reserved-D7A4>..<reserved-D7AF>
@@ -333,9 +327,6 @@ FB3D          ; Cn #       <reserved-FB3D>
 FB3F          ; Cn #       <reserved-FB3F>
 FB42          ; Cn #       <reserved-FB42>
 FB45          ; Cn #       <reserved-FB45>
-FBC3..FBD2    ; Cn #  [16] <reserved-FBC3>..<reserved-FBD2>
-FD90..FD91    ; Cn #   [2] <reserved-FD90>..<reserved-FD91>
-FDC8..FDCE    ; Cn #   [7] <reserved-FDC8>..<reserved-FDCE>
 FDD0..FDEF    ; Cn #  [32] <noncharacter-FDD0>..<noncharacter-FDEF>
 FE1A..FE1F    ; Cn #   [6] <reserved-FE1A>..<reserved-FE1F>
 FE53          ; Cn #       <reserved-FE53>
@@ -394,7 +385,7 @@ FFFE..FFFF    ; Cn #   [2] <noncharacter-FFFE>..<noncharacter-FFFF>
 10768..1077F  ; Cn #  [24] <reserved-10768>..<reserved-1077F>
 10786         ; Cn #       <reserved-10786>
 107B1         ; Cn #       <reserved-107B1>
-107BB..107FF  ; Cn #  [69] <reserved-107BB>..<reserved-107FF>
+107C0..107FF  ; Cn #  [64] <reserved-107C0>..<reserved-107FF>
 10806..10807  ; Cn #   [2] <reserved-10806>..<reserved-10807>
 10809         ; Cn #       <reserved-10809>
 10836         ; Cn #       <reserved-10836>
@@ -407,7 +398,7 @@ FFFE..FFFF    ; Cn #   [2] <noncharacter-FFFE>..<noncharacter-FFFF>
 108F6..108FA  ; Cn #   [5] <reserved-108F6>..<reserved-108FA>
 1091C..1091E  ; Cn #   [3] <reserved-1091C>..<reserved-1091E>
 1093A..1093E  ; Cn #   [5] <reserved-1093A>..<reserved-1093E>
-10940..1097F  ; Cn #  [64] <reserved-10940>..<reserved-1097F>
+1095A..1097F  ; Cn #  [38] <reserved-1095A>..<reserved-1097F>
 109B8..109BB  ; Cn #   [4] <reserved-109B8>..<reserved-109BB>
 109D0..109D1  ; Cn #   [2] <reserved-109D0>..<reserved-109D1>
 10A04         ; Cn #       <reserved-10A04>
@@ -439,7 +430,8 @@ FFFE..FFFF    ; Cn #   [2] <noncharacter-FFFE>..<noncharacter-FFFF>
 10EAA         ; Cn #       <reserved-10EAA>
 10EAE..10EAF  ; Cn #   [2] <reserved-10EAE>..<reserved-10EAF>
 10EB2..10EC1  ; Cn #  [16] <reserved-10EB2>..<reserved-10EC1>
-10EC5..10EFB  ; Cn #  [55] <reserved-10EC5>..<reserved-10EFB>
+10EC8         ; Cn #       <reserved-10EC8>
+10EEF         ; Cn #       <reserved-10EEF>
 10F28..10F2F  ; Cn #   [8] <reserved-10F28>..<reserved-10F2F>
 10F5A..10F6F  ; Cn #  [22] <reserved-10F5A>..<reserved-10F6F>
 10F8A..10FAF  ; Cn #  [38] <reserved-10F8A>..<reserved-10FAF>
@@ -522,7 +514,8 @@ FFFE..FFFF    ; Cn #   [2] <noncharacter-FFFE>..<noncharacter-FFFF>
 11A48..11A4F  ; Cn #   [8] <reserved-11A48>..<reserved-11A4F>
 11AA3..11AAF  ; Cn #  [13] <reserved-11AA3>..<reserved-11AAF>
 11AF9..11AFF  ; Cn #   [7] <reserved-11AF9>..<reserved-11AFF>
-11B0A..11BBF  ; Cn # [182] <reserved-11B0A>..<reserved-11BBF>
+11B0B..11B5F  ; Cn #  [85] <reserved-11B0B>..<reserved-11B5F>
+11B68..11BBF  ; Cn #  [88] <reserved-11B68>..<reserved-11BBF>
 11BE2..11BEF  ; Cn #  [14] <reserved-11BE2>..<reserved-11BEF>
 11BFA..11BFF  ; Cn #   [6] <reserved-11BFA>..<reserved-11BFF>
 11C09         ; Cn #       <reserved-11C09>
@@ -544,7 +537,10 @@ FFFE..FFFF    ; Cn #   [2] <noncharacter-FFFE>..<noncharacter-FFFF>
 11D8F         ; Cn #       <reserved-11D8F>
 11D92         ; Cn #       <reserved-11D92>
 11D99..11D9F  ; Cn #   [7] <reserved-11D99>..<reserved-11D9F>
-11DAA..11EDF  ; Cn # [310] <reserved-11DAA>..<reserved-11EDF>
+11DAA..11DAF  ; Cn #   [6] <reserved-11DAA>..<reserved-11DAF>
+11DDC..11DDF  ; Cn #   [4] <reserved-11DDC>..<reserved-11DDF>
+11DEA..11DEF  ; Cn #   [6] <reserved-11DEA>..<reserved-11DEF>
+11DF2..11EDF  ; Cn # [238] <reserved-11DF2>..<reserved-11EDF>
 11EF9..11EFF  ; Cn #   [7] <reserved-11EF9>..<reserved-11EFF>
 11F11         ; Cn #       <reserved-11F11>
 11F3B..11F3D  ; Cn #   [3] <reserved-11F3B>..<reserved-11F3D>
@@ -552,9 +548,8 @@ FFFE..FFFF    ; Cn #   [2] <noncharacter-FFFE>..<noncharacter-FFFF>
 11FB1..11FBF  ; Cn #  [15] <reserved-11FB1>..<reserved-11FBF>
 11FF2..11FFE  ; Cn #  [13] <reserved-11FF2>..<reserved-11FFE>
 1239A..123FF  ; Cn # [102] <reserved-1239A>..<reserved-123FF>
-1246F         ; Cn #       <reserved-1246F>
-12475..1247F  ; Cn #  [11] <reserved-12475>..<reserved-1247F>
-12544..12F8F  ; Cn # [2636] <reserved-12544>..<reserved-12F8F>
+12544..1254F  ; Cn #  [12] <reserved-12544>..<reserved-1254F>
+12687..12F8F  ; Cn # [2313] <reserved-12687>..<reserved-12F8F>
 12FF3..12FFF  ; Cn #  [13] <reserved-12FF3>..<reserved-12FFF>
 13456..1345F  ; Cn #  [10] <reserved-13456>..<reserved-1345F>
 143FB..143FF  ; Cn #   [5] <reserved-143FB>..<reserved-143FF>
@@ -572,39 +567,47 @@ FFFE..FFFF    ; Cn #   [2] <noncharacter-FFFE>..<noncharacter-FFFF>
 16B62         ; Cn #       <reserved-16B62>
 16B78..16B7C  ; Cn #   [5] <reserved-16B78>..<reserved-16B7C>
 16B90..16D3F  ; Cn # [432] <reserved-16B90>..<reserved-16D3F>
-16D7A..16E3F  ; Cn # [198] <reserved-16D7A>..<reserved-16E3F>
-16E9B..16EFF  ; Cn # [101] <reserved-16E9B>..<reserved-16EFF>
+16D7A..16D7F  ; Cn #   [6] <reserved-16D7A>..<reserved-16D7F>
+16D9E..16D9F  ; Cn #   [2] <reserved-16D9E>..<reserved-16D9F>
+16DAA..16E3F  ; Cn # [150] <reserved-16DAA>..<reserved-16E3F>
+16E9B..16E9F  ; Cn #   [5] <reserved-16E9B>..<reserved-16E9F>
+16EB9..16EBA  ; Cn #   [2] <reserved-16EB9>..<reserved-16EBA>
+16ED4..16EFF  ; Cn #  [44] <reserved-16ED4>..<reserved-16EFF>
 16F4B..16F4E  ; Cn #   [4] <reserved-16F4B>..<reserved-16F4E>
 16F88..16F8E  ; Cn #   [7] <reserved-16F88>..<reserved-16F8E>
 16FA0..16FDF  ; Cn #  [64] <reserved-16FA0>..<reserved-16FDF>
 16FE5..16FEF  ; Cn #  [11] <reserved-16FE5>..<reserved-16FEF>
-16FF2..16FFF  ; Cn #  [14] <reserved-16FF2>..<reserved-16FFF>
-187F8..187FF  ; Cn #   [8] <reserved-187F8>..<reserved-187FF>
-18CD6..18CFE  ; Cn #  [41] <reserved-18CD6>..<reserved-18CFE>
-18D09..1AFEF  ; Cn # [8935] <reserved-18D09>..<reserved-1AFEF>
+16FF7..16FFF  ; Cn #   [9] <reserved-16FF7>..<reserved-16FFF>
+18CDB..18CFE  ; Cn #  [36] <reserved-18CDB>..<reserved-18CFE>
+18D21..18D7F  ; Cn #  [95] <reserved-18D21>..<reserved-18D7F>
+18DF3..18DFF  ; Cn #  [13] <reserved-18DF3>..<reserved-18DFF>
+19192..1919F  ; Cn #  [14] <reserved-19192>..<reserved-1919F>
+191D3..1AFEF  ; Cn # [7709] <reserved-191D3>..<reserved-1AFEF>
 1AFF4         ; Cn #       <reserved-1AFF4>
 1AFFC         ; Cn #       <reserved-1AFFC>
 1AFFF         ; Cn #       <reserved-1AFFF>
-1B123..1B131  ; Cn #  [15] <reserved-1B123>..<reserved-1B131>
+1B129..1B131  ; Cn #   [9] <reserved-1B129>..<reserved-1B131>
 1B133..1B14F  ; Cn #  [29] <reserved-1B133>..<reserved-1B14F>
 1B153..1B154  ; Cn #   [2] <reserved-1B153>..<reserved-1B154>
 1B156..1B163  ; Cn #  [14] <reserved-1B156>..<reserved-1B163>
-1B168..1B16F  ; Cn #   [8] <reserved-1B168>..<reserved-1B16F>
+1B169..1B16F  ; Cn #   [7] <reserved-1B169>..<reserved-1B16F>
 1B2FC..1BBFF  ; Cn # [2308] <reserved-1B2FC>..<reserved-1BBFF>
 1BC6B..1BC6F  ; Cn #   [5] <reserved-1BC6B>..<reserved-1BC6F>
 1BC7D..1BC7F  ; Cn #   [3] <reserved-1BC7D>..<reserved-1BC7F>
 1BC89..1BC8F  ; Cn #   [7] <reserved-1BC89>..<reserved-1BC8F>
 1BC9A..1BC9B  ; Cn #   [2] <reserved-1BC9A>..<reserved-1BC9B>
 1BCA4..1CBFF  ; Cn # [3932] <reserved-1BCA4>..<reserved-1CBFF>
-1CCFA..1CCFF  ; Cn #   [6] <reserved-1CCFA>..<reserved-1CCFF>
-1CEB4..1CEFF  ; Cn #  [76] <reserved-1CEB4>..<reserved-1CEFF>
+1CCFD..1CCFF  ; Cn #   [3] <reserved-1CCFD>..<reserved-1CCFF>
+1CEB4..1CEB9  ; Cn #   [6] <reserved-1CEB4>..<reserved-1CEB9>
+1CED1         ; Cn #       <reserved-1CED1>
+1CED5..1CEDC  ; Cn #   [8] <reserved-1CED5>..<reserved-1CEDC>
+1CEFE..1CEFF  ; Cn #   [2] <reserved-1CEFE>..<reserved-1CEFF>
 1CF2E..1CF2F  ; Cn #   [2] <reserved-1CF2E>..<reserved-1CF2F>
 1CF47..1CF4F  ; Cn #   [9] <reserved-1CF47>..<reserved-1CF4F>
 1CFC4..1CFFF  ; Cn #  [60] <reserved-1CFC4>..<reserved-1CFFF>
 1D0F6..1D0FF  ; Cn #  [10] <reserved-1D0F6>..<reserved-1D0FF>
-1D127..1D128  ; Cn #   [2] <reserved-1D127>..<reserved-1D128>
-1D1EB..1D1FF  ; Cn #  [21] <reserved-1D1EB>..<reserved-1D1FF>
-1D246..1D2BF  ; Cn # [122] <reserved-1D246>..<reserved-1D2BF>
+1D246..1D24F  ; Cn #  [10] <reserved-1D246>..<reserved-1D24F>
+1D282..1D2BF  ; Cn #  [62] <reserved-1D282>..<reserved-1D2BF>
 1D2D4..1D2DF  ; Cn #  [12] <reserved-1D2D4>..<reserved-1D2DF>
 1D2F4..1D2FF  ; Cn #  [12] <reserved-1D2F4>..<reserved-1D2FF>
 1D357..1D35F  ; Cn #   [9] <reserved-1D357>..<reserved-1D35F>
@@ -627,13 +630,14 @@ FFFE..FFFF    ; Cn #   [2] <noncharacter-FFFE>..<noncharacter-FFFF>
 1D545         ; Cn #       <reserved-1D545>
 1D547..1D549  ; Cn #   [3] <reserved-1D547>..<reserved-1D549>
 1D551         ; Cn #       <reserved-1D551>
-1D6A6..1D6A7  ; Cn #   [2] <reserved-1D6A6>..<reserved-1D6A7>
+1D6A7         ; Cn #       <reserved-1D6A7>
 1D7CC..1D7CD  ; Cn #   [2] <reserved-1D7CC>..<reserved-1D7CD>
 1DA8C..1DA9A  ; Cn #  [15] <reserved-1DA8C>..<reserved-1DA9A>
 1DAA0         ; Cn #       <reserved-1DAA0>
-1DAB0..1DEFF  ; Cn # [1104] <reserved-1DAB0>..<reserved-1DEFF>
-1DF1F..1DF24  ; Cn #   [6] <reserved-1DF1F>..<reserved-1DF24>
-1DF2B..1DFFF  ; Cn # [213] <reserved-1DF2B>..<reserved-1DFFF>
+1DAB0..1DAFF  ; Cn #  [80] <reserved-1DAB0>..<reserved-1DAFF>
+1DB1D..1DEFF  ; Cn # [995] <reserved-1DB1D>..<reserved-1DEFF>
+1DF82..1DF8F  ; Cn #  [14] <reserved-1DF82>..<reserved-1DF8F>
+1DF97..1DFCC  ; Cn #  [54] <reserved-1DF97>..<reserved-1DFCC>
 1E007         ; Cn #       <reserved-1E007>
 1E019..1E01A  ; Cn #   [2] <reserved-1E019>..<reserved-1E01A>
 1E022         ; Cn #       <reserved-1E022>
@@ -650,7 +654,10 @@ FFFE..FFFF    ; Cn #   [2] <noncharacter-FFFE>..<noncharacter-FFFF>
 1E300..1E4CF  ; Cn # [464] <reserved-1E300>..<reserved-1E4CF>
 1E4FA..1E5CF  ; Cn # [214] <reserved-1E4FA>..<reserved-1E5CF>
 1E5FB..1E5FE  ; Cn #   [4] <reserved-1E5FB>..<reserved-1E5FE>
-1E600..1E7DF  ; Cn # [480] <reserved-1E600>..<reserved-1E7DF>
+1E600..1E6BF  ; Cn # [192] <reserved-1E600>..<reserved-1E6BF>
+1E6DF         ; Cn #       <reserved-1E6DF>
+1E6F6..1E6FD  ; Cn #   [8] <reserved-1E6F6>..<reserved-1E6FD>
+1E700..1E7DF  ; Cn # [224] <reserved-1E700>..<reserved-1E7DF>
 1E7E7         ; Cn #       <reserved-1E7E7>
 1E7EC         ; Cn #       <reserved-1E7EC>
 1E7EF         ; Cn #       <reserved-1E7EF>
@@ -702,52 +709,51 @@ FFFE..FFFF    ; Cn #   [2] <noncharacter-FFFE>..<noncharacter-FFFF>
 1F0C0         ; Cn #       <reserved-1F0C0>
 1F0D0         ; Cn #       <reserved-1F0D0>
 1F0F6..1F0FF  ; Cn #  [10] <reserved-1F0F6>..<reserved-1F0FF>
-1F1AE..1F1E5  ; Cn #  [56] <reserved-1F1AE>..<reserved-1F1E5>
+1F1AF..1F1E5  ; Cn #  [55] <reserved-1F1AF>..<reserved-1F1E5>
 1F203..1F20F  ; Cn #  [13] <reserved-1F203>..<reserved-1F20F>
 1F23C..1F23F  ; Cn #   [4] <reserved-1F23C>..<reserved-1F23F>
 1F249..1F24F  ; Cn #   [7] <reserved-1F249>..<reserved-1F24F>
 1F252..1F25F  ; Cn #  [14] <reserved-1F252>..<reserved-1F25F>
 1F266..1F2FF  ; Cn # [154] <reserved-1F266>..<reserved-1F2FF>
-1F6D8..1F6DB  ; Cn #   [4] <reserved-1F6D8>..<reserved-1F6DB>
+1F6DA..1F6DB  ; Cn #   [2] <reserved-1F6DA>..<reserved-1F6DB>
 1F6ED..1F6EF  ; Cn #   [3] <reserved-1F6ED>..<reserved-1F6EF>
 1F6FD..1F6FF  ; Cn #   [3] <reserved-1F6FD>..<reserved-1F6FF>
-1F777..1F77A  ; Cn #   [4] <reserved-1F777>..<reserved-1F77A>
-1F7DA..1F7DF  ; Cn #   [6] <reserved-1F7DA>..<reserved-1F7DF>
+1F7DC..1F7DF  ; Cn #   [4] <reserved-1F7DC>..<reserved-1F7DF>
 1F7EC..1F7EF  ; Cn #   [4] <reserved-1F7EC>..<reserved-1F7EF>
-1F7F1..1F7FF  ; Cn #  [15] <reserved-1F7F1>..<reserved-1F7FF>
 1F80C..1F80F  ; Cn #   [4] <reserved-1F80C>..<reserved-1F80F>
 1F848..1F84F  ; Cn #   [8] <reserved-1F848>..<reserved-1F84F>
 1F85A..1F85F  ; Cn #   [6] <reserved-1F85A>..<reserved-1F85F>
 1F888..1F88F  ; Cn #   [8] <reserved-1F888>..<reserved-1F88F>
 1F8AE..1F8AF  ; Cn #   [2] <reserved-1F8AE>..<reserved-1F8AF>
 1F8BC..1F8BF  ; Cn #   [4] <reserved-1F8BC>..<reserved-1F8BF>
-1F8C2..1F8FF  ; Cn #  [62] <reserved-1F8C2>..<reserved-1F8FF>
-1FA54..1FA5F  ; Cn #  [12] <reserved-1FA54>..<reserved-1FA5F>
+1F8C2..1F8CF  ; Cn #  [14] <reserved-1F8C2>..<reserved-1F8CF>
+1F8D9..1F8FF  ; Cn #  [39] <reserved-1F8D9>..<reserved-1F8FF>
+1FA58..1FA5F  ; Cn #   [8] <reserved-1FA58>..<reserved-1FA5F>
 1FA6E..1FA6F  ; Cn #   [2] <reserved-1FA6E>..<reserved-1FA6F>
 1FA7D..1FA7F  ; Cn #   [3] <reserved-1FA7D>..<reserved-1FA7F>
-1FA8A..1FA8E  ; Cn #   [5] <reserved-1FA8A>..<reserved-1FA8E>
-1FAC7..1FACD  ; Cn #   [7] <reserved-1FAC7>..<reserved-1FACD>
-1FADD..1FADE  ; Cn #   [2] <reserved-1FADD>..<reserved-1FADE>
-1FAEA..1FAEF  ; Cn #   [6] <reserved-1FAEA>..<reserved-1FAEF>
-1FAF9..1FAFF  ; Cn #   [7] <reserved-1FAF9>..<reserved-1FAFF>
+1FAC7         ; Cn #       <reserved-1FAC7>
+1FAC9..1FACB  ; Cn #   [3] <reserved-1FAC9>..<reserved-1FACB>
+1FADE         ; Cn #       <reserved-1FADE>
+1FAEC..1FAEE  ; Cn #   [3] <reserved-1FAEC>..<reserved-1FAEE>
+1FAFB..1FAFF  ; Cn #   [5] <reserved-1FAFB>..<reserved-1FAFF>
 1FB93         ; Cn #       <reserved-1FB93>
-1FBFA..1FFFF  ; Cn # [1030] <reserved-1FBFA>..<noncharacter-1FFFF>
+1FBFB..1FFFF  ; Cn # [1029] <reserved-1FBFB>..<noncharacter-1FFFF>
 2A6E0..2A6FF  ; Cn #  [32] <reserved-2A6E0>..<reserved-2A6FF>
-2B73A..2B73F  ; Cn #   [6] <reserved-2B73A>..<reserved-2B73F>
-2B81E..2B81F  ; Cn #   [2] <reserved-2B81E>..<reserved-2B81F>
-2CEA2..2CEAF  ; Cn #  [14] <reserved-2CEA2>..<reserved-2CEAF>
+2B81F         ; Cn #       <reserved-2B81F>
+2CEAE..2CEAF  ; Cn #   [2] <reserved-2CEAE>..<reserved-2CEAF>
 2EBE1..2EBEF  ; Cn #  [15] <reserved-2EBE1>..<reserved-2EBEF>
 2EE5E..2F7FF  ; Cn # [2466] <reserved-2EE5E>..<reserved-2F7FF>
 2FA1E..2FFFF  ; Cn # [1506] <reserved-2FA1E>..<noncharacter-2FFFF>
 3134B..3134F  ; Cn #   [5] <reserved-3134B>..<reserved-3134F>
-323B0..E0000  ; Cn # [711761] <reserved-323B0>..<reserved-E0000>
+3347A..3CFFF  ; Cn # [39814] <reserved-3347A>..<reserved-3CFFF>
+3FC40..E0000  ; Cn # [656321] <reserved-3FC40>..<reserved-E0000>
 E0002..E001F  ; Cn #  [30] <reserved-E0002>..<reserved-E001F>
 E0080..E00FF  ; Cn # [128] <reserved-E0080>..<reserved-E00FF>
 E01F0..EFFFF  ; Cn # [65040] <reserved-E01F0>..<noncharacter-EFFFF>
 FFFFE..FFFFF  ; Cn #   [2] <noncharacter-FFFFE>..<noncharacter-FFFFF>
 10FFFE..10FFFF; Cn #   [2] <noncharacter-10FFFE>..<noncharacter-10FFFF>
 
-# Total code points: 819533
+# Total code points: 801683
 
 # ================================================
 
@@ -1355,12 +1361,17 @@ A7C2          ; Lu #       LATIN CAPITAL LETTER ANGLICANA W
 A7C4..A7C7    ; Lu #   [4] LATIN CAPITAL LETTER C WITH PALATAL HOOK..LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY
 A7C9          ; Lu #       LATIN CAPITAL LETTER S WITH SHORT STROKE OVERLAY
 A7CB..A7CC    ; Lu #   [2] LATIN CAPITAL LETTER RAMS HORN..LATIN CAPITAL LETTER S WITH DIAGONAL STROKE
+A7CE          ; Lu #       LATIN CAPITAL LETTER PHARYNGEAL VOICED FRICATIVE
 A7D0          ; Lu #       LATIN CAPITAL LETTER CLOSED INSULAR G
+A7D2          ; Lu #       LATIN CAPITAL LETTER DOUBLE THORN
+A7D4          ; Lu #       LATIN CAPITAL LETTER DOUBLE WYNN
 A7D6          ; Lu #       LATIN CAPITAL LETTER MIDDLE SCOTS S
 A7D8          ; Lu #       LATIN CAPITAL LETTER SIGMOID S
 A7DA          ; Lu #       LATIN CAPITAL LETTER LAMBDA
-A7DC          ; Lu #       LATIN CAPITAL LETTER LAMBDA WITH STROKE
+A7DC..A7DD    ; Lu #   [2] LATIN CAPITAL LETTER LAMBDA WITH STROKE..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2          ; Lu #       LATIN CAPITAL LETTER R WITH LONG LEG
 A7F5          ; Lu #       LATIN CAPITAL LETTER REVERSED HALF H
+AB6C..AB6D    ; Lu #   [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 FF21..FF3A    ; Lu #  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z
 10400..10427  ; Lu #  [40] DESERET CAPITAL LETTER LONG I..DESERET CAPITAL LETTER EW
 104B0..104D3  ; Lu #  [36] OSAGE CAPITAL LETTER A..OSAGE CAPITAL LETTER ZHA
@@ -1372,6 +1383,7 @@ FF21..FF3A    ; Lu #  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAP
 10D50..10D65  ; Lu #  [22] GARAY CAPITAL LETTER A..GARAY CAPITAL LETTER OLD NA
 118A0..118BF  ; Lu #  [32] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI CAPITAL LETTER VIYO
 16E40..16E5F  ; Lu #  [32] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN CAPITAL LETTER Y
+16EA0..16EB8  ; Lu #  [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
 1D400..1D419  ; Lu #  [26] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL BOLD CAPITAL Z
 1D434..1D44D  ; Lu #  [26] MATHEMATICAL ITALIC CAPITAL A..MATHEMATICAL ITALIC CAPITAL Z
 1D468..1D481  ; Lu #  [26] MATHEMATICAL BOLD ITALIC CAPITAL A..MATHEMATICAL BOLD ITALIC CAPITAL Z
@@ -1403,9 +1415,25 @@ FF21..FF3A    ; Lu #  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAP
 1D756..1D76E  ; Lu #  [25] MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA
 1D790..1D7A8  ; Lu #  [25] MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA
 1D7CA         ; Lu #       MATHEMATICAL BOLD CAPITAL DIGAMMA
+1DF40         ; Lu #       LATIN CAPITAL LETTER BARRED A
+1DF48         ; Lu #       LATIN CAPITAL LETTER BARRED K
+1DF4A         ; Lu #       LATIN CAPITAL LETTER BARRED M
+1DF4D         ; Lu #       LATIN CAPITAL LETTER BARRED N
+1DF51         ; Lu #       LATIN CAPITAL LETTER BARRED V
+1DF68         ; Lu #       LATIN CAPITAL LETTER PHONOTYPIC A WITH SWASH
+1DF6A         ; Lu #       LATIN CAPITAL LETTER PHONOTYPIC ROUNDTOP A
+1DF6C         ; Lu #       LATIN CAPITAL LETTER REVERSED SCRUPLE
+1DF6E         ; Lu #       LATIN CAPITAL LETTER PHONOTYPIC DIPHTHONG AI
+1DF72         ; Lu #       LATIN CAPITAL LETTER O WITH CURL
+1DF74         ; Lu #       LATIN CAPITAL LETTER CLOSED OMEGA WITH LONG STEM
+1DF76         ; Lu #       LATIN CAPITAL LETTER TURNED CLOSED OMEGA
+1DF78         ; Lu #       LATIN CAPITAL LETTER PHONOTYPIC TH
+1DF7A         ; Lu #       LATIN CAPITAL LETTER U WITH HOOK TAIL
+1DF7C         ; Lu #       LATIN CAPITAL LETTER U WITH NOTCH AT BOTTOM
+1DF7E         ; Lu #       LATIN CAPITAL LETTER REVERSED U
 1E900..1E921  ; Lu #  [34] ADLAM CAPITAL LETTER ALIF..ADLAM CAPITAL LETTER SHA
 
-# Total code points: 1858
+# Total code points: 1906
 
 # ================================================
 
@@ -1556,7 +1584,7 @@ FF21..FF3A    ; Lu #  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAP
 024B          ; Ll #       LATIN SMALL LETTER Q WITH HOOK TAIL
 024D          ; Ll #       LATIN SMALL LETTER R WITH STROKE
 024F..0293    ; Ll #  [69] LATIN SMALL LETTER Y WITH STROKE..LATIN SMALL LETTER EZH WITH CURL
-0295..02AF    ; Ll #  [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+0296..02AF    ; Ll #  [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
 0371          ; Ll #       GREEK SMALL LETTER HETA
 0373          ; Ll #       GREEK SMALL LETTER ARCHAIC SAMPI
 0377          ; Ll #       GREEK SMALL LETTER PAMPHYLIAN DIGAMMA
@@ -2017,6 +2045,7 @@ A7C3          ; Ll #       LATIN SMALL LETTER ANGLICANA W
 A7C8          ; Ll #       LATIN SMALL LETTER D WITH SHORT STROKE OVERLAY
 A7CA          ; Ll #       LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY
 A7CD          ; Ll #       LATIN SMALL LETTER S WITH DIAGONAL STROKE
+A7CF          ; Ll #       LATIN SMALL LETTER PHARYNGEAL VOICED FRICATIVE
 A7D1          ; Ll #       LATIN SMALL LETTER CLOSED INSULAR G
 A7D3          ; Ll #       LATIN SMALL LETTER DOUBLE THORN
 A7D5          ; Ll #       LATIN SMALL LETTER DOUBLE WYNN
@@ -2041,6 +2070,7 @@ FF41..FF5A    ; Ll #  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL
 10D70..10D85  ; Ll #  [22] GARAY SMALL LETTER A..GARAY SMALL LETTER OLD NA
 118C0..118DF  ; Ll #  [32] WARANG CITI SMALL LETTER NGAA..WARANG CITI SMALL LETTER VIYO
 16E60..16E7F  ; Ll #  [32] MEDEFAIDRIN SMALL LETTER M..MEDEFAIDRIN SMALL LETTER Y
+16EBB..16ED3  ; Ll #  [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
 1D41A..1D433  ; Ll #  [26] MATHEMATICAL BOLD SMALL A..MATHEMATICAL BOLD SMALL Z
 1D44E..1D454  ; Ll #   [7] MATHEMATICAL ITALIC SMALL A..MATHEMATICAL ITALIC SMALL G
 1D456..1D467  ; Ll #  [18] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL ITALIC SMALL Z
@@ -2057,7 +2087,7 @@ FF41..FF5A    ; Ll #  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL
 1D5EE..1D607  ; Ll #  [26] MATHEMATICAL SANS-SERIF BOLD SMALL A..MATHEMATICAL SANS-SERIF BOLD SMALL Z
 1D622..1D63B  ; Ll #  [26] MATHEMATICAL SANS-SERIF ITALIC SMALL A..MATHEMATICAL SANS-SERIF ITALIC SMALL Z
 1D656..1D66F  ; Ll #  [26] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z
-1D68A..1D6A5  ; Ll #  [28] MATHEMATICAL MONOSPACE SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D68A..1D6A6  ; Ll #  [29] MATHEMATICAL MONOSPACE SMALL A..MATHEMATICAL ITALIC SMALL LIGATURE LONG S WITH DESCENDER S
 1D6C2..1D6DA  ; Ll #  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
 1D6DC..1D6E1  ; Ll #   [6] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL BOLD PI SYMBOL
 1D6FC..1D714  ; Ll #  [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA
@@ -2070,11 +2100,27 @@ FF41..FF5A    ; Ll #  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL
 1D7C4..1D7C9  ; Ll #   [6] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL
 1D7CB         ; Ll #       MATHEMATICAL BOLD SMALL DIGAMMA
 1DF00..1DF09  ; Ll #  [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
-1DF0B..1DF1E  ; Ll #  [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL
-1DF25..1DF2A  ; Ll #   [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK
+1DF0B..1DF3F  ; Ll #  [53] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER BARRED W
+1DF41..1DF47  ; Ll #   [7] LATIN SMALL LETTER BARRED A..LATIN SMALL LETTER BARRED H WITH HOOK
+1DF49         ; Ll #       LATIN SMALL LETTER BARRED K
+1DF4B..1DF4C  ; Ll #   [2] LATIN SMALL LETTER BARRED M..LATIN SMALL LETTER BARRED M WITH HOOK
+1DF4E..1DF50  ; Ll #   [3] LATIN SMALL LETTER BARRED N..LATIN SMALL LETTER TURNED R WITH STROKE
+1DF52..1DF67  ; Ll #  [22] LATIN SMALL LETTER BARRED V..LATIN SMALL LETTER LEZH WITH CURL
+1DF69         ; Ll #       LATIN SMALL LETTER PHONOTYPIC A WITH SWASH
+1DF6B         ; Ll #       LATIN SMALL LETTER PHONOTYPIC ROUNDTOP A
+1DF6D         ; Ll #       LATIN SMALL LETTER REVERSED SCRUPLE
+1DF6F..1DF71  ; Ll #   [3] LATIN SMALL LETTER PHONOTYPIC DIPHTHONG AI..LATIN SMALL LETTER STRETCHED I
+1DF73         ; Ll #       LATIN SMALL LETTER O WITH CURL
+1DF75         ; Ll #       LATIN SMALL LETTER CLOSED OMEGA WITH LONG STEM
+1DF77         ; Ll #       LATIN SMALL LETTER TURNED CLOSED OMEGA
+1DF79         ; Ll #       LATIN SMALL LETTER PHONOTYPIC TH
+1DF7B         ; Ll #       LATIN SMALL LETTER U WITH HOOK TAIL
+1DF7D         ; Ll #       LATIN SMALL LETTER U WITH NOTCH AT BOTTOM
+1DF7F         ; Ll #       LATIN SMALL LETTER REVERSED U
+1DF90..1DF96  ; Ll #   [7] LATIN SMALL LETTER C WITH LOW SLASH..LATIN SMALL LETTER LONG S WITH TOP LOOP
 1E922..1E943  ; Ll #  [34] ADLAM SMALL LETTER ALIF..ADLAM SMALL LETTER SHA
 
-# Total code points: 2258
+# Total code points: 2366
 
 # ================================================
 
@@ -2104,7 +2150,8 @@ FF41..FF5A    ; Ll #  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL
 02EE          ; Lm #       MODIFIER LETTER DOUBLE APOSTROPHE
 0374          ; Lm #       GREEK NUMERAL SIGN
 037A          ; Lm #       GREEK YPOGEGRAMMENI
-0559          ; Lm #       ARMENIAN MODIFIER LETTER LEFT HALF RING
+0558..0559    ; Lm #   [2] MODIFIER LETTER ARMENIAN SMALL EH..ARMENIAN MODIFIER LETTER LEFT HALF RING
+058B..058C    ; Lm #   [2] MODIFIER LETTER ARMENIAN SMALL INI..MODIFIER LETTER ARMENIAN SMALL YI
 0640          ; Lm #       ARABIC TATWEEL
 06E5..06E6    ; Lm #   [2] ARABIC SMALL WAW..ARABIC SMALL YEH
 07F4..07F5    ; Lm #   [2] NKO HIGH TONE APOSTROPHE..NKO LOW TONE APOSTROPHE
@@ -2126,7 +2173,7 @@ FF41..FF5A    ; Ll #  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL
 1D9B..1DBF    ; Lm #  [37] MODIFIER LETTER SMALL TURNED ALPHA..MODIFIER LETTER SMALL THETA
 2071          ; Lm #       SUPERSCRIPT LATIN SMALL LETTER I
 207F          ; Lm #       SUPERSCRIPT LATIN SMALL LETTER N
-2090..209C    ; Lm #  [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T
+208F..209F    ; Lm #  [17] MODIFIER LETTER HIGH AND LOW VERTICAL LINE..LATIN SUBSCRIPT SMALL LETTER Z
 2C7C..2C7D    ; Lm #   [2] LATIN SUBSCRIPT SMALL LETTER J..MODIFIER LETTER CAPITAL V
 2D6F          ; Lm #       TIFINAGH MODIFIER LETTER LABIALIZATION MARK
 2E2F          ; Lm #       VERTICAL TILDE
@@ -2143,7 +2190,7 @@ A69C..A69D    ; Lm #   [2] MODIFIER LETTER CYRILLIC HARD SIGN..MODIFIER LETTER C
 A717..A71F    ; Lm #   [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK
 A770          ; Lm #       MODIFIER LETTER US
 A788          ; Lm #       MODIFIER LETTER LOW CIRCUMFLEX ACCENT
-A7F2..A7F4    ; Lm #   [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
+A7F1..A7F4    ; Lm #   [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q
 A7F8..A7F9    ; Lm #   [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
 A9CF          ; Lm #       JAVANESE PANGRANGKEP
 A9E6          ; Lm #       MYANMAR MODIFIER LETTER SHAN REDUPLICATION
@@ -2156,24 +2203,29 @@ FF70          ; Lm #       HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK
 FF9E..FF9F    ; Lm #   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
 10780..10785  ; Lm #   [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK
 10787..107B0  ; Lm #  [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
-107B2..107BA  ; Lm #   [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
+107B2..107BF  ; Lm #  [14] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL ESH WITH DOUBLE BAR
 10D4E         ; Lm #       GARAY VOWEL LENGTH MARK
 10D6F         ; Lm #       GARAY REDUPLICATION MARK
+10EC5         ; Lm #       ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW
+11DD9         ; Lm #       TOLONG SIKI SIGN SELA
 16B40..16B43  ; Lm #   [4] PAHAWH HMONG SIGN VOS SEEV..PAHAWH HMONG SIGN IB YAM
 16D40..16D42  ; Lm #   [3] KIRAT RAI SIGN ANUSVARA..KIRAT RAI SIGN VISARGA
 16D6B..16D6C  ; Lm #   [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT
 16F93..16F9F  ; Lm #  [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8
 16FE0..16FE1  ; Lm #   [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK
 16FE3         ; Lm #       OLD CHINESE ITERATION MARK
+16FF2..16FF3  ; Lm #   [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER
 1AFF0..1AFF3  ; Lm #   [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5
 1AFF5..1AFFB  ; Lm #   [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5
 1AFFD..1AFFE  ; Lm #   [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8
+1DFCD..1DFFF  ; Lm #  [51] MODIFIER LETTER SMALL TURNED R WITH MID-HEIGHT LEFT HOOK..MODIFIER LETTER SMALL T WITH HOOK AND RETROFLEX HOOK
 1E030..1E06D  ; Lm #  [62] MODIFIER LETTER CYRILLIC SMALL A..MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE
 1E137..1E13D  ; Lm #   [7] NYIAKENG PUACHUE HMONG SIGN FOR PERSON..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER
 1E4EB         ; Lm #       NAG MUNDARI SIGN OJOD
+1E6FF         ; Lm #       TAI YO XAM LAI
 1E94B         ; Lm #       ADLAM NASALIZATION MARK
 
-# Total code points: 404
+# Total code points: 473
 
 # ================================================
 
@@ -2183,7 +2235,7 @@ FF9E..FF9F    ; Lm #   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAK
 00BA          ; Lo #       MASCULINE ORDINAL INDICATOR
 01BB          ; Lo #       LATIN LETTER TWO WITH STROKE
 01C0..01C3    ; Lo #   [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK
-0294          ; Lo #       LATIN LETTER GLOTTAL STOP
+0294..0295    ; Lo #   [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE
 05D0..05EA    ; Lo #  [27] HEBREW LETTER ALEF..HEBREW LETTER TAV
 05EF..05F2    ; Lo #   [4] HEBREW YOD TRIANGLE..HEBREW LIGATURE YIDDISH DOUBLE YOD
 0620..063F    ; Lo #  [32] ARABIC LETTER KASHMIRI YEH..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE
@@ -2203,7 +2255,7 @@ FF9E..FF9F    ; Lm #   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAK
 0840..0858    ; Lo #  [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN
 0860..086A    ; Lo #  [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA
 0870..0887    ; Lo #  [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT
-0889..088E    ; Lo #   [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL
+0889..088F    ; Lo #   [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE
 08A0..08C8    ; Lo #  [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF
 0904..0939    ; Lo #  [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA
 093D          ; Lo #       DEVANAGARI SIGN AVAGRAHA
@@ -2269,7 +2321,7 @@ FF9E..FF9F    ; Lm #   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAK
 0C2A..0C39    ; Lo #  [16] TELUGU LETTER PA..TELUGU LETTER HA
 0C3D          ; Lo #       TELUGU SIGN AVAGRAHA
 0C58..0C5A    ; Lo #   [3] TELUGU LETTER TSA..TELUGU LETTER RRRA
-0C5D          ; Lo #       TELUGU LETTER NAKAARA POLLU
+0C5C..0C5D    ; Lo #   [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU
 0C60..0C61    ; Lo #   [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
 0C80          ; Lo #       KANNADA SIGN SPACING CANDRABINDU
 0C85..0C8C    ; Lo #   [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L
@@ -2278,7 +2330,7 @@ FF9E..FF9F    ; Lm #   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAK
 0CAA..0CB3    ; Lo #  [10] KANNADA LETTER PA..KANNADA LETTER LLA
 0CB5..0CB9    ; Lo #   [5] KANNADA LETTER VA..KANNADA LETTER HA
 0CBD          ; Lo #       KANNADA SIGN AVAGRAHA
-0CDD..0CDE    ; Lo #   [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA
+0CDC..0CDE    ; Lo #   [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA
 0CE0..0CE1    ; Lo #   [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
 0CF1..0CF2    ; Lo #   [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA
 0D04..0D0C    ; Lo #   [9] MALAYALAM LETTER VEDIC ANUSVARA..MALAYALAM LETTER VOCALIC L
@@ -2504,6 +2556,7 @@ FFDA..FFDC    ; Lo #   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
 108F4..108F5  ; Lo #   [2] HATRAN LETTER SHIN..HATRAN LETTER TAW
 10900..10915  ; Lo #  [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU
 10920..10939  ; Lo #  [26] LYDIAN LETTER A..LYDIAN LETTER C
+10940..10959  ; Lo #  [26] SIDETIC LETTER N01..SIDETIC LETTER N26
 10980..109B7  ; Lo #  [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA
 109BE..109BF  ; Lo #   [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN
 10A00         ; Lo #       KHAROSHTHI LETTER A
@@ -2525,6 +2578,8 @@ FFDA..FFDC    ; Lo #   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
 10E80..10EA9  ; Lo #  [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET
 10EB0..10EB1  ; Lo #   [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
 10EC2..10EC4  ; Lo #   [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW
+10EC6..10EC7  ; Lo #   [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW
+10ED9..10EEE  ; Lo #  [22] ARABIC CROWN LETTER BEH..ARABIC CROWN LETTER YEH
 10F00..10F1C  ; Lo #  [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL
 10F27         ; Lo #       OLD SOGDIAN LIGATURE AYIN-DALETH
 10F30..10F45  ; Lo #  [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN
@@ -2603,6 +2658,7 @@ FFDA..FFDC    ; Lo #   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
 11A5C..11A89  ; Lo #  [46] SOYOMBO LETTER KA..SOYOMBO CLUSTER-INITIAL LETTER SA
 11A9D         ; Lo #       SOYOMBO MARK PLUTA
 11AB0..11AF8  ; Lo #  [73] CANADIAN SYLLABICS NATTILIK HI..PAU CIN HAU GLOTTAL STOP FINAL
+11B0A         ; Lo #       DEVANAGARI LETTER ALTERNATE DDDA
 11BC0..11BE0  ; Lo #  [33] SUNUWAR LETTER DEVI..SUNUWAR LETTER KLOKO
 11C00..11C08  ; Lo #   [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L
 11C0A..11C2E  ; Lo #  [37] BHAIKSUKI LETTER E..BHAIKSUKI LETTER HA
@@ -2616,6 +2672,9 @@ FFDA..FFDC    ; Lo #   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
 11D67..11D68  ; Lo #   [2] GUNJALA GONDI LETTER EE..GUNJALA GONDI LETTER AI
 11D6A..11D89  ; Lo #  [32] GUNJALA GONDI LETTER OO..GUNJALA GONDI LETTER SA
 11D98         ; Lo #       GUNJALA GONDI OM
+11DB0..11DD8  ; Lo #  [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH
+11DDA..11DDB  ; Lo #   [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA
+11DF1         ; Lo #       BENGALI LETTER ALTERNATE BARGIYA BA
 11EE0..11EF2  ; Lo #  [19] MAKASAR LETTER KA..MAKASAR ANGKA
 11F02         ; Lo #       KAWI SIGN REPHA
 11F04..11F10  ; Lo #  [13] KAWI LETTER A..KAWI LETTER O
@@ -2637,22 +2696,27 @@ FFDA..FFDC    ; Lo #   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
 16B63..16B77  ; Lo #  [21] PAHAWH HMONG SIGN VOS LUB..PAHAWH HMONG SIGN CIM NRES TOS
 16B7D..16B8F  ; Lo #  [19] PAHAWH HMONG CLAN SIGN TSHEEJ..PAHAWH HMONG CLAN SIGN VWJ
 16D43..16D6A  ; Lo #  [40] KIRAT RAI LETTER A..KIRAT RAI VOWEL SIGN AU
+16D80..16D97  ; Lo #  [24] CHISOI LETTER A..CHISOI LETTER PA
+16D99..16D9C  ; Lo #   [4] CHISOI LETTER YA..CHISOI LETTER JARAHA
 16F00..16F4A  ; Lo #  [75] MIAO LETTER PA..MIAO LETTER RTE
 16F50         ; Lo #       MIAO LETTER NASALIZATION
-17000..187F7  ; Lo # [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
-18800..18CD5  ; Lo # [1238] TANGUT COMPONENT-001..KHITAN SMALL SCRIPT CHARACTER-18CD5
-18CFF..18D08  ; Lo #  [10] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D08
-1B000..1B122  ; Lo # [291] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ARCHAIC WU
+17000..18CDA  ; Lo # [7387] TANGUT IDEOGRAPH-17000..KHITAN SMALL SCRIPT CHARACTER-18CDA
+18CFF..18D20  ; Lo #  [34] KHITAN SMALL SCRIPT CHARACTER-18CFF..TANGUT IDEOGRAPH-18D20
+18D80..18DF2  ; Lo # [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883
+18E00..19191  ; Lo # [914] JURCHEN CHARACTER-18E00..JURCHEN CHARACTER-19191
+191A0..191D2  ; Lo #  [51] JURCHEN RADICAL-01..JURCHEN RADICAL-51
+1B000..1B128  ; Lo # [297] KATAKANA LETTER ARCHAIC E..KATAKANA LETTER ALTERNATE WI
 1B132         ; Lo #       HIRAGANA LETTER SMALL KO
 1B150..1B152  ; Lo #   [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO
 1B155         ; Lo #       KATAKANA LETTER SMALL KO
-1B164..1B167  ; Lo #   [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N
+1B164..1B168  ; Lo #   [5] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL ARCHAIC YE
 1B170..1B2FB  ; Lo # [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
 1BC00..1BC6A  ; Lo # [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M
 1BC70..1BC7C  ; Lo #  [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK
 1BC80..1BC88  ; Lo #   [9] DUPLOYAN AFFIX HIGH ACUTE..DUPLOYAN AFFIX HIGH VERTICAL
 1BC90..1BC99  ; Lo #  [10] DUPLOYAN AFFIX LOW ACUTE..DUPLOYAN AFFIX LOW ARROW
 1DF0A         ; Lo #       LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK
+1DF80..1DF81  ; Lo #   [2] LATIN CAPITAL LETTER A WITH TOPBAR..LATIN CAPITAL LETTER E WITH BENT TOPBAR
 1E100..1E12C  ; Lo #  [45] NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W
 1E14E         ; Lo #       NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ
 1E290..1E2AD  ; Lo #  [30] TOTO LETTER PA..TOTO LETTER A
@@ -2660,6 +2724,12 @@ FFDA..FFDC    ; Lo #   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
 1E4D0..1E4EA  ; Lo #  [27] NAG MUNDARI LETTER O..NAG MUNDARI LETTER ELL
 1E5D0..1E5ED  ; Lo #  [30] OL ONAL LETTER O..OL ONAL LETTER EG
 1E5F0         ; Lo #       OL ONAL SIGN HODDOND
+1E6C0..1E6DE  ; Lo #  [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO
+1E6E0..1E6E2  ; Lo #   [3] TAI YO LETTER AA..TAI YO LETTER UE
+1E6E4..1E6E5  ; Lo #   [2] TAI YO LETTER U..TAI YO LETTER AE
+1E6E7..1E6ED  ; Lo #   [7] TAI YO LETTER O..TAI YO LETTER AUE
+1E6F0..1E6F4  ; Lo #   [5] TAI YO LETTER AN..TAI YO LETTER AP
+1E6FE         ; Lo #       TAI YO SYMBOL MUEANG
 1E7E0..1E7E6  ; Lo #   [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO
 1E7E8..1E7EB  ; Lo #   [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE
 1E7ED..1E7EE  ; Lo #   [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE
@@ -2699,16 +2769,16 @@ FFDA..FFDC    ; Lo #   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
 1EEA5..1EEA9  ; Lo #   [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH
 1EEAB..1EEBB  ; Lo #  [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN
 20000..2A6DF  ; Lo # [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF
-2A700..2B739  ; Lo # [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739
-2B740..2B81D  ; Lo # [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D
-2B820..2CEA1  ; Lo # [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1
+2A700..2B81E  ; Lo # [4383] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81E
+2B820..2CEAD  ; Lo # [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD
 2CEB0..2EBE0  ; Lo # [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
 2EBF0..2EE5D  ; Lo # [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D
 2F800..2FA1D  ; Lo # [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
 30000..3134A  ; Lo # [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A
-31350..323AF  ; Lo # [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF
+31350..33479  ; Lo # [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479
+3D000..3FC3F  ; Lo # [11328] SEAL CHARACTER-3D000..SEAL CHARACTER-3FC3F
 
-# Total code points: 136477
+# Total code points: 153424
 
 # ================================================
 
@@ -2720,7 +2790,7 @@ FFDA..FFDC    ; Lo #   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
 05BF          ; Mn #       HEBREW POINT RAFE
 05C1..05C2    ; Mn #   [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
 05C4..05C5    ; Mn #   [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
-05C7          ; Mn #       HEBREW POINT QAMATS QATAN
+05C7..05C9    ; Mn #   [3] HEBREW POINT QAMATS QATAN..HEBREW POINT DAGESH HAZAQ MUDGASH
 0610..061A    ; Mn #  [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
 064B..065F    ; Mn #  [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW
 0670          ; Mn #       ARABIC LETTER SUPERSCRIPT ALEF
@@ -2773,7 +2843,7 @@ FFDA..FFDC    ; Lo #   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
 0B3F          ; Mn #       ORIYA VOWEL SIGN I
 0B41..0B44    ; Mn #   [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR
 0B4D          ; Mn #       ORIYA SIGN VIRAMA
-0B55..0B56    ; Mn #   [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK
+0B53..0B56    ; Mn #   [4] ORIYA SIGN DOT ABOVE..ORIYA AI LENGTH MARK
 0B62..0B63    ; Mn #   [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL
 0B82          ; Mn #       TAMIL SIGN ANUSVARA
 0BC0          ; Mn #       TAMIL VOWEL SIGN II
@@ -2856,7 +2926,7 @@ FFDA..FFDC    ; Lo #   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
 1A73..1A7C    ; Mn #  [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN
 1A7F          ; Mn #       TAI THAM COMBINING CRYPTOGRAMMIC DOT
 1AB0..1ABD    ; Mn #  [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
-1ABF..1ACE    ; Mn #  [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T
+1ABF..1AF0    ; Mn #  [50] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOUBLE COMMA ABOVE
 1B00..1B03    ; Mn #   [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
 1B34          ; Mn #       BALINESE SIGN REREKAN
 1B36..1B3A    ; Mn #   [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA
@@ -2938,7 +3008,8 @@ FE20..FE2F    ; Mn #  [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITL
 10D24..10D27  ; Mn #   [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI
 10D69..10D6D  ; Mn #   [5] GARAY VOWEL SIGN E..GARAY CONSONANT NASALIZATION MARK
 10EAB..10EAC  ; Mn #   [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
-10EFC..10EFF  ; Mn #   [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA
+10ECB..10ECF  ; Mn #   [5] ARABIC NORTHEAST POINTING ARROWHEAD ABOVE..ARABIC LARGE CIRCLE ABOVE
+10EF0..10EFF  ; Mn #  [16] ARABIC SMALL LOW UPRIGHT RECTANGULAR ZERO..ARABIC SMALL LOW WORD MADDA
 10F46..10F50  ; Mn #  [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW
 10F82..10F85  ; Mn #   [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW
 11001         ; Mn #       BRAHMI SIGN ANUSVARA
@@ -3013,6 +3084,9 @@ FE20..FE2F    ; Mn #  [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITL
 11A59..11A5B  ; Mn #   [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK
 11A8A..11A96  ; Mn #  [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA
 11A98..11A99  ; Mn #   [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER
+11B60         ; Mn #       SHARADA VOWEL SIGN OE
+11B62..11B64  ; Mn #   [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E
+11B66         ; Mn #       SHARADA VOWEL SIGN CANDRA E
 11C30..11C36  ; Mn #   [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L
 11C38..11C3D  ; Mn #   [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA
 11C3F         ; Mn #       BHAIKSUKI SIGN VIRAMA
@@ -3028,6 +3102,7 @@ FE20..FE2F    ; Mn #  [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITL
 11D90..11D91  ; Mn #   [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI
 11D95         ; Mn #       GUNJALA GONDI SIGN ANUSVARA
 11D97         ; Mn #       GUNJALA GONDI VIRAMA
+11DF0         ; Mn #       BENGALI SIGN COMBINING ANUSVARA ABOVE
 11EF3..11EF4  ; Mn #   [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
 11F00..11F01  ; Mn #   [2] KAWI SIGN CANDRABINDU..KAWI SIGN ANUSVARA
 11F36..11F3A  ; Mn #   [5] KAWI VOWEL SIGN I..KAWI VOWEL SIGN VOCALIC R
@@ -3040,17 +3115,21 @@ FE20..FE2F    ; Mn #  [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITL
 1612D..1612F  ; Mn #   [3] GURUNG KHEMA SIGN ANUSVARA..GURUNG KHEMA SIGN THOLHOMA
 16AF0..16AF4  ; Mn #   [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
 16B30..16B36  ; Mn #   [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
+16D98         ; Mn #       CHISOI SIGN ANUSVARA
+16D9D         ; Mn #       CHISOI SIGN SISO
 16F4F         ; Mn #       MIAO SIGN CONSONANT MODIFIER BAR
 16F8F..16F92  ; Mn #   [4] MIAO TONE RIGHT..MIAO TONE BELOW
 16FE4         ; Mn #       KHITAN SMALL SCRIPT FILLER
 1BC9D..1BC9E  ; Mn #   [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK
 1CF00..1CF2D  ; Mn #  [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT
 1CF30..1CF46  ; Mn #  [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG
+1D127..1D128  ; Mn #   [2] MUSICAL SYMBOL COMBINING STRESS..MUSICAL SYMBOL COMBINING UNSTRESS
 1D167..1D169  ; Mn #   [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
 1D17B..1D182  ; Mn #   [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE
 1D185..1D18B  ; Mn #   [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
 1D1AA..1D1AD  ; Mn #   [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
 1D242..1D244  ; Mn #   [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+1D25B..1D25C  ; Mn #   [2] MUSICAL SYMBOL COMBINING TREMOLO-4..MUSICAL SYMBOL COMBINING TREMOLO-5
 1DA00..1DA36  ; Mn #  [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN
 1DA3B..1DA6C  ; Mn #  [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT
 1DA75         ; Mn #       SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS
@@ -3068,11 +3147,15 @@ FE20..FE2F    ; Mn #  [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITL
 1E2EC..1E2EF  ; Mn #   [4] WANCHO TONE TUP..WANCHO TONE KOINI
 1E4EC..1E4EF  ; Mn #   [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH
 1E5EE..1E5EF  ; Mn #   [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR
+1E6E3         ; Mn #       TAI YO SIGN UE
+1E6E6         ; Mn #       TAI YO SIGN AU
+1E6EE..1E6EF  ; Mn #   [2] TAI YO SIGN AY..TAI YO SIGN ANG
+1E6F5         ; Mn #       TAI YO SIGN OM
 1E8D0..1E8D6  ; Mn #   [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
 1E944..1E94A  ; Mn #   [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
 E0100..E01EF  ; Mn # [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
 
-# Total code points: 2020
+# Total code points: 2092
 
 # ================================================
 
@@ -3262,6 +3345,9 @@ ABEC          ; Mc #       MEETEI MAYEK LUM IYEK
 11A39         ; Mc #       ZANABAZAR SQUARE SIGN VISARGA
 11A57..11A58  ; Mc #   [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU
 11A97         ; Mc #       SOYOMBO SIGN VISARGA
+11B61         ; Mc #       SHARADA VOWEL SIGN OOE
+11B65         ; Mc #       SHARADA VOWEL SIGN SHORT O
+11B67         ; Mc #       SHARADA VOWEL SIGN CANDRA O
 11C2F         ; Mc #       BHAIKSUKI VOWEL SIGN AA
 11C3E         ; Mc #       BHAIKSUKI SIGN VISARGA
 11CA9         ; Mc #       MARCHEN SUBJOINED LETTER YA
@@ -3280,8 +3366,11 @@ ABEC          ; Mc #       MEETEI MAYEK LUM IYEK
 16FF0..16FF1  ; Mc #   [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY
 1D165..1D166  ; Mc #   [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
 1D16D..1D172  ; Mc #   [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5
+1D250..1D252  ; Mc #   [3] MUSICAL SYMBOL COMBINING FLAG-6..MUSICAL SYMBOL COMBINING FLAG-8
+1D25F         ; Mc #       MUSICAL SYMBOL COMBINING BUZZ ROLL STEM
+1D280..1D281  ; Mc #   [2] MUSICAL SYMBOL COMBINING STEM BOW BEHIND BRIDGE..MUSICAL SYMBOL COMBINING STEM BOW ON TOP OF BRIDGE
 
-# Total code points: 468
+# Total code points: 477
 
 # ================================================
 
@@ -3344,12 +3433,14 @@ FF10..FF19    ; Nd #  [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE
 11C50..11C59  ; Nd #  [10] BHAIKSUKI DIGIT ZERO..BHAIKSUKI DIGIT NINE
 11D50..11D59  ; Nd #  [10] MASARAM GONDI DIGIT ZERO..MASARAM GONDI DIGIT NINE
 11DA0..11DA9  ; Nd #  [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE
+11DE0..11DE9  ; Nd #  [10] TOLONG SIKI DIGIT ZERO..TOLONG SIKI DIGIT NINE
 11F50..11F59  ; Nd #  [10] KAWI DIGIT ZERO..KAWI DIGIT NINE
 16130..16139  ; Nd #  [10] GURUNG KHEMA DIGIT ZERO..GURUNG KHEMA DIGIT NINE
 16A60..16A69  ; Nd #  [10] MRO DIGIT ZERO..MRO DIGIT NINE
 16AC0..16AC9  ; Nd #  [10] TANGSA DIGIT ZERO..TANGSA DIGIT NINE
 16B50..16B59  ; Nd #  [10] PAHAWH HMONG DIGIT ZERO..PAHAWH HMONG DIGIT NINE
 16D70..16D79  ; Nd #  [10] KIRAT RAI DIGIT ZERO..KIRAT RAI DIGIT NINE
+16DA0..16DA9  ; Nd #  [10] CHISOI DIGIT ZERO..CHISOI DIGIT NINE
 1CCF0..1CCF9  ; Nd #  [10] OUTLINED DIGIT ZERO..OUTLINED DIGIT NINE
 1D7CE..1D7FF  ; Nd #  [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE
 1E140..1E149  ; Nd #  [10] NYIAKENG PUACHUE HMONG DIGIT ZERO..NYIAKENG PUACHUE HMONG DIGIT NINE
@@ -3359,7 +3450,7 @@ FF10..FF19    ; Nd #  [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE
 1E950..1E959  ; Nd #  [10] ADLAM DIGIT ZERO..ADLAM DIGIT NINE
 1FBF0..1FBF9  ; Nd #  [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE
 
-# Total code points: 760
+# Total code points: 780
 
 # ================================================
 
@@ -3376,9 +3467,12 @@ A6E6..A6EF    ; Nl #  [10] BAMUM LETTER MO..BAMUM LETTER KOGHOM
 10341         ; Nl #       GOTHIC LETTER NINETY
 1034A         ; Nl #       GOTHIC LETTER NINE HUNDRED
 103D1..103D5  ; Nl #   [5] OLD PERSIAN NUMBER ONE..OLD PERSIAN NUMBER HUNDRED
-12400..1246E  ; Nl # [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM
+12400..1246F  ; Nl # [112] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN SEVEN ASH TENU
+12475..1247F  ; Nl #  [11] CUNEIFORM NUMERIC SIGN EIGHT ASH TENU..CUNEIFORM NUMERIC SIGN ASH TIMES NINE DISH TENU
+12550..12686  ; Nl # [311] CUNEIFORM NUMERIC SIGN ONE N01..CUNEIFORM NUMERIC SIGN ONE N36 FLAT
+16FF4..16FF6  ; Nl #   [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS
 
-# Total code points: 236
+# Total code points: 562
 
 # ================================================
 
@@ -3625,6 +3719,7 @@ FF0D          ; Pd #       FULLWIDTH HYPHEN-MINUS
 2E57          ; Ps #       LEFT SQUARE BRACKET WITH DOUBLE STROKE
 2E59          ; Ps #       TOP HALF LEFT PARENTHESIS
 2E5B          ; Ps #       BOTTOM HALF LEFT PARENTHESIS
+2E62          ; Ps #       LEFT PARENTHESIS WITH MIDDLE RING
 3008          ; Ps #       LEFT ANGLE BRACKET
 300A          ; Ps #       LEFT DOUBLE ANGLE BRACKET
 300C          ; Ps #       LEFT CORNER BRACKET
@@ -3655,7 +3750,7 @@ FF5B          ; Ps #       FULLWIDTH LEFT CURLY BRACKET
 FF5F          ; Ps #       FULLWIDTH LEFT WHITE PARENTHESIS
 FF62          ; Ps #       HALFWIDTH LEFT CORNER BRACKET
 
-# Total code points: 79
+# Total code points: 80
 
 # ================================================
 
@@ -3708,6 +3803,7 @@ FF62          ; Ps #       HALFWIDTH LEFT CORNER BRACKET
 2E58          ; Pe #       RIGHT SQUARE BRACKET WITH DOUBLE STROKE
 2E5A          ; Pe #       TOP HALF RIGHT PARENTHESIS
 2E5C          ; Pe #       BOTTOM HALF RIGHT PARENTHESIS
+2E63          ; Pe #       RIGHT PARENTHESIS WITH MIDDLE RING
 3009          ; Pe #       RIGHT ANGLE BRACKET
 300B          ; Pe #       RIGHT DOUBLE ANGLE BRACKET
 300D          ; Pe #       RIGHT CORNER BRACKET
@@ -3738,7 +3834,7 @@ FF5D          ; Pe #       FULLWIDTH RIGHT CURLY BRACKET
 FF60          ; Pe #       FULLWIDTH RIGHT WHITE PARENTHESIS
 FF63          ; Pe #       HALFWIDTH RIGHT CORNER BRACKET
 
-# Total code points: 77
+# Total code points: 78
 
 # ================================================
 
@@ -3848,6 +3944,7 @@ FF3F          ; Pc #       FULLWIDTH LOW LINE
 2E41          ; Po #       REVERSED COMMA
 2E43..2E4F    ; Po #  [13] DASH WITH LEFT UPTURN..CORNISH VERSE DIVIDER
 2E52..2E54    ; Po #   [3] TIRONIAN SIGN CAPITAL ET..MEDIEVAL QUESTION MARK
+2E60..2E61    ; Po #   [2] WIGGLY EXCLAMATION MARK..INVERTED WIGGLY EXCLAMATION MARK
 3001..3003    ; Po #   [3] IDEOGRAPHIC COMMA..DITTO MARK
 303D          ; Po #       PART ALTERNATION MARK
 30FB          ; Po #       KATAKANA MIDDLE DOT
@@ -3900,6 +3997,7 @@ FF64..FF65    ; Po #   [2] HALFWIDTH IDEOGRAPHIC COMMA..HALFWIDTH KATAKANA MIDDL
 10AF0..10AF6  ; Po #   [7] MANICHAEAN PUNCTUATION STAR..MANICHAEAN PUNCTUATION LINE FILLER
 10B39..10B3F  ; Po #   [7] AVESTAN ABBREVIATION MARK..LARGE ONE RING OVER TWO RINGS PUNCTUATION
 10B99..10B9C  ; Po #   [4] PSALTER PAHLAVI SECTION MARK..PSALTER PAHLAVI FOUR DOTS WITH DOT
+10ED0         ; Po #       ARABIC BIBLICAL END OF VERSE
 10F55..10F59  ; Po #   [5] SOGDIAN PUNCTUATION TWO VERTICAL BARS..SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT
 10F86..10F89  ; Po #   [4] OLD UYGHUR PUNCTUATION BAR..OLD UYGHUR PUNCTUATION FOUR DOTS
 11047..1104D  ; Po #   [7] BRAHMI DANDA..BRAHMI PUNCTUATION LOTUS
@@ -3951,7 +4049,7 @@ FF64..FF65    ; Po #   [2] HALFWIDTH IDEOGRAPHIC COMMA..HALFWIDTH KATAKANA MIDDL
 1E5FF         ; Po #       OL ONAL ABBREVIATION SIGN
 1E95E..1E95F  ; Po #   [2] ADLAM INITIAL EXCLAMATION MARK..ADLAM INITIAL QUESTION MARK
 
-# Total code points: 640
+# Total code points: 643
 
 # ================================================
 
@@ -4011,6 +4109,8 @@ FF5E          ; Sm #       FULLWIDTH TILDE
 FFE2          ; Sm #       FULLWIDTH NOT SIGN
 FFE9..FFEC    ; Sm #   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW
 10D8E..10D8F  ; Sm #   [2] GARAY PLUS SIGN..GARAY MINUS SIGN
+1CEDD..1CEDF  ; Sm #   [3] SQUARE ROOT OF SQUARE ROOT..SQUARE ROOT OF SQUARE ROOT OF SQUARE ROOT OF SQUARE ROOT
+1CEF0..1CEFD  ; Sm #  [14] MEDIUM SMALL WHITE CIRCLE WITH HORIZONTAL BAR..HYPERBOLA
 1D6C1         ; Sm #       MATHEMATICAL BOLD NABLA
 1D6DB         ; Sm #       MATHEMATICAL BOLD PARTIAL DIFFERENTIAL
 1D6FB         ; Sm #       MATHEMATICAL ITALIC NABLA
@@ -4021,9 +4121,11 @@ FFE9..FFEC    ; Sm #   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW
 1D789         ; Sm #       MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL
 1D7A9         ; Sm #       MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA
 1D7C3         ; Sm #       MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL
+1DB00..1DB1C  ; Sm #  [29] LEIBNIZIAN EQUALS SIGN..FACIT SYMBOL
 1EEF0..1EEF1  ; Sm #   [2] ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL..ARABIC MATHEMATICAL OPERATOR HAH WITH DAL
+1F8D0..1F8D8  ; Sm #   [9] LONG RIGHTWARDS ARROW OVER LONG LEFTWARDS ARROW..LONG LEFT RIGHT ARROW WITH DEPENDENT LOBE
 
-# Total code points: 950
+# Total code points: 1005
 
 # ================================================
 
@@ -4040,7 +4142,7 @@ FFE9..FFEC    ; Sm #   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW
 0BF9          ; Sc #       TAMIL RUPEE SIGN
 0E3F          ; Sc #       THAI CURRENCY SYMBOL BAHT
 17DB          ; Sc #       KHMER CURRENCY SYMBOL RIEL
-20A0..20C0    ; Sc #  [33] EURO-CURRENCY SIGN..SOM SIGN
+20A0..20C4    ; Sc #  [37] EURO-CURRENCY SIGN..OMANI RIAL SIGN
 A838          ; Sc #       NORTH INDIC RUPEE MARK
 FDFC          ; Sc #       RIAL SIGN
 FE69          ; Sc #       SMALL DOLLAR SIGN
@@ -4051,7 +4153,7 @@ FFE5..FFE6    ; Sc #   [2] FULLWIDTH YEN SIGN..FULLWIDTH WON SIGN
 1E2FF         ; Sc #       WANCHO NGUN SIGN
 1ECB0         ; Sc #       INDIC SIYAQ RUPEE MARK
 
-# Total code points: 63
+# Total code points: 67
 
 # ================================================
 
@@ -4087,9 +4189,10 @@ FBB2..FBC2    ; Sk #  [17] ARABIC SYMBOL DOT ABOVE..ARABIC SYMBOL WASLA ABOVE
 FF3E          ; Sk #       FULLWIDTH CIRCUMFLEX ACCENT
 FF40          ; Sk #       FULLWIDTH GRAVE ACCENT
 FFE3          ; Sk #       FULLWIDTH MACRON
+10EC9..10ECA  ; Sk #   [2] ARABIC SMALL BASELINE FATHA..ARABIC SMALL BASELINE DOTLESS HEAD OF KHAH
 1F3FB..1F3FF  ; Sk #   [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6
 
-# Total code points: 125
+# Total code points: 127
 
 # ================================================
 
@@ -4174,8 +4277,7 @@ FFE3          ; Sk #       FULLWIDTH MACRON
 2B00..2B2F    ; So #  [48] NORTH EAST WHITE ARROW..WHITE VERTICAL ELLIPSE
 2B45..2B46    ; So #   [2] LEFTWARDS QUADRUPLE ARROW..RIGHTWARDS QUADRUPLE ARROW
 2B4D..2B73    ; So #  [39] DOWNWARDS TRIANGLE-HEADED ZIGZAG ARROW..DOWNWARDS TRIANGLE-HEADED ARROW TO BAR
-2B76..2B95    ; So #  [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW
-2B97..2BFF    ; So # [105] SYMBOL FOR TYPE A ELECTRONICS..HELLSCHREIBER PAUSE SYMBOL
+2B76..2BFF    ; So # [138] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..HELLSCHREIBER PAUSE SYMBOL
 2CE5..2CEA    ; So #   [6] COPTIC SYMBOL MI RO..COPTIC SYMBOL SHIMA SIMA
 2E50..2E51    ; So #   [2] CROSS PATTY WITH RIGHT CROSSBAR..CROSS PATTY WITH LEFT CROSSBAR
 2E80..2E99    ; So #  [26] CJK RADICAL REPEAT..CJK RADICAL RAP
@@ -4203,8 +4305,10 @@ A828..A82B    ; So #   [4] SYLOTI NAGRI POETRY MARK-1..SYLOTI NAGRI POETRY MARK-
 A836..A837    ; So #   [2] NORTH INDIC QUARTER MARK..NORTH INDIC PLACEHOLDER MARK
 A839          ; So #       NORTH INDIC QUANTITY MARK
 AA77..AA79    ; So #   [3] MYANMAR SYMBOL AITON EXCLAMATION..MYANMAR SYMBOL AITON TWO
+FBC3..FBD2    ; So #  [16] ARABIC LIGATURE JALLA WA-ALAA..ARABIC LIGATURE ALAYHI AR-RAHMAH
 FD40..FD4F    ; So #  [16] ARABIC LIGATURE RAHIMAHU ALLAAH..ARABIC LIGATURE RAHIMAHUM ALLAAH
-FDCF          ; So #       ARABIC LIGATURE SALAAMUHU ALAYNAA
+FD90..FD91    ; So #   [2] ARABIC LIGATURE RAHMATU ALLAAHI ALAYH..ARABIC LIGATURE RAHMATU ALLAAHI ALAYHAA
+FDC8..FDCF    ; So #   [8] ARABIC LIGATURE RAHIMAHU ALLAAH TAAALAA..ARABIC LIGATURE SALAAMUHU ALAYNAA
 FDFD..FDFF    ; So #   [3] ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM..ARABIC LIGATURE AZZA WA JALL
 FFE4          ; So #       FULLWIDTH BROKEN BAR
 FFE8          ; So #       HALFWIDTH FORMS LIGHT VERTICAL
@@ -4218,6 +4322,7 @@ FFFC..FFFD    ; So #   [2] OBJECT REPLACEMENT CHARACTER..REPLACEMENT CHARACTER
 101D0..101FC  ; So #  [45] PHAISTOS DISC SIGN PEDESTRIAN..PHAISTOS DISC SIGN WAVY BAND
 10877..10878  ; So #   [2] PALMYRENE LEFT-POINTING FLEURON..PALMYRENE RIGHT-POINTING FLEURON
 10AC8         ; So #       MANICHAEAN SIGN UD
+10ED1..10ED8  ; So #   [8] ARABIC LIGATURE ALAYHAA AS-SALAATU WAS-SALAAM..ARABIC LIGATURE NAWWARA ALLAAHU MARQADAH
 1173F         ; So #       AHOM SYMBOL VI
 11FD5..11FDC  ; So #   [8] TAMIL SIGN NEL..TAMIL SIGN MUKKURUNI
 11FE1..11FF1  ; So #  [17] TAMIL SIGN PAARAM..TAMIL SIGN VAKAIYARAA
@@ -4225,7 +4330,11 @@ FFFC..FFFD    ; So #   [2] OBJECT REPLACEMENT CHARACTER..REPLACEMENT CHARACTER
 16B45         ; So #       PAHAWH HMONG SIGN CIM TSOV ROG
 1BC9C         ; So #       DUPLOYAN SIGN O WITH CROSS
 1CC00..1CCEF  ; So # [240] UP-POINTING GO-KART..OUTLINED LATIN CAPITAL LETTER Z
+1CCFA..1CCFC  ; So #   [3] SNAKE SYMBOL..NOSE SYMBOL
 1CD00..1CEB3  ; So # [436] BLOCK OCTANT-3..BLACK RIGHT TRIANGLE CARET
+1CEBA..1CED0  ; So #  [23] FRAGILE SYMBOL..LEUKOTHEA
+1CED2..1CED4  ; So #   [3] ALCHEMICAL SYMBOL FOR OIL INVERTED..ALCHEMICAL SYMBOL FOR SALT OF TARTAR
+1CEE0..1CEEF  ; So #  [16] GEOMANTIC FIGURE POPULUS..GEOMANTIC FIGURE VIA
 1CF50..1CFC3  ; So # [116] ZNAMENNY NEUME KRYUK..ZNAMENNY NEUME PAUK
 1D000..1D0F5  ; So # [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO
 1D100..1D126  ; So #  [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2
@@ -4233,9 +4342,11 @@ FFFC..FFFD    ; So #   [2] OBJECT REPLACEMENT CHARACTER..REPLACEMENT CHARACTER
 1D16A..1D16C  ; So #   [3] MUSICAL SYMBOL FINGERED TREMOLO-1..MUSICAL SYMBOL FINGERED TREMOLO-3
 1D183..1D184  ; So #   [2] MUSICAL SYMBOL ARPEGGIATO UP..MUSICAL SYMBOL ARPEGGIATO DOWN
 1D18C..1D1A9  ; So #  [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL DEGREE SLASH
-1D1AE..1D1EA  ; So #  [61] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL KORON
-1D200..1D241  ; So #  [66] GREEK VOCAL NOTATION SYMBOL-1..GREEK INSTRUMENTAL NOTATION SYMBOL-54
+1D1AE..1D241  ; So # [148] MUSICAL SYMBOL PEDAL MARK..GREEK INSTRUMENTAL NOTATION SYMBOL-54
 1D245         ; So #       GREEK MUSICAL LEIMMA
+1D253..1D25A  ; So #   [8] MUSICAL SYMBOL TWO HUNDRED FIFTY-SIXTH REST..MUSICAL SYMBOL SHARP WITH STROKE
+1D25D..1D25E  ; So #   [2] MUSICAL SYMBOL FINGERED TREMOLO-4..MUSICAL SYMBOL FINGERED TREMOLO-5
+1D260..1D27F  ; So #  [32] MUSICAL SYMBOL TRIPLE FLAT..MUSICAL SYMBOL ARPEGGIATO UP WITH ARROW
 1D300..1D356  ; So #  [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING
 1D800..1D9FF  ; So # [512] SIGNWRITING HAND-FIST INDEX..SIGNWRITING HEAD
 1DA37..1DA3A  ; So #   [4] SIGNWRITING AIR BLOW SMALL ROTATIONS..SIGNWRITING BREATH EXHALE
@@ -4251,39 +4362,38 @@ FFFC..FFFD    ; So #   [2] OBJECT REPLACEMENT CHARACTER..REPLACEMENT CHARACTER
 1F0B1..1F0BF  ; So #  [15] PLAYING CARD ACE OF HEARTS..PLAYING CARD RED JOKER
 1F0C1..1F0CF  ; So #  [15] PLAYING CARD ACE OF DIAMONDS..PLAYING CARD BLACK JOKER
 1F0D1..1F0F5  ; So #  [37] PLAYING CARD ACE OF CLUBS..PLAYING CARD TRUMP-21
-1F10D..1F1AD  ; So # [161] CIRCLED ZERO WITH SLASH..MASK WORK SYMBOL
+1F10D..1F1AE  ; So # [162] CIRCLED ZERO WITH SLASH..TOMOBIKI SYMBOL
 1F1E6..1F202  ; So #  [29] REGIONAL INDICATOR SYMBOL LETTER A..SQUARED KATAKANA SA
 1F210..1F23B  ; So #  [44] SQUARED CJK UNIFIED IDEOGRAPH-624B..SQUARED CJK UNIFIED IDEOGRAPH-914D
 1F240..1F248  ; So #   [9] TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C..TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557
 1F250..1F251  ; So #   [2] CIRCLED IDEOGRAPH ADVANTAGE..CIRCLED IDEOGRAPH ACCEPT
 1F260..1F265  ; So #   [6] ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
 1F300..1F3FA  ; So # [251] CYCLONE..AMPHORA
-1F400..1F6D7  ; So # [728] RAT..ELEVATOR
+1F400..1F6D9  ; So # [730] RAT..LIGHTHOUSE
 1F6DC..1F6EC  ; So #  [17] WIRELESS..AIRPLANE ARRIVING
 1F6F0..1F6FC  ; So #  [13] SATELLITE..ROLLER SKATE
-1F700..1F776  ; So # [119] ALCHEMICAL SYMBOL FOR QUINTESSENCE..LUNAR ECLIPSE
-1F77B..1F7D9  ; So #  [95] HAUMEA..NINE POINTED WHITE STAR
+1F700..1F7DB  ; So # [220] ALCHEMICAL SYMBOL FOR QUINTESSENCE..BULLET IN DOUBLE CIRCLE
 1F7E0..1F7EB  ; So #  [12] LARGE ORANGE CIRCLE..LARGE BROWN SQUARE
-1F7F0         ; So #       HEAVY EQUALS SIGN
-1F800..1F80B  ; So #  [12] LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD
+1F7F0..1F80B  ; So #  [28] HEAVY EQUALS SIGN..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD
 1F810..1F847  ; So #  [56] LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD..DOWNWARDS HEAVY ARROW
 1F850..1F859  ; So #  [10] LEFTWARDS SANS-SERIF ARROW..UP DOWN SANS-SERIF ARROW
 1F860..1F887  ; So #  [40] WIDE-HEADED LEFTWARDS LIGHT BARB ARROW..WIDE-HEADED SOUTH WEST VERY HEAVY BARB ARROW
 1F890..1F8AD  ; So #  [30] LEFTWARDS TRIANGLE ARROWHEAD..WHITE ARROW SHAFT WIDTH TWO THIRDS
 1F8B0..1F8BB  ; So #  [12] ARROW POINTING UPWARDS THEN NORTH WEST..SOUTH WEST ARROW FROM BAR
 1F8C0..1F8C1  ; So #   [2] LEFTWARDS ARROW FROM DOWNWARDS ARROW..RIGHTWARDS ARROW FROM DOWNWARDS ARROW
-1F900..1FA53  ; So # [340] CIRCLED CROSS FORMEE WITH FOUR DOTS..BLACK CHESS KNIGHT-BISHOP
+1F900..1FA57  ; So # [344] CIRCLED CROSS FORMEE WITH FOUR DOTS..BLACK CHESS ALFIL
 1FA60..1FA6D  ; So #  [14] XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
 1FA70..1FA7C  ; So #  [13] BALLET SHOES..CRUTCH
-1FA80..1FA89  ; So #  [10] YO-YO..HARP
-1FA8F..1FAC6  ; So #  [56] SHOVEL..FINGERPRINT
-1FACE..1FADC  ; So #  [15] MOOSE..ROOT VEGETABLE
-1FADF..1FAE9  ; So #  [11] SPLATTER..FACE WITH BAGS UNDER EYES
-1FAF0..1FAF8  ; So #   [9] HAND WITH INDEX FINGER AND THUMB CROSSED..RIGHTWARDS PUSHING HAND
+1FA80..1FAC6  ; So #  [71] YO-YO..FINGERPRINT
+1FAC8         ; So #       HAIRY CREATURE
+1FACC..1FADD  ; So #  [18] MONARCH BUTTERFLY..PICKLE
+1FADF..1FAEB  ; So #  [13] SPLATTER..CRACKING FACE
+1FAEF..1FAFA  ; So #  [12] FIGHT CLOUD..RIGHTWARDS THUMB SIGN
 1FB00..1FB92  ; So # [147] BLOCK SEXTANT-1..UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK
 1FB94..1FBEF  ; So #  [92] LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK..TOP LEFT JUSTIFIED LOWER RIGHT QUARTER BLACK CIRCLE
+1FBFA         ; So #       ALARM BELL SYMBOL
 
-# Total code points: 7376
+# Total code points: 7561
 
 # ================================================
 
diff --git a/libcxx/utils/data/unicode/EastAsianWidth.txt b/libcxx/utils/data/unicode/EastAsianWidth.txt
index 99f7a31ea5d8a..501249db619e5 100644
--- a/libcxx/utils/data/unicode/EastAsianWidth.txt
+++ b/libcxx/utils/data/unicode/EastAsianWidth.txt
@@ -1,6 +1,6 @@
-# EastAsianWidth-16.0.0.txt
-# Date: 2024-04-30, 21:48:20 GMT
-# © 2024 Unicode®, Inc.
+# EastAsianWidth-18.0.0.txt
+# Date: 2026-04-29, 19:44:08 GMT
+# © 2026 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
 # For terms of use and license, see https://www.unicode.org/terms_of_use.html
 #
@@ -180,8 +180,8 @@
 0252..0260     ; N  # Ll    [15] LATIN SMALL LETTER TURNED ALPHA..LATIN SMALL LETTER G WITH HOOK
 0261           ; A  # Ll         LATIN SMALL LETTER SCRIPT G
 0262..0293     ; N  # Ll    [50] LATIN LETTER SMALL CAPITAL G..LATIN SMALL LETTER EZH WITH CURL
-0294           ; N  # Lo         LATIN LETTER GLOTTAL STOP
-0295..02AF     ; N  # Ll    [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+0294..0295     ; N  # Lo     [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE
+0296..02AF     ; N  # Ll    [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
 02B0..02C1     ; N  # Lm    [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP
 02C2..02C3     ; N  # Sk     [2] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER RIGHT ARROWHEAD
 02C4           ; A  # Sk         MODIFIER LETTER UP ARROWHEAD
@@ -244,11 +244,12 @@
 048A..04FF     ; N  # L&   [118] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER HA WITH STROKE
 0500..052F     ; N  # L&    [48] CYRILLIC CAPITAL LETTER KOMI DE..CYRILLIC SMALL LETTER EL WITH DESCENDER
 0531..0556     ; N  # Lu    [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH
-0559           ; N  # Lm         ARMENIAN MODIFIER LETTER LEFT HALF RING
+0558..0559     ; N  # Lm     [2] MODIFIER LETTER ARMENIAN SMALL EH..ARMENIAN MODIFIER LETTER LEFT HALF RING
 055A..055F     ; N  # Po     [6] ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION MARK
 0560..0588     ; N  # Ll    [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE
 0589           ; N  # Po         ARMENIAN FULL STOP
 058A           ; N  # Pd         ARMENIAN HYPHEN
+058B..058C     ; N  # Lm     [2] MODIFIER LETTER ARMENIAN SMALL INI..MODIFIER LETTER ARMENIAN SMALL YI
 058D..058E     ; N  # So     [2] RIGHT-FACING ARMENIAN ETERNITY SIGN..LEFT-FACING ARMENIAN ETERNITY SIGN
 058F           ; N  # Sc         ARMENIAN DRAM SIGN
 0591..05BD     ; N  # Mn    [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
@@ -259,7 +260,7 @@
 05C3           ; N  # Po         HEBREW PUNCTUATION SOF PASUQ
 05C4..05C5     ; N  # Mn     [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
 05C6           ; N  # Po         HEBREW PUNCTUATION NUN HAFUKHA
-05C7           ; N  # Mn         HEBREW POINT QAMATS QATAN
+05C7..05C9     ; N  # Mn     [3] HEBREW POINT QAMATS QATAN..HEBREW POINT DAGESH HAZAQ MUDGASH
 05D0..05EA     ; N  # Lo    [27] HEBREW LETTER ALEF..HEBREW LETTER TAV
 05EF..05F2     ; N  # Lo     [4] HEBREW YOD TRIANGLE..HEBREW LIGATURE YIDDISH DOUBLE YOD
 05F3..05F4     ; N  # Po     [2] HEBREW PUNCTUATION GERESH..HEBREW PUNCTUATION GERSHAYIM
@@ -332,7 +333,7 @@
 0860..086A     ; N  # Lo    [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA
 0870..0887     ; N  # Lo    [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT
 0888           ; N  # Sk         ARABIC RAISED ROUND DOT
-0889..088E     ; N  # Lo     [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL
+0889..088F     ; N  # Lo     [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE
 0890..0891     ; N  # Cf     [2] ARABIC POUND MARK ABOVE..ARABIC PIASTRE MARK ABOVE
 0897..089F     ; N  # Mn     [9] ARABIC PEPET..ARABIC HALF MADDA OVER MADDA
 08A0..08C8     ; N  # Lo    [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF
@@ -454,7 +455,7 @@
 0B47..0B48     ; N  # Mc     [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
 0B4B..0B4C     ; N  # Mc     [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
 0B4D           ; N  # Mn         ORIYA SIGN VIRAMA
-0B55..0B56     ; N  # Mn     [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK
+0B53..0B56     ; N  # Mn     [4] ORIYA SIGN DOT ABOVE..ORIYA AI LENGTH MARK
 0B57           ; N  # Mc         ORIYA AU LENGTH MARK
 0B5C..0B5D     ; N  # Lo     [2] ORIYA LETTER RRA..ORIYA LETTER RHA
 0B5F..0B61     ; N  # Lo     [3] ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL
@@ -502,7 +503,7 @@
 0C4A..0C4D     ; N  # Mn     [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
 0C55..0C56     ; N  # Mn     [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
 0C58..0C5A     ; N  # Lo     [3] TELUGU LETTER TSA..TELUGU LETTER RRRA
-0C5D           ; N  # Lo         TELUGU LETTER NAKAARA POLLU
+0C5C..0C5D     ; N  # Lo     [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU
 0C60..0C61     ; N  # Lo     [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
 0C62..0C63     ; N  # Mn     [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
 0C66..0C6F     ; N  # Nd    [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE
@@ -528,7 +529,7 @@
 0CCA..0CCB     ; N  # Mc     [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
 0CCC..0CCD     ; N  # Mn     [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA
 0CD5..0CD6     ; N  # Mc     [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
-0CDD..0CDE     ; N  # Lo     [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA
+0CDC..0CDE     ; N  # Lo     [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA
 0CE0..0CE1     ; N  # Lo     [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
 0CE2..0CE3     ; N  # Mn     [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL
 0CE6..0CEF     ; N  # Nd    [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE
@@ -806,7 +807,7 @@
 1AA8..1AAD     ; N  # Po     [6] TAI THAM SIGN KAAN..TAI THAM SIGN CAANG
 1AB0..1ABD     ; N  # Mn    [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
 1ABE           ; N  # Me         COMBINING PARENTHESES OVERLAY
-1ABF..1ACE     ; N  # Mn    [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T
+1ABF..1AF0     ; N  # Mn    [50] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOUBLE COMMA ABOVE
 1B00..1B03     ; N  # Mn     [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
 1B04           ; N  # Mc         BALINESE SIGN BISAH
 1B05..1B33     ; N  # Lo    [47] BALINESE LETTER AKARA..BALINESE LETTER HA
@@ -971,12 +972,12 @@
 208A..208C     ; N  # Sm     [3] SUBSCRIPT PLUS SIGN..SUBSCRIPT EQUALS SIGN
 208D           ; N  # Ps         SUBSCRIPT LEFT PARENTHESIS
 208E           ; N  # Pe         SUBSCRIPT RIGHT PARENTHESIS
-2090..209C     ; N  # Lm    [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T
+208F..209F     ; N  # Lm    [17] MODIFIER LETTER HIGH AND LOW VERTICAL LINE..LATIN SUBSCRIPT SMALL LETTER Z
 20A0..20A8     ; N  # Sc     [9] EURO-CURRENCY SIGN..RUPEE SIGN
 20A9           ; H  # Sc         WON SIGN
 20AA..20AB     ; N  # Sc     [2] NEW SHEQEL SIGN..DONG SIGN
 20AC           ; A  # Sc         EURO SIGN
-20AD..20C0     ; N  # Sc    [20] KIP SIGN..SOM SIGN
+20AD..20C4     ; N  # Sc    [24] KIP SIGN..OMANI RIAL SIGN
 20D0..20DC     ; N  # Mn    [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE
 20DD..20E0     ; N  # Me     [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH
 20E1           ; N  # Mn         COMBINING LEFT RIGHT ARROW ABOVE
@@ -1351,8 +1352,7 @@
 2B55           ; W  # So         HEAVY LARGE CIRCLE
 2B56..2B59     ; A  # So     [4] HEAVY OVAL WITH OVAL INSIDE..HEAVY CIRCLED SALTIRE
 2B5A..2B73     ; N  # So    [26] SLANTED NORTH ARROW WITH HOOKED HEAD..DOWNWARDS TRIANGLE-HEADED ARROW TO BAR
-2B76..2B95     ; N  # So    [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW
-2B97..2BFF     ; N  # So   [105] SYMBOL FOR TYPE A ELECTRONICS..HELLSCHREIBER PAUSE SYMBOL
+2B76..2BFF     ; N  # So   [138] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..HELLSCHREIBER PAUSE SYMBOL
 2C00..2C5F     ; N  # L&    [96] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC SMALL LETTER CAUDATE CHRIVI
 2C60..2C7B     ; N  # L&    [28] LATIN CAPITAL LETTER L WITH DOUBLE BAR..LATIN LETTER SMALL CAPITAL TURNED E
 2C7C..2C7D     ; N  # Lm     [2] LATIN SUBSCRIPT SMALL LETTER J..MODIFIER LETTER CAPITAL V
@@ -1431,6 +1431,9 @@
 2E5B           ; N  # Ps         BOTTOM HALF LEFT PARENTHESIS
 2E5C           ; N  # Pe         BOTTOM HALF RIGHT PARENTHESIS
 2E5D           ; N  # Pd         OBLIQUE HYPHEN
+2E60..2E61     ; N  # Po     [2] WIGGLY EXCLAMATION MARK..INVERTED WIGGLY EXCLAMATION MARK
+2E62           ; N  # Ps         LEFT PARENTHESIS WITH MIDDLE RING
+2E63           ; N  # Pe         RIGHT PARENTHESIS WITH MIDDLE RING
 2E80..2E99     ; W  # So    [26] CJK RADICAL REPEAT..CJK RADICAL RAP
 2E9B..2EF3     ; W  # So    [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE
 2F00..2FD5     ; W  # So   [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE
@@ -1548,11 +1551,9 @@ A788           ; N  # Lm         MODIFIER LETTER LOW CIRCUMFLEX ACCENT
 A789..A78A     ; N  # Sk     [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
 A78B..A78E     ; N  # L&     [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT
 A78F           ; N  # Lo         LATIN LETTER SINOLOGICAL DOT
-A790..A7CD     ; N  # L&    [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE
-A7D0..A7D1     ; N  # L&     [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G
-A7D3           ; N  # Ll         LATIN SMALL LETTER DOUBLE THORN
-A7D5..A7DC     ; N  # L&     [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE
-A7F2..A7F4     ; N  # Lm     [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
+A790..A7DD     ; N  # L&    [78] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER CLOSED OMEGA
+A7E2           ; N  # Lu         LATIN CAPITAL LETTER R WITH LONG LEG
+A7F1..A7F4     ; N  # Lm     [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q
 A7F5..A7F6     ; N  # L&     [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H
 A7F7           ; N  # Lo         LATIN EPIGRAPHIC LETTER SIDEWAYS I
 A7F8..A7F9     ; N  # Lm     [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
@@ -1673,6 +1674,7 @@ AB5C..AB5F     ; N  # Lm     [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMA
 AB60..AB68     ; N  # Ll     [9] LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER TURNED R WITH MIDDLE TILDE
 AB69           ; N  # Lm         MODIFIER LETTER SMALL TURNED W
 AB6A..AB6B     ; N  # Sk     [2] MODIFIER LETTER LEFT TACK..MODIFIER LETTER RIGHT TACK
+AB6C..AB6D     ; N  # Lu     [2] LATIN CAPITAL LETTER SCRIPT R..LATIN CAPITAL LETTER SCRIPT R WITH RING
 AB70..ABBF     ; N  # Ll    [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
 ABC0..ABE2     ; N  # Lo    [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM
 ABE3..ABE4     ; N  # Mc     [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP
@@ -1709,13 +1711,15 @@ FB43..FB44     ; N  # Lo     [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETT
 FB46..FB4F     ; N  # Lo    [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATURE ALEF LAMED
 FB50..FBB1     ; N  # Lo    [98] ARABIC LETTER ALEF WASLA ISOLATED FORM..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
 FBB2..FBC2     ; N  # Sk    [17] ARABIC SYMBOL DOT ABOVE..ARABIC SYMBOL WASLA ABOVE
+FBC3..FBD2     ; N  # So    [16] ARABIC LIGATURE JALLA WA-ALAA..ARABIC LIGATURE ALAYHI AR-RAHMAH
 FBD3..FD3D     ; N  # Lo   [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
 FD3E           ; N  # Pe         ORNATE LEFT PARENTHESIS
 FD3F           ; N  # Ps         ORNATE RIGHT PARENTHESIS
 FD40..FD4F     ; N  # So    [16] ARABIC LIGATURE RAHIMAHU ALLAAH..ARABIC LIGATURE RAHIMAHUM ALLAAH
 FD50..FD8F     ; N  # Lo    [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+FD90..FD91     ; N  # So     [2] ARABIC LIGATURE RAHMATU ALLAAHI ALAYH..ARABIC LIGATURE RAHMATU ALLAAHI ALAYHAA
 FD92..FDC7     ; N  # Lo    [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
-FDCF           ; N  # So         ARABIC LIGATURE SALAAMUHU ALAYNAA
+FDC8..FDCF     ; N  # So     [8] ARABIC LIGATURE RAHIMAHU ALLAAH TAAALAA..ARABIC LIGATURE SALAAMUHU ALAYNAA
 FDF0..FDFB     ; N  # Lo    [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU
 FDFC           ; N  # Sc         RIAL SIGN
 FDFD..FDFF     ; N  # So     [3] ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM..ARABIC LIGATURE AZZA WA JALL
@@ -1881,7 +1885,7 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 10760..10767   ; N  # Lo     [8] LINEAR A SIGN A800..LINEAR A SIGN A807
 10780..10785   ; N  # Lm     [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK
 10787..107B0   ; N  # Lm    [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
-107B2..107BA   ; N  # Lm     [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
+107B2..107BF   ; N  # Lm    [14] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL ESH WITH DOUBLE BAR
 10800..10805   ; N  # Lo     [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA
 10808          ; N  # Lo         CYPRIOT SYLLABLE JO
 1080A..10835   ; N  # Lo    [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO
@@ -1904,6 +1908,7 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 1091F          ; N  # Po         PHOENICIAN WORD SEPARATOR
 10920..10939   ; N  # Lo    [26] LYDIAN LETTER A..LYDIAN LETTER C
 1093F          ; N  # Po         LYDIAN TRIANGULAR MARK
+10940..10959   ; N  # Lo    [26] SIDETIC LETTER N01..SIDETIC LETTER N26
 10980..1099F   ; N  # Lo    [32] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC HIEROGLYPHIC SYMBOL VIDJ-2
 109A0..109B7   ; N  # Lo    [24] MEROITIC CURSIVE LETTER A..MEROITIC CURSIVE LETTER DA
 109BC..109BD   ; N  # No     [2] MEROITIC CURSIVE FRACTION ELEVEN TWELFTHS..MEROITIC CURSIVE FRACTION ONE HALF
@@ -1964,7 +1969,14 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 10EAD          ; N  # Pd         YEZIDI HYPHENATION MARK
 10EB0..10EB1   ; N  # Lo     [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
 10EC2..10EC4   ; N  # Lo     [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW
-10EFC..10EFF   ; N  # Mn     [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA
+10EC5          ; N  # Lm         ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW
+10EC6..10EC7   ; N  # Lo     [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW
+10EC9..10ECA   ; N  # Sk     [2] ARABIC SMALL BASELINE FATHA..ARABIC SMALL BASELINE DOTLESS HEAD OF KHAH
+10ECB..10ECF   ; N  # Mn     [5] ARABIC NORTHEAST POINTING ARROWHEAD ABOVE..ARABIC LARGE CIRCLE ABOVE
+10ED0          ; N  # Po         ARABIC BIBLICAL END OF VERSE
+10ED1..10ED8   ; N  # So     [8] ARABIC LIGATURE ALAYHAA AS-SALAATU WAS-SALAAM..ARABIC LIGATURE NAWWARA ALLAAHU MARQADAH
+10ED9..10EEE   ; N  # Lo    [22] ARABIC CROWN LETTER BEH..ARABIC CROWN LETTER YEH
+10EF0..10EFF   ; N  # Mn    [16] ARABIC SMALL LOW UPRIGHT RECTANGULAR ZERO..ARABIC SMALL LOW WORD MADDA
 10F00..10F1C   ; N  # Lo    [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL
 10F1D..10F26   ; N  # No    [10] OLD SOGDIAN NUMBER ONE..OLD SOGDIAN FRACTION ONE HALF
 10F27          ; N  # Lo         OLD SOGDIAN LIGATURE AYIN-DALETH
@@ -2235,6 +2247,13 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 11AB0..11ABF   ; N  # Lo    [16] CANADIAN SYLLABICS NATTILIK HI..CANADIAN SYLLABICS SPA
 11AC0..11AF8   ; N  # Lo    [57] PAU CIN HAU LETTER PA..PAU CIN HAU GLOTTAL STOP FINAL
 11B00..11B09   ; N  # Po    [10] DEVANAGARI HEAD MARK..DEVANAGARI SIGN MINDU
+11B0A          ; N  # Lo         DEVANAGARI LETTER ALTERNATE DDDA
+11B60          ; N  # Mn         SHARADA VOWEL SIGN OE
+11B61          ; N  # Mc         SHARADA VOWEL SIGN OOE
+11B62..11B64   ; N  # Mn     [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E
+11B65          ; N  # Mc         SHARADA VOWEL SIGN SHORT O
+11B66          ; N  # Mn         SHARADA VOWEL SIGN CANDRA E
+11B67          ; N  # Mc         SHARADA VOWEL SIGN CANDRA O
 11BC0..11BE0   ; N  # Lo    [33] SUNUWAR LETTER DEVI..SUNUWAR LETTER KLOKO
 11BE1          ; N  # Po         SUNUWAR SIGN PVO
 11BF0..11BF9   ; N  # Nd    [10] SUNUWAR DIGIT ZERO..SUNUWAR DIGIT NINE
@@ -2279,6 +2298,12 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 11D97          ; N  # Mn         GUNJALA GONDI VIRAMA
 11D98          ; N  # Lo         GUNJALA GONDI OM
 11DA0..11DA9   ; N  # Nd    [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE
+11DB0..11DD8   ; N  # Lo    [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH
+11DD9          ; N  # Lm         TOLONG SIKI SIGN SELA
+11DDA..11DDB   ; N  # Lo     [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA
+11DE0..11DE9   ; N  # Nd    [10] TOLONG SIKI DIGIT ZERO..TOLONG SIKI DIGIT NINE
+11DF0          ; N  # Mn         BENGALI SIGN COMBINING ANUSVARA ABOVE
+11DF1          ; N  # Lo         BENGALI LETTER ALTERNATE BARGIYA BA
 11EE0..11EF2   ; N  # Lo    [19] MAKASAR LETTER KA..MAKASAR ANGKA
 11EF3..11EF4   ; N  # Mn     [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
 11EF5..11EF6   ; N  # Mc     [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
@@ -2304,9 +2329,11 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 11FE1..11FF1   ; N  # So    [17] TAMIL SIGN PAARAM..TAMIL SIGN VAKAIYARAA
 11FFF          ; N  # Po         TAMIL PUNCTUATION END OF TEXT
 12000..12399   ; N  # Lo   [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U
-12400..1246E   ; N  # Nl   [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM
+12400..1246F   ; N  # Nl   [112] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN SEVEN ASH TENU
 12470..12474   ; N  # Po     [5] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON
+12475..1247F   ; N  # Nl    [11] CUNEIFORM NUMERIC SIGN EIGHT ASH TENU..CUNEIFORM NUMERIC SIGN ASH TIMES NINE DISH TENU
 12480..12543   ; N  # Lo   [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU
+12550..12686   ; N  # Nl   [311] CUNEIFORM NUMERIC SIGN ONE N01..CUNEIFORM NUMERIC SIGN ONE N36 FLAT
 12F90..12FF0   ; N  # Lo    [97] CYPRO-MINOAN SIGN CM001..CYPRO-MINOAN SIGN CM114
 12FF1..12FF2   ; N  # Po     [2] CYPRO-MINOAN SIGN CM301..CYPRO-MINOAN SIGN CM302
 13000..1342F   ; N  # Lo  [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D
@@ -2346,9 +2373,16 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 16D6B..16D6C   ; N  # Lm     [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT
 16D6D..16D6F   ; N  # Po     [3] KIRAT RAI SIGN YUPI..KIRAT RAI DOUBLE DANDA
 16D70..16D79   ; N  # Nd    [10] KIRAT RAI DIGIT ZERO..KIRAT RAI DIGIT NINE
+16D80..16D97   ; N  # Lo    [24] CHISOI LETTER A..CHISOI LETTER PA
+16D98          ; N  # Mn         CHISOI SIGN ANUSVARA
+16D99..16D9C   ; N  # Lo     [4] CHISOI LETTER YA..CHISOI LETTER JARAHA
+16D9D          ; N  # Mn         CHISOI SIGN SISO
+16DA0..16DA9   ; N  # Nd    [10] CHISOI DIGIT ZERO..CHISOI DIGIT NINE
 16E40..16E7F   ; N  # L&    [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y
 16E80..16E96   ; N  # No    [23] MEDEFAIDRIN DIGIT ZERO..MEDEFAIDRIN DIGIT THREE ALTERNATE FORM
 16E97..16E9A   ; N  # Po     [4] MEDEFAIDRIN COMMA..MEDEFAIDRIN EXCLAMATION OH
+16EA0..16EB8   ; N  # Lu    [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
+16EBB..16ED3   ; N  # Ll    [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
 16F00..16F4A   ; N  # Lo    [75] MIAO LETTER PA..MIAO LETTER RTE
 16F4F          ; N  # Mn         MIAO SIGN CONSONANT MODIFIER BAR
 16F50          ; N  # Lo         MIAO LETTER NASALIZATION
@@ -2360,20 +2394,25 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 16FE3          ; W  # Lm         OLD CHINESE ITERATION MARK
 16FE4          ; W  # Mn         KHITAN SMALL SCRIPT FILLER
 16FF0..16FF1   ; W  # Mc     [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY
-17000..187F7   ; W  # Lo  [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
+16FF2..16FF3   ; W  # Lm     [2] CHINESE SMALL SIMPLIFIED ER..CHINESE SMALL TRADITIONAL ER
+16FF4..16FF6   ; W  # Nl     [3] YANGQIN SIGN SLOW ONE BEAT..YANGQIN SIGN SLOW TWO BEATS
+17000..187FF   ; W  # Lo  [6144] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187FF
 18800..18AFF   ; W  # Lo   [768] TANGUT COMPONENT-001..TANGUT COMPONENT-768
-18B00..18CD5   ; W  # Lo   [470] KHITAN SMALL SCRIPT CHARACTER-18B00..KHITAN SMALL SCRIPT CHARACTER-18CD5
+18B00..18CDA   ; W  # Lo   [475] KHITAN SMALL SCRIPT CHARACTER-18B00..KHITAN SMALL SCRIPT CHARACTER-18CDA
 18CFF          ; W  # Lo         KHITAN SMALL SCRIPT CHARACTER-18CFF
-18D00..18D08   ; W  # Lo     [9] TANGUT IDEOGRAPH-18D00..TANGUT IDEOGRAPH-18D08
+18D00..18D20   ; W  # Lo    [33] TANGUT IDEOGRAPH-18D00..TANGUT IDEOGRAPH-18D20
+18D80..18DF2   ; W  # Lo   [115] TANGUT COMPONENT-769..TANGUT COMPONENT-883
+18E00..19191   ; W  # Lo   [914] JURCHEN CHARACTER-18E00..JURCHEN CHARACTER-19191
+191A0..191D2   ; W  # Lo    [51] JURCHEN RADICAL-01..JURCHEN RADICAL-51
 1AFF0..1AFF3   ; W  # Lm     [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5
 1AFF5..1AFFB   ; W  # Lm     [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5
 1AFFD..1AFFE   ; W  # Lm     [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8
 1B000..1B0FF   ; W  # Lo   [256] KATAKANA LETTER ARCHAIC E..HENTAIGANA LETTER RE-2
-1B100..1B122   ; W  # Lo    [35] HENTAIGANA LETTER RE-3..KATAKANA LETTER ARCHAIC WU
+1B100..1B128   ; W  # Lo    [41] HENTAIGANA LETTER RE-3..KATAKANA LETTER ALTERNATE WI
 1B132          ; W  # Lo         HIRAGANA LETTER SMALL KO
 1B150..1B152   ; W  # Lo     [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO
 1B155          ; W  # Lo         KATAKANA LETTER SMALL KO
-1B164..1B167   ; W  # Lo     [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N
+1B164..1B168   ; W  # Lo     [5] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL ARCHAIC YE
 1B170..1B2FB   ; W  # Lo   [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
 1BC00..1BC6A   ; N  # Lo   [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M
 1BC70..1BC7C   ; N  # Lo    [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK
@@ -2385,12 +2424,20 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 1BCA0..1BCA3   ; N  # Cf     [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
 1CC00..1CCEF   ; N  # So   [240] UP-POINTING GO-KART..OUTLINED LATIN CAPITAL LETTER Z
 1CCF0..1CCF9   ; N  # Nd    [10] OUTLINED DIGIT ZERO..OUTLINED DIGIT NINE
+1CCFA..1CCFC   ; N  # So     [3] SNAKE SYMBOL..NOSE SYMBOL
 1CD00..1CEB3   ; N  # So   [436] BLOCK OCTANT-3..BLACK RIGHT TRIANGLE CARET
+1CEBA..1CEBF   ; N  # So     [6] FRAGILE SYMBOL..STRAWBERRY SYMBOL
+1CEC0..1CED0   ; N  # So    [17] HEBE..LEUKOTHEA
+1CED2..1CED4   ; N  # So     [3] ALCHEMICAL SYMBOL FOR OIL INVERTED..ALCHEMICAL SYMBOL FOR SALT OF TARTAR
+1CEDD..1CEDF   ; N  # Sm     [3] SQUARE ROOT OF SQUARE ROOT..SQUARE ROOT OF SQUARE ROOT OF SQUARE ROOT OF SQUARE ROOT
+1CEE0..1CEEF   ; N  # So    [16] GEOMANTIC FIGURE POPULUS..GEOMANTIC FIGURE VIA
+1CEF0..1CEFD   ; N  # Sm    [14] MEDIUM SMALL WHITE CIRCLE WITH HORIZONTAL BAR..HYPERBOLA
 1CF00..1CF2D   ; N  # Mn    [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT
 1CF30..1CF46   ; N  # Mn    [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG
 1CF50..1CFC3   ; N  # So   [116] ZNAMENNY NEUME KRYUK..ZNAMENNY NEUME PAUK
 1D000..1D0F5   ; N  # So   [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO
 1D100..1D126   ; N  # So    [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2
+1D127..1D128   ; N  # Mn     [2] MUSICAL SYMBOL COMBINING STRESS..MUSICAL SYMBOL COMBINING UNSTRESS
 1D129..1D164   ; N  # So    [60] MUSICAL SYMBOL MULTIPLE MEASURE REST..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
 1D165..1D166   ; N  # Mc     [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
 1D167..1D169   ; N  # Mn     [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
@@ -2402,10 +2449,17 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 1D185..1D18B   ; N  # Mn     [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
 1D18C..1D1A9   ; N  # So    [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL DEGREE SLASH
 1D1AA..1D1AD   ; N  # Mn     [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
-1D1AE..1D1EA   ; N  # So    [61] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL KORON
+1D1AE..1D1FF   ; N  # So    [82] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL LONGA REST
 1D200..1D241   ; N  # So    [66] GREEK VOCAL NOTATION SYMBOL-1..GREEK INSTRUMENTAL NOTATION SYMBOL-54
 1D242..1D244   ; N  # Mn     [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
 1D245          ; N  # So         GREEK MUSICAL LEIMMA
+1D250..1D252   ; N  # Mc     [3] MUSICAL SYMBOL COMBINING FLAG-6..MUSICAL SYMBOL COMBINING FLAG-8
+1D253..1D25A   ; N  # So     [8] MUSICAL SYMBOL TWO HUNDRED FIFTY-SIXTH REST..MUSICAL SYMBOL SHARP WITH STROKE
+1D25B..1D25C   ; N  # Mn     [2] MUSICAL SYMBOL COMBINING TREMOLO-4..MUSICAL SYMBOL COMBINING TREMOLO-5
+1D25D..1D25E   ; N  # So     [2] MUSICAL SYMBOL FINGERED TREMOLO-4..MUSICAL SYMBOL FINGERED TREMOLO-5
+1D25F          ; N  # Mc         MUSICAL SYMBOL COMBINING BUZZ ROLL STEM
+1D260..1D27F   ; N  # So    [32] MUSICAL SYMBOL TRIPLE FLAT..MUSICAL SYMBOL ARPEGGIATO UP WITH ARROW
+1D280..1D281   ; N  # Mc     [2] MUSICAL SYMBOL COMBINING STEM BOW BEHIND BRIDGE..MUSICAL SYMBOL COMBINING STEM BOW ON TOP OF BRIDGE
 1D2C0..1D2D3   ; N  # No    [20] KAKTOVIK NUMERAL ZERO..KAKTOVIK NUMERAL NINETEEN
 1D2E0..1D2F3   ; N  # No    [20] MAYAN NUMERAL ZERO..MAYAN NUMERAL NINETEEN
 1D300..1D356   ; W  # So    [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING
@@ -2429,7 +2483,7 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 1D540..1D544   ; N  # Lu     [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
 1D546          ; N  # Lu         MATHEMATICAL DOUBLE-STRUCK CAPITAL O
 1D54A..1D550   ; N  # Lu     [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
-1D552..1D6A5   ; N  # L&   [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D552..1D6A6   ; N  # L&   [341] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL LIGATURE LONG S WITH DESCENDER S
 1D6A8..1D6C0   ; N  # Lu    [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
 1D6C1          ; N  # Sm         MATHEMATICAL BOLD NABLA
 1D6C2..1D6DA   ; N  # Ll    [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
@@ -2464,10 +2518,13 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 1DA87..1DA8B   ; N  # Po     [5] SIGNWRITING COMMA..SIGNWRITING PARENTHESIS
 1DA9B..1DA9F   ; N  # Mn     [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6
 1DAA1..1DAAF   ; N  # Mn    [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16
+1DB00..1DB1C   ; N  # Sm    [29] LEIBNIZIAN EQUALS SIGN..FACIT SYMBOL
 1DF00..1DF09   ; N  # Ll    [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
 1DF0A          ; N  # Lo         LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK
-1DF0B..1DF1E   ; N  # Ll    [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL
-1DF25..1DF2A   ; N  # Ll     [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK
+1DF0B..1DF7F   ; N  # L&   [117] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER REVERSED U
+1DF80..1DF81   ; N  # Lo     [2] LATIN CAPITAL LETTER A WITH TOPBAR..LATIN CAPITAL LETTER E WITH BENT TOPBAR
+1DF90..1DF96   ; N  # Ll     [7] LATIN SMALL LETTER C WITH LOW SLASH..LATIN SMALL LETTER LONG S WITH TOP LOOP
+1DFCD..1DFFF   ; N  # Lm    [51] MODIFIER LETTER SMALL TURNED R WITH MID-HEIGHT LEFT HOOK..MODIFIER LETTER SMALL T WITH HOOK AND RETROFLEX HOOK
 1E000..1E006   ; N  # Mn     [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
 1E008..1E018   ; N  # Mn    [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
 1E01B..1E021   ; N  # Mn     [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
@@ -2496,6 +2553,17 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 1E5F0          ; N  # Lo         OL ONAL SIGN HODDOND
 1E5F1..1E5FA   ; N  # Nd    [10] OL ONAL DIGIT ZERO..OL ONAL DIGIT NINE
 1E5FF          ; N  # Po         OL ONAL ABBREVIATION SIGN
+1E6C0..1E6DE   ; N  # Lo    [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO
+1E6E0..1E6E2   ; N  # Lo     [3] TAI YO LETTER AA..TAI YO LETTER UE
+1E6E3          ; N  # Mn         TAI YO SIGN UE
+1E6E4..1E6E5   ; N  # Lo     [2] TAI YO LETTER U..TAI YO LETTER AE
+1E6E6          ; N  # Mn         TAI YO SIGN AU
+1E6E7..1E6ED   ; N  # Lo     [7] TAI YO LETTER O..TAI YO LETTER AUE
+1E6EE..1E6EF   ; N  # Mn     [2] TAI YO SIGN AY..TAI YO SIGN ANG
+1E6F0..1E6F4   ; N  # Lo     [5] TAI YO LETTER AN..TAI YO LETTER AP
+1E6F5          ; N  # Mn         TAI YO SIGN OM
+1E6FE          ; N  # Lo         TAI YO SYMBOL MUEANG
+1E6FF          ; N  # Lm         TAI YO XAM LAI
 1E7E0..1E7E6   ; N  # Lo     [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO
 1E7E8..1E7EB   ; N  # Lo     [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE
 1E7ED..1E7EE   ; N  # Lo     [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE
@@ -2572,6 +2640,7 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 1F191..1F19A   ; W  # So    [10] SQUARED CL..SQUARED VS
 1F19B..1F1AC   ; A  # So    [18] SQUARED THREE D..SQUARED VOD
 1F1AD          ; N  # So         MASK WORK SYMBOL
+1F1AE          ; W  # So         TOMOBIKI SYMBOL
 1F1E6..1F1FF   ; N  # So    [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z
 1F200..1F202   ; W  # So     [3] SQUARE HIRAGANA HOKA..SQUARED KATAKANA SA
 1F210..1F23B   ; W  # So    [44] SQUARED CJK UNIFIED IDEOGRAPH-624B..SQUARED CJK UNIFIED IDEOGRAPH-914D
@@ -2623,17 +2692,19 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 1F6CD..1F6CF   ; N  # So     [3] SHOPPING BAGS..BED
 1F6D0..1F6D2   ; W  # So     [3] PLACE OF WORSHIP..SHOPPING TROLLEY
 1F6D3..1F6D4   ; N  # So     [2] STUPA..PAGODA
-1F6D5..1F6D7   ; W  # So     [3] HINDU TEMPLE..ELEVATOR
+1F6D5..1F6D9   ; W  # So     [5] HINDU TEMPLE..LIGHTHOUSE
 1F6DC..1F6DF   ; W  # So     [4] WIRELESS..RING BUOY
 1F6E0..1F6EA   ; N  # So    [11] HAMMER AND WRENCH..NORTHEAST-POINTING AIRPLANE
 1F6EB..1F6EC   ; W  # So     [2] AIRPLANE DEPARTURE..AIRPLANE ARRIVING
 1F6F0..1F6F3   ; N  # So     [4] SATELLITE..PASSENGER SHIP
 1F6F4..1F6FC   ; W  # So     [9] SCOOTER..ROLLER SKATE
-1F700..1F776   ; N  # So   [119] ALCHEMICAL SYMBOL FOR QUINTESSENCE..LUNAR ECLIPSE
-1F77B..1F77F   ; N  # So     [5] HAUMEA..ORCUS
+1F700..1F77F   ; N  # So   [128] ALCHEMICAL SYMBOL FOR QUINTESSENCE..ORCUS
 1F780..1F7D9   ; N  # So    [90] BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE..NINE POINTED WHITE STAR
+1F7DA          ; W  # So         BLACK CIRCLE WITH WHITE VERTICAL BAR
+1F7DB          ; N  # So         BULLET IN DOUBLE CIRCLE
 1F7E0..1F7EB   ; W  # So    [12] LARGE ORANGE CIRCLE..LARGE BROWN SQUARE
 1F7F0          ; W  # So         HEAVY EQUALS SIGN
+1F7F1..1F7FF   ; N  # So    [15] CIRCLE WITH DOUBLE VERTICAL AND HORIZONTAL LINE..RHOMBUS
 1F800..1F80B   ; N  # So    [12] LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD
 1F810..1F847   ; N  # So    [56] LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD..DOWNWARDS HEAVY ARROW
 1F850..1F859   ; N  # So    [10] LEFTWARDS SANS-SERIF ARROW..UP DOWN SANS-SERIF ARROW
@@ -2641,31 +2712,31 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 1F890..1F8AD   ; N  # So    [30] LEFTWARDS TRIANGLE ARROWHEAD..WHITE ARROW SHAFT WIDTH TWO THIRDS
 1F8B0..1F8BB   ; N  # So    [12] ARROW POINTING UPWARDS THEN NORTH WEST..SOUTH WEST ARROW FROM BAR
 1F8C0..1F8C1   ; N  # So     [2] LEFTWARDS ARROW FROM DOWNWARDS ARROW..RIGHTWARDS ARROW FROM DOWNWARDS ARROW
+1F8D0..1F8D8   ; N  # Sm     [9] LONG RIGHTWARDS ARROW OVER LONG LEFTWARDS ARROW..LONG LEFT RIGHT ARROW WITH DEPENDENT LOBE
 1F900..1F90B   ; N  # So    [12] CIRCLED CROSS FORMEE WITH FOUR DOTS..DOWNWARD FACING NOTCHED HOOK WITH DOT
 1F90C..1F93A   ; W  # So    [47] PINCHED FINGERS..FENCER
 1F93B          ; N  # So         MODERN PENTATHLON
 1F93C..1F945   ; W  # So    [10] WRESTLERS..GOAL NET
 1F946          ; N  # So         RIFLE
 1F947..1F9FF   ; W  # So   [185] FIRST PLACE MEDAL..NAZAR AMULET
-1FA00..1FA53   ; N  # So    [84] NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP
+1FA00..1FA57   ; N  # So    [88] NEUTRAL CHESS KING..BLACK CHESS ALFIL
 1FA60..1FA6D   ; N  # So    [14] XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
 1FA70..1FA7C   ; W  # So    [13] BALLET SHOES..CRUTCH
-1FA80..1FA89   ; W  # So    [10] YO-YO..HARP
-1FA8F..1FAC6   ; W  # So    [56] SHOVEL..FINGERPRINT
-1FACE..1FADC   ; W  # So    [15] MOOSE..ROOT VEGETABLE
-1FADF..1FAE9   ; W  # So    [11] SPLATTER..FACE WITH BAGS UNDER EYES
-1FAF0..1FAF8   ; W  # So     [9] HAND WITH INDEX FINGER AND THUMB CROSSED..RIGHTWARDS PUSHING HAND
+1FA80..1FAC6   ; W  # So    [71] YO-YO..FINGERPRINT
+1FAC8          ; W  # So         HAIRY CREATURE
+1FACC..1FADD   ; W  # So    [18] MONARCH BUTTERFLY..PICKLE
+1FADF..1FAEB   ; W  # So    [13] SPLATTER..CRACKING FACE
+1FAEF..1FAFA   ; W  # So    [12] FIGHT CLOUD..RIGHTWARDS THUMB SIGN
 1FB00..1FB92   ; N  # So   [147] BLOCK SEXTANT-1..UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK
 1FB94..1FBEF   ; N  # So    [92] LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK..TOP LEFT JUSTIFIED LOWER RIGHT QUARTER BLACK CIRCLE
 1FBF0..1FBF9   ; N  # Nd    [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE
+1FBFA          ; N  # So         ALARM BELL SYMBOL
 20000..2A6DF   ; W  # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF
 2A6E0..2A6FF   ; W  # Cn    [32] <reserved-2A6E0>..<reserved-2A6FF>
-2A700..2B739   ; W  # Lo  [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B739
-2B73A..2B73F   ; W  # Cn     [6] <reserved-2B73A>..<reserved-2B73F>
-2B740..2B81D   ; W  # Lo   [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D
-2B81E..2B81F   ; W  # Cn     [2] <reserved-2B81E>..<reserved-2B81F>
-2B820..2CEA1   ; W  # Lo  [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1
-2CEA2..2CEAF   ; W  # Cn    [14] <reserved-2CEA2>..<reserved-2CEAF>
+2A700..2B81E   ; W  # Lo  [4383] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B81E
+2B81F          ; W  # Cn         <reserved-2B81F>
+2B820..2CEAD   ; W  # Lo  [5774] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEAD
+2CEAE..2CEAF   ; W  # Cn     [2] <reserved-2CEAE>..<reserved-2CEAF>
 2CEB0..2EBE0   ; W  # Lo  [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
 2EBE1..2EBEF   ; W  # Cn    [15] <reserved-2EBE1>..<reserved-2EBEF>
 2EBF0..2EE5D   ; W  # Lo   [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D
@@ -2675,8 +2746,10 @@ FFFD           ; A  # So         REPLACEMENT CHARACTER
 2FA20..2FFFD   ; W  # Cn  [1502] <reserved-2FA20>..<reserved-2FFFD>
 30000..3134A   ; W  # Lo  [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A
 3134B..3134F   ; W  # Cn     [5] <reserved-3134B>..<reserved-3134F>
-31350..323AF   ; W  # Lo  [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF
-323B0..3FFFD   ; W  # Cn [56398] <reserved-323B0>..<reserved-3FFFD>
+31350..33479   ; W  # Lo  [8490] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-33479
+3347A..3CFFF   ; W  # Cn [39814] <reserved-3347A>..<reserved-3CFFF>
+3D000..3FC3F   ; W  # Lo [11328] SEAL CHARACTER-3D000..SEAL CHARACTER-3FC3F
+3FC40..3FFFD   ; W  # Cn   [958] <reserved-3FC40>..<reserved-3FFFD>
 E0001          ; N  # Cf         LANGUAGE TAG
 E0020..E007F   ; N  # Cf    [96] TAG SPACE..CANCEL TAG
 E0100..E01EF   ; A  # Mn   [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
diff --git a/libcxx/utils/data/unicode/GraphemeBreakProperty.txt b/libcxx/utils/data/unicode/GraphemeBreakProperty.txt
index a863397ddabaf..5237062063389 100644
--- a/libcxx/utils/data/unicode/GraphemeBreakProperty.txt
+++ b/libcxx/utils/data/unicode/GraphemeBreakProperty.txt
@@ -1,6 +1,6 @@
-# GraphemeBreakProperty-16.0.0.txt
-# Date: 2024-05-31, 18:09:38 GMT
-# © 2024 Unicode®, Inc.
+# GraphemeBreakProperty-18.0.0.txt
+# Date: 2026-04-29, 12:18:11 GMT
+# © 2026 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
 # For terms of use and license, see https://www.unicode.org/terms_of_use.html
 #
@@ -30,12 +30,11 @@
 113D1         ; Prepend # Lo       TULU-TIGALARI REPHA
 1193F         ; Prepend # Lo       DIVES AKURU PREFIXED NASAL SIGN
 11941         ; Prepend # Lo       DIVES AKURU INITIAL RA
-11A3A         ; Prepend # Lo       ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA
 11A84..11A89  ; Prepend # Lo   [6] SOYOMBO SIGN JIHVAMULIYA..SOYOMBO CLUSTER-INITIAL LETTER SA
 11D46         ; Prepend # Lo       MASARAM GONDI REPHA
 11F02         ; Prepend # Lo       KAWI SIGN REPHA
 
-# Total code points: 28
+# Total code points: 27
 
 # ================================================
 
@@ -89,7 +88,7 @@ E01F0..E0FFF  ; Control # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
 05BF          ; Extend # Mn       HEBREW POINT RAFE
 05C1..05C2    ; Extend # Mn   [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
 05C4..05C5    ; Extend # Mn   [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
-05C7          ; Extend # Mn       HEBREW POINT QAMATS QATAN
+05C7..05C9    ; Extend # Mn   [3] HEBREW POINT QAMATS QATAN..HEBREW POINT DAGESH HAZAQ MUDGASH
 0610..061A    ; Extend # Mn  [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
 064B..065F    ; Extend # Mn  [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW
 0670          ; Extend # Mn       ARABIC LETTER SUPERSCRIPT ALEF
@@ -145,7 +144,7 @@ E01F0..E0FFF  ; Control # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
 0B3F          ; Extend # Mn       ORIYA VOWEL SIGN I
 0B41..0B44    ; Extend # Mn   [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR
 0B4D          ; Extend # Mn       ORIYA SIGN VIRAMA
-0B55..0B56    ; Extend # Mn   [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK
+0B53..0B56    ; Extend # Mn   [4] ORIYA SIGN DOT ABOVE..ORIYA AI LENGTH MARK
 0B57          ; Extend # Mc       ORIYA AU LENGTH MARK
 0B62..0B63    ; Extend # Mn   [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL
 0B82          ; Extend # Mn       TAMIL SIGN ANUSVARA
@@ -243,7 +242,7 @@ E01F0..E0FFF  ; Control # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
 1A7F          ; Extend # Mn       TAI THAM COMBINING CRYPTOGRAMMIC DOT
 1AB0..1ABD    ; Extend # Mn  [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
 1ABE          ; Extend # Me       COMBINING PARENTHESES OVERLAY
-1ABF..1ACE    ; Extend # Mn  [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T
+1ABF..1AF0    ; Extend # Mn  [50] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOUBLE COMMA ABOVE
 1B00..1B03    ; Extend # Mn   [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
 1B34          ; Extend # Mn       BALINESE SIGN REREKAN
 1B35          ; Extend # Mc       BALINESE VOWEL SIGN TEDUNG
@@ -339,7 +338,8 @@ FF9E..FF9F    ; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
 10D24..10D27  ; Extend # Mn   [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI
 10D69..10D6D  ; Extend # Mn   [5] GARAY VOWEL SIGN E..GARAY CONSONANT NASALIZATION MARK
 10EAB..10EAC  ; Extend # Mn   [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
-10EFC..10EFF  ; Extend # Mn   [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA
+10ECB..10ECF  ; Extend # Mn   [5] ARABIC NORTHEAST POINTING ARROWHEAD ABOVE..ARABIC LARGE CIRCLE ABOVE
+10EF0..10EFF  ; Extend # Mn  [16] ARABIC SMALL LOW UPRIGHT RECTANGULAR ZERO..ARABIC SMALL LOW WORD MADDA
 10F46..10F50  ; Extend # Mn  [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW
 10F82..10F85  ; Extend # Mn   [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW
 11001         ; Extend # Mn       BRAHMI SIGN ANUSVARA
@@ -430,6 +430,9 @@ FF9E..FF9F    ; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
 11A59..11A5B  ; Extend # Mn   [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK
 11A8A..11A96  ; Extend # Mn  [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA
 11A98..11A99  ; Extend # Mn   [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER
+11B60         ; Extend # Mn       SHARADA VOWEL SIGN OE
+11B62..11B64  ; Extend # Mn   [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E
+11B66         ; Extend # Mn       SHARADA VOWEL SIGN CANDRA E
 11C30..11C36  ; Extend # Mn   [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L
 11C38..11C3D  ; Extend # Mn   [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA
 11C3F         ; Extend # Mn       BHAIKSUKI SIGN VIRAMA
@@ -445,6 +448,7 @@ FF9E..FF9F    ; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
 11D90..11D91  ; Extend # Mn   [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI
 11D95         ; Extend # Mn       GUNJALA GONDI SIGN ANUSVARA
 11D97         ; Extend # Mn       GUNJALA GONDI VIRAMA
+11DF0         ; Extend # Mn       BENGALI SIGN COMBINING ANUSVARA ABOVE
 11EF3..11EF4  ; Extend # Mn   [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
 11F00..11F01  ; Extend # Mn   [2] KAWI SIGN CANDRABINDU..KAWI SIGN ANUSVARA
 11F36..11F3A  ; Extend # Mn   [5] KAWI VOWEL SIGN I..KAWI VOWEL SIGN VOCALIC R
@@ -458,6 +462,8 @@ FF9E..FF9F    ; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
 1612D..1612F  ; Extend # Mn   [3] GURUNG KHEMA SIGN ANUSVARA..GURUNG KHEMA SIGN THOLHOMA
 16AF0..16AF4  ; Extend # Mn   [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
 16B30..16B36  ; Extend # Mn   [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
+16D98         ; Extend # Mn       CHISOI SIGN ANUSVARA
+16D9D         ; Extend # Mn       CHISOI SIGN SISO
 16F4F         ; Extend # Mn       MIAO SIGN CONSONANT MODIFIER BAR
 16F8F..16F92  ; Extend # Mn   [4] MIAO TONE RIGHT..MIAO TONE BELOW
 16FE4         ; Extend # Mn       KHITAN SMALL SCRIPT FILLER
@@ -465,6 +471,7 @@ FF9E..FF9F    ; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
 1BC9D..1BC9E  ; Extend # Mn   [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK
 1CF00..1CF2D  ; Extend # Mn  [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT
 1CF30..1CF46  ; Extend # Mn  [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG
+1D127..1D128  ; Extend # Mn   [2] MUSICAL SYMBOL COMBINING STRESS..MUSICAL SYMBOL COMBINING UNSTRESS
 1D165..1D166  ; Extend # Mc   [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
 1D167..1D169  ; Extend # Mn   [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
 1D16D..1D172  ; Extend # Mc   [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5
@@ -472,6 +479,10 @@ FF9E..FF9F    ; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
 1D185..1D18B  ; Extend # Mn   [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
 1D1AA..1D1AD  ; Extend # Mn   [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
 1D242..1D244  ; Extend # Mn   [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+1D250..1D252  ; Extend # Mc   [3] MUSICAL SYMBOL COMBINING FLAG-6..MUSICAL SYMBOL COMBINING FLAG-8
+1D25B..1D25C  ; Extend # Mn   [2] MUSICAL SYMBOL COMBINING TREMOLO-4..MUSICAL SYMBOL COMBINING TREMOLO-5
+1D25F         ; Extend # Mc       MUSICAL SYMBOL COMBINING BUZZ ROLL STEM
+1D280..1D281  ; Extend # Mc   [2] MUSICAL SYMBOL COMBINING STEM BOW BEHIND BRIDGE..MUSICAL SYMBOL COMBINING STEM BOW ON TOP OF BRIDGE
 1DA00..1DA36  ; Extend # Mn  [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN
 1DA3B..1DA6C  ; Extend # Mn  [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT
 1DA75         ; Extend # Mn       SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS
@@ -489,13 +500,17 @@ FF9E..FF9F    ; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
 1E2EC..1E2EF  ; Extend # Mn   [4] WANCHO TONE TUP..WANCHO TONE KOINI
 1E4EC..1E4EF  ; Extend # Mn   [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH
 1E5EE..1E5EF  ; Extend # Mn   [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR
+1E6E3         ; Extend # Mn       TAI YO SIGN UE
+1E6E6         ; Extend # Mn       TAI YO SIGN AU
+1E6EE..1E6EF  ; Extend # Mn   [2] TAI YO SIGN AY..TAI YO SIGN ANG
+1E6F5         ; Extend # Mn       TAI YO SIGN OM
 1E8D0..1E8D6  ; Extend # Mn   [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
 1E944..1E94A  ; Extend # Mn   [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
 1F3FB..1F3FF  ; Extend # Sk   [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6
 E0020..E007F  ; Extend # Cf  [96] TAG SPACE..CANCEL TAG
 E0100..E01EF  ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
 
-# Total code points: 2198
+# Total code points: 2276
 
 # ================================================
 
@@ -646,6 +661,9 @@ ABEC          ; SpacingMark # Mc       MEETEI MAYEK LUM IYEK
 11A39         ; SpacingMark # Mc       ZANABAZAR SQUARE SIGN VISARGA
 11A57..11A58  ; SpacingMark # Mc   [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU
 11A97         ; SpacingMark # Mc       SOYOMBO SIGN VISARGA
+11B61         ; SpacingMark # Mc       SHARADA VOWEL SIGN OOE
+11B65         ; SpacingMark # Mc       SHARADA VOWEL SIGN SHORT O
+11B67         ; SpacingMark # Mc       SHARADA VOWEL SIGN CANDRA O
 11C2F         ; SpacingMark # Mc       BHAIKSUKI VOWEL SIGN AA
 11C3E         ; SpacingMark # Mc       BHAIKSUKI SIGN VISARGA
 11CA9         ; SpacingMark # Mc       MARCHEN SUBJOINED LETTER YA
@@ -661,7 +679,7 @@ ABEC          ; SpacingMark # Mc       MEETEI MAYEK LUM IYEK
 1612A..1612C  ; SpacingMark # Mc   [3] GURUNG KHEMA CONSONANT SIGN MEDIAL YA..GURUNG KHEMA CONSONANT SIGN MEDIAL HA
 16F51..16F87  ; SpacingMark # Mc  [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
 
-# Total code points: 378
+# Total code points: 381
 
 # ================================================
 
diff --git a/libcxx/utils/data/unicode/GraphemeBreakTest.txt b/libcxx/utils/data/unicode/GraphemeBreakTest.txt
index d10c174b6896d..f19fee32b3651 100644
--- a/libcxx/utils/data/unicode/GraphemeBreakTest.txt
+++ b/libcxx/utils/data/unicode/GraphemeBreakTest.txt
@@ -1,6 +1,6 @@
-# GraphemeBreakTest-16.0.0.txt
-# Date: 2024-05-02, 15:02:48 GMT
-# © 2024 Unicode®, Inc.
+# GraphemeBreakTest-18.0.0.txt
+# Date: 2026-05-12, 22:11:40 GMT
+# © 2026 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
 # For terms of use and license, see https://www.unicode.org/terms_of_use.html
 #
@@ -16,1106 +16,868 @@
 #	× wherever there is not.
 #  <comment> the format can change, but currently it shows:
 #	- the sample character name
-#	- (x) the Grapheme_Cluster_Break property value for the sample character
+#	- (x) the Grapheme_Cluster_Break property value for the sample character and 
+#	  any other properties relevant to the algorithm, as described in 
+#	  GraphemeBreakTest.html
 #	- [x] the rule that determines whether there is a break or not,
 #	   as listed in the Rules section of GraphemeBreakTest.html
 #
 # These samples may be extended or changed in the future.
 #
-÷ 0020 ÷ 0020 ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0020 × 0308 ÷ 0020 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0020 ÷ 000D ÷	#  ÷ [0.2] SPACE (Other) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0020 × 0308 ÷ 000D ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0020 ÷ 000A ÷	#  ÷ [0.2] SPACE (Other) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0020 × 0308 ÷ 000A ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0020 ÷ 0001 ÷	#  ÷ [0.2] SPACE (Other) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0020 × 0308 ÷ 0001 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0020 × 200C ÷	#  ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0020 × 0308 × 200C ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0020 ÷ 1F1E6 ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0020 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0020 ÷ 0600 ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0020 × 0308 ÷ 0600 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0020 × 0A03 ÷	#  ÷ [0.2] SPACE (Other) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0020 × 0308 × 0A03 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0020 ÷ 1100 ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0020 × 0308 ÷ 1100 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0020 ÷ 1160 ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0020 × 0308 ÷ 1160 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0020 ÷ 11A8 ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0020 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0020 ÷ AC00 ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0020 × 0308 ÷ AC00 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0020 ÷ AC01 ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0020 × 0308 ÷ AC01 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0020 × 0903 ÷	#  ÷ [0.2] SPACE (Other) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0020 × 0308 × 0903 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0020 ÷ 0904 ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0020 × 0308 ÷ 0904 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0020 ÷ 0D4E ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0020 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0020 ÷ 0915 ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0020 × 0308 ÷ 0915 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0020 ÷ 231A ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0020 × 0308 ÷ 231A ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0020 × 0300 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0020 × 0308 × 0300 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0020 × 0900 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0020 × 0308 × 0900 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0020 × 094D ÷	#  ÷ [0.2] SPACE (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0020 × 0308 × 094D ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0020 × 200D ÷	#  ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0020 × 0308 × 200D ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0020 ÷ 0378 ÷	#  ÷ [0.2] SPACE (Other) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0020 × 0308 ÷ 0378 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 000D ÷ 0020 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] SPACE (Other) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 0020 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
 ÷ 000D ÷ 000D ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 000D ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 000D ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
 ÷ 000D × 000A ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) × [3.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 000A ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 000D ÷ 0001 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 0001 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 000D ÷ 200C ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 000D ÷ 0308 × 200C ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 000A ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 000D ÷ 0000 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <NULL> (Control) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 0000 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 000D ÷ 094D ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 000D ÷ 0308 × 094D ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 000D ÷ 0300 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 000D ÷ 0308 × 0300 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 000D ÷ 200C ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 000D ÷ 0308 × 200C ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 000D ÷ 200D ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 000D ÷ 0308 × 200D ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
 ÷ 000D ÷ 1F1E6 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 000D ÷ 0600 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 0600 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 000D ÷ 0A03 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 000D ÷ 0308 × 0A03 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 000D ÷ 06DD ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 06DD ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 000D ÷ 0903 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 000D ÷ 0308 × 0903 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
 ÷ 000D ÷ 1100 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 1100 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 1100 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
 ÷ 000D ÷ 1160 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 1160 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 1160 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
 ÷ 000D ÷ 11A8 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 11A8 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 11A8 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
 ÷ 000D ÷ AC00 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ AC00 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ AC00 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
 ÷ 000D ÷ AC01 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ AC01 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 000D ÷ 0903 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 000D ÷ 0308 × 0903 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 000D ÷ 0904 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 0904 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 000D ÷ 0D4E ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 0D4E ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 000D ÷ 0915 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 0915 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 000D ÷ 231A ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] WATCH (ExtPict) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 231A ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 000D ÷ 0300 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 000D ÷ 0308 × 0300 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 000D ÷ 0900 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 000D ÷ 0308 × 0900 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 000D ÷ 094D ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 000D ÷ 0308 × 094D ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 000D ÷ 200D ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 000D ÷ 0308 × 200D ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 000D ÷ 0378 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ 0378 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 000A ÷ 0020 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] SPACE (Other) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 0020 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ AC01 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 000D ÷ 1CF5 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 1CF5 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 000D ÷ 0915 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 0915 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 000D ÷ 00A9 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 00A9 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 000D ÷ 0020 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 0020 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 000D ÷ 0378 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 000D ÷ 0308 ÷ 0378 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
 ÷ 000A ÷ 000D ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 000D ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 000D ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
 ÷ 000A ÷ 000A ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 000A ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 000A ÷ 0001 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 0001 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 000A ÷ 200C ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 000A ÷ 0308 × 200C ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 000A ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 000A ÷ 0000 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <NULL> (Control) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 0000 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 000A ÷ 094D ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 000A ÷ 0308 × 094D ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 000A ÷ 0300 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 000A ÷ 0308 × 0300 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 000A ÷ 200C ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 000A ÷ 0308 × 200C ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 000A ÷ 200D ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 000A ÷ 0308 × 200D ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
 ÷ 000A ÷ 1F1E6 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 000A ÷ 0600 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 0600 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 000A ÷ 0A03 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 000A ÷ 0308 × 0A03 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 000A ÷ 06DD ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 06DD ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 000A ÷ 0903 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 000A ÷ 0308 × 0903 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
 ÷ 000A ÷ 1100 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 1100 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 1100 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
 ÷ 000A ÷ 1160 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 1160 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 1160 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
 ÷ 000A ÷ 11A8 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 11A8 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 11A8 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
 ÷ 000A ÷ AC00 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ AC00 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ AC00 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
 ÷ 000A ÷ AC01 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ AC01 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 000A ÷ 0903 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 000A ÷ 0308 × 0903 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 000A ÷ 0904 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 0904 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 000A ÷ 0D4E ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 0D4E ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 000A ÷ 0915 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 0915 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 000A ÷ 231A ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] WATCH (ExtPict) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 231A ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 000A ÷ 0300 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 000A ÷ 0308 × 0300 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 000A ÷ 0900 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 000A ÷ 0308 × 0900 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 000A ÷ 094D ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 000A ÷ 0308 × 094D ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 000A ÷ 200D ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 000A ÷ 0308 × 200D ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 000A ÷ 0378 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ 0378 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0001 ÷ 0020 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] SPACE (Other) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 0020 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0001 ÷ 000D ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 000D ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0001 ÷ 000A ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 000A ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0001 ÷ 0001 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 0001 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0001 ÷ 200C ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0001 ÷ 0308 × 200C ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0001 ÷ 1F1E6 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0001 ÷ 0600 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 0600 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0001 ÷ 0A03 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0001 ÷ 0308 × 0A03 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0001 ÷ 1100 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 1100 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0001 ÷ 1160 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 1160 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0001 ÷ 11A8 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 11A8 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0001 ÷ AC00 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ AC00 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0001 ÷ AC01 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ AC01 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0001 ÷ 0903 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0001 ÷ 0308 × 0903 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0001 ÷ 0904 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 0904 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0001 ÷ 0D4E ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 0D4E ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0001 ÷ 0915 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 0915 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0001 ÷ 231A ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 231A ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0001 ÷ 0300 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0001 ÷ 0308 × 0300 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0001 ÷ 0900 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0001 ÷ 0308 × 0900 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0001 ÷ 094D ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0001 ÷ 0308 × 094D ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0001 ÷ 200D ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0001 ÷ 0308 × 200D ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0001 ÷ 0378 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ 0378 ÷	#  ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 200C ÷ 0020 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 200C × 0308 ÷ 0020 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 200C ÷ 000D ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 200C × 0308 ÷ 000D ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 200C ÷ 000A ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 200C × 0308 ÷ 000A ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 200C ÷ 0001 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 200C × 0308 ÷ 0001 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 200C × 200C ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 200C × 0308 × 200C ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 200C ÷ 1F1E6 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 200C × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 200C ÷ 0600 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 200C × 0308 ÷ 0600 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 200C × 0A03 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 200C × 0308 × 0A03 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 200C ÷ 1100 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 200C × 0308 ÷ 1100 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 200C ÷ 1160 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 200C × 0308 ÷ 1160 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 200C ÷ 11A8 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 200C × 0308 ÷ 11A8 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 200C ÷ AC00 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 200C × 0308 ÷ AC00 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 200C ÷ AC01 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 200C × 0308 ÷ AC01 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 200C × 0903 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 200C × 0308 × 0903 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 200C ÷ 0904 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 200C × 0308 ÷ 0904 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 200C ÷ 0D4E ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 200C × 0308 ÷ 0D4E ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 200C ÷ 0915 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 200C × 0308 ÷ 0915 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 200C ÷ 231A ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 200C × 0308 ÷ 231A ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 200C × 0300 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 200C × 0308 × 0300 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 200C × 0900 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 200C × 0308 × 0900 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 200C × 094D ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 200C × 0308 × 094D ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 200C × 200D ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 200C × 0308 × 200D ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 200C ÷ 0378 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 200C × 0308 ÷ 0378 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1F1E6 ÷ 0020 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 0020 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ AC01 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 000A ÷ 1CF5 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 1CF5 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 000A ÷ 0915 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 0915 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 000A ÷ 00A9 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 00A9 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 000A ÷ 0020 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 0020 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 000A ÷ 0378 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 000A ÷ 0308 ÷ 0378 ÷	#  ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0000 ÷ 000D ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 000D ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 0000 ÷ 000A ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 000A ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 0000 ÷ 0000 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] <NULL> (Control) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 0000 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 0000 ÷ 094D ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 0000 ÷ 0308 × 094D ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 0000 ÷ 0300 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0000 ÷ 0308 × 0300 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0000 ÷ 200C ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 0000 ÷ 0308 × 200C ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 0000 ÷ 200D ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0000 ÷ 0308 × 200D ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0000 ÷ 1F1E6 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 0000 ÷ 06DD ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 06DD ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 0000 ÷ 0903 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 0000 ÷ 0308 × 0903 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 0000 ÷ 1100 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 1100 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 0000 ÷ 1160 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 1160 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 0000 ÷ 11A8 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 11A8 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 0000 ÷ AC00 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ AC00 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 0000 ÷ AC01 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ AC01 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 0000 ÷ 1CF5 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 1CF5 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 0000 ÷ 0915 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 0915 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 0000 ÷ 00A9 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 00A9 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 0000 ÷ 0020 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 0020 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0000 ÷ 0378 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0000 ÷ 0308 ÷ 0378 ÷	#  ÷ [0.2] <NULL> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 094D ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 094D × 0308 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 094D ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 094D × 0308 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 094D ÷ 0000 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 094D × 0308 ÷ 0000 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 094D × 094D ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 094D × 0308 × 094D ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 094D × 0300 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 094D × 0308 × 0300 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 094D × 200C ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 094D × 0308 × 200C ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 094D × 200D ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 094D × 0308 × 200D ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 094D ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 094D × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 094D ÷ 06DD ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 094D × 0308 ÷ 06DD ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 094D × 0903 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 094D × 0308 × 0903 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 094D ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 094D × 0308 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 094D ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 094D × 0308 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 094D ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 094D × 0308 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 094D ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 094D × 0308 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 094D ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 094D × 0308 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 094D ÷ 1CF5 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 094D × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 094D × 0915 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 094D × 0308 × 0915 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.3] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 094D ÷ 00A9 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 094D × 0308 ÷ 00A9 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 094D ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 094D × 0308 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 094D ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 094D × 0308 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0300 ÷ 000D ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 0300 × 0308 ÷ 000D ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 0300 ÷ 000A ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 0300 × 0308 ÷ 000A ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 0300 ÷ 0000 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 0300 × 0308 ÷ 0000 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 0300 × 094D ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 0300 × 0308 × 094D ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 0300 × 0300 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0300 × 0308 × 0300 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0300 × 200C ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 0300 × 0308 × 200C ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 0300 × 200D ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0300 × 0308 × 200D ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0300 ÷ 1F1E6 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 0300 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 0300 ÷ 06DD ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 0300 × 0308 ÷ 06DD ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 0300 × 0903 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 0300 × 0308 × 0903 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 0300 ÷ 1100 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 0300 × 0308 ÷ 1100 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 0300 ÷ 1160 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 0300 × 0308 ÷ 1160 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 0300 ÷ 11A8 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 0300 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 0300 ÷ AC00 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 0300 × 0308 ÷ AC00 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 0300 ÷ AC01 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 0300 × 0308 ÷ AC01 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 0300 ÷ 1CF5 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 0300 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 0300 ÷ 0915 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 0300 × 0308 ÷ 0915 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 0300 ÷ 00A9 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 0300 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 0300 ÷ 0020 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0300 × 0308 ÷ 0020 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0300 ÷ 0378 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0300 × 0308 ÷ 0378 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 200C ÷ 000D ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 200C × 0308 ÷ 000D ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 200C ÷ 000A ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 200C × 0308 ÷ 000A ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 200C ÷ 0000 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 200C × 0308 ÷ 0000 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 200C × 094D ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 200C × 0308 × 094D ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 200C × 0300 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 200C × 0308 × 0300 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 200C × 200C ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 200C × 0308 × 200C ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 200C × 200D ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 200C × 0308 × 200D ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 200C ÷ 1F1E6 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 200C × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 200C ÷ 06DD ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 200C × 0308 ÷ 06DD ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 200C × 0903 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 200C × 0308 × 0903 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 200C ÷ 1100 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 200C × 0308 ÷ 1100 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 200C ÷ 1160 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 200C × 0308 ÷ 1160 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 200C ÷ 11A8 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 200C × 0308 ÷ 11A8 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 200C ÷ AC00 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 200C × 0308 ÷ AC00 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 200C ÷ AC01 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 200C × 0308 ÷ AC01 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 200C ÷ 1CF5 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 200C × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 200C ÷ 0915 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 200C × 0308 ÷ 0915 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 200C ÷ 00A9 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 200C × 0308 ÷ 00A9 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 200C ÷ 0020 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 200C × 0308 ÷ 0020 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 200C ÷ 0378 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 200C × 0308 ÷ 0378 ÷	#  ÷ [0.2] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 200D ÷ 000D ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 200D × 0308 ÷ 000D ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 200D ÷ 000A ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 200D × 0308 ÷ 000A ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 200D ÷ 0000 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 200D × 0308 ÷ 0000 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 200D × 094D ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 200D × 0308 × 094D ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 200D × 0300 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 200D × 0308 × 0300 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 200D × 200C ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 200D × 0308 × 200C ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 200D × 200D ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 200D × 0308 × 200D ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 200D ÷ 1F1E6 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 200D × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 200D ÷ 06DD ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 200D × 0308 ÷ 06DD ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 200D × 0903 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 200D × 0308 × 0903 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 200D ÷ 1100 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 200D × 0308 ÷ 1100 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 200D ÷ 1160 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 200D × 0308 ÷ 1160 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 200D ÷ 11A8 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 200D × 0308 ÷ 11A8 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 200D ÷ AC00 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 200D × 0308 ÷ AC00 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 200D ÷ AC01 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 200D × 0308 ÷ AC01 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 200D ÷ 1CF5 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 200D × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 200D ÷ 0915 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 200D × 0308 ÷ 0915 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 200D ÷ 00A9 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 200D × 0308 ÷ 00A9 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 200D ÷ 0020 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 200D × 0308 ÷ 0020 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 200D ÷ 0378 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 200D × 0308 ÷ 0378 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
 ÷ 1F1E6 ÷ 000D ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 000D ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 000D ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
 ÷ 1F1E6 ÷ 000A ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 000A ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 1F1E6 ÷ 0001 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 0001 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 1F1E6 × 200C ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 1F1E6 × 0308 × 200C ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 000A ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 1F1E6 ÷ 0000 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 0000 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 1F1E6 × 094D ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 1F1E6 × 0308 × 094D ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 1F1E6 × 0300 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1F1E6 × 0308 × 0300 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1F1E6 × 200C ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 1F1E6 × 0308 × 200C ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 1F1E6 × 200D ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 1F1E6 × 0308 × 200D ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
 ÷ 1F1E6 × 1F1E6 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 1F1E6 ÷ 0600 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 0600 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 1F1E6 × 0A03 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 1F1E6 × 0308 × 0A03 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 1F1E6 ÷ 06DD ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 06DD ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 1F1E6 × 0903 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 1F1E6 × 0308 × 0903 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
 ÷ 1F1E6 ÷ 1100 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 1100 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 1100 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
 ÷ 1F1E6 ÷ 1160 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 1160 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 1160 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
 ÷ 1F1E6 ÷ 11A8 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
 ÷ 1F1E6 ÷ AC00 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ AC00 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ AC00 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
 ÷ 1F1E6 ÷ AC01 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ AC01 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 1F1E6 × 0903 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 1F1E6 × 0308 × 0903 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 1F1E6 ÷ 0904 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 0904 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 1F1E6 ÷ 0D4E ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 1F1E6 ÷ 0915 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 0915 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 1F1E6 ÷ 231A ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 231A ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 1F1E6 × 0300 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 1F1E6 × 0308 × 0300 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 1F1E6 × 0900 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 1F1E6 × 0308 × 0900 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 1F1E6 × 094D ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 1F1E6 × 0308 × 094D ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 1F1E6 × 200D ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 1F1E6 × 0308 × 200D ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 1F1E6 ÷ 0378 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ 0378 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0600 × 0020 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] SPACE (Other) ÷ [0.3]
-÷ 0600 × 0308 ÷ 0020 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0600 ÷ 000D ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0600 × 0308 ÷ 000D ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0600 ÷ 000A ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0600 × 0308 ÷ 000A ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0600 ÷ 0001 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0600 × 0308 ÷ 0001 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0600 × 200C ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0600 × 0308 × 200C ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0600 × 1F1E6 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0600 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0600 × 0600 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0600 × 0308 ÷ 0600 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0600 × 0A03 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0600 × 0308 × 0A03 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0600 × 1100 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0600 × 0308 ÷ 1100 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0600 × 1160 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0600 × 0308 ÷ 1160 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0600 × 11A8 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0600 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0600 × AC00 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0600 × 0308 ÷ AC00 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0600 × AC01 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0600 × 0308 ÷ AC01 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0600 × 0903 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0600 × 0308 × 0903 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0600 × 0904 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0600 × 0308 ÷ 0904 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0600 × 0D4E ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0600 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0600 × 0915 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0600 × 0308 ÷ 0915 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0600 × 231A ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] WATCH (ExtPict) ÷ [0.3]
-÷ 0600 × 0308 ÷ 231A ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0600 × 0300 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0600 × 0308 × 0300 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0600 × 0900 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0600 × 0308 × 0900 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0600 × 094D ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0600 × 0308 × 094D ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0600 × 200D ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0600 × 0308 × 200D ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0600 × 0378 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] <reserved-0378> (Other) ÷ [0.3]
-÷ 0600 × 0308 ÷ 0378 ÷	#  ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0A03 ÷ 0020 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 0020 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0A03 ÷ 000D ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 000D ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0A03 ÷ 000A ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 000A ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0A03 ÷ 0001 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 0001 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0A03 × 200C ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0A03 × 0308 × 200C ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0A03 ÷ 1F1E6 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0A03 ÷ 0600 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 0600 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0A03 × 0A03 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0A03 × 0308 × 0A03 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0A03 ÷ 1100 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 1100 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0A03 ÷ 1160 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 1160 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0A03 ÷ 11A8 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0A03 ÷ AC00 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0A03 × 0308 ÷ AC00 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0A03 ÷ AC01 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0A03 × 0308 ÷ AC01 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0A03 × 0903 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0A03 × 0308 × 0903 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0A03 ÷ 0904 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 0904 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0A03 ÷ 0D4E ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0A03 ÷ 0915 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 0915 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0A03 ÷ 231A ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 231A ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0A03 × 0300 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0A03 × 0308 × 0300 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0A03 × 0900 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0A03 × 0308 × 0900 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0A03 × 094D ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0A03 × 0308 × 094D ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0A03 × 200D ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0A03 × 0308 × 200D ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0A03 ÷ 0378 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0A03 × 0308 ÷ 0378 ÷	#  ÷ [0.2] GURMUKHI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1100 ÷ 0020 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 1100 × 0308 ÷ 0020 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ AC01 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 1F1E6 ÷ 1CF5 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 1F1E6 ÷ 0915 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 0915 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 1F1E6 ÷ 00A9 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 1F1E6 ÷ 0020 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 0020 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1F1E6 ÷ 0378 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1F1E6 × 0308 ÷ 0378 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 06DD ÷ 000D ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 06DD × 0308 ÷ 000D ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 06DD ÷ 000A ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 06DD × 0308 ÷ 000A ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 06DD ÷ 0000 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 06DD × 0308 ÷ 0000 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 06DD × 094D ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 06DD × 0308 × 094D ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 06DD × 0300 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 06DD × 0308 × 0300 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 06DD × 200C ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 06DD × 0308 × 200C ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 06DD × 200D ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 06DD × 0308 × 200D ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 06DD × 1F1E6 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 06DD × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 06DD × 06DD ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 06DD × 0308 ÷ 06DD ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 06DD × 0903 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 06DD × 0308 × 0903 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 06DD × 1100 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 06DD × 0308 ÷ 1100 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 06DD × 1160 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 06DD × 0308 ÷ 1160 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 06DD × 11A8 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 06DD × 0308 ÷ 11A8 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 06DD × AC00 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 06DD × 0308 ÷ AC00 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 06DD × AC01 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 06DD × 0308 ÷ AC01 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 06DD × 1CF5 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 06DD × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 06DD × 0915 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 06DD × 0308 ÷ 0915 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 06DD × 00A9 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 06DD × 0308 ÷ 00A9 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 06DD × 0020 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 06DD × 0308 ÷ 0020 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 06DD × 0378 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 06DD × 0308 ÷ 0378 ÷	#  ÷ [0.2] ARABIC END OF AYAH (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0903 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 0903 × 0308 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 0903 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 0903 × 0308 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 0903 ÷ 0000 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 0903 × 0308 ÷ 0000 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 0903 × 094D ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 0903 × 0308 × 094D ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 0903 × 0300 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0903 × 0308 × 0300 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0903 × 200C ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 0903 × 0308 × 200C ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 0903 × 200D ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0903 × 0308 × 200D ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0903 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 0903 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 0903 ÷ 06DD ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 0903 × 0308 ÷ 06DD ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 0903 × 0903 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 0903 × 0308 × 0903 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 0903 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 0903 × 0308 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 0903 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 0903 × 0308 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 0903 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 0903 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 0903 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 0903 × 0308 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 0903 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 0903 × 0308 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 0903 ÷ 1CF5 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 0903 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 0903 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 0903 × 0308 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 0903 ÷ 00A9 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 0903 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 0903 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0903 × 0308 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0903 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0903 × 0308 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
 ÷ 1100 ÷ 000D ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 1100 × 0308 ÷ 000D ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 1100 × 0308 ÷ 000D ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
 ÷ 1100 ÷ 000A ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 1100 × 0308 ÷ 000A ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 1100 ÷ 0001 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 1100 × 0308 ÷ 0001 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 1100 × 200C ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 1100 × 0308 × 200C ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
+÷ 1100 × 0308 ÷ 000A ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 1100 ÷ 0000 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 1100 × 0308 ÷ 0000 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 1100 × 094D ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 1100 × 0308 × 094D ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 1100 × 0300 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1100 × 0308 × 0300 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1100 × 200C ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 1100 × 0308 × 200C ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 1100 × 200D ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 1100 × 0308 × 200D ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
 ÷ 1100 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 1100 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 1100 ÷ 0600 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 1100 × 0308 ÷ 0600 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 1100 × 0A03 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 1100 × 0308 × 0A03 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 1100 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 1100 ÷ 06DD ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 1100 × 0308 ÷ 06DD ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 1100 × 0903 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 1100 × 0308 × 0903 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
 ÷ 1100 × 1100 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 1100 × 0308 ÷ 1100 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 1100 × 0308 ÷ 1100 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
 ÷ 1100 × 1160 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 1100 × 0308 ÷ 1160 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 1100 × 0308 ÷ 1160 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
 ÷ 1100 ÷ 11A8 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 1100 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 1100 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
 ÷ 1100 × AC00 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 1100 × 0308 ÷ AC00 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 1100 × 0308 ÷ AC00 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
 ÷ 1100 × AC01 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 1100 × 0308 ÷ AC01 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 1100 × 0903 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 1100 × 0308 × 0903 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 1100 ÷ 0904 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 1100 × 0308 ÷ 0904 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 1100 ÷ 0D4E ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 1100 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 1100 ÷ 0915 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 1100 × 0308 ÷ 0915 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 1100 ÷ 231A ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 1100 × 0308 ÷ 231A ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 1100 × 0300 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 1100 × 0308 × 0300 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 1100 × 0900 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 1100 × 0308 × 0900 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 1100 × 094D ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 1100 × 0308 × 094D ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 1100 × 200D ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 1100 × 0308 × 200D ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 1100 ÷ 0378 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1100 × 0308 ÷ 0378 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1160 ÷ 0020 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 1160 × 0308 ÷ 0020 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
+÷ 1100 × 0308 ÷ AC01 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 1100 ÷ 1CF5 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 1100 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 1100 ÷ 0915 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 1100 × 0308 ÷ 0915 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 1100 ÷ 00A9 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 1100 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 1100 ÷ 0020 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1100 × 0308 ÷ 0020 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1100 ÷ 0378 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1100 × 0308 ÷ 0378 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
 ÷ 1160 ÷ 000D ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 1160 × 0308 ÷ 000D ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 1160 × 0308 ÷ 000D ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
 ÷ 1160 ÷ 000A ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 1160 × 0308 ÷ 000A ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 1160 ÷ 0001 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 1160 × 0308 ÷ 0001 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 1160 × 200C ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 1160 × 0308 × 200C ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
+÷ 1160 × 0308 ÷ 000A ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 1160 ÷ 0000 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 1160 × 0308 ÷ 0000 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 1160 × 094D ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 1160 × 0308 × 094D ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 1160 × 0300 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1160 × 0308 × 0300 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1160 × 200C ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 1160 × 0308 × 200C ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 1160 × 200D ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 1160 × 0308 × 200D ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
 ÷ 1160 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 1160 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 1160 ÷ 0600 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 1160 × 0308 ÷ 0600 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 1160 × 0A03 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 1160 × 0308 × 0A03 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 1160 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 1160 ÷ 06DD ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 1160 × 0308 ÷ 06DD ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 1160 × 0903 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 1160 × 0308 × 0903 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
 ÷ 1160 ÷ 1100 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 1160 × 0308 ÷ 1100 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 1160 × 0308 ÷ 1100 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
 ÷ 1160 × 1160 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [7.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 1160 × 0308 ÷ 1160 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 1160 × 0308 ÷ 1160 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
 ÷ 1160 × 11A8 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 1160 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 1160 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
 ÷ 1160 ÷ AC00 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 1160 × 0308 ÷ AC00 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 1160 × 0308 ÷ AC00 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
 ÷ 1160 ÷ AC01 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 1160 × 0308 ÷ AC01 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 1160 × 0903 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 1160 × 0308 × 0903 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 1160 ÷ 0904 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 1160 × 0308 ÷ 0904 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 1160 ÷ 0D4E ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 1160 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 1160 ÷ 0915 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 1160 × 0308 ÷ 0915 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 1160 ÷ 231A ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 1160 × 0308 ÷ 231A ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 1160 × 0300 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 1160 × 0308 × 0300 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 1160 × 0900 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 1160 × 0308 × 0900 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 1160 × 094D ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 1160 × 0308 × 094D ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 1160 × 200D ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 1160 × 0308 × 200D ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 1160 ÷ 0378 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1160 × 0308 ÷ 0378 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 11A8 ÷ 0020 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 0020 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
+÷ 1160 × 0308 ÷ AC01 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 1160 ÷ 1CF5 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 1160 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 1160 ÷ 0915 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 1160 × 0308 ÷ 0915 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 1160 ÷ 00A9 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 1160 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 1160 ÷ 0020 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1160 × 0308 ÷ 0020 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1160 ÷ 0378 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1160 × 0308 ÷ 0378 ÷	#  ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
 ÷ 11A8 ÷ 000D ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 000D ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 000D ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
 ÷ 11A8 ÷ 000A ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 000A ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 11A8 ÷ 0001 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 0001 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 11A8 × 200C ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 11A8 × 0308 × 200C ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 000A ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 11A8 ÷ 0000 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 0000 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 11A8 × 094D ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 11A8 × 0308 × 094D ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 11A8 × 0300 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 11A8 × 0308 × 0300 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 11A8 × 200C ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 11A8 × 0308 × 200C ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 11A8 × 200D ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 11A8 × 0308 × 200D ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
 ÷ 11A8 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 11A8 ÷ 0600 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 0600 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 11A8 × 0A03 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 11A8 × 0308 × 0A03 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 11A8 ÷ 06DD ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 06DD ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 11A8 × 0903 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 11A8 × 0308 × 0903 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
 ÷ 11A8 ÷ 1100 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 1100 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 1100 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
 ÷ 11A8 ÷ 1160 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 1160 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 1160 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
 ÷ 11A8 × 11A8 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
 ÷ 11A8 ÷ AC00 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 11A8 × 0308 ÷ AC00 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 11A8 × 0308 ÷ AC00 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
 ÷ 11A8 ÷ AC01 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 11A8 × 0308 ÷ AC01 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 11A8 × 0903 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 11A8 × 0308 × 0903 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 11A8 ÷ 0904 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 0904 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 11A8 ÷ 0D4E ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 11A8 ÷ 0915 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 0915 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 11A8 ÷ 231A ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 231A ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 11A8 × 0300 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 11A8 × 0308 × 0300 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 11A8 × 0900 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 11A8 × 0308 × 0900 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 11A8 × 094D ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 11A8 × 0308 × 094D ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 11A8 × 200D ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 11A8 × 0308 × 200D ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 11A8 ÷ 0378 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 11A8 × 0308 ÷ 0378 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ AC00 ÷ 0020 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ AC00 × 0308 ÷ 0020 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
+÷ 11A8 × 0308 ÷ AC01 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 11A8 ÷ 1CF5 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 11A8 ÷ 0915 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 0915 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 11A8 ÷ 00A9 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 11A8 ÷ 0020 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 0020 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 11A8 ÷ 0378 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 11A8 × 0308 ÷ 0378 ÷	#  ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
 ÷ AC00 ÷ 000D ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ AC00 × 0308 ÷ 000D ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ AC00 × 0308 ÷ 000D ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
 ÷ AC00 ÷ 000A ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ AC00 × 0308 ÷ 000A ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ AC00 ÷ 0001 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ AC00 × 0308 ÷ 0001 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ AC00 × 200C ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ AC00 × 0308 × 200C ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
+÷ AC00 × 0308 ÷ 000A ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ AC00 ÷ 0000 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ AC00 × 0308 ÷ 0000 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ AC00 × 094D ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ AC00 × 0308 × 094D ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ AC00 × 0300 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ AC00 × 0308 × 0300 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ AC00 × 200C ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ AC00 × 0308 × 200C ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ AC00 × 200D ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ AC00 × 0308 × 200D ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
 ÷ AC00 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ AC00 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ AC00 ÷ 0600 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ AC00 × 0308 ÷ 0600 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ AC00 × 0A03 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ AC00 × 0308 × 0A03 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ AC00 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ AC00 ÷ 06DD ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ AC00 × 0308 ÷ 06DD ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ AC00 × 0903 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ AC00 × 0308 × 0903 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
 ÷ AC00 ÷ 1100 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ AC00 × 0308 ÷ 1100 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ AC00 × 0308 ÷ 1100 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
 ÷ AC00 × 1160 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ AC00 × 0308 ÷ 1160 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ AC00 × 0308 ÷ 1160 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
 ÷ AC00 × 11A8 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ AC00 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ AC00 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
 ÷ AC00 ÷ AC00 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ AC00 × 0308 ÷ AC00 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ AC00 × 0308 ÷ AC00 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
 ÷ AC00 ÷ AC01 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ AC00 × 0308 ÷ AC01 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ AC00 × 0903 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ AC00 × 0308 × 0903 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ AC00 ÷ 0904 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ AC00 × 0308 ÷ 0904 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ AC00 ÷ 0D4E ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ AC00 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ AC00 ÷ 0915 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ AC00 × 0308 ÷ 0915 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ AC00 ÷ 231A ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ AC00 × 0308 ÷ 231A ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ AC00 × 0300 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ AC00 × 0308 × 0300 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ AC00 × 0900 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ AC00 × 0308 × 0900 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ AC00 × 094D ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ AC00 × 0308 × 094D ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ AC00 × 200D ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ AC00 × 0308 × 200D ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ AC00 ÷ 0378 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ AC00 × 0308 ÷ 0378 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ AC01 ÷ 0020 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ AC01 × 0308 ÷ 0020 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
+÷ AC00 × 0308 ÷ AC01 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ AC00 ÷ 1CF5 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ AC00 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ AC00 ÷ 0915 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ AC00 × 0308 ÷ 0915 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ AC00 ÷ 00A9 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ AC00 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ AC00 ÷ 0020 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ AC00 × 0308 ÷ 0020 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ AC00 ÷ 0378 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ AC00 × 0308 ÷ 0378 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
 ÷ AC01 ÷ 000D ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ AC01 × 0308 ÷ 000D ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ AC01 × 0308 ÷ 000D ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
 ÷ AC01 ÷ 000A ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ AC01 × 0308 ÷ 000A ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ AC01 ÷ 0001 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ AC01 × 0308 ÷ 0001 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ AC01 × 200C ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ AC01 × 0308 × 200C ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
+÷ AC01 × 0308 ÷ 000A ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ AC01 ÷ 0000 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ AC01 × 0308 ÷ 0000 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ AC01 × 094D ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ AC01 × 0308 × 094D ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ AC01 × 0300 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ AC01 × 0308 × 0300 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ AC01 × 200C ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ AC01 × 0308 × 200C ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ AC01 × 200D ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ AC01 × 0308 × 200D ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
 ÷ AC01 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ AC01 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ AC01 ÷ 0600 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ AC01 × 0308 ÷ 0600 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ AC01 × 0A03 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ AC01 × 0308 × 0A03 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ AC01 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ AC01 ÷ 06DD ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ AC01 × 0308 ÷ 06DD ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ AC01 × 0903 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ AC01 × 0308 × 0903 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
 ÷ AC01 ÷ 1100 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ AC01 × 0308 ÷ 1100 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ AC01 × 0308 ÷ 1100 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
 ÷ AC01 ÷ 1160 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ AC01 × 0308 ÷ 1160 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ AC01 × 0308 ÷ 1160 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
 ÷ AC01 × 11A8 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ AC01 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ AC01 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
 ÷ AC01 ÷ AC00 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ AC01 × 0308 ÷ AC00 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ AC01 × 0308 ÷ AC00 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
 ÷ AC01 ÷ AC01 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ AC01 × 0308 ÷ AC01 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ AC01 × 0903 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ AC01 × 0308 × 0903 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ AC01 ÷ 0904 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ AC01 × 0308 ÷ 0904 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ AC01 ÷ 0D4E ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ AC01 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ AC01 ÷ 0915 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ AC01 × 0308 ÷ 0915 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ AC01 ÷ 231A ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ AC01 × 0308 ÷ 231A ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ AC01 × 0300 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ AC01 × 0308 × 0300 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ AC01 × 0900 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ AC01 × 0308 × 0900 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ AC01 × 094D ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ AC01 × 0308 × 094D ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ AC01 × 200D ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ AC01 × 0308 × 200D ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ AC01 ÷ 0378 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ AC01 × 0308 ÷ 0378 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0903 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0903 × 0308 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0903 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0903 × 0308 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0903 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0903 × 0308 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0903 ÷ 0001 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0903 × 0308 ÷ 0001 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0903 × 200C ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0903 × 0308 × 200C ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0903 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0903 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0903 ÷ 0600 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0903 × 0308 ÷ 0600 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0903 × 0A03 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0903 × 0308 × 0A03 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0903 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0903 × 0308 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0903 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0903 × 0308 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0903 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0903 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0903 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0903 × 0308 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0903 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0903 × 0308 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0903 × 0903 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0903 × 0308 × 0903 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0903 ÷ 0904 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0903 × 0308 ÷ 0904 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0903 ÷ 0D4E ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0903 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0903 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0903 × 0308 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0903 ÷ 231A ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0903 × 0308 ÷ 231A ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0903 × 0300 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0903 × 0308 × 0300 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0903 × 0900 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0903 × 0308 × 0900 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0903 × 094D ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0903 × 0308 × 094D ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0903 × 200D ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0903 × 0308 × 200D ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0903 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0903 × 0308 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0904 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0904 × 0308 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0904 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0904 × 0308 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0904 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0904 × 0308 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0904 ÷ 0001 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0904 × 0308 ÷ 0001 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0904 × 200C ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0904 × 0308 × 200C ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0904 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0904 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0904 ÷ 0600 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0904 × 0308 ÷ 0600 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0904 × 0A03 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0904 × 0308 × 0A03 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0904 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0904 × 0308 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0904 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0904 × 0308 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0904 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0904 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0904 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0904 × 0308 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0904 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0904 × 0308 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0904 × 0903 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0904 × 0308 × 0903 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0904 ÷ 0904 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0904 × 0308 ÷ 0904 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0904 ÷ 0D4E ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0904 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0904 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0904 × 0308 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0904 ÷ 231A ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0904 × 0308 ÷ 231A ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0904 × 0300 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0904 × 0308 × 0300 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0904 × 0900 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0904 × 0308 × 0900 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0904 × 094D ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0904 × 0308 × 094D ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0904 × 200D ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0904 × 0308 × 200D ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0904 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0904 × 0308 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0D4E × 0020 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] SPACE (Other) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 0020 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0D4E ÷ 000D ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 000D ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0D4E ÷ 000A ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 000A ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0D4E ÷ 0001 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 0001 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0D4E × 200C ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0D4E × 0308 × 200C ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0D4E × 1F1E6 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0D4E × 0600 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 0600 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0D4E × 0A03 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0D4E × 0308 × 0A03 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0D4E × 1100 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 1100 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0D4E × 1160 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 1160 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0D4E × 11A8 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 11A8 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0D4E × AC00 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0D4E × 0308 ÷ AC00 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0D4E × AC01 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0D4E × 0308 ÷ AC01 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0D4E × 0903 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0D4E × 0308 × 0903 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0D4E × 0904 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 0904 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0D4E × 0D4E ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 0D4E ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0D4E × 0915 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 0915 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0D4E × 231A ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] WATCH (ExtPict) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 231A ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0D4E × 0300 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0D4E × 0308 × 0300 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0D4E × 0900 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0D4E × 0308 × 0900 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0D4E × 094D ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0D4E × 0308 × 094D ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0D4E × 200D ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0D4E × 0308 × 200D ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0D4E × 0378 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.2] <reserved-0378> (Other) ÷ [0.3]
-÷ 0D4E × 0308 ÷ 0378 ÷	#  ÷ [0.2] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0915 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0915 × 0308 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0915 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0915 × 0308 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0915 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0915 × 0308 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0915 ÷ 0001 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0915 × 0308 ÷ 0001 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0915 × 200C ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0915 × 0308 × 200C ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0915 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0915 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0915 ÷ 0600 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0915 × 0308 ÷ 0600 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0915 × 0A03 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0915 × 0308 × 0A03 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0915 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0915 × 0308 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0915 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0915 × 0308 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0915 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0915 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0915 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0915 × 0308 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0915 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0915 × 0308 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0915 × 0903 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0915 × 0308 × 0903 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0915 ÷ 0904 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0915 × 0308 ÷ 0904 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0915 ÷ 0D4E ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0915 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0915 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0915 × 0308 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0915 ÷ 231A ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0915 × 0308 ÷ 231A ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0915 × 0300 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0915 × 0308 × 0300 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0915 × 0900 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0915 × 0308 × 0900 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0915 × 094D ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0915 × 0308 × 094D ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0915 × 200D ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0915 × 0308 × 200D ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0915 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0915 × 0308 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 231A ÷ 0020 ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 231A × 0308 ÷ 0020 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 231A ÷ 000D ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 231A × 0308 ÷ 000D ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 231A ÷ 000A ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 231A × 0308 ÷ 000A ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 231A ÷ 0001 ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 231A × 0308 ÷ 0001 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 231A × 200C ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 231A × 0308 × 200C ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 231A ÷ 1F1E6 ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 231A × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 231A ÷ 0600 ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 231A × 0308 ÷ 0600 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 231A × 0A03 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 231A × 0308 × 0A03 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 231A ÷ 1100 ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 231A × 0308 ÷ 1100 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 231A ÷ 1160 ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 231A × 0308 ÷ 1160 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 231A ÷ 11A8 ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 231A × 0308 ÷ 11A8 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 231A ÷ AC00 ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 231A × 0308 ÷ AC00 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 231A ÷ AC01 ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 231A × 0308 ÷ AC01 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 231A × 0903 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 231A × 0308 × 0903 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 231A ÷ 0904 ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 231A × 0308 ÷ 0904 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 231A ÷ 0D4E ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 231A × 0308 ÷ 0D4E ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 231A ÷ 0915 ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 231A × 0308 ÷ 0915 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 231A ÷ 231A ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 231A × 0308 ÷ 231A ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 231A × 0300 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 231A × 0308 × 0300 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 231A × 0900 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 231A × 0308 × 0900 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 231A × 094D ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 231A × 0308 × 094D ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 231A × 200D ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 231A × 0308 × 200D ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 231A ÷ 0378 ÷	#  ÷ [0.2] WATCH (ExtPict) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 231A × 0308 ÷ 0378 ÷	#  ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0300 ÷ 0020 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0300 × 0308 ÷ 0020 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0300 ÷ 000D ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0300 × 0308 ÷ 000D ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0300 ÷ 000A ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0300 × 0308 ÷ 000A ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0300 ÷ 0001 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0300 × 0308 ÷ 0001 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0300 × 200C ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0300 × 0308 × 200C ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0300 ÷ 1F1E6 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0300 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0300 ÷ 0600 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0300 × 0308 ÷ 0600 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0300 × 0A03 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0300 × 0308 × 0A03 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0300 ÷ 1100 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0300 × 0308 ÷ 1100 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0300 ÷ 1160 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0300 × 0308 ÷ 1160 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0300 ÷ 11A8 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0300 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0300 ÷ AC00 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0300 × 0308 ÷ AC00 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0300 ÷ AC01 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0300 × 0308 ÷ AC01 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0300 × 0903 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0300 × 0308 × 0903 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0300 ÷ 0904 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0300 × 0308 ÷ 0904 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0300 ÷ 0D4E ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0300 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0300 ÷ 0915 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0300 × 0308 ÷ 0915 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0300 ÷ 231A ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0300 × 0308 ÷ 231A ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0300 × 0300 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0300 × 0308 × 0300 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0300 × 0900 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0300 × 0308 × 0900 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0300 × 094D ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0300 × 0308 × 094D ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0300 × 200D ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0300 × 0308 × 200D ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0300 ÷ 0378 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0300 × 0308 ÷ 0378 ÷	#  ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0900 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0900 × 0308 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0900 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0900 × 0308 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0900 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0900 × 0308 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0900 ÷ 0001 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0900 × 0308 ÷ 0001 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0900 × 200C ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0900 × 0308 × 200C ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0900 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0900 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0900 ÷ 0600 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0900 × 0308 ÷ 0600 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0900 × 0A03 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0900 × 0308 × 0A03 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0900 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0900 × 0308 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0900 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0900 × 0308 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0900 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0900 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0900 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0900 × 0308 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0900 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0900 × 0308 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0900 × 0903 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0900 × 0308 × 0903 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0900 ÷ 0904 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0900 × 0308 ÷ 0904 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0900 ÷ 0D4E ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0900 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0900 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0900 × 0308 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0900 ÷ 231A ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0900 × 0308 ÷ 231A ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0900 × 0300 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0900 × 0308 × 0300 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0900 × 0900 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0900 × 0308 × 0900 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0900 × 094D ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0900 × 0308 × 094D ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0900 × 200D ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0900 × 0308 × 200D ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0900 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0900 × 0308 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 094D ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 094D × 0308 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 094D ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 094D × 0308 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 094D ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 094D × 0308 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 094D ÷ 0001 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 094D × 0308 ÷ 0001 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 094D × 200C ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 094D × 0308 × 200C ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 094D ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 094D × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 094D ÷ 0600 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 094D × 0308 ÷ 0600 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 094D × 0A03 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 094D × 0308 × 0A03 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 094D ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 094D × 0308 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 094D ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 094D × 0308 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 094D ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 094D × 0308 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 094D ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 094D × 0308 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 094D ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 094D × 0308 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 094D × 0903 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 094D × 0308 × 0903 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 094D ÷ 0904 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 094D × 0308 ÷ 0904 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 094D ÷ 0D4E ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 094D × 0308 ÷ 0D4E ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 094D ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 094D × 0308 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 094D ÷ 231A ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 094D × 0308 ÷ 231A ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 094D × 0300 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 094D × 0308 × 0300 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 094D × 0900 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 094D × 0308 × 0900 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 094D × 094D ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 094D × 0308 × 094D ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 094D × 200D ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 094D × 0308 × 200D ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 094D ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 094D × 0308 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 200D ÷ 0020 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 200D × 0308 ÷ 0020 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 200D ÷ 000D ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 200D × 0308 ÷ 000D ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 200D ÷ 000A ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 200D × 0308 ÷ 000A ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 200D ÷ 0001 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 200D × 0308 ÷ 0001 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 200D × 200C ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 200D × 0308 × 200C ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 200D ÷ 1F1E6 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 200D × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 200D ÷ 0600 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 200D × 0308 ÷ 0600 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 200D × 0A03 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 200D × 0308 × 0A03 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 200D ÷ 1100 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 200D × 0308 ÷ 1100 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 200D ÷ 1160 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 200D × 0308 ÷ 1160 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 200D ÷ 11A8 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 200D × 0308 ÷ 11A8 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 200D ÷ AC00 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 200D × 0308 ÷ AC00 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 200D ÷ AC01 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 200D × 0308 ÷ AC01 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 200D × 0903 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 200D × 0308 × 0903 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 200D ÷ 0904 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 200D × 0308 ÷ 0904 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 200D ÷ 0D4E ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 200D × 0308 ÷ 0D4E ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 200D ÷ 0915 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 200D × 0308 ÷ 0915 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 200D ÷ 231A ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 200D × 0308 ÷ 231A ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 200D × 0300 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 200D × 0308 × 0300 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 200D × 0900 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 200D × 0308 × 0900 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 200D × 094D ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 200D × 0308 × 094D ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 200D × 200D ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 200D × 0308 × 200D ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 200D ÷ 0378 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 200D × 0308 ÷ 0378 ÷	#  ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0378 ÷ 0020 ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0378 × 0308 ÷ 0020 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ 0378 ÷ 000D ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0378 × 0308 ÷ 000D ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ 0378 ÷ 000A ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0378 × 0308 ÷ 000A ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ 0378 ÷ 0001 ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0378 × 0308 ÷ 0001 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ 0378 × 200C ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0378 × 0308 × 200C ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH NON-JOINER (Extend) ÷ [0.3]
-÷ 0378 ÷ 1F1E6 ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0378 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ 0378 ÷ 0600 ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0378 × 0308 ÷ 0600 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ 0378 × 0A03 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0378 × 0308 × 0A03 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] GURMUKHI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ 0378 ÷ 1100 ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0378 × 0308 ÷ 1100 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 0378 ÷ 1160 ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0378 × 0308 ÷ 1160 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ 0378 ÷ 11A8 ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0378 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ 0378 ÷ AC00 ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0378 × 0308 ÷ AC00 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ 0378 ÷ AC01 ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0378 × 0308 ÷ AC01 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ 0378 × 0903 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0378 × 0308 × 0903 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0378 ÷ 0904 ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0378 × 0308 ÷ 0904 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER SHORT A (ConjunctLinkingScripts) ÷ [0.3]
-÷ 0378 ÷ 0D4E ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0378 × 0308 ÷ 0D4E ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] MALAYALAM LETTER DOT REPH (Prepend_ConjunctLinkingScripts) ÷ [0.3]
-÷ 0378 ÷ 0915 ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0378 × 0308 ÷ 0915 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0378 ÷ 231A ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0378 × 0308 ÷ 231A ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ 0378 × 0300 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0378 × 0308 × 0300 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0378 × 0900 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0378 × 0308 × 0900 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN INVERTED CANDRABINDU (Extend_ConjunctLinkingScripts_ExtCccZwj) ÷ [0.3]
-÷ 0378 × 094D ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0378 × 0308 × 094D ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [0.3]
-÷ 0378 × 200D ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0378 × 0308 × 200D ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0378 ÷ 0378 ÷	#  ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0378 × 0308 ÷ 0378 ÷	#  ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 000D × 000A ÷ 0061 ÷ 000A ÷ 0308 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) × [3.0] <LINE FEED (LF)> (LF) ÷ [4.0] LATIN SMALL LETTER A (Other) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0061 × 0308 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3]
-÷ 0020 × 200D ÷ 0646 ÷	#  ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] ARABIC LETTER NOON (Other) ÷ [0.3]
-÷ 0646 × 200D ÷ 0020 ÷	#  ÷ [0.2] ARABIC LETTER NOON (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
+÷ AC01 × 0308 ÷ AC01 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ AC01 ÷ 1CF5 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ AC01 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ AC01 ÷ 0915 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ AC01 × 0308 ÷ 0915 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ AC01 ÷ 00A9 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ AC01 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ AC01 ÷ 0020 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ AC01 × 0308 ÷ 0020 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ AC01 ÷ 0378 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ AC01 × 0308 ÷ 0378 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1CF5 ÷ 000D ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ 000D ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 1CF5 ÷ 000A ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ 000A ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 1CF5 ÷ 0000 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ 0000 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 1CF5 × 094D ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 1CF5 × 0308 × 094D ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 1CF5 × 0300 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1CF5 × 0308 × 0300 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1CF5 × 200C ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 1CF5 × 0308 × 200C ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 1CF5 × 200D ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 1CF5 × 0308 × 200D ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 1CF5 ÷ 1F1E6 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 1CF5 ÷ 06DD ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ 06DD ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 1CF5 × 0903 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 1CF5 × 0308 × 0903 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 1CF5 ÷ 1100 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ 1100 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 1CF5 ÷ 1160 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ 1160 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 1CF5 ÷ 11A8 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 1CF5 ÷ AC00 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ AC00 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 1CF5 ÷ AC01 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ AC01 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 1CF5 ÷ 1CF5 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 1CF5 × 0915 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.3] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 1CF5 × 0308 × 0915 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.3] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 1CF5 ÷ 00A9 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 1CF5 ÷ 0020 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ 0020 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1CF5 ÷ 0378 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1CF5 × 0308 ÷ 0378 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0915 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 0915 × 0308 ÷ 000D ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 0915 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 0915 × 0308 ÷ 000A ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 0915 ÷ 0000 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 0915 × 0308 ÷ 0000 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 0915 × 094D ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 0915 × 0308 × 094D ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 0915 × 0300 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0915 × 0308 × 0300 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0915 × 200C ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 0915 × 0308 × 200C ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 0915 × 200D ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0915 × 0308 × 200D ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0915 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 0915 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 0915 ÷ 06DD ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 0915 × 0308 ÷ 06DD ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 0915 × 0903 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 0915 × 0308 × 0903 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 0915 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 0915 × 0308 ÷ 1100 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 0915 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 0915 × 0308 ÷ 1160 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 0915 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 0915 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 0915 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 0915 × 0308 ÷ AC00 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 0915 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 0915 × 0308 ÷ AC01 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 0915 ÷ 1CF5 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 0915 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 0915 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 0915 × 0308 ÷ 0915 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 0915 ÷ 00A9 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 0915 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 0915 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0915 × 0308 ÷ 0020 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0915 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0915 × 0308 ÷ 0378 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 00A9 ÷ 000D ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 000D ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 00A9 ÷ 000A ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 000A ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 00A9 ÷ 0000 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 0000 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 00A9 × 094D ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 00A9 × 0308 × 094D ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 00A9 × 0300 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 00A9 × 0308 × 0300 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 00A9 × 200C ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 00A9 × 0308 × 200C ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 00A9 × 200D ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 00A9 × 0308 × 200D ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 00A9 ÷ 1F1E6 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 00A9 ÷ 06DD ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 06DD ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 00A9 × 0903 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 00A9 × 0308 × 0903 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 00A9 ÷ 1100 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 1100 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 00A9 ÷ 1160 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 1160 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 00A9 ÷ 11A8 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 00A9 ÷ AC00 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 00A9 × 0308 ÷ AC00 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 00A9 ÷ AC01 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 00A9 × 0308 ÷ AC01 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 00A9 ÷ 1CF5 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 00A9 ÷ 0915 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 0915 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 00A9 ÷ 00A9 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 00A9 ÷ 0020 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 0020 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 00A9 ÷ 0378 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 00A9 × 0308 ÷ 0378 ÷	#  ÷ [0.2] COPYRIGHT SIGN (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0020 ÷ 000D ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 0020 × 0308 ÷ 000D ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 0020 ÷ 000A ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 0020 × 0308 ÷ 000A ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 0020 ÷ 0000 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 0020 × 0308 ÷ 0000 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 0020 × 094D ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 0020 × 0308 × 094D ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 0020 × 0300 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0020 × 0308 × 0300 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0020 × 200C ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 0020 × 0308 × 200C ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 0020 × 200D ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0020 × 0308 × 200D ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0020 ÷ 1F1E6 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 0020 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 0020 ÷ 06DD ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 0020 × 0308 ÷ 06DD ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 0020 × 0903 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 0020 × 0308 × 0903 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 0020 ÷ 1100 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 0020 × 0308 ÷ 1100 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 0020 ÷ 1160 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 0020 × 0308 ÷ 1160 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 0020 ÷ 11A8 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 0020 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 0020 ÷ AC00 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 0020 × 0308 ÷ AC00 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 0020 ÷ AC01 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 0020 × 0308 ÷ AC01 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 0020 ÷ 1CF5 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 0020 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 0020 ÷ 0915 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 0020 × 0308 ÷ 0915 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 0020 ÷ 00A9 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 0020 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 0020 ÷ 0020 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0020 × 0308 ÷ 0020 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0020 ÷ 0378 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0020 × 0308 ÷ 0378 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0378 ÷ 000D ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 0378 × 0308 ÷ 000D ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
+÷ 0378 ÷ 000A ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 0378 × 0308 ÷ 000A ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
+÷ 0378 ÷ 0000 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 0378 × 0308 ÷ 0000 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [5.0] <NULL> (Control) ÷ [0.3]
+÷ 0378 × 094D ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 0378 × 0308 × 094D ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [0.3]
+÷ 0378 × 0300 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0378 × 0308 × 0300 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] COMBINING GRAVE ACCENT (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0378 × 200C ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 0378 × 0308 × 200C ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [0.3]
+÷ 0378 × 200D ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0378 × 0308 × 200D ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0378 ÷ 1F1E6 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 0378 × 0308 ÷ 1F1E6 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
+÷ 0378 ÷ 06DD ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 0378 × 0308 ÷ 06DD ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] ARABIC END OF AYAH (Prepend) ÷ [0.3]
+÷ 0378 × 0903 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 0378 × 0308 × 0903 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
+÷ 0378 ÷ 1100 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 0378 × 0308 ÷ 1100 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
+÷ 0378 ÷ 1160 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 0378 × 0308 ÷ 1160 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
+÷ 0378 ÷ 11A8 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 0378 × 0308 ÷ 11A8 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
+÷ 0378 ÷ AC00 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 0378 × 0308 ÷ AC00 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
+÷ 0378 ÷ AC01 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 0378 × 0308 ÷ AC01 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
+÷ 0378 ÷ 1CF5 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 0378 × 0308 ÷ 1CF5 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) ÷ [0.3]
+÷ 0378 ÷ 0915 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 0378 × 0308 ÷ 0915 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 0378 ÷ 00A9 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 0378 × 0308 ÷ 00A9 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] COPYRIGHT SIGN (ExtPict) ÷ [0.3]
+÷ 0378 ÷ 0020 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0378 × 0308 ÷ 0020 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0378 ÷ 0378 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0378 × 0308 ÷ 0378 ÷	#  ÷ [0.2] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] <reserved-0378> (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 000D × 000A ÷ 0061 ÷ 000A ÷ 0308 ÷	#  ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) × [3.0] <LINE FEED (LF)> (LF) ÷ [4.0] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0061 × 0308 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0020 × 200D ÷ 0646 ÷	#  ÷ [0.2] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] ARABIC LETTER NOON (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0646 × 200D ÷ 0020 ÷	#  ÷ [0.2] ARABIC LETTER NOON (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] SPACE (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
 ÷ 1100 × 1100 ÷	#  ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
 ÷ AC00 × 11A8 ÷ 1100 ÷	#  ÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
 ÷ AC01 × 11A8 ÷ 1100 ÷	#  ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ 1F1E6 × 1F1E7 ÷ 1F1E8 ÷ 0062 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]
-÷ 0061 ÷ 1F1E6 × 1F1E7 ÷ 1F1E8 ÷ 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]
-÷ 0061 ÷ 1F1E6 × 1F1E7 × 200D ÷ 1F1E8 ÷ 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]
-÷ 0061 ÷ 1F1E6 × 200D ÷ 1F1E7 × 1F1E8 ÷ 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]
-÷ 0061 ÷ 1F1E6 × 1F1E7 ÷ 1F1E8 × 1F1E9 ÷ 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER D (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]
-÷ 0061 × 200D ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ 0061 × 0308 ÷ 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]
-÷ 0061 × 0903 ÷ 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark_ConjunctLinkingScripts) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]
-÷ 0061 ÷ 0600 × 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) × [9.2] LATIN SMALL LETTER B (Other) ÷ [0.3]
-÷ 1F476 × 1F3FF ÷ 1F476 ÷	#  ÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ExtCccZwj) ÷ [999.0] BABY (ExtPict) ÷ [0.3]
-÷ 0061 × 1F3FF ÷ 1F476 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ExtCccZwj) ÷ [999.0] BABY (ExtPict) ÷ [0.3]
-÷ 0061 × 1F3FF ÷ 1F476 × 200D × 1F6D1 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ExtCccZwj) ÷ [999.0] BABY (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]
-÷ 1F476 × 1F3FF × 0308 × 200D × 1F476 × 1F3FF ÷	#  ÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ExtCccZwj) ÷ [0.3]
-÷ 1F6D1 × 200D × 1F6D1 ÷	#  ÷ [0.2] OCTAGONAL SIGN (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]
-÷ 0061 × 200D ÷ 1F6D1 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]
-÷ 2701 × 200D × 2701 ÷	#  ÷ [0.2] UPPER BLADE SCISSORS (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] UPPER BLADE SCISSORS (Other) ÷ [0.3]
-÷ 0061 × 200D ÷ 2701 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] UPPER BLADE SCISSORS (Other) ÷ [0.3]
-÷ 0915 ÷ 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0915 × 094D × 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0915 × 094D × 094D × 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0915 × 094D × 200D × 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0915 × 093C × 200D × 094D × 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0915 × 093C × 094D × 200D × 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctLinkingScripts_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0915 × 094D × 0924 × 094D × 092F ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER YA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0915 × 094D ÷ 0061 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] LATIN SMALL LETTER A (Other) ÷ [0.3]
-÷ 0061 × 094D ÷ 0924 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 003F × 094D ÷ 0924 ÷	#  ÷ [0.2] QUESTION MARK (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
-÷ 0915 × 094D × 094D × 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (ConjunctLinkingScripts_LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinkingScripts_ConjunctLinker_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (ConjunctLinkingScripts_LinkingConsonant) ÷ [0.3]
+÷ 1F1E6 × 1F1E7 ÷ 1F1E8 ÷ 0062 ÷	#  ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0061 ÷ 1F1E6 × 1F1E7 ÷ 1F1E8 ÷ 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0061 ÷ 1F1E6 × 1F1E7 × 200D ÷ 1F1E8 ÷ 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0061 ÷ 1F1E6 × 200D ÷ 1F1E7 × 1F1E8 ÷ 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0061 ÷ 1F1E6 × 1F1E7 ÷ 1F1E8 × 1F1E9 ÷ 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER D (RI) ÷ [999.0] LATIN SMALL LETTER B (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0061 × 200D ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [0.3]
+÷ 0061 × 0308 ÷ 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) ÷ [999.0] LATIN SMALL LETTER B (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0061 × 0903 ÷ 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] LATIN SMALL LETTER B (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0061 ÷ 0600 × 0062 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) × [9.2] LATIN SMALL LETTER B (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 1F476 × 1F3FF ÷ 1F476 ÷	#  ÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtender) ÷ [999.0] BABY (ExtPict) ÷ [0.3]
+÷ 0061 × 1F3FF ÷ 1F476 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtender) ÷ [999.0] BABY (ExtPict) ÷ [0.3]
+÷ 0061 × 1F3FF ÷ 1F476 × 200D × 1F6D1 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtender) ÷ [999.0] BABY (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]
+÷ 1F476 × 1F3FF × 0308 × 200D × 1F476 × 1F3FF ÷	#  ÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtender) × [9.0] COMBINING DIAERESIS (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) × [11.0] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1F6D1 × 200D × 1F6D1 ÷	#  ÷ [0.2] OCTAGONAL SIGN (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]
+÷ 0061 × 200D ÷ 1F6D1 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3]
+÷ 2701 × 200D ÷ 2701 ÷	#  ÷ [0.2] UPPER BLADE SCISSORS (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] UPPER BLADE SCISSORS (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0061 × 200D ÷ 2701 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ) ÷ [999.0] UPPER BLADE SCISSORS (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0915 ÷ 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]
+÷ 0915 × 094D × 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]
+÷ 0915 × 094D × 094D × 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]
+÷ 0915 × 094D × 200D × 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]
+÷ 0915 × 093C × 200D × 094D × 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctExtender) × [9.0] ZERO WIDTH JOINER (ZWJ) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]
+÷ 0915 × 093C × 094D × 200D × 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ConjunctExtender) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] ZERO WIDTH JOINER (ZWJ) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]
+÷ 0915 × 094D × 0924 × 094D × 092F ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER YA (LinkingConsonant) ÷ [0.3]
+÷ 0915 × 094D ÷ 0061 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) ÷ [999.0] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0061 × 094D × 0924 ÷	#  ÷ [0.2] LATIN SMALL LETTER A (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]
+÷ 003F × 094D × 0924 ÷	#  ÷ [0.2] QUESTION MARK (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]
+÷ 0915 × 094D × 094D × 0924 ÷	#  ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3]
+÷ 0AB8 × 0AFB × 0ACD × 0AB8 × 0AFB ÷	#  ÷ [0.2] GUJARATI LETTER SA (LinkingConsonant) × [9.0] GUJARATI SIGN SHADDA (Extend_ConjunctExtender) × [9.0] GUJARATI SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] GUJARATI LETTER SA (LinkingConsonant) × [9.0] GUJARATI SIGN SHADDA (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1019 × 1039 × 1018 ÷ 102C × 1037 ÷	#  ÷ [0.2] MYANMAR LETTER MA (LinkingConsonant) × [9.0] MYANMAR SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] MYANMAR LETTER BHA (LinkingConsonant) ÷ [999.0] MYANMAR VOWEL SIGN AA (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] MYANMAR SIGN DOT BELOW (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1004 × 103A × 1039 × 1011 × 1039 × 1011 ÷	#  ÷ [0.2] MYANMAR LETTER NGA (LinkingConsonant) × [9.0] MYANMAR SIGN ASAT (Extend_ConjunctExtender) × [9.0] MYANMAR SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] MYANMAR LETTER THA (LinkingConsonant) × [9.0] MYANMAR SIGN VIRAMA (Extend_ConjunctLinker) × [9.3] MYANMAR LETTER THA (LinkingConsonant) ÷ [0.3]
+÷ 1B12 × 1B01 ÷ 1B32 × 1B44 × 1B2F ÷ 1B32 × 1B44 × 1B22 × 1B44 × 1B2C ÷ 1B32 × 1B44 × 1B22 × 1B38 ÷	#  ÷ [0.2] BALINESE LETTER OKARA TEDUNG (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] BALINESE SIGN ULU CANDRA (Extend_ConjunctExtender) ÷ [999.0] BALINESE LETTER SA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER WA (LinkingConsonant) ÷ [999.0] BALINESE LETTER SA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER TA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER YA (LinkingConsonant) ÷ [999.0] BALINESE LETTER SA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER TA (LinkingConsonant) × [9.0] BALINESE VOWEL SIGN SUKU (Extend_ConjunctExtender) ÷ [0.3]
+÷ 179F × 17D2 × 178F × 17D2 × 179A × 17B8 ÷	#  ÷ [0.2] KHMER LETTER SA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER LETTER TA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER LETTER RO (LinkingConsonant) × [9.0] KHMER VOWEL SIGN II (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1B26 ÷ 1B17 × 1B44 × 1B13 ÷	#  ÷ [0.2] BALINESE LETTER NA (LinkingConsonant) ÷ [999.0] BALINESE LETTER NGA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 1B27 ÷ 1B13 × 1B44 × 1B0B ÷ 1B0B × 1B04 ÷	#  ÷ [0.2] BALINESE LETTER PA (LinkingConsonant) ÷ [999.0] BALINESE LETTER KA (LinkingConsonant) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER RA REPA (LinkingConsonant) ÷ [999.0] BALINESE LETTER RA REPA (LinkingConsonant) × [9.1] BALINESE SIGN BISAH (SpacingMark) ÷ [0.3]
+÷ 1795 × 17D2 × 17AF ÷ 1798 ÷	#  ÷ [0.2] KHMER LETTER PHA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER INDEPENDENT VOWEL QE (LinkingConsonant) ÷ [999.0] KHMER LETTER MO (LinkingConsonant) ÷ [0.3]
+÷ 17A0 × 17D2 × 17AB ÷ 1791 × 17D0 ÷ 1799 ÷	#  ÷ [0.2] KHMER LETTER HA (LinkingConsonant) × [9.0] KHMER SIGN COENG (Extend_ConjunctLinker) × [9.3] KHMER INDEPENDENT VOWEL RY (LinkingConsonant) ÷ [999.0] KHMER LETTER TO (LinkingConsonant) × [9.0] KHMER SIGN SAMYOK SANNYA (Extend_ConjunctExtender) ÷ [999.0] KHMER LETTER YO (LinkingConsonant) ÷ [0.3]
+÷ 1B05 × 1B44 × 1B33 × 1B03 ÷	#  ÷ [0.2] BALINESE LETTER AKARA (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] BALINESE ADEG ADEG (Extend_ConjunctLinker) × [9.3] BALINESE LETTER HA (LinkingConsonant) × [9.0] BALINESE SIGN SURANG (Extend_ConjunctExtender) ÷ [0.3]
+÷ 0CF1 ÷ 0C95 ÷	#  ÷ [0.2] KANNADA SIGN JIHVAMULIYA (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] KANNADA LETTER KA (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0CF2 ÷ 0CAB ÷	#  ÷ [0.2] KANNADA SIGN UPADHMANIYA (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] KANNADA LETTER PHA (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [0.3]
+÷ 0CF1 ÷ 0C95 × 0CBF ÷	#  ÷ [0.2] KANNADA SIGN JIHVAMULIYA (XXmConjunctLinkermLinkingConsonantmExtPict) ÷ [999.0] KANNADA LETTER KA (XXmConjunctLinkermLinkingConsonantmExtPict) × [9.0] KANNADA VOWEL SIGN I (Extend_ConjunctExtender) ÷ [0.3]
+÷ 1CF5 × 0995 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.3] BENGALI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 1CF6 × 09AA ÷	#  ÷ [0.2] VEDIC SIGN UPADHMANIYA (ConjunctLinkermExtend) × [9.3] BENGALI LETTER PA (LinkingConsonant) ÷ [0.3]
+÷ 1CF5 × 200C ÷ 0995 ÷	#  ÷ [0.2] VEDIC SIGN JIHVAMULIYA (ConjunctLinkermExtend) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] BENGALI LETTER KA (LinkingConsonant) ÷ [0.3]
+÷ 1CF6 × 200C ÷ 09AA ÷	#  ÷ [0.2] VEDIC SIGN UPADHMANIYA (ConjunctLinkermExtend) × [9.0] ZERO WIDTH NON-JOINER (ExtendmConjunctLinkermConjunctExtender) ÷ [999.0] BENGALI LETTER PA (LinkingConsonant) ÷ [0.3]
+÷ 11A3A × 11A0B ÷	#  ÷ [0.2] ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA (ConjunctLinkermExtend) × [9.3] ZANABAZAR SQUARE LETTER KA (LinkingConsonant) ÷ [0.3]
 #
-# Lines: 1093
+# Lines: 853
 #
 # EOF
diff --git a/libcxx/utils/data/unicode/emoji-data.txt b/libcxx/utils/data/unicode/emoji-data.txt
index ff99028248b5f..7a383f0dcfd6a 100644
--- a/libcxx/utils/data/unicode/emoji-data.txt
+++ b/libcxx/utils/data/unicode/emoji-data.txt
@@ -1,11 +1,11 @@
 # emoji-data.txt
-# Date: 2024-05-01, 21:25:24 GMT
-# © 2024 Unicode®, Inc.
+# Date: 2026-01-30, 22:30:17 GMT
+# © 2026 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
 # For terms of use and license, see https://www.unicode.org/terms_of_use.html
 #
 # Emoji Data for UTS #51
-# Used with Emoji Version 16.0 and subsequent minor revisions (if any)
+# Version: 18.0.0
 #
 # For documentation and usage, see https://www.unicode.org/reports/tr51
 #
@@ -340,6 +340,8 @@
 1F6D1..1F6D2  ; Emoji                # E3.0   [2] (🛑..🛒)    stop sign..shopping cart
 1F6D5         ; Emoji                # E12.0  [1] (🛕)       hindu temple
 1F6D6..1F6D7  ; Emoji                # E13.0  [2] (🛖..🛗)    hut..elevator
+1F6D8         ; Emoji                # E17.0  [1] (🛘)       landslide
+1F6D9         ; Emoji                # E18.0  [1] (🛙)       lighthouse
 1F6DC         ; Emoji                # E15.0  [1] (🛜)       wireless
 1F6DD..1F6DF  ; Emoji                # E14.0  [3] (🛝..🛟)    playground slide..ring buoy
 1F6E0..1F6E5  ; Emoji                # E0.7   [6] (🛠️..🛥️)    hammer and wrench..motor boat
@@ -408,6 +410,9 @@
 1FA83..1FA86  ; Emoji                # E13.0  [4] (🪃..🪆)    boomerang..nesting dolls
 1FA87..1FA88  ; Emoji                # E15.0  [2] (🪇..🪈)    maracas..flute
 1FA89         ; Emoji                # E16.0  [1] (🪉)       harp
+1FA8A         ; Emoji                # E17.0  [1] (🪊)       trombone
+1FA8B..1FA8D  ; Emoji                # E18.0  [3] (🪋..🪍)    meteor..net with handle
+1FA8E         ; Emoji                # E17.0  [1] (🪎)       treasure chest
 1FA8F         ; Emoji                # E16.0  [1] (🪏)       shovel
 1FA90..1FA95  ; Emoji                # E12.0  [6] (🪐..🪕)    ringed planet..banjo
 1FA96..1FAA8  ; Emoji                # E13.0 [19] (🪖..🪨)    military helmet..rock
@@ -421,19 +426,27 @@
 1FAC0..1FAC2  ; Emoji                # E13.0  [3] (🫀..🫂)    anatomical heart..people hugging
 1FAC3..1FAC5  ; Emoji                # E14.0  [3] (🫃..🫅)    pregnant man..person with crown
 1FAC6         ; Emoji                # E16.0  [1] (🫆)       fingerprint
+1FAC8         ; Emoji                # E17.0  [1] (🫈)       hairy creature
+1FACC         ; Emoji                # E18.0  [1] (🫌)       monarch butterfly
+1FACD         ; Emoji                # E17.0  [1] (🫍)       orca
 1FACE..1FACF  ; Emoji                # E15.0  [2] (🫎..🫏)    moose..donkey
 1FAD0..1FAD6  ; Emoji                # E13.0  [7] (🫐..🫖)    blueberries..teapot
 1FAD7..1FAD9  ; Emoji                # E14.0  [3] (🫗..🫙)    pouring liquid..jar
 1FADA..1FADB  ; Emoji                # E15.0  [2] (🫚..🫛)    ginger root..pea pod
 1FADC         ; Emoji                # E16.0  [1] (🫜)       root vegetable
+1FADD         ; Emoji                # E18.0  [1] (🫝)       pickle
 1FADF         ; Emoji                # E16.0  [1] (🫟)       splatter
 1FAE0..1FAE7  ; Emoji                # E14.0  [8] (🫠..🫧)    melting face..bubbles
 1FAE8         ; Emoji                # E15.0  [1] (🫨)       shaking face
 1FAE9         ; Emoji                # E16.0  [1] (🫩)       face with bags under eyes
+1FAEA         ; Emoji                # E17.0  [1] (🫪)       distorted face
+1FAEB         ; Emoji                # E18.0  [1] (🫫)       cracking face
+1FAEF         ; Emoji                # E17.0  [1] (🫯)       fight cloud
 1FAF0..1FAF6  ; Emoji                # E14.0  [7] (🫰..🫶)    hand with index finger and thumb crossed..heart hands
 1FAF7..1FAF8  ; Emoji                # E15.0  [2] (🫷..🫸)    leftwards pushing hand..rightwards pushing hand
+1FAF9..1FAFA  ; Emoji                # E18.0  [2] (🫹..🫺)    leftwards thumb sign..rightwards thumb sign
 
-# Total elements: 1431
+# Total elements: 1447
 
 # ================================================
 
@@ -640,6 +653,8 @@
 1F6D1..1F6D2  ; Emoji_Presentation   # E3.0   [2] (🛑..🛒)    stop sign..shopping cart
 1F6D5         ; Emoji_Presentation   # E12.0  [1] (🛕)       hindu temple
 1F6D6..1F6D7  ; Emoji_Presentation   # E13.0  [2] (🛖..🛗)    hut..elevator
+1F6D8         ; Emoji_Presentation   # E17.0  [1] (🛘)       landslide
+1F6D9         ; Emoji_Presentation   # E18.0  [1] (🛙)       lighthouse
 1F6DC         ; Emoji_Presentation   # E15.0  [1] (🛜)       wireless
 1F6DD..1F6DF  ; Emoji_Presentation   # E14.0  [3] (🛝..🛟)    playground slide..ring buoy
 1F6EB..1F6EC  ; Emoji_Presentation   # E1.0   [2] (🛫..🛬)    airplane departure..airplane arrival
@@ -704,6 +719,9 @@
 1FA83..1FA86  ; Emoji_Presentation   # E13.0  [4] (🪃..🪆)    boomerang..nesting dolls
 1FA87..1FA88  ; Emoji_Presentation   # E15.0  [2] (🪇..🪈)    maracas..flute
 1FA89         ; Emoji_Presentation   # E16.0  [1] (🪉)       harp
+1FA8A         ; Emoji_Presentation   # E17.0  [1] (🪊)       trombone
+1FA8B..1FA8D  ; Emoji_Presentation   # E18.0  [3] (🪋..🪍)    meteor..net with handle
+1FA8E         ; Emoji_Presentation   # E17.0  [1] (🪎)       treasure chest
 1FA8F         ; Emoji_Presentation   # E16.0  [1] (🪏)       shovel
 1FA90..1FA95  ; Emoji_Presentation   # E12.0  [6] (🪐..🪕)    ringed planet..banjo
 1FA96..1FAA8  ; Emoji_Presentation   # E13.0 [19] (🪖..🪨)    military helmet..rock
@@ -717,19 +735,27 @@
 1FAC0..1FAC2  ; Emoji_Presentation   # E13.0  [3] (🫀..🫂)    anatomical heart..people hugging
 1FAC3..1FAC5  ; Emoji_Presentation   # E14.0  [3] (🫃..🫅)    pregnant man..person with crown
 1FAC6         ; Emoji_Presentation   # E16.0  [1] (🫆)       fingerprint
+1FAC8         ; Emoji_Presentation   # E17.0  [1] (🫈)       hairy creature
+1FACC         ; Emoji_Presentation   # E18.0  [1] (🫌)       monarch butterfly
+1FACD         ; Emoji_Presentation   # E17.0  [1] (🫍)       orca
 1FACE..1FACF  ; Emoji_Presentation   # E15.0  [2] (🫎..🫏)    moose..donkey
 1FAD0..1FAD6  ; Emoji_Presentation   # E13.0  [7] (🫐..🫖)    blueberries..teapot
 1FAD7..1FAD9  ; Emoji_Presentation   # E14.0  [3] (🫗..🫙)    pouring liquid..jar
 1FADA..1FADB  ; Emoji_Presentation   # E15.0  [2] (🫚..🫛)    ginger root..pea pod
 1FADC         ; Emoji_Presentation   # E16.0  [1] (🫜)       root vegetable
+1FADD         ; Emoji_Presentation   # E18.0  [1] (🫝)       pickle
 1FADF         ; Emoji_Presentation   # E16.0  [1] (🫟)       splatter
 1FAE0..1FAE7  ; Emoji_Presentation   # E14.0  [8] (🫠..🫧)    melting face..bubbles
 1FAE8         ; Emoji_Presentation   # E15.0  [1] (🫨)       shaking face
 1FAE9         ; Emoji_Presentation   # E16.0  [1] (🫩)       face with bags under eyes
+1FAEA         ; Emoji_Presentation   # E17.0  [1] (🫪)       distorted face
+1FAEB         ; Emoji_Presentation   # E18.0  [1] (🫫)       cracking face
+1FAEF         ; Emoji_Presentation   # E17.0  [1] (🫯)       fight cloud
 1FAF0..1FAF6  ; Emoji_Presentation   # E14.0  [7] (🫰..🫶)    hand with index finger and thumb crossed..heart hands
 1FAF7..1FAF8  ; Emoji_Presentation   # E15.0  [2] (🫷..🫸)    leftwards pushing hand..rightwards pushing hand
+1FAF9..1FAFA  ; Emoji_Presentation   # E18.0  [2] (🫹..🫺)    leftwards thumb sign..rightwards thumb sign
 
-# Total elements: 1212
+# Total elements: 1228
 
 # ================================================
 
@@ -793,8 +819,9 @@
 1FAC3..1FAC5  ; Emoji_Modifier_Base  # E14.0  [3] (🫃..🫅)    pregnant man..person with crown
 1FAF0..1FAF6  ; Emoji_Modifier_Base  # E14.0  [7] (🫰..🫶)    hand with index finger and thumb crossed..heart hands
 1FAF7..1FAF8  ; Emoji_Modifier_Base  # E15.0  [2] (🫷..🫸)    leftwards pushing hand..rightwards pushing hand
+1FAF9..1FAFA  ; Emoji_Modifier_Base  # E18.0  [2] (🫹..🫺)    leftwards thumb sign..rightwards thumb sign
 
-# Total elements: 134
+# Total elements: 136
 
 # ================================================
 
@@ -827,7 +854,6 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 21A9..21AA    ; Extended_Pictographic# E0.6   [2] (↩️..↪️)    right arrow curving left..left arrow curving right
 231A..231B    ; Extended_Pictographic# E0.6   [2] (⌚..⌛)    watch..hourglass done
 2328          ; Extended_Pictographic# E1.0   [1] (⌨️)       keyboard
-2388          ; Extended_Pictographic# E0.0   [1] (⎈)       HELM SYMBOL
 23CF          ; Extended_Pictographic# E1.0   [1] (⏏️)       eject button
 23E9..23EC    ; Extended_Pictographic# E0.6   [4] (⏩..⏬)    fast-forward button..fast down button
 23ED..23EE    ; Extended_Pictographic# E0.7   [2] (⏭️..⏮️)    next track button..last track button
@@ -844,106 +870,63 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 2600..2601    ; Extended_Pictographic# E0.6   [2] (☀️..☁️)    sun..cloud
 2602..2603    ; Extended_Pictographic# E0.7   [2] (☂️..☃️)    umbrella..snowman
 2604          ; Extended_Pictographic# E1.0   [1] (☄️)       comet
-2605          ; Extended_Pictographic# E0.0   [1] (★)       BLACK STAR
-2607..260D    ; Extended_Pictographic# E0.0   [7] (☇..☍)    LIGHTNING..OPPOSITION
 260E          ; Extended_Pictographic# E0.6   [1] (☎️)       telephone
-260F..2610    ; Extended_Pictographic# E0.0   [2] (☏..☐)    WHITE TELEPHONE..BALLOT BOX
 2611          ; Extended_Pictographic# E0.6   [1] (☑️)       check box with check
-2612          ; Extended_Pictographic# E0.0   [1] (☒)       BALLOT BOX WITH X
 2614..2615    ; Extended_Pictographic# E0.6   [2] (☔..☕)    umbrella with rain drops..hot beverage
-2616..2617    ; Extended_Pictographic# E0.0   [2] (☖..☗)    WHITE SHOGI PIECE..BLACK SHOGI PIECE
 2618          ; Extended_Pictographic# E1.0   [1] (☘️)       shamrock
-2619..261C    ; Extended_Pictographic# E0.0   [4] (☙..☜)    REVERSED ROTATED FLORAL HEART BULLET..WHITE LEFT POINTING INDEX
 261D          ; Extended_Pictographic# E0.6   [1] (☝️)       index pointing up
-261E..261F    ; Extended_Pictographic# E0.0   [2] (☞..☟)    WHITE RIGHT POINTING INDEX..WHITE DOWN POINTING INDEX
 2620          ; Extended_Pictographic# E1.0   [1] (☠️)       skull and crossbones
-2621          ; Extended_Pictographic# E0.0   [1] (☡)       CAUTION SIGN
 2622..2623    ; Extended_Pictographic# E1.0   [2] (☢️..☣️)    radioactive..biohazard
-2624..2625    ; Extended_Pictographic# E0.0   [2] (☤..☥)    CADUCEUS..ANKH
 2626          ; Extended_Pictographic# E1.0   [1] (☦️)       orthodox cross
-2627..2629    ; Extended_Pictographic# E0.0   [3] (☧..☩)    CHI RHO..CROSS OF JERUSALEM
 262A          ; Extended_Pictographic# E0.7   [1] (☪️)       star and crescent
-262B..262D    ; Extended_Pictographic# E0.0   [3] (☫..☭)    FARSI SYMBOL..HAMMER AND SICKLE
 262E          ; Extended_Pictographic# E1.0   [1] (☮️)       peace symbol
 262F          ; Extended_Pictographic# E0.7   [1] (☯️)       yin yang
-2630..2637    ; Extended_Pictographic# E0.0   [8] (☰..☷)    TRIGRAM FOR HEAVEN..TRIGRAM FOR EARTH
 2638..2639    ; Extended_Pictographic# E0.7   [2] (☸️..☹️)    wheel of dharma..frowning face
 263A          ; Extended_Pictographic# E0.6   [1] (☺️)       smiling face
-263B..263F    ; Extended_Pictographic# E0.0   [5] (☻..☿)    BLACK SMILING FACE..MERCURY
 2640          ; Extended_Pictographic# E4.0   [1] (♀️)       female sign
-2641          ; Extended_Pictographic# E0.0   [1] (♁)       EARTH
 2642          ; Extended_Pictographic# E4.0   [1] (♂️)       male sign
-2643..2647    ; Extended_Pictographic# E0.0   [5] (♃..♇)    JUPITER..PLUTO
 2648..2653    ; Extended_Pictographic# E0.6  [12] (♈..♓)    Aries..Pisces
-2654..265E    ; Extended_Pictographic# E0.0  [11] (♔..♞)    WHITE CHESS KING..BLACK CHESS KNIGHT
 265F          ; Extended_Pictographic# E11.0  [1] (♟️)       chess pawn
 2660          ; Extended_Pictographic# E0.6   [1] (♠️)       spade suit
-2661..2662    ; Extended_Pictographic# E0.0   [2] (♡..♢)    WHITE HEART SUIT..WHITE DIAMOND SUIT
 2663          ; Extended_Pictographic# E0.6   [1] (♣️)       club suit
-2664          ; Extended_Pictographic# E0.0   [1] (♤)       WHITE SPADE SUIT
 2665..2666    ; Extended_Pictographic# E0.6   [2] (♥️..♦️)    heart suit..diamond suit
-2667          ; Extended_Pictographic# E0.0   [1] (♧)       WHITE CLUB SUIT
 2668          ; Extended_Pictographic# E0.6   [1] (♨️)       hot springs
-2669..267A    ; Extended_Pictographic# E0.0  [18] (♩..♺)    QUARTER NOTE..RECYCLING SYMBOL FOR GENERIC MATERIALS
 267B          ; Extended_Pictographic# E0.6   [1] (♻️)       recycling symbol
-267C..267D    ; Extended_Pictographic# E0.0   [2] (♼..♽)    RECYCLED PAPER SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
 267E          ; Extended_Pictographic# E11.0  [1] (♾️)       infinity
 267F          ; Extended_Pictographic# E0.6   [1] (♿)       wheelchair symbol
-2680..2685    ; Extended_Pictographic# E0.0   [6] (⚀..⚅)    DIE FACE-1..DIE FACE-6
-2690..2691    ; Extended_Pictographic# E0.0   [2] (⚐..⚑)    WHITE FLAG..BLACK FLAG
 2692          ; Extended_Pictographic# E1.0   [1] (⚒️)       hammer and pick
 2693          ; Extended_Pictographic# E0.6   [1] (⚓)       anchor
 2694          ; Extended_Pictographic# E1.0   [1] (⚔️)       crossed swords
 2695          ; Extended_Pictographic# E4.0   [1] (⚕️)       medical symbol
 2696..2697    ; Extended_Pictographic# E1.0   [2] (⚖️..⚗️)    balance scale..alembic
-2698          ; Extended_Pictographic# E0.0   [1] (⚘)       FLOWER
 2699          ; Extended_Pictographic# E1.0   [1] (⚙️)       gear
-269A          ; Extended_Pictographic# E0.0   [1] (⚚)       STAFF OF HERMES
 269B..269C    ; Extended_Pictographic# E1.0   [2] (⚛️..⚜️)    atom symbol..fleur-de-lis
-269D..269F    ; Extended_Pictographic# E0.0   [3] (⚝..⚟)    OUTLINED WHITE STAR..THREE LINES CONVERGING LEFT
 26A0..26A1    ; Extended_Pictographic# E0.6   [2] (⚠️..⚡)    warning..high voltage
-26A2..26A6    ; Extended_Pictographic# E0.0   [5] (⚢..⚦)    DOUBLED FEMALE SIGN..MALE WITH STROKE SIGN
 26A7          ; Extended_Pictographic# E13.0  [1] (⚧️)       transgender symbol
-26A8..26A9    ; Extended_Pictographic# E0.0   [2] (⚨..⚩)    VERTICAL MALE WITH STROKE SIGN..HORIZONTAL MALE WITH STROKE SIGN
 26AA..26AB    ; Extended_Pictographic# E0.6   [2] (⚪..⚫)    white circle..black circle
-26AC..26AF    ; Extended_Pictographic# E0.0   [4] (⚬..⚯)    MEDIUM SMALL WHITE CIRCLE..UNMARRIED PARTNERSHIP SYMBOL
 26B0..26B1    ; Extended_Pictographic# E1.0   [2] (⚰️..⚱️)    coffin..funeral urn
-26B2..26BC    ; Extended_Pictographic# E0.0  [11] (⚲..⚼)    NEUTER..SESQUIQUADRATE
 26BD..26BE    ; Extended_Pictographic# E0.6   [2] (⚽..⚾)    soccer ball..baseball
-26BF..26C3    ; Extended_Pictographic# E0.0   [5] (⚿..⛃)    SQUARED KEY..BLACK DRAUGHTS KING
 26C4..26C5    ; Extended_Pictographic# E0.6   [2] (⛄..⛅)    snowman without snow..sun behind cloud
-26C6..26C7    ; Extended_Pictographic# E0.0   [2] (⛆..⛇)    RAIN..BLACK SNOWMAN
 26C8          ; Extended_Pictographic# E0.7   [1] (⛈️)       cloud with lightning and rain
-26C9..26CD    ; Extended_Pictographic# E0.0   [5] (⛉..⛍)    TURNED WHITE SHOGI PIECE..DISABLED CAR
 26CE          ; Extended_Pictographic# E0.6   [1] (⛎)       Ophiuchus
 26CF          ; Extended_Pictographic# E0.7   [1] (⛏️)       pick
-26D0          ; Extended_Pictographic# E0.0   [1] (⛐)       CAR SLIDING
 26D1          ; Extended_Pictographic# E0.7   [1] (⛑️)       rescue worker’s helmet
-26D2          ; Extended_Pictographic# E0.0   [1] (⛒)       CIRCLED CROSSING LANES
 26D3          ; Extended_Pictographic# E0.7   [1] (⛓️)       chains
 26D4          ; Extended_Pictographic# E0.6   [1] (⛔)       no entry
-26D5..26E8    ; Extended_Pictographic# E0.0  [20] (⛕..⛨)    ALTERNATE ONE-WAY LEFT WAY TRAFFIC..BLACK CROSS ON SHIELD
 26E9          ; Extended_Pictographic# E0.7   [1] (⛩️)       shinto shrine
 26EA          ; Extended_Pictographic# E0.6   [1] (⛪)       church
-26EB..26EF    ; Extended_Pictographic# E0.0   [5] (⛫..⛯)    CASTLE..MAP SYMBOL FOR LIGHTHOUSE
 26F0..26F1    ; Extended_Pictographic# E0.7   [2] (⛰️..⛱️)    mountain..umbrella on ground
 26F2..26F3    ; Extended_Pictographic# E0.6   [2] (⛲..⛳)    fountain..flag in hole
 26F4          ; Extended_Pictographic# E0.7   [1] (⛴️)       ferry
 26F5          ; Extended_Pictographic# E0.6   [1] (⛵)       sailboat
-26F6          ; Extended_Pictographic# E0.0   [1] (⛶)       SQUARE FOUR CORNERS
 26F7..26F9    ; Extended_Pictographic# E0.7   [3] (⛷️..⛹️)    skier..person bouncing ball
 26FA          ; Extended_Pictographic# E0.6   [1] (⛺)       tent
-26FB..26FC    ; Extended_Pictographic# E0.0   [2] (⛻..⛼)    JAPANESE BANK SYMBOL..HEADSTONE GRAVEYARD SYMBOL
 26FD          ; Extended_Pictographic# E0.6   [1] (⛽)       fuel pump
-26FE..2701    ; Extended_Pictographic# E0.0   [4] (⛾..✁)    CUP ON BLACK SQUARE..UPPER BLADE SCISSORS
 2702          ; Extended_Pictographic# E0.6   [1] (✂️)       scissors
-2703..2704    ; Extended_Pictographic# E0.0   [2] (✃..✄)    LOWER BLADE SCISSORS..WHITE SCISSORS
 2705          ; Extended_Pictographic# E0.6   [1] (✅)       check mark button
 2708..270C    ; Extended_Pictographic# E0.6   [5] (✈️..✌️)    airplane..victory hand
 270D          ; Extended_Pictographic# E0.7   [1] (✍️)       writing hand
-270E          ; Extended_Pictographic# E0.0   [1] (✎)       LOWER RIGHT PENCIL
 270F          ; Extended_Pictographic# E0.6   [1] (✏️)       pencil
-2710..2711    ; Extended_Pictographic# E0.0   [2] (✐..✑)    UPPER RIGHT PENCIL..WHITE NIB
 2712          ; Extended_Pictographic# E0.6   [1] (✒️)       black nib
 2714          ; Extended_Pictographic# E0.6   [1] (✔️)       check mark
 2716          ; Extended_Pictographic# E0.6   [1] (✖️)       multiply
@@ -959,7 +942,6 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 2757          ; Extended_Pictographic# E0.6   [1] (❗)       red exclamation mark
 2763          ; Extended_Pictographic# E1.0   [1] (❣️)       heart exclamation
 2764          ; Extended_Pictographic# E0.6   [1] (❤️)       red heart
-2765..2767    ; Extended_Pictographic# E0.0   [3] (❥..❧)    ROTATED HEAVY BLACK HEART BULLET..ROTATED FLORAL HEART BULLET
 2795..2797    ; Extended_Pictographic# E0.6   [3] (➕..➗)    plus..divide
 27A1          ; Extended_Pictographic# E0.6   [1] (➡️)       right arrow
 27B0          ; Extended_Pictographic# E0.6   [1] (➰)       curly loop
@@ -973,19 +955,19 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 303D          ; Extended_Pictographic# E0.6   [1] (〽️)       part alternation mark
 3297          ; Extended_Pictographic# E0.6   [1] (㊗️)       Japanese “congratulations” button
 3299          ; Extended_Pictographic# E0.6   [1] (㊙️)       Japanese “secret” button
-1F000..1F003  ; Extended_Pictographic# E0.0   [4] (🀀..🀃)    MAHJONG TILE EAST WIND..MAHJONG TILE NORTH WIND
 1F004         ; Extended_Pictographic# E0.6   [1] (🀄)       mahjong red dragon
-1F005..1F0CE  ; Extended_Pictographic# E0.0 [202] (🀅..🃎)    MAHJONG TILE GREEN DRAGON..PLAYING CARD KING OF DIAMONDS
+1F02C..1F02F  ; Extended_Pictographic# E0.0   [4] (🀬..🀯)    <reserved-1F02C>..<reserved-1F02F>
+1F094..1F09F  ; Extended_Pictographic# E0.0  [12] (🂔..🂟)    <reserved-1F094>..<reserved-1F09F>
+1F0AF..1F0B0  ; Extended_Pictographic# E0.0   [2] (🂯..🂰)    <reserved-1F0AF>..<reserved-1F0B0>
+1F0C0         ; Extended_Pictographic# E0.0   [1] (🃀)       <reserved-1F0C0>
 1F0CF         ; Extended_Pictographic# E0.6   [1] (🃏)       joker
-1F0D0..1F0FF  ; Extended_Pictographic# E0.0  [48] (🃐..🃿)    <reserved-1F0D0>..<reserved-1F0FF>
-1F10D..1F10F  ; Extended_Pictographic# E0.0   [3] (🄍..🄏)    CIRCLED ZERO WITH SLASH..CIRCLED DOLLAR SIGN WITH OVERLAID BACKSLASH
-1F12F         ; Extended_Pictographic# E0.0   [1] (🄯)       COPYLEFT SYMBOL
-1F16C..1F16F  ; Extended_Pictographic# E0.0   [4] (🅬..🅯)    RAISED MR SIGN..CIRCLED HUMAN FIGURE
+1F0D0         ; Extended_Pictographic# E0.0   [1] (🃐)       <reserved-1F0D0>
+1F0F6..1F0FF  ; Extended_Pictographic# E0.0  [10] (🃶..🃿)    <reserved-1F0F6>..<reserved-1F0FF>
 1F170..1F171  ; Extended_Pictographic# E0.6   [2] (🅰️..🅱️)    A button (blood type)..B button (blood type)
 1F17E..1F17F  ; Extended_Pictographic# E0.6   [2] (🅾️..🅿️)    O button (blood type)..P button
 1F18E         ; Extended_Pictographic# E0.6   [1] (🆎)       AB button (blood type)
 1F191..1F19A  ; Extended_Pictographic# E0.6  [10] (🆑..🆚)    CL button..VS button
-1F1AD..1F1E5  ; Extended_Pictographic# E0.0  [57] (🆭..🇥)    MASK WORK SYMBOL..<reserved-1F1E5>
+1F1AF..1F1E5  ; Extended_Pictographic# E0.0  [55] (🆯..🇥)    <reserved-1F1AF>..<reserved-1F1E5>
 1F201..1F202  ; Extended_Pictographic# E0.6   [2] (🈁..🈂️)    Japanese “here” button..Japanese “service charge” button
 1F203..1F20F  ; Extended_Pictographic# E0.0  [13] (🈃..🈏)    <reserved-1F203>..<reserved-1F20F>
 1F21A         ; Extended_Pictographic# E0.6   [1] (🈚)       Japanese “free of charge” button
@@ -994,7 +976,8 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 1F23C..1F23F  ; Extended_Pictographic# E0.0   [4] (🈼..🈿)    <reserved-1F23C>..<reserved-1F23F>
 1F249..1F24F  ; Extended_Pictographic# E0.0   [7] (🉉..🉏)    <reserved-1F249>..<reserved-1F24F>
 1F250..1F251  ; Extended_Pictographic# E0.6   [2] (🉐..🉑)    Japanese “bargain” button..Japanese “acceptable” button
-1F252..1F2FF  ; Extended_Pictographic# E0.0 [174] (🉒..🋿)    <reserved-1F252>..<reserved-1F2FF>
+1F252..1F25F  ; Extended_Pictographic# E0.0  [14] (🉒..🉟)    <reserved-1F252>..<reserved-1F25F>
+1F266..1F2FF  ; Extended_Pictographic# E0.0 [154] (🉦..🋿)    <reserved-1F266>..<reserved-1F2FF>
 1F300..1F30C  ; Extended_Pictographic# E0.6  [13] (🌀..🌌)    cyclone..milky way
 1F30D..1F30E  ; Extended_Pictographic# E0.7   [2] (🌍..🌎)    globe showing Europe-Africa..globe showing Americas
 1F30F         ; Extended_Pictographic# E0.6   [1] (🌏)       globe showing Asia-Australia
@@ -1010,7 +993,6 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 1F31D..1F31E  ; Extended_Pictographic# E1.0   [2] (🌝..🌞)    full moon face..sun with face
 1F31F..1F320  ; Extended_Pictographic# E0.6   [2] (🌟..🌠)    glowing star..shooting star
 1F321         ; Extended_Pictographic# E0.7   [1] (🌡️)       thermometer
-1F322..1F323  ; Extended_Pictographic# E0.0   [2] (🌢..🌣)    BLACK DROPLET..WHITE SUN
 1F324..1F32C  ; Extended_Pictographic# E0.7   [9] (🌤️..🌬️)    sun behind small cloud..wind face
 1F32D..1F32F  ; Extended_Pictographic# E1.0   [3] (🌭..🌯)    hot dog..burrito
 1F330..1F331  ; Extended_Pictographic# E0.6   [2] (🌰..🌱)    chestnut..seedling
@@ -1026,11 +1008,8 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 1F37D         ; Extended_Pictographic# E0.7   [1] (🍽️)       fork and knife with plate
 1F37E..1F37F  ; Extended_Pictographic# E1.0   [2] (🍾..🍿)    bottle with popping cork..popcorn
 1F380..1F393  ; Extended_Pictographic# E0.6  [20] (🎀..🎓)    ribbon..graduation cap
-1F394..1F395  ; Extended_Pictographic# E0.0   [2] (🎔..🎕)    HEART WITH TIP ON THE LEFT..BOUQUET OF FLOWERS
 1F396..1F397  ; Extended_Pictographic# E0.7   [2] (🎖️..🎗️)    military medal..reminder ribbon
-1F398         ; Extended_Pictographic# E0.0   [1] (🎘)       MUSICAL KEYBOARD WITH JACKS
 1F399..1F39B  ; Extended_Pictographic# E0.7   [3] (🎙️..🎛️)    studio microphone..control knobs
-1F39C..1F39D  ; Extended_Pictographic# E0.0   [2] (🎜..🎝)    BEAMED ASCENDING MUSICAL NOTES..BEAMED DESCENDING MUSICAL NOTES
 1F39E..1F39F  ; Extended_Pictographic# E0.7   [2] (🎞️..🎟️)    film frames..admission tickets
 1F3A0..1F3C4  ; Extended_Pictographic# E0.6  [37] (🎠..🏄)    carousel horse..person surfing
 1F3C5         ; Extended_Pictographic# E1.0   [1] (🏅)       sports medal
@@ -1045,11 +1024,9 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 1F3E0..1F3E3  ; Extended_Pictographic# E0.6   [4] (🏠..🏣)    house..Japanese post office
 1F3E4         ; Extended_Pictographic# E1.0   [1] (🏤)       post office
 1F3E5..1F3F0  ; Extended_Pictographic# E0.6  [12] (🏥..🏰)    hospital..castle
-1F3F1..1F3F2  ; Extended_Pictographic# E0.0   [2] (🏱..🏲)    WHITE PENNANT..BLACK PENNANT
 1F3F3         ; Extended_Pictographic# E0.7   [1] (🏳️)       white flag
 1F3F4         ; Extended_Pictographic# E1.0   [1] (🏴)       black flag
 1F3F5         ; Extended_Pictographic# E0.7   [1] (🏵️)       rosette
-1F3F6         ; Extended_Pictographic# E0.0   [1] (🏶)       BLACK ROSETTE
 1F3F7         ; Extended_Pictographic# E0.7   [1] (🏷️)       label
 1F3F8..1F3FA  ; Extended_Pictographic# E1.0   [3] (🏸..🏺)    badminton..amphora
 1F400..1F407  ; Extended_Pictographic# E1.0   [8] (🐀..🐇)    rat..rabbit
@@ -1086,7 +1063,6 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 1F4F8         ; Extended_Pictographic# E1.0   [1] (📸)       camera with flash
 1F4F9..1F4FC  ; Extended_Pictographic# E0.6   [4] (📹..📼)    video camera..videocassette
 1F4FD         ; Extended_Pictographic# E0.7   [1] (📽️)       film projector
-1F4FE         ; Extended_Pictographic# E0.0   [1] (📾)       PORTABLE STEREO
 1F4FF..1F502  ; Extended_Pictographic# E1.0   [4] (📿..🔂)    prayer beads..repeat single button
 1F503         ; Extended_Pictographic# E0.6   [1] (🔃)       clockwise vertical arrows
 1F504..1F507  ; Extended_Pictographic# E1.0   [4] (🔄..🔇)    counterclockwise arrows button..muted speaker
@@ -1097,51 +1073,30 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 1F516..1F52B  ; Extended_Pictographic# E0.6  [22] (🔖..🔫)    bookmark..water pistol
 1F52C..1F52D  ; Extended_Pictographic# E1.0   [2] (🔬..🔭)    microscope..telescope
 1F52E..1F53D  ; Extended_Pictographic# E0.6  [16] (🔮..🔽)    crystal ball..downwards button
-1F546..1F548  ; Extended_Pictographic# E0.0   [3] (🕆..🕈)    WHITE LATIN CROSS..CELTIC CROSS
 1F549..1F54A  ; Extended_Pictographic# E0.7   [2] (🕉️..🕊️)    om..dove
 1F54B..1F54E  ; Extended_Pictographic# E1.0   [4] (🕋..🕎)    kaaba..menorah
-1F54F         ; Extended_Pictographic# E0.0   [1] (🕏)       BOWL OF HYGIEIA
 1F550..1F55B  ; Extended_Pictographic# E0.6  [12] (🕐..🕛)    one o’clock..twelve o’clock
 1F55C..1F567  ; Extended_Pictographic# E0.7  [12] (🕜..🕧)    one-thirty..twelve-thirty
-1F568..1F56E  ; Extended_Pictographic# E0.0   [7] (🕨..🕮)    RIGHT SPEAKER..BOOK
 1F56F..1F570  ; Extended_Pictographic# E0.7   [2] (🕯️..🕰️)    candle..mantelpiece clock
-1F571..1F572  ; Extended_Pictographic# E0.0   [2] (🕱..🕲)    BLACK SKULL AND CROSSBONES..NO PIRACY
 1F573..1F579  ; Extended_Pictographic# E0.7   [7] (🕳️..🕹️)    hole..joystick
 1F57A         ; Extended_Pictographic# E3.0   [1] (🕺)       man dancing
-1F57B..1F586  ; Extended_Pictographic# E0.0  [12] (🕻..🖆)    LEFT HAND TELEPHONE RECEIVER..PEN OVER STAMPED ENVELOPE
 1F587         ; Extended_Pictographic# E0.7   [1] (🖇️)       linked paperclips
-1F588..1F589  ; Extended_Pictographic# E0.0   [2] (🖈..🖉)    BLACK PUSHPIN..LOWER LEFT PENCIL
 1F58A..1F58D  ; Extended_Pictographic# E0.7   [4] (🖊️..🖍️)    pen..crayon
-1F58E..1F58F  ; Extended_Pictographic# E0.0   [2] (🖎..🖏)    LEFT WRITING HAND..TURNED OK HAND SIGN
 1F590         ; Extended_Pictographic# E0.7   [1] (🖐️)       hand with fingers splayed
-1F591..1F594  ; Extended_Pictographic# E0.0   [4] (🖑..🖔)    REVERSED RAISED HAND WITH FINGERS SPLAYED..REVERSED VICTORY HAND
 1F595..1F596  ; Extended_Pictographic# E1.0   [2] (🖕..🖖)    middle finger..vulcan salute
-1F597..1F5A3  ; Extended_Pictographic# E0.0  [13] (🖗..🖣)    WHITE DOWN POINTING LEFT HAND INDEX..BLACK DOWN POINTING BACKHAND INDEX
 1F5A4         ; Extended_Pictographic# E3.0   [1] (🖤)       black heart
 1F5A5         ; Extended_Pictographic# E0.7   [1] (🖥️)       desktop computer
-1F5A6..1F5A7  ; Extended_Pictographic# E0.0   [2] (🖦..🖧)    KEYBOARD AND MOUSE..THREE NETWORKED COMPUTERS
 1F5A8         ; Extended_Pictographic# E0.7   [1] (🖨️)       printer
-1F5A9..1F5B0  ; Extended_Pictographic# E0.0   [8] (🖩..🖰)    POCKET CALCULATOR..TWO BUTTON MOUSE
 1F5B1..1F5B2  ; Extended_Pictographic# E0.7   [2] (🖱️..🖲️)    computer mouse..trackball
-1F5B3..1F5BB  ; Extended_Pictographic# E0.0   [9] (🖳..🖻)    OLD PERSONAL COMPUTER..DOCUMENT WITH PICTURE
 1F5BC         ; Extended_Pictographic# E0.7   [1] (🖼️)       framed picture
-1F5BD..1F5C1  ; Extended_Pictographic# E0.0   [5] (🖽..🗁)    FRAME WITH TILES..OPEN FOLDER
 1F5C2..1F5C4  ; Extended_Pictographic# E0.7   [3] (🗂️..🗄️)    card index dividers..file cabinet
-1F5C5..1F5D0  ; Extended_Pictographic# E0.0  [12] (🗅..🗐)    EMPTY NOTE..PAGES
 1F5D1..1F5D3  ; Extended_Pictographic# E0.7   [3] (🗑️..🗓️)    wastebasket..spiral calendar
-1F5D4..1F5DB  ; Extended_Pictographic# E0.0   [8] (🗔..🗛)    DESKTOP WINDOW..DECREASE FONT SIZE SYMBOL
 1F5DC..1F5DE  ; Extended_Pictographic# E0.7   [3] (🗜️..🗞️)    clamp..rolled-up newspaper
-1F5DF..1F5E0  ; Extended_Pictographic# E0.0   [2] (🗟..🗠)    PAGE WITH CIRCLED TEXT..STOCK CHART
 1F5E1         ; Extended_Pictographic# E0.7   [1] (🗡️)       dagger
-1F5E2         ; Extended_Pictographic# E0.0   [1] (🗢)       LIPS
 1F5E3         ; Extended_Pictographic# E0.7   [1] (🗣️)       speaking head
-1F5E4..1F5E7  ; Extended_Pictographic# E0.0   [4] (🗤..🗧)    THREE RAYS ABOVE..THREE RAYS RIGHT
 1F5E8         ; Extended_Pictographic# E2.0   [1] (🗨️)       left speech bubble
-1F5E9..1F5EE  ; Extended_Pictographic# E0.0   [6] (🗩..🗮)    RIGHT SPEECH BUBBLE..LEFT ANGER BUBBLE
 1F5EF         ; Extended_Pictographic# E0.7   [1] (🗯️)       right anger bubble
-1F5F0..1F5F2  ; Extended_Pictographic# E0.0   [3] (🗰..🗲)    MOOD BUBBLE..LIGHTNING MOOD
 1F5F3         ; Extended_Pictographic# E0.7   [1] (🗳️)       ballot box with ballot
-1F5F4..1F5F9  ; Extended_Pictographic# E0.0   [6] (🗴..🗹)    BALLOT SCRIPT X..BALLOT BOX WITH BOLD CHECK
 1F5FA         ; Extended_Pictographic# E0.7   [1] (🗺️)       world map
 1F5FB..1F5FF  ; Extended_Pictographic# E0.6   [5] (🗻..🗿)    mount fuji..moai
 1F600         ; Extended_Pictographic# E1.0   [1] (😀)       grinning face
@@ -1210,26 +1165,23 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 1F6BF         ; Extended_Pictographic# E1.0   [1] (🚿)       shower
 1F6C0         ; Extended_Pictographic# E0.6   [1] (🛀)       person taking bath
 1F6C1..1F6C5  ; Extended_Pictographic# E1.0   [5] (🛁..🛅)    bathtub..left luggage
-1F6C6..1F6CA  ; Extended_Pictographic# E0.0   [5] (🛆..🛊)    TRIANGLE WITH ROUNDED CORNERS..GIRLS SYMBOL
 1F6CB         ; Extended_Pictographic# E0.7   [1] (🛋️)       couch and lamp
 1F6CC         ; Extended_Pictographic# E1.0   [1] (🛌)       person in bed
 1F6CD..1F6CF  ; Extended_Pictographic# E0.7   [3] (🛍️..🛏️)    shopping bags..bed
 1F6D0         ; Extended_Pictographic# E1.0   [1] (🛐)       place of worship
 1F6D1..1F6D2  ; Extended_Pictographic# E3.0   [2] (🛑..🛒)    stop sign..shopping cart
-1F6D3..1F6D4  ; Extended_Pictographic# E0.0   [2] (🛓..🛔)    STUPA..PAGODA
 1F6D5         ; Extended_Pictographic# E12.0  [1] (🛕)       hindu temple
 1F6D6..1F6D7  ; Extended_Pictographic# E13.0  [2] (🛖..🛗)    hut..elevator
-1F6D8..1F6DB  ; Extended_Pictographic# E0.0   [4] (🛘..🛛)    <reserved-1F6D8>..<reserved-1F6DB>
+1F6D8         ; Extended_Pictographic# E17.0  [1] (🛘)       landslide
+1F6D9         ; Extended_Pictographic# E18.0  [1] (🛙)       lighthouse
+1F6DA..1F6DB  ; Extended_Pictographic# E0.0   [2] (🛚..🛛)    <reserved-1F6DA>..<reserved-1F6DB>
 1F6DC         ; Extended_Pictographic# E15.0  [1] (🛜)       wireless
 1F6DD..1F6DF  ; Extended_Pictographic# E14.0  [3] (🛝..🛟)    playground slide..ring buoy
 1F6E0..1F6E5  ; Extended_Pictographic# E0.7   [6] (🛠️..🛥️)    hammer and wrench..motor boat
-1F6E6..1F6E8  ; Extended_Pictographic# E0.0   [3] (🛦..🛨)    UP-POINTING MILITARY AIRPLANE..UP-POINTING SMALL AIRPLANE
 1F6E9         ; Extended_Pictographic# E0.7   [1] (🛩️)       small airplane
-1F6EA         ; Extended_Pictographic# E0.0   [1] (🛪)       NORTHEAST-POINTING AIRPLANE
 1F6EB..1F6EC  ; Extended_Pictographic# E1.0   [2] (🛫..🛬)    airplane departure..airplane arrival
 1F6ED..1F6EF  ; Extended_Pictographic# E0.0   [3] (🛭..🛯)    <reserved-1F6ED>..<reserved-1F6EF>
 1F6F0         ; Extended_Pictographic# E0.7   [1] (🛰️)       satellite
-1F6F1..1F6F2  ; Extended_Pictographic# E0.0   [2] (🛱..🛲)    ONCOMING FIRE ENGINE..DIESEL LOCOMOTIVE
 1F6F3         ; Extended_Pictographic# E0.7   [1] (🛳️)       passenger ship
 1F6F4..1F6F6  ; Extended_Pictographic# E3.0   [3] (🛴..🛶)    kick scooter..canoe
 1F6F7..1F6F8  ; Extended_Pictographic# E5.0   [2] (🛷..🛸)    sled..flying saucer
@@ -1237,17 +1189,18 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 1F6FA         ; Extended_Pictographic# E12.0  [1] (🛺)       auto rickshaw
 1F6FB..1F6FC  ; Extended_Pictographic# E13.0  [2] (🛻..🛼)    pickup truck..roller skate
 1F6FD..1F6FF  ; Extended_Pictographic# E0.0   [3] (🛽..🛿)    <reserved-1F6FD>..<reserved-1F6FF>
-1F774..1F77F  ; Extended_Pictographic# E0.0  [12] (🝴..🝿)    LOT OF FORTUNE..ORCUS
-1F7D5..1F7DF  ; Extended_Pictographic# E0.0  [11] (🟕..🟟)    CIRCLED TRIANGLE..<reserved-1F7DF>
+1F7DC..1F7DF  ; Extended_Pictographic# E0.0   [4] (🟜..🟟)    <reserved-1F7DC>..<reserved-1F7DF>
 1F7E0..1F7EB  ; Extended_Pictographic# E12.0 [12] (🟠..🟫)    orange circle..brown square
 1F7EC..1F7EF  ; Extended_Pictographic# E0.0   [4] (🟬..🟯)    <reserved-1F7EC>..<reserved-1F7EF>
 1F7F0         ; Extended_Pictographic# E14.0  [1] (🟰)       heavy equals sign
-1F7F1..1F7FF  ; Extended_Pictographic# E0.0  [15] (🟱..🟿)    <reserved-1F7F1>..<reserved-1F7FF>
 1F80C..1F80F  ; Extended_Pictographic# E0.0   [4] (🠌..🠏)    <reserved-1F80C>..<reserved-1F80F>
 1F848..1F84F  ; Extended_Pictographic# E0.0   [8] (🡈..🡏)    <reserved-1F848>..<reserved-1F84F>
 1F85A..1F85F  ; Extended_Pictographic# E0.0   [6] (🡚..🡟)    <reserved-1F85A>..<reserved-1F85F>
 1F888..1F88F  ; Extended_Pictographic# E0.0   [8] (🢈..🢏)    <reserved-1F888>..<reserved-1F88F>
-1F8AE..1F8FF  ; Extended_Pictographic# E0.0  [82] (🢮..🣿)    <reserved-1F8AE>..<reserved-1F8FF>
+1F8AE..1F8AF  ; Extended_Pictographic# E0.0   [2] (🢮..🢯)    <reserved-1F8AE>..<reserved-1F8AF>
+1F8BC..1F8BF  ; Extended_Pictographic# E0.0   [4] (🢼..🢿)    <reserved-1F8BC>..<reserved-1F8BF>
+1F8C2..1F8CF  ; Extended_Pictographic# E0.0  [14] (🣂..🣏)    <reserved-1F8C2>..<reserved-1F8CF>
+1F8D9..1F8FF  ; Extended_Pictographic# E0.0  [39] (🣙..🣿)    <reserved-1F8D9>..<reserved-1F8FF>
 1F90C         ; Extended_Pictographic# E13.0  [1] (🤌)       pinched fingers
 1F90D..1F90F  ; Extended_Pictographic# E12.0  [3] (🤍..🤏)    white heart..pinching hand
 1F910..1F918  ; Extended_Pictographic# E1.0   [9] (🤐..🤘)    zipper-mouth face..sign of the horns
@@ -1293,7 +1246,8 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 1F9CD..1F9CF  ; Extended_Pictographic# E12.0  [3] (🧍..🧏)    person standing..deaf person
 1F9D0..1F9E6  ; Extended_Pictographic# E5.0  [23] (🧐..🧦)    face with monocle..socks
 1F9E7..1F9FF  ; Extended_Pictographic# E11.0 [25] (🧧..🧿)    red envelope..nazar amulet
-1FA00..1FA6F  ; Extended_Pictographic# E0.0 [112] (🨀..🩯)    NEUTRAL CHESS KING..<reserved-1FA6F>
+1FA58..1FA5F  ; Extended_Pictographic# E0.0   [8] (🩘..🩟)    <reserved-1FA58>..<reserved-1FA5F>
+1FA6E..1FA6F  ; Extended_Pictographic# E0.0   [2] (🩮..🩯)    <reserved-1FA6E>..<reserved-1FA6F>
 1FA70..1FA73  ; Extended_Pictographic# E12.0  [4] (🩰..🩳)    ballet shoes..shorts
 1FA74         ; Extended_Pictographic# E13.0  [1] (🩴)       thong sandal
 1FA75..1FA77  ; Extended_Pictographic# E15.0  [3] (🩵..🩷)    light blue heart..pink heart
@@ -1304,7 +1258,9 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 1FA83..1FA86  ; Extended_Pictographic# E13.0  [4] (🪃..🪆)    boomerang..nesting dolls
 1FA87..1FA88  ; Extended_Pictographic# E15.0  [2] (🪇..🪈)    maracas..flute
 1FA89         ; Extended_Pictographic# E16.0  [1] (🪉)       harp
-1FA8A..1FA8E  ; Extended_Pictographic# E0.0   [5] (🪊..🪎)    <reserved-1FA8A>..<reserved-1FA8E>
+1FA8A         ; Extended_Pictographic# E17.0  [1] (🪊)       trombone
+1FA8B..1FA8D  ; Extended_Pictographic# E18.0  [3] (🪋..🪍)    meteor..net with handle
+1FA8E         ; Extended_Pictographic# E17.0  [1] (🪎)       treasure chest
 1FA8F         ; Extended_Pictographic# E16.0  [1] (🪏)       shovel
 1FA90..1FA95  ; Extended_Pictographic# E12.0  [6] (🪐..🪕)    ringed planet..banjo
 1FA96..1FAA8  ; Extended_Pictographic# E13.0 [19] (🪖..🪨)    military helmet..rock
@@ -1318,23 +1274,32 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)      tag space..c
 1FAC0..1FAC2  ; Extended_Pictographic# E13.0  [3] (🫀..🫂)    anatomical heart..people hugging
 1FAC3..1FAC5  ; Extended_Pictographic# E14.0  [3] (🫃..🫅)    pregnant man..person with crown
 1FAC6         ; Extended_Pictographic# E16.0  [1] (🫆)       fingerprint
-1FAC7..1FACD  ; Extended_Pictographic# E0.0   [7] (🫇..🫍)    <reserved-1FAC7>..<reserved-1FACD>
+1FAC7         ; Extended_Pictographic# E0.0   [1] (🫇)       <reserved-1FAC7>
+1FAC8         ; Extended_Pictographic# E17.0  [1] (🫈)       hairy creature
+1FAC9..1FACB  ; Extended_Pictographic# E0.0   [3] (🫉..🫋)    <reserved-1FAC9>..<reserved-1FACB>
+1FACC         ; Extended_Pictographic# E18.0  [1] (🫌)       monarch butterfly
+1FACD         ; Extended_Pictographic# E17.0  [1] (🫍)       orca
 1FACE..1FACF  ; Extended_Pictographic# E15.0  [2] (🫎..🫏)    moose..donkey
 1FAD0..1FAD6  ; Extended_Pictographic# E13.0  [7] (🫐..🫖)    blueberries..teapot
 1FAD7..1FAD9  ; Extended_Pictographic# E14.0  [3] (🫗..🫙)    pouring liquid..jar
 1FADA..1FADB  ; Extended_Pictographic# E15.0  [2] (🫚..🫛)    ginger root..pea pod
 1FADC         ; Extended_Pictographic# E16.0  [1] (🫜)       root vegetable
-1FADD..1FADE  ; Extended_Pictographic# E0.0   [2] (🫝..🫞)    <reserved-1FADD>..<reserved-1FADE>
+1FADD         ; Extended_Pictographic# E18.0  [1] (🫝)       pickle
+1FADE         ; Extended_Pictographic# E0.0   [1] (🫞)       <reserved-1FADE>
 1FADF         ; Extended_Pictographic# E16.0  [1] (🫟)       splatter
 1FAE0..1FAE7  ; Extended_Pictographic# E14.0  [8] (🫠..🫧)    melting face..bubbles
 1FAE8         ; Extended_Pictographic# E15.0  [1] (🫨)       shaking face
 1FAE9         ; Extended_Pictographic# E16.0  [1] (🫩)       face with bags under eyes
-1FAEA..1FAEF  ; Extended_Pictographic# E0.0   [6] (🫪..🫯)    <reserved-1FAEA>..<reserved-1FAEF>
+1FAEA         ; Extended_Pictographic# E17.0  [1] (🫪)       distorted face
+1FAEB         ; Extended_Pictographic# E18.0  [1] (🫫)       cracking face
+1FAEC..1FAEE  ; Extended_Pictographic# E0.0   [3] (🫬..🫮)    <reserved-1FAEC>..<reserved-1FAEE>
+1FAEF         ; Extended_Pictographic# E17.0  [1] (🫯)       fight cloud
 1FAF0..1FAF6  ; Extended_Pictographic# E14.0  [7] (🫰..🫶)    hand with index finger and thumb crossed..heart hands
 1FAF7..1FAF8  ; Extended_Pictographic# E15.0  [2] (🫷..🫸)    leftwards pushing hand..rightwards pushing hand
-1FAF9..1FAFF  ; Extended_Pictographic# E0.0   [7] (🫹..🫿)    <reserved-1FAF9>..<reserved-1FAFF>
+1FAF9..1FAFA  ; Extended_Pictographic# E18.0  [2] (🫹..🫺)    leftwards thumb sign..rightwards thumb sign
+1FAFB..1FAFF  ; Extended_Pictographic# E0.0   [5] (🫻..🫿)    <reserved-1FAFB>..<reserved-1FAFF>
 1FC00..1FFFD  ; Extended_Pictographic# E0.0[1022] (🰀..🿽)    <reserved-1FC00>..<reserved-1FFFD>
 
-# Total elements: 3537
+# Total elements: 2830
 
 #EOF

>From 78186eaeaa2a6672cdb4f0caa031d746627f5d87 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Sat, 20 Jun 2026 07:08:27 -0400
Subject: [PATCH 22/33] Update assertion for data lower value

---
 libcxx/utils/generate_escaped_output_table.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/utils/generate_escaped_output_table.py b/libcxx/utils/generate_escaped_output_table.py
index aa918a6977371..744835129161d 100755
--- a/libcxx/utils/generate_escaped_output_table.py
+++ b/libcxx/utils/generate_escaped_output_table.py
@@ -330,7 +330,7 @@ def generate_data_tables() -> str:
     # The exact entries may differ between Unicode versions. When these numbers
     # change the test needs to be updated too.
     #   libcxx/test/libcxx/utilities/format/format.string/format.string.std/escaped_output.pass.cpp
-    assert (data[-2].lower) == 0x323B0, hex(data[-2].lower)
+    assert (data[-2].lower) == 0x3FC40, hex(data[-2].lower)
     assert (data[-2].upper) == 0xE00FF, hex(data[-2].upper)
     assert (data[-1].lower) == 0xE01F0, hex(data[-1].lower)
     assert (data[-1].upper) == 0x10FFFF, hex(data[-1].upper)

>From 92d92773557c4948e7338a9bad3cb6b107dc8f39 Mon Sep 17 00:00:00 2001
From: unknown <lol>
Date: Sat, 20 Jun 2026 17:32:50 -0400
Subject: [PATCH 23/33] Regenerate requisite headers derived from newly added
 UCDs

---
 .../include/__format/escaped_output_table.h   |    2 +-
 .../extended_grapheme_cluster_table.h         |  159 +-
 .../__format/indic_conjunct_break_table.h     |  120 +-
 .../include/__format/width_estimation_table.h |   30 +-
 .../extended_grapheme_cluster.h               | 4260 +++++++----------
 5 files changed, 2013 insertions(+), 2558 deletions(-)

diff --git a/libcxx/include/__format/escaped_output_table.h b/libcxx/include/__format/escaped_output_table.h
index c0d401d9a00c1..a8e301ab092f9 100644
--- a/libcxx/include/__format/escaped_output_table.h
+++ b/libcxx/include/__format/escaped_output_table.h
@@ -98,7 +98,7 @@ namespace __escaped_output_table {
 /// - Unassigned.
 ///
 /// The data is generated from
-/// - https://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedGeneralCategory.txt
+/// - https://www.unicode.org/Public/18.0.0/ucd/extracted/DerivedGeneralCategory.txt
 ///
 /// The table is similar to the table
 ///  __extended_grapheme_custer_property_boundary::__entries
diff --git a/libcxx/include/__format/extended_grapheme_cluster_table.h b/libcxx/include/__format/extended_grapheme_cluster_table.h
index 6da07862d41e6..67b2796f328a1 100644
--- a/libcxx/include/__format/extended_grapheme_cluster_table.h
+++ b/libcxx/include/__format/extended_grapheme_cluster_table.h
@@ -125,7 +125,7 @@ enum class __property : uint8_t {
 /// following benchmark.
 /// libcxx/benchmarks/std_format_spec_string_unicode.bench.cpp
 // clang-format off
-_LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
+_LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1590] = {
     0x00000091,
     0x00005005,
     0x00005811,
@@ -141,7 +141,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x002df802,
     0x002e0812,
     0x002e2012,
-    0x002e3802,
+    0x002e3822,
     0x00300058,
     0x003080a2,
     0x0030e001,
@@ -221,7 +221,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x005a381a,
     0x005a581a,
     0x005a6802,
-    0x005aa822,
+    0x005a9842,
     0x005b1012,
     0x005c1002,
     0x005df002,
@@ -354,7 +354,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x00d3685a,
     0x00d39892,
     0x00d3f802,
-    0x00d581e2,
+    0x00d58402,
     0x00d80032,
     0x00d8200a,
     0x00d9a092,
@@ -402,7 +402,6 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x010d4813,
     0x0118d013,
     0x01194003,
-    0x011c4003,
     0x011e7803,
     0x011f48a3,
     0x011fc023,
@@ -411,11 +410,49 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x012db003,
     0x012e0003,
     0x012fd833,
-    0x01300053,
-    0x013038b3,
-    0x0130a713,
-    0x01348753,
-    0x013840a3,
+    0x01300043,
+    0x01307003,
+    0x01308803,
+    0x0130a013,
+    0x0130c003,
+    0x0130e803,
+    0x01310003,
+    0x01311013,
+    0x01313003,
+    0x01315003,
+    0x01317013,
+    0x0131c023,
+    0x01320003,
+    0x01321003,
+    0x013240b3,
+    0x0132f813,
+    0x01331803,
+    0x01332813,
+    0x01334003,
+    0x0133d803,
+    0x0133f013,
+    0x01349053,
+    0x0134c803,
+    0x0134d813,
+    0x01350013,
+    0x01353803,
+    0x01355013,
+    0x01358013,
+    0x0135e813,
+    0x01362013,
+    0x01364003,
+    0x01367013,
+    0x01368803,
+    0x01369813,
+    0x01374813,
+    0x01378053,
+    0x0137b833,
+    0x0137e803,
+    0x01381003,
+    0x01382803,
+    0x01384053,
+    0x01387803,
+    0x01389003,
     0x0138a003,
     0x0138b003,
     0x0138e803,
@@ -428,7 +465,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x013a7003,
     0x013a9823,
     0x013ab803,
-    0x013b1843,
+    0x013b1813,
     0x013ca823,
     0x013d0803,
     0x013d8003,
@@ -1322,7 +1359,8 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x08692032,
     0x086b4842,
     0x08755812,
-    0x0877e032,
+    0x08765842,
+    0x087780f2,
     0x087a30a2,
     0x087c1032,
     0x0880000a,
@@ -1456,7 +1494,6 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x08d00892,
     0x08d19852,
     0x08d1c80a,
-    0x08d1d008,
     0x08d1d832,
     0x08d23802,
     0x08d28852,
@@ -1466,6 +1503,12 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x08d450c2,
     0x08d4b80a,
     0x08d4c012,
+    0x08db0002,
+    0x08db080a,
+    0x08db1022,
+    0x08db280a,
+    0x08db3002,
+    0x08db380a,
     0x08e1780a,
     0x08e18062,
     0x08e1c052,
@@ -1490,6 +1533,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x08eca802,
     0x08ecb00a,
     0x08ecb802,
+    0x08ef8002,
     0x08f79812,
     0x08f7a81a,
     0x08f80012,
@@ -1510,6 +1554,8 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x0b598062,
     0x0b6b180c,
     0x0b6b383c,
+    0x0b6cc002,
+    0x0b6ce802,
     0x0b7a7802,
     0x0b7a8b6a,
     0x0b7c7832,
@@ -1519,6 +1565,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x0de50031,
     0x0e7802d2,
     0x0e798162,
+    0x0e893812,
     0x0e8b2842,
     0x0e8b6852,
     0x0e8b9871,
@@ -1526,6 +1573,10 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x0e8c2862,
     0x0e8d5032,
     0x0e921022,
+    0x0e928022,
+    0x0e92d812,
+    0x0e92f802,
+    0x0e940012,
     0x0ed00362,
     0x0ed1db12,
     0x0ed3a802,
@@ -1543,48 +1594,86 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[1501] = {
     0x0f176032,
     0x0f276032,
     0x0f2f7012,
+    0x0f371802,
+    0x0f373002,
+    0x0f377012,
+    0x0f37a802,
     0x0f468062,
     0x0f4a2062,
-    0x0f8007f3,
-    0x0f8407f3,
-    0x0f886823,
-    0x0f897803,
-    0x0f8b6053,
+    0x0f802003,
+    0x0f816033,
+    0x0f84a0b3,
+    0x0f857813,
+    0x0f860003,
+    0x0f867813,
+    0x0f87b093,
+    0x0f8b8013,
     0x0f8bf013,
     0x0f8c7003,
     0x0f8c8893,
-    0x0f8d6b83,
+    0x0f8d7b63,
     0x0f8f3199,
     0x0f9008e3,
     0x0f90d003,
     0x0f917803,
     0x0f919083,
     0x0f91e033,
-    0x0f924ff3,
-    0x0f964ff3,
-    0x0f9a4ff3,
-    0x0f9e4b13,
+    0x0f924963,
+    0x0f9337f3,
+    0x0f9733b3,
+    0x0f9926f3,
+    0x0f9cb013,
+    0x0f9cc823,
+    0x0f9cf523,
+    0x0f9f9823,
+    0x0f9fb833,
     0x0f9fd842,
     0x0fa007f3,
-    0x0fa407f3,
-    0x0fa803d3,
-    0x0faa37f3,
-    0x0fae37f3,
-    0x0fb23093,
-    0x0fb407f3,
-    0x0fbba0b3,
-    0x0fbeaaa3,
+    0x0fa407d3,
+    0x0fa7fbe3,
+    0x0faa4853,
+    0x0faa8173,
+    0x0fab7813,
+    0x0fab9873,
+    0x0fac3803,
+    0x0fac5033,
+    0x0fac8003,
+    0x0faca813,
+    0x0fad2013,
+    0x0fad4003,
+    0x0fad8813,
+    0x0fade003,
+    0x0fae1023,
+    0x0fae8823,
+    0x0faee023,
+    0x0faf0803,
+    0x0faf1803,
+    0x0faf4003,
+    0x0faf7803,
+    0x0faf9803,
+    0x0fafd553,
+    0x0fb40453,
+    0x0fb65873,
+    0x0fb6a903,
+    0x0fb74803,
+    0x0fb75853,
+    0x0fb798c3,
+    0x0fbee143,
     0x0fc06033,
     0x0fc24073,
     0x0fc2d053,
     0x0fc44073,
-    0x0fc57513,
+    0x0fc57013,
+    0x0fc5e033,
+    0x0fc610d3,
+    0x0fc6ca63,
     0x0fc862e3,
     0x0fc9e093,
     0x0fca3ff3,
-    0x0fce3ff3,
-    0x0fd23ff3,
-    0x0fd63b83,
+    0x0fce3b83,
+    0x0fd2c073,
+    0x0fd377f3,
+    0x0fd77113,
     0x0fe007f3,
     0x0fe407f3,
     0x0fe807f3,
diff --git a/libcxx/include/__format/indic_conjunct_break_table.h b/libcxx/include/__format/indic_conjunct_break_table.h
index d85782d73286b..e96cd6cba9ec4 100644
--- a/libcxx/include/__format/indic_conjunct_break_table.h
+++ b/libcxx/include/__format/indic_conjunct_break_table.h
@@ -107,14 +107,14 @@ enum class __property : uint8_t {
 /// following benchmark.
 /// libcxx/benchmarks/std_format_spec_string_unicode.bench.cpp
 // clang-format off
-_LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
+_LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[485] = {
     0x001801bd,
     0x00241819,
     0x002c88b1,
     0x002df801,
     0x002e0805,
     0x002e2005,
-    0x002e3801,
+    0x002e3809,
     0x00308029,
     0x00325851,
     0x00338001,
@@ -188,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x0059f005,
     0x005a080d,
     0x005a6802,
-    0x005aa809,
+    0x005a9811,
     0x005ae004,
     0x005af800,
     0x005b1005,
@@ -248,26 +248,39 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x007c6829,
     0x007cc88d,
     0x007e3001,
+    0x008000a8,
     0x0081680d,
     0x00819015,
-    0x0081c805,
+    0x0081c802,
+    0x0081d001,
     0x0081e805,
+    0x0081f800,
+    0x00828014,
     0x0082c005,
+    0x0082d00c,
     0x0082f009,
+    0x00830800,
+    0x00832804,
+    0x00837008,
     0x0083880d,
+    0x0083a830,
     0x00841001,
     0x00842805,
     0x00846801,
+    0x00847000,
     0x0084e801,
     0x009ae809,
     0x00b8900d,
     0x00b99009,
     0x00ba9005,
     0x00bb9005,
+    0x00bc00cc,
     0x00bda005,
     0x00bdb819,
     0x00be3001,
-    0x00be4829,
+    0x00be4821,
+    0x00be9002,
+    0x00be9801,
     0x00bee801,
     0x00c05809,
     0x00c07801,
@@ -279,21 +292,31 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x00c9c809,
     0x00d0b805,
     0x00d0d801,
+    0x00d100d0,
     0x00d2b001,
     0x00d2c019,
-    0x00d30001,
+    0x00d30002,
     0x00d31001,
     0x00d3281d,
     0x00d39825,
     0x00d3f801,
-    0x00d58079,
+    0x00d58101,
     0x00d8000d,
+    0x00d85804,
+    0x00d89880,
     0x00d9a025,
-    0x00da1009,
+    0x00da1005,
+    0x00da2002,
+    0x00da281c,
     0x00db5821,
     0x00dc0005,
+    0x00dc1874,
     0x00dd100d,
-    0x00dd4015,
+    0x00dd4009,
+    0x00dd5802,
+    0x00dd6005,
+    0x00dd7004,
+    0x00ddd808,
     0x00df3001,
     0x00df4005,
     0x00df6801,
@@ -305,6 +328,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x00e71019,
     0x00e76801,
     0x00e7a001,
+    0x00e7a806,
     0x00e7c005,
     0x00ee00fd,
     0x01006801,
@@ -330,24 +354,35 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x054a3829,
     0x054a9801,
     0x054c0009,
+    0x054c4808,
+    0x054c788c,
     0x054d9801,
     0x054db00d,
     0x054de005,
-    0x054e0001,
+    0x054e0002,
+    0x054f0010,
     0x054f2801,
+    0x054f3820,
+    0x054fd010,
     0x05514815,
     0x05518805,
     0x0551a805,
     0x05521801,
     0x05526001,
+    0x0553003c,
+    0x05538808,
+    0x0553d000,
     0x0553e001,
+    0x0553f004,
     0x05558001,
     0x05559009,
     0x0555b805,
     0x0555f005,
     0x05560801,
+    0x05570028,
     0x05576005,
-    0x0557b001,
+    0x0557b002,
+    0x055e0068,
     0x055f2801,
     0x055f4001,
     0x055f6801,
@@ -358,16 +393,21 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x080fe801,
     0x08170001,
     0x081bb011,
+    0x08500000,
     0x08500809,
     0x08502805,
     0x0850600d,
+    0x0850800c,
+    0x0850a808,
+    0x0850c870,
     0x0851c009,
-    0x0851f801,
+    0x0851f802,
     0x08572805,
     0x0869200d,
     0x086b4811,
     0x08755805,
-    0x0877e00d,
+    0x08765811,
+    0x0877803d,
     0x087a3029,
     0x087c100d,
     0x08800801,
@@ -379,8 +419,13 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x0885c805,
     0x08861001,
     0x08880009,
+    0x0888188c,
     0x08893811,
-    0x0889681d,
+    0x08896815,
+    0x08899802,
+    0x0889a001,
+    0x088a2000,
+    0x088a3800,
     0x088b9801,
     0x088c0005,
     0x088db021,
@@ -401,12 +446,17 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x089ab801,
     0x089b3019,
     0x089b8011,
+    0x089c0024,
+    0x089c5800,
+    0x089c7000,
+    0x089c8094,
     0x089dc001,
     0x089dd815,
     0x089e1001,
     0x089e2801,
     0x089e3809,
-    0x089e7009,
+    0x089e7005,
+    0x089e8002,
     0x089e9001,
     0x089f0805,
     0x08a1c01d,
@@ -436,20 +486,36 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x08b93811,
     0x08c17821,
     0x08c1c805,
+    0x08c80018,
+    0x08c84800,
+    0x08c8601c,
+    0x08c8a804,
+    0x08c8c05c,
     0x08c98001,
-    0x08c9d80d,
+    0x08c9d809,
+    0x08c9f002,
     0x08ca1801,
     0x08cea00d,
     0x08ced005,
     0x08cf0001,
+    0x08d00000,
     0x08d00825,
+    0x08d0589c,
     0x08d19815,
+    0x08d1d002,
     0x08d1d80d,
-    0x08d23801,
+    0x08d23802,
+    0x08d28000,
     0x08d28815,
     0x08d2c809,
+    0x08d2e09c,
     0x08d45031,
-    0x08d4c005,
+    0x08d4c001,
+    0x08d4c802,
+    0x08d85000,
+    0x08db0001,
+    0x08db1009,
+    0x08db3001,
     0x08e18019,
     0x08e1c015,
     0x08e1f801,
@@ -465,10 +531,15 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x08ec8005,
     0x08eca801,
     0x08ecb801,
+    0x08ef8001,
+    0x08ef8800,
     0x08f79805,
     0x08f80005,
+    0x08f82030,
+    0x08f89084,
     0x08f9b011,
-    0x08fa0009,
+    0x08fa0005,
+    0x08fa1002,
     0x08fad001,
     0x09a20001,
     0x09a23839,
@@ -476,6 +547,8 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x0b096809,
     0x0b578011,
     0x0b598019,
+    0x0b6cc001,
+    0x0b6ce801,
     0x0b7a7801,
     0x0b7c780d,
     0x0b7f2001,
@@ -483,12 +556,17 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x0de4e805,
     0x0e7800b5,
     0x0e798059,
+    0x0e893805,
     0x0e8b2811,
     0x0e8b6815,
     0x0e8bd81d,
     0x0e8c2819,
     0x0e8d500d,
     0x0e921009,
+    0x0e928009,
+    0x0e92d805,
+    0x0e92f801,
+    0x0e940005,
     0x0ed000d9,
     0x0ed1d8c5,
     0x0ed3a801,
@@ -506,6 +584,10 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[403] = {
     0x0f17600d,
     0x0f27600d,
     0x0f2f7005,
+    0x0f371801,
+    0x0f373001,
+    0x0f377005,
+    0x0f37a801,
     0x0f468019,
     0x0f4a2019,
     0x0f9fd811,
diff --git a/libcxx/include/__format/width_estimation_table.h b/libcxx/include/__format/width_estimation_table.h
index ae10a77a5b980..c4625818a6192 100644
--- a/libcxx/include/__format/width_estimation_table.h
+++ b/libcxx/include/__format/width_estimation_table.h
@@ -120,7 +120,7 @@ namespace __width_estimation_table {
 /// - bits [0, 13] The size of the range, allowing 16384 elements.
 /// - bits [14, 31] The lower bound code point of the range. The upper bound of
 ///   the range is lower bound + size.
-_LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[110] = {
+_LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[114] = {
     0x0440005f /* 00001100 - 0000115f [   96] */, //
     0x08c68001 /* 0000231a - 0000231b [    2] */, //
     0x08ca4001 /* 00002329 - 0000232a [    2] */, //
@@ -182,18 +182,20 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[110] = {
     0x3fc0405f /* 0000ff01 - 0000ff60 [   96] */, //
     0x3ff80006 /* 0000ffe0 - 0000ffe6 [    7] */, //
     0x5bf80004 /* 00016fe0 - 00016fe4 [    5] */, //
-    0x5bfc0001 /* 00016ff0 - 00016ff1 [    2] */, //
-    0x5c0017f7 /* 00017000 - 000187f7 [ 6136] */, //
-    0x620004d5 /* 00018800 - 00018cd5 [ 1238] */, //
-    0x633fc009 /* 00018cff - 00018d08 [   10] */, //
+    0x5bfc0006 /* 00016ff0 - 00016ff6 [    7] */, //
+    0x5c001cda /* 00017000 - 00018cda [ 7387] */, //
+    0x633fc021 /* 00018cff - 00018d20 [   34] */, //
+    0x63600072 /* 00018d80 - 00018df2 [  115] */, //
+    0x63800391 /* 00018e00 - 00019191 [  914] */, //
+    0x64680032 /* 000191a0 - 000191d2 [   51] */, //
     0x6bfc0003 /* 0001aff0 - 0001aff3 [    4] */, //
     0x6bfd4006 /* 0001aff5 - 0001affb [    7] */, //
     0x6bff4001 /* 0001affd - 0001affe [    2] */, //
-    0x6c000122 /* 0001b000 - 0001b122 [  291] */, //
+    0x6c000128 /* 0001b000 - 0001b128 [  297] */, //
     0x6c4c8000 /* 0001b132 - 0001b132 [    1] */, //
     0x6c540002 /* 0001b150 - 0001b152 [    3] */, //
     0x6c554000 /* 0001b155 - 0001b155 [    1] */, //
-    0x6c590003 /* 0001b164 - 0001b167 [    4] */, //
+    0x6c590004 /* 0001b164 - 0001b168 [    5] */, //
     0x6c5c018b /* 0001b170 - 0001b2fb [  396] */, //
     0x74c00056 /* 0001d300 - 0001d356 [   87] */, //
     0x74d80016 /* 0001d360 - 0001d376 [   23] */, //
@@ -201,6 +203,7 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[110] = {
     0x7c33c000 /* 0001f0cf - 0001f0cf [    1] */, //
     0x7c638000 /* 0001f18e - 0001f18e [    1] */, //
     0x7c644009 /* 0001f191 - 0001f19a [   10] */, //
+    0x7c6b8000 /* 0001f1ae - 0001f1ae [    1] */, //
     0x7c800002 /* 0001f200 - 0001f202 [    3] */, //
     0x7c84002b /* 0001f210 - 0001f23b [   44] */, //
     0x7c900008 /* 0001f240 - 0001f248 [    9] */, //
@@ -210,19 +213,20 @@ _LIBCPP_HIDE_FROM_ABI inline constexpr uint32_t __entries[110] = {
     0x7da00045 /* 0001f680 - 0001f6c5 [   70] */, //
     0x7db30000 /* 0001f6cc - 0001f6cc [    1] */, //
     0x7db40002 /* 0001f6d0 - 0001f6d2 [    3] */, //
-    0x7db54002 /* 0001f6d5 - 0001f6d7 [    3] */, //
+    0x7db54004 /* 0001f6d5 - 0001f6d9 [    5] */, //
     0x7db70003 /* 0001f6dc - 0001f6df [    4] */, //
     0x7dbac001 /* 0001f6eb - 0001f6ec [    2] */, //
     0x7dbd0008 /* 0001f6f4 - 0001f6fc [    9] */, //
+    0x7df68000 /* 0001f7da - 0001f7da [    1] */, //
     0x7df8000b /* 0001f7e0 - 0001f7eb [   12] */, //
     0x7dfc0000 /* 0001f7f0 - 0001f7f0 [    1] */, //
     0x7e4000ff /* 0001f900 - 0001f9ff [  256] */, //
     0x7e9c000c /* 0001fa70 - 0001fa7c [   13] */, //
-    0x7ea00009 /* 0001fa80 - 0001fa89 [   10] */, //
-    0x7ea3c037 /* 0001fa8f - 0001fac6 [   56] */, //
-    0x7eb3800e /* 0001face - 0001fadc [   15] */, //
-    0x7eb7c00a /* 0001fadf - 0001fae9 [   11] */, //
-    0x7ebc0008 /* 0001faf0 - 0001faf8 [    9] */, //
+    0x7ea00046 /* 0001fa80 - 0001fac6 [   71] */, //
+    0x7eb20000 /* 0001fac8 - 0001fac8 [    1] */, //
+    0x7eb30011 /* 0001facc - 0001fadd [   18] */, //
+    0x7eb7c00c /* 0001fadf - 0001faeb [   13] */, //
+    0x7ebbc00b /* 0001faef - 0001fafa [   12] */, //
     0x80003fff /* 00020000 - 00023fff [16384] */, //
     0x90003fff /* 00024000 - 00027fff [16384] */, //
     0xa0003fff /* 00028000 - 0002bfff [16384] */, //
diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.h b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.h
index 9664622ab4e40..c7b0abbe0bced 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.h
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.h
@@ -82,69 +82,27 @@ struct data {
 };
 
 /// The data for UTF-8.
-std::array<data<char>, 1093> data_utf8 = {{
-     {"\U00000020\U00000020", {32, 32}, {1, 2}},
-     {"\U00000020\U00000308\U00000020", {32, 32}, {3, 4}},
-     {"\U00000020\U0000000d", {32, 13}, {1, 2}},
-     {"\U00000020\U00000308\U0000000d", {32, 13}, {3, 4}},
-     {"\U00000020\U0000000a", {32, 10}, {1, 2}},
-     {"\U00000020\U00000308\U0000000a", {32, 10}, {3, 4}},
-     {"\U00000020\U00000001", {32, 1}, {1, 2}},
-     {"\U00000020\U00000308\U00000001", {32, 1}, {3, 4}},
-     {"\U00000020\U0000200c", {32}, {4}},
-     {"\U00000020\U00000308\U0000200c", {32}, {6}},
-     {"\U00000020\U0001f1e6", {32, 127462}, {1, 5}},
-     {"\U00000020\U00000308\U0001f1e6", {32, 127462}, {3, 7}},
-     {"\U00000020\U00000600", {32, 1536}, {1, 3}},
-     {"\U00000020\U00000308\U00000600", {32, 1536}, {3, 5}},
-     {"\U00000020\U00000a03", {32}, {4}},
-     {"\U00000020\U00000308\U00000a03", {32}, {6}},
-     {"\U00000020\U00001100", {32, 4352}, {1, 4}},
-     {"\U00000020\U00000308\U00001100", {32, 4352}, {3, 6}},
-     {"\U00000020\U00001160", {32, 4448}, {1, 4}},
-     {"\U00000020\U00000308\U00001160", {32, 4448}, {3, 6}},
-     {"\U00000020\U000011a8", {32, 4520}, {1, 4}},
-     {"\U00000020\U00000308\U000011a8", {32, 4520}, {3, 6}},
-     {"\U00000020\U0000ac00", {32, 44032}, {1, 4}},
-     {"\U00000020\U00000308\U0000ac00", {32, 44032}, {3, 6}},
-     {"\U00000020\U0000ac01", {32, 44033}, {1, 4}},
-     {"\U00000020\U00000308\U0000ac01", {32, 44033}, {3, 6}},
-     {"\U00000020\U00000903", {32}, {4}},
-     {"\U00000020\U00000308\U00000903", {32}, {6}},
-     {"\U00000020\U00000904", {32, 2308}, {1, 4}},
-     {"\U00000020\U00000308\U00000904", {32, 2308}, {3, 6}},
-     {"\U00000020\U00000d4e", {32, 3406}, {1, 4}},
-     {"\U00000020\U00000308\U00000d4e", {32, 3406}, {3, 6}},
-     {"\U00000020\U00000915", {32, 2325}, {1, 4}},
-     {"\U00000020\U00000308\U00000915", {32, 2325}, {3, 6}},
-     {"\U00000020\U0000231a", {32, 8986}, {1, 4}},
-     {"\U00000020\U00000308\U0000231a", {32, 8986}, {3, 6}},
-     {"\U00000020\U00000300", {32}, {3}},
-     {"\U00000020\U00000308\U00000300", {32}, {5}},
-     {"\U00000020\U00000900", {32}, {4}},
-     {"\U00000020\U00000308\U00000900", {32}, {6}},
-     {"\U00000020\U0000094d", {32}, {4}},
-     {"\U00000020\U00000308\U0000094d", {32}, {6}},
-     {"\U00000020\U0000200d", {32}, {4}},
-     {"\U00000020\U00000308\U0000200d", {32}, {6}},
-     {"\U00000020\U00000378", {32, 888}, {1, 3}},
-     {"\U00000020\U00000308\U00000378", {32, 888}, {3, 5}},
-     {"\U0000000d\U00000020", {13, 32}, {1, 2}},
-     {"\U0000000d\U00000308\U00000020", {13, 776, 32}, {1, 3, 4}},
+std::array<data<char>, 853> data_utf8 = {{
      {"\U0000000d\U0000000d", {13, 13}, {1, 2}},
      {"\U0000000d\U00000308\U0000000d", {13, 776, 13}, {1, 3, 4}},
      {"\U0000000d\U0000000a", {13}, {2}},
      {"\U0000000d\U00000308\U0000000a", {13, 776, 10}, {1, 3, 4}},
-     {"\U0000000d\U00000001", {13, 1}, {1, 2}},
-     {"\U0000000d\U00000308\U00000001", {13, 776, 1}, {1, 3, 4}},
+     {"\U0000000d\U00000000", {13, 0}, {1, 2}},
+     {"\U0000000d\U00000308\U00000000", {13, 776, 0}, {1, 3, 4}},
+     {"\U0000000d\U0000094d", {13, 2381}, {1, 4}},
+     {"\U0000000d\U00000308\U0000094d", {13, 776}, {1, 6}},
+     {"\U0000000d\U00000300", {13, 768}, {1, 3}},
+     {"\U0000000d\U00000308\U00000300", {13, 776}, {1, 5}},
      {"\U0000000d\U0000200c", {13, 8204}, {1, 4}},
      {"\U0000000d\U00000308\U0000200c", {13, 776}, {1, 6}},
+     {"\U0000000d\U0000200d", {13, 8205}, {1, 4}},
+     {"\U0000000d\U00000308\U0000200d", {13, 776}, {1, 6}},
      {"\U0000000d\U0001f1e6", {13, 127462}, {1, 5}},
      {"\U0000000d\U00000308\U0001f1e6", {13, 776, 127462}, {1, 3, 7}},
-     {"\U0000000d\U00000600", {13, 1536}, {1, 3}},
-     {"\U0000000d\U00000308\U00000600", {13, 776, 1536}, {1, 3, 5}},
-     {"\U0000000d\U00000a03", {13, 2563}, {1, 4}},
-     {"\U0000000d\U00000308\U00000a03", {13, 776}, {1, 6}},
+     {"\U0000000d\U000006dd", {13, 1757}, {1, 3}},
+     {"\U0000000d\U00000308\U000006dd", {13, 776, 1757}, {1, 3, 5}},
+     {"\U0000000d\U00000903", {13, 2307}, {1, 4}},
+     {"\U0000000d\U00000308\U00000903", {13, 776}, {1, 6}},
      {"\U0000000d\U00001100", {13, 4352}, {1, 4}},
      {"\U0000000d\U00000308\U00001100", {13, 776, 4352}, {1, 3, 6}},
      {"\U0000000d\U00001160", {13, 4448}, {1, 4}},
@@ -155,42 +113,36 @@ std::array<data<char>, 1093> data_utf8 = {{
      {"\U0000000d\U00000308\U0000ac00", {13, 776, 44032}, {1, 3, 6}},
      {"\U0000000d\U0000ac01", {13, 44033}, {1, 4}},
      {"\U0000000d\U00000308\U0000ac01", {13, 776, 44033}, {1, 3, 6}},
-     {"\U0000000d\U00000903", {13, 2307}, {1, 4}},
-     {"\U0000000d\U00000308\U00000903", {13, 776}, {1, 6}},
-     {"\U0000000d\U00000904", {13, 2308}, {1, 4}},
-     {"\U0000000d\U00000308\U00000904", {13, 776, 2308}, {1, 3, 6}},
-     {"\U0000000d\U00000d4e", {13, 3406}, {1, 4}},
-     {"\U0000000d\U00000308\U00000d4e", {13, 776, 3406}, {1, 3, 6}},
+     {"\U0000000d\U00001cf5", {13, 7413}, {1, 4}},
+     {"\U0000000d\U00000308\U00001cf5", {13, 776, 7413}, {1, 3, 6}},
      {"\U0000000d\U00000915", {13, 2325}, {1, 4}},
      {"\U0000000d\U00000308\U00000915", {13, 776, 2325}, {1, 3, 6}},
-     {"\U0000000d\U0000231a", {13, 8986}, {1, 4}},
-     {"\U0000000d\U00000308\U0000231a", {13, 776, 8986}, {1, 3, 6}},
-     {"\U0000000d\U00000300", {13, 768}, {1, 3}},
-     {"\U0000000d\U00000308\U00000300", {13, 776}, {1, 5}},
-     {"\U0000000d\U00000900", {13, 2304}, {1, 4}},
-     {"\U0000000d\U00000308\U00000900", {13, 776}, {1, 6}},
-     {"\U0000000d\U0000094d", {13, 2381}, {1, 4}},
-     {"\U0000000d\U00000308\U0000094d", {13, 776}, {1, 6}},
-     {"\U0000000d\U0000200d", {13, 8205}, {1, 4}},
-     {"\U0000000d\U00000308\U0000200d", {13, 776}, {1, 6}},
+     {"\U0000000d\U000000a9", {13, 169}, {1, 3}},
+     {"\U0000000d\U00000308\U000000a9", {13, 776, 169}, {1, 3, 5}},
+     {"\U0000000d\U00000020", {13, 32}, {1, 2}},
+     {"\U0000000d\U00000308\U00000020", {13, 776, 32}, {1, 3, 4}},
      {"\U0000000d\U00000378", {13, 888}, {1, 3}},
      {"\U0000000d\U00000308\U00000378", {13, 776, 888}, {1, 3, 5}},
-     {"\U0000000a\U00000020", {10, 32}, {1, 2}},
-     {"\U0000000a\U00000308\U00000020", {10, 776, 32}, {1, 3, 4}},
      {"\U0000000a\U0000000d", {10, 13}, {1, 2}},
      {"\U0000000a\U00000308\U0000000d", {10, 776, 13}, {1, 3, 4}},
      {"\U0000000a\U0000000a", {10, 10}, {1, 2}},
      {"\U0000000a\U00000308\U0000000a", {10, 776, 10}, {1, 3, 4}},
-     {"\U0000000a\U00000001", {10, 1}, {1, 2}},
-     {"\U0000000a\U00000308\U00000001", {10, 776, 1}, {1, 3, 4}},
+     {"\U0000000a\U00000000", {10, 0}, {1, 2}},
+     {"\U0000000a\U00000308\U00000000", {10, 776, 0}, {1, 3, 4}},
+     {"\U0000000a\U0000094d", {10, 2381}, {1, 4}},
+     {"\U0000000a\U00000308\U0000094d", {10, 776}, {1, 6}},
+     {"\U0000000a\U00000300", {10, 768}, {1, 3}},
+     {"\U0000000a\U00000308\U00000300", {10, 776}, {1, 5}},
      {"\U0000000a\U0000200c", {10, 8204}, {1, 4}},
      {"\U0000000a\U00000308\U0000200c", {10, 776}, {1, 6}},
+     {"\U0000000a\U0000200d", {10, 8205}, {1, 4}},
+     {"\U0000000a\U00000308\U0000200d", {10, 776}, {1, 6}},
      {"\U0000000a\U0001f1e6", {10, 127462}, {1, 5}},
      {"\U0000000a\U00000308\U0001f1e6", {10, 776, 127462}, {1, 3, 7}},
-     {"\U0000000a\U00000600", {10, 1536}, {1, 3}},
-     {"\U0000000a\U00000308\U00000600", {10, 776, 1536}, {1, 3, 5}},
-     {"\U0000000a\U00000a03", {10, 2563}, {1, 4}},
-     {"\U0000000a\U00000308\U00000a03", {10, 776}, {1, 6}},
+     {"\U0000000a\U000006dd", {10, 1757}, {1, 3}},
+     {"\U0000000a\U00000308\U000006dd", {10, 776, 1757}, {1, 3, 5}},
+     {"\U0000000a\U00000903", {10, 2307}, {1, 4}},
+     {"\U0000000a\U00000308\U00000903", {10, 776}, {1, 6}},
      {"\U0000000a\U00001100", {10, 4352}, {1, 4}},
      {"\U0000000a\U00000308\U00001100", {10, 776, 4352}, {1, 3, 6}},
      {"\U0000000a\U00001160", {10, 4448}, {1, 4}},
@@ -201,88 +153,156 @@ std::array<data<char>, 1093> data_utf8 = {{
      {"\U0000000a\U00000308\U0000ac00", {10, 776, 44032}, {1, 3, 6}},
      {"\U0000000a\U0000ac01", {10, 44033}, {1, 4}},
      {"\U0000000a\U00000308\U0000ac01", {10, 776, 44033}, {1, 3, 6}},
-     {"\U0000000a\U00000903", {10, 2307}, {1, 4}},
-     {"\U0000000a\U00000308\U00000903", {10, 776}, {1, 6}},
-     {"\U0000000a\U00000904", {10, 2308}, {1, 4}},
-     {"\U0000000a\U00000308\U00000904", {10, 776, 2308}, {1, 3, 6}},
-     {"\U0000000a\U00000d4e", {10, 3406}, {1, 4}},
-     {"\U0000000a\U00000308\U00000d4e", {10, 776, 3406}, {1, 3, 6}},
+     {"\U0000000a\U00001cf5", {10, 7413}, {1, 4}},
+     {"\U0000000a\U00000308\U00001cf5", {10, 776, 7413}, {1, 3, 6}},
      {"\U0000000a\U00000915", {10, 2325}, {1, 4}},
      {"\U0000000a\U00000308\U00000915", {10, 776, 2325}, {1, 3, 6}},
-     {"\U0000000a\U0000231a", {10, 8986}, {1, 4}},
-     {"\U0000000a\U00000308\U0000231a", {10, 776, 8986}, {1, 3, 6}},
-     {"\U0000000a\U00000300", {10, 768}, {1, 3}},
-     {"\U0000000a\U00000308\U00000300", {10, 776}, {1, 5}},
-     {"\U0000000a\U00000900", {10, 2304}, {1, 4}},
-     {"\U0000000a\U00000308\U00000900", {10, 776}, {1, 6}},
-     {"\U0000000a\U0000094d", {10, 2381}, {1, 4}},
-     {"\U0000000a\U00000308\U0000094d", {10, 776}, {1, 6}},
-     {"\U0000000a\U0000200d", {10, 8205}, {1, 4}},
-     {"\U0000000a\U00000308\U0000200d", {10, 776}, {1, 6}},
+     {"\U0000000a\U000000a9", {10, 169}, {1, 3}},
+     {"\U0000000a\U00000308\U000000a9", {10, 776, 169}, {1, 3, 5}},
+     {"\U0000000a\U00000020", {10, 32}, {1, 2}},
+     {"\U0000000a\U00000308\U00000020", {10, 776, 32}, {1, 3, 4}},
      {"\U0000000a\U00000378", {10, 888}, {1, 3}},
      {"\U0000000a\U00000308\U00000378", {10, 776, 888}, {1, 3, 5}},
-     {"\U00000001\U00000020", {1, 32}, {1, 2}},
-     {"\U00000001\U00000308\U00000020", {1, 776, 32}, {1, 3, 4}},
-     {"\U00000001\U0000000d", {1, 13}, {1, 2}},
-     {"\U00000001\U00000308\U0000000d", {1, 776, 13}, {1, 3, 4}},
-     {"\U00000001\U0000000a", {1, 10}, {1, 2}},
-     {"\U00000001\U00000308\U0000000a", {1, 776, 10}, {1, 3, 4}},
-     {"\U00000001\U00000001", {1, 1}, {1, 2}},
-     {"\U00000001\U00000308\U00000001", {1, 776, 1}, {1, 3, 4}},
-     {"\U00000001\U0000200c", {1, 8204}, {1, 4}},
-     {"\U00000001\U00000308\U0000200c", {1, 776}, {1, 6}},
-     {"\U00000001\U0001f1e6", {1, 127462}, {1, 5}},
-     {"\U00000001\U00000308\U0001f1e6", {1, 776, 127462}, {1, 3, 7}},
-     {"\U00000001\U00000600", {1, 1536}, {1, 3}},
-     {"\U00000001\U00000308\U00000600", {1, 776, 1536}, {1, 3, 5}},
-     {"\U00000001\U00000a03", {1, 2563}, {1, 4}},
-     {"\U00000001\U00000308\U00000a03", {1, 776}, {1, 6}},
-     {"\U00000001\U00001100", {1, 4352}, {1, 4}},
-     {"\U00000001\U00000308\U00001100", {1, 776, 4352}, {1, 3, 6}},
-     {"\U00000001\U00001160", {1, 4448}, {1, 4}},
-     {"\U00000001\U00000308\U00001160", {1, 776, 4448}, {1, 3, 6}},
-     {"\U00000001\U000011a8", {1, 4520}, {1, 4}},
-     {"\U00000001\U00000308\U000011a8", {1, 776, 4520}, {1, 3, 6}},
-     {"\U00000001\U0000ac00", {1, 44032}, {1, 4}},
-     {"\U00000001\U00000308\U0000ac00", {1, 776, 44032}, {1, 3, 6}},
-     {"\U00000001\U0000ac01", {1, 44033}, {1, 4}},
-     {"\U00000001\U00000308\U0000ac01", {1, 776, 44033}, {1, 3, 6}},
-     {"\U00000001\U00000903", {1, 2307}, {1, 4}},
-     {"\U00000001\U00000308\U00000903", {1, 776}, {1, 6}},
-     {"\U00000001\U00000904", {1, 2308}, {1, 4}},
-     {"\U00000001\U00000308\U00000904", {1, 776, 2308}, {1, 3, 6}},
-     {"\U00000001\U00000d4e", {1, 3406}, {1, 4}},
-     {"\U00000001\U00000308\U00000d4e", {1, 776, 3406}, {1, 3, 6}},
-     {"\U00000001\U00000915", {1, 2325}, {1, 4}},
-     {"\U00000001\U00000308\U00000915", {1, 776, 2325}, {1, 3, 6}},
-     {"\U00000001\U0000231a", {1, 8986}, {1, 4}},
-     {"\U00000001\U00000308\U0000231a", {1, 776, 8986}, {1, 3, 6}},
-     {"\U00000001\U00000300", {1, 768}, {1, 3}},
-     {"\U00000001\U00000308\U00000300", {1, 776}, {1, 5}},
-     {"\U00000001\U00000900", {1, 2304}, {1, 4}},
-     {"\U00000001\U00000308\U00000900", {1, 776}, {1, 6}},
-     {"\U00000001\U0000094d", {1, 2381}, {1, 4}},
-     {"\U00000001\U00000308\U0000094d", {1, 776}, {1, 6}},
-     {"\U00000001\U0000200d", {1, 8205}, {1, 4}},
-     {"\U00000001\U00000308\U0000200d", {1, 776}, {1, 6}},
-     {"\U00000001\U00000378", {1, 888}, {1, 3}},
-     {"\U00000001\U00000308\U00000378", {1, 776, 888}, {1, 3, 5}},
-     {"\U0000200c\U00000020", {8204, 32}, {3, 4}},
-     {"\U0000200c\U00000308\U00000020", {8204, 32}, {5, 6}},
+     {"\U00000000\U0000000d", {0, 13}, {1, 2}},
+     {"\U00000000\U00000308\U0000000d", {0, 776, 13}, {1, 3, 4}},
+     {"\U00000000\U0000000a", {0, 10}, {1, 2}},
+     {"\U00000000\U00000308\U0000000a", {0, 776, 10}, {1, 3, 4}},
+     {"\U00000000\U00000000", {0, 0}, {1, 2}},
+     {"\U00000000\U00000308\U00000000", {0, 776, 0}, {1, 3, 4}},
+     {"\U00000000\U0000094d", {0, 2381}, {1, 4}},
+     {"\U00000000\U00000308\U0000094d", {0, 776}, {1, 6}},
+     {"\U00000000\U00000300", {0, 768}, {1, 3}},
+     {"\U00000000\U00000308\U00000300", {0, 776}, {1, 5}},
+     {"\U00000000\U0000200c", {0, 8204}, {1, 4}},
+     {"\U00000000\U00000308\U0000200c", {0, 776}, {1, 6}},
+     {"\U00000000\U0000200d", {0, 8205}, {1, 4}},
+     {"\U00000000\U00000308\U0000200d", {0, 776}, {1, 6}},
+     {"\U00000000\U0001f1e6", {0, 127462}, {1, 5}},
+     {"\U00000000\U00000308\U0001f1e6", {0, 776, 127462}, {1, 3, 7}},
+     {"\U00000000\U000006dd", {0, 1757}, {1, 3}},
+     {"\U00000000\U00000308\U000006dd", {0, 776, 1757}, {1, 3, 5}},
+     {"\U00000000\U00000903", {0, 2307}, {1, 4}},
+     {"\U00000000\U00000308\U00000903", {0, 776}, {1, 6}},
+     {"\U00000000\U00001100", {0, 4352}, {1, 4}},
+     {"\U00000000\U00000308\U00001100", {0, 776, 4352}, {1, 3, 6}},
+     {"\U00000000\U00001160", {0, 4448}, {1, 4}},
+     {"\U00000000\U00000308\U00001160", {0, 776, 4448}, {1, 3, 6}},
+     {"\U00000000\U000011a8", {0, 4520}, {1, 4}},
+     {"\U00000000\U00000308\U000011a8", {0, 776, 4520}, {1, 3, 6}},
+     {"\U00000000\U0000ac00", {0, 44032}, {1, 4}},
+     {"\U00000000\U00000308\U0000ac00", {0, 776, 44032}, {1, 3, 6}},
+     {"\U00000000\U0000ac01", {0, 44033}, {1, 4}},
+     {"\U00000000\U00000308\U0000ac01", {0, 776, 44033}, {1, 3, 6}},
+     {"\U00000000\U00001cf5", {0, 7413}, {1, 4}},
+     {"\U00000000\U00000308\U00001cf5", {0, 776, 7413}, {1, 3, 6}},
+     {"\U00000000\U00000915", {0, 2325}, {1, 4}},
+     {"\U00000000\U00000308\U00000915", {0, 776, 2325}, {1, 3, 6}},
+     {"\U00000000\U000000a9", {0, 169}, {1, 3}},
+     {"\U00000000\U00000308\U000000a9", {0, 776, 169}, {1, 3, 5}},
+     {"\U00000000\U00000020", {0, 32}, {1, 2}},
+     {"\U00000000\U00000308\U00000020", {0, 776, 32}, {1, 3, 4}},
+     {"\U00000000\U00000378", {0, 888}, {1, 3}},
+     {"\U00000000\U00000308\U00000378", {0, 776, 888}, {1, 3, 5}},
+     {"\U0000094d\U0000000d", {2381, 13}, {3, 4}},
+     {"\U0000094d\U00000308\U0000000d", {2381, 13}, {5, 6}},
+     {"\U0000094d\U0000000a", {2381, 10}, {3, 4}},
+     {"\U0000094d\U00000308\U0000000a", {2381, 10}, {5, 6}},
+     {"\U0000094d\U00000000", {2381, 0}, {3, 4}},
+     {"\U0000094d\U00000308\U00000000", {2381, 0}, {5, 6}},
+     {"\U0000094d\U0000094d", {2381}, {6}},
+     {"\U0000094d\U00000308\U0000094d", {2381}, {8}},
+     {"\U0000094d\U00000300", {2381}, {5}},
+     {"\U0000094d\U00000308\U00000300", {2381}, {7}},
+     {"\U0000094d\U0000200c", {2381}, {6}},
+     {"\U0000094d\U00000308\U0000200c", {2381}, {8}},
+     {"\U0000094d\U0000200d", {2381}, {6}},
+     {"\U0000094d\U00000308\U0000200d", {2381}, {8}},
+     {"\U0000094d\U0001f1e6", {2381, 127462}, {3, 7}},
+     {"\U0000094d\U00000308\U0001f1e6", {2381, 127462}, {5, 9}},
+     {"\U0000094d\U000006dd", {2381, 1757}, {3, 5}},
+     {"\U0000094d\U00000308\U000006dd", {2381, 1757}, {5, 7}},
+     {"\U0000094d\U00000903", {2381}, {6}},
+     {"\U0000094d\U00000308\U00000903", {2381}, {8}},
+     {"\U0000094d\U00001100", {2381, 4352}, {3, 6}},
+     {"\U0000094d\U00000308\U00001100", {2381, 4352}, {5, 8}},
+     {"\U0000094d\U00001160", {2381, 4448}, {3, 6}},
+     {"\U0000094d\U00000308\U00001160", {2381, 4448}, {5, 8}},
+     {"\U0000094d\U000011a8", {2381, 4520}, {3, 6}},
+     {"\U0000094d\U00000308\U000011a8", {2381, 4520}, {5, 8}},
+     {"\U0000094d\U0000ac00", {2381, 44032}, {3, 6}},
+     {"\U0000094d\U00000308\U0000ac00", {2381, 44032}, {5, 8}},
+     {"\U0000094d\U0000ac01", {2381, 44033}, {3, 6}},
+     {"\U0000094d\U00000308\U0000ac01", {2381, 44033}, {5, 8}},
+     {"\U0000094d\U00001cf5", {2381, 7413}, {3, 6}},
+     {"\U0000094d\U00000308\U00001cf5", {2381, 7413}, {5, 8}},
+     {"\U0000094d\U00000915", {2381}, {6}},
+     {"\U0000094d\U00000308\U00000915", {2381}, {8}},
+     {"\U0000094d\U000000a9", {2381, 169}, {3, 5}},
+     {"\U0000094d\U00000308\U000000a9", {2381, 169}, {5, 7}},
+     {"\U0000094d\U00000020", {2381, 32}, {3, 4}},
+     {"\U0000094d\U00000308\U00000020", {2381, 32}, {5, 6}},
+     {"\U0000094d\U00000378", {2381, 888}, {3, 5}},
+     {"\U0000094d\U00000308\U00000378", {2381, 888}, {5, 7}},
+     {"\U00000300\U0000000d", {768, 13}, {2, 3}},
+     {"\U00000300\U00000308\U0000000d", {768, 13}, {4, 5}},
+     {"\U00000300\U0000000a", {768, 10}, {2, 3}},
+     {"\U00000300\U00000308\U0000000a", {768, 10}, {4, 5}},
+     {"\U00000300\U00000000", {768, 0}, {2, 3}},
+     {"\U00000300\U00000308\U00000000", {768, 0}, {4, 5}},
+     {"\U00000300\U0000094d", {768}, {5}},
+     {"\U00000300\U00000308\U0000094d", {768}, {7}},
+     {"\U00000300\U00000300", {768}, {4}},
+     {"\U00000300\U00000308\U00000300", {768}, {6}},
+     {"\U00000300\U0000200c", {768}, {5}},
+     {"\U00000300\U00000308\U0000200c", {768}, {7}},
+     {"\U00000300\U0000200d", {768}, {5}},
+     {"\U00000300\U00000308\U0000200d", {768}, {7}},
+     {"\U00000300\U0001f1e6", {768, 127462}, {2, 6}},
+     {"\U00000300\U00000308\U0001f1e6", {768, 127462}, {4, 8}},
+     {"\U00000300\U000006dd", {768, 1757}, {2, 4}},
+     {"\U00000300\U00000308\U000006dd", {768, 1757}, {4, 6}},
+     {"\U00000300\U00000903", {768}, {5}},
+     {"\U00000300\U00000308\U00000903", {768}, {7}},
+     {"\U00000300\U00001100", {768, 4352}, {2, 5}},
+     {"\U00000300\U00000308\U00001100", {768, 4352}, {4, 7}},
+     {"\U00000300\U00001160", {768, 4448}, {2, 5}},
+     {"\U00000300\U00000308\U00001160", {768, 4448}, {4, 7}},
+     {"\U00000300\U000011a8", {768, 4520}, {2, 5}},
+     {"\U00000300\U00000308\U000011a8", {768, 4520}, {4, 7}},
+     {"\U00000300\U0000ac00", {768, 44032}, {2, 5}},
+     {"\U00000300\U00000308\U0000ac00", {768, 44032}, {4, 7}},
+     {"\U00000300\U0000ac01", {768, 44033}, {2, 5}},
+     {"\U00000300\U00000308\U0000ac01", {768, 44033}, {4, 7}},
+     {"\U00000300\U00001cf5", {768, 7413}, {2, 5}},
+     {"\U00000300\U00000308\U00001cf5", {768, 7413}, {4, 7}},
+     {"\U00000300\U00000915", {768, 2325}, {2, 5}},
+     {"\U00000300\U00000308\U00000915", {768, 2325}, {4, 7}},
+     {"\U00000300\U000000a9", {768, 169}, {2, 4}},
+     {"\U00000300\U00000308\U000000a9", {768, 169}, {4, 6}},
+     {"\U00000300\U00000020", {768, 32}, {2, 3}},
+     {"\U00000300\U00000308\U00000020", {768, 32}, {4, 5}},
+     {"\U00000300\U00000378", {768, 888}, {2, 4}},
+     {"\U00000300\U00000308\U00000378", {768, 888}, {4, 6}},
      {"\U0000200c\U0000000d", {8204, 13}, {3, 4}},
      {"\U0000200c\U00000308\U0000000d", {8204, 13}, {5, 6}},
      {"\U0000200c\U0000000a", {8204, 10}, {3, 4}},
      {"\U0000200c\U00000308\U0000000a", {8204, 10}, {5, 6}},
-     {"\U0000200c\U00000001", {8204, 1}, {3, 4}},
-     {"\U0000200c\U00000308\U00000001", {8204, 1}, {5, 6}},
+     {"\U0000200c\U00000000", {8204, 0}, {3, 4}},
+     {"\U0000200c\U00000308\U00000000", {8204, 0}, {5, 6}},
+     {"\U0000200c\U0000094d", {8204}, {6}},
+     {"\U0000200c\U00000308\U0000094d", {8204}, {8}},
+     {"\U0000200c\U00000300", {8204}, {5}},
+     {"\U0000200c\U00000308\U00000300", {8204}, {7}},
      {"\U0000200c\U0000200c", {8204}, {6}},
      {"\U0000200c\U00000308\U0000200c", {8204}, {8}},
+     {"\U0000200c\U0000200d", {8204}, {6}},
+     {"\U0000200c\U00000308\U0000200d", {8204}, {8}},
      {"\U0000200c\U0001f1e6", {8204, 127462}, {3, 7}},
      {"\U0000200c\U00000308\U0001f1e6", {8204, 127462}, {5, 9}},
-     {"\U0000200c\U00000600", {8204, 1536}, {3, 5}},
-     {"\U0000200c\U00000308\U00000600", {8204, 1536}, {5, 7}},
-     {"\U0000200c\U00000a03", {8204}, {6}},
-     {"\U0000200c\U00000308\U00000a03", {8204}, {8}},
+     {"\U0000200c\U000006dd", {8204, 1757}, {3, 5}},
+     {"\U0000200c\U00000308\U000006dd", {8204, 1757}, {5, 7}},
+     {"\U0000200c\U00000903", {8204}, {6}},
+     {"\U0000200c\U00000308\U00000903", {8204}, {8}},
      {"\U0000200c\U00001100", {8204, 4352}, {3, 6}},
      {"\U0000200c\U00000308\U00001100", {8204, 4352}, {5, 8}},
      {"\U0000200c\U00001160", {8204, 4448}, {3, 6}},
@@ -293,42 +313,76 @@ std::array<data<char>, 1093> data_utf8 = {{
      {"\U0000200c\U00000308\U0000ac00", {8204, 44032}, {5, 8}},
      {"\U0000200c\U0000ac01", {8204, 44033}, {3, 6}},
      {"\U0000200c\U00000308\U0000ac01", {8204, 44033}, {5, 8}},
-     {"\U0000200c\U00000903", {8204}, {6}},
-     {"\U0000200c\U00000308\U00000903", {8204}, {8}},
-     {"\U0000200c\U00000904", {8204, 2308}, {3, 6}},
-     {"\U0000200c\U00000308\U00000904", {8204, 2308}, {5, 8}},
-     {"\U0000200c\U00000d4e", {8204, 3406}, {3, 6}},
-     {"\U0000200c\U00000308\U00000d4e", {8204, 3406}, {5, 8}},
+     {"\U0000200c\U00001cf5", {8204, 7413}, {3, 6}},
+     {"\U0000200c\U00000308\U00001cf5", {8204, 7413}, {5, 8}},
      {"\U0000200c\U00000915", {8204, 2325}, {3, 6}},
      {"\U0000200c\U00000308\U00000915", {8204, 2325}, {5, 8}},
-     {"\U0000200c\U0000231a", {8204, 8986}, {3, 6}},
-     {"\U0000200c\U00000308\U0000231a", {8204, 8986}, {5, 8}},
-     {"\U0000200c\U00000300", {8204}, {5}},
-     {"\U0000200c\U00000308\U00000300", {8204}, {7}},
-     {"\U0000200c\U00000900", {8204}, {6}},
-     {"\U0000200c\U00000308\U00000900", {8204}, {8}},
-     {"\U0000200c\U0000094d", {8204}, {6}},
-     {"\U0000200c\U00000308\U0000094d", {8204}, {8}},
-     {"\U0000200c\U0000200d", {8204}, {6}},
-     {"\U0000200c\U00000308\U0000200d", {8204}, {8}},
+     {"\U0000200c\U000000a9", {8204, 169}, {3, 5}},
+     {"\U0000200c\U00000308\U000000a9", {8204, 169}, {5, 7}},
+     {"\U0000200c\U00000020", {8204, 32}, {3, 4}},
+     {"\U0000200c\U00000308\U00000020", {8204, 32}, {5, 6}},
      {"\U0000200c\U00000378", {8204, 888}, {3, 5}},
      {"\U0000200c\U00000308\U00000378", {8204, 888}, {5, 7}},
-     {"\U0001f1e6\U00000020", {127462, 32}, {4, 5}},
-     {"\U0001f1e6\U00000308\U00000020", {127462, 32}, {6, 7}},
+     {"\U0000200d\U0000000d", {8205, 13}, {3, 4}},
+     {"\U0000200d\U00000308\U0000000d", {8205, 13}, {5, 6}},
+     {"\U0000200d\U0000000a", {8205, 10}, {3, 4}},
+     {"\U0000200d\U00000308\U0000000a", {8205, 10}, {5, 6}},
+     {"\U0000200d\U00000000", {8205, 0}, {3, 4}},
+     {"\U0000200d\U00000308\U00000000", {8205, 0}, {5, 6}},
+     {"\U0000200d\U0000094d", {8205}, {6}},
+     {"\U0000200d\U00000308\U0000094d", {8205}, {8}},
+     {"\U0000200d\U00000300", {8205}, {5}},
+     {"\U0000200d\U00000308\U00000300", {8205}, {7}},
+     {"\U0000200d\U0000200c", {8205}, {6}},
+     {"\U0000200d\U00000308\U0000200c", {8205}, {8}},
+     {"\U0000200d\U0000200d", {8205}, {6}},
+     {"\U0000200d\U00000308\U0000200d", {8205}, {8}},
+     {"\U0000200d\U0001f1e6", {8205, 127462}, {3, 7}},
+     {"\U0000200d\U00000308\U0001f1e6", {8205, 127462}, {5, 9}},
+     {"\U0000200d\U000006dd", {8205, 1757}, {3, 5}},
+     {"\U0000200d\U00000308\U000006dd", {8205, 1757}, {5, 7}},
+     {"\U0000200d\U00000903", {8205}, {6}},
+     {"\U0000200d\U00000308\U00000903", {8205}, {8}},
+     {"\U0000200d\U00001100", {8205, 4352}, {3, 6}},
+     {"\U0000200d\U00000308\U00001100", {8205, 4352}, {5, 8}},
+     {"\U0000200d\U00001160", {8205, 4448}, {3, 6}},
+     {"\U0000200d\U00000308\U00001160", {8205, 4448}, {5, 8}},
+     {"\U0000200d\U000011a8", {8205, 4520}, {3, 6}},
+     {"\U0000200d\U00000308\U000011a8", {8205, 4520}, {5, 8}},
+     {"\U0000200d\U0000ac00", {8205, 44032}, {3, 6}},
+     {"\U0000200d\U00000308\U0000ac00", {8205, 44032}, {5, 8}},
+     {"\U0000200d\U0000ac01", {8205, 44033}, {3, 6}},
+     {"\U0000200d\U00000308\U0000ac01", {8205, 44033}, {5, 8}},
+     {"\U0000200d\U00001cf5", {8205, 7413}, {3, 6}},
+     {"\U0000200d\U00000308\U00001cf5", {8205, 7413}, {5, 8}},
+     {"\U0000200d\U00000915", {8205, 2325}, {3, 6}},
+     {"\U0000200d\U00000308\U00000915", {8205, 2325}, {5, 8}},
+     {"\U0000200d\U000000a9", {8205, 169}, {3, 5}},
+     {"\U0000200d\U00000308\U000000a9", {8205, 169}, {5, 7}},
+     {"\U0000200d\U00000020", {8205, 32}, {3, 4}},
+     {"\U0000200d\U00000308\U00000020", {8205, 32}, {5, 6}},
+     {"\U0000200d\U00000378", {8205, 888}, {3, 5}},
+     {"\U0000200d\U00000308\U00000378", {8205, 888}, {5, 7}},
      {"\U0001f1e6\U0000000d", {127462, 13}, {4, 5}},
      {"\U0001f1e6\U00000308\U0000000d", {127462, 13}, {6, 7}},
      {"\U0001f1e6\U0000000a", {127462, 10}, {4, 5}},
      {"\U0001f1e6\U00000308\U0000000a", {127462, 10}, {6, 7}},
-     {"\U0001f1e6\U00000001", {127462, 1}, {4, 5}},
-     {"\U0001f1e6\U00000308\U00000001", {127462, 1}, {6, 7}},
+     {"\U0001f1e6\U00000000", {127462, 0}, {4, 5}},
+     {"\U0001f1e6\U00000308\U00000000", {127462, 0}, {6, 7}},
+     {"\U0001f1e6\U0000094d", {127462}, {7}},
+     {"\U0001f1e6\U00000308\U0000094d", {127462}, {9}},
+     {"\U0001f1e6\U00000300", {127462}, {6}},
+     {"\U0001f1e6\U00000308\U00000300", {127462}, {8}},
      {"\U0001f1e6\U0000200c", {127462}, {7}},
      {"\U0001f1e6\U00000308\U0000200c", {127462}, {9}},
+     {"\U0001f1e6\U0000200d", {127462}, {7}},
+     {"\U0001f1e6\U00000308\U0000200d", {127462}, {9}},
      {"\U0001f1e6\U0001f1e6", {127462}, {8}},
      {"\U0001f1e6\U00000308\U0001f1e6", {127462, 127462}, {6, 10}},
-     {"\U0001f1e6\U00000600", {127462, 1536}, {4, 6}},
-     {"\U0001f1e6\U00000308\U00000600", {127462, 1536}, {6, 8}},
-     {"\U0001f1e6\U00000a03", {127462}, {7}},
-     {"\U0001f1e6\U00000308\U00000a03", {127462}, {9}},
+     {"\U0001f1e6\U000006dd", {127462, 1757}, {4, 6}},
+     {"\U0001f1e6\U00000308\U000006dd", {127462, 1757}, {6, 8}},
+     {"\U0001f1e6\U00000903", {127462}, {7}},
+     {"\U0001f1e6\U00000308\U00000903", {127462}, {9}},
      {"\U0001f1e6\U00001100", {127462, 4352}, {4, 7}},
      {"\U0001f1e6\U00000308\U00001100", {127462, 4352}, {6, 9}},
      {"\U0001f1e6\U00001160", {127462, 4448}, {4, 7}},
@@ -339,134 +393,116 @@ std::array<data<char>, 1093> data_utf8 = {{
      {"\U0001f1e6\U00000308\U0000ac00", {127462, 44032}, {6, 9}},
      {"\U0001f1e6\U0000ac01", {127462, 44033}, {4, 7}},
      {"\U0001f1e6\U00000308\U0000ac01", {127462, 44033}, {6, 9}},
-     {"\U0001f1e6\U00000903", {127462}, {7}},
-     {"\U0001f1e6\U00000308\U00000903", {127462}, {9}},
-     {"\U0001f1e6\U00000904", {127462, 2308}, {4, 7}},
-     {"\U0001f1e6\U00000308\U00000904", {127462, 2308}, {6, 9}},
-     {"\U0001f1e6\U00000d4e", {127462, 3406}, {4, 7}},
-     {"\U0001f1e6\U00000308\U00000d4e", {127462, 3406}, {6, 9}},
+     {"\U0001f1e6\U00001cf5", {127462, 7413}, {4, 7}},
+     {"\U0001f1e6\U00000308\U00001cf5", {127462, 7413}, {6, 9}},
      {"\U0001f1e6\U00000915", {127462, 2325}, {4, 7}},
      {"\U0001f1e6\U00000308\U00000915", {127462, 2325}, {6, 9}},
-     {"\U0001f1e6\U0000231a", {127462, 8986}, {4, 7}},
-     {"\U0001f1e6\U00000308\U0000231a", {127462, 8986}, {6, 9}},
-     {"\U0001f1e6\U00000300", {127462}, {6}},
-     {"\U0001f1e6\U00000308\U00000300", {127462}, {8}},
-     {"\U0001f1e6\U00000900", {127462}, {7}},
-     {"\U0001f1e6\U00000308\U00000900", {127462}, {9}},
-     {"\U0001f1e6\U0000094d", {127462}, {7}},
-     {"\U0001f1e6\U00000308\U0000094d", {127462}, {9}},
-     {"\U0001f1e6\U0000200d", {127462}, {7}},
-     {"\U0001f1e6\U00000308\U0000200d", {127462}, {9}},
+     {"\U0001f1e6\U000000a9", {127462, 169}, {4, 6}},
+     {"\U0001f1e6\U00000308\U000000a9", {127462, 169}, {6, 8}},
+     {"\U0001f1e6\U00000020", {127462, 32}, {4, 5}},
+     {"\U0001f1e6\U00000308\U00000020", {127462, 32}, {6, 7}},
      {"\U0001f1e6\U00000378", {127462, 888}, {4, 6}},
      {"\U0001f1e6\U00000308\U00000378", {127462, 888}, {6, 8}},
-     {"\U00000600\U00000020", {1536}, {3}},
-     {"\U00000600\U00000308\U00000020", {1536, 32}, {4, 5}},
-     {"\U00000600\U0000000d", {1536, 13}, {2, 3}},
-     {"\U00000600\U00000308\U0000000d", {1536, 13}, {4, 5}},
-     {"\U00000600\U0000000a", {1536, 10}, {2, 3}},
-     {"\U00000600\U00000308\U0000000a", {1536, 10}, {4, 5}},
-     {"\U00000600\U00000001", {1536, 1}, {2, 3}},
-     {"\U00000600\U00000308\U00000001", {1536, 1}, {4, 5}},
-     {"\U00000600\U0000200c", {1536}, {5}},
-     {"\U00000600\U00000308\U0000200c", {1536}, {7}},
-     {"\U00000600\U0001f1e6", {1536}, {6}},
-     {"\U00000600\U00000308\U0001f1e6", {1536, 127462}, {4, 8}},
-     {"\U00000600\U00000600", {1536}, {4}},
-     {"\U00000600\U00000308\U00000600", {1536, 1536}, {4, 6}},
-     {"\U00000600\U00000a03", {1536}, {5}},
-     {"\U00000600\U00000308\U00000a03", {1536}, {7}},
-     {"\U00000600\U00001100", {1536}, {5}},
-     {"\U00000600\U00000308\U00001100", {1536, 4352}, {4, 7}},
-     {"\U00000600\U00001160", {1536}, {5}},
-     {"\U00000600\U00000308\U00001160", {1536, 4448}, {4, 7}},
-     {"\U00000600\U000011a8", {1536}, {5}},
-     {"\U00000600\U00000308\U000011a8", {1536, 4520}, {4, 7}},
-     {"\U00000600\U0000ac00", {1536}, {5}},
-     {"\U00000600\U00000308\U0000ac00", {1536, 44032}, {4, 7}},
-     {"\U00000600\U0000ac01", {1536}, {5}},
-     {"\U00000600\U00000308\U0000ac01", {1536, 44033}, {4, 7}},
-     {"\U00000600\U00000903", {1536}, {5}},
-     {"\U00000600\U00000308\U00000903", {1536}, {7}},
-     {"\U00000600\U00000904", {1536}, {5}},
-     {"\U00000600\U00000308\U00000904", {1536, 2308}, {4, 7}},
-     {"\U00000600\U00000d4e", {1536}, {5}},
-     {"\U00000600\U00000308\U00000d4e", {1536, 3406}, {4, 7}},
-     {"\U00000600\U00000915", {1536}, {5}},
-     {"\U00000600\U00000308\U00000915", {1536, 2325}, {4, 7}},
-     {"\U00000600\U0000231a", {1536}, {5}},
-     {"\U00000600\U00000308\U0000231a", {1536, 8986}, {4, 7}},
-     {"\U00000600\U00000300", {1536}, {4}},
-     {"\U00000600\U00000308\U00000300", {1536}, {6}},
-     {"\U00000600\U00000900", {1536}, {5}},
-     {"\U00000600\U00000308\U00000900", {1536}, {7}},
-     {"\U00000600\U0000094d", {1536}, {5}},
-     {"\U00000600\U00000308\U0000094d", {1536}, {7}},
-     {"\U00000600\U0000200d", {1536}, {5}},
-     {"\U00000600\U00000308\U0000200d", {1536}, {7}},
-     {"\U00000600\U00000378", {1536}, {4}},
-     {"\U00000600\U00000308\U00000378", {1536, 888}, {4, 6}},
-     {"\U00000a03\U00000020", {2563, 32}, {3, 4}},
-     {"\U00000a03\U00000308\U00000020", {2563, 32}, {5, 6}},
-     {"\U00000a03\U0000000d", {2563, 13}, {3, 4}},
-     {"\U00000a03\U00000308\U0000000d", {2563, 13}, {5, 6}},
-     {"\U00000a03\U0000000a", {2563, 10}, {3, 4}},
-     {"\U00000a03\U00000308\U0000000a", {2563, 10}, {5, 6}},
-     {"\U00000a03\U00000001", {2563, 1}, {3, 4}},
-     {"\U00000a03\U00000308\U00000001", {2563, 1}, {5, 6}},
-     {"\U00000a03\U0000200c", {2563}, {6}},
-     {"\U00000a03\U00000308\U0000200c", {2563}, {8}},
-     {"\U00000a03\U0001f1e6", {2563, 127462}, {3, 7}},
-     {"\U00000a03\U00000308\U0001f1e6", {2563, 127462}, {5, 9}},
-     {"\U00000a03\U00000600", {2563, 1536}, {3, 5}},
-     {"\U00000a03\U00000308\U00000600", {2563, 1536}, {5, 7}},
-     {"\U00000a03\U00000a03", {2563}, {6}},
-     {"\U00000a03\U00000308\U00000a03", {2563}, {8}},
-     {"\U00000a03\U00001100", {2563, 4352}, {3, 6}},
-     {"\U00000a03\U00000308\U00001100", {2563, 4352}, {5, 8}},
-     {"\U00000a03\U00001160", {2563, 4448}, {3, 6}},
-     {"\U00000a03\U00000308\U00001160", {2563, 4448}, {5, 8}},
-     {"\U00000a03\U000011a8", {2563, 4520}, {3, 6}},
-     {"\U00000a03\U00000308\U000011a8", {2563, 4520}, {5, 8}},
-     {"\U00000a03\U0000ac00", {2563, 44032}, {3, 6}},
-     {"\U00000a03\U00000308\U0000ac00", {2563, 44032}, {5, 8}},
-     {"\U00000a03\U0000ac01", {2563, 44033}, {3, 6}},
-     {"\U00000a03\U00000308\U0000ac01", {2563, 44033}, {5, 8}},
-     {"\U00000a03\U00000903", {2563}, {6}},
-     {"\U00000a03\U00000308\U00000903", {2563}, {8}},
-     {"\U00000a03\U00000904", {2563, 2308}, {3, 6}},
-     {"\U00000a03\U00000308\U00000904", {2563, 2308}, {5, 8}},
-     {"\U00000a03\U00000d4e", {2563, 3406}, {3, 6}},
-     {"\U00000a03\U00000308\U00000d4e", {2563, 3406}, {5, 8}},
-     {"\U00000a03\U00000915", {2563, 2325}, {3, 6}},
-     {"\U00000a03\U00000308\U00000915", {2563, 2325}, {5, 8}},
-     {"\U00000a03\U0000231a", {2563, 8986}, {3, 6}},
-     {"\U00000a03\U00000308\U0000231a", {2563, 8986}, {5, 8}},
-     {"\U00000a03\U00000300", {2563}, {5}},
-     {"\U00000a03\U00000308\U00000300", {2563}, {7}},
-     {"\U00000a03\U00000900", {2563}, {6}},
-     {"\U00000a03\U00000308\U00000900", {2563}, {8}},
-     {"\U00000a03\U0000094d", {2563}, {6}},
-     {"\U00000a03\U00000308\U0000094d", {2563}, {8}},
-     {"\U00000a03\U0000200d", {2563}, {6}},
-     {"\U00000a03\U00000308\U0000200d", {2563}, {8}},
-     {"\U00000a03\U00000378", {2563, 888}, {3, 5}},
-     {"\U00000a03\U00000308\U00000378", {2563, 888}, {5, 7}},
-     {"\U00001100\U00000020", {4352, 32}, {3, 4}},
-     {"\U00001100\U00000308\U00000020", {4352, 32}, {5, 6}},
+     {"\U000006dd\U0000000d", {1757, 13}, {2, 3}},
+     {"\U000006dd\U00000308\U0000000d", {1757, 13}, {4, 5}},
+     {"\U000006dd\U0000000a", {1757, 10}, {2, 3}},
+     {"\U000006dd\U00000308\U0000000a", {1757, 10}, {4, 5}},
+     {"\U000006dd\U00000000", {1757, 0}, {2, 3}},
+     {"\U000006dd\U00000308\U00000000", {1757, 0}, {4, 5}},
+     {"\U000006dd\U0000094d", {1757}, {5}},
+     {"\U000006dd\U00000308\U0000094d", {1757}, {7}},
+     {"\U000006dd\U00000300", {1757}, {4}},
+     {"\U000006dd\U00000308\U00000300", {1757}, {6}},
+     {"\U000006dd\U0000200c", {1757}, {5}},
+     {"\U000006dd\U00000308\U0000200c", {1757}, {7}},
+     {"\U000006dd\U0000200d", {1757}, {5}},
+     {"\U000006dd\U00000308\U0000200d", {1757}, {7}},
+     {"\U000006dd\U0001f1e6", {1757}, {6}},
+     {"\U000006dd\U00000308\U0001f1e6", {1757, 127462}, {4, 8}},
+     {"\U000006dd\U000006dd", {1757}, {4}},
+     {"\U000006dd\U00000308\U000006dd", {1757, 1757}, {4, 6}},
+     {"\U000006dd\U00000903", {1757}, {5}},
+     {"\U000006dd\U00000308\U00000903", {1757}, {7}},
+     {"\U000006dd\U00001100", {1757}, {5}},
+     {"\U000006dd\U00000308\U00001100", {1757, 4352}, {4, 7}},
+     {"\U000006dd\U00001160", {1757}, {5}},
+     {"\U000006dd\U00000308\U00001160", {1757, 4448}, {4, 7}},
+     {"\U000006dd\U000011a8", {1757}, {5}},
+     {"\U000006dd\U00000308\U000011a8", {1757, 4520}, {4, 7}},
+     {"\U000006dd\U0000ac00", {1757}, {5}},
+     {"\U000006dd\U00000308\U0000ac00", {1757, 44032}, {4, 7}},
+     {"\U000006dd\U0000ac01", {1757}, {5}},
+     {"\U000006dd\U00000308\U0000ac01", {1757, 44033}, {4, 7}},
+     {"\U000006dd\U00001cf5", {1757}, {5}},
+     {"\U000006dd\U00000308\U00001cf5", {1757, 7413}, {4, 7}},
+     {"\U000006dd\U00000915", {1757}, {5}},
+     {"\U000006dd\U00000308\U00000915", {1757, 2325}, {4, 7}},
+     {"\U000006dd\U000000a9", {1757}, {4}},
+     {"\U000006dd\U00000308\U000000a9", {1757, 169}, {4, 6}},
+     {"\U000006dd\U00000020", {1757}, {3}},
+     {"\U000006dd\U00000308\U00000020", {1757, 32}, {4, 5}},
+     {"\U000006dd\U00000378", {1757}, {4}},
+     {"\U000006dd\U00000308\U00000378", {1757, 888}, {4, 6}},
+     {"\U00000903\U0000000d", {2307, 13}, {3, 4}},
+     {"\U00000903\U00000308\U0000000d", {2307, 13}, {5, 6}},
+     {"\U00000903\U0000000a", {2307, 10}, {3, 4}},
+     {"\U00000903\U00000308\U0000000a", {2307, 10}, {5, 6}},
+     {"\U00000903\U00000000", {2307, 0}, {3, 4}},
+     {"\U00000903\U00000308\U00000000", {2307, 0}, {5, 6}},
+     {"\U00000903\U0000094d", {2307}, {6}},
+     {"\U00000903\U00000308\U0000094d", {2307}, {8}},
+     {"\U00000903\U00000300", {2307}, {5}},
+     {"\U00000903\U00000308\U00000300", {2307}, {7}},
+     {"\U00000903\U0000200c", {2307}, {6}},
+     {"\U00000903\U00000308\U0000200c", {2307}, {8}},
+     {"\U00000903\U0000200d", {2307}, {6}},
+     {"\U00000903\U00000308\U0000200d", {2307}, {8}},
+     {"\U00000903\U0001f1e6", {2307, 127462}, {3, 7}},
+     {"\U00000903\U00000308\U0001f1e6", {2307, 127462}, {5, 9}},
+     {"\U00000903\U000006dd", {2307, 1757}, {3, 5}},
+     {"\U00000903\U00000308\U000006dd", {2307, 1757}, {5, 7}},
+     {"\U00000903\U00000903", {2307}, {6}},
+     {"\U00000903\U00000308\U00000903", {2307}, {8}},
+     {"\U00000903\U00001100", {2307, 4352}, {3, 6}},
+     {"\U00000903\U00000308\U00001100", {2307, 4352}, {5, 8}},
+     {"\U00000903\U00001160", {2307, 4448}, {3, 6}},
+     {"\U00000903\U00000308\U00001160", {2307, 4448}, {5, 8}},
+     {"\U00000903\U000011a8", {2307, 4520}, {3, 6}},
+     {"\U00000903\U00000308\U000011a8", {2307, 4520}, {5, 8}},
+     {"\U00000903\U0000ac00", {2307, 44032}, {3, 6}},
+     {"\U00000903\U00000308\U0000ac00", {2307, 44032}, {5, 8}},
+     {"\U00000903\U0000ac01", {2307, 44033}, {3, 6}},
+     {"\U00000903\U00000308\U0000ac01", {2307, 44033}, {5, 8}},
+     {"\U00000903\U00001cf5", {2307, 7413}, {3, 6}},
+     {"\U00000903\U00000308\U00001cf5", {2307, 7413}, {5, 8}},
+     {"\U00000903\U00000915", {2307, 2325}, {3, 6}},
+     {"\U00000903\U00000308\U00000915", {2307, 2325}, {5, 8}},
+     {"\U00000903\U000000a9", {2307, 169}, {3, 5}},
+     {"\U00000903\U00000308\U000000a9", {2307, 169}, {5, 7}},
+     {"\U00000903\U00000020", {2307, 32}, {3, 4}},
+     {"\U00000903\U00000308\U00000020", {2307, 32}, {5, 6}},
+     {"\U00000903\U00000378", {2307, 888}, {3, 5}},
+     {"\U00000903\U00000308\U00000378", {2307, 888}, {5, 7}},
      {"\U00001100\U0000000d", {4352, 13}, {3, 4}},
      {"\U00001100\U00000308\U0000000d", {4352, 13}, {5, 6}},
      {"\U00001100\U0000000a", {4352, 10}, {3, 4}},
      {"\U00001100\U00000308\U0000000a", {4352, 10}, {5, 6}},
-     {"\U00001100\U00000001", {4352, 1}, {3, 4}},
-     {"\U00001100\U00000308\U00000001", {4352, 1}, {5, 6}},
+     {"\U00001100\U00000000", {4352, 0}, {3, 4}},
+     {"\U00001100\U00000308\U00000000", {4352, 0}, {5, 6}},
+     {"\U00001100\U0000094d", {4352}, {6}},
+     {"\U00001100\U00000308\U0000094d", {4352}, {8}},
+     {"\U00001100\U00000300", {4352}, {5}},
+     {"\U00001100\U00000308\U00000300", {4352}, {7}},
      {"\U00001100\U0000200c", {4352}, {6}},
      {"\U00001100\U00000308\U0000200c", {4352}, {8}},
+     {"\U00001100\U0000200d", {4352}, {6}},
+     {"\U00001100\U00000308\U0000200d", {4352}, {8}},
      {"\U00001100\U0001f1e6", {4352, 127462}, {3, 7}},
      {"\U00001100\U00000308\U0001f1e6", {4352, 127462}, {5, 9}},
-     {"\U00001100\U00000600", {4352, 1536}, {3, 5}},
-     {"\U00001100\U00000308\U00000600", {4352, 1536}, {5, 7}},
-     {"\U00001100\U00000a03", {4352}, {6}},
-     {"\U00001100\U00000308\U00000a03", {4352}, {8}},
+     {"\U00001100\U000006dd", {4352, 1757}, {3, 5}},
+     {"\U00001100\U00000308\U000006dd", {4352, 1757}, {5, 7}},
+     {"\U00001100\U00000903", {4352}, {6}},
+     {"\U00001100\U00000308\U00000903", {4352}, {8}},
      {"\U00001100\U00001100", {4352}, {6}},
      {"\U00001100\U00000308\U00001100", {4352, 4352}, {5, 8}},
      {"\U00001100\U00001160", {4352}, {6}},
@@ -477,42 +513,36 @@ std::array<data<char>, 1093> data_utf8 = {{
      {"\U00001100\U00000308\U0000ac00", {4352, 44032}, {5, 8}},
      {"\U00001100\U0000ac01", {4352}, {6}},
      {"\U00001100\U00000308\U0000ac01", {4352, 44033}, {5, 8}},
-     {"\U00001100\U00000903", {4352}, {6}},
-     {"\U00001100\U00000308\U00000903", {4352}, {8}},
-     {"\U00001100\U00000904", {4352, 2308}, {3, 6}},
-     {"\U00001100\U00000308\U00000904", {4352, 2308}, {5, 8}},
-     {"\U00001100\U00000d4e", {4352, 3406}, {3, 6}},
-     {"\U00001100\U00000308\U00000d4e", {4352, 3406}, {5, 8}},
+     {"\U00001100\U00001cf5", {4352, 7413}, {3, 6}},
+     {"\U00001100\U00000308\U00001cf5", {4352, 7413}, {5, 8}},
      {"\U00001100\U00000915", {4352, 2325}, {3, 6}},
      {"\U00001100\U00000308\U00000915", {4352, 2325}, {5, 8}},
-     {"\U00001100\U0000231a", {4352, 8986}, {3, 6}},
-     {"\U00001100\U00000308\U0000231a", {4352, 8986}, {5, 8}},
-     {"\U00001100\U00000300", {4352}, {5}},
-     {"\U00001100\U00000308\U00000300", {4352}, {7}},
-     {"\U00001100\U00000900", {4352}, {6}},
-     {"\U00001100\U00000308\U00000900", {4352}, {8}},
-     {"\U00001100\U0000094d", {4352}, {6}},
-     {"\U00001100\U00000308\U0000094d", {4352}, {8}},
-     {"\U00001100\U0000200d", {4352}, {6}},
-     {"\U00001100\U00000308\U0000200d", {4352}, {8}},
+     {"\U00001100\U000000a9", {4352, 169}, {3, 5}},
+     {"\U00001100\U00000308\U000000a9", {4352, 169}, {5, 7}},
+     {"\U00001100\U00000020", {4352, 32}, {3, 4}},
+     {"\U00001100\U00000308\U00000020", {4352, 32}, {5, 6}},
      {"\U00001100\U00000378", {4352, 888}, {3, 5}},
      {"\U00001100\U00000308\U00000378", {4352, 888}, {5, 7}},
-     {"\U00001160\U00000020", {4448, 32}, {3, 4}},
-     {"\U00001160\U00000308\U00000020", {4448, 32}, {5, 6}},
      {"\U00001160\U0000000d", {4448, 13}, {3, 4}},
      {"\U00001160\U00000308\U0000000d", {4448, 13}, {5, 6}},
      {"\U00001160\U0000000a", {4448, 10}, {3, 4}},
      {"\U00001160\U00000308\U0000000a", {4448, 10}, {5, 6}},
-     {"\U00001160\U00000001", {4448, 1}, {3, 4}},
-     {"\U00001160\U00000308\U00000001", {4448, 1}, {5, 6}},
+     {"\U00001160\U00000000", {4448, 0}, {3, 4}},
+     {"\U00001160\U00000308\U00000000", {4448, 0}, {5, 6}},
+     {"\U00001160\U0000094d", {4448}, {6}},
+     {"\U00001160\U00000308\U0000094d", {4448}, {8}},
+     {"\U00001160\U00000300", {4448}, {5}},
+     {"\U00001160\U00000308\U00000300", {4448}, {7}},
      {"\U00001160\U0000200c", {4448}, {6}},
      {"\U00001160\U00000308\U0000200c", {4448}, {8}},
+     {"\U00001160\U0000200d", {4448}, {6}},
+     {"\U00001160\U00000308\U0000200d", {4448}, {8}},
      {"\U00001160\U0001f1e6", {4448, 127462}, {3, 7}},
      {"\U00001160\U00000308\U0001f1e6", {4448, 127462}, {5, 9}},
-     {"\U00001160\U00000600", {4448, 1536}, {3, 5}},
-     {"\U00001160\U00000308\U00000600", {4448, 1536}, {5, 7}},
-     {"\U00001160\U00000a03", {4448}, {6}},
-     {"\U00001160\U00000308\U00000a03", {4448}, {8}},
+     {"\U00001160\U000006dd", {4448, 1757}, {3, 5}},
+     {"\U00001160\U00000308\U000006dd", {4448, 1757}, {5, 7}},
+     {"\U00001160\U00000903", {4448}, {6}},
+     {"\U00001160\U00000308\U00000903", {4448}, {8}},
      {"\U00001160\U00001100", {4448, 4352}, {3, 6}},
      {"\U00001160\U00000308\U00001100", {4448, 4352}, {5, 8}},
      {"\U00001160\U00001160", {4448}, {6}},
@@ -523,42 +553,36 @@ std::array<data<char>, 1093> data_utf8 = {{
      {"\U00001160\U00000308\U0000ac00", {4448, 44032}, {5, 8}},
      {"\U00001160\U0000ac01", {4448, 44033}, {3, 6}},
      {"\U00001160\U00000308\U0000ac01", {4448, 44033}, {5, 8}},
-     {"\U00001160\U00000903", {4448}, {6}},
-     {"\U00001160\U00000308\U00000903", {4448}, {8}},
-     {"\U00001160\U00000904", {4448, 2308}, {3, 6}},
-     {"\U00001160\U00000308\U00000904", {4448, 2308}, {5, 8}},
-     {"\U00001160\U00000d4e", {4448, 3406}, {3, 6}},
-     {"\U00001160\U00000308\U00000d4e", {4448, 3406}, {5, 8}},
+     {"\U00001160\U00001cf5", {4448, 7413}, {3, 6}},
+     {"\U00001160\U00000308\U00001cf5", {4448, 7413}, {5, 8}},
      {"\U00001160\U00000915", {4448, 2325}, {3, 6}},
      {"\U00001160\U00000308\U00000915", {4448, 2325}, {5, 8}},
-     {"\U00001160\U0000231a", {4448, 8986}, {3, 6}},
-     {"\U00001160\U00000308\U0000231a", {4448, 8986}, {5, 8}},
-     {"\U00001160\U00000300", {4448}, {5}},
-     {"\U00001160\U00000308\U00000300", {4448}, {7}},
-     {"\U00001160\U00000900", {4448}, {6}},
-     {"\U00001160\U00000308\U00000900", {4448}, {8}},
-     {"\U00001160\U0000094d", {4448}, {6}},
-     {"\U00001160\U00000308\U0000094d", {4448}, {8}},
-     {"\U00001160\U0000200d", {4448}, {6}},
-     {"\U00001160\U00000308\U0000200d", {4448}, {8}},
+     {"\U00001160\U000000a9", {4448, 169}, {3, 5}},
+     {"\U00001160\U00000308\U000000a9", {4448, 169}, {5, 7}},
+     {"\U00001160\U00000020", {4448, 32}, {3, 4}},
+     {"\U00001160\U00000308\U00000020", {4448, 32}, {5, 6}},
      {"\U00001160\U00000378", {4448, 888}, {3, 5}},
      {"\U00001160\U00000308\U00000378", {4448, 888}, {5, 7}},
-     {"\U000011a8\U00000020", {4520, 32}, {3, 4}},
-     {"\U000011a8\U00000308\U00000020", {4520, 32}, {5, 6}},
      {"\U000011a8\U0000000d", {4520, 13}, {3, 4}},
      {"\U000011a8\U00000308\U0000000d", {4520, 13}, {5, 6}},
      {"\U000011a8\U0000000a", {4520, 10}, {3, 4}},
      {"\U000011a8\U00000308\U0000000a", {4520, 10}, {5, 6}},
-     {"\U000011a8\U00000001", {4520, 1}, {3, 4}},
-     {"\U000011a8\U00000308\U00000001", {4520, 1}, {5, 6}},
+     {"\U000011a8\U00000000", {4520, 0}, {3, 4}},
+     {"\U000011a8\U00000308\U00000000", {4520, 0}, {5, 6}},
+     {"\U000011a8\U0000094d", {4520}, {6}},
+     {"\U000011a8\U00000308\U0000094d", {4520}, {8}},
+     {"\U000011a8\U00000300", {4520}, {5}},
+     {"\U000011a8\U00000308\U00000300", {4520}, {7}},
      {"\U000011a8\U0000200c", {4520}, {6}},
      {"\U000011a8\U00000308\U0000200c", {4520}, {8}},
+     {"\U000011a8\U0000200d", {4520}, {6}},
+     {"\U000011a8\U00000308\U0000200d", {4520}, {8}},
      {"\U000011a8\U0001f1e6", {4520, 127462}, {3, 7}},
      {"\U000011a8\U00000308\U0001f1e6", {4520, 127462}, {5, 9}},
-     {"\U000011a8\U00000600", {4520, 1536}, {3, 5}},
-     {"\U000011a8\U00000308\U00000600", {4520, 1536}, {5, 7}},
-     {"\U000011a8\U00000a03", {4520}, {6}},
-     {"\U000011a8\U00000308\U00000a03", {4520}, {8}},
+     {"\U000011a8\U000006dd", {4520, 1757}, {3, 5}},
+     {"\U000011a8\U00000308\U000006dd", {4520, 1757}, {5, 7}},
+     {"\U000011a8\U00000903", {4520}, {6}},
+     {"\U000011a8\U00000308\U00000903", {4520}, {8}},
      {"\U000011a8\U00001100", {4520, 4352}, {3, 6}},
      {"\U000011a8\U00000308\U00001100", {4520, 4352}, {5, 8}},
      {"\U000011a8\U00001160", {4520, 4448}, {3, 6}},
@@ -569,42 +593,36 @@ std::array<data<char>, 1093> data_utf8 = {{
      {"\U000011a8\U00000308\U0000ac00", {4520, 44032}, {5, 8}},
      {"\U000011a8\U0000ac01", {4520, 44033}, {3, 6}},
      {"\U000011a8\U00000308\U0000ac01", {4520, 44033}, {5, 8}},
-     {"\U000011a8\U00000903", {4520}, {6}},
-     {"\U000011a8\U00000308\U00000903", {4520}, {8}},
-     {"\U000011a8\U00000904", {4520, 2308}, {3, 6}},
-     {"\U000011a8\U00000308\U00000904", {4520, 2308}, {5, 8}},
-     {"\U000011a8\U00000d4e", {4520, 3406}, {3, 6}},
-     {"\U000011a8\U00000308\U00000d4e", {4520, 3406}, {5, 8}},
+     {"\U000011a8\U00001cf5", {4520, 7413}, {3, 6}},
+     {"\U000011a8\U00000308\U00001cf5", {4520, 7413}, {5, 8}},
      {"\U000011a8\U00000915", {4520, 2325}, {3, 6}},
      {"\U000011a8\U00000308\U00000915", {4520, 2325}, {5, 8}},
-     {"\U000011a8\U0000231a", {4520, 8986}, {3, 6}},
-     {"\U000011a8\U00000308\U0000231a", {4520, 8986}, {5, 8}},
-     {"\U000011a8\U00000300", {4520}, {5}},
-     {"\U000011a8\U00000308\U00000300", {4520}, {7}},
-     {"\U000011a8\U00000900", {4520}, {6}},
-     {"\U000011a8\U00000308\U00000900", {4520}, {8}},
-     {"\U000011a8\U0000094d", {4520}, {6}},
-     {"\U000011a8\U00000308\U0000094d", {4520}, {8}},
-     {"\U000011a8\U0000200d", {4520}, {6}},
-     {"\U000011a8\U00000308\U0000200d", {4520}, {8}},
+     {"\U000011a8\U000000a9", {4520, 169}, {3, 5}},
+     {"\U000011a8\U00000308\U000000a9", {4520, 169}, {5, 7}},
+     {"\U000011a8\U00000020", {4520, 32}, {3, 4}},
+     {"\U000011a8\U00000308\U00000020", {4520, 32}, {5, 6}},
      {"\U000011a8\U00000378", {4520, 888}, {3, 5}},
      {"\U000011a8\U00000308\U00000378", {4520, 888}, {5, 7}},
-     {"\U0000ac00\U00000020", {44032, 32}, {3, 4}},
-     {"\U0000ac00\U00000308\U00000020", {44032, 32}, {5, 6}},
      {"\U0000ac00\U0000000d", {44032, 13}, {3, 4}},
      {"\U0000ac00\U00000308\U0000000d", {44032, 13}, {5, 6}},
      {"\U0000ac00\U0000000a", {44032, 10}, {3, 4}},
      {"\U0000ac00\U00000308\U0000000a", {44032, 10}, {5, 6}},
-     {"\U0000ac00\U00000001", {44032, 1}, {3, 4}},
-     {"\U0000ac00\U00000308\U00000001", {44032, 1}, {5, 6}},
+     {"\U0000ac00\U00000000", {44032, 0}, {3, 4}},
+     {"\U0000ac00\U00000308\U00000000", {44032, 0}, {5, 6}},
+     {"\U0000ac00\U0000094d", {44032}, {6}},
+     {"\U0000ac00\U00000308\U0000094d", {44032}, {8}},
+     {"\U0000ac00\U00000300", {44032}, {5}},
+     {"\U0000ac00\U00000308\U00000300", {44032}, {7}},
      {"\U0000ac00\U0000200c", {44032}, {6}},
      {"\U0000ac00\U00000308\U0000200c", {44032}, {8}},
+     {"\U0000ac00\U0000200d", {44032}, {6}},
+     {"\U0000ac00\U00000308\U0000200d", {44032}, {8}},
      {"\U0000ac00\U0001f1e6", {44032, 127462}, {3, 7}},
      {"\U0000ac00\U00000308\U0001f1e6", {44032, 127462}, {5, 9}},
-     {"\U0000ac00\U00000600", {44032, 1536}, {3, 5}},
-     {"\U0000ac00\U00000308\U00000600", {44032, 1536}, {5, 7}},
-     {"\U0000ac00\U00000a03", {44032}, {6}},
-     {"\U0000ac00\U00000308\U00000a03", {44032}, {8}},
+     {"\U0000ac00\U000006dd", {44032, 1757}, {3, 5}},
+     {"\U0000ac00\U00000308\U000006dd", {44032, 1757}, {5, 7}},
+     {"\U0000ac00\U00000903", {44032}, {6}},
+     {"\U0000ac00\U00000308\U00000903", {44032}, {8}},
      {"\U0000ac00\U00001100", {44032, 4352}, {3, 6}},
      {"\U0000ac00\U00000308\U00001100", {44032, 4352}, {5, 8}},
      {"\U0000ac00\U00001160", {44032}, {6}},
@@ -615,42 +633,36 @@ std::array<data<char>, 1093> data_utf8 = {{
      {"\U0000ac00\U00000308\U0000ac00", {44032, 44032}, {5, 8}},
      {"\U0000ac00\U0000ac01", {44032, 44033}, {3, 6}},
      {"\U0000ac00\U00000308\U0000ac01", {44032, 44033}, {5, 8}},
-     {"\U0000ac00\U00000903", {44032}, {6}},
-     {"\U0000ac00\U00000308\U00000903", {44032}, {8}},
-     {"\U0000ac00\U00000904", {44032, 2308}, {3, 6}},
-     {"\U0000ac00\U00000308\U00000904", {44032, 2308}, {5, 8}},
-     {"\U0000ac00\U00000d4e", {44032, 3406}, {3, 6}},
-     {"\U0000ac00\U00000308\U00000d4e", {44032, 3406}, {5, 8}},
+     {"\U0000ac00\U00001cf5", {44032, 7413}, {3, 6}},
+     {"\U0000ac00\U00000308\U00001cf5", {44032, 7413}, {5, 8}},
      {"\U0000ac00\U00000915", {44032, 2325}, {3, 6}},
      {"\U0000ac00\U00000308\U00000915", {44032, 2325}, {5, 8}},
-     {"\U0000ac00\U0000231a", {44032, 8986}, {3, 6}},
-     {"\U0000ac00\U00000308\U0000231a", {44032, 8986}, {5, 8}},
-     {"\U0000ac00\U00000300", {44032}, {5}},
-     {"\U0000ac00\U00000308\U00000300", {44032}, {7}},
-     {"\U0000ac00\U00000900", {44032}, {6}},
-     {"\U0000ac00\U00000308\U00000900", {44032}, {8}},
-     {"\U0000ac00\U0000094d", {44032}, {6}},
-     {"\U0000ac00\U00000308\U0000094d", {44032}, {8}},
-     {"\U0000ac00\U0000200d", {44032}, {6}},
-     {"\U0000ac00\U00000308\U0000200d", {44032}, {8}},
+     {"\U0000ac00\U000000a9", {44032, 169}, {3, 5}},
+     {"\U0000ac00\U00000308\U000000a9", {44032, 169}, {5, 7}},
+     {"\U0000ac00\U00000020", {44032, 32}, {3, 4}},
+     {"\U0000ac00\U00000308\U00000020", {44032, 32}, {5, 6}},
      {"\U0000ac00\U00000378", {44032, 888}, {3, 5}},
      {"\U0000ac00\U00000308\U00000378", {44032, 888}, {5, 7}},
-     {"\U0000ac01\U00000020", {44033, 32}, {3, 4}},
-     {"\U0000ac01\U00000308\U00000020", {44033, 32}, {5, 6}},
      {"\U0000ac01\U0000000d", {44033, 13}, {3, 4}},
      {"\U0000ac01\U00000308\U0000000d", {44033, 13}, {5, 6}},
      {"\U0000ac01\U0000000a", {44033, 10}, {3, 4}},
      {"\U0000ac01\U00000308\U0000000a", {44033, 10}, {5, 6}},
-     {"\U0000ac01\U00000001", {44033, 1}, {3, 4}},
-     {"\U0000ac01\U00000308\U00000001", {44033, 1}, {5, 6}},
+     {"\U0000ac01\U00000000", {44033, 0}, {3, 4}},
+     {"\U0000ac01\U00000308\U00000000", {44033, 0}, {5, 6}},
+     {"\U0000ac01\U0000094d", {44033}, {6}},
+     {"\U0000ac01\U00000308\U0000094d", {44033}, {8}},
+     {"\U0000ac01\U00000300", {44033}, {5}},
+     {"\U0000ac01\U00000308\U00000300", {44033}, {7}},
      {"\U0000ac01\U0000200c", {44033}, {6}},
      {"\U0000ac01\U00000308\U0000200c", {44033}, {8}},
+     {"\U0000ac01\U0000200d", {44033}, {6}},
+     {"\U0000ac01\U00000308\U0000200d", {44033}, {8}},
      {"\U0000ac01\U0001f1e6", {44033, 127462}, {3, 7}},
      {"\U0000ac01\U00000308\U0001f1e6", {44033, 127462}, {5, 9}},
-     {"\U0000ac01\U00000600", {44033, 1536}, {3, 5}},
-     {"\U0000ac01\U00000308\U00000600", {44033, 1536}, {5, 7}},
-     {"\U0000ac01\U00000a03", {44033}, {6}},
-     {"\U0000ac01\U00000308\U00000a03", {44033}, {8}},
+     {"\U0000ac01\U000006dd", {44033, 1757}, {3, 5}},
+     {"\U0000ac01\U00000308\U000006dd", {44033, 1757}, {5, 7}},
+     {"\U0000ac01\U00000903", {44033}, {6}},
+     {"\U0000ac01\U00000308\U00000903", {44033}, {8}},
      {"\U0000ac01\U00001100", {44033, 4352}, {3, 6}},
      {"\U0000ac01\U00000308\U00001100", {44033, 4352}, {5, 8}},
      {"\U0000ac01\U00001160", {44033, 4448}, {3, 6}},
@@ -661,180 +673,76 @@ std::array<data<char>, 1093> data_utf8 = {{
      {"\U0000ac01\U00000308\U0000ac00", {44033, 44032}, {5, 8}},
      {"\U0000ac01\U0000ac01", {44033, 44033}, {3, 6}},
      {"\U0000ac01\U00000308\U0000ac01", {44033, 44033}, {5, 8}},
-     {"\U0000ac01\U00000903", {44033}, {6}},
-     {"\U0000ac01\U00000308\U00000903", {44033}, {8}},
-     {"\U0000ac01\U00000904", {44033, 2308}, {3, 6}},
-     {"\U0000ac01\U00000308\U00000904", {44033, 2308}, {5, 8}},
-     {"\U0000ac01\U00000d4e", {44033, 3406}, {3, 6}},
-     {"\U0000ac01\U00000308\U00000d4e", {44033, 3406}, {5, 8}},
+     {"\U0000ac01\U00001cf5", {44033, 7413}, {3, 6}},
+     {"\U0000ac01\U00000308\U00001cf5", {44033, 7413}, {5, 8}},
      {"\U0000ac01\U00000915", {44033, 2325}, {3, 6}},
      {"\U0000ac01\U00000308\U00000915", {44033, 2325}, {5, 8}},
-     {"\U0000ac01\U0000231a", {44033, 8986}, {3, 6}},
-     {"\U0000ac01\U00000308\U0000231a", {44033, 8986}, {5, 8}},
-     {"\U0000ac01\U00000300", {44033}, {5}},
-     {"\U0000ac01\U00000308\U00000300", {44033}, {7}},
-     {"\U0000ac01\U00000900", {44033}, {6}},
-     {"\U0000ac01\U00000308\U00000900", {44033}, {8}},
-     {"\U0000ac01\U0000094d", {44033}, {6}},
-     {"\U0000ac01\U00000308\U0000094d", {44033}, {8}},
-     {"\U0000ac01\U0000200d", {44033}, {6}},
-     {"\U0000ac01\U00000308\U0000200d", {44033}, {8}},
+     {"\U0000ac01\U000000a9", {44033, 169}, {3, 5}},
+     {"\U0000ac01\U00000308\U000000a9", {44033, 169}, {5, 7}},
+     {"\U0000ac01\U00000020", {44033, 32}, {3, 4}},
+     {"\U0000ac01\U00000308\U00000020", {44033, 32}, {5, 6}},
      {"\U0000ac01\U00000378", {44033, 888}, {3, 5}},
      {"\U0000ac01\U00000308\U00000378", {44033, 888}, {5, 7}},
-     {"\U00000903\U00000020", {2307, 32}, {3, 4}},
-     {"\U00000903\U00000308\U00000020", {2307, 32}, {5, 6}},
-     {"\U00000903\U0000000d", {2307, 13}, {3, 4}},
-     {"\U00000903\U00000308\U0000000d", {2307, 13}, {5, 6}},
-     {"\U00000903\U0000000a", {2307, 10}, {3, 4}},
-     {"\U00000903\U00000308\U0000000a", {2307, 10}, {5, 6}},
-     {"\U00000903\U00000001", {2307, 1}, {3, 4}},
-     {"\U00000903\U00000308\U00000001", {2307, 1}, {5, 6}},
-     {"\U00000903\U0000200c", {2307}, {6}},
-     {"\U00000903\U00000308\U0000200c", {2307}, {8}},
-     {"\U00000903\U0001f1e6", {2307, 127462}, {3, 7}},
-     {"\U00000903\U00000308\U0001f1e6", {2307, 127462}, {5, 9}},
-     {"\U00000903\U00000600", {2307, 1536}, {3, 5}},
-     {"\U00000903\U00000308\U00000600", {2307, 1536}, {5, 7}},
-     {"\U00000903\U00000a03", {2307}, {6}},
-     {"\U00000903\U00000308\U00000a03", {2307}, {8}},
-     {"\U00000903\U00001100", {2307, 4352}, {3, 6}},
-     {"\U00000903\U00000308\U00001100", {2307, 4352}, {5, 8}},
-     {"\U00000903\U00001160", {2307, 4448}, {3, 6}},
-     {"\U00000903\U00000308\U00001160", {2307, 4448}, {5, 8}},
-     {"\U00000903\U000011a8", {2307, 4520}, {3, 6}},
-     {"\U00000903\U00000308\U000011a8", {2307, 4520}, {5, 8}},
-     {"\U00000903\U0000ac00", {2307, 44032}, {3, 6}},
-     {"\U00000903\U00000308\U0000ac00", {2307, 44032}, {5, 8}},
-     {"\U00000903\U0000ac01", {2307, 44033}, {3, 6}},
-     {"\U00000903\U00000308\U0000ac01", {2307, 44033}, {5, 8}},
-     {"\U00000903\U00000903", {2307}, {6}},
-     {"\U00000903\U00000308\U00000903", {2307}, {8}},
-     {"\U00000903\U00000904", {2307, 2308}, {3, 6}},
-     {"\U00000903\U00000308\U00000904", {2307, 2308}, {5, 8}},
-     {"\U00000903\U00000d4e", {2307, 3406}, {3, 6}},
-     {"\U00000903\U00000308\U00000d4e", {2307, 3406}, {5, 8}},
-     {"\U00000903\U00000915", {2307, 2325}, {3, 6}},
-     {"\U00000903\U00000308\U00000915", {2307, 2325}, {5, 8}},
-     {"\U00000903\U0000231a", {2307, 8986}, {3, 6}},
-     {"\U00000903\U00000308\U0000231a", {2307, 8986}, {5, 8}},
-     {"\U00000903\U00000300", {2307}, {5}},
-     {"\U00000903\U00000308\U00000300", {2307}, {7}},
-     {"\U00000903\U00000900", {2307}, {6}},
-     {"\U00000903\U00000308\U00000900", {2307}, {8}},
-     {"\U00000903\U0000094d", {2307}, {6}},
-     {"\U00000903\U00000308\U0000094d", {2307}, {8}},
-     {"\U00000903\U0000200d", {2307}, {6}},
-     {"\U00000903\U00000308\U0000200d", {2307}, {8}},
-     {"\U00000903\U00000378", {2307, 888}, {3, 5}},
-     {"\U00000903\U00000308\U00000378", {2307, 888}, {5, 7}},
-     {"\U00000904\U00000020", {2308, 32}, {3, 4}},
-     {"\U00000904\U00000308\U00000020", {2308, 32}, {5, 6}},
-     {"\U00000904\U0000000d", {2308, 13}, {3, 4}},
-     {"\U00000904\U00000308\U0000000d", {2308, 13}, {5, 6}},
-     {"\U00000904\U0000000a", {2308, 10}, {3, 4}},
-     {"\U00000904\U00000308\U0000000a", {2308, 10}, {5, 6}},
-     {"\U00000904\U00000001", {2308, 1}, {3, 4}},
-     {"\U00000904\U00000308\U00000001", {2308, 1}, {5, 6}},
-     {"\U00000904\U0000200c", {2308}, {6}},
-     {"\U00000904\U00000308\U0000200c", {2308}, {8}},
-     {"\U00000904\U0001f1e6", {2308, 127462}, {3, 7}},
-     {"\U00000904\U00000308\U0001f1e6", {2308, 127462}, {5, 9}},
-     {"\U00000904\U00000600", {2308, 1536}, {3, 5}},
-     {"\U00000904\U00000308\U00000600", {2308, 1536}, {5, 7}},
-     {"\U00000904\U00000a03", {2308}, {6}},
-     {"\U00000904\U00000308\U00000a03", {2308}, {8}},
-     {"\U00000904\U00001100", {2308, 4352}, {3, 6}},
-     {"\U00000904\U00000308\U00001100", {2308, 4352}, {5, 8}},
-     {"\U00000904\U00001160", {2308, 4448}, {3, 6}},
-     {"\U00000904\U00000308\U00001160", {2308, 4448}, {5, 8}},
-     {"\U00000904\U000011a8", {2308, 4520}, {3, 6}},
-     {"\U00000904\U00000308\U000011a8", {2308, 4520}, {5, 8}},
-     {"\U00000904\U0000ac00", {2308, 44032}, {3, 6}},
-     {"\U00000904\U00000308\U0000ac00", {2308, 44032}, {5, 8}},
-     {"\U00000904\U0000ac01", {2308, 44033}, {3, 6}},
-     {"\U00000904\U00000308\U0000ac01", {2308, 44033}, {5, 8}},
-     {"\U00000904\U00000903", {2308}, {6}},
-     {"\U00000904\U00000308\U00000903", {2308}, {8}},
-     {"\U00000904\U00000904", {2308, 2308}, {3, 6}},
-     {"\U00000904\U00000308\U00000904", {2308, 2308}, {5, 8}},
-     {"\U00000904\U00000d4e", {2308, 3406}, {3, 6}},
-     {"\U00000904\U00000308\U00000d4e", {2308, 3406}, {5, 8}},
-     {"\U00000904\U00000915", {2308, 2325}, {3, 6}},
-     {"\U00000904\U00000308\U00000915", {2308, 2325}, {5, 8}},
-     {"\U00000904\U0000231a", {2308, 8986}, {3, 6}},
-     {"\U00000904\U00000308\U0000231a", {2308, 8986}, {5, 8}},
-     {"\U00000904\U00000300", {2308}, {5}},
-     {"\U00000904\U00000308\U00000300", {2308}, {7}},
-     {"\U00000904\U00000900", {2308}, {6}},
-     {"\U00000904\U00000308\U00000900", {2308}, {8}},
-     {"\U00000904\U0000094d", {2308}, {6}},
-     {"\U00000904\U00000308\U0000094d", {2308}, {8}},
-     {"\U00000904\U0000200d", {2308}, {6}},
-     {"\U00000904\U00000308\U0000200d", {2308}, {8}},
-     {"\U00000904\U00000378", {2308, 888}, {3, 5}},
-     {"\U00000904\U00000308\U00000378", {2308, 888}, {5, 7}},
-     {"\U00000d4e\U00000020", {3406}, {4}},
-     {"\U00000d4e\U00000308\U00000020", {3406, 32}, {5, 6}},
-     {"\U00000d4e\U0000000d", {3406, 13}, {3, 4}},
-     {"\U00000d4e\U00000308\U0000000d", {3406, 13}, {5, 6}},
-     {"\U00000d4e\U0000000a", {3406, 10}, {3, 4}},
-     {"\U00000d4e\U00000308\U0000000a", {3406, 10}, {5, 6}},
-     {"\U00000d4e\U00000001", {3406, 1}, {3, 4}},
-     {"\U00000d4e\U00000308\U00000001", {3406, 1}, {5, 6}},
-     {"\U00000d4e\U0000200c", {3406}, {6}},
-     {"\U00000d4e\U00000308\U0000200c", {3406}, {8}},
-     {"\U00000d4e\U0001f1e6", {3406}, {7}},
-     {"\U00000d4e\U00000308\U0001f1e6", {3406, 127462}, {5, 9}},
-     {"\U00000d4e\U00000600", {3406}, {5}},
-     {"\U00000d4e\U00000308\U00000600", {3406, 1536}, {5, 7}},
-     {"\U00000d4e\U00000a03", {3406}, {6}},
-     {"\U00000d4e\U00000308\U00000a03", {3406}, {8}},
-     {"\U00000d4e\U00001100", {3406}, {6}},
-     {"\U00000d4e\U00000308\U00001100", {3406, 4352}, {5, 8}},
-     {"\U00000d4e\U00001160", {3406}, {6}},
-     {"\U00000d4e\U00000308\U00001160", {3406, 4448}, {5, 8}},
-     {"\U00000d4e\U000011a8", {3406}, {6}},
-     {"\U00000d4e\U00000308\U000011a8", {3406, 4520}, {5, 8}},
-     {"\U00000d4e\U0000ac00", {3406}, {6}},
-     {"\U00000d4e\U00000308\U0000ac00", {3406, 44032}, {5, 8}},
-     {"\U00000d4e\U0000ac01", {3406}, {6}},
-     {"\U00000d4e\U00000308\U0000ac01", {3406, 44033}, {5, 8}},
-     {"\U00000d4e\U00000903", {3406}, {6}},
-     {"\U00000d4e\U00000308\U00000903", {3406}, {8}},
-     {"\U00000d4e\U00000904", {3406}, {6}},
-     {"\U00000d4e\U00000308\U00000904", {3406, 2308}, {5, 8}},
-     {"\U00000d4e\U00000d4e", {3406}, {6}},
-     {"\U00000d4e\U00000308\U00000d4e", {3406, 3406}, {5, 8}},
-     {"\U00000d4e\U00000915", {3406}, {6}},
-     {"\U00000d4e\U00000308\U00000915", {3406, 2325}, {5, 8}},
-     {"\U00000d4e\U0000231a", {3406}, {6}},
-     {"\U00000d4e\U00000308\U0000231a", {3406, 8986}, {5, 8}},
-     {"\U00000d4e\U00000300", {3406}, {5}},
-     {"\U00000d4e\U00000308\U00000300", {3406}, {7}},
-     {"\U00000d4e\U00000900", {3406}, {6}},
-     {"\U00000d4e\U00000308\U00000900", {3406}, {8}},
-     {"\U00000d4e\U0000094d", {3406}, {6}},
-     {"\U00000d4e\U00000308\U0000094d", {3406}, {8}},
-     {"\U00000d4e\U0000200d", {3406}, {6}},
-     {"\U00000d4e\U00000308\U0000200d", {3406}, {8}},
-     {"\U00000d4e\U00000378", {3406}, {5}},
-     {"\U00000d4e\U00000308\U00000378", {3406, 888}, {5, 7}},
-     {"\U00000915\U00000020", {2325, 32}, {3, 4}},
-     {"\U00000915\U00000308\U00000020", {2325, 32}, {5, 6}},
+     {"\U00001cf5\U0000000d", {7413, 13}, {3, 4}},
+     {"\U00001cf5\U00000308\U0000000d", {7413, 13}, {5, 6}},
+     {"\U00001cf5\U0000000a", {7413, 10}, {3, 4}},
+     {"\U00001cf5\U00000308\U0000000a", {7413, 10}, {5, 6}},
+     {"\U00001cf5\U00000000", {7413, 0}, {3, 4}},
+     {"\U00001cf5\U00000308\U00000000", {7413, 0}, {5, 6}},
+     {"\U00001cf5\U0000094d", {7413}, {6}},
+     {"\U00001cf5\U00000308\U0000094d", {7413}, {8}},
+     {"\U00001cf5\U00000300", {7413}, {5}},
+     {"\U00001cf5\U00000308\U00000300", {7413}, {7}},
+     {"\U00001cf5\U0000200c", {7413}, {6}},
+     {"\U00001cf5\U00000308\U0000200c", {7413}, {8}},
+     {"\U00001cf5\U0000200d", {7413}, {6}},
+     {"\U00001cf5\U00000308\U0000200d", {7413}, {8}},
+     {"\U00001cf5\U0001f1e6", {7413, 127462}, {3, 7}},
+     {"\U00001cf5\U00000308\U0001f1e6", {7413, 127462}, {5, 9}},
+     {"\U00001cf5\U000006dd", {7413, 1757}, {3, 5}},
+     {"\U00001cf5\U00000308\U000006dd", {7413, 1757}, {5, 7}},
+     {"\U00001cf5\U00000903", {7413}, {6}},
+     {"\U00001cf5\U00000308\U00000903", {7413}, {8}},
+     {"\U00001cf5\U00001100", {7413, 4352}, {3, 6}},
+     {"\U00001cf5\U00000308\U00001100", {7413, 4352}, {5, 8}},
+     {"\U00001cf5\U00001160", {7413, 4448}, {3, 6}},
+     {"\U00001cf5\U00000308\U00001160", {7413, 4448}, {5, 8}},
+     {"\U00001cf5\U000011a8", {7413, 4520}, {3, 6}},
+     {"\U00001cf5\U00000308\U000011a8", {7413, 4520}, {5, 8}},
+     {"\U00001cf5\U0000ac00", {7413, 44032}, {3, 6}},
+     {"\U00001cf5\U00000308\U0000ac00", {7413, 44032}, {5, 8}},
+     {"\U00001cf5\U0000ac01", {7413, 44033}, {3, 6}},
+     {"\U00001cf5\U00000308\U0000ac01", {7413, 44033}, {5, 8}},
+     {"\U00001cf5\U00001cf5", {7413, 7413}, {3, 6}},
+     {"\U00001cf5\U00000308\U00001cf5", {7413, 7413}, {5, 8}},
+     {"\U00001cf5\U00000915", {7413}, {6}},
+     {"\U00001cf5\U00000308\U00000915", {7413}, {8}},
+     {"\U00001cf5\U000000a9", {7413, 169}, {3, 5}},
+     {"\U00001cf5\U00000308\U000000a9", {7413, 169}, {5, 7}},
+     {"\U00001cf5\U00000020", {7413, 32}, {3, 4}},
+     {"\U00001cf5\U00000308\U00000020", {7413, 32}, {5, 6}},
+     {"\U00001cf5\U00000378", {7413, 888}, {3, 5}},
+     {"\U00001cf5\U00000308\U00000378", {7413, 888}, {5, 7}},
      {"\U00000915\U0000000d", {2325, 13}, {3, 4}},
      {"\U00000915\U00000308\U0000000d", {2325, 13}, {5, 6}},
      {"\U00000915\U0000000a", {2325, 10}, {3, 4}},
      {"\U00000915\U00000308\U0000000a", {2325, 10}, {5, 6}},
-     {"\U00000915\U00000001", {2325, 1}, {3, 4}},
-     {"\U00000915\U00000308\U00000001", {2325, 1}, {5, 6}},
+     {"\U00000915\U00000000", {2325, 0}, {3, 4}},
+     {"\U00000915\U00000308\U00000000", {2325, 0}, {5, 6}},
+     {"\U00000915\U0000094d", {2325}, {6}},
+     {"\U00000915\U00000308\U0000094d", {2325}, {8}},
+     {"\U00000915\U00000300", {2325}, {5}},
+     {"\U00000915\U00000308\U00000300", {2325}, {7}},
      {"\U00000915\U0000200c", {2325}, {6}},
      {"\U00000915\U00000308\U0000200c", {2325}, {8}},
+     {"\U00000915\U0000200d", {2325}, {6}},
+     {"\U00000915\U00000308\U0000200d", {2325}, {8}},
      {"\U00000915\U0001f1e6", {2325, 127462}, {3, 7}},
      {"\U00000915\U00000308\U0001f1e6", {2325, 127462}, {5, 9}},
-     {"\U00000915\U00000600", {2325, 1536}, {3, 5}},
-     {"\U00000915\U00000308\U00000600", {2325, 1536}, {5, 7}},
-     {"\U00000915\U00000a03", {2325}, {6}},
-     {"\U00000915\U00000308\U00000a03", {2325}, {8}},
+     {"\U00000915\U000006dd", {2325, 1757}, {3, 5}},
+     {"\U00000915\U00000308\U000006dd", {2325, 1757}, {5, 7}},
+     {"\U00000915\U00000903", {2325}, {6}},
+     {"\U00000915\U00000308\U00000903", {2325}, {8}},
      {"\U00000915\U00001100", {2325, 4352}, {3, 6}},
      {"\U00000915\U00000308\U00001100", {2325, 4352}, {5, 8}},
      {"\U00000915\U00001160", {2325, 4448}, {3, 6}},
@@ -845,300 +753,134 @@ std::array<data<char>, 1093> data_utf8 = {{
      {"\U00000915\U00000308\U0000ac00", {2325, 44032}, {5, 8}},
      {"\U00000915\U0000ac01", {2325, 44033}, {3, 6}},
      {"\U00000915\U00000308\U0000ac01", {2325, 44033}, {5, 8}},
-     {"\U00000915\U00000903", {2325}, {6}},
-     {"\U00000915\U00000308\U00000903", {2325}, {8}},
-     {"\U00000915\U00000904", {2325, 2308}, {3, 6}},
-     {"\U00000915\U00000308\U00000904", {2325, 2308}, {5, 8}},
-     {"\U00000915\U00000d4e", {2325, 3406}, {3, 6}},
-     {"\U00000915\U00000308\U00000d4e", {2325, 3406}, {5, 8}},
+     {"\U00000915\U00001cf5", {2325, 7413}, {3, 6}},
+     {"\U00000915\U00000308\U00001cf5", {2325, 7413}, {5, 8}},
      {"\U00000915\U00000915", {2325, 2325}, {3, 6}},
      {"\U00000915\U00000308\U00000915", {2325, 2325}, {5, 8}},
-     {"\U00000915\U0000231a", {2325, 8986}, {3, 6}},
-     {"\U00000915\U00000308\U0000231a", {2325, 8986}, {5, 8}},
-     {"\U00000915\U00000300", {2325}, {5}},
-     {"\U00000915\U00000308\U00000300", {2325}, {7}},
-     {"\U00000915\U00000900", {2325}, {6}},
-     {"\U00000915\U00000308\U00000900", {2325}, {8}},
-     {"\U00000915\U0000094d", {2325}, {6}},
-     {"\U00000915\U00000308\U0000094d", {2325}, {8}},
-     {"\U00000915\U0000200d", {2325}, {6}},
-     {"\U00000915\U00000308\U0000200d", {2325}, {8}},
+     {"\U00000915\U000000a9", {2325, 169}, {3, 5}},
+     {"\U00000915\U00000308\U000000a9", {2325, 169}, {5, 7}},
+     {"\U00000915\U00000020", {2325, 32}, {3, 4}},
+     {"\U00000915\U00000308\U00000020", {2325, 32}, {5, 6}},
      {"\U00000915\U00000378", {2325, 888}, {3, 5}},
      {"\U00000915\U00000308\U00000378", {2325, 888}, {5, 7}},
-     {"\U0000231a\U00000020", {8986, 32}, {3, 4}},
-     {"\U0000231a\U00000308\U00000020", {8986, 32}, {5, 6}},
-     {"\U0000231a\U0000000d", {8986, 13}, {3, 4}},
-     {"\U0000231a\U00000308\U0000000d", {8986, 13}, {5, 6}},
-     {"\U0000231a\U0000000a", {8986, 10}, {3, 4}},
-     {"\U0000231a\U00000308\U0000000a", {8986, 10}, {5, 6}},
-     {"\U0000231a\U00000001", {8986, 1}, {3, 4}},
-     {"\U0000231a\U00000308\U00000001", {8986, 1}, {5, 6}},
-     {"\U0000231a\U0000200c", {8986}, {6}},
-     {"\U0000231a\U00000308\U0000200c", {8986}, {8}},
-     {"\U0000231a\U0001f1e6", {8986, 127462}, {3, 7}},
-     {"\U0000231a\U00000308\U0001f1e6", {8986, 127462}, {5, 9}},
-     {"\U0000231a\U00000600", {8986, 1536}, {3, 5}},
-     {"\U0000231a\U00000308\U00000600", {8986, 1536}, {5, 7}},
-     {"\U0000231a\U00000a03", {8986}, {6}},
-     {"\U0000231a\U00000308\U00000a03", {8986}, {8}},
-     {"\U0000231a\U00001100", {8986, 4352}, {3, 6}},
-     {"\U0000231a\U00000308\U00001100", {8986, 4352}, {5, 8}},
-     {"\U0000231a\U00001160", {8986, 4448}, {3, 6}},
-     {"\U0000231a\U00000308\U00001160", {8986, 4448}, {5, 8}},
-     {"\U0000231a\U000011a8", {8986, 4520}, {3, 6}},
-     {"\U0000231a\U00000308\U000011a8", {8986, 4520}, {5, 8}},
-     {"\U0000231a\U0000ac00", {8986, 44032}, {3, 6}},
-     {"\U0000231a\U00000308\U0000ac00", {8986, 44032}, {5, 8}},
-     {"\U0000231a\U0000ac01", {8986, 44033}, {3, 6}},
-     {"\U0000231a\U00000308\U0000ac01", {8986, 44033}, {5, 8}},
-     {"\U0000231a\U00000903", {8986}, {6}},
-     {"\U0000231a\U00000308\U00000903", {8986}, {8}},
-     {"\U0000231a\U00000904", {8986, 2308}, {3, 6}},
-     {"\U0000231a\U00000308\U00000904", {8986, 2308}, {5, 8}},
-     {"\U0000231a\U00000d4e", {8986, 3406}, {3, 6}},
-     {"\U0000231a\U00000308\U00000d4e", {8986, 3406}, {5, 8}},
-     {"\U0000231a\U00000915", {8986, 2325}, {3, 6}},
-     {"\U0000231a\U00000308\U00000915", {8986, 2325}, {5, 8}},
-     {"\U0000231a\U0000231a", {8986, 8986}, {3, 6}},
-     {"\U0000231a\U00000308\U0000231a", {8986, 8986}, {5, 8}},
-     {"\U0000231a\U00000300", {8986}, {5}},
-     {"\U0000231a\U00000308\U00000300", {8986}, {7}},
-     {"\U0000231a\U00000900", {8986}, {6}},
-     {"\U0000231a\U00000308\U00000900", {8986}, {8}},
-     {"\U0000231a\U0000094d", {8986}, {6}},
-     {"\U0000231a\U00000308\U0000094d", {8986}, {8}},
-     {"\U0000231a\U0000200d", {8986}, {6}},
-     {"\U0000231a\U00000308\U0000200d", {8986}, {8}},
-     {"\U0000231a\U00000378", {8986, 888}, {3, 5}},
-     {"\U0000231a\U00000308\U00000378", {8986, 888}, {5, 7}},
-     {"\U00000300\U00000020", {768, 32}, {2, 3}},
-     {"\U00000300\U00000308\U00000020", {768, 32}, {4, 5}},
-     {"\U00000300\U0000000d", {768, 13}, {2, 3}},
-     {"\U00000300\U00000308\U0000000d", {768, 13}, {4, 5}},
-     {"\U00000300\U0000000a", {768, 10}, {2, 3}},
-     {"\U00000300\U00000308\U0000000a", {768, 10}, {4, 5}},
-     {"\U00000300\U00000001", {768, 1}, {2, 3}},
-     {"\U00000300\U00000308\U00000001", {768, 1}, {4, 5}},
-     {"\U00000300\U0000200c", {768}, {5}},
-     {"\U00000300\U00000308\U0000200c", {768}, {7}},
-     {"\U00000300\U0001f1e6", {768, 127462}, {2, 6}},
-     {"\U00000300\U00000308\U0001f1e6", {768, 127462}, {4, 8}},
-     {"\U00000300\U00000600", {768, 1536}, {2, 4}},
-     {"\U00000300\U00000308\U00000600", {768, 1536}, {4, 6}},
-     {"\U00000300\U00000a03", {768}, {5}},
-     {"\U00000300\U00000308\U00000a03", {768}, {7}},
-     {"\U00000300\U00001100", {768, 4352}, {2, 5}},
-     {"\U00000300\U00000308\U00001100", {768, 4352}, {4, 7}},
-     {"\U00000300\U00001160", {768, 4448}, {2, 5}},
-     {"\U00000300\U00000308\U00001160", {768, 4448}, {4, 7}},
-     {"\U00000300\U000011a8", {768, 4520}, {2, 5}},
-     {"\U00000300\U00000308\U000011a8", {768, 4520}, {4, 7}},
-     {"\U00000300\U0000ac00", {768, 44032}, {2, 5}},
-     {"\U00000300\U00000308\U0000ac00", {768, 44032}, {4, 7}},
-     {"\U00000300\U0000ac01", {768, 44033}, {2, 5}},
-     {"\U00000300\U00000308\U0000ac01", {768, 44033}, {4, 7}},
-     {"\U00000300\U00000903", {768}, {5}},
-     {"\U00000300\U00000308\U00000903", {768}, {7}},
-     {"\U00000300\U00000904", {768, 2308}, {2, 5}},
-     {"\U00000300\U00000308\U00000904", {768, 2308}, {4, 7}},
-     {"\U00000300\U00000d4e", {768, 3406}, {2, 5}},
-     {"\U00000300\U00000308\U00000d4e", {768, 3406}, {4, 7}},
-     {"\U00000300\U00000915", {768, 2325}, {2, 5}},
-     {"\U00000300\U00000308\U00000915", {768, 2325}, {4, 7}},
-     {"\U00000300\U0000231a", {768, 8986}, {2, 5}},
-     {"\U00000300\U00000308\U0000231a", {768, 8986}, {4, 7}},
-     {"\U00000300\U00000300", {768}, {4}},
-     {"\U00000300\U00000308\U00000300", {768}, {6}},
-     {"\U00000300\U00000900", {768}, {5}},
-     {"\U00000300\U00000308\U00000900", {768}, {7}},
-     {"\U00000300\U0000094d", {768}, {5}},
-     {"\U00000300\U00000308\U0000094d", {768}, {7}},
-     {"\U00000300\U0000200d", {768}, {5}},
-     {"\U00000300\U00000308\U0000200d", {768}, {7}},
-     {"\U00000300\U00000378", {768, 888}, {2, 4}},
-     {"\U00000300\U00000308\U00000378", {768, 888}, {4, 6}},
-     {"\U00000900\U00000020", {2304, 32}, {3, 4}},
-     {"\U00000900\U00000308\U00000020", {2304, 32}, {5, 6}},
-     {"\U00000900\U0000000d", {2304, 13}, {3, 4}},
-     {"\U00000900\U00000308\U0000000d", {2304, 13}, {5, 6}},
-     {"\U00000900\U0000000a", {2304, 10}, {3, 4}},
-     {"\U00000900\U00000308\U0000000a", {2304, 10}, {5, 6}},
-     {"\U00000900\U00000001", {2304, 1}, {3, 4}},
-     {"\U00000900\U00000308\U00000001", {2304, 1}, {5, 6}},
-     {"\U00000900\U0000200c", {2304}, {6}},
-     {"\U00000900\U00000308\U0000200c", {2304}, {8}},
-     {"\U00000900\U0001f1e6", {2304, 127462}, {3, 7}},
-     {"\U00000900\U00000308\U0001f1e6", {2304, 127462}, {5, 9}},
-     {"\U00000900\U00000600", {2304, 1536}, {3, 5}},
-     {"\U00000900\U00000308\U00000600", {2304, 1536}, {5, 7}},
-     {"\U00000900\U00000a03", {2304}, {6}},
-     {"\U00000900\U00000308\U00000a03", {2304}, {8}},
-     {"\U00000900\U00001100", {2304, 4352}, {3, 6}},
-     {"\U00000900\U00000308\U00001100", {2304, 4352}, {5, 8}},
-     {"\U00000900\U00001160", {2304, 4448}, {3, 6}},
-     {"\U00000900\U00000308\U00001160", {2304, 4448}, {5, 8}},
-     {"\U00000900\U000011a8", {2304, 4520}, {3, 6}},
-     {"\U00000900\U00000308\U000011a8", {2304, 4520}, {5, 8}},
-     {"\U00000900\U0000ac00", {2304, 44032}, {3, 6}},
-     {"\U00000900\U00000308\U0000ac00", {2304, 44032}, {5, 8}},
-     {"\U00000900\U0000ac01", {2304, 44033}, {3, 6}},
-     {"\U00000900\U00000308\U0000ac01", {2304, 44033}, {5, 8}},
-     {"\U00000900\U00000903", {2304}, {6}},
-     {"\U00000900\U00000308\U00000903", {2304}, {8}},
-     {"\U00000900\U00000904", {2304, 2308}, {3, 6}},
-     {"\U00000900\U00000308\U00000904", {2304, 2308}, {5, 8}},
-     {"\U00000900\U00000d4e", {2304, 3406}, {3, 6}},
-     {"\U00000900\U00000308\U00000d4e", {2304, 3406}, {5, 8}},
-     {"\U00000900\U00000915", {2304, 2325}, {3, 6}},
-     {"\U00000900\U00000308\U00000915", {2304, 2325}, {5, 8}},
-     {"\U00000900\U0000231a", {2304, 8986}, {3, 6}},
-     {"\U00000900\U00000308\U0000231a", {2304, 8986}, {5, 8}},
-     {"\U00000900\U00000300", {2304}, {5}},
-     {"\U00000900\U00000308\U00000300", {2304}, {7}},
-     {"\U00000900\U00000900", {2304}, {6}},
-     {"\U00000900\U00000308\U00000900", {2304}, {8}},
-     {"\U00000900\U0000094d", {2304}, {6}},
-     {"\U00000900\U00000308\U0000094d", {2304}, {8}},
-     {"\U00000900\U0000200d", {2304}, {6}},
-     {"\U00000900\U00000308\U0000200d", {2304}, {8}},
-     {"\U00000900\U00000378", {2304, 888}, {3, 5}},
-     {"\U00000900\U00000308\U00000378", {2304, 888}, {5, 7}},
-     {"\U0000094d\U00000020", {2381, 32}, {3, 4}},
-     {"\U0000094d\U00000308\U00000020", {2381, 32}, {5, 6}},
-     {"\U0000094d\U0000000d", {2381, 13}, {3, 4}},
-     {"\U0000094d\U00000308\U0000000d", {2381, 13}, {5, 6}},
-     {"\U0000094d\U0000000a", {2381, 10}, {3, 4}},
-     {"\U0000094d\U00000308\U0000000a", {2381, 10}, {5, 6}},
-     {"\U0000094d\U00000001", {2381, 1}, {3, 4}},
-     {"\U0000094d\U00000308\U00000001", {2381, 1}, {5, 6}},
-     {"\U0000094d\U0000200c", {2381}, {6}},
-     {"\U0000094d\U00000308\U0000200c", {2381}, {8}},
-     {"\U0000094d\U0001f1e6", {2381, 127462}, {3, 7}},
-     {"\U0000094d\U00000308\U0001f1e6", {2381, 127462}, {5, 9}},
-     {"\U0000094d\U00000600", {2381, 1536}, {3, 5}},
-     {"\U0000094d\U00000308\U00000600", {2381, 1536}, {5, 7}},
-     {"\U0000094d\U00000a03", {2381}, {6}},
-     {"\U0000094d\U00000308\U00000a03", {2381}, {8}},
-     {"\U0000094d\U00001100", {2381, 4352}, {3, 6}},
-     {"\U0000094d\U00000308\U00001100", {2381, 4352}, {5, 8}},
-     {"\U0000094d\U00001160", {2381, 4448}, {3, 6}},
-     {"\U0000094d\U00000308\U00001160", {2381, 4448}, {5, 8}},
-     {"\U0000094d\U000011a8", {2381, 4520}, {3, 6}},
-     {"\U0000094d\U00000308\U000011a8", {2381, 4520}, {5, 8}},
-     {"\U0000094d\U0000ac00", {2381, 44032}, {3, 6}},
-     {"\U0000094d\U00000308\U0000ac00", {2381, 44032}, {5, 8}},
-     {"\U0000094d\U0000ac01", {2381, 44033}, {3, 6}},
-     {"\U0000094d\U00000308\U0000ac01", {2381, 44033}, {5, 8}},
-     {"\U0000094d\U00000903", {2381}, {6}},
-     {"\U0000094d\U00000308\U00000903", {2381}, {8}},
-     {"\U0000094d\U00000904", {2381, 2308}, {3, 6}},
-     {"\U0000094d\U00000308\U00000904", {2381, 2308}, {5, 8}},
-     {"\U0000094d\U00000d4e", {2381, 3406}, {3, 6}},
-     {"\U0000094d\U00000308\U00000d4e", {2381, 3406}, {5, 8}},
-     {"\U0000094d\U00000915", {2381, 2325}, {3, 6}},
-     {"\U0000094d\U00000308\U00000915", {2381, 2325}, {5, 8}},
-     {"\U0000094d\U0000231a", {2381, 8986}, {3, 6}},
-     {"\U0000094d\U00000308\U0000231a", {2381, 8986}, {5, 8}},
-     {"\U0000094d\U00000300", {2381}, {5}},
-     {"\U0000094d\U00000308\U00000300", {2381}, {7}},
-     {"\U0000094d\U00000900", {2381}, {6}},
-     {"\U0000094d\U00000308\U00000900", {2381}, {8}},
-     {"\U0000094d\U0000094d", {2381}, {6}},
-     {"\U0000094d\U00000308\U0000094d", {2381}, {8}},
-     {"\U0000094d\U0000200d", {2381}, {6}},
-     {"\U0000094d\U00000308\U0000200d", {2381}, {8}},
-     {"\U0000094d\U00000378", {2381, 888}, {3, 5}},
-     {"\U0000094d\U00000308\U00000378", {2381, 888}, {5, 7}},
-     {"\U0000200d\U00000020", {8205, 32}, {3, 4}},
-     {"\U0000200d\U00000308\U00000020", {8205, 32}, {5, 6}},
-     {"\U0000200d\U0000000d", {8205, 13}, {3, 4}},
-     {"\U0000200d\U00000308\U0000000d", {8205, 13}, {5, 6}},
-     {"\U0000200d\U0000000a", {8205, 10}, {3, 4}},
-     {"\U0000200d\U00000308\U0000000a", {8205, 10}, {5, 6}},
-     {"\U0000200d\U00000001", {8205, 1}, {3, 4}},
-     {"\U0000200d\U00000308\U00000001", {8205, 1}, {5, 6}},
-     {"\U0000200d\U0000200c", {8205}, {6}},
-     {"\U0000200d\U00000308\U0000200c", {8205}, {8}},
-     {"\U0000200d\U0001f1e6", {8205, 127462}, {3, 7}},
-     {"\U0000200d\U00000308\U0001f1e6", {8205, 127462}, {5, 9}},
-     {"\U0000200d\U00000600", {8205, 1536}, {3, 5}},
-     {"\U0000200d\U00000308\U00000600", {8205, 1536}, {5, 7}},
-     {"\U0000200d\U00000a03", {8205}, {6}},
-     {"\U0000200d\U00000308\U00000a03", {8205}, {8}},
-     {"\U0000200d\U00001100", {8205, 4352}, {3, 6}},
-     {"\U0000200d\U00000308\U00001100", {8205, 4352}, {5, 8}},
-     {"\U0000200d\U00001160", {8205, 4448}, {3, 6}},
-     {"\U0000200d\U00000308\U00001160", {8205, 4448}, {5, 8}},
-     {"\U0000200d\U000011a8", {8205, 4520}, {3, 6}},
-     {"\U0000200d\U00000308\U000011a8", {8205, 4520}, {5, 8}},
-     {"\U0000200d\U0000ac00", {8205, 44032}, {3, 6}},
-     {"\U0000200d\U00000308\U0000ac00", {8205, 44032}, {5, 8}},
-     {"\U0000200d\U0000ac01", {8205, 44033}, {3, 6}},
-     {"\U0000200d\U00000308\U0000ac01", {8205, 44033}, {5, 8}},
-     {"\U0000200d\U00000903", {8205}, {6}},
-     {"\U0000200d\U00000308\U00000903", {8205}, {8}},
-     {"\U0000200d\U00000904", {8205, 2308}, {3, 6}},
-     {"\U0000200d\U00000308\U00000904", {8205, 2308}, {5, 8}},
-     {"\U0000200d\U00000d4e", {8205, 3406}, {3, 6}},
-     {"\U0000200d\U00000308\U00000d4e", {8205, 3406}, {5, 8}},
-     {"\U0000200d\U00000915", {8205, 2325}, {3, 6}},
-     {"\U0000200d\U00000308\U00000915", {8205, 2325}, {5, 8}},
-     {"\U0000200d\U0000231a", {8205, 8986}, {3, 6}},
-     {"\U0000200d\U00000308\U0000231a", {8205, 8986}, {5, 8}},
-     {"\U0000200d\U00000300", {8205}, {5}},
-     {"\U0000200d\U00000308\U00000300", {8205}, {7}},
-     {"\U0000200d\U00000900", {8205}, {6}},
-     {"\U0000200d\U00000308\U00000900", {8205}, {8}},
-     {"\U0000200d\U0000094d", {8205}, {6}},
-     {"\U0000200d\U00000308\U0000094d", {8205}, {8}},
-     {"\U0000200d\U0000200d", {8205}, {6}},
-     {"\U0000200d\U00000308\U0000200d", {8205}, {8}},
-     {"\U0000200d\U00000378", {8205, 888}, {3, 5}},
-     {"\U0000200d\U00000308\U00000378", {8205, 888}, {5, 7}},
-     {"\U00000378\U00000020", {888, 32}, {2, 3}},
-     {"\U00000378\U00000308\U00000020", {888, 32}, {4, 5}},
-     {"\U00000378\U0000000d", {888, 13}, {2, 3}},
-     {"\U00000378\U00000308\U0000000d", {888, 13}, {4, 5}},
-     {"\U00000378\U0000000a", {888, 10}, {2, 3}},
-     {"\U00000378\U00000308\U0000000a", {888, 10}, {4, 5}},
-     {"\U00000378\U00000001", {888, 1}, {2, 3}},
-     {"\U00000378\U00000308\U00000001", {888, 1}, {4, 5}},
-     {"\U00000378\U0000200c", {888}, {5}},
-     {"\U00000378\U00000308\U0000200c", {888}, {7}},
-     {"\U00000378\U0001f1e6", {888, 127462}, {2, 6}},
-     {"\U00000378\U00000308\U0001f1e6", {888, 127462}, {4, 8}},
-     {"\U00000378\U00000600", {888, 1536}, {2, 4}},
-     {"\U00000378\U00000308\U00000600", {888, 1536}, {4, 6}},
-     {"\U00000378\U00000a03", {888}, {5}},
-     {"\U00000378\U00000308\U00000a03", {888}, {7}},
-     {"\U00000378\U00001100", {888, 4352}, {2, 5}},
-     {"\U00000378\U00000308\U00001100", {888, 4352}, {4, 7}},
-     {"\U00000378\U00001160", {888, 4448}, {2, 5}},
-     {"\U00000378\U00000308\U00001160", {888, 4448}, {4, 7}},
-     {"\U00000378\U000011a8", {888, 4520}, {2, 5}},
-     {"\U00000378\U00000308\U000011a8", {888, 4520}, {4, 7}},
-     {"\U00000378\U0000ac00", {888, 44032}, {2, 5}},
-     {"\U00000378\U00000308\U0000ac00", {888, 44032}, {4, 7}},
-     {"\U00000378\U0000ac01", {888, 44033}, {2, 5}},
-     {"\U00000378\U00000308\U0000ac01", {888, 44033}, {4, 7}},
-     {"\U00000378\U00000903", {888}, {5}},
-     {"\U00000378\U00000308\U00000903", {888}, {7}},
-     {"\U00000378\U00000904", {888, 2308}, {2, 5}},
-     {"\U00000378\U00000308\U00000904", {888, 2308}, {4, 7}},
-     {"\U00000378\U00000d4e", {888, 3406}, {2, 5}},
-     {"\U00000378\U00000308\U00000d4e", {888, 3406}, {4, 7}},
-     {"\U00000378\U00000915", {888, 2325}, {2, 5}},
-     {"\U00000378\U00000308\U00000915", {888, 2325}, {4, 7}},
-     {"\U00000378\U0000231a", {888, 8986}, {2, 5}},
-     {"\U00000378\U00000308\U0000231a", {888, 8986}, {4, 7}},
-     {"\U00000378\U00000300", {888}, {4}},
-     {"\U00000378\U00000308\U00000300", {888}, {6}},
-     {"\U00000378\U00000900", {888}, {5}},
-     {"\U00000378\U00000308\U00000900", {888}, {7}},
-     {"\U00000378\U0000094d", {888}, {5}},
-     {"\U00000378\U00000308\U0000094d", {888}, {7}},
+     {"\U000000a9\U0000000d", {169, 13}, {2, 3}},
+     {"\U000000a9\U00000308\U0000000d", {169, 13}, {4, 5}},
+     {"\U000000a9\U0000000a", {169, 10}, {2, 3}},
+     {"\U000000a9\U00000308\U0000000a", {169, 10}, {4, 5}},
+     {"\U000000a9\U00000000", {169, 0}, {2, 3}},
+     {"\U000000a9\U00000308\U00000000", {169, 0}, {4, 5}},
+     {"\U000000a9\U0000094d", {169}, {5}},
+     {"\U000000a9\U00000308\U0000094d", {169}, {7}},
+     {"\U000000a9\U00000300", {169}, {4}},
+     {"\U000000a9\U00000308\U00000300", {169}, {6}},
+     {"\U000000a9\U0000200c", {169}, {5}},
+     {"\U000000a9\U00000308\U0000200c", {169}, {7}},
+     {"\U000000a9\U0000200d", {169}, {5}},
+     {"\U000000a9\U00000308\U0000200d", {169}, {7}},
+     {"\U000000a9\U0001f1e6", {169, 127462}, {2, 6}},
+     {"\U000000a9\U00000308\U0001f1e6", {169, 127462}, {4, 8}},
+     {"\U000000a9\U000006dd", {169, 1757}, {2, 4}},
+     {"\U000000a9\U00000308\U000006dd", {169, 1757}, {4, 6}},
+     {"\U000000a9\U00000903", {169}, {5}},
+     {"\U000000a9\U00000308\U00000903", {169}, {7}},
+     {"\U000000a9\U00001100", {169, 4352}, {2, 5}},
+     {"\U000000a9\U00000308\U00001100", {169, 4352}, {4, 7}},
+     {"\U000000a9\U00001160", {169, 4448}, {2, 5}},
+     {"\U000000a9\U00000308\U00001160", {169, 4448}, {4, 7}},
+     {"\U000000a9\U000011a8", {169, 4520}, {2, 5}},
+     {"\U000000a9\U00000308\U000011a8", {169, 4520}, {4, 7}},
+     {"\U000000a9\U0000ac00", {169, 44032}, {2, 5}},
+     {"\U000000a9\U00000308\U0000ac00", {169, 44032}, {4, 7}},
+     {"\U000000a9\U0000ac01", {169, 44033}, {2, 5}},
+     {"\U000000a9\U00000308\U0000ac01", {169, 44033}, {4, 7}},
+     {"\U000000a9\U00001cf5", {169, 7413}, {2, 5}},
+     {"\U000000a9\U00000308\U00001cf5", {169, 7413}, {4, 7}},
+     {"\U000000a9\U00000915", {169, 2325}, {2, 5}},
+     {"\U000000a9\U00000308\U00000915", {169, 2325}, {4, 7}},
+     {"\U000000a9\U000000a9", {169, 169}, {2, 4}},
+     {"\U000000a9\U00000308\U000000a9", {169, 169}, {4, 6}},
+     {"\U000000a9\U00000020", {169, 32}, {2, 3}},
+     {"\U000000a9\U00000308\U00000020", {169, 32}, {4, 5}},
+     {"\U000000a9\U00000378", {169, 888}, {2, 4}},
+     {"\U000000a9\U00000308\U00000378", {169, 888}, {4, 6}},
+     {"\U00000020\U0000000d", {32, 13}, {1, 2}},
+     {"\U00000020\U00000308\U0000000d", {32, 13}, {3, 4}},
+     {"\U00000020\U0000000a", {32, 10}, {1, 2}},
+     {"\U00000020\U00000308\U0000000a", {32, 10}, {3, 4}},
+     {"\U00000020\U00000000", {32, 0}, {1, 2}},
+     {"\U00000020\U00000308\U00000000", {32, 0}, {3, 4}},
+     {"\U00000020\U0000094d", {32}, {4}},
+     {"\U00000020\U00000308\U0000094d", {32}, {6}},
+     {"\U00000020\U00000300", {32}, {3}},
+     {"\U00000020\U00000308\U00000300", {32}, {5}},
+     {"\U00000020\U0000200c", {32}, {4}},
+     {"\U00000020\U00000308\U0000200c", {32}, {6}},
+     {"\U00000020\U0000200d", {32}, {4}},
+     {"\U00000020\U00000308\U0000200d", {32}, {6}},
+     {"\U00000020\U0001f1e6", {32, 127462}, {1, 5}},
+     {"\U00000020\U00000308\U0001f1e6", {32, 127462}, {3, 7}},
+     {"\U00000020\U000006dd", {32, 1757}, {1, 3}},
+     {"\U00000020\U00000308\U000006dd", {32, 1757}, {3, 5}},
+     {"\U00000020\U00000903", {32}, {4}},
+     {"\U00000020\U00000308\U00000903", {32}, {6}},
+     {"\U00000020\U00001100", {32, 4352}, {1, 4}},
+     {"\U00000020\U00000308\U00001100", {32, 4352}, {3, 6}},
+     {"\U00000020\U00001160", {32, 4448}, {1, 4}},
+     {"\U00000020\U00000308\U00001160", {32, 4448}, {3, 6}},
+     {"\U00000020\U000011a8", {32, 4520}, {1, 4}},
+     {"\U00000020\U00000308\U000011a8", {32, 4520}, {3, 6}},
+     {"\U00000020\U0000ac00", {32, 44032}, {1, 4}},
+     {"\U00000020\U00000308\U0000ac00", {32, 44032}, {3, 6}},
+     {"\U00000020\U0000ac01", {32, 44033}, {1, 4}},
+     {"\U00000020\U00000308\U0000ac01", {32, 44033}, {3, 6}},
+     {"\U00000020\U00001cf5", {32, 7413}, {1, 4}},
+     {"\U00000020\U00000308\U00001cf5", {32, 7413}, {3, 6}},
+     {"\U00000020\U00000915", {32, 2325}, {1, 4}},
+     {"\U00000020\U00000308\U00000915", {32, 2325}, {3, 6}},
+     {"\U00000020\U000000a9", {32, 169}, {1, 3}},
+     {"\U00000020\U00000308\U000000a9", {32, 169}, {3, 5}},
+     {"\U00000020\U00000020", {32, 32}, {1, 2}},
+     {"\U00000020\U00000308\U00000020", {32, 32}, {3, 4}},
+     {"\U00000020\U00000378", {32, 888}, {1, 3}},
+     {"\U00000020\U00000308\U00000378", {32, 888}, {3, 5}},
+     {"\U00000378\U0000000d", {888, 13}, {2, 3}},
+     {"\U00000378\U00000308\U0000000d", {888, 13}, {4, 5}},
+     {"\U00000378\U0000000a", {888, 10}, {2, 3}},
+     {"\U00000378\U00000308\U0000000a", {888, 10}, {4, 5}},
+     {"\U00000378\U00000000", {888, 0}, {2, 3}},
+     {"\U00000378\U00000308\U00000000", {888, 0}, {4, 5}},
+     {"\U00000378\U0000094d", {888}, {5}},
+     {"\U00000378\U00000308\U0000094d", {888}, {7}},
+     {"\U00000378\U00000300", {888}, {4}},
+     {"\U00000378\U00000308\U00000300", {888}, {6}},
+     {"\U00000378\U0000200c", {888}, {5}},
+     {"\U00000378\U00000308\U0000200c", {888}, {7}},
      {"\U00000378\U0000200d", {888}, {5}},
      {"\U00000378\U00000308\U0000200d", {888}, {7}},
+     {"\U00000378\U0001f1e6", {888, 127462}, {2, 6}},
+     {"\U00000378\U00000308\U0001f1e6", {888, 127462}, {4, 8}},
+     {"\U00000378\U000006dd", {888, 1757}, {2, 4}},
+     {"\U00000378\U00000308\U000006dd", {888, 1757}, {4, 6}},
+     {"\U00000378\U00000903", {888}, {5}},
+     {"\U00000378\U00000308\U00000903", {888}, {7}},
+     {"\U00000378\U00001100", {888, 4352}, {2, 5}},
+     {"\U00000378\U00000308\U00001100", {888, 4352}, {4, 7}},
+     {"\U00000378\U00001160", {888, 4448}, {2, 5}},
+     {"\U00000378\U00000308\U00001160", {888, 4448}, {4, 7}},
+     {"\U00000378\U000011a8", {888, 4520}, {2, 5}},
+     {"\U00000378\U00000308\U000011a8", {888, 4520}, {4, 7}},
+     {"\U00000378\U0000ac00", {888, 44032}, {2, 5}},
+     {"\U00000378\U00000308\U0000ac00", {888, 44032}, {4, 7}},
+     {"\U00000378\U0000ac01", {888, 44033}, {2, 5}},
+     {"\U00000378\U00000308\U0000ac01", {888, 44033}, {4, 7}},
+     {"\U00000378\U00001cf5", {888, 7413}, {2, 5}},
+     {"\U00000378\U00000308\U00001cf5", {888, 7413}, {4, 7}},
+     {"\U00000378\U00000915", {888, 2325}, {2, 5}},
+     {"\U00000378\U00000308\U00000915", {888, 2325}, {4, 7}},
+     {"\U00000378\U000000a9", {888, 169}, {2, 4}},
+     {"\U00000378\U00000308\U000000a9", {888, 169}, {4, 6}},
+     {"\U00000378\U00000020", {888, 32}, {2, 3}},
+     {"\U00000378\U00000308\U00000020", {888, 32}, {4, 5}},
      {"\U00000378\U00000378", {888, 888}, {2, 4}},
      {"\U00000378\U00000308\U00000378", {888, 888}, {4, 6}},
      {"\U0000000d\U0000000a\U00000061\U0000000a\U00000308", {13, 97, 10, 776}, {2, 3, 4, 6}},
@@ -1163,7 +905,7 @@ std::array<data<char>, 1093> data_utf8 = {{
      {"\U0001f476\U0001f3ff\U00000308\U0000200d\U0001f476\U0001f3ff", {128118}, {21}},
      {"\U0001f6d1\U0000200d\U0001f6d1", {128721}, {11}},
      {"\U00000061\U0000200d\U0001f6d1", {97, 128721}, {4, 8}},
-     {"\U00002701\U0000200d\U00002701", {9985}, {9}},
+     {"\U00002701\U0000200d\U00002701", {9985, 9985}, {6, 9}},
      {"\U00000061\U0000200d\U00002701", {97, 9985}, {4, 7}},
      {"\U00000915\U00000924", {2325, 2340}, {3, 6}},
      {"\U00000915\U0000094d\U00000924", {2325}, {9}},
@@ -1173,9 +915,27 @@ std::array<data<char>, 1093> data_utf8 = {{
      {"\U00000915\U0000093c\U0000094d\U0000200d\U00000924", {2325}, {15}},
      {"\U00000915\U0000094d\U00000924\U0000094d\U0000092f", {2325}, {15}},
      {"\U00000915\U0000094d\U00000061", {2325, 97}, {6, 7}},
-     {"\U00000061\U0000094d\U00000924", {97, 2340}, {4, 7}},
-     {"\U0000003f\U0000094d\U00000924", {63, 2340}, {4, 7}},
-     {"\U00000915\U0000094d\U0000094d\U00000924", {2325}, {12}}}};
+     {"\U00000061\U0000094d\U00000924", {97}, {7}},
+     {"\U0000003f\U0000094d\U00000924", {63}, {7}},
+     {"\U00000915\U0000094d\U0000094d\U00000924", {2325}, {12}},
+     {"\U00000ab8\U00000afb\U00000acd\U00000ab8\U00000afb", {2744}, {15}},
+     {"\U00001019\U00001039\U00001018\U0000102c\U00001037", {4121, 4140}, {9, 15}},
+     {"\U00001004\U0000103a\U00001039\U00001011\U00001039\U00001011", {4100}, {18}},
+     {"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38", {6930, 6962, 6962, 6962}, {6, 15, 30, 42}},
+     {"\U0000179f\U000017d2\U0000178f\U000017d2\U0000179a\U000017b8", {6047}, {18}},
+     {"\U00001b26\U00001b17\U00001b44\U00001b13", {6950, 6935}, {3, 12}},
+     {"\U00001b27\U00001b13\U00001b44\U00001b0b\U00001b0b\U00001b04", {6951, 6931, 6923}, {3, 12, 18}},
+     {"\U00001795\U000017d2\U000017af\U00001798", {6037, 6040}, {9, 12}},
+     {"\U000017a0\U000017d2\U000017ab\U00001791\U000017d0\U00001799", {6048, 6033, 6041}, {9, 15, 18}},
+     {"\U00001b05\U00001b44\U00001b33\U00001b03", {6917}, {12}},
+     {"\U00000cf1\U00000c95", {3313, 3221}, {3, 6}},
+     {"\U00000cf2\U00000cab", {3314, 3243}, {3, 6}},
+     {"\U00000cf1\U00000c95\U00000cbf", {3313, 3221}, {3, 9}},
+     {"\U00001cf5\U00000995", {7413}, {6}},
+     {"\U00001cf6\U000009aa", {7414}, {6}},
+     {"\U00001cf5\U0000200c\U00000995", {7413, 2453}, {6, 9}},
+     {"\U00001cf6\U0000200c\U000009aa", {7414, 2474}, {6, 9}},
+     {"\U00011a3a\U00011a0b", {72250}, {8}}}};
 
 /// The data for UTF-16.
 ///
@@ -1183,69 +943,27 @@ std::array<data<char>, 1093> data_utf8 = {{
 /// since the size of the code units differ the breaks can contain different
 /// values.
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-std::array<data<wchar_t>, 1093> data_utf16 = {{
-     {L"\U00000020\U00000020", {32, 32}, {1, 2}},
-     {L"\U00000020\U00000308\U00000020", {32, 32}, {2, 3}},
-     {L"\U00000020\U0000000d", {32, 13}, {1, 2}},
-     {L"\U00000020\U00000308\U0000000d", {32, 13}, {2, 3}},
-     {L"\U00000020\U0000000a", {32, 10}, {1, 2}},
-     {L"\U00000020\U00000308\U0000000a", {32, 10}, {2, 3}},
-     {L"\U00000020\U00000001", {32, 1}, {1, 2}},
-     {L"\U00000020\U00000308\U00000001", {32, 1}, {2, 3}},
-     {L"\U00000020\U0000200c", {32}, {2}},
-     {L"\U00000020\U00000308\U0000200c", {32}, {3}},
-     {L"\U00000020\U0001f1e6", {32, 127462}, {1, 3}},
-     {L"\U00000020\U00000308\U0001f1e6", {32, 127462}, {2, 4}},
-     {L"\U00000020\U00000600", {32, 1536}, {1, 2}},
-     {L"\U00000020\U00000308\U00000600", {32, 1536}, {2, 3}},
-     {L"\U00000020\U00000a03", {32}, {2}},
-     {L"\U00000020\U00000308\U00000a03", {32}, {3}},
-     {L"\U00000020\U00001100", {32, 4352}, {1, 2}},
-     {L"\U00000020\U00000308\U00001100", {32, 4352}, {2, 3}},
-     {L"\U00000020\U00001160", {32, 4448}, {1, 2}},
-     {L"\U00000020\U00000308\U00001160", {32, 4448}, {2, 3}},
-     {L"\U00000020\U000011a8", {32, 4520}, {1, 2}},
-     {L"\U00000020\U00000308\U000011a8", {32, 4520}, {2, 3}},
-     {L"\U00000020\U0000ac00", {32, 44032}, {1, 2}},
-     {L"\U00000020\U00000308\U0000ac00", {32, 44032}, {2, 3}},
-     {L"\U00000020\U0000ac01", {32, 44033}, {1, 2}},
-     {L"\U00000020\U00000308\U0000ac01", {32, 44033}, {2, 3}},
-     {L"\U00000020\U00000903", {32}, {2}},
-     {L"\U00000020\U00000308\U00000903", {32}, {3}},
-     {L"\U00000020\U00000904", {32, 2308}, {1, 2}},
-     {L"\U00000020\U00000308\U00000904", {32, 2308}, {2, 3}},
-     {L"\U00000020\U00000d4e", {32, 3406}, {1, 2}},
-     {L"\U00000020\U00000308\U00000d4e", {32, 3406}, {2, 3}},
-     {L"\U00000020\U00000915", {32, 2325}, {1, 2}},
-     {L"\U00000020\U00000308\U00000915", {32, 2325}, {2, 3}},
-     {L"\U00000020\U0000231a", {32, 8986}, {1, 2}},
-     {L"\U00000020\U00000308\U0000231a", {32, 8986}, {2, 3}},
-     {L"\U00000020\U00000300", {32}, {2}},
-     {L"\U00000020\U00000308\U00000300", {32}, {3}},
-     {L"\U00000020\U00000900", {32}, {2}},
-     {L"\U00000020\U00000308\U00000900", {32}, {3}},
-     {L"\U00000020\U0000094d", {32}, {2}},
-     {L"\U00000020\U00000308\U0000094d", {32}, {3}},
-     {L"\U00000020\U0000200d", {32}, {2}},
-     {L"\U00000020\U00000308\U0000200d", {32}, {3}},
-     {L"\U00000020\U00000378", {32, 888}, {1, 2}},
-     {L"\U00000020\U00000308\U00000378", {32, 888}, {2, 3}},
-     {L"\U0000000d\U00000020", {13, 32}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000020", {13, 776, 32}, {1, 2, 3}},
+std::array<data<wchar_t>, 853> data_utf16 = {{
      {L"\U0000000d\U0000000d", {13, 13}, {1, 2}},
      {L"\U0000000d\U00000308\U0000000d", {13, 776, 13}, {1, 2, 3}},
      {L"\U0000000d\U0000000a", {13}, {2}},
      {L"\U0000000d\U00000308\U0000000a", {13, 776, 10}, {1, 2, 3}},
-     {L"\U0000000d\U00000001", {13, 1}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000001", {13, 776, 1}, {1, 2, 3}},
+     {L"\U0000000d\U00000000", {13, 0}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000000", {13, 776, 0}, {1, 2, 3}},
+     {L"\U0000000d\U0000094d", {13, 2381}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000094d", {13, 776}, {1, 3}},
+     {L"\U0000000d\U00000300", {13, 768}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000300", {13, 776}, {1, 3}},
      {L"\U0000000d\U0000200c", {13, 8204}, {1, 2}},
      {L"\U0000000d\U00000308\U0000200c", {13, 776}, {1, 3}},
+     {L"\U0000000d\U0000200d", {13, 8205}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000200d", {13, 776}, {1, 3}},
      {L"\U0000000d\U0001f1e6", {13, 127462}, {1, 3}},
      {L"\U0000000d\U00000308\U0001f1e6", {13, 776, 127462}, {1, 2, 4}},
-     {L"\U0000000d\U00000600", {13, 1536}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000600", {13, 776, 1536}, {1, 2, 3}},
-     {L"\U0000000d\U00000a03", {13, 2563}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000a03", {13, 776}, {1, 3}},
+     {L"\U0000000d\U000006dd", {13, 1757}, {1, 2}},
+     {L"\U0000000d\U00000308\U000006dd", {13, 776, 1757}, {1, 2, 3}},
+     {L"\U0000000d\U00000903", {13, 2307}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000903", {13, 776}, {1, 3}},
      {L"\U0000000d\U00001100", {13, 4352}, {1, 2}},
      {L"\U0000000d\U00000308\U00001100", {13, 776, 4352}, {1, 2, 3}},
      {L"\U0000000d\U00001160", {13, 4448}, {1, 2}},
@@ -1256,42 +974,36 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U0000000d\U00000308\U0000ac00", {13, 776, 44032}, {1, 2, 3}},
      {L"\U0000000d\U0000ac01", {13, 44033}, {1, 2}},
      {L"\U0000000d\U00000308\U0000ac01", {13, 776, 44033}, {1, 2, 3}},
-     {L"\U0000000d\U00000903", {13, 2307}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000903", {13, 776}, {1, 3}},
-     {L"\U0000000d\U00000904", {13, 2308}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000904", {13, 776, 2308}, {1, 2, 3}},
-     {L"\U0000000d\U00000d4e", {13, 3406}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000d4e", {13, 776, 3406}, {1, 2, 3}},
+     {L"\U0000000d\U00001cf5", {13, 7413}, {1, 2}},
+     {L"\U0000000d\U00000308\U00001cf5", {13, 776, 7413}, {1, 2, 3}},
      {L"\U0000000d\U00000915", {13, 2325}, {1, 2}},
      {L"\U0000000d\U00000308\U00000915", {13, 776, 2325}, {1, 2, 3}},
-     {L"\U0000000d\U0000231a", {13, 8986}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000231a", {13, 776, 8986}, {1, 2, 3}},
-     {L"\U0000000d\U00000300", {13, 768}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000300", {13, 776}, {1, 3}},
-     {L"\U0000000d\U00000900", {13, 2304}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000900", {13, 776}, {1, 3}},
-     {L"\U0000000d\U0000094d", {13, 2381}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000094d", {13, 776}, {1, 3}},
-     {L"\U0000000d\U0000200d", {13, 8205}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000200d", {13, 776}, {1, 3}},
+     {L"\U0000000d\U000000a9", {13, 169}, {1, 2}},
+     {L"\U0000000d\U00000308\U000000a9", {13, 776, 169}, {1, 2, 3}},
+     {L"\U0000000d\U00000020", {13, 32}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000020", {13, 776, 32}, {1, 2, 3}},
      {L"\U0000000d\U00000378", {13, 888}, {1, 2}},
      {L"\U0000000d\U00000308\U00000378", {13, 776, 888}, {1, 2, 3}},
-     {L"\U0000000a\U00000020", {10, 32}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000020", {10, 776, 32}, {1, 2, 3}},
      {L"\U0000000a\U0000000d", {10, 13}, {1, 2}},
      {L"\U0000000a\U00000308\U0000000d", {10, 776, 13}, {1, 2, 3}},
      {L"\U0000000a\U0000000a", {10, 10}, {1, 2}},
      {L"\U0000000a\U00000308\U0000000a", {10, 776, 10}, {1, 2, 3}},
-     {L"\U0000000a\U00000001", {10, 1}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000001", {10, 776, 1}, {1, 2, 3}},
+     {L"\U0000000a\U00000000", {10, 0}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000000", {10, 776, 0}, {1, 2, 3}},
+     {L"\U0000000a\U0000094d", {10, 2381}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000094d", {10, 776}, {1, 3}},
+     {L"\U0000000a\U00000300", {10, 768}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000300", {10, 776}, {1, 3}},
      {L"\U0000000a\U0000200c", {10, 8204}, {1, 2}},
      {L"\U0000000a\U00000308\U0000200c", {10, 776}, {1, 3}},
+     {L"\U0000000a\U0000200d", {10, 8205}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000200d", {10, 776}, {1, 3}},
      {L"\U0000000a\U0001f1e6", {10, 127462}, {1, 3}},
      {L"\U0000000a\U00000308\U0001f1e6", {10, 776, 127462}, {1, 2, 4}},
-     {L"\U0000000a\U00000600", {10, 1536}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000600", {10, 776, 1536}, {1, 2, 3}},
-     {L"\U0000000a\U00000a03", {10, 2563}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000a03", {10, 776}, {1, 3}},
+     {L"\U0000000a\U000006dd", {10, 1757}, {1, 2}},
+     {L"\U0000000a\U00000308\U000006dd", {10, 776, 1757}, {1, 2, 3}},
+     {L"\U0000000a\U00000903", {10, 2307}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000903", {10, 776}, {1, 3}},
      {L"\U0000000a\U00001100", {10, 4352}, {1, 2}},
      {L"\U0000000a\U00000308\U00001100", {10, 776, 4352}, {1, 2, 3}},
      {L"\U0000000a\U00001160", {10, 4448}, {1, 2}},
@@ -1302,88 +1014,156 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U0000000a\U00000308\U0000ac00", {10, 776, 44032}, {1, 2, 3}},
      {L"\U0000000a\U0000ac01", {10, 44033}, {1, 2}},
      {L"\U0000000a\U00000308\U0000ac01", {10, 776, 44033}, {1, 2, 3}},
-     {L"\U0000000a\U00000903", {10, 2307}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000903", {10, 776}, {1, 3}},
-     {L"\U0000000a\U00000904", {10, 2308}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000904", {10, 776, 2308}, {1, 2, 3}},
-     {L"\U0000000a\U00000d4e", {10, 3406}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000d4e", {10, 776, 3406}, {1, 2, 3}},
+     {L"\U0000000a\U00001cf5", {10, 7413}, {1, 2}},
+     {L"\U0000000a\U00000308\U00001cf5", {10, 776, 7413}, {1, 2, 3}},
      {L"\U0000000a\U00000915", {10, 2325}, {1, 2}},
      {L"\U0000000a\U00000308\U00000915", {10, 776, 2325}, {1, 2, 3}},
-     {L"\U0000000a\U0000231a", {10, 8986}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000231a", {10, 776, 8986}, {1, 2, 3}},
-     {L"\U0000000a\U00000300", {10, 768}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000300", {10, 776}, {1, 3}},
-     {L"\U0000000a\U00000900", {10, 2304}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000900", {10, 776}, {1, 3}},
-     {L"\U0000000a\U0000094d", {10, 2381}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000094d", {10, 776}, {1, 3}},
-     {L"\U0000000a\U0000200d", {10, 8205}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000200d", {10, 776}, {1, 3}},
+     {L"\U0000000a\U000000a9", {10, 169}, {1, 2}},
+     {L"\U0000000a\U00000308\U000000a9", {10, 776, 169}, {1, 2, 3}},
+     {L"\U0000000a\U00000020", {10, 32}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000020", {10, 776, 32}, {1, 2, 3}},
      {L"\U0000000a\U00000378", {10, 888}, {1, 2}},
      {L"\U0000000a\U00000308\U00000378", {10, 776, 888}, {1, 2, 3}},
-     {L"\U00000001\U00000020", {1, 32}, {1, 2}},
-     {L"\U00000001\U00000308\U00000020", {1, 776, 32}, {1, 2, 3}},
-     {L"\U00000001\U0000000d", {1, 13}, {1, 2}},
-     {L"\U00000001\U00000308\U0000000d", {1, 776, 13}, {1, 2, 3}},
-     {L"\U00000001\U0000000a", {1, 10}, {1, 2}},
-     {L"\U00000001\U00000308\U0000000a", {1, 776, 10}, {1, 2, 3}},
-     {L"\U00000001\U00000001", {1, 1}, {1, 2}},
-     {L"\U00000001\U00000308\U00000001", {1, 776, 1}, {1, 2, 3}},
-     {L"\U00000001\U0000200c", {1, 8204}, {1, 2}},
-     {L"\U00000001\U00000308\U0000200c", {1, 776}, {1, 3}},
-     {L"\U00000001\U0001f1e6", {1, 127462}, {1, 3}},
-     {L"\U00000001\U00000308\U0001f1e6", {1, 776, 127462}, {1, 2, 4}},
-     {L"\U00000001\U00000600", {1, 1536}, {1, 2}},
-     {L"\U00000001\U00000308\U00000600", {1, 776, 1536}, {1, 2, 3}},
-     {L"\U00000001\U00000a03", {1, 2563}, {1, 2}},
-     {L"\U00000001\U00000308\U00000a03", {1, 776}, {1, 3}},
-     {L"\U00000001\U00001100", {1, 4352}, {1, 2}},
-     {L"\U00000001\U00000308\U00001100", {1, 776, 4352}, {1, 2, 3}},
-     {L"\U00000001\U00001160", {1, 4448}, {1, 2}},
-     {L"\U00000001\U00000308\U00001160", {1, 776, 4448}, {1, 2, 3}},
-     {L"\U00000001\U000011a8", {1, 4520}, {1, 2}},
-     {L"\U00000001\U00000308\U000011a8", {1, 776, 4520}, {1, 2, 3}},
-     {L"\U00000001\U0000ac00", {1, 44032}, {1, 2}},
-     {L"\U00000001\U00000308\U0000ac00", {1, 776, 44032}, {1, 2, 3}},
-     {L"\U00000001\U0000ac01", {1, 44033}, {1, 2}},
-     {L"\U00000001\U00000308\U0000ac01", {1, 776, 44033}, {1, 2, 3}},
-     {L"\U00000001\U00000903", {1, 2307}, {1, 2}},
-     {L"\U00000001\U00000308\U00000903", {1, 776}, {1, 3}},
-     {L"\U00000001\U00000904", {1, 2308}, {1, 2}},
-     {L"\U00000001\U00000308\U00000904", {1, 776, 2308}, {1, 2, 3}},
-     {L"\U00000001\U00000d4e", {1, 3406}, {1, 2}},
-     {L"\U00000001\U00000308\U00000d4e", {1, 776, 3406}, {1, 2, 3}},
-     {L"\U00000001\U00000915", {1, 2325}, {1, 2}},
-     {L"\U00000001\U00000308\U00000915", {1, 776, 2325}, {1, 2, 3}},
-     {L"\U00000001\U0000231a", {1, 8986}, {1, 2}},
-     {L"\U00000001\U00000308\U0000231a", {1, 776, 8986}, {1, 2, 3}},
-     {L"\U00000001\U00000300", {1, 768}, {1, 2}},
-     {L"\U00000001\U00000308\U00000300", {1, 776}, {1, 3}},
-     {L"\U00000001\U00000900", {1, 2304}, {1, 2}},
-     {L"\U00000001\U00000308\U00000900", {1, 776}, {1, 3}},
-     {L"\U00000001\U0000094d", {1, 2381}, {1, 2}},
-     {L"\U00000001\U00000308\U0000094d", {1, 776}, {1, 3}},
-     {L"\U00000001\U0000200d", {1, 8205}, {1, 2}},
-     {L"\U00000001\U00000308\U0000200d", {1, 776}, {1, 3}},
-     {L"\U00000001\U00000378", {1, 888}, {1, 2}},
-     {L"\U00000001\U00000308\U00000378", {1, 776, 888}, {1, 2, 3}},
-     {L"\U0000200c\U00000020", {8204, 32}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000020", {8204, 32}, {2, 3}},
+     {L"\U00000000\U0000000d", {0, 13}, {1, 2}},
+     {L"\U00000000\U00000308\U0000000d", {0, 776, 13}, {1, 2, 3}},
+     {L"\U00000000\U0000000a", {0, 10}, {1, 2}},
+     {L"\U00000000\U00000308\U0000000a", {0, 776, 10}, {1, 2, 3}},
+     {L"\U00000000\U00000000", {0, 0}, {1, 2}},
+     {L"\U00000000\U00000308\U00000000", {0, 776, 0}, {1, 2, 3}},
+     {L"\U00000000\U0000094d", {0, 2381}, {1, 2}},
+     {L"\U00000000\U00000308\U0000094d", {0, 776}, {1, 3}},
+     {L"\U00000000\U00000300", {0, 768}, {1, 2}},
+     {L"\U00000000\U00000308\U00000300", {0, 776}, {1, 3}},
+     {L"\U00000000\U0000200c", {0, 8204}, {1, 2}},
+     {L"\U00000000\U00000308\U0000200c", {0, 776}, {1, 3}},
+     {L"\U00000000\U0000200d", {0, 8205}, {1, 2}},
+     {L"\U00000000\U00000308\U0000200d", {0, 776}, {1, 3}},
+     {L"\U00000000\U0001f1e6", {0, 127462}, {1, 3}},
+     {L"\U00000000\U00000308\U0001f1e6", {0, 776, 127462}, {1, 2, 4}},
+     {L"\U00000000\U000006dd", {0, 1757}, {1, 2}},
+     {L"\U00000000\U00000308\U000006dd", {0, 776, 1757}, {1, 2, 3}},
+     {L"\U00000000\U00000903", {0, 2307}, {1, 2}},
+     {L"\U00000000\U00000308\U00000903", {0, 776}, {1, 3}},
+     {L"\U00000000\U00001100", {0, 4352}, {1, 2}},
+     {L"\U00000000\U00000308\U00001100", {0, 776, 4352}, {1, 2, 3}},
+     {L"\U00000000\U00001160", {0, 4448}, {1, 2}},
+     {L"\U00000000\U00000308\U00001160", {0, 776, 4448}, {1, 2, 3}},
+     {L"\U00000000\U000011a8", {0, 4520}, {1, 2}},
+     {L"\U00000000\U00000308\U000011a8", {0, 776, 4520}, {1, 2, 3}},
+     {L"\U00000000\U0000ac00", {0, 44032}, {1, 2}},
+     {L"\U00000000\U00000308\U0000ac00", {0, 776, 44032}, {1, 2, 3}},
+     {L"\U00000000\U0000ac01", {0, 44033}, {1, 2}},
+     {L"\U00000000\U00000308\U0000ac01", {0, 776, 44033}, {1, 2, 3}},
+     {L"\U00000000\U00001cf5", {0, 7413}, {1, 2}},
+     {L"\U00000000\U00000308\U00001cf5", {0, 776, 7413}, {1, 2, 3}},
+     {L"\U00000000\U00000915", {0, 2325}, {1, 2}},
+     {L"\U00000000\U00000308\U00000915", {0, 776, 2325}, {1, 2, 3}},
+     {L"\U00000000\U000000a9", {0, 169}, {1, 2}},
+     {L"\U00000000\U00000308\U000000a9", {0, 776, 169}, {1, 2, 3}},
+     {L"\U00000000\U00000020", {0, 32}, {1, 2}},
+     {L"\U00000000\U00000308\U00000020", {0, 776, 32}, {1, 2, 3}},
+     {L"\U00000000\U00000378", {0, 888}, {1, 2}},
+     {L"\U00000000\U00000308\U00000378", {0, 776, 888}, {1, 2, 3}},
+     {L"\U0000094d\U0000000d", {2381, 13}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000000d", {2381, 13}, {2, 3}},
+     {L"\U0000094d\U0000000a", {2381, 10}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000000a", {2381, 10}, {2, 3}},
+     {L"\U0000094d\U00000000", {2381, 0}, {1, 2}},
+     {L"\U0000094d\U00000308\U00000000", {2381, 0}, {2, 3}},
+     {L"\U0000094d\U0000094d", {2381}, {2}},
+     {L"\U0000094d\U00000308\U0000094d", {2381}, {3}},
+     {L"\U0000094d\U00000300", {2381}, {2}},
+     {L"\U0000094d\U00000308\U00000300", {2381}, {3}},
+     {L"\U0000094d\U0000200c", {2381}, {2}},
+     {L"\U0000094d\U00000308\U0000200c", {2381}, {3}},
+     {L"\U0000094d\U0000200d", {2381}, {2}},
+     {L"\U0000094d\U00000308\U0000200d", {2381}, {3}},
+     {L"\U0000094d\U0001f1e6", {2381, 127462}, {1, 3}},
+     {L"\U0000094d\U00000308\U0001f1e6", {2381, 127462}, {2, 4}},
+     {L"\U0000094d\U000006dd", {2381, 1757}, {1, 2}},
+     {L"\U0000094d\U00000308\U000006dd", {2381, 1757}, {2, 3}},
+     {L"\U0000094d\U00000903", {2381}, {2}},
+     {L"\U0000094d\U00000308\U00000903", {2381}, {3}},
+     {L"\U0000094d\U00001100", {2381, 4352}, {1, 2}},
+     {L"\U0000094d\U00000308\U00001100", {2381, 4352}, {2, 3}},
+     {L"\U0000094d\U00001160", {2381, 4448}, {1, 2}},
+     {L"\U0000094d\U00000308\U00001160", {2381, 4448}, {2, 3}},
+     {L"\U0000094d\U000011a8", {2381, 4520}, {1, 2}},
+     {L"\U0000094d\U00000308\U000011a8", {2381, 4520}, {2, 3}},
+     {L"\U0000094d\U0000ac00", {2381, 44032}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000ac00", {2381, 44032}, {2, 3}},
+     {L"\U0000094d\U0000ac01", {2381, 44033}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000ac01", {2381, 44033}, {2, 3}},
+     {L"\U0000094d\U00001cf5", {2381, 7413}, {1, 2}},
+     {L"\U0000094d\U00000308\U00001cf5", {2381, 7413}, {2, 3}},
+     {L"\U0000094d\U00000915", {2381}, {2}},
+     {L"\U0000094d\U00000308\U00000915", {2381}, {3}},
+     {L"\U0000094d\U000000a9", {2381, 169}, {1, 2}},
+     {L"\U0000094d\U00000308\U000000a9", {2381, 169}, {2, 3}},
+     {L"\U0000094d\U00000020", {2381, 32}, {1, 2}},
+     {L"\U0000094d\U00000308\U00000020", {2381, 32}, {2, 3}},
+     {L"\U0000094d\U00000378", {2381, 888}, {1, 2}},
+     {L"\U0000094d\U00000308\U00000378", {2381, 888}, {2, 3}},
+     {L"\U00000300\U0000000d", {768, 13}, {1, 2}},
+     {L"\U00000300\U00000308\U0000000d", {768, 13}, {2, 3}},
+     {L"\U00000300\U0000000a", {768, 10}, {1, 2}},
+     {L"\U00000300\U00000308\U0000000a", {768, 10}, {2, 3}},
+     {L"\U00000300\U00000000", {768, 0}, {1, 2}},
+     {L"\U00000300\U00000308\U00000000", {768, 0}, {2, 3}},
+     {L"\U00000300\U0000094d", {768}, {2}},
+     {L"\U00000300\U00000308\U0000094d", {768}, {3}},
+     {L"\U00000300\U00000300", {768}, {2}},
+     {L"\U00000300\U00000308\U00000300", {768}, {3}},
+     {L"\U00000300\U0000200c", {768}, {2}},
+     {L"\U00000300\U00000308\U0000200c", {768}, {3}},
+     {L"\U00000300\U0000200d", {768}, {2}},
+     {L"\U00000300\U00000308\U0000200d", {768}, {3}},
+     {L"\U00000300\U0001f1e6", {768, 127462}, {1, 3}},
+     {L"\U00000300\U00000308\U0001f1e6", {768, 127462}, {2, 4}},
+     {L"\U00000300\U000006dd", {768, 1757}, {1, 2}},
+     {L"\U00000300\U00000308\U000006dd", {768, 1757}, {2, 3}},
+     {L"\U00000300\U00000903", {768}, {2}},
+     {L"\U00000300\U00000308\U00000903", {768}, {3}},
+     {L"\U00000300\U00001100", {768, 4352}, {1, 2}},
+     {L"\U00000300\U00000308\U00001100", {768, 4352}, {2, 3}},
+     {L"\U00000300\U00001160", {768, 4448}, {1, 2}},
+     {L"\U00000300\U00000308\U00001160", {768, 4448}, {2, 3}},
+     {L"\U00000300\U000011a8", {768, 4520}, {1, 2}},
+     {L"\U00000300\U00000308\U000011a8", {768, 4520}, {2, 3}},
+     {L"\U00000300\U0000ac00", {768, 44032}, {1, 2}},
+     {L"\U00000300\U00000308\U0000ac00", {768, 44032}, {2, 3}},
+     {L"\U00000300\U0000ac01", {768, 44033}, {1, 2}},
+     {L"\U00000300\U00000308\U0000ac01", {768, 44033}, {2, 3}},
+     {L"\U00000300\U00001cf5", {768, 7413}, {1, 2}},
+     {L"\U00000300\U00000308\U00001cf5", {768, 7413}, {2, 3}},
+     {L"\U00000300\U00000915", {768, 2325}, {1, 2}},
+     {L"\U00000300\U00000308\U00000915", {768, 2325}, {2, 3}},
+     {L"\U00000300\U000000a9", {768, 169}, {1, 2}},
+     {L"\U00000300\U00000308\U000000a9", {768, 169}, {2, 3}},
+     {L"\U00000300\U00000020", {768, 32}, {1, 2}},
+     {L"\U00000300\U00000308\U00000020", {768, 32}, {2, 3}},
+     {L"\U00000300\U00000378", {768, 888}, {1, 2}},
+     {L"\U00000300\U00000308\U00000378", {768, 888}, {2, 3}},
      {L"\U0000200c\U0000000d", {8204, 13}, {1, 2}},
      {L"\U0000200c\U00000308\U0000000d", {8204, 13}, {2, 3}},
      {L"\U0000200c\U0000000a", {8204, 10}, {1, 2}},
      {L"\U0000200c\U00000308\U0000000a", {8204, 10}, {2, 3}},
-     {L"\U0000200c\U00000001", {8204, 1}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000001", {8204, 1}, {2, 3}},
+     {L"\U0000200c\U00000000", {8204, 0}, {1, 2}},
+     {L"\U0000200c\U00000308\U00000000", {8204, 0}, {2, 3}},
+     {L"\U0000200c\U0000094d", {8204}, {2}},
+     {L"\U0000200c\U00000308\U0000094d", {8204}, {3}},
+     {L"\U0000200c\U00000300", {8204}, {2}},
+     {L"\U0000200c\U00000308\U00000300", {8204}, {3}},
      {L"\U0000200c\U0000200c", {8204}, {2}},
      {L"\U0000200c\U00000308\U0000200c", {8204}, {3}},
+     {L"\U0000200c\U0000200d", {8204}, {2}},
+     {L"\U0000200c\U00000308\U0000200d", {8204}, {3}},
      {L"\U0000200c\U0001f1e6", {8204, 127462}, {1, 3}},
      {L"\U0000200c\U00000308\U0001f1e6", {8204, 127462}, {2, 4}},
-     {L"\U0000200c\U00000600", {8204, 1536}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000600", {8204, 1536}, {2, 3}},
-     {L"\U0000200c\U00000a03", {8204}, {2}},
-     {L"\U0000200c\U00000308\U00000a03", {8204}, {3}},
+     {L"\U0000200c\U000006dd", {8204, 1757}, {1, 2}},
+     {L"\U0000200c\U00000308\U000006dd", {8204, 1757}, {2, 3}},
+     {L"\U0000200c\U00000903", {8204}, {2}},
+     {L"\U0000200c\U00000308\U00000903", {8204}, {3}},
      {L"\U0000200c\U00001100", {8204, 4352}, {1, 2}},
      {L"\U0000200c\U00000308\U00001100", {8204, 4352}, {2, 3}},
      {L"\U0000200c\U00001160", {8204, 4448}, {1, 2}},
@@ -1394,43 +1174,77 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U0000200c\U00000308\U0000ac00", {8204, 44032}, {2, 3}},
      {L"\U0000200c\U0000ac01", {8204, 44033}, {1, 2}},
      {L"\U0000200c\U00000308\U0000ac01", {8204, 44033}, {2, 3}},
-     {L"\U0000200c\U00000903", {8204}, {2}},
-     {L"\U0000200c\U00000308\U00000903", {8204}, {3}},
-     {L"\U0000200c\U00000904", {8204, 2308}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000904", {8204, 2308}, {2, 3}},
-     {L"\U0000200c\U00000d4e", {8204, 3406}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000d4e", {8204, 3406}, {2, 3}},
+     {L"\U0000200c\U00001cf5", {8204, 7413}, {1, 2}},
+     {L"\U0000200c\U00000308\U00001cf5", {8204, 7413}, {2, 3}},
      {L"\U0000200c\U00000915", {8204, 2325}, {1, 2}},
      {L"\U0000200c\U00000308\U00000915", {8204, 2325}, {2, 3}},
-     {L"\U0000200c\U0000231a", {8204, 8986}, {1, 2}},
-     {L"\U0000200c\U00000308\U0000231a", {8204, 8986}, {2, 3}},
-     {L"\U0000200c\U00000300", {8204}, {2}},
-     {L"\U0000200c\U00000308\U00000300", {8204}, {3}},
-     {L"\U0000200c\U00000900", {8204}, {2}},
-     {L"\U0000200c\U00000308\U00000900", {8204}, {3}},
-     {L"\U0000200c\U0000094d", {8204}, {2}},
-     {L"\U0000200c\U00000308\U0000094d", {8204}, {3}},
-     {L"\U0000200c\U0000200d", {8204}, {2}},
-     {L"\U0000200c\U00000308\U0000200d", {8204}, {3}},
+     {L"\U0000200c\U000000a9", {8204, 169}, {1, 2}},
+     {L"\U0000200c\U00000308\U000000a9", {8204, 169}, {2, 3}},
+     {L"\U0000200c\U00000020", {8204, 32}, {1, 2}},
+     {L"\U0000200c\U00000308\U00000020", {8204, 32}, {2, 3}},
      {L"\U0000200c\U00000378", {8204, 888}, {1, 2}},
      {L"\U0000200c\U00000308\U00000378", {8204, 888}, {2, 3}},
-     {L"\U0001f1e6\U00000020", {127462, 32}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U00000020", {127462, 32}, {3, 4}},
+     {L"\U0000200d\U0000000d", {8205, 13}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000000d", {8205, 13}, {2, 3}},
+     {L"\U0000200d\U0000000a", {8205, 10}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000000a", {8205, 10}, {2, 3}},
+     {L"\U0000200d\U00000000", {8205, 0}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000000", {8205, 0}, {2, 3}},
+     {L"\U0000200d\U0000094d", {8205}, {2}},
+     {L"\U0000200d\U00000308\U0000094d", {8205}, {3}},
+     {L"\U0000200d\U00000300", {8205}, {2}},
+     {L"\U0000200d\U00000308\U00000300", {8205}, {3}},
+     {L"\U0000200d\U0000200c", {8205}, {2}},
+     {L"\U0000200d\U00000308\U0000200c", {8205}, {3}},
+     {L"\U0000200d\U0000200d", {8205}, {2}},
+     {L"\U0000200d\U00000308\U0000200d", {8205}, {3}},
+     {L"\U0000200d\U0001f1e6", {8205, 127462}, {1, 3}},
+     {L"\U0000200d\U00000308\U0001f1e6", {8205, 127462}, {2, 4}},
+     {L"\U0000200d\U000006dd", {8205, 1757}, {1, 2}},
+     {L"\U0000200d\U00000308\U000006dd", {8205, 1757}, {2, 3}},
+     {L"\U0000200d\U00000903", {8205}, {2}},
+     {L"\U0000200d\U00000308\U00000903", {8205}, {3}},
+     {L"\U0000200d\U00001100", {8205, 4352}, {1, 2}},
+     {L"\U0000200d\U00000308\U00001100", {8205, 4352}, {2, 3}},
+     {L"\U0000200d\U00001160", {8205, 4448}, {1, 2}},
+     {L"\U0000200d\U00000308\U00001160", {8205, 4448}, {2, 3}},
+     {L"\U0000200d\U000011a8", {8205, 4520}, {1, 2}},
+     {L"\U0000200d\U00000308\U000011a8", {8205, 4520}, {2, 3}},
+     {L"\U0000200d\U0000ac00", {8205, 44032}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000ac00", {8205, 44032}, {2, 3}},
+     {L"\U0000200d\U0000ac01", {8205, 44033}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000ac01", {8205, 44033}, {2, 3}},
+     {L"\U0000200d\U00001cf5", {8205, 7413}, {1, 2}},
+     {L"\U0000200d\U00000308\U00001cf5", {8205, 7413}, {2, 3}},
+     {L"\U0000200d\U00000915", {8205, 2325}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000915", {8205, 2325}, {2, 3}},
+     {L"\U0000200d\U000000a9", {8205, 169}, {1, 2}},
+     {L"\U0000200d\U00000308\U000000a9", {8205, 169}, {2, 3}},
+     {L"\U0000200d\U00000020", {8205, 32}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000020", {8205, 32}, {2, 3}},
+     {L"\U0000200d\U00000378", {8205, 888}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000378", {8205, 888}, {2, 3}},
      {L"\U0001f1e6\U0000000d", {127462, 13}, {2, 3}},
      {L"\U0001f1e6\U00000308\U0000000d", {127462, 13}, {3, 4}},
      {L"\U0001f1e6\U0000000a", {127462, 10}, {2, 3}},
      {L"\U0001f1e6\U00000308\U0000000a", {127462, 10}, {3, 4}},
-     {L"\U0001f1e6\U00000001", {127462, 1}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U00000001", {127462, 1}, {3, 4}},
+     {L"\U0001f1e6\U00000000", {127462, 0}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U00000000", {127462, 0}, {3, 4}},
+     {L"\U0001f1e6\U0000094d", {127462}, {3}},
+     {L"\U0001f1e6\U00000308\U0000094d", {127462}, {4}},
+     {L"\U0001f1e6\U00000300", {127462}, {3}},
+     {L"\U0001f1e6\U00000308\U00000300", {127462}, {4}},
      {L"\U0001f1e6\U0000200c", {127462}, {3}},
      {L"\U0001f1e6\U00000308\U0000200c", {127462}, {4}},
+     {L"\U0001f1e6\U0000200d", {127462}, {3}},
+     {L"\U0001f1e6\U00000308\U0000200d", {127462}, {4}},
      {L"\U0001f1e6\U0001f1e6", {127462}, {4}},
      {L"\U0001f1e6\U00000308\U0001f1e6", {127462, 127462}, {3, 5}},
-     {L"\U0001f1e6\U00000600", {127462, 1536}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U00000600", {127462, 1536}, {3, 4}},
-     {L"\U0001f1e6\U00000a03", {127462}, {3}},
-     {L"\U0001f1e6\U00000308\U00000a03", {127462}, {4}},
-     {L"\U0001f1e6\U00001100", {127462, 4352}, {2, 3}},
+     {L"\U0001f1e6\U000006dd", {127462, 1757}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U000006dd", {127462, 1757}, {3, 4}},
+     {L"\U0001f1e6\U00000903", {127462}, {3}},
+     {L"\U0001f1e6\U00000308\U00000903", {127462}, {4}},
+     {L"\U0001f1e6\U00001100", {127462, 4352}, {2, 3}},
      {L"\U0001f1e6\U00000308\U00001100", {127462, 4352}, {3, 4}},
      {L"\U0001f1e6\U00001160", {127462, 4448}, {2, 3}},
      {L"\U0001f1e6\U00000308\U00001160", {127462, 4448}, {3, 4}},
@@ -1440,134 +1254,116 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U0001f1e6\U00000308\U0000ac00", {127462, 44032}, {3, 4}},
      {L"\U0001f1e6\U0000ac01", {127462, 44033}, {2, 3}},
      {L"\U0001f1e6\U00000308\U0000ac01", {127462, 44033}, {3, 4}},
-     {L"\U0001f1e6\U00000903", {127462}, {3}},
-     {L"\U0001f1e6\U00000308\U00000903", {127462}, {4}},
-     {L"\U0001f1e6\U00000904", {127462, 2308}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U00000904", {127462, 2308}, {3, 4}},
-     {L"\U0001f1e6\U00000d4e", {127462, 3406}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U00000d4e", {127462, 3406}, {3, 4}},
+     {L"\U0001f1e6\U00001cf5", {127462, 7413}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U00001cf5", {127462, 7413}, {3, 4}},
      {L"\U0001f1e6\U00000915", {127462, 2325}, {2, 3}},
      {L"\U0001f1e6\U00000308\U00000915", {127462, 2325}, {3, 4}},
-     {L"\U0001f1e6\U0000231a", {127462, 8986}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U0000231a", {127462, 8986}, {3, 4}},
-     {L"\U0001f1e6\U00000300", {127462}, {3}},
-     {L"\U0001f1e6\U00000308\U00000300", {127462}, {4}},
-     {L"\U0001f1e6\U00000900", {127462}, {3}},
-     {L"\U0001f1e6\U00000308\U00000900", {127462}, {4}},
-     {L"\U0001f1e6\U0000094d", {127462}, {3}},
-     {L"\U0001f1e6\U00000308\U0000094d", {127462}, {4}},
-     {L"\U0001f1e6\U0000200d", {127462}, {3}},
-     {L"\U0001f1e6\U00000308\U0000200d", {127462}, {4}},
+     {L"\U0001f1e6\U000000a9", {127462, 169}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U000000a9", {127462, 169}, {3, 4}},
+     {L"\U0001f1e6\U00000020", {127462, 32}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U00000020", {127462, 32}, {3, 4}},
      {L"\U0001f1e6\U00000378", {127462, 888}, {2, 3}},
      {L"\U0001f1e6\U00000308\U00000378", {127462, 888}, {3, 4}},
-     {L"\U00000600\U00000020", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000020", {1536, 32}, {2, 3}},
-     {L"\U00000600\U0000000d", {1536, 13}, {1, 2}},
-     {L"\U00000600\U00000308\U0000000d", {1536, 13}, {2, 3}},
-     {L"\U00000600\U0000000a", {1536, 10}, {1, 2}},
-     {L"\U00000600\U00000308\U0000000a", {1536, 10}, {2, 3}},
-     {L"\U00000600\U00000001", {1536, 1}, {1, 2}},
-     {L"\U00000600\U00000308\U00000001", {1536, 1}, {2, 3}},
-     {L"\U00000600\U0000200c", {1536}, {2}},
-     {L"\U00000600\U00000308\U0000200c", {1536}, {3}},
-     {L"\U00000600\U0001f1e6", {1536}, {3}},
-     {L"\U00000600\U00000308\U0001f1e6", {1536, 127462}, {2, 4}},
-     {L"\U00000600\U00000600", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000600", {1536, 1536}, {2, 3}},
-     {L"\U00000600\U00000a03", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000a03", {1536}, {3}},
-     {L"\U00000600\U00001100", {1536}, {2}},
-     {L"\U00000600\U00000308\U00001100", {1536, 4352}, {2, 3}},
-     {L"\U00000600\U00001160", {1536}, {2}},
-     {L"\U00000600\U00000308\U00001160", {1536, 4448}, {2, 3}},
-     {L"\U00000600\U000011a8", {1536}, {2}},
-     {L"\U00000600\U00000308\U000011a8", {1536, 4520}, {2, 3}},
-     {L"\U00000600\U0000ac00", {1536}, {2}},
-     {L"\U00000600\U00000308\U0000ac00", {1536, 44032}, {2, 3}},
-     {L"\U00000600\U0000ac01", {1536}, {2}},
-     {L"\U00000600\U00000308\U0000ac01", {1536, 44033}, {2, 3}},
-     {L"\U00000600\U00000903", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000903", {1536}, {3}},
-     {L"\U00000600\U00000904", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000904", {1536, 2308}, {2, 3}},
-     {L"\U00000600\U00000d4e", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000d4e", {1536, 3406}, {2, 3}},
-     {L"\U00000600\U00000915", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000915", {1536, 2325}, {2, 3}},
-     {L"\U00000600\U0000231a", {1536}, {2}},
-     {L"\U00000600\U00000308\U0000231a", {1536, 8986}, {2, 3}},
-     {L"\U00000600\U00000300", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000300", {1536}, {3}},
-     {L"\U00000600\U00000900", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000900", {1536}, {3}},
-     {L"\U00000600\U0000094d", {1536}, {2}},
-     {L"\U00000600\U00000308\U0000094d", {1536}, {3}},
-     {L"\U00000600\U0000200d", {1536}, {2}},
-     {L"\U00000600\U00000308\U0000200d", {1536}, {3}},
-     {L"\U00000600\U00000378", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000378", {1536, 888}, {2, 3}},
-     {L"\U00000a03\U00000020", {2563, 32}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000020", {2563, 32}, {2, 3}},
-     {L"\U00000a03\U0000000d", {2563, 13}, {1, 2}},
-     {L"\U00000a03\U00000308\U0000000d", {2563, 13}, {2, 3}},
-     {L"\U00000a03\U0000000a", {2563, 10}, {1, 2}},
-     {L"\U00000a03\U00000308\U0000000a", {2563, 10}, {2, 3}},
-     {L"\U00000a03\U00000001", {2563, 1}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000001", {2563, 1}, {2, 3}},
-     {L"\U00000a03\U0000200c", {2563}, {2}},
-     {L"\U00000a03\U00000308\U0000200c", {2563}, {3}},
-     {L"\U00000a03\U0001f1e6", {2563, 127462}, {1, 3}},
-     {L"\U00000a03\U00000308\U0001f1e6", {2563, 127462}, {2, 4}},
-     {L"\U00000a03\U00000600", {2563, 1536}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000600", {2563, 1536}, {2, 3}},
-     {L"\U00000a03\U00000a03", {2563}, {2}},
-     {L"\U00000a03\U00000308\U00000a03", {2563}, {3}},
-     {L"\U00000a03\U00001100", {2563, 4352}, {1, 2}},
-     {L"\U00000a03\U00000308\U00001100", {2563, 4352}, {2, 3}},
-     {L"\U00000a03\U00001160", {2563, 4448}, {1, 2}},
-     {L"\U00000a03\U00000308\U00001160", {2563, 4448}, {2, 3}},
-     {L"\U00000a03\U000011a8", {2563, 4520}, {1, 2}},
-     {L"\U00000a03\U00000308\U000011a8", {2563, 4520}, {2, 3}},
-     {L"\U00000a03\U0000ac00", {2563, 44032}, {1, 2}},
-     {L"\U00000a03\U00000308\U0000ac00", {2563, 44032}, {2, 3}},
-     {L"\U00000a03\U0000ac01", {2563, 44033}, {1, 2}},
-     {L"\U00000a03\U00000308\U0000ac01", {2563, 44033}, {2, 3}},
-     {L"\U00000a03\U00000903", {2563}, {2}},
-     {L"\U00000a03\U00000308\U00000903", {2563}, {3}},
-     {L"\U00000a03\U00000904", {2563, 2308}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000904", {2563, 2308}, {2, 3}},
-     {L"\U00000a03\U00000d4e", {2563, 3406}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000d4e", {2563, 3406}, {2, 3}},
-     {L"\U00000a03\U00000915", {2563, 2325}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000915", {2563, 2325}, {2, 3}},
-     {L"\U00000a03\U0000231a", {2563, 8986}, {1, 2}},
-     {L"\U00000a03\U00000308\U0000231a", {2563, 8986}, {2, 3}},
-     {L"\U00000a03\U00000300", {2563}, {2}},
-     {L"\U00000a03\U00000308\U00000300", {2563}, {3}},
-     {L"\U00000a03\U00000900", {2563}, {2}},
-     {L"\U00000a03\U00000308\U00000900", {2563}, {3}},
-     {L"\U00000a03\U0000094d", {2563}, {2}},
-     {L"\U00000a03\U00000308\U0000094d", {2563}, {3}},
-     {L"\U00000a03\U0000200d", {2563}, {2}},
-     {L"\U00000a03\U00000308\U0000200d", {2563}, {3}},
-     {L"\U00000a03\U00000378", {2563, 888}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000378", {2563, 888}, {2, 3}},
-     {L"\U00001100\U00000020", {4352, 32}, {1, 2}},
-     {L"\U00001100\U00000308\U00000020", {4352, 32}, {2, 3}},
+     {L"\U000006dd\U0000000d", {1757, 13}, {1, 2}},
+     {L"\U000006dd\U00000308\U0000000d", {1757, 13}, {2, 3}},
+     {L"\U000006dd\U0000000a", {1757, 10}, {1, 2}},
+     {L"\U000006dd\U00000308\U0000000a", {1757, 10}, {2, 3}},
+     {L"\U000006dd\U00000000", {1757, 0}, {1, 2}},
+     {L"\U000006dd\U00000308\U00000000", {1757, 0}, {2, 3}},
+     {L"\U000006dd\U0000094d", {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000094d", {1757}, {3}},
+     {L"\U000006dd\U00000300", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000300", {1757}, {3}},
+     {L"\U000006dd\U0000200c", {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000200c", {1757}, {3}},
+     {L"\U000006dd\U0000200d", {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000200d", {1757}, {3}},
+     {L"\U000006dd\U0001f1e6", {1757}, {3}},
+     {L"\U000006dd\U00000308\U0001f1e6", {1757, 127462}, {2, 4}},
+     {L"\U000006dd\U000006dd", {1757}, {2}},
+     {L"\U000006dd\U00000308\U000006dd", {1757, 1757}, {2, 3}},
+     {L"\U000006dd\U00000903", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000903", {1757}, {3}},
+     {L"\U000006dd\U00001100", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00001100", {1757, 4352}, {2, 3}},
+     {L"\U000006dd\U00001160", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00001160", {1757, 4448}, {2, 3}},
+     {L"\U000006dd\U000011a8", {1757}, {2}},
+     {L"\U000006dd\U00000308\U000011a8", {1757, 4520}, {2, 3}},
+     {L"\U000006dd\U0000ac00", {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000ac00", {1757, 44032}, {2, 3}},
+     {L"\U000006dd\U0000ac01", {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000ac01", {1757, 44033}, {2, 3}},
+     {L"\U000006dd\U00001cf5", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00001cf5", {1757, 7413}, {2, 3}},
+     {L"\U000006dd\U00000915", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000915", {1757, 2325}, {2, 3}},
+     {L"\U000006dd\U000000a9", {1757}, {2}},
+     {L"\U000006dd\U00000308\U000000a9", {1757, 169}, {2, 3}},
+     {L"\U000006dd\U00000020", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000020", {1757, 32}, {2, 3}},
+     {L"\U000006dd\U00000378", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000378", {1757, 888}, {2, 3}},
+     {L"\U00000903\U0000000d", {2307, 13}, {1, 2}},
+     {L"\U00000903\U00000308\U0000000d", {2307, 13}, {2, 3}},
+     {L"\U00000903\U0000000a", {2307, 10}, {1, 2}},
+     {L"\U00000903\U00000308\U0000000a", {2307, 10}, {2, 3}},
+     {L"\U00000903\U00000000", {2307, 0}, {1, 2}},
+     {L"\U00000903\U00000308\U00000000", {2307, 0}, {2, 3}},
+     {L"\U00000903\U0000094d", {2307}, {2}},
+     {L"\U00000903\U00000308\U0000094d", {2307}, {3}},
+     {L"\U00000903\U00000300", {2307}, {2}},
+     {L"\U00000903\U00000308\U00000300", {2307}, {3}},
+     {L"\U00000903\U0000200c", {2307}, {2}},
+     {L"\U00000903\U00000308\U0000200c", {2307}, {3}},
+     {L"\U00000903\U0000200d", {2307}, {2}},
+     {L"\U00000903\U00000308\U0000200d", {2307}, {3}},
+     {L"\U00000903\U0001f1e6", {2307, 127462}, {1, 3}},
+     {L"\U00000903\U00000308\U0001f1e6", {2307, 127462}, {2, 4}},
+     {L"\U00000903\U000006dd", {2307, 1757}, {1, 2}},
+     {L"\U00000903\U00000308\U000006dd", {2307, 1757}, {2, 3}},
+     {L"\U00000903\U00000903", {2307}, {2}},
+     {L"\U00000903\U00000308\U00000903", {2307}, {3}},
+     {L"\U00000903\U00001100", {2307, 4352}, {1, 2}},
+     {L"\U00000903\U00000308\U00001100", {2307, 4352}, {2, 3}},
+     {L"\U00000903\U00001160", {2307, 4448}, {1, 2}},
+     {L"\U00000903\U00000308\U00001160", {2307, 4448}, {2, 3}},
+     {L"\U00000903\U000011a8", {2307, 4520}, {1, 2}},
+     {L"\U00000903\U00000308\U000011a8", {2307, 4520}, {2, 3}},
+     {L"\U00000903\U0000ac00", {2307, 44032}, {1, 2}},
+     {L"\U00000903\U00000308\U0000ac00", {2307, 44032}, {2, 3}},
+     {L"\U00000903\U0000ac01", {2307, 44033}, {1, 2}},
+     {L"\U00000903\U00000308\U0000ac01", {2307, 44033}, {2, 3}},
+     {L"\U00000903\U00001cf5", {2307, 7413}, {1, 2}},
+     {L"\U00000903\U00000308\U00001cf5", {2307, 7413}, {2, 3}},
+     {L"\U00000903\U00000915", {2307, 2325}, {1, 2}},
+     {L"\U00000903\U00000308\U00000915", {2307, 2325}, {2, 3}},
+     {L"\U00000903\U000000a9", {2307, 169}, {1, 2}},
+     {L"\U00000903\U00000308\U000000a9", {2307, 169}, {2, 3}},
+     {L"\U00000903\U00000020", {2307, 32}, {1, 2}},
+     {L"\U00000903\U00000308\U00000020", {2307, 32}, {2, 3}},
+     {L"\U00000903\U00000378", {2307, 888}, {1, 2}},
+     {L"\U00000903\U00000308\U00000378", {2307, 888}, {2, 3}},
      {L"\U00001100\U0000000d", {4352, 13}, {1, 2}},
      {L"\U00001100\U00000308\U0000000d", {4352, 13}, {2, 3}},
      {L"\U00001100\U0000000a", {4352, 10}, {1, 2}},
      {L"\U00001100\U00000308\U0000000a", {4352, 10}, {2, 3}},
-     {L"\U00001100\U00000001", {4352, 1}, {1, 2}},
-     {L"\U00001100\U00000308\U00000001", {4352, 1}, {2, 3}},
+     {L"\U00001100\U00000000", {4352, 0}, {1, 2}},
+     {L"\U00001100\U00000308\U00000000", {4352, 0}, {2, 3}},
+     {L"\U00001100\U0000094d", {4352}, {2}},
+     {L"\U00001100\U00000308\U0000094d", {4352}, {3}},
+     {L"\U00001100\U00000300", {4352}, {2}},
+     {L"\U00001100\U00000308\U00000300", {4352}, {3}},
      {L"\U00001100\U0000200c", {4352}, {2}},
      {L"\U00001100\U00000308\U0000200c", {4352}, {3}},
+     {L"\U00001100\U0000200d", {4352}, {2}},
+     {L"\U00001100\U00000308\U0000200d", {4352}, {3}},
      {L"\U00001100\U0001f1e6", {4352, 127462}, {1, 3}},
      {L"\U00001100\U00000308\U0001f1e6", {4352, 127462}, {2, 4}},
-     {L"\U00001100\U00000600", {4352, 1536}, {1, 2}},
-     {L"\U00001100\U00000308\U00000600", {4352, 1536}, {2, 3}},
-     {L"\U00001100\U00000a03", {4352}, {2}},
-     {L"\U00001100\U00000308\U00000a03", {4352}, {3}},
+     {L"\U00001100\U000006dd", {4352, 1757}, {1, 2}},
+     {L"\U00001100\U00000308\U000006dd", {4352, 1757}, {2, 3}},
+     {L"\U00001100\U00000903", {4352}, {2}},
+     {L"\U00001100\U00000308\U00000903", {4352}, {3}},
      {L"\U00001100\U00001100", {4352}, {2}},
      {L"\U00001100\U00000308\U00001100", {4352, 4352}, {2, 3}},
      {L"\U00001100\U00001160", {4352}, {2}},
@@ -1578,42 +1374,36 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U00001100\U00000308\U0000ac00", {4352, 44032}, {2, 3}},
      {L"\U00001100\U0000ac01", {4352}, {2}},
      {L"\U00001100\U00000308\U0000ac01", {4352, 44033}, {2, 3}},
-     {L"\U00001100\U00000903", {4352}, {2}},
-     {L"\U00001100\U00000308\U00000903", {4352}, {3}},
-     {L"\U00001100\U00000904", {4352, 2308}, {1, 2}},
-     {L"\U00001100\U00000308\U00000904", {4352, 2308}, {2, 3}},
-     {L"\U00001100\U00000d4e", {4352, 3406}, {1, 2}},
-     {L"\U00001100\U00000308\U00000d4e", {4352, 3406}, {2, 3}},
+     {L"\U00001100\U00001cf5", {4352, 7413}, {1, 2}},
+     {L"\U00001100\U00000308\U00001cf5", {4352, 7413}, {2, 3}},
      {L"\U00001100\U00000915", {4352, 2325}, {1, 2}},
      {L"\U00001100\U00000308\U00000915", {4352, 2325}, {2, 3}},
-     {L"\U00001100\U0000231a", {4352, 8986}, {1, 2}},
-     {L"\U00001100\U00000308\U0000231a", {4352, 8986}, {2, 3}},
-     {L"\U00001100\U00000300", {4352}, {2}},
-     {L"\U00001100\U00000308\U00000300", {4352}, {3}},
-     {L"\U00001100\U00000900", {4352}, {2}},
-     {L"\U00001100\U00000308\U00000900", {4352}, {3}},
-     {L"\U00001100\U0000094d", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000094d", {4352}, {3}},
-     {L"\U00001100\U0000200d", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000200d", {4352}, {3}},
+     {L"\U00001100\U000000a9", {4352, 169}, {1, 2}},
+     {L"\U00001100\U00000308\U000000a9", {4352, 169}, {2, 3}},
+     {L"\U00001100\U00000020", {4352, 32}, {1, 2}},
+     {L"\U00001100\U00000308\U00000020", {4352, 32}, {2, 3}},
      {L"\U00001100\U00000378", {4352, 888}, {1, 2}},
      {L"\U00001100\U00000308\U00000378", {4352, 888}, {2, 3}},
-     {L"\U00001160\U00000020", {4448, 32}, {1, 2}},
-     {L"\U00001160\U00000308\U00000020", {4448, 32}, {2, 3}},
      {L"\U00001160\U0000000d", {4448, 13}, {1, 2}},
      {L"\U00001160\U00000308\U0000000d", {4448, 13}, {2, 3}},
      {L"\U00001160\U0000000a", {4448, 10}, {1, 2}},
      {L"\U00001160\U00000308\U0000000a", {4448, 10}, {2, 3}},
-     {L"\U00001160\U00000001", {4448, 1}, {1, 2}},
-     {L"\U00001160\U00000308\U00000001", {4448, 1}, {2, 3}},
+     {L"\U00001160\U00000000", {4448, 0}, {1, 2}},
+     {L"\U00001160\U00000308\U00000000", {4448, 0}, {2, 3}},
+     {L"\U00001160\U0000094d", {4448}, {2}},
+     {L"\U00001160\U00000308\U0000094d", {4448}, {3}},
+     {L"\U00001160\U00000300", {4448}, {2}},
+     {L"\U00001160\U00000308\U00000300", {4448}, {3}},
      {L"\U00001160\U0000200c", {4448}, {2}},
      {L"\U00001160\U00000308\U0000200c", {4448}, {3}},
+     {L"\U00001160\U0000200d", {4448}, {2}},
+     {L"\U00001160\U00000308\U0000200d", {4448}, {3}},
      {L"\U00001160\U0001f1e6", {4448, 127462}, {1, 3}},
      {L"\U00001160\U00000308\U0001f1e6", {4448, 127462}, {2, 4}},
-     {L"\U00001160\U00000600", {4448, 1536}, {1, 2}},
-     {L"\U00001160\U00000308\U00000600", {4448, 1536}, {2, 3}},
-     {L"\U00001160\U00000a03", {4448}, {2}},
-     {L"\U00001160\U00000308\U00000a03", {4448}, {3}},
+     {L"\U00001160\U000006dd", {4448, 1757}, {1, 2}},
+     {L"\U00001160\U00000308\U000006dd", {4448, 1757}, {2, 3}},
+     {L"\U00001160\U00000903", {4448}, {2}},
+     {L"\U00001160\U00000308\U00000903", {4448}, {3}},
      {L"\U00001160\U00001100", {4448, 4352}, {1, 2}},
      {L"\U00001160\U00000308\U00001100", {4448, 4352}, {2, 3}},
      {L"\U00001160\U00001160", {4448}, {2}},
@@ -1624,42 +1414,36 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U00001160\U00000308\U0000ac00", {4448, 44032}, {2, 3}},
      {L"\U00001160\U0000ac01", {4448, 44033}, {1, 2}},
      {L"\U00001160\U00000308\U0000ac01", {4448, 44033}, {2, 3}},
-     {L"\U00001160\U00000903", {4448}, {2}},
-     {L"\U00001160\U00000308\U00000903", {4448}, {3}},
-     {L"\U00001160\U00000904", {4448, 2308}, {1, 2}},
-     {L"\U00001160\U00000308\U00000904", {4448, 2308}, {2, 3}},
-     {L"\U00001160\U00000d4e", {4448, 3406}, {1, 2}},
-     {L"\U00001160\U00000308\U00000d4e", {4448, 3406}, {2, 3}},
+     {L"\U00001160\U00001cf5", {4448, 7413}, {1, 2}},
+     {L"\U00001160\U00000308\U00001cf5", {4448, 7413}, {2, 3}},
      {L"\U00001160\U00000915", {4448, 2325}, {1, 2}},
      {L"\U00001160\U00000308\U00000915", {4448, 2325}, {2, 3}},
-     {L"\U00001160\U0000231a", {4448, 8986}, {1, 2}},
-     {L"\U00001160\U00000308\U0000231a", {4448, 8986}, {2, 3}},
-     {L"\U00001160\U00000300", {4448}, {2}},
-     {L"\U00001160\U00000308\U00000300", {4448}, {3}},
-     {L"\U00001160\U00000900", {4448}, {2}},
-     {L"\U00001160\U00000308\U00000900", {4448}, {3}},
-     {L"\U00001160\U0000094d", {4448}, {2}},
-     {L"\U00001160\U00000308\U0000094d", {4448}, {3}},
-     {L"\U00001160\U0000200d", {4448}, {2}},
-     {L"\U00001160\U00000308\U0000200d", {4448}, {3}},
+     {L"\U00001160\U000000a9", {4448, 169}, {1, 2}},
+     {L"\U00001160\U00000308\U000000a9", {4448, 169}, {2, 3}},
+     {L"\U00001160\U00000020", {4448, 32}, {1, 2}},
+     {L"\U00001160\U00000308\U00000020", {4448, 32}, {2, 3}},
      {L"\U00001160\U00000378", {4448, 888}, {1, 2}},
      {L"\U00001160\U00000308\U00000378", {4448, 888}, {2, 3}},
-     {L"\U000011a8\U00000020", {4520, 32}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000020", {4520, 32}, {2, 3}},
      {L"\U000011a8\U0000000d", {4520, 13}, {1, 2}},
      {L"\U000011a8\U00000308\U0000000d", {4520, 13}, {2, 3}},
      {L"\U000011a8\U0000000a", {4520, 10}, {1, 2}},
      {L"\U000011a8\U00000308\U0000000a", {4520, 10}, {2, 3}},
-     {L"\U000011a8\U00000001", {4520, 1}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000001", {4520, 1}, {2, 3}},
+     {L"\U000011a8\U00000000", {4520, 0}, {1, 2}},
+     {L"\U000011a8\U00000308\U00000000", {4520, 0}, {2, 3}},
+     {L"\U000011a8\U0000094d", {4520}, {2}},
+     {L"\U000011a8\U00000308\U0000094d", {4520}, {3}},
+     {L"\U000011a8\U00000300", {4520}, {2}},
+     {L"\U000011a8\U00000308\U00000300", {4520}, {3}},
      {L"\U000011a8\U0000200c", {4520}, {2}},
      {L"\U000011a8\U00000308\U0000200c", {4520}, {3}},
+     {L"\U000011a8\U0000200d", {4520}, {2}},
+     {L"\U000011a8\U00000308\U0000200d", {4520}, {3}},
      {L"\U000011a8\U0001f1e6", {4520, 127462}, {1, 3}},
      {L"\U000011a8\U00000308\U0001f1e6", {4520, 127462}, {2, 4}},
-     {L"\U000011a8\U00000600", {4520, 1536}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000600", {4520, 1536}, {2, 3}},
-     {L"\U000011a8\U00000a03", {4520}, {2}},
-     {L"\U000011a8\U00000308\U00000a03", {4520}, {3}},
+     {L"\U000011a8\U000006dd", {4520, 1757}, {1, 2}},
+     {L"\U000011a8\U00000308\U000006dd", {4520, 1757}, {2, 3}},
+     {L"\U000011a8\U00000903", {4520}, {2}},
+     {L"\U000011a8\U00000308\U00000903", {4520}, {3}},
      {L"\U000011a8\U00001100", {4520, 4352}, {1, 2}},
      {L"\U000011a8\U00000308\U00001100", {4520, 4352}, {2, 3}},
      {L"\U000011a8\U00001160", {4520, 4448}, {1, 2}},
@@ -1670,42 +1454,36 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U000011a8\U00000308\U0000ac00", {4520, 44032}, {2, 3}},
      {L"\U000011a8\U0000ac01", {4520, 44033}, {1, 2}},
      {L"\U000011a8\U00000308\U0000ac01", {4520, 44033}, {2, 3}},
-     {L"\U000011a8\U00000903", {4520}, {2}},
-     {L"\U000011a8\U00000308\U00000903", {4520}, {3}},
-     {L"\U000011a8\U00000904", {4520, 2308}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000904", {4520, 2308}, {2, 3}},
-     {L"\U000011a8\U00000d4e", {4520, 3406}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000d4e", {4520, 3406}, {2, 3}},
+     {L"\U000011a8\U00001cf5", {4520, 7413}, {1, 2}},
+     {L"\U000011a8\U00000308\U00001cf5", {4520, 7413}, {2, 3}},
      {L"\U000011a8\U00000915", {4520, 2325}, {1, 2}},
      {L"\U000011a8\U00000308\U00000915", {4520, 2325}, {2, 3}},
-     {L"\U000011a8\U0000231a", {4520, 8986}, {1, 2}},
-     {L"\U000011a8\U00000308\U0000231a", {4520, 8986}, {2, 3}},
-     {L"\U000011a8\U00000300", {4520}, {2}},
-     {L"\U000011a8\U00000308\U00000300", {4520}, {3}},
-     {L"\U000011a8\U00000900", {4520}, {2}},
-     {L"\U000011a8\U00000308\U00000900", {4520}, {3}},
-     {L"\U000011a8\U0000094d", {4520}, {2}},
-     {L"\U000011a8\U00000308\U0000094d", {4520}, {3}},
-     {L"\U000011a8\U0000200d", {4520}, {2}},
-     {L"\U000011a8\U00000308\U0000200d", {4520}, {3}},
+     {L"\U000011a8\U000000a9", {4520, 169}, {1, 2}},
+     {L"\U000011a8\U00000308\U000000a9", {4520, 169}, {2, 3}},
+     {L"\U000011a8\U00000020", {4520, 32}, {1, 2}},
+     {L"\U000011a8\U00000308\U00000020", {4520, 32}, {2, 3}},
      {L"\U000011a8\U00000378", {4520, 888}, {1, 2}},
      {L"\U000011a8\U00000308\U00000378", {4520, 888}, {2, 3}},
-     {L"\U0000ac00\U00000020", {44032, 32}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000020", {44032, 32}, {2, 3}},
      {L"\U0000ac00\U0000000d", {44032, 13}, {1, 2}},
      {L"\U0000ac00\U00000308\U0000000d", {44032, 13}, {2, 3}},
      {L"\U0000ac00\U0000000a", {44032, 10}, {1, 2}},
      {L"\U0000ac00\U00000308\U0000000a", {44032, 10}, {2, 3}},
-     {L"\U0000ac00\U00000001", {44032, 1}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000001", {44032, 1}, {2, 3}},
+     {L"\U0000ac00\U00000000", {44032, 0}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00000000", {44032, 0}, {2, 3}},
+     {L"\U0000ac00\U0000094d", {44032}, {2}},
+     {L"\U0000ac00\U00000308\U0000094d", {44032}, {3}},
+     {L"\U0000ac00\U00000300", {44032}, {2}},
+     {L"\U0000ac00\U00000308\U00000300", {44032}, {3}},
      {L"\U0000ac00\U0000200c", {44032}, {2}},
      {L"\U0000ac00\U00000308\U0000200c", {44032}, {3}},
+     {L"\U0000ac00\U0000200d", {44032}, {2}},
+     {L"\U0000ac00\U00000308\U0000200d", {44032}, {3}},
      {L"\U0000ac00\U0001f1e6", {44032, 127462}, {1, 3}},
      {L"\U0000ac00\U00000308\U0001f1e6", {44032, 127462}, {2, 4}},
-     {L"\U0000ac00\U00000600", {44032, 1536}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000600", {44032, 1536}, {2, 3}},
-     {L"\U0000ac00\U00000a03", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00000a03", {44032}, {3}},
+     {L"\U0000ac00\U000006dd", {44032, 1757}, {1, 2}},
+     {L"\U0000ac00\U00000308\U000006dd", {44032, 1757}, {2, 3}},
+     {L"\U0000ac00\U00000903", {44032}, {2}},
+     {L"\U0000ac00\U00000308\U00000903", {44032}, {3}},
      {L"\U0000ac00\U00001100", {44032, 4352}, {1, 2}},
      {L"\U0000ac00\U00000308\U00001100", {44032, 4352}, {2, 3}},
      {L"\U0000ac00\U00001160", {44032}, {2}},
@@ -1716,42 +1494,36 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U0000ac00\U00000308\U0000ac00", {44032, 44032}, {2, 3}},
      {L"\U0000ac00\U0000ac01", {44032, 44033}, {1, 2}},
      {L"\U0000ac00\U00000308\U0000ac01", {44032, 44033}, {2, 3}},
-     {L"\U0000ac00\U00000903", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00000903", {44032}, {3}},
-     {L"\U0000ac00\U00000904", {44032, 2308}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000904", {44032, 2308}, {2, 3}},
-     {L"\U0000ac00\U00000d4e", {44032, 3406}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000d4e", {44032, 3406}, {2, 3}},
+     {L"\U0000ac00\U00001cf5", {44032, 7413}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00001cf5", {44032, 7413}, {2, 3}},
      {L"\U0000ac00\U00000915", {44032, 2325}, {1, 2}},
      {L"\U0000ac00\U00000308\U00000915", {44032, 2325}, {2, 3}},
-     {L"\U0000ac00\U0000231a", {44032, 8986}, {1, 2}},
-     {L"\U0000ac00\U00000308\U0000231a", {44032, 8986}, {2, 3}},
-     {L"\U0000ac00\U00000300", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00000300", {44032}, {3}},
-     {L"\U0000ac00\U00000900", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00000900", {44032}, {3}},
-     {L"\U0000ac00\U0000094d", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U0000094d", {44032}, {3}},
-     {L"\U0000ac00\U0000200d", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U0000200d", {44032}, {3}},
+     {L"\U0000ac00\U000000a9", {44032, 169}, {1, 2}},
+     {L"\U0000ac00\U00000308\U000000a9", {44032, 169}, {2, 3}},
+     {L"\U0000ac00\U00000020", {44032, 32}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00000020", {44032, 32}, {2, 3}},
      {L"\U0000ac00\U00000378", {44032, 888}, {1, 2}},
      {L"\U0000ac00\U00000308\U00000378", {44032, 888}, {2, 3}},
-     {L"\U0000ac01\U00000020", {44033, 32}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000020", {44033, 32}, {2, 3}},
      {L"\U0000ac01\U0000000d", {44033, 13}, {1, 2}},
      {L"\U0000ac01\U00000308\U0000000d", {44033, 13}, {2, 3}},
      {L"\U0000ac01\U0000000a", {44033, 10}, {1, 2}},
      {L"\U0000ac01\U00000308\U0000000a", {44033, 10}, {2, 3}},
-     {L"\U0000ac01\U00000001", {44033, 1}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000001", {44033, 1}, {2, 3}},
+     {L"\U0000ac01\U00000000", {44033, 0}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00000000", {44033, 0}, {2, 3}},
+     {L"\U0000ac01\U0000094d", {44033}, {2}},
+     {L"\U0000ac01\U00000308\U0000094d", {44033}, {3}},
+     {L"\U0000ac01\U00000300", {44033}, {2}},
+     {L"\U0000ac01\U00000308\U00000300", {44033}, {3}},
      {L"\U0000ac01\U0000200c", {44033}, {2}},
      {L"\U0000ac01\U00000308\U0000200c", {44033}, {3}},
+     {L"\U0000ac01\U0000200d", {44033}, {2}},
+     {L"\U0000ac01\U00000308\U0000200d", {44033}, {3}},
      {L"\U0000ac01\U0001f1e6", {44033, 127462}, {1, 3}},
      {L"\U0000ac01\U00000308\U0001f1e6", {44033, 127462}, {2, 4}},
-     {L"\U0000ac01\U00000600", {44033, 1536}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000600", {44033, 1536}, {2, 3}},
-     {L"\U0000ac01\U00000a03", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U00000a03", {44033}, {3}},
+     {L"\U0000ac01\U000006dd", {44033, 1757}, {1, 2}},
+     {L"\U0000ac01\U00000308\U000006dd", {44033, 1757}, {2, 3}},
+     {L"\U0000ac01\U00000903", {44033}, {2}},
+     {L"\U0000ac01\U00000308\U00000903", {44033}, {3}},
      {L"\U0000ac01\U00001100", {44033, 4352}, {1, 2}},
      {L"\U0000ac01\U00000308\U00001100", {44033, 4352}, {2, 3}},
      {L"\U0000ac01\U00001160", {44033, 4448}, {1, 2}},
@@ -1762,180 +1534,76 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U0000ac01\U00000308\U0000ac00", {44033, 44032}, {2, 3}},
      {L"\U0000ac01\U0000ac01", {44033, 44033}, {1, 2}},
      {L"\U0000ac01\U00000308\U0000ac01", {44033, 44033}, {2, 3}},
-     {L"\U0000ac01\U00000903", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U00000903", {44033}, {3}},
-     {L"\U0000ac01\U00000904", {44033, 2308}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000904", {44033, 2308}, {2, 3}},
-     {L"\U0000ac01\U00000d4e", {44033, 3406}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000d4e", {44033, 3406}, {2, 3}},
+     {L"\U0000ac01\U00001cf5", {44033, 7413}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00001cf5", {44033, 7413}, {2, 3}},
      {L"\U0000ac01\U00000915", {44033, 2325}, {1, 2}},
      {L"\U0000ac01\U00000308\U00000915", {44033, 2325}, {2, 3}},
-     {L"\U0000ac01\U0000231a", {44033, 8986}, {1, 2}},
-     {L"\U0000ac01\U00000308\U0000231a", {44033, 8986}, {2, 3}},
-     {L"\U0000ac01\U00000300", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U00000300", {44033}, {3}},
-     {L"\U0000ac01\U00000900", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U00000900", {44033}, {3}},
-     {L"\U0000ac01\U0000094d", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U0000094d", {44033}, {3}},
-     {L"\U0000ac01\U0000200d", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U0000200d", {44033}, {3}},
+     {L"\U0000ac01\U000000a9", {44033, 169}, {1, 2}},
+     {L"\U0000ac01\U00000308\U000000a9", {44033, 169}, {2, 3}},
+     {L"\U0000ac01\U00000020", {44033, 32}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00000020", {44033, 32}, {2, 3}},
      {L"\U0000ac01\U00000378", {44033, 888}, {1, 2}},
      {L"\U0000ac01\U00000308\U00000378", {44033, 888}, {2, 3}},
-     {L"\U00000903\U00000020", {2307, 32}, {1, 2}},
-     {L"\U00000903\U00000308\U00000020", {2307, 32}, {2, 3}},
-     {L"\U00000903\U0000000d", {2307, 13}, {1, 2}},
-     {L"\U00000903\U00000308\U0000000d", {2307, 13}, {2, 3}},
-     {L"\U00000903\U0000000a", {2307, 10}, {1, 2}},
-     {L"\U00000903\U00000308\U0000000a", {2307, 10}, {2, 3}},
-     {L"\U00000903\U00000001", {2307, 1}, {1, 2}},
-     {L"\U00000903\U00000308\U00000001", {2307, 1}, {2, 3}},
-     {L"\U00000903\U0000200c", {2307}, {2}},
-     {L"\U00000903\U00000308\U0000200c", {2307}, {3}},
-     {L"\U00000903\U0001f1e6", {2307, 127462}, {1, 3}},
-     {L"\U00000903\U00000308\U0001f1e6", {2307, 127462}, {2, 4}},
-     {L"\U00000903\U00000600", {2307, 1536}, {1, 2}},
-     {L"\U00000903\U00000308\U00000600", {2307, 1536}, {2, 3}},
-     {L"\U00000903\U00000a03", {2307}, {2}},
-     {L"\U00000903\U00000308\U00000a03", {2307}, {3}},
-     {L"\U00000903\U00001100", {2307, 4352}, {1, 2}},
-     {L"\U00000903\U00000308\U00001100", {2307, 4352}, {2, 3}},
-     {L"\U00000903\U00001160", {2307, 4448}, {1, 2}},
-     {L"\U00000903\U00000308\U00001160", {2307, 4448}, {2, 3}},
-     {L"\U00000903\U000011a8", {2307, 4520}, {1, 2}},
-     {L"\U00000903\U00000308\U000011a8", {2307, 4520}, {2, 3}},
-     {L"\U00000903\U0000ac00", {2307, 44032}, {1, 2}},
-     {L"\U00000903\U00000308\U0000ac00", {2307, 44032}, {2, 3}},
-     {L"\U00000903\U0000ac01", {2307, 44033}, {1, 2}},
-     {L"\U00000903\U00000308\U0000ac01", {2307, 44033}, {2, 3}},
-     {L"\U00000903\U00000903", {2307}, {2}},
-     {L"\U00000903\U00000308\U00000903", {2307}, {3}},
-     {L"\U00000903\U00000904", {2307, 2308}, {1, 2}},
-     {L"\U00000903\U00000308\U00000904", {2307, 2308}, {2, 3}},
-     {L"\U00000903\U00000d4e", {2307, 3406}, {1, 2}},
-     {L"\U00000903\U00000308\U00000d4e", {2307, 3406}, {2, 3}},
-     {L"\U00000903\U00000915", {2307, 2325}, {1, 2}},
-     {L"\U00000903\U00000308\U00000915", {2307, 2325}, {2, 3}},
-     {L"\U00000903\U0000231a", {2307, 8986}, {1, 2}},
-     {L"\U00000903\U00000308\U0000231a", {2307, 8986}, {2, 3}},
-     {L"\U00000903\U00000300", {2307}, {2}},
-     {L"\U00000903\U00000308\U00000300", {2307}, {3}},
-     {L"\U00000903\U00000900", {2307}, {2}},
-     {L"\U00000903\U00000308\U00000900", {2307}, {3}},
-     {L"\U00000903\U0000094d", {2307}, {2}},
-     {L"\U00000903\U00000308\U0000094d", {2307}, {3}},
-     {L"\U00000903\U0000200d", {2307}, {2}},
-     {L"\U00000903\U00000308\U0000200d", {2307}, {3}},
-     {L"\U00000903\U00000378", {2307, 888}, {1, 2}},
-     {L"\U00000903\U00000308\U00000378", {2307, 888}, {2, 3}},
-     {L"\U00000904\U00000020", {2308, 32}, {1, 2}},
-     {L"\U00000904\U00000308\U00000020", {2308, 32}, {2, 3}},
-     {L"\U00000904\U0000000d", {2308, 13}, {1, 2}},
-     {L"\U00000904\U00000308\U0000000d", {2308, 13}, {2, 3}},
-     {L"\U00000904\U0000000a", {2308, 10}, {1, 2}},
-     {L"\U00000904\U00000308\U0000000a", {2308, 10}, {2, 3}},
-     {L"\U00000904\U00000001", {2308, 1}, {1, 2}},
-     {L"\U00000904\U00000308\U00000001", {2308, 1}, {2, 3}},
-     {L"\U00000904\U0000200c", {2308}, {2}},
-     {L"\U00000904\U00000308\U0000200c", {2308}, {3}},
-     {L"\U00000904\U0001f1e6", {2308, 127462}, {1, 3}},
-     {L"\U00000904\U00000308\U0001f1e6", {2308, 127462}, {2, 4}},
-     {L"\U00000904\U00000600", {2308, 1536}, {1, 2}},
-     {L"\U00000904\U00000308\U00000600", {2308, 1536}, {2, 3}},
-     {L"\U00000904\U00000a03", {2308}, {2}},
-     {L"\U00000904\U00000308\U00000a03", {2308}, {3}},
-     {L"\U00000904\U00001100", {2308, 4352}, {1, 2}},
-     {L"\U00000904\U00000308\U00001100", {2308, 4352}, {2, 3}},
-     {L"\U00000904\U00001160", {2308, 4448}, {1, 2}},
-     {L"\U00000904\U00000308\U00001160", {2308, 4448}, {2, 3}},
-     {L"\U00000904\U000011a8", {2308, 4520}, {1, 2}},
-     {L"\U00000904\U00000308\U000011a8", {2308, 4520}, {2, 3}},
-     {L"\U00000904\U0000ac00", {2308, 44032}, {1, 2}},
-     {L"\U00000904\U00000308\U0000ac00", {2308, 44032}, {2, 3}},
-     {L"\U00000904\U0000ac01", {2308, 44033}, {1, 2}},
-     {L"\U00000904\U00000308\U0000ac01", {2308, 44033}, {2, 3}},
-     {L"\U00000904\U00000903", {2308}, {2}},
-     {L"\U00000904\U00000308\U00000903", {2308}, {3}},
-     {L"\U00000904\U00000904", {2308, 2308}, {1, 2}},
-     {L"\U00000904\U00000308\U00000904", {2308, 2308}, {2, 3}},
-     {L"\U00000904\U00000d4e", {2308, 3406}, {1, 2}},
-     {L"\U00000904\U00000308\U00000d4e", {2308, 3406}, {2, 3}},
-     {L"\U00000904\U00000915", {2308, 2325}, {1, 2}},
-     {L"\U00000904\U00000308\U00000915", {2308, 2325}, {2, 3}},
-     {L"\U00000904\U0000231a", {2308, 8986}, {1, 2}},
-     {L"\U00000904\U00000308\U0000231a", {2308, 8986}, {2, 3}},
-     {L"\U00000904\U00000300", {2308}, {2}},
-     {L"\U00000904\U00000308\U00000300", {2308}, {3}},
-     {L"\U00000904\U00000900", {2308}, {2}},
-     {L"\U00000904\U00000308\U00000900", {2308}, {3}},
-     {L"\U00000904\U0000094d", {2308}, {2}},
-     {L"\U00000904\U00000308\U0000094d", {2308}, {3}},
-     {L"\U00000904\U0000200d", {2308}, {2}},
-     {L"\U00000904\U00000308\U0000200d", {2308}, {3}},
-     {L"\U00000904\U00000378", {2308, 888}, {1, 2}},
-     {L"\U00000904\U00000308\U00000378", {2308, 888}, {2, 3}},
-     {L"\U00000d4e\U00000020", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000020", {3406, 32}, {2, 3}},
-     {L"\U00000d4e\U0000000d", {3406, 13}, {1, 2}},
-     {L"\U00000d4e\U00000308\U0000000d", {3406, 13}, {2, 3}},
-     {L"\U00000d4e\U0000000a", {3406, 10}, {1, 2}},
-     {L"\U00000d4e\U00000308\U0000000a", {3406, 10}, {2, 3}},
-     {L"\U00000d4e\U00000001", {3406, 1}, {1, 2}},
-     {L"\U00000d4e\U00000308\U00000001", {3406, 1}, {2, 3}},
-     {L"\U00000d4e\U0000200c", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0000200c", {3406}, {3}},
-     {L"\U00000d4e\U0001f1e6", {3406}, {3}},
-     {L"\U00000d4e\U00000308\U0001f1e6", {3406, 127462}, {2, 4}},
-     {L"\U00000d4e\U00000600", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000600", {3406, 1536}, {2, 3}},
-     {L"\U00000d4e\U00000a03", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000a03", {3406}, {3}},
-     {L"\U00000d4e\U00001100", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00001100", {3406, 4352}, {2, 3}},
-     {L"\U00000d4e\U00001160", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00001160", {3406, 4448}, {2, 3}},
-     {L"\U00000d4e\U000011a8", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U000011a8", {3406, 4520}, {2, 3}},
-     {L"\U00000d4e\U0000ac00", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0000ac00", {3406, 44032}, {2, 3}},
-     {L"\U00000d4e\U0000ac01", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0000ac01", {3406, 44033}, {2, 3}},
-     {L"\U00000d4e\U00000903", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000903", {3406}, {3}},
-     {L"\U00000d4e\U00000904", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000904", {3406, 2308}, {2, 3}},
-     {L"\U00000d4e\U00000d4e", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000d4e", {3406, 3406}, {2, 3}},
-     {L"\U00000d4e\U00000915", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000915", {3406, 2325}, {2, 3}},
-     {L"\U00000d4e\U0000231a", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0000231a", {3406, 8986}, {2, 3}},
-     {L"\U00000d4e\U00000300", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000300", {3406}, {3}},
-     {L"\U00000d4e\U00000900", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000900", {3406}, {3}},
-     {L"\U00000d4e\U0000094d", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0000094d", {3406}, {3}},
-     {L"\U00000d4e\U0000200d", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0000200d", {3406}, {3}},
-     {L"\U00000d4e\U00000378", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000378", {3406, 888}, {2, 3}},
-     {L"\U00000915\U00000020", {2325, 32}, {1, 2}},
-     {L"\U00000915\U00000308\U00000020", {2325, 32}, {2, 3}},
+     {L"\U00001cf5\U0000000d", {7413, 13}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000000d", {7413, 13}, {2, 3}},
+     {L"\U00001cf5\U0000000a", {7413, 10}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000000a", {7413, 10}, {2, 3}},
+     {L"\U00001cf5\U00000000", {7413, 0}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00000000", {7413, 0}, {2, 3}},
+     {L"\U00001cf5\U0000094d", {7413}, {2}},
+     {L"\U00001cf5\U00000308\U0000094d", {7413}, {3}},
+     {L"\U00001cf5\U00000300", {7413}, {2}},
+     {L"\U00001cf5\U00000308\U00000300", {7413}, {3}},
+     {L"\U00001cf5\U0000200c", {7413}, {2}},
+     {L"\U00001cf5\U00000308\U0000200c", {7413}, {3}},
+     {L"\U00001cf5\U0000200d", {7413}, {2}},
+     {L"\U00001cf5\U00000308\U0000200d", {7413}, {3}},
+     {L"\U00001cf5\U0001f1e6", {7413, 127462}, {1, 3}},
+     {L"\U00001cf5\U00000308\U0001f1e6", {7413, 127462}, {2, 4}},
+     {L"\U00001cf5\U000006dd", {7413, 1757}, {1, 2}},
+     {L"\U00001cf5\U00000308\U000006dd", {7413, 1757}, {2, 3}},
+     {L"\U00001cf5\U00000903", {7413}, {2}},
+     {L"\U00001cf5\U00000308\U00000903", {7413}, {3}},
+     {L"\U00001cf5\U00001100", {7413, 4352}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00001100", {7413, 4352}, {2, 3}},
+     {L"\U00001cf5\U00001160", {7413, 4448}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00001160", {7413, 4448}, {2, 3}},
+     {L"\U00001cf5\U000011a8", {7413, 4520}, {1, 2}},
+     {L"\U00001cf5\U00000308\U000011a8", {7413, 4520}, {2, 3}},
+     {L"\U00001cf5\U0000ac00", {7413, 44032}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000ac00", {7413, 44032}, {2, 3}},
+     {L"\U00001cf5\U0000ac01", {7413, 44033}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000ac01", {7413, 44033}, {2, 3}},
+     {L"\U00001cf5\U00001cf5", {7413, 7413}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00001cf5", {7413, 7413}, {2, 3}},
+     {L"\U00001cf5\U00000915", {7413}, {2}},
+     {L"\U00001cf5\U00000308\U00000915", {7413}, {3}},
+     {L"\U00001cf5\U000000a9", {7413, 169}, {1, 2}},
+     {L"\U00001cf5\U00000308\U000000a9", {7413, 169}, {2, 3}},
+     {L"\U00001cf5\U00000020", {7413, 32}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00000020", {7413, 32}, {2, 3}},
+     {L"\U00001cf5\U00000378", {7413, 888}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00000378", {7413, 888}, {2, 3}},
      {L"\U00000915\U0000000d", {2325, 13}, {1, 2}},
      {L"\U00000915\U00000308\U0000000d", {2325, 13}, {2, 3}},
      {L"\U00000915\U0000000a", {2325, 10}, {1, 2}},
      {L"\U00000915\U00000308\U0000000a", {2325, 10}, {2, 3}},
-     {L"\U00000915\U00000001", {2325, 1}, {1, 2}},
-     {L"\U00000915\U00000308\U00000001", {2325, 1}, {2, 3}},
+     {L"\U00000915\U00000000", {2325, 0}, {1, 2}},
+     {L"\U00000915\U00000308\U00000000", {2325, 0}, {2, 3}},
+     {L"\U00000915\U0000094d", {2325}, {2}},
+     {L"\U00000915\U00000308\U0000094d", {2325}, {3}},
+     {L"\U00000915\U00000300", {2325}, {2}},
+     {L"\U00000915\U00000308\U00000300", {2325}, {3}},
      {L"\U00000915\U0000200c", {2325}, {2}},
      {L"\U00000915\U00000308\U0000200c", {2325}, {3}},
+     {L"\U00000915\U0000200d", {2325}, {2}},
+     {L"\U00000915\U00000308\U0000200d", {2325}, {3}},
      {L"\U00000915\U0001f1e6", {2325, 127462}, {1, 3}},
      {L"\U00000915\U00000308\U0001f1e6", {2325, 127462}, {2, 4}},
-     {L"\U00000915\U00000600", {2325, 1536}, {1, 2}},
-     {L"\U00000915\U00000308\U00000600", {2325, 1536}, {2, 3}},
-     {L"\U00000915\U00000a03", {2325}, {2}},
-     {L"\U00000915\U00000308\U00000a03", {2325}, {3}},
+     {L"\U00000915\U000006dd", {2325, 1757}, {1, 2}},
+     {L"\U00000915\U00000308\U000006dd", {2325, 1757}, {2, 3}},
+     {L"\U00000915\U00000903", {2325}, {2}},
+     {L"\U00000915\U00000308\U00000903", {2325}, {3}},
      {L"\U00000915\U00001100", {2325, 4352}, {1, 2}},
      {L"\U00000915\U00000308\U00001100", {2325, 4352}, {2, 3}},
      {L"\U00000915\U00001160", {2325, 4448}, {1, 2}},
@@ -1946,272 +1614,116 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U00000915\U00000308\U0000ac00", {2325, 44032}, {2, 3}},
      {L"\U00000915\U0000ac01", {2325, 44033}, {1, 2}},
      {L"\U00000915\U00000308\U0000ac01", {2325, 44033}, {2, 3}},
-     {L"\U00000915\U00000903", {2325}, {2}},
-     {L"\U00000915\U00000308\U00000903", {2325}, {3}},
-     {L"\U00000915\U00000904", {2325, 2308}, {1, 2}},
-     {L"\U00000915\U00000308\U00000904", {2325, 2308}, {2, 3}},
-     {L"\U00000915\U00000d4e", {2325, 3406}, {1, 2}},
-     {L"\U00000915\U00000308\U00000d4e", {2325, 3406}, {2, 3}},
+     {L"\U00000915\U00001cf5", {2325, 7413}, {1, 2}},
+     {L"\U00000915\U00000308\U00001cf5", {2325, 7413}, {2, 3}},
      {L"\U00000915\U00000915", {2325, 2325}, {1, 2}},
      {L"\U00000915\U00000308\U00000915", {2325, 2325}, {2, 3}},
-     {L"\U00000915\U0000231a", {2325, 8986}, {1, 2}},
-     {L"\U00000915\U00000308\U0000231a", {2325, 8986}, {2, 3}},
-     {L"\U00000915\U00000300", {2325}, {2}},
-     {L"\U00000915\U00000308\U00000300", {2325}, {3}},
-     {L"\U00000915\U00000900", {2325}, {2}},
-     {L"\U00000915\U00000308\U00000900", {2325}, {3}},
-     {L"\U00000915\U0000094d", {2325}, {2}},
-     {L"\U00000915\U00000308\U0000094d", {2325}, {3}},
-     {L"\U00000915\U0000200d", {2325}, {2}},
-     {L"\U00000915\U00000308\U0000200d", {2325}, {3}},
+     {L"\U00000915\U000000a9", {2325, 169}, {1, 2}},
+     {L"\U00000915\U00000308\U000000a9", {2325, 169}, {2, 3}},
+     {L"\U00000915\U00000020", {2325, 32}, {1, 2}},
+     {L"\U00000915\U00000308\U00000020", {2325, 32}, {2, 3}},
      {L"\U00000915\U00000378", {2325, 888}, {1, 2}},
      {L"\U00000915\U00000308\U00000378", {2325, 888}, {2, 3}},
-     {L"\U0000231a\U00000020", {8986, 32}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000020", {8986, 32}, {2, 3}},
-     {L"\U0000231a\U0000000d", {8986, 13}, {1, 2}},
-     {L"\U0000231a\U00000308\U0000000d", {8986, 13}, {2, 3}},
-     {L"\U0000231a\U0000000a", {8986, 10}, {1, 2}},
-     {L"\U0000231a\U00000308\U0000000a", {8986, 10}, {2, 3}},
-     {L"\U0000231a\U00000001", {8986, 1}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000001", {8986, 1}, {2, 3}},
-     {L"\U0000231a\U0000200c", {8986}, {2}},
-     {L"\U0000231a\U00000308\U0000200c", {8986}, {3}},
-     {L"\U0000231a\U0001f1e6", {8986, 127462}, {1, 3}},
-     {L"\U0000231a\U00000308\U0001f1e6", {8986, 127462}, {2, 4}},
-     {L"\U0000231a\U00000600", {8986, 1536}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000600", {8986, 1536}, {2, 3}},
-     {L"\U0000231a\U00000a03", {8986}, {2}},
-     {L"\U0000231a\U00000308\U00000a03", {8986}, {3}},
-     {L"\U0000231a\U00001100", {8986, 4352}, {1, 2}},
-     {L"\U0000231a\U00000308\U00001100", {8986, 4352}, {2, 3}},
-     {L"\U0000231a\U00001160", {8986, 4448}, {1, 2}},
-     {L"\U0000231a\U00000308\U00001160", {8986, 4448}, {2, 3}},
-     {L"\U0000231a\U000011a8", {8986, 4520}, {1, 2}},
-     {L"\U0000231a\U00000308\U000011a8", {8986, 4520}, {2, 3}},
-     {L"\U0000231a\U0000ac00", {8986, 44032}, {1, 2}},
-     {L"\U0000231a\U00000308\U0000ac00", {8986, 44032}, {2, 3}},
-     {L"\U0000231a\U0000ac01", {8986, 44033}, {1, 2}},
-     {L"\U0000231a\U00000308\U0000ac01", {8986, 44033}, {2, 3}},
-     {L"\U0000231a\U00000903", {8986}, {2}},
-     {L"\U0000231a\U00000308\U00000903", {8986}, {3}},
-     {L"\U0000231a\U00000904", {8986, 2308}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000904", {8986, 2308}, {2, 3}},
-     {L"\U0000231a\U00000d4e", {8986, 3406}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000d4e", {8986, 3406}, {2, 3}},
-     {L"\U0000231a\U00000915", {8986, 2325}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000915", {8986, 2325}, {2, 3}},
-     {L"\U0000231a\U0000231a", {8986, 8986}, {1, 2}},
-     {L"\U0000231a\U00000308\U0000231a", {8986, 8986}, {2, 3}},
-     {L"\U0000231a\U00000300", {8986}, {2}},
-     {L"\U0000231a\U00000308\U00000300", {8986}, {3}},
-     {L"\U0000231a\U00000900", {8986}, {2}},
-     {L"\U0000231a\U00000308\U00000900", {8986}, {3}},
-     {L"\U0000231a\U0000094d", {8986}, {2}},
-     {L"\U0000231a\U00000308\U0000094d", {8986}, {3}},
-     {L"\U0000231a\U0000200d", {8986}, {2}},
-     {L"\U0000231a\U00000308\U0000200d", {8986}, {3}},
-     {L"\U0000231a\U00000378", {8986, 888}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000378", {8986, 888}, {2, 3}},
-     {L"\U00000300\U00000020", {768, 32}, {1, 2}},
-     {L"\U00000300\U00000308\U00000020", {768, 32}, {2, 3}},
-     {L"\U00000300\U0000000d", {768, 13}, {1, 2}},
-     {L"\U00000300\U00000308\U0000000d", {768, 13}, {2, 3}},
-     {L"\U00000300\U0000000a", {768, 10}, {1, 2}},
-     {L"\U00000300\U00000308\U0000000a", {768, 10}, {2, 3}},
-     {L"\U00000300\U00000001", {768, 1}, {1, 2}},
-     {L"\U00000300\U00000308\U00000001", {768, 1}, {2, 3}},
-     {L"\U00000300\U0000200c", {768}, {2}},
-     {L"\U00000300\U00000308\U0000200c", {768}, {3}},
-     {L"\U00000300\U0001f1e6", {768, 127462}, {1, 3}},
-     {L"\U00000300\U00000308\U0001f1e6", {768, 127462}, {2, 4}},
-     {L"\U00000300\U00000600", {768, 1536}, {1, 2}},
-     {L"\U00000300\U00000308\U00000600", {768, 1536}, {2, 3}},
-     {L"\U00000300\U00000a03", {768}, {2}},
-     {L"\U00000300\U00000308\U00000a03", {768}, {3}},
-     {L"\U00000300\U00001100", {768, 4352}, {1, 2}},
-     {L"\U00000300\U00000308\U00001100", {768, 4352}, {2, 3}},
-     {L"\U00000300\U00001160", {768, 4448}, {1, 2}},
-     {L"\U00000300\U00000308\U00001160", {768, 4448}, {2, 3}},
-     {L"\U00000300\U000011a8", {768, 4520}, {1, 2}},
-     {L"\U00000300\U00000308\U000011a8", {768, 4520}, {2, 3}},
-     {L"\U00000300\U0000ac00", {768, 44032}, {1, 2}},
-     {L"\U00000300\U00000308\U0000ac00", {768, 44032}, {2, 3}},
-     {L"\U00000300\U0000ac01", {768, 44033}, {1, 2}},
-     {L"\U00000300\U00000308\U0000ac01", {768, 44033}, {2, 3}},
-     {L"\U00000300\U00000903", {768}, {2}},
-     {L"\U00000300\U00000308\U00000903", {768}, {3}},
-     {L"\U00000300\U00000904", {768, 2308}, {1, 2}},
-     {L"\U00000300\U00000308\U00000904", {768, 2308}, {2, 3}},
-     {L"\U00000300\U00000d4e", {768, 3406}, {1, 2}},
-     {L"\U00000300\U00000308\U00000d4e", {768, 3406}, {2, 3}},
-     {L"\U00000300\U00000915", {768, 2325}, {1, 2}},
-     {L"\U00000300\U00000308\U00000915", {768, 2325}, {2, 3}},
-     {L"\U00000300\U0000231a", {768, 8986}, {1, 2}},
-     {L"\U00000300\U00000308\U0000231a", {768, 8986}, {2, 3}},
-     {L"\U00000300\U00000300", {768}, {2}},
-     {L"\U00000300\U00000308\U00000300", {768}, {3}},
-     {L"\U00000300\U00000900", {768}, {2}},
-     {L"\U00000300\U00000308\U00000900", {768}, {3}},
-     {L"\U00000300\U0000094d", {768}, {2}},
-     {L"\U00000300\U00000308\U0000094d", {768}, {3}},
-     {L"\U00000300\U0000200d", {768}, {2}},
-     {L"\U00000300\U00000308\U0000200d", {768}, {3}},
-     {L"\U00000300\U00000378", {768, 888}, {1, 2}},
-     {L"\U00000300\U00000308\U00000378", {768, 888}, {2, 3}},
-     {L"\U00000900\U00000020", {2304, 32}, {1, 2}},
-     {L"\U00000900\U00000308\U00000020", {2304, 32}, {2, 3}},
-     {L"\U00000900\U0000000d", {2304, 13}, {1, 2}},
-     {L"\U00000900\U00000308\U0000000d", {2304, 13}, {2, 3}},
-     {L"\U00000900\U0000000a", {2304, 10}, {1, 2}},
-     {L"\U00000900\U00000308\U0000000a", {2304, 10}, {2, 3}},
-     {L"\U00000900\U00000001", {2304, 1}, {1, 2}},
-     {L"\U00000900\U00000308\U00000001", {2304, 1}, {2, 3}},
-     {L"\U00000900\U0000200c", {2304}, {2}},
-     {L"\U00000900\U00000308\U0000200c", {2304}, {3}},
-     {L"\U00000900\U0001f1e6", {2304, 127462}, {1, 3}},
-     {L"\U00000900\U00000308\U0001f1e6", {2304, 127462}, {2, 4}},
-     {L"\U00000900\U00000600", {2304, 1536}, {1, 2}},
-     {L"\U00000900\U00000308\U00000600", {2304, 1536}, {2, 3}},
-     {L"\U00000900\U00000a03", {2304}, {2}},
-     {L"\U00000900\U00000308\U00000a03", {2304}, {3}},
-     {L"\U00000900\U00001100", {2304, 4352}, {1, 2}},
-     {L"\U00000900\U00000308\U00001100", {2304, 4352}, {2, 3}},
-     {L"\U00000900\U00001160", {2304, 4448}, {1, 2}},
-     {L"\U00000900\U00000308\U00001160", {2304, 4448}, {2, 3}},
-     {L"\U00000900\U000011a8", {2304, 4520}, {1, 2}},
-     {L"\U00000900\U00000308\U000011a8", {2304, 4520}, {2, 3}},
-     {L"\U00000900\U0000ac00", {2304, 44032}, {1, 2}},
-     {L"\U00000900\U00000308\U0000ac00", {2304, 44032}, {2, 3}},
-     {L"\U00000900\U0000ac01", {2304, 44033}, {1, 2}},
-     {L"\U00000900\U00000308\U0000ac01", {2304, 44033}, {2, 3}},
-     {L"\U00000900\U00000903", {2304}, {2}},
-     {L"\U00000900\U00000308\U00000903", {2304}, {3}},
-     {L"\U00000900\U00000904", {2304, 2308}, {1, 2}},
-     {L"\U00000900\U00000308\U00000904", {2304, 2308}, {2, 3}},
-     {L"\U00000900\U00000d4e", {2304, 3406}, {1, 2}},
-     {L"\U00000900\U00000308\U00000d4e", {2304, 3406}, {2, 3}},
-     {L"\U00000900\U00000915", {2304, 2325}, {1, 2}},
-     {L"\U00000900\U00000308\U00000915", {2304, 2325}, {2, 3}},
-     {L"\U00000900\U0000231a", {2304, 8986}, {1, 2}},
-     {L"\U00000900\U00000308\U0000231a", {2304, 8986}, {2, 3}},
-     {L"\U00000900\U00000300", {2304}, {2}},
-     {L"\U00000900\U00000308\U00000300", {2304}, {3}},
-     {L"\U00000900\U00000900", {2304}, {2}},
-     {L"\U00000900\U00000308\U00000900", {2304}, {3}},
-     {L"\U00000900\U0000094d", {2304}, {2}},
-     {L"\U00000900\U00000308\U0000094d", {2304}, {3}},
-     {L"\U00000900\U0000200d", {2304}, {2}},
-     {L"\U00000900\U00000308\U0000200d", {2304}, {3}},
-     {L"\U00000900\U00000378", {2304, 888}, {1, 2}},
-     {L"\U00000900\U00000308\U00000378", {2304, 888}, {2, 3}},
-     {L"\U0000094d\U00000020", {2381, 32}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000020", {2381, 32}, {2, 3}},
-     {L"\U0000094d\U0000000d", {2381, 13}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000000d", {2381, 13}, {2, 3}},
-     {L"\U0000094d\U0000000a", {2381, 10}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000000a", {2381, 10}, {2, 3}},
-     {L"\U0000094d\U00000001", {2381, 1}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000001", {2381, 1}, {2, 3}},
-     {L"\U0000094d\U0000200c", {2381}, {2}},
-     {L"\U0000094d\U00000308\U0000200c", {2381}, {3}},
-     {L"\U0000094d\U0001f1e6", {2381, 127462}, {1, 3}},
-     {L"\U0000094d\U00000308\U0001f1e6", {2381, 127462}, {2, 4}},
-     {L"\U0000094d\U00000600", {2381, 1536}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000600", {2381, 1536}, {2, 3}},
-     {L"\U0000094d\U00000a03", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000a03", {2381}, {3}},
-     {L"\U0000094d\U00001100", {2381, 4352}, {1, 2}},
-     {L"\U0000094d\U00000308\U00001100", {2381, 4352}, {2, 3}},
-     {L"\U0000094d\U00001160", {2381, 4448}, {1, 2}},
-     {L"\U0000094d\U00000308\U00001160", {2381, 4448}, {2, 3}},
-     {L"\U0000094d\U000011a8", {2381, 4520}, {1, 2}},
-     {L"\U0000094d\U00000308\U000011a8", {2381, 4520}, {2, 3}},
-     {L"\U0000094d\U0000ac00", {2381, 44032}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000ac00", {2381, 44032}, {2, 3}},
-     {L"\U0000094d\U0000ac01", {2381, 44033}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000ac01", {2381, 44033}, {2, 3}},
-     {L"\U0000094d\U00000903", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000903", {2381}, {3}},
-     {L"\U0000094d\U00000904", {2381, 2308}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000904", {2381, 2308}, {2, 3}},
-     {L"\U0000094d\U00000d4e", {2381, 3406}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000d4e", {2381, 3406}, {2, 3}},
-     {L"\U0000094d\U00000915", {2381, 2325}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000915", {2381, 2325}, {2, 3}},
-     {L"\U0000094d\U0000231a", {2381, 8986}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000231a", {2381, 8986}, {2, 3}},
-     {L"\U0000094d\U00000300", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000300", {2381}, {3}},
-     {L"\U0000094d\U00000900", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000900", {2381}, {3}},
-     {L"\U0000094d\U0000094d", {2381}, {2}},
-     {L"\U0000094d\U00000308\U0000094d", {2381}, {3}},
-     {L"\U0000094d\U0000200d", {2381}, {2}},
-     {L"\U0000094d\U00000308\U0000200d", {2381}, {3}},
-     {L"\U0000094d\U00000378", {2381, 888}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000378", {2381, 888}, {2, 3}},
-     {L"\U0000200d\U00000020", {8205, 32}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000020", {8205, 32}, {2, 3}},
-     {L"\U0000200d\U0000000d", {8205, 13}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000000d", {8205, 13}, {2, 3}},
-     {L"\U0000200d\U0000000a", {8205, 10}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000000a", {8205, 10}, {2, 3}},
-     {L"\U0000200d\U00000001", {8205, 1}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000001", {8205, 1}, {2, 3}},
-     {L"\U0000200d\U0000200c", {8205}, {2}},
-     {L"\U0000200d\U00000308\U0000200c", {8205}, {3}},
-     {L"\U0000200d\U0001f1e6", {8205, 127462}, {1, 3}},
-     {L"\U0000200d\U00000308\U0001f1e6", {8205, 127462}, {2, 4}},
-     {L"\U0000200d\U00000600", {8205, 1536}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000600", {8205, 1536}, {2, 3}},
-     {L"\U0000200d\U00000a03", {8205}, {2}},
-     {L"\U0000200d\U00000308\U00000a03", {8205}, {3}},
-     {L"\U0000200d\U00001100", {8205, 4352}, {1, 2}},
-     {L"\U0000200d\U00000308\U00001100", {8205, 4352}, {2, 3}},
-     {L"\U0000200d\U00001160", {8205, 4448}, {1, 2}},
-     {L"\U0000200d\U00000308\U00001160", {8205, 4448}, {2, 3}},
-     {L"\U0000200d\U000011a8", {8205, 4520}, {1, 2}},
-     {L"\U0000200d\U00000308\U000011a8", {8205, 4520}, {2, 3}},
-     {L"\U0000200d\U0000ac00", {8205, 44032}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000ac00", {8205, 44032}, {2, 3}},
-     {L"\U0000200d\U0000ac01", {8205, 44033}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000ac01", {8205, 44033}, {2, 3}},
-     {L"\U0000200d\U00000903", {8205}, {2}},
-     {L"\U0000200d\U00000308\U00000903", {8205}, {3}},
-     {L"\U0000200d\U00000904", {8205, 2308}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000904", {8205, 2308}, {2, 3}},
-     {L"\U0000200d\U00000d4e", {8205, 3406}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000d4e", {8205, 3406}, {2, 3}},
-     {L"\U0000200d\U00000915", {8205, 2325}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000915", {8205, 2325}, {2, 3}},
-     {L"\U0000200d\U0000231a", {8205, 8986}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000231a", {8205, 8986}, {2, 3}},
-     {L"\U0000200d\U00000300", {8205}, {2}},
-     {L"\U0000200d\U00000308\U00000300", {8205}, {3}},
-     {L"\U0000200d\U00000900", {8205}, {2}},
-     {L"\U0000200d\U00000308\U00000900", {8205}, {3}},
-     {L"\U0000200d\U0000094d", {8205}, {2}},
-     {L"\U0000200d\U00000308\U0000094d", {8205}, {3}},
-     {L"\U0000200d\U0000200d", {8205}, {2}},
-     {L"\U0000200d\U00000308\U0000200d", {8205}, {3}},
-     {L"\U0000200d\U00000378", {8205, 888}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000378", {8205, 888}, {2, 3}},
-     {L"\U00000378\U00000020", {888, 32}, {1, 2}},
-     {L"\U00000378\U00000308\U00000020", {888, 32}, {2, 3}},
+     {L"\U000000a9\U0000000d", {169, 13}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000000d", {169, 13}, {2, 3}},
+     {L"\U000000a9\U0000000a", {169, 10}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000000a", {169, 10}, {2, 3}},
+     {L"\U000000a9\U00000000", {169, 0}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000000", {169, 0}, {2, 3}},
+     {L"\U000000a9\U0000094d", {169}, {2}},
+     {L"\U000000a9\U00000308\U0000094d", {169}, {3}},
+     {L"\U000000a9\U00000300", {169}, {2}},
+     {L"\U000000a9\U00000308\U00000300", {169}, {3}},
+     {L"\U000000a9\U0000200c", {169}, {2}},
+     {L"\U000000a9\U00000308\U0000200c", {169}, {3}},
+     {L"\U000000a9\U0000200d", {169}, {2}},
+     {L"\U000000a9\U00000308\U0000200d", {169}, {3}},
+     {L"\U000000a9\U0001f1e6", {169, 127462}, {1, 3}},
+     {L"\U000000a9\U00000308\U0001f1e6", {169, 127462}, {2, 4}},
+     {L"\U000000a9\U000006dd", {169, 1757}, {1, 2}},
+     {L"\U000000a9\U00000308\U000006dd", {169, 1757}, {2, 3}},
+     {L"\U000000a9\U00000903", {169}, {2}},
+     {L"\U000000a9\U00000308\U00000903", {169}, {3}},
+     {L"\U000000a9\U00001100", {169, 4352}, {1, 2}},
+     {L"\U000000a9\U00000308\U00001100", {169, 4352}, {2, 3}},
+     {L"\U000000a9\U00001160", {169, 4448}, {1, 2}},
+     {L"\U000000a9\U00000308\U00001160", {169, 4448}, {2, 3}},
+     {L"\U000000a9\U000011a8", {169, 4520}, {1, 2}},
+     {L"\U000000a9\U00000308\U000011a8", {169, 4520}, {2, 3}},
+     {L"\U000000a9\U0000ac00", {169, 44032}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000ac00", {169, 44032}, {2, 3}},
+     {L"\U000000a9\U0000ac01", {169, 44033}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000ac01", {169, 44033}, {2, 3}},
+     {L"\U000000a9\U00001cf5", {169, 7413}, {1, 2}},
+     {L"\U000000a9\U00000308\U00001cf5", {169, 7413}, {2, 3}},
+     {L"\U000000a9\U00000915", {169, 2325}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000915", {169, 2325}, {2, 3}},
+     {L"\U000000a9\U000000a9", {169, 169}, {1, 2}},
+     {L"\U000000a9\U00000308\U000000a9", {169, 169}, {2, 3}},
+     {L"\U000000a9\U00000020", {169, 32}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000020", {169, 32}, {2, 3}},
+     {L"\U000000a9\U00000378", {169, 888}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000378", {169, 888}, {2, 3}},
+     {L"\U00000020\U0000000d", {32, 13}, {1, 2}},
+     {L"\U00000020\U00000308\U0000000d", {32, 13}, {2, 3}},
+     {L"\U00000020\U0000000a", {32, 10}, {1, 2}},
+     {L"\U00000020\U00000308\U0000000a", {32, 10}, {2, 3}},
+     {L"\U00000020\U00000000", {32, 0}, {1, 2}},
+     {L"\U00000020\U00000308\U00000000", {32, 0}, {2, 3}},
+     {L"\U00000020\U0000094d", {32}, {2}},
+     {L"\U00000020\U00000308\U0000094d", {32}, {3}},
+     {L"\U00000020\U00000300", {32}, {2}},
+     {L"\U00000020\U00000308\U00000300", {32}, {3}},
+     {L"\U00000020\U0000200c", {32}, {2}},
+     {L"\U00000020\U00000308\U0000200c", {32}, {3}},
+     {L"\U00000020\U0000200d", {32}, {2}},
+     {L"\U00000020\U00000308\U0000200d", {32}, {3}},
+     {L"\U00000020\U0001f1e6", {32, 127462}, {1, 3}},
+     {L"\U00000020\U00000308\U0001f1e6", {32, 127462}, {2, 4}},
+     {L"\U00000020\U000006dd", {32, 1757}, {1, 2}},
+     {L"\U00000020\U00000308\U000006dd", {32, 1757}, {2, 3}},
+     {L"\U00000020\U00000903", {32}, {2}},
+     {L"\U00000020\U00000308\U00000903", {32}, {3}},
+     {L"\U00000020\U00001100", {32, 4352}, {1, 2}},
+     {L"\U00000020\U00000308\U00001100", {32, 4352}, {2, 3}},
+     {L"\U00000020\U00001160", {32, 4448}, {1, 2}},
+     {L"\U00000020\U00000308\U00001160", {32, 4448}, {2, 3}},
+     {L"\U00000020\U000011a8", {32, 4520}, {1, 2}},
+     {L"\U00000020\U00000308\U000011a8", {32, 4520}, {2, 3}},
+     {L"\U00000020\U0000ac00", {32, 44032}, {1, 2}},
+     {L"\U00000020\U00000308\U0000ac00", {32, 44032}, {2, 3}},
+     {L"\U00000020\U0000ac01", {32, 44033}, {1, 2}},
+     {L"\U00000020\U00000308\U0000ac01", {32, 44033}, {2, 3}},
+     {L"\U00000020\U00001cf5", {32, 7413}, {1, 2}},
+     {L"\U00000020\U00000308\U00001cf5", {32, 7413}, {2, 3}},
+     {L"\U00000020\U00000915", {32, 2325}, {1, 2}},
+     {L"\U00000020\U00000308\U00000915", {32, 2325}, {2, 3}},
+     {L"\U00000020\U000000a9", {32, 169}, {1, 2}},
+     {L"\U00000020\U00000308\U000000a9", {32, 169}, {2, 3}},
+     {L"\U00000020\U00000020", {32, 32}, {1, 2}},
+     {L"\U00000020\U00000308\U00000020", {32, 32}, {2, 3}},
+     {L"\U00000020\U00000378", {32, 888}, {1, 2}},
+     {L"\U00000020\U00000308\U00000378", {32, 888}, {2, 3}},
      {L"\U00000378\U0000000d", {888, 13}, {1, 2}},
      {L"\U00000378\U00000308\U0000000d", {888, 13}, {2, 3}},
      {L"\U00000378\U0000000a", {888, 10}, {1, 2}},
      {L"\U00000378\U00000308\U0000000a", {888, 10}, {2, 3}},
-     {L"\U00000378\U00000001", {888, 1}, {1, 2}},
-     {L"\U00000378\U00000308\U00000001", {888, 1}, {2, 3}},
+     {L"\U00000378\U00000000", {888, 0}, {1, 2}},
+     {L"\U00000378\U00000308\U00000000", {888, 0}, {2, 3}},
+     {L"\U00000378\U0000094d", {888}, {2}},
+     {L"\U00000378\U00000308\U0000094d", {888}, {3}},
+     {L"\U00000378\U00000300", {888}, {2}},
+     {L"\U00000378\U00000308\U00000300", {888}, {3}},
      {L"\U00000378\U0000200c", {888}, {2}},
      {L"\U00000378\U00000308\U0000200c", {888}, {3}},
+     {L"\U00000378\U0000200d", {888}, {2}},
+     {L"\U00000378\U00000308\U0000200d", {888}, {3}},
      {L"\U00000378\U0001f1e6", {888, 127462}, {1, 3}},
      {L"\U00000378\U00000308\U0001f1e6", {888, 127462}, {2, 4}},
-     {L"\U00000378\U00000600", {888, 1536}, {1, 2}},
-     {L"\U00000378\U00000308\U00000600", {888, 1536}, {2, 3}},
-     {L"\U00000378\U00000a03", {888}, {2}},
-     {L"\U00000378\U00000308\U00000a03", {888}, {3}},
+     {L"\U00000378\U000006dd", {888, 1757}, {1, 2}},
+     {L"\U00000378\U00000308\U000006dd", {888, 1757}, {2, 3}},
+     {L"\U00000378\U00000903", {888}, {2}},
+     {L"\U00000378\U00000308\U00000903", {888}, {3}},
      {L"\U00000378\U00001100", {888, 4352}, {1, 2}},
      {L"\U00000378\U00000308\U00001100", {888, 4352}, {2, 3}},
      {L"\U00000378\U00001160", {888, 4448}, {1, 2}},
@@ -2222,24 +1734,14 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U00000378\U00000308\U0000ac00", {888, 44032}, {2, 3}},
      {L"\U00000378\U0000ac01", {888, 44033}, {1, 2}},
      {L"\U00000378\U00000308\U0000ac01", {888, 44033}, {2, 3}},
-     {L"\U00000378\U00000903", {888}, {2}},
-     {L"\U00000378\U00000308\U00000903", {888}, {3}},
-     {L"\U00000378\U00000904", {888, 2308}, {1, 2}},
-     {L"\U00000378\U00000308\U00000904", {888, 2308}, {2, 3}},
-     {L"\U00000378\U00000d4e", {888, 3406}, {1, 2}},
-     {L"\U00000378\U00000308\U00000d4e", {888, 3406}, {2, 3}},
+     {L"\U00000378\U00001cf5", {888, 7413}, {1, 2}},
+     {L"\U00000378\U00000308\U00001cf5", {888, 7413}, {2, 3}},
      {L"\U00000378\U00000915", {888, 2325}, {1, 2}},
      {L"\U00000378\U00000308\U00000915", {888, 2325}, {2, 3}},
-     {L"\U00000378\U0000231a", {888, 8986}, {1, 2}},
-     {L"\U00000378\U00000308\U0000231a", {888, 8986}, {2, 3}},
-     {L"\U00000378\U00000300", {888}, {2}},
-     {L"\U00000378\U00000308\U00000300", {888}, {3}},
-     {L"\U00000378\U00000900", {888}, {2}},
-     {L"\U00000378\U00000308\U00000900", {888}, {3}},
-     {L"\U00000378\U0000094d", {888}, {2}},
-     {L"\U00000378\U00000308\U0000094d", {888}, {3}},
-     {L"\U00000378\U0000200d", {888}, {2}},
-     {L"\U00000378\U00000308\U0000200d", {888}, {3}},
+     {L"\U00000378\U000000a9", {888, 169}, {1, 2}},
+     {L"\U00000378\U00000308\U000000a9", {888, 169}, {2, 3}},
+     {L"\U00000378\U00000020", {888, 32}, {1, 2}},
+     {L"\U00000378\U00000308\U00000020", {888, 32}, {2, 3}},
      {L"\U00000378\U00000378", {888, 888}, {1, 2}},
      {L"\U00000378\U00000308\U00000378", {888, 888}, {2, 3}},
      {L"\U0000000d\U0000000a\U00000061\U0000000a\U00000308", {13, 97, 10, 776}, {2, 3, 4, 5}},
@@ -2264,7 +1766,7 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U0001f476\U0001f3ff\U00000308\U0000200d\U0001f476\U0001f3ff", {128118}, {10}},
      {L"\U0001f6d1\U0000200d\U0001f6d1", {128721}, {5}},
      {L"\U00000061\U0000200d\U0001f6d1", {97, 128721}, {2, 4}},
-     {L"\U00002701\U0000200d\U00002701", {9985}, {3}},
+     {L"\U00002701\U0000200d\U00002701", {9985, 9985}, {2, 3}},
      {L"\U00000061\U0000200d\U00002701", {97, 9985}, {2, 3}},
      {L"\U00000915\U00000924", {2325, 2340}, {1, 2}},
      {L"\U00000915\U0000094d\U00000924", {2325}, {3}},
@@ -2272,80 +1774,56 @@ std::array<data<wchar_t>, 1093> data_utf16 = {{
      {L"\U00000915\U0000094d\U0000200d\U00000924", {2325}, {4}},
      {L"\U00000915\U0000093c\U0000200d\U0000094d\U00000924", {2325}, {5}},
      {L"\U00000915\U0000093c\U0000094d\U0000200d\U00000924", {2325}, {5}},
-     {L"\U00000915\U0000094d\U00000924\U0000094d\U0000092f", {2325}, {5}},
-     {L"\U00000915\U0000094d\U00000061", {2325, 97}, {2, 3}},
-     {L"\U00000061\U0000094d\U00000924", {97, 2340}, {2, 3}},
-     {L"\U0000003f\U0000094d\U00000924", {63, 2340}, {2, 3}},
-     {L"\U00000915\U0000094d\U0000094d\U00000924", {2325}, {4}}}};
-
-/// The data for UTF-8.
-///
-/// Note that most of the data for the UTF-16 and UTF-32 are identical. However
-/// since the size of the code units differ the breaks can contain different
-/// values.
-std::array<data<wchar_t>, 1093> data_utf32 = {{
-     {L"\U00000020\U00000020", {32, 32}, {1, 2}},
-     {L"\U00000020\U00000308\U00000020", {32, 32}, {2, 3}},
-     {L"\U00000020\U0000000d", {32, 13}, {1, 2}},
-     {L"\U00000020\U00000308\U0000000d", {32, 13}, {2, 3}},
-     {L"\U00000020\U0000000a", {32, 10}, {1, 2}},
-     {L"\U00000020\U00000308\U0000000a", {32, 10}, {2, 3}},
-     {L"\U00000020\U00000001", {32, 1}, {1, 2}},
-     {L"\U00000020\U00000308\U00000001", {32, 1}, {2, 3}},
-     {L"\U00000020\U0000200c", {32}, {2}},
-     {L"\U00000020\U00000308\U0000200c", {32}, {3}},
-     {L"\U00000020\U0001f1e6", {32, 127462}, {1, 2}},
-     {L"\U00000020\U00000308\U0001f1e6", {32, 127462}, {2, 3}},
-     {L"\U00000020\U00000600", {32, 1536}, {1, 2}},
-     {L"\U00000020\U00000308\U00000600", {32, 1536}, {2, 3}},
-     {L"\U00000020\U00000a03", {32}, {2}},
-     {L"\U00000020\U00000308\U00000a03", {32}, {3}},
-     {L"\U00000020\U00001100", {32, 4352}, {1, 2}},
-     {L"\U00000020\U00000308\U00001100", {32, 4352}, {2, 3}},
-     {L"\U00000020\U00001160", {32, 4448}, {1, 2}},
-     {L"\U00000020\U00000308\U00001160", {32, 4448}, {2, 3}},
-     {L"\U00000020\U000011a8", {32, 4520}, {1, 2}},
-     {L"\U00000020\U00000308\U000011a8", {32, 4520}, {2, 3}},
-     {L"\U00000020\U0000ac00", {32, 44032}, {1, 2}},
-     {L"\U00000020\U00000308\U0000ac00", {32, 44032}, {2, 3}},
-     {L"\U00000020\U0000ac01", {32, 44033}, {1, 2}},
-     {L"\U00000020\U00000308\U0000ac01", {32, 44033}, {2, 3}},
-     {L"\U00000020\U00000903", {32}, {2}},
-     {L"\U00000020\U00000308\U00000903", {32}, {3}},
-     {L"\U00000020\U00000904", {32, 2308}, {1, 2}},
-     {L"\U00000020\U00000308\U00000904", {32, 2308}, {2, 3}},
-     {L"\U00000020\U00000d4e", {32, 3406}, {1, 2}},
-     {L"\U00000020\U00000308\U00000d4e", {32, 3406}, {2, 3}},
-     {L"\U00000020\U00000915", {32, 2325}, {1, 2}},
-     {L"\U00000020\U00000308\U00000915", {32, 2325}, {2, 3}},
-     {L"\U00000020\U0000231a", {32, 8986}, {1, 2}},
-     {L"\U00000020\U00000308\U0000231a", {32, 8986}, {2, 3}},
-     {L"\U00000020\U00000300", {32}, {2}},
-     {L"\U00000020\U00000308\U00000300", {32}, {3}},
-     {L"\U00000020\U00000900", {32}, {2}},
-     {L"\U00000020\U00000308\U00000900", {32}, {3}},
-     {L"\U00000020\U0000094d", {32}, {2}},
-     {L"\U00000020\U00000308\U0000094d", {32}, {3}},
-     {L"\U00000020\U0000200d", {32}, {2}},
-     {L"\U00000020\U00000308\U0000200d", {32}, {3}},
-     {L"\U00000020\U00000378", {32, 888}, {1, 2}},
-     {L"\U00000020\U00000308\U00000378", {32, 888}, {2, 3}},
-     {L"\U0000000d\U00000020", {13, 32}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000020", {13, 776, 32}, {1, 2, 3}},
+     {L"\U00000915\U0000094d\U00000924\U0000094d\U0000092f", {2325}, {5}},
+     {L"\U00000915\U0000094d\U00000061", {2325, 97}, {2, 3}},
+     {L"\U00000061\U0000094d\U00000924", {97}, {3}},
+     {L"\U0000003f\U0000094d\U00000924", {63}, {3}},
+     {L"\U00000915\U0000094d\U0000094d\U00000924", {2325}, {4}},
+     {L"\U00000ab8\U00000afb\U00000acd\U00000ab8\U00000afb", {2744}, {5}},
+     {L"\U00001019\U00001039\U00001018\U0000102c\U00001037", {4121, 4140}, {3, 5}},
+     {L"\U00001004\U0000103a\U00001039\U00001011\U00001039\U00001011", {4100}, {6}},
+     {L"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38", {6930, 6962, 6962, 6962}, {2, 5, 10, 14}},
+     {L"\U0000179f\U000017d2\U0000178f\U000017d2\U0000179a\U000017b8", {6047}, {6}},
+     {L"\U00001b26\U00001b17\U00001b44\U00001b13", {6950, 6935}, {1, 4}},
+     {L"\U00001b27\U00001b13\U00001b44\U00001b0b\U00001b0b\U00001b04", {6951, 6931, 6923}, {1, 4, 6}},
+     {L"\U00001795\U000017d2\U000017af\U00001798", {6037, 6040}, {3, 4}},
+     {L"\U000017a0\U000017d2\U000017ab\U00001791\U000017d0\U00001799", {6048, 6033, 6041}, {3, 5, 6}},
+     {L"\U00001b05\U00001b44\U00001b33\U00001b03", {6917}, {4}},
+     {L"\U00000cf1\U00000c95", {3313, 3221}, {1, 2}},
+     {L"\U00000cf2\U00000cab", {3314, 3243}, {1, 2}},
+     {L"\U00000cf1\U00000c95\U00000cbf", {3313, 3221}, {1, 3}},
+     {L"\U00001cf5\U00000995", {7413}, {2}},
+     {L"\U00001cf6\U000009aa", {7414}, {2}},
+     {L"\U00001cf5\U0000200c\U00000995", {7413, 2453}, {2, 3}},
+     {L"\U00001cf6\U0000200c\U000009aa", {7414, 2474}, {2, 3}},
+     {L"\U00011a3a\U00011a0b", {72250}, {4}}}};
+
+/// The data for UTF-8.
+///
+/// Note that most of the data for the UTF-16 and UTF-32 are identical. However
+/// since the size of the code units differ the breaks can contain different
+/// values.
+std::array<data<wchar_t>, 853> data_utf32 = {{
      {L"\U0000000d\U0000000d", {13, 13}, {1, 2}},
      {L"\U0000000d\U00000308\U0000000d", {13, 776, 13}, {1, 2, 3}},
      {L"\U0000000d\U0000000a", {13}, {2}},
      {L"\U0000000d\U00000308\U0000000a", {13, 776, 10}, {1, 2, 3}},
-     {L"\U0000000d\U00000001", {13, 1}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000001", {13, 776, 1}, {1, 2, 3}},
+     {L"\U0000000d\U00000000", {13, 0}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000000", {13, 776, 0}, {1, 2, 3}},
+     {L"\U0000000d\U0000094d", {13, 2381}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000094d", {13, 776}, {1, 3}},
+     {L"\U0000000d\U00000300", {13, 768}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000300", {13, 776}, {1, 3}},
      {L"\U0000000d\U0000200c", {13, 8204}, {1, 2}},
      {L"\U0000000d\U00000308\U0000200c", {13, 776}, {1, 3}},
+     {L"\U0000000d\U0000200d", {13, 8205}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000200d", {13, 776}, {1, 3}},
      {L"\U0000000d\U0001f1e6", {13, 127462}, {1, 2}},
      {L"\U0000000d\U00000308\U0001f1e6", {13, 776, 127462}, {1, 2, 3}},
-     {L"\U0000000d\U00000600", {13, 1536}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000600", {13, 776, 1536}, {1, 2, 3}},
-     {L"\U0000000d\U00000a03", {13, 2563}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000a03", {13, 776}, {1, 3}},
+     {L"\U0000000d\U000006dd", {13, 1757}, {1, 2}},
+     {L"\U0000000d\U00000308\U000006dd", {13, 776, 1757}, {1, 2, 3}},
+     {L"\U0000000d\U00000903", {13, 2307}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000903", {13, 776}, {1, 3}},
      {L"\U0000000d\U00001100", {13, 4352}, {1, 2}},
      {L"\U0000000d\U00000308\U00001100", {13, 776, 4352}, {1, 2, 3}},
      {L"\U0000000d\U00001160", {13, 4448}, {1, 2}},
@@ -2356,42 +1834,36 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U0000000d\U00000308\U0000ac00", {13, 776, 44032}, {1, 2, 3}},
      {L"\U0000000d\U0000ac01", {13, 44033}, {1, 2}},
      {L"\U0000000d\U00000308\U0000ac01", {13, 776, 44033}, {1, 2, 3}},
-     {L"\U0000000d\U00000903", {13, 2307}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000903", {13, 776}, {1, 3}},
-     {L"\U0000000d\U00000904", {13, 2308}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000904", {13, 776, 2308}, {1, 2, 3}},
-     {L"\U0000000d\U00000d4e", {13, 3406}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000d4e", {13, 776, 3406}, {1, 2, 3}},
+     {L"\U0000000d\U00001cf5", {13, 7413}, {1, 2}},
+     {L"\U0000000d\U00000308\U00001cf5", {13, 776, 7413}, {1, 2, 3}},
      {L"\U0000000d\U00000915", {13, 2325}, {1, 2}},
      {L"\U0000000d\U00000308\U00000915", {13, 776, 2325}, {1, 2, 3}},
-     {L"\U0000000d\U0000231a", {13, 8986}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000231a", {13, 776, 8986}, {1, 2, 3}},
-     {L"\U0000000d\U00000300", {13, 768}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000300", {13, 776}, {1, 3}},
-     {L"\U0000000d\U00000900", {13, 2304}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000900", {13, 776}, {1, 3}},
-     {L"\U0000000d\U0000094d", {13, 2381}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000094d", {13, 776}, {1, 3}},
-     {L"\U0000000d\U0000200d", {13, 8205}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000200d", {13, 776}, {1, 3}},
+     {L"\U0000000d\U000000a9", {13, 169}, {1, 2}},
+     {L"\U0000000d\U00000308\U000000a9", {13, 776, 169}, {1, 2, 3}},
+     {L"\U0000000d\U00000020", {13, 32}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000020", {13, 776, 32}, {1, 2, 3}},
      {L"\U0000000d\U00000378", {13, 888}, {1, 2}},
      {L"\U0000000d\U00000308\U00000378", {13, 776, 888}, {1, 2, 3}},
-     {L"\U0000000a\U00000020", {10, 32}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000020", {10, 776, 32}, {1, 2, 3}},
      {L"\U0000000a\U0000000d", {10, 13}, {1, 2}},
      {L"\U0000000a\U00000308\U0000000d", {10, 776, 13}, {1, 2, 3}},
      {L"\U0000000a\U0000000a", {10, 10}, {1, 2}},
      {L"\U0000000a\U00000308\U0000000a", {10, 776, 10}, {1, 2, 3}},
-     {L"\U0000000a\U00000001", {10, 1}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000001", {10, 776, 1}, {1, 2, 3}},
+     {L"\U0000000a\U00000000", {10, 0}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000000", {10, 776, 0}, {1, 2, 3}},
+     {L"\U0000000a\U0000094d", {10, 2381}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000094d", {10, 776}, {1, 3}},
+     {L"\U0000000a\U00000300", {10, 768}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000300", {10, 776}, {1, 3}},
      {L"\U0000000a\U0000200c", {10, 8204}, {1, 2}},
      {L"\U0000000a\U00000308\U0000200c", {10, 776}, {1, 3}},
+     {L"\U0000000a\U0000200d", {10, 8205}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000200d", {10, 776}, {1, 3}},
      {L"\U0000000a\U0001f1e6", {10, 127462}, {1, 2}},
      {L"\U0000000a\U00000308\U0001f1e6", {10, 776, 127462}, {1, 2, 3}},
-     {L"\U0000000a\U00000600", {10, 1536}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000600", {10, 776, 1536}, {1, 2, 3}},
-     {L"\U0000000a\U00000a03", {10, 2563}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000a03", {10, 776}, {1, 3}},
+     {L"\U0000000a\U000006dd", {10, 1757}, {1, 2}},
+     {L"\U0000000a\U00000308\U000006dd", {10, 776, 1757}, {1, 2, 3}},
+     {L"\U0000000a\U00000903", {10, 2307}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000903", {10, 776}, {1, 3}},
      {L"\U0000000a\U00001100", {10, 4352}, {1, 2}},
      {L"\U0000000a\U00000308\U00001100", {10, 776, 4352}, {1, 2, 3}},
      {L"\U0000000a\U00001160", {10, 4448}, {1, 2}},
@@ -2402,88 +1874,156 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U0000000a\U00000308\U0000ac00", {10, 776, 44032}, {1, 2, 3}},
      {L"\U0000000a\U0000ac01", {10, 44033}, {1, 2}},
      {L"\U0000000a\U00000308\U0000ac01", {10, 776, 44033}, {1, 2, 3}},
-     {L"\U0000000a\U00000903", {10, 2307}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000903", {10, 776}, {1, 3}},
-     {L"\U0000000a\U00000904", {10, 2308}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000904", {10, 776, 2308}, {1, 2, 3}},
-     {L"\U0000000a\U00000d4e", {10, 3406}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000d4e", {10, 776, 3406}, {1, 2, 3}},
+     {L"\U0000000a\U00001cf5", {10, 7413}, {1, 2}},
+     {L"\U0000000a\U00000308\U00001cf5", {10, 776, 7413}, {1, 2, 3}},
      {L"\U0000000a\U00000915", {10, 2325}, {1, 2}},
      {L"\U0000000a\U00000308\U00000915", {10, 776, 2325}, {1, 2, 3}},
-     {L"\U0000000a\U0000231a", {10, 8986}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000231a", {10, 776, 8986}, {1, 2, 3}},
-     {L"\U0000000a\U00000300", {10, 768}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000300", {10, 776}, {1, 3}},
-     {L"\U0000000a\U00000900", {10, 2304}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000900", {10, 776}, {1, 3}},
-     {L"\U0000000a\U0000094d", {10, 2381}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000094d", {10, 776}, {1, 3}},
-     {L"\U0000000a\U0000200d", {10, 8205}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000200d", {10, 776}, {1, 3}},
+     {L"\U0000000a\U000000a9", {10, 169}, {1, 2}},
+     {L"\U0000000a\U00000308\U000000a9", {10, 776, 169}, {1, 2, 3}},
+     {L"\U0000000a\U00000020", {10, 32}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000020", {10, 776, 32}, {1, 2, 3}},
      {L"\U0000000a\U00000378", {10, 888}, {1, 2}},
      {L"\U0000000a\U00000308\U00000378", {10, 776, 888}, {1, 2, 3}},
-     {L"\U00000001\U00000020", {1, 32}, {1, 2}},
-     {L"\U00000001\U00000308\U00000020", {1, 776, 32}, {1, 2, 3}},
-     {L"\U00000001\U0000000d", {1, 13}, {1, 2}},
-     {L"\U00000001\U00000308\U0000000d", {1, 776, 13}, {1, 2, 3}},
-     {L"\U00000001\U0000000a", {1, 10}, {1, 2}},
-     {L"\U00000001\U00000308\U0000000a", {1, 776, 10}, {1, 2, 3}},
-     {L"\U00000001\U00000001", {1, 1}, {1, 2}},
-     {L"\U00000001\U00000308\U00000001", {1, 776, 1}, {1, 2, 3}},
-     {L"\U00000001\U0000200c", {1, 8204}, {1, 2}},
-     {L"\U00000001\U00000308\U0000200c", {1, 776}, {1, 3}},
-     {L"\U00000001\U0001f1e6", {1, 127462}, {1, 2}},
-     {L"\U00000001\U00000308\U0001f1e6", {1, 776, 127462}, {1, 2, 3}},
-     {L"\U00000001\U00000600", {1, 1536}, {1, 2}},
-     {L"\U00000001\U00000308\U00000600", {1, 776, 1536}, {1, 2, 3}},
-     {L"\U00000001\U00000a03", {1, 2563}, {1, 2}},
-     {L"\U00000001\U00000308\U00000a03", {1, 776}, {1, 3}},
-     {L"\U00000001\U00001100", {1, 4352}, {1, 2}},
-     {L"\U00000001\U00000308\U00001100", {1, 776, 4352}, {1, 2, 3}},
-     {L"\U00000001\U00001160", {1, 4448}, {1, 2}},
-     {L"\U00000001\U00000308\U00001160", {1, 776, 4448}, {1, 2, 3}},
-     {L"\U00000001\U000011a8", {1, 4520}, {1, 2}},
-     {L"\U00000001\U00000308\U000011a8", {1, 776, 4520}, {1, 2, 3}},
-     {L"\U00000001\U0000ac00", {1, 44032}, {1, 2}},
-     {L"\U00000001\U00000308\U0000ac00", {1, 776, 44032}, {1, 2, 3}},
-     {L"\U00000001\U0000ac01", {1, 44033}, {1, 2}},
-     {L"\U00000001\U00000308\U0000ac01", {1, 776, 44033}, {1, 2, 3}},
-     {L"\U00000001\U00000903", {1, 2307}, {1, 2}},
-     {L"\U00000001\U00000308\U00000903", {1, 776}, {1, 3}},
-     {L"\U00000001\U00000904", {1, 2308}, {1, 2}},
-     {L"\U00000001\U00000308\U00000904", {1, 776, 2308}, {1, 2, 3}},
-     {L"\U00000001\U00000d4e", {1, 3406}, {1, 2}},
-     {L"\U00000001\U00000308\U00000d4e", {1, 776, 3406}, {1, 2, 3}},
-     {L"\U00000001\U00000915", {1, 2325}, {1, 2}},
-     {L"\U00000001\U00000308\U00000915", {1, 776, 2325}, {1, 2, 3}},
-     {L"\U00000001\U0000231a", {1, 8986}, {1, 2}},
-     {L"\U00000001\U00000308\U0000231a", {1, 776, 8986}, {1, 2, 3}},
-     {L"\U00000001\U00000300", {1, 768}, {1, 2}},
-     {L"\U00000001\U00000308\U00000300", {1, 776}, {1, 3}},
-     {L"\U00000001\U00000900", {1, 2304}, {1, 2}},
-     {L"\U00000001\U00000308\U00000900", {1, 776}, {1, 3}},
-     {L"\U00000001\U0000094d", {1, 2381}, {1, 2}},
-     {L"\U00000001\U00000308\U0000094d", {1, 776}, {1, 3}},
-     {L"\U00000001\U0000200d", {1, 8205}, {1, 2}},
-     {L"\U00000001\U00000308\U0000200d", {1, 776}, {1, 3}},
-     {L"\U00000001\U00000378", {1, 888}, {1, 2}},
-     {L"\U00000001\U00000308\U00000378", {1, 776, 888}, {1, 2, 3}},
-     {L"\U0000200c\U00000020", {8204, 32}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000020", {8204, 32}, {2, 3}},
+     {L"\U00000000\U0000000d", {0, 13}, {1, 2}},
+     {L"\U00000000\U00000308\U0000000d", {0, 776, 13}, {1, 2, 3}},
+     {L"\U00000000\U0000000a", {0, 10}, {1, 2}},
+     {L"\U00000000\U00000308\U0000000a", {0, 776, 10}, {1, 2, 3}},
+     {L"\U00000000\U00000000", {0, 0}, {1, 2}},
+     {L"\U00000000\U00000308\U00000000", {0, 776, 0}, {1, 2, 3}},
+     {L"\U00000000\U0000094d", {0, 2381}, {1, 2}},
+     {L"\U00000000\U00000308\U0000094d", {0, 776}, {1, 3}},
+     {L"\U00000000\U00000300", {0, 768}, {1, 2}},
+     {L"\U00000000\U00000308\U00000300", {0, 776}, {1, 3}},
+     {L"\U00000000\U0000200c", {0, 8204}, {1, 2}},
+     {L"\U00000000\U00000308\U0000200c", {0, 776}, {1, 3}},
+     {L"\U00000000\U0000200d", {0, 8205}, {1, 2}},
+     {L"\U00000000\U00000308\U0000200d", {0, 776}, {1, 3}},
+     {L"\U00000000\U0001f1e6", {0, 127462}, {1, 2}},
+     {L"\U00000000\U00000308\U0001f1e6", {0, 776, 127462}, {1, 2, 3}},
+     {L"\U00000000\U000006dd", {0, 1757}, {1, 2}},
+     {L"\U00000000\U00000308\U000006dd", {0, 776, 1757}, {1, 2, 3}},
+     {L"\U00000000\U00000903", {0, 2307}, {1, 2}},
+     {L"\U00000000\U00000308\U00000903", {0, 776}, {1, 3}},
+     {L"\U00000000\U00001100", {0, 4352}, {1, 2}},
+     {L"\U00000000\U00000308\U00001100", {0, 776, 4352}, {1, 2, 3}},
+     {L"\U00000000\U00001160", {0, 4448}, {1, 2}},
+     {L"\U00000000\U00000308\U00001160", {0, 776, 4448}, {1, 2, 3}},
+     {L"\U00000000\U000011a8", {0, 4520}, {1, 2}},
+     {L"\U00000000\U00000308\U000011a8", {0, 776, 4520}, {1, 2, 3}},
+     {L"\U00000000\U0000ac00", {0, 44032}, {1, 2}},
+     {L"\U00000000\U00000308\U0000ac00", {0, 776, 44032}, {1, 2, 3}},
+     {L"\U00000000\U0000ac01", {0, 44033}, {1, 2}},
+     {L"\U00000000\U00000308\U0000ac01", {0, 776, 44033}, {1, 2, 3}},
+     {L"\U00000000\U00001cf5", {0, 7413}, {1, 2}},
+     {L"\U00000000\U00000308\U00001cf5", {0, 776, 7413}, {1, 2, 3}},
+     {L"\U00000000\U00000915", {0, 2325}, {1, 2}},
+     {L"\U00000000\U00000308\U00000915", {0, 776, 2325}, {1, 2, 3}},
+     {L"\U00000000\U000000a9", {0, 169}, {1, 2}},
+     {L"\U00000000\U00000308\U000000a9", {0, 776, 169}, {1, 2, 3}},
+     {L"\U00000000\U00000020", {0, 32}, {1, 2}},
+     {L"\U00000000\U00000308\U00000020", {0, 776, 32}, {1, 2, 3}},
+     {L"\U00000000\U00000378", {0, 888}, {1, 2}},
+     {L"\U00000000\U00000308\U00000378", {0, 776, 888}, {1, 2, 3}},
+     {L"\U0000094d\U0000000d", {2381, 13}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000000d", {2381, 13}, {2, 3}},
+     {L"\U0000094d\U0000000a", {2381, 10}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000000a", {2381, 10}, {2, 3}},
+     {L"\U0000094d\U00000000", {2381, 0}, {1, 2}},
+     {L"\U0000094d\U00000308\U00000000", {2381, 0}, {2, 3}},
+     {L"\U0000094d\U0000094d", {2381}, {2}},
+     {L"\U0000094d\U00000308\U0000094d", {2381}, {3}},
+     {L"\U0000094d\U00000300", {2381}, {2}},
+     {L"\U0000094d\U00000308\U00000300", {2381}, {3}},
+     {L"\U0000094d\U0000200c", {2381}, {2}},
+     {L"\U0000094d\U00000308\U0000200c", {2381}, {3}},
+     {L"\U0000094d\U0000200d", {2381}, {2}},
+     {L"\U0000094d\U00000308\U0000200d", {2381}, {3}},
+     {L"\U0000094d\U0001f1e6", {2381, 127462}, {1, 2}},
+     {L"\U0000094d\U00000308\U0001f1e6", {2381, 127462}, {2, 3}},
+     {L"\U0000094d\U000006dd", {2381, 1757}, {1, 2}},
+     {L"\U0000094d\U00000308\U000006dd", {2381, 1757}, {2, 3}},
+     {L"\U0000094d\U00000903", {2381}, {2}},
+     {L"\U0000094d\U00000308\U00000903", {2381}, {3}},
+     {L"\U0000094d\U00001100", {2381, 4352}, {1, 2}},
+     {L"\U0000094d\U00000308\U00001100", {2381, 4352}, {2, 3}},
+     {L"\U0000094d\U00001160", {2381, 4448}, {1, 2}},
+     {L"\U0000094d\U00000308\U00001160", {2381, 4448}, {2, 3}},
+     {L"\U0000094d\U000011a8", {2381, 4520}, {1, 2}},
+     {L"\U0000094d\U00000308\U000011a8", {2381, 4520}, {2, 3}},
+     {L"\U0000094d\U0000ac00", {2381, 44032}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000ac00", {2381, 44032}, {2, 3}},
+     {L"\U0000094d\U0000ac01", {2381, 44033}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000ac01", {2381, 44033}, {2, 3}},
+     {L"\U0000094d\U00001cf5", {2381, 7413}, {1, 2}},
+     {L"\U0000094d\U00000308\U00001cf5", {2381, 7413}, {2, 3}},
+     {L"\U0000094d\U00000915", {2381}, {2}},
+     {L"\U0000094d\U00000308\U00000915", {2381}, {3}},
+     {L"\U0000094d\U000000a9", {2381, 169}, {1, 2}},
+     {L"\U0000094d\U00000308\U000000a9", {2381, 169}, {2, 3}},
+     {L"\U0000094d\U00000020", {2381, 32}, {1, 2}},
+     {L"\U0000094d\U00000308\U00000020", {2381, 32}, {2, 3}},
+     {L"\U0000094d\U00000378", {2381, 888}, {1, 2}},
+     {L"\U0000094d\U00000308\U00000378", {2381, 888}, {2, 3}},
+     {L"\U00000300\U0000000d", {768, 13}, {1, 2}},
+     {L"\U00000300\U00000308\U0000000d", {768, 13}, {2, 3}},
+     {L"\U00000300\U0000000a", {768, 10}, {1, 2}},
+     {L"\U00000300\U00000308\U0000000a", {768, 10}, {2, 3}},
+     {L"\U00000300\U00000000", {768, 0}, {1, 2}},
+     {L"\U00000300\U00000308\U00000000", {768, 0}, {2, 3}},
+     {L"\U00000300\U0000094d", {768}, {2}},
+     {L"\U00000300\U00000308\U0000094d", {768}, {3}},
+     {L"\U00000300\U00000300", {768}, {2}},
+     {L"\U00000300\U00000308\U00000300", {768}, {3}},
+     {L"\U00000300\U0000200c", {768}, {2}},
+     {L"\U00000300\U00000308\U0000200c", {768}, {3}},
+     {L"\U00000300\U0000200d", {768}, {2}},
+     {L"\U00000300\U00000308\U0000200d", {768}, {3}},
+     {L"\U00000300\U0001f1e6", {768, 127462}, {1, 2}},
+     {L"\U00000300\U00000308\U0001f1e6", {768, 127462}, {2, 3}},
+     {L"\U00000300\U000006dd", {768, 1757}, {1, 2}},
+     {L"\U00000300\U00000308\U000006dd", {768, 1757}, {2, 3}},
+     {L"\U00000300\U00000903", {768}, {2}},
+     {L"\U00000300\U00000308\U00000903", {768}, {3}},
+     {L"\U00000300\U00001100", {768, 4352}, {1, 2}},
+     {L"\U00000300\U00000308\U00001100", {768, 4352}, {2, 3}},
+     {L"\U00000300\U00001160", {768, 4448}, {1, 2}},
+     {L"\U00000300\U00000308\U00001160", {768, 4448}, {2, 3}},
+     {L"\U00000300\U000011a8", {768, 4520}, {1, 2}},
+     {L"\U00000300\U00000308\U000011a8", {768, 4520}, {2, 3}},
+     {L"\U00000300\U0000ac00", {768, 44032}, {1, 2}},
+     {L"\U00000300\U00000308\U0000ac00", {768, 44032}, {2, 3}},
+     {L"\U00000300\U0000ac01", {768, 44033}, {1, 2}},
+     {L"\U00000300\U00000308\U0000ac01", {768, 44033}, {2, 3}},
+     {L"\U00000300\U00001cf5", {768, 7413}, {1, 2}},
+     {L"\U00000300\U00000308\U00001cf5", {768, 7413}, {2, 3}},
+     {L"\U00000300\U00000915", {768, 2325}, {1, 2}},
+     {L"\U00000300\U00000308\U00000915", {768, 2325}, {2, 3}},
+     {L"\U00000300\U000000a9", {768, 169}, {1, 2}},
+     {L"\U00000300\U00000308\U000000a9", {768, 169}, {2, 3}},
+     {L"\U00000300\U00000020", {768, 32}, {1, 2}},
+     {L"\U00000300\U00000308\U00000020", {768, 32}, {2, 3}},
+     {L"\U00000300\U00000378", {768, 888}, {1, 2}},
+     {L"\U00000300\U00000308\U00000378", {768, 888}, {2, 3}},
      {L"\U0000200c\U0000000d", {8204, 13}, {1, 2}},
      {L"\U0000200c\U00000308\U0000000d", {8204, 13}, {2, 3}},
      {L"\U0000200c\U0000000a", {8204, 10}, {1, 2}},
      {L"\U0000200c\U00000308\U0000000a", {8204, 10}, {2, 3}},
-     {L"\U0000200c\U00000001", {8204, 1}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000001", {8204, 1}, {2, 3}},
+     {L"\U0000200c\U00000000", {8204, 0}, {1, 2}},
+     {L"\U0000200c\U00000308\U00000000", {8204, 0}, {2, 3}},
+     {L"\U0000200c\U0000094d", {8204}, {2}},
+     {L"\U0000200c\U00000308\U0000094d", {8204}, {3}},
+     {L"\U0000200c\U00000300", {8204}, {2}},
+     {L"\U0000200c\U00000308\U00000300", {8204}, {3}},
      {L"\U0000200c\U0000200c", {8204}, {2}},
      {L"\U0000200c\U00000308\U0000200c", {8204}, {3}},
+     {L"\U0000200c\U0000200d", {8204}, {2}},
+     {L"\U0000200c\U00000308\U0000200d", {8204}, {3}},
      {L"\U0000200c\U0001f1e6", {8204, 127462}, {1, 2}},
      {L"\U0000200c\U00000308\U0001f1e6", {8204, 127462}, {2, 3}},
-     {L"\U0000200c\U00000600", {8204, 1536}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000600", {8204, 1536}, {2, 3}},
-     {L"\U0000200c\U00000a03", {8204}, {2}},
-     {L"\U0000200c\U00000308\U00000a03", {8204}, {3}},
+     {L"\U0000200c\U000006dd", {8204, 1757}, {1, 2}},
+     {L"\U0000200c\U00000308\U000006dd", {8204, 1757}, {2, 3}},
+     {L"\U0000200c\U00000903", {8204}, {2}},
+     {L"\U0000200c\U00000308\U00000903", {8204}, {3}},
      {L"\U0000200c\U00001100", {8204, 4352}, {1, 2}},
      {L"\U0000200c\U00000308\U00001100", {8204, 4352}, {2, 3}},
      {L"\U0000200c\U00001160", {8204, 4448}, {1, 2}},
@@ -2494,42 +2034,76 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U0000200c\U00000308\U0000ac00", {8204, 44032}, {2, 3}},
      {L"\U0000200c\U0000ac01", {8204, 44033}, {1, 2}},
      {L"\U0000200c\U00000308\U0000ac01", {8204, 44033}, {2, 3}},
-     {L"\U0000200c\U00000903", {8204}, {2}},
-     {L"\U0000200c\U00000308\U00000903", {8204}, {3}},
-     {L"\U0000200c\U00000904", {8204, 2308}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000904", {8204, 2308}, {2, 3}},
-     {L"\U0000200c\U00000d4e", {8204, 3406}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000d4e", {8204, 3406}, {2, 3}},
+     {L"\U0000200c\U00001cf5", {8204, 7413}, {1, 2}},
+     {L"\U0000200c\U00000308\U00001cf5", {8204, 7413}, {2, 3}},
      {L"\U0000200c\U00000915", {8204, 2325}, {1, 2}},
      {L"\U0000200c\U00000308\U00000915", {8204, 2325}, {2, 3}},
-     {L"\U0000200c\U0000231a", {8204, 8986}, {1, 2}},
-     {L"\U0000200c\U00000308\U0000231a", {8204, 8986}, {2, 3}},
-     {L"\U0000200c\U00000300", {8204}, {2}},
-     {L"\U0000200c\U00000308\U00000300", {8204}, {3}},
-     {L"\U0000200c\U00000900", {8204}, {2}},
-     {L"\U0000200c\U00000308\U00000900", {8204}, {3}},
-     {L"\U0000200c\U0000094d", {8204}, {2}},
-     {L"\U0000200c\U00000308\U0000094d", {8204}, {3}},
-     {L"\U0000200c\U0000200d", {8204}, {2}},
-     {L"\U0000200c\U00000308\U0000200d", {8204}, {3}},
+     {L"\U0000200c\U000000a9", {8204, 169}, {1, 2}},
+     {L"\U0000200c\U00000308\U000000a9", {8204, 169}, {2, 3}},
+     {L"\U0000200c\U00000020", {8204, 32}, {1, 2}},
+     {L"\U0000200c\U00000308\U00000020", {8204, 32}, {2, 3}},
      {L"\U0000200c\U00000378", {8204, 888}, {1, 2}},
      {L"\U0000200c\U00000308\U00000378", {8204, 888}, {2, 3}},
-     {L"\U0001f1e6\U00000020", {127462, 32}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U00000020", {127462, 32}, {2, 3}},
+     {L"\U0000200d\U0000000d", {8205, 13}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000000d", {8205, 13}, {2, 3}},
+     {L"\U0000200d\U0000000a", {8205, 10}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000000a", {8205, 10}, {2, 3}},
+     {L"\U0000200d\U00000000", {8205, 0}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000000", {8205, 0}, {2, 3}},
+     {L"\U0000200d\U0000094d", {8205}, {2}},
+     {L"\U0000200d\U00000308\U0000094d", {8205}, {3}},
+     {L"\U0000200d\U00000300", {8205}, {2}},
+     {L"\U0000200d\U00000308\U00000300", {8205}, {3}},
+     {L"\U0000200d\U0000200c", {8205}, {2}},
+     {L"\U0000200d\U00000308\U0000200c", {8205}, {3}},
+     {L"\U0000200d\U0000200d", {8205}, {2}},
+     {L"\U0000200d\U00000308\U0000200d", {8205}, {3}},
+     {L"\U0000200d\U0001f1e6", {8205, 127462}, {1, 2}},
+     {L"\U0000200d\U00000308\U0001f1e6", {8205, 127462}, {2, 3}},
+     {L"\U0000200d\U000006dd", {8205, 1757}, {1, 2}},
+     {L"\U0000200d\U00000308\U000006dd", {8205, 1757}, {2, 3}},
+     {L"\U0000200d\U00000903", {8205}, {2}},
+     {L"\U0000200d\U00000308\U00000903", {8205}, {3}},
+     {L"\U0000200d\U00001100", {8205, 4352}, {1, 2}},
+     {L"\U0000200d\U00000308\U00001100", {8205, 4352}, {2, 3}},
+     {L"\U0000200d\U00001160", {8205, 4448}, {1, 2}},
+     {L"\U0000200d\U00000308\U00001160", {8205, 4448}, {2, 3}},
+     {L"\U0000200d\U000011a8", {8205, 4520}, {1, 2}},
+     {L"\U0000200d\U00000308\U000011a8", {8205, 4520}, {2, 3}},
+     {L"\U0000200d\U0000ac00", {8205, 44032}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000ac00", {8205, 44032}, {2, 3}},
+     {L"\U0000200d\U0000ac01", {8205, 44033}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000ac01", {8205, 44033}, {2, 3}},
+     {L"\U0000200d\U00001cf5", {8205, 7413}, {1, 2}},
+     {L"\U0000200d\U00000308\U00001cf5", {8205, 7413}, {2, 3}},
+     {L"\U0000200d\U00000915", {8205, 2325}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000915", {8205, 2325}, {2, 3}},
+     {L"\U0000200d\U000000a9", {8205, 169}, {1, 2}},
+     {L"\U0000200d\U00000308\U000000a9", {8205, 169}, {2, 3}},
+     {L"\U0000200d\U00000020", {8205, 32}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000020", {8205, 32}, {2, 3}},
+     {L"\U0000200d\U00000378", {8205, 888}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000378", {8205, 888}, {2, 3}},
      {L"\U0001f1e6\U0000000d", {127462, 13}, {1, 2}},
      {L"\U0001f1e6\U00000308\U0000000d", {127462, 13}, {2, 3}},
      {L"\U0001f1e6\U0000000a", {127462, 10}, {1, 2}},
      {L"\U0001f1e6\U00000308\U0000000a", {127462, 10}, {2, 3}},
-     {L"\U0001f1e6\U00000001", {127462, 1}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U00000001", {127462, 1}, {2, 3}},
+     {L"\U0001f1e6\U00000000", {127462, 0}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U00000000", {127462, 0}, {2, 3}},
+     {L"\U0001f1e6\U0000094d", {127462}, {2}},
+     {L"\U0001f1e6\U00000308\U0000094d", {127462}, {3}},
+     {L"\U0001f1e6\U00000300", {127462}, {2}},
+     {L"\U0001f1e6\U00000308\U00000300", {127462}, {3}},
      {L"\U0001f1e6\U0000200c", {127462}, {2}},
      {L"\U0001f1e6\U00000308\U0000200c", {127462}, {3}},
+     {L"\U0001f1e6\U0000200d", {127462}, {2}},
+     {L"\U0001f1e6\U00000308\U0000200d", {127462}, {3}},
      {L"\U0001f1e6\U0001f1e6", {127462}, {2}},
      {L"\U0001f1e6\U00000308\U0001f1e6", {127462, 127462}, {2, 3}},
-     {L"\U0001f1e6\U00000600", {127462, 1536}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U00000600", {127462, 1536}, {2, 3}},
-     {L"\U0001f1e6\U00000a03", {127462}, {2}},
-     {L"\U0001f1e6\U00000308\U00000a03", {127462}, {3}},
+     {L"\U0001f1e6\U000006dd", {127462, 1757}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U000006dd", {127462, 1757}, {2, 3}},
+     {L"\U0001f1e6\U00000903", {127462}, {2}},
+     {L"\U0001f1e6\U00000308\U00000903", {127462}, {3}},
      {L"\U0001f1e6\U00001100", {127462, 4352}, {1, 2}},
      {L"\U0001f1e6\U00000308\U00001100", {127462, 4352}, {2, 3}},
      {L"\U0001f1e6\U00001160", {127462, 4448}, {1, 2}},
@@ -2540,134 +2114,116 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U0001f1e6\U00000308\U0000ac00", {127462, 44032}, {2, 3}},
      {L"\U0001f1e6\U0000ac01", {127462, 44033}, {1, 2}},
      {L"\U0001f1e6\U00000308\U0000ac01", {127462, 44033}, {2, 3}},
-     {L"\U0001f1e6\U00000903", {127462}, {2}},
-     {L"\U0001f1e6\U00000308\U00000903", {127462}, {3}},
-     {L"\U0001f1e6\U00000904", {127462, 2308}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U00000904", {127462, 2308}, {2, 3}},
-     {L"\U0001f1e6\U00000d4e", {127462, 3406}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U00000d4e", {127462, 3406}, {2, 3}},
+     {L"\U0001f1e6\U00001cf5", {127462, 7413}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U00001cf5", {127462, 7413}, {2, 3}},
      {L"\U0001f1e6\U00000915", {127462, 2325}, {1, 2}},
      {L"\U0001f1e6\U00000308\U00000915", {127462, 2325}, {2, 3}},
-     {L"\U0001f1e6\U0000231a", {127462, 8986}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U0000231a", {127462, 8986}, {2, 3}},
-     {L"\U0001f1e6\U00000300", {127462}, {2}},
-     {L"\U0001f1e6\U00000308\U00000300", {127462}, {3}},
-     {L"\U0001f1e6\U00000900", {127462}, {2}},
-     {L"\U0001f1e6\U00000308\U00000900", {127462}, {3}},
-     {L"\U0001f1e6\U0000094d", {127462}, {2}},
-     {L"\U0001f1e6\U00000308\U0000094d", {127462}, {3}},
-     {L"\U0001f1e6\U0000200d", {127462}, {2}},
-     {L"\U0001f1e6\U00000308\U0000200d", {127462}, {3}},
+     {L"\U0001f1e6\U000000a9", {127462, 169}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U000000a9", {127462, 169}, {2, 3}},
+     {L"\U0001f1e6\U00000020", {127462, 32}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U00000020", {127462, 32}, {2, 3}},
      {L"\U0001f1e6\U00000378", {127462, 888}, {1, 2}},
      {L"\U0001f1e6\U00000308\U00000378", {127462, 888}, {2, 3}},
-     {L"\U00000600\U00000020", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000020", {1536, 32}, {2, 3}},
-     {L"\U00000600\U0000000d", {1536, 13}, {1, 2}},
-     {L"\U00000600\U00000308\U0000000d", {1536, 13}, {2, 3}},
-     {L"\U00000600\U0000000a", {1536, 10}, {1, 2}},
-     {L"\U00000600\U00000308\U0000000a", {1536, 10}, {2, 3}},
-     {L"\U00000600\U00000001", {1536, 1}, {1, 2}},
-     {L"\U00000600\U00000308\U00000001", {1536, 1}, {2, 3}},
-     {L"\U00000600\U0000200c", {1536}, {2}},
-     {L"\U00000600\U00000308\U0000200c", {1536}, {3}},
-     {L"\U00000600\U0001f1e6", {1536}, {2}},
-     {L"\U00000600\U00000308\U0001f1e6", {1536, 127462}, {2, 3}},
-     {L"\U00000600\U00000600", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000600", {1536, 1536}, {2, 3}},
-     {L"\U00000600\U00000a03", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000a03", {1536}, {3}},
-     {L"\U00000600\U00001100", {1536}, {2}},
-     {L"\U00000600\U00000308\U00001100", {1536, 4352}, {2, 3}},
-     {L"\U00000600\U00001160", {1536}, {2}},
-     {L"\U00000600\U00000308\U00001160", {1536, 4448}, {2, 3}},
-     {L"\U00000600\U000011a8", {1536}, {2}},
-     {L"\U00000600\U00000308\U000011a8", {1536, 4520}, {2, 3}},
-     {L"\U00000600\U0000ac00", {1536}, {2}},
-     {L"\U00000600\U00000308\U0000ac00", {1536, 44032}, {2, 3}},
-     {L"\U00000600\U0000ac01", {1536}, {2}},
-     {L"\U00000600\U00000308\U0000ac01", {1536, 44033}, {2, 3}},
-     {L"\U00000600\U00000903", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000903", {1536}, {3}},
-     {L"\U00000600\U00000904", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000904", {1536, 2308}, {2, 3}},
-     {L"\U00000600\U00000d4e", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000d4e", {1536, 3406}, {2, 3}},
-     {L"\U00000600\U00000915", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000915", {1536, 2325}, {2, 3}},
-     {L"\U00000600\U0000231a", {1536}, {2}},
-     {L"\U00000600\U00000308\U0000231a", {1536, 8986}, {2, 3}},
-     {L"\U00000600\U00000300", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000300", {1536}, {3}},
-     {L"\U00000600\U00000900", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000900", {1536}, {3}},
-     {L"\U00000600\U0000094d", {1536}, {2}},
-     {L"\U00000600\U00000308\U0000094d", {1536}, {3}},
-     {L"\U00000600\U0000200d", {1536}, {2}},
-     {L"\U00000600\U00000308\U0000200d", {1536}, {3}},
-     {L"\U00000600\U00000378", {1536}, {2}},
-     {L"\U00000600\U00000308\U00000378", {1536, 888}, {2, 3}},
-     {L"\U00000a03\U00000020", {2563, 32}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000020", {2563, 32}, {2, 3}},
-     {L"\U00000a03\U0000000d", {2563, 13}, {1, 2}},
-     {L"\U00000a03\U00000308\U0000000d", {2563, 13}, {2, 3}},
-     {L"\U00000a03\U0000000a", {2563, 10}, {1, 2}},
-     {L"\U00000a03\U00000308\U0000000a", {2563, 10}, {2, 3}},
-     {L"\U00000a03\U00000001", {2563, 1}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000001", {2563, 1}, {2, 3}},
-     {L"\U00000a03\U0000200c", {2563}, {2}},
-     {L"\U00000a03\U00000308\U0000200c", {2563}, {3}},
-     {L"\U00000a03\U0001f1e6", {2563, 127462}, {1, 2}},
-     {L"\U00000a03\U00000308\U0001f1e6", {2563, 127462}, {2, 3}},
-     {L"\U00000a03\U00000600", {2563, 1536}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000600", {2563, 1536}, {2, 3}},
-     {L"\U00000a03\U00000a03", {2563}, {2}},
-     {L"\U00000a03\U00000308\U00000a03", {2563}, {3}},
-     {L"\U00000a03\U00001100", {2563, 4352}, {1, 2}},
-     {L"\U00000a03\U00000308\U00001100", {2563, 4352}, {2, 3}},
-     {L"\U00000a03\U00001160", {2563, 4448}, {1, 2}},
-     {L"\U00000a03\U00000308\U00001160", {2563, 4448}, {2, 3}},
-     {L"\U00000a03\U000011a8", {2563, 4520}, {1, 2}},
-     {L"\U00000a03\U00000308\U000011a8", {2563, 4520}, {2, 3}},
-     {L"\U00000a03\U0000ac00", {2563, 44032}, {1, 2}},
-     {L"\U00000a03\U00000308\U0000ac00", {2563, 44032}, {2, 3}},
-     {L"\U00000a03\U0000ac01", {2563, 44033}, {1, 2}},
-     {L"\U00000a03\U00000308\U0000ac01", {2563, 44033}, {2, 3}},
-     {L"\U00000a03\U00000903", {2563}, {2}},
-     {L"\U00000a03\U00000308\U00000903", {2563}, {3}},
-     {L"\U00000a03\U00000904", {2563, 2308}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000904", {2563, 2308}, {2, 3}},
-     {L"\U00000a03\U00000d4e", {2563, 3406}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000d4e", {2563, 3406}, {2, 3}},
-     {L"\U00000a03\U00000915", {2563, 2325}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000915", {2563, 2325}, {2, 3}},
-     {L"\U00000a03\U0000231a", {2563, 8986}, {1, 2}},
-     {L"\U00000a03\U00000308\U0000231a", {2563, 8986}, {2, 3}},
-     {L"\U00000a03\U00000300", {2563}, {2}},
-     {L"\U00000a03\U00000308\U00000300", {2563}, {3}},
-     {L"\U00000a03\U00000900", {2563}, {2}},
-     {L"\U00000a03\U00000308\U00000900", {2563}, {3}},
-     {L"\U00000a03\U0000094d", {2563}, {2}},
-     {L"\U00000a03\U00000308\U0000094d", {2563}, {3}},
-     {L"\U00000a03\U0000200d", {2563}, {2}},
-     {L"\U00000a03\U00000308\U0000200d", {2563}, {3}},
-     {L"\U00000a03\U00000378", {2563, 888}, {1, 2}},
-     {L"\U00000a03\U00000308\U00000378", {2563, 888}, {2, 3}},
-     {L"\U00001100\U00000020", {4352, 32}, {1, 2}},
-     {L"\U00001100\U00000308\U00000020", {4352, 32}, {2, 3}},
+     {L"\U000006dd\U0000000d", {1757, 13}, {1, 2}},
+     {L"\U000006dd\U00000308\U0000000d", {1757, 13}, {2, 3}},
+     {L"\U000006dd\U0000000a", {1757, 10}, {1, 2}},
+     {L"\U000006dd\U00000308\U0000000a", {1757, 10}, {2, 3}},
+     {L"\U000006dd\U00000000", {1757, 0}, {1, 2}},
+     {L"\U000006dd\U00000308\U00000000", {1757, 0}, {2, 3}},
+     {L"\U000006dd\U0000094d", {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000094d", {1757}, {3}},
+     {L"\U000006dd\U00000300", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000300", {1757}, {3}},
+     {L"\U000006dd\U0000200c", {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000200c", {1757}, {3}},
+     {L"\U000006dd\U0000200d", {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000200d", {1757}, {3}},
+     {L"\U000006dd\U0001f1e6", {1757}, {2}},
+     {L"\U000006dd\U00000308\U0001f1e6", {1757, 127462}, {2, 3}},
+     {L"\U000006dd\U000006dd", {1757}, {2}},
+     {L"\U000006dd\U00000308\U000006dd", {1757, 1757}, {2, 3}},
+     {L"\U000006dd\U00000903", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000903", {1757}, {3}},
+     {L"\U000006dd\U00001100", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00001100", {1757, 4352}, {2, 3}},
+     {L"\U000006dd\U00001160", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00001160", {1757, 4448}, {2, 3}},
+     {L"\U000006dd\U000011a8", {1757}, {2}},
+     {L"\U000006dd\U00000308\U000011a8", {1757, 4520}, {2, 3}},
+     {L"\U000006dd\U0000ac00", {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000ac00", {1757, 44032}, {2, 3}},
+     {L"\U000006dd\U0000ac01", {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000ac01", {1757, 44033}, {2, 3}},
+     {L"\U000006dd\U00001cf5", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00001cf5", {1757, 7413}, {2, 3}},
+     {L"\U000006dd\U00000915", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000915", {1757, 2325}, {2, 3}},
+     {L"\U000006dd\U000000a9", {1757}, {2}},
+     {L"\U000006dd\U00000308\U000000a9", {1757, 169}, {2, 3}},
+     {L"\U000006dd\U00000020", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000020", {1757, 32}, {2, 3}},
+     {L"\U000006dd\U00000378", {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000378", {1757, 888}, {2, 3}},
+     {L"\U00000903\U0000000d", {2307, 13}, {1, 2}},
+     {L"\U00000903\U00000308\U0000000d", {2307, 13}, {2, 3}},
+     {L"\U00000903\U0000000a", {2307, 10}, {1, 2}},
+     {L"\U00000903\U00000308\U0000000a", {2307, 10}, {2, 3}},
+     {L"\U00000903\U00000000", {2307, 0}, {1, 2}},
+     {L"\U00000903\U00000308\U00000000", {2307, 0}, {2, 3}},
+     {L"\U00000903\U0000094d", {2307}, {2}},
+     {L"\U00000903\U00000308\U0000094d", {2307}, {3}},
+     {L"\U00000903\U00000300", {2307}, {2}},
+     {L"\U00000903\U00000308\U00000300", {2307}, {3}},
+     {L"\U00000903\U0000200c", {2307}, {2}},
+     {L"\U00000903\U00000308\U0000200c", {2307}, {3}},
+     {L"\U00000903\U0000200d", {2307}, {2}},
+     {L"\U00000903\U00000308\U0000200d", {2307}, {3}},
+     {L"\U00000903\U0001f1e6", {2307, 127462}, {1, 2}},
+     {L"\U00000903\U00000308\U0001f1e6", {2307, 127462}, {2, 3}},
+     {L"\U00000903\U000006dd", {2307, 1757}, {1, 2}},
+     {L"\U00000903\U00000308\U000006dd", {2307, 1757}, {2, 3}},
+     {L"\U00000903\U00000903", {2307}, {2}},
+     {L"\U00000903\U00000308\U00000903", {2307}, {3}},
+     {L"\U00000903\U00001100", {2307, 4352}, {1, 2}},
+     {L"\U00000903\U00000308\U00001100", {2307, 4352}, {2, 3}},
+     {L"\U00000903\U00001160", {2307, 4448}, {1, 2}},
+     {L"\U00000903\U00000308\U00001160", {2307, 4448}, {2, 3}},
+     {L"\U00000903\U000011a8", {2307, 4520}, {1, 2}},
+     {L"\U00000903\U00000308\U000011a8", {2307, 4520}, {2, 3}},
+     {L"\U00000903\U0000ac00", {2307, 44032}, {1, 2}},
+     {L"\U00000903\U00000308\U0000ac00", {2307, 44032}, {2, 3}},
+     {L"\U00000903\U0000ac01", {2307, 44033}, {1, 2}},
+     {L"\U00000903\U00000308\U0000ac01", {2307, 44033}, {2, 3}},
+     {L"\U00000903\U00001cf5", {2307, 7413}, {1, 2}},
+     {L"\U00000903\U00000308\U00001cf5", {2307, 7413}, {2, 3}},
+     {L"\U00000903\U00000915", {2307, 2325}, {1, 2}},
+     {L"\U00000903\U00000308\U00000915", {2307, 2325}, {2, 3}},
+     {L"\U00000903\U000000a9", {2307, 169}, {1, 2}},
+     {L"\U00000903\U00000308\U000000a9", {2307, 169}, {2, 3}},
+     {L"\U00000903\U00000020", {2307, 32}, {1, 2}},
+     {L"\U00000903\U00000308\U00000020", {2307, 32}, {2, 3}},
+     {L"\U00000903\U00000378", {2307, 888}, {1, 2}},
+     {L"\U00000903\U00000308\U00000378", {2307, 888}, {2, 3}},
      {L"\U00001100\U0000000d", {4352, 13}, {1, 2}},
      {L"\U00001100\U00000308\U0000000d", {4352, 13}, {2, 3}},
      {L"\U00001100\U0000000a", {4352, 10}, {1, 2}},
      {L"\U00001100\U00000308\U0000000a", {4352, 10}, {2, 3}},
-     {L"\U00001100\U00000001", {4352, 1}, {1, 2}},
-     {L"\U00001100\U00000308\U00000001", {4352, 1}, {2, 3}},
+     {L"\U00001100\U00000000", {4352, 0}, {1, 2}},
+     {L"\U00001100\U00000308\U00000000", {4352, 0}, {2, 3}},
+     {L"\U00001100\U0000094d", {4352}, {2}},
+     {L"\U00001100\U00000308\U0000094d", {4352}, {3}},
+     {L"\U00001100\U00000300", {4352}, {2}},
+     {L"\U00001100\U00000308\U00000300", {4352}, {3}},
      {L"\U00001100\U0000200c", {4352}, {2}},
      {L"\U00001100\U00000308\U0000200c", {4352}, {3}},
+     {L"\U00001100\U0000200d", {4352}, {2}},
+     {L"\U00001100\U00000308\U0000200d", {4352}, {3}},
      {L"\U00001100\U0001f1e6", {4352, 127462}, {1, 2}},
      {L"\U00001100\U00000308\U0001f1e6", {4352, 127462}, {2, 3}},
-     {L"\U00001100\U00000600", {4352, 1536}, {1, 2}},
-     {L"\U00001100\U00000308\U00000600", {4352, 1536}, {2, 3}},
-     {L"\U00001100\U00000a03", {4352}, {2}},
-     {L"\U00001100\U00000308\U00000a03", {4352}, {3}},
+     {L"\U00001100\U000006dd", {4352, 1757}, {1, 2}},
+     {L"\U00001100\U00000308\U000006dd", {4352, 1757}, {2, 3}},
+     {L"\U00001100\U00000903", {4352}, {2}},
+     {L"\U00001100\U00000308\U00000903", {4352}, {3}},
      {L"\U00001100\U00001100", {4352}, {2}},
      {L"\U00001100\U00000308\U00001100", {4352, 4352}, {2, 3}},
      {L"\U00001100\U00001160", {4352}, {2}},
@@ -2678,42 +2234,36 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U00001100\U00000308\U0000ac00", {4352, 44032}, {2, 3}},
      {L"\U00001100\U0000ac01", {4352}, {2}},
      {L"\U00001100\U00000308\U0000ac01", {4352, 44033}, {2, 3}},
-     {L"\U00001100\U00000903", {4352}, {2}},
-     {L"\U00001100\U00000308\U00000903", {4352}, {3}},
-     {L"\U00001100\U00000904", {4352, 2308}, {1, 2}},
-     {L"\U00001100\U00000308\U00000904", {4352, 2308}, {2, 3}},
-     {L"\U00001100\U00000d4e", {4352, 3406}, {1, 2}},
-     {L"\U00001100\U00000308\U00000d4e", {4352, 3406}, {2, 3}},
+     {L"\U00001100\U00001cf5", {4352, 7413}, {1, 2}},
+     {L"\U00001100\U00000308\U00001cf5", {4352, 7413}, {2, 3}},
      {L"\U00001100\U00000915", {4352, 2325}, {1, 2}},
      {L"\U00001100\U00000308\U00000915", {4352, 2325}, {2, 3}},
-     {L"\U00001100\U0000231a", {4352, 8986}, {1, 2}},
-     {L"\U00001100\U00000308\U0000231a", {4352, 8986}, {2, 3}},
-     {L"\U00001100\U00000300", {4352}, {2}},
-     {L"\U00001100\U00000308\U00000300", {4352}, {3}},
-     {L"\U00001100\U00000900", {4352}, {2}},
-     {L"\U00001100\U00000308\U00000900", {4352}, {3}},
-     {L"\U00001100\U0000094d", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000094d", {4352}, {3}},
-     {L"\U00001100\U0000200d", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000200d", {4352}, {3}},
+     {L"\U00001100\U000000a9", {4352, 169}, {1, 2}},
+     {L"\U00001100\U00000308\U000000a9", {4352, 169}, {2, 3}},
+     {L"\U00001100\U00000020", {4352, 32}, {1, 2}},
+     {L"\U00001100\U00000308\U00000020", {4352, 32}, {2, 3}},
      {L"\U00001100\U00000378", {4352, 888}, {1, 2}},
      {L"\U00001100\U00000308\U00000378", {4352, 888}, {2, 3}},
-     {L"\U00001160\U00000020", {4448, 32}, {1, 2}},
-     {L"\U00001160\U00000308\U00000020", {4448, 32}, {2, 3}},
      {L"\U00001160\U0000000d", {4448, 13}, {1, 2}},
      {L"\U00001160\U00000308\U0000000d", {4448, 13}, {2, 3}},
      {L"\U00001160\U0000000a", {4448, 10}, {1, 2}},
      {L"\U00001160\U00000308\U0000000a", {4448, 10}, {2, 3}},
-     {L"\U00001160\U00000001", {4448, 1}, {1, 2}},
-     {L"\U00001160\U00000308\U00000001", {4448, 1}, {2, 3}},
+     {L"\U00001160\U00000000", {4448, 0}, {1, 2}},
+     {L"\U00001160\U00000308\U00000000", {4448, 0}, {2, 3}},
+     {L"\U00001160\U0000094d", {4448}, {2}},
+     {L"\U00001160\U00000308\U0000094d", {4448}, {3}},
+     {L"\U00001160\U00000300", {4448}, {2}},
+     {L"\U00001160\U00000308\U00000300", {4448}, {3}},
      {L"\U00001160\U0000200c", {4448}, {2}},
      {L"\U00001160\U00000308\U0000200c", {4448}, {3}},
+     {L"\U00001160\U0000200d", {4448}, {2}},
+     {L"\U00001160\U00000308\U0000200d", {4448}, {3}},
      {L"\U00001160\U0001f1e6", {4448, 127462}, {1, 2}},
      {L"\U00001160\U00000308\U0001f1e6", {4448, 127462}, {2, 3}},
-     {L"\U00001160\U00000600", {4448, 1536}, {1, 2}},
-     {L"\U00001160\U00000308\U00000600", {4448, 1536}, {2, 3}},
-     {L"\U00001160\U00000a03", {4448}, {2}},
-     {L"\U00001160\U00000308\U00000a03", {4448}, {3}},
+     {L"\U00001160\U000006dd", {4448, 1757}, {1, 2}},
+     {L"\U00001160\U00000308\U000006dd", {4448, 1757}, {2, 3}},
+     {L"\U00001160\U00000903", {4448}, {2}},
+     {L"\U00001160\U00000308\U00000903", {4448}, {3}},
      {L"\U00001160\U00001100", {4448, 4352}, {1, 2}},
      {L"\U00001160\U00000308\U00001100", {4448, 4352}, {2, 3}},
      {L"\U00001160\U00001160", {4448}, {2}},
@@ -2724,42 +2274,36 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U00001160\U00000308\U0000ac00", {4448, 44032}, {2, 3}},
      {L"\U00001160\U0000ac01", {4448, 44033}, {1, 2}},
      {L"\U00001160\U00000308\U0000ac01", {4448, 44033}, {2, 3}},
-     {L"\U00001160\U00000903", {4448}, {2}},
-     {L"\U00001160\U00000308\U00000903", {4448}, {3}},
-     {L"\U00001160\U00000904", {4448, 2308}, {1, 2}},
-     {L"\U00001160\U00000308\U00000904", {4448, 2308}, {2, 3}},
-     {L"\U00001160\U00000d4e", {4448, 3406}, {1, 2}},
-     {L"\U00001160\U00000308\U00000d4e", {4448, 3406}, {2, 3}},
+     {L"\U00001160\U00001cf5", {4448, 7413}, {1, 2}},
+     {L"\U00001160\U00000308\U00001cf5", {4448, 7413}, {2, 3}},
      {L"\U00001160\U00000915", {4448, 2325}, {1, 2}},
      {L"\U00001160\U00000308\U00000915", {4448, 2325}, {2, 3}},
-     {L"\U00001160\U0000231a", {4448, 8986}, {1, 2}},
-     {L"\U00001160\U00000308\U0000231a", {4448, 8986}, {2, 3}},
-     {L"\U00001160\U00000300", {4448}, {2}},
-     {L"\U00001160\U00000308\U00000300", {4448}, {3}},
-     {L"\U00001160\U00000900", {4448}, {2}},
-     {L"\U00001160\U00000308\U00000900", {4448}, {3}},
-     {L"\U00001160\U0000094d", {4448}, {2}},
-     {L"\U00001160\U00000308\U0000094d", {4448}, {3}},
-     {L"\U00001160\U0000200d", {4448}, {2}},
-     {L"\U00001160\U00000308\U0000200d", {4448}, {3}},
+     {L"\U00001160\U000000a9", {4448, 169}, {1, 2}},
+     {L"\U00001160\U00000308\U000000a9", {4448, 169}, {2, 3}},
+     {L"\U00001160\U00000020", {4448, 32}, {1, 2}},
+     {L"\U00001160\U00000308\U00000020", {4448, 32}, {2, 3}},
      {L"\U00001160\U00000378", {4448, 888}, {1, 2}},
      {L"\U00001160\U00000308\U00000378", {4448, 888}, {2, 3}},
-     {L"\U000011a8\U00000020", {4520, 32}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000020", {4520, 32}, {2, 3}},
      {L"\U000011a8\U0000000d", {4520, 13}, {1, 2}},
      {L"\U000011a8\U00000308\U0000000d", {4520, 13}, {2, 3}},
      {L"\U000011a8\U0000000a", {4520, 10}, {1, 2}},
      {L"\U000011a8\U00000308\U0000000a", {4520, 10}, {2, 3}},
-     {L"\U000011a8\U00000001", {4520, 1}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000001", {4520, 1}, {2, 3}},
+     {L"\U000011a8\U00000000", {4520, 0}, {1, 2}},
+     {L"\U000011a8\U00000308\U00000000", {4520, 0}, {2, 3}},
+     {L"\U000011a8\U0000094d", {4520}, {2}},
+     {L"\U000011a8\U00000308\U0000094d", {4520}, {3}},
+     {L"\U000011a8\U00000300", {4520}, {2}},
+     {L"\U000011a8\U00000308\U00000300", {4520}, {3}},
      {L"\U000011a8\U0000200c", {4520}, {2}},
      {L"\U000011a8\U00000308\U0000200c", {4520}, {3}},
+     {L"\U000011a8\U0000200d", {4520}, {2}},
+     {L"\U000011a8\U00000308\U0000200d", {4520}, {3}},
      {L"\U000011a8\U0001f1e6", {4520, 127462}, {1, 2}},
      {L"\U000011a8\U00000308\U0001f1e6", {4520, 127462}, {2, 3}},
-     {L"\U000011a8\U00000600", {4520, 1536}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000600", {4520, 1536}, {2, 3}},
-     {L"\U000011a8\U00000a03", {4520}, {2}},
-     {L"\U000011a8\U00000308\U00000a03", {4520}, {3}},
+     {L"\U000011a8\U000006dd", {4520, 1757}, {1, 2}},
+     {L"\U000011a8\U00000308\U000006dd", {4520, 1757}, {2, 3}},
+     {L"\U000011a8\U00000903", {4520}, {2}},
+     {L"\U000011a8\U00000308\U00000903", {4520}, {3}},
      {L"\U000011a8\U00001100", {4520, 4352}, {1, 2}},
      {L"\U000011a8\U00000308\U00001100", {4520, 4352}, {2, 3}},
      {L"\U000011a8\U00001160", {4520, 4448}, {1, 2}},
@@ -2770,42 +2314,36 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U000011a8\U00000308\U0000ac00", {4520, 44032}, {2, 3}},
      {L"\U000011a8\U0000ac01", {4520, 44033}, {1, 2}},
      {L"\U000011a8\U00000308\U0000ac01", {4520, 44033}, {2, 3}},
-     {L"\U000011a8\U00000903", {4520}, {2}},
-     {L"\U000011a8\U00000308\U00000903", {4520}, {3}},
-     {L"\U000011a8\U00000904", {4520, 2308}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000904", {4520, 2308}, {2, 3}},
-     {L"\U000011a8\U00000d4e", {4520, 3406}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000d4e", {4520, 3406}, {2, 3}},
+     {L"\U000011a8\U00001cf5", {4520, 7413}, {1, 2}},
+     {L"\U000011a8\U00000308\U00001cf5", {4520, 7413}, {2, 3}},
      {L"\U000011a8\U00000915", {4520, 2325}, {1, 2}},
      {L"\U000011a8\U00000308\U00000915", {4520, 2325}, {2, 3}},
-     {L"\U000011a8\U0000231a", {4520, 8986}, {1, 2}},
-     {L"\U000011a8\U00000308\U0000231a", {4520, 8986}, {2, 3}},
-     {L"\U000011a8\U00000300", {4520}, {2}},
-     {L"\U000011a8\U00000308\U00000300", {4520}, {3}},
-     {L"\U000011a8\U00000900", {4520}, {2}},
-     {L"\U000011a8\U00000308\U00000900", {4520}, {3}},
-     {L"\U000011a8\U0000094d", {4520}, {2}},
-     {L"\U000011a8\U00000308\U0000094d", {4520}, {3}},
-     {L"\U000011a8\U0000200d", {4520}, {2}},
-     {L"\U000011a8\U00000308\U0000200d", {4520}, {3}},
+     {L"\U000011a8\U000000a9", {4520, 169}, {1, 2}},
+     {L"\U000011a8\U00000308\U000000a9", {4520, 169}, {2, 3}},
+     {L"\U000011a8\U00000020", {4520, 32}, {1, 2}},
+     {L"\U000011a8\U00000308\U00000020", {4520, 32}, {2, 3}},
      {L"\U000011a8\U00000378", {4520, 888}, {1, 2}},
      {L"\U000011a8\U00000308\U00000378", {4520, 888}, {2, 3}},
-     {L"\U0000ac00\U00000020", {44032, 32}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000020", {44032, 32}, {2, 3}},
      {L"\U0000ac00\U0000000d", {44032, 13}, {1, 2}},
      {L"\U0000ac00\U00000308\U0000000d", {44032, 13}, {2, 3}},
      {L"\U0000ac00\U0000000a", {44032, 10}, {1, 2}},
      {L"\U0000ac00\U00000308\U0000000a", {44032, 10}, {2, 3}},
-     {L"\U0000ac00\U00000001", {44032, 1}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000001", {44032, 1}, {2, 3}},
+     {L"\U0000ac00\U00000000", {44032, 0}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00000000", {44032, 0}, {2, 3}},
+     {L"\U0000ac00\U0000094d", {44032}, {2}},
+     {L"\U0000ac00\U00000308\U0000094d", {44032}, {3}},
+     {L"\U0000ac00\U00000300", {44032}, {2}},
+     {L"\U0000ac00\U00000308\U00000300", {44032}, {3}},
      {L"\U0000ac00\U0000200c", {44032}, {2}},
      {L"\U0000ac00\U00000308\U0000200c", {44032}, {3}},
+     {L"\U0000ac00\U0000200d", {44032}, {2}},
+     {L"\U0000ac00\U00000308\U0000200d", {44032}, {3}},
      {L"\U0000ac00\U0001f1e6", {44032, 127462}, {1, 2}},
      {L"\U0000ac00\U00000308\U0001f1e6", {44032, 127462}, {2, 3}},
-     {L"\U0000ac00\U00000600", {44032, 1536}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000600", {44032, 1536}, {2, 3}},
-     {L"\U0000ac00\U00000a03", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00000a03", {44032}, {3}},
+     {L"\U0000ac00\U000006dd", {44032, 1757}, {1, 2}},
+     {L"\U0000ac00\U00000308\U000006dd", {44032, 1757}, {2, 3}},
+     {L"\U0000ac00\U00000903", {44032}, {2}},
+     {L"\U0000ac00\U00000308\U00000903", {44032}, {3}},
      {L"\U0000ac00\U00001100", {44032, 4352}, {1, 2}},
      {L"\U0000ac00\U00000308\U00001100", {44032, 4352}, {2, 3}},
      {L"\U0000ac00\U00001160", {44032}, {2}},
@@ -2816,42 +2354,36 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U0000ac00\U00000308\U0000ac00", {44032, 44032}, {2, 3}},
      {L"\U0000ac00\U0000ac01", {44032, 44033}, {1, 2}},
      {L"\U0000ac00\U00000308\U0000ac01", {44032, 44033}, {2, 3}},
-     {L"\U0000ac00\U00000903", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00000903", {44032}, {3}},
-     {L"\U0000ac00\U00000904", {44032, 2308}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000904", {44032, 2308}, {2, 3}},
-     {L"\U0000ac00\U00000d4e", {44032, 3406}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000d4e", {44032, 3406}, {2, 3}},
+     {L"\U0000ac00\U00001cf5", {44032, 7413}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00001cf5", {44032, 7413}, {2, 3}},
      {L"\U0000ac00\U00000915", {44032, 2325}, {1, 2}},
      {L"\U0000ac00\U00000308\U00000915", {44032, 2325}, {2, 3}},
-     {L"\U0000ac00\U0000231a", {44032, 8986}, {1, 2}},
-     {L"\U0000ac00\U00000308\U0000231a", {44032, 8986}, {2, 3}},
-     {L"\U0000ac00\U00000300", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00000300", {44032}, {3}},
-     {L"\U0000ac00\U00000900", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00000900", {44032}, {3}},
-     {L"\U0000ac00\U0000094d", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U0000094d", {44032}, {3}},
-     {L"\U0000ac00\U0000200d", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U0000200d", {44032}, {3}},
+     {L"\U0000ac00\U000000a9", {44032, 169}, {1, 2}},
+     {L"\U0000ac00\U00000308\U000000a9", {44032, 169}, {2, 3}},
+     {L"\U0000ac00\U00000020", {44032, 32}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00000020", {44032, 32}, {2, 3}},
      {L"\U0000ac00\U00000378", {44032, 888}, {1, 2}},
      {L"\U0000ac00\U00000308\U00000378", {44032, 888}, {2, 3}},
-     {L"\U0000ac01\U00000020", {44033, 32}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000020", {44033, 32}, {2, 3}},
      {L"\U0000ac01\U0000000d", {44033, 13}, {1, 2}},
      {L"\U0000ac01\U00000308\U0000000d", {44033, 13}, {2, 3}},
      {L"\U0000ac01\U0000000a", {44033, 10}, {1, 2}},
      {L"\U0000ac01\U00000308\U0000000a", {44033, 10}, {2, 3}},
-     {L"\U0000ac01\U00000001", {44033, 1}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000001", {44033, 1}, {2, 3}},
+     {L"\U0000ac01\U00000000", {44033, 0}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00000000", {44033, 0}, {2, 3}},
+     {L"\U0000ac01\U0000094d", {44033}, {2}},
+     {L"\U0000ac01\U00000308\U0000094d", {44033}, {3}},
+     {L"\U0000ac01\U00000300", {44033}, {2}},
+     {L"\U0000ac01\U00000308\U00000300", {44033}, {3}},
      {L"\U0000ac01\U0000200c", {44033}, {2}},
      {L"\U0000ac01\U00000308\U0000200c", {44033}, {3}},
+     {L"\U0000ac01\U0000200d", {44033}, {2}},
+     {L"\U0000ac01\U00000308\U0000200d", {44033}, {3}},
      {L"\U0000ac01\U0001f1e6", {44033, 127462}, {1, 2}},
      {L"\U0000ac01\U00000308\U0001f1e6", {44033, 127462}, {2, 3}},
-     {L"\U0000ac01\U00000600", {44033, 1536}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000600", {44033, 1536}, {2, 3}},
-     {L"\U0000ac01\U00000a03", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U00000a03", {44033}, {3}},
+     {L"\U0000ac01\U000006dd", {44033, 1757}, {1, 2}},
+     {L"\U0000ac01\U00000308\U000006dd", {44033, 1757}, {2, 3}},
+     {L"\U0000ac01\U00000903", {44033}, {2}},
+     {L"\U0000ac01\U00000308\U00000903", {44033}, {3}},
      {L"\U0000ac01\U00001100", {44033, 4352}, {1, 2}},
      {L"\U0000ac01\U00000308\U00001100", {44033, 4352}, {2, 3}},
      {L"\U0000ac01\U00001160", {44033, 4448}, {1, 2}},
@@ -2862,180 +2394,76 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U0000ac01\U00000308\U0000ac00", {44033, 44032}, {2, 3}},
      {L"\U0000ac01\U0000ac01", {44033, 44033}, {1, 2}},
      {L"\U0000ac01\U00000308\U0000ac01", {44033, 44033}, {2, 3}},
-     {L"\U0000ac01\U00000903", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U00000903", {44033}, {3}},
-     {L"\U0000ac01\U00000904", {44033, 2308}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000904", {44033, 2308}, {2, 3}},
-     {L"\U0000ac01\U00000d4e", {44033, 3406}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000d4e", {44033, 3406}, {2, 3}},
+     {L"\U0000ac01\U00001cf5", {44033, 7413}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00001cf5", {44033, 7413}, {2, 3}},
      {L"\U0000ac01\U00000915", {44033, 2325}, {1, 2}},
      {L"\U0000ac01\U00000308\U00000915", {44033, 2325}, {2, 3}},
-     {L"\U0000ac01\U0000231a", {44033, 8986}, {1, 2}},
-     {L"\U0000ac01\U00000308\U0000231a", {44033, 8986}, {2, 3}},
-     {L"\U0000ac01\U00000300", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U00000300", {44033}, {3}},
-     {L"\U0000ac01\U00000900", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U00000900", {44033}, {3}},
-     {L"\U0000ac01\U0000094d", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U0000094d", {44033}, {3}},
-     {L"\U0000ac01\U0000200d", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U0000200d", {44033}, {3}},
+     {L"\U0000ac01\U000000a9", {44033, 169}, {1, 2}},
+     {L"\U0000ac01\U00000308\U000000a9", {44033, 169}, {2, 3}},
+     {L"\U0000ac01\U00000020", {44033, 32}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00000020", {44033, 32}, {2, 3}},
      {L"\U0000ac01\U00000378", {44033, 888}, {1, 2}},
      {L"\U0000ac01\U00000308\U00000378", {44033, 888}, {2, 3}},
-     {L"\U00000903\U00000020", {2307, 32}, {1, 2}},
-     {L"\U00000903\U00000308\U00000020", {2307, 32}, {2, 3}},
-     {L"\U00000903\U0000000d", {2307, 13}, {1, 2}},
-     {L"\U00000903\U00000308\U0000000d", {2307, 13}, {2, 3}},
-     {L"\U00000903\U0000000a", {2307, 10}, {1, 2}},
-     {L"\U00000903\U00000308\U0000000a", {2307, 10}, {2, 3}},
-     {L"\U00000903\U00000001", {2307, 1}, {1, 2}},
-     {L"\U00000903\U00000308\U00000001", {2307, 1}, {2, 3}},
-     {L"\U00000903\U0000200c", {2307}, {2}},
-     {L"\U00000903\U00000308\U0000200c", {2307}, {3}},
-     {L"\U00000903\U0001f1e6", {2307, 127462}, {1, 2}},
-     {L"\U00000903\U00000308\U0001f1e6", {2307, 127462}, {2, 3}},
-     {L"\U00000903\U00000600", {2307, 1536}, {1, 2}},
-     {L"\U00000903\U00000308\U00000600", {2307, 1536}, {2, 3}},
-     {L"\U00000903\U00000a03", {2307}, {2}},
-     {L"\U00000903\U00000308\U00000a03", {2307}, {3}},
-     {L"\U00000903\U00001100", {2307, 4352}, {1, 2}},
-     {L"\U00000903\U00000308\U00001100", {2307, 4352}, {2, 3}},
-     {L"\U00000903\U00001160", {2307, 4448}, {1, 2}},
-     {L"\U00000903\U00000308\U00001160", {2307, 4448}, {2, 3}},
-     {L"\U00000903\U000011a8", {2307, 4520}, {1, 2}},
-     {L"\U00000903\U00000308\U000011a8", {2307, 4520}, {2, 3}},
-     {L"\U00000903\U0000ac00", {2307, 44032}, {1, 2}},
-     {L"\U00000903\U00000308\U0000ac00", {2307, 44032}, {2, 3}},
-     {L"\U00000903\U0000ac01", {2307, 44033}, {1, 2}},
-     {L"\U00000903\U00000308\U0000ac01", {2307, 44033}, {2, 3}},
-     {L"\U00000903\U00000903", {2307}, {2}},
-     {L"\U00000903\U00000308\U00000903", {2307}, {3}},
-     {L"\U00000903\U00000904", {2307, 2308}, {1, 2}},
-     {L"\U00000903\U00000308\U00000904", {2307, 2308}, {2, 3}},
-     {L"\U00000903\U00000d4e", {2307, 3406}, {1, 2}},
-     {L"\U00000903\U00000308\U00000d4e", {2307, 3406}, {2, 3}},
-     {L"\U00000903\U00000915", {2307, 2325}, {1, 2}},
-     {L"\U00000903\U00000308\U00000915", {2307, 2325}, {2, 3}},
-     {L"\U00000903\U0000231a", {2307, 8986}, {1, 2}},
-     {L"\U00000903\U00000308\U0000231a", {2307, 8986}, {2, 3}},
-     {L"\U00000903\U00000300", {2307}, {2}},
-     {L"\U00000903\U00000308\U00000300", {2307}, {3}},
-     {L"\U00000903\U00000900", {2307}, {2}},
-     {L"\U00000903\U00000308\U00000900", {2307}, {3}},
-     {L"\U00000903\U0000094d", {2307}, {2}},
-     {L"\U00000903\U00000308\U0000094d", {2307}, {3}},
-     {L"\U00000903\U0000200d", {2307}, {2}},
-     {L"\U00000903\U00000308\U0000200d", {2307}, {3}},
-     {L"\U00000903\U00000378", {2307, 888}, {1, 2}},
-     {L"\U00000903\U00000308\U00000378", {2307, 888}, {2, 3}},
-     {L"\U00000904\U00000020", {2308, 32}, {1, 2}},
-     {L"\U00000904\U00000308\U00000020", {2308, 32}, {2, 3}},
-     {L"\U00000904\U0000000d", {2308, 13}, {1, 2}},
-     {L"\U00000904\U00000308\U0000000d", {2308, 13}, {2, 3}},
-     {L"\U00000904\U0000000a", {2308, 10}, {1, 2}},
-     {L"\U00000904\U00000308\U0000000a", {2308, 10}, {2, 3}},
-     {L"\U00000904\U00000001", {2308, 1}, {1, 2}},
-     {L"\U00000904\U00000308\U00000001", {2308, 1}, {2, 3}},
-     {L"\U00000904\U0000200c", {2308}, {2}},
-     {L"\U00000904\U00000308\U0000200c", {2308}, {3}},
-     {L"\U00000904\U0001f1e6", {2308, 127462}, {1, 2}},
-     {L"\U00000904\U00000308\U0001f1e6", {2308, 127462}, {2, 3}},
-     {L"\U00000904\U00000600", {2308, 1536}, {1, 2}},
-     {L"\U00000904\U00000308\U00000600", {2308, 1536}, {2, 3}},
-     {L"\U00000904\U00000a03", {2308}, {2}},
-     {L"\U00000904\U00000308\U00000a03", {2308}, {3}},
-     {L"\U00000904\U00001100", {2308, 4352}, {1, 2}},
-     {L"\U00000904\U00000308\U00001100", {2308, 4352}, {2, 3}},
-     {L"\U00000904\U00001160", {2308, 4448}, {1, 2}},
-     {L"\U00000904\U00000308\U00001160", {2308, 4448}, {2, 3}},
-     {L"\U00000904\U000011a8", {2308, 4520}, {1, 2}},
-     {L"\U00000904\U00000308\U000011a8", {2308, 4520}, {2, 3}},
-     {L"\U00000904\U0000ac00", {2308, 44032}, {1, 2}},
-     {L"\U00000904\U00000308\U0000ac00", {2308, 44032}, {2, 3}},
-     {L"\U00000904\U0000ac01", {2308, 44033}, {1, 2}},
-     {L"\U00000904\U00000308\U0000ac01", {2308, 44033}, {2, 3}},
-     {L"\U00000904\U00000903", {2308}, {2}},
-     {L"\U00000904\U00000308\U00000903", {2308}, {3}},
-     {L"\U00000904\U00000904", {2308, 2308}, {1, 2}},
-     {L"\U00000904\U00000308\U00000904", {2308, 2308}, {2, 3}},
-     {L"\U00000904\U00000d4e", {2308, 3406}, {1, 2}},
-     {L"\U00000904\U00000308\U00000d4e", {2308, 3406}, {2, 3}},
-     {L"\U00000904\U00000915", {2308, 2325}, {1, 2}},
-     {L"\U00000904\U00000308\U00000915", {2308, 2325}, {2, 3}},
-     {L"\U00000904\U0000231a", {2308, 8986}, {1, 2}},
-     {L"\U00000904\U00000308\U0000231a", {2308, 8986}, {2, 3}},
-     {L"\U00000904\U00000300", {2308}, {2}},
-     {L"\U00000904\U00000308\U00000300", {2308}, {3}},
-     {L"\U00000904\U00000900", {2308}, {2}},
-     {L"\U00000904\U00000308\U00000900", {2308}, {3}},
-     {L"\U00000904\U0000094d", {2308}, {2}},
-     {L"\U00000904\U00000308\U0000094d", {2308}, {3}},
-     {L"\U00000904\U0000200d", {2308}, {2}},
-     {L"\U00000904\U00000308\U0000200d", {2308}, {3}},
-     {L"\U00000904\U00000378", {2308, 888}, {1, 2}},
-     {L"\U00000904\U00000308\U00000378", {2308, 888}, {2, 3}},
-     {L"\U00000d4e\U00000020", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000020", {3406, 32}, {2, 3}},
-     {L"\U00000d4e\U0000000d", {3406, 13}, {1, 2}},
-     {L"\U00000d4e\U00000308\U0000000d", {3406, 13}, {2, 3}},
-     {L"\U00000d4e\U0000000a", {3406, 10}, {1, 2}},
-     {L"\U00000d4e\U00000308\U0000000a", {3406, 10}, {2, 3}},
-     {L"\U00000d4e\U00000001", {3406, 1}, {1, 2}},
-     {L"\U00000d4e\U00000308\U00000001", {3406, 1}, {2, 3}},
-     {L"\U00000d4e\U0000200c", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0000200c", {3406}, {3}},
-     {L"\U00000d4e\U0001f1e6", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0001f1e6", {3406, 127462}, {2, 3}},
-     {L"\U00000d4e\U00000600", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000600", {3406, 1536}, {2, 3}},
-     {L"\U00000d4e\U00000a03", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000a03", {3406}, {3}},
-     {L"\U00000d4e\U00001100", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00001100", {3406, 4352}, {2, 3}},
-     {L"\U00000d4e\U00001160", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00001160", {3406, 4448}, {2, 3}},
-     {L"\U00000d4e\U000011a8", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U000011a8", {3406, 4520}, {2, 3}},
-     {L"\U00000d4e\U0000ac00", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0000ac00", {3406, 44032}, {2, 3}},
-     {L"\U00000d4e\U0000ac01", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0000ac01", {3406, 44033}, {2, 3}},
-     {L"\U00000d4e\U00000903", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000903", {3406}, {3}},
-     {L"\U00000d4e\U00000904", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000904", {3406, 2308}, {2, 3}},
-     {L"\U00000d4e\U00000d4e", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000d4e", {3406, 3406}, {2, 3}},
-     {L"\U00000d4e\U00000915", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000915", {3406, 2325}, {2, 3}},
-     {L"\U00000d4e\U0000231a", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0000231a", {3406, 8986}, {2, 3}},
-     {L"\U00000d4e\U00000300", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000300", {3406}, {3}},
-     {L"\U00000d4e\U00000900", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000900", {3406}, {3}},
-     {L"\U00000d4e\U0000094d", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0000094d", {3406}, {3}},
-     {L"\U00000d4e\U0000200d", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U0000200d", {3406}, {3}},
-     {L"\U00000d4e\U00000378", {3406}, {2}},
-     {L"\U00000d4e\U00000308\U00000378", {3406, 888}, {2, 3}},
-     {L"\U00000915\U00000020", {2325, 32}, {1, 2}},
-     {L"\U00000915\U00000308\U00000020", {2325, 32}, {2, 3}},
+     {L"\U00001cf5\U0000000d", {7413, 13}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000000d", {7413, 13}, {2, 3}},
+     {L"\U00001cf5\U0000000a", {7413, 10}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000000a", {7413, 10}, {2, 3}},
+     {L"\U00001cf5\U00000000", {7413, 0}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00000000", {7413, 0}, {2, 3}},
+     {L"\U00001cf5\U0000094d", {7413}, {2}},
+     {L"\U00001cf5\U00000308\U0000094d", {7413}, {3}},
+     {L"\U00001cf5\U00000300", {7413}, {2}},
+     {L"\U00001cf5\U00000308\U00000300", {7413}, {3}},
+     {L"\U00001cf5\U0000200c", {7413}, {2}},
+     {L"\U00001cf5\U00000308\U0000200c", {7413}, {3}},
+     {L"\U00001cf5\U0000200d", {7413}, {2}},
+     {L"\U00001cf5\U00000308\U0000200d", {7413}, {3}},
+     {L"\U00001cf5\U0001f1e6", {7413, 127462}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0001f1e6", {7413, 127462}, {2, 3}},
+     {L"\U00001cf5\U000006dd", {7413, 1757}, {1, 2}},
+     {L"\U00001cf5\U00000308\U000006dd", {7413, 1757}, {2, 3}},
+     {L"\U00001cf5\U00000903", {7413}, {2}},
+     {L"\U00001cf5\U00000308\U00000903", {7413}, {3}},
+     {L"\U00001cf5\U00001100", {7413, 4352}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00001100", {7413, 4352}, {2, 3}},
+     {L"\U00001cf5\U00001160", {7413, 4448}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00001160", {7413, 4448}, {2, 3}},
+     {L"\U00001cf5\U000011a8", {7413, 4520}, {1, 2}},
+     {L"\U00001cf5\U00000308\U000011a8", {7413, 4520}, {2, 3}},
+     {L"\U00001cf5\U0000ac00", {7413, 44032}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000ac00", {7413, 44032}, {2, 3}},
+     {L"\U00001cf5\U0000ac01", {7413, 44033}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000ac01", {7413, 44033}, {2, 3}},
+     {L"\U00001cf5\U00001cf5", {7413, 7413}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00001cf5", {7413, 7413}, {2, 3}},
+     {L"\U00001cf5\U00000915", {7413}, {2}},
+     {L"\U00001cf5\U00000308\U00000915", {7413}, {3}},
+     {L"\U00001cf5\U000000a9", {7413, 169}, {1, 2}},
+     {L"\U00001cf5\U00000308\U000000a9", {7413, 169}, {2, 3}},
+     {L"\U00001cf5\U00000020", {7413, 32}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00000020", {7413, 32}, {2, 3}},
+     {L"\U00001cf5\U00000378", {7413, 888}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00000378", {7413, 888}, {2, 3}},
      {L"\U00000915\U0000000d", {2325, 13}, {1, 2}},
      {L"\U00000915\U00000308\U0000000d", {2325, 13}, {2, 3}},
      {L"\U00000915\U0000000a", {2325, 10}, {1, 2}},
      {L"\U00000915\U00000308\U0000000a", {2325, 10}, {2, 3}},
-     {L"\U00000915\U00000001", {2325, 1}, {1, 2}},
-     {L"\U00000915\U00000308\U00000001", {2325, 1}, {2, 3}},
+     {L"\U00000915\U00000000", {2325, 0}, {1, 2}},
+     {L"\U00000915\U00000308\U00000000", {2325, 0}, {2, 3}},
+     {L"\U00000915\U0000094d", {2325}, {2}},
+     {L"\U00000915\U00000308\U0000094d", {2325}, {3}},
+     {L"\U00000915\U00000300", {2325}, {2}},
+     {L"\U00000915\U00000308\U00000300", {2325}, {3}},
      {L"\U00000915\U0000200c", {2325}, {2}},
      {L"\U00000915\U00000308\U0000200c", {2325}, {3}},
+     {L"\U00000915\U0000200d", {2325}, {2}},
+     {L"\U00000915\U00000308\U0000200d", {2325}, {3}},
      {L"\U00000915\U0001f1e6", {2325, 127462}, {1, 2}},
      {L"\U00000915\U00000308\U0001f1e6", {2325, 127462}, {2, 3}},
-     {L"\U00000915\U00000600", {2325, 1536}, {1, 2}},
-     {L"\U00000915\U00000308\U00000600", {2325, 1536}, {2, 3}},
-     {L"\U00000915\U00000a03", {2325}, {2}},
-     {L"\U00000915\U00000308\U00000a03", {2325}, {3}},
+     {L"\U00000915\U000006dd", {2325, 1757}, {1, 2}},
+     {L"\U00000915\U00000308\U000006dd", {2325, 1757}, {2, 3}},
+     {L"\U00000915\U00000903", {2325}, {2}},
+     {L"\U00000915\U00000308\U00000903", {2325}, {3}},
      {L"\U00000915\U00001100", {2325, 4352}, {1, 2}},
      {L"\U00000915\U00000308\U00001100", {2325, 4352}, {2, 3}},
      {L"\U00000915\U00001160", {2325, 4448}, {1, 2}},
@@ -3046,272 +2474,116 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U00000915\U00000308\U0000ac00", {2325, 44032}, {2, 3}},
      {L"\U00000915\U0000ac01", {2325, 44033}, {1, 2}},
      {L"\U00000915\U00000308\U0000ac01", {2325, 44033}, {2, 3}},
-     {L"\U00000915\U00000903", {2325}, {2}},
-     {L"\U00000915\U00000308\U00000903", {2325}, {3}},
-     {L"\U00000915\U00000904", {2325, 2308}, {1, 2}},
-     {L"\U00000915\U00000308\U00000904", {2325, 2308}, {2, 3}},
-     {L"\U00000915\U00000d4e", {2325, 3406}, {1, 2}},
-     {L"\U00000915\U00000308\U00000d4e", {2325, 3406}, {2, 3}},
+     {L"\U00000915\U00001cf5", {2325, 7413}, {1, 2}},
+     {L"\U00000915\U00000308\U00001cf5", {2325, 7413}, {2, 3}},
      {L"\U00000915\U00000915", {2325, 2325}, {1, 2}},
      {L"\U00000915\U00000308\U00000915", {2325, 2325}, {2, 3}},
-     {L"\U00000915\U0000231a", {2325, 8986}, {1, 2}},
-     {L"\U00000915\U00000308\U0000231a", {2325, 8986}, {2, 3}},
-     {L"\U00000915\U00000300", {2325}, {2}},
-     {L"\U00000915\U00000308\U00000300", {2325}, {3}},
-     {L"\U00000915\U00000900", {2325}, {2}},
-     {L"\U00000915\U00000308\U00000900", {2325}, {3}},
-     {L"\U00000915\U0000094d", {2325}, {2}},
-     {L"\U00000915\U00000308\U0000094d", {2325}, {3}},
-     {L"\U00000915\U0000200d", {2325}, {2}},
-     {L"\U00000915\U00000308\U0000200d", {2325}, {3}},
+     {L"\U00000915\U000000a9", {2325, 169}, {1, 2}},
+     {L"\U00000915\U00000308\U000000a9", {2325, 169}, {2, 3}},
+     {L"\U00000915\U00000020", {2325, 32}, {1, 2}},
+     {L"\U00000915\U00000308\U00000020", {2325, 32}, {2, 3}},
      {L"\U00000915\U00000378", {2325, 888}, {1, 2}},
      {L"\U00000915\U00000308\U00000378", {2325, 888}, {2, 3}},
-     {L"\U0000231a\U00000020", {8986, 32}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000020", {8986, 32}, {2, 3}},
-     {L"\U0000231a\U0000000d", {8986, 13}, {1, 2}},
-     {L"\U0000231a\U00000308\U0000000d", {8986, 13}, {2, 3}},
-     {L"\U0000231a\U0000000a", {8986, 10}, {1, 2}},
-     {L"\U0000231a\U00000308\U0000000a", {8986, 10}, {2, 3}},
-     {L"\U0000231a\U00000001", {8986, 1}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000001", {8986, 1}, {2, 3}},
-     {L"\U0000231a\U0000200c", {8986}, {2}},
-     {L"\U0000231a\U00000308\U0000200c", {8986}, {3}},
-     {L"\U0000231a\U0001f1e6", {8986, 127462}, {1, 2}},
-     {L"\U0000231a\U00000308\U0001f1e6", {8986, 127462}, {2, 3}},
-     {L"\U0000231a\U00000600", {8986, 1536}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000600", {8986, 1536}, {2, 3}},
-     {L"\U0000231a\U00000a03", {8986}, {2}},
-     {L"\U0000231a\U00000308\U00000a03", {8986}, {3}},
-     {L"\U0000231a\U00001100", {8986, 4352}, {1, 2}},
-     {L"\U0000231a\U00000308\U00001100", {8986, 4352}, {2, 3}},
-     {L"\U0000231a\U00001160", {8986, 4448}, {1, 2}},
-     {L"\U0000231a\U00000308\U00001160", {8986, 4448}, {2, 3}},
-     {L"\U0000231a\U000011a8", {8986, 4520}, {1, 2}},
-     {L"\U0000231a\U00000308\U000011a8", {8986, 4520}, {2, 3}},
-     {L"\U0000231a\U0000ac00", {8986, 44032}, {1, 2}},
-     {L"\U0000231a\U00000308\U0000ac00", {8986, 44032}, {2, 3}},
-     {L"\U0000231a\U0000ac01", {8986, 44033}, {1, 2}},
-     {L"\U0000231a\U00000308\U0000ac01", {8986, 44033}, {2, 3}},
-     {L"\U0000231a\U00000903", {8986}, {2}},
-     {L"\U0000231a\U00000308\U00000903", {8986}, {3}},
-     {L"\U0000231a\U00000904", {8986, 2308}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000904", {8986, 2308}, {2, 3}},
-     {L"\U0000231a\U00000d4e", {8986, 3406}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000d4e", {8986, 3406}, {2, 3}},
-     {L"\U0000231a\U00000915", {8986, 2325}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000915", {8986, 2325}, {2, 3}},
-     {L"\U0000231a\U0000231a", {8986, 8986}, {1, 2}},
-     {L"\U0000231a\U00000308\U0000231a", {8986, 8986}, {2, 3}},
-     {L"\U0000231a\U00000300", {8986}, {2}},
-     {L"\U0000231a\U00000308\U00000300", {8986}, {3}},
-     {L"\U0000231a\U00000900", {8986}, {2}},
-     {L"\U0000231a\U00000308\U00000900", {8986}, {3}},
-     {L"\U0000231a\U0000094d", {8986}, {2}},
-     {L"\U0000231a\U00000308\U0000094d", {8986}, {3}},
-     {L"\U0000231a\U0000200d", {8986}, {2}},
-     {L"\U0000231a\U00000308\U0000200d", {8986}, {3}},
-     {L"\U0000231a\U00000378", {8986, 888}, {1, 2}},
-     {L"\U0000231a\U00000308\U00000378", {8986, 888}, {2, 3}},
-     {L"\U00000300\U00000020", {768, 32}, {1, 2}},
-     {L"\U00000300\U00000308\U00000020", {768, 32}, {2, 3}},
-     {L"\U00000300\U0000000d", {768, 13}, {1, 2}},
-     {L"\U00000300\U00000308\U0000000d", {768, 13}, {2, 3}},
-     {L"\U00000300\U0000000a", {768, 10}, {1, 2}},
-     {L"\U00000300\U00000308\U0000000a", {768, 10}, {2, 3}},
-     {L"\U00000300\U00000001", {768, 1}, {1, 2}},
-     {L"\U00000300\U00000308\U00000001", {768, 1}, {2, 3}},
-     {L"\U00000300\U0000200c", {768}, {2}},
-     {L"\U00000300\U00000308\U0000200c", {768}, {3}},
-     {L"\U00000300\U0001f1e6", {768, 127462}, {1, 2}},
-     {L"\U00000300\U00000308\U0001f1e6", {768, 127462}, {2, 3}},
-     {L"\U00000300\U00000600", {768, 1536}, {1, 2}},
-     {L"\U00000300\U00000308\U00000600", {768, 1536}, {2, 3}},
-     {L"\U00000300\U00000a03", {768}, {2}},
-     {L"\U00000300\U00000308\U00000a03", {768}, {3}},
-     {L"\U00000300\U00001100", {768, 4352}, {1, 2}},
-     {L"\U00000300\U00000308\U00001100", {768, 4352}, {2, 3}},
-     {L"\U00000300\U00001160", {768, 4448}, {1, 2}},
-     {L"\U00000300\U00000308\U00001160", {768, 4448}, {2, 3}},
-     {L"\U00000300\U000011a8", {768, 4520}, {1, 2}},
-     {L"\U00000300\U00000308\U000011a8", {768, 4520}, {2, 3}},
-     {L"\U00000300\U0000ac00", {768, 44032}, {1, 2}},
-     {L"\U00000300\U00000308\U0000ac00", {768, 44032}, {2, 3}},
-     {L"\U00000300\U0000ac01", {768, 44033}, {1, 2}},
-     {L"\U00000300\U00000308\U0000ac01", {768, 44033}, {2, 3}},
-     {L"\U00000300\U00000903", {768}, {2}},
-     {L"\U00000300\U00000308\U00000903", {768}, {3}},
-     {L"\U00000300\U00000904", {768, 2308}, {1, 2}},
-     {L"\U00000300\U00000308\U00000904", {768, 2308}, {2, 3}},
-     {L"\U00000300\U00000d4e", {768, 3406}, {1, 2}},
-     {L"\U00000300\U00000308\U00000d4e", {768, 3406}, {2, 3}},
-     {L"\U00000300\U00000915", {768, 2325}, {1, 2}},
-     {L"\U00000300\U00000308\U00000915", {768, 2325}, {2, 3}},
-     {L"\U00000300\U0000231a", {768, 8986}, {1, 2}},
-     {L"\U00000300\U00000308\U0000231a", {768, 8986}, {2, 3}},
-     {L"\U00000300\U00000300", {768}, {2}},
-     {L"\U00000300\U00000308\U00000300", {768}, {3}},
-     {L"\U00000300\U00000900", {768}, {2}},
-     {L"\U00000300\U00000308\U00000900", {768}, {3}},
-     {L"\U00000300\U0000094d", {768}, {2}},
-     {L"\U00000300\U00000308\U0000094d", {768}, {3}},
-     {L"\U00000300\U0000200d", {768}, {2}},
-     {L"\U00000300\U00000308\U0000200d", {768}, {3}},
-     {L"\U00000300\U00000378", {768, 888}, {1, 2}},
-     {L"\U00000300\U00000308\U00000378", {768, 888}, {2, 3}},
-     {L"\U00000900\U00000020", {2304, 32}, {1, 2}},
-     {L"\U00000900\U00000308\U00000020", {2304, 32}, {2, 3}},
-     {L"\U00000900\U0000000d", {2304, 13}, {1, 2}},
-     {L"\U00000900\U00000308\U0000000d", {2304, 13}, {2, 3}},
-     {L"\U00000900\U0000000a", {2304, 10}, {1, 2}},
-     {L"\U00000900\U00000308\U0000000a", {2304, 10}, {2, 3}},
-     {L"\U00000900\U00000001", {2304, 1}, {1, 2}},
-     {L"\U00000900\U00000308\U00000001", {2304, 1}, {2, 3}},
-     {L"\U00000900\U0000200c", {2304}, {2}},
-     {L"\U00000900\U00000308\U0000200c", {2304}, {3}},
-     {L"\U00000900\U0001f1e6", {2304, 127462}, {1, 2}},
-     {L"\U00000900\U00000308\U0001f1e6", {2304, 127462}, {2, 3}},
-     {L"\U00000900\U00000600", {2304, 1536}, {1, 2}},
-     {L"\U00000900\U00000308\U00000600", {2304, 1536}, {2, 3}},
-     {L"\U00000900\U00000a03", {2304}, {2}},
-     {L"\U00000900\U00000308\U00000a03", {2304}, {3}},
-     {L"\U00000900\U00001100", {2304, 4352}, {1, 2}},
-     {L"\U00000900\U00000308\U00001100", {2304, 4352}, {2, 3}},
-     {L"\U00000900\U00001160", {2304, 4448}, {1, 2}},
-     {L"\U00000900\U00000308\U00001160", {2304, 4448}, {2, 3}},
-     {L"\U00000900\U000011a8", {2304, 4520}, {1, 2}},
-     {L"\U00000900\U00000308\U000011a8", {2304, 4520}, {2, 3}},
-     {L"\U00000900\U0000ac00", {2304, 44032}, {1, 2}},
-     {L"\U00000900\U00000308\U0000ac00", {2304, 44032}, {2, 3}},
-     {L"\U00000900\U0000ac01", {2304, 44033}, {1, 2}},
-     {L"\U00000900\U00000308\U0000ac01", {2304, 44033}, {2, 3}},
-     {L"\U00000900\U00000903", {2304}, {2}},
-     {L"\U00000900\U00000308\U00000903", {2304}, {3}},
-     {L"\U00000900\U00000904", {2304, 2308}, {1, 2}},
-     {L"\U00000900\U00000308\U00000904", {2304, 2308}, {2, 3}},
-     {L"\U00000900\U00000d4e", {2304, 3406}, {1, 2}},
-     {L"\U00000900\U00000308\U00000d4e", {2304, 3406}, {2, 3}},
-     {L"\U00000900\U00000915", {2304, 2325}, {1, 2}},
-     {L"\U00000900\U00000308\U00000915", {2304, 2325}, {2, 3}},
-     {L"\U00000900\U0000231a", {2304, 8986}, {1, 2}},
-     {L"\U00000900\U00000308\U0000231a", {2304, 8986}, {2, 3}},
-     {L"\U00000900\U00000300", {2304}, {2}},
-     {L"\U00000900\U00000308\U00000300", {2304}, {3}},
-     {L"\U00000900\U00000900", {2304}, {2}},
-     {L"\U00000900\U00000308\U00000900", {2304}, {3}},
-     {L"\U00000900\U0000094d", {2304}, {2}},
-     {L"\U00000900\U00000308\U0000094d", {2304}, {3}},
-     {L"\U00000900\U0000200d", {2304}, {2}},
-     {L"\U00000900\U00000308\U0000200d", {2304}, {3}},
-     {L"\U00000900\U00000378", {2304, 888}, {1, 2}},
-     {L"\U00000900\U00000308\U00000378", {2304, 888}, {2, 3}},
-     {L"\U0000094d\U00000020", {2381, 32}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000020", {2381, 32}, {2, 3}},
-     {L"\U0000094d\U0000000d", {2381, 13}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000000d", {2381, 13}, {2, 3}},
-     {L"\U0000094d\U0000000a", {2381, 10}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000000a", {2381, 10}, {2, 3}},
-     {L"\U0000094d\U00000001", {2381, 1}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000001", {2381, 1}, {2, 3}},
-     {L"\U0000094d\U0000200c", {2381}, {2}},
-     {L"\U0000094d\U00000308\U0000200c", {2381}, {3}},
-     {L"\U0000094d\U0001f1e6", {2381, 127462}, {1, 2}},
-     {L"\U0000094d\U00000308\U0001f1e6", {2381, 127462}, {2, 3}},
-     {L"\U0000094d\U00000600", {2381, 1536}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000600", {2381, 1536}, {2, 3}},
-     {L"\U0000094d\U00000a03", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000a03", {2381}, {3}},
-     {L"\U0000094d\U00001100", {2381, 4352}, {1, 2}},
-     {L"\U0000094d\U00000308\U00001100", {2381, 4352}, {2, 3}},
-     {L"\U0000094d\U00001160", {2381, 4448}, {1, 2}},
-     {L"\U0000094d\U00000308\U00001160", {2381, 4448}, {2, 3}},
-     {L"\U0000094d\U000011a8", {2381, 4520}, {1, 2}},
-     {L"\U0000094d\U00000308\U000011a8", {2381, 4520}, {2, 3}},
-     {L"\U0000094d\U0000ac00", {2381, 44032}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000ac00", {2381, 44032}, {2, 3}},
-     {L"\U0000094d\U0000ac01", {2381, 44033}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000ac01", {2381, 44033}, {2, 3}},
-     {L"\U0000094d\U00000903", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000903", {2381}, {3}},
-     {L"\U0000094d\U00000904", {2381, 2308}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000904", {2381, 2308}, {2, 3}},
-     {L"\U0000094d\U00000d4e", {2381, 3406}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000d4e", {2381, 3406}, {2, 3}},
-     {L"\U0000094d\U00000915", {2381, 2325}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000915", {2381, 2325}, {2, 3}},
-     {L"\U0000094d\U0000231a", {2381, 8986}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000231a", {2381, 8986}, {2, 3}},
-     {L"\U0000094d\U00000300", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000300", {2381}, {3}},
-     {L"\U0000094d\U00000900", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000900", {2381}, {3}},
-     {L"\U0000094d\U0000094d", {2381}, {2}},
-     {L"\U0000094d\U00000308\U0000094d", {2381}, {3}},
-     {L"\U0000094d\U0000200d", {2381}, {2}},
-     {L"\U0000094d\U00000308\U0000200d", {2381}, {3}},
-     {L"\U0000094d\U00000378", {2381, 888}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000378", {2381, 888}, {2, 3}},
-     {L"\U0000200d\U00000020", {8205, 32}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000020", {8205, 32}, {2, 3}},
-     {L"\U0000200d\U0000000d", {8205, 13}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000000d", {8205, 13}, {2, 3}},
-     {L"\U0000200d\U0000000a", {8205, 10}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000000a", {8205, 10}, {2, 3}},
-     {L"\U0000200d\U00000001", {8205, 1}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000001", {8205, 1}, {2, 3}},
-     {L"\U0000200d\U0000200c", {8205}, {2}},
-     {L"\U0000200d\U00000308\U0000200c", {8205}, {3}},
-     {L"\U0000200d\U0001f1e6", {8205, 127462}, {1, 2}},
-     {L"\U0000200d\U00000308\U0001f1e6", {8205, 127462}, {2, 3}},
-     {L"\U0000200d\U00000600", {8205, 1536}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000600", {8205, 1536}, {2, 3}},
-     {L"\U0000200d\U00000a03", {8205}, {2}},
-     {L"\U0000200d\U00000308\U00000a03", {8205}, {3}},
-     {L"\U0000200d\U00001100", {8205, 4352}, {1, 2}},
-     {L"\U0000200d\U00000308\U00001100", {8205, 4352}, {2, 3}},
-     {L"\U0000200d\U00001160", {8205, 4448}, {1, 2}},
-     {L"\U0000200d\U00000308\U00001160", {8205, 4448}, {2, 3}},
-     {L"\U0000200d\U000011a8", {8205, 4520}, {1, 2}},
-     {L"\U0000200d\U00000308\U000011a8", {8205, 4520}, {2, 3}},
-     {L"\U0000200d\U0000ac00", {8205, 44032}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000ac00", {8205, 44032}, {2, 3}},
-     {L"\U0000200d\U0000ac01", {8205, 44033}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000ac01", {8205, 44033}, {2, 3}},
-     {L"\U0000200d\U00000903", {8205}, {2}},
-     {L"\U0000200d\U00000308\U00000903", {8205}, {3}},
-     {L"\U0000200d\U00000904", {8205, 2308}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000904", {8205, 2308}, {2, 3}},
-     {L"\U0000200d\U00000d4e", {8205, 3406}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000d4e", {8205, 3406}, {2, 3}},
-     {L"\U0000200d\U00000915", {8205, 2325}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000915", {8205, 2325}, {2, 3}},
-     {L"\U0000200d\U0000231a", {8205, 8986}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000231a", {8205, 8986}, {2, 3}},
-     {L"\U0000200d\U00000300", {8205}, {2}},
-     {L"\U0000200d\U00000308\U00000300", {8205}, {3}},
-     {L"\U0000200d\U00000900", {8205}, {2}},
-     {L"\U0000200d\U00000308\U00000900", {8205}, {3}},
-     {L"\U0000200d\U0000094d", {8205}, {2}},
-     {L"\U0000200d\U00000308\U0000094d", {8205}, {3}},
-     {L"\U0000200d\U0000200d", {8205}, {2}},
-     {L"\U0000200d\U00000308\U0000200d", {8205}, {3}},
-     {L"\U0000200d\U00000378", {8205, 888}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000378", {8205, 888}, {2, 3}},
-     {L"\U00000378\U00000020", {888, 32}, {1, 2}},
-     {L"\U00000378\U00000308\U00000020", {888, 32}, {2, 3}},
+     {L"\U000000a9\U0000000d", {169, 13}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000000d", {169, 13}, {2, 3}},
+     {L"\U000000a9\U0000000a", {169, 10}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000000a", {169, 10}, {2, 3}},
+     {L"\U000000a9\U00000000", {169, 0}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000000", {169, 0}, {2, 3}},
+     {L"\U000000a9\U0000094d", {169}, {2}},
+     {L"\U000000a9\U00000308\U0000094d", {169}, {3}},
+     {L"\U000000a9\U00000300", {169}, {2}},
+     {L"\U000000a9\U00000308\U00000300", {169}, {3}},
+     {L"\U000000a9\U0000200c", {169}, {2}},
+     {L"\U000000a9\U00000308\U0000200c", {169}, {3}},
+     {L"\U000000a9\U0000200d", {169}, {2}},
+     {L"\U000000a9\U00000308\U0000200d", {169}, {3}},
+     {L"\U000000a9\U0001f1e6", {169, 127462}, {1, 2}},
+     {L"\U000000a9\U00000308\U0001f1e6", {169, 127462}, {2, 3}},
+     {L"\U000000a9\U000006dd", {169, 1757}, {1, 2}},
+     {L"\U000000a9\U00000308\U000006dd", {169, 1757}, {2, 3}},
+     {L"\U000000a9\U00000903", {169}, {2}},
+     {L"\U000000a9\U00000308\U00000903", {169}, {3}},
+     {L"\U000000a9\U00001100", {169, 4352}, {1, 2}},
+     {L"\U000000a9\U00000308\U00001100", {169, 4352}, {2, 3}},
+     {L"\U000000a9\U00001160", {169, 4448}, {1, 2}},
+     {L"\U000000a9\U00000308\U00001160", {169, 4448}, {2, 3}},
+     {L"\U000000a9\U000011a8", {169, 4520}, {1, 2}},
+     {L"\U000000a9\U00000308\U000011a8", {169, 4520}, {2, 3}},
+     {L"\U000000a9\U0000ac00", {169, 44032}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000ac00", {169, 44032}, {2, 3}},
+     {L"\U000000a9\U0000ac01", {169, 44033}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000ac01", {169, 44033}, {2, 3}},
+     {L"\U000000a9\U00001cf5", {169, 7413}, {1, 2}},
+     {L"\U000000a9\U00000308\U00001cf5", {169, 7413}, {2, 3}},
+     {L"\U000000a9\U00000915", {169, 2325}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000915", {169, 2325}, {2, 3}},
+     {L"\U000000a9\U000000a9", {169, 169}, {1, 2}},
+     {L"\U000000a9\U00000308\U000000a9", {169, 169}, {2, 3}},
+     {L"\U000000a9\U00000020", {169, 32}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000020", {169, 32}, {2, 3}},
+     {L"\U000000a9\U00000378", {169, 888}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000378", {169, 888}, {2, 3}},
+     {L"\U00000020\U0000000d", {32, 13}, {1, 2}},
+     {L"\U00000020\U00000308\U0000000d", {32, 13}, {2, 3}},
+     {L"\U00000020\U0000000a", {32, 10}, {1, 2}},
+     {L"\U00000020\U00000308\U0000000a", {32, 10}, {2, 3}},
+     {L"\U00000020\U00000000", {32, 0}, {1, 2}},
+     {L"\U00000020\U00000308\U00000000", {32, 0}, {2, 3}},
+     {L"\U00000020\U0000094d", {32}, {2}},
+     {L"\U00000020\U00000308\U0000094d", {32}, {3}},
+     {L"\U00000020\U00000300", {32}, {2}},
+     {L"\U00000020\U00000308\U00000300", {32}, {3}},
+     {L"\U00000020\U0000200c", {32}, {2}},
+     {L"\U00000020\U00000308\U0000200c", {32}, {3}},
+     {L"\U00000020\U0000200d", {32}, {2}},
+     {L"\U00000020\U00000308\U0000200d", {32}, {3}},
+     {L"\U00000020\U0001f1e6", {32, 127462}, {1, 2}},
+     {L"\U00000020\U00000308\U0001f1e6", {32, 127462}, {2, 3}},
+     {L"\U00000020\U000006dd", {32, 1757}, {1, 2}},
+     {L"\U00000020\U00000308\U000006dd", {32, 1757}, {2, 3}},
+     {L"\U00000020\U00000903", {32}, {2}},
+     {L"\U00000020\U00000308\U00000903", {32}, {3}},
+     {L"\U00000020\U00001100", {32, 4352}, {1, 2}},
+     {L"\U00000020\U00000308\U00001100", {32, 4352}, {2, 3}},
+     {L"\U00000020\U00001160", {32, 4448}, {1, 2}},
+     {L"\U00000020\U00000308\U00001160", {32, 4448}, {2, 3}},
+     {L"\U00000020\U000011a8", {32, 4520}, {1, 2}},
+     {L"\U00000020\U00000308\U000011a8", {32, 4520}, {2, 3}},
+     {L"\U00000020\U0000ac00", {32, 44032}, {1, 2}},
+     {L"\U00000020\U00000308\U0000ac00", {32, 44032}, {2, 3}},
+     {L"\U00000020\U0000ac01", {32, 44033}, {1, 2}},
+     {L"\U00000020\U00000308\U0000ac01", {32, 44033}, {2, 3}},
+     {L"\U00000020\U00001cf5", {32, 7413}, {1, 2}},
+     {L"\U00000020\U00000308\U00001cf5", {32, 7413}, {2, 3}},
+     {L"\U00000020\U00000915", {32, 2325}, {1, 2}},
+     {L"\U00000020\U00000308\U00000915", {32, 2325}, {2, 3}},
+     {L"\U00000020\U000000a9", {32, 169}, {1, 2}},
+     {L"\U00000020\U00000308\U000000a9", {32, 169}, {2, 3}},
+     {L"\U00000020\U00000020", {32, 32}, {1, 2}},
+     {L"\U00000020\U00000308\U00000020", {32, 32}, {2, 3}},
+     {L"\U00000020\U00000378", {32, 888}, {1, 2}},
+     {L"\U00000020\U00000308\U00000378", {32, 888}, {2, 3}},
      {L"\U00000378\U0000000d", {888, 13}, {1, 2}},
      {L"\U00000378\U00000308\U0000000d", {888, 13}, {2, 3}},
      {L"\U00000378\U0000000a", {888, 10}, {1, 2}},
      {L"\U00000378\U00000308\U0000000a", {888, 10}, {2, 3}},
-     {L"\U00000378\U00000001", {888, 1}, {1, 2}},
-     {L"\U00000378\U00000308\U00000001", {888, 1}, {2, 3}},
+     {L"\U00000378\U00000000", {888, 0}, {1, 2}},
+     {L"\U00000378\U00000308\U00000000", {888, 0}, {2, 3}},
+     {L"\U00000378\U0000094d", {888}, {2}},
+     {L"\U00000378\U00000308\U0000094d", {888}, {3}},
+     {L"\U00000378\U00000300", {888}, {2}},
+     {L"\U00000378\U00000308\U00000300", {888}, {3}},
      {L"\U00000378\U0000200c", {888}, {2}},
      {L"\U00000378\U00000308\U0000200c", {888}, {3}},
+     {L"\U00000378\U0000200d", {888}, {2}},
+     {L"\U00000378\U00000308\U0000200d", {888}, {3}},
      {L"\U00000378\U0001f1e6", {888, 127462}, {1, 2}},
      {L"\U00000378\U00000308\U0001f1e6", {888, 127462}, {2, 3}},
-     {L"\U00000378\U00000600", {888, 1536}, {1, 2}},
-     {L"\U00000378\U00000308\U00000600", {888, 1536}, {2, 3}},
-     {L"\U00000378\U00000a03", {888}, {2}},
-     {L"\U00000378\U00000308\U00000a03", {888}, {3}},
+     {L"\U00000378\U000006dd", {888, 1757}, {1, 2}},
+     {L"\U00000378\U00000308\U000006dd", {888, 1757}, {2, 3}},
+     {L"\U00000378\U00000903", {888}, {2}},
+     {L"\U00000378\U00000308\U00000903", {888}, {3}},
      {L"\U00000378\U00001100", {888, 4352}, {1, 2}},
      {L"\U00000378\U00000308\U00001100", {888, 4352}, {2, 3}},
      {L"\U00000378\U00001160", {888, 4448}, {1, 2}},
@@ -3322,24 +2594,14 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U00000378\U00000308\U0000ac00", {888, 44032}, {2, 3}},
      {L"\U00000378\U0000ac01", {888, 44033}, {1, 2}},
      {L"\U00000378\U00000308\U0000ac01", {888, 44033}, {2, 3}},
-     {L"\U00000378\U00000903", {888}, {2}},
-     {L"\U00000378\U00000308\U00000903", {888}, {3}},
-     {L"\U00000378\U00000904", {888, 2308}, {1, 2}},
-     {L"\U00000378\U00000308\U00000904", {888, 2308}, {2, 3}},
-     {L"\U00000378\U00000d4e", {888, 3406}, {1, 2}},
-     {L"\U00000378\U00000308\U00000d4e", {888, 3406}, {2, 3}},
+     {L"\U00000378\U00001cf5", {888, 7413}, {1, 2}},
+     {L"\U00000378\U00000308\U00001cf5", {888, 7413}, {2, 3}},
      {L"\U00000378\U00000915", {888, 2325}, {1, 2}},
      {L"\U00000378\U00000308\U00000915", {888, 2325}, {2, 3}},
-     {L"\U00000378\U0000231a", {888, 8986}, {1, 2}},
-     {L"\U00000378\U00000308\U0000231a", {888, 8986}, {2, 3}},
-     {L"\U00000378\U00000300", {888}, {2}},
-     {L"\U00000378\U00000308\U00000300", {888}, {3}},
-     {L"\U00000378\U00000900", {888}, {2}},
-     {L"\U00000378\U00000308\U00000900", {888}, {3}},
-     {L"\U00000378\U0000094d", {888}, {2}},
-     {L"\U00000378\U00000308\U0000094d", {888}, {3}},
-     {L"\U00000378\U0000200d", {888}, {2}},
-     {L"\U00000378\U00000308\U0000200d", {888}, {3}},
+     {L"\U00000378\U000000a9", {888, 169}, {1, 2}},
+     {L"\U00000378\U00000308\U000000a9", {888, 169}, {2, 3}},
+     {L"\U00000378\U00000020", {888, 32}, {1, 2}},
+     {L"\U00000378\U00000308\U00000020", {888, 32}, {2, 3}},
      {L"\U00000378\U00000378", {888, 888}, {1, 2}},
      {L"\U00000378\U00000308\U00000378", {888, 888}, {2, 3}},
      {L"\U0000000d\U0000000a\U00000061\U0000000a\U00000308", {13, 97, 10, 776}, {2, 3, 4, 5}},
@@ -3364,7 +2626,7 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U0001f476\U0001f3ff\U00000308\U0000200d\U0001f476\U0001f3ff", {128118}, {6}},
      {L"\U0001f6d1\U0000200d\U0001f6d1", {128721}, {3}},
      {L"\U00000061\U0000200d\U0001f6d1", {97, 128721}, {2, 3}},
-     {L"\U00002701\U0000200d\U00002701", {9985}, {3}},
+     {L"\U00002701\U0000200d\U00002701", {9985, 9985}, {2, 3}},
      {L"\U00000061\U0000200d\U00002701", {97, 9985}, {2, 3}},
      {L"\U00000915\U00000924", {2325, 2340}, {1, 2}},
      {L"\U00000915\U0000094d\U00000924", {2325}, {3}},
@@ -3374,9 +2636,27 @@ std::array<data<wchar_t>, 1093> data_utf32 = {{
      {L"\U00000915\U0000093c\U0000094d\U0000200d\U00000924", {2325}, {5}},
      {L"\U00000915\U0000094d\U00000924\U0000094d\U0000092f", {2325}, {5}},
      {L"\U00000915\U0000094d\U00000061", {2325, 97}, {2, 3}},
-     {L"\U00000061\U0000094d\U00000924", {97, 2340}, {2, 3}},
-     {L"\U0000003f\U0000094d\U00000924", {63, 2340}, {2, 3}},
-     {L"\U00000915\U0000094d\U0000094d\U00000924", {2325}, {4}}}};
+     {L"\U00000061\U0000094d\U00000924", {97}, {3}},
+     {L"\U0000003f\U0000094d\U00000924", {63}, {3}},
+     {L"\U00000915\U0000094d\U0000094d\U00000924", {2325}, {4}},
+     {L"\U00000ab8\U00000afb\U00000acd\U00000ab8\U00000afb", {2744}, {5}},
+     {L"\U00001019\U00001039\U00001018\U0000102c\U00001037", {4121, 4140}, {3, 5}},
+     {L"\U00001004\U0000103a\U00001039\U00001011\U00001039\U00001011", {4100}, {6}},
+     {L"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38", {6930, 6962, 6962, 6962}, {2, 5, 10, 14}},
+     {L"\U0000179f\U000017d2\U0000178f\U000017d2\U0000179a\U000017b8", {6047}, {6}},
+     {L"\U00001b26\U00001b17\U00001b44\U00001b13", {6950, 6935}, {1, 4}},
+     {L"\U00001b27\U00001b13\U00001b44\U00001b0b\U00001b0b\U00001b04", {6951, 6931, 6923}, {1, 4, 6}},
+     {L"\U00001795\U000017d2\U000017af\U00001798", {6037, 6040}, {3, 4}},
+     {L"\U000017a0\U000017d2\U000017ab\U00001791\U000017d0\U00001799", {6048, 6033, 6041}, {3, 5, 6}},
+     {L"\U00001b05\U00001b44\U00001b33\U00001b03", {6917}, {4}},
+     {L"\U00000cf1\U00000c95", {3313, 3221}, {1, 2}},
+     {L"\U00000cf2\U00000cab", {3314, 3243}, {1, 2}},
+     {L"\U00000cf1\U00000c95\U00000cbf", {3313, 3221}, {1, 3}},
+     {L"\U00001cf5\U00000995", {7413}, {2}},
+     {L"\U00001cf6\U000009aa", {7414}, {2}},
+     {L"\U00001cf5\U0000200c\U00000995", {7413, 2453}, {2, 3}},
+     {L"\U00001cf6\U0000200c\U000009aa", {7414, 2474}, {2, 3}},
+     {L"\U00011a3a\U00011a0b", {72250}, {2}}}};
 #endif // TEST_HAS_NO_WIDE_CHARACTERS
 
 #endif // LIBCXX_TEST_STD_UTILITIES_FORMAT_FORMAT_STRING_FORMAT_STRING_STD_EXTENDED_GRAPHEME_CLUSTER_H

>From 41ba507af4fa6fa7138509635dce38162bcf55be Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Sat, 20 Jun 2026 21:03:57 -0400
Subject: [PATCH 24/33] Update static assertions for grapheme cluster
 properties

---
 .../extended_grapheme_cluster.pass.cpp             | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
index 90f7cb2c6ee09..fc3f55bb581e9 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
@@ -40,20 +40,20 @@ constexpr int count_entries(cluster::__property property) {
       });
 }
 
-static_assert(count_entries(cluster::__property::__Prepend) == 28);
+static_assert(count_entries(cluster::__property::__Prepend) == 27);
 static_assert(count_entries(cluster::__property::__CR) == 1);
 static_assert(count_entries(cluster::__property::__LF) == 1);
 static_assert(count_entries(cluster::__property::__Control) == 3893);
-static_assert(count_entries(cluster::__property::__Extend) == 2198);
+static_assert(count_entries(cluster::__property::__Extend) == 2276);
 static_assert(count_entries(cluster::__property::__Regional_Indicator) == 26);
-static_assert(count_entries(cluster::__property::__SpacingMark) == 378);
+static_assert(count_entries(cluster::__property::__SpacingMark) == 381);
 static_assert(count_entries(cluster::__property::__L) == 125);
 static_assert(count_entries(cluster::__property::__V) == 100);
 static_assert(count_entries(cluster::__property::__T) == 137);
 static_assert(count_entries(cluster::__property::__LV) == 399);
 static_assert(count_entries(cluster::__property::__LVT) == 10773);
 static_assert(count_entries(cluster::__property::__ZWJ) == 1);
-static_assert(count_entries(cluster::__property::__Extended_Pictographic) == 3537);
+static_assert(count_entries(cluster::__property::__Extended_Pictographic) == 2830);
 
 namespace inCB = std::__indic_conjunct_break;
 constexpr int count_entries(inCB::__property property) {
@@ -66,9 +66,9 @@ constexpr int count_entries(inCB::__property property) {
       });
 }
 
-static_assert(count_entries(inCB::__property::__Linker) == 6);
-static_assert(count_entries(inCB::__property::__Consonant) == 240);
-static_assert(count_entries(inCB::__property::__Extend) == 2192);
+static_assert(count_entries(inCB::__property::__Linker) == 23);
+static_assert(count_entries(inCB::__property::__Consonant) == 913);
+static_assert(count_entries(inCB::__property::__Extend) == 2256);
 
 } // namespace
 

>From 6543ed07aee2f4e3fb05de687cb88d7123fe97c9 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Sat, 20 Jun 2026 22:41:44 -0400
Subject: [PATCH 25/33] Add debug output to extended grapheme cluster test

Added debug output for code point comparison in test.
---
 .../format.string.std/extended_grapheme_cluster.pass.cpp       | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
index fc3f55bb581e9..93f7de151b6b3 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
@@ -21,6 +21,7 @@
 #include <cassert>
 #include <format>
 #include <functional>
+#include <iostream>
 #include <numeric>
 
 #include "extended_grapheme_cluster.h"
@@ -80,6 +81,8 @@ constexpr void test(const Data& data) {
     std::__unicode::__extended_grapheme_cluster_view view{d.input.begin(), d.input.end()};
     for (std::size_t i = 0; i < d.breaks.size(); ++i) {
       auto r = view.__consume();
+      // XXX: remove after libcxx tetss pass
+      std::cerr << "r.__code__point: " << r.__code_point_ << ", d.code_points[" << i << "]: " << d.code_points[i] << '\n'; 
       assert(r.__code_point_ == d.code_points[i]);
       assert(r.__last_ == d.input.begin() + d.breaks[i]);
     }

>From f81cfef460865ba333c8df986f3cb4d8c8f6d135 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Sat, 20 Jun 2026 23:02:23 -0400
Subject: [PATCH 26/33] Update extended_grapheme_cluster.pass.cpp

---
 .../format.string.std/extended_grapheme_cluster.pass.cpp      | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
index 93f7de151b6b3..3bef1025ca13a 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
@@ -82,7 +82,9 @@ constexpr void test(const Data& data) {
     for (std::size_t i = 0; i < d.breaks.size(); ++i) {
       auto r = view.__consume();
       // XXX: remove after libcxx tetss pass
-      std::cerr << "r.__code__point: " << r.__code_point_ << ", d.code_points[" << i << "]: " << d.code_points[i] << '\n'; 
+      std::cerr.setf(std::ios_base::hex | std::ios_base::showbase);
+      std::cerr << "r.__code__point: " << (unsigned long long)r.__code_point_ <<
+        ", d.code_points[" << i << "]: " << (unsigned long long)d.code_points[i] << '\n'; 
       assert(r.__code_point_ == d.code_points[i]);
       assert(r.__last_ == d.input.begin() + d.breaks[i]);
     }

>From ea7e42770d03039e2ad2f23b30abc15305ccc1b7 Mon Sep 17 00:00:00 2001
From: unknown <lol>
Date: Sun, 21 Jun 2026 18:08:47 -0400
Subject: [PATCH 27/33] Use sv literal, and regenerate

---
 .../extended_grapheme_cluster.h               | 5120 +++++++++--------
 ...generate_extended_grapheme_cluster_test.py |    8 +-
 2 files changed, 2566 insertions(+), 2562 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.h b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.h
index c7b0abbe0bced..90e5de6a3f931 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.h
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.h
@@ -67,6 +67,8 @@
 
 #include "test_macros.h"
 
+using namespace std::literals;
+
 template <class CharT>
 struct data {
   /// The input to parse.
@@ -83,859 +85,859 @@ struct data {
 
 /// The data for UTF-8.
 std::array<data<char>, 853> data_utf8 = {{
-     {"\U0000000d\U0000000d", {13, 13}, {1, 2}},
-     {"\U0000000d\U00000308\U0000000d", {13, 776, 13}, {1, 3, 4}},
-     {"\U0000000d\U0000000a", {13}, {2}},
-     {"\U0000000d\U00000308\U0000000a", {13, 776, 10}, {1, 3, 4}},
-     {"\U0000000d\U00000000", {13, 0}, {1, 2}},
-     {"\U0000000d\U00000308\U00000000", {13, 776, 0}, {1, 3, 4}},
-     {"\U0000000d\U0000094d", {13, 2381}, {1, 4}},
-     {"\U0000000d\U00000308\U0000094d", {13, 776}, {1, 6}},
-     {"\U0000000d\U00000300", {13, 768}, {1, 3}},
-     {"\U0000000d\U00000308\U00000300", {13, 776}, {1, 5}},
-     {"\U0000000d\U0000200c", {13, 8204}, {1, 4}},
-     {"\U0000000d\U00000308\U0000200c", {13, 776}, {1, 6}},
-     {"\U0000000d\U0000200d", {13, 8205}, {1, 4}},
-     {"\U0000000d\U00000308\U0000200d", {13, 776}, {1, 6}},
-     {"\U0000000d\U0001f1e6", {13, 127462}, {1, 5}},
-     {"\U0000000d\U00000308\U0001f1e6", {13, 776, 127462}, {1, 3, 7}},
-     {"\U0000000d\U000006dd", {13, 1757}, {1, 3}},
-     {"\U0000000d\U00000308\U000006dd", {13, 776, 1757}, {1, 3, 5}},
-     {"\U0000000d\U00000903", {13, 2307}, {1, 4}},
-     {"\U0000000d\U00000308\U00000903", {13, 776}, {1, 6}},
-     {"\U0000000d\U00001100", {13, 4352}, {1, 4}},
-     {"\U0000000d\U00000308\U00001100", {13, 776, 4352}, {1, 3, 6}},
-     {"\U0000000d\U00001160", {13, 4448}, {1, 4}},
-     {"\U0000000d\U00000308\U00001160", {13, 776, 4448}, {1, 3, 6}},
-     {"\U0000000d\U000011a8", {13, 4520}, {1, 4}},
-     {"\U0000000d\U00000308\U000011a8", {13, 776, 4520}, {1, 3, 6}},
-     {"\U0000000d\U0000ac00", {13, 44032}, {1, 4}},
-     {"\U0000000d\U00000308\U0000ac00", {13, 776, 44032}, {1, 3, 6}},
-     {"\U0000000d\U0000ac01", {13, 44033}, {1, 4}},
-     {"\U0000000d\U00000308\U0000ac01", {13, 776, 44033}, {1, 3, 6}},
-     {"\U0000000d\U00001cf5", {13, 7413}, {1, 4}},
-     {"\U0000000d\U00000308\U00001cf5", {13, 776, 7413}, {1, 3, 6}},
-     {"\U0000000d\U00000915", {13, 2325}, {1, 4}},
-     {"\U0000000d\U00000308\U00000915", {13, 776, 2325}, {1, 3, 6}},
-     {"\U0000000d\U000000a9", {13, 169}, {1, 3}},
-     {"\U0000000d\U00000308\U000000a9", {13, 776, 169}, {1, 3, 5}},
-     {"\U0000000d\U00000020", {13, 32}, {1, 2}},
-     {"\U0000000d\U00000308\U00000020", {13, 776, 32}, {1, 3, 4}},
-     {"\U0000000d\U00000378", {13, 888}, {1, 3}},
-     {"\U0000000d\U00000308\U00000378", {13, 776, 888}, {1, 3, 5}},
-     {"\U0000000a\U0000000d", {10, 13}, {1, 2}},
-     {"\U0000000a\U00000308\U0000000d", {10, 776, 13}, {1, 3, 4}},
-     {"\U0000000a\U0000000a", {10, 10}, {1, 2}},
-     {"\U0000000a\U00000308\U0000000a", {10, 776, 10}, {1, 3, 4}},
-     {"\U0000000a\U00000000", {10, 0}, {1, 2}},
-     {"\U0000000a\U00000308\U00000000", {10, 776, 0}, {1, 3, 4}},
-     {"\U0000000a\U0000094d", {10, 2381}, {1, 4}},
-     {"\U0000000a\U00000308\U0000094d", {10, 776}, {1, 6}},
-     {"\U0000000a\U00000300", {10, 768}, {1, 3}},
-     {"\U0000000a\U00000308\U00000300", {10, 776}, {1, 5}},
-     {"\U0000000a\U0000200c", {10, 8204}, {1, 4}},
-     {"\U0000000a\U00000308\U0000200c", {10, 776}, {1, 6}},
-     {"\U0000000a\U0000200d", {10, 8205}, {1, 4}},
-     {"\U0000000a\U00000308\U0000200d", {10, 776}, {1, 6}},
-     {"\U0000000a\U0001f1e6", {10, 127462}, {1, 5}},
-     {"\U0000000a\U00000308\U0001f1e6", {10, 776, 127462}, {1, 3, 7}},
-     {"\U0000000a\U000006dd", {10, 1757}, {1, 3}},
-     {"\U0000000a\U00000308\U000006dd", {10, 776, 1757}, {1, 3, 5}},
-     {"\U0000000a\U00000903", {10, 2307}, {1, 4}},
-     {"\U0000000a\U00000308\U00000903", {10, 776}, {1, 6}},
-     {"\U0000000a\U00001100", {10, 4352}, {1, 4}},
-     {"\U0000000a\U00000308\U00001100", {10, 776, 4352}, {1, 3, 6}},
-     {"\U0000000a\U00001160", {10, 4448}, {1, 4}},
-     {"\U0000000a\U00000308\U00001160", {10, 776, 4448}, {1, 3, 6}},
-     {"\U0000000a\U000011a8", {10, 4520}, {1, 4}},
-     {"\U0000000a\U00000308\U000011a8", {10, 776, 4520}, {1, 3, 6}},
-     {"\U0000000a\U0000ac00", {10, 44032}, {1, 4}},
-     {"\U0000000a\U00000308\U0000ac00", {10, 776, 44032}, {1, 3, 6}},
-     {"\U0000000a\U0000ac01", {10, 44033}, {1, 4}},
-     {"\U0000000a\U00000308\U0000ac01", {10, 776, 44033}, {1, 3, 6}},
-     {"\U0000000a\U00001cf5", {10, 7413}, {1, 4}},
-     {"\U0000000a\U00000308\U00001cf5", {10, 776, 7413}, {1, 3, 6}},
-     {"\U0000000a\U00000915", {10, 2325}, {1, 4}},
-     {"\U0000000a\U00000308\U00000915", {10, 776, 2325}, {1, 3, 6}},
-     {"\U0000000a\U000000a9", {10, 169}, {1, 3}},
-     {"\U0000000a\U00000308\U000000a9", {10, 776, 169}, {1, 3, 5}},
-     {"\U0000000a\U00000020", {10, 32}, {1, 2}},
-     {"\U0000000a\U00000308\U00000020", {10, 776, 32}, {1, 3, 4}},
-     {"\U0000000a\U00000378", {10, 888}, {1, 3}},
-     {"\U0000000a\U00000308\U00000378", {10, 776, 888}, {1, 3, 5}},
-     {"\U00000000\U0000000d", {0, 13}, {1, 2}},
-     {"\U00000000\U00000308\U0000000d", {0, 776, 13}, {1, 3, 4}},
-     {"\U00000000\U0000000a", {0, 10}, {1, 2}},
-     {"\U00000000\U00000308\U0000000a", {0, 776, 10}, {1, 3, 4}},
-     {"\U00000000\U00000000", {0, 0}, {1, 2}},
-     {"\U00000000\U00000308\U00000000", {0, 776, 0}, {1, 3, 4}},
-     {"\U00000000\U0000094d", {0, 2381}, {1, 4}},
-     {"\U00000000\U00000308\U0000094d", {0, 776}, {1, 6}},
-     {"\U00000000\U00000300", {0, 768}, {1, 3}},
-     {"\U00000000\U00000308\U00000300", {0, 776}, {1, 5}},
-     {"\U00000000\U0000200c", {0, 8204}, {1, 4}},
-     {"\U00000000\U00000308\U0000200c", {0, 776}, {1, 6}},
-     {"\U00000000\U0000200d", {0, 8205}, {1, 4}},
-     {"\U00000000\U00000308\U0000200d", {0, 776}, {1, 6}},
-     {"\U00000000\U0001f1e6", {0, 127462}, {1, 5}},
-     {"\U00000000\U00000308\U0001f1e6", {0, 776, 127462}, {1, 3, 7}},
-     {"\U00000000\U000006dd", {0, 1757}, {1, 3}},
-     {"\U00000000\U00000308\U000006dd", {0, 776, 1757}, {1, 3, 5}},
-     {"\U00000000\U00000903", {0, 2307}, {1, 4}},
-     {"\U00000000\U00000308\U00000903", {0, 776}, {1, 6}},
-     {"\U00000000\U00001100", {0, 4352}, {1, 4}},
-     {"\U00000000\U00000308\U00001100", {0, 776, 4352}, {1, 3, 6}},
-     {"\U00000000\U00001160", {0, 4448}, {1, 4}},
-     {"\U00000000\U00000308\U00001160", {0, 776, 4448}, {1, 3, 6}},
-     {"\U00000000\U000011a8", {0, 4520}, {1, 4}},
-     {"\U00000000\U00000308\U000011a8", {0, 776, 4520}, {1, 3, 6}},
-     {"\U00000000\U0000ac00", {0, 44032}, {1, 4}},
-     {"\U00000000\U00000308\U0000ac00", {0, 776, 44032}, {1, 3, 6}},
-     {"\U00000000\U0000ac01", {0, 44033}, {1, 4}},
-     {"\U00000000\U00000308\U0000ac01", {0, 776, 44033}, {1, 3, 6}},
-     {"\U00000000\U00001cf5", {0, 7413}, {1, 4}},
-     {"\U00000000\U00000308\U00001cf5", {0, 776, 7413}, {1, 3, 6}},
-     {"\U00000000\U00000915", {0, 2325}, {1, 4}},
-     {"\U00000000\U00000308\U00000915", {0, 776, 2325}, {1, 3, 6}},
-     {"\U00000000\U000000a9", {0, 169}, {1, 3}},
-     {"\U00000000\U00000308\U000000a9", {0, 776, 169}, {1, 3, 5}},
-     {"\U00000000\U00000020", {0, 32}, {1, 2}},
-     {"\U00000000\U00000308\U00000020", {0, 776, 32}, {1, 3, 4}},
-     {"\U00000000\U00000378", {0, 888}, {1, 3}},
-     {"\U00000000\U00000308\U00000378", {0, 776, 888}, {1, 3, 5}},
-     {"\U0000094d\U0000000d", {2381, 13}, {3, 4}},
-     {"\U0000094d\U00000308\U0000000d", {2381, 13}, {5, 6}},
-     {"\U0000094d\U0000000a", {2381, 10}, {3, 4}},
-     {"\U0000094d\U00000308\U0000000a", {2381, 10}, {5, 6}},
-     {"\U0000094d\U00000000", {2381, 0}, {3, 4}},
-     {"\U0000094d\U00000308\U00000000", {2381, 0}, {5, 6}},
-     {"\U0000094d\U0000094d", {2381}, {6}},
-     {"\U0000094d\U00000308\U0000094d", {2381}, {8}},
-     {"\U0000094d\U00000300", {2381}, {5}},
-     {"\U0000094d\U00000308\U00000300", {2381}, {7}},
-     {"\U0000094d\U0000200c", {2381}, {6}},
-     {"\U0000094d\U00000308\U0000200c", {2381}, {8}},
-     {"\U0000094d\U0000200d", {2381}, {6}},
-     {"\U0000094d\U00000308\U0000200d", {2381}, {8}},
-     {"\U0000094d\U0001f1e6", {2381, 127462}, {3, 7}},
-     {"\U0000094d\U00000308\U0001f1e6", {2381, 127462}, {5, 9}},
-     {"\U0000094d\U000006dd", {2381, 1757}, {3, 5}},
-     {"\U0000094d\U00000308\U000006dd", {2381, 1757}, {5, 7}},
-     {"\U0000094d\U00000903", {2381}, {6}},
-     {"\U0000094d\U00000308\U00000903", {2381}, {8}},
-     {"\U0000094d\U00001100", {2381, 4352}, {3, 6}},
-     {"\U0000094d\U00000308\U00001100", {2381, 4352}, {5, 8}},
-     {"\U0000094d\U00001160", {2381, 4448}, {3, 6}},
-     {"\U0000094d\U00000308\U00001160", {2381, 4448}, {5, 8}},
-     {"\U0000094d\U000011a8", {2381, 4520}, {3, 6}},
-     {"\U0000094d\U00000308\U000011a8", {2381, 4520}, {5, 8}},
-     {"\U0000094d\U0000ac00", {2381, 44032}, {3, 6}},
-     {"\U0000094d\U00000308\U0000ac00", {2381, 44032}, {5, 8}},
-     {"\U0000094d\U0000ac01", {2381, 44033}, {3, 6}},
-     {"\U0000094d\U00000308\U0000ac01", {2381, 44033}, {5, 8}},
-     {"\U0000094d\U00001cf5", {2381, 7413}, {3, 6}},
-     {"\U0000094d\U00000308\U00001cf5", {2381, 7413}, {5, 8}},
-     {"\U0000094d\U00000915", {2381}, {6}},
-     {"\U0000094d\U00000308\U00000915", {2381}, {8}},
-     {"\U0000094d\U000000a9", {2381, 169}, {3, 5}},
-     {"\U0000094d\U00000308\U000000a9", {2381, 169}, {5, 7}},
-     {"\U0000094d\U00000020", {2381, 32}, {3, 4}},
-     {"\U0000094d\U00000308\U00000020", {2381, 32}, {5, 6}},
-     {"\U0000094d\U00000378", {2381, 888}, {3, 5}},
-     {"\U0000094d\U00000308\U00000378", {2381, 888}, {5, 7}},
-     {"\U00000300\U0000000d", {768, 13}, {2, 3}},
-     {"\U00000300\U00000308\U0000000d", {768, 13}, {4, 5}},
-     {"\U00000300\U0000000a", {768, 10}, {2, 3}},
-     {"\U00000300\U00000308\U0000000a", {768, 10}, {4, 5}},
-     {"\U00000300\U00000000", {768, 0}, {2, 3}},
-     {"\U00000300\U00000308\U00000000", {768, 0}, {4, 5}},
-     {"\U00000300\U0000094d", {768}, {5}},
-     {"\U00000300\U00000308\U0000094d", {768}, {7}},
-     {"\U00000300\U00000300", {768}, {4}},
-     {"\U00000300\U00000308\U00000300", {768}, {6}},
-     {"\U00000300\U0000200c", {768}, {5}},
-     {"\U00000300\U00000308\U0000200c", {768}, {7}},
-     {"\U00000300\U0000200d", {768}, {5}},
-     {"\U00000300\U00000308\U0000200d", {768}, {7}},
-     {"\U00000300\U0001f1e6", {768, 127462}, {2, 6}},
-     {"\U00000300\U00000308\U0001f1e6", {768, 127462}, {4, 8}},
-     {"\U00000300\U000006dd", {768, 1757}, {2, 4}},
-     {"\U00000300\U00000308\U000006dd", {768, 1757}, {4, 6}},
-     {"\U00000300\U00000903", {768}, {5}},
-     {"\U00000300\U00000308\U00000903", {768}, {7}},
-     {"\U00000300\U00001100", {768, 4352}, {2, 5}},
-     {"\U00000300\U00000308\U00001100", {768, 4352}, {4, 7}},
-     {"\U00000300\U00001160", {768, 4448}, {2, 5}},
-     {"\U00000300\U00000308\U00001160", {768, 4448}, {4, 7}},
-     {"\U00000300\U000011a8", {768, 4520}, {2, 5}},
-     {"\U00000300\U00000308\U000011a8", {768, 4520}, {4, 7}},
-     {"\U00000300\U0000ac00", {768, 44032}, {2, 5}},
-     {"\U00000300\U00000308\U0000ac00", {768, 44032}, {4, 7}},
-     {"\U00000300\U0000ac01", {768, 44033}, {2, 5}},
-     {"\U00000300\U00000308\U0000ac01", {768, 44033}, {4, 7}},
-     {"\U00000300\U00001cf5", {768, 7413}, {2, 5}},
-     {"\U00000300\U00000308\U00001cf5", {768, 7413}, {4, 7}},
-     {"\U00000300\U00000915", {768, 2325}, {2, 5}},
-     {"\U00000300\U00000308\U00000915", {768, 2325}, {4, 7}},
-     {"\U00000300\U000000a9", {768, 169}, {2, 4}},
-     {"\U00000300\U00000308\U000000a9", {768, 169}, {4, 6}},
-     {"\U00000300\U00000020", {768, 32}, {2, 3}},
-     {"\U00000300\U00000308\U00000020", {768, 32}, {4, 5}},
-     {"\U00000300\U00000378", {768, 888}, {2, 4}},
-     {"\U00000300\U00000308\U00000378", {768, 888}, {4, 6}},
-     {"\U0000200c\U0000000d", {8204, 13}, {3, 4}},
-     {"\U0000200c\U00000308\U0000000d", {8204, 13}, {5, 6}},
-     {"\U0000200c\U0000000a", {8204, 10}, {3, 4}},
-     {"\U0000200c\U00000308\U0000000a", {8204, 10}, {5, 6}},
-     {"\U0000200c\U00000000", {8204, 0}, {3, 4}},
-     {"\U0000200c\U00000308\U00000000", {8204, 0}, {5, 6}},
-     {"\U0000200c\U0000094d", {8204}, {6}},
-     {"\U0000200c\U00000308\U0000094d", {8204}, {8}},
-     {"\U0000200c\U00000300", {8204}, {5}},
-     {"\U0000200c\U00000308\U00000300", {8204}, {7}},
-     {"\U0000200c\U0000200c", {8204}, {6}},
-     {"\U0000200c\U00000308\U0000200c", {8204}, {8}},
-     {"\U0000200c\U0000200d", {8204}, {6}},
-     {"\U0000200c\U00000308\U0000200d", {8204}, {8}},
-     {"\U0000200c\U0001f1e6", {8204, 127462}, {3, 7}},
-     {"\U0000200c\U00000308\U0001f1e6", {8204, 127462}, {5, 9}},
-     {"\U0000200c\U000006dd", {8204, 1757}, {3, 5}},
-     {"\U0000200c\U00000308\U000006dd", {8204, 1757}, {5, 7}},
-     {"\U0000200c\U00000903", {8204}, {6}},
-     {"\U0000200c\U00000308\U00000903", {8204}, {8}},
-     {"\U0000200c\U00001100", {8204, 4352}, {3, 6}},
-     {"\U0000200c\U00000308\U00001100", {8204, 4352}, {5, 8}},
-     {"\U0000200c\U00001160", {8204, 4448}, {3, 6}},
-     {"\U0000200c\U00000308\U00001160", {8204, 4448}, {5, 8}},
-     {"\U0000200c\U000011a8", {8204, 4520}, {3, 6}},
-     {"\U0000200c\U00000308\U000011a8", {8204, 4520}, {5, 8}},
-     {"\U0000200c\U0000ac00", {8204, 44032}, {3, 6}},
-     {"\U0000200c\U00000308\U0000ac00", {8204, 44032}, {5, 8}},
-     {"\U0000200c\U0000ac01", {8204, 44033}, {3, 6}},
-     {"\U0000200c\U00000308\U0000ac01", {8204, 44033}, {5, 8}},
-     {"\U0000200c\U00001cf5", {8204, 7413}, {3, 6}},
-     {"\U0000200c\U00000308\U00001cf5", {8204, 7413}, {5, 8}},
-     {"\U0000200c\U00000915", {8204, 2325}, {3, 6}},
-     {"\U0000200c\U00000308\U00000915", {8204, 2325}, {5, 8}},
-     {"\U0000200c\U000000a9", {8204, 169}, {3, 5}},
-     {"\U0000200c\U00000308\U000000a9", {8204, 169}, {5, 7}},
-     {"\U0000200c\U00000020", {8204, 32}, {3, 4}},
-     {"\U0000200c\U00000308\U00000020", {8204, 32}, {5, 6}},
-     {"\U0000200c\U00000378", {8204, 888}, {3, 5}},
-     {"\U0000200c\U00000308\U00000378", {8204, 888}, {5, 7}},
-     {"\U0000200d\U0000000d", {8205, 13}, {3, 4}},
-     {"\U0000200d\U00000308\U0000000d", {8205, 13}, {5, 6}},
-     {"\U0000200d\U0000000a", {8205, 10}, {3, 4}},
-     {"\U0000200d\U00000308\U0000000a", {8205, 10}, {5, 6}},
-     {"\U0000200d\U00000000", {8205, 0}, {3, 4}},
-     {"\U0000200d\U00000308\U00000000", {8205, 0}, {5, 6}},
-     {"\U0000200d\U0000094d", {8205}, {6}},
-     {"\U0000200d\U00000308\U0000094d", {8205}, {8}},
-     {"\U0000200d\U00000300", {8205}, {5}},
-     {"\U0000200d\U00000308\U00000300", {8205}, {7}},
-     {"\U0000200d\U0000200c", {8205}, {6}},
-     {"\U0000200d\U00000308\U0000200c", {8205}, {8}},
-     {"\U0000200d\U0000200d", {8205}, {6}},
-     {"\U0000200d\U00000308\U0000200d", {8205}, {8}},
-     {"\U0000200d\U0001f1e6", {8205, 127462}, {3, 7}},
-     {"\U0000200d\U00000308\U0001f1e6", {8205, 127462}, {5, 9}},
-     {"\U0000200d\U000006dd", {8205, 1757}, {3, 5}},
-     {"\U0000200d\U00000308\U000006dd", {8205, 1757}, {5, 7}},
-     {"\U0000200d\U00000903", {8205}, {6}},
-     {"\U0000200d\U00000308\U00000903", {8205}, {8}},
-     {"\U0000200d\U00001100", {8205, 4352}, {3, 6}},
-     {"\U0000200d\U00000308\U00001100", {8205, 4352}, {5, 8}},
-     {"\U0000200d\U00001160", {8205, 4448}, {3, 6}},
-     {"\U0000200d\U00000308\U00001160", {8205, 4448}, {5, 8}},
-     {"\U0000200d\U000011a8", {8205, 4520}, {3, 6}},
-     {"\U0000200d\U00000308\U000011a8", {8205, 4520}, {5, 8}},
-     {"\U0000200d\U0000ac00", {8205, 44032}, {3, 6}},
-     {"\U0000200d\U00000308\U0000ac00", {8205, 44032}, {5, 8}},
-     {"\U0000200d\U0000ac01", {8205, 44033}, {3, 6}},
-     {"\U0000200d\U00000308\U0000ac01", {8205, 44033}, {5, 8}},
-     {"\U0000200d\U00001cf5", {8205, 7413}, {3, 6}},
-     {"\U0000200d\U00000308\U00001cf5", {8205, 7413}, {5, 8}},
-     {"\U0000200d\U00000915", {8205, 2325}, {3, 6}},
-     {"\U0000200d\U00000308\U00000915", {8205, 2325}, {5, 8}},
-     {"\U0000200d\U000000a9", {8205, 169}, {3, 5}},
-     {"\U0000200d\U00000308\U000000a9", {8205, 169}, {5, 7}},
-     {"\U0000200d\U00000020", {8205, 32}, {3, 4}},
-     {"\U0000200d\U00000308\U00000020", {8205, 32}, {5, 6}},
-     {"\U0000200d\U00000378", {8205, 888}, {3, 5}},
-     {"\U0000200d\U00000308\U00000378", {8205, 888}, {5, 7}},
-     {"\U0001f1e6\U0000000d", {127462, 13}, {4, 5}},
-     {"\U0001f1e6\U00000308\U0000000d", {127462, 13}, {6, 7}},
-     {"\U0001f1e6\U0000000a", {127462, 10}, {4, 5}},
-     {"\U0001f1e6\U00000308\U0000000a", {127462, 10}, {6, 7}},
-     {"\U0001f1e6\U00000000", {127462, 0}, {4, 5}},
-     {"\U0001f1e6\U00000308\U00000000", {127462, 0}, {6, 7}},
-     {"\U0001f1e6\U0000094d", {127462}, {7}},
-     {"\U0001f1e6\U00000308\U0000094d", {127462}, {9}},
-     {"\U0001f1e6\U00000300", {127462}, {6}},
-     {"\U0001f1e6\U00000308\U00000300", {127462}, {8}},
-     {"\U0001f1e6\U0000200c", {127462}, {7}},
-     {"\U0001f1e6\U00000308\U0000200c", {127462}, {9}},
-     {"\U0001f1e6\U0000200d", {127462}, {7}},
-     {"\U0001f1e6\U00000308\U0000200d", {127462}, {9}},
-     {"\U0001f1e6\U0001f1e6", {127462}, {8}},
-     {"\U0001f1e6\U00000308\U0001f1e6", {127462, 127462}, {6, 10}},
-     {"\U0001f1e6\U000006dd", {127462, 1757}, {4, 6}},
-     {"\U0001f1e6\U00000308\U000006dd", {127462, 1757}, {6, 8}},
-     {"\U0001f1e6\U00000903", {127462}, {7}},
-     {"\U0001f1e6\U00000308\U00000903", {127462}, {9}},
-     {"\U0001f1e6\U00001100", {127462, 4352}, {4, 7}},
-     {"\U0001f1e6\U00000308\U00001100", {127462, 4352}, {6, 9}},
-     {"\U0001f1e6\U00001160", {127462, 4448}, {4, 7}},
-     {"\U0001f1e6\U00000308\U00001160", {127462, 4448}, {6, 9}},
-     {"\U0001f1e6\U000011a8", {127462, 4520}, {4, 7}},
-     {"\U0001f1e6\U00000308\U000011a8", {127462, 4520}, {6, 9}},
-     {"\U0001f1e6\U0000ac00", {127462, 44032}, {4, 7}},
-     {"\U0001f1e6\U00000308\U0000ac00", {127462, 44032}, {6, 9}},
-     {"\U0001f1e6\U0000ac01", {127462, 44033}, {4, 7}},
-     {"\U0001f1e6\U00000308\U0000ac01", {127462, 44033}, {6, 9}},
-     {"\U0001f1e6\U00001cf5", {127462, 7413}, {4, 7}},
-     {"\U0001f1e6\U00000308\U00001cf5", {127462, 7413}, {6, 9}},
-     {"\U0001f1e6\U00000915", {127462, 2325}, {4, 7}},
-     {"\U0001f1e6\U00000308\U00000915", {127462, 2325}, {6, 9}},
-     {"\U0001f1e6\U000000a9", {127462, 169}, {4, 6}},
-     {"\U0001f1e6\U00000308\U000000a9", {127462, 169}, {6, 8}},
-     {"\U0001f1e6\U00000020", {127462, 32}, {4, 5}},
-     {"\U0001f1e6\U00000308\U00000020", {127462, 32}, {6, 7}},
-     {"\U0001f1e6\U00000378", {127462, 888}, {4, 6}},
-     {"\U0001f1e6\U00000308\U00000378", {127462, 888}, {6, 8}},
-     {"\U000006dd\U0000000d", {1757, 13}, {2, 3}},
-     {"\U000006dd\U00000308\U0000000d", {1757, 13}, {4, 5}},
-     {"\U000006dd\U0000000a", {1757, 10}, {2, 3}},
-     {"\U000006dd\U00000308\U0000000a", {1757, 10}, {4, 5}},
-     {"\U000006dd\U00000000", {1757, 0}, {2, 3}},
-     {"\U000006dd\U00000308\U00000000", {1757, 0}, {4, 5}},
-     {"\U000006dd\U0000094d", {1757}, {5}},
-     {"\U000006dd\U00000308\U0000094d", {1757}, {7}},
-     {"\U000006dd\U00000300", {1757}, {4}},
-     {"\U000006dd\U00000308\U00000300", {1757}, {6}},
-     {"\U000006dd\U0000200c", {1757}, {5}},
-     {"\U000006dd\U00000308\U0000200c", {1757}, {7}},
-     {"\U000006dd\U0000200d", {1757}, {5}},
-     {"\U000006dd\U00000308\U0000200d", {1757}, {7}},
-     {"\U000006dd\U0001f1e6", {1757}, {6}},
-     {"\U000006dd\U00000308\U0001f1e6", {1757, 127462}, {4, 8}},
-     {"\U000006dd\U000006dd", {1757}, {4}},
-     {"\U000006dd\U00000308\U000006dd", {1757, 1757}, {4, 6}},
-     {"\U000006dd\U00000903", {1757}, {5}},
-     {"\U000006dd\U00000308\U00000903", {1757}, {7}},
-     {"\U000006dd\U00001100", {1757}, {5}},
-     {"\U000006dd\U00000308\U00001100", {1757, 4352}, {4, 7}},
-     {"\U000006dd\U00001160", {1757}, {5}},
-     {"\U000006dd\U00000308\U00001160", {1757, 4448}, {4, 7}},
-     {"\U000006dd\U000011a8", {1757}, {5}},
-     {"\U000006dd\U00000308\U000011a8", {1757, 4520}, {4, 7}},
-     {"\U000006dd\U0000ac00", {1757}, {5}},
-     {"\U000006dd\U00000308\U0000ac00", {1757, 44032}, {4, 7}},
-     {"\U000006dd\U0000ac01", {1757}, {5}},
-     {"\U000006dd\U00000308\U0000ac01", {1757, 44033}, {4, 7}},
-     {"\U000006dd\U00001cf5", {1757}, {5}},
-     {"\U000006dd\U00000308\U00001cf5", {1757, 7413}, {4, 7}},
-     {"\U000006dd\U00000915", {1757}, {5}},
-     {"\U000006dd\U00000308\U00000915", {1757, 2325}, {4, 7}},
-     {"\U000006dd\U000000a9", {1757}, {4}},
-     {"\U000006dd\U00000308\U000000a9", {1757, 169}, {4, 6}},
-     {"\U000006dd\U00000020", {1757}, {3}},
-     {"\U000006dd\U00000308\U00000020", {1757, 32}, {4, 5}},
-     {"\U000006dd\U00000378", {1757}, {4}},
-     {"\U000006dd\U00000308\U00000378", {1757, 888}, {4, 6}},
-     {"\U00000903\U0000000d", {2307, 13}, {3, 4}},
-     {"\U00000903\U00000308\U0000000d", {2307, 13}, {5, 6}},
-     {"\U00000903\U0000000a", {2307, 10}, {3, 4}},
-     {"\U00000903\U00000308\U0000000a", {2307, 10}, {5, 6}},
-     {"\U00000903\U00000000", {2307, 0}, {3, 4}},
-     {"\U00000903\U00000308\U00000000", {2307, 0}, {5, 6}},
-     {"\U00000903\U0000094d", {2307}, {6}},
-     {"\U00000903\U00000308\U0000094d", {2307}, {8}},
-     {"\U00000903\U00000300", {2307}, {5}},
-     {"\U00000903\U00000308\U00000300", {2307}, {7}},
-     {"\U00000903\U0000200c", {2307}, {6}},
-     {"\U00000903\U00000308\U0000200c", {2307}, {8}},
-     {"\U00000903\U0000200d", {2307}, {6}},
-     {"\U00000903\U00000308\U0000200d", {2307}, {8}},
-     {"\U00000903\U0001f1e6", {2307, 127462}, {3, 7}},
-     {"\U00000903\U00000308\U0001f1e6", {2307, 127462}, {5, 9}},
-     {"\U00000903\U000006dd", {2307, 1757}, {3, 5}},
-     {"\U00000903\U00000308\U000006dd", {2307, 1757}, {5, 7}},
-     {"\U00000903\U00000903", {2307}, {6}},
-     {"\U00000903\U00000308\U00000903", {2307}, {8}},
-     {"\U00000903\U00001100", {2307, 4352}, {3, 6}},
-     {"\U00000903\U00000308\U00001100", {2307, 4352}, {5, 8}},
-     {"\U00000903\U00001160", {2307, 4448}, {3, 6}},
-     {"\U00000903\U00000308\U00001160", {2307, 4448}, {5, 8}},
-     {"\U00000903\U000011a8", {2307, 4520}, {3, 6}},
-     {"\U00000903\U00000308\U000011a8", {2307, 4520}, {5, 8}},
-     {"\U00000903\U0000ac00", {2307, 44032}, {3, 6}},
-     {"\U00000903\U00000308\U0000ac00", {2307, 44032}, {5, 8}},
-     {"\U00000903\U0000ac01", {2307, 44033}, {3, 6}},
-     {"\U00000903\U00000308\U0000ac01", {2307, 44033}, {5, 8}},
-     {"\U00000903\U00001cf5", {2307, 7413}, {3, 6}},
-     {"\U00000903\U00000308\U00001cf5", {2307, 7413}, {5, 8}},
-     {"\U00000903\U00000915", {2307, 2325}, {3, 6}},
-     {"\U00000903\U00000308\U00000915", {2307, 2325}, {5, 8}},
-     {"\U00000903\U000000a9", {2307, 169}, {3, 5}},
-     {"\U00000903\U00000308\U000000a9", {2307, 169}, {5, 7}},
-     {"\U00000903\U00000020", {2307, 32}, {3, 4}},
-     {"\U00000903\U00000308\U00000020", {2307, 32}, {5, 6}},
-     {"\U00000903\U00000378", {2307, 888}, {3, 5}},
-     {"\U00000903\U00000308\U00000378", {2307, 888}, {5, 7}},
-     {"\U00001100\U0000000d", {4352, 13}, {3, 4}},
-     {"\U00001100\U00000308\U0000000d", {4352, 13}, {5, 6}},
-     {"\U00001100\U0000000a", {4352, 10}, {3, 4}},
-     {"\U00001100\U00000308\U0000000a", {4352, 10}, {5, 6}},
-     {"\U00001100\U00000000", {4352, 0}, {3, 4}},
-     {"\U00001100\U00000308\U00000000", {4352, 0}, {5, 6}},
-     {"\U00001100\U0000094d", {4352}, {6}},
-     {"\U00001100\U00000308\U0000094d", {4352}, {8}},
-     {"\U00001100\U00000300", {4352}, {5}},
-     {"\U00001100\U00000308\U00000300", {4352}, {7}},
-     {"\U00001100\U0000200c", {4352}, {6}},
-     {"\U00001100\U00000308\U0000200c", {4352}, {8}},
-     {"\U00001100\U0000200d", {4352}, {6}},
-     {"\U00001100\U00000308\U0000200d", {4352}, {8}},
-     {"\U00001100\U0001f1e6", {4352, 127462}, {3, 7}},
-     {"\U00001100\U00000308\U0001f1e6", {4352, 127462}, {5, 9}},
-     {"\U00001100\U000006dd", {4352, 1757}, {3, 5}},
-     {"\U00001100\U00000308\U000006dd", {4352, 1757}, {5, 7}},
-     {"\U00001100\U00000903", {4352}, {6}},
-     {"\U00001100\U00000308\U00000903", {4352}, {8}},
-     {"\U00001100\U00001100", {4352}, {6}},
-     {"\U00001100\U00000308\U00001100", {4352, 4352}, {5, 8}},
-     {"\U00001100\U00001160", {4352}, {6}},
-     {"\U00001100\U00000308\U00001160", {4352, 4448}, {5, 8}},
-     {"\U00001100\U000011a8", {4352, 4520}, {3, 6}},
-     {"\U00001100\U00000308\U000011a8", {4352, 4520}, {5, 8}},
-     {"\U00001100\U0000ac00", {4352}, {6}},
-     {"\U00001100\U00000308\U0000ac00", {4352, 44032}, {5, 8}},
-     {"\U00001100\U0000ac01", {4352}, {6}},
-     {"\U00001100\U00000308\U0000ac01", {4352, 44033}, {5, 8}},
-     {"\U00001100\U00001cf5", {4352, 7413}, {3, 6}},
-     {"\U00001100\U00000308\U00001cf5", {4352, 7413}, {5, 8}},
-     {"\U00001100\U00000915", {4352, 2325}, {3, 6}},
-     {"\U00001100\U00000308\U00000915", {4352, 2325}, {5, 8}},
-     {"\U00001100\U000000a9", {4352, 169}, {3, 5}},
-     {"\U00001100\U00000308\U000000a9", {4352, 169}, {5, 7}},
-     {"\U00001100\U00000020", {4352, 32}, {3, 4}},
-     {"\U00001100\U00000308\U00000020", {4352, 32}, {5, 6}},
-     {"\U00001100\U00000378", {4352, 888}, {3, 5}},
-     {"\U00001100\U00000308\U00000378", {4352, 888}, {5, 7}},
-     {"\U00001160\U0000000d", {4448, 13}, {3, 4}},
-     {"\U00001160\U00000308\U0000000d", {4448, 13}, {5, 6}},
-     {"\U00001160\U0000000a", {4448, 10}, {3, 4}},
-     {"\U00001160\U00000308\U0000000a", {4448, 10}, {5, 6}},
-     {"\U00001160\U00000000", {4448, 0}, {3, 4}},
-     {"\U00001160\U00000308\U00000000", {4448, 0}, {5, 6}},
-     {"\U00001160\U0000094d", {4448}, {6}},
-     {"\U00001160\U00000308\U0000094d", {4448}, {8}},
-     {"\U00001160\U00000300", {4448}, {5}},
-     {"\U00001160\U00000308\U00000300", {4448}, {7}},
-     {"\U00001160\U0000200c", {4448}, {6}},
-     {"\U00001160\U00000308\U0000200c", {4448}, {8}},
-     {"\U00001160\U0000200d", {4448}, {6}},
-     {"\U00001160\U00000308\U0000200d", {4448}, {8}},
-     {"\U00001160\U0001f1e6", {4448, 127462}, {3, 7}},
-     {"\U00001160\U00000308\U0001f1e6", {4448, 127462}, {5, 9}},
-     {"\U00001160\U000006dd", {4448, 1757}, {3, 5}},
-     {"\U00001160\U00000308\U000006dd", {4448, 1757}, {5, 7}},
-     {"\U00001160\U00000903", {4448}, {6}},
-     {"\U00001160\U00000308\U00000903", {4448}, {8}},
-     {"\U00001160\U00001100", {4448, 4352}, {3, 6}},
-     {"\U00001160\U00000308\U00001100", {4448, 4352}, {5, 8}},
-     {"\U00001160\U00001160", {4448}, {6}},
-     {"\U00001160\U00000308\U00001160", {4448, 4448}, {5, 8}},
-     {"\U00001160\U000011a8", {4448}, {6}},
-     {"\U00001160\U00000308\U000011a8", {4448, 4520}, {5, 8}},
-     {"\U00001160\U0000ac00", {4448, 44032}, {3, 6}},
-     {"\U00001160\U00000308\U0000ac00", {4448, 44032}, {5, 8}},
-     {"\U00001160\U0000ac01", {4448, 44033}, {3, 6}},
-     {"\U00001160\U00000308\U0000ac01", {4448, 44033}, {5, 8}},
-     {"\U00001160\U00001cf5", {4448, 7413}, {3, 6}},
-     {"\U00001160\U00000308\U00001cf5", {4448, 7413}, {5, 8}},
-     {"\U00001160\U00000915", {4448, 2325}, {3, 6}},
-     {"\U00001160\U00000308\U00000915", {4448, 2325}, {5, 8}},
-     {"\U00001160\U000000a9", {4448, 169}, {3, 5}},
-     {"\U00001160\U00000308\U000000a9", {4448, 169}, {5, 7}},
-     {"\U00001160\U00000020", {4448, 32}, {3, 4}},
-     {"\U00001160\U00000308\U00000020", {4448, 32}, {5, 6}},
-     {"\U00001160\U00000378", {4448, 888}, {3, 5}},
-     {"\U00001160\U00000308\U00000378", {4448, 888}, {5, 7}},
-     {"\U000011a8\U0000000d", {4520, 13}, {3, 4}},
-     {"\U000011a8\U00000308\U0000000d", {4520, 13}, {5, 6}},
-     {"\U000011a8\U0000000a", {4520, 10}, {3, 4}},
-     {"\U000011a8\U00000308\U0000000a", {4520, 10}, {5, 6}},
-     {"\U000011a8\U00000000", {4520, 0}, {3, 4}},
-     {"\U000011a8\U00000308\U00000000", {4520, 0}, {5, 6}},
-     {"\U000011a8\U0000094d", {4520}, {6}},
-     {"\U000011a8\U00000308\U0000094d", {4520}, {8}},
-     {"\U000011a8\U00000300", {4520}, {5}},
-     {"\U000011a8\U00000308\U00000300", {4520}, {7}},
-     {"\U000011a8\U0000200c", {4520}, {6}},
-     {"\U000011a8\U00000308\U0000200c", {4520}, {8}},
-     {"\U000011a8\U0000200d", {4520}, {6}},
-     {"\U000011a8\U00000308\U0000200d", {4520}, {8}},
-     {"\U000011a8\U0001f1e6", {4520, 127462}, {3, 7}},
-     {"\U000011a8\U00000308\U0001f1e6", {4520, 127462}, {5, 9}},
-     {"\U000011a8\U000006dd", {4520, 1757}, {3, 5}},
-     {"\U000011a8\U00000308\U000006dd", {4520, 1757}, {5, 7}},
-     {"\U000011a8\U00000903", {4520}, {6}},
-     {"\U000011a8\U00000308\U00000903", {4520}, {8}},
-     {"\U000011a8\U00001100", {4520, 4352}, {3, 6}},
-     {"\U000011a8\U00000308\U00001100", {4520, 4352}, {5, 8}},
-     {"\U000011a8\U00001160", {4520, 4448}, {3, 6}},
-     {"\U000011a8\U00000308\U00001160", {4520, 4448}, {5, 8}},
-     {"\U000011a8\U000011a8", {4520}, {6}},
-     {"\U000011a8\U00000308\U000011a8", {4520, 4520}, {5, 8}},
-     {"\U000011a8\U0000ac00", {4520, 44032}, {3, 6}},
-     {"\U000011a8\U00000308\U0000ac00", {4520, 44032}, {5, 8}},
-     {"\U000011a8\U0000ac01", {4520, 44033}, {3, 6}},
-     {"\U000011a8\U00000308\U0000ac01", {4520, 44033}, {5, 8}},
-     {"\U000011a8\U00001cf5", {4520, 7413}, {3, 6}},
-     {"\U000011a8\U00000308\U00001cf5", {4520, 7413}, {5, 8}},
-     {"\U000011a8\U00000915", {4520, 2325}, {3, 6}},
-     {"\U000011a8\U00000308\U00000915", {4520, 2325}, {5, 8}},
-     {"\U000011a8\U000000a9", {4520, 169}, {3, 5}},
-     {"\U000011a8\U00000308\U000000a9", {4520, 169}, {5, 7}},
-     {"\U000011a8\U00000020", {4520, 32}, {3, 4}},
-     {"\U000011a8\U00000308\U00000020", {4520, 32}, {5, 6}},
-     {"\U000011a8\U00000378", {4520, 888}, {3, 5}},
-     {"\U000011a8\U00000308\U00000378", {4520, 888}, {5, 7}},
-     {"\U0000ac00\U0000000d", {44032, 13}, {3, 4}},
-     {"\U0000ac00\U00000308\U0000000d", {44032, 13}, {5, 6}},
-     {"\U0000ac00\U0000000a", {44032, 10}, {3, 4}},
-     {"\U0000ac00\U00000308\U0000000a", {44032, 10}, {5, 6}},
-     {"\U0000ac00\U00000000", {44032, 0}, {3, 4}},
-     {"\U0000ac00\U00000308\U00000000", {44032, 0}, {5, 6}},
-     {"\U0000ac00\U0000094d", {44032}, {6}},
-     {"\U0000ac00\U00000308\U0000094d", {44032}, {8}},
-     {"\U0000ac00\U00000300", {44032}, {5}},
-     {"\U0000ac00\U00000308\U00000300", {44032}, {7}},
-     {"\U0000ac00\U0000200c", {44032}, {6}},
-     {"\U0000ac00\U00000308\U0000200c", {44032}, {8}},
-     {"\U0000ac00\U0000200d", {44032}, {6}},
-     {"\U0000ac00\U00000308\U0000200d", {44032}, {8}},
-     {"\U0000ac00\U0001f1e6", {44032, 127462}, {3, 7}},
-     {"\U0000ac00\U00000308\U0001f1e6", {44032, 127462}, {5, 9}},
-     {"\U0000ac00\U000006dd", {44032, 1757}, {3, 5}},
-     {"\U0000ac00\U00000308\U000006dd", {44032, 1757}, {5, 7}},
-     {"\U0000ac00\U00000903", {44032}, {6}},
-     {"\U0000ac00\U00000308\U00000903", {44032}, {8}},
-     {"\U0000ac00\U00001100", {44032, 4352}, {3, 6}},
-     {"\U0000ac00\U00000308\U00001100", {44032, 4352}, {5, 8}},
-     {"\U0000ac00\U00001160", {44032}, {6}},
-     {"\U0000ac00\U00000308\U00001160", {44032, 4448}, {5, 8}},
-     {"\U0000ac00\U000011a8", {44032}, {6}},
-     {"\U0000ac00\U00000308\U000011a8", {44032, 4520}, {5, 8}},
-     {"\U0000ac00\U0000ac00", {44032, 44032}, {3, 6}},
-     {"\U0000ac00\U00000308\U0000ac00", {44032, 44032}, {5, 8}},
-     {"\U0000ac00\U0000ac01", {44032, 44033}, {3, 6}},
-     {"\U0000ac00\U00000308\U0000ac01", {44032, 44033}, {5, 8}},
-     {"\U0000ac00\U00001cf5", {44032, 7413}, {3, 6}},
-     {"\U0000ac00\U00000308\U00001cf5", {44032, 7413}, {5, 8}},
-     {"\U0000ac00\U00000915", {44032, 2325}, {3, 6}},
-     {"\U0000ac00\U00000308\U00000915", {44032, 2325}, {5, 8}},
-     {"\U0000ac00\U000000a9", {44032, 169}, {3, 5}},
-     {"\U0000ac00\U00000308\U000000a9", {44032, 169}, {5, 7}},
-     {"\U0000ac00\U00000020", {44032, 32}, {3, 4}},
-     {"\U0000ac00\U00000308\U00000020", {44032, 32}, {5, 6}},
-     {"\U0000ac00\U00000378", {44032, 888}, {3, 5}},
-     {"\U0000ac00\U00000308\U00000378", {44032, 888}, {5, 7}},
-     {"\U0000ac01\U0000000d", {44033, 13}, {3, 4}},
-     {"\U0000ac01\U00000308\U0000000d", {44033, 13}, {5, 6}},
-     {"\U0000ac01\U0000000a", {44033, 10}, {3, 4}},
-     {"\U0000ac01\U00000308\U0000000a", {44033, 10}, {5, 6}},
-     {"\U0000ac01\U00000000", {44033, 0}, {3, 4}},
-     {"\U0000ac01\U00000308\U00000000", {44033, 0}, {5, 6}},
-     {"\U0000ac01\U0000094d", {44033}, {6}},
-     {"\U0000ac01\U00000308\U0000094d", {44033}, {8}},
-     {"\U0000ac01\U00000300", {44033}, {5}},
-     {"\U0000ac01\U00000308\U00000300", {44033}, {7}},
-     {"\U0000ac01\U0000200c", {44033}, {6}},
-     {"\U0000ac01\U00000308\U0000200c", {44033}, {8}},
-     {"\U0000ac01\U0000200d", {44033}, {6}},
-     {"\U0000ac01\U00000308\U0000200d", {44033}, {8}},
-     {"\U0000ac01\U0001f1e6", {44033, 127462}, {3, 7}},
-     {"\U0000ac01\U00000308\U0001f1e6", {44033, 127462}, {5, 9}},
-     {"\U0000ac01\U000006dd", {44033, 1757}, {3, 5}},
-     {"\U0000ac01\U00000308\U000006dd", {44033, 1757}, {5, 7}},
-     {"\U0000ac01\U00000903", {44033}, {6}},
-     {"\U0000ac01\U00000308\U00000903", {44033}, {8}},
-     {"\U0000ac01\U00001100", {44033, 4352}, {3, 6}},
-     {"\U0000ac01\U00000308\U00001100", {44033, 4352}, {5, 8}},
-     {"\U0000ac01\U00001160", {44033, 4448}, {3, 6}},
-     {"\U0000ac01\U00000308\U00001160", {44033, 4448}, {5, 8}},
-     {"\U0000ac01\U000011a8", {44033}, {6}},
-     {"\U0000ac01\U00000308\U000011a8", {44033, 4520}, {5, 8}},
-     {"\U0000ac01\U0000ac00", {44033, 44032}, {3, 6}},
-     {"\U0000ac01\U00000308\U0000ac00", {44033, 44032}, {5, 8}},
-     {"\U0000ac01\U0000ac01", {44033, 44033}, {3, 6}},
-     {"\U0000ac01\U00000308\U0000ac01", {44033, 44033}, {5, 8}},
-     {"\U0000ac01\U00001cf5", {44033, 7413}, {3, 6}},
-     {"\U0000ac01\U00000308\U00001cf5", {44033, 7413}, {5, 8}},
-     {"\U0000ac01\U00000915", {44033, 2325}, {3, 6}},
-     {"\U0000ac01\U00000308\U00000915", {44033, 2325}, {5, 8}},
-     {"\U0000ac01\U000000a9", {44033, 169}, {3, 5}},
-     {"\U0000ac01\U00000308\U000000a9", {44033, 169}, {5, 7}},
-     {"\U0000ac01\U00000020", {44033, 32}, {3, 4}},
-     {"\U0000ac01\U00000308\U00000020", {44033, 32}, {5, 6}},
-     {"\U0000ac01\U00000378", {44033, 888}, {3, 5}},
-     {"\U0000ac01\U00000308\U00000378", {44033, 888}, {5, 7}},
-     {"\U00001cf5\U0000000d", {7413, 13}, {3, 4}},
-     {"\U00001cf5\U00000308\U0000000d", {7413, 13}, {5, 6}},
-     {"\U00001cf5\U0000000a", {7413, 10}, {3, 4}},
-     {"\U00001cf5\U00000308\U0000000a", {7413, 10}, {5, 6}},
-     {"\U00001cf5\U00000000", {7413, 0}, {3, 4}},
-     {"\U00001cf5\U00000308\U00000000", {7413, 0}, {5, 6}},
-     {"\U00001cf5\U0000094d", {7413}, {6}},
-     {"\U00001cf5\U00000308\U0000094d", {7413}, {8}},
-     {"\U00001cf5\U00000300", {7413}, {5}},
-     {"\U00001cf5\U00000308\U00000300", {7413}, {7}},
-     {"\U00001cf5\U0000200c", {7413}, {6}},
-     {"\U00001cf5\U00000308\U0000200c", {7413}, {8}},
-     {"\U00001cf5\U0000200d", {7413}, {6}},
-     {"\U00001cf5\U00000308\U0000200d", {7413}, {8}},
-     {"\U00001cf5\U0001f1e6", {7413, 127462}, {3, 7}},
-     {"\U00001cf5\U00000308\U0001f1e6", {7413, 127462}, {5, 9}},
-     {"\U00001cf5\U000006dd", {7413, 1757}, {3, 5}},
-     {"\U00001cf5\U00000308\U000006dd", {7413, 1757}, {5, 7}},
-     {"\U00001cf5\U00000903", {7413}, {6}},
-     {"\U00001cf5\U00000308\U00000903", {7413}, {8}},
-     {"\U00001cf5\U00001100", {7413, 4352}, {3, 6}},
-     {"\U00001cf5\U00000308\U00001100", {7413, 4352}, {5, 8}},
-     {"\U00001cf5\U00001160", {7413, 4448}, {3, 6}},
-     {"\U00001cf5\U00000308\U00001160", {7413, 4448}, {5, 8}},
-     {"\U00001cf5\U000011a8", {7413, 4520}, {3, 6}},
-     {"\U00001cf5\U00000308\U000011a8", {7413, 4520}, {5, 8}},
-     {"\U00001cf5\U0000ac00", {7413, 44032}, {3, 6}},
-     {"\U00001cf5\U00000308\U0000ac00", {7413, 44032}, {5, 8}},
-     {"\U00001cf5\U0000ac01", {7413, 44033}, {3, 6}},
-     {"\U00001cf5\U00000308\U0000ac01", {7413, 44033}, {5, 8}},
-     {"\U00001cf5\U00001cf5", {7413, 7413}, {3, 6}},
-     {"\U00001cf5\U00000308\U00001cf5", {7413, 7413}, {5, 8}},
-     {"\U00001cf5\U00000915", {7413}, {6}},
-     {"\U00001cf5\U00000308\U00000915", {7413}, {8}},
-     {"\U00001cf5\U000000a9", {7413, 169}, {3, 5}},
-     {"\U00001cf5\U00000308\U000000a9", {7413, 169}, {5, 7}},
-     {"\U00001cf5\U00000020", {7413, 32}, {3, 4}},
-     {"\U00001cf5\U00000308\U00000020", {7413, 32}, {5, 6}},
-     {"\U00001cf5\U00000378", {7413, 888}, {3, 5}},
-     {"\U00001cf5\U00000308\U00000378", {7413, 888}, {5, 7}},
-     {"\U00000915\U0000000d", {2325, 13}, {3, 4}},
-     {"\U00000915\U00000308\U0000000d", {2325, 13}, {5, 6}},
-     {"\U00000915\U0000000a", {2325, 10}, {3, 4}},
-     {"\U00000915\U00000308\U0000000a", {2325, 10}, {5, 6}},
-     {"\U00000915\U00000000", {2325, 0}, {3, 4}},
-     {"\U00000915\U00000308\U00000000", {2325, 0}, {5, 6}},
-     {"\U00000915\U0000094d", {2325}, {6}},
-     {"\U00000915\U00000308\U0000094d", {2325}, {8}},
-     {"\U00000915\U00000300", {2325}, {5}},
-     {"\U00000915\U00000308\U00000300", {2325}, {7}},
-     {"\U00000915\U0000200c", {2325}, {6}},
-     {"\U00000915\U00000308\U0000200c", {2325}, {8}},
-     {"\U00000915\U0000200d", {2325}, {6}},
-     {"\U00000915\U00000308\U0000200d", {2325}, {8}},
-     {"\U00000915\U0001f1e6", {2325, 127462}, {3, 7}},
-     {"\U00000915\U00000308\U0001f1e6", {2325, 127462}, {5, 9}},
-     {"\U00000915\U000006dd", {2325, 1757}, {3, 5}},
-     {"\U00000915\U00000308\U000006dd", {2325, 1757}, {5, 7}},
-     {"\U00000915\U00000903", {2325}, {6}},
-     {"\U00000915\U00000308\U00000903", {2325}, {8}},
-     {"\U00000915\U00001100", {2325, 4352}, {3, 6}},
-     {"\U00000915\U00000308\U00001100", {2325, 4352}, {5, 8}},
-     {"\U00000915\U00001160", {2325, 4448}, {3, 6}},
-     {"\U00000915\U00000308\U00001160", {2325, 4448}, {5, 8}},
-     {"\U00000915\U000011a8", {2325, 4520}, {3, 6}},
-     {"\U00000915\U00000308\U000011a8", {2325, 4520}, {5, 8}},
-     {"\U00000915\U0000ac00", {2325, 44032}, {3, 6}},
-     {"\U00000915\U00000308\U0000ac00", {2325, 44032}, {5, 8}},
-     {"\U00000915\U0000ac01", {2325, 44033}, {3, 6}},
-     {"\U00000915\U00000308\U0000ac01", {2325, 44033}, {5, 8}},
-     {"\U00000915\U00001cf5", {2325, 7413}, {3, 6}},
-     {"\U00000915\U00000308\U00001cf5", {2325, 7413}, {5, 8}},
-     {"\U00000915\U00000915", {2325, 2325}, {3, 6}},
-     {"\U00000915\U00000308\U00000915", {2325, 2325}, {5, 8}},
-     {"\U00000915\U000000a9", {2325, 169}, {3, 5}},
-     {"\U00000915\U00000308\U000000a9", {2325, 169}, {5, 7}},
-     {"\U00000915\U00000020", {2325, 32}, {3, 4}},
-     {"\U00000915\U00000308\U00000020", {2325, 32}, {5, 6}},
-     {"\U00000915\U00000378", {2325, 888}, {3, 5}},
-     {"\U00000915\U00000308\U00000378", {2325, 888}, {5, 7}},
-     {"\U000000a9\U0000000d", {169, 13}, {2, 3}},
-     {"\U000000a9\U00000308\U0000000d", {169, 13}, {4, 5}},
-     {"\U000000a9\U0000000a", {169, 10}, {2, 3}},
-     {"\U000000a9\U00000308\U0000000a", {169, 10}, {4, 5}},
-     {"\U000000a9\U00000000", {169, 0}, {2, 3}},
-     {"\U000000a9\U00000308\U00000000", {169, 0}, {4, 5}},
-     {"\U000000a9\U0000094d", {169}, {5}},
-     {"\U000000a9\U00000308\U0000094d", {169}, {7}},
-     {"\U000000a9\U00000300", {169}, {4}},
-     {"\U000000a9\U00000308\U00000300", {169}, {6}},
-     {"\U000000a9\U0000200c", {169}, {5}},
-     {"\U000000a9\U00000308\U0000200c", {169}, {7}},
-     {"\U000000a9\U0000200d", {169}, {5}},
-     {"\U000000a9\U00000308\U0000200d", {169}, {7}},
-     {"\U000000a9\U0001f1e6", {169, 127462}, {2, 6}},
-     {"\U000000a9\U00000308\U0001f1e6", {169, 127462}, {4, 8}},
-     {"\U000000a9\U000006dd", {169, 1757}, {2, 4}},
-     {"\U000000a9\U00000308\U000006dd", {169, 1757}, {4, 6}},
-     {"\U000000a9\U00000903", {169}, {5}},
-     {"\U000000a9\U00000308\U00000903", {169}, {7}},
-     {"\U000000a9\U00001100", {169, 4352}, {2, 5}},
-     {"\U000000a9\U00000308\U00001100", {169, 4352}, {4, 7}},
-     {"\U000000a9\U00001160", {169, 4448}, {2, 5}},
-     {"\U000000a9\U00000308\U00001160", {169, 4448}, {4, 7}},
-     {"\U000000a9\U000011a8", {169, 4520}, {2, 5}},
-     {"\U000000a9\U00000308\U000011a8", {169, 4520}, {4, 7}},
-     {"\U000000a9\U0000ac00", {169, 44032}, {2, 5}},
-     {"\U000000a9\U00000308\U0000ac00", {169, 44032}, {4, 7}},
-     {"\U000000a9\U0000ac01", {169, 44033}, {2, 5}},
-     {"\U000000a9\U00000308\U0000ac01", {169, 44033}, {4, 7}},
-     {"\U000000a9\U00001cf5", {169, 7413}, {2, 5}},
-     {"\U000000a9\U00000308\U00001cf5", {169, 7413}, {4, 7}},
-     {"\U000000a9\U00000915", {169, 2325}, {2, 5}},
-     {"\U000000a9\U00000308\U00000915", {169, 2325}, {4, 7}},
-     {"\U000000a9\U000000a9", {169, 169}, {2, 4}},
-     {"\U000000a9\U00000308\U000000a9", {169, 169}, {4, 6}},
-     {"\U000000a9\U00000020", {169, 32}, {2, 3}},
-     {"\U000000a9\U00000308\U00000020", {169, 32}, {4, 5}},
-     {"\U000000a9\U00000378", {169, 888}, {2, 4}},
-     {"\U000000a9\U00000308\U00000378", {169, 888}, {4, 6}},
-     {"\U00000020\U0000000d", {32, 13}, {1, 2}},
-     {"\U00000020\U00000308\U0000000d", {32, 13}, {3, 4}},
-     {"\U00000020\U0000000a", {32, 10}, {1, 2}},
-     {"\U00000020\U00000308\U0000000a", {32, 10}, {3, 4}},
-     {"\U00000020\U00000000", {32, 0}, {1, 2}},
-     {"\U00000020\U00000308\U00000000", {32, 0}, {3, 4}},
-     {"\U00000020\U0000094d", {32}, {4}},
-     {"\U00000020\U00000308\U0000094d", {32}, {6}},
-     {"\U00000020\U00000300", {32}, {3}},
-     {"\U00000020\U00000308\U00000300", {32}, {5}},
-     {"\U00000020\U0000200c", {32}, {4}},
-     {"\U00000020\U00000308\U0000200c", {32}, {6}},
-     {"\U00000020\U0000200d", {32}, {4}},
-     {"\U00000020\U00000308\U0000200d", {32}, {6}},
-     {"\U00000020\U0001f1e6", {32, 127462}, {1, 5}},
-     {"\U00000020\U00000308\U0001f1e6", {32, 127462}, {3, 7}},
-     {"\U00000020\U000006dd", {32, 1757}, {1, 3}},
-     {"\U00000020\U00000308\U000006dd", {32, 1757}, {3, 5}},
-     {"\U00000020\U00000903", {32}, {4}},
-     {"\U00000020\U00000308\U00000903", {32}, {6}},
-     {"\U00000020\U00001100", {32, 4352}, {1, 4}},
-     {"\U00000020\U00000308\U00001100", {32, 4352}, {3, 6}},
-     {"\U00000020\U00001160", {32, 4448}, {1, 4}},
-     {"\U00000020\U00000308\U00001160", {32, 4448}, {3, 6}},
-     {"\U00000020\U000011a8", {32, 4520}, {1, 4}},
-     {"\U00000020\U00000308\U000011a8", {32, 4520}, {3, 6}},
-     {"\U00000020\U0000ac00", {32, 44032}, {1, 4}},
-     {"\U00000020\U00000308\U0000ac00", {32, 44032}, {3, 6}},
-     {"\U00000020\U0000ac01", {32, 44033}, {1, 4}},
-     {"\U00000020\U00000308\U0000ac01", {32, 44033}, {3, 6}},
-     {"\U00000020\U00001cf5", {32, 7413}, {1, 4}},
-     {"\U00000020\U00000308\U00001cf5", {32, 7413}, {3, 6}},
-     {"\U00000020\U00000915", {32, 2325}, {1, 4}},
-     {"\U00000020\U00000308\U00000915", {32, 2325}, {3, 6}},
-     {"\U00000020\U000000a9", {32, 169}, {1, 3}},
-     {"\U00000020\U00000308\U000000a9", {32, 169}, {3, 5}},
-     {"\U00000020\U00000020", {32, 32}, {1, 2}},
-     {"\U00000020\U00000308\U00000020", {32, 32}, {3, 4}},
-     {"\U00000020\U00000378", {32, 888}, {1, 3}},
-     {"\U00000020\U00000308\U00000378", {32, 888}, {3, 5}},
-     {"\U00000378\U0000000d", {888, 13}, {2, 3}},
-     {"\U00000378\U00000308\U0000000d", {888, 13}, {4, 5}},
-     {"\U00000378\U0000000a", {888, 10}, {2, 3}},
-     {"\U00000378\U00000308\U0000000a", {888, 10}, {4, 5}},
-     {"\U00000378\U00000000", {888, 0}, {2, 3}},
-     {"\U00000378\U00000308\U00000000", {888, 0}, {4, 5}},
-     {"\U00000378\U0000094d", {888}, {5}},
-     {"\U00000378\U00000308\U0000094d", {888}, {7}},
-     {"\U00000378\U00000300", {888}, {4}},
-     {"\U00000378\U00000308\U00000300", {888}, {6}},
-     {"\U00000378\U0000200c", {888}, {5}},
-     {"\U00000378\U00000308\U0000200c", {888}, {7}},
-     {"\U00000378\U0000200d", {888}, {5}},
-     {"\U00000378\U00000308\U0000200d", {888}, {7}},
-     {"\U00000378\U0001f1e6", {888, 127462}, {2, 6}},
-     {"\U00000378\U00000308\U0001f1e6", {888, 127462}, {4, 8}},
-     {"\U00000378\U000006dd", {888, 1757}, {2, 4}},
-     {"\U00000378\U00000308\U000006dd", {888, 1757}, {4, 6}},
-     {"\U00000378\U00000903", {888}, {5}},
-     {"\U00000378\U00000308\U00000903", {888}, {7}},
-     {"\U00000378\U00001100", {888, 4352}, {2, 5}},
-     {"\U00000378\U00000308\U00001100", {888, 4352}, {4, 7}},
-     {"\U00000378\U00001160", {888, 4448}, {2, 5}},
-     {"\U00000378\U00000308\U00001160", {888, 4448}, {4, 7}},
-     {"\U00000378\U000011a8", {888, 4520}, {2, 5}},
-     {"\U00000378\U00000308\U000011a8", {888, 4520}, {4, 7}},
-     {"\U00000378\U0000ac00", {888, 44032}, {2, 5}},
-     {"\U00000378\U00000308\U0000ac00", {888, 44032}, {4, 7}},
-     {"\U00000378\U0000ac01", {888, 44033}, {2, 5}},
-     {"\U00000378\U00000308\U0000ac01", {888, 44033}, {4, 7}},
-     {"\U00000378\U00001cf5", {888, 7413}, {2, 5}},
-     {"\U00000378\U00000308\U00001cf5", {888, 7413}, {4, 7}},
-     {"\U00000378\U00000915", {888, 2325}, {2, 5}},
-     {"\U00000378\U00000308\U00000915", {888, 2325}, {4, 7}},
-     {"\U00000378\U000000a9", {888, 169}, {2, 4}},
-     {"\U00000378\U00000308\U000000a9", {888, 169}, {4, 6}},
-     {"\U00000378\U00000020", {888, 32}, {2, 3}},
-     {"\U00000378\U00000308\U00000020", {888, 32}, {4, 5}},
-     {"\U00000378\U00000378", {888, 888}, {2, 4}},
-     {"\U00000378\U00000308\U00000378", {888, 888}, {4, 6}},
-     {"\U0000000d\U0000000a\U00000061\U0000000a\U00000308", {13, 97, 10, 776}, {2, 3, 4, 6}},
-     {"\U00000061\U00000308", {97}, {3}},
-     {"\U00000020\U0000200d\U00000646", {32, 1606}, {4, 6}},
-     {"\U00000646\U0000200d\U00000020", {1606, 32}, {5, 6}},
-     {"\U00001100\U00001100", {4352}, {6}},
-     {"\U0000ac00\U000011a8\U00001100", {44032, 4352}, {6, 9}},
-     {"\U0000ac01\U000011a8\U00001100", {44033, 4352}, {6, 9}},
-     {"\U0001f1e6\U0001f1e7\U0001f1e8\U00000062", {127462, 127464, 98}, {8, 12, 13}},
-     {"\U00000061\U0001f1e6\U0001f1e7\U0001f1e8\U00000062", {97, 127462, 127464, 98}, {1, 9, 13, 14}},
-     {"\U00000061\U0001f1e6\U0001f1e7\U0000200d\U0001f1e8\U00000062", {97, 127462, 127464, 98}, {1, 12, 16, 17}},
-     {"\U00000061\U0001f1e6\U0000200d\U0001f1e7\U0001f1e8\U00000062", {97, 127462, 127463, 98}, {1, 8, 16, 17}},
-     {"\U00000061\U0001f1e6\U0001f1e7\U0001f1e8\U0001f1e9\U00000062", {97, 127462, 127464, 98}, {1, 9, 17, 18}},
-     {"\U00000061\U0000200d", {97}, {4}},
-     {"\U00000061\U00000308\U00000062", {97, 98}, {3, 4}},
-     {"\U00000061\U00000903\U00000062", {97, 98}, {4, 5}},
-     {"\U00000061\U00000600\U00000062", {97, 1536}, {1, 4}},
-     {"\U0001f476\U0001f3ff\U0001f476", {128118, 128118}, {8, 12}},
-     {"\U00000061\U0001f3ff\U0001f476", {97, 128118}, {5, 9}},
-     {"\U00000061\U0001f3ff\U0001f476\U0000200d\U0001f6d1", {97, 128118}, {5, 16}},
-     {"\U0001f476\U0001f3ff\U00000308\U0000200d\U0001f476\U0001f3ff", {128118}, {21}},
-     {"\U0001f6d1\U0000200d\U0001f6d1", {128721}, {11}},
-     {"\U00000061\U0000200d\U0001f6d1", {97, 128721}, {4, 8}},
-     {"\U00002701\U0000200d\U00002701", {9985, 9985}, {6, 9}},
-     {"\U00000061\U0000200d\U00002701", {97, 9985}, {4, 7}},
-     {"\U00000915\U00000924", {2325, 2340}, {3, 6}},
-     {"\U00000915\U0000094d\U00000924", {2325}, {9}},
-     {"\U00000915\U0000094d\U0000094d\U00000924", {2325}, {12}},
-     {"\U00000915\U0000094d\U0000200d\U00000924", {2325}, {12}},
-     {"\U00000915\U0000093c\U0000200d\U0000094d\U00000924", {2325}, {15}},
-     {"\U00000915\U0000093c\U0000094d\U0000200d\U00000924", {2325}, {15}},
-     {"\U00000915\U0000094d\U00000924\U0000094d\U0000092f", {2325}, {15}},
-     {"\U00000915\U0000094d\U00000061", {2325, 97}, {6, 7}},
-     {"\U00000061\U0000094d\U00000924", {97}, {7}},
-     {"\U0000003f\U0000094d\U00000924", {63}, {7}},
-     {"\U00000915\U0000094d\U0000094d\U00000924", {2325}, {12}},
-     {"\U00000ab8\U00000afb\U00000acd\U00000ab8\U00000afb", {2744}, {15}},
-     {"\U00001019\U00001039\U00001018\U0000102c\U00001037", {4121, 4140}, {9, 15}},
-     {"\U00001004\U0000103a\U00001039\U00001011\U00001039\U00001011", {4100}, {18}},
-     {"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38", {6930, 6962, 6962, 6962}, {6, 15, 30, 42}},
-     {"\U0000179f\U000017d2\U0000178f\U000017d2\U0000179a\U000017b8", {6047}, {18}},
-     {"\U00001b26\U00001b17\U00001b44\U00001b13", {6950, 6935}, {3, 12}},
-     {"\U00001b27\U00001b13\U00001b44\U00001b0b\U00001b0b\U00001b04", {6951, 6931, 6923}, {3, 12, 18}},
-     {"\U00001795\U000017d2\U000017af\U00001798", {6037, 6040}, {9, 12}},
-     {"\U000017a0\U000017d2\U000017ab\U00001791\U000017d0\U00001799", {6048, 6033, 6041}, {9, 15, 18}},
-     {"\U00001b05\U00001b44\U00001b33\U00001b03", {6917}, {12}},
-     {"\U00000cf1\U00000c95", {3313, 3221}, {3, 6}},
-     {"\U00000cf2\U00000cab", {3314, 3243}, {3, 6}},
-     {"\U00000cf1\U00000c95\U00000cbf", {3313, 3221}, {3, 9}},
-     {"\U00001cf5\U00000995", {7413}, {6}},
-     {"\U00001cf6\U000009aa", {7414}, {6}},
-     {"\U00001cf5\U0000200c\U00000995", {7413, 2453}, {6, 9}},
-     {"\U00001cf6\U0000200c\U000009aa", {7414, 2474}, {6, 9}},
-     {"\U00011a3a\U00011a0b", {72250}, {8}}}};
+     {"\U0000000d\U0000000d"sv, {13, 13}, {1, 2}},
+     {"\U0000000d\U00000308\U0000000d"sv, {13, 776, 13}, {1, 3, 4}},
+     {"\U0000000d\U0000000a"sv, {13}, {2}},
+     {"\U0000000d\U00000308\U0000000a"sv, {13, 776, 10}, {1, 3, 4}},
+     {"\U0000000d\U00000000"sv, {13, 0}, {1, 2}},
+     {"\U0000000d\U00000308\U00000000"sv, {13, 776, 0}, {1, 3, 4}},
+     {"\U0000000d\U0000094d"sv, {13, 2381}, {1, 4}},
+     {"\U0000000d\U00000308\U0000094d"sv, {13, 776}, {1, 6}},
+     {"\U0000000d\U00000300"sv, {13, 768}, {1, 3}},
+     {"\U0000000d\U00000308\U00000300"sv, {13, 776}, {1, 5}},
+     {"\U0000000d\U0000200c"sv, {13, 8204}, {1, 4}},
+     {"\U0000000d\U00000308\U0000200c"sv, {13, 776}, {1, 6}},
+     {"\U0000000d\U0000200d"sv, {13, 8205}, {1, 4}},
+     {"\U0000000d\U00000308\U0000200d"sv, {13, 776}, {1, 6}},
+     {"\U0000000d\U0001f1e6"sv, {13, 127462}, {1, 5}},
+     {"\U0000000d\U00000308\U0001f1e6"sv, {13, 776, 127462}, {1, 3, 7}},
+     {"\U0000000d\U000006dd"sv, {13, 1757}, {1, 3}},
+     {"\U0000000d\U00000308\U000006dd"sv, {13, 776, 1757}, {1, 3, 5}},
+     {"\U0000000d\U00000903"sv, {13, 2307}, {1, 4}},
+     {"\U0000000d\U00000308\U00000903"sv, {13, 776}, {1, 6}},
+     {"\U0000000d\U00001100"sv, {13, 4352}, {1, 4}},
+     {"\U0000000d\U00000308\U00001100"sv, {13, 776, 4352}, {1, 3, 6}},
+     {"\U0000000d\U00001160"sv, {13, 4448}, {1, 4}},
+     {"\U0000000d\U00000308\U00001160"sv, {13, 776, 4448}, {1, 3, 6}},
+     {"\U0000000d\U000011a8"sv, {13, 4520}, {1, 4}},
+     {"\U0000000d\U00000308\U000011a8"sv, {13, 776, 4520}, {1, 3, 6}},
+     {"\U0000000d\U0000ac00"sv, {13, 44032}, {1, 4}},
+     {"\U0000000d\U00000308\U0000ac00"sv, {13, 776, 44032}, {1, 3, 6}},
+     {"\U0000000d\U0000ac01"sv, {13, 44033}, {1, 4}},
+     {"\U0000000d\U00000308\U0000ac01"sv, {13, 776, 44033}, {1, 3, 6}},
+     {"\U0000000d\U00001cf5"sv, {13, 7413}, {1, 4}},
+     {"\U0000000d\U00000308\U00001cf5"sv, {13, 776, 7413}, {1, 3, 6}},
+     {"\U0000000d\U00000915"sv, {13, 2325}, {1, 4}},
+     {"\U0000000d\U00000308\U00000915"sv, {13, 776, 2325}, {1, 3, 6}},
+     {"\U0000000d\U000000a9"sv, {13, 169}, {1, 3}},
+     {"\U0000000d\U00000308\U000000a9"sv, {13, 776, 169}, {1, 3, 5}},
+     {"\U0000000d\U00000020"sv, {13, 32}, {1, 2}},
+     {"\U0000000d\U00000308\U00000020"sv, {13, 776, 32}, {1, 3, 4}},
+     {"\U0000000d\U00000378"sv, {13, 888}, {1, 3}},
+     {"\U0000000d\U00000308\U00000378"sv, {13, 776, 888}, {1, 3, 5}},
+     {"\U0000000a\U0000000d"sv, {10, 13}, {1, 2}},
+     {"\U0000000a\U00000308\U0000000d"sv, {10, 776, 13}, {1, 3, 4}},
+     {"\U0000000a\U0000000a"sv, {10, 10}, {1, 2}},
+     {"\U0000000a\U00000308\U0000000a"sv, {10, 776, 10}, {1, 3, 4}},
+     {"\U0000000a\U00000000"sv, {10, 0}, {1, 2}},
+     {"\U0000000a\U00000308\U00000000"sv, {10, 776, 0}, {1, 3, 4}},
+     {"\U0000000a\U0000094d"sv, {10, 2381}, {1, 4}},
+     {"\U0000000a\U00000308\U0000094d"sv, {10, 776}, {1, 6}},
+     {"\U0000000a\U00000300"sv, {10, 768}, {1, 3}},
+     {"\U0000000a\U00000308\U00000300"sv, {10, 776}, {1, 5}},
+     {"\U0000000a\U0000200c"sv, {10, 8204}, {1, 4}},
+     {"\U0000000a\U00000308\U0000200c"sv, {10, 776}, {1, 6}},
+     {"\U0000000a\U0000200d"sv, {10, 8205}, {1, 4}},
+     {"\U0000000a\U00000308\U0000200d"sv, {10, 776}, {1, 6}},
+     {"\U0000000a\U0001f1e6"sv, {10, 127462}, {1, 5}},
+     {"\U0000000a\U00000308\U0001f1e6"sv, {10, 776, 127462}, {1, 3, 7}},
+     {"\U0000000a\U000006dd"sv, {10, 1757}, {1, 3}},
+     {"\U0000000a\U00000308\U000006dd"sv, {10, 776, 1757}, {1, 3, 5}},
+     {"\U0000000a\U00000903"sv, {10, 2307}, {1, 4}},
+     {"\U0000000a\U00000308\U00000903"sv, {10, 776}, {1, 6}},
+     {"\U0000000a\U00001100"sv, {10, 4352}, {1, 4}},
+     {"\U0000000a\U00000308\U00001100"sv, {10, 776, 4352}, {1, 3, 6}},
+     {"\U0000000a\U00001160"sv, {10, 4448}, {1, 4}},
+     {"\U0000000a\U00000308\U00001160"sv, {10, 776, 4448}, {1, 3, 6}},
+     {"\U0000000a\U000011a8"sv, {10, 4520}, {1, 4}},
+     {"\U0000000a\U00000308\U000011a8"sv, {10, 776, 4520}, {1, 3, 6}},
+     {"\U0000000a\U0000ac00"sv, {10, 44032}, {1, 4}},
+     {"\U0000000a\U00000308\U0000ac00"sv, {10, 776, 44032}, {1, 3, 6}},
+     {"\U0000000a\U0000ac01"sv, {10, 44033}, {1, 4}},
+     {"\U0000000a\U00000308\U0000ac01"sv, {10, 776, 44033}, {1, 3, 6}},
+     {"\U0000000a\U00001cf5"sv, {10, 7413}, {1, 4}},
+     {"\U0000000a\U00000308\U00001cf5"sv, {10, 776, 7413}, {1, 3, 6}},
+     {"\U0000000a\U00000915"sv, {10, 2325}, {1, 4}},
+     {"\U0000000a\U00000308\U00000915"sv, {10, 776, 2325}, {1, 3, 6}},
+     {"\U0000000a\U000000a9"sv, {10, 169}, {1, 3}},
+     {"\U0000000a\U00000308\U000000a9"sv, {10, 776, 169}, {1, 3, 5}},
+     {"\U0000000a\U00000020"sv, {10, 32}, {1, 2}},
+     {"\U0000000a\U00000308\U00000020"sv, {10, 776, 32}, {1, 3, 4}},
+     {"\U0000000a\U00000378"sv, {10, 888}, {1, 3}},
+     {"\U0000000a\U00000308\U00000378"sv, {10, 776, 888}, {1, 3, 5}},
+     {"\U00000000\U0000000d"sv, {0, 13}, {1, 2}},
+     {"\U00000000\U00000308\U0000000d"sv, {0, 776, 13}, {1, 3, 4}},
+     {"\U00000000\U0000000a"sv, {0, 10}, {1, 2}},
+     {"\U00000000\U00000308\U0000000a"sv, {0, 776, 10}, {1, 3, 4}},
+     {"\U00000000\U00000000"sv, {0, 0}, {1, 2}},
+     {"\U00000000\U00000308\U00000000"sv, {0, 776, 0}, {1, 3, 4}},
+     {"\U00000000\U0000094d"sv, {0, 2381}, {1, 4}},
+     {"\U00000000\U00000308\U0000094d"sv, {0, 776}, {1, 6}},
+     {"\U00000000\U00000300"sv, {0, 768}, {1, 3}},
+     {"\U00000000\U00000308\U00000300"sv, {0, 776}, {1, 5}},
+     {"\U00000000\U0000200c"sv, {0, 8204}, {1, 4}},
+     {"\U00000000\U00000308\U0000200c"sv, {0, 776}, {1, 6}},
+     {"\U00000000\U0000200d"sv, {0, 8205}, {1, 4}},
+     {"\U00000000\U00000308\U0000200d"sv, {0, 776}, {1, 6}},
+     {"\U00000000\U0001f1e6"sv, {0, 127462}, {1, 5}},
+     {"\U00000000\U00000308\U0001f1e6"sv, {0, 776, 127462}, {1, 3, 7}},
+     {"\U00000000\U000006dd"sv, {0, 1757}, {1, 3}},
+     {"\U00000000\U00000308\U000006dd"sv, {0, 776, 1757}, {1, 3, 5}},
+     {"\U00000000\U00000903"sv, {0, 2307}, {1, 4}},
+     {"\U00000000\U00000308\U00000903"sv, {0, 776}, {1, 6}},
+     {"\U00000000\U00001100"sv, {0, 4352}, {1, 4}},
+     {"\U00000000\U00000308\U00001100"sv, {0, 776, 4352}, {1, 3, 6}},
+     {"\U00000000\U00001160"sv, {0, 4448}, {1, 4}},
+     {"\U00000000\U00000308\U00001160"sv, {0, 776, 4448}, {1, 3, 6}},
+     {"\U00000000\U000011a8"sv, {0, 4520}, {1, 4}},
+     {"\U00000000\U00000308\U000011a8"sv, {0, 776, 4520}, {1, 3, 6}},
+     {"\U00000000\U0000ac00"sv, {0, 44032}, {1, 4}},
+     {"\U00000000\U00000308\U0000ac00"sv, {0, 776, 44032}, {1, 3, 6}},
+     {"\U00000000\U0000ac01"sv, {0, 44033}, {1, 4}},
+     {"\U00000000\U00000308\U0000ac01"sv, {0, 776, 44033}, {1, 3, 6}},
+     {"\U00000000\U00001cf5"sv, {0, 7413}, {1, 4}},
+     {"\U00000000\U00000308\U00001cf5"sv, {0, 776, 7413}, {1, 3, 6}},
+     {"\U00000000\U00000915"sv, {0, 2325}, {1, 4}},
+     {"\U00000000\U00000308\U00000915"sv, {0, 776, 2325}, {1, 3, 6}},
+     {"\U00000000\U000000a9"sv, {0, 169}, {1, 3}},
+     {"\U00000000\U00000308\U000000a9"sv, {0, 776, 169}, {1, 3, 5}},
+     {"\U00000000\U00000020"sv, {0, 32}, {1, 2}},
+     {"\U00000000\U00000308\U00000020"sv, {0, 776, 32}, {1, 3, 4}},
+     {"\U00000000\U00000378"sv, {0, 888}, {1, 3}},
+     {"\U00000000\U00000308\U00000378"sv, {0, 776, 888}, {1, 3, 5}},
+     {"\U0000094d\U0000000d"sv, {2381, 13}, {3, 4}},
+     {"\U0000094d\U00000308\U0000000d"sv, {2381, 13}, {5, 6}},
+     {"\U0000094d\U0000000a"sv, {2381, 10}, {3, 4}},
+     {"\U0000094d\U00000308\U0000000a"sv, {2381, 10}, {5, 6}},
+     {"\U0000094d\U00000000"sv, {2381, 0}, {3, 4}},
+     {"\U0000094d\U00000308\U00000000"sv, {2381, 0}, {5, 6}},
+     {"\U0000094d\U0000094d"sv, {2381}, {6}},
+     {"\U0000094d\U00000308\U0000094d"sv, {2381}, {8}},
+     {"\U0000094d\U00000300"sv, {2381}, {5}},
+     {"\U0000094d\U00000308\U00000300"sv, {2381}, {7}},
+     {"\U0000094d\U0000200c"sv, {2381}, {6}},
+     {"\U0000094d\U00000308\U0000200c"sv, {2381}, {8}},
+     {"\U0000094d\U0000200d"sv, {2381}, {6}},
+     {"\U0000094d\U00000308\U0000200d"sv, {2381}, {8}},
+     {"\U0000094d\U0001f1e6"sv, {2381, 127462}, {3, 7}},
+     {"\U0000094d\U00000308\U0001f1e6"sv, {2381, 127462}, {5, 9}},
+     {"\U0000094d\U000006dd"sv, {2381, 1757}, {3, 5}},
+     {"\U0000094d\U00000308\U000006dd"sv, {2381, 1757}, {5, 7}},
+     {"\U0000094d\U00000903"sv, {2381}, {6}},
+     {"\U0000094d\U00000308\U00000903"sv, {2381}, {8}},
+     {"\U0000094d\U00001100"sv, {2381, 4352}, {3, 6}},
+     {"\U0000094d\U00000308\U00001100"sv, {2381, 4352}, {5, 8}},
+     {"\U0000094d\U00001160"sv, {2381, 4448}, {3, 6}},
+     {"\U0000094d\U00000308\U00001160"sv, {2381, 4448}, {5, 8}},
+     {"\U0000094d\U000011a8"sv, {2381, 4520}, {3, 6}},
+     {"\U0000094d\U00000308\U000011a8"sv, {2381, 4520}, {5, 8}},
+     {"\U0000094d\U0000ac00"sv, {2381, 44032}, {3, 6}},
+     {"\U0000094d\U00000308\U0000ac00"sv, {2381, 44032}, {5, 8}},
+     {"\U0000094d\U0000ac01"sv, {2381, 44033}, {3, 6}},
+     {"\U0000094d\U00000308\U0000ac01"sv, {2381, 44033}, {5, 8}},
+     {"\U0000094d\U00001cf5"sv, {2381, 7413}, {3, 6}},
+     {"\U0000094d\U00000308\U00001cf5"sv, {2381, 7413}, {5, 8}},
+     {"\U0000094d\U00000915"sv, {2381}, {6}},
+     {"\U0000094d\U00000308\U00000915"sv, {2381}, {8}},
+     {"\U0000094d\U000000a9"sv, {2381, 169}, {3, 5}},
+     {"\U0000094d\U00000308\U000000a9"sv, {2381, 169}, {5, 7}},
+     {"\U0000094d\U00000020"sv, {2381, 32}, {3, 4}},
+     {"\U0000094d\U00000308\U00000020"sv, {2381, 32}, {5, 6}},
+     {"\U0000094d\U00000378"sv, {2381, 888}, {3, 5}},
+     {"\U0000094d\U00000308\U00000378"sv, {2381, 888}, {5, 7}},
+     {"\U00000300\U0000000d"sv, {768, 13}, {2, 3}},
+     {"\U00000300\U00000308\U0000000d"sv, {768, 13}, {4, 5}},
+     {"\U00000300\U0000000a"sv, {768, 10}, {2, 3}},
+     {"\U00000300\U00000308\U0000000a"sv, {768, 10}, {4, 5}},
+     {"\U00000300\U00000000"sv, {768, 0}, {2, 3}},
+     {"\U00000300\U00000308\U00000000"sv, {768, 0}, {4, 5}},
+     {"\U00000300\U0000094d"sv, {768}, {5}},
+     {"\U00000300\U00000308\U0000094d"sv, {768}, {7}},
+     {"\U00000300\U00000300"sv, {768}, {4}},
+     {"\U00000300\U00000308\U00000300"sv, {768}, {6}},
+     {"\U00000300\U0000200c"sv, {768}, {5}},
+     {"\U00000300\U00000308\U0000200c"sv, {768}, {7}},
+     {"\U00000300\U0000200d"sv, {768}, {5}},
+     {"\U00000300\U00000308\U0000200d"sv, {768}, {7}},
+     {"\U00000300\U0001f1e6"sv, {768, 127462}, {2, 6}},
+     {"\U00000300\U00000308\U0001f1e6"sv, {768, 127462}, {4, 8}},
+     {"\U00000300\U000006dd"sv, {768, 1757}, {2, 4}},
+     {"\U00000300\U00000308\U000006dd"sv, {768, 1757}, {4, 6}},
+     {"\U00000300\U00000903"sv, {768}, {5}},
+     {"\U00000300\U00000308\U00000903"sv, {768}, {7}},
+     {"\U00000300\U00001100"sv, {768, 4352}, {2, 5}},
+     {"\U00000300\U00000308\U00001100"sv, {768, 4352}, {4, 7}},
+     {"\U00000300\U00001160"sv, {768, 4448}, {2, 5}},
+     {"\U00000300\U00000308\U00001160"sv, {768, 4448}, {4, 7}},
+     {"\U00000300\U000011a8"sv, {768, 4520}, {2, 5}},
+     {"\U00000300\U00000308\U000011a8"sv, {768, 4520}, {4, 7}},
+     {"\U00000300\U0000ac00"sv, {768, 44032}, {2, 5}},
+     {"\U00000300\U00000308\U0000ac00"sv, {768, 44032}, {4, 7}},
+     {"\U00000300\U0000ac01"sv, {768, 44033}, {2, 5}},
+     {"\U00000300\U00000308\U0000ac01"sv, {768, 44033}, {4, 7}},
+     {"\U00000300\U00001cf5"sv, {768, 7413}, {2, 5}},
+     {"\U00000300\U00000308\U00001cf5"sv, {768, 7413}, {4, 7}},
+     {"\U00000300\U00000915"sv, {768, 2325}, {2, 5}},
+     {"\U00000300\U00000308\U00000915"sv, {768, 2325}, {4, 7}},
+     {"\U00000300\U000000a9"sv, {768, 169}, {2, 4}},
+     {"\U00000300\U00000308\U000000a9"sv, {768, 169}, {4, 6}},
+     {"\U00000300\U00000020"sv, {768, 32}, {2, 3}},
+     {"\U00000300\U00000308\U00000020"sv, {768, 32}, {4, 5}},
+     {"\U00000300\U00000378"sv, {768, 888}, {2, 4}},
+     {"\U00000300\U00000308\U00000378"sv, {768, 888}, {4, 6}},
+     {"\U0000200c\U0000000d"sv, {8204, 13}, {3, 4}},
+     {"\U0000200c\U00000308\U0000000d"sv, {8204, 13}, {5, 6}},
+     {"\U0000200c\U0000000a"sv, {8204, 10}, {3, 4}},
+     {"\U0000200c\U00000308\U0000000a"sv, {8204, 10}, {5, 6}},
+     {"\U0000200c\U00000000"sv, {8204, 0}, {3, 4}},
+     {"\U0000200c\U00000308\U00000000"sv, {8204, 0}, {5, 6}},
+     {"\U0000200c\U0000094d"sv, {8204}, {6}},
+     {"\U0000200c\U00000308\U0000094d"sv, {8204}, {8}},
+     {"\U0000200c\U00000300"sv, {8204}, {5}},
+     {"\U0000200c\U00000308\U00000300"sv, {8204}, {7}},
+     {"\U0000200c\U0000200c"sv, {8204}, {6}},
+     {"\U0000200c\U00000308\U0000200c"sv, {8204}, {8}},
+     {"\U0000200c\U0000200d"sv, {8204}, {6}},
+     {"\U0000200c\U00000308\U0000200d"sv, {8204}, {8}},
+     {"\U0000200c\U0001f1e6"sv, {8204, 127462}, {3, 7}},
+     {"\U0000200c\U00000308\U0001f1e6"sv, {8204, 127462}, {5, 9}},
+     {"\U0000200c\U000006dd"sv, {8204, 1757}, {3, 5}},
+     {"\U0000200c\U00000308\U000006dd"sv, {8204, 1757}, {5, 7}},
+     {"\U0000200c\U00000903"sv, {8204}, {6}},
+     {"\U0000200c\U00000308\U00000903"sv, {8204}, {8}},
+     {"\U0000200c\U00001100"sv, {8204, 4352}, {3, 6}},
+     {"\U0000200c\U00000308\U00001100"sv, {8204, 4352}, {5, 8}},
+     {"\U0000200c\U00001160"sv, {8204, 4448}, {3, 6}},
+     {"\U0000200c\U00000308\U00001160"sv, {8204, 4448}, {5, 8}},
+     {"\U0000200c\U000011a8"sv, {8204, 4520}, {3, 6}},
+     {"\U0000200c\U00000308\U000011a8"sv, {8204, 4520}, {5, 8}},
+     {"\U0000200c\U0000ac00"sv, {8204, 44032}, {3, 6}},
+     {"\U0000200c\U00000308\U0000ac00"sv, {8204, 44032}, {5, 8}},
+     {"\U0000200c\U0000ac01"sv, {8204, 44033}, {3, 6}},
+     {"\U0000200c\U00000308\U0000ac01"sv, {8204, 44033}, {5, 8}},
+     {"\U0000200c\U00001cf5"sv, {8204, 7413}, {3, 6}},
+     {"\U0000200c\U00000308\U00001cf5"sv, {8204, 7413}, {5, 8}},
+     {"\U0000200c\U00000915"sv, {8204, 2325}, {3, 6}},
+     {"\U0000200c\U00000308\U00000915"sv, {8204, 2325}, {5, 8}},
+     {"\U0000200c\U000000a9"sv, {8204, 169}, {3, 5}},
+     {"\U0000200c\U00000308\U000000a9"sv, {8204, 169}, {5, 7}},
+     {"\U0000200c\U00000020"sv, {8204, 32}, {3, 4}},
+     {"\U0000200c\U00000308\U00000020"sv, {8204, 32}, {5, 6}},
+     {"\U0000200c\U00000378"sv, {8204, 888}, {3, 5}},
+     {"\U0000200c\U00000308\U00000378"sv, {8204, 888}, {5, 7}},
+     {"\U0000200d\U0000000d"sv, {8205, 13}, {3, 4}},
+     {"\U0000200d\U00000308\U0000000d"sv, {8205, 13}, {5, 6}},
+     {"\U0000200d\U0000000a"sv, {8205, 10}, {3, 4}},
+     {"\U0000200d\U00000308\U0000000a"sv, {8205, 10}, {5, 6}},
+     {"\U0000200d\U00000000"sv, {8205, 0}, {3, 4}},
+     {"\U0000200d\U00000308\U00000000"sv, {8205, 0}, {5, 6}},
+     {"\U0000200d\U0000094d"sv, {8205}, {6}},
+     {"\U0000200d\U00000308\U0000094d"sv, {8205}, {8}},
+     {"\U0000200d\U00000300"sv, {8205}, {5}},
+     {"\U0000200d\U00000308\U00000300"sv, {8205}, {7}},
+     {"\U0000200d\U0000200c"sv, {8205}, {6}},
+     {"\U0000200d\U00000308\U0000200c"sv, {8205}, {8}},
+     {"\U0000200d\U0000200d"sv, {8205}, {6}},
+     {"\U0000200d\U00000308\U0000200d"sv, {8205}, {8}},
+     {"\U0000200d\U0001f1e6"sv, {8205, 127462}, {3, 7}},
+     {"\U0000200d\U00000308\U0001f1e6"sv, {8205, 127462}, {5, 9}},
+     {"\U0000200d\U000006dd"sv, {8205, 1757}, {3, 5}},
+     {"\U0000200d\U00000308\U000006dd"sv, {8205, 1757}, {5, 7}},
+     {"\U0000200d\U00000903"sv, {8205}, {6}},
+     {"\U0000200d\U00000308\U00000903"sv, {8205}, {8}},
+     {"\U0000200d\U00001100"sv, {8205, 4352}, {3, 6}},
+     {"\U0000200d\U00000308\U00001100"sv, {8205, 4352}, {5, 8}},
+     {"\U0000200d\U00001160"sv, {8205, 4448}, {3, 6}},
+     {"\U0000200d\U00000308\U00001160"sv, {8205, 4448}, {5, 8}},
+     {"\U0000200d\U000011a8"sv, {8205, 4520}, {3, 6}},
+     {"\U0000200d\U00000308\U000011a8"sv, {8205, 4520}, {5, 8}},
+     {"\U0000200d\U0000ac00"sv, {8205, 44032}, {3, 6}},
+     {"\U0000200d\U00000308\U0000ac00"sv, {8205, 44032}, {5, 8}},
+     {"\U0000200d\U0000ac01"sv, {8205, 44033}, {3, 6}},
+     {"\U0000200d\U00000308\U0000ac01"sv, {8205, 44033}, {5, 8}},
+     {"\U0000200d\U00001cf5"sv, {8205, 7413}, {3, 6}},
+     {"\U0000200d\U00000308\U00001cf5"sv, {8205, 7413}, {5, 8}},
+     {"\U0000200d\U00000915"sv, {8205, 2325}, {3, 6}},
+     {"\U0000200d\U00000308\U00000915"sv, {8205, 2325}, {5, 8}},
+     {"\U0000200d\U000000a9"sv, {8205, 169}, {3, 5}},
+     {"\U0000200d\U00000308\U000000a9"sv, {8205, 169}, {5, 7}},
+     {"\U0000200d\U00000020"sv, {8205, 32}, {3, 4}},
+     {"\U0000200d\U00000308\U00000020"sv, {8205, 32}, {5, 6}},
+     {"\U0000200d\U00000378"sv, {8205, 888}, {3, 5}},
+     {"\U0000200d\U00000308\U00000378"sv, {8205, 888}, {5, 7}},
+     {"\U0001f1e6\U0000000d"sv, {127462, 13}, {4, 5}},
+     {"\U0001f1e6\U00000308\U0000000d"sv, {127462, 13}, {6, 7}},
+     {"\U0001f1e6\U0000000a"sv, {127462, 10}, {4, 5}},
+     {"\U0001f1e6\U00000308\U0000000a"sv, {127462, 10}, {6, 7}},
+     {"\U0001f1e6\U00000000"sv, {127462, 0}, {4, 5}},
+     {"\U0001f1e6\U00000308\U00000000"sv, {127462, 0}, {6, 7}},
+     {"\U0001f1e6\U0000094d"sv, {127462}, {7}},
+     {"\U0001f1e6\U00000308\U0000094d"sv, {127462}, {9}},
+     {"\U0001f1e6\U00000300"sv, {127462}, {6}},
+     {"\U0001f1e6\U00000308\U00000300"sv, {127462}, {8}},
+     {"\U0001f1e6\U0000200c"sv, {127462}, {7}},
+     {"\U0001f1e6\U00000308\U0000200c"sv, {127462}, {9}},
+     {"\U0001f1e6\U0000200d"sv, {127462}, {7}},
+     {"\U0001f1e6\U00000308\U0000200d"sv, {127462}, {9}},
+     {"\U0001f1e6\U0001f1e6"sv, {127462}, {8}},
+     {"\U0001f1e6\U00000308\U0001f1e6"sv, {127462, 127462}, {6, 10}},
+     {"\U0001f1e6\U000006dd"sv, {127462, 1757}, {4, 6}},
+     {"\U0001f1e6\U00000308\U000006dd"sv, {127462, 1757}, {6, 8}},
+     {"\U0001f1e6\U00000903"sv, {127462}, {7}},
+     {"\U0001f1e6\U00000308\U00000903"sv, {127462}, {9}},
+     {"\U0001f1e6\U00001100"sv, {127462, 4352}, {4, 7}},
+     {"\U0001f1e6\U00000308\U00001100"sv, {127462, 4352}, {6, 9}},
+     {"\U0001f1e6\U00001160"sv, {127462, 4448}, {4, 7}},
+     {"\U0001f1e6\U00000308\U00001160"sv, {127462, 4448}, {6, 9}},
+     {"\U0001f1e6\U000011a8"sv, {127462, 4520}, {4, 7}},
+     {"\U0001f1e6\U00000308\U000011a8"sv, {127462, 4520}, {6, 9}},
+     {"\U0001f1e6\U0000ac00"sv, {127462, 44032}, {4, 7}},
+     {"\U0001f1e6\U00000308\U0000ac00"sv, {127462, 44032}, {6, 9}},
+     {"\U0001f1e6\U0000ac01"sv, {127462, 44033}, {4, 7}},
+     {"\U0001f1e6\U00000308\U0000ac01"sv, {127462, 44033}, {6, 9}},
+     {"\U0001f1e6\U00001cf5"sv, {127462, 7413}, {4, 7}},
+     {"\U0001f1e6\U00000308\U00001cf5"sv, {127462, 7413}, {6, 9}},
+     {"\U0001f1e6\U00000915"sv, {127462, 2325}, {4, 7}},
+     {"\U0001f1e6\U00000308\U00000915"sv, {127462, 2325}, {6, 9}},
+     {"\U0001f1e6\U000000a9"sv, {127462, 169}, {4, 6}},
+     {"\U0001f1e6\U00000308\U000000a9"sv, {127462, 169}, {6, 8}},
+     {"\U0001f1e6\U00000020"sv, {127462, 32}, {4, 5}},
+     {"\U0001f1e6\U00000308\U00000020"sv, {127462, 32}, {6, 7}},
+     {"\U0001f1e6\U00000378"sv, {127462, 888}, {4, 6}},
+     {"\U0001f1e6\U00000308\U00000378"sv, {127462, 888}, {6, 8}},
+     {"\U000006dd\U0000000d"sv, {1757, 13}, {2, 3}},
+     {"\U000006dd\U00000308\U0000000d"sv, {1757, 13}, {4, 5}},
+     {"\U000006dd\U0000000a"sv, {1757, 10}, {2, 3}},
+     {"\U000006dd\U00000308\U0000000a"sv, {1757, 10}, {4, 5}},
+     {"\U000006dd\U00000000"sv, {1757, 0}, {2, 3}},
+     {"\U000006dd\U00000308\U00000000"sv, {1757, 0}, {4, 5}},
+     {"\U000006dd\U0000094d"sv, {1757}, {5}},
+     {"\U000006dd\U00000308\U0000094d"sv, {1757}, {7}},
+     {"\U000006dd\U00000300"sv, {1757}, {4}},
+     {"\U000006dd\U00000308\U00000300"sv, {1757}, {6}},
+     {"\U000006dd\U0000200c"sv, {1757}, {5}},
+     {"\U000006dd\U00000308\U0000200c"sv, {1757}, {7}},
+     {"\U000006dd\U0000200d"sv, {1757}, {5}},
+     {"\U000006dd\U00000308\U0000200d"sv, {1757}, {7}},
+     {"\U000006dd\U0001f1e6"sv, {1757}, {6}},
+     {"\U000006dd\U00000308\U0001f1e6"sv, {1757, 127462}, {4, 8}},
+     {"\U000006dd\U000006dd"sv, {1757}, {4}},
+     {"\U000006dd\U00000308\U000006dd"sv, {1757, 1757}, {4, 6}},
+     {"\U000006dd\U00000903"sv, {1757}, {5}},
+     {"\U000006dd\U00000308\U00000903"sv, {1757}, {7}},
+     {"\U000006dd\U00001100"sv, {1757}, {5}},
+     {"\U000006dd\U00000308\U00001100"sv, {1757, 4352}, {4, 7}},
+     {"\U000006dd\U00001160"sv, {1757}, {5}},
+     {"\U000006dd\U00000308\U00001160"sv, {1757, 4448}, {4, 7}},
+     {"\U000006dd\U000011a8"sv, {1757}, {5}},
+     {"\U000006dd\U00000308\U000011a8"sv, {1757, 4520}, {4, 7}},
+     {"\U000006dd\U0000ac00"sv, {1757}, {5}},
+     {"\U000006dd\U00000308\U0000ac00"sv, {1757, 44032}, {4, 7}},
+     {"\U000006dd\U0000ac01"sv, {1757}, {5}},
+     {"\U000006dd\U00000308\U0000ac01"sv, {1757, 44033}, {4, 7}},
+     {"\U000006dd\U00001cf5"sv, {1757}, {5}},
+     {"\U000006dd\U00000308\U00001cf5"sv, {1757, 7413}, {4, 7}},
+     {"\U000006dd\U00000915"sv, {1757}, {5}},
+     {"\U000006dd\U00000308\U00000915"sv, {1757, 2325}, {4, 7}},
+     {"\U000006dd\U000000a9"sv, {1757}, {4}},
+     {"\U000006dd\U00000308\U000000a9"sv, {1757, 169}, {4, 6}},
+     {"\U000006dd\U00000020"sv, {1757}, {3}},
+     {"\U000006dd\U00000308\U00000020"sv, {1757, 32}, {4, 5}},
+     {"\U000006dd\U00000378"sv, {1757}, {4}},
+     {"\U000006dd\U00000308\U00000378"sv, {1757, 888}, {4, 6}},
+     {"\U00000903\U0000000d"sv, {2307, 13}, {3, 4}},
+     {"\U00000903\U00000308\U0000000d"sv, {2307, 13}, {5, 6}},
+     {"\U00000903\U0000000a"sv, {2307, 10}, {3, 4}},
+     {"\U00000903\U00000308\U0000000a"sv, {2307, 10}, {5, 6}},
+     {"\U00000903\U00000000"sv, {2307, 0}, {3, 4}},
+     {"\U00000903\U00000308\U00000000"sv, {2307, 0}, {5, 6}},
+     {"\U00000903\U0000094d"sv, {2307}, {6}},
+     {"\U00000903\U00000308\U0000094d"sv, {2307}, {8}},
+     {"\U00000903\U00000300"sv, {2307}, {5}},
+     {"\U00000903\U00000308\U00000300"sv, {2307}, {7}},
+     {"\U00000903\U0000200c"sv, {2307}, {6}},
+     {"\U00000903\U00000308\U0000200c"sv, {2307}, {8}},
+     {"\U00000903\U0000200d"sv, {2307}, {6}},
+     {"\U00000903\U00000308\U0000200d"sv, {2307}, {8}},
+     {"\U00000903\U0001f1e6"sv, {2307, 127462}, {3, 7}},
+     {"\U00000903\U00000308\U0001f1e6"sv, {2307, 127462}, {5, 9}},
+     {"\U00000903\U000006dd"sv, {2307, 1757}, {3, 5}},
+     {"\U00000903\U00000308\U000006dd"sv, {2307, 1757}, {5, 7}},
+     {"\U00000903\U00000903"sv, {2307}, {6}},
+     {"\U00000903\U00000308\U00000903"sv, {2307}, {8}},
+     {"\U00000903\U00001100"sv, {2307, 4352}, {3, 6}},
+     {"\U00000903\U00000308\U00001100"sv, {2307, 4352}, {5, 8}},
+     {"\U00000903\U00001160"sv, {2307, 4448}, {3, 6}},
+     {"\U00000903\U00000308\U00001160"sv, {2307, 4448}, {5, 8}},
+     {"\U00000903\U000011a8"sv, {2307, 4520}, {3, 6}},
+     {"\U00000903\U00000308\U000011a8"sv, {2307, 4520}, {5, 8}},
+     {"\U00000903\U0000ac00"sv, {2307, 44032}, {3, 6}},
+     {"\U00000903\U00000308\U0000ac00"sv, {2307, 44032}, {5, 8}},
+     {"\U00000903\U0000ac01"sv, {2307, 44033}, {3, 6}},
+     {"\U00000903\U00000308\U0000ac01"sv, {2307, 44033}, {5, 8}},
+     {"\U00000903\U00001cf5"sv, {2307, 7413}, {3, 6}},
+     {"\U00000903\U00000308\U00001cf5"sv, {2307, 7413}, {5, 8}},
+     {"\U00000903\U00000915"sv, {2307, 2325}, {3, 6}},
+     {"\U00000903\U00000308\U00000915"sv, {2307, 2325}, {5, 8}},
+     {"\U00000903\U000000a9"sv, {2307, 169}, {3, 5}},
+     {"\U00000903\U00000308\U000000a9"sv, {2307, 169}, {5, 7}},
+     {"\U00000903\U00000020"sv, {2307, 32}, {3, 4}},
+     {"\U00000903\U00000308\U00000020"sv, {2307, 32}, {5, 6}},
+     {"\U00000903\U00000378"sv, {2307, 888}, {3, 5}},
+     {"\U00000903\U00000308\U00000378"sv, {2307, 888}, {5, 7}},
+     {"\U00001100\U0000000d"sv, {4352, 13}, {3, 4}},
+     {"\U00001100\U00000308\U0000000d"sv, {4352, 13}, {5, 6}},
+     {"\U00001100\U0000000a"sv, {4352, 10}, {3, 4}},
+     {"\U00001100\U00000308\U0000000a"sv, {4352, 10}, {5, 6}},
+     {"\U00001100\U00000000"sv, {4352, 0}, {3, 4}},
+     {"\U00001100\U00000308\U00000000"sv, {4352, 0}, {5, 6}},
+     {"\U00001100\U0000094d"sv, {4352}, {6}},
+     {"\U00001100\U00000308\U0000094d"sv, {4352}, {8}},
+     {"\U00001100\U00000300"sv, {4352}, {5}},
+     {"\U00001100\U00000308\U00000300"sv, {4352}, {7}},
+     {"\U00001100\U0000200c"sv, {4352}, {6}},
+     {"\U00001100\U00000308\U0000200c"sv, {4352}, {8}},
+     {"\U00001100\U0000200d"sv, {4352}, {6}},
+     {"\U00001100\U00000308\U0000200d"sv, {4352}, {8}},
+     {"\U00001100\U0001f1e6"sv, {4352, 127462}, {3, 7}},
+     {"\U00001100\U00000308\U0001f1e6"sv, {4352, 127462}, {5, 9}},
+     {"\U00001100\U000006dd"sv, {4352, 1757}, {3, 5}},
+     {"\U00001100\U00000308\U000006dd"sv, {4352, 1757}, {5, 7}},
+     {"\U00001100\U00000903"sv, {4352}, {6}},
+     {"\U00001100\U00000308\U00000903"sv, {4352}, {8}},
+     {"\U00001100\U00001100"sv, {4352}, {6}},
+     {"\U00001100\U00000308\U00001100"sv, {4352, 4352}, {5, 8}},
+     {"\U00001100\U00001160"sv, {4352}, {6}},
+     {"\U00001100\U00000308\U00001160"sv, {4352, 4448}, {5, 8}},
+     {"\U00001100\U000011a8"sv, {4352, 4520}, {3, 6}},
+     {"\U00001100\U00000308\U000011a8"sv, {4352, 4520}, {5, 8}},
+     {"\U00001100\U0000ac00"sv, {4352}, {6}},
+     {"\U00001100\U00000308\U0000ac00"sv, {4352, 44032}, {5, 8}},
+     {"\U00001100\U0000ac01"sv, {4352}, {6}},
+     {"\U00001100\U00000308\U0000ac01"sv, {4352, 44033}, {5, 8}},
+     {"\U00001100\U00001cf5"sv, {4352, 7413}, {3, 6}},
+     {"\U00001100\U00000308\U00001cf5"sv, {4352, 7413}, {5, 8}},
+     {"\U00001100\U00000915"sv, {4352, 2325}, {3, 6}},
+     {"\U00001100\U00000308\U00000915"sv, {4352, 2325}, {5, 8}},
+     {"\U00001100\U000000a9"sv, {4352, 169}, {3, 5}},
+     {"\U00001100\U00000308\U000000a9"sv, {4352, 169}, {5, 7}},
+     {"\U00001100\U00000020"sv, {4352, 32}, {3, 4}},
+     {"\U00001100\U00000308\U00000020"sv, {4352, 32}, {5, 6}},
+     {"\U00001100\U00000378"sv, {4352, 888}, {3, 5}},
+     {"\U00001100\U00000308\U00000378"sv, {4352, 888}, {5, 7}},
+     {"\U00001160\U0000000d"sv, {4448, 13}, {3, 4}},
+     {"\U00001160\U00000308\U0000000d"sv, {4448, 13}, {5, 6}},
+     {"\U00001160\U0000000a"sv, {4448, 10}, {3, 4}},
+     {"\U00001160\U00000308\U0000000a"sv, {4448, 10}, {5, 6}},
+     {"\U00001160\U00000000"sv, {4448, 0}, {3, 4}},
+     {"\U00001160\U00000308\U00000000"sv, {4448, 0}, {5, 6}},
+     {"\U00001160\U0000094d"sv, {4448}, {6}},
+     {"\U00001160\U00000308\U0000094d"sv, {4448}, {8}},
+     {"\U00001160\U00000300"sv, {4448}, {5}},
+     {"\U00001160\U00000308\U00000300"sv, {4448}, {7}},
+     {"\U00001160\U0000200c"sv, {4448}, {6}},
+     {"\U00001160\U00000308\U0000200c"sv, {4448}, {8}},
+     {"\U00001160\U0000200d"sv, {4448}, {6}},
+     {"\U00001160\U00000308\U0000200d"sv, {4448}, {8}},
+     {"\U00001160\U0001f1e6"sv, {4448, 127462}, {3, 7}},
+     {"\U00001160\U00000308\U0001f1e6"sv, {4448, 127462}, {5, 9}},
+     {"\U00001160\U000006dd"sv, {4448, 1757}, {3, 5}},
+     {"\U00001160\U00000308\U000006dd"sv, {4448, 1757}, {5, 7}},
+     {"\U00001160\U00000903"sv, {4448}, {6}},
+     {"\U00001160\U00000308\U00000903"sv, {4448}, {8}},
+     {"\U00001160\U00001100"sv, {4448, 4352}, {3, 6}},
+     {"\U00001160\U00000308\U00001100"sv, {4448, 4352}, {5, 8}},
+     {"\U00001160\U00001160"sv, {4448}, {6}},
+     {"\U00001160\U00000308\U00001160"sv, {4448, 4448}, {5, 8}},
+     {"\U00001160\U000011a8"sv, {4448}, {6}},
+     {"\U00001160\U00000308\U000011a8"sv, {4448, 4520}, {5, 8}},
+     {"\U00001160\U0000ac00"sv, {4448, 44032}, {3, 6}},
+     {"\U00001160\U00000308\U0000ac00"sv, {4448, 44032}, {5, 8}},
+     {"\U00001160\U0000ac01"sv, {4448, 44033}, {3, 6}},
+     {"\U00001160\U00000308\U0000ac01"sv, {4448, 44033}, {5, 8}},
+     {"\U00001160\U00001cf5"sv, {4448, 7413}, {3, 6}},
+     {"\U00001160\U00000308\U00001cf5"sv, {4448, 7413}, {5, 8}},
+     {"\U00001160\U00000915"sv, {4448, 2325}, {3, 6}},
+     {"\U00001160\U00000308\U00000915"sv, {4448, 2325}, {5, 8}},
+     {"\U00001160\U000000a9"sv, {4448, 169}, {3, 5}},
+     {"\U00001160\U00000308\U000000a9"sv, {4448, 169}, {5, 7}},
+     {"\U00001160\U00000020"sv, {4448, 32}, {3, 4}},
+     {"\U00001160\U00000308\U00000020"sv, {4448, 32}, {5, 6}},
+     {"\U00001160\U00000378"sv, {4448, 888}, {3, 5}},
+     {"\U00001160\U00000308\U00000378"sv, {4448, 888}, {5, 7}},
+     {"\U000011a8\U0000000d"sv, {4520, 13}, {3, 4}},
+     {"\U000011a8\U00000308\U0000000d"sv, {4520, 13}, {5, 6}},
+     {"\U000011a8\U0000000a"sv, {4520, 10}, {3, 4}},
+     {"\U000011a8\U00000308\U0000000a"sv, {4520, 10}, {5, 6}},
+     {"\U000011a8\U00000000"sv, {4520, 0}, {3, 4}},
+     {"\U000011a8\U00000308\U00000000"sv, {4520, 0}, {5, 6}},
+     {"\U000011a8\U0000094d"sv, {4520}, {6}},
+     {"\U000011a8\U00000308\U0000094d"sv, {4520}, {8}},
+     {"\U000011a8\U00000300"sv, {4520}, {5}},
+     {"\U000011a8\U00000308\U00000300"sv, {4520}, {7}},
+     {"\U000011a8\U0000200c"sv, {4520}, {6}},
+     {"\U000011a8\U00000308\U0000200c"sv, {4520}, {8}},
+     {"\U000011a8\U0000200d"sv, {4520}, {6}},
+     {"\U000011a8\U00000308\U0000200d"sv, {4520}, {8}},
+     {"\U000011a8\U0001f1e6"sv, {4520, 127462}, {3, 7}},
+     {"\U000011a8\U00000308\U0001f1e6"sv, {4520, 127462}, {5, 9}},
+     {"\U000011a8\U000006dd"sv, {4520, 1757}, {3, 5}},
+     {"\U000011a8\U00000308\U000006dd"sv, {4520, 1757}, {5, 7}},
+     {"\U000011a8\U00000903"sv, {4520}, {6}},
+     {"\U000011a8\U00000308\U00000903"sv, {4520}, {8}},
+     {"\U000011a8\U00001100"sv, {4520, 4352}, {3, 6}},
+     {"\U000011a8\U00000308\U00001100"sv, {4520, 4352}, {5, 8}},
+     {"\U000011a8\U00001160"sv, {4520, 4448}, {3, 6}},
+     {"\U000011a8\U00000308\U00001160"sv, {4520, 4448}, {5, 8}},
+     {"\U000011a8\U000011a8"sv, {4520}, {6}},
+     {"\U000011a8\U00000308\U000011a8"sv, {4520, 4520}, {5, 8}},
+     {"\U000011a8\U0000ac00"sv, {4520, 44032}, {3, 6}},
+     {"\U000011a8\U00000308\U0000ac00"sv, {4520, 44032}, {5, 8}},
+     {"\U000011a8\U0000ac01"sv, {4520, 44033}, {3, 6}},
+     {"\U000011a8\U00000308\U0000ac01"sv, {4520, 44033}, {5, 8}},
+     {"\U000011a8\U00001cf5"sv, {4520, 7413}, {3, 6}},
+     {"\U000011a8\U00000308\U00001cf5"sv, {4520, 7413}, {5, 8}},
+     {"\U000011a8\U00000915"sv, {4520, 2325}, {3, 6}},
+     {"\U000011a8\U00000308\U00000915"sv, {4520, 2325}, {5, 8}},
+     {"\U000011a8\U000000a9"sv, {4520, 169}, {3, 5}},
+     {"\U000011a8\U00000308\U000000a9"sv, {4520, 169}, {5, 7}},
+     {"\U000011a8\U00000020"sv, {4520, 32}, {3, 4}},
+     {"\U000011a8\U00000308\U00000020"sv, {4520, 32}, {5, 6}},
+     {"\U000011a8\U00000378"sv, {4520, 888}, {3, 5}},
+     {"\U000011a8\U00000308\U00000378"sv, {4520, 888}, {5, 7}},
+     {"\U0000ac00\U0000000d"sv, {44032, 13}, {3, 4}},
+     {"\U0000ac00\U00000308\U0000000d"sv, {44032, 13}, {5, 6}},
+     {"\U0000ac00\U0000000a"sv, {44032, 10}, {3, 4}},
+     {"\U0000ac00\U00000308\U0000000a"sv, {44032, 10}, {5, 6}},
+     {"\U0000ac00\U00000000"sv, {44032, 0}, {3, 4}},
+     {"\U0000ac00\U00000308\U00000000"sv, {44032, 0}, {5, 6}},
+     {"\U0000ac00\U0000094d"sv, {44032}, {6}},
+     {"\U0000ac00\U00000308\U0000094d"sv, {44032}, {8}},
+     {"\U0000ac00\U00000300"sv, {44032}, {5}},
+     {"\U0000ac00\U00000308\U00000300"sv, {44032}, {7}},
+     {"\U0000ac00\U0000200c"sv, {44032}, {6}},
+     {"\U0000ac00\U00000308\U0000200c"sv, {44032}, {8}},
+     {"\U0000ac00\U0000200d"sv, {44032}, {6}},
+     {"\U0000ac00\U00000308\U0000200d"sv, {44032}, {8}},
+     {"\U0000ac00\U0001f1e6"sv, {44032, 127462}, {3, 7}},
+     {"\U0000ac00\U00000308\U0001f1e6"sv, {44032, 127462}, {5, 9}},
+     {"\U0000ac00\U000006dd"sv, {44032, 1757}, {3, 5}},
+     {"\U0000ac00\U00000308\U000006dd"sv, {44032, 1757}, {5, 7}},
+     {"\U0000ac00\U00000903"sv, {44032}, {6}},
+     {"\U0000ac00\U00000308\U00000903"sv, {44032}, {8}},
+     {"\U0000ac00\U00001100"sv, {44032, 4352}, {3, 6}},
+     {"\U0000ac00\U00000308\U00001100"sv, {44032, 4352}, {5, 8}},
+     {"\U0000ac00\U00001160"sv, {44032}, {6}},
+     {"\U0000ac00\U00000308\U00001160"sv, {44032, 4448}, {5, 8}},
+     {"\U0000ac00\U000011a8"sv, {44032}, {6}},
+     {"\U0000ac00\U00000308\U000011a8"sv, {44032, 4520}, {5, 8}},
+     {"\U0000ac00\U0000ac00"sv, {44032, 44032}, {3, 6}},
+     {"\U0000ac00\U00000308\U0000ac00"sv, {44032, 44032}, {5, 8}},
+     {"\U0000ac00\U0000ac01"sv, {44032, 44033}, {3, 6}},
+     {"\U0000ac00\U00000308\U0000ac01"sv, {44032, 44033}, {5, 8}},
+     {"\U0000ac00\U00001cf5"sv, {44032, 7413}, {3, 6}},
+     {"\U0000ac00\U00000308\U00001cf5"sv, {44032, 7413}, {5, 8}},
+     {"\U0000ac00\U00000915"sv, {44032, 2325}, {3, 6}},
+     {"\U0000ac00\U00000308\U00000915"sv, {44032, 2325}, {5, 8}},
+     {"\U0000ac00\U000000a9"sv, {44032, 169}, {3, 5}},
+     {"\U0000ac00\U00000308\U000000a9"sv, {44032, 169}, {5, 7}},
+     {"\U0000ac00\U00000020"sv, {44032, 32}, {3, 4}},
+     {"\U0000ac00\U00000308\U00000020"sv, {44032, 32}, {5, 6}},
+     {"\U0000ac00\U00000378"sv, {44032, 888}, {3, 5}},
+     {"\U0000ac00\U00000308\U00000378"sv, {44032, 888}, {5, 7}},
+     {"\U0000ac01\U0000000d"sv, {44033, 13}, {3, 4}},
+     {"\U0000ac01\U00000308\U0000000d"sv, {44033, 13}, {5, 6}},
+     {"\U0000ac01\U0000000a"sv, {44033, 10}, {3, 4}},
+     {"\U0000ac01\U00000308\U0000000a"sv, {44033, 10}, {5, 6}},
+     {"\U0000ac01\U00000000"sv, {44033, 0}, {3, 4}},
+     {"\U0000ac01\U00000308\U00000000"sv, {44033, 0}, {5, 6}},
+     {"\U0000ac01\U0000094d"sv, {44033}, {6}},
+     {"\U0000ac01\U00000308\U0000094d"sv, {44033}, {8}},
+     {"\U0000ac01\U00000300"sv, {44033}, {5}},
+     {"\U0000ac01\U00000308\U00000300"sv, {44033}, {7}},
+     {"\U0000ac01\U0000200c"sv, {44033}, {6}},
+     {"\U0000ac01\U00000308\U0000200c"sv, {44033}, {8}},
+     {"\U0000ac01\U0000200d"sv, {44033}, {6}},
+     {"\U0000ac01\U00000308\U0000200d"sv, {44033}, {8}},
+     {"\U0000ac01\U0001f1e6"sv, {44033, 127462}, {3, 7}},
+     {"\U0000ac01\U00000308\U0001f1e6"sv, {44033, 127462}, {5, 9}},
+     {"\U0000ac01\U000006dd"sv, {44033, 1757}, {3, 5}},
+     {"\U0000ac01\U00000308\U000006dd"sv, {44033, 1757}, {5, 7}},
+     {"\U0000ac01\U00000903"sv, {44033}, {6}},
+     {"\U0000ac01\U00000308\U00000903"sv, {44033}, {8}},
+     {"\U0000ac01\U00001100"sv, {44033, 4352}, {3, 6}},
+     {"\U0000ac01\U00000308\U00001100"sv, {44033, 4352}, {5, 8}},
+     {"\U0000ac01\U00001160"sv, {44033, 4448}, {3, 6}},
+     {"\U0000ac01\U00000308\U00001160"sv, {44033, 4448}, {5, 8}},
+     {"\U0000ac01\U000011a8"sv, {44033}, {6}},
+     {"\U0000ac01\U00000308\U000011a8"sv, {44033, 4520}, {5, 8}},
+     {"\U0000ac01\U0000ac00"sv, {44033, 44032}, {3, 6}},
+     {"\U0000ac01\U00000308\U0000ac00"sv, {44033, 44032}, {5, 8}},
+     {"\U0000ac01\U0000ac01"sv, {44033, 44033}, {3, 6}},
+     {"\U0000ac01\U00000308\U0000ac01"sv, {44033, 44033}, {5, 8}},
+     {"\U0000ac01\U00001cf5"sv, {44033, 7413}, {3, 6}},
+     {"\U0000ac01\U00000308\U00001cf5"sv, {44033, 7413}, {5, 8}},
+     {"\U0000ac01\U00000915"sv, {44033, 2325}, {3, 6}},
+     {"\U0000ac01\U00000308\U00000915"sv, {44033, 2325}, {5, 8}},
+     {"\U0000ac01\U000000a9"sv, {44033, 169}, {3, 5}},
+     {"\U0000ac01\U00000308\U000000a9"sv, {44033, 169}, {5, 7}},
+     {"\U0000ac01\U00000020"sv, {44033, 32}, {3, 4}},
+     {"\U0000ac01\U00000308\U00000020"sv, {44033, 32}, {5, 6}},
+     {"\U0000ac01\U00000378"sv, {44033, 888}, {3, 5}},
+     {"\U0000ac01\U00000308\U00000378"sv, {44033, 888}, {5, 7}},
+     {"\U00001cf5\U0000000d"sv, {7413, 13}, {3, 4}},
+     {"\U00001cf5\U00000308\U0000000d"sv, {7413, 13}, {5, 6}},
+     {"\U00001cf5\U0000000a"sv, {7413, 10}, {3, 4}},
+     {"\U00001cf5\U00000308\U0000000a"sv, {7413, 10}, {5, 6}},
+     {"\U00001cf5\U00000000"sv, {7413, 0}, {3, 4}},
+     {"\U00001cf5\U00000308\U00000000"sv, {7413, 0}, {5, 6}},
+     {"\U00001cf5\U0000094d"sv, {7413}, {6}},
+     {"\U00001cf5\U00000308\U0000094d"sv, {7413}, {8}},
+     {"\U00001cf5\U00000300"sv, {7413}, {5}},
+     {"\U00001cf5\U00000308\U00000300"sv, {7413}, {7}},
+     {"\U00001cf5\U0000200c"sv, {7413}, {6}},
+     {"\U00001cf5\U00000308\U0000200c"sv, {7413}, {8}},
+     {"\U00001cf5\U0000200d"sv, {7413}, {6}},
+     {"\U00001cf5\U00000308\U0000200d"sv, {7413}, {8}},
+     {"\U00001cf5\U0001f1e6"sv, {7413, 127462}, {3, 7}},
+     {"\U00001cf5\U00000308\U0001f1e6"sv, {7413, 127462}, {5, 9}},
+     {"\U00001cf5\U000006dd"sv, {7413, 1757}, {3, 5}},
+     {"\U00001cf5\U00000308\U000006dd"sv, {7413, 1757}, {5, 7}},
+     {"\U00001cf5\U00000903"sv, {7413}, {6}},
+     {"\U00001cf5\U00000308\U00000903"sv, {7413}, {8}},
+     {"\U00001cf5\U00001100"sv, {7413, 4352}, {3, 6}},
+     {"\U00001cf5\U00000308\U00001100"sv, {7413, 4352}, {5, 8}},
+     {"\U00001cf5\U00001160"sv, {7413, 4448}, {3, 6}},
+     {"\U00001cf5\U00000308\U00001160"sv, {7413, 4448}, {5, 8}},
+     {"\U00001cf5\U000011a8"sv, {7413, 4520}, {3, 6}},
+     {"\U00001cf5\U00000308\U000011a8"sv, {7413, 4520}, {5, 8}},
+     {"\U00001cf5\U0000ac00"sv, {7413, 44032}, {3, 6}},
+     {"\U00001cf5\U00000308\U0000ac00"sv, {7413, 44032}, {5, 8}},
+     {"\U00001cf5\U0000ac01"sv, {7413, 44033}, {3, 6}},
+     {"\U00001cf5\U00000308\U0000ac01"sv, {7413, 44033}, {5, 8}},
+     {"\U00001cf5\U00001cf5"sv, {7413, 7413}, {3, 6}},
+     {"\U00001cf5\U00000308\U00001cf5"sv, {7413, 7413}, {5, 8}},
+     {"\U00001cf5\U00000915"sv, {7413}, {6}},
+     {"\U00001cf5\U00000308\U00000915"sv, {7413}, {8}},
+     {"\U00001cf5\U000000a9"sv, {7413, 169}, {3, 5}},
+     {"\U00001cf5\U00000308\U000000a9"sv, {7413, 169}, {5, 7}},
+     {"\U00001cf5\U00000020"sv, {7413, 32}, {3, 4}},
+     {"\U00001cf5\U00000308\U00000020"sv, {7413, 32}, {5, 6}},
+     {"\U00001cf5\U00000378"sv, {7413, 888}, {3, 5}},
+     {"\U00001cf5\U00000308\U00000378"sv, {7413, 888}, {5, 7}},
+     {"\U00000915\U0000000d"sv, {2325, 13}, {3, 4}},
+     {"\U00000915\U00000308\U0000000d"sv, {2325, 13}, {5, 6}},
+     {"\U00000915\U0000000a"sv, {2325, 10}, {3, 4}},
+     {"\U00000915\U00000308\U0000000a"sv, {2325, 10}, {5, 6}},
+     {"\U00000915\U00000000"sv, {2325, 0}, {3, 4}},
+     {"\U00000915\U00000308\U00000000"sv, {2325, 0}, {5, 6}},
+     {"\U00000915\U0000094d"sv, {2325}, {6}},
+     {"\U00000915\U00000308\U0000094d"sv, {2325}, {8}},
+     {"\U00000915\U00000300"sv, {2325}, {5}},
+     {"\U00000915\U00000308\U00000300"sv, {2325}, {7}},
+     {"\U00000915\U0000200c"sv, {2325}, {6}},
+     {"\U00000915\U00000308\U0000200c"sv, {2325}, {8}},
+     {"\U00000915\U0000200d"sv, {2325}, {6}},
+     {"\U00000915\U00000308\U0000200d"sv, {2325}, {8}},
+     {"\U00000915\U0001f1e6"sv, {2325, 127462}, {3, 7}},
+     {"\U00000915\U00000308\U0001f1e6"sv, {2325, 127462}, {5, 9}},
+     {"\U00000915\U000006dd"sv, {2325, 1757}, {3, 5}},
+     {"\U00000915\U00000308\U000006dd"sv, {2325, 1757}, {5, 7}},
+     {"\U00000915\U00000903"sv, {2325}, {6}},
+     {"\U00000915\U00000308\U00000903"sv, {2325}, {8}},
+     {"\U00000915\U00001100"sv, {2325, 4352}, {3, 6}},
+     {"\U00000915\U00000308\U00001100"sv, {2325, 4352}, {5, 8}},
+     {"\U00000915\U00001160"sv, {2325, 4448}, {3, 6}},
+     {"\U00000915\U00000308\U00001160"sv, {2325, 4448}, {5, 8}},
+     {"\U00000915\U000011a8"sv, {2325, 4520}, {3, 6}},
+     {"\U00000915\U00000308\U000011a8"sv, {2325, 4520}, {5, 8}},
+     {"\U00000915\U0000ac00"sv, {2325, 44032}, {3, 6}},
+     {"\U00000915\U00000308\U0000ac00"sv, {2325, 44032}, {5, 8}},
+     {"\U00000915\U0000ac01"sv, {2325, 44033}, {3, 6}},
+     {"\U00000915\U00000308\U0000ac01"sv, {2325, 44033}, {5, 8}},
+     {"\U00000915\U00001cf5"sv, {2325, 7413}, {3, 6}},
+     {"\U00000915\U00000308\U00001cf5"sv, {2325, 7413}, {5, 8}},
+     {"\U00000915\U00000915"sv, {2325, 2325}, {3, 6}},
+     {"\U00000915\U00000308\U00000915"sv, {2325, 2325}, {5, 8}},
+     {"\U00000915\U000000a9"sv, {2325, 169}, {3, 5}},
+     {"\U00000915\U00000308\U000000a9"sv, {2325, 169}, {5, 7}},
+     {"\U00000915\U00000020"sv, {2325, 32}, {3, 4}},
+     {"\U00000915\U00000308\U00000020"sv, {2325, 32}, {5, 6}},
+     {"\U00000915\U00000378"sv, {2325, 888}, {3, 5}},
+     {"\U00000915\U00000308\U00000378"sv, {2325, 888}, {5, 7}},
+     {"\U000000a9\U0000000d"sv, {169, 13}, {2, 3}},
+     {"\U000000a9\U00000308\U0000000d"sv, {169, 13}, {4, 5}},
+     {"\U000000a9\U0000000a"sv, {169, 10}, {2, 3}},
+     {"\U000000a9\U00000308\U0000000a"sv, {169, 10}, {4, 5}},
+     {"\U000000a9\U00000000"sv, {169, 0}, {2, 3}},
+     {"\U000000a9\U00000308\U00000000"sv, {169, 0}, {4, 5}},
+     {"\U000000a9\U0000094d"sv, {169}, {5}},
+     {"\U000000a9\U00000308\U0000094d"sv, {169}, {7}},
+     {"\U000000a9\U00000300"sv, {169}, {4}},
+     {"\U000000a9\U00000308\U00000300"sv, {169}, {6}},
+     {"\U000000a9\U0000200c"sv, {169}, {5}},
+     {"\U000000a9\U00000308\U0000200c"sv, {169}, {7}},
+     {"\U000000a9\U0000200d"sv, {169}, {5}},
+     {"\U000000a9\U00000308\U0000200d"sv, {169}, {7}},
+     {"\U000000a9\U0001f1e6"sv, {169, 127462}, {2, 6}},
+     {"\U000000a9\U00000308\U0001f1e6"sv, {169, 127462}, {4, 8}},
+     {"\U000000a9\U000006dd"sv, {169, 1757}, {2, 4}},
+     {"\U000000a9\U00000308\U000006dd"sv, {169, 1757}, {4, 6}},
+     {"\U000000a9\U00000903"sv, {169}, {5}},
+     {"\U000000a9\U00000308\U00000903"sv, {169}, {7}},
+     {"\U000000a9\U00001100"sv, {169, 4352}, {2, 5}},
+     {"\U000000a9\U00000308\U00001100"sv, {169, 4352}, {4, 7}},
+     {"\U000000a9\U00001160"sv, {169, 4448}, {2, 5}},
+     {"\U000000a9\U00000308\U00001160"sv, {169, 4448}, {4, 7}},
+     {"\U000000a9\U000011a8"sv, {169, 4520}, {2, 5}},
+     {"\U000000a9\U00000308\U000011a8"sv, {169, 4520}, {4, 7}},
+     {"\U000000a9\U0000ac00"sv, {169, 44032}, {2, 5}},
+     {"\U000000a9\U00000308\U0000ac00"sv, {169, 44032}, {4, 7}},
+     {"\U000000a9\U0000ac01"sv, {169, 44033}, {2, 5}},
+     {"\U000000a9\U00000308\U0000ac01"sv, {169, 44033}, {4, 7}},
+     {"\U000000a9\U00001cf5"sv, {169, 7413}, {2, 5}},
+     {"\U000000a9\U00000308\U00001cf5"sv, {169, 7413}, {4, 7}},
+     {"\U000000a9\U00000915"sv, {169, 2325}, {2, 5}},
+     {"\U000000a9\U00000308\U00000915"sv, {169, 2325}, {4, 7}},
+     {"\U000000a9\U000000a9"sv, {169, 169}, {2, 4}},
+     {"\U000000a9\U00000308\U000000a9"sv, {169, 169}, {4, 6}},
+     {"\U000000a9\U00000020"sv, {169, 32}, {2, 3}},
+     {"\U000000a9\U00000308\U00000020"sv, {169, 32}, {4, 5}},
+     {"\U000000a9\U00000378"sv, {169, 888}, {2, 4}},
+     {"\U000000a9\U00000308\U00000378"sv, {169, 888}, {4, 6}},
+     {"\U00000020\U0000000d"sv, {32, 13}, {1, 2}},
+     {"\U00000020\U00000308\U0000000d"sv, {32, 13}, {3, 4}},
+     {"\U00000020\U0000000a"sv, {32, 10}, {1, 2}},
+     {"\U00000020\U00000308\U0000000a"sv, {32, 10}, {3, 4}},
+     {"\U00000020\U00000000"sv, {32, 0}, {1, 2}},
+     {"\U00000020\U00000308\U00000000"sv, {32, 0}, {3, 4}},
+     {"\U00000020\U0000094d"sv, {32}, {4}},
+     {"\U00000020\U00000308\U0000094d"sv, {32}, {6}},
+     {"\U00000020\U00000300"sv, {32}, {3}},
+     {"\U00000020\U00000308\U00000300"sv, {32}, {5}},
+     {"\U00000020\U0000200c"sv, {32}, {4}},
+     {"\U00000020\U00000308\U0000200c"sv, {32}, {6}},
+     {"\U00000020\U0000200d"sv, {32}, {4}},
+     {"\U00000020\U00000308\U0000200d"sv, {32}, {6}},
+     {"\U00000020\U0001f1e6"sv, {32, 127462}, {1, 5}},
+     {"\U00000020\U00000308\U0001f1e6"sv, {32, 127462}, {3, 7}},
+     {"\U00000020\U000006dd"sv, {32, 1757}, {1, 3}},
+     {"\U00000020\U00000308\U000006dd"sv, {32, 1757}, {3, 5}},
+     {"\U00000020\U00000903"sv, {32}, {4}},
+     {"\U00000020\U00000308\U00000903"sv, {32}, {6}},
+     {"\U00000020\U00001100"sv, {32, 4352}, {1, 4}},
+     {"\U00000020\U00000308\U00001100"sv, {32, 4352}, {3, 6}},
+     {"\U00000020\U00001160"sv, {32, 4448}, {1, 4}},
+     {"\U00000020\U00000308\U00001160"sv, {32, 4448}, {3, 6}},
+     {"\U00000020\U000011a8"sv, {32, 4520}, {1, 4}},
+     {"\U00000020\U00000308\U000011a8"sv, {32, 4520}, {3, 6}},
+     {"\U00000020\U0000ac00"sv, {32, 44032}, {1, 4}},
+     {"\U00000020\U00000308\U0000ac00"sv, {32, 44032}, {3, 6}},
+     {"\U00000020\U0000ac01"sv, {32, 44033}, {1, 4}},
+     {"\U00000020\U00000308\U0000ac01"sv, {32, 44033}, {3, 6}},
+     {"\U00000020\U00001cf5"sv, {32, 7413}, {1, 4}},
+     {"\U00000020\U00000308\U00001cf5"sv, {32, 7413}, {3, 6}},
+     {"\U00000020\U00000915"sv, {32, 2325}, {1, 4}},
+     {"\U00000020\U00000308\U00000915"sv, {32, 2325}, {3, 6}},
+     {"\U00000020\U000000a9"sv, {32, 169}, {1, 3}},
+     {"\U00000020\U00000308\U000000a9"sv, {32, 169}, {3, 5}},
+     {"\U00000020\U00000020"sv, {32, 32}, {1, 2}},
+     {"\U00000020\U00000308\U00000020"sv, {32, 32}, {3, 4}},
+     {"\U00000020\U00000378"sv, {32, 888}, {1, 3}},
+     {"\U00000020\U00000308\U00000378"sv, {32, 888}, {3, 5}},
+     {"\U00000378\U0000000d"sv, {888, 13}, {2, 3}},
+     {"\U00000378\U00000308\U0000000d"sv, {888, 13}, {4, 5}},
+     {"\U00000378\U0000000a"sv, {888, 10}, {2, 3}},
+     {"\U00000378\U00000308\U0000000a"sv, {888, 10}, {4, 5}},
+     {"\U00000378\U00000000"sv, {888, 0}, {2, 3}},
+     {"\U00000378\U00000308\U00000000"sv, {888, 0}, {4, 5}},
+     {"\U00000378\U0000094d"sv, {888}, {5}},
+     {"\U00000378\U00000308\U0000094d"sv, {888}, {7}},
+     {"\U00000378\U00000300"sv, {888}, {4}},
+     {"\U00000378\U00000308\U00000300"sv, {888}, {6}},
+     {"\U00000378\U0000200c"sv, {888}, {5}},
+     {"\U00000378\U00000308\U0000200c"sv, {888}, {7}},
+     {"\U00000378\U0000200d"sv, {888}, {5}},
+     {"\U00000378\U00000308\U0000200d"sv, {888}, {7}},
+     {"\U00000378\U0001f1e6"sv, {888, 127462}, {2, 6}},
+     {"\U00000378\U00000308\U0001f1e6"sv, {888, 127462}, {4, 8}},
+     {"\U00000378\U000006dd"sv, {888, 1757}, {2, 4}},
+     {"\U00000378\U00000308\U000006dd"sv, {888, 1757}, {4, 6}},
+     {"\U00000378\U00000903"sv, {888}, {5}},
+     {"\U00000378\U00000308\U00000903"sv, {888}, {7}},
+     {"\U00000378\U00001100"sv, {888, 4352}, {2, 5}},
+     {"\U00000378\U00000308\U00001100"sv, {888, 4352}, {4, 7}},
+     {"\U00000378\U00001160"sv, {888, 4448}, {2, 5}},
+     {"\U00000378\U00000308\U00001160"sv, {888, 4448}, {4, 7}},
+     {"\U00000378\U000011a8"sv, {888, 4520}, {2, 5}},
+     {"\U00000378\U00000308\U000011a8"sv, {888, 4520}, {4, 7}},
+     {"\U00000378\U0000ac00"sv, {888, 44032}, {2, 5}},
+     {"\U00000378\U00000308\U0000ac00"sv, {888, 44032}, {4, 7}},
+     {"\U00000378\U0000ac01"sv, {888, 44033}, {2, 5}},
+     {"\U00000378\U00000308\U0000ac01"sv, {888, 44033}, {4, 7}},
+     {"\U00000378\U00001cf5"sv, {888, 7413}, {2, 5}},
+     {"\U00000378\U00000308\U00001cf5"sv, {888, 7413}, {4, 7}},
+     {"\U00000378\U00000915"sv, {888, 2325}, {2, 5}},
+     {"\U00000378\U00000308\U00000915"sv, {888, 2325}, {4, 7}},
+     {"\U00000378\U000000a9"sv, {888, 169}, {2, 4}},
+     {"\U00000378\U00000308\U000000a9"sv, {888, 169}, {4, 6}},
+     {"\U00000378\U00000020"sv, {888, 32}, {2, 3}},
+     {"\U00000378\U00000308\U00000020"sv, {888, 32}, {4, 5}},
+     {"\U00000378\U00000378"sv, {888, 888}, {2, 4}},
+     {"\U00000378\U00000308\U00000378"sv, {888, 888}, {4, 6}},
+     {"\U0000000d\U0000000a\U00000061\U0000000a\U00000308"sv, {13, 97, 10, 776}, {2, 3, 4, 6}},
+     {"\U00000061\U00000308"sv, {97}, {3}},
+     {"\U00000020\U0000200d\U00000646"sv, {32, 1606}, {4, 6}},
+     {"\U00000646\U0000200d\U00000020"sv, {1606, 32}, {5, 6}},
+     {"\U00001100\U00001100"sv, {4352}, {6}},
+     {"\U0000ac00\U000011a8\U00001100"sv, {44032, 4352}, {6, 9}},
+     {"\U0000ac01\U000011a8\U00001100"sv, {44033, 4352}, {6, 9}},
+     {"\U0001f1e6\U0001f1e7\U0001f1e8\U00000062"sv, {127462, 127464, 98}, {8, 12, 13}},
+     {"\U00000061\U0001f1e6\U0001f1e7\U0001f1e8\U00000062"sv, {97, 127462, 127464, 98}, {1, 9, 13, 14}},
+     {"\U00000061\U0001f1e6\U0001f1e7\U0000200d\U0001f1e8\U00000062"sv, {97, 127462, 127464, 98}, {1, 12, 16, 17}},
+     {"\U00000061\U0001f1e6\U0000200d\U0001f1e7\U0001f1e8\U00000062"sv, {97, 127462, 127463, 98}, {1, 8, 16, 17}},
+     {"\U00000061\U0001f1e6\U0001f1e7\U0001f1e8\U0001f1e9\U00000062"sv, {97, 127462, 127464, 98}, {1, 9, 17, 18}},
+     {"\U00000061\U0000200d"sv, {97}, {4}},
+     {"\U00000061\U00000308\U00000062"sv, {97, 98}, {3, 4}},
+     {"\U00000061\U00000903\U00000062"sv, {97, 98}, {4, 5}},
+     {"\U00000061\U00000600\U00000062"sv, {97, 1536}, {1, 4}},
+     {"\U0001f476\U0001f3ff\U0001f476"sv, {128118, 128118}, {8, 12}},
+     {"\U00000061\U0001f3ff\U0001f476"sv, {97, 128118}, {5, 9}},
+     {"\U00000061\U0001f3ff\U0001f476\U0000200d\U0001f6d1"sv, {97, 128118}, {5, 16}},
+     {"\U0001f476\U0001f3ff\U00000308\U0000200d\U0001f476\U0001f3ff"sv, {128118}, {21}},
+     {"\U0001f6d1\U0000200d\U0001f6d1"sv, {128721}, {11}},
+     {"\U00000061\U0000200d\U0001f6d1"sv, {97, 128721}, {4, 8}},
+     {"\U00002701\U0000200d\U00002701"sv, {9985, 9985}, {6, 9}},
+     {"\U00000061\U0000200d\U00002701"sv, {97, 9985}, {4, 7}},
+     {"\U00000915\U00000924"sv, {2325, 2340}, {3, 6}},
+     {"\U00000915\U0000094d\U00000924"sv, {2325}, {9}},
+     {"\U00000915\U0000094d\U0000094d\U00000924"sv, {2325}, {12}},
+     {"\U00000915\U0000094d\U0000200d\U00000924"sv, {2325}, {12}},
+     {"\U00000915\U0000093c\U0000200d\U0000094d\U00000924"sv, {2325}, {15}},
+     {"\U00000915\U0000093c\U0000094d\U0000200d\U00000924"sv, {2325}, {15}},
+     {"\U00000915\U0000094d\U00000924\U0000094d\U0000092f"sv, {2325}, {15}},
+     {"\U00000915\U0000094d\U00000061"sv, {2325, 97}, {6, 7}},
+     {"\U00000061\U0000094d\U00000924"sv, {97}, {7}},
+     {"\U0000003f\U0000094d\U00000924"sv, {63}, {7}},
+     {"\U00000915\U0000094d\U0000094d\U00000924"sv, {2325}, {12}},
+     {"\U00000ab8\U00000afb\U00000acd\U00000ab8\U00000afb"sv, {2744}, {15}},
+     {"\U00001019\U00001039\U00001018\U0000102c\U00001037"sv, {4121, 4140}, {9, 15}},
+     {"\U00001004\U0000103a\U00001039\U00001011\U00001039\U00001011"sv, {4100}, {18}},
+     {"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38"sv, {6930, 6962, 6962, 6962}, {6, 15, 30, 42}},
+     {"\U0000179f\U000017d2\U0000178f\U000017d2\U0000179a\U000017b8"sv, {6047}, {18}},
+     {"\U00001b26\U00001b17\U00001b44\U00001b13"sv, {6950, 6935}, {3, 12}},
+     {"\U00001b27\U00001b13\U00001b44\U00001b0b\U00001b0b\U00001b04"sv, {6951, 6931, 6923}, {3, 12, 18}},
+     {"\U00001795\U000017d2\U000017af\U00001798"sv, {6037, 6040}, {9, 12}},
+     {"\U000017a0\U000017d2\U000017ab\U00001791\U000017d0\U00001799"sv, {6048, 6033, 6041}, {9, 15, 18}},
+     {"\U00001b05\U00001b44\U00001b33\U00001b03"sv, {6917}, {12}},
+     {"\U00000cf1\U00000c95"sv, {3313, 3221}, {3, 6}},
+     {"\U00000cf2\U00000cab"sv, {3314, 3243}, {3, 6}},
+     {"\U00000cf1\U00000c95\U00000cbf"sv, {3313, 3221}, {3, 9}},
+     {"\U00001cf5\U00000995"sv, {7413}, {6}},
+     {"\U00001cf6\U000009aa"sv, {7414}, {6}},
+     {"\U00001cf5\U0000200c\U00000995"sv, {7413, 2453}, {6, 9}},
+     {"\U00001cf6\U0000200c\U000009aa"sv, {7414, 2474}, {6, 9}},
+     {"\U00011a3a\U00011a0b"sv, {72250}, {8}}}};
 
 /// The data for UTF-16.
 ///
@@ -944,859 +946,859 @@ std::array<data<char>, 853> data_utf8 = {{
 /// values.
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
 std::array<data<wchar_t>, 853> data_utf16 = {{
-     {L"\U0000000d\U0000000d", {13, 13}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000000d", {13, 776, 13}, {1, 2, 3}},
-     {L"\U0000000d\U0000000a", {13}, {2}},
-     {L"\U0000000d\U00000308\U0000000a", {13, 776, 10}, {1, 2, 3}},
-     {L"\U0000000d\U00000000", {13, 0}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000000", {13, 776, 0}, {1, 2, 3}},
-     {L"\U0000000d\U0000094d", {13, 2381}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000094d", {13, 776}, {1, 3}},
-     {L"\U0000000d\U00000300", {13, 768}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000300", {13, 776}, {1, 3}},
-     {L"\U0000000d\U0000200c", {13, 8204}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000200c", {13, 776}, {1, 3}},
-     {L"\U0000000d\U0000200d", {13, 8205}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000200d", {13, 776}, {1, 3}},
-     {L"\U0000000d\U0001f1e6", {13, 127462}, {1, 3}},
-     {L"\U0000000d\U00000308\U0001f1e6", {13, 776, 127462}, {1, 2, 4}},
-     {L"\U0000000d\U000006dd", {13, 1757}, {1, 2}},
-     {L"\U0000000d\U00000308\U000006dd", {13, 776, 1757}, {1, 2, 3}},
-     {L"\U0000000d\U00000903", {13, 2307}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000903", {13, 776}, {1, 3}},
-     {L"\U0000000d\U00001100", {13, 4352}, {1, 2}},
-     {L"\U0000000d\U00000308\U00001100", {13, 776, 4352}, {1, 2, 3}},
-     {L"\U0000000d\U00001160", {13, 4448}, {1, 2}},
-     {L"\U0000000d\U00000308\U00001160", {13, 776, 4448}, {1, 2, 3}},
-     {L"\U0000000d\U000011a8", {13, 4520}, {1, 2}},
-     {L"\U0000000d\U00000308\U000011a8", {13, 776, 4520}, {1, 2, 3}},
-     {L"\U0000000d\U0000ac00", {13, 44032}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000ac00", {13, 776, 44032}, {1, 2, 3}},
-     {L"\U0000000d\U0000ac01", {13, 44033}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000ac01", {13, 776, 44033}, {1, 2, 3}},
-     {L"\U0000000d\U00001cf5", {13, 7413}, {1, 2}},
-     {L"\U0000000d\U00000308\U00001cf5", {13, 776, 7413}, {1, 2, 3}},
-     {L"\U0000000d\U00000915", {13, 2325}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000915", {13, 776, 2325}, {1, 2, 3}},
-     {L"\U0000000d\U000000a9", {13, 169}, {1, 2}},
-     {L"\U0000000d\U00000308\U000000a9", {13, 776, 169}, {1, 2, 3}},
-     {L"\U0000000d\U00000020", {13, 32}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000020", {13, 776, 32}, {1, 2, 3}},
-     {L"\U0000000d\U00000378", {13, 888}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000378", {13, 776, 888}, {1, 2, 3}},
-     {L"\U0000000a\U0000000d", {10, 13}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000000d", {10, 776, 13}, {1, 2, 3}},
-     {L"\U0000000a\U0000000a", {10, 10}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000000a", {10, 776, 10}, {1, 2, 3}},
-     {L"\U0000000a\U00000000", {10, 0}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000000", {10, 776, 0}, {1, 2, 3}},
-     {L"\U0000000a\U0000094d", {10, 2381}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000094d", {10, 776}, {1, 3}},
-     {L"\U0000000a\U00000300", {10, 768}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000300", {10, 776}, {1, 3}},
-     {L"\U0000000a\U0000200c", {10, 8204}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000200c", {10, 776}, {1, 3}},
-     {L"\U0000000a\U0000200d", {10, 8205}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000200d", {10, 776}, {1, 3}},
-     {L"\U0000000a\U0001f1e6", {10, 127462}, {1, 3}},
-     {L"\U0000000a\U00000308\U0001f1e6", {10, 776, 127462}, {1, 2, 4}},
-     {L"\U0000000a\U000006dd", {10, 1757}, {1, 2}},
-     {L"\U0000000a\U00000308\U000006dd", {10, 776, 1757}, {1, 2, 3}},
-     {L"\U0000000a\U00000903", {10, 2307}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000903", {10, 776}, {1, 3}},
-     {L"\U0000000a\U00001100", {10, 4352}, {1, 2}},
-     {L"\U0000000a\U00000308\U00001100", {10, 776, 4352}, {1, 2, 3}},
-     {L"\U0000000a\U00001160", {10, 4448}, {1, 2}},
-     {L"\U0000000a\U00000308\U00001160", {10, 776, 4448}, {1, 2, 3}},
-     {L"\U0000000a\U000011a8", {10, 4520}, {1, 2}},
-     {L"\U0000000a\U00000308\U000011a8", {10, 776, 4520}, {1, 2, 3}},
-     {L"\U0000000a\U0000ac00", {10, 44032}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000ac00", {10, 776, 44032}, {1, 2, 3}},
-     {L"\U0000000a\U0000ac01", {10, 44033}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000ac01", {10, 776, 44033}, {1, 2, 3}},
-     {L"\U0000000a\U00001cf5", {10, 7413}, {1, 2}},
-     {L"\U0000000a\U00000308\U00001cf5", {10, 776, 7413}, {1, 2, 3}},
-     {L"\U0000000a\U00000915", {10, 2325}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000915", {10, 776, 2325}, {1, 2, 3}},
-     {L"\U0000000a\U000000a9", {10, 169}, {1, 2}},
-     {L"\U0000000a\U00000308\U000000a9", {10, 776, 169}, {1, 2, 3}},
-     {L"\U0000000a\U00000020", {10, 32}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000020", {10, 776, 32}, {1, 2, 3}},
-     {L"\U0000000a\U00000378", {10, 888}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000378", {10, 776, 888}, {1, 2, 3}},
-     {L"\U00000000\U0000000d", {0, 13}, {1, 2}},
-     {L"\U00000000\U00000308\U0000000d", {0, 776, 13}, {1, 2, 3}},
-     {L"\U00000000\U0000000a", {0, 10}, {1, 2}},
-     {L"\U00000000\U00000308\U0000000a", {0, 776, 10}, {1, 2, 3}},
-     {L"\U00000000\U00000000", {0, 0}, {1, 2}},
-     {L"\U00000000\U00000308\U00000000", {0, 776, 0}, {1, 2, 3}},
-     {L"\U00000000\U0000094d", {0, 2381}, {1, 2}},
-     {L"\U00000000\U00000308\U0000094d", {0, 776}, {1, 3}},
-     {L"\U00000000\U00000300", {0, 768}, {1, 2}},
-     {L"\U00000000\U00000308\U00000300", {0, 776}, {1, 3}},
-     {L"\U00000000\U0000200c", {0, 8204}, {1, 2}},
-     {L"\U00000000\U00000308\U0000200c", {0, 776}, {1, 3}},
-     {L"\U00000000\U0000200d", {0, 8205}, {1, 2}},
-     {L"\U00000000\U00000308\U0000200d", {0, 776}, {1, 3}},
-     {L"\U00000000\U0001f1e6", {0, 127462}, {1, 3}},
-     {L"\U00000000\U00000308\U0001f1e6", {0, 776, 127462}, {1, 2, 4}},
-     {L"\U00000000\U000006dd", {0, 1757}, {1, 2}},
-     {L"\U00000000\U00000308\U000006dd", {0, 776, 1757}, {1, 2, 3}},
-     {L"\U00000000\U00000903", {0, 2307}, {1, 2}},
-     {L"\U00000000\U00000308\U00000903", {0, 776}, {1, 3}},
-     {L"\U00000000\U00001100", {0, 4352}, {1, 2}},
-     {L"\U00000000\U00000308\U00001100", {0, 776, 4352}, {1, 2, 3}},
-     {L"\U00000000\U00001160", {0, 4448}, {1, 2}},
-     {L"\U00000000\U00000308\U00001160", {0, 776, 4448}, {1, 2, 3}},
-     {L"\U00000000\U000011a8", {0, 4520}, {1, 2}},
-     {L"\U00000000\U00000308\U000011a8", {0, 776, 4520}, {1, 2, 3}},
-     {L"\U00000000\U0000ac00", {0, 44032}, {1, 2}},
-     {L"\U00000000\U00000308\U0000ac00", {0, 776, 44032}, {1, 2, 3}},
-     {L"\U00000000\U0000ac01", {0, 44033}, {1, 2}},
-     {L"\U00000000\U00000308\U0000ac01", {0, 776, 44033}, {1, 2, 3}},
-     {L"\U00000000\U00001cf5", {0, 7413}, {1, 2}},
-     {L"\U00000000\U00000308\U00001cf5", {0, 776, 7413}, {1, 2, 3}},
-     {L"\U00000000\U00000915", {0, 2325}, {1, 2}},
-     {L"\U00000000\U00000308\U00000915", {0, 776, 2325}, {1, 2, 3}},
-     {L"\U00000000\U000000a9", {0, 169}, {1, 2}},
-     {L"\U00000000\U00000308\U000000a9", {0, 776, 169}, {1, 2, 3}},
-     {L"\U00000000\U00000020", {0, 32}, {1, 2}},
-     {L"\U00000000\U00000308\U00000020", {0, 776, 32}, {1, 2, 3}},
-     {L"\U00000000\U00000378", {0, 888}, {1, 2}},
-     {L"\U00000000\U00000308\U00000378", {0, 776, 888}, {1, 2, 3}},
-     {L"\U0000094d\U0000000d", {2381, 13}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000000d", {2381, 13}, {2, 3}},
-     {L"\U0000094d\U0000000a", {2381, 10}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000000a", {2381, 10}, {2, 3}},
-     {L"\U0000094d\U00000000", {2381, 0}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000000", {2381, 0}, {2, 3}},
-     {L"\U0000094d\U0000094d", {2381}, {2}},
-     {L"\U0000094d\U00000308\U0000094d", {2381}, {3}},
-     {L"\U0000094d\U00000300", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000300", {2381}, {3}},
-     {L"\U0000094d\U0000200c", {2381}, {2}},
-     {L"\U0000094d\U00000308\U0000200c", {2381}, {3}},
-     {L"\U0000094d\U0000200d", {2381}, {2}},
-     {L"\U0000094d\U00000308\U0000200d", {2381}, {3}},
-     {L"\U0000094d\U0001f1e6", {2381, 127462}, {1, 3}},
-     {L"\U0000094d\U00000308\U0001f1e6", {2381, 127462}, {2, 4}},
-     {L"\U0000094d\U000006dd", {2381, 1757}, {1, 2}},
-     {L"\U0000094d\U00000308\U000006dd", {2381, 1757}, {2, 3}},
-     {L"\U0000094d\U00000903", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000903", {2381}, {3}},
-     {L"\U0000094d\U00001100", {2381, 4352}, {1, 2}},
-     {L"\U0000094d\U00000308\U00001100", {2381, 4352}, {2, 3}},
-     {L"\U0000094d\U00001160", {2381, 4448}, {1, 2}},
-     {L"\U0000094d\U00000308\U00001160", {2381, 4448}, {2, 3}},
-     {L"\U0000094d\U000011a8", {2381, 4520}, {1, 2}},
-     {L"\U0000094d\U00000308\U000011a8", {2381, 4520}, {2, 3}},
-     {L"\U0000094d\U0000ac00", {2381, 44032}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000ac00", {2381, 44032}, {2, 3}},
-     {L"\U0000094d\U0000ac01", {2381, 44033}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000ac01", {2381, 44033}, {2, 3}},
-     {L"\U0000094d\U00001cf5", {2381, 7413}, {1, 2}},
-     {L"\U0000094d\U00000308\U00001cf5", {2381, 7413}, {2, 3}},
-     {L"\U0000094d\U00000915", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000915", {2381}, {3}},
-     {L"\U0000094d\U000000a9", {2381, 169}, {1, 2}},
-     {L"\U0000094d\U00000308\U000000a9", {2381, 169}, {2, 3}},
-     {L"\U0000094d\U00000020", {2381, 32}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000020", {2381, 32}, {2, 3}},
-     {L"\U0000094d\U00000378", {2381, 888}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000378", {2381, 888}, {2, 3}},
-     {L"\U00000300\U0000000d", {768, 13}, {1, 2}},
-     {L"\U00000300\U00000308\U0000000d", {768, 13}, {2, 3}},
-     {L"\U00000300\U0000000a", {768, 10}, {1, 2}},
-     {L"\U00000300\U00000308\U0000000a", {768, 10}, {2, 3}},
-     {L"\U00000300\U00000000", {768, 0}, {1, 2}},
-     {L"\U00000300\U00000308\U00000000", {768, 0}, {2, 3}},
-     {L"\U00000300\U0000094d", {768}, {2}},
-     {L"\U00000300\U00000308\U0000094d", {768}, {3}},
-     {L"\U00000300\U00000300", {768}, {2}},
-     {L"\U00000300\U00000308\U00000300", {768}, {3}},
-     {L"\U00000300\U0000200c", {768}, {2}},
-     {L"\U00000300\U00000308\U0000200c", {768}, {3}},
-     {L"\U00000300\U0000200d", {768}, {2}},
-     {L"\U00000300\U00000308\U0000200d", {768}, {3}},
-     {L"\U00000300\U0001f1e6", {768, 127462}, {1, 3}},
-     {L"\U00000300\U00000308\U0001f1e6", {768, 127462}, {2, 4}},
-     {L"\U00000300\U000006dd", {768, 1757}, {1, 2}},
-     {L"\U00000300\U00000308\U000006dd", {768, 1757}, {2, 3}},
-     {L"\U00000300\U00000903", {768}, {2}},
-     {L"\U00000300\U00000308\U00000903", {768}, {3}},
-     {L"\U00000300\U00001100", {768, 4352}, {1, 2}},
-     {L"\U00000300\U00000308\U00001100", {768, 4352}, {2, 3}},
-     {L"\U00000300\U00001160", {768, 4448}, {1, 2}},
-     {L"\U00000300\U00000308\U00001160", {768, 4448}, {2, 3}},
-     {L"\U00000300\U000011a8", {768, 4520}, {1, 2}},
-     {L"\U00000300\U00000308\U000011a8", {768, 4520}, {2, 3}},
-     {L"\U00000300\U0000ac00", {768, 44032}, {1, 2}},
-     {L"\U00000300\U00000308\U0000ac00", {768, 44032}, {2, 3}},
-     {L"\U00000300\U0000ac01", {768, 44033}, {1, 2}},
-     {L"\U00000300\U00000308\U0000ac01", {768, 44033}, {2, 3}},
-     {L"\U00000300\U00001cf5", {768, 7413}, {1, 2}},
-     {L"\U00000300\U00000308\U00001cf5", {768, 7413}, {2, 3}},
-     {L"\U00000300\U00000915", {768, 2325}, {1, 2}},
-     {L"\U00000300\U00000308\U00000915", {768, 2325}, {2, 3}},
-     {L"\U00000300\U000000a9", {768, 169}, {1, 2}},
-     {L"\U00000300\U00000308\U000000a9", {768, 169}, {2, 3}},
-     {L"\U00000300\U00000020", {768, 32}, {1, 2}},
-     {L"\U00000300\U00000308\U00000020", {768, 32}, {2, 3}},
-     {L"\U00000300\U00000378", {768, 888}, {1, 2}},
-     {L"\U00000300\U00000308\U00000378", {768, 888}, {2, 3}},
-     {L"\U0000200c\U0000000d", {8204, 13}, {1, 2}},
-     {L"\U0000200c\U00000308\U0000000d", {8204, 13}, {2, 3}},
-     {L"\U0000200c\U0000000a", {8204, 10}, {1, 2}},
-     {L"\U0000200c\U00000308\U0000000a", {8204, 10}, {2, 3}},
-     {L"\U0000200c\U00000000", {8204, 0}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000000", {8204, 0}, {2, 3}},
-     {L"\U0000200c\U0000094d", {8204}, {2}},
-     {L"\U0000200c\U00000308\U0000094d", {8204}, {3}},
-     {L"\U0000200c\U00000300", {8204}, {2}},
-     {L"\U0000200c\U00000308\U00000300", {8204}, {3}},
-     {L"\U0000200c\U0000200c", {8204}, {2}},
-     {L"\U0000200c\U00000308\U0000200c", {8204}, {3}},
-     {L"\U0000200c\U0000200d", {8204}, {2}},
-     {L"\U0000200c\U00000308\U0000200d", {8204}, {3}},
-     {L"\U0000200c\U0001f1e6", {8204, 127462}, {1, 3}},
-     {L"\U0000200c\U00000308\U0001f1e6", {8204, 127462}, {2, 4}},
-     {L"\U0000200c\U000006dd", {8204, 1757}, {1, 2}},
-     {L"\U0000200c\U00000308\U000006dd", {8204, 1757}, {2, 3}},
-     {L"\U0000200c\U00000903", {8204}, {2}},
-     {L"\U0000200c\U00000308\U00000903", {8204}, {3}},
-     {L"\U0000200c\U00001100", {8204, 4352}, {1, 2}},
-     {L"\U0000200c\U00000308\U00001100", {8204, 4352}, {2, 3}},
-     {L"\U0000200c\U00001160", {8204, 4448}, {1, 2}},
-     {L"\U0000200c\U00000308\U00001160", {8204, 4448}, {2, 3}},
-     {L"\U0000200c\U000011a8", {8204, 4520}, {1, 2}},
-     {L"\U0000200c\U00000308\U000011a8", {8204, 4520}, {2, 3}},
-     {L"\U0000200c\U0000ac00", {8204, 44032}, {1, 2}},
-     {L"\U0000200c\U00000308\U0000ac00", {8204, 44032}, {2, 3}},
-     {L"\U0000200c\U0000ac01", {8204, 44033}, {1, 2}},
-     {L"\U0000200c\U00000308\U0000ac01", {8204, 44033}, {2, 3}},
-     {L"\U0000200c\U00001cf5", {8204, 7413}, {1, 2}},
-     {L"\U0000200c\U00000308\U00001cf5", {8204, 7413}, {2, 3}},
-     {L"\U0000200c\U00000915", {8204, 2325}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000915", {8204, 2325}, {2, 3}},
-     {L"\U0000200c\U000000a9", {8204, 169}, {1, 2}},
-     {L"\U0000200c\U00000308\U000000a9", {8204, 169}, {2, 3}},
-     {L"\U0000200c\U00000020", {8204, 32}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000020", {8204, 32}, {2, 3}},
-     {L"\U0000200c\U00000378", {8204, 888}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000378", {8204, 888}, {2, 3}},
-     {L"\U0000200d\U0000000d", {8205, 13}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000000d", {8205, 13}, {2, 3}},
-     {L"\U0000200d\U0000000a", {8205, 10}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000000a", {8205, 10}, {2, 3}},
-     {L"\U0000200d\U00000000", {8205, 0}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000000", {8205, 0}, {2, 3}},
-     {L"\U0000200d\U0000094d", {8205}, {2}},
-     {L"\U0000200d\U00000308\U0000094d", {8205}, {3}},
-     {L"\U0000200d\U00000300", {8205}, {2}},
-     {L"\U0000200d\U00000308\U00000300", {8205}, {3}},
-     {L"\U0000200d\U0000200c", {8205}, {2}},
-     {L"\U0000200d\U00000308\U0000200c", {8205}, {3}},
-     {L"\U0000200d\U0000200d", {8205}, {2}},
-     {L"\U0000200d\U00000308\U0000200d", {8205}, {3}},
-     {L"\U0000200d\U0001f1e6", {8205, 127462}, {1, 3}},
-     {L"\U0000200d\U00000308\U0001f1e6", {8205, 127462}, {2, 4}},
-     {L"\U0000200d\U000006dd", {8205, 1757}, {1, 2}},
-     {L"\U0000200d\U00000308\U000006dd", {8205, 1757}, {2, 3}},
-     {L"\U0000200d\U00000903", {8205}, {2}},
-     {L"\U0000200d\U00000308\U00000903", {8205}, {3}},
-     {L"\U0000200d\U00001100", {8205, 4352}, {1, 2}},
-     {L"\U0000200d\U00000308\U00001100", {8205, 4352}, {2, 3}},
-     {L"\U0000200d\U00001160", {8205, 4448}, {1, 2}},
-     {L"\U0000200d\U00000308\U00001160", {8205, 4448}, {2, 3}},
-     {L"\U0000200d\U000011a8", {8205, 4520}, {1, 2}},
-     {L"\U0000200d\U00000308\U000011a8", {8205, 4520}, {2, 3}},
-     {L"\U0000200d\U0000ac00", {8205, 44032}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000ac00", {8205, 44032}, {2, 3}},
-     {L"\U0000200d\U0000ac01", {8205, 44033}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000ac01", {8205, 44033}, {2, 3}},
-     {L"\U0000200d\U00001cf5", {8205, 7413}, {1, 2}},
-     {L"\U0000200d\U00000308\U00001cf5", {8205, 7413}, {2, 3}},
-     {L"\U0000200d\U00000915", {8205, 2325}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000915", {8205, 2325}, {2, 3}},
-     {L"\U0000200d\U000000a9", {8205, 169}, {1, 2}},
-     {L"\U0000200d\U00000308\U000000a9", {8205, 169}, {2, 3}},
-     {L"\U0000200d\U00000020", {8205, 32}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000020", {8205, 32}, {2, 3}},
-     {L"\U0000200d\U00000378", {8205, 888}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000378", {8205, 888}, {2, 3}},
-     {L"\U0001f1e6\U0000000d", {127462, 13}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U0000000d", {127462, 13}, {3, 4}},
-     {L"\U0001f1e6\U0000000a", {127462, 10}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U0000000a", {127462, 10}, {3, 4}},
-     {L"\U0001f1e6\U00000000", {127462, 0}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U00000000", {127462, 0}, {3, 4}},
-     {L"\U0001f1e6\U0000094d", {127462}, {3}},
-     {L"\U0001f1e6\U00000308\U0000094d", {127462}, {4}},
-     {L"\U0001f1e6\U00000300", {127462}, {3}},
-     {L"\U0001f1e6\U00000308\U00000300", {127462}, {4}},
-     {L"\U0001f1e6\U0000200c", {127462}, {3}},
-     {L"\U0001f1e6\U00000308\U0000200c", {127462}, {4}},
-     {L"\U0001f1e6\U0000200d", {127462}, {3}},
-     {L"\U0001f1e6\U00000308\U0000200d", {127462}, {4}},
-     {L"\U0001f1e6\U0001f1e6", {127462}, {4}},
-     {L"\U0001f1e6\U00000308\U0001f1e6", {127462, 127462}, {3, 5}},
-     {L"\U0001f1e6\U000006dd", {127462, 1757}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U000006dd", {127462, 1757}, {3, 4}},
-     {L"\U0001f1e6\U00000903", {127462}, {3}},
-     {L"\U0001f1e6\U00000308\U00000903", {127462}, {4}},
-     {L"\U0001f1e6\U00001100", {127462, 4352}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U00001100", {127462, 4352}, {3, 4}},
-     {L"\U0001f1e6\U00001160", {127462, 4448}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U00001160", {127462, 4448}, {3, 4}},
-     {L"\U0001f1e6\U000011a8", {127462, 4520}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U000011a8", {127462, 4520}, {3, 4}},
-     {L"\U0001f1e6\U0000ac00", {127462, 44032}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U0000ac00", {127462, 44032}, {3, 4}},
-     {L"\U0001f1e6\U0000ac01", {127462, 44033}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U0000ac01", {127462, 44033}, {3, 4}},
-     {L"\U0001f1e6\U00001cf5", {127462, 7413}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U00001cf5", {127462, 7413}, {3, 4}},
-     {L"\U0001f1e6\U00000915", {127462, 2325}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U00000915", {127462, 2325}, {3, 4}},
-     {L"\U0001f1e6\U000000a9", {127462, 169}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U000000a9", {127462, 169}, {3, 4}},
-     {L"\U0001f1e6\U00000020", {127462, 32}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U00000020", {127462, 32}, {3, 4}},
-     {L"\U0001f1e6\U00000378", {127462, 888}, {2, 3}},
-     {L"\U0001f1e6\U00000308\U00000378", {127462, 888}, {3, 4}},
-     {L"\U000006dd\U0000000d", {1757, 13}, {1, 2}},
-     {L"\U000006dd\U00000308\U0000000d", {1757, 13}, {2, 3}},
-     {L"\U000006dd\U0000000a", {1757, 10}, {1, 2}},
-     {L"\U000006dd\U00000308\U0000000a", {1757, 10}, {2, 3}},
-     {L"\U000006dd\U00000000", {1757, 0}, {1, 2}},
-     {L"\U000006dd\U00000308\U00000000", {1757, 0}, {2, 3}},
-     {L"\U000006dd\U0000094d", {1757}, {2}},
-     {L"\U000006dd\U00000308\U0000094d", {1757}, {3}},
-     {L"\U000006dd\U00000300", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00000300", {1757}, {3}},
-     {L"\U000006dd\U0000200c", {1757}, {2}},
-     {L"\U000006dd\U00000308\U0000200c", {1757}, {3}},
-     {L"\U000006dd\U0000200d", {1757}, {2}},
-     {L"\U000006dd\U00000308\U0000200d", {1757}, {3}},
-     {L"\U000006dd\U0001f1e6", {1757}, {3}},
-     {L"\U000006dd\U00000308\U0001f1e6", {1757, 127462}, {2, 4}},
-     {L"\U000006dd\U000006dd", {1757}, {2}},
-     {L"\U000006dd\U00000308\U000006dd", {1757, 1757}, {2, 3}},
-     {L"\U000006dd\U00000903", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00000903", {1757}, {3}},
-     {L"\U000006dd\U00001100", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00001100", {1757, 4352}, {2, 3}},
-     {L"\U000006dd\U00001160", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00001160", {1757, 4448}, {2, 3}},
-     {L"\U000006dd\U000011a8", {1757}, {2}},
-     {L"\U000006dd\U00000308\U000011a8", {1757, 4520}, {2, 3}},
-     {L"\U000006dd\U0000ac00", {1757}, {2}},
-     {L"\U000006dd\U00000308\U0000ac00", {1757, 44032}, {2, 3}},
-     {L"\U000006dd\U0000ac01", {1757}, {2}},
-     {L"\U000006dd\U00000308\U0000ac01", {1757, 44033}, {2, 3}},
-     {L"\U000006dd\U00001cf5", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00001cf5", {1757, 7413}, {2, 3}},
-     {L"\U000006dd\U00000915", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00000915", {1757, 2325}, {2, 3}},
-     {L"\U000006dd\U000000a9", {1757}, {2}},
-     {L"\U000006dd\U00000308\U000000a9", {1757, 169}, {2, 3}},
-     {L"\U000006dd\U00000020", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00000020", {1757, 32}, {2, 3}},
-     {L"\U000006dd\U00000378", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00000378", {1757, 888}, {2, 3}},
-     {L"\U00000903\U0000000d", {2307, 13}, {1, 2}},
-     {L"\U00000903\U00000308\U0000000d", {2307, 13}, {2, 3}},
-     {L"\U00000903\U0000000a", {2307, 10}, {1, 2}},
-     {L"\U00000903\U00000308\U0000000a", {2307, 10}, {2, 3}},
-     {L"\U00000903\U00000000", {2307, 0}, {1, 2}},
-     {L"\U00000903\U00000308\U00000000", {2307, 0}, {2, 3}},
-     {L"\U00000903\U0000094d", {2307}, {2}},
-     {L"\U00000903\U00000308\U0000094d", {2307}, {3}},
-     {L"\U00000903\U00000300", {2307}, {2}},
-     {L"\U00000903\U00000308\U00000300", {2307}, {3}},
-     {L"\U00000903\U0000200c", {2307}, {2}},
-     {L"\U00000903\U00000308\U0000200c", {2307}, {3}},
-     {L"\U00000903\U0000200d", {2307}, {2}},
-     {L"\U00000903\U00000308\U0000200d", {2307}, {3}},
-     {L"\U00000903\U0001f1e6", {2307, 127462}, {1, 3}},
-     {L"\U00000903\U00000308\U0001f1e6", {2307, 127462}, {2, 4}},
-     {L"\U00000903\U000006dd", {2307, 1757}, {1, 2}},
-     {L"\U00000903\U00000308\U000006dd", {2307, 1757}, {2, 3}},
-     {L"\U00000903\U00000903", {2307}, {2}},
-     {L"\U00000903\U00000308\U00000903", {2307}, {3}},
-     {L"\U00000903\U00001100", {2307, 4352}, {1, 2}},
-     {L"\U00000903\U00000308\U00001100", {2307, 4352}, {2, 3}},
-     {L"\U00000903\U00001160", {2307, 4448}, {1, 2}},
-     {L"\U00000903\U00000308\U00001160", {2307, 4448}, {2, 3}},
-     {L"\U00000903\U000011a8", {2307, 4520}, {1, 2}},
-     {L"\U00000903\U00000308\U000011a8", {2307, 4520}, {2, 3}},
-     {L"\U00000903\U0000ac00", {2307, 44032}, {1, 2}},
-     {L"\U00000903\U00000308\U0000ac00", {2307, 44032}, {2, 3}},
-     {L"\U00000903\U0000ac01", {2307, 44033}, {1, 2}},
-     {L"\U00000903\U00000308\U0000ac01", {2307, 44033}, {2, 3}},
-     {L"\U00000903\U00001cf5", {2307, 7413}, {1, 2}},
-     {L"\U00000903\U00000308\U00001cf5", {2307, 7413}, {2, 3}},
-     {L"\U00000903\U00000915", {2307, 2325}, {1, 2}},
-     {L"\U00000903\U00000308\U00000915", {2307, 2325}, {2, 3}},
-     {L"\U00000903\U000000a9", {2307, 169}, {1, 2}},
-     {L"\U00000903\U00000308\U000000a9", {2307, 169}, {2, 3}},
-     {L"\U00000903\U00000020", {2307, 32}, {1, 2}},
-     {L"\U00000903\U00000308\U00000020", {2307, 32}, {2, 3}},
-     {L"\U00000903\U00000378", {2307, 888}, {1, 2}},
-     {L"\U00000903\U00000308\U00000378", {2307, 888}, {2, 3}},
-     {L"\U00001100\U0000000d", {4352, 13}, {1, 2}},
-     {L"\U00001100\U00000308\U0000000d", {4352, 13}, {2, 3}},
-     {L"\U00001100\U0000000a", {4352, 10}, {1, 2}},
-     {L"\U00001100\U00000308\U0000000a", {4352, 10}, {2, 3}},
-     {L"\U00001100\U00000000", {4352, 0}, {1, 2}},
-     {L"\U00001100\U00000308\U00000000", {4352, 0}, {2, 3}},
-     {L"\U00001100\U0000094d", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000094d", {4352}, {3}},
-     {L"\U00001100\U00000300", {4352}, {2}},
-     {L"\U00001100\U00000308\U00000300", {4352}, {3}},
-     {L"\U00001100\U0000200c", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000200c", {4352}, {3}},
-     {L"\U00001100\U0000200d", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000200d", {4352}, {3}},
-     {L"\U00001100\U0001f1e6", {4352, 127462}, {1, 3}},
-     {L"\U00001100\U00000308\U0001f1e6", {4352, 127462}, {2, 4}},
-     {L"\U00001100\U000006dd", {4352, 1757}, {1, 2}},
-     {L"\U00001100\U00000308\U000006dd", {4352, 1757}, {2, 3}},
-     {L"\U00001100\U00000903", {4352}, {2}},
-     {L"\U00001100\U00000308\U00000903", {4352}, {3}},
-     {L"\U00001100\U00001100", {4352}, {2}},
-     {L"\U00001100\U00000308\U00001100", {4352, 4352}, {2, 3}},
-     {L"\U00001100\U00001160", {4352}, {2}},
-     {L"\U00001100\U00000308\U00001160", {4352, 4448}, {2, 3}},
-     {L"\U00001100\U000011a8", {4352, 4520}, {1, 2}},
-     {L"\U00001100\U00000308\U000011a8", {4352, 4520}, {2, 3}},
-     {L"\U00001100\U0000ac00", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000ac00", {4352, 44032}, {2, 3}},
-     {L"\U00001100\U0000ac01", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000ac01", {4352, 44033}, {2, 3}},
-     {L"\U00001100\U00001cf5", {4352, 7413}, {1, 2}},
-     {L"\U00001100\U00000308\U00001cf5", {4352, 7413}, {2, 3}},
-     {L"\U00001100\U00000915", {4352, 2325}, {1, 2}},
-     {L"\U00001100\U00000308\U00000915", {4352, 2325}, {2, 3}},
-     {L"\U00001100\U000000a9", {4352, 169}, {1, 2}},
-     {L"\U00001100\U00000308\U000000a9", {4352, 169}, {2, 3}},
-     {L"\U00001100\U00000020", {4352, 32}, {1, 2}},
-     {L"\U00001100\U00000308\U00000020", {4352, 32}, {2, 3}},
-     {L"\U00001100\U00000378", {4352, 888}, {1, 2}},
-     {L"\U00001100\U00000308\U00000378", {4352, 888}, {2, 3}},
-     {L"\U00001160\U0000000d", {4448, 13}, {1, 2}},
-     {L"\U00001160\U00000308\U0000000d", {4448, 13}, {2, 3}},
-     {L"\U00001160\U0000000a", {4448, 10}, {1, 2}},
-     {L"\U00001160\U00000308\U0000000a", {4448, 10}, {2, 3}},
-     {L"\U00001160\U00000000", {4448, 0}, {1, 2}},
-     {L"\U00001160\U00000308\U00000000", {4448, 0}, {2, 3}},
-     {L"\U00001160\U0000094d", {4448}, {2}},
-     {L"\U00001160\U00000308\U0000094d", {4448}, {3}},
-     {L"\U00001160\U00000300", {4448}, {2}},
-     {L"\U00001160\U00000308\U00000300", {4448}, {3}},
-     {L"\U00001160\U0000200c", {4448}, {2}},
-     {L"\U00001160\U00000308\U0000200c", {4448}, {3}},
-     {L"\U00001160\U0000200d", {4448}, {2}},
-     {L"\U00001160\U00000308\U0000200d", {4448}, {3}},
-     {L"\U00001160\U0001f1e6", {4448, 127462}, {1, 3}},
-     {L"\U00001160\U00000308\U0001f1e6", {4448, 127462}, {2, 4}},
-     {L"\U00001160\U000006dd", {4448, 1757}, {1, 2}},
-     {L"\U00001160\U00000308\U000006dd", {4448, 1757}, {2, 3}},
-     {L"\U00001160\U00000903", {4448}, {2}},
-     {L"\U00001160\U00000308\U00000903", {4448}, {3}},
-     {L"\U00001160\U00001100", {4448, 4352}, {1, 2}},
-     {L"\U00001160\U00000308\U00001100", {4448, 4352}, {2, 3}},
-     {L"\U00001160\U00001160", {4448}, {2}},
-     {L"\U00001160\U00000308\U00001160", {4448, 4448}, {2, 3}},
-     {L"\U00001160\U000011a8", {4448}, {2}},
-     {L"\U00001160\U00000308\U000011a8", {4448, 4520}, {2, 3}},
-     {L"\U00001160\U0000ac00", {4448, 44032}, {1, 2}},
-     {L"\U00001160\U00000308\U0000ac00", {4448, 44032}, {2, 3}},
-     {L"\U00001160\U0000ac01", {4448, 44033}, {1, 2}},
-     {L"\U00001160\U00000308\U0000ac01", {4448, 44033}, {2, 3}},
-     {L"\U00001160\U00001cf5", {4448, 7413}, {1, 2}},
-     {L"\U00001160\U00000308\U00001cf5", {4448, 7413}, {2, 3}},
-     {L"\U00001160\U00000915", {4448, 2325}, {1, 2}},
-     {L"\U00001160\U00000308\U00000915", {4448, 2325}, {2, 3}},
-     {L"\U00001160\U000000a9", {4448, 169}, {1, 2}},
-     {L"\U00001160\U00000308\U000000a9", {4448, 169}, {2, 3}},
-     {L"\U00001160\U00000020", {4448, 32}, {1, 2}},
-     {L"\U00001160\U00000308\U00000020", {4448, 32}, {2, 3}},
-     {L"\U00001160\U00000378", {4448, 888}, {1, 2}},
-     {L"\U00001160\U00000308\U00000378", {4448, 888}, {2, 3}},
-     {L"\U000011a8\U0000000d", {4520, 13}, {1, 2}},
-     {L"\U000011a8\U00000308\U0000000d", {4520, 13}, {2, 3}},
-     {L"\U000011a8\U0000000a", {4520, 10}, {1, 2}},
-     {L"\U000011a8\U00000308\U0000000a", {4520, 10}, {2, 3}},
-     {L"\U000011a8\U00000000", {4520, 0}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000000", {4520, 0}, {2, 3}},
-     {L"\U000011a8\U0000094d", {4520}, {2}},
-     {L"\U000011a8\U00000308\U0000094d", {4520}, {3}},
-     {L"\U000011a8\U00000300", {4520}, {2}},
-     {L"\U000011a8\U00000308\U00000300", {4520}, {3}},
-     {L"\U000011a8\U0000200c", {4520}, {2}},
-     {L"\U000011a8\U00000308\U0000200c", {4520}, {3}},
-     {L"\U000011a8\U0000200d", {4520}, {2}},
-     {L"\U000011a8\U00000308\U0000200d", {4520}, {3}},
-     {L"\U000011a8\U0001f1e6", {4520, 127462}, {1, 3}},
-     {L"\U000011a8\U00000308\U0001f1e6", {4520, 127462}, {2, 4}},
-     {L"\U000011a8\U000006dd", {4520, 1757}, {1, 2}},
-     {L"\U000011a8\U00000308\U000006dd", {4520, 1757}, {2, 3}},
-     {L"\U000011a8\U00000903", {4520}, {2}},
-     {L"\U000011a8\U00000308\U00000903", {4520}, {3}},
-     {L"\U000011a8\U00001100", {4520, 4352}, {1, 2}},
-     {L"\U000011a8\U00000308\U00001100", {4520, 4352}, {2, 3}},
-     {L"\U000011a8\U00001160", {4520, 4448}, {1, 2}},
-     {L"\U000011a8\U00000308\U00001160", {4520, 4448}, {2, 3}},
-     {L"\U000011a8\U000011a8", {4520}, {2}},
-     {L"\U000011a8\U00000308\U000011a8", {4520, 4520}, {2, 3}},
-     {L"\U000011a8\U0000ac00", {4520, 44032}, {1, 2}},
-     {L"\U000011a8\U00000308\U0000ac00", {4520, 44032}, {2, 3}},
-     {L"\U000011a8\U0000ac01", {4520, 44033}, {1, 2}},
-     {L"\U000011a8\U00000308\U0000ac01", {4520, 44033}, {2, 3}},
-     {L"\U000011a8\U00001cf5", {4520, 7413}, {1, 2}},
-     {L"\U000011a8\U00000308\U00001cf5", {4520, 7413}, {2, 3}},
-     {L"\U000011a8\U00000915", {4520, 2325}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000915", {4520, 2325}, {2, 3}},
-     {L"\U000011a8\U000000a9", {4520, 169}, {1, 2}},
-     {L"\U000011a8\U00000308\U000000a9", {4520, 169}, {2, 3}},
-     {L"\U000011a8\U00000020", {4520, 32}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000020", {4520, 32}, {2, 3}},
-     {L"\U000011a8\U00000378", {4520, 888}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000378", {4520, 888}, {2, 3}},
-     {L"\U0000ac00\U0000000d", {44032, 13}, {1, 2}},
-     {L"\U0000ac00\U00000308\U0000000d", {44032, 13}, {2, 3}},
-     {L"\U0000ac00\U0000000a", {44032, 10}, {1, 2}},
-     {L"\U0000ac00\U00000308\U0000000a", {44032, 10}, {2, 3}},
-     {L"\U0000ac00\U00000000", {44032, 0}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000000", {44032, 0}, {2, 3}},
-     {L"\U0000ac00\U0000094d", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U0000094d", {44032}, {3}},
-     {L"\U0000ac00\U00000300", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00000300", {44032}, {3}},
-     {L"\U0000ac00\U0000200c", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U0000200c", {44032}, {3}},
-     {L"\U0000ac00\U0000200d", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U0000200d", {44032}, {3}},
-     {L"\U0000ac00\U0001f1e6", {44032, 127462}, {1, 3}},
-     {L"\U0000ac00\U00000308\U0001f1e6", {44032, 127462}, {2, 4}},
-     {L"\U0000ac00\U000006dd", {44032, 1757}, {1, 2}},
-     {L"\U0000ac00\U00000308\U000006dd", {44032, 1757}, {2, 3}},
-     {L"\U0000ac00\U00000903", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00000903", {44032}, {3}},
-     {L"\U0000ac00\U00001100", {44032, 4352}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00001100", {44032, 4352}, {2, 3}},
-     {L"\U0000ac00\U00001160", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00001160", {44032, 4448}, {2, 3}},
-     {L"\U0000ac00\U000011a8", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U000011a8", {44032, 4520}, {2, 3}},
-     {L"\U0000ac00\U0000ac00", {44032, 44032}, {1, 2}},
-     {L"\U0000ac00\U00000308\U0000ac00", {44032, 44032}, {2, 3}},
-     {L"\U0000ac00\U0000ac01", {44032, 44033}, {1, 2}},
-     {L"\U0000ac00\U00000308\U0000ac01", {44032, 44033}, {2, 3}},
-     {L"\U0000ac00\U00001cf5", {44032, 7413}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00001cf5", {44032, 7413}, {2, 3}},
-     {L"\U0000ac00\U00000915", {44032, 2325}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000915", {44032, 2325}, {2, 3}},
-     {L"\U0000ac00\U000000a9", {44032, 169}, {1, 2}},
-     {L"\U0000ac00\U00000308\U000000a9", {44032, 169}, {2, 3}},
-     {L"\U0000ac00\U00000020", {44032, 32}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000020", {44032, 32}, {2, 3}},
-     {L"\U0000ac00\U00000378", {44032, 888}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000378", {44032, 888}, {2, 3}},
-     {L"\U0000ac01\U0000000d", {44033, 13}, {1, 2}},
-     {L"\U0000ac01\U00000308\U0000000d", {44033, 13}, {2, 3}},
-     {L"\U0000ac01\U0000000a", {44033, 10}, {1, 2}},
-     {L"\U0000ac01\U00000308\U0000000a", {44033, 10}, {2, 3}},
-     {L"\U0000ac01\U00000000", {44033, 0}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000000", {44033, 0}, {2, 3}},
-     {L"\U0000ac01\U0000094d", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U0000094d", {44033}, {3}},
-     {L"\U0000ac01\U00000300", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U00000300", {44033}, {3}},
-     {L"\U0000ac01\U0000200c", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U0000200c", {44033}, {3}},
-     {L"\U0000ac01\U0000200d", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U0000200d", {44033}, {3}},
-     {L"\U0000ac01\U0001f1e6", {44033, 127462}, {1, 3}},
-     {L"\U0000ac01\U00000308\U0001f1e6", {44033, 127462}, {2, 4}},
-     {L"\U0000ac01\U000006dd", {44033, 1757}, {1, 2}},
-     {L"\U0000ac01\U00000308\U000006dd", {44033, 1757}, {2, 3}},
-     {L"\U0000ac01\U00000903", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U00000903", {44033}, {3}},
-     {L"\U0000ac01\U00001100", {44033, 4352}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00001100", {44033, 4352}, {2, 3}},
-     {L"\U0000ac01\U00001160", {44033, 4448}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00001160", {44033, 4448}, {2, 3}},
-     {L"\U0000ac01\U000011a8", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U000011a8", {44033, 4520}, {2, 3}},
-     {L"\U0000ac01\U0000ac00", {44033, 44032}, {1, 2}},
-     {L"\U0000ac01\U00000308\U0000ac00", {44033, 44032}, {2, 3}},
-     {L"\U0000ac01\U0000ac01", {44033, 44033}, {1, 2}},
-     {L"\U0000ac01\U00000308\U0000ac01", {44033, 44033}, {2, 3}},
-     {L"\U0000ac01\U00001cf5", {44033, 7413}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00001cf5", {44033, 7413}, {2, 3}},
-     {L"\U0000ac01\U00000915", {44033, 2325}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000915", {44033, 2325}, {2, 3}},
-     {L"\U0000ac01\U000000a9", {44033, 169}, {1, 2}},
-     {L"\U0000ac01\U00000308\U000000a9", {44033, 169}, {2, 3}},
-     {L"\U0000ac01\U00000020", {44033, 32}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000020", {44033, 32}, {2, 3}},
-     {L"\U0000ac01\U00000378", {44033, 888}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000378", {44033, 888}, {2, 3}},
-     {L"\U00001cf5\U0000000d", {7413, 13}, {1, 2}},
-     {L"\U00001cf5\U00000308\U0000000d", {7413, 13}, {2, 3}},
-     {L"\U00001cf5\U0000000a", {7413, 10}, {1, 2}},
-     {L"\U00001cf5\U00000308\U0000000a", {7413, 10}, {2, 3}},
-     {L"\U00001cf5\U00000000", {7413, 0}, {1, 2}},
-     {L"\U00001cf5\U00000308\U00000000", {7413, 0}, {2, 3}},
-     {L"\U00001cf5\U0000094d", {7413}, {2}},
-     {L"\U00001cf5\U00000308\U0000094d", {7413}, {3}},
-     {L"\U00001cf5\U00000300", {7413}, {2}},
-     {L"\U00001cf5\U00000308\U00000300", {7413}, {3}},
-     {L"\U00001cf5\U0000200c", {7413}, {2}},
-     {L"\U00001cf5\U00000308\U0000200c", {7413}, {3}},
-     {L"\U00001cf5\U0000200d", {7413}, {2}},
-     {L"\U00001cf5\U00000308\U0000200d", {7413}, {3}},
-     {L"\U00001cf5\U0001f1e6", {7413, 127462}, {1, 3}},
-     {L"\U00001cf5\U00000308\U0001f1e6", {7413, 127462}, {2, 4}},
-     {L"\U00001cf5\U000006dd", {7413, 1757}, {1, 2}},
-     {L"\U00001cf5\U00000308\U000006dd", {7413, 1757}, {2, 3}},
-     {L"\U00001cf5\U00000903", {7413}, {2}},
-     {L"\U00001cf5\U00000308\U00000903", {7413}, {3}},
-     {L"\U00001cf5\U00001100", {7413, 4352}, {1, 2}},
-     {L"\U00001cf5\U00000308\U00001100", {7413, 4352}, {2, 3}},
-     {L"\U00001cf5\U00001160", {7413, 4448}, {1, 2}},
-     {L"\U00001cf5\U00000308\U00001160", {7413, 4448}, {2, 3}},
-     {L"\U00001cf5\U000011a8", {7413, 4520}, {1, 2}},
-     {L"\U00001cf5\U00000308\U000011a8", {7413, 4520}, {2, 3}},
-     {L"\U00001cf5\U0000ac00", {7413, 44032}, {1, 2}},
-     {L"\U00001cf5\U00000308\U0000ac00", {7413, 44032}, {2, 3}},
-     {L"\U00001cf5\U0000ac01", {7413, 44033}, {1, 2}},
-     {L"\U00001cf5\U00000308\U0000ac01", {7413, 44033}, {2, 3}},
-     {L"\U00001cf5\U00001cf5", {7413, 7413}, {1, 2}},
-     {L"\U00001cf5\U00000308\U00001cf5", {7413, 7413}, {2, 3}},
-     {L"\U00001cf5\U00000915", {7413}, {2}},
-     {L"\U00001cf5\U00000308\U00000915", {7413}, {3}},
-     {L"\U00001cf5\U000000a9", {7413, 169}, {1, 2}},
-     {L"\U00001cf5\U00000308\U000000a9", {7413, 169}, {2, 3}},
-     {L"\U00001cf5\U00000020", {7413, 32}, {1, 2}},
-     {L"\U00001cf5\U00000308\U00000020", {7413, 32}, {2, 3}},
-     {L"\U00001cf5\U00000378", {7413, 888}, {1, 2}},
-     {L"\U00001cf5\U00000308\U00000378", {7413, 888}, {2, 3}},
-     {L"\U00000915\U0000000d", {2325, 13}, {1, 2}},
-     {L"\U00000915\U00000308\U0000000d", {2325, 13}, {2, 3}},
-     {L"\U00000915\U0000000a", {2325, 10}, {1, 2}},
-     {L"\U00000915\U00000308\U0000000a", {2325, 10}, {2, 3}},
-     {L"\U00000915\U00000000", {2325, 0}, {1, 2}},
-     {L"\U00000915\U00000308\U00000000", {2325, 0}, {2, 3}},
-     {L"\U00000915\U0000094d", {2325}, {2}},
-     {L"\U00000915\U00000308\U0000094d", {2325}, {3}},
-     {L"\U00000915\U00000300", {2325}, {2}},
-     {L"\U00000915\U00000308\U00000300", {2325}, {3}},
-     {L"\U00000915\U0000200c", {2325}, {2}},
-     {L"\U00000915\U00000308\U0000200c", {2325}, {3}},
-     {L"\U00000915\U0000200d", {2325}, {2}},
-     {L"\U00000915\U00000308\U0000200d", {2325}, {3}},
-     {L"\U00000915\U0001f1e6", {2325, 127462}, {1, 3}},
-     {L"\U00000915\U00000308\U0001f1e6", {2325, 127462}, {2, 4}},
-     {L"\U00000915\U000006dd", {2325, 1757}, {1, 2}},
-     {L"\U00000915\U00000308\U000006dd", {2325, 1757}, {2, 3}},
-     {L"\U00000915\U00000903", {2325}, {2}},
-     {L"\U00000915\U00000308\U00000903", {2325}, {3}},
-     {L"\U00000915\U00001100", {2325, 4352}, {1, 2}},
-     {L"\U00000915\U00000308\U00001100", {2325, 4352}, {2, 3}},
-     {L"\U00000915\U00001160", {2325, 4448}, {1, 2}},
-     {L"\U00000915\U00000308\U00001160", {2325, 4448}, {2, 3}},
-     {L"\U00000915\U000011a8", {2325, 4520}, {1, 2}},
-     {L"\U00000915\U00000308\U000011a8", {2325, 4520}, {2, 3}},
-     {L"\U00000915\U0000ac00", {2325, 44032}, {1, 2}},
-     {L"\U00000915\U00000308\U0000ac00", {2325, 44032}, {2, 3}},
-     {L"\U00000915\U0000ac01", {2325, 44033}, {1, 2}},
-     {L"\U00000915\U00000308\U0000ac01", {2325, 44033}, {2, 3}},
-     {L"\U00000915\U00001cf5", {2325, 7413}, {1, 2}},
-     {L"\U00000915\U00000308\U00001cf5", {2325, 7413}, {2, 3}},
-     {L"\U00000915\U00000915", {2325, 2325}, {1, 2}},
-     {L"\U00000915\U00000308\U00000915", {2325, 2325}, {2, 3}},
-     {L"\U00000915\U000000a9", {2325, 169}, {1, 2}},
-     {L"\U00000915\U00000308\U000000a9", {2325, 169}, {2, 3}},
-     {L"\U00000915\U00000020", {2325, 32}, {1, 2}},
-     {L"\U00000915\U00000308\U00000020", {2325, 32}, {2, 3}},
-     {L"\U00000915\U00000378", {2325, 888}, {1, 2}},
-     {L"\U00000915\U00000308\U00000378", {2325, 888}, {2, 3}},
-     {L"\U000000a9\U0000000d", {169, 13}, {1, 2}},
-     {L"\U000000a9\U00000308\U0000000d", {169, 13}, {2, 3}},
-     {L"\U000000a9\U0000000a", {169, 10}, {1, 2}},
-     {L"\U000000a9\U00000308\U0000000a", {169, 10}, {2, 3}},
-     {L"\U000000a9\U00000000", {169, 0}, {1, 2}},
-     {L"\U000000a9\U00000308\U00000000", {169, 0}, {2, 3}},
-     {L"\U000000a9\U0000094d", {169}, {2}},
-     {L"\U000000a9\U00000308\U0000094d", {169}, {3}},
-     {L"\U000000a9\U00000300", {169}, {2}},
-     {L"\U000000a9\U00000308\U00000300", {169}, {3}},
-     {L"\U000000a9\U0000200c", {169}, {2}},
-     {L"\U000000a9\U00000308\U0000200c", {169}, {3}},
-     {L"\U000000a9\U0000200d", {169}, {2}},
-     {L"\U000000a9\U00000308\U0000200d", {169}, {3}},
-     {L"\U000000a9\U0001f1e6", {169, 127462}, {1, 3}},
-     {L"\U000000a9\U00000308\U0001f1e6", {169, 127462}, {2, 4}},
-     {L"\U000000a9\U000006dd", {169, 1757}, {1, 2}},
-     {L"\U000000a9\U00000308\U000006dd", {169, 1757}, {2, 3}},
-     {L"\U000000a9\U00000903", {169}, {2}},
-     {L"\U000000a9\U00000308\U00000903", {169}, {3}},
-     {L"\U000000a9\U00001100", {169, 4352}, {1, 2}},
-     {L"\U000000a9\U00000308\U00001100", {169, 4352}, {2, 3}},
-     {L"\U000000a9\U00001160", {169, 4448}, {1, 2}},
-     {L"\U000000a9\U00000308\U00001160", {169, 4448}, {2, 3}},
-     {L"\U000000a9\U000011a8", {169, 4520}, {1, 2}},
-     {L"\U000000a9\U00000308\U000011a8", {169, 4520}, {2, 3}},
-     {L"\U000000a9\U0000ac00", {169, 44032}, {1, 2}},
-     {L"\U000000a9\U00000308\U0000ac00", {169, 44032}, {2, 3}},
-     {L"\U000000a9\U0000ac01", {169, 44033}, {1, 2}},
-     {L"\U000000a9\U00000308\U0000ac01", {169, 44033}, {2, 3}},
-     {L"\U000000a9\U00001cf5", {169, 7413}, {1, 2}},
-     {L"\U000000a9\U00000308\U00001cf5", {169, 7413}, {2, 3}},
-     {L"\U000000a9\U00000915", {169, 2325}, {1, 2}},
-     {L"\U000000a9\U00000308\U00000915", {169, 2325}, {2, 3}},
-     {L"\U000000a9\U000000a9", {169, 169}, {1, 2}},
-     {L"\U000000a9\U00000308\U000000a9", {169, 169}, {2, 3}},
-     {L"\U000000a9\U00000020", {169, 32}, {1, 2}},
-     {L"\U000000a9\U00000308\U00000020", {169, 32}, {2, 3}},
-     {L"\U000000a9\U00000378", {169, 888}, {1, 2}},
-     {L"\U000000a9\U00000308\U00000378", {169, 888}, {2, 3}},
-     {L"\U00000020\U0000000d", {32, 13}, {1, 2}},
-     {L"\U00000020\U00000308\U0000000d", {32, 13}, {2, 3}},
-     {L"\U00000020\U0000000a", {32, 10}, {1, 2}},
-     {L"\U00000020\U00000308\U0000000a", {32, 10}, {2, 3}},
-     {L"\U00000020\U00000000", {32, 0}, {1, 2}},
-     {L"\U00000020\U00000308\U00000000", {32, 0}, {2, 3}},
-     {L"\U00000020\U0000094d", {32}, {2}},
-     {L"\U00000020\U00000308\U0000094d", {32}, {3}},
-     {L"\U00000020\U00000300", {32}, {2}},
-     {L"\U00000020\U00000308\U00000300", {32}, {3}},
-     {L"\U00000020\U0000200c", {32}, {2}},
-     {L"\U00000020\U00000308\U0000200c", {32}, {3}},
-     {L"\U00000020\U0000200d", {32}, {2}},
-     {L"\U00000020\U00000308\U0000200d", {32}, {3}},
-     {L"\U00000020\U0001f1e6", {32, 127462}, {1, 3}},
-     {L"\U00000020\U00000308\U0001f1e6", {32, 127462}, {2, 4}},
-     {L"\U00000020\U000006dd", {32, 1757}, {1, 2}},
-     {L"\U00000020\U00000308\U000006dd", {32, 1757}, {2, 3}},
-     {L"\U00000020\U00000903", {32}, {2}},
-     {L"\U00000020\U00000308\U00000903", {32}, {3}},
-     {L"\U00000020\U00001100", {32, 4352}, {1, 2}},
-     {L"\U00000020\U00000308\U00001100", {32, 4352}, {2, 3}},
-     {L"\U00000020\U00001160", {32, 4448}, {1, 2}},
-     {L"\U00000020\U00000308\U00001160", {32, 4448}, {2, 3}},
-     {L"\U00000020\U000011a8", {32, 4520}, {1, 2}},
-     {L"\U00000020\U00000308\U000011a8", {32, 4520}, {2, 3}},
-     {L"\U00000020\U0000ac00", {32, 44032}, {1, 2}},
-     {L"\U00000020\U00000308\U0000ac00", {32, 44032}, {2, 3}},
-     {L"\U00000020\U0000ac01", {32, 44033}, {1, 2}},
-     {L"\U00000020\U00000308\U0000ac01", {32, 44033}, {2, 3}},
-     {L"\U00000020\U00001cf5", {32, 7413}, {1, 2}},
-     {L"\U00000020\U00000308\U00001cf5", {32, 7413}, {2, 3}},
-     {L"\U00000020\U00000915", {32, 2325}, {1, 2}},
-     {L"\U00000020\U00000308\U00000915", {32, 2325}, {2, 3}},
-     {L"\U00000020\U000000a9", {32, 169}, {1, 2}},
-     {L"\U00000020\U00000308\U000000a9", {32, 169}, {2, 3}},
-     {L"\U00000020\U00000020", {32, 32}, {1, 2}},
-     {L"\U00000020\U00000308\U00000020", {32, 32}, {2, 3}},
-     {L"\U00000020\U00000378", {32, 888}, {1, 2}},
-     {L"\U00000020\U00000308\U00000378", {32, 888}, {2, 3}},
-     {L"\U00000378\U0000000d", {888, 13}, {1, 2}},
-     {L"\U00000378\U00000308\U0000000d", {888, 13}, {2, 3}},
-     {L"\U00000378\U0000000a", {888, 10}, {1, 2}},
-     {L"\U00000378\U00000308\U0000000a", {888, 10}, {2, 3}},
-     {L"\U00000378\U00000000", {888, 0}, {1, 2}},
-     {L"\U00000378\U00000308\U00000000", {888, 0}, {2, 3}},
-     {L"\U00000378\U0000094d", {888}, {2}},
-     {L"\U00000378\U00000308\U0000094d", {888}, {3}},
-     {L"\U00000378\U00000300", {888}, {2}},
-     {L"\U00000378\U00000308\U00000300", {888}, {3}},
-     {L"\U00000378\U0000200c", {888}, {2}},
-     {L"\U00000378\U00000308\U0000200c", {888}, {3}},
-     {L"\U00000378\U0000200d", {888}, {2}},
-     {L"\U00000378\U00000308\U0000200d", {888}, {3}},
-     {L"\U00000378\U0001f1e6", {888, 127462}, {1, 3}},
-     {L"\U00000378\U00000308\U0001f1e6", {888, 127462}, {2, 4}},
-     {L"\U00000378\U000006dd", {888, 1757}, {1, 2}},
-     {L"\U00000378\U00000308\U000006dd", {888, 1757}, {2, 3}},
-     {L"\U00000378\U00000903", {888}, {2}},
-     {L"\U00000378\U00000308\U00000903", {888}, {3}},
-     {L"\U00000378\U00001100", {888, 4352}, {1, 2}},
-     {L"\U00000378\U00000308\U00001100", {888, 4352}, {2, 3}},
-     {L"\U00000378\U00001160", {888, 4448}, {1, 2}},
-     {L"\U00000378\U00000308\U00001160", {888, 4448}, {2, 3}},
-     {L"\U00000378\U000011a8", {888, 4520}, {1, 2}},
-     {L"\U00000378\U00000308\U000011a8", {888, 4520}, {2, 3}},
-     {L"\U00000378\U0000ac00", {888, 44032}, {1, 2}},
-     {L"\U00000378\U00000308\U0000ac00", {888, 44032}, {2, 3}},
-     {L"\U00000378\U0000ac01", {888, 44033}, {1, 2}},
-     {L"\U00000378\U00000308\U0000ac01", {888, 44033}, {2, 3}},
-     {L"\U00000378\U00001cf5", {888, 7413}, {1, 2}},
-     {L"\U00000378\U00000308\U00001cf5", {888, 7413}, {2, 3}},
-     {L"\U00000378\U00000915", {888, 2325}, {1, 2}},
-     {L"\U00000378\U00000308\U00000915", {888, 2325}, {2, 3}},
-     {L"\U00000378\U000000a9", {888, 169}, {1, 2}},
-     {L"\U00000378\U00000308\U000000a9", {888, 169}, {2, 3}},
-     {L"\U00000378\U00000020", {888, 32}, {1, 2}},
-     {L"\U00000378\U00000308\U00000020", {888, 32}, {2, 3}},
-     {L"\U00000378\U00000378", {888, 888}, {1, 2}},
-     {L"\U00000378\U00000308\U00000378", {888, 888}, {2, 3}},
-     {L"\U0000000d\U0000000a\U00000061\U0000000a\U00000308", {13, 97, 10, 776}, {2, 3, 4, 5}},
-     {L"\U00000061\U00000308", {97}, {2}},
-     {L"\U00000020\U0000200d\U00000646", {32, 1606}, {2, 3}},
-     {L"\U00000646\U0000200d\U00000020", {1606, 32}, {2, 3}},
-     {L"\U00001100\U00001100", {4352}, {2}},
-     {L"\U0000ac00\U000011a8\U00001100", {44032, 4352}, {2, 3}},
-     {L"\U0000ac01\U000011a8\U00001100", {44033, 4352}, {2, 3}},
-     {L"\U0001f1e6\U0001f1e7\U0001f1e8\U00000062", {127462, 127464, 98}, {4, 6, 7}},
-     {L"\U00000061\U0001f1e6\U0001f1e7\U0001f1e8\U00000062", {97, 127462, 127464, 98}, {1, 5, 7, 8}},
-     {L"\U00000061\U0001f1e6\U0001f1e7\U0000200d\U0001f1e8\U00000062", {97, 127462, 127464, 98}, {1, 6, 8, 9}},
-     {L"\U00000061\U0001f1e6\U0000200d\U0001f1e7\U0001f1e8\U00000062", {97, 127462, 127463, 98}, {1, 4, 8, 9}},
-     {L"\U00000061\U0001f1e6\U0001f1e7\U0001f1e8\U0001f1e9\U00000062", {97, 127462, 127464, 98}, {1, 5, 9, 10}},
-     {L"\U00000061\U0000200d", {97}, {2}},
-     {L"\U00000061\U00000308\U00000062", {97, 98}, {2, 3}},
-     {L"\U00000061\U00000903\U00000062", {97, 98}, {2, 3}},
-     {L"\U00000061\U00000600\U00000062", {97, 1536}, {1, 3}},
-     {L"\U0001f476\U0001f3ff\U0001f476", {128118, 128118}, {4, 6}},
-     {L"\U00000061\U0001f3ff\U0001f476", {97, 128118}, {3, 5}},
-     {L"\U00000061\U0001f3ff\U0001f476\U0000200d\U0001f6d1", {97, 128118}, {3, 8}},
-     {L"\U0001f476\U0001f3ff\U00000308\U0000200d\U0001f476\U0001f3ff", {128118}, {10}},
-     {L"\U0001f6d1\U0000200d\U0001f6d1", {128721}, {5}},
-     {L"\U00000061\U0000200d\U0001f6d1", {97, 128721}, {2, 4}},
-     {L"\U00002701\U0000200d\U00002701", {9985, 9985}, {2, 3}},
-     {L"\U00000061\U0000200d\U00002701", {97, 9985}, {2, 3}},
-     {L"\U00000915\U00000924", {2325, 2340}, {1, 2}},
-     {L"\U00000915\U0000094d\U00000924", {2325}, {3}},
-     {L"\U00000915\U0000094d\U0000094d\U00000924", {2325}, {4}},
-     {L"\U00000915\U0000094d\U0000200d\U00000924", {2325}, {4}},
-     {L"\U00000915\U0000093c\U0000200d\U0000094d\U00000924", {2325}, {5}},
-     {L"\U00000915\U0000093c\U0000094d\U0000200d\U00000924", {2325}, {5}},
-     {L"\U00000915\U0000094d\U00000924\U0000094d\U0000092f", {2325}, {5}},
-     {L"\U00000915\U0000094d\U00000061", {2325, 97}, {2, 3}},
-     {L"\U00000061\U0000094d\U00000924", {97}, {3}},
-     {L"\U0000003f\U0000094d\U00000924", {63}, {3}},
-     {L"\U00000915\U0000094d\U0000094d\U00000924", {2325}, {4}},
-     {L"\U00000ab8\U00000afb\U00000acd\U00000ab8\U00000afb", {2744}, {5}},
-     {L"\U00001019\U00001039\U00001018\U0000102c\U00001037", {4121, 4140}, {3, 5}},
-     {L"\U00001004\U0000103a\U00001039\U00001011\U00001039\U00001011", {4100}, {6}},
-     {L"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38", {6930, 6962, 6962, 6962}, {2, 5, 10, 14}},
-     {L"\U0000179f\U000017d2\U0000178f\U000017d2\U0000179a\U000017b8", {6047}, {6}},
-     {L"\U00001b26\U00001b17\U00001b44\U00001b13", {6950, 6935}, {1, 4}},
-     {L"\U00001b27\U00001b13\U00001b44\U00001b0b\U00001b0b\U00001b04", {6951, 6931, 6923}, {1, 4, 6}},
-     {L"\U00001795\U000017d2\U000017af\U00001798", {6037, 6040}, {3, 4}},
-     {L"\U000017a0\U000017d2\U000017ab\U00001791\U000017d0\U00001799", {6048, 6033, 6041}, {3, 5, 6}},
-     {L"\U00001b05\U00001b44\U00001b33\U00001b03", {6917}, {4}},
-     {L"\U00000cf1\U00000c95", {3313, 3221}, {1, 2}},
-     {L"\U00000cf2\U00000cab", {3314, 3243}, {1, 2}},
-     {L"\U00000cf1\U00000c95\U00000cbf", {3313, 3221}, {1, 3}},
-     {L"\U00001cf5\U00000995", {7413}, {2}},
-     {L"\U00001cf6\U000009aa", {7414}, {2}},
-     {L"\U00001cf5\U0000200c\U00000995", {7413, 2453}, {2, 3}},
-     {L"\U00001cf6\U0000200c\U000009aa", {7414, 2474}, {2, 3}},
-     {L"\U00011a3a\U00011a0b", {72250}, {4}}}};
+     {L"\U0000000d\U0000000d"sv, {13, 13}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000000d"sv, {13, 776, 13}, {1, 2, 3}},
+     {L"\U0000000d\U0000000a"sv, {13}, {2}},
+     {L"\U0000000d\U00000308\U0000000a"sv, {13, 776, 10}, {1, 2, 3}},
+     {L"\U0000000d\U00000000"sv, {13, 0}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000000"sv, {13, 776, 0}, {1, 2, 3}},
+     {L"\U0000000d\U0000094d"sv, {13, 2381}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000094d"sv, {13, 776}, {1, 3}},
+     {L"\U0000000d\U00000300"sv, {13, 768}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000300"sv, {13, 776}, {1, 3}},
+     {L"\U0000000d\U0000200c"sv, {13, 8204}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000200c"sv, {13, 776}, {1, 3}},
+     {L"\U0000000d\U0000200d"sv, {13, 8205}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000200d"sv, {13, 776}, {1, 3}},
+     {L"\U0000000d\U0001f1e6"sv, {13, 127462}, {1, 3}},
+     {L"\U0000000d\U00000308\U0001f1e6"sv, {13, 776, 127462}, {1, 2, 4}},
+     {L"\U0000000d\U000006dd"sv, {13, 1757}, {1, 2}},
+     {L"\U0000000d\U00000308\U000006dd"sv, {13, 776, 1757}, {1, 2, 3}},
+     {L"\U0000000d\U00000903"sv, {13, 2307}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000903"sv, {13, 776}, {1, 3}},
+     {L"\U0000000d\U00001100"sv, {13, 4352}, {1, 2}},
+     {L"\U0000000d\U00000308\U00001100"sv, {13, 776, 4352}, {1, 2, 3}},
+     {L"\U0000000d\U00001160"sv, {13, 4448}, {1, 2}},
+     {L"\U0000000d\U00000308\U00001160"sv, {13, 776, 4448}, {1, 2, 3}},
+     {L"\U0000000d\U000011a8"sv, {13, 4520}, {1, 2}},
+     {L"\U0000000d\U00000308\U000011a8"sv, {13, 776, 4520}, {1, 2, 3}},
+     {L"\U0000000d\U0000ac00"sv, {13, 44032}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000ac00"sv, {13, 776, 44032}, {1, 2, 3}},
+     {L"\U0000000d\U0000ac01"sv, {13, 44033}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000ac01"sv, {13, 776, 44033}, {1, 2, 3}},
+     {L"\U0000000d\U00001cf5"sv, {13, 7413}, {1, 2}},
+     {L"\U0000000d\U00000308\U00001cf5"sv, {13, 776, 7413}, {1, 2, 3}},
+     {L"\U0000000d\U00000915"sv, {13, 2325}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000915"sv, {13, 776, 2325}, {1, 2, 3}},
+     {L"\U0000000d\U000000a9"sv, {13, 169}, {1, 2}},
+     {L"\U0000000d\U00000308\U000000a9"sv, {13, 776, 169}, {1, 2, 3}},
+     {L"\U0000000d\U00000020"sv, {13, 32}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000020"sv, {13, 776, 32}, {1, 2, 3}},
+     {L"\U0000000d\U00000378"sv, {13, 888}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000378"sv, {13, 776, 888}, {1, 2, 3}},
+     {L"\U0000000a\U0000000d"sv, {10, 13}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000000d"sv, {10, 776, 13}, {1, 2, 3}},
+     {L"\U0000000a\U0000000a"sv, {10, 10}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000000a"sv, {10, 776, 10}, {1, 2, 3}},
+     {L"\U0000000a\U00000000"sv, {10, 0}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000000"sv, {10, 776, 0}, {1, 2, 3}},
+     {L"\U0000000a\U0000094d"sv, {10, 2381}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000094d"sv, {10, 776}, {1, 3}},
+     {L"\U0000000a\U00000300"sv, {10, 768}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000300"sv, {10, 776}, {1, 3}},
+     {L"\U0000000a\U0000200c"sv, {10, 8204}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000200c"sv, {10, 776}, {1, 3}},
+     {L"\U0000000a\U0000200d"sv, {10, 8205}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000200d"sv, {10, 776}, {1, 3}},
+     {L"\U0000000a\U0001f1e6"sv, {10, 127462}, {1, 3}},
+     {L"\U0000000a\U00000308\U0001f1e6"sv, {10, 776, 127462}, {1, 2, 4}},
+     {L"\U0000000a\U000006dd"sv, {10, 1757}, {1, 2}},
+     {L"\U0000000a\U00000308\U000006dd"sv, {10, 776, 1757}, {1, 2, 3}},
+     {L"\U0000000a\U00000903"sv, {10, 2307}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000903"sv, {10, 776}, {1, 3}},
+     {L"\U0000000a\U00001100"sv, {10, 4352}, {1, 2}},
+     {L"\U0000000a\U00000308\U00001100"sv, {10, 776, 4352}, {1, 2, 3}},
+     {L"\U0000000a\U00001160"sv, {10, 4448}, {1, 2}},
+     {L"\U0000000a\U00000308\U00001160"sv, {10, 776, 4448}, {1, 2, 3}},
+     {L"\U0000000a\U000011a8"sv, {10, 4520}, {1, 2}},
+     {L"\U0000000a\U00000308\U000011a8"sv, {10, 776, 4520}, {1, 2, 3}},
+     {L"\U0000000a\U0000ac00"sv, {10, 44032}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000ac00"sv, {10, 776, 44032}, {1, 2, 3}},
+     {L"\U0000000a\U0000ac01"sv, {10, 44033}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000ac01"sv, {10, 776, 44033}, {1, 2, 3}},
+     {L"\U0000000a\U00001cf5"sv, {10, 7413}, {1, 2}},
+     {L"\U0000000a\U00000308\U00001cf5"sv, {10, 776, 7413}, {1, 2, 3}},
+     {L"\U0000000a\U00000915"sv, {10, 2325}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000915"sv, {10, 776, 2325}, {1, 2, 3}},
+     {L"\U0000000a\U000000a9"sv, {10, 169}, {1, 2}},
+     {L"\U0000000a\U00000308\U000000a9"sv, {10, 776, 169}, {1, 2, 3}},
+     {L"\U0000000a\U00000020"sv, {10, 32}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000020"sv, {10, 776, 32}, {1, 2, 3}},
+     {L"\U0000000a\U00000378"sv, {10, 888}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000378"sv, {10, 776, 888}, {1, 2, 3}},
+     {L"\U00000000\U0000000d"sv, {0, 13}, {1, 2}},
+     {L"\U00000000\U00000308\U0000000d"sv, {0, 776, 13}, {1, 2, 3}},
+     {L"\U00000000\U0000000a"sv, {0, 10}, {1, 2}},
+     {L"\U00000000\U00000308\U0000000a"sv, {0, 776, 10}, {1, 2, 3}},
+     {L"\U00000000\U00000000"sv, {0, 0}, {1, 2}},
+     {L"\U00000000\U00000308\U00000000"sv, {0, 776, 0}, {1, 2, 3}},
+     {L"\U00000000\U0000094d"sv, {0, 2381}, {1, 2}},
+     {L"\U00000000\U00000308\U0000094d"sv, {0, 776}, {1, 3}},
+     {L"\U00000000\U00000300"sv, {0, 768}, {1, 2}},
+     {L"\U00000000\U00000308\U00000300"sv, {0, 776}, {1, 3}},
+     {L"\U00000000\U0000200c"sv, {0, 8204}, {1, 2}},
+     {L"\U00000000\U00000308\U0000200c"sv, {0, 776}, {1, 3}},
+     {L"\U00000000\U0000200d"sv, {0, 8205}, {1, 2}},
+     {L"\U00000000\U00000308\U0000200d"sv, {0, 776}, {1, 3}},
+     {L"\U00000000\U0001f1e6"sv, {0, 127462}, {1, 3}},
+     {L"\U00000000\U00000308\U0001f1e6"sv, {0, 776, 127462}, {1, 2, 4}},
+     {L"\U00000000\U000006dd"sv, {0, 1757}, {1, 2}},
+     {L"\U00000000\U00000308\U000006dd"sv, {0, 776, 1757}, {1, 2, 3}},
+     {L"\U00000000\U00000903"sv, {0, 2307}, {1, 2}},
+     {L"\U00000000\U00000308\U00000903"sv, {0, 776}, {1, 3}},
+     {L"\U00000000\U00001100"sv, {0, 4352}, {1, 2}},
+     {L"\U00000000\U00000308\U00001100"sv, {0, 776, 4352}, {1, 2, 3}},
+     {L"\U00000000\U00001160"sv, {0, 4448}, {1, 2}},
+     {L"\U00000000\U00000308\U00001160"sv, {0, 776, 4448}, {1, 2, 3}},
+     {L"\U00000000\U000011a8"sv, {0, 4520}, {1, 2}},
+     {L"\U00000000\U00000308\U000011a8"sv, {0, 776, 4520}, {1, 2, 3}},
+     {L"\U00000000\U0000ac00"sv, {0, 44032}, {1, 2}},
+     {L"\U00000000\U00000308\U0000ac00"sv, {0, 776, 44032}, {1, 2, 3}},
+     {L"\U00000000\U0000ac01"sv, {0, 44033}, {1, 2}},
+     {L"\U00000000\U00000308\U0000ac01"sv, {0, 776, 44033}, {1, 2, 3}},
+     {L"\U00000000\U00001cf5"sv, {0, 7413}, {1, 2}},
+     {L"\U00000000\U00000308\U00001cf5"sv, {0, 776, 7413}, {1, 2, 3}},
+     {L"\U00000000\U00000915"sv, {0, 2325}, {1, 2}},
+     {L"\U00000000\U00000308\U00000915"sv, {0, 776, 2325}, {1, 2, 3}},
+     {L"\U00000000\U000000a9"sv, {0, 169}, {1, 2}},
+     {L"\U00000000\U00000308\U000000a9"sv, {0, 776, 169}, {1, 2, 3}},
+     {L"\U00000000\U00000020"sv, {0, 32}, {1, 2}},
+     {L"\U00000000\U00000308\U00000020"sv, {0, 776, 32}, {1, 2, 3}},
+     {L"\U00000000\U00000378"sv, {0, 888}, {1, 2}},
+     {L"\U00000000\U00000308\U00000378"sv, {0, 776, 888}, {1, 2, 3}},
+     {L"\U0000094d\U0000000d"sv, {2381, 13}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000000d"sv, {2381, 13}, {2, 3}},
+     {L"\U0000094d\U0000000a"sv, {2381, 10}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000000a"sv, {2381, 10}, {2, 3}},
+     {L"\U0000094d\U00000000"sv, {2381, 0}, {1, 2}},
+     {L"\U0000094d\U00000308\U00000000"sv, {2381, 0}, {2, 3}},
+     {L"\U0000094d\U0000094d"sv, {2381}, {2}},
+     {L"\U0000094d\U00000308\U0000094d"sv, {2381}, {3}},
+     {L"\U0000094d\U00000300"sv, {2381}, {2}},
+     {L"\U0000094d\U00000308\U00000300"sv, {2381}, {3}},
+     {L"\U0000094d\U0000200c"sv, {2381}, {2}},
+     {L"\U0000094d\U00000308\U0000200c"sv, {2381}, {3}},
+     {L"\U0000094d\U0000200d"sv, {2381}, {2}},
+     {L"\U0000094d\U00000308\U0000200d"sv, {2381}, {3}},
+     {L"\U0000094d\U0001f1e6"sv, {2381, 127462}, {1, 3}},
+     {L"\U0000094d\U00000308\U0001f1e6"sv, {2381, 127462}, {2, 4}},
+     {L"\U0000094d\U000006dd"sv, {2381, 1757}, {1, 2}},
+     {L"\U0000094d\U00000308\U000006dd"sv, {2381, 1757}, {2, 3}},
+     {L"\U0000094d\U00000903"sv, {2381}, {2}},
+     {L"\U0000094d\U00000308\U00000903"sv, {2381}, {3}},
+     {L"\U0000094d\U00001100"sv, {2381, 4352}, {1, 2}},
+     {L"\U0000094d\U00000308\U00001100"sv, {2381, 4352}, {2, 3}},
+     {L"\U0000094d\U00001160"sv, {2381, 4448}, {1, 2}},
+     {L"\U0000094d\U00000308\U00001160"sv, {2381, 4448}, {2, 3}},
+     {L"\U0000094d\U000011a8"sv, {2381, 4520}, {1, 2}},
+     {L"\U0000094d\U00000308\U000011a8"sv, {2381, 4520}, {2, 3}},
+     {L"\U0000094d\U0000ac00"sv, {2381, 44032}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000ac00"sv, {2381, 44032}, {2, 3}},
+     {L"\U0000094d\U0000ac01"sv, {2381, 44033}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000ac01"sv, {2381, 44033}, {2, 3}},
+     {L"\U0000094d\U00001cf5"sv, {2381, 7413}, {1, 2}},
+     {L"\U0000094d\U00000308\U00001cf5"sv, {2381, 7413}, {2, 3}},
+     {L"\U0000094d\U00000915"sv, {2381}, {2}},
+     {L"\U0000094d\U00000308\U00000915"sv, {2381}, {3}},
+     {L"\U0000094d\U000000a9"sv, {2381, 169}, {1, 2}},
+     {L"\U0000094d\U00000308\U000000a9"sv, {2381, 169}, {2, 3}},
+     {L"\U0000094d\U00000020"sv, {2381, 32}, {1, 2}},
+     {L"\U0000094d\U00000308\U00000020"sv, {2381, 32}, {2, 3}},
+     {L"\U0000094d\U00000378"sv, {2381, 888}, {1, 2}},
+     {L"\U0000094d\U00000308\U00000378"sv, {2381, 888}, {2, 3}},
+     {L"\U00000300\U0000000d"sv, {768, 13}, {1, 2}},
+     {L"\U00000300\U00000308\U0000000d"sv, {768, 13}, {2, 3}},
+     {L"\U00000300\U0000000a"sv, {768, 10}, {1, 2}},
+     {L"\U00000300\U00000308\U0000000a"sv, {768, 10}, {2, 3}},
+     {L"\U00000300\U00000000"sv, {768, 0}, {1, 2}},
+     {L"\U00000300\U00000308\U00000000"sv, {768, 0}, {2, 3}},
+     {L"\U00000300\U0000094d"sv, {768}, {2}},
+     {L"\U00000300\U00000308\U0000094d"sv, {768}, {3}},
+     {L"\U00000300\U00000300"sv, {768}, {2}},
+     {L"\U00000300\U00000308\U00000300"sv, {768}, {3}},
+     {L"\U00000300\U0000200c"sv, {768}, {2}},
+     {L"\U00000300\U00000308\U0000200c"sv, {768}, {3}},
+     {L"\U00000300\U0000200d"sv, {768}, {2}},
+     {L"\U00000300\U00000308\U0000200d"sv, {768}, {3}},
+     {L"\U00000300\U0001f1e6"sv, {768, 127462}, {1, 3}},
+     {L"\U00000300\U00000308\U0001f1e6"sv, {768, 127462}, {2, 4}},
+     {L"\U00000300\U000006dd"sv, {768, 1757}, {1, 2}},
+     {L"\U00000300\U00000308\U000006dd"sv, {768, 1757}, {2, 3}},
+     {L"\U00000300\U00000903"sv, {768}, {2}},
+     {L"\U00000300\U00000308\U00000903"sv, {768}, {3}},
+     {L"\U00000300\U00001100"sv, {768, 4352}, {1, 2}},
+     {L"\U00000300\U00000308\U00001100"sv, {768, 4352}, {2, 3}},
+     {L"\U00000300\U00001160"sv, {768, 4448}, {1, 2}},
+     {L"\U00000300\U00000308\U00001160"sv, {768, 4448}, {2, 3}},
+     {L"\U00000300\U000011a8"sv, {768, 4520}, {1, 2}},
+     {L"\U00000300\U00000308\U000011a8"sv, {768, 4520}, {2, 3}},
+     {L"\U00000300\U0000ac00"sv, {768, 44032}, {1, 2}},
+     {L"\U00000300\U00000308\U0000ac00"sv, {768, 44032}, {2, 3}},
+     {L"\U00000300\U0000ac01"sv, {768, 44033}, {1, 2}},
+     {L"\U00000300\U00000308\U0000ac01"sv, {768, 44033}, {2, 3}},
+     {L"\U00000300\U00001cf5"sv, {768, 7413}, {1, 2}},
+     {L"\U00000300\U00000308\U00001cf5"sv, {768, 7413}, {2, 3}},
+     {L"\U00000300\U00000915"sv, {768, 2325}, {1, 2}},
+     {L"\U00000300\U00000308\U00000915"sv, {768, 2325}, {2, 3}},
+     {L"\U00000300\U000000a9"sv, {768, 169}, {1, 2}},
+     {L"\U00000300\U00000308\U000000a9"sv, {768, 169}, {2, 3}},
+     {L"\U00000300\U00000020"sv, {768, 32}, {1, 2}},
+     {L"\U00000300\U00000308\U00000020"sv, {768, 32}, {2, 3}},
+     {L"\U00000300\U00000378"sv, {768, 888}, {1, 2}},
+     {L"\U00000300\U00000308\U00000378"sv, {768, 888}, {2, 3}},
+     {L"\U0000200c\U0000000d"sv, {8204, 13}, {1, 2}},
+     {L"\U0000200c\U00000308\U0000000d"sv, {8204, 13}, {2, 3}},
+     {L"\U0000200c\U0000000a"sv, {8204, 10}, {1, 2}},
+     {L"\U0000200c\U00000308\U0000000a"sv, {8204, 10}, {2, 3}},
+     {L"\U0000200c\U00000000"sv, {8204, 0}, {1, 2}},
+     {L"\U0000200c\U00000308\U00000000"sv, {8204, 0}, {2, 3}},
+     {L"\U0000200c\U0000094d"sv, {8204}, {2}},
+     {L"\U0000200c\U00000308\U0000094d"sv, {8204}, {3}},
+     {L"\U0000200c\U00000300"sv, {8204}, {2}},
+     {L"\U0000200c\U00000308\U00000300"sv, {8204}, {3}},
+     {L"\U0000200c\U0000200c"sv, {8204}, {2}},
+     {L"\U0000200c\U00000308\U0000200c"sv, {8204}, {3}},
+     {L"\U0000200c\U0000200d"sv, {8204}, {2}},
+     {L"\U0000200c\U00000308\U0000200d"sv, {8204}, {3}},
+     {L"\U0000200c\U0001f1e6"sv, {8204, 127462}, {1, 3}},
+     {L"\U0000200c\U00000308\U0001f1e6"sv, {8204, 127462}, {2, 4}},
+     {L"\U0000200c\U000006dd"sv, {8204, 1757}, {1, 2}},
+     {L"\U0000200c\U00000308\U000006dd"sv, {8204, 1757}, {2, 3}},
+     {L"\U0000200c\U00000903"sv, {8204}, {2}},
+     {L"\U0000200c\U00000308\U00000903"sv, {8204}, {3}},
+     {L"\U0000200c\U00001100"sv, {8204, 4352}, {1, 2}},
+     {L"\U0000200c\U00000308\U00001100"sv, {8204, 4352}, {2, 3}},
+     {L"\U0000200c\U00001160"sv, {8204, 4448}, {1, 2}},
+     {L"\U0000200c\U00000308\U00001160"sv, {8204, 4448}, {2, 3}},
+     {L"\U0000200c\U000011a8"sv, {8204, 4520}, {1, 2}},
+     {L"\U0000200c\U00000308\U000011a8"sv, {8204, 4520}, {2, 3}},
+     {L"\U0000200c\U0000ac00"sv, {8204, 44032}, {1, 2}},
+     {L"\U0000200c\U00000308\U0000ac00"sv, {8204, 44032}, {2, 3}},
+     {L"\U0000200c\U0000ac01"sv, {8204, 44033}, {1, 2}},
+     {L"\U0000200c\U00000308\U0000ac01"sv, {8204, 44033}, {2, 3}},
+     {L"\U0000200c\U00001cf5"sv, {8204, 7413}, {1, 2}},
+     {L"\U0000200c\U00000308\U00001cf5"sv, {8204, 7413}, {2, 3}},
+     {L"\U0000200c\U00000915"sv, {8204, 2325}, {1, 2}},
+     {L"\U0000200c\U00000308\U00000915"sv, {8204, 2325}, {2, 3}},
+     {L"\U0000200c\U000000a9"sv, {8204, 169}, {1, 2}},
+     {L"\U0000200c\U00000308\U000000a9"sv, {8204, 169}, {2, 3}},
+     {L"\U0000200c\U00000020"sv, {8204, 32}, {1, 2}},
+     {L"\U0000200c\U00000308\U00000020"sv, {8204, 32}, {2, 3}},
+     {L"\U0000200c\U00000378"sv, {8204, 888}, {1, 2}},
+     {L"\U0000200c\U00000308\U00000378"sv, {8204, 888}, {2, 3}},
+     {L"\U0000200d\U0000000d"sv, {8205, 13}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000000d"sv, {8205, 13}, {2, 3}},
+     {L"\U0000200d\U0000000a"sv, {8205, 10}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000000a"sv, {8205, 10}, {2, 3}},
+     {L"\U0000200d\U00000000"sv, {8205, 0}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000000"sv, {8205, 0}, {2, 3}},
+     {L"\U0000200d\U0000094d"sv, {8205}, {2}},
+     {L"\U0000200d\U00000308\U0000094d"sv, {8205}, {3}},
+     {L"\U0000200d\U00000300"sv, {8205}, {2}},
+     {L"\U0000200d\U00000308\U00000300"sv, {8205}, {3}},
+     {L"\U0000200d\U0000200c"sv, {8205}, {2}},
+     {L"\U0000200d\U00000308\U0000200c"sv, {8205}, {3}},
+     {L"\U0000200d\U0000200d"sv, {8205}, {2}},
+     {L"\U0000200d\U00000308\U0000200d"sv, {8205}, {3}},
+     {L"\U0000200d\U0001f1e6"sv, {8205, 127462}, {1, 3}},
+     {L"\U0000200d\U00000308\U0001f1e6"sv, {8205, 127462}, {2, 4}},
+     {L"\U0000200d\U000006dd"sv, {8205, 1757}, {1, 2}},
+     {L"\U0000200d\U00000308\U000006dd"sv, {8205, 1757}, {2, 3}},
+     {L"\U0000200d\U00000903"sv, {8205}, {2}},
+     {L"\U0000200d\U00000308\U00000903"sv, {8205}, {3}},
+     {L"\U0000200d\U00001100"sv, {8205, 4352}, {1, 2}},
+     {L"\U0000200d\U00000308\U00001100"sv, {8205, 4352}, {2, 3}},
+     {L"\U0000200d\U00001160"sv, {8205, 4448}, {1, 2}},
+     {L"\U0000200d\U00000308\U00001160"sv, {8205, 4448}, {2, 3}},
+     {L"\U0000200d\U000011a8"sv, {8205, 4520}, {1, 2}},
+     {L"\U0000200d\U00000308\U000011a8"sv, {8205, 4520}, {2, 3}},
+     {L"\U0000200d\U0000ac00"sv, {8205, 44032}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000ac00"sv, {8205, 44032}, {2, 3}},
+     {L"\U0000200d\U0000ac01"sv, {8205, 44033}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000ac01"sv, {8205, 44033}, {2, 3}},
+     {L"\U0000200d\U00001cf5"sv, {8205, 7413}, {1, 2}},
+     {L"\U0000200d\U00000308\U00001cf5"sv, {8205, 7413}, {2, 3}},
+     {L"\U0000200d\U00000915"sv, {8205, 2325}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000915"sv, {8205, 2325}, {2, 3}},
+     {L"\U0000200d\U000000a9"sv, {8205, 169}, {1, 2}},
+     {L"\U0000200d\U00000308\U000000a9"sv, {8205, 169}, {2, 3}},
+     {L"\U0000200d\U00000020"sv, {8205, 32}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000020"sv, {8205, 32}, {2, 3}},
+     {L"\U0000200d\U00000378"sv, {8205, 888}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000378"sv, {8205, 888}, {2, 3}},
+     {L"\U0001f1e6\U0000000d"sv, {127462, 13}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U0000000d"sv, {127462, 13}, {3, 4}},
+     {L"\U0001f1e6\U0000000a"sv, {127462, 10}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U0000000a"sv, {127462, 10}, {3, 4}},
+     {L"\U0001f1e6\U00000000"sv, {127462, 0}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U00000000"sv, {127462, 0}, {3, 4}},
+     {L"\U0001f1e6\U0000094d"sv, {127462}, {3}},
+     {L"\U0001f1e6\U00000308\U0000094d"sv, {127462}, {4}},
+     {L"\U0001f1e6\U00000300"sv, {127462}, {3}},
+     {L"\U0001f1e6\U00000308\U00000300"sv, {127462}, {4}},
+     {L"\U0001f1e6\U0000200c"sv, {127462}, {3}},
+     {L"\U0001f1e6\U00000308\U0000200c"sv, {127462}, {4}},
+     {L"\U0001f1e6\U0000200d"sv, {127462}, {3}},
+     {L"\U0001f1e6\U00000308\U0000200d"sv, {127462}, {4}},
+     {L"\U0001f1e6\U0001f1e6"sv, {127462}, {4}},
+     {L"\U0001f1e6\U00000308\U0001f1e6"sv, {127462, 127462}, {3, 5}},
+     {L"\U0001f1e6\U000006dd"sv, {127462, 1757}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U000006dd"sv, {127462, 1757}, {3, 4}},
+     {L"\U0001f1e6\U00000903"sv, {127462}, {3}},
+     {L"\U0001f1e6\U00000308\U00000903"sv, {127462}, {4}},
+     {L"\U0001f1e6\U00001100"sv, {127462, 4352}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U00001100"sv, {127462, 4352}, {3, 4}},
+     {L"\U0001f1e6\U00001160"sv, {127462, 4448}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U00001160"sv, {127462, 4448}, {3, 4}},
+     {L"\U0001f1e6\U000011a8"sv, {127462, 4520}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U000011a8"sv, {127462, 4520}, {3, 4}},
+     {L"\U0001f1e6\U0000ac00"sv, {127462, 44032}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U0000ac00"sv, {127462, 44032}, {3, 4}},
+     {L"\U0001f1e6\U0000ac01"sv, {127462, 44033}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U0000ac01"sv, {127462, 44033}, {3, 4}},
+     {L"\U0001f1e6\U00001cf5"sv, {127462, 7413}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U00001cf5"sv, {127462, 7413}, {3, 4}},
+     {L"\U0001f1e6\U00000915"sv, {127462, 2325}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U00000915"sv, {127462, 2325}, {3, 4}},
+     {L"\U0001f1e6\U000000a9"sv, {127462, 169}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U000000a9"sv, {127462, 169}, {3, 4}},
+     {L"\U0001f1e6\U00000020"sv, {127462, 32}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U00000020"sv, {127462, 32}, {3, 4}},
+     {L"\U0001f1e6\U00000378"sv, {127462, 888}, {2, 3}},
+     {L"\U0001f1e6\U00000308\U00000378"sv, {127462, 888}, {3, 4}},
+     {L"\U000006dd\U0000000d"sv, {1757, 13}, {1, 2}},
+     {L"\U000006dd\U00000308\U0000000d"sv, {1757, 13}, {2, 3}},
+     {L"\U000006dd\U0000000a"sv, {1757, 10}, {1, 2}},
+     {L"\U000006dd\U00000308\U0000000a"sv, {1757, 10}, {2, 3}},
+     {L"\U000006dd\U00000000"sv, {1757, 0}, {1, 2}},
+     {L"\U000006dd\U00000308\U00000000"sv, {1757, 0}, {2, 3}},
+     {L"\U000006dd\U0000094d"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000094d"sv, {1757}, {3}},
+     {L"\U000006dd\U00000300"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000300"sv, {1757}, {3}},
+     {L"\U000006dd\U0000200c"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000200c"sv, {1757}, {3}},
+     {L"\U000006dd\U0000200d"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000200d"sv, {1757}, {3}},
+     {L"\U000006dd\U0001f1e6"sv, {1757}, {3}},
+     {L"\U000006dd\U00000308\U0001f1e6"sv, {1757, 127462}, {2, 4}},
+     {L"\U000006dd\U000006dd"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U000006dd"sv, {1757, 1757}, {2, 3}},
+     {L"\U000006dd\U00000903"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000903"sv, {1757}, {3}},
+     {L"\U000006dd\U00001100"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00001100"sv, {1757, 4352}, {2, 3}},
+     {L"\U000006dd\U00001160"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00001160"sv, {1757, 4448}, {2, 3}},
+     {L"\U000006dd\U000011a8"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U000011a8"sv, {1757, 4520}, {2, 3}},
+     {L"\U000006dd\U0000ac00"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000ac00"sv, {1757, 44032}, {2, 3}},
+     {L"\U000006dd\U0000ac01"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000ac01"sv, {1757, 44033}, {2, 3}},
+     {L"\U000006dd\U00001cf5"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00001cf5"sv, {1757, 7413}, {2, 3}},
+     {L"\U000006dd\U00000915"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000915"sv, {1757, 2325}, {2, 3}},
+     {L"\U000006dd\U000000a9"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U000000a9"sv, {1757, 169}, {2, 3}},
+     {L"\U000006dd\U00000020"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000020"sv, {1757, 32}, {2, 3}},
+     {L"\U000006dd\U00000378"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000378"sv, {1757, 888}, {2, 3}},
+     {L"\U00000903\U0000000d"sv, {2307, 13}, {1, 2}},
+     {L"\U00000903\U00000308\U0000000d"sv, {2307, 13}, {2, 3}},
+     {L"\U00000903\U0000000a"sv, {2307, 10}, {1, 2}},
+     {L"\U00000903\U00000308\U0000000a"sv, {2307, 10}, {2, 3}},
+     {L"\U00000903\U00000000"sv, {2307, 0}, {1, 2}},
+     {L"\U00000903\U00000308\U00000000"sv, {2307, 0}, {2, 3}},
+     {L"\U00000903\U0000094d"sv, {2307}, {2}},
+     {L"\U00000903\U00000308\U0000094d"sv, {2307}, {3}},
+     {L"\U00000903\U00000300"sv, {2307}, {2}},
+     {L"\U00000903\U00000308\U00000300"sv, {2307}, {3}},
+     {L"\U00000903\U0000200c"sv, {2307}, {2}},
+     {L"\U00000903\U00000308\U0000200c"sv, {2307}, {3}},
+     {L"\U00000903\U0000200d"sv, {2307}, {2}},
+     {L"\U00000903\U00000308\U0000200d"sv, {2307}, {3}},
+     {L"\U00000903\U0001f1e6"sv, {2307, 127462}, {1, 3}},
+     {L"\U00000903\U00000308\U0001f1e6"sv, {2307, 127462}, {2, 4}},
+     {L"\U00000903\U000006dd"sv, {2307, 1757}, {1, 2}},
+     {L"\U00000903\U00000308\U000006dd"sv, {2307, 1757}, {2, 3}},
+     {L"\U00000903\U00000903"sv, {2307}, {2}},
+     {L"\U00000903\U00000308\U00000903"sv, {2307}, {3}},
+     {L"\U00000903\U00001100"sv, {2307, 4352}, {1, 2}},
+     {L"\U00000903\U00000308\U00001100"sv, {2307, 4352}, {2, 3}},
+     {L"\U00000903\U00001160"sv, {2307, 4448}, {1, 2}},
+     {L"\U00000903\U00000308\U00001160"sv, {2307, 4448}, {2, 3}},
+     {L"\U00000903\U000011a8"sv, {2307, 4520}, {1, 2}},
+     {L"\U00000903\U00000308\U000011a8"sv, {2307, 4520}, {2, 3}},
+     {L"\U00000903\U0000ac00"sv, {2307, 44032}, {1, 2}},
+     {L"\U00000903\U00000308\U0000ac00"sv, {2307, 44032}, {2, 3}},
+     {L"\U00000903\U0000ac01"sv, {2307, 44033}, {1, 2}},
+     {L"\U00000903\U00000308\U0000ac01"sv, {2307, 44033}, {2, 3}},
+     {L"\U00000903\U00001cf5"sv, {2307, 7413}, {1, 2}},
+     {L"\U00000903\U00000308\U00001cf5"sv, {2307, 7413}, {2, 3}},
+     {L"\U00000903\U00000915"sv, {2307, 2325}, {1, 2}},
+     {L"\U00000903\U00000308\U00000915"sv, {2307, 2325}, {2, 3}},
+     {L"\U00000903\U000000a9"sv, {2307, 169}, {1, 2}},
+     {L"\U00000903\U00000308\U000000a9"sv, {2307, 169}, {2, 3}},
+     {L"\U00000903\U00000020"sv, {2307, 32}, {1, 2}},
+     {L"\U00000903\U00000308\U00000020"sv, {2307, 32}, {2, 3}},
+     {L"\U00000903\U00000378"sv, {2307, 888}, {1, 2}},
+     {L"\U00000903\U00000308\U00000378"sv, {2307, 888}, {2, 3}},
+     {L"\U00001100\U0000000d"sv, {4352, 13}, {1, 2}},
+     {L"\U00001100\U00000308\U0000000d"sv, {4352, 13}, {2, 3}},
+     {L"\U00001100\U0000000a"sv, {4352, 10}, {1, 2}},
+     {L"\U00001100\U00000308\U0000000a"sv, {4352, 10}, {2, 3}},
+     {L"\U00001100\U00000000"sv, {4352, 0}, {1, 2}},
+     {L"\U00001100\U00000308\U00000000"sv, {4352, 0}, {2, 3}},
+     {L"\U00001100\U0000094d"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U0000094d"sv, {4352}, {3}},
+     {L"\U00001100\U00000300"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U00000300"sv, {4352}, {3}},
+     {L"\U00001100\U0000200c"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U0000200c"sv, {4352}, {3}},
+     {L"\U00001100\U0000200d"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U0000200d"sv, {4352}, {3}},
+     {L"\U00001100\U0001f1e6"sv, {4352, 127462}, {1, 3}},
+     {L"\U00001100\U00000308\U0001f1e6"sv, {4352, 127462}, {2, 4}},
+     {L"\U00001100\U000006dd"sv, {4352, 1757}, {1, 2}},
+     {L"\U00001100\U00000308\U000006dd"sv, {4352, 1757}, {2, 3}},
+     {L"\U00001100\U00000903"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U00000903"sv, {4352}, {3}},
+     {L"\U00001100\U00001100"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U00001100"sv, {4352, 4352}, {2, 3}},
+     {L"\U00001100\U00001160"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U00001160"sv, {4352, 4448}, {2, 3}},
+     {L"\U00001100\U000011a8"sv, {4352, 4520}, {1, 2}},
+     {L"\U00001100\U00000308\U000011a8"sv, {4352, 4520}, {2, 3}},
+     {L"\U00001100\U0000ac00"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U0000ac00"sv, {4352, 44032}, {2, 3}},
+     {L"\U00001100\U0000ac01"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U0000ac01"sv, {4352, 44033}, {2, 3}},
+     {L"\U00001100\U00001cf5"sv, {4352, 7413}, {1, 2}},
+     {L"\U00001100\U00000308\U00001cf5"sv, {4352, 7413}, {2, 3}},
+     {L"\U00001100\U00000915"sv, {4352, 2325}, {1, 2}},
+     {L"\U00001100\U00000308\U00000915"sv, {4352, 2325}, {2, 3}},
+     {L"\U00001100\U000000a9"sv, {4352, 169}, {1, 2}},
+     {L"\U00001100\U00000308\U000000a9"sv, {4352, 169}, {2, 3}},
+     {L"\U00001100\U00000020"sv, {4352, 32}, {1, 2}},
+     {L"\U00001100\U00000308\U00000020"sv, {4352, 32}, {2, 3}},
+     {L"\U00001100\U00000378"sv, {4352, 888}, {1, 2}},
+     {L"\U00001100\U00000308\U00000378"sv, {4352, 888}, {2, 3}},
+     {L"\U00001160\U0000000d"sv, {4448, 13}, {1, 2}},
+     {L"\U00001160\U00000308\U0000000d"sv, {4448, 13}, {2, 3}},
+     {L"\U00001160\U0000000a"sv, {4448, 10}, {1, 2}},
+     {L"\U00001160\U00000308\U0000000a"sv, {4448, 10}, {2, 3}},
+     {L"\U00001160\U00000000"sv, {4448, 0}, {1, 2}},
+     {L"\U00001160\U00000308\U00000000"sv, {4448, 0}, {2, 3}},
+     {L"\U00001160\U0000094d"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U0000094d"sv, {4448}, {3}},
+     {L"\U00001160\U00000300"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U00000300"sv, {4448}, {3}},
+     {L"\U00001160\U0000200c"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U0000200c"sv, {4448}, {3}},
+     {L"\U00001160\U0000200d"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U0000200d"sv, {4448}, {3}},
+     {L"\U00001160\U0001f1e6"sv, {4448, 127462}, {1, 3}},
+     {L"\U00001160\U00000308\U0001f1e6"sv, {4448, 127462}, {2, 4}},
+     {L"\U00001160\U000006dd"sv, {4448, 1757}, {1, 2}},
+     {L"\U00001160\U00000308\U000006dd"sv, {4448, 1757}, {2, 3}},
+     {L"\U00001160\U00000903"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U00000903"sv, {4448}, {3}},
+     {L"\U00001160\U00001100"sv, {4448, 4352}, {1, 2}},
+     {L"\U00001160\U00000308\U00001100"sv, {4448, 4352}, {2, 3}},
+     {L"\U00001160\U00001160"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U00001160"sv, {4448, 4448}, {2, 3}},
+     {L"\U00001160\U000011a8"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U000011a8"sv, {4448, 4520}, {2, 3}},
+     {L"\U00001160\U0000ac00"sv, {4448, 44032}, {1, 2}},
+     {L"\U00001160\U00000308\U0000ac00"sv, {4448, 44032}, {2, 3}},
+     {L"\U00001160\U0000ac01"sv, {4448, 44033}, {1, 2}},
+     {L"\U00001160\U00000308\U0000ac01"sv, {4448, 44033}, {2, 3}},
+     {L"\U00001160\U00001cf5"sv, {4448, 7413}, {1, 2}},
+     {L"\U00001160\U00000308\U00001cf5"sv, {4448, 7413}, {2, 3}},
+     {L"\U00001160\U00000915"sv, {4448, 2325}, {1, 2}},
+     {L"\U00001160\U00000308\U00000915"sv, {4448, 2325}, {2, 3}},
+     {L"\U00001160\U000000a9"sv, {4448, 169}, {1, 2}},
+     {L"\U00001160\U00000308\U000000a9"sv, {4448, 169}, {2, 3}},
+     {L"\U00001160\U00000020"sv, {4448, 32}, {1, 2}},
+     {L"\U00001160\U00000308\U00000020"sv, {4448, 32}, {2, 3}},
+     {L"\U00001160\U00000378"sv, {4448, 888}, {1, 2}},
+     {L"\U00001160\U00000308\U00000378"sv, {4448, 888}, {2, 3}},
+     {L"\U000011a8\U0000000d"sv, {4520, 13}, {1, 2}},
+     {L"\U000011a8\U00000308\U0000000d"sv, {4520, 13}, {2, 3}},
+     {L"\U000011a8\U0000000a"sv, {4520, 10}, {1, 2}},
+     {L"\U000011a8\U00000308\U0000000a"sv, {4520, 10}, {2, 3}},
+     {L"\U000011a8\U00000000"sv, {4520, 0}, {1, 2}},
+     {L"\U000011a8\U00000308\U00000000"sv, {4520, 0}, {2, 3}},
+     {L"\U000011a8\U0000094d"sv, {4520}, {2}},
+     {L"\U000011a8\U00000308\U0000094d"sv, {4520}, {3}},
+     {L"\U000011a8\U00000300"sv, {4520}, {2}},
+     {L"\U000011a8\U00000308\U00000300"sv, {4520}, {3}},
+     {L"\U000011a8\U0000200c"sv, {4520}, {2}},
+     {L"\U000011a8\U00000308\U0000200c"sv, {4520}, {3}},
+     {L"\U000011a8\U0000200d"sv, {4520}, {2}},
+     {L"\U000011a8\U00000308\U0000200d"sv, {4520}, {3}},
+     {L"\U000011a8\U0001f1e6"sv, {4520, 127462}, {1, 3}},
+     {L"\U000011a8\U00000308\U0001f1e6"sv, {4520, 127462}, {2, 4}},
+     {L"\U000011a8\U000006dd"sv, {4520, 1757}, {1, 2}},
+     {L"\U000011a8\U00000308\U000006dd"sv, {4520, 1757}, {2, 3}},
+     {L"\U000011a8\U00000903"sv, {4520}, {2}},
+     {L"\U000011a8\U00000308\U00000903"sv, {4520}, {3}},
+     {L"\U000011a8\U00001100"sv, {4520, 4352}, {1, 2}},
+     {L"\U000011a8\U00000308\U00001100"sv, {4520, 4352}, {2, 3}},
+     {L"\U000011a8\U00001160"sv, {4520, 4448}, {1, 2}},
+     {L"\U000011a8\U00000308\U00001160"sv, {4520, 4448}, {2, 3}},
+     {L"\U000011a8\U000011a8"sv, {4520}, {2}},
+     {L"\U000011a8\U00000308\U000011a8"sv, {4520, 4520}, {2, 3}},
+     {L"\U000011a8\U0000ac00"sv, {4520, 44032}, {1, 2}},
+     {L"\U000011a8\U00000308\U0000ac00"sv, {4520, 44032}, {2, 3}},
+     {L"\U000011a8\U0000ac01"sv, {4520, 44033}, {1, 2}},
+     {L"\U000011a8\U00000308\U0000ac01"sv, {4520, 44033}, {2, 3}},
+     {L"\U000011a8\U00001cf5"sv, {4520, 7413}, {1, 2}},
+     {L"\U000011a8\U00000308\U00001cf5"sv, {4520, 7413}, {2, 3}},
+     {L"\U000011a8\U00000915"sv, {4520, 2325}, {1, 2}},
+     {L"\U000011a8\U00000308\U00000915"sv, {4520, 2325}, {2, 3}},
+     {L"\U000011a8\U000000a9"sv, {4520, 169}, {1, 2}},
+     {L"\U000011a8\U00000308\U000000a9"sv, {4520, 169}, {2, 3}},
+     {L"\U000011a8\U00000020"sv, {4520, 32}, {1, 2}},
+     {L"\U000011a8\U00000308\U00000020"sv, {4520, 32}, {2, 3}},
+     {L"\U000011a8\U00000378"sv, {4520, 888}, {1, 2}},
+     {L"\U000011a8\U00000308\U00000378"sv, {4520, 888}, {2, 3}},
+     {L"\U0000ac00\U0000000d"sv, {44032, 13}, {1, 2}},
+     {L"\U0000ac00\U00000308\U0000000d"sv, {44032, 13}, {2, 3}},
+     {L"\U0000ac00\U0000000a"sv, {44032, 10}, {1, 2}},
+     {L"\U0000ac00\U00000308\U0000000a"sv, {44032, 10}, {2, 3}},
+     {L"\U0000ac00\U00000000"sv, {44032, 0}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00000000"sv, {44032, 0}, {2, 3}},
+     {L"\U0000ac00\U0000094d"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U0000094d"sv, {44032}, {3}},
+     {L"\U0000ac00\U00000300"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U00000300"sv, {44032}, {3}},
+     {L"\U0000ac00\U0000200c"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U0000200c"sv, {44032}, {3}},
+     {L"\U0000ac00\U0000200d"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U0000200d"sv, {44032}, {3}},
+     {L"\U0000ac00\U0001f1e6"sv, {44032, 127462}, {1, 3}},
+     {L"\U0000ac00\U00000308\U0001f1e6"sv, {44032, 127462}, {2, 4}},
+     {L"\U0000ac00\U000006dd"sv, {44032, 1757}, {1, 2}},
+     {L"\U0000ac00\U00000308\U000006dd"sv, {44032, 1757}, {2, 3}},
+     {L"\U0000ac00\U00000903"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U00000903"sv, {44032}, {3}},
+     {L"\U0000ac00\U00001100"sv, {44032, 4352}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00001100"sv, {44032, 4352}, {2, 3}},
+     {L"\U0000ac00\U00001160"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U00001160"sv, {44032, 4448}, {2, 3}},
+     {L"\U0000ac00\U000011a8"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U000011a8"sv, {44032, 4520}, {2, 3}},
+     {L"\U0000ac00\U0000ac00"sv, {44032, 44032}, {1, 2}},
+     {L"\U0000ac00\U00000308\U0000ac00"sv, {44032, 44032}, {2, 3}},
+     {L"\U0000ac00\U0000ac01"sv, {44032, 44033}, {1, 2}},
+     {L"\U0000ac00\U00000308\U0000ac01"sv, {44032, 44033}, {2, 3}},
+     {L"\U0000ac00\U00001cf5"sv, {44032, 7413}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00001cf5"sv, {44032, 7413}, {2, 3}},
+     {L"\U0000ac00\U00000915"sv, {44032, 2325}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00000915"sv, {44032, 2325}, {2, 3}},
+     {L"\U0000ac00\U000000a9"sv, {44032, 169}, {1, 2}},
+     {L"\U0000ac00\U00000308\U000000a9"sv, {44032, 169}, {2, 3}},
+     {L"\U0000ac00\U00000020"sv, {44032, 32}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00000020"sv, {44032, 32}, {2, 3}},
+     {L"\U0000ac00\U00000378"sv, {44032, 888}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00000378"sv, {44032, 888}, {2, 3}},
+     {L"\U0000ac01\U0000000d"sv, {44033, 13}, {1, 2}},
+     {L"\U0000ac01\U00000308\U0000000d"sv, {44033, 13}, {2, 3}},
+     {L"\U0000ac01\U0000000a"sv, {44033, 10}, {1, 2}},
+     {L"\U0000ac01\U00000308\U0000000a"sv, {44033, 10}, {2, 3}},
+     {L"\U0000ac01\U00000000"sv, {44033, 0}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00000000"sv, {44033, 0}, {2, 3}},
+     {L"\U0000ac01\U0000094d"sv, {44033}, {2}},
+     {L"\U0000ac01\U00000308\U0000094d"sv, {44033}, {3}},
+     {L"\U0000ac01\U00000300"sv, {44033}, {2}},
+     {L"\U0000ac01\U00000308\U00000300"sv, {44033}, {3}},
+     {L"\U0000ac01\U0000200c"sv, {44033}, {2}},
+     {L"\U0000ac01\U00000308\U0000200c"sv, {44033}, {3}},
+     {L"\U0000ac01\U0000200d"sv, {44033}, {2}},
+     {L"\U0000ac01\U00000308\U0000200d"sv, {44033}, {3}},
+     {L"\U0000ac01\U0001f1e6"sv, {44033, 127462}, {1, 3}},
+     {L"\U0000ac01\U00000308\U0001f1e6"sv, {44033, 127462}, {2, 4}},
+     {L"\U0000ac01\U000006dd"sv, {44033, 1757}, {1, 2}},
+     {L"\U0000ac01\U00000308\U000006dd"sv, {44033, 1757}, {2, 3}},
+     {L"\U0000ac01\U00000903"sv, {44033}, {2}},
+     {L"\U0000ac01\U00000308\U00000903"sv, {44033}, {3}},
+     {L"\U0000ac01\U00001100"sv, {44033, 4352}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00001100"sv, {44033, 4352}, {2, 3}},
+     {L"\U0000ac01\U00001160"sv, {44033, 4448}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00001160"sv, {44033, 4448}, {2, 3}},
+     {L"\U0000ac01\U000011a8"sv, {44033}, {2}},
+     {L"\U0000ac01\U00000308\U000011a8"sv, {44033, 4520}, {2, 3}},
+     {L"\U0000ac01\U0000ac00"sv, {44033, 44032}, {1, 2}},
+     {L"\U0000ac01\U00000308\U0000ac00"sv, {44033, 44032}, {2, 3}},
+     {L"\U0000ac01\U0000ac01"sv, {44033, 44033}, {1, 2}},
+     {L"\U0000ac01\U00000308\U0000ac01"sv, {44033, 44033}, {2, 3}},
+     {L"\U0000ac01\U00001cf5"sv, {44033, 7413}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00001cf5"sv, {44033, 7413}, {2, 3}},
+     {L"\U0000ac01\U00000915"sv, {44033, 2325}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00000915"sv, {44033, 2325}, {2, 3}},
+     {L"\U0000ac01\U000000a9"sv, {44033, 169}, {1, 2}},
+     {L"\U0000ac01\U00000308\U000000a9"sv, {44033, 169}, {2, 3}},
+     {L"\U0000ac01\U00000020"sv, {44033, 32}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00000020"sv, {44033, 32}, {2, 3}},
+     {L"\U0000ac01\U00000378"sv, {44033, 888}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00000378"sv, {44033, 888}, {2, 3}},
+     {L"\U00001cf5\U0000000d"sv, {7413, 13}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000000d"sv, {7413, 13}, {2, 3}},
+     {L"\U00001cf5\U0000000a"sv, {7413, 10}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000000a"sv, {7413, 10}, {2, 3}},
+     {L"\U00001cf5\U00000000"sv, {7413, 0}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00000000"sv, {7413, 0}, {2, 3}},
+     {L"\U00001cf5\U0000094d"sv, {7413}, {2}},
+     {L"\U00001cf5\U00000308\U0000094d"sv, {7413}, {3}},
+     {L"\U00001cf5\U00000300"sv, {7413}, {2}},
+     {L"\U00001cf5\U00000308\U00000300"sv, {7413}, {3}},
+     {L"\U00001cf5\U0000200c"sv, {7413}, {2}},
+     {L"\U00001cf5\U00000308\U0000200c"sv, {7413}, {3}},
+     {L"\U00001cf5\U0000200d"sv, {7413}, {2}},
+     {L"\U00001cf5\U00000308\U0000200d"sv, {7413}, {3}},
+     {L"\U00001cf5\U0001f1e6"sv, {7413, 127462}, {1, 3}},
+     {L"\U00001cf5\U00000308\U0001f1e6"sv, {7413, 127462}, {2, 4}},
+     {L"\U00001cf5\U000006dd"sv, {7413, 1757}, {1, 2}},
+     {L"\U00001cf5\U00000308\U000006dd"sv, {7413, 1757}, {2, 3}},
+     {L"\U00001cf5\U00000903"sv, {7413}, {2}},
+     {L"\U00001cf5\U00000308\U00000903"sv, {7413}, {3}},
+     {L"\U00001cf5\U00001100"sv, {7413, 4352}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00001100"sv, {7413, 4352}, {2, 3}},
+     {L"\U00001cf5\U00001160"sv, {7413, 4448}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00001160"sv, {7413, 4448}, {2, 3}},
+     {L"\U00001cf5\U000011a8"sv, {7413, 4520}, {1, 2}},
+     {L"\U00001cf5\U00000308\U000011a8"sv, {7413, 4520}, {2, 3}},
+     {L"\U00001cf5\U0000ac00"sv, {7413, 44032}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000ac00"sv, {7413, 44032}, {2, 3}},
+     {L"\U00001cf5\U0000ac01"sv, {7413, 44033}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000ac01"sv, {7413, 44033}, {2, 3}},
+     {L"\U00001cf5\U00001cf5"sv, {7413, 7413}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00001cf5"sv, {7413, 7413}, {2, 3}},
+     {L"\U00001cf5\U00000915"sv, {7413}, {2}},
+     {L"\U00001cf5\U00000308\U00000915"sv, {7413}, {3}},
+     {L"\U00001cf5\U000000a9"sv, {7413, 169}, {1, 2}},
+     {L"\U00001cf5\U00000308\U000000a9"sv, {7413, 169}, {2, 3}},
+     {L"\U00001cf5\U00000020"sv, {7413, 32}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00000020"sv, {7413, 32}, {2, 3}},
+     {L"\U00001cf5\U00000378"sv, {7413, 888}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00000378"sv, {7413, 888}, {2, 3}},
+     {L"\U00000915\U0000000d"sv, {2325, 13}, {1, 2}},
+     {L"\U00000915\U00000308\U0000000d"sv, {2325, 13}, {2, 3}},
+     {L"\U00000915\U0000000a"sv, {2325, 10}, {1, 2}},
+     {L"\U00000915\U00000308\U0000000a"sv, {2325, 10}, {2, 3}},
+     {L"\U00000915\U00000000"sv, {2325, 0}, {1, 2}},
+     {L"\U00000915\U00000308\U00000000"sv, {2325, 0}, {2, 3}},
+     {L"\U00000915\U0000094d"sv, {2325}, {2}},
+     {L"\U00000915\U00000308\U0000094d"sv, {2325}, {3}},
+     {L"\U00000915\U00000300"sv, {2325}, {2}},
+     {L"\U00000915\U00000308\U00000300"sv, {2325}, {3}},
+     {L"\U00000915\U0000200c"sv, {2325}, {2}},
+     {L"\U00000915\U00000308\U0000200c"sv, {2325}, {3}},
+     {L"\U00000915\U0000200d"sv, {2325}, {2}},
+     {L"\U00000915\U00000308\U0000200d"sv, {2325}, {3}},
+     {L"\U00000915\U0001f1e6"sv, {2325, 127462}, {1, 3}},
+     {L"\U00000915\U00000308\U0001f1e6"sv, {2325, 127462}, {2, 4}},
+     {L"\U00000915\U000006dd"sv, {2325, 1757}, {1, 2}},
+     {L"\U00000915\U00000308\U000006dd"sv, {2325, 1757}, {2, 3}},
+     {L"\U00000915\U00000903"sv, {2325}, {2}},
+     {L"\U00000915\U00000308\U00000903"sv, {2325}, {3}},
+     {L"\U00000915\U00001100"sv, {2325, 4352}, {1, 2}},
+     {L"\U00000915\U00000308\U00001100"sv, {2325, 4352}, {2, 3}},
+     {L"\U00000915\U00001160"sv, {2325, 4448}, {1, 2}},
+     {L"\U00000915\U00000308\U00001160"sv, {2325, 4448}, {2, 3}},
+     {L"\U00000915\U000011a8"sv, {2325, 4520}, {1, 2}},
+     {L"\U00000915\U00000308\U000011a8"sv, {2325, 4520}, {2, 3}},
+     {L"\U00000915\U0000ac00"sv, {2325, 44032}, {1, 2}},
+     {L"\U00000915\U00000308\U0000ac00"sv, {2325, 44032}, {2, 3}},
+     {L"\U00000915\U0000ac01"sv, {2325, 44033}, {1, 2}},
+     {L"\U00000915\U00000308\U0000ac01"sv, {2325, 44033}, {2, 3}},
+     {L"\U00000915\U00001cf5"sv, {2325, 7413}, {1, 2}},
+     {L"\U00000915\U00000308\U00001cf5"sv, {2325, 7413}, {2, 3}},
+     {L"\U00000915\U00000915"sv, {2325, 2325}, {1, 2}},
+     {L"\U00000915\U00000308\U00000915"sv, {2325, 2325}, {2, 3}},
+     {L"\U00000915\U000000a9"sv, {2325, 169}, {1, 2}},
+     {L"\U00000915\U00000308\U000000a9"sv, {2325, 169}, {2, 3}},
+     {L"\U00000915\U00000020"sv, {2325, 32}, {1, 2}},
+     {L"\U00000915\U00000308\U00000020"sv, {2325, 32}, {2, 3}},
+     {L"\U00000915\U00000378"sv, {2325, 888}, {1, 2}},
+     {L"\U00000915\U00000308\U00000378"sv, {2325, 888}, {2, 3}},
+     {L"\U000000a9\U0000000d"sv, {169, 13}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000000d"sv, {169, 13}, {2, 3}},
+     {L"\U000000a9\U0000000a"sv, {169, 10}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000000a"sv, {169, 10}, {2, 3}},
+     {L"\U000000a9\U00000000"sv, {169, 0}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000000"sv, {169, 0}, {2, 3}},
+     {L"\U000000a9\U0000094d"sv, {169}, {2}},
+     {L"\U000000a9\U00000308\U0000094d"sv, {169}, {3}},
+     {L"\U000000a9\U00000300"sv, {169}, {2}},
+     {L"\U000000a9\U00000308\U00000300"sv, {169}, {3}},
+     {L"\U000000a9\U0000200c"sv, {169}, {2}},
+     {L"\U000000a9\U00000308\U0000200c"sv, {169}, {3}},
+     {L"\U000000a9\U0000200d"sv, {169}, {2}},
+     {L"\U000000a9\U00000308\U0000200d"sv, {169}, {3}},
+     {L"\U000000a9\U0001f1e6"sv, {169, 127462}, {1, 3}},
+     {L"\U000000a9\U00000308\U0001f1e6"sv, {169, 127462}, {2, 4}},
+     {L"\U000000a9\U000006dd"sv, {169, 1757}, {1, 2}},
+     {L"\U000000a9\U00000308\U000006dd"sv, {169, 1757}, {2, 3}},
+     {L"\U000000a9\U00000903"sv, {169}, {2}},
+     {L"\U000000a9\U00000308\U00000903"sv, {169}, {3}},
+     {L"\U000000a9\U00001100"sv, {169, 4352}, {1, 2}},
+     {L"\U000000a9\U00000308\U00001100"sv, {169, 4352}, {2, 3}},
+     {L"\U000000a9\U00001160"sv, {169, 4448}, {1, 2}},
+     {L"\U000000a9\U00000308\U00001160"sv, {169, 4448}, {2, 3}},
+     {L"\U000000a9\U000011a8"sv, {169, 4520}, {1, 2}},
+     {L"\U000000a9\U00000308\U000011a8"sv, {169, 4520}, {2, 3}},
+     {L"\U000000a9\U0000ac00"sv, {169, 44032}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000ac00"sv, {169, 44032}, {2, 3}},
+     {L"\U000000a9\U0000ac01"sv, {169, 44033}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000ac01"sv, {169, 44033}, {2, 3}},
+     {L"\U000000a9\U00001cf5"sv, {169, 7413}, {1, 2}},
+     {L"\U000000a9\U00000308\U00001cf5"sv, {169, 7413}, {2, 3}},
+     {L"\U000000a9\U00000915"sv, {169, 2325}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000915"sv, {169, 2325}, {2, 3}},
+     {L"\U000000a9\U000000a9"sv, {169, 169}, {1, 2}},
+     {L"\U000000a9\U00000308\U000000a9"sv, {169, 169}, {2, 3}},
+     {L"\U000000a9\U00000020"sv, {169, 32}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000020"sv, {169, 32}, {2, 3}},
+     {L"\U000000a9\U00000378"sv, {169, 888}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000378"sv, {169, 888}, {2, 3}},
+     {L"\U00000020\U0000000d"sv, {32, 13}, {1, 2}},
+     {L"\U00000020\U00000308\U0000000d"sv, {32, 13}, {2, 3}},
+     {L"\U00000020\U0000000a"sv, {32, 10}, {1, 2}},
+     {L"\U00000020\U00000308\U0000000a"sv, {32, 10}, {2, 3}},
+     {L"\U00000020\U00000000"sv, {32, 0}, {1, 2}},
+     {L"\U00000020\U00000308\U00000000"sv, {32, 0}, {2, 3}},
+     {L"\U00000020\U0000094d"sv, {32}, {2}},
+     {L"\U00000020\U00000308\U0000094d"sv, {32}, {3}},
+     {L"\U00000020\U00000300"sv, {32}, {2}},
+     {L"\U00000020\U00000308\U00000300"sv, {32}, {3}},
+     {L"\U00000020\U0000200c"sv, {32}, {2}},
+     {L"\U00000020\U00000308\U0000200c"sv, {32}, {3}},
+     {L"\U00000020\U0000200d"sv, {32}, {2}},
+     {L"\U00000020\U00000308\U0000200d"sv, {32}, {3}},
+     {L"\U00000020\U0001f1e6"sv, {32, 127462}, {1, 3}},
+     {L"\U00000020\U00000308\U0001f1e6"sv, {32, 127462}, {2, 4}},
+     {L"\U00000020\U000006dd"sv, {32, 1757}, {1, 2}},
+     {L"\U00000020\U00000308\U000006dd"sv, {32, 1757}, {2, 3}},
+     {L"\U00000020\U00000903"sv, {32}, {2}},
+     {L"\U00000020\U00000308\U00000903"sv, {32}, {3}},
+     {L"\U00000020\U00001100"sv, {32, 4352}, {1, 2}},
+     {L"\U00000020\U00000308\U00001100"sv, {32, 4352}, {2, 3}},
+     {L"\U00000020\U00001160"sv, {32, 4448}, {1, 2}},
+     {L"\U00000020\U00000308\U00001160"sv, {32, 4448}, {2, 3}},
+     {L"\U00000020\U000011a8"sv, {32, 4520}, {1, 2}},
+     {L"\U00000020\U00000308\U000011a8"sv, {32, 4520}, {2, 3}},
+     {L"\U00000020\U0000ac00"sv, {32, 44032}, {1, 2}},
+     {L"\U00000020\U00000308\U0000ac00"sv, {32, 44032}, {2, 3}},
+     {L"\U00000020\U0000ac01"sv, {32, 44033}, {1, 2}},
+     {L"\U00000020\U00000308\U0000ac01"sv, {32, 44033}, {2, 3}},
+     {L"\U00000020\U00001cf5"sv, {32, 7413}, {1, 2}},
+     {L"\U00000020\U00000308\U00001cf5"sv, {32, 7413}, {2, 3}},
+     {L"\U00000020\U00000915"sv, {32, 2325}, {1, 2}},
+     {L"\U00000020\U00000308\U00000915"sv, {32, 2325}, {2, 3}},
+     {L"\U00000020\U000000a9"sv, {32, 169}, {1, 2}},
+     {L"\U00000020\U00000308\U000000a9"sv, {32, 169}, {2, 3}},
+     {L"\U00000020\U00000020"sv, {32, 32}, {1, 2}},
+     {L"\U00000020\U00000308\U00000020"sv, {32, 32}, {2, 3}},
+     {L"\U00000020\U00000378"sv, {32, 888}, {1, 2}},
+     {L"\U00000020\U00000308\U00000378"sv, {32, 888}, {2, 3}},
+     {L"\U00000378\U0000000d"sv, {888, 13}, {1, 2}},
+     {L"\U00000378\U00000308\U0000000d"sv, {888, 13}, {2, 3}},
+     {L"\U00000378\U0000000a"sv, {888, 10}, {1, 2}},
+     {L"\U00000378\U00000308\U0000000a"sv, {888, 10}, {2, 3}},
+     {L"\U00000378\U00000000"sv, {888, 0}, {1, 2}},
+     {L"\U00000378\U00000308\U00000000"sv, {888, 0}, {2, 3}},
+     {L"\U00000378\U0000094d"sv, {888}, {2}},
+     {L"\U00000378\U00000308\U0000094d"sv, {888}, {3}},
+     {L"\U00000378\U00000300"sv, {888}, {2}},
+     {L"\U00000378\U00000308\U00000300"sv, {888}, {3}},
+     {L"\U00000378\U0000200c"sv, {888}, {2}},
+     {L"\U00000378\U00000308\U0000200c"sv, {888}, {3}},
+     {L"\U00000378\U0000200d"sv, {888}, {2}},
+     {L"\U00000378\U00000308\U0000200d"sv, {888}, {3}},
+     {L"\U00000378\U0001f1e6"sv, {888, 127462}, {1, 3}},
+     {L"\U00000378\U00000308\U0001f1e6"sv, {888, 127462}, {2, 4}},
+     {L"\U00000378\U000006dd"sv, {888, 1757}, {1, 2}},
+     {L"\U00000378\U00000308\U000006dd"sv, {888, 1757}, {2, 3}},
+     {L"\U00000378\U00000903"sv, {888}, {2}},
+     {L"\U00000378\U00000308\U00000903"sv, {888}, {3}},
+     {L"\U00000378\U00001100"sv, {888, 4352}, {1, 2}},
+     {L"\U00000378\U00000308\U00001100"sv, {888, 4352}, {2, 3}},
+     {L"\U00000378\U00001160"sv, {888, 4448}, {1, 2}},
+     {L"\U00000378\U00000308\U00001160"sv, {888, 4448}, {2, 3}},
+     {L"\U00000378\U000011a8"sv, {888, 4520}, {1, 2}},
+     {L"\U00000378\U00000308\U000011a8"sv, {888, 4520}, {2, 3}},
+     {L"\U00000378\U0000ac00"sv, {888, 44032}, {1, 2}},
+     {L"\U00000378\U00000308\U0000ac00"sv, {888, 44032}, {2, 3}},
+     {L"\U00000378\U0000ac01"sv, {888, 44033}, {1, 2}},
+     {L"\U00000378\U00000308\U0000ac01"sv, {888, 44033}, {2, 3}},
+     {L"\U00000378\U00001cf5"sv, {888, 7413}, {1, 2}},
+     {L"\U00000378\U00000308\U00001cf5"sv, {888, 7413}, {2, 3}},
+     {L"\U00000378\U00000915"sv, {888, 2325}, {1, 2}},
+     {L"\U00000378\U00000308\U00000915"sv, {888, 2325}, {2, 3}},
+     {L"\U00000378\U000000a9"sv, {888, 169}, {1, 2}},
+     {L"\U00000378\U00000308\U000000a9"sv, {888, 169}, {2, 3}},
+     {L"\U00000378\U00000020"sv, {888, 32}, {1, 2}},
+     {L"\U00000378\U00000308\U00000020"sv, {888, 32}, {2, 3}},
+     {L"\U00000378\U00000378"sv, {888, 888}, {1, 2}},
+     {L"\U00000378\U00000308\U00000378"sv, {888, 888}, {2, 3}},
+     {L"\U0000000d\U0000000a\U00000061\U0000000a\U00000308"sv, {13, 97, 10, 776}, {2, 3, 4, 5}},
+     {L"\U00000061\U00000308"sv, {97}, {2}},
+     {L"\U00000020\U0000200d\U00000646"sv, {32, 1606}, {2, 3}},
+     {L"\U00000646\U0000200d\U00000020"sv, {1606, 32}, {2, 3}},
+     {L"\U00001100\U00001100"sv, {4352}, {2}},
+     {L"\U0000ac00\U000011a8\U00001100"sv, {44032, 4352}, {2, 3}},
+     {L"\U0000ac01\U000011a8\U00001100"sv, {44033, 4352}, {2, 3}},
+     {L"\U0001f1e6\U0001f1e7\U0001f1e8\U00000062"sv, {127462, 127464, 98}, {4, 6, 7}},
+     {L"\U00000061\U0001f1e6\U0001f1e7\U0001f1e8\U00000062"sv, {97, 127462, 127464, 98}, {1, 5, 7, 8}},
+     {L"\U00000061\U0001f1e6\U0001f1e7\U0000200d\U0001f1e8\U00000062"sv, {97, 127462, 127464, 98}, {1, 6, 8, 9}},
+     {L"\U00000061\U0001f1e6\U0000200d\U0001f1e7\U0001f1e8\U00000062"sv, {97, 127462, 127463, 98}, {1, 4, 8, 9}},
+     {L"\U00000061\U0001f1e6\U0001f1e7\U0001f1e8\U0001f1e9\U00000062"sv, {97, 127462, 127464, 98}, {1, 5, 9, 10}},
+     {L"\U00000061\U0000200d"sv, {97}, {2}},
+     {L"\U00000061\U00000308\U00000062"sv, {97, 98}, {2, 3}},
+     {L"\U00000061\U00000903\U00000062"sv, {97, 98}, {2, 3}},
+     {L"\U00000061\U00000600\U00000062"sv, {97, 1536}, {1, 3}},
+     {L"\U0001f476\U0001f3ff\U0001f476"sv, {128118, 128118}, {4, 6}},
+     {L"\U00000061\U0001f3ff\U0001f476"sv, {97, 128118}, {3, 5}},
+     {L"\U00000061\U0001f3ff\U0001f476\U0000200d\U0001f6d1"sv, {97, 128118}, {3, 8}},
+     {L"\U0001f476\U0001f3ff\U00000308\U0000200d\U0001f476\U0001f3ff"sv, {128118}, {10}},
+     {L"\U0001f6d1\U0000200d\U0001f6d1"sv, {128721}, {5}},
+     {L"\U00000061\U0000200d\U0001f6d1"sv, {97, 128721}, {2, 4}},
+     {L"\U00002701\U0000200d\U00002701"sv, {9985, 9985}, {2, 3}},
+     {L"\U00000061\U0000200d\U00002701"sv, {97, 9985}, {2, 3}},
+     {L"\U00000915\U00000924"sv, {2325, 2340}, {1, 2}},
+     {L"\U00000915\U0000094d\U00000924"sv, {2325}, {3}},
+     {L"\U00000915\U0000094d\U0000094d\U00000924"sv, {2325}, {4}},
+     {L"\U00000915\U0000094d\U0000200d\U00000924"sv, {2325}, {4}},
+     {L"\U00000915\U0000093c\U0000200d\U0000094d\U00000924"sv, {2325}, {5}},
+     {L"\U00000915\U0000093c\U0000094d\U0000200d\U00000924"sv, {2325}, {5}},
+     {L"\U00000915\U0000094d\U00000924\U0000094d\U0000092f"sv, {2325}, {5}},
+     {L"\U00000915\U0000094d\U00000061"sv, {2325, 97}, {2, 3}},
+     {L"\U00000061\U0000094d\U00000924"sv, {97}, {3}},
+     {L"\U0000003f\U0000094d\U00000924"sv, {63}, {3}},
+     {L"\U00000915\U0000094d\U0000094d\U00000924"sv, {2325}, {4}},
+     {L"\U00000ab8\U00000afb\U00000acd\U00000ab8\U00000afb"sv, {2744}, {5}},
+     {L"\U00001019\U00001039\U00001018\U0000102c\U00001037"sv, {4121, 4140}, {3, 5}},
+     {L"\U00001004\U0000103a\U00001039\U00001011\U00001039\U00001011"sv, {4100}, {6}},
+     {L"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38"sv, {6930, 6962, 6962, 6962}, {2, 5, 10, 14}},
+     {L"\U0000179f\U000017d2\U0000178f\U000017d2\U0000179a\U000017b8"sv, {6047}, {6}},
+     {L"\U00001b26\U00001b17\U00001b44\U00001b13"sv, {6950, 6935}, {1, 4}},
+     {L"\U00001b27\U00001b13\U00001b44\U00001b0b\U00001b0b\U00001b04"sv, {6951, 6931, 6923}, {1, 4, 6}},
+     {L"\U00001795\U000017d2\U000017af\U00001798"sv, {6037, 6040}, {3, 4}},
+     {L"\U000017a0\U000017d2\U000017ab\U00001791\U000017d0\U00001799"sv, {6048, 6033, 6041}, {3, 5, 6}},
+     {L"\U00001b05\U00001b44\U00001b33\U00001b03"sv, {6917}, {4}},
+     {L"\U00000cf1\U00000c95"sv, {3313, 3221}, {1, 2}},
+     {L"\U00000cf2\U00000cab"sv, {3314, 3243}, {1, 2}},
+     {L"\U00000cf1\U00000c95\U00000cbf"sv, {3313, 3221}, {1, 3}},
+     {L"\U00001cf5\U00000995"sv, {7413}, {2}},
+     {L"\U00001cf6\U000009aa"sv, {7414}, {2}},
+     {L"\U00001cf5\U0000200c\U00000995"sv, {7413, 2453}, {2, 3}},
+     {L"\U00001cf6\U0000200c\U000009aa"sv, {7414, 2474}, {2, 3}},
+     {L"\U00011a3a\U00011a0b"sv, {72250}, {4}}}};
 
 /// The data for UTF-8.
 ///
@@ -1804,859 +1806,859 @@ std::array<data<wchar_t>, 853> data_utf16 = {{
 /// since the size of the code units differ the breaks can contain different
 /// values.
 std::array<data<wchar_t>, 853> data_utf32 = {{
-     {L"\U0000000d\U0000000d", {13, 13}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000000d", {13, 776, 13}, {1, 2, 3}},
-     {L"\U0000000d\U0000000a", {13}, {2}},
-     {L"\U0000000d\U00000308\U0000000a", {13, 776, 10}, {1, 2, 3}},
-     {L"\U0000000d\U00000000", {13, 0}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000000", {13, 776, 0}, {1, 2, 3}},
-     {L"\U0000000d\U0000094d", {13, 2381}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000094d", {13, 776}, {1, 3}},
-     {L"\U0000000d\U00000300", {13, 768}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000300", {13, 776}, {1, 3}},
-     {L"\U0000000d\U0000200c", {13, 8204}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000200c", {13, 776}, {1, 3}},
-     {L"\U0000000d\U0000200d", {13, 8205}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000200d", {13, 776}, {1, 3}},
-     {L"\U0000000d\U0001f1e6", {13, 127462}, {1, 2}},
-     {L"\U0000000d\U00000308\U0001f1e6", {13, 776, 127462}, {1, 2, 3}},
-     {L"\U0000000d\U000006dd", {13, 1757}, {1, 2}},
-     {L"\U0000000d\U00000308\U000006dd", {13, 776, 1757}, {1, 2, 3}},
-     {L"\U0000000d\U00000903", {13, 2307}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000903", {13, 776}, {1, 3}},
-     {L"\U0000000d\U00001100", {13, 4352}, {1, 2}},
-     {L"\U0000000d\U00000308\U00001100", {13, 776, 4352}, {1, 2, 3}},
-     {L"\U0000000d\U00001160", {13, 4448}, {1, 2}},
-     {L"\U0000000d\U00000308\U00001160", {13, 776, 4448}, {1, 2, 3}},
-     {L"\U0000000d\U000011a8", {13, 4520}, {1, 2}},
-     {L"\U0000000d\U00000308\U000011a8", {13, 776, 4520}, {1, 2, 3}},
-     {L"\U0000000d\U0000ac00", {13, 44032}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000ac00", {13, 776, 44032}, {1, 2, 3}},
-     {L"\U0000000d\U0000ac01", {13, 44033}, {1, 2}},
-     {L"\U0000000d\U00000308\U0000ac01", {13, 776, 44033}, {1, 2, 3}},
-     {L"\U0000000d\U00001cf5", {13, 7413}, {1, 2}},
-     {L"\U0000000d\U00000308\U00001cf5", {13, 776, 7413}, {1, 2, 3}},
-     {L"\U0000000d\U00000915", {13, 2325}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000915", {13, 776, 2325}, {1, 2, 3}},
-     {L"\U0000000d\U000000a9", {13, 169}, {1, 2}},
-     {L"\U0000000d\U00000308\U000000a9", {13, 776, 169}, {1, 2, 3}},
-     {L"\U0000000d\U00000020", {13, 32}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000020", {13, 776, 32}, {1, 2, 3}},
-     {L"\U0000000d\U00000378", {13, 888}, {1, 2}},
-     {L"\U0000000d\U00000308\U00000378", {13, 776, 888}, {1, 2, 3}},
-     {L"\U0000000a\U0000000d", {10, 13}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000000d", {10, 776, 13}, {1, 2, 3}},
-     {L"\U0000000a\U0000000a", {10, 10}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000000a", {10, 776, 10}, {1, 2, 3}},
-     {L"\U0000000a\U00000000", {10, 0}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000000", {10, 776, 0}, {1, 2, 3}},
-     {L"\U0000000a\U0000094d", {10, 2381}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000094d", {10, 776}, {1, 3}},
-     {L"\U0000000a\U00000300", {10, 768}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000300", {10, 776}, {1, 3}},
-     {L"\U0000000a\U0000200c", {10, 8204}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000200c", {10, 776}, {1, 3}},
-     {L"\U0000000a\U0000200d", {10, 8205}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000200d", {10, 776}, {1, 3}},
-     {L"\U0000000a\U0001f1e6", {10, 127462}, {1, 2}},
-     {L"\U0000000a\U00000308\U0001f1e6", {10, 776, 127462}, {1, 2, 3}},
-     {L"\U0000000a\U000006dd", {10, 1757}, {1, 2}},
-     {L"\U0000000a\U00000308\U000006dd", {10, 776, 1757}, {1, 2, 3}},
-     {L"\U0000000a\U00000903", {10, 2307}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000903", {10, 776}, {1, 3}},
-     {L"\U0000000a\U00001100", {10, 4352}, {1, 2}},
-     {L"\U0000000a\U00000308\U00001100", {10, 776, 4352}, {1, 2, 3}},
-     {L"\U0000000a\U00001160", {10, 4448}, {1, 2}},
-     {L"\U0000000a\U00000308\U00001160", {10, 776, 4448}, {1, 2, 3}},
-     {L"\U0000000a\U000011a8", {10, 4520}, {1, 2}},
-     {L"\U0000000a\U00000308\U000011a8", {10, 776, 4520}, {1, 2, 3}},
-     {L"\U0000000a\U0000ac00", {10, 44032}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000ac00", {10, 776, 44032}, {1, 2, 3}},
-     {L"\U0000000a\U0000ac01", {10, 44033}, {1, 2}},
-     {L"\U0000000a\U00000308\U0000ac01", {10, 776, 44033}, {1, 2, 3}},
-     {L"\U0000000a\U00001cf5", {10, 7413}, {1, 2}},
-     {L"\U0000000a\U00000308\U00001cf5", {10, 776, 7413}, {1, 2, 3}},
-     {L"\U0000000a\U00000915", {10, 2325}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000915", {10, 776, 2325}, {1, 2, 3}},
-     {L"\U0000000a\U000000a9", {10, 169}, {1, 2}},
-     {L"\U0000000a\U00000308\U000000a9", {10, 776, 169}, {1, 2, 3}},
-     {L"\U0000000a\U00000020", {10, 32}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000020", {10, 776, 32}, {1, 2, 3}},
-     {L"\U0000000a\U00000378", {10, 888}, {1, 2}},
-     {L"\U0000000a\U00000308\U00000378", {10, 776, 888}, {1, 2, 3}},
-     {L"\U00000000\U0000000d", {0, 13}, {1, 2}},
-     {L"\U00000000\U00000308\U0000000d", {0, 776, 13}, {1, 2, 3}},
-     {L"\U00000000\U0000000a", {0, 10}, {1, 2}},
-     {L"\U00000000\U00000308\U0000000a", {0, 776, 10}, {1, 2, 3}},
-     {L"\U00000000\U00000000", {0, 0}, {1, 2}},
-     {L"\U00000000\U00000308\U00000000", {0, 776, 0}, {1, 2, 3}},
-     {L"\U00000000\U0000094d", {0, 2381}, {1, 2}},
-     {L"\U00000000\U00000308\U0000094d", {0, 776}, {1, 3}},
-     {L"\U00000000\U00000300", {0, 768}, {1, 2}},
-     {L"\U00000000\U00000308\U00000300", {0, 776}, {1, 3}},
-     {L"\U00000000\U0000200c", {0, 8204}, {1, 2}},
-     {L"\U00000000\U00000308\U0000200c", {0, 776}, {1, 3}},
-     {L"\U00000000\U0000200d", {0, 8205}, {1, 2}},
-     {L"\U00000000\U00000308\U0000200d", {0, 776}, {1, 3}},
-     {L"\U00000000\U0001f1e6", {0, 127462}, {1, 2}},
-     {L"\U00000000\U00000308\U0001f1e6", {0, 776, 127462}, {1, 2, 3}},
-     {L"\U00000000\U000006dd", {0, 1757}, {1, 2}},
-     {L"\U00000000\U00000308\U000006dd", {0, 776, 1757}, {1, 2, 3}},
-     {L"\U00000000\U00000903", {0, 2307}, {1, 2}},
-     {L"\U00000000\U00000308\U00000903", {0, 776}, {1, 3}},
-     {L"\U00000000\U00001100", {0, 4352}, {1, 2}},
-     {L"\U00000000\U00000308\U00001100", {0, 776, 4352}, {1, 2, 3}},
-     {L"\U00000000\U00001160", {0, 4448}, {1, 2}},
-     {L"\U00000000\U00000308\U00001160", {0, 776, 4448}, {1, 2, 3}},
-     {L"\U00000000\U000011a8", {0, 4520}, {1, 2}},
-     {L"\U00000000\U00000308\U000011a8", {0, 776, 4520}, {1, 2, 3}},
-     {L"\U00000000\U0000ac00", {0, 44032}, {1, 2}},
-     {L"\U00000000\U00000308\U0000ac00", {0, 776, 44032}, {1, 2, 3}},
-     {L"\U00000000\U0000ac01", {0, 44033}, {1, 2}},
-     {L"\U00000000\U00000308\U0000ac01", {0, 776, 44033}, {1, 2, 3}},
-     {L"\U00000000\U00001cf5", {0, 7413}, {1, 2}},
-     {L"\U00000000\U00000308\U00001cf5", {0, 776, 7413}, {1, 2, 3}},
-     {L"\U00000000\U00000915", {0, 2325}, {1, 2}},
-     {L"\U00000000\U00000308\U00000915", {0, 776, 2325}, {1, 2, 3}},
-     {L"\U00000000\U000000a9", {0, 169}, {1, 2}},
-     {L"\U00000000\U00000308\U000000a9", {0, 776, 169}, {1, 2, 3}},
-     {L"\U00000000\U00000020", {0, 32}, {1, 2}},
-     {L"\U00000000\U00000308\U00000020", {0, 776, 32}, {1, 2, 3}},
-     {L"\U00000000\U00000378", {0, 888}, {1, 2}},
-     {L"\U00000000\U00000308\U00000378", {0, 776, 888}, {1, 2, 3}},
-     {L"\U0000094d\U0000000d", {2381, 13}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000000d", {2381, 13}, {2, 3}},
-     {L"\U0000094d\U0000000a", {2381, 10}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000000a", {2381, 10}, {2, 3}},
-     {L"\U0000094d\U00000000", {2381, 0}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000000", {2381, 0}, {2, 3}},
-     {L"\U0000094d\U0000094d", {2381}, {2}},
-     {L"\U0000094d\U00000308\U0000094d", {2381}, {3}},
-     {L"\U0000094d\U00000300", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000300", {2381}, {3}},
-     {L"\U0000094d\U0000200c", {2381}, {2}},
-     {L"\U0000094d\U00000308\U0000200c", {2381}, {3}},
-     {L"\U0000094d\U0000200d", {2381}, {2}},
-     {L"\U0000094d\U00000308\U0000200d", {2381}, {3}},
-     {L"\U0000094d\U0001f1e6", {2381, 127462}, {1, 2}},
-     {L"\U0000094d\U00000308\U0001f1e6", {2381, 127462}, {2, 3}},
-     {L"\U0000094d\U000006dd", {2381, 1757}, {1, 2}},
-     {L"\U0000094d\U00000308\U000006dd", {2381, 1757}, {2, 3}},
-     {L"\U0000094d\U00000903", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000903", {2381}, {3}},
-     {L"\U0000094d\U00001100", {2381, 4352}, {1, 2}},
-     {L"\U0000094d\U00000308\U00001100", {2381, 4352}, {2, 3}},
-     {L"\U0000094d\U00001160", {2381, 4448}, {1, 2}},
-     {L"\U0000094d\U00000308\U00001160", {2381, 4448}, {2, 3}},
-     {L"\U0000094d\U000011a8", {2381, 4520}, {1, 2}},
-     {L"\U0000094d\U00000308\U000011a8", {2381, 4520}, {2, 3}},
-     {L"\U0000094d\U0000ac00", {2381, 44032}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000ac00", {2381, 44032}, {2, 3}},
-     {L"\U0000094d\U0000ac01", {2381, 44033}, {1, 2}},
-     {L"\U0000094d\U00000308\U0000ac01", {2381, 44033}, {2, 3}},
-     {L"\U0000094d\U00001cf5", {2381, 7413}, {1, 2}},
-     {L"\U0000094d\U00000308\U00001cf5", {2381, 7413}, {2, 3}},
-     {L"\U0000094d\U00000915", {2381}, {2}},
-     {L"\U0000094d\U00000308\U00000915", {2381}, {3}},
-     {L"\U0000094d\U000000a9", {2381, 169}, {1, 2}},
-     {L"\U0000094d\U00000308\U000000a9", {2381, 169}, {2, 3}},
-     {L"\U0000094d\U00000020", {2381, 32}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000020", {2381, 32}, {2, 3}},
-     {L"\U0000094d\U00000378", {2381, 888}, {1, 2}},
-     {L"\U0000094d\U00000308\U00000378", {2381, 888}, {2, 3}},
-     {L"\U00000300\U0000000d", {768, 13}, {1, 2}},
-     {L"\U00000300\U00000308\U0000000d", {768, 13}, {2, 3}},
-     {L"\U00000300\U0000000a", {768, 10}, {1, 2}},
-     {L"\U00000300\U00000308\U0000000a", {768, 10}, {2, 3}},
-     {L"\U00000300\U00000000", {768, 0}, {1, 2}},
-     {L"\U00000300\U00000308\U00000000", {768, 0}, {2, 3}},
-     {L"\U00000300\U0000094d", {768}, {2}},
-     {L"\U00000300\U00000308\U0000094d", {768}, {3}},
-     {L"\U00000300\U00000300", {768}, {2}},
-     {L"\U00000300\U00000308\U00000300", {768}, {3}},
-     {L"\U00000300\U0000200c", {768}, {2}},
-     {L"\U00000300\U00000308\U0000200c", {768}, {3}},
-     {L"\U00000300\U0000200d", {768}, {2}},
-     {L"\U00000300\U00000308\U0000200d", {768}, {3}},
-     {L"\U00000300\U0001f1e6", {768, 127462}, {1, 2}},
-     {L"\U00000300\U00000308\U0001f1e6", {768, 127462}, {2, 3}},
-     {L"\U00000300\U000006dd", {768, 1757}, {1, 2}},
-     {L"\U00000300\U00000308\U000006dd", {768, 1757}, {2, 3}},
-     {L"\U00000300\U00000903", {768}, {2}},
-     {L"\U00000300\U00000308\U00000903", {768}, {3}},
-     {L"\U00000300\U00001100", {768, 4352}, {1, 2}},
-     {L"\U00000300\U00000308\U00001100", {768, 4352}, {2, 3}},
-     {L"\U00000300\U00001160", {768, 4448}, {1, 2}},
-     {L"\U00000300\U00000308\U00001160", {768, 4448}, {2, 3}},
-     {L"\U00000300\U000011a8", {768, 4520}, {1, 2}},
-     {L"\U00000300\U00000308\U000011a8", {768, 4520}, {2, 3}},
-     {L"\U00000300\U0000ac00", {768, 44032}, {1, 2}},
-     {L"\U00000300\U00000308\U0000ac00", {768, 44032}, {2, 3}},
-     {L"\U00000300\U0000ac01", {768, 44033}, {1, 2}},
-     {L"\U00000300\U00000308\U0000ac01", {768, 44033}, {2, 3}},
-     {L"\U00000300\U00001cf5", {768, 7413}, {1, 2}},
-     {L"\U00000300\U00000308\U00001cf5", {768, 7413}, {2, 3}},
-     {L"\U00000300\U00000915", {768, 2325}, {1, 2}},
-     {L"\U00000300\U00000308\U00000915", {768, 2325}, {2, 3}},
-     {L"\U00000300\U000000a9", {768, 169}, {1, 2}},
-     {L"\U00000300\U00000308\U000000a9", {768, 169}, {2, 3}},
-     {L"\U00000300\U00000020", {768, 32}, {1, 2}},
-     {L"\U00000300\U00000308\U00000020", {768, 32}, {2, 3}},
-     {L"\U00000300\U00000378", {768, 888}, {1, 2}},
-     {L"\U00000300\U00000308\U00000378", {768, 888}, {2, 3}},
-     {L"\U0000200c\U0000000d", {8204, 13}, {1, 2}},
-     {L"\U0000200c\U00000308\U0000000d", {8204, 13}, {2, 3}},
-     {L"\U0000200c\U0000000a", {8204, 10}, {1, 2}},
-     {L"\U0000200c\U00000308\U0000000a", {8204, 10}, {2, 3}},
-     {L"\U0000200c\U00000000", {8204, 0}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000000", {8204, 0}, {2, 3}},
-     {L"\U0000200c\U0000094d", {8204}, {2}},
-     {L"\U0000200c\U00000308\U0000094d", {8204}, {3}},
-     {L"\U0000200c\U00000300", {8204}, {2}},
-     {L"\U0000200c\U00000308\U00000300", {8204}, {3}},
-     {L"\U0000200c\U0000200c", {8204}, {2}},
-     {L"\U0000200c\U00000308\U0000200c", {8204}, {3}},
-     {L"\U0000200c\U0000200d", {8204}, {2}},
-     {L"\U0000200c\U00000308\U0000200d", {8204}, {3}},
-     {L"\U0000200c\U0001f1e6", {8204, 127462}, {1, 2}},
-     {L"\U0000200c\U00000308\U0001f1e6", {8204, 127462}, {2, 3}},
-     {L"\U0000200c\U000006dd", {8204, 1757}, {1, 2}},
-     {L"\U0000200c\U00000308\U000006dd", {8204, 1757}, {2, 3}},
-     {L"\U0000200c\U00000903", {8204}, {2}},
-     {L"\U0000200c\U00000308\U00000903", {8204}, {3}},
-     {L"\U0000200c\U00001100", {8204, 4352}, {1, 2}},
-     {L"\U0000200c\U00000308\U00001100", {8204, 4352}, {2, 3}},
-     {L"\U0000200c\U00001160", {8204, 4448}, {1, 2}},
-     {L"\U0000200c\U00000308\U00001160", {8204, 4448}, {2, 3}},
-     {L"\U0000200c\U000011a8", {8204, 4520}, {1, 2}},
-     {L"\U0000200c\U00000308\U000011a8", {8204, 4520}, {2, 3}},
-     {L"\U0000200c\U0000ac00", {8204, 44032}, {1, 2}},
-     {L"\U0000200c\U00000308\U0000ac00", {8204, 44032}, {2, 3}},
-     {L"\U0000200c\U0000ac01", {8204, 44033}, {1, 2}},
-     {L"\U0000200c\U00000308\U0000ac01", {8204, 44033}, {2, 3}},
-     {L"\U0000200c\U00001cf5", {8204, 7413}, {1, 2}},
-     {L"\U0000200c\U00000308\U00001cf5", {8204, 7413}, {2, 3}},
-     {L"\U0000200c\U00000915", {8204, 2325}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000915", {8204, 2325}, {2, 3}},
-     {L"\U0000200c\U000000a9", {8204, 169}, {1, 2}},
-     {L"\U0000200c\U00000308\U000000a9", {8204, 169}, {2, 3}},
-     {L"\U0000200c\U00000020", {8204, 32}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000020", {8204, 32}, {2, 3}},
-     {L"\U0000200c\U00000378", {8204, 888}, {1, 2}},
-     {L"\U0000200c\U00000308\U00000378", {8204, 888}, {2, 3}},
-     {L"\U0000200d\U0000000d", {8205, 13}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000000d", {8205, 13}, {2, 3}},
-     {L"\U0000200d\U0000000a", {8205, 10}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000000a", {8205, 10}, {2, 3}},
-     {L"\U0000200d\U00000000", {8205, 0}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000000", {8205, 0}, {2, 3}},
-     {L"\U0000200d\U0000094d", {8205}, {2}},
-     {L"\U0000200d\U00000308\U0000094d", {8205}, {3}},
-     {L"\U0000200d\U00000300", {8205}, {2}},
-     {L"\U0000200d\U00000308\U00000300", {8205}, {3}},
-     {L"\U0000200d\U0000200c", {8205}, {2}},
-     {L"\U0000200d\U00000308\U0000200c", {8205}, {3}},
-     {L"\U0000200d\U0000200d", {8205}, {2}},
-     {L"\U0000200d\U00000308\U0000200d", {8205}, {3}},
-     {L"\U0000200d\U0001f1e6", {8205, 127462}, {1, 2}},
-     {L"\U0000200d\U00000308\U0001f1e6", {8205, 127462}, {2, 3}},
-     {L"\U0000200d\U000006dd", {8205, 1757}, {1, 2}},
-     {L"\U0000200d\U00000308\U000006dd", {8205, 1757}, {2, 3}},
-     {L"\U0000200d\U00000903", {8205}, {2}},
-     {L"\U0000200d\U00000308\U00000903", {8205}, {3}},
-     {L"\U0000200d\U00001100", {8205, 4352}, {1, 2}},
-     {L"\U0000200d\U00000308\U00001100", {8205, 4352}, {2, 3}},
-     {L"\U0000200d\U00001160", {8205, 4448}, {1, 2}},
-     {L"\U0000200d\U00000308\U00001160", {8205, 4448}, {2, 3}},
-     {L"\U0000200d\U000011a8", {8205, 4520}, {1, 2}},
-     {L"\U0000200d\U00000308\U000011a8", {8205, 4520}, {2, 3}},
-     {L"\U0000200d\U0000ac00", {8205, 44032}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000ac00", {8205, 44032}, {2, 3}},
-     {L"\U0000200d\U0000ac01", {8205, 44033}, {1, 2}},
-     {L"\U0000200d\U00000308\U0000ac01", {8205, 44033}, {2, 3}},
-     {L"\U0000200d\U00001cf5", {8205, 7413}, {1, 2}},
-     {L"\U0000200d\U00000308\U00001cf5", {8205, 7413}, {2, 3}},
-     {L"\U0000200d\U00000915", {8205, 2325}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000915", {8205, 2325}, {2, 3}},
-     {L"\U0000200d\U000000a9", {8205, 169}, {1, 2}},
-     {L"\U0000200d\U00000308\U000000a9", {8205, 169}, {2, 3}},
-     {L"\U0000200d\U00000020", {8205, 32}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000020", {8205, 32}, {2, 3}},
-     {L"\U0000200d\U00000378", {8205, 888}, {1, 2}},
-     {L"\U0000200d\U00000308\U00000378", {8205, 888}, {2, 3}},
-     {L"\U0001f1e6\U0000000d", {127462, 13}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U0000000d", {127462, 13}, {2, 3}},
-     {L"\U0001f1e6\U0000000a", {127462, 10}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U0000000a", {127462, 10}, {2, 3}},
-     {L"\U0001f1e6\U00000000", {127462, 0}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U00000000", {127462, 0}, {2, 3}},
-     {L"\U0001f1e6\U0000094d", {127462}, {2}},
-     {L"\U0001f1e6\U00000308\U0000094d", {127462}, {3}},
-     {L"\U0001f1e6\U00000300", {127462}, {2}},
-     {L"\U0001f1e6\U00000308\U00000300", {127462}, {3}},
-     {L"\U0001f1e6\U0000200c", {127462}, {2}},
-     {L"\U0001f1e6\U00000308\U0000200c", {127462}, {3}},
-     {L"\U0001f1e6\U0000200d", {127462}, {2}},
-     {L"\U0001f1e6\U00000308\U0000200d", {127462}, {3}},
-     {L"\U0001f1e6\U0001f1e6", {127462}, {2}},
-     {L"\U0001f1e6\U00000308\U0001f1e6", {127462, 127462}, {2, 3}},
-     {L"\U0001f1e6\U000006dd", {127462, 1757}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U000006dd", {127462, 1757}, {2, 3}},
-     {L"\U0001f1e6\U00000903", {127462}, {2}},
-     {L"\U0001f1e6\U00000308\U00000903", {127462}, {3}},
-     {L"\U0001f1e6\U00001100", {127462, 4352}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U00001100", {127462, 4352}, {2, 3}},
-     {L"\U0001f1e6\U00001160", {127462, 4448}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U00001160", {127462, 4448}, {2, 3}},
-     {L"\U0001f1e6\U000011a8", {127462, 4520}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U000011a8", {127462, 4520}, {2, 3}},
-     {L"\U0001f1e6\U0000ac00", {127462, 44032}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U0000ac00", {127462, 44032}, {2, 3}},
-     {L"\U0001f1e6\U0000ac01", {127462, 44033}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U0000ac01", {127462, 44033}, {2, 3}},
-     {L"\U0001f1e6\U00001cf5", {127462, 7413}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U00001cf5", {127462, 7413}, {2, 3}},
-     {L"\U0001f1e6\U00000915", {127462, 2325}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U00000915", {127462, 2325}, {2, 3}},
-     {L"\U0001f1e6\U000000a9", {127462, 169}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U000000a9", {127462, 169}, {2, 3}},
-     {L"\U0001f1e6\U00000020", {127462, 32}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U00000020", {127462, 32}, {2, 3}},
-     {L"\U0001f1e6\U00000378", {127462, 888}, {1, 2}},
-     {L"\U0001f1e6\U00000308\U00000378", {127462, 888}, {2, 3}},
-     {L"\U000006dd\U0000000d", {1757, 13}, {1, 2}},
-     {L"\U000006dd\U00000308\U0000000d", {1757, 13}, {2, 3}},
-     {L"\U000006dd\U0000000a", {1757, 10}, {1, 2}},
-     {L"\U000006dd\U00000308\U0000000a", {1757, 10}, {2, 3}},
-     {L"\U000006dd\U00000000", {1757, 0}, {1, 2}},
-     {L"\U000006dd\U00000308\U00000000", {1757, 0}, {2, 3}},
-     {L"\U000006dd\U0000094d", {1757}, {2}},
-     {L"\U000006dd\U00000308\U0000094d", {1757}, {3}},
-     {L"\U000006dd\U00000300", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00000300", {1757}, {3}},
-     {L"\U000006dd\U0000200c", {1757}, {2}},
-     {L"\U000006dd\U00000308\U0000200c", {1757}, {3}},
-     {L"\U000006dd\U0000200d", {1757}, {2}},
-     {L"\U000006dd\U00000308\U0000200d", {1757}, {3}},
-     {L"\U000006dd\U0001f1e6", {1757}, {2}},
-     {L"\U000006dd\U00000308\U0001f1e6", {1757, 127462}, {2, 3}},
-     {L"\U000006dd\U000006dd", {1757}, {2}},
-     {L"\U000006dd\U00000308\U000006dd", {1757, 1757}, {2, 3}},
-     {L"\U000006dd\U00000903", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00000903", {1757}, {3}},
-     {L"\U000006dd\U00001100", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00001100", {1757, 4352}, {2, 3}},
-     {L"\U000006dd\U00001160", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00001160", {1757, 4448}, {2, 3}},
-     {L"\U000006dd\U000011a8", {1757}, {2}},
-     {L"\U000006dd\U00000308\U000011a8", {1757, 4520}, {2, 3}},
-     {L"\U000006dd\U0000ac00", {1757}, {2}},
-     {L"\U000006dd\U00000308\U0000ac00", {1757, 44032}, {2, 3}},
-     {L"\U000006dd\U0000ac01", {1757}, {2}},
-     {L"\U000006dd\U00000308\U0000ac01", {1757, 44033}, {2, 3}},
-     {L"\U000006dd\U00001cf5", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00001cf5", {1757, 7413}, {2, 3}},
-     {L"\U000006dd\U00000915", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00000915", {1757, 2325}, {2, 3}},
-     {L"\U000006dd\U000000a9", {1757}, {2}},
-     {L"\U000006dd\U00000308\U000000a9", {1757, 169}, {2, 3}},
-     {L"\U000006dd\U00000020", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00000020", {1757, 32}, {2, 3}},
-     {L"\U000006dd\U00000378", {1757}, {2}},
-     {L"\U000006dd\U00000308\U00000378", {1757, 888}, {2, 3}},
-     {L"\U00000903\U0000000d", {2307, 13}, {1, 2}},
-     {L"\U00000903\U00000308\U0000000d", {2307, 13}, {2, 3}},
-     {L"\U00000903\U0000000a", {2307, 10}, {1, 2}},
-     {L"\U00000903\U00000308\U0000000a", {2307, 10}, {2, 3}},
-     {L"\U00000903\U00000000", {2307, 0}, {1, 2}},
-     {L"\U00000903\U00000308\U00000000", {2307, 0}, {2, 3}},
-     {L"\U00000903\U0000094d", {2307}, {2}},
-     {L"\U00000903\U00000308\U0000094d", {2307}, {3}},
-     {L"\U00000903\U00000300", {2307}, {2}},
-     {L"\U00000903\U00000308\U00000300", {2307}, {3}},
-     {L"\U00000903\U0000200c", {2307}, {2}},
-     {L"\U00000903\U00000308\U0000200c", {2307}, {3}},
-     {L"\U00000903\U0000200d", {2307}, {2}},
-     {L"\U00000903\U00000308\U0000200d", {2307}, {3}},
-     {L"\U00000903\U0001f1e6", {2307, 127462}, {1, 2}},
-     {L"\U00000903\U00000308\U0001f1e6", {2307, 127462}, {2, 3}},
-     {L"\U00000903\U000006dd", {2307, 1757}, {1, 2}},
-     {L"\U00000903\U00000308\U000006dd", {2307, 1757}, {2, 3}},
-     {L"\U00000903\U00000903", {2307}, {2}},
-     {L"\U00000903\U00000308\U00000903", {2307}, {3}},
-     {L"\U00000903\U00001100", {2307, 4352}, {1, 2}},
-     {L"\U00000903\U00000308\U00001100", {2307, 4352}, {2, 3}},
-     {L"\U00000903\U00001160", {2307, 4448}, {1, 2}},
-     {L"\U00000903\U00000308\U00001160", {2307, 4448}, {2, 3}},
-     {L"\U00000903\U000011a8", {2307, 4520}, {1, 2}},
-     {L"\U00000903\U00000308\U000011a8", {2307, 4520}, {2, 3}},
-     {L"\U00000903\U0000ac00", {2307, 44032}, {1, 2}},
-     {L"\U00000903\U00000308\U0000ac00", {2307, 44032}, {2, 3}},
-     {L"\U00000903\U0000ac01", {2307, 44033}, {1, 2}},
-     {L"\U00000903\U00000308\U0000ac01", {2307, 44033}, {2, 3}},
-     {L"\U00000903\U00001cf5", {2307, 7413}, {1, 2}},
-     {L"\U00000903\U00000308\U00001cf5", {2307, 7413}, {2, 3}},
-     {L"\U00000903\U00000915", {2307, 2325}, {1, 2}},
-     {L"\U00000903\U00000308\U00000915", {2307, 2325}, {2, 3}},
-     {L"\U00000903\U000000a9", {2307, 169}, {1, 2}},
-     {L"\U00000903\U00000308\U000000a9", {2307, 169}, {2, 3}},
-     {L"\U00000903\U00000020", {2307, 32}, {1, 2}},
-     {L"\U00000903\U00000308\U00000020", {2307, 32}, {2, 3}},
-     {L"\U00000903\U00000378", {2307, 888}, {1, 2}},
-     {L"\U00000903\U00000308\U00000378", {2307, 888}, {2, 3}},
-     {L"\U00001100\U0000000d", {4352, 13}, {1, 2}},
-     {L"\U00001100\U00000308\U0000000d", {4352, 13}, {2, 3}},
-     {L"\U00001100\U0000000a", {4352, 10}, {1, 2}},
-     {L"\U00001100\U00000308\U0000000a", {4352, 10}, {2, 3}},
-     {L"\U00001100\U00000000", {4352, 0}, {1, 2}},
-     {L"\U00001100\U00000308\U00000000", {4352, 0}, {2, 3}},
-     {L"\U00001100\U0000094d", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000094d", {4352}, {3}},
-     {L"\U00001100\U00000300", {4352}, {2}},
-     {L"\U00001100\U00000308\U00000300", {4352}, {3}},
-     {L"\U00001100\U0000200c", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000200c", {4352}, {3}},
-     {L"\U00001100\U0000200d", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000200d", {4352}, {3}},
-     {L"\U00001100\U0001f1e6", {4352, 127462}, {1, 2}},
-     {L"\U00001100\U00000308\U0001f1e6", {4352, 127462}, {2, 3}},
-     {L"\U00001100\U000006dd", {4352, 1757}, {1, 2}},
-     {L"\U00001100\U00000308\U000006dd", {4352, 1757}, {2, 3}},
-     {L"\U00001100\U00000903", {4352}, {2}},
-     {L"\U00001100\U00000308\U00000903", {4352}, {3}},
-     {L"\U00001100\U00001100", {4352}, {2}},
-     {L"\U00001100\U00000308\U00001100", {4352, 4352}, {2, 3}},
-     {L"\U00001100\U00001160", {4352}, {2}},
-     {L"\U00001100\U00000308\U00001160", {4352, 4448}, {2, 3}},
-     {L"\U00001100\U000011a8", {4352, 4520}, {1, 2}},
-     {L"\U00001100\U00000308\U000011a8", {4352, 4520}, {2, 3}},
-     {L"\U00001100\U0000ac00", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000ac00", {4352, 44032}, {2, 3}},
-     {L"\U00001100\U0000ac01", {4352}, {2}},
-     {L"\U00001100\U00000308\U0000ac01", {4352, 44033}, {2, 3}},
-     {L"\U00001100\U00001cf5", {4352, 7413}, {1, 2}},
-     {L"\U00001100\U00000308\U00001cf5", {4352, 7413}, {2, 3}},
-     {L"\U00001100\U00000915", {4352, 2325}, {1, 2}},
-     {L"\U00001100\U00000308\U00000915", {4352, 2325}, {2, 3}},
-     {L"\U00001100\U000000a9", {4352, 169}, {1, 2}},
-     {L"\U00001100\U00000308\U000000a9", {4352, 169}, {2, 3}},
-     {L"\U00001100\U00000020", {4352, 32}, {1, 2}},
-     {L"\U00001100\U00000308\U00000020", {4352, 32}, {2, 3}},
-     {L"\U00001100\U00000378", {4352, 888}, {1, 2}},
-     {L"\U00001100\U00000308\U00000378", {4352, 888}, {2, 3}},
-     {L"\U00001160\U0000000d", {4448, 13}, {1, 2}},
-     {L"\U00001160\U00000308\U0000000d", {4448, 13}, {2, 3}},
-     {L"\U00001160\U0000000a", {4448, 10}, {1, 2}},
-     {L"\U00001160\U00000308\U0000000a", {4448, 10}, {2, 3}},
-     {L"\U00001160\U00000000", {4448, 0}, {1, 2}},
-     {L"\U00001160\U00000308\U00000000", {4448, 0}, {2, 3}},
-     {L"\U00001160\U0000094d", {4448}, {2}},
-     {L"\U00001160\U00000308\U0000094d", {4448}, {3}},
-     {L"\U00001160\U00000300", {4448}, {2}},
-     {L"\U00001160\U00000308\U00000300", {4448}, {3}},
-     {L"\U00001160\U0000200c", {4448}, {2}},
-     {L"\U00001160\U00000308\U0000200c", {4448}, {3}},
-     {L"\U00001160\U0000200d", {4448}, {2}},
-     {L"\U00001160\U00000308\U0000200d", {4448}, {3}},
-     {L"\U00001160\U0001f1e6", {4448, 127462}, {1, 2}},
-     {L"\U00001160\U00000308\U0001f1e6", {4448, 127462}, {2, 3}},
-     {L"\U00001160\U000006dd", {4448, 1757}, {1, 2}},
-     {L"\U00001160\U00000308\U000006dd", {4448, 1757}, {2, 3}},
-     {L"\U00001160\U00000903", {4448}, {2}},
-     {L"\U00001160\U00000308\U00000903", {4448}, {3}},
-     {L"\U00001160\U00001100", {4448, 4352}, {1, 2}},
-     {L"\U00001160\U00000308\U00001100", {4448, 4352}, {2, 3}},
-     {L"\U00001160\U00001160", {4448}, {2}},
-     {L"\U00001160\U00000308\U00001160", {4448, 4448}, {2, 3}},
-     {L"\U00001160\U000011a8", {4448}, {2}},
-     {L"\U00001160\U00000308\U000011a8", {4448, 4520}, {2, 3}},
-     {L"\U00001160\U0000ac00", {4448, 44032}, {1, 2}},
-     {L"\U00001160\U00000308\U0000ac00", {4448, 44032}, {2, 3}},
-     {L"\U00001160\U0000ac01", {4448, 44033}, {1, 2}},
-     {L"\U00001160\U00000308\U0000ac01", {4448, 44033}, {2, 3}},
-     {L"\U00001160\U00001cf5", {4448, 7413}, {1, 2}},
-     {L"\U00001160\U00000308\U00001cf5", {4448, 7413}, {2, 3}},
-     {L"\U00001160\U00000915", {4448, 2325}, {1, 2}},
-     {L"\U00001160\U00000308\U00000915", {4448, 2325}, {2, 3}},
-     {L"\U00001160\U000000a9", {4448, 169}, {1, 2}},
-     {L"\U00001160\U00000308\U000000a9", {4448, 169}, {2, 3}},
-     {L"\U00001160\U00000020", {4448, 32}, {1, 2}},
-     {L"\U00001160\U00000308\U00000020", {4448, 32}, {2, 3}},
-     {L"\U00001160\U00000378", {4448, 888}, {1, 2}},
-     {L"\U00001160\U00000308\U00000378", {4448, 888}, {2, 3}},
-     {L"\U000011a8\U0000000d", {4520, 13}, {1, 2}},
-     {L"\U000011a8\U00000308\U0000000d", {4520, 13}, {2, 3}},
-     {L"\U000011a8\U0000000a", {4520, 10}, {1, 2}},
-     {L"\U000011a8\U00000308\U0000000a", {4520, 10}, {2, 3}},
-     {L"\U000011a8\U00000000", {4520, 0}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000000", {4520, 0}, {2, 3}},
-     {L"\U000011a8\U0000094d", {4520}, {2}},
-     {L"\U000011a8\U00000308\U0000094d", {4520}, {3}},
-     {L"\U000011a8\U00000300", {4520}, {2}},
-     {L"\U000011a8\U00000308\U00000300", {4520}, {3}},
-     {L"\U000011a8\U0000200c", {4520}, {2}},
-     {L"\U000011a8\U00000308\U0000200c", {4520}, {3}},
-     {L"\U000011a8\U0000200d", {4520}, {2}},
-     {L"\U000011a8\U00000308\U0000200d", {4520}, {3}},
-     {L"\U000011a8\U0001f1e6", {4520, 127462}, {1, 2}},
-     {L"\U000011a8\U00000308\U0001f1e6", {4520, 127462}, {2, 3}},
-     {L"\U000011a8\U000006dd", {4520, 1757}, {1, 2}},
-     {L"\U000011a8\U00000308\U000006dd", {4520, 1757}, {2, 3}},
-     {L"\U000011a8\U00000903", {4520}, {2}},
-     {L"\U000011a8\U00000308\U00000903", {4520}, {3}},
-     {L"\U000011a8\U00001100", {4520, 4352}, {1, 2}},
-     {L"\U000011a8\U00000308\U00001100", {4520, 4352}, {2, 3}},
-     {L"\U000011a8\U00001160", {4520, 4448}, {1, 2}},
-     {L"\U000011a8\U00000308\U00001160", {4520, 4448}, {2, 3}},
-     {L"\U000011a8\U000011a8", {4520}, {2}},
-     {L"\U000011a8\U00000308\U000011a8", {4520, 4520}, {2, 3}},
-     {L"\U000011a8\U0000ac00", {4520, 44032}, {1, 2}},
-     {L"\U000011a8\U00000308\U0000ac00", {4520, 44032}, {2, 3}},
-     {L"\U000011a8\U0000ac01", {4520, 44033}, {1, 2}},
-     {L"\U000011a8\U00000308\U0000ac01", {4520, 44033}, {2, 3}},
-     {L"\U000011a8\U00001cf5", {4520, 7413}, {1, 2}},
-     {L"\U000011a8\U00000308\U00001cf5", {4520, 7413}, {2, 3}},
-     {L"\U000011a8\U00000915", {4520, 2325}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000915", {4520, 2325}, {2, 3}},
-     {L"\U000011a8\U000000a9", {4520, 169}, {1, 2}},
-     {L"\U000011a8\U00000308\U000000a9", {4520, 169}, {2, 3}},
-     {L"\U000011a8\U00000020", {4520, 32}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000020", {4520, 32}, {2, 3}},
-     {L"\U000011a8\U00000378", {4520, 888}, {1, 2}},
-     {L"\U000011a8\U00000308\U00000378", {4520, 888}, {2, 3}},
-     {L"\U0000ac00\U0000000d", {44032, 13}, {1, 2}},
-     {L"\U0000ac00\U00000308\U0000000d", {44032, 13}, {2, 3}},
-     {L"\U0000ac00\U0000000a", {44032, 10}, {1, 2}},
-     {L"\U0000ac00\U00000308\U0000000a", {44032, 10}, {2, 3}},
-     {L"\U0000ac00\U00000000", {44032, 0}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000000", {44032, 0}, {2, 3}},
-     {L"\U0000ac00\U0000094d", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U0000094d", {44032}, {3}},
-     {L"\U0000ac00\U00000300", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00000300", {44032}, {3}},
-     {L"\U0000ac00\U0000200c", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U0000200c", {44032}, {3}},
-     {L"\U0000ac00\U0000200d", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U0000200d", {44032}, {3}},
-     {L"\U0000ac00\U0001f1e6", {44032, 127462}, {1, 2}},
-     {L"\U0000ac00\U00000308\U0001f1e6", {44032, 127462}, {2, 3}},
-     {L"\U0000ac00\U000006dd", {44032, 1757}, {1, 2}},
-     {L"\U0000ac00\U00000308\U000006dd", {44032, 1757}, {2, 3}},
-     {L"\U0000ac00\U00000903", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00000903", {44032}, {3}},
-     {L"\U0000ac00\U00001100", {44032, 4352}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00001100", {44032, 4352}, {2, 3}},
-     {L"\U0000ac00\U00001160", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U00001160", {44032, 4448}, {2, 3}},
-     {L"\U0000ac00\U000011a8", {44032}, {2}},
-     {L"\U0000ac00\U00000308\U000011a8", {44032, 4520}, {2, 3}},
-     {L"\U0000ac00\U0000ac00", {44032, 44032}, {1, 2}},
-     {L"\U0000ac00\U00000308\U0000ac00", {44032, 44032}, {2, 3}},
-     {L"\U0000ac00\U0000ac01", {44032, 44033}, {1, 2}},
-     {L"\U0000ac00\U00000308\U0000ac01", {44032, 44033}, {2, 3}},
-     {L"\U0000ac00\U00001cf5", {44032, 7413}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00001cf5", {44032, 7413}, {2, 3}},
-     {L"\U0000ac00\U00000915", {44032, 2325}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000915", {44032, 2325}, {2, 3}},
-     {L"\U0000ac00\U000000a9", {44032, 169}, {1, 2}},
-     {L"\U0000ac00\U00000308\U000000a9", {44032, 169}, {2, 3}},
-     {L"\U0000ac00\U00000020", {44032, 32}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000020", {44032, 32}, {2, 3}},
-     {L"\U0000ac00\U00000378", {44032, 888}, {1, 2}},
-     {L"\U0000ac00\U00000308\U00000378", {44032, 888}, {2, 3}},
-     {L"\U0000ac01\U0000000d", {44033, 13}, {1, 2}},
-     {L"\U0000ac01\U00000308\U0000000d", {44033, 13}, {2, 3}},
-     {L"\U0000ac01\U0000000a", {44033, 10}, {1, 2}},
-     {L"\U0000ac01\U00000308\U0000000a", {44033, 10}, {2, 3}},
-     {L"\U0000ac01\U00000000", {44033, 0}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000000", {44033, 0}, {2, 3}},
-     {L"\U0000ac01\U0000094d", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U0000094d", {44033}, {3}},
-     {L"\U0000ac01\U00000300", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U00000300", {44033}, {3}},
-     {L"\U0000ac01\U0000200c", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U0000200c", {44033}, {3}},
-     {L"\U0000ac01\U0000200d", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U0000200d", {44033}, {3}},
-     {L"\U0000ac01\U0001f1e6", {44033, 127462}, {1, 2}},
-     {L"\U0000ac01\U00000308\U0001f1e6", {44033, 127462}, {2, 3}},
-     {L"\U0000ac01\U000006dd", {44033, 1757}, {1, 2}},
-     {L"\U0000ac01\U00000308\U000006dd", {44033, 1757}, {2, 3}},
-     {L"\U0000ac01\U00000903", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U00000903", {44033}, {3}},
-     {L"\U0000ac01\U00001100", {44033, 4352}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00001100", {44033, 4352}, {2, 3}},
-     {L"\U0000ac01\U00001160", {44033, 4448}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00001160", {44033, 4448}, {2, 3}},
-     {L"\U0000ac01\U000011a8", {44033}, {2}},
-     {L"\U0000ac01\U00000308\U000011a8", {44033, 4520}, {2, 3}},
-     {L"\U0000ac01\U0000ac00", {44033, 44032}, {1, 2}},
-     {L"\U0000ac01\U00000308\U0000ac00", {44033, 44032}, {2, 3}},
-     {L"\U0000ac01\U0000ac01", {44033, 44033}, {1, 2}},
-     {L"\U0000ac01\U00000308\U0000ac01", {44033, 44033}, {2, 3}},
-     {L"\U0000ac01\U00001cf5", {44033, 7413}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00001cf5", {44033, 7413}, {2, 3}},
-     {L"\U0000ac01\U00000915", {44033, 2325}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000915", {44033, 2325}, {2, 3}},
-     {L"\U0000ac01\U000000a9", {44033, 169}, {1, 2}},
-     {L"\U0000ac01\U00000308\U000000a9", {44033, 169}, {2, 3}},
-     {L"\U0000ac01\U00000020", {44033, 32}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000020", {44033, 32}, {2, 3}},
-     {L"\U0000ac01\U00000378", {44033, 888}, {1, 2}},
-     {L"\U0000ac01\U00000308\U00000378", {44033, 888}, {2, 3}},
-     {L"\U00001cf5\U0000000d", {7413, 13}, {1, 2}},
-     {L"\U00001cf5\U00000308\U0000000d", {7413, 13}, {2, 3}},
-     {L"\U00001cf5\U0000000a", {7413, 10}, {1, 2}},
-     {L"\U00001cf5\U00000308\U0000000a", {7413, 10}, {2, 3}},
-     {L"\U00001cf5\U00000000", {7413, 0}, {1, 2}},
-     {L"\U00001cf5\U00000308\U00000000", {7413, 0}, {2, 3}},
-     {L"\U00001cf5\U0000094d", {7413}, {2}},
-     {L"\U00001cf5\U00000308\U0000094d", {7413}, {3}},
-     {L"\U00001cf5\U00000300", {7413}, {2}},
-     {L"\U00001cf5\U00000308\U00000300", {7413}, {3}},
-     {L"\U00001cf5\U0000200c", {7413}, {2}},
-     {L"\U00001cf5\U00000308\U0000200c", {7413}, {3}},
-     {L"\U00001cf5\U0000200d", {7413}, {2}},
-     {L"\U00001cf5\U00000308\U0000200d", {7413}, {3}},
-     {L"\U00001cf5\U0001f1e6", {7413, 127462}, {1, 2}},
-     {L"\U00001cf5\U00000308\U0001f1e6", {7413, 127462}, {2, 3}},
-     {L"\U00001cf5\U000006dd", {7413, 1757}, {1, 2}},
-     {L"\U00001cf5\U00000308\U000006dd", {7413, 1757}, {2, 3}},
-     {L"\U00001cf5\U00000903", {7413}, {2}},
-     {L"\U00001cf5\U00000308\U00000903", {7413}, {3}},
-     {L"\U00001cf5\U00001100", {7413, 4352}, {1, 2}},
-     {L"\U00001cf5\U00000308\U00001100", {7413, 4352}, {2, 3}},
-     {L"\U00001cf5\U00001160", {7413, 4448}, {1, 2}},
-     {L"\U00001cf5\U00000308\U00001160", {7413, 4448}, {2, 3}},
-     {L"\U00001cf5\U000011a8", {7413, 4520}, {1, 2}},
-     {L"\U00001cf5\U00000308\U000011a8", {7413, 4520}, {2, 3}},
-     {L"\U00001cf5\U0000ac00", {7413, 44032}, {1, 2}},
-     {L"\U00001cf5\U00000308\U0000ac00", {7413, 44032}, {2, 3}},
-     {L"\U00001cf5\U0000ac01", {7413, 44033}, {1, 2}},
-     {L"\U00001cf5\U00000308\U0000ac01", {7413, 44033}, {2, 3}},
-     {L"\U00001cf5\U00001cf5", {7413, 7413}, {1, 2}},
-     {L"\U00001cf5\U00000308\U00001cf5", {7413, 7413}, {2, 3}},
-     {L"\U00001cf5\U00000915", {7413}, {2}},
-     {L"\U00001cf5\U00000308\U00000915", {7413}, {3}},
-     {L"\U00001cf5\U000000a9", {7413, 169}, {1, 2}},
-     {L"\U00001cf5\U00000308\U000000a9", {7413, 169}, {2, 3}},
-     {L"\U00001cf5\U00000020", {7413, 32}, {1, 2}},
-     {L"\U00001cf5\U00000308\U00000020", {7413, 32}, {2, 3}},
-     {L"\U00001cf5\U00000378", {7413, 888}, {1, 2}},
-     {L"\U00001cf5\U00000308\U00000378", {7413, 888}, {2, 3}},
-     {L"\U00000915\U0000000d", {2325, 13}, {1, 2}},
-     {L"\U00000915\U00000308\U0000000d", {2325, 13}, {2, 3}},
-     {L"\U00000915\U0000000a", {2325, 10}, {1, 2}},
-     {L"\U00000915\U00000308\U0000000a", {2325, 10}, {2, 3}},
-     {L"\U00000915\U00000000", {2325, 0}, {1, 2}},
-     {L"\U00000915\U00000308\U00000000", {2325, 0}, {2, 3}},
-     {L"\U00000915\U0000094d", {2325}, {2}},
-     {L"\U00000915\U00000308\U0000094d", {2325}, {3}},
-     {L"\U00000915\U00000300", {2325}, {2}},
-     {L"\U00000915\U00000308\U00000300", {2325}, {3}},
-     {L"\U00000915\U0000200c", {2325}, {2}},
-     {L"\U00000915\U00000308\U0000200c", {2325}, {3}},
-     {L"\U00000915\U0000200d", {2325}, {2}},
-     {L"\U00000915\U00000308\U0000200d", {2325}, {3}},
-     {L"\U00000915\U0001f1e6", {2325, 127462}, {1, 2}},
-     {L"\U00000915\U00000308\U0001f1e6", {2325, 127462}, {2, 3}},
-     {L"\U00000915\U000006dd", {2325, 1757}, {1, 2}},
-     {L"\U00000915\U00000308\U000006dd", {2325, 1757}, {2, 3}},
-     {L"\U00000915\U00000903", {2325}, {2}},
-     {L"\U00000915\U00000308\U00000903", {2325}, {3}},
-     {L"\U00000915\U00001100", {2325, 4352}, {1, 2}},
-     {L"\U00000915\U00000308\U00001100", {2325, 4352}, {2, 3}},
-     {L"\U00000915\U00001160", {2325, 4448}, {1, 2}},
-     {L"\U00000915\U00000308\U00001160", {2325, 4448}, {2, 3}},
-     {L"\U00000915\U000011a8", {2325, 4520}, {1, 2}},
-     {L"\U00000915\U00000308\U000011a8", {2325, 4520}, {2, 3}},
-     {L"\U00000915\U0000ac00", {2325, 44032}, {1, 2}},
-     {L"\U00000915\U00000308\U0000ac00", {2325, 44032}, {2, 3}},
-     {L"\U00000915\U0000ac01", {2325, 44033}, {1, 2}},
-     {L"\U00000915\U00000308\U0000ac01", {2325, 44033}, {2, 3}},
-     {L"\U00000915\U00001cf5", {2325, 7413}, {1, 2}},
-     {L"\U00000915\U00000308\U00001cf5", {2325, 7413}, {2, 3}},
-     {L"\U00000915\U00000915", {2325, 2325}, {1, 2}},
-     {L"\U00000915\U00000308\U00000915", {2325, 2325}, {2, 3}},
-     {L"\U00000915\U000000a9", {2325, 169}, {1, 2}},
-     {L"\U00000915\U00000308\U000000a9", {2325, 169}, {2, 3}},
-     {L"\U00000915\U00000020", {2325, 32}, {1, 2}},
-     {L"\U00000915\U00000308\U00000020", {2325, 32}, {2, 3}},
-     {L"\U00000915\U00000378", {2325, 888}, {1, 2}},
-     {L"\U00000915\U00000308\U00000378", {2325, 888}, {2, 3}},
-     {L"\U000000a9\U0000000d", {169, 13}, {1, 2}},
-     {L"\U000000a9\U00000308\U0000000d", {169, 13}, {2, 3}},
-     {L"\U000000a9\U0000000a", {169, 10}, {1, 2}},
-     {L"\U000000a9\U00000308\U0000000a", {169, 10}, {2, 3}},
-     {L"\U000000a9\U00000000", {169, 0}, {1, 2}},
-     {L"\U000000a9\U00000308\U00000000", {169, 0}, {2, 3}},
-     {L"\U000000a9\U0000094d", {169}, {2}},
-     {L"\U000000a9\U00000308\U0000094d", {169}, {3}},
-     {L"\U000000a9\U00000300", {169}, {2}},
-     {L"\U000000a9\U00000308\U00000300", {169}, {3}},
-     {L"\U000000a9\U0000200c", {169}, {2}},
-     {L"\U000000a9\U00000308\U0000200c", {169}, {3}},
-     {L"\U000000a9\U0000200d", {169}, {2}},
-     {L"\U000000a9\U00000308\U0000200d", {169}, {3}},
-     {L"\U000000a9\U0001f1e6", {169, 127462}, {1, 2}},
-     {L"\U000000a9\U00000308\U0001f1e6", {169, 127462}, {2, 3}},
-     {L"\U000000a9\U000006dd", {169, 1757}, {1, 2}},
-     {L"\U000000a9\U00000308\U000006dd", {169, 1757}, {2, 3}},
-     {L"\U000000a9\U00000903", {169}, {2}},
-     {L"\U000000a9\U00000308\U00000903", {169}, {3}},
-     {L"\U000000a9\U00001100", {169, 4352}, {1, 2}},
-     {L"\U000000a9\U00000308\U00001100", {169, 4352}, {2, 3}},
-     {L"\U000000a9\U00001160", {169, 4448}, {1, 2}},
-     {L"\U000000a9\U00000308\U00001160", {169, 4448}, {2, 3}},
-     {L"\U000000a9\U000011a8", {169, 4520}, {1, 2}},
-     {L"\U000000a9\U00000308\U000011a8", {169, 4520}, {2, 3}},
-     {L"\U000000a9\U0000ac00", {169, 44032}, {1, 2}},
-     {L"\U000000a9\U00000308\U0000ac00", {169, 44032}, {2, 3}},
-     {L"\U000000a9\U0000ac01", {169, 44033}, {1, 2}},
-     {L"\U000000a9\U00000308\U0000ac01", {169, 44033}, {2, 3}},
-     {L"\U000000a9\U00001cf5", {169, 7413}, {1, 2}},
-     {L"\U000000a9\U00000308\U00001cf5", {169, 7413}, {2, 3}},
-     {L"\U000000a9\U00000915", {169, 2325}, {1, 2}},
-     {L"\U000000a9\U00000308\U00000915", {169, 2325}, {2, 3}},
-     {L"\U000000a9\U000000a9", {169, 169}, {1, 2}},
-     {L"\U000000a9\U00000308\U000000a9", {169, 169}, {2, 3}},
-     {L"\U000000a9\U00000020", {169, 32}, {1, 2}},
-     {L"\U000000a9\U00000308\U00000020", {169, 32}, {2, 3}},
-     {L"\U000000a9\U00000378", {169, 888}, {1, 2}},
-     {L"\U000000a9\U00000308\U00000378", {169, 888}, {2, 3}},
-     {L"\U00000020\U0000000d", {32, 13}, {1, 2}},
-     {L"\U00000020\U00000308\U0000000d", {32, 13}, {2, 3}},
-     {L"\U00000020\U0000000a", {32, 10}, {1, 2}},
-     {L"\U00000020\U00000308\U0000000a", {32, 10}, {2, 3}},
-     {L"\U00000020\U00000000", {32, 0}, {1, 2}},
-     {L"\U00000020\U00000308\U00000000", {32, 0}, {2, 3}},
-     {L"\U00000020\U0000094d", {32}, {2}},
-     {L"\U00000020\U00000308\U0000094d", {32}, {3}},
-     {L"\U00000020\U00000300", {32}, {2}},
-     {L"\U00000020\U00000308\U00000300", {32}, {3}},
-     {L"\U00000020\U0000200c", {32}, {2}},
-     {L"\U00000020\U00000308\U0000200c", {32}, {3}},
-     {L"\U00000020\U0000200d", {32}, {2}},
-     {L"\U00000020\U00000308\U0000200d", {32}, {3}},
-     {L"\U00000020\U0001f1e6", {32, 127462}, {1, 2}},
-     {L"\U00000020\U00000308\U0001f1e6", {32, 127462}, {2, 3}},
-     {L"\U00000020\U000006dd", {32, 1757}, {1, 2}},
-     {L"\U00000020\U00000308\U000006dd", {32, 1757}, {2, 3}},
-     {L"\U00000020\U00000903", {32}, {2}},
-     {L"\U00000020\U00000308\U00000903", {32}, {3}},
-     {L"\U00000020\U00001100", {32, 4352}, {1, 2}},
-     {L"\U00000020\U00000308\U00001100", {32, 4352}, {2, 3}},
-     {L"\U00000020\U00001160", {32, 4448}, {1, 2}},
-     {L"\U00000020\U00000308\U00001160", {32, 4448}, {2, 3}},
-     {L"\U00000020\U000011a8", {32, 4520}, {1, 2}},
-     {L"\U00000020\U00000308\U000011a8", {32, 4520}, {2, 3}},
-     {L"\U00000020\U0000ac00", {32, 44032}, {1, 2}},
-     {L"\U00000020\U00000308\U0000ac00", {32, 44032}, {2, 3}},
-     {L"\U00000020\U0000ac01", {32, 44033}, {1, 2}},
-     {L"\U00000020\U00000308\U0000ac01", {32, 44033}, {2, 3}},
-     {L"\U00000020\U00001cf5", {32, 7413}, {1, 2}},
-     {L"\U00000020\U00000308\U00001cf5", {32, 7413}, {2, 3}},
-     {L"\U00000020\U00000915", {32, 2325}, {1, 2}},
-     {L"\U00000020\U00000308\U00000915", {32, 2325}, {2, 3}},
-     {L"\U00000020\U000000a9", {32, 169}, {1, 2}},
-     {L"\U00000020\U00000308\U000000a9", {32, 169}, {2, 3}},
-     {L"\U00000020\U00000020", {32, 32}, {1, 2}},
-     {L"\U00000020\U00000308\U00000020", {32, 32}, {2, 3}},
-     {L"\U00000020\U00000378", {32, 888}, {1, 2}},
-     {L"\U00000020\U00000308\U00000378", {32, 888}, {2, 3}},
-     {L"\U00000378\U0000000d", {888, 13}, {1, 2}},
-     {L"\U00000378\U00000308\U0000000d", {888, 13}, {2, 3}},
-     {L"\U00000378\U0000000a", {888, 10}, {1, 2}},
-     {L"\U00000378\U00000308\U0000000a", {888, 10}, {2, 3}},
-     {L"\U00000378\U00000000", {888, 0}, {1, 2}},
-     {L"\U00000378\U00000308\U00000000", {888, 0}, {2, 3}},
-     {L"\U00000378\U0000094d", {888}, {2}},
-     {L"\U00000378\U00000308\U0000094d", {888}, {3}},
-     {L"\U00000378\U00000300", {888}, {2}},
-     {L"\U00000378\U00000308\U00000300", {888}, {3}},
-     {L"\U00000378\U0000200c", {888}, {2}},
-     {L"\U00000378\U00000308\U0000200c", {888}, {3}},
-     {L"\U00000378\U0000200d", {888}, {2}},
-     {L"\U00000378\U00000308\U0000200d", {888}, {3}},
-     {L"\U00000378\U0001f1e6", {888, 127462}, {1, 2}},
-     {L"\U00000378\U00000308\U0001f1e6", {888, 127462}, {2, 3}},
-     {L"\U00000378\U000006dd", {888, 1757}, {1, 2}},
-     {L"\U00000378\U00000308\U000006dd", {888, 1757}, {2, 3}},
-     {L"\U00000378\U00000903", {888}, {2}},
-     {L"\U00000378\U00000308\U00000903", {888}, {3}},
-     {L"\U00000378\U00001100", {888, 4352}, {1, 2}},
-     {L"\U00000378\U00000308\U00001100", {888, 4352}, {2, 3}},
-     {L"\U00000378\U00001160", {888, 4448}, {1, 2}},
-     {L"\U00000378\U00000308\U00001160", {888, 4448}, {2, 3}},
-     {L"\U00000378\U000011a8", {888, 4520}, {1, 2}},
-     {L"\U00000378\U00000308\U000011a8", {888, 4520}, {2, 3}},
-     {L"\U00000378\U0000ac00", {888, 44032}, {1, 2}},
-     {L"\U00000378\U00000308\U0000ac00", {888, 44032}, {2, 3}},
-     {L"\U00000378\U0000ac01", {888, 44033}, {1, 2}},
-     {L"\U00000378\U00000308\U0000ac01", {888, 44033}, {2, 3}},
-     {L"\U00000378\U00001cf5", {888, 7413}, {1, 2}},
-     {L"\U00000378\U00000308\U00001cf5", {888, 7413}, {2, 3}},
-     {L"\U00000378\U00000915", {888, 2325}, {1, 2}},
-     {L"\U00000378\U00000308\U00000915", {888, 2325}, {2, 3}},
-     {L"\U00000378\U000000a9", {888, 169}, {1, 2}},
-     {L"\U00000378\U00000308\U000000a9", {888, 169}, {2, 3}},
-     {L"\U00000378\U00000020", {888, 32}, {1, 2}},
-     {L"\U00000378\U00000308\U00000020", {888, 32}, {2, 3}},
-     {L"\U00000378\U00000378", {888, 888}, {1, 2}},
-     {L"\U00000378\U00000308\U00000378", {888, 888}, {2, 3}},
-     {L"\U0000000d\U0000000a\U00000061\U0000000a\U00000308", {13, 97, 10, 776}, {2, 3, 4, 5}},
-     {L"\U00000061\U00000308", {97}, {2}},
-     {L"\U00000020\U0000200d\U00000646", {32, 1606}, {2, 3}},
-     {L"\U00000646\U0000200d\U00000020", {1606, 32}, {2, 3}},
-     {L"\U00001100\U00001100", {4352}, {2}},
-     {L"\U0000ac00\U000011a8\U00001100", {44032, 4352}, {2, 3}},
-     {L"\U0000ac01\U000011a8\U00001100", {44033, 4352}, {2, 3}},
-     {L"\U0001f1e6\U0001f1e7\U0001f1e8\U00000062", {127462, 127464, 98}, {2, 3, 4}},
-     {L"\U00000061\U0001f1e6\U0001f1e7\U0001f1e8\U00000062", {97, 127462, 127464, 98}, {1, 3, 4, 5}},
-     {L"\U00000061\U0001f1e6\U0001f1e7\U0000200d\U0001f1e8\U00000062", {97, 127462, 127464, 98}, {1, 4, 5, 6}},
-     {L"\U00000061\U0001f1e6\U0000200d\U0001f1e7\U0001f1e8\U00000062", {97, 127462, 127463, 98}, {1, 3, 5, 6}},
-     {L"\U00000061\U0001f1e6\U0001f1e7\U0001f1e8\U0001f1e9\U00000062", {97, 127462, 127464, 98}, {1, 3, 5, 6}},
-     {L"\U00000061\U0000200d", {97}, {2}},
-     {L"\U00000061\U00000308\U00000062", {97, 98}, {2, 3}},
-     {L"\U00000061\U00000903\U00000062", {97, 98}, {2, 3}},
-     {L"\U00000061\U00000600\U00000062", {97, 1536}, {1, 3}},
-     {L"\U0001f476\U0001f3ff\U0001f476", {128118, 128118}, {2, 3}},
-     {L"\U00000061\U0001f3ff\U0001f476", {97, 128118}, {2, 3}},
-     {L"\U00000061\U0001f3ff\U0001f476\U0000200d\U0001f6d1", {97, 128118}, {2, 5}},
-     {L"\U0001f476\U0001f3ff\U00000308\U0000200d\U0001f476\U0001f3ff", {128118}, {6}},
-     {L"\U0001f6d1\U0000200d\U0001f6d1", {128721}, {3}},
-     {L"\U00000061\U0000200d\U0001f6d1", {97, 128721}, {2, 3}},
-     {L"\U00002701\U0000200d\U00002701", {9985, 9985}, {2, 3}},
-     {L"\U00000061\U0000200d\U00002701", {97, 9985}, {2, 3}},
-     {L"\U00000915\U00000924", {2325, 2340}, {1, 2}},
-     {L"\U00000915\U0000094d\U00000924", {2325}, {3}},
-     {L"\U00000915\U0000094d\U0000094d\U00000924", {2325}, {4}},
-     {L"\U00000915\U0000094d\U0000200d\U00000924", {2325}, {4}},
-     {L"\U00000915\U0000093c\U0000200d\U0000094d\U00000924", {2325}, {5}},
-     {L"\U00000915\U0000093c\U0000094d\U0000200d\U00000924", {2325}, {5}},
-     {L"\U00000915\U0000094d\U00000924\U0000094d\U0000092f", {2325}, {5}},
-     {L"\U00000915\U0000094d\U00000061", {2325, 97}, {2, 3}},
-     {L"\U00000061\U0000094d\U00000924", {97}, {3}},
-     {L"\U0000003f\U0000094d\U00000924", {63}, {3}},
-     {L"\U00000915\U0000094d\U0000094d\U00000924", {2325}, {4}},
-     {L"\U00000ab8\U00000afb\U00000acd\U00000ab8\U00000afb", {2744}, {5}},
-     {L"\U00001019\U00001039\U00001018\U0000102c\U00001037", {4121, 4140}, {3, 5}},
-     {L"\U00001004\U0000103a\U00001039\U00001011\U00001039\U00001011", {4100}, {6}},
-     {L"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38", {6930, 6962, 6962, 6962}, {2, 5, 10, 14}},
-     {L"\U0000179f\U000017d2\U0000178f\U000017d2\U0000179a\U000017b8", {6047}, {6}},
-     {L"\U00001b26\U00001b17\U00001b44\U00001b13", {6950, 6935}, {1, 4}},
-     {L"\U00001b27\U00001b13\U00001b44\U00001b0b\U00001b0b\U00001b04", {6951, 6931, 6923}, {1, 4, 6}},
-     {L"\U00001795\U000017d2\U000017af\U00001798", {6037, 6040}, {3, 4}},
-     {L"\U000017a0\U000017d2\U000017ab\U00001791\U000017d0\U00001799", {6048, 6033, 6041}, {3, 5, 6}},
-     {L"\U00001b05\U00001b44\U00001b33\U00001b03", {6917}, {4}},
-     {L"\U00000cf1\U00000c95", {3313, 3221}, {1, 2}},
-     {L"\U00000cf2\U00000cab", {3314, 3243}, {1, 2}},
-     {L"\U00000cf1\U00000c95\U00000cbf", {3313, 3221}, {1, 3}},
-     {L"\U00001cf5\U00000995", {7413}, {2}},
-     {L"\U00001cf6\U000009aa", {7414}, {2}},
-     {L"\U00001cf5\U0000200c\U00000995", {7413, 2453}, {2, 3}},
-     {L"\U00001cf6\U0000200c\U000009aa", {7414, 2474}, {2, 3}},
-     {L"\U00011a3a\U00011a0b", {72250}, {2}}}};
+     {L"\U0000000d\U0000000d"sv, {13, 13}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000000d"sv, {13, 776, 13}, {1, 2, 3}},
+     {L"\U0000000d\U0000000a"sv, {13}, {2}},
+     {L"\U0000000d\U00000308\U0000000a"sv, {13, 776, 10}, {1, 2, 3}},
+     {L"\U0000000d\U00000000"sv, {13, 0}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000000"sv, {13, 776, 0}, {1, 2, 3}},
+     {L"\U0000000d\U0000094d"sv, {13, 2381}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000094d"sv, {13, 776}, {1, 3}},
+     {L"\U0000000d\U00000300"sv, {13, 768}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000300"sv, {13, 776}, {1, 3}},
+     {L"\U0000000d\U0000200c"sv, {13, 8204}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000200c"sv, {13, 776}, {1, 3}},
+     {L"\U0000000d\U0000200d"sv, {13, 8205}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000200d"sv, {13, 776}, {1, 3}},
+     {L"\U0000000d\U0001f1e6"sv, {13, 127462}, {1, 2}},
+     {L"\U0000000d\U00000308\U0001f1e6"sv, {13, 776, 127462}, {1, 2, 3}},
+     {L"\U0000000d\U000006dd"sv, {13, 1757}, {1, 2}},
+     {L"\U0000000d\U00000308\U000006dd"sv, {13, 776, 1757}, {1, 2, 3}},
+     {L"\U0000000d\U00000903"sv, {13, 2307}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000903"sv, {13, 776}, {1, 3}},
+     {L"\U0000000d\U00001100"sv, {13, 4352}, {1, 2}},
+     {L"\U0000000d\U00000308\U00001100"sv, {13, 776, 4352}, {1, 2, 3}},
+     {L"\U0000000d\U00001160"sv, {13, 4448}, {1, 2}},
+     {L"\U0000000d\U00000308\U00001160"sv, {13, 776, 4448}, {1, 2, 3}},
+     {L"\U0000000d\U000011a8"sv, {13, 4520}, {1, 2}},
+     {L"\U0000000d\U00000308\U000011a8"sv, {13, 776, 4520}, {1, 2, 3}},
+     {L"\U0000000d\U0000ac00"sv, {13, 44032}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000ac00"sv, {13, 776, 44032}, {1, 2, 3}},
+     {L"\U0000000d\U0000ac01"sv, {13, 44033}, {1, 2}},
+     {L"\U0000000d\U00000308\U0000ac01"sv, {13, 776, 44033}, {1, 2, 3}},
+     {L"\U0000000d\U00001cf5"sv, {13, 7413}, {1, 2}},
+     {L"\U0000000d\U00000308\U00001cf5"sv, {13, 776, 7413}, {1, 2, 3}},
+     {L"\U0000000d\U00000915"sv, {13, 2325}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000915"sv, {13, 776, 2325}, {1, 2, 3}},
+     {L"\U0000000d\U000000a9"sv, {13, 169}, {1, 2}},
+     {L"\U0000000d\U00000308\U000000a9"sv, {13, 776, 169}, {1, 2, 3}},
+     {L"\U0000000d\U00000020"sv, {13, 32}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000020"sv, {13, 776, 32}, {1, 2, 3}},
+     {L"\U0000000d\U00000378"sv, {13, 888}, {1, 2}},
+     {L"\U0000000d\U00000308\U00000378"sv, {13, 776, 888}, {1, 2, 3}},
+     {L"\U0000000a\U0000000d"sv, {10, 13}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000000d"sv, {10, 776, 13}, {1, 2, 3}},
+     {L"\U0000000a\U0000000a"sv, {10, 10}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000000a"sv, {10, 776, 10}, {1, 2, 3}},
+     {L"\U0000000a\U00000000"sv, {10, 0}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000000"sv, {10, 776, 0}, {1, 2, 3}},
+     {L"\U0000000a\U0000094d"sv, {10, 2381}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000094d"sv, {10, 776}, {1, 3}},
+     {L"\U0000000a\U00000300"sv, {10, 768}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000300"sv, {10, 776}, {1, 3}},
+     {L"\U0000000a\U0000200c"sv, {10, 8204}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000200c"sv, {10, 776}, {1, 3}},
+     {L"\U0000000a\U0000200d"sv, {10, 8205}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000200d"sv, {10, 776}, {1, 3}},
+     {L"\U0000000a\U0001f1e6"sv, {10, 127462}, {1, 2}},
+     {L"\U0000000a\U00000308\U0001f1e6"sv, {10, 776, 127462}, {1, 2, 3}},
+     {L"\U0000000a\U000006dd"sv, {10, 1757}, {1, 2}},
+     {L"\U0000000a\U00000308\U000006dd"sv, {10, 776, 1757}, {1, 2, 3}},
+     {L"\U0000000a\U00000903"sv, {10, 2307}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000903"sv, {10, 776}, {1, 3}},
+     {L"\U0000000a\U00001100"sv, {10, 4352}, {1, 2}},
+     {L"\U0000000a\U00000308\U00001100"sv, {10, 776, 4352}, {1, 2, 3}},
+     {L"\U0000000a\U00001160"sv, {10, 4448}, {1, 2}},
+     {L"\U0000000a\U00000308\U00001160"sv, {10, 776, 4448}, {1, 2, 3}},
+     {L"\U0000000a\U000011a8"sv, {10, 4520}, {1, 2}},
+     {L"\U0000000a\U00000308\U000011a8"sv, {10, 776, 4520}, {1, 2, 3}},
+     {L"\U0000000a\U0000ac00"sv, {10, 44032}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000ac00"sv, {10, 776, 44032}, {1, 2, 3}},
+     {L"\U0000000a\U0000ac01"sv, {10, 44033}, {1, 2}},
+     {L"\U0000000a\U00000308\U0000ac01"sv, {10, 776, 44033}, {1, 2, 3}},
+     {L"\U0000000a\U00001cf5"sv, {10, 7413}, {1, 2}},
+     {L"\U0000000a\U00000308\U00001cf5"sv, {10, 776, 7413}, {1, 2, 3}},
+     {L"\U0000000a\U00000915"sv, {10, 2325}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000915"sv, {10, 776, 2325}, {1, 2, 3}},
+     {L"\U0000000a\U000000a9"sv, {10, 169}, {1, 2}},
+     {L"\U0000000a\U00000308\U000000a9"sv, {10, 776, 169}, {1, 2, 3}},
+     {L"\U0000000a\U00000020"sv, {10, 32}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000020"sv, {10, 776, 32}, {1, 2, 3}},
+     {L"\U0000000a\U00000378"sv, {10, 888}, {1, 2}},
+     {L"\U0000000a\U00000308\U00000378"sv, {10, 776, 888}, {1, 2, 3}},
+     {L"\U00000000\U0000000d"sv, {0, 13}, {1, 2}},
+     {L"\U00000000\U00000308\U0000000d"sv, {0, 776, 13}, {1, 2, 3}},
+     {L"\U00000000\U0000000a"sv, {0, 10}, {1, 2}},
+     {L"\U00000000\U00000308\U0000000a"sv, {0, 776, 10}, {1, 2, 3}},
+     {L"\U00000000\U00000000"sv, {0, 0}, {1, 2}},
+     {L"\U00000000\U00000308\U00000000"sv, {0, 776, 0}, {1, 2, 3}},
+     {L"\U00000000\U0000094d"sv, {0, 2381}, {1, 2}},
+     {L"\U00000000\U00000308\U0000094d"sv, {0, 776}, {1, 3}},
+     {L"\U00000000\U00000300"sv, {0, 768}, {1, 2}},
+     {L"\U00000000\U00000308\U00000300"sv, {0, 776}, {1, 3}},
+     {L"\U00000000\U0000200c"sv, {0, 8204}, {1, 2}},
+     {L"\U00000000\U00000308\U0000200c"sv, {0, 776}, {1, 3}},
+     {L"\U00000000\U0000200d"sv, {0, 8205}, {1, 2}},
+     {L"\U00000000\U00000308\U0000200d"sv, {0, 776}, {1, 3}},
+     {L"\U00000000\U0001f1e6"sv, {0, 127462}, {1, 2}},
+     {L"\U00000000\U00000308\U0001f1e6"sv, {0, 776, 127462}, {1, 2, 3}},
+     {L"\U00000000\U000006dd"sv, {0, 1757}, {1, 2}},
+     {L"\U00000000\U00000308\U000006dd"sv, {0, 776, 1757}, {1, 2, 3}},
+     {L"\U00000000\U00000903"sv, {0, 2307}, {1, 2}},
+     {L"\U00000000\U00000308\U00000903"sv, {0, 776}, {1, 3}},
+     {L"\U00000000\U00001100"sv, {0, 4352}, {1, 2}},
+     {L"\U00000000\U00000308\U00001100"sv, {0, 776, 4352}, {1, 2, 3}},
+     {L"\U00000000\U00001160"sv, {0, 4448}, {1, 2}},
+     {L"\U00000000\U00000308\U00001160"sv, {0, 776, 4448}, {1, 2, 3}},
+     {L"\U00000000\U000011a8"sv, {0, 4520}, {1, 2}},
+     {L"\U00000000\U00000308\U000011a8"sv, {0, 776, 4520}, {1, 2, 3}},
+     {L"\U00000000\U0000ac00"sv, {0, 44032}, {1, 2}},
+     {L"\U00000000\U00000308\U0000ac00"sv, {0, 776, 44032}, {1, 2, 3}},
+     {L"\U00000000\U0000ac01"sv, {0, 44033}, {1, 2}},
+     {L"\U00000000\U00000308\U0000ac01"sv, {0, 776, 44033}, {1, 2, 3}},
+     {L"\U00000000\U00001cf5"sv, {0, 7413}, {1, 2}},
+     {L"\U00000000\U00000308\U00001cf5"sv, {0, 776, 7413}, {1, 2, 3}},
+     {L"\U00000000\U00000915"sv, {0, 2325}, {1, 2}},
+     {L"\U00000000\U00000308\U00000915"sv, {0, 776, 2325}, {1, 2, 3}},
+     {L"\U00000000\U000000a9"sv, {0, 169}, {1, 2}},
+     {L"\U00000000\U00000308\U000000a9"sv, {0, 776, 169}, {1, 2, 3}},
+     {L"\U00000000\U00000020"sv, {0, 32}, {1, 2}},
+     {L"\U00000000\U00000308\U00000020"sv, {0, 776, 32}, {1, 2, 3}},
+     {L"\U00000000\U00000378"sv, {0, 888}, {1, 2}},
+     {L"\U00000000\U00000308\U00000378"sv, {0, 776, 888}, {1, 2, 3}},
+     {L"\U0000094d\U0000000d"sv, {2381, 13}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000000d"sv, {2381, 13}, {2, 3}},
+     {L"\U0000094d\U0000000a"sv, {2381, 10}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000000a"sv, {2381, 10}, {2, 3}},
+     {L"\U0000094d\U00000000"sv, {2381, 0}, {1, 2}},
+     {L"\U0000094d\U00000308\U00000000"sv, {2381, 0}, {2, 3}},
+     {L"\U0000094d\U0000094d"sv, {2381}, {2}},
+     {L"\U0000094d\U00000308\U0000094d"sv, {2381}, {3}},
+     {L"\U0000094d\U00000300"sv, {2381}, {2}},
+     {L"\U0000094d\U00000308\U00000300"sv, {2381}, {3}},
+     {L"\U0000094d\U0000200c"sv, {2381}, {2}},
+     {L"\U0000094d\U00000308\U0000200c"sv, {2381}, {3}},
+     {L"\U0000094d\U0000200d"sv, {2381}, {2}},
+     {L"\U0000094d\U00000308\U0000200d"sv, {2381}, {3}},
+     {L"\U0000094d\U0001f1e6"sv, {2381, 127462}, {1, 2}},
+     {L"\U0000094d\U00000308\U0001f1e6"sv, {2381, 127462}, {2, 3}},
+     {L"\U0000094d\U000006dd"sv, {2381, 1757}, {1, 2}},
+     {L"\U0000094d\U00000308\U000006dd"sv, {2381, 1757}, {2, 3}},
+     {L"\U0000094d\U00000903"sv, {2381}, {2}},
+     {L"\U0000094d\U00000308\U00000903"sv, {2381}, {3}},
+     {L"\U0000094d\U00001100"sv, {2381, 4352}, {1, 2}},
+     {L"\U0000094d\U00000308\U00001100"sv, {2381, 4352}, {2, 3}},
+     {L"\U0000094d\U00001160"sv, {2381, 4448}, {1, 2}},
+     {L"\U0000094d\U00000308\U00001160"sv, {2381, 4448}, {2, 3}},
+     {L"\U0000094d\U000011a8"sv, {2381, 4520}, {1, 2}},
+     {L"\U0000094d\U00000308\U000011a8"sv, {2381, 4520}, {2, 3}},
+     {L"\U0000094d\U0000ac00"sv, {2381, 44032}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000ac00"sv, {2381, 44032}, {2, 3}},
+     {L"\U0000094d\U0000ac01"sv, {2381, 44033}, {1, 2}},
+     {L"\U0000094d\U00000308\U0000ac01"sv, {2381, 44033}, {2, 3}},
+     {L"\U0000094d\U00001cf5"sv, {2381, 7413}, {1, 2}},
+     {L"\U0000094d\U00000308\U00001cf5"sv, {2381, 7413}, {2, 3}},
+     {L"\U0000094d\U00000915"sv, {2381}, {2}},
+     {L"\U0000094d\U00000308\U00000915"sv, {2381}, {3}},
+     {L"\U0000094d\U000000a9"sv, {2381, 169}, {1, 2}},
+     {L"\U0000094d\U00000308\U000000a9"sv, {2381, 169}, {2, 3}},
+     {L"\U0000094d\U00000020"sv, {2381, 32}, {1, 2}},
+     {L"\U0000094d\U00000308\U00000020"sv, {2381, 32}, {2, 3}},
+     {L"\U0000094d\U00000378"sv, {2381, 888}, {1, 2}},
+     {L"\U0000094d\U00000308\U00000378"sv, {2381, 888}, {2, 3}},
+     {L"\U00000300\U0000000d"sv, {768, 13}, {1, 2}},
+     {L"\U00000300\U00000308\U0000000d"sv, {768, 13}, {2, 3}},
+     {L"\U00000300\U0000000a"sv, {768, 10}, {1, 2}},
+     {L"\U00000300\U00000308\U0000000a"sv, {768, 10}, {2, 3}},
+     {L"\U00000300\U00000000"sv, {768, 0}, {1, 2}},
+     {L"\U00000300\U00000308\U00000000"sv, {768, 0}, {2, 3}},
+     {L"\U00000300\U0000094d"sv, {768}, {2}},
+     {L"\U00000300\U00000308\U0000094d"sv, {768}, {3}},
+     {L"\U00000300\U00000300"sv, {768}, {2}},
+     {L"\U00000300\U00000308\U00000300"sv, {768}, {3}},
+     {L"\U00000300\U0000200c"sv, {768}, {2}},
+     {L"\U00000300\U00000308\U0000200c"sv, {768}, {3}},
+     {L"\U00000300\U0000200d"sv, {768}, {2}},
+     {L"\U00000300\U00000308\U0000200d"sv, {768}, {3}},
+     {L"\U00000300\U0001f1e6"sv, {768, 127462}, {1, 2}},
+     {L"\U00000300\U00000308\U0001f1e6"sv, {768, 127462}, {2, 3}},
+     {L"\U00000300\U000006dd"sv, {768, 1757}, {1, 2}},
+     {L"\U00000300\U00000308\U000006dd"sv, {768, 1757}, {2, 3}},
+     {L"\U00000300\U00000903"sv, {768}, {2}},
+     {L"\U00000300\U00000308\U00000903"sv, {768}, {3}},
+     {L"\U00000300\U00001100"sv, {768, 4352}, {1, 2}},
+     {L"\U00000300\U00000308\U00001100"sv, {768, 4352}, {2, 3}},
+     {L"\U00000300\U00001160"sv, {768, 4448}, {1, 2}},
+     {L"\U00000300\U00000308\U00001160"sv, {768, 4448}, {2, 3}},
+     {L"\U00000300\U000011a8"sv, {768, 4520}, {1, 2}},
+     {L"\U00000300\U00000308\U000011a8"sv, {768, 4520}, {2, 3}},
+     {L"\U00000300\U0000ac00"sv, {768, 44032}, {1, 2}},
+     {L"\U00000300\U00000308\U0000ac00"sv, {768, 44032}, {2, 3}},
+     {L"\U00000300\U0000ac01"sv, {768, 44033}, {1, 2}},
+     {L"\U00000300\U00000308\U0000ac01"sv, {768, 44033}, {2, 3}},
+     {L"\U00000300\U00001cf5"sv, {768, 7413}, {1, 2}},
+     {L"\U00000300\U00000308\U00001cf5"sv, {768, 7413}, {2, 3}},
+     {L"\U00000300\U00000915"sv, {768, 2325}, {1, 2}},
+     {L"\U00000300\U00000308\U00000915"sv, {768, 2325}, {2, 3}},
+     {L"\U00000300\U000000a9"sv, {768, 169}, {1, 2}},
+     {L"\U00000300\U00000308\U000000a9"sv, {768, 169}, {2, 3}},
+     {L"\U00000300\U00000020"sv, {768, 32}, {1, 2}},
+     {L"\U00000300\U00000308\U00000020"sv, {768, 32}, {2, 3}},
+     {L"\U00000300\U00000378"sv, {768, 888}, {1, 2}},
+     {L"\U00000300\U00000308\U00000378"sv, {768, 888}, {2, 3}},
+     {L"\U0000200c\U0000000d"sv, {8204, 13}, {1, 2}},
+     {L"\U0000200c\U00000308\U0000000d"sv, {8204, 13}, {2, 3}},
+     {L"\U0000200c\U0000000a"sv, {8204, 10}, {1, 2}},
+     {L"\U0000200c\U00000308\U0000000a"sv, {8204, 10}, {2, 3}},
+     {L"\U0000200c\U00000000"sv, {8204, 0}, {1, 2}},
+     {L"\U0000200c\U00000308\U00000000"sv, {8204, 0}, {2, 3}},
+     {L"\U0000200c\U0000094d"sv, {8204}, {2}},
+     {L"\U0000200c\U00000308\U0000094d"sv, {8204}, {3}},
+     {L"\U0000200c\U00000300"sv, {8204}, {2}},
+     {L"\U0000200c\U00000308\U00000300"sv, {8204}, {3}},
+     {L"\U0000200c\U0000200c"sv, {8204}, {2}},
+     {L"\U0000200c\U00000308\U0000200c"sv, {8204}, {3}},
+     {L"\U0000200c\U0000200d"sv, {8204}, {2}},
+     {L"\U0000200c\U00000308\U0000200d"sv, {8204}, {3}},
+     {L"\U0000200c\U0001f1e6"sv, {8204, 127462}, {1, 2}},
+     {L"\U0000200c\U00000308\U0001f1e6"sv, {8204, 127462}, {2, 3}},
+     {L"\U0000200c\U000006dd"sv, {8204, 1757}, {1, 2}},
+     {L"\U0000200c\U00000308\U000006dd"sv, {8204, 1757}, {2, 3}},
+     {L"\U0000200c\U00000903"sv, {8204}, {2}},
+     {L"\U0000200c\U00000308\U00000903"sv, {8204}, {3}},
+     {L"\U0000200c\U00001100"sv, {8204, 4352}, {1, 2}},
+     {L"\U0000200c\U00000308\U00001100"sv, {8204, 4352}, {2, 3}},
+     {L"\U0000200c\U00001160"sv, {8204, 4448}, {1, 2}},
+     {L"\U0000200c\U00000308\U00001160"sv, {8204, 4448}, {2, 3}},
+     {L"\U0000200c\U000011a8"sv, {8204, 4520}, {1, 2}},
+     {L"\U0000200c\U00000308\U000011a8"sv, {8204, 4520}, {2, 3}},
+     {L"\U0000200c\U0000ac00"sv, {8204, 44032}, {1, 2}},
+     {L"\U0000200c\U00000308\U0000ac00"sv, {8204, 44032}, {2, 3}},
+     {L"\U0000200c\U0000ac01"sv, {8204, 44033}, {1, 2}},
+     {L"\U0000200c\U00000308\U0000ac01"sv, {8204, 44033}, {2, 3}},
+     {L"\U0000200c\U00001cf5"sv, {8204, 7413}, {1, 2}},
+     {L"\U0000200c\U00000308\U00001cf5"sv, {8204, 7413}, {2, 3}},
+     {L"\U0000200c\U00000915"sv, {8204, 2325}, {1, 2}},
+     {L"\U0000200c\U00000308\U00000915"sv, {8204, 2325}, {2, 3}},
+     {L"\U0000200c\U000000a9"sv, {8204, 169}, {1, 2}},
+     {L"\U0000200c\U00000308\U000000a9"sv, {8204, 169}, {2, 3}},
+     {L"\U0000200c\U00000020"sv, {8204, 32}, {1, 2}},
+     {L"\U0000200c\U00000308\U00000020"sv, {8204, 32}, {2, 3}},
+     {L"\U0000200c\U00000378"sv, {8204, 888}, {1, 2}},
+     {L"\U0000200c\U00000308\U00000378"sv, {8204, 888}, {2, 3}},
+     {L"\U0000200d\U0000000d"sv, {8205, 13}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000000d"sv, {8205, 13}, {2, 3}},
+     {L"\U0000200d\U0000000a"sv, {8205, 10}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000000a"sv, {8205, 10}, {2, 3}},
+     {L"\U0000200d\U00000000"sv, {8205, 0}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000000"sv, {8205, 0}, {2, 3}},
+     {L"\U0000200d\U0000094d"sv, {8205}, {2}},
+     {L"\U0000200d\U00000308\U0000094d"sv, {8205}, {3}},
+     {L"\U0000200d\U00000300"sv, {8205}, {2}},
+     {L"\U0000200d\U00000308\U00000300"sv, {8205}, {3}},
+     {L"\U0000200d\U0000200c"sv, {8205}, {2}},
+     {L"\U0000200d\U00000308\U0000200c"sv, {8205}, {3}},
+     {L"\U0000200d\U0000200d"sv, {8205}, {2}},
+     {L"\U0000200d\U00000308\U0000200d"sv, {8205}, {3}},
+     {L"\U0000200d\U0001f1e6"sv, {8205, 127462}, {1, 2}},
+     {L"\U0000200d\U00000308\U0001f1e6"sv, {8205, 127462}, {2, 3}},
+     {L"\U0000200d\U000006dd"sv, {8205, 1757}, {1, 2}},
+     {L"\U0000200d\U00000308\U000006dd"sv, {8205, 1757}, {2, 3}},
+     {L"\U0000200d\U00000903"sv, {8205}, {2}},
+     {L"\U0000200d\U00000308\U00000903"sv, {8205}, {3}},
+     {L"\U0000200d\U00001100"sv, {8205, 4352}, {1, 2}},
+     {L"\U0000200d\U00000308\U00001100"sv, {8205, 4352}, {2, 3}},
+     {L"\U0000200d\U00001160"sv, {8205, 4448}, {1, 2}},
+     {L"\U0000200d\U00000308\U00001160"sv, {8205, 4448}, {2, 3}},
+     {L"\U0000200d\U000011a8"sv, {8205, 4520}, {1, 2}},
+     {L"\U0000200d\U00000308\U000011a8"sv, {8205, 4520}, {2, 3}},
+     {L"\U0000200d\U0000ac00"sv, {8205, 44032}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000ac00"sv, {8205, 44032}, {2, 3}},
+     {L"\U0000200d\U0000ac01"sv, {8205, 44033}, {1, 2}},
+     {L"\U0000200d\U00000308\U0000ac01"sv, {8205, 44033}, {2, 3}},
+     {L"\U0000200d\U00001cf5"sv, {8205, 7413}, {1, 2}},
+     {L"\U0000200d\U00000308\U00001cf5"sv, {8205, 7413}, {2, 3}},
+     {L"\U0000200d\U00000915"sv, {8205, 2325}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000915"sv, {8205, 2325}, {2, 3}},
+     {L"\U0000200d\U000000a9"sv, {8205, 169}, {1, 2}},
+     {L"\U0000200d\U00000308\U000000a9"sv, {8205, 169}, {2, 3}},
+     {L"\U0000200d\U00000020"sv, {8205, 32}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000020"sv, {8205, 32}, {2, 3}},
+     {L"\U0000200d\U00000378"sv, {8205, 888}, {1, 2}},
+     {L"\U0000200d\U00000308\U00000378"sv, {8205, 888}, {2, 3}},
+     {L"\U0001f1e6\U0000000d"sv, {127462, 13}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U0000000d"sv, {127462, 13}, {2, 3}},
+     {L"\U0001f1e6\U0000000a"sv, {127462, 10}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U0000000a"sv, {127462, 10}, {2, 3}},
+     {L"\U0001f1e6\U00000000"sv, {127462, 0}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U00000000"sv, {127462, 0}, {2, 3}},
+     {L"\U0001f1e6\U0000094d"sv, {127462}, {2}},
+     {L"\U0001f1e6\U00000308\U0000094d"sv, {127462}, {3}},
+     {L"\U0001f1e6\U00000300"sv, {127462}, {2}},
+     {L"\U0001f1e6\U00000308\U00000300"sv, {127462}, {3}},
+     {L"\U0001f1e6\U0000200c"sv, {127462}, {2}},
+     {L"\U0001f1e6\U00000308\U0000200c"sv, {127462}, {3}},
+     {L"\U0001f1e6\U0000200d"sv, {127462}, {2}},
+     {L"\U0001f1e6\U00000308\U0000200d"sv, {127462}, {3}},
+     {L"\U0001f1e6\U0001f1e6"sv, {127462}, {2}},
+     {L"\U0001f1e6\U00000308\U0001f1e6"sv, {127462, 127462}, {2, 3}},
+     {L"\U0001f1e6\U000006dd"sv, {127462, 1757}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U000006dd"sv, {127462, 1757}, {2, 3}},
+     {L"\U0001f1e6\U00000903"sv, {127462}, {2}},
+     {L"\U0001f1e6\U00000308\U00000903"sv, {127462}, {3}},
+     {L"\U0001f1e6\U00001100"sv, {127462, 4352}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U00001100"sv, {127462, 4352}, {2, 3}},
+     {L"\U0001f1e6\U00001160"sv, {127462, 4448}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U00001160"sv, {127462, 4448}, {2, 3}},
+     {L"\U0001f1e6\U000011a8"sv, {127462, 4520}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U000011a8"sv, {127462, 4520}, {2, 3}},
+     {L"\U0001f1e6\U0000ac00"sv, {127462, 44032}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U0000ac00"sv, {127462, 44032}, {2, 3}},
+     {L"\U0001f1e6\U0000ac01"sv, {127462, 44033}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U0000ac01"sv, {127462, 44033}, {2, 3}},
+     {L"\U0001f1e6\U00001cf5"sv, {127462, 7413}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U00001cf5"sv, {127462, 7413}, {2, 3}},
+     {L"\U0001f1e6\U00000915"sv, {127462, 2325}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U00000915"sv, {127462, 2325}, {2, 3}},
+     {L"\U0001f1e6\U000000a9"sv, {127462, 169}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U000000a9"sv, {127462, 169}, {2, 3}},
+     {L"\U0001f1e6\U00000020"sv, {127462, 32}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U00000020"sv, {127462, 32}, {2, 3}},
+     {L"\U0001f1e6\U00000378"sv, {127462, 888}, {1, 2}},
+     {L"\U0001f1e6\U00000308\U00000378"sv, {127462, 888}, {2, 3}},
+     {L"\U000006dd\U0000000d"sv, {1757, 13}, {1, 2}},
+     {L"\U000006dd\U00000308\U0000000d"sv, {1757, 13}, {2, 3}},
+     {L"\U000006dd\U0000000a"sv, {1757, 10}, {1, 2}},
+     {L"\U000006dd\U00000308\U0000000a"sv, {1757, 10}, {2, 3}},
+     {L"\U000006dd\U00000000"sv, {1757, 0}, {1, 2}},
+     {L"\U000006dd\U00000308\U00000000"sv, {1757, 0}, {2, 3}},
+     {L"\U000006dd\U0000094d"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000094d"sv, {1757}, {3}},
+     {L"\U000006dd\U00000300"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000300"sv, {1757}, {3}},
+     {L"\U000006dd\U0000200c"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000200c"sv, {1757}, {3}},
+     {L"\U000006dd\U0000200d"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000200d"sv, {1757}, {3}},
+     {L"\U000006dd\U0001f1e6"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U0001f1e6"sv, {1757, 127462}, {2, 3}},
+     {L"\U000006dd\U000006dd"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U000006dd"sv, {1757, 1757}, {2, 3}},
+     {L"\U000006dd\U00000903"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000903"sv, {1757}, {3}},
+     {L"\U000006dd\U00001100"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00001100"sv, {1757, 4352}, {2, 3}},
+     {L"\U000006dd\U00001160"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00001160"sv, {1757, 4448}, {2, 3}},
+     {L"\U000006dd\U000011a8"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U000011a8"sv, {1757, 4520}, {2, 3}},
+     {L"\U000006dd\U0000ac00"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000ac00"sv, {1757, 44032}, {2, 3}},
+     {L"\U000006dd\U0000ac01"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U0000ac01"sv, {1757, 44033}, {2, 3}},
+     {L"\U000006dd\U00001cf5"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00001cf5"sv, {1757, 7413}, {2, 3}},
+     {L"\U000006dd\U00000915"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000915"sv, {1757, 2325}, {2, 3}},
+     {L"\U000006dd\U000000a9"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U000000a9"sv, {1757, 169}, {2, 3}},
+     {L"\U000006dd\U00000020"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000020"sv, {1757, 32}, {2, 3}},
+     {L"\U000006dd\U00000378"sv, {1757}, {2}},
+     {L"\U000006dd\U00000308\U00000378"sv, {1757, 888}, {2, 3}},
+     {L"\U00000903\U0000000d"sv, {2307, 13}, {1, 2}},
+     {L"\U00000903\U00000308\U0000000d"sv, {2307, 13}, {2, 3}},
+     {L"\U00000903\U0000000a"sv, {2307, 10}, {1, 2}},
+     {L"\U00000903\U00000308\U0000000a"sv, {2307, 10}, {2, 3}},
+     {L"\U00000903\U00000000"sv, {2307, 0}, {1, 2}},
+     {L"\U00000903\U00000308\U00000000"sv, {2307, 0}, {2, 3}},
+     {L"\U00000903\U0000094d"sv, {2307}, {2}},
+     {L"\U00000903\U00000308\U0000094d"sv, {2307}, {3}},
+     {L"\U00000903\U00000300"sv, {2307}, {2}},
+     {L"\U00000903\U00000308\U00000300"sv, {2307}, {3}},
+     {L"\U00000903\U0000200c"sv, {2307}, {2}},
+     {L"\U00000903\U00000308\U0000200c"sv, {2307}, {3}},
+     {L"\U00000903\U0000200d"sv, {2307}, {2}},
+     {L"\U00000903\U00000308\U0000200d"sv, {2307}, {3}},
+     {L"\U00000903\U0001f1e6"sv, {2307, 127462}, {1, 2}},
+     {L"\U00000903\U00000308\U0001f1e6"sv, {2307, 127462}, {2, 3}},
+     {L"\U00000903\U000006dd"sv, {2307, 1757}, {1, 2}},
+     {L"\U00000903\U00000308\U000006dd"sv, {2307, 1757}, {2, 3}},
+     {L"\U00000903\U00000903"sv, {2307}, {2}},
+     {L"\U00000903\U00000308\U00000903"sv, {2307}, {3}},
+     {L"\U00000903\U00001100"sv, {2307, 4352}, {1, 2}},
+     {L"\U00000903\U00000308\U00001100"sv, {2307, 4352}, {2, 3}},
+     {L"\U00000903\U00001160"sv, {2307, 4448}, {1, 2}},
+     {L"\U00000903\U00000308\U00001160"sv, {2307, 4448}, {2, 3}},
+     {L"\U00000903\U000011a8"sv, {2307, 4520}, {1, 2}},
+     {L"\U00000903\U00000308\U000011a8"sv, {2307, 4520}, {2, 3}},
+     {L"\U00000903\U0000ac00"sv, {2307, 44032}, {1, 2}},
+     {L"\U00000903\U00000308\U0000ac00"sv, {2307, 44032}, {2, 3}},
+     {L"\U00000903\U0000ac01"sv, {2307, 44033}, {1, 2}},
+     {L"\U00000903\U00000308\U0000ac01"sv, {2307, 44033}, {2, 3}},
+     {L"\U00000903\U00001cf5"sv, {2307, 7413}, {1, 2}},
+     {L"\U00000903\U00000308\U00001cf5"sv, {2307, 7413}, {2, 3}},
+     {L"\U00000903\U00000915"sv, {2307, 2325}, {1, 2}},
+     {L"\U00000903\U00000308\U00000915"sv, {2307, 2325}, {2, 3}},
+     {L"\U00000903\U000000a9"sv, {2307, 169}, {1, 2}},
+     {L"\U00000903\U00000308\U000000a9"sv, {2307, 169}, {2, 3}},
+     {L"\U00000903\U00000020"sv, {2307, 32}, {1, 2}},
+     {L"\U00000903\U00000308\U00000020"sv, {2307, 32}, {2, 3}},
+     {L"\U00000903\U00000378"sv, {2307, 888}, {1, 2}},
+     {L"\U00000903\U00000308\U00000378"sv, {2307, 888}, {2, 3}},
+     {L"\U00001100\U0000000d"sv, {4352, 13}, {1, 2}},
+     {L"\U00001100\U00000308\U0000000d"sv, {4352, 13}, {2, 3}},
+     {L"\U00001100\U0000000a"sv, {4352, 10}, {1, 2}},
+     {L"\U00001100\U00000308\U0000000a"sv, {4352, 10}, {2, 3}},
+     {L"\U00001100\U00000000"sv, {4352, 0}, {1, 2}},
+     {L"\U00001100\U00000308\U00000000"sv, {4352, 0}, {2, 3}},
+     {L"\U00001100\U0000094d"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U0000094d"sv, {4352}, {3}},
+     {L"\U00001100\U00000300"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U00000300"sv, {4352}, {3}},
+     {L"\U00001100\U0000200c"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U0000200c"sv, {4352}, {3}},
+     {L"\U00001100\U0000200d"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U0000200d"sv, {4352}, {3}},
+     {L"\U00001100\U0001f1e6"sv, {4352, 127462}, {1, 2}},
+     {L"\U00001100\U00000308\U0001f1e6"sv, {4352, 127462}, {2, 3}},
+     {L"\U00001100\U000006dd"sv, {4352, 1757}, {1, 2}},
+     {L"\U00001100\U00000308\U000006dd"sv, {4352, 1757}, {2, 3}},
+     {L"\U00001100\U00000903"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U00000903"sv, {4352}, {3}},
+     {L"\U00001100\U00001100"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U00001100"sv, {4352, 4352}, {2, 3}},
+     {L"\U00001100\U00001160"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U00001160"sv, {4352, 4448}, {2, 3}},
+     {L"\U00001100\U000011a8"sv, {4352, 4520}, {1, 2}},
+     {L"\U00001100\U00000308\U000011a8"sv, {4352, 4520}, {2, 3}},
+     {L"\U00001100\U0000ac00"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U0000ac00"sv, {4352, 44032}, {2, 3}},
+     {L"\U00001100\U0000ac01"sv, {4352}, {2}},
+     {L"\U00001100\U00000308\U0000ac01"sv, {4352, 44033}, {2, 3}},
+     {L"\U00001100\U00001cf5"sv, {4352, 7413}, {1, 2}},
+     {L"\U00001100\U00000308\U00001cf5"sv, {4352, 7413}, {2, 3}},
+     {L"\U00001100\U00000915"sv, {4352, 2325}, {1, 2}},
+     {L"\U00001100\U00000308\U00000915"sv, {4352, 2325}, {2, 3}},
+     {L"\U00001100\U000000a9"sv, {4352, 169}, {1, 2}},
+     {L"\U00001100\U00000308\U000000a9"sv, {4352, 169}, {2, 3}},
+     {L"\U00001100\U00000020"sv, {4352, 32}, {1, 2}},
+     {L"\U00001100\U00000308\U00000020"sv, {4352, 32}, {2, 3}},
+     {L"\U00001100\U00000378"sv, {4352, 888}, {1, 2}},
+     {L"\U00001100\U00000308\U00000378"sv, {4352, 888}, {2, 3}},
+     {L"\U00001160\U0000000d"sv, {4448, 13}, {1, 2}},
+     {L"\U00001160\U00000308\U0000000d"sv, {4448, 13}, {2, 3}},
+     {L"\U00001160\U0000000a"sv, {4448, 10}, {1, 2}},
+     {L"\U00001160\U00000308\U0000000a"sv, {4448, 10}, {2, 3}},
+     {L"\U00001160\U00000000"sv, {4448, 0}, {1, 2}},
+     {L"\U00001160\U00000308\U00000000"sv, {4448, 0}, {2, 3}},
+     {L"\U00001160\U0000094d"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U0000094d"sv, {4448}, {3}},
+     {L"\U00001160\U00000300"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U00000300"sv, {4448}, {3}},
+     {L"\U00001160\U0000200c"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U0000200c"sv, {4448}, {3}},
+     {L"\U00001160\U0000200d"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U0000200d"sv, {4448}, {3}},
+     {L"\U00001160\U0001f1e6"sv, {4448, 127462}, {1, 2}},
+     {L"\U00001160\U00000308\U0001f1e6"sv, {4448, 127462}, {2, 3}},
+     {L"\U00001160\U000006dd"sv, {4448, 1757}, {1, 2}},
+     {L"\U00001160\U00000308\U000006dd"sv, {4448, 1757}, {2, 3}},
+     {L"\U00001160\U00000903"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U00000903"sv, {4448}, {3}},
+     {L"\U00001160\U00001100"sv, {4448, 4352}, {1, 2}},
+     {L"\U00001160\U00000308\U00001100"sv, {4448, 4352}, {2, 3}},
+     {L"\U00001160\U00001160"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U00001160"sv, {4448, 4448}, {2, 3}},
+     {L"\U00001160\U000011a8"sv, {4448}, {2}},
+     {L"\U00001160\U00000308\U000011a8"sv, {4448, 4520}, {2, 3}},
+     {L"\U00001160\U0000ac00"sv, {4448, 44032}, {1, 2}},
+     {L"\U00001160\U00000308\U0000ac00"sv, {4448, 44032}, {2, 3}},
+     {L"\U00001160\U0000ac01"sv, {4448, 44033}, {1, 2}},
+     {L"\U00001160\U00000308\U0000ac01"sv, {4448, 44033}, {2, 3}},
+     {L"\U00001160\U00001cf5"sv, {4448, 7413}, {1, 2}},
+     {L"\U00001160\U00000308\U00001cf5"sv, {4448, 7413}, {2, 3}},
+     {L"\U00001160\U00000915"sv, {4448, 2325}, {1, 2}},
+     {L"\U00001160\U00000308\U00000915"sv, {4448, 2325}, {2, 3}},
+     {L"\U00001160\U000000a9"sv, {4448, 169}, {1, 2}},
+     {L"\U00001160\U00000308\U000000a9"sv, {4448, 169}, {2, 3}},
+     {L"\U00001160\U00000020"sv, {4448, 32}, {1, 2}},
+     {L"\U00001160\U00000308\U00000020"sv, {4448, 32}, {2, 3}},
+     {L"\U00001160\U00000378"sv, {4448, 888}, {1, 2}},
+     {L"\U00001160\U00000308\U00000378"sv, {4448, 888}, {2, 3}},
+     {L"\U000011a8\U0000000d"sv, {4520, 13}, {1, 2}},
+     {L"\U000011a8\U00000308\U0000000d"sv, {4520, 13}, {2, 3}},
+     {L"\U000011a8\U0000000a"sv, {4520, 10}, {1, 2}},
+     {L"\U000011a8\U00000308\U0000000a"sv, {4520, 10}, {2, 3}},
+     {L"\U000011a8\U00000000"sv, {4520, 0}, {1, 2}},
+     {L"\U000011a8\U00000308\U00000000"sv, {4520, 0}, {2, 3}},
+     {L"\U000011a8\U0000094d"sv, {4520}, {2}},
+     {L"\U000011a8\U00000308\U0000094d"sv, {4520}, {3}},
+     {L"\U000011a8\U00000300"sv, {4520}, {2}},
+     {L"\U000011a8\U00000308\U00000300"sv, {4520}, {3}},
+     {L"\U000011a8\U0000200c"sv, {4520}, {2}},
+     {L"\U000011a8\U00000308\U0000200c"sv, {4520}, {3}},
+     {L"\U000011a8\U0000200d"sv, {4520}, {2}},
+     {L"\U000011a8\U00000308\U0000200d"sv, {4520}, {3}},
+     {L"\U000011a8\U0001f1e6"sv, {4520, 127462}, {1, 2}},
+     {L"\U000011a8\U00000308\U0001f1e6"sv, {4520, 127462}, {2, 3}},
+     {L"\U000011a8\U000006dd"sv, {4520, 1757}, {1, 2}},
+     {L"\U000011a8\U00000308\U000006dd"sv, {4520, 1757}, {2, 3}},
+     {L"\U000011a8\U00000903"sv, {4520}, {2}},
+     {L"\U000011a8\U00000308\U00000903"sv, {4520}, {3}},
+     {L"\U000011a8\U00001100"sv, {4520, 4352}, {1, 2}},
+     {L"\U000011a8\U00000308\U00001100"sv, {4520, 4352}, {2, 3}},
+     {L"\U000011a8\U00001160"sv, {4520, 4448}, {1, 2}},
+     {L"\U000011a8\U00000308\U00001160"sv, {4520, 4448}, {2, 3}},
+     {L"\U000011a8\U000011a8"sv, {4520}, {2}},
+     {L"\U000011a8\U00000308\U000011a8"sv, {4520, 4520}, {2, 3}},
+     {L"\U000011a8\U0000ac00"sv, {4520, 44032}, {1, 2}},
+     {L"\U000011a8\U00000308\U0000ac00"sv, {4520, 44032}, {2, 3}},
+     {L"\U000011a8\U0000ac01"sv, {4520, 44033}, {1, 2}},
+     {L"\U000011a8\U00000308\U0000ac01"sv, {4520, 44033}, {2, 3}},
+     {L"\U000011a8\U00001cf5"sv, {4520, 7413}, {1, 2}},
+     {L"\U000011a8\U00000308\U00001cf5"sv, {4520, 7413}, {2, 3}},
+     {L"\U000011a8\U00000915"sv, {4520, 2325}, {1, 2}},
+     {L"\U000011a8\U00000308\U00000915"sv, {4520, 2325}, {2, 3}},
+     {L"\U000011a8\U000000a9"sv, {4520, 169}, {1, 2}},
+     {L"\U000011a8\U00000308\U000000a9"sv, {4520, 169}, {2, 3}},
+     {L"\U000011a8\U00000020"sv, {4520, 32}, {1, 2}},
+     {L"\U000011a8\U00000308\U00000020"sv, {4520, 32}, {2, 3}},
+     {L"\U000011a8\U00000378"sv, {4520, 888}, {1, 2}},
+     {L"\U000011a8\U00000308\U00000378"sv, {4520, 888}, {2, 3}},
+     {L"\U0000ac00\U0000000d"sv, {44032, 13}, {1, 2}},
+     {L"\U0000ac00\U00000308\U0000000d"sv, {44032, 13}, {2, 3}},
+     {L"\U0000ac00\U0000000a"sv, {44032, 10}, {1, 2}},
+     {L"\U0000ac00\U00000308\U0000000a"sv, {44032, 10}, {2, 3}},
+     {L"\U0000ac00\U00000000"sv, {44032, 0}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00000000"sv, {44032, 0}, {2, 3}},
+     {L"\U0000ac00\U0000094d"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U0000094d"sv, {44032}, {3}},
+     {L"\U0000ac00\U00000300"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U00000300"sv, {44032}, {3}},
+     {L"\U0000ac00\U0000200c"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U0000200c"sv, {44032}, {3}},
+     {L"\U0000ac00\U0000200d"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U0000200d"sv, {44032}, {3}},
+     {L"\U0000ac00\U0001f1e6"sv, {44032, 127462}, {1, 2}},
+     {L"\U0000ac00\U00000308\U0001f1e6"sv, {44032, 127462}, {2, 3}},
+     {L"\U0000ac00\U000006dd"sv, {44032, 1757}, {1, 2}},
+     {L"\U0000ac00\U00000308\U000006dd"sv, {44032, 1757}, {2, 3}},
+     {L"\U0000ac00\U00000903"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U00000903"sv, {44032}, {3}},
+     {L"\U0000ac00\U00001100"sv, {44032, 4352}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00001100"sv, {44032, 4352}, {2, 3}},
+     {L"\U0000ac00\U00001160"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U00001160"sv, {44032, 4448}, {2, 3}},
+     {L"\U0000ac00\U000011a8"sv, {44032}, {2}},
+     {L"\U0000ac00\U00000308\U000011a8"sv, {44032, 4520}, {2, 3}},
+     {L"\U0000ac00\U0000ac00"sv, {44032, 44032}, {1, 2}},
+     {L"\U0000ac00\U00000308\U0000ac00"sv, {44032, 44032}, {2, 3}},
+     {L"\U0000ac00\U0000ac01"sv, {44032, 44033}, {1, 2}},
+     {L"\U0000ac00\U00000308\U0000ac01"sv, {44032, 44033}, {2, 3}},
+     {L"\U0000ac00\U00001cf5"sv, {44032, 7413}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00001cf5"sv, {44032, 7413}, {2, 3}},
+     {L"\U0000ac00\U00000915"sv, {44032, 2325}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00000915"sv, {44032, 2325}, {2, 3}},
+     {L"\U0000ac00\U000000a9"sv, {44032, 169}, {1, 2}},
+     {L"\U0000ac00\U00000308\U000000a9"sv, {44032, 169}, {2, 3}},
+     {L"\U0000ac00\U00000020"sv, {44032, 32}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00000020"sv, {44032, 32}, {2, 3}},
+     {L"\U0000ac00\U00000378"sv, {44032, 888}, {1, 2}},
+     {L"\U0000ac00\U00000308\U00000378"sv, {44032, 888}, {2, 3}},
+     {L"\U0000ac01\U0000000d"sv, {44033, 13}, {1, 2}},
+     {L"\U0000ac01\U00000308\U0000000d"sv, {44033, 13}, {2, 3}},
+     {L"\U0000ac01\U0000000a"sv, {44033, 10}, {1, 2}},
+     {L"\U0000ac01\U00000308\U0000000a"sv, {44033, 10}, {2, 3}},
+     {L"\U0000ac01\U00000000"sv, {44033, 0}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00000000"sv, {44033, 0}, {2, 3}},
+     {L"\U0000ac01\U0000094d"sv, {44033}, {2}},
+     {L"\U0000ac01\U00000308\U0000094d"sv, {44033}, {3}},
+     {L"\U0000ac01\U00000300"sv, {44033}, {2}},
+     {L"\U0000ac01\U00000308\U00000300"sv, {44033}, {3}},
+     {L"\U0000ac01\U0000200c"sv, {44033}, {2}},
+     {L"\U0000ac01\U00000308\U0000200c"sv, {44033}, {3}},
+     {L"\U0000ac01\U0000200d"sv, {44033}, {2}},
+     {L"\U0000ac01\U00000308\U0000200d"sv, {44033}, {3}},
+     {L"\U0000ac01\U0001f1e6"sv, {44033, 127462}, {1, 2}},
+     {L"\U0000ac01\U00000308\U0001f1e6"sv, {44033, 127462}, {2, 3}},
+     {L"\U0000ac01\U000006dd"sv, {44033, 1757}, {1, 2}},
+     {L"\U0000ac01\U00000308\U000006dd"sv, {44033, 1757}, {2, 3}},
+     {L"\U0000ac01\U00000903"sv, {44033}, {2}},
+     {L"\U0000ac01\U00000308\U00000903"sv, {44033}, {3}},
+     {L"\U0000ac01\U00001100"sv, {44033, 4352}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00001100"sv, {44033, 4352}, {2, 3}},
+     {L"\U0000ac01\U00001160"sv, {44033, 4448}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00001160"sv, {44033, 4448}, {2, 3}},
+     {L"\U0000ac01\U000011a8"sv, {44033}, {2}},
+     {L"\U0000ac01\U00000308\U000011a8"sv, {44033, 4520}, {2, 3}},
+     {L"\U0000ac01\U0000ac00"sv, {44033, 44032}, {1, 2}},
+     {L"\U0000ac01\U00000308\U0000ac00"sv, {44033, 44032}, {2, 3}},
+     {L"\U0000ac01\U0000ac01"sv, {44033, 44033}, {1, 2}},
+     {L"\U0000ac01\U00000308\U0000ac01"sv, {44033, 44033}, {2, 3}},
+     {L"\U0000ac01\U00001cf5"sv, {44033, 7413}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00001cf5"sv, {44033, 7413}, {2, 3}},
+     {L"\U0000ac01\U00000915"sv, {44033, 2325}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00000915"sv, {44033, 2325}, {2, 3}},
+     {L"\U0000ac01\U000000a9"sv, {44033, 169}, {1, 2}},
+     {L"\U0000ac01\U00000308\U000000a9"sv, {44033, 169}, {2, 3}},
+     {L"\U0000ac01\U00000020"sv, {44033, 32}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00000020"sv, {44033, 32}, {2, 3}},
+     {L"\U0000ac01\U00000378"sv, {44033, 888}, {1, 2}},
+     {L"\U0000ac01\U00000308\U00000378"sv, {44033, 888}, {2, 3}},
+     {L"\U00001cf5\U0000000d"sv, {7413, 13}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000000d"sv, {7413, 13}, {2, 3}},
+     {L"\U00001cf5\U0000000a"sv, {7413, 10}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000000a"sv, {7413, 10}, {2, 3}},
+     {L"\U00001cf5\U00000000"sv, {7413, 0}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00000000"sv, {7413, 0}, {2, 3}},
+     {L"\U00001cf5\U0000094d"sv, {7413}, {2}},
+     {L"\U00001cf5\U00000308\U0000094d"sv, {7413}, {3}},
+     {L"\U00001cf5\U00000300"sv, {7413}, {2}},
+     {L"\U00001cf5\U00000308\U00000300"sv, {7413}, {3}},
+     {L"\U00001cf5\U0000200c"sv, {7413}, {2}},
+     {L"\U00001cf5\U00000308\U0000200c"sv, {7413}, {3}},
+     {L"\U00001cf5\U0000200d"sv, {7413}, {2}},
+     {L"\U00001cf5\U00000308\U0000200d"sv, {7413}, {3}},
+     {L"\U00001cf5\U0001f1e6"sv, {7413, 127462}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0001f1e6"sv, {7413, 127462}, {2, 3}},
+     {L"\U00001cf5\U000006dd"sv, {7413, 1757}, {1, 2}},
+     {L"\U00001cf5\U00000308\U000006dd"sv, {7413, 1757}, {2, 3}},
+     {L"\U00001cf5\U00000903"sv, {7413}, {2}},
+     {L"\U00001cf5\U00000308\U00000903"sv, {7413}, {3}},
+     {L"\U00001cf5\U00001100"sv, {7413, 4352}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00001100"sv, {7413, 4352}, {2, 3}},
+     {L"\U00001cf5\U00001160"sv, {7413, 4448}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00001160"sv, {7413, 4448}, {2, 3}},
+     {L"\U00001cf5\U000011a8"sv, {7413, 4520}, {1, 2}},
+     {L"\U00001cf5\U00000308\U000011a8"sv, {7413, 4520}, {2, 3}},
+     {L"\U00001cf5\U0000ac00"sv, {7413, 44032}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000ac00"sv, {7413, 44032}, {2, 3}},
+     {L"\U00001cf5\U0000ac01"sv, {7413, 44033}, {1, 2}},
+     {L"\U00001cf5\U00000308\U0000ac01"sv, {7413, 44033}, {2, 3}},
+     {L"\U00001cf5\U00001cf5"sv, {7413, 7413}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00001cf5"sv, {7413, 7413}, {2, 3}},
+     {L"\U00001cf5\U00000915"sv, {7413}, {2}},
+     {L"\U00001cf5\U00000308\U00000915"sv, {7413}, {3}},
+     {L"\U00001cf5\U000000a9"sv, {7413, 169}, {1, 2}},
+     {L"\U00001cf5\U00000308\U000000a9"sv, {7413, 169}, {2, 3}},
+     {L"\U00001cf5\U00000020"sv, {7413, 32}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00000020"sv, {7413, 32}, {2, 3}},
+     {L"\U00001cf5\U00000378"sv, {7413, 888}, {1, 2}},
+     {L"\U00001cf5\U00000308\U00000378"sv, {7413, 888}, {2, 3}},
+     {L"\U00000915\U0000000d"sv, {2325, 13}, {1, 2}},
+     {L"\U00000915\U00000308\U0000000d"sv, {2325, 13}, {2, 3}},
+     {L"\U00000915\U0000000a"sv, {2325, 10}, {1, 2}},
+     {L"\U00000915\U00000308\U0000000a"sv, {2325, 10}, {2, 3}},
+     {L"\U00000915\U00000000"sv, {2325, 0}, {1, 2}},
+     {L"\U00000915\U00000308\U00000000"sv, {2325, 0}, {2, 3}},
+     {L"\U00000915\U0000094d"sv, {2325}, {2}},
+     {L"\U00000915\U00000308\U0000094d"sv, {2325}, {3}},
+     {L"\U00000915\U00000300"sv, {2325}, {2}},
+     {L"\U00000915\U00000308\U00000300"sv, {2325}, {3}},
+     {L"\U00000915\U0000200c"sv, {2325}, {2}},
+     {L"\U00000915\U00000308\U0000200c"sv, {2325}, {3}},
+     {L"\U00000915\U0000200d"sv, {2325}, {2}},
+     {L"\U00000915\U00000308\U0000200d"sv, {2325}, {3}},
+     {L"\U00000915\U0001f1e6"sv, {2325, 127462}, {1, 2}},
+     {L"\U00000915\U00000308\U0001f1e6"sv, {2325, 127462}, {2, 3}},
+     {L"\U00000915\U000006dd"sv, {2325, 1757}, {1, 2}},
+     {L"\U00000915\U00000308\U000006dd"sv, {2325, 1757}, {2, 3}},
+     {L"\U00000915\U00000903"sv, {2325}, {2}},
+     {L"\U00000915\U00000308\U00000903"sv, {2325}, {3}},
+     {L"\U00000915\U00001100"sv, {2325, 4352}, {1, 2}},
+     {L"\U00000915\U00000308\U00001100"sv, {2325, 4352}, {2, 3}},
+     {L"\U00000915\U00001160"sv, {2325, 4448}, {1, 2}},
+     {L"\U00000915\U00000308\U00001160"sv, {2325, 4448}, {2, 3}},
+     {L"\U00000915\U000011a8"sv, {2325, 4520}, {1, 2}},
+     {L"\U00000915\U00000308\U000011a8"sv, {2325, 4520}, {2, 3}},
+     {L"\U00000915\U0000ac00"sv, {2325, 44032}, {1, 2}},
+     {L"\U00000915\U00000308\U0000ac00"sv, {2325, 44032}, {2, 3}},
+     {L"\U00000915\U0000ac01"sv, {2325, 44033}, {1, 2}},
+     {L"\U00000915\U00000308\U0000ac01"sv, {2325, 44033}, {2, 3}},
+     {L"\U00000915\U00001cf5"sv, {2325, 7413}, {1, 2}},
+     {L"\U00000915\U00000308\U00001cf5"sv, {2325, 7413}, {2, 3}},
+     {L"\U00000915\U00000915"sv, {2325, 2325}, {1, 2}},
+     {L"\U00000915\U00000308\U00000915"sv, {2325, 2325}, {2, 3}},
+     {L"\U00000915\U000000a9"sv, {2325, 169}, {1, 2}},
+     {L"\U00000915\U00000308\U000000a9"sv, {2325, 169}, {2, 3}},
+     {L"\U00000915\U00000020"sv, {2325, 32}, {1, 2}},
+     {L"\U00000915\U00000308\U00000020"sv, {2325, 32}, {2, 3}},
+     {L"\U00000915\U00000378"sv, {2325, 888}, {1, 2}},
+     {L"\U00000915\U00000308\U00000378"sv, {2325, 888}, {2, 3}},
+     {L"\U000000a9\U0000000d"sv, {169, 13}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000000d"sv, {169, 13}, {2, 3}},
+     {L"\U000000a9\U0000000a"sv, {169, 10}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000000a"sv, {169, 10}, {2, 3}},
+     {L"\U000000a9\U00000000"sv, {169, 0}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000000"sv, {169, 0}, {2, 3}},
+     {L"\U000000a9\U0000094d"sv, {169}, {2}},
+     {L"\U000000a9\U00000308\U0000094d"sv, {169}, {3}},
+     {L"\U000000a9\U00000300"sv, {169}, {2}},
+     {L"\U000000a9\U00000308\U00000300"sv, {169}, {3}},
+     {L"\U000000a9\U0000200c"sv, {169}, {2}},
+     {L"\U000000a9\U00000308\U0000200c"sv, {169}, {3}},
+     {L"\U000000a9\U0000200d"sv, {169}, {2}},
+     {L"\U000000a9\U00000308\U0000200d"sv, {169}, {3}},
+     {L"\U000000a9\U0001f1e6"sv, {169, 127462}, {1, 2}},
+     {L"\U000000a9\U00000308\U0001f1e6"sv, {169, 127462}, {2, 3}},
+     {L"\U000000a9\U000006dd"sv, {169, 1757}, {1, 2}},
+     {L"\U000000a9\U00000308\U000006dd"sv, {169, 1757}, {2, 3}},
+     {L"\U000000a9\U00000903"sv, {169}, {2}},
+     {L"\U000000a9\U00000308\U00000903"sv, {169}, {3}},
+     {L"\U000000a9\U00001100"sv, {169, 4352}, {1, 2}},
+     {L"\U000000a9\U00000308\U00001100"sv, {169, 4352}, {2, 3}},
+     {L"\U000000a9\U00001160"sv, {169, 4448}, {1, 2}},
+     {L"\U000000a9\U00000308\U00001160"sv, {169, 4448}, {2, 3}},
+     {L"\U000000a9\U000011a8"sv, {169, 4520}, {1, 2}},
+     {L"\U000000a9\U00000308\U000011a8"sv, {169, 4520}, {2, 3}},
+     {L"\U000000a9\U0000ac00"sv, {169, 44032}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000ac00"sv, {169, 44032}, {2, 3}},
+     {L"\U000000a9\U0000ac01"sv, {169, 44033}, {1, 2}},
+     {L"\U000000a9\U00000308\U0000ac01"sv, {169, 44033}, {2, 3}},
+     {L"\U000000a9\U00001cf5"sv, {169, 7413}, {1, 2}},
+     {L"\U000000a9\U00000308\U00001cf5"sv, {169, 7413}, {2, 3}},
+     {L"\U000000a9\U00000915"sv, {169, 2325}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000915"sv, {169, 2325}, {2, 3}},
+     {L"\U000000a9\U000000a9"sv, {169, 169}, {1, 2}},
+     {L"\U000000a9\U00000308\U000000a9"sv, {169, 169}, {2, 3}},
+     {L"\U000000a9\U00000020"sv, {169, 32}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000020"sv, {169, 32}, {2, 3}},
+     {L"\U000000a9\U00000378"sv, {169, 888}, {1, 2}},
+     {L"\U000000a9\U00000308\U00000378"sv, {169, 888}, {2, 3}},
+     {L"\U00000020\U0000000d"sv, {32, 13}, {1, 2}},
+     {L"\U00000020\U00000308\U0000000d"sv, {32, 13}, {2, 3}},
+     {L"\U00000020\U0000000a"sv, {32, 10}, {1, 2}},
+     {L"\U00000020\U00000308\U0000000a"sv, {32, 10}, {2, 3}},
+     {L"\U00000020\U00000000"sv, {32, 0}, {1, 2}},
+     {L"\U00000020\U00000308\U00000000"sv, {32, 0}, {2, 3}},
+     {L"\U00000020\U0000094d"sv, {32}, {2}},
+     {L"\U00000020\U00000308\U0000094d"sv, {32}, {3}},
+     {L"\U00000020\U00000300"sv, {32}, {2}},
+     {L"\U00000020\U00000308\U00000300"sv, {32}, {3}},
+     {L"\U00000020\U0000200c"sv, {32}, {2}},
+     {L"\U00000020\U00000308\U0000200c"sv, {32}, {3}},
+     {L"\U00000020\U0000200d"sv, {32}, {2}},
+     {L"\U00000020\U00000308\U0000200d"sv, {32}, {3}},
+     {L"\U00000020\U0001f1e6"sv, {32, 127462}, {1, 2}},
+     {L"\U00000020\U00000308\U0001f1e6"sv, {32, 127462}, {2, 3}},
+     {L"\U00000020\U000006dd"sv, {32, 1757}, {1, 2}},
+     {L"\U00000020\U00000308\U000006dd"sv, {32, 1757}, {2, 3}},
+     {L"\U00000020\U00000903"sv, {32}, {2}},
+     {L"\U00000020\U00000308\U00000903"sv, {32}, {3}},
+     {L"\U00000020\U00001100"sv, {32, 4352}, {1, 2}},
+     {L"\U00000020\U00000308\U00001100"sv, {32, 4352}, {2, 3}},
+     {L"\U00000020\U00001160"sv, {32, 4448}, {1, 2}},
+     {L"\U00000020\U00000308\U00001160"sv, {32, 4448}, {2, 3}},
+     {L"\U00000020\U000011a8"sv, {32, 4520}, {1, 2}},
+     {L"\U00000020\U00000308\U000011a8"sv, {32, 4520}, {2, 3}},
+     {L"\U00000020\U0000ac00"sv, {32, 44032}, {1, 2}},
+     {L"\U00000020\U00000308\U0000ac00"sv, {32, 44032}, {2, 3}},
+     {L"\U00000020\U0000ac01"sv, {32, 44033}, {1, 2}},
+     {L"\U00000020\U00000308\U0000ac01"sv, {32, 44033}, {2, 3}},
+     {L"\U00000020\U00001cf5"sv, {32, 7413}, {1, 2}},
+     {L"\U00000020\U00000308\U00001cf5"sv, {32, 7413}, {2, 3}},
+     {L"\U00000020\U00000915"sv, {32, 2325}, {1, 2}},
+     {L"\U00000020\U00000308\U00000915"sv, {32, 2325}, {2, 3}},
+     {L"\U00000020\U000000a9"sv, {32, 169}, {1, 2}},
+     {L"\U00000020\U00000308\U000000a9"sv, {32, 169}, {2, 3}},
+     {L"\U00000020\U00000020"sv, {32, 32}, {1, 2}},
+     {L"\U00000020\U00000308\U00000020"sv, {32, 32}, {2, 3}},
+     {L"\U00000020\U00000378"sv, {32, 888}, {1, 2}},
+     {L"\U00000020\U00000308\U00000378"sv, {32, 888}, {2, 3}},
+     {L"\U00000378\U0000000d"sv, {888, 13}, {1, 2}},
+     {L"\U00000378\U00000308\U0000000d"sv, {888, 13}, {2, 3}},
+     {L"\U00000378\U0000000a"sv, {888, 10}, {1, 2}},
+     {L"\U00000378\U00000308\U0000000a"sv, {888, 10}, {2, 3}},
+     {L"\U00000378\U00000000"sv, {888, 0}, {1, 2}},
+     {L"\U00000378\U00000308\U00000000"sv, {888, 0}, {2, 3}},
+     {L"\U00000378\U0000094d"sv, {888}, {2}},
+     {L"\U00000378\U00000308\U0000094d"sv, {888}, {3}},
+     {L"\U00000378\U00000300"sv, {888}, {2}},
+     {L"\U00000378\U00000308\U00000300"sv, {888}, {3}},
+     {L"\U00000378\U0000200c"sv, {888}, {2}},
+     {L"\U00000378\U00000308\U0000200c"sv, {888}, {3}},
+     {L"\U00000378\U0000200d"sv, {888}, {2}},
+     {L"\U00000378\U00000308\U0000200d"sv, {888}, {3}},
+     {L"\U00000378\U0001f1e6"sv, {888, 127462}, {1, 2}},
+     {L"\U00000378\U00000308\U0001f1e6"sv, {888, 127462}, {2, 3}},
+     {L"\U00000378\U000006dd"sv, {888, 1757}, {1, 2}},
+     {L"\U00000378\U00000308\U000006dd"sv, {888, 1757}, {2, 3}},
+     {L"\U00000378\U00000903"sv, {888}, {2}},
+     {L"\U00000378\U00000308\U00000903"sv, {888}, {3}},
+     {L"\U00000378\U00001100"sv, {888, 4352}, {1, 2}},
+     {L"\U00000378\U00000308\U00001100"sv, {888, 4352}, {2, 3}},
+     {L"\U00000378\U00001160"sv, {888, 4448}, {1, 2}},
+     {L"\U00000378\U00000308\U00001160"sv, {888, 4448}, {2, 3}},
+     {L"\U00000378\U000011a8"sv, {888, 4520}, {1, 2}},
+     {L"\U00000378\U00000308\U000011a8"sv, {888, 4520}, {2, 3}},
+     {L"\U00000378\U0000ac00"sv, {888, 44032}, {1, 2}},
+     {L"\U00000378\U00000308\U0000ac00"sv, {888, 44032}, {2, 3}},
+     {L"\U00000378\U0000ac01"sv, {888, 44033}, {1, 2}},
+     {L"\U00000378\U00000308\U0000ac01"sv, {888, 44033}, {2, 3}},
+     {L"\U00000378\U00001cf5"sv, {888, 7413}, {1, 2}},
+     {L"\U00000378\U00000308\U00001cf5"sv, {888, 7413}, {2, 3}},
+     {L"\U00000378\U00000915"sv, {888, 2325}, {1, 2}},
+     {L"\U00000378\U00000308\U00000915"sv, {888, 2325}, {2, 3}},
+     {L"\U00000378\U000000a9"sv, {888, 169}, {1, 2}},
+     {L"\U00000378\U00000308\U000000a9"sv, {888, 169}, {2, 3}},
+     {L"\U00000378\U00000020"sv, {888, 32}, {1, 2}},
+     {L"\U00000378\U00000308\U00000020"sv, {888, 32}, {2, 3}},
+     {L"\U00000378\U00000378"sv, {888, 888}, {1, 2}},
+     {L"\U00000378\U00000308\U00000378"sv, {888, 888}, {2, 3}},
+     {L"\U0000000d\U0000000a\U00000061\U0000000a\U00000308"sv, {13, 97, 10, 776}, {2, 3, 4, 5}},
+     {L"\U00000061\U00000308"sv, {97}, {2}},
+     {L"\U00000020\U0000200d\U00000646"sv, {32, 1606}, {2, 3}},
+     {L"\U00000646\U0000200d\U00000020"sv, {1606, 32}, {2, 3}},
+     {L"\U00001100\U00001100"sv, {4352}, {2}},
+     {L"\U0000ac00\U000011a8\U00001100"sv, {44032, 4352}, {2, 3}},
+     {L"\U0000ac01\U000011a8\U00001100"sv, {44033, 4352}, {2, 3}},
+     {L"\U0001f1e6\U0001f1e7\U0001f1e8\U00000062"sv, {127462, 127464, 98}, {2, 3, 4}},
+     {L"\U00000061\U0001f1e6\U0001f1e7\U0001f1e8\U00000062"sv, {97, 127462, 127464, 98}, {1, 3, 4, 5}},
+     {L"\U00000061\U0001f1e6\U0001f1e7\U0000200d\U0001f1e8\U00000062"sv, {97, 127462, 127464, 98}, {1, 4, 5, 6}},
+     {L"\U00000061\U0001f1e6\U0000200d\U0001f1e7\U0001f1e8\U00000062"sv, {97, 127462, 127463, 98}, {1, 3, 5, 6}},
+     {L"\U00000061\U0001f1e6\U0001f1e7\U0001f1e8\U0001f1e9\U00000062"sv, {97, 127462, 127464, 98}, {1, 3, 5, 6}},
+     {L"\U00000061\U0000200d"sv, {97}, {2}},
+     {L"\U00000061\U00000308\U00000062"sv, {97, 98}, {2, 3}},
+     {L"\U00000061\U00000903\U00000062"sv, {97, 98}, {2, 3}},
+     {L"\U00000061\U00000600\U00000062"sv, {97, 1536}, {1, 3}},
+     {L"\U0001f476\U0001f3ff\U0001f476"sv, {128118, 128118}, {2, 3}},
+     {L"\U00000061\U0001f3ff\U0001f476"sv, {97, 128118}, {2, 3}},
+     {L"\U00000061\U0001f3ff\U0001f476\U0000200d\U0001f6d1"sv, {97, 128118}, {2, 5}},
+     {L"\U0001f476\U0001f3ff\U00000308\U0000200d\U0001f476\U0001f3ff"sv, {128118}, {6}},
+     {L"\U0001f6d1\U0000200d\U0001f6d1"sv, {128721}, {3}},
+     {L"\U00000061\U0000200d\U0001f6d1"sv, {97, 128721}, {2, 3}},
+     {L"\U00002701\U0000200d\U00002701"sv, {9985, 9985}, {2, 3}},
+     {L"\U00000061\U0000200d\U00002701"sv, {97, 9985}, {2, 3}},
+     {L"\U00000915\U00000924"sv, {2325, 2340}, {1, 2}},
+     {L"\U00000915\U0000094d\U00000924"sv, {2325}, {3}},
+     {L"\U00000915\U0000094d\U0000094d\U00000924"sv, {2325}, {4}},
+     {L"\U00000915\U0000094d\U0000200d\U00000924"sv, {2325}, {4}},
+     {L"\U00000915\U0000093c\U0000200d\U0000094d\U00000924"sv, {2325}, {5}},
+     {L"\U00000915\U0000093c\U0000094d\U0000200d\U00000924"sv, {2325}, {5}},
+     {L"\U00000915\U0000094d\U00000924\U0000094d\U0000092f"sv, {2325}, {5}},
+     {L"\U00000915\U0000094d\U00000061"sv, {2325, 97}, {2, 3}},
+     {L"\U00000061\U0000094d\U00000924"sv, {97}, {3}},
+     {L"\U0000003f\U0000094d\U00000924"sv, {63}, {3}},
+     {L"\U00000915\U0000094d\U0000094d\U00000924"sv, {2325}, {4}},
+     {L"\U00000ab8\U00000afb\U00000acd\U00000ab8\U00000afb"sv, {2744}, {5}},
+     {L"\U00001019\U00001039\U00001018\U0000102c\U00001037"sv, {4121, 4140}, {3, 5}},
+     {L"\U00001004\U0000103a\U00001039\U00001011\U00001039\U00001011"sv, {4100}, {6}},
+     {L"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38"sv, {6930, 6962, 6962, 6962}, {2, 5, 10, 14}},
+     {L"\U0000179f\U000017d2\U0000178f\U000017d2\U0000179a\U000017b8"sv, {6047}, {6}},
+     {L"\U00001b26\U00001b17\U00001b44\U00001b13"sv, {6950, 6935}, {1, 4}},
+     {L"\U00001b27\U00001b13\U00001b44\U00001b0b\U00001b0b\U00001b04"sv, {6951, 6931, 6923}, {1, 4, 6}},
+     {L"\U00001795\U000017d2\U000017af\U00001798"sv, {6037, 6040}, {3, 4}},
+     {L"\U000017a0\U000017d2\U000017ab\U00001791\U000017d0\U00001799"sv, {6048, 6033, 6041}, {3, 5, 6}},
+     {L"\U00001b05\U00001b44\U00001b33\U00001b03"sv, {6917}, {4}},
+     {L"\U00000cf1\U00000c95"sv, {3313, 3221}, {1, 2}},
+     {L"\U00000cf2\U00000cab"sv, {3314, 3243}, {1, 2}},
+     {L"\U00000cf1\U00000c95\U00000cbf"sv, {3313, 3221}, {1, 3}},
+     {L"\U00001cf5\U00000995"sv, {7413}, {2}},
+     {L"\U00001cf6\U000009aa"sv, {7414}, {2}},
+     {L"\U00001cf5\U0000200c\U00000995"sv, {7413, 2453}, {2, 3}},
+     {L"\U00001cf6\U0000200c\U000009aa"sv, {7414, 2474}, {2, 3}},
+     {L"\U00011a3a\U00011a0b"sv, {72250}, {2}}}};
 #endif // TEST_HAS_NO_WIDE_CHARACTERS
 
 #endif // LIBCXX_TEST_STD_UTILITIES_FORMAT_FORMAT_STRING_FORMAT_STRING_STD_EXTENDED_GRAPHEME_CLUSTER_H
diff --git a/libcxx/utils/generate_extended_grapheme_cluster_test.py b/libcxx/utils/generate_extended_grapheme_cluster_test.py
index 29a3e14014e8d..e9166fb66d58d 100755
--- a/libcxx/utils/generate_extended_grapheme_cluster_test.py
+++ b/libcxx/utils/generate_extended_grapheme_cluster_test.py
@@ -157,6 +157,8 @@ def parseBreakTestLine(input: TextIO) -> Optional[BreakTestItem]:
 
 #include "test_macros.h"
 
+using namespace std::literals;
+
 template <class CharT>
 struct data {{
   /// The input to parse.
@@ -200,7 +202,7 @@ def parseBreakTestLine(input: TextIO) -> Optional[BreakTestItem]:
 
 def lineToCppDataLineUtf8(line: BreakTestItem) -> str:
     return cpp_test_data_line_template.format(
-        f'"{line.encoded}"',
+        f'"{line.encoded}"sv',
         ", ".join([str(x) for x in line.code_points]),
         ", ".join([str(x) for x in line.breaks_utf8]),
     )
@@ -208,7 +210,7 @@ def lineToCppDataLineUtf8(line: BreakTestItem) -> str:
 
 def lineToCppDataLineUtf16(line: BreakTestItem) -> str:
     return cpp_test_data_line_template.format(
-        f'L"{line.encoded}"',
+        f'L"{line.encoded}"sv',
         ", ".join([str(x) for x in line.code_points]),
         ", ".join([str(x) for x in line.breaks_utf16]),
     )
@@ -216,7 +218,7 @@ def lineToCppDataLineUtf16(line: BreakTestItem) -> str:
 
 def lineToCppDataLineUtf32(line: BreakTestItem) -> str:
     return cpp_test_data_line_template.format(
-        f'L"{line.encoded}"',
+        f'L"{line.encoded}"sv',
         ", ".join([str(x) for x in line.code_points]),
         ", ".join([str(x) for x in line.breaks_utf32]),
     )

>From e18fdb8fbcac888a8f496ea576c96b176041c8ef Mon Sep 17 00:00:00 2001
From: unknown <lol>
Date: Sun, 21 Jun 2026 18:48:28 -0400
Subject: [PATCH 28/33] Add debug lines to fix assertion error

---
 .../format.string.std/extended_grapheme_cluster.pass.cpp  | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
index 3bef1025ca13a..24b98c2b914d3 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
@@ -81,11 +81,11 @@ constexpr void test(const Data& data) {
     std::__unicode::__extended_grapheme_cluster_view view{d.input.begin(), d.input.end()};
     for (std::size_t i = 0; i < d.breaks.size(); ++i) {
       auto r = view.__consume();
-      // XXX: remove after libcxx tetss pass
-      std::cerr.setf(std::ios_base::hex | std::ios_base::showbase);
-      std::cerr << "r.__code__point: " << (unsigned long long)r.__code_point_ <<
-        ", d.code_points[" << i << "]: " << (unsigned long long)d.code_points[i] << '\n'; 
       assert(r.__code_point_ == d.code_points[i]);
+      // XXX: remove after libcxx tests pass
+      std::cerr << "r.__last_: " << (unsigned long long)r.__last_ <<
+        ", d.input.begin(): " << (unsigned long long)d.input.begin() << 
+        ", d.breaks[" << i << "]: " << (unsigned long long)d.breaks[i] << '\n'; 
       assert(r.__last_ == d.input.begin() + d.breaks[i]);
     }
   }

>From a7a3db974804efb480f7279941eda49e12acdc8a Mon Sep 17 00:00:00 2001
From: unknown <lol>
Date: Mon, 22 Jun 2026 09:15:07 -0400
Subject: [PATCH 29/33] [libc++][format] Fix GB9c Indic_Conjunct_Break grapheme
 cluster boundary

The state machine handling the Indic_Conjunct_Break (InCB) GB9c rule in
__extended_grapheme_cluster_break only armed itself when a Consonant was
seen, and then greedily committed to "do not break" as soon as a Linker
followed, without verifying a Consonant eventually closes the run. This
caused two classes of mismatches against the UCD GraphemeBreakTest.txt
conformance data (e.g. the AIX extended_grapheme_cluster.pass.cpp crash):

- A leading Linker with no preceding Consonant (e.g. sot + U+094D + U+0915)
  was incorrectly split into two clusters instead of one.
- A Consonant followed by a Linker that never resolves into a trailing
  Consonant (e.g. U+0915 + U+1CF5 at end of text) was incorrectly joined
  into one cluster instead of two.

Replace the separate GB9c sub-state-machine with a single __pending_linker_
flag updated in operator(), tracking whether an unresolved InCB Linker run
is in progress so the decision can be overridden the moment a resolving
Consonant is actually observed. Verified against all 853 UTF-8 and UTF-16
entries in extended_grapheme_cluster.h.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
 libcxx/include/__format/unicode.h | 102 +++++++++++++-----------------
 1 file changed, 43 insertions(+), 59 deletions(-)

diff --git a/libcxx/include/__format/unicode.h b/libcxx/include/__format/unicode.h
index dbb87f2d711b0..73347f16f2ac2 100644
--- a/libcxx/include/__format/unicode.h
+++ b/libcxx/include/__format/unicode.h
@@ -306,21 +306,55 @@ class __extended_grapheme_cluster_break {
 public:
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __extended_grapheme_cluster_break(char32_t __first_code_point)
       : __prev_code_point_(__first_code_point),
-        __prev_property_(__extended_grapheme_custer_property_boundary::__get_property(__first_code_point)) {
+        __prev_property_(__extended_grapheme_custer_property_boundary::__get_property(__first_code_point)),
+        __pending_linker_(__indic_conjunct_break::__get_property(__first_code_point) == __inCB_property::__Linker) {
     // Initializes the active rule.
     if (__prev_property_ == __EGC_property::__Extended_Pictographic)
       __active_rule_ = __rule::__GB11_emoji;
     else if (__prev_property_ == __EGC_property::__Regional_Indicator)
       __active_rule_ = __rule::__GB12_GB13_regional_indicator;
-    else if (__indic_conjunct_break::__get_property(__first_code_point) == __inCB_property::__Consonant)
-      __active_rule_ = __rule::__GB9c_indic_conjunct_break;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(char32_t __next_code_point) {
     __EGC_property __next_property = __extended_grapheme_custer_property_boundary::__get_property(__next_code_point);
     bool __result                  = __evaluate(__next_code_point, __next_property);
-    __prev_code_point_             = __next_code_point;
-    __prev_property_               = __next_property;
+
+    // *** GB9c: Indic_Conjunct_Break (InCB). ***
+    //
+    // Do not break within certain combinations with Indic_Conjunct_Break
+    // (InCB)=Linker. A Linker (optionally preceded by a run of InCB Extend or
+    // further Linker code points, themselves possibly preceded by a
+    // Consonant) is only known to belong with a following Consonant once
+    // that Consonant is actually seen; until then the Linker's own break
+    // decision is whatever the other rules say. `__pending_linker_` tracks
+    // whether such an unresolved Linker run is in progress; if so, and the
+    // next code point is the resolving Consonant, override the break
+    // decision to "do not break", regardless of what the other rules said.
+    __inCB_property __next_inCB = __indic_conjunct_break::__get_property(__next_code_point);
+    if (__pending_linker_ && __next_inCB == __inCB_property::__Consonant)
+      __result = false;
+
+    if (__result) {
+      // A break occurred; the new cluster starts fresh at __next_code_point.
+      __pending_linker_ = __next_inCB == __inCB_property::__Linker;
+    } else {
+      switch (__next_inCB) {
+      case __inCB_property::__Consonant:
+        __pending_linker_ = false; // The run is resolved.
+        break;
+      case __inCB_property::__Linker:
+        __pending_linker_ = true;
+        break;
+      case __inCB_property::__Extend:
+        break; // Carries the run forward without changing its state.
+      case __inCB_property::__none:
+        __pending_linker_ = false; // An unrelated code point breaks the run's contiguity.
+        break;
+      }
+    }
+
+    __prev_code_point_ = __next_code_point;
+    __prev_property_   = __next_property;
     return __result;
   }
 
@@ -337,8 +371,6 @@ class __extended_grapheme_cluster_break {
     switch (__active_rule_) {
     case __rule::__none:
       return __evaluate_none(__next_code_point, __next_property);
-    case __rule::__GB9c_indic_conjunct_break:
-      return __evaluate_GB9c_indic_conjunct_break(__next_code_point, __next_property);
     case __rule::__GB11_emoji:
       return __evaluate_GB11_emoji(__next_code_point, __next_property);
     case __rule::__GB12_GB13_regional_indicator:
@@ -390,13 +422,6 @@ class __extended_grapheme_cluster_break {
     if (__prev_property_ == __EGC_property::__Prepend) // GB9b
       return false;
 
-    // *** Do not break within certain combinations with Indic_Conjunct_Break (InCB)=Linker. ***
-    if (__indic_conjunct_break::__get_property(__next_code_point) == __inCB_property::__Consonant) {
-      __active_rule_                     = __rule::__GB9c_indic_conjunct_break;
-      __GB9c_indic_conjunct_break_state_ = __GB9c_indic_conjunct_break_state::__Consonant;
-      return true;
-    }
-
     // *** Do not break within emoji modifier sequences or emoji zwj sequences. ***
     if (__next_property == __EGC_property::__Extended_Pictographic) {
       __active_rule_      = __rule::__GB11_emoji;
@@ -417,43 +442,6 @@ class __extended_grapheme_cluster_break {
     return true; // GB999
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool
-  __evaluate_GB9c_indic_conjunct_break(char32_t __next_code_point, __EGC_property __next_property) {
-    __inCB_property __break = __indic_conjunct_break::__get_property(__next_code_point);
-    if (__break == __inCB_property::__none) {
-      __active_rule_ = __rule::__none;
-      return __evaluate_none(__next_code_point, __next_property);
-    }
-
-    switch (__GB9c_indic_conjunct_break_state_) {
-    case __GB9c_indic_conjunct_break_state::__Consonant:
-      if (__break == __inCB_property::__Extend) {
-        return false;
-      }
-      if (__break == __inCB_property::__Linker) {
-        __GB9c_indic_conjunct_break_state_ = __GB9c_indic_conjunct_break_state::__Linker;
-        return false;
-      }
-      __active_rule_ = __rule::__none;
-      return __evaluate_none(__next_code_point, __next_property);
-
-    case __GB9c_indic_conjunct_break_state::__Linker:
-      if (__break == __inCB_property::__Extend) {
-        return false;
-      }
-      if (__break == __inCB_property::__Linker) {
-        return false;
-      }
-      if (__break == __inCB_property::__Consonant) {
-        __GB9c_indic_conjunct_break_state_ = __GB9c_indic_conjunct_break_state::__Consonant;
-        return false;
-      }
-      __active_rule_ = __rule::__none;
-      return __evaluate_none(__next_code_point, __next_property);
-    }
-    __libcpp_unreachable();
-  }
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool
   __evaluate_GB11_emoji(char32_t __next_code_point, __EGC_property __next_property) {
     switch (__GB11_emoji_state_) {
@@ -495,9 +483,12 @@ class __extended_grapheme_cluster_break {
   char32_t __prev_code_point_;
   __EGC_property __prev_property_;
 
+  // Tracks whether an unresolved Indic_Conjunct_Break (InCB) Linker run is in
+  // progress; see the GB9c handling in operator().
+  bool __pending_linker_;
+
   enum class __rule {
     __none,
-    __GB9c_indic_conjunct_break,
     __GB11_emoji,
     __GB12_GB13_regional_indicator,
   };
@@ -510,13 +501,6 @@ class __extended_grapheme_cluster_break {
   };
   __GB11_emoji_state __GB11_emoji_state_ = __GB11_emoji_state::__Extended_Pictographic;
 
-  enum class __GB9c_indic_conjunct_break_state {
-    __Consonant,
-    __Linker,
-  };
-
-  __GB9c_indic_conjunct_break_state __GB9c_indic_conjunct_break_state_ = __GB9c_indic_conjunct_break_state::__Consonant;
-
   // NOLINTEND(readability-identifier-naming)
 };
 

>From f0ce9d311f82076ab0516f94757a921150bc6cb3 Mon Sep 17 00:00:00 2001
From: unknown <lol>
Date: Mon, 22 Jun 2026 09:47:51 -0400
Subject: [PATCH 30/33] Pin Unicode URLs to 18.0.0 in unicode.h

---
 libcxx/include/__format/unicode.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__format/unicode.h b/libcxx/include/__format/unicode.h
index 73347f16f2ac2..8cd797d78acb4 100644
--- a/libcxx/include/__format/unicode.h
+++ b/libcxx/include/__format/unicode.h
@@ -65,9 +65,9 @@ static_assert(sizeof(__consume_result) == sizeof(char32_t));
 /// https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules
 ///
 /// The data tables used are
-/// https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakProperty.txt
-/// https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-data.txt
-/// https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakTest.txt (for testing only)
+/// https://www.unicode.org/Public/18.0.0/ucd/auxiliary/GraphemeBreakProperty.txt
+/// https://www.unicode.org/Public/18.0.0/ucd/emoji/emoji-data.txt
+/// https://www.unicode.org/Public/18.0.0/ucd/auxiliary/GraphemeBreakTest.txt (for testing only)
 
 inline constexpr char32_t __replacement_character = U'\ufffd';
 
@@ -132,7 +132,7 @@ class __code_point_view<char> {
   _LIBCPP_HIDE_FROM_ABI constexpr bool __at_end() const noexcept { return __first_ == __last_; }
   _LIBCPP_HIDE_FROM_ABI constexpr _Iterator __position() const noexcept { return __first_; }
 
-  // https://www.unicode.org/versions/latest/ch03.pdf#G7404
+  // https://www.unicode.org/versions/Unicode18.0.0/core-spec/chapter-3/#G7404
   // Based on Table 3-7, Well-Formed UTF-8 Byte Sequences
   //
   // Code Points        First Byte Second Byte Third Byte Fourth Byte  Remarks

>From ebaf5d28ed9fcd15f08459f7c1eaecf0c50bc0f6 Mon Sep 17 00:00:00 2001
From: unknown <lol>
Date: Mon, 22 Jun 2026 09:50:44 -0400
Subject: [PATCH 31/33] Silence unused parameter warning

---
 libcxx/include/__format/unicode.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__format/unicode.h b/libcxx/include/__format/unicode.h
index 8cd797d78acb4..b8aed65a65292 100644
--- a/libcxx/include/__format/unicode.h
+++ b/libcxx/include/__format/unicode.h
@@ -380,6 +380,7 @@ class __extended_grapheme_cluster_break {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr bool __evaluate_none(char32_t __next_code_point, __EGC_property __next_property) {
+    (void)__next_code_point;
     // *** Break at the start and end of text, unless the text is empty. ***
 
     _LIBCPP_ASSERT_INTERNAL(__prev_property_ != __EGC_property::__sot, "should be handled in the constructor"); // GB1

>From c71ef23cf4d84f57e76ffea76c85287dffaea044 Mon Sep 17 00:00:00 2001
From: Charlie Lin <tuug at gmx.us>
Date: Mon, 22 Jun 2026 12:13:25 -0400
Subject: [PATCH 32/33] Remove debug output from extended grapheme cluster test

Removed debug output for assertion checks.
---
 .../format.string.std/extended_grapheme_cluster.pass.cpp     | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
index 24b98c2b914d3..fc3f55bb581e9 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.pass.cpp
@@ -21,7 +21,6 @@
 #include <cassert>
 #include <format>
 #include <functional>
-#include <iostream>
 #include <numeric>
 
 #include "extended_grapheme_cluster.h"
@@ -82,10 +81,6 @@ constexpr void test(const Data& data) {
     for (std::size_t i = 0; i < d.breaks.size(); ++i) {
       auto r = view.__consume();
       assert(r.__code_point_ == d.code_points[i]);
-      // XXX: remove after libcxx tests pass
-      std::cerr << "r.__last_: " << (unsigned long long)r.__last_ <<
-        ", d.input.begin(): " << (unsigned long long)d.input.begin() << 
-        ", d.breaks[" << i << "]: " << (unsigned long long)d.breaks[i] << '\n'; 
       assert(r.__last_ == d.input.begin() + d.breaks[i]);
     }
   }

>From 1c6e8abec83b6f26b9479234629702b187bc05f6 Mon Sep 17 00:00:00 2001
From: unknown <lol>
Date: Tue, 23 Jun 2026 08:25:29 -0400
Subject: [PATCH 33/33] clang-format

---
 .../extended_grapheme_cluster.h               | 24 ++++++++++++-------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.h b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.h
index 90e5de6a3f931..081e19f772944 100644
--- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.h
+++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/extended_grapheme_cluster.h
@@ -84,8 +84,8 @@ struct data {
 };
 
 /// The data for UTF-8.
-std::array<data<char>, 853> data_utf8 = {{
-     {"\U0000000d\U0000000d"sv, {13, 13}, {1, 2}},
+std::array<data<char>, 853> data_utf8 = {
+    {{"\U0000000d\U0000000d"sv, {13, 13}, {1, 2}},
      {"\U0000000d\U00000308\U0000000d"sv, {13, 776, 13}, {1, 3, 4}},
      {"\U0000000d\U0000000a"sv, {13}, {2}},
      {"\U0000000d\U00000308\U0000000a"sv, {13, 776, 10}, {1, 3, 4}},
@@ -923,7 +923,9 @@ std::array<data<char>, 853> data_utf8 = {{
      {"\U00000ab8\U00000afb\U00000acd\U00000ab8\U00000afb"sv, {2744}, {15}},
      {"\U00001019\U00001039\U00001018\U0000102c\U00001037"sv, {4121, 4140}, {9, 15}},
      {"\U00001004\U0000103a\U00001039\U00001011\U00001039\U00001011"sv, {4100}, {18}},
-     {"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38"sv, {6930, 6962, 6962, 6962}, {6, 15, 30, 42}},
+     {"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38"sv,
+      {6930, 6962, 6962, 6962},
+      {6, 15, 30, 42}},
      {"\U0000179f\U000017d2\U0000178f\U000017d2\U0000179a\U000017b8"sv, {6047}, {18}},
      {"\U00001b26\U00001b17\U00001b44\U00001b13"sv, {6950, 6935}, {3, 12}},
      {"\U00001b27\U00001b13\U00001b44\U00001b0b\U00001b0b\U00001b04"sv, {6951, 6931, 6923}, {3, 12, 18}},
@@ -945,8 +947,8 @@ std::array<data<char>, 853> data_utf8 = {{
 /// since the size of the code units differ the breaks can contain different
 /// values.
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-std::array<data<wchar_t>, 853> data_utf16 = {{
-     {L"\U0000000d\U0000000d"sv, {13, 13}, {1, 2}},
+std::array<data<wchar_t>, 853> data_utf16 = {
+    {{L"\U0000000d\U0000000d"sv, {13, 13}, {1, 2}},
      {L"\U0000000d\U00000308\U0000000d"sv, {13, 776, 13}, {1, 2, 3}},
      {L"\U0000000d\U0000000a"sv, {13}, {2}},
      {L"\U0000000d\U00000308\U0000000a"sv, {13, 776, 10}, {1, 2, 3}},
@@ -1784,7 +1786,9 @@ std::array<data<wchar_t>, 853> data_utf16 = {{
      {L"\U00000ab8\U00000afb\U00000acd\U00000ab8\U00000afb"sv, {2744}, {5}},
      {L"\U00001019\U00001039\U00001018\U0000102c\U00001037"sv, {4121, 4140}, {3, 5}},
      {L"\U00001004\U0000103a\U00001039\U00001011\U00001039\U00001011"sv, {4100}, {6}},
-     {L"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38"sv, {6930, 6962, 6962, 6962}, {2, 5, 10, 14}},
+     {L"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38"sv,
+      {6930, 6962, 6962, 6962},
+      {2, 5, 10, 14}},
      {L"\U0000179f\U000017d2\U0000178f\U000017d2\U0000179a\U000017b8"sv, {6047}, {6}},
      {L"\U00001b26\U00001b17\U00001b44\U00001b13"sv, {6950, 6935}, {1, 4}},
      {L"\U00001b27\U00001b13\U00001b44\U00001b0b\U00001b0b\U00001b04"sv, {6951, 6931, 6923}, {1, 4, 6}},
@@ -1805,8 +1809,8 @@ std::array<data<wchar_t>, 853> data_utf16 = {{
 /// Note that most of the data for the UTF-16 and UTF-32 are identical. However
 /// since the size of the code units differ the breaks can contain different
 /// values.
-std::array<data<wchar_t>, 853> data_utf32 = {{
-     {L"\U0000000d\U0000000d"sv, {13, 13}, {1, 2}},
+std::array<data<wchar_t>, 853> data_utf32 = {
+    {{L"\U0000000d\U0000000d"sv, {13, 13}, {1, 2}},
      {L"\U0000000d\U00000308\U0000000d"sv, {13, 776, 13}, {1, 2, 3}},
      {L"\U0000000d\U0000000a"sv, {13}, {2}},
      {L"\U0000000d\U00000308\U0000000a"sv, {13, 776, 10}, {1, 2, 3}},
@@ -2644,7 +2648,9 @@ std::array<data<wchar_t>, 853> data_utf32 = {{
      {L"\U00000ab8\U00000afb\U00000acd\U00000ab8\U00000afb"sv, {2744}, {5}},
      {L"\U00001019\U00001039\U00001018\U0000102c\U00001037"sv, {4121, 4140}, {3, 5}},
      {L"\U00001004\U0000103a\U00001039\U00001011\U00001039\U00001011"sv, {4100}, {6}},
-     {L"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38"sv, {6930, 6962, 6962, 6962}, {2, 5, 10, 14}},
+     {L"\U00001b12\U00001b01\U00001b32\U00001b44\U00001b2f\U00001b32\U00001b44\U00001b22\U00001b44\U00001b2c\U00001b32\U00001b44\U00001b22\U00001b38"sv,
+      {6930, 6962, 6962, 6962},
+      {2, 5, 10, 14}},
      {L"\U0000179f\U000017d2\U0000178f\U000017d2\U0000179a\U000017b8"sv, {6047}, {6}},
      {L"\U00001b26\U00001b17\U00001b44\U00001b13"sv, {6950, 6935}, {1, 4}},
      {L"\U00001b27\U00001b13\U00001b44\U00001b0b\U00001b0b\U00001b04"sv, {6951, 6931, 6923}, {1, 4, 6}},


From libcxx-commits at lists.llvm.org  Mon Jul  6 18:56:16 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 18:56:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918: copy elision in
 `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4c5cc0.abb10101.3d0bf8.5fdb@mx.google.com>


================
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
----------------
frederick-vs-ja wrote:

```suggestion
  NoMoveNoCopy(const NoMoveNoCopy&) = delete;
```

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Mon Jul  6 18:56:16 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 18:56:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918: copy elision in
 `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4c5cc0.8e75e0cb.5e108.7640@mx.google.com>


================
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
+
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using difference_type   = std::ptrdiff_t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
----------------
frederick-vs-ja wrote:

```suggestion
  constexpr explicit PrvalueIterator(const int* ptr) : ptr_(ptr) {}
```

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Mon Jul  6 18:56:16 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 18:56:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918: copy elision in
 `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4c5cc0.f732ad81.3a4994.1c62@mx.google.com>


================
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
+
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using difference_type   = std::ptrdiff_t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
----------------
frederick-vs-ja wrote:

Nit: It seems more reasonable to me to make this constructor `explicit`.
```suggestion
  constexpr explicit PrvalueIterator(const int* ptr) : ptr_(ptr) {}
```

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Mon Jul  6 18:56:16 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 18:56:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] LWG3918: copy elision in
 `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4c5cc0.331483e7.2440f8.3b02@mx.google.com>


================
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
----------------
frederick-vs-ja wrote:

I think we should delete the copy constructor. The declaration move constructor will be suppressed so the class will also be non-movable.

Also, as a nitpick, I think west `const` is preferred.
```suggestion
  NoMoveNoCopy(const NoMoveNoCopy&) = delete;
```

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Mon Jul  6 19:14:22 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 19:14:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3585 as resolved (PR
	#207717)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207717@github.com>
Message-ID: <6a4c60fe.fdc477d6.23591.3ef9@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/207717

From libcxx-commits at lists.llvm.org  Mon Jul  6 19:16:09 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 19:16:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3585 as resolved (PR
	#207717)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207717@github.com>
Message-ID: <6a4c6169.13be6df8.145c.f68a@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.

LGTM. Although I'm not sure whether the PR description would be fine as squashed commit message. I changed `blame` to `blob` to make the related code snippet directly shown with GitHub UI. Feel to free to edit it again if that's not ideal.

https://github.com/llvm/llvm-project/pull/207717

From libcxx-commits at lists.llvm.org  Mon Jul  6 19:38:09 2026
From: libcxx-commits at lists.llvm.org (Alexey Samsonov via libcxx-commits)
Date: Mon, 06 Jul 2026 19:38:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][libc] update LLVM-libc test
	status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a4c6691.ff037943.2e959a.356a@mx.google.com>


https://github.com/vonosmas approved this pull request.


https://github.com/llvm/llvm-project/pull/201236

From libcxx-commits at lists.llvm.org  Mon Jul  6 22:00:31 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 22:00:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] enable AltiVec vectorization
 with element-wise comparison wrapper (PR #207832)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207832@github.com>
Message-ID: <6a4c87ef.f1c1923d.991d1.4e51@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/207832

>From 451c89af5f6abf5927e6cbf0620e78479399694a Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 6 Jul 2026 19:37:14 +0530
Subject: [PATCH 1/3] [libcxx] enable AltiVec vectorization with element-wise
 comparison wrapper

---
 libcxx/include/__algorithm/find.h             |  6 ++--
 libcxx/include/__algorithm/mismatch.h         |  6 ++--
 libcxx/include/__algorithm/simd_utils.h       | 34 +++++++++++++++++--
 .../include/__cxx03/__algorithm/simd_utils.h  |  3 +-
 libcxx/include/__locale_dir/num.h             |  2 +-
 .../algorithms/vectorization.compile.pass.cpp |  3 --
 6 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__algorithm/find.h b/libcxx/include/__algorithm/find.h
index f677fb2c7392d..94087fb4afe78 100644
--- a/libcxx/include/__algorithm/find.h
+++ b/libcxx/include/__algorithm/find.h
@@ -81,7 +81,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last,
         __lhs[__i] = std::__load_vector<__vec>(__first + __i * __vec_size);
 
       for (size_t __i = 0; __i != __unroll_count; ++__i) {
-        if (auto __cmp_res = __lhs[__i] == __values; std::__any_of(__cmp_res)) {
+        if (auto __cmp_res = std::__simd_compare_eq(__lhs[__i], __values); std::__any_of(__cmp_res)) {
           auto __offset = __i * __vec_size + std::__find_first_set(__cmp_res);
           return __first + __offset;
         }
@@ -92,7 +92,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last,
 
     // check the remaining 0-3 vectors
     while (static_cast<size_t>(__last - __first) >= __vec_size) {
-      if (auto __cmp_res = std::__load_vector<__vec>(__first) == __values; std::__any_of(__cmp_res)) {
+      if (auto __cmp_res = std::__simd_compare_eq(std::__load_vector<__vec>(__first), __values); std::__any_of(__cmp_res)) {
         return __first + std::__find_first_set(__cmp_res);
       }
       __first += __vec_size;
@@ -105,7 +105,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last,
     // (last - vector_size) to check the remaining elements
     if (static_cast<size_t>(__first - __orig_first) >= __vec_size) {
       __first = __last - __vec_size;
-      return __first + std::__find_first_set(std::__load_vector<__vec>(__first) == __values);
+      return __first + std::__find_first_set(std::__simd_compare_eq(std::__load_vector<__vec>(__first), __values));
     }
   }
 
diff --git a/libcxx/include/__algorithm/mismatch.h b/libcxx/include/__algorithm/mismatch.h
index 7111cd9398838..aba1e111ce470 100644
--- a/libcxx/include/__algorithm/mismatch.h
+++ b/libcxx/include/__algorithm/mismatch.h
@@ -78,7 +78,7 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
       }
 
       for (size_t __i = 0; __i != __unroll_count; ++__i) {
-        if (auto __cmp_res = __lhs[__i] == __rhs[__i]; !std::__all_of(__cmp_res)) {
+        if (auto __cmp_res = std::__simd_compare_eq(__lhs[__i], __rhs[__i]); !std::__all_of(__cmp_res)) {
           auto __offset = __i * __vec_size + std::__find_first_not_set(__cmp_res);
           return {__first1 + __offset, __first2 + __offset};
         }
@@ -90,7 +90,7 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
 
     // check the remaining 0-3 vectors
     while (static_cast<size_t>(__last1 - __first1) >= __vec_size) {
-      if (auto __cmp_res = std::__load_vector<__vec>(__first1) == std::__load_vector<__vec>(__first2);
+      if (auto __cmp_res = std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2));
           !std::__all_of(__cmp_res)) {
         auto __offset = std::__find_first_not_set(__cmp_res);
         return {__first1 + __offset, __first2 + __offset};
@@ -108,7 +108,7 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
       __first1 = __last1 - __vec_size;
       __first2 = __last2 - __vec_size;
       auto __offset =
-          std::__find_first_not_set(std::__load_vector<__vec>(__first1) == std::__load_vector<__vec>(__first2));
+          std::__find_first_not_set(std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2)));
       return {__first1 + __offset, __first2 + __offset};
     } // else loop over the elements individually
   }
diff --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index c47f79ac7f1dd..95da2066f44ae 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -25,8 +25,13 @@
 _LIBCPP_PUSH_MACROS
 #include <__undef_macros>
 
-// TODO: Find out how altivec changes things and allow vectorizations there too.
-#if _LIBCPP_STD_VER >= 14 && defined(_LIBCPP_COMPILER_CLANG_BASED) && !defined(__ALTIVEC__)
+// AltiVec changes the semantics of vector comparisons: in `-faltivec-src-compat=xl`
+// mode, == on vector types yields a scalar bool rather than an element-wise mask,
+// which silently breaks the mask-based algorithms below. We enable vectorization on
+// AIX (where we can enforce the correct mode via a static_assert) but leave other
+// AltiVec platforms disabled until they are explicitly validated.
+#if _LIBCPP_STD_VER >= 14 && defined(_LIBCPP_COMPILER_CLANG_BASED) &&                                                 \
+    (!defined(__ALTIVEC__) || defined(_AIX))
 #  define _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS 1
 #else
 #  define _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS 0
@@ -76,7 +81,7 @@ using __get_as_integer_type_t _LIBCPP_NODEBUG = typename __get_as_integer_type_i
 #  if defined(__AVX__) || defined(__MVS__)
 template <class _Tp>
 inline constexpr size_t __native_vector_size = 32 / sizeof(_Tp);
-#  elif defined(__SSE__) || defined(__ARM_NEON)
+#  elif defined(__SSE__) || defined(__ARM_NEON) || defined(__ALTIVEC__)
 template <class _Tp>
 inline constexpr size_t __native_vector_size = 16 / sizeof(_Tp);
 #  elif defined(__MMX__)
@@ -126,6 +131,29 @@ template <class _VecT, size_t _Np, class _Iter>
 }
 _LIBCPP_DIAGNOSTIC_POP
 
+// On targets with Altivec (PowerPC), the == operator on __ext_vector_type__
+// vectors produces a deprecated vector bool result under the old XL compat mode.
+// Centralise the comparison here so the suppression is in one place.
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-altivec-src-compat")
+template <class _Tp, size_t _Np>
+[[__nodiscard__]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI __simd_vector<_Tp, _Np>
+__simd_compare_eq(__simd_vector<_Tp, _Np> __lhs, __simd_vector<_Tp, _Np> __rhs) noexcept {
+  return __lhs == __rhs;
+}
+_LIBCPP_DIAGNOSTIC_POP
+
+#  if defined(__ALTIVEC__)
+// In -faltivec-src-compat=xl mode, == on vector types returns a scalar bool,
+// which silently breaks the mask-based algorithms below. Refuse to compile in
+// that mode rather than produce wrong results.
+static_assert(sizeof(std::__simd_compare_eq(std::declval<__simd_vector<int, 4>>(),
+                                             std::declval<__simd_vector<int, 4>>())) ==
+                  sizeof(__simd_vector<int, 4>),
+              "libc++'s vectorized algorithms require element-wise vector comparison semantics. "
+              "Compile with -faltivec-src-compat=mixed or -faltivec-src-compat=gcc (not =xl).");
+#  endif
+
 template <class _Tp, size_t _Np>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __any_of(__simd_vector<_Tp, _Np> __vec) noexcept {
   return __builtin_reduce_or(__builtin_convertvector(__vec, __simd_vector<bool, _Np>));
diff --git a/libcxx/include/__cxx03/__algorithm/simd_utils.h b/libcxx/include/__cxx03/__algorithm/simd_utils.h
index 7b0e825afaa1f..d61a9f0524486 100644
--- a/libcxx/include/__cxx03/__algorithm/simd_utils.h
+++ b/libcxx/include/__cxx03/__algorithm/simd_utils.h
@@ -26,7 +26,8 @@
 _LIBCPP_PUSH_MACROS
 #include <__cxx03/__undef_macros>
 
-// TODO: Find out how altivec changes things and allow vectorizations there too.
+// The __cxx03 layer is a frozen C++03 ABI shim. Vectorization is handled by
+// the standard headers; do not enable it here.
 #define _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS 0
 
 #if _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS && !defined(__OPTIMIZE_SIZE__)
diff --git a/libcxx/include/__locale_dir/num.h b/libcxx/include/__locale_dir/num.h
index 5b3b917384121..792b24e72dd09 100644
--- a/libcxx/include/__locale_dir/num.h
+++ b/libcxx/include/__locale_dir/num.h
@@ -97,7 +97,7 @@ struct __num_get : protected __num_get_base {
       _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
       using __vec   = __simd_vector<char, 32>;
       __vec __cmp   = std::__partial_load<__vec, __int_chr_cnt>(__atoms);
-      auto __res    = __vec(__val) == __cmp;
+      auto __res    = std::__simd_compare_eq(__vec(__val), __cmp);
       if (std::__none_of(__res))
         return __int_chr_cnt;
       return std::min(__int_chr_cnt, std::__find_first_set(__res));
diff --git a/libcxx/test/libcxx/algorithms/vectorization.compile.pass.cpp b/libcxx/test/libcxx/algorithms/vectorization.compile.pass.cpp
index 109fc78507cc4..2b6a794652630 100644
--- a/libcxx/test/libcxx/algorithms/vectorization.compile.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/vectorization.compile.pass.cpp
@@ -12,9 +12,6 @@
 // We don't vectorize algorithms before C++14
 // XFAIL: c++03, c++11
 
-// We don't vectorize algorithms on AIX right now.
-// XFAIL: target={{.+}}-aix{{.*}}
-
 // This test ensures that we enable the vectorization of algorithms on the expected
 // platforms.
 

>From 9380904348d900771216519ecf3e8e03b5450896 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Tue, 7 Jul 2026 02:08:19 +0530
Subject: [PATCH 2/3] [libcxx] use decltype for __simd_compare_eq return type
 on Altivec targets

---
 libcxx/include/__algorithm/find.h       |  3 ++-
 libcxx/include/__algorithm/mismatch.h   |  7 ++++---
 libcxx/include/__algorithm/simd_utils.h | 13 +++++++------
 3 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__algorithm/find.h b/libcxx/include/__algorithm/find.h
index 94087fb4afe78..c1c8f81bf3144 100644
--- a/libcxx/include/__algorithm/find.h
+++ b/libcxx/include/__algorithm/find.h
@@ -92,7 +92,8 @@ _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last,
 
     // check the remaining 0-3 vectors
     while (static_cast<size_t>(__last - __first) >= __vec_size) {
-      if (auto __cmp_res = std::__simd_compare_eq(std::__load_vector<__vec>(__first), __values); std::__any_of(__cmp_res)) {
+      if (auto __cmp_res = std::__simd_compare_eq(std::__load_vector<__vec>(__first), __values);
+          std::__any_of(__cmp_res)) {
         return __first + std::__find_first_set(__cmp_res);
       }
       __first += __vec_size;
diff --git a/libcxx/include/__algorithm/mismatch.h b/libcxx/include/__algorithm/mismatch.h
index aba1e111ce470..f6e65822549b1 100644
--- a/libcxx/include/__algorithm/mismatch.h
+++ b/libcxx/include/__algorithm/mismatch.h
@@ -90,7 +90,8 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
 
     // check the remaining 0-3 vectors
     while (static_cast<size_t>(__last1 - __first1) >= __vec_size) {
-      if (auto __cmp_res = std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2));
+      if (auto __cmp_res =
+              std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2));
           !std::__all_of(__cmp_res)) {
         auto __offset = std::__find_first_not_set(__cmp_res);
         return {__first1 + __offset, __first2 + __offset};
@@ -107,8 +108,8 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
     if (static_cast<size_t>(__first1 - __orig_first1) >= __vec_size) {
       __first1 = __last1 - __vec_size;
       __first2 = __last2 - __vec_size;
-      auto __offset =
-          std::__find_first_not_set(std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2)));
+      auto __offset = std::__find_first_not_set(
+          std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2)));
       return {__first1 + __offset, __first2 + __offset};
     } // else loop over the elements individually
   }
diff --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index 95da2066f44ae..44530d6cd8cae 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -30,8 +30,7 @@ _LIBCPP_PUSH_MACROS
 // which silently breaks the mask-based algorithms below. We enable vectorization on
 // AIX (where we can enforce the correct mode via a static_assert) but leave other
 // AltiVec platforms disabled until they are explicitly validated.
-#if _LIBCPP_STD_VER >= 14 && defined(_LIBCPP_COMPILER_CLANG_BASED) &&                                                 \
-    (!defined(__ALTIVEC__) || defined(_AIX))
+#if _LIBCPP_STD_VER >= 14 && defined(_LIBCPP_COMPILER_CLANG_BASED) && (!defined(__ALTIVEC__) || defined(_AIX))
 #  define _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS 1
 #else
 #  define _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS 0
@@ -134,11 +133,14 @@ _LIBCPP_DIAGNOSTIC_POP
 // On targets with Altivec (PowerPC), the == operator on __ext_vector_type__
 // vectors produces a deprecated vector bool result under the old XL compat mode.
 // Centralise the comparison here so the suppression is in one place.
+// The return type is deduced via decltype because under Altivec the comparison
+// result element type differs from the operand element type (e.g. char16_t
+// operands yield a short mask vector).
 _LIBCPP_DIAGNOSTIC_PUSH
 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-altivec-src-compat")
 template <class _Tp, size_t _Np>
-[[__nodiscard__]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI __simd_vector<_Tp, _Np>
-__simd_compare_eq(__simd_vector<_Tp, _Np> __lhs, __simd_vector<_Tp, _Np> __rhs) noexcept {
+[[__nodiscard__]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI auto
+__simd_compare_eq(__simd_vector<_Tp, _Np> __lhs, __simd_vector<_Tp, _Np> __rhs) noexcept -> decltype(__lhs == __rhs) {
   return __lhs == __rhs;
 }
 _LIBCPP_DIAGNOSTIC_POP
@@ -148,8 +150,7 @@ _LIBCPP_DIAGNOSTIC_POP
 // which silently breaks the mask-based algorithms below. Refuse to compile in
 // that mode rather than produce wrong results.
 static_assert(sizeof(std::__simd_compare_eq(std::declval<__simd_vector<int, 4>>(),
-                                             std::declval<__simd_vector<int, 4>>())) ==
-                  sizeof(__simd_vector<int, 4>),
+                                            std::declval<__simd_vector<int, 4>>())) == sizeof(__simd_vector<int, 4>),
               "libc++'s vectorized algorithms require element-wise vector comparison semantics. "
               "Compile with -faltivec-src-compat=mixed or -faltivec-src-compat=gcc (not =xl).");
 #  endif

>From ed766f8b8eb0f7c21e511fe77cec106a009ebb55 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Tue, 7 Jul 2026 02:34:57 +0530
Subject: [PATCH 3/3] [libcxx][AIX] add -faltivec-src-compat=mixed to fix
 vector comparisons

---
 libcxx/CMakeLists.txt                   |  3 +++
 libcxx/include/__algorithm/find.h       |  2 +-
 libcxx/include/__algorithm/mismatch.h   |  2 +-
 libcxx/include/__algorithm/simd_utils.h | 22 +++++++++-------------
 libcxx/include/__locale_dir/num.h       |  2 +-
 5 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..4f86542020207 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -532,6 +532,9 @@ include(HandleLibcxxFlags)
 
 if ("${CMAKE_SYSTEM_NAME}" MATCHES "AIX")
   add_flags_if_supported("-mdefault-visibility-export-mapping=explicit")
+  # Required for __ext_vector_type__ comparisons to produce element-wise masks, not scalar bools (=xl default).
+  # Appended directly to LIBCXX_COMPILE_FLAGS so it applies only to the library sources, not the test suite.
+  list(APPEND LIBCXX_COMPILE_FLAGS "-faltivec-src-compat=mixed")
   set(CMAKE_AIX_EXPORT_ALL_SYMBOLS OFF)
 endif()
 
diff --git a/libcxx/include/__algorithm/find.h b/libcxx/include/__algorithm/find.h
index c1c8f81bf3144..343aec518f358 100644
--- a/libcxx/include/__algorithm/find.h
+++ b/libcxx/include/__algorithm/find.h
@@ -66,7 +66,7 @@ __find(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
 template <class _Tp, class _Up>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI
 _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last, _Up __value) {
-  if (!__libcpp_is_constant_evaluated()) {
+  if (!__libcpp_is_constant_evaluated() && std::__altivec_has_element_wise_compare) {
     constexpr size_t __unroll_count = 4;
     constexpr size_t __vec_size     = __native_vector_size<_Tp>;
     using __vec                     = __simd_vector<_Tp, __vec_size>;
diff --git a/libcxx/include/__algorithm/mismatch.h b/libcxx/include/__algorithm/mismatch.h
index f6e65822549b1..ed9b8862953ba 100644
--- a/libcxx/include/__algorithm/mismatch.h
+++ b/libcxx/include/__algorithm/mismatch.h
@@ -65,7 +65,7 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
   constexpr size_t __vec_size     = __native_vector_size<__value_type>;
   using __vec                     = __simd_vector<__value_type, __vec_size>;
 
-  if (!__libcpp_is_constant_evaluated()) {
+  if (!__libcpp_is_constant_evaluated() && std::__altivec_has_element_wise_compare) {
     auto __orig_first1 = __first1;
     auto __last2       = __first2 + (__last1 - __first1);
     while (static_cast<size_t>(__last1 - __first1) >= __unroll_count * __vec_size) [[__unlikely__]] {
diff --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index 44530d6cd8cae..040bdf9eb3c8c 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -130,12 +130,8 @@ template <class _VecT, size_t _Np, class _Iter>
 }
 _LIBCPP_DIAGNOSTIC_POP
 
-// On targets with Altivec (PowerPC), the == operator on __ext_vector_type__
-// vectors produces a deprecated vector bool result under the old XL compat mode.
-// Centralise the comparison here so the suppression is in one place.
-// The return type is deduced via decltype because under Altivec the comparison
-// result element type differs from the operand element type (e.g. char16_t
-// operands yield a short mask vector).
+// The return type uses decltype because under Altivec the comparison result
+// element type differs from the operand type (e.g. char16_t -> short mask).
 _LIBCPP_DIAGNOSTIC_PUSH
 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-altivec-src-compat")
 template <class _Tp, size_t _Np>
@@ -145,14 +141,14 @@ __simd_compare_eq(__simd_vector<_Tp, _Np> __lhs, __simd_vector<_Tp, _Np> __rhs)
 }
 _LIBCPP_DIAGNOSTIC_POP
 
+// On non-Altivec targets this is always true. On Altivec targets it is false
+// when -faltivec-src-compat=xl is active (== returns scalar bool, not a mask).
 #  if defined(__ALTIVEC__)
-// In -faltivec-src-compat=xl mode, == on vector types returns a scalar bool,
-// which silently breaks the mask-based algorithms below. Refuse to compile in
-// that mode rather than produce wrong results.
-static_assert(sizeof(std::__simd_compare_eq(std::declval<__simd_vector<int, 4>>(),
-                                            std::declval<__simd_vector<int, 4>>())) == sizeof(__simd_vector<int, 4>),
-              "libc++'s vectorized algorithms require element-wise vector comparison semantics. "
-              "Compile with -faltivec-src-compat=mixed or -faltivec-src-compat=gcc (not =xl).");
+inline constexpr bool __altivec_has_element_wise_compare =
+    sizeof(std::__simd_compare_eq(std::declval<__simd_vector<int, 4>>(),
+                                  std::declval<__simd_vector<int, 4>>())) == sizeof(__simd_vector<int, 4>);
+#  else
+inline constexpr bool __altivec_has_element_wise_compare = true;
 #  endif
 
 template <class _Tp, size_t _Np>
diff --git a/libcxx/include/__locale_dir/num.h b/libcxx/include/__locale_dir/num.h
index 792b24e72dd09..d1927be96f734 100644
--- a/libcxx/include/__locale_dir/num.h
+++ b/libcxx/include/__locale_dir/num.h
@@ -91,7 +91,7 @@ struct __num_get : protected __num_get_base {
   _LIBCPP_HIDE_FROM_ABI static ptrdiff_t __atoms_offset(const _CharT* __atoms, _CharT __val) {
     // TODO: Remove the manual vectorization once https://llvm.org/PR168551 is resolved
 #  if _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS
-    if constexpr (is_same<_CharT, char>::value) {
+    if constexpr (is_same<_CharT, char>::value && std::__altivec_has_element_wise_compare) {
       // TODO(LLVM 24): This can be removed, since -Wpsabi doesn't warn on [[gnu::always_inline]] functions anymore.
       _LIBCPP_DIAGNOSTIC_PUSH
       _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")


From libcxx-commits at lists.llvm.org  Mon Jul  6 22:12:44 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Mon, 06 Jul 2026 22:12:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a4c8acc.7270d2fd.2d4879.6617@mx.google.com>


https://github.com/smallp-o-p edited https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Mon Jul  6 22:18:17 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 22:18:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] enable AltiVec vectorization
 with element-wise comparison wrapper (PR #207832)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207832@github.com>
Message-ID: <6a4c8c19.d5797644.338ca1.3e0c@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/207832

>From 451c89af5f6abf5927e6cbf0620e78479399694a Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 6 Jul 2026 19:37:14 +0530
Subject: [PATCH 1/4] [libcxx] enable AltiVec vectorization with element-wise
 comparison wrapper

---
 libcxx/include/__algorithm/find.h             |  6 ++--
 libcxx/include/__algorithm/mismatch.h         |  6 ++--
 libcxx/include/__algorithm/simd_utils.h       | 34 +++++++++++++++++--
 .../include/__cxx03/__algorithm/simd_utils.h  |  3 +-
 libcxx/include/__locale_dir/num.h             |  2 +-
 .../algorithms/vectorization.compile.pass.cpp |  3 --
 6 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__algorithm/find.h b/libcxx/include/__algorithm/find.h
index f677fb2c7392d..94087fb4afe78 100644
--- a/libcxx/include/__algorithm/find.h
+++ b/libcxx/include/__algorithm/find.h
@@ -81,7 +81,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last,
         __lhs[__i] = std::__load_vector<__vec>(__first + __i * __vec_size);
 
       for (size_t __i = 0; __i != __unroll_count; ++__i) {
-        if (auto __cmp_res = __lhs[__i] == __values; std::__any_of(__cmp_res)) {
+        if (auto __cmp_res = std::__simd_compare_eq(__lhs[__i], __values); std::__any_of(__cmp_res)) {
           auto __offset = __i * __vec_size + std::__find_first_set(__cmp_res);
           return __first + __offset;
         }
@@ -92,7 +92,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last,
 
     // check the remaining 0-3 vectors
     while (static_cast<size_t>(__last - __first) >= __vec_size) {
-      if (auto __cmp_res = std::__load_vector<__vec>(__first) == __values; std::__any_of(__cmp_res)) {
+      if (auto __cmp_res = std::__simd_compare_eq(std::__load_vector<__vec>(__first), __values); std::__any_of(__cmp_res)) {
         return __first + std::__find_first_set(__cmp_res);
       }
       __first += __vec_size;
@@ -105,7 +105,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last,
     // (last - vector_size) to check the remaining elements
     if (static_cast<size_t>(__first - __orig_first) >= __vec_size) {
       __first = __last - __vec_size;
-      return __first + std::__find_first_set(std::__load_vector<__vec>(__first) == __values);
+      return __first + std::__find_first_set(std::__simd_compare_eq(std::__load_vector<__vec>(__first), __values));
     }
   }
 
diff --git a/libcxx/include/__algorithm/mismatch.h b/libcxx/include/__algorithm/mismatch.h
index 7111cd9398838..aba1e111ce470 100644
--- a/libcxx/include/__algorithm/mismatch.h
+++ b/libcxx/include/__algorithm/mismatch.h
@@ -78,7 +78,7 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
       }
 
       for (size_t __i = 0; __i != __unroll_count; ++__i) {
-        if (auto __cmp_res = __lhs[__i] == __rhs[__i]; !std::__all_of(__cmp_res)) {
+        if (auto __cmp_res = std::__simd_compare_eq(__lhs[__i], __rhs[__i]); !std::__all_of(__cmp_res)) {
           auto __offset = __i * __vec_size + std::__find_first_not_set(__cmp_res);
           return {__first1 + __offset, __first2 + __offset};
         }
@@ -90,7 +90,7 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
 
     // check the remaining 0-3 vectors
     while (static_cast<size_t>(__last1 - __first1) >= __vec_size) {
-      if (auto __cmp_res = std::__load_vector<__vec>(__first1) == std::__load_vector<__vec>(__first2);
+      if (auto __cmp_res = std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2));
           !std::__all_of(__cmp_res)) {
         auto __offset = std::__find_first_not_set(__cmp_res);
         return {__first1 + __offset, __first2 + __offset};
@@ -108,7 +108,7 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
       __first1 = __last1 - __vec_size;
       __first2 = __last2 - __vec_size;
       auto __offset =
-          std::__find_first_not_set(std::__load_vector<__vec>(__first1) == std::__load_vector<__vec>(__first2));
+          std::__find_first_not_set(std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2)));
       return {__first1 + __offset, __first2 + __offset};
     } // else loop over the elements individually
   }
diff --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index c47f79ac7f1dd..95da2066f44ae 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -25,8 +25,13 @@
 _LIBCPP_PUSH_MACROS
 #include <__undef_macros>
 
-// TODO: Find out how altivec changes things and allow vectorizations there too.
-#if _LIBCPP_STD_VER >= 14 && defined(_LIBCPP_COMPILER_CLANG_BASED) && !defined(__ALTIVEC__)
+// AltiVec changes the semantics of vector comparisons: in `-faltivec-src-compat=xl`
+// mode, == on vector types yields a scalar bool rather than an element-wise mask,
+// which silently breaks the mask-based algorithms below. We enable vectorization on
+// AIX (where we can enforce the correct mode via a static_assert) but leave other
+// AltiVec platforms disabled until they are explicitly validated.
+#if _LIBCPP_STD_VER >= 14 && defined(_LIBCPP_COMPILER_CLANG_BASED) &&                                                 \
+    (!defined(__ALTIVEC__) || defined(_AIX))
 #  define _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS 1
 #else
 #  define _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS 0
@@ -76,7 +81,7 @@ using __get_as_integer_type_t _LIBCPP_NODEBUG = typename __get_as_integer_type_i
 #  if defined(__AVX__) || defined(__MVS__)
 template <class _Tp>
 inline constexpr size_t __native_vector_size = 32 / sizeof(_Tp);
-#  elif defined(__SSE__) || defined(__ARM_NEON)
+#  elif defined(__SSE__) || defined(__ARM_NEON) || defined(__ALTIVEC__)
 template <class _Tp>
 inline constexpr size_t __native_vector_size = 16 / sizeof(_Tp);
 #  elif defined(__MMX__)
@@ -126,6 +131,29 @@ template <class _VecT, size_t _Np, class _Iter>
 }
 _LIBCPP_DIAGNOSTIC_POP
 
+// On targets with Altivec (PowerPC), the == operator on __ext_vector_type__
+// vectors produces a deprecated vector bool result under the old XL compat mode.
+// Centralise the comparison here so the suppression is in one place.
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-altivec-src-compat")
+template <class _Tp, size_t _Np>
+[[__nodiscard__]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI __simd_vector<_Tp, _Np>
+__simd_compare_eq(__simd_vector<_Tp, _Np> __lhs, __simd_vector<_Tp, _Np> __rhs) noexcept {
+  return __lhs == __rhs;
+}
+_LIBCPP_DIAGNOSTIC_POP
+
+#  if defined(__ALTIVEC__)
+// In -faltivec-src-compat=xl mode, == on vector types returns a scalar bool,
+// which silently breaks the mask-based algorithms below. Refuse to compile in
+// that mode rather than produce wrong results.
+static_assert(sizeof(std::__simd_compare_eq(std::declval<__simd_vector<int, 4>>(),
+                                             std::declval<__simd_vector<int, 4>>())) ==
+                  sizeof(__simd_vector<int, 4>),
+              "libc++'s vectorized algorithms require element-wise vector comparison semantics. "
+              "Compile with -faltivec-src-compat=mixed or -faltivec-src-compat=gcc (not =xl).");
+#  endif
+
 template <class _Tp, size_t _Np>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __any_of(__simd_vector<_Tp, _Np> __vec) noexcept {
   return __builtin_reduce_or(__builtin_convertvector(__vec, __simd_vector<bool, _Np>));
diff --git a/libcxx/include/__cxx03/__algorithm/simd_utils.h b/libcxx/include/__cxx03/__algorithm/simd_utils.h
index 7b0e825afaa1f..d61a9f0524486 100644
--- a/libcxx/include/__cxx03/__algorithm/simd_utils.h
+++ b/libcxx/include/__cxx03/__algorithm/simd_utils.h
@@ -26,7 +26,8 @@
 _LIBCPP_PUSH_MACROS
 #include <__cxx03/__undef_macros>
 
-// TODO: Find out how altivec changes things and allow vectorizations there too.
+// The __cxx03 layer is a frozen C++03 ABI shim. Vectorization is handled by
+// the standard headers; do not enable it here.
 #define _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS 0
 
 #if _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS && !defined(__OPTIMIZE_SIZE__)
diff --git a/libcxx/include/__locale_dir/num.h b/libcxx/include/__locale_dir/num.h
index 5b3b917384121..792b24e72dd09 100644
--- a/libcxx/include/__locale_dir/num.h
+++ b/libcxx/include/__locale_dir/num.h
@@ -97,7 +97,7 @@ struct __num_get : protected __num_get_base {
       _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
       using __vec   = __simd_vector<char, 32>;
       __vec __cmp   = std::__partial_load<__vec, __int_chr_cnt>(__atoms);
-      auto __res    = __vec(__val) == __cmp;
+      auto __res    = std::__simd_compare_eq(__vec(__val), __cmp);
       if (std::__none_of(__res))
         return __int_chr_cnt;
       return std::min(__int_chr_cnt, std::__find_first_set(__res));
diff --git a/libcxx/test/libcxx/algorithms/vectorization.compile.pass.cpp b/libcxx/test/libcxx/algorithms/vectorization.compile.pass.cpp
index 109fc78507cc4..2b6a794652630 100644
--- a/libcxx/test/libcxx/algorithms/vectorization.compile.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/vectorization.compile.pass.cpp
@@ -12,9 +12,6 @@
 // We don't vectorize algorithms before C++14
 // XFAIL: c++03, c++11
 
-// We don't vectorize algorithms on AIX right now.
-// XFAIL: target={{.+}}-aix{{.*}}
-
 // This test ensures that we enable the vectorization of algorithms on the expected
 // platforms.
 

>From 9380904348d900771216519ecf3e8e03b5450896 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Tue, 7 Jul 2026 02:08:19 +0530
Subject: [PATCH 2/4] [libcxx] use decltype for __simd_compare_eq return type
 on Altivec targets

---
 libcxx/include/__algorithm/find.h       |  3 ++-
 libcxx/include/__algorithm/mismatch.h   |  7 ++++---
 libcxx/include/__algorithm/simd_utils.h | 13 +++++++------
 3 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__algorithm/find.h b/libcxx/include/__algorithm/find.h
index 94087fb4afe78..c1c8f81bf3144 100644
--- a/libcxx/include/__algorithm/find.h
+++ b/libcxx/include/__algorithm/find.h
@@ -92,7 +92,8 @@ _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last,
 
     // check the remaining 0-3 vectors
     while (static_cast<size_t>(__last - __first) >= __vec_size) {
-      if (auto __cmp_res = std::__simd_compare_eq(std::__load_vector<__vec>(__first), __values); std::__any_of(__cmp_res)) {
+      if (auto __cmp_res = std::__simd_compare_eq(std::__load_vector<__vec>(__first), __values);
+          std::__any_of(__cmp_res)) {
         return __first + std::__find_first_set(__cmp_res);
       }
       __first += __vec_size;
diff --git a/libcxx/include/__algorithm/mismatch.h b/libcxx/include/__algorithm/mismatch.h
index aba1e111ce470..f6e65822549b1 100644
--- a/libcxx/include/__algorithm/mismatch.h
+++ b/libcxx/include/__algorithm/mismatch.h
@@ -90,7 +90,8 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
 
     // check the remaining 0-3 vectors
     while (static_cast<size_t>(__last1 - __first1) >= __vec_size) {
-      if (auto __cmp_res = std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2));
+      if (auto __cmp_res =
+              std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2));
           !std::__all_of(__cmp_res)) {
         auto __offset = std::__find_first_not_set(__cmp_res);
         return {__first1 + __offset, __first2 + __offset};
@@ -107,8 +108,8 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
     if (static_cast<size_t>(__first1 - __orig_first1) >= __vec_size) {
       __first1 = __last1 - __vec_size;
       __first2 = __last2 - __vec_size;
-      auto __offset =
-          std::__find_first_not_set(std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2)));
+      auto __offset = std::__find_first_not_set(
+          std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2)));
       return {__first1 + __offset, __first2 + __offset};
     } // else loop over the elements individually
   }
diff --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index 95da2066f44ae..44530d6cd8cae 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -30,8 +30,7 @@ _LIBCPP_PUSH_MACROS
 // which silently breaks the mask-based algorithms below. We enable vectorization on
 // AIX (where we can enforce the correct mode via a static_assert) but leave other
 // AltiVec platforms disabled until they are explicitly validated.
-#if _LIBCPP_STD_VER >= 14 && defined(_LIBCPP_COMPILER_CLANG_BASED) &&                                                 \
-    (!defined(__ALTIVEC__) || defined(_AIX))
+#if _LIBCPP_STD_VER >= 14 && defined(_LIBCPP_COMPILER_CLANG_BASED) && (!defined(__ALTIVEC__) || defined(_AIX))
 #  define _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS 1
 #else
 #  define _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS 0
@@ -134,11 +133,14 @@ _LIBCPP_DIAGNOSTIC_POP
 // On targets with Altivec (PowerPC), the == operator on __ext_vector_type__
 // vectors produces a deprecated vector bool result under the old XL compat mode.
 // Centralise the comparison here so the suppression is in one place.
+// The return type is deduced via decltype because under Altivec the comparison
+// result element type differs from the operand element type (e.g. char16_t
+// operands yield a short mask vector).
 _LIBCPP_DIAGNOSTIC_PUSH
 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-altivec-src-compat")
 template <class _Tp, size_t _Np>
-[[__nodiscard__]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI __simd_vector<_Tp, _Np>
-__simd_compare_eq(__simd_vector<_Tp, _Np> __lhs, __simd_vector<_Tp, _Np> __rhs) noexcept {
+[[__nodiscard__]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI auto
+__simd_compare_eq(__simd_vector<_Tp, _Np> __lhs, __simd_vector<_Tp, _Np> __rhs) noexcept -> decltype(__lhs == __rhs) {
   return __lhs == __rhs;
 }
 _LIBCPP_DIAGNOSTIC_POP
@@ -148,8 +150,7 @@ _LIBCPP_DIAGNOSTIC_POP
 // which silently breaks the mask-based algorithms below. Refuse to compile in
 // that mode rather than produce wrong results.
 static_assert(sizeof(std::__simd_compare_eq(std::declval<__simd_vector<int, 4>>(),
-                                             std::declval<__simd_vector<int, 4>>())) ==
-                  sizeof(__simd_vector<int, 4>),
+                                            std::declval<__simd_vector<int, 4>>())) == sizeof(__simd_vector<int, 4>),
               "libc++'s vectorized algorithms require element-wise vector comparison semantics. "
               "Compile with -faltivec-src-compat=mixed or -faltivec-src-compat=gcc (not =xl).");
 #  endif

>From ed766f8b8eb0f7c21e511fe77cec106a009ebb55 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Tue, 7 Jul 2026 02:34:57 +0530
Subject: [PATCH 3/4] [libcxx][AIX] add -faltivec-src-compat=mixed to fix
 vector comparisons

---
 libcxx/CMakeLists.txt                   |  3 +++
 libcxx/include/__algorithm/find.h       |  2 +-
 libcxx/include/__algorithm/mismatch.h   |  2 +-
 libcxx/include/__algorithm/simd_utils.h | 22 +++++++++-------------
 libcxx/include/__locale_dir/num.h       |  2 +-
 5 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..4f86542020207 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -532,6 +532,9 @@ include(HandleLibcxxFlags)
 
 if ("${CMAKE_SYSTEM_NAME}" MATCHES "AIX")
   add_flags_if_supported("-mdefault-visibility-export-mapping=explicit")
+  # Required for __ext_vector_type__ comparisons to produce element-wise masks, not scalar bools (=xl default).
+  # Appended directly to LIBCXX_COMPILE_FLAGS so it applies only to the library sources, not the test suite.
+  list(APPEND LIBCXX_COMPILE_FLAGS "-faltivec-src-compat=mixed")
   set(CMAKE_AIX_EXPORT_ALL_SYMBOLS OFF)
 endif()
 
diff --git a/libcxx/include/__algorithm/find.h b/libcxx/include/__algorithm/find.h
index c1c8f81bf3144..343aec518f358 100644
--- a/libcxx/include/__algorithm/find.h
+++ b/libcxx/include/__algorithm/find.h
@@ -66,7 +66,7 @@ __find(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
 template <class _Tp, class _Up>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI
 _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last, _Up __value) {
-  if (!__libcpp_is_constant_evaluated()) {
+  if (!__libcpp_is_constant_evaluated() && std::__altivec_has_element_wise_compare) {
     constexpr size_t __unroll_count = 4;
     constexpr size_t __vec_size     = __native_vector_size<_Tp>;
     using __vec                     = __simd_vector<_Tp, __vec_size>;
diff --git a/libcxx/include/__algorithm/mismatch.h b/libcxx/include/__algorithm/mismatch.h
index f6e65822549b1..ed9b8862953ba 100644
--- a/libcxx/include/__algorithm/mismatch.h
+++ b/libcxx/include/__algorithm/mismatch.h
@@ -65,7 +65,7 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
   constexpr size_t __vec_size     = __native_vector_size<__value_type>;
   using __vec                     = __simd_vector<__value_type, __vec_size>;
 
-  if (!__libcpp_is_constant_evaluated()) {
+  if (!__libcpp_is_constant_evaluated() && std::__altivec_has_element_wise_compare) {
     auto __orig_first1 = __first1;
     auto __last2       = __first2 + (__last1 - __first1);
     while (static_cast<size_t>(__last1 - __first1) >= __unroll_count * __vec_size) [[__unlikely__]] {
diff --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index 44530d6cd8cae..040bdf9eb3c8c 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -130,12 +130,8 @@ template <class _VecT, size_t _Np, class _Iter>
 }
 _LIBCPP_DIAGNOSTIC_POP
 
-// On targets with Altivec (PowerPC), the == operator on __ext_vector_type__
-// vectors produces a deprecated vector bool result under the old XL compat mode.
-// Centralise the comparison here so the suppression is in one place.
-// The return type is deduced via decltype because under Altivec the comparison
-// result element type differs from the operand element type (e.g. char16_t
-// operands yield a short mask vector).
+// The return type uses decltype because under Altivec the comparison result
+// element type differs from the operand type (e.g. char16_t -> short mask).
 _LIBCPP_DIAGNOSTIC_PUSH
 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-altivec-src-compat")
 template <class _Tp, size_t _Np>
@@ -145,14 +141,14 @@ __simd_compare_eq(__simd_vector<_Tp, _Np> __lhs, __simd_vector<_Tp, _Np> __rhs)
 }
 _LIBCPP_DIAGNOSTIC_POP
 
+// On non-Altivec targets this is always true. On Altivec targets it is false
+// when -faltivec-src-compat=xl is active (== returns scalar bool, not a mask).
 #  if defined(__ALTIVEC__)
-// In -faltivec-src-compat=xl mode, == on vector types returns a scalar bool,
-// which silently breaks the mask-based algorithms below. Refuse to compile in
-// that mode rather than produce wrong results.
-static_assert(sizeof(std::__simd_compare_eq(std::declval<__simd_vector<int, 4>>(),
-                                            std::declval<__simd_vector<int, 4>>())) == sizeof(__simd_vector<int, 4>),
-              "libc++'s vectorized algorithms require element-wise vector comparison semantics. "
-              "Compile with -faltivec-src-compat=mixed or -faltivec-src-compat=gcc (not =xl).");
+inline constexpr bool __altivec_has_element_wise_compare =
+    sizeof(std::__simd_compare_eq(std::declval<__simd_vector<int, 4>>(),
+                                  std::declval<__simd_vector<int, 4>>())) == sizeof(__simd_vector<int, 4>);
+#  else
+inline constexpr bool __altivec_has_element_wise_compare = true;
 #  endif
 
 template <class _Tp, size_t _Np>
diff --git a/libcxx/include/__locale_dir/num.h b/libcxx/include/__locale_dir/num.h
index 792b24e72dd09..d1927be96f734 100644
--- a/libcxx/include/__locale_dir/num.h
+++ b/libcxx/include/__locale_dir/num.h
@@ -91,7 +91,7 @@ struct __num_get : protected __num_get_base {
   _LIBCPP_HIDE_FROM_ABI static ptrdiff_t __atoms_offset(const _CharT* __atoms, _CharT __val) {
     // TODO: Remove the manual vectorization once https://llvm.org/PR168551 is resolved
 #  if _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS
-    if constexpr (is_same<_CharT, char>::value) {
+    if constexpr (is_same<_CharT, char>::value && std::__altivec_has_element_wise_compare) {
       // TODO(LLVM 24): This can be removed, since -Wpsabi doesn't warn on [[gnu::always_inline]] functions anymore.
       _LIBCPP_DIAGNOSTIC_PUSH
       _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")

>From 2c04e251ed46c8044587ea6ef60586d3bd96baac Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Tue, 7 Jul 2026 10:48:04 +0530
Subject: [PATCH 4/4] [libcxx][AIX] use if constexpr to guard xl-mode vector
 comparison paths

---
 libcxx/include/__algorithm/find.h     | 4 +++-
 libcxx/include/__algorithm/mismatch.h | 4 +++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__algorithm/find.h b/libcxx/include/__algorithm/find.h
index 343aec518f358..b3d3d8edf7410 100644
--- a/libcxx/include/__algorithm/find.h
+++ b/libcxx/include/__algorithm/find.h
@@ -66,7 +66,8 @@ __find(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
 template <class _Tp, class _Up>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI
 _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last, _Up __value) {
-  if (!__libcpp_is_constant_evaluated() && std::__altivec_has_element_wise_compare) {
+  if (!__libcpp_is_constant_evaluated()) {
+    if constexpr (std::__altivec_has_element_wise_compare) {
     constexpr size_t __unroll_count = 4;
     constexpr size_t __vec_size     = __native_vector_size<_Tp>;
     using __vec                     = __simd_vector<_Tp, __vec_size>;
@@ -108,6 +109,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __find_vectorized(_Tp* __first, _Tp* __last,
       __first = __last - __vec_size;
       return __first + std::__find_first_set(std::__simd_compare_eq(std::__load_vector<__vec>(__first), __values));
     }
+    } // end if constexpr
   }
 
   __identity __proj;
diff --git a/libcxx/include/__algorithm/mismatch.h b/libcxx/include/__algorithm/mismatch.h
index ed9b8862953ba..c14f27650cf7a 100644
--- a/libcxx/include/__algorithm/mismatch.h
+++ b/libcxx/include/__algorithm/mismatch.h
@@ -65,7 +65,8 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
   constexpr size_t __vec_size     = __native_vector_size<__value_type>;
   using __vec                     = __simd_vector<__value_type, __vec_size>;
 
-  if (!__libcpp_is_constant_evaluated() && std::__altivec_has_element_wise_compare) {
+  if (!__libcpp_is_constant_evaluated()) {
+    if constexpr (std::__altivec_has_element_wise_compare) {
     auto __orig_first1 = __first1;
     auto __last2       = __first2 + (__last1 - __first1);
     while (static_cast<size_t>(__last1 - __first1) >= __unroll_count * __vec_size) [[__unlikely__]] {
@@ -112,6 +113,7 @@ __mismatch_vectorized(_Iter __first1, _Iter __last1, _Iter __first2) {
           std::__simd_compare_eq(std::__load_vector<__vec>(__first1), std::__load_vector<__vec>(__first2)));
       return {__first1 + __offset, __first2 + __offset};
     } // else loop over the elements individually
+    } // end if constexpr
   }
 
   __equal_to __pred;


From libcxx-commits at lists.llvm.org  Mon Jul  6 22:47:43 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 22:47:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`join_view` (PR #206838)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206838@github.com>
Message-ID: <6a4c92ff.27b3b003.fc443.e4e6@mx.google.com>


================
@@ -0,0 +1,73 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Test the libc++ extension that std::ranges::join_view and std::views::view are marked as [[nodiscard]].
+
+#include <functional>
+#include <ranges>
+#include <string>
+#include <utility>
+#include <vector>
+
+struct InnerView : std::ranges::view_interface<InnerView> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+
+struct View : std::ranges::view_interface<View> {
+  InnerView* begin();
+  const InnerView* begin() const;
+  volatile InnerView* end();
+  const volatile InnerView* end() const;
+};
----------------
frederick-vs-ja wrote:

I think it's fine to merge this PR now.

https://github.com/llvm/llvm-project/pull/206838

From libcxx-commits at lists.llvm.org  Mon Jul  6 22:47:49 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 22:47:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`join_view` (PR #206838)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206838@github.com>
Message-ID: <6a4c9305.d5797644.338ca1.7f9f@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/206838

From libcxx-commits at lists.llvm.org  Mon Jul  6 22:48:11 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 06 Jul 2026 22:48:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 8992cdf - [libc++][ranges] Applied
 [[nodiscard]] to `join_view` (#206838)
Message-ID: <6a4c931b.27b3b003.fc443.fa15@mx.google.com>


Author: Lucas Mellone
Date: 2026-07-07T13:48:07+08:00
New Revision: 8992cdf458f78c5ef9b018c92234658fc78cfd80

URL: https://github.com/llvm/llvm-project/commit/8992cdf458f78c5ef9b018c92234658fc78cfd80
DIFF: https://github.com/llvm/llvm-project/commit/8992cdf458f78c5ef9b018c92234658fc78cfd80.diff

LOG: [libc++][ranges] Applied [[nodiscard]] to `join_view` (#206838)

[[nodiscard]] should be applied to functions where discarding the return
value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.join

Towards https://github.com/llvm/llvm-project/issues/172124

Added: 
    libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp

Modified: 
    libcxx/include/__ranges/join_view.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index 364f056d8d2cf..f80b75c839d4a 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -100,15 +100,15 @@ class join_view : public view_interface<join_view<_View>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit join_view(_View __base) : __base_(std::move(__base)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       constexpr bool __use_const = __simple_view<_View> && is_reference_v<range_reference_t<_View>>;
       return __iterator<__use_const>{*this, ranges::begin(__base_)};
@@ -119,14 +119,14 @@ class join_view : public view_interface<join_view<_View>> {
   }
 
   template <class _V2 = _View>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<const _V2> && is_reference_v<range_reference_t<const _V2>> &&
              input_range<range_reference_t<const _V2>>
   {
     return __iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
     if constexpr (forward_range<_View> && is_reference_v<_InnerRange> && forward_range<_InnerRange> &&
                   common_range<_View> && common_range<_InnerRange>)
       return __iterator<__simple_view<_View>>{*this, ranges::end(__base_)};
@@ -135,7 +135,7 @@ class join_view : public view_interface<join_view<_View>> {
   }
 
   template <class _V2 = _View>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires forward_range<const _V2> && is_reference_v<range_reference_t<const _V2>> &&
              input_range<range_reference_t<const _V2>>
   {
@@ -276,7 +276,7 @@ struct join_view<_View>::__iterator final : public __join_view_iterator_category
     requires _Const && convertible_to<iterator_t<_View>, _Outer> && convertible_to<iterator_t<_InnerRange>, _Inner>
       : __outer_(std::move(__i.__outer_)), __inner_(std::move(__i.__inner_)), __parent_(__i.__parent_) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return **__inner_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return **__inner_; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Inner operator->() const
     requires __has_arrow<_Inner> && copyable<_Inner>
@@ -339,7 +339,7 @@ struct join_view<_View>::__iterator final : public __join_view_iterator_category
     return __x.__outer_ == __y.__outer_ && __x.__inner_ == __y.__inner_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
   iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__inner_))) {
     return ranges::iter_move(*__i.__inner_);
   }

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..6a7a93cbe5e81
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join/nodiscard.verify.cpp
@@ -0,0 +1,73 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Test the libc++ extension that std::ranges::join_view and std::views::view are marked as [[nodiscard]].
+
+#include <functional>
+#include <ranges>
+#include <string>
+#include <utility>
+#include <vector>
+
+struct InnerView : std::ranges::view_interface<InnerView> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+
+struct View : std::ranges::view_interface<View> {
+  InnerView* begin();
+  const InnerView* begin() const;
+  volatile InnerView* end();
+  const volatile InnerView* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
+void test() {
+  auto v = View{} | std::views::join;
+
+  // [range.join.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.join.iterator]
+
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(c_it);
+
+  // [range.join.overview]
+
+  std::vector<std::string> ss{"hello", " ", "world", "!"};
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::join(ss);
+}


        

From libcxx-commits at lists.llvm.org  Mon Jul  6 22:48:15 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 22:48:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`join_view` (PR #206838)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206838@github.com>
Message-ID: <6a4c931f.7270d2fd.2d4879.c6b8@mx.google.com>


https://github.com/frederick-vs-ja closed https://github.com/llvm/llvm-project/pull/206838

From libcxx-commits at lists.llvm.org  Mon Jul  6 22:51:10 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 22:51:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::find_first_of (PR #206328)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206328@github.com>
Message-ID: <6a4c93ce.331483e7.2440f8.c8eb@mx.google.com>


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/206328

>From 80420b031a2664ffaaac079b74864f6f035d0810 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 28 Jun 2026 11:21:56 +0100
Subject: [PATCH 01/12] Added implementation of find_first_of on top of
 __find_if

---
 libcxx/include/__algorithm/pstl.h             | 22 +++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++
 libcxx/include/__pstl/backends/default.h      | 19 ++++
 .../pstl.find_first_of.pass.cpp               | 87 +++++++++++++++++++
 4 files changed, 135 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 10625ea3f8e3d..7d5b20ad96c1d 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -679,6 +679,28 @@ is_sorted(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterato
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a52e6db954d0c..cf64e190ad4ed 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -109,6 +109,13 @@ struct __is_partitioned;
 // optional<bool>
 // operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __find_first_of;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2>
+// optional<_ForwardIterator1>
+// operator()(_Policy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __for_each;
 // template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index be90715af13b5..4fcd9b55e5066 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -12,6 +12,7 @@
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
+#include <__algorithm/find.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
 #include <__config>
@@ -57,6 +58,7 @@ namespace __pstl {
 // - all_of
 // - none_of
 // - is_partitioned
+// - find_first_of
 //
 // for_each family
 // ---------------
@@ -181,6 +183,23 @@ struct __is_partitioned<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2) const noexcept {
+    using _FindIf = __dispatch<__find_if, __current_configuration, _ExecutionPolicy>;
+    using _Ref    = __iterator_reference<_ForwardIterator1>;
+    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref __element) {
+      return std::find(__first2, __last2, __element) != __last2;
+    });
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
new file mode 100644
index 0000000000000..c4093daa71ba7
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator1 find_first_of(ExecutionPolicy&& exec,
+//                                  ForwardIterator1 first1,
+//                                  ForwardIterator1 last1,
+//                                  ForwardIterator2 first2,
+//                                  ForwardIterator2 last2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]     = {0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a)) == Iter(a));
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a)) == Iter(a + sa));
+    }
+    {
+      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
+      unsigned sa = sizeof(ia) / sizeof(ia[0]);
+      int ib[]    = {1, 3, 5, 7};
+      unsigned sb = sizeof(ib) / sizeof(ib[0]);
+      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
+      int ic[] = {7};
+      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
+      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
+      assert(std::find_first_of(Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
+    }
+    {
+      int a[8192];
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      std::iota(a, a + sa, 1);
+      a[1023]     = -1;
+      a[2048]     = -1;
+      a[3071]     = -1;
+      int b[]     = {-1, 999999};
+      unsigned sb = sizeof(b) / sizeof(b[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb)) == Iter(a + 1023));
+    }
+    {
+      int a[1073];
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      std::iota(a, a + sa, 0);
+      int b[]     = {1070, 1071, 1072, -1};
+      unsigned sb = sizeof(b) / sizeof(b[0]);
+      for (unsigned i = 0; i < sa; i = i <= 16 ? i + 1 : unsigned(3.1415 * i)) {
+        a[i] = -1;
+        assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb)) == Iter(a + i));
+        a[i] = i;
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
+                                       types::bidirectional_iterator_list<int*>,
+                                       types::random_access_iterator_list<int*>>{},
+                  TestIteratorWithPolicies<Test>{});
+
+  return 0;
+}

>From c4185be74a100792d2e08987b3991397446a39d1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 28 Jun 2026 12:08:03 +0100
Subject: [PATCH 02/12] Added a predicate version of find_first_of

---
 libcxx/include/__algorithm/pstl.h             | 28 +++++-
 libcxx/include/__pstl/backend_fwd.h           |  4 +-
 libcxx/include/__pstl/backends/default.h      | 14 +--
 .../pstl.find_first_of_pred.pass.cpp          | 95 +++++++++++++++++++
 4 files changed, 132 insertions(+), 9 deletions(-)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 7d5b20ad96c1d..84418222def52 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -698,7 +698,33 @@ template <class _ExecutionPolicy,
       std::move(__first1),
       std::move(__last1),
       std::move(__first2),
-      std::move(__last2));
+      std::move(__last2),
+      std::equal_to<>{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index cf64e190ad4ed..1ea121367fbc7 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -111,10 +111,10 @@ struct __is_partitioned;
 
 template <class _Backend, class _ExecutionPolicy>
 struct __find_first_of;
-// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2>
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
 // optional<_ForwardIterator1>
 // operator()(_Policy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
-//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2) const noexcept;
+//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2, _Predicate __pred) const noexcept;
 
 template <class _Backend, class _ExecutionPolicy>
 struct __for_each;
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 4fcd9b55e5066..6306b051f9dc4 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -9,10 +9,10 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 #define _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 
+#include <__algorithm/any_of.h>
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
-#include <__algorithm/find.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
 #include <__config>
@@ -185,17 +185,19 @@ struct __is_partitioned<__default_backend_tag, _ExecutionPolicy> {
 
 template <class _ExecutionPolicy>
 struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
-  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2>
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
   operator()(_Policy&& __policy,
              _ForwardIterator1 __first1,
              _ForwardIterator1 __last1,
              _ForwardIterator2 __first2,
-             _ForwardIterator2 __last2) const noexcept {
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
     using _FindIf = __dispatch<__find_if, __current_configuration, _ExecutionPolicy>;
-    using _Ref    = __iterator_reference<_ForwardIterator1>;
-    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref __element) {
-      return std::find(__first2, __last2, __element) != __last2;
+    using _Ref1   = __iterator_reference<_ForwardIterator1>;
+    using _Ref2   = __iterator_reference<_ForwardIterator2>;
+    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
+      return std::any_of(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); });
     });
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
new file mode 100644
index 0000000000000..00cb0f149a399
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   ForwardIterator1 find_first_of(ExecutionPolicy&& exec,
+//                                  ForwardIterator1 first1,
+//                                  ForwardIterator1 last1,
+//                                  ForwardIterator2 first2,
+//                                  ForwardIterator2 last2,
+//                                  BinaryPredicate p);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct Pred {
+  bool operator()(int l, int r) const {
+    return l + 1 == r; // ensures that the predicate is not equivalent to std::equal_to
+  }
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]     = {0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a), Pred{}) == Iter(a));
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a), Pred{}) == Iter(a + sa));
+    }
+    {
+      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
+      unsigned sa = sizeof(ia) / sizeof(ia[0]);
+      int ib[]    = {2, 4, 6, 8};
+      unsigned sb = sizeof(ib) / sizeof(ib[0]);
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb), Pred{}) == Iter(ia + 1));
+      int ic[] = {7};
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1), Pred{}) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic), Pred{}) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1), Pred{}) == Iter(ia));
+    }
+    {
+      int a[8192];
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      std::iota(a, a + sa, 1);
+      a[1023]     = -2;
+      a[2048]     = -2;
+      a[3071]     = -2;
+      int b[]     = {-1, 999999};
+      unsigned sb = sizeof(b) / sizeof(b[0]);
+      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb), Pred{}) == Iter(a + 1023));
+    }
+    {
+      int a[1073];
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      std::iota(a, a + sa, 0);
+      int b[]     = {1070, 1071, 1072, -1};
+      unsigned sb = sizeof(b) / sizeof(b[0]);
+      for (unsigned i = 0; i < sa; i = i <= 16 ? i + 1 : unsigned(3.1415 * i)) {
+        a[i] = -2;
+        assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb), Pred{}) == Iter(a + i));
+        a[i] = i;
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
+                                       types::bidirectional_iterator_list<int*>,
+                                       types::random_access_iterator_list<int*>>{},
+                  TestIteratorWithPolicies<Test>{});
+
+  return 0;
+}

>From 92e67cdebae5293ae478ecc9d385585e1f9b8d55 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 28 Jun 2026 12:30:10 +0100
Subject: [PATCH 03/12] Added verification for iterator types, nodiscard and
 noexcept

---
 .../pstl.iterator-requirements.verify.cpp         |  7 +++++++
 .../libcxx/algorithms/pstl.nodiscard.verify.cpp   |  4 ++++
 .../algorithms/pstl.exception_handling.pass.cpp   | 15 +++++++++++++++
 3 files changed, 26 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e5bd7e764c59b..1abaf5cc7b193 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -89,6 +89,13 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::find_if_not(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_if_not}}
   }
 
+  {
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_first_of}}
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
+  }
+
   {
     (void)std::for_each(pol, non_fwd, non_fwd, func); // expected-error@*:* {{static assertion failed: for_each}}
     (void)std::for_each_n(pol, non_fwd, n, func);     // expected-error@*:* {{static assertion failed: for_each_n}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index b6554cef6f32a..747d62bc66837 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -47,6 +47,10 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::find(std::execution::par, std::begin(a), std::end(a), 1);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::find_first_of(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::find_first_of(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_partitioned(std::execution::par, std::begin(a), std::end(a), pred);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a3db159289393..9e6c8f5b8ce40 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -175,6 +175,21 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; });
+
+        // find_first_of(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::find_first_of(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // find_first_of(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::find_first_of(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), pred);
+        });
+      }
+
       {
         auto func = maybe_throw(tokens[5], [](int) {});
 

>From 65aa1ad5d9dd25a930e0cca5ba0c1d59fb3e8aa4 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 28 Jun 2026 13:09:11 +0100
Subject: [PATCH 04/12] Added a missing policy in the unit test

---
 .../alg.find.first.of/pstl.find_first_of.pass.cpp         | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
index c4093daa71ba7..5768a8cf1f3c0 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -45,11 +45,11 @@ struct Test {
       unsigned sa = sizeof(ia) / sizeof(ia[0]);
       int ib[]    = {1, 3, 5, 7};
       unsigned sb = sizeof(ib) / sizeof(ib[0]);
-      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
       int ic[] = {7};
-      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
-      assert(std::find_first_of(Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
-      assert(std::find_first_of(Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
+      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
     }
     {
       int a[8192];

>From 18881aea45556d321505c64ef3ebcbab78c331ec Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 30 Jun 2026 22:10:58 +0100
Subject: [PATCH 05/12] Addressed the feedback

---
 libcxx/include/__algorithm/pstl.h             | 96 +++++++++----------
 .../pstl.iterator-requirements.verify.cpp     |  8 +-
 .../pstl.find_first_of.pass.cpp               | 89 ++++++++++-------
 .../pstl.find_first_of_pred.pass.cpp          | 89 ++++++++++-------
 4 files changed, 168 insertions(+), 114 deletions(-)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 84418222def52..3530b3207f59e 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -293,6 +293,54 @@ find(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __l
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::equal_to<>{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator,
           class _Function,
@@ -679,54 +727,6 @@ is_sorted(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterato
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
 }
 
-template <class _ExecutionPolicy,
-          class _ForwardIterator1,
-          class _ForwardIterator2,
-          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
-          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
-    _ExecutionPolicy&& __policy,
-    _ForwardIterator1 __first1,
-    _ForwardIterator1 __last1,
-    _ForwardIterator2 __first2,
-    _ForwardIterator2 __last2) {
-  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
-  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
-  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
-  return __pstl::__handle_exception<_Implementation>(
-      std::forward<_ExecutionPolicy>(__policy),
-      std::move(__first1),
-      std::move(__last1),
-      std::move(__first2),
-      std::move(__last2),
-      std::equal_to<>{});
-}
-
-template <class _ExecutionPolicy,
-          class _ForwardIterator1,
-          class _ForwardIterator2,
-          class _BinaryPredicate,
-          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
-          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
-    _ExecutionPolicy&& __policy,
-    _ForwardIterator1 __first1,
-    _ForwardIterator1 __last1,
-    _ForwardIterator2 __first2,
-    _ForwardIterator2 __last2,
-    _BinaryPredicate __pred) {
-  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
-  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
-  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
-  return __pstl::__handle_exception<_Implementation>(
-      std::forward<_ExecutionPolicy>(__policy),
-      std::move(__first1),
-      std::move(__last1),
-      std::move(__first2),
-      std::move(__last2),
-      std::move(__pred));
-}
-
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 1abaf5cc7b193..aee3f97019071 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -91,9 +91,13 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
 
   {
     (void)std::find_first_of(
-        pol, non_fwd, non_fwd, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_first_of}}
+        pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_first_of}}
     (void)std::find_first_of(
-        pol, non_fwd, non_fwd, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: find_first_of}}
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
   }
 
   {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
index 5768a8cf1f3c0..ea2fdc6eebdef 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -30,58 +30,83 @@
 #include "test_macros.h"
 #include "type_algorithms.h"
 
-template <class Iter>
+EXECUTION_POLICY_SFINAE_TEST(find_first_of);
+
+static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable&& callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    using std::begin;
+    using std::end;
     {
-      int a[]     = {0};
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a)) == Iter(a));
-      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a)) == Iter(a + sa));
+      int a[] = {0};
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(begin(a)), Iter2(begin(a)), Iter2(begin(a))) ==
+             Iter1(begin(a)));
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(a)), Iter2(begin(a))) ==
+             Iter1(end(a)));
     }
     {
-      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
-      unsigned sa = sizeof(ia) / sizeof(ia[0]);
-      int ib[]    = {1, 3, 5, 7};
-      unsigned sb = sizeof(ib) / sizeof(ib[0]);
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb)) == Iter(ia + 1));
+      int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int ib[] = {1, 3, 5, 7};
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ib)), Iter2(end(ib))) ==
+             Iter1(begin(ia) + 1));
       int ic[] = {7};
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1)) == Iter(ia + sa));
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic)) == Iter(ia + sa));
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1)) == Iter(ia));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(end(ic))) ==
+             Iter1(end(ia)));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(begin(ic))) ==
+             Iter1(end(ia)));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(begin(ia)), Iter2(begin(ic)), Iter2(end(ic))) ==
+             Iter1(begin(ia)));
     }
     {
       int a[8192];
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      std::iota(a, a + sa, 1);
-      a[1023]     = -1;
-      a[2048]     = -1;
-      a[3071]     = -1;
-      int b[]     = {-1, 999999};
-      unsigned sb = sizeof(b) / sizeof(b[0]);
-      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb)) == Iter(a + 1023));
+      std::iota(begin(a), end(a), 1);
+      a[1023] = -1;
+      a[2048] = -1;
+      a[3071] = -1;
+      int b[] = {-1, 999999};
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b))) ==
+             Iter1(begin(a) + 1023));
     }
     {
       int a[1073];
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      std::iota(a, a + sa, 0);
-      int b[]     = {1070, 1071, 1072, -1};
-      unsigned sb = sizeof(b) / sizeof(b[0]);
-      for (unsigned i = 0; i < sa; i = i <= 16 ? i + 1 : unsigned(3.1415 * i)) {
+      std::iota(begin(a), end(a), 0);
+      int b[] = {1073, 1074, 1075, -1};
+      runway_sample(std::size(a), [&](size_t i) {
         a[i] = -1;
-        assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb)) == Iter(a + i));
+        assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b))) ==
+               Iter1(begin(a) + i));
         a[i] = i;
-      }
+      });
     }
   }
 };
 
 int main(int, char**) {
-  types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
-                                       types::bidirectional_iterator_list<int*>,
-                                       types::random_access_iterator_list<int*>>{},
-                  TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies< types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
 
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
index 00cb0f149a399..770d0109c4f4a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -32,64 +32,89 @@
 #include "test_macros.h"
 #include "type_algorithms.h"
 
+EXECUTION_POLICY_SFINAE_TEST(find_first_of);
+
+static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
 struct Pred {
   bool operator()(int l, int r) const {
     return l + 1 == r; // ensures that the predicate is not equivalent to std::equal_to
   }
 };
 
-template <class Iter>
+template <class Callable>
+void runway_sample(size_t size, Callable&& callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    using std::begin;
+    using std::end;
     {
-      int a[]     = {0};
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      assert(std::find_first_of(policy, Iter(a), Iter(a), Iter(a), Iter(a), Pred{}) == Iter(a));
-      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(a), Iter(a), Pred{}) == Iter(a + sa));
+      int a[] = {0};
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(begin(a)), Iter2(begin(a)), Iter2(begin(a)), Pred{}) ==
+             Iter1(begin(a)));
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(a)), Iter2(begin(a)), Pred{}) ==
+             Iter1(end(a)));
     }
     {
-      int ia[]    = {0, 1, 2, 3, 0, 1, 2, 3};
-      unsigned sa = sizeof(ia) / sizeof(ia[0]);
-      int ib[]    = {2, 4, 6, 8};
-      unsigned sb = sizeof(ib) / sizeof(ib[0]);
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ib), Iter(ib + sb), Pred{}) == Iter(ia + 1));
+      int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int ib[] = {2, 4, 6, 8};
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ib)), Iter2(end(ib)), Pred{}) ==
+             Iter1(begin(ia) + 1));
       int ic[] = {7};
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic + 1), Pred{}) == Iter(ia + sa));
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia + sa), Iter(ic), Iter(ic), Pred{}) == Iter(ia + sa));
-      assert(std::find_first_of(policy, Iter(ia), Iter(ia), Iter(ic), Iter(ic + 1), Pred{}) == Iter(ia));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(end(ic)), Pred{}) ==
+             Iter1(end(ia)));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(begin(ic)), Pred{}) ==
+             Iter1(end(ia)));
+      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(begin(ia)), Iter2(begin(ic)), Iter2(end(ic)), Pred{}) ==
+             Iter1(begin(ia)));
     }
     {
       int a[8192];
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      std::iota(a, a + sa, 1);
-      a[1023]     = -2;
-      a[2048]     = -2;
-      a[3071]     = -2;
-      int b[]     = {-1, 999999};
-      unsigned sb = sizeof(b) / sizeof(b[0]);
-      assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb), Pred{}) == Iter(a + 1023));
+      std::iota(begin(a), end(a), 1);
+      a[1023] = -2;
+      a[2048] = -2;
+      a[3071] = -2;
+      int b[] = {-1, 999999};
+      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b)), Pred{}) ==
+             Iter1(begin(a) + 1023));
     }
     {
       int a[1073];
-      unsigned sa = sizeof(a) / sizeof(a[0]);
-      std::iota(a, a + sa, 0);
-      int b[]     = {1070, 1071, 1072, -1};
-      unsigned sb = sizeof(b) / sizeof(b[0]);
-      for (unsigned i = 0; i < sa; i = i <= 16 ? i + 1 : unsigned(3.1415 * i)) {
+      std::iota(begin(a), end(a), 0);
+      int b[] = {1074, 1075, 1076, -1};
+      runway_sample(std::size(a), [&](size_t i) {
         a[i] = -2;
-        assert(std::find_first_of(policy, Iter(a), Iter(a + sa), Iter(b), Iter(b + sb), Pred{}) == Iter(a + i));
+        assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b)), Pred{}) ==
+               Iter1(begin(a) + i));
         a[i] = i;
-      }
+      });
     }
   }
 };
 
 int main(int, char**) {
-  types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
-                                       types::bidirectional_iterator_list<int*>,
-                                       types::random_access_iterator_list<int*>>{},
-                  TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies< types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
 
   return 0;
 }

>From fa3700e14b9c5f311df716d57f018a94a3438b2e Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 30 Jun 2026 22:42:34 +0100
Subject: [PATCH 06/12] Missing include

---
 .../alg.find.first.of/pstl.find_first_of.pass.cpp                | 1 +
 .../alg.find.first.of/pstl.find_first_of_pred.pass.cpp           | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
index ea2fdc6eebdef..0ad0bbfdd700a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -22,6 +22,7 @@
 #include <algorithm>
 #include <cassert>
 #include <functional>
+#include <iterator>
 #include <limits>
 #include <numeric>
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
index 770d0109c4f4a..5532ed2f8e075 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -24,6 +24,7 @@
 #include <algorithm>
 #include <cassert>
 #include <functional>
+#include <iterator>
 #include <limits>
 #include <numeric>
 

>From 8dd415ea0a27cb125486a9b32f4350d5b45c18f1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 1 Jul 2026 21:21:11 +0100
Subject: [PATCH 07/12] Branch off to std::find() if the predicate is equality,
 call into more general std::find_if() otherwise.

---
 libcxx/include/__pstl/backends/default.h      | 12 +++-
 .../pstl.find_first_of.pass.cpp               | 48 ++++++++-------
 .../pstl.find_first_of_pred.pass.cpp          | 61 ++++++++++++-------
 3 files changed, 77 insertions(+), 44 deletions(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 6306b051f9dc4..a5cd1d1b09912 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -9,10 +9,11 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 #define _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 
-#include <__algorithm/any_of.h>
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
+#include <__algorithm/find.h>
+#include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
 #include <__config>
@@ -24,6 +25,8 @@
 #include <__iterator/next.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
+#include <__type_traits/desugars_to.h>
+#include <__type_traits/remove_cvref.h>
 #include <__utility/empty.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -197,7 +200,12 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
     using _Ref1   = __iterator_reference<_ForwardIterator1>;
     using _Ref2   = __iterator_reference<_ForwardIterator2>;
     return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
-      return std::any_of(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); });
+      if constexpr (__desugars_to_v<__equal_tag, _Predicate, __remove_cvref_t<_Ref1>, __remove_cvref_t<_Ref2>>) {
+        // bypass an equality predicate and call directly to std::find() to allow more vectorization
+        return std::find(__first2, __last2, __element) != __last2;
+      } else {
+        return std::find_if(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); }) != __last2;
+      }
     });
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
index 0ad0bbfdd700a..118b34da0cb6c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -37,7 +37,7 @@ static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*>);
 
 template <class Callable>
-void runway_sample(size_t size, Callable&& callable) {
+void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
   // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
   for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
@@ -55,46 +55,52 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
-    using std::begin;
-    using std::end;
     {
       int a[] = {0};
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(begin(a)), Iter2(begin(a)), Iter2(begin(a))) ==
-             Iter1(begin(a)));
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(a)), Iter2(begin(a))) ==
-             Iter1(end(a)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)), Iter2(std::begin(a))) ==
+             Iter1(std::begin(a)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), Iter2(std::begin(a))) ==
+             Iter1(std::end(a)));
     }
     {
       int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
       int ib[] = {1, 3, 5, 7};
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ib)), Iter2(end(ib))) ==
-             Iter1(begin(ia) + 1));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Iter2(std::end(ib))) ==
+             Iter1(std::begin(ia) + 1));
       int ic[] = {7};
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(end(ic))) ==
-             Iter1(end(ia)));
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(begin(ic))) ==
-             Iter1(end(ia)));
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(begin(ia)), Iter2(begin(ic)), Iter2(end(ic))) ==
-             Iter1(begin(ia)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ic)), Iter2(std::end(ic))) ==
+             Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ic)), Iter2(std::begin(ic))) ==
+             Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ic)), Iter2(std::end(ic))) ==
+             Iter1(std::begin(ia)));
     }
     {
       int a[8192];
-      std::iota(begin(a), end(a), 1);
+      std::iota(std::begin(a), std::end(a), 1);
       a[1023] = -1;
       a[2048] = -1;
       a[3071] = -1;
       int b[] = {-1, 999999};
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b))) ==
-             Iter1(begin(a) + 1023));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))) ==
+             Iter1(std::begin(a) + 1023));
     }
     {
       int a[1073];
-      std::iota(begin(a), end(a), 0);
+      std::iota(std::begin(a), std::end(a), 0);
       int b[] = {1073, 1074, 1075, -1};
       runway_sample(std::size(a), [&](size_t i) {
         a[i] = -1;
-        assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b))) ==
-               Iter1(begin(a) + i));
+        assert(std::find_first_of(
+                   policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))) ==
+               Iter1(std::begin(a) + i));
         a[i] = i;
       });
     }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
index 5532ed2f8e075..1033b52e4621f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -45,7 +45,7 @@ struct Pred {
 };
 
 template <class Callable>
-void runway_sample(size_t size, Callable&& callable) {
+void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
   // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
   for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
@@ -63,46 +63,65 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
-    using std::begin;
-    using std::end;
     {
       int a[] = {0};
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(begin(a)), Iter2(begin(a)), Iter2(begin(a)), Pred{}) ==
-             Iter1(begin(a)));
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(a)), Iter2(begin(a)), Pred{}) ==
-             Iter1(end(a)));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)), Iter2(std::begin(a)), Pred{}) ==
+          Iter1(std::begin(a)));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), Iter2(std::begin(a)), Pred{}) ==
+          Iter1(std::end(a)));
     }
     {
       int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
       int ib[] = {2, 4, 6, 8};
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ib)), Iter2(end(ib)), Pred{}) ==
-             Iter1(begin(ia) + 1));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Iter2(std::end(ib)), Pred{}) ==
+          Iter1(std::begin(ia) + 1));
       int ic[] = {7};
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(end(ic)), Pred{}) ==
-             Iter1(end(ia)));
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(end(ia)), Iter2(begin(ic)), Iter2(begin(ic)), Pred{}) ==
-             Iter1(end(ia)));
-      assert(std::find_first_of(policy, Iter1(begin(ia)), Iter1(begin(ia)), Iter2(begin(ic)), Iter2(end(ic)), Pred{}) ==
-             Iter1(begin(ia)));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ic)), Iter2(std::end(ic)), Pred{}) ==
+          Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy,
+                 Iter1(std::begin(ia)),
+                 Iter1(std::end(ia)),
+                 Iter2(std::begin(ic)),
+                 Iter2(std::begin(ic)),
+                 Pred{}) == Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy,
+                 Iter1(std::begin(ia)),
+                 Iter1(std::begin(ia)),
+                 Iter2(std::begin(ic)),
+                 Iter2(std::end(ic)),
+                 Pred{}) == Iter1(std::begin(ia)));
     }
     {
       int a[8192];
-      std::iota(begin(a), end(a), 1);
+      std::iota(std::begin(a), std::end(a), 1);
       a[1023] = -2;
       a[2048] = -2;
       a[3071] = -2;
       int b[] = {-1, 999999};
-      assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b)), Pred{}) ==
-             Iter1(begin(a) + 1023));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{}) ==
+             Iter1(std::begin(a) + 1023));
     }
     {
       int a[1073];
-      std::iota(begin(a), end(a), 0);
+      std::iota(std::begin(a), std::end(a), 0);
       int b[] = {1074, 1075, 1076, -1};
       runway_sample(std::size(a), [&](size_t i) {
         a[i] = -2;
-        assert(std::find_first_of(policy, Iter1(begin(a)), Iter1(end(a)), Iter2(begin(b)), Iter2(end(b)), Pred{}) ==
-               Iter1(begin(a) + i));
+        assert(
+            std::find_first_of(
+                policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{}) ==
+            Iter1(std::begin(a) + i));
         a[i] = i;
       });
     }

>From f850d3d6b613a8ed59102a4fb3add7863766412f Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 1 Jul 2026 22:55:54 +0100
Subject: [PATCH 08/12] <numeric> pulls in more headers via
 <__algorithm/find.h>

---
 libcxx/test/libcxx/transitive_includes/cxx23.csv | 3 +++
 libcxx/test/libcxx/transitive_includes/cxx26.csv | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 8385b37c07822..53ae6c9b4e591 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -704,11 +704,14 @@ mutex typeinfo
 mutex version
 new version
 numbers version
+numeric cctype
 numeric climits
 numeric compare
 numeric cstdint
 numeric cstring
 numeric ctime
+numeric cwchar
+numeric cwctype
 numeric initializer_list
 numeric limits
 numeric optional
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index cf1154a251f23..849c9b0ce932b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -664,11 +664,14 @@ mutex tuple
 mutex version
 new version
 numbers version
+numeric cctype
 numeric climits
 numeric compare
 numeric cstdint
 numeric cstring
 numeric ctime
+numeric cwchar
+numeric cwctype
 numeric initializer_list
 numeric limits
 numeric optional

>From 645820c13db62c99a27d253902acd441488156e0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 1 Jul 2026 23:09:04 +0100
Subject: [PATCH 09/12] Expanded tests cases of the serial versions a bit,
 supporting down to C++03

---
 .../alg.find.first.of/find_first_of.pass.cpp  |  60 ++++++----
 .../find_first_of_pred.pass.cpp               | 104 +++++++++++-------
 2 files changed, 105 insertions(+), 59 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
index efa54bbc8fd66..4740029d4c22b 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
@@ -16,18 +16,19 @@
 #include <algorithm>
 #include <cassert>
 #include <iterator>
+#include <numeric>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
 #if TEST_STD_VER > 17
 TEST_CONSTEXPR bool test_constexpr() {
-    int ia[] = {1, 2, 3};
-    int ib[] = {7, 8, 9};
-    int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
+  int ia[] = {1, 2, 3};
+  int ib[] = {7, 8, 9};
+  int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
     typedef forward_iterator<int*>       FI;
-    typedef bidirectional_iterator<int*> BI;
-    typedef random_access_iterator<int*> RI;
+  typedef bidirectional_iterator<int*> BI;
+  typedef random_access_iterator<int*> RI;
 
     return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia))) == FI(ic+1))
            && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib))) == FI(std::end(ic)))
@@ -36,39 +37,56 @@ TEST_CONSTEXPR bool test_constexpr() {
            && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia))) == RI(ic+1))
            && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib))) == RI(std::end(ic)))
            ;
-    }
+}
 #endif
 
-int main(int, char**)
-{
-    int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
-    const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-    int ib[] = {1, 3, 5, 7};
-    const unsigned sb = sizeof(ib)/sizeof(ib[0]);
+int main(int, char**) {
+  {
+    int ia[]          = {0, 1, 2, 3, 0, 1, 2, 3};
+    const unsigned sa = sizeof(ia) / sizeof(ia[0]);
+    int ib[]          = {1, 3, 5, 7};
+    const unsigned sb = sizeof(ib) / sizeof(ib[0]);
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia + sa),
                               forward_iterator<const int*>(ib),
-                              forward_iterator<const int*>(ib + sb)) ==
-                              cpp17_input_iterator<const int*>(ia+1));
+                              forward_iterator<const int*>(ib + sb)) == cpp17_input_iterator<const int*>(ia + 1));
     int ic[] = {7};
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia + sa),
                               forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic + 1)) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
+                              forward_iterator<const int*>(ic + 1)) == cpp17_input_iterator<const int*>(ia + sa));
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia + sa),
                               forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic)) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
+                              forward_iterator<const int*>(ic)) == cpp17_input_iterator<const int*>(ia + sa));
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia),
                               forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic+1)) ==
-                              cpp17_input_iterator<const int*>(ia));
+                              forward_iterator<const int*>(ic + 1)) == cpp17_input_iterator<const int*>(ia));
+  }
+  {
+    int a[8192];
+    std::iota(std::begin(a), std::end(a), 1);
+    a[1023] = -1;
+    a[2048] = -1;
+    a[3071] = -1;
+    int b[] = {-1, 999999};
+    assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b)) == std::begin(a) + 1023);
+  }
+  {
+    int a[1073];
+    const unsigned sa = sizeof(a) / sizeof(a[0]);
+    std::iota(std::begin(a), std::end(a), 0);
+    int b[] = {1073, 1074, 1075, -1};
+    for (unsigned i = 0; i < sa; i = i < 16 ? i + 1 : unsigned(3.1415 * i)) {
+      a[i] = -1;
+      assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b)) == std::begin(a) + i);
+      a[i] = i;
+    }
+  }
 
 #if TEST_STD_VER > 17
-    static_assert(test_constexpr());
+  static_assert(test_constexpr());
 #endif
 
   return 0;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
index fd2e4d426fbf9..25699f4b5c216 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
@@ -18,19 +18,26 @@
 #include <cassert>
 #include <functional>
 #include <iterator>
+#include <numeric>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
+struct Pred {
+  bool operator()(int l, int r) const {
+    return l + 1 == r; // ensures that the predicate is not equivalent to std::equal_to
+  }
+};
+
 #if TEST_STD_VER > 17
 constexpr bool test_constexpr() {
-    int ia[] = {1, 2, 3};
-    int ib[] = {7, 8, 9};
-    int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
+  int ia[] = {1, 2, 3};
+  int ib[] = {7, 8, 9};
+  int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
     typedef forward_iterator<int*>       FI;
-    typedef bidirectional_iterator<int*> BI;
-    typedef random_access_iterator<int*> RI;
-    std::equal_to<int> eq{};
+  typedef bidirectional_iterator<int*> BI;
+  typedef random_access_iterator<int*> RI;
+  std::equal_to<int> eq{};
     return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia)), eq) == FI(ic+1))
            && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib)), eq) == FI(std::end(ic)))
            && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia)), eq) == BI(ic+1))
@@ -38,43 +45,64 @@ constexpr bool test_constexpr() {
            && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia)), eq) == RI(ic+1))
            && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib)), eq) == RI(std::end(ic)))
            ;
-    }
+}
 #endif
 
-int main(int, char**)
-{
-    int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
-    const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-    int ib[] = {1, 3, 5, 7};
-    const unsigned sb = sizeof(ib)/sizeof(ib[0]);
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia + sa),
-                              forward_iterator<const int*>(ib),
-                              forward_iterator<const int*>(ib + sb),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia+1));
+int main(int, char**) {
+  {
+    int ia[]          = {0, 1, 2, 3, 0, 1, 2, 3};
+    const unsigned sa = sizeof(ia) / sizeof(ia[0]);
+    int ib[]          = {1, 3, 5, 7};
+    const unsigned sb = sizeof(ib) / sizeof(ib[0]);
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia + sa),
+               forward_iterator<const int*>(ib),
+               forward_iterator<const int*>(ib + sb),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia + 1));
     int ic[] = {7};
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia + sa),
-                              forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic + 1),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia + sa),
-                              forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia),
-                              forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic+1),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia));
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia + sa),
+               forward_iterator<const int*>(ic),
+               forward_iterator<const int*>(ic + 1),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia + sa));
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia + sa),
+               forward_iterator<const int*>(ic),
+               forward_iterator<const int*>(ic),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia + sa));
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia),
+               forward_iterator<const int*>(ic),
+               forward_iterator<const int*>(ic + 1),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia));
+  }
+  {
+    int a[8192];
+    std::iota(std::begin(a), std::end(a), 1);
+    a[1023] = -2;
+    a[2048] = -2;
+    a[3071] = -2;
+    int b[] = {-1, 999999};
+    assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b), Pred()) == std::begin(a) + 1023);
+  }
+  {
+    int a[1073];
+    const unsigned sa = sizeof(a) / sizeof(a[0]);
+    std::iota(std::begin(a), std::end(a), 0);
+    int b[] = {1074, 1075, 1076, -1};
+    for (unsigned i = 0; i < sa; i = i < 16 ? i + 1 : unsigned(3.1415 * i)) {
+      a[i] = -2;
+      assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b), Pred()) == std::begin(a) + i);
+      a[i] = i;
+    };
+  }
 
 #if TEST_STD_VER > 17
-    static_assert(test_constexpr());
+  static_assert(test_constexpr());
 #endif
 
   return 0;

>From 12ba4c956bfba72044d2423496800660da679a0f Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 1 Jul 2026 23:12:37 +0100
Subject: [PATCH 10/12] clang-format the serial tests fully

---
 .../alg.find.first.of/find_first_of.pass.cpp  | 21 ++++++++++++-------
 .../find_first_of_pred.pass.cpp               | 21 ++++++++++++-------
 2 files changed, 26 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
index 4740029d4c22b..25a223e274706 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
@@ -26,17 +26,22 @@ TEST_CONSTEXPR bool test_constexpr() {
   int ia[] = {1, 2, 3};
   int ib[] = {7, 8, 9};
   int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
-    typedef forward_iterator<int*>       FI;
+  typedef forward_iterator<int*> FI;
   typedef bidirectional_iterator<int*> BI;
   typedef random_access_iterator<int*> RI;
 
-    return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia))) == FI(ic+1))
-           && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib))) == FI(std::end(ic)))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia))) == BI(ic+1))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib))) == BI(std::end(ic)))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia))) == RI(ic+1))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib))) == RI(std::end(ic)))
-           ;
+  return (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia))) ==
+          FI(ic + 1)) &&
+         (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib))) ==
+          FI(std::end(ic))) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia))) ==
+          BI(ic + 1)) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib))) ==
+          BI(std::end(ic))) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia))) ==
+          RI(ic + 1)) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib))) ==
+          RI(std::end(ic)));
 }
 #endif
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
index 25699f4b5c216..9fdc4081b0141 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
@@ -34,17 +34,22 @@ constexpr bool test_constexpr() {
   int ia[] = {1, 2, 3};
   int ib[] = {7, 8, 9};
   int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
-    typedef forward_iterator<int*>       FI;
+  typedef forward_iterator<int*> FI;
   typedef bidirectional_iterator<int*> BI;
   typedef random_access_iterator<int*> RI;
   std::equal_to<int> eq{};
-    return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia)), eq) == FI(ic+1))
-           && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib)), eq) == FI(std::end(ic)))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia)), eq) == BI(ic+1))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib)), eq) == BI(std::end(ic)))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia)), eq) == RI(ic+1))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib)), eq) == RI(std::end(ic)))
-           ;
+  return (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia)), eq) ==
+          FI(ic + 1)) &&
+         (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib)), eq) ==
+          FI(std::end(ic))) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia)), eq) ==
+          BI(ic + 1)) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib)), eq) ==
+          BI(std::end(ic))) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia)), eq) ==
+          RI(ic + 1)) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib)), eq) ==
+          RI(std::end(ic)));
 }
 #endif
 

>From 12be042391977e8ba12419b3371e5650d90b2a9e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 16:15:40 -0400
Subject: [PATCH 11/12] Update libcxx/include/__pstl/backends/default.h

---
 libcxx/include/__pstl/backends/default.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index a5cd1d1b09912..86f3899dc8ab1 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -200,7 +200,7 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
     using _Ref1   = __iterator_reference<_ForwardIterator1>;
     using _Ref2   = __iterator_reference<_ForwardIterator2>;
     return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
-      if constexpr (__desugars_to_v<__equal_tag, _Predicate, __remove_cvref_t<_Ref1>, __remove_cvref_t<_Ref2>>) {
+      if constexpr (__desugars_to_v<__equal_tag, _Predicate, _Ref1, _Ref2>) {
         // bypass an equality predicate and call directly to std::find() to allow more vectorization
         return std::find(__first2, __last2, __element) != __last2;
       } else {

>From de6f3e6097065c937b4b2aa6f8efc417e11d6a8d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 16:47:28 -0400
Subject: [PATCH 12/12] Update libcxx/include/__pstl/backends/default.h

---
 libcxx/include/__pstl/backends/default.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 86f3899dc8ab1..cb963a130f743 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -26,7 +26,6 @@
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
 #include <__type_traits/desugars_to.h>
-#include <__type_traits/remove_cvref.h>
 #include <__utility/empty.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>


From libcxx-commits at lists.llvm.org  Mon Jul  6 23:05:56 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 06 Jul 2026 23:05:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
	to `zip_view` (PR #207667)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207667@github.com>
Message-ID: <6a4c9744.eb165e5c.1817a5.d758@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/207667

From libcxx-commits at lists.llvm.org  Tue Jul  7 00:48:17 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Tue, 07 Jul 2026 00:48:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Set
 `__cpp_lib_reference_from_temporary` (PR #207920)
Message-ID: <llvm.org/llvm/llvm-project/pull/207920@github.com>


https://github.com/yronglin created https://github.com/llvm/llvm-project/pull/207920

This PR set `__cpp_lib_reference_from_temporary` to `202202L`.

Depends https://github.com/llvm/llvm-project/pull/205379.
Fixes https://github.com/llvm/llvm-project/issues/130405.

>From cdaede498c471d2e17572dd9ab7ae90d3ddb161a Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 7 Jul 2026 00:45:47 -0700
Subject: [PATCH] [libc++] P2255R2: Set __cpp_lib_reference_from_temporary

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/include/version                        |  2 +-
 .../type_traits.version.compile.pass.cpp      | 32 ++++++-------------
 .../version.version.compile.pass.cpp          | 32 ++++++-------------
 .../generate_feature_test_macro_components.py |  3 +-
 5 files changed, 23 insertions(+), 48 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..2d8a9b513ad5c 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -398,7 +398,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_zip``                                   ``202110L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_reference_from_temporary``                     *unimplemented*
+    ``__cpp_lib_reference_from_temporary``                     ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_shift``                                        ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..cffadf4655ce2 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -542,7 +542,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges_stride                        202207L
 # define __cpp_lib_ranges_to_container                  202202L
 # define __cpp_lib_ranges_zip                           202110L
-// # define __cpp_lib_reference_from_temporary             202202L
+# define __cpp_lib_reference_from_temporary             202202L
 # undef  __cpp_lib_shift
 # define __cpp_lib_shift                                202202L
 // # define __cpp_lib_spanstream                           202106L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
index cb5c008f16bb3..e69c4b5c97ea6 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
@@ -718,17 +718,11 @@
 #    error "__cpp_lib_logical_traits should have the value 201510L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should be defined in c++23"
-#    endif
-#    if __cpp_lib_reference_from_temporary != 202202L
-#      error "__cpp_lib_reference_from_temporary should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_reference_from_temporary
+#    error "__cpp_lib_reference_from_temporary should be defined in c++23"
+#  endif
+#  if __cpp_lib_reference_from_temporary != 202202L
+#    error "__cpp_lib_reference_from_temporary should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_remove_cvref
@@ -938,17 +932,11 @@
 #    error "__cpp_lib_logical_traits should have the value 201510L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should be defined in c++26"
-#    endif
-#    if __cpp_lib_reference_from_temporary != 202202L
-#      error "__cpp_lib_reference_from_temporary should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_reference_from_temporary
+#    error "__cpp_lib_reference_from_temporary should be defined in c++26"
+#  endif
+#  if __cpp_lib_reference_from_temporary != 202202L
+#    error "__cpp_lib_reference_from_temporary should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_remove_cvref
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 f2d74cc99d864..4afa5cc09c57c 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
@@ -5907,17 +5907,11 @@
 #    error "__cpp_lib_rcu should not be defined before c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should be defined in c++23"
-#    endif
-#    if __cpp_lib_reference_from_temporary != 202202L
-#      error "__cpp_lib_reference_from_temporary should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_reference_from_temporary
+#    error "__cpp_lib_reference_from_temporary should be defined in c++23"
+#  endif
+#  if __cpp_lib_reference_from_temporary != 202202L
+#    error "__cpp_lib_reference_from_temporary should have the value 202202L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_reference_wrapper
@@ -7876,17 +7870,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should be defined in c++26"
-#    endif
-#    if __cpp_lib_reference_from_temporary != 202202L
-#      error "__cpp_lib_reference_from_temporary should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_reference_from_temporary
+#    error "__cpp_lib_reference_from_temporary should be defined in c++26"
+#  endif
+#  if __cpp_lib_reference_from_temporary != 202202L
+#    error "__cpp_lib_reference_from_temporary should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_reference_wrapper
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..9794f3d453703 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1223,8 +1223,7 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_reference_from_temporary",
             "values": {"c++23": 202202},
-            "headers": ["type_traits"],
-            "unimplemented": True,
+            "headers": ["type_traits"]
         },
         {
             "name": "__cpp_lib_reference_wrapper",


From libcxx-commits at lists.llvm.org  Tue Jul  7 00:48:56 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 00:48:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Set
 `__cpp_lib_reference_from_temporary` (PR #207920)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207920@github.com>
Message-ID: <6a4caf68.f06f4994.80921.bf98@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Yihan Wang (yronglin)

<details>
<summary>Changes</summary>

This PR set `__cpp_lib_reference_from_temporary` to `202202L`.

Depends https://github.com/llvm/llvm-project/pull/205379.
Fixes https://github.com/llvm/llvm-project/issues/130405.

---
Full diff: https://github.com/llvm/llvm-project/pull/207920.diff


5 Files Affected:

- (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-1) 
- (modified) libcxx/include/version (+1-1) 
- (modified) libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp (+10-22) 
- (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+10-22) 
- (modified) libcxx/utils/generate_feature_test_macro_components.py (+1-2) 


``````````diff
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..2d8a9b513ad5c 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -398,7 +398,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_zip``                                   ``202110L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_reference_from_temporary``                     *unimplemented*
+    ``__cpp_lib_reference_from_temporary``                     ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_shift``                                        ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..cffadf4655ce2 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -542,7 +542,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges_stride                        202207L
 # define __cpp_lib_ranges_to_container                  202202L
 # define __cpp_lib_ranges_zip                           202110L
-// # define __cpp_lib_reference_from_temporary             202202L
+# define __cpp_lib_reference_from_temporary             202202L
 # undef  __cpp_lib_shift
 # define __cpp_lib_shift                                202202L
 // # define __cpp_lib_spanstream                           202106L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
index cb5c008f16bb3..e69c4b5c97ea6 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
@@ -718,17 +718,11 @@
 #    error "__cpp_lib_logical_traits should have the value 201510L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should be defined in c++23"
-#    endif
-#    if __cpp_lib_reference_from_temporary != 202202L
-#      error "__cpp_lib_reference_from_temporary should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_reference_from_temporary
+#    error "__cpp_lib_reference_from_temporary should be defined in c++23"
+#  endif
+#  if __cpp_lib_reference_from_temporary != 202202L
+#    error "__cpp_lib_reference_from_temporary should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_remove_cvref
@@ -938,17 +932,11 @@
 #    error "__cpp_lib_logical_traits should have the value 201510L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should be defined in c++26"
-#    endif
-#    if __cpp_lib_reference_from_temporary != 202202L
-#      error "__cpp_lib_reference_from_temporary should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_reference_from_temporary
+#    error "__cpp_lib_reference_from_temporary should be defined in c++26"
+#  endif
+#  if __cpp_lib_reference_from_temporary != 202202L
+#    error "__cpp_lib_reference_from_temporary should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_remove_cvref
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 f2d74cc99d864..4afa5cc09c57c 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
@@ -5907,17 +5907,11 @@
 #    error "__cpp_lib_rcu should not be defined before c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should be defined in c++23"
-#    endif
-#    if __cpp_lib_reference_from_temporary != 202202L
-#      error "__cpp_lib_reference_from_temporary should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_reference_from_temporary
+#    error "__cpp_lib_reference_from_temporary should be defined in c++23"
+#  endif
+#  if __cpp_lib_reference_from_temporary != 202202L
+#    error "__cpp_lib_reference_from_temporary should have the value 202202L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_reference_wrapper
@@ -7876,17 +7870,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should be defined in c++26"
-#    endif
-#    if __cpp_lib_reference_from_temporary != 202202L
-#      error "__cpp_lib_reference_from_temporary should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_reference_from_temporary
+#    error "__cpp_lib_reference_from_temporary should be defined in c++26"
+#  endif
+#  if __cpp_lib_reference_from_temporary != 202202L
+#    error "__cpp_lib_reference_from_temporary should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_reference_wrapper
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..9794f3d453703 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1223,8 +1223,7 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_reference_from_temporary",
             "values": {"c++23": 202202},
-            "headers": ["type_traits"],
-            "unimplemented": True,
+            "headers": ["type_traits"]
         },
         {
             "name": "__cpp_lib_reference_wrapper",

``````````

</details>


https://github.com/llvm/llvm-project/pull/207920

From libcxx-commits at lists.llvm.org  Tue Jul  7 00:49:57 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 00:49:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Set
 `__cpp_lib_reference_from_temporary` (PR #207920)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207920@github.com>
Message-ID: <6a4cafa5.cbc3f03a.10d998.198e@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {darker}-->


:warning: Python code formatter, darker found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
darker --check --diff -r origin/main...HEAD libcxx/utils/generate_feature_test_macro_components.py
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from darker here.
</summary>

``````````diff
--- generate_feature_test_macro_components.py	2026-07-07 07:45:47.000000 +0000
+++ generate_feature_test_macro_components.py	2026-07-07 07:49:23.803923 +0000
@@ -1221,11 +1221,11 @@
             "unimplemented": True,
         },
         {
             "name": "__cpp_lib_reference_from_temporary",
             "values": {"c++23": 202202},
-            "headers": ["type_traits"]
+            "headers": ["type_traits"],
         },
         {
             "name": "__cpp_lib_reference_wrapper",
             "values": {"c++26": 202403}, # P2944R3: Comparisons for reference_wrapper
             "headers": ["functional"],

``````````

</details>


https://github.com/llvm/llvm-project/pull/207920

From libcxx-commits at lists.llvm.org  Tue Jul  7 00:56:10 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 00:56:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cb11a.92ea0e2a.13fd37.644f@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 00:58:46 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 00:58:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 disallow chrono::duration<const T, P> (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4cb1b6.331483e7.2440f8.f33d@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Tue Jul  7 01:01:09 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 01:01:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
	to `zip_view` (PR #207667)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207667@github.com>
Message-ID: <6a4cb245.01f0a1da.1e9662.35bc@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/207667

From libcxx-commits at lists.llvm.org  Tue Jul  7 01:04:34 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Tue, 07 Jul 2026 01:04:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Set
 `__cpp_lib_reference_from_temporary` (PR #207920)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207920@github.com>
Message-ID: <6a4cb312.492e23e4.157d47.2a33@mx.google.com>


https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/207920

>From cdaede498c471d2e17572dd9ab7ae90d3ddb161a Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 7 Jul 2026 00:45:47 -0700
Subject: [PATCH 1/2] [libc++] P2255R2: Set __cpp_lib_reference_from_temporary

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/include/version                        |  2 +-
 .../type_traits.version.compile.pass.cpp      | 32 ++++++-------------
 .../version.version.compile.pass.cpp          | 32 ++++++-------------
 .../generate_feature_test_macro_components.py |  3 +-
 5 files changed, 23 insertions(+), 48 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..2d8a9b513ad5c 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -398,7 +398,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_zip``                                   ``202110L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_reference_from_temporary``                     *unimplemented*
+    ``__cpp_lib_reference_from_temporary``                     ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_shift``                                        ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..cffadf4655ce2 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -542,7 +542,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges_stride                        202207L
 # define __cpp_lib_ranges_to_container                  202202L
 # define __cpp_lib_ranges_zip                           202110L
-// # define __cpp_lib_reference_from_temporary             202202L
+# define __cpp_lib_reference_from_temporary             202202L
 # undef  __cpp_lib_shift
 # define __cpp_lib_shift                                202202L
 // # define __cpp_lib_spanstream                           202106L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
index cb5c008f16bb3..e69c4b5c97ea6 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp
@@ -718,17 +718,11 @@
 #    error "__cpp_lib_logical_traits should have the value 201510L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should be defined in c++23"
-#    endif
-#    if __cpp_lib_reference_from_temporary != 202202L
-#      error "__cpp_lib_reference_from_temporary should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_reference_from_temporary
+#    error "__cpp_lib_reference_from_temporary should be defined in c++23"
+#  endif
+#  if __cpp_lib_reference_from_temporary != 202202L
+#    error "__cpp_lib_reference_from_temporary should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_remove_cvref
@@ -938,17 +932,11 @@
 #    error "__cpp_lib_logical_traits should have the value 201510L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should be defined in c++26"
-#    endif
-#    if __cpp_lib_reference_from_temporary != 202202L
-#      error "__cpp_lib_reference_from_temporary should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_reference_from_temporary
+#    error "__cpp_lib_reference_from_temporary should be defined in c++26"
+#  endif
+#  if __cpp_lib_reference_from_temporary != 202202L
+#    error "__cpp_lib_reference_from_temporary should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_remove_cvref
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 f2d74cc99d864..4afa5cc09c57c 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
@@ -5907,17 +5907,11 @@
 #    error "__cpp_lib_rcu should not be defined before c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should be defined in c++23"
-#    endif
-#    if __cpp_lib_reference_from_temporary != 202202L
-#      error "__cpp_lib_reference_from_temporary should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_reference_from_temporary
+#    error "__cpp_lib_reference_from_temporary should be defined in c++23"
+#  endif
+#  if __cpp_lib_reference_from_temporary != 202202L
+#    error "__cpp_lib_reference_from_temporary should have the value 202202L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_reference_wrapper
@@ -7876,17 +7870,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should be defined in c++26"
-#    endif
-#    if __cpp_lib_reference_from_temporary != 202202L
-#      error "__cpp_lib_reference_from_temporary should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_reference_from_temporary
-#      error "__cpp_lib_reference_from_temporary should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_reference_from_temporary
+#    error "__cpp_lib_reference_from_temporary should be defined in c++26"
+#  endif
+#  if __cpp_lib_reference_from_temporary != 202202L
+#    error "__cpp_lib_reference_from_temporary should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_reference_wrapper
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..9794f3d453703 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1223,8 +1223,7 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_reference_from_temporary",
             "values": {"c++23": 202202},
-            "headers": ["type_traits"],
-            "unimplemented": True,
+            "headers": ["type_traits"]
         },
         {
             "name": "__cpp_lib_reference_wrapper",

>From 32e22fcd81abc2282f3cbe06ba2ccc5e4cce74d8 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 7 Jul 2026 01:04:16 -0700
Subject: [PATCH 2/2] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/utils/generate_feature_test_macro_components.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 9794f3d453703..26cf78154205d 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1223,7 +1223,7 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_reference_from_temporary",
             "values": {"c++23": 202202},
-            "headers": ["type_traits"]
+            "headers": ["type_traits"],
         },
         {
             "name": "__cpp_lib_reference_wrapper",


From libcxx-commits at lists.llvm.org  Tue Jul  7 01:20:46 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Tue, 07 Jul 2026 01:20:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Always enable
	-fvisibility=hidden (PR #207333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207333@github.com>
Message-ID: <6a4cb6de.8e75e0cb.5e108.c13e@mx.google.com>


mstorsjo wrote:

This change broke building for Windows (or at least mingw) if Clang is built with asserts enabled: https://github.com/mstorsjo/llvm-mingw/actions/runs/28837278474/job/85523635822

(Unfortunately for this case, the libcxx CI doesn't run with an asserts enabled Clang - although that would be a fair bit slower.)

Building with an asserts enabled Clang produces errors like this:
```
dllexport GlobalValue must have default or protected visibility
ptr @_ZTIv
[repeated for dozens of symbols]
fatal error: error in backend: Broken module found, compilation aborted!
```

So for symbols that are marked dllexport, you can't have hidden visibility. For the same set of symbols, you'd either have dllexport vs no-dllexport, or hidden vs default visibility. Intermixing the two probably doesn't give reasonable results, and specifically a symbol marked both dllexport and hidden is tautological.

Can we revert this for now? I think it would be safe to reland with the `target_add_compile_flags_if_supported()` enclosed in `if (NOT WIN32)`. (Or if you prefer I can try to post a patch to just add such conditions.)

https://github.com/llvm/llvm-project/pull/207333

From libcxx-commits at lists.llvm.org  Tue Jul  7 01:29:44 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 01:29:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement any_of in terms of
	find_if (PR #207274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207274@github.com>
Message-ID: <6a4cb8f8.d5797644.338ca1.5b47@mx.google.com>


================
@@ -10,33 +10,37 @@
 #ifndef _LIBCPP___ALGORITHM_ANY_OF_H
 #define _LIBCPP___ALGORITHM_ANY_OF_H
 
+#include <__algorithm/find_if.h>
 #include <__config>
 #include <__functional/identity.h>
-#include <__type_traits/invoke.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Iter, class _Sent, class _Proj, class _Pred>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
 __any_of(_Iter __first, _Sent __last, _Pred& __pred, _Proj& __proj) {
-  for (; __first != __last; ++__first) {
-    if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
-      return true;
-  }
-  return false;
+  auto __found = std::__find_if(std::move(__first), __last, __pred, __proj);
+  return __found != __last;
----------------
philnik777 wrote:

```suggestion
  return std::__find_if(std::move(__first), __last, __pred, __proj) != __last;
```
No strong feelings either way. (Though I don't like `__found`, since it sounds like a bool)

https://github.com/llvm/llvm-project/pull/207274

From libcxx-commits at lists.llvm.org  Tue Jul  7 01:29:44 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 01:29:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement any_of in terms of
	find_if (PR #207274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207274@github.com>
Message-ID: <6a4cb8f8.14d9b9dc.f3195.8d34@mx.google.com>


================
@@ -10,33 +10,37 @@
 #ifndef _LIBCPP___ALGORITHM_ANY_OF_H
 #define _LIBCPP___ALGORITHM_ANY_OF_H
 
+#include <__algorithm/find_if.h>
 #include <__config>
 #include <__functional/identity.h>
-#include <__type_traits/invoke.h>
+#include <__utility/forward.h>
----------------
philnik777 wrote:

This seems unused?

https://github.com/llvm/llvm-project/pull/207274

From libcxx-commits at lists.llvm.org  Tue Jul  7 01:54:00 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 01:54:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for std::is_heap
 and std::is_heap_until (PR #207045)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207045@github.com>
Message-ID: <6a4cbea8.449bfb44.172ed9.2798@mx.google.com>


================
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_is_heap      = [](auto first, auto last) { return std::is_heap(first, last); };
+  auto std_is_heap_pred = [](auto first, auto last) {
+    return std::is_heap(first, last, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
----------------
philnik777 wrote:

I don't think we want this here. This basically just disabled auto-vectorization, but I don't see how this could change how the library works.

https://github.com/llvm/llvm-project/pull/207045

From libcxx-commits at lists.llvm.org  Tue Jul  7 01:54:00 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 01:54:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for std::is_heap
 and std::is_heap_until (PR #207045)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207045@github.com>
Message-ID: <6a4cbea8.6c8668f2.e4f82.c58d@mx.google.com>


================
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_is_heap      = [](auto first, auto last) { return std::is_heap(first, last); };
+  auto std_is_heap_pred = [](auto first, auto last) {
+    return std::is_heap(first, last, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+  auto std_is_heap_until      = [](auto first, auto last) { return std::is_heap_until(first, last); };
+  auto std_is_heap_until_pred = [](auto first, auto last) {
+    return std::is_heap_until(first, last, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+
+  // Benchmark {std,ranges}::{is_heap,is_heap_until} on a valid heap. This is the worst case since the
+  // whole range must be scanned before we can conclude the heap invariant holds.
+  {
+    auto bm = []<class Container>(std::string name, auto is_heap) {
+      benchmark::RegisterBenchmark(
+          name,
+          [is_heap](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            std::vector<ValueType> data;
+            std::generate_n(std::back_inserter(data), size, [] { return Generate<ValueType>::random(); });
+            std::make_heap(data.begin(), data.end());
+
+            Container c(data.begin(), data.end());
+
+            for ([[maybe_unused]] auto _ : st) {
----------------
philnik777 wrote:

```suggestion
            for (auto _ : st) {
```
I don't think this will be diagnosed.

https://github.com/llvm/llvm-project/pull/207045

From libcxx-commits at lists.llvm.org  Tue Jul  7 01:59:35 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 01:59:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][libc] update LLVM-libc test
	status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a4cbff7.af6ba3ed.36d9db.9c8e@mx.google.com>


================
@@ -8,6 +8,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing fwprintf, fwscanf, swscanf, vfwprintf, vfwscanf, vswprintf, vswscanf, wcsftime, vwscanf, wscanf
----------------
philnik777 wrote:

This seems like it could easily become wrong. Maybe just a "various wchar_t functions" or something like that?

https://github.com/llvm/llvm-project/pull/201236

From libcxx-commits at lists.llvm.org  Tue Jul  7 02:03:15 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 02:03:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cc0d3.6c8668f2.e4f82.85b5@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/207692

>From 8551a998c0e6da70f2168cd02b1249b28de12adf Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 5 Jul 2026 01:27:35 -0700
Subject: [PATCH 1/4] Ensure copy-elision for uninitialized_move(_n)

This implements the resolution for LWG issues 3918 and 4452.
---
 libcxx/include/__memory/uninitialized_algorithms.h | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..f5035ea2e21b9 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -28,6 +28,7 @@
 #include <__type_traits/extent.h>
 #include <__type_traits/is_array.h>
 #include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
@@ -145,6 +146,14 @@ uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) {
 
 #if _LIBCPP_STD_VER >= 17
 
+template <class _Iter>
+_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) __deref_move(_Iter& __it) {
+  if constexpr (is_lvalue_reference_v<decltype(*__it)>)
+    return std::move(*__it);
+  else
+    return *__it;
+}
+
 // uninitialized_default_construct
 
 template <class _ValueType, class _ForwardIterator, class _Sentinel>
@@ -257,7 +266,7 @@ template <class _InputIterator, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
 uninitialized_move(_InputIterator __ifirst, _InputIterator __ilast, _ForwardIterator __ofirst) {
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
-  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::move(*__iter); };
+  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::__deref_move(__iter); };
 
   auto __result = std::__uninitialized_move<_ValueType>(
       std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __always_false(), __iter_move);
@@ -288,7 +297,7 @@ template <class _InputIterator, class _Size, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<_InputIterator, _ForwardIterator>
 uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst) {
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
-  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::move(*__iter); };
+  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::__deref_move(__iter); };
 
   auto __result = std::__uninitialized_move_n<_ValueType>(
       std::move(__ifirst), __n, std::move(__ofirst), __always_false(), __iter_move);

>From dad12baeec8d251e68ee97a73f237588da3518df Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 6 Jul 2026 02:38:47 -0700
Subject: [PATCH 2/4] Test changes to uninitialized_move(_n)

This tests that copies are elided for prvalues. It includes a
constexpr test in C++26.
---
 .../uninitialized_move.pass.cpp               | 62 ++++++++++++++++++
 .../uninitialized_move_n.pass.cpp             | 65 +++++++++++++++++++
 2 files changed, 127 insertions(+)

diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
index 13fef7a972e95..a248e25e8d966 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
+
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using difference_type   = std::ptrdiff_t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
+
+  constexpr NoMoveNoCopy operator*() const { return NoMoveNoCopy(*ptr_); }
+
+  constexpr PrvalueIterator& operator++() {
+    ++ptr_;
+    return *this;
+  }
+
+  friend constexpr bool operator==(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ == b.ptr_; }
+  friend constexpr bool operator!=(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ != b.ptr_; }
+
+private:
+  const int* ptr_;
+};
+
 TEST_CONSTEXPR_CXX26 bool test() {
   const int n    = 3;
   MoveOnly in[n] = {1, 2, 3};
@@ -128,9 +162,36 @@ void test_counted()
     assert(Counted::count == 0);
 }
 
+TEST_CONSTEXPR_CXX26 bool test_copy_elision() {
+  using It      = PrvalueIterator;
+  using FIt     = forward_iterator<NoMoveNoCopy*>;
+  const int N   = 5;
+  int values[N] = {1, 2, 3, 4, 5};
+  std::allocator<NoMoveNoCopy> alloc;
+  NoMoveNoCopy* p = alloc.allocate(N);
+  auto ret        = std::uninitialized_move(It(values), It(values + 1), FIt(p));
+  assert(ret == FIt(p + 1));
+  assert(p[0].value == 1);
+  assert(values[0] == 1);
+  ret = std::uninitialized_move(It(values + 1), It(values + N), FIt(p + 1));
+  assert(p[1].value == 2);
+  assert(p[2].value == 3);
+  assert(p[3].value == 4);
+  assert(p[4].value == 5);
+  assert(values[1] == 2);
+  assert(values[2] == 3);
+  assert(values[3] == 4);
+  assert(values[4] == 5);
+  std::destroy(p, p + N);
+  alloc.deallocate(p, N);
+
+  return true;
+}
+
 int main(int, char**) {
     test_counted();
     test_ctor_throws();
+    test_copy_elision();
 
     // Test with an iterator that overloads operator== and operator!= as the input and output iterators
     {
@@ -164,6 +225,7 @@ int main(int, char**) {
     test();
 #if TEST_STD_VER >= 26
     static_assert(test());
+    static_assert(test_copy_elision());
 #endif
 
     return 0;
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
index 785cfb406efd9..ab0b917972963 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
+
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using difference_type   = std::ptrdiff_t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
+
+  constexpr NoMoveNoCopy operator*() const { return NoMoveNoCopy(*ptr_); }
+
+  constexpr PrvalueIterator& operator++() {
+    ++ptr_;
+    return *this;
+  }
+
+  friend constexpr bool operator==(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ == b.ptr_; }
+  friend constexpr bool operator!=(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ != b.ptr_; }
+
+private:
+  const int* ptr_;
+};
+
 TEST_CONSTEXPR_CXX26 bool test() {
   const int n    = 3;
   MoveOnly in[n] = {1, 2, 3};
@@ -99,6 +133,35 @@ void test_ctor_throws()
 #endif
 }
 
+TEST_CONSTEXPR_CXX26 bool test_copy_elision() {
+  using It      = PrvalueIterator;
+  using FIt     = forward_iterator<NoMoveNoCopy*>;
+  const int N   = 5;
+  int values[N] = {1, 2, 3, 4, 5};
+  std::allocator<NoMoveNoCopy> alloc;
+  NoMoveNoCopy* p = alloc.allocate(N);
+  auto ret        = std::uninitialized_move_n(It(values), 1, FIt(p));
+  assert(ret.first == It(values + 1));
+  assert(ret.second == FIt(p + 1));
+  assert(p[0].value == 1);
+  assert(values[0] == 1);
+  ret = std::uninitialized_move_n(It(values + 1), N - 1, FIt(p + 1));
+  assert(ret.first == It(values + N));
+  assert(ret.second == FIt(p + N));
+  assert(p[1].value == 2);
+  assert(p[2].value == 3);
+  assert(p[3].value == 4);
+  assert(p[4].value == 5);
+  assert(values[1] == 2);
+  assert(values[2] == 3);
+  assert(values[3] == 4);
+  assert(values[4] == 5);
+  std::destroy(p, p + N);
+  alloc.deallocate(p, N);
+
+  return true;
+}
+
 void test_counted()
 {
     using It = cpp17_input_iterator<int*>;
@@ -135,6 +198,7 @@ int main(int, char**)
 {
     test_counted();
     test_ctor_throws();
+    test_copy_elision();
 
     // Test with an iterator that overloads operator== and operator!= as the input and output iterators
     {
@@ -168,6 +232,7 @@ int main(int, char**)
     test();
 #if TEST_STD_VER >= 26
     static_assert(test());
+    static_assert(test_copy_elision());
 #endif
 
     return 0;

>From 0a53270b60cc74d91306e400dee5163777b9e180 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 6 Jul 2026 02:47:30 -0700
Subject: [PATCH 3/4] Mark LWG issues 3918 and 4452 as complete

---
 libcxx/docs/Status/Cxx26Issues.csv | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 3f2a446f7e1f3..cba4246e05361 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -81,7 +81,7 @@
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
 "`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
 "`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
-"`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
+"`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","|Complete|","23","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
 "`LWG4027 <https://wg21.link/LWG4027>`__","``possibly-const-range`` should prefer returning ``const R&``","2024-11 (Wrocław)","","","`#118342 <https://github.com/llvm/llvm-project/issues/118342>`__",""
@@ -250,7 +250,7 @@
 "`LWG4449 <https://wg21.link/LWG4449>`__","``define_aggregate`` members must be public","2025-11 (Kona)","","","`#171412 <https://github.com/llvm/llvm-project/issues/171412>`__",""
 "`LWG4450 <https://wg21.link/LWG4450>`__","``std::atomic_ref<T>::store_key`` should be disabled for const ``T``","2025-11 (Kona)","","","`#171414 <https://github.com/llvm/llvm-project/issues/171414>`__",""
 "`LWG4451 <https://wg21.link/LWG4451>`__","``make_shared`` should not refer to a type ``U[N]`` for runtime N","2025-11 (Kona)","","","`#171415 <https://github.com/llvm/llvm-project/issues/171415>`__",""
-"`LWG4452 <https://wg21.link/LWG4452>`__","Make *deref-move* constexpr","2025-11 (Kona)","","","`#171416 <https://github.com/llvm/llvm-project/issues/171416>`__",""
+"`LWG4452 <https://wg21.link/LWG4452>`__","Make *deref-move* constexpr","2025-11 (Kona)","|Complete|","23","`#171416 <https://github.com/llvm/llvm-project/issues/171416>`__",""
 "`LWG4455 <https://wg21.link/LWG4455>`__","Add missing constraint to ``basic-sender::get_completion_signatures`` definition","2025-11 (Kona)","","","`#171417 <https://github.com/llvm/llvm-project/issues/171417>`__",""
 "`LWG4456 <https://wg21.link/LWG4456>`__","Decay ``Data`` and ``Child`` in ``make-sender``","2025-11 (Kona)","","","`#171418 <https://github.com/llvm/llvm-project/issues/171418>`__",""
 "`LWG4459 <https://wg21.link/LWG4459>`__","Protect ``get_completion_signatures`` fold expression from overloaded commas","2025-11 (Kona)","","","`#171419 <https://github.com/llvm/llvm-project/issues/171419>`__",""

>From a6a7201818767443211cb4dd5a2c9c9ea5fd4494 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 01:52:34 -0700
Subject: [PATCH 4/4] Address review comments

The changes here alter formatting and stylistic choices in tests,
but do not change the underlying text logic.
---
 .../uninitialized.move/uninitialized_move.pass.cpp          | 6 ++----
 .../uninitialized.move/uninitialized_move_n.pass.cpp        | 6 ++----
 2 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
index a248e25e8d966..ac348ac88164d 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
@@ -58,9 +58,7 @@ int ThrowsCounted::throw_after = 0;
 
 struct NoMoveNoCopy {
   constexpr explicit NoMoveNoCopy(int x) : value(x) {}
-  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
-  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
-
+  NoMoveNoCopy(const NoMoveNoCopy&)   = delete;
   friend void operator&(NoMoveNoCopy) = delete;
   int value;
 };
@@ -74,7 +72,7 @@ class PrvalueIterator {
   using value_type        = NoMoveNoCopy;
 
   PrvalueIterator() = delete;
-  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
+  constexpr explicit PrvalueIterator(const int* ptr) : ptr_(ptr) {}
 
   constexpr NoMoveNoCopy operator*() const { return NoMoveNoCopy(*ptr_); }
 
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
index ab0b917972963..2483a5519d51e 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
@@ -58,9 +58,7 @@ int ThrowsCounted::throw_after = 0;
 
 struct NoMoveNoCopy {
   constexpr explicit NoMoveNoCopy(int x) : value(x) {}
-  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
-  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
-
+  NoMoveNoCopy(const NoMoveNoCopy&)   = delete;
   friend void operator&(NoMoveNoCopy) = delete;
   int value;
 };
@@ -74,7 +72,7 @@ class PrvalueIterator {
   using value_type        = NoMoveNoCopy;
 
   PrvalueIterator() = delete;
-  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
+  constexpr explicit PrvalueIterator(const int* ptr) : ptr_(ptr) {}
 
   constexpr NoMoveNoCopy operator*() const { return NoMoveNoCopy(*ptr_); }
 


From libcxx-commits at lists.llvm.org  Tue Jul  7 02:08:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 02:08:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cc1f9.da8bafd3.14cd50.f415@mx.google.com>


================
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
----------------
inquisitivecrystal wrote:

Done!

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 02:08:18 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 02:08:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cc202.bd93e731.251b21.f467@mx.google.com>


================
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
----------------
inquisitivecrystal wrote:

Done!

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 02:08:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 02:08:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cc20c.abb10101.3d0bf8.067b@mx.google.com>


================
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
+
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using difference_type   = std::ptrdiff_t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
----------------
inquisitivecrystal wrote:

Done!

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 02:08:48 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 02:08:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cc220.510f576d.1b3cb9.304f@mx.google.com>


================
@@ -56,6 +56,40 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(NoMoveNoCopy const&) { assert(false); }
+  NoMoveNoCopy(NoMoveNoCopy const&&) { assert(false); }
+
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using difference_type   = std::ptrdiff_t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr PrvalueIterator(const int* ptr) : ptr_(ptr) {}
----------------
inquisitivecrystal wrote:

Done! Thank you for all the review comments, they were very helpful.

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 02:13:25 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 07 Jul 2026 02:13:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Set
 `__cpp_lib_reference_from_temporary` (PR #207920)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207920@github.com>
Message-ID: <6a4cc335.449bfb44.172ed9.745c@mx.google.com>


Zingam wrote:

> Why don't we just set it in the other PR if it's all that's left?

https://github.com/llvm/llvm-project/issues/105180


I don't think we are ready to set the FTM yet?? Am I wrong?

https://github.com/llvm/llvm-project/pull/207920

From libcxx-commits at lists.llvm.org  Tue Jul  7 02:17:08 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Tue, 07 Jul 2026 02:17:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libcxxabi] Don't enable
 -fvisibility=hidden on Windows (PR #207943)
Message-ID: <llvm.org/llvm/llvm-project/pull/207943@github.com>


https://github.com/mstorsjo created https://github.com/llvm/llvm-project/pull/207943

This option is supported (and hence gets added by the target_add_compile_flags_if_supported function) on Windows, but using hidden/default visibility should be mutually exclusive to using dllexport vs no dllexport.

In asserts enabled builds of Clang, building with -fvisibility=hidden caused errors like this:

    dllexport GlobalValue must have default or protected visibility
    ptr @_ZTIv
    [...]
    fatal error: error in backend: Broken module found, compilation aborted!

Since we're using dllexport on Windows to limit which symbols get exported, don't try to use -fvisibility=hidden. (If we'd use that option, we would also need to use per-symbol attributes to mark symbols with default visibility for the symbols that we do want exported.)

This fixes Windows builds with Clang with asserts enabled, after cb942d009a5ed029ce20d9b75b3604f551f0cb56.

>From 983bb575bf736c41de4da140753456c21980911a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin at martin.st>
Date: Tue, 7 Jul 2026 12:12:48 +0300
Subject: [PATCH] [libcxxabi] Don't enable -fvisibility=hidden on Windows

This option is supported (and hence gets added by the
target_add_compile_flags_if_supported function) on Windows, but
using hidden/default visibility should be mutually exclusive to
using dllexport vs no dllexport.

In asserts enabled builds of Clang, building with -fvisibility=hidden
caused errors like this:

    dllexport GlobalValue must have default or protected visibility
    ptr @_ZTIv
    [...]
    fatal error: error in backend: Broken module found, compilation aborted!

Since we're using dllexport on Windows to limit which symbols
get exported, don't try to use -fvisibility=hidden. (If we'd use
that option, we would also need to use per-symbol attributes to
mark symbols with default visibility for the symbols that we do
want exported.)

This fixes Windows builds with Clang with asserts enabled, after
cb942d009a5ed029ce20d9b75b3604f551f0cb56.
---
 libcxxabi/src/CMakeLists.txt | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index fc66ce7187170..6809ae452228f 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -195,7 +195,10 @@ if (LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
   # Build with -fsized-deallocation, which is default in recent versions of Clang.
   # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
   target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
-  target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
+  if (NOT WIN32)
+    # We use dllexport on Windows, and that is mutually exclusive with visibility attributes.
+    target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
+  endif()
 
   add_library(cxxabi_shared SHARED)
   set_target_properties(cxxabi_shared
@@ -286,7 +289,10 @@ target_compile_options(cxxabi_static_objects PRIVATE "${LIBCXXABI_ADDITIONAL_COM
 # Build with -fsized-deallocation, which is default in recent versions of Clang.
 # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
 target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fsized-deallocation)
-target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
+if (NOT WIN32)
+  # We use dllexport on Windows, and that is mutually exclusive with visibility attributes.
+  target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
+endif()
 
 if(LIBCXXABI_HERMETIC_STATIC_LIBRARY)
   # If the hermetic library doesn't define the operator new/delete functions


From libcxx-commits at lists.llvm.org  Tue Jul  7 02:17:35 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Tue, 07 Jul 2026 02:17:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Always enable
	-fvisibility=hidden (PR #207333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207333@github.com>
Message-ID: <6a4cc42f.70920dee.290dca.634e@mx.google.com>


mstorsjo wrote:

> I think I'd prefer a follow-up. That condition should be simple enough to add, no?

Sure, done - https://github.com/llvm/llvm-project/pull/207943.


https://github.com/llvm/llvm-project/pull/207333

From libcxx-commits at lists.llvm.org  Tue Jul  7 02:17:41 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 02:17:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libcxxabi] Don't enable
 -fvisibility=hidden on Windows (PR #207943)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207943@github.com>
Message-ID: <6a4cc435.eb165e5c.1817a5.724f@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxxabi

Author: Martin Storsjö (mstorsjo)

<details>
<summary>Changes</summary>

This option is supported (and hence gets added by the target_add_compile_flags_if_supported function) on Windows, but using hidden/default visibility should be mutually exclusive to using dllexport vs no dllexport.

In asserts enabled builds of Clang, building with -fvisibility=hidden caused errors like this:

    dllexport GlobalValue must have default or protected visibility
    ptr @<!-- -->_ZTIv
    [...]
    fatal error: error in backend: Broken module found, compilation aborted!

Since we're using dllexport on Windows to limit which symbols get exported, don't try to use -fvisibility=hidden. (If we'd use that option, we would also need to use per-symbol attributes to mark symbols with default visibility for the symbols that we do want exported.)

This fixes Windows builds with Clang with asserts enabled, after cb942d009a5ed029ce20d9b75b3604f551f0cb56.

---
Full diff: https://github.com/llvm/llvm-project/pull/207943.diff


1 Files Affected:

- (modified) libcxxabi/src/CMakeLists.txt (+8-2) 


``````````diff
diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index fc66ce7187170..6809ae452228f 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -195,7 +195,10 @@ if (LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
   # Build with -fsized-deallocation, which is default in recent versions of Clang.
   # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
   target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
-  target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
+  if (NOT WIN32)
+    # We use dllexport on Windows, and that is mutually exclusive with visibility attributes.
+    target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
+  endif()
 
   add_library(cxxabi_shared SHARED)
   set_target_properties(cxxabi_shared
@@ -286,7 +289,10 @@ target_compile_options(cxxabi_static_objects PRIVATE "${LIBCXXABI_ADDITIONAL_COM
 # Build with -fsized-deallocation, which is default in recent versions of Clang.
 # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
 target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fsized-deallocation)
-target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
+if (NOT WIN32)
+  # We use dllexport on Windows, and that is mutually exclusive with visibility attributes.
+  target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
+endif()
 
 if(LIBCXXABI_HERMETIC_STATIC_LIBRARY)
   # If the hermetic library doesn't define the operator new/delete functions

``````````

</details>


https://github.com/llvm/llvm-project/pull/207943

From libcxx-commits at lists.llvm.org  Tue Jul  7 02:23:50 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 02:23:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libcxxabi] Don't enable
 -fvisibility=hidden on Windows (PR #207943)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207943@github.com>
Message-ID: <6a4cc5a6.01f0a1da.1e9662.45d9@mx.google.com>


https://github.com/philnik777 approved this pull request.

LGTM, but I'm a bit confused how this works with libc++. I don't see any similar guards there.

https://github.com/llvm/llvm-project/pull/207943

From libcxx-commits at lists.llvm.org  Tue Jul  7 02:30:19 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 02:30:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a4cc72b.bb839515.27d21e.b7f4@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/21] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/21] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/21] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/21] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/21] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/21] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/21] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/21] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/21] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/21] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/21] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/21] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/21] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/21] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/21] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/21] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/21] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/21] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/21] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/21] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/21] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}


From libcxx-commits at lists.llvm.org  Tue Jul  7 03:09:28 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Tue, 07 Jul 2026 03:09:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Set
 `__cpp_lib_reference_from_temporary` (PR #207920)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207920@github.com>
Message-ID: <6a4cd058.ac469dc7.3a57b1.cc60@mx.google.com>


yronglin wrote:

Ah, sorry for the misunderstanding. We should wait for the three PRs to be merged before setting up FTM.

https://github.com/llvm/llvm-project/pull/207920

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:09:51 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Tue, 07 Jul 2026 03:09:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Set
 `__cpp_lib_reference_from_temporary` (PR #207920)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207920@github.com>
Message-ID: <6a4cd06f.faf16b37.2f0e1d.e832@mx.google.com>


https://github.com/yronglin converted_to_draft https://github.com/llvm/llvm-project/pull/207920

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:11:45 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Tue, 07 Jul 2026 03:11:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Set
 `__cpp_lib_reference_from_temporary` (PR #207920)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207920@github.com>
Message-ID: <6a4cd0e1.f732ad81.3a4994.95bf@mx.google.com>


https://github.com/yronglin edited https://github.com/llvm/llvm-project/pull/207920

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:19:04 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 03:19:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cd298.27b3b003.fc443.e409@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:20:03 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 03:20:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cd2d3.c56fb102.2b5c33.cf6c@mx.google.com>


frederick-vs-ja wrote:

I rephrased the PR description which will be used as squashed commit message by default. Please double check the changes. Feel free to improve it again.

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:26:37 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Tue, 07 Jul 2026 03:26:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libcxxabi] Don't enable
 -fvisibility=hidden on Windows (PR #207943)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207943@github.com>
Message-ID: <6a4cd45d.01f0a1da.1e9662.f67b@mx.google.com>


mstorsjo wrote:

> LGTM, but I'm a bit confused how this works with libc++. I don't see any similar guards there.

That's a very good point.

First I thought that `_LibcxxExplicitABIAnnotations.push` which touches visibility and similar things could be involved, but it turns out to be even much more subtle than that.

A trivial example like this doesn't trigger the issue:
```c++
void __declspec(dllexport) func(void) {
}
```
```console
$ clang -target x86_64-windows-gnu -c export.cpp -fvisibility=hidden
$ clang -target x86_64-windows-gnu -S -emit-llvm export.cpp -fvisibility=hidden -o -
[...]

; Function Attrs: mustprogress noinline nounwind optnone
define dso_local dllexport void @_Z4funcv() #0 {
entry:
  ret void
}

[...]
```

So for such a case, Clang does know and set the right nonconflicting attributes - dllexport only, nothing else.

I took the source file that triggered the issue and fed it to cvise to reduce it, and came up with this:
```c++
namespace __cxxabiv1 {
struct __attribute__((dllexport)) __fundamental_type_info {
  virtual ~__fundamental_type_info();
};
__fundamental_type_info::~__fundamental_type_info() {}
} // namespace __cxxabiv1
```
```console
$ clang -target x86_64-windows-gnu -c repro.cpp -fvisibility=hidden
dllexport GlobalValue must have default or protected visibility
ptr @_ZTIv
dllexport GlobalValue must have default or protected visibility
ptr @_ZTSv
dllexport GlobalValue must have default or protected visibility
ptr @_ZTIPv
[...]
fatal error: error in backend: Broken module found, compilation
      aborted!
```

So apparently there's some very custom code generation triggered around the class `__cxxabiv1::__fundamental_type_info`, in `clang/lib/CodeGen/ItaniumCXXABI.cpp`, which ends up with inconsistent attributes on the generated symbols here.

So this is even clearer for a bug report to Clang itself for now. The best workaround probably also is roughly the same, but we probably should tweak the comments a bit...

https://github.com/llvm/llvm-project/pull/207943

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:33:21 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 03:33:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cd5f1.2020f6c2.243e78.ab42@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:34:18 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 03:34:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cd62a.f8b79514.1d52d0.1e2f@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:35:22 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 03:35:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cd66a.fdc477d6.23591.284d@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:36:31 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Tue, 07 Jul 2026 03:36:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libcxxabi] Don't enable
 -fvisibility=hidden on Windows (PR #207943)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207943@github.com>
Message-ID: <6a4cd6af.05c456d6.33bf9d.05a3@mx.google.com>


mstorsjo wrote:

I filed https://github.com/llvm/llvm-project/issues/207963 for the issue itself.

https://github.com/llvm/llvm-project/pull/207943

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:46:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 03:46:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cd8ff.f732ad81.3a4994.48df@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:47:59 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 03:47:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cd95f.f732ad81.3a4994.8a20@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:48:04 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Tue, 07 Jul 2026 03:48:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libcxxabi] Don't enable
 -fvisibility=hidden on Windows (PR #207943)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207943@github.com>
Message-ID: <6a4cd964.27b3b003.fc443.9b03@mx.google.com>


https://github.com/mstorsjo updated https://github.com/llvm/llvm-project/pull/207943

>From 983bb575bf736c41de4da140753456c21980911a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin at martin.st>
Date: Tue, 7 Jul 2026 12:12:48 +0300
Subject: [PATCH 1/2] [libcxxabi] Don't enable -fvisibility=hidden on Windows

This option is supported (and hence gets added by the
target_add_compile_flags_if_supported function) on Windows, but
using hidden/default visibility should be mutually exclusive to
using dllexport vs no dllexport.

In asserts enabled builds of Clang, building with -fvisibility=hidden
caused errors like this:

    dllexport GlobalValue must have default or protected visibility
    ptr @_ZTIv
    [...]
    fatal error: error in backend: Broken module found, compilation aborted!

Since we're using dllexport on Windows to limit which symbols
get exported, don't try to use -fvisibility=hidden. (If we'd use
that option, we would also need to use per-symbol attributes to
mark symbols with default visibility for the symbols that we do
want exported.)

This fixes Windows builds with Clang with asserts enabled, after
cb942d009a5ed029ce20d9b75b3604f551f0cb56.
---
 libcxxabi/src/CMakeLists.txt | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index fc66ce7187170..6809ae452228f 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -195,7 +195,10 @@ if (LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
   # Build with -fsized-deallocation, which is default in recent versions of Clang.
   # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
   target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
-  target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
+  if (NOT WIN32)
+    # We use dllexport on Windows, and that is mutually exclusive with visibility attributes.
+    target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
+  endif()
 
   add_library(cxxabi_shared SHARED)
   set_target_properties(cxxabi_shared
@@ -286,7 +289,10 @@ target_compile_options(cxxabi_static_objects PRIVATE "${LIBCXXABI_ADDITIONAL_COM
 # Build with -fsized-deallocation, which is default in recent versions of Clang.
 # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
 target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fsized-deallocation)
-target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
+if (NOT WIN32)
+  # We use dllexport on Windows, and that is mutually exclusive with visibility attributes.
+  target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
+endif()
 
 if(LIBCXXABI_HERMETIC_STATIC_LIBRARY)
   # If the hermetic library doesn't define the operator new/delete functions

>From abf0abe7229098bd577df6cf5ea2699027c319e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin at martin.st>
Date: Tue, 7 Jul 2026 13:47:37 +0300
Subject: [PATCH 2/2] Clarify the comment and reference the clang bug

---
 libcxxabi/src/CMakeLists.txt | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index 6809ae452228f..dd23e9bb5bf79 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -196,7 +196,11 @@ if (LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
   # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
   target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
   if (NOT WIN32)
-    # We use dllexport on Windows, and that is mutually exclusive with visibility attributes.
+    # Building libcxxabi with -fvisibility=hidden on Windows, if dllexport is
+    # used, can trigger a Clang error (if Clang is built with asserts enabled),
+    # https://github.com/llvm/llvm-project/issues/207963
+    # Once fixed, we could consider allowing -fvisibility=hidden on
+    # Windows too (as it can be beneficial for static libraries).
     target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
   endif()
 
@@ -290,7 +294,11 @@ target_compile_options(cxxabi_static_objects PRIVATE "${LIBCXXABI_ADDITIONAL_COM
 # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
 target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fsized-deallocation)
 if (NOT WIN32)
-  # We use dllexport on Windows, and that is mutually exclusive with visibility attributes.
+  # Building libcxxabi with -fvisibility=hidden on Windows, if dllexport is
+  # used, can trigger a Clang error (if Clang is built with asserts enabled),
+  # https://github.com/llvm/llvm-project/issues/207963
+  # Once fixed, we could consider allowing -fvisibility=hidden on
+  # Windows too (as it can be beneficial for static libraries).
   target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
 endif()
 


From libcxx-commits at lists.llvm.org  Tue Jul  7 03:48:27 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Tue, 07 Jul 2026 03:48:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libcxxabi] Don't enable
 -fvisibility=hidden on Windows (PR #207943)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207943@github.com>
Message-ID: <6a4cd97b.ff037943.2e959a.b5c8@mx.google.com>


mstorsjo wrote:

And actually, compiling with `-fvisibility=hidden`, if we could do it, would be beneficial for the static library builds.

If a user statically links in libcxxabi into their own DLL, and there's no explicit dllexports, then the mingw linkers default to exporting all symbols. This would end up exporting the statically linked libcxx/libcxxabi symbols too, if we didn't have explicit excludes for them in LLD, https://github.com/llvm/llvm-project/blob/llvmorg-22.1.8/lld/COFF/MinGW.cpp#L51-L52. But if those libraries are built with `-fvisibility=hidden`, it would work even despite that.

So this workaround probably is reasonable as such, but we could consider reverting it at some point in the future if Clang is fixed to not trip up on this at some point. I updated the code comment to reflect this, and to reference the bug.

https://github.com/llvm/llvm-project/pull/207943

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:50:05 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 03:50:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cd9dd.82d06577.0dff.1192@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:50:40 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Tue, 07 Jul 2026 03:50:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libcxxabi] Don't enable
 -fvisibility=hidden on Windows (PR #207943)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207943@github.com>
Message-ID: <6a4cda00.449bfb44.172ed9.ab92@mx.google.com>


https://github.com/mstorsjo edited https://github.com/llvm/llvm-project/pull/207943

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:50:44 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 03:50:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cda04.481afadf.196d57.e9d2@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:51:20 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 03:51:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libcxxabi] Don't enable
 -fvisibility=hidden on Windows (PR #207943)
Message-ID: <6a4cda28.8b87e04f.184605.de06@mx.google.com>

Martin =?utf-8?q?Storsj=C3=B6?= <martin at martin.st>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207943 at github.com>


https://github.com/philnik777 approved this pull request.

Thanks for looking into it! libc++abi triggers some special magic indeed.

https://github.com/llvm/llvm-project/pull/207943

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:51:57 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 03:51:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cda4d.bb985344.1e9879.c841@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:54:31 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 03:54:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cdae7.6c8668f2.e4f82.83a3@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:56:19 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 03:56:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Set
 `__cpp_lib_reference_from_temporary` (PR #207920)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207920@github.com>
Message-ID: <6a4cdb53.bf38ae28.39239a.586e@mx.google.com>


frederick-vs-ja wrote:

If we want a separated FTM bumping PR, we may also want that PR to update release notes and CSV lists.

But do we really want such a PR?

https://github.com/llvm/llvm-project/pull/207920

From libcxx-commits at lists.llvm.org  Tue Jul  7 03:57:03 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 03:57:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cdb7f.7d41f3a1.237e53.cfae@mx.google.com>


inquisitivecrystal wrote:

I've reworded it further to flow a bit better.

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 04:01:21 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 04:01:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4cdc81.70920dee.290dca.c8e5@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Tue Jul  7 04:04:44 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 04:04:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Update the label for FreeBSD in
 CI to version 15.1 (PR #205417)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205417@github.com>
Message-ID: <6a4cdd4c.eb165e5c.1817a5.175c@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/205417

From libcxx-commits at lists.llvm.org  Tue Jul  7 04:05:15 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 04:05:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 7b4fd87 - [libc++] Mark LWG3585 as
	resolved (#207717)
Message-ID: <6a4cdd6b.6c8668f2.e4f82.55ce@mx.google.com>


Author: Connector Switch
Date: 2026-07-07T19:05:10+08:00
New Revision: 7b4fd8762aa76e8b256afb51043a1508af2646fe

URL: https://github.com/llvm/llvm-project/commit/7b4fd8762aa76e8b256afb51043a1508af2646fe
DIFF: https://github.com/llvm/llvm-project/commit/7b4fd8762aa76e8b256afb51043a1508af2646fe.diff

LOG: [libc++] Mark LWG3585 as resolved (#207717)

- Close: https://github.com/llvm/llvm-project/issues/104934.
- Commit:
https://github.com/llvm/llvm-project/commit/0d3d8de014f1012dd0b2c2c077099cf6face44e5
- Test:
https://github.com/llvm/llvm-project/blob/52271a5c11f6abde1fa1221db304212b5eb8ec7c/libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp#L29-L34

Added: 
    

Modified: 
    libcxx/docs/Status/Cxx23Issues.csv

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 8c4cf77d42fbb..d0616fb46681c 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -129,7 +129,7 @@
 "`LWG3574 <https://wg21.link/LWG3574>`__","``common_iterator`` should be completely ``constexpr``-able","2021-10 (Virtual)","|Complete|","14","`#104931 <https://github.com/llvm/llvm-project/issues/104931>`__",""
 "`LWG3580 <https://wg21.link/LWG3580>`__","``iota_view``'s ``iterator``'s binary ``operator+`` should be improved","2021-10 (Virtual)","|Complete|","14","`#104932 <https://github.com/llvm/llvm-project/issues/104932>`__",""
 "`LWG3581 <https://wg21.link/LWG3581>`__","The range constructor makes ``basic_string_view`` not trivially move constructible","2021-10 (Virtual)","|Complete|","14","`#104933 <https://github.com/llvm/llvm-project/issues/104933>`__",""
-"`LWG3585 <https://wg21.link/LWG3585>`__","``variant`` converting assignment with immovable alternative","2021-10 (Virtual)","","","`#104934 <https://github.com/llvm/llvm-project/issues/104934>`__",""
+"`LWG3585 <https://wg21.link/LWG3585>`__","``variant`` converting assignment with immovable alternative","2021-10 (Virtual)","|Complete|","4","`#104934 <https://github.com/llvm/llvm-project/issues/104934>`__",""
 "`LWG3589 <https://wg21.link/LWG3589>`__","The ``const`` lvalue reference overload of ``get`` for ``subrange`` does not constrain ``I`` to be ``copyable`` when ``N == 0``","2021-10 (Virtual)","|Complete|","14","`#104935 <https://github.com/llvm/llvm-project/issues/104935>`__",""
 "`LWG3590 <https://wg21.link/LWG3590>`__","``split_view::base() const &`` is overconstrained","2021-10 (Virtual)","|Complete|","16","`#104936 <https://github.com/llvm/llvm-project/issues/104936>`__",""
 "`LWG3591 <https://wg21.link/LWG3591>`__","``lazy_split_view<input_view>::inner-iterator::base() &&`` invalidates outer iterators","2021-10 (Virtual)","","","`#104937 <https://github.com/llvm/llvm-project/issues/104937>`__",""


        

From libcxx-commits at lists.llvm.org  Tue Jul  7 04:05:17 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Tue, 07 Jul 2026 04:05:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3585 as resolved (PR
	#207717)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207717@github.com>
Message-ID: <6a4cdd6d.8e75e0cb.5e108.e72e@mx.google.com>


https://github.com/c8ef closed https://github.com/llvm/llvm-project/pull/207717

From libcxx-commits at lists.llvm.org  Tue Jul  7 04:05:48 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 04:05:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Update the label for FreeBSD in
 CI to version 15.1 (PR #205417)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205417@github.com>
Message-ID: <6a4cdd8c.13be6df8.145c.f57f@mx.google.com>


frederick-vs-ja wrote:

@aokblast @philnik777 I rephrased the PR title to reflect the IMO actual change. Please double check it.

https://github.com/llvm/llvm-project/pull/205417

From libcxx-commits at lists.llvm.org  Tue Jul  7 04:09:42 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 04:09:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Update the label for FreeBSD in
 CI to version 15.1 (PR #205417)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205417@github.com>
Message-ID: <6a4cde76.14d9b9dc.f3195.3ab7@mx.google.com>


https://github.com/philnik777 commented:

The CI claims `target=x86_64-unknown-freebsd13.4`. What's going on?

https://github.com/llvm/llvm-project/pull/205417

From libcxx-commits at lists.llvm.org  Tue Jul  7 04:21:02 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 04:21:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a4ce11e.510f576d.1b3cb9.90f9@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/22] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/22] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/22] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/22] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/22] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/22] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/22] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/22] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/22] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/22] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/22] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/22] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/22] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/22] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/22] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/22] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/22] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/22] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/22] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/22] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/22] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}

>From 86f59d72480a80cb0d3325cac1ee59d5b08524bb Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 04:18:49 -0700
Subject: [PATCH 22/22] Permit reserve_hint's unqualified lookup

reserve_hint must use ADL per the specification, and thus needs to
be exempted from the libcpp-robust-against-adl check like other
CPOs.
---
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..16c45a524811e 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -20,7 +20,8 @@ AST_MATCHER(clang::CallExpr, isOperator) { return llvm::isa<clang::CXXOperatorCa
 
 AST_MATCHER(clang::UnresolvedLookupExpr, isCustomizationPoint) {
   return std::ranges::any_of(
-      std::array{"swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin"},
+      std::array{
+          "swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin", "reserve_hint"},
       [&](const char* func) { return Node.getName().getAsString() == func; });
 }
 


From libcxx-commits at lists.llvm.org  Tue Jul  7 04:22:18 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 04:22:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a4ce16a.492e23e4.157d47.5210@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Tue Jul  7 05:28:05 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 05:28:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Overhaul how we check ABI
	stability (PR #207989)
Message-ID: <llvm.org/llvm/llvm-project/pull/207989@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/207989

None

>From dec3ec9dcc1ea8347c31b129dcc693a7623415f4 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 7 Jul 2026 14:27:43 +0200
Subject: [PATCH] [libc++] Overhaul how we check ABI stability

---
 .../arm64-apple-darwin/abilist.sh.cpp         |   18 +
 .../arm64-apple-darwin/expected.abilist       | 2294 +++++++++++++++++
 .../x86_64-apple-darwin/abilist.sh.cpp        |   18 +
 .../x86_64-apple-darwin/expected.abilist      | 2294 +++++++++++++++++
 .../x86_64-unknown-linux-gnu/abilist.sh.cpp   |   18 +
 .../x86_64-unknown-linux-gnu/expected.abilist | 1973 ++++++++++++++
 6 files changed, 6615 insertions(+)
 create mode 100644 libcxx/test/libcxx/abilists/arm64-apple-darwin/abilist.sh.cpp
 create mode 100644 libcxx/test/libcxx/abilists/arm64-apple-darwin/expected.abilist
 create mode 100644 libcxx/test/libcxx/abilists/x86_64-apple-darwin/abilist.sh.cpp
 create mode 100644 libcxx/test/libcxx/abilists/x86_64-apple-darwin/expected.abilist
 create mode 100644 libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/abilist.sh.cpp
 create mode 100644 libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/expected.abilist

diff --git a/libcxx/test/libcxx/abilists/arm64-apple-darwin/abilist.sh.cpp b/libcxx/test/libcxx/abilists/arm64-apple-darwin/abilist.sh.cpp
new file mode 100644
index 0000000000000..12e790bdd03b7
--- /dev/null
+++ b/libcxx/test/libcxx/abilists/arm64-apple-darwin/abilist.sh.cpp
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FIXME: Encode whether the library was built with exceptions and check the abilist for that case as well
+
+// REQUIRES: target=arm64-apple-darwin{{.*}}
+
+// Check that the list of symbols exported from the dylib doesn't change unexpectedly
+
+// FILE_DEPENDENCIES: expected.abilist
+
+// RUN: nm --defined-only --extern-only %{lib-dir}/libc++.so.1.0 | cut -c 18- > %t.actual.abilist
+// RUN: diff expected.abilist %t.actual.abilist
diff --git a/libcxx/test/libcxx/abilists/arm64-apple-darwin/expected.abilist b/libcxx/test/libcxx/abilists/arm64-apple-darwin/expected.abilist
new file mode 100644
index 0000000000000..97550cd99f7d4
--- /dev/null
+++ b/libcxx/test/libcxx/abilists/arm64-apple-darwin/expected.abilist
@@ -0,0 +1,2294 @@
+I __ZNKSt10bad_typeid4whatEv (indirect for __ZNKSt10bad_typeid4whatEv)
+I __ZNKSt11logic_error4whatEv (indirect for __ZNKSt11logic_error4whatEv)
+T __ZNKSt12bad_any_cast4whatEv
+T __ZNKSt12experimental15fundamentals_v112bad_any_cast4whatEv
+I __ZNKSt13bad_exception4whatEv (indirect for __ZNKSt13bad_exception4whatEv)
+I __ZNKSt13runtime_error4whatEv (indirect for __ZNKSt13runtime_error4whatEv)
+T __ZNKSt16nested_exception14rethrow_nestedEv
+T __ZNKSt18bad_variant_access4whatEv
+T __ZNKSt19bad_optional_access4whatEv
+I __ZNKSt20bad_array_new_length4whatEv (indirect for __ZNKSt20bad_array_new_length4whatEv)
+T __ZNKSt29__bad_variant_access_with_msg4whatEv
+T __ZNKSt3__110__time_put8__do_putEPcRS1_PK2tmcc
+T __ZNKSt3__110__time_put8__do_putEPwRS1_PK2tmcc
+T __ZNKSt3__110error_code7messageEv
+T __ZNKSt3__110moneypunctIcLb0EE11do_groupingEv
+T __ZNKSt3__110moneypunctIcLb0EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIcLb0EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIcLb0EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIcLb0EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_thousands_sepEv
+T __ZNKSt3__110moneypunctIcLb1EE11do_groupingEv
+T __ZNKSt3__110moneypunctIcLb1EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIcLb1EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIcLb1EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIcLb1EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_thousands_sepEv
+T __ZNKSt3__110moneypunctIwLb0EE11do_groupingEv
+T __ZNKSt3__110moneypunctIwLb0EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIwLb0EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIwLb0EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIwLb0EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_thousands_sepEv
+T __ZNKSt3__110moneypunctIwLb1EE11do_groupingEv
+T __ZNKSt3__110moneypunctIwLb1EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIwLb1EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIwLb1EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIwLb1EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv
+T __ZNKSt3__112bad_weak_ptr4whatEv
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE16find_last_not_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE17find_first_not_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEcm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5rfindEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5rfindEcm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEPKc
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKc
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKcm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmRKS5_mm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE12find_last_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE13find_first_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE16find_last_not_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE17find_first_not_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE2atEm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4copyEPwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4findEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4findEwm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5rfindEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5rfindEwm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEPKw
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmPKw
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmPKwm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmRKS5_mm
+T __ZNKSt3__112ctype_bynameIcE10do_tolowerEPcPKc
+T __ZNKSt3__112ctype_bynameIcE10do_tolowerEc
+T __ZNKSt3__112ctype_bynameIcE10do_toupperEPcPKc
+T __ZNKSt3__112ctype_bynameIcE10do_toupperEc
+T __ZNKSt3__112ctype_bynameIwE10do_scan_isEjPKwS3_
+T __ZNKSt3__112ctype_bynameIwE10do_tolowerEPwPKw
+T __ZNKSt3__112ctype_bynameIwE10do_tolowerEw
+T __ZNKSt3__112ctype_bynameIwE10do_toupperEPwPKw
+T __ZNKSt3__112ctype_bynameIwE10do_toupperEw
+T __ZNKSt3__112ctype_bynameIwE11do_scan_notEjPKwS3_
+T __ZNKSt3__112ctype_bynameIwE5do_isEPKwS3_Pj
+T __ZNKSt3__112ctype_bynameIwE5do_isEjw
+T __ZNKSt3__112ctype_bynameIwE8do_widenEPKcS3_Pw
+T __ZNKSt3__112ctype_bynameIwE8do_widenEc
+T __ZNKSt3__112ctype_bynameIwE9do_narrowEPKwS3_cPc
+T __ZNKSt3__112ctype_bynameIwE9do_narrowEwc
+T __ZNKSt3__112strstreambuf6pcountEv
+T __ZNKSt3__113random_device7entropyEv
+T __ZNKSt3__114__codecvt_utf8IDiE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__114__codecvt_utf8IDiE11do_encodingEv
+T __ZNKSt3__114__codecvt_utf8IDiE13do_max_lengthEv
+T __ZNKSt3__114__codecvt_utf8IDiE16do_always_noconvEv
+T __ZNKSt3__114__codecvt_utf8IDiE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDiE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDiE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__114__codecvt_utf8IDsE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__114__codecvt_utf8IDsE11do_encodingEv
+T __ZNKSt3__114__codecvt_utf8IDsE13do_max_lengthEv
+T __ZNKSt3__114__codecvt_utf8IDsE16do_always_noconvEv
+T __ZNKSt3__114__codecvt_utf8IDsE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDsE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDsE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__114__codecvt_utf8IwE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__114__codecvt_utf8IwE11do_encodingEv
+T __ZNKSt3__114__codecvt_utf8IwE13do_max_lengthEv
+T __ZNKSt3__114__codecvt_utf8IwE16do_always_noconvEv
+T __ZNKSt3__114__codecvt_utf8IwE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IwE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IwE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__114collate_bynameIcE10do_compareEPKcS3_S3_S3_
+T __ZNKSt3__114collate_bynameIcE12do_transformEPKcS3_
+T __ZNKSt3__114collate_bynameIwE10do_compareEPKwS3_S3_S3_
+T __ZNKSt3__114collate_bynameIwE12do_transformEPKwS3_
+T __ZNKSt3__114error_category10equivalentERKNS_10error_codeEi
+T __ZNKSt3__114error_category10equivalentEiRKNS_15error_conditionE
+T __ZNKSt3__114error_category23default_error_conditionEi
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IwLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IwLb0EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IwLb0EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IwLb0EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IwLb0EE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb0EE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IwLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IwLb1EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IwLb1EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IwLb1EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IwLb1EE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb1EE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115basic_streambufIcNS_11char_traitsIcEEE6getlocEv
+T __ZNKSt3__115basic_streambufIwNS_11char_traitsIwEEE6getlocEv
+T __ZNKSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strEv
+T __ZNKSt3__115error_condition7messageEv
+T __ZNKSt3__117bad_function_call4whatEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_thousands_sepEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_thousands_sepEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_thousands_sepEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_thousands_sepEv
+T __ZNKSt3__118__time_get_storageIcE15__do_date_orderEv
+T __ZNKSt3__118__time_get_storageIwE15__do_date_orderEv
+T __ZNKSt3__119__shared_weak_count13__get_deleterERKSt9type_info
+T __ZNKSt3__119bad_expected_accessIvE4whatEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE11do_encodingEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE13do_max_lengthEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE16do_always_noconvEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE11do_encodingEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE13do_max_lengthEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE16do_always_noconvEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__120__codecvt_utf8_utf16IwE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__120__codecvt_utf8_utf16IwE11do_encodingEv
+T __ZNKSt3__120__codecvt_utf8_utf16IwE13do_max_lengthEv
+T __ZNKSt3__120__codecvt_utf8_utf16IwE16do_always_noconvEv
+T __ZNKSt3__120__codecvt_utf8_utf16IwE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IwE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IwE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__120__time_get_c_storageIcE3__XEv
+T __ZNKSt3__120__time_get_c_storageIcE3__cEv
+T __ZNKSt3__120__time_get_c_storageIcE3__rEv
+T __ZNKSt3__120__time_get_c_storageIcE3__xEv
+T __ZNKSt3__120__time_get_c_storageIcE7__am_pmEv
+T __ZNKSt3__120__time_get_c_storageIcE7__weeksEv
+T __ZNKSt3__120__time_get_c_storageIcE8__monthsEv
+T __ZNKSt3__120__time_get_c_storageIwE3__XEv
+T __ZNKSt3__120__time_get_c_storageIwE3__cEv
+T __ZNKSt3__120__time_get_c_storageIwE3__rEv
+T __ZNKSt3__120__time_get_c_storageIwE3__xEv
+T __ZNKSt3__120__time_get_c_storageIwE7__am_pmEv
+T __ZNKSt3__120__time_get_c_storageIwE7__weeksEv
+T __ZNKSt3__120__time_get_c_storageIwE8__monthsEv
+T __ZNKSt3__120__vector_base_commonILb1EE20__throw_length_errorEv
+T __ZNKSt3__120__vector_base_commonILb1EE20__throw_out_of_rangeEv
+T __ZNKSt3__121__basic_string_commonILb1EE20__throw_length_errorEv
+T __ZNKSt3__121__basic_string_commonILb1EE20__throw_out_of_rangeEv
+T __ZNKSt3__123__match_any_but_newlineIcE6__execERNS_7__stateIcEE
+T __ZNKSt3__123__match_any_but_newlineIwE6__execERNS_7__stateIwEE
+T __ZNKSt3__13pmr26synchronized_pool_resource11do_is_equalERKNS0_15memory_resourceE
+T __ZNKSt3__13pmr28unsynchronized_pool_resource12__pool_indexEmm
+T __ZNKSt3__13pmr28unsynchronized_pool_resource17__pool_block_sizeEi
+T __ZNKSt3__13pmr28unsynchronized_pool_resource22__log2_pool_block_sizeEi
+T __ZNKSt3__13pmr28unsynchronized_pool_resource7optionsEv
+T __ZNKSt3__14__fs10filesystem18directory_iterator13__dereferenceEv
+T __ZNKSt3__14__fs10filesystem28recursive_directory_iterator13__dereferenceEv
+T __ZNKSt3__14__fs10filesystem28recursive_directory_iterator5depthEv
+T __ZNKSt3__14__fs10filesystem28recursive_directory_iterator7optionsEv
+T __ZNKSt3__14__fs10filesystem4path10__filenameEv
+T __ZNKSt3__14__fs10filesystem4path11__extensionEv
+T __ZNKSt3__14__fs10filesystem4path11__root_nameEv
+T __ZNKSt3__14__fs10filesystem4path13__parent_pathEv
+T __ZNKSt3__14__fs10filesystem4path15__relative_pathEv
+T __ZNKSt3__14__fs10filesystem4path15__root_path_rawEv
+T __ZNKSt3__14__fs10filesystem4path16__root_directoryEv
+T __ZNKSt3__14__fs10filesystem4path16lexically_normalEv
+T __ZNKSt3__14__fs10filesystem4path18lexically_relativeERKS2_
+T __ZNKSt3__14__fs10filesystem4path3endEv
+T __ZNKSt3__14__fs10filesystem4path5beginEv
+T __ZNKSt3__14__fs10filesystem4path6__stemEv
+T __ZNKSt3__14__fs10filesystem4path9__compareENS_17basic_string_viewIcNS_11char_traitsIcEEEE
+T __ZNKSt3__15ctypeIcE10do_tolowerEPcPKc
+T __ZNKSt3__15ctypeIcE10do_tolowerEc
+T __ZNKSt3__15ctypeIcE10do_toupperEPcPKc
+T __ZNKSt3__15ctypeIcE10do_toupperEc
+T __ZNKSt3__15ctypeIcE8do_widenEPKcS3_Pc
+T __ZNKSt3__15ctypeIcE8do_widenEc
+T __ZNKSt3__15ctypeIcE9do_narrowEPKcS3_cPc
+T __ZNKSt3__15ctypeIcE9do_narrowEcc
+T __ZNKSt3__15ctypeIwE10do_scan_isEjPKwS3_
+T __ZNKSt3__15ctypeIwE10do_tolowerEPwPKw
+T __ZNKSt3__15ctypeIwE10do_tolowerEw
+T __ZNKSt3__15ctypeIwE10do_toupperEPwPKw
+T __ZNKSt3__15ctypeIwE10do_toupperEw
+T __ZNKSt3__15ctypeIwE11do_scan_notEjPKwS3_
+T __ZNKSt3__15ctypeIwE5do_isEPKwS3_Pj
+T __ZNKSt3__15ctypeIwE5do_isEjw
+T __ZNKSt3__15ctypeIwE8do_widenEPKcS3_Pw
+T __ZNKSt3__15ctypeIwE8do_widenEc
+T __ZNKSt3__15ctypeIwE9do_narrowEPKwS3_cPc
+T __ZNKSt3__15ctypeIwE9do_narrowEwc
+T __ZNKSt3__16locale4nameEv
+T __ZNKSt3__16locale9has_facetERNS0_2idE
+T __ZNKSt3__16locale9use_facetERNS0_2idE
+T __ZNKSt3__16localeeqERKS0_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE10do_unshiftERS1_PDuS4_RS4_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE5do_inERS1_PKDuS5_RS5_PDiS7_RS7_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE6do_outERS1_PKDiS5_RS5_PDuS7_RS7_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE9do_lengthERS1_PKDuS5_m
+T __ZNKSt3__17codecvtIDic11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIDic11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDic11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDic11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDic11__mbstate_tE5do_inERS1_PKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__17codecvtIDic11__mbstate_tE6do_outERS1_PKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIDic11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE10do_unshiftERS1_PDuS4_RS4_
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE5do_inERS1_PKDuS5_RS5_PDsS7_RS7_
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE6do_outERS1_PKDsS5_RS5_PDuS7_RS7_
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE9do_lengthERS1_PKDuS5_m
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE5do_inERS1_PKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE6do_outERS1_PKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17codecvtIcc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIcc11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIcc11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIcc11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIcc11__mbstate_tE5do_inERS1_PKcS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIcc11__mbstate_tE6do_outERS1_PKcS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIcc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17codecvtIwc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIwc11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIwc11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIwc11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIwc11__mbstate_tE5do_inERS1_PKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__17codecvtIwc11__mbstate_tE6do_outERS1_PKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIwc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17collateIcE10do_compareEPKcS3_S3_S3_
+T __ZNKSt3__17collateIcE12do_transformEPKcS3_
+T __ZNKSt3__17collateIcE7do_hashEPKcS3_
+T __ZNKSt3__17collateIwE10do_compareEPKwS3_S3_S3_
+T __ZNKSt3__17collateIwE12do_transformEPKwS3_
+T __ZNKSt3__17collateIwE7do_hashEPKwS3_
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRPv
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRb
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRd
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRe
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRf
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRl
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRm
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRt
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRx
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRy
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjS8_
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRPv
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRb
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRd
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRe
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRf
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRl
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRm
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRt
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRx
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRy
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjS8_
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcPKv
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcb
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcd
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEce
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcl
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcm
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcx
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcy
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPKv
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwb
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwd
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwe
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwl
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwm
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwx
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwy
+T __ZNKSt3__18ios_base6getlocEv
+T __ZNKSt3__18messagesIcE6do_getEliiRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNKSt3__18messagesIcE7do_openERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_6localeE
+T __ZNKSt3__18messagesIcE8do_closeEl
+T __ZNKSt3__18messagesIwE6do_getEliiRKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEE
+T __ZNKSt3__18messagesIwE7do_openERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_6localeE
+T __ZNKSt3__18messagesIwE8do_closeEl
+T __ZNKSt3__18numpunctIcE11do_groupingEv
+T __ZNKSt3__18numpunctIcE11do_truenameEv
+T __ZNKSt3__18numpunctIcE12do_falsenameEv
+T __ZNKSt3__18numpunctIcE16do_decimal_pointEv
+T __ZNKSt3__18numpunctIcE16do_thousands_sepEv
+T __ZNKSt3__18numpunctIwE11do_groupingEv
+T __ZNKSt3__18numpunctIwE11do_truenameEv
+T __ZNKSt3__18numpunctIwE12do_falsenameEv
+T __ZNKSt3__18numpunctIwE16do_decimal_pointEv
+T __ZNKSt3__18numpunctIwE16do_thousands_sepEv
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE10__get_hourERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE10__get_yearERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_am_pmERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_monthERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_year4ERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_dateES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_timeES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_yearES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE12__get_minuteERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE12__get_secondERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_12_hourERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_percentERS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_weekdayERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13do_date_orderEv
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE14do_get_weekdayES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE15__get_monthnameERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE16do_get_monthnameES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE17__get_weekdaynameERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE17__get_white_spaceERS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE18__get_day_year_numERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE3getES4_S4_RNS_8ios_baseERjP2tmPKcSC_
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjP2tmcc
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE9__get_dayERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE10__get_hourERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE10__get_yearERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_am_pmERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_monthERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_year4ERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_dateES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_timeES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_yearES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE12__get_minuteERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE12__get_secondERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_12_hourERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_percentERS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_weekdayERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13do_date_orderEv
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE14do_get_weekdayES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE15__get_monthnameERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE16do_get_monthnameES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE17__get_weekdaynameERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE17__get_white_spaceERS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE18__get_day_year_numERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE3getES4_S4_RNS_8ios_baseERjP2tmPKwSC_
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjP2tmcc
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE9__get_dayERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE3putES4_RNS_8ios_baseEcPK2tmPKcSC_
+T __ZNKSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcPK2tmcc
+T __ZNKSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE3putES4_RNS_8ios_baseEwPK2tmPKwSC_
+T __ZNKSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPK2tmcc
+T __ZNKSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_bRNS_8ios_baseERjRNS_12basic_stringIcS3_NS_9allocatorIcEEEE
+T __ZNKSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_bRNS_8ios_baseERjRe
+T __ZNKSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_bRNS_8ios_baseERjRNS_12basic_stringIwS3_NS_9allocatorIwEEEE
+T __ZNKSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_bRNS_8ios_baseERjRe
+T __ZNKSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_bRNS_8ios_baseEcRKNS_12basic_stringIcS3_NS_9allocatorIcEEEE
+T __ZNKSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_bRNS_8ios_baseEce
+T __ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwRKNS_12basic_stringIwS3_NS_9allocatorIwEEEE
+T __ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwe
+I __ZNKSt8bad_cast4whatEv (indirect for __ZNKSt8bad_cast4whatEv)
+I __ZNKSt9bad_alloc4whatEv (indirect for __ZNKSt9bad_alloc4whatEv)
+I __ZNKSt9exception4whatEv (indirect for __ZNKSt9exception4whatEv)
+I __ZNSt10bad_typeidC1Ev (indirect for __ZNSt10bad_typeidC1Ev)
+I __ZNSt10bad_typeidC2Ev (indirect for __ZNSt10bad_typeidC2Ev)
+I __ZNSt10bad_typeidD0Ev (indirect for __ZNSt10bad_typeidD0Ev)
+I __ZNSt10bad_typeidD1Ev (indirect for __ZNSt10bad_typeidD1Ev)
+I __ZNSt10bad_typeidD2Ev (indirect for __ZNSt10bad_typeidD2Ev)
+T __ZNSt11logic_errorC1EPKc
+T __ZNSt11logic_errorC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt11logic_errorC1ERKS_
+T __ZNSt11logic_errorC2EPKc
+T __ZNSt11logic_errorC2ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt11logic_errorC2ERKS_
+I __ZNSt11logic_errorD0Ev (indirect for __ZNSt11logic_errorD0Ev)
+I __ZNSt11logic_errorD1Ev (indirect for __ZNSt11logic_errorD1Ev)
+I __ZNSt11logic_errorD2Ev (indirect for __ZNSt11logic_errorD2Ev)
+T __ZNSt11logic_erroraSERKS_
+I __ZNSt11range_errorD0Ev (indirect for __ZNSt11range_errorD0Ev)
+I __ZNSt11range_errorD1Ev (indirect for __ZNSt11range_errorD1Ev)
+I __ZNSt11range_errorD2Ev (indirect for __ZNSt11range_errorD2Ev)
+I __ZNSt12domain_errorD0Ev (indirect for __ZNSt12domain_errorD0Ev)
+I __ZNSt12domain_errorD1Ev (indirect for __ZNSt12domain_errorD1Ev)
+I __ZNSt12domain_errorD2Ev (indirect for __ZNSt12domain_errorD2Ev)
+T __ZNSt12experimental19bad_optional_accessD0Ev
+T __ZNSt12experimental19bad_optional_accessD1Ev
+T __ZNSt12experimental19bad_optional_accessD2Ev
+I __ZNSt12length_errorD0Ev (indirect for __ZNSt12length_errorD0Ev)
+I __ZNSt12length_errorD1Ev (indirect for __ZNSt12length_errorD1Ev)
+I __ZNSt12length_errorD2Ev (indirect for __ZNSt12length_errorD2Ev)
+I __ZNSt12out_of_rangeD0Ev (indirect for __ZNSt12out_of_rangeD0Ev)
+I __ZNSt12out_of_rangeD1Ev (indirect for __ZNSt12out_of_rangeD1Ev)
+I __ZNSt12out_of_rangeD2Ev (indirect for __ZNSt12out_of_rangeD2Ev)
+I __ZNSt13bad_exceptionD0Ev (indirect for __ZNSt13bad_exceptionD0Ev)
+I __ZNSt13bad_exceptionD1Ev (indirect for __ZNSt13bad_exceptionD1Ev)
+I __ZNSt13bad_exceptionD2Ev (indirect for __ZNSt13bad_exceptionD2Ev)
+T __ZNSt13exception_ptr31__from_native_exception_pointerEPv
+T __ZNSt13exception_ptrC1ERKS_
+T __ZNSt13exception_ptrC2ERKS_
+T __ZNSt13exception_ptrD1Ev
+T __ZNSt13exception_ptrD2Ev
+T __ZNSt13exception_ptraSERKS_
+T __ZNSt13runtime_errorC1EPKc
+T __ZNSt13runtime_errorC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt13runtime_errorC1ERKS_
+T __ZNSt13runtime_errorC2EPKc
+T __ZNSt13runtime_errorC2ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt13runtime_errorC2ERKS_
+I __ZNSt13runtime_errorD0Ev (indirect for __ZNSt13runtime_errorD0Ev)
+I __ZNSt13runtime_errorD1Ev (indirect for __ZNSt13runtime_errorD1Ev)
+I __ZNSt13runtime_errorD2Ev (indirect for __ZNSt13runtime_errorD2Ev)
+T __ZNSt13runtime_erroraSERKS_
+I __ZNSt14overflow_errorD0Ev (indirect for __ZNSt14overflow_errorD0Ev)
+I __ZNSt14overflow_errorD1Ev (indirect for __ZNSt14overflow_errorD1Ev)
+I __ZNSt14overflow_errorD2Ev (indirect for __ZNSt14overflow_errorD2Ev)
+I __ZNSt15underflow_errorD0Ev (indirect for __ZNSt15underflow_errorD0Ev)
+I __ZNSt15underflow_errorD1Ev (indirect for __ZNSt15underflow_errorD1Ev)
+I __ZNSt15underflow_errorD2Ev (indirect for __ZNSt15underflow_errorD2Ev)
+I __ZNSt16invalid_argumentD0Ev (indirect for __ZNSt16invalid_argumentD0Ev)
+I __ZNSt16invalid_argumentD1Ev (indirect for __ZNSt16invalid_argumentD1Ev)
+I __ZNSt16invalid_argumentD2Ev (indirect for __ZNSt16invalid_argumentD2Ev)
+T __ZNSt16nested_exceptionC1Ev
+T __ZNSt16nested_exceptionC2Ev
+T __ZNSt16nested_exceptionD0Ev
+T __ZNSt16nested_exceptionD1Ev
+T __ZNSt16nested_exceptionD2Ev
+T __ZNSt19bad_optional_accessD0Ev
+T __ZNSt19bad_optional_accessD1Ev
+T __ZNSt19bad_optional_accessD2Ev
+I __ZNSt20bad_array_new_lengthC1Ev (indirect for __ZNSt20bad_array_new_lengthC1Ev)
+I __ZNSt20bad_array_new_lengthC2Ev (indirect for __ZNSt20bad_array_new_lengthC2Ev)
+I __ZNSt20bad_array_new_lengthD0Ev (indirect for __ZNSt20bad_array_new_lengthD0Ev)
+I __ZNSt20bad_array_new_lengthD1Ev (indirect for __ZNSt20bad_array_new_lengthD1Ev)
+I __ZNSt20bad_array_new_lengthD2Ev (indirect for __ZNSt20bad_array_new_lengthD2Ev)
+T __ZNSt3__110__time_getC1EPKc
+T __ZNSt3__110__time_getC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_getC2EPKc
+T __ZNSt3__110__time_getC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_getD1Ev
+T __ZNSt3__110__time_getD2Ev
+T __ZNSt3__110__time_putC1EPKc
+T __ZNSt3__110__time_putC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_putC2EPKc
+T __ZNSt3__110__time_putC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_putD1Ev
+T __ZNSt3__110__time_putD2Ev
+S __ZNSt3__110ctype_base5alnumE
+S __ZNSt3__110ctype_base5alphaE
+S __ZNSt3__110ctype_base5blankE
+S __ZNSt3__110ctype_base5cntrlE
+S __ZNSt3__110ctype_base5digitE
+S __ZNSt3__110ctype_base5graphE
+S __ZNSt3__110ctype_base5lowerE
+S __ZNSt3__110ctype_base5printE
+S __ZNSt3__110ctype_base5punctE
+S __ZNSt3__110ctype_base5spaceE
+S __ZNSt3__110ctype_base5upperE
+S __ZNSt3__110ctype_base6xdigitE
+T __ZNSt3__110istrstreamD0Ev
+T __ZNSt3__110istrstreamD1Ev
+T __ZNSt3__110istrstreamD2Ev
+D __ZNSt3__110moneypunctIcLb0EE2idE
+S __ZNSt3__110moneypunctIcLb0EE4intlE
+D __ZNSt3__110moneypunctIcLb1EE2idE
+S __ZNSt3__110moneypunctIcLb1EE4intlE
+D __ZNSt3__110moneypunctIwLb0EE2idE
+S __ZNSt3__110moneypunctIwLb0EE4intlE
+D __ZNSt3__110moneypunctIwLb1EE2idE
+S __ZNSt3__110moneypunctIwLb1EE4intlE
+T __ZNSt3__110ostrstreamD0Ev
+T __ZNSt3__110ostrstreamD1Ev
+T __ZNSt3__110ostrstreamD2Ev
+T __ZNSt3__110to_wstringEd
+T __ZNSt3__110to_wstringEe
+T __ZNSt3__110to_wstringEf
+T __ZNSt3__110to_wstringEi
+T __ZNSt3__110to_wstringEj
+T __ZNSt3__110to_wstringEl
+T __ZNSt3__110to_wstringEm
+T __ZNSt3__110to_wstringEx
+T __ZNSt3__110to_wstringEy
+T __ZNSt3__111__call_onceERVmPvPFvS2_E
+T __ZNSt3__111__money_getIcE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_SF_Ri
+T __ZNSt3__111__money_getIwE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_SJ_Ri
+T __ZNSt3__111__money_putIcE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_Ri
+T __ZNSt3__111__money_putIcE8__formatEPcRS2_S3_jPKcS5_RKNS_5ctypeIcEEbRKNS_10money_base7patternEccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESL_SL_i
+T __ZNSt3__111__money_putIwE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_Ri
+T __ZNSt3__111__money_putIwE8__formatEPwRS2_S3_jPKwS5_RKNS_5ctypeIwEEbRKNS_10money_base7patternEwwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNSE_IwNSF_IwEENSH_IwEEEESQ_i
+T __ZNSt3__111regex_errorC1ENS_15regex_constants10error_typeE
+T __ZNSt3__111regex_errorC2ENS_15regex_constants10error_typeE
+T __ZNSt3__111regex_errorD0Ev
+T __ZNSt3__111regex_errorD1Ev
+T __ZNSt3__111regex_errorD2Ev
+T __ZNSt3__111this_thread9sleep_forERKNS_6chrono8durationIxNS_5ratioILl1ELl1000000000EEEEE
+T __ZNSt3__111timed_mutex4lockEv
+T __ZNSt3__111timed_mutex6unlockEv
+T __ZNSt3__111timed_mutex8try_lockEv
+T __ZNSt3__111timed_mutexC1Ev
+T __ZNSt3__111timed_mutexC2Ev
+T __ZNSt3__111timed_mutexD1Ev
+T __ZNSt3__111timed_mutexD2Ev
+T __ZNSt3__112__do_nothingEPv
+T __ZNSt3__112__get_sp_mutEPKv
+T __ZNSt3__112__next_primeEm
+S __ZNSt3__112__rs_default4__c_E
+T __ZNSt3__112__rs_defaultC1ERKS0_
+T __ZNSt3__112__rs_defaultC1Ev
+T __ZNSt3__112__rs_defaultC2ERKS0_
+T __ZNSt3__112__rs_defaultC2Ev
+T __ZNSt3__112__rs_defaultD1Ev
+T __ZNSt3__112__rs_defaultD2Ev
+T __ZNSt3__112__rs_defaultclEv
+T __ZNSt3__112bad_weak_ptrD0Ev
+T __ZNSt3__112bad_weak_ptrD1Ev
+T __ZNSt3__112bad_weak_ptrD2Ev
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE21__grow_by_and_replaceEmmmmmmPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm
+S __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4nposE
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5eraseEmm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcmm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendERKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignERKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertENS_11__wrap_iterIPKcEEc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmRKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6resizeEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmRKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEmmmmmm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_RKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_RKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSERKS5_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEc
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE21__grow_by_and_replaceEmmmmmmPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE2atEm
+S __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4nposE
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5eraseEmm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEPKwmm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendERKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignERKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertENS_11__wrap_iterIPKwEEw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmRKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6resizeEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmRKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7reserveEm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9__grow_byEmmmmmm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9push_backEw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_RKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_RKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED1Ev
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED2Ev
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEaSERKS5_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEaSEw
+T __ZNSt3__112ctype_bynameIcEC1EPKcm
+T __ZNSt3__112ctype_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIcEC2EPKcm
+T __ZNSt3__112ctype_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIcED0Ev
+T __ZNSt3__112ctype_bynameIcED1Ev
+T __ZNSt3__112ctype_bynameIcED2Ev
+T __ZNSt3__112ctype_bynameIwEC1EPKcm
+T __ZNSt3__112ctype_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIwEC2EPKcm
+T __ZNSt3__112ctype_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIwED0Ev
+T __ZNSt3__112ctype_bynameIwED1Ev
+T __ZNSt3__112ctype_bynameIwED2Ev
+T __ZNSt3__112future_errorC1ENS_10error_codeE
+T __ZNSt3__112future_errorC2ENS_10error_codeE
+T __ZNSt3__112future_errorD0Ev
+T __ZNSt3__112future_errorD1Ev
+T __ZNSt3__112future_errorD2Ev
+S __ZNSt3__112placeholders2_1E
+S __ZNSt3__112placeholders2_2E
+S __ZNSt3__112placeholders2_3E
+S __ZNSt3__112placeholders2_4E
+S __ZNSt3__112placeholders2_5E
+S __ZNSt3__112placeholders2_6E
+S __ZNSt3__112placeholders2_7E
+S __ZNSt3__112placeholders2_8E
+S __ZNSt3__112placeholders2_9E
+S __ZNSt3__112placeholders3_10E
+T __ZNSt3__112strstreambuf3strEv
+T __ZNSt3__112strstreambuf4swapERS0_
+T __ZNSt3__112strstreambuf6__initEPclS1_
+T __ZNSt3__112strstreambuf6freezeEb
+T __ZNSt3__112strstreambuf7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__112strstreambuf7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__112strstreambuf8overflowEi
+T __ZNSt3__112strstreambuf9pbackfailEi
+T __ZNSt3__112strstreambuf9underflowEv
+T __ZNSt3__112strstreambufC1EPFPvmEPFvS1_E
+T __ZNSt3__112strstreambufC1EPKal
+T __ZNSt3__112strstreambufC1EPKcl
+T __ZNSt3__112strstreambufC1EPKhl
+T __ZNSt3__112strstreambufC1EPalS1_
+T __ZNSt3__112strstreambufC1EPclS1_
+T __ZNSt3__112strstreambufC1EPhlS1_
+T __ZNSt3__112strstreambufC1El
+T __ZNSt3__112strstreambufC2EPFPvmEPFvS1_E
+T __ZNSt3__112strstreambufC2EPKal
+T __ZNSt3__112strstreambufC2EPKcl
+T __ZNSt3__112strstreambufC2EPKhl
+T __ZNSt3__112strstreambufC2EPalS1_
+T __ZNSt3__112strstreambufC2EPclS1_
+T __ZNSt3__112strstreambufC2EPhlS1_
+T __ZNSt3__112strstreambufC2El
+T __ZNSt3__112strstreambufD0Ev
+T __ZNSt3__112strstreambufD1Ev
+T __ZNSt3__112strstreambufD2Ev
+T __ZNSt3__112system_errorC1ENS_10error_codeE
+T __ZNSt3__112system_errorC1ENS_10error_codeEPKc
+T __ZNSt3__112system_errorC1ENS_10error_codeERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorC1EiRKNS_14error_categoryE
+T __ZNSt3__112system_errorC1EiRKNS_14error_categoryEPKc
+T __ZNSt3__112system_errorC1EiRKNS_14error_categoryERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorC2ENS_10error_codeE
+T __ZNSt3__112system_errorC2ENS_10error_codeEPKc
+T __ZNSt3__112system_errorC2ENS_10error_codeERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorC2EiRKNS_14error_categoryE
+T __ZNSt3__112system_errorC2EiRKNS_14error_categoryEPKc
+T __ZNSt3__112system_errorC2EiRKNS_14error_categoryERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorD0Ev
+T __ZNSt3__112system_errorD1Ev
+T __ZNSt3__112system_errorD2Ev
+T __ZNSt3__113__hash_memoryEPKvm
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE11__read_modeEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE12__write_modeEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4openEPKcj
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4syncEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5closeEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE6setbufEPcl
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE8overflowEi
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9pbackfailEi
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9underflowEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC1EOS3_
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC1Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2EOS3_
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEPclc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERNS_15basic_streambufIcS2_EEc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4peekEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4readEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4syncEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5seekgENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5seekgExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5tellgEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5ungetEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6ignoreEli
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6sentryC1ERS3_b
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6sentryC2ERS3_b
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7getlineEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7getlineEPclc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7putbackEc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE8readsomeEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRNS_9basic_iosIcS2_EES6_E
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRS3_S4_E
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERPv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERb
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERd
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERe
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERf
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERi
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERj
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERm
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERs
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERt
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERx
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERy
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEPwlw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERNS_15basic_streambufIwS2_EEw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4peekEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4readEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4swapERS3_
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4syncEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5seekgENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5seekgExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5tellgEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5ungetEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6ignoreEli
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6sentryC1ERS3_b
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6sentryC2ERS3_b
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7getlineEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7getlineEPwlw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7putbackEw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE8readsomeEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEEC1EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEEC2EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED2Ev
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRNS_9basic_iosIwS2_EES6_E
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRS3_S4_E
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERPv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERb
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERd
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERe
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERf
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERi
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERj
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERm
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERs
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERt
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERx
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERy
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE3putEc
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5seekpENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5seekpExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5tellpEv
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5writeEPKcl
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC2ERS3_
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD2Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRNS_9basic_iosIcS2_EES6_E
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRS3_S4_E
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPKv
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEb
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEd
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEe
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEf
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEi
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEj
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEl
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEm
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEs
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEt
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEx
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEy
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE3putEw
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE4swapERS3_
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5flushEv
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5seekpENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5seekpExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5tellpEv
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5writeEPKwl
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryC1ERS3_
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryC2ERS3_
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryD1Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryD2Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEEC1EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEEC2EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED2Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRNS_9basic_iosIwS2_EES6_E
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRS3_S4_E
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPKv
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEb
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEd
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEe
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEf
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEi
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEj
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEl
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEm
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEs
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEt
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEx
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEy
+T __ZNSt3__113random_deviceC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__113random_deviceC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__113random_deviceD1Ev
+T __ZNSt3__113random_deviceD2Ev
+T __ZNSt3__113random_deviceclEv
+T __ZNSt3__113shared_futureIvED1Ev
+T __ZNSt3__113shared_futureIvED2Ev
+T __ZNSt3__113shared_futureIvEaSERKS1_
+T __ZNSt3__114__num_get_base10__get_baseERNS_8ios_baseE
+S __ZNSt3__114__num_get_base5__srcE
+T __ZNSt3__114__num_put_base12__format_intEPcPKcbj
+T __ZNSt3__114__num_put_base14__format_floatEPcPKcj
+T __ZNSt3__114__num_put_base18__identify_paddingEPcS1_RKNS_8ios_baseE
+T __ZNSt3__114__shared_count12__add_sharedEv
+T __ZNSt3__114__shared_count16__release_sharedEv
+T __ZNSt3__114__shared_countD0Ev
+T __ZNSt3__114__shared_countD1Ev
+T __ZNSt3__114__shared_countD2Ev
+T __ZNSt3__114basic_ifstreamIcNS_11char_traitsIcEEE4openEPKcj
+T __ZNSt3__114basic_ifstreamIcNS_11char_traitsIcEEE4openERKNS_12basic_stringIcS2_NS_9allocatorIcEEEEj
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__114basic_ofstreamIcNS_11char_traitsIcEEE4openEPKcj
+T __ZNSt3__114basic_ofstreamIcNS_11char_traitsIcEEE4openERKNS_12basic_stringIcS2_NS_9allocatorIcEEEEj
+T __ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIDic11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDic11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDic11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIDsc11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDsc11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDsc11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIcc11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIcc11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIcc11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIwc11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIwc11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIwc11__mbstate_tED2Ev
+T __ZNSt3__114collate_bynameIcEC1EPKcm
+T __ZNSt3__114collate_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIcEC2EPKcm
+T __ZNSt3__114collate_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIcED0Ev
+T __ZNSt3__114collate_bynameIcED1Ev
+T __ZNSt3__114collate_bynameIcED2Ev
+T __ZNSt3__114collate_bynameIwEC1EPKcm
+T __ZNSt3__114collate_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIwEC2EPKcm
+T __ZNSt3__114collate_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIwED0Ev
+T __ZNSt3__114collate_bynameIwED1Ev
+T __ZNSt3__114collate_bynameIwED2Ev
+T __ZNSt3__114error_categoryC2Ev
+T __ZNSt3__114error_categoryD0Ev
+T __ZNSt3__114error_categoryD1Ev
+T __ZNSt3__114error_categoryD2Ev
+T __ZNSt3__115__get_classnameEPKcb
+T __ZNSt3__115__thread_struct25notify_all_at_thread_exitEPNS_18condition_variableEPNS_5mutexE
+T __ZNSt3__115__thread_struct27__make_ready_at_thread_exitEPNS_17__assoc_sub_stateE
+T __ZNSt3__115__thread_structC1Ev
+T __ZNSt3__115__thread_structC2Ev
+T __ZNSt3__115__thread_structD1Ev
+T __ZNSt3__115__thread_structD2Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE10pubseekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE10pubseekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4setgEPcS4_S4_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4setpEPcS4_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4syncEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5gbumpEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5pbumpEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sgetcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sgetnEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputcEc
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputnEPKcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5uflowEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6sbumpcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6setbufEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6snextcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsgetnEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsputnEPKcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7pubsyncEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7sungetcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8in_availEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8overflowEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8pubimbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9pbackfailEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9pubsetbufEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9showmanycEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9sputbackcEc
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9underflowEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC1ERKS3_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC1Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2ERKS3_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEaSERKS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE10pubseekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE10pubseekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4setgEPwS4_S4_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4setpEPwS4_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4swapERS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4syncEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5gbumpEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5imbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5pbumpEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sgetcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sgetnEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sputcEw
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sputnEPKwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5uflowEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6sbumpcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6setbufEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6snextcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6xsgetnEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6xsputnEPKwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7pubsyncEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7sungetcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8in_availEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8overflowEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8pubimbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9pbackfailEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9pubsetbufEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9showmanycEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9sputbackcEw
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9underflowEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC1ERKS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC1Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC2ERKS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC2Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED2Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEaSERKS3_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strERKNS_12basic_stringIcS2_S4_EE
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE4swapERS5_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE8overflowEi
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9pbackfailEi
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9underflowEv
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEC1EOS5_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEC2EOS5_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__115future_categoryEv
+T __ZNSt3__115numpunct_bynameIcEC1EPKcm
+T __ZNSt3__115numpunct_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIcEC2EPKcm
+T __ZNSt3__115numpunct_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIcED0Ev
+T __ZNSt3__115numpunct_bynameIcED1Ev
+T __ZNSt3__115numpunct_bynameIcED2Ev
+T __ZNSt3__115numpunct_bynameIwEC1EPKcm
+T __ZNSt3__115numpunct_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIwEC2EPKcm
+T __ZNSt3__115numpunct_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIwED0Ev
+T __ZNSt3__115numpunct_bynameIwED1Ev
+T __ZNSt3__115numpunct_bynameIwED2Ev
+T __ZNSt3__115recursive_mutex4lockEv
+T __ZNSt3__115recursive_mutex6unlockEv
+T __ZNSt3__115recursive_mutex8try_lockEv
+T __ZNSt3__115recursive_mutexC1Ev
+T __ZNSt3__115recursive_mutexC2Ev
+T __ZNSt3__115recursive_mutexD1Ev
+T __ZNSt3__115recursive_mutexD2Ev
+T __ZNSt3__115system_categoryEv
+T __ZNSt3__116__check_groupingERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjS8_Rj
+T __ZNSt3__116__narrow_to_utf8ILm16EED0Ev
+T __ZNSt3__116__narrow_to_utf8ILm16EED1Ev
+T __ZNSt3__116__narrow_to_utf8ILm16EED2Ev
+T __ZNSt3__116__narrow_to_utf8ILm32EED0Ev
+T __ZNSt3__116__narrow_to_utf8ILm32EED1Ev
+T __ZNSt3__116__narrow_to_utf8ILm32EED2Ev
+T __ZNSt3__116generic_categoryEv
+T __ZNSt3__117__assoc_sub_state10__sub_waitERNS_11unique_lockINS_5mutexEEE
+T __ZNSt3__117__assoc_sub_state12__make_readyEv
+T __ZNSt3__117__assoc_sub_state13set_exceptionESt13exception_ptr
+T __ZNSt3__117__assoc_sub_state16__on_zero_sharedEv
+T __ZNSt3__117__assoc_sub_state24set_value_at_thread_exitEv
+T __ZNSt3__117__assoc_sub_state28set_exception_at_thread_exitESt13exception_ptr
+T __ZNSt3__117__assoc_sub_state4copyEv
+T __ZNSt3__117__assoc_sub_state4waitEv
+T __ZNSt3__117__assoc_sub_state9__executeEv
+T __ZNSt3__117__assoc_sub_state9set_valueEv
+T __ZNSt3__117__widen_from_utf8ILm16EED0Ev
+T __ZNSt3__117__widen_from_utf8ILm16EED1Ev
+T __ZNSt3__117__widen_from_utf8ILm16EED2Ev
+T __ZNSt3__117__widen_from_utf8ILm32EED0Ev
+T __ZNSt3__117__widen_from_utf8ILm32EED1Ev
+T __ZNSt3__117__widen_from_utf8ILm32EED2Ev
+T __ZNSt3__117bad_function_callD0Ev
+T __ZNSt3__117bad_function_callD1Ev
+T __ZNSt3__117bad_function_callD2Ev
+T __ZNSt3__117iostream_categoryEv
+T __ZNSt3__117moneypunct_bynameIcLb0EE4initEPKc
+T __ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc
+T __ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc
+T __ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc
+T __ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE
+T __ZNSt3__118__time_get_storageIcEC1EPKc
+T __ZNSt3__118__time_get_storageIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118__time_get_storageIcEC2EPKc
+T __ZNSt3__118__time_get_storageIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118__time_get_storageIwEC1EPKc
+T __ZNSt3__118__time_get_storageIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118__time_get_storageIwEC2EPKc
+T __ZNSt3__118__time_get_storageIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__118condition_variable10notify_allEv
+T __ZNSt3__118condition_variable10notify_oneEv
+T __ZNSt3__118condition_variable15__do_timed_waitERNS_11unique_lockINS_5mutexEEENS_6chrono10time_pointINS5_12system_clockENS5_8durationIxNS_5ratioILl1ELl1000000000EEEEEEE
+T __ZNSt3__118condition_variable4waitERNS_11unique_lockINS_5mutexEEE
+T __ZNSt3__118condition_variableD1Ev
+T __ZNSt3__118condition_variableD2Ev
+T __ZNSt3__118shared_timed_mutex11lock_sharedEv
+T __ZNSt3__118shared_timed_mutex13unlock_sharedEv
+T __ZNSt3__118shared_timed_mutex15try_lock_sharedEv
+T __ZNSt3__118shared_timed_mutex4lockEv
+T __ZNSt3__118shared_timed_mutex6unlockEv
+T __ZNSt3__118shared_timed_mutex8try_lockEv
+T __ZNSt3__118shared_timed_mutexC1Ev
+T __ZNSt3__118shared_timed_mutexC2Ev
+T __ZNSt3__119__is_posix_terminalEP7__sFILE
+T __ZNSt3__119__shared_mutex_base11lock_sharedEv
+T __ZNSt3__119__shared_mutex_base13unlock_sharedEv
+T __ZNSt3__119__shared_mutex_base15try_lock_sharedEv
+T __ZNSt3__119__shared_mutex_base4lockEv
+T __ZNSt3__119__shared_mutex_base6unlockEv
+T __ZNSt3__119__shared_mutex_base8try_lockEv
+T __ZNSt3__119__shared_mutex_baseC1Ev
+T __ZNSt3__119__shared_mutex_baseC2Ev
+T __ZNSt3__119__shared_weak_count10__add_weakEv
+T __ZNSt3__119__shared_weak_count12__add_sharedEv
+T __ZNSt3__119__shared_weak_count14__release_weakEv
+T __ZNSt3__119__shared_weak_count16__release_sharedEv
+T __ZNSt3__119__shared_weak_count4lockEv
+T __ZNSt3__119__shared_weak_countD0Ev
+T __ZNSt3__119__shared_weak_countD1Ev
+T __ZNSt3__119__shared_weak_countD2Ev
+T __ZNSt3__119__thread_local_dataEv
+T __ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__120__atomic_wait_nativeILm8EEEvPKvS2_
+T __ZNSt3__120__get_collation_nameEPKc
+T __ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEEx
+T __ZNSt3__120__libcpp_atomic_waitEPVKvx
+T __ZNSt3__120__throw_system_errorEiPKc
+T __ZNSt3__121__get_locale_encodingEPKc
+T __ZNSt3__121__throw_runtime_errorEPKc
+T __ZNSt3__121recursive_timed_mutex4lockEv
+T __ZNSt3__121recursive_timed_mutex6unlockEv
+T __ZNSt3__121recursive_timed_mutex8try_lockEv
+T __ZNSt3__121recursive_timed_mutexC1Ev
+T __ZNSt3__121recursive_timed_mutexC2Ev
+T __ZNSt3__121recursive_timed_mutexD1Ev
+T __ZNSt3__121recursive_timed_mutexD2Ev
+T __ZNSt3__122__libcpp_verbose_abortEPKcz
+T __ZNSt3__123__atomic_monitor_globalEPKv
+T __ZNSt3__123__cxx_atomic_notify_allEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE
+T __ZNSt3__123__cxx_atomic_notify_allEPVKv
+T __ZNSt3__123__cxx_atomic_notify_oneEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE
+T __ZNSt3__123__cxx_atomic_notify_oneEPVKv
+T __ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE
+T __ZNSt3__123__libcpp_atomic_monitorEPVKv
+T __ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE
+T __ZNSt3__126__atomic_notify_all_nativeILm8EEEvPKv
+T __ZNSt3__126__atomic_notify_one_nativeILm8EEEvPKv
+T __ZNSt3__126__atomic_wait_global_tableEPKvx
+T __ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+T __ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+T __ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh
+T __ZNSt3__132__atomic_notify_all_global_tableEPKv
+T __ZNSt3__132__atomic_notify_one_global_tableEPKv
+T __ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE
+T __ZNSt3__133__atomic_wait_native_with_timeoutILm8EEEvPKvS2_y
+T __ZNSt3__134__construct_barrier_algorithm_baseERl
+T __ZNSt3__139__atomic_wait_global_table_with_timeoutEPKvxy
+S __ZNSt3__13cinE
+T __ZNSt3__13pmr15memory_resourceD0Ev
+T __ZNSt3__13pmr15memory_resourceD1Ev
+T __ZNSt3__13pmr15memory_resourceD2Ev
+T __ZNSt3__13pmr19new_delete_resourceEv
+T __ZNSt3__13pmr20get_default_resourceEv
+T __ZNSt3__13pmr20null_memory_resourceEv
+T __ZNSt3__13pmr20set_default_resourceEPNS0_15memory_resourceE
+T __ZNSt3__13pmr25monotonic_buffer_resource11do_allocateEmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource11do_allocateEmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__do_allocateEPNS0_15memory_resourceEmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__release_ptrEPNS0_15memory_resourceE
+T __ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool15__do_deallocateEPNS0_15memory_resourceEPvmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource13do_deallocateEPvmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource7releaseEv
+T __ZNSt3__13pmr28unsynchronized_pool_resourceC1ERKNS0_12pool_optionsEPNS0_15memory_resourceE
+T __ZNSt3__13pmr28unsynchronized_pool_resourceC2ERKNS0_12pool_optionsEPNS0_15memory_resourceE
+T __ZNSt3__14__fs10filesystem10__absoluteERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem10hash_valueERKNS1_4pathE
+T __ZNSt3__14__fs10filesystem11__canonicalERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem11__copy_fileERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem11__file_sizeERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem12__equivalentERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem12__remove_allERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem13__fs_is_emptyERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem13__permissionsERKNS1_4pathENS1_5permsENS1_12perm_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem13__resize_fileERKNS1_4pathEmPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__copy_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__current_pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__current_pathERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__read_symlinkERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem15directory_entry12__do_refreshEv
+T __ZNSt3__14__fs10filesystem16_FilesystemClock3nowEv
+S __ZNSt3__14__fs10filesystem16_FilesystemClock9is_steadyE
+T __ZNSt3__14__fs10filesystem16__create_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem16__symlink_statusERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem16filesystem_error13__create_whatEi
+T __ZNSt3__14__fs10filesystem16filesystem_errorD0Ev
+T __ZNSt3__14__fs10filesystem16filesystem_errorD1Ev
+T __ZNSt3__14__fs10filesystem16filesystem_errorD2Ev
+T __ZNSt3__14__fs10filesystem17__hard_link_countERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem17__last_write_timeERKNS1_4pathENS_6chrono10time_pointINS1_16_FilesystemClockENS5_8durationInNS_5ratioILl1ELl1000000000EEEEEEEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem17__last_write_timeERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__create_directoryERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__create_directoryERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__create_hard_linkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__weakly_canonicalERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18directory_iterator11__incrementEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18directory_iteratorC1ERKNS1_4pathEPNS_10error_codeENS1_17directory_optionsE
+T __ZNSt3__14__fs10filesystem18directory_iteratorC2ERKNS1_4pathEPNS_10error_codeENS1_17directory_optionsE
+T __ZNSt3__14__fs10filesystem20__create_directoriesERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem21__temp_directory_pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem26__create_directory_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator11__incrementEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator15__try_recursionEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator5__popEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator9__advanceEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iteratorC1ERKNS1_4pathENS1_17directory_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iteratorC2ERKNS1_4pathENS1_17directory_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem4path17replace_extensionERKS2_
+S __ZNSt3__14__fs10filesystem4path19preferred_separatorE
+T __ZNSt3__14__fs10filesystem4path8iterator11__decrementEv
+T __ZNSt3__14__fs10filesystem4path8iterator11__incrementEv
+T __ZNSt3__14__fs10filesystem6__copyERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem7__spaceERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem8__statusERKNS1_4pathEPNS_10error_codeE
+S __ZNSt3__14cerrE
+S __ZNSt3__14clogE
+S __ZNSt3__14coutE
+T __ZNSt3__14stodERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T __ZNSt3__14stodERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T __ZNSt3__14stofERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T __ZNSt3__14stofERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T __ZNSt3__14stoiERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__14stoiERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T __ZNSt3__14stolERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__14stolERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+S __ZNSt3__14wcinE
+T __ZNSt3__15alignEmmRPvRm
+S __ZNSt3__15ctypeIcE10table_sizeE
+T __ZNSt3__15ctypeIcE13classic_tableEv
+S __ZNSt3__15ctypeIcE2idE
+T __ZNSt3__15ctypeIcEC1EPKjbm
+T __ZNSt3__15ctypeIcEC2EPKjbm
+T __ZNSt3__15ctypeIcED0Ev
+T __ZNSt3__15ctypeIcED1Ev
+T __ZNSt3__15ctypeIcED2Ev
+S __ZNSt3__15ctypeIwE2idE
+T __ZNSt3__15ctypeIwED0Ev
+T __ZNSt3__15ctypeIwED1Ev
+T __ZNSt3__15ctypeIwED2Ev
+T __ZNSt3__15mutex4lockEv
+T __ZNSt3__15mutex6unlockEv
+T __ZNSt3__15mutex8try_lockEv
+T __ZNSt3__15mutexD1Ev
+T __ZNSt3__15mutexD2Ev
+T __ZNSt3__15stoldERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T __ZNSt3__15stoldERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T __ZNSt3__15stollERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__15stollERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T __ZNSt3__15stoulERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__15stoulERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+S __ZNSt3__15wcerrE
+S __ZNSt3__15wclogE
+S __ZNSt3__15wcoutE
+T __ZNSt3__16__itoa8__u32toaEjPc
+T __ZNSt3__16__itoa8__u64toaEyPc
+T __ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIeeEEPeEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIffEEPfEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIhhEEPhEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIiiEEPiEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIjjEEPjEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIllEEPlEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessImmEEPmEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIssEEPsEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIttEEPtEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIwwEEPwEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIxxEEPxEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIyyEEPyEEvT0_S5_T_
+T __ZNSt3__16chrono12steady_clock3nowEv
+S __ZNSt3__16chrono12steady_clock9is_steadyE
+T __ZNSt3__16chrono12system_clock11from_time_tEl
+T __ZNSt3__16chrono12system_clock3nowEv
+S __ZNSt3__16chrono12system_clock9is_steadyE
+T __ZNSt3__16chrono12system_clock9to_time_tERKNS0_10time_pointIS1_NS0_8durationIxNS_5ratioILl1ELl1000000EEEEEEE
+T __ZNSt3__16futureIvE3getEv
+T __ZNSt3__16futureIvEC1EPNS_17__assoc_sub_stateE
+T __ZNSt3__16futureIvEC2EPNS_17__assoc_sub_stateE
+T __ZNSt3__16futureIvED1Ev
+T __ZNSt3__16futureIvED2Ev
+T __ZNSt3__16gslice6__initEm
+T __ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl
+T __ZNSt3__16locale2id5__getEv
+S __ZNSt3__16locale2id9__next_idE
+S __ZNSt3__16locale3allE
+S __ZNSt3__16locale4noneE
+S __ZNSt3__16locale4timeE
+S __ZNSt3__16locale5ctypeE
+T __ZNSt3__16locale5facet16__on_zero_sharedEv
+T __ZNSt3__16locale5facetD0Ev
+T __ZNSt3__16locale5facetD1Ev
+T __ZNSt3__16locale5facetD2Ev
+T __ZNSt3__16locale6globalERKS0_
+T __ZNSt3__16locale7classicEv
+S __ZNSt3__16locale7collateE
+S __ZNSt3__16locale7numericE
+T __ZNSt3__16locale8__globalEv
+S __ZNSt3__16locale8messagesE
+S __ZNSt3__16locale8monetaryE
+T __ZNSt3__16localeC1EPKc
+T __ZNSt3__16localeC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__16localeC1ERKS0_
+T __ZNSt3__16localeC1ERKS0_PKci
+T __ZNSt3__16localeC1ERKS0_RKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEi
+T __ZNSt3__16localeC1ERKS0_S2_i
+T __ZNSt3__16localeC1Ev
+T __ZNSt3__16localeC2EPKc
+T __ZNSt3__16localeC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__16localeC2ERKS0_
+T __ZNSt3__16localeC2ERKS0_PKci
+T __ZNSt3__16localeC2ERKS0_RKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEi
+T __ZNSt3__16localeC2ERKS0_S2_i
+T __ZNSt3__16localeC2Ev
+T __ZNSt3__16localeD1Ev
+T __ZNSt3__16localeD2Ev
+T __ZNSt3__16localeaSERKS0_
+T __ZNSt3__16stoullERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__16stoullERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T __ZNSt3__16thread20hardware_concurrencyEv
+T __ZNSt3__16thread4joinEv
+T __ZNSt3__16thread6detachEv
+T __ZNSt3__16threadD1Ev
+T __ZNSt3__16threadD2Ev
+S __ZNSt3__17codecvtIDiDu11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDiDu11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDiDu11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDiDu11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIDic11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDic11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDic11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDic11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIDsDu11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDsDu11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDsDu11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDsDu11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIDsc11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDsc11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDsc11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDsc11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIcc11__mbstate_tE2idE
+T __ZNSt3__17codecvtIcc11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIcc11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIcc11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIwc11__mbstate_tE2idE
+T __ZNSt3__17codecvtIwc11__mbstate_tEC1EPKcm
+T __ZNSt3__17codecvtIwc11__mbstate_tEC1Em
+T __ZNSt3__17codecvtIwc11__mbstate_tEC2EPKcm
+T __ZNSt3__17codecvtIwc11__mbstate_tEC2Em
+T __ZNSt3__17codecvtIwc11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIwc11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIwc11__mbstate_tED2Ev
+D __ZNSt3__17collateIcE2idE
+T __ZNSt3__17collateIcED0Ev
+T __ZNSt3__17collateIcED1Ev
+T __ZNSt3__17collateIcED2Ev
+D __ZNSt3__17collateIwE2idE
+T __ZNSt3__17collateIwED0Ev
+T __ZNSt3__17collateIwED1Ev
+T __ZNSt3__17collateIwED2Ev
+D __ZNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+D __ZNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__17promiseIvE10get_futureEv
+T __ZNSt3__17promiseIvE13set_exceptionESt13exception_ptr
+T __ZNSt3__17promiseIvE24set_value_at_thread_exitEv
+T __ZNSt3__17promiseIvE28set_exception_at_thread_exitESt13exception_ptr
+T __ZNSt3__17promiseIvE9set_valueEv
+T __ZNSt3__17promiseIvEC1Ev
+T __ZNSt3__17promiseIvEC2Ev
+T __ZNSt3__17promiseIvED1Ev
+T __ZNSt3__17promiseIvED2Ev
+T __ZNSt3__18__rs_getEv
+T __ZNSt3__18__sp_mut4lockEv
+T __ZNSt3__18__sp_mut6unlockEv
+S __ZNSt3__18ios_base10floatfieldE
+S __ZNSt3__18ios_base10scientificE
+S __ZNSt3__18ios_base11adjustfieldE
+T __ZNSt3__18ios_base15sync_with_stdioEb
+T __ZNSt3__18ios_base16__call_callbacksENS0_5eventE
+T __ZNSt3__18ios_base17register_callbackEPFvNS0_5eventERS0_iEi
+S __ZNSt3__18ios_base2inE
+T __ZNSt3__18ios_base33__set_badbit_and_consider_rethrowEv
+T __ZNSt3__18ios_base34__set_failbit_and_consider_rethrowEv
+S __ZNSt3__18ios_base3appE
+S __ZNSt3__18ios_base3ateE
+S __ZNSt3__18ios_base3decE
+S __ZNSt3__18ios_base3hexE
+S __ZNSt3__18ios_base3octE
+S __ZNSt3__18ios_base3outE
+T __ZNSt3__18ios_base4InitC1Ev
+T __ZNSt3__18ios_base4InitC2Ev
+T __ZNSt3__18ios_base4InitD1Ev
+T __ZNSt3__18ios_base4InitD2Ev
+T __ZNSt3__18ios_base4initEPv
+S __ZNSt3__18ios_base4leftE
+T __ZNSt3__18ios_base4moveERS0_
+T __ZNSt3__18ios_base4swapERS0_
+T __ZNSt3__18ios_base5clearEj
+S __ZNSt3__18ios_base5fixedE
+T __ZNSt3__18ios_base5imbueERKNS_6localeE
+T __ZNSt3__18ios_base5iwordEi
+T __ZNSt3__18ios_base5pwordEi
+S __ZNSt3__18ios_base5rightE
+S __ZNSt3__18ios_base5truncE
+S __ZNSt3__18ios_base6badbitE
+S __ZNSt3__18ios_base6binaryE
+S __ZNSt3__18ios_base6eofbitE
+S __ZNSt3__18ios_base6skipwsE
+T __ZNSt3__18ios_base6xallocEv
+T __ZNSt3__18ios_base7copyfmtERKS0_
+S __ZNSt3__18ios_base7failbitE
+T __ZNSt3__18ios_base7failureC1EPKcRKNS_10error_codeE
+T __ZNSt3__18ios_base7failureC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_10error_codeE
+T __ZNSt3__18ios_base7failureC2EPKcRKNS_10error_codeE
+T __ZNSt3__18ios_base7failureC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_10error_codeE
+T __ZNSt3__18ios_base7failureD0Ev
+T __ZNSt3__18ios_base7failureD1Ev
+T __ZNSt3__18ios_base7failureD2Ev
+S __ZNSt3__18ios_base7goodbitE
+S __ZNSt3__18ios_base7showposE
+S __ZNSt3__18ios_base7unitbufE
+S __ZNSt3__18ios_base8internalE
+S __ZNSt3__18ios_base8showbaseE
+S __ZNSt3__18ios_base9basefieldE
+S __ZNSt3__18ios_base9boolalphaE
+S __ZNSt3__18ios_base9showpointE
+S __ZNSt3__18ios_base9uppercaseE
+T __ZNSt3__18ios_baseD0Ev
+T __ZNSt3__18ios_baseD1Ev
+T __ZNSt3__18ios_baseD2Ev
+D __ZNSt3__18messagesIcE2idE
+D __ZNSt3__18messagesIwE2idE
+S __ZNSt3__18numpunctIcE2idE
+T __ZNSt3__18numpunctIcEC1Em
+T __ZNSt3__18numpunctIcEC2Em
+T __ZNSt3__18numpunctIcED0Ev
+T __ZNSt3__18numpunctIcED1Ev
+T __ZNSt3__18numpunctIcED2Ev
+S __ZNSt3__18numpunctIwE2idE
+T __ZNSt3__18numpunctIwEC1Em
+T __ZNSt3__18numpunctIwEC2Em
+T __ZNSt3__18numpunctIwED0Ev
+T __ZNSt3__18numpunctIwED1Ev
+T __ZNSt3__18numpunctIwED2Ev
+D __ZNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+D __ZNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__18to_charsEPcS0_d
+T __ZNSt3__18to_charsEPcS0_dNS_12chars_formatE
+T __ZNSt3__18to_charsEPcS0_dNS_12chars_formatEi
+T __ZNSt3__18to_charsEPcS0_e
+T __ZNSt3__18to_charsEPcS0_eNS_12chars_formatE
+T __ZNSt3__18to_charsEPcS0_eNS_12chars_formatEi
+T __ZNSt3__18to_charsEPcS0_f
+T __ZNSt3__18to_charsEPcS0_fNS_12chars_formatE
+T __ZNSt3__18to_charsEPcS0_fNS_12chars_formatEi
+T __ZNSt3__18valarrayImE6resizeEmm
+T __ZNSt3__18valarrayImEC1Em
+T __ZNSt3__18valarrayImEC2Em
+T __ZNSt3__18valarrayImED1Ev
+T __ZNSt3__18valarrayImED2Ev
+T __ZNSt3__19__num_getIcE17__stage2_int_loopEciPcRS2_RjcRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_S2_
+T __ZNSt3__19__num_getIcE17__stage2_int_prepERNS_8ios_baseEPcRc
+T __ZNSt3__19__num_getIcE19__stage2_float_loopEcRbRcPcRS4_ccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjS4_
+T __ZNSt3__19__num_getIcE19__stage2_float_prepERNS_8ios_baseEPcRcS5_
+T __ZNSt3__19__num_getIwE17__stage2_int_loopEwiPcRS2_RjwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_Pw
+T __ZNSt3__19__num_getIwE17__stage2_int_prepERNS_8ios_baseEPwRw
+T __ZNSt3__19__num_getIwE19__stage2_float_loopEwRbRcPcRS4_wwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjPw
+T __ZNSt3__19__num_getIwE19__stage2_float_prepERNS_8ios_baseEPwRwS5_
+T __ZNSt3__19__num_putIcE21__widen_and_group_intEPcS2_S2_S2_RS2_S3_RKNS_6localeE
+T __ZNSt3__19__num_putIcE23__widen_and_group_floatEPcS2_S2_S2_RS2_S3_RKNS_6localeE
+T __ZNSt3__19__num_putIwE21__widen_and_group_intEPcS2_S2_PwRS3_S4_RKNS_6localeE
+T __ZNSt3__19__num_putIwE23__widen_and_group_floatEPcS2_S2_PwRS3_S4_RKNS_6localeE
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEE7copyfmtERKS3_
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEE7copyfmtERKS3_
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEED2Ev
+D __ZNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+T __ZNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE8__do_getERS4_S4_bRKNS_6localeEjRjRbRKNS_5ctypeIcEERNS_10unique_ptrIcPFvPvEEERPcSM_
+D __ZNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE8__do_getERS4_S4_bRKNS_6localeEjRjRbRKNS_5ctypeIwEERNS_10unique_ptrIwPFvPvEEERPwSM_
+D __ZNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__19strstreamD0Ev
+T __ZNSt3__19strstreamD1Ev
+T __ZNSt3__19strstreamD2Ev
+T __ZNSt3__19to_stringEd
+T __ZNSt3__19to_stringEe
+T __ZNSt3__19to_stringEf
+T __ZNSt3__19to_stringEi
+T __ZNSt3__19to_stringEj
+T __ZNSt3__19to_stringEl
+T __ZNSt3__19to_stringEm
+T __ZNSt3__19to_stringEx
+T __ZNSt3__19to_stringEy
+T __ZNSt3__1plIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_12basic_stringIT_T0_T1_EEPKS6_RKS9_
+I __ZNSt8bad_castC1Ev (indirect for __ZNSt8bad_castC1Ev)
+I __ZNSt8bad_castC2Ev (indirect for __ZNSt8bad_castC2Ev)
+I __ZNSt8bad_castD0Ev (indirect for __ZNSt8bad_castD0Ev)
+I __ZNSt8bad_castD1Ev (indirect for __ZNSt8bad_castD1Ev)
+I __ZNSt8bad_castD2Ev (indirect for __ZNSt8bad_castD2Ev)
+I __ZNSt9bad_allocC1Ev (indirect for __ZNSt9bad_allocC1Ev)
+I __ZNSt9bad_allocC2Ev (indirect for __ZNSt9bad_allocC2Ev)
+I __ZNSt9bad_allocD0Ev (indirect for __ZNSt9bad_allocD0Ev)
+I __ZNSt9bad_allocD1Ev (indirect for __ZNSt9bad_allocD1Ev)
+I __ZNSt9bad_allocD2Ev (indirect for __ZNSt9bad_allocD2Ev)
+I __ZNSt9exceptionD0Ev (indirect for __ZNSt9exceptionD0Ev)
+I __ZNSt9exceptionD1Ev (indirect for __ZNSt9exceptionD1Ev)
+I __ZNSt9exceptionD2Ev (indirect for __ZNSt9exceptionD2Ev)
+I __ZNSt9type_infoD0Ev (indirect for __ZNSt9type_infoD0Ev)
+I __ZNSt9type_infoD1Ev (indirect for __ZNSt9type_infoD1Ev)
+I __ZNSt9type_infoD2Ev (indirect for __ZNSt9type_infoD2Ev)
+I __ZSt10unexpectedv (indirect for __ZSt10unexpectedv)
+I __ZSt13get_terminatev (indirect for __ZSt13get_terminatev)
+I __ZSt13set_terminatePFvvE (indirect for __ZSt13set_terminatePFvvE)
+I __ZSt14get_unexpectedv (indirect for __ZSt14get_unexpectedv)
+I __ZSt14set_unexpectedPFvvE (indirect for __ZSt14set_unexpectedPFvvE)
+I __ZSt15get_new_handlerv (indirect for __ZSt15get_new_handlerv)
+I __ZSt15set_new_handlerPFvvE (indirect for __ZSt15set_new_handlerPFvvE)
+T __ZSt17__throw_bad_allocv
+T __ZSt17current_exceptionv
+T __ZSt17rethrow_exceptionSt13exception_ptr
+T __ZSt18uncaught_exceptionv
+T __ZSt19uncaught_exceptionsv
+S __ZSt7nothrow
+I __ZSt9terminatev (indirect for __ZSt9terminatev)
+S __ZTCNSt3__110istrstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__110ostrstreamE0_NS_13basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE16_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE0_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_14basic_iostreamIcS2_EE
+S __ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE16_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__19strstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__19strstreamE0_NS_14basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__19strstreamE16_NS_13basic_ostreamIcNS_11char_traitsIcEEEE
+I __ZTIDh (indirect for __ZTIDh)
+I __ZTIDi (indirect for __ZTIDi)
+I __ZTIDn (indirect for __ZTIDn)
+I __ZTIDs (indirect for __ZTIDs)
+I __ZTIDu (indirect for __ZTIDu)
+I __ZTIN10__cxxabiv116__enum_type_infoE (indirect for __ZTIN10__cxxabiv116__enum_type_infoE)
+I __ZTIN10__cxxabiv116__shim_type_infoE (indirect for __ZTIN10__cxxabiv116__shim_type_infoE)
+I __ZTIN10__cxxabiv117__array_type_infoE (indirect for __ZTIN10__cxxabiv117__array_type_infoE)
+I __ZTIN10__cxxabiv117__pbase_type_infoE (indirect for __ZTIN10__cxxabiv117__pbase_type_infoE)
+I __ZTIN10__cxxabiv119__pointer_type_infoE (indirect for __ZTIN10__cxxabiv119__pointer_type_infoE)
+I __ZTIN10__cxxabiv120__function_type_infoE (indirect for __ZTIN10__cxxabiv120__function_type_infoE)
+I __ZTIN10__cxxabiv123__fundamental_type_infoE (indirect for __ZTIN10__cxxabiv123__fundamental_type_infoE)
+I __ZTIN10__cxxabiv129__pointer_to_member_type_infoE (indirect for __ZTIN10__cxxabiv129__pointer_to_member_type_infoE)
+S __ZTINSt12experimental15fundamentals_v112bad_any_castE
+S __ZTINSt12experimental19bad_optional_accessE
+S __ZTINSt3__110istrstreamE
+S __ZTINSt3__110moneypunctIcLb0EEE
+S __ZTINSt3__110moneypunctIcLb1EEE
+S __ZTINSt3__110moneypunctIwLb0EEE
+S __ZTINSt3__110moneypunctIwLb1EEE
+S __ZTINSt3__110ostrstreamE
+S __ZTINSt3__111regex_errorE
+S __ZTINSt3__112bad_weak_ptrE
+S __ZTINSt3__112ctype_bynameIcEE
+S __ZTINSt3__112ctype_bynameIwEE
+S __ZTINSt3__112future_errorE
+S __ZTINSt3__112strstreambufE
+S __ZTINSt3__112system_errorE
+S __ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+S __ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTINSt3__114__codecvt_utf8IDiEE
+S __ZTINSt3__114__codecvt_utf8IDsEE
+S __ZTINSt3__114__codecvt_utf8IwEE
+S __ZTINSt3__114__shared_countE
+S __ZTINSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIDic11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIDsc11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIcc11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIwc11__mbstate_tEE
+S __ZTINSt3__114collate_bynameIcEE
+S __ZTINSt3__114collate_bynameIwEE
+S __ZTINSt3__114error_categoryE
+S __ZTINSt3__115__codecvt_utf16IDiLb0EEE
+S __ZTINSt3__115__codecvt_utf16IDiLb1EEE
+S __ZTINSt3__115__codecvt_utf16IDsLb0EEE
+S __ZTINSt3__115__codecvt_utf16IDsLb1EEE
+S __ZTINSt3__115__codecvt_utf16IwLb0EEE
+S __ZTINSt3__115__codecvt_utf16IwLb1EEE
+S __ZTINSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+S __ZTINSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+S __ZTINSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__115messages_bynameIcEE
+S __ZTINSt3__115messages_bynameIwEE
+S __ZTINSt3__115numpunct_bynameIcEE
+S __ZTINSt3__115numpunct_bynameIwEE
+S __ZTINSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__116__narrow_to_utf8ILm16EEE
+S __ZTINSt3__116__narrow_to_utf8ILm32EEE
+S __ZTINSt3__117__assoc_sub_stateE
+S __ZTINSt3__117__widen_from_utf8ILm16EEE
+S __ZTINSt3__117__widen_from_utf8ILm32EEE
+S __ZTINSt3__117bad_function_callE
+S __ZTINSt3__117moneypunct_bynameIcLb0EEE
+S __ZTINSt3__117moneypunct_bynameIcLb1EEE
+S __ZTINSt3__117moneypunct_bynameIwLb0EEE
+S __ZTINSt3__117moneypunct_bynameIwLb1EEE
+S __ZTINSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__119__shared_weak_countE
+S __ZTINSt3__119bad_expected_accessIvEE
+S __ZTINSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__120__codecvt_utf8_utf16IDiEE
+S __ZTINSt3__120__codecvt_utf8_utf16IDsEE
+S __ZTINSt3__120__codecvt_utf8_utf16IwEE
+S __ZTINSt3__13pmr15memory_resourceE
+S __ZTINSt3__13pmr25monotonic_buffer_resourceE
+S __ZTINSt3__13pmr26synchronized_pool_resourceE
+S __ZTINSt3__13pmr28unsynchronized_pool_resourceE
+S __ZTINSt3__14__fs10filesystem16filesystem_errorE
+S __ZTINSt3__15ctypeIcEE
+S __ZTINSt3__15ctypeIwEE
+S __ZTINSt3__16locale5facetE
+S __ZTINSt3__17codecvtIDiDu11__mbstate_tEE
+S __ZTINSt3__17codecvtIDic11__mbstate_tEE
+S __ZTINSt3__17codecvtIDsDu11__mbstate_tEE
+S __ZTINSt3__17codecvtIDsc11__mbstate_tEE
+S __ZTINSt3__17codecvtIcc11__mbstate_tEE
+S __ZTINSt3__17codecvtIwc11__mbstate_tEE
+S __ZTINSt3__17collateIcEE
+S __ZTINSt3__17collateIwEE
+S __ZTINSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__18ios_base7failureE
+S __ZTINSt3__18ios_baseE
+S __ZTINSt3__18messagesIcEE
+S __ZTINSt3__18messagesIwEE
+S __ZTINSt3__18numpunctIcEE
+S __ZTINSt3__18numpunctIwEE
+S __ZTINSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__19basic_iosIcNS_11char_traitsIcEEEE
+S __ZTINSt3__19basic_iosIwNS_11char_traitsIwEEEE
+S __ZTINSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__19strstreamE
+I __ZTIPDh (indirect for __ZTIPDh)
+I __ZTIPDi (indirect for __ZTIPDi)
+I __ZTIPDn (indirect for __ZTIPDn)
+I __ZTIPDs (indirect for __ZTIPDs)
+I __ZTIPDu (indirect for __ZTIPDu)
+I __ZTIPKDh (indirect for __ZTIPKDh)
+I __ZTIPKDi (indirect for __ZTIPKDi)
+I __ZTIPKDn (indirect for __ZTIPKDn)
+I __ZTIPKDs (indirect for __ZTIPKDs)
+I __ZTIPKDu (indirect for __ZTIPKDu)
+I __ZTIPKa (indirect for __ZTIPKa)
+I __ZTIPKb (indirect for __ZTIPKb)
+I __ZTIPKc (indirect for __ZTIPKc)
+I __ZTIPKd (indirect for __ZTIPKd)
+I __ZTIPKe (indirect for __ZTIPKe)
+I __ZTIPKf (indirect for __ZTIPKf)
+I __ZTIPKg (indirect for __ZTIPKg)
+I __ZTIPKh (indirect for __ZTIPKh)
+I __ZTIPKi (indirect for __ZTIPKi)
+I __ZTIPKj (indirect for __ZTIPKj)
+I __ZTIPKl (indirect for __ZTIPKl)
+I __ZTIPKm (indirect for __ZTIPKm)
+I __ZTIPKn (indirect for __ZTIPKn)
+I __ZTIPKo (indirect for __ZTIPKo)
+I __ZTIPKs (indirect for __ZTIPKs)
+I __ZTIPKt (indirect for __ZTIPKt)
+I __ZTIPKv (indirect for __ZTIPKv)
+I __ZTIPKw (indirect for __ZTIPKw)
+I __ZTIPKx (indirect for __ZTIPKx)
+I __ZTIPKy (indirect for __ZTIPKy)
+I __ZTIPa (indirect for __ZTIPa)
+I __ZTIPb (indirect for __ZTIPb)
+I __ZTIPc (indirect for __ZTIPc)
+I __ZTIPd (indirect for __ZTIPd)
+I __ZTIPe (indirect for __ZTIPe)
+I __ZTIPf (indirect for __ZTIPf)
+I __ZTIPg (indirect for __ZTIPg)
+I __ZTIPh (indirect for __ZTIPh)
+I __ZTIPi (indirect for __ZTIPi)
+I __ZTIPj (indirect for __ZTIPj)
+I __ZTIPl (indirect for __ZTIPl)
+I __ZTIPm (indirect for __ZTIPm)
+I __ZTIPn (indirect for __ZTIPn)
+I __ZTIPo (indirect for __ZTIPo)
+I __ZTIPs (indirect for __ZTIPs)
+I __ZTIPt (indirect for __ZTIPt)
+I __ZTIPv (indirect for __ZTIPv)
+I __ZTIPw (indirect for __ZTIPw)
+I __ZTIPx (indirect for __ZTIPx)
+I __ZTIPy (indirect for __ZTIPy)
+I __ZTISt10bad_typeid (indirect for __ZTISt10bad_typeid)
+I __ZTISt11logic_error (indirect for __ZTISt11logic_error)
+I __ZTISt11range_error (indirect for __ZTISt11range_error)
+S __ZTISt12bad_any_cast
+I __ZTISt12domain_error (indirect for __ZTISt12domain_error)
+I __ZTISt12length_error (indirect for __ZTISt12length_error)
+I __ZTISt12out_of_range (indirect for __ZTISt12out_of_range)
+I __ZTISt13bad_exception (indirect for __ZTISt13bad_exception)
+I __ZTISt13runtime_error (indirect for __ZTISt13runtime_error)
+I __ZTISt14overflow_error (indirect for __ZTISt14overflow_error)
+I __ZTISt15underflow_error (indirect for __ZTISt15underflow_error)
+I __ZTISt16invalid_argument (indirect for __ZTISt16invalid_argument)
+S __ZTISt16nested_exception
+S __ZTISt18bad_variant_access
+S __ZTISt19bad_optional_access
+I __ZTISt20bad_array_new_length (indirect for __ZTISt20bad_array_new_length)
+S __ZTISt29__bad_variant_access_with_msg
+I __ZTISt8bad_cast (indirect for __ZTISt8bad_cast)
+I __ZTISt9bad_alloc (indirect for __ZTISt9bad_alloc)
+I __ZTISt9exception (indirect for __ZTISt9exception)
+I __ZTISt9type_info (indirect for __ZTISt9type_info)
+I __ZTIa (indirect for __ZTIa)
+I __ZTIb (indirect for __ZTIb)
+I __ZTIc (indirect for __ZTIc)
+I __ZTId (indirect for __ZTId)
+I __ZTIe (indirect for __ZTIe)
+I __ZTIf (indirect for __ZTIf)
+I __ZTIg (indirect for __ZTIg)
+I __ZTIh (indirect for __ZTIh)
+I __ZTIi (indirect for __ZTIi)
+I __ZTIj (indirect for __ZTIj)
+I __ZTIl (indirect for __ZTIl)
+I __ZTIm (indirect for __ZTIm)
+I __ZTIn (indirect for __ZTIn)
+I __ZTIo (indirect for __ZTIo)
+I __ZTIs (indirect for __ZTIs)
+I __ZTIt (indirect for __ZTIt)
+I __ZTIv (indirect for __ZTIv)
+I __ZTIw (indirect for __ZTIw)
+I __ZTIx (indirect for __ZTIx)
+I __ZTIy (indirect for __ZTIy)
+I __ZTSDh (indirect for __ZTSDh)
+I __ZTSDi (indirect for __ZTSDi)
+I __ZTSDn (indirect for __ZTSDn)
+I __ZTSDs (indirect for __ZTSDs)
+I __ZTSDu (indirect for __ZTSDu)
+I __ZTSN10__cxxabiv116__enum_type_infoE (indirect for __ZTSN10__cxxabiv116__enum_type_infoE)
+I __ZTSN10__cxxabiv116__shim_type_infoE (indirect for __ZTSN10__cxxabiv116__shim_type_infoE)
+I __ZTSN10__cxxabiv117__array_type_infoE (indirect for __ZTSN10__cxxabiv117__array_type_infoE)
+I __ZTSN10__cxxabiv117__class_type_infoE (indirect for __ZTSN10__cxxabiv117__class_type_infoE)
+I __ZTSN10__cxxabiv117__pbase_type_infoE (indirect for __ZTSN10__cxxabiv117__pbase_type_infoE)
+I __ZTSN10__cxxabiv119__pointer_type_infoE (indirect for __ZTSN10__cxxabiv119__pointer_type_infoE)
+I __ZTSN10__cxxabiv120__function_type_infoE (indirect for __ZTSN10__cxxabiv120__function_type_infoE)
+I __ZTSN10__cxxabiv120__si_class_type_infoE (indirect for __ZTSN10__cxxabiv120__si_class_type_infoE)
+I __ZTSN10__cxxabiv121__vmi_class_type_infoE (indirect for __ZTSN10__cxxabiv121__vmi_class_type_infoE)
+I __ZTSN10__cxxabiv123__fundamental_type_infoE (indirect for __ZTSN10__cxxabiv123__fundamental_type_infoE)
+I __ZTSN10__cxxabiv129__pointer_to_member_type_infoE (indirect for __ZTSN10__cxxabiv129__pointer_to_member_type_infoE)
+S __ZTSNSt12experimental15fundamentals_v112bad_any_castE
+S __ZTSNSt12experimental19bad_optional_accessE
+S __ZTSNSt3__110istrstreamE
+S __ZTSNSt3__110moneypunctIcLb0EEE
+S __ZTSNSt3__110moneypunctIcLb1EEE
+S __ZTSNSt3__110moneypunctIwLb0EEE
+S __ZTSNSt3__110moneypunctIwLb1EEE
+S __ZTSNSt3__110ostrstreamE
+S __ZTSNSt3__111regex_errorE
+S __ZTSNSt3__112bad_weak_ptrE
+S __ZTSNSt3__112ctype_bynameIcEE
+S __ZTSNSt3__112ctype_bynameIwEE
+S __ZTSNSt3__112future_errorE
+S __ZTSNSt3__112strstreambufE
+S __ZTSNSt3__112system_errorE
+S __ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__114__codecvt_utf8IDiEE
+S __ZTSNSt3__114__codecvt_utf8IDsEE
+S __ZTSNSt3__114__codecvt_utf8IwEE
+S __ZTSNSt3__114__shared_countE
+S __ZTSNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIDic11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIDsc11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIcc11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIwc11__mbstate_tEE
+S __ZTSNSt3__114collate_bynameIcEE
+S __ZTSNSt3__114collate_bynameIwEE
+S __ZTSNSt3__114error_categoryE
+S __ZTSNSt3__115__codecvt_utf16IDiLb0EEE
+S __ZTSNSt3__115__codecvt_utf16IDiLb1EEE
+S __ZTSNSt3__115__codecvt_utf16IDsLb0EEE
+S __ZTSNSt3__115__codecvt_utf16IDsLb1EEE
+S __ZTSNSt3__115__codecvt_utf16IwLb0EEE
+S __ZTSNSt3__115__codecvt_utf16IwLb1EEE
+S __ZTSNSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__115messages_bynameIcEE
+S __ZTSNSt3__115messages_bynameIwEE
+S __ZTSNSt3__115numpunct_bynameIcEE
+S __ZTSNSt3__115numpunct_bynameIwEE
+S __ZTSNSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__116__narrow_to_utf8ILm16EEE
+S __ZTSNSt3__116__narrow_to_utf8ILm32EEE
+S __ZTSNSt3__117__assoc_sub_stateE
+S __ZTSNSt3__117__widen_from_utf8ILm16EEE
+S __ZTSNSt3__117__widen_from_utf8ILm32EEE
+S __ZTSNSt3__117bad_function_callE
+S __ZTSNSt3__117moneypunct_bynameIcLb0EEE
+S __ZTSNSt3__117moneypunct_bynameIcLb1EEE
+S __ZTSNSt3__117moneypunct_bynameIwLb0EEE
+S __ZTSNSt3__117moneypunct_bynameIwLb1EEE
+S __ZTSNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__119__shared_weak_countE
+S __ZTSNSt3__119bad_expected_accessIvEE
+S __ZTSNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__120__codecvt_utf8_utf16IDiEE
+S __ZTSNSt3__120__codecvt_utf8_utf16IDsEE
+S __ZTSNSt3__120__codecvt_utf8_utf16IwEE
+S __ZTSNSt3__13pmr15memory_resourceE
+S __ZTSNSt3__13pmr25monotonic_buffer_resourceE
+S __ZTSNSt3__13pmr26synchronized_pool_resourceE
+S __ZTSNSt3__13pmr28unsynchronized_pool_resourceE
+S __ZTSNSt3__14__fs10filesystem16filesystem_errorE
+S __ZTSNSt3__15ctypeIcEE
+S __ZTSNSt3__15ctypeIwEE
+S __ZTSNSt3__16locale5facetE
+S __ZTSNSt3__17codecvtIDiDu11__mbstate_tEE
+S __ZTSNSt3__17codecvtIDic11__mbstate_tEE
+S __ZTSNSt3__17codecvtIDsDu11__mbstate_tEE
+S __ZTSNSt3__17codecvtIDsc11__mbstate_tEE
+S __ZTSNSt3__17codecvtIcc11__mbstate_tEE
+S __ZTSNSt3__17codecvtIwc11__mbstate_tEE
+S __ZTSNSt3__17collateIcEE
+S __ZTSNSt3__17collateIwEE
+S __ZTSNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__18ios_base7failureE
+S __ZTSNSt3__18ios_baseE
+S __ZTSNSt3__18messagesIcEE
+S __ZTSNSt3__18messagesIwEE
+S __ZTSNSt3__18numpunctIcEE
+S __ZTSNSt3__18numpunctIwEE
+S __ZTSNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__19basic_iosIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__19basic_iosIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__19strstreamE
+I __ZTSPDh (indirect for __ZTSPDh)
+I __ZTSPDi (indirect for __ZTSPDi)
+I __ZTSPDn (indirect for __ZTSPDn)
+I __ZTSPDs (indirect for __ZTSPDs)
+I __ZTSPDu (indirect for __ZTSPDu)
+I __ZTSPKDh (indirect for __ZTSPKDh)
+I __ZTSPKDi (indirect for __ZTSPKDi)
+I __ZTSPKDn (indirect for __ZTSPKDn)
+I __ZTSPKDs (indirect for __ZTSPKDs)
+I __ZTSPKDu (indirect for __ZTSPKDu)
+I __ZTSPKa (indirect for __ZTSPKa)
+I __ZTSPKb (indirect for __ZTSPKb)
+I __ZTSPKc (indirect for __ZTSPKc)
+I __ZTSPKd (indirect for __ZTSPKd)
+I __ZTSPKe (indirect for __ZTSPKe)
+I __ZTSPKf (indirect for __ZTSPKf)
+I __ZTSPKg (indirect for __ZTSPKg)
+I __ZTSPKh (indirect for __ZTSPKh)
+I __ZTSPKi (indirect for __ZTSPKi)
+I __ZTSPKj (indirect for __ZTSPKj)
+I __ZTSPKl (indirect for __ZTSPKl)
+I __ZTSPKm (indirect for __ZTSPKm)
+I __ZTSPKn (indirect for __ZTSPKn)
+I __ZTSPKo (indirect for __ZTSPKo)
+I __ZTSPKs (indirect for __ZTSPKs)
+I __ZTSPKt (indirect for __ZTSPKt)
+I __ZTSPKv (indirect for __ZTSPKv)
+I __ZTSPKw (indirect for __ZTSPKw)
+I __ZTSPKx (indirect for __ZTSPKx)
+I __ZTSPKy (indirect for __ZTSPKy)
+I __ZTSPa (indirect for __ZTSPa)
+I __ZTSPb (indirect for __ZTSPb)
+I __ZTSPc (indirect for __ZTSPc)
+I __ZTSPd (indirect for __ZTSPd)
+I __ZTSPe (indirect for __ZTSPe)
+I __ZTSPf (indirect for __ZTSPf)
+I __ZTSPg (indirect for __ZTSPg)
+I __ZTSPh (indirect for __ZTSPh)
+I __ZTSPi (indirect for __ZTSPi)
+I __ZTSPj (indirect for __ZTSPj)
+I __ZTSPl (indirect for __ZTSPl)
+I __ZTSPm (indirect for __ZTSPm)
+I __ZTSPn (indirect for __ZTSPn)
+I __ZTSPo (indirect for __ZTSPo)
+I __ZTSPs (indirect for __ZTSPs)
+I __ZTSPt (indirect for __ZTSPt)
+I __ZTSPv (indirect for __ZTSPv)
+I __ZTSPw (indirect for __ZTSPw)
+I __ZTSPx (indirect for __ZTSPx)
+I __ZTSPy (indirect for __ZTSPy)
+I __ZTSSt10bad_typeid (indirect for __ZTSSt10bad_typeid)
+I __ZTSSt11logic_error (indirect for __ZTSSt11logic_error)
+I __ZTSSt11range_error (indirect for __ZTSSt11range_error)
+S __ZTSSt12bad_any_cast
+I __ZTSSt12domain_error (indirect for __ZTSSt12domain_error)
+I __ZTSSt12length_error (indirect for __ZTSSt12length_error)
+I __ZTSSt12out_of_range (indirect for __ZTSSt12out_of_range)
+I __ZTSSt13bad_exception (indirect for __ZTSSt13bad_exception)
+I __ZTSSt13runtime_error (indirect for __ZTSSt13runtime_error)
+I __ZTSSt14overflow_error (indirect for __ZTSSt14overflow_error)
+I __ZTSSt15underflow_error (indirect for __ZTSSt15underflow_error)
+I __ZTSSt16invalid_argument (indirect for __ZTSSt16invalid_argument)
+S __ZTSSt16nested_exception
+S __ZTSSt18bad_variant_access
+S __ZTSSt19bad_optional_access
+I __ZTSSt20bad_array_new_length (indirect for __ZTSSt20bad_array_new_length)
+S __ZTSSt29__bad_variant_access_with_msg
+I __ZTSSt8bad_cast (indirect for __ZTSSt8bad_cast)
+I __ZTSSt9bad_alloc (indirect for __ZTSSt9bad_alloc)
+I __ZTSSt9exception (indirect for __ZTSSt9exception)
+I __ZTSSt9type_info (indirect for __ZTSSt9type_info)
+I __ZTSa (indirect for __ZTSa)
+I __ZTSb (indirect for __ZTSb)
+I __ZTSc (indirect for __ZTSc)
+I __ZTSd (indirect for __ZTSd)
+I __ZTSe (indirect for __ZTSe)
+I __ZTSf (indirect for __ZTSf)
+I __ZTSg (indirect for __ZTSg)
+I __ZTSh (indirect for __ZTSh)
+I __ZTSi (indirect for __ZTSi)
+I __ZTSj (indirect for __ZTSj)
+I __ZTSl (indirect for __ZTSl)
+I __ZTSm (indirect for __ZTSm)
+I __ZTSn (indirect for __ZTSn)
+I __ZTSo (indirect for __ZTSo)
+I __ZTSs (indirect for __ZTSs)
+I __ZTSt (indirect for __ZTSt)
+I __ZTSv (indirect for __ZTSv)
+I __ZTSw (indirect for __ZTSw)
+I __ZTSx (indirect for __ZTSx)
+I __ZTSy (indirect for __ZTSy)
+S __ZTTNSt3__110istrstreamE
+S __ZTTNSt3__110ostrstreamE
+S __ZTTNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTTNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTTNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTTNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTTNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTTNSt3__19strstreamE
+I __ZTVN10__cxxabiv116__enum_type_infoE (indirect for __ZTVN10__cxxabiv116__enum_type_infoE)
+I __ZTVN10__cxxabiv116__shim_type_infoE (indirect for __ZTVN10__cxxabiv116__shim_type_infoE)
+I __ZTVN10__cxxabiv117__array_type_infoE (indirect for __ZTVN10__cxxabiv117__array_type_infoE)
+I __ZTVN10__cxxabiv117__class_type_infoE (indirect for __ZTVN10__cxxabiv117__class_type_infoE)
+I __ZTVN10__cxxabiv117__pbase_type_infoE (indirect for __ZTVN10__cxxabiv117__pbase_type_infoE)
+I __ZTVN10__cxxabiv119__pointer_type_infoE (indirect for __ZTVN10__cxxabiv119__pointer_type_infoE)
+I __ZTVN10__cxxabiv120__function_type_infoE (indirect for __ZTVN10__cxxabiv120__function_type_infoE)
+I __ZTVN10__cxxabiv120__si_class_type_infoE (indirect for __ZTVN10__cxxabiv120__si_class_type_infoE)
+I __ZTVN10__cxxabiv121__vmi_class_type_infoE (indirect for __ZTVN10__cxxabiv121__vmi_class_type_infoE)
+I __ZTVN10__cxxabiv123__fundamental_type_infoE (indirect for __ZTVN10__cxxabiv123__fundamental_type_infoE)
+I __ZTVN10__cxxabiv129__pointer_to_member_type_infoE (indirect for __ZTVN10__cxxabiv129__pointer_to_member_type_infoE)
+S __ZTVNSt12experimental15fundamentals_v112bad_any_castE
+S __ZTVNSt12experimental19bad_optional_accessE
+S __ZTVNSt3__110istrstreamE
+S __ZTVNSt3__110moneypunctIcLb0EEE
+S __ZTVNSt3__110moneypunctIcLb1EEE
+S __ZTVNSt3__110moneypunctIwLb0EEE
+S __ZTVNSt3__110moneypunctIwLb1EEE
+S __ZTVNSt3__110ostrstreamE
+S __ZTVNSt3__111regex_errorE
+S __ZTVNSt3__112bad_weak_ptrE
+S __ZTVNSt3__112ctype_bynameIcEE
+S __ZTVNSt3__112ctype_bynameIwEE
+S __ZTVNSt3__112future_errorE
+S __ZTVNSt3__112strstreambufE
+S __ZTVNSt3__112system_errorE
+S __ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__114__codecvt_utf8IDiEE
+S __ZTVNSt3__114__codecvt_utf8IDsEE
+S __ZTVNSt3__114__codecvt_utf8IwEE
+S __ZTVNSt3__114__shared_countE
+S __ZTVNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIDic11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIDsc11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIcc11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIwc11__mbstate_tEE
+S __ZTVNSt3__114collate_bynameIcEE
+S __ZTVNSt3__114collate_bynameIwEE
+S __ZTVNSt3__114error_categoryE
+S __ZTVNSt3__115__codecvt_utf16IDiLb0EEE
+S __ZTVNSt3__115__codecvt_utf16IDiLb1EEE
+S __ZTVNSt3__115__codecvt_utf16IDsLb0EEE
+S __ZTVNSt3__115__codecvt_utf16IDsLb1EEE
+S __ZTVNSt3__115__codecvt_utf16IwLb0EEE
+S __ZTVNSt3__115__codecvt_utf16IwLb1EEE
+S __ZTVNSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__115messages_bynameIcEE
+S __ZTVNSt3__115messages_bynameIwEE
+S __ZTVNSt3__115numpunct_bynameIcEE
+S __ZTVNSt3__115numpunct_bynameIwEE
+S __ZTVNSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__116__narrow_to_utf8ILm16EEE
+S __ZTVNSt3__116__narrow_to_utf8ILm32EEE
+S __ZTVNSt3__117__assoc_sub_stateE
+S __ZTVNSt3__117__widen_from_utf8ILm16EEE
+S __ZTVNSt3__117__widen_from_utf8ILm32EEE
+S __ZTVNSt3__117bad_function_callE
+S __ZTVNSt3__117moneypunct_bynameIcLb0EEE
+S __ZTVNSt3__117moneypunct_bynameIcLb1EEE
+S __ZTVNSt3__117moneypunct_bynameIwLb0EEE
+S __ZTVNSt3__117moneypunct_bynameIwLb1EEE
+S __ZTVNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__119__shared_weak_countE
+S __ZTVNSt3__119bad_expected_accessIvEE
+S __ZTVNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__120__codecvt_utf8_utf16IDiEE
+S __ZTVNSt3__120__codecvt_utf8_utf16IDsEE
+S __ZTVNSt3__120__codecvt_utf8_utf16IwEE
+S __ZTVNSt3__13pmr15memory_resourceE
+S __ZTVNSt3__13pmr25monotonic_buffer_resourceE
+S __ZTVNSt3__13pmr26synchronized_pool_resourceE
+S __ZTVNSt3__13pmr28unsynchronized_pool_resourceE
+S __ZTVNSt3__14__fs10filesystem16filesystem_errorE
+S __ZTVNSt3__15ctypeIcEE
+S __ZTVNSt3__15ctypeIwEE
+S __ZTVNSt3__16locale5facetE
+S __ZTVNSt3__17codecvtIDiDu11__mbstate_tEE
+S __ZTVNSt3__17codecvtIDic11__mbstate_tEE
+S __ZTVNSt3__17codecvtIDsDu11__mbstate_tEE
+S __ZTVNSt3__17codecvtIDsc11__mbstate_tEE
+S __ZTVNSt3__17codecvtIcc11__mbstate_tEE
+S __ZTVNSt3__17codecvtIwc11__mbstate_tEE
+S __ZTVNSt3__17collateIcEE
+S __ZTVNSt3__17collateIwEE
+S __ZTVNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__18ios_base7failureE
+S __ZTVNSt3__18ios_baseE
+S __ZTVNSt3__18messagesIcEE
+S __ZTVNSt3__18messagesIwEE
+S __ZTVNSt3__18numpunctIcEE
+S __ZTVNSt3__18numpunctIwEE
+S __ZTVNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__19basic_iosIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__19basic_iosIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__19strstreamE
+I __ZTVSt10bad_typeid (indirect for __ZTVSt10bad_typeid)
+I __ZTVSt11logic_error (indirect for __ZTVSt11logic_error)
+I __ZTVSt11range_error (indirect for __ZTVSt11range_error)
+S __ZTVSt12bad_any_cast
+I __ZTVSt12domain_error (indirect for __ZTVSt12domain_error)
+I __ZTVSt12length_error (indirect for __ZTVSt12length_error)
+I __ZTVSt12out_of_range (indirect for __ZTVSt12out_of_range)
+I __ZTVSt13bad_exception (indirect for __ZTVSt13bad_exception)
+I __ZTVSt13runtime_error (indirect for __ZTVSt13runtime_error)
+I __ZTVSt14overflow_error (indirect for __ZTVSt14overflow_error)
+I __ZTVSt15underflow_error (indirect for __ZTVSt15underflow_error)
+I __ZTVSt16invalid_argument (indirect for __ZTVSt16invalid_argument)
+S __ZTVSt16nested_exception
+S __ZTVSt18bad_variant_access
+S __ZTVSt19bad_optional_access
+I __ZTVSt20bad_array_new_length (indirect for __ZTVSt20bad_array_new_length)
+S __ZTVSt29__bad_variant_access_with_msg
+I __ZTVSt8bad_cast (indirect for __ZTVSt8bad_cast)
+I __ZTVSt9bad_alloc (indirect for __ZTVSt9bad_alloc)
+I __ZTVSt9exception (indirect for __ZTVSt9exception)
+I __ZTVSt9type_info (indirect for __ZTVSt9type_info)
+T __ZThn16_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+T __ZThn16_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T __ZThn16_NSt3__19strstreamD0Ev
+T __ZThn16_NSt3__19strstreamD1Ev
+T __ZTv0_n24_NSt3__110istrstreamD0Ev
+T __ZTv0_n24_NSt3__110istrstreamD1Ev
+T __ZTv0_n24_NSt3__110ostrstreamD0Ev
+T __ZTv0_n24_NSt3__110ostrstreamD1Ev
+T __ZTv0_n24_NSt3__113basic_istreamIcNS_11char_traitsIcEEED0Ev
+T __ZTv0_n24_NSt3__113basic_istreamIcNS_11char_traitsIcEEED1Ev
+T __ZTv0_n24_NSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev
+T __ZTv0_n24_NSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIwNS_11char_traitsIwEEED0Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIwNS_11char_traitsIwEEED1Ev
+T __ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+T __ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T __ZTv0_n24_NSt3__19strstreamD0Ev
+T __ZTv0_n24_NSt3__19strstreamD1Ev
+I __ZdaPv (indirect for __ZdaPv)
+I __ZdaPvRKSt9nothrow_t (indirect for __ZdaPvRKSt9nothrow_t)
+I __ZdaPvSt11align_val_t (indirect for __ZdaPvSt11align_val_t)
+I __ZdaPvSt11align_val_tRKSt9nothrow_t (indirect for __ZdaPvSt11align_val_tRKSt9nothrow_t)
+I __ZdaPvm (indirect for __ZdaPvm)
+I __ZdaPvmSt11align_val_t (indirect for __ZdaPvmSt11align_val_t)
+I __ZdlPv (indirect for __ZdlPv)
+I __ZdlPvRKSt9nothrow_t (indirect for __ZdlPvRKSt9nothrow_t)
+I __ZdlPvSt11align_val_t (indirect for __ZdlPvSt11align_val_t)
+I __ZdlPvSt11align_val_tRKSt9nothrow_t (indirect for __ZdlPvSt11align_val_tRKSt9nothrow_t)
+I __ZdlPvm (indirect for __ZdlPvm)
+I __ZdlPvmSt11align_val_t (indirect for __ZdlPvmSt11align_val_t)
+I __Znam (indirect for __Znam)
+I __ZnamRKSt9nothrow_t (indirect for __ZnamRKSt9nothrow_t)
+I __ZnamSt11align_val_t (indirect for __ZnamSt11align_val_t)
+I __ZnamSt11align_val_tRKSt9nothrow_t (indirect for __ZnamSt11align_val_tRKSt9nothrow_t)
+I __Znwm (indirect for __Znwm)
+I __ZnwmRKSt9nothrow_t (indirect for __ZnwmRKSt9nothrow_t)
+I __ZnwmSt11align_val_t (indirect for __ZnwmSt11align_val_t)
+I __ZnwmSt11align_val_tRKSt9nothrow_t (indirect for __ZnwmSt11align_val_tRKSt9nothrow_t)
+I ___cxa_allocate_dependent_exception (indirect for ___cxa_allocate_dependent_exception)
+I ___cxa_allocate_exception (indirect for ___cxa_allocate_exception)
+I ___cxa_bad_cast (indirect for ___cxa_bad_cast)
+I ___cxa_bad_typeid (indirect for ___cxa_bad_typeid)
+I ___cxa_begin_catch (indirect for ___cxa_begin_catch)
+I ___cxa_call_terminate (indirect for ___cxa_call_terminate)
+I ___cxa_call_unexpected (indirect for ___cxa_call_unexpected)
+I ___cxa_current_exception_type (indirect for ___cxa_current_exception_type)
+I ___cxa_current_primary_exception (indirect for ___cxa_current_primary_exception)
+I ___cxa_decrement_exception_refcount (indirect for ___cxa_decrement_exception_refcount)
+I ___cxa_deleted_virtual (indirect for ___cxa_deleted_virtual)
+I ___cxa_demangle (indirect for ___cxa_demangle)
+I ___cxa_end_catch (indirect for ___cxa_end_catch)
+I ___cxa_free_dependent_exception (indirect for ___cxa_free_dependent_exception)
+I ___cxa_free_exception (indirect for ___cxa_free_exception)
+I ___cxa_get_exception_ptr (indirect for ___cxa_get_exception_ptr)
+I ___cxa_get_globals (indirect for ___cxa_get_globals)
+I ___cxa_get_globals_fast (indirect for ___cxa_get_globals_fast)
+I ___cxa_guard_abort (indirect for ___cxa_guard_abort)
+I ___cxa_guard_acquire (indirect for ___cxa_guard_acquire)
+I ___cxa_guard_release (indirect for ___cxa_guard_release)
+I ___cxa_increment_exception_refcount (indirect for ___cxa_increment_exception_refcount)
+I ___cxa_init_primary_exception (indirect for ___cxa_init_primary_exception)
+I ___cxa_new_handler (indirect for ___cxa_new_handler)
+I ___cxa_pure_virtual (indirect for ___cxa_pure_virtual)
+I ___cxa_rethrow (indirect for ___cxa_rethrow)
+I ___cxa_terminate_handler (indirect for ___cxa_terminate_handler)
+I ___cxa_throw (indirect for ___cxa_throw)
+I ___cxa_throw_bad_array_new_length (indirect for ___cxa_throw_bad_array_new_length)
+I ___cxa_uncaught_exception (indirect for ___cxa_uncaught_exception)
+I ___cxa_uncaught_exceptions (indirect for ___cxa_uncaught_exceptions)
+I ___cxa_unexpected_handler (indirect for ___cxa_unexpected_handler)
+I ___cxa_vec_cctor (indirect for ___cxa_vec_cctor)
+I ___cxa_vec_cleanup (indirect for ___cxa_vec_cleanup)
+I ___cxa_vec_ctor (indirect for ___cxa_vec_ctor)
+I ___cxa_vec_delete (indirect for ___cxa_vec_delete)
+I ___cxa_vec_delete2 (indirect for ___cxa_vec_delete2)
+I ___cxa_vec_delete3 (indirect for ___cxa_vec_delete3)
+I ___cxa_vec_dtor (indirect for ___cxa_vec_dtor)
+I ___cxa_vec_new (indirect for ___cxa_vec_new)
+I ___cxa_vec_new2 (indirect for ___cxa_vec_new2)
+I ___cxa_vec_new3 (indirect for ___cxa_vec_new3)
+I ___dynamic_cast (indirect for ___dynamic_cast)
+I ___gxx_personality_v0 (indirect for ___gxx_personality_v0)
+T ___muloti4
diff --git a/libcxx/test/libcxx/abilists/x86_64-apple-darwin/abilist.sh.cpp b/libcxx/test/libcxx/abilists/x86_64-apple-darwin/abilist.sh.cpp
new file mode 100644
index 0000000000000..f8f2d6f9499d2
--- /dev/null
+++ b/libcxx/test/libcxx/abilists/x86_64-apple-darwin/abilist.sh.cpp
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FIXME: Encode whether the library was built with exceptions and check the abilist for that case as well
+
+// REQUIRES: target=x86_64-apple-darwin{{.*}}
+
+// Check that the list of symbols exported from the dylib doesn't change unexpectedly
+
+// FILE_DEPENDENCIES: expected.abilist
+
+// RUN: nm --defined-only --extern-only %{lib-dir}/libc++.so.1.0 | cut -c 18- > %t.actual.abilist
+// RUN: diff expected.abilist %t.actual.abilist
diff --git a/libcxx/test/libcxx/abilists/x86_64-apple-darwin/expected.abilist b/libcxx/test/libcxx/abilists/x86_64-apple-darwin/expected.abilist
new file mode 100644
index 0000000000000..97550cd99f7d4
--- /dev/null
+++ b/libcxx/test/libcxx/abilists/x86_64-apple-darwin/expected.abilist
@@ -0,0 +1,2294 @@
+I __ZNKSt10bad_typeid4whatEv (indirect for __ZNKSt10bad_typeid4whatEv)
+I __ZNKSt11logic_error4whatEv (indirect for __ZNKSt11logic_error4whatEv)
+T __ZNKSt12bad_any_cast4whatEv
+T __ZNKSt12experimental15fundamentals_v112bad_any_cast4whatEv
+I __ZNKSt13bad_exception4whatEv (indirect for __ZNKSt13bad_exception4whatEv)
+I __ZNKSt13runtime_error4whatEv (indirect for __ZNKSt13runtime_error4whatEv)
+T __ZNKSt16nested_exception14rethrow_nestedEv
+T __ZNKSt18bad_variant_access4whatEv
+T __ZNKSt19bad_optional_access4whatEv
+I __ZNKSt20bad_array_new_length4whatEv (indirect for __ZNKSt20bad_array_new_length4whatEv)
+T __ZNKSt29__bad_variant_access_with_msg4whatEv
+T __ZNKSt3__110__time_put8__do_putEPcRS1_PK2tmcc
+T __ZNKSt3__110__time_put8__do_putEPwRS1_PK2tmcc
+T __ZNKSt3__110error_code7messageEv
+T __ZNKSt3__110moneypunctIcLb0EE11do_groupingEv
+T __ZNKSt3__110moneypunctIcLb0EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIcLb0EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIcLb0EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIcLb0EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_thousands_sepEv
+T __ZNKSt3__110moneypunctIcLb1EE11do_groupingEv
+T __ZNKSt3__110moneypunctIcLb1EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIcLb1EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIcLb1EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIcLb1EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_thousands_sepEv
+T __ZNKSt3__110moneypunctIwLb0EE11do_groupingEv
+T __ZNKSt3__110moneypunctIwLb0EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIwLb0EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIwLb0EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIwLb0EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_thousands_sepEv
+T __ZNKSt3__110moneypunctIwLb1EE11do_groupingEv
+T __ZNKSt3__110moneypunctIwLb1EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIwLb1EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIwLb1EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIwLb1EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv
+T __ZNKSt3__112bad_weak_ptr4whatEv
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE16find_last_not_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE17find_first_not_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEcm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5rfindEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5rfindEcm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEPKc
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKc
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKcm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmRKS5_mm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE12find_last_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE13find_first_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE16find_last_not_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE17find_first_not_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE2atEm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4copyEPwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4findEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4findEwm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5rfindEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5rfindEwm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEPKw
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmPKw
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmPKwm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmRKS5_mm
+T __ZNKSt3__112ctype_bynameIcE10do_tolowerEPcPKc
+T __ZNKSt3__112ctype_bynameIcE10do_tolowerEc
+T __ZNKSt3__112ctype_bynameIcE10do_toupperEPcPKc
+T __ZNKSt3__112ctype_bynameIcE10do_toupperEc
+T __ZNKSt3__112ctype_bynameIwE10do_scan_isEjPKwS3_
+T __ZNKSt3__112ctype_bynameIwE10do_tolowerEPwPKw
+T __ZNKSt3__112ctype_bynameIwE10do_tolowerEw
+T __ZNKSt3__112ctype_bynameIwE10do_toupperEPwPKw
+T __ZNKSt3__112ctype_bynameIwE10do_toupperEw
+T __ZNKSt3__112ctype_bynameIwE11do_scan_notEjPKwS3_
+T __ZNKSt3__112ctype_bynameIwE5do_isEPKwS3_Pj
+T __ZNKSt3__112ctype_bynameIwE5do_isEjw
+T __ZNKSt3__112ctype_bynameIwE8do_widenEPKcS3_Pw
+T __ZNKSt3__112ctype_bynameIwE8do_widenEc
+T __ZNKSt3__112ctype_bynameIwE9do_narrowEPKwS3_cPc
+T __ZNKSt3__112ctype_bynameIwE9do_narrowEwc
+T __ZNKSt3__112strstreambuf6pcountEv
+T __ZNKSt3__113random_device7entropyEv
+T __ZNKSt3__114__codecvt_utf8IDiE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__114__codecvt_utf8IDiE11do_encodingEv
+T __ZNKSt3__114__codecvt_utf8IDiE13do_max_lengthEv
+T __ZNKSt3__114__codecvt_utf8IDiE16do_always_noconvEv
+T __ZNKSt3__114__codecvt_utf8IDiE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDiE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDiE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__114__codecvt_utf8IDsE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__114__codecvt_utf8IDsE11do_encodingEv
+T __ZNKSt3__114__codecvt_utf8IDsE13do_max_lengthEv
+T __ZNKSt3__114__codecvt_utf8IDsE16do_always_noconvEv
+T __ZNKSt3__114__codecvt_utf8IDsE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDsE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDsE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__114__codecvt_utf8IwE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__114__codecvt_utf8IwE11do_encodingEv
+T __ZNKSt3__114__codecvt_utf8IwE13do_max_lengthEv
+T __ZNKSt3__114__codecvt_utf8IwE16do_always_noconvEv
+T __ZNKSt3__114__codecvt_utf8IwE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IwE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IwE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__114collate_bynameIcE10do_compareEPKcS3_S3_S3_
+T __ZNKSt3__114collate_bynameIcE12do_transformEPKcS3_
+T __ZNKSt3__114collate_bynameIwE10do_compareEPKwS3_S3_S3_
+T __ZNKSt3__114collate_bynameIwE12do_transformEPKwS3_
+T __ZNKSt3__114error_category10equivalentERKNS_10error_codeEi
+T __ZNKSt3__114error_category10equivalentEiRKNS_15error_conditionE
+T __ZNKSt3__114error_category23default_error_conditionEi
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IwLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IwLb0EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IwLb0EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IwLb0EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IwLb0EE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb0EE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IwLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IwLb1EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IwLb1EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IwLb1EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IwLb1EE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb1EE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115basic_streambufIcNS_11char_traitsIcEEE6getlocEv
+T __ZNKSt3__115basic_streambufIwNS_11char_traitsIwEEE6getlocEv
+T __ZNKSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strEv
+T __ZNKSt3__115error_condition7messageEv
+T __ZNKSt3__117bad_function_call4whatEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_thousands_sepEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_thousands_sepEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_thousands_sepEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_thousands_sepEv
+T __ZNKSt3__118__time_get_storageIcE15__do_date_orderEv
+T __ZNKSt3__118__time_get_storageIwE15__do_date_orderEv
+T __ZNKSt3__119__shared_weak_count13__get_deleterERKSt9type_info
+T __ZNKSt3__119bad_expected_accessIvE4whatEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE11do_encodingEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE13do_max_lengthEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE16do_always_noconvEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE11do_encodingEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE13do_max_lengthEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE16do_always_noconvEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__120__codecvt_utf8_utf16IwE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__120__codecvt_utf8_utf16IwE11do_encodingEv
+T __ZNKSt3__120__codecvt_utf8_utf16IwE13do_max_lengthEv
+T __ZNKSt3__120__codecvt_utf8_utf16IwE16do_always_noconvEv
+T __ZNKSt3__120__codecvt_utf8_utf16IwE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IwE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IwE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__120__time_get_c_storageIcE3__XEv
+T __ZNKSt3__120__time_get_c_storageIcE3__cEv
+T __ZNKSt3__120__time_get_c_storageIcE3__rEv
+T __ZNKSt3__120__time_get_c_storageIcE3__xEv
+T __ZNKSt3__120__time_get_c_storageIcE7__am_pmEv
+T __ZNKSt3__120__time_get_c_storageIcE7__weeksEv
+T __ZNKSt3__120__time_get_c_storageIcE8__monthsEv
+T __ZNKSt3__120__time_get_c_storageIwE3__XEv
+T __ZNKSt3__120__time_get_c_storageIwE3__cEv
+T __ZNKSt3__120__time_get_c_storageIwE3__rEv
+T __ZNKSt3__120__time_get_c_storageIwE3__xEv
+T __ZNKSt3__120__time_get_c_storageIwE7__am_pmEv
+T __ZNKSt3__120__time_get_c_storageIwE7__weeksEv
+T __ZNKSt3__120__time_get_c_storageIwE8__monthsEv
+T __ZNKSt3__120__vector_base_commonILb1EE20__throw_length_errorEv
+T __ZNKSt3__120__vector_base_commonILb1EE20__throw_out_of_rangeEv
+T __ZNKSt3__121__basic_string_commonILb1EE20__throw_length_errorEv
+T __ZNKSt3__121__basic_string_commonILb1EE20__throw_out_of_rangeEv
+T __ZNKSt3__123__match_any_but_newlineIcE6__execERNS_7__stateIcEE
+T __ZNKSt3__123__match_any_but_newlineIwE6__execERNS_7__stateIwEE
+T __ZNKSt3__13pmr26synchronized_pool_resource11do_is_equalERKNS0_15memory_resourceE
+T __ZNKSt3__13pmr28unsynchronized_pool_resource12__pool_indexEmm
+T __ZNKSt3__13pmr28unsynchronized_pool_resource17__pool_block_sizeEi
+T __ZNKSt3__13pmr28unsynchronized_pool_resource22__log2_pool_block_sizeEi
+T __ZNKSt3__13pmr28unsynchronized_pool_resource7optionsEv
+T __ZNKSt3__14__fs10filesystem18directory_iterator13__dereferenceEv
+T __ZNKSt3__14__fs10filesystem28recursive_directory_iterator13__dereferenceEv
+T __ZNKSt3__14__fs10filesystem28recursive_directory_iterator5depthEv
+T __ZNKSt3__14__fs10filesystem28recursive_directory_iterator7optionsEv
+T __ZNKSt3__14__fs10filesystem4path10__filenameEv
+T __ZNKSt3__14__fs10filesystem4path11__extensionEv
+T __ZNKSt3__14__fs10filesystem4path11__root_nameEv
+T __ZNKSt3__14__fs10filesystem4path13__parent_pathEv
+T __ZNKSt3__14__fs10filesystem4path15__relative_pathEv
+T __ZNKSt3__14__fs10filesystem4path15__root_path_rawEv
+T __ZNKSt3__14__fs10filesystem4path16__root_directoryEv
+T __ZNKSt3__14__fs10filesystem4path16lexically_normalEv
+T __ZNKSt3__14__fs10filesystem4path18lexically_relativeERKS2_
+T __ZNKSt3__14__fs10filesystem4path3endEv
+T __ZNKSt3__14__fs10filesystem4path5beginEv
+T __ZNKSt3__14__fs10filesystem4path6__stemEv
+T __ZNKSt3__14__fs10filesystem4path9__compareENS_17basic_string_viewIcNS_11char_traitsIcEEEE
+T __ZNKSt3__15ctypeIcE10do_tolowerEPcPKc
+T __ZNKSt3__15ctypeIcE10do_tolowerEc
+T __ZNKSt3__15ctypeIcE10do_toupperEPcPKc
+T __ZNKSt3__15ctypeIcE10do_toupperEc
+T __ZNKSt3__15ctypeIcE8do_widenEPKcS3_Pc
+T __ZNKSt3__15ctypeIcE8do_widenEc
+T __ZNKSt3__15ctypeIcE9do_narrowEPKcS3_cPc
+T __ZNKSt3__15ctypeIcE9do_narrowEcc
+T __ZNKSt3__15ctypeIwE10do_scan_isEjPKwS3_
+T __ZNKSt3__15ctypeIwE10do_tolowerEPwPKw
+T __ZNKSt3__15ctypeIwE10do_tolowerEw
+T __ZNKSt3__15ctypeIwE10do_toupperEPwPKw
+T __ZNKSt3__15ctypeIwE10do_toupperEw
+T __ZNKSt3__15ctypeIwE11do_scan_notEjPKwS3_
+T __ZNKSt3__15ctypeIwE5do_isEPKwS3_Pj
+T __ZNKSt3__15ctypeIwE5do_isEjw
+T __ZNKSt3__15ctypeIwE8do_widenEPKcS3_Pw
+T __ZNKSt3__15ctypeIwE8do_widenEc
+T __ZNKSt3__15ctypeIwE9do_narrowEPKwS3_cPc
+T __ZNKSt3__15ctypeIwE9do_narrowEwc
+T __ZNKSt3__16locale4nameEv
+T __ZNKSt3__16locale9has_facetERNS0_2idE
+T __ZNKSt3__16locale9use_facetERNS0_2idE
+T __ZNKSt3__16localeeqERKS0_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE10do_unshiftERS1_PDuS4_RS4_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE5do_inERS1_PKDuS5_RS5_PDiS7_RS7_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE6do_outERS1_PKDiS5_RS5_PDuS7_RS7_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE9do_lengthERS1_PKDuS5_m
+T __ZNKSt3__17codecvtIDic11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIDic11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDic11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDic11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDic11__mbstate_tE5do_inERS1_PKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__17codecvtIDic11__mbstate_tE6do_outERS1_PKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIDic11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE10do_unshiftERS1_PDuS4_RS4_
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE5do_inERS1_PKDuS5_RS5_PDsS7_RS7_
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE6do_outERS1_PKDsS5_RS5_PDuS7_RS7_
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE9do_lengthERS1_PKDuS5_m
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE5do_inERS1_PKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE6do_outERS1_PKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17codecvtIcc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIcc11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIcc11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIcc11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIcc11__mbstate_tE5do_inERS1_PKcS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIcc11__mbstate_tE6do_outERS1_PKcS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIcc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17codecvtIwc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIwc11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIwc11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIwc11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIwc11__mbstate_tE5do_inERS1_PKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__17codecvtIwc11__mbstate_tE6do_outERS1_PKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIwc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17collateIcE10do_compareEPKcS3_S3_S3_
+T __ZNKSt3__17collateIcE12do_transformEPKcS3_
+T __ZNKSt3__17collateIcE7do_hashEPKcS3_
+T __ZNKSt3__17collateIwE10do_compareEPKwS3_S3_S3_
+T __ZNKSt3__17collateIwE12do_transformEPKwS3_
+T __ZNKSt3__17collateIwE7do_hashEPKwS3_
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRPv
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRb
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRd
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRe
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRf
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRl
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRm
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRt
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRx
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRy
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjS8_
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRPv
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRb
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRd
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRe
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRf
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRl
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRm
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRt
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRx
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRy
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjS8_
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcPKv
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcb
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcd
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEce
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcl
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcm
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcx
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcy
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPKv
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwb
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwd
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwe
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwl
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwm
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwx
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwy
+T __ZNKSt3__18ios_base6getlocEv
+T __ZNKSt3__18messagesIcE6do_getEliiRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNKSt3__18messagesIcE7do_openERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_6localeE
+T __ZNKSt3__18messagesIcE8do_closeEl
+T __ZNKSt3__18messagesIwE6do_getEliiRKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEE
+T __ZNKSt3__18messagesIwE7do_openERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_6localeE
+T __ZNKSt3__18messagesIwE8do_closeEl
+T __ZNKSt3__18numpunctIcE11do_groupingEv
+T __ZNKSt3__18numpunctIcE11do_truenameEv
+T __ZNKSt3__18numpunctIcE12do_falsenameEv
+T __ZNKSt3__18numpunctIcE16do_decimal_pointEv
+T __ZNKSt3__18numpunctIcE16do_thousands_sepEv
+T __ZNKSt3__18numpunctIwE11do_groupingEv
+T __ZNKSt3__18numpunctIwE11do_truenameEv
+T __ZNKSt3__18numpunctIwE12do_falsenameEv
+T __ZNKSt3__18numpunctIwE16do_decimal_pointEv
+T __ZNKSt3__18numpunctIwE16do_thousands_sepEv
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE10__get_hourERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE10__get_yearERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_am_pmERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_monthERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_year4ERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_dateES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_timeES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_yearES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE12__get_minuteERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE12__get_secondERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_12_hourERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_percentERS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_weekdayERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13do_date_orderEv
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE14do_get_weekdayES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE15__get_monthnameERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE16do_get_monthnameES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE17__get_weekdaynameERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE17__get_white_spaceERS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE18__get_day_year_numERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE3getES4_S4_RNS_8ios_baseERjP2tmPKcSC_
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjP2tmcc
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE9__get_dayERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE10__get_hourERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE10__get_yearERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_am_pmERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_monthERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_year4ERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_dateES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_timeES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_yearES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE12__get_minuteERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE12__get_secondERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_12_hourERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_percentERS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_weekdayERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13do_date_orderEv
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE14do_get_weekdayES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE15__get_monthnameERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE16do_get_monthnameES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE17__get_weekdaynameERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE17__get_white_spaceERS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE18__get_day_year_numERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE3getES4_S4_RNS_8ios_baseERjP2tmPKwSC_
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjP2tmcc
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE9__get_dayERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE3putES4_RNS_8ios_baseEcPK2tmPKcSC_
+T __ZNKSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcPK2tmcc
+T __ZNKSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE3putES4_RNS_8ios_baseEwPK2tmPKwSC_
+T __ZNKSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPK2tmcc
+T __ZNKSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_bRNS_8ios_baseERjRNS_12basic_stringIcS3_NS_9allocatorIcEEEE
+T __ZNKSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_bRNS_8ios_baseERjRe
+T __ZNKSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_bRNS_8ios_baseERjRNS_12basic_stringIwS3_NS_9allocatorIwEEEE
+T __ZNKSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_bRNS_8ios_baseERjRe
+T __ZNKSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_bRNS_8ios_baseEcRKNS_12basic_stringIcS3_NS_9allocatorIcEEEE
+T __ZNKSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_bRNS_8ios_baseEce
+T __ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwRKNS_12basic_stringIwS3_NS_9allocatorIwEEEE
+T __ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwe
+I __ZNKSt8bad_cast4whatEv (indirect for __ZNKSt8bad_cast4whatEv)
+I __ZNKSt9bad_alloc4whatEv (indirect for __ZNKSt9bad_alloc4whatEv)
+I __ZNKSt9exception4whatEv (indirect for __ZNKSt9exception4whatEv)
+I __ZNSt10bad_typeidC1Ev (indirect for __ZNSt10bad_typeidC1Ev)
+I __ZNSt10bad_typeidC2Ev (indirect for __ZNSt10bad_typeidC2Ev)
+I __ZNSt10bad_typeidD0Ev (indirect for __ZNSt10bad_typeidD0Ev)
+I __ZNSt10bad_typeidD1Ev (indirect for __ZNSt10bad_typeidD1Ev)
+I __ZNSt10bad_typeidD2Ev (indirect for __ZNSt10bad_typeidD2Ev)
+T __ZNSt11logic_errorC1EPKc
+T __ZNSt11logic_errorC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt11logic_errorC1ERKS_
+T __ZNSt11logic_errorC2EPKc
+T __ZNSt11logic_errorC2ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt11logic_errorC2ERKS_
+I __ZNSt11logic_errorD0Ev (indirect for __ZNSt11logic_errorD0Ev)
+I __ZNSt11logic_errorD1Ev (indirect for __ZNSt11logic_errorD1Ev)
+I __ZNSt11logic_errorD2Ev (indirect for __ZNSt11logic_errorD2Ev)
+T __ZNSt11logic_erroraSERKS_
+I __ZNSt11range_errorD0Ev (indirect for __ZNSt11range_errorD0Ev)
+I __ZNSt11range_errorD1Ev (indirect for __ZNSt11range_errorD1Ev)
+I __ZNSt11range_errorD2Ev (indirect for __ZNSt11range_errorD2Ev)
+I __ZNSt12domain_errorD0Ev (indirect for __ZNSt12domain_errorD0Ev)
+I __ZNSt12domain_errorD1Ev (indirect for __ZNSt12domain_errorD1Ev)
+I __ZNSt12domain_errorD2Ev (indirect for __ZNSt12domain_errorD2Ev)
+T __ZNSt12experimental19bad_optional_accessD0Ev
+T __ZNSt12experimental19bad_optional_accessD1Ev
+T __ZNSt12experimental19bad_optional_accessD2Ev
+I __ZNSt12length_errorD0Ev (indirect for __ZNSt12length_errorD0Ev)
+I __ZNSt12length_errorD1Ev (indirect for __ZNSt12length_errorD1Ev)
+I __ZNSt12length_errorD2Ev (indirect for __ZNSt12length_errorD2Ev)
+I __ZNSt12out_of_rangeD0Ev (indirect for __ZNSt12out_of_rangeD0Ev)
+I __ZNSt12out_of_rangeD1Ev (indirect for __ZNSt12out_of_rangeD1Ev)
+I __ZNSt12out_of_rangeD2Ev (indirect for __ZNSt12out_of_rangeD2Ev)
+I __ZNSt13bad_exceptionD0Ev (indirect for __ZNSt13bad_exceptionD0Ev)
+I __ZNSt13bad_exceptionD1Ev (indirect for __ZNSt13bad_exceptionD1Ev)
+I __ZNSt13bad_exceptionD2Ev (indirect for __ZNSt13bad_exceptionD2Ev)
+T __ZNSt13exception_ptr31__from_native_exception_pointerEPv
+T __ZNSt13exception_ptrC1ERKS_
+T __ZNSt13exception_ptrC2ERKS_
+T __ZNSt13exception_ptrD1Ev
+T __ZNSt13exception_ptrD2Ev
+T __ZNSt13exception_ptraSERKS_
+T __ZNSt13runtime_errorC1EPKc
+T __ZNSt13runtime_errorC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt13runtime_errorC1ERKS_
+T __ZNSt13runtime_errorC2EPKc
+T __ZNSt13runtime_errorC2ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt13runtime_errorC2ERKS_
+I __ZNSt13runtime_errorD0Ev (indirect for __ZNSt13runtime_errorD0Ev)
+I __ZNSt13runtime_errorD1Ev (indirect for __ZNSt13runtime_errorD1Ev)
+I __ZNSt13runtime_errorD2Ev (indirect for __ZNSt13runtime_errorD2Ev)
+T __ZNSt13runtime_erroraSERKS_
+I __ZNSt14overflow_errorD0Ev (indirect for __ZNSt14overflow_errorD0Ev)
+I __ZNSt14overflow_errorD1Ev (indirect for __ZNSt14overflow_errorD1Ev)
+I __ZNSt14overflow_errorD2Ev (indirect for __ZNSt14overflow_errorD2Ev)
+I __ZNSt15underflow_errorD0Ev (indirect for __ZNSt15underflow_errorD0Ev)
+I __ZNSt15underflow_errorD1Ev (indirect for __ZNSt15underflow_errorD1Ev)
+I __ZNSt15underflow_errorD2Ev (indirect for __ZNSt15underflow_errorD2Ev)
+I __ZNSt16invalid_argumentD0Ev (indirect for __ZNSt16invalid_argumentD0Ev)
+I __ZNSt16invalid_argumentD1Ev (indirect for __ZNSt16invalid_argumentD1Ev)
+I __ZNSt16invalid_argumentD2Ev (indirect for __ZNSt16invalid_argumentD2Ev)
+T __ZNSt16nested_exceptionC1Ev
+T __ZNSt16nested_exceptionC2Ev
+T __ZNSt16nested_exceptionD0Ev
+T __ZNSt16nested_exceptionD1Ev
+T __ZNSt16nested_exceptionD2Ev
+T __ZNSt19bad_optional_accessD0Ev
+T __ZNSt19bad_optional_accessD1Ev
+T __ZNSt19bad_optional_accessD2Ev
+I __ZNSt20bad_array_new_lengthC1Ev (indirect for __ZNSt20bad_array_new_lengthC1Ev)
+I __ZNSt20bad_array_new_lengthC2Ev (indirect for __ZNSt20bad_array_new_lengthC2Ev)
+I __ZNSt20bad_array_new_lengthD0Ev (indirect for __ZNSt20bad_array_new_lengthD0Ev)
+I __ZNSt20bad_array_new_lengthD1Ev (indirect for __ZNSt20bad_array_new_lengthD1Ev)
+I __ZNSt20bad_array_new_lengthD2Ev (indirect for __ZNSt20bad_array_new_lengthD2Ev)
+T __ZNSt3__110__time_getC1EPKc
+T __ZNSt3__110__time_getC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_getC2EPKc
+T __ZNSt3__110__time_getC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_getD1Ev
+T __ZNSt3__110__time_getD2Ev
+T __ZNSt3__110__time_putC1EPKc
+T __ZNSt3__110__time_putC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_putC2EPKc
+T __ZNSt3__110__time_putC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_putD1Ev
+T __ZNSt3__110__time_putD2Ev
+S __ZNSt3__110ctype_base5alnumE
+S __ZNSt3__110ctype_base5alphaE
+S __ZNSt3__110ctype_base5blankE
+S __ZNSt3__110ctype_base5cntrlE
+S __ZNSt3__110ctype_base5digitE
+S __ZNSt3__110ctype_base5graphE
+S __ZNSt3__110ctype_base5lowerE
+S __ZNSt3__110ctype_base5printE
+S __ZNSt3__110ctype_base5punctE
+S __ZNSt3__110ctype_base5spaceE
+S __ZNSt3__110ctype_base5upperE
+S __ZNSt3__110ctype_base6xdigitE
+T __ZNSt3__110istrstreamD0Ev
+T __ZNSt3__110istrstreamD1Ev
+T __ZNSt3__110istrstreamD2Ev
+D __ZNSt3__110moneypunctIcLb0EE2idE
+S __ZNSt3__110moneypunctIcLb0EE4intlE
+D __ZNSt3__110moneypunctIcLb1EE2idE
+S __ZNSt3__110moneypunctIcLb1EE4intlE
+D __ZNSt3__110moneypunctIwLb0EE2idE
+S __ZNSt3__110moneypunctIwLb0EE4intlE
+D __ZNSt3__110moneypunctIwLb1EE2idE
+S __ZNSt3__110moneypunctIwLb1EE4intlE
+T __ZNSt3__110ostrstreamD0Ev
+T __ZNSt3__110ostrstreamD1Ev
+T __ZNSt3__110ostrstreamD2Ev
+T __ZNSt3__110to_wstringEd
+T __ZNSt3__110to_wstringEe
+T __ZNSt3__110to_wstringEf
+T __ZNSt3__110to_wstringEi
+T __ZNSt3__110to_wstringEj
+T __ZNSt3__110to_wstringEl
+T __ZNSt3__110to_wstringEm
+T __ZNSt3__110to_wstringEx
+T __ZNSt3__110to_wstringEy
+T __ZNSt3__111__call_onceERVmPvPFvS2_E
+T __ZNSt3__111__money_getIcE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_SF_Ri
+T __ZNSt3__111__money_getIwE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_SJ_Ri
+T __ZNSt3__111__money_putIcE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_Ri
+T __ZNSt3__111__money_putIcE8__formatEPcRS2_S3_jPKcS5_RKNS_5ctypeIcEEbRKNS_10money_base7patternEccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESL_SL_i
+T __ZNSt3__111__money_putIwE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_Ri
+T __ZNSt3__111__money_putIwE8__formatEPwRS2_S3_jPKwS5_RKNS_5ctypeIwEEbRKNS_10money_base7patternEwwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNSE_IwNSF_IwEENSH_IwEEEESQ_i
+T __ZNSt3__111regex_errorC1ENS_15regex_constants10error_typeE
+T __ZNSt3__111regex_errorC2ENS_15regex_constants10error_typeE
+T __ZNSt3__111regex_errorD0Ev
+T __ZNSt3__111regex_errorD1Ev
+T __ZNSt3__111regex_errorD2Ev
+T __ZNSt3__111this_thread9sleep_forERKNS_6chrono8durationIxNS_5ratioILl1ELl1000000000EEEEE
+T __ZNSt3__111timed_mutex4lockEv
+T __ZNSt3__111timed_mutex6unlockEv
+T __ZNSt3__111timed_mutex8try_lockEv
+T __ZNSt3__111timed_mutexC1Ev
+T __ZNSt3__111timed_mutexC2Ev
+T __ZNSt3__111timed_mutexD1Ev
+T __ZNSt3__111timed_mutexD2Ev
+T __ZNSt3__112__do_nothingEPv
+T __ZNSt3__112__get_sp_mutEPKv
+T __ZNSt3__112__next_primeEm
+S __ZNSt3__112__rs_default4__c_E
+T __ZNSt3__112__rs_defaultC1ERKS0_
+T __ZNSt3__112__rs_defaultC1Ev
+T __ZNSt3__112__rs_defaultC2ERKS0_
+T __ZNSt3__112__rs_defaultC2Ev
+T __ZNSt3__112__rs_defaultD1Ev
+T __ZNSt3__112__rs_defaultD2Ev
+T __ZNSt3__112__rs_defaultclEv
+T __ZNSt3__112bad_weak_ptrD0Ev
+T __ZNSt3__112bad_weak_ptrD1Ev
+T __ZNSt3__112bad_weak_ptrD2Ev
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE21__grow_by_and_replaceEmmmmmmPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm
+S __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4nposE
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5eraseEmm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcmm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendERKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignERKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertENS_11__wrap_iterIPKcEEc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmRKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6resizeEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmRKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEmmmmmm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_RKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_RKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSERKS5_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEc
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE21__grow_by_and_replaceEmmmmmmPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE2atEm
+S __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4nposE
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5eraseEmm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEPKwmm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendERKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignERKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertENS_11__wrap_iterIPKwEEw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmRKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6resizeEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmRKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7reserveEm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9__grow_byEmmmmmm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9push_backEw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_RKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_RKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED1Ev
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED2Ev
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEaSERKS5_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEaSEw
+T __ZNSt3__112ctype_bynameIcEC1EPKcm
+T __ZNSt3__112ctype_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIcEC2EPKcm
+T __ZNSt3__112ctype_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIcED0Ev
+T __ZNSt3__112ctype_bynameIcED1Ev
+T __ZNSt3__112ctype_bynameIcED2Ev
+T __ZNSt3__112ctype_bynameIwEC1EPKcm
+T __ZNSt3__112ctype_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIwEC2EPKcm
+T __ZNSt3__112ctype_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIwED0Ev
+T __ZNSt3__112ctype_bynameIwED1Ev
+T __ZNSt3__112ctype_bynameIwED2Ev
+T __ZNSt3__112future_errorC1ENS_10error_codeE
+T __ZNSt3__112future_errorC2ENS_10error_codeE
+T __ZNSt3__112future_errorD0Ev
+T __ZNSt3__112future_errorD1Ev
+T __ZNSt3__112future_errorD2Ev
+S __ZNSt3__112placeholders2_1E
+S __ZNSt3__112placeholders2_2E
+S __ZNSt3__112placeholders2_3E
+S __ZNSt3__112placeholders2_4E
+S __ZNSt3__112placeholders2_5E
+S __ZNSt3__112placeholders2_6E
+S __ZNSt3__112placeholders2_7E
+S __ZNSt3__112placeholders2_8E
+S __ZNSt3__112placeholders2_9E
+S __ZNSt3__112placeholders3_10E
+T __ZNSt3__112strstreambuf3strEv
+T __ZNSt3__112strstreambuf4swapERS0_
+T __ZNSt3__112strstreambuf6__initEPclS1_
+T __ZNSt3__112strstreambuf6freezeEb
+T __ZNSt3__112strstreambuf7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__112strstreambuf7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__112strstreambuf8overflowEi
+T __ZNSt3__112strstreambuf9pbackfailEi
+T __ZNSt3__112strstreambuf9underflowEv
+T __ZNSt3__112strstreambufC1EPFPvmEPFvS1_E
+T __ZNSt3__112strstreambufC1EPKal
+T __ZNSt3__112strstreambufC1EPKcl
+T __ZNSt3__112strstreambufC1EPKhl
+T __ZNSt3__112strstreambufC1EPalS1_
+T __ZNSt3__112strstreambufC1EPclS1_
+T __ZNSt3__112strstreambufC1EPhlS1_
+T __ZNSt3__112strstreambufC1El
+T __ZNSt3__112strstreambufC2EPFPvmEPFvS1_E
+T __ZNSt3__112strstreambufC2EPKal
+T __ZNSt3__112strstreambufC2EPKcl
+T __ZNSt3__112strstreambufC2EPKhl
+T __ZNSt3__112strstreambufC2EPalS1_
+T __ZNSt3__112strstreambufC2EPclS1_
+T __ZNSt3__112strstreambufC2EPhlS1_
+T __ZNSt3__112strstreambufC2El
+T __ZNSt3__112strstreambufD0Ev
+T __ZNSt3__112strstreambufD1Ev
+T __ZNSt3__112strstreambufD2Ev
+T __ZNSt3__112system_errorC1ENS_10error_codeE
+T __ZNSt3__112system_errorC1ENS_10error_codeEPKc
+T __ZNSt3__112system_errorC1ENS_10error_codeERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorC1EiRKNS_14error_categoryE
+T __ZNSt3__112system_errorC1EiRKNS_14error_categoryEPKc
+T __ZNSt3__112system_errorC1EiRKNS_14error_categoryERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorC2ENS_10error_codeE
+T __ZNSt3__112system_errorC2ENS_10error_codeEPKc
+T __ZNSt3__112system_errorC2ENS_10error_codeERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorC2EiRKNS_14error_categoryE
+T __ZNSt3__112system_errorC2EiRKNS_14error_categoryEPKc
+T __ZNSt3__112system_errorC2EiRKNS_14error_categoryERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorD0Ev
+T __ZNSt3__112system_errorD1Ev
+T __ZNSt3__112system_errorD2Ev
+T __ZNSt3__113__hash_memoryEPKvm
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE11__read_modeEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE12__write_modeEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4openEPKcj
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4syncEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5closeEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE6setbufEPcl
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE8overflowEi
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9pbackfailEi
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9underflowEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC1EOS3_
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC1Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2EOS3_
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEPclc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERNS_15basic_streambufIcS2_EEc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4peekEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4readEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4syncEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5seekgENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5seekgExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5tellgEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5ungetEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6ignoreEli
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6sentryC1ERS3_b
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6sentryC2ERS3_b
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7getlineEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7getlineEPclc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7putbackEc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE8readsomeEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRNS_9basic_iosIcS2_EES6_E
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRS3_S4_E
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERPv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERb
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERd
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERe
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERf
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERi
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERj
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERm
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERs
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERt
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERx
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERy
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEPwlw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERNS_15basic_streambufIwS2_EEw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4peekEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4readEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4swapERS3_
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4syncEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5seekgENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5seekgExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5tellgEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5ungetEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6ignoreEli
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6sentryC1ERS3_b
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6sentryC2ERS3_b
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7getlineEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7getlineEPwlw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7putbackEw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE8readsomeEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEEC1EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEEC2EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED2Ev
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRNS_9basic_iosIwS2_EES6_E
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRS3_S4_E
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERPv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERb
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERd
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERe
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERf
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERi
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERj
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERm
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERs
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERt
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERx
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERy
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE3putEc
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5seekpENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5seekpExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5tellpEv
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5writeEPKcl
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC2ERS3_
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD2Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRNS_9basic_iosIcS2_EES6_E
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRS3_S4_E
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPKv
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEb
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEd
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEe
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEf
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEi
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEj
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEl
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEm
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEs
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEt
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEx
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEy
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE3putEw
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE4swapERS3_
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5flushEv
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5seekpENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5seekpExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5tellpEv
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5writeEPKwl
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryC1ERS3_
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryC2ERS3_
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryD1Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryD2Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEEC1EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEEC2EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED2Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRNS_9basic_iosIwS2_EES6_E
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRS3_S4_E
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPKv
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEb
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEd
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEe
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEf
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEi
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEj
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEl
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEm
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEs
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEt
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEx
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEy
+T __ZNSt3__113random_deviceC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__113random_deviceC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__113random_deviceD1Ev
+T __ZNSt3__113random_deviceD2Ev
+T __ZNSt3__113random_deviceclEv
+T __ZNSt3__113shared_futureIvED1Ev
+T __ZNSt3__113shared_futureIvED2Ev
+T __ZNSt3__113shared_futureIvEaSERKS1_
+T __ZNSt3__114__num_get_base10__get_baseERNS_8ios_baseE
+S __ZNSt3__114__num_get_base5__srcE
+T __ZNSt3__114__num_put_base12__format_intEPcPKcbj
+T __ZNSt3__114__num_put_base14__format_floatEPcPKcj
+T __ZNSt3__114__num_put_base18__identify_paddingEPcS1_RKNS_8ios_baseE
+T __ZNSt3__114__shared_count12__add_sharedEv
+T __ZNSt3__114__shared_count16__release_sharedEv
+T __ZNSt3__114__shared_countD0Ev
+T __ZNSt3__114__shared_countD1Ev
+T __ZNSt3__114__shared_countD2Ev
+T __ZNSt3__114basic_ifstreamIcNS_11char_traitsIcEEE4openEPKcj
+T __ZNSt3__114basic_ifstreamIcNS_11char_traitsIcEEE4openERKNS_12basic_stringIcS2_NS_9allocatorIcEEEEj
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__114basic_ofstreamIcNS_11char_traitsIcEEE4openEPKcj
+T __ZNSt3__114basic_ofstreamIcNS_11char_traitsIcEEE4openERKNS_12basic_stringIcS2_NS_9allocatorIcEEEEj
+T __ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIDic11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDic11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDic11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIDsc11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDsc11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDsc11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIcc11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIcc11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIcc11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIwc11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIwc11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIwc11__mbstate_tED2Ev
+T __ZNSt3__114collate_bynameIcEC1EPKcm
+T __ZNSt3__114collate_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIcEC2EPKcm
+T __ZNSt3__114collate_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIcED0Ev
+T __ZNSt3__114collate_bynameIcED1Ev
+T __ZNSt3__114collate_bynameIcED2Ev
+T __ZNSt3__114collate_bynameIwEC1EPKcm
+T __ZNSt3__114collate_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIwEC2EPKcm
+T __ZNSt3__114collate_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIwED0Ev
+T __ZNSt3__114collate_bynameIwED1Ev
+T __ZNSt3__114collate_bynameIwED2Ev
+T __ZNSt3__114error_categoryC2Ev
+T __ZNSt3__114error_categoryD0Ev
+T __ZNSt3__114error_categoryD1Ev
+T __ZNSt3__114error_categoryD2Ev
+T __ZNSt3__115__get_classnameEPKcb
+T __ZNSt3__115__thread_struct25notify_all_at_thread_exitEPNS_18condition_variableEPNS_5mutexE
+T __ZNSt3__115__thread_struct27__make_ready_at_thread_exitEPNS_17__assoc_sub_stateE
+T __ZNSt3__115__thread_structC1Ev
+T __ZNSt3__115__thread_structC2Ev
+T __ZNSt3__115__thread_structD1Ev
+T __ZNSt3__115__thread_structD2Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE10pubseekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE10pubseekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4setgEPcS4_S4_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4setpEPcS4_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4syncEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5gbumpEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5pbumpEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sgetcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sgetnEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputcEc
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputnEPKcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5uflowEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6sbumpcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6setbufEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6snextcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsgetnEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsputnEPKcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7pubsyncEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7sungetcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8in_availEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8overflowEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8pubimbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9pbackfailEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9pubsetbufEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9showmanycEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9sputbackcEc
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9underflowEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC1ERKS3_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC1Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2ERKS3_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEaSERKS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE10pubseekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE10pubseekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4setgEPwS4_S4_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4setpEPwS4_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4swapERS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4syncEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5gbumpEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5imbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5pbumpEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sgetcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sgetnEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sputcEw
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sputnEPKwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5uflowEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6sbumpcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6setbufEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6snextcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6xsgetnEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6xsputnEPKwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7pubsyncEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7sungetcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8in_availEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8overflowEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8pubimbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9pbackfailEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9pubsetbufEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9showmanycEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9sputbackcEw
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9underflowEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC1ERKS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC1Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC2ERKS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC2Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED2Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEaSERKS3_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strERKNS_12basic_stringIcS2_S4_EE
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE4swapERS5_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE8overflowEi
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9pbackfailEi
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9underflowEv
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEC1EOS5_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEC2EOS5_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__115future_categoryEv
+T __ZNSt3__115numpunct_bynameIcEC1EPKcm
+T __ZNSt3__115numpunct_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIcEC2EPKcm
+T __ZNSt3__115numpunct_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIcED0Ev
+T __ZNSt3__115numpunct_bynameIcED1Ev
+T __ZNSt3__115numpunct_bynameIcED2Ev
+T __ZNSt3__115numpunct_bynameIwEC1EPKcm
+T __ZNSt3__115numpunct_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIwEC2EPKcm
+T __ZNSt3__115numpunct_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIwED0Ev
+T __ZNSt3__115numpunct_bynameIwED1Ev
+T __ZNSt3__115numpunct_bynameIwED2Ev
+T __ZNSt3__115recursive_mutex4lockEv
+T __ZNSt3__115recursive_mutex6unlockEv
+T __ZNSt3__115recursive_mutex8try_lockEv
+T __ZNSt3__115recursive_mutexC1Ev
+T __ZNSt3__115recursive_mutexC2Ev
+T __ZNSt3__115recursive_mutexD1Ev
+T __ZNSt3__115recursive_mutexD2Ev
+T __ZNSt3__115system_categoryEv
+T __ZNSt3__116__check_groupingERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjS8_Rj
+T __ZNSt3__116__narrow_to_utf8ILm16EED0Ev
+T __ZNSt3__116__narrow_to_utf8ILm16EED1Ev
+T __ZNSt3__116__narrow_to_utf8ILm16EED2Ev
+T __ZNSt3__116__narrow_to_utf8ILm32EED0Ev
+T __ZNSt3__116__narrow_to_utf8ILm32EED1Ev
+T __ZNSt3__116__narrow_to_utf8ILm32EED2Ev
+T __ZNSt3__116generic_categoryEv
+T __ZNSt3__117__assoc_sub_state10__sub_waitERNS_11unique_lockINS_5mutexEEE
+T __ZNSt3__117__assoc_sub_state12__make_readyEv
+T __ZNSt3__117__assoc_sub_state13set_exceptionESt13exception_ptr
+T __ZNSt3__117__assoc_sub_state16__on_zero_sharedEv
+T __ZNSt3__117__assoc_sub_state24set_value_at_thread_exitEv
+T __ZNSt3__117__assoc_sub_state28set_exception_at_thread_exitESt13exception_ptr
+T __ZNSt3__117__assoc_sub_state4copyEv
+T __ZNSt3__117__assoc_sub_state4waitEv
+T __ZNSt3__117__assoc_sub_state9__executeEv
+T __ZNSt3__117__assoc_sub_state9set_valueEv
+T __ZNSt3__117__widen_from_utf8ILm16EED0Ev
+T __ZNSt3__117__widen_from_utf8ILm16EED1Ev
+T __ZNSt3__117__widen_from_utf8ILm16EED2Ev
+T __ZNSt3__117__widen_from_utf8ILm32EED0Ev
+T __ZNSt3__117__widen_from_utf8ILm32EED1Ev
+T __ZNSt3__117__widen_from_utf8ILm32EED2Ev
+T __ZNSt3__117bad_function_callD0Ev
+T __ZNSt3__117bad_function_callD1Ev
+T __ZNSt3__117bad_function_callD2Ev
+T __ZNSt3__117iostream_categoryEv
+T __ZNSt3__117moneypunct_bynameIcLb0EE4initEPKc
+T __ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc
+T __ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc
+T __ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc
+T __ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE
+T __ZNSt3__118__time_get_storageIcEC1EPKc
+T __ZNSt3__118__time_get_storageIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118__time_get_storageIcEC2EPKc
+T __ZNSt3__118__time_get_storageIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118__time_get_storageIwEC1EPKc
+T __ZNSt3__118__time_get_storageIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118__time_get_storageIwEC2EPKc
+T __ZNSt3__118__time_get_storageIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__118condition_variable10notify_allEv
+T __ZNSt3__118condition_variable10notify_oneEv
+T __ZNSt3__118condition_variable15__do_timed_waitERNS_11unique_lockINS_5mutexEEENS_6chrono10time_pointINS5_12system_clockENS5_8durationIxNS_5ratioILl1ELl1000000000EEEEEEE
+T __ZNSt3__118condition_variable4waitERNS_11unique_lockINS_5mutexEEE
+T __ZNSt3__118condition_variableD1Ev
+T __ZNSt3__118condition_variableD2Ev
+T __ZNSt3__118shared_timed_mutex11lock_sharedEv
+T __ZNSt3__118shared_timed_mutex13unlock_sharedEv
+T __ZNSt3__118shared_timed_mutex15try_lock_sharedEv
+T __ZNSt3__118shared_timed_mutex4lockEv
+T __ZNSt3__118shared_timed_mutex6unlockEv
+T __ZNSt3__118shared_timed_mutex8try_lockEv
+T __ZNSt3__118shared_timed_mutexC1Ev
+T __ZNSt3__118shared_timed_mutexC2Ev
+T __ZNSt3__119__is_posix_terminalEP7__sFILE
+T __ZNSt3__119__shared_mutex_base11lock_sharedEv
+T __ZNSt3__119__shared_mutex_base13unlock_sharedEv
+T __ZNSt3__119__shared_mutex_base15try_lock_sharedEv
+T __ZNSt3__119__shared_mutex_base4lockEv
+T __ZNSt3__119__shared_mutex_base6unlockEv
+T __ZNSt3__119__shared_mutex_base8try_lockEv
+T __ZNSt3__119__shared_mutex_baseC1Ev
+T __ZNSt3__119__shared_mutex_baseC2Ev
+T __ZNSt3__119__shared_weak_count10__add_weakEv
+T __ZNSt3__119__shared_weak_count12__add_sharedEv
+T __ZNSt3__119__shared_weak_count14__release_weakEv
+T __ZNSt3__119__shared_weak_count16__release_sharedEv
+T __ZNSt3__119__shared_weak_count4lockEv
+T __ZNSt3__119__shared_weak_countD0Ev
+T __ZNSt3__119__shared_weak_countD1Ev
+T __ZNSt3__119__shared_weak_countD2Ev
+T __ZNSt3__119__thread_local_dataEv
+T __ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__120__atomic_wait_nativeILm8EEEvPKvS2_
+T __ZNSt3__120__get_collation_nameEPKc
+T __ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEEx
+T __ZNSt3__120__libcpp_atomic_waitEPVKvx
+T __ZNSt3__120__throw_system_errorEiPKc
+T __ZNSt3__121__get_locale_encodingEPKc
+T __ZNSt3__121__throw_runtime_errorEPKc
+T __ZNSt3__121recursive_timed_mutex4lockEv
+T __ZNSt3__121recursive_timed_mutex6unlockEv
+T __ZNSt3__121recursive_timed_mutex8try_lockEv
+T __ZNSt3__121recursive_timed_mutexC1Ev
+T __ZNSt3__121recursive_timed_mutexC2Ev
+T __ZNSt3__121recursive_timed_mutexD1Ev
+T __ZNSt3__121recursive_timed_mutexD2Ev
+T __ZNSt3__122__libcpp_verbose_abortEPKcz
+T __ZNSt3__123__atomic_monitor_globalEPKv
+T __ZNSt3__123__cxx_atomic_notify_allEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE
+T __ZNSt3__123__cxx_atomic_notify_allEPVKv
+T __ZNSt3__123__cxx_atomic_notify_oneEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE
+T __ZNSt3__123__cxx_atomic_notify_oneEPVKv
+T __ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE
+T __ZNSt3__123__libcpp_atomic_monitorEPVKv
+T __ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE
+T __ZNSt3__126__atomic_notify_all_nativeILm8EEEvPKv
+T __ZNSt3__126__atomic_notify_one_nativeILm8EEEvPKv
+T __ZNSt3__126__atomic_wait_global_tableEPKvx
+T __ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+T __ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+T __ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh
+T __ZNSt3__132__atomic_notify_all_global_tableEPKv
+T __ZNSt3__132__atomic_notify_one_global_tableEPKv
+T __ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE
+T __ZNSt3__133__atomic_wait_native_with_timeoutILm8EEEvPKvS2_y
+T __ZNSt3__134__construct_barrier_algorithm_baseERl
+T __ZNSt3__139__atomic_wait_global_table_with_timeoutEPKvxy
+S __ZNSt3__13cinE
+T __ZNSt3__13pmr15memory_resourceD0Ev
+T __ZNSt3__13pmr15memory_resourceD1Ev
+T __ZNSt3__13pmr15memory_resourceD2Ev
+T __ZNSt3__13pmr19new_delete_resourceEv
+T __ZNSt3__13pmr20get_default_resourceEv
+T __ZNSt3__13pmr20null_memory_resourceEv
+T __ZNSt3__13pmr20set_default_resourceEPNS0_15memory_resourceE
+T __ZNSt3__13pmr25monotonic_buffer_resource11do_allocateEmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource11do_allocateEmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__do_allocateEPNS0_15memory_resourceEmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__release_ptrEPNS0_15memory_resourceE
+T __ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool15__do_deallocateEPNS0_15memory_resourceEPvmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource13do_deallocateEPvmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource7releaseEv
+T __ZNSt3__13pmr28unsynchronized_pool_resourceC1ERKNS0_12pool_optionsEPNS0_15memory_resourceE
+T __ZNSt3__13pmr28unsynchronized_pool_resourceC2ERKNS0_12pool_optionsEPNS0_15memory_resourceE
+T __ZNSt3__14__fs10filesystem10__absoluteERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem10hash_valueERKNS1_4pathE
+T __ZNSt3__14__fs10filesystem11__canonicalERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem11__copy_fileERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem11__file_sizeERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem12__equivalentERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem12__remove_allERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem13__fs_is_emptyERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem13__permissionsERKNS1_4pathENS1_5permsENS1_12perm_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem13__resize_fileERKNS1_4pathEmPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__copy_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__current_pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__current_pathERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__read_symlinkERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem15directory_entry12__do_refreshEv
+T __ZNSt3__14__fs10filesystem16_FilesystemClock3nowEv
+S __ZNSt3__14__fs10filesystem16_FilesystemClock9is_steadyE
+T __ZNSt3__14__fs10filesystem16__create_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem16__symlink_statusERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem16filesystem_error13__create_whatEi
+T __ZNSt3__14__fs10filesystem16filesystem_errorD0Ev
+T __ZNSt3__14__fs10filesystem16filesystem_errorD1Ev
+T __ZNSt3__14__fs10filesystem16filesystem_errorD2Ev
+T __ZNSt3__14__fs10filesystem17__hard_link_countERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem17__last_write_timeERKNS1_4pathENS_6chrono10time_pointINS1_16_FilesystemClockENS5_8durationInNS_5ratioILl1ELl1000000000EEEEEEEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem17__last_write_timeERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__create_directoryERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__create_directoryERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__create_hard_linkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__weakly_canonicalERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18directory_iterator11__incrementEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18directory_iteratorC1ERKNS1_4pathEPNS_10error_codeENS1_17directory_optionsE
+T __ZNSt3__14__fs10filesystem18directory_iteratorC2ERKNS1_4pathEPNS_10error_codeENS1_17directory_optionsE
+T __ZNSt3__14__fs10filesystem20__create_directoriesERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem21__temp_directory_pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem26__create_directory_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator11__incrementEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator15__try_recursionEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator5__popEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator9__advanceEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iteratorC1ERKNS1_4pathENS1_17directory_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iteratorC2ERKNS1_4pathENS1_17directory_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem4path17replace_extensionERKS2_
+S __ZNSt3__14__fs10filesystem4path19preferred_separatorE
+T __ZNSt3__14__fs10filesystem4path8iterator11__decrementEv
+T __ZNSt3__14__fs10filesystem4path8iterator11__incrementEv
+T __ZNSt3__14__fs10filesystem6__copyERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem7__spaceERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem8__statusERKNS1_4pathEPNS_10error_codeE
+S __ZNSt3__14cerrE
+S __ZNSt3__14clogE
+S __ZNSt3__14coutE
+T __ZNSt3__14stodERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T __ZNSt3__14stodERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T __ZNSt3__14stofERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T __ZNSt3__14stofERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T __ZNSt3__14stoiERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__14stoiERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T __ZNSt3__14stolERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__14stolERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+S __ZNSt3__14wcinE
+T __ZNSt3__15alignEmmRPvRm
+S __ZNSt3__15ctypeIcE10table_sizeE
+T __ZNSt3__15ctypeIcE13classic_tableEv
+S __ZNSt3__15ctypeIcE2idE
+T __ZNSt3__15ctypeIcEC1EPKjbm
+T __ZNSt3__15ctypeIcEC2EPKjbm
+T __ZNSt3__15ctypeIcED0Ev
+T __ZNSt3__15ctypeIcED1Ev
+T __ZNSt3__15ctypeIcED2Ev
+S __ZNSt3__15ctypeIwE2idE
+T __ZNSt3__15ctypeIwED0Ev
+T __ZNSt3__15ctypeIwED1Ev
+T __ZNSt3__15ctypeIwED2Ev
+T __ZNSt3__15mutex4lockEv
+T __ZNSt3__15mutex6unlockEv
+T __ZNSt3__15mutex8try_lockEv
+T __ZNSt3__15mutexD1Ev
+T __ZNSt3__15mutexD2Ev
+T __ZNSt3__15stoldERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T __ZNSt3__15stoldERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T __ZNSt3__15stollERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__15stollERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T __ZNSt3__15stoulERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__15stoulERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+S __ZNSt3__15wcerrE
+S __ZNSt3__15wclogE
+S __ZNSt3__15wcoutE
+T __ZNSt3__16__itoa8__u32toaEjPc
+T __ZNSt3__16__itoa8__u64toaEyPc
+T __ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIeeEEPeEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIffEEPfEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIhhEEPhEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIiiEEPiEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIjjEEPjEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIllEEPlEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessImmEEPmEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIssEEPsEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIttEEPtEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIwwEEPwEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIxxEEPxEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIyyEEPyEEvT0_S5_T_
+T __ZNSt3__16chrono12steady_clock3nowEv
+S __ZNSt3__16chrono12steady_clock9is_steadyE
+T __ZNSt3__16chrono12system_clock11from_time_tEl
+T __ZNSt3__16chrono12system_clock3nowEv
+S __ZNSt3__16chrono12system_clock9is_steadyE
+T __ZNSt3__16chrono12system_clock9to_time_tERKNS0_10time_pointIS1_NS0_8durationIxNS_5ratioILl1ELl1000000EEEEEEE
+T __ZNSt3__16futureIvE3getEv
+T __ZNSt3__16futureIvEC1EPNS_17__assoc_sub_stateE
+T __ZNSt3__16futureIvEC2EPNS_17__assoc_sub_stateE
+T __ZNSt3__16futureIvED1Ev
+T __ZNSt3__16futureIvED2Ev
+T __ZNSt3__16gslice6__initEm
+T __ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl
+T __ZNSt3__16locale2id5__getEv
+S __ZNSt3__16locale2id9__next_idE
+S __ZNSt3__16locale3allE
+S __ZNSt3__16locale4noneE
+S __ZNSt3__16locale4timeE
+S __ZNSt3__16locale5ctypeE
+T __ZNSt3__16locale5facet16__on_zero_sharedEv
+T __ZNSt3__16locale5facetD0Ev
+T __ZNSt3__16locale5facetD1Ev
+T __ZNSt3__16locale5facetD2Ev
+T __ZNSt3__16locale6globalERKS0_
+T __ZNSt3__16locale7classicEv
+S __ZNSt3__16locale7collateE
+S __ZNSt3__16locale7numericE
+T __ZNSt3__16locale8__globalEv
+S __ZNSt3__16locale8messagesE
+S __ZNSt3__16locale8monetaryE
+T __ZNSt3__16localeC1EPKc
+T __ZNSt3__16localeC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__16localeC1ERKS0_
+T __ZNSt3__16localeC1ERKS0_PKci
+T __ZNSt3__16localeC1ERKS0_RKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEi
+T __ZNSt3__16localeC1ERKS0_S2_i
+T __ZNSt3__16localeC1Ev
+T __ZNSt3__16localeC2EPKc
+T __ZNSt3__16localeC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__16localeC2ERKS0_
+T __ZNSt3__16localeC2ERKS0_PKci
+T __ZNSt3__16localeC2ERKS0_RKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEi
+T __ZNSt3__16localeC2ERKS0_S2_i
+T __ZNSt3__16localeC2Ev
+T __ZNSt3__16localeD1Ev
+T __ZNSt3__16localeD2Ev
+T __ZNSt3__16localeaSERKS0_
+T __ZNSt3__16stoullERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__16stoullERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T __ZNSt3__16thread20hardware_concurrencyEv
+T __ZNSt3__16thread4joinEv
+T __ZNSt3__16thread6detachEv
+T __ZNSt3__16threadD1Ev
+T __ZNSt3__16threadD2Ev
+S __ZNSt3__17codecvtIDiDu11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDiDu11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDiDu11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDiDu11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIDic11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDic11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDic11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDic11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIDsDu11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDsDu11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDsDu11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDsDu11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIDsc11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDsc11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDsc11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDsc11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIcc11__mbstate_tE2idE
+T __ZNSt3__17codecvtIcc11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIcc11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIcc11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIwc11__mbstate_tE2idE
+T __ZNSt3__17codecvtIwc11__mbstate_tEC1EPKcm
+T __ZNSt3__17codecvtIwc11__mbstate_tEC1Em
+T __ZNSt3__17codecvtIwc11__mbstate_tEC2EPKcm
+T __ZNSt3__17codecvtIwc11__mbstate_tEC2Em
+T __ZNSt3__17codecvtIwc11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIwc11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIwc11__mbstate_tED2Ev
+D __ZNSt3__17collateIcE2idE
+T __ZNSt3__17collateIcED0Ev
+T __ZNSt3__17collateIcED1Ev
+T __ZNSt3__17collateIcED2Ev
+D __ZNSt3__17collateIwE2idE
+T __ZNSt3__17collateIwED0Ev
+T __ZNSt3__17collateIwED1Ev
+T __ZNSt3__17collateIwED2Ev
+D __ZNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+D __ZNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__17promiseIvE10get_futureEv
+T __ZNSt3__17promiseIvE13set_exceptionESt13exception_ptr
+T __ZNSt3__17promiseIvE24set_value_at_thread_exitEv
+T __ZNSt3__17promiseIvE28set_exception_at_thread_exitESt13exception_ptr
+T __ZNSt3__17promiseIvE9set_valueEv
+T __ZNSt3__17promiseIvEC1Ev
+T __ZNSt3__17promiseIvEC2Ev
+T __ZNSt3__17promiseIvED1Ev
+T __ZNSt3__17promiseIvED2Ev
+T __ZNSt3__18__rs_getEv
+T __ZNSt3__18__sp_mut4lockEv
+T __ZNSt3__18__sp_mut6unlockEv
+S __ZNSt3__18ios_base10floatfieldE
+S __ZNSt3__18ios_base10scientificE
+S __ZNSt3__18ios_base11adjustfieldE
+T __ZNSt3__18ios_base15sync_with_stdioEb
+T __ZNSt3__18ios_base16__call_callbacksENS0_5eventE
+T __ZNSt3__18ios_base17register_callbackEPFvNS0_5eventERS0_iEi
+S __ZNSt3__18ios_base2inE
+T __ZNSt3__18ios_base33__set_badbit_and_consider_rethrowEv
+T __ZNSt3__18ios_base34__set_failbit_and_consider_rethrowEv
+S __ZNSt3__18ios_base3appE
+S __ZNSt3__18ios_base3ateE
+S __ZNSt3__18ios_base3decE
+S __ZNSt3__18ios_base3hexE
+S __ZNSt3__18ios_base3octE
+S __ZNSt3__18ios_base3outE
+T __ZNSt3__18ios_base4InitC1Ev
+T __ZNSt3__18ios_base4InitC2Ev
+T __ZNSt3__18ios_base4InitD1Ev
+T __ZNSt3__18ios_base4InitD2Ev
+T __ZNSt3__18ios_base4initEPv
+S __ZNSt3__18ios_base4leftE
+T __ZNSt3__18ios_base4moveERS0_
+T __ZNSt3__18ios_base4swapERS0_
+T __ZNSt3__18ios_base5clearEj
+S __ZNSt3__18ios_base5fixedE
+T __ZNSt3__18ios_base5imbueERKNS_6localeE
+T __ZNSt3__18ios_base5iwordEi
+T __ZNSt3__18ios_base5pwordEi
+S __ZNSt3__18ios_base5rightE
+S __ZNSt3__18ios_base5truncE
+S __ZNSt3__18ios_base6badbitE
+S __ZNSt3__18ios_base6binaryE
+S __ZNSt3__18ios_base6eofbitE
+S __ZNSt3__18ios_base6skipwsE
+T __ZNSt3__18ios_base6xallocEv
+T __ZNSt3__18ios_base7copyfmtERKS0_
+S __ZNSt3__18ios_base7failbitE
+T __ZNSt3__18ios_base7failureC1EPKcRKNS_10error_codeE
+T __ZNSt3__18ios_base7failureC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_10error_codeE
+T __ZNSt3__18ios_base7failureC2EPKcRKNS_10error_codeE
+T __ZNSt3__18ios_base7failureC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_10error_codeE
+T __ZNSt3__18ios_base7failureD0Ev
+T __ZNSt3__18ios_base7failureD1Ev
+T __ZNSt3__18ios_base7failureD2Ev
+S __ZNSt3__18ios_base7goodbitE
+S __ZNSt3__18ios_base7showposE
+S __ZNSt3__18ios_base7unitbufE
+S __ZNSt3__18ios_base8internalE
+S __ZNSt3__18ios_base8showbaseE
+S __ZNSt3__18ios_base9basefieldE
+S __ZNSt3__18ios_base9boolalphaE
+S __ZNSt3__18ios_base9showpointE
+S __ZNSt3__18ios_base9uppercaseE
+T __ZNSt3__18ios_baseD0Ev
+T __ZNSt3__18ios_baseD1Ev
+T __ZNSt3__18ios_baseD2Ev
+D __ZNSt3__18messagesIcE2idE
+D __ZNSt3__18messagesIwE2idE
+S __ZNSt3__18numpunctIcE2idE
+T __ZNSt3__18numpunctIcEC1Em
+T __ZNSt3__18numpunctIcEC2Em
+T __ZNSt3__18numpunctIcED0Ev
+T __ZNSt3__18numpunctIcED1Ev
+T __ZNSt3__18numpunctIcED2Ev
+S __ZNSt3__18numpunctIwE2idE
+T __ZNSt3__18numpunctIwEC1Em
+T __ZNSt3__18numpunctIwEC2Em
+T __ZNSt3__18numpunctIwED0Ev
+T __ZNSt3__18numpunctIwED1Ev
+T __ZNSt3__18numpunctIwED2Ev
+D __ZNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+D __ZNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__18to_charsEPcS0_d
+T __ZNSt3__18to_charsEPcS0_dNS_12chars_formatE
+T __ZNSt3__18to_charsEPcS0_dNS_12chars_formatEi
+T __ZNSt3__18to_charsEPcS0_e
+T __ZNSt3__18to_charsEPcS0_eNS_12chars_formatE
+T __ZNSt3__18to_charsEPcS0_eNS_12chars_formatEi
+T __ZNSt3__18to_charsEPcS0_f
+T __ZNSt3__18to_charsEPcS0_fNS_12chars_formatE
+T __ZNSt3__18to_charsEPcS0_fNS_12chars_formatEi
+T __ZNSt3__18valarrayImE6resizeEmm
+T __ZNSt3__18valarrayImEC1Em
+T __ZNSt3__18valarrayImEC2Em
+T __ZNSt3__18valarrayImED1Ev
+T __ZNSt3__18valarrayImED2Ev
+T __ZNSt3__19__num_getIcE17__stage2_int_loopEciPcRS2_RjcRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_S2_
+T __ZNSt3__19__num_getIcE17__stage2_int_prepERNS_8ios_baseEPcRc
+T __ZNSt3__19__num_getIcE19__stage2_float_loopEcRbRcPcRS4_ccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjS4_
+T __ZNSt3__19__num_getIcE19__stage2_float_prepERNS_8ios_baseEPcRcS5_
+T __ZNSt3__19__num_getIwE17__stage2_int_loopEwiPcRS2_RjwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_Pw
+T __ZNSt3__19__num_getIwE17__stage2_int_prepERNS_8ios_baseEPwRw
+T __ZNSt3__19__num_getIwE19__stage2_float_loopEwRbRcPcRS4_wwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjPw
+T __ZNSt3__19__num_getIwE19__stage2_float_prepERNS_8ios_baseEPwRwS5_
+T __ZNSt3__19__num_putIcE21__widen_and_group_intEPcS2_S2_S2_RS2_S3_RKNS_6localeE
+T __ZNSt3__19__num_putIcE23__widen_and_group_floatEPcS2_S2_S2_RS2_S3_RKNS_6localeE
+T __ZNSt3__19__num_putIwE21__widen_and_group_intEPcS2_S2_PwRS3_S4_RKNS_6localeE
+T __ZNSt3__19__num_putIwE23__widen_and_group_floatEPcS2_S2_PwRS3_S4_RKNS_6localeE
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEE7copyfmtERKS3_
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEE7copyfmtERKS3_
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEED2Ev
+D __ZNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+T __ZNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE8__do_getERS4_S4_bRKNS_6localeEjRjRbRKNS_5ctypeIcEERNS_10unique_ptrIcPFvPvEEERPcSM_
+D __ZNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE8__do_getERS4_S4_bRKNS_6localeEjRjRbRKNS_5ctypeIwEERNS_10unique_ptrIwPFvPvEEERPwSM_
+D __ZNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__19strstreamD0Ev
+T __ZNSt3__19strstreamD1Ev
+T __ZNSt3__19strstreamD2Ev
+T __ZNSt3__19to_stringEd
+T __ZNSt3__19to_stringEe
+T __ZNSt3__19to_stringEf
+T __ZNSt3__19to_stringEi
+T __ZNSt3__19to_stringEj
+T __ZNSt3__19to_stringEl
+T __ZNSt3__19to_stringEm
+T __ZNSt3__19to_stringEx
+T __ZNSt3__19to_stringEy
+T __ZNSt3__1plIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_12basic_stringIT_T0_T1_EEPKS6_RKS9_
+I __ZNSt8bad_castC1Ev (indirect for __ZNSt8bad_castC1Ev)
+I __ZNSt8bad_castC2Ev (indirect for __ZNSt8bad_castC2Ev)
+I __ZNSt8bad_castD0Ev (indirect for __ZNSt8bad_castD0Ev)
+I __ZNSt8bad_castD1Ev (indirect for __ZNSt8bad_castD1Ev)
+I __ZNSt8bad_castD2Ev (indirect for __ZNSt8bad_castD2Ev)
+I __ZNSt9bad_allocC1Ev (indirect for __ZNSt9bad_allocC1Ev)
+I __ZNSt9bad_allocC2Ev (indirect for __ZNSt9bad_allocC2Ev)
+I __ZNSt9bad_allocD0Ev (indirect for __ZNSt9bad_allocD0Ev)
+I __ZNSt9bad_allocD1Ev (indirect for __ZNSt9bad_allocD1Ev)
+I __ZNSt9bad_allocD2Ev (indirect for __ZNSt9bad_allocD2Ev)
+I __ZNSt9exceptionD0Ev (indirect for __ZNSt9exceptionD0Ev)
+I __ZNSt9exceptionD1Ev (indirect for __ZNSt9exceptionD1Ev)
+I __ZNSt9exceptionD2Ev (indirect for __ZNSt9exceptionD2Ev)
+I __ZNSt9type_infoD0Ev (indirect for __ZNSt9type_infoD0Ev)
+I __ZNSt9type_infoD1Ev (indirect for __ZNSt9type_infoD1Ev)
+I __ZNSt9type_infoD2Ev (indirect for __ZNSt9type_infoD2Ev)
+I __ZSt10unexpectedv (indirect for __ZSt10unexpectedv)
+I __ZSt13get_terminatev (indirect for __ZSt13get_terminatev)
+I __ZSt13set_terminatePFvvE (indirect for __ZSt13set_terminatePFvvE)
+I __ZSt14get_unexpectedv (indirect for __ZSt14get_unexpectedv)
+I __ZSt14set_unexpectedPFvvE (indirect for __ZSt14set_unexpectedPFvvE)
+I __ZSt15get_new_handlerv (indirect for __ZSt15get_new_handlerv)
+I __ZSt15set_new_handlerPFvvE (indirect for __ZSt15set_new_handlerPFvvE)
+T __ZSt17__throw_bad_allocv
+T __ZSt17current_exceptionv
+T __ZSt17rethrow_exceptionSt13exception_ptr
+T __ZSt18uncaught_exceptionv
+T __ZSt19uncaught_exceptionsv
+S __ZSt7nothrow
+I __ZSt9terminatev (indirect for __ZSt9terminatev)
+S __ZTCNSt3__110istrstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__110ostrstreamE0_NS_13basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE16_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE0_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_14basic_iostreamIcS2_EE
+S __ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE16_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__19strstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__19strstreamE0_NS_14basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__19strstreamE16_NS_13basic_ostreamIcNS_11char_traitsIcEEEE
+I __ZTIDh (indirect for __ZTIDh)
+I __ZTIDi (indirect for __ZTIDi)
+I __ZTIDn (indirect for __ZTIDn)
+I __ZTIDs (indirect for __ZTIDs)
+I __ZTIDu (indirect for __ZTIDu)
+I __ZTIN10__cxxabiv116__enum_type_infoE (indirect for __ZTIN10__cxxabiv116__enum_type_infoE)
+I __ZTIN10__cxxabiv116__shim_type_infoE (indirect for __ZTIN10__cxxabiv116__shim_type_infoE)
+I __ZTIN10__cxxabiv117__array_type_infoE (indirect for __ZTIN10__cxxabiv117__array_type_infoE)
+I __ZTIN10__cxxabiv117__pbase_type_infoE (indirect for __ZTIN10__cxxabiv117__pbase_type_infoE)
+I __ZTIN10__cxxabiv119__pointer_type_infoE (indirect for __ZTIN10__cxxabiv119__pointer_type_infoE)
+I __ZTIN10__cxxabiv120__function_type_infoE (indirect for __ZTIN10__cxxabiv120__function_type_infoE)
+I __ZTIN10__cxxabiv123__fundamental_type_infoE (indirect for __ZTIN10__cxxabiv123__fundamental_type_infoE)
+I __ZTIN10__cxxabiv129__pointer_to_member_type_infoE (indirect for __ZTIN10__cxxabiv129__pointer_to_member_type_infoE)
+S __ZTINSt12experimental15fundamentals_v112bad_any_castE
+S __ZTINSt12experimental19bad_optional_accessE
+S __ZTINSt3__110istrstreamE
+S __ZTINSt3__110moneypunctIcLb0EEE
+S __ZTINSt3__110moneypunctIcLb1EEE
+S __ZTINSt3__110moneypunctIwLb0EEE
+S __ZTINSt3__110moneypunctIwLb1EEE
+S __ZTINSt3__110ostrstreamE
+S __ZTINSt3__111regex_errorE
+S __ZTINSt3__112bad_weak_ptrE
+S __ZTINSt3__112ctype_bynameIcEE
+S __ZTINSt3__112ctype_bynameIwEE
+S __ZTINSt3__112future_errorE
+S __ZTINSt3__112strstreambufE
+S __ZTINSt3__112system_errorE
+S __ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+S __ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTINSt3__114__codecvt_utf8IDiEE
+S __ZTINSt3__114__codecvt_utf8IDsEE
+S __ZTINSt3__114__codecvt_utf8IwEE
+S __ZTINSt3__114__shared_countE
+S __ZTINSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIDic11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIDsc11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIcc11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIwc11__mbstate_tEE
+S __ZTINSt3__114collate_bynameIcEE
+S __ZTINSt3__114collate_bynameIwEE
+S __ZTINSt3__114error_categoryE
+S __ZTINSt3__115__codecvt_utf16IDiLb0EEE
+S __ZTINSt3__115__codecvt_utf16IDiLb1EEE
+S __ZTINSt3__115__codecvt_utf16IDsLb0EEE
+S __ZTINSt3__115__codecvt_utf16IDsLb1EEE
+S __ZTINSt3__115__codecvt_utf16IwLb0EEE
+S __ZTINSt3__115__codecvt_utf16IwLb1EEE
+S __ZTINSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+S __ZTINSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+S __ZTINSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__115messages_bynameIcEE
+S __ZTINSt3__115messages_bynameIwEE
+S __ZTINSt3__115numpunct_bynameIcEE
+S __ZTINSt3__115numpunct_bynameIwEE
+S __ZTINSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__116__narrow_to_utf8ILm16EEE
+S __ZTINSt3__116__narrow_to_utf8ILm32EEE
+S __ZTINSt3__117__assoc_sub_stateE
+S __ZTINSt3__117__widen_from_utf8ILm16EEE
+S __ZTINSt3__117__widen_from_utf8ILm32EEE
+S __ZTINSt3__117bad_function_callE
+S __ZTINSt3__117moneypunct_bynameIcLb0EEE
+S __ZTINSt3__117moneypunct_bynameIcLb1EEE
+S __ZTINSt3__117moneypunct_bynameIwLb0EEE
+S __ZTINSt3__117moneypunct_bynameIwLb1EEE
+S __ZTINSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__119__shared_weak_countE
+S __ZTINSt3__119bad_expected_accessIvEE
+S __ZTINSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__120__codecvt_utf8_utf16IDiEE
+S __ZTINSt3__120__codecvt_utf8_utf16IDsEE
+S __ZTINSt3__120__codecvt_utf8_utf16IwEE
+S __ZTINSt3__13pmr15memory_resourceE
+S __ZTINSt3__13pmr25monotonic_buffer_resourceE
+S __ZTINSt3__13pmr26synchronized_pool_resourceE
+S __ZTINSt3__13pmr28unsynchronized_pool_resourceE
+S __ZTINSt3__14__fs10filesystem16filesystem_errorE
+S __ZTINSt3__15ctypeIcEE
+S __ZTINSt3__15ctypeIwEE
+S __ZTINSt3__16locale5facetE
+S __ZTINSt3__17codecvtIDiDu11__mbstate_tEE
+S __ZTINSt3__17codecvtIDic11__mbstate_tEE
+S __ZTINSt3__17codecvtIDsDu11__mbstate_tEE
+S __ZTINSt3__17codecvtIDsc11__mbstate_tEE
+S __ZTINSt3__17codecvtIcc11__mbstate_tEE
+S __ZTINSt3__17codecvtIwc11__mbstate_tEE
+S __ZTINSt3__17collateIcEE
+S __ZTINSt3__17collateIwEE
+S __ZTINSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__18ios_base7failureE
+S __ZTINSt3__18ios_baseE
+S __ZTINSt3__18messagesIcEE
+S __ZTINSt3__18messagesIwEE
+S __ZTINSt3__18numpunctIcEE
+S __ZTINSt3__18numpunctIwEE
+S __ZTINSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__19basic_iosIcNS_11char_traitsIcEEEE
+S __ZTINSt3__19basic_iosIwNS_11char_traitsIwEEEE
+S __ZTINSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__19strstreamE
+I __ZTIPDh (indirect for __ZTIPDh)
+I __ZTIPDi (indirect for __ZTIPDi)
+I __ZTIPDn (indirect for __ZTIPDn)
+I __ZTIPDs (indirect for __ZTIPDs)
+I __ZTIPDu (indirect for __ZTIPDu)
+I __ZTIPKDh (indirect for __ZTIPKDh)
+I __ZTIPKDi (indirect for __ZTIPKDi)
+I __ZTIPKDn (indirect for __ZTIPKDn)
+I __ZTIPKDs (indirect for __ZTIPKDs)
+I __ZTIPKDu (indirect for __ZTIPKDu)
+I __ZTIPKa (indirect for __ZTIPKa)
+I __ZTIPKb (indirect for __ZTIPKb)
+I __ZTIPKc (indirect for __ZTIPKc)
+I __ZTIPKd (indirect for __ZTIPKd)
+I __ZTIPKe (indirect for __ZTIPKe)
+I __ZTIPKf (indirect for __ZTIPKf)
+I __ZTIPKg (indirect for __ZTIPKg)
+I __ZTIPKh (indirect for __ZTIPKh)
+I __ZTIPKi (indirect for __ZTIPKi)
+I __ZTIPKj (indirect for __ZTIPKj)
+I __ZTIPKl (indirect for __ZTIPKl)
+I __ZTIPKm (indirect for __ZTIPKm)
+I __ZTIPKn (indirect for __ZTIPKn)
+I __ZTIPKo (indirect for __ZTIPKo)
+I __ZTIPKs (indirect for __ZTIPKs)
+I __ZTIPKt (indirect for __ZTIPKt)
+I __ZTIPKv (indirect for __ZTIPKv)
+I __ZTIPKw (indirect for __ZTIPKw)
+I __ZTIPKx (indirect for __ZTIPKx)
+I __ZTIPKy (indirect for __ZTIPKy)
+I __ZTIPa (indirect for __ZTIPa)
+I __ZTIPb (indirect for __ZTIPb)
+I __ZTIPc (indirect for __ZTIPc)
+I __ZTIPd (indirect for __ZTIPd)
+I __ZTIPe (indirect for __ZTIPe)
+I __ZTIPf (indirect for __ZTIPf)
+I __ZTIPg (indirect for __ZTIPg)
+I __ZTIPh (indirect for __ZTIPh)
+I __ZTIPi (indirect for __ZTIPi)
+I __ZTIPj (indirect for __ZTIPj)
+I __ZTIPl (indirect for __ZTIPl)
+I __ZTIPm (indirect for __ZTIPm)
+I __ZTIPn (indirect for __ZTIPn)
+I __ZTIPo (indirect for __ZTIPo)
+I __ZTIPs (indirect for __ZTIPs)
+I __ZTIPt (indirect for __ZTIPt)
+I __ZTIPv (indirect for __ZTIPv)
+I __ZTIPw (indirect for __ZTIPw)
+I __ZTIPx (indirect for __ZTIPx)
+I __ZTIPy (indirect for __ZTIPy)
+I __ZTISt10bad_typeid (indirect for __ZTISt10bad_typeid)
+I __ZTISt11logic_error (indirect for __ZTISt11logic_error)
+I __ZTISt11range_error (indirect for __ZTISt11range_error)
+S __ZTISt12bad_any_cast
+I __ZTISt12domain_error (indirect for __ZTISt12domain_error)
+I __ZTISt12length_error (indirect for __ZTISt12length_error)
+I __ZTISt12out_of_range (indirect for __ZTISt12out_of_range)
+I __ZTISt13bad_exception (indirect for __ZTISt13bad_exception)
+I __ZTISt13runtime_error (indirect for __ZTISt13runtime_error)
+I __ZTISt14overflow_error (indirect for __ZTISt14overflow_error)
+I __ZTISt15underflow_error (indirect for __ZTISt15underflow_error)
+I __ZTISt16invalid_argument (indirect for __ZTISt16invalid_argument)
+S __ZTISt16nested_exception
+S __ZTISt18bad_variant_access
+S __ZTISt19bad_optional_access
+I __ZTISt20bad_array_new_length (indirect for __ZTISt20bad_array_new_length)
+S __ZTISt29__bad_variant_access_with_msg
+I __ZTISt8bad_cast (indirect for __ZTISt8bad_cast)
+I __ZTISt9bad_alloc (indirect for __ZTISt9bad_alloc)
+I __ZTISt9exception (indirect for __ZTISt9exception)
+I __ZTISt9type_info (indirect for __ZTISt9type_info)
+I __ZTIa (indirect for __ZTIa)
+I __ZTIb (indirect for __ZTIb)
+I __ZTIc (indirect for __ZTIc)
+I __ZTId (indirect for __ZTId)
+I __ZTIe (indirect for __ZTIe)
+I __ZTIf (indirect for __ZTIf)
+I __ZTIg (indirect for __ZTIg)
+I __ZTIh (indirect for __ZTIh)
+I __ZTIi (indirect for __ZTIi)
+I __ZTIj (indirect for __ZTIj)
+I __ZTIl (indirect for __ZTIl)
+I __ZTIm (indirect for __ZTIm)
+I __ZTIn (indirect for __ZTIn)
+I __ZTIo (indirect for __ZTIo)
+I __ZTIs (indirect for __ZTIs)
+I __ZTIt (indirect for __ZTIt)
+I __ZTIv (indirect for __ZTIv)
+I __ZTIw (indirect for __ZTIw)
+I __ZTIx (indirect for __ZTIx)
+I __ZTIy (indirect for __ZTIy)
+I __ZTSDh (indirect for __ZTSDh)
+I __ZTSDi (indirect for __ZTSDi)
+I __ZTSDn (indirect for __ZTSDn)
+I __ZTSDs (indirect for __ZTSDs)
+I __ZTSDu (indirect for __ZTSDu)
+I __ZTSN10__cxxabiv116__enum_type_infoE (indirect for __ZTSN10__cxxabiv116__enum_type_infoE)
+I __ZTSN10__cxxabiv116__shim_type_infoE (indirect for __ZTSN10__cxxabiv116__shim_type_infoE)
+I __ZTSN10__cxxabiv117__array_type_infoE (indirect for __ZTSN10__cxxabiv117__array_type_infoE)
+I __ZTSN10__cxxabiv117__class_type_infoE (indirect for __ZTSN10__cxxabiv117__class_type_infoE)
+I __ZTSN10__cxxabiv117__pbase_type_infoE (indirect for __ZTSN10__cxxabiv117__pbase_type_infoE)
+I __ZTSN10__cxxabiv119__pointer_type_infoE (indirect for __ZTSN10__cxxabiv119__pointer_type_infoE)
+I __ZTSN10__cxxabiv120__function_type_infoE (indirect for __ZTSN10__cxxabiv120__function_type_infoE)
+I __ZTSN10__cxxabiv120__si_class_type_infoE (indirect for __ZTSN10__cxxabiv120__si_class_type_infoE)
+I __ZTSN10__cxxabiv121__vmi_class_type_infoE (indirect for __ZTSN10__cxxabiv121__vmi_class_type_infoE)
+I __ZTSN10__cxxabiv123__fundamental_type_infoE (indirect for __ZTSN10__cxxabiv123__fundamental_type_infoE)
+I __ZTSN10__cxxabiv129__pointer_to_member_type_infoE (indirect for __ZTSN10__cxxabiv129__pointer_to_member_type_infoE)
+S __ZTSNSt12experimental15fundamentals_v112bad_any_castE
+S __ZTSNSt12experimental19bad_optional_accessE
+S __ZTSNSt3__110istrstreamE
+S __ZTSNSt3__110moneypunctIcLb0EEE
+S __ZTSNSt3__110moneypunctIcLb1EEE
+S __ZTSNSt3__110moneypunctIwLb0EEE
+S __ZTSNSt3__110moneypunctIwLb1EEE
+S __ZTSNSt3__110ostrstreamE
+S __ZTSNSt3__111regex_errorE
+S __ZTSNSt3__112bad_weak_ptrE
+S __ZTSNSt3__112ctype_bynameIcEE
+S __ZTSNSt3__112ctype_bynameIwEE
+S __ZTSNSt3__112future_errorE
+S __ZTSNSt3__112strstreambufE
+S __ZTSNSt3__112system_errorE
+S __ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__114__codecvt_utf8IDiEE
+S __ZTSNSt3__114__codecvt_utf8IDsEE
+S __ZTSNSt3__114__codecvt_utf8IwEE
+S __ZTSNSt3__114__shared_countE
+S __ZTSNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIDic11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIDsc11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIcc11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIwc11__mbstate_tEE
+S __ZTSNSt3__114collate_bynameIcEE
+S __ZTSNSt3__114collate_bynameIwEE
+S __ZTSNSt3__114error_categoryE
+S __ZTSNSt3__115__codecvt_utf16IDiLb0EEE
+S __ZTSNSt3__115__codecvt_utf16IDiLb1EEE
+S __ZTSNSt3__115__codecvt_utf16IDsLb0EEE
+S __ZTSNSt3__115__codecvt_utf16IDsLb1EEE
+S __ZTSNSt3__115__codecvt_utf16IwLb0EEE
+S __ZTSNSt3__115__codecvt_utf16IwLb1EEE
+S __ZTSNSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__115messages_bynameIcEE
+S __ZTSNSt3__115messages_bynameIwEE
+S __ZTSNSt3__115numpunct_bynameIcEE
+S __ZTSNSt3__115numpunct_bynameIwEE
+S __ZTSNSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__116__narrow_to_utf8ILm16EEE
+S __ZTSNSt3__116__narrow_to_utf8ILm32EEE
+S __ZTSNSt3__117__assoc_sub_stateE
+S __ZTSNSt3__117__widen_from_utf8ILm16EEE
+S __ZTSNSt3__117__widen_from_utf8ILm32EEE
+S __ZTSNSt3__117bad_function_callE
+S __ZTSNSt3__117moneypunct_bynameIcLb0EEE
+S __ZTSNSt3__117moneypunct_bynameIcLb1EEE
+S __ZTSNSt3__117moneypunct_bynameIwLb0EEE
+S __ZTSNSt3__117moneypunct_bynameIwLb1EEE
+S __ZTSNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__119__shared_weak_countE
+S __ZTSNSt3__119bad_expected_accessIvEE
+S __ZTSNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__120__codecvt_utf8_utf16IDiEE
+S __ZTSNSt3__120__codecvt_utf8_utf16IDsEE
+S __ZTSNSt3__120__codecvt_utf8_utf16IwEE
+S __ZTSNSt3__13pmr15memory_resourceE
+S __ZTSNSt3__13pmr25monotonic_buffer_resourceE
+S __ZTSNSt3__13pmr26synchronized_pool_resourceE
+S __ZTSNSt3__13pmr28unsynchronized_pool_resourceE
+S __ZTSNSt3__14__fs10filesystem16filesystem_errorE
+S __ZTSNSt3__15ctypeIcEE
+S __ZTSNSt3__15ctypeIwEE
+S __ZTSNSt3__16locale5facetE
+S __ZTSNSt3__17codecvtIDiDu11__mbstate_tEE
+S __ZTSNSt3__17codecvtIDic11__mbstate_tEE
+S __ZTSNSt3__17codecvtIDsDu11__mbstate_tEE
+S __ZTSNSt3__17codecvtIDsc11__mbstate_tEE
+S __ZTSNSt3__17codecvtIcc11__mbstate_tEE
+S __ZTSNSt3__17codecvtIwc11__mbstate_tEE
+S __ZTSNSt3__17collateIcEE
+S __ZTSNSt3__17collateIwEE
+S __ZTSNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__18ios_base7failureE
+S __ZTSNSt3__18ios_baseE
+S __ZTSNSt3__18messagesIcEE
+S __ZTSNSt3__18messagesIwEE
+S __ZTSNSt3__18numpunctIcEE
+S __ZTSNSt3__18numpunctIwEE
+S __ZTSNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__19basic_iosIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__19basic_iosIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__19strstreamE
+I __ZTSPDh (indirect for __ZTSPDh)
+I __ZTSPDi (indirect for __ZTSPDi)
+I __ZTSPDn (indirect for __ZTSPDn)
+I __ZTSPDs (indirect for __ZTSPDs)
+I __ZTSPDu (indirect for __ZTSPDu)
+I __ZTSPKDh (indirect for __ZTSPKDh)
+I __ZTSPKDi (indirect for __ZTSPKDi)
+I __ZTSPKDn (indirect for __ZTSPKDn)
+I __ZTSPKDs (indirect for __ZTSPKDs)
+I __ZTSPKDu (indirect for __ZTSPKDu)
+I __ZTSPKa (indirect for __ZTSPKa)
+I __ZTSPKb (indirect for __ZTSPKb)
+I __ZTSPKc (indirect for __ZTSPKc)
+I __ZTSPKd (indirect for __ZTSPKd)
+I __ZTSPKe (indirect for __ZTSPKe)
+I __ZTSPKf (indirect for __ZTSPKf)
+I __ZTSPKg (indirect for __ZTSPKg)
+I __ZTSPKh (indirect for __ZTSPKh)
+I __ZTSPKi (indirect for __ZTSPKi)
+I __ZTSPKj (indirect for __ZTSPKj)
+I __ZTSPKl (indirect for __ZTSPKl)
+I __ZTSPKm (indirect for __ZTSPKm)
+I __ZTSPKn (indirect for __ZTSPKn)
+I __ZTSPKo (indirect for __ZTSPKo)
+I __ZTSPKs (indirect for __ZTSPKs)
+I __ZTSPKt (indirect for __ZTSPKt)
+I __ZTSPKv (indirect for __ZTSPKv)
+I __ZTSPKw (indirect for __ZTSPKw)
+I __ZTSPKx (indirect for __ZTSPKx)
+I __ZTSPKy (indirect for __ZTSPKy)
+I __ZTSPa (indirect for __ZTSPa)
+I __ZTSPb (indirect for __ZTSPb)
+I __ZTSPc (indirect for __ZTSPc)
+I __ZTSPd (indirect for __ZTSPd)
+I __ZTSPe (indirect for __ZTSPe)
+I __ZTSPf (indirect for __ZTSPf)
+I __ZTSPg (indirect for __ZTSPg)
+I __ZTSPh (indirect for __ZTSPh)
+I __ZTSPi (indirect for __ZTSPi)
+I __ZTSPj (indirect for __ZTSPj)
+I __ZTSPl (indirect for __ZTSPl)
+I __ZTSPm (indirect for __ZTSPm)
+I __ZTSPn (indirect for __ZTSPn)
+I __ZTSPo (indirect for __ZTSPo)
+I __ZTSPs (indirect for __ZTSPs)
+I __ZTSPt (indirect for __ZTSPt)
+I __ZTSPv (indirect for __ZTSPv)
+I __ZTSPw (indirect for __ZTSPw)
+I __ZTSPx (indirect for __ZTSPx)
+I __ZTSPy (indirect for __ZTSPy)
+I __ZTSSt10bad_typeid (indirect for __ZTSSt10bad_typeid)
+I __ZTSSt11logic_error (indirect for __ZTSSt11logic_error)
+I __ZTSSt11range_error (indirect for __ZTSSt11range_error)
+S __ZTSSt12bad_any_cast
+I __ZTSSt12domain_error (indirect for __ZTSSt12domain_error)
+I __ZTSSt12length_error (indirect for __ZTSSt12length_error)
+I __ZTSSt12out_of_range (indirect for __ZTSSt12out_of_range)
+I __ZTSSt13bad_exception (indirect for __ZTSSt13bad_exception)
+I __ZTSSt13runtime_error (indirect for __ZTSSt13runtime_error)
+I __ZTSSt14overflow_error (indirect for __ZTSSt14overflow_error)
+I __ZTSSt15underflow_error (indirect for __ZTSSt15underflow_error)
+I __ZTSSt16invalid_argument (indirect for __ZTSSt16invalid_argument)
+S __ZTSSt16nested_exception
+S __ZTSSt18bad_variant_access
+S __ZTSSt19bad_optional_access
+I __ZTSSt20bad_array_new_length (indirect for __ZTSSt20bad_array_new_length)
+S __ZTSSt29__bad_variant_access_with_msg
+I __ZTSSt8bad_cast (indirect for __ZTSSt8bad_cast)
+I __ZTSSt9bad_alloc (indirect for __ZTSSt9bad_alloc)
+I __ZTSSt9exception (indirect for __ZTSSt9exception)
+I __ZTSSt9type_info (indirect for __ZTSSt9type_info)
+I __ZTSa (indirect for __ZTSa)
+I __ZTSb (indirect for __ZTSb)
+I __ZTSc (indirect for __ZTSc)
+I __ZTSd (indirect for __ZTSd)
+I __ZTSe (indirect for __ZTSe)
+I __ZTSf (indirect for __ZTSf)
+I __ZTSg (indirect for __ZTSg)
+I __ZTSh (indirect for __ZTSh)
+I __ZTSi (indirect for __ZTSi)
+I __ZTSj (indirect for __ZTSj)
+I __ZTSl (indirect for __ZTSl)
+I __ZTSm (indirect for __ZTSm)
+I __ZTSn (indirect for __ZTSn)
+I __ZTSo (indirect for __ZTSo)
+I __ZTSs (indirect for __ZTSs)
+I __ZTSt (indirect for __ZTSt)
+I __ZTSv (indirect for __ZTSv)
+I __ZTSw (indirect for __ZTSw)
+I __ZTSx (indirect for __ZTSx)
+I __ZTSy (indirect for __ZTSy)
+S __ZTTNSt3__110istrstreamE
+S __ZTTNSt3__110ostrstreamE
+S __ZTTNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTTNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTTNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTTNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTTNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTTNSt3__19strstreamE
+I __ZTVN10__cxxabiv116__enum_type_infoE (indirect for __ZTVN10__cxxabiv116__enum_type_infoE)
+I __ZTVN10__cxxabiv116__shim_type_infoE (indirect for __ZTVN10__cxxabiv116__shim_type_infoE)
+I __ZTVN10__cxxabiv117__array_type_infoE (indirect for __ZTVN10__cxxabiv117__array_type_infoE)
+I __ZTVN10__cxxabiv117__class_type_infoE (indirect for __ZTVN10__cxxabiv117__class_type_infoE)
+I __ZTVN10__cxxabiv117__pbase_type_infoE (indirect for __ZTVN10__cxxabiv117__pbase_type_infoE)
+I __ZTVN10__cxxabiv119__pointer_type_infoE (indirect for __ZTVN10__cxxabiv119__pointer_type_infoE)
+I __ZTVN10__cxxabiv120__function_type_infoE (indirect for __ZTVN10__cxxabiv120__function_type_infoE)
+I __ZTVN10__cxxabiv120__si_class_type_infoE (indirect for __ZTVN10__cxxabiv120__si_class_type_infoE)
+I __ZTVN10__cxxabiv121__vmi_class_type_infoE (indirect for __ZTVN10__cxxabiv121__vmi_class_type_infoE)
+I __ZTVN10__cxxabiv123__fundamental_type_infoE (indirect for __ZTVN10__cxxabiv123__fundamental_type_infoE)
+I __ZTVN10__cxxabiv129__pointer_to_member_type_infoE (indirect for __ZTVN10__cxxabiv129__pointer_to_member_type_infoE)
+S __ZTVNSt12experimental15fundamentals_v112bad_any_castE
+S __ZTVNSt12experimental19bad_optional_accessE
+S __ZTVNSt3__110istrstreamE
+S __ZTVNSt3__110moneypunctIcLb0EEE
+S __ZTVNSt3__110moneypunctIcLb1EEE
+S __ZTVNSt3__110moneypunctIwLb0EEE
+S __ZTVNSt3__110moneypunctIwLb1EEE
+S __ZTVNSt3__110ostrstreamE
+S __ZTVNSt3__111regex_errorE
+S __ZTVNSt3__112bad_weak_ptrE
+S __ZTVNSt3__112ctype_bynameIcEE
+S __ZTVNSt3__112ctype_bynameIwEE
+S __ZTVNSt3__112future_errorE
+S __ZTVNSt3__112strstreambufE
+S __ZTVNSt3__112system_errorE
+S __ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__114__codecvt_utf8IDiEE
+S __ZTVNSt3__114__codecvt_utf8IDsEE
+S __ZTVNSt3__114__codecvt_utf8IwEE
+S __ZTVNSt3__114__shared_countE
+S __ZTVNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIDic11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIDsc11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIcc11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIwc11__mbstate_tEE
+S __ZTVNSt3__114collate_bynameIcEE
+S __ZTVNSt3__114collate_bynameIwEE
+S __ZTVNSt3__114error_categoryE
+S __ZTVNSt3__115__codecvt_utf16IDiLb0EEE
+S __ZTVNSt3__115__codecvt_utf16IDiLb1EEE
+S __ZTVNSt3__115__codecvt_utf16IDsLb0EEE
+S __ZTVNSt3__115__codecvt_utf16IDsLb1EEE
+S __ZTVNSt3__115__codecvt_utf16IwLb0EEE
+S __ZTVNSt3__115__codecvt_utf16IwLb1EEE
+S __ZTVNSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__115messages_bynameIcEE
+S __ZTVNSt3__115messages_bynameIwEE
+S __ZTVNSt3__115numpunct_bynameIcEE
+S __ZTVNSt3__115numpunct_bynameIwEE
+S __ZTVNSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__116__narrow_to_utf8ILm16EEE
+S __ZTVNSt3__116__narrow_to_utf8ILm32EEE
+S __ZTVNSt3__117__assoc_sub_stateE
+S __ZTVNSt3__117__widen_from_utf8ILm16EEE
+S __ZTVNSt3__117__widen_from_utf8ILm32EEE
+S __ZTVNSt3__117bad_function_callE
+S __ZTVNSt3__117moneypunct_bynameIcLb0EEE
+S __ZTVNSt3__117moneypunct_bynameIcLb1EEE
+S __ZTVNSt3__117moneypunct_bynameIwLb0EEE
+S __ZTVNSt3__117moneypunct_bynameIwLb1EEE
+S __ZTVNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__119__shared_weak_countE
+S __ZTVNSt3__119bad_expected_accessIvEE
+S __ZTVNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__120__codecvt_utf8_utf16IDiEE
+S __ZTVNSt3__120__codecvt_utf8_utf16IDsEE
+S __ZTVNSt3__120__codecvt_utf8_utf16IwEE
+S __ZTVNSt3__13pmr15memory_resourceE
+S __ZTVNSt3__13pmr25monotonic_buffer_resourceE
+S __ZTVNSt3__13pmr26synchronized_pool_resourceE
+S __ZTVNSt3__13pmr28unsynchronized_pool_resourceE
+S __ZTVNSt3__14__fs10filesystem16filesystem_errorE
+S __ZTVNSt3__15ctypeIcEE
+S __ZTVNSt3__15ctypeIwEE
+S __ZTVNSt3__16locale5facetE
+S __ZTVNSt3__17codecvtIDiDu11__mbstate_tEE
+S __ZTVNSt3__17codecvtIDic11__mbstate_tEE
+S __ZTVNSt3__17codecvtIDsDu11__mbstate_tEE
+S __ZTVNSt3__17codecvtIDsc11__mbstate_tEE
+S __ZTVNSt3__17codecvtIcc11__mbstate_tEE
+S __ZTVNSt3__17codecvtIwc11__mbstate_tEE
+S __ZTVNSt3__17collateIcEE
+S __ZTVNSt3__17collateIwEE
+S __ZTVNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__18ios_base7failureE
+S __ZTVNSt3__18ios_baseE
+S __ZTVNSt3__18messagesIcEE
+S __ZTVNSt3__18messagesIwEE
+S __ZTVNSt3__18numpunctIcEE
+S __ZTVNSt3__18numpunctIwEE
+S __ZTVNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__19basic_iosIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__19basic_iosIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__19strstreamE
+I __ZTVSt10bad_typeid (indirect for __ZTVSt10bad_typeid)
+I __ZTVSt11logic_error (indirect for __ZTVSt11logic_error)
+I __ZTVSt11range_error (indirect for __ZTVSt11range_error)
+S __ZTVSt12bad_any_cast
+I __ZTVSt12domain_error (indirect for __ZTVSt12domain_error)
+I __ZTVSt12length_error (indirect for __ZTVSt12length_error)
+I __ZTVSt12out_of_range (indirect for __ZTVSt12out_of_range)
+I __ZTVSt13bad_exception (indirect for __ZTVSt13bad_exception)
+I __ZTVSt13runtime_error (indirect for __ZTVSt13runtime_error)
+I __ZTVSt14overflow_error (indirect for __ZTVSt14overflow_error)
+I __ZTVSt15underflow_error (indirect for __ZTVSt15underflow_error)
+I __ZTVSt16invalid_argument (indirect for __ZTVSt16invalid_argument)
+S __ZTVSt16nested_exception
+S __ZTVSt18bad_variant_access
+S __ZTVSt19bad_optional_access
+I __ZTVSt20bad_array_new_length (indirect for __ZTVSt20bad_array_new_length)
+S __ZTVSt29__bad_variant_access_with_msg
+I __ZTVSt8bad_cast (indirect for __ZTVSt8bad_cast)
+I __ZTVSt9bad_alloc (indirect for __ZTVSt9bad_alloc)
+I __ZTVSt9exception (indirect for __ZTVSt9exception)
+I __ZTVSt9type_info (indirect for __ZTVSt9type_info)
+T __ZThn16_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+T __ZThn16_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T __ZThn16_NSt3__19strstreamD0Ev
+T __ZThn16_NSt3__19strstreamD1Ev
+T __ZTv0_n24_NSt3__110istrstreamD0Ev
+T __ZTv0_n24_NSt3__110istrstreamD1Ev
+T __ZTv0_n24_NSt3__110ostrstreamD0Ev
+T __ZTv0_n24_NSt3__110ostrstreamD1Ev
+T __ZTv0_n24_NSt3__113basic_istreamIcNS_11char_traitsIcEEED0Ev
+T __ZTv0_n24_NSt3__113basic_istreamIcNS_11char_traitsIcEEED1Ev
+T __ZTv0_n24_NSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev
+T __ZTv0_n24_NSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIwNS_11char_traitsIwEEED0Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIwNS_11char_traitsIwEEED1Ev
+T __ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+T __ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T __ZTv0_n24_NSt3__19strstreamD0Ev
+T __ZTv0_n24_NSt3__19strstreamD1Ev
+I __ZdaPv (indirect for __ZdaPv)
+I __ZdaPvRKSt9nothrow_t (indirect for __ZdaPvRKSt9nothrow_t)
+I __ZdaPvSt11align_val_t (indirect for __ZdaPvSt11align_val_t)
+I __ZdaPvSt11align_val_tRKSt9nothrow_t (indirect for __ZdaPvSt11align_val_tRKSt9nothrow_t)
+I __ZdaPvm (indirect for __ZdaPvm)
+I __ZdaPvmSt11align_val_t (indirect for __ZdaPvmSt11align_val_t)
+I __ZdlPv (indirect for __ZdlPv)
+I __ZdlPvRKSt9nothrow_t (indirect for __ZdlPvRKSt9nothrow_t)
+I __ZdlPvSt11align_val_t (indirect for __ZdlPvSt11align_val_t)
+I __ZdlPvSt11align_val_tRKSt9nothrow_t (indirect for __ZdlPvSt11align_val_tRKSt9nothrow_t)
+I __ZdlPvm (indirect for __ZdlPvm)
+I __ZdlPvmSt11align_val_t (indirect for __ZdlPvmSt11align_val_t)
+I __Znam (indirect for __Znam)
+I __ZnamRKSt9nothrow_t (indirect for __ZnamRKSt9nothrow_t)
+I __ZnamSt11align_val_t (indirect for __ZnamSt11align_val_t)
+I __ZnamSt11align_val_tRKSt9nothrow_t (indirect for __ZnamSt11align_val_tRKSt9nothrow_t)
+I __Znwm (indirect for __Znwm)
+I __ZnwmRKSt9nothrow_t (indirect for __ZnwmRKSt9nothrow_t)
+I __ZnwmSt11align_val_t (indirect for __ZnwmSt11align_val_t)
+I __ZnwmSt11align_val_tRKSt9nothrow_t (indirect for __ZnwmSt11align_val_tRKSt9nothrow_t)
+I ___cxa_allocate_dependent_exception (indirect for ___cxa_allocate_dependent_exception)
+I ___cxa_allocate_exception (indirect for ___cxa_allocate_exception)
+I ___cxa_bad_cast (indirect for ___cxa_bad_cast)
+I ___cxa_bad_typeid (indirect for ___cxa_bad_typeid)
+I ___cxa_begin_catch (indirect for ___cxa_begin_catch)
+I ___cxa_call_terminate (indirect for ___cxa_call_terminate)
+I ___cxa_call_unexpected (indirect for ___cxa_call_unexpected)
+I ___cxa_current_exception_type (indirect for ___cxa_current_exception_type)
+I ___cxa_current_primary_exception (indirect for ___cxa_current_primary_exception)
+I ___cxa_decrement_exception_refcount (indirect for ___cxa_decrement_exception_refcount)
+I ___cxa_deleted_virtual (indirect for ___cxa_deleted_virtual)
+I ___cxa_demangle (indirect for ___cxa_demangle)
+I ___cxa_end_catch (indirect for ___cxa_end_catch)
+I ___cxa_free_dependent_exception (indirect for ___cxa_free_dependent_exception)
+I ___cxa_free_exception (indirect for ___cxa_free_exception)
+I ___cxa_get_exception_ptr (indirect for ___cxa_get_exception_ptr)
+I ___cxa_get_globals (indirect for ___cxa_get_globals)
+I ___cxa_get_globals_fast (indirect for ___cxa_get_globals_fast)
+I ___cxa_guard_abort (indirect for ___cxa_guard_abort)
+I ___cxa_guard_acquire (indirect for ___cxa_guard_acquire)
+I ___cxa_guard_release (indirect for ___cxa_guard_release)
+I ___cxa_increment_exception_refcount (indirect for ___cxa_increment_exception_refcount)
+I ___cxa_init_primary_exception (indirect for ___cxa_init_primary_exception)
+I ___cxa_new_handler (indirect for ___cxa_new_handler)
+I ___cxa_pure_virtual (indirect for ___cxa_pure_virtual)
+I ___cxa_rethrow (indirect for ___cxa_rethrow)
+I ___cxa_terminate_handler (indirect for ___cxa_terminate_handler)
+I ___cxa_throw (indirect for ___cxa_throw)
+I ___cxa_throw_bad_array_new_length (indirect for ___cxa_throw_bad_array_new_length)
+I ___cxa_uncaught_exception (indirect for ___cxa_uncaught_exception)
+I ___cxa_uncaught_exceptions (indirect for ___cxa_uncaught_exceptions)
+I ___cxa_unexpected_handler (indirect for ___cxa_unexpected_handler)
+I ___cxa_vec_cctor (indirect for ___cxa_vec_cctor)
+I ___cxa_vec_cleanup (indirect for ___cxa_vec_cleanup)
+I ___cxa_vec_ctor (indirect for ___cxa_vec_ctor)
+I ___cxa_vec_delete (indirect for ___cxa_vec_delete)
+I ___cxa_vec_delete2 (indirect for ___cxa_vec_delete2)
+I ___cxa_vec_delete3 (indirect for ___cxa_vec_delete3)
+I ___cxa_vec_dtor (indirect for ___cxa_vec_dtor)
+I ___cxa_vec_new (indirect for ___cxa_vec_new)
+I ___cxa_vec_new2 (indirect for ___cxa_vec_new2)
+I ___cxa_vec_new3 (indirect for ___cxa_vec_new3)
+I ___dynamic_cast (indirect for ___dynamic_cast)
+I ___gxx_personality_v0 (indirect for ___gxx_personality_v0)
+T ___muloti4
diff --git a/libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/abilist.sh.cpp b/libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/abilist.sh.cpp
new file mode 100644
index 0000000000000..470821a478758
--- /dev/null
+++ b/libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/abilist.sh.cpp
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FIXME: Encode whether the library was built with exceptions and check the abilist for that case as well
+
+// REQUIRES: target=x86_64-unknown-linux-gnu && !no-exceptions
+
+// Check that the list of symbols exported from the dylib doesn't change unexpectedly
+
+// FILE_DEPENDENCIES: expected.abilist
+
+// RUN: nm --defined-only --extern-only %{lib-dir}/libc++.so.1.0 | cut -c 18- > %t.actual.abilist
+// RUN: diff expected.abilist %t.actual.abilist
diff --git a/libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/expected.abilist b/libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/expected.abilist
new file mode 100644
index 0000000000000..54ede84721087
--- /dev/null
+++ b/libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/expected.abilist
@@ -0,0 +1,1973 @@
+T __muloti4
+T _ZNKSt12bad_any_cast4whatEv
+T _ZNKSt12experimental15fundamentals_v112bad_any_cast4whatEv
+T _ZNKSt16nested_exception14rethrow_nestedEv
+T _ZNKSt18bad_variant_access4whatEv
+T _ZNKSt19bad_optional_access4whatEv
+T _ZNKSt29__bad_variant_access_with_msg4whatEv
+T _ZNKSt3__110error_code7messageEv
+W _ZNKSt3__110moneypunctIcLb0EE11do_groupingEv
+W _ZNKSt3__110moneypunctIcLb0EE13do_neg_formatEv
+W _ZNKSt3__110moneypunctIcLb0EE13do_pos_formatEv
+W _ZNKSt3__110moneypunctIcLb0EE14do_curr_symbolEv
+W _ZNKSt3__110moneypunctIcLb0EE14do_frac_digitsEv
+W _ZNKSt3__110moneypunctIcLb0EE16do_decimal_pointEv
+W _ZNKSt3__110moneypunctIcLb0EE16do_negative_signEv
+W _ZNKSt3__110moneypunctIcLb0EE16do_positive_signEv
+W _ZNKSt3__110moneypunctIcLb0EE16do_thousands_sepEv
+W _ZNKSt3__110moneypunctIcLb1EE11do_groupingEv
+W _ZNKSt3__110moneypunctIcLb1EE13do_neg_formatEv
+W _ZNKSt3__110moneypunctIcLb1EE13do_pos_formatEv
+W _ZNKSt3__110moneypunctIcLb1EE14do_curr_symbolEv
+W _ZNKSt3__110moneypunctIcLb1EE14do_frac_digitsEv
+W _ZNKSt3__110moneypunctIcLb1EE16do_decimal_pointEv
+W _ZNKSt3__110moneypunctIcLb1EE16do_negative_signEv
+W _ZNKSt3__110moneypunctIcLb1EE16do_positive_signEv
+W _ZNKSt3__110moneypunctIcLb1EE16do_thousands_sepEv
+W _ZNKSt3__110moneypunctIwLb0EE11do_groupingEv
+W _ZNKSt3__110moneypunctIwLb0EE13do_neg_formatEv
+W _ZNKSt3__110moneypunctIwLb0EE13do_pos_formatEv
+W _ZNKSt3__110moneypunctIwLb0EE14do_curr_symbolEv
+W _ZNKSt3__110moneypunctIwLb0EE14do_frac_digitsEv
+W _ZNKSt3__110moneypunctIwLb0EE16do_decimal_pointEv
+W _ZNKSt3__110moneypunctIwLb0EE16do_negative_signEv
+W _ZNKSt3__110moneypunctIwLb0EE16do_positive_signEv
+W _ZNKSt3__110moneypunctIwLb0EE16do_thousands_sepEv
+W _ZNKSt3__110moneypunctIwLb1EE11do_groupingEv
+W _ZNKSt3__110moneypunctIwLb1EE13do_neg_formatEv
+W _ZNKSt3__110moneypunctIwLb1EE13do_pos_formatEv
+W _ZNKSt3__110moneypunctIwLb1EE14do_curr_symbolEv
+W _ZNKSt3__110moneypunctIwLb1EE14do_frac_digitsEv
+W _ZNKSt3__110moneypunctIwLb1EE16do_decimal_pointEv
+W _ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv
+W _ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv
+W _ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv
+T _ZNKSt3__110__time_put8__do_putEPcRS1_PK2tmcc
+T _ZNKSt3__110__time_put8__do_putEPwRS1_PK2tmcc
+T _ZNKSt3__112bad_weak_ptr4whatEv
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE16find_last_not_ofEPKcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE17find_first_not_ofEPKcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEcm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEPKcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5rfindEcm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5rfindEPKcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKc
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKcm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmRKS5_mm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEPKc
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE12find_last_ofEPKwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE13find_first_ofEPKwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE16find_last_not_ofEPKwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE17find_first_not_ofEPKwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE2atEm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4copyEPwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4findEPKwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4findEwm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5rfindEPKwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5rfindEwm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmPKw
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmPKwm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmRKS5_mm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEPKw
+T _ZNKSt3__112ctype_bynameIcE10do_tolowerEc
+T _ZNKSt3__112ctype_bynameIcE10do_tolowerEPcPKc
+T _ZNKSt3__112ctype_bynameIcE10do_toupperEc
+T _ZNKSt3__112ctype_bynameIcE10do_toupperEPcPKc
+T _ZNKSt3__112ctype_bynameIwE10do_scan_isEtPKwS3_
+T _ZNKSt3__112ctype_bynameIwE10do_tolowerEPwPKw
+T _ZNKSt3__112ctype_bynameIwE10do_tolowerEw
+T _ZNKSt3__112ctype_bynameIwE10do_toupperEPwPKw
+T _ZNKSt3__112ctype_bynameIwE10do_toupperEw
+T _ZNKSt3__112ctype_bynameIwE11do_scan_notEtPKwS3_
+T _ZNKSt3__112ctype_bynameIwE5do_isEPKwS3_Pt
+T _ZNKSt3__112ctype_bynameIwE5do_isEtw
+T _ZNKSt3__112ctype_bynameIwE8do_widenEc
+T _ZNKSt3__112ctype_bynameIwE8do_widenEPKcS3_Pw
+T _ZNKSt3__112ctype_bynameIwE9do_narrowEPKwS3_cPc
+T _ZNKSt3__112ctype_bynameIwE9do_narrowEwc
+T _ZNKSt3__112strstreambuf6pcountEv
+T _ZNKSt3__113random_device7entropyEv
+T _ZNKSt3__114__codecvt_utf8IDiE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__114__codecvt_utf8IDiE11do_encodingEv
+T _ZNKSt3__114__codecvt_utf8IDiE13do_max_lengthEv
+T _ZNKSt3__114__codecvt_utf8IDiE16do_always_noconvEv
+T _ZNKSt3__114__codecvt_utf8IDiE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T _ZNKSt3__114__codecvt_utf8IDiE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T _ZNKSt3__114__codecvt_utf8IDiE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__114__codecvt_utf8IDsE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__114__codecvt_utf8IDsE11do_encodingEv
+T _ZNKSt3__114__codecvt_utf8IDsE13do_max_lengthEv
+T _ZNKSt3__114__codecvt_utf8IDsE16do_always_noconvEv
+T _ZNKSt3__114__codecvt_utf8IDsE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T _ZNKSt3__114__codecvt_utf8IDsE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T _ZNKSt3__114__codecvt_utf8IDsE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__114__codecvt_utf8IwE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__114__codecvt_utf8IwE11do_encodingEv
+T _ZNKSt3__114__codecvt_utf8IwE13do_max_lengthEv
+T _ZNKSt3__114__codecvt_utf8IwE16do_always_noconvEv
+T _ZNKSt3__114__codecvt_utf8IwE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T _ZNKSt3__114__codecvt_utf8IwE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T _ZNKSt3__114__codecvt_utf8IwE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__114collate_bynameIcE10do_compareEPKcS3_S3_S3_
+T _ZNKSt3__114collate_bynameIcE12do_transformEPKcS3_
+T _ZNKSt3__114collate_bynameIwE10do_compareEPKwS3_S3_S3_
+T _ZNKSt3__114collate_bynameIwE12do_transformEPKwS3_
+T _ZNKSt3__114error_category10equivalentEiRKNS_15error_conditionE
+T _ZNKSt3__114error_category10equivalentERKNS_10error_codeEi
+T _ZNKSt3__114error_category23default_error_conditionEi
+W _ZNKSt3__115basic_streambufIcNS_11char_traitsIcEEE6getlocEv
+W _ZNKSt3__115basic_streambufIwNS_11char_traitsIwEEE6getlocEv
+W _ZNKSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strEv
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE11do_encodingEv
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE13do_max_lengthEv
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE16do_always_noconvEv
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE11do_encodingEv
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE13do_max_lengthEv
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE16do_always_noconvEv
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE11do_encodingEv
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE13do_max_lengthEv
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE16do_always_noconvEv
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE11do_encodingEv
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE13do_max_lengthEv
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE16do_always_noconvEv
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__115__codecvt_utf16IwLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__115__codecvt_utf16IwLb0EE11do_encodingEv
+T _ZNKSt3__115__codecvt_utf16IwLb0EE13do_max_lengthEv
+T _ZNKSt3__115__codecvt_utf16IwLb0EE16do_always_noconvEv
+T _ZNKSt3__115__codecvt_utf16IwLb0EE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IwLb0EE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IwLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__115__codecvt_utf16IwLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__115__codecvt_utf16IwLb1EE11do_encodingEv
+T _ZNKSt3__115__codecvt_utf16IwLb1EE13do_max_lengthEv
+T _ZNKSt3__115__codecvt_utf16IwLb1EE16do_always_noconvEv
+T _ZNKSt3__115__codecvt_utf16IwLb1EE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IwLb1EE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IwLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__115error_condition7messageEv
+T _ZNKSt3__117bad_function_call4whatEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE11do_groupingEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE13do_neg_formatEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE13do_pos_formatEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE14do_curr_symbolEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE14do_frac_digitsEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE16do_decimal_pointEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE16do_negative_signEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE16do_positive_signEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE16do_thousands_sepEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE11do_groupingEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE13do_neg_formatEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE13do_pos_formatEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE14do_curr_symbolEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE14do_frac_digitsEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE16do_decimal_pointEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE16do_negative_signEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE16do_positive_signEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE16do_thousands_sepEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE11do_groupingEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE13do_neg_formatEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE13do_pos_formatEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE14do_curr_symbolEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE14do_frac_digitsEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE16do_decimal_pointEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE16do_negative_signEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE16do_positive_signEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE16do_thousands_sepEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE11do_groupingEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE13do_neg_formatEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE13do_pos_formatEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE14do_curr_symbolEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE14do_frac_digitsEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE16do_decimal_pointEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE16do_negative_signEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE16do_positive_signEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE16do_thousands_sepEv
+T _ZNKSt3__118__time_get_storageIcE15__do_date_orderEv
+T _ZNKSt3__118__time_get_storageIwE15__do_date_orderEv
+T _ZNKSt3__119bad_expected_accessIvE4whatEv
+T _ZNKSt3__119__shared_weak_count13__get_deleterERKSt9type_info
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE11do_encodingEv
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE13do_max_lengthEv
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE16do_always_noconvEv
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE11do_encodingEv
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE13do_max_lengthEv
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE16do_always_noconvEv
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__120__codecvt_utf8_utf16IwE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__120__codecvt_utf8_utf16IwE11do_encodingEv
+T _ZNKSt3__120__codecvt_utf8_utf16IwE13do_max_lengthEv
+T _ZNKSt3__120__codecvt_utf8_utf16IwE16do_always_noconvEv
+T _ZNKSt3__120__codecvt_utf8_utf16IwE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T _ZNKSt3__120__codecvt_utf8_utf16IwE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T _ZNKSt3__120__codecvt_utf8_utf16IwE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__120__time_get_c_storageIcE3__cEv
+T _ZNKSt3__120__time_get_c_storageIcE3__rEv
+T _ZNKSt3__120__time_get_c_storageIcE3__xEv
+T _ZNKSt3__120__time_get_c_storageIcE3__XEv
+T _ZNKSt3__120__time_get_c_storageIcE7__am_pmEv
+T _ZNKSt3__120__time_get_c_storageIcE7__weeksEv
+T _ZNKSt3__120__time_get_c_storageIcE8__monthsEv
+T _ZNKSt3__120__time_get_c_storageIwE3__cEv
+T _ZNKSt3__120__time_get_c_storageIwE3__rEv
+T _ZNKSt3__120__time_get_c_storageIwE3__xEv
+T _ZNKSt3__120__time_get_c_storageIwE3__XEv
+T _ZNKSt3__120__time_get_c_storageIwE7__am_pmEv
+T _ZNKSt3__120__time_get_c_storageIwE7__weeksEv
+T _ZNKSt3__120__time_get_c_storageIwE8__monthsEv
+T _ZNKSt3__120__vector_base_commonILb1EE20__throw_length_errorEv
+T _ZNKSt3__120__vector_base_commonILb1EE20__throw_out_of_rangeEv
+T _ZNKSt3__121__basic_string_commonILb1EE20__throw_length_errorEv
+T _ZNKSt3__121__basic_string_commonILb1EE20__throw_out_of_rangeEv
+T _ZNKSt3__123__match_any_but_newlineIcE6__execERNS_7__stateIcEE
+T _ZNKSt3__123__match_any_but_newlineIwE6__execERNS_7__stateIwEE
+T _ZNKSt3__13pmr26synchronized_pool_resource11do_is_equalERKNS0_15memory_resourceE
+T _ZNKSt3__13pmr28unsynchronized_pool_resource12__pool_indexEmm
+T _ZNKSt3__13pmr28unsynchronized_pool_resource17__pool_block_sizeEi
+T _ZNKSt3__13pmr28unsynchronized_pool_resource22__log2_pool_block_sizeEi
+T _ZNKSt3__13pmr28unsynchronized_pool_resource7optionsEv
+T _ZNKSt3__14__fs10filesystem18directory_iterator13__dereferenceEv
+T _ZNKSt3__14__fs10filesystem28recursive_directory_iterator13__dereferenceEv
+T _ZNKSt3__14__fs10filesystem28recursive_directory_iterator5depthEv
+T _ZNKSt3__14__fs10filesystem28recursive_directory_iterator7optionsEv
+T _ZNKSt3__14__fs10filesystem4path10__filenameEv
+T _ZNKSt3__14__fs10filesystem4path11__extensionEv
+T _ZNKSt3__14__fs10filesystem4path11__root_nameEv
+T _ZNKSt3__14__fs10filesystem4path13__parent_pathEv
+T _ZNKSt3__14__fs10filesystem4path15__relative_pathEv
+T _ZNKSt3__14__fs10filesystem4path15__root_path_rawEv
+T _ZNKSt3__14__fs10filesystem4path16lexically_normalEv
+T _ZNKSt3__14__fs10filesystem4path16__root_directoryEv
+T _ZNKSt3__14__fs10filesystem4path18lexically_relativeERKS2_
+T _ZNKSt3__14__fs10filesystem4path3endEv
+T _ZNKSt3__14__fs10filesystem4path5beginEv
+T _ZNKSt3__14__fs10filesystem4path6__stemEv
+T _ZNKSt3__14__fs10filesystem4path9__compareENS_17basic_string_viewIcNS_11char_traitsIcEEEE
+T _ZNKSt3__15ctypeIcE10do_tolowerEc
+T _ZNKSt3__15ctypeIcE10do_tolowerEPcPKc
+T _ZNKSt3__15ctypeIcE10do_toupperEc
+T _ZNKSt3__15ctypeIcE10do_toupperEPcPKc
+T _ZNKSt3__15ctypeIcE8do_widenEc
+T _ZNKSt3__15ctypeIcE8do_widenEPKcS3_Pc
+T _ZNKSt3__15ctypeIcE9do_narrowEcc
+T _ZNKSt3__15ctypeIcE9do_narrowEPKcS3_cPc
+T _ZNKSt3__15ctypeIwE10do_scan_isEtPKwS3_
+T _ZNKSt3__15ctypeIwE10do_tolowerEPwPKw
+T _ZNKSt3__15ctypeIwE10do_tolowerEw
+T _ZNKSt3__15ctypeIwE10do_toupperEPwPKw
+T _ZNKSt3__15ctypeIwE10do_toupperEw
+T _ZNKSt3__15ctypeIwE11do_scan_notEtPKwS3_
+T _ZNKSt3__15ctypeIwE5do_isEPKwS3_Pt
+T _ZNKSt3__15ctypeIwE5do_isEtw
+T _ZNKSt3__15ctypeIwE8do_widenEc
+T _ZNKSt3__15ctypeIwE8do_widenEPKcS3_Pw
+T _ZNKSt3__15ctypeIwE9do_narrowEPKwS3_cPc
+T _ZNKSt3__15ctypeIwE9do_narrowEwc
+T _ZNKSt3__16locale4nameEv
+T _ZNKSt3__16locale9has_facetERNS0_2idE
+T _ZNKSt3__16locale9use_facetERNS0_2idE
+T _ZNKSt3__16localeeqERKS0_
+T _ZNKSt3__17codecvtIcc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T _ZNKSt3__17codecvtIcc11__mbstate_tE11do_encodingEv
+T _ZNKSt3__17codecvtIcc11__mbstate_tE13do_max_lengthEv
+T _ZNKSt3__17codecvtIcc11__mbstate_tE16do_always_noconvEv
+T _ZNKSt3__17codecvtIcc11__mbstate_tE5do_inERS1_PKcS5_RS5_PcS7_RS7_
+T _ZNKSt3__17codecvtIcc11__mbstate_tE6do_outERS1_PKcS5_RS5_PcS7_RS7_
+T _ZNKSt3__17codecvtIcc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T _ZNKSt3__17codecvtIDic11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T _ZNKSt3__17codecvtIDic11__mbstate_tE11do_encodingEv
+T _ZNKSt3__17codecvtIDic11__mbstate_tE13do_max_lengthEv
+T _ZNKSt3__17codecvtIDic11__mbstate_tE16do_always_noconvEv
+T _ZNKSt3__17codecvtIDic11__mbstate_tE5do_inERS1_PKcS5_RS5_PDiS7_RS7_
+T _ZNKSt3__17codecvtIDic11__mbstate_tE6do_outERS1_PKDiS5_RS5_PcS7_RS7_
+T _ZNKSt3__17codecvtIDic11__mbstate_tE9do_lengthERS1_PKcS5_m
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE10do_unshiftERS1_PDuS4_RS4_
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE11do_encodingEv
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE13do_max_lengthEv
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE16do_always_noconvEv
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE5do_inERS1_PKDuS5_RS5_PDiS7_RS7_
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE6do_outERS1_PKDiS5_RS5_PDuS7_RS7_
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE9do_lengthERS1_PKDuS5_m
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE11do_encodingEv
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE13do_max_lengthEv
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE16do_always_noconvEv
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE5do_inERS1_PKcS5_RS5_PDsS7_RS7_
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE6do_outERS1_PKDsS5_RS5_PcS7_RS7_
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE10do_unshiftERS1_PDuS4_RS4_
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE11do_encodingEv
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE13do_max_lengthEv
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE16do_always_noconvEv
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE5do_inERS1_PKDuS5_RS5_PDsS7_RS7_
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE6do_outERS1_PKDsS5_RS5_PDuS7_RS7_
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE9do_lengthERS1_PKDuS5_m
+T _ZNKSt3__17codecvtIwc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T _ZNKSt3__17codecvtIwc11__mbstate_tE11do_encodingEv
+T _ZNKSt3__17codecvtIwc11__mbstate_tE13do_max_lengthEv
+T _ZNKSt3__17codecvtIwc11__mbstate_tE16do_always_noconvEv
+T _ZNKSt3__17codecvtIwc11__mbstate_tE5do_inERS1_PKcS5_RS5_PwS7_RS7_
+T _ZNKSt3__17codecvtIwc11__mbstate_tE6do_outERS1_PKwS5_RS5_PcS7_RS7_
+T _ZNKSt3__17codecvtIwc11__mbstate_tE9do_lengthERS1_PKcS5_m
+W _ZNKSt3__17collateIcE10do_compareEPKcS3_S3_S3_
+W _ZNKSt3__17collateIcE12do_transformEPKcS3_
+W _ZNKSt3__17collateIcE7do_hashEPKcS3_
+W _ZNKSt3__17collateIwE10do_compareEPKwS3_S3_S3_
+W _ZNKSt3__17collateIwE12do_transformEPKwS3_
+W _ZNKSt3__17collateIwE7do_hashEPKwS3_
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRb
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRd
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRe
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRf
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRl
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRm
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRPv
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRt
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRx
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRy
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjS8_
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRb
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRd
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRe
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRf
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRl
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRm
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRPv
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRt
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRx
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRy
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjS8_
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcb
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcd
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEce
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcl
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcm
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcPKv
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcx
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcy
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwb
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwd
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwe
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwl
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwm
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPKv
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwx
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwy
+T _ZNKSt3__18ios_base6getlocEv
+W _ZNKSt3__18messagesIcE6do_getEliiRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+W _ZNKSt3__18messagesIcE7do_openERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_6localeE
+W _ZNKSt3__18messagesIcE8do_closeEl
+W _ZNKSt3__18messagesIwE6do_getEliiRKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEE
+W _ZNKSt3__18messagesIwE7do_openERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_6localeE
+W _ZNKSt3__18messagesIwE8do_closeEl
+T _ZNKSt3__18numpunctIcE11do_groupingEv
+T _ZNKSt3__18numpunctIcE11do_truenameEv
+T _ZNKSt3__18numpunctIcE12do_falsenameEv
+T _ZNKSt3__18numpunctIcE16do_decimal_pointEv
+T _ZNKSt3__18numpunctIcE16do_thousands_sepEv
+T _ZNKSt3__18numpunctIwE11do_groupingEv
+T _ZNKSt3__18numpunctIwE11do_truenameEv
+T _ZNKSt3__18numpunctIwE12do_falsenameEv
+T _ZNKSt3__18numpunctIwE16do_decimal_pointEv
+T _ZNKSt3__18numpunctIwE16do_thousands_sepEv
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE10__get_hourERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE10__get_yearERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_dateES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_timeES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_yearES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_am_pmERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_monthERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_year4ERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE12__get_minuteERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE12__get_secondERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13do_date_orderEv
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_12_hourERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_percentERS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_weekdayERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE14do_get_weekdayES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE15__get_monthnameERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE16do_get_monthnameES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE17__get_weekdaynameERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE17__get_white_spaceERS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE18__get_day_year_numERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE3getES4_S4_RNS_8ios_baseERjP2tmPKcSC_
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjP2tmcc
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE9__get_dayERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE10__get_hourERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE10__get_yearERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_dateES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_timeES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_yearES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_am_pmERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_monthERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_year4ERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE12__get_minuteERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE12__get_secondERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13do_date_orderEv
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_12_hourERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_percentERS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_weekdayERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE14do_get_weekdayES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE15__get_monthnameERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE16do_get_monthnameES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE17__get_weekdaynameERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE17__get_white_spaceERS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE18__get_day_year_numERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE3getES4_S4_RNS_8ios_baseERjP2tmPKwSC_
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjP2tmcc
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE9__get_dayERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE3putES4_RNS_8ios_baseEcPK2tmPKcSC_
+W _ZNKSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcPK2tmcc
+W _ZNKSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE3putES4_RNS_8ios_baseEwPK2tmPKwSC_
+W _ZNKSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPK2tmcc
+W _ZNKSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_bRNS_8ios_baseERjRe
+W _ZNKSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_bRNS_8ios_baseERjRNS_12basic_stringIcS3_NS_9allocatorIcEEEE
+W _ZNKSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_bRNS_8ios_baseERjRe
+W _ZNKSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_bRNS_8ios_baseERjRNS_12basic_stringIwS3_NS_9allocatorIwEEEE
+W _ZNKSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_bRNS_8ios_baseEce
+W _ZNKSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_bRNS_8ios_baseEcRKNS_12basic_stringIcS3_NS_9allocatorIcEEEE
+W _ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwe
+W _ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwRKNS_12basic_stringIwS3_NS_9allocatorIwEEEE
+T _ZNSt11logic_erroraSERKS_
+T _ZNSt11logic_errorC1EPKc
+T _ZNSt11logic_errorC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T _ZNSt11logic_errorC1ERKS_
+T _ZNSt11logic_errorC2EPKc
+T _ZNSt11logic_errorC2ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T _ZNSt11logic_errorC2ERKS_
+T _ZNSt12experimental19bad_optional_accessD0Ev
+T _ZNSt12experimental19bad_optional_accessD1Ev
+T _ZNSt12experimental19bad_optional_accessD2Ev
+T _ZNSt13exception_ptr31__from_native_exception_pointerEPv
+T _ZNSt13exception_ptraSERKS_
+T _ZNSt13exception_ptrC1ERKS_
+T _ZNSt13exception_ptrC2ERKS_
+T _ZNSt13exception_ptrD1Ev
+T _ZNSt13exception_ptrD2Ev
+T _ZNSt13runtime_erroraSERKS_
+T _ZNSt13runtime_errorC1EPKc
+T _ZNSt13runtime_errorC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T _ZNSt13runtime_errorC1ERKS_
+T _ZNSt13runtime_errorC2EPKc
+T _ZNSt13runtime_errorC2ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T _ZNSt13runtime_errorC2ERKS_
+T _ZNSt16nested_exceptionC1Ev
+T _ZNSt16nested_exceptionC2Ev
+T _ZNSt16nested_exceptionD0Ev
+T _ZNSt16nested_exceptionD1Ev
+T _ZNSt16nested_exceptionD2Ev
+T _ZNSt19bad_optional_accessD0Ev
+T _ZNSt19bad_optional_accessD1Ev
+T _ZNSt19bad_optional_accessD2Ev
+R _ZNSt3__110ctype_base5alnumE
+R _ZNSt3__110ctype_base5alphaE
+R _ZNSt3__110ctype_base5blankE
+R _ZNSt3__110ctype_base5cntrlE
+R _ZNSt3__110ctype_base5digitE
+R _ZNSt3__110ctype_base5graphE
+R _ZNSt3__110ctype_base5lowerE
+R _ZNSt3__110ctype_base5printE
+R _ZNSt3__110ctype_base5punctE
+R _ZNSt3__110ctype_base5spaceE
+R _ZNSt3__110ctype_base5upperE
+R _ZNSt3__110ctype_base6xdigitE
+T _ZNSt3__110istrstreamD0Ev
+T _ZNSt3__110istrstreamD1Ev
+T _ZNSt3__110istrstreamD2Ev
+V _ZNSt3__110moneypunctIcLb0EE2idE
+V _ZNSt3__110moneypunctIcLb0EE4intlE
+V _ZNSt3__110moneypunctIcLb1EE2idE
+V _ZNSt3__110moneypunctIcLb1EE4intlE
+V _ZNSt3__110moneypunctIwLb0EE2idE
+V _ZNSt3__110moneypunctIwLb0EE4intlE
+V _ZNSt3__110moneypunctIwLb1EE2idE
+V _ZNSt3__110moneypunctIwLb1EE4intlE
+T _ZNSt3__110ostrstreamD0Ev
+T _ZNSt3__110ostrstreamD1Ev
+T _ZNSt3__110ostrstreamD2Ev
+T _ZNSt3__110__time_getC1EPKc
+T _ZNSt3__110__time_getC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__110__time_getC2EPKc
+T _ZNSt3__110__time_getC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__110__time_getD1Ev
+T _ZNSt3__110__time_getD2Ev
+T _ZNSt3__110__time_putC1EPKc
+T _ZNSt3__110__time_putC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__110__time_putC2EPKc
+T _ZNSt3__110__time_putC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__110__time_putD1Ev
+T _ZNSt3__110__time_putD2Ev
+T _ZNSt3__110to_wstringEd
+T _ZNSt3__110to_wstringEe
+T _ZNSt3__110to_wstringEf
+T _ZNSt3__110to_wstringEi
+T _ZNSt3__110to_wstringEj
+T _ZNSt3__110to_wstringEl
+T _ZNSt3__110to_wstringEm
+T _ZNSt3__110to_wstringEx
+T _ZNSt3__110to_wstringEy
+T _ZNSt3__111__call_onceERVmPvPFvS2_E
+W _ZNSt3__111__money_getIcE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_SF_Ri
+W _ZNSt3__111__money_getIwE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_SJ_Ri
+W _ZNSt3__111__money_putIcE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_Ri
+W _ZNSt3__111__money_putIcE8__formatEPcRS2_S3_jPKcS5_RKNS_5ctypeIcEEbRKNS_10money_base7patternEccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESL_SL_i
+W _ZNSt3__111__money_putIwE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_Ri
+W _ZNSt3__111__money_putIwE8__formatEPwRS2_S3_jPKwS5_RKNS_5ctypeIwEEbRKNS_10money_base7patternEwwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNSE_IwNSF_IwEENSH_IwEEEESQ_i
+T _ZNSt3__111regex_errorC1ENS_15regex_constants10error_typeE
+T _ZNSt3__111regex_errorC2ENS_15regex_constants10error_typeE
+T _ZNSt3__111regex_errorD0Ev
+T _ZNSt3__111regex_errorD1Ev
+T _ZNSt3__111regex_errorD2Ev
+T _ZNSt3__111this_thread9sleep_forERKNS_6chrono8durationIxNS_5ratioILl1ELl1000000000EEEEE
+T _ZNSt3__111timed_mutex4lockEv
+T _ZNSt3__111timed_mutex6unlockEv
+T _ZNSt3__111timed_mutex8try_lockEv
+T _ZNSt3__111timed_mutexC1Ev
+T _ZNSt3__111timed_mutexC2Ev
+T _ZNSt3__111timed_mutexD1Ev
+T _ZNSt3__111timed_mutexD2Ev
+T _ZNSt3__112bad_weak_ptrD0Ev
+T _ZNSt3__112bad_weak_ptrD1Ev
+T _ZNSt3__112bad_weak_ptrD2Ev
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE21__grow_by_and_replaceEmmmmmmPKc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm
+V _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4nposE
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5eraseEmm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEmc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKcm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendERKS5_mm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEmc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKcm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignERKS5_mm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEmc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcmm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmmc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmPKc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmPKcm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmRKS5_mm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertENS_11__wrap_iterIPKcEEc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6resizeEmc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmmc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmPKc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmPKcm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmRKS5_mm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEmmmmmm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSERKS5_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_mmRKS4_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_RKS4_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_mmRKS4_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_RKS4_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE21__grow_by_and_replaceEmmmmmmPKw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE2atEm
+V _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4nposE
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5eraseEmm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEmw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEPKw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEPKwm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendERKS5_mm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEmw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEPKw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEPKwm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignERKS5_mm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEmw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEPKwm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEPKwmm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmmw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmPKw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmPKwm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmRKS5_mm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertENS_11__wrap_iterIPKwEEw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6resizeEmw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmmw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmPKw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmPKwm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmRKS5_mm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7reserveEm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9__grow_byEmmmmmm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9push_backEw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEaSERKS5_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEaSEw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_mmRKS4_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_RKS4_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_mmRKS4_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_RKS4_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED1Ev
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED2Ev
+T _ZNSt3__112ctype_bynameIcEC1EPKcm
+T _ZNSt3__112ctype_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__112ctype_bynameIcEC2EPKcm
+T _ZNSt3__112ctype_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__112ctype_bynameIcED0Ev
+T _ZNSt3__112ctype_bynameIcED1Ev
+T _ZNSt3__112ctype_bynameIcED2Ev
+T _ZNSt3__112ctype_bynameIwEC1EPKcm
+T _ZNSt3__112ctype_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__112ctype_bynameIwEC2EPKcm
+T _ZNSt3__112ctype_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__112ctype_bynameIwED0Ev
+T _ZNSt3__112ctype_bynameIwED1Ev
+T _ZNSt3__112ctype_bynameIwED2Ev
+T _ZNSt3__112__do_nothingEPv
+T _ZNSt3__112future_errorC1ENS_10error_codeE
+T _ZNSt3__112future_errorC2ENS_10error_codeE
+T _ZNSt3__112future_errorD0Ev
+T _ZNSt3__112future_errorD1Ev
+T _ZNSt3__112future_errorD2Ev
+T _ZNSt3__112__get_sp_mutEPKv
+T _ZNSt3__112__next_primeEm
+R _ZNSt3__112placeholders2_1E
+R _ZNSt3__112placeholders2_2E
+R _ZNSt3__112placeholders2_3E
+R _ZNSt3__112placeholders2_4E
+R _ZNSt3__112placeholders2_5E
+R _ZNSt3__112placeholders2_6E
+R _ZNSt3__112placeholders2_7E
+R _ZNSt3__112placeholders2_8E
+R _ZNSt3__112placeholders2_9E
+R _ZNSt3__112placeholders3_10E
+B _ZNSt3__112__rs_default4__c_E
+T _ZNSt3__112__rs_defaultC1ERKS0_
+T _ZNSt3__112__rs_defaultC1Ev
+T _ZNSt3__112__rs_defaultC2ERKS0_
+T _ZNSt3__112__rs_defaultC2Ev
+T _ZNSt3__112__rs_defaultclEv
+T _ZNSt3__112__rs_defaultD1Ev
+T _ZNSt3__112__rs_defaultD2Ev
+T _ZNSt3__112strstreambuf3strEv
+T _ZNSt3__112strstreambuf4swapERS0_
+T _ZNSt3__112strstreambuf6freezeEb
+T _ZNSt3__112strstreambuf6__initEPclS1_
+T _ZNSt3__112strstreambuf7seekoffExNS_8ios_base7seekdirEj
+T _ZNSt3__112strstreambuf7seekposENS_4fposI11__mbstate_tEEj
+T _ZNSt3__112strstreambuf8overflowEi
+T _ZNSt3__112strstreambuf9pbackfailEi
+T _ZNSt3__112strstreambuf9underflowEv
+T _ZNSt3__112strstreambufC1El
+T _ZNSt3__112strstreambufC1EPalS1_
+T _ZNSt3__112strstreambufC1EPclS1_
+T _ZNSt3__112strstreambufC1EPFPvmEPFvS1_E
+T _ZNSt3__112strstreambufC1EPhlS1_
+T _ZNSt3__112strstreambufC1EPKal
+T _ZNSt3__112strstreambufC1EPKcl
+T _ZNSt3__112strstreambufC1EPKhl
+T _ZNSt3__112strstreambufC2El
+T _ZNSt3__112strstreambufC2EPalS1_
+T _ZNSt3__112strstreambufC2EPclS1_
+T _ZNSt3__112strstreambufC2EPFPvmEPFvS1_E
+T _ZNSt3__112strstreambufC2EPhlS1_
+T _ZNSt3__112strstreambufC2EPKal
+T _ZNSt3__112strstreambufC2EPKcl
+T _ZNSt3__112strstreambufC2EPKhl
+T _ZNSt3__112strstreambufD0Ev
+T _ZNSt3__112strstreambufD1Ev
+T _ZNSt3__112strstreambufD2Ev
+T _ZNSt3__112system_errorC1EiRKNS_14error_categoryE
+T _ZNSt3__112system_errorC1EiRKNS_14error_categoryEPKc
+T _ZNSt3__112system_errorC1EiRKNS_14error_categoryERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__112system_errorC1ENS_10error_codeE
+T _ZNSt3__112system_errorC1ENS_10error_codeEPKc
+T _ZNSt3__112system_errorC1ENS_10error_codeERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__112system_errorC2EiRKNS_14error_categoryE
+T _ZNSt3__112system_errorC2EiRKNS_14error_categoryEPKc
+T _ZNSt3__112system_errorC2EiRKNS_14error_categoryERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__112system_errorC2ENS_10error_codeE
+T _ZNSt3__112system_errorC2ENS_10error_codeEPKc
+T _ZNSt3__112system_errorC2ENS_10error_codeERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__112system_errorD0Ev
+T _ZNSt3__112system_errorD1Ev
+T _ZNSt3__112system_errorD2Ev
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE11__read_modeEv
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE12__write_modeEv
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4openEPKcj
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4swapERS3_
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4syncEv
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5closeEv
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE6setbufEPcl
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekposENS_4fposI11__mbstate_tEEj
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE8overflowEi
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9pbackfailEi
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9underflowEv
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC1EOS3_
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC1Ev
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2EOS3_
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2Ev
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED0Ev
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED1Ev
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED2Ev
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEPcl
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEPclc
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERc
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERNS_15basic_streambufIcS2_EEc
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEv
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4peekEv
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4readEPcl
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4swapERS3_
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4syncEv
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5seekgENS_4fposI11__mbstate_tEE
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5seekgExNS_8ios_base7seekdirE
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5tellgEv
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5ungetEv
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6ignoreEli
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6sentryC1ERS3_b
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6sentryC2ERS3_b
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7getlineEPcl
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7getlineEPclc
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7putbackEc
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE8readsomeEPcl
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED0Ev
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED1Ev
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED2Ev
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRNS_8ios_baseES5_E
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRNS_9basic_iosIcS2_EES6_E
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRS3_S4_E
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERb
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERd
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERe
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERf
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERi
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERj
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERl
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERm
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERPv
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERs
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERt
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERx
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERy
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEPwl
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEPwlw
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERNS_15basic_streambufIwS2_EEw
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERw
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEv
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4peekEv
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4readEPwl
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4swapERS3_
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4syncEv
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5seekgENS_4fposI11__mbstate_tEE
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5seekgExNS_8ios_base7seekdirE
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5tellgEv
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5ungetEv
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6ignoreElj
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6sentryC1ERS3_b
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6sentryC2ERS3_b
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7getlineEPwl
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7getlineEPwlw
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7putbackEw
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE8readsomeEPwl
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEEC1EPNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEEC2EPNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED2Ev
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRNS_8ios_baseES5_E
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRNS_9basic_iosIwS2_EES6_E
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRS3_S4_E
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERb
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERd
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERe
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERf
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERi
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERj
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERl
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERm
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERPv
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERs
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERt
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERx
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERy
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE3putEc
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE4swapERS3_
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5seekpENS_4fposI11__mbstate_tEE
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5seekpExNS_8ios_base7seekdirE
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5tellpEv
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5writeEPKcl
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC2ERS3_
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD2Ev
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED2Ev
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEb
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEd
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEe
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEf
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEi
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEj
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEl
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEm
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRNS_8ios_baseES5_E
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRNS_9basic_iosIcS2_EES6_E
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRS3_S4_E
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPKv
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEs
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEt
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEx
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEy
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE3putEw
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE4swapERS3_
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5flushEv
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5seekpENS_4fposI11__mbstate_tEE
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5seekpExNS_8ios_base7seekdirE
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5tellpEv
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5writeEPKwl
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryC1ERS3_
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryC2ERS3_
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryD1Ev
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryD2Ev
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEEC1EPNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEEC2EPNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED0Ev
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED1Ev
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED2Ev
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEb
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEd
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEe
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEf
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEi
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEj
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEl
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEm
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRNS_8ios_baseES5_E
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRNS_9basic_iosIwS2_EES6_E
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRS3_S4_E
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPKv
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEs
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEt
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEx
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEy
+T _ZNSt3__113__hash_memoryEPKvm
+T _ZNSt3__113random_deviceC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__113random_deviceC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__113random_deviceclEv
+T _ZNSt3__113random_deviceD1Ev
+T _ZNSt3__113random_deviceD2Ev
+T _ZNSt3__113shared_futureIvEaSERKS1_
+T _ZNSt3__113shared_futureIvED1Ev
+T _ZNSt3__113shared_futureIvED2Ev
+W _ZNSt3__114basic_ifstreamIcNS_11char_traitsIcEEE4openEPKcj
+W _ZNSt3__114basic_ifstreamIcNS_11char_traitsIcEEE4openERKNS_12basic_stringIcS2_NS_9allocatorIcEEEEj
+W _ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEE4swapERS3_
+W _ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+W _ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+W _ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+W _ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+W _ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED2Ev
+W _ZNSt3__114basic_ofstreamIcNS_11char_traitsIcEEE4openEPKcj
+W _ZNSt3__114basic_ofstreamIcNS_11char_traitsIcEEE4openERKNS_12basic_stringIcS2_NS_9allocatorIcEEEEj
+W _ZNSt3__114codecvt_bynameIcc11__mbstate_tED0Ev
+W _ZNSt3__114codecvt_bynameIcc11__mbstate_tED1Ev
+W _ZNSt3__114codecvt_bynameIcc11__mbstate_tED2Ev
+W _ZNSt3__114codecvt_bynameIDic11__mbstate_tED0Ev
+W _ZNSt3__114codecvt_bynameIDic11__mbstate_tED1Ev
+W _ZNSt3__114codecvt_bynameIDic11__mbstate_tED2Ev
+W _ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED0Ev
+W _ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED1Ev
+W _ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED2Ev
+W _ZNSt3__114codecvt_bynameIDsc11__mbstate_tED0Ev
+W _ZNSt3__114codecvt_bynameIDsc11__mbstate_tED1Ev
+W _ZNSt3__114codecvt_bynameIDsc11__mbstate_tED2Ev
+W _ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED0Ev
+W _ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED1Ev
+W _ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED2Ev
+W _ZNSt3__114codecvt_bynameIwc11__mbstate_tED0Ev
+W _ZNSt3__114codecvt_bynameIwc11__mbstate_tED1Ev
+W _ZNSt3__114codecvt_bynameIwc11__mbstate_tED2Ev
+T _ZNSt3__114collate_bynameIcEC1EPKcm
+T _ZNSt3__114collate_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__114collate_bynameIcEC2EPKcm
+T _ZNSt3__114collate_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__114collate_bynameIcED0Ev
+T _ZNSt3__114collate_bynameIcED1Ev
+T _ZNSt3__114collate_bynameIcED2Ev
+T _ZNSt3__114collate_bynameIwEC1EPKcm
+T _ZNSt3__114collate_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__114collate_bynameIwEC2EPKcm
+T _ZNSt3__114collate_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__114collate_bynameIwED0Ev
+T _ZNSt3__114collate_bynameIwED1Ev
+T _ZNSt3__114collate_bynameIwED2Ev
+T _ZNSt3__114error_categoryC2Ev
+T _ZNSt3__114error_categoryD0Ev
+T _ZNSt3__114error_categoryD1Ev
+T _ZNSt3__114error_categoryD2Ev
+T _ZNSt3__114__num_get_base10__get_baseERNS_8ios_baseE
+R _ZNSt3__114__num_get_base5__srcE
+T _ZNSt3__114__num_put_base12__format_intEPcPKcbj
+T _ZNSt3__114__num_put_base14__format_floatEPcPKcj
+T _ZNSt3__114__num_put_base18__identify_paddingEPcS1_RKNS_8ios_baseE
+T _ZNSt3__114__shared_count12__add_sharedEv
+T _ZNSt3__114__shared_count16__release_sharedEv
+T _ZNSt3__114__shared_countD0Ev
+T _ZNSt3__114__shared_countD1Ev
+T _ZNSt3__114__shared_countD2Ev
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE10pubseekoffExNS_8ios_base7seekdirEj
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE10pubseekposENS_4fposI11__mbstate_tEEj
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4setgEPcS4_S4_
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4setpEPcS4_
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4swapERS3_
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4syncEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5gbumpEi
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5pbumpEi
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sgetcEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sgetnEPcl
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputcEc
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputnEPKcl
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5uflowEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6sbumpcEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6setbufEPcl
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6snextcEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsgetnEPcl
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsputnEPKcl
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7pubsyncEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7seekposENS_4fposI11__mbstate_tEEj
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7sungetcEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8in_availEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8overflowEi
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8pubimbueERKNS_6localeE
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9pbackfailEi
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9pubsetbufEPcl
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9showmanycEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9sputbackcEc
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9underflowEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEaSERKS3_
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC1ERKS3_
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC1Ev
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2ERKS3_
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2Ev
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED0Ev
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED1Ev
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED2Ev
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE10pubseekoffExNS_8ios_base7seekdirEj
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE10pubseekposENS_4fposI11__mbstate_tEEj
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4setgEPwS4_S4_
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4setpEPwS4_
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4swapERS3_
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4syncEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5gbumpEi
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5imbueERKNS_6localeE
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5pbumpEi
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sgetcEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sgetnEPwl
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sputcEw
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sputnEPKwl
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5uflowEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6sbumpcEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6setbufEPwl
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6snextcEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6xsgetnEPwl
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6xsputnEPKwl
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7pubsyncEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7seekoffExNS_8ios_base7seekdirEj
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7seekposENS_4fposI11__mbstate_tEEj
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7sungetcEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8in_availEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8overflowEj
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8pubimbueERKNS_6localeE
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9pbackfailEj
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9pubsetbufEPwl
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9showmanycEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9sputbackcEw
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9underflowEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEaSERKS3_
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC1ERKS3_
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC1Ev
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC2ERKS3_
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC2Ev
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED0Ev
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED1Ev
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED2Ev
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strERKNS_12basic_stringIcS2_S4_EE
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE4swapERS5_
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE7seekoffExNS_8ios_base7seekdirEj
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE8overflowEi
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9pbackfailEi
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9underflowEv
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEC1EOS5_
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEC2EOS5_
+T _ZNSt3__115future_categoryEv
+T _ZNSt3__115__get_classnameEPKcb
+T _ZNSt3__115numpunct_bynameIcEC1EPKcm
+T _ZNSt3__115numpunct_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__115numpunct_bynameIcEC2EPKcm
+T _ZNSt3__115numpunct_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__115numpunct_bynameIcED0Ev
+T _ZNSt3__115numpunct_bynameIcED1Ev
+T _ZNSt3__115numpunct_bynameIcED2Ev
+T _ZNSt3__115numpunct_bynameIwEC1EPKcm
+T _ZNSt3__115numpunct_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__115numpunct_bynameIwEC2EPKcm
+T _ZNSt3__115numpunct_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__115numpunct_bynameIwED0Ev
+T _ZNSt3__115numpunct_bynameIwED1Ev
+T _ZNSt3__115numpunct_bynameIwED2Ev
+T _ZNSt3__115recursive_mutex4lockEv
+T _ZNSt3__115recursive_mutex6unlockEv
+T _ZNSt3__115recursive_mutex8try_lockEv
+T _ZNSt3__115recursive_mutexC1Ev
+T _ZNSt3__115recursive_mutexC2Ev
+T _ZNSt3__115recursive_mutexD1Ev
+T _ZNSt3__115recursive_mutexD2Ev
+T _ZNSt3__115system_categoryEv
+T _ZNSt3__115__thread_struct25notify_all_at_thread_exitEPNS_18condition_variableEPNS_5mutexE
+T _ZNSt3__115__thread_struct27__make_ready_at_thread_exitEPNS_17__assoc_sub_stateE
+T _ZNSt3__115__thread_structC1Ev
+T _ZNSt3__115__thread_structC2Ev
+T _ZNSt3__115__thread_structD1Ev
+T _ZNSt3__115__thread_structD2Ev
+T _ZNSt3__116__check_groupingERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjS8_Rj
+T _ZNSt3__116generic_categoryEv
+T _ZNSt3__116__narrow_to_utf8ILm16EED0Ev
+T _ZNSt3__116__narrow_to_utf8ILm16EED1Ev
+T _ZNSt3__116__narrow_to_utf8ILm16EED2Ev
+T _ZNSt3__116__narrow_to_utf8ILm32EED0Ev
+T _ZNSt3__116__narrow_to_utf8ILm32EED1Ev
+T _ZNSt3__116__narrow_to_utf8ILm32EED2Ev
+T _ZNSt3__117__assoc_sub_state10__sub_waitERNS_11unique_lockINS_5mutexEEE
+T _ZNSt3__117__assoc_sub_state12__make_readyEv
+T _ZNSt3__117__assoc_sub_state13set_exceptionESt13exception_ptr
+T _ZNSt3__117__assoc_sub_state16__on_zero_sharedEv
+T _ZNSt3__117__assoc_sub_state24set_value_at_thread_exitEv
+T _ZNSt3__117__assoc_sub_state28set_exception_at_thread_exitESt13exception_ptr
+T _ZNSt3__117__assoc_sub_state4copyEv
+T _ZNSt3__117__assoc_sub_state4waitEv
+T _ZNSt3__117__assoc_sub_state9__executeEv
+T _ZNSt3__117__assoc_sub_state9set_valueEv
+T _ZNSt3__117bad_function_callD0Ev
+T _ZNSt3__117bad_function_callD1Ev
+T _ZNSt3__117bad_function_callD2Ev
+T _ZNSt3__117iostream_categoryEv
+T _ZNSt3__117moneypunct_bynameIcLb0EE4initEPKc
+T _ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc
+T _ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc
+T _ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc
+T _ZNSt3__117__widen_from_utf8ILm16EED0Ev
+T _ZNSt3__117__widen_from_utf8ILm16EED1Ev
+T _ZNSt3__117__widen_from_utf8ILm16EED2Ev
+T _ZNSt3__117__widen_from_utf8ILm32EED0Ev
+T _ZNSt3__117__widen_from_utf8ILm32EED1Ev
+T _ZNSt3__117__widen_from_utf8ILm32EED2Ev
+W _ZNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T _ZNSt3__118condition_variable10notify_allEv
+T _ZNSt3__118condition_variable10notify_oneEv
+T _ZNSt3__118condition_variable15__do_timed_waitERNS_11unique_lockINS_5mutexEEENS_6chrono10time_pointINS5_12system_clockENS5_8durationIxNS_5ratioILl1ELl1000000000EEEEEEE
+T _ZNSt3__118condition_variable4waitERNS_11unique_lockINS_5mutexEEE
+T _ZNSt3__118condition_variableD1Ev
+T _ZNSt3__118condition_variableD2Ev
+T _ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE
+T _ZNSt3__118shared_timed_mutex11lock_sharedEv
+T _ZNSt3__118shared_timed_mutex13unlock_sharedEv
+T _ZNSt3__118shared_timed_mutex15try_lock_sharedEv
+T _ZNSt3__118shared_timed_mutex4lockEv
+T _ZNSt3__118shared_timed_mutex6unlockEv
+T _ZNSt3__118shared_timed_mutex8try_lockEv
+T _ZNSt3__118shared_timed_mutexC1Ev
+T _ZNSt3__118shared_timed_mutexC2Ev
+T _ZNSt3__118__time_get_storageIcEC1EPKc
+T _ZNSt3__118__time_get_storageIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__118__time_get_storageIcEC2EPKc
+T _ZNSt3__118__time_get_storageIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__118__time_get_storageIwEC1EPKc
+T _ZNSt3__118__time_get_storageIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__118__time_get_storageIwEC2EPKc
+T _ZNSt3__118__time_get_storageIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+W _ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+W _ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T _ZNSt3__119__is_posix_terminalEP8_IO_FILE
+T _ZNSt3__119__shared_mutex_base11lock_sharedEv
+T _ZNSt3__119__shared_mutex_base13unlock_sharedEv
+T _ZNSt3__119__shared_mutex_base15try_lock_sharedEv
+T _ZNSt3__119__shared_mutex_base4lockEv
+T _ZNSt3__119__shared_mutex_base6unlockEv
+T _ZNSt3__119__shared_mutex_base8try_lockEv
+T _ZNSt3__119__shared_mutex_baseC1Ev
+T _ZNSt3__119__shared_mutex_baseC2Ev
+T _ZNSt3__119__shared_weak_count10__add_weakEv
+T _ZNSt3__119__shared_weak_count12__add_sharedEv
+T _ZNSt3__119__shared_weak_count14__release_weakEv
+T _ZNSt3__119__shared_weak_count16__release_sharedEv
+T _ZNSt3__119__shared_weak_count4lockEv
+T _ZNSt3__119__shared_weak_countD0Ev
+T _ZNSt3__119__shared_weak_countD1Ev
+T _ZNSt3__119__shared_weak_countD2Ev
+T _ZNSt3__119__thread_local_dataEv
+W _ZNSt3__120__atomic_wait_nativeILm4EEEvPKvS2_
+T _ZNSt3__120__get_collation_nameEPKc
+T _ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEEi
+T _ZNSt3__120__libcpp_atomic_waitEPVKvi
+T _ZNSt3__120__throw_system_errorEiPKc
+T _ZNSt3__121__get_locale_encodingEPKc
+T _ZNSt3__121recursive_timed_mutex4lockEv
+T _ZNSt3__121recursive_timed_mutex6unlockEv
+T _ZNSt3__121recursive_timed_mutex8try_lockEv
+T _ZNSt3__121recursive_timed_mutexC1Ev
+T _ZNSt3__121recursive_timed_mutexC2Ev
+T _ZNSt3__121recursive_timed_mutexD1Ev
+T _ZNSt3__121recursive_timed_mutexD2Ev
+T _ZNSt3__121__throw_runtime_errorEPKc
+W _ZNSt3__122__libcpp_verbose_abortEPKcz
+T _ZNSt3__123__atomic_monitor_globalEPKv
+T _ZNSt3__123__cxx_atomic_notify_allEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE
+T _ZNSt3__123__cxx_atomic_notify_allEPVKv
+T _ZNSt3__123__cxx_atomic_notify_oneEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE
+T _ZNSt3__123__cxx_atomic_notify_oneEPVKv
+T _ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE
+T _ZNSt3__123__libcpp_atomic_monitorEPVKv
+T _ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE
+W _ZNSt3__126__atomic_notify_all_nativeILm4EEEvPKv
+W _ZNSt3__126__atomic_notify_one_nativeILm4EEEvPKv
+T _ZNSt3__126__atomic_wait_global_tableEPKvi
+W _ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+W _ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+T _ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh
+T _ZNSt3__132__atomic_notify_all_global_tableEPKv
+T _ZNSt3__132__atomic_notify_one_global_tableEPKv
+T _ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE
+W _ZNSt3__133__atomic_wait_native_with_timeoutILm4EEEvPKvS2_m
+T _ZNSt3__134__construct_barrier_algorithm_baseERl
+T _ZNSt3__139__atomic_wait_global_table_with_timeoutEPKvim
+B _ZNSt3__13cinE
+T _ZNSt3__13pmr15memory_resourceD0Ev
+T _ZNSt3__13pmr15memory_resourceD1Ev
+T _ZNSt3__13pmr15memory_resourceD2Ev
+T _ZNSt3__13pmr19new_delete_resourceEv
+T _ZNSt3__13pmr20get_default_resourceEv
+T _ZNSt3__13pmr20null_memory_resourceEv
+T _ZNSt3__13pmr20set_default_resourceEPNS0_15memory_resourceE
+T _ZNSt3__13pmr25monotonic_buffer_resource11do_allocateEmm
+T _ZNSt3__13pmr28unsynchronized_pool_resource11do_allocateEmm
+T _ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__do_allocateEPNS0_15memory_resourceEmm
+T _ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__release_ptrEPNS0_15memory_resourceE
+T _ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool15__do_deallocateEPNS0_15memory_resourceEPvmm
+T _ZNSt3__13pmr28unsynchronized_pool_resource13do_deallocateEPvmm
+T _ZNSt3__13pmr28unsynchronized_pool_resource7releaseEv
+T _ZNSt3__13pmr28unsynchronized_pool_resourceC1ERKNS0_12pool_optionsEPNS0_15memory_resourceE
+T _ZNSt3__13pmr28unsynchronized_pool_resourceC2ERKNS0_12pool_optionsEPNS0_15memory_resourceE
+B _ZNSt3__14cerrE
+B _ZNSt3__14clogE
+B _ZNSt3__14coutE
+T _ZNSt3__14__fs10filesystem10__absoluteERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem10hash_valueERKNS1_4pathE
+T _ZNSt3__14__fs10filesystem11__canonicalERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem11__copy_fileERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem11__file_sizeERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem12__equivalentERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem12__remove_allERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem13__fs_is_emptyERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem13__permissionsERKNS1_4pathENS1_5permsENS1_12perm_optionsEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem13__resize_fileERKNS1_4pathEmPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem14__copy_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem14__current_pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem14__current_pathERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem14__read_symlinkERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem15directory_entry12__do_refreshEv
+T _ZNSt3__14__fs10filesystem16__create_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem16_FilesystemClock3nowEv
+V _ZNSt3__14__fs10filesystem16_FilesystemClock9is_steadyE
+T _ZNSt3__14__fs10filesystem16filesystem_error13__create_whatEi
+T _ZNSt3__14__fs10filesystem16filesystem_errorD0Ev
+T _ZNSt3__14__fs10filesystem16filesystem_errorD1Ev
+T _ZNSt3__14__fs10filesystem16filesystem_errorD2Ev
+T _ZNSt3__14__fs10filesystem16__symlink_statusERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem17__hard_link_countERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem17__last_write_timeERKNS1_4pathENS_6chrono10time_pointINS1_16_FilesystemClockENS5_8durationInNS_5ratioILl1ELl1000000000EEEEEEEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem17__last_write_timeERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem18__create_directoryERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem18__create_directoryERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem18__create_hard_linkERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem18directory_iterator11__incrementEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem18directory_iteratorC1ERKNS1_4pathEPNS_10error_codeENS1_17directory_optionsE
+T _ZNSt3__14__fs10filesystem18directory_iteratorC2ERKNS1_4pathEPNS_10error_codeENS1_17directory_optionsE
+T _ZNSt3__14__fs10filesystem18__weakly_canonicalERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem20__create_directoriesERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem21__temp_directory_pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem26__create_directory_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem28recursive_directory_iterator11__incrementEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem28recursive_directory_iterator15__try_recursionEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem28recursive_directory_iterator5__popEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem28recursive_directory_iterator9__advanceEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem28recursive_directory_iteratorC1ERKNS1_4pathENS1_17directory_optionsEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem28recursive_directory_iteratorC2ERKNS1_4pathENS1_17directory_optionsEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem4path17replace_extensionERKS2_
+V _ZNSt3__14__fs10filesystem4path19preferred_separatorE
+T _ZNSt3__14__fs10filesystem4path8iterator11__decrementEv
+T _ZNSt3__14__fs10filesystem4path8iterator11__incrementEv
+T _ZNSt3__14__fs10filesystem6__copyERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem7__spaceERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem8__statusERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14stodERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T _ZNSt3__14stodERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T _ZNSt3__14stofERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T _ZNSt3__14stofERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T _ZNSt3__14stoiERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T _ZNSt3__14stoiERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T _ZNSt3__14stolERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T _ZNSt3__14stolERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+B _ZNSt3__14wcinE
+T _ZNSt3__15alignEmmRPvRm
+R _ZNSt3__15ctypeIcE10table_sizeE
+T _ZNSt3__15ctypeIcE13classic_tableEv
+B _ZNSt3__15ctypeIcE2idE
+T _ZNSt3__15ctypeIcEC1EPKtbm
+T _ZNSt3__15ctypeIcEC2EPKtbm
+T _ZNSt3__15ctypeIcED0Ev
+T _ZNSt3__15ctypeIcED1Ev
+T _ZNSt3__15ctypeIcED2Ev
+B _ZNSt3__15ctypeIwE2idE
+T _ZNSt3__15ctypeIwED0Ev
+T _ZNSt3__15ctypeIwED1Ev
+T _ZNSt3__15ctypeIwED2Ev
+T _ZNSt3__15mutex4lockEv
+T _ZNSt3__15mutex6unlockEv
+T _ZNSt3__15mutex8try_lockEv
+T _ZNSt3__15mutexD1Ev
+T _ZNSt3__15mutexD2Ev
+T _ZNSt3__15stoldERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T _ZNSt3__15stoldERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T _ZNSt3__15stollERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T _ZNSt3__15stollERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T _ZNSt3__15stoulERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T _ZNSt3__15stoulERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+B _ZNSt3__15wcerrE
+B _ZNSt3__15wclogE
+B _ZNSt3__15wcoutE
+T _ZNSt3__16chrono12steady_clock3nowEv
+V _ZNSt3__16chrono12steady_clock9is_steadyE
+T _ZNSt3__16chrono12system_clock11from_time_tEl
+T _ZNSt3__16chrono12system_clock3nowEv
+V _ZNSt3__16chrono12system_clock9is_steadyE
+T _ZNSt3__16chrono12system_clock9to_time_tERKNS0_10time_pointIS1_NS0_8durationIxNS_5ratioILl1ELl1000000EEEEEEE
+T _ZNSt3__16__clocEv
+T _ZNSt3__16futureIvE3getEv
+T _ZNSt3__16futureIvEC1EPNS_17__assoc_sub_stateE
+T _ZNSt3__16futureIvEC2EPNS_17__assoc_sub_stateE
+T _ZNSt3__16futureIvED1Ev
+T _ZNSt3__16futureIvED2Ev
+T _ZNSt3__16gslice6__initEm
+T _ZNSt3__16__itoa8__u32toaEjPc
+T _ZNSt3__16__itoa8__u64toaEmPc
+T _ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl
+T _ZNSt3__16locale2id5__getEv
+B _ZNSt3__16locale2id9__next_idE
+R _ZNSt3__16locale3allE
+R _ZNSt3__16locale4noneE
+R _ZNSt3__16locale4timeE
+R _ZNSt3__16locale5ctypeE
+T _ZNSt3__16locale5facet16__on_zero_sharedEv
+T _ZNSt3__16locale5facetD0Ev
+T _ZNSt3__16locale5facetD1Ev
+T _ZNSt3__16locale5facetD2Ev
+T _ZNSt3__16locale6globalERKS0_
+T _ZNSt3__16locale7classicEv
+R _ZNSt3__16locale7collateE
+R _ZNSt3__16locale7numericE
+T _ZNSt3__16locale8__globalEv
+R _ZNSt3__16locale8messagesE
+R _ZNSt3__16locale8monetaryE
+T _ZNSt3__16localeaSERKS0_
+T _ZNSt3__16localeC1EPKc
+T _ZNSt3__16localeC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__16localeC1ERKS0_
+T _ZNSt3__16localeC1ERKS0_PKci
+T _ZNSt3__16localeC1ERKS0_RKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEi
+T _ZNSt3__16localeC1ERKS0_S2_i
+T _ZNSt3__16localeC1Ev
+T _ZNSt3__16localeC2EPKc
+T _ZNSt3__16localeC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__16localeC2ERKS0_
+T _ZNSt3__16localeC2ERKS0_PKci
+T _ZNSt3__16localeC2ERKS0_RKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEi
+T _ZNSt3__16localeC2ERKS0_S2_i
+T _ZNSt3__16localeC2Ev
+T _ZNSt3__16localeD1Ev
+T _ZNSt3__16localeD2Ev
+W _ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIeeEEPeEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIffEEPfEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIhhEEPhEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIiiEEPiEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIjjEEPjEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIllEEPlEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessImmEEPmEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIssEEPsEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIttEEPtEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIwwEEPwEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIxxEEPxEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIyyEEPyEEvT0_S5_T_
+T _ZNSt3__16stoullERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T _ZNSt3__16stoullERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T _ZNSt3__16thread20hardware_concurrencyEv
+T _ZNSt3__16thread4joinEv
+T _ZNSt3__16thread6detachEv
+T _ZNSt3__16threadD1Ev
+T _ZNSt3__16threadD2Ev
+B _ZNSt3__17codecvtIcc11__mbstate_tE2idE
+T _ZNSt3__17codecvtIcc11__mbstate_tED0Ev
+T _ZNSt3__17codecvtIcc11__mbstate_tED1Ev
+T _ZNSt3__17codecvtIcc11__mbstate_tED2Ev
+B _ZNSt3__17codecvtIDic11__mbstate_tE2idE
+T _ZNSt3__17codecvtIDic11__mbstate_tED0Ev
+T _ZNSt3__17codecvtIDic11__mbstate_tED1Ev
+T _ZNSt3__17codecvtIDic11__mbstate_tED2Ev
+B _ZNSt3__17codecvtIDiDu11__mbstate_tE2idE
+T _ZNSt3__17codecvtIDiDu11__mbstate_tED0Ev
+T _ZNSt3__17codecvtIDiDu11__mbstate_tED1Ev
+T _ZNSt3__17codecvtIDiDu11__mbstate_tED2Ev
+B _ZNSt3__17codecvtIDsc11__mbstate_tE2idE
+T _ZNSt3__17codecvtIDsc11__mbstate_tED0Ev
+T _ZNSt3__17codecvtIDsc11__mbstate_tED1Ev
+T _ZNSt3__17codecvtIDsc11__mbstate_tED2Ev
+B _ZNSt3__17codecvtIDsDu11__mbstate_tE2idE
+T _ZNSt3__17codecvtIDsDu11__mbstate_tED0Ev
+T _ZNSt3__17codecvtIDsDu11__mbstate_tED1Ev
+T _ZNSt3__17codecvtIDsDu11__mbstate_tED2Ev
+B _ZNSt3__17codecvtIwc11__mbstate_tE2idE
+T _ZNSt3__17codecvtIwc11__mbstate_tEC1Em
+T _ZNSt3__17codecvtIwc11__mbstate_tEC1EPKcm
+T _ZNSt3__17codecvtIwc11__mbstate_tEC2Em
+T _ZNSt3__17codecvtIwc11__mbstate_tEC2EPKcm
+T _ZNSt3__17codecvtIwc11__mbstate_tED0Ev
+T _ZNSt3__17codecvtIwc11__mbstate_tED1Ev
+T _ZNSt3__17codecvtIwc11__mbstate_tED2Ev
+V _ZNSt3__17collateIcE2idE
+W _ZNSt3__17collateIcED0Ev
+W _ZNSt3__17collateIcED1Ev
+W _ZNSt3__17collateIcED2Ev
+V _ZNSt3__17collateIwE2idE
+W _ZNSt3__17collateIwED0Ev
+W _ZNSt3__17collateIwED1Ev
+W _ZNSt3__17collateIwED2Ev
+V _ZNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+V _ZNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+V _ZNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+V _ZNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T _ZNSt3__17promiseIvE10get_futureEv
+T _ZNSt3__17promiseIvE13set_exceptionESt13exception_ptr
+T _ZNSt3__17promiseIvE24set_value_at_thread_exitEv
+T _ZNSt3__17promiseIvE28set_exception_at_thread_exitESt13exception_ptr
+T _ZNSt3__17promiseIvE9set_valueEv
+T _ZNSt3__17promiseIvEC1Ev
+T _ZNSt3__17promiseIvEC2Ev
+T _ZNSt3__17promiseIvED1Ev
+T _ZNSt3__17promiseIvED2Ev
+R _ZNSt3__18ios_base10floatfieldE
+R _ZNSt3__18ios_base10scientificE
+R _ZNSt3__18ios_base11adjustfieldE
+T _ZNSt3__18ios_base15sync_with_stdioEb
+T _ZNSt3__18ios_base16__call_callbacksENS0_5eventE
+T _ZNSt3__18ios_base17register_callbackEPFvNS0_5eventERS0_iEi
+R _ZNSt3__18ios_base2inE
+T _ZNSt3__18ios_base33__set_badbit_and_consider_rethrowEv
+T _ZNSt3__18ios_base34__set_failbit_and_consider_rethrowEv
+R _ZNSt3__18ios_base3appE
+R _ZNSt3__18ios_base3ateE
+R _ZNSt3__18ios_base3decE
+R _ZNSt3__18ios_base3hexE
+R _ZNSt3__18ios_base3octE
+R _ZNSt3__18ios_base3outE
+T _ZNSt3__18ios_base4InitC1Ev
+T _ZNSt3__18ios_base4InitC2Ev
+T _ZNSt3__18ios_base4InitD1Ev
+T _ZNSt3__18ios_base4InitD2Ev
+T _ZNSt3__18ios_base4initEPv
+R _ZNSt3__18ios_base4leftE
+T _ZNSt3__18ios_base4moveERS0_
+T _ZNSt3__18ios_base4swapERS0_
+T _ZNSt3__18ios_base5clearEj
+R _ZNSt3__18ios_base5fixedE
+T _ZNSt3__18ios_base5imbueERKNS_6localeE
+T _ZNSt3__18ios_base5iwordEi
+T _ZNSt3__18ios_base5pwordEi
+R _ZNSt3__18ios_base5rightE
+R _ZNSt3__18ios_base5truncE
+R _ZNSt3__18ios_base6badbitE
+R _ZNSt3__18ios_base6binaryE
+R _ZNSt3__18ios_base6eofbitE
+R _ZNSt3__18ios_base6skipwsE
+T _ZNSt3__18ios_base6xallocEv
+T _ZNSt3__18ios_base7copyfmtERKS0_
+R _ZNSt3__18ios_base7failbitE
+T _ZNSt3__18ios_base7failureC1EPKcRKNS_10error_codeE
+T _ZNSt3__18ios_base7failureC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_10error_codeE
+T _ZNSt3__18ios_base7failureC2EPKcRKNS_10error_codeE
+T _ZNSt3__18ios_base7failureC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_10error_codeE
+T _ZNSt3__18ios_base7failureD0Ev
+T _ZNSt3__18ios_base7failureD1Ev
+T _ZNSt3__18ios_base7failureD2Ev
+R _ZNSt3__18ios_base7goodbitE
+R _ZNSt3__18ios_base7showposE
+R _ZNSt3__18ios_base7unitbufE
+R _ZNSt3__18ios_base8internalE
+R _ZNSt3__18ios_base8showbaseE
+R _ZNSt3__18ios_base9basefieldE
+R _ZNSt3__18ios_base9boolalphaE
+R _ZNSt3__18ios_base9showpointE
+R _ZNSt3__18ios_base9uppercaseE
+T _ZNSt3__18ios_baseD0Ev
+T _ZNSt3__18ios_baseD1Ev
+T _ZNSt3__18ios_baseD2Ev
+V _ZNSt3__18messagesIcE2idE
+V _ZNSt3__18messagesIwE2idE
+B _ZNSt3__18numpunctIcE2idE
+T _ZNSt3__18numpunctIcEC1Em
+T _ZNSt3__18numpunctIcEC2Em
+T _ZNSt3__18numpunctIcED0Ev
+T _ZNSt3__18numpunctIcED1Ev
+T _ZNSt3__18numpunctIcED2Ev
+B _ZNSt3__18numpunctIwE2idE
+T _ZNSt3__18numpunctIwEC1Em
+T _ZNSt3__18numpunctIwEC2Em
+T _ZNSt3__18numpunctIwED0Ev
+T _ZNSt3__18numpunctIwED1Ev
+T _ZNSt3__18numpunctIwED2Ev
+T _ZNSt3__18__rs_getEv
+T _ZNSt3__18__sp_mut4lockEv
+T _ZNSt3__18__sp_mut6unlockEv
+V _ZNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+V _ZNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+V _ZNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+V _ZNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T _ZNSt3__18to_charsEPcS0_d
+T _ZNSt3__18to_charsEPcS0_dNS_12chars_formatE
+T _ZNSt3__18to_charsEPcS0_dNS_12chars_formatEi
+T _ZNSt3__18to_charsEPcS0_e
+T _ZNSt3__18to_charsEPcS0_eNS_12chars_formatE
+T _ZNSt3__18to_charsEPcS0_eNS_12chars_formatEi
+T _ZNSt3__18to_charsEPcS0_f
+T _ZNSt3__18to_charsEPcS0_fNS_12chars_formatE
+T _ZNSt3__18to_charsEPcS0_fNS_12chars_formatEi
+W _ZNSt3__18valarrayImE6resizeEmm
+W _ZNSt3__18valarrayImEC1Em
+W _ZNSt3__18valarrayImEC2Em
+W _ZNSt3__18valarrayImED1Ev
+W _ZNSt3__18valarrayImED2Ev
+W _ZNSt3__19basic_iosIcNS_11char_traitsIcEEE7copyfmtERKS3_
+W _ZNSt3__19basic_iosIcNS_11char_traitsIcEEED0Ev
+W _ZNSt3__19basic_iosIcNS_11char_traitsIcEEED1Ev
+W _ZNSt3__19basic_iosIcNS_11char_traitsIcEEED2Ev
+W _ZNSt3__19basic_iosIwNS_11char_traitsIwEEE7copyfmtERKS3_
+W _ZNSt3__19basic_iosIwNS_11char_traitsIwEEED0Ev
+W _ZNSt3__19basic_iosIwNS_11char_traitsIwEEED1Ev
+W _ZNSt3__19basic_iosIwNS_11char_traitsIwEEED2Ev
+V _ZNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+W _ZNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE8__do_getERS4_S4_bRKNS_6localeEjRjRbRKNS_5ctypeIcEERNS_10unique_ptrIcPFvPvEEERPcSM_
+V _ZNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+W _ZNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE8__do_getERS4_S4_bRKNS_6localeEjRjRbRKNS_5ctypeIwEERNS_10unique_ptrIwPFvPvEEERPwSM_
+V _ZNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+V _ZNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+W _ZNSt3__19__num_getIcE17__stage2_int_loopEciPcRS2_RjcRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_S2_
+W _ZNSt3__19__num_getIcE17__stage2_int_prepERNS_8ios_baseEPcRc
+W _ZNSt3__19__num_getIcE19__stage2_float_loopEcRbRcPcRS4_ccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjS4_
+W _ZNSt3__19__num_getIcE19__stage2_float_prepERNS_8ios_baseEPcRcS5_
+W _ZNSt3__19__num_getIwE17__stage2_int_loopEwiPcRS2_RjwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_Pw
+W _ZNSt3__19__num_getIwE17__stage2_int_prepERNS_8ios_baseEPwRw
+W _ZNSt3__19__num_getIwE19__stage2_float_loopEwRbRcPcRS4_wwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjPw
+W _ZNSt3__19__num_getIwE19__stage2_float_prepERNS_8ios_baseEPwRwS5_
+W _ZNSt3__19__num_putIcE21__widen_and_group_intEPcS2_S2_S2_RS2_S3_RKNS_6localeE
+W _ZNSt3__19__num_putIcE23__widen_and_group_floatEPcS2_S2_S2_RS2_S3_RKNS_6localeE
+W _ZNSt3__19__num_putIwE21__widen_and_group_intEPcS2_S2_PwRS3_S4_RKNS_6localeE
+W _ZNSt3__19__num_putIwE23__widen_and_group_floatEPcS2_S2_PwRS3_S4_RKNS_6localeE
+T _ZNSt3__19strstreamD0Ev
+T _ZNSt3__19strstreamD1Ev
+T _ZNSt3__19strstreamD2Ev
+T _ZNSt3__19to_stringEd
+T _ZNSt3__19to_stringEe
+T _ZNSt3__19to_stringEf
+T _ZNSt3__19to_stringEi
+T _ZNSt3__19to_stringEj
+T _ZNSt3__19to_stringEl
+T _ZNSt3__19to_stringEm
+T _ZNSt3__19to_stringEx
+T _ZNSt3__19to_stringEy
+W _ZNSt3__1plIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_12basic_stringIT_T0_T1_EEPKS6_RKS9_
+T _ZSt17current_exceptionv
+T _ZSt17rethrow_exceptionSt13exception_ptr
+T _ZSt17__throw_bad_allocv
+T _ZSt18uncaught_exceptionv
+T _ZSt19uncaught_exceptionsv
+R _ZSt7nothrow
+D _ZTCNSt3__110istrstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE
+D _ZTCNSt3__110ostrstreamE0_NS_13basic_ostreamIcNS_11char_traitsIcEEEE
+V _ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE
+V _ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE
+V _ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE16_NS_13basic_ostreamIcS2_EE
+V _ZTCNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE0_NS_13basic_ostreamIcS2_EE
+V _ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE
+V _ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_14basic_iostreamIcS2_EE
+V _ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE16_NS_13basic_ostreamIcS2_EE
+V _ZTCNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE
+V _ZTCNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_ostreamIcS2_EE
+D _ZTCNSt3__19strstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE
+D _ZTCNSt3__19strstreamE0_NS_14basic_iostreamIcNS_11char_traitsIcEEEE
+D _ZTCNSt3__19strstreamE16_NS_13basic_ostreamIcNS_11char_traitsIcEEEE
+W _ZThn16_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+W _ZThn16_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T _ZThn16_NSt3__19strstreamD0Ev
+T _ZThn16_NSt3__19strstreamD1Ev
+D _ZTINSt12experimental15fundamentals_v112bad_any_castE
+D _ZTINSt12experimental19bad_optional_accessE
+V _ZTINSt3__110ctype_baseE
+D _ZTINSt3__110istrstreamE
+V _ZTINSt3__110money_baseE
+V _ZTINSt3__110moneypunctIcLb0EEE
+V _ZTINSt3__110moneypunctIcLb1EEE
+V _ZTINSt3__110moneypunctIwLb0EEE
+V _ZTINSt3__110moneypunctIwLb1EEE
+D _ZTINSt3__110ostrstreamE
+V _ZTINSt3__110__time_getE
+V _ZTINSt3__110__time_putE
+V _ZTINSt3__111__money_getIcEE
+V _ZTINSt3__111__money_getIwEE
+V _ZTINSt3__111__money_putIcEE
+V _ZTINSt3__111__money_putIwEE
+D _ZTINSt3__111regex_errorE
+D _ZTINSt3__112bad_weak_ptrE
+V _ZTINSt3__112codecvt_baseE
+D _ZTINSt3__112ctype_bynameIcEE
+D _ZTINSt3__112ctype_bynameIwEE
+D _ZTINSt3__112future_errorE
+D _ZTINSt3__112strstreambufE
+D _ZTINSt3__112system_errorE
+V _ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+V _ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+V _ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+V _ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+V _ZTINSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+V _ZTINSt3__113messages_baseE
+V _ZTINSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+V _ZTINSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+V _ZTINSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+V _ZTINSt3__114codecvt_bynameIcc11__mbstate_tEE
+V _ZTINSt3__114codecvt_bynameIDic11__mbstate_tEE
+V _ZTINSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+V _ZTINSt3__114codecvt_bynameIDsc11__mbstate_tEE
+V _ZTINSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+V _ZTINSt3__114codecvt_bynameIwc11__mbstate_tEE
+D _ZTINSt3__114__codecvt_utf8IDiEE
+D _ZTINSt3__114__codecvt_utf8IDsEE
+D _ZTINSt3__114__codecvt_utf8IwEE
+D _ZTINSt3__114collate_bynameIcEE
+D _ZTINSt3__114collate_bynameIwEE
+D _ZTINSt3__114error_categoryE
+V _ZTINSt3__114__num_get_baseE
+V _ZTINSt3__114__num_put_baseE
+D _ZTINSt3__114__shared_countE
+V _ZTINSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+V _ZTINSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+V _ZTINSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+D _ZTINSt3__115__codecvt_utf16IDiLb0EEE
+D _ZTINSt3__115__codecvt_utf16IDiLb1EEE
+D _ZTINSt3__115__codecvt_utf16IDsLb0EEE
+D _ZTINSt3__115__codecvt_utf16IDsLb1EEE
+D _ZTINSt3__115__codecvt_utf16IwLb0EEE
+D _ZTINSt3__115__codecvt_utf16IwLb1EEE
+V _ZTINSt3__115messages_bynameIcEE
+V _ZTINSt3__115messages_bynameIwEE
+D _ZTINSt3__115numpunct_bynameIcEE
+D _ZTINSt3__115numpunct_bynameIwEE
+V _ZTINSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+D _ZTINSt3__116__narrow_to_utf8ILm16EEE
+D _ZTINSt3__116__narrow_to_utf8ILm32EEE
+D _ZTINSt3__117__assoc_sub_stateE
+D _ZTINSt3__117bad_function_callE
+V _ZTINSt3__117moneypunct_bynameIcLb0EEE
+V _ZTINSt3__117moneypunct_bynameIcLb1EEE
+V _ZTINSt3__117moneypunct_bynameIwLb0EEE
+V _ZTINSt3__117moneypunct_bynameIwLb1EEE
+D _ZTINSt3__117__widen_from_utf8ILm16EEE
+D _ZTINSt3__117__widen_from_utf8ILm32EEE
+V _ZTINSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTINSt3__118__time_get_storageIcEE
+V _ZTINSt3__118__time_get_storageIwEE
+D _ZTINSt3__119bad_expected_accessIvEE
+V _ZTINSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTINSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+D _ZTINSt3__119__shared_weak_countE
+D _ZTINSt3__120__codecvt_utf8_utf16IDiEE
+D _ZTINSt3__120__codecvt_utf8_utf16IDsEE
+D _ZTINSt3__120__codecvt_utf8_utf16IwEE
+V _ZTINSt3__120__time_get_c_storageIcEE
+V _ZTINSt3__120__time_get_c_storageIwEE
+D _ZTINSt3__13pmr15memory_resourceE
+D _ZTINSt3__13pmr25monotonic_buffer_resourceE
+D _ZTINSt3__13pmr26synchronized_pool_resourceE
+D _ZTINSt3__13pmr28unsynchronized_pool_resourceE
+D _ZTINSt3__14__fs10filesystem16filesystem_errorE
+D _ZTINSt3__15ctypeIcEE
+D _ZTINSt3__15ctypeIwEE
+D _ZTINSt3__16locale5facetE
+D _ZTINSt3__17codecvtIcc11__mbstate_tEE
+D _ZTINSt3__17codecvtIDic11__mbstate_tEE
+D _ZTINSt3__17codecvtIDiDu11__mbstate_tEE
+D _ZTINSt3__17codecvtIDsc11__mbstate_tEE
+D _ZTINSt3__17codecvtIDsDu11__mbstate_tEE
+D _ZTINSt3__17codecvtIwc11__mbstate_tEE
+V _ZTINSt3__17collateIcEE
+V _ZTINSt3__17collateIwEE
+V _ZTINSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+D _ZTINSt3__18ios_base7failureE
+D _ZTINSt3__18ios_baseE
+V _ZTINSt3__18messagesIcEE
+V _ZTINSt3__18messagesIwEE
+D _ZTINSt3__18numpunctIcEE
+D _ZTINSt3__18numpunctIwEE
+V _ZTINSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__19basic_iosIcNS_11char_traitsIcEEEE
+V _ZTINSt3__19basic_iosIwNS_11char_traitsIwEEEE
+V _ZTINSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__19__num_getIcEE
+V _ZTINSt3__19__num_getIwEE
+V _ZTINSt3__19__num_putIcEE
+V _ZTINSt3__19__num_putIwEE
+D _ZTINSt3__19strstreamE
+V _ZTINSt3__19time_baseE
+D _ZTISt12bad_any_cast
+D _ZTISt16nested_exception
+D _ZTISt18bad_variant_access
+D _ZTISt19bad_optional_access
+D _ZTISt29__bad_variant_access_with_msg
+R _ZTSNSt12experimental15fundamentals_v112bad_any_castE
+R _ZTSNSt12experimental19bad_optional_accessE
+V _ZTSNSt3__110ctype_baseE
+R _ZTSNSt3__110istrstreamE
+V _ZTSNSt3__110money_baseE
+V _ZTSNSt3__110moneypunctIcLb0EEE
+V _ZTSNSt3__110moneypunctIcLb1EEE
+V _ZTSNSt3__110moneypunctIwLb0EEE
+V _ZTSNSt3__110moneypunctIwLb1EEE
+R _ZTSNSt3__110ostrstreamE
+V _ZTSNSt3__110__time_getE
+V _ZTSNSt3__110__time_putE
+V _ZTSNSt3__111__money_getIcEE
+V _ZTSNSt3__111__money_getIwEE
+V _ZTSNSt3__111__money_putIcEE
+V _ZTSNSt3__111__money_putIwEE
+R _ZTSNSt3__111regex_errorE
+R _ZTSNSt3__112bad_weak_ptrE
+V _ZTSNSt3__112codecvt_baseE
+R _ZTSNSt3__112ctype_bynameIcEE
+R _ZTSNSt3__112ctype_bynameIwEE
+R _ZTSNSt3__112future_errorE
+R _ZTSNSt3__112strstreambufE
+R _ZTSNSt3__112system_errorE
+V _ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+V _ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+V _ZTSNSt3__113messages_baseE
+V _ZTSNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__114codecvt_bynameIcc11__mbstate_tEE
+V _ZTSNSt3__114codecvt_bynameIDic11__mbstate_tEE
+V _ZTSNSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+V _ZTSNSt3__114codecvt_bynameIDsc11__mbstate_tEE
+V _ZTSNSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+V _ZTSNSt3__114codecvt_bynameIwc11__mbstate_tEE
+R _ZTSNSt3__114__codecvt_utf8IDiEE
+R _ZTSNSt3__114__codecvt_utf8IDsEE
+R _ZTSNSt3__114__codecvt_utf8IwEE
+R _ZTSNSt3__114collate_bynameIcEE
+R _ZTSNSt3__114collate_bynameIwEE
+R _ZTSNSt3__114error_categoryE
+V _ZTSNSt3__114__num_get_baseE
+V _ZTSNSt3__114__num_put_baseE
+R _ZTSNSt3__114__shared_countE
+V _ZTSNSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+V _ZTSNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+R _ZTSNSt3__115__codecvt_utf16IDiLb0EEE
+R _ZTSNSt3__115__codecvt_utf16IDiLb1EEE
+R _ZTSNSt3__115__codecvt_utf16IDsLb0EEE
+R _ZTSNSt3__115__codecvt_utf16IDsLb1EEE
+R _ZTSNSt3__115__codecvt_utf16IwLb0EEE
+R _ZTSNSt3__115__codecvt_utf16IwLb1EEE
+V _ZTSNSt3__115messages_bynameIcEE
+V _ZTSNSt3__115messages_bynameIwEE
+R _ZTSNSt3__115numpunct_bynameIcEE
+R _ZTSNSt3__115numpunct_bynameIwEE
+V _ZTSNSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+R _ZTSNSt3__116__narrow_to_utf8ILm16EEE
+R _ZTSNSt3__116__narrow_to_utf8ILm32EEE
+R _ZTSNSt3__117__assoc_sub_stateE
+R _ZTSNSt3__117bad_function_callE
+V _ZTSNSt3__117moneypunct_bynameIcLb0EEE
+V _ZTSNSt3__117moneypunct_bynameIcLb1EEE
+V _ZTSNSt3__117moneypunct_bynameIwLb0EEE
+V _ZTSNSt3__117moneypunct_bynameIwLb1EEE
+R _ZTSNSt3__117__widen_from_utf8ILm16EEE
+R _ZTSNSt3__117__widen_from_utf8ILm32EEE
+V _ZTSNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTSNSt3__118__time_get_storageIcEE
+V _ZTSNSt3__118__time_get_storageIwEE
+R _ZTSNSt3__119bad_expected_accessIvEE
+V _ZTSNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTSNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+R _ZTSNSt3__119__shared_weak_countE
+R _ZTSNSt3__120__codecvt_utf8_utf16IDiEE
+R _ZTSNSt3__120__codecvt_utf8_utf16IDsEE
+R _ZTSNSt3__120__codecvt_utf8_utf16IwEE
+V _ZTSNSt3__120__time_get_c_storageIcEE
+V _ZTSNSt3__120__time_get_c_storageIwEE
+R _ZTSNSt3__13pmr15memory_resourceE
+R _ZTSNSt3__13pmr25monotonic_buffer_resourceE
+R _ZTSNSt3__13pmr26synchronized_pool_resourceE
+R _ZTSNSt3__13pmr28unsynchronized_pool_resourceE
+R _ZTSNSt3__14__fs10filesystem16filesystem_errorE
+R _ZTSNSt3__15ctypeIcEE
+R _ZTSNSt3__15ctypeIwEE
+R _ZTSNSt3__16locale5facetE
+R _ZTSNSt3__17codecvtIcc11__mbstate_tEE
+R _ZTSNSt3__17codecvtIDic11__mbstate_tEE
+R _ZTSNSt3__17codecvtIDiDu11__mbstate_tEE
+R _ZTSNSt3__17codecvtIDsc11__mbstate_tEE
+R _ZTSNSt3__17codecvtIDsDu11__mbstate_tEE
+R _ZTSNSt3__17codecvtIwc11__mbstate_tEE
+V _ZTSNSt3__17collateIcEE
+V _ZTSNSt3__17collateIwEE
+V _ZTSNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+R _ZTSNSt3__18ios_base7failureE
+R _ZTSNSt3__18ios_baseE
+V _ZTSNSt3__18messagesIcEE
+V _ZTSNSt3__18messagesIwEE
+R _ZTSNSt3__18numpunctIcEE
+R _ZTSNSt3__18numpunctIwEE
+V _ZTSNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__19basic_iosIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__19basic_iosIwNS_11char_traitsIwEEEE
+V _ZTSNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__19__num_getIcEE
+V _ZTSNSt3__19__num_getIwEE
+V _ZTSNSt3__19__num_putIcEE
+V _ZTSNSt3__19__num_putIwEE
+R _ZTSNSt3__19strstreamE
+V _ZTSNSt3__19time_baseE
+R _ZTSSt12bad_any_cast
+R _ZTSSt16nested_exception
+R _ZTSSt18bad_variant_access
+R _ZTSSt19bad_optional_access
+R _ZTSSt29__bad_variant_access_with_msg
+D _ZTTNSt3__110istrstreamE
+D _ZTTNSt3__110ostrstreamE
+V _ZTTNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+V _ZTTNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+V _ZTTNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+V _ZTTNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+V _ZTTNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+V _ZTTNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+V _ZTTNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+V _ZTTNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTTNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTTNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+D _ZTTNSt3__19strstreamE
+T _ZTv0_n24_NSt3__110istrstreamD0Ev
+T _ZTv0_n24_NSt3__110istrstreamD1Ev
+T _ZTv0_n24_NSt3__110ostrstreamD0Ev
+T _ZTv0_n24_NSt3__110ostrstreamD1Ev
+W _ZTv0_n24_NSt3__113basic_istreamIcNS_11char_traitsIcEEED0Ev
+W _ZTv0_n24_NSt3__113basic_istreamIcNS_11char_traitsIcEEED1Ev
+W _ZTv0_n24_NSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev
+W _ZTv0_n24_NSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev
+W _ZTv0_n24_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
+W _ZTv0_n24_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
+W _ZTv0_n24_NSt3__113basic_ostreamIwNS_11char_traitsIwEEED0Ev
+W _ZTv0_n24_NSt3__113basic_ostreamIwNS_11char_traitsIwEEED1Ev
+W _ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+W _ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T _ZTv0_n24_NSt3__19strstreamD0Ev
+T _ZTv0_n24_NSt3__19strstreamD1Ev
+D _ZTVNSt12experimental15fundamentals_v112bad_any_castE
+D _ZTVNSt12experimental19bad_optional_accessE
+D _ZTVNSt3__110istrstreamE
+V _ZTVNSt3__110moneypunctIcLb0EEE
+V _ZTVNSt3__110moneypunctIcLb1EEE
+V _ZTVNSt3__110moneypunctIwLb0EEE
+V _ZTVNSt3__110moneypunctIwLb1EEE
+D _ZTVNSt3__110ostrstreamE
+D _ZTVNSt3__111regex_errorE
+D _ZTVNSt3__112bad_weak_ptrE
+D _ZTVNSt3__112ctype_bynameIcEE
+D _ZTVNSt3__112ctype_bynameIwEE
+D _ZTVNSt3__112future_errorE
+D _ZTVNSt3__112strstreambufE
+D _ZTVNSt3__112system_errorE
+V _ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+V _ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+V _ZTVNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__114codecvt_bynameIcc11__mbstate_tEE
+V _ZTVNSt3__114codecvt_bynameIDic11__mbstate_tEE
+V _ZTVNSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+V _ZTVNSt3__114codecvt_bynameIDsc11__mbstate_tEE
+V _ZTVNSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+V _ZTVNSt3__114codecvt_bynameIwc11__mbstate_tEE
+D _ZTVNSt3__114__codecvt_utf8IDiEE
+D _ZTVNSt3__114__codecvt_utf8IDsEE
+D _ZTVNSt3__114__codecvt_utf8IwEE
+D _ZTVNSt3__114collate_bynameIcEE
+D _ZTVNSt3__114collate_bynameIwEE
+D _ZTVNSt3__114error_categoryE
+D _ZTVNSt3__114__shared_countE
+V _ZTVNSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+V _ZTVNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+D _ZTVNSt3__115__codecvt_utf16IDiLb0EEE
+D _ZTVNSt3__115__codecvt_utf16IDiLb1EEE
+D _ZTVNSt3__115__codecvt_utf16IDsLb0EEE
+D _ZTVNSt3__115__codecvt_utf16IDsLb1EEE
+D _ZTVNSt3__115__codecvt_utf16IwLb0EEE
+D _ZTVNSt3__115__codecvt_utf16IwLb1EEE
+V _ZTVNSt3__115messages_bynameIcEE
+V _ZTVNSt3__115messages_bynameIwEE
+D _ZTVNSt3__115numpunct_bynameIcEE
+D _ZTVNSt3__115numpunct_bynameIwEE
+V _ZTVNSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTVNSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+D _ZTVNSt3__116__narrow_to_utf8ILm16EEE
+D _ZTVNSt3__116__narrow_to_utf8ILm32EEE
+D _ZTVNSt3__117__assoc_sub_stateE
+D _ZTVNSt3__117bad_function_callE
+V _ZTVNSt3__117moneypunct_bynameIcLb0EEE
+V _ZTVNSt3__117moneypunct_bynameIcLb1EEE
+V _ZTVNSt3__117moneypunct_bynameIwLb0EEE
+V _ZTVNSt3__117moneypunct_bynameIwLb1EEE
+D _ZTVNSt3__117__widen_from_utf8ILm16EEE
+D _ZTVNSt3__117__widen_from_utf8ILm32EEE
+V _ZTVNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+D _ZTVNSt3__119bad_expected_accessIvEE
+V _ZTVNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTVNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+D _ZTVNSt3__119__shared_weak_countE
+D _ZTVNSt3__120__codecvt_utf8_utf16IDiEE
+D _ZTVNSt3__120__codecvt_utf8_utf16IDsEE
+D _ZTVNSt3__120__codecvt_utf8_utf16IwEE
+D _ZTVNSt3__13pmr15memory_resourceE
+D _ZTVNSt3__13pmr25monotonic_buffer_resourceE
+D _ZTVNSt3__13pmr26synchronized_pool_resourceE
+D _ZTVNSt3__13pmr28unsynchronized_pool_resourceE
+D _ZTVNSt3__14__fs10filesystem16filesystem_errorE
+D _ZTVNSt3__15ctypeIcEE
+D _ZTVNSt3__15ctypeIwEE
+D _ZTVNSt3__16locale5facetE
+D _ZTVNSt3__17codecvtIcc11__mbstate_tEE
+D _ZTVNSt3__17codecvtIDic11__mbstate_tEE
+D _ZTVNSt3__17codecvtIDiDu11__mbstate_tEE
+D _ZTVNSt3__17codecvtIDsc11__mbstate_tEE
+D _ZTVNSt3__17codecvtIDsDu11__mbstate_tEE
+D _ZTVNSt3__17codecvtIwc11__mbstate_tEE
+V _ZTVNSt3__17collateIcEE
+V _ZTVNSt3__17collateIwEE
+V _ZTVNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTVNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+D _ZTVNSt3__18ios_base7failureE
+D _ZTVNSt3__18ios_baseE
+V _ZTVNSt3__18messagesIcEE
+V _ZTVNSt3__18messagesIwEE
+D _ZTVNSt3__18numpunctIcEE
+D _ZTVNSt3__18numpunctIwEE
+V _ZTVNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTVNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTVNSt3__19basic_iosIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__19basic_iosIwNS_11char_traitsIwEEEE
+V _ZTVNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTVNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+D _ZTVNSt3__19strstreamE
+D _ZTVSt12bad_any_cast
+D _ZTVSt16nested_exception
+D _ZTVSt18bad_variant_access
+D _ZTVSt19bad_optional_access
+D _ZTVSt29__bad_variant_access_with_msg


From libcxx-commits at lists.llvm.org  Tue Jul  7 05:44:05 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 05:44:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add MSVC's implementation of
 `exception_ptr` for Windows (PR #94977)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/94977@github.com>
Message-ID: <6a4cf495.2c9cebe8.17c115.4515@mx.google.com>


================
@@ -7,70 +7,453 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include <exception>
-#include <stdio.h>
-#include <stdlib.h>
-
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrCreate(void*);
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrDestroy(void*);
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrCopy(void*, const void*);
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrAssign(void*, const void*);
-_LIBCPP_CRT_FUNC bool __cdecl __ExceptionPtrCompare(const void*, const void*);
-_LIBCPP_CRT_FUNC bool __cdecl __ExceptionPtrToBool(const void*);
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrSwap(void*, void*);
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrCurrentException(void*);
-[[noreturn]] _LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrRethrow(const void*);
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrCopyException(void*, const void*, const void*);
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmultichar")
 
-namespace std {
+#include <__exception/exception_ptr.h>
+#include <__memory/shared_count.h>
+#include <cstdlib>
+#include <cstring>
 
-exception_ptr::exception_ptr() noexcept { __ExceptionPtrCreate(this); }
-exception_ptr::exception_ptr(nullptr_t) noexcept { __ExceptionPtrCreate(this); }
+#include <Unknwn.h>
+#include <Windows.h>
+struct _ThrowInfo;
+#include <eh.h>
+#include <ehdata.h>
+#include <malloc.h> // alloca
 
-exception_ptr::exception_ptr(const exception_ptr& __other) noexcept { __ExceptionPtrCopy(this, &__other); }
-exception_ptr& exception_ptr::operator=(const exception_ptr& __other) noexcept {
-  __ExceptionPtrAssign(this, &__other);
-  return *this;
+// Pre-V4 managed exception code
+#define MANAGED_EXCEPTION_CODE 0XE0434F4D
+
+// V4 and later managed exception code
+#define MANAGED_EXCEPTION_CODE_V4 0XE0434352
+
+extern "C" _LIBCPP_CRT_FUNC void* __cdecl __AdjustPointer(void*, const PMD&);
+extern "C" _LIBCPP_CRT_FUNC void** __cdecl __current_exception();
+
+namespace {
+
+typedef void(__stdcall* __prepare_for_throw_t)(void*);
+
+struct __winrt_exception_info {
+  void* __description;
+  void* __restricted_error_string;
+  void* __restricted_error_reference;
+  void* __capability_sid;
+  long __hr;
+  void* __restricted_info;
+  ThrowInfo* __throw_info;
+  unsigned int __size;
+  __prepare_for_throw_t __prepare_throw;
+};
+
+inline EHExceptionRecord* __get_current_exception() noexcept {
+  return *reinterpret_cast<EHExceptionRecord**>(__current_exception());
 }
 
-exception_ptr& exception_ptr::operator=(nullptr_t) noexcept {
-  exception_ptr dummy;
-  __ExceptionPtrAssign(this, &dummy);
-  return *this;
+inline void __call_member_function_0(void* __this, void* __mfn) {
+  auto __fn = reinterpret_cast<void(__thiscall*)(void*)>(__mfn);
+  __fn(__this);
+}
+
+inline void __call_member_function_1(void* __this, void* __mfn, void* __arg) {
+  auto __fn = reinterpret_cast<void(__thiscall*)(void*, void*)>(__mfn);
+  __fn(__this, __arg);
+}
+
+inline void __call_member_function_2(void* __this, void* __mfn, void* __arg1, int __arg2) {
+  auto __fn = reinterpret_cast<void(__thiscall*)(void*, void*, int)>(__mfn);
+  __fn(__this, __arg1, __arg2);
 }
 
-exception_ptr::~exception_ptr() noexcept { __ExceptionPtrDestroy(this); }
+void __populate_cpp_exception_record(
+    _EXCEPTION_RECORD& __record, const void* const __except_obj, ThrowInfo* __throw_info) noexcept {
+  __record.ExceptionCode           = EH_EXCEPTION_NUMBER;
+  __record.ExceptionFlags          = EXCEPTION_NONCONTINUABLE;
+  __record.ExceptionRecord         = nullptr;
+  __record.ExceptionAddress        = nullptr;
+  __record.NumberParameters        = EH_EXCEPTION_PARAMETERS;
+  __record.ExceptionInformation[0] = EH_MAGIC_NUMBER1;
+  __record.ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(__except_obj);
+
+  if (__throw_info && (__throw_info->attributes & TI_IsWinRT)) {
+    const auto __wei = (*static_cast<__winrt_exception_info** const*>(const_cast<void*>(__except_obj)))[-1];
+    __throw_info     = __wei->__throw_info;
+  }
+
+  __record.ExceptionInformation[2] = reinterpret_cast<ULONG_PTR>(__throw_info);
+
+#if _EH_RELATIVE_TYPEINFO
+  void* __throw_image_base =
+      __throw_info ? RtlPcToFileHeader(const_cast<void*>(static_cast<const void*>(__throw_info)), &__throw_image_base)
+                   : nullptr;
+  __record.ExceptionInformation[3] = reinterpret_cast<ULONG_PTR>(__throw_image_base);
+#endif
+
+  if (__throw_info && ((__throw_info->attributes & TI_IsPure)
+#if _EH_RELATIVE_TYPEINFO
+                       || !__throw_image_base
+#endif
+                       )) {
+    __record.ExceptionInformation[0] = EH_PURE_MAGIC_NUMBER1;
+  }
+}
+
+void __copy_exception_record(_EXCEPTION_RECORD& __dest, const _EXCEPTION_RECORD& __src) noexcept {
+  __dest.ExceptionCode    = __src.ExceptionCode;
+  __dest.ExceptionFlags   = __src.ExceptionFlags | EXCEPTION_NONCONTINUABLE;
+  __dest.ExceptionRecord  = nullptr;
+  __dest.ExceptionAddress = nullptr;
+  const auto __parameters = __src.NumberParameters;
+  __dest.NumberParameters = __parameters;
+
+  constexpr auto __max_parameters = static_cast<DWORD>(EXCEPTION_MAXIMUM_PARAMETERS);
+  const auto __in_use             = (__parameters < __max_parameters) ? __parameters : __max_parameters;
+  std::memcpy(__dest.ExceptionInformation, __src.ExceptionInformation, __in_use * sizeof(ULONG_PTR));
+  std::memset(&__dest.ExceptionInformation[__in_use], 0, (__max_parameters - __in_use) * sizeof(ULONG_PTR));
+}
+
+void __copy_exception_object(void* __dest, const void* __src, const CatchableType* const __type
+#if _EH_RELATIVE_TYPEINFO
+                             ,
+                             uintptr_t __throw_image_base
+#endif
+) {
+  if ((__type->properties & CT_IsSimpleType) || __type->copyFunction == 0) {
+    std::memcpy(__dest, __src, __type->sizeOrOffset);
+
+    if (__type->properties & CT_IsWinRTHandle) {
+      const auto __unknown = *static_cast<IUnknown* const*>(const_cast<void*>(__src));
+      if (__unknown) {
+        __unknown->AddRef();
+      }
+    }
+    return;
+  }
+
+#if _EH_RELATIVE_TYPEINFO
+  const auto __copy_func = reinterpret_cast<void*>(__throw_image_base + __type->copyFunction);
+#else
+  const auto __copy_func = __type->copyFunction;
+#endif
+
+  const auto __adjusted = __AdjustPointer(const_cast<void*>(__src), __type->thisDisplacement);
+  if (__type->properties & CT_HasVirtualBase) {
+    __call_member_function_2(__dest, __copy_func, __adjusted, 1);
+  } else {
+    __call_member_function_1(__dest, __copy_func, __adjusted);
+  }
+}
+
+struct __exception_ptr_storage : public std::__shared_count {
+  _EXCEPTION_RECORD __record_;
+  explicit __exception_ptr_storage(long __refs = 0) noexcept : std::__shared_count(__refs) {}
+};
+
+template <class _StaticEx>
+struct __exception_ptr_static final : public __exception_ptr_storage {
+  _StaticEx __ex_;
+
+  __exception_ptr_static() noexcept : __exception_ptr_storage(0) {
+    __populate_cpp_exception_record(__record_, &__ex_, static_cast<ThrowInfo*>(__GetExceptionInfo(__ex_)));
+  }
+
+  void __on_zero_shared() noexcept override {}
+
+  static __exception_ptr_storage* __get() noexcept {
+    struct __container {
+      union {
+        __exception_ptr_static __instance_;
+      };
+      __container() noexcept : __instance_() {}
+      ~__container() {}
+    };
+    static __container __storage;
+    return &__storage.__instance_;
+  }
+};
+
+struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) __exception_ptr_normal final : public __exception_ptr_storage {
+  explicit __exception_ptr_normal(const _EXCEPTION_RECORD& __record) noexcept : __exception_ptr_storage(0) {
+    __copy_exception_record(__record_, __record);
+  }
+
+  void __on_zero_shared() noexcept override {
+    const auto& __cpp_eh_record = reinterpret_cast<EHExceptionRecord&>(__record_);
+    if (PER_IS_MSVC_PURE_OR_NATIVE_EH(&__cpp_eh_record)) {
+      const auto* __throw_info = __cpp_eh_record.params.pThrowInfo;
+      if (__throw_info && __cpp_eh_record.params.pExceptionObject) {
+#if _EH_RELATIVE_TYPEINFO
+        const auto __throw_image_base = reinterpret_cast<uintptr_t>(__cpp_eh_record.params.pThrowImageBase);
+        const auto* __catchable_type_array = reinterpret_cast<const CatchableTypeArray*>(
+            static_cast<uintptr_t>(__throw_info->pCatchableTypeArray) + __throw_image_base);
+        const auto* __type = reinterpret_cast<CatchableType*>(
+            static_cast<uintptr_t>(__catchable_type_array->arrayOfCatchableTypes[0]) + __throw_image_base);
+#else
+        const auto* __type = __throw_info->pCatchableTypeArray->arrayOfCatchableTypes[0];
+#endif
+        if (__throw_info->pmfnUnwind) {
+#if _EH_RELATIVE_TYPEINFO
+          __call_member_function_0(__cpp_eh_record.params.pExceptionObject,
+                                   reinterpret_cast<void*>(__throw_info->pmfnUnwind + __throw_image_base));
+#else
+          __call_member_function_0(__cpp_eh_record.params.pExceptionObject, __throw_info->pmfnUnwind);
+#endif
+        } else if (__type->properties & CT_IsWinRTHandle) {
+          const auto* __unknown = *static_cast<IUnknown* const*>(__cpp_eh_record.params.pExceptionObject);
+          if (__unknown) {
+            const_cast<IUnknown*>(__unknown)->Release();
+          }
+        }
+      }
+    }
+    std::free(this);
+  }
+};
+
+static_assert(sizeof(__exception_ptr_normal) % __STDCPP_DEFAULT_NEW_ALIGNMENT__ == 0,
+              "Exception in exception_ptr would be constructed with the wrong alignment");
 
-exception_ptr::operator bool() const noexcept { return __ExceptionPtrToBool(this); }
+void __assign_seh_exception_ptr_from_record(
+    void*& __dest, const _EXCEPTION_RECORD& __record, void* const __rx_raw) noexcept {
+  if (!__rx_raw) {
+    __dest = __exception_ptr_static<std::bad_alloc>::__get();
+    return;
+  }
 
-bool operator==(const exception_ptr& __x, const exception_ptr& __y) noexcept {
-  return __ExceptionPtrCompare(&__x, &__y);
+  __dest = ::new (__rx_raw) __exception_ptr_normal(__record);
 }
 
-void swap(exception_ptr& lhs, exception_ptr& rhs) noexcept { __ExceptionPtrSwap(&rhs, &lhs); }
+void __assign_cpp_exception_ptr_from_record(void*& __dest, const EHExceptionRecord& __record) noexcept {
+  const auto* __throw_info = __record.params.pThrowInfo;
+#if _EH_RELATIVE_TYPEINFO
+  const auto __throw_image_base = reinterpret_cast<uintptr_t>(__record.params.pThrowImageBase);
+  const auto* __catchable_type_array = reinterpret_cast<const CatchableTypeArray*>(
+      static_cast<uintptr_t>(__throw_info->pCatchableTypeArray) + __throw_image_base);
+  const auto* __type = reinterpret_cast<CatchableType*>(
+      static_cast<uintptr_t>(__catchable_type_array->arrayOfCatchableTypes[0]) + __throw_image_base);
+#else
+  const auto* __type = __throw_info->pCatchableTypeArray->arrayOfCatchableTypes[0];
+#endif
+
+  const auto __except_obj_size = static_cast<size_t>(__type->sizeOrOffset);
+  const auto __alloc_size      = sizeof(__exception_ptr_normal) + __except_obj_size;
+  auto* __rx_raw               = std::malloc(__alloc_size);
+  if (!__rx_raw) {
+    __dest = __exception_ptr_static<std::bad_alloc>::__get();
+    return;
+  }
+
+  try {
+    __copy_exception_object(static_cast<__exception_ptr_normal*>(__rx_raw) + 1,
+                            __record.params.pExceptionObject,
+                            __type
+#if _EH_RELATIVE_TYPEINFO
+                            ,
+                            __throw_image_base
+#endif
+    );
+
+    const auto* __rx = ::new (__rx_raw) __exception_ptr_normal(reinterpret_cast<const _EXCEPTION_RECORD&>(__record));
+    reinterpret_cast<EHExceptionRecord&>(const_cast<__exception_ptr_normal*>(__rx)->__record_).params.pExceptionObject =
+        static_cast<__exception_ptr_normal*>(__rx_raw) + 1;
+    __dest = const_cast<__exception_ptr_normal*>(__rx);
+  } catch (...) {
+    const auto* __inner_record_ptr = __get_current_exception();
+    if (!__inner_record_ptr) {
+      std::free(__rx_raw);
+      __dest = __exception_ptr_static<std::bad_exception>::__get();
+      return;
+    }
+    const auto& __inner_record = *__inner_record_ptr;
+    if (__inner_record.ExceptionCode == MANAGED_EXCEPTION_CODE ||
+        __inner_record.ExceptionCode == MANAGED_EXCEPTION_CODE_V4) {
+      std::free(__rx_raw);
+      __dest = __exception_ptr_static<std::bad_exception>::__get();
+      return;
+    }
+
+    if (!PER_IS_MSVC_PURE_OR_NATIVE_EH(&__inner_record)) {
+      __assign_seh_exception_ptr_from_record(
+          __dest, reinterpret_cast<const _EXCEPTION_RECORD&>(__inner_record), __rx_raw);
+      return;
+    }
+
+    const auto* __inner_throw = __inner_record.params.pThrowInfo;
+#if _EH_RELATIVE_TYPEINFO
+    const auto __inner_throw_image_base = reinterpret_cast<uintptr_t>(__inner_record.params.pThrowImageBase);
+    const auto* __inner_catchable_type_array = reinterpret_cast<const CatchableTypeArray*>(
+        static_cast<uintptr_t>(__inner_throw->pCatchableTypeArray) + __inner_throw_image_base);
+    const auto* __inner_type = reinterpret_cast<CatchableType*>(
+        static_cast<uintptr_t>(__inner_catchable_type_array->arrayOfCatchableTypes[0]) + __inner_throw_image_base);
+#else
+    const auto* __inner_type = __inner_throw->pCatchableTypeArray->arrayOfCatchableTypes[0];
+#endif
+
+    const auto __inner_except_size = static_cast<size_t>(__inner_type->sizeOrOffset);
+    const auto __inner_alloc_size  = sizeof(__exception_ptr_normal) + __inner_except_size;
+    if (__inner_alloc_size > __alloc_size) {
+      std::free(__rx_raw);
+      __rx_raw = std::malloc(__inner_alloc_size);
+      if (!__rx_raw) {
+        __dest = __exception_ptr_static<std::bad_alloc>::__get();
+        return;
+      }
+    }
+
+    try {
+      __copy_exception_object(static_cast<__exception_ptr_normal*>(__rx_raw) + 1,
+                              __inner_record.params.pExceptionObject,
+                              __inner_type
+#if _EH_RELATIVE_TYPEINFO
+                              ,
+                              __inner_throw_image_base
+#endif
+      );
+    } catch (...) {
+      std::free(__rx_raw);
+      __dest = __exception_ptr_static<std::bad_exception>::__get();
+      return;
+    }
+
+    const auto* __rx =
+        ::new (__rx_raw) __exception_ptr_normal(reinterpret_cast<const _EXCEPTION_RECORD&>(__inner_record));
+    reinterpret_cast<EHExceptionRecord&>(const_cast<__exception_ptr_normal*>(__rx)->__record_).params.pExceptionObject =
+        static_cast<__exception_ptr_normal*>(__rx_raw) + 1;
+    __dest = const_cast<__exception_ptr_normal*>(__rx);
+  }
+}
+
+} // namespace
+
+namespace std {
+
+exception_ptr::~exception_ptr() noexcept {
+  if (__ptr_) {
+    static_cast<__shared_count*>(__ptr_)->__release_shared();
+  }
+}
+
+exception_ptr::exception_ptr(const exception_ptr& __other) noexcept : __ptr_(__other.__ptr_) {
+  if (__ptr_) {
+    static_cast<__shared_count*>(__ptr_)->__add_shared();
+  }
+}
+
+exception_ptr& exception_ptr::operator=(const exception_ptr& __other) noexcept {
+  if (__ptr_ != __other.__ptr_) {
+    if (__other.__ptr_) {
+      static_cast<__shared_count*>(__other.__ptr_)->__add_shared();
+    }
+    if (__ptr_) {
+      static_cast<__shared_count*>(__ptr_)->__release_shared();
+    }
+    __ptr_ = __other.__ptr_;
+  }
+  return *this;
+}
+
+exception_ptr exception_ptr::__from_native_exception_pointer(void* __p) noexcept {
+  exception_ptr __ret;
+  __ret.__ptr_ = __p;
+  if (__ret.__ptr_) {
+    static_cast<__shared_count*>(__ret.__ptr_)->__add_shared();
+  }
+  return __ret;
+}
 
 exception_ptr __copy_exception_ptr(void* __except, const void* __ptr) {
   exception_ptr __ret = nullptr;
-  if (__ptr)
-    __ExceptionPtrCopyException(&__ret, __except, __ptr);
+  if (!__ptr) {
+    return __ret;
+  }
+
+  _EXCEPTION_RECORD __record;
+  __populate_cpp_exception_record(__record, __except, static_cast<ThrowInfo*>(const_cast<void*>(__ptr)));
+  __assign_cpp_exception_ptr_from_record(__ret.__ptr_, reinterpret_cast<const EHExceptionRecord&>(__record));
   return __ret;
 }
 
 exception_ptr current_exception() noexcept {
   exception_ptr __ret;
-  __ExceptionPtrCurrentException(&__ret);
+  const auto* __record = __get_current_exception();
+  if (!__record || __record->ExceptionCode == MANAGED_EXCEPTION_CODE ||
+      __record->ExceptionCode == MANAGED_EXCEPTION_CODE_V4) {
+    return __ret;
+  }
+
+  if (PER_IS_MSVC_PURE_OR_NATIVE_EH(__record)) {
+    __assign_cpp_exception_ptr_from_record(__ret.__ptr_, *__record);
+  } else {
+    __assign_seh_exception_ptr_from_record(
+        __ret.__ptr_, reinterpret_cast<const _EXCEPTION_RECORD&>(*__record), std::malloc(sizeof(__exception_ptr_normal)));
+  }
   return __ret;
 }
 
-[[noreturn]] void rethrow_exception(exception_ptr p) { __ExceptionPtrRethrow(&p); }
+[[noreturn]] void rethrow_exception(exception_ptr __p) {
+  if (!__p) {
+    throw bad_exception();
+  }
+
+  auto* __rep = static_cast<__exception_ptr_storage*>(__p.__ptr_);
+  auto __record_copy = __rep->__record_;
+  auto& __cpp_record = reinterpret_cast<EHExceptionRecord&>(__record_copy);
+  if (PER_IS_MSVC_PURE_OR_NATIVE_EH(&__cpp_record)) {
+    const auto* __throw_info = __cpp_record.params.pThrowInfo;
+    if (!__cpp_record.params.pExceptionObject || !__throw_info || !__throw_info->pCatchableTypeArray) {
+      terminate();
+    }
+
+#if _EH_RELATIVE_TYPEINFO
+    const auto __throw_image_base = reinterpret_cast<uintptr_t>(__cpp_record.params.pThrowImageBase);
+    const auto* __catchable_type_array =
+        reinterpret_cast<const CatchableTypeArray*>(__throw_image_base + __throw_info->pCatchableTypeArray);
+#else
+    const auto* __catchable_type_array = __throw_info->pCatchableTypeArray;
+#endif
+
+    if (__catchable_type_array->nCatchableTypes <= 0) {
+      terminate();
+    }
+
+#if _EH_RELATIVE_TYPEINFO
+    const auto* __type = reinterpret_cast<CatchableType*>(
+        static_cast<uintptr_t>(__catchable_type_array->arrayOfCatchableTypes[0]) + __throw_image_base);
+#else
+    const auto* __type = __throw_info->pCatchableTypeArray->arrayOfCatchableTypes[0];
+#endif
+
+#pragma warning(suppress : 6255)
+    void* __exception_buffer = alloca(__type->sizeOrOffset);
+    __copy_exception_object(__exception_buffer, __cpp_record.params.pExceptionObject, __type
+#if _EH_RELATIVE_TYPEINFO
+                            ,
+                            __throw_image_base
+#endif
+    );
+
+    __cpp_record.params.pExceptionObject = __exception_buffer;
+  }
+
+  // Under MSVC C++ exception handling (/EHsc), C Win32 API functions like RaiseException
+  // are assumed not to throw synchronous C++ exceptions. As a result, stack unwinding
+  // initiated by RaiseException does not execute local destructors in this frame.
+  // We must explicitly reset __p here so that the reference count on the stored
+  // exception record is properly decremented before unwinding begins.
+  __p = nullptr;
+  RaiseException(__record_copy.ExceptionCode, __record_copy.ExceptionFlags, __record_copy.NumberParameters,
+                 __record_copy.ExceptionInformation);
+  terminate();
+}
 
 nested_exception::nested_exception() noexcept : __ptr_(current_exception()) {}
 
 nested_exception::~nested_exception() noexcept {}
 
 [[noreturn]] void nested_exception::rethrow_nested() const {
-  if (__ptr_ == nullptr)
+  if (__ptr_ == nullptr) {
     terminate();
+  }
----------------
frederick-vs-ja wrote:

These lines don't need to be touched. I _guess_ it's better to avoid unrelated changes.

https://github.com/llvm/llvm-project/pull/94977

From libcxx-commits at lists.llvm.org  Tue Jul  7 06:15:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 06:15:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a4cfc03.ad047e0e.18a05d.78b0@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/23] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/23] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/23] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/23] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/23] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/23] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/23] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/23] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/23] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/23] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/23] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/23] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/23] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/23] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/23] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/23] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/23] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/23] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/23] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/23] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/23] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}

>From 86f59d72480a80cb0d3325cac1ee59d5b08524bb Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 04:18:49 -0700
Subject: [PATCH 22/23] Permit reserve_hint's unqualified lookup

reserve_hint must use ADL per the specification, and thus needs to
be exempted from the libcpp-robust-against-adl check like other
CPOs.
---
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..16c45a524811e 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -20,7 +20,8 @@ AST_MATCHER(clang::CallExpr, isOperator) { return llvm::isa<clang::CXXOperatorCa
 
 AST_MATCHER(clang::UnresolvedLookupExpr, isCustomizationPoint) {
   return std::ranges::any_of(
-      std::array{"swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin"},
+      std::array{
+          "swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin", "reserve_hint"},
       [&](const char* func) { return Node.getName().getAsString() == func; });
 }
 

>From 86941a184f211d27a43076ad2e210f0607c1f470 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 06:14:47 -0700
Subject: [PATCH 23/23] Fix unused variable warning in GCC

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 11379bedf7bf2..6b6a8523afbdc 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -39,7 +39,7 @@ struct HasSizeMember {
 };
 
 struct HasSizeFunction {
-  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+  friend constexpr std::size_t size(HasSizeFunction) { return 42; }
 };
 
 struct HasReserveHintMember {
@@ -47,7 +47,7 @@ struct HasReserveHintMember {
 };
 
 struct HasReserveHintFunction {
-  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction) { return 42; }
 };
 
 struct HasReserveHintMemberBool {
@@ -86,12 +86,12 @@ ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})),
 struct HasSizeAndReserveHint {
   constexpr std::size_t size() { return 42; }
   constexpr std::size_t reserve_hint() { return 0; }
-  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint) { return 0; }
 };
 
 struct HasReserveHintMemberAndFunction {
   constexpr std::size_t reserve_hint() { return 42; }
-  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction) { return 0; }
 };
 
 static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);


From libcxx-commits at lists.llvm.org  Tue Jul  7 06:16:41 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 06:16:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] 5343b20 - [libcxxabi] Don't enable
 -fvisibility=hidden on Windows (#207943)
Message-ID: <6a4cfc39.cfe86883.2618e0.55c5@mx.google.com>


Author: Martin Storsjö
Date: 2026-07-07T16:16:36+03:00
New Revision: 5343b204252d7bf5bdfcacd664e78e9f7f9fd745

URL: https://github.com/llvm/llvm-project/commit/5343b204252d7bf5bdfcacd664e78e9f7f9fd745
DIFF: https://github.com/llvm/llvm-project/commit/5343b204252d7bf5bdfcacd664e78e9f7f9fd745.diff

LOG: [libcxxabi] Don't enable -fvisibility=hidden on Windows (#207943)

This option is supported (and hence gets added by the
target_add_compile_flags_if_supported function) on Windows, but if using
dllexport, there's no point in using hidden visibility. (However if
building both static and shared libraries at the same time, it could be
beneficial to have hidden visibilty in the static libraries.)

In asserts enabled builds of Clang, building with -fvisibility=hidden
caused errors like this:

    dllexport GlobalValue must have default or protected visibility
    ptr @_ZTIv
    [...]
    fatal error: error in backend: Broken module found, compilation aborted!

Normally, combining -fvisibility=hidden with dllexport is not a problem,
but specifically for `__cxxabiv1:: __fundamental_type_info` it does make
Clang trigger an inconsistency, visible in asserts enabled builds of
Clang - see https://github.com/llvm/llvm-project/issues/207963.

This fixes Windows builds with Clang with asserts enabled, after
cb942d009a5ed029ce20d9b75b3604f551f0cb56.

Added: 
    

Modified: 
    libcxxabi/src/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libcxxabi/src/CMakeLists.txt b/libcxxabi/src/CMakeLists.txt
index fc66ce7187170..dd23e9bb5bf79 100644
--- a/libcxxabi/src/CMakeLists.txt
+++ b/libcxxabi/src/CMakeLists.txt
@@ -195,7 +195,14 @@ if (LIBCXXABI_SUPPORTS_SHARED_LIBRARY)
   # Build with -fsized-deallocation, which is default in recent versions of Clang.
   # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
   target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fsized-deallocation)
-  target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
+  if (NOT WIN32)
+    # Building libcxxabi with -fvisibility=hidden on Windows, if dllexport is
+    # used, can trigger a Clang error (if Clang is built with asserts enabled),
+    # https://github.com/llvm/llvm-project/issues/207963
+    # Once fixed, we could consider allowing -fvisibility=hidden on
+    # Windows too (as it can be beneficial for static libraries).
+    target_add_compile_flags_if_supported(cxxabi_shared_objects PRIVATE -fvisibility=hidden)
+  endif()
 
   add_library(cxxabi_shared SHARED)
   set_target_properties(cxxabi_shared
@@ -286,7 +293,14 @@ target_compile_options(cxxabi_static_objects PRIVATE "${LIBCXXABI_ADDITIONAL_COM
 # Build with -fsized-deallocation, which is default in recent versions of Clang.
 # TODO(LLVM 21): This can be dropped once we only support Clang >= 19.
 target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fsized-deallocation)
-target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
+if (NOT WIN32)
+  # Building libcxxabi with -fvisibility=hidden on Windows, if dllexport is
+  # used, can trigger a Clang error (if Clang is built with asserts enabled),
+  # https://github.com/llvm/llvm-project/issues/207963
+  # Once fixed, we could consider allowing -fvisibility=hidden on
+  # Windows too (as it can be beneficial for static libraries).
+  target_add_compile_flags_if_supported(cxxabi_static_objects PRIVATE -fvisibility=hidden)
+endif()
 
 if(LIBCXXABI_HERMETIC_STATIC_LIBRARY)
   # If the hermetic library doesn't define the operator new/delete functions


        

From libcxx-commits at lists.llvm.org  Tue Jul  7 06:16:44 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Tue, 07 Jul 2026 06:16:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libcxxabi] Don't enable
 -fvisibility=hidden on Windows (PR #207943)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207943@github.com>
Message-ID: <6a4cfc3c.91d695f4.35603a.6cd8@mx.google.com>


https://github.com/mstorsjo closed https://github.com/llvm/llvm-project/pull/207943

From libcxx-commits at lists.llvm.org  Tue Jul  7 06:21:45 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 06:21:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a4cfd69.32420449.204364.aa4d@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/23] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/23] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/23] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/23] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/23] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/23] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/23] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/23] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/23] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/23] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/23] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/23] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/23] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/23] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/23] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/23] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/23] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/23] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/23] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/23] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/23] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}

>From 86f59d72480a80cb0d3325cac1ee59d5b08524bb Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 04:18:49 -0700
Subject: [PATCH 22/23] Permit reserve_hint's unqualified lookup

reserve_hint must use ADL per the specification, and thus needs to
be exempted from the libcpp-robust-against-adl check like other
CPOs.
---
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..16c45a524811e 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -20,7 +20,8 @@ AST_MATCHER(clang::CallExpr, isOperator) { return llvm::isa<clang::CXXOperatorCa
 
 AST_MATCHER(clang::UnresolvedLookupExpr, isCustomizationPoint) {
   return std::ranges::any_of(
-      std::array{"swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin"},
+      std::array{
+          "swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin", "reserve_hint"},
       [&](const char* func) { return Node.getName().getAsString() == func; });
 }
 

>From 0020ac432662be8ace647b3c92368cbc13744c24 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 06:14:47 -0700
Subject: [PATCH 23/23] Fix unused variable warning from GCC

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 11379bedf7bf2..6b6a8523afbdc 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -39,7 +39,7 @@ struct HasSizeMember {
 };
 
 struct HasSizeFunction {
-  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+  friend constexpr std::size_t size(HasSizeFunction) { return 42; }
 };
 
 struct HasReserveHintMember {
@@ -47,7 +47,7 @@ struct HasReserveHintMember {
 };
 
 struct HasReserveHintFunction {
-  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction) { return 42; }
 };
 
 struct HasReserveHintMemberBool {
@@ -86,12 +86,12 @@ ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})),
 struct HasSizeAndReserveHint {
   constexpr std::size_t size() { return 42; }
   constexpr std::size_t reserve_hint() { return 0; }
-  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint) { return 0; }
 };
 
 struct HasReserveHintMemberAndFunction {
   constexpr std::size_t reserve_hint() { return 42; }
-  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction) { return 0; }
 };
 
 static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);


From libcxx-commits at lists.llvm.org  Tue Jul  7 08:17:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 08:17:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 31fc40b - [clang][X86] Emit AVX level
 mismatch psABI warnings on function definitions (#199091)
Message-ID: <6a4d186c.fdc477d6.23591.01e9@mx.google.com>


Author: Benjamin Luke
Date: 2026-07-07T11:16:56-04:00
New Revision: 31fc40b0904861d33b926dfd6ea04432e7690c49

URL: https://github.com/llvm/llvm-project/commit/31fc40b0904861d33b926dfd6ea04432e7690c49
DIFF: https://github.com/llvm/llvm-project/commit/31fc40b0904861d33b926dfd6ea04432e7690c49.diff

LOG: [clang][X86] Emit AVX level mismatch psABI warnings on function definitions (#199091)

Emit -WpsABI for x86_64 function definitions whose return type or
parameter type uses a vector wider than 128 bits without the required
ABI feature enabled. 256-bit vectors require avx, and 512-bit vectors
require avx512f.

Previously this diagnostic was only emitted at call sites, so
definitions with wide vector signatures could be introduced without a
warning until they were called. Use the function feature map so
attribute(target("avx/avx512f")) definitions are accepted, and emit no
warnings for prototype-only declarations.

Added: 
    clang/test/CodeGen/target-avx-abi-diag-knr.c
    clang/test/CodeGenCXX/target-avx-abi-diag.cpp

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/CodeGen/Targets/X86.cpp
    clang/test/CodeGen/X86/mmx-inline-asm-error.c
    clang/test/CodeGen/target-builtin-error-3.c
    clang/test/CodeGen/target-features-error-2.c
    libcxx/include/__algorithm/simd_utils.h

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index c7806878a4ff4..0a4bf068c8da4 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -626,6 +626,10 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
 
 - Improved `-Wassign-enum` performance by caching enum enumerator values. (#GH176454)
 
+- Clang now emits `-Wpsabi` diagnostics for externally visible x86-64
+  function definitions that return or take AVX or AVX-512 vector types without
+  enabling the corresponding target feature.
+
 - Fixed a false negative in `-Warray-bounds` where the warning was suppressed
   when accessing a member function on a past-the-end array element.
   (#GH179128)

diff  --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 4e99ebab6ba34..807d372c3c8fd 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -9,6 +9,7 @@
 #include "ABIInfoImpl.h"
 #include "TargetInfo.h"
 #include "clang/Basic/DiagnosticFrontend.h"
+#include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/SmallBitVector.h"
 
 using namespace clang;
@@ -1509,6 +1510,9 @@ class X86_64TargetCodeGenInfo : public TargetCodeGenInfo {
                             const FunctionDecl *Caller,
                             const FunctionDecl *Callee, const CallArgList &Args,
                             QualType ReturnType) const override;
+
+  void checkFunctionABI(CodeGenModule &CGM,
+                        const FunctionDecl *FD) const override;
 };
 } // namespace
 
@@ -1571,6 +1575,65 @@ static bool checkAVXParam(DiagnosticsEngine &Diag, ASTContext &Ctx,
   return false;
 }
 
+void X86_64TargetCodeGenInfo::checkFunctionABI(CodeGenModule &CGM,
+                                               const FunctionDecl *FD) const {
+  auto GetReturnTypeLoc = [](const FunctionDecl *FD) {
+    if (const TypeSourceInfo *TSI = FD->getTypeSourceInfo()) {
+      TypeLoc TL = TSI->getTypeLoc();
+
+      if (auto FTL = TL.IgnoreParens().getAs<FunctionTypeLoc>()) {
+        SourceLocation Loc = FTL.getReturnLoc().getBeginLoc();
+        if (Loc.isValid())
+          return Loc;
+      }
+    }
+
+    SourceLocation Loc = FD->getLocation();
+    if (Loc.isValid())
+      return Loc;
+
+    return FD->getBeginLoc();
+  };
+
+  auto Check = [&](QualType Ty, SourceLocation Loc, bool IsReturn) {
+    if (!Ty->isVectorType())
+      return false;
+    if (CGM.getContext().getTypeSize(Ty) <= 128)
+      return false;
+
+    StringRef Feature =
+        CGM.getContext().getTypeSize(Ty) > 256 ? "avx512f" : "avx";
+
+    llvm::StringMap<bool> FeatureMap;
+    CGM.getContext().getFunctionFeatureMap(FeatureMap, FD);
+    if (!FeatureMap.lookup(Feature)) {
+      CGM.getDiags().Report(Loc, diag::warn_avx_calling_convention)
+          << !IsReturn << Ty << Feature;
+      return true;
+    }
+
+    return false;
+  };
+
+  // psABI warnings & errors for function definitions that are only visible
+  // in this translation unit are handled at call site by checkFunctionCallABI.
+  if (!FD->isExternallyVisible())
+    return;
+
+  // First check the return type and emit diagnostic if required.
+  Check(FD->getReturnType(), GetReturnTypeLoc(FD), true);
+
+  // Go through the parameters and emit a warning for the first vector found
+  // without the matching function AVX level attribute.
+  for (const ParmVarDecl *P : FD->parameters()) {
+    SourceLocation Loc = P->getLocation();
+    if (Loc.isInvalid())
+      Loc = P->getBeginLoc();
+    if (Check(P->getType(), Loc, false))
+      return;
+  }
+}
+
 void X86_64TargetCodeGenInfo::checkFunctionCallABI(CodeGenModule &CGM,
                                                    SourceLocation CallLoc,
                                                    const FunctionDecl *Caller,

diff  --git a/clang/test/CodeGen/X86/mmx-inline-asm-error.c b/clang/test/CodeGen/X86/mmx-inline-asm-error.c
index 8a2f991a537a2..7f7f53a553057 100644
--- a/clang/test/CodeGen/X86/mmx-inline-asm-error.c
+++ b/clang/test/CodeGen/X86/mmx-inline-asm-error.c
@@ -2,6 +2,8 @@
 // RUN: %clang_cc1 -verify=omp -triple x86_64-unknown-unknown -emit-llvm-only -fopenmp %s
 typedef int vec256 __attribute__((ext_vector_type(8)));
 
+// omp-warning at +2 {{AVX vector return of type 'vec256' (vector of 8 'int' values) without 'avx' enabled changes the ABI}}
+// omp-warning at +1 {{AVX vector argument of type 'vec256' (vector of 8 'int' values) without 'avx' enabled changes the ABI}}
 vec256 foo(vec256 in) {
   vec256 out;
 

diff  --git a/clang/test/CodeGen/target-avx-abi-diag-knr.c b/clang/test/CodeGen/target-avx-abi-diag-knr.c
new file mode 100644
index 0000000000000..c9fca7d39e042
--- /dev/null
+++ b/clang/test/CodeGen/target-avx-abi-diag-knr.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -std=c17 -verify=no256,knr -o - -S
+// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -std=c17 -target-feature +avx -verify=knr -o - -S
+// REQUIRES: x86-registered-target
+
+typedef short avx256Type __attribute__((vector_size(32)));
+
+// knr-warning at +3 {{a function definition without a prototype is deprecated in all versions of C and is not supported in C23}}
+// no256-warning at +2 {{AVX vector return of type 'avx256Type' (vector of 16 'short' values) without 'avx' enabled changes the ABI}}
+// no256-warning at +2 {{AVX vector argument of type 'avx256Type' (vector of 16 'short' values) without 'avx' enabled changes the ABI}}
+avx256Type knr_def(x)
+  avx256Type x;
+{
+  return x;
+}

diff  --git a/clang/test/CodeGen/target-builtin-error-3.c b/clang/test/CodeGen/target-builtin-error-3.c
index 056dc940f7a93..75754f40ac44d 100644
--- a/clang/test/CodeGen/target-builtin-error-3.c
+++ b/clang/test/CodeGen/target-builtin-error-3.c
@@ -17,32 +17,38 @@ typedef __attribute__ ((ext_vector_type(16),__aligned__( 64))) float float16;
 static inline half8 __attribute__((__overloadable__)) convert_half( float8 a ) {
   return __extension__ ({ __m256 __a = (a); (__m128i)__builtin_ia32_vcvtps2ph256((__v8sf)__a, (0x00)); }); // expected-error {{'__builtin_ia32_vcvtps2ph256' needs target feature f16c}}
 }
+// Internal definitions do not warn.
 static inline half16 __attribute__((__overloadable__)) convert_half( float16 a ) {
   half16 r;
   r.lo = convert_half(a.lo);
   return r;
 }
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 void avx_test( uint16_t *destData, float16 argbF)
 {
   ((half16U *)destData)[0] = convert_half(argbF);
 }
 
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 half16 test( float16 a ) {
   half16 r;
   r.lo = convert_half(a.lo);
   return r;
 }
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 void avx_test2( uint16_t *destData, float16 argbF)
 {
   // expected-warning at +1{{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
   ((half16U *)destData)[0] = test(argbF);
 }
 
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 __attribute__((always_inline)) half16 test2( float16 a ) {
   half16 r;
   r.lo = convert_half(a.lo);
   return r;
 }
+// expected-warning at +1 {{AVX vector argument of type 'float16' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
 void avx_test3( uint16_t *destData, float16 argbF)
 {
   ((half16U *)destData)[0] = test2(argbF);

diff  --git a/clang/test/CodeGen/target-features-error-2.c b/clang/test/CodeGen/target-features-error-2.c
index 1599b0135b747..8f2ef35b8a931 100644
--- a/clang/test/CodeGen/target-features-error-2.c
+++ b/clang/test/CodeGen/target-features-error-2.c
@@ -8,6 +8,7 @@
 #include <x86intrin.h>
 
 #if NEED_AVX_1
+// expected-warning at +1 {{AVX vector argument of type '__m256i' (vector of 4 'long long' values) without 'avx' enabled changes the ABI}}
 int baz(__m256i a) {
   return _mm256_extract_epi32(a, 3); // expected-error {{'__builtin_ia32_vec_ext_v8si' needs target feature avx}}
 }

diff  --git a/clang/test/CodeGenCXX/target-avx-abi-diag.cpp b/clang/test/CodeGenCXX/target-avx-abi-diag.cpp
new file mode 100644
index 0000000000000..66312d513058e
--- /dev/null
+++ b/clang/test/CodeGenCXX/target-avx-abi-diag.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -emit-llvm -disable-llvm-passes -verify=noavx,noavx512 -o - %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -target-feature +avx -emit-llvm -disable-llvm-passes -verify=noavx512 -o - %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -target-feature +avx512f -emit-llvm -disable-llvm-passes -verify=both -o - %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -Wno-psabi -emit-llvm -disable-llvm-passes -verify=suppressed -o - %s
+// REQUIRES: x86-registered-target
+
+// both-no-diagnostics
+// suppressed-no-diagnostics
+
+typedef float v8f __attribute__((vector_size(32)));
+typedef float v16f __attribute__((vector_size(64)));
+
+// Prototype-only declarations do not warn.
+v8f proto256(v8f);
+v16f proto512(v16f);
+
+// noavx-warning at +2 {{AVX vector return of type 'v8f' (vector of 8 'float' values) without 'avx' enabled changes the ABI}}
+// noavx-warning at +1 {{AVX vector argument of type 'v8f' (vector of 8 'float' values) without 'avx' enabled changes the ABI}}
+v8f def256(v8f x) {
+  return x;
+}
+
+// noavx512-warning at +2 {{AVX vector return of type 'v16f' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
+// noavx512-warning at +1 {{AVX vector argument of type 'v16f' (vector of 16 'float' values) without 'avx512f' enabled changes the ABI}}
+v16f def512(v16f x) {
+  return x;
+}
+
+// Internal definitions do not warn
+static v8f internal_def256(v8f x) {
+  return x;
+}
+
+__attribute__((target("avx")))
+v8f def256_avx(v8f x) {
+  return x;
+}
+
+__attribute__((target("avx512f")))
+v16f def512_avx512(v16f x) {
+  return x;
+}
+
+// Check to make sure deduced return lambdas still have valid source location which allows these pragmas to work
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpsabi"
+template <class T>
+T template_lambda_return() {
+  return []() { return T{}; }();
+}
+
+v8f use_template_lambda_return() {
+  return template_lambda_return<v8f>();
+}
+#pragma clang diagnostic pop

diff  --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index c47f79ac7f1dd..495aed4e21216 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -90,6 +90,9 @@ inline constexpr size_t __native_vector_size = 1;
 template <class _ArithmeticT, size_t _Np>
 using __simd_vector __attribute__((__ext_vector_type__(_Np))) _LIBCPP_NODEBUG = _ArithmeticT;
 
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
+
 template <class _VecT>
 inline constexpr size_t __simd_vector_size_v = []<bool _False = false>() -> size_t {
   static_assert(_False, "Not a vector!");
@@ -115,8 +118,6 @@ template <class _VecT, class _Iter>
 }
 
 // Load the first _Np elements, zero the rest
-_LIBCPP_DIAGNOSTIC_PUSH
-_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
 template <class _VecT, size_t _Np, class _Iter>
 [[__nodiscard__]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _VecT __partial_load(_Iter __iter) noexcept {
   return [=]<size_t... _LoadIndices, size_t... _ZeroIndices>(
@@ -124,7 +125,6 @@ template <class _VecT, size_t _Np, class _Iter>
     return _VecT{__iter[_LoadIndices]..., ((void)_ZeroIndices, 0)...};
   }(make_index_sequence<_Np>{}, make_index_sequence<__simd_vector_size_v<_VecT> - _Np>{});
 }
-_LIBCPP_DIAGNOSTIC_POP
 
 template <class _Tp, size_t _Np>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __any_of(__simd_vector<_Tp, _Np> __vec) noexcept {
@@ -174,6 +174,7 @@ template <class _Tp, size_t _Np>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t __find_first_not_set(__simd_vector<_Tp, _Np> __vec) noexcept {
   return std::__find_first_set(~__vec);
 }
+_LIBCPP_DIAGNOSTIC_POP
 
 _LIBCPP_END_NAMESPACE_STD
 


        

From libcxx-commits at lists.llvm.org  Tue Jul  7 08:17:12 2026
From: libcxx-commits at lists.llvm.org (Aaron Ballman via libcxx-commits)
Date: Tue, 07 Jul 2026 08:17:12 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libc] [libcxx] [clang][X86] Emit AVX
 level mismatch psABI warnings on function definitions (PR #199091)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/199091@github.com>
Message-ID: <6a4d1878.f06f4994.80921.d71e@mx.google.com>


https://github.com/AaronBallman closed https://github.com/llvm/llvm-project/pull/199091

From libcxx-commits at lists.llvm.org  Tue Jul  7 08:36:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 08:36:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ThreadSafety] Add thread safety
 annotations for variadic std::scoped_lock (PR #204462)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204462@github.com>
Message-ID: <6a4d1d05.eb24c6ea.27aedc.b882@mx.google.com>


AnthonyCalandraGeotab wrote:

Anything actionable on my end? @philnik777 

https://github.com/llvm/llvm-project/pull/204462

From libcxx-commits at lists.llvm.org  Tue Jul  7 08:44:50 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Tue, 07 Jul 2026 08:44:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Set
 `__cpp_lib_reference_from_temporary` (PR #207920)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207920@github.com>
Message-ID: <6a4d1ef2.dde95cf2.3c4a3f.1b1d@mx.google.com>


yronglin wrote:

> If we want a separated FTM setting/bumping PR, we may also want that PR to update release notes and CSV lists.
> 
> But do we really want such a PR?

We also can update FTM in one of these three PRs, I have no preference on this. The reason I did this was because I originally thought we had to strictly follow the GitHub issues for pull requests(1:1 mapping).

Since there are three dependent PRs, I think this PR is premature (or even unnecessary), so I will close it for now and reopen it if needed.

https://github.com/llvm/llvm-project/pull/207920

From libcxx-commits at lists.llvm.org  Tue Jul  7 08:44:52 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Tue, 07 Jul 2026 08:44:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Set
 `__cpp_lib_reference_from_temporary` (PR #207920)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207920@github.com>
Message-ID: <6a4d1ef4.1ded2248.161b49.0c65@mx.google.com>


https://github.com/yronglin closed https://github.com/llvm/llvm-project/pull/207920

From libcxx-commits at lists.llvm.org  Tue Jul  7 08:53:04 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 08:53:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 72e1037 - [libc++][ThreadSafety] Add
 thread safety annotations for variadic std::scoped_lock (#204462)
Message-ID: <6a4d20e0.481afadf.196d57.d17e@mx.google.com>


Author: AnthonyCalandraGeotab
Date: 2026-07-07T17:52:56+02:00
New Revision: 72e1037c32e22759b682a0e973af6b95b0630a01

URL: https://github.com/llvm/llvm-project/commit/72e1037c32e22759b682a0e973af6b95b0630a01
DIFF: https://github.com/llvm/llvm-project/commit/72e1037c32e22759b682a0e973af6b95b0630a01.diff

LOG: [libc++][ThreadSafety] Add thread safety annotations for variadic std::scoped_lock (#204462)

The thread safety annotations on std::scoped_lock were previously only
applied to the empty and single-mutex specializations. The general
variadic specialization carried no annotations, so -Wthread-safety
considered none of the mutexes held inside a multi-mutex scoped_lock
block and reported the guarded data accessed there as unprotected.

Now that Clang supports pack expansion inside thread safety attributes
(landed for Clang 21), annotate the variadic scoped_lock with
acquire_capability/requires_capability on its constructors,
release_capability on its destructor, and scoped_lockable on the class.

Fixes #42000.

Co-authored-by: Anthony Calandra <anthony at anthony-calandra.com>

Added: 
    libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.pass.cpp
    libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.verify.cpp

Modified: 
    libcxx/docs/ReleaseNotes/23.rst
    libcxx/include/__configuration/attributes.h
    libcxx/include/__mutex/lock_guard.h
    libcxx/include/__mutex/mutex.h
    libcxx/include/mutex
    libcxx/include/shared_mutex

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 312841c1de36a..1213e8f43c8cd 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -71,6 +71,9 @@ Improvements and New Features
   unspecified non-null string, so user code that does not match on the exact message remains correct.
 - ``ifstream::read`` has been optimized to pass through large reads to system calls directly instead of copying them in
   chunks.
+- ``std::scoped_lock`` now carries Clang thread safety annotations when locking multiple mutexes, so
+  ``-Wthread-safety`` no longer incorrectly reports the guarded data accessed inside the block as
+  unprotected. This requires Clang 21 or later.
 
 Deprecations and Removals
 -------------------------

diff  --git a/libcxx/include/__configuration/attributes.h b/libcxx/include/__configuration/attributes.h
index c543ba9cb9e25..a49bedd1b874a 100644
--- a/libcxx/include/__configuration/attributes.h
+++ b/libcxx/include/__configuration/attributes.h
@@ -342,10 +342,10 @@
 #  define _LIBCPP_TRY_ACQUIRE_SHARED_CAPABILITY(...)
 #endif
 
-#if __has_cpp_attribute(_Clang::__release_capability__)
-#  define _LIBCPP_RELEASE_CAPABILITY [[_Clang::__release_capability__]]
+#if __has_attribute(__release_capability__)
+#  define _LIBCPP_RELEASE_CAPABILITY(...) __attribute__((__release_capability__(__VA_ARGS__)))
 #else
-#  define _LIBCPP_RELEASE_CAPABILITY
+#  define _LIBCPP_RELEASE_CAPABILITY(...)
 #endif
 
 #if __has_cpp_attribute(_Clang::__release_shared_capability__)

diff  --git a/libcxx/include/__mutex/lock_guard.h b/libcxx/include/__mutex/lock_guard.h
index 67a25ed2f8a89..29022e8c23ece 100644
--- a/libcxx/include/__mutex/lock_guard.h
+++ b/libcxx/include/__mutex/lock_guard.h
@@ -34,7 +34,7 @@ class _LIBCPP_SCOPED_LOCKABLE lock_guard {
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI lock_guard(mutex_type& __m, adopt_lock_t) _LIBCPP_REQUIRES_CAPABILITY(__m)
       : __m_(__m) {}
-  _LIBCPP_RELEASE_CAPABILITY _LIBCPP_HIDE_FROM_ABI ~lock_guard() { __m_.unlock(); }
+  _LIBCPP_RELEASE_CAPABILITY() _LIBCPP_HIDE_FROM_ABI ~lock_guard() { __m_.unlock(); }
 
   lock_guard(lock_guard const&)            = delete;
   lock_guard& operator=(lock_guard const&) = delete;

diff  --git a/libcxx/include/__mutex/mutex.h b/libcxx/include/__mutex/mutex.h
index d0addacc2893f..f71cba03cd4dc 100644
--- a/libcxx/include/__mutex/mutex.h
+++ b/libcxx/include/__mutex/mutex.h
@@ -39,7 +39,7 @@ class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_CAPABILITY("mutex") mutex {
 
   _LIBCPP_ACQUIRE_CAPABILITY() void lock();
   [[__nodiscard__]] _LIBCPP_TRY_ACQUIRE_CAPABILITY(true) bool try_lock() _NOEXCEPT;
-  _LIBCPP_RELEASE_CAPABILITY void unlock() _NOEXCEPT;
+  _LIBCPP_RELEASE_CAPABILITY() void unlock() _NOEXCEPT;
 
   typedef __libcpp_mutex_t* native_handle_type;
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__m_; }

diff  --git a/libcxx/include/mutex b/libcxx/include/mutex
index 75b080f8cd088..26ff343a21c1d 100644
--- a/libcxx/include/mutex
+++ b/libcxx/include/mutex
@@ -458,7 +458,7 @@ public:
     __m_.lock();
   }
 
-  _LIBCPP_RELEASE_CAPABILITY _LIBCPP_HIDE_FROM_ABI ~scoped_lock() { __m_.unlock(); }
+  _LIBCPP_RELEASE_CAPABILITY() _LIBCPP_HIDE_FROM_ABI ~scoped_lock() { __m_.unlock(); }
 
   [[nodiscard]]
   _LIBCPP_HIDE_FROM_ABI explicit scoped_lock(adopt_lock_t, mutex_type& __m) _LIBCPP_REQUIRES_CAPABILITY(__m)
@@ -469,25 +469,31 @@ public:
 };
 
 template <class... _MArgs>
-class scoped_lock {
+class _LIBCPP_SCOPED_LOCKABLE scoped_lock {
   static_assert(sizeof...(_MArgs) > 1, "At least 2 lock types required");
   typedef tuple<_MArgs&...> _MutexTuple;
 
 public:
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit scoped_lock(_MArgs&... __margs) : __t_(__margs...) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit scoped_lock(_MArgs&... __margs) _LIBCPP_ACQUIRE_CAPABILITY(__margs...)
+      : __t_(__margs...) {
     std::lock(__margs...);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI scoped_lock(adopt_lock_t, _MArgs&... __margs) : __t_(__margs...) {}
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI scoped_lock(adopt_lock_t, _MArgs&... __margs)
+      _LIBCPP_REQUIRES_CAPABILITY(__margs...)
+      : __t_(__margs...) {}
 
-  _LIBCPP_HIDE_FROM_ABI ~scoped_lock() { __unlock_unpack(make_index_sequence<sizeof...(_MArgs)>(), __t_); }
+  _LIBCPP_RELEASE_CAPABILITY() _LIBCPP_HIDE_FROM_ABI ~scoped_lock() {
+    __unlock_unpack(make_index_sequence<sizeof...(_MArgs)>(), __t_);
+  }
 
   scoped_lock(scoped_lock const&)            = delete;
   scoped_lock& operator=(scoped_lock const&) = delete;
 
 private:
   template <size_t... _Indx>
-  _LIBCPP_HIDE_FROM_ABI static void __unlock_unpack(index_sequence<_Indx...>, _MutexTuple& __mt) {
+  _LIBCPP_HIDE_FROM_ABI static void __unlock_unpack(index_sequence<_Indx...>, _MutexTuple& __mt)
+      _LIBCPP_RELEASE_CAPABILITY(std::get<_Indx>(__mt)...) {
     (std::get<_Indx>(__mt).unlock(), ...);
   }
 

diff  --git a/libcxx/include/shared_mutex b/libcxx/include/shared_mutex
index 9479f0aa616f8..8fc89fe1cb97c 100644
--- a/libcxx/include/shared_mutex
+++ b/libcxx/include/shared_mutex
@@ -200,7 +200,7 @@ public:
   [[nodiscard]] _LIBCPP_TRY_ACQUIRE_CAPABILITY(true) _LIBCPP_HIDE_FROM_ABI bool try_lock() {
     return __base_.try_lock();
   }
-  _LIBCPP_RELEASE_CAPABILITY _LIBCPP_HIDE_FROM_ABI void unlock() { return __base_.unlock(); }
+  _LIBCPP_RELEASE_CAPABILITY() _LIBCPP_HIDE_FROM_ABI void unlock() { return __base_.unlock(); }
 
   // Shared ownership
   _LIBCPP_ACQUIRE_SHARED_CAPABILITY _LIBCPP_HIDE_FROM_ABI void lock_shared() { return __base_.lock_shared(); }
@@ -262,7 +262,7 @@ public:
     return true;
   }
 
-  _LIBCPP_RELEASE_CAPABILITY void unlock();
+  _LIBCPP_RELEASE_CAPABILITY() void unlock();
 
   // Shared ownership
   _LIBCPP_ACQUIRE_SHARED_CAPABILITY void lock_shared();

diff  --git a/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.pass.cpp b/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.pass.cpp
new file mode 100644
index 0000000000000..06751f9d49215
--- /dev/null
+++ b/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: no-threads, c++03, c++11, c++14
+
+// <mutex>
+
+// GCC doesn't have thread safety attributes
+// UNSUPPORTED: gcc
+
+// ADDITIONAL_COMPILE_FLAGS: -Wthread-safety
+
+#include <mutex>
+
+std::mutex m1;
+std::mutex m2;
+int data1 __attribute__((guarded_by(m1)));
+int data2 __attribute__((guarded_by(m2)));
+int data3;
+
+static void no_mutex() {
+  std::scoped_lock<> lock;
+  data3++;
+}
+
+static void one_mutex() {
+  std::scoped_lock<std::mutex> lock(m1);
+  data1++;
+}
+
+static void two_mutexes() {
+  std::scoped_lock<std::mutex, std::mutex> lock(m1, m2);
+  data1++;
+  data2++;
+}
+
+static void adopt_none() {
+  std::scoped_lock<> lock(std::adopt_lock);
+  data3++;
+}
+
+static void adopt_one() {
+  m1.lock();
+  std::scoped_lock<std::mutex> lock(std::adopt_lock, m1);
+  data1++;
+}
+
+static void adopt_two() {
+  m1.lock();
+  m2.lock();
+  std::scoped_lock<std::mutex, std::mutex> lock(std::adopt_lock, m1, m2);
+  data1++;
+  data2++;
+}
+
+int main(int, char**) {
+  no_mutex();
+  one_mutex();
+  two_mutexes();
+  adopt_none();
+  adopt_one();
+  adopt_two();
+  return 0;
+}

diff  --git a/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.verify.cpp b/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.verify.cpp
new file mode 100644
index 0000000000000..3f9dd62c91f52
--- /dev/null
+++ b/libcxx/test/extensions/clang/thread/thread.mutex/thread_safety_scoped_lock.verify.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: no-threads, c++03, c++11, c++14
+
+// <mutex>
+
+// GCC doesn't have thread safety attributes
+// UNSUPPORTED: gcc
+
+// ADDITIONAL_COMPILE_FLAGS: -Wthread-safety
+
+#include <mutex>
+
+std::mutex m1;
+std::mutex m2;
+int data1 __attribute__((guarded_by(m1)));
+int data2 __attribute__((guarded_by(m2)));
+
+void no_lock_write() {
+  data1++; // expected-warning {{writing variable 'data1' requires holding mutex 'm1' exclusively}}
+}
+
+void no_lock_write2() {
+  std::scoped_lock<> lock;
+  data1++; // expected-warning {{writing variable 'data1' requires holding mutex 'm1' exclusively}}
+}
+
+void wrong_lock_write() {
+  std::scoped_lock<std::mutex> lock(m1);
+  data2++; // expected-warning {{writing variable 'data2' requires holding mutex 'm2' exclusively}}
+}
+
+void adopt_without_lock() {
+  // expected-warning at +1 {{calling function 'scoped_lock' requires holding mutex 'm1' exclusively}}
+  std::scoped_lock<std::mutex> lock(std::adopt_lock, m1);
+  data1++; // expected-warning {{writing variable 'data1' requires holding mutex 'm1' exclusively}}
+}
+
+void adopt_two_only_one_locked() {
+  m1.lock();
+  // expected-warning at +1 {{calling function 'scoped_lock' requires holding mutex 'm2' exclusively}}
+  std::scoped_lock<std::mutex, std::mutex> lock(std::adopt_lock, m1, m2);
+  data1++;
+  data2++; // expected-warning {{writing variable 'data2' requires holding mutex 'm2' exclusively}}
+}
+
+void after_scoped_lock_scope() {
+  {
+    std::scoped_lock<std::mutex> lock(m1);
+    data1++;
+  }
+  data1++; // expected-warning {{writing variable 'data1' requires holding mutex 'm1' exclusively}}
+}


        

From libcxx-commits at lists.llvm.org  Tue Jul  7 08:53:05 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 08:53:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ThreadSafety] Add thread safety
 annotations for variadic std::scoped_lock (PR #204462)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204462@github.com>
Message-ID: <6a4d20e1.bf38ae28.39239a.ce2e@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/204462

From libcxx-commits at lists.llvm.org  Tue Jul  7 08:53:31 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 08:53:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ThreadSafety] Add thread safety
 annotations for variadic std::scoped_lock (PR #204462)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204462@github.com>
Message-ID: <6a4d20fb.af6ba3ed.36d9db.fad7@mx.google.com>


github-actions[bot] wrote:

<!--LLVM BUILDBOT INFORMATION COMMENT-->

@AnthonyCalandraGeotab Congratulations on having your first Pull Request (PR) merged into the LLVM Project!

Your changes will be combined with recent changes from other authors, then tested by our [build bots](https://lab.llvm.org/buildbot/). If there is a problem with a build, you may receive a report in an email or a comment on this PR.

Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail [here](https://llvm.org/docs/MyFirstTypoFix.html#myfirsttypofix-issues-after-landing-your-pr).

If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of [LLVM development](https://llvm.org/docs/DeveloperPolicy.html#patch-reversion-policy). You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are working as expected, well done!


https://github.com/llvm/llvm-project/pull/204462

From libcxx-commits at lists.llvm.org  Tue Jul  7 09:01:04 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 09:01:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 1045d1e - [libc++][ranges] Applied
 `[[nodiscard]]` to `zip_view` (#207667)
Message-ID: <6a4d22c0.faf16b37.2f0e1d.e5bf@mx.google.com>


Author: Hristo Hristov
Date: 2026-07-07T19:00:58+03:00
New Revision: 1045d1ebca559d7e6dee49b002142e52bb61b49e

URL: https://github.com/llvm/llvm-project/commit/1045d1ebca559d7e6dee49b002142e52bb61b49e
DIFF: https://github.com/llvm/llvm-project/commit/1045d1ebca559d7e6dee49b002142e52bb61b49e.diff

LOG: [libc++][ranges] Applied `[[nodiscard]]` to `zip_view` (#207667)

`[[nodiscard]]` should be applied to functions where discarding the
return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.zip

Towards #172124

Added: 
    libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range_adaptor_types.h

Modified: 
    libcxx/include/__ranges/zip_view.h
    libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h
index bea64c4997a9e..aa13bed5f428d 100644
--- a/libcxx/include/__ranges/zip_view.h
+++ b/libcxx/include/__ranges/zip_view.h
@@ -134,19 +134,19 @@ class zip_view : public view_interface<zip_view<_Views...>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_view(_Views... __views) : __views_(std::move(__views)...) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
     requires(!(__simple_view<_Views> && ...))
   {
     return __iterator<false>(std::__tuple_transform(ranges::begin, __views_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires(range<const _Views> && ...)
   {
     return __iterator<true>(std::__tuple_transform(ranges::begin, __views_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!(__simple_view<_Views> && ...))
   {
     if constexpr (!__zip_is_common<_Views...>) {
@@ -158,7 +158,7 @@ class zip_view : public view_interface<zip_view<_Views...>> {
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires(range<const _Views> && ...)
   {
     if constexpr (!__zip_is_common<const _Views...>) {
@@ -170,7 +170,7 @@ class zip_view : public view_interface<zip_view<_Views...>> {
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires(sized_range<_Views> && ...)
   {
     return std::apply(
@@ -181,7 +181,7 @@ class zip_view : public view_interface<zip_view<_Views...>> {
         std::__tuple_transform(ranges::size, __views_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires(sized_range<const _Views> && ...)
   {
     return std::apply(
@@ -267,7 +267,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     requires _Const && (convertible_to<iterator_t<_Views>, iterator_t<__maybe_const<_Const, _Views>>> && ...)
       : __current_(std::move(__i.__current_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const {
     return std::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_);
   }
 
@@ -315,7 +315,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](
diff erence_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](
diff erence_type __n) const
     requires __zip_all_random_access<_Const, _Views...>
   {
     return std::__tuple_transform(
@@ -338,7 +338,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     return __x.__current_ <=> __y.__current_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, 
diff erence_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, 
diff erence_type __n)
     requires __zip_all_random_access<_Const, _Views...>
   {
     auto __r = __i;
@@ -346,13 +346,13 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     return __r;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(
diff erence_type __n, const __iterator& __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(
diff erence_type __n, const __iterator& __i)
     requires __zip_all_random_access<_Const, _Views...>
   {
     return __i + __n;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, 
diff erence_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, 
diff erence_type __n)
     requires __zip_all_random_access<_Const, _Views...>
   {
     auto __r = __i;
@@ -360,7 +360,8 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
     return __r;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr 
diff erence_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr 
diff erence_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires(sized_sentinel_for<iterator_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_Const, _Views>>> &&
              ...)
   {
@@ -374,7 +375,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
         __
diff s);
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept(
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept(
       (noexcept(ranges::iter_move(std::declval<const iterator_t<__maybe_const<_Const, _Views>>&>())) && ...) &&
       (is_nothrow_move_constructible_v<range_rvalue_reference_t<__maybe_const<_Const, _Views>>> && ...)) {
     return std::__tuple_transform(ranges::iter_move, __i.__current_);
@@ -426,6 +427,7 @@ class zip_view<_Views...>::__sentinel {
     requires(
         sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> &&
         ...)
+  [[nodiscard]]
   _LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t<range_
diff erence_t<__maybe_const<_OtherConst, _Views>>...>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     const auto __
diff s = ranges::__tuple_zip_transform(minus<>(), __iter_current(__x), __y.__end_);
@@ -443,6 +445,7 @@ class zip_view<_Views...>::__sentinel {
     requires(
         sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> &&
         ...)
+  [[nodiscard]]
   _LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t<range_
diff erence_t<__maybe_const<_OtherConst, _Views>>...>
   operator-(const __sentinel& __y, const __iterator<_OtherConst>& __x) {
     return -(__x - __y);
@@ -456,10 +459,10 @@ namespace views {
 namespace __zip {
 
 struct __fn {
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()() noexcept { return empty_view<tuple<>>{}; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()() noexcept { return empty_view<tuple<>>{}; }
 
   template <class... _Ranges>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
   operator()(_Ranges&&... __rs) noexcept(noexcept(zip_view<all_t<_Ranges&&>...>(std::forward<_Ranges>(__rs)...)))
       -> decltype(zip_view<all_t<_Ranges&&>...>(std::forward<_Ranges>(__rs)...)) {
     return zip_view<all_t<_Ranges>...>(std::forward<_Ranges>(__rs)...);

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
index e681d338dc786..0c4e60701e1eb 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 #include <vector>
 
+#include "../range_adaptor_types.h"
 #include "test_iterators.h"
 
 struct CommonView : std::ranges::view_interface<CommonView> {
@@ -28,26 +29,6 @@ static_assert(std::ranges::common_range<CommonView>);
 static_assert(std::same_as<std::ranges::iterator_t<CommonView>, std::ranges::iterator_t<const CommonView>>);
 static_assert(std::same_as<std::ranges::sentinel_t<CommonView>, std::ranges::sentinel_t<const CommonView>>);
 
-struct ForwardSizedNonCommon {
-  int* buffer_      = nullptr;
-  std::size_t size_ = 0;
-
-  template <std::size_t N>
-  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
-
-  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
-
-  using iterator = forward_sized_iterator<int*>;
-  using sentinel = sized_sentinel<iterator>;
-
-  constexpr iterator begin() const { return iterator(buffer_); }
-  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
-};
-static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
-static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
-static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
-static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
-
 template <class... Args>
 struct Invocable {
   int operator()(Args...) const { return 5; }

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..0667757b07658
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip/nodiscard.verify.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <vector>
+#include <utility>
+
+#include "../range_adaptor_types.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+void test() {
+  int arr[]{94, 82, 49};
+  std::vector<int> range;
+
+  std::ranges::zip_view zv{range, arr};
+
+  // [range.zip.view]
+  {
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    zv.begin();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(zv).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    zv.end();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(zv).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    zv.size();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(zv).size();
+  }
+
+  // [range.zip.iterator]
+
+  {
+    auto it  = zv.begin();
+    auto cIt = std::as_const(zv).begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *cIt;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt[0];
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    iter_move(it);
+  }
+
+  // [range.zip.sentinel]
+
+  {
+    std::ranges::zip_view nonCommonZv{ForwardSizedNonCommon(arr)};
+    static_assert(!std::ranges::common_range<decltype(nonCommonZv)>);
+    static_assert(!std::same_as<decltype(nonCommonZv.end()), decltype(nonCommonZv.begin())>);
+    auto it  = nonCommonZv.begin();
+    auto cIt = std::as_const(nonCommonZv).begin();
+    auto st  = nonCommonZv.end();
+    auto cSt = std::as_const(nonCommonZv).end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - cSt;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cSt - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    cIt - st;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - cIt;
+  }
+
+  // [range.zip.overview]
+
+  {
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::zip();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::zip(arr, arr);
+  }
+}

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/libcxx/ranges/range.adaptors/range_adaptor_types.h
new file mode 100644
index 0000000000000..cea4cb0dfddd5
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range_adaptor_types.h
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADAPTOR_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADAPTOR_TYPES_H
+
+#include <cstdint>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+#if TEST_STD_VER <= 20
+#  error "range.adaptor/types.h" can only be included in builds supporting C++20
+#endif // TEST_STD_VER <= 20
+
+struct ForwardSizedNonCommon {
+  int* buffer_      = nullptr;
+  std::size_t size_ = 0;
+
+  template <std::size_t N>
+  constexpr ForwardSizedNonCommon(int (&b)[N]) : buffer_(b), size_(N) {}
+
+  constexpr ForwardSizedNonCommon(int* b, std::size_t s) : buffer_(b), size_(s) {}
+
+  using iterator = forward_sized_iterator<int*>;
+  using sentinel = sized_sentinel<iterator>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel end() const { return sentinel(iterator(buffer_ + size_)); }
+};
+static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
+
+#endif //  TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADAPTOR_TYPES_H


        

From libcxx-commits at lists.llvm.org  Tue Jul  7 09:01:10 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 07 Jul 2026 09:01:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]`
	to `zip_view` (PR #207667)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207667@github.com>
Message-ID: <6a4d22c6.8e75e0cb.5e108.09d3@mx.google.com>


https://github.com/Zingam closed https://github.com/llvm/llvm-project/pull/207667

From libcxx-commits at lists.llvm.org  Tue Jul  7 09:02:47 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 07 Jul 2026 09:02:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`lazy_split` (PR #208036)
Message-ID: <llvm.org/llvm/llvm-project/pull/208036@github.com>


https://github.com/lknknm created https://github.com/llvm/llvm-project/pull/208036

[[nodiscard]] should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.lazy.split

Towards https://github.com/llvm/llvm-project/issues/172124

>From 1a1da3d615018ef52c9eefcbb376679f5cdd7505 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 18:00:56 +0200
Subject: [PATCH] initial: nodiscard implementation to lazy_split_view.h

---
 libcxx/include/__ranges/lazy_split_view.h | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 938dca24cc4fc..01d398a0ac6d8 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -95,14 +95,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
       : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
@@ -111,19 +111,19 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<_View> && forward_range<const _View>
   {
     return __outer_iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
     return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
     if constexpr (forward_range<_View> && forward_range<const _View> && common_range<const _View>) {
       return __outer_iterator<true>{*this, ranges::end(__base_)};
     } else {
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
@@ -342,14 +342,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
-    _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
       return std::move(__i_.__current());
     }
 
-    _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
       __incremented_ = true;
@@ -385,7 +385,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       return __x.__is_done();
     }
 
-    _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
     iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(__i.__outer_current()))) {
       return ranges::iter_move(__i.__outer_current());
     }


From libcxx-commits at lists.llvm.org  Tue Jul  7 09:04:14 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 07 Jul 2026 09:04:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4d237e.d1496011.26913c.8751@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/208036

From libcxx-commits at lists.llvm.org  Tue Jul  7 09:05:14 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 09:05:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4d23ba.faf16b37.2f0e1d.6002@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions h -- libcxx/include/__ranges/lazy_split_view.h --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 01d398a0a..c05c932d0 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -342,7 +342,9 @@ private:
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
+      return __i_.__current();
+    }
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {

``````````

</details>


https://github.com/llvm/llvm-project/pull/208036

From libcxx-commits at lists.llvm.org  Tue Jul  7 09:54:24 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 09:54:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Simplify a bunch of noexcept
	specifications (PR #166397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/166397@github.com>
Message-ID: <6a4d2f40.f732ad81.3a4994.6a0c@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/166397

>From 9e75b0c7393d2b529779871c8d5491ea966da78e Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 4 Nov 2025 17:33:32 +0100
Subject: [PATCH] [libc++] Simplify a bunch of noexcept specifications

---
 libcxx/include/CMakeLists.txt                 |   1 -
 libcxx/include/__hash_table                   |  60 ++-----
 libcxx/include/__memory/allocator_traits.h    |   4 +
 .../__memory/noexcept_move_assign_container.h |  37 -----
 libcxx/include/__split_buffer                 |  37 ++---
 libcxx/include/__tree                         |  40 ++---
 libcxx/include/__vector/vector.h              |  64 ++------
 libcxx/include/__vector/vector_bool.h         |  70 ++------
 libcxx/include/deque                          |  51 ++----
 libcxx/include/forward_list                   |  65 ++------
 libcxx/include/list                           |  61 ++-----
 libcxx/include/queue                          |   1 +
 libcxx/include/stack                          |   1 +
 libcxx/include/string                         |  50 ++----
 .../associative/map/noexcept.compile.pass.cpp | 116 +++++++++++++
 .../move_noexcept.compile.pass.cpp}           |  29 +---
 .../move_noexcept.compile.pass.cpp}           |  29 +---
 .../list.cons/move_noexcept.compile.pass.cpp} |  26 +--
 .../move_noexcept.compile.pass.cpp            |  32 ++++
 .../vector.bool/noexcept.compile.pass.cpp     |  38 +++++
 .../vector/noexcept.compile.pass.cpp          |  38 +++++
 .../unord/unord.map/noexcept.compile.pass.cpp | 154 +++++++++++++++++
 .../unord.multimap/noexcept.compile.pass.cpp  | 155 ++++++++++++++++++
 .../unord.multiset/noexcept.compile.pass.cpp  | 154 +++++++++++++++++
 .../unord/unord.set/noexcept.compile.pass.cpp | 154 +++++++++++++++++
 .../flat.map.cons/move_noexcept.pass.cpp      |  29 ----
 .../flat.multimap.cons/move_noexcept.pass.cpp |  31 ----
 .../flat.multiset.cons/move.pass.cpp          |  19 ---
 .../flat.set/flat.set.cons/move.pass.cpp      |  16 +-
 .../deque.special/swap_noexcept.pass.cpp      |   6 +-
 .../swap_noexcept.compile.pass.cpp            |   6 +-
 .../list/list.special/swap_noexcept.pass.cpp  |   6 +-
 .../vector.bool/move_noexcept.pass.cpp        |  60 -------
 .../vector.bool/swap_noexcept.pass.cpp        |  11 --
 .../vector/vector.cons/move_noexcept.pass.cpp |   5 +-
 .../string.cons/move_noexcept.pass.cpp        |   4 +-
 36 files changed, 1008 insertions(+), 652 deletions(-)
 delete mode 100644 libcxx/include/__memory/noexcept_move_assign_container.h
 create mode 100644 libcxx/test/libcxx/containers/associative/map/noexcept.compile.pass.cpp
 rename libcxx/test/{std/containers/sequences/deque/deque.cons/move_noexcept.pass.cpp => libcxx/containers/sequences/deque/deque.cons/move_noexcept.compile.pass.cpp} (53%)
 rename libcxx/test/{std/containers/sequences/forwardlist/forwardlist.cons/move_noexcept.pass.cpp => libcxx/containers/sequences/forwardlist/move_noexcept.compile.pass.cpp} (53%)
 rename libcxx/test/{std/containers/sequences/list/list.cons/move_noexcept.pass.cpp => libcxx/containers/sequences/list/list.cons/move_noexcept.compile.pass.cpp} (55%)
 create mode 100644 libcxx/test/libcxx/containers/sequences/vector.bool/move_noexcept.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/sequences/vector.bool/noexcept.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/sequences/vector/noexcept.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.map/noexcept.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.multimap/noexcept.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.multiset/noexcept.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.set/noexcept.compile.pass.cpp
 delete mode 100644 libcxx/test/std/containers/sequences/vector.bool/move_noexcept.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..32726900fa3fd 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -605,7 +605,6 @@ set(files
   __memory/destruct_n.h
   __memory/inout_ptr.h
   __memory/is_sufficiently_aligned.h
-  __memory/noexcept_move_assign_container.h
   __memory/out_ptr.h
   __memory/pointer_traits.h
   __memory/ranges_construct_at.h
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 0ce4cf3edd6fd..fa7503d45e712 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -522,13 +522,13 @@ public:
   _LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator() _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value)
       : __size_(0) {}
 
-  _LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator(const allocator_type& __a, size_type __size)
-      _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value)
-      : __size_(__size), __alloc_(__a) {}
+  _LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator(const allocator_type& __a, size_type __size) _NOEXCEPT
+      : __size_(__size),
+        __alloc_(__a) {}
 
-  _LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator(__bucket_list_deallocator&& __x)
-      _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
-      : __size_(std::__exchange(__x.__size_, 0)), __alloc_(std::move(__x.__alloc_)) {}
+  _LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator(__bucket_list_deallocator&& __x) _NOEXCEPT
+      : __size_(std::__exchange(__x.__size_, 0)),
+        __alloc_(std::move(__x.__alloc_)) {}
 
   _LIBCPP_HIDE_FROM_ABI size_type& size() _NOEXCEPT { return __size_; }
   _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __size_; }
@@ -754,17 +754,14 @@ public:
   _LIBCPP_HIDE_FROM_ABI __hash_table(const __hash_table& __u, const allocator_type& __a);
   _LIBCPP_HIDE_FROM_ABI __hash_table(__hash_table&& __u) _NOEXCEPT_(
       is_nothrow_move_constructible<__bucket_list>::value&& is_nothrow_move_constructible<__first_node>::value&&
-          is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<hasher>::value&&
-              is_nothrow_move_constructible<key_equal>::value);
+          is_nothrow_move_constructible<hasher>::value&& is_nothrow_move_constructible<key_equal>::value);
   _LIBCPP_HIDE_FROM_ABI __hash_table(__hash_table&& __u, const allocator_type& __a);
   _LIBCPP_HIDE_FROM_ABI ~__hash_table();
 
   _LIBCPP_HIDE_FROM_ABI __hash_table& operator=(const __hash_table& __u);
   _LIBCPP_HIDE_FROM_ABI __hash_table& operator=(__hash_table&& __u)
-      _NOEXCEPT_(is_nothrow_move_assignable<hasher>::value&& is_nothrow_move_assignable<key_equal>::value &&
-                 ((__node_traits::propagate_on_container_move_assignment::value &&
-                   is_nothrow_move_assignable<__node_allocator>::value) ||
-                  allocator_traits<__node_allocator>::is_always_equal::value));
+      _NOEXCEPT_(is_nothrow_move_assignable<hasher>::value&& is_nothrow_move_assignable<key_equal>::value&&
+                     __is_allocator_aware_container_move_nothrow_v<allocator_type>);
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI void __assign_unique(_InputIterator __first, _InputIterator __last);
   template <class _InputIterator>
@@ -958,14 +955,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> __equal_range_multi(const _Key& __k) const;
 
   _LIBCPP_HIDE_FROM_ABI void swap(__hash_table& __u)
-#if _LIBCPP_STD_VER <= 11
-      _NOEXCEPT_(__is_nothrow_swappable_v<hasher>&& __is_nothrow_swappable_v<key_equal> &&
-                 (!allocator_traits<__pointer_allocator>::propagate_on_container_swap::value ||
-                  __is_nothrow_swappable_v<__pointer_allocator>) &&
-                 (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>));
-#else
       _NOEXCEPT_(__is_nothrow_swappable_v<hasher>&& __is_nothrow_swappable_v<key_equal>);
-#endif
 
   _LIBCPP_HIDE_FROM_ABI size_type max_bucket_count() const _NOEXCEPT { return max_size(); }
   _LIBCPP_HIDE_FROM_ABI size_type bucket_size(size_type __n) const;
@@ -1025,15 +1015,11 @@ private:
 
   _LIBCPP_HIDE_FROM_ABI void __move_assign(__hash_table& __u, false_type);
   _LIBCPP_HIDE_FROM_ABI void __move_assign(__hash_table& __u, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value&& is_nothrow_move_assignable<hasher>::value&&
-                     is_nothrow_move_assignable<key_equal>::value);
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__hash_table& __u) _NOEXCEPT_(
-      !__node_traits::propagate_on_container_move_assignment::value ||
-      (is_nothrow_move_assignable<__pointer_allocator>::value && is_nothrow_move_assignable<__node_allocator>::value)) {
+      _NOEXCEPT_(is_nothrow_move_assignable<hasher>::value&& is_nothrow_move_assignable<key_equal>::value);
+  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__hash_table& __u) _NOEXCEPT {
     __move_assign_alloc(__u, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   }
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__hash_table& __u, true_type) _NOEXCEPT_(
-      is_nothrow_move_assignable<__pointer_allocator>::value&& is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__hash_table& __u, true_type) _NOEXCEPT {
     __bucket_list_.get_deleter().__alloc() = std::move(__u.__bucket_list_.get_deleter().__alloc());
     __node_alloc()                         = std::move(__u.__node_alloc());
   }
@@ -1146,8 +1132,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__hash_table(const __hash_table& __u,
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 __hash_table<_Tp, _Hash, _Equal, _Alloc>::__hash_table(__hash_table&& __u) _NOEXCEPT_(
     is_nothrow_move_constructible<__bucket_list>::value&& is_nothrow_move_constructible<__first_node>::value&&
-        is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<hasher>::value&&
-            is_nothrow_move_constructible<key_equal>::value)
+        is_nothrow_move_constructible<hasher>::value&& is_nothrow_move_constructible<key_equal>::value)
     : __bucket_list_(std::move(__u.__bucket_list_)),
       __first_node_(std::move(__u.__first_node_)),
       __node_alloc_(std::move(__u.__node_alloc_)),
@@ -1292,8 +1277,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__detach() _NOEXCEPT {
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__move_assign(__hash_table& __u, true_type)
-    _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value&& is_nothrow_move_assignable<hasher>::value&&
-                   is_nothrow_move_assignable<key_equal>::value) {
+    _NOEXCEPT_(is_nothrow_move_assignable<hasher>::value&& is_nothrow_move_assignable<key_equal>::value) {
   clear();
   __bucket_list_.reset(__u.__bucket_list_.release());
   __bucket_list_.get_deleter().size()     = __u.__bucket_list_.get_deleter().size();
@@ -1338,10 +1322,8 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__move_assign(__hash_table& __u,
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 inline __hash_table<_Tp, _Hash, _Equal, _Alloc>& __hash_table<_Tp, _Hash, _Equal, _Alloc>::operator=(__hash_table&& __u)
-    _NOEXCEPT_(is_nothrow_move_assignable<hasher>::value&& is_nothrow_move_assignable<key_equal>::value &&
-               ((__node_traits::propagate_on_container_move_assignment::value &&
-                 is_nothrow_move_assignable<__node_allocator>::value) ||
-                allocator_traits<__node_allocator>::is_always_equal::value)) {
+    _NOEXCEPT_(is_nothrow_move_assignable<hasher>::value&& is_nothrow_move_assignable<key_equal>::value&&
+                   __is_allocator_aware_container_move_nothrow_v<allocator_type>) {
   __move_assign(__u, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   return *this;
 }
@@ -2074,15 +2056,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__equal_range_multi(const _Key& __k) c
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 void __hash_table<_Tp, _Hash, _Equal, _Alloc>::swap(__hash_table& __u)
-#if _LIBCPP_STD_VER <= 11
-    _NOEXCEPT_(__is_nothrow_swappable_v<hasher>&& __is_nothrow_swappable_v<key_equal> &&
-               (!allocator_traits<__pointer_allocator>::propagate_on_container_swap::value ||
-                __is_nothrow_swappable_v<__pointer_allocator>) &&
-               (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>))
-#else
-    _NOEXCEPT_(__is_nothrow_swappable_v<hasher>&& __is_nothrow_swappable_v<key_equal>)
-#endif
-{
+    _NOEXCEPT_(__is_nothrow_swappable_v<hasher>&& __is_nothrow_swappable_v<key_equal>) {
   _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
       __node_traits::propagate_on_container_swap::value || this->__node_alloc() == __u.__node_alloc(),
       "unordered container::swap: Either propagate_on_container_swap "
diff --git a/libcxx/include/__memory/allocator_traits.h b/libcxx/include/__memory/allocator_traits.h
index 7038885a8b24f..d32e167ed833f 100644
--- a/libcxx/include/__memory/allocator_traits.h
+++ b/libcxx/include/__memory/allocator_traits.h
@@ -436,6 +436,10 @@ inline const bool __is_cpp17_copy_insertable_v =
      (!__is_std_allocator_v<_Alloc> &&
       __has_construct_v<_Alloc, typename _Alloc::value_type*, const typename _Alloc::value_type&>));
 
+template <typename _Alloc, typename _Traits = allocator_traits<_Alloc> >
+inline const bool __is_allocator_aware_container_move_nothrow_v =
+    _Traits::propagate_on_container_move_assignment::value || _Traits::is_always_equal::value;
+
 _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
diff --git a/libcxx/include/__memory/noexcept_move_assign_container.h b/libcxx/include/__memory/noexcept_move_assign_container.h
deleted file mode 100644
index b0063516aaafc..0000000000000
--- a/libcxx/include/__memory/noexcept_move_assign_container.h
+++ /dev/null
@@ -1,37 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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___MEMORY_NOEXCEPT_MOVE_ASSIGN_CONTAINER_H
-#define _LIBCPP___MEMORY_NOEXCEPT_MOVE_ASSIGN_CONTAINER_H
-
-#include <__config>
-#include <__memory/allocator_traits.h>
-#include <__type_traits/integral_constant.h>
-#include <__type_traits/is_nothrow_assignable.h>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-template <typename _Alloc, typename _Traits = allocator_traits<_Alloc> >
-struct __noexcept_move_assign_container
-    : public integral_constant<bool,
-                               _Traits::propagate_on_container_move_assignment::value
-#if _LIBCPP_STD_VER >= 17
-                                   || _Traits::is_always_equal::value
-#else
-                                   && is_nothrow_move_assignable<_Alloc>::value
-#endif
-                               > {
-};
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP___MEMORY_NOEXCEPT_MOVE_ASSIGN_CONTAINER_H
diff --git a/libcxx/include/__split_buffer b/libcxx/include/__split_buffer
index 1d50486947f08..31915540f06b9 100644
--- a/libcxx/include/__split_buffer
+++ b/libcxx/include/__split_buffer
@@ -29,8 +29,6 @@
 #include <__type_traits/conditional.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/integral_constant.h>
-#include <__type_traits/is_nothrow_assignable.h>
-#include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/is_swappable.h>
 #include <__type_traits/is_trivially_destructible.h>
 #include <__type_traits/is_trivially_relocatable.h>
@@ -181,8 +179,7 @@ public:
   }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
-  __copy_without_alloc(__split_buffer_pointer_layout const& __other)
-      _NOEXCEPT_(is_nothrow_copy_assignable<pointer>::value) {
+  __copy_without_alloc(__split_buffer_pointer_layout const& __other) _NOEXCEPT {
     __front_cap_ = __other.__front_cap_;
     __begin_     = __other.__begin_;
     __end_       = __other.__end_;
@@ -388,8 +385,7 @@ public:
   }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
-  __copy_without_alloc(__split_buffer_size_layout const& __other)
-      _NOEXCEPT_(is_nothrow_copy_assignable<pointer>::value) {
+  __copy_without_alloc(__split_buffer_size_layout const& __other) _NOEXCEPT {
     __front_cap_ = __other.__front_cap_;
     __begin_     = __other.__begin_;
     __cap_       = __other.__cap_;
@@ -607,15 +603,11 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI
   __split_buffer(size_type __cap, size_type __start, allocator_type& __a);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __split_buffer(__split_buffer&& __c)
-      _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __split_buffer(__split_buffer&& __c) _NOEXCEPT;
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __split_buffer(__split_buffer&& __c, const allocator_type& __a);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __split_buffer& operator=(__split_buffer&& __c)
-      _NOEXCEPT_((__alloc_traits::propagate_on_container_move_assignment::value &&
-                  is_nothrow_move_assignable<allocator_type>::value) ||
-                 !__alloc_traits::propagate_on_container_move_assignment::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __split_buffer& operator=(__split_buffer&& __c) _NOEXCEPT;
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI ~__split_buffer();
 
@@ -669,8 +661,7 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __destruct_at_end(pointer __new_last, false_type) _NOEXCEPT;
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __destruct_at_end(pointer __new_last, true_type) _NOEXCEPT;
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void swap(__split_buffer& __x)
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void swap(__split_buffer& __x) _NOEXCEPT;
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __invariants() const {
     if (__front_cap() == nullptr) {
@@ -704,8 +695,8 @@ public:
   }
 
 private:
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__split_buffer& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
+  __move_assign_alloc(__split_buffer& __c, true_type) _NOEXCEPT {
     __get_allocator() = std::move(__c.__get_allocator());
   }
 
@@ -850,8 +841,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 __split_buffer<_Tp, _Allocator, _Layout>::~__split
 }
 
 template <class _Tp, class _Allocator, template <class, class, class> class _Layout>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 __split_buffer<_Tp, _Allocator, _Layout>::__split_buffer(__split_buffer&& __c)
-    _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
+_LIBCPP_CONSTEXPR_SINCE_CXX20 __split_buffer<_Tp, _Allocator, _Layout>::__split_buffer(__split_buffer&& __c) _NOEXCEPT
     : __base_type(std::move(__c)) {
   __c.__reset();
 }
@@ -877,10 +867,7 @@ __split_buffer<_Tp, _Allocator, _Layout>::__split_buffer(__split_buffer&& __c, c
 
 template <class _Tp, class _Allocator, template <class, class, class> class _Layout>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 __split_buffer<_Tp, _Allocator, _Layout>&
-__split_buffer<_Tp, _Allocator, _Layout>::operator=(__split_buffer&& __c)
-    _NOEXCEPT_((__alloc_traits::propagate_on_container_move_assignment::value &&
-                is_nothrow_move_assignable<allocator_type>::value) ||
-               !__alloc_traits::propagate_on_container_move_assignment::value) {
+__split_buffer<_Tp, _Allocator, _Layout>::operator=(__split_buffer&& __c) _NOEXCEPT {
   clear();
   shrink_to_fit();
   __copy_without_alloc(__c);
@@ -890,8 +877,7 @@ __split_buffer<_Tp, _Allocator, _Layout>::operator=(__split_buffer&& __c)
 }
 
 template <class _Tp, class _Allocator, template <class, class, class> class _Layout>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator, _Layout>::swap(__split_buffer& __x)
-    _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>) {
+_LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator, _Layout>::swap(__split_buffer& __x) _NOEXCEPT {
   __base_type::swap(__x);
 }
 
@@ -962,8 +948,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator, _Layout>::emp
 
 template <class _Tp, class _Allocator, template <class, class, class> class _Layout>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI void
-swap(__split_buffer<_Tp, _Allocator, _Layout>& __x, __split_buffer<_Tp, _Allocator, _Layout>& __y)
-    _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
+swap(__split_buffer<_Tp, _Allocator, _Layout>& __x, __split_buffer<_Tp, _Allocator, _Layout>& __y) _NOEXCEPT {
   __x.swap(__y);
 }
 
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index dafeff3e2e019..4965e35571b47 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1021,15 +1021,13 @@ public:
   template <class _ForwardIterator>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   __assign_unique(_ForwardIterator __first, _ForwardIterator __last);
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t) _NOEXCEPT_(
-      is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t)
+      _NOEXCEPT_(is_nothrow_move_constructible<value_compare>::value);
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t, const allocator_type& __a);
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree& operator=(__tree&& __t)
-      _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value &&
-                 ((__node_traits::propagate_on_container_move_assignment::value &&
-                   is_nothrow_move_assignable<__node_allocator>::value) ||
-                  allocator_traits<__node_allocator>::is_always_equal::value)) {
+      _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value&&
+                     __is_allocator_aware_container_move_nothrow_v<__node_allocator>) {
     __move_assign(__t,
                   integral_constant<bool,
                                     __node_traits::propagate_on_container_move_assignment::value ||
@@ -1058,12 +1056,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT;
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(__tree& __t)
-#if _LIBCPP_STD_VER <= 11
-      _NOEXCEPT_(__is_nothrow_swappable_v<value_compare> &&
-                 (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>));
-#else
       _NOEXCEPT_(__is_nothrow_swappable_v<value_compare>);
-#endif
 
   template <class... _Args>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __emplace_multi(_Args&&... __args);
@@ -1472,17 +1465,14 @@ private:
   }
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, false_type);
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, true_type) _NOEXCEPT_(
-      is_nothrow_move_assignable<value_compare>::value&& is_nothrow_move_assignable<__node_allocator>::value);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, true_type)
+      _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value);
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t)
-      _NOEXCEPT_(!__node_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t) _NOEXCEPT {
     __move_assign_alloc(__t, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t, true_type) _NOEXCEPT {
     __node_alloc() = std::move(__t.__node_alloc());
   }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree&, false_type) _NOEXCEPT {}
@@ -1727,8 +1717,8 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(const __
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
-    is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t)
+    _NOEXCEPT_(is_nothrow_move_constructible<value_compare>::value)
     : __begin_node_(std::move(__t.__begin_node_)),
       __end_node_(std::move(__t.__end_node_)),
       __node_alloc_(std::move(__t.__node_alloc_)),
@@ -1771,7 +1761,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&&
 
 template <class _Tp, class _Compare, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
-    _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value&& is_nothrow_move_assignable<__node_allocator>::value) {
+    _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value) {
   destroy(std::__static_fancy_pointer_cast<__node_pointer>(__end_node()->__left_));
   __begin_node_ = __t.__begin_node_;
   __end_node_   = __t.__end_node_;
@@ -1811,13 +1801,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__move_ass
 
 template <class _Tp, class _Compare, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t)
-#if _LIBCPP_STD_VER <= 11
-    _NOEXCEPT_(__is_nothrow_swappable_v<value_compare> &&
-               (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>))
-#else
-    _NOEXCEPT_(__is_nothrow_swappable_v<value_compare>)
-#endif
-{
+    _NOEXCEPT_(__is_nothrow_swappable_v<value_compare>) {
   using std::swap;
   swap(__begin_node_, __t.__begin_node_);
   swap(__end_node_, __t.__end_node_);
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..c66b2fce60d06 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -36,7 +36,6 @@
 #include <__memory/allocator.h>
 #include <__memory/allocator_traits.h>
 #include <__memory/compressed_pair.h>
-#include <__memory/noexcept_move_assign_container.h>
 #include <__memory/pointer_traits.h>
 #include <__memory/swap_allocator.h>
 #include <__memory/temp_value.h>
@@ -52,7 +51,6 @@
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_constructible.h>
-#include <__type_traits/is_nothrow_assignable.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
@@ -135,14 +133,8 @@ class vector {
   //
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector()
       _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value) {}
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI explicit vector(const allocator_type& __a)
-#if _LIBCPP_STD_VER <= 14
-      _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value)
-#else
-      noexcept
-#endif
-      : __layout_(__a) {
-  }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20
+  _LIBCPP_HIDE_FROM_ABI explicit vector(const allocator_type& __a) _NOEXCEPT : __layout_(__a) {}
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI explicit vector(size_type __n) {
     auto __guard = std::__make_exception_guard(__destroy_vector(*this));
@@ -290,17 +282,11 @@ class vector {
   }
 #endif // !_LIBCPP_CXX03_LANG
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector(vector&& __x)
-#if _LIBCPP_STD_VER >= 17
-      noexcept;
-#else
-      _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value);
-#endif
-
-  _LIBCPP_CONSTEXPR_SINCE_CXX20
-  _LIBCPP_HIDE_FROM_ABI vector(vector&& __x, const __type_identity_t<allocator_type>& __a);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector(vector&& __x) _NOEXCEPT;
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI
+  vector(vector&& __x, const __type_identity_t<allocator_type>& __a);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector& operator=(vector&& __x)
-      _NOEXCEPT_(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value) {
+      _NOEXCEPT_(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
     __move_assign(__x, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
     return *this;
   }
@@ -566,12 +552,7 @@ class vector {
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void resize(size_type __sz);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void resize(size_type __sz, const_reference __x);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void swap(vector&)
-#if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT;
-#else
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>);
-#endif
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void swap(vector&) _NOEXCEPT;
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __invariants() const _NOEXCEPT {
     return __layout_.__invariants();
@@ -709,8 +690,7 @@ class vector {
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
   __move_range(pointer __from_s, pointer __from_e, pointer __to);
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign(vector& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign(vector& __c, true_type) _NOEXCEPT;
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign(vector& __c, false_type)
       _NOEXCEPT_(__alloc_traits::is_always_equal::value);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __destruct_at_end(pointer __new_last) _NOEXCEPT {
@@ -783,9 +763,7 @@ class vector {
     __copy_assign_alloc(__c, integral_constant<bool, __alloc_traits::propagate_on_container_copy_assignment::value>());
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(vector& __c)
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(vector& __c) _NOEXCEPT {
     __move_assign_alloc(__c, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
   }
 
@@ -805,8 +783,7 @@ class vector {
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const vector&, false_type) {}
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(vector& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(vector& __c, true_type) _NOEXCEPT {
     this->__layout_.__alloc() = std::move(__c.__layout_.__alloc());
   }
 
@@ -912,14 +889,8 @@ vector<_Tp, _Allocator>::__construct_at_end(_InputIterator __first, _Sentinel __
 }
 
 template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI vector<_Tp, _Allocator>::vector(vector&& __x)
-#if _LIBCPP_STD_VER >= 17
-    noexcept
-#else
-    _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
-#endif
-    : __layout_(std::move(__x.__layout_)) {
-}
+_LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI vector<_Tp, _Allocator>::vector(vector&& __x) _NOEXCEPT
+    : __layout_(std::move(__x.__layout_)) {}
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI
@@ -944,8 +915,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__move_assign(vector
 }
 
 template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__move_assign(vector& __c, true_type)
-    _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__move_assign(vector& __c, true_type) _NOEXCEPT {
   __vdeallocate();
   __move_assign_alloc(__c); // this can throw
   __layout_.__move_assign_without_allocator(__c.__layout_);
@@ -1338,13 +1308,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::resize(size_type __n
 }
 
 template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::swap(vector& __x)
-#if _LIBCPP_STD_VER >= 14
-    _NOEXCEPT
-#else
-    _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>)
-#endif
-{
+_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::swap(vector& __x) _NOEXCEPT {
   _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
       __alloc_traits::propagate_on_container_swap::value || __layout_.__alloc() == __x.__layout_.__alloc(),
       "vector::swap: Either propagate_on_container_swap must be true"
diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index f73e086478a70..84824239f8c54 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -127,12 +127,7 @@ class vector<bool, _Allocator> {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector()
       _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value);
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit vector(const allocator_type& __a)
-#if _LIBCPP_STD_VER <= 14
-      _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value);
-#else
-      _NOEXCEPT;
-#endif
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit vector(const allocator_type& __a) _NOEXCEPT;
 
 private:
   class __destroy_vector {
@@ -199,16 +194,11 @@ class vector<bool, _Allocator> {
 
 #endif // !_LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector(vector&& __v)
-#if _LIBCPP_STD_VER >= 17
-      noexcept;
-#else
-      _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value);
-#endif
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector(vector&& __v) _NOEXCEPT;
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
   vector(vector&& __v, const __type_identity_t<allocator_type>& __a);
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector& operator=(vector&& __v)
-      _NOEXCEPT_(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value);
+      _NOEXCEPT_(__is_allocator_aware_container_move_nothrow_v<allocator_type>);
 
   template <class _InputIterator, __enable_if_t<__has_exactly_input_iterator_category<_InputIterator>::value, int> = 0>
   void _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 assign(_InputIterator __first, _InputIterator __last);
@@ -393,12 +383,7 @@ class vector<bool, _Allocator> {
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void clear() _NOEXCEPT { __size_ = 0; }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(vector&)
-#if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT;
-#else
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>);
-#endif
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(vector&) _NOEXCEPT;
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void swap(reference __x, reference __y) _NOEXCEPT {
     std::swap(__x, __y);
   }
@@ -513,16 +498,12 @@ class vector<bool, _Allocator> {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __copy_assign_alloc(const vector&, false_type) {}
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign(vector& __c, false_type);
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign(vector& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value);
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(vector& __c)
-      _NOEXCEPT_(!__storage_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign(vector& __c, true_type) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(vector& __c) _NOEXCEPT {
     __move_assign_alloc(
         __c, integral_constant<bool, __storage_traits::propagate_on_container_move_assignment::value>());
   }
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(vector& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(vector& __c, true_type) _NOEXCEPT {
     __alloc_ = std::move(__c.__alloc_);
   }
 
@@ -586,14 +567,12 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocat
     : __begin_(nullptr), __size_(0), __cap_(0) {}
 
 template <class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(const allocator_type& __a)
-#if _LIBCPP_STD_VER <= 14
-    _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value)
-#else
-        _NOEXCEPT
-#endif
-    : __begin_(nullptr), __size_(0), __cap_(0), __alloc_(static_cast<__storage_allocator>(__a)) {
-}
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+vector<bool, _Allocator>::vector(const allocator_type& __a) _NOEXCEPT
+    : __begin_(nullptr),
+      __size_(0),
+      __cap_(0),
+      __alloc_(static_cast<__storage_allocator>(__a)) {}
 
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(size_type __n)
@@ -735,17 +714,11 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>& vector<bool, _Allocator>
 }
 
 template <class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(vector&& __v)
-#if _LIBCPP_STD_VER >= 17
-    _NOEXCEPT
-#else
-    _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
-#endif
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(vector&& __v) _NOEXCEPT
     : __begin_(std::__exchange(__v.__begin_, nullptr)),
       __size_(std::__exchange(__v.__size_, 0)),
       __cap_(std::__exchange(__v.__cap_, 0)),
-      __alloc_(std::move(__v.__alloc_)) {
-}
+      __alloc_(std::move(__v.__alloc_)) {}
 
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20
@@ -765,7 +738,7 @@ vector<bool, _Allocator>::vector(vector&& __v, const __type_identity_t<allocator
 template <class _Allocator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>&
 vector<bool, _Allocator>::operator=(vector&& __v)
-    _NOEXCEPT_(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value) {
+    _NOEXCEPT_(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
   __move_assign(__v, integral_constant<bool, __storage_traits::propagate_on_container_move_assignment::value>());
   return *this;
 }
@@ -779,8 +752,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::__move_assign(vecto
 }
 
 template <class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::__move_assign(vector& __c, true_type)
-    _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::__move_assign(vector& __c, true_type) _NOEXCEPT {
   __vdeallocate();
   __move_assign_alloc(__c);
   __begin_ = std::__exchange(__c.__begin_, nullptr);
@@ -1030,13 +1002,7 @@ vector<bool, _Allocator>::erase(const_iterator __first, const_iterator __last) {
 }
 
 template <class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::swap(vector& __x)
-#if _LIBCPP_STD_VER >= 14
-    _NOEXCEPT
-#else
-    _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>)
-#endif
-{
+_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::swap(vector& __x) _NOEXCEPT {
   std::swap(this->__begin_, __x.__begin_);
   std::swap(this->__size_, __x.__size_);
   std::swap(this->__cap_, __x.__cap_);
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 7aee552c63d37..dd06349d16a96 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -226,7 +226,6 @@ template <class T, class Allocator, class Predicate>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/is_allocator.h>
 #  include <__type_traits/is_convertible.h>
-#  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
 #  include <__type_traits/is_same.h>
 #  include <__type_traits/is_swappable.h>
@@ -553,9 +552,6 @@ public:
   static_assert(is_nothrow_default_constructible<allocator_type>::value ==
                     is_nothrow_default_constructible<__pointer_allocator>::value,
                 "rebinding an allocator should not change exception guarantees");
-  static_assert(is_nothrow_move_constructible<allocator_type>::value ==
-                    is_nothrow_move_constructible<typename __map::allocator_type>::value,
-                "rebinding an allocator should not change exception guarantees");
 
 private:
   struct __deque_block_range {
@@ -691,12 +687,10 @@ public:
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI deque(deque&& __c) noexcept(is_nothrow_move_constructible<allocator_type>::value);
+  _LIBCPP_HIDE_FROM_ABI deque(deque&& __c) noexcept;
   _LIBCPP_HIDE_FROM_ABI deque(deque&& __c, const __type_identity_t<allocator_type>& __a);
-  _LIBCPP_HIDE_FROM_ABI deque& operator=(deque&& __c) noexcept(
-      (__alloc_traits::propagate_on_container_move_assignment::value &&
-       is_nothrow_move_assignable<allocator_type>::value) ||
-      __alloc_traits::is_always_equal::value);
+  _LIBCPP_HIDE_FROM_ABI deque&
+  operator=(deque&& __c) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>);
 
   _LIBCPP_HIDE_FROM_ABI void assign(initializer_list<value_type> __il) { assign(__il.begin(), __il.end()); }
 #  endif // _LIBCPP_CXX03_LANG
@@ -888,12 +882,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p);
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l);
 
-  _LIBCPP_HIDE_FROM_ABI void swap(deque& __c)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT;
-#  else
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>);
-#  endif
+  _LIBCPP_HIDE_FROM_ABI void swap(deque& __c) _NOEXCEPT;
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
 
   _LIBCPP_HIDE_FROM_ABI bool __invariants() const {
@@ -918,22 +907,18 @@ public:
     return true;
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(deque& __c)
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(deque& __c) _NOEXCEPT {
     __move_assign_alloc(__c, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(deque& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(deque& __c, true_type) _NOEXCEPT {
     __alloc() = std::move(__c.__alloc());
   }
 
   _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(deque&, false_type) _NOEXCEPT {}
 
   _LIBCPP_HIDE_FROM_ABI void __move_assign(deque& __c)
-      _NOEXCEPT_(__alloc_traits::propagate_on_container_move_assignment::value&&
-                     is_nothrow_move_assignable<allocator_type>::value) {
+      _NOEXCEPT_(__alloc_traits::propagate_on_container_move_assignment::value) {
     __map_   = std::move(__c.__map_);
     __start_ = __c.__start_;
     __size() = __c.size();
@@ -1286,8 +1271,7 @@ private:
 
   _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const deque&, false_type) {}
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign(deque& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value);
+  _LIBCPP_HIDE_FROM_ABI void __move_assign(deque& __c, true_type) _NOEXCEPT;
   _LIBCPP_HIDE_FROM_ABI void __move_assign(deque& __c, false_type);
 };
 
@@ -1397,7 +1381,7 @@ deque<_Tp, _Allocator>::deque(initializer_list<value_type> __il, const allocator
 }
 
 template <class _Tp, class _Allocator>
-inline deque<_Tp, _Allocator>::deque(deque&& __c) noexcept(is_nothrow_move_constructible<allocator_type>::value)
+inline deque<_Tp, _Allocator>::deque(deque&& __c) noexcept
     : __map_(std::move(__c.__map_)),
       __start_(std::move(__c.__start_)),
       __size_(std::move(__c.__size_)),
@@ -1425,10 +1409,8 @@ inline deque<_Tp, _Allocator>::deque(deque&& __c, const __type_identity_t<alloca
 }
 
 template <class _Tp, class _Allocator>
-inline deque<_Tp, _Allocator>& deque<_Tp, _Allocator>::operator=(deque&& __c) noexcept(
-    (__alloc_traits::propagate_on_container_move_assignment::value &&
-     is_nothrow_move_assignable<allocator_type>::value) ||
-    __alloc_traits::is_always_equal::value) {
+inline deque<_Tp, _Allocator>&
+deque<_Tp, _Allocator>::operator=(deque&& __c) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
   __move_assign(__c, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
   return *this;
 }
@@ -1443,8 +1425,7 @@ void deque<_Tp, _Allocator>::__move_assign(deque& __c, false_type) {
 }
 
 template <class _Tp, class _Allocator>
-void deque<_Tp, _Allocator>::__move_assign(deque& __c,
-                                           true_type) noexcept(is_nothrow_move_assignable<allocator_type>::value) {
+void deque<_Tp, _Allocator>::__move_assign(deque& __c, true_type) noexcept {
   clear();
   shrink_to_fit();
   __move_assign(__c);
@@ -2414,13 +2395,7 @@ void deque<_Tp, _Allocator>::__erase_to_end(const_iterator __f) {
 }
 
 template <class _Tp, class _Allocator>
-inline void deque<_Tp, _Allocator>::swap(deque& __c)
-#  if _LIBCPP_STD_VER >= 14
-    _NOEXCEPT
-#  else
-    _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>)
-#  endif
-{
+inline void deque<_Tp, _Allocator>::swap(deque& __c) _NOEXCEPT {
   __map_.swap(__c.__map_);
   std::swap(__start_, __c.__start_);
   std::swap(__size(), __c.__size());
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index 6d86f7a8e5975..767a959bf8866 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -227,7 +227,6 @@ template <class T, class Allocator, class Predicate>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/is_allocator.h>
 #  include <__type_traits/is_const.h>
-#  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
 #  include <__type_traits/is_same.h>
 #  include <__type_traits/is_swappable.h>
@@ -499,8 +498,7 @@ protected:
 
 public:
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
-  __forward_list_base(__forward_list_base&& __x) noexcept(is_nothrow_move_constructible<__node_allocator>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __forward_list_base(__forward_list_base&& __x) noexcept;
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
   __forward_list_base(__forward_list_base&& __x, const allocator_type& __a);
 #  endif // _LIBCPP_CXX03_LANG
@@ -515,9 +513,7 @@ protected:
     __copy_assign_alloc(__x, integral_constant<bool, __node_traits::propagate_on_container_copy_assignment::value>());
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__forward_list_base& __x)
-      _NOEXCEPT_(!__node_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__forward_list_base& __x) _NOEXCEPT {
     __move_assign_alloc(__x, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   }
 
@@ -547,12 +543,7 @@ protected:
   }
 
 public:
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(__forward_list_base& __x)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT;
-#  else
-      _NOEXCEPT_(!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>);
-#  endif
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(__forward_list_base& __x) _NOEXCEPT;
 
 protected:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
@@ -569,8 +560,8 @@ private:
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void
   __move_assign_alloc(__forward_list_base&, false_type) _NOEXCEPT {}
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__forward_list_base& __x, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void
+  __move_assign_alloc(__forward_list_base& __x, true_type) _NOEXCEPT {
     __alloc_ = std::move(__x.__alloc_);
   }
 };
@@ -579,7 +570,7 @@ private:
 
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 inline __forward_list_base<_Tp, _Alloc>::__forward_list_base(
-    __forward_list_base&& __x) noexcept(is_nothrow_move_constructible<__node_allocator>::value)
+    __forward_list_base&& __x) noexcept
     : __before_begin_(std::move(__x.__before_begin_)), __alloc_(std::move(__x.__alloc_)) {
   __x.__before_begin()->__next_ = nullptr;
 }
@@ -602,13 +593,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 __forward_list_base<_Tp, _Alloc>::~__forward_list_
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 inline void __forward_list_base<_Tp, _Alloc>::swap(__forward_list_base& __x)
-#  if _LIBCPP_STD_VER >= 14
-    _NOEXCEPT
-#  else
-    _NOEXCEPT_(!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>)
-#  endif
-{
+_LIBCPP_CONSTEXPR_SINCE_CXX26 inline void __forward_list_base<_Tp, _Alloc>::swap(__forward_list_base& __x) _NOEXCEPT {
   std::__swap_allocator(__alloc_, __x.__alloc_);
   using std::swap;
   swap(__before_begin()->__next_, __x.__before_begin()->__next_);
@@ -699,8 +684,7 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI forward_list& operator=(const forward_list& __x);
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
-  forward_list(forward_list&& __x) noexcept(is_nothrow_move_constructible<__base>::value)
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI forward_list(forward_list&& __x) noexcept
       : __base(std::move(__x)) {}
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
   forward_list(forward_list&& __x, const __type_identity_t<allocator_type>& __a);
@@ -709,10 +693,8 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
   forward_list(initializer_list<value_type> __il, const allocator_type& __a);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI forward_list& operator=(forward_list&& __x) noexcept(
-      (__node_traits::propagate_on_container_move_assignment::value &&
-       is_nothrow_move_assignable<allocator_type>::value) ||
-      allocator_traits<allocator_type>::is_always_equal::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI forward_list&
+  operator=(forward_list&& __x) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>);
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI forward_list& operator=(initializer_list<value_type> __il);
 
@@ -838,15 +820,7 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI iterator erase_after(const_iterator __p);
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI iterator erase_after(const_iterator __f, const_iterator __l);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(forward_list& __x)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT
-#  else
-      _NOEXCEPT_(!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>)
-#  endif
-  {
-    __base::swap(__x);
-  }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(forward_list& __x) _NOEXCEPT { __base::swap(__x); }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void resize(size_type __n);
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void resize(size_type __n, const value_type& __v);
@@ -885,8 +859,7 @@ public:
 
 private:
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign(forward_list& __x, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign(forward_list& __x, true_type) _NOEXCEPT;
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign(forward_list& __x, false_type);
 #  endif // _LIBCPP_CXX03_LANG
 
@@ -1019,8 +992,7 @@ forward_list<_Tp, _Alloc>::forward_list(initializer_list<value_type> __il, const
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 void forward_list<_Tp, _Alloc>::__move_assign(forward_list& __x, true_type)
-    _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void forward_list<_Tp, _Alloc>::__move_assign(forward_list& __x, true_type) _NOEXCEPT {
   clear();
   __base::__move_assign_alloc(__x);
   __base::__before_begin()->__next_ = std::__exchange(__x.__before_begin()->__next_, nullptr);
@@ -1037,11 +1009,8 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void forward_list<_Tp, _Alloc>::__move_assign(forw
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 inline forward_list<_Tp, _Alloc>&
-forward_list<_Tp, _Alloc>::operator=(forward_list&& __x) noexcept(
-    (__node_traits::propagate_on_container_move_assignment::value &&
-     is_nothrow_move_assignable<allocator_type>::value) ||
-    allocator_traits<allocator_type>::is_always_equal::value) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 inline forward_list<_Tp, _Alloc>& forward_list<_Tp, _Alloc>::operator=(
+    forward_list&& __x) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
   __move_assign(__x, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   return *this;
 }
@@ -1558,8 +1527,8 @@ operator<=>(const forward_list<_Tp, _Allocator>& __x, const forward_list<_Tp, _A
 #  endif // #if _LIBCPP_STD_VER <= 17
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 inline _LIBCPP_HIDE_FROM_ABI void
-swap(forward_list<_Tp, _Alloc>& __x, forward_list<_Tp, _Alloc>& __y) _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 inline
+    _LIBCPP_HIDE_FROM_ABI void swap(forward_list<_Tp, _Alloc>& __x, forward_list<_Tp, _Alloc>& __y) _NOEXCEPT {
   __x.swap(__y);
 }
 
diff --git a/libcxx/include/list b/libcxx/include/list
index 3223f25e1cfba..7b631554d0ed7 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -231,7 +231,6 @@ template <class T, class Allocator, class Predicate>
 #  include <__type_traits/container_traits.h>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/is_allocator.h>
-#  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
 #  include <__type_traits/is_same.h>
 #  include <__type_traits/type_identity.h>
@@ -535,21 +534,14 @@ protected:
     return const_iterator(__end_as_link());
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(__list_imp& __c)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT;
-#  else
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>);
-#  endif
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(__list_imp& __c) _NOEXCEPT;
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __list_imp& __c) {
     __copy_assign_alloc(
         __c, integral_constant<bool, __node_alloc_traits::propagate_on_container_copy_assignment::value>());
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__list_imp& __c)
-      _NOEXCEPT_(!__node_alloc_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__list_imp& __c) _NOEXCEPT {
     __move_assign_alloc(
         __c, integral_constant<bool, __node_alloc_traits::propagate_on_container_move_assignment::value>());
   }
@@ -589,8 +581,7 @@ private:
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __list_imp&, false_type) {}
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__list_imp& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__list_imp& __c, true_type) _NOEXCEPT {
     __node_alloc_ = std::move(__c.__node_alloc_);
   }
 
@@ -646,13 +637,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void __list_imp<_Tp, _Alloc>::clear() _NOEXCEPT {
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 void __list_imp<_Tp, _Alloc>::swap(__list_imp& __c)
-#  if _LIBCPP_STD_VER >= 14
-    _NOEXCEPT
-#  else
-    _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>)
-#  endif
-{
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __list_imp<_Tp, _Alloc>::swap(__list_imp& __c) _NOEXCEPT {
   _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
       __alloc_traits::propagate_on_container_swap::value || this->__node_alloc_ == __c.__node_alloc_,
       "list::swap: Either propagate_on_container_swap must be true"
@@ -744,13 +729,10 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
   list(initializer_list<value_type> __il, const allocator_type& __a);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list(list&& __c)
-      _NOEXCEPT_(is_nothrow_move_constructible<__node_allocator>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list(list&& __c) noexcept;
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list(list&& __c, const __type_identity_t<allocator_type>& __a);
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list& operator=(list&& __c) noexcept(
-      (__node_alloc_traits::propagate_on_container_move_assignment::value &&
-       is_nothrow_move_assignable<__node_allocator>::value) ||
-      allocator_traits<allocator_type>::is_always_equal::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list&
+  operator=(list&& __c) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>);
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list& operator=(initializer_list<value_type> __il) {
     assign(__il.begin(), __il.end());
@@ -915,15 +897,7 @@ public:
   }
 #  endif
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(list& __c)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT
-#  else
-      _NOEXCEPT_(!__node_alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>)
-#  endif
-  {
-    __base::swap(__c);
-  }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(list& __c) _NOEXCEPT { __base::swap(__c); }
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __base::clear(); }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void pop_front();
@@ -995,8 +969,7 @@ private:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDDEN static iterator
   __sort(iterator __f1, iterator __e2, size_type __n, _Comp& __comp);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign(list& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign(list& __c, true_type);
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign(list& __c, false_type);
 };
 
@@ -1125,8 +1098,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 list<_Tp, _Alloc>::list(initializer_list<value_typ
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 inline list<_Tp, _Alloc>::list(list&& __c) noexcept(
-    is_nothrow_move_constructible<__node_allocator>::value)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 inline list<_Tp, _Alloc>::list(list&& __c) noexcept
     : __base(std::move(__c.__node_alloc_)) {
   splice(end(), __c);
 }
@@ -1143,10 +1115,8 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 inline list<_Tp, _Alloc>::list(list&& __c, const _
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 inline list<_Tp, _Alloc>& list<_Tp, _Alloc>::operator=(list&& __c) noexcept(
-    (__node_alloc_traits::propagate_on_container_move_assignment::value &&
-     is_nothrow_move_assignable<__node_allocator>::value) ||
-    allocator_traits<allocator_type>::is_always_equal::value) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 inline list<_Tp, _Alloc>&
+list<_Tp, _Alloc>::operator=(list&& __c) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
   __move_assign(__c, integral_constant<bool, __node_alloc_traits::propagate_on_container_move_assignment::value>());
   return *this;
 }
@@ -1161,8 +1131,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void list<_Tp, _Alloc>::__move_assign(list& __c, f
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 void
-list<_Tp, _Alloc>::__move_assign(list& __c, true_type) noexcept(is_nothrow_move_assignable<__node_allocator>::value) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void list<_Tp, _Alloc>::__move_assign(list& __c, true_type) {
   clear();
   __base::__move_assign_alloc(__c);
   splice(end(), __c);
@@ -1762,8 +1731,8 @@ operator<=>(const list<_Tp, _Allocator>& __x, const list<_Tp, _Allocator>& __y)
 #  endif // _LIBCPP_STD_VER <= 17
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 inline _LIBCPP_HIDE_FROM_ABI void swap(list<_Tp, _Alloc>& __x, list<_Tp, _Alloc>& __y)
-    _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 inline
+    _LIBCPP_HIDE_FROM_ABI void swap(list<_Tp, _Alloc>& __x, list<_Tp, _Alloc>& __y) _NOEXCEPT {
   __x.swap(__y);
 }
 
diff --git a/libcxx/include/queue b/libcxx/include/queue
index b469240cc94f9..b2c4c01a6eb62 100644
--- a/libcxx/include/queue
+++ b/libcxx/include/queue
@@ -272,6 +272,7 @@ template <class T, class Container, class Compare>
 #  include <__ranges/concepts.h>
 #  include <__ranges/container_compatible_range.h>
 #  include <__ranges/from_range.h>
+#  include <__type_traits/is_nothrow_assignable.h>
 #  include <__utility/forward.h>
 #  include <deque>
 #  include <vector>
diff --git a/libcxx/include/stack b/libcxx/include/stack
index 313c305dd8088..84da311dbf89a 100644
--- a/libcxx/include/stack
+++ b/libcxx/include/stack
@@ -126,6 +126,7 @@ template <class T, class Container>
 #  include <__ranges/concepts.h>
 #  include <__ranges/container_compatible_range.h>
 #  include <__ranges/from_range.h>
+#  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_same.h>
 #  include <__utility/forward.h>
 #  include <deque>
diff --git a/libcxx/include/string b/libcxx/include/string
index a297a05813cee..94457f45f4add 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -946,13 +946,9 @@ public:
     __annotate_new(0);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit basic_string(const allocator_type& __a)
-#  if _LIBCPP_STD_VER <= 14
-      _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value)
-#  else
-      _NOEXCEPT
-#  endif
-      : __rep_(), __alloc_(__a) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit basic_string(const allocator_type& __a) _NOEXCEPT
+      : __rep_(),
+        __alloc_(__a) {
     __annotate_new(0);
   }
 
@@ -974,13 +970,9 @@ public:
   }
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(basic_string&& __str)
-#    if _LIBCPP_STD_VER <= 14
-      _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
-#    else
-      _NOEXCEPT
-#    endif
-      : __rep_(std::move(__str.__rep_)), __alloc_(std::move(__str.__alloc_)) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(basic_string&& __str) _NOEXCEPT
+      : __rep_(std::move(__str.__rep_)),
+        __alloc_(std::move(__str.__alloc_)) {
     __str.__rep_ = __rep();
     __str.__annotate_new(0);
     if (!__is_long())
@@ -1144,7 +1136,7 @@ public:
 
 #  ifndef _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
-  operator=(basic_string&& __str) noexcept(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value) {
+  operator=(basic_string&& __str) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
     __move_assign(__str, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
     return *this;
   }
@@ -1449,7 +1441,7 @@ public:
   }
 #  ifndef _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
-  assign(basic_string&& __str) noexcept(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value) {
+  assign(basic_string&& __str) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
     *this = std::move(__str);
     return *this;
   }
@@ -1704,12 +1696,7 @@ public:
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(basic_string& __str)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT;
-#  else
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>);
-#  endif
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(basic_string& __str) _NOEXCEPT;
 
   // [string.ops]
   // ------------
@@ -2422,15 +2409,12 @@ private:
 #    endif
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(basic_string& __str)
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(basic_string& __str) _NOEXCEPT {
     __move_assign_alloc(
         __str, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(basic_string& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(basic_string& __c, true_type) _NOEXCEPT {
     __alloc_ = std::move(__c.__alloc_);
   }
 
@@ -3436,13 +3420,8 @@ basic_string<_CharT, _Traits, _Allocator>::copy(value_type* __s, size_type __n,
 }
 
 template <class _CharT, class _Traits, class _Allocator>
-inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::swap(basic_string& __str)
-#  if _LIBCPP_STD_VER >= 14
-    _NOEXCEPT
-#  else
-    _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>)
-#  endif
-{
+inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+basic_string<_CharT, _Traits, _Allocator>::swap(basic_string& __str) _NOEXCEPT {
   _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
       __alloc_traits::propagate_on_container_swap::value || __alloc_traits::is_always_equal::value ||
           __alloc_ == __str.__alloc_,
@@ -3786,8 +3765,7 @@ operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
 
 template <class _CharT, class _Traits, class _Allocator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-swap(basic_string<_CharT, _Traits, _Allocator>& __lhs, basic_string<_CharT, _Traits, _Allocator>& __rhs)
-    _NOEXCEPT_(_NOEXCEPT_(__lhs.swap(__rhs))) {
+swap(basic_string<_CharT, _Traits, _Allocator>& __lhs, basic_string<_CharT, _Traits, _Allocator>& __rhs) _NOEXCEPT {
   __lhs.swap(__rhs);
 }
 
diff --git a/libcxx/test/libcxx/containers/associative/map/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/associative/map/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..6da2fd33fc169
--- /dev/null
+++ b/libcxx/test/libcxx/containers/associative/map/noexcept.compile.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <unordered_map>
+
+#include <type_traits>
+#include <map>
+
+#include "test_allocator.h"
+
+template <class... Args>
+inline constexpr bool is_constructible_but_throws =
+    std::is_constructible<Args...>::value && !std::is_nothrow_constructible<Args...>::value;
+
+namespace std_allocator {
+using map        = std::map<int, int>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map, const std::less<int>&, const std::allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          const std::less<int>&,
+                                          const std::allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(
+    is_constructible_but_throws<map, std::from_range_t, map, const std::less<int>&, const std::allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, map&&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+
+using multimap = std::multimap<int, int>;
+
+static_assert(std::is_nothrow_constructible<multimap>::value);
+static_assert(is_constructible_but_throws<multimap, const std::less<int>&, const std::allocator<value_type>&>);
+static_assert(is_constructible_but_throws<multimap,
+                                          value_type*,
+                                          value_type*,
+                                          const std::less<int>&,
+                                          const std::allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<multimap,
+                                          std::from_range_t,
+                                          multimap,
+                                          const std::less<int>&,
+                                          const std::allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<multimap, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<multimap, const multimap&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<multimap, multimap&&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<multimap, multimap&&>::value);
+
+static_assert(std::is_nothrow_assignable<multimap&, multimap&&>::value);
+static_assert(std::is_nothrow_swappable<multimap>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using map        = std::map<int, int, std::less<int>, test_allocator<std::pair<const int, int>>>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map, const std::less<int>&, const test_allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          const std::less<int>&,
+                                          const test_allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(
+    is_constructible_but_throws<map, std::from_range_t, map, const std::less<int>&, const test_allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, map&&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(!std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+
+using multimap = std::multimap<int, int, std::less<int>, test_allocator<std::pair<const int, int>>>;
+
+static_assert(std::is_nothrow_constructible<multimap>::value);
+static_assert(is_constructible_but_throws<multimap, const std::less<int>&, const test_allocator<value_type>&>);
+static_assert(is_constructible_but_throws<multimap,
+                                          value_type*,
+                                          value_type*,
+                                          const std::less<int>&,
+                                          const test_allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<multimap,
+                                          std::from_range_t,
+                                          multimap,
+                                          const std::less<int>&,
+                                          const test_allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<multimap, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<multimap, const multimap&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<multimap, multimap&&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<multimap, multimap&&>::value);
+
+static_assert(!std::is_nothrow_assignable<multimap&, multimap&&>::value);
+static_assert(std::is_nothrow_swappable<multimap>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/std/containers/sequences/deque/deque.cons/move_noexcept.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/deque.cons/move_noexcept.compile.pass.cpp
similarity index 53%
rename from libcxx/test/std/containers/sequences/deque/deque.cons/move_noexcept.pass.cpp
rename to libcxx/test/libcxx/containers/sequences/deque/deque.cons/move_noexcept.compile.pass.cpp
index 37e8a801e9d71..d38d1d05db033 100644
--- a/libcxx/test/std/containers/sequences/deque/deque.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/deque.cons/move_noexcept.compile.pass.cpp
@@ -15,10 +15,9 @@
 
 // UNSUPPORTED: c++03
 
-#include <deque>
 #include <cassert>
+#include <deque>
 
-#include "test_macros.h"
 #include "MoveOnly.h"
 #include "test_allocator.h"
 
@@ -29,25 +28,7 @@ struct some_alloc {
   void allocate(std::size_t);
 };
 
-int main(int, char**) {
-#if defined(_LIBCPP_VERSION)
-  {
-    typedef std::deque<MoveOnly> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::deque<MoveOnly, test_allocator<MoveOnly>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::deque<MoveOnly, other_allocator<MoveOnly>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::deque<MoveOnly, some_alloc<MoveOnly>> C;
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
-  }
-#endif // _LIBCPP_VERSION
-
-  return 0;
-}
+static_assert(std::is_nothrow_move_constructible<std::deque<MoveOnly>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::deque<MoveOnly, test_allocator<MoveOnly>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::deque<MoveOnly, other_allocator<MoveOnly>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::deque<MoveOnly, some_alloc<MoveOnly>>>::value, "");
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/move_noexcept.pass.cpp b/libcxx/test/libcxx/containers/sequences/forwardlist/move_noexcept.compile.pass.cpp
similarity index 53%
rename from libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/move_noexcept.pass.cpp
rename to libcxx/test/libcxx/containers/sequences/forwardlist/move_noexcept.compile.pass.cpp
index 8dcf00b49c78b..5e692a183440f 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/forwardlist/move_noexcept.compile.pass.cpp
@@ -15,10 +15,9 @@
 
 // UNSUPPORTED: c++03
 
-#include <forward_list>
 #include <cassert>
+#include <forward_list>
 
-#include "test_macros.h"
 #include "MoveOnly.h"
 #include "test_allocator.h"
 
@@ -29,25 +28,7 @@ struct some_alloc {
   void allocate(std::size_t);
 };
 
-int main(int, char**) {
-#if defined(_LIBCPP_VERSION)
-  {
-    typedef std::forward_list<MoveOnly> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::forward_list<MoveOnly, test_allocator<MoveOnly>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::forward_list<MoveOnly, other_allocator<MoveOnly>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::forward_list<MoveOnly, some_alloc<MoveOnly>> C;
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
-  }
-#endif // _LIBCPP_VERSION
-
-  return 0;
-}
+static_assert(std::is_nothrow_move_constructible<std::forward_list<MoveOnly>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::forward_list<MoveOnly, test_allocator<MoveOnly>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::forward_list<MoveOnly, other_allocator<MoveOnly>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::forward_list<MoveOnly, some_alloc<MoveOnly>>>::value, "");
diff --git a/libcxx/test/std/containers/sequences/list/list.cons/move_noexcept.pass.cpp b/libcxx/test/libcxx/containers/sequences/list/list.cons/move_noexcept.compile.pass.cpp
similarity index 55%
rename from libcxx/test/std/containers/sequences/list/list.cons/move_noexcept.pass.cpp
rename to libcxx/test/libcxx/containers/sequences/list/list.cons/move_noexcept.compile.pass.cpp
index c33ef906be400..23b647693937a 100644
--- a/libcxx/test/std/containers/sequences/list/list.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/list/list.cons/move_noexcept.compile.pass.cpp
@@ -29,25 +29,7 @@ struct some_alloc {
   void allocate(std::size_t);
 };
 
-int main(int, char**) {
-#if defined(_LIBCPP_VERSION)
-  {
-    typedef std::list<MoveOnly> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::list<MoveOnly, test_allocator<MoveOnly>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::list<MoveOnly, other_allocator<MoveOnly>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-#endif // _LIBCPP_VERSION
-  {
-    typedef std::list<MoveOnly, some_alloc<MoveOnly>> C;
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
-  }
-
-  return 0;
-}
+static_assert(std::is_nothrow_move_constructible<std::list<MoveOnly>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::list<MoveOnly, test_allocator<MoveOnly>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::list<MoveOnly, other_allocator<MoveOnly>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::list<MoveOnly, some_alloc<MoveOnly>>>::value, "");
diff --git a/libcxx/test/libcxx/containers/sequences/vector.bool/move_noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector.bool/move_noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..642ce4d966eff
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/vector.bool/move_noexcept.compile.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <vector>
+
+// vector(vector&&)
+//        noexcept(is_nothrow_move_constructible<allocator_type>::value);
+
+// This tests a conforming extension
+
+// UNSUPPORTED: c++03
+
+#include <vector>
+#include <cassert>
+
+#include "test_allocator.h"
+
+template <class T>
+struct some_alloc {
+  typedef T value_type;
+  some_alloc(const some_alloc&);
+};
+
+static_assert(std::is_nothrow_move_constructible<std::vector<bool>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::vector<bool, test_allocator<bool>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::vector<bool, other_allocator<bool>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::vector<bool, some_alloc<bool>>>::value, "");
diff --git a/libcxx/test/libcxx/containers/sequences/vector.bool/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector.bool/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..a3881e9401f46
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/vector.bool/noexcept.compile.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <vector>
+
+#include <type_traits>
+#include <vector>
+
+#include "test_allocator.h"
+
+namespace std_allocator {
+using vec = std::vector<bool>;
+
+static_assert(std::is_nothrow_constructible<vec>::value);
+static_assert(std::is_nothrow_constructible<vec, const std::allocator<bool>&>::value);
+static_assert(std::is_nothrow_constructible<vec, vec&&>::value);
+
+static_assert(std::is_nothrow_assignable<vec&, vec&&>::value);
+static_assert(std::is_nothrow_swappable<vec>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using vec = std::vector<bool, test_allocator<bool>>;
+
+static_assert(std::is_nothrow_constructible<vec>::value);
+static_assert(std::is_nothrow_constructible<vec, const test_allocator<bool>&>::value);
+static_assert(std::is_nothrow_constructible<vec, vec&&>::value);
+
+static_assert(!std::is_nothrow_assignable<vec&, vec&&>::value);
+static_assert(std::is_nothrow_swappable<vec>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/libcxx/containers/sequences/vector/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..92734fd995232
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/vector/noexcept.compile.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <vector>
+
+#include <type_traits>
+#include <vector>
+
+#include "test_allocator.h"
+
+namespace std_allocator {
+using vec = std::vector<int>;
+
+static_assert(std::is_nothrow_constructible<vec>::value);
+static_assert(std::is_nothrow_constructible<vec, const std::allocator<int>&>::value);
+static_assert(std::is_nothrow_constructible<vec, vec&&>::value);
+
+static_assert(std::is_nothrow_assignable<vec&, vec&&>::value);
+static_assert(std::is_nothrow_swappable<vec>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using vec = std::vector<int, test_allocator<int>>;
+
+static_assert(std::is_nothrow_constructible<vec>::value);
+static_assert(std::is_nothrow_constructible<vec, const test_allocator<int>&>::value);
+static_assert(std::is_nothrow_constructible<vec, vec&&>::value);
+
+static_assert(!std::is_nothrow_assignable<vec&, vec&&>::value);
+static_assert(std::is_nothrow_swappable<vec>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.map/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..f283f2593f140
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.map/noexcept.compile.pass.cpp
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <unordered_map>
+
+#include <type_traits>
+#include <unordered_map>
+
+#include "test_allocator.h"
+
+template <class... Args>
+inline constexpr bool is_constructible_but_throws =
+    std::is_constructible<Args...>::value && !std::is_nothrow_constructible<Args...>::value;
+
+namespace std_allocator {
+using map        = std::unordered_map<int, int>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using map = std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, test_allocator<std::pair<const int, int>>>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(!std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..43f17772de236
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/noexcept.compile.pass.cpp
@@ -0,0 +1,155 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <unordered_map>
+
+#include <type_traits>
+#include <unordered_map>
+
+#include "test_allocator.h"
+
+template <class... Args>
+inline constexpr bool is_constructible_but_throws =
+    std::is_constructible<Args...>::value && !std::is_nothrow_constructible<Args...>::value;
+
+namespace std_allocator {
+using map        = std::unordered_multimap<int, int>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using map =
+    std::unordered_multimap<int, int, std::hash<int>, std::equal_to<int>, test_allocator<std::pair<const int, int>>>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(!std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..38780139ff417
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/noexcept.compile.pass.cpp
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <unordered_set>
+
+#include <type_traits>
+#include <unordered_set>
+
+#include "test_allocator.h"
+
+template <class... Args>
+inline constexpr bool is_constructible_but_throws =
+    std::is_constructible<Args...>::value && !std::is_nothrow_constructible<Args...>::value;
+
+namespace std_allocator {
+using map        = std::unordered_multiset<int>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using map        = std::unordered_multiset<int, std::hash<int>, std::equal_to<int>, test_allocator<int>>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(!std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.set/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..df0d87f74292b
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.set/noexcept.compile.pass.cpp
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <unordered_set>
+
+#include <type_traits>
+#include <unordered_set>
+
+#include "test_allocator.h"
+
+template <class... Args>
+inline constexpr bool is_constructible_but_throws =
+    std::is_constructible<Args...>::value && !std::is_nothrow_constructible<Args...>::value;
+
+namespace std_allocator {
+using map        = std::unordered_set<int>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using map        = std::unordered_set<int, std::hash<int>, std::equal_to<int>, test_allocator<int>>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(!std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/move_noexcept.pass.cpp
index d281dafbcf72d..5ad42e8734f8c 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/move_noexcept.pass.cpp
@@ -29,17 +29,6 @@
 #include "MoveOnly.h"
 #include "test_allocator.h"
 
-template <class T>
-struct ThrowingMoveAllocator {
-  using value_type                                    = T;
-  explicit ThrowingMoveAllocator()                    = default;
-  ThrowingMoveAllocator(const ThrowingMoveAllocator&) = default;
-  ThrowingMoveAllocator(ThrowingMoveAllocator&&) noexcept(false) {}
-  T* allocate(std::ptrdiff_t n) { return std::allocator<T>().allocate(n); }
-  void deallocate(T* p, std::ptrdiff_t n) { return std::allocator<T>().deallocate(p, n); }
-  friend bool operator==(ThrowingMoveAllocator, ThrowingMoveAllocator) = default;
-};
-
 struct ThrowingMoveComp {
   ThrowingMoveComp() = default;
   ThrowingMoveComp(const ThrowingMoveComp&) noexcept(true) {}
@@ -73,24 +62,6 @@ int main(int, char**) {
     C c;
     C d = std::move(c);
   }
-#if _LIBCPP_VERSION
-  {
-    // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators
-    using C = std::flat_map<int, int, std::less<int>, std::deque<int, ThrowingMoveAllocator<int>>, std::vector<int>>;
-    static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);
-    static_assert(!std::is_nothrow_move_constructible_v<C>);
-    C c;
-    C d = std::move(c);
-  }
-  {
-    // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators
-    using C = std::flat_map<int, int, std::less<int>, std::vector<int>, std::deque<int, ThrowingMoveAllocator<int>>>;
-    static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);
-    static_assert(!std::is_nothrow_move_constructible_v<C>);
-    C c;
-    C d = std::move(c);
-  }
-#endif // _LIBCPP_VERSION
   {
     // Comparator fails to be nothrow-move-constructible
     using C = std::flat_map<int, int, ThrowingMoveComp>;
diff --git a/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.cons/move_noexcept.pass.cpp
index e038902e26d52..f5d18ac331378 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.cons/move_noexcept.pass.cpp
@@ -29,17 +29,6 @@
 #include "MoveOnly.h"
 #include "test_allocator.h"
 
-template <class T>
-struct ThrowingMoveAllocator {
-  using value_type                                    = T;
-  explicit ThrowingMoveAllocator()                    = default;
-  ThrowingMoveAllocator(const ThrowingMoveAllocator&) = default;
-  ThrowingMoveAllocator(ThrowingMoveAllocator&&) noexcept(false) {}
-  T* allocate(std::ptrdiff_t n) { return std::allocator<T>().allocate(n); }
-  void deallocate(T* p, std::ptrdiff_t n) { return std::allocator<T>().deallocate(p, n); }
-  friend bool operator==(ThrowingMoveAllocator, ThrowingMoveAllocator) = default;
-};
-
 struct ThrowingMoveComp {
   ThrowingMoveComp() = default;
   ThrowingMoveComp(const ThrowingMoveComp&) noexcept(true) {}
@@ -73,26 +62,6 @@ int main(int, char**) {
     C c;
     C d = std::move(c);
   }
-#if _LIBCPP_VERSION
-  {
-    // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators
-    using C =
-        std::flat_multimap<int, int, std::less<int>, std::deque<int, ThrowingMoveAllocator<int>>, std::vector<int>>;
-    static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);
-    static_assert(!std::is_nothrow_move_constructible_v<C>);
-    C c;
-    C d = std::move(c);
-  }
-  {
-    // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators
-    using C =
-        std::flat_multimap<int, int, std::less<int>, std::vector<int>, std::deque<int, ThrowingMoveAllocator<int>>>;
-    static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);
-    static_assert(!std::is_nothrow_move_constructible_v<C>);
-    C c;
-    C d = std::move(c);
-  }
-#endif // _LIBCPP_VERSION
   {
     // Comparator fails to be nothrow-move-constructible
     using C = std::flat_multimap<int, int, ThrowingMoveComp>;
diff --git a/libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.cons/move.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.cons/move.pass.cpp
index f9fea737254d3..113a7e36259f5 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.cons/move.pass.cpp
@@ -92,17 +92,6 @@ constexpr bool test() {
   return true;
 }
 
-template <class T>
-struct ThrowingMoveAllocator {
-  using value_type                                    = T;
-  explicit ThrowingMoveAllocator()                    = default;
-  ThrowingMoveAllocator(const ThrowingMoveAllocator&) = default;
-  ThrowingMoveAllocator(ThrowingMoveAllocator&&) noexcept(false) {}
-  T* allocate(std::ptrdiff_t n) { return std::allocator<T>().allocate(n); }
-  void deallocate(T* p, std::ptrdiff_t n) { return std::allocator<T>().deallocate(p, n); }
-  friend bool operator==(ThrowingMoveAllocator, ThrowingMoveAllocator) = default;
-};
-
 struct ThrowingMoveComp {
   ThrowingMoveComp() = default;
   ThrowingMoveComp(const ThrowingMoveComp&) noexcept(true) {}
@@ -137,14 +126,6 @@ void test_move_noexcept() {
     C d = std::move(c);
   }
 #if _LIBCPP_VERSION
-  {
-    // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators
-    using C = std::flat_multiset<int, std::less<int>, std::deque<int, ThrowingMoveAllocator<int>>>;
-    static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);
-    static_assert(!std::is_nothrow_move_constructible_v<C>);
-    C c;
-    C d = std::move(c);
-  }
   {
     // Comparator fails to be nothrow-move-constructible
     using C = std::flat_multiset<int, ThrowingMoveComp>;
diff --git a/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/move.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/move.pass.cpp
index 1d7a3589cc109..5d6ab501cc38f 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/move.pass.cpp
@@ -83,14 +83,14 @@ constexpr void test() {
 }
 
 template <class T>
-struct ThrowingMoveAllocator {
+struct PotentiallyThrowingMoveAllocator {
   using value_type                                    = T;
-  explicit ThrowingMoveAllocator()                    = default;
-  ThrowingMoveAllocator(const ThrowingMoveAllocator&) = default;
-  constexpr ThrowingMoveAllocator(ThrowingMoveAllocator&&) noexcept(false) {}
+  explicit PotentiallyThrowingMoveAllocator()                    = default;
+  PotentiallyThrowingMoveAllocator(const PotentiallyThrowingMoveAllocator&) = default;
+  constexpr PotentiallyThrowingMoveAllocator(PotentiallyThrowingMoveAllocator&&) noexcept(false) {}
   constexpr T* allocate(std::ptrdiff_t n) { return std::allocator<T>().allocate(n); }
   constexpr void deallocate(T* p, std::ptrdiff_t n) { return std::allocator<T>().deallocate(p, n); }
-  friend bool operator==(ThrowingMoveAllocator, ThrowingMoveAllocator) = default;
+  friend bool operator==(PotentiallyThrowingMoveAllocator, PotentiallyThrowingMoveAllocator) = default;
 };
 
 struct ThrowingMoveComp {
@@ -117,9 +117,9 @@ constexpr void test_move_noexcept() {
 #if _LIBCPP_VERSION
   if (!TEST_IS_CONSTANT_EVALUATED) {
     // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators
-    using C = std::flat_set<int, std::less<int>, std::deque<int, ThrowingMoveAllocator<int>>>;
-    static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);
-    static_assert(!std::is_nothrow_move_constructible_v<C>);
+    using C = std::flat_set<int, std::less<int>, std::deque<int, PotentiallyThrowingMoveAllocator<int>>>;
+    static_assert(std::is_nothrow_move_constructible_v<std::deque<int, PotentiallyThrowingMoveAllocator<int>>>);
+    static_assert(std::is_nothrow_move_constructible_v<C>);
     C c;
     C d = std::move(c);
   }
diff --git a/libcxx/test/std/containers/sequences/deque/deque.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.special/swap_noexcept.pass.cpp
index 6347496d67c15..96195bf13da67 100644
--- a/libcxx/test/std/containers/sequences/deque/deque.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/sequences/deque/deque.special/swap_noexcept.pass.cpp
@@ -69,14 +69,12 @@ int main(int, char**) {
 #endif // _LIBCPP_VERSION
   {
     typedef std::deque<MoveOnly, some_alloc<MoveOnly>> C;
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 14 || defined(_LIBCPP_VERSION)
     //  In C++14, if POCS is set, swapping the allocator is required not to throw
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
-#else
-    static_assert(!noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
 #endif
   }
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 14 || defined(_LIBCPP_VERSION)
   {
     typedef std::deque<MoveOnly, some_alloc2<MoveOnly>> C;
     //  if the allocators are always equal, then the swap can be noexcept
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/swap_noexcept.compile.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/swap_noexcept.compile.pass.cpp
index 02b7b471a1ae8..978727f9418bf 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/swap_noexcept.compile.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/swap_noexcept.compile.pass.cpp
@@ -69,14 +69,12 @@ void f() {
 #endif // _LIBCPP_VERSION
   {
     typedef std::forward_list<MoveOnly, some_alloc<MoveOnly>> C;
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 14 || defined(_LIBCPP_VERSION)
     //  In C++14, if POCS is set, swapping the allocator is required not to throw
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
-#else
-    static_assert(!noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
 #endif
   }
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 14 || defined(_LIBCPP_VERSION)
   {
     typedef std::forward_list<MoveOnly, some_alloc2<MoveOnly>> C;
     //  if the allocators are always equal, then the swap can be noexcept
diff --git a/libcxx/test/std/containers/sequences/list/list.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/sequences/list/list.special/swap_noexcept.pass.cpp
index 037c7d07c4cb8..f528b7ebad074 100644
--- a/libcxx/test/std/containers/sequences/list/list.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/sequences/list/list.special/swap_noexcept.pass.cpp
@@ -69,14 +69,12 @@ TEST_CONSTEXPR_CXX26 bool test() {
 #endif // _LIBCPP_VERSION
   {
     typedef std::list<MoveOnly, some_alloc<MoveOnly>> C;
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 14 || defined(_LIBCPP_VERSION)
     //  In C++14, if POCS is set, swapping the allocator is required not to throw
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
-#else
-    static_assert(!noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
 #endif
   }
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 14 || defined(_LIBCPP_VERSION)
   {
     typedef std::list<MoveOnly, some_alloc2<MoveOnly>> C;
     //  if the allocators are always equal, then the swap can be noexcept
diff --git a/libcxx/test/std/containers/sequences/vector.bool/move_noexcept.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/move_noexcept.pass.cpp
deleted file mode 100644
index 5bdae01f871f9..0000000000000
--- a/libcxx/test/std/containers/sequences/vector.bool/move_noexcept.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <vector>
-
-// vector(vector&&)
-//        noexcept(is_nothrow_move_constructible<allocator_type>::value);
-
-// This tests a conforming extension
-
-// UNSUPPORTED: c++03
-
-#include <cassert>
-#include <vector>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "test_allocator.h"
-
-template <class T>
-struct some_alloc {
-  typedef T value_type;
-  some_alloc(const some_alloc&);
-};
-
-int main(int, char**) {
-#if defined(_LIBCPP_VERSION)
-  {
-    typedef std::vector<bool> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::vector<bool, test_allocator<bool>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::vector<bool, other_allocator<bool>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-#endif // _LIBCPP_VERSION
-  {
-    //  In C++17, move constructors for allocators are not allowed to throw
-#if TEST_STD_VER > 14
-#  if defined(_LIBCPP_VERSION)
-    typedef std::vector<bool, some_alloc<bool>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-#  endif // _LIBCPP_VERSION
-#else
-    typedef std::vector<bool, some_alloc<bool>> C;
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
-#endif
-  }
-
-  return 0;
-}
diff --git a/libcxx/test/std/containers/sequences/vector.bool/swap_noexcept.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/swap_noexcept.pass.cpp
index 1683191403709..5c65510a203c0 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector.bool/swap_noexcept.pass.cpp
@@ -64,27 +64,16 @@ int main(int, char**) {
     typedef std::vector<bool, other_allocator<bool>> C;
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
   }
-#endif // _LIBCPP_VERSION
   {
-#if TEST_STD_VER >= 14
-#  if defined(_LIBCPP_VERSION)
     //  In C++14, if POCS is set, swapping the allocator is required not to throw
     typedef std::vector<bool, some_alloc<bool>> C;
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
-#  endif // _LIBCPP_VERSION
-#else
-    typedef std::vector<bool, some_alloc<bool>> C;
-    static_assert(!noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
-#endif
   }
-#if TEST_STD_VER >= 14
-#  if defined(_LIBCPP_VERSION)
   {
     typedef std::vector<bool, some_alloc2<bool>> C;
     //  if the allocators are always equal, then the swap can be noexcept
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
   }
-#  endif // _LIBCPP_VERSION
 #endif
 
   return 0;
diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/move_noexcept.pass.cpp
index 13dc6d41684fa..21b14a76260cb 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/move_noexcept.pass.cpp
@@ -44,11 +44,8 @@ int main(int, char**) {
   }
   {
     typedef std::vector<MoveOnly, some_alloc<MoveOnly>> C;
-    //  In C++17, move constructors for allocators are not allowed to throw
-#if TEST_STD_VER > 14
+#if TEST_STD_VER > 14 || defined(_LIBCPP_VERSION)
     static_assert(std::is_nothrow_move_constructible<C>::value, "");
-#else
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
 #endif
   }
 
diff --git a/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp
index d807cac2c72c8..ba7f93e2a52cb 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp
@@ -32,9 +32,7 @@ int main(int, char**) {
   }
   {
     typedef std::basic_string<char, std::char_traits<char>, limited_allocator<char, 10>> C;
-#if TEST_STD_VER <= 14
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
-#else
+#if TEST_STD_VER >= 17 || defined(_LIBCPP_VERSION)
     static_assert(std::is_nothrow_move_constructible<C>::value, "");
 #endif
   }


From libcxx-commits at lists.llvm.org  Tue Jul  7 19:16:32 2026
From: libcxx-commits at lists.llvm.org (Avi Patel via libcxx-commits)
Date: Tue, 07 Jul 2026 19:16:32 -0700 (PDT)
Subject: [libcxx-commits] =?utf-8?q?=5Blibcxx=5D_=5Blibc++=5D_Implement_LW?=
 =?utf-8?q?G4472=3A_std=3A=3Aatomic=5Fref=3Cconst_T=3E_can_be_construct?=
 =?utf-8?b?4oCmIChQUiAjMjA4MTMxKQ==?=
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208131@github.com>
Message-ID: <6a4db300.d5797644.338ca1.67f9@mx.google.com>


https://github.com/firedog1234 updated https://github.com/llvm/llvm-project/pull/208131

>From 54aa6b59fd6e6964ea351f7dd12a4ed0358ae454 Mon Sep 17 00:00:00 2001
From: avipatel <aviipatel06 at gmail.com>
Date: Tue, 7 Jul 2026 21:19:43 -0400
Subject: [PATCH 1/2] [libc++] Implement LWG4472: std::atomic_ref<const T> can
 be constructed from temporaries

---
 libcxx/docs/Status/Cxx26Issues.csv                | 2 +-
 libcxx/include/__atomic/atomic_ref.h              | 8 ++++++++
 libcxx/test/std/atomics/atomics.ref/ctor.pass.cpp | 6 +++++-
 3 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 23adfde5bd4d7..ade40adc499fe 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -295,7 +295,7 @@
 "`LWG4467 <https://wg21.link/LWG4467>`__","``hive::splice`` can throw ``bad_alloc``","2026-03 (Croydon)","","","`#189837 <https://github.com/llvm/llvm-project/issues/189837>`__",""
 "`LWG4468 <https://wg21.link/LWG4468>`__","§[const.wrap.class] ""``operator decltype(auto)``"" is ill-formed","2026-03 (Croydon)","|Complete|","23","`#189838 <https://github.com/llvm/llvm-project/issues/189838>`__",""
 "`LWG4469 <https://wg21.link/LWG4469>`__","Names of parameters of addressable function shall remain unspecified","2026-03 (Croydon)","","","`#189839 <https://github.com/llvm/llvm-project/issues/189839>`__",""
-"`LWG4472 <https://wg21.link/LWG4472>`__","``std::atomic_ref<const T>`` can be constructed from temporaries","2026-03 (Croydon)","","","`#189840 <https://github.com/llvm/llvm-project/issues/189840>`__",""
+"`LWG4472 <https://wg21.link/LWG4472>`__","``std::atomic_ref<const T>`` can be constructed from temporaries","2026-03 (Croydon)","|Complete|","23","`#189840 <https://github.com/llvm/llvm-project/issues/189840>`__",""
 "`LWG4474 <https://wg21.link/LWG4474>`__","""``round_to_nearest``"" rounding mode is unclear","2026-03 (Croydon)","","","`#189841 <https://github.com/llvm/llvm-project/issues/189841>`__",""
 "`LWG4476 <https://wg21.link/LWG4476>`__","``run_loop`` should not have a ``set_error`` completion","2026-03 (Croydon)","","","`#189842 <https://github.com/llvm/llvm-project/issues/189842>`__",""
 "`LWG4477 <https://wg21.link/LWG4477>`__","Placement ``operator delete`` should be constexpr","2026-03 (Croydon)","|Complete|","23","`#189843 <https://github.com/llvm/llvm-project/issues/189843>`__",""
diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index c551f99024931..93f19a740ba3a 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -259,6 +259,8 @@ struct atomic_ref : public __atomic_ref_base<_Tp> {
         "atomic_ref ctor: referenced object must be aligned to required_alignment");
   }
 
+  _LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp&&) = delete;
+
   _LIBCPP_HIDE_FROM_ABI atomic_ref(const atomic_ref&) noexcept = default;
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept { return __base::operator=(__desired); }
@@ -279,6 +281,8 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
         "atomic_ref ctor: referenced object must be aligned to required_alignment");
   }
 
+  _LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp&&) = delete;
+
   _LIBCPP_HIDE_FROM_ABI atomic_ref(const atomic_ref&) noexcept = default;
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept { return __base::operator=(__desired); }
@@ -325,6 +329,8 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
         "atomic_ref ctor: referenced object must be aligned to required_alignment");
   }
 
+  _LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp&&) = delete;
+
   _LIBCPP_HIDE_FROM_ABI atomic_ref(const atomic_ref&) noexcept = default;
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept { return __base::operator=(__desired); }
@@ -368,6 +374,8 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
 
   _LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp*& __ptr) : __base(__ptr) {}
 
+  _LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp*&&) = delete;
+
   _LIBCPP_HIDE_FROM_ABI _Tp* operator=(_Tp* __desired) const noexcept { return __base::operator=(__desired); }
 
   atomic_ref& operator=(const atomic_ref&) = delete;
diff --git a/libcxx/test/std/atomics/atomics.ref/ctor.pass.cpp b/libcxx/test/std/atomics/atomics.ref/ctor.pass.cpp
index 9fb06de357eb4..d37fa245d974e 100644
--- a/libcxx/test/std/atomics/atomics.ref/ctor.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/ctor.pass.cpp
@@ -11,6 +11,7 @@
 // <atomic>
 
 // explicit atomic_ref(T&);
+// explicit atomic_ref(T&&) = delete;
 
 #include <atomic>
 #include <type_traits>
@@ -21,10 +22,13 @@
 template <typename T>
 struct TestCtor {
   void operator()() const {
-    // check that the constructor is explicit
     static_assert(!std::is_convertible_v<T, std::atomic_ref<T>>);
     static_assert(std::is_constructible_v<std::atomic_ref<T>, T&>);
 
+    static_assert(!std::is_constructible_v<std::atomic_ref<T>, T&&>);
+    static_assert(!std::is_constructible_v<std::atomic_ref<const T>, T&&>);
+    static_assert(!std::is_constructible_v<std::atomic_ref<const T>, const T&&>);
+
     alignas(std::atomic_ref<T>::required_alignment) T x(T(0));
     std::atomic_ref<T> a(x);
     (void)a;

>From e833ab2e98cd1d970e1bee45d2f04ef2a5384f54 Mon Sep 17 00:00:00 2001
From: avipatel <aviipatel06 at gmail.com>
Date: Tue, 7 Jul 2026 22:15:28 -0400
Subject: [PATCH 2/2] chore: added back comment for clarity

---
 libcxx/test/std/atomics/atomics.ref/ctor.pass.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/atomics/atomics.ref/ctor.pass.cpp b/libcxx/test/std/atomics/atomics.ref/ctor.pass.cpp
index d37fa245d974e..3eca6a139d061 100644
--- a/libcxx/test/std/atomics/atomics.ref/ctor.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/ctor.pass.cpp
@@ -22,6 +22,7 @@
 template <typename T>
 struct TestCtor {
   void operator()() const {
+    // check that the constructor is explicit
     static_assert(!std::is_convertible_v<T, std::atomic_ref<T>>);
     static_assert(std::is_constructible_v<std::atomic_ref<T>, T&>);
 


From libcxx-commits at lists.llvm.org  Tue Jul  7 19:23:21 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 19:23:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Backport P3029R1 Better
 `mdspan`'s CTAD to C++20/23 (PR #208132)
Message-ID: <llvm.org/llvm/llvm-project/pull/208132@github.com>


https://github.com/frederick-vs-ja created https://github.com/llvm/llvm-project/pull/208132

Also backport
- support for `T::value` of a reference type in `integral-constant-like` from P2781R9 `std::constant_wrapper`, and
- LWG4351 _`integral-constant-like`_ needs more `remove_cvref_t`.

P3029R1 (and its further fixes) should be treated as a Defect Report against C++20/23 to avoid behavior difference in different modes. Other implementations will do or are already doing the same.

Fixes #207727.

>From 90b9f5ada6e9997bb10d70c3d051796134431fd1 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 8 Jul 2026 10:14:16 +0800
Subject: [PATCH] [libc++] Backport P3029R1 Better `mdspan`'s CTAD to C++20/23

Also backport
- support for `T::value` of a reference type in `integral-constant-like`
from P2781R9 `std::constant_wrapper`, and
- LWG4351 _`integral-constant-like`_ needs more `remove_cvref_t`.

P3029R1 (and its further fixes) should be treated as a Defect Report
against C++20/23 to avoid behavior difference in different modes. Other
implementations will do or are already doing the same.
---
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 libcxx/include/__mdspan/extents.h             |  6 -----
 libcxx/include/__mdspan/mdspan.h              |  7 -----
 libcxx/include/mdspan                         |  6 +----
 libcxx/include/span                           | 15 +++--------
 .../views/mdspan/extents/ctad.pass.cpp        | 13 ++++++---
 .../views/mdspan/mdspan/deduction.pass.cpp    | 19 ++++++++-----
 .../views.span/span.cons/deduct.pass.cpp      | 27 ++++++++++++++-----
 8 files changed, 48 insertions(+), 47 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..e1fcaf907c95b 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -61,7 +61,7 @@
 "`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","","`#105423 <https://github.com/llvm/llvm-project/issues/105423>`__",""
 "`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","22","`#105424 <https://github.com/llvm/llvm-project/issues/105424>`__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
 "`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","`#105425 <https://github.com/llvm/llvm-project/issues/105425>`__",""
-"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__",""
+"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23. (MSVC STL and libstdc++ will do the same.)"
 "","","","","","",""
 "`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","`#105427 <https://github.com/llvm/llvm-project/issues/105427>`__",""
 "`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","`#105428 <https://github.com/llvm/llvm-project/issues/105428>`__","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
diff --git a/libcxx/include/__mdspan/extents.h b/libcxx/include/__mdspan/extents.h
index bc4490aa5b470..99366b9ca9cc4 100644
--- a/libcxx/include/__mdspan/extents.h
+++ b/libcxx/include/__mdspan/extents.h
@@ -466,15 +466,9 @@ using dims = dextents<_IndexType, _Rank>;
 #  endif
 
 // Deduction guide for extents
-#  if _LIBCPP_STD_VER >= 26
 template <class... _IndexTypes>
   requires(is_convertible_v<_IndexTypes, size_t> && ...)
 explicit extents(_IndexTypes...) -> extents<size_t, __maybe_static_ext<_IndexTypes>...>;
-#  else
-template <class... _IndexTypes>
-  requires(is_convertible_v<_IndexTypes, size_t> && ...)
-explicit extents(_IndexTypes...) -> extents<size_t, size_t(((void)sizeof(_IndexTypes), dynamic_extent))...>;
-#  endif
 
 namespace __mdspan_detail {
 
diff --git a/libcxx/include/__mdspan/mdspan.h b/libcxx/include/__mdspan/mdspan.h
index bcfa302231cd4..3b78f792770f0 100644
--- a/libcxx/include/__mdspan/mdspan.h
+++ b/libcxx/include/__mdspan/mdspan.h
@@ -312,17 +312,10 @@ class mdspan {
 #  endif
 };
 
-#  if _LIBCPP_STD_VER >= 26
 template <class _ElementType, class... _OtherIndexTypes>
   requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
 explicit mdspan(_ElementType*, _OtherIndexTypes...)
     -> mdspan<_ElementType, extents<size_t, __maybe_static_ext<_OtherIndexTypes>...>>;
-#  else
-template <class _ElementType, class... _OtherIndexTypes>
-  requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
-explicit mdspan(_ElementType*, _OtherIndexTypes...)
-    -> mdspan<_ElementType, dextents<size_t, sizeof...(_OtherIndexTypes)>>;
-#  endif
 
 template <class _Pointer>
   requires(is_pointer_v<remove_reference_t<_Pointer>>)
diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan
index 2f0e1f9b87751..6313c03f041af 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -411,14 +411,10 @@ namespace std {
     mdspan(Pointer&&)
       -> mdspan<remove_pointer_t<remove_reference_t<Pointer>>, extents<size_t>>;
 
-  template<class ElementType, class... Integrals>
-    requires ((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
-    explicit mdspan(ElementType*, Integrals...)
-      -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;            // until C++26
   template<class ElementType, class... Integrals>
     requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
     explicit mdspan(ElementType*, Integrals...)
-      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;  // since C++26
+      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;
 
   template<class ElementType, class OtherIndexType, size_t N>
     mdspan(ElementType*, span<OtherIndexType, N>)
diff --git a/libcxx/include/span b/libcxx/include/span
index e39b811f9b1ea..1c52983f808ca 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -19,16 +19,16 @@ namespace std {
 inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();
 
 template<class T>
-  concept integral-constant-like =                           // exposition only, since C++26
-    is_integral_v<decltype(T::value)> &&
-    !is_same_v<bool, remove_const_t<decltype(T::value)>> &&
+  concept integral-constant-like =                           // exposition only
+    is_integral_v<remove_cvref_t<decltype(T::value)>> &&
+    !is_same_v<bool, remove_cvref_t<decltype(T::value)>> &&
     convertible_to<T, decltype(T::value)> &&
     equality_comparable_with<T, decltype(T::value)> &&
     bool_constant<T() == T::value>::value &&
     bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value;
 
 template<class T>
-  constexpr size_t maybe-static-ext = dynamic_extent;        // exposition only, since C++26
+  constexpr size_t maybe-static-ext = dynamic_extent;        // exposition only
 template<integral-constant-like T>
   constexpr size_t maybe-static-ext<T> = {T::value};
 
@@ -122,8 +122,6 @@ private:
     size_type size_;  // exposition only
 };
 
-template<class It, class EndOrSize>
-    span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;                             // until C++26
 template<class It, class EndOrSize>
     span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>; // since C++26
 
@@ -597,7 +595,6 @@ template <class _Tp, size_t _Extent>
   return __s.__as_writable_bytes();
 }
 
-#    if _LIBCPP_STD_VER >= 26
 template <class _Tp>
 concept __integral_constant_like =
     is_integral_v<remove_cvref_t<decltype(_Tp::value)>> && !is_same_v<bool, remove_cvref_t<decltype(_Tp::value)>> &&
@@ -613,10 +610,6 @@ inline constexpr size_t __maybe_static_ext<_Tp> = {_Tp::value};
 
 template <contiguous_iterator _It, class _EndOrSize>
 span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>, __maybe_static_ext<_EndOrSize>>;
-#    else
-template <contiguous_iterator _It, class _EndOrSize>
-span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;
-#    endif
 
 template <class _Tp, size_t _Sz>
 span(_Tp (&)[_Sz]) -> span<_Tp, _Sz>;
diff --git a/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
index b72c3e050b1f9..e34113f91ad06 100644
--- a/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
@@ -13,8 +13,7 @@
 // explicit extents(Integrals...) -> see below;
 //   Constraints: (is_convertible_v<Integrals, size_t> && ...) is true.
 //
-// Remarks: The deduced type is dextents<size_t, sizeof...(Integrals)>.           // until C++26
-// Remarks: The deduced type is extents<size_t, maybe-static-ext<Integrals>...>.  // since C++26
+// Remarks: The deduced type is extents<size_t, maybe-static-ext<Integrals>...>.
 
 #include <cassert>
 #include <cstddef>
@@ -31,6 +30,9 @@ struct NoDefaultCtorIndex {
   constexpr operator size_t() const noexcept { return value; }
 };
 
+template <std::size_t N>
+using size_ref_constant = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, N>::value>;
+
 template <class E, class Expected>
 constexpr void test(E e, Expected expected) {
   ASSERT_SAME_TYPE(E, Expected);
@@ -47,13 +49,16 @@ constexpr bool test() {
        std::extents<std::size_t, D, D, D, D, D, D, D, D, D>(1, 2u, 3, 4, 5, 6, 7, 8, 9));
   test(std::extents(NoDefaultCtorIndex{1}, NoDefaultCtorIndex{2}), std::extents<std::size_t, D, D>(1, 2));
 
-#if _LIBCPP_STD_VER >= 26
   // P3029R1: deduction from `integral_constant`
   test(std::extents(std::integral_constant<size_t, 5>{}), std::extents<std::size_t, 5>());
   test(std::extents(std::integral_constant<size_t, 5>{}, 6), std::extents<std::size_t, 5, std::dynamic_extent>(6));
   test(std::extents(std::integral_constant<size_t, 5>{}, 6, std::integral_constant<size_t, 7>{}),
        std::extents<std::size_t, 5, std::dynamic_extent, 7>(6));
-#endif
+  // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
+  test(std::extents(size_ref_constant<5>{}), std::extents<std::size_t, 5>());
+  test(std::extents(size_ref_constant<5>{}, 6), std::extents<std::size_t, 5, std::dynamic_extent>(6));
+  test(std::extents(std::integral_constant<size_t, 5>{}, 6, size_ref_constant<7>{}),
+       std::extents<std::size_t, 5, std::dynamic_extent, 7>(6));
 
   return true;
 }
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
index 762841016e29e..6c12ccb7fd86a 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
@@ -22,11 +22,7 @@
 //  template<class ElementType, class... Integrals>
 //    requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
 //    explicit mdspan(ElementType*, Integrals...)
-//      -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;            // until C++26
-//  template<class ElementType, class... Integrals>
-//    requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
-//    explicit mdspan(ElementType*, Integrals...)
-//      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;  // since C++26
+//      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;
 //
 //  template<class ElementType, class OtherIndexType, size_t N>
 //    mdspan(ElementType*, span<OtherIndexType, N>)
@@ -53,6 +49,7 @@
 
 #include <mdspan>
 #include <cassert>
+#include <cstddef>
 #include <concepts>
 #include <span> // dynamic_extent
 #include <type_traits>
@@ -98,6 +95,9 @@ struct SizeTIntType {
   constexpr operator size_t() const noexcept { return size_t(val); }
 };
 
+template <std::size_t N>
+using size_ref_constant = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, N>::value>;
+
 template <class H, class A>
   requires(sizeof(decltype(std::mdspan(std::declval<H>(), 10))) > 0)
 constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
@@ -107,7 +107,6 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
   // deduction from pointer and integral like
   ASSERT_SAME_TYPE(decltype(std::mdspan(handle, 5, SizeTIntType(6))), std::mdspan<T, std::dextents<size_t, 2>>);
 
-#if _LIBCPP_STD_VER >= 26
   // P3029R1: deduction from `integral_constant`
   ASSERT_SAME_TYPE(
       decltype(std::mdspan(handle, std::integral_constant<size_t, 5>{})), std::mdspan<T, std::extents<size_t, 5>>);
@@ -117,7 +116,13 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
       decltype(std::mdspan(
           handle, std::integral_constant<size_t, 5>{}, std::dynamic_extent, std::integral_constant<size_t, 7>{})),
       std::mdspan<T, std::extents<size_t, 5, std::dynamic_extent, 7>>);
-#endif
+  // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle, size_ref_constant<5>{})), std::mdspan<T, std::extents<std::size_t, 5>>);
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle, size_ref_constant<5>{}, std::dynamic_extent)),
+                   std::mdspan<T, std::extents<std::size_t, 5, std::dynamic_extent>>);
+  ASSERT_SAME_TYPE(decltype(std::mdspan(
+                       handle, std::integral_constant<std::size_t, 5>{}, std::dynamic_extent, size_ref_constant<7>{})),
+                   std::mdspan<T, std::extents<std::size_t, 5, std::dynamic_extent, 7>>);
 
   std::array<char, 3> exts;
   // deduction from pointer and array
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index 9e41afad2258e..35d9b36f824aa 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -11,10 +11,7 @@
 // <span>
 
 // template<class It, class EndOrSize>
-//   span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>>; // until C++26
-// template<class It, class EndOrSize>
-//   span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>,
-//                               maybe-static-ext<EndOrSize>>; // since C++26
+//   span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>;
 // template<class T, size_t N>
 //   span(T (&)[N]) -> span<T, N>;
 // template<class T, size_t N>
@@ -27,6 +24,7 @@
 #include <span>
 #include <array>
 #include <cassert>
+#include <cstddef>
 #include <iterator>
 #include <memory>
 #include <string>
@@ -50,7 +48,6 @@ void test_iterator_sentinel() {
     assert(s.data() == std::data(arr));
   }
 
-#if TEST_STD_VER >= 26
   // P3029R1: deduction from `integral_constant`
   {
     std::span s{std::begin(arr), std::integral_constant<size_t, 3>{}};
@@ -58,15 +55,33 @@ void test_iterator_sentinel() {
     assert(s.size() == 3);
     assert(s.data() == std::data(arr));
   }
-
+  // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
+  {
+    using size_3_ref_type = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, 3>::value>;
+    std::span s{std::begin(arr), size_3_ref_type{}};
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+    assert(s.size() == 3);
+    assert(s.data() == std::data(arr));
+  }
+#if TEST_STD_VER >= 26
   {
     std::span s{std::begin(arr), std::cw<3>};
     ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
     assert(s.size() == 3);
     assert(s.data() == std::data(arr));
   }
+#endif
   {
     // LWG4351 integral-constant-like needs more remove_cvref_t
+    using true_ref_type = std::integral_constant<const bool&, std::true_type::value>;
+    LIBCPP_STATIC_ASSERT(!std::__integral_constant_like<true_ref_type>);
+    std::span s(std::begin(arr), true_ref_type{});
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, std::dynamic_extent>);
+    assert(s.size() == 1);
+    assert(s.data() == std::data(arr));
+  }
+#if TEST_STD_VER >= 26
+  {
     LIBCPP_STATIC_ASSERT(!std::__integral_constant_like<decltype(std::cw<true>)>);
     std::span s(std::begin(arr), std::cw<true>);
     ASSERT_SAME_TYPE(decltype(s), std::span<int, std::dynamic_extent>);


From libcxx-commits at lists.llvm.org  Tue Jul  7 19:24:04 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 19:24:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Backport P3029R1 Better
 `mdspan`'s CTAD to C++20/23 (PR #208132)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208132@github.com>
Message-ID: <6a4db4c4.1905e68e.18ad68.cf91@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: A. Jiang (frederick-vs-ja)

<details>
<summary>Changes</summary>

Also backport
- support for `T::value` of a reference type in `integral-constant-like` from P2781R9 `std::constant_wrapper`, and
- LWG4351 _`integral-constant-like`_ needs more `remove_cvref_t`.

P3029R1 (and its further fixes) should be treated as a Defect Report against C++20/23 to avoid behavior difference in different modes. Other implementations will do or are already doing the same.

Fixes #<!-- -->207727.

---
Full diff: https://github.com/llvm/llvm-project/pull/208132.diff


8 Files Affected:

- (modified) libcxx/docs/Status/Cxx26Papers.csv (+1-1) 
- (modified) libcxx/include/__mdspan/extents.h (-6) 
- (modified) libcxx/include/__mdspan/mdspan.h (-7) 
- (modified) libcxx/include/mdspan (+1-5) 
- (modified) libcxx/include/span (+4-11) 
- (modified) libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp (+9-4) 
- (modified) libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp (+12-7) 
- (modified) libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp (+21-6) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..e1fcaf907c95b 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -61,7 +61,7 @@
 "`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","","`#105423 <https://github.com/llvm/llvm-project/issues/105423>`__",""
 "`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","22","`#105424 <https://github.com/llvm/llvm-project/issues/105424>`__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
 "`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","`#105425 <https://github.com/llvm/llvm-project/issues/105425>`__",""
-"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__",""
+"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23. (MSVC STL and libstdc++ will do the same.)"
 "","","","","","",""
 "`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","`#105427 <https://github.com/llvm/llvm-project/issues/105427>`__",""
 "`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","`#105428 <https://github.com/llvm/llvm-project/issues/105428>`__","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
diff --git a/libcxx/include/__mdspan/extents.h b/libcxx/include/__mdspan/extents.h
index bc4490aa5b470..99366b9ca9cc4 100644
--- a/libcxx/include/__mdspan/extents.h
+++ b/libcxx/include/__mdspan/extents.h
@@ -466,15 +466,9 @@ using dims = dextents<_IndexType, _Rank>;
 #  endif
 
 // Deduction guide for extents
-#  if _LIBCPP_STD_VER >= 26
 template <class... _IndexTypes>
   requires(is_convertible_v<_IndexTypes, size_t> && ...)
 explicit extents(_IndexTypes...) -> extents<size_t, __maybe_static_ext<_IndexTypes>...>;
-#  else
-template <class... _IndexTypes>
-  requires(is_convertible_v<_IndexTypes, size_t> && ...)
-explicit extents(_IndexTypes...) -> extents<size_t, size_t(((void)sizeof(_IndexTypes), dynamic_extent))...>;
-#  endif
 
 namespace __mdspan_detail {
 
diff --git a/libcxx/include/__mdspan/mdspan.h b/libcxx/include/__mdspan/mdspan.h
index bcfa302231cd4..3b78f792770f0 100644
--- a/libcxx/include/__mdspan/mdspan.h
+++ b/libcxx/include/__mdspan/mdspan.h
@@ -312,17 +312,10 @@ class mdspan {
 #  endif
 };
 
-#  if _LIBCPP_STD_VER >= 26
 template <class _ElementType, class... _OtherIndexTypes>
   requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
 explicit mdspan(_ElementType*, _OtherIndexTypes...)
     -> mdspan<_ElementType, extents<size_t, __maybe_static_ext<_OtherIndexTypes>...>>;
-#  else
-template <class _ElementType, class... _OtherIndexTypes>
-  requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
-explicit mdspan(_ElementType*, _OtherIndexTypes...)
-    -> mdspan<_ElementType, dextents<size_t, sizeof...(_OtherIndexTypes)>>;
-#  endif
 
 template <class _Pointer>
   requires(is_pointer_v<remove_reference_t<_Pointer>>)
diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan
index 2f0e1f9b87751..6313c03f041af 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -411,14 +411,10 @@ namespace std {
     mdspan(Pointer&&)
       -> mdspan<remove_pointer_t<remove_reference_t<Pointer>>, extents<size_t>>;
 
-  template<class ElementType, class... Integrals>
-    requires ((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
-    explicit mdspan(ElementType*, Integrals...)
-      -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;            // until C++26
   template<class ElementType, class... Integrals>
     requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
     explicit mdspan(ElementType*, Integrals...)
-      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;  // since C++26
+      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;
 
   template<class ElementType, class OtherIndexType, size_t N>
     mdspan(ElementType*, span<OtherIndexType, N>)
diff --git a/libcxx/include/span b/libcxx/include/span
index e39b811f9b1ea..1c52983f808ca 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -19,16 +19,16 @@ namespace std {
 inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();
 
 template<class T>
-  concept integral-constant-like =                           // exposition only, since C++26
-    is_integral_v<decltype(T::value)> &&
-    !is_same_v<bool, remove_const_t<decltype(T::value)>> &&
+  concept integral-constant-like =                           // exposition only
+    is_integral_v<remove_cvref_t<decltype(T::value)>> &&
+    !is_same_v<bool, remove_cvref_t<decltype(T::value)>> &&
     convertible_to<T, decltype(T::value)> &&
     equality_comparable_with<T, decltype(T::value)> &&
     bool_constant<T() == T::value>::value &&
     bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value;
 
 template<class T>
-  constexpr size_t maybe-static-ext = dynamic_extent;        // exposition only, since C++26
+  constexpr size_t maybe-static-ext = dynamic_extent;        // exposition only
 template<integral-constant-like T>
   constexpr size_t maybe-static-ext<T> = {T::value};
 
@@ -122,8 +122,6 @@ private:
     size_type size_;  // exposition only
 };
 
-template<class It, class EndOrSize>
-    span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;                             // until C++26
 template<class It, class EndOrSize>
     span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>; // since C++26
 
@@ -597,7 +595,6 @@ template <class _Tp, size_t _Extent>
   return __s.__as_writable_bytes();
 }
 
-#    if _LIBCPP_STD_VER >= 26
 template <class _Tp>
 concept __integral_constant_like =
     is_integral_v<remove_cvref_t<decltype(_Tp::value)>> && !is_same_v<bool, remove_cvref_t<decltype(_Tp::value)>> &&
@@ -613,10 +610,6 @@ inline constexpr size_t __maybe_static_ext<_Tp> = {_Tp::value};
 
 template <contiguous_iterator _It, class _EndOrSize>
 span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>, __maybe_static_ext<_EndOrSize>>;
-#    else
-template <contiguous_iterator _It, class _EndOrSize>
-span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;
-#    endif
 
 template <class _Tp, size_t _Sz>
 span(_Tp (&)[_Sz]) -> span<_Tp, _Sz>;
diff --git a/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
index b72c3e050b1f9..e34113f91ad06 100644
--- a/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
@@ -13,8 +13,7 @@
 // explicit extents(Integrals...) -> see below;
 //   Constraints: (is_convertible_v<Integrals, size_t> && ...) is true.
 //
-// Remarks: The deduced type is dextents<size_t, sizeof...(Integrals)>.           // until C++26
-// Remarks: The deduced type is extents<size_t, maybe-static-ext<Integrals>...>.  // since C++26
+// Remarks: The deduced type is extents<size_t, maybe-static-ext<Integrals>...>.
 
 #include <cassert>
 #include <cstddef>
@@ -31,6 +30,9 @@ struct NoDefaultCtorIndex {
   constexpr operator size_t() const noexcept { return value; }
 };
 
+template <std::size_t N>
+using size_ref_constant = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, N>::value>;
+
 template <class E, class Expected>
 constexpr void test(E e, Expected expected) {
   ASSERT_SAME_TYPE(E, Expected);
@@ -47,13 +49,16 @@ constexpr bool test() {
        std::extents<std::size_t, D, D, D, D, D, D, D, D, D>(1, 2u, 3, 4, 5, 6, 7, 8, 9));
   test(std::extents(NoDefaultCtorIndex{1}, NoDefaultCtorIndex{2}), std::extents<std::size_t, D, D>(1, 2));
 
-#if _LIBCPP_STD_VER >= 26
   // P3029R1: deduction from `integral_constant`
   test(std::extents(std::integral_constant<size_t, 5>{}), std::extents<std::size_t, 5>());
   test(std::extents(std::integral_constant<size_t, 5>{}, 6), std::extents<std::size_t, 5, std::dynamic_extent>(6));
   test(std::extents(std::integral_constant<size_t, 5>{}, 6, std::integral_constant<size_t, 7>{}),
        std::extents<std::size_t, 5, std::dynamic_extent, 7>(6));
-#endif
+  // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
+  test(std::extents(size_ref_constant<5>{}), std::extents<std::size_t, 5>());
+  test(std::extents(size_ref_constant<5>{}, 6), std::extents<std::size_t, 5, std::dynamic_extent>(6));
+  test(std::extents(std::integral_constant<size_t, 5>{}, 6, size_ref_constant<7>{}),
+       std::extents<std::size_t, 5, std::dynamic_extent, 7>(6));
 
   return true;
 }
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
index 762841016e29e..6c12ccb7fd86a 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
@@ -22,11 +22,7 @@
 //  template<class ElementType, class... Integrals>
 //    requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
 //    explicit mdspan(ElementType*, Integrals...)
-//      -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;            // until C++26
-//  template<class ElementType, class... Integrals>
-//    requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
-//    explicit mdspan(ElementType*, Integrals...)
-//      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;  // since C++26
+//      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;
 //
 //  template<class ElementType, class OtherIndexType, size_t N>
 //    mdspan(ElementType*, span<OtherIndexType, N>)
@@ -53,6 +49,7 @@
 
 #include <mdspan>
 #include <cassert>
+#include <cstddef>
 #include <concepts>
 #include <span> // dynamic_extent
 #include <type_traits>
@@ -98,6 +95,9 @@ struct SizeTIntType {
   constexpr operator size_t() const noexcept { return size_t(val); }
 };
 
+template <std::size_t N>
+using size_ref_constant = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, N>::value>;
+
 template <class H, class A>
   requires(sizeof(decltype(std::mdspan(std::declval<H>(), 10))) > 0)
 constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
@@ -107,7 +107,6 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
   // deduction from pointer and integral like
   ASSERT_SAME_TYPE(decltype(std::mdspan(handle, 5, SizeTIntType(6))), std::mdspan<T, std::dextents<size_t, 2>>);
 
-#if _LIBCPP_STD_VER >= 26
   // P3029R1: deduction from `integral_constant`
   ASSERT_SAME_TYPE(
       decltype(std::mdspan(handle, std::integral_constant<size_t, 5>{})), std::mdspan<T, std::extents<size_t, 5>>);
@@ -117,7 +116,13 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
       decltype(std::mdspan(
           handle, std::integral_constant<size_t, 5>{}, std::dynamic_extent, std::integral_constant<size_t, 7>{})),
       std::mdspan<T, std::extents<size_t, 5, std::dynamic_extent, 7>>);
-#endif
+  // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle, size_ref_constant<5>{})), std::mdspan<T, std::extents<std::size_t, 5>>);
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle, size_ref_constant<5>{}, std::dynamic_extent)),
+                   std::mdspan<T, std::extents<std::size_t, 5, std::dynamic_extent>>);
+  ASSERT_SAME_TYPE(decltype(std::mdspan(
+                       handle, std::integral_constant<std::size_t, 5>{}, std::dynamic_extent, size_ref_constant<7>{})),
+                   std::mdspan<T, std::extents<std::size_t, 5, std::dynamic_extent, 7>>);
 
   std::array<char, 3> exts;
   // deduction from pointer and array
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index 9e41afad2258e..35d9b36f824aa 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -11,10 +11,7 @@
 // <span>
 
 // template<class It, class EndOrSize>
-//   span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>>; // until C++26
-// template<class It, class EndOrSize>
-//   span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>,
-//                               maybe-static-ext<EndOrSize>>; // since C++26
+//   span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>;
 // template<class T, size_t N>
 //   span(T (&)[N]) -> span<T, N>;
 // template<class T, size_t N>
@@ -27,6 +24,7 @@
 #include <span>
 #include <array>
 #include <cassert>
+#include <cstddef>
 #include <iterator>
 #include <memory>
 #include <string>
@@ -50,7 +48,6 @@ void test_iterator_sentinel() {
     assert(s.data() == std::data(arr));
   }
 
-#if TEST_STD_VER >= 26
   // P3029R1: deduction from `integral_constant`
   {
     std::span s{std::begin(arr), std::integral_constant<size_t, 3>{}};
@@ -58,15 +55,33 @@ void test_iterator_sentinel() {
     assert(s.size() == 3);
     assert(s.data() == std::data(arr));
   }
-
+  // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
+  {
+    using size_3_ref_type = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, 3>::value>;
+    std::span s{std::begin(arr), size_3_ref_type{}};
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+    assert(s.size() == 3);
+    assert(s.data() == std::data(arr));
+  }
+#if TEST_STD_VER >= 26
   {
     std::span s{std::begin(arr), std::cw<3>};
     ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
     assert(s.size() == 3);
     assert(s.data() == std::data(arr));
   }
+#endif
   {
     // LWG4351 integral-constant-like needs more remove_cvref_t
+    using true_ref_type = std::integral_constant<const bool&, std::true_type::value>;
+    LIBCPP_STATIC_ASSERT(!std::__integral_constant_like<true_ref_type>);
+    std::span s(std::begin(arr), true_ref_type{});
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, std::dynamic_extent>);
+    assert(s.size() == 1);
+    assert(s.data() == std::data(arr));
+  }
+#if TEST_STD_VER >= 26
+  {
     LIBCPP_STATIC_ASSERT(!std::__integral_constant_like<decltype(std::cw<true>)>);
     std::span s(std::begin(arr), std::cw<true>);
     ASSERT_SAME_TYPE(decltype(s), std::span<int, std::dynamic_extent>);

``````````

</details>


https://github.com/llvm/llvm-project/pull/208132

From libcxx-commits at lists.llvm.org  Tue Jul  7 19:27:43 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 19:27:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG4472:
 std::atomic_ref<const T> can be constructed from temporaries (PR #208131)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208131@github.com>
Message-ID: <6a4db59f.92ea0e2a.13fd37.2884@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/208131

From libcxx-commits at lists.llvm.org  Tue Jul  7 19:43:12 2026
From: libcxx-commits at lists.llvm.org (Shonie Caplan via libcxx-commits)
Date: Tue, 07 Jul 2026 19:43:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Replace mutex+condvar with
 atomics in __call_once (PR #192433)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/192433@github.com>
Message-ID: <6a4db940.f06f4994.80921.9a7a@mx.google.com>


================
@@ -25,8 +27,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 // keep in sync with:  7741191.
 
 #if _LIBCPP_HAS_THREADS
-static constinit __libcpp_mutex_t mut  = _LIBCPP_MUTEX_INITIALIZER;
-static constinit __libcpp_condvar_t cv = _LIBCPP_CONDVAR_INITIALIZER;
+_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(void const volatile*) _NOEXCEPT;
----------------
shoniecaplan wrote:

Kind of figured, I guess the question is more: is that still more clear than using the builtin functions?

https://github.com/llvm/llvm-project/pull/192433

From libcxx-commits at lists.llvm.org  Tue Jul  7 20:21:49 2026
From: libcxx-commits at lists.llvm.org (Jake Egan via libcxx-commits)
Date: Tue, 07 Jul 2026 20:21:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4dc24d.7270d2fd.2d4879.872e@mx.google.com>


================
@@ -258,6 +258,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
----------------
jakeegan wrote:

char* overloads are missing the warning

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Tue Jul  7 20:25:34 2026
From: libcxx-commits at lists.llvm.org (Jake Egan via libcxx-commits)
Date: Tue, 07 Jul 2026 20:25:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4dc32e.3cce8983.225732.5d08@mx.google.com>


================
@@ -103,12 +105,17 @@ int main(int, char**)
             f.open(tmp.c_str(), mode);
             assert(!f.is_open()); // since it already exists
           }
+
----------------
jakeegan wrote:

nit: added blank line

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Tue Jul  7 20:25:34 2026
From: libcxx-commits at lists.llvm.org (Jake Egan via libcxx-commits)
Date: Tue, 07 Jul 2026 20:25:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4dc32e.30759430.24d50c.6d11@mx.google.com>


================
@@ -664,6 +666,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined on AIX by all supported compilers.
----------------
jakeegan wrote:

Please make the comment more concise. "_AIX is defined on AIX by all supported compilers" is redundant and can be removed. 

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Tue Jul  7 20:25:34 2026
From: libcxx-commits at lists.llvm.org (Jake Egan via libcxx-commits)
Date: Tue, 07 Jul 2026 20:25:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4dc32e.4a342cbc.ca844.1ee2@mx.google.com>


================
@@ -83,7 +81,11 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX)
+            assert(!f.is_open());
----------------
jakeegan wrote:

Should check for the warning with `// expected-warning`

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Tue Jul  7 20:25:34 2026
From: libcxx-commits at lists.llvm.org (Jake Egan via libcxx-commits)
Date: Tue, 07 Jul 2026 20:25:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4dc32e.7d41f3a1.237e53.1cc1@mx.google.com>


================
@@ -766,7 +778,6 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   const wchar_t* __mdstr = __make_mdwstring(__mode);
   if (!__mdstr)
     return nullptr;
-
----------------
jakeegan wrote:

nit: deleted blank line

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Tue Jul  7 20:26:04 2026
From: libcxx-commits at lists.llvm.org (Jake Egan via libcxx-commits)
Date: Tue, 07 Jul 2026 20:26:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4dc34c.92ea0e2a.13fd37.e68a@mx.google.com>


https://github.com/jakeegan edited https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Tue Jul  7 20:37:57 2026
From: libcxx-commits at lists.llvm.org (Jake Egan via libcxx-commits)
Date: Tue, 07 Jul 2026 20:37:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4dc615.bf38ae28.39239a.2da0@mx.google.com>


================
@@ -1208,6 +1219,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
----------------
jakeegan wrote:

Checking `_AIX` with a preprocessor conditional should resolve the compilation failure on the CI.

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Tue Jul  7 21:01:54 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 07 Jul 2026 21:01:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4dcbb2.cb30c3e4.3ce559.bf3b@mx.google.com>


================
@@ -95,35 +95,35 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
       : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
----------------
Zingam wrote:

unrelated formatting

https://github.com/llvm/llvm-project/pull/208036

From libcxx-commits at lists.llvm.org  Tue Jul  7 21:01:57 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 21:01:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] Fix redefinition of wchar_t
 overloads on AIX (PR #201432)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201432@github.com>
Message-ID: <6a4dcbb5.27b3b003.fc443.eb46@mx.google.com>


https://github.com/Himadhith ready_for_review https://github.com/llvm/llvm-project/pull/201432

From libcxx-commits at lists.llvm.org  Tue Jul  7 21:02:42 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 21:02:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][AIX] Marking the tests
 UNSUPPORTED as the current implementation of fopen w+bx overrides
 ios::noreplace (PR #188425)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/188425@github.com>
Message-ID: <6a4dcbe2.3cce8983.225732.66a2@mx.google.com>


https://github.com/Himadhith closed https://github.com/llvm/llvm-project/pull/188425

From libcxx-commits at lists.llvm.org  Tue Jul  7 21:03:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 21:03:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] Fix redefinition of wchar_t
 overloads on AIX (PR #201432)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201432@github.com>
Message-ID: <6a4dcbfe.bd93e731.251b21.d6e4@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Himadhith

<details>
<summary>Changes</summary>

On AIX, the system headers have a circular dependency: `wctype.h → wchar.h → wctype.h` which causes wchar.h to be processed multiple times. 

The existing `_LIBCPP_WCHAR_H` include guard does not protect because wchar.h is a textual header and they get their own pre-processing per module compilation. 

---
Full diff: https://github.com/llvm/llvm-project/pull/201432.diff


2 Files Affected:

- (modified) libcxx/include/wchar.h (+3) 
- (modified) libcxx/test/extensions/clang/clang_modules_include.gen.py (-2) 


``````````diff
diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index a932dd266b862..31ff5490c9885 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -137,6 +137,8 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 
 #    if _LIBCPP_HAS_WIDE_CHARACTERS
 #      if defined(__cplusplus) && !defined(_LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS) && defined(_LIBCPP_PREFERRED_OVERLOAD)
+#        if defined(_AIX) && !defined(_LIBCPP_WCHAR_H_OVERLOADS)
+#          define _LIBCPP_WCHAR_H_OVERLOADS
 extern "C++" {
 inline _LIBCPP_HIDE_FROM_ABI wchar_t* __libcpp_wcschr(const wchar_t* __s, wchar_t __c) {
   return (wchar_t*)wcschr(__s, __c);
@@ -191,6 +193,7 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD wchar_t* wmemchr(wchar_t
   return __libcpp_wmemchr(__s, __c, __n);
 }
 }
+#        endif // _LIBCPP_WCHAR_H_OVERLOADS
 #      endif
 
 #      if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index 28661049d6e85..f0de60bacd7a7 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -30,8 +30,6 @@
 # TODO: Investigate why this doesn't work on Picolibc once the locale base API is refactored
 # UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
 
-# TODO: Fix seemingly circular inclusion or <wchar.h> on AIX
-# UNSUPPORTED: LIBCXX-AIX-FIXME
 
 # UNSUPPORTED: FROZEN-CXX03-HEADERS-FIXME
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/201432

From libcxx-commits at lists.llvm.org  Tue Jul  7 21:11:35 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 21:11:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] Fix redefinition of wchar_t
 overloads on AIX (PR #201432)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201432@github.com>
Message-ID: <6a4dcdf7.58bdd12c.19c81.e041@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/201432

>From ba9eb996768f01ef13bf3e6aa2a2b25b02eacc55 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 3 Jun 2026 14:48:19 -0400
Subject: [PATCH 1/2] [AIX][libc++] Fix redefinition of wchar_t overloads on
 AIX

---
 libcxx/include/wchar.h                                    | 6 ++++--
 libcxx/test/extensions/clang/clang_modules_include.gen.py | 2 --
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index 7ff03870bdfdb..0913c4eed937c 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -139,6 +139,7 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 
 #    if _LIBCPP_HAS_WIDE_CHARACTERS
 #      if defined(__cplusplus) && !defined(_LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS) && defined(_LIBCPP_PREFERRED_OVERLOAD)
+#        if defined(_AIX) && !defined(_LIBCPP_WCHAR_H_OVERLOADS)
 extern "C++" {
 inline _LIBCPP_HIDE_FROM_ABI wchar_t* __libcpp_wcschr(const wchar_t* __s, wchar_t __c) {
   return (wchar_t*)wcschr(__s, __c);
@@ -193,15 +194,16 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD wchar_t* wmemchr(wchar_t
   return __libcpp_wmemchr(__s, __c, __n);
 }
 }
-#      endif
+#        endif
 
-#      if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
+#        if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
 extern "C" {
 size_t mbsnrtowcs(
     wchar_t* __restrict __dst, const char** __restrict __src, size_t __nmc, size_t __len, mbstate_t* __restrict __ps);
 size_t wcsnrtombs(
     char* __restrict __dst, const wchar_t** __restrict __src, size_t __nwc, size_t __len, mbstate_t* __restrict __ps);
 } // extern "C"
+#        endif // _LIBCPP_WCHAR_H_OVERLOADS
 #      endif // __cplusplus && (_LIBCPP_MSVCRT || __MVS__)
 #    endif   // _LIBCPP_HAS_WIDE_CHARACTERS
 #  endif     // _LIBCPP_WCHAR_H
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..0ce7f73df1ee1 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -30,8 +30,6 @@
 # TODO: Investigate why this doesn't work on Picolibc once the locale base API is refactored
 # UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
 
-# TODO: Fix seemingly circular inclusion or <wchar.h> on AIX
-# UNSUPPORTED: LIBCXX-AIX-FIXME
 
 # UNSUPPORTED: FROZEN-CXX03-HEADERS-FIXME
 

>From 5e152f1b80f908008e4024d2bbf9fff018218dc0 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 4 Jun 2026 01:16:00 -0400
Subject: [PATCH 2/2] fix format and add define

---
 libcxx/include/wchar.h | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index 0913c4eed937c..6f15625cd2fb5 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -140,6 +140,7 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 #    if _LIBCPP_HAS_WIDE_CHARACTERS
 #      if defined(__cplusplus) && !defined(_LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS) && defined(_LIBCPP_PREFERRED_OVERLOAD)
 #        if defined(_AIX) && !defined(_LIBCPP_WCHAR_H_OVERLOADS)
+#          define _LIBCPP_WCHAR_H_OVERLOADS
 extern "C++" {
 inline _LIBCPP_HIDE_FROM_ABI wchar_t* __libcpp_wcschr(const wchar_t* __s, wchar_t __c) {
   return (wchar_t*)wcschr(__s, __c);
@@ -194,16 +195,16 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD wchar_t* wmemchr(wchar_t
   return __libcpp_wmemchr(__s, __c, __n);
 }
 }
-#        endif
+#        endif // _LIBCPP_WCHAR_H_OVERLOADS
+#      endif
 
-#        if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
+#      if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
 extern "C" {
 size_t mbsnrtowcs(
     wchar_t* __restrict __dst, const char** __restrict __src, size_t __nmc, size_t __len, mbstate_t* __restrict __ps);
 size_t wcsnrtombs(
     char* __restrict __dst, const wchar_t** __restrict __src, size_t __nwc, size_t __len, mbstate_t* __restrict __ps);
 } // extern "C"
-#        endif // _LIBCPP_WCHAR_H_OVERLOADS
 #      endif // __cplusplus && (_LIBCPP_MSVCRT || __MVS__)
 #    endif   // _LIBCPP_HAS_WIDE_CHARACTERS
 #  endif     // _LIBCPP_WCHAR_H


From libcxx-commits at lists.llvm.org  Tue Jul  7 22:09:53 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 22:09:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 1cefbac - [libc++] Replace the of use
 custom sections for detecting overriden functions (#175896)
Message-ID: <6a4ddba1.4a342cbc.ca844.cafd@mx.google.com>


Author: Petr Hosek
Date: 2026-07-07T22:09:42-07:00
New Revision: 1cefbacdaa0156880d8363b24ae04638a8fd31c8

URL: https://github.com/llvm/llvm-project/commit/1cefbacdaa0156880d8363b24ae04638a8fd31c8
DIFF: https://github.com/llvm/llvm-project/commit/1cefbacdaa0156880d8363b24ae04638a8fd31c8.diff

LOG: [libc++] Replace the of use custom sections for detecting overriden functions (#175896)

This is a follow up to #133876 and an alternative to #120805 which
doesn't rely on aliases and works across both ELF and Mach-O. This
mechanism is preferable in baremetal environments since it doesn't
require special handling of the custom sections.

Added: 
    

Modified: 
    libcxx/src/include/overridable_function.h

Removed: 
    


################################################################################
diff  --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index c8d9f30c74c3e..085662e446332 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -11,11 +11,6 @@
 #define _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H
 
 #include <__config>
-#include <cstdint>
-
-#if __has_feature(ptrauth_calls)
-#  include <ptrauth.h>
-#endif
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -42,18 +37,11 @@
 // -------------------
 //
 // Let's say we want to check whether a weak function `f` has been overridden by the user.
-// The general mechanism works by placing `f`'s definition (in the libc++ built library)
-// inside a special section, which we do using the `__section__` attribute via the
-// OVERRIDABLE_FUNCTION macro.
-//
-// Then, when comes the time to check whether the function has been overridden, we take
-// the address of the function and we check whether it falls inside the special function
-// we created. This can be done by finding pointers to the start and the end of the section
-// (which is done 
diff erently for ELF and Mach-O), and then checking whether `f` falls
-// within those bounds. If it falls within those bounds, then `f` is still inside the
-// special section and so it is the version we defined in the libc++ built library, i.e.
-// it was not overridden. Otherwise, it was overridden by the user because it falls
-// outside of the section.
+// The general mechanism works by defining a local symbol `ImplRef<f>::Impl` with the same
+// address as `f` as a constant expression using direct PC-relative materialization thus
+// pointing at the symbol defined in the same TU. At runtime, it compares the address of
+// `ImplRef<f>::Impl` with the address of `f` loaded from GOT: if `f` was overridden by
+// the user in another TU, the addresses will be 
diff erent.
 //
 // Important note
 // --------------
@@ -63,63 +51,40 @@
 // want to be defining special sections inside user's executables which use our headers.
 //
 
-#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
+#if defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
 
-#  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
+template <auto* _Func>
+struct ImplRef;
 
-_LIBCPP_BEGIN_NAMESPACE_STD template <typename T, T* _Func>
-_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-  // Declare two dummy bytes and give them these special `__asm` values. These values are
-  // defined by the linker, which means that referring to `&__lcxx_override_start` will
-  // effectively refer to the address where the section starts (and same for the end).
-  extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
-  extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
-
-  // Now get a uintptr_t out of these locations, and out of the function pointer.
-  uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
-  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
-  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
-
-#  if __has_feature(ptrauth_calls)
-  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
-  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
-  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
-  // stripped the function pointer. See rdar://122927845.
-  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
-#  endif
-
-  // Finally, the function was overridden if it falls outside of the section's bounds.
-  return __ptr < __start || __ptr > __end;
-}
-_LIBCPP_END_NAMESPACE_STD
-
-// The NVPTX linker cannot create '__start/__stop' sections.
-#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)
+// ImplRef<...>::Impl is expected to be defined elsewhere, so the compiler emits assembly
+// references to the mangled symbol with no definition. This template saves us the trouble
+// of providing manual declarations for overloads with some other local name for each
+// function name being overloaded (operator new, operator new[], etc.).
+template <typename _Ret, typename... _Args, _Ret (*_Func)(_Args...)>
+struct ImplRef<_Func> {
+  [[gnu::visibility("hidden")]] static _Ret Impl(_Args...);
+};
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
-
-// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
-// variables with those names corresponding to the start and the end of the section.
-//
-// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
-extern char __start___lcxx_override;
-extern char __stop___lcxx_override;
+#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
 
 _LIBCPP_BEGIN_NAMESPACE_STD
+// This takes a function type template argument first so that the second non-type template
+// argument (pointer to the public function) gets the benefit of type-aware overload
+// resolution, rather than having to use a static_cast.
 template <typename T, T* _Func>
 _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-  uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
-  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
-  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
-
-#  if __has_feature(ptrauth_calls)
-  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. See full explanation above.
-  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
+#  if !defined(_LIBCPP_CLANG_VER) || _LIBCPP_CLANG_VER >= 2101
+  __asm__("%cc0 = %cc1" : : "X"(ImplRef<_Func>::Impl), "X"(_Func));
+#  else
+  __asm__("%c0 = %c1" : : "X"(ImplRef<_Func>::Impl), "X"(_Func));
 #  endif
-
-  return __ptr < __start || __ptr > __end;
+  // This just has the compiler compare the two symbols. For PIC mode, this will do
+  // a direct PC-relative materialization for ImplRef<...>::Impl and a GOT load for
+  // the _Func symbol. The compiler thinks ImplRef<...>::Impl is defined elsewhere at
+  // link time and will be an undefined symbol. It doesn't know that the __asm__ tells
+  // the assembler to define it as a local symbol.
+  return _Func != ImplRef<_Func>::Impl;
 }
 _LIBCPP_END_NAMESPACE_STD
 


        

From libcxx-commits at lists.llvm.org  Tue Jul  7 22:10:03 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Tue, 07 Jul 2026 22:10:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Replace the of use custom
 sections for detecting overriden functions (PR #175896)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/175896@github.com>
Message-ID: <6a4ddbab.e55dc341.1f19bd.9cae@mx.google.com>


https://github.com/petrhosek closed https://github.com/llvm/llvm-project/pull/175896

From libcxx-commits at lists.llvm.org  Tue Jul  7 22:22:33 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 22:22:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG4472:
 std::atomic_ref<const T> can be constructed from temporaries (PR #208131)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208131@github.com>
Message-ID: <6a4dde99.ff037943.2e959a.6263@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/208131

From libcxx-commits at lists.llvm.org  Tue Jul  7 22:25:22 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 07 Jul 2026 22:25:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Backport P3029R1 Better
 `mdspan`'s CTAD to C++20/23 (PR #208132)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208132@github.com>
Message-ID: <6a4ddf42.0c05e68e.1e82f1.ff01@mx.google.com>


================
@@ -61,7 +61,7 @@
 "`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","","`#105423 <https://github.com/llvm/llvm-project/issues/105423>`__",""
 "`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","22","`#105424 <https://github.com/llvm/llvm-project/issues/105424>`__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
 "`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","`#105425 <https://github.com/llvm/llvm-project/issues/105425>`__",""
-"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__",""
+"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23. (MSVC STL and libstdc++ will do the same.)"
----------------
philnik777 wrote:

```suggestion
"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23."
```
I'm not sure this is particularly relevant for libc++. If we want to keep it, this shouldn't be worded in a way that we have to modify it once other implementations change.

https://github.com/llvm/llvm-project/pull/208132

From libcxx-commits at lists.llvm.org  Tue Jul  7 22:26:12 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 22:26:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a4ddf74.8b87e04f.184605.592f@mx.google.com>


inquisitivecrystal wrote:

I've addressed all the comments except the one I believe to be in error.

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Tue Jul  7 22:26:20 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Tue, 07 Jul 2026 22:26:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Replace the of use custom
 sections for detecting overriden functions (PR #175896)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/175896@github.com>
Message-ID: <6a4ddf7c.abb10101.3d0bf8.c119@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `sanitizer-aarch64-linux` running on `sanitizer-buildbot7` while building `libcxx` at step 2 "annotate".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/51/builds/39785

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 2 (annotate) failure: 'python ../sanitizer_buildbot/sanitizers/zorg/buildbot/builders/sanitizers/buildbot_selector.py' (failure)
...
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-aarch64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/msan/tests/./Msan-aarch64-with-call-Test': Command '['/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/msan/tests/./Msan-aarch64-with-call-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-aarch64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/msan/tests/./Msan-aarch64-Test': Command '['/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/msan/tests/./Msan-aarch64-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-aarch64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/unit/./TsanUnitTest-aarch64-Test': Command '['/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/unit/./TsanUnitTest-aarch64-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-aarch64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/rtl/./TsanRtlTest-aarch64-Test': Command '['/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/rtl/./TsanRtlTest-aarch64-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
-- Testing: 5879 tests, 72 workers --
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-aarch64-calls-Dynamic-Test/failed_to_discover_tests_from_gtest (1 of 5879)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-aarch64-calls-Dynamic-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-aarch64-calls-Test/failed_to_discover_tests_from_gtest (2 of 5879)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-aarch64-calls-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-aarch64-inline-Dynamic-Test/failed_to_discover_tests_from_gtest (3 of 5879)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-aarch64-inline-Dynamic-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-aarch64-inline-Test/failed_to_discover_tests_from_gtest (4 of 5879)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-aarch64-inline-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-aarch64-linux :: TestCases/Linux/long-object-path.cpp (110 of 5879)
******************** TEST 'AddressSanitizer-aarch64-linux :: TestCases/Linux/long-object-path.cpp' FAILED ********************
Exit Code: -6

Command Output (stdout):
--
# RUN: at line 1
mkdir -p /home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/asan/AARCH64LinuxConfig/TestCases/Linux/Output/long-object-path.cpp.tmp.dir/a-long-directory-name-to-test-allocations-for-exceptions-in-_dl_lookup_symbol_x-since-glibc-2.27
# executed command: mkdir -p /home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/asan/AARCH64LinuxConfig/TestCases/Linux/Output/long-object-path.cpp.tmp.dir/a-long-directory-name-to-test-allocations-for-exceptions-in-_dl_lookup_symbol_x-since-glibc-2.27
# note: command had no output on stdout or stderr
# RUN: at line 2
/home/b/sanitizer-aarch64-linux/build/build_default/./bin/clang  --driver-mode=g++ -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only   -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta   -g /home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/asan/TestCases/Linux/long-object-path.cpp -o /home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/asan/AARCH64LinuxConfig/TestCases/Linux/Output/long-object-path.cpp.tmp.dir/long-object-path
# executed command: /home/b/sanitizer-aarch64-linux/build/build_default/./bin/clang --driver-mode=g++ -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta -g /home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/asan/TestCases/Linux/long-object-path.cpp -o /home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/asan/AARCH64LinuxConfig/TestCases/Linux/Output/long-object-path.cpp.tmp.dir/long-object-path
# note: command had no output on stdout or stderr
# RUN: at line 3
/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/asan/AARCH64LinuxConfig/TestCases/Linux/Output/long-object-path.cpp.tmp.dir/a-*/../a-*/../a-*/../a-*/../a-*/../a-*/../a-*/../a-*/../long-object-path
# executed command: '/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/asan/AARCH64LinuxConfig/TestCases/Linux/Output/long-object-path.cpp.tmp.dir/a-*/../a-*/../a-*/../a-*/../a-*/../a-*/../a-*/../a-*/../long-object-path'
# .---command stderr------------
# | libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
Step 9 (test compiler-rt symbolizer) failure: test compiler-rt symbolizer (failure)
...
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-aarch64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/msan/tests/./Msan-aarch64-with-call-Test': Command '['/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/msan/tests/./Msan-aarch64-with-call-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-aarch64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/msan/tests/./Msan-aarch64-Test': Command '['/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/msan/tests/./Msan-aarch64-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-aarch64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/unit/./TsanUnitTest-aarch64-Test': Command '['/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/unit/./TsanUnitTest-aarch64-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-aarch64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/rtl/./TsanRtlTest-aarch64-Test': Command '['/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/rtl/./TsanRtlTest-aarch64-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
-- Testing: 5879 tests, 72 workers --
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-aarch64-calls-Dynamic-Test/failed_to_discover_tests_from_gtest (1 of 5879)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-aarch64-calls-Dynamic-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-aarch64-calls-Test/failed_to_discover_tests_from_gtest (2 of 5879)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-aarch64-calls-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-aarch64-inline-Dynamic-Test/failed_to_discover_tests_from_gtest (3 of 5879)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-aarch64-inline-Dynamic-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-aarch64-inline-Test/failed_to_discover_tests_from_gtest (4 of 5879)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-aarch64-inline-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-aarch64-linux :: TestCases/Linux/long-object-path.cpp (110 of 5879)
******************** TEST 'AddressSanitizer-aarch64-linux :: TestCases/Linux/long-object-path.cpp' FAILED ********************
Exit Code: -6

Command Output (stdout):
--
# RUN: at line 1
mkdir -p /home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/asan/AARCH64LinuxConfig/TestCases/Linux/Output/long-object-path.cpp.tmp.dir/a-long-directory-name-to-test-allocations-for-exceptions-in-_dl_lookup_symbol_x-since-glibc-2.27
# executed command: mkdir -p /home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/asan/AARCH64LinuxConfig/TestCases/Linux/Output/long-object-path.cpp.tmp.dir/a-long-directory-name-to-test-allocations-for-exceptions-in-_dl_lookup_symbol_x-since-glibc-2.27
# note: command had no output on stdout or stderr
# RUN: at line 2
/home/b/sanitizer-aarch64-linux/build/build_default/./bin/clang  --driver-mode=g++ -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only   -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta   -g /home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/asan/TestCases/Linux/long-object-path.cpp -o /home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/asan/AARCH64LinuxConfig/TestCases/Linux/Output/long-object-path.cpp.tmp.dir/long-object-path
# executed command: /home/b/sanitizer-aarch64-linux/build/build_default/./bin/clang --driver-mode=g++ -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta -g /home/b/sanitizer-aarch64-linux/build/llvm-project/compiler-rt/test/asan/TestCases/Linux/long-object-path.cpp -o /home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/asan/AARCH64LinuxConfig/TestCases/Linux/Output/long-object-path.cpp.tmp.dir/long-object-path
# note: command had no output on stdout or stderr
# RUN: at line 3
/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/asan/AARCH64LinuxConfig/TestCases/Linux/Output/long-object-path.cpp.tmp.dir/a-*/../a-*/../a-*/../a-*/../a-*/../a-*/../a-*/../a-*/../long-object-path
# executed command: '/home/b/sanitizer-aarch64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/asan/AARCH64LinuxConfig/TestCases/Linux/Output/long-object-path.cpp.tmp.dir/a-*/../a-*/../a-*/../a-*/../a-*/../a-*/../a-*/../a-*/../long-object-path'
# .---command stderr------------
# | libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.

```

</details>

https://github.com/llvm/llvm-project/pull/175896

From libcxx-commits at lists.llvm.org  Tue Jul  7 22:35:22 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 22:35:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4de19a.0c05e68e.1e82f1.0b6d@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/206498

>From eac8181d96f0ed0ff110d7cf7602551199913a6b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 18:53:17 +0530
Subject: [PATCH 1/8] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. Omit the noreplace mode strings in
 __make_mdstring() on AIX, causing open() to return nullptr for these modes.
 Update the test to expect failure on AIX for all noreplace cases, since the
 platform cannot provide atomic exclusive-open semantics.

---
 libcxx/include/fstream                        | 10 ++++++++
 .../filebuf.members/open_pointer.pass.cpp     | 24 +++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..f979db89db794 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -664,6 +664,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -674,6 +683,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..f7ab0239a1b18 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -75,7 +73,10 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -83,7 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 
@@ -94,7 +101,10 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -102,7 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 #  endif

>From 9c8dfa79291faa95c5a036e5d6b2ac43d199aee1 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 12:40:23 -0400
Subject: [PATCH 2/8] fix format and add warning

---
 libcxx/include/fstream                               |  3 +++
 .../fstreams/filebuf.members/open_pointer.pass.cpp   | 12 ++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index f979db89db794..b71baabff2524 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -672,6 +672,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if defined(_AIX) || defined(__TOS_AIX__)
+#        warning 'exclusive mode is unsupported on this target'
+#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index f7ab0239a1b18..627a65cac3f2f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -84,13 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#  else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#  endif
           }
         }
 
@@ -112,13 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#    else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#    endif
           }
         }
 #  endif

>From d324a675f05f851656c4fb10ac6eaf5120ebb08e Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 12:52:06 -0400
Subject: [PATCH 3/8] fix warnings treated as errors

---
 libcxx/include/fstream | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b71baabff2524..91259a39f4000 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -673,7 +673,10 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
 #      if defined(_AIX) || defined(__TOS_AIX__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic warning "-Wcpp"
 #        warning 'exclusive mode is unsupported on this target'
+#        pragma GCC diagnostic pop
 #      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:

>From 450f6694bddb62f12cc9a666b3c1c300a90f725c Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 13:49:31 -0400
Subject: [PATCH 4/8] add warning as fprintf

---
 libcxx/include/fstream | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 91259a39f4000..92baa4670b363 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -672,12 +672,6 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if defined(_AIX) || defined(__TOS_AIX__)
-#        pragma GCC diagnostic push
-#        pragma GCC diagnostic warning "-Wcpp"
-#        warning 'exclusive mode is unsupported on this target'
-#        pragma GCC diagnostic pop
-#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -780,9 +774,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr)
+  if (!__mdstr){
+#     if defined(_AIX) || defined(__TOS_AIX__)
+    if (__mode & ios_base::noreplace)
+      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
+#     endif
     return nullptr;
-
+}
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From eb0c8a9711dd9ed86e21ec217adb49f8cc7e3ec3 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:30:10 +0530
Subject: [PATCH 5/8] fix formatting

---
 libcxx/include/fstream | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 92baa4670b363..216082cfb8283 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -774,13 +774,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr){
-#     if defined(_AIX) || defined(__TOS_AIX__)
+  if (!__mdstr) {
+#      if defined(_AIX) || defined(__TOS_AIX__)
     if (__mode & ios_base::noreplace)
       fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#     endif
+#      endif
     return nullptr;
-}
+  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From 9f7f1e7bc05bf06350f2846f8f736366133b1b8b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:37:48 +0530
Subject: [PATCH 6/8] add the remaining test files

---
 .../fstreams/fstream.cons/pointer.pass.cpp    | 24 +++++++++++++++---
 .../fstream.members/open_pointer.pass.cpp     | 24 +++++++++++++++---
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 24 +++++++++++++++---
 .../ofstream.members/open_pointer.pass.cpp    | 25 ++++++++++++++++---
 4 files changed, 81 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..a677b16e76479 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
@@ -91,14 +89,23 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -108,14 +115,23 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..92c0cf1aa0c00 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -74,7 +72,10 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -82,7 +83,13 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -93,7 +100,10 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -101,7 +111,13 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..4fae51f38adbd 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
@@ -145,14 +143,23 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -162,14 +169,23 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..493af1bda9c98 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -82,7 +80,10 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -90,7 +91,13 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -101,14 +108,24 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
+
           {
             std::remove(tmp.c_str());
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif

>From 150ffaacc7f92197986a67f317374c030a7e78cf Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 6 Jul 2026 12:15:37 +0530
Subject: [PATCH 7/8] review changes

---
 libcxx/include/fstream                        | 21 +++++++++++--------
 .../filebuf.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/fstream.cons/pointer.pass.cpp    | 18 ++++------------
 .../fstream.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 18 ++++------------
 .../ofstream.members/open_pointer.pass.cpp    | 18 ++++------------
 6 files changed, 32 insertions(+), 79 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 216082cfb8283..f37522db70b1d 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -258,6 +258,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -671,8 +673,8 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // existence of the file and create it, it shall fail rather than perform a
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
-// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if !defined(_AIX) && !defined(__TOS_AIX__)
+// _AIX is defined on AIX by all supported compilers.
+#      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -683,7 +685,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
-#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
+#      endif // !defined(_AIX)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
@@ -774,13 +776,8 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr) {
-#      if defined(_AIX) || defined(__TOS_AIX__)
-    if (__mode & ios_base::noreplace)
-      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#      endif
+  if (!__mdstr)
     return nullptr;
-  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1222,6 +1219,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1380,6 +1379,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1544,6 +1545,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 627a65cac3f2f..37cfd985d1c11 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -73,10 +73,7 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -84,9 +81,7 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -101,10 +96,7 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -112,9 +104,7 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index a677b16e76479..2a297ca87903e 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -89,19 +89,14 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -115,19 +110,14 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 92c0cf1aa0c00..9178788b60354 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -72,10 +72,7 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -83,9 +80,7 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -100,10 +95,7 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -111,9 +103,7 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index 4fae51f38adbd..31a944236d1d0 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -143,19 +143,14 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -169,19 +164,14 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 493af1bda9c98..0b77f012b345d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -80,10 +80,7 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -91,9 +88,7 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -108,10 +103,7 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -119,9 +111,7 @@ int main(int, char**)
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist

>From 6102db9d9b3706a12567951f77bf2546007a65a0 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 8 Jul 2026 11:03:55 +0530
Subject: [PATCH 8/8] add warnings for char* overloads

---
 libcxx/include/fstream                        | 43 +++++++++++++------
 .../filebuf.members/open_pointer.pass.cpp     |  2 +
 .../fstreams/fstream.cons/pointer.pass.cpp    |  2 +
 .../fstream.members/open_pointer.pass.cpp     |  2 +
 .../fstreams/ofstream.cons/pointer.pass.cpp   |  2 +
 .../ofstream.members/open_pointer.pass.cpp    |  3 +-
 6 files changed, 41 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index f37522db70b1d..94c55ea794d22 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -256,10 +256,16 @@ public:
 
   // 27.9.1.4 Members:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode & ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+#      if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode & ios_base::noreplace,
                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -666,14 +672,8 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
-// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
-// cases are omitted on AIX and fall through to default (return nullptr), signalling
-// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
-// by proxy, requires that if the implementation cannot atomically check for the
-// existence of the file and create it, it shall fail rather than perform a
-// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
-// semantics via fopen(), returning nullptr is the correct behaviour.
-// _AIX is defined on AIX by all supported compilers.
+// AIX fopen() does not support the 'x' (noreplace) mode suffix; these cases fall through to
+// default (return nullptr), which is conformant per C23 7.21.5.3p5 (fail if atomic check-and-create is unsupported).
 #      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -778,6 +778,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   const wchar_t* __mdstr = __make_mdwstring(__mode);
   if (!__mdstr)
     return nullptr;
+
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1217,10 +1218,16 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode & ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+#      if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode & ios_base::noreplace,
                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1377,10 +1384,16 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode & ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+#      if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode & ios_base::noreplace,
                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1543,10 +1556,16 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode & ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+#      if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode & ios_base::noreplace,
                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 37cfd985d1c11..9fdd47e8a837d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -80,6 +80,7 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::filebuf f;
+            // expected-warning at +1 {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
             f.open(tmp.c_str(), mode);
 #  if defined(_AIX)
             assert(!f.is_open());
@@ -103,6 +104,7 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::wfilebuf f;
+            // expected-warning at +1 {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
             f.open(tmp.c_str(), mode);
 #    if defined(_AIX)
             assert(!f.is_open());
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2a297ca87903e..84bc54b80ba60 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -95,6 +95,7 @@ int main(int, char**)
           {
             std::remove(tmp.c_str());
 
+            // expected-warning at +1 {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
             std::fstream f(tmp.c_str(), mode);
 #  if defined(_AIX)
             assert(!f.is_open());
@@ -116,6 +117,7 @@ int main(int, char**)
           {
             std::remove(tmp.c_str());
 
+            // expected-warning at +1 {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
             std::wfstream f(tmp.c_str(), mode);
 #    if defined(_AIX)
             assert(!f.is_open());
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 9178788b60354..a191c3588801f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -79,6 +79,7 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::fstream f;
+            // expected-warning at +1 {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
             f.open(tmp.c_str(), mode);
 #  if defined(_AIX)
             assert(!f.is_open());
@@ -102,6 +103,7 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::wfstream f;
+            // expected-warning at +1 {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
             f.open(tmp.c_str(), mode);
 #    if defined(_AIX)
             assert(!f.is_open());
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index 31a944236d1d0..e14efd444a52d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -149,6 +149,7 @@ int main(int, char**)
           {
             std::remove(tmp.c_str());
 
+            // expected-warning at +1 {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
             std::ofstream f(tmp.c_str(), mode);
 #  if defined(_AIX)
             assert(!f.is_open());
@@ -170,6 +171,7 @@ int main(int, char**)
           {
             std::remove(tmp.c_str());
 
+            // expected-warning at +1 {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
             std::wofstream f(tmp.c_str(), mode);
 #    if defined(_AIX)
             assert(!f.is_open());
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 0b77f012b345d..978d0be2d037d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -87,6 +87,7 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::ofstream f;
+            // expected-warning at +1 {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
             f.open(tmp.c_str(), mode);
 #  if defined(_AIX)
             assert(!f.is_open());
@@ -105,11 +106,11 @@ int main(int, char**)
             f.open(tmp.c_str(), mode);
             assert(!f.is_open()); // since it already exists
           }
-
           {
             std::remove(tmp.c_str());
 
             std::wofstream f;
+            // expected-warning at +1 {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
             f.open(tmp.c_str(), mode);
 #    if defined(_AIX)
             assert(!f.is_open());


From libcxx-commits at lists.llvm.org  Tue Jul  7 22:39:18 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 22:39:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Backport P3029R1 Better
 `mdspan`'s CTAD to C++20/23 (PR #208132)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208132@github.com>
Message-ID: <6a4de286.c56fb102.7c9cd.48b4@mx.google.com>


================
@@ -61,7 +61,7 @@
 "`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","","`#105423 <https://github.com/llvm/llvm-project/issues/105423>`__",""
 "`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","22","`#105424 <https://github.com/llvm/llvm-project/issues/105424>`__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
 "`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","`#105425 <https://github.com/llvm/llvm-project/issues/105425>`__",""
-"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__",""
+"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23. (MSVC STL and libstdc++ will do the same.)"
----------------
frederick-vs-ja wrote:

I'd like to drop this part.

But there're other occurrences of "MSVC STL and libstdc++ will do the same" in the documentations and MSVC STL and libstdc++ has been doing so already. Perhaps we should clean them up in a future PR.

https://github.com/llvm/llvm-project/pull/208132

From libcxx-commits at lists.llvm.org  Tue Jul  7 22:42:36 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 22:42:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Backport P3029R1 Better
 `mdspan`'s CTAD to C++20/23 (PR #208132)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208132@github.com>
Message-ID: <6a4de34c.eb24c6ea.27aedc.3c2f@mx.google.com>


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/208132

>From 90b9f5ada6e9997bb10d70c3d051796134431fd1 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 8 Jul 2026 10:14:16 +0800
Subject: [PATCH 1/2] [libc++] Backport P3029R1 Better `mdspan`'s CTAD to
 C++20/23

Also backport
- support for `T::value` of a reference type in `integral-constant-like`
from P2781R9 `std::constant_wrapper`, and
- LWG4351 _`integral-constant-like`_ needs more `remove_cvref_t`.

P3029R1 (and its further fixes) should be treated as a Defect Report
against C++20/23 to avoid behavior difference in different modes. Other
implementations will do or are already doing the same.
---
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 libcxx/include/__mdspan/extents.h             |  6 -----
 libcxx/include/__mdspan/mdspan.h              |  7 -----
 libcxx/include/mdspan                         |  6 +----
 libcxx/include/span                           | 15 +++--------
 .../views/mdspan/extents/ctad.pass.cpp        | 13 ++++++---
 .../views/mdspan/mdspan/deduction.pass.cpp    | 19 ++++++++-----
 .../views.span/span.cons/deduct.pass.cpp      | 27 ++++++++++++++-----
 8 files changed, 48 insertions(+), 47 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..e1fcaf907c95b 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -61,7 +61,7 @@
 "`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","","`#105423 <https://github.com/llvm/llvm-project/issues/105423>`__",""
 "`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","22","`#105424 <https://github.com/llvm/llvm-project/issues/105424>`__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
 "`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","`#105425 <https://github.com/llvm/llvm-project/issues/105425>`__",""
-"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__",""
+"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23. (MSVC STL and libstdc++ will do the same.)"
 "","","","","","",""
 "`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","`#105427 <https://github.com/llvm/llvm-project/issues/105427>`__",""
 "`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","`#105428 <https://github.com/llvm/llvm-project/issues/105428>`__","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
diff --git a/libcxx/include/__mdspan/extents.h b/libcxx/include/__mdspan/extents.h
index bc4490aa5b470..99366b9ca9cc4 100644
--- a/libcxx/include/__mdspan/extents.h
+++ b/libcxx/include/__mdspan/extents.h
@@ -466,15 +466,9 @@ using dims = dextents<_IndexType, _Rank>;
 #  endif
 
 // Deduction guide for extents
-#  if _LIBCPP_STD_VER >= 26
 template <class... _IndexTypes>
   requires(is_convertible_v<_IndexTypes, size_t> && ...)
 explicit extents(_IndexTypes...) -> extents<size_t, __maybe_static_ext<_IndexTypes>...>;
-#  else
-template <class... _IndexTypes>
-  requires(is_convertible_v<_IndexTypes, size_t> && ...)
-explicit extents(_IndexTypes...) -> extents<size_t, size_t(((void)sizeof(_IndexTypes), dynamic_extent))...>;
-#  endif
 
 namespace __mdspan_detail {
 
diff --git a/libcxx/include/__mdspan/mdspan.h b/libcxx/include/__mdspan/mdspan.h
index bcfa302231cd4..3b78f792770f0 100644
--- a/libcxx/include/__mdspan/mdspan.h
+++ b/libcxx/include/__mdspan/mdspan.h
@@ -312,17 +312,10 @@ class mdspan {
 #  endif
 };
 
-#  if _LIBCPP_STD_VER >= 26
 template <class _ElementType, class... _OtherIndexTypes>
   requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
 explicit mdspan(_ElementType*, _OtherIndexTypes...)
     -> mdspan<_ElementType, extents<size_t, __maybe_static_ext<_OtherIndexTypes>...>>;
-#  else
-template <class _ElementType, class... _OtherIndexTypes>
-  requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
-explicit mdspan(_ElementType*, _OtherIndexTypes...)
-    -> mdspan<_ElementType, dextents<size_t, sizeof...(_OtherIndexTypes)>>;
-#  endif
 
 template <class _Pointer>
   requires(is_pointer_v<remove_reference_t<_Pointer>>)
diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan
index 2f0e1f9b87751..6313c03f041af 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -411,14 +411,10 @@ namespace std {
     mdspan(Pointer&&)
       -> mdspan<remove_pointer_t<remove_reference_t<Pointer>>, extents<size_t>>;
 
-  template<class ElementType, class... Integrals>
-    requires ((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
-    explicit mdspan(ElementType*, Integrals...)
-      -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;            // until C++26
   template<class ElementType, class... Integrals>
     requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
     explicit mdspan(ElementType*, Integrals...)
-      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;  // since C++26
+      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;
 
   template<class ElementType, class OtherIndexType, size_t N>
     mdspan(ElementType*, span<OtherIndexType, N>)
diff --git a/libcxx/include/span b/libcxx/include/span
index e39b811f9b1ea..1c52983f808ca 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -19,16 +19,16 @@ namespace std {
 inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();
 
 template<class T>
-  concept integral-constant-like =                           // exposition only, since C++26
-    is_integral_v<decltype(T::value)> &&
-    !is_same_v<bool, remove_const_t<decltype(T::value)>> &&
+  concept integral-constant-like =                           // exposition only
+    is_integral_v<remove_cvref_t<decltype(T::value)>> &&
+    !is_same_v<bool, remove_cvref_t<decltype(T::value)>> &&
     convertible_to<T, decltype(T::value)> &&
     equality_comparable_with<T, decltype(T::value)> &&
     bool_constant<T() == T::value>::value &&
     bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value;
 
 template<class T>
-  constexpr size_t maybe-static-ext = dynamic_extent;        // exposition only, since C++26
+  constexpr size_t maybe-static-ext = dynamic_extent;        // exposition only
 template<integral-constant-like T>
   constexpr size_t maybe-static-ext<T> = {T::value};
 
@@ -122,8 +122,6 @@ private:
     size_type size_;  // exposition only
 };
 
-template<class It, class EndOrSize>
-    span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;                             // until C++26
 template<class It, class EndOrSize>
     span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>; // since C++26
 
@@ -597,7 +595,6 @@ template <class _Tp, size_t _Extent>
   return __s.__as_writable_bytes();
 }
 
-#    if _LIBCPP_STD_VER >= 26
 template <class _Tp>
 concept __integral_constant_like =
     is_integral_v<remove_cvref_t<decltype(_Tp::value)>> && !is_same_v<bool, remove_cvref_t<decltype(_Tp::value)>> &&
@@ -613,10 +610,6 @@ inline constexpr size_t __maybe_static_ext<_Tp> = {_Tp::value};
 
 template <contiguous_iterator _It, class _EndOrSize>
 span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>, __maybe_static_ext<_EndOrSize>>;
-#    else
-template <contiguous_iterator _It, class _EndOrSize>
-span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;
-#    endif
 
 template <class _Tp, size_t _Sz>
 span(_Tp (&)[_Sz]) -> span<_Tp, _Sz>;
diff --git a/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
index b72c3e050b1f9..e34113f91ad06 100644
--- a/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
@@ -13,8 +13,7 @@
 // explicit extents(Integrals...) -> see below;
 //   Constraints: (is_convertible_v<Integrals, size_t> && ...) is true.
 //
-// Remarks: The deduced type is dextents<size_t, sizeof...(Integrals)>.           // until C++26
-// Remarks: The deduced type is extents<size_t, maybe-static-ext<Integrals>...>.  // since C++26
+// Remarks: The deduced type is extents<size_t, maybe-static-ext<Integrals>...>.
 
 #include <cassert>
 #include <cstddef>
@@ -31,6 +30,9 @@ struct NoDefaultCtorIndex {
   constexpr operator size_t() const noexcept { return value; }
 };
 
+template <std::size_t N>
+using size_ref_constant = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, N>::value>;
+
 template <class E, class Expected>
 constexpr void test(E e, Expected expected) {
   ASSERT_SAME_TYPE(E, Expected);
@@ -47,13 +49,16 @@ constexpr bool test() {
        std::extents<std::size_t, D, D, D, D, D, D, D, D, D>(1, 2u, 3, 4, 5, 6, 7, 8, 9));
   test(std::extents(NoDefaultCtorIndex{1}, NoDefaultCtorIndex{2}), std::extents<std::size_t, D, D>(1, 2));
 
-#if _LIBCPP_STD_VER >= 26
   // P3029R1: deduction from `integral_constant`
   test(std::extents(std::integral_constant<size_t, 5>{}), std::extents<std::size_t, 5>());
   test(std::extents(std::integral_constant<size_t, 5>{}, 6), std::extents<std::size_t, 5, std::dynamic_extent>(6));
   test(std::extents(std::integral_constant<size_t, 5>{}, 6, std::integral_constant<size_t, 7>{}),
        std::extents<std::size_t, 5, std::dynamic_extent, 7>(6));
-#endif
+  // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
+  test(std::extents(size_ref_constant<5>{}), std::extents<std::size_t, 5>());
+  test(std::extents(size_ref_constant<5>{}, 6), std::extents<std::size_t, 5, std::dynamic_extent>(6));
+  test(std::extents(std::integral_constant<size_t, 5>{}, 6, size_ref_constant<7>{}),
+       std::extents<std::size_t, 5, std::dynamic_extent, 7>(6));
 
   return true;
 }
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
index 762841016e29e..6c12ccb7fd86a 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
@@ -22,11 +22,7 @@
 //  template<class ElementType, class... Integrals>
 //    requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
 //    explicit mdspan(ElementType*, Integrals...)
-//      -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;            // until C++26
-//  template<class ElementType, class... Integrals>
-//    requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
-//    explicit mdspan(ElementType*, Integrals...)
-//      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;  // since C++26
+//      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;
 //
 //  template<class ElementType, class OtherIndexType, size_t N>
 //    mdspan(ElementType*, span<OtherIndexType, N>)
@@ -53,6 +49,7 @@
 
 #include <mdspan>
 #include <cassert>
+#include <cstddef>
 #include <concepts>
 #include <span> // dynamic_extent
 #include <type_traits>
@@ -98,6 +95,9 @@ struct SizeTIntType {
   constexpr operator size_t() const noexcept { return size_t(val); }
 };
 
+template <std::size_t N>
+using size_ref_constant = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, N>::value>;
+
 template <class H, class A>
   requires(sizeof(decltype(std::mdspan(std::declval<H>(), 10))) > 0)
 constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
@@ -107,7 +107,6 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
   // deduction from pointer and integral like
   ASSERT_SAME_TYPE(decltype(std::mdspan(handle, 5, SizeTIntType(6))), std::mdspan<T, std::dextents<size_t, 2>>);
 
-#if _LIBCPP_STD_VER >= 26
   // P3029R1: deduction from `integral_constant`
   ASSERT_SAME_TYPE(
       decltype(std::mdspan(handle, std::integral_constant<size_t, 5>{})), std::mdspan<T, std::extents<size_t, 5>>);
@@ -117,7 +116,13 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
       decltype(std::mdspan(
           handle, std::integral_constant<size_t, 5>{}, std::dynamic_extent, std::integral_constant<size_t, 7>{})),
       std::mdspan<T, std::extents<size_t, 5, std::dynamic_extent, 7>>);
-#endif
+  // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle, size_ref_constant<5>{})), std::mdspan<T, std::extents<std::size_t, 5>>);
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle, size_ref_constant<5>{}, std::dynamic_extent)),
+                   std::mdspan<T, std::extents<std::size_t, 5, std::dynamic_extent>>);
+  ASSERT_SAME_TYPE(decltype(std::mdspan(
+                       handle, std::integral_constant<std::size_t, 5>{}, std::dynamic_extent, size_ref_constant<7>{})),
+                   std::mdspan<T, std::extents<std::size_t, 5, std::dynamic_extent, 7>>);
 
   std::array<char, 3> exts;
   // deduction from pointer and array
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index 9e41afad2258e..35d9b36f824aa 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -11,10 +11,7 @@
 // <span>
 
 // template<class It, class EndOrSize>
-//   span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>>; // until C++26
-// template<class It, class EndOrSize>
-//   span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>,
-//                               maybe-static-ext<EndOrSize>>; // since C++26
+//   span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>;
 // template<class T, size_t N>
 //   span(T (&)[N]) -> span<T, N>;
 // template<class T, size_t N>
@@ -27,6 +24,7 @@
 #include <span>
 #include <array>
 #include <cassert>
+#include <cstddef>
 #include <iterator>
 #include <memory>
 #include <string>
@@ -50,7 +48,6 @@ void test_iterator_sentinel() {
     assert(s.data() == std::data(arr));
   }
 
-#if TEST_STD_VER >= 26
   // P3029R1: deduction from `integral_constant`
   {
     std::span s{std::begin(arr), std::integral_constant<size_t, 3>{}};
@@ -58,15 +55,33 @@ void test_iterator_sentinel() {
     assert(s.size() == 3);
     assert(s.data() == std::data(arr));
   }
-
+  // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
+  {
+    using size_3_ref_type = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, 3>::value>;
+    std::span s{std::begin(arr), size_3_ref_type{}};
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+    assert(s.size() == 3);
+    assert(s.data() == std::data(arr));
+  }
+#if TEST_STD_VER >= 26
   {
     std::span s{std::begin(arr), std::cw<3>};
     ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
     assert(s.size() == 3);
     assert(s.data() == std::data(arr));
   }
+#endif
   {
     // LWG4351 integral-constant-like needs more remove_cvref_t
+    using true_ref_type = std::integral_constant<const bool&, std::true_type::value>;
+    LIBCPP_STATIC_ASSERT(!std::__integral_constant_like<true_ref_type>);
+    std::span s(std::begin(arr), true_ref_type{});
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, std::dynamic_extent>);
+    assert(s.size() == 1);
+    assert(s.data() == std::data(arr));
+  }
+#if TEST_STD_VER >= 26
+  {
     LIBCPP_STATIC_ASSERT(!std::__integral_constant_like<decltype(std::cw<true>)>);
     std::span s(std::begin(arr), std::cw<true>);
     ASSERT_SAME_TYPE(decltype(s), std::span<int, std::dynamic_extent>);

>From 5108cca329c7c1374047312f970ffdf1aa72431b Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 8 Jul 2026 13:41:37 +0800
Subject: [PATCH 2/2] Remove one `// since C++26`; avoid mentioning other
 implementations

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 libcxx/include/span                | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index e1fcaf907c95b..2522dd98e4b9c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -61,7 +61,7 @@
 "`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","","`#105423 <https://github.com/llvm/llvm-project/issues/105423>`__",""
 "`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","22","`#105424 <https://github.com/llvm/llvm-project/issues/105424>`__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
 "`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","`#105425 <https://github.com/llvm/llvm-project/issues/105425>`__",""
-"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23. (MSVC STL and libstdc++ will do the same.)"
+"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23."
 "","","","","","",""
 "`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","`#105427 <https://github.com/llvm/llvm-project/issues/105427>`__",""
 "`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","`#105428 <https://github.com/llvm/llvm-project/issues/105428>`__","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
diff --git a/libcxx/include/span b/libcxx/include/span
index 1c52983f808ca..b05a953829b5a 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -123,7 +123,7 @@ private:
 };
 
 template<class It, class EndOrSize>
-    span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>; // since C++26
+    span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>;
 
 template<class T, size_t N>
     span(T (&)[N]) -> span<T, N>;


From libcxx-commits at lists.llvm.org  Tue Jul  7 22:48:29 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Tue, 07 Jul 2026 22:48:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Replace the of use custom
 sections for detecting overriden functions (PR #175896)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/175896@github.com>
Message-ID: <6a4de4ad.cb30c3e4.3ce559.2567@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `sanitizer-x86_64-linux` running on `sanitizer-buildbot1` while building `libcxx` at step 2 "annotate".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/66/builds/33420

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 2 (annotate) failure: 'python ../sanitizer_buildbot/sanitizers/zorg/buildbot/builders/sanitizers/buildbot_selector.py' (failure)
...
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-x86_64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/msan/tests/./Msan-x86_64-with-call-Test': Command '['/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/msan/tests/./Msan-x86_64-with-call-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-x86_64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/rtl/./TsanRtlTest-x86_64-Test': Command '['/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/rtl/./TsanRtlTest-x86_64-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-x86_64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/unit/./TsanUnitTest-x86_64-Test': Command '['/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/unit/./TsanUnitTest-x86_64-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-x86_64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/nsan/tests/./Nsan-x86_64-Test': Command '['/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/nsan/tests/./Nsan-x86_64-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
-- Testing: 10645 tests, 64 workers --
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-i386-calls-Dynamic-Test/failed_to_discover_tests_from_gtest (1 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-i386-calls-Dynamic-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-i386-calls-Test/failed_to_discover_tests_from_gtest (2 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-i386-calls-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-i386-inline-Dynamic-Test/failed_to_discover_tests_from_gtest (3 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-i386-inline-Dynamic-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-i386-inline-Test/failed_to_discover_tests_from_gtest (4 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-i386-inline-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-x86_64-calls-Dynamic-Test/failed_to_discover_tests_from_gtest (5 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-x86_64-calls-Dynamic-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-x86_64-calls-Test/failed_to_discover_tests_from_gtest (24 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-x86_64-calls-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-x86_64-inline-Dynamic-Test/failed_to_discover_tests_from_gtest (25 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-x86_64-inline-Dynamic-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-x86_64-inline-Test/failed_to_discover_tests_from_gtest (29 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-x86_64-inline-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Step 9 (test compiler-rt symbolizer) failure: test compiler-rt symbolizer (failure)
...
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-x86_64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/msan/tests/./Msan-x86_64-with-call-Test': Command '['/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/msan/tests/./Msan-x86_64-with-call-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-x86_64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/rtl/./TsanRtlTest-x86_64-Test': Command '['/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/rtl/./TsanRtlTest-x86_64-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-x86_64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/unit/./TsanUnitTest-x86_64-Test': Command '['/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/tsan/tests/unit/./TsanUnitTest-x86_64-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
libc++abi: libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because `operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you override `operator new(size_t, align_val_t, nothrow_t)` as well.
llvm-lit: /home/b/sanitizer-x86_64-linux/build/llvm-project/llvm/utils/lit/lit/formats/googletest.py:38: warning: unable to discover google-tests in '/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/nsan/tests/./Nsan-x86_64-Test': Command '['/home/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/lib/nsan/tests/./Nsan-x86_64-Test', '--gtest_list_tests', '--gtest_filter=-*DISABLED_*']' died with <Signals.SIGABRT: 6>.. Process output: b''
-- Testing: 10645 tests, 64 workers --
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-i386-calls-Dynamic-Test/failed_to_discover_tests_from_gtest (1 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-i386-calls-Dynamic-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-i386-calls-Test/failed_to_discover_tests_from_gtest (2 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-i386-calls-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-i386-inline-Dynamic-Test/failed_to_discover_tests_from_gtest (3 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-i386-inline-Dynamic-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-i386-inline-Test/failed_to_discover_tests_from_gtest (4 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-i386-inline-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-x86_64-calls-Dynamic-Test/failed_to_discover_tests_from_gtest (5 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-x86_64-calls-Dynamic-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-x86_64-calls-Test/failed_to_discover_tests_from_gtest (24 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-x86_64-calls-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-x86_64-inline-Dynamic-Test/failed_to_discover_tests_from_gtest (25 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-x86_64-inline-Dynamic-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************
Testing: 
FAIL: AddressSanitizer-Unit :: ./Asan-x86_64-inline-Test/failed_to_discover_tests_from_gtest (29 of 10645)
******************** TEST 'AddressSanitizer-Unit :: ./Asan-x86_64-inline-Test/failed_to_discover_tests_from_gtest' FAILED ********************

********************

```

</details>

https://github.com/llvm/llvm-project/pull/175896

From libcxx-commits at lists.llvm.org  Tue Jul  7 22:55:27 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 07 Jul 2026 22:55:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG4472:
 std::atomic_ref<const T> can be constructed from temporaries (PR #208131)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208131@github.com>
Message-ID: <6a4de64f.bf38ae28.39239a.4835@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/208131

From libcxx-commits at lists.llvm.org  Tue Jul  7 22:58:35 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Yordan_V=C3=A1squez?= via libcxx-commits)
Date: Tue, 07 Jul 2026 22:58:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3133: Modernizing
 numeric type requirements (PR #208145)
Message-ID: <llvm.org/llvm/llvm-project/pull/208145@github.com>


https://github.com/vyordan created https://github.com/llvm/llvm-project/pull/208145

This implements the resolution of LWG3133, which modernizes the requirements on `T` for `std::complex<T>` and `std::valarray<T>` in [numeric.requirements].

- Adds a `static_assert` to `std::complex<T>` requiring `T` to be a cv-unqualified object type, following the same pattern already used by `std::optional<T>`.
- No code changes were needed for `std::valarray<T>`: its `operator[]` already indexes through a raw pointer and never relies on a user-overloadable `operator&`, so it already conforms to the revised wording.
- Added a `.verify.cpp` test covering `complex<const T>` and `complex<volatile T>`.
- Ran the full `complex.number` test suite locally (97/97 passing) to confirm no regressions.

Fixes #100254

>From 16f173d3dbc1840d52e09121dff1df14297e71d1 Mon Sep 17 00:00:00 2001
From: vyordan <vyordangiovani at gmail.com>
Date: Tue, 7 Jul 2026 23:55:36 -0600
Subject: [PATCH] [libc++] Implement LWG3133: Modernizing numeric type
 requirements

Add a static_assert to std::complex<T> requiring that T be a
cv-unqualified object type, matching the revised wording in
[numeric.requirements]. This mirrors the existing pattern used by
std::optional<T>.

No implementation changes were needed for std::valarray<T>, since
its operator[] already indexes safely without depending on a
user-overloadable operator&.

Fixes #100254
---
 libcxx/docs/Status/Cxx20Issues.csv            |  2 +-
 libcxx/include/complex                        |  6 ++++
 ..._requires_cv_unqualified_object.verify.cpp | 30 +++++++++++++++++++
 3 files changed, 37 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/complex.number/complex/complex_requires_cv_unqualified_object.verify.cpp

diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index f43a56f99f025..e3845d3e946d3 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -136,7 +136,7 @@
 "`LWG3101 <https://wg21.link/LWG3101>`__","``span``\ 's ``Container``\  constructors need another constraint","2019-02 (Kona)","|Complete|","","`#103837 <https://github.com/llvm/llvm-project/issues/103837>`__",""
 "`LWG3112 <https://wg21.link/LWG3112>`__","``system_error``\  and ``filesystem_error``\  constructors taking a ``string``\  may not be able to meet their postconditions","2019-02 (Kona)","|Nothing To Do|","","`#100253 <https://github.com/llvm/llvm-project/issues/100253>`__",""
 "`LWG3119 <https://wg21.link/LWG3119>`__","Program-definedness of closure types","2019-02 (Kona)","|Nothing To Do|","","`#103838 <https://github.com/llvm/llvm-project/issues/103838>`__",""
-"`LWG3133 <https://wg21.link/LWG3133>`__","Modernizing numeric type requirements","2019-02 (Kona)","","","`#100254 <https://github.com/llvm/llvm-project/issues/100254>`__",""
+"`LWG3133 <https://wg21.link/LWG3133>`__","Modernizing numeric type requirements","2019-02 (Kona)","|Complete|","23","`#100254 <https://github.com/llvm/llvm-project/issues/100254>`__",""
 "`LWG3144 <https://wg21.link/LWG3144>`__","``span``\  does not have a ``const_pointer``\  typedef","2019-02 (Kona)","|Complete|","","`#103839 <https://github.com/llvm/llvm-project/issues/103839>`__",""
 "`LWG3173 <https://wg21.link/LWG3173>`__","Enable CTAD for ``ref-view``\ ","2019-02 (Kona)","|Complete|","15","`#103840 <https://github.com/llvm/llvm-project/issues/103840>`__",""
 "`LWG3179 <https://wg21.link/LWG3179>`__","``subrange``\  should always model ``Range``\ ","2019-02 (Kona)","|Nothing To Do|","","`#103842 <https://github.com/llvm/llvm-project/issues/103842>`__",""
diff --git a/libcxx/include/complex b/libcxx/include/complex
index f1ba85eb9624e..1fb4db220c0b8 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -266,6 +266,9 @@ template<class T> complex<T> tanh (const complex<T>&);
 #  include <__tuple/tuple_element.h>
 #  include <__tuple/tuple_size.h>
 #  include <__type_traits/conditional.h>
+#  include <__type_traits/is_object.h>
+#  include <__type_traits/is_same.h>
+#  include <__type_traits/remove_cv.h>
 #  include <__utility/move.h>
 #  include <cmath>
 #  include <version>
@@ -307,6 +310,9 @@ class complex {
 public:
   typedef _Tp value_type;
 
+  static_assert(is_object_v<_Tp> && is_same_v<_Tp, remove_cv_t<_Tp>>,
+                "std::complex<T> requires that T be a cv-unqualified object type");
+
 private:
   value_type __re_;
   value_type __im_;
diff --git a/libcxx/test/std/numerics/complex.number/complex/complex_requires_cv_unqualified_object.verify.cpp b/libcxx/test/std/numerics/complex.number/complex/complex_requires_cv_unqualified_object.verify.cpp
new file mode 100644
index 0000000000000..4c00068ff390f
--- /dev/null
+++ b/libcxx/test/std/numerics/complex.number/complex/complex_requires_cv_unqualified_object.verify.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <complex>
+
+// template<class T>
+// class complex
+//
+// T shall be a cv-unqualified object type (LWG3133)
+
+#include <complex>
+
+int main(int, char**) {
+  {
+    // expected-error-re at complex:* {{static assertion failed{{.*}}std::complex<T> requires that T be a cv-unqualified object type}}
+    std::complex<const double> c1;
+  }
+  {
+    // expected-error-re at complex:* {{static assertion failed{{.*}}std::complex<T> requires that T be a cv-unqualified object type}}
+    std::complex<volatile int> c2;
+  }
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Tue Jul  7 22:59:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 07 Jul 2026 22:59:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3133: Modernizing
 numeric type requirements (PR #208145)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208145@github.com>
Message-ID: <6a4de72d.0c05e68e.1e82f1.4f62@mx.google.com>


github-actions[bot] wrote:

<!--LLVM NEW CONTRIBUTOR COMMENT-->

Hello @vyordan :wave:

Thank you for submitting a Pull Request (PR) to the LLVM Project. Since this is your first PR, here are a few useful links covering our main contribution policies and review practices.

* All contributions to LLVM must follow our [LLVM AI Tool Use Policy](https://llvm.org/docs/AIToolPolicy.html). In particular, if you used AI while working on this PR, remember to add a note to the PR description.
* The [LLVM Code-Review Policy and Practices](https://llvm.org/docs/CodeReview.html) document contains practical information about the PR process, including how patches are reviewed and accepted, and who can review a PR.
* Our [LLVM Developer Policy](https://llvm.org/docs/DeveloperPolicy.html) describes our expectations for code quality, commit summaries and contains notes on our CI system.

Please reply to this message to confirm that you have read these policies, especially the LLVM AI Tool Use Policy, and that any AI tool usage has been noted in the PR description.

---

### Frequently asked questions

**How do I add reviewers?**

This PR will be automatically labeled, and the relevant teams will be notified. For some parts of the project, reviewers may also be added automatically.

You can also add reviewers manually using the **Reviewers** section on this page. If you cannot use that section, it is probably because you do not have write permissions for the repository. In that case, you can request a review by tagging reviewers in a comment using `@` followed by their GitHub username.

**What if there are no comments?**

If you have not received any comments on your PR after a week, you can request a review by pinging the PR with a comment such as “Ping”. The common courtesy ping rate is once a week. Please remember that you are asking for volunteer time from other developers.

**Are any special GitHub settings required to contribute to LLVM?**

We only require contributors to have a public email address associated with their GitHub commits, see this [section](https://llvm.org/docs/DeveloperPolicy.html#email-addresses) of LLVM Developer Policy for details.

---

If you have questions, feel free to leave a comment on this PR, or ask on [LLVM Discord](https://discord.com/invite/xS7Z362) or [LLVM Discourse](https://discourse.llvm.org/).

Thank you,
The LLVM Community

https://github.com/llvm/llvm-project/pull/208145

From libcxx-commits at lists.llvm.org  Wed Jul  8 00:15:14 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Wed, 08 Jul 2026 00:15:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Revert "[libc++] Replace the of use
 custom sections for detecting overriden functions" (PR #208157)
Message-ID: <llvm.org/llvm/llvm-project/pull/208157@github.com>


https://github.com/petrhosek created https://github.com/llvm/llvm-project/pull/208157

Reverts llvm/llvm-project#175896 since it broke the build of internal symbolizer used by sanitizers.

>From f3d3723c16e45555df9fe40fc0afd047565d9ede Mon Sep 17 00:00:00 2001
From: Petr Hosek <phosek at google.com>
Date: Wed, 8 Jul 2026 00:14:06 -0700
Subject: [PATCH] =?UTF-8?q?Revert=20"[libc++]=20Replace=20the=20of=20use?=
 =?UTF-8?q?=20custom=20sections=20for=20detecting=20overriden=20f=E2=80=A6?=
 =?UTF-8?q?"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This reverts commit 1cefbacdaa0156880d8363b24ae04638a8fd31c8.
---
 libcxx/src/include/overridable_function.h | 95 ++++++++++++++++-------
 1 file changed, 65 insertions(+), 30 deletions(-)

diff --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index 085662e446332..c8d9f30c74c3e 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -11,6 +11,11 @@
 #define _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H
 
 #include <__config>
+#include <cstdint>
+
+#if __has_feature(ptrauth_calls)
+#  include <ptrauth.h>
+#endif
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -37,11 +42,18 @@
 // -------------------
 //
 // Let's say we want to check whether a weak function `f` has been overridden by the user.
-// The general mechanism works by defining a local symbol `ImplRef<f>::Impl` with the same
-// address as `f` as a constant expression using direct PC-relative materialization thus
-// pointing at the symbol defined in the same TU. At runtime, it compares the address of
-// `ImplRef<f>::Impl` with the address of `f` loaded from GOT: if `f` was overridden by
-// the user in another TU, the addresses will be different.
+// The general mechanism works by placing `f`'s definition (in the libc++ built library)
+// inside a special section, which we do using the `__section__` attribute via the
+// OVERRIDABLE_FUNCTION macro.
+//
+// Then, when comes the time to check whether the function has been overridden, we take
+// the address of the function and we check whether it falls inside the special function
+// we created. This can be done by finding pointers to the start and the end of the section
+// (which is done differently for ELF and Mach-O), and then checking whether `f` falls
+// within those bounds. If it falls within those bounds, then `f` is still inside the
+// special section and so it is the version we defined in the libc++ built library, i.e.
+// it was not overridden. Otherwise, it was overridden by the user because it falls
+// outside of the section.
 //
 // Important note
 // --------------
@@ -51,40 +63,63 @@
 // want to be defining special sections inside user's executables which use our headers.
 //
 
-#if defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
+#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
 
-template <auto* _Func>
-struct ImplRef;
+#  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
+#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
 
-// ImplRef<...>::Impl is expected to be defined elsewhere, so the compiler emits assembly
-// references to the mangled symbol with no definition. This template saves us the trouble
-// of providing manual declarations for overloads with some other local name for each
-// function name being overloaded (operator new, operator new[], etc.).
-template <typename _Ret, typename... _Args, _Ret (*_Func)(_Args...)>
-struct ImplRef<_Func> {
-  [[gnu::visibility("hidden")]] static _Ret Impl(_Args...);
-};
+_LIBCPP_BEGIN_NAMESPACE_STD template <typename T, T* _Func>
+_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
+  // Declare two dummy bytes and give them these special `__asm` values. These values are
+  // defined by the linker, which means that referring to `&__lcxx_override_start` will
+  // effectively refer to the address where the section starts (and same for the end).
+  extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
+  extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
+
+  // Now get a uintptr_t out of these locations, and out of the function pointer.
+  uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
+  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
+  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
+
+#  if __has_feature(ptrauth_calls)
+  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
+  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
+  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
+  // stripped the function pointer. See rdar://122927845.
+  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
+#  endif
+
+  // Finally, the function was overridden if it falls outside of the section's bounds.
+  return __ptr < __start || __ptr > __end;
+}
+_LIBCPP_END_NAMESPACE_STD
+
+// The NVPTX linker cannot create '__start/__stop' sections.
+#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
+#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+
+// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
+// variables with those names corresponding to the start and the end of the section.
+//
+// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
+extern char __start___lcxx_override;
+extern char __stop___lcxx_override;
 
 _LIBCPP_BEGIN_NAMESPACE_STD
-// This takes a function type template argument first so that the second non-type template
-// argument (pointer to the public function) gets the benefit of type-aware overload
-// resolution, rather than having to use a static_cast.
 template <typename T, T* _Func>
 _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-#  if !defined(_LIBCPP_CLANG_VER) || _LIBCPP_CLANG_VER >= 2101
-  __asm__("%cc0 = %cc1" : : "X"(ImplRef<_Func>::Impl), "X"(_Func));
-#  else
-  __asm__("%c0 = %c1" : : "X"(ImplRef<_Func>::Impl), "X"(_Func));
+  uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
+  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
+  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
+
+#  if __has_feature(ptrauth_calls)
+  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. See full explanation above.
+  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
 #  endif
-  // This just has the compiler compare the two symbols. For PIC mode, this will do
-  // a direct PC-relative materialization for ImplRef<...>::Impl and a GOT load for
-  // the _Func symbol. The compiler thinks ImplRef<...>::Impl is defined elsewhere at
-  // link time and will be an undefined symbol. It doesn't know that the __asm__ tells
-  // the assembler to define it as a local symbol.
-  return _Func != ImplRef<_Func>::Impl;
+
+  return __ptr < __start || __ptr > __end;
 }
 _LIBCPP_END_NAMESPACE_STD
 


From libcxx-commits at lists.llvm.org  Wed Jul  8 00:16:13 2026
From: libcxx-commits at lists.llvm.org (Adrian Kuegel via libcxx-commits)
Date: Wed, 08 Jul 2026 00:16:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Revert "[libc++] Replace the of use
 custom sections for detecting overriden functions" (PR #208157)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208157@github.com>
Message-ID: <6a4df93d.30759430.24d50c.59a5@mx.google.com>


https://github.com/akuegel approved this pull request.


https://github.com/llvm/llvm-project/pull/208157

From libcxx-commits at lists.llvm.org  Wed Jul  8 00:17:21 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Yordan_V=C3=A1squez?= via libcxx-commits)
Date: Wed, 08 Jul 2026 00:17:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3133: Modernizing
 numeric type requirements (PR #208145)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208145@github.com>
Message-ID: <6a4df981.e9d484b9.15e549.b91f@mx.google.com>


https://github.com/vyordan updated https://github.com/llvm/llvm-project/pull/208145

>From cb1a4be06fb739c649cd28828b56eaffc0072bff Mon Sep 17 00:00:00 2001
From: vyordan <vyordangiovani at gmail.com>
Date: Tue, 7 Jul 2026 23:55:36 -0600
Subject: [PATCH] [libc++] Implement LWG3133: Modernizing numeric type
 requirements

Add a static_assert to std::complex<T> requiring that T be a
cv-unqualified object type, matching the revised wording in
[numeric.requirements]. This mirrors the existing pattern used by
std::optional<T>.

No implementation changes were needed for std::valarray<T>, since
its operator[] already indexes safely without depending on a
user-overloadable operator&.

Fixes #100254
---
 libcxx/docs/Status/Cxx20Issues.csv            |  2 +-
 libcxx/include/complex                        |  5 ++++
 ..._requires_cv_unqualified_object.verify.cpp | 27 +++++++++++++++++++
 3 files changed, 33 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/complex.number/complex/complex_requires_cv_unqualified_object.verify.cpp

diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index f43a56f99f025..e3845d3e946d3 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -136,7 +136,7 @@
 "`LWG3101 <https://wg21.link/LWG3101>`__","``span``\ 's ``Container``\  constructors need another constraint","2019-02 (Kona)","|Complete|","","`#103837 <https://github.com/llvm/llvm-project/issues/103837>`__",""
 "`LWG3112 <https://wg21.link/LWG3112>`__","``system_error``\  and ``filesystem_error``\  constructors taking a ``string``\  may not be able to meet their postconditions","2019-02 (Kona)","|Nothing To Do|","","`#100253 <https://github.com/llvm/llvm-project/issues/100253>`__",""
 "`LWG3119 <https://wg21.link/LWG3119>`__","Program-definedness of closure types","2019-02 (Kona)","|Nothing To Do|","","`#103838 <https://github.com/llvm/llvm-project/issues/103838>`__",""
-"`LWG3133 <https://wg21.link/LWG3133>`__","Modernizing numeric type requirements","2019-02 (Kona)","","","`#100254 <https://github.com/llvm/llvm-project/issues/100254>`__",""
+"`LWG3133 <https://wg21.link/LWG3133>`__","Modernizing numeric type requirements","2019-02 (Kona)","|Complete|","23","`#100254 <https://github.com/llvm/llvm-project/issues/100254>`__",""
 "`LWG3144 <https://wg21.link/LWG3144>`__","``span``\  does not have a ``const_pointer``\  typedef","2019-02 (Kona)","|Complete|","","`#103839 <https://github.com/llvm/llvm-project/issues/103839>`__",""
 "`LWG3173 <https://wg21.link/LWG3173>`__","Enable CTAD for ``ref-view``\ ","2019-02 (Kona)","|Complete|","15","`#103840 <https://github.com/llvm/llvm-project/issues/103840>`__",""
 "`LWG3179 <https://wg21.link/LWG3179>`__","``subrange``\  should always model ``Range``\ ","2019-02 (Kona)","|Nothing To Do|","","`#103842 <https://github.com/llvm/llvm-project/issues/103842>`__",""
diff --git a/libcxx/include/complex b/libcxx/include/complex
index f1ba85eb9624e..37dfc747b712e 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -266,6 +266,8 @@ template<class T> complex<T> tanh (const complex<T>&);
 #  include <__tuple/tuple_element.h>
 #  include <__tuple/tuple_size.h>
 #  include <__type_traits/conditional.h>
+#  include <__type_traits/is_object.h>
+#  include <__type_traits/is_unqualified.h>
 #  include <__utility/move.h>
 #  include <cmath>
 #  include <version>
@@ -307,6 +309,9 @@ class complex {
 public:
   typedef _Tp value_type;
 
+  static_assert(is_object<_Tp>::value && __is_unqualified_v<_Tp>,
+                "std::complex<T> requires that T be a cv-unqualified object type");
+
 private:
   value_type __re_;
   value_type __im_;
diff --git a/libcxx/test/std/numerics/complex.number/complex/complex_requires_cv_unqualified_object.verify.cpp b/libcxx/test/std/numerics/complex.number/complex/complex_requires_cv_unqualified_object.verify.cpp
new file mode 100644
index 0000000000000..da87ea62cd4c4
--- /dev/null
+++ b/libcxx/test/std/numerics/complex.number/complex/complex_requires_cv_unqualified_object.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+// <complex>
+
+// template<class T>
+// class complex
+//
+// T shall be a cv-unqualified object type (LWG3133)
+
+#include <complex>
+
+void test() {
+  // expected-error-re at complex:* {{static assertion failed{{.*}}std::complex<T> requires that T be a cv-unqualified object type}}
+  std::complex<const double> c1;
+  // expected-error-re at complex:* {{static assertion failed{{.*}}std::complex<T> requires that T be a cv-unqualified object type}}
+  std::complex<volatile int> c2;
+  // expected-error-re at complex:* {{static assertion failed{{.*}}std::complex<T> requires that T be a cv-unqualified object type}}
+  std::complex<const volatile float> c3;
+}


From libcxx-commits at lists.llvm.org  Wed Jul  8 00:20:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 00:20:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 70df3cc - Revert "[libc++] Replace the of
 use custom sections for detecting overriden functions" (#208157)
Message-ID: <6a4dfa21.cbc3f03a.10d998.35ff@mx.google.com>


Author: Petr Hosek
Date: 2026-07-08T00:19:47-07:00
New Revision: 70df3ccef10d336d3644df293fddb2adcb36e097

URL: https://github.com/llvm/llvm-project/commit/70df3ccef10d336d3644df293fddb2adcb36e097
DIFF: https://github.com/llvm/llvm-project/commit/70df3ccef10d336d3644df293fddb2adcb36e097.diff

LOG: Revert "[libc++] Replace the of use custom sections for detecting overriden functions" (#208157)

Reverts llvm/llvm-project#175896 since it broke the build of internal
symbolizer used by sanitizers.

Added: 
    

Modified: 
    libcxx/src/include/overridable_function.h

Removed: 
    


################################################################################
diff  --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index 085662e446332..c8d9f30c74c3e 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -11,6 +11,11 @@
 #define _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H
 
 #include <__config>
+#include <cstdint>
+
+#if __has_feature(ptrauth_calls)
+#  include <ptrauth.h>
+#endif
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -37,11 +42,18 @@
 // -------------------
 //
 // Let's say we want to check whether a weak function `f` has been overridden by the user.
-// The general mechanism works by defining a local symbol `ImplRef<f>::Impl` with the same
-// address as `f` as a constant expression using direct PC-relative materialization thus
-// pointing at the symbol defined in the same TU. At runtime, it compares the address of
-// `ImplRef<f>::Impl` with the address of `f` loaded from GOT: if `f` was overridden by
-// the user in another TU, the addresses will be 
diff erent.
+// The general mechanism works by placing `f`'s definition (in the libc++ built library)
+// inside a special section, which we do using the `__section__` attribute via the
+// OVERRIDABLE_FUNCTION macro.
+//
+// Then, when comes the time to check whether the function has been overridden, we take
+// the address of the function and we check whether it falls inside the special function
+// we created. This can be done by finding pointers to the start and the end of the section
+// (which is done 
diff erently for ELF and Mach-O), and then checking whether `f` falls
+// within those bounds. If it falls within those bounds, then `f` is still inside the
+// special section and so it is the version we defined in the libc++ built library, i.e.
+// it was not overridden. Otherwise, it was overridden by the user because it falls
+// outside of the section.
 //
 // Important note
 // --------------
@@ -51,40 +63,63 @@
 // want to be defining special sections inside user's executables which use our headers.
 //
 
-#if defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
+#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
 
-template <auto* _Func>
-struct ImplRef;
+#  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
+#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
 
-// ImplRef<...>::Impl is expected to be defined elsewhere, so the compiler emits assembly
-// references to the mangled symbol with no definition. This template saves us the trouble
-// of providing manual declarations for overloads with some other local name for each
-// function name being overloaded (operator new, operator new[], etc.).
-template <typename _Ret, typename... _Args, _Ret (*_Func)(_Args...)>
-struct ImplRef<_Func> {
-  [[gnu::visibility("hidden")]] static _Ret Impl(_Args...);
-};
+_LIBCPP_BEGIN_NAMESPACE_STD template <typename T, T* _Func>
+_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
+  // Declare two dummy bytes and give them these special `__asm` values. These values are
+  // defined by the linker, which means that referring to `&__lcxx_override_start` will
+  // effectively refer to the address where the section starts (and same for the end).
+  extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
+  extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
+
+  // Now get a uintptr_t out of these locations, and out of the function pointer.
+  uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
+  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
+  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
+
+#  if __has_feature(ptrauth_calls)
+  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
+  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
+  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
+  // stripped the function pointer. See rdar://122927845.
+  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
+#  endif
+
+  // Finally, the function was overridden if it falls outside of the section's bounds.
+  return __ptr < __start || __ptr > __end;
+}
+_LIBCPP_END_NAMESPACE_STD
+
+// The NVPTX linker cannot create '__start/__stop' sections.
+#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
+#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+
+// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
+// variables with those names corresponding to the start and the end of the section.
+//
+// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
+extern char __start___lcxx_override;
+extern char __stop___lcxx_override;
 
 _LIBCPP_BEGIN_NAMESPACE_STD
-// This takes a function type template argument first so that the second non-type template
-// argument (pointer to the public function) gets the benefit of type-aware overload
-// resolution, rather than having to use a static_cast.
 template <typename T, T* _Func>
 _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-#  if !defined(_LIBCPP_CLANG_VER) || _LIBCPP_CLANG_VER >= 2101
-  __asm__("%cc0 = %cc1" : : "X"(ImplRef<_Func>::Impl), "X"(_Func));
-#  else
-  __asm__("%c0 = %c1" : : "X"(ImplRef<_Func>::Impl), "X"(_Func));
+  uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
+  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
+  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
+
+#  if __has_feature(ptrauth_calls)
+  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. See full explanation above.
+  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
 #  endif
-  // This just has the compiler compare the two symbols. For PIC mode, this will do
-  // a direct PC-relative materialization for ImplRef<...>::Impl and a GOT load for
-  // the _Func symbol. The compiler thinks ImplRef<...>::Impl is defined elsewhere at
-  // link time and will be an undefined symbol. It doesn't know that the __asm__ tells
-  // the assembler to define it as a local symbol.
-  return _Func != ImplRef<_Func>::Impl;
+
+  return __ptr < __start || __ptr > __end;
 }
 _LIBCPP_END_NAMESPACE_STD
 


        

From libcxx-commits at lists.llvm.org  Wed Jul  8 00:20:09 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Wed, 08 Jul 2026 00:20:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Revert "[libc++] Replace the of use
 custom sections for detecting overriden functions" (PR #208157)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208157@github.com>
Message-ID: <6a4dfa29.c56fb102.7c9cd.8d07@mx.google.com>


https://github.com/petrhosek closed https://github.com/llvm/llvm-project/pull/208157

From libcxx-commits at lists.llvm.org  Wed Jul  8 00:21:54 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Wed, 08 Jul 2026 00:21:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Revert "[libc++] Replace the of use
 custom sections for detecting overriden functions" (PR #208157)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208157@github.com>
Message-ID: <6a4dfa92.27b3b003.3b871.ccbc@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `amdgpu-offload-ubuntu-22-cmake-build-only` running on `rocm-docker-ubu-22` while building `libcxx` at step 3 "checkout".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/203/builds/51617

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 3 (checkout) failure: update (failure)
git version 2.34.1
error: RPC failed; HTTP 429 curl 22 The requested URL returned error: 429
fatal: error reading section header 'acknowledgments'
remote: This request was rate-limited due to too many requests. Reduce the frequency of your requests or try again later.
remote: For more on scraping GitHub and how it may affect your rights, please review our Terms of Service (https://docs.github.com/en/site-policy/github-terms/github-terms-of-service).
fatal: unable to access 'https://github.com/llvm/llvm-project.git/': The requested URL returned error: 429

```

</details>

https://github.com/llvm/llvm-project/pull/208157

From libcxx-commits at lists.llvm.org  Wed Jul  8 00:21:57 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Wed, 08 Jul 2026 00:21:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Revert "[libc++] Replace the of use
 custom sections for detecting overriden functions" (PR #208157)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208157@github.com>
Message-ID: <6a4dfa95.27b3b003.3b871.cf76@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `amdgpu-offload-build-only` running on `AMD-bb-w-01` while building `libcxx` at step 3 "checkout".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/226/builds/12062

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 3 (checkout) failure: update (failure)
git version 2.49.0
remote: This request was rate-limited due to too many requests. Reduce the frequency of your requests or try again later.
remote: For more on scraping GitHub and how it may affect your rights, please review our Terms of Service (https://docs.github.com/en/site-policy/github-terms/github-terms-of-service).
fatal: unable to access 'https://github.com/llvm/llvm-project.git/': The requested URL returned error: 429
remote: This request was rate-limited due to too many requests. Reduce the frequency of your requests or try again later.
remote: For more on scraping GitHub and how it may affect your rights, please review our Terms of Service (https://docs.github.com/en/site-policy/github-terms/github-terms-of-service).
fatal: unable to access 'https://github.com/llvm/llvm-project.git/': The requested URL returned error: 429

```

</details>

https://github.com/llvm/llvm-project/pull/208157

From libcxx-commits at lists.llvm.org  Wed Jul  8 00:22:10 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Wed, 08 Jul 2026 00:22:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Revert "[libc++] Replace the of use
 custom sections for detecting overriden functions" (PR #208157)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208157@github.com>
Message-ID: <6a4dfaa2.7d41f3a1.237e53.0457@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `openmp-offload-libc-amdgpu-runtime` running on `omp-vega20-1` while building `libcxx` at step 3 "checkout".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/73/builds/28594

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 3 (checkout) failure: update (failure)
git version 2.34.1
remote: This request was rate-limited due to too many requests. Reduce the frequency of your requests or try again later.
remote: For more on scraping GitHub and how it may affect your rights, please review our Terms of Service (https://docs.github.com/en/site-policy/github-terms/github-terms-of-service).
fatal: unable to access 'https://github.com/llvm/llvm-project.git/': The requested URL returned error: 429
remote: This request was rate-limited due to too many requests. Reduce the frequency of your requests or try again later.
remote: For more on scraping GitHub and how it may affect your rights, please review our Terms of Service (https://docs.github.com/en/site-policy/github-terms/github-terms-of-service).
fatal: unable to access 'https://github.com/llvm/llvm-project.git/': The requested URL returned error: 429

```

</details>

https://github.com/llvm/llvm-project/pull/208157

From libcxx-commits at lists.llvm.org  Wed Jul  8 00:23:40 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 00:23:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Revert "[libc++] Replace the of use
 custom sections for detecting overriden functions" (PR #208157)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208157@github.com>
Message-ID: <6a4dfafc.c56fb102.7c9cd.30a9@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Petr Hosek (petrhosek)

<details>
<summary>Changes</summary>

Reverts llvm/llvm-project#<!-- -->175896 since it broke the build of internal symbolizer used by sanitizers.

---
Full diff: https://github.com/llvm/llvm-project/pull/208157.diff


1 Files Affected:

- (modified) libcxx/src/include/overridable_function.h (+65-30) 


``````````diff
diff --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index 085662e446332..c8d9f30c74c3e 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -11,6 +11,11 @@
 #define _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H
 
 #include <__config>
+#include <cstdint>
+
+#if __has_feature(ptrauth_calls)
+#  include <ptrauth.h>
+#endif
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -37,11 +42,18 @@
 // -------------------
 //
 // Let's say we want to check whether a weak function `f` has been overridden by the user.
-// The general mechanism works by defining a local symbol `ImplRef<f>::Impl` with the same
-// address as `f` as a constant expression using direct PC-relative materialization thus
-// pointing at the symbol defined in the same TU. At runtime, it compares the address of
-// `ImplRef<f>::Impl` with the address of `f` loaded from GOT: if `f` was overridden by
-// the user in another TU, the addresses will be different.
+// The general mechanism works by placing `f`'s definition (in the libc++ built library)
+// inside a special section, which we do using the `__section__` attribute via the
+// OVERRIDABLE_FUNCTION macro.
+//
+// Then, when comes the time to check whether the function has been overridden, we take
+// the address of the function and we check whether it falls inside the special function
+// we created. This can be done by finding pointers to the start and the end of the section
+// (which is done differently for ELF and Mach-O), and then checking whether `f` falls
+// within those bounds. If it falls within those bounds, then `f` is still inside the
+// special section and so it is the version we defined in the libc++ built library, i.e.
+// it was not overridden. Otherwise, it was overridden by the user because it falls
+// outside of the section.
 //
 // Important note
 // --------------
@@ -51,40 +63,63 @@
 // want to be defining special sections inside user's executables which use our headers.
 //
 
-#if defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
+#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
 
-template <auto* _Func>
-struct ImplRef;
+#  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
+#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
 
-// ImplRef<...>::Impl is expected to be defined elsewhere, so the compiler emits assembly
-// references to the mangled symbol with no definition. This template saves us the trouble
-// of providing manual declarations for overloads with some other local name for each
-// function name being overloaded (operator new, operator new[], etc.).
-template <typename _Ret, typename... _Args, _Ret (*_Func)(_Args...)>
-struct ImplRef<_Func> {
-  [[gnu::visibility("hidden")]] static _Ret Impl(_Args...);
-};
+_LIBCPP_BEGIN_NAMESPACE_STD template <typename T, T* _Func>
+_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
+  // Declare two dummy bytes and give them these special `__asm` values. These values are
+  // defined by the linker, which means that referring to `&__lcxx_override_start` will
+  // effectively refer to the address where the section starts (and same for the end).
+  extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
+  extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
+
+  // Now get a uintptr_t out of these locations, and out of the function pointer.
+  uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
+  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
+  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
+
+#  if __has_feature(ptrauth_calls)
+  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
+  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
+  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
+  // stripped the function pointer. See rdar://122927845.
+  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
+#  endif
+
+  // Finally, the function was overridden if it falls outside of the section's bounds.
+  return __ptr < __start || __ptr > __end;
+}
+_LIBCPP_END_NAMESPACE_STD
+
+// The NVPTX linker cannot create '__start/__stop' sections.
+#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
+#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+
+// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
+// variables with those names corresponding to the start and the end of the section.
+//
+// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
+extern char __start___lcxx_override;
+extern char __stop___lcxx_override;
 
 _LIBCPP_BEGIN_NAMESPACE_STD
-// This takes a function type template argument first so that the second non-type template
-// argument (pointer to the public function) gets the benefit of type-aware overload
-// resolution, rather than having to use a static_cast.
 template <typename T, T* _Func>
 _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-#  if !defined(_LIBCPP_CLANG_VER) || _LIBCPP_CLANG_VER >= 2101
-  __asm__("%cc0 = %cc1" : : "X"(ImplRef<_Func>::Impl), "X"(_Func));
-#  else
-  __asm__("%c0 = %c1" : : "X"(ImplRef<_Func>::Impl), "X"(_Func));
+  uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
+  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
+  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
+
+#  if __has_feature(ptrauth_calls)
+  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. See full explanation above.
+  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
 #  endif
-  // This just has the compiler compare the two symbols. For PIC mode, this will do
-  // a direct PC-relative materialization for ImplRef<...>::Impl and a GOT load for
-  // the _Func symbol. The compiler thinks ImplRef<...>::Impl is defined elsewhere at
-  // link time and will be an undefined symbol. It doesn't know that the __asm__ tells
-  // the assembler to define it as a local symbol.
-  return _Func != ImplRef<_Func>::Impl;
+
+  return __ptr < __start || __ptr > __end;
 }
 _LIBCPP_END_NAMESPACE_STD
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/208157

From libcxx-commits at lists.llvm.org  Wed Jul  8 00:25:54 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Yordan_V=C3=A1squez?= via libcxx-commits)
Date: Wed, 08 Jul 2026 00:25:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3133: Modernizing
 numeric type requirements (PR #208145)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208145@github.com>
Message-ID: <6a4dfb82.70920dee.290dca.dab3@mx.google.com>


================
@@ -266,6 +266,9 @@ template<class T> complex<T> tanh (const complex<T>&);
 #  include <__tuple/tuple_element.h>
 #  include <__tuple/tuple_size.h>
 #  include <__type_traits/conditional.h>
+#  include <__type_traits/is_object.h>
+#  include <__type_traits/is_same.h>
----------------
vyordan wrote:

Hello, thank you for the review.
I understood that I should remove `#include <__type_traits/is_same.h>` and `#include <__type_traits/remove_cv.h>`, since they are no longer used

However, my `static_assert` uses `is_object<_Tp>::value`, so I need to keep `#include <__type_traits/is_object.h>` — otherwise the code won't compile.
Could you please clarify whether I should replace `is_object` with some other trait (e.g., `__is_object` or similar), or does `__is_unqualified_v` already cover the "object type" check internally?

Thanks again for your time!

https://github.com/llvm/llvm-project/pull/208145

From libcxx-commits at lists.llvm.org  Wed Jul  8 01:23:18 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 08 Jul 2026 01:23:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 Disallow `chrono::duration<const T, P>` (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4e08f6.eb24c6ea.27aedc.7d82@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Wed Jul  8 02:00:03 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 02:00:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 459cadf - [libc++][chrono] Implement LWG
 4481: Disallow `chrono::duration<const T, P>` (#207558)
Message-ID: <6a4e1193.bb839515.27d21e.a156@mx.google.com>


Author: Emmett
Date: 2026-07-08T16:59:53+08:00
New Revision: 459cadfaf12cde6b1fda2fde932b277f1630c124

URL: https://github.com/llvm/llvm-project/commit/459cadfaf12cde6b1fda2fde932b277f1630c124
DIFF: https://github.com/llvm/llvm-project/commit/459cadfaf12cde6b1fda2fde932b277f1630c124.diff

LOG: [libc++][chrono] Implement LWG 4481: Disallow `chrono::duration<const T, P>` (#207558)

Added: 
    libcxx/test/std/time/time.duration/duration.verify.cpp

Modified: 
    libcxx/docs/Status/Cxx26Issues.csv
    libcxx/include/__chrono/duration.h
    libcxx/include/chrono

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 23adfde5bd4d7..5de35c96d4c0a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -301,7 +301,7 @@
 "`LWG4477 <https://wg21.link/LWG4477>`__","Placement ``operator delete`` should be constexpr","2026-03 (Croydon)","|Complete|","23","`#189843 <https://github.com/llvm/llvm-project/issues/189843>`__",""
 "`LWG4478 <https://wg21.link/LWG4478>`__","``meta::has_identifier`` is not specified for annotations","2026-03 (Croydon)","","","`#189844 <https://github.com/llvm/llvm-project/issues/189844>`__",""
 "`LWG4480 <https://wg21.link/LWG4480>`__","``<stdatomic.h>`` should provide ``ATOMIC_CHAR8_T_LOCK_FREE``","2026-03 (Croydon)","","","`#189845 <https://github.com/llvm/llvm-project/issues/189845>`__",""
-"`LWG4481 <https://wg21.link/LWG4481>`__","Disallow ``chrono::duration<const T, P>``","2026-03 (Croydon)","","","`#189846 <https://github.com/llvm/llvm-project/issues/189846>`__",""
+"`LWG4481 <https://wg21.link/LWG4481>`__","Disallow ``chrono::duration<const T, P>``","2026-03 (Croydon)","|Complete|","23","`#189846 <https://github.com/llvm/llvm-project/issues/189846>`__",""
 "`LWG4483 <https://wg21.link/LWG4483>`__","Multidimensional arrays are not supported by ``meta::reflect_constant_array`` and related functions","2026-03 (Croydon)","","","`#189847 <https://github.com/llvm/llvm-project/issues/189847>`__",""
 "`LWG4485 <https://wg21.link/LWG4485>`__","Move specification for ``task::stop_token_type``","2026-03 (Croydon)","","","`#189848 <https://github.com/llvm/llvm-project/issues/189848>`__",""
 "`LWG4486 <https://wg21.link/LWG4486>`__","``integral-constant-like`` and ``constexpr-wrapper-like`` exposition-only concept duplication","2026-03 (Croydon)","","","`#189849 <https://github.com/llvm/llvm-project/issues/189849>`__",""

diff  --git a/libcxx/include/__chrono/duration.h b/libcxx/include/__chrono/duration.h
index 92efd82f72145..fe5bbd07f6d89 100644
--- a/libcxx/include/__chrono/duration.h
+++ b/libcxx/include/__chrono/duration.h
@@ -19,6 +19,7 @@
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_convertible.h>
 #include <__type_traits/is_floating_point.h>
+#include <__type_traits/is_unqualified.h>
 #include <limits>
 #include <ratio>
 
@@ -164,6 +165,7 @@ template <class _ToDuration, class _Rep, class _Period, enable_if_t<__is_duratio
 
 template <class _Rep, class _Period>
 class duration {
+  static_assert(__is_unqualified_v<_Rep>, "A duration representation cannot be qualified");
   static_assert(!__is_duration_v<_Rep>, "A duration representation can not be a duration");
   static_assert(__is_ratio_v<_Period>, "Second template parameter of duration must be a std::ratio");
   static_assert(_Period::num > 0, "duration period must be positive");

diff  --git a/libcxx/include/chrono b/libcxx/include/chrono
index 39822d5e74f79..8cc6b9bb883b9 100644
--- a/libcxx/include/chrono
+++ b/libcxx/include/chrono
@@ -46,6 +46,7 @@ public:
 template <class Rep, class Period = ratio<1>>
 class duration
 {
+    static_assert(__is_unqualified_v<Rep>, "A duration representation cannot be qualified");
     static_assert(!__is_duration<Rep>::value, "A duration representation can not be a duration");
     static_assert(__is_ratio<Period>::value, "Second template parameter of duration must be a std::ratio");
     static_assert(Period::num > 0, "duration period must be positive");

diff  --git a/libcxx/test/std/time/time.duration/duration.verify.cpp b/libcxx/test/std/time/time.duration/duration.verify.cpp
new file mode 100644
index 0000000000000..f8c3307b2ceb7
--- /dev/null
+++ b/libcxx/test/std/time/time.duration/duration.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <chrono>
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+// LWG 4481
+// A program that instantiates duration with a cv-qualified Rep is ill-formed.
+
+#include <chrono>
+
+void test() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be qualified}}
+  (void)sizeof(std::chrono::duration<const int>);
+
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be qualified}}
+  (void)sizeof(std::chrono::duration<volatile int>);
+
+  // expected-error-re@*:* {{static assertion failed {{.*}}A duration representation cannot be qualified}}
+  (void)sizeof(std::chrono::duration<const volatile int>);
+}


        

From libcxx-commits at lists.llvm.org  Wed Jul  8 02:00:08 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 08 Jul 2026 02:00:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 Disallow `chrono::duration<const T, P>` (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4e1198.6c8668f2.e4f82.eb5e@mx.google.com>


https://github.com/frederick-vs-ja closed https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Wed Jul  8 02:00:31 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 02:00:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4481:
 Disallow `chrono::duration<const T, P>` (PR #207558)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207558@github.com>
Message-ID: <6a4e11af.ff037943.2e959a.3da5@mx.google.com>


github-actions[bot] wrote:

<!--LLVM BUILDBOT INFORMATION COMMENT-->

@emmett2020 Congratulations on having your first Pull Request (PR) merged into the LLVM Project!

Your changes will be combined with recent changes from other authors, then tested by our [build bots](https://lab.llvm.org/buildbot/). If there is a problem with a build, you may receive a report in an email or a comment on this PR.

Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail [here](https://llvm.org/docs/MyFirstTypoFix.html#myfirsttypofix-issues-after-landing-your-pr).

If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of [LLVM development](https://llvm.org/docs/DeveloperPolicy.html#patch-reversion-policy). You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are working as expected, well done!


https://github.com/llvm/llvm-project/pull/207558

From libcxx-commits at lists.llvm.org  Wed Jul  8 02:06:52 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 02:06:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a4e132c.331483e7.2440f8.d70c@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From d93e557c304cea49a5cf654b3bf1710f5cee6855 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/10] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..5e1a7afea06a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From f17e51a372a7a1372fa63a572871939eec329145 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/10] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From ac972d414dd906c0e73a3e8b70697abfc537dafa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/10] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e1a7afea06a2..4477c821235de 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..b0f40e5a261ed 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -896,6 +896,7 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 33732fee8003a503f62524e1208aef02094b0bc6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/10] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From dc33657dd85cca1c1511d5cd7605bbcca05b35f8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/10] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From f133b64bf0da078d6ef4659603f8f92f12e7ec8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/10] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From e943f4968a86c2c73214cdb22ccdf77b122428e5 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/10] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 6070b97b3d62691fabebe6dbd7b79a3fd5a58c4d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/10] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 56cbfdaa5c0ff..8ccbcf9d8d227 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -160,7 +160,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -178,7 +178,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -193,7 +193,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b20b5e25fc9cd..04e12afd21689 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From d85c6a4fd888729fc38b7b3bae192263875cc4ed Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/10] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 4477c821235de..0e214d48c5672 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 8ccbcf9d8d227..04bdd2a6b343f 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -28,29 +28,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 04e12afd21689..3776873d8936d 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b0f40e5a261ed..a15ec1342a56d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -885,6 +885,7 @@ module std [system] {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -896,7 +897,6 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From af8e3868ec116fc226f4d5b1c3402d99dade4505 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/10] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }


From libcxx-commits at lists.llvm.org  Wed Jul  8 02:25:07 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 08 Jul 2026 02:25:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Backport P3029R1 Better
 `mdspan`'s CTAD to C++20/23 (PR #208132)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208132@github.com>
Message-ID: <6a4e1773.6c8668f2.e4f82.dd72@mx.google.com>


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/208132

>From 90b9f5ada6e9997bb10d70c3d051796134431fd1 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 8 Jul 2026 10:14:16 +0800
Subject: [PATCH 1/2] [libc++] Backport P3029R1 Better `mdspan`'s CTAD to
 C++20/23

Also backport
- support for `T::value` of a reference type in `integral-constant-like`
from P2781R9 `std::constant_wrapper`, and
- LWG4351 _`integral-constant-like`_ needs more `remove_cvref_t`.

P3029R1 (and its further fixes) should be treated as a Defect Report
against C++20/23 to avoid behavior difference in different modes. Other
implementations will do or are already doing the same.
---
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 libcxx/include/__mdspan/extents.h             |  6 -----
 libcxx/include/__mdspan/mdspan.h              |  7 -----
 libcxx/include/mdspan                         |  6 +----
 libcxx/include/span                           | 15 +++--------
 .../views/mdspan/extents/ctad.pass.cpp        | 13 ++++++---
 .../views/mdspan/mdspan/deduction.pass.cpp    | 19 ++++++++-----
 .../views.span/span.cons/deduct.pass.cpp      | 27 ++++++++++++++-----
 8 files changed, 48 insertions(+), 47 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..e1fcaf907c95b 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -61,7 +61,7 @@
 "`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","","`#105423 <https://github.com/llvm/llvm-project/issues/105423>`__",""
 "`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","22","`#105424 <https://github.com/llvm/llvm-project/issues/105424>`__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
 "`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","`#105425 <https://github.com/llvm/llvm-project/issues/105425>`__",""
-"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__",""
+"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23. (MSVC STL and libstdc++ will do the same.)"
 "","","","","","",""
 "`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","`#105427 <https://github.com/llvm/llvm-project/issues/105427>`__",""
 "`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","`#105428 <https://github.com/llvm/llvm-project/issues/105428>`__","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
diff --git a/libcxx/include/__mdspan/extents.h b/libcxx/include/__mdspan/extents.h
index bc4490aa5b470..99366b9ca9cc4 100644
--- a/libcxx/include/__mdspan/extents.h
+++ b/libcxx/include/__mdspan/extents.h
@@ -466,15 +466,9 @@ using dims = dextents<_IndexType, _Rank>;
 #  endif
 
 // Deduction guide for extents
-#  if _LIBCPP_STD_VER >= 26
 template <class... _IndexTypes>
   requires(is_convertible_v<_IndexTypes, size_t> && ...)
 explicit extents(_IndexTypes...) -> extents<size_t, __maybe_static_ext<_IndexTypes>...>;
-#  else
-template <class... _IndexTypes>
-  requires(is_convertible_v<_IndexTypes, size_t> && ...)
-explicit extents(_IndexTypes...) -> extents<size_t, size_t(((void)sizeof(_IndexTypes), dynamic_extent))...>;
-#  endif
 
 namespace __mdspan_detail {
 
diff --git a/libcxx/include/__mdspan/mdspan.h b/libcxx/include/__mdspan/mdspan.h
index bcfa302231cd4..3b78f792770f0 100644
--- a/libcxx/include/__mdspan/mdspan.h
+++ b/libcxx/include/__mdspan/mdspan.h
@@ -312,17 +312,10 @@ class mdspan {
 #  endif
 };
 
-#  if _LIBCPP_STD_VER >= 26
 template <class _ElementType, class... _OtherIndexTypes>
   requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
 explicit mdspan(_ElementType*, _OtherIndexTypes...)
     -> mdspan<_ElementType, extents<size_t, __maybe_static_ext<_OtherIndexTypes>...>>;
-#  else
-template <class _ElementType, class... _OtherIndexTypes>
-  requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
-explicit mdspan(_ElementType*, _OtherIndexTypes...)
-    -> mdspan<_ElementType, dextents<size_t, sizeof...(_OtherIndexTypes)>>;
-#  endif
 
 template <class _Pointer>
   requires(is_pointer_v<remove_reference_t<_Pointer>>)
diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan
index 2f0e1f9b87751..6313c03f041af 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -411,14 +411,10 @@ namespace std {
     mdspan(Pointer&&)
       -> mdspan<remove_pointer_t<remove_reference_t<Pointer>>, extents<size_t>>;
 
-  template<class ElementType, class... Integrals>
-    requires ((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
-    explicit mdspan(ElementType*, Integrals...)
-      -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;            // until C++26
   template<class ElementType, class... Integrals>
     requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
     explicit mdspan(ElementType*, Integrals...)
-      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;  // since C++26
+      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;
 
   template<class ElementType, class OtherIndexType, size_t N>
     mdspan(ElementType*, span<OtherIndexType, N>)
diff --git a/libcxx/include/span b/libcxx/include/span
index e39b811f9b1ea..1c52983f808ca 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -19,16 +19,16 @@ namespace std {
 inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();
 
 template<class T>
-  concept integral-constant-like =                           // exposition only, since C++26
-    is_integral_v<decltype(T::value)> &&
-    !is_same_v<bool, remove_const_t<decltype(T::value)>> &&
+  concept integral-constant-like =                           // exposition only
+    is_integral_v<remove_cvref_t<decltype(T::value)>> &&
+    !is_same_v<bool, remove_cvref_t<decltype(T::value)>> &&
     convertible_to<T, decltype(T::value)> &&
     equality_comparable_with<T, decltype(T::value)> &&
     bool_constant<T() == T::value>::value &&
     bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value;
 
 template<class T>
-  constexpr size_t maybe-static-ext = dynamic_extent;        // exposition only, since C++26
+  constexpr size_t maybe-static-ext = dynamic_extent;        // exposition only
 template<integral-constant-like T>
   constexpr size_t maybe-static-ext<T> = {T::value};
 
@@ -122,8 +122,6 @@ private:
     size_type size_;  // exposition only
 };
 
-template<class It, class EndOrSize>
-    span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;                             // until C++26
 template<class It, class EndOrSize>
     span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>; // since C++26
 
@@ -597,7 +595,6 @@ template <class _Tp, size_t _Extent>
   return __s.__as_writable_bytes();
 }
 
-#    if _LIBCPP_STD_VER >= 26
 template <class _Tp>
 concept __integral_constant_like =
     is_integral_v<remove_cvref_t<decltype(_Tp::value)>> && !is_same_v<bool, remove_cvref_t<decltype(_Tp::value)>> &&
@@ -613,10 +610,6 @@ inline constexpr size_t __maybe_static_ext<_Tp> = {_Tp::value};
 
 template <contiguous_iterator _It, class _EndOrSize>
 span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>, __maybe_static_ext<_EndOrSize>>;
-#    else
-template <contiguous_iterator _It, class _EndOrSize>
-span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;
-#    endif
 
 template <class _Tp, size_t _Sz>
 span(_Tp (&)[_Sz]) -> span<_Tp, _Sz>;
diff --git a/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
index b72c3e050b1f9..e34113f91ad06 100644
--- a/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
@@ -13,8 +13,7 @@
 // explicit extents(Integrals...) -> see below;
 //   Constraints: (is_convertible_v<Integrals, size_t> && ...) is true.
 //
-// Remarks: The deduced type is dextents<size_t, sizeof...(Integrals)>.           // until C++26
-// Remarks: The deduced type is extents<size_t, maybe-static-ext<Integrals>...>.  // since C++26
+// Remarks: The deduced type is extents<size_t, maybe-static-ext<Integrals>...>.
 
 #include <cassert>
 #include <cstddef>
@@ -31,6 +30,9 @@ struct NoDefaultCtorIndex {
   constexpr operator size_t() const noexcept { return value; }
 };
 
+template <std::size_t N>
+using size_ref_constant = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, N>::value>;
+
 template <class E, class Expected>
 constexpr void test(E e, Expected expected) {
   ASSERT_SAME_TYPE(E, Expected);
@@ -47,13 +49,16 @@ constexpr bool test() {
        std::extents<std::size_t, D, D, D, D, D, D, D, D, D>(1, 2u, 3, 4, 5, 6, 7, 8, 9));
   test(std::extents(NoDefaultCtorIndex{1}, NoDefaultCtorIndex{2}), std::extents<std::size_t, D, D>(1, 2));
 
-#if _LIBCPP_STD_VER >= 26
   // P3029R1: deduction from `integral_constant`
   test(std::extents(std::integral_constant<size_t, 5>{}), std::extents<std::size_t, 5>());
   test(std::extents(std::integral_constant<size_t, 5>{}, 6), std::extents<std::size_t, 5, std::dynamic_extent>(6));
   test(std::extents(std::integral_constant<size_t, 5>{}, 6, std::integral_constant<size_t, 7>{}),
        std::extents<std::size_t, 5, std::dynamic_extent, 7>(6));
-#endif
+  // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
+  test(std::extents(size_ref_constant<5>{}), std::extents<std::size_t, 5>());
+  test(std::extents(size_ref_constant<5>{}, 6), std::extents<std::size_t, 5, std::dynamic_extent>(6));
+  test(std::extents(std::integral_constant<size_t, 5>{}, 6, size_ref_constant<7>{}),
+       std::extents<std::size_t, 5, std::dynamic_extent, 7>(6));
 
   return true;
 }
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
index 762841016e29e..6c12ccb7fd86a 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
@@ -22,11 +22,7 @@
 //  template<class ElementType, class... Integrals>
 //    requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
 //    explicit mdspan(ElementType*, Integrals...)
-//      -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;            // until C++26
-//  template<class ElementType, class... Integrals>
-//    requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
-//    explicit mdspan(ElementType*, Integrals...)
-//      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;  // since C++26
+//      -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;
 //
 //  template<class ElementType, class OtherIndexType, size_t N>
 //    mdspan(ElementType*, span<OtherIndexType, N>)
@@ -53,6 +49,7 @@
 
 #include <mdspan>
 #include <cassert>
+#include <cstddef>
 #include <concepts>
 #include <span> // dynamic_extent
 #include <type_traits>
@@ -98,6 +95,9 @@ struct SizeTIntType {
   constexpr operator size_t() const noexcept { return size_t(val); }
 };
 
+template <std::size_t N>
+using size_ref_constant = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, N>::value>;
+
 template <class H, class A>
   requires(sizeof(decltype(std::mdspan(std::declval<H>(), 10))) > 0)
 constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
@@ -107,7 +107,6 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
   // deduction from pointer and integral like
   ASSERT_SAME_TYPE(decltype(std::mdspan(handle, 5, SizeTIntType(6))), std::mdspan<T, std::dextents<size_t, 2>>);
 
-#if _LIBCPP_STD_VER >= 26
   // P3029R1: deduction from `integral_constant`
   ASSERT_SAME_TYPE(
       decltype(std::mdspan(handle, std::integral_constant<size_t, 5>{})), std::mdspan<T, std::extents<size_t, 5>>);
@@ -117,7 +116,13 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
       decltype(std::mdspan(
           handle, std::integral_constant<size_t, 5>{}, std::dynamic_extent, std::integral_constant<size_t, 7>{})),
       std::mdspan<T, std::extents<size_t, 5, std::dynamic_extent, 7>>);
-#endif
+  // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle, size_ref_constant<5>{})), std::mdspan<T, std::extents<std::size_t, 5>>);
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle, size_ref_constant<5>{}, std::dynamic_extent)),
+                   std::mdspan<T, std::extents<std::size_t, 5, std::dynamic_extent>>);
+  ASSERT_SAME_TYPE(decltype(std::mdspan(
+                       handle, std::integral_constant<std::size_t, 5>{}, std::dynamic_extent, size_ref_constant<7>{})),
+                   std::mdspan<T, std::extents<std::size_t, 5, std::dynamic_extent, 7>>);
 
   std::array<char, 3> exts;
   // deduction from pointer and array
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index 9e41afad2258e..35d9b36f824aa 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -11,10 +11,7 @@
 // <span>
 
 // template<class It, class EndOrSize>
-//   span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>>; // until C++26
-// template<class It, class EndOrSize>
-//   span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>,
-//                               maybe-static-ext<EndOrSize>>; // since C++26
+//   span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>;
 // template<class T, size_t N>
 //   span(T (&)[N]) -> span<T, N>;
 // template<class T, size_t N>
@@ -27,6 +24,7 @@
 #include <span>
 #include <array>
 #include <cassert>
+#include <cstddef>
 #include <iterator>
 #include <memory>
 #include <string>
@@ -50,7 +48,6 @@ void test_iterator_sentinel() {
     assert(s.data() == std::data(arr));
   }
 
-#if TEST_STD_VER >= 26
   // P3029R1: deduction from `integral_constant`
   {
     std::span s{std::begin(arr), std::integral_constant<size_t, 3>{}};
@@ -58,15 +55,33 @@ void test_iterator_sentinel() {
     assert(s.size() == 3);
     assert(s.data() == std::data(arr));
   }
-
+  // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
+  {
+    using size_3_ref_type = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, 3>::value>;
+    std::span s{std::begin(arr), size_3_ref_type{}};
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+    assert(s.size() == 3);
+    assert(s.data() == std::data(arr));
+  }
+#if TEST_STD_VER >= 26
   {
     std::span s{std::begin(arr), std::cw<3>};
     ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
     assert(s.size() == 3);
     assert(s.data() == std::data(arr));
   }
+#endif
   {
     // LWG4351 integral-constant-like needs more remove_cvref_t
+    using true_ref_type = std::integral_constant<const bool&, std::true_type::value>;
+    LIBCPP_STATIC_ASSERT(!std::__integral_constant_like<true_ref_type>);
+    std::span s(std::begin(arr), true_ref_type{});
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, std::dynamic_extent>);
+    assert(s.size() == 1);
+    assert(s.data() == std::data(arr));
+  }
+#if TEST_STD_VER >= 26
+  {
     LIBCPP_STATIC_ASSERT(!std::__integral_constant_like<decltype(std::cw<true>)>);
     std::span s(std::begin(arr), std::cw<true>);
     ASSERT_SAME_TYPE(decltype(s), std::span<int, std::dynamic_extent>);

>From 5108cca329c7c1374047312f970ffdf1aa72431b Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 8 Jul 2026 13:41:37 +0800
Subject: [PATCH 2/2] Remove one `// since C++26`; avoid mentioning other
 implementations

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 libcxx/include/span                | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index e1fcaf907c95b..2522dd98e4b9c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -61,7 +61,7 @@
 "`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","","`#105423 <https://github.com/llvm/llvm-project/issues/105423>`__",""
 "`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","22","`#105424 <https://github.com/llvm/llvm-project/issues/105424>`__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
 "`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","`#105425 <https://github.com/llvm/llvm-project/issues/105425>`__",""
-"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23. (MSVC STL and libstdc++ will do the same.)"
+"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23."
 "","","","","","",""
 "`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","`#105427 <https://github.com/llvm/llvm-project/issues/105427>`__",""
 "`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","`#105428 <https://github.com/llvm/llvm-project/issues/105428>`__","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
diff --git a/libcxx/include/span b/libcxx/include/span
index 1c52983f808ca..b05a953829b5a 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -123,7 +123,7 @@ private:
 };
 
 template<class It, class EndOrSize>
-    span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>; // since C++26
+    span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>;
 
 template<class T, size_t N>
     span(T (&)[N]) -> span<T, N>;


From libcxx-commits at lists.llvm.org  Wed Jul  8 02:32:59 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 08 Jul 2026 02:32:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Enable missing allocator
	constructors in C++11 (PR #204845)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204845@github.com>
Message-ID: <6a4e194b.a70a48cb.2d4b1.70e1@mx.google.com>


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/204845

>From c7bb1403282a0c0d89b1bae0cccdadecf9c70345 Mon Sep 17 00:00:00 2001
From: halbi2 <hehiralbi at gmail.com>
Date: Fri, 19 Jun 2026 10:55:49 -0400
Subject: [PATCH 01/13] [libc++] Add missing constructor for forward_list

Fixes #204843
---
 libcxx/include/forward_list                                 | 6 +-----
 .../sequences/forwardlist/forwardlist.cons/size.pass.cpp    | 2 +-
 2 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index 6d86f7a8e5975..d963fea6084ca 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -37,7 +37,7 @@ public:
         noexcept(is_nothrow_default_constructible<allocator_type>::value);
     explicit forward_list(const allocator_type& a);
     explicit forward_list(size_type n);
-    explicit forward_list(size_type n, const allocator_type& a); // C++14
+    explicit forward_list(size_type n, const allocator_type& a);
     forward_list(size_type n, const value_type& v);
     forward_list(size_type n, const value_type& v, const allocator_type& a);
     template <class InputIterator>
@@ -664,9 +664,7 @@ public:
       _NOEXCEPT_(is_nothrow_default_constructible<__node_allocator>::value) {} // = default;
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit forward_list(const allocator_type& __a);
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit forward_list(size_type __n);
-#  if _LIBCPP_STD_VER >= 14
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit forward_list(size_type __n, const allocator_type& __a);
-#  endif
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI forward_list(size_type __n, const value_type& __v);
 
   template <__enable_if_t<__is_allocator_v<_Alloc>, int> = 0>
@@ -941,7 +939,6 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 forward_list<_Tp, _Alloc>::forward_list(size_type
   }
 }
 
-#  if _LIBCPP_STD_VER >= 14
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 forward_list<_Tp, _Alloc>::forward_list(size_type __n, const allocator_type& __base_alloc)
     : __base(__base_alloc) {
@@ -952,7 +949,6 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 forward_list<_Tp, _Alloc>::forward_list(size_type
     }
   }
 }
-#  endif
 
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 forward_list<_Tp, _Alloc>::forward_list(size_type __n, const value_type& __v) {
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
index 206854560c19f..2cfd829a1eb23 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
@@ -21,7 +21,7 @@
 
 template <class T, class Allocator>
 void check_allocator(unsigned n, Allocator const& alloc = Allocator()) {
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   typedef std::forward_list<T, Allocator> C;
   C d(n, alloc);
   assert(d.get_allocator() == alloc);

>From c16e921d91bf3e27ab50be188c3f63b97d42864f Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 12:43:05 -0400
Subject: [PATCH 02/13] [libc++] Apply LWG 2210 as a DR for all containers

---
 libcxx/include/__vector/vector.h              |  2 -
 libcxx/include/__vector/vector_bool.h         |  4 --
 libcxx/include/deque                          |  6 +--
 libcxx/include/list                           |  6 +--
 libcxx/include/map                            | 16 ++------
 libcxx/include/set                            | 16 ++------
 libcxx/include/unordered_map                  | 36 ++++++++---------
 libcxx/include/unordered_set                  | 36 ++++++-----------
 libcxx/include/vector                         |  4 +-
 .../map.cons/iter_iter_comp_alloc.pass.cpp    |  2 +-
 .../iter_iter_comp_alloc.pass.cpp             | 40 +++++++++++++++++++
 .../multiset.cons/iter_iter_alloc.pass.cpp    |  2 +-
 .../set/set.cons/iter_iter_alloc.pass.cpp     |  2 +-
 .../sequences/deque/deque.cons/size.pass.cpp  |  7 ++--
 .../list/list.cons/size_type.pass.cpp         |  7 ++--
 .../vector.bool/construct_size.pass.cpp       |  3 +-
 .../vector.cons/construct_size.pass.cpp       |  2 +-
 .../init_size_allocator.pass.cpp              |  2 +-
 .../init_size_hash_allocator.pass.cpp         |  2 +-
 .../iter_iter_size_allocator.pass.cpp         |  2 +-
 .../iter_iter_size_hash_allocator.pass.cpp    |  2 +-
 .../unord.map.cnstr/size_allocator.pass.cpp   |  2 +-
 .../size_hash_allocator.pass.cpp              |  2 +-
 .../init_size_allocator.pass.cpp              |  2 +-
 .../init_size_hash_allocator.pass.cpp         |  2 +-
 .../iter_iter_size_allocator.pass.cpp         |  2 +-
 .../iter_iter_size_hash_allocator.pass.cpp    |  2 +-
 .../size_allocator.pass.cpp                   |  2 +-
 .../size_hash_allocator.pass.cpp              |  2 +-
 .../init_size_allocator.pass.cpp              |  2 +-
 .../init_size_hash_allocator.pass.cpp         |  2 +-
 .../iter_iter_size_allocator.pass.cpp         |  2 +-
 .../iter_iter_size_hash_allocator.pass.cpp    |  2 +-
 .../size_allocator.pass.cpp                   |  2 +-
 .../size_hash_allocator.pass.cpp              |  2 +-
 .../init_size_allocator.pass.cpp              |  2 +-
 .../init_size_hash_allocator.pass.cpp         |  2 +-
 .../iter_iter_size_allocator.pass.cpp         |  2 +-
 .../iter_iter_size_hash_allocator.pass.cpp    |  2 +-
 .../unord.set.cnstr/size_allocator.pass.cpp   |  2 +-
 .../size_hash_allocator.pass.cpp              |  2 +-
 41 files changed, 120 insertions(+), 119 deletions(-)

diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2a081e3cdb1e2..e9b8c4090419c 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -149,7 +149,6 @@ class vector {
     __guard.__complete();
   }
 
-#if _LIBCPP_STD_VER >= 14
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI explicit vector(size_type __n, const allocator_type& __a)
       : __alloc_(__a) {
     auto __guard = std::__make_exception_guard(__destroy_vector(*this));
@@ -159,7 +158,6 @@ class vector {
     }
     __guard.__complete();
   }
-#endif
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector(size_type __n, const value_type& __x) {
     auto __guard = std::__make_exception_guard(__destroy_vector(*this));
diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index f73e086478a70..6dc904e6d3bd9 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -152,9 +152,7 @@ class vector<bool, _Allocator> {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~vector() { __destroy_vector (*this)(); }
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit vector(size_type __n);
-#if _LIBCPP_STD_VER >= 14
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit vector(size_type __n, const allocator_type& __a);
-#endif
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector(size_type __n, const value_type& __v);
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
   vector(size_type __n, const value_type& __v, const allocator_type& __a);
@@ -605,7 +603,6 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(size_type __n)
   }
 }
 
-#if _LIBCPP_STD_VER >= 14
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(size_type __n, const allocator_type& __a)
     : __begin_(nullptr), __size_(0), __cap_(0), __alloc_(static_cast<__storage_allocator>(__a)) {
@@ -615,7 +612,6 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(size_type __n, co
     __size_ = __n;
   }
 }
-#endif
 
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(size_type __n, const value_type& __x)
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 7aee552c63d37..5a4de99c7f143 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -40,7 +40,7 @@ public:
     deque() noexcept(is_nothrow_default_constructible<allocator_type>::value);
     explicit deque(const allocator_type& a);
     explicit deque(size_type n);
-    explicit deque(size_type n, const allocator_type& a); // C++14
+    explicit deque(size_type n, const allocator_type& a);
     deque(size_type n, const value_type& v);
     deque(size_type n, const value_type& v, const allocator_type& a);
     template <class InputIterator>
@@ -644,9 +644,7 @@ public:
   }
 
   explicit _LIBCPP_HIDE_FROM_ABI deque(size_type __n);
-#  if _LIBCPP_STD_VER >= 14
   explicit _LIBCPP_HIDE_FROM_ABI deque(size_type __n, const _Allocator& __a);
-#  endif
   _LIBCPP_HIDE_FROM_ABI deque(size_type __n, const value_type& __v);
 
   template <__enable_if_t<__is_allocator_v<_Allocator>, int> = 0>
@@ -1323,7 +1321,6 @@ deque<_Tp, _Allocator>::deque(size_type __n) : __start_(0), __size_(0) {
     __append(__n);
 }
 
-#  if _LIBCPP_STD_VER >= 14
 template <class _Tp, class _Allocator>
 deque<_Tp, _Allocator>::deque(size_type __n, const _Allocator& __a)
     : __map_(__pointer_allocator(__a)), __start_(0), __size_(0), __alloc_(__a) {
@@ -1331,7 +1328,6 @@ deque<_Tp, _Allocator>::deque(size_type __n, const _Allocator& __a)
   if (__n > 0)
     __append(__n);
 }
-#  endif
 
 template <class _Tp, class _Allocator>
 deque<_Tp, _Allocator>::deque(size_type __n, const value_type& __v) : __start_(0), __size_(0) {
diff --git a/libcxx/include/list b/libcxx/include/list
index 3223f25e1cfba..8747ebbbcbfaa 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -39,7 +39,7 @@ public:
         noexcept(is_nothrow_default_constructible<allocator_type>::value);
     explicit list(const allocator_type& a);
     explicit list(size_type n);
-    explicit list(size_type n, const allocator_type& a); // C++14
+    explicit list(size_type n, const allocator_type& a);
     list(size_type n, const value_type& value);
     list(size_type n, const value_type& value, const allocator_type& a);
     template <class Iter>
@@ -708,9 +708,7 @@ public:
       _NOEXCEPT_(is_nothrow_default_constructible<__node_allocator>::value) {}
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit list(const allocator_type& __a) : __base(__a) {}
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit list(size_type __n);
-#  if _LIBCPP_STD_VER >= 14
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit list(size_type __n, const allocator_type& __a);
-#  endif
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list(size_type __n, const value_type& __x);
   template <__enable_if_t<__is_allocator_v<_Alloc>, int> = 0>
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
@@ -1066,13 +1064,11 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 list<_Tp, _Alloc>::list(size_type __n) {
 #  endif
 }
 
-#  if _LIBCPP_STD_VER >= 14
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 list<_Tp, _Alloc>::list(size_type __n, const allocator_type& __a) : __base(__a) {
   for (; __n > 0; --__n)
     emplace_back();
 }
-#  endif
 
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 list<_Tp, _Alloc>::list(size_type __n, const value_type& __x) {
diff --git a/libcxx/include/map b/libcxx/include/map
index de0a475ee572f..0afa72662aa32 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -84,12 +84,12 @@ public:
     constexpr map(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a);
     template <class InputIterator>
         constexpr map(InputIterator first, InputIterator last, const allocator_type& a)
-            : map(first, last, Compare(), a) {}                                                               // C++14
+            : map(first, last, Compare(), a) {}
     template<container-compatible-range<value_type> R>
       constexpr map(from_range_t, R&& rg, const Allocator& a))
         : map(from_range, std::forward<R>(rg), Compare(), a) { }                                              // C++23
     constexpr map(initializer_list<value_type> il, const allocator_type& a)
-        : map(il, Compare(), a) {}                                                                            // C++14
+        : map(il, Compare(), a) {}
    constexpr ~map();
 
     constexpr map& operator=(const map& m);
@@ -368,12 +368,12 @@ public:
              const allocator_type& a);
     template <class InputIterator>
         multimap(InputIterator first, InputIterator last, const allocator_type& a)
-            : multimap(first, last, Compare(), a) {} // C++14
+            : multimap(first, last, Compare(), a) {}
     template<container-compatible-range<value_type> R>
       multimap(from_range_t, R&& rg, const Allocator& a))
         : multimap(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
     multimap(initializer_list<value_type> il, const allocator_type& a)
-        : multimap(il, Compare(), a) {} // C++14
+        : multimap(il, Compare(), a) {}
     ~multimap();
 
     multimap& operator=(const multimap& m);
@@ -1035,12 +1035,10 @@ public:
   }
 #  endif
 
-#  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
-#  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
@@ -1073,10 +1071,8 @@ public:
     insert(__il.begin(), __il.end());
   }
 
-#    if _LIBCPP_STD_VER >= 14
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 map(initializer_list<value_type> __il, const allocator_type& __a)
       : map(__il, key_compare(), __a) {}
-#    endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 map& operator=(initializer_list<value_type> __il) {
     clear();
@@ -1817,12 +1813,10 @@ public:
   }
 #  endif
 
-#  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multimap(__f, __l, key_compare(), __a) {}
-#  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
@@ -1856,11 +1850,9 @@ public:
     insert(__il.begin(), __il.end());
   }
 
-#    if _LIBCPP_STD_VER >= 14
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(initializer_list<value_type> __il, const allocator_type& __a)
       : multimap(__il, key_compare(), __a) {}
-#    endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap& operator=(initializer_list<value_type> __il) {
     clear();
diff --git a/libcxx/include/set b/libcxx/include/set
index 3f2d2cd164d2d..2b7755b057977 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -71,12 +71,12 @@ public:
         const allocator_type& a);
     template <class InputIterator>
         set(InputIterator first, InputIterator last, const allocator_type& a)
-            : set(first, last, Compare(), a) {}  // C++14
+            : set(first, last, Compare(), a) {}
     template<container-compatible-range<value_type> R>
       set(from_range_t, R&& rg, const Allocator& a))
         : set(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
     set(initializer_list<value_type> il, const allocator_type& a)
-        : set(il, Compare(), a) {}  // C++14
+        : set(il, Compare(), a) {}
     ~set();
 
     set& operator=(const set& s);
@@ -316,12 +316,12 @@ public:
              const allocator_type& a);
     template <class InputIterator>
         multiset(InputIterator first, InputIterator last, const allocator_type& a)
-            : set(first, last, Compare(), a) {}  // C++14
+            : set(first, last, Compare(), a) {}
     template<container-compatible-range<value_type> R>
       multiset(from_range_t, R&& rg, const Allocator& a))
         : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
     multiset(initializer_list<value_type> il, const allocator_type& a)
-        : set(il, Compare(), a) {}  // C++14
+        : set(il, Compare(), a) {}
     ~multiset();
 
     multiset& operator=(const multiset& s);
@@ -650,11 +650,9 @@ public:
   }
 #  endif
 
-#  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI set(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : set(__f, __l, key_compare(), __a) {}
-#  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
@@ -687,10 +685,8 @@ public:
     insert(__il.begin(), __il.end());
   }
 
-#    if _LIBCPP_STD_VER >= 14
   _LIBCPP_HIDE_FROM_ABI set(initializer_list<value_type> __il, const allocator_type& __a)
       : set(__il, key_compare(), __a) {}
-#    endif
 
   _LIBCPP_HIDE_FROM_ABI set& operator=(initializer_list<value_type> __il) {
     clear();
@@ -1123,11 +1119,9 @@ public:
     insert(__f, __l);
   }
 
-#  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multiset(__f, __l, key_compare(), __a) {}
-#  endif
 
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI
@@ -1176,10 +1170,8 @@ public:
     insert(__il.begin(), __il.end());
   }
 
-#    if _LIBCPP_STD_VER >= 14
   _LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const allocator_type& __a)
       : multiset(__il, key_compare(), __a) {}
-#    endif
 
   _LIBCPP_HIDE_FROM_ABI multiset& operator=(initializer_list<value_type> __il) {
     clear();
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 8cefba01fb5f6..b8b160cd892d4 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -77,16 +77,16 @@ public:
                   const hasher& hf = hasher(), const key_equal& eql = key_equal(),
                   const allocator_type& a = allocator_type());
     unordered_map(size_type n, const allocator_type& a)
-      : unordered_map(n, hasher(), key_equal(), a) {}  // C++14
+      : unordered_map(n, hasher(), key_equal(), a) {}
     unordered_map(size_type n, const hasher& hf, const allocator_type& a)
-      : unordered_map(n, hf, key_equal(), a) {}  // C++14
+      : unordered_map(n, hf, key_equal(), a) {}
     template <class InputIterator>
       unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
-      : unordered_map(f, l, n, hasher(), key_equal(), a) {}  // C++14
+      : unordered_map(f, l, n, hasher(), key_equal(), a) {}
     template <class InputIterator>
       unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
         const allocator_type& a)
-      : unordered_map(f, l, n, hf, key_equal(), a) {}  // C++14
+      : unordered_map(f, l, n, hf, key_equal(), a) {}
     template<container-compatible-range<value_type> R>
       unordered_map(from_range_t, R&& rg, size_type n, const allocator_type& a)
         : unordered_map(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
@@ -94,10 +94,10 @@ public:
       unordered_map(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
         : unordered_map(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
     unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a)
-      : unordered_map(il, n, hasher(), key_equal(), a) {}  // C++14
+      : unordered_map(il, n, hasher(), key_equal(), a) {}
     unordered_map(initializer_list<value_type> il, size_type n, const hasher& hf,
       const allocator_type& a)
-      : unordered_map(il, n, hf, key_equal(), a) {}  // C++14
+      : unordered_map(il, n, hf, key_equal(), a) {}
     ~unordered_map();
     unordered_map& operator=(const unordered_map&);
     unordered_map& operator=(unordered_map&&)
@@ -363,16 +363,16 @@ public:
                   const hasher& hf = hasher(), const key_equal& eql = key_equal(),
                   const allocator_type& a = allocator_type());
     unordered_multimap(size_type n, const allocator_type& a)
-      : unordered_multimap(n, hasher(), key_equal(), a) {}  // C++14
+      : unordered_multimap(n, hasher(), key_equal(), a) {}
     unordered_multimap(size_type n, const hasher& hf, const allocator_type& a)
-      : unordered_multimap(n, hf, key_equal(), a) {}  // C++14
+      : unordered_multimap(n, hf, key_equal(), a) {}
     template <class InputIterator>
       unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
-      : unordered_multimap(f, l, n, hasher(), key_equal(), a) {}  // C++14
+      : unordered_multimap(f, l, n, hasher(), key_equal(), a) {}
     template <class InputIterator>
       unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf,
         const allocator_type& a)
-      : unordered_multimap(f, l, n, hf, key_equal(), a) {}  // C++14
+      : unordered_multimap(f, l, n, hf, key_equal(), a) {}
     template<container-compatible-range<value_type> R>
       unordered_multimap(from_range_t, R&& rg, size_type n, const allocator_type& a)
         : unordered_multimap(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
@@ -380,10 +380,10 @@ public:
       unordered_multimap(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
         : unordered_multimap(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
     unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a)
-      : unordered_multimap(il, n, hasher(), key_equal(), a) {}  // C++14
+      : unordered_multimap(il, n, hasher(), key_equal(), a) {}
     unordered_multimap(initializer_list<value_type> il, size_type n, const hasher& hf,
       const allocator_type& a)
-      : unordered_multimap(il, n, hf, key_equal(), a) {}  // C++14
+      : unordered_multimap(il, n, hf, key_equal(), a) {}
     ~unordered_multimap();
     unordered_multimap& operator=(const unordered_multimap&);
     unordered_multimap& operator=(unordered_multimap&&)
@@ -993,7 +993,6 @@ public:
       const key_equal& __eql,
       const allocator_type& __a);
 #  endif // _LIBCPP_CXX03_LANG
-#  if _LIBCPP_STD_VER >= 14
   _LIBCPP_HIDE_FROM_ABI unordered_map(size_type __n, const allocator_type& __a)
       : unordered_map(__n, hasher(), key_equal(), __a) {}
   _LIBCPP_HIDE_FROM_ABI unordered_map(size_type __n, const hasher& __hf, const allocator_type& __a)
@@ -1007,7 +1006,7 @@ public:
       _InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_map(__first, __last, __n, __hf, key_equal(), __a) {}
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
   _LIBCPP_HIDE_FROM_ABI unordered_map(from_range_t, _Range&& __range, size_type __n, const allocator_type& __a)
       : unordered_map(from_range, std::forward<_Range>(__range), __n, hasher(), key_equal(), __a) {}
@@ -1016,8 +1015,9 @@ public:
   _LIBCPP_HIDE_FROM_ABI
   unordered_map(from_range_t, _Range&& __range, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_map(from_range, std::forward<_Range>(__range), __n, __hf, key_equal(), __a) {}
-#    endif
+#  endif
 
+#  ifndef _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI unordered_map(initializer_list<value_type> __il, size_type __n, const allocator_type& __a)
       : unordered_map(__il, __n, hasher(), key_equal(), __a) {}
   _LIBCPP_HIDE_FROM_ABI
@@ -1787,7 +1787,6 @@ public:
       const key_equal& __eql,
       const allocator_type& __a);
 #  endif // _LIBCPP_CXX03_LANG
-#  if _LIBCPP_STD_VER >= 14
   _LIBCPP_HIDE_FROM_ABI unordered_multimap(size_type __n, const allocator_type& __a)
       : unordered_multimap(__n, hasher(), key_equal(), __a) {}
   _LIBCPP_HIDE_FROM_ABI unordered_multimap(size_type __n, const hasher& __hf, const allocator_type& __a)
@@ -1801,7 +1800,7 @@ public:
       _InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_multimap(__first, __last, __n, __hf, key_equal(), __a) {}
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
   _LIBCPP_HIDE_FROM_ABI unordered_multimap(from_range_t, _Range&& __range, size_type __n, const allocator_type& __a)
       : unordered_multimap(from_range, std::forward<_Range>(__range), __n, hasher(), key_equal(), __a) {}
@@ -1810,8 +1809,9 @@ public:
   _LIBCPP_HIDE_FROM_ABI
   unordered_multimap(from_range_t, _Range&& __range, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_multimap(from_range, std::forward<_Range>(__range), __n, __hf, key_equal(), __a) {}
-#    endif
+#  endif
 
+#  ifndef _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI unordered_multimap(initializer_list<value_type> __il, size_type __n, const allocator_type& __a)
       : unordered_multimap(__il, __n, hasher(), key_equal(), __a) {}
   _LIBCPP_HIDE_FROM_ABI
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index d54d59403b038..10f8b730b842b 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -76,22 +76,22 @@ public:
     unordered_set(initializer_list<value_type>, size_type n = 0,
                   const hasher& hf = hasher(), const key_equal& eql = key_equal(),
                   const allocator_type& a = allocator_type());
-    unordered_set(size_type n, const allocator_type& a); // C++14
-    unordered_set(size_type n, const hasher& hf, const allocator_type& a); // C++14
+    unordered_set(size_type n, const allocator_type& a);
+    unordered_set(size_type n, const hasher& hf, const allocator_type& a);
     template <class InputIterator>
-      unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a); // C++14
+      unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
     template <class InputIterator>
       unordered_set(InputIterator f, InputIterator l, size_type n,
-                    const hasher& hf,  const allocator_type& a); // C++14
+                    const hasher& hf,  const allocator_type& a);
     template<container-compatible-range<value_type> R>
       unordered_set(from_range_t, R&& rg, size_type n, const allocator_type& a)
         : unordered_set(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
     template<container-compatible-range<value_type> R>
       unordered_set(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
         : unordered_set(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
-    unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a); // C++14
+    unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a);
     unordered_set(initializer_list<value_type> il, size_type n,
-                  const hasher& hf,  const allocator_type& a); // C++14
+                  const hasher& hf,  const allocator_type& a);
     ~unordered_set();
     unordered_set& operator=(const unordered_set&);
     unordered_set& operator=(unordered_set&&)
@@ -324,22 +324,22 @@ public:
     unordered_multiset(initializer_list<value_type>, size_type n = /see below/,
                   const hasher& hf = hasher(), const key_equal& eql = key_equal(),
                   const allocator_type& a = allocator_type());
-    unordered_multiset(size_type n, const allocator_type& a); // C++14
-    unordered_multiset(size_type n, const hasher& hf, const allocator_type& a); // C++14
+    unordered_multiset(size_type n, const allocator_type& a);
+    unordered_multiset(size_type n, const hasher& hf, const allocator_type& a);
     template <class InputIterator>
-      unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a); // C++14
+      unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
     template <class InputIterator>
       unordered_multiset(InputIterator f, InputIterator l, size_type n,
-                         const hasher& hf, const allocator_type& a); // C++14
+                         const hasher& hf, const allocator_type& a);
     template<container-compatible-range<value_type> R>
       unordered_multiset(from_range_t, R&& rg, size_type n, const allocator_type& a)
         : unordered_multiset(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
     template<container-compatible-range<value_type> R>
       unordered_multiset(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
         : unordered_multiset(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
-    unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a); // C++14
+    unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a);
     unordered_multiset(initializer_list<value_type> il, size_type n,
-                       const hasher& hf,  const allocator_type& a); // C++14
+                       const hasher& hf,  const allocator_type& a);
     ~unordered_multiset();
     unordered_multiset& operator=(const unordered_multiset&);
     unordered_multiset& operator=(unordered_multiset&&)
@@ -634,12 +634,10 @@ public:
   _LIBCPP_HIDE_FROM_ABI unordered_set() _NOEXCEPT_(is_nothrow_default_constructible<__table>::value) {}
   explicit _LIBCPP_HIDE_FROM_ABI
   unordered_set(size_type __n, const hasher& __hf = hasher(), const key_equal& __eql = key_equal());
-#  if _LIBCPP_STD_VER >= 14
   inline _LIBCPP_HIDE_FROM_ABI unordered_set(size_type __n, const allocator_type& __a)
       : unordered_set(__n, hasher(), key_equal(), __a) {}
   inline _LIBCPP_HIDE_FROM_ABI unordered_set(size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_set(__n, __hf, key_equal(), __a) {}
-#  endif
   _LIBCPP_HIDE_FROM_ABI
   unordered_set(size_type __n, const hasher& __hf, const key_equal& __eql, const allocator_type& __a);
   template <class _InputIterator>
@@ -677,7 +675,6 @@ public:
   }
 #  endif
 
-#  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   inline _LIBCPP_HIDE_FROM_ABI
   unordered_set(_InputIterator __first, _InputIterator __last, size_type __n, const allocator_type& __a)
@@ -686,7 +683,6 @@ public:
   _LIBCPP_HIDE_FROM_ABI unordered_set(
       _InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_set(__first, __last, __n, __hf, key_equal(), __a) {}
-#  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
@@ -717,14 +713,12 @@ public:
       const hasher& __hf,
       const key_equal& __eql,
       const allocator_type& __a);
-#    if _LIBCPP_STD_VER >= 14
   inline _LIBCPP_HIDE_FROM_ABI
   unordered_set(initializer_list<value_type> __il, size_type __n, const allocator_type& __a)
       : unordered_set(__il, __n, hasher(), key_equal(), __a) {}
   inline _LIBCPP_HIDE_FROM_ABI
   unordered_set(initializer_list<value_type> __il, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_set(__il, __n, __hf, key_equal(), __a) {}
-#    endif
 #  endif // _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI ~unordered_set() {
     static_assert(sizeof(std::__diagnose_unordered_container_requirements<_Value, _Hash, _Pred>(0)), "");
@@ -1250,12 +1244,10 @@ public:
   unordered_multiset(size_type __n, const hasher& __hf = hasher(), const key_equal& __eql = key_equal());
   _LIBCPP_HIDE_FROM_ABI
   unordered_multiset(size_type __n, const hasher& __hf, const key_equal& __eql, const allocator_type& __a);
-#  if _LIBCPP_STD_VER >= 14
   inline _LIBCPP_HIDE_FROM_ABI unordered_multiset(size_type __n, const allocator_type& __a)
       : unordered_multiset(__n, hasher(), key_equal(), __a) {}
   inline _LIBCPP_HIDE_FROM_ABI unordered_multiset(size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_multiset(__n, __hf, key_equal(), __a) {}
-#  endif
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI unordered_multiset(_InputIterator __first, _InputIterator __last);
   template <class _InputIterator>
@@ -1291,7 +1283,6 @@ public:
   }
 #  endif
 
-#  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   inline _LIBCPP_HIDE_FROM_ABI
   unordered_multiset(_InputIterator __first, _InputIterator __last, size_type __n, const allocator_type& __a)
@@ -1300,7 +1291,6 @@ public:
   inline _LIBCPP_HIDE_FROM_ABI unordered_multiset(
       _InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_multiset(__first, __last, __n, __hf, key_equal(), __a) {}
-#  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
@@ -1331,14 +1321,12 @@ public:
       const hasher& __hf,
       const key_equal& __eql,
       const allocator_type& __a);
-#    if _LIBCPP_STD_VER >= 14
   inline _LIBCPP_HIDE_FROM_ABI
   unordered_multiset(initializer_list<value_type> __il, size_type __n, const allocator_type& __a)
       : unordered_multiset(__il, __n, hasher(), key_equal(), __a) {}
   inline _LIBCPP_HIDE_FROM_ABI
   unordered_multiset(initializer_list<value_type> __il, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_multiset(__il, __n, __hf, key_equal(), __a) {}
-#    endif
 #  endif // _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI ~unordered_multiset() {
     static_assert(sizeof(std::__diagnose_unordered_container_requirements<_Value, _Hash, _Pred>(0)), "");
diff --git a/libcxx/include/vector b/libcxx/include/vector
index f289013a682bc..b136cca1f6050 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -39,7 +39,7 @@ public:
         noexcept(is_nothrow_default_constructible<allocator_type>::value);
     explicit vector(const allocator_type&);
     explicit vector(size_type n);
-    explicit vector(size_type n, const allocator_type&); // C++14
+    explicit vector(size_type n, const allocator_type&);
     vector(size_type n, const value_type& value, const allocator_type& = allocator_type());
     template <class InputIterator>
         vector(InputIterator first, InputIterator last, const allocator_type& = allocator_type());
@@ -171,7 +171,7 @@ public:
     vector()
         noexcept(is_nothrow_default_constructible<allocator_type>::value);
     explicit vector(const allocator_type&) noexcept;
-    explicit vector(size_type n, const allocator_type& a = allocator_type()); // C++14
+    explicit vector(size_type n, const allocator_type& a = allocator_type());
     vector(size_type n, const value_type& value, const allocator_type& = allocator_type());
     template <class InputIterator>
         vector(InputIterator first, InputIterator last, const allocator_type& = allocator_type());
diff --git a/libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp b/libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp
index 014f9c5960e5b..859a24911bab2 100644
--- a/libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp
@@ -72,7 +72,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*std::next(m.begin()) == V(2, 1));
     assert(*std::next(m.begin(), 2) == V(3, 1));
   }
-#  if TEST_STD_VER > 11
+#  if TEST_STD_VER >= 11
   {
     typedef std::pair<const int, double> V;
     V ar[] = {
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.cons/iter_iter_comp_alloc.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.cons/iter_iter_comp_alloc.pass.cpp
index fb3f4b831cc0d..08320024cc5bd 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.cons/iter_iter_comp_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.cons/iter_iter_comp_alloc.pass.cpp
@@ -115,6 +115,46 @@ bool test() {
     assert(*std::next(m.begin(), 7) == V(3, 1.5));
     assert(*std::next(m.begin(), 8) == V(3, 2));
   }
+  {
+    typedef std::pair<const int, double> V;
+    V ar[] = {
+        V(1, 1),
+        V(1, 1.5),
+        V(1, 2),
+        V(2, 1),
+        V(2, 1.5),
+        V(2, 2),
+        V(3, 1),
+        V(3, 1.5),
+        V(3, 2),
+    };
+    {
+      typedef min_allocator<V> A;
+      typedef test_less<int> C;
+      A a;
+      std::multimap<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]), a);
+
+      assert(m.size() == 9);
+      assert(std::distance(m.begin(), m.end()) == 9);
+      assert(*m.begin() == V(1, 1));
+      assert(*std::next(m.begin(), 3) == V(2, 1));
+      assert(*std::next(m.begin(), 6) == V(3, 1));
+      assert(m.get_allocator() == a);
+    }
+    {
+      typedef explicit_allocator<V> A;
+      typedef test_less<int> C;
+      A a;
+      std::multimap<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]), a);
+
+      assert(m.size() == 9);
+      assert(std::distance(m.begin(), m.end()) == 9);
+      assert(*m.begin() == V(1, 1));
+      assert(*std::next(m.begin(), 3) == V(2, 1));
+      assert(*std::next(m.begin(), 6) == V(3, 1));
+      assert(m.get_allocator() == a);
+    }
+  }
 #endif
 
   return true;
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
index 50f8fda2a676b..c2636ff7ec7e6 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
@@ -48,7 +48,7 @@ int main(int, char**) {
     assert(*std::next(m.begin(), 7) == 3);
     assert(*std::next(m.begin(), 8) == 3);
   }
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
diff --git a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
index 6a3146fcd2b0a..45cb62afa17e5 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
@@ -46,7 +46,7 @@ int main(int, char**) {
     assert(*std::next(m.begin()) == 2);
     assert(*std::next(m.begin(), 2) == 3);
   }
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
diff --git a/libcxx/test/std/containers/sequences/deque/deque.cons/size.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.cons/size.pass.cpp
index f8f42bd668f83..c31caa6c2d414 100644
--- a/libcxx/test/std/containers/sequences/deque/deque.cons/size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/deque/deque.cons/size.pass.cpp
@@ -9,6 +9,7 @@
 // <deque>
 
 // explicit deque(size_type n);
+// explicit deque(size_type n, const Allocator& a);
 
 #include "asan_testing.h"
 #include <deque>
@@ -22,7 +23,7 @@
 
 template <class T, class Allocator>
 void test2(unsigned n) {
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   typedef std::deque<T, Allocator> C;
   typedef typename C::const_iterator const_iterator;
   assert(DefaultOnly::count == 0);
@@ -62,7 +63,7 @@ void test1(unsigned n) {
 
 template <class T, class Allocator>
 void test3(unsigned n, Allocator const& alloc = Allocator()) {
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   typedef std::deque<T, Allocator> C;
   {
     C d(n, alloc);
@@ -102,7 +103,7 @@ int main(int, char**) {
   test<DefaultOnly, min_allocator<DefaultOnly> >(4095);
 #endif
 
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   test3<DefaultOnly, std::allocator<DefaultOnly>>(1023);
   test3<int, std::allocator<int>>(1);
   test3<int, min_allocator<int>>(3);
diff --git a/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp b/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
index 55371e8354a9e..fed3d1caa4dda 100644
--- a/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
+++ b/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
@@ -8,7 +8,8 @@
 
 // <list>
 
-// explicit list(size_type n); // constexpr since C++26
+// explicit list(size_type n);                       // constexpr since C++26
+// explicit list(size_type n, const Allocator& a);   // constexpr since C++26
 
 #include <list>
 #include <cassert>
@@ -21,7 +22,7 @@
 
 template <class T, class Allocator>
 TEST_CONSTEXPR_CXX26 void test1(unsigned n, Allocator const& alloc = Allocator()) {
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   typedef std::list<T, Allocator> C;
   {
     C d(n, alloc);
@@ -59,7 +60,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     ++i;
     assert(*i == 0);
   }
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   {
     typedef std::list<int, min_allocator<int> > C;
     C l(3, min_allocator<int>());
diff --git a/libcxx/test/std/containers/sequences/vector.bool/construct_size.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/construct_size.pass.cpp
index 80b3fe307c93a..f97396d67ae9d 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/construct_size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector.bool/construct_size.pass.cpp
@@ -10,6 +10,7 @@
 // vector<bool>
 
 // explicit vector(size_type n);
+// explicit vector(size_type n, const Allocator& alloc = Allocator());
 
 #include <vector>
 #include <cassert>
@@ -21,7 +22,7 @@
 template <class C>
 TEST_CONSTEXPR_CXX20 void
 test2(typename C::size_type n, typename C::allocator_type const& a = typename C::allocator_type()) {
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 11
   C c(n, a);
   LIBCPP_ASSERT(c.__invariants());
   assert(c.size() == n);
diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp
index cbae3653d995f..0902a4070b7aa 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp
@@ -38,7 +38,7 @@ test(typename C::size_type n, typename C::allocator_type const& a = typename C::
   }
 
   // Test with a custom allocator
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 11
   {
     C c(n, a);
     LIBCPP_ASSERT(c.__invariants());
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_allocator.pass.cpp
index e29ecea327971..3fd4e7fcfa5f5 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_hash_allocator.pass.cpp
index c2adf98c7eaca..31d4ce0adf437 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_allocator.pass.cpp
index 1039e1d92be79..bf1536dd0b44a 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_hash_allocator.pass.cpp
index b6e06df9c8fe2..a5f9ddeb0d825 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_allocator.pass.cpp
index 450fb7b5e40d4..8e4d7ff8e1fbe 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_hash_allocator.pass.cpp
index a9f73f3f45fe2..19bbb305fe10f 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_allocator.pass.cpp
index 7ec2dd2efb895..a8371367f7ead 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_hash_allocator.pass.cpp
index eab5d60fbf6f4..ec566bb75a2f9 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_allocator.pass.cpp
index ad6ad9d1ad1ef..5e694b5d5ed3a 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_hash_allocator.pass.cpp
index ee35b36cf7519..7592b10b792ec 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_allocator.pass.cpp
index d49be5518922a..70798e3920a7d 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_hash_allocator.pass.cpp
index f26fae2a2a81d..42d75cfa28ba0 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 
diff --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_allocator.pass.cpp
index bb0cc2441bfb7..314e60381dfcc 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 
diff --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_hash_allocator.pass.cpp
index 24b802f4fcca2..f962ddd8f0344 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 
diff --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_allocator.pass.cpp
index 994ec058fae94..6eb48201d116c 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 
diff --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_hash_allocator.pass.cpp
index 138af889d5978..44e572e10071f 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 
diff --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_allocator.pass.cpp
index e536644470122..ddf0a4d02b932 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 
diff --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_hash_allocator.pass.cpp
index 6ea22dab1aac9..531215e0671e3 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 
diff --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_allocator.pass.cpp
index 7346a951b1976..90c6d9720e90c 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 
diff --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_hash_allocator.pass.cpp
index 704fff0f51cca..306ec62c81d7b 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 
diff --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_allocator.pass.cpp
index 414485466d71e..bdd2d841283a6 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 
diff --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_hash_allocator.pass.cpp
index ba986eba5f71c..48d64a8860e23 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 
diff --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_allocator.pass.cpp
index 99abd12745eca..6af94925a5a2b 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 
diff --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_hash_allocator.pass.cpp
index e44854eb0a132..11c3e7bd2ee0c 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 

>From 6f65da7e4fcf49cdbdfb26e9de28f18512d0b780 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 15:00:01 -0400
Subject: [PATCH 03/13] [libc++] Remove redundant TEST_STD_VER guard

---
 .../associative/map/map.cons/iter_iter_comp_alloc.pass.cpp      | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp b/libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp
index 859a24911bab2..ce2b2ef185d90 100644
--- a/libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp
@@ -72,7 +72,6 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*std::next(m.begin()) == V(2, 1));
     assert(*std::next(m.begin(), 2) == V(3, 1));
   }
-#  if TEST_STD_VER >= 11
   {
     typedef std::pair<const int, double> V;
     V ar[] = {
@@ -113,7 +112,6 @@ TEST_CONSTEXPR_CXX26 bool test() {
       assert(m.get_allocator() == a);
     }
   }
-#  endif
 #endif
   return true;
 }

>From 65c03f06b9bd22b01888624112b77ecdd1367578 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 15:05:45 -0400
Subject: [PATCH 04/13] Format

---
 libcxx/include/map | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/map b/libcxx/include/map
index 0afa72662aa32..c4a5dd2f74d6d 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1850,8 +1850,8 @@ public:
     insert(__il.begin(), __il.end());
   }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multimap(initializer_list<value_type> __il, const allocator_type& __a)
       : multimap(__il, key_compare(), __a) {}
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap& operator=(initializer_list<value_type> __il) {

>From 9637a5399748b17f8eabdd8f2370abc5ac9f8e40 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 15:41:48 -0400
Subject: [PATCH 05/13] Expand beyond C++11

---
 .../forwardlist/forwardlist.cons/size.pass.cpp      | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
index 2cfd829a1eb23..760b3a985678a 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
@@ -21,15 +21,10 @@
 
 template <class T, class Allocator>
 void check_allocator(unsigned n, Allocator const& alloc = Allocator()) {
-#if TEST_STD_VER >= 11
   typedef std::forward_list<T, Allocator> C;
   C d(n, alloc);
   assert(d.get_allocator() == alloc);
   assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == n);
-#else
-  ((void)n);
-  ((void)alloc);
-#endif
 }
 
 int main(int, char**) {
@@ -54,20 +49,18 @@ int main(int, char**) {
     }
     assert(n == N);
   }
-#if TEST_STD_VER >= 11
   {
     typedef DefaultOnly T;
-    typedef std::forward_list<T, min_allocator<T>> C;
+    typedef std::forward_list<T, min_allocator<T> > C;
     unsigned N = 10;
     C c(N);
     unsigned n = 0;
     for (C::const_iterator i = c.begin(), e = c.end(); i != e; ++i, ++n)
       assert(*i == T());
     assert(n == N);
-    check_allocator<T, min_allocator<T>>(0);
-    check_allocator<T, min_allocator<T>>(3);
+    check_allocator<T, min_allocator<T> >(0);
+    check_allocator<T, min_allocator<T> >(3);
   }
-#endif
 
   return 0;
 }

>From 2a476d5a8b8b86e8030eb4bc21f81dd1d492a536 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 15:42:28 -0400
Subject: [PATCH 06/13] Add XFAIL

---
 .../sequences/forwardlist/forwardlist.cons/size.pass.cpp        | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
index 760b3a985678a..ac6f6c768bc42 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
@@ -11,6 +11,8 @@
 // explicit forward_list(size_type n);                 // constexpr since C++26
 // explicit forward_list(size_type n, const Alloc& a); // constexpr since C++26
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 #include <forward_list>
 #include <cassert>
 #include <cstddef>

>From 3fdd1d2812806e122e537d8c82dd56df4c82ebea Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 15:47:32 -0400
Subject: [PATCH 07/13] Fix broken list ctor in C++03

---
 libcxx/include/list | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/include/list b/libcxx/include/list
index 8747ebbbcbfaa..b4fb9191cd7f7 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -1067,7 +1067,11 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 list<_Tp, _Alloc>::list(size_type __n) {
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 list<_Tp, _Alloc>::list(size_type __n, const allocator_type& __a) : __base(__a) {
   for (; __n > 0; --__n)
+#  ifndef _LIBCPP_CXX03_LANG
     emplace_back();
+#  else
+    push_back(value_type());
+#  endif
 }
 
 template <class _Tp, class _Alloc>

>From a81a73f39da5a6abaabbd37a7133acc37184183c Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 15:51:13 -0400
Subject: [PATCH 08/13] Don't expand beyond C++11

---
 .../forwardlist/forwardlist.cons/size.pass.cpp      | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
index ac6f6c768bc42..623807b0e5a87 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
@@ -23,10 +23,15 @@
 
 template <class T, class Allocator>
 void check_allocator(unsigned n, Allocator const& alloc = Allocator()) {
+#if TEST_STD_VER >= 11
   typedef std::forward_list<T, Allocator> C;
   C d(n, alloc);
   assert(d.get_allocator() == alloc);
   assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == n);
+#else
+  ((void)n);
+  ((void)alloc);
+#endif
 }
 
 int main(int, char**) {
@@ -51,18 +56,20 @@ int main(int, char**) {
     }
     assert(n == N);
   }
+#if TEST_STD_VER >= 11
   {
     typedef DefaultOnly T;
-    typedef std::forward_list<T, min_allocator<T> > C;
+    typedef std::forward_list<T, min_allocator<T>> C;
     unsigned N = 10;
     C c(N);
     unsigned n = 0;
     for (C::const_iterator i = c.begin(), e = c.end(); i != e; ++i, ++n)
       assert(*i == T());
     assert(n == N);
-    check_allocator<T, min_allocator<T> >(0);
-    check_allocator<T, min_allocator<T> >(3);
+    check_allocator<T, min_allocator<T>>(0);
+    check_allocator<T, min_allocator<T>>(3);
   }
+#endif
 
   return 0;
 }

>From f0b7958855d116fb877c7e0e8853526ee42dcfa6 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 15:52:36 -0400
Subject: [PATCH 09/13] Remove XFAIL for now

---
 .../sequences/forwardlist/forwardlist.cons/size.pass.cpp        | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
index 623807b0e5a87..2cfd829a1eb23 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
@@ -11,8 +11,6 @@
 // explicit forward_list(size_type n);                 // constexpr since C++26
 // explicit forward_list(size_type n, const Alloc& a); // constexpr since C++26
 
-// XFAIL: FROZEN-CXX03-HEADERS-FIXME
-
 #include <forward_list>
 #include <cassert>
 #include <cstddef>

>From 100a7e796cadc596cfc7345b39a3db140adfddd0 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 29 Jun 2026 10:06:24 -0400
Subject: [PATCH 10/13] Expand coverage for list allocator/size constructor to
 C++03

---
 .../sequences/list/list.cons/size_type.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp b/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
index fed3d1caa4dda..74d6a04c44fde 100644
--- a/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
+++ b/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
@@ -22,7 +22,6 @@
 
 template <class T, class Allocator>
 TEST_CONSTEXPR_CXX26 void test1(unsigned n, Allocator const& alloc = Allocator()) {
-#if TEST_STD_VER >= 11
   typedef std::list<T, Allocator> C;
   {
     C d(n, alloc);
@@ -30,10 +29,6 @@ TEST_CONSTEXPR_CXX26 void test1(unsigned n, Allocator const& alloc = Allocator()
     assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == n);
     assert(d.get_allocator() == alloc);
   }
-#else
-  ((void)n);
-  ((void)alloc);
-#endif
 }
 
 TEST_CONSTEXPR_CXX26 bool test() {
@@ -60,6 +55,18 @@ TEST_CONSTEXPR_CXX26 bool test() {
     ++i;
     assert(*i == 0);
   }
+  {
+    std::list<int, std::allocator<int> > l(3, std::allocator<int>());
+    assert(l.size() == 3);
+    assert(std::distance(l.begin(), l.end()) == 3);
+    std::list<int, std::allocator<int> >::const_iterator i = l.begin();
+    assert(*i == 0);
+    ++i;
+    assert(*i == 0);
+    ++i;
+    assert(*i == 0);
+    test1<int, std::allocator<int> >(3);
+  }
 #if TEST_STD_VER >= 11
   {
     typedef std::list<int, min_allocator<int> > C;

>From 9b0e808cb149f5ffc14bfaf4371bcaf2c91181c8 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 29 Jun 2026 10:15:28 -0400
Subject: [PATCH 11/13] Same in forward_list

---
 .../forwardlist.cons/size.pass.cpp            | 23 +++++++++++--------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
index 2cfd829a1eb23..ba2061ecb1c71 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
@@ -21,15 +21,10 @@
 
 template <class T, class Allocator>
 void check_allocator(unsigned n, Allocator const& alloc = Allocator()) {
-#if TEST_STD_VER >= 11
   typedef std::forward_list<T, Allocator> C;
   C d(n, alloc);
   assert(d.get_allocator() == alloc);
   assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == n);
-#else
-  ((void)n);
-  ((void)alloc);
-#endif
 }
 
 int main(int, char**) {
@@ -46,14 +41,24 @@ int main(int, char**) {
     unsigned n = 0;
 
     for (C::const_iterator i = c.begin(), e = c.end(); i != e; ++i, ++n) {
-#if TEST_STD_VER >= 11
       assert(*i == T());
-#else
-      ((void)0);
-#endif
     }
     assert(n == N);
   }
+  {
+    typedef DefaultOnly T;
+    typedef std::forward_list<T, std::allocator<T> > C;
+    unsigned N = 10;
+    C c(N, std::allocator<T>());
+    unsigned n = 0;
+
+    for (C::const_iterator i = c.begin(), e = c.end(); i != e; ++i, ++n) {
+      assert(*i == T());
+    }
+    assert(n == N);
+    check_allocator<T, std::allocator<T> >(0);
+    check_allocator<T, std::allocator<T> >(3);
+  }
 #if TEST_STD_VER >= 11
   {
     typedef DefaultOnly T;

>From dbf890498128a9f1aa14ec6a0c5db974ef00e2e5 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 8 Jul 2026 11:16:20 +0800
Subject: [PATCH 12/13] Restore `// XFAIL: FROZEN-CXX03-HEADERS-FIXME`

---
 .../sequences/forwardlist/forwardlist.cons/size.pass.cpp        | 2 ++
 .../std/containers/sequences/list/list.cons/size_type.pass.cpp  | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
index ba2061ecb1c71..89baa06dec475 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
@@ -6,6 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 // <forward_list>
 
 // explicit forward_list(size_type n);                 // constexpr since C++26
diff --git a/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp b/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
index 74d6a04c44fde..4992400c406d7 100644
--- a/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
+++ b/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
@@ -6,6 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 // <list>
 
 // explicit list(size_type n);                       // constexpr since C++26

>From 3e3faa32383d35a93a660fdd1b463480c92f67c0 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 8 Jul 2026 11:20:51 +0800
Subject: [PATCH 13/13] clang-format

---
 libcxx/include/set | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/set b/libcxx/include/set
index 36592610c6112..59aef4a42b428 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -1192,8 +1192,8 @@ public:
   }
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multiset(__f, __l, key_compare(), __a) {}
 
   template <class _InputIterator>
@@ -1247,8 +1247,8 @@ public:
     insert(__il.begin(), __il.end());
   }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(initializer_list<value_type> __il, const allocator_type& __a)
       : multiset(__il, key_compare(), __a) {}
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(initializer_list<value_type> __il) {


From libcxx-commits at lists.llvm.org  Wed Jul  8 02:49:09 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 08 Jul 2026 02:49:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4e1d15.af6ba3ed.36d9db.3126@mx.google.com>


================
@@ -95,35 +95,35 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
       : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
----------------
lknknm wrote:

yeah... I think again it was my clang-format configuration. I'll manually fix that. Thanks :)

https://github.com/llvm/llvm-project/pull/208036

From libcxx-commits at lists.llvm.org  Wed Jul  8 03:02:06 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 08 Jul 2026 03:02:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4e201e.eb24c6ea.27aedc.d435@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/208036

>From 1a1da3d615018ef52c9eefcbb376679f5cdd7505 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 18:00:56 +0200
Subject: [PATCH 1/5] initial: nodiscard implementation to lazy_split_view.h

---
 libcxx/include/__ranges/lazy_split_view.h | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 938dca24cc4fc..01d398a0ac6d8 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -95,14 +95,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
       : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
@@ -111,19 +111,19 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<_View> && forward_range<const _View>
   {
     return __outer_iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
     return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
     if constexpr (forward_range<_View> && forward_range<const _View> && common_range<const _View>) {
       return __outer_iterator<true>{*this, ranges::end(__base_)};
     } else {
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
@@ -342,14 +342,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
-    _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
       return std::move(__i_.__current());
     }
 
-    _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
       __incremented_ = true;
@@ -385,7 +385,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       return __x.__is_done();
     }
 
-    _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
     iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(__i.__outer_current()))) {
       return ranges::iter_move(__i.__outer_current());
     }

>From 845a2090e07f251f9a46f849f149d5662ecdbc45 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 22:49:19 +0200
Subject: [PATCH 2/5] add: initial tests and clang-format fix

---
 libcxx/include/__ranges/lazy_split_view.h     | 12 +++++----
 .../range.lazy.split/nodiscard.verify.cpp     | 27 +++++++++++++++++++
 2 files changed, 34 insertions(+), 5 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 01d398a0ac6d8..5e336cbfcac49 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
+      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
+    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -342,7 +342,9 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
+      return __i_.__current();
+    }
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
@@ -404,8 +406,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&,
-                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, range_value_t<_Range>)
+    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cd15b33b66d59
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  // [range.lazy.split.overview]
+
+  std::string str = "the quick brown fox";
+  char pattern    = ' ';
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::split(str, pattern);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::split(pattern);
+}

>From 054f55611b632030795527d777509c70609e5df0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 22:57:55 +0200
Subject: [PATCH 3/5] fix: minor fixes for header include and lazy_split

---
 .../range.adaptors/range.lazy.split/nodiscard.verify.cpp  | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index cd15b33b66d59..c6420b0adf5d5 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -11,17 +11,19 @@
 // Check that functions are marked [[nodiscard]]
 
 #include <ranges>
+#include <string>
 #include <utility>
 
 void test() {
   // [range.lazy.split.overview]
 
-  std::string str = "the quick brown fox";
+  std::string str { "the quick brown fox" };
   char pattern    = ' ';
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::split(str, pattern);
+  std::views::lazy_split(str, pattern);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::split(pattern);
+  std::views::lazy_split(pattern);
+
 }

>From fbf22aa66f5d3205c82bbba057664780a8fbe057 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 23:32:22 +0200
Subject: [PATCH 4/5] add: test cases and remove nodiscard in implementation
 detail

---
 libcxx/include/__ranges/lazy_split_view.h     |  2 +-
 .../range.lazy.split/nodiscard.verify.cpp     | 41 ++++++++++++++++---
 2 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 5e336cbfcac49..66f18d5edad76 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index c6420b0adf5d5..b80f6f00836b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -8,22 +8,53 @@
 
 // REQUIRES: std-at-least-c++20
 
-// Check that functions are marked [[nodiscard]]
+// Test the libc++ extension that std::ranges::lazy_split_view and std::views::lazy_split are marked as [[nodiscard]].
 
 #include <ranges>
 #include <string>
 #include <utility>
 
 void test() {
-  // [range.lazy.split.overview]
-
-  std::string str { "the quick brown fox" };
+  std::string str = "the quick brown fox";
   char pattern    = ' ';
 
+  auto v = std::views::lazy_split(str, pattern);
+
+  // [range.lazy.split.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.lazy.split.iterator]
+
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it.base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(c_it);
+
+  // [range.lazy.split.overview]
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::lazy_split(str, pattern);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::lazy_split(pattern);
-
 }

>From 997525b35d5e7a64e9db03541cfbc7acf934f1da Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 12:01:40 +0200
Subject: [PATCH 5/5] revert: accidental format changes

---
 libcxx/include/__ranges/lazy_split_view.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 66f18d5edad76..c9a941abe7ff9 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
+      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
+    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -406,8 +406,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&, range_value_t<_Range>)
-    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, 
+                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {


From libcxx-commits at lists.llvm.org  Wed Jul  8 03:03:15 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 08 Jul 2026 03:03:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4e2063.d1496011.26913c.1c73@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/208036

>From 1a1da3d615018ef52c9eefcbb376679f5cdd7505 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 18:00:56 +0200
Subject: [PATCH 1/6] initial: nodiscard implementation to lazy_split_view.h

---
 libcxx/include/__ranges/lazy_split_view.h | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 938dca24cc4fc..01d398a0ac6d8 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -95,14 +95,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
       : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
@@ -111,19 +111,19 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<_View> && forward_range<const _View>
   {
     return __outer_iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
     return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
     if constexpr (forward_range<_View> && forward_range<const _View> && common_range<const _View>) {
       return __outer_iterator<true>{*this, ranges::end(__base_)};
     } else {
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
@@ -342,14 +342,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
-    _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
       return std::move(__i_.__current());
     }
 
-    _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
       __incremented_ = true;
@@ -385,7 +385,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       return __x.__is_done();
     }
 
-    _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
     iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(__i.__outer_current()))) {
       return ranges::iter_move(__i.__outer_current());
     }

>From 845a2090e07f251f9a46f849f149d5662ecdbc45 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 22:49:19 +0200
Subject: [PATCH 2/6] add: initial tests and clang-format fix

---
 libcxx/include/__ranges/lazy_split_view.h     | 12 +++++----
 .../range.lazy.split/nodiscard.verify.cpp     | 27 +++++++++++++++++++
 2 files changed, 34 insertions(+), 5 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 01d398a0ac6d8..5e336cbfcac49 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
+      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
+    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -342,7 +342,9 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
+      return __i_.__current();
+    }
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
@@ -404,8 +406,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&,
-                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, range_value_t<_Range>)
+    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cd15b33b66d59
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  // [range.lazy.split.overview]
+
+  std::string str = "the quick brown fox";
+  char pattern    = ' ';
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::split(str, pattern);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::split(pattern);
+}

>From 054f55611b632030795527d777509c70609e5df0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 22:57:55 +0200
Subject: [PATCH 3/6] fix: minor fixes for header include and lazy_split

---
 .../range.adaptors/range.lazy.split/nodiscard.verify.cpp  | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index cd15b33b66d59..c6420b0adf5d5 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -11,17 +11,19 @@
 // Check that functions are marked [[nodiscard]]
 
 #include <ranges>
+#include <string>
 #include <utility>
 
 void test() {
   // [range.lazy.split.overview]
 
-  std::string str = "the quick brown fox";
+  std::string str { "the quick brown fox" };
   char pattern    = ' ';
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::split(str, pattern);
+  std::views::lazy_split(str, pattern);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::split(pattern);
+  std::views::lazy_split(pattern);
+
 }

>From fbf22aa66f5d3205c82bbba057664780a8fbe057 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 23:32:22 +0200
Subject: [PATCH 4/6] add: test cases and remove nodiscard in implementation
 detail

---
 libcxx/include/__ranges/lazy_split_view.h     |  2 +-
 .../range.lazy.split/nodiscard.verify.cpp     | 41 ++++++++++++++++---
 2 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 5e336cbfcac49..66f18d5edad76 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index c6420b0adf5d5..b80f6f00836b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -8,22 +8,53 @@
 
 // REQUIRES: std-at-least-c++20
 
-// Check that functions are marked [[nodiscard]]
+// Test the libc++ extension that std::ranges::lazy_split_view and std::views::lazy_split are marked as [[nodiscard]].
 
 #include <ranges>
 #include <string>
 #include <utility>
 
 void test() {
-  // [range.lazy.split.overview]
-
-  std::string str { "the quick brown fox" };
+  std::string str = "the quick brown fox";
   char pattern    = ' ';
 
+  auto v = std::views::lazy_split(str, pattern);
+
+  // [range.lazy.split.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.lazy.split.iterator]
+
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it.base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(c_it);
+
+  // [range.lazy.split.overview]
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::lazy_split(str, pattern);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::lazy_split(pattern);
-
 }

>From 997525b35d5e7a64e9db03541cfbc7acf934f1da Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 12:01:40 +0200
Subject: [PATCH 5/6] revert: accidental format changes

---
 libcxx/include/__ranges/lazy_split_view.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 66f18d5edad76..c9a941abe7ff9 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
+      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
+    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -406,8 +406,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&, range_value_t<_Range>)
-    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, 
+                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {

>From 598aacef1fcf0d98cce6e0fd4b1c7ef3ff8e64db Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 12:02:50 +0200
Subject: [PATCH 6/6] revert: accidental format changes

---
 libcxx/include/__ranges/lazy_split_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index c9a941abe7ff9..fc41f5c49523b 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::begin(__base_)};
+      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::end(__base_)};
+    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -406,7 +406,7 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&, 
+lazy_split_view(_Range&&,
                 range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {


From libcxx-commits at lists.llvm.org  Wed Jul  8 03:09:56 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 08 Jul 2026 03:09:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3133: Modernizing
 numeric type requirements (PR #208145)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208145@github.com>
Message-ID: <6a4e21f4.491d90f5.2facd.a746@mx.google.com>


https://github.com/frederick-vs-ja requested changes to this pull request.

I think we need further changes and test coverage.

----
> - No code changes were needed for `std::valarray<T>`: its `operator[]` already indexes through a raw pointer and never relies on a user-overloadable `operator&`, so it already conforms to the revised wording.

However, I think we should still test this. I think we can use `operator_hijacker` from `operator_hijacker.h` to verify this, and add test cases to

- `libcxx/test/std/numerics/numarray/template.valarray/valarray.access/access.pass.cpp`
- `libcxx/test/std/numerics/numarray/template.valarray/valarray.access/const_access.pass.cpp`

----
Moreover, if we do want reject instantiating `valarray` and `complex` with incorrect types, we should `static_assert` all syntactic requirements in "_Cpp17DefaultConstructible_, _Cpp17CopyConstructible_, _Cpp17CopyAssignable_, and _Cpp17Destructible_".

This would be lengthy but implementable. Please be aware of rvalues and non-const lvalues.

Also, we can use `(void)sizeof(std::valarray<T>);` for "pure" instantiation of the class template specialization, which should ensure that we won't reject instantiation too late.

https://github.com/llvm/llvm-project/pull/208145

From libcxx-commits at lists.llvm.org  Wed Jul  8 05:06:39 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 08 Jul 2026 05:06:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4e3d4f.f732ad81.3a4994.031f@mx.google.com>


frederick-vs-ja wrote:

Merging as the failure on FreeBSD has been fixed by #186129.

https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Wed Jul  8 05:07:06 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 05:07:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] b5fa9ee - [libc++] Implement LWG3918:
 copy elision in `std::uninitialized_move/_n` (#207692)
Message-ID: <6a4e3d6a.7d41f3a1.237e53.b800@mx.google.com>


Author: inquisitivecrystal
Date: 2026-07-08T20:06:57+08:00
New Revision: b5fa9eee6798b678fc7cb5f2b42a977932b708f9

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

LOG: [libc++] Implement LWG3918: copy elision in `std::uninitialized_move/_n` (#207692)

This implements [LWG3918](https://wg21.link/LWG3918), which guarantees
copy elision for rvalues in `std::uninitialized_move/_n`. It also
implements [LWG4452](https://wg21.link/LWG4452), a minor correction that
makes the helper added by LWG3918 constexpr.

This additionally fixes a bug in `std::uninitialized_move/_n` where they
could create and then access dangling references. The previous
implementation used the following lambda as an implementation detail:
```c++
[](auto&& __iter) -> decltype(auto) { return std::move(*__iter); }
```
When `__iter` is a prvalue, this creates a temporary object within the
lambda's body and `std::move` then returns a reference to that object.
The reference dangles as soon as control leaves the lambda. This
behavior was not permitted by the standard even before LWG3918.

The new implementation fixes the bug by using a helper,
`std::__deref_move(__iter)`, which returns `*__iter` directly when
`*__iter` is already an rvalue.

Resolves #118339.
Resolves #171416.

Added: 
    

Modified: 
    libcxx/docs/Status/Cxx26Issues.csv
    libcxx/include/__memory/uninitialized_algorithms.h
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 5de35c96d4c0a..e8edd9eefe1b1 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -81,7 +81,7 @@
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
 "`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
 "`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
-"`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
+"`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","|Complete|","23","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
 "`LWG4027 <https://wg21.link/LWG4027>`__","``possibly-const-range`` should prefer returning ``const R&``","2024-11 (Wrocław)","","","`#118342 <https://github.com/llvm/llvm-project/issues/118342>`__",""
@@ -250,7 +250,7 @@
 "`LWG4449 <https://wg21.link/LWG4449>`__","``define_aggregate`` members must be public","2025-11 (Kona)","","","`#171412 <https://github.com/llvm/llvm-project/issues/171412>`__",""
 "`LWG4450 <https://wg21.link/LWG4450>`__","``std::atomic_ref<T>::store_key`` should be disabled for const ``T``","2025-11 (Kona)","","","`#171414 <https://github.com/llvm/llvm-project/issues/171414>`__",""
 "`LWG4451 <https://wg21.link/LWG4451>`__","``make_shared`` should not refer to a type ``U[N]`` for runtime N","2025-11 (Kona)","","","`#171415 <https://github.com/llvm/llvm-project/issues/171415>`__",""
-"`LWG4452 <https://wg21.link/LWG4452>`__","Make *deref-move* constexpr","2025-11 (Kona)","","","`#171416 <https://github.com/llvm/llvm-project/issues/171416>`__",""
+"`LWG4452 <https://wg21.link/LWG4452>`__","Make *deref-move* constexpr","2025-11 (Kona)","|Complete|","23","`#171416 <https://github.com/llvm/llvm-project/issues/171416>`__",""
 "`LWG4455 <https://wg21.link/LWG4455>`__","Add missing constraint to ``basic-sender::get_completion_signatures`` definition","2025-11 (Kona)","","","`#171417 <https://github.com/llvm/llvm-project/issues/171417>`__",""
 "`LWG4456 <https://wg21.link/LWG4456>`__","Decay ``Data`` and ``Child`` in ``make-sender``","2025-11 (Kona)","","","`#171418 <https://github.com/llvm/llvm-project/issues/171418>`__",""
 "`LWG4459 <https://wg21.link/LWG4459>`__","Protect ``get_completion_signatures`` fold expression from overloaded commas","2025-11 (Kona)","","","`#171419 <https://github.com/llvm/llvm-project/issues/171419>`__",""

diff  --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index d4cd49a1deb6e..f184b2b9dd925 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -25,6 +25,7 @@
 #include <__memory/pointer_traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
@@ -141,6 +142,14 @@ uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) {
 
 #if _LIBCPP_STD_VER >= 17
 
+template <class _Iter>
+_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) __deref_move(_Iter& __it) {
+  if constexpr (is_lvalue_reference_v<decltype(*__it)>)
+    return std::move(*__it);
+  else
+    return *__it;
+}
+
 // uninitialized_default_construct
 
 template <class _ValueType, class _ForwardIterator, class _Sentinel>
@@ -253,7 +262,7 @@ template <class _InputIterator, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
 uninitialized_move(_InputIterator __ifirst, _InputIterator __ilast, _ForwardIterator __ofirst) {
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
-  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::move(*__iter); };
+  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::__deref_move(__iter); };
 
   auto __result = std::__uninitialized_move<_ValueType>(
       std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __always_false(), __iter_move);
@@ -284,7 +293,7 @@ template <class _InputIterator, class _Size, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<_InputIterator, _ForwardIterator>
 uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofirst) {
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
-  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::move(*__iter); };
+  auto __iter_move = [](auto&& __iter) -> decltype(auto) { return std::__deref_move(__iter); };
 
   auto __result = std::__uninitialized_move_n<_ValueType>(
       std::move(__ifirst), __n, std::move(__ofirst), __always_false(), __iter_move);

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
index 13fef7a972e95..ac348ac88164d 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp
@@ -56,6 +56,38 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(const NoMoveNoCopy&)   = delete;
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using 
diff erence_type   = std::ptr
diff _t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr explicit PrvalueIterator(const int* ptr) : ptr_(ptr) {}
+
+  constexpr NoMoveNoCopy operator*() const { return NoMoveNoCopy(*ptr_); }
+
+  constexpr PrvalueIterator& operator++() {
+    ++ptr_;
+    return *this;
+  }
+
+  friend constexpr bool operator==(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ == b.ptr_; }
+  friend constexpr bool operator!=(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ != b.ptr_; }
+
+private:
+  const int* ptr_;
+};
+
 TEST_CONSTEXPR_CXX26 bool test() {
   const int n    = 3;
   MoveOnly in[n] = {1, 2, 3};
@@ -128,9 +160,36 @@ void test_counted()
     assert(Counted::count == 0);
 }
 
+TEST_CONSTEXPR_CXX26 bool test_copy_elision() {
+  using It      = PrvalueIterator;
+  using FIt     = forward_iterator<NoMoveNoCopy*>;
+  const int N   = 5;
+  int values[N] = {1, 2, 3, 4, 5};
+  std::allocator<NoMoveNoCopy> alloc;
+  NoMoveNoCopy* p = alloc.allocate(N);
+  auto ret        = std::uninitialized_move(It(values), It(values + 1), FIt(p));
+  assert(ret == FIt(p + 1));
+  assert(p[0].value == 1);
+  assert(values[0] == 1);
+  ret = std::uninitialized_move(It(values + 1), It(values + N), FIt(p + 1));
+  assert(p[1].value == 2);
+  assert(p[2].value == 3);
+  assert(p[3].value == 4);
+  assert(p[4].value == 5);
+  assert(values[1] == 2);
+  assert(values[2] == 3);
+  assert(values[3] == 4);
+  assert(values[4] == 5);
+  std::destroy(p, p + N);
+  alloc.deallocate(p, N);
+
+  return true;
+}
+
 int main(int, char**) {
     test_counted();
     test_ctor_throws();
+    test_copy_elision();
 
     // Test with an iterator that overloads operator== and operator!= as the input and output iterators
     {
@@ -164,6 +223,7 @@ int main(int, char**) {
     test();
 #if TEST_STD_VER >= 26
     static_assert(test());
+    static_assert(test_copy_elision());
 #endif
 
     return 0;

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
index 785cfb406efd9..2483a5519d51e 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move_n.pass.cpp
@@ -56,6 +56,38 @@ int ThrowsCounted::count = 0;
 int ThrowsCounted::constructed = 0;
 int ThrowsCounted::throw_after = 0;
 
+struct NoMoveNoCopy {
+  constexpr explicit NoMoveNoCopy(int x) : value(x) {}
+  NoMoveNoCopy(const NoMoveNoCopy&)   = delete;
+  friend void operator&(NoMoveNoCopy) = delete;
+  int value;
+};
+
+class PrvalueIterator {
+public:
+  using iterator_category = std::input_iterator_tag;
+  using 
diff erence_type   = std::ptr
diff _t;
+  using reference         = NoMoveNoCopy;
+  using pointer           = void;
+  using value_type        = NoMoveNoCopy;
+
+  PrvalueIterator() = delete;
+  constexpr explicit PrvalueIterator(const int* ptr) : ptr_(ptr) {}
+
+  constexpr NoMoveNoCopy operator*() const { return NoMoveNoCopy(*ptr_); }
+
+  constexpr PrvalueIterator& operator++() {
+    ++ptr_;
+    return *this;
+  }
+
+  friend constexpr bool operator==(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ == b.ptr_; }
+  friend constexpr bool operator!=(PrvalueIterator a, PrvalueIterator b) { return a.ptr_ != b.ptr_; }
+
+private:
+  const int* ptr_;
+};
+
 TEST_CONSTEXPR_CXX26 bool test() {
   const int n    = 3;
   MoveOnly in[n] = {1, 2, 3};
@@ -99,6 +131,35 @@ void test_ctor_throws()
 #endif
 }
 
+TEST_CONSTEXPR_CXX26 bool test_copy_elision() {
+  using It      = PrvalueIterator;
+  using FIt     = forward_iterator<NoMoveNoCopy*>;
+  const int N   = 5;
+  int values[N] = {1, 2, 3, 4, 5};
+  std::allocator<NoMoveNoCopy> alloc;
+  NoMoveNoCopy* p = alloc.allocate(N);
+  auto ret        = std::uninitialized_move_n(It(values), 1, FIt(p));
+  assert(ret.first == It(values + 1));
+  assert(ret.second == FIt(p + 1));
+  assert(p[0].value == 1);
+  assert(values[0] == 1);
+  ret = std::uninitialized_move_n(It(values + 1), N - 1, FIt(p + 1));
+  assert(ret.first == It(values + N));
+  assert(ret.second == FIt(p + N));
+  assert(p[1].value == 2);
+  assert(p[2].value == 3);
+  assert(p[3].value == 4);
+  assert(p[4].value == 5);
+  assert(values[1] == 2);
+  assert(values[2] == 3);
+  assert(values[3] == 4);
+  assert(values[4] == 5);
+  std::destroy(p, p + N);
+  alloc.deallocate(p, N);
+
+  return true;
+}
+
 void test_counted()
 {
     using It = cpp17_input_iterator<int*>;
@@ -135,6 +196,7 @@ int main(int, char**)
 {
     test_counted();
     test_ctor_throws();
+    test_copy_elision();
 
     // Test with an iterator that overloads operator== and operator!= as the input and output iterators
     {
@@ -168,6 +230,7 @@ int main(int, char**)
     test();
 #if TEST_STD_VER >= 26
     static_assert(test());
+    static_assert(test_copy_elision());
 #endif
 
     return 0;


        

From libcxx-commits at lists.llvm.org  Wed Jul  8 05:07:08 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 08 Jul 2026 05:07:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4e3d6c.491d90f5.2facd.72bb@mx.google.com>


https://github.com/frederick-vs-ja closed https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Wed Jul  8 05:07:27 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 05:07:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3918: copy elision
 in `std::uninitialized_move/_n` (PR #207692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207692@github.com>
Message-ID: <6a4e3d7f.faf16b37.2f0e1d.75d4@mx.google.com>


github-actions[bot] wrote:

<!--LLVM BUILDBOT INFORMATION COMMENT-->

@inquisitivecrystal Congratulations on having your first Pull Request (PR) merged into the LLVM Project!

Your changes will be combined with recent changes from other authors, then tested by our [build bots](https://lab.llvm.org/buildbot/). If there is a problem with a build, you may receive a report in an email or a comment on this PR.

Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail [here](https://llvm.org/docs/MyFirstTypoFix.html#myfirsttypofix-issues-after-landing-your-pr).

If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of [LLVM development](https://llvm.org/docs/DeveloperPolicy.html#patch-reversion-policy). You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are working as expected, well done!


https://github.com/llvm/llvm-project/pull/207692

From libcxx-commits at lists.llvm.org  Wed Jul  8 05:14:07 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 08 Jul 2026 05:14:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4e3f0f.7270d2fd.2d4879.f207@mx.google.com>


================
@@ -256,8 +256,16 @@ public:
 
   // 27.9.1.4 Members:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
----------------
philnik777 wrote:

I'm not sure how big a fan I am of having such platform-specific warnings just in the code randomly. CC @ldionne 

This also needs to be trailing the function.

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Wed Jul  8 05:14:07 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 08 Jul 2026 05:14:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4e3f0f.eb24c6ea.27aedc.fe3d@mx.google.com>


================
@@ -82,8 +80,13 @@ int main(int, char**)
             std::remove(tmp.c_str());
 
             std::filebuf f;
+            // expected-warning at +1 {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
----------------
philnik777 wrote:

This doesn't test anything. It needs to be in a `.verify.cpp` test.

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Wed Jul  8 05:14:07 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 08 Jul 2026 05:14:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4e3f0f.f732ad81.3a4994.6aa6@mx.google.com>


https://github.com/philnik777 requested changes to this pull request.


https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Wed Jul  8 05:24:22 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 08 Jul 2026 05:24:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Overhaul how we check ABI
	stability (PR #207989)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207989@github.com>
Message-ID: <6a4e4176.492e23e4.157d47.ce66@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207989

>From d21cae5d58949d754953080e5a71194ef8d2587f Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 7 Jul 2026 14:27:43 +0200
Subject: [PATCH] [libc++] Overhaul how we check ABI stability

---
 .../arm64-apple-darwin/abilist.sh.cpp         |   18 +
 .../arm64-apple-darwin/expected.abilist       | 2294 +++++++++++++++++
 .../x86_64-apple-darwin/abilist.sh.cpp        |   18 +
 .../x86_64-apple-darwin/expected.abilist      | 2294 +++++++++++++++++
 .../x86_64-unknown-linux-gnu/abilist.sh.cpp   |   18 +
 .../x86_64-unknown-linux-gnu/expected.abilist | 1973 ++++++++++++++
 6 files changed, 6615 insertions(+)
 create mode 100644 libcxx/test/libcxx/abilists/arm64-apple-darwin/abilist.sh.cpp
 create mode 100644 libcxx/test/libcxx/abilists/arm64-apple-darwin/expected.abilist
 create mode 100644 libcxx/test/libcxx/abilists/x86_64-apple-darwin/abilist.sh.cpp
 create mode 100644 libcxx/test/libcxx/abilists/x86_64-apple-darwin/expected.abilist
 create mode 100644 libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/abilist.sh.cpp
 create mode 100644 libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/expected.abilist

diff --git a/libcxx/test/libcxx/abilists/arm64-apple-darwin/abilist.sh.cpp b/libcxx/test/libcxx/abilists/arm64-apple-darwin/abilist.sh.cpp
new file mode 100644
index 0000000000000..12e790bdd03b7
--- /dev/null
+++ b/libcxx/test/libcxx/abilists/arm64-apple-darwin/abilist.sh.cpp
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FIXME: Encode whether the library was built with exceptions and check the abilist for that case as well
+
+// REQUIRES: target=arm64-apple-darwin{{.*}}
+
+// Check that the list of symbols exported from the dylib doesn't change unexpectedly
+
+// FILE_DEPENDENCIES: expected.abilist
+
+// RUN: nm --defined-only --extern-only %{lib-dir}/libc++.so.1.0 | cut -c 18- > %t.actual.abilist
+// RUN: diff expected.abilist %t.actual.abilist
diff --git a/libcxx/test/libcxx/abilists/arm64-apple-darwin/expected.abilist b/libcxx/test/libcxx/abilists/arm64-apple-darwin/expected.abilist
new file mode 100644
index 0000000000000..97550cd99f7d4
--- /dev/null
+++ b/libcxx/test/libcxx/abilists/arm64-apple-darwin/expected.abilist
@@ -0,0 +1,2294 @@
+I __ZNKSt10bad_typeid4whatEv (indirect for __ZNKSt10bad_typeid4whatEv)
+I __ZNKSt11logic_error4whatEv (indirect for __ZNKSt11logic_error4whatEv)
+T __ZNKSt12bad_any_cast4whatEv
+T __ZNKSt12experimental15fundamentals_v112bad_any_cast4whatEv
+I __ZNKSt13bad_exception4whatEv (indirect for __ZNKSt13bad_exception4whatEv)
+I __ZNKSt13runtime_error4whatEv (indirect for __ZNKSt13runtime_error4whatEv)
+T __ZNKSt16nested_exception14rethrow_nestedEv
+T __ZNKSt18bad_variant_access4whatEv
+T __ZNKSt19bad_optional_access4whatEv
+I __ZNKSt20bad_array_new_length4whatEv (indirect for __ZNKSt20bad_array_new_length4whatEv)
+T __ZNKSt29__bad_variant_access_with_msg4whatEv
+T __ZNKSt3__110__time_put8__do_putEPcRS1_PK2tmcc
+T __ZNKSt3__110__time_put8__do_putEPwRS1_PK2tmcc
+T __ZNKSt3__110error_code7messageEv
+T __ZNKSt3__110moneypunctIcLb0EE11do_groupingEv
+T __ZNKSt3__110moneypunctIcLb0EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIcLb0EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIcLb0EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIcLb0EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_thousands_sepEv
+T __ZNKSt3__110moneypunctIcLb1EE11do_groupingEv
+T __ZNKSt3__110moneypunctIcLb1EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIcLb1EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIcLb1EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIcLb1EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_thousands_sepEv
+T __ZNKSt3__110moneypunctIwLb0EE11do_groupingEv
+T __ZNKSt3__110moneypunctIwLb0EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIwLb0EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIwLb0EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIwLb0EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_thousands_sepEv
+T __ZNKSt3__110moneypunctIwLb1EE11do_groupingEv
+T __ZNKSt3__110moneypunctIwLb1EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIwLb1EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIwLb1EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIwLb1EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv
+T __ZNKSt3__112bad_weak_ptr4whatEv
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE16find_last_not_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE17find_first_not_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEcm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5rfindEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5rfindEcm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEPKc
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKc
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKcm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmRKS5_mm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE12find_last_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE13find_first_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE16find_last_not_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE17find_first_not_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE2atEm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4copyEPwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4findEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4findEwm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5rfindEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5rfindEwm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEPKw
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmPKw
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmPKwm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmRKS5_mm
+T __ZNKSt3__112ctype_bynameIcE10do_tolowerEPcPKc
+T __ZNKSt3__112ctype_bynameIcE10do_tolowerEc
+T __ZNKSt3__112ctype_bynameIcE10do_toupperEPcPKc
+T __ZNKSt3__112ctype_bynameIcE10do_toupperEc
+T __ZNKSt3__112ctype_bynameIwE10do_scan_isEjPKwS3_
+T __ZNKSt3__112ctype_bynameIwE10do_tolowerEPwPKw
+T __ZNKSt3__112ctype_bynameIwE10do_tolowerEw
+T __ZNKSt3__112ctype_bynameIwE10do_toupperEPwPKw
+T __ZNKSt3__112ctype_bynameIwE10do_toupperEw
+T __ZNKSt3__112ctype_bynameIwE11do_scan_notEjPKwS3_
+T __ZNKSt3__112ctype_bynameIwE5do_isEPKwS3_Pj
+T __ZNKSt3__112ctype_bynameIwE5do_isEjw
+T __ZNKSt3__112ctype_bynameIwE8do_widenEPKcS3_Pw
+T __ZNKSt3__112ctype_bynameIwE8do_widenEc
+T __ZNKSt3__112ctype_bynameIwE9do_narrowEPKwS3_cPc
+T __ZNKSt3__112ctype_bynameIwE9do_narrowEwc
+T __ZNKSt3__112strstreambuf6pcountEv
+T __ZNKSt3__113random_device7entropyEv
+T __ZNKSt3__114__codecvt_utf8IDiE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__114__codecvt_utf8IDiE11do_encodingEv
+T __ZNKSt3__114__codecvt_utf8IDiE13do_max_lengthEv
+T __ZNKSt3__114__codecvt_utf8IDiE16do_always_noconvEv
+T __ZNKSt3__114__codecvt_utf8IDiE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDiE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDiE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__114__codecvt_utf8IDsE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__114__codecvt_utf8IDsE11do_encodingEv
+T __ZNKSt3__114__codecvt_utf8IDsE13do_max_lengthEv
+T __ZNKSt3__114__codecvt_utf8IDsE16do_always_noconvEv
+T __ZNKSt3__114__codecvt_utf8IDsE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDsE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDsE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__114__codecvt_utf8IwE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__114__codecvt_utf8IwE11do_encodingEv
+T __ZNKSt3__114__codecvt_utf8IwE13do_max_lengthEv
+T __ZNKSt3__114__codecvt_utf8IwE16do_always_noconvEv
+T __ZNKSt3__114__codecvt_utf8IwE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IwE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IwE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__114collate_bynameIcE10do_compareEPKcS3_S3_S3_
+T __ZNKSt3__114collate_bynameIcE12do_transformEPKcS3_
+T __ZNKSt3__114collate_bynameIwE10do_compareEPKwS3_S3_S3_
+T __ZNKSt3__114collate_bynameIwE12do_transformEPKwS3_
+T __ZNKSt3__114error_category10equivalentERKNS_10error_codeEi
+T __ZNKSt3__114error_category10equivalentEiRKNS_15error_conditionE
+T __ZNKSt3__114error_category23default_error_conditionEi
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IwLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IwLb0EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IwLb0EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IwLb0EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IwLb0EE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb0EE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IwLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IwLb1EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IwLb1EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IwLb1EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IwLb1EE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb1EE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115basic_streambufIcNS_11char_traitsIcEEE6getlocEv
+T __ZNKSt3__115basic_streambufIwNS_11char_traitsIwEEE6getlocEv
+T __ZNKSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strEv
+T __ZNKSt3__115error_condition7messageEv
+T __ZNKSt3__117bad_function_call4whatEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_thousands_sepEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_thousands_sepEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_thousands_sepEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_thousands_sepEv
+T __ZNKSt3__118__time_get_storageIcE15__do_date_orderEv
+T __ZNKSt3__118__time_get_storageIwE15__do_date_orderEv
+T __ZNKSt3__119__shared_weak_count13__get_deleterERKSt9type_info
+T __ZNKSt3__119bad_expected_accessIvE4whatEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE11do_encodingEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE13do_max_lengthEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE16do_always_noconvEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE11do_encodingEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE13do_max_lengthEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE16do_always_noconvEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__120__codecvt_utf8_utf16IwE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__120__codecvt_utf8_utf16IwE11do_encodingEv
+T __ZNKSt3__120__codecvt_utf8_utf16IwE13do_max_lengthEv
+T __ZNKSt3__120__codecvt_utf8_utf16IwE16do_always_noconvEv
+T __ZNKSt3__120__codecvt_utf8_utf16IwE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IwE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IwE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__120__time_get_c_storageIcE3__XEv
+T __ZNKSt3__120__time_get_c_storageIcE3__cEv
+T __ZNKSt3__120__time_get_c_storageIcE3__rEv
+T __ZNKSt3__120__time_get_c_storageIcE3__xEv
+T __ZNKSt3__120__time_get_c_storageIcE7__am_pmEv
+T __ZNKSt3__120__time_get_c_storageIcE7__weeksEv
+T __ZNKSt3__120__time_get_c_storageIcE8__monthsEv
+T __ZNKSt3__120__time_get_c_storageIwE3__XEv
+T __ZNKSt3__120__time_get_c_storageIwE3__cEv
+T __ZNKSt3__120__time_get_c_storageIwE3__rEv
+T __ZNKSt3__120__time_get_c_storageIwE3__xEv
+T __ZNKSt3__120__time_get_c_storageIwE7__am_pmEv
+T __ZNKSt3__120__time_get_c_storageIwE7__weeksEv
+T __ZNKSt3__120__time_get_c_storageIwE8__monthsEv
+T __ZNKSt3__120__vector_base_commonILb1EE20__throw_length_errorEv
+T __ZNKSt3__120__vector_base_commonILb1EE20__throw_out_of_rangeEv
+T __ZNKSt3__121__basic_string_commonILb1EE20__throw_length_errorEv
+T __ZNKSt3__121__basic_string_commonILb1EE20__throw_out_of_rangeEv
+T __ZNKSt3__123__match_any_but_newlineIcE6__execERNS_7__stateIcEE
+T __ZNKSt3__123__match_any_but_newlineIwE6__execERNS_7__stateIwEE
+T __ZNKSt3__13pmr26synchronized_pool_resource11do_is_equalERKNS0_15memory_resourceE
+T __ZNKSt3__13pmr28unsynchronized_pool_resource12__pool_indexEmm
+T __ZNKSt3__13pmr28unsynchronized_pool_resource17__pool_block_sizeEi
+T __ZNKSt3__13pmr28unsynchronized_pool_resource22__log2_pool_block_sizeEi
+T __ZNKSt3__13pmr28unsynchronized_pool_resource7optionsEv
+T __ZNKSt3__14__fs10filesystem18directory_iterator13__dereferenceEv
+T __ZNKSt3__14__fs10filesystem28recursive_directory_iterator13__dereferenceEv
+T __ZNKSt3__14__fs10filesystem28recursive_directory_iterator5depthEv
+T __ZNKSt3__14__fs10filesystem28recursive_directory_iterator7optionsEv
+T __ZNKSt3__14__fs10filesystem4path10__filenameEv
+T __ZNKSt3__14__fs10filesystem4path11__extensionEv
+T __ZNKSt3__14__fs10filesystem4path11__root_nameEv
+T __ZNKSt3__14__fs10filesystem4path13__parent_pathEv
+T __ZNKSt3__14__fs10filesystem4path15__relative_pathEv
+T __ZNKSt3__14__fs10filesystem4path15__root_path_rawEv
+T __ZNKSt3__14__fs10filesystem4path16__root_directoryEv
+T __ZNKSt3__14__fs10filesystem4path16lexically_normalEv
+T __ZNKSt3__14__fs10filesystem4path18lexically_relativeERKS2_
+T __ZNKSt3__14__fs10filesystem4path3endEv
+T __ZNKSt3__14__fs10filesystem4path5beginEv
+T __ZNKSt3__14__fs10filesystem4path6__stemEv
+T __ZNKSt3__14__fs10filesystem4path9__compareENS_17basic_string_viewIcNS_11char_traitsIcEEEE
+T __ZNKSt3__15ctypeIcE10do_tolowerEPcPKc
+T __ZNKSt3__15ctypeIcE10do_tolowerEc
+T __ZNKSt3__15ctypeIcE10do_toupperEPcPKc
+T __ZNKSt3__15ctypeIcE10do_toupperEc
+T __ZNKSt3__15ctypeIcE8do_widenEPKcS3_Pc
+T __ZNKSt3__15ctypeIcE8do_widenEc
+T __ZNKSt3__15ctypeIcE9do_narrowEPKcS3_cPc
+T __ZNKSt3__15ctypeIcE9do_narrowEcc
+T __ZNKSt3__15ctypeIwE10do_scan_isEjPKwS3_
+T __ZNKSt3__15ctypeIwE10do_tolowerEPwPKw
+T __ZNKSt3__15ctypeIwE10do_tolowerEw
+T __ZNKSt3__15ctypeIwE10do_toupperEPwPKw
+T __ZNKSt3__15ctypeIwE10do_toupperEw
+T __ZNKSt3__15ctypeIwE11do_scan_notEjPKwS3_
+T __ZNKSt3__15ctypeIwE5do_isEPKwS3_Pj
+T __ZNKSt3__15ctypeIwE5do_isEjw
+T __ZNKSt3__15ctypeIwE8do_widenEPKcS3_Pw
+T __ZNKSt3__15ctypeIwE8do_widenEc
+T __ZNKSt3__15ctypeIwE9do_narrowEPKwS3_cPc
+T __ZNKSt3__15ctypeIwE9do_narrowEwc
+T __ZNKSt3__16locale4nameEv
+T __ZNKSt3__16locale9has_facetERNS0_2idE
+T __ZNKSt3__16locale9use_facetERNS0_2idE
+T __ZNKSt3__16localeeqERKS0_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE10do_unshiftERS1_PDuS4_RS4_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE5do_inERS1_PKDuS5_RS5_PDiS7_RS7_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE6do_outERS1_PKDiS5_RS5_PDuS7_RS7_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE9do_lengthERS1_PKDuS5_m
+T __ZNKSt3__17codecvtIDic11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIDic11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDic11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDic11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDic11__mbstate_tE5do_inERS1_PKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__17codecvtIDic11__mbstate_tE6do_outERS1_PKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIDic11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE10do_unshiftERS1_PDuS4_RS4_
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE5do_inERS1_PKDuS5_RS5_PDsS7_RS7_
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE6do_outERS1_PKDsS5_RS5_PDuS7_RS7_
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE9do_lengthERS1_PKDuS5_m
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE5do_inERS1_PKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE6do_outERS1_PKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17codecvtIcc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIcc11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIcc11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIcc11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIcc11__mbstate_tE5do_inERS1_PKcS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIcc11__mbstate_tE6do_outERS1_PKcS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIcc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17codecvtIwc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIwc11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIwc11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIwc11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIwc11__mbstate_tE5do_inERS1_PKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__17codecvtIwc11__mbstate_tE6do_outERS1_PKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIwc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17collateIcE10do_compareEPKcS3_S3_S3_
+T __ZNKSt3__17collateIcE12do_transformEPKcS3_
+T __ZNKSt3__17collateIcE7do_hashEPKcS3_
+T __ZNKSt3__17collateIwE10do_compareEPKwS3_S3_S3_
+T __ZNKSt3__17collateIwE12do_transformEPKwS3_
+T __ZNKSt3__17collateIwE7do_hashEPKwS3_
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRPv
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRb
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRd
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRe
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRf
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRl
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRm
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRt
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRx
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRy
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjS8_
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRPv
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRb
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRd
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRe
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRf
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRl
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRm
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRt
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRx
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRy
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjS8_
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcPKv
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcb
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcd
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEce
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcl
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcm
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcx
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcy
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPKv
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwb
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwd
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwe
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwl
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwm
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwx
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwy
+T __ZNKSt3__18ios_base6getlocEv
+T __ZNKSt3__18messagesIcE6do_getEliiRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNKSt3__18messagesIcE7do_openERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_6localeE
+T __ZNKSt3__18messagesIcE8do_closeEl
+T __ZNKSt3__18messagesIwE6do_getEliiRKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEE
+T __ZNKSt3__18messagesIwE7do_openERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_6localeE
+T __ZNKSt3__18messagesIwE8do_closeEl
+T __ZNKSt3__18numpunctIcE11do_groupingEv
+T __ZNKSt3__18numpunctIcE11do_truenameEv
+T __ZNKSt3__18numpunctIcE12do_falsenameEv
+T __ZNKSt3__18numpunctIcE16do_decimal_pointEv
+T __ZNKSt3__18numpunctIcE16do_thousands_sepEv
+T __ZNKSt3__18numpunctIwE11do_groupingEv
+T __ZNKSt3__18numpunctIwE11do_truenameEv
+T __ZNKSt3__18numpunctIwE12do_falsenameEv
+T __ZNKSt3__18numpunctIwE16do_decimal_pointEv
+T __ZNKSt3__18numpunctIwE16do_thousands_sepEv
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE10__get_hourERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE10__get_yearERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_am_pmERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_monthERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_year4ERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_dateES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_timeES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_yearES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE12__get_minuteERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE12__get_secondERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_12_hourERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_percentERS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_weekdayERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13do_date_orderEv
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE14do_get_weekdayES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE15__get_monthnameERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE16do_get_monthnameES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE17__get_weekdaynameERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE17__get_white_spaceERS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE18__get_day_year_numERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE3getES4_S4_RNS_8ios_baseERjP2tmPKcSC_
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjP2tmcc
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE9__get_dayERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE10__get_hourERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE10__get_yearERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_am_pmERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_monthERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_year4ERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_dateES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_timeES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_yearES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE12__get_minuteERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE12__get_secondERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_12_hourERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_percentERS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_weekdayERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13do_date_orderEv
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE14do_get_weekdayES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE15__get_monthnameERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE16do_get_monthnameES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE17__get_weekdaynameERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE17__get_white_spaceERS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE18__get_day_year_numERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE3getES4_S4_RNS_8ios_baseERjP2tmPKwSC_
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjP2tmcc
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE9__get_dayERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE3putES4_RNS_8ios_baseEcPK2tmPKcSC_
+T __ZNKSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcPK2tmcc
+T __ZNKSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE3putES4_RNS_8ios_baseEwPK2tmPKwSC_
+T __ZNKSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPK2tmcc
+T __ZNKSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_bRNS_8ios_baseERjRNS_12basic_stringIcS3_NS_9allocatorIcEEEE
+T __ZNKSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_bRNS_8ios_baseERjRe
+T __ZNKSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_bRNS_8ios_baseERjRNS_12basic_stringIwS3_NS_9allocatorIwEEEE
+T __ZNKSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_bRNS_8ios_baseERjRe
+T __ZNKSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_bRNS_8ios_baseEcRKNS_12basic_stringIcS3_NS_9allocatorIcEEEE
+T __ZNKSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_bRNS_8ios_baseEce
+T __ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwRKNS_12basic_stringIwS3_NS_9allocatorIwEEEE
+T __ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwe
+I __ZNKSt8bad_cast4whatEv (indirect for __ZNKSt8bad_cast4whatEv)
+I __ZNKSt9bad_alloc4whatEv (indirect for __ZNKSt9bad_alloc4whatEv)
+I __ZNKSt9exception4whatEv (indirect for __ZNKSt9exception4whatEv)
+I __ZNSt10bad_typeidC1Ev (indirect for __ZNSt10bad_typeidC1Ev)
+I __ZNSt10bad_typeidC2Ev (indirect for __ZNSt10bad_typeidC2Ev)
+I __ZNSt10bad_typeidD0Ev (indirect for __ZNSt10bad_typeidD0Ev)
+I __ZNSt10bad_typeidD1Ev (indirect for __ZNSt10bad_typeidD1Ev)
+I __ZNSt10bad_typeidD2Ev (indirect for __ZNSt10bad_typeidD2Ev)
+T __ZNSt11logic_errorC1EPKc
+T __ZNSt11logic_errorC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt11logic_errorC1ERKS_
+T __ZNSt11logic_errorC2EPKc
+T __ZNSt11logic_errorC2ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt11logic_errorC2ERKS_
+I __ZNSt11logic_errorD0Ev (indirect for __ZNSt11logic_errorD0Ev)
+I __ZNSt11logic_errorD1Ev (indirect for __ZNSt11logic_errorD1Ev)
+I __ZNSt11logic_errorD2Ev (indirect for __ZNSt11logic_errorD2Ev)
+T __ZNSt11logic_erroraSERKS_
+I __ZNSt11range_errorD0Ev (indirect for __ZNSt11range_errorD0Ev)
+I __ZNSt11range_errorD1Ev (indirect for __ZNSt11range_errorD1Ev)
+I __ZNSt11range_errorD2Ev (indirect for __ZNSt11range_errorD2Ev)
+I __ZNSt12domain_errorD0Ev (indirect for __ZNSt12domain_errorD0Ev)
+I __ZNSt12domain_errorD1Ev (indirect for __ZNSt12domain_errorD1Ev)
+I __ZNSt12domain_errorD2Ev (indirect for __ZNSt12domain_errorD2Ev)
+T __ZNSt12experimental19bad_optional_accessD0Ev
+T __ZNSt12experimental19bad_optional_accessD1Ev
+T __ZNSt12experimental19bad_optional_accessD2Ev
+I __ZNSt12length_errorD0Ev (indirect for __ZNSt12length_errorD0Ev)
+I __ZNSt12length_errorD1Ev (indirect for __ZNSt12length_errorD1Ev)
+I __ZNSt12length_errorD2Ev (indirect for __ZNSt12length_errorD2Ev)
+I __ZNSt12out_of_rangeD0Ev (indirect for __ZNSt12out_of_rangeD0Ev)
+I __ZNSt12out_of_rangeD1Ev (indirect for __ZNSt12out_of_rangeD1Ev)
+I __ZNSt12out_of_rangeD2Ev (indirect for __ZNSt12out_of_rangeD2Ev)
+I __ZNSt13bad_exceptionD0Ev (indirect for __ZNSt13bad_exceptionD0Ev)
+I __ZNSt13bad_exceptionD1Ev (indirect for __ZNSt13bad_exceptionD1Ev)
+I __ZNSt13bad_exceptionD2Ev (indirect for __ZNSt13bad_exceptionD2Ev)
+T __ZNSt13exception_ptr31__from_native_exception_pointerEPv
+T __ZNSt13exception_ptrC1ERKS_
+T __ZNSt13exception_ptrC2ERKS_
+T __ZNSt13exception_ptrD1Ev
+T __ZNSt13exception_ptrD2Ev
+T __ZNSt13exception_ptraSERKS_
+T __ZNSt13runtime_errorC1EPKc
+T __ZNSt13runtime_errorC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt13runtime_errorC1ERKS_
+T __ZNSt13runtime_errorC2EPKc
+T __ZNSt13runtime_errorC2ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt13runtime_errorC2ERKS_
+I __ZNSt13runtime_errorD0Ev (indirect for __ZNSt13runtime_errorD0Ev)
+I __ZNSt13runtime_errorD1Ev (indirect for __ZNSt13runtime_errorD1Ev)
+I __ZNSt13runtime_errorD2Ev (indirect for __ZNSt13runtime_errorD2Ev)
+T __ZNSt13runtime_erroraSERKS_
+I __ZNSt14overflow_errorD0Ev (indirect for __ZNSt14overflow_errorD0Ev)
+I __ZNSt14overflow_errorD1Ev (indirect for __ZNSt14overflow_errorD1Ev)
+I __ZNSt14overflow_errorD2Ev (indirect for __ZNSt14overflow_errorD2Ev)
+I __ZNSt15underflow_errorD0Ev (indirect for __ZNSt15underflow_errorD0Ev)
+I __ZNSt15underflow_errorD1Ev (indirect for __ZNSt15underflow_errorD1Ev)
+I __ZNSt15underflow_errorD2Ev (indirect for __ZNSt15underflow_errorD2Ev)
+I __ZNSt16invalid_argumentD0Ev (indirect for __ZNSt16invalid_argumentD0Ev)
+I __ZNSt16invalid_argumentD1Ev (indirect for __ZNSt16invalid_argumentD1Ev)
+I __ZNSt16invalid_argumentD2Ev (indirect for __ZNSt16invalid_argumentD2Ev)
+T __ZNSt16nested_exceptionC1Ev
+T __ZNSt16nested_exceptionC2Ev
+T __ZNSt16nested_exceptionD0Ev
+T __ZNSt16nested_exceptionD1Ev
+T __ZNSt16nested_exceptionD2Ev
+T __ZNSt19bad_optional_accessD0Ev
+T __ZNSt19bad_optional_accessD1Ev
+T __ZNSt19bad_optional_accessD2Ev
+I __ZNSt20bad_array_new_lengthC1Ev (indirect for __ZNSt20bad_array_new_lengthC1Ev)
+I __ZNSt20bad_array_new_lengthC2Ev (indirect for __ZNSt20bad_array_new_lengthC2Ev)
+I __ZNSt20bad_array_new_lengthD0Ev (indirect for __ZNSt20bad_array_new_lengthD0Ev)
+I __ZNSt20bad_array_new_lengthD1Ev (indirect for __ZNSt20bad_array_new_lengthD1Ev)
+I __ZNSt20bad_array_new_lengthD2Ev (indirect for __ZNSt20bad_array_new_lengthD2Ev)
+T __ZNSt3__110__time_getC1EPKc
+T __ZNSt3__110__time_getC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_getC2EPKc
+T __ZNSt3__110__time_getC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_getD1Ev
+T __ZNSt3__110__time_getD2Ev
+T __ZNSt3__110__time_putC1EPKc
+T __ZNSt3__110__time_putC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_putC2EPKc
+T __ZNSt3__110__time_putC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_putD1Ev
+T __ZNSt3__110__time_putD2Ev
+S __ZNSt3__110ctype_base5alnumE
+S __ZNSt3__110ctype_base5alphaE
+S __ZNSt3__110ctype_base5blankE
+S __ZNSt3__110ctype_base5cntrlE
+S __ZNSt3__110ctype_base5digitE
+S __ZNSt3__110ctype_base5graphE
+S __ZNSt3__110ctype_base5lowerE
+S __ZNSt3__110ctype_base5printE
+S __ZNSt3__110ctype_base5punctE
+S __ZNSt3__110ctype_base5spaceE
+S __ZNSt3__110ctype_base5upperE
+S __ZNSt3__110ctype_base6xdigitE
+T __ZNSt3__110istrstreamD0Ev
+T __ZNSt3__110istrstreamD1Ev
+T __ZNSt3__110istrstreamD2Ev
+D __ZNSt3__110moneypunctIcLb0EE2idE
+S __ZNSt3__110moneypunctIcLb0EE4intlE
+D __ZNSt3__110moneypunctIcLb1EE2idE
+S __ZNSt3__110moneypunctIcLb1EE4intlE
+D __ZNSt3__110moneypunctIwLb0EE2idE
+S __ZNSt3__110moneypunctIwLb0EE4intlE
+D __ZNSt3__110moneypunctIwLb1EE2idE
+S __ZNSt3__110moneypunctIwLb1EE4intlE
+T __ZNSt3__110ostrstreamD0Ev
+T __ZNSt3__110ostrstreamD1Ev
+T __ZNSt3__110ostrstreamD2Ev
+T __ZNSt3__110to_wstringEd
+T __ZNSt3__110to_wstringEe
+T __ZNSt3__110to_wstringEf
+T __ZNSt3__110to_wstringEi
+T __ZNSt3__110to_wstringEj
+T __ZNSt3__110to_wstringEl
+T __ZNSt3__110to_wstringEm
+T __ZNSt3__110to_wstringEx
+T __ZNSt3__110to_wstringEy
+T __ZNSt3__111__call_onceERVmPvPFvS2_E
+T __ZNSt3__111__money_getIcE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_SF_Ri
+T __ZNSt3__111__money_getIwE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_SJ_Ri
+T __ZNSt3__111__money_putIcE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_Ri
+T __ZNSt3__111__money_putIcE8__formatEPcRS2_S3_jPKcS5_RKNS_5ctypeIcEEbRKNS_10money_base7patternEccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESL_SL_i
+T __ZNSt3__111__money_putIwE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_Ri
+T __ZNSt3__111__money_putIwE8__formatEPwRS2_S3_jPKwS5_RKNS_5ctypeIwEEbRKNS_10money_base7patternEwwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNSE_IwNSF_IwEENSH_IwEEEESQ_i
+T __ZNSt3__111regex_errorC1ENS_15regex_constants10error_typeE
+T __ZNSt3__111regex_errorC2ENS_15regex_constants10error_typeE
+T __ZNSt3__111regex_errorD0Ev
+T __ZNSt3__111regex_errorD1Ev
+T __ZNSt3__111regex_errorD2Ev
+T __ZNSt3__111this_thread9sleep_forERKNS_6chrono8durationIxNS_5ratioILl1ELl1000000000EEEEE
+T __ZNSt3__111timed_mutex4lockEv
+T __ZNSt3__111timed_mutex6unlockEv
+T __ZNSt3__111timed_mutex8try_lockEv
+T __ZNSt3__111timed_mutexC1Ev
+T __ZNSt3__111timed_mutexC2Ev
+T __ZNSt3__111timed_mutexD1Ev
+T __ZNSt3__111timed_mutexD2Ev
+T __ZNSt3__112__do_nothingEPv
+T __ZNSt3__112__get_sp_mutEPKv
+T __ZNSt3__112__next_primeEm
+S __ZNSt3__112__rs_default4__c_E
+T __ZNSt3__112__rs_defaultC1ERKS0_
+T __ZNSt3__112__rs_defaultC1Ev
+T __ZNSt3__112__rs_defaultC2ERKS0_
+T __ZNSt3__112__rs_defaultC2Ev
+T __ZNSt3__112__rs_defaultD1Ev
+T __ZNSt3__112__rs_defaultD2Ev
+T __ZNSt3__112__rs_defaultclEv
+T __ZNSt3__112bad_weak_ptrD0Ev
+T __ZNSt3__112bad_weak_ptrD1Ev
+T __ZNSt3__112bad_weak_ptrD2Ev
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE21__grow_by_and_replaceEmmmmmmPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm
+S __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4nposE
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5eraseEmm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcmm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendERKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignERKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertENS_11__wrap_iterIPKcEEc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmRKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6resizeEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmRKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEmmmmmm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_RKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_RKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSERKS5_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEc
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE21__grow_by_and_replaceEmmmmmmPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE2atEm
+S __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4nposE
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5eraseEmm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEPKwmm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendERKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignERKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertENS_11__wrap_iterIPKwEEw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmRKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6resizeEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmRKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7reserveEm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9__grow_byEmmmmmm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9push_backEw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_RKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_RKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED1Ev
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED2Ev
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEaSERKS5_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEaSEw
+T __ZNSt3__112ctype_bynameIcEC1EPKcm
+T __ZNSt3__112ctype_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIcEC2EPKcm
+T __ZNSt3__112ctype_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIcED0Ev
+T __ZNSt3__112ctype_bynameIcED1Ev
+T __ZNSt3__112ctype_bynameIcED2Ev
+T __ZNSt3__112ctype_bynameIwEC1EPKcm
+T __ZNSt3__112ctype_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIwEC2EPKcm
+T __ZNSt3__112ctype_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIwED0Ev
+T __ZNSt3__112ctype_bynameIwED1Ev
+T __ZNSt3__112ctype_bynameIwED2Ev
+T __ZNSt3__112future_errorC1ENS_10error_codeE
+T __ZNSt3__112future_errorC2ENS_10error_codeE
+T __ZNSt3__112future_errorD0Ev
+T __ZNSt3__112future_errorD1Ev
+T __ZNSt3__112future_errorD2Ev
+S __ZNSt3__112placeholders2_1E
+S __ZNSt3__112placeholders2_2E
+S __ZNSt3__112placeholders2_3E
+S __ZNSt3__112placeholders2_4E
+S __ZNSt3__112placeholders2_5E
+S __ZNSt3__112placeholders2_6E
+S __ZNSt3__112placeholders2_7E
+S __ZNSt3__112placeholders2_8E
+S __ZNSt3__112placeholders2_9E
+S __ZNSt3__112placeholders3_10E
+T __ZNSt3__112strstreambuf3strEv
+T __ZNSt3__112strstreambuf4swapERS0_
+T __ZNSt3__112strstreambuf6__initEPclS1_
+T __ZNSt3__112strstreambuf6freezeEb
+T __ZNSt3__112strstreambuf7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__112strstreambuf7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__112strstreambuf8overflowEi
+T __ZNSt3__112strstreambuf9pbackfailEi
+T __ZNSt3__112strstreambuf9underflowEv
+T __ZNSt3__112strstreambufC1EPFPvmEPFvS1_E
+T __ZNSt3__112strstreambufC1EPKal
+T __ZNSt3__112strstreambufC1EPKcl
+T __ZNSt3__112strstreambufC1EPKhl
+T __ZNSt3__112strstreambufC1EPalS1_
+T __ZNSt3__112strstreambufC1EPclS1_
+T __ZNSt3__112strstreambufC1EPhlS1_
+T __ZNSt3__112strstreambufC1El
+T __ZNSt3__112strstreambufC2EPFPvmEPFvS1_E
+T __ZNSt3__112strstreambufC2EPKal
+T __ZNSt3__112strstreambufC2EPKcl
+T __ZNSt3__112strstreambufC2EPKhl
+T __ZNSt3__112strstreambufC2EPalS1_
+T __ZNSt3__112strstreambufC2EPclS1_
+T __ZNSt3__112strstreambufC2EPhlS1_
+T __ZNSt3__112strstreambufC2El
+T __ZNSt3__112strstreambufD0Ev
+T __ZNSt3__112strstreambufD1Ev
+T __ZNSt3__112strstreambufD2Ev
+T __ZNSt3__112system_errorC1ENS_10error_codeE
+T __ZNSt3__112system_errorC1ENS_10error_codeEPKc
+T __ZNSt3__112system_errorC1ENS_10error_codeERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorC1EiRKNS_14error_categoryE
+T __ZNSt3__112system_errorC1EiRKNS_14error_categoryEPKc
+T __ZNSt3__112system_errorC1EiRKNS_14error_categoryERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorC2ENS_10error_codeE
+T __ZNSt3__112system_errorC2ENS_10error_codeEPKc
+T __ZNSt3__112system_errorC2ENS_10error_codeERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorC2EiRKNS_14error_categoryE
+T __ZNSt3__112system_errorC2EiRKNS_14error_categoryEPKc
+T __ZNSt3__112system_errorC2EiRKNS_14error_categoryERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorD0Ev
+T __ZNSt3__112system_errorD1Ev
+T __ZNSt3__112system_errorD2Ev
+T __ZNSt3__113__hash_memoryEPKvm
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE11__read_modeEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE12__write_modeEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4openEPKcj
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4syncEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5closeEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE6setbufEPcl
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE8overflowEi
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9pbackfailEi
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9underflowEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC1EOS3_
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC1Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2EOS3_
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEPclc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERNS_15basic_streambufIcS2_EEc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4peekEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4readEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4syncEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5seekgENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5seekgExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5tellgEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5ungetEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6ignoreEli
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6sentryC1ERS3_b
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6sentryC2ERS3_b
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7getlineEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7getlineEPclc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7putbackEc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE8readsomeEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRNS_9basic_iosIcS2_EES6_E
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRS3_S4_E
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERPv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERb
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERd
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERe
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERf
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERi
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERj
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERm
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERs
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERt
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERx
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERy
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEPwlw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERNS_15basic_streambufIwS2_EEw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4peekEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4readEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4swapERS3_
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4syncEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5seekgENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5seekgExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5tellgEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5ungetEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6ignoreEli
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6sentryC1ERS3_b
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6sentryC2ERS3_b
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7getlineEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7getlineEPwlw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7putbackEw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE8readsomeEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEEC1EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEEC2EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED2Ev
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRNS_9basic_iosIwS2_EES6_E
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRS3_S4_E
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERPv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERb
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERd
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERe
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERf
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERi
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERj
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERm
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERs
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERt
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERx
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERy
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE3putEc
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5seekpENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5seekpExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5tellpEv
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5writeEPKcl
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC2ERS3_
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD2Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRNS_9basic_iosIcS2_EES6_E
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRS3_S4_E
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPKv
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEb
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEd
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEe
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEf
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEi
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEj
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEl
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEm
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEs
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEt
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEx
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEy
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE3putEw
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE4swapERS3_
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5flushEv
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5seekpENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5seekpExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5tellpEv
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5writeEPKwl
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryC1ERS3_
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryC2ERS3_
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryD1Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryD2Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEEC1EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEEC2EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED2Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRNS_9basic_iosIwS2_EES6_E
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRS3_S4_E
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPKv
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEb
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEd
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEe
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEf
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEi
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEj
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEl
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEm
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEs
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEt
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEx
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEy
+T __ZNSt3__113random_deviceC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__113random_deviceC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__113random_deviceD1Ev
+T __ZNSt3__113random_deviceD2Ev
+T __ZNSt3__113random_deviceclEv
+T __ZNSt3__113shared_futureIvED1Ev
+T __ZNSt3__113shared_futureIvED2Ev
+T __ZNSt3__113shared_futureIvEaSERKS1_
+T __ZNSt3__114__num_get_base10__get_baseERNS_8ios_baseE
+S __ZNSt3__114__num_get_base5__srcE
+T __ZNSt3__114__num_put_base12__format_intEPcPKcbj
+T __ZNSt3__114__num_put_base14__format_floatEPcPKcj
+T __ZNSt3__114__num_put_base18__identify_paddingEPcS1_RKNS_8ios_baseE
+T __ZNSt3__114__shared_count12__add_sharedEv
+T __ZNSt3__114__shared_count16__release_sharedEv
+T __ZNSt3__114__shared_countD0Ev
+T __ZNSt3__114__shared_countD1Ev
+T __ZNSt3__114__shared_countD2Ev
+T __ZNSt3__114basic_ifstreamIcNS_11char_traitsIcEEE4openEPKcj
+T __ZNSt3__114basic_ifstreamIcNS_11char_traitsIcEEE4openERKNS_12basic_stringIcS2_NS_9allocatorIcEEEEj
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__114basic_ofstreamIcNS_11char_traitsIcEEE4openEPKcj
+T __ZNSt3__114basic_ofstreamIcNS_11char_traitsIcEEE4openERKNS_12basic_stringIcS2_NS_9allocatorIcEEEEj
+T __ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIDic11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDic11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDic11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIDsc11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDsc11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDsc11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIcc11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIcc11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIcc11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIwc11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIwc11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIwc11__mbstate_tED2Ev
+T __ZNSt3__114collate_bynameIcEC1EPKcm
+T __ZNSt3__114collate_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIcEC2EPKcm
+T __ZNSt3__114collate_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIcED0Ev
+T __ZNSt3__114collate_bynameIcED1Ev
+T __ZNSt3__114collate_bynameIcED2Ev
+T __ZNSt3__114collate_bynameIwEC1EPKcm
+T __ZNSt3__114collate_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIwEC2EPKcm
+T __ZNSt3__114collate_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIwED0Ev
+T __ZNSt3__114collate_bynameIwED1Ev
+T __ZNSt3__114collate_bynameIwED2Ev
+T __ZNSt3__114error_categoryC2Ev
+T __ZNSt3__114error_categoryD0Ev
+T __ZNSt3__114error_categoryD1Ev
+T __ZNSt3__114error_categoryD2Ev
+T __ZNSt3__115__get_classnameEPKcb
+T __ZNSt3__115__thread_struct25notify_all_at_thread_exitEPNS_18condition_variableEPNS_5mutexE
+T __ZNSt3__115__thread_struct27__make_ready_at_thread_exitEPNS_17__assoc_sub_stateE
+T __ZNSt3__115__thread_structC1Ev
+T __ZNSt3__115__thread_structC2Ev
+T __ZNSt3__115__thread_structD1Ev
+T __ZNSt3__115__thread_structD2Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE10pubseekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE10pubseekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4setgEPcS4_S4_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4setpEPcS4_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4syncEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5gbumpEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5pbumpEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sgetcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sgetnEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputcEc
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputnEPKcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5uflowEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6sbumpcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6setbufEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6snextcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsgetnEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsputnEPKcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7pubsyncEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7sungetcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8in_availEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8overflowEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8pubimbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9pbackfailEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9pubsetbufEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9showmanycEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9sputbackcEc
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9underflowEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC1ERKS3_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC1Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2ERKS3_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEaSERKS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE10pubseekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE10pubseekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4setgEPwS4_S4_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4setpEPwS4_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4swapERS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4syncEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5gbumpEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5imbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5pbumpEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sgetcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sgetnEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sputcEw
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sputnEPKwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5uflowEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6sbumpcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6setbufEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6snextcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6xsgetnEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6xsputnEPKwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7pubsyncEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7sungetcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8in_availEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8overflowEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8pubimbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9pbackfailEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9pubsetbufEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9showmanycEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9sputbackcEw
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9underflowEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC1ERKS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC1Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC2ERKS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC2Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED2Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEaSERKS3_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strERKNS_12basic_stringIcS2_S4_EE
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE4swapERS5_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE8overflowEi
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9pbackfailEi
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9underflowEv
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEC1EOS5_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEC2EOS5_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__115future_categoryEv
+T __ZNSt3__115numpunct_bynameIcEC1EPKcm
+T __ZNSt3__115numpunct_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIcEC2EPKcm
+T __ZNSt3__115numpunct_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIcED0Ev
+T __ZNSt3__115numpunct_bynameIcED1Ev
+T __ZNSt3__115numpunct_bynameIcED2Ev
+T __ZNSt3__115numpunct_bynameIwEC1EPKcm
+T __ZNSt3__115numpunct_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIwEC2EPKcm
+T __ZNSt3__115numpunct_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIwED0Ev
+T __ZNSt3__115numpunct_bynameIwED1Ev
+T __ZNSt3__115numpunct_bynameIwED2Ev
+T __ZNSt3__115recursive_mutex4lockEv
+T __ZNSt3__115recursive_mutex6unlockEv
+T __ZNSt3__115recursive_mutex8try_lockEv
+T __ZNSt3__115recursive_mutexC1Ev
+T __ZNSt3__115recursive_mutexC2Ev
+T __ZNSt3__115recursive_mutexD1Ev
+T __ZNSt3__115recursive_mutexD2Ev
+T __ZNSt3__115system_categoryEv
+T __ZNSt3__116__check_groupingERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjS8_Rj
+T __ZNSt3__116__narrow_to_utf8ILm16EED0Ev
+T __ZNSt3__116__narrow_to_utf8ILm16EED1Ev
+T __ZNSt3__116__narrow_to_utf8ILm16EED2Ev
+T __ZNSt3__116__narrow_to_utf8ILm32EED0Ev
+T __ZNSt3__116__narrow_to_utf8ILm32EED1Ev
+T __ZNSt3__116__narrow_to_utf8ILm32EED2Ev
+T __ZNSt3__116generic_categoryEv
+T __ZNSt3__117__assoc_sub_state10__sub_waitERNS_11unique_lockINS_5mutexEEE
+T __ZNSt3__117__assoc_sub_state12__make_readyEv
+T __ZNSt3__117__assoc_sub_state13set_exceptionESt13exception_ptr
+T __ZNSt3__117__assoc_sub_state16__on_zero_sharedEv
+T __ZNSt3__117__assoc_sub_state24set_value_at_thread_exitEv
+T __ZNSt3__117__assoc_sub_state28set_exception_at_thread_exitESt13exception_ptr
+T __ZNSt3__117__assoc_sub_state4copyEv
+T __ZNSt3__117__assoc_sub_state4waitEv
+T __ZNSt3__117__assoc_sub_state9__executeEv
+T __ZNSt3__117__assoc_sub_state9set_valueEv
+T __ZNSt3__117__widen_from_utf8ILm16EED0Ev
+T __ZNSt3__117__widen_from_utf8ILm16EED1Ev
+T __ZNSt3__117__widen_from_utf8ILm16EED2Ev
+T __ZNSt3__117__widen_from_utf8ILm32EED0Ev
+T __ZNSt3__117__widen_from_utf8ILm32EED1Ev
+T __ZNSt3__117__widen_from_utf8ILm32EED2Ev
+T __ZNSt3__117bad_function_callD0Ev
+T __ZNSt3__117bad_function_callD1Ev
+T __ZNSt3__117bad_function_callD2Ev
+T __ZNSt3__117iostream_categoryEv
+T __ZNSt3__117moneypunct_bynameIcLb0EE4initEPKc
+T __ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc
+T __ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc
+T __ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc
+T __ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE
+T __ZNSt3__118__time_get_storageIcEC1EPKc
+T __ZNSt3__118__time_get_storageIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118__time_get_storageIcEC2EPKc
+T __ZNSt3__118__time_get_storageIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118__time_get_storageIwEC1EPKc
+T __ZNSt3__118__time_get_storageIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118__time_get_storageIwEC2EPKc
+T __ZNSt3__118__time_get_storageIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__118condition_variable10notify_allEv
+T __ZNSt3__118condition_variable10notify_oneEv
+T __ZNSt3__118condition_variable15__do_timed_waitERNS_11unique_lockINS_5mutexEEENS_6chrono10time_pointINS5_12system_clockENS5_8durationIxNS_5ratioILl1ELl1000000000EEEEEEE
+T __ZNSt3__118condition_variable4waitERNS_11unique_lockINS_5mutexEEE
+T __ZNSt3__118condition_variableD1Ev
+T __ZNSt3__118condition_variableD2Ev
+T __ZNSt3__118shared_timed_mutex11lock_sharedEv
+T __ZNSt3__118shared_timed_mutex13unlock_sharedEv
+T __ZNSt3__118shared_timed_mutex15try_lock_sharedEv
+T __ZNSt3__118shared_timed_mutex4lockEv
+T __ZNSt3__118shared_timed_mutex6unlockEv
+T __ZNSt3__118shared_timed_mutex8try_lockEv
+T __ZNSt3__118shared_timed_mutexC1Ev
+T __ZNSt3__118shared_timed_mutexC2Ev
+T __ZNSt3__119__is_posix_terminalEP7__sFILE
+T __ZNSt3__119__shared_mutex_base11lock_sharedEv
+T __ZNSt3__119__shared_mutex_base13unlock_sharedEv
+T __ZNSt3__119__shared_mutex_base15try_lock_sharedEv
+T __ZNSt3__119__shared_mutex_base4lockEv
+T __ZNSt3__119__shared_mutex_base6unlockEv
+T __ZNSt3__119__shared_mutex_base8try_lockEv
+T __ZNSt3__119__shared_mutex_baseC1Ev
+T __ZNSt3__119__shared_mutex_baseC2Ev
+T __ZNSt3__119__shared_weak_count10__add_weakEv
+T __ZNSt3__119__shared_weak_count12__add_sharedEv
+T __ZNSt3__119__shared_weak_count14__release_weakEv
+T __ZNSt3__119__shared_weak_count16__release_sharedEv
+T __ZNSt3__119__shared_weak_count4lockEv
+T __ZNSt3__119__shared_weak_countD0Ev
+T __ZNSt3__119__shared_weak_countD1Ev
+T __ZNSt3__119__shared_weak_countD2Ev
+T __ZNSt3__119__thread_local_dataEv
+T __ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__120__atomic_wait_nativeILm8EEEvPKvS2_
+T __ZNSt3__120__get_collation_nameEPKc
+T __ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEEx
+T __ZNSt3__120__libcpp_atomic_waitEPVKvx
+T __ZNSt3__120__throw_system_errorEiPKc
+T __ZNSt3__121__get_locale_encodingEPKc
+T __ZNSt3__121__throw_runtime_errorEPKc
+T __ZNSt3__121recursive_timed_mutex4lockEv
+T __ZNSt3__121recursive_timed_mutex6unlockEv
+T __ZNSt3__121recursive_timed_mutex8try_lockEv
+T __ZNSt3__121recursive_timed_mutexC1Ev
+T __ZNSt3__121recursive_timed_mutexC2Ev
+T __ZNSt3__121recursive_timed_mutexD1Ev
+T __ZNSt3__121recursive_timed_mutexD2Ev
+T __ZNSt3__122__libcpp_verbose_abortEPKcz
+T __ZNSt3__123__atomic_monitor_globalEPKv
+T __ZNSt3__123__cxx_atomic_notify_allEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE
+T __ZNSt3__123__cxx_atomic_notify_allEPVKv
+T __ZNSt3__123__cxx_atomic_notify_oneEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE
+T __ZNSt3__123__cxx_atomic_notify_oneEPVKv
+T __ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE
+T __ZNSt3__123__libcpp_atomic_monitorEPVKv
+T __ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE
+T __ZNSt3__126__atomic_notify_all_nativeILm8EEEvPKv
+T __ZNSt3__126__atomic_notify_one_nativeILm8EEEvPKv
+T __ZNSt3__126__atomic_wait_global_tableEPKvx
+T __ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+T __ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+T __ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh
+T __ZNSt3__132__atomic_notify_all_global_tableEPKv
+T __ZNSt3__132__atomic_notify_one_global_tableEPKv
+T __ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE
+T __ZNSt3__133__atomic_wait_native_with_timeoutILm8EEEvPKvS2_y
+T __ZNSt3__134__construct_barrier_algorithm_baseERl
+T __ZNSt3__139__atomic_wait_global_table_with_timeoutEPKvxy
+S __ZNSt3__13cinE
+T __ZNSt3__13pmr15memory_resourceD0Ev
+T __ZNSt3__13pmr15memory_resourceD1Ev
+T __ZNSt3__13pmr15memory_resourceD2Ev
+T __ZNSt3__13pmr19new_delete_resourceEv
+T __ZNSt3__13pmr20get_default_resourceEv
+T __ZNSt3__13pmr20null_memory_resourceEv
+T __ZNSt3__13pmr20set_default_resourceEPNS0_15memory_resourceE
+T __ZNSt3__13pmr25monotonic_buffer_resource11do_allocateEmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource11do_allocateEmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__do_allocateEPNS0_15memory_resourceEmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__release_ptrEPNS0_15memory_resourceE
+T __ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool15__do_deallocateEPNS0_15memory_resourceEPvmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource13do_deallocateEPvmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource7releaseEv
+T __ZNSt3__13pmr28unsynchronized_pool_resourceC1ERKNS0_12pool_optionsEPNS0_15memory_resourceE
+T __ZNSt3__13pmr28unsynchronized_pool_resourceC2ERKNS0_12pool_optionsEPNS0_15memory_resourceE
+T __ZNSt3__14__fs10filesystem10__absoluteERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem10hash_valueERKNS1_4pathE
+T __ZNSt3__14__fs10filesystem11__canonicalERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem11__copy_fileERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem11__file_sizeERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem12__equivalentERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem12__remove_allERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem13__fs_is_emptyERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem13__permissionsERKNS1_4pathENS1_5permsENS1_12perm_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem13__resize_fileERKNS1_4pathEmPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__copy_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__current_pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__current_pathERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__read_symlinkERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem15directory_entry12__do_refreshEv
+T __ZNSt3__14__fs10filesystem16_FilesystemClock3nowEv
+S __ZNSt3__14__fs10filesystem16_FilesystemClock9is_steadyE
+T __ZNSt3__14__fs10filesystem16__create_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem16__symlink_statusERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem16filesystem_error13__create_whatEi
+T __ZNSt3__14__fs10filesystem16filesystem_errorD0Ev
+T __ZNSt3__14__fs10filesystem16filesystem_errorD1Ev
+T __ZNSt3__14__fs10filesystem16filesystem_errorD2Ev
+T __ZNSt3__14__fs10filesystem17__hard_link_countERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem17__last_write_timeERKNS1_4pathENS_6chrono10time_pointINS1_16_FilesystemClockENS5_8durationInNS_5ratioILl1ELl1000000000EEEEEEEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem17__last_write_timeERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__create_directoryERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__create_directoryERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__create_hard_linkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__weakly_canonicalERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18directory_iterator11__incrementEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18directory_iteratorC1ERKNS1_4pathEPNS_10error_codeENS1_17directory_optionsE
+T __ZNSt3__14__fs10filesystem18directory_iteratorC2ERKNS1_4pathEPNS_10error_codeENS1_17directory_optionsE
+T __ZNSt3__14__fs10filesystem20__create_directoriesERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem21__temp_directory_pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem26__create_directory_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator11__incrementEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator15__try_recursionEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator5__popEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator9__advanceEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iteratorC1ERKNS1_4pathENS1_17directory_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iteratorC2ERKNS1_4pathENS1_17directory_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem4path17replace_extensionERKS2_
+S __ZNSt3__14__fs10filesystem4path19preferred_separatorE
+T __ZNSt3__14__fs10filesystem4path8iterator11__decrementEv
+T __ZNSt3__14__fs10filesystem4path8iterator11__incrementEv
+T __ZNSt3__14__fs10filesystem6__copyERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem7__spaceERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem8__statusERKNS1_4pathEPNS_10error_codeE
+S __ZNSt3__14cerrE
+S __ZNSt3__14clogE
+S __ZNSt3__14coutE
+T __ZNSt3__14stodERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T __ZNSt3__14stodERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T __ZNSt3__14stofERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T __ZNSt3__14stofERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T __ZNSt3__14stoiERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__14stoiERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T __ZNSt3__14stolERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__14stolERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+S __ZNSt3__14wcinE
+T __ZNSt3__15alignEmmRPvRm
+S __ZNSt3__15ctypeIcE10table_sizeE
+T __ZNSt3__15ctypeIcE13classic_tableEv
+S __ZNSt3__15ctypeIcE2idE
+T __ZNSt3__15ctypeIcEC1EPKjbm
+T __ZNSt3__15ctypeIcEC2EPKjbm
+T __ZNSt3__15ctypeIcED0Ev
+T __ZNSt3__15ctypeIcED1Ev
+T __ZNSt3__15ctypeIcED2Ev
+S __ZNSt3__15ctypeIwE2idE
+T __ZNSt3__15ctypeIwED0Ev
+T __ZNSt3__15ctypeIwED1Ev
+T __ZNSt3__15ctypeIwED2Ev
+T __ZNSt3__15mutex4lockEv
+T __ZNSt3__15mutex6unlockEv
+T __ZNSt3__15mutex8try_lockEv
+T __ZNSt3__15mutexD1Ev
+T __ZNSt3__15mutexD2Ev
+T __ZNSt3__15stoldERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T __ZNSt3__15stoldERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T __ZNSt3__15stollERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__15stollERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T __ZNSt3__15stoulERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__15stoulERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+S __ZNSt3__15wcerrE
+S __ZNSt3__15wclogE
+S __ZNSt3__15wcoutE
+T __ZNSt3__16__itoa8__u32toaEjPc
+T __ZNSt3__16__itoa8__u64toaEyPc
+T __ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIeeEEPeEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIffEEPfEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIhhEEPhEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIiiEEPiEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIjjEEPjEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIllEEPlEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessImmEEPmEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIssEEPsEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIttEEPtEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIwwEEPwEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIxxEEPxEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIyyEEPyEEvT0_S5_T_
+T __ZNSt3__16chrono12steady_clock3nowEv
+S __ZNSt3__16chrono12steady_clock9is_steadyE
+T __ZNSt3__16chrono12system_clock11from_time_tEl
+T __ZNSt3__16chrono12system_clock3nowEv
+S __ZNSt3__16chrono12system_clock9is_steadyE
+T __ZNSt3__16chrono12system_clock9to_time_tERKNS0_10time_pointIS1_NS0_8durationIxNS_5ratioILl1ELl1000000EEEEEEE
+T __ZNSt3__16futureIvE3getEv
+T __ZNSt3__16futureIvEC1EPNS_17__assoc_sub_stateE
+T __ZNSt3__16futureIvEC2EPNS_17__assoc_sub_stateE
+T __ZNSt3__16futureIvED1Ev
+T __ZNSt3__16futureIvED2Ev
+T __ZNSt3__16gslice6__initEm
+T __ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl
+T __ZNSt3__16locale2id5__getEv
+S __ZNSt3__16locale2id9__next_idE
+S __ZNSt3__16locale3allE
+S __ZNSt3__16locale4noneE
+S __ZNSt3__16locale4timeE
+S __ZNSt3__16locale5ctypeE
+T __ZNSt3__16locale5facet16__on_zero_sharedEv
+T __ZNSt3__16locale5facetD0Ev
+T __ZNSt3__16locale5facetD1Ev
+T __ZNSt3__16locale5facetD2Ev
+T __ZNSt3__16locale6globalERKS0_
+T __ZNSt3__16locale7classicEv
+S __ZNSt3__16locale7collateE
+S __ZNSt3__16locale7numericE
+T __ZNSt3__16locale8__globalEv
+S __ZNSt3__16locale8messagesE
+S __ZNSt3__16locale8monetaryE
+T __ZNSt3__16localeC1EPKc
+T __ZNSt3__16localeC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__16localeC1ERKS0_
+T __ZNSt3__16localeC1ERKS0_PKci
+T __ZNSt3__16localeC1ERKS0_RKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEi
+T __ZNSt3__16localeC1ERKS0_S2_i
+T __ZNSt3__16localeC1Ev
+T __ZNSt3__16localeC2EPKc
+T __ZNSt3__16localeC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__16localeC2ERKS0_
+T __ZNSt3__16localeC2ERKS0_PKci
+T __ZNSt3__16localeC2ERKS0_RKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEi
+T __ZNSt3__16localeC2ERKS0_S2_i
+T __ZNSt3__16localeC2Ev
+T __ZNSt3__16localeD1Ev
+T __ZNSt3__16localeD2Ev
+T __ZNSt3__16localeaSERKS0_
+T __ZNSt3__16stoullERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__16stoullERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T __ZNSt3__16thread20hardware_concurrencyEv
+T __ZNSt3__16thread4joinEv
+T __ZNSt3__16thread6detachEv
+T __ZNSt3__16threadD1Ev
+T __ZNSt3__16threadD2Ev
+S __ZNSt3__17codecvtIDiDu11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDiDu11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDiDu11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDiDu11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIDic11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDic11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDic11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDic11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIDsDu11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDsDu11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDsDu11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDsDu11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIDsc11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDsc11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDsc11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDsc11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIcc11__mbstate_tE2idE
+T __ZNSt3__17codecvtIcc11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIcc11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIcc11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIwc11__mbstate_tE2idE
+T __ZNSt3__17codecvtIwc11__mbstate_tEC1EPKcm
+T __ZNSt3__17codecvtIwc11__mbstate_tEC1Em
+T __ZNSt3__17codecvtIwc11__mbstate_tEC2EPKcm
+T __ZNSt3__17codecvtIwc11__mbstate_tEC2Em
+T __ZNSt3__17codecvtIwc11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIwc11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIwc11__mbstate_tED2Ev
+D __ZNSt3__17collateIcE2idE
+T __ZNSt3__17collateIcED0Ev
+T __ZNSt3__17collateIcED1Ev
+T __ZNSt3__17collateIcED2Ev
+D __ZNSt3__17collateIwE2idE
+T __ZNSt3__17collateIwED0Ev
+T __ZNSt3__17collateIwED1Ev
+T __ZNSt3__17collateIwED2Ev
+D __ZNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+D __ZNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__17promiseIvE10get_futureEv
+T __ZNSt3__17promiseIvE13set_exceptionESt13exception_ptr
+T __ZNSt3__17promiseIvE24set_value_at_thread_exitEv
+T __ZNSt3__17promiseIvE28set_exception_at_thread_exitESt13exception_ptr
+T __ZNSt3__17promiseIvE9set_valueEv
+T __ZNSt3__17promiseIvEC1Ev
+T __ZNSt3__17promiseIvEC2Ev
+T __ZNSt3__17promiseIvED1Ev
+T __ZNSt3__17promiseIvED2Ev
+T __ZNSt3__18__rs_getEv
+T __ZNSt3__18__sp_mut4lockEv
+T __ZNSt3__18__sp_mut6unlockEv
+S __ZNSt3__18ios_base10floatfieldE
+S __ZNSt3__18ios_base10scientificE
+S __ZNSt3__18ios_base11adjustfieldE
+T __ZNSt3__18ios_base15sync_with_stdioEb
+T __ZNSt3__18ios_base16__call_callbacksENS0_5eventE
+T __ZNSt3__18ios_base17register_callbackEPFvNS0_5eventERS0_iEi
+S __ZNSt3__18ios_base2inE
+T __ZNSt3__18ios_base33__set_badbit_and_consider_rethrowEv
+T __ZNSt3__18ios_base34__set_failbit_and_consider_rethrowEv
+S __ZNSt3__18ios_base3appE
+S __ZNSt3__18ios_base3ateE
+S __ZNSt3__18ios_base3decE
+S __ZNSt3__18ios_base3hexE
+S __ZNSt3__18ios_base3octE
+S __ZNSt3__18ios_base3outE
+T __ZNSt3__18ios_base4InitC1Ev
+T __ZNSt3__18ios_base4InitC2Ev
+T __ZNSt3__18ios_base4InitD1Ev
+T __ZNSt3__18ios_base4InitD2Ev
+T __ZNSt3__18ios_base4initEPv
+S __ZNSt3__18ios_base4leftE
+T __ZNSt3__18ios_base4moveERS0_
+T __ZNSt3__18ios_base4swapERS0_
+T __ZNSt3__18ios_base5clearEj
+S __ZNSt3__18ios_base5fixedE
+T __ZNSt3__18ios_base5imbueERKNS_6localeE
+T __ZNSt3__18ios_base5iwordEi
+T __ZNSt3__18ios_base5pwordEi
+S __ZNSt3__18ios_base5rightE
+S __ZNSt3__18ios_base5truncE
+S __ZNSt3__18ios_base6badbitE
+S __ZNSt3__18ios_base6binaryE
+S __ZNSt3__18ios_base6eofbitE
+S __ZNSt3__18ios_base6skipwsE
+T __ZNSt3__18ios_base6xallocEv
+T __ZNSt3__18ios_base7copyfmtERKS0_
+S __ZNSt3__18ios_base7failbitE
+T __ZNSt3__18ios_base7failureC1EPKcRKNS_10error_codeE
+T __ZNSt3__18ios_base7failureC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_10error_codeE
+T __ZNSt3__18ios_base7failureC2EPKcRKNS_10error_codeE
+T __ZNSt3__18ios_base7failureC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_10error_codeE
+T __ZNSt3__18ios_base7failureD0Ev
+T __ZNSt3__18ios_base7failureD1Ev
+T __ZNSt3__18ios_base7failureD2Ev
+S __ZNSt3__18ios_base7goodbitE
+S __ZNSt3__18ios_base7showposE
+S __ZNSt3__18ios_base7unitbufE
+S __ZNSt3__18ios_base8internalE
+S __ZNSt3__18ios_base8showbaseE
+S __ZNSt3__18ios_base9basefieldE
+S __ZNSt3__18ios_base9boolalphaE
+S __ZNSt3__18ios_base9showpointE
+S __ZNSt3__18ios_base9uppercaseE
+T __ZNSt3__18ios_baseD0Ev
+T __ZNSt3__18ios_baseD1Ev
+T __ZNSt3__18ios_baseD2Ev
+D __ZNSt3__18messagesIcE2idE
+D __ZNSt3__18messagesIwE2idE
+S __ZNSt3__18numpunctIcE2idE
+T __ZNSt3__18numpunctIcEC1Em
+T __ZNSt3__18numpunctIcEC2Em
+T __ZNSt3__18numpunctIcED0Ev
+T __ZNSt3__18numpunctIcED1Ev
+T __ZNSt3__18numpunctIcED2Ev
+S __ZNSt3__18numpunctIwE2idE
+T __ZNSt3__18numpunctIwEC1Em
+T __ZNSt3__18numpunctIwEC2Em
+T __ZNSt3__18numpunctIwED0Ev
+T __ZNSt3__18numpunctIwED1Ev
+T __ZNSt3__18numpunctIwED2Ev
+D __ZNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+D __ZNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__18to_charsEPcS0_d
+T __ZNSt3__18to_charsEPcS0_dNS_12chars_formatE
+T __ZNSt3__18to_charsEPcS0_dNS_12chars_formatEi
+T __ZNSt3__18to_charsEPcS0_e
+T __ZNSt3__18to_charsEPcS0_eNS_12chars_formatE
+T __ZNSt3__18to_charsEPcS0_eNS_12chars_formatEi
+T __ZNSt3__18to_charsEPcS0_f
+T __ZNSt3__18to_charsEPcS0_fNS_12chars_formatE
+T __ZNSt3__18to_charsEPcS0_fNS_12chars_formatEi
+T __ZNSt3__18valarrayImE6resizeEmm
+T __ZNSt3__18valarrayImEC1Em
+T __ZNSt3__18valarrayImEC2Em
+T __ZNSt3__18valarrayImED1Ev
+T __ZNSt3__18valarrayImED2Ev
+T __ZNSt3__19__num_getIcE17__stage2_int_loopEciPcRS2_RjcRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_S2_
+T __ZNSt3__19__num_getIcE17__stage2_int_prepERNS_8ios_baseEPcRc
+T __ZNSt3__19__num_getIcE19__stage2_float_loopEcRbRcPcRS4_ccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjS4_
+T __ZNSt3__19__num_getIcE19__stage2_float_prepERNS_8ios_baseEPcRcS5_
+T __ZNSt3__19__num_getIwE17__stage2_int_loopEwiPcRS2_RjwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_Pw
+T __ZNSt3__19__num_getIwE17__stage2_int_prepERNS_8ios_baseEPwRw
+T __ZNSt3__19__num_getIwE19__stage2_float_loopEwRbRcPcRS4_wwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjPw
+T __ZNSt3__19__num_getIwE19__stage2_float_prepERNS_8ios_baseEPwRwS5_
+T __ZNSt3__19__num_putIcE21__widen_and_group_intEPcS2_S2_S2_RS2_S3_RKNS_6localeE
+T __ZNSt3__19__num_putIcE23__widen_and_group_floatEPcS2_S2_S2_RS2_S3_RKNS_6localeE
+T __ZNSt3__19__num_putIwE21__widen_and_group_intEPcS2_S2_PwRS3_S4_RKNS_6localeE
+T __ZNSt3__19__num_putIwE23__widen_and_group_floatEPcS2_S2_PwRS3_S4_RKNS_6localeE
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEE7copyfmtERKS3_
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEE7copyfmtERKS3_
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEED2Ev
+D __ZNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+T __ZNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE8__do_getERS4_S4_bRKNS_6localeEjRjRbRKNS_5ctypeIcEERNS_10unique_ptrIcPFvPvEEERPcSM_
+D __ZNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE8__do_getERS4_S4_bRKNS_6localeEjRjRbRKNS_5ctypeIwEERNS_10unique_ptrIwPFvPvEEERPwSM_
+D __ZNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__19strstreamD0Ev
+T __ZNSt3__19strstreamD1Ev
+T __ZNSt3__19strstreamD2Ev
+T __ZNSt3__19to_stringEd
+T __ZNSt3__19to_stringEe
+T __ZNSt3__19to_stringEf
+T __ZNSt3__19to_stringEi
+T __ZNSt3__19to_stringEj
+T __ZNSt3__19to_stringEl
+T __ZNSt3__19to_stringEm
+T __ZNSt3__19to_stringEx
+T __ZNSt3__19to_stringEy
+T __ZNSt3__1plIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_12basic_stringIT_T0_T1_EEPKS6_RKS9_
+I __ZNSt8bad_castC1Ev (indirect for __ZNSt8bad_castC1Ev)
+I __ZNSt8bad_castC2Ev (indirect for __ZNSt8bad_castC2Ev)
+I __ZNSt8bad_castD0Ev (indirect for __ZNSt8bad_castD0Ev)
+I __ZNSt8bad_castD1Ev (indirect for __ZNSt8bad_castD1Ev)
+I __ZNSt8bad_castD2Ev (indirect for __ZNSt8bad_castD2Ev)
+I __ZNSt9bad_allocC1Ev (indirect for __ZNSt9bad_allocC1Ev)
+I __ZNSt9bad_allocC2Ev (indirect for __ZNSt9bad_allocC2Ev)
+I __ZNSt9bad_allocD0Ev (indirect for __ZNSt9bad_allocD0Ev)
+I __ZNSt9bad_allocD1Ev (indirect for __ZNSt9bad_allocD1Ev)
+I __ZNSt9bad_allocD2Ev (indirect for __ZNSt9bad_allocD2Ev)
+I __ZNSt9exceptionD0Ev (indirect for __ZNSt9exceptionD0Ev)
+I __ZNSt9exceptionD1Ev (indirect for __ZNSt9exceptionD1Ev)
+I __ZNSt9exceptionD2Ev (indirect for __ZNSt9exceptionD2Ev)
+I __ZNSt9type_infoD0Ev (indirect for __ZNSt9type_infoD0Ev)
+I __ZNSt9type_infoD1Ev (indirect for __ZNSt9type_infoD1Ev)
+I __ZNSt9type_infoD2Ev (indirect for __ZNSt9type_infoD2Ev)
+I __ZSt10unexpectedv (indirect for __ZSt10unexpectedv)
+I __ZSt13get_terminatev (indirect for __ZSt13get_terminatev)
+I __ZSt13set_terminatePFvvE (indirect for __ZSt13set_terminatePFvvE)
+I __ZSt14get_unexpectedv (indirect for __ZSt14get_unexpectedv)
+I __ZSt14set_unexpectedPFvvE (indirect for __ZSt14set_unexpectedPFvvE)
+I __ZSt15get_new_handlerv (indirect for __ZSt15get_new_handlerv)
+I __ZSt15set_new_handlerPFvvE (indirect for __ZSt15set_new_handlerPFvvE)
+T __ZSt17__throw_bad_allocv
+T __ZSt17current_exceptionv
+T __ZSt17rethrow_exceptionSt13exception_ptr
+T __ZSt18uncaught_exceptionv
+T __ZSt19uncaught_exceptionsv
+S __ZSt7nothrow
+I __ZSt9terminatev (indirect for __ZSt9terminatev)
+S __ZTCNSt3__110istrstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__110ostrstreamE0_NS_13basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE16_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE0_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_14basic_iostreamIcS2_EE
+S __ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE16_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__19strstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__19strstreamE0_NS_14basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__19strstreamE16_NS_13basic_ostreamIcNS_11char_traitsIcEEEE
+I __ZTIDh (indirect for __ZTIDh)
+I __ZTIDi (indirect for __ZTIDi)
+I __ZTIDn (indirect for __ZTIDn)
+I __ZTIDs (indirect for __ZTIDs)
+I __ZTIDu (indirect for __ZTIDu)
+I __ZTIN10__cxxabiv116__enum_type_infoE (indirect for __ZTIN10__cxxabiv116__enum_type_infoE)
+I __ZTIN10__cxxabiv116__shim_type_infoE (indirect for __ZTIN10__cxxabiv116__shim_type_infoE)
+I __ZTIN10__cxxabiv117__array_type_infoE (indirect for __ZTIN10__cxxabiv117__array_type_infoE)
+I __ZTIN10__cxxabiv117__pbase_type_infoE (indirect for __ZTIN10__cxxabiv117__pbase_type_infoE)
+I __ZTIN10__cxxabiv119__pointer_type_infoE (indirect for __ZTIN10__cxxabiv119__pointer_type_infoE)
+I __ZTIN10__cxxabiv120__function_type_infoE (indirect for __ZTIN10__cxxabiv120__function_type_infoE)
+I __ZTIN10__cxxabiv123__fundamental_type_infoE (indirect for __ZTIN10__cxxabiv123__fundamental_type_infoE)
+I __ZTIN10__cxxabiv129__pointer_to_member_type_infoE (indirect for __ZTIN10__cxxabiv129__pointer_to_member_type_infoE)
+S __ZTINSt12experimental15fundamentals_v112bad_any_castE
+S __ZTINSt12experimental19bad_optional_accessE
+S __ZTINSt3__110istrstreamE
+S __ZTINSt3__110moneypunctIcLb0EEE
+S __ZTINSt3__110moneypunctIcLb1EEE
+S __ZTINSt3__110moneypunctIwLb0EEE
+S __ZTINSt3__110moneypunctIwLb1EEE
+S __ZTINSt3__110ostrstreamE
+S __ZTINSt3__111regex_errorE
+S __ZTINSt3__112bad_weak_ptrE
+S __ZTINSt3__112ctype_bynameIcEE
+S __ZTINSt3__112ctype_bynameIwEE
+S __ZTINSt3__112future_errorE
+S __ZTINSt3__112strstreambufE
+S __ZTINSt3__112system_errorE
+S __ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+S __ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTINSt3__114__codecvt_utf8IDiEE
+S __ZTINSt3__114__codecvt_utf8IDsEE
+S __ZTINSt3__114__codecvt_utf8IwEE
+S __ZTINSt3__114__shared_countE
+S __ZTINSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIDic11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIDsc11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIcc11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIwc11__mbstate_tEE
+S __ZTINSt3__114collate_bynameIcEE
+S __ZTINSt3__114collate_bynameIwEE
+S __ZTINSt3__114error_categoryE
+S __ZTINSt3__115__codecvt_utf16IDiLb0EEE
+S __ZTINSt3__115__codecvt_utf16IDiLb1EEE
+S __ZTINSt3__115__codecvt_utf16IDsLb0EEE
+S __ZTINSt3__115__codecvt_utf16IDsLb1EEE
+S __ZTINSt3__115__codecvt_utf16IwLb0EEE
+S __ZTINSt3__115__codecvt_utf16IwLb1EEE
+S __ZTINSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+S __ZTINSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+S __ZTINSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__115messages_bynameIcEE
+S __ZTINSt3__115messages_bynameIwEE
+S __ZTINSt3__115numpunct_bynameIcEE
+S __ZTINSt3__115numpunct_bynameIwEE
+S __ZTINSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__116__narrow_to_utf8ILm16EEE
+S __ZTINSt3__116__narrow_to_utf8ILm32EEE
+S __ZTINSt3__117__assoc_sub_stateE
+S __ZTINSt3__117__widen_from_utf8ILm16EEE
+S __ZTINSt3__117__widen_from_utf8ILm32EEE
+S __ZTINSt3__117bad_function_callE
+S __ZTINSt3__117moneypunct_bynameIcLb0EEE
+S __ZTINSt3__117moneypunct_bynameIcLb1EEE
+S __ZTINSt3__117moneypunct_bynameIwLb0EEE
+S __ZTINSt3__117moneypunct_bynameIwLb1EEE
+S __ZTINSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__119__shared_weak_countE
+S __ZTINSt3__119bad_expected_accessIvEE
+S __ZTINSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__120__codecvt_utf8_utf16IDiEE
+S __ZTINSt3__120__codecvt_utf8_utf16IDsEE
+S __ZTINSt3__120__codecvt_utf8_utf16IwEE
+S __ZTINSt3__13pmr15memory_resourceE
+S __ZTINSt3__13pmr25monotonic_buffer_resourceE
+S __ZTINSt3__13pmr26synchronized_pool_resourceE
+S __ZTINSt3__13pmr28unsynchronized_pool_resourceE
+S __ZTINSt3__14__fs10filesystem16filesystem_errorE
+S __ZTINSt3__15ctypeIcEE
+S __ZTINSt3__15ctypeIwEE
+S __ZTINSt3__16locale5facetE
+S __ZTINSt3__17codecvtIDiDu11__mbstate_tEE
+S __ZTINSt3__17codecvtIDic11__mbstate_tEE
+S __ZTINSt3__17codecvtIDsDu11__mbstate_tEE
+S __ZTINSt3__17codecvtIDsc11__mbstate_tEE
+S __ZTINSt3__17codecvtIcc11__mbstate_tEE
+S __ZTINSt3__17codecvtIwc11__mbstate_tEE
+S __ZTINSt3__17collateIcEE
+S __ZTINSt3__17collateIwEE
+S __ZTINSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__18ios_base7failureE
+S __ZTINSt3__18ios_baseE
+S __ZTINSt3__18messagesIcEE
+S __ZTINSt3__18messagesIwEE
+S __ZTINSt3__18numpunctIcEE
+S __ZTINSt3__18numpunctIwEE
+S __ZTINSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__19basic_iosIcNS_11char_traitsIcEEEE
+S __ZTINSt3__19basic_iosIwNS_11char_traitsIwEEEE
+S __ZTINSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__19strstreamE
+I __ZTIPDh (indirect for __ZTIPDh)
+I __ZTIPDi (indirect for __ZTIPDi)
+I __ZTIPDn (indirect for __ZTIPDn)
+I __ZTIPDs (indirect for __ZTIPDs)
+I __ZTIPDu (indirect for __ZTIPDu)
+I __ZTIPKDh (indirect for __ZTIPKDh)
+I __ZTIPKDi (indirect for __ZTIPKDi)
+I __ZTIPKDn (indirect for __ZTIPKDn)
+I __ZTIPKDs (indirect for __ZTIPKDs)
+I __ZTIPKDu (indirect for __ZTIPKDu)
+I __ZTIPKa (indirect for __ZTIPKa)
+I __ZTIPKb (indirect for __ZTIPKb)
+I __ZTIPKc (indirect for __ZTIPKc)
+I __ZTIPKd (indirect for __ZTIPKd)
+I __ZTIPKe (indirect for __ZTIPKe)
+I __ZTIPKf (indirect for __ZTIPKf)
+I __ZTIPKg (indirect for __ZTIPKg)
+I __ZTIPKh (indirect for __ZTIPKh)
+I __ZTIPKi (indirect for __ZTIPKi)
+I __ZTIPKj (indirect for __ZTIPKj)
+I __ZTIPKl (indirect for __ZTIPKl)
+I __ZTIPKm (indirect for __ZTIPKm)
+I __ZTIPKn (indirect for __ZTIPKn)
+I __ZTIPKo (indirect for __ZTIPKo)
+I __ZTIPKs (indirect for __ZTIPKs)
+I __ZTIPKt (indirect for __ZTIPKt)
+I __ZTIPKv (indirect for __ZTIPKv)
+I __ZTIPKw (indirect for __ZTIPKw)
+I __ZTIPKx (indirect for __ZTIPKx)
+I __ZTIPKy (indirect for __ZTIPKy)
+I __ZTIPa (indirect for __ZTIPa)
+I __ZTIPb (indirect for __ZTIPb)
+I __ZTIPc (indirect for __ZTIPc)
+I __ZTIPd (indirect for __ZTIPd)
+I __ZTIPe (indirect for __ZTIPe)
+I __ZTIPf (indirect for __ZTIPf)
+I __ZTIPg (indirect for __ZTIPg)
+I __ZTIPh (indirect for __ZTIPh)
+I __ZTIPi (indirect for __ZTIPi)
+I __ZTIPj (indirect for __ZTIPj)
+I __ZTIPl (indirect for __ZTIPl)
+I __ZTIPm (indirect for __ZTIPm)
+I __ZTIPn (indirect for __ZTIPn)
+I __ZTIPo (indirect for __ZTIPo)
+I __ZTIPs (indirect for __ZTIPs)
+I __ZTIPt (indirect for __ZTIPt)
+I __ZTIPv (indirect for __ZTIPv)
+I __ZTIPw (indirect for __ZTIPw)
+I __ZTIPx (indirect for __ZTIPx)
+I __ZTIPy (indirect for __ZTIPy)
+I __ZTISt10bad_typeid (indirect for __ZTISt10bad_typeid)
+I __ZTISt11logic_error (indirect for __ZTISt11logic_error)
+I __ZTISt11range_error (indirect for __ZTISt11range_error)
+S __ZTISt12bad_any_cast
+I __ZTISt12domain_error (indirect for __ZTISt12domain_error)
+I __ZTISt12length_error (indirect for __ZTISt12length_error)
+I __ZTISt12out_of_range (indirect for __ZTISt12out_of_range)
+I __ZTISt13bad_exception (indirect for __ZTISt13bad_exception)
+I __ZTISt13runtime_error (indirect for __ZTISt13runtime_error)
+I __ZTISt14overflow_error (indirect for __ZTISt14overflow_error)
+I __ZTISt15underflow_error (indirect for __ZTISt15underflow_error)
+I __ZTISt16invalid_argument (indirect for __ZTISt16invalid_argument)
+S __ZTISt16nested_exception
+S __ZTISt18bad_variant_access
+S __ZTISt19bad_optional_access
+I __ZTISt20bad_array_new_length (indirect for __ZTISt20bad_array_new_length)
+S __ZTISt29__bad_variant_access_with_msg
+I __ZTISt8bad_cast (indirect for __ZTISt8bad_cast)
+I __ZTISt9bad_alloc (indirect for __ZTISt9bad_alloc)
+I __ZTISt9exception (indirect for __ZTISt9exception)
+I __ZTISt9type_info (indirect for __ZTISt9type_info)
+I __ZTIa (indirect for __ZTIa)
+I __ZTIb (indirect for __ZTIb)
+I __ZTIc (indirect for __ZTIc)
+I __ZTId (indirect for __ZTId)
+I __ZTIe (indirect for __ZTIe)
+I __ZTIf (indirect for __ZTIf)
+I __ZTIg (indirect for __ZTIg)
+I __ZTIh (indirect for __ZTIh)
+I __ZTIi (indirect for __ZTIi)
+I __ZTIj (indirect for __ZTIj)
+I __ZTIl (indirect for __ZTIl)
+I __ZTIm (indirect for __ZTIm)
+I __ZTIn (indirect for __ZTIn)
+I __ZTIo (indirect for __ZTIo)
+I __ZTIs (indirect for __ZTIs)
+I __ZTIt (indirect for __ZTIt)
+I __ZTIv (indirect for __ZTIv)
+I __ZTIw (indirect for __ZTIw)
+I __ZTIx (indirect for __ZTIx)
+I __ZTIy (indirect for __ZTIy)
+I __ZTSDh (indirect for __ZTSDh)
+I __ZTSDi (indirect for __ZTSDi)
+I __ZTSDn (indirect for __ZTSDn)
+I __ZTSDs (indirect for __ZTSDs)
+I __ZTSDu (indirect for __ZTSDu)
+I __ZTSN10__cxxabiv116__enum_type_infoE (indirect for __ZTSN10__cxxabiv116__enum_type_infoE)
+I __ZTSN10__cxxabiv116__shim_type_infoE (indirect for __ZTSN10__cxxabiv116__shim_type_infoE)
+I __ZTSN10__cxxabiv117__array_type_infoE (indirect for __ZTSN10__cxxabiv117__array_type_infoE)
+I __ZTSN10__cxxabiv117__class_type_infoE (indirect for __ZTSN10__cxxabiv117__class_type_infoE)
+I __ZTSN10__cxxabiv117__pbase_type_infoE (indirect for __ZTSN10__cxxabiv117__pbase_type_infoE)
+I __ZTSN10__cxxabiv119__pointer_type_infoE (indirect for __ZTSN10__cxxabiv119__pointer_type_infoE)
+I __ZTSN10__cxxabiv120__function_type_infoE (indirect for __ZTSN10__cxxabiv120__function_type_infoE)
+I __ZTSN10__cxxabiv120__si_class_type_infoE (indirect for __ZTSN10__cxxabiv120__si_class_type_infoE)
+I __ZTSN10__cxxabiv121__vmi_class_type_infoE (indirect for __ZTSN10__cxxabiv121__vmi_class_type_infoE)
+I __ZTSN10__cxxabiv123__fundamental_type_infoE (indirect for __ZTSN10__cxxabiv123__fundamental_type_infoE)
+I __ZTSN10__cxxabiv129__pointer_to_member_type_infoE (indirect for __ZTSN10__cxxabiv129__pointer_to_member_type_infoE)
+S __ZTSNSt12experimental15fundamentals_v112bad_any_castE
+S __ZTSNSt12experimental19bad_optional_accessE
+S __ZTSNSt3__110istrstreamE
+S __ZTSNSt3__110moneypunctIcLb0EEE
+S __ZTSNSt3__110moneypunctIcLb1EEE
+S __ZTSNSt3__110moneypunctIwLb0EEE
+S __ZTSNSt3__110moneypunctIwLb1EEE
+S __ZTSNSt3__110ostrstreamE
+S __ZTSNSt3__111regex_errorE
+S __ZTSNSt3__112bad_weak_ptrE
+S __ZTSNSt3__112ctype_bynameIcEE
+S __ZTSNSt3__112ctype_bynameIwEE
+S __ZTSNSt3__112future_errorE
+S __ZTSNSt3__112strstreambufE
+S __ZTSNSt3__112system_errorE
+S __ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__114__codecvt_utf8IDiEE
+S __ZTSNSt3__114__codecvt_utf8IDsEE
+S __ZTSNSt3__114__codecvt_utf8IwEE
+S __ZTSNSt3__114__shared_countE
+S __ZTSNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIDic11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIDsc11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIcc11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIwc11__mbstate_tEE
+S __ZTSNSt3__114collate_bynameIcEE
+S __ZTSNSt3__114collate_bynameIwEE
+S __ZTSNSt3__114error_categoryE
+S __ZTSNSt3__115__codecvt_utf16IDiLb0EEE
+S __ZTSNSt3__115__codecvt_utf16IDiLb1EEE
+S __ZTSNSt3__115__codecvt_utf16IDsLb0EEE
+S __ZTSNSt3__115__codecvt_utf16IDsLb1EEE
+S __ZTSNSt3__115__codecvt_utf16IwLb0EEE
+S __ZTSNSt3__115__codecvt_utf16IwLb1EEE
+S __ZTSNSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__115messages_bynameIcEE
+S __ZTSNSt3__115messages_bynameIwEE
+S __ZTSNSt3__115numpunct_bynameIcEE
+S __ZTSNSt3__115numpunct_bynameIwEE
+S __ZTSNSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__116__narrow_to_utf8ILm16EEE
+S __ZTSNSt3__116__narrow_to_utf8ILm32EEE
+S __ZTSNSt3__117__assoc_sub_stateE
+S __ZTSNSt3__117__widen_from_utf8ILm16EEE
+S __ZTSNSt3__117__widen_from_utf8ILm32EEE
+S __ZTSNSt3__117bad_function_callE
+S __ZTSNSt3__117moneypunct_bynameIcLb0EEE
+S __ZTSNSt3__117moneypunct_bynameIcLb1EEE
+S __ZTSNSt3__117moneypunct_bynameIwLb0EEE
+S __ZTSNSt3__117moneypunct_bynameIwLb1EEE
+S __ZTSNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__119__shared_weak_countE
+S __ZTSNSt3__119bad_expected_accessIvEE
+S __ZTSNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__120__codecvt_utf8_utf16IDiEE
+S __ZTSNSt3__120__codecvt_utf8_utf16IDsEE
+S __ZTSNSt3__120__codecvt_utf8_utf16IwEE
+S __ZTSNSt3__13pmr15memory_resourceE
+S __ZTSNSt3__13pmr25monotonic_buffer_resourceE
+S __ZTSNSt3__13pmr26synchronized_pool_resourceE
+S __ZTSNSt3__13pmr28unsynchronized_pool_resourceE
+S __ZTSNSt3__14__fs10filesystem16filesystem_errorE
+S __ZTSNSt3__15ctypeIcEE
+S __ZTSNSt3__15ctypeIwEE
+S __ZTSNSt3__16locale5facetE
+S __ZTSNSt3__17codecvtIDiDu11__mbstate_tEE
+S __ZTSNSt3__17codecvtIDic11__mbstate_tEE
+S __ZTSNSt3__17codecvtIDsDu11__mbstate_tEE
+S __ZTSNSt3__17codecvtIDsc11__mbstate_tEE
+S __ZTSNSt3__17codecvtIcc11__mbstate_tEE
+S __ZTSNSt3__17codecvtIwc11__mbstate_tEE
+S __ZTSNSt3__17collateIcEE
+S __ZTSNSt3__17collateIwEE
+S __ZTSNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__18ios_base7failureE
+S __ZTSNSt3__18ios_baseE
+S __ZTSNSt3__18messagesIcEE
+S __ZTSNSt3__18messagesIwEE
+S __ZTSNSt3__18numpunctIcEE
+S __ZTSNSt3__18numpunctIwEE
+S __ZTSNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__19basic_iosIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__19basic_iosIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__19strstreamE
+I __ZTSPDh (indirect for __ZTSPDh)
+I __ZTSPDi (indirect for __ZTSPDi)
+I __ZTSPDn (indirect for __ZTSPDn)
+I __ZTSPDs (indirect for __ZTSPDs)
+I __ZTSPDu (indirect for __ZTSPDu)
+I __ZTSPKDh (indirect for __ZTSPKDh)
+I __ZTSPKDi (indirect for __ZTSPKDi)
+I __ZTSPKDn (indirect for __ZTSPKDn)
+I __ZTSPKDs (indirect for __ZTSPKDs)
+I __ZTSPKDu (indirect for __ZTSPKDu)
+I __ZTSPKa (indirect for __ZTSPKa)
+I __ZTSPKb (indirect for __ZTSPKb)
+I __ZTSPKc (indirect for __ZTSPKc)
+I __ZTSPKd (indirect for __ZTSPKd)
+I __ZTSPKe (indirect for __ZTSPKe)
+I __ZTSPKf (indirect for __ZTSPKf)
+I __ZTSPKg (indirect for __ZTSPKg)
+I __ZTSPKh (indirect for __ZTSPKh)
+I __ZTSPKi (indirect for __ZTSPKi)
+I __ZTSPKj (indirect for __ZTSPKj)
+I __ZTSPKl (indirect for __ZTSPKl)
+I __ZTSPKm (indirect for __ZTSPKm)
+I __ZTSPKn (indirect for __ZTSPKn)
+I __ZTSPKo (indirect for __ZTSPKo)
+I __ZTSPKs (indirect for __ZTSPKs)
+I __ZTSPKt (indirect for __ZTSPKt)
+I __ZTSPKv (indirect for __ZTSPKv)
+I __ZTSPKw (indirect for __ZTSPKw)
+I __ZTSPKx (indirect for __ZTSPKx)
+I __ZTSPKy (indirect for __ZTSPKy)
+I __ZTSPa (indirect for __ZTSPa)
+I __ZTSPb (indirect for __ZTSPb)
+I __ZTSPc (indirect for __ZTSPc)
+I __ZTSPd (indirect for __ZTSPd)
+I __ZTSPe (indirect for __ZTSPe)
+I __ZTSPf (indirect for __ZTSPf)
+I __ZTSPg (indirect for __ZTSPg)
+I __ZTSPh (indirect for __ZTSPh)
+I __ZTSPi (indirect for __ZTSPi)
+I __ZTSPj (indirect for __ZTSPj)
+I __ZTSPl (indirect for __ZTSPl)
+I __ZTSPm (indirect for __ZTSPm)
+I __ZTSPn (indirect for __ZTSPn)
+I __ZTSPo (indirect for __ZTSPo)
+I __ZTSPs (indirect for __ZTSPs)
+I __ZTSPt (indirect for __ZTSPt)
+I __ZTSPv (indirect for __ZTSPv)
+I __ZTSPw (indirect for __ZTSPw)
+I __ZTSPx (indirect for __ZTSPx)
+I __ZTSPy (indirect for __ZTSPy)
+I __ZTSSt10bad_typeid (indirect for __ZTSSt10bad_typeid)
+I __ZTSSt11logic_error (indirect for __ZTSSt11logic_error)
+I __ZTSSt11range_error (indirect for __ZTSSt11range_error)
+S __ZTSSt12bad_any_cast
+I __ZTSSt12domain_error (indirect for __ZTSSt12domain_error)
+I __ZTSSt12length_error (indirect for __ZTSSt12length_error)
+I __ZTSSt12out_of_range (indirect for __ZTSSt12out_of_range)
+I __ZTSSt13bad_exception (indirect for __ZTSSt13bad_exception)
+I __ZTSSt13runtime_error (indirect for __ZTSSt13runtime_error)
+I __ZTSSt14overflow_error (indirect for __ZTSSt14overflow_error)
+I __ZTSSt15underflow_error (indirect for __ZTSSt15underflow_error)
+I __ZTSSt16invalid_argument (indirect for __ZTSSt16invalid_argument)
+S __ZTSSt16nested_exception
+S __ZTSSt18bad_variant_access
+S __ZTSSt19bad_optional_access
+I __ZTSSt20bad_array_new_length (indirect for __ZTSSt20bad_array_new_length)
+S __ZTSSt29__bad_variant_access_with_msg
+I __ZTSSt8bad_cast (indirect for __ZTSSt8bad_cast)
+I __ZTSSt9bad_alloc (indirect for __ZTSSt9bad_alloc)
+I __ZTSSt9exception (indirect for __ZTSSt9exception)
+I __ZTSSt9type_info (indirect for __ZTSSt9type_info)
+I __ZTSa (indirect for __ZTSa)
+I __ZTSb (indirect for __ZTSb)
+I __ZTSc (indirect for __ZTSc)
+I __ZTSd (indirect for __ZTSd)
+I __ZTSe (indirect for __ZTSe)
+I __ZTSf (indirect for __ZTSf)
+I __ZTSg (indirect for __ZTSg)
+I __ZTSh (indirect for __ZTSh)
+I __ZTSi (indirect for __ZTSi)
+I __ZTSj (indirect for __ZTSj)
+I __ZTSl (indirect for __ZTSl)
+I __ZTSm (indirect for __ZTSm)
+I __ZTSn (indirect for __ZTSn)
+I __ZTSo (indirect for __ZTSo)
+I __ZTSs (indirect for __ZTSs)
+I __ZTSt (indirect for __ZTSt)
+I __ZTSv (indirect for __ZTSv)
+I __ZTSw (indirect for __ZTSw)
+I __ZTSx (indirect for __ZTSx)
+I __ZTSy (indirect for __ZTSy)
+S __ZTTNSt3__110istrstreamE
+S __ZTTNSt3__110ostrstreamE
+S __ZTTNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTTNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTTNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTTNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTTNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTTNSt3__19strstreamE
+I __ZTVN10__cxxabiv116__enum_type_infoE (indirect for __ZTVN10__cxxabiv116__enum_type_infoE)
+I __ZTVN10__cxxabiv116__shim_type_infoE (indirect for __ZTVN10__cxxabiv116__shim_type_infoE)
+I __ZTVN10__cxxabiv117__array_type_infoE (indirect for __ZTVN10__cxxabiv117__array_type_infoE)
+I __ZTVN10__cxxabiv117__class_type_infoE (indirect for __ZTVN10__cxxabiv117__class_type_infoE)
+I __ZTVN10__cxxabiv117__pbase_type_infoE (indirect for __ZTVN10__cxxabiv117__pbase_type_infoE)
+I __ZTVN10__cxxabiv119__pointer_type_infoE (indirect for __ZTVN10__cxxabiv119__pointer_type_infoE)
+I __ZTVN10__cxxabiv120__function_type_infoE (indirect for __ZTVN10__cxxabiv120__function_type_infoE)
+I __ZTVN10__cxxabiv120__si_class_type_infoE (indirect for __ZTVN10__cxxabiv120__si_class_type_infoE)
+I __ZTVN10__cxxabiv121__vmi_class_type_infoE (indirect for __ZTVN10__cxxabiv121__vmi_class_type_infoE)
+I __ZTVN10__cxxabiv123__fundamental_type_infoE (indirect for __ZTVN10__cxxabiv123__fundamental_type_infoE)
+I __ZTVN10__cxxabiv129__pointer_to_member_type_infoE (indirect for __ZTVN10__cxxabiv129__pointer_to_member_type_infoE)
+S __ZTVNSt12experimental15fundamentals_v112bad_any_castE
+S __ZTVNSt12experimental19bad_optional_accessE
+S __ZTVNSt3__110istrstreamE
+S __ZTVNSt3__110moneypunctIcLb0EEE
+S __ZTVNSt3__110moneypunctIcLb1EEE
+S __ZTVNSt3__110moneypunctIwLb0EEE
+S __ZTVNSt3__110moneypunctIwLb1EEE
+S __ZTVNSt3__110ostrstreamE
+S __ZTVNSt3__111regex_errorE
+S __ZTVNSt3__112bad_weak_ptrE
+S __ZTVNSt3__112ctype_bynameIcEE
+S __ZTVNSt3__112ctype_bynameIwEE
+S __ZTVNSt3__112future_errorE
+S __ZTVNSt3__112strstreambufE
+S __ZTVNSt3__112system_errorE
+S __ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__114__codecvt_utf8IDiEE
+S __ZTVNSt3__114__codecvt_utf8IDsEE
+S __ZTVNSt3__114__codecvt_utf8IwEE
+S __ZTVNSt3__114__shared_countE
+S __ZTVNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIDic11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIDsc11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIcc11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIwc11__mbstate_tEE
+S __ZTVNSt3__114collate_bynameIcEE
+S __ZTVNSt3__114collate_bynameIwEE
+S __ZTVNSt3__114error_categoryE
+S __ZTVNSt3__115__codecvt_utf16IDiLb0EEE
+S __ZTVNSt3__115__codecvt_utf16IDiLb1EEE
+S __ZTVNSt3__115__codecvt_utf16IDsLb0EEE
+S __ZTVNSt3__115__codecvt_utf16IDsLb1EEE
+S __ZTVNSt3__115__codecvt_utf16IwLb0EEE
+S __ZTVNSt3__115__codecvt_utf16IwLb1EEE
+S __ZTVNSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__115messages_bynameIcEE
+S __ZTVNSt3__115messages_bynameIwEE
+S __ZTVNSt3__115numpunct_bynameIcEE
+S __ZTVNSt3__115numpunct_bynameIwEE
+S __ZTVNSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__116__narrow_to_utf8ILm16EEE
+S __ZTVNSt3__116__narrow_to_utf8ILm32EEE
+S __ZTVNSt3__117__assoc_sub_stateE
+S __ZTVNSt3__117__widen_from_utf8ILm16EEE
+S __ZTVNSt3__117__widen_from_utf8ILm32EEE
+S __ZTVNSt3__117bad_function_callE
+S __ZTVNSt3__117moneypunct_bynameIcLb0EEE
+S __ZTVNSt3__117moneypunct_bynameIcLb1EEE
+S __ZTVNSt3__117moneypunct_bynameIwLb0EEE
+S __ZTVNSt3__117moneypunct_bynameIwLb1EEE
+S __ZTVNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__119__shared_weak_countE
+S __ZTVNSt3__119bad_expected_accessIvEE
+S __ZTVNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__120__codecvt_utf8_utf16IDiEE
+S __ZTVNSt3__120__codecvt_utf8_utf16IDsEE
+S __ZTVNSt3__120__codecvt_utf8_utf16IwEE
+S __ZTVNSt3__13pmr15memory_resourceE
+S __ZTVNSt3__13pmr25monotonic_buffer_resourceE
+S __ZTVNSt3__13pmr26synchronized_pool_resourceE
+S __ZTVNSt3__13pmr28unsynchronized_pool_resourceE
+S __ZTVNSt3__14__fs10filesystem16filesystem_errorE
+S __ZTVNSt3__15ctypeIcEE
+S __ZTVNSt3__15ctypeIwEE
+S __ZTVNSt3__16locale5facetE
+S __ZTVNSt3__17codecvtIDiDu11__mbstate_tEE
+S __ZTVNSt3__17codecvtIDic11__mbstate_tEE
+S __ZTVNSt3__17codecvtIDsDu11__mbstate_tEE
+S __ZTVNSt3__17codecvtIDsc11__mbstate_tEE
+S __ZTVNSt3__17codecvtIcc11__mbstate_tEE
+S __ZTVNSt3__17codecvtIwc11__mbstate_tEE
+S __ZTVNSt3__17collateIcEE
+S __ZTVNSt3__17collateIwEE
+S __ZTVNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__18ios_base7failureE
+S __ZTVNSt3__18ios_baseE
+S __ZTVNSt3__18messagesIcEE
+S __ZTVNSt3__18messagesIwEE
+S __ZTVNSt3__18numpunctIcEE
+S __ZTVNSt3__18numpunctIwEE
+S __ZTVNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__19basic_iosIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__19basic_iosIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__19strstreamE
+I __ZTVSt10bad_typeid (indirect for __ZTVSt10bad_typeid)
+I __ZTVSt11logic_error (indirect for __ZTVSt11logic_error)
+I __ZTVSt11range_error (indirect for __ZTVSt11range_error)
+S __ZTVSt12bad_any_cast
+I __ZTVSt12domain_error (indirect for __ZTVSt12domain_error)
+I __ZTVSt12length_error (indirect for __ZTVSt12length_error)
+I __ZTVSt12out_of_range (indirect for __ZTVSt12out_of_range)
+I __ZTVSt13bad_exception (indirect for __ZTVSt13bad_exception)
+I __ZTVSt13runtime_error (indirect for __ZTVSt13runtime_error)
+I __ZTVSt14overflow_error (indirect for __ZTVSt14overflow_error)
+I __ZTVSt15underflow_error (indirect for __ZTVSt15underflow_error)
+I __ZTVSt16invalid_argument (indirect for __ZTVSt16invalid_argument)
+S __ZTVSt16nested_exception
+S __ZTVSt18bad_variant_access
+S __ZTVSt19bad_optional_access
+I __ZTVSt20bad_array_new_length (indirect for __ZTVSt20bad_array_new_length)
+S __ZTVSt29__bad_variant_access_with_msg
+I __ZTVSt8bad_cast (indirect for __ZTVSt8bad_cast)
+I __ZTVSt9bad_alloc (indirect for __ZTVSt9bad_alloc)
+I __ZTVSt9exception (indirect for __ZTVSt9exception)
+I __ZTVSt9type_info (indirect for __ZTVSt9type_info)
+T __ZThn16_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+T __ZThn16_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T __ZThn16_NSt3__19strstreamD0Ev
+T __ZThn16_NSt3__19strstreamD1Ev
+T __ZTv0_n24_NSt3__110istrstreamD0Ev
+T __ZTv0_n24_NSt3__110istrstreamD1Ev
+T __ZTv0_n24_NSt3__110ostrstreamD0Ev
+T __ZTv0_n24_NSt3__110ostrstreamD1Ev
+T __ZTv0_n24_NSt3__113basic_istreamIcNS_11char_traitsIcEEED0Ev
+T __ZTv0_n24_NSt3__113basic_istreamIcNS_11char_traitsIcEEED1Ev
+T __ZTv0_n24_NSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev
+T __ZTv0_n24_NSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIwNS_11char_traitsIwEEED0Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIwNS_11char_traitsIwEEED1Ev
+T __ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+T __ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T __ZTv0_n24_NSt3__19strstreamD0Ev
+T __ZTv0_n24_NSt3__19strstreamD1Ev
+I __ZdaPv (indirect for __ZdaPv)
+I __ZdaPvRKSt9nothrow_t (indirect for __ZdaPvRKSt9nothrow_t)
+I __ZdaPvSt11align_val_t (indirect for __ZdaPvSt11align_val_t)
+I __ZdaPvSt11align_val_tRKSt9nothrow_t (indirect for __ZdaPvSt11align_val_tRKSt9nothrow_t)
+I __ZdaPvm (indirect for __ZdaPvm)
+I __ZdaPvmSt11align_val_t (indirect for __ZdaPvmSt11align_val_t)
+I __ZdlPv (indirect for __ZdlPv)
+I __ZdlPvRKSt9nothrow_t (indirect for __ZdlPvRKSt9nothrow_t)
+I __ZdlPvSt11align_val_t (indirect for __ZdlPvSt11align_val_t)
+I __ZdlPvSt11align_val_tRKSt9nothrow_t (indirect for __ZdlPvSt11align_val_tRKSt9nothrow_t)
+I __ZdlPvm (indirect for __ZdlPvm)
+I __ZdlPvmSt11align_val_t (indirect for __ZdlPvmSt11align_val_t)
+I __Znam (indirect for __Znam)
+I __ZnamRKSt9nothrow_t (indirect for __ZnamRKSt9nothrow_t)
+I __ZnamSt11align_val_t (indirect for __ZnamSt11align_val_t)
+I __ZnamSt11align_val_tRKSt9nothrow_t (indirect for __ZnamSt11align_val_tRKSt9nothrow_t)
+I __Znwm (indirect for __Znwm)
+I __ZnwmRKSt9nothrow_t (indirect for __ZnwmRKSt9nothrow_t)
+I __ZnwmSt11align_val_t (indirect for __ZnwmSt11align_val_t)
+I __ZnwmSt11align_val_tRKSt9nothrow_t (indirect for __ZnwmSt11align_val_tRKSt9nothrow_t)
+I ___cxa_allocate_dependent_exception (indirect for ___cxa_allocate_dependent_exception)
+I ___cxa_allocate_exception (indirect for ___cxa_allocate_exception)
+I ___cxa_bad_cast (indirect for ___cxa_bad_cast)
+I ___cxa_bad_typeid (indirect for ___cxa_bad_typeid)
+I ___cxa_begin_catch (indirect for ___cxa_begin_catch)
+I ___cxa_call_terminate (indirect for ___cxa_call_terminate)
+I ___cxa_call_unexpected (indirect for ___cxa_call_unexpected)
+I ___cxa_current_exception_type (indirect for ___cxa_current_exception_type)
+I ___cxa_current_primary_exception (indirect for ___cxa_current_primary_exception)
+I ___cxa_decrement_exception_refcount (indirect for ___cxa_decrement_exception_refcount)
+I ___cxa_deleted_virtual (indirect for ___cxa_deleted_virtual)
+I ___cxa_demangle (indirect for ___cxa_demangle)
+I ___cxa_end_catch (indirect for ___cxa_end_catch)
+I ___cxa_free_dependent_exception (indirect for ___cxa_free_dependent_exception)
+I ___cxa_free_exception (indirect for ___cxa_free_exception)
+I ___cxa_get_exception_ptr (indirect for ___cxa_get_exception_ptr)
+I ___cxa_get_globals (indirect for ___cxa_get_globals)
+I ___cxa_get_globals_fast (indirect for ___cxa_get_globals_fast)
+I ___cxa_guard_abort (indirect for ___cxa_guard_abort)
+I ___cxa_guard_acquire (indirect for ___cxa_guard_acquire)
+I ___cxa_guard_release (indirect for ___cxa_guard_release)
+I ___cxa_increment_exception_refcount (indirect for ___cxa_increment_exception_refcount)
+I ___cxa_init_primary_exception (indirect for ___cxa_init_primary_exception)
+I ___cxa_new_handler (indirect for ___cxa_new_handler)
+I ___cxa_pure_virtual (indirect for ___cxa_pure_virtual)
+I ___cxa_rethrow (indirect for ___cxa_rethrow)
+I ___cxa_terminate_handler (indirect for ___cxa_terminate_handler)
+I ___cxa_throw (indirect for ___cxa_throw)
+I ___cxa_throw_bad_array_new_length (indirect for ___cxa_throw_bad_array_new_length)
+I ___cxa_uncaught_exception (indirect for ___cxa_uncaught_exception)
+I ___cxa_uncaught_exceptions (indirect for ___cxa_uncaught_exceptions)
+I ___cxa_unexpected_handler (indirect for ___cxa_unexpected_handler)
+I ___cxa_vec_cctor (indirect for ___cxa_vec_cctor)
+I ___cxa_vec_cleanup (indirect for ___cxa_vec_cleanup)
+I ___cxa_vec_ctor (indirect for ___cxa_vec_ctor)
+I ___cxa_vec_delete (indirect for ___cxa_vec_delete)
+I ___cxa_vec_delete2 (indirect for ___cxa_vec_delete2)
+I ___cxa_vec_delete3 (indirect for ___cxa_vec_delete3)
+I ___cxa_vec_dtor (indirect for ___cxa_vec_dtor)
+I ___cxa_vec_new (indirect for ___cxa_vec_new)
+I ___cxa_vec_new2 (indirect for ___cxa_vec_new2)
+I ___cxa_vec_new3 (indirect for ___cxa_vec_new3)
+I ___dynamic_cast (indirect for ___dynamic_cast)
+I ___gxx_personality_v0 (indirect for ___gxx_personality_v0)
+T ___muloti4
diff --git a/libcxx/test/libcxx/abilists/x86_64-apple-darwin/abilist.sh.cpp b/libcxx/test/libcxx/abilists/x86_64-apple-darwin/abilist.sh.cpp
new file mode 100644
index 0000000000000..f8f2d6f9499d2
--- /dev/null
+++ b/libcxx/test/libcxx/abilists/x86_64-apple-darwin/abilist.sh.cpp
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FIXME: Encode whether the library was built with exceptions and check the abilist for that case as well
+
+// REQUIRES: target=x86_64-apple-darwin{{.*}}
+
+// Check that the list of symbols exported from the dylib doesn't change unexpectedly
+
+// FILE_DEPENDENCIES: expected.abilist
+
+// RUN: nm --defined-only --extern-only %{lib-dir}/libc++.so.1.0 | cut -c 18- > %t.actual.abilist
+// RUN: diff expected.abilist %t.actual.abilist
diff --git a/libcxx/test/libcxx/abilists/x86_64-apple-darwin/expected.abilist b/libcxx/test/libcxx/abilists/x86_64-apple-darwin/expected.abilist
new file mode 100644
index 0000000000000..97550cd99f7d4
--- /dev/null
+++ b/libcxx/test/libcxx/abilists/x86_64-apple-darwin/expected.abilist
@@ -0,0 +1,2294 @@
+I __ZNKSt10bad_typeid4whatEv (indirect for __ZNKSt10bad_typeid4whatEv)
+I __ZNKSt11logic_error4whatEv (indirect for __ZNKSt11logic_error4whatEv)
+T __ZNKSt12bad_any_cast4whatEv
+T __ZNKSt12experimental15fundamentals_v112bad_any_cast4whatEv
+I __ZNKSt13bad_exception4whatEv (indirect for __ZNKSt13bad_exception4whatEv)
+I __ZNKSt13runtime_error4whatEv (indirect for __ZNKSt13runtime_error4whatEv)
+T __ZNKSt16nested_exception14rethrow_nestedEv
+T __ZNKSt18bad_variant_access4whatEv
+T __ZNKSt19bad_optional_access4whatEv
+I __ZNKSt20bad_array_new_length4whatEv (indirect for __ZNKSt20bad_array_new_length4whatEv)
+T __ZNKSt29__bad_variant_access_with_msg4whatEv
+T __ZNKSt3__110__time_put8__do_putEPcRS1_PK2tmcc
+T __ZNKSt3__110__time_put8__do_putEPwRS1_PK2tmcc
+T __ZNKSt3__110error_code7messageEv
+T __ZNKSt3__110moneypunctIcLb0EE11do_groupingEv
+T __ZNKSt3__110moneypunctIcLb0EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIcLb0EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIcLb0EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIcLb0EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIcLb0EE16do_thousands_sepEv
+T __ZNKSt3__110moneypunctIcLb1EE11do_groupingEv
+T __ZNKSt3__110moneypunctIcLb1EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIcLb1EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIcLb1EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIcLb1EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIcLb1EE16do_thousands_sepEv
+T __ZNKSt3__110moneypunctIwLb0EE11do_groupingEv
+T __ZNKSt3__110moneypunctIwLb0EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIwLb0EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIwLb0EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIwLb0EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIwLb0EE16do_thousands_sepEv
+T __ZNKSt3__110moneypunctIwLb1EE11do_groupingEv
+T __ZNKSt3__110moneypunctIwLb1EE13do_neg_formatEv
+T __ZNKSt3__110moneypunctIwLb1EE13do_pos_formatEv
+T __ZNKSt3__110moneypunctIwLb1EE14do_curr_symbolEv
+T __ZNKSt3__110moneypunctIwLb1EE14do_frac_digitsEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_decimal_pointEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv
+T __ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv
+T __ZNKSt3__112bad_weak_ptr4whatEv
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE16find_last_not_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE17find_first_not_ofEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEcm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5rfindEPKcmm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5rfindEcm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEPKc
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKc
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKcm
+T __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmRKS5_mm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE12find_last_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE13find_first_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE16find_last_not_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE17find_first_not_ofEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE2atEm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4copyEPwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4findEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4findEwm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5rfindEPKwmm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5rfindEwm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEPKw
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmPKw
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmPKwm
+T __ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmRKS5_mm
+T __ZNKSt3__112ctype_bynameIcE10do_tolowerEPcPKc
+T __ZNKSt3__112ctype_bynameIcE10do_tolowerEc
+T __ZNKSt3__112ctype_bynameIcE10do_toupperEPcPKc
+T __ZNKSt3__112ctype_bynameIcE10do_toupperEc
+T __ZNKSt3__112ctype_bynameIwE10do_scan_isEjPKwS3_
+T __ZNKSt3__112ctype_bynameIwE10do_tolowerEPwPKw
+T __ZNKSt3__112ctype_bynameIwE10do_tolowerEw
+T __ZNKSt3__112ctype_bynameIwE10do_toupperEPwPKw
+T __ZNKSt3__112ctype_bynameIwE10do_toupperEw
+T __ZNKSt3__112ctype_bynameIwE11do_scan_notEjPKwS3_
+T __ZNKSt3__112ctype_bynameIwE5do_isEPKwS3_Pj
+T __ZNKSt3__112ctype_bynameIwE5do_isEjw
+T __ZNKSt3__112ctype_bynameIwE8do_widenEPKcS3_Pw
+T __ZNKSt3__112ctype_bynameIwE8do_widenEc
+T __ZNKSt3__112ctype_bynameIwE9do_narrowEPKwS3_cPc
+T __ZNKSt3__112ctype_bynameIwE9do_narrowEwc
+T __ZNKSt3__112strstreambuf6pcountEv
+T __ZNKSt3__113random_device7entropyEv
+T __ZNKSt3__114__codecvt_utf8IDiE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__114__codecvt_utf8IDiE11do_encodingEv
+T __ZNKSt3__114__codecvt_utf8IDiE13do_max_lengthEv
+T __ZNKSt3__114__codecvt_utf8IDiE16do_always_noconvEv
+T __ZNKSt3__114__codecvt_utf8IDiE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDiE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDiE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__114__codecvt_utf8IDsE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__114__codecvt_utf8IDsE11do_encodingEv
+T __ZNKSt3__114__codecvt_utf8IDsE13do_max_lengthEv
+T __ZNKSt3__114__codecvt_utf8IDsE16do_always_noconvEv
+T __ZNKSt3__114__codecvt_utf8IDsE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDsE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IDsE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__114__codecvt_utf8IwE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__114__codecvt_utf8IwE11do_encodingEv
+T __ZNKSt3__114__codecvt_utf8IwE13do_max_lengthEv
+T __ZNKSt3__114__codecvt_utf8IwE16do_always_noconvEv
+T __ZNKSt3__114__codecvt_utf8IwE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IwE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__114__codecvt_utf8IwE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__114collate_bynameIcE10do_compareEPKcS3_S3_S3_
+T __ZNKSt3__114collate_bynameIcE12do_transformEPKcS3_
+T __ZNKSt3__114collate_bynameIwE10do_compareEPKwS3_S3_S3_
+T __ZNKSt3__114collate_bynameIwE12do_transformEPKwS3_
+T __ZNKSt3__114error_category10equivalentERKNS_10error_codeEi
+T __ZNKSt3__114error_category10equivalentEiRKNS_15error_conditionE
+T __ZNKSt3__114error_category23default_error_conditionEi
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDiLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IDsLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IwLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IwLb0EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IwLb0EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IwLb0EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IwLb0EE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb0EE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115__codecvt_utf16IwLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__115__codecvt_utf16IwLb1EE11do_encodingEv
+T __ZNKSt3__115__codecvt_utf16IwLb1EE13do_max_lengthEv
+T __ZNKSt3__115__codecvt_utf16IwLb1EE16do_always_noconvEv
+T __ZNKSt3__115__codecvt_utf16IwLb1EE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb1EE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__115__codecvt_utf16IwLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__115basic_streambufIcNS_11char_traitsIcEEE6getlocEv
+T __ZNKSt3__115basic_streambufIwNS_11char_traitsIwEEE6getlocEv
+T __ZNKSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strEv
+T __ZNKSt3__115error_condition7messageEv
+T __ZNKSt3__117bad_function_call4whatEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb0EE16do_thousands_sepEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIcLb1EE16do_thousands_sepEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb0EE16do_thousands_sepEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE11do_groupingEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE13do_neg_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE13do_pos_formatEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE14do_curr_symbolEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE14do_frac_digitsEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_decimal_pointEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_negative_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_positive_signEv
+T __ZNKSt3__117moneypunct_bynameIwLb1EE16do_thousands_sepEv
+T __ZNKSt3__118__time_get_storageIcE15__do_date_orderEv
+T __ZNKSt3__118__time_get_storageIwE15__do_date_orderEv
+T __ZNKSt3__119__shared_weak_count13__get_deleterERKSt9type_info
+T __ZNKSt3__119bad_expected_accessIvE4whatEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE11do_encodingEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE13do_max_lengthEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE16do_always_noconvEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDiE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE11do_encodingEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE13do_max_lengthEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE16do_always_noconvEv
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IDsE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__120__codecvt_utf8_utf16IwE10do_unshiftER11__mbstate_tPcS4_RS4_
+T __ZNKSt3__120__codecvt_utf8_utf16IwE11do_encodingEv
+T __ZNKSt3__120__codecvt_utf8_utf16IwE13do_max_lengthEv
+T __ZNKSt3__120__codecvt_utf8_utf16IwE16do_always_noconvEv
+T __ZNKSt3__120__codecvt_utf8_utf16IwE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IwE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__120__codecvt_utf8_utf16IwE9do_lengthER11__mbstate_tPKcS5_m
+T __ZNKSt3__120__time_get_c_storageIcE3__XEv
+T __ZNKSt3__120__time_get_c_storageIcE3__cEv
+T __ZNKSt3__120__time_get_c_storageIcE3__rEv
+T __ZNKSt3__120__time_get_c_storageIcE3__xEv
+T __ZNKSt3__120__time_get_c_storageIcE7__am_pmEv
+T __ZNKSt3__120__time_get_c_storageIcE7__weeksEv
+T __ZNKSt3__120__time_get_c_storageIcE8__monthsEv
+T __ZNKSt3__120__time_get_c_storageIwE3__XEv
+T __ZNKSt3__120__time_get_c_storageIwE3__cEv
+T __ZNKSt3__120__time_get_c_storageIwE3__rEv
+T __ZNKSt3__120__time_get_c_storageIwE3__xEv
+T __ZNKSt3__120__time_get_c_storageIwE7__am_pmEv
+T __ZNKSt3__120__time_get_c_storageIwE7__weeksEv
+T __ZNKSt3__120__time_get_c_storageIwE8__monthsEv
+T __ZNKSt3__120__vector_base_commonILb1EE20__throw_length_errorEv
+T __ZNKSt3__120__vector_base_commonILb1EE20__throw_out_of_rangeEv
+T __ZNKSt3__121__basic_string_commonILb1EE20__throw_length_errorEv
+T __ZNKSt3__121__basic_string_commonILb1EE20__throw_out_of_rangeEv
+T __ZNKSt3__123__match_any_but_newlineIcE6__execERNS_7__stateIcEE
+T __ZNKSt3__123__match_any_but_newlineIwE6__execERNS_7__stateIwEE
+T __ZNKSt3__13pmr26synchronized_pool_resource11do_is_equalERKNS0_15memory_resourceE
+T __ZNKSt3__13pmr28unsynchronized_pool_resource12__pool_indexEmm
+T __ZNKSt3__13pmr28unsynchronized_pool_resource17__pool_block_sizeEi
+T __ZNKSt3__13pmr28unsynchronized_pool_resource22__log2_pool_block_sizeEi
+T __ZNKSt3__13pmr28unsynchronized_pool_resource7optionsEv
+T __ZNKSt3__14__fs10filesystem18directory_iterator13__dereferenceEv
+T __ZNKSt3__14__fs10filesystem28recursive_directory_iterator13__dereferenceEv
+T __ZNKSt3__14__fs10filesystem28recursive_directory_iterator5depthEv
+T __ZNKSt3__14__fs10filesystem28recursive_directory_iterator7optionsEv
+T __ZNKSt3__14__fs10filesystem4path10__filenameEv
+T __ZNKSt3__14__fs10filesystem4path11__extensionEv
+T __ZNKSt3__14__fs10filesystem4path11__root_nameEv
+T __ZNKSt3__14__fs10filesystem4path13__parent_pathEv
+T __ZNKSt3__14__fs10filesystem4path15__relative_pathEv
+T __ZNKSt3__14__fs10filesystem4path15__root_path_rawEv
+T __ZNKSt3__14__fs10filesystem4path16__root_directoryEv
+T __ZNKSt3__14__fs10filesystem4path16lexically_normalEv
+T __ZNKSt3__14__fs10filesystem4path18lexically_relativeERKS2_
+T __ZNKSt3__14__fs10filesystem4path3endEv
+T __ZNKSt3__14__fs10filesystem4path5beginEv
+T __ZNKSt3__14__fs10filesystem4path6__stemEv
+T __ZNKSt3__14__fs10filesystem4path9__compareENS_17basic_string_viewIcNS_11char_traitsIcEEEE
+T __ZNKSt3__15ctypeIcE10do_tolowerEPcPKc
+T __ZNKSt3__15ctypeIcE10do_tolowerEc
+T __ZNKSt3__15ctypeIcE10do_toupperEPcPKc
+T __ZNKSt3__15ctypeIcE10do_toupperEc
+T __ZNKSt3__15ctypeIcE8do_widenEPKcS3_Pc
+T __ZNKSt3__15ctypeIcE8do_widenEc
+T __ZNKSt3__15ctypeIcE9do_narrowEPKcS3_cPc
+T __ZNKSt3__15ctypeIcE9do_narrowEcc
+T __ZNKSt3__15ctypeIwE10do_scan_isEjPKwS3_
+T __ZNKSt3__15ctypeIwE10do_tolowerEPwPKw
+T __ZNKSt3__15ctypeIwE10do_tolowerEw
+T __ZNKSt3__15ctypeIwE10do_toupperEPwPKw
+T __ZNKSt3__15ctypeIwE10do_toupperEw
+T __ZNKSt3__15ctypeIwE11do_scan_notEjPKwS3_
+T __ZNKSt3__15ctypeIwE5do_isEPKwS3_Pj
+T __ZNKSt3__15ctypeIwE5do_isEjw
+T __ZNKSt3__15ctypeIwE8do_widenEPKcS3_Pw
+T __ZNKSt3__15ctypeIwE8do_widenEc
+T __ZNKSt3__15ctypeIwE9do_narrowEPKwS3_cPc
+T __ZNKSt3__15ctypeIwE9do_narrowEwc
+T __ZNKSt3__16locale4nameEv
+T __ZNKSt3__16locale9has_facetERNS0_2idE
+T __ZNKSt3__16locale9use_facetERNS0_2idE
+T __ZNKSt3__16localeeqERKS0_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE10do_unshiftERS1_PDuS4_RS4_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE5do_inERS1_PKDuS5_RS5_PDiS7_RS7_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE6do_outERS1_PKDiS5_RS5_PDuS7_RS7_
+T __ZNKSt3__17codecvtIDiDu11__mbstate_tE9do_lengthERS1_PKDuS5_m
+T __ZNKSt3__17codecvtIDic11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIDic11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDic11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDic11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDic11__mbstate_tE5do_inERS1_PKcS5_RS5_PDiS7_RS7_
+T __ZNKSt3__17codecvtIDic11__mbstate_tE6do_outERS1_PKDiS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIDic11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE10do_unshiftERS1_PDuS4_RS4_
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE5do_inERS1_PKDuS5_RS5_PDsS7_RS7_
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE6do_outERS1_PKDsS5_RS5_PDuS7_RS7_
+T __ZNKSt3__17codecvtIDsDu11__mbstate_tE9do_lengthERS1_PKDuS5_m
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE5do_inERS1_PKcS5_RS5_PDsS7_RS7_
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE6do_outERS1_PKDsS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIDsc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17codecvtIcc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIcc11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIcc11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIcc11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIcc11__mbstate_tE5do_inERS1_PKcS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIcc11__mbstate_tE6do_outERS1_PKcS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIcc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17codecvtIwc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T __ZNKSt3__17codecvtIwc11__mbstate_tE11do_encodingEv
+T __ZNKSt3__17codecvtIwc11__mbstate_tE13do_max_lengthEv
+T __ZNKSt3__17codecvtIwc11__mbstate_tE16do_always_noconvEv
+T __ZNKSt3__17codecvtIwc11__mbstate_tE5do_inERS1_PKcS5_RS5_PwS7_RS7_
+T __ZNKSt3__17codecvtIwc11__mbstate_tE6do_outERS1_PKwS5_RS5_PcS7_RS7_
+T __ZNKSt3__17codecvtIwc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T __ZNKSt3__17collateIcE10do_compareEPKcS3_S3_S3_
+T __ZNKSt3__17collateIcE12do_transformEPKcS3_
+T __ZNKSt3__17collateIcE7do_hashEPKcS3_
+T __ZNKSt3__17collateIwE10do_compareEPKwS3_S3_S3_
+T __ZNKSt3__17collateIwE12do_transformEPKwS3_
+T __ZNKSt3__17collateIwE7do_hashEPKwS3_
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRPv
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRb
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRd
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRe
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRf
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRl
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRm
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRt
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRx
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRy
+T __ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjS8_
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRPv
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRb
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRd
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRe
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRf
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRl
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRm
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRt
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRx
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRy
+T __ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjS8_
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcPKv
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcb
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcd
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEce
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcl
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcm
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcx
+T __ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcy
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPKv
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwb
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwd
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwe
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwl
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwm
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwx
+T __ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwy
+T __ZNKSt3__18ios_base6getlocEv
+T __ZNKSt3__18messagesIcE6do_getEliiRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNKSt3__18messagesIcE7do_openERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_6localeE
+T __ZNKSt3__18messagesIcE8do_closeEl
+T __ZNKSt3__18messagesIwE6do_getEliiRKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEE
+T __ZNKSt3__18messagesIwE7do_openERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_6localeE
+T __ZNKSt3__18messagesIwE8do_closeEl
+T __ZNKSt3__18numpunctIcE11do_groupingEv
+T __ZNKSt3__18numpunctIcE11do_truenameEv
+T __ZNKSt3__18numpunctIcE12do_falsenameEv
+T __ZNKSt3__18numpunctIcE16do_decimal_pointEv
+T __ZNKSt3__18numpunctIcE16do_thousands_sepEv
+T __ZNKSt3__18numpunctIwE11do_groupingEv
+T __ZNKSt3__18numpunctIwE11do_truenameEv
+T __ZNKSt3__18numpunctIwE12do_falsenameEv
+T __ZNKSt3__18numpunctIwE16do_decimal_pointEv
+T __ZNKSt3__18numpunctIwE16do_thousands_sepEv
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE10__get_hourERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE10__get_yearERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_am_pmERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_monthERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_year4ERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_dateES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_timeES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_yearES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE12__get_minuteERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE12__get_secondERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_12_hourERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_percentERS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_weekdayERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13do_date_orderEv
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE14do_get_weekdayES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE15__get_monthnameERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE16do_get_monthnameES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE17__get_weekdaynameERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE17__get_white_spaceERS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE18__get_day_year_numERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE3getES4_S4_RNS_8ios_baseERjP2tmPKcSC_
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjP2tmcc
+T __ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE9__get_dayERiRS4_S4_RjRKNS_5ctypeIcEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE10__get_hourERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE10__get_yearERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_am_pmERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_monthERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_year4ERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_dateES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_timeES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_yearES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE12__get_minuteERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE12__get_secondERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_12_hourERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_percentERS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_weekdayERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13do_date_orderEv
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE14do_get_weekdayES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE15__get_monthnameERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE16do_get_monthnameES4_S4_RNS_8ios_baseERjP2tm
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE17__get_weekdaynameERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE17__get_white_spaceERS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE18__get_day_year_numERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE3getES4_S4_RNS_8ios_baseERjP2tmPKwSC_
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjP2tmcc
+T __ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE9__get_dayERiRS4_S4_RjRKNS_5ctypeIwEE
+T __ZNKSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE3putES4_RNS_8ios_baseEcPK2tmPKcSC_
+T __ZNKSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcPK2tmcc
+T __ZNKSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE3putES4_RNS_8ios_baseEwPK2tmPKwSC_
+T __ZNKSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPK2tmcc
+T __ZNKSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_bRNS_8ios_baseERjRNS_12basic_stringIcS3_NS_9allocatorIcEEEE
+T __ZNKSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_bRNS_8ios_baseERjRe
+T __ZNKSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_bRNS_8ios_baseERjRNS_12basic_stringIwS3_NS_9allocatorIwEEEE
+T __ZNKSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_bRNS_8ios_baseERjRe
+T __ZNKSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_bRNS_8ios_baseEcRKNS_12basic_stringIcS3_NS_9allocatorIcEEEE
+T __ZNKSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_bRNS_8ios_baseEce
+T __ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwRKNS_12basic_stringIwS3_NS_9allocatorIwEEEE
+T __ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwe
+I __ZNKSt8bad_cast4whatEv (indirect for __ZNKSt8bad_cast4whatEv)
+I __ZNKSt9bad_alloc4whatEv (indirect for __ZNKSt9bad_alloc4whatEv)
+I __ZNKSt9exception4whatEv (indirect for __ZNKSt9exception4whatEv)
+I __ZNSt10bad_typeidC1Ev (indirect for __ZNSt10bad_typeidC1Ev)
+I __ZNSt10bad_typeidC2Ev (indirect for __ZNSt10bad_typeidC2Ev)
+I __ZNSt10bad_typeidD0Ev (indirect for __ZNSt10bad_typeidD0Ev)
+I __ZNSt10bad_typeidD1Ev (indirect for __ZNSt10bad_typeidD1Ev)
+I __ZNSt10bad_typeidD2Ev (indirect for __ZNSt10bad_typeidD2Ev)
+T __ZNSt11logic_errorC1EPKc
+T __ZNSt11logic_errorC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt11logic_errorC1ERKS_
+T __ZNSt11logic_errorC2EPKc
+T __ZNSt11logic_errorC2ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt11logic_errorC2ERKS_
+I __ZNSt11logic_errorD0Ev (indirect for __ZNSt11logic_errorD0Ev)
+I __ZNSt11logic_errorD1Ev (indirect for __ZNSt11logic_errorD1Ev)
+I __ZNSt11logic_errorD2Ev (indirect for __ZNSt11logic_errorD2Ev)
+T __ZNSt11logic_erroraSERKS_
+I __ZNSt11range_errorD0Ev (indirect for __ZNSt11range_errorD0Ev)
+I __ZNSt11range_errorD1Ev (indirect for __ZNSt11range_errorD1Ev)
+I __ZNSt11range_errorD2Ev (indirect for __ZNSt11range_errorD2Ev)
+I __ZNSt12domain_errorD0Ev (indirect for __ZNSt12domain_errorD0Ev)
+I __ZNSt12domain_errorD1Ev (indirect for __ZNSt12domain_errorD1Ev)
+I __ZNSt12domain_errorD2Ev (indirect for __ZNSt12domain_errorD2Ev)
+T __ZNSt12experimental19bad_optional_accessD0Ev
+T __ZNSt12experimental19bad_optional_accessD1Ev
+T __ZNSt12experimental19bad_optional_accessD2Ev
+I __ZNSt12length_errorD0Ev (indirect for __ZNSt12length_errorD0Ev)
+I __ZNSt12length_errorD1Ev (indirect for __ZNSt12length_errorD1Ev)
+I __ZNSt12length_errorD2Ev (indirect for __ZNSt12length_errorD2Ev)
+I __ZNSt12out_of_rangeD0Ev (indirect for __ZNSt12out_of_rangeD0Ev)
+I __ZNSt12out_of_rangeD1Ev (indirect for __ZNSt12out_of_rangeD1Ev)
+I __ZNSt12out_of_rangeD2Ev (indirect for __ZNSt12out_of_rangeD2Ev)
+I __ZNSt13bad_exceptionD0Ev (indirect for __ZNSt13bad_exceptionD0Ev)
+I __ZNSt13bad_exceptionD1Ev (indirect for __ZNSt13bad_exceptionD1Ev)
+I __ZNSt13bad_exceptionD2Ev (indirect for __ZNSt13bad_exceptionD2Ev)
+T __ZNSt13exception_ptr31__from_native_exception_pointerEPv
+T __ZNSt13exception_ptrC1ERKS_
+T __ZNSt13exception_ptrC2ERKS_
+T __ZNSt13exception_ptrD1Ev
+T __ZNSt13exception_ptrD2Ev
+T __ZNSt13exception_ptraSERKS_
+T __ZNSt13runtime_errorC1EPKc
+T __ZNSt13runtime_errorC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt13runtime_errorC1ERKS_
+T __ZNSt13runtime_errorC2EPKc
+T __ZNSt13runtime_errorC2ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T __ZNSt13runtime_errorC2ERKS_
+I __ZNSt13runtime_errorD0Ev (indirect for __ZNSt13runtime_errorD0Ev)
+I __ZNSt13runtime_errorD1Ev (indirect for __ZNSt13runtime_errorD1Ev)
+I __ZNSt13runtime_errorD2Ev (indirect for __ZNSt13runtime_errorD2Ev)
+T __ZNSt13runtime_erroraSERKS_
+I __ZNSt14overflow_errorD0Ev (indirect for __ZNSt14overflow_errorD0Ev)
+I __ZNSt14overflow_errorD1Ev (indirect for __ZNSt14overflow_errorD1Ev)
+I __ZNSt14overflow_errorD2Ev (indirect for __ZNSt14overflow_errorD2Ev)
+I __ZNSt15underflow_errorD0Ev (indirect for __ZNSt15underflow_errorD0Ev)
+I __ZNSt15underflow_errorD1Ev (indirect for __ZNSt15underflow_errorD1Ev)
+I __ZNSt15underflow_errorD2Ev (indirect for __ZNSt15underflow_errorD2Ev)
+I __ZNSt16invalid_argumentD0Ev (indirect for __ZNSt16invalid_argumentD0Ev)
+I __ZNSt16invalid_argumentD1Ev (indirect for __ZNSt16invalid_argumentD1Ev)
+I __ZNSt16invalid_argumentD2Ev (indirect for __ZNSt16invalid_argumentD2Ev)
+T __ZNSt16nested_exceptionC1Ev
+T __ZNSt16nested_exceptionC2Ev
+T __ZNSt16nested_exceptionD0Ev
+T __ZNSt16nested_exceptionD1Ev
+T __ZNSt16nested_exceptionD2Ev
+T __ZNSt19bad_optional_accessD0Ev
+T __ZNSt19bad_optional_accessD1Ev
+T __ZNSt19bad_optional_accessD2Ev
+I __ZNSt20bad_array_new_lengthC1Ev (indirect for __ZNSt20bad_array_new_lengthC1Ev)
+I __ZNSt20bad_array_new_lengthC2Ev (indirect for __ZNSt20bad_array_new_lengthC2Ev)
+I __ZNSt20bad_array_new_lengthD0Ev (indirect for __ZNSt20bad_array_new_lengthD0Ev)
+I __ZNSt20bad_array_new_lengthD1Ev (indirect for __ZNSt20bad_array_new_lengthD1Ev)
+I __ZNSt20bad_array_new_lengthD2Ev (indirect for __ZNSt20bad_array_new_lengthD2Ev)
+T __ZNSt3__110__time_getC1EPKc
+T __ZNSt3__110__time_getC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_getC2EPKc
+T __ZNSt3__110__time_getC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_getD1Ev
+T __ZNSt3__110__time_getD2Ev
+T __ZNSt3__110__time_putC1EPKc
+T __ZNSt3__110__time_putC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_putC2EPKc
+T __ZNSt3__110__time_putC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__110__time_putD1Ev
+T __ZNSt3__110__time_putD2Ev
+S __ZNSt3__110ctype_base5alnumE
+S __ZNSt3__110ctype_base5alphaE
+S __ZNSt3__110ctype_base5blankE
+S __ZNSt3__110ctype_base5cntrlE
+S __ZNSt3__110ctype_base5digitE
+S __ZNSt3__110ctype_base5graphE
+S __ZNSt3__110ctype_base5lowerE
+S __ZNSt3__110ctype_base5printE
+S __ZNSt3__110ctype_base5punctE
+S __ZNSt3__110ctype_base5spaceE
+S __ZNSt3__110ctype_base5upperE
+S __ZNSt3__110ctype_base6xdigitE
+T __ZNSt3__110istrstreamD0Ev
+T __ZNSt3__110istrstreamD1Ev
+T __ZNSt3__110istrstreamD2Ev
+D __ZNSt3__110moneypunctIcLb0EE2idE
+S __ZNSt3__110moneypunctIcLb0EE4intlE
+D __ZNSt3__110moneypunctIcLb1EE2idE
+S __ZNSt3__110moneypunctIcLb1EE4intlE
+D __ZNSt3__110moneypunctIwLb0EE2idE
+S __ZNSt3__110moneypunctIwLb0EE4intlE
+D __ZNSt3__110moneypunctIwLb1EE2idE
+S __ZNSt3__110moneypunctIwLb1EE4intlE
+T __ZNSt3__110ostrstreamD0Ev
+T __ZNSt3__110ostrstreamD1Ev
+T __ZNSt3__110ostrstreamD2Ev
+T __ZNSt3__110to_wstringEd
+T __ZNSt3__110to_wstringEe
+T __ZNSt3__110to_wstringEf
+T __ZNSt3__110to_wstringEi
+T __ZNSt3__110to_wstringEj
+T __ZNSt3__110to_wstringEl
+T __ZNSt3__110to_wstringEm
+T __ZNSt3__110to_wstringEx
+T __ZNSt3__110to_wstringEy
+T __ZNSt3__111__call_onceERVmPvPFvS2_E
+T __ZNSt3__111__money_getIcE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_SF_Ri
+T __ZNSt3__111__money_getIwE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_SJ_Ri
+T __ZNSt3__111__money_putIcE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_Ri
+T __ZNSt3__111__money_putIcE8__formatEPcRS2_S3_jPKcS5_RKNS_5ctypeIcEEbRKNS_10money_base7patternEccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESL_SL_i
+T __ZNSt3__111__money_putIwE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_Ri
+T __ZNSt3__111__money_putIwE8__formatEPwRS2_S3_jPKwS5_RKNS_5ctypeIwEEbRKNS_10money_base7patternEwwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNSE_IwNSF_IwEENSH_IwEEEESQ_i
+T __ZNSt3__111regex_errorC1ENS_15regex_constants10error_typeE
+T __ZNSt3__111regex_errorC2ENS_15regex_constants10error_typeE
+T __ZNSt3__111regex_errorD0Ev
+T __ZNSt3__111regex_errorD1Ev
+T __ZNSt3__111regex_errorD2Ev
+T __ZNSt3__111this_thread9sleep_forERKNS_6chrono8durationIxNS_5ratioILl1ELl1000000000EEEEE
+T __ZNSt3__111timed_mutex4lockEv
+T __ZNSt3__111timed_mutex6unlockEv
+T __ZNSt3__111timed_mutex8try_lockEv
+T __ZNSt3__111timed_mutexC1Ev
+T __ZNSt3__111timed_mutexC2Ev
+T __ZNSt3__111timed_mutexD1Ev
+T __ZNSt3__111timed_mutexD2Ev
+T __ZNSt3__112__do_nothingEPv
+T __ZNSt3__112__get_sp_mutEPKv
+T __ZNSt3__112__next_primeEm
+S __ZNSt3__112__rs_default4__c_E
+T __ZNSt3__112__rs_defaultC1ERKS0_
+T __ZNSt3__112__rs_defaultC1Ev
+T __ZNSt3__112__rs_defaultC2ERKS0_
+T __ZNSt3__112__rs_defaultC2Ev
+T __ZNSt3__112__rs_defaultD1Ev
+T __ZNSt3__112__rs_defaultD2Ev
+T __ZNSt3__112__rs_defaultclEv
+T __ZNSt3__112bad_weak_ptrD0Ev
+T __ZNSt3__112bad_weak_ptrD1Ev
+T __ZNSt3__112bad_weak_ptrD2Ev
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE21__grow_by_and_replaceEmmmmmmPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm
+S __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4nposE
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5eraseEmm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcmm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendERKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignERKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertENS_11__wrap_iterIPKcEEc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmRKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6resizeEmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmPKc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmPKcm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmRKS5_mm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmmc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEmmmmmm
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_RKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_RKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSERKS5_
+T __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEc
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE21__grow_by_and_replaceEmmmmmmPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE2atEm
+S __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4nposE
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5eraseEmm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEPKwmm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendERKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignERKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertENS_11__wrap_iterIPKwEEw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmRKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6resizeEmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmPKw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmPKwm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmRKS5_mm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmmw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7reserveEm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9__grow_byEmmmmmm
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9push_backEw
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_RKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_RKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_mmRKS4_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED1Ev
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED2Ev
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEaSERKS5_
+T __ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEaSEw
+T __ZNSt3__112ctype_bynameIcEC1EPKcm
+T __ZNSt3__112ctype_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIcEC2EPKcm
+T __ZNSt3__112ctype_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIcED0Ev
+T __ZNSt3__112ctype_bynameIcED1Ev
+T __ZNSt3__112ctype_bynameIcED2Ev
+T __ZNSt3__112ctype_bynameIwEC1EPKcm
+T __ZNSt3__112ctype_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIwEC2EPKcm
+T __ZNSt3__112ctype_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__112ctype_bynameIwED0Ev
+T __ZNSt3__112ctype_bynameIwED1Ev
+T __ZNSt3__112ctype_bynameIwED2Ev
+T __ZNSt3__112future_errorC1ENS_10error_codeE
+T __ZNSt3__112future_errorC2ENS_10error_codeE
+T __ZNSt3__112future_errorD0Ev
+T __ZNSt3__112future_errorD1Ev
+T __ZNSt3__112future_errorD2Ev
+S __ZNSt3__112placeholders2_1E
+S __ZNSt3__112placeholders2_2E
+S __ZNSt3__112placeholders2_3E
+S __ZNSt3__112placeholders2_4E
+S __ZNSt3__112placeholders2_5E
+S __ZNSt3__112placeholders2_6E
+S __ZNSt3__112placeholders2_7E
+S __ZNSt3__112placeholders2_8E
+S __ZNSt3__112placeholders2_9E
+S __ZNSt3__112placeholders3_10E
+T __ZNSt3__112strstreambuf3strEv
+T __ZNSt3__112strstreambuf4swapERS0_
+T __ZNSt3__112strstreambuf6__initEPclS1_
+T __ZNSt3__112strstreambuf6freezeEb
+T __ZNSt3__112strstreambuf7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__112strstreambuf7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__112strstreambuf8overflowEi
+T __ZNSt3__112strstreambuf9pbackfailEi
+T __ZNSt3__112strstreambuf9underflowEv
+T __ZNSt3__112strstreambufC1EPFPvmEPFvS1_E
+T __ZNSt3__112strstreambufC1EPKal
+T __ZNSt3__112strstreambufC1EPKcl
+T __ZNSt3__112strstreambufC1EPKhl
+T __ZNSt3__112strstreambufC1EPalS1_
+T __ZNSt3__112strstreambufC1EPclS1_
+T __ZNSt3__112strstreambufC1EPhlS1_
+T __ZNSt3__112strstreambufC1El
+T __ZNSt3__112strstreambufC2EPFPvmEPFvS1_E
+T __ZNSt3__112strstreambufC2EPKal
+T __ZNSt3__112strstreambufC2EPKcl
+T __ZNSt3__112strstreambufC2EPKhl
+T __ZNSt3__112strstreambufC2EPalS1_
+T __ZNSt3__112strstreambufC2EPclS1_
+T __ZNSt3__112strstreambufC2EPhlS1_
+T __ZNSt3__112strstreambufC2El
+T __ZNSt3__112strstreambufD0Ev
+T __ZNSt3__112strstreambufD1Ev
+T __ZNSt3__112strstreambufD2Ev
+T __ZNSt3__112system_errorC1ENS_10error_codeE
+T __ZNSt3__112system_errorC1ENS_10error_codeEPKc
+T __ZNSt3__112system_errorC1ENS_10error_codeERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorC1EiRKNS_14error_categoryE
+T __ZNSt3__112system_errorC1EiRKNS_14error_categoryEPKc
+T __ZNSt3__112system_errorC1EiRKNS_14error_categoryERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorC2ENS_10error_codeE
+T __ZNSt3__112system_errorC2ENS_10error_codeEPKc
+T __ZNSt3__112system_errorC2ENS_10error_codeERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorC2EiRKNS_14error_categoryE
+T __ZNSt3__112system_errorC2EiRKNS_14error_categoryEPKc
+T __ZNSt3__112system_errorC2EiRKNS_14error_categoryERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__112system_errorD0Ev
+T __ZNSt3__112system_errorD1Ev
+T __ZNSt3__112system_errorD2Ev
+T __ZNSt3__113__hash_memoryEPKvm
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE11__read_modeEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE12__write_modeEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4openEPKcj
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4syncEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5closeEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE6setbufEPcl
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE8overflowEi
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9pbackfailEi
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9underflowEv
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC1EOS3_
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC1Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2EOS3_
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEPclc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERNS_15basic_streambufIcS2_EEc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4peekEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4readEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4syncEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5seekgENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5seekgExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5tellgEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5ungetEv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6ignoreEli
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6sentryC1ERS3_b
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6sentryC2ERS3_b
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7getlineEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7getlineEPclc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7putbackEc
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE8readsomeEPcl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRNS_9basic_iosIcS2_EES6_E
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRS3_S4_E
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERPv
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERb
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERd
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERe
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERf
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERi
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERj
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERl
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERm
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERs
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERt
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERx
+T __ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERy
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEPwlw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERNS_15basic_streambufIwS2_EEw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4peekEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4readEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4swapERS3_
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4syncEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5seekgENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5seekgExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5tellgEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5ungetEv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6ignoreEli
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6sentryC1ERS3_b
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6sentryC2ERS3_b
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7getlineEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7getlineEPwlw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7putbackEw
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE8readsomeEPwl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEEC1EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEEC2EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED2Ev
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRNS_9basic_iosIwS2_EES6_E
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRS3_S4_E
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERPv
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERb
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERd
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERe
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERf
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERi
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERj
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERl
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERm
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERs
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERt
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERx
+T __ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERy
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE3putEc
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5seekpENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5seekpExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5tellpEv
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5writeEPKcl
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC2ERS3_
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD2Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRNS_9basic_iosIcS2_EES6_E
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRS3_S4_E
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPKv
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPNS_15basic_streambufIcS2_EE
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEb
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEd
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEe
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEf
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEi
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEj
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEl
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEm
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEs
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEt
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEx
+T __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEy
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE3putEw
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE4swapERS3_
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5flushEv
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5seekpENS_4fposI11__mbstate_tEE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5seekpExNS_8ios_base7seekdirE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5tellpEv
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5writeEPKwl
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryC1ERS3_
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryC2ERS3_
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryD1Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryD2Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEEC1EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEEC2EPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED2Ev
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRNS_8ios_baseES5_E
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRNS_9basic_iosIwS2_EES6_E
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRS3_S4_E
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPKv
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPNS_15basic_streambufIwS2_EE
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEb
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEd
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEe
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEf
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEi
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEj
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEl
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEm
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEs
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEt
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEx
+T __ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEy
+T __ZNSt3__113random_deviceC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__113random_deviceC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__113random_deviceD1Ev
+T __ZNSt3__113random_deviceD2Ev
+T __ZNSt3__113random_deviceclEv
+T __ZNSt3__113shared_futureIvED1Ev
+T __ZNSt3__113shared_futureIvED2Ev
+T __ZNSt3__113shared_futureIvEaSERKS1_
+T __ZNSt3__114__num_get_base10__get_baseERNS_8ios_baseE
+S __ZNSt3__114__num_get_base5__srcE
+T __ZNSt3__114__num_put_base12__format_intEPcPKcbj
+T __ZNSt3__114__num_put_base14__format_floatEPcPKcj
+T __ZNSt3__114__num_put_base18__identify_paddingEPcS1_RKNS_8ios_baseE
+T __ZNSt3__114__shared_count12__add_sharedEv
+T __ZNSt3__114__shared_count16__release_sharedEv
+T __ZNSt3__114__shared_countD0Ev
+T __ZNSt3__114__shared_countD1Ev
+T __ZNSt3__114__shared_countD2Ev
+T __ZNSt3__114basic_ifstreamIcNS_11char_traitsIcEEE4openEPKcj
+T __ZNSt3__114basic_ifstreamIcNS_11char_traitsIcEEE4openERKNS_12basic_stringIcS2_NS_9allocatorIcEEEEj
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__114basic_ofstreamIcNS_11char_traitsIcEEE4openEPKcj
+T __ZNSt3__114basic_ofstreamIcNS_11char_traitsIcEEE4openERKNS_12basic_stringIcS2_NS_9allocatorIcEEEEj
+T __ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIDic11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDic11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDic11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIDsc11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIDsc11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIDsc11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIcc11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIcc11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIcc11__mbstate_tED2Ev
+T __ZNSt3__114codecvt_bynameIwc11__mbstate_tED0Ev
+T __ZNSt3__114codecvt_bynameIwc11__mbstate_tED1Ev
+T __ZNSt3__114codecvt_bynameIwc11__mbstate_tED2Ev
+T __ZNSt3__114collate_bynameIcEC1EPKcm
+T __ZNSt3__114collate_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIcEC2EPKcm
+T __ZNSt3__114collate_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIcED0Ev
+T __ZNSt3__114collate_bynameIcED1Ev
+T __ZNSt3__114collate_bynameIcED2Ev
+T __ZNSt3__114collate_bynameIwEC1EPKcm
+T __ZNSt3__114collate_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIwEC2EPKcm
+T __ZNSt3__114collate_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__114collate_bynameIwED0Ev
+T __ZNSt3__114collate_bynameIwED1Ev
+T __ZNSt3__114collate_bynameIwED2Ev
+T __ZNSt3__114error_categoryC2Ev
+T __ZNSt3__114error_categoryD0Ev
+T __ZNSt3__114error_categoryD1Ev
+T __ZNSt3__114error_categoryD2Ev
+T __ZNSt3__115__get_classnameEPKcb
+T __ZNSt3__115__thread_struct25notify_all_at_thread_exitEPNS_18condition_variableEPNS_5mutexE
+T __ZNSt3__115__thread_struct27__make_ready_at_thread_exitEPNS_17__assoc_sub_stateE
+T __ZNSt3__115__thread_structC1Ev
+T __ZNSt3__115__thread_structC2Ev
+T __ZNSt3__115__thread_structD1Ev
+T __ZNSt3__115__thread_structD2Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE10pubseekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE10pubseekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4setgEPcS4_S4_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4setpEPcS4_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4swapERS3_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4syncEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5gbumpEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5pbumpEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sgetcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sgetnEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputcEc
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputnEPKcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5uflowEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6sbumpcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6setbufEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6snextcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsgetnEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsputnEPKcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7pubsyncEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7sungetcEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8in_availEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8overflowEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8pubimbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9pbackfailEi
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9pubsetbufEPcl
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9showmanycEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9sputbackcEc
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9underflowEv
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC1ERKS3_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC1Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2ERKS3_
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEaSERKS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE10pubseekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE10pubseekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4setgEPwS4_S4_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4setpEPwS4_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4swapERS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4syncEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5gbumpEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5imbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5pbumpEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sgetcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sgetnEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sputcEw
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sputnEPKwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5uflowEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6sbumpcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6setbufEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6snextcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6xsgetnEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6xsputnEPKwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7pubsyncEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7seekposENS_4fposI11__mbstate_tEEj
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7sungetcEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8in_availEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8overflowEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8pubimbueERKNS_6localeE
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9pbackfailEi
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9pubsetbufEPwl
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9showmanycEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9sputbackcEw
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9underflowEv
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC1ERKS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC1Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC2ERKS3_
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC2Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED2Ev
+T __ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEaSERKS3_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strERKNS_12basic_stringIcS2_S4_EE
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE4swapERS5_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE7seekoffExNS_8ios_base7seekdirEj
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE8overflowEi
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9pbackfailEi
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9underflowEv
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEC1EOS5_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEC2EOS5_
+T __ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__115future_categoryEv
+T __ZNSt3__115numpunct_bynameIcEC1EPKcm
+T __ZNSt3__115numpunct_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIcEC2EPKcm
+T __ZNSt3__115numpunct_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIcED0Ev
+T __ZNSt3__115numpunct_bynameIcED1Ev
+T __ZNSt3__115numpunct_bynameIcED2Ev
+T __ZNSt3__115numpunct_bynameIwEC1EPKcm
+T __ZNSt3__115numpunct_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIwEC2EPKcm
+T __ZNSt3__115numpunct_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T __ZNSt3__115numpunct_bynameIwED0Ev
+T __ZNSt3__115numpunct_bynameIwED1Ev
+T __ZNSt3__115numpunct_bynameIwED2Ev
+T __ZNSt3__115recursive_mutex4lockEv
+T __ZNSt3__115recursive_mutex6unlockEv
+T __ZNSt3__115recursive_mutex8try_lockEv
+T __ZNSt3__115recursive_mutexC1Ev
+T __ZNSt3__115recursive_mutexC2Ev
+T __ZNSt3__115recursive_mutexD1Ev
+T __ZNSt3__115recursive_mutexD2Ev
+T __ZNSt3__115system_categoryEv
+T __ZNSt3__116__check_groupingERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjS8_Rj
+T __ZNSt3__116__narrow_to_utf8ILm16EED0Ev
+T __ZNSt3__116__narrow_to_utf8ILm16EED1Ev
+T __ZNSt3__116__narrow_to_utf8ILm16EED2Ev
+T __ZNSt3__116__narrow_to_utf8ILm32EED0Ev
+T __ZNSt3__116__narrow_to_utf8ILm32EED1Ev
+T __ZNSt3__116__narrow_to_utf8ILm32EED2Ev
+T __ZNSt3__116generic_categoryEv
+T __ZNSt3__117__assoc_sub_state10__sub_waitERNS_11unique_lockINS_5mutexEEE
+T __ZNSt3__117__assoc_sub_state12__make_readyEv
+T __ZNSt3__117__assoc_sub_state13set_exceptionESt13exception_ptr
+T __ZNSt3__117__assoc_sub_state16__on_zero_sharedEv
+T __ZNSt3__117__assoc_sub_state24set_value_at_thread_exitEv
+T __ZNSt3__117__assoc_sub_state28set_exception_at_thread_exitESt13exception_ptr
+T __ZNSt3__117__assoc_sub_state4copyEv
+T __ZNSt3__117__assoc_sub_state4waitEv
+T __ZNSt3__117__assoc_sub_state9__executeEv
+T __ZNSt3__117__assoc_sub_state9set_valueEv
+T __ZNSt3__117__widen_from_utf8ILm16EED0Ev
+T __ZNSt3__117__widen_from_utf8ILm16EED1Ev
+T __ZNSt3__117__widen_from_utf8ILm16EED2Ev
+T __ZNSt3__117__widen_from_utf8ILm32EED0Ev
+T __ZNSt3__117__widen_from_utf8ILm32EED1Ev
+T __ZNSt3__117__widen_from_utf8ILm32EED2Ev
+T __ZNSt3__117bad_function_callD0Ev
+T __ZNSt3__117bad_function_callD1Ev
+T __ZNSt3__117bad_function_callD2Ev
+T __ZNSt3__117iostream_categoryEv
+T __ZNSt3__117moneypunct_bynameIcLb0EE4initEPKc
+T __ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc
+T __ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc
+T __ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc
+T __ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE
+T __ZNSt3__118__time_get_storageIcEC1EPKc
+T __ZNSt3__118__time_get_storageIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118__time_get_storageIcEC2EPKc
+T __ZNSt3__118__time_get_storageIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118__time_get_storageIwEC1EPKc
+T __ZNSt3__118__time_get_storageIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118__time_get_storageIwEC2EPKc
+T __ZNSt3__118__time_get_storageIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__118condition_variable10notify_allEv
+T __ZNSt3__118condition_variable10notify_oneEv
+T __ZNSt3__118condition_variable15__do_timed_waitERNS_11unique_lockINS_5mutexEEENS_6chrono10time_pointINS5_12system_clockENS5_8durationIxNS_5ratioILl1ELl1000000000EEEEEEE
+T __ZNSt3__118condition_variable4waitERNS_11unique_lockINS_5mutexEEE
+T __ZNSt3__118condition_variableD1Ev
+T __ZNSt3__118condition_variableD2Ev
+T __ZNSt3__118shared_timed_mutex11lock_sharedEv
+T __ZNSt3__118shared_timed_mutex13unlock_sharedEv
+T __ZNSt3__118shared_timed_mutex15try_lock_sharedEv
+T __ZNSt3__118shared_timed_mutex4lockEv
+T __ZNSt3__118shared_timed_mutex6unlockEv
+T __ZNSt3__118shared_timed_mutex8try_lockEv
+T __ZNSt3__118shared_timed_mutexC1Ev
+T __ZNSt3__118shared_timed_mutexC2Ev
+T __ZNSt3__119__is_posix_terminalEP7__sFILE
+T __ZNSt3__119__shared_mutex_base11lock_sharedEv
+T __ZNSt3__119__shared_mutex_base13unlock_sharedEv
+T __ZNSt3__119__shared_mutex_base15try_lock_sharedEv
+T __ZNSt3__119__shared_mutex_base4lockEv
+T __ZNSt3__119__shared_mutex_base6unlockEv
+T __ZNSt3__119__shared_mutex_base8try_lockEv
+T __ZNSt3__119__shared_mutex_baseC1Ev
+T __ZNSt3__119__shared_mutex_baseC2Ev
+T __ZNSt3__119__shared_weak_count10__add_weakEv
+T __ZNSt3__119__shared_weak_count12__add_sharedEv
+T __ZNSt3__119__shared_weak_count14__release_weakEv
+T __ZNSt3__119__shared_weak_count16__release_sharedEv
+T __ZNSt3__119__shared_weak_count4lockEv
+T __ZNSt3__119__shared_weak_countD0Ev
+T __ZNSt3__119__shared_weak_countD1Ev
+T __ZNSt3__119__shared_weak_countD2Ev
+T __ZNSt3__119__thread_local_dataEv
+T __ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+T __ZNSt3__120__atomic_wait_nativeILm8EEEvPKvS2_
+T __ZNSt3__120__get_collation_nameEPKc
+T __ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEEx
+T __ZNSt3__120__libcpp_atomic_waitEPVKvx
+T __ZNSt3__120__throw_system_errorEiPKc
+T __ZNSt3__121__get_locale_encodingEPKc
+T __ZNSt3__121__throw_runtime_errorEPKc
+T __ZNSt3__121recursive_timed_mutex4lockEv
+T __ZNSt3__121recursive_timed_mutex6unlockEv
+T __ZNSt3__121recursive_timed_mutex8try_lockEv
+T __ZNSt3__121recursive_timed_mutexC1Ev
+T __ZNSt3__121recursive_timed_mutexC2Ev
+T __ZNSt3__121recursive_timed_mutexD1Ev
+T __ZNSt3__121recursive_timed_mutexD2Ev
+T __ZNSt3__122__libcpp_verbose_abortEPKcz
+T __ZNSt3__123__atomic_monitor_globalEPKv
+T __ZNSt3__123__cxx_atomic_notify_allEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE
+T __ZNSt3__123__cxx_atomic_notify_allEPVKv
+T __ZNSt3__123__cxx_atomic_notify_oneEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE
+T __ZNSt3__123__cxx_atomic_notify_oneEPVKv
+T __ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE
+T __ZNSt3__123__libcpp_atomic_monitorEPVKv
+T __ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE
+T __ZNSt3__126__atomic_notify_all_nativeILm8EEEvPKv
+T __ZNSt3__126__atomic_notify_one_nativeILm8EEEvPKv
+T __ZNSt3__126__atomic_wait_global_tableEPKvx
+T __ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+T __ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+T __ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh
+T __ZNSt3__132__atomic_notify_all_global_tableEPKv
+T __ZNSt3__132__atomic_notify_one_global_tableEPKv
+T __ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE
+T __ZNSt3__133__atomic_wait_native_with_timeoutILm8EEEvPKvS2_y
+T __ZNSt3__134__construct_barrier_algorithm_baseERl
+T __ZNSt3__139__atomic_wait_global_table_with_timeoutEPKvxy
+S __ZNSt3__13cinE
+T __ZNSt3__13pmr15memory_resourceD0Ev
+T __ZNSt3__13pmr15memory_resourceD1Ev
+T __ZNSt3__13pmr15memory_resourceD2Ev
+T __ZNSt3__13pmr19new_delete_resourceEv
+T __ZNSt3__13pmr20get_default_resourceEv
+T __ZNSt3__13pmr20null_memory_resourceEv
+T __ZNSt3__13pmr20set_default_resourceEPNS0_15memory_resourceE
+T __ZNSt3__13pmr25monotonic_buffer_resource11do_allocateEmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource11do_allocateEmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__do_allocateEPNS0_15memory_resourceEmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__release_ptrEPNS0_15memory_resourceE
+T __ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool15__do_deallocateEPNS0_15memory_resourceEPvmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource13do_deallocateEPvmm
+T __ZNSt3__13pmr28unsynchronized_pool_resource7releaseEv
+T __ZNSt3__13pmr28unsynchronized_pool_resourceC1ERKNS0_12pool_optionsEPNS0_15memory_resourceE
+T __ZNSt3__13pmr28unsynchronized_pool_resourceC2ERKNS0_12pool_optionsEPNS0_15memory_resourceE
+T __ZNSt3__14__fs10filesystem10__absoluteERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem10hash_valueERKNS1_4pathE
+T __ZNSt3__14__fs10filesystem11__canonicalERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem11__copy_fileERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem11__file_sizeERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem12__equivalentERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem12__remove_allERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem13__fs_is_emptyERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem13__permissionsERKNS1_4pathENS1_5permsENS1_12perm_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem13__resize_fileERKNS1_4pathEmPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__copy_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__current_pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__current_pathERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem14__read_symlinkERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem15directory_entry12__do_refreshEv
+T __ZNSt3__14__fs10filesystem16_FilesystemClock3nowEv
+S __ZNSt3__14__fs10filesystem16_FilesystemClock9is_steadyE
+T __ZNSt3__14__fs10filesystem16__create_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem16__symlink_statusERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem16filesystem_error13__create_whatEi
+T __ZNSt3__14__fs10filesystem16filesystem_errorD0Ev
+T __ZNSt3__14__fs10filesystem16filesystem_errorD1Ev
+T __ZNSt3__14__fs10filesystem16filesystem_errorD2Ev
+T __ZNSt3__14__fs10filesystem17__hard_link_countERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem17__last_write_timeERKNS1_4pathENS_6chrono10time_pointINS1_16_FilesystemClockENS5_8durationInNS_5ratioILl1ELl1000000000EEEEEEEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem17__last_write_timeERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__create_directoryERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__create_directoryERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__create_hard_linkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18__weakly_canonicalERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18directory_iterator11__incrementEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem18directory_iteratorC1ERKNS1_4pathEPNS_10error_codeENS1_17directory_optionsE
+T __ZNSt3__14__fs10filesystem18directory_iteratorC2ERKNS1_4pathEPNS_10error_codeENS1_17directory_optionsE
+T __ZNSt3__14__fs10filesystem20__create_directoriesERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem21__temp_directory_pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem26__create_directory_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator11__incrementEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator15__try_recursionEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator5__popEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iterator9__advanceEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iteratorC1ERKNS1_4pathENS1_17directory_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem28recursive_directory_iteratorC2ERKNS1_4pathENS1_17directory_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem4path17replace_extensionERKS2_
+S __ZNSt3__14__fs10filesystem4path19preferred_separatorE
+T __ZNSt3__14__fs10filesystem4path8iterator11__decrementEv
+T __ZNSt3__14__fs10filesystem4path8iterator11__incrementEv
+T __ZNSt3__14__fs10filesystem6__copyERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem7__spaceERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE
+T __ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE
+T __ZNSt3__14__fs10filesystem8__statusERKNS1_4pathEPNS_10error_codeE
+S __ZNSt3__14cerrE
+S __ZNSt3__14clogE
+S __ZNSt3__14coutE
+T __ZNSt3__14stodERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T __ZNSt3__14stodERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T __ZNSt3__14stofERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T __ZNSt3__14stofERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T __ZNSt3__14stoiERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__14stoiERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T __ZNSt3__14stolERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__14stolERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+S __ZNSt3__14wcinE
+T __ZNSt3__15alignEmmRPvRm
+S __ZNSt3__15ctypeIcE10table_sizeE
+T __ZNSt3__15ctypeIcE13classic_tableEv
+S __ZNSt3__15ctypeIcE2idE
+T __ZNSt3__15ctypeIcEC1EPKjbm
+T __ZNSt3__15ctypeIcEC2EPKjbm
+T __ZNSt3__15ctypeIcED0Ev
+T __ZNSt3__15ctypeIcED1Ev
+T __ZNSt3__15ctypeIcED2Ev
+S __ZNSt3__15ctypeIwE2idE
+T __ZNSt3__15ctypeIwED0Ev
+T __ZNSt3__15ctypeIwED1Ev
+T __ZNSt3__15ctypeIwED2Ev
+T __ZNSt3__15mutex4lockEv
+T __ZNSt3__15mutex6unlockEv
+T __ZNSt3__15mutex8try_lockEv
+T __ZNSt3__15mutexD1Ev
+T __ZNSt3__15mutexD2Ev
+T __ZNSt3__15stoldERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T __ZNSt3__15stoldERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T __ZNSt3__15stollERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__15stollERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T __ZNSt3__15stoulERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__15stoulERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+S __ZNSt3__15wcerrE
+S __ZNSt3__15wclogE
+S __ZNSt3__15wcoutE
+T __ZNSt3__16__itoa8__u32toaEjPc
+T __ZNSt3__16__itoa8__u64toaEyPc
+T __ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIeeEEPeEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIffEEPfEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIhhEEPhEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIiiEEPiEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIjjEEPjEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIllEEPlEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessImmEEPmEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIssEEPsEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIttEEPtEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIwwEEPwEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIxxEEPxEEvT0_S5_T_
+T __ZNSt3__16__sortIRNS_6__lessIyyEEPyEEvT0_S5_T_
+T __ZNSt3__16chrono12steady_clock3nowEv
+S __ZNSt3__16chrono12steady_clock9is_steadyE
+T __ZNSt3__16chrono12system_clock11from_time_tEl
+T __ZNSt3__16chrono12system_clock3nowEv
+S __ZNSt3__16chrono12system_clock9is_steadyE
+T __ZNSt3__16chrono12system_clock9to_time_tERKNS0_10time_pointIS1_NS0_8durationIxNS_5ratioILl1ELl1000000EEEEEEE
+T __ZNSt3__16futureIvE3getEv
+T __ZNSt3__16futureIvEC1EPNS_17__assoc_sub_stateE
+T __ZNSt3__16futureIvEC2EPNS_17__assoc_sub_stateE
+T __ZNSt3__16futureIvED1Ev
+T __ZNSt3__16futureIvED2Ev
+T __ZNSt3__16gslice6__initEm
+T __ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl
+T __ZNSt3__16locale2id5__getEv
+S __ZNSt3__16locale2id9__next_idE
+S __ZNSt3__16locale3allE
+S __ZNSt3__16locale4noneE
+S __ZNSt3__16locale4timeE
+S __ZNSt3__16locale5ctypeE
+T __ZNSt3__16locale5facet16__on_zero_sharedEv
+T __ZNSt3__16locale5facetD0Ev
+T __ZNSt3__16locale5facetD1Ev
+T __ZNSt3__16locale5facetD2Ev
+T __ZNSt3__16locale6globalERKS0_
+T __ZNSt3__16locale7classicEv
+S __ZNSt3__16locale7collateE
+S __ZNSt3__16locale7numericE
+T __ZNSt3__16locale8__globalEv
+S __ZNSt3__16locale8messagesE
+S __ZNSt3__16locale8monetaryE
+T __ZNSt3__16localeC1EPKc
+T __ZNSt3__16localeC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__16localeC1ERKS0_
+T __ZNSt3__16localeC1ERKS0_PKci
+T __ZNSt3__16localeC1ERKS0_RKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEi
+T __ZNSt3__16localeC1ERKS0_S2_i
+T __ZNSt3__16localeC1Ev
+T __ZNSt3__16localeC2EPKc
+T __ZNSt3__16localeC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T __ZNSt3__16localeC2ERKS0_
+T __ZNSt3__16localeC2ERKS0_PKci
+T __ZNSt3__16localeC2ERKS0_RKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEi
+T __ZNSt3__16localeC2ERKS0_S2_i
+T __ZNSt3__16localeC2Ev
+T __ZNSt3__16localeD1Ev
+T __ZNSt3__16localeD2Ev
+T __ZNSt3__16localeaSERKS0_
+T __ZNSt3__16stoullERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T __ZNSt3__16stoullERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T __ZNSt3__16thread20hardware_concurrencyEv
+T __ZNSt3__16thread4joinEv
+T __ZNSt3__16thread6detachEv
+T __ZNSt3__16threadD1Ev
+T __ZNSt3__16threadD2Ev
+S __ZNSt3__17codecvtIDiDu11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDiDu11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDiDu11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDiDu11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIDic11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDic11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDic11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDic11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIDsDu11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDsDu11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDsDu11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDsDu11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIDsc11__mbstate_tE2idE
+T __ZNSt3__17codecvtIDsc11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIDsc11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIDsc11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIcc11__mbstate_tE2idE
+T __ZNSt3__17codecvtIcc11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIcc11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIcc11__mbstate_tED2Ev
+S __ZNSt3__17codecvtIwc11__mbstate_tE2idE
+T __ZNSt3__17codecvtIwc11__mbstate_tEC1EPKcm
+T __ZNSt3__17codecvtIwc11__mbstate_tEC1Em
+T __ZNSt3__17codecvtIwc11__mbstate_tEC2EPKcm
+T __ZNSt3__17codecvtIwc11__mbstate_tEC2Em
+T __ZNSt3__17codecvtIwc11__mbstate_tED0Ev
+T __ZNSt3__17codecvtIwc11__mbstate_tED1Ev
+T __ZNSt3__17codecvtIwc11__mbstate_tED2Ev
+D __ZNSt3__17collateIcE2idE
+T __ZNSt3__17collateIcED0Ev
+T __ZNSt3__17collateIcED1Ev
+T __ZNSt3__17collateIcED2Ev
+D __ZNSt3__17collateIwE2idE
+T __ZNSt3__17collateIwED0Ev
+T __ZNSt3__17collateIwED1Ev
+T __ZNSt3__17collateIwED2Ev
+D __ZNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+D __ZNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__17promiseIvE10get_futureEv
+T __ZNSt3__17promiseIvE13set_exceptionESt13exception_ptr
+T __ZNSt3__17promiseIvE24set_value_at_thread_exitEv
+T __ZNSt3__17promiseIvE28set_exception_at_thread_exitESt13exception_ptr
+T __ZNSt3__17promiseIvE9set_valueEv
+T __ZNSt3__17promiseIvEC1Ev
+T __ZNSt3__17promiseIvEC2Ev
+T __ZNSt3__17promiseIvED1Ev
+T __ZNSt3__17promiseIvED2Ev
+T __ZNSt3__18__rs_getEv
+T __ZNSt3__18__sp_mut4lockEv
+T __ZNSt3__18__sp_mut6unlockEv
+S __ZNSt3__18ios_base10floatfieldE
+S __ZNSt3__18ios_base10scientificE
+S __ZNSt3__18ios_base11adjustfieldE
+T __ZNSt3__18ios_base15sync_with_stdioEb
+T __ZNSt3__18ios_base16__call_callbacksENS0_5eventE
+T __ZNSt3__18ios_base17register_callbackEPFvNS0_5eventERS0_iEi
+S __ZNSt3__18ios_base2inE
+T __ZNSt3__18ios_base33__set_badbit_and_consider_rethrowEv
+T __ZNSt3__18ios_base34__set_failbit_and_consider_rethrowEv
+S __ZNSt3__18ios_base3appE
+S __ZNSt3__18ios_base3ateE
+S __ZNSt3__18ios_base3decE
+S __ZNSt3__18ios_base3hexE
+S __ZNSt3__18ios_base3octE
+S __ZNSt3__18ios_base3outE
+T __ZNSt3__18ios_base4InitC1Ev
+T __ZNSt3__18ios_base4InitC2Ev
+T __ZNSt3__18ios_base4InitD1Ev
+T __ZNSt3__18ios_base4InitD2Ev
+T __ZNSt3__18ios_base4initEPv
+S __ZNSt3__18ios_base4leftE
+T __ZNSt3__18ios_base4moveERS0_
+T __ZNSt3__18ios_base4swapERS0_
+T __ZNSt3__18ios_base5clearEj
+S __ZNSt3__18ios_base5fixedE
+T __ZNSt3__18ios_base5imbueERKNS_6localeE
+T __ZNSt3__18ios_base5iwordEi
+T __ZNSt3__18ios_base5pwordEi
+S __ZNSt3__18ios_base5rightE
+S __ZNSt3__18ios_base5truncE
+S __ZNSt3__18ios_base6badbitE
+S __ZNSt3__18ios_base6binaryE
+S __ZNSt3__18ios_base6eofbitE
+S __ZNSt3__18ios_base6skipwsE
+T __ZNSt3__18ios_base6xallocEv
+T __ZNSt3__18ios_base7copyfmtERKS0_
+S __ZNSt3__18ios_base7failbitE
+T __ZNSt3__18ios_base7failureC1EPKcRKNS_10error_codeE
+T __ZNSt3__18ios_base7failureC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_10error_codeE
+T __ZNSt3__18ios_base7failureC2EPKcRKNS_10error_codeE
+T __ZNSt3__18ios_base7failureC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_10error_codeE
+T __ZNSt3__18ios_base7failureD0Ev
+T __ZNSt3__18ios_base7failureD1Ev
+T __ZNSt3__18ios_base7failureD2Ev
+S __ZNSt3__18ios_base7goodbitE
+S __ZNSt3__18ios_base7showposE
+S __ZNSt3__18ios_base7unitbufE
+S __ZNSt3__18ios_base8internalE
+S __ZNSt3__18ios_base8showbaseE
+S __ZNSt3__18ios_base9basefieldE
+S __ZNSt3__18ios_base9boolalphaE
+S __ZNSt3__18ios_base9showpointE
+S __ZNSt3__18ios_base9uppercaseE
+T __ZNSt3__18ios_baseD0Ev
+T __ZNSt3__18ios_baseD1Ev
+T __ZNSt3__18ios_baseD2Ev
+D __ZNSt3__18messagesIcE2idE
+D __ZNSt3__18messagesIwE2idE
+S __ZNSt3__18numpunctIcE2idE
+T __ZNSt3__18numpunctIcEC1Em
+T __ZNSt3__18numpunctIcEC2Em
+T __ZNSt3__18numpunctIcED0Ev
+T __ZNSt3__18numpunctIcED1Ev
+T __ZNSt3__18numpunctIcED2Ev
+S __ZNSt3__18numpunctIwE2idE
+T __ZNSt3__18numpunctIwEC1Em
+T __ZNSt3__18numpunctIwEC2Em
+T __ZNSt3__18numpunctIwED0Ev
+T __ZNSt3__18numpunctIwED1Ev
+T __ZNSt3__18numpunctIwED2Ev
+D __ZNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+D __ZNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__18to_charsEPcS0_d
+T __ZNSt3__18to_charsEPcS0_dNS_12chars_formatE
+T __ZNSt3__18to_charsEPcS0_dNS_12chars_formatEi
+T __ZNSt3__18to_charsEPcS0_e
+T __ZNSt3__18to_charsEPcS0_eNS_12chars_formatE
+T __ZNSt3__18to_charsEPcS0_eNS_12chars_formatEi
+T __ZNSt3__18to_charsEPcS0_f
+T __ZNSt3__18to_charsEPcS0_fNS_12chars_formatE
+T __ZNSt3__18to_charsEPcS0_fNS_12chars_formatEi
+T __ZNSt3__18valarrayImE6resizeEmm
+T __ZNSt3__18valarrayImEC1Em
+T __ZNSt3__18valarrayImEC2Em
+T __ZNSt3__18valarrayImED1Ev
+T __ZNSt3__18valarrayImED2Ev
+T __ZNSt3__19__num_getIcE17__stage2_int_loopEciPcRS2_RjcRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_S2_
+T __ZNSt3__19__num_getIcE17__stage2_int_prepERNS_8ios_baseEPcRc
+T __ZNSt3__19__num_getIcE19__stage2_float_loopEcRbRcPcRS4_ccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjS4_
+T __ZNSt3__19__num_getIcE19__stage2_float_prepERNS_8ios_baseEPcRcS5_
+T __ZNSt3__19__num_getIwE17__stage2_int_loopEwiPcRS2_RjwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_Pw
+T __ZNSt3__19__num_getIwE17__stage2_int_prepERNS_8ios_baseEPwRw
+T __ZNSt3__19__num_getIwE19__stage2_float_loopEwRbRcPcRS4_wwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjPw
+T __ZNSt3__19__num_getIwE19__stage2_float_prepERNS_8ios_baseEPwRwS5_
+T __ZNSt3__19__num_putIcE21__widen_and_group_intEPcS2_S2_S2_RS2_S3_RKNS_6localeE
+T __ZNSt3__19__num_putIcE23__widen_and_group_floatEPcS2_S2_S2_RS2_S3_RKNS_6localeE
+T __ZNSt3__19__num_putIwE21__widen_and_group_intEPcS2_S2_PwRS3_S4_RKNS_6localeE
+T __ZNSt3__19__num_putIwE23__widen_and_group_floatEPcS2_S2_PwRS3_S4_RKNS_6localeE
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEE7copyfmtERKS3_
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEED0Ev
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEED1Ev
+T __ZNSt3__19basic_iosIcNS_11char_traitsIcEEED2Ev
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEE7copyfmtERKS3_
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEED0Ev
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEED1Ev
+T __ZNSt3__19basic_iosIwNS_11char_traitsIwEEED2Ev
+D __ZNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+T __ZNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE8__do_getERS4_S4_bRKNS_6localeEjRjRbRKNS_5ctypeIcEERNS_10unique_ptrIcPFvPvEEERPcSM_
+D __ZNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE8__do_getERS4_S4_bRKNS_6localeEjRjRbRKNS_5ctypeIwEERNS_10unique_ptrIwPFvPvEEERPwSM_
+D __ZNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+D __ZNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+T __ZNSt3__19strstreamD0Ev
+T __ZNSt3__19strstreamD1Ev
+T __ZNSt3__19strstreamD2Ev
+T __ZNSt3__19to_stringEd
+T __ZNSt3__19to_stringEe
+T __ZNSt3__19to_stringEf
+T __ZNSt3__19to_stringEi
+T __ZNSt3__19to_stringEj
+T __ZNSt3__19to_stringEl
+T __ZNSt3__19to_stringEm
+T __ZNSt3__19to_stringEx
+T __ZNSt3__19to_stringEy
+T __ZNSt3__1plIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_12basic_stringIT_T0_T1_EEPKS6_RKS9_
+I __ZNSt8bad_castC1Ev (indirect for __ZNSt8bad_castC1Ev)
+I __ZNSt8bad_castC2Ev (indirect for __ZNSt8bad_castC2Ev)
+I __ZNSt8bad_castD0Ev (indirect for __ZNSt8bad_castD0Ev)
+I __ZNSt8bad_castD1Ev (indirect for __ZNSt8bad_castD1Ev)
+I __ZNSt8bad_castD2Ev (indirect for __ZNSt8bad_castD2Ev)
+I __ZNSt9bad_allocC1Ev (indirect for __ZNSt9bad_allocC1Ev)
+I __ZNSt9bad_allocC2Ev (indirect for __ZNSt9bad_allocC2Ev)
+I __ZNSt9bad_allocD0Ev (indirect for __ZNSt9bad_allocD0Ev)
+I __ZNSt9bad_allocD1Ev (indirect for __ZNSt9bad_allocD1Ev)
+I __ZNSt9bad_allocD2Ev (indirect for __ZNSt9bad_allocD2Ev)
+I __ZNSt9exceptionD0Ev (indirect for __ZNSt9exceptionD0Ev)
+I __ZNSt9exceptionD1Ev (indirect for __ZNSt9exceptionD1Ev)
+I __ZNSt9exceptionD2Ev (indirect for __ZNSt9exceptionD2Ev)
+I __ZNSt9type_infoD0Ev (indirect for __ZNSt9type_infoD0Ev)
+I __ZNSt9type_infoD1Ev (indirect for __ZNSt9type_infoD1Ev)
+I __ZNSt9type_infoD2Ev (indirect for __ZNSt9type_infoD2Ev)
+I __ZSt10unexpectedv (indirect for __ZSt10unexpectedv)
+I __ZSt13get_terminatev (indirect for __ZSt13get_terminatev)
+I __ZSt13set_terminatePFvvE (indirect for __ZSt13set_terminatePFvvE)
+I __ZSt14get_unexpectedv (indirect for __ZSt14get_unexpectedv)
+I __ZSt14set_unexpectedPFvvE (indirect for __ZSt14set_unexpectedPFvvE)
+I __ZSt15get_new_handlerv (indirect for __ZSt15get_new_handlerv)
+I __ZSt15set_new_handlerPFvvE (indirect for __ZSt15set_new_handlerPFvvE)
+T __ZSt17__throw_bad_allocv
+T __ZSt17current_exceptionv
+T __ZSt17rethrow_exceptionSt13exception_ptr
+T __ZSt18uncaught_exceptionv
+T __ZSt19uncaught_exceptionsv
+S __ZSt7nothrow
+I __ZSt9terminatev (indirect for __ZSt9terminatev)
+S __ZTCNSt3__110istrstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__110ostrstreamE0_NS_13basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE16_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE0_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_14basic_iostreamIcS2_EE
+S __ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE16_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE
+S __ZTCNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_ostreamIcS2_EE
+S __ZTCNSt3__19strstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__19strstreamE0_NS_14basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTCNSt3__19strstreamE16_NS_13basic_ostreamIcNS_11char_traitsIcEEEE
+I __ZTIDh (indirect for __ZTIDh)
+I __ZTIDi (indirect for __ZTIDi)
+I __ZTIDn (indirect for __ZTIDn)
+I __ZTIDs (indirect for __ZTIDs)
+I __ZTIDu (indirect for __ZTIDu)
+I __ZTIN10__cxxabiv116__enum_type_infoE (indirect for __ZTIN10__cxxabiv116__enum_type_infoE)
+I __ZTIN10__cxxabiv116__shim_type_infoE (indirect for __ZTIN10__cxxabiv116__shim_type_infoE)
+I __ZTIN10__cxxabiv117__array_type_infoE (indirect for __ZTIN10__cxxabiv117__array_type_infoE)
+I __ZTIN10__cxxabiv117__pbase_type_infoE (indirect for __ZTIN10__cxxabiv117__pbase_type_infoE)
+I __ZTIN10__cxxabiv119__pointer_type_infoE (indirect for __ZTIN10__cxxabiv119__pointer_type_infoE)
+I __ZTIN10__cxxabiv120__function_type_infoE (indirect for __ZTIN10__cxxabiv120__function_type_infoE)
+I __ZTIN10__cxxabiv123__fundamental_type_infoE (indirect for __ZTIN10__cxxabiv123__fundamental_type_infoE)
+I __ZTIN10__cxxabiv129__pointer_to_member_type_infoE (indirect for __ZTIN10__cxxabiv129__pointer_to_member_type_infoE)
+S __ZTINSt12experimental15fundamentals_v112bad_any_castE
+S __ZTINSt12experimental19bad_optional_accessE
+S __ZTINSt3__110istrstreamE
+S __ZTINSt3__110moneypunctIcLb0EEE
+S __ZTINSt3__110moneypunctIcLb1EEE
+S __ZTINSt3__110moneypunctIwLb0EEE
+S __ZTINSt3__110moneypunctIwLb1EEE
+S __ZTINSt3__110ostrstreamE
+S __ZTINSt3__111regex_errorE
+S __ZTINSt3__112bad_weak_ptrE
+S __ZTINSt3__112ctype_bynameIcEE
+S __ZTINSt3__112ctype_bynameIwEE
+S __ZTINSt3__112future_errorE
+S __ZTINSt3__112strstreambufE
+S __ZTINSt3__112system_errorE
+S __ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+S __ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTINSt3__114__codecvt_utf8IDiEE
+S __ZTINSt3__114__codecvt_utf8IDsEE
+S __ZTINSt3__114__codecvt_utf8IwEE
+S __ZTINSt3__114__shared_countE
+S __ZTINSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTINSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIDic11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIDsc11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIcc11__mbstate_tEE
+S __ZTINSt3__114codecvt_bynameIwc11__mbstate_tEE
+S __ZTINSt3__114collate_bynameIcEE
+S __ZTINSt3__114collate_bynameIwEE
+S __ZTINSt3__114error_categoryE
+S __ZTINSt3__115__codecvt_utf16IDiLb0EEE
+S __ZTINSt3__115__codecvt_utf16IDiLb1EEE
+S __ZTINSt3__115__codecvt_utf16IDsLb0EEE
+S __ZTINSt3__115__codecvt_utf16IDsLb1EEE
+S __ZTINSt3__115__codecvt_utf16IwLb0EEE
+S __ZTINSt3__115__codecvt_utf16IwLb1EEE
+S __ZTINSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+S __ZTINSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+S __ZTINSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__115messages_bynameIcEE
+S __ZTINSt3__115messages_bynameIwEE
+S __ZTINSt3__115numpunct_bynameIcEE
+S __ZTINSt3__115numpunct_bynameIwEE
+S __ZTINSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__116__narrow_to_utf8ILm16EEE
+S __ZTINSt3__116__narrow_to_utf8ILm32EEE
+S __ZTINSt3__117__assoc_sub_stateE
+S __ZTINSt3__117__widen_from_utf8ILm16EEE
+S __ZTINSt3__117__widen_from_utf8ILm32EEE
+S __ZTINSt3__117bad_function_callE
+S __ZTINSt3__117moneypunct_bynameIcLb0EEE
+S __ZTINSt3__117moneypunct_bynameIcLb1EEE
+S __ZTINSt3__117moneypunct_bynameIwLb0EEE
+S __ZTINSt3__117moneypunct_bynameIwLb1EEE
+S __ZTINSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__119__shared_weak_countE
+S __ZTINSt3__119bad_expected_accessIvEE
+S __ZTINSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTINSt3__120__codecvt_utf8_utf16IDiEE
+S __ZTINSt3__120__codecvt_utf8_utf16IDsEE
+S __ZTINSt3__120__codecvt_utf8_utf16IwEE
+S __ZTINSt3__13pmr15memory_resourceE
+S __ZTINSt3__13pmr25monotonic_buffer_resourceE
+S __ZTINSt3__13pmr26synchronized_pool_resourceE
+S __ZTINSt3__13pmr28unsynchronized_pool_resourceE
+S __ZTINSt3__14__fs10filesystem16filesystem_errorE
+S __ZTINSt3__15ctypeIcEE
+S __ZTINSt3__15ctypeIwEE
+S __ZTINSt3__16locale5facetE
+S __ZTINSt3__17codecvtIDiDu11__mbstate_tEE
+S __ZTINSt3__17codecvtIDic11__mbstate_tEE
+S __ZTINSt3__17codecvtIDsDu11__mbstate_tEE
+S __ZTINSt3__17codecvtIDsc11__mbstate_tEE
+S __ZTINSt3__17codecvtIcc11__mbstate_tEE
+S __ZTINSt3__17codecvtIwc11__mbstate_tEE
+S __ZTINSt3__17collateIcEE
+S __ZTINSt3__17collateIwEE
+S __ZTINSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__18ios_base7failureE
+S __ZTINSt3__18ios_baseE
+S __ZTINSt3__18messagesIcEE
+S __ZTINSt3__18messagesIwEE
+S __ZTINSt3__18numpunctIcEE
+S __ZTINSt3__18numpunctIwEE
+S __ZTINSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__19basic_iosIcNS_11char_traitsIcEEEE
+S __ZTINSt3__19basic_iosIwNS_11char_traitsIwEEEE
+S __ZTINSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTINSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTINSt3__19strstreamE
+I __ZTIPDh (indirect for __ZTIPDh)
+I __ZTIPDi (indirect for __ZTIPDi)
+I __ZTIPDn (indirect for __ZTIPDn)
+I __ZTIPDs (indirect for __ZTIPDs)
+I __ZTIPDu (indirect for __ZTIPDu)
+I __ZTIPKDh (indirect for __ZTIPKDh)
+I __ZTIPKDi (indirect for __ZTIPKDi)
+I __ZTIPKDn (indirect for __ZTIPKDn)
+I __ZTIPKDs (indirect for __ZTIPKDs)
+I __ZTIPKDu (indirect for __ZTIPKDu)
+I __ZTIPKa (indirect for __ZTIPKa)
+I __ZTIPKb (indirect for __ZTIPKb)
+I __ZTIPKc (indirect for __ZTIPKc)
+I __ZTIPKd (indirect for __ZTIPKd)
+I __ZTIPKe (indirect for __ZTIPKe)
+I __ZTIPKf (indirect for __ZTIPKf)
+I __ZTIPKg (indirect for __ZTIPKg)
+I __ZTIPKh (indirect for __ZTIPKh)
+I __ZTIPKi (indirect for __ZTIPKi)
+I __ZTIPKj (indirect for __ZTIPKj)
+I __ZTIPKl (indirect for __ZTIPKl)
+I __ZTIPKm (indirect for __ZTIPKm)
+I __ZTIPKn (indirect for __ZTIPKn)
+I __ZTIPKo (indirect for __ZTIPKo)
+I __ZTIPKs (indirect for __ZTIPKs)
+I __ZTIPKt (indirect for __ZTIPKt)
+I __ZTIPKv (indirect for __ZTIPKv)
+I __ZTIPKw (indirect for __ZTIPKw)
+I __ZTIPKx (indirect for __ZTIPKx)
+I __ZTIPKy (indirect for __ZTIPKy)
+I __ZTIPa (indirect for __ZTIPa)
+I __ZTIPb (indirect for __ZTIPb)
+I __ZTIPc (indirect for __ZTIPc)
+I __ZTIPd (indirect for __ZTIPd)
+I __ZTIPe (indirect for __ZTIPe)
+I __ZTIPf (indirect for __ZTIPf)
+I __ZTIPg (indirect for __ZTIPg)
+I __ZTIPh (indirect for __ZTIPh)
+I __ZTIPi (indirect for __ZTIPi)
+I __ZTIPj (indirect for __ZTIPj)
+I __ZTIPl (indirect for __ZTIPl)
+I __ZTIPm (indirect for __ZTIPm)
+I __ZTIPn (indirect for __ZTIPn)
+I __ZTIPo (indirect for __ZTIPo)
+I __ZTIPs (indirect for __ZTIPs)
+I __ZTIPt (indirect for __ZTIPt)
+I __ZTIPv (indirect for __ZTIPv)
+I __ZTIPw (indirect for __ZTIPw)
+I __ZTIPx (indirect for __ZTIPx)
+I __ZTIPy (indirect for __ZTIPy)
+I __ZTISt10bad_typeid (indirect for __ZTISt10bad_typeid)
+I __ZTISt11logic_error (indirect for __ZTISt11logic_error)
+I __ZTISt11range_error (indirect for __ZTISt11range_error)
+S __ZTISt12bad_any_cast
+I __ZTISt12domain_error (indirect for __ZTISt12domain_error)
+I __ZTISt12length_error (indirect for __ZTISt12length_error)
+I __ZTISt12out_of_range (indirect for __ZTISt12out_of_range)
+I __ZTISt13bad_exception (indirect for __ZTISt13bad_exception)
+I __ZTISt13runtime_error (indirect for __ZTISt13runtime_error)
+I __ZTISt14overflow_error (indirect for __ZTISt14overflow_error)
+I __ZTISt15underflow_error (indirect for __ZTISt15underflow_error)
+I __ZTISt16invalid_argument (indirect for __ZTISt16invalid_argument)
+S __ZTISt16nested_exception
+S __ZTISt18bad_variant_access
+S __ZTISt19bad_optional_access
+I __ZTISt20bad_array_new_length (indirect for __ZTISt20bad_array_new_length)
+S __ZTISt29__bad_variant_access_with_msg
+I __ZTISt8bad_cast (indirect for __ZTISt8bad_cast)
+I __ZTISt9bad_alloc (indirect for __ZTISt9bad_alloc)
+I __ZTISt9exception (indirect for __ZTISt9exception)
+I __ZTISt9type_info (indirect for __ZTISt9type_info)
+I __ZTIa (indirect for __ZTIa)
+I __ZTIb (indirect for __ZTIb)
+I __ZTIc (indirect for __ZTIc)
+I __ZTId (indirect for __ZTId)
+I __ZTIe (indirect for __ZTIe)
+I __ZTIf (indirect for __ZTIf)
+I __ZTIg (indirect for __ZTIg)
+I __ZTIh (indirect for __ZTIh)
+I __ZTIi (indirect for __ZTIi)
+I __ZTIj (indirect for __ZTIj)
+I __ZTIl (indirect for __ZTIl)
+I __ZTIm (indirect for __ZTIm)
+I __ZTIn (indirect for __ZTIn)
+I __ZTIo (indirect for __ZTIo)
+I __ZTIs (indirect for __ZTIs)
+I __ZTIt (indirect for __ZTIt)
+I __ZTIv (indirect for __ZTIv)
+I __ZTIw (indirect for __ZTIw)
+I __ZTIx (indirect for __ZTIx)
+I __ZTIy (indirect for __ZTIy)
+I __ZTSDh (indirect for __ZTSDh)
+I __ZTSDi (indirect for __ZTSDi)
+I __ZTSDn (indirect for __ZTSDn)
+I __ZTSDs (indirect for __ZTSDs)
+I __ZTSDu (indirect for __ZTSDu)
+I __ZTSN10__cxxabiv116__enum_type_infoE (indirect for __ZTSN10__cxxabiv116__enum_type_infoE)
+I __ZTSN10__cxxabiv116__shim_type_infoE (indirect for __ZTSN10__cxxabiv116__shim_type_infoE)
+I __ZTSN10__cxxabiv117__array_type_infoE (indirect for __ZTSN10__cxxabiv117__array_type_infoE)
+I __ZTSN10__cxxabiv117__class_type_infoE (indirect for __ZTSN10__cxxabiv117__class_type_infoE)
+I __ZTSN10__cxxabiv117__pbase_type_infoE (indirect for __ZTSN10__cxxabiv117__pbase_type_infoE)
+I __ZTSN10__cxxabiv119__pointer_type_infoE (indirect for __ZTSN10__cxxabiv119__pointer_type_infoE)
+I __ZTSN10__cxxabiv120__function_type_infoE (indirect for __ZTSN10__cxxabiv120__function_type_infoE)
+I __ZTSN10__cxxabiv120__si_class_type_infoE (indirect for __ZTSN10__cxxabiv120__si_class_type_infoE)
+I __ZTSN10__cxxabiv121__vmi_class_type_infoE (indirect for __ZTSN10__cxxabiv121__vmi_class_type_infoE)
+I __ZTSN10__cxxabiv123__fundamental_type_infoE (indirect for __ZTSN10__cxxabiv123__fundamental_type_infoE)
+I __ZTSN10__cxxabiv129__pointer_to_member_type_infoE (indirect for __ZTSN10__cxxabiv129__pointer_to_member_type_infoE)
+S __ZTSNSt12experimental15fundamentals_v112bad_any_castE
+S __ZTSNSt12experimental19bad_optional_accessE
+S __ZTSNSt3__110istrstreamE
+S __ZTSNSt3__110moneypunctIcLb0EEE
+S __ZTSNSt3__110moneypunctIcLb1EEE
+S __ZTSNSt3__110moneypunctIwLb0EEE
+S __ZTSNSt3__110moneypunctIwLb1EEE
+S __ZTSNSt3__110ostrstreamE
+S __ZTSNSt3__111regex_errorE
+S __ZTSNSt3__112bad_weak_ptrE
+S __ZTSNSt3__112ctype_bynameIcEE
+S __ZTSNSt3__112ctype_bynameIwEE
+S __ZTSNSt3__112future_errorE
+S __ZTSNSt3__112strstreambufE
+S __ZTSNSt3__112system_errorE
+S __ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__114__codecvt_utf8IDiEE
+S __ZTSNSt3__114__codecvt_utf8IDsEE
+S __ZTSNSt3__114__codecvt_utf8IwEE
+S __ZTSNSt3__114__shared_countE
+S __ZTSNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIDic11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIDsc11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIcc11__mbstate_tEE
+S __ZTSNSt3__114codecvt_bynameIwc11__mbstate_tEE
+S __ZTSNSt3__114collate_bynameIcEE
+S __ZTSNSt3__114collate_bynameIwEE
+S __ZTSNSt3__114error_categoryE
+S __ZTSNSt3__115__codecvt_utf16IDiLb0EEE
+S __ZTSNSt3__115__codecvt_utf16IDiLb1EEE
+S __ZTSNSt3__115__codecvt_utf16IDsLb0EEE
+S __ZTSNSt3__115__codecvt_utf16IDsLb1EEE
+S __ZTSNSt3__115__codecvt_utf16IwLb0EEE
+S __ZTSNSt3__115__codecvt_utf16IwLb1EEE
+S __ZTSNSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__115messages_bynameIcEE
+S __ZTSNSt3__115messages_bynameIwEE
+S __ZTSNSt3__115numpunct_bynameIcEE
+S __ZTSNSt3__115numpunct_bynameIwEE
+S __ZTSNSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__116__narrow_to_utf8ILm16EEE
+S __ZTSNSt3__116__narrow_to_utf8ILm32EEE
+S __ZTSNSt3__117__assoc_sub_stateE
+S __ZTSNSt3__117__widen_from_utf8ILm16EEE
+S __ZTSNSt3__117__widen_from_utf8ILm32EEE
+S __ZTSNSt3__117bad_function_callE
+S __ZTSNSt3__117moneypunct_bynameIcLb0EEE
+S __ZTSNSt3__117moneypunct_bynameIcLb1EEE
+S __ZTSNSt3__117moneypunct_bynameIwLb0EEE
+S __ZTSNSt3__117moneypunct_bynameIwLb1EEE
+S __ZTSNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__119__shared_weak_countE
+S __ZTSNSt3__119bad_expected_accessIvEE
+S __ZTSNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTSNSt3__120__codecvt_utf8_utf16IDiEE
+S __ZTSNSt3__120__codecvt_utf8_utf16IDsEE
+S __ZTSNSt3__120__codecvt_utf8_utf16IwEE
+S __ZTSNSt3__13pmr15memory_resourceE
+S __ZTSNSt3__13pmr25monotonic_buffer_resourceE
+S __ZTSNSt3__13pmr26synchronized_pool_resourceE
+S __ZTSNSt3__13pmr28unsynchronized_pool_resourceE
+S __ZTSNSt3__14__fs10filesystem16filesystem_errorE
+S __ZTSNSt3__15ctypeIcEE
+S __ZTSNSt3__15ctypeIwEE
+S __ZTSNSt3__16locale5facetE
+S __ZTSNSt3__17codecvtIDiDu11__mbstate_tEE
+S __ZTSNSt3__17codecvtIDic11__mbstate_tEE
+S __ZTSNSt3__17codecvtIDsDu11__mbstate_tEE
+S __ZTSNSt3__17codecvtIDsc11__mbstate_tEE
+S __ZTSNSt3__17codecvtIcc11__mbstate_tEE
+S __ZTSNSt3__17codecvtIwc11__mbstate_tEE
+S __ZTSNSt3__17collateIcEE
+S __ZTSNSt3__17collateIwEE
+S __ZTSNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__18ios_base7failureE
+S __ZTSNSt3__18ios_baseE
+S __ZTSNSt3__18messagesIcEE
+S __ZTSNSt3__18messagesIwEE
+S __ZTSNSt3__18numpunctIcEE
+S __ZTSNSt3__18numpunctIwEE
+S __ZTSNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__19basic_iosIcNS_11char_traitsIcEEEE
+S __ZTSNSt3__19basic_iosIwNS_11char_traitsIwEEEE
+S __ZTSNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTSNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTSNSt3__19strstreamE
+I __ZTSPDh (indirect for __ZTSPDh)
+I __ZTSPDi (indirect for __ZTSPDi)
+I __ZTSPDn (indirect for __ZTSPDn)
+I __ZTSPDs (indirect for __ZTSPDs)
+I __ZTSPDu (indirect for __ZTSPDu)
+I __ZTSPKDh (indirect for __ZTSPKDh)
+I __ZTSPKDi (indirect for __ZTSPKDi)
+I __ZTSPKDn (indirect for __ZTSPKDn)
+I __ZTSPKDs (indirect for __ZTSPKDs)
+I __ZTSPKDu (indirect for __ZTSPKDu)
+I __ZTSPKa (indirect for __ZTSPKa)
+I __ZTSPKb (indirect for __ZTSPKb)
+I __ZTSPKc (indirect for __ZTSPKc)
+I __ZTSPKd (indirect for __ZTSPKd)
+I __ZTSPKe (indirect for __ZTSPKe)
+I __ZTSPKf (indirect for __ZTSPKf)
+I __ZTSPKg (indirect for __ZTSPKg)
+I __ZTSPKh (indirect for __ZTSPKh)
+I __ZTSPKi (indirect for __ZTSPKi)
+I __ZTSPKj (indirect for __ZTSPKj)
+I __ZTSPKl (indirect for __ZTSPKl)
+I __ZTSPKm (indirect for __ZTSPKm)
+I __ZTSPKn (indirect for __ZTSPKn)
+I __ZTSPKo (indirect for __ZTSPKo)
+I __ZTSPKs (indirect for __ZTSPKs)
+I __ZTSPKt (indirect for __ZTSPKt)
+I __ZTSPKv (indirect for __ZTSPKv)
+I __ZTSPKw (indirect for __ZTSPKw)
+I __ZTSPKx (indirect for __ZTSPKx)
+I __ZTSPKy (indirect for __ZTSPKy)
+I __ZTSPa (indirect for __ZTSPa)
+I __ZTSPb (indirect for __ZTSPb)
+I __ZTSPc (indirect for __ZTSPc)
+I __ZTSPd (indirect for __ZTSPd)
+I __ZTSPe (indirect for __ZTSPe)
+I __ZTSPf (indirect for __ZTSPf)
+I __ZTSPg (indirect for __ZTSPg)
+I __ZTSPh (indirect for __ZTSPh)
+I __ZTSPi (indirect for __ZTSPi)
+I __ZTSPj (indirect for __ZTSPj)
+I __ZTSPl (indirect for __ZTSPl)
+I __ZTSPm (indirect for __ZTSPm)
+I __ZTSPn (indirect for __ZTSPn)
+I __ZTSPo (indirect for __ZTSPo)
+I __ZTSPs (indirect for __ZTSPs)
+I __ZTSPt (indirect for __ZTSPt)
+I __ZTSPv (indirect for __ZTSPv)
+I __ZTSPw (indirect for __ZTSPw)
+I __ZTSPx (indirect for __ZTSPx)
+I __ZTSPy (indirect for __ZTSPy)
+I __ZTSSt10bad_typeid (indirect for __ZTSSt10bad_typeid)
+I __ZTSSt11logic_error (indirect for __ZTSSt11logic_error)
+I __ZTSSt11range_error (indirect for __ZTSSt11range_error)
+S __ZTSSt12bad_any_cast
+I __ZTSSt12domain_error (indirect for __ZTSSt12domain_error)
+I __ZTSSt12length_error (indirect for __ZTSSt12length_error)
+I __ZTSSt12out_of_range (indirect for __ZTSSt12out_of_range)
+I __ZTSSt13bad_exception (indirect for __ZTSSt13bad_exception)
+I __ZTSSt13runtime_error (indirect for __ZTSSt13runtime_error)
+I __ZTSSt14overflow_error (indirect for __ZTSSt14overflow_error)
+I __ZTSSt15underflow_error (indirect for __ZTSSt15underflow_error)
+I __ZTSSt16invalid_argument (indirect for __ZTSSt16invalid_argument)
+S __ZTSSt16nested_exception
+S __ZTSSt18bad_variant_access
+S __ZTSSt19bad_optional_access
+I __ZTSSt20bad_array_new_length (indirect for __ZTSSt20bad_array_new_length)
+S __ZTSSt29__bad_variant_access_with_msg
+I __ZTSSt8bad_cast (indirect for __ZTSSt8bad_cast)
+I __ZTSSt9bad_alloc (indirect for __ZTSSt9bad_alloc)
+I __ZTSSt9exception (indirect for __ZTSSt9exception)
+I __ZTSSt9type_info (indirect for __ZTSSt9type_info)
+I __ZTSa (indirect for __ZTSa)
+I __ZTSb (indirect for __ZTSb)
+I __ZTSc (indirect for __ZTSc)
+I __ZTSd (indirect for __ZTSd)
+I __ZTSe (indirect for __ZTSe)
+I __ZTSf (indirect for __ZTSf)
+I __ZTSg (indirect for __ZTSg)
+I __ZTSh (indirect for __ZTSh)
+I __ZTSi (indirect for __ZTSi)
+I __ZTSj (indirect for __ZTSj)
+I __ZTSl (indirect for __ZTSl)
+I __ZTSm (indirect for __ZTSm)
+I __ZTSn (indirect for __ZTSn)
+I __ZTSo (indirect for __ZTSo)
+I __ZTSs (indirect for __ZTSs)
+I __ZTSt (indirect for __ZTSt)
+I __ZTSv (indirect for __ZTSv)
+I __ZTSw (indirect for __ZTSw)
+I __ZTSx (indirect for __ZTSx)
+I __ZTSy (indirect for __ZTSy)
+S __ZTTNSt3__110istrstreamE
+S __ZTTNSt3__110ostrstreamE
+S __ZTTNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTTNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTTNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTTNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTTNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTTNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTTNSt3__19strstreamE
+I __ZTVN10__cxxabiv116__enum_type_infoE (indirect for __ZTVN10__cxxabiv116__enum_type_infoE)
+I __ZTVN10__cxxabiv116__shim_type_infoE (indirect for __ZTVN10__cxxabiv116__shim_type_infoE)
+I __ZTVN10__cxxabiv117__array_type_infoE (indirect for __ZTVN10__cxxabiv117__array_type_infoE)
+I __ZTVN10__cxxabiv117__class_type_infoE (indirect for __ZTVN10__cxxabiv117__class_type_infoE)
+I __ZTVN10__cxxabiv117__pbase_type_infoE (indirect for __ZTVN10__cxxabiv117__pbase_type_infoE)
+I __ZTVN10__cxxabiv119__pointer_type_infoE (indirect for __ZTVN10__cxxabiv119__pointer_type_infoE)
+I __ZTVN10__cxxabiv120__function_type_infoE (indirect for __ZTVN10__cxxabiv120__function_type_infoE)
+I __ZTVN10__cxxabiv120__si_class_type_infoE (indirect for __ZTVN10__cxxabiv120__si_class_type_infoE)
+I __ZTVN10__cxxabiv121__vmi_class_type_infoE (indirect for __ZTVN10__cxxabiv121__vmi_class_type_infoE)
+I __ZTVN10__cxxabiv123__fundamental_type_infoE (indirect for __ZTVN10__cxxabiv123__fundamental_type_infoE)
+I __ZTVN10__cxxabiv129__pointer_to_member_type_infoE (indirect for __ZTVN10__cxxabiv129__pointer_to_member_type_infoE)
+S __ZTVNSt12experimental15fundamentals_v112bad_any_castE
+S __ZTVNSt12experimental19bad_optional_accessE
+S __ZTVNSt3__110istrstreamE
+S __ZTVNSt3__110moneypunctIcLb0EEE
+S __ZTVNSt3__110moneypunctIcLb1EEE
+S __ZTVNSt3__110moneypunctIwLb0EEE
+S __ZTVNSt3__110moneypunctIwLb1EEE
+S __ZTVNSt3__110ostrstreamE
+S __ZTVNSt3__111regex_errorE
+S __ZTVNSt3__112bad_weak_ptrE
+S __ZTVNSt3__112ctype_bynameIcEE
+S __ZTVNSt3__112ctype_bynameIwEE
+S __ZTVNSt3__112future_errorE
+S __ZTVNSt3__112strstreambufE
+S __ZTVNSt3__112system_errorE
+S __ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__114__codecvt_utf8IDiEE
+S __ZTVNSt3__114__codecvt_utf8IDsEE
+S __ZTVNSt3__114__codecvt_utf8IwEE
+S __ZTVNSt3__114__shared_countE
+S __ZTVNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIDic11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIDsc11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIcc11__mbstate_tEE
+S __ZTVNSt3__114codecvt_bynameIwc11__mbstate_tEE
+S __ZTVNSt3__114collate_bynameIcEE
+S __ZTVNSt3__114collate_bynameIwEE
+S __ZTVNSt3__114error_categoryE
+S __ZTVNSt3__115__codecvt_utf16IDiLb0EEE
+S __ZTVNSt3__115__codecvt_utf16IDiLb1EEE
+S __ZTVNSt3__115__codecvt_utf16IDsLb0EEE
+S __ZTVNSt3__115__codecvt_utf16IDsLb1EEE
+S __ZTVNSt3__115__codecvt_utf16IwLb0EEE
+S __ZTVNSt3__115__codecvt_utf16IwLb1EEE
+S __ZTVNSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__115messages_bynameIcEE
+S __ZTVNSt3__115messages_bynameIwEE
+S __ZTVNSt3__115numpunct_bynameIcEE
+S __ZTVNSt3__115numpunct_bynameIwEE
+S __ZTVNSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__116__narrow_to_utf8ILm16EEE
+S __ZTVNSt3__116__narrow_to_utf8ILm32EEE
+S __ZTVNSt3__117__assoc_sub_stateE
+S __ZTVNSt3__117__widen_from_utf8ILm16EEE
+S __ZTVNSt3__117__widen_from_utf8ILm32EEE
+S __ZTVNSt3__117bad_function_callE
+S __ZTVNSt3__117moneypunct_bynameIcLb0EEE
+S __ZTVNSt3__117moneypunct_bynameIcLb1EEE
+S __ZTVNSt3__117moneypunct_bynameIwLb0EEE
+S __ZTVNSt3__117moneypunct_bynameIwLb1EEE
+S __ZTVNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__119__shared_weak_countE
+S __ZTVNSt3__119bad_expected_accessIvEE
+S __ZTVNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+S __ZTVNSt3__120__codecvt_utf8_utf16IDiEE
+S __ZTVNSt3__120__codecvt_utf8_utf16IDsEE
+S __ZTVNSt3__120__codecvt_utf8_utf16IwEE
+S __ZTVNSt3__13pmr15memory_resourceE
+S __ZTVNSt3__13pmr25monotonic_buffer_resourceE
+S __ZTVNSt3__13pmr26synchronized_pool_resourceE
+S __ZTVNSt3__13pmr28unsynchronized_pool_resourceE
+S __ZTVNSt3__14__fs10filesystem16filesystem_errorE
+S __ZTVNSt3__15ctypeIcEE
+S __ZTVNSt3__15ctypeIwEE
+S __ZTVNSt3__16locale5facetE
+S __ZTVNSt3__17codecvtIDiDu11__mbstate_tEE
+S __ZTVNSt3__17codecvtIDic11__mbstate_tEE
+S __ZTVNSt3__17codecvtIDsDu11__mbstate_tEE
+S __ZTVNSt3__17codecvtIDsc11__mbstate_tEE
+S __ZTVNSt3__17codecvtIcc11__mbstate_tEE
+S __ZTVNSt3__17codecvtIwc11__mbstate_tEE
+S __ZTVNSt3__17collateIcEE
+S __ZTVNSt3__17collateIwEE
+S __ZTVNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__18ios_base7failureE
+S __ZTVNSt3__18ios_baseE
+S __ZTVNSt3__18messagesIcEE
+S __ZTVNSt3__18messagesIwEE
+S __ZTVNSt3__18numpunctIcEE
+S __ZTVNSt3__18numpunctIwEE
+S __ZTVNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__19basic_iosIcNS_11char_traitsIcEEEE
+S __ZTVNSt3__19basic_iosIwNS_11char_traitsIwEEEE
+S __ZTVNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+S __ZTVNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+S __ZTVNSt3__19strstreamE
+I __ZTVSt10bad_typeid (indirect for __ZTVSt10bad_typeid)
+I __ZTVSt11logic_error (indirect for __ZTVSt11logic_error)
+I __ZTVSt11range_error (indirect for __ZTVSt11range_error)
+S __ZTVSt12bad_any_cast
+I __ZTVSt12domain_error (indirect for __ZTVSt12domain_error)
+I __ZTVSt12length_error (indirect for __ZTVSt12length_error)
+I __ZTVSt12out_of_range (indirect for __ZTVSt12out_of_range)
+I __ZTVSt13bad_exception (indirect for __ZTVSt13bad_exception)
+I __ZTVSt13runtime_error (indirect for __ZTVSt13runtime_error)
+I __ZTVSt14overflow_error (indirect for __ZTVSt14overflow_error)
+I __ZTVSt15underflow_error (indirect for __ZTVSt15underflow_error)
+I __ZTVSt16invalid_argument (indirect for __ZTVSt16invalid_argument)
+S __ZTVSt16nested_exception
+S __ZTVSt18bad_variant_access
+S __ZTVSt19bad_optional_access
+I __ZTVSt20bad_array_new_length (indirect for __ZTVSt20bad_array_new_length)
+S __ZTVSt29__bad_variant_access_with_msg
+I __ZTVSt8bad_cast (indirect for __ZTVSt8bad_cast)
+I __ZTVSt9bad_alloc (indirect for __ZTVSt9bad_alloc)
+I __ZTVSt9exception (indirect for __ZTVSt9exception)
+I __ZTVSt9type_info (indirect for __ZTVSt9type_info)
+T __ZThn16_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+T __ZThn16_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T __ZThn16_NSt3__19strstreamD0Ev
+T __ZThn16_NSt3__19strstreamD1Ev
+T __ZTv0_n24_NSt3__110istrstreamD0Ev
+T __ZTv0_n24_NSt3__110istrstreamD1Ev
+T __ZTv0_n24_NSt3__110ostrstreamD0Ev
+T __ZTv0_n24_NSt3__110ostrstreamD1Ev
+T __ZTv0_n24_NSt3__113basic_istreamIcNS_11char_traitsIcEEED0Ev
+T __ZTv0_n24_NSt3__113basic_istreamIcNS_11char_traitsIcEEED1Ev
+T __ZTv0_n24_NSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev
+T __ZTv0_n24_NSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIwNS_11char_traitsIwEEED0Ev
+T __ZTv0_n24_NSt3__113basic_ostreamIwNS_11char_traitsIwEEED1Ev
+T __ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+T __ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+T __ZTv0_n24_NSt3__19strstreamD0Ev
+T __ZTv0_n24_NSt3__19strstreamD1Ev
+I __ZdaPv (indirect for __ZdaPv)
+I __ZdaPvRKSt9nothrow_t (indirect for __ZdaPvRKSt9nothrow_t)
+I __ZdaPvSt11align_val_t (indirect for __ZdaPvSt11align_val_t)
+I __ZdaPvSt11align_val_tRKSt9nothrow_t (indirect for __ZdaPvSt11align_val_tRKSt9nothrow_t)
+I __ZdaPvm (indirect for __ZdaPvm)
+I __ZdaPvmSt11align_val_t (indirect for __ZdaPvmSt11align_val_t)
+I __ZdlPv (indirect for __ZdlPv)
+I __ZdlPvRKSt9nothrow_t (indirect for __ZdlPvRKSt9nothrow_t)
+I __ZdlPvSt11align_val_t (indirect for __ZdlPvSt11align_val_t)
+I __ZdlPvSt11align_val_tRKSt9nothrow_t (indirect for __ZdlPvSt11align_val_tRKSt9nothrow_t)
+I __ZdlPvm (indirect for __ZdlPvm)
+I __ZdlPvmSt11align_val_t (indirect for __ZdlPvmSt11align_val_t)
+I __Znam (indirect for __Znam)
+I __ZnamRKSt9nothrow_t (indirect for __ZnamRKSt9nothrow_t)
+I __ZnamSt11align_val_t (indirect for __ZnamSt11align_val_t)
+I __ZnamSt11align_val_tRKSt9nothrow_t (indirect for __ZnamSt11align_val_tRKSt9nothrow_t)
+I __Znwm (indirect for __Znwm)
+I __ZnwmRKSt9nothrow_t (indirect for __ZnwmRKSt9nothrow_t)
+I __ZnwmSt11align_val_t (indirect for __ZnwmSt11align_val_t)
+I __ZnwmSt11align_val_tRKSt9nothrow_t (indirect for __ZnwmSt11align_val_tRKSt9nothrow_t)
+I ___cxa_allocate_dependent_exception (indirect for ___cxa_allocate_dependent_exception)
+I ___cxa_allocate_exception (indirect for ___cxa_allocate_exception)
+I ___cxa_bad_cast (indirect for ___cxa_bad_cast)
+I ___cxa_bad_typeid (indirect for ___cxa_bad_typeid)
+I ___cxa_begin_catch (indirect for ___cxa_begin_catch)
+I ___cxa_call_terminate (indirect for ___cxa_call_terminate)
+I ___cxa_call_unexpected (indirect for ___cxa_call_unexpected)
+I ___cxa_current_exception_type (indirect for ___cxa_current_exception_type)
+I ___cxa_current_primary_exception (indirect for ___cxa_current_primary_exception)
+I ___cxa_decrement_exception_refcount (indirect for ___cxa_decrement_exception_refcount)
+I ___cxa_deleted_virtual (indirect for ___cxa_deleted_virtual)
+I ___cxa_demangle (indirect for ___cxa_demangle)
+I ___cxa_end_catch (indirect for ___cxa_end_catch)
+I ___cxa_free_dependent_exception (indirect for ___cxa_free_dependent_exception)
+I ___cxa_free_exception (indirect for ___cxa_free_exception)
+I ___cxa_get_exception_ptr (indirect for ___cxa_get_exception_ptr)
+I ___cxa_get_globals (indirect for ___cxa_get_globals)
+I ___cxa_get_globals_fast (indirect for ___cxa_get_globals_fast)
+I ___cxa_guard_abort (indirect for ___cxa_guard_abort)
+I ___cxa_guard_acquire (indirect for ___cxa_guard_acquire)
+I ___cxa_guard_release (indirect for ___cxa_guard_release)
+I ___cxa_increment_exception_refcount (indirect for ___cxa_increment_exception_refcount)
+I ___cxa_init_primary_exception (indirect for ___cxa_init_primary_exception)
+I ___cxa_new_handler (indirect for ___cxa_new_handler)
+I ___cxa_pure_virtual (indirect for ___cxa_pure_virtual)
+I ___cxa_rethrow (indirect for ___cxa_rethrow)
+I ___cxa_terminate_handler (indirect for ___cxa_terminate_handler)
+I ___cxa_throw (indirect for ___cxa_throw)
+I ___cxa_throw_bad_array_new_length (indirect for ___cxa_throw_bad_array_new_length)
+I ___cxa_uncaught_exception (indirect for ___cxa_uncaught_exception)
+I ___cxa_uncaught_exceptions (indirect for ___cxa_uncaught_exceptions)
+I ___cxa_unexpected_handler (indirect for ___cxa_unexpected_handler)
+I ___cxa_vec_cctor (indirect for ___cxa_vec_cctor)
+I ___cxa_vec_cleanup (indirect for ___cxa_vec_cleanup)
+I ___cxa_vec_ctor (indirect for ___cxa_vec_ctor)
+I ___cxa_vec_delete (indirect for ___cxa_vec_delete)
+I ___cxa_vec_delete2 (indirect for ___cxa_vec_delete2)
+I ___cxa_vec_delete3 (indirect for ___cxa_vec_delete3)
+I ___cxa_vec_dtor (indirect for ___cxa_vec_dtor)
+I ___cxa_vec_new (indirect for ___cxa_vec_new)
+I ___cxa_vec_new2 (indirect for ___cxa_vec_new2)
+I ___cxa_vec_new3 (indirect for ___cxa_vec_new3)
+I ___dynamic_cast (indirect for ___dynamic_cast)
+I ___gxx_personality_v0 (indirect for ___gxx_personality_v0)
+T ___muloti4
diff --git a/libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/abilist.sh.cpp b/libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/abilist.sh.cpp
new file mode 100644
index 0000000000000..738f465c84596
--- /dev/null
+++ b/libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/abilist.sh.cpp
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FIXME: Encode whether the library was built with exceptions and check the abilist for that case as well
+
+// REQUIRES: target=x86_64-unknown-linux-gnu && !no-exceptions
+
+// Check that the list of symbols exported from the dylib doesn't change unexpectedly
+
+// FILE_DEPENDENCIES: expected.abilist
+
+// RUN: nm --defined-only --extern-only %{lib-dir}/libc++.so.1.0 | cut -c 18- | sort > %t.actual.abilist
+// RUN: diff expected.abilist %t.actual.abilist
diff --git a/libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/expected.abilist b/libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/expected.abilist
new file mode 100644
index 0000000000000..7ae69ee004c12
--- /dev/null
+++ b/libcxx/test/libcxx/abilists/x86_64-unknown-linux-gnu/expected.abilist
@@ -0,0 +1,1973 @@
+B _ZNSt3__112__rs_default4__c_E
+B _ZNSt3__13cinE
+B _ZNSt3__14cerrE
+B _ZNSt3__14clogE
+B _ZNSt3__14coutE
+B _ZNSt3__14wcinE
+B _ZNSt3__15ctypeIcE2idE
+B _ZNSt3__15ctypeIwE2idE
+B _ZNSt3__15wcerrE
+B _ZNSt3__15wclogE
+B _ZNSt3__15wcoutE
+B _ZNSt3__16locale2id9__next_idE
+B _ZNSt3__17codecvtIcc11__mbstate_tE2idE
+B _ZNSt3__17codecvtIDic11__mbstate_tE2idE
+B _ZNSt3__17codecvtIDiDu11__mbstate_tE2idE
+B _ZNSt3__17codecvtIDsc11__mbstate_tE2idE
+B _ZNSt3__17codecvtIDsDu11__mbstate_tE2idE
+B _ZNSt3__17codecvtIwc11__mbstate_tE2idE
+B _ZNSt3__18numpunctIcE2idE
+B _ZNSt3__18numpunctIwE2idE
+D _ZTCNSt3__110istrstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE
+D _ZTCNSt3__110ostrstreamE0_NS_13basic_ostreamIcNS_11char_traitsIcEEEE
+D _ZTCNSt3__19strstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE
+D _ZTCNSt3__19strstreamE0_NS_14basic_iostreamIcNS_11char_traitsIcEEEE
+D _ZTCNSt3__19strstreamE16_NS_13basic_ostreamIcNS_11char_traitsIcEEEE
+D _ZTINSt12experimental15fundamentals_v112bad_any_castE
+D _ZTINSt12experimental19bad_optional_accessE
+D _ZTINSt3__110istrstreamE
+D _ZTINSt3__110ostrstreamE
+D _ZTINSt3__111regex_errorE
+D _ZTINSt3__112bad_weak_ptrE
+D _ZTINSt3__112ctype_bynameIcEE
+D _ZTINSt3__112ctype_bynameIwEE
+D _ZTINSt3__112future_errorE
+D _ZTINSt3__112strstreambufE
+D _ZTINSt3__112system_errorE
+D _ZTINSt3__114__codecvt_utf8IDiEE
+D _ZTINSt3__114__codecvt_utf8IDsEE
+D _ZTINSt3__114__codecvt_utf8IwEE
+D _ZTINSt3__114collate_bynameIcEE
+D _ZTINSt3__114collate_bynameIwEE
+D _ZTINSt3__114error_categoryE
+D _ZTINSt3__114__shared_countE
+D _ZTINSt3__115__codecvt_utf16IDiLb0EEE
+D _ZTINSt3__115__codecvt_utf16IDiLb1EEE
+D _ZTINSt3__115__codecvt_utf16IDsLb0EEE
+D _ZTINSt3__115__codecvt_utf16IDsLb1EEE
+D _ZTINSt3__115__codecvt_utf16IwLb0EEE
+D _ZTINSt3__115__codecvt_utf16IwLb1EEE
+D _ZTINSt3__115numpunct_bynameIcEE
+D _ZTINSt3__115numpunct_bynameIwEE
+D _ZTINSt3__116__narrow_to_utf8ILm16EEE
+D _ZTINSt3__116__narrow_to_utf8ILm32EEE
+D _ZTINSt3__117__assoc_sub_stateE
+D _ZTINSt3__117bad_function_callE
+D _ZTINSt3__117__widen_from_utf8ILm16EEE
+D _ZTINSt3__117__widen_from_utf8ILm32EEE
+D _ZTINSt3__119bad_expected_accessIvEE
+D _ZTINSt3__119__shared_weak_countE
+D _ZTINSt3__120__codecvt_utf8_utf16IDiEE
+D _ZTINSt3__120__codecvt_utf8_utf16IDsEE
+D _ZTINSt3__120__codecvt_utf8_utf16IwEE
+D _ZTINSt3__13pmr15memory_resourceE
+D _ZTINSt3__13pmr25monotonic_buffer_resourceE
+D _ZTINSt3__13pmr26synchronized_pool_resourceE
+D _ZTINSt3__13pmr28unsynchronized_pool_resourceE
+D _ZTINSt3__14__fs10filesystem16filesystem_errorE
+D _ZTINSt3__15ctypeIcEE
+D _ZTINSt3__15ctypeIwEE
+D _ZTINSt3__16locale5facetE
+D _ZTINSt3__17codecvtIcc11__mbstate_tEE
+D _ZTINSt3__17codecvtIDic11__mbstate_tEE
+D _ZTINSt3__17codecvtIDiDu11__mbstate_tEE
+D _ZTINSt3__17codecvtIDsc11__mbstate_tEE
+D _ZTINSt3__17codecvtIDsDu11__mbstate_tEE
+D _ZTINSt3__17codecvtIwc11__mbstate_tEE
+D _ZTINSt3__18ios_base7failureE
+D _ZTINSt3__18ios_baseE
+D _ZTINSt3__18numpunctIcEE
+D _ZTINSt3__18numpunctIwEE
+D _ZTINSt3__19strstreamE
+D _ZTISt12bad_any_cast
+D _ZTISt16nested_exception
+D _ZTISt18bad_variant_access
+D _ZTISt19bad_optional_access
+D _ZTISt29__bad_variant_access_with_msg
+D _ZTTNSt3__110istrstreamE
+D _ZTTNSt3__110ostrstreamE
+D _ZTTNSt3__19strstreamE
+D _ZTVNSt12experimental15fundamentals_v112bad_any_castE
+D _ZTVNSt12experimental19bad_optional_accessE
+D _ZTVNSt3__110istrstreamE
+D _ZTVNSt3__110ostrstreamE
+D _ZTVNSt3__111regex_errorE
+D _ZTVNSt3__112bad_weak_ptrE
+D _ZTVNSt3__112ctype_bynameIcEE
+D _ZTVNSt3__112ctype_bynameIwEE
+D _ZTVNSt3__112future_errorE
+D _ZTVNSt3__112strstreambufE
+D _ZTVNSt3__112system_errorE
+D _ZTVNSt3__114__codecvt_utf8IDiEE
+D _ZTVNSt3__114__codecvt_utf8IDsEE
+D _ZTVNSt3__114__codecvt_utf8IwEE
+D _ZTVNSt3__114collate_bynameIcEE
+D _ZTVNSt3__114collate_bynameIwEE
+D _ZTVNSt3__114error_categoryE
+D _ZTVNSt3__114__shared_countE
+D _ZTVNSt3__115__codecvt_utf16IDiLb0EEE
+D _ZTVNSt3__115__codecvt_utf16IDiLb1EEE
+D _ZTVNSt3__115__codecvt_utf16IDsLb0EEE
+D _ZTVNSt3__115__codecvt_utf16IDsLb1EEE
+D _ZTVNSt3__115__codecvt_utf16IwLb0EEE
+D _ZTVNSt3__115__codecvt_utf16IwLb1EEE
+D _ZTVNSt3__115numpunct_bynameIcEE
+D _ZTVNSt3__115numpunct_bynameIwEE
+D _ZTVNSt3__116__narrow_to_utf8ILm16EEE
+D _ZTVNSt3__116__narrow_to_utf8ILm32EEE
+D _ZTVNSt3__117__assoc_sub_stateE
+D _ZTVNSt3__117bad_function_callE
+D _ZTVNSt3__117__widen_from_utf8ILm16EEE
+D _ZTVNSt3__117__widen_from_utf8ILm32EEE
+D _ZTVNSt3__119bad_expected_accessIvEE
+D _ZTVNSt3__119__shared_weak_countE
+D _ZTVNSt3__120__codecvt_utf8_utf16IDiEE
+D _ZTVNSt3__120__codecvt_utf8_utf16IDsEE
+D _ZTVNSt3__120__codecvt_utf8_utf16IwEE
+D _ZTVNSt3__13pmr15memory_resourceE
+D _ZTVNSt3__13pmr25monotonic_buffer_resourceE
+D _ZTVNSt3__13pmr26synchronized_pool_resourceE
+D _ZTVNSt3__13pmr28unsynchronized_pool_resourceE
+D _ZTVNSt3__14__fs10filesystem16filesystem_errorE
+D _ZTVNSt3__15ctypeIcEE
+D _ZTVNSt3__15ctypeIwEE
+D _ZTVNSt3__16locale5facetE
+D _ZTVNSt3__17codecvtIcc11__mbstate_tEE
+D _ZTVNSt3__17codecvtIDic11__mbstate_tEE
+D _ZTVNSt3__17codecvtIDiDu11__mbstate_tEE
+D _ZTVNSt3__17codecvtIDsc11__mbstate_tEE
+D _ZTVNSt3__17codecvtIDsDu11__mbstate_tEE
+D _ZTVNSt3__17codecvtIwc11__mbstate_tEE
+D _ZTVNSt3__18ios_base7failureE
+D _ZTVNSt3__18ios_baseE
+D _ZTVNSt3__18numpunctIcEE
+D _ZTVNSt3__18numpunctIwEE
+D _ZTVNSt3__19strstreamE
+D _ZTVSt12bad_any_cast
+D _ZTVSt16nested_exception
+D _ZTVSt18bad_variant_access
+D _ZTVSt19bad_optional_access
+D _ZTVSt29__bad_variant_access_with_msg
+R _ZNSt3__110ctype_base5alnumE
+R _ZNSt3__110ctype_base5alphaE
+R _ZNSt3__110ctype_base5blankE
+R _ZNSt3__110ctype_base5cntrlE
+R _ZNSt3__110ctype_base5digitE
+R _ZNSt3__110ctype_base5graphE
+R _ZNSt3__110ctype_base5lowerE
+R _ZNSt3__110ctype_base5printE
+R _ZNSt3__110ctype_base5punctE
+R _ZNSt3__110ctype_base5spaceE
+R _ZNSt3__110ctype_base5upperE
+R _ZNSt3__110ctype_base6xdigitE
+R _ZNSt3__112placeholders2_1E
+R _ZNSt3__112placeholders2_2E
+R _ZNSt3__112placeholders2_3E
+R _ZNSt3__112placeholders2_4E
+R _ZNSt3__112placeholders2_5E
+R _ZNSt3__112placeholders2_6E
+R _ZNSt3__112placeholders2_7E
+R _ZNSt3__112placeholders2_8E
+R _ZNSt3__112placeholders2_9E
+R _ZNSt3__112placeholders3_10E
+R _ZNSt3__114__num_get_base5__srcE
+R _ZNSt3__15ctypeIcE10table_sizeE
+R _ZNSt3__16locale3allE
+R _ZNSt3__16locale4noneE
+R _ZNSt3__16locale4timeE
+R _ZNSt3__16locale5ctypeE
+R _ZNSt3__16locale7collateE
+R _ZNSt3__16locale7numericE
+R _ZNSt3__16locale8messagesE
+R _ZNSt3__16locale8monetaryE
+R _ZNSt3__18ios_base10floatfieldE
+R _ZNSt3__18ios_base10scientificE
+R _ZNSt3__18ios_base11adjustfieldE
+R _ZNSt3__18ios_base2inE
+R _ZNSt3__18ios_base3appE
+R _ZNSt3__18ios_base3ateE
+R _ZNSt3__18ios_base3decE
+R _ZNSt3__18ios_base3hexE
+R _ZNSt3__18ios_base3octE
+R _ZNSt3__18ios_base3outE
+R _ZNSt3__18ios_base4leftE
+R _ZNSt3__18ios_base5fixedE
+R _ZNSt3__18ios_base5rightE
+R _ZNSt3__18ios_base5truncE
+R _ZNSt3__18ios_base6badbitE
+R _ZNSt3__18ios_base6binaryE
+R _ZNSt3__18ios_base6eofbitE
+R _ZNSt3__18ios_base6skipwsE
+R _ZNSt3__18ios_base7failbitE
+R _ZNSt3__18ios_base7goodbitE
+R _ZNSt3__18ios_base7showposE
+R _ZNSt3__18ios_base7unitbufE
+R _ZNSt3__18ios_base8internalE
+R _ZNSt3__18ios_base8showbaseE
+R _ZNSt3__18ios_base9basefieldE
+R _ZNSt3__18ios_base9boolalphaE
+R _ZNSt3__18ios_base9showpointE
+R _ZNSt3__18ios_base9uppercaseE
+R _ZSt7nothrow
+R _ZTSNSt12experimental15fundamentals_v112bad_any_castE
+R _ZTSNSt12experimental19bad_optional_accessE
+R _ZTSNSt3__110istrstreamE
+R _ZTSNSt3__110ostrstreamE
+R _ZTSNSt3__111regex_errorE
+R _ZTSNSt3__112bad_weak_ptrE
+R _ZTSNSt3__112ctype_bynameIcEE
+R _ZTSNSt3__112ctype_bynameIwEE
+R _ZTSNSt3__112future_errorE
+R _ZTSNSt3__112strstreambufE
+R _ZTSNSt3__112system_errorE
+R _ZTSNSt3__114__codecvt_utf8IDiEE
+R _ZTSNSt3__114__codecvt_utf8IDsEE
+R _ZTSNSt3__114__codecvt_utf8IwEE
+R _ZTSNSt3__114collate_bynameIcEE
+R _ZTSNSt3__114collate_bynameIwEE
+R _ZTSNSt3__114error_categoryE
+R _ZTSNSt3__114__shared_countE
+R _ZTSNSt3__115__codecvt_utf16IDiLb0EEE
+R _ZTSNSt3__115__codecvt_utf16IDiLb1EEE
+R _ZTSNSt3__115__codecvt_utf16IDsLb0EEE
+R _ZTSNSt3__115__codecvt_utf16IDsLb1EEE
+R _ZTSNSt3__115__codecvt_utf16IwLb0EEE
+R _ZTSNSt3__115__codecvt_utf16IwLb1EEE
+R _ZTSNSt3__115numpunct_bynameIcEE
+R _ZTSNSt3__115numpunct_bynameIwEE
+R _ZTSNSt3__116__narrow_to_utf8ILm16EEE
+R _ZTSNSt3__116__narrow_to_utf8ILm32EEE
+R _ZTSNSt3__117__assoc_sub_stateE
+R _ZTSNSt3__117bad_function_callE
+R _ZTSNSt3__117__widen_from_utf8ILm16EEE
+R _ZTSNSt3__117__widen_from_utf8ILm32EEE
+R _ZTSNSt3__119bad_expected_accessIvEE
+R _ZTSNSt3__119__shared_weak_countE
+R _ZTSNSt3__120__codecvt_utf8_utf16IDiEE
+R _ZTSNSt3__120__codecvt_utf8_utf16IDsEE
+R _ZTSNSt3__120__codecvt_utf8_utf16IwEE
+R _ZTSNSt3__13pmr15memory_resourceE
+R _ZTSNSt3__13pmr25monotonic_buffer_resourceE
+R _ZTSNSt3__13pmr26synchronized_pool_resourceE
+R _ZTSNSt3__13pmr28unsynchronized_pool_resourceE
+R _ZTSNSt3__14__fs10filesystem16filesystem_errorE
+R _ZTSNSt3__15ctypeIcEE
+R _ZTSNSt3__15ctypeIwEE
+R _ZTSNSt3__16locale5facetE
+R _ZTSNSt3__17codecvtIcc11__mbstate_tEE
+R _ZTSNSt3__17codecvtIDic11__mbstate_tEE
+R _ZTSNSt3__17codecvtIDiDu11__mbstate_tEE
+R _ZTSNSt3__17codecvtIDsc11__mbstate_tEE
+R _ZTSNSt3__17codecvtIDsDu11__mbstate_tEE
+R _ZTSNSt3__17codecvtIwc11__mbstate_tEE
+R _ZTSNSt3__18ios_base7failureE
+R _ZTSNSt3__18ios_baseE
+R _ZTSNSt3__18numpunctIcEE
+R _ZTSNSt3__18numpunctIwEE
+R _ZTSNSt3__19strstreamE
+R _ZTSSt12bad_any_cast
+R _ZTSSt16nested_exception
+R _ZTSSt18bad_variant_access
+R _ZTSSt19bad_optional_access
+R _ZTSSt29__bad_variant_access_with_msg
+T __muloti4
+T _ZNKSt12bad_any_cast4whatEv
+T _ZNKSt12experimental15fundamentals_v112bad_any_cast4whatEv
+T _ZNKSt16nested_exception14rethrow_nestedEv
+T _ZNKSt18bad_variant_access4whatEv
+T _ZNKSt19bad_optional_access4whatEv
+T _ZNKSt29__bad_variant_access_with_msg4whatEv
+T _ZNKSt3__110error_code7messageEv
+T _ZNKSt3__110__time_put8__do_putEPcRS1_PK2tmcc
+T _ZNKSt3__110__time_put8__do_putEPwRS1_PK2tmcc
+T _ZNKSt3__112bad_weak_ptr4whatEv
+T _ZNKSt3__112ctype_bynameIcE10do_tolowerEc
+T _ZNKSt3__112ctype_bynameIcE10do_tolowerEPcPKc
+T _ZNKSt3__112ctype_bynameIcE10do_toupperEc
+T _ZNKSt3__112ctype_bynameIcE10do_toupperEPcPKc
+T _ZNKSt3__112ctype_bynameIwE10do_scan_isEtPKwS3_
+T _ZNKSt3__112ctype_bynameIwE10do_tolowerEPwPKw
+T _ZNKSt3__112ctype_bynameIwE10do_tolowerEw
+T _ZNKSt3__112ctype_bynameIwE10do_toupperEPwPKw
+T _ZNKSt3__112ctype_bynameIwE10do_toupperEw
+T _ZNKSt3__112ctype_bynameIwE11do_scan_notEtPKwS3_
+T _ZNKSt3__112ctype_bynameIwE5do_isEPKwS3_Pt
+T _ZNKSt3__112ctype_bynameIwE5do_isEtw
+T _ZNKSt3__112ctype_bynameIwE8do_widenEc
+T _ZNKSt3__112ctype_bynameIwE8do_widenEPKcS3_Pw
+T _ZNKSt3__112ctype_bynameIwE9do_narrowEPKwS3_cPc
+T _ZNKSt3__112ctype_bynameIwE9do_narrowEwc
+T _ZNKSt3__112strstreambuf6pcountEv
+T _ZNKSt3__113random_device7entropyEv
+T _ZNKSt3__114__codecvt_utf8IDiE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__114__codecvt_utf8IDiE11do_encodingEv
+T _ZNKSt3__114__codecvt_utf8IDiE13do_max_lengthEv
+T _ZNKSt3__114__codecvt_utf8IDiE16do_always_noconvEv
+T _ZNKSt3__114__codecvt_utf8IDiE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T _ZNKSt3__114__codecvt_utf8IDiE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T _ZNKSt3__114__codecvt_utf8IDiE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__114__codecvt_utf8IDsE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__114__codecvt_utf8IDsE11do_encodingEv
+T _ZNKSt3__114__codecvt_utf8IDsE13do_max_lengthEv
+T _ZNKSt3__114__codecvt_utf8IDsE16do_always_noconvEv
+T _ZNKSt3__114__codecvt_utf8IDsE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T _ZNKSt3__114__codecvt_utf8IDsE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T _ZNKSt3__114__codecvt_utf8IDsE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__114__codecvt_utf8IwE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__114__codecvt_utf8IwE11do_encodingEv
+T _ZNKSt3__114__codecvt_utf8IwE13do_max_lengthEv
+T _ZNKSt3__114__codecvt_utf8IwE16do_always_noconvEv
+T _ZNKSt3__114__codecvt_utf8IwE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T _ZNKSt3__114__codecvt_utf8IwE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T _ZNKSt3__114__codecvt_utf8IwE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__114collate_bynameIcE10do_compareEPKcS3_S3_S3_
+T _ZNKSt3__114collate_bynameIcE12do_transformEPKcS3_
+T _ZNKSt3__114collate_bynameIwE10do_compareEPKwS3_S3_S3_
+T _ZNKSt3__114collate_bynameIwE12do_transformEPKwS3_
+T _ZNKSt3__114error_category10equivalentEiRKNS_15error_conditionE
+T _ZNKSt3__114error_category10equivalentERKNS_10error_codeEi
+T _ZNKSt3__114error_category23default_error_conditionEi
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE11do_encodingEv
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE13do_max_lengthEv
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE16do_always_noconvEv
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDiLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE11do_encodingEv
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE13do_max_lengthEv
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE16do_always_noconvEv
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDiLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE11do_encodingEv
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE13do_max_lengthEv
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE16do_always_noconvEv
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDsLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE11do_encodingEv
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE13do_max_lengthEv
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE16do_always_noconvEv
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IDsLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__115__codecvt_utf16IwLb0EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__115__codecvt_utf16IwLb0EE11do_encodingEv
+T _ZNKSt3__115__codecvt_utf16IwLb0EE13do_max_lengthEv
+T _ZNKSt3__115__codecvt_utf16IwLb0EE16do_always_noconvEv
+T _ZNKSt3__115__codecvt_utf16IwLb0EE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IwLb0EE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IwLb0EE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__115__codecvt_utf16IwLb1EE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__115__codecvt_utf16IwLb1EE11do_encodingEv
+T _ZNKSt3__115__codecvt_utf16IwLb1EE13do_max_lengthEv
+T _ZNKSt3__115__codecvt_utf16IwLb1EE16do_always_noconvEv
+T _ZNKSt3__115__codecvt_utf16IwLb1EE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IwLb1EE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T _ZNKSt3__115__codecvt_utf16IwLb1EE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__115error_condition7messageEv
+T _ZNKSt3__117bad_function_call4whatEv
+T _ZNKSt3__118__time_get_storageIcE15__do_date_orderEv
+T _ZNKSt3__118__time_get_storageIwE15__do_date_orderEv
+T _ZNKSt3__119bad_expected_accessIvE4whatEv
+T _ZNKSt3__119__shared_weak_count13__get_deleterERKSt9type_info
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE11do_encodingEv
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE13do_max_lengthEv
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE16do_always_noconvEv
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE5do_inER11__mbstate_tPKcS5_RS5_PDiS7_RS7_
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE6do_outER11__mbstate_tPKDiS5_RS5_PcS7_RS7_
+T _ZNKSt3__120__codecvt_utf8_utf16IDiE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE11do_encodingEv
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE13do_max_lengthEv
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE16do_always_noconvEv
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE5do_inER11__mbstate_tPKcS5_RS5_PDsS7_RS7_
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE6do_outER11__mbstate_tPKDsS5_RS5_PcS7_RS7_
+T _ZNKSt3__120__codecvt_utf8_utf16IDsE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__120__codecvt_utf8_utf16IwE10do_unshiftER11__mbstate_tPcS4_RS4_
+T _ZNKSt3__120__codecvt_utf8_utf16IwE11do_encodingEv
+T _ZNKSt3__120__codecvt_utf8_utf16IwE13do_max_lengthEv
+T _ZNKSt3__120__codecvt_utf8_utf16IwE16do_always_noconvEv
+T _ZNKSt3__120__codecvt_utf8_utf16IwE5do_inER11__mbstate_tPKcS5_RS5_PwS7_RS7_
+T _ZNKSt3__120__codecvt_utf8_utf16IwE6do_outER11__mbstate_tPKwS5_RS5_PcS7_RS7_
+T _ZNKSt3__120__codecvt_utf8_utf16IwE9do_lengthER11__mbstate_tPKcS5_m
+T _ZNKSt3__120__time_get_c_storageIcE3__cEv
+T _ZNKSt3__120__time_get_c_storageIcE3__rEv
+T _ZNKSt3__120__time_get_c_storageIcE3__xEv
+T _ZNKSt3__120__time_get_c_storageIcE3__XEv
+T _ZNKSt3__120__time_get_c_storageIcE7__am_pmEv
+T _ZNKSt3__120__time_get_c_storageIcE7__weeksEv
+T _ZNKSt3__120__time_get_c_storageIcE8__monthsEv
+T _ZNKSt3__120__time_get_c_storageIwE3__cEv
+T _ZNKSt3__120__time_get_c_storageIwE3__rEv
+T _ZNKSt3__120__time_get_c_storageIwE3__xEv
+T _ZNKSt3__120__time_get_c_storageIwE3__XEv
+T _ZNKSt3__120__time_get_c_storageIwE7__am_pmEv
+T _ZNKSt3__120__time_get_c_storageIwE7__weeksEv
+T _ZNKSt3__120__time_get_c_storageIwE8__monthsEv
+T _ZNKSt3__120__vector_base_commonILb1EE20__throw_length_errorEv
+T _ZNKSt3__120__vector_base_commonILb1EE20__throw_out_of_rangeEv
+T _ZNKSt3__121__basic_string_commonILb1EE20__throw_length_errorEv
+T _ZNKSt3__121__basic_string_commonILb1EE20__throw_out_of_rangeEv
+T _ZNKSt3__123__match_any_but_newlineIcE6__execERNS_7__stateIcEE
+T _ZNKSt3__123__match_any_but_newlineIwE6__execERNS_7__stateIwEE
+T _ZNKSt3__13pmr26synchronized_pool_resource11do_is_equalERKNS0_15memory_resourceE
+T _ZNKSt3__13pmr28unsynchronized_pool_resource12__pool_indexEmm
+T _ZNKSt3__13pmr28unsynchronized_pool_resource17__pool_block_sizeEi
+T _ZNKSt3__13pmr28unsynchronized_pool_resource22__log2_pool_block_sizeEi
+T _ZNKSt3__13pmr28unsynchronized_pool_resource7optionsEv
+T _ZNKSt3__14__fs10filesystem18directory_iterator13__dereferenceEv
+T _ZNKSt3__14__fs10filesystem28recursive_directory_iterator13__dereferenceEv
+T _ZNKSt3__14__fs10filesystem28recursive_directory_iterator5depthEv
+T _ZNKSt3__14__fs10filesystem28recursive_directory_iterator7optionsEv
+T _ZNKSt3__14__fs10filesystem4path10__filenameEv
+T _ZNKSt3__14__fs10filesystem4path11__extensionEv
+T _ZNKSt3__14__fs10filesystem4path11__root_nameEv
+T _ZNKSt3__14__fs10filesystem4path13__parent_pathEv
+T _ZNKSt3__14__fs10filesystem4path15__relative_pathEv
+T _ZNKSt3__14__fs10filesystem4path15__root_path_rawEv
+T _ZNKSt3__14__fs10filesystem4path16lexically_normalEv
+T _ZNKSt3__14__fs10filesystem4path16__root_directoryEv
+T _ZNKSt3__14__fs10filesystem4path18lexically_relativeERKS2_
+T _ZNKSt3__14__fs10filesystem4path3endEv
+T _ZNKSt3__14__fs10filesystem4path5beginEv
+T _ZNKSt3__14__fs10filesystem4path6__stemEv
+T _ZNKSt3__14__fs10filesystem4path9__compareENS_17basic_string_viewIcNS_11char_traitsIcEEEE
+T _ZNKSt3__15ctypeIcE10do_tolowerEc
+T _ZNKSt3__15ctypeIcE10do_tolowerEPcPKc
+T _ZNKSt3__15ctypeIcE10do_toupperEc
+T _ZNKSt3__15ctypeIcE10do_toupperEPcPKc
+T _ZNKSt3__15ctypeIcE8do_widenEc
+T _ZNKSt3__15ctypeIcE8do_widenEPKcS3_Pc
+T _ZNKSt3__15ctypeIcE9do_narrowEcc
+T _ZNKSt3__15ctypeIcE9do_narrowEPKcS3_cPc
+T _ZNKSt3__15ctypeIwE10do_scan_isEtPKwS3_
+T _ZNKSt3__15ctypeIwE10do_tolowerEPwPKw
+T _ZNKSt3__15ctypeIwE10do_tolowerEw
+T _ZNKSt3__15ctypeIwE10do_toupperEPwPKw
+T _ZNKSt3__15ctypeIwE10do_toupperEw
+T _ZNKSt3__15ctypeIwE11do_scan_notEtPKwS3_
+T _ZNKSt3__15ctypeIwE5do_isEPKwS3_Pt
+T _ZNKSt3__15ctypeIwE5do_isEtw
+T _ZNKSt3__15ctypeIwE8do_widenEc
+T _ZNKSt3__15ctypeIwE8do_widenEPKcS3_Pw
+T _ZNKSt3__15ctypeIwE9do_narrowEPKwS3_cPc
+T _ZNKSt3__15ctypeIwE9do_narrowEwc
+T _ZNKSt3__16locale4nameEv
+T _ZNKSt3__16locale9has_facetERNS0_2idE
+T _ZNKSt3__16locale9use_facetERNS0_2idE
+T _ZNKSt3__16localeeqERKS0_
+T _ZNKSt3__17codecvtIcc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T _ZNKSt3__17codecvtIcc11__mbstate_tE11do_encodingEv
+T _ZNKSt3__17codecvtIcc11__mbstate_tE13do_max_lengthEv
+T _ZNKSt3__17codecvtIcc11__mbstate_tE16do_always_noconvEv
+T _ZNKSt3__17codecvtIcc11__mbstate_tE5do_inERS1_PKcS5_RS5_PcS7_RS7_
+T _ZNKSt3__17codecvtIcc11__mbstate_tE6do_outERS1_PKcS5_RS5_PcS7_RS7_
+T _ZNKSt3__17codecvtIcc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T _ZNKSt3__17codecvtIDic11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T _ZNKSt3__17codecvtIDic11__mbstate_tE11do_encodingEv
+T _ZNKSt3__17codecvtIDic11__mbstate_tE13do_max_lengthEv
+T _ZNKSt3__17codecvtIDic11__mbstate_tE16do_always_noconvEv
+T _ZNKSt3__17codecvtIDic11__mbstate_tE5do_inERS1_PKcS5_RS5_PDiS7_RS7_
+T _ZNKSt3__17codecvtIDic11__mbstate_tE6do_outERS1_PKDiS5_RS5_PcS7_RS7_
+T _ZNKSt3__17codecvtIDic11__mbstate_tE9do_lengthERS1_PKcS5_m
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE10do_unshiftERS1_PDuS4_RS4_
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE11do_encodingEv
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE13do_max_lengthEv
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE16do_always_noconvEv
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE5do_inERS1_PKDuS5_RS5_PDiS7_RS7_
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE6do_outERS1_PKDiS5_RS5_PDuS7_RS7_
+T _ZNKSt3__17codecvtIDiDu11__mbstate_tE9do_lengthERS1_PKDuS5_m
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE11do_encodingEv
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE13do_max_lengthEv
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE16do_always_noconvEv
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE5do_inERS1_PKcS5_RS5_PDsS7_RS7_
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE6do_outERS1_PKDsS5_RS5_PcS7_RS7_
+T _ZNKSt3__17codecvtIDsc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE10do_unshiftERS1_PDuS4_RS4_
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE11do_encodingEv
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE13do_max_lengthEv
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE16do_always_noconvEv
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE5do_inERS1_PKDuS5_RS5_PDsS7_RS7_
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE6do_outERS1_PKDsS5_RS5_PDuS7_RS7_
+T _ZNKSt3__17codecvtIDsDu11__mbstate_tE9do_lengthERS1_PKDuS5_m
+T _ZNKSt3__17codecvtIwc11__mbstate_tE10do_unshiftERS1_PcS4_RS4_
+T _ZNKSt3__17codecvtIwc11__mbstate_tE11do_encodingEv
+T _ZNKSt3__17codecvtIwc11__mbstate_tE13do_max_lengthEv
+T _ZNKSt3__17codecvtIwc11__mbstate_tE16do_always_noconvEv
+T _ZNKSt3__17codecvtIwc11__mbstate_tE5do_inERS1_PKcS5_RS5_PwS7_RS7_
+T _ZNKSt3__17codecvtIwc11__mbstate_tE6do_outERS1_PKwS5_RS5_PcS7_RS7_
+T _ZNKSt3__17codecvtIwc11__mbstate_tE9do_lengthERS1_PKcS5_m
+T _ZNKSt3__18ios_base6getlocEv
+T _ZNKSt3__18numpunctIcE11do_groupingEv
+T _ZNKSt3__18numpunctIcE11do_truenameEv
+T _ZNKSt3__18numpunctIcE12do_falsenameEv
+T _ZNKSt3__18numpunctIcE16do_decimal_pointEv
+T _ZNKSt3__18numpunctIcE16do_thousands_sepEv
+T _ZNKSt3__18numpunctIwE11do_groupingEv
+T _ZNKSt3__18numpunctIwE11do_truenameEv
+T _ZNKSt3__18numpunctIwE12do_falsenameEv
+T _ZNKSt3__18numpunctIwE16do_decimal_pointEv
+T _ZNKSt3__18numpunctIwE16do_thousands_sepEv
+T _ZNSt11logic_erroraSERKS_
+T _ZNSt11logic_errorC1EPKc
+T _ZNSt11logic_errorC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T _ZNSt11logic_errorC1ERKS_
+T _ZNSt11logic_errorC2EPKc
+T _ZNSt11logic_errorC2ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T _ZNSt11logic_errorC2ERKS_
+T _ZNSt12experimental19bad_optional_accessD0Ev
+T _ZNSt12experimental19bad_optional_accessD1Ev
+T _ZNSt12experimental19bad_optional_accessD2Ev
+T _ZNSt13exception_ptr31__from_native_exception_pointerEPv
+T _ZNSt13exception_ptraSERKS_
+T _ZNSt13exception_ptrC1ERKS_
+T _ZNSt13exception_ptrC2ERKS_
+T _ZNSt13exception_ptrD1Ev
+T _ZNSt13exception_ptrD2Ev
+T _ZNSt13runtime_erroraSERKS_
+T _ZNSt13runtime_errorC1EPKc
+T _ZNSt13runtime_errorC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T _ZNSt13runtime_errorC1ERKS_
+T _ZNSt13runtime_errorC2EPKc
+T _ZNSt13runtime_errorC2ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
+T _ZNSt13runtime_errorC2ERKS_
+T _ZNSt16nested_exceptionC1Ev
+T _ZNSt16nested_exceptionC2Ev
+T _ZNSt16nested_exceptionD0Ev
+T _ZNSt16nested_exceptionD1Ev
+T _ZNSt16nested_exceptionD2Ev
+T _ZNSt19bad_optional_accessD0Ev
+T _ZNSt19bad_optional_accessD1Ev
+T _ZNSt19bad_optional_accessD2Ev
+T _ZNSt3__110istrstreamD0Ev
+T _ZNSt3__110istrstreamD1Ev
+T _ZNSt3__110istrstreamD2Ev
+T _ZNSt3__110ostrstreamD0Ev
+T _ZNSt3__110ostrstreamD1Ev
+T _ZNSt3__110ostrstreamD2Ev
+T _ZNSt3__110__time_getC1EPKc
+T _ZNSt3__110__time_getC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__110__time_getC2EPKc
+T _ZNSt3__110__time_getC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__110__time_getD1Ev
+T _ZNSt3__110__time_getD2Ev
+T _ZNSt3__110__time_putC1EPKc
+T _ZNSt3__110__time_putC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__110__time_putC2EPKc
+T _ZNSt3__110__time_putC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__110__time_putD1Ev
+T _ZNSt3__110__time_putD2Ev
+T _ZNSt3__110to_wstringEd
+T _ZNSt3__110to_wstringEe
+T _ZNSt3__110to_wstringEf
+T _ZNSt3__110to_wstringEi
+T _ZNSt3__110to_wstringEj
+T _ZNSt3__110to_wstringEl
+T _ZNSt3__110to_wstringEm
+T _ZNSt3__110to_wstringEx
+T _ZNSt3__110to_wstringEy
+T _ZNSt3__111__call_onceERVmPvPFvS2_E
+T _ZNSt3__111regex_errorC1ENS_15regex_constants10error_typeE
+T _ZNSt3__111regex_errorC2ENS_15regex_constants10error_typeE
+T _ZNSt3__111regex_errorD0Ev
+T _ZNSt3__111regex_errorD1Ev
+T _ZNSt3__111regex_errorD2Ev
+T _ZNSt3__111this_thread9sleep_forERKNS_6chrono8durationIxNS_5ratioILl1ELl1000000000EEEEE
+T _ZNSt3__111timed_mutex4lockEv
+T _ZNSt3__111timed_mutex6unlockEv
+T _ZNSt3__111timed_mutex8try_lockEv
+T _ZNSt3__111timed_mutexC1Ev
+T _ZNSt3__111timed_mutexC2Ev
+T _ZNSt3__111timed_mutexD1Ev
+T _ZNSt3__111timed_mutexD2Ev
+T _ZNSt3__112bad_weak_ptrD0Ev
+T _ZNSt3__112bad_weak_ptrD1Ev
+T _ZNSt3__112bad_weak_ptrD2Ev
+T _ZNSt3__112ctype_bynameIcEC1EPKcm
+T _ZNSt3__112ctype_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__112ctype_bynameIcEC2EPKcm
+T _ZNSt3__112ctype_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__112ctype_bynameIcED0Ev
+T _ZNSt3__112ctype_bynameIcED1Ev
+T _ZNSt3__112ctype_bynameIcED2Ev
+T _ZNSt3__112ctype_bynameIwEC1EPKcm
+T _ZNSt3__112ctype_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__112ctype_bynameIwEC2EPKcm
+T _ZNSt3__112ctype_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__112ctype_bynameIwED0Ev
+T _ZNSt3__112ctype_bynameIwED1Ev
+T _ZNSt3__112ctype_bynameIwED2Ev
+T _ZNSt3__112__do_nothingEPv
+T _ZNSt3__112future_errorC1ENS_10error_codeE
+T _ZNSt3__112future_errorC2ENS_10error_codeE
+T _ZNSt3__112future_errorD0Ev
+T _ZNSt3__112future_errorD1Ev
+T _ZNSt3__112future_errorD2Ev
+T _ZNSt3__112__get_sp_mutEPKv
+T _ZNSt3__112__next_primeEm
+T _ZNSt3__112__rs_defaultC1ERKS0_
+T _ZNSt3__112__rs_defaultC1Ev
+T _ZNSt3__112__rs_defaultC2ERKS0_
+T _ZNSt3__112__rs_defaultC2Ev
+T _ZNSt3__112__rs_defaultclEv
+T _ZNSt3__112__rs_defaultD1Ev
+T _ZNSt3__112__rs_defaultD2Ev
+T _ZNSt3__112strstreambuf3strEv
+T _ZNSt3__112strstreambuf4swapERS0_
+T _ZNSt3__112strstreambuf6freezeEb
+T _ZNSt3__112strstreambuf6__initEPclS1_
+T _ZNSt3__112strstreambuf7seekoffExNS_8ios_base7seekdirEj
+T _ZNSt3__112strstreambuf7seekposENS_4fposI11__mbstate_tEEj
+T _ZNSt3__112strstreambuf8overflowEi
+T _ZNSt3__112strstreambuf9pbackfailEi
+T _ZNSt3__112strstreambuf9underflowEv
+T _ZNSt3__112strstreambufC1El
+T _ZNSt3__112strstreambufC1EPalS1_
+T _ZNSt3__112strstreambufC1EPclS1_
+T _ZNSt3__112strstreambufC1EPFPvmEPFvS1_E
+T _ZNSt3__112strstreambufC1EPhlS1_
+T _ZNSt3__112strstreambufC1EPKal
+T _ZNSt3__112strstreambufC1EPKcl
+T _ZNSt3__112strstreambufC1EPKhl
+T _ZNSt3__112strstreambufC2El
+T _ZNSt3__112strstreambufC2EPalS1_
+T _ZNSt3__112strstreambufC2EPclS1_
+T _ZNSt3__112strstreambufC2EPFPvmEPFvS1_E
+T _ZNSt3__112strstreambufC2EPhlS1_
+T _ZNSt3__112strstreambufC2EPKal
+T _ZNSt3__112strstreambufC2EPKcl
+T _ZNSt3__112strstreambufC2EPKhl
+T _ZNSt3__112strstreambufD0Ev
+T _ZNSt3__112strstreambufD1Ev
+T _ZNSt3__112strstreambufD2Ev
+T _ZNSt3__112system_errorC1EiRKNS_14error_categoryE
+T _ZNSt3__112system_errorC1EiRKNS_14error_categoryEPKc
+T _ZNSt3__112system_errorC1EiRKNS_14error_categoryERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__112system_errorC1ENS_10error_codeE
+T _ZNSt3__112system_errorC1ENS_10error_codeEPKc
+T _ZNSt3__112system_errorC1ENS_10error_codeERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__112system_errorC2EiRKNS_14error_categoryE
+T _ZNSt3__112system_errorC2EiRKNS_14error_categoryEPKc
+T _ZNSt3__112system_errorC2EiRKNS_14error_categoryERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__112system_errorC2ENS_10error_codeE
+T _ZNSt3__112system_errorC2ENS_10error_codeEPKc
+T _ZNSt3__112system_errorC2ENS_10error_codeERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__112system_errorD0Ev
+T _ZNSt3__112system_errorD1Ev
+T _ZNSt3__112system_errorD2Ev
+T _ZNSt3__113__hash_memoryEPKvm
+T _ZNSt3__113random_deviceC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__113random_deviceC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__113random_deviceclEv
+T _ZNSt3__113random_deviceD1Ev
+T _ZNSt3__113random_deviceD2Ev
+T _ZNSt3__113shared_futureIvEaSERKS1_
+T _ZNSt3__113shared_futureIvED1Ev
+T _ZNSt3__113shared_futureIvED2Ev
+T _ZNSt3__114collate_bynameIcEC1EPKcm
+T _ZNSt3__114collate_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__114collate_bynameIcEC2EPKcm
+T _ZNSt3__114collate_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__114collate_bynameIcED0Ev
+T _ZNSt3__114collate_bynameIcED1Ev
+T _ZNSt3__114collate_bynameIcED2Ev
+T _ZNSt3__114collate_bynameIwEC1EPKcm
+T _ZNSt3__114collate_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__114collate_bynameIwEC2EPKcm
+T _ZNSt3__114collate_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__114collate_bynameIwED0Ev
+T _ZNSt3__114collate_bynameIwED1Ev
+T _ZNSt3__114collate_bynameIwED2Ev
+T _ZNSt3__114error_categoryC2Ev
+T _ZNSt3__114error_categoryD0Ev
+T _ZNSt3__114error_categoryD1Ev
+T _ZNSt3__114error_categoryD2Ev
+T _ZNSt3__114__num_get_base10__get_baseERNS_8ios_baseE
+T _ZNSt3__114__num_put_base12__format_intEPcPKcbj
+T _ZNSt3__114__num_put_base14__format_floatEPcPKcj
+T _ZNSt3__114__num_put_base18__identify_paddingEPcS1_RKNS_8ios_baseE
+T _ZNSt3__114__shared_count12__add_sharedEv
+T _ZNSt3__114__shared_count16__release_sharedEv
+T _ZNSt3__114__shared_countD0Ev
+T _ZNSt3__114__shared_countD1Ev
+T _ZNSt3__114__shared_countD2Ev
+T _ZNSt3__115future_categoryEv
+T _ZNSt3__115__get_classnameEPKcb
+T _ZNSt3__115numpunct_bynameIcEC1EPKcm
+T _ZNSt3__115numpunct_bynameIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__115numpunct_bynameIcEC2EPKcm
+T _ZNSt3__115numpunct_bynameIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__115numpunct_bynameIcED0Ev
+T _ZNSt3__115numpunct_bynameIcED1Ev
+T _ZNSt3__115numpunct_bynameIcED2Ev
+T _ZNSt3__115numpunct_bynameIwEC1EPKcm
+T _ZNSt3__115numpunct_bynameIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__115numpunct_bynameIwEC2EPKcm
+T _ZNSt3__115numpunct_bynameIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEm
+T _ZNSt3__115numpunct_bynameIwED0Ev
+T _ZNSt3__115numpunct_bynameIwED1Ev
+T _ZNSt3__115numpunct_bynameIwED2Ev
+T _ZNSt3__115recursive_mutex4lockEv
+T _ZNSt3__115recursive_mutex6unlockEv
+T _ZNSt3__115recursive_mutex8try_lockEv
+T _ZNSt3__115recursive_mutexC1Ev
+T _ZNSt3__115recursive_mutexC2Ev
+T _ZNSt3__115recursive_mutexD1Ev
+T _ZNSt3__115recursive_mutexD2Ev
+T _ZNSt3__115system_categoryEv
+T _ZNSt3__115__thread_struct25notify_all_at_thread_exitEPNS_18condition_variableEPNS_5mutexE
+T _ZNSt3__115__thread_struct27__make_ready_at_thread_exitEPNS_17__assoc_sub_stateE
+T _ZNSt3__115__thread_structC1Ev
+T _ZNSt3__115__thread_structC2Ev
+T _ZNSt3__115__thread_structD1Ev
+T _ZNSt3__115__thread_structD2Ev
+T _ZNSt3__116__check_groupingERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjS8_Rj
+T _ZNSt3__116generic_categoryEv
+T _ZNSt3__116__narrow_to_utf8ILm16EED0Ev
+T _ZNSt3__116__narrow_to_utf8ILm16EED1Ev
+T _ZNSt3__116__narrow_to_utf8ILm16EED2Ev
+T _ZNSt3__116__narrow_to_utf8ILm32EED0Ev
+T _ZNSt3__116__narrow_to_utf8ILm32EED1Ev
+T _ZNSt3__116__narrow_to_utf8ILm32EED2Ev
+T _ZNSt3__117__assoc_sub_state10__sub_waitERNS_11unique_lockINS_5mutexEEE
+T _ZNSt3__117__assoc_sub_state12__make_readyEv
+T _ZNSt3__117__assoc_sub_state13set_exceptionESt13exception_ptr
+T _ZNSt3__117__assoc_sub_state16__on_zero_sharedEv
+T _ZNSt3__117__assoc_sub_state24set_value_at_thread_exitEv
+T _ZNSt3__117__assoc_sub_state28set_exception_at_thread_exitESt13exception_ptr
+T _ZNSt3__117__assoc_sub_state4copyEv
+T _ZNSt3__117__assoc_sub_state4waitEv
+T _ZNSt3__117__assoc_sub_state9__executeEv
+T _ZNSt3__117__assoc_sub_state9set_valueEv
+T _ZNSt3__117bad_function_callD0Ev
+T _ZNSt3__117bad_function_callD1Ev
+T _ZNSt3__117bad_function_callD2Ev
+T _ZNSt3__117iostream_categoryEv
+T _ZNSt3__117moneypunct_bynameIcLb0EE4initEPKc
+T _ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc
+T _ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc
+T _ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc
+T _ZNSt3__117__widen_from_utf8ILm16EED0Ev
+T _ZNSt3__117__widen_from_utf8ILm16EED1Ev
+T _ZNSt3__117__widen_from_utf8ILm16EED2Ev
+T _ZNSt3__117__widen_from_utf8ILm32EED0Ev
+T _ZNSt3__117__widen_from_utf8ILm32EED1Ev
+T _ZNSt3__117__widen_from_utf8ILm32EED2Ev
+T _ZNSt3__118condition_variable10notify_allEv
+T _ZNSt3__118condition_variable10notify_oneEv
+T _ZNSt3__118condition_variable15__do_timed_waitERNS_11unique_lockINS_5mutexEEENS_6chrono10time_pointINS5_12system_clockENS5_8durationIxNS_5ratioILl1ELl1000000000EEEEEEE
+T _ZNSt3__118condition_variable4waitERNS_11unique_lockINS_5mutexEEE
+T _ZNSt3__118condition_variableD1Ev
+T _ZNSt3__118condition_variableD2Ev
+T _ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE
+T _ZNSt3__118shared_timed_mutex11lock_sharedEv
+T _ZNSt3__118shared_timed_mutex13unlock_sharedEv
+T _ZNSt3__118shared_timed_mutex15try_lock_sharedEv
+T _ZNSt3__118shared_timed_mutex4lockEv
+T _ZNSt3__118shared_timed_mutex6unlockEv
+T _ZNSt3__118shared_timed_mutex8try_lockEv
+T _ZNSt3__118shared_timed_mutexC1Ev
+T _ZNSt3__118shared_timed_mutexC2Ev
+T _ZNSt3__118__time_get_storageIcEC1EPKc
+T _ZNSt3__118__time_get_storageIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__118__time_get_storageIcEC2EPKc
+T _ZNSt3__118__time_get_storageIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__118__time_get_storageIwEC1EPKc
+T _ZNSt3__118__time_get_storageIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__118__time_get_storageIwEC2EPKc
+T _ZNSt3__118__time_get_storageIwEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__119__is_posix_terminalEP8_IO_FILE
+T _ZNSt3__119__shared_mutex_base11lock_sharedEv
+T _ZNSt3__119__shared_mutex_base13unlock_sharedEv
+T _ZNSt3__119__shared_mutex_base15try_lock_sharedEv
+T _ZNSt3__119__shared_mutex_base4lockEv
+T _ZNSt3__119__shared_mutex_base6unlockEv
+T _ZNSt3__119__shared_mutex_base8try_lockEv
+T _ZNSt3__119__shared_mutex_baseC1Ev
+T _ZNSt3__119__shared_mutex_baseC2Ev
+T _ZNSt3__119__shared_weak_count10__add_weakEv
+T _ZNSt3__119__shared_weak_count12__add_sharedEv
+T _ZNSt3__119__shared_weak_count14__release_weakEv
+T _ZNSt3__119__shared_weak_count16__release_sharedEv
+T _ZNSt3__119__shared_weak_count4lockEv
+T _ZNSt3__119__shared_weak_countD0Ev
+T _ZNSt3__119__shared_weak_countD1Ev
+T _ZNSt3__119__shared_weak_countD2Ev
+T _ZNSt3__119__thread_local_dataEv
+T _ZNSt3__120__get_collation_nameEPKc
+T _ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEEi
+T _ZNSt3__120__libcpp_atomic_waitEPVKvi
+T _ZNSt3__120__throw_system_errorEiPKc
+T _ZNSt3__121__get_locale_encodingEPKc
+T _ZNSt3__121recursive_timed_mutex4lockEv
+T _ZNSt3__121recursive_timed_mutex6unlockEv
+T _ZNSt3__121recursive_timed_mutex8try_lockEv
+T _ZNSt3__121recursive_timed_mutexC1Ev
+T _ZNSt3__121recursive_timed_mutexC2Ev
+T _ZNSt3__121recursive_timed_mutexD1Ev
+T _ZNSt3__121recursive_timed_mutexD2Ev
+T _ZNSt3__121__throw_runtime_errorEPKc
+T _ZNSt3__123__atomic_monitor_globalEPKv
+T _ZNSt3__123__cxx_atomic_notify_allEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE
+T _ZNSt3__123__cxx_atomic_notify_allEPVKv
+T _ZNSt3__123__cxx_atomic_notify_oneEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE
+T _ZNSt3__123__cxx_atomic_notify_oneEPVKv
+T _ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE
+T _ZNSt3__123__libcpp_atomic_monitorEPVKv
+T _ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE
+T _ZNSt3__126__atomic_wait_global_tableEPKvi
+T _ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh
+T _ZNSt3__132__atomic_notify_all_global_tableEPKv
+T _ZNSt3__132__atomic_notify_one_global_tableEPKv
+T _ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE
+T _ZNSt3__134__construct_barrier_algorithm_baseERl
+T _ZNSt3__139__atomic_wait_global_table_with_timeoutEPKvim
+T _ZNSt3__13pmr15memory_resourceD0Ev
+T _ZNSt3__13pmr15memory_resourceD1Ev
+T _ZNSt3__13pmr15memory_resourceD2Ev
+T _ZNSt3__13pmr19new_delete_resourceEv
+T _ZNSt3__13pmr20get_default_resourceEv
+T _ZNSt3__13pmr20null_memory_resourceEv
+T _ZNSt3__13pmr20set_default_resourceEPNS0_15memory_resourceE
+T _ZNSt3__13pmr25monotonic_buffer_resource11do_allocateEmm
+T _ZNSt3__13pmr28unsynchronized_pool_resource11do_allocateEmm
+T _ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__do_allocateEPNS0_15memory_resourceEmm
+T _ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__release_ptrEPNS0_15memory_resourceE
+T _ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool15__do_deallocateEPNS0_15memory_resourceEPvmm
+T _ZNSt3__13pmr28unsynchronized_pool_resource13do_deallocateEPvmm
+T _ZNSt3__13pmr28unsynchronized_pool_resource7releaseEv
+T _ZNSt3__13pmr28unsynchronized_pool_resourceC1ERKNS0_12pool_optionsEPNS0_15memory_resourceE
+T _ZNSt3__13pmr28unsynchronized_pool_resourceC2ERKNS0_12pool_optionsEPNS0_15memory_resourceE
+T _ZNSt3__14__fs10filesystem10__absoluteERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem10hash_valueERKNS1_4pathE
+T _ZNSt3__14__fs10filesystem11__canonicalERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem11__copy_fileERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem11__file_sizeERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem12__equivalentERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem12__remove_allERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem13__fs_is_emptyERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem13__permissionsERKNS1_4pathENS1_5permsENS1_12perm_optionsEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem13__resize_fileERKNS1_4pathEmPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem14__copy_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem14__current_pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem14__current_pathERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem14__read_symlinkERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem15directory_entry12__do_refreshEv
+T _ZNSt3__14__fs10filesystem16__create_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem16_FilesystemClock3nowEv
+T _ZNSt3__14__fs10filesystem16filesystem_error13__create_whatEi
+T _ZNSt3__14__fs10filesystem16filesystem_errorD0Ev
+T _ZNSt3__14__fs10filesystem16filesystem_errorD1Ev
+T _ZNSt3__14__fs10filesystem16filesystem_errorD2Ev
+T _ZNSt3__14__fs10filesystem16__symlink_statusERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem17__hard_link_countERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem17__last_write_timeERKNS1_4pathENS_6chrono10time_pointINS1_16_FilesystemClockENS5_8durationInNS_5ratioILl1ELl1000000000EEEEEEEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem17__last_write_timeERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem18__create_directoryERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem18__create_directoryERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem18__create_hard_linkERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem18directory_iterator11__incrementEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem18directory_iteratorC1ERKNS1_4pathEPNS_10error_codeENS1_17directory_optionsE
+T _ZNSt3__14__fs10filesystem18directory_iteratorC2ERKNS1_4pathEPNS_10error_codeENS1_17directory_optionsE
+T _ZNSt3__14__fs10filesystem18__weakly_canonicalERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem20__create_directoriesERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem21__temp_directory_pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem26__create_directory_symlinkERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem28recursive_directory_iterator11__incrementEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem28recursive_directory_iterator15__try_recursionEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem28recursive_directory_iterator5__popEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem28recursive_directory_iterator9__advanceEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem28recursive_directory_iteratorC1ERKNS1_4pathENS1_17directory_optionsEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem28recursive_directory_iteratorC2ERKNS1_4pathENS1_17directory_optionsEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem4path17replace_extensionERKS2_
+T _ZNSt3__14__fs10filesystem4path8iterator11__decrementEv
+T _ZNSt3__14__fs10filesystem4path8iterator11__incrementEv
+T _ZNSt3__14__fs10filesystem6__copyERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem7__spaceERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE
+T _ZNSt3__14__fs10filesystem8__statusERKNS1_4pathEPNS_10error_codeE
+T _ZNSt3__14stodERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T _ZNSt3__14stodERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T _ZNSt3__14stofERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T _ZNSt3__14stofERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T _ZNSt3__14stoiERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T _ZNSt3__14stoiERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T _ZNSt3__14stolERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T _ZNSt3__14stolERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T _ZNSt3__15alignEmmRPvRm
+T _ZNSt3__15ctypeIcE13classic_tableEv
+T _ZNSt3__15ctypeIcEC1EPKtbm
+T _ZNSt3__15ctypeIcEC2EPKtbm
+T _ZNSt3__15ctypeIcED0Ev
+T _ZNSt3__15ctypeIcED1Ev
+T _ZNSt3__15ctypeIcED2Ev
+T _ZNSt3__15ctypeIwED0Ev
+T _ZNSt3__15ctypeIwED1Ev
+T _ZNSt3__15ctypeIwED2Ev
+T _ZNSt3__15mutex4lockEv
+T _ZNSt3__15mutex6unlockEv
+T _ZNSt3__15mutex8try_lockEv
+T _ZNSt3__15mutexD1Ev
+T _ZNSt3__15mutexD2Ev
+T _ZNSt3__15stoldERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm
+T _ZNSt3__15stoldERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm
+T _ZNSt3__15stollERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T _ZNSt3__15stollERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T _ZNSt3__15stoulERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T _ZNSt3__15stoulERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T _ZNSt3__16chrono12steady_clock3nowEv
+T _ZNSt3__16chrono12system_clock11from_time_tEl
+T _ZNSt3__16chrono12system_clock3nowEv
+T _ZNSt3__16chrono12system_clock9to_time_tERKNS0_10time_pointIS1_NS0_8durationIxNS_5ratioILl1ELl1000000EEEEEEE
+T _ZNSt3__16__clocEv
+T _ZNSt3__16futureIvE3getEv
+T _ZNSt3__16futureIvEC1EPNS_17__assoc_sub_stateE
+T _ZNSt3__16futureIvEC2EPNS_17__assoc_sub_stateE
+T _ZNSt3__16futureIvED1Ev
+T _ZNSt3__16futureIvED2Ev
+T _ZNSt3__16gslice6__initEm
+T _ZNSt3__16__itoa8__u32toaEjPc
+T _ZNSt3__16__itoa8__u64toaEmPc
+T _ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl
+T _ZNSt3__16locale2id5__getEv
+T _ZNSt3__16locale5facet16__on_zero_sharedEv
+T _ZNSt3__16locale5facetD0Ev
+T _ZNSt3__16locale5facetD1Ev
+T _ZNSt3__16locale5facetD2Ev
+T _ZNSt3__16locale6globalERKS0_
+T _ZNSt3__16locale7classicEv
+T _ZNSt3__16locale8__globalEv
+T _ZNSt3__16localeaSERKS0_
+T _ZNSt3__16localeC1EPKc
+T _ZNSt3__16localeC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__16localeC1ERKS0_
+T _ZNSt3__16localeC1ERKS0_PKci
+T _ZNSt3__16localeC1ERKS0_RKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEi
+T _ZNSt3__16localeC1ERKS0_S2_i
+T _ZNSt3__16localeC1Ev
+T _ZNSt3__16localeC2EPKc
+T _ZNSt3__16localeC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+T _ZNSt3__16localeC2ERKS0_
+T _ZNSt3__16localeC2ERKS0_PKci
+T _ZNSt3__16localeC2ERKS0_RKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEi
+T _ZNSt3__16localeC2ERKS0_S2_i
+T _ZNSt3__16localeC2Ev
+T _ZNSt3__16localeD1Ev
+T _ZNSt3__16localeD2Ev
+T _ZNSt3__16stoullERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi
+T _ZNSt3__16stoullERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi
+T _ZNSt3__16thread20hardware_concurrencyEv
+T _ZNSt3__16thread4joinEv
+T _ZNSt3__16thread6detachEv
+T _ZNSt3__16threadD1Ev
+T _ZNSt3__16threadD2Ev
+T _ZNSt3__17codecvtIcc11__mbstate_tED0Ev
+T _ZNSt3__17codecvtIcc11__mbstate_tED1Ev
+T _ZNSt3__17codecvtIcc11__mbstate_tED2Ev
+T _ZNSt3__17codecvtIDic11__mbstate_tED0Ev
+T _ZNSt3__17codecvtIDic11__mbstate_tED1Ev
+T _ZNSt3__17codecvtIDic11__mbstate_tED2Ev
+T _ZNSt3__17codecvtIDiDu11__mbstate_tED0Ev
+T _ZNSt3__17codecvtIDiDu11__mbstate_tED1Ev
+T _ZNSt3__17codecvtIDiDu11__mbstate_tED2Ev
+T _ZNSt3__17codecvtIDsc11__mbstate_tED0Ev
+T _ZNSt3__17codecvtIDsc11__mbstate_tED1Ev
+T _ZNSt3__17codecvtIDsc11__mbstate_tED2Ev
+T _ZNSt3__17codecvtIDsDu11__mbstate_tED0Ev
+T _ZNSt3__17codecvtIDsDu11__mbstate_tED1Ev
+T _ZNSt3__17codecvtIDsDu11__mbstate_tED2Ev
+T _ZNSt3__17codecvtIwc11__mbstate_tEC1Em
+T _ZNSt3__17codecvtIwc11__mbstate_tEC1EPKcm
+T _ZNSt3__17codecvtIwc11__mbstate_tEC2Em
+T _ZNSt3__17codecvtIwc11__mbstate_tEC2EPKcm
+T _ZNSt3__17codecvtIwc11__mbstate_tED0Ev
+T _ZNSt3__17codecvtIwc11__mbstate_tED1Ev
+T _ZNSt3__17codecvtIwc11__mbstate_tED2Ev
+T _ZNSt3__17promiseIvE10get_futureEv
+T _ZNSt3__17promiseIvE13set_exceptionESt13exception_ptr
+T _ZNSt3__17promiseIvE24set_value_at_thread_exitEv
+T _ZNSt3__17promiseIvE28set_exception_at_thread_exitESt13exception_ptr
+T _ZNSt3__17promiseIvE9set_valueEv
+T _ZNSt3__17promiseIvEC1Ev
+T _ZNSt3__17promiseIvEC2Ev
+T _ZNSt3__17promiseIvED1Ev
+T _ZNSt3__17promiseIvED2Ev
+T _ZNSt3__18ios_base15sync_with_stdioEb
+T _ZNSt3__18ios_base16__call_callbacksENS0_5eventE
+T _ZNSt3__18ios_base17register_callbackEPFvNS0_5eventERS0_iEi
+T _ZNSt3__18ios_base33__set_badbit_and_consider_rethrowEv
+T _ZNSt3__18ios_base34__set_failbit_and_consider_rethrowEv
+T _ZNSt3__18ios_base4InitC1Ev
+T _ZNSt3__18ios_base4InitC2Ev
+T _ZNSt3__18ios_base4InitD1Ev
+T _ZNSt3__18ios_base4InitD2Ev
+T _ZNSt3__18ios_base4initEPv
+T _ZNSt3__18ios_base4moveERS0_
+T _ZNSt3__18ios_base4swapERS0_
+T _ZNSt3__18ios_base5clearEj
+T _ZNSt3__18ios_base5imbueERKNS_6localeE
+T _ZNSt3__18ios_base5iwordEi
+T _ZNSt3__18ios_base5pwordEi
+T _ZNSt3__18ios_base6xallocEv
+T _ZNSt3__18ios_base7copyfmtERKS0_
+T _ZNSt3__18ios_base7failureC1EPKcRKNS_10error_codeE
+T _ZNSt3__18ios_base7failureC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_10error_codeE
+T _ZNSt3__18ios_base7failureC2EPKcRKNS_10error_codeE
+T _ZNSt3__18ios_base7failureC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_10error_codeE
+T _ZNSt3__18ios_base7failureD0Ev
+T _ZNSt3__18ios_base7failureD1Ev
+T _ZNSt3__18ios_base7failureD2Ev
+T _ZNSt3__18ios_baseD0Ev
+T _ZNSt3__18ios_baseD1Ev
+T _ZNSt3__18ios_baseD2Ev
+T _ZNSt3__18numpunctIcEC1Em
+T _ZNSt3__18numpunctIcEC2Em
+T _ZNSt3__18numpunctIcED0Ev
+T _ZNSt3__18numpunctIcED1Ev
+T _ZNSt3__18numpunctIcED2Ev
+T _ZNSt3__18numpunctIwEC1Em
+T _ZNSt3__18numpunctIwEC2Em
+T _ZNSt3__18numpunctIwED0Ev
+T _ZNSt3__18numpunctIwED1Ev
+T _ZNSt3__18numpunctIwED2Ev
+T _ZNSt3__18__rs_getEv
+T _ZNSt3__18__sp_mut4lockEv
+T _ZNSt3__18__sp_mut6unlockEv
+T _ZNSt3__18to_charsEPcS0_d
+T _ZNSt3__18to_charsEPcS0_dNS_12chars_formatE
+T _ZNSt3__18to_charsEPcS0_dNS_12chars_formatEi
+T _ZNSt3__18to_charsEPcS0_e
+T _ZNSt3__18to_charsEPcS0_eNS_12chars_formatE
+T _ZNSt3__18to_charsEPcS0_eNS_12chars_formatEi
+T _ZNSt3__18to_charsEPcS0_f
+T _ZNSt3__18to_charsEPcS0_fNS_12chars_formatE
+T _ZNSt3__18to_charsEPcS0_fNS_12chars_formatEi
+T _ZNSt3__19strstreamD0Ev
+T _ZNSt3__19strstreamD1Ev
+T _ZNSt3__19strstreamD2Ev
+T _ZNSt3__19to_stringEd
+T _ZNSt3__19to_stringEe
+T _ZNSt3__19to_stringEf
+T _ZNSt3__19to_stringEi
+T _ZNSt3__19to_stringEj
+T _ZNSt3__19to_stringEl
+T _ZNSt3__19to_stringEm
+T _ZNSt3__19to_stringEx
+T _ZNSt3__19to_stringEy
+T _ZSt17current_exceptionv
+T _ZSt17rethrow_exceptionSt13exception_ptr
+T _ZSt17__throw_bad_allocv
+T _ZSt18uncaught_exceptionv
+T _ZSt19uncaught_exceptionsv
+T _ZThn16_NSt3__19strstreamD0Ev
+T _ZThn16_NSt3__19strstreamD1Ev
+T _ZTv0_n24_NSt3__110istrstreamD0Ev
+T _ZTv0_n24_NSt3__110istrstreamD1Ev
+T _ZTv0_n24_NSt3__110ostrstreamD0Ev
+T _ZTv0_n24_NSt3__110ostrstreamD1Ev
+T _ZTv0_n24_NSt3__19strstreamD0Ev
+T _ZTv0_n24_NSt3__19strstreamD1Ev
+V _ZNSt3__110moneypunctIcLb0EE2idE
+V _ZNSt3__110moneypunctIcLb0EE4intlE
+V _ZNSt3__110moneypunctIcLb1EE2idE
+V _ZNSt3__110moneypunctIcLb1EE4intlE
+V _ZNSt3__110moneypunctIwLb0EE2idE
+V _ZNSt3__110moneypunctIwLb0EE4intlE
+V _ZNSt3__110moneypunctIwLb1EE2idE
+V _ZNSt3__110moneypunctIwLb1EE4intlE
+V _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4nposE
+V _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4nposE
+V _ZNSt3__14__fs10filesystem16_FilesystemClock9is_steadyE
+V _ZNSt3__14__fs10filesystem4path19preferred_separatorE
+V _ZNSt3__16chrono12steady_clock9is_steadyE
+V _ZNSt3__16chrono12system_clock9is_steadyE
+V _ZNSt3__17collateIcE2idE
+V _ZNSt3__17collateIwE2idE
+V _ZNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+V _ZNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+V _ZNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+V _ZNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+V _ZNSt3__18messagesIcE2idE
+V _ZNSt3__18messagesIwE2idE
+V _ZNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+V _ZNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+V _ZNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+V _ZNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+V _ZNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+V _ZNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+V _ZNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE2idE
+V _ZNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE2idE
+V _ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE
+V _ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE
+V _ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE16_NS_13basic_ostreamIcS2_EE
+V _ZTCNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE0_NS_13basic_ostreamIcS2_EE
+V _ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE
+V _ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_14basic_iostreamIcS2_EE
+V _ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE16_NS_13basic_ostreamIcS2_EE
+V _ZTCNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE
+V _ZTCNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_ostreamIcS2_EE
+V _ZTINSt3__110ctype_baseE
+V _ZTINSt3__110money_baseE
+V _ZTINSt3__110moneypunctIcLb0EEE
+V _ZTINSt3__110moneypunctIcLb1EEE
+V _ZTINSt3__110moneypunctIwLb0EEE
+V _ZTINSt3__110moneypunctIwLb1EEE
+V _ZTINSt3__110__time_getE
+V _ZTINSt3__110__time_putE
+V _ZTINSt3__111__money_getIcEE
+V _ZTINSt3__111__money_getIwEE
+V _ZTINSt3__111__money_putIcEE
+V _ZTINSt3__111__money_putIwEE
+V _ZTINSt3__112codecvt_baseE
+V _ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+V _ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+V _ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+V _ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+V _ZTINSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+V _ZTINSt3__113messages_baseE
+V _ZTINSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+V _ZTINSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+V _ZTINSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+V _ZTINSt3__114codecvt_bynameIcc11__mbstate_tEE
+V _ZTINSt3__114codecvt_bynameIDic11__mbstate_tEE
+V _ZTINSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+V _ZTINSt3__114codecvt_bynameIDsc11__mbstate_tEE
+V _ZTINSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+V _ZTINSt3__114codecvt_bynameIwc11__mbstate_tEE
+V _ZTINSt3__114__num_get_baseE
+V _ZTINSt3__114__num_put_baseE
+V _ZTINSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+V _ZTINSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+V _ZTINSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTINSt3__115messages_bynameIcEE
+V _ZTINSt3__115messages_bynameIwEE
+V _ZTINSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__117moneypunct_bynameIcLb0EEE
+V _ZTINSt3__117moneypunct_bynameIcLb1EEE
+V _ZTINSt3__117moneypunct_bynameIwLb0EEE
+V _ZTINSt3__117moneypunct_bynameIwLb1EEE
+V _ZTINSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTINSt3__118__time_get_storageIcEE
+V _ZTINSt3__118__time_get_storageIwEE
+V _ZTINSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTINSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTINSt3__120__time_get_c_storageIcEE
+V _ZTINSt3__120__time_get_c_storageIwEE
+V _ZTINSt3__17collateIcEE
+V _ZTINSt3__17collateIwEE
+V _ZTINSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__18messagesIcEE
+V _ZTINSt3__18messagesIwEE
+V _ZTINSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__19basic_iosIcNS_11char_traitsIcEEEE
+V _ZTINSt3__19basic_iosIwNS_11char_traitsIwEEEE
+V _ZTINSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTINSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTINSt3__19__num_getIcEE
+V _ZTINSt3__19__num_getIwEE
+V _ZTINSt3__19__num_putIcEE
+V _ZTINSt3__19__num_putIwEE
+V _ZTINSt3__19time_baseE
+V _ZTSNSt3__110ctype_baseE
+V _ZTSNSt3__110money_baseE
+V _ZTSNSt3__110moneypunctIcLb0EEE
+V _ZTSNSt3__110moneypunctIcLb1EEE
+V _ZTSNSt3__110moneypunctIwLb0EEE
+V _ZTSNSt3__110moneypunctIwLb1EEE
+V _ZTSNSt3__110__time_getE
+V _ZTSNSt3__110__time_putE
+V _ZTSNSt3__111__money_getIcEE
+V _ZTSNSt3__111__money_getIwEE
+V _ZTSNSt3__111__money_putIcEE
+V _ZTSNSt3__111__money_putIwEE
+V _ZTSNSt3__112codecvt_baseE
+V _ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+V _ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+V _ZTSNSt3__113messages_baseE
+V _ZTSNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__114codecvt_bynameIcc11__mbstate_tEE
+V _ZTSNSt3__114codecvt_bynameIDic11__mbstate_tEE
+V _ZTSNSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+V _ZTSNSt3__114codecvt_bynameIDsc11__mbstate_tEE
+V _ZTSNSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+V _ZTSNSt3__114codecvt_bynameIwc11__mbstate_tEE
+V _ZTSNSt3__114__num_get_baseE
+V _ZTSNSt3__114__num_put_baseE
+V _ZTSNSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+V _ZTSNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTSNSt3__115messages_bynameIcEE
+V _ZTSNSt3__115messages_bynameIwEE
+V _ZTSNSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__117moneypunct_bynameIcLb0EEE
+V _ZTSNSt3__117moneypunct_bynameIcLb1EEE
+V _ZTSNSt3__117moneypunct_bynameIwLb0EEE
+V _ZTSNSt3__117moneypunct_bynameIwLb1EEE
+V _ZTSNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTSNSt3__118__time_get_storageIcEE
+V _ZTSNSt3__118__time_get_storageIwEE
+V _ZTSNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTSNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTSNSt3__120__time_get_c_storageIcEE
+V _ZTSNSt3__120__time_get_c_storageIwEE
+V _ZTSNSt3__17collateIcEE
+V _ZTSNSt3__17collateIwEE
+V _ZTSNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__18messagesIcEE
+V _ZTSNSt3__18messagesIwEE
+V _ZTSNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__19basic_iosIcNS_11char_traitsIcEEEE
+V _ZTSNSt3__19basic_iosIwNS_11char_traitsIwEEEE
+V _ZTSNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTSNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTSNSt3__19__num_getIcEE
+V _ZTSNSt3__19__num_getIwEE
+V _ZTSNSt3__19__num_putIcEE
+V _ZTSNSt3__19__num_putIwEE
+V _ZTSNSt3__19time_baseE
+V _ZTTNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+V _ZTTNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+V _ZTTNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+V _ZTTNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+V _ZTTNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+V _ZTTNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+V _ZTTNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+V _ZTTNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTTNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTTNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTVNSt3__110moneypunctIcLb0EEE
+V _ZTVNSt3__110moneypunctIcLb1EEE
+V _ZTVNSt3__110moneypunctIwLb0EEE
+V _ZTVNSt3__110moneypunctIwLb1EEE
+V _ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE
+V _ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__113basic_ostreamIwNS_11char_traitsIwEEEE
+V _ZTVNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__114codecvt_bynameIcc11__mbstate_tEE
+V _ZTVNSt3__114codecvt_bynameIDic11__mbstate_tEE
+V _ZTVNSt3__114codecvt_bynameIDiDu11__mbstate_tEE
+V _ZTVNSt3__114codecvt_bynameIDsc11__mbstate_tEE
+V _ZTVNSt3__114codecvt_bynameIDsDu11__mbstate_tEE
+V _ZTVNSt3__114codecvt_bynameIwc11__mbstate_tEE
+V _ZTVNSt3__115basic_streambufIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__115basic_streambufIwNS_11char_traitsIwEEEE
+V _ZTVNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTVNSt3__115messages_bynameIcEE
+V _ZTVNSt3__115messages_bynameIwEE
+V _ZTVNSt3__115time_get_bynameIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__115time_get_bynameIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTVNSt3__115time_put_bynameIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__115time_put_bynameIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTVNSt3__117moneypunct_bynameIcLb0EEE
+V _ZTVNSt3__117moneypunct_bynameIcLb1EEE
+V _ZTVNSt3__117moneypunct_bynameIwLb0EEE
+V _ZTVNSt3__117moneypunct_bynameIwLb1EEE
+V _ZTVNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTVNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTVNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+V _ZTVNSt3__17collateIcEE
+V _ZTVNSt3__17collateIwEE
+V _ZTVNSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTVNSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTVNSt3__18messagesIcEE
+V _ZTVNSt3__18messagesIwEE
+V _ZTVNSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTVNSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTVNSt3__19basic_iosIcNS_11char_traitsIcEEEE
+V _ZTVNSt3__19basic_iosIwNS_11char_traitsIwEEEE
+V _ZTVNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+V _ZTVNSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEEE
+V _ZTVNSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEEE
+W _ZNKSt3__110moneypunctIcLb0EE11do_groupingEv
+W _ZNKSt3__110moneypunctIcLb0EE13do_neg_formatEv
+W _ZNKSt3__110moneypunctIcLb0EE13do_pos_formatEv
+W _ZNKSt3__110moneypunctIcLb0EE14do_curr_symbolEv
+W _ZNKSt3__110moneypunctIcLb0EE14do_frac_digitsEv
+W _ZNKSt3__110moneypunctIcLb0EE16do_decimal_pointEv
+W _ZNKSt3__110moneypunctIcLb0EE16do_negative_signEv
+W _ZNKSt3__110moneypunctIcLb0EE16do_positive_signEv
+W _ZNKSt3__110moneypunctIcLb0EE16do_thousands_sepEv
+W _ZNKSt3__110moneypunctIcLb1EE11do_groupingEv
+W _ZNKSt3__110moneypunctIcLb1EE13do_neg_formatEv
+W _ZNKSt3__110moneypunctIcLb1EE13do_pos_formatEv
+W _ZNKSt3__110moneypunctIcLb1EE14do_curr_symbolEv
+W _ZNKSt3__110moneypunctIcLb1EE14do_frac_digitsEv
+W _ZNKSt3__110moneypunctIcLb1EE16do_decimal_pointEv
+W _ZNKSt3__110moneypunctIcLb1EE16do_negative_signEv
+W _ZNKSt3__110moneypunctIcLb1EE16do_positive_signEv
+W _ZNKSt3__110moneypunctIcLb1EE16do_thousands_sepEv
+W _ZNKSt3__110moneypunctIwLb0EE11do_groupingEv
+W _ZNKSt3__110moneypunctIwLb0EE13do_neg_formatEv
+W _ZNKSt3__110moneypunctIwLb0EE13do_pos_formatEv
+W _ZNKSt3__110moneypunctIwLb0EE14do_curr_symbolEv
+W _ZNKSt3__110moneypunctIwLb0EE14do_frac_digitsEv
+W _ZNKSt3__110moneypunctIwLb0EE16do_decimal_pointEv
+W _ZNKSt3__110moneypunctIwLb0EE16do_negative_signEv
+W _ZNKSt3__110moneypunctIwLb0EE16do_positive_signEv
+W _ZNKSt3__110moneypunctIwLb0EE16do_thousands_sepEv
+W _ZNKSt3__110moneypunctIwLb1EE11do_groupingEv
+W _ZNKSt3__110moneypunctIwLb1EE13do_neg_formatEv
+W _ZNKSt3__110moneypunctIwLb1EE13do_pos_formatEv
+W _ZNKSt3__110moneypunctIwLb1EE14do_curr_symbolEv
+W _ZNKSt3__110moneypunctIwLb1EE14do_frac_digitsEv
+W _ZNKSt3__110moneypunctIwLb1EE16do_decimal_pointEv
+W _ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv
+W _ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv
+W _ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE16find_last_not_ofEPKcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE17find_first_not_ofEPKcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEcm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4findEPKcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5rfindEcm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5rfindEPKcmm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKc
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKcm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmRKS5_mm
+W _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEPKc
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE12find_last_ofEPKwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE13find_first_ofEPKwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE16find_last_not_ofEPKwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE17find_first_not_ofEPKwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE2atEm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4copyEPwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4findEPKwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE4findEwm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5rfindEPKwmm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5rfindEwm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmPKw
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmPKwm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEmmRKS5_mm
+W _ZNKSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7compareEPKw
+W _ZNKSt3__115basic_streambufIcNS_11char_traitsIcEEE6getlocEv
+W _ZNKSt3__115basic_streambufIwNS_11char_traitsIwEEE6getlocEv
+W _ZNKSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE11do_groupingEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE13do_neg_formatEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE13do_pos_formatEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE14do_curr_symbolEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE14do_frac_digitsEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE16do_decimal_pointEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE16do_negative_signEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE16do_positive_signEv
+W _ZNKSt3__117moneypunct_bynameIcLb0EE16do_thousands_sepEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE11do_groupingEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE13do_neg_formatEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE13do_pos_formatEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE14do_curr_symbolEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE14do_frac_digitsEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE16do_decimal_pointEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE16do_negative_signEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE16do_positive_signEv
+W _ZNKSt3__117moneypunct_bynameIcLb1EE16do_thousands_sepEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE11do_groupingEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE13do_neg_formatEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE13do_pos_formatEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE14do_curr_symbolEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE14do_frac_digitsEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE16do_decimal_pointEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE16do_negative_signEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE16do_positive_signEv
+W _ZNKSt3__117moneypunct_bynameIwLb0EE16do_thousands_sepEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE11do_groupingEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE13do_neg_formatEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE13do_pos_formatEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE14do_curr_symbolEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE14do_frac_digitsEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE16do_decimal_pointEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE16do_negative_signEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE16do_positive_signEv
+W _ZNKSt3__117moneypunct_bynameIwLb1EE16do_thousands_sepEv
+W _ZNKSt3__17collateIcE10do_compareEPKcS3_S3_S3_
+W _ZNKSt3__17collateIcE12do_transformEPKcS3_
+W _ZNKSt3__17collateIcE7do_hashEPKcS3_
+W _ZNKSt3__17collateIwE10do_compareEPKwS3_S3_S3_
+W _ZNKSt3__17collateIwE12do_transformEPKwS3_
+W _ZNKSt3__17collateIwE7do_hashEPKwS3_
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRb
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRd
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRe
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRf
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRl
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRm
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRPv
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRt
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRx
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjRy
+W _ZNKSt3__17num_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjS8_
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRb
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRd
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRe
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRf
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRl
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRm
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRPv
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRt
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRx
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjRy
+W _ZNKSt3__17num_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjS8_
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcb
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcd
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEce
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcl
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcm
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcPKv
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcx
+W _ZNKSt3__17num_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcy
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwb
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwd
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwe
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwl
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwm
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPKv
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwx
+W _ZNKSt3__17num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwy
+W _ZNKSt3__18messagesIcE6do_getEliiRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
+W _ZNKSt3__18messagesIcE7do_openERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_6localeE
+W _ZNKSt3__18messagesIcE8do_closeEl
+W _ZNKSt3__18messagesIwE6do_getEliiRKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEE
+W _ZNKSt3__18messagesIwE7do_openERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNS_6localeE
+W _ZNKSt3__18messagesIwE8do_closeEl
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE10__get_hourERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE10__get_yearERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_dateES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_timeES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11do_get_yearES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_am_pmERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_monthERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE11__get_year4ERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE12__get_minuteERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE12__get_secondERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13do_date_orderEv
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_12_hourERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_percentERS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE13__get_weekdayERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE14do_get_weekdayES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE15__get_monthnameERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE16do_get_monthnameES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE17__get_weekdaynameERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE17__get_white_spaceERS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE18__get_day_year_numERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE3getES4_S4_RNS_8ios_baseERjP2tmPKcSC_
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_RNS_8ios_baseERjP2tmcc
+W _ZNKSt3__18time_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE9__get_dayERiRS4_S4_RjRKNS_5ctypeIcEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE10__get_hourERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE10__get_yearERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_dateES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_timeES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11do_get_yearES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_am_pmERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_monthERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE11__get_year4ERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE12__get_minuteERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE12__get_secondERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13do_date_orderEv
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_12_hourERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_percentERS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE13__get_weekdayERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE14do_get_weekdayES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE15__get_monthnameERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE16do_get_monthnameES4_S4_RNS_8ios_baseERjP2tm
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE17__get_weekdaynameERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE17__get_white_spaceERS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE18__get_day_year_numERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE3getES4_S4_RNS_8ios_baseERjP2tmPKwSC_
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_RNS_8ios_baseERjP2tmcc
+W _ZNKSt3__18time_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE9__get_dayERiRS4_S4_RjRKNS_5ctypeIwEE
+W _ZNKSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE3putES4_RNS_8ios_baseEcPK2tmPKcSC_
+W _ZNKSt3__18time_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_RNS_8ios_baseEcPK2tmcc
+W _ZNKSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE3putES4_RNS_8ios_baseEwPK2tmPKwSC_
+W _ZNKSt3__18time_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPK2tmcc
+W _ZNKSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_bRNS_8ios_baseERjRe
+W _ZNKSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_getES4_S4_bRNS_8ios_baseERjRNS_12basic_stringIcS3_NS_9allocatorIcEEEE
+W _ZNKSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_bRNS_8ios_baseERjRe
+W _ZNKSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_getES4_S4_bRNS_8ios_baseERjRNS_12basic_stringIwS3_NS_9allocatorIwEEEE
+W _ZNKSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_bRNS_8ios_baseEce
+W _ZNKSt3__19money_putIcNS_19ostreambuf_iteratorIcNS_11char_traitsIcEEEEE6do_putES4_bRNS_8ios_baseEcRKNS_12basic_stringIcS3_NS_9allocatorIcEEEE
+W _ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwe
+W _ZNKSt3__19money_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_bRNS_8ios_baseEwRKNS_12basic_stringIwS3_NS_9allocatorIwEEEE
+W _ZNSt3__111__money_getIcE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_SF_Ri
+W _ZNSt3__111__money_getIwE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_SJ_Ri
+W _ZNSt3__111__money_putIcE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_Ri
+W _ZNSt3__111__money_putIcE8__formatEPcRS2_S3_jPKcS5_RKNS_5ctypeIcEEbRKNS_10money_base7patternEccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESL_SL_i
+W _ZNSt3__111__money_putIwE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_Ri
+W _ZNSt3__111__money_putIwE8__formatEPwRS2_S3_jPKwS5_RKNS_5ctypeIwEEbRKNS_10money_base7patternEwwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERKNSE_IwNSF_IwEENSH_IwEEEESQ_i
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE21__grow_by_and_replaceEmmmmmmPKc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE2atEm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5eraseEmm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEmc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKcm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendERKS5_mm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEmc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignEPKcm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6assignERKS5_mm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEmc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcmm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmmc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmPKc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmPKcm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertEmRKS5_mm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6insertENS_11__wrap_iterIPKcEEc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6resizeEmc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmmc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmPKc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmPKcm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7replaceEmmRKS5_mm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEmmmmmm
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEc
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSERKS5_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_mmRKS4_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1ERKS5_RKS4_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_mmRKS4_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2ERKS5_RKS4_
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev
+W _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE21__grow_by_and_replaceEmmmmmmPKw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE2atEm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE5eraseEmm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEmw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEPKw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendEPKwm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6appendERKS5_mm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEmw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEPKw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignEPKwm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6assignERKS5_mm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEmw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEPKwm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6__initEPKwmm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmmw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmPKw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmPKwm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertEmRKS5_mm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6insertENS_11__wrap_iterIPKwEEw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6resizeEmw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmmw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmPKw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmPKwm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7replaceEmmRKS5_mm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE7reserveEm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9__grow_byEmmmmmm
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9push_backEw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEaSERKS5_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEaSEw
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_mmRKS4_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC1ERKS5_RKS4_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_mmRKS4_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEC2ERKS5_RKS4_
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED1Ev
+W _ZNSt3__112basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED2Ev
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE11__read_modeEv
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE12__write_modeEv
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4openEPKcj
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4swapERS3_
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4syncEv
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5closeEv
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE6setbufEPcl
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekposENS_4fposI11__mbstate_tEEj
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE8overflowEi
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9pbackfailEi
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9underflowEv
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC1EOS3_
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC1Ev
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2EOS3_
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2Ev
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED0Ev
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED1Ev
+W _ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED2Ev
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEPcl
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEPclc
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERc
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getERNS_15basic_streambufIcS2_EEc
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE3getEv
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4peekEv
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4readEPcl
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4swapERS3_
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE4syncEv
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5seekgENS_4fposI11__mbstate_tEE
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5seekgExNS_8ios_base7seekdirE
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5tellgEv
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE5ungetEv
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6ignoreEli
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6sentryC1ERS3_b
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE6sentryC2ERS3_b
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7getlineEPcl
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7getlineEPclc
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE7putbackEc
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEE8readsomeEPcl
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED0Ev
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED1Ev
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEED2Ev
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRNS_8ios_baseES5_E
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRNS_9basic_iosIcS2_EES6_E
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPFRS3_S4_E
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsEPNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERb
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERd
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERe
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERf
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERi
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERj
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERl
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERm
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERPv
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERs
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERt
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERx
+W _ZNSt3__113basic_istreamIcNS_11char_traitsIcEEErsERy
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEPwl
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEPwlw
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERNS_15basic_streambufIwS2_EEw
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getERw
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE3getEv
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4peekEv
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4readEPwl
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4swapERS3_
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE4syncEv
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5seekgENS_4fposI11__mbstate_tEE
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5seekgExNS_8ios_base7seekdirE
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5tellgEv
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE5ungetEv
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6ignoreElj
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6sentryC1ERS3_b
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE6sentryC2ERS3_b
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7getlineEPwl
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7getlineEPwlw
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE7putbackEw
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEE8readsomeEPwl
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEEC1EPNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEEC2EPNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEED2Ev
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRNS_8ios_baseES5_E
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRNS_9basic_iosIwS2_EES6_E
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPFRS3_S4_E
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsEPNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERb
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERd
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERe
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERf
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERi
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERj
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERl
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERm
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERPv
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERs
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERt
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERx
+W _ZNSt3__113basic_istreamIwNS_11char_traitsIwEEErsERy
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE3putEc
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE4swapERS3_
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5seekpENS_4fposI11__mbstate_tEE
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5seekpExNS_8ios_base7seekdirE
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5tellpEv
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5writeEPKcl
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC2ERS3_
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD2Ev
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEED2Ev
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEb
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEd
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEe
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEf
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEi
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEj
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEl
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEm
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRNS_8ios_baseES5_E
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRNS_9basic_iosIcS2_EES6_E
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPFRS3_S4_E
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPKv
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPNS_15basic_streambufIcS2_EE
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEs
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEt
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEx
+W _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEy
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE3putEw
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE4swapERS3_
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5flushEv
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5seekpENS_4fposI11__mbstate_tEE
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5seekpExNS_8ios_base7seekdirE
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5tellpEv
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5writeEPKwl
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryC1ERS3_
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryC2ERS3_
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryD1Ev
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE6sentryD2Ev
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEEC1EPNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEEC2EPNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED0Ev
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED1Ev
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEED2Ev
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEb
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEd
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEe
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEf
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEi
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEj
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEl
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEm
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRNS_8ios_baseES5_E
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRNS_9basic_iosIwS2_EES6_E
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPFRS3_S4_E
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPKv
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEPNS_15basic_streambufIwS2_EE
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEs
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEt
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEx
+W _ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEElsEy
+W _ZNSt3__114basic_ifstreamIcNS_11char_traitsIcEEE4openEPKcj
+W _ZNSt3__114basic_ifstreamIcNS_11char_traitsIcEEE4openERKNS_12basic_stringIcS2_NS_9allocatorIcEEEEj
+W _ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEE4swapERS3_
+W _ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEEC1EPNS_15basic_streambufIcS2_EE
+W _ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEEC2EPNS_15basic_streambufIcS2_EE
+W _ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+W _ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+W _ZNSt3__114basic_iostreamIcNS_11char_traitsIcEEED2Ev
+W _ZNSt3__114basic_ofstreamIcNS_11char_traitsIcEEE4openEPKcj
+W _ZNSt3__114basic_ofstreamIcNS_11char_traitsIcEEE4openERKNS_12basic_stringIcS2_NS_9allocatorIcEEEEj
+W _ZNSt3__114codecvt_bynameIcc11__mbstate_tED0Ev
+W _ZNSt3__114codecvt_bynameIcc11__mbstate_tED1Ev
+W _ZNSt3__114codecvt_bynameIcc11__mbstate_tED2Ev
+W _ZNSt3__114codecvt_bynameIDic11__mbstate_tED0Ev
+W _ZNSt3__114codecvt_bynameIDic11__mbstate_tED1Ev
+W _ZNSt3__114codecvt_bynameIDic11__mbstate_tED2Ev
+W _ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED0Ev
+W _ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED1Ev
+W _ZNSt3__114codecvt_bynameIDiDu11__mbstate_tED2Ev
+W _ZNSt3__114codecvt_bynameIDsc11__mbstate_tED0Ev
+W _ZNSt3__114codecvt_bynameIDsc11__mbstate_tED1Ev
+W _ZNSt3__114codecvt_bynameIDsc11__mbstate_tED2Ev
+W _ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED0Ev
+W _ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED1Ev
+W _ZNSt3__114codecvt_bynameIDsDu11__mbstate_tED2Ev
+W _ZNSt3__114codecvt_bynameIwc11__mbstate_tED0Ev
+W _ZNSt3__114codecvt_bynameIwc11__mbstate_tED1Ev
+W _ZNSt3__114codecvt_bynameIwc11__mbstate_tED2Ev
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE10pubseekoffExNS_8ios_base7seekdirEj
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE10pubseekposENS_4fposI11__mbstate_tEEj
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4setgEPcS4_S4_
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4setpEPcS4_
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4swapERS3_
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE4syncEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5gbumpEi
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5pbumpEi
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sgetcEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sgetnEPcl
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputcEc
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputnEPKcl
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5uflowEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6sbumpcEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6setbufEPcl
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6snextcEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsgetnEPcl
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE6xsputnEPKcl
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7pubsyncEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7seekposENS_4fposI11__mbstate_tEEj
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE7sungetcEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8in_availEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8overflowEi
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE8pubimbueERKNS_6localeE
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9pbackfailEi
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9pubsetbufEPcl
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9showmanycEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9sputbackcEc
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE9underflowEv
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEaSERKS3_
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC1ERKS3_
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC1Ev
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2ERKS3_
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEEC2Ev
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED0Ev
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED1Ev
+W _ZNSt3__115basic_streambufIcNS_11char_traitsIcEEED2Ev
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE10pubseekoffExNS_8ios_base7seekdirEj
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE10pubseekposENS_4fposI11__mbstate_tEEj
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4setgEPwS4_S4_
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4setpEPwS4_
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4swapERS3_
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE4syncEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5gbumpEi
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5imbueERKNS_6localeE
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5pbumpEi
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sgetcEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sgetnEPwl
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sputcEw
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5sputnEPKwl
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE5uflowEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6sbumpcEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6setbufEPwl
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6snextcEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6xsgetnEPwl
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE6xsputnEPKwl
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7pubsyncEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7seekoffExNS_8ios_base7seekdirEj
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7seekposENS_4fposI11__mbstate_tEEj
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE7sungetcEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8in_availEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8overflowEj
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE8pubimbueERKNS_6localeE
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9pbackfailEj
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9pubsetbufEPwl
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9showmanycEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9sputbackcEw
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEE9underflowEv
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEaSERKS3_
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC1ERKS3_
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC1Ev
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC2ERKS3_
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEEC2Ev
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED0Ev
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED1Ev
+W _ZNSt3__115basic_streambufIwNS_11char_traitsIwEEED2Ev
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strERKNS_12basic_stringIcS2_S4_EE
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE4swapERS5_
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE7seekoffExNS_8ios_base7seekdirEj
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE8overflowEi
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9pbackfailEi
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE9underflowEv
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEC1EOS5_
+W _ZNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEC2EOS5_
+W _ZNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+W _ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+W _ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_
+W _ZNSt3__120__atomic_wait_nativeILm4EEEvPKvS2_
+W _ZNSt3__122__libcpp_verbose_abortEPKcz
+W _ZNSt3__126__atomic_notify_all_nativeILm4EEEvPKv
+W _ZNSt3__126__atomic_notify_one_nativeILm4EEEvPKv
+W _ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+W _ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+W _ZNSt3__133__atomic_wait_native_with_timeoutILm4EEEvPKvS2_m
+W _ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIccEEPcEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIddEEPdEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIeeEEPeEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIffEEPfEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIhhEEPhEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIiiEEPiEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIjjEEPjEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIllEEPlEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessImmEEPmEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIssEEPsEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIttEEPtEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIwwEEPwEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIxxEEPxEEvT0_S5_T_
+W _ZNSt3__16__sortIRNS_6__lessIyyEEPyEEvT0_S5_T_
+W _ZNSt3__17collateIcED0Ev
+W _ZNSt3__17collateIcED1Ev
+W _ZNSt3__17collateIcED2Ev
+W _ZNSt3__17collateIwED0Ev
+W _ZNSt3__17collateIwED1Ev
+W _ZNSt3__17collateIwED2Ev
+W _ZNSt3__18valarrayImE6resizeEmm
+W _ZNSt3__18valarrayImEC1Em
+W _ZNSt3__18valarrayImEC2Em
+W _ZNSt3__18valarrayImED1Ev
+W _ZNSt3__18valarrayImED2Ev
+W _ZNSt3__19basic_iosIcNS_11char_traitsIcEEE7copyfmtERKS3_
+W _ZNSt3__19basic_iosIcNS_11char_traitsIcEEED0Ev
+W _ZNSt3__19basic_iosIcNS_11char_traitsIcEEED1Ev
+W _ZNSt3__19basic_iosIcNS_11char_traitsIcEEED2Ev
+W _ZNSt3__19basic_iosIwNS_11char_traitsIwEEE7copyfmtERKS3_
+W _ZNSt3__19basic_iosIwNS_11char_traitsIwEEED0Ev
+W _ZNSt3__19basic_iosIwNS_11char_traitsIwEEED1Ev
+W _ZNSt3__19basic_iosIwNS_11char_traitsIwEEED2Ev
+W _ZNSt3__19money_getIcNS_19istreambuf_iteratorIcNS_11char_traitsIcEEEEE8__do_getERS4_S4_bRKNS_6localeEjRjRbRKNS_5ctypeIcEERNS_10unique_ptrIcPFvPvEEERPcSM_
+W _ZNSt3__19money_getIwNS_19istreambuf_iteratorIwNS_11char_traitsIwEEEEE8__do_getERS4_S4_bRKNS_6localeEjRjRbRKNS_5ctypeIwEERNS_10unique_ptrIwPFvPvEEERPwSM_
+W _ZNSt3__19__num_getIcE17__stage2_int_loopEciPcRS2_RjcRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_S2_
+W _ZNSt3__19__num_getIcE17__stage2_int_prepERNS_8ios_baseEPcRc
+W _ZNSt3__19__num_getIcE19__stage2_float_loopEcRbRcPcRS4_ccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjS4_
+W _ZNSt3__19__num_getIcE19__stage2_float_prepERNS_8ios_baseEPcRcS5_
+W _ZNSt3__19__num_getIwE17__stage2_int_loopEwiPcRS2_RjwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_Pw
+W _ZNSt3__19__num_getIwE17__stage2_int_prepERNS_8ios_baseEPwRw
+W _ZNSt3__19__num_getIwE19__stage2_float_loopEwRbRcPcRS4_wwRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjPw
+W _ZNSt3__19__num_getIwE19__stage2_float_prepERNS_8ios_baseEPwRwS5_
+W _ZNSt3__19__num_putIcE21__widen_and_group_intEPcS2_S2_S2_RS2_S3_RKNS_6localeE
+W _ZNSt3__19__num_putIcE23__widen_and_group_floatEPcS2_S2_S2_RS2_S3_RKNS_6localeE
+W _ZNSt3__19__num_putIwE21__widen_and_group_intEPcS2_S2_PwRS3_S4_RKNS_6localeE
+W _ZNSt3__19__num_putIwE23__widen_and_group_floatEPcS2_S2_PwRS3_S4_RKNS_6localeE
+W _ZNSt3__1plIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_12basic_stringIT_T0_T1_EEPKS6_RKS9_
+W _ZThn16_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+W _ZThn16_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev
+W _ZTv0_n24_NSt3__113basic_istreamIcNS_11char_traitsIcEEED0Ev
+W _ZTv0_n24_NSt3__113basic_istreamIcNS_11char_traitsIcEEED1Ev
+W _ZTv0_n24_NSt3__113basic_istreamIwNS_11char_traitsIwEEED0Ev
+W _ZTv0_n24_NSt3__113basic_istreamIwNS_11char_traitsIwEEED1Ev
+W _ZTv0_n24_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED0Ev
+W _ZTv0_n24_NSt3__113basic_ostreamIcNS_11char_traitsIcEEED1Ev
+W _ZTv0_n24_NSt3__113basic_ostreamIwNS_11char_traitsIwEEED0Ev
+W _ZTv0_n24_NSt3__113basic_ostreamIwNS_11char_traitsIwEEED1Ev
+W _ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev
+W _ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev


From libcxx-commits at lists.llvm.org  Wed Jul  8 05:37:04 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 08 Jul 2026 05:37:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a4e4470.8e75e0cb.5e108.08be@mx.google.com>


https://github.com/philnik777 ready_for_review https://github.com/llvm/llvm-project/pull/207500

From libcxx-commits at lists.llvm.org  Wed Jul  8 05:37:40 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 05:37:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a4e4494.dd2b4bd3.20635.a819@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/207500.diff


3 Files Affected:

- (modified) libcxx/include/__memory/uninitialized_algorithms.h (+38) 
- (modified) libcxx/include/__vector/vector.h (+3-11) 
- (modified) libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp (+1-1) 


``````````diff
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..5426dae7ef114 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -450,6 +450,30 @@ __uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _B
 
 #endif // _LIBCPP_STD_VER >= 17
 
+template <class _Alloc, class _Iter, class _Sent, class _Tp>
+_LIBCPP_CONSTEXPR_SINCE_CXX20
+_Iter __uninitialized_allocator_fill(_Alloc& __alloc, _Iter __first, _Sent __last, const _Tp& __value) {
+  auto __iter = __first;
+
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __first, __iter); });
+  for (; __iter != __last; ++__iter)
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__iter), __value);
+  __guard.__complete();
+  return __iter;
+}
+
+template <class _Alloc, class _Iter, class _Sent>
+_LIBCPP_CONSTEXPR_SINCE_CXX20
+_Iter __uninitialized_allocator_value_construct(_Alloc& __alloc, _Iter __first, _Sent __last) {
+  auto __iter = __first;
+
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __first, __iter); });
+  for (; __iter != __last; ++__iter)
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__iter));
+  __guard.__complete();
+  return __iter;
+}
+
 template <class _Alloc, class _Iter>
 class _AllocatorDestroyRangeReverse {
 public:
@@ -528,6 +552,20 @@ inline const bool __allocator_has_trivial_move_construct_v = !__has_construct_v<
 template <class _Type>
 inline const bool __allocator_has_trivial_move_construct_v<allocator<_Type>, _Type> = true;
 
+template <class _Alloc, class _InIter, class _Sent, class _OutIter>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
+__uninitialized_allocator_move(_Alloc& __alloc, _InIter __first, _Sent __last, _OutIter __result) {
+  auto __destruct_first = __result;
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __destruct_first, __result); });
+  while (__first != __last) {
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__result), std::move(*__first));
+    ++__first;
+    ++__result;
+  }
+  __guard.__complete();
+  return {std::move(__first), std::move(__result)};
+}
+
 template <class _Alloc, class _Tp>
 inline const bool __allocator_has_trivial_destroy_v = !__has_destroy_v<_Alloc, _Tp*>;
 
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..603b5676e05fb 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -880,10 +880,7 @@ vector<_Tp, _Allocator>::__recommend(size_type __new_size) const {
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__construct_at_end(size_type __n) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos));
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_value_construct(__layout_.__alloc(), __tx.__pos_, __tx.__new_end_);
 }
 
 //  Copy constructs __n objects starting at __layout_.__end_ptr() from __x
@@ -896,10 +893,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline void
 vector<_Tp, _Allocator>::__construct_at_end(size_type __n, const_reference __x) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), __x);
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_fill(__layout_.__alloc(), __tx.__pos_, __tx.__new_end_, __x);
 }
 
 template <class _Tp, class _Allocator>
@@ -1123,9 +1117,7 @@ vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointe
   {
     pointer __i = __from_s + __n;
     _ConstructTransaction __tx(*this, __from_e - __i);
-    for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
-      __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
-    }
+    __tx.__pos_ = std::__uninitialized_allocator_move(__layout_.__alloc(), __i, __from_e, __tx.__pos_).__out_;
   }
   std::move_backward(__from_s, __from_s + __n, __old_last);
 }
diff --git a/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp
index dcfa8029cfc0d..5089cd716266a 100644
--- a/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp
@@ -182,7 +182,7 @@ void test_insert_n2() {
     v.insert(v.cbegin(), 5, ThrowOnCopy());
     assert(0);
   } catch (int e) {
-    assert(v.size() == 11);
+    assert(v.size() == 10);
     assert(is_contiguous_container_asan_correct(v));
     return;
   }

``````````

</details>


https://github.com/llvm/llvm-project/pull/207500

From libcxx-commits at lists.llvm.org  Wed Jul  8 06:59:51 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 06:59:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Move guards outside
	namespace (PR #208089)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208089@github.com>
Message-ID: <6a4e57d7.af6ba3ed.36d9db.b310@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/208089

>From 1ebdd91a309fbdc8d7fb0d17a13b731ce5c6022d Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 7 Jul 2026 20:12:00 +0000
Subject: [PATCH] [libcxx][NFC] Move guards outside namespace

Libc++ has a warning that triggers on empty namespaces. When
_LIBCPP_BUILDING_LIBRARY is not defined, the __locale namespace was
empty. This PR moves the #ifdef to surround the namespace and fix the
warning.
---
 libcxx/include/__locale_dir/support/no_locale/characters.h  | 5 +++--
 libcxx/include/__locale_dir/support/no_locale/conversions.h | 5 +++--
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__locale_dir/support/no_locale/characters.h b/libcxx/include/__locale_dir/support/no_locale/characters.h
index 52e842d8f494f..ffe6697916914 100644
--- a/libcxx/include/__locale_dir/support/no_locale/characters.h
+++ b/libcxx/include/__locale_dir/support/no_locale/characters.h
@@ -24,13 +24,14 @@
 #  pragma GCC system_header
 #endif
 
+#if defined(_LIBCPP_BUILDING_LIBRARY)
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __locale {
 
 //
 // Character manipulation functions
 //
-#if defined(_LIBCPP_BUILDING_LIBRARY)
 inline _LIBCPP_HIDE_FROM_ABI int __toupper(int __c, __locale_t) { return std::toupper(__c); }
 
 inline _LIBCPP_HIDE_FROM_ABI int __tolower(int __c, __locale_t) { return std::tolower(__c); }
@@ -85,9 +86,9 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_ATTRIBUTE_FORMAT(__strftime__, 3, 0) size_t
     __strftime(char* __s, size_t __max, const char* __format, const struct tm* __tm, __locale_t) {
   return std::strftime(__s, __max, __format, __tm);
 }
-#endif // _LIBCPP_BUILDING_LIBRARY
 
 } // namespace __locale
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_BUILDING_LIBRARY
 #endif // _LIBCPP___LOCALE_DIR_SUPPORT_NO_LOCALE_CHARACTERS_H
diff --git a/libcxx/include/__locale_dir/support/no_locale/conversions.h b/libcxx/include/__locale_dir/support/no_locale/conversions.h
index 64a50df4a50b9..bb501515c4d4e 100644
--- a/libcxx/include/__locale_dir/support/no_locale/conversions.h
+++ b/libcxx/include/__locale_dir/support/no_locale/conversions.h
@@ -21,10 +21,11 @@
 #  pragma GCC system_header
 #endif
 
+#if defined(_LIBCPP_BUILDING_LIBRARY)
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __locale {
 
-#if defined(_LIBCPP_BUILDING_LIBRARY)
 inline _LIBCPP_HIDE_FROM_ABI decltype(MB_CUR_MAX) __mb_len_max(__locale_t) { return MB_CUR_MAX; }
 
 #  if _LIBCPP_HAS_WIDE_CHARACTERS
@@ -64,9 +65,9 @@ __mbsrtowcs(wchar_t* __dest, const char** __src, size_t __len, mbstate_t* __ps,
   return std::mbsrtowcs(__dest, __src, __len, __ps);
 }
 #  endif // _LIBCPP_HAS_WIDE_CHARACTERS
-#endif   // _LIBCPP_BUILDING_LIBRARY
 
 } // namespace __locale
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_BUILDING_LIBRARY
 #endif // _LIBCPP___LOCALE_DIR_SUPPORT_NO_LOCALE_CONVERSIONS_H


From libcxx-commits at lists.llvm.org  Wed Jul  8 07:00:13 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:00:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Move guards outside
	namespace (PR #208089)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208089@github.com>
Message-ID: <6a4e57ed.70920dee.290dca.e462@mx.google.com>


ldionne wrote:

Re-triggering CI now that Github is hopefully back to stability

https://github.com/llvm/llvm-project/pull/208089

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:02:01 2026
From: libcxx-commits at lists.llvm.org (Xavier Roche via libcxx-commits)
Date: Wed, 08 Jul 2026 07:02:01 -0700 (PDT)
Subject: [libcxx-commits] [clang] [compiler-rt] [libcxx] [Clang] Support
 atomic operations on _BitInt(N) (PR #204815)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204815@github.com>
Message-ID: <6a4e5859.0c05e68e.1e82f1.dbb7@mx.google.com>


https://github.com/xroche updated https://github.com/llvm/llvm-project/pull/204815

Not Found

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:06:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 07:06:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] b3e6e6d - [libcxx][NFC] Move guards
	outside namespace (#208089)
Message-ID: <6a4e595f.4a342cbc.ca844.3c9f@mx.google.com>


Author: Michael Jones
Date: 2026-07-08T14:06:18Z
New Revision: b3e6e6dabdc02153552a64fc74ff5c7532447eed

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

LOG: [libcxx][NFC] Move guards outside namespace (#208089)

Libc++ has a warning that triggers on empty namespaces. When
_LIBCPP_BUILDING_LIBRARY is not defined, the __locale namespace was
empty. This PR moves the #ifdef to surround the namespace and fix the
warning.

Added: 
    

Modified: 
    libcxx/include/__locale_dir/support/no_locale/characters.h
    libcxx/include/__locale_dir/support/no_locale/conversions.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__locale_dir/support/no_locale/characters.h b/libcxx/include/__locale_dir/support/no_locale/characters.h
index 52e842d8f494f..ffe6697916914 100644
--- a/libcxx/include/__locale_dir/support/no_locale/characters.h
+++ b/libcxx/include/__locale_dir/support/no_locale/characters.h
@@ -24,13 +24,14 @@
 #  pragma GCC system_header
 #endif
 
+#if defined(_LIBCPP_BUILDING_LIBRARY)
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __locale {
 
 //
 // Character manipulation functions
 //
-#if defined(_LIBCPP_BUILDING_LIBRARY)
 inline _LIBCPP_HIDE_FROM_ABI int __toupper(int __c, __locale_t) { return std::toupper(__c); }
 
 inline _LIBCPP_HIDE_FROM_ABI int __tolower(int __c, __locale_t) { return std::tolower(__c); }
@@ -85,9 +86,9 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_ATTRIBUTE_FORMAT(__strftime__, 3, 0) size_t
     __strftime(char* __s, size_t __max, const char* __format, const struct tm* __tm, __locale_t) {
   return std::strftime(__s, __max, __format, __tm);
 }
-#endif // _LIBCPP_BUILDING_LIBRARY
 
 } // namespace __locale
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_BUILDING_LIBRARY
 #endif // _LIBCPP___LOCALE_DIR_SUPPORT_NO_LOCALE_CHARACTERS_H

diff  --git a/libcxx/include/__locale_dir/support/no_locale/conversions.h b/libcxx/include/__locale_dir/support/no_locale/conversions.h
index 64a50df4a50b9..bb501515c4d4e 100644
--- a/libcxx/include/__locale_dir/support/no_locale/conversions.h
+++ b/libcxx/include/__locale_dir/support/no_locale/conversions.h
@@ -21,10 +21,11 @@
 #  pragma GCC system_header
 #endif
 
+#if defined(_LIBCPP_BUILDING_LIBRARY)
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __locale {
 
-#if defined(_LIBCPP_BUILDING_LIBRARY)
 inline _LIBCPP_HIDE_FROM_ABI decltype(MB_CUR_MAX) __mb_len_max(__locale_t) { return MB_CUR_MAX; }
 
 #  if _LIBCPP_HAS_WIDE_CHARACTERS
@@ -64,9 +65,9 @@ __mbsrtowcs(wchar_t* __dest, const char** __src, size_t __len, mbstate_t* __ps,
   return std::mbsrtowcs(__dest, __src, __len, __ps);
 }
 #  endif // _LIBCPP_HAS_WIDE_CHARACTERS
-#endif   // _LIBCPP_BUILDING_LIBRARY
 
 } // namespace __locale
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_BUILDING_LIBRARY
 #endif // _LIBCPP___LOCALE_DIR_SUPPORT_NO_LOCALE_CONVERSIONS_H


        

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:06:26 2026
From: libcxx-commits at lists.llvm.org (Michael Jones via libcxx-commits)
Date: Wed, 08 Jul 2026 07:06:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Move guards outside
	namespace (PR #208089)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208089@github.com>
Message-ID: <6a4e5962.491d90f5.2facd.a8c3@mx.google.com>


https://github.com/michaelrj-google closed https://github.com/llvm/llvm-project/pull/208089

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:19:04 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:19:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a4e5c58.1905e68e.18ad68.c428@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM with nitpicks

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:19:04 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:19:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a4e5c58.331483e7.2440f8.8820@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:19:05 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:19:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a4e5c59.58bdd12c.19c81.487b@mx.google.com>


================
@@ -6,602 +6,479 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench(
+      "std::basic_string::ctor(const value_type*)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> str(state.range(), 'a');
+
+        for (auto _ : state) {
+          auto ptr = str.data();
+          std::basic_string<CharT> copy(ptr);
+          benchmark::DoNotOptimize(copy);
+        }
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
----------------
ldionne wrote:

Let's use names for small/large. That way we can change the sizes by only modifying one "variable" in the code.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:19:05 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:19:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a4e5c59.eb24c6ea.27aedc.f246@mx.google.com>


================
@@ -6,602 +6,499 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench("std::basic_string::ctor(const value_type*)",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto ptr = str.data();
+            std::basic_string<CharT> copy(ptr);
+            benchmark::DoNotOptimize(copy);
+          }
+        });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string == std::basic_string (opaque)",
----------------
ldionne wrote:

I think the benchmarks for `::compare` are the ones closest to `operator<`.

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:19:06 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:19:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a4e5c5a.7270d2fd.2d4879.412c@mx.google.com>


================
@@ -6,602 +6,479 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
-
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
 }
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
-  }
-  __builtin_unreachable();
-}
-
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
 
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
-    }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-  }
+  });
+
+  bench(
+      "std::basic_string::ctor(const value_type*)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> str(state.range(), 'a');
+
+        for (auto _ : state) {
+          auto ptr = str.data();
+          std::basic_string<CharT> copy(ptr);
+          benchmark::DoNotOptimize(copy);
+        }
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(5)->Arg(30);
+    });
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(5)->Arg(30); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}));
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}));
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}));
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}));
   }
 
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(5)->Arg(30); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths differ.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(5)->Arg(30)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}));
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}));
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}));
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}));
   }
 
-  static bool skip() {
-    // Doesn't matter how they differ if they have different size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything different than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}));
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}));
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}));
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}));
   }
 
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::true_type{}));
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::true_type{}));
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 5>{}, std::false_type{}));
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 30>{}, std::false_type{}));
   }
 
-  static bool skip() {
-    // Huge does not give us anything that Large doesn't have. Skip it.
-    return Length() == ::Length::Huge;
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> src(size, 'a');
+
+          std::basic_string<CharT> strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string.compare(src));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const std::basic_string&) (opaque)",
----------------
ldionne wrote:

Can we say something like:

```
// These also cover basically the same code path as operator<
```

https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:22:15 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:22:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some unnecessary
 functions from __vector_layout (PR #207152)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207152@github.com>
Message-ID: <6a4e5d17.d1496011.26913c.4027@mx.google.com>


https://github.com/ldionne approved this pull request.

I think this makes sense. As discussed, you verified these didn't make any difference codegen wise, so I think we could simply not have introduced them in the first place, to keep the layout API minimal.

https://github.com/llvm/llvm-project/pull/207152

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:23:16 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:23:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Simplify a bunch of noexcept
	specifications (PR #166397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/166397@github.com>
Message-ID: <6a4e5d54.da8bafd3.14cd50.a973@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/166397

>From 9e75b0c7393d2b529779871c8d5491ea966da78e Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 4 Nov 2025 17:33:32 +0100
Subject: [PATCH] [libc++] Simplify a bunch of noexcept specifications

---
 libcxx/include/CMakeLists.txt                 |   1 -
 libcxx/include/__hash_table                   |  60 ++-----
 libcxx/include/__memory/allocator_traits.h    |   4 +
 .../__memory/noexcept_move_assign_container.h |  37 -----
 libcxx/include/__split_buffer                 |  37 ++---
 libcxx/include/__tree                         |  40 ++---
 libcxx/include/__vector/vector.h              |  64 ++------
 libcxx/include/__vector/vector_bool.h         |  70 ++------
 libcxx/include/deque                          |  51 ++----
 libcxx/include/forward_list                   |  65 ++------
 libcxx/include/list                           |  61 ++-----
 libcxx/include/queue                          |   1 +
 libcxx/include/stack                          |   1 +
 libcxx/include/string                         |  50 ++----
 .../associative/map/noexcept.compile.pass.cpp | 116 +++++++++++++
 .../move_noexcept.compile.pass.cpp}           |  29 +---
 .../move_noexcept.compile.pass.cpp}           |  29 +---
 .../list.cons/move_noexcept.compile.pass.cpp} |  26 +--
 .../move_noexcept.compile.pass.cpp            |  32 ++++
 .../vector.bool/noexcept.compile.pass.cpp     |  38 +++++
 .../vector/noexcept.compile.pass.cpp          |  38 +++++
 .../unord/unord.map/noexcept.compile.pass.cpp | 154 +++++++++++++++++
 .../unord.multimap/noexcept.compile.pass.cpp  | 155 ++++++++++++++++++
 .../unord.multiset/noexcept.compile.pass.cpp  | 154 +++++++++++++++++
 .../unord/unord.set/noexcept.compile.pass.cpp | 154 +++++++++++++++++
 .../flat.map.cons/move_noexcept.pass.cpp      |  29 ----
 .../flat.multimap.cons/move_noexcept.pass.cpp |  31 ----
 .../flat.multiset.cons/move.pass.cpp          |  19 ---
 .../flat.set/flat.set.cons/move.pass.cpp      |  16 +-
 .../deque.special/swap_noexcept.pass.cpp      |   6 +-
 .../swap_noexcept.compile.pass.cpp            |   6 +-
 .../list/list.special/swap_noexcept.pass.cpp  |   6 +-
 .../vector.bool/move_noexcept.pass.cpp        |  60 -------
 .../vector.bool/swap_noexcept.pass.cpp        |  11 --
 .../vector/vector.cons/move_noexcept.pass.cpp |   5 +-
 .../string.cons/move_noexcept.pass.cpp        |   4 +-
 36 files changed, 1008 insertions(+), 652 deletions(-)
 delete mode 100644 libcxx/include/__memory/noexcept_move_assign_container.h
 create mode 100644 libcxx/test/libcxx/containers/associative/map/noexcept.compile.pass.cpp
 rename libcxx/test/{std/containers/sequences/deque/deque.cons/move_noexcept.pass.cpp => libcxx/containers/sequences/deque/deque.cons/move_noexcept.compile.pass.cpp} (53%)
 rename libcxx/test/{std/containers/sequences/forwardlist/forwardlist.cons/move_noexcept.pass.cpp => libcxx/containers/sequences/forwardlist/move_noexcept.compile.pass.cpp} (53%)
 rename libcxx/test/{std/containers/sequences/list/list.cons/move_noexcept.pass.cpp => libcxx/containers/sequences/list/list.cons/move_noexcept.compile.pass.cpp} (55%)
 create mode 100644 libcxx/test/libcxx/containers/sequences/vector.bool/move_noexcept.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/sequences/vector.bool/noexcept.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/sequences/vector/noexcept.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.map/noexcept.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.multimap/noexcept.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.multiset/noexcept.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/unord/unord.set/noexcept.compile.pass.cpp
 delete mode 100644 libcxx/test/std/containers/sequences/vector.bool/move_noexcept.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..32726900fa3fd 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -605,7 +605,6 @@ set(files
   __memory/destruct_n.h
   __memory/inout_ptr.h
   __memory/is_sufficiently_aligned.h
-  __memory/noexcept_move_assign_container.h
   __memory/out_ptr.h
   __memory/pointer_traits.h
   __memory/ranges_construct_at.h
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 0ce4cf3edd6fd..fa7503d45e712 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -522,13 +522,13 @@ public:
   _LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator() _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value)
       : __size_(0) {}
 
-  _LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator(const allocator_type& __a, size_type __size)
-      _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value)
-      : __size_(__size), __alloc_(__a) {}
+  _LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator(const allocator_type& __a, size_type __size) _NOEXCEPT
+      : __size_(__size),
+        __alloc_(__a) {}
 
-  _LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator(__bucket_list_deallocator&& __x)
-      _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
-      : __size_(std::__exchange(__x.__size_, 0)), __alloc_(std::move(__x.__alloc_)) {}
+  _LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator(__bucket_list_deallocator&& __x) _NOEXCEPT
+      : __size_(std::__exchange(__x.__size_, 0)),
+        __alloc_(std::move(__x.__alloc_)) {}
 
   _LIBCPP_HIDE_FROM_ABI size_type& size() _NOEXCEPT { return __size_; }
   _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __size_; }
@@ -754,17 +754,14 @@ public:
   _LIBCPP_HIDE_FROM_ABI __hash_table(const __hash_table& __u, const allocator_type& __a);
   _LIBCPP_HIDE_FROM_ABI __hash_table(__hash_table&& __u) _NOEXCEPT_(
       is_nothrow_move_constructible<__bucket_list>::value&& is_nothrow_move_constructible<__first_node>::value&&
-          is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<hasher>::value&&
-              is_nothrow_move_constructible<key_equal>::value);
+          is_nothrow_move_constructible<hasher>::value&& is_nothrow_move_constructible<key_equal>::value);
   _LIBCPP_HIDE_FROM_ABI __hash_table(__hash_table&& __u, const allocator_type& __a);
   _LIBCPP_HIDE_FROM_ABI ~__hash_table();
 
   _LIBCPP_HIDE_FROM_ABI __hash_table& operator=(const __hash_table& __u);
   _LIBCPP_HIDE_FROM_ABI __hash_table& operator=(__hash_table&& __u)
-      _NOEXCEPT_(is_nothrow_move_assignable<hasher>::value&& is_nothrow_move_assignable<key_equal>::value &&
-                 ((__node_traits::propagate_on_container_move_assignment::value &&
-                   is_nothrow_move_assignable<__node_allocator>::value) ||
-                  allocator_traits<__node_allocator>::is_always_equal::value));
+      _NOEXCEPT_(is_nothrow_move_assignable<hasher>::value&& is_nothrow_move_assignable<key_equal>::value&&
+                     __is_allocator_aware_container_move_nothrow_v<allocator_type>);
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI void __assign_unique(_InputIterator __first, _InputIterator __last);
   template <class _InputIterator>
@@ -958,14 +955,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI pair<const_iterator, const_iterator> __equal_range_multi(const _Key& __k) const;
 
   _LIBCPP_HIDE_FROM_ABI void swap(__hash_table& __u)
-#if _LIBCPP_STD_VER <= 11
-      _NOEXCEPT_(__is_nothrow_swappable_v<hasher>&& __is_nothrow_swappable_v<key_equal> &&
-                 (!allocator_traits<__pointer_allocator>::propagate_on_container_swap::value ||
-                  __is_nothrow_swappable_v<__pointer_allocator>) &&
-                 (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>));
-#else
       _NOEXCEPT_(__is_nothrow_swappable_v<hasher>&& __is_nothrow_swappable_v<key_equal>);
-#endif
 
   _LIBCPP_HIDE_FROM_ABI size_type max_bucket_count() const _NOEXCEPT { return max_size(); }
   _LIBCPP_HIDE_FROM_ABI size_type bucket_size(size_type __n) const;
@@ -1025,15 +1015,11 @@ private:
 
   _LIBCPP_HIDE_FROM_ABI void __move_assign(__hash_table& __u, false_type);
   _LIBCPP_HIDE_FROM_ABI void __move_assign(__hash_table& __u, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value&& is_nothrow_move_assignable<hasher>::value&&
-                     is_nothrow_move_assignable<key_equal>::value);
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__hash_table& __u) _NOEXCEPT_(
-      !__node_traits::propagate_on_container_move_assignment::value ||
-      (is_nothrow_move_assignable<__pointer_allocator>::value && is_nothrow_move_assignable<__node_allocator>::value)) {
+      _NOEXCEPT_(is_nothrow_move_assignable<hasher>::value&& is_nothrow_move_assignable<key_equal>::value);
+  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__hash_table& __u) _NOEXCEPT {
     __move_assign_alloc(__u, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   }
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__hash_table& __u, true_type) _NOEXCEPT_(
-      is_nothrow_move_assignable<__pointer_allocator>::value&& is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__hash_table& __u, true_type) _NOEXCEPT {
     __bucket_list_.get_deleter().__alloc() = std::move(__u.__bucket_list_.get_deleter().__alloc());
     __node_alloc()                         = std::move(__u.__node_alloc());
   }
@@ -1146,8 +1132,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__hash_table(const __hash_table& __u,
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 __hash_table<_Tp, _Hash, _Equal, _Alloc>::__hash_table(__hash_table&& __u) _NOEXCEPT_(
     is_nothrow_move_constructible<__bucket_list>::value&& is_nothrow_move_constructible<__first_node>::value&&
-        is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<hasher>::value&&
-            is_nothrow_move_constructible<key_equal>::value)
+        is_nothrow_move_constructible<hasher>::value&& is_nothrow_move_constructible<key_equal>::value)
     : __bucket_list_(std::move(__u.__bucket_list_)),
       __first_node_(std::move(__u.__first_node_)),
       __node_alloc_(std::move(__u.__node_alloc_)),
@@ -1292,8 +1277,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__detach() _NOEXCEPT {
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__move_assign(__hash_table& __u, true_type)
-    _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value&& is_nothrow_move_assignable<hasher>::value&&
-                   is_nothrow_move_assignable<key_equal>::value) {
+    _NOEXCEPT_(is_nothrow_move_assignable<hasher>::value&& is_nothrow_move_assignable<key_equal>::value) {
   clear();
   __bucket_list_.reset(__u.__bucket_list_.release());
   __bucket_list_.get_deleter().size()     = __u.__bucket_list_.get_deleter().size();
@@ -1338,10 +1322,8 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__move_assign(__hash_table& __u,
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 inline __hash_table<_Tp, _Hash, _Equal, _Alloc>& __hash_table<_Tp, _Hash, _Equal, _Alloc>::operator=(__hash_table&& __u)
-    _NOEXCEPT_(is_nothrow_move_assignable<hasher>::value&& is_nothrow_move_assignable<key_equal>::value &&
-               ((__node_traits::propagate_on_container_move_assignment::value &&
-                 is_nothrow_move_assignable<__node_allocator>::value) ||
-                allocator_traits<__node_allocator>::is_always_equal::value)) {
+    _NOEXCEPT_(is_nothrow_move_assignable<hasher>::value&& is_nothrow_move_assignable<key_equal>::value&&
+                   __is_allocator_aware_container_move_nothrow_v<allocator_type>) {
   __move_assign(__u, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   return *this;
 }
@@ -2074,15 +2056,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__equal_range_multi(const _Key& __k) c
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 void __hash_table<_Tp, _Hash, _Equal, _Alloc>::swap(__hash_table& __u)
-#if _LIBCPP_STD_VER <= 11
-    _NOEXCEPT_(__is_nothrow_swappable_v<hasher>&& __is_nothrow_swappable_v<key_equal> &&
-               (!allocator_traits<__pointer_allocator>::propagate_on_container_swap::value ||
-                __is_nothrow_swappable_v<__pointer_allocator>) &&
-               (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>))
-#else
-    _NOEXCEPT_(__is_nothrow_swappable_v<hasher>&& __is_nothrow_swappable_v<key_equal>)
-#endif
-{
+    _NOEXCEPT_(__is_nothrow_swappable_v<hasher>&& __is_nothrow_swappable_v<key_equal>) {
   _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
       __node_traits::propagate_on_container_swap::value || this->__node_alloc() == __u.__node_alloc(),
       "unordered container::swap: Either propagate_on_container_swap "
diff --git a/libcxx/include/__memory/allocator_traits.h b/libcxx/include/__memory/allocator_traits.h
index 7038885a8b24f..d32e167ed833f 100644
--- a/libcxx/include/__memory/allocator_traits.h
+++ b/libcxx/include/__memory/allocator_traits.h
@@ -436,6 +436,10 @@ inline const bool __is_cpp17_copy_insertable_v =
      (!__is_std_allocator_v<_Alloc> &&
       __has_construct_v<_Alloc, typename _Alloc::value_type*, const typename _Alloc::value_type&>));
 
+template <typename _Alloc, typename _Traits = allocator_traits<_Alloc> >
+inline const bool __is_allocator_aware_container_move_nothrow_v =
+    _Traits::propagate_on_container_move_assignment::value || _Traits::is_always_equal::value;
+
 _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
diff --git a/libcxx/include/__memory/noexcept_move_assign_container.h b/libcxx/include/__memory/noexcept_move_assign_container.h
deleted file mode 100644
index b0063516aaafc..0000000000000
--- a/libcxx/include/__memory/noexcept_move_assign_container.h
+++ /dev/null
@@ -1,37 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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___MEMORY_NOEXCEPT_MOVE_ASSIGN_CONTAINER_H
-#define _LIBCPP___MEMORY_NOEXCEPT_MOVE_ASSIGN_CONTAINER_H
-
-#include <__config>
-#include <__memory/allocator_traits.h>
-#include <__type_traits/integral_constant.h>
-#include <__type_traits/is_nothrow_assignable.h>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-template <typename _Alloc, typename _Traits = allocator_traits<_Alloc> >
-struct __noexcept_move_assign_container
-    : public integral_constant<bool,
-                               _Traits::propagate_on_container_move_assignment::value
-#if _LIBCPP_STD_VER >= 17
-                                   || _Traits::is_always_equal::value
-#else
-                                   && is_nothrow_move_assignable<_Alloc>::value
-#endif
-                               > {
-};
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP___MEMORY_NOEXCEPT_MOVE_ASSIGN_CONTAINER_H
diff --git a/libcxx/include/__split_buffer b/libcxx/include/__split_buffer
index 1d50486947f08..31915540f06b9 100644
--- a/libcxx/include/__split_buffer
+++ b/libcxx/include/__split_buffer
@@ -29,8 +29,6 @@
 #include <__type_traits/conditional.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/integral_constant.h>
-#include <__type_traits/is_nothrow_assignable.h>
-#include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/is_swappable.h>
 #include <__type_traits/is_trivially_destructible.h>
 #include <__type_traits/is_trivially_relocatable.h>
@@ -181,8 +179,7 @@ public:
   }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
-  __copy_without_alloc(__split_buffer_pointer_layout const& __other)
-      _NOEXCEPT_(is_nothrow_copy_assignable<pointer>::value) {
+  __copy_without_alloc(__split_buffer_pointer_layout const& __other) _NOEXCEPT {
     __front_cap_ = __other.__front_cap_;
     __begin_     = __other.__begin_;
     __end_       = __other.__end_;
@@ -388,8 +385,7 @@ public:
   }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
-  __copy_without_alloc(__split_buffer_size_layout const& __other)
-      _NOEXCEPT_(is_nothrow_copy_assignable<pointer>::value) {
+  __copy_without_alloc(__split_buffer_size_layout const& __other) _NOEXCEPT {
     __front_cap_ = __other.__front_cap_;
     __begin_     = __other.__begin_;
     __cap_       = __other.__cap_;
@@ -607,15 +603,11 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI
   __split_buffer(size_type __cap, size_type __start, allocator_type& __a);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __split_buffer(__split_buffer&& __c)
-      _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __split_buffer(__split_buffer&& __c) _NOEXCEPT;
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __split_buffer(__split_buffer&& __c, const allocator_type& __a);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __split_buffer& operator=(__split_buffer&& __c)
-      _NOEXCEPT_((__alloc_traits::propagate_on_container_move_assignment::value &&
-                  is_nothrow_move_assignable<allocator_type>::value) ||
-                 !__alloc_traits::propagate_on_container_move_assignment::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __split_buffer& operator=(__split_buffer&& __c) _NOEXCEPT;
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI ~__split_buffer();
 
@@ -669,8 +661,7 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __destruct_at_end(pointer __new_last, false_type) _NOEXCEPT;
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __destruct_at_end(pointer __new_last, true_type) _NOEXCEPT;
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void swap(__split_buffer& __x)
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void swap(__split_buffer& __x) _NOEXCEPT;
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __invariants() const {
     if (__front_cap() == nullptr) {
@@ -704,8 +695,8 @@ public:
   }
 
 private:
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__split_buffer& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
+  __move_assign_alloc(__split_buffer& __c, true_type) _NOEXCEPT {
     __get_allocator() = std::move(__c.__get_allocator());
   }
 
@@ -850,8 +841,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 __split_buffer<_Tp, _Allocator, _Layout>::~__split
 }
 
 template <class _Tp, class _Allocator, template <class, class, class> class _Layout>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 __split_buffer<_Tp, _Allocator, _Layout>::__split_buffer(__split_buffer&& __c)
-    _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
+_LIBCPP_CONSTEXPR_SINCE_CXX20 __split_buffer<_Tp, _Allocator, _Layout>::__split_buffer(__split_buffer&& __c) _NOEXCEPT
     : __base_type(std::move(__c)) {
   __c.__reset();
 }
@@ -877,10 +867,7 @@ __split_buffer<_Tp, _Allocator, _Layout>::__split_buffer(__split_buffer&& __c, c
 
 template <class _Tp, class _Allocator, template <class, class, class> class _Layout>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 __split_buffer<_Tp, _Allocator, _Layout>&
-__split_buffer<_Tp, _Allocator, _Layout>::operator=(__split_buffer&& __c)
-    _NOEXCEPT_((__alloc_traits::propagate_on_container_move_assignment::value &&
-                is_nothrow_move_assignable<allocator_type>::value) ||
-               !__alloc_traits::propagate_on_container_move_assignment::value) {
+__split_buffer<_Tp, _Allocator, _Layout>::operator=(__split_buffer&& __c) _NOEXCEPT {
   clear();
   shrink_to_fit();
   __copy_without_alloc(__c);
@@ -890,8 +877,7 @@ __split_buffer<_Tp, _Allocator, _Layout>::operator=(__split_buffer&& __c)
 }
 
 template <class _Tp, class _Allocator, template <class, class, class> class _Layout>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator, _Layout>::swap(__split_buffer& __x)
-    _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>) {
+_LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator, _Layout>::swap(__split_buffer& __x) _NOEXCEPT {
   __base_type::swap(__x);
 }
 
@@ -962,8 +948,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator, _Layout>::emp
 
 template <class _Tp, class _Allocator, template <class, class, class> class _Layout>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI void
-swap(__split_buffer<_Tp, _Allocator, _Layout>& __x, __split_buffer<_Tp, _Allocator, _Layout>& __y)
-    _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
+swap(__split_buffer<_Tp, _Allocator, _Layout>& __x, __split_buffer<_Tp, _Allocator, _Layout>& __y) _NOEXCEPT {
   __x.swap(__y);
 }
 
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index dafeff3e2e019..4965e35571b47 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1021,15 +1021,13 @@ public:
   template <class _ForwardIterator>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   __assign_unique(_ForwardIterator __first, _ForwardIterator __last);
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t) _NOEXCEPT_(
-      is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t)
+      _NOEXCEPT_(is_nothrow_move_constructible<value_compare>::value);
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t, const allocator_type& __a);
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree& operator=(__tree&& __t)
-      _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value &&
-                 ((__node_traits::propagate_on_container_move_assignment::value &&
-                   is_nothrow_move_assignable<__node_allocator>::value) ||
-                  allocator_traits<__node_allocator>::is_always_equal::value)) {
+      _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value&&
+                     __is_allocator_aware_container_move_nothrow_v<__node_allocator>) {
     __move_assign(__t,
                   integral_constant<bool,
                                     __node_traits::propagate_on_container_move_assignment::value ||
@@ -1058,12 +1056,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT;
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(__tree& __t)
-#if _LIBCPP_STD_VER <= 11
-      _NOEXCEPT_(__is_nothrow_swappable_v<value_compare> &&
-                 (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>));
-#else
       _NOEXCEPT_(__is_nothrow_swappable_v<value_compare>);
-#endif
 
   template <class... _Args>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __emplace_multi(_Args&&... __args);
@@ -1472,17 +1465,14 @@ private:
   }
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, false_type);
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, true_type) _NOEXCEPT_(
-      is_nothrow_move_assignable<value_compare>::value&& is_nothrow_move_assignable<__node_allocator>::value);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, true_type)
+      _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value);
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t)
-      _NOEXCEPT_(!__node_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t) _NOEXCEPT {
     __move_assign_alloc(__t, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t, true_type) _NOEXCEPT {
     __node_alloc() = std::move(__t.__node_alloc());
   }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree&, false_type) _NOEXCEPT {}
@@ -1727,8 +1717,8 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(const __
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
-    is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t)
+    _NOEXCEPT_(is_nothrow_move_constructible<value_compare>::value)
     : __begin_node_(std::move(__t.__begin_node_)),
       __end_node_(std::move(__t.__end_node_)),
       __node_alloc_(std::move(__t.__node_alloc_)),
@@ -1771,7 +1761,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&&
 
 template <class _Tp, class _Compare, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
-    _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value&& is_nothrow_move_assignable<__node_allocator>::value) {
+    _NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value) {
   destroy(std::__static_fancy_pointer_cast<__node_pointer>(__end_node()->__left_));
   __begin_node_ = __t.__begin_node_;
   __end_node_   = __t.__end_node_;
@@ -1811,13 +1801,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__move_ass
 
 template <class _Tp, class _Compare, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t)
-#if _LIBCPP_STD_VER <= 11
-    _NOEXCEPT_(__is_nothrow_swappable_v<value_compare> &&
-               (!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>))
-#else
-    _NOEXCEPT_(__is_nothrow_swappable_v<value_compare>)
-#endif
-{
+    _NOEXCEPT_(__is_nothrow_swappable_v<value_compare>) {
   using std::swap;
   swap(__begin_node_, __t.__begin_node_);
   swap(__end_node_, __t.__end_node_);
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..c66b2fce60d06 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -36,7 +36,6 @@
 #include <__memory/allocator.h>
 #include <__memory/allocator_traits.h>
 #include <__memory/compressed_pair.h>
-#include <__memory/noexcept_move_assign_container.h>
 #include <__memory/pointer_traits.h>
 #include <__memory/swap_allocator.h>
 #include <__memory/temp_value.h>
@@ -52,7 +51,6 @@
 #include <__type_traits/is_allocator.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_constructible.h>
-#include <__type_traits/is_nothrow_assignable.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
@@ -135,14 +133,8 @@ class vector {
   //
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector()
       _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value) {}
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI explicit vector(const allocator_type& __a)
-#if _LIBCPP_STD_VER <= 14
-      _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value)
-#else
-      noexcept
-#endif
-      : __layout_(__a) {
-  }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20
+  _LIBCPP_HIDE_FROM_ABI explicit vector(const allocator_type& __a) _NOEXCEPT : __layout_(__a) {}
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI explicit vector(size_type __n) {
     auto __guard = std::__make_exception_guard(__destroy_vector(*this));
@@ -290,17 +282,11 @@ class vector {
   }
 #endif // !_LIBCPP_CXX03_LANG
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector(vector&& __x)
-#if _LIBCPP_STD_VER >= 17
-      noexcept;
-#else
-      _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value);
-#endif
-
-  _LIBCPP_CONSTEXPR_SINCE_CXX20
-  _LIBCPP_HIDE_FROM_ABI vector(vector&& __x, const __type_identity_t<allocator_type>& __a);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector(vector&& __x) _NOEXCEPT;
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI
+  vector(vector&& __x, const __type_identity_t<allocator_type>& __a);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector& operator=(vector&& __x)
-      _NOEXCEPT_(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value) {
+      _NOEXCEPT_(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
     __move_assign(__x, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
     return *this;
   }
@@ -566,12 +552,7 @@ class vector {
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void resize(size_type __sz);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void resize(size_type __sz, const_reference __x);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void swap(vector&)
-#if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT;
-#else
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>);
-#endif
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void swap(vector&) _NOEXCEPT;
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __invariants() const _NOEXCEPT {
     return __layout_.__invariants();
@@ -709,8 +690,7 @@ class vector {
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
   __move_range(pointer __from_s, pointer __from_e, pointer __to);
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign(vector& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign(vector& __c, true_type) _NOEXCEPT;
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign(vector& __c, false_type)
       _NOEXCEPT_(__alloc_traits::is_always_equal::value);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __destruct_at_end(pointer __new_last) _NOEXCEPT {
@@ -783,9 +763,7 @@ class vector {
     __copy_assign_alloc(__c, integral_constant<bool, __alloc_traits::propagate_on_container_copy_assignment::value>());
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(vector& __c)
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(vector& __c) _NOEXCEPT {
     __move_assign_alloc(__c, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
   }
 
@@ -805,8 +783,7 @@ class vector {
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const vector&, false_type) {}
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(vector& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(vector& __c, true_type) _NOEXCEPT {
     this->__layout_.__alloc() = std::move(__c.__layout_.__alloc());
   }
 
@@ -912,14 +889,8 @@ vector<_Tp, _Allocator>::__construct_at_end(_InputIterator __first, _Sentinel __
 }
 
 template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI vector<_Tp, _Allocator>::vector(vector&& __x)
-#if _LIBCPP_STD_VER >= 17
-    noexcept
-#else
-    _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
-#endif
-    : __layout_(std::move(__x.__layout_)) {
-}
+_LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI vector<_Tp, _Allocator>::vector(vector&& __x) _NOEXCEPT
+    : __layout_(std::move(__x.__layout_)) {}
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI
@@ -944,8 +915,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__move_assign(vector
 }
 
 template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__move_assign(vector& __c, true_type)
-    _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__move_assign(vector& __c, true_type) _NOEXCEPT {
   __vdeallocate();
   __move_assign_alloc(__c); // this can throw
   __layout_.__move_assign_without_allocator(__c.__layout_);
@@ -1338,13 +1308,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::resize(size_type __n
 }
 
 template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::swap(vector& __x)
-#if _LIBCPP_STD_VER >= 14
-    _NOEXCEPT
-#else
-    _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>)
-#endif
-{
+_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::swap(vector& __x) _NOEXCEPT {
   _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
       __alloc_traits::propagate_on_container_swap::value || __layout_.__alloc() == __x.__layout_.__alloc(),
       "vector::swap: Either propagate_on_container_swap must be true"
diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index f73e086478a70..84824239f8c54 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -127,12 +127,7 @@ class vector<bool, _Allocator> {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector()
       _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value);
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit vector(const allocator_type& __a)
-#if _LIBCPP_STD_VER <= 14
-      _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value);
-#else
-      _NOEXCEPT;
-#endif
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit vector(const allocator_type& __a) _NOEXCEPT;
 
 private:
   class __destroy_vector {
@@ -199,16 +194,11 @@ class vector<bool, _Allocator> {
 
 #endif // !_LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector(vector&& __v)
-#if _LIBCPP_STD_VER >= 17
-      noexcept;
-#else
-      _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value);
-#endif
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector(vector&& __v) _NOEXCEPT;
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
   vector(vector&& __v, const __type_identity_t<allocator_type>& __a);
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector& operator=(vector&& __v)
-      _NOEXCEPT_(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value);
+      _NOEXCEPT_(__is_allocator_aware_container_move_nothrow_v<allocator_type>);
 
   template <class _InputIterator, __enable_if_t<__has_exactly_input_iterator_category<_InputIterator>::value, int> = 0>
   void _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 assign(_InputIterator __first, _InputIterator __last);
@@ -393,12 +383,7 @@ class vector<bool, _Allocator> {
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void clear() _NOEXCEPT { __size_ = 0; }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(vector&)
-#if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT;
-#else
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>);
-#endif
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(vector&) _NOEXCEPT;
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void swap(reference __x, reference __y) _NOEXCEPT {
     std::swap(__x, __y);
   }
@@ -513,16 +498,12 @@ class vector<bool, _Allocator> {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __copy_assign_alloc(const vector&, false_type) {}
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign(vector& __c, false_type);
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign(vector& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value);
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(vector& __c)
-      _NOEXCEPT_(!__storage_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign(vector& __c, true_type) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(vector& __c) _NOEXCEPT {
     __move_assign_alloc(
         __c, integral_constant<bool, __storage_traits::propagate_on_container_move_assignment::value>());
   }
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(vector& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(vector& __c, true_type) _NOEXCEPT {
     __alloc_ = std::move(__c.__alloc_);
   }
 
@@ -586,14 +567,12 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocat
     : __begin_(nullptr), __size_(0), __cap_(0) {}
 
 template <class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(const allocator_type& __a)
-#if _LIBCPP_STD_VER <= 14
-    _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value)
-#else
-        _NOEXCEPT
-#endif
-    : __begin_(nullptr), __size_(0), __cap_(0), __alloc_(static_cast<__storage_allocator>(__a)) {
-}
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+vector<bool, _Allocator>::vector(const allocator_type& __a) _NOEXCEPT
+    : __begin_(nullptr),
+      __size_(0),
+      __cap_(0),
+      __alloc_(static_cast<__storage_allocator>(__a)) {}
 
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(size_type __n)
@@ -735,17 +714,11 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>& vector<bool, _Allocator>
 }
 
 template <class _Allocator>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(vector&& __v)
-#if _LIBCPP_STD_VER >= 17
-    _NOEXCEPT
-#else
-    _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
-#endif
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(vector&& __v) _NOEXCEPT
     : __begin_(std::__exchange(__v.__begin_, nullptr)),
       __size_(std::__exchange(__v.__size_, 0)),
       __cap_(std::__exchange(__v.__cap_, 0)),
-      __alloc_(std::move(__v.__alloc_)) {
-}
+      __alloc_(std::move(__v.__alloc_)) {}
 
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20
@@ -765,7 +738,7 @@ vector<bool, _Allocator>::vector(vector&& __v, const __type_identity_t<allocator
 template <class _Allocator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>&
 vector<bool, _Allocator>::operator=(vector&& __v)
-    _NOEXCEPT_(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value) {
+    _NOEXCEPT_(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
   __move_assign(__v, integral_constant<bool, __storage_traits::propagate_on_container_move_assignment::value>());
   return *this;
 }
@@ -779,8 +752,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::__move_assign(vecto
 }
 
 template <class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::__move_assign(vector& __c, true_type)
-    _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::__move_assign(vector& __c, true_type) _NOEXCEPT {
   __vdeallocate();
   __move_assign_alloc(__c);
   __begin_ = std::__exchange(__c.__begin_, nullptr);
@@ -1030,13 +1002,7 @@ vector<bool, _Allocator>::erase(const_iterator __first, const_iterator __last) {
 }
 
 template <class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::swap(vector& __x)
-#if _LIBCPP_STD_VER >= 14
-    _NOEXCEPT
-#else
-    _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>)
-#endif
-{
+_LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::swap(vector& __x) _NOEXCEPT {
   std::swap(this->__begin_, __x.__begin_);
   std::swap(this->__size_, __x.__size_);
   std::swap(this->__cap_, __x.__cap_);
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 7aee552c63d37..dd06349d16a96 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -226,7 +226,6 @@ template <class T, class Allocator, class Predicate>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/is_allocator.h>
 #  include <__type_traits/is_convertible.h>
-#  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
 #  include <__type_traits/is_same.h>
 #  include <__type_traits/is_swappable.h>
@@ -553,9 +552,6 @@ public:
   static_assert(is_nothrow_default_constructible<allocator_type>::value ==
                     is_nothrow_default_constructible<__pointer_allocator>::value,
                 "rebinding an allocator should not change exception guarantees");
-  static_assert(is_nothrow_move_constructible<allocator_type>::value ==
-                    is_nothrow_move_constructible<typename __map::allocator_type>::value,
-                "rebinding an allocator should not change exception guarantees");
 
 private:
   struct __deque_block_range {
@@ -691,12 +687,10 @@ public:
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI deque(deque&& __c) noexcept(is_nothrow_move_constructible<allocator_type>::value);
+  _LIBCPP_HIDE_FROM_ABI deque(deque&& __c) noexcept;
   _LIBCPP_HIDE_FROM_ABI deque(deque&& __c, const __type_identity_t<allocator_type>& __a);
-  _LIBCPP_HIDE_FROM_ABI deque& operator=(deque&& __c) noexcept(
-      (__alloc_traits::propagate_on_container_move_assignment::value &&
-       is_nothrow_move_assignable<allocator_type>::value) ||
-      __alloc_traits::is_always_equal::value);
+  _LIBCPP_HIDE_FROM_ABI deque&
+  operator=(deque&& __c) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>);
 
   _LIBCPP_HIDE_FROM_ABI void assign(initializer_list<value_type> __il) { assign(__il.begin(), __il.end()); }
 #  endif // _LIBCPP_CXX03_LANG
@@ -888,12 +882,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p);
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l);
 
-  _LIBCPP_HIDE_FROM_ABI void swap(deque& __c)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT;
-#  else
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>);
-#  endif
+  _LIBCPP_HIDE_FROM_ABI void swap(deque& __c) _NOEXCEPT;
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
 
   _LIBCPP_HIDE_FROM_ABI bool __invariants() const {
@@ -918,22 +907,18 @@ public:
     return true;
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(deque& __c)
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(deque& __c) _NOEXCEPT {
     __move_assign_alloc(__c, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(deque& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(deque& __c, true_type) _NOEXCEPT {
     __alloc() = std::move(__c.__alloc());
   }
 
   _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(deque&, false_type) _NOEXCEPT {}
 
   _LIBCPP_HIDE_FROM_ABI void __move_assign(deque& __c)
-      _NOEXCEPT_(__alloc_traits::propagate_on_container_move_assignment::value&&
-                     is_nothrow_move_assignable<allocator_type>::value) {
+      _NOEXCEPT_(__alloc_traits::propagate_on_container_move_assignment::value) {
     __map_   = std::move(__c.__map_);
     __start_ = __c.__start_;
     __size() = __c.size();
@@ -1286,8 +1271,7 @@ private:
 
   _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const deque&, false_type) {}
 
-  _LIBCPP_HIDE_FROM_ABI void __move_assign(deque& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value);
+  _LIBCPP_HIDE_FROM_ABI void __move_assign(deque& __c, true_type) _NOEXCEPT;
   _LIBCPP_HIDE_FROM_ABI void __move_assign(deque& __c, false_type);
 };
 
@@ -1397,7 +1381,7 @@ deque<_Tp, _Allocator>::deque(initializer_list<value_type> __il, const allocator
 }
 
 template <class _Tp, class _Allocator>
-inline deque<_Tp, _Allocator>::deque(deque&& __c) noexcept(is_nothrow_move_constructible<allocator_type>::value)
+inline deque<_Tp, _Allocator>::deque(deque&& __c) noexcept
     : __map_(std::move(__c.__map_)),
       __start_(std::move(__c.__start_)),
       __size_(std::move(__c.__size_)),
@@ -1425,10 +1409,8 @@ inline deque<_Tp, _Allocator>::deque(deque&& __c, const __type_identity_t<alloca
 }
 
 template <class _Tp, class _Allocator>
-inline deque<_Tp, _Allocator>& deque<_Tp, _Allocator>::operator=(deque&& __c) noexcept(
-    (__alloc_traits::propagate_on_container_move_assignment::value &&
-     is_nothrow_move_assignable<allocator_type>::value) ||
-    __alloc_traits::is_always_equal::value) {
+inline deque<_Tp, _Allocator>&
+deque<_Tp, _Allocator>::operator=(deque&& __c) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
   __move_assign(__c, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
   return *this;
 }
@@ -1443,8 +1425,7 @@ void deque<_Tp, _Allocator>::__move_assign(deque& __c, false_type) {
 }
 
 template <class _Tp, class _Allocator>
-void deque<_Tp, _Allocator>::__move_assign(deque& __c,
-                                           true_type) noexcept(is_nothrow_move_assignable<allocator_type>::value) {
+void deque<_Tp, _Allocator>::__move_assign(deque& __c, true_type) noexcept {
   clear();
   shrink_to_fit();
   __move_assign(__c);
@@ -2414,13 +2395,7 @@ void deque<_Tp, _Allocator>::__erase_to_end(const_iterator __f) {
 }
 
 template <class _Tp, class _Allocator>
-inline void deque<_Tp, _Allocator>::swap(deque& __c)
-#  if _LIBCPP_STD_VER >= 14
-    _NOEXCEPT
-#  else
-    _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>)
-#  endif
-{
+inline void deque<_Tp, _Allocator>::swap(deque& __c) _NOEXCEPT {
   __map_.swap(__c.__map_);
   std::swap(__start_, __c.__start_);
   std::swap(__size(), __c.__size());
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index 6d86f7a8e5975..767a959bf8866 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -227,7 +227,6 @@ template <class T, class Allocator, class Predicate>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/is_allocator.h>
 #  include <__type_traits/is_const.h>
-#  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
 #  include <__type_traits/is_same.h>
 #  include <__type_traits/is_swappable.h>
@@ -499,8 +498,7 @@ protected:
 
 public:
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
-  __forward_list_base(__forward_list_base&& __x) noexcept(is_nothrow_move_constructible<__node_allocator>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI __forward_list_base(__forward_list_base&& __x) noexcept;
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
   __forward_list_base(__forward_list_base&& __x, const allocator_type& __a);
 #  endif // _LIBCPP_CXX03_LANG
@@ -515,9 +513,7 @@ protected:
     __copy_assign_alloc(__x, integral_constant<bool, __node_traits::propagate_on_container_copy_assignment::value>());
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__forward_list_base& __x)
-      _NOEXCEPT_(!__node_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__forward_list_base& __x) _NOEXCEPT {
     __move_assign_alloc(__x, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   }
 
@@ -547,12 +543,7 @@ protected:
   }
 
 public:
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(__forward_list_base& __x)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT;
-#  else
-      _NOEXCEPT_(!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>);
-#  endif
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(__forward_list_base& __x) _NOEXCEPT;
 
 protected:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
@@ -569,8 +560,8 @@ private:
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void
   __move_assign_alloc(__forward_list_base&, false_type) _NOEXCEPT {}
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__forward_list_base& __x, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void
+  __move_assign_alloc(__forward_list_base& __x, true_type) _NOEXCEPT {
     __alloc_ = std::move(__x.__alloc_);
   }
 };
@@ -579,7 +570,7 @@ private:
 
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 inline __forward_list_base<_Tp, _Alloc>::__forward_list_base(
-    __forward_list_base&& __x) noexcept(is_nothrow_move_constructible<__node_allocator>::value)
+    __forward_list_base&& __x) noexcept
     : __before_begin_(std::move(__x.__before_begin_)), __alloc_(std::move(__x.__alloc_)) {
   __x.__before_begin()->__next_ = nullptr;
 }
@@ -602,13 +593,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 __forward_list_base<_Tp, _Alloc>::~__forward_list_
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 inline void __forward_list_base<_Tp, _Alloc>::swap(__forward_list_base& __x)
-#  if _LIBCPP_STD_VER >= 14
-    _NOEXCEPT
-#  else
-    _NOEXCEPT_(!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>)
-#  endif
-{
+_LIBCPP_CONSTEXPR_SINCE_CXX26 inline void __forward_list_base<_Tp, _Alloc>::swap(__forward_list_base& __x) _NOEXCEPT {
   std::__swap_allocator(__alloc_, __x.__alloc_);
   using std::swap;
   swap(__before_begin()->__next_, __x.__before_begin()->__next_);
@@ -699,8 +684,7 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI forward_list& operator=(const forward_list& __x);
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
-  forward_list(forward_list&& __x) noexcept(is_nothrow_move_constructible<__base>::value)
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI forward_list(forward_list&& __x) noexcept
       : __base(std::move(__x)) {}
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
   forward_list(forward_list&& __x, const __type_identity_t<allocator_type>& __a);
@@ -709,10 +693,8 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
   forward_list(initializer_list<value_type> __il, const allocator_type& __a);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI forward_list& operator=(forward_list&& __x) noexcept(
-      (__node_traits::propagate_on_container_move_assignment::value &&
-       is_nothrow_move_assignable<allocator_type>::value) ||
-      allocator_traits<allocator_type>::is_always_equal::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI forward_list&
+  operator=(forward_list&& __x) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>);
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI forward_list& operator=(initializer_list<value_type> __il);
 
@@ -838,15 +820,7 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI iterator erase_after(const_iterator __p);
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI iterator erase_after(const_iterator __f, const_iterator __l);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(forward_list& __x)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT
-#  else
-      _NOEXCEPT_(!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>)
-#  endif
-  {
-    __base::swap(__x);
-  }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(forward_list& __x) _NOEXCEPT { __base::swap(__x); }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void resize(size_type __n);
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void resize(size_type __n, const value_type& __v);
@@ -885,8 +859,7 @@ public:
 
 private:
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign(forward_list& __x, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign(forward_list& __x, true_type) _NOEXCEPT;
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign(forward_list& __x, false_type);
 #  endif // _LIBCPP_CXX03_LANG
 
@@ -1019,8 +992,7 @@ forward_list<_Tp, _Alloc>::forward_list(initializer_list<value_type> __il, const
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 void forward_list<_Tp, _Alloc>::__move_assign(forward_list& __x, true_type)
-    _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void forward_list<_Tp, _Alloc>::__move_assign(forward_list& __x, true_type) _NOEXCEPT {
   clear();
   __base::__move_assign_alloc(__x);
   __base::__before_begin()->__next_ = std::__exchange(__x.__before_begin()->__next_, nullptr);
@@ -1037,11 +1009,8 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void forward_list<_Tp, _Alloc>::__move_assign(forw
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 inline forward_list<_Tp, _Alloc>&
-forward_list<_Tp, _Alloc>::operator=(forward_list&& __x) noexcept(
-    (__node_traits::propagate_on_container_move_assignment::value &&
-     is_nothrow_move_assignable<allocator_type>::value) ||
-    allocator_traits<allocator_type>::is_always_equal::value) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 inline forward_list<_Tp, _Alloc>& forward_list<_Tp, _Alloc>::operator=(
+    forward_list&& __x) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
   __move_assign(__x, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
   return *this;
 }
@@ -1558,8 +1527,8 @@ operator<=>(const forward_list<_Tp, _Allocator>& __x, const forward_list<_Tp, _A
 #  endif // #if _LIBCPP_STD_VER <= 17
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 inline _LIBCPP_HIDE_FROM_ABI void
-swap(forward_list<_Tp, _Alloc>& __x, forward_list<_Tp, _Alloc>& __y) _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 inline
+    _LIBCPP_HIDE_FROM_ABI void swap(forward_list<_Tp, _Alloc>& __x, forward_list<_Tp, _Alloc>& __y) _NOEXCEPT {
   __x.swap(__y);
 }
 
diff --git a/libcxx/include/list b/libcxx/include/list
index 3223f25e1cfba..7b631554d0ed7 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -231,7 +231,6 @@ template <class T, class Allocator, class Predicate>
 #  include <__type_traits/container_traits.h>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/is_allocator.h>
-#  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
 #  include <__type_traits/is_same.h>
 #  include <__type_traits/type_identity.h>
@@ -535,21 +534,14 @@ protected:
     return const_iterator(__end_as_link());
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(__list_imp& __c)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT;
-#  else
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>);
-#  endif
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(__list_imp& __c) _NOEXCEPT;
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __list_imp& __c) {
     __copy_assign_alloc(
         __c, integral_constant<bool, __node_alloc_traits::propagate_on_container_copy_assignment::value>());
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__list_imp& __c)
-      _NOEXCEPT_(!__node_alloc_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__list_imp& __c) _NOEXCEPT {
     __move_assign_alloc(
         __c, integral_constant<bool, __node_alloc_traits::propagate_on_container_move_assignment::value>());
   }
@@ -589,8 +581,7 @@ private:
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const __list_imp&, false_type) {}
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__list_imp& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value) {
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__list_imp& __c, true_type) _NOEXCEPT {
     __node_alloc_ = std::move(__c.__node_alloc_);
   }
 
@@ -646,13 +637,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void __list_imp<_Tp, _Alloc>::clear() _NOEXCEPT {
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 void __list_imp<_Tp, _Alloc>::swap(__list_imp& __c)
-#  if _LIBCPP_STD_VER >= 14
-    _NOEXCEPT
-#  else
-    _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>)
-#  endif
-{
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __list_imp<_Tp, _Alloc>::swap(__list_imp& __c) _NOEXCEPT {
   _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
       __alloc_traits::propagate_on_container_swap::value || this->__node_alloc_ == __c.__node_alloc_,
       "list::swap: Either propagate_on_container_swap must be true"
@@ -744,13 +729,10 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
   list(initializer_list<value_type> __il, const allocator_type& __a);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list(list&& __c)
-      _NOEXCEPT_(is_nothrow_move_constructible<__node_allocator>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list(list&& __c) noexcept;
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list(list&& __c, const __type_identity_t<allocator_type>& __a);
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list& operator=(list&& __c) noexcept(
-      (__node_alloc_traits::propagate_on_container_move_assignment::value &&
-       is_nothrow_move_assignable<__node_allocator>::value) ||
-      allocator_traits<allocator_type>::is_always_equal::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list&
+  operator=(list&& __c) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>);
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list& operator=(initializer_list<value_type> __il) {
     assign(__il.begin(), __il.end());
@@ -915,15 +897,7 @@ public:
   }
 #  endif
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(list& __c)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT
-#  else
-      _NOEXCEPT_(!__node_alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>)
-#  endif
-  {
-    __base::swap(__c);
-  }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(list& __c) _NOEXCEPT { __base::swap(__c); }
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __base::clear(); }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void pop_front();
@@ -995,8 +969,7 @@ private:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDDEN static iterator
   __sort(iterator __f1, iterator __e2, size_type __n, _Comp& __comp);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign(list& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign(list& __c, true_type);
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void __move_assign(list& __c, false_type);
 };
 
@@ -1125,8 +1098,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 list<_Tp, _Alloc>::list(initializer_list<value_typ
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 inline list<_Tp, _Alloc>::list(list&& __c) noexcept(
-    is_nothrow_move_constructible<__node_allocator>::value)
+_LIBCPP_CONSTEXPR_SINCE_CXX26 inline list<_Tp, _Alloc>::list(list&& __c) noexcept
     : __base(std::move(__c.__node_alloc_)) {
   splice(end(), __c);
 }
@@ -1143,10 +1115,8 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 inline list<_Tp, _Alloc>::list(list&& __c, const _
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 inline list<_Tp, _Alloc>& list<_Tp, _Alloc>::operator=(list&& __c) noexcept(
-    (__node_alloc_traits::propagate_on_container_move_assignment::value &&
-     is_nothrow_move_assignable<__node_allocator>::value) ||
-    allocator_traits<allocator_type>::is_always_equal::value) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 inline list<_Tp, _Alloc>&
+list<_Tp, _Alloc>::operator=(list&& __c) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
   __move_assign(__c, integral_constant<bool, __node_alloc_traits::propagate_on_container_move_assignment::value>());
   return *this;
 }
@@ -1161,8 +1131,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void list<_Tp, _Alloc>::__move_assign(list& __c, f
 }
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 void
-list<_Tp, _Alloc>::__move_assign(list& __c, true_type) noexcept(is_nothrow_move_assignable<__node_allocator>::value) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void list<_Tp, _Alloc>::__move_assign(list& __c, true_type) {
   clear();
   __base::__move_assign_alloc(__c);
   splice(end(), __c);
@@ -1762,8 +1731,8 @@ operator<=>(const list<_Tp, _Allocator>& __x, const list<_Tp, _Allocator>& __y)
 #  endif // _LIBCPP_STD_VER <= 17
 
 template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX26 inline _LIBCPP_HIDE_FROM_ABI void swap(list<_Tp, _Alloc>& __x, list<_Tp, _Alloc>& __y)
-    _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 inline
+    _LIBCPP_HIDE_FROM_ABI void swap(list<_Tp, _Alloc>& __x, list<_Tp, _Alloc>& __y) _NOEXCEPT {
   __x.swap(__y);
 }
 
diff --git a/libcxx/include/queue b/libcxx/include/queue
index b469240cc94f9..b2c4c01a6eb62 100644
--- a/libcxx/include/queue
+++ b/libcxx/include/queue
@@ -272,6 +272,7 @@ template <class T, class Container, class Compare>
 #  include <__ranges/concepts.h>
 #  include <__ranges/container_compatible_range.h>
 #  include <__ranges/from_range.h>
+#  include <__type_traits/is_nothrow_assignable.h>
 #  include <__utility/forward.h>
 #  include <deque>
 #  include <vector>
diff --git a/libcxx/include/stack b/libcxx/include/stack
index 313c305dd8088..84da311dbf89a 100644
--- a/libcxx/include/stack
+++ b/libcxx/include/stack
@@ -126,6 +126,7 @@ template <class T, class Container>
 #  include <__ranges/concepts.h>
 #  include <__ranges/container_compatible_range.h>
 #  include <__ranges/from_range.h>
+#  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_same.h>
 #  include <__utility/forward.h>
 #  include <deque>
diff --git a/libcxx/include/string b/libcxx/include/string
index a297a05813cee..94457f45f4add 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -946,13 +946,9 @@ public:
     __annotate_new(0);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit basic_string(const allocator_type& __a)
-#  if _LIBCPP_STD_VER <= 14
-      _NOEXCEPT_(is_nothrow_copy_constructible<allocator_type>::value)
-#  else
-      _NOEXCEPT
-#  endif
-      : __rep_(), __alloc_(__a) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit basic_string(const allocator_type& __a) _NOEXCEPT
+      : __rep_(),
+        __alloc_(__a) {
     __annotate_new(0);
   }
 
@@ -974,13 +970,9 @@ public:
   }
 
 #  ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(basic_string&& __str)
-#    if _LIBCPP_STD_VER <= 14
-      _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
-#    else
-      _NOEXCEPT
-#    endif
-      : __rep_(std::move(__str.__rep_)), __alloc_(std::move(__str.__alloc_)) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(basic_string&& __str) _NOEXCEPT
+      : __rep_(std::move(__str.__rep_)),
+        __alloc_(std::move(__str.__alloc_)) {
     __str.__rep_ = __rep();
     __str.__annotate_new(0);
     if (!__is_long())
@@ -1144,7 +1136,7 @@ public:
 
 #  ifndef _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
-  operator=(basic_string&& __str) noexcept(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value) {
+  operator=(basic_string&& __str) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
     __move_assign(__str, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
     return *this;
   }
@@ -1449,7 +1441,7 @@ public:
   }
 #  ifndef _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
-  assign(basic_string&& __str) noexcept(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value) {
+  assign(basic_string&& __str) noexcept(__is_allocator_aware_container_move_nothrow_v<allocator_type>) {
     *this = std::move(__str);
     return *this;
   }
@@ -1704,12 +1696,7 @@ public:
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(basic_string& __str)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT;
-#  else
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>);
-#  endif
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(basic_string& __str) _NOEXCEPT;
 
   // [string.ops]
   // ------------
@@ -2422,15 +2409,12 @@ private:
 #    endif
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(basic_string& __str)
-      _NOEXCEPT_(!__alloc_traits::propagate_on_container_move_assignment::value ||
-                 is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(basic_string& __str) _NOEXCEPT {
     __move_assign_alloc(
         __str, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(basic_string& __c, true_type)
-      _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __move_assign_alloc(basic_string& __c, true_type) _NOEXCEPT {
     __alloc_ = std::move(__c.__alloc_);
   }
 
@@ -3436,13 +3420,8 @@ basic_string<_CharT, _Traits, _Allocator>::copy(value_type* __s, size_type __n,
 }
 
 template <class _CharT, class _Traits, class _Allocator>
-inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::swap(basic_string& __str)
-#  if _LIBCPP_STD_VER >= 14
-    _NOEXCEPT
-#  else
-    _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<allocator_type>)
-#  endif
-{
+inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+basic_string<_CharT, _Traits, _Allocator>::swap(basic_string& __str) _NOEXCEPT {
   _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
       __alloc_traits::propagate_on_container_swap::value || __alloc_traits::is_always_equal::value ||
           __alloc_ == __str.__alloc_,
@@ -3786,8 +3765,7 @@ operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
 
 template <class _CharT, class _Traits, class _Allocator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-swap(basic_string<_CharT, _Traits, _Allocator>& __lhs, basic_string<_CharT, _Traits, _Allocator>& __rhs)
-    _NOEXCEPT_(_NOEXCEPT_(__lhs.swap(__rhs))) {
+swap(basic_string<_CharT, _Traits, _Allocator>& __lhs, basic_string<_CharT, _Traits, _Allocator>& __rhs) _NOEXCEPT {
   __lhs.swap(__rhs);
 }
 
diff --git a/libcxx/test/libcxx/containers/associative/map/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/associative/map/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..6da2fd33fc169
--- /dev/null
+++ b/libcxx/test/libcxx/containers/associative/map/noexcept.compile.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <unordered_map>
+
+#include <type_traits>
+#include <map>
+
+#include "test_allocator.h"
+
+template <class... Args>
+inline constexpr bool is_constructible_but_throws =
+    std::is_constructible<Args...>::value && !std::is_nothrow_constructible<Args...>::value;
+
+namespace std_allocator {
+using map        = std::map<int, int>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map, const std::less<int>&, const std::allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          const std::less<int>&,
+                                          const std::allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(
+    is_constructible_but_throws<map, std::from_range_t, map, const std::less<int>&, const std::allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, map&&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+
+using multimap = std::multimap<int, int>;
+
+static_assert(std::is_nothrow_constructible<multimap>::value);
+static_assert(is_constructible_but_throws<multimap, const std::less<int>&, const std::allocator<value_type>&>);
+static_assert(is_constructible_but_throws<multimap,
+                                          value_type*,
+                                          value_type*,
+                                          const std::less<int>&,
+                                          const std::allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<multimap,
+                                          std::from_range_t,
+                                          multimap,
+                                          const std::less<int>&,
+                                          const std::allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<multimap, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<multimap, const multimap&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<multimap, multimap&&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<multimap, multimap&&>::value);
+
+static_assert(std::is_nothrow_assignable<multimap&, multimap&&>::value);
+static_assert(std::is_nothrow_swappable<multimap>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using map        = std::map<int, int, std::less<int>, test_allocator<std::pair<const int, int>>>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map, const std::less<int>&, const test_allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          const std::less<int>&,
+                                          const test_allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(
+    is_constructible_but_throws<map, std::from_range_t, map, const std::less<int>&, const test_allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, map&&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(!std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+
+using multimap = std::multimap<int, int, std::less<int>, test_allocator<std::pair<const int, int>>>;
+
+static_assert(std::is_nothrow_constructible<multimap>::value);
+static_assert(is_constructible_but_throws<multimap, const std::less<int>&, const test_allocator<value_type>&>);
+static_assert(is_constructible_but_throws<multimap,
+                                          value_type*,
+                                          value_type*,
+                                          const std::less<int>&,
+                                          const test_allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<multimap,
+                                          std::from_range_t,
+                                          multimap,
+                                          const std::less<int>&,
+                                          const test_allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<multimap, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<multimap, const multimap&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<multimap, multimap&&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<multimap, multimap&&>::value);
+
+static_assert(!std::is_nothrow_assignable<multimap&, multimap&&>::value);
+static_assert(std::is_nothrow_swappable<multimap>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/std/containers/sequences/deque/deque.cons/move_noexcept.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/deque.cons/move_noexcept.compile.pass.cpp
similarity index 53%
rename from libcxx/test/std/containers/sequences/deque/deque.cons/move_noexcept.pass.cpp
rename to libcxx/test/libcxx/containers/sequences/deque/deque.cons/move_noexcept.compile.pass.cpp
index 37e8a801e9d71..d38d1d05db033 100644
--- a/libcxx/test/std/containers/sequences/deque/deque.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/deque.cons/move_noexcept.compile.pass.cpp
@@ -15,10 +15,9 @@
 
 // UNSUPPORTED: c++03
 
-#include <deque>
 #include <cassert>
+#include <deque>
 
-#include "test_macros.h"
 #include "MoveOnly.h"
 #include "test_allocator.h"
 
@@ -29,25 +28,7 @@ struct some_alloc {
   void allocate(std::size_t);
 };
 
-int main(int, char**) {
-#if defined(_LIBCPP_VERSION)
-  {
-    typedef std::deque<MoveOnly> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::deque<MoveOnly, test_allocator<MoveOnly>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::deque<MoveOnly, other_allocator<MoveOnly>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::deque<MoveOnly, some_alloc<MoveOnly>> C;
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
-  }
-#endif // _LIBCPP_VERSION
-
-  return 0;
-}
+static_assert(std::is_nothrow_move_constructible<std::deque<MoveOnly>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::deque<MoveOnly, test_allocator<MoveOnly>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::deque<MoveOnly, other_allocator<MoveOnly>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::deque<MoveOnly, some_alloc<MoveOnly>>>::value, "");
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/move_noexcept.pass.cpp b/libcxx/test/libcxx/containers/sequences/forwardlist/move_noexcept.compile.pass.cpp
similarity index 53%
rename from libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/move_noexcept.pass.cpp
rename to libcxx/test/libcxx/containers/sequences/forwardlist/move_noexcept.compile.pass.cpp
index 8dcf00b49c78b..5e692a183440f 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/forwardlist/move_noexcept.compile.pass.cpp
@@ -15,10 +15,9 @@
 
 // UNSUPPORTED: c++03
 
-#include <forward_list>
 #include <cassert>
+#include <forward_list>
 
-#include "test_macros.h"
 #include "MoveOnly.h"
 #include "test_allocator.h"
 
@@ -29,25 +28,7 @@ struct some_alloc {
   void allocate(std::size_t);
 };
 
-int main(int, char**) {
-#if defined(_LIBCPP_VERSION)
-  {
-    typedef std::forward_list<MoveOnly> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::forward_list<MoveOnly, test_allocator<MoveOnly>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::forward_list<MoveOnly, other_allocator<MoveOnly>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::forward_list<MoveOnly, some_alloc<MoveOnly>> C;
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
-  }
-#endif // _LIBCPP_VERSION
-
-  return 0;
-}
+static_assert(std::is_nothrow_move_constructible<std::forward_list<MoveOnly>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::forward_list<MoveOnly, test_allocator<MoveOnly>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::forward_list<MoveOnly, other_allocator<MoveOnly>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::forward_list<MoveOnly, some_alloc<MoveOnly>>>::value, "");
diff --git a/libcxx/test/std/containers/sequences/list/list.cons/move_noexcept.pass.cpp b/libcxx/test/libcxx/containers/sequences/list/list.cons/move_noexcept.compile.pass.cpp
similarity index 55%
rename from libcxx/test/std/containers/sequences/list/list.cons/move_noexcept.pass.cpp
rename to libcxx/test/libcxx/containers/sequences/list/list.cons/move_noexcept.compile.pass.cpp
index c33ef906be400..23b647693937a 100644
--- a/libcxx/test/std/containers/sequences/list/list.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/list/list.cons/move_noexcept.compile.pass.cpp
@@ -29,25 +29,7 @@ struct some_alloc {
   void allocate(std::size_t);
 };
 
-int main(int, char**) {
-#if defined(_LIBCPP_VERSION)
-  {
-    typedef std::list<MoveOnly> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::list<MoveOnly, test_allocator<MoveOnly>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::list<MoveOnly, other_allocator<MoveOnly>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-#endif // _LIBCPP_VERSION
-  {
-    typedef std::list<MoveOnly, some_alloc<MoveOnly>> C;
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
-  }
-
-  return 0;
-}
+static_assert(std::is_nothrow_move_constructible<std::list<MoveOnly>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::list<MoveOnly, test_allocator<MoveOnly>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::list<MoveOnly, other_allocator<MoveOnly>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::list<MoveOnly, some_alloc<MoveOnly>>>::value, "");
diff --git a/libcxx/test/libcxx/containers/sequences/vector.bool/move_noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector.bool/move_noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..642ce4d966eff
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/vector.bool/move_noexcept.compile.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <vector>
+
+// vector(vector&&)
+//        noexcept(is_nothrow_move_constructible<allocator_type>::value);
+
+// This tests a conforming extension
+
+// UNSUPPORTED: c++03
+
+#include <vector>
+#include <cassert>
+
+#include "test_allocator.h"
+
+template <class T>
+struct some_alloc {
+  typedef T value_type;
+  some_alloc(const some_alloc&);
+};
+
+static_assert(std::is_nothrow_move_constructible<std::vector<bool>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::vector<bool, test_allocator<bool>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::vector<bool, other_allocator<bool>>>::value, "");
+static_assert(std::is_nothrow_move_constructible<std::vector<bool, some_alloc<bool>>>::value, "");
diff --git a/libcxx/test/libcxx/containers/sequences/vector.bool/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector.bool/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..a3881e9401f46
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/vector.bool/noexcept.compile.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <vector>
+
+#include <type_traits>
+#include <vector>
+
+#include "test_allocator.h"
+
+namespace std_allocator {
+using vec = std::vector<bool>;
+
+static_assert(std::is_nothrow_constructible<vec>::value);
+static_assert(std::is_nothrow_constructible<vec, const std::allocator<bool>&>::value);
+static_assert(std::is_nothrow_constructible<vec, vec&&>::value);
+
+static_assert(std::is_nothrow_assignable<vec&, vec&&>::value);
+static_assert(std::is_nothrow_swappable<vec>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using vec = std::vector<bool, test_allocator<bool>>;
+
+static_assert(std::is_nothrow_constructible<vec>::value);
+static_assert(std::is_nothrow_constructible<vec, const test_allocator<bool>&>::value);
+static_assert(std::is_nothrow_constructible<vec, vec&&>::value);
+
+static_assert(!std::is_nothrow_assignable<vec&, vec&&>::value);
+static_assert(std::is_nothrow_swappable<vec>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/libcxx/containers/sequences/vector/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..92734fd995232
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/vector/noexcept.compile.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <vector>
+
+#include <type_traits>
+#include <vector>
+
+#include "test_allocator.h"
+
+namespace std_allocator {
+using vec = std::vector<int>;
+
+static_assert(std::is_nothrow_constructible<vec>::value);
+static_assert(std::is_nothrow_constructible<vec, const std::allocator<int>&>::value);
+static_assert(std::is_nothrow_constructible<vec, vec&&>::value);
+
+static_assert(std::is_nothrow_assignable<vec&, vec&&>::value);
+static_assert(std::is_nothrow_swappable<vec>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using vec = std::vector<int, test_allocator<int>>;
+
+static_assert(std::is_nothrow_constructible<vec>::value);
+static_assert(std::is_nothrow_constructible<vec, const test_allocator<int>&>::value);
+static_assert(std::is_nothrow_constructible<vec, vec&&>::value);
+
+static_assert(!std::is_nothrow_assignable<vec&, vec&&>::value);
+static_assert(std::is_nothrow_swappable<vec>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.map/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..f283f2593f140
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.map/noexcept.compile.pass.cpp
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <unordered_map>
+
+#include <type_traits>
+#include <unordered_map>
+
+#include "test_allocator.h"
+
+template <class... Args>
+inline constexpr bool is_constructible_but_throws =
+    std::is_constructible<Args...>::value && !std::is_nothrow_constructible<Args...>::value;
+
+namespace std_allocator {
+using map        = std::unordered_map<int, int>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using map = std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, test_allocator<std::pair<const int, int>>>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(!std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..43f17772de236
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/noexcept.compile.pass.cpp
@@ -0,0 +1,155 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <unordered_map>
+
+#include <type_traits>
+#include <unordered_map>
+
+#include "test_allocator.h"
+
+template <class... Args>
+inline constexpr bool is_constructible_but_throws =
+    std::is_constructible<Args...>::value && !std::is_nothrow_constructible<Args...>::value;
+
+namespace std_allocator {
+using map        = std::unordered_multimap<int, int>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using map =
+    std::unordered_multimap<int, int, std::hash<int>, std::equal_to<int>, test_allocator<std::pair<const int, int>>>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(!std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..38780139ff417
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/noexcept.compile.pass.cpp
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <unordered_set>
+
+#include <type_traits>
+#include <unordered_set>
+
+#include "test_allocator.h"
+
+template <class... Args>
+inline constexpr bool is_constructible_but_throws =
+    std::is_constructible<Args...>::value && !std::is_nothrow_constructible<Args...>::value;
+
+namespace std_allocator {
+using map        = std::unordered_multiset<int>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using map        = std::unordered_multiset<int, std::hash<int>, std::equal_to<int>, test_allocator<int>>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(!std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/noexcept.compile.pass.cpp b/libcxx/test/libcxx/containers/unord/unord.set/noexcept.compile.pass.cpp
new file mode 100644
index 0000000000000..df0d87f74292b
--- /dev/null
+++ b/libcxx/test/libcxx/containers/unord/unord.set/noexcept.compile.pass.cpp
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <unordered_set>
+
+#include <type_traits>
+#include <unordered_set>
+
+#include "test_allocator.h"
+
+template <class... Args>
+inline constexpr bool is_constructible_but_throws =
+    std::is_constructible<Args...>::value && !std::is_nothrow_constructible<Args...>::value;
+
+namespace std_allocator {
+using map        = std::unordered_set<int>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const std::allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const std::allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace std_allocator
+
+namespace test_alloc {
+using map        = std::unordered_set<int, std::hash<int>, std::equal_to<int>, test_allocator<int>>;
+using value_type = typename map::value_type;
+
+static_assert(std::is_nothrow_constructible<map>::value);
+static_assert(is_constructible_but_throws<map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          map,
+                                          int,
+                                          const std::hash<int>&,
+                                          const std::equal_to<int>&,
+                                          const test_allocator<value_type>&>);
+#endif
+static_assert(is_constructible_but_throws<map, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, const map&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, int, const std::hash<int>&, const test_allocator<std::pair<const int, int>>&>);
+static_assert(
+    is_constructible_but_throws<map, value_type*, value_type*, int, const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          value_type*,
+                                          value_type*,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#if TEST_STD_VER >= 23
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::from_range_t,
+                                          const map&,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+#endif
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(is_constructible_but_throws<map,
+                                          std::initializer_list<value_type>,
+                                          int,
+                                          const std::hash<int>&,
+                                          const test_allocator<std::pair<const int, int>>&>);
+static_assert(std::is_nothrow_constructible<map, map&&>::value);
+
+static_assert(!std::is_nothrow_assignable<map&, map&&>::value);
+static_assert(std::is_nothrow_swappable<map>::value);
+} // namespace test_alloc
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/move_noexcept.pass.cpp
index d281dafbcf72d..5ad42e8734f8c 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/move_noexcept.pass.cpp
@@ -29,17 +29,6 @@
 #include "MoveOnly.h"
 #include "test_allocator.h"
 
-template <class T>
-struct ThrowingMoveAllocator {
-  using value_type                                    = T;
-  explicit ThrowingMoveAllocator()                    = default;
-  ThrowingMoveAllocator(const ThrowingMoveAllocator&) = default;
-  ThrowingMoveAllocator(ThrowingMoveAllocator&&) noexcept(false) {}
-  T* allocate(std::ptrdiff_t n) { return std::allocator<T>().allocate(n); }
-  void deallocate(T* p, std::ptrdiff_t n) { return std::allocator<T>().deallocate(p, n); }
-  friend bool operator==(ThrowingMoveAllocator, ThrowingMoveAllocator) = default;
-};
-
 struct ThrowingMoveComp {
   ThrowingMoveComp() = default;
   ThrowingMoveComp(const ThrowingMoveComp&) noexcept(true) {}
@@ -73,24 +62,6 @@ int main(int, char**) {
     C c;
     C d = std::move(c);
   }
-#if _LIBCPP_VERSION
-  {
-    // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators
-    using C = std::flat_map<int, int, std::less<int>, std::deque<int, ThrowingMoveAllocator<int>>, std::vector<int>>;
-    static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);
-    static_assert(!std::is_nothrow_move_constructible_v<C>);
-    C c;
-    C d = std::move(c);
-  }
-  {
-    // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators
-    using C = std::flat_map<int, int, std::less<int>, std::vector<int>, std::deque<int, ThrowingMoveAllocator<int>>>;
-    static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);
-    static_assert(!std::is_nothrow_move_constructible_v<C>);
-    C c;
-    C d = std::move(c);
-  }
-#endif // _LIBCPP_VERSION
   {
     // Comparator fails to be nothrow-move-constructible
     using C = std::flat_map<int, int, ThrowingMoveComp>;
diff --git a/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.cons/move_noexcept.pass.cpp
index e038902e26d52..f5d18ac331378 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.cons/move_noexcept.pass.cpp
@@ -29,17 +29,6 @@
 #include "MoveOnly.h"
 #include "test_allocator.h"
 
-template <class T>
-struct ThrowingMoveAllocator {
-  using value_type                                    = T;
-  explicit ThrowingMoveAllocator()                    = default;
-  ThrowingMoveAllocator(const ThrowingMoveAllocator&) = default;
-  ThrowingMoveAllocator(ThrowingMoveAllocator&&) noexcept(false) {}
-  T* allocate(std::ptrdiff_t n) { return std::allocator<T>().allocate(n); }
-  void deallocate(T* p, std::ptrdiff_t n) { return std::allocator<T>().deallocate(p, n); }
-  friend bool operator==(ThrowingMoveAllocator, ThrowingMoveAllocator) = default;
-};
-
 struct ThrowingMoveComp {
   ThrowingMoveComp() = default;
   ThrowingMoveComp(const ThrowingMoveComp&) noexcept(true) {}
@@ -73,26 +62,6 @@ int main(int, char**) {
     C c;
     C d = std::move(c);
   }
-#if _LIBCPP_VERSION
-  {
-    // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators
-    using C =
-        std::flat_multimap<int, int, std::less<int>, std::deque<int, ThrowingMoveAllocator<int>>, std::vector<int>>;
-    static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);
-    static_assert(!std::is_nothrow_move_constructible_v<C>);
-    C c;
-    C d = std::move(c);
-  }
-  {
-    // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators
-    using C =
-        std::flat_multimap<int, int, std::less<int>, std::vector<int>, std::deque<int, ThrowingMoveAllocator<int>>>;
-    static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);
-    static_assert(!std::is_nothrow_move_constructible_v<C>);
-    C c;
-    C d = std::move(c);
-  }
-#endif // _LIBCPP_VERSION
   {
     // Comparator fails to be nothrow-move-constructible
     using C = std::flat_multimap<int, int, ThrowingMoveComp>;
diff --git a/libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.cons/move.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.cons/move.pass.cpp
index f9fea737254d3..113a7e36259f5 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.cons/move.pass.cpp
@@ -92,17 +92,6 @@ constexpr bool test() {
   return true;
 }
 
-template <class T>
-struct ThrowingMoveAllocator {
-  using value_type                                    = T;
-  explicit ThrowingMoveAllocator()                    = default;
-  ThrowingMoveAllocator(const ThrowingMoveAllocator&) = default;
-  ThrowingMoveAllocator(ThrowingMoveAllocator&&) noexcept(false) {}
-  T* allocate(std::ptrdiff_t n) { return std::allocator<T>().allocate(n); }
-  void deallocate(T* p, std::ptrdiff_t n) { return std::allocator<T>().deallocate(p, n); }
-  friend bool operator==(ThrowingMoveAllocator, ThrowingMoveAllocator) = default;
-};
-
 struct ThrowingMoveComp {
   ThrowingMoveComp() = default;
   ThrowingMoveComp(const ThrowingMoveComp&) noexcept(true) {}
@@ -137,14 +126,6 @@ void test_move_noexcept() {
     C d = std::move(c);
   }
 #if _LIBCPP_VERSION
-  {
-    // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators
-    using C = std::flat_multiset<int, std::less<int>, std::deque<int, ThrowingMoveAllocator<int>>>;
-    static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);
-    static_assert(!std::is_nothrow_move_constructible_v<C>);
-    C c;
-    C d = std::move(c);
-  }
   {
     // Comparator fails to be nothrow-move-constructible
     using C = std::flat_multiset<int, ThrowingMoveComp>;
diff --git a/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/move.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/move.pass.cpp
index 1d7a3589cc109..5d6ab501cc38f 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/move.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/move.pass.cpp
@@ -83,14 +83,14 @@ constexpr void test() {
 }
 
 template <class T>
-struct ThrowingMoveAllocator {
+struct PotentiallyThrowingMoveAllocator {
   using value_type                                    = T;
-  explicit ThrowingMoveAllocator()                    = default;
-  ThrowingMoveAllocator(const ThrowingMoveAllocator&) = default;
-  constexpr ThrowingMoveAllocator(ThrowingMoveAllocator&&) noexcept(false) {}
+  explicit PotentiallyThrowingMoveAllocator()                    = default;
+  PotentiallyThrowingMoveAllocator(const PotentiallyThrowingMoveAllocator&) = default;
+  constexpr PotentiallyThrowingMoveAllocator(PotentiallyThrowingMoveAllocator&&) noexcept(false) {}
   constexpr T* allocate(std::ptrdiff_t n) { return std::allocator<T>().allocate(n); }
   constexpr void deallocate(T* p, std::ptrdiff_t n) { return std::allocator<T>().deallocate(p, n); }
-  friend bool operator==(ThrowingMoveAllocator, ThrowingMoveAllocator) = default;
+  friend bool operator==(PotentiallyThrowingMoveAllocator, PotentiallyThrowingMoveAllocator) = default;
 };
 
 struct ThrowingMoveComp {
@@ -117,9 +117,9 @@ constexpr void test_move_noexcept() {
 #if _LIBCPP_VERSION
   if (!TEST_IS_CONSTANT_EVALUATED) {
     // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators
-    using C = std::flat_set<int, std::less<int>, std::deque<int, ThrowingMoveAllocator<int>>>;
-    static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);
-    static_assert(!std::is_nothrow_move_constructible_v<C>);
+    using C = std::flat_set<int, std::less<int>, std::deque<int, PotentiallyThrowingMoveAllocator<int>>>;
+    static_assert(std::is_nothrow_move_constructible_v<std::deque<int, PotentiallyThrowingMoveAllocator<int>>>);
+    static_assert(std::is_nothrow_move_constructible_v<C>);
     C c;
     C d = std::move(c);
   }
diff --git a/libcxx/test/std/containers/sequences/deque/deque.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.special/swap_noexcept.pass.cpp
index 6347496d67c15..96195bf13da67 100644
--- a/libcxx/test/std/containers/sequences/deque/deque.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/sequences/deque/deque.special/swap_noexcept.pass.cpp
@@ -69,14 +69,12 @@ int main(int, char**) {
 #endif // _LIBCPP_VERSION
   {
     typedef std::deque<MoveOnly, some_alloc<MoveOnly>> C;
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 14 || defined(_LIBCPP_VERSION)
     //  In C++14, if POCS is set, swapping the allocator is required not to throw
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
-#else
-    static_assert(!noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
 #endif
   }
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 14 || defined(_LIBCPP_VERSION)
   {
     typedef std::deque<MoveOnly, some_alloc2<MoveOnly>> C;
     //  if the allocators are always equal, then the swap can be noexcept
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/swap_noexcept.compile.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/swap_noexcept.compile.pass.cpp
index 02b7b471a1ae8..978727f9418bf 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/swap_noexcept.compile.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.spec/swap_noexcept.compile.pass.cpp
@@ -69,14 +69,12 @@ void f() {
 #endif // _LIBCPP_VERSION
   {
     typedef std::forward_list<MoveOnly, some_alloc<MoveOnly>> C;
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 14 || defined(_LIBCPP_VERSION)
     //  In C++14, if POCS is set, swapping the allocator is required not to throw
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
-#else
-    static_assert(!noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
 #endif
   }
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 14 || defined(_LIBCPP_VERSION)
   {
     typedef std::forward_list<MoveOnly, some_alloc2<MoveOnly>> C;
     //  if the allocators are always equal, then the swap can be noexcept
diff --git a/libcxx/test/std/containers/sequences/list/list.special/swap_noexcept.pass.cpp b/libcxx/test/std/containers/sequences/list/list.special/swap_noexcept.pass.cpp
index 037c7d07c4cb8..f528b7ebad074 100644
--- a/libcxx/test/std/containers/sequences/list/list.special/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/sequences/list/list.special/swap_noexcept.pass.cpp
@@ -69,14 +69,12 @@ TEST_CONSTEXPR_CXX26 bool test() {
 #endif // _LIBCPP_VERSION
   {
     typedef std::list<MoveOnly, some_alloc<MoveOnly>> C;
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 14 || defined(_LIBCPP_VERSION)
     //  In C++14, if POCS is set, swapping the allocator is required not to throw
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
-#else
-    static_assert(!noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
 #endif
   }
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 14 || defined(_LIBCPP_VERSION)
   {
     typedef std::list<MoveOnly, some_alloc2<MoveOnly>> C;
     //  if the allocators are always equal, then the swap can be noexcept
diff --git a/libcxx/test/std/containers/sequences/vector.bool/move_noexcept.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/move_noexcept.pass.cpp
deleted file mode 100644
index 5bdae01f871f9..0000000000000
--- a/libcxx/test/std/containers/sequences/vector.bool/move_noexcept.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <vector>
-
-// vector(vector&&)
-//        noexcept(is_nothrow_move_constructible<allocator_type>::value);
-
-// This tests a conforming extension
-
-// UNSUPPORTED: c++03
-
-#include <cassert>
-#include <vector>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "test_allocator.h"
-
-template <class T>
-struct some_alloc {
-  typedef T value_type;
-  some_alloc(const some_alloc&);
-};
-
-int main(int, char**) {
-#if defined(_LIBCPP_VERSION)
-  {
-    typedef std::vector<bool> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::vector<bool, test_allocator<bool>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-  {
-    typedef std::vector<bool, other_allocator<bool>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-  }
-#endif // _LIBCPP_VERSION
-  {
-    //  In C++17, move constructors for allocators are not allowed to throw
-#if TEST_STD_VER > 14
-#  if defined(_LIBCPP_VERSION)
-    typedef std::vector<bool, some_alloc<bool>> C;
-    static_assert(std::is_nothrow_move_constructible<C>::value, "");
-#  endif // _LIBCPP_VERSION
-#else
-    typedef std::vector<bool, some_alloc<bool>> C;
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
-#endif
-  }
-
-  return 0;
-}
diff --git a/libcxx/test/std/containers/sequences/vector.bool/swap_noexcept.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/swap_noexcept.pass.cpp
index 1683191403709..5c65510a203c0 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/swap_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector.bool/swap_noexcept.pass.cpp
@@ -64,27 +64,16 @@ int main(int, char**) {
     typedef std::vector<bool, other_allocator<bool>> C;
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
   }
-#endif // _LIBCPP_VERSION
   {
-#if TEST_STD_VER >= 14
-#  if defined(_LIBCPP_VERSION)
     //  In C++14, if POCS is set, swapping the allocator is required not to throw
     typedef std::vector<bool, some_alloc<bool>> C;
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
-#  endif // _LIBCPP_VERSION
-#else
-    typedef std::vector<bool, some_alloc<bool>> C;
-    static_assert(!noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
-#endif
   }
-#if TEST_STD_VER >= 14
-#  if defined(_LIBCPP_VERSION)
   {
     typedef std::vector<bool, some_alloc2<bool>> C;
     //  if the allocators are always equal, then the swap can be noexcept
     static_assert(noexcept(swap(std::declval<C&>(), std::declval<C&>())), "");
   }
-#  endif // _LIBCPP_VERSION
 #endif
 
   return 0;
diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/move_noexcept.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/move_noexcept.pass.cpp
index 13dc6d41684fa..21b14a76260cb 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/move_noexcept.pass.cpp
@@ -44,11 +44,8 @@ int main(int, char**) {
   }
   {
     typedef std::vector<MoveOnly, some_alloc<MoveOnly>> C;
-    //  In C++17, move constructors for allocators are not allowed to throw
-#if TEST_STD_VER > 14
+#if TEST_STD_VER > 14 || defined(_LIBCPP_VERSION)
     static_assert(std::is_nothrow_move_constructible<C>::value, "");
-#else
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
 #endif
   }
 
diff --git a/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp
index d807cac2c72c8..ba7f93e2a52cb 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp
@@ -32,9 +32,7 @@ int main(int, char**) {
   }
   {
     typedef std::basic_string<char, std::char_traits<char>, limited_allocator<char, 10>> C;
-#if TEST_STD_VER <= 14
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
-#else
+#if TEST_STD_VER >= 17 || defined(_LIBCPP_VERSION)
     static_assert(std::is_nothrow_move_constructible<C>::value, "");
 #endif
   }


From libcxx-commits at lists.llvm.org  Wed Jul  8 07:32:38 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 08 Jul 2026 07:32:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4e5f86.92d2873c.1ab170.0e07@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/208036

>From 1a1da3d615018ef52c9eefcbb376679f5cdd7505 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 18:00:56 +0200
Subject: [PATCH 1/7] initial: nodiscard implementation to lazy_split_view.h

---
 libcxx/include/__ranges/lazy_split_view.h | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 938dca24cc4fc..01d398a0ac6d8 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -95,14 +95,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
       : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
@@ -111,19 +111,19 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<_View> && forward_range<const _View>
   {
     return __outer_iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
     return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
     if constexpr (forward_range<_View> && forward_range<const _View> && common_range<const _View>) {
       return __outer_iterator<true>{*this, ranges::end(__base_)};
     } else {
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
@@ -342,14 +342,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
-    _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
       return std::move(__i_.__current());
     }
 
-    _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
       __incremented_ = true;
@@ -385,7 +385,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       return __x.__is_done();
     }
 
-    _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
     iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(__i.__outer_current()))) {
       return ranges::iter_move(__i.__outer_current());
     }

>From 845a2090e07f251f9a46f849f149d5662ecdbc45 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 22:49:19 +0200
Subject: [PATCH 2/7] add: initial tests and clang-format fix

---
 libcxx/include/__ranges/lazy_split_view.h     | 12 +++++----
 .../range.lazy.split/nodiscard.verify.cpp     | 27 +++++++++++++++++++
 2 files changed, 34 insertions(+), 5 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 01d398a0ac6d8..5e336cbfcac49 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
+      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
+    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -342,7 +342,9 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
+      return __i_.__current();
+    }
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
@@ -404,8 +406,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&,
-                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, range_value_t<_Range>)
+    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cd15b33b66d59
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  // [range.lazy.split.overview]
+
+  std::string str = "the quick brown fox";
+  char pattern    = ' ';
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::split(str, pattern);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::split(pattern);
+}

>From 054f55611b632030795527d777509c70609e5df0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 22:57:55 +0200
Subject: [PATCH 3/7] fix: minor fixes for header include and lazy_split

---
 .../range.adaptors/range.lazy.split/nodiscard.verify.cpp  | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index cd15b33b66d59..c6420b0adf5d5 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -11,17 +11,19 @@
 // Check that functions are marked [[nodiscard]]
 
 #include <ranges>
+#include <string>
 #include <utility>
 
 void test() {
   // [range.lazy.split.overview]
 
-  std::string str = "the quick brown fox";
+  std::string str { "the quick brown fox" };
   char pattern    = ' ';
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::split(str, pattern);
+  std::views::lazy_split(str, pattern);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::split(pattern);
+  std::views::lazy_split(pattern);
+
 }

>From fbf22aa66f5d3205c82bbba057664780a8fbe057 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 23:32:22 +0200
Subject: [PATCH 4/7] add: test cases and remove nodiscard in implementation
 detail

---
 libcxx/include/__ranges/lazy_split_view.h     |  2 +-
 .../range.lazy.split/nodiscard.verify.cpp     | 41 ++++++++++++++++---
 2 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 5e336cbfcac49..66f18d5edad76 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index c6420b0adf5d5..b80f6f00836b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -8,22 +8,53 @@
 
 // REQUIRES: std-at-least-c++20
 
-// Check that functions are marked [[nodiscard]]
+// Test the libc++ extension that std::ranges::lazy_split_view and std::views::lazy_split are marked as [[nodiscard]].
 
 #include <ranges>
 #include <string>
 #include <utility>
 
 void test() {
-  // [range.lazy.split.overview]
-
-  std::string str { "the quick brown fox" };
+  std::string str = "the quick brown fox";
   char pattern    = ' ';
 
+  auto v = std::views::lazy_split(str, pattern);
+
+  // [range.lazy.split.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.lazy.split.iterator]
+
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it.base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(c_it);
+
+  // [range.lazy.split.overview]
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::lazy_split(str, pattern);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::lazy_split(pattern);
-
 }

>From 997525b35d5e7a64e9db03541cfbc7acf934f1da Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 12:01:40 +0200
Subject: [PATCH 5/7] revert: accidental format changes

---
 libcxx/include/__ranges/lazy_split_view.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 66f18d5edad76..c9a941abe7ff9 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
+      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
+    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -406,8 +406,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&, range_value_t<_Range>)
-    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, 
+                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {

>From 598aacef1fcf0d98cce6e0fd4b1c7ef3ff8e64db Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 12:02:50 +0200
Subject: [PATCH 6/7] revert: accidental format changes

---
 libcxx/include/__ranges/lazy_split_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index c9a941abe7ff9..fc41f5c49523b 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::begin(__base_)};
+      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::end(__base_)};
+    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -406,7 +406,7 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&, 
+lazy_split_view(_Range&&,
                 range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {

>From f81b6437ddbec10579a87c58dca1286b63f10763 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 16:32:21 +0200
Subject: [PATCH 7/7] fix: __outer_iterator to __inner_iterator nodiscard
 testing

---
 libcxx/include/__ranges/lazy_split_view.h              |  2 +-
 .../range.lazy.split/nodiscard.verify.cpp              | 10 +++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index fc41f5c49523b..c05c932d09703 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index b80f6f00836b0..ab865c7c94b85 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -37,18 +37,18 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).end();
 
-  // [range.lazy.split.iterator]
+  // [range.lazy.split.outer-iterator]
 
-  auto c_it = std::as_const(v).begin();
+  auto c_it = as_const(v).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  c_it.base();
+  (*c_it).begin().base();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *c_it;
+  (*c_it);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  iter_move(c_it);
+  iter_move((*c_it).begin());
 
   // [range.lazy.split.overview]
 


From libcxx-commits at lists.llvm.org  Wed Jul  8 07:35:53 2026
From: libcxx-commits at lists.llvm.org (Jake Egan via libcxx-commits)
Date: Wed, 08 Jul 2026 07:35:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] Fix redefinition of wchar_t
 overloads on AIX (PR #201432)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201432@github.com>
Message-ID: <6a4e6049.bb839515.27d21e.031e@mx.google.com>


================
@@ -139,6 +139,8 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 
 #    if _LIBCPP_HAS_WIDE_CHARACTERS
 #      if defined(__cplusplus) && !defined(_LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS) && defined(_LIBCPP_PREFERRED_OVERLOAD)
+#        if defined(_AIX) && !defined(_LIBCPP_WCHAR_H_OVERLOADS)
----------------
jakeegan wrote:

This excludes the existing code below from non-AIX platforms, which seems to cause the CI failures.

https://github.com/llvm/llvm-project/pull/201432

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:46:13 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Wed, 08 Jul 2026 07:46:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::reverse_copy (PR #208085)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208085@github.com>
Message-ID: <6a4e62b5.ac469dc7.3a57b1.ab94@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/208085

>From d0733a0157e03c566fc6dedce3a340b3a74422ca Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 7 Jul 2026 20:15:09 +0100
Subject: [PATCH 1/2] Added a default implementation of parallel
 std::reverse_copy based on std::copy + std::reverse_iterator

---
 libcxx/include/__algorithm/pstl.h             |  17 +++
 libcxx/include/__pstl/backend_fwd.h           |   7 ++
 libcxx/include/__pstl/backends/default.h      |  17 +++
 .../pstl.iterator-requirements.verify.cpp     |  14 ++-
 .../algorithms/pstl.nodiscard.verify.cpp      |   2 +
 .../alg.reverse/pstl.reverse_copy.pass.cpp    | 116 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |   7 ++
 7 files changed, 178 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 10625ea3f8e3d..55654637df57f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -347,6 +347,23 @@ generate_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n, _Ge
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__n), std::move(__gen));
 }
 
+template <class _ExecutionPolicy,
+          class _BidirectionalIterator,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator reverse_copy(
+    _ExecutionPolicy&& __policy,
+    _BidirectionalIterator __first,
+    _BidirectionalIterator __last,
+    _ForwardIterator __result) {
+  _LIBCPP_REQUIRE_CPP17_BIDIRECTIONAL_ITERATOR(_BidirectionalIterator, "reverse_copy requires a BidirectionalIterator");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reverse_copy requires a ForwardIterator");
+  using _Implementation = __pstl::__dispatch<__pstl::__reverse_copy, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__result));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator,
           class _Predicate,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a52e6db954d0c..6b72350d43a10 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -159,6 +159,13 @@ struct __generate_n;
 // optional<__empty>
 // operator()(_Policy&&, _ForwardIterator __first, _Size __n, _Generator __gen) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __reverse_copy;
+// template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
+// optional<_ForwardIterator>
+// operator()(_Policy&&, _BidirectionalIterator __first, _BidirectionalIterator __last,
+//                       _ForwardIterator __result) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __merge;
 // template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Comp>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index be90715af13b5..6cbd3bcd0af64 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -21,6 +21,7 @@
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
+#include <__iterator/reverse_iterator.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
 #include <__utility/empty.h>
@@ -529,6 +530,22 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __reverse_copy<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+  operator()(_Policy&& __policy,
+             _BidirectionalIterator __first,
+             _BidirectionalIterator __last,
+             _ForwardIterator __result) const noexcept {
+    using _Copy = __dispatch<__copy, __current_configuration, _ExecutionPolicy>;
+    return _Copy()(__policy,
+                   std::reverse_iterator<_BidirectionalIterator>(std::move(__last)),
+                   std::reverse_iterator<_BidirectionalIterator>(std::move(__first)),
+                   std::move(__result));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e5bd7e764c59b..4aa435245e863 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -31,12 +31,16 @@
 
 #include "test_iterators.h"
 
-using non_forward_iterator = cpp17_input_iterator<int*>;
+using non_forward_iterator       = cpp17_input_iterator<int*>;
+using non_bidirectional_iterator = forward_iterator<int*>;
 struct non_output_iterator : forward_iterator<int*> {
   constexpr int const& operator*() const; // prevent it from being an output iterator
 };
 
-void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execution::sequenced_policy pol) {
+void f(non_forward_iterator non_fwd,
+       non_output_iterator non_output,
+       non_bidirectional_iterator non_bidir,
+       std::execution::sequenced_policy pol) {
   auto pred     = [](auto&&...) -> bool { return true; };
   auto func     = [](auto&&...) -> int { return 1; };
   int* it       = nullptr;
@@ -99,6 +103,12 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::generate_n(pol, non_fwd, n, func);     // expected-error@*:* {{static assertion failed: generate_n}}
   }
 
+  {
+    (void)std::reverse_copy(
+        pol, non_bidir, non_bidir, it);            // expected-error@*:* {{static assertion failed: reverse_copy}}
+    (void)std::reverse_copy(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: reverse_copy}}
+  }
+
   {
     (void)std::is_partitioned(
         pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: is_partitioned}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index b6554cef6f32a..38e85fb3b6b83 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -52,4 +52,6 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
new file mode 100644
index 0000000000000..b805fab2f1fb0
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class BidirectionalIterator,
+//           class ForwardIterator>
+//   ForwardIterator reverse_copy(ExecutionPolicy&& exec,
+//                                BidirectionalIterator first,
+//                                BidirectionalIterator last,
+//                                ForwardIterator result);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(reverse_copy);
+
+static_assert(sfinae_test_reverse_copy<int, int*, int*, int*>);
+static_assert(!sfinae_test_reverse_copy<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[]              = {0};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      assert(ib[0] == -1);
+      r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(ia[0] == 0);
+    }
+    {
+      int ia[]              = {0, 1};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 1);
+      assert(ib[1] == 0);
+    }
+    {
+      int ia[]              = {0, 1, 2};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 2);
+      assert(ib[1] == 1);
+      assert(ib[2] == 0);
+    }
+    {
+      int ia[]              = {0, 1, 2, 3};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 3);
+      assert(ib[1] == 2);
+      assert(ib[2] == 1);
+      assert(ib[3] == 0);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia) + 1, [&](size_t i) {
+        Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j) {
+          assert(ib[j] == static_cast<int>(i - j));
+        }
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::bidirectional_iterator_list<int*>, types::bidirectional_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+}
\ No newline at end of file
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a3db159289393..546862169813e 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -195,6 +195,13 @@ int main(int, char**) {
         assert_non_throwing([=, &policy] { (void)std::generate_n(policy, std::move(first1), n, gen); });
       }
 
+      {
+        // reverse_copy(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::reverse_copy(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+      }
+
       {
         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
 

>From 6632a8261585b97670183b719c2fbab3330f839e Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 8 Jul 2026 15:45:42 +0100
Subject: [PATCH 2/2] Added a missing line in the top comments

---
 libcxx/include/__pstl/backends/default.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 6cbd3bcd0af64..49ab69b497ddd 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -93,6 +93,7 @@ namespace __pstl {
 // - move
 // - copy
 // - copy_n
+// - reverse_copy
 // - rotate_copy
 //
 


From libcxx-commits at lists.llvm.org  Wed Jul  8 07:46:33 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Wed, 08 Jul 2026 07:46:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::reverse_copy (PR #208085)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208085@github.com>
Message-ID: <6a4e62c9.f06f4994.1fc857.c997@mx.google.com>


https://github.com/mikekazakov ready_for_review https://github.com/llvm/llvm-project/pull/208085

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:47:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 07:47:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::reverse_copy (PR #208085)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208085@github.com>
Message-ID: <6a4e62ed.13be6df8.145c.1632@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Michael G. Kazakov (mikekazakov)

<details>
<summary>Changes</summary>

This PR adds a default parallel implementation of `std::reverse_copy` based on `std::reverse_iterator` + parallel `std::copy`.

Effectively it's a one-liner:
```c++
    return _Copy()(__policy,
                   std::reverse_iterator&lt;_BidirectionalIterator&gt;(std::move(__last)),
                   std::reverse_iterator&lt;_BidirectionalIterator&gt;(std::move(__first)),
                   std::move(__result));
```

Included tests check that:

- Semantics of the function is correct.
- The function correctly SFINAE out when the first argument is not an execution policy.
- The `nodiscard` policy is followed.
- The `noexcept` policy is followed.
- `static_assert` verifies iterators' categories (bidirectional and forward).

Part of #<!-- -->99938.

---
Full diff: https://github.com/llvm/llvm-project/pull/208085.diff


7 Files Affected:

- (modified) libcxx/include/__algorithm/pstl.h (+17) 
- (modified) libcxx/include/__pstl/backend_fwd.h (+7) 
- (modified) libcxx/include/__pstl/backends/default.h (+18) 
- (modified) libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp (+12-2) 
- (modified) libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp (+2) 
- (added) libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp (+116) 
- (modified) libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp (+7) 


``````````diff
diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 10625ea3f8e3d..55654637df57f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -347,6 +347,23 @@ generate_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n, _Ge
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__n), std::move(__gen));
 }
 
+template <class _ExecutionPolicy,
+          class _BidirectionalIterator,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator reverse_copy(
+    _ExecutionPolicy&& __policy,
+    _BidirectionalIterator __first,
+    _BidirectionalIterator __last,
+    _ForwardIterator __result) {
+  _LIBCPP_REQUIRE_CPP17_BIDIRECTIONAL_ITERATOR(_BidirectionalIterator, "reverse_copy requires a BidirectionalIterator");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reverse_copy requires a ForwardIterator");
+  using _Implementation = __pstl::__dispatch<__pstl::__reverse_copy, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__result));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator,
           class _Predicate,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a52e6db954d0c..6b72350d43a10 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -159,6 +159,13 @@ struct __generate_n;
 // optional<__empty>
 // operator()(_Policy&&, _ForwardIterator __first, _Size __n, _Generator __gen) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __reverse_copy;
+// template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
+// optional<_ForwardIterator>
+// operator()(_Policy&&, _BidirectionalIterator __first, _BidirectionalIterator __last,
+//                       _ForwardIterator __result) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __merge;
 // template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Comp>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index be90715af13b5..49ab69b497ddd 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -21,6 +21,7 @@
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
+#include <__iterator/reverse_iterator.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
 #include <__utility/empty.h>
@@ -92,6 +93,7 @@ namespace __pstl {
 // - move
 // - copy
 // - copy_n
+// - reverse_copy
 // - rotate_copy
 //
 
@@ -529,6 +531,22 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __reverse_copy<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+  operator()(_Policy&& __policy,
+             _BidirectionalIterator __first,
+             _BidirectionalIterator __last,
+             _ForwardIterator __result) const noexcept {
+    using _Copy = __dispatch<__copy, __current_configuration, _ExecutionPolicy>;
+    return _Copy()(__policy,
+                   std::reverse_iterator<_BidirectionalIterator>(std::move(__last)),
+                   std::reverse_iterator<_BidirectionalIterator>(std::move(__first)),
+                   std::move(__result));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e5bd7e764c59b..4aa435245e863 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -31,12 +31,16 @@
 
 #include "test_iterators.h"
 
-using non_forward_iterator = cpp17_input_iterator<int*>;
+using non_forward_iterator       = cpp17_input_iterator<int*>;
+using non_bidirectional_iterator = forward_iterator<int*>;
 struct non_output_iterator : forward_iterator<int*> {
   constexpr int const& operator*() const; // prevent it from being an output iterator
 };
 
-void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execution::sequenced_policy pol) {
+void f(non_forward_iterator non_fwd,
+       non_output_iterator non_output,
+       non_bidirectional_iterator non_bidir,
+       std::execution::sequenced_policy pol) {
   auto pred     = [](auto&&...) -> bool { return true; };
   auto func     = [](auto&&...) -> int { return 1; };
   int* it       = nullptr;
@@ -99,6 +103,12 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::generate_n(pol, non_fwd, n, func);     // expected-error@*:* {{static assertion failed: generate_n}}
   }
 
+  {
+    (void)std::reverse_copy(
+        pol, non_bidir, non_bidir, it);            // expected-error@*:* {{static assertion failed: reverse_copy}}
+    (void)std::reverse_copy(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: reverse_copy}}
+  }
+
   {
     (void)std::is_partitioned(
         pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: is_partitioned}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index b6554cef6f32a..38e85fb3b6b83 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -52,4 +52,6 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
new file mode 100644
index 0000000000000..b805fab2f1fb0
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class BidirectionalIterator,
+//           class ForwardIterator>
+//   ForwardIterator reverse_copy(ExecutionPolicy&& exec,
+//                                BidirectionalIterator first,
+//                                BidirectionalIterator last,
+//                                ForwardIterator result);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(reverse_copy);
+
+static_assert(sfinae_test_reverse_copy<int, int*, int*, int*>);
+static_assert(!sfinae_test_reverse_copy<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[]              = {0};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      assert(ib[0] == -1);
+      r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(ia[0] == 0);
+    }
+    {
+      int ia[]              = {0, 1};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 1);
+      assert(ib[1] == 0);
+    }
+    {
+      int ia[]              = {0, 1, 2};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 2);
+      assert(ib[1] == 1);
+      assert(ib[2] == 0);
+    }
+    {
+      int ia[]              = {0, 1, 2, 3};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 3);
+      assert(ib[1] == 2);
+      assert(ib[2] == 1);
+      assert(ib[3] == 0);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia) + 1, [&](size_t i) {
+        Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j) {
+          assert(ib[j] == static_cast<int>(i - j));
+        }
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::bidirectional_iterator_list<int*>, types::bidirectional_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+}
\ No newline at end of file
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a3db159289393..546862169813e 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -195,6 +195,13 @@ int main(int, char**) {
         assert_non_throwing([=, &policy] { (void)std::generate_n(policy, std::move(first1), n, gen); });
       }
 
+      {
+        // reverse_copy(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::reverse_copy(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+      }
+
       {
         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/208085

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:52:47 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 08 Jul 2026 07:52:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4e643f.07d7ca98.9dee3.b335@mx.google.com>


================
@@ -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++20
+
+// Test the libc++ extension that std::ranges::lazy_split_view and std::views::lazy_split are marked as [[nodiscard]].
+
+#include <ranges>
+#include <string>
+#include <utility>
+
+void test() {
+  std::string str = "the quick brown fox";
+  char pattern    = ' ';
+
+  auto v = std::views::lazy_split(str, pattern);
+
+  // [range.lazy.split.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.lazy.split.outer-iterator]
----------------
Zingam wrote:



We test here: https://eel.is/c++draft/range.lazy.split.outer

Also lets have these sections:

https://eel.is/c++draft/range.lazy.split#inner
https://eel.is/c++draft/range.lazy.split#outer.value


```suggestion
  // [range.lazy.split.outer]
  
  ...
  
  // [range.lazy.split.outer.value]
  
  ...

 // [range.lazy.split.inner]
 
 ...
```

https://github.com/llvm/llvm-project/pull/208036

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:54:05 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:54:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Simplify a bunch of noexcept
	specifications (PR #166397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/166397@github.com>
Message-ID: <6a4e648d.c56fb102.7c9cd.38d1@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/166397

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:54:05 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:54:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Simplify a bunch of noexcept
	specifications (PR #166397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/166397@github.com>
Message-ID: <6a4e648d.7d41f3a1.237e53.6892@mx.google.com>


https://github.com/ldionne commented:

I think this makes sense, but let's address review comments and document the strenghtenings (only to public APIs)

https://github.com/llvm/llvm-project/pull/166397

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:54:05 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:54:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Simplify a bunch of noexcept
	specifications (PR #166397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/166397@github.com>
Message-ID: <6a4e648d.ac469dc7.3a57b1.a37a@mx.google.com>


================
@@ -915,15 +897,7 @@ public:
   }
 #  endif
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(list& __c)
-#  if _LIBCPP_STD_VER >= 14
-      _NOEXCEPT
-#  else
-      _NOEXCEPT_(!__node_alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>)
-#  endif
-  {
-    __base::swap(__c);
-  }
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(list& __c) _NOEXCEPT { __base::swap(__c); }
----------------
ldionne wrote:

```suggestion
  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI void swap(list& __c) _NOEXCEPT { __base::swap(__c); } // strengthened(never throws)
```

Per https://github.com/llvm/llvm-project/pull/203869

https://github.com/llvm/llvm-project/pull/166397

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:54:05 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:54:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Simplify a bunch of noexcept
	specifications (PR #166397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/166397@github.com>
Message-ID: <6a4e648d.cbc3f03a.10d998.4656@mx.google.com>


================
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <vector>
+
+#include <type_traits>
+#include <vector>
+
+#include "test_allocator.h"
+
+namespace std_allocator {
+using vec = std::vector<int>;
+
+static_assert(std::is_nothrow_constructible<vec>::value);
----------------
ldionne wrote:

Instead of adding a `noexcept.compile.pass` test under `libcxx/test/libcxx`, let's go to each operation's existing tests under `libcxx/test/std` and add a `LIBCPP_STATIC_ASSERT` for the right noexcept-ness. It would catch things like the missed update to

```
#if TEST_STD_VER > 14
  static_assert((noexcept(C{typename C::allocator_type{}})), "");
#elif TEST_STD_VER >= 11
  static_assert((noexcept(C(typename C::allocator_type())) ==
                 std::is_nothrow_copy_constructible<typename C::allocator_type>::value),
                "");
```

in `libcxx/test/std/containers/sequences/vector/vector.cons/construct_default.pass.cpp`.

https://github.com/llvm/llvm-project/pull/166397

From libcxx-commits at lists.llvm.org  Wed Jul  8 07:54:05 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 07:54:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Simplify a bunch of noexcept
	specifications (PR #166397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/166397@github.com>
Message-ID: <6a4e648d.7270d2fd.2d4879.1e17@mx.google.com>


================
@@ -744,13 +729,10 @@ public:
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
   list(initializer_list<value_type> __il, const allocator_type& __a);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list(list&& __c)
-      _NOEXCEPT_(is_nothrow_move_constructible<__node_allocator>::value);
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list(list&& __c) noexcept;
----------------
ldionne wrote:

```suggestion
  _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list(list&& __c) noexcept; // strengthened(never throws)
```

https://github.com/llvm/llvm-project/pull/166397

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:01:56 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:01:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a4e6664.3cce8983.225732.cc29@mx.google.com>


================
@@ -528,6 +552,20 @@ inline const bool __allocator_has_trivial_move_construct_v = !__has_construct_v<
 template <class _Type>
 inline const bool __allocator_has_trivial_move_construct_v<allocator<_Type>, _Type> = true;
 
+template <class _Alloc, class _InIter, class _Sent, class _OutIter>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
+__uninitialized_allocator_move(_Alloc& __alloc, _InIter __first, _Sent __last, _OutIter __result) {
----------------
ldionne wrote:

Since there's a behavior change, we should have a libc++ specific test about it.

https://github.com/llvm/llvm-project/pull/207500

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:01:56 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:01:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a4e6664.70920dee.290dca.77de@mx.google.com>


================
@@ -880,10 +880,7 @@ vector<_Tp, _Allocator>::__recommend(size_type __new_size) const {
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__construct_at_end(size_type __n) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos));
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_value_construct(__layout_.__alloc(), __tx.__pos_, __tx.__new_end_);
----------------
ldionne wrote:

Can't we get rid of the transaction now?

https://github.com/llvm/llvm-project/pull/207500

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:01:58 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:01:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a4e6666.f732ad81.3a4994.0f1b@mx.google.com>


================
@@ -450,6 +450,30 @@ __uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _B
 
----------------
ldionne wrote:

This change should come with a release note explaining the behavior change.

https://github.com/llvm/llvm-project/pull/207500

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:03:13 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:03:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a4e66b1.510f576d.1b3cb9.0a18@mx.google.com>


ldionne wrote:

Let's also run the benchmarks for vector (use the bot)

https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:03:46 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 08:03:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Default the allocator argument
 for most string constructors (PR #169901)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/169901@github.com>
Message-ID: <6a4e66d2.bd93e731.358318.416b@mx.google.com>


bgra8 wrote:

@philnik777 this CL is breaking code using strings with custom allocators.

This code no longer compiles after this revision:

```c++
#include <cstddef>
#include <memory>
#include <scoped_allocator>
#include <string>
#include <type_traits>

template <typename T>
struct Alloc {
  using value_type = T;
  using size_type = size_t;
  using difference_type = ptrdiff_t;

  constexpr Alloc(int) {}

  template <typename U>
  constexpr Alloc(Alloc<U>) {}

  friend constexpr bool operator==(Alloc, Alloc) { return true; }

  T* allocate(size_t);
  void deallocate(void*, size_t);
};

using AllocString =
    std::basic_string<char, std::char_traits<char>, Alloc<char>>;

int main() {
  printf("is_constructible<AllocString, char*>=%d\n",
         std::is_constructible_v<AllocString, char*>);
}
```

The error we get is:

```
src/libcxx/include/string:1010:84: error: no matching constructor for initialization of 'Alloc<char>'
 1010 |   basic_string(const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __s, const _Allocator& __a = _Allocator())
      |                                                                                    ^
src/libcxx/include/__type_traits/is_constructible.h:27:71: note: in instantiation of default function argument expression for 'basic_string<0>' required here
   27 | _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_constructible_v = __is_constructible(_Tp, _Args...);
```

Can you please take a look?

https://github.com/llvm/llvm-project/pull/169901

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:07:28 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 08 Jul 2026 08:07:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4e67b0.eb165e5c.1817a5.22b0@mx.google.com>


================

----------------
Zingam wrote:

Part of `// [range.lazy.split.outer.value]`

```suggestion
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
```

https://github.com/llvm/llvm-project/pull/208036

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:17:55 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 08 Jul 2026 08:17:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4e6a23.f8b79514.1d52d0.2d07@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/208036

>From 1a1da3d615018ef52c9eefcbb376679f5cdd7505 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 18:00:56 +0200
Subject: [PATCH 1/8] initial: nodiscard implementation to lazy_split_view.h

---
 libcxx/include/__ranges/lazy_split_view.h | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 938dca24cc4fc..01d398a0ac6d8 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -95,14 +95,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
       : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
@@ -111,19 +111,19 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<_View> && forward_range<const _View>
   {
     return __outer_iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
     return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
     if constexpr (forward_range<_View> && forward_range<const _View> && common_range<const _View>) {
       return __outer_iterator<true>{*this, ranges::end(__base_)};
     } else {
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
@@ -342,14 +342,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
-    _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
       return std::move(__i_.__current());
     }
 
-    _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
       __incremented_ = true;
@@ -385,7 +385,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       return __x.__is_done();
     }
 
-    _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
     iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(__i.__outer_current()))) {
       return ranges::iter_move(__i.__outer_current());
     }

>From 845a2090e07f251f9a46f849f149d5662ecdbc45 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 22:49:19 +0200
Subject: [PATCH 2/8] add: initial tests and clang-format fix

---
 libcxx/include/__ranges/lazy_split_view.h     | 12 +++++----
 .../range.lazy.split/nodiscard.verify.cpp     | 27 +++++++++++++++++++
 2 files changed, 34 insertions(+), 5 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 01d398a0ac6d8..5e336cbfcac49 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
+      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
+    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -342,7 +342,9 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
+      return __i_.__current();
+    }
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
@@ -404,8 +406,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&,
-                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, range_value_t<_Range>)
+    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cd15b33b66d59
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  // [range.lazy.split.overview]
+
+  std::string str = "the quick brown fox";
+  char pattern    = ' ';
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::split(str, pattern);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::split(pattern);
+}

>From 054f55611b632030795527d777509c70609e5df0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 22:57:55 +0200
Subject: [PATCH 3/8] fix: minor fixes for header include and lazy_split

---
 .../range.adaptors/range.lazy.split/nodiscard.verify.cpp  | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index cd15b33b66d59..c6420b0adf5d5 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -11,17 +11,19 @@
 // Check that functions are marked [[nodiscard]]
 
 #include <ranges>
+#include <string>
 #include <utility>
 
 void test() {
   // [range.lazy.split.overview]
 
-  std::string str = "the quick brown fox";
+  std::string str { "the quick brown fox" };
   char pattern    = ' ';
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::split(str, pattern);
+  std::views::lazy_split(str, pattern);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::split(pattern);
+  std::views::lazy_split(pattern);
+
 }

>From fbf22aa66f5d3205c82bbba057664780a8fbe057 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 23:32:22 +0200
Subject: [PATCH 4/8] add: test cases and remove nodiscard in implementation
 detail

---
 libcxx/include/__ranges/lazy_split_view.h     |  2 +-
 .../range.lazy.split/nodiscard.verify.cpp     | 41 ++++++++++++++++---
 2 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 5e336cbfcac49..66f18d5edad76 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index c6420b0adf5d5..b80f6f00836b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -8,22 +8,53 @@
 
 // REQUIRES: std-at-least-c++20
 
-// Check that functions are marked [[nodiscard]]
+// Test the libc++ extension that std::ranges::lazy_split_view and std::views::lazy_split are marked as [[nodiscard]].
 
 #include <ranges>
 #include <string>
 #include <utility>
 
 void test() {
-  // [range.lazy.split.overview]
-
-  std::string str { "the quick brown fox" };
+  std::string str = "the quick brown fox";
   char pattern    = ' ';
 
+  auto v = std::views::lazy_split(str, pattern);
+
+  // [range.lazy.split.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.lazy.split.iterator]
+
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it.base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(c_it);
+
+  // [range.lazy.split.overview]
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::lazy_split(str, pattern);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::lazy_split(pattern);
-
 }

>From 997525b35d5e7a64e9db03541cfbc7acf934f1da Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 12:01:40 +0200
Subject: [PATCH 5/8] revert: accidental format changes

---
 libcxx/include/__ranges/lazy_split_view.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 66f18d5edad76..c9a941abe7ff9 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
+      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
+    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -406,8 +406,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&, range_value_t<_Range>)
-    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, 
+                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {

>From 598aacef1fcf0d98cce6e0fd4b1c7ef3ff8e64db Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 12:02:50 +0200
Subject: [PATCH 6/8] revert: accidental format changes

---
 libcxx/include/__ranges/lazy_split_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index c9a941abe7ff9..fc41f5c49523b 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::begin(__base_)};
+      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::end(__base_)};
+    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -406,7 +406,7 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&, 
+lazy_split_view(_Range&&,
                 range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {

>From f81b6437ddbec10579a87c58dca1286b63f10763 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 16:32:21 +0200
Subject: [PATCH 7/8] fix: __outer_iterator to __inner_iterator nodiscard
 testing

---
 libcxx/include/__ranges/lazy_split_view.h              |  2 +-
 .../range.lazy.split/nodiscard.verify.cpp              | 10 +++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index fc41f5c49523b..c05c932d09703 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index b80f6f00836b0..ab865c7c94b85 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -37,18 +37,18 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).end();
 
-  // [range.lazy.split.iterator]
+  // [range.lazy.split.outer-iterator]
 
-  auto c_it = std::as_const(v).begin();
+  auto c_it = as_const(v).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  c_it.base();
+  (*c_it).begin().base();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *c_it;
+  (*c_it);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  iter_move(c_it);
+  iter_move((*c_it).begin());
 
   // [range.lazy.split.overview]
 

>From f8a987f6a23e981178760868a13059d73a2e52da Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 17:17:36 +0200
Subject: [PATCH 8/8] Apply suggestion from @Zingam

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 libcxx/include/__ranges/lazy_split_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index c05c932d09703..b628df397739b 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -189,7 +189,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(__outer_iterator __i) : __i_(std::move(__i)) {}
 
       _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; }
-      _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; }
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
     };
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;


From libcxx-commits at lists.llvm.org  Wed Jul  8 08:17:57 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:17:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::find_first_of (PR #206328)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206328@github.com>
Message-ID: <6a4e6a25.abb10101.3d0bf8.94f7@mx.google.com>


================
@@ -181,6 +186,30 @@ struct __is_partitioned<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    using _FindIf = __dispatch<__find_if, __current_configuration, _ExecutionPolicy>;
+    using _Ref1   = __iterator_reference<_ForwardIterator1>;
+    using _Ref2   = __iterator_reference<_ForwardIterator2>;
+    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
+      if constexpr (__desugars_to_v<__equal_tag, _Predicate, __remove_cvref_t<_Ref1>, __remove_cvref_t<_Ref2>>) {
----------------
ldionne wrote:

Note that the above is actually incorrect: `__desugars_to` DOES NOT strip cv refs on the argument types. However, we do intend to pass real argument types to it, not cv-ref stripped types.

https://github.com/llvm/llvm-project/pull/206328

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:18:36 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:18:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::find_first_of (PR #206328)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206328@github.com>
Message-ID: <6a4e6a4c.331483e7.2440f8.66ee@mx.google.com>


https://github.com/ldionne auto_merge_enabled https://github.com/llvm/llvm-project/pull/206328

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:19:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 08:19:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 3e7a2ec - [libc++][pstl] Default
 implementation of parallel std::find_first_of (#206328)
Message-ID: <6a4e6a65.e9d484b9.15e549.5d52@mx.google.com>


Author: Michael G. Kazakov
Date: 2026-07-08T11:18:56-04:00
New Revision: 3e7a2ec74cd8f8e0e32037d0192a1378789e9af7

URL: https://github.com/llvm/llvm-project/commit/3e7a2ec74cd8f8e0e32037d0192a1378789e9af7
DIFF: https://github.com/llvm/llvm-project/commit/3e7a2ec74cd8f8e0e32037d0192a1378789e9af7.diff

LOG: [libc++][pstl] Default implementation of parallel std::find_first_of (#206328)

This PR adds a default "one-liner" implementation of parallel
`std::find_first_of` expressed as a call to `__find_if`.

The implementation is based on find_if and any_of.


Part of #99938

Added: 
    libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp

Modified: 
    libcxx/include/__algorithm/pstl.h
    libcxx/include/__pstl/backend_fwd.h
    libcxx/include/__pstl/backends/default.h
    libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
    libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
    libcxx/test/libcxx/transitive_includes/cxx23.csv
    libcxx/test/libcxx/transitive_includes/cxx26.csv
    libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
    libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 10625ea3f8e3d..3530b3207f59e 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -293,6 +293,54 @@ find(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __l
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::equal_to<>{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_first_of(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_first_of requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_first_of requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_first_of, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator,
           class _Function,

diff  --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a52e6db954d0c..1ea121367fbc7 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -109,6 +109,13 @@ struct __is_partitioned;
 // optional<bool>
 // operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __find_first_of;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+// optional<_ForwardIterator1>
+// operator()(_Policy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2, _Predicate __pred) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __for_each;
 // template <class _Policy, class _ForwardIterator, class _Function>

diff  --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index be90715af13b5..cb963a130f743 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -12,6 +12,8 @@
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
+#include <__algorithm/find.h>
+#include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
 #include <__config>
@@ -23,6 +25,7 @@
 #include <__iterator/next.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
+#include <__type_traits/desugars_to.h>
 #include <__utility/empty.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -57,6 +60,7 @@ namespace __pstl {
 // - all_of
 // - none_of
 // - is_partitioned
+// - find_first_of
 //
 // for_each family
 // ---------------
@@ -181,6 +185,30 @@ struct __is_partitioned<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    using _FindIf = __dispatch<__find_if, __current_configuration, _ExecutionPolicy>;
+    using _Ref1   = __iterator_reference<_ForwardIterator1>;
+    using _Ref2   = __iterator_reference<_ForwardIterator2>;
+    return _FindIf()(__policy, std::move(__first1), std::move(__last1), [&](_Ref1 __element) {
+      if constexpr (__desugars_to_v<__equal_tag, _Predicate, _Ref1, _Ref2>) {
+        // bypass an equality predicate and call directly to std::find() to allow more vectorization
+        return std::find(__first2, __last2, __element) != __last2;
+      } else {
+        return std::find_if(__first2, __last2, [&](_Ref2 __value) { return __pred(__element, __value); }) != __last2;
+      }
+    });
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////

diff  --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e5bd7e764c59b..aee3f97019071 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -89,6 +89,17 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::find_if_not(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_if_not}}
   }
 
+  {
+    (void)std::find_first_of(
+        pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_first_of}}
+    (void)std::find_first_of(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: find_first_of}}
+    (void)std::find_first_of(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: find_first_of}}
+  }
+
   {
     (void)std::for_each(pol, non_fwd, non_fwd, func); // expected-error@*:* {{static assertion failed: for_each}}
     (void)std::for_each_n(pol, non_fwd, n, func);     // expected-error@*:* {{static assertion failed: for_each_n}}

diff  --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index b6554cef6f32a..747d62bc66837 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -47,6 +47,10 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::find(std::execution::par, std::begin(a), std::end(a), 1);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::find_first_of(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::find_first_of(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_partitioned(std::execution::par, std::begin(a), std::end(a), pred);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 8385b37c07822..53ae6c9b4e591 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -704,11 +704,14 @@ mutex typeinfo
 mutex version
 new version
 numbers version
+numeric cctype
 numeric climits
 numeric compare
 numeric cstdint
 numeric cstring
 numeric ctime
+numeric cwchar
+numeric cwctype
 numeric initializer_list
 numeric limits
 numeric optional

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 6abceca182a95..f712f36caa9fc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -655,11 +655,14 @@ mutex tuple
 mutex version
 new version
 numbers version
+numeric cctype
 numeric climits
 numeric compare
 numeric cstdint
 numeric cstring
 numeric ctime
+numeric cwchar
+numeric cwctype
 numeric initializer_list
 numeric limits
 numeric optional

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
index efa54bbc8fd66..25a223e274706 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of.pass.cpp
@@ -16,59 +16,82 @@
 #include <algorithm>
 #include <cassert>
 #include <iterator>
+#include <numeric>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
 #if TEST_STD_VER > 17
 TEST_CONSTEXPR bool test_constexpr() {
-    int ia[] = {1, 2, 3};
-    int ib[] = {7, 8, 9};
-    int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
-    typedef forward_iterator<int*>       FI;
-    typedef bidirectional_iterator<int*> BI;
-    typedef random_access_iterator<int*> RI;
+  int ia[] = {1, 2, 3};
+  int ib[] = {7, 8, 9};
+  int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
+  typedef forward_iterator<int*> FI;
+  typedef bidirectional_iterator<int*> BI;
+  typedef random_access_iterator<int*> RI;
 
-    return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia))) == FI(ic+1))
-           && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib))) == FI(std::end(ic)))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia))) == BI(ic+1))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib))) == BI(std::end(ic)))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia))) == RI(ic+1))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib))) == RI(std::end(ic)))
-           ;
-    }
+  return (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia))) ==
+          FI(ic + 1)) &&
+         (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib))) ==
+          FI(std::end(ic))) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia))) ==
+          BI(ic + 1)) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib))) ==
+          BI(std::end(ic))) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia))) ==
+          RI(ic + 1)) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib))) ==
+          RI(std::end(ic)));
+}
 #endif
 
-int main(int, char**)
-{
-    int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
-    const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-    int ib[] = {1, 3, 5, 7};
-    const unsigned sb = sizeof(ib)/sizeof(ib[0]);
+int main(int, char**) {
+  {
+    int ia[]          = {0, 1, 2, 3, 0, 1, 2, 3};
+    const unsigned sa = sizeof(ia) / sizeof(ia[0]);
+    int ib[]          = {1, 3, 5, 7};
+    const unsigned sb = sizeof(ib) / sizeof(ib[0]);
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia + sa),
                               forward_iterator<const int*>(ib),
-                              forward_iterator<const int*>(ib + sb)) ==
-                              cpp17_input_iterator<const int*>(ia+1));
+                              forward_iterator<const int*>(ib + sb)) == cpp17_input_iterator<const int*>(ia + 1));
     int ic[] = {7};
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia + sa),
                               forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic + 1)) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
+                              forward_iterator<const int*>(ic + 1)) == cpp17_input_iterator<const int*>(ia + sa));
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia + sa),
                               forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic)) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
+                              forward_iterator<const int*>(ic)) == cpp17_input_iterator<const int*>(ia + sa));
     assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
                               cpp17_input_iterator<const int*>(ia),
                               forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic+1)) ==
-                              cpp17_input_iterator<const int*>(ia));
+                              forward_iterator<const int*>(ic + 1)) == cpp17_input_iterator<const int*>(ia));
+  }
+  {
+    int a[8192];
+    std::iota(std::begin(a), std::end(a), 1);
+    a[1023] = -1;
+    a[2048] = -1;
+    a[3071] = -1;
+    int b[] = {-1, 999999};
+    assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b)) == std::begin(a) + 1023);
+  }
+  {
+    int a[1073];
+    const unsigned sa = sizeof(a) / sizeof(a[0]);
+    std::iota(std::begin(a), std::end(a), 0);
+    int b[] = {1073, 1074, 1075, -1};
+    for (unsigned i = 0; i < sa; i = i < 16 ? i + 1 : unsigned(3.1415 * i)) {
+      a[i] = -1;
+      assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b)) == std::begin(a) + i);
+      a[i] = i;
+    }
+  }
 
 #if TEST_STD_VER > 17
-    static_assert(test_constexpr());
+  static_assert(test_constexpr());
 #endif
 
   return 0;

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
index fd2e4d426fbf9..9fdc4081b0141 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/find_first_of_pred.pass.cpp
@@ -18,63 +18,96 @@
 #include <cassert>
 #include <functional>
 #include <iterator>
+#include <numeric>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
+struct Pred {
+  bool operator()(int l, int r) const {
+    return l + 1 == r; // ensures that the predicate is not equivalent to std::equal_to
+  }
+};
+
 #if TEST_STD_VER > 17
 constexpr bool test_constexpr() {
-    int ia[] = {1, 2, 3};
-    int ib[] = {7, 8, 9};
-    int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
-    typedef forward_iterator<int*>       FI;
-    typedef bidirectional_iterator<int*> BI;
-    typedef random_access_iterator<int*> RI;
-    std::equal_to<int> eq{};
-    return    (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia)), eq) == FI(ic+1))
-           && (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib)), eq) == FI(std::end(ic)))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia)), eq) == BI(ic+1))
-           && (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib)), eq) == BI(std::end(ic)))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia)), eq) == RI(ic+1))
-           && (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib)), eq) == RI(std::end(ic)))
-           ;
-    }
+  int ia[] = {1, 2, 3};
+  int ib[] = {7, 8, 9};
+  int ic[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};
+  typedef forward_iterator<int*> FI;
+  typedef bidirectional_iterator<int*> BI;
+  typedef random_access_iterator<int*> RI;
+  std::equal_to<int> eq{};
+  return (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ia)), FI(std::end(ia)), eq) ==
+          FI(ic + 1)) &&
+         (std::find_first_of(FI(std::begin(ic)), FI(std::end(ic)), FI(std::begin(ib)), FI(std::end(ib)), eq) ==
+          FI(std::end(ic))) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ia)), BI(std::end(ia)), eq) ==
+          BI(ic + 1)) &&
+         (std::find_first_of(BI(std::begin(ic)), BI(std::end(ic)), BI(std::begin(ib)), BI(std::end(ib)), eq) ==
+          BI(std::end(ic))) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ia)), RI(std::end(ia)), eq) ==
+          RI(ic + 1)) &&
+         (std::find_first_of(RI(std::begin(ic)), RI(std::end(ic)), RI(std::begin(ib)), RI(std::end(ib)), eq) ==
+          RI(std::end(ic)));
+}
 #endif
 
-int main(int, char**)
-{
-    int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
-    const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-    int ib[] = {1, 3, 5, 7};
-    const unsigned sb = sizeof(ib)/sizeof(ib[0]);
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia + sa),
-                              forward_iterator<const int*>(ib),
-                              forward_iterator<const int*>(ib + sb),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia+1));
+int main(int, char**) {
+  {
+    int ia[]          = {0, 1, 2, 3, 0, 1, 2, 3};
+    const unsigned sa = sizeof(ia) / sizeof(ia[0]);
+    int ib[]          = {1, 3, 5, 7};
+    const unsigned sb = sizeof(ib) / sizeof(ib[0]);
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia + sa),
+               forward_iterator<const int*>(ib),
+               forward_iterator<const int*>(ib + sb),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia + 1));
     int ic[] = {7};
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia + sa),
-                              forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic + 1),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia + sa),
-                              forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia+sa));
-    assert(std::find_first_of(cpp17_input_iterator<const int*>(ia),
-                              cpp17_input_iterator<const int*>(ia),
-                              forward_iterator<const int*>(ic),
-                              forward_iterator<const int*>(ic+1),
-                              std::equal_to<int>()) ==
-                              cpp17_input_iterator<const int*>(ia));
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia + sa),
+               forward_iterator<const int*>(ic),
+               forward_iterator<const int*>(ic + 1),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia + sa));
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia + sa),
+               forward_iterator<const int*>(ic),
+               forward_iterator<const int*>(ic),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia + sa));
+    assert(std::find_first_of(
+               cpp17_input_iterator<const int*>(ia),
+               cpp17_input_iterator<const int*>(ia),
+               forward_iterator<const int*>(ic),
+               forward_iterator<const int*>(ic + 1),
+               std::equal_to<int>()) == cpp17_input_iterator<const int*>(ia));
+  }
+  {
+    int a[8192];
+    std::iota(std::begin(a), std::end(a), 1);
+    a[1023] = -2;
+    a[2048] = -2;
+    a[3071] = -2;
+    int b[] = {-1, 999999};
+    assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b), Pred()) == std::begin(a) + 1023);
+  }
+  {
+    int a[1073];
+    const unsigned sa = sizeof(a) / sizeof(a[0]);
+    std::iota(std::begin(a), std::end(a), 0);
+    int b[] = {1074, 1075, 1076, -1};
+    for (unsigned i = 0; i < sa; i = i < 16 ? i + 1 : unsigned(3.1415 * i)) {
+      a[i] = -2;
+      assert(std::find_first_of(std::begin(a), std::end(a), std::begin(b), std::end(b), Pred()) == std::begin(a) + i);
+      a[i] = i;
+    };
+  }
 
 #if TEST_STD_VER > 17
-    static_assert(test_constexpr());
+  static_assert(test_constexpr());
 #endif
 
   return 0;

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
new file mode 100644
index 0000000000000..118b34da0cb6c
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator1 find_first_of(ExecutionPolicy&& exec,
+//                                  ForwardIterator1 first1,
+//                                  ForwardIterator1 last1,
+//                                  ForwardIterator2 first2,
+//                                  ForwardIterator2 last2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(find_first_of);
+
+static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[] = {0};
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)), Iter2(std::begin(a))) ==
+             Iter1(std::begin(a)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), Iter2(std::begin(a))) ==
+             Iter1(std::end(a)));
+    }
+    {
+      int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int ib[] = {1, 3, 5, 7};
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Iter2(std::end(ib))) ==
+             Iter1(std::begin(ia) + 1));
+      int ic[] = {7};
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ic)), Iter2(std::end(ic))) ==
+             Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ic)), Iter2(std::begin(ic))) ==
+             Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ic)), Iter2(std::end(ic))) ==
+             Iter1(std::begin(ia)));
+    }
+    {
+      int a[8192];
+      std::iota(std::begin(a), std::end(a), 1);
+      a[1023] = -1;
+      a[2048] = -1;
+      a[3071] = -1;
+      int b[] = {-1, 999999};
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))) ==
+             Iter1(std::begin(a) + 1023));
+    }
+    {
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 0);
+      int b[] = {1073, 1074, 1075, -1};
+      runway_sample(std::size(a), [&](size_t i) {
+        a[i] = -1;
+        assert(std::find_first_of(
+                   policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))) ==
+               Iter1(std::begin(a) + i));
+        a[i] = i;
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies< types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+
+  return 0;
+}

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
new file mode 100644
index 0000000000000..1033b52e4621f
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.first.of/pstl.find_first_of_pred.pass.cpp
@@ -0,0 +1,140 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   ForwardIterator1 find_first_of(ExecutionPolicy&& exec,
+//                                  ForwardIterator1 first1,
+//                                  ForwardIterator1 last1,
+//                                  ForwardIterator2 first2,
+//                                  ForwardIterator2 last2,
+//                                  BinaryPredicate p);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(find_first_of);
+
+static_assert(sfinae_test_find_first_of<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_find_first_of<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+struct Pred {
+  bool operator()(int l, int r) const {
+    return l + 1 == r; // ensures that the predicate is not equivalent to std::equal_to
+  }
+};
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[] = {0};
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)), Iter2(std::begin(a)), Pred{}) ==
+          Iter1(std::begin(a)));
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), Iter2(std::begin(a)), Pred{}) ==
+          Iter1(std::end(a)));
+    }
+    {
+      int ia[] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int ib[] = {2, 4, 6, 8};
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Iter2(std::end(ib)), Pred{}) ==
+          Iter1(std::begin(ia) + 1));
+      int ic[] = {7};
+      assert(
+          std::find_first_of(
+              policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ic)), Iter2(std::end(ic)), Pred{}) ==
+          Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy,
+                 Iter1(std::begin(ia)),
+                 Iter1(std::end(ia)),
+                 Iter2(std::begin(ic)),
+                 Iter2(std::begin(ic)),
+                 Pred{}) == Iter1(std::end(ia)));
+      assert(std::find_first_of(
+                 policy,
+                 Iter1(std::begin(ia)),
+                 Iter1(std::begin(ia)),
+                 Iter2(std::begin(ic)),
+                 Iter2(std::end(ic)),
+                 Pred{}) == Iter1(std::begin(ia)));
+    }
+    {
+      int a[8192];
+      std::iota(std::begin(a), std::end(a), 1);
+      a[1023] = -2;
+      a[2048] = -2;
+      a[3071] = -2;
+      int b[] = {-1, 999999};
+      assert(std::find_first_of(
+                 policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{}) ==
+             Iter1(std::begin(a) + 1023));
+    }
+    {
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 0);
+      int b[] = {1074, 1075, 1076, -1};
+      runway_sample(std::size(a), [&](size_t i) {
+        a[i] = -2;
+        assert(
+            std::find_first_of(
+                policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{}) ==
+            Iter1(std::begin(a) + i));
+        a[i] = i;
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies< types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+
+  return 0;
+}

diff  --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a3db159289393..9e6c8f5b8ce40 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -175,6 +175,21 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; });
+
+        // find_first_of(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::find_first_of(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // find_first_of(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::find_first_of(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), pred);
+        });
+      }
+
       {
         auto func = maybe_throw(tokens[5], [](int) {});
 


        

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:19:10 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:19:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::find_first_of (PR #206328)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206328@github.com>
Message-ID: <6a4e6a6e.af6ba3ed.36d9db.e23e@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/206328

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:35:56 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 08 Jul 2026 08:35:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4e6e5c.faf16b37.2f0e1d.4a3b@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/208036

>From 1a1da3d615018ef52c9eefcbb376679f5cdd7505 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 18:00:56 +0200
Subject: [PATCH 1/9] initial: nodiscard implementation to lazy_split_view.h

---
 libcxx/include/__ranges/lazy_split_view.h | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 938dca24cc4fc..01d398a0ac6d8 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -95,14 +95,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
       : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
@@ -111,19 +111,19 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<_View> && forward_range<const _View>
   {
     return __outer_iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
     return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
     if constexpr (forward_range<_View> && forward_range<const _View> && common_range<const _View>) {
       return __outer_iterator<true>{*this, ranges::end(__base_)};
     } else {
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
@@ -342,14 +342,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
-    _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
       return std::move(__i_.__current());
     }
 
-    _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
       __incremented_ = true;
@@ -385,7 +385,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       return __x.__is_done();
     }
 
-    _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
     iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(__i.__outer_current()))) {
       return ranges::iter_move(__i.__outer_current());
     }

>From 845a2090e07f251f9a46f849f149d5662ecdbc45 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 22:49:19 +0200
Subject: [PATCH 2/9] add: initial tests and clang-format fix

---
 libcxx/include/__ranges/lazy_split_view.h     | 12 +++++----
 .../range.lazy.split/nodiscard.verify.cpp     | 27 +++++++++++++++++++
 2 files changed, 34 insertions(+), 5 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 01d398a0ac6d8..5e336cbfcac49 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
+      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
+    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -342,7 +342,9 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
+      return __i_.__current();
+    }
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
@@ -404,8 +406,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&,
-                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, range_value_t<_Range>)
+    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cd15b33b66d59
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  // [range.lazy.split.overview]
+
+  std::string str = "the quick brown fox";
+  char pattern    = ' ';
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::split(str, pattern);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::split(pattern);
+}

>From 054f55611b632030795527d777509c70609e5df0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 22:57:55 +0200
Subject: [PATCH 3/9] fix: minor fixes for header include and lazy_split

---
 .../range.adaptors/range.lazy.split/nodiscard.verify.cpp  | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index cd15b33b66d59..c6420b0adf5d5 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -11,17 +11,19 @@
 // Check that functions are marked [[nodiscard]]
 
 #include <ranges>
+#include <string>
 #include <utility>
 
 void test() {
   // [range.lazy.split.overview]
 
-  std::string str = "the quick brown fox";
+  std::string str { "the quick brown fox" };
   char pattern    = ' ';
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::split(str, pattern);
+  std::views::lazy_split(str, pattern);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::split(pattern);
+  std::views::lazy_split(pattern);
+
 }

>From fbf22aa66f5d3205c82bbba057664780a8fbe057 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 23:32:22 +0200
Subject: [PATCH 4/9] add: test cases and remove nodiscard in implementation
 detail

---
 libcxx/include/__ranges/lazy_split_view.h     |  2 +-
 .../range.lazy.split/nodiscard.verify.cpp     | 41 ++++++++++++++++---
 2 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 5e336cbfcac49..66f18d5edad76 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index c6420b0adf5d5..b80f6f00836b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -8,22 +8,53 @@
 
 // REQUIRES: std-at-least-c++20
 
-// Check that functions are marked [[nodiscard]]
+// Test the libc++ extension that std::ranges::lazy_split_view and std::views::lazy_split are marked as [[nodiscard]].
 
 #include <ranges>
 #include <string>
 #include <utility>
 
 void test() {
-  // [range.lazy.split.overview]
-
-  std::string str { "the quick brown fox" };
+  std::string str = "the quick brown fox";
   char pattern    = ' ';
 
+  auto v = std::views::lazy_split(str, pattern);
+
+  // [range.lazy.split.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.lazy.split.iterator]
+
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it.base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(c_it);
+
+  // [range.lazy.split.overview]
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::lazy_split(str, pattern);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::lazy_split(pattern);
-
 }

>From 997525b35d5e7a64e9db03541cfbc7acf934f1da Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 12:01:40 +0200
Subject: [PATCH 5/9] revert: accidental format changes

---
 libcxx/include/__ranges/lazy_split_view.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 66f18d5edad76..c9a941abe7ff9 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
+      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
+    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -406,8 +406,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&, range_value_t<_Range>)
-    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, 
+                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {

>From 598aacef1fcf0d98cce6e0fd4b1c7ef3ff8e64db Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 12:02:50 +0200
Subject: [PATCH 6/9] revert: accidental format changes

---
 libcxx/include/__ranges/lazy_split_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index c9a941abe7ff9..fc41f5c49523b 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::begin(__base_)};
+      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::end(__base_)};
+    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -406,7 +406,7 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&, 
+lazy_split_view(_Range&&,
                 range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {

>From f81b6437ddbec10579a87c58dca1286b63f10763 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 16:32:21 +0200
Subject: [PATCH 7/9] fix: __outer_iterator to __inner_iterator nodiscard
 testing

---
 libcxx/include/__ranges/lazy_split_view.h              |  2 +-
 .../range.lazy.split/nodiscard.verify.cpp              | 10 +++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index fc41f5c49523b..c05c932d09703 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index b80f6f00836b0..ab865c7c94b85 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -37,18 +37,18 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).end();
 
-  // [range.lazy.split.iterator]
+  // [range.lazy.split.outer-iterator]
 
-  auto c_it = std::as_const(v).begin();
+  auto c_it = as_const(v).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  c_it.base();
+  (*c_it).begin().base();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *c_it;
+  (*c_it);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  iter_move(c_it);
+  iter_move((*c_it).begin());
 
   // [range.lazy.split.overview]
 

>From f8a987f6a23e981178760868a13059d73a2e52da Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 17:17:36 +0200
Subject: [PATCH 8/9] Apply suggestion from @Zingam

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 libcxx/include/__ranges/lazy_split_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index c05c932d09703..b628df397739b 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -189,7 +189,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(__outer_iterator __i) : __i_(std::move(__i)) {}
 
       _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; }
-      _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; }
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
     };
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;

>From 724b89b4fcb7a020f4942177ace2379f8bd191b1 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 17:35:40 +0200
Subject: [PATCH 9/9] fix: add outer, value and inner sections

---
 .../range.adaptors/range.lazy.split/nodiscard.verify.cpp  | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index ab865c7c94b85..bcc7273343d54 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -37,7 +37,7 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).end();
 
-  // [range.lazy.split.outer-iterator]
+  // [range.lazy.split.outer]
 
   auto c_it = as_const(v).begin();
 
@@ -50,6 +50,12 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   iter_move((*c_it).begin());
 
+  // [range.lazy.split.outer.value]
+  // wip
+
+  // [range.lazy.split.outer.inner]
+  // wip
+
   // [range.lazy.split.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}


From libcxx-commits at lists.llvm.org  Wed Jul  8 08:37:07 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 08 Jul 2026 08:37:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] Applied
 [[nodiscard]] to `lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4e6ea3.f732ad81.3a4994.ff16@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/208036

>From 1a1da3d615018ef52c9eefcbb376679f5cdd7505 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 18:00:56 +0200
Subject: [PATCH 01/10] initial: nodiscard implementation to lazy_split_view.h

---
 libcxx/include/__ranges/lazy_split_view.h | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 938dca24cc4fc..01d398a0ac6d8 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -95,14 +95,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
       : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
@@ -111,19 +111,19 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<_View> && forward_range<const _View>
   {
     return __outer_iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
     return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
     if constexpr (forward_range<_View> && forward_range<const _View> && common_range<const _View>) {
       return __outer_iterator<true>{*this, ranges::end(__base_)};
     } else {
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
@@ -342,14 +342,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
-    _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
       return std::move(__i_.__current());
     }
 
-    _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
       __incremented_ = true;
@@ -385,7 +385,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       return __x.__is_done();
     }
 
-    _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
     iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(__i.__outer_current()))) {
       return ranges::iter_move(__i.__outer_current());
     }

>From 845a2090e07f251f9a46f849f149d5662ecdbc45 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 22:49:19 +0200
Subject: [PATCH 02/10] add: initial tests and clang-format fix

---
 libcxx/include/__ranges/lazy_split_view.h     | 12 +++++----
 .../range.lazy.split/nodiscard.verify.cpp     | 27 +++++++++++++++++++
 2 files changed, 34 insertions(+), 5 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 01d398a0ac6d8..5e336cbfcac49 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
+      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
+    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -342,7 +342,9 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
+      return __i_.__current();
+    }
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
@@ -404,8 +406,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&,
-                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, range_value_t<_Range>)
+    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cd15b33b66d59
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Check that functions are marked [[nodiscard]]
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  // [range.lazy.split.overview]
+
+  std::string str = "the quick brown fox";
+  char pattern    = ' ';
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::split(str, pattern);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::split(pattern);
+}

>From 054f55611b632030795527d777509c70609e5df0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 22:57:55 +0200
Subject: [PATCH 03/10] fix: minor fixes for header include and lazy_split

---
 .../range.adaptors/range.lazy.split/nodiscard.verify.cpp  | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index cd15b33b66d59..c6420b0adf5d5 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -11,17 +11,19 @@
 // Check that functions are marked [[nodiscard]]
 
 #include <ranges>
+#include <string>
 #include <utility>
 
 void test() {
   // [range.lazy.split.overview]
 
-  std::string str = "the quick brown fox";
+  std::string str { "the quick brown fox" };
   char pattern    = ' ';
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::split(str, pattern);
+  std::views::lazy_split(str, pattern);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::split(pattern);
+  std::views::lazy_split(pattern);
+
 }

>From fbf22aa66f5d3205c82bbba057664780a8fbe057 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 7 Jul 2026 23:32:22 +0200
Subject: [PATCH 04/10] add: test cases and remove nodiscard in implementation
 detail

---
 libcxx/include/__ranges/lazy_split_view.h     |  2 +-
 .../range.lazy.split/nodiscard.verify.cpp     | 41 ++++++++++++++++---
 2 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 5e336cbfcac49..66f18d5edad76 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index c6420b0adf5d5..b80f6f00836b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -8,22 +8,53 @@
 
 // REQUIRES: std-at-least-c++20
 
-// Check that functions are marked [[nodiscard]]
+// Test the libc++ extension that std::ranges::lazy_split_view and std::views::lazy_split are marked as [[nodiscard]].
 
 #include <ranges>
 #include <string>
 #include <utility>
 
 void test() {
-  // [range.lazy.split.overview]
-
-  std::string str { "the quick brown fox" };
+  std::string str = "the quick brown fox";
   char pattern    = ' ';
 
+  auto v = std::views::lazy_split(str, pattern);
+
+  // [range.lazy.split.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.lazy.split.iterator]
+
+  auto c_it = std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it.base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *c_it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(c_it);
+
+  // [range.lazy.split.overview]
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::lazy_split(str, pattern);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::lazy_split(pattern);
-
 }

>From 997525b35d5e7a64e9db03541cfbc7acf934f1da Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 12:01:40 +0200
Subject: [PATCH 05/10] revert: accidental format changes

---
 libcxx/include/__ranges/lazy_split_view.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 66f18d5edad76..c9a941abe7ff9 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
+      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
+    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -406,8 +406,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&, range_value_t<_Range>)
-    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, 
+                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {

>From 598aacef1fcf0d98cce6e0fd4b1c7ef3ff8e64db Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 12:02:50 +0200
Subject: [PATCH 06/10] revert: accidental format changes

---
 libcxx/include/__ranges/lazy_split_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index c9a941abe7ff9..fc41f5c49523b 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::begin(__base_)};
+      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >>{*this, ranges::end(__base_)};
+    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -406,7 +406,7 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&, 
+lazy_split_view(_Range&&,
                 range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {

>From f81b6437ddbec10579a87c58dca1286b63f10763 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 16:32:21 +0200
Subject: [PATCH 07/10] fix: __outer_iterator to __inner_iterator nodiscard
 testing

---
 libcxx/include/__ranges/lazy_split_view.h              |  2 +-
 .../range.lazy.split/nodiscard.verify.cpp              | 10 +++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index fc41f5c49523b..c05c932d09703 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -206,7 +206,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index b80f6f00836b0..ab865c7c94b85 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -37,18 +37,18 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).end();
 
-  // [range.lazy.split.iterator]
+  // [range.lazy.split.outer-iterator]
 
-  auto c_it = std::as_const(v).begin();
+  auto c_it = as_const(v).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  c_it.base();
+  (*c_it).begin().base();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  *c_it;
+  (*c_it);
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  iter_move(c_it);
+  iter_move((*c_it).begin());
 
   // [range.lazy.split.overview]
 

>From f8a987f6a23e981178760868a13059d73a2e52da Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 17:17:36 +0200
Subject: [PATCH 08/10] Apply suggestion from @Zingam

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 libcxx/include/__ranges/lazy_split_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index c05c932d09703..b628df397739b 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -189,7 +189,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(__outer_iterator __i) : __i_(std::move(__i)) {}
 
       _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; }
-      _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; }
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
     };
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;

>From 724b89b4fcb7a020f4942177ace2379f8bd191b1 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 17:35:40 +0200
Subject: [PATCH 09/10] fix: add outer, value and inner sections

---
 .../range.adaptors/range.lazy.split/nodiscard.verify.cpp  | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
index ab865c7c94b85..bcc7273343d54 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -37,7 +37,7 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).end();
 
-  // [range.lazy.split.outer-iterator]
+  // [range.lazy.split.outer]
 
   auto c_it = as_const(v).begin();
 
@@ -50,6 +50,12 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   iter_move((*c_it).begin());
 
+  // [range.lazy.split.outer.value]
+  // wip
+
+  // [range.lazy.split.outer.inner]
+  // wip
+
   // [range.lazy.split.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}

>From 3012ee290b45075cab4c99c9e3ff9691ce987cbf Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 8 Jul 2026 17:36:50 +0200
Subject: [PATCH 10/10] fix: formatting

---
 libcxx/include/__ranges/lazy_split_view.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index b628df397739b..4b529c5b1ad8d 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -189,8 +189,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(__outer_iterator __i) : __i_(std::move(__i)) {}
 
       _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; }
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; }
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+      [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; }
+      [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
     };
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;


From libcxx-commits at lists.llvm.org  Wed Jul  8 08:45:13 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 08:45:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a4e7089.13be6df8.145c.c5e3@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From d93e557c304cea49a5cf654b3bf1710f5cee6855 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/11] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..5e1a7afea06a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From f17e51a372a7a1372fa63a572871939eec329145 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/11] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From ac972d414dd906c0e73a3e8b70697abfc537dafa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/11] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e1a7afea06a2..4477c821235de 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..b0f40e5a261ed 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -896,6 +896,7 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 33732fee8003a503f62524e1208aef02094b0bc6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/11] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From dc33657dd85cca1c1511d5cd7605bbcca05b35f8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/11] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From f133b64bf0da078d6ef4659603f8f92f12e7ec8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/11] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From e943f4968a86c2c73214cdb22ccdf77b122428e5 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/11] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 6070b97b3d62691fabebe6dbd7b79a3fd5a58c4d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/11] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 56cbfdaa5c0ff..8ccbcf9d8d227 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -160,7 +160,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -178,7 +178,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -193,7 +193,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b20b5e25fc9cd..04e12afd21689 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From d85c6a4fd888729fc38b7b3bae192263875cc4ed Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/11] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 4477c821235de..0e214d48c5672 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 8ccbcf9d8d227..04bdd2a6b343f 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -28,29 +28,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 04e12afd21689..3776873d8936d 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b0f40e5a261ed..a15ec1342a56d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -885,6 +885,7 @@ module std [system] {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -896,7 +897,6 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From af8e3868ec116fc226f4d5b1c3402d99dade4505 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/11] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 25dccbcfcce342be3de56230774f7790d0c2f5c2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/11] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 3776873d8936d..fbe90c05ccaeb 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Wed Jul  8 08:48:26 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:48:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4e714a.faf16b37.2f0e1d.9d19@mx.google.com>


================
@@ -529,6 +530,34 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
----------------
ldionne wrote:

I would suggest calling this `__out` or `__dest` or `__result`. Non-blocking, just a preference.

https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:48:26 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:48:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4e714a.2020f6c2.243e78.38ec@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:48:26 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:48:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4e714a.07d7ca98.9dee3.4b8c@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM with nitpicks, thanks!

https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:48:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:48:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4e714b.491d90f5.2facd.7c88@mx.google.com>


================
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
----------------
ldionne wrote:

This could live in `libcxx/test/support/runway_sample.h`. And then we'd want to add a comment explaining what this function does and what its purpose is.

https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:48:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:48:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4e714b.f8b79514.1d52d0.4cb6@mx.google.com>


================
@@ -529,6 +530,34 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _BinaryOperation&& __op) const noexcept {
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    if (__first1 == __last1)
+      return __first2; // edge case: empty input range, just return the output iterator
+    *__first2 = *__first1;
+    ++__first2;
+    _ForwardIterator1 __first_left = std::next(__first1);
+    if (__first_left == __last1)
+      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+    // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
+    return _TransformBinary()(
+        __policy,
+        std::move(__first_left),
+        std::move(__last1),
+        std::move(__first1),
+        std::move(__first2),
----------------
ldionne wrote:

```suggestion
        std::move(__first2),
        std::move(__last1),
        std::move(__first1),
        std::move(__result),
```

https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:48:28 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:48:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4e714c.abb10101.3d0bf8.4098@mx.google.com>


================
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
----------------
ldionne wrote:

Let's add a short comment explaining what `X` and `Y` are (and their relationship), and why they are useful for testing `adjacent_difference`. Granted, the existing `adjacent_difference` tests probably didn't document that, but we've raised the bar for our tests since the early days of libc++ :-).

https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:48:28 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:48:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4e714c.492e23e4.157d47.936f@mx.google.com>


================
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
----------------
ldionne wrote:

```suggestion
TEST_CONSTEXPR_CXX14 void runway_sample(size_t size, Callable callable) {
```

https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:48:28 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:48:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4e714c.f1c1923d.991d1.0332@mx.google.com>


================
@@ -529,6 +530,34 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _BinaryOperation&& __op) const noexcept {
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    if (__first1 == __last1)
+      return __first2; // edge case: empty input range, just return the output iterator
+    *__first2 = *__first1;
+    ++__first2;
+    _ForwardIterator1 __first_left = std::next(__first1);
----------------
ldionne wrote:

I would suggest using `__first2` here (that name will be free if you rename the output iterator).

https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:48:28 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:48:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a4e714c.7270d2fd.2d4879.65b1@mx.google.com>


================
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
----------------
ldionne wrote:

I think that processing the prefix first, then the "pi increments", and then the suffix, would make this easier to understand while also ensuring that we don't call `callable(i)` with duplicate `i`s.

https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Wed Jul  8 08:50:34 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 08:50:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::reverse_copy (PR #208085)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208085@github.com>
Message-ID: <6a4e71ca.07d7ca98.9dee3.8010@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/208085

>From d0733a0157e03c566fc6dedce3a340b3a74422ca Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 7 Jul 2026 20:15:09 +0100
Subject: [PATCH 1/2] Added a default implementation of parallel
 std::reverse_copy based on std::copy + std::reverse_iterator

---
 libcxx/include/__algorithm/pstl.h             |  17 +++
 libcxx/include/__pstl/backend_fwd.h           |   7 ++
 libcxx/include/__pstl/backends/default.h      |  17 +++
 .../pstl.iterator-requirements.verify.cpp     |  14 ++-
 .../algorithms/pstl.nodiscard.verify.cpp      |   2 +
 .../alg.reverse/pstl.reverse_copy.pass.cpp    | 116 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |   7 ++
 7 files changed, 178 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 10625ea3f8e3d..55654637df57f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -347,6 +347,23 @@ generate_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n, _Ge
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__n), std::move(__gen));
 }
 
+template <class _ExecutionPolicy,
+          class _BidirectionalIterator,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator reverse_copy(
+    _ExecutionPolicy&& __policy,
+    _BidirectionalIterator __first,
+    _BidirectionalIterator __last,
+    _ForwardIterator __result) {
+  _LIBCPP_REQUIRE_CPP17_BIDIRECTIONAL_ITERATOR(_BidirectionalIterator, "reverse_copy requires a BidirectionalIterator");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reverse_copy requires a ForwardIterator");
+  using _Implementation = __pstl::__dispatch<__pstl::__reverse_copy, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__result));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator,
           class _Predicate,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a52e6db954d0c..6b72350d43a10 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -159,6 +159,13 @@ struct __generate_n;
 // optional<__empty>
 // operator()(_Policy&&, _ForwardIterator __first, _Size __n, _Generator __gen) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __reverse_copy;
+// template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
+// optional<_ForwardIterator>
+// operator()(_Policy&&, _BidirectionalIterator __first, _BidirectionalIterator __last,
+//                       _ForwardIterator __result) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __merge;
 // template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Comp>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index be90715af13b5..6cbd3bcd0af64 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -21,6 +21,7 @@
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
+#include <__iterator/reverse_iterator.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
 #include <__utility/empty.h>
@@ -529,6 +530,22 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __reverse_copy<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+  operator()(_Policy&& __policy,
+             _BidirectionalIterator __first,
+             _BidirectionalIterator __last,
+             _ForwardIterator __result) const noexcept {
+    using _Copy = __dispatch<__copy, __current_configuration, _ExecutionPolicy>;
+    return _Copy()(__policy,
+                   std::reverse_iterator<_BidirectionalIterator>(std::move(__last)),
+                   std::reverse_iterator<_BidirectionalIterator>(std::move(__first)),
+                   std::move(__result));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e5bd7e764c59b..4aa435245e863 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -31,12 +31,16 @@
 
 #include "test_iterators.h"
 
-using non_forward_iterator = cpp17_input_iterator<int*>;
+using non_forward_iterator       = cpp17_input_iterator<int*>;
+using non_bidirectional_iterator = forward_iterator<int*>;
 struct non_output_iterator : forward_iterator<int*> {
   constexpr int const& operator*() const; // prevent it from being an output iterator
 };
 
-void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execution::sequenced_policy pol) {
+void f(non_forward_iterator non_fwd,
+       non_output_iterator non_output,
+       non_bidirectional_iterator non_bidir,
+       std::execution::sequenced_policy pol) {
   auto pred     = [](auto&&...) -> bool { return true; };
   auto func     = [](auto&&...) -> int { return 1; };
   int* it       = nullptr;
@@ -99,6 +103,12 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::generate_n(pol, non_fwd, n, func);     // expected-error@*:* {{static assertion failed: generate_n}}
   }
 
+  {
+    (void)std::reverse_copy(
+        pol, non_bidir, non_bidir, it);            // expected-error@*:* {{static assertion failed: reverse_copy}}
+    (void)std::reverse_copy(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: reverse_copy}}
+  }
+
   {
     (void)std::is_partitioned(
         pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: is_partitioned}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index b6554cef6f32a..38e85fb3b6b83 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -52,4 +52,6 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
new file mode 100644
index 0000000000000..b805fab2f1fb0
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class BidirectionalIterator,
+//           class ForwardIterator>
+//   ForwardIterator reverse_copy(ExecutionPolicy&& exec,
+//                                BidirectionalIterator first,
+//                                BidirectionalIterator last,
+//                                ForwardIterator result);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(reverse_copy);
+
+static_assert(sfinae_test_reverse_copy<int, int*, int*, int*>);
+static_assert(!sfinae_test_reverse_copy<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[]              = {0};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      assert(ib[0] == -1);
+      r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(ia[0] == 0);
+    }
+    {
+      int ia[]              = {0, 1};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 1);
+      assert(ib[1] == 0);
+    }
+    {
+      int ia[]              = {0, 1, 2};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 2);
+      assert(ib[1] == 1);
+      assert(ib[2] == 0);
+    }
+    {
+      int ia[]              = {0, 1, 2, 3};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 3);
+      assert(ib[1] == 2);
+      assert(ib[2] == 1);
+      assert(ib[3] == 0);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia) + 1, [&](size_t i) {
+        Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j) {
+          assert(ib[j] == static_cast<int>(i - j));
+        }
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::bidirectional_iterator_list<int*>, types::bidirectional_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+}
\ No newline at end of file
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a3db159289393..546862169813e 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -195,6 +195,13 @@ int main(int, char**) {
         assert_non_throwing([=, &policy] { (void)std::generate_n(policy, std::move(first1), n, gen); });
       }
 
+      {
+        // reverse_copy(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::reverse_copy(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+      }
+
       {
         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
 

>From 6632a8261585b97670183b719c2fbab3330f839e Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 8 Jul 2026 15:45:42 +0100
Subject: [PATCH 2/2] Added a missing line in the top comments

---
 libcxx/include/__pstl/backends/default.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 6cbd3bcd0af64..49ab69b497ddd 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -93,6 +93,7 @@ namespace __pstl {
 // - move
 // - copy
 // - copy_n
+// - reverse_copy
 // - rotate_copy
 //
 


From libcxx-commits at lists.llvm.org  Wed Jul  8 09:07:21 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 09:07:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a4e75b9.30759430.24d50c.fdd7@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From d93e557c304cea49a5cf654b3bf1710f5cee6855 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/12] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..5e1a7afea06a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From f17e51a372a7a1372fa63a572871939eec329145 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/12] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From ac972d414dd906c0e73a3e8b70697abfc537dafa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/12] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e1a7afea06a2..4477c821235de 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..b0f40e5a261ed 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -896,6 +896,7 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 33732fee8003a503f62524e1208aef02094b0bc6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/12] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From dc33657dd85cca1c1511d5cd7605bbcca05b35f8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/12] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From f133b64bf0da078d6ef4659603f8f92f12e7ec8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/12] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From e943f4968a86c2c73214cdb22ccdf77b122428e5 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/12] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 6070b97b3d62691fabebe6dbd7b79a3fd5a58c4d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/12] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 56cbfdaa5c0ff..8ccbcf9d8d227 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -160,7 +160,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -178,7 +178,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -193,7 +193,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b20b5e25fc9cd..04e12afd21689 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From d85c6a4fd888729fc38b7b3bae192263875cc4ed Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/12] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 4477c821235de..0e214d48c5672 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 8ccbcf9d8d227..04bdd2a6b343f 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -28,29 +28,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 04e12afd21689..3776873d8936d 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b0f40e5a261ed..a15ec1342a56d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -885,6 +885,7 @@ module std [system] {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -896,7 +897,6 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From af8e3868ec116fc226f4d5b1c3402d99dade4505 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/12] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 25dccbcfcce342be3de56230774f7790d0c2f5c2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/12] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 3776873d8936d..fbe90c05ccaeb 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From cf4a2421de4a7c9d7fd6729329022602cfa07ff7 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/12] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak


From libcxx-commits at lists.llvm.org  Wed Jul  8 09:08:24 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 09:08:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a4e75f8.bf38ae28.39239a.c5c9@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From d93e557c304cea49a5cf654b3bf1710f5cee6855 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/13] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..5e1a7afea06a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From f17e51a372a7a1372fa63a572871939eec329145 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/13] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From ac972d414dd906c0e73a3e8b70697abfc537dafa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/13] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e1a7afea06a2..4477c821235de 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..b0f40e5a261ed 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -896,6 +896,7 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 33732fee8003a503f62524e1208aef02094b0bc6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/13] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From dc33657dd85cca1c1511d5cd7605bbcca05b35f8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/13] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From f133b64bf0da078d6ef4659603f8f92f12e7ec8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/13] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From e943f4968a86c2c73214cdb22ccdf77b122428e5 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/13] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 6070b97b3d62691fabebe6dbd7b79a3fd5a58c4d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/13] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 56cbfdaa5c0ff..8ccbcf9d8d227 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -160,7 +160,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -178,7 +178,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -193,7 +193,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b20b5e25fc9cd..04e12afd21689 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From d85c6a4fd888729fc38b7b3bae192263875cc4ed Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/13] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 4477c821235de..0e214d48c5672 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 8ccbcf9d8d227..04bdd2a6b343f 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -28,29 +28,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 04e12afd21689..3776873d8936d 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b0f40e5a261ed..a15ec1342a56d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -885,6 +885,7 @@ module std [system] {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -896,7 +897,6 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From af8e3868ec116fc226f4d5b1c3402d99dade4505 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/13] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 25dccbcfcce342be3de56230774f7790d0c2f5c2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/13] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 3776873d8936d..fbe90c05ccaeb 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From cf4a2421de4a7c9d7fd6729329022602cfa07ff7 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/13] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From 8c1bda248bc075b4614c605761b4ef34519b10e8 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/13] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;


From libcxx-commits at lists.llvm.org  Wed Jul  8 09:10:25 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 09:10:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a4e7671.e55dc341.1f19bd.fa32@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From d93e557c304cea49a5cf654b3bf1710f5cee6855 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/14] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..5e1a7afea06a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From f17e51a372a7a1372fa63a572871939eec329145 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/14] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From ac972d414dd906c0e73a3e8b70697abfc537dafa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/14] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e1a7afea06a2..4477c821235de 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..b0f40e5a261ed 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -896,6 +896,7 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 33732fee8003a503f62524e1208aef02094b0bc6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/14] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From dc33657dd85cca1c1511d5cd7605bbcca05b35f8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/14] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From f133b64bf0da078d6ef4659603f8f92f12e7ec8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/14] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From e943f4968a86c2c73214cdb22ccdf77b122428e5 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/14] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 6070b97b3d62691fabebe6dbd7b79a3fd5a58c4d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/14] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 56cbfdaa5c0ff..8ccbcf9d8d227 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -160,7 +160,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -178,7 +178,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -193,7 +193,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b20b5e25fc9cd..04e12afd21689 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From d85c6a4fd888729fc38b7b3bae192263875cc4ed Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/14] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 4477c821235de..0e214d48c5672 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 8ccbcf9d8d227..04bdd2a6b343f 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -28,29 +28,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 04e12afd21689..3776873d8936d 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b0f40e5a261ed..a15ec1342a56d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -885,6 +885,7 @@ module std [system] {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -896,7 +897,6 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From af8e3868ec116fc226f4d5b1c3402d99dade4505 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/14] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 25dccbcfcce342be3de56230774f7790d0c2f5c2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/14] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 3776873d8936d..fbe90c05ccaeb 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From cf4a2421de4a7c9d7fd6729329022602cfa07ff7 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/14] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From 8c1bda248bc075b4614c605761b4ef34519b10e8 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/14] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From 430ca438d1f650e1b9ce026b5ef4408801e42945 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/14] 14

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <cstring>
+#include <type_traits>
 
 struct WithTailPadding {
   int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;


From libcxx-commits at lists.llvm.org  Wed Jul  8 09:15:08 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 09:15:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::reverse_copy (PR #208085)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208085@github.com>
Message-ID: <6a4e778c.70920dee.290dca.7b98@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/208085

From libcxx-commits at lists.llvm.org  Wed Jul  8 09:15:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 09:15:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::reverse_copy (PR #208085)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208085@github.com>
Message-ID: <6a4e778d.6c8668f2.e4f82.dbfa@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM w/ nitpick newline!

https://github.com/llvm/llvm-project/pull/208085

From libcxx-commits at lists.llvm.org  Wed Jul  8 09:15:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 09:15:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::reverse_copy (PR #208085)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208085@github.com>
Message-ID: <6a4e778d.e9d484b9.15e549.5685@mx.google.com>


================
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class BidirectionalIterator,
+//           class ForwardIterator>
+//   ForwardIterator reverse_copy(ExecutionPolicy&& exec,
+//                                BidirectionalIterator first,
+//                                BidirectionalIterator last,
+//                                ForwardIterator result);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(reverse_copy);
+
+static_assert(sfinae_test_reverse_copy<int, int*, int*, int*>);
+static_assert(!sfinae_test_reverse_copy<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[]              = {0};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      assert(ib[0] == -1);
+      r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(ia[0] == 0);
+    }
+    {
+      int ia[]              = {0, 1};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 1);
+      assert(ib[1] == 0);
+    }
+    {
+      int ia[]              = {0, 1, 2};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 2);
+      assert(ib[1] == 1);
+      assert(ib[2] == 0);
+    }
+    {
+      int ia[]              = {0, 1, 2, 3};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 3);
+      assert(ib[1] == 2);
+      assert(ib[2] == 1);
+      assert(ib[3] == 0);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia) + 1, [&](size_t i) {
+        Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j) {
+          assert(ib[j] == static_cast<int>(i - j));
+        }
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::bidirectional_iterator_list<int*>, types::bidirectional_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+}
----------------
ldionne wrote:

Nit: missing newline

https://github.com/llvm/llvm-project/pull/208085

From libcxx-commits at lists.llvm.org  Wed Jul  8 09:52:48 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 09:52:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Speed up compilation of common uses of
	std::visit() (PR #164196)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/164196@github.com>
Message-ID: <6a4e8060.8e75e0cb.5e108.4fd5@mx.google.com>


https://github.com/higher-performance updated https://github.com/llvm/llvm-project/pull/164196

>From fead503f03ca88c7c73fd23503f04997c6c8d702 Mon Sep 17 00:00:00 2001
From: higher-performance <higher.performance.github at gmail.com>
Date: Fri, 28 Nov 2025 10:48:16 -0500
Subject: [PATCH] Speed up compilation of std::visit() by hard-coding the most
 common cases

---
 libcxx/include/variant | 95 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 88 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/variant b/libcxx/include/variant
index cf3bd82ee7c1f..466e07c7db210 100644
--- a/libcxx/include/variant
+++ b/libcxx/include/variant
@@ -1332,6 +1332,10 @@ private:
   friend struct __variant_detail::__visitation::__variant;
 };
 
+template <class... _Types>
+variant<_Types...> __upcast_to_variant(const volatile variant<_Types...>*);
+void __upcast_to_variant(...);
+
 template <size_t _Ip, class... _Types>
 _LIBCPP_HIDE_FROM_ABI constexpr bool __holds_alternative(const variant<_Types...>& __v) noexcept {
   return __v.index() == _Ip;
@@ -1589,23 +1593,100 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __throw_if_valueless(_Vs&&... __vs) {
   }
 }
 
+#    define _LIBCPP_VARIANT_DISPATCH_COUNT 11 // Speed up compilation for the common cases
+
 template < class _Visitor, class... _Vs, typename>
 _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
-  using __variant_detail::__visitation::__variant;
-  std::__throw_if_valueless(std::forward<_Vs>(__vs)...);
-  // NOLINTNEXTLINE(bugprone-use-after-move) __throw_if_valueless doesn't actually forward the variants
-  return __variant::__visit_value(std::forward<_Visitor>(__visitor), std::forward<_Vs>(__vs)...);
+  using __variant_type = decltype(std::__upcast_to_variant(std::declval<remove_reference_t<_Vs>*>()...));
+  constexpr size_t __variant_size =
+      conditional_t<is_void_v<__variant_type>,
+                    std::integral_constant<size_t, variant_npos>,
+                    variant_size<__variant_type>>::value;
+  if constexpr (__variant_size <= _LIBCPP_VARIANT_DISPATCH_COUNT) {
+    using __variant_detail::__access::__variant;
+#    define _LIBCPP_VARIANT_DISPATCH_INDEX(_I)                                                                         \
+    case _I:                                                                                                           \
+      if constexpr (_I < __variant_size) {                                                                             \
+        return std::__invoke(                                                                                          \
+            std::forward<_Visitor>(__visitor), __variant::__get_alt<_I>(std::forward<_Vs>(__vs)...).__value);          \
+      }                                                                                                                \
+      [[__fallthrough__]]
+    switch ((..., __vs.__variant_type::index())) {
+      enum : size_t { _I0 = __LINE__ + 1 };           // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      static_assert(__LINE__ - _I0 == _LIBCPP_VARIANT_DISPATCH_COUNT, "index count mismatch");
+    default:
+      std::__throw_bad_variant_access("std::visit: variant is valueless");
+    }
+#    undef _LIBCPP_VARIANT_DISPATCH_INDEX
+  } else {
+    using __variant_detail::__visitation::__variant;
+    std::__throw_if_valueless(std::forward<_Vs>(__vs)...);
+    // NOLINTNEXTLINE(bugprone-use-after-move) __throw_if_valueless doesn't actually forward the variants
+    return __variant::__visit_value(std::forward<_Visitor>(__visitor), std::forward<_Vs>(__vs)...);
+  }
 }
 
 #    if _LIBCPP_STD_VER >= 20
 template < class _Rp, class _Visitor, class... _Vs, typename>
 _LIBCPP_HIDE_FROM_ABI constexpr _Rp visit(_Visitor&& __visitor, _Vs&&... __vs) {
-  using __variant_detail::__visitation::__variant;
-  std::__throw_if_valueless(std::forward<_Vs>(__vs)...);
-  return __variant::__visit_value<_Rp>(std::forward<_Visitor>(__visitor), std::forward<_Vs>(__vs)...);
+  using __variant_type = decltype(std::__upcast_to_variant(std::declval<remove_reference_t<_Vs>*>()...));
+  constexpr size_t __variant_size =
+      conditional_t<is_void_v<__variant_type>,
+                    std::integral_constant<size_t, variant_npos>,
+                    variant_size<__variant_type>>::value;
+  if constexpr (__variant_size <= _LIBCPP_VARIANT_DISPATCH_COUNT) {
+    using __variant_detail::__access::__variant;
+#      define _LIBCPP_VARIANT_DISPATCH_INDEX(_I)                                                                       \
+      case _I:                                                                                                         \
+        if constexpr (_I < __variant_size) {                                                                           \
+          if constexpr (std::is_void_v<_Rp>) {                                                                         \
+            return (void)std::__invoke(                                                                                \
+                std::forward<_Visitor>(__visitor), __variant::__get_alt<_I>(std::forward<_Vs>(__vs)...).__value);      \
+          } else {                                                                                                     \
+            return std::__invoke(                                                                                      \
+                std::forward<_Visitor>(__visitor), __variant::__get_alt<_I>(std::forward<_Vs>(__vs)...).__value);      \
+          }                                                                                                            \
+        }                                                                                                              \
+        [[__fallthrough__]]
+    switch ((..., __vs.__variant_type::index())) {
+      enum : size_t { _I0 = __LINE__ + 1 };           // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      _LIBCPP_VARIANT_DISPATCH_INDEX(__LINE__ - _I0); // New line required
+      static_assert(__LINE__ - _I0 == _LIBCPP_VARIANT_DISPATCH_COUNT, "index count mismatch");
+    default:
+      std::__throw_bad_variant_access("std::visit: variant is valueless");
+    }
+#      undef _LIBCPP_VARIANT_DISPATCH_INDEX
+  } else {
+    using __variant_detail::__visitation::__variant;
+    std::__throw_if_valueless(std::forward<_Vs>(__vs)...);
+    return __variant::__visit_value<_Rp>(std::forward<_Visitor>(__visitor), std::forward<_Vs>(__vs)...);
+  }
 }
 #    endif
 
+#    undef _LIBCPP_VARIANT_DISPATCH_COUNT
+
 template <class... _Types>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 auto
 swap(variant<_Types...>& __lhs, variant<_Types...>& __rhs) noexcept(noexcept(__lhs.swap(__rhs)))


From libcxx-commits at lists.llvm.org  Wed Jul  8 09:54:00 2026
From: libcxx-commits at lists.llvm.org (Michael Jones via libcxx-commits)
Date: Wed, 08 Jul 2026 09:54:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][libc] update LLVM-libc test
	status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a4e80a8.30759430.24d50c.2be8@mx.google.com>


https://github.com/michaelrj-google edited https://github.com/llvm/llvm-project/pull/201236

From libcxx-commits at lists.llvm.org  Wed Jul  8 09:54:07 2026
From: libcxx-commits at lists.llvm.org (Michael Jones via libcxx-commits)
Date: Wed, 08 Jul 2026 09:54:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][libc] update LLVM-libc test
	status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a4e80af.4a342cbc.ca844.a97b@mx.google.com>


https://github.com/michaelrj-google updated https://github.com/llvm/llvm-project/pull/201236

>From 5b2606356aa735a951b0bfc98416089f7e4fee88 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 2 Jun 2026 23:27:03 +0000
Subject: [PATCH 1/7] [libcxx][libc] update LLVM-libc test status

This update removes some unnecessary carveouts, marks some newly
XFAILing tests, and unmarks some now passing tests.

With this change the status of tests is:
Total Discovered Tests: 11402
  Unsupported      : 1821 (15.97%)
  Passed           : 9409 (82.52%)
  Expectedly Failed:  172 (1.51%)

Depends on #201235

Assisted-by: Automated tooling, human reviewed.
---
 libcxx/cmake/caches/Generic-llvm-libc.cmake           | 11 -----------
 libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in      |  2 +-
 ...nothrow_new_not_overridden_fno_exceptions.pass.cpp |  3 ---
 .../support.dynamic/new_dont_return_nullptr.pass.cpp  |  3 ---
 ...R30202_notify_from_pthread_created_thread.pass.cpp |  2 ++
 .../thread.thread.member/native_handle.pass.cpp       |  3 +++
 .../osyncstream/thread/several_threads.pass.cpp       |  3 +++
 .../complex.member.ops/divide_equal_complex.pass.cpp  |  3 ---
 .../complex.ops/complex_divide_complex.pass.cpp       |  3 ---
 .../complex.ops/scalar_divide_complex.pass.cpp        |  3 ---
 .../rand.dist.bern.bin/eval.PR44847.pass.cpp          |  3 +++
 .../rand.dist.bern/rand.dist.bern.bin/io.pass.cpp     |  3 +++
 .../notify_all_at_thread_exit_lwg3343.pass.cpp        |  3 +++
 .../thread.thread.this/sleep_for.signals.pass.cpp     |  3 +++
 .../std/time/time.hash/time.hash_enabled.pass.cpp     |  3 ---
 15 files changed, 21 insertions(+), 30 deletions(-)

diff --git a/libcxx/cmake/caches/Generic-llvm-libc.cmake b/libcxx/cmake/caches/Generic-llvm-libc.cmake
index 8e8a2699858b3..96bbeab040362 100644
--- a/libcxx/cmake/caches/Generic-llvm-libc.cmake
+++ b/libcxx/cmake/caches/Generic-llvm-libc.cmake
@@ -32,22 +32,11 @@ set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
 
 # Not yet fully implemented.
-set(LIBCXX_ENABLE_LOCALIZATION OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF CACHE BOOL "")
 
 # Still missing some components.
 set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
 
-# Still needs validation before turning on.
-set(LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "")
-
-# Still missing some components.
-set(LIBCXX_ENABLE_MONOTONIC_CLOCK OFF CACHE BOOL "")
-
-# requires LIBCXX_ENABLE_MONOTONIC_CLOCK=ON
-set(LIBCXX_ENABLE_THREADS OFF CACHE BOOL "")
-set(LIBCXXABI_ENABLE_THREADS OFF CACHE BOOL "")
-
 set(LIBCXX_ENABLE_UNICODE ON CACHE BOOL "")
 set(LIBCXX_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
 set(LIBCXXABI_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
diff --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index cae8a8007214e..7d878e9888ce0 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include-dir} -I %{target-include-dir} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx-dir}/test/support -idirafter @LIBC_KERNEL_HEADERS@'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a'
+    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--defsym,__dso_handle=0 -latomic -Wl,--end-group'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '
diff --git a/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp b/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
index 39a51352eadd9..fb26bdf49cc3c 100644
--- a/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
+++ b/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
@@ -29,9 +29,6 @@
 //       spuriously fail.
 // REQUIRES: no-exceptions
 
-// No member named 'regex'.
-// XFAIL: LLVM-LIBC-FIXME
-
 #include <cstddef>
 #include <new>
 
diff --git a/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp b/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
index 339e756b86fc8..02b20a8c98b21 100644
--- a/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
+++ b/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
@@ -22,9 +22,6 @@
 // REQUIRES: no-exceptions
 // UNSUPPORTED: c++03, c++11, c++14
 
-// No member named 'regex'.
-// XFAIL: LLVM-LIBC-FIXME
-
 #include <cstddef>
 #include <limits>
 #include <new>
diff --git a/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp b/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
index 66251648bd50c..1df75885e4347 100644
--- a/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
@@ -20,6 +20,8 @@
 // Test that this function works with threads that were not created by
 // std::thread. See https://llvm.org/PR30202
 
+// Segfault
+// XFAIL: LLVM-LIBC-FIXME
 
 #include <condition_variable>
 #include <mutex>
diff --git a/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
index 96ec3332519bc..5c5c0fed58726 100644
--- a/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
@@ -16,6 +16,9 @@
 
 // native_handle_type native_handle();
 
+// pthread_t and int not comparable.
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <thread>
 #include <new>
 #include <cstdlib>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..82d43c1673d2d 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -21,6 +21,9 @@
 // but the words should be written without interleaving. To increment the
 // change of interleaving words are written one character at a time.
 
+// segmentation fault
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <cassert>
 #include <chrono>
 #include <mutex>
diff --git a/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
index b9467a166e8d5..61e0d01f2ae4d 100644
--- a/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing some math functions that compiler-rt buildins need.
-// XFAIL: LLVM-LIBC-FIXME
-
 // <complex>
 
 // complex& operator/=(const complex& rhs); // constexpr in C++20
diff --git a/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
index 4e00e87f18220..d12dfd994b0ae 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing some math functions that compiler-rt buildins need.
-// XFAIL: LLVM-LIBC-FIXME
-
 // <complex>
 
 // template<class T>
diff --git a/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
index 56d9664b49aea..f32b560c1e864 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing some math functions that compiler-rt buildins need.
-// XFAIL: LLVM-LIBC-FIXME
-
 // <complex>
 
 // template<class T>
diff --git a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
index 8f7c8eb7ccc0f..22319e5b31454 100644
--- a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
@@ -21,6 +21,9 @@
 // Very slow when run in qemu.
 // REQUIRES: long_tests
 
+// missing lgamma_r
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <random>
 #include <cassert>
 #include <cmath>
diff --git a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
index 35f579903a460..a53a5db3be4c0 100644
--- a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
@@ -23,6 +23,9 @@
 // operator>>(basic_istream<charT, traits>& is,
 //            binomial_distribution& x);
 
+// missing lgamma_r
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <random>
 #include <sstream>
 #include <cassert>
diff --git a/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp b/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
index 61094eced4cb2..f1b349017b1a5 100644
--- a/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
+++ b/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
@@ -23,6 +23,9 @@
 //
 // void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);
 
+// segmentation fault
+// XFAIL: LLVM-LIBC-FIXME
+
 #include "make_test_thread.h"
 #include "test_macros.h"
 
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
index 1dba5d8a40976..726b5807d55e3 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
@@ -21,6 +21,9 @@
 // This test ensures that we sleep for the right amount of time even when
 // we get interrupted by a signal, as fixed in 58a0a70fb2f1.
 
+// missing ITIMER_REAL
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <thread>
 #include <cassert>
 #include <chrono>
diff --git a/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp b/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
index e3ac7e53251d5..cac7fbda0fbb3 100644
--- a/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
+++ b/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// No member named `leapsecond'.
-// XFAIL: LLVM-LIBC-FIXME
-
 // REQUIRES: std-at-least-c++26
 
 // <chrono>

>From 7895159c9203b0c8f9d3774febe98a4510124366 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 3 Jun 2026 23:59:29 +0000
Subject: [PATCH 2/7] enable wchar, fix tests

---
 libcxx/cmake/caches/Generic-llvm-libc.cmake                    | 3 ++-
 .../extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp  | 3 +++
 .../test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp   | 3 +++
 libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp   | 3 +++
 libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp  | 3 +++
 libcxx/test/std/strings/c.strings/cwchar.pass.cpp              | 3 +++
 libcxx/test/std/strings/c.strings/cwctype.pass.cpp             | 3 +++
 libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp | 3 +++
 8 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/libcxx/cmake/caches/Generic-llvm-libc.cmake b/libcxx/cmake/caches/Generic-llvm-libc.cmake
index 96bbeab040362..2a4df69bec105 100644
--- a/libcxx/cmake/caches/Generic-llvm-libc.cmake
+++ b/libcxx/cmake/caches/Generic-llvm-libc.cmake
@@ -32,7 +32,7 @@ set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
 
 # Not yet fully implemented.
-set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF CACHE BOOL "")
+set(LIBCXX_ENABLE_WIDE_CHARACTERS ON CACHE BOOL "")
 
 # Still missing some components.
 set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
@@ -47,5 +47,6 @@ set(CMAKE_BUILD_TYPE "Release" CACHE STRING "")
 set(LLVM_ENABLE_RUNTIMES "compiler-rt;libc;libunwind;libcxxabi;libcxx" CACHE STRING "")
 set(LLVM_LIBC_FULL_BUILD ON CACHE BOOL "")
 set(LLVM_LIBC_INCLUDE_SCUDO ON CACHE BOOL "")
+set(LLVM_LIBC_ENABLE_EXPERIMENTAL_ENTRYPOINTS ON CACHE BOOL "")
 set(COMPILER_RT_BUILD_GWP_ASAN OFF CACHE BOOL "")
 set(COMPILER_RT_BUILD_SCUDO_STANDALONE_WITH_LLVM_LIBC ON CACHE BOOL "")
diff --git a/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp b/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
index dd9847f63253b..4b8297145e35a 100644
--- a/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
+++ b/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
@@ -9,6 +9,9 @@
 // We're building as C, so this test doesn't work when building with modules.
 // ADDITIONAL_COMPILE_FLAGS: -fno-modules
 
+// Incompatible redeclaration of wcschr and wmemchr builtins
+// XFAIL: LLVM-LIBC-FIXME
+
 // GCC complains about unrecognized arguments because we're compiling the
 // file as C, but we're passing C++ flags on the command-line.
 // UNSUPPORTED: gcc
diff --git a/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
index eaae3aa3417e4..a730debab2e9b 100644
--- a/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
@@ -8,6 +8,9 @@
 
 // test <inttypes.h>
 
+// Missing wcstoimax, wcstoumax
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <inttypes.h>
 
 #include "test_macros.h"
diff --git a/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
index 98d028c5181d5..df7bda3baf8af 100644
--- a/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
@@ -8,6 +8,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing fwprintf, fwscanf, swscanf, vfwprintf, vfwscanf, vswprintf, vswscanf, wcsftime, vwscanf, wscanf
+// XFAIL: LLVM-LIBC-FIXME
+
 // <wchar.h>
 
 #include <wchar.h>
diff --git a/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
index 4e2fb319336f1..221c219918614 100644
--- a/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
@@ -8,6 +8,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing WEOF, wctrans_t
+// XFAIL: LLVM-LIBC-FIXME
+
 // towctrans and wctrans were added in Android API 26.
 // TODO: Switch from UNSUPPORTED to XFAIL once the Android CI Docker sysroot is
 // updated.
diff --git a/libcxx/test/std/strings/c.strings/cwchar.pass.cpp b/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
index 0caf4b9913b50..1758a703b3b62 100644
--- a/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
+++ b/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
@@ -10,6 +10,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing fwprintf, fwscanf, swscanf, vfwprintf, vfwscanf, vswprintf, vswscanf, wcsftime, vwscanf, wscanf
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <cwchar>
 #include <ctime>
 #include <cstdarg>
diff --git a/libcxx/test/std/strings/c.strings/cwctype.pass.cpp b/libcxx/test/std/strings/c.strings/cwctype.pass.cpp
index 0deabf51ed59c..3de582714d01d 100644
--- a/libcxx/test/std/strings/c.strings/cwctype.pass.cpp
+++ b/libcxx/test/std/strings/c.strings/cwctype.pass.cpp
@@ -10,6 +10,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing WEOF, towctrans, wctrans_t
+// XFAIL: LLVM-LIBC-FIXME
+
 // towctrans and wctrans were added in Android API 26.
 // TODO: Switch from UNSUPPORTED to XFAIL once the Android CI Docker sysroot is
 // updated.
diff --git a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
index fff5ede848b57..bd4bd47671a81 100644
--- a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
+++ b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
@@ -20,6 +20,9 @@
 // wstring to_wstring(double val);
 // wstring to_wstring(long double val);
 
+// Missing swprintf (currently a stub that infinite loops)
+// UNSUPPORTED: LLVM-LIBC-FIXME
+
 #include <string>
 #include <cassert>
 #include <limits>

>From b015d1ff6bd9223ba2c3c6628d8afae172937371 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 24 Jun 2026 17:52:24 +0000
Subject: [PATCH 3/7] move where `__dso_handle` is defined

---
 libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index 7d878e9888ce0..ed1a63c027c9e 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include-dir} -I %{target-include-dir} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx-dir}/test/support -idirafter @LIBC_KERNEL_HEADERS@'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--defsym,__dso_handle=0 -latomic -Wl,--end-group'
+    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -latomic -Wl,--end-group -Wl,--defsym,__dso_handle=0'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '

>From fa496efb18b8eb1e884bd366744b71ec1e0c3bfa Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 1 Jul 2026 20:22:46 +0000
Subject: [PATCH 4/7] update XFAILs with new version

---
 .../extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp  | 3 ---
 .../test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp   | 3 ---
 2 files changed, 6 deletions(-)

diff --git a/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp b/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
index 4b8297145e35a..dd9847f63253b 100644
--- a/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
+++ b/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
@@ -9,9 +9,6 @@
 // We're building as C, so this test doesn't work when building with modules.
 // ADDITIONAL_COMPILE_FLAGS: -fno-modules
 
-// Incompatible redeclaration of wcschr and wmemchr builtins
-// XFAIL: LLVM-LIBC-FIXME
-
 // GCC complains about unrecognized arguments because we're compiling the
 // file as C, but we're passing C++ flags on the command-line.
 // UNSUPPORTED: gcc
diff --git a/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
index a730debab2e9b..eaae3aa3417e4 100644
--- a/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
@@ -8,9 +8,6 @@
 
 // test <inttypes.h>
 
-// Missing wcstoimax, wcstoumax
-// XFAIL: LLVM-LIBC-FIXME
-
 #include <inttypes.h>
 
 #include "test_macros.h"

>From 2dff85cf1c78d5016390afcd91360d682d9f6c55 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Mon, 6 Jul 2026 23:52:38 +0000
Subject: [PATCH 5/7] disable threads due to atomic issue

---
 libcxx/cmake/caches/Generic-llvm-libc.cmake      | 7 ++++---
 libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in | 2 +-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/cmake/caches/Generic-llvm-libc.cmake b/libcxx/cmake/caches/Generic-llvm-libc.cmake
index 2a4df69bec105..dce7fb73ff8b3 100644
--- a/libcxx/cmake/caches/Generic-llvm-libc.cmake
+++ b/libcxx/cmake/caches/Generic-llvm-libc.cmake
@@ -31,12 +31,13 @@ set(LIBCXX_ENABLE_STATIC ON CACHE BOOL "")
 set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
 
-# Not yet fully implemented.
-set(LIBCXX_ENABLE_WIDE_CHARACTERS ON CACHE BOOL "")
-
 # Still missing some components.
 set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
 
+# blocked on issues with atomic
+set(LIBCXX_ENABLE_THREADS OFF CACHE BOOL "")
+set(LIBCXXABI_ENABLE_THREADS OFF CACHE BOOL "")
+
 set(LIBCXX_ENABLE_UNICODE ON CACHE BOOL "")
 set(LIBCXX_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
 set(LIBCXXABI_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
diff --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index ed1a63c027c9e..9dbf8a98dd5e4 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include-dir} -I %{target-include-dir} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx-dir}/test/support -idirafter @LIBC_KERNEL_HEADERS@'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -latomic -Wl,--end-group -Wl,--defsym,__dso_handle=0'
+    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--end-group -Wl,--defsym,__dso_handle=0'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '

>From 0e59eb4b901826432c3334e2f5080097d8803ecd Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 7 Jul 2026 17:17:24 +0000
Subject: [PATCH 6/7] address comment

---
 libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
index df7bda3baf8af..7fee899683ee1 100644
--- a/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: no-wide-characters
 
-// Missing fwprintf, fwscanf, swscanf, vfwprintf, vfwscanf, vswprintf, vswscanf, wcsftime, vwscanf, wscanf
+// Missing wchar string formatting functions.
 // XFAIL: LLVM-LIBC-FIXME
 
 // <wchar.h>

>From 2830be93556ef01c259b29854262cf5ea6c06365 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 7 Jul 2026 17:32:22 +0000
Subject: [PATCH 7/7] address other comment

---
 libcxx/test/std/strings/c.strings/cwchar.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/strings/c.strings/cwchar.pass.cpp b/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
index 1758a703b3b62..a9e99914c7b1a 100644
--- a/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
+++ b/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
@@ -10,7 +10,7 @@
 
 // UNSUPPORTED: no-wide-characters
 
-// Missing fwprintf, fwscanf, swscanf, vfwprintf, vfwscanf, vswprintf, vswscanf, wcsftime, vwscanf, wscanf
+// Missing wchar string formatting functions.
 // XFAIL: LLVM-LIBC-FIXME
 
 #include <cwchar>


From libcxx-commits at lists.llvm.org  Wed Jul  8 09:54:37 2026
From: libcxx-commits at lists.llvm.org (Michael Jones via libcxx-commits)
Date: Wed, 08 Jul 2026 09:54:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][libc] update LLVM-libc test
	status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a4e80cd.f1c1923d.991d1.6677@mx.google.com>


michaelrj-google wrote:

rebased to pick up #208089

https://github.com/llvm/llvm-project/pull/201236

From libcxx-commits at lists.llvm.org  Wed Jul  8 10:46:00 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 10:46:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Improve diagnostic for libcxxabi
 missing from the runtimes (PR #208274)
Message-ID: <llvm.org/llvm/llvm-project/pull/208274@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/208274

When libc++ is configured to use libc++abi as an ABI library but libcxxabi is not part of the runtimes, we'd end up with a confusing build error about a missing <cxxabi.h> header. Instead, diagnose at CMake configuration time.

Closes #136480

>From 46f65633ffa15d30079697228e06b4156174faa6 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 8 Jul 2026 13:44:02 -0400
Subject: [PATCH] [libc++] Improve diagnostic for libcxxabi missing from the
 runtimes

When libc++ is configured to use libc++abi as an ABI library but libcxxabi
is not part of the runtimes, we'd end up with a confusing build error about
a missing <cxxabi.h> header. Instead, diagnose at CMake configuration time.

Closes #136480
---
 libcxx/cmake/Modules/HandleLibCXXABI.cmake | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libcxx/cmake/Modules/HandleLibCXXABI.cmake b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
index 52236f473f35d..4b4ff7c7f9d5f 100644
--- a/libcxx/cmake/Modules/HandleLibCXXABI.cmake
+++ b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
@@ -148,6 +148,12 @@ elseif ("${LIBCXX_CXX_ABI}" STREQUAL "libcxxabi")
     add_library(libcxx-abi-static-objects ALIAS cxxabi_static_objects)
   endif()
 
+  if (NOT TARGET cxxabi_static AND NOT TARGET cxxabi_shared
+      AND NOT TARGET cxxabi_static_objects AND NOT TARGET cxxabi_shared_objects)
+    message(FATAL_ERROR "Can't find any libc++abi target but LIBCXX_CXX_ABI is set to libcxxabi, "
+                        "did you forget to include libcxxabi in LLVM_ENABLE_RUNTIMES?")
+  endif()
+
 # Link against a system-provided libc++abi
 elseif ("${LIBCXX_CXX_ABI}" STREQUAL "system-libcxxabi")
   if(NOT LIBCXX_CXX_ABI_INCLUDE_PATHS)


From libcxx-commits at lists.llvm.org  Wed Jul  8 10:46:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 10:46:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Improve diagnostic for libcxxabi
 missing from the runtimes (PR #208274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208274@github.com>
Message-ID: <6a4e8cfd.bf38ae28.39239a.b033@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

When libc++ is configured to use libc++abi as an ABI library but libcxxabi is not part of the runtimes, we'd end up with a confusing build error about a missing &lt;cxxabi.h&gt; header. Instead, diagnose at CMake configuration time.

Closes #<!-- -->136480

---
Full diff: https://github.com/llvm/llvm-project/pull/208274.diff


1 Files Affected:

- (modified) libcxx/cmake/Modules/HandleLibCXXABI.cmake (+6) 


``````````diff
diff --git a/libcxx/cmake/Modules/HandleLibCXXABI.cmake b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
index 52236f473f35d..4b4ff7c7f9d5f 100644
--- a/libcxx/cmake/Modules/HandleLibCXXABI.cmake
+++ b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
@@ -148,6 +148,12 @@ elseif ("${LIBCXX_CXX_ABI}" STREQUAL "libcxxabi")
     add_library(libcxx-abi-static-objects ALIAS cxxabi_static_objects)
   endif()
 
+  if (NOT TARGET cxxabi_static AND NOT TARGET cxxabi_shared
+      AND NOT TARGET cxxabi_static_objects AND NOT TARGET cxxabi_shared_objects)
+    message(FATAL_ERROR "Can't find any libc++abi target but LIBCXX_CXX_ABI is set to libcxxabi, "
+                        "did you forget to include libcxxabi in LLVM_ENABLE_RUNTIMES?")
+  endif()
+
 # Link against a system-provided libc++abi
 elseif ("${LIBCXX_CXX_ABI}" STREQUAL "system-libcxxabi")
   if(NOT LIBCXX_CXX_ABI_INCLUDE_PATHS)

``````````

</details>


https://github.com/llvm/llvm-project/pull/208274

From libcxx-commits at lists.llvm.org  Wed Jul  8 11:53:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 11:53:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix assign_range test for
	forward_list (PR #208283)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208283@github.com>
Message-ID: <6a4e9cc3.13be6df8.145c.d052@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

That test was checking prepend_range instead of assign_range due to a copy-paste error.

Fixes #<!-- -->74536

---
Full diff: https://github.com/llvm/llvm-project/pull/208283.diff


1 Files Affected:

- (modified) libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp (+7-7) 


``````````diff
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp
index 9a3adec1d9756..61500b58757cb 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 // template<container-compatible-range<T> R>
-//   constexpr void prepend_range(R&& rg); // C++23; constexpr since C++26
+//   constexpr void assign_range(R&& rg); // C++23; constexpr since C++26
 
 #include <forward_list>
 
@@ -17,23 +17,23 @@
 #include "test_macros.h"
 
 // Tested cases:
-// - different kinds of insertions (prepending an {empty/one-element/mid-sized/long range} into an
+// - different kinds of assignments (assigning an {empty/one-element/mid-sized/long range} to an
 //   {empty/one-element/full} container);
-// - prepending move-only elements;
+// - assigning move-only elements;
 // - an exception is thrown when copying the elements or when allocating new elements.
 TEST_CONSTEXPR_CXX26 bool test() {
   static_assert(test_constraints_assign_range<std::forward_list, int, double>());
 
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
-    test_sequence_prepend_range<std::forward_list<int, Alloc>, Iter, Sent>([](auto&&) {
+    test_sequence_assign_range<std::forward_list<int, Alloc>, Iter, Sent>([](auto&&) {
       // No additional validation to do.
     });
   });
-  test_sequence_prepend_range_move_only<std::forward_list>();
+  test_sequence_assign_range_move_only<std::forward_list>();
 
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    test_prepend_range_exception_safety_throwing_copy<std::forward_list>();
-    test_prepend_range_exception_safety_throwing_allocator<std::forward_list, int>();
+    test_assign_range_exception_safety_throwing_copy<std::forward_list>();
+    test_assign_range_exception_safety_throwing_allocator<std::forward_list, int>();
   }
 
   return true;

``````````

</details>


https://github.com/llvm/llvm-project/pull/208283

From libcxx-commits at lists.llvm.org  Wed Jul  8 12:54:16 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 08 Jul 2026 12:54:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Compute a confidence interval in
 compare-benchmarks (PR #208090)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208090@github.com>
Message-ID: <6a4eaae8.4a342cbc.ca844.10ff@mx.google.com>


ldionne wrote:

> I would do the inverse and only flag rows that have a high confidence/are statistically significant. I would assume most of the time there will be no significant difference.

Ah, I think we're coming at this from different angles! In my mind, we mostly use this job to measure changes that we know will actually be significant, so I would be tempted to say that your proposed approach would flag most rows (artificial example):

```
Benchmark                         Baseline    Candidate    Difference    % Difference  95% Confidence interval of %diff     High confidence?
------------------------------  ----------  -----------  ------------  --------------  ----------------------------------  -----------------
std::any_of(list<int>)/32            39.92        39.22         -0.70          -1.75%  [-6.5%, +1.6%]                              
std::any_of(list<int>)/32768      54071.34     65395.51      11324.17          20.94%  [-2.8%, +34.5%]                             
std::any_of(list<int>)/50            67.39        69.08          1.69           2.51%  [+0.2%, +4.7%]                              x
std::any_of(list<int>)/8              7.21         6.46         -0.75         -10.40%  [-25.0%, -0.7%]                             x
std::any_of(list<int>)/8192       21745.58     22299.32        553.74           2.55%  [-20.3%, +38.4%]                            
std::any_of(vector<int>)/32          24.21        14.61         -9.60         -39.65%  [-41.2%, -38.4%]                            x
std::any_of(vector<int>)/33         24.21        14.61         -9.60         -39.65%  [-41.2%, -38.4%]                             x
std::any_of(vector<int>)/34          24.21        14.61         -9.60         -39.65%  [-41.2%, -38.4%]                            x
std::any_of(vector<int>)/35          24.21        14.61         -9.60         -39.65%  [-41.2%, -38.4%]                            x
std::any_of(vector<int>)/36          24.21        14.61         -9.60         -39.65%  [-41.2%, -38.4%]                            x
std::any_of(vector<int>)/32768    24365.14     12498.21     -11866.93         -48.70%  [-48.9%, -48.6%]
Geomean                             324.15       247.83        -76.32         -23.54%
```

However, IIUC you're suggesting that the main use case here would be to validate that there's no regression, and in that case most of the rows would end up being neutral (or within the noise). Is that your thinking? I think it also makes sense, and indeed that would make it more useful to flag rows where there is a high-confidence change.

https://github.com/llvm/llvm-project/pull/208090

From libcxx-commits at lists.llvm.org  Wed Jul  8 14:47:54 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Wed, 08 Jul 2026 14:47:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Replace the of use custom
 sections for detecting overriden functions (PR #208330)
Message-ID: <llvm.org/llvm/llvm-project/pull/208330@github.com>


https://github.com/petrhosek created https://github.com/llvm/llvm-project/pull/208330

This is a follow up to #133876 and an alternative to #120805 which doesn't rely on aliases and works across both ELF and Mach-O. This mechanism is preferable in baremetal environments since it doesn't require special handling of the custom sections.

>From 991e3e2411b93d8f6e388bfd2095f06d237d3506 Mon Sep 17 00:00:00 2001
From: Petr Hosek <phosek at google.com>
Date: Tue, 7 Jul 2026 22:09:42 -0700
Subject: [PATCH] [libc++] Replace the of use custom sections for detecting
 overriden functions

This is a follow up to #133876 and an alternative to #120805 which
doesn't rely on aliases and works across both ELF and Mach-O. This
mechanism is preferable in baremetal environments since it doesn't
require special handling of the custom sections.
---
 libcxx/src/include/overridable_function.h | 105 +++++++++-------------
 1 file changed, 41 insertions(+), 64 deletions(-)

diff --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index c8d9f30c74c3e..31bd96c3abd9c 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -11,11 +11,6 @@
 #define _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H
 
 #include <__config>
-#include <cstdint>
-
-#if __has_feature(ptrauth_calls)
-#  include <ptrauth.h>
-#endif
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -42,18 +37,11 @@
 // -------------------
 //
 // Let's say we want to check whether a weak function `f` has been overridden by the user.
-// The general mechanism works by placing `f`'s definition (in the libc++ built library)
-// inside a special section, which we do using the `__section__` attribute via the
-// OVERRIDABLE_FUNCTION macro.
-//
-// Then, when comes the time to check whether the function has been overridden, we take
-// the address of the function and we check whether it falls inside the special function
-// we created. This can be done by finding pointers to the start and the end of the section
-// (which is done differently for ELF and Mach-O), and then checking whether `f` falls
-// within those bounds. If it falls within those bounds, then `f` is still inside the
-// special section and so it is the version we defined in the libc++ built library, i.e.
-// it was not overridden. Otherwise, it was overridden by the user because it falls
-// outside of the section.
+// The general mechanism works by defining a local symbol `__impl_ref<f>::__impl_` with
+// the same address as `f` as a constant expression using direct PC-relative
+// materialization thus pointing at the symbol defined in the same TU. At runtime, it
+// compares the address of `__impl_ref<f>::__impl_` with the address of `f` loaded from
+// GOT: if `f` was overridden by the user in another TU, the addresses will be different.
 //
 // Important note
 // --------------
@@ -63,64 +51,53 @@
 // want to be defining special sections inside user's executables which use our headers.
 //
 
-#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
+#if defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
-
-_LIBCPP_BEGIN_NAMESPACE_STD template <typename T, T* _Func>
-_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-  // Declare two dummy bytes and give them these special `__asm` values. These values are
-  // defined by the linker, which means that referring to `&__lcxx_override_start` will
-  // effectively refer to the address where the section starts (and same for the end).
-  extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
-  extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
-
-  // Now get a uintptr_t out of these locations, and out of the function pointer.
-  uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
-  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
-  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
-
-#  if __has_feature(ptrauth_calls)
-  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
-  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
-  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
-  // stripped the function pointer. See rdar://122927845.
-  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
-#  endif
+#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
 
-  // Finally, the function was overridden if it falls outside of the section's bounds.
-  return __ptr < __start || __ptr > __end;
-}
-_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_BEGIN_NAMESPACE_STD
 
-// The NVPTX linker cannot create '__start/__stop' sections.
-#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)
+namespace {
 
-#  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+template <typename T>
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI T* __libcpp_launder(T* __ptr) noexcept {
+  __asm__ volatile("" : "+r"(__ptr));
+  return __ptr;
+}
 
-// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
-// variables with those names corresponding to the start and the end of the section.
-//
-// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
-extern char __start___lcxx_override;
-extern char __stop___lcxx_override;
+}
 
-_LIBCPP_BEGIN_NAMESPACE_STD
+template <auto* _Func>
+struct __impl_ref;
+
+// __impl_ref<...>::__impl_ is expected to be defined elsewhere, so the compiler emits
+// assembly references to the mangled symbol with no definition. This template saves us
+// the trouble of providing manual declarations for overloads with some other local name
+// for each function name being overloaded (operator new, operator new[], etc.).
+template <typename _Ret, typename... _Args, _Ret (*_Func)(_Args...)>
+struct __impl_ref<_Func> {
+  [[gnu::visibility("hidden")]] static _Ret __impl_(_Args...);
+};
+
+// This takes a function type template argument first so that the second non-type template
+// argument (pointer to the public function) gets the benefit of type-aware overload
+// resolution, rather than having to use a static_cast.
 template <typename T, T* _Func>
 _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-  uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
-  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
-  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
-
-#  if __has_feature(ptrauth_calls)
-  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. See full explanation above.
-  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
+#  if !defined(_LIBCPP_CLANG_VER) || _LIBCPP_CLANG_VER >= 2101
+  __asm__("%cc0 = %cc1" : : "X"(__impl_ref<_Func>::__impl_), "X"(_Func));
+#  else
+  __asm__("%c0 = %c1" : : "X"(__impl_ref<_Func>::__impl_), "X"(_Func));
 #  endif
-
-  return __ptr < __start || __ptr > __end;
+  // This just has the compiler compare the two symbols. For PIC mode, this will do a
+  // direct PC-relative materialization for __impl_ref<...>::__impl_ and a GOT load for
+  // the _Func symbol. The compiler thinks __impl_ref<...>::__impl_ is defined elsewhere
+  // at link time and will be an undefined symbol. It doesn't know that the __asm__ tells
+  // the assembler to define it as a local symbol.
+  return __libcpp_launder(_Func) != __impl_ref<_Func>::__impl_;
 }
+
 _LIBCPP_END_NAMESPACE_STD
 
 #else


From libcxx-commits at lists.llvm.org  Wed Jul  8 14:48:22 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 14:48:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Replace the of use custom
 sections for detecting overriden functions (PR #208330)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208330@github.com>
Message-ID: <6a4ec5a6.13be6df8.145c.d281@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Petr Hosek (petrhosek)

<details>
<summary>Changes</summary>

This is a follow up to #<!-- -->133876 and an alternative to #<!-- -->120805 which doesn't rely on aliases and works across both ELF and Mach-O. This mechanism is preferable in baremetal environments since it doesn't require special handling of the custom sections.

---
Full diff: https://github.com/llvm/llvm-project/pull/208330.diff


1 Files Affected:

- (modified) libcxx/src/include/overridable_function.h (+41-64) 


``````````diff
diff --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index c8d9f30c74c3e..31bd96c3abd9c 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -11,11 +11,6 @@
 #define _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H
 
 #include <__config>
-#include <cstdint>
-
-#if __has_feature(ptrauth_calls)
-#  include <ptrauth.h>
-#endif
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -42,18 +37,11 @@
 // -------------------
 //
 // Let's say we want to check whether a weak function `f` has been overridden by the user.
-// The general mechanism works by placing `f`'s definition (in the libc++ built library)
-// inside a special section, which we do using the `__section__` attribute via the
-// OVERRIDABLE_FUNCTION macro.
-//
-// Then, when comes the time to check whether the function has been overridden, we take
-// the address of the function and we check whether it falls inside the special function
-// we created. This can be done by finding pointers to the start and the end of the section
-// (which is done differently for ELF and Mach-O), and then checking whether `f` falls
-// within those bounds. If it falls within those bounds, then `f` is still inside the
-// special section and so it is the version we defined in the libc++ built library, i.e.
-// it was not overridden. Otherwise, it was overridden by the user because it falls
-// outside of the section.
+// The general mechanism works by defining a local symbol `__impl_ref<f>::__impl_` with
+// the same address as `f` as a constant expression using direct PC-relative
+// materialization thus pointing at the symbol defined in the same TU. At runtime, it
+// compares the address of `__impl_ref<f>::__impl_` with the address of `f` loaded from
+// GOT: if `f` was overridden by the user in another TU, the addresses will be different.
 //
 // Important note
 // --------------
@@ -63,64 +51,53 @@
 // want to be defining special sections inside user's executables which use our headers.
 //
 
-#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
+#if defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
-
-_LIBCPP_BEGIN_NAMESPACE_STD template <typename T, T* _Func>
-_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-  // Declare two dummy bytes and give them these special `__asm` values. These values are
-  // defined by the linker, which means that referring to `&__lcxx_override_start` will
-  // effectively refer to the address where the section starts (and same for the end).
-  extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
-  extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
-
-  // Now get a uintptr_t out of these locations, and out of the function pointer.
-  uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
-  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
-  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
-
-#  if __has_feature(ptrauth_calls)
-  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
-  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
-  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
-  // stripped the function pointer. See rdar://122927845.
-  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
-#  endif
+#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
 
-  // Finally, the function was overridden if it falls outside of the section's bounds.
-  return __ptr < __start || __ptr > __end;
-}
-_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_BEGIN_NAMESPACE_STD
 
-// The NVPTX linker cannot create '__start/__stop' sections.
-#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)
+namespace {
 
-#  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+template <typename T>
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI T* __libcpp_launder(T* __ptr) noexcept {
+  __asm__ volatile("" : "+r"(__ptr));
+  return __ptr;
+}
 
-// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
-// variables with those names corresponding to the start and the end of the section.
-//
-// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
-extern char __start___lcxx_override;
-extern char __stop___lcxx_override;
+}
 
-_LIBCPP_BEGIN_NAMESPACE_STD
+template <auto* _Func>
+struct __impl_ref;
+
+// __impl_ref<...>::__impl_ is expected to be defined elsewhere, so the compiler emits
+// assembly references to the mangled symbol with no definition. This template saves us
+// the trouble of providing manual declarations for overloads with some other local name
+// for each function name being overloaded (operator new, operator new[], etc.).
+template <typename _Ret, typename... _Args, _Ret (*_Func)(_Args...)>
+struct __impl_ref<_Func> {
+  [[gnu::visibility("hidden")]] static _Ret __impl_(_Args...);
+};
+
+// This takes a function type template argument first so that the second non-type template
+// argument (pointer to the public function) gets the benefit of type-aware overload
+// resolution, rather than having to use a static_cast.
 template <typename T, T* _Func>
 _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-  uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
-  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
-  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
-
-#  if __has_feature(ptrauth_calls)
-  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. See full explanation above.
-  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
+#  if !defined(_LIBCPP_CLANG_VER) || _LIBCPP_CLANG_VER >= 2101
+  __asm__("%cc0 = %cc1" : : "X"(__impl_ref<_Func>::__impl_), "X"(_Func));
+#  else
+  __asm__("%c0 = %c1" : : "X"(__impl_ref<_Func>::__impl_), "X"(_Func));
 #  endif
-
-  return __ptr < __start || __ptr > __end;
+  // This just has the compiler compare the two symbols. For PIC mode, this will do a
+  // direct PC-relative materialization for __impl_ref<...>::__impl_ and a GOT load for
+  // the _Func symbol. The compiler thinks __impl_ref<...>::__impl_ is defined elsewhere
+  // at link time and will be an undefined symbol. It doesn't know that the __asm__ tells
+  // the assembler to define it as a local symbol.
+  return __libcpp_launder(_Func) != __impl_ref<_Func>::__impl_;
 }
+
 _LIBCPP_END_NAMESPACE_STD
 
 #else

``````````

</details>


https://github.com/llvm/llvm-project/pull/208330

From libcxx-commits at lists.llvm.org  Wed Jul  8 17:10:06 2026
From: libcxx-commits at lists.llvm.org (Eli Friedman via libcxx-commits)
Date: Wed, 08 Jul 2026 17:10:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Make ios_base::xalloc non-atomic with
 LIBCXX_ENABLE_THREADS=OFF. (PR #208356)
Message-ID: <llvm.org/llvm/llvm-project/pull/208356@github.com>


https://github.com/efriedma-quic created https://github.com/llvm/llvm-project/pull/208356

762b77a moved the definition of "xindex" out of the header, and in the process dropped the _LIBCPP_HAS_THREADS check.  Re-add the check to maintain the status quo.

The discussion on https://github.com/llvm/llvm-project/pull/198994 indicates it's not clear whether LIBCXX_ENABLE_THREADS=OFF is actually supposed to mean single-threaded.  But it clearly does in practice: atomic_support.h uses non-atomic ops when threads are disabled, and a few other APIs have explicit non-atomic fallback paths.

My team ran into this trying to run libc++ tests for a RISC-V core without the "a" extension.

>From 1c1c7bf8d2b3169713f18e46698f102c496f6042 Mon Sep 17 00:00:00 2001
From: Eli Friedman <efriedma at qti.qualcomm.com>
Date: Wed, 8 Jul 2026 16:57:44 -0700
Subject: [PATCH] Make ios_base::xalloc non-atomic with
 LIBCXX_ENABLE_THREADS=OFF.

762b77a moved the definition of "xindex" out of the header, and in the
process dropped the _LIBCPP_HAS_THREADS check.  Re-add the check to
maintain the status quo.

The discussion on https://github.com/llvm/llvm-project/pull/198994
indicates it's not clear whether LIBCXX_ENABLE_THREADS=OFF is actually
supposed to mean single-threaded.  But it clearly does in practice:
atomic_support.h uses non-atomic ops when threads are disabled, and
a few other APIs have explicit non-atomic fallback paths.
---
 libcxx/src/ios.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index 2e049098740dc..db6ca50f7f6ca 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -122,7 +122,14 @@ static size_t __ios_new_cap(size_t __req_size, size_t __current_cap) { // Precon
 }
 
 int ios_base::xalloc() {
+#if _LIBCPP_HAS_THREADS
   constinit static atomic<int> xindex = 0;
+#else
+  // If we don't have atomics, fall back to single-threaded implementation.
+  // FIXME: Should "single-threaded" be a separate option from
+  // _LIBCPP_HAS_THREADS?
+  static int xindex = 0;
+#endif // _LIBCPP_HAS_THREADS
   return xindex++;
 }
 


From libcxx-commits at lists.llvm.org  Wed Jul  8 17:17:24 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 17:17:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Make ios_base::xalloc non-atomic with
 LIBCXX_ENABLE_THREADS=OFF. (PR #208356)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208356@github.com>
Message-ID: <6a4ee894.4a342cbc.ca844.fd7b@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Eli Friedman (efriedma-quic)

<details>
<summary>Changes</summary>

762b77a moved the definition of "xindex" out of the header, and in the process dropped the _LIBCPP_HAS_THREADS check.  Re-add the check to maintain the status quo.

The discussion on https://github.com/llvm/llvm-project/pull/198994 indicates it's not clear whether LIBCXX_ENABLE_THREADS=OFF is actually supposed to mean single-threaded.  But it clearly does in practice: atomic_support.h uses non-atomic ops when threads are disabled, and a few other APIs have explicit non-atomic fallback paths.

My team ran into this trying to run libc++ tests for a RISC-V core without the "a" extension.

---
Full diff: https://github.com/llvm/llvm-project/pull/208356.diff


1 Files Affected:

- (modified) libcxx/src/ios.cpp (+7) 


``````````diff
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index 2e049098740dc..db6ca50f7f6ca 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -122,7 +122,14 @@ static size_t __ios_new_cap(size_t __req_size, size_t __current_cap) { // Precon
 }
 
 int ios_base::xalloc() {
+#if _LIBCPP_HAS_THREADS
   constinit static atomic<int> xindex = 0;
+#else
+  // If we don't have atomics, fall back to single-threaded implementation.
+  // FIXME: Should "single-threaded" be a separate option from
+  // _LIBCPP_HAS_THREADS?
+  static int xindex = 0;
+#endif // _LIBCPP_HAS_THREADS
   return xindex++;
 }
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/208356

From libcxx-commits at lists.llvm.org  Wed Jul  8 17:19:39 2026
From: libcxx-commits at lists.llvm.org (Eli Friedman via libcxx-commits)
Date: Wed, 08 Jul 2026 17:19:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove ios_base::__xindex_ from
	the ABI (PR #198994)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/198994@github.com>
Message-ID: <6a4ee91b.7270d2fd.2d4879.26ee@mx.google.com>


efriedma-quic wrote:

Opened #208356 to restore the old behavior with `LIBCXX_ENABLE_THREADS=OFF`.

Bug #131365 describes the issue that `LIBCXX_ENABLE_THREADS` doesn't mean what we want it to mean.

https://github.com/llvm/llvm-project/pull/198994

From libcxx-commits at lists.llvm.org  Wed Jul  8 17:21:58 2026
From: libcxx-commits at lists.llvm.org (Eli Friedman via libcxx-commits)
Date: Wed, 08 Jul 2026 17:21:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Make ios_base::xalloc non-atomic with
 LIBCXX_ENABLE_THREADS=OFF. (PR #208356)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208356@github.com>
Message-ID: <6a4ee9a6.f8b79514.1d52d0.8b96@mx.google.com>


efriedma-quic wrote:

See also #131365 .

https://github.com/llvm/llvm-project/pull/208356

From libcxx-commits at lists.llvm.org  Wed Jul  8 21:13:27 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 21:13:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4f1fe7.331483e7.2440f8.0222@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Lucas Mellone (lknknm)

<details>
<summary>Changes</summary>

[[nodiscard]] should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/range.lazy.split

Towards https://github.com/llvm/llvm-project/issues/172124

---
Full diff: https://github.com/llvm/llvm-project/pull/208036.diff


2 Files Affected:

- (modified) libcxx/include/__ranges/lazy_split_view.h (+17-13) 
- (added) libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp (+79) 


``````````diff
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 938dca24cc4fc..27a9d5e77abf1 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -95,14 +95,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
       : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
       return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
     } else {
@@ -111,19 +111,19 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<_View> && forward_range<const _View>
   {
     return __outer_iterator<true>{*this, ranges::begin(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
     return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
     if constexpr (forward_range<_View> && forward_range<const _View> && common_range<const _View>) {
       return __outer_iterator<true>{*this, ranges::end(__base_)};
     } else {
@@ -188,8 +188,10 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       _LIBCPP_HIDE_FROM_ABI value_type() = default;
       _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(__outer_iterator __i) : __i_(std::move(__i)) {}
 
-      _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; }
-      _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+      [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator<_Const> begin() const {
+        return __inner_iterator<_Const>{__i_};
+      }
+      [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
     };
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
@@ -206,7 +208,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return value_type{*this}; }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
       const auto __end = ranges::end(__parent_->__base_);
@@ -342,14 +344,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
 
-    _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
-    _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
+      return __i_.__current();
+    }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
     {
       return std::move(__i_.__current());
     }
 
-    _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__i_.__current(); }
 
     _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
       __incremented_ = true;
@@ -385,7 +389,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
       return __x.__is_done();
     }
 
-    _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
     iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(__i.__outer_current()))) {
       return ranges::iter_move(__i.__outer_current());
     }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..aa33b8ce42331
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/nodiscard.verify.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Test the libc++ extension that std::ranges::lazy_split_view and std::views::lazy_split are marked as [[nodiscard]].
+
+#include <ranges>
+#include <string>
+#include <utility>
+
+void test() {
+  std::string str = "the quick brown fox";
+  char pattern    = ' ';
+
+  auto v = std::views::lazy_split(str, pattern);
+
+  // [range.lazy.split.view]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(v).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // [range.lazy.split.outer]
+
+  auto outer_it = as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *outer_it;
+
+  // [range.lazy.split.outer.value]
+
+  auto value = *outer_it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  value.begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  value.end();
+
+  // [range.lazy.split.outer.inner]
+
+  auto inner_it = value.begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  inner_it.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(inner_it).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *inner_it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(inner_it);
+
+  // [range.lazy.split.overview]
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::lazy_split(str, pattern);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::lazy_split(pattern);
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/208036

From libcxx-commits at lists.llvm.org  Wed Jul  8 21:52:13 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 08 Jul 2026 21:52:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4f28fd.f732ad81.3a4994.2f05@mx.google.com>


Zingam wrote:

LGTM. The failing Windows test is unrelated.

https://github.com/llvm/llvm-project/pull/208036

From libcxx-commits at lists.llvm.org  Wed Jul  8 22:09:54 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 08 Jul 2026 22:09:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a4f2d22.f732ad81.3a4994.d353@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/206498

>From eac8181d96f0ed0ff110d7cf7602551199913a6b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 18:53:17 +0530
Subject: [PATCH 1/9] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. Omit the noreplace mode strings in
 __make_mdstring() on AIX, causing open() to return nullptr for these modes.
 Update the test to expect failure on AIX for all noreplace cases, since the
 platform cannot provide atomic exclusive-open semantics.

---
 libcxx/include/fstream                        | 10 ++++++++
 .../filebuf.members/open_pointer.pass.cpp     | 24 +++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..f979db89db794 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -664,6 +664,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -674,6 +683,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..f7ab0239a1b18 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -75,7 +73,10 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -83,7 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 
@@ -94,7 +101,10 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -102,7 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 #  endif

>From 9c8dfa79291faa95c5a036e5d6b2ac43d199aee1 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 12:40:23 -0400
Subject: [PATCH 2/9] fix format and add warning

---
 libcxx/include/fstream                               |  3 +++
 .../fstreams/filebuf.members/open_pointer.pass.cpp   | 12 ++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index f979db89db794..b71baabff2524 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -672,6 +672,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if defined(_AIX) || defined(__TOS_AIX__)
+#        warning 'exclusive mode is unsupported on this target'
+#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index f7ab0239a1b18..627a65cac3f2f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -84,13 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#  else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#  endif
           }
         }
 
@@ -112,13 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#    else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#    endif
           }
         }
 #  endif

>From d324a675f05f851656c4fb10ac6eaf5120ebb08e Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 12:52:06 -0400
Subject: [PATCH 3/9] fix warnings treated as errors

---
 libcxx/include/fstream | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b71baabff2524..91259a39f4000 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -673,7 +673,10 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
 #      if defined(_AIX) || defined(__TOS_AIX__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic warning "-Wcpp"
 #        warning 'exclusive mode is unsupported on this target'
+#        pragma GCC diagnostic pop
 #      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:

>From 450f6694bddb62f12cc9a666b3c1c300a90f725c Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 13:49:31 -0400
Subject: [PATCH 4/9] add warning as fprintf

---
 libcxx/include/fstream | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 91259a39f4000..92baa4670b363 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -672,12 +672,6 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if defined(_AIX) || defined(__TOS_AIX__)
-#        pragma GCC diagnostic push
-#        pragma GCC diagnostic warning "-Wcpp"
-#        warning 'exclusive mode is unsupported on this target'
-#        pragma GCC diagnostic pop
-#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -780,9 +774,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr)
+  if (!__mdstr){
+#     if defined(_AIX) || defined(__TOS_AIX__)
+    if (__mode & ios_base::noreplace)
+      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
+#     endif
     return nullptr;
-
+}
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From eb0c8a9711dd9ed86e21ec217adb49f8cc7e3ec3 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:30:10 +0530
Subject: [PATCH 5/9] fix formatting

---
 libcxx/include/fstream | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 92baa4670b363..216082cfb8283 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -774,13 +774,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr){
-#     if defined(_AIX) || defined(__TOS_AIX__)
+  if (!__mdstr) {
+#      if defined(_AIX) || defined(__TOS_AIX__)
     if (__mode & ios_base::noreplace)
       fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#     endif
+#      endif
     return nullptr;
-}
+  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From 9f7f1e7bc05bf06350f2846f8f736366133b1b8b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:37:48 +0530
Subject: [PATCH 6/9] add the remaining test files

---
 .../fstreams/fstream.cons/pointer.pass.cpp    | 24 +++++++++++++++---
 .../fstream.members/open_pointer.pass.cpp     | 24 +++++++++++++++---
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 24 +++++++++++++++---
 .../ofstream.members/open_pointer.pass.cpp    | 25 ++++++++++++++++---
 4 files changed, 81 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..a677b16e76479 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
@@ -91,14 +89,23 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -108,14 +115,23 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..92c0cf1aa0c00 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -74,7 +72,10 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -82,7 +83,13 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -93,7 +100,10 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -101,7 +111,13 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..4fae51f38adbd 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
@@ -145,14 +143,23 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -162,14 +169,23 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..493af1bda9c98 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -82,7 +80,10 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -90,7 +91,13 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -101,14 +108,24 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
+
           {
             std::remove(tmp.c_str());
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif

>From 150ffaacc7f92197986a67f317374c030a7e78cf Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 6 Jul 2026 12:15:37 +0530
Subject: [PATCH 7/9] review changes

---
 libcxx/include/fstream                        | 21 +++++++++++--------
 .../filebuf.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/fstream.cons/pointer.pass.cpp    | 18 ++++------------
 .../fstream.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 18 ++++------------
 .../ofstream.members/open_pointer.pass.cpp    | 18 ++++------------
 6 files changed, 32 insertions(+), 79 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 216082cfb8283..f37522db70b1d 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -258,6 +258,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -671,8 +673,8 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // existence of the file and create it, it shall fail rather than perform a
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
-// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if !defined(_AIX) && !defined(__TOS_AIX__)
+// _AIX is defined on AIX by all supported compilers.
+#      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -683,7 +685,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
-#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
+#      endif // !defined(_AIX)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
@@ -774,13 +776,8 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr) {
-#      if defined(_AIX) || defined(__TOS_AIX__)
-    if (__mode & ios_base::noreplace)
-      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#      endif
+  if (!__mdstr)
     return nullptr;
-  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1222,6 +1219,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1380,6 +1379,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1544,6 +1545,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 627a65cac3f2f..37cfd985d1c11 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -73,10 +73,7 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -84,9 +81,7 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -101,10 +96,7 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -112,9 +104,7 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index a677b16e76479..2a297ca87903e 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -89,19 +89,14 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -115,19 +110,14 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 92c0cf1aa0c00..9178788b60354 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -72,10 +72,7 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -83,9 +80,7 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -100,10 +95,7 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -111,9 +103,7 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index 4fae51f38adbd..31a944236d1d0 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -143,19 +143,14 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -169,19 +164,14 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 493af1bda9c98..0b77f012b345d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -80,10 +80,7 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -91,9 +88,7 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -108,10 +103,7 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -119,9 +111,7 @@ int main(int, char**)
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist

>From 956eafcbab954425ae5f1e0864d1499b183c0734 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 8 Jul 2026 11:03:55 +0530
Subject: [PATCH 8/9] add warnings for char* overloads

---
 libcxx/include/fstream                        | 45 +++++++++++++------
 .../fstreams/diagnose_noreplace.verify.cpp    | 27 +++++++++++
 .../ofstream.members/open_pointer.pass.cpp    |  1 -
 3 files changed, 59 insertions(+), 14 deletions(-)
 create mode 100644 libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index f37522db70b1d..4de91b1fb232b 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -256,10 +256,16 @@ public:
 
   // 27.9.1.4 Members:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+#      if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -666,14 +672,8 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
-// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
-// cases are omitted on AIX and fall through to default (return nullptr), signalling
-// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
-// by proxy, requires that if the implementation cannot atomically check for the
-// existence of the file and create it, it shall fail rather than perform a
-// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
-// semantics via fopen(), returning nullptr is the correct behaviour.
-// _AIX is defined on AIX by all supported compilers.
+// AIX fopen() does not support the 'x' (noreplace) mode suffix; these cases fall through to
+// default (return nullptr), which is conformant per C23 7.21.5.3p5 (fail if atomic check-and-create is unsupported).
 #      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -686,7 +686,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #      endif // !defined(_AIX)
-#    endif // _LIBCPP_STD_VER >= 23
+#    endif   // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
   }
@@ -778,6 +778,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   const wchar_t* __mdstr = __make_mdwstring(__mode);
   if (!__mdstr)
     return nullptr;
+
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1217,10 +1218,16 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+#      if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1377,10 +1384,16 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+#      if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1543,10 +1556,16 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+#      if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp
new file mode 100644
index 0000000000000..0a1d484dcd41d
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// AIX fopen() does not support the 'x' mode.
+
+// REQUIRES: aix
+
+#include <fstream>
+
+void test() {
+  std::filebuf fb;
+  fb.open("f", std::ios_base::out | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+
+  std::ifstream ifs;
+  ifs.open("f", std::ios_base::in | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+
+  std::ofstream ofs;
+  ofs.open("f", std::ios_base::out | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+
+  std::fstream fs;
+  fs.open("f", std::ios_base::out | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+}
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 0b77f012b345d..834a4c4d9cde8 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -105,7 +105,6 @@ int main(int, char**)
             f.open(tmp.c_str(), mode);
             assert(!f.is_open()); // since it already exists
           }
-
           {
             std::remove(tmp.c_str());
 

>From 24f802b71b4794c946250d96a7365e47e53af56c Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 9 Jul 2026 10:39:29 +0530
Subject: [PATCH 9/9] change requires to expect target aix

---
 .../fstreams/diagnose_noreplace.verify.cpp    | 26 +++++++++++++++----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp
index 0a1d484dcd41d..4842176f572c4 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp
@@ -8,20 +8,36 @@
 
 // AIX fopen() does not support the 'x' mode.
 
-// REQUIRES: aix
+// REQUIRES: target={{.+}}-aix{{.*}}
 
 #include <fstream>
 
 void test() {
   std::filebuf fb;
-  fb.open("f", std::ios_base::out | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+  fb.open(
+      "f",
+      std::ios_base::out |
+          std::ios_base::
+              noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
 
   std::ifstream ifs;
-  ifs.open("f", std::ios_base::in | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+  ifs.open(
+      "f",
+      std::ios_base::in |
+          std::ios_base::
+              noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
 
   std::ofstream ofs;
-  ofs.open("f", std::ios_base::out | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+  ofs.open(
+      "f",
+      std::ios_base::out |
+          std::ios_base::
+              noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
 
   std::fstream fs;
-  fs.open("f", std::ios_base::out | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+  fs.open(
+      "f",
+      std::ios_base::out |
+          std::ios_base::
+              noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
 }


From libcxx-commits at lists.llvm.org  Wed Jul  8 23:28:24 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 08 Jul 2026 23:28:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add missing <string> include (PR
	#208281)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208281@github.com>
Message-ID: <6a4f3f88.bd93e731.358318.e45c@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/208281

From libcxx-commits at lists.llvm.org  Wed Jul  8 23:48:04 2026
From: libcxx-commits at lists.llvm.org (Michele Scandale via libcxx-commits)
Date: Wed, 08 Jul 2026 23:48:04 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] Fix vector types w.r.t. qualified
	element type. (PR #197624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/197624@github.com>
Message-ID: <6a4f4424.510f576d.1b3cb9.17f2@mx.google.com>


https://github.com/michele-scandale updated https://github.com/llvm/llvm-project/pull/197624

>From 052e2b1e381145e1e90b6e48fc8f319e4fee5901 Mon Sep 17 00:00:00 2001
From: Michele Scandale <michele.scandale at gmail.com>
Date: Thu, 25 Jun 2026 12:51:58 -0700
Subject: [PATCH 1/2] Fix vector types w.r.t. qualified element type.

This commit changes the way vector types are constructed to ensure that
qualifiers on the element type are moved to the vector type itself --
i.e. a canonical vector type has always an unqualified element type.

RFC: https://discourse.llvm.org/t/90783
---
 clang/include/clang/AST/ASTContext.h |   5 +-
 clang/include/clang/AST/TypeBase.h   |  36 +++++---
 clang/lib/AST/ASTContext.cpp         | 127 +++++++++++++--------------
 clang/lib/AST/Type.cpp               |  80 ++++++++++++++---
 clang/test/Sema/types.c              |  12 +++
 clang/test/SemaCXX/vector.cpp        |  14 +++
 6 files changed, 183 insertions(+), 91 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index c04b380f9ec5b..aedef5cd853d1 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -238,13 +238,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
   mutable std::vector<VariableArrayType*> VariableArrayTypes;
   mutable llvm::ContextualFoldingSet<DependentSizedArrayType, ASTContext &>
       DependentSizedArrayTypes;
-  mutable llvm::ContextualFoldingSet<DependentSizedExtVectorType, ASTContext &>
+  mutable llvm::FoldingSet<DependentSizedExtVectorType>
       DependentSizedExtVectorTypes;
   mutable llvm::ContextualFoldingSet<DependentAddressSpaceType, ASTContext &>
       DependentAddressSpaceTypes;
   mutable llvm::FoldingSet<VectorType> VectorTypes;
-  mutable llvm::ContextualFoldingSet<DependentVectorType, ASTContext &>
-      DependentVectorTypes;
+  mutable llvm::FoldingSet<DependentVectorType> DependentVectorTypes;
   mutable llvm::FoldingSet<ConstantMatrixType> MatrixTypes;
   mutable llvm::ContextualFoldingSet<DependentSizedMatrixType, ASTContext &>
       DependentSizedMatrixTypes;
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index c9658775f0470..88c31fc0bf037 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -4167,13 +4167,14 @@ class DependentSizedExtVectorType : public Type, public llvm::FoldingSetNode {
 
   Expr *SizeExpr;
 
-  /// The element type of the array.
+  /// The element type of the vector.
   QualType ElementType;
 
   SourceLocation loc;
+  const ASTContext &Context;
 
-  DependentSizedExtVectorType(QualType ElementType, QualType can,
-                              Expr *SizeExpr, SourceLocation loc);
+  DependentSizedExtVectorType(const ASTContext &Context, QualType ElementType,
+                              QualType can, Expr *SizeExpr, SourceLocation loc);
 
 public:
   Expr *getSizeExpr() const { return SizeExpr; }
@@ -4183,11 +4184,13 @@ class DependentSizedExtVectorType : public Type, public llvm::FoldingSetNode {
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
 
+  SplitQualType getSplitUnqualifiedType() const;
+
   static bool classof(const Type *T) {
     return T->getTypeClass() == DependentSizedExtVector;
   }
 
-  void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
+  void Profile(llvm::FoldingSetNodeID &ID) {
     Profile(ID, Context, getElementType(), getSizeExpr());
   }
 
@@ -4243,12 +4246,14 @@ class VectorType : public Type, public llvm::FoldingSetNode {
   /// The element type of the vector.
   QualType ElementType;
 
-  VectorType(QualType vecType, unsigned nElements, QualType canonType,
-             VectorKind vecKind);
+  const ASTContext &Context;
 
-  VectorType(TypeClass tc, QualType vecType, unsigned nElements,
+  VectorType(const ASTContext &Context, QualType vecType, unsigned nElements,
              QualType canonType, VectorKind vecKind);
 
+  VectorType(const ASTContext &Context, TypeClass tc, QualType vecType,
+             unsigned nElements, QualType canonType, VectorKind vecKind);
+
 public:
   QualType getElementType() const { return ElementType; }
   unsigned getNumElements() const { return VectorTypeBits.NumElements; }
@@ -4256,6 +4261,8 @@ class VectorType : public Type, public llvm::FoldingSetNode {
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
 
+  SplitQualType getSplitUnqualifiedType() const;
+
   VectorKind getVectorKind() const {
     return VectorKind(VectorTypeBits.VecKind);
   }
@@ -4294,9 +4301,11 @@ class DependentVectorType : public Type, public llvm::FoldingSetNode {
   QualType ElementType;
   Expr *SizeExpr;
   SourceLocation Loc;
+  const ASTContext &Context;
 
-  DependentVectorType(QualType ElementType, QualType CanonType, Expr *SizeExpr,
-                      SourceLocation Loc, VectorKind vecKind);
+  DependentVectorType(const ASTContext &Context, QualType ElementType,
+                      QualType CanonType, Expr *SizeExpr, SourceLocation Loc,
+                      VectorKind vecKind);
 
 public:
   Expr *getSizeExpr() const { return SizeExpr; }
@@ -4309,11 +4318,13 @@ class DependentVectorType : public Type, public llvm::FoldingSetNode {
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
 
+  SplitQualType getSplitUnqualifiedType() const;
+
   static bool classof(const Type *T) {
     return T->getTypeClass() == DependentVector;
   }
 
-  void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
+  void Profile(llvm::FoldingSetNodeID &ID) {
     Profile(ID, Context, getElementType(), getSizeExpr(), getVectorKind());
   }
 
@@ -4331,8 +4342,9 @@ class DependentVectorType : public Type, public llvm::FoldingSetNode {
 class ExtVectorType : public VectorType {
   friend class ASTContext; // ASTContext creates these.
 
-  ExtVectorType(QualType vecType, unsigned nElements, QualType canonType)
-      : VectorType(ExtVector, vecType, nElements, canonType,
+  ExtVectorType(const ASTContext &Context, QualType vecType, unsigned nElements,
+                QualType canonType)
+      : VectorType(Context, ExtVector, vecType, nElements, canonType,
                    VectorKind::Generic) {}
 
 public:
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 525d0b6619119..8060b018b66a5 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -931,8 +931,7 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
                        IdentifierTable &idents, SelectorTable &sels,
                        Builtin::Context &builtins, TranslationUnitKind TUKind)
     : ConstantArrayTypes(this_(), ConstantArrayTypesLog2InitSize),
-      DependentSizedArrayTypes(this_()), DependentSizedExtVectorTypes(this_()),
-      DependentAddressSpaceTypes(this_()), DependentVectorTypes(this_()),
+      DependentSizedArrayTypes(this_()), DependentAddressSpaceTypes(this_()),
       DependentSizedMatrixTypes(this_()),
       FunctionProtoTypes(this_(), FunctionProtoTypesLog2InitSize),
       DependentTypeOfExprTypes(this_()), DependentDecltypeTypes(this_()),
@@ -4717,18 +4716,20 @@ QualType ASTContext::getVectorType(QualType vecType, unsigned NumElts,
   if (VectorType *VTP = VectorTypes.FindNodeOrInsertPos(ID, InsertPos))
     return QualType(VTP, 0);
 
-  // If the element type isn't canonical, this won't be a canonical type either,
-  // so fill in the canonical type field.
+  // If the element type isn't canonical or has qualifiers, this won't be a
+  // canonical type either, so fill in the canonical type field.
   QualType Canonical;
-  if (!vecType.isCanonical()) {
-    Canonical = getVectorType(getCanonicalType(vecType), NumElts, VecKind);
+  if (!vecType.isCanonical() || vecType.hasLocalQualifiers()) {
+    SplitQualType canonSplit = getCanonicalType(vecType).split();
+    Canonical = getVectorType(QualType(canonSplit.Ty, 0), NumElts, VecKind);
+    Canonical = getQualifiedType(Canonical, canonSplit.Quals);
 
     // Get the new insert position for the node we care about.
     VectorType *NewIP = VectorTypes.FindNodeOrInsertPos(ID, InsertPos);
     assert(!NewIP && "Shouldn't be in the map!"); (void)NewIP;
   }
   auto *New = new (*this, alignof(VectorType))
-      VectorType(vecType, NumElts, Canonical, VecKind);
+      VectorType(*this, vecType, NumElts, Canonical, VecKind);
   VectorTypes.InsertNode(New, InsertPos);
   Types.push_back(New);
   return QualType(New, 0);
@@ -4737,37 +4738,37 @@ QualType ASTContext::getVectorType(QualType vecType, unsigned NumElts,
 QualType ASTContext::getDependentVectorType(QualType VecType, Expr *SizeExpr,
                                             SourceLocation AttrLoc,
                                             VectorKind VecKind) const {
+  SplitQualType CanonVecType = getCanonicalType(VecType).split();
+
   llvm::FoldingSetNodeID ID;
-  DependentVectorType::Profile(ID, *this, getCanonicalType(VecType), SizeExpr,
-                               VecKind);
+  DependentVectorType::Profile(ID, *this, QualType(CanonVecType.Ty, 0),
+                               SizeExpr, VecKind);
   void *InsertPos = nullptr;
   DependentVectorType *Canon =
       DependentVectorTypes.FindNodeOrInsertPos(ID, InsertPos);
-  DependentVectorType *New;
 
-  if (Canon) {
-    New = new (*this, alignof(DependentVectorType)) DependentVectorType(
-        VecType, QualType(Canon, 0), SizeExpr, AttrLoc, VecKind);
-  } else {
-    QualType CanonVecTy = getCanonicalType(VecType);
-    if (CanonVecTy == VecType) {
-      New = new (*this, alignof(DependentVectorType))
-          DependentVectorType(VecType, QualType(), SizeExpr, AttrLoc, VecKind);
-
-      DependentVectorType *CanonCheck =
-          DependentVectorTypes.FindNodeOrInsertPos(ID, InsertPos);
-      assert(!CanonCheck &&
-             "Dependent-sized vector_size canonical type broken");
-      (void)CanonCheck;
-      DependentVectorTypes.InsertNode(New, InsertPos);
-    } else {
-      QualType CanonTy = getDependentVectorType(CanonVecTy, SizeExpr,
-                                                SourceLocation(), VecKind);
-      New = new (*this, alignof(DependentVectorType))
-          DependentVectorType(VecType, CanonTy, SizeExpr, AttrLoc, VecKind);
-    }
+  // If we don't have one, build one.
+  if (!Canon) {
+    Canon = new (*this, TypeAlignment)
+        DependentVectorType(*this, QualType(CanonVecType.Ty, 0), QualType(),
+                            SizeExpr, AttrLoc, VecKind);
+    DependentVectorTypes.InsertNode(Canon, InsertPos);
+    Types.push_back(Canon);
   }
 
+  // Apply qualifiers from the element type to the vector.
+  QualType CanonTy = getQualifiedType(QualType(Canon, 0), CanonVecType.Quals);
+
+  // If we didn't need extra canonicalization for the element type or the size
+  // expression, then just use that as our result.
+  if (QualType(CanonVecType.Ty, 0) == VecType &&
+      Canon->getSizeExpr() == SizeExpr)
+    return CanonTy;
+
+  // Otherwise, we need to build a type which follows the spelling
+  // of the element type.
+  auto *New = new (*this, TypeAlignment)
+      DependentVectorType(*this, VecType, CanonTy, SizeExpr, AttrLoc, VecKind);
   Types.push_back(New);
   return QualType(New, 0);
 }
@@ -4789,60 +4790,58 @@ QualType ASTContext::getExtVectorType(QualType vecType,
   if (VectorType *VTP = VectorTypes.FindNodeOrInsertPos(ID, InsertPos))
     return QualType(VTP, 0);
 
-  // If the element type isn't canonical, this won't be a canonical type either,
-  // so fill in the canonical type field.
+  // If the element type isn't canonical or has qualifiers, this won't be a
+  // canonical type either, so fill in the canonical type field.
   QualType Canonical;
-  if (!vecType.isCanonical()) {
-    Canonical = getExtVectorType(getCanonicalType(vecType), NumElts);
+  if (!vecType.isCanonical() || vecType.hasLocalQualifiers()) {
+    SplitQualType canonSplit = getCanonicalType(vecType).split();
+    Canonical = getExtVectorType(QualType(canonSplit.Ty, 0), NumElts);
+    Canonical = getQualifiedType(Canonical, canonSplit.Quals);
 
     // Get the new insert position for the node we care about.
     VectorType *NewIP = VectorTypes.FindNodeOrInsertPos(ID, InsertPos);
     assert(!NewIP && "Shouldn't be in the map!"); (void)NewIP;
   }
   auto *New = new (*this, alignof(ExtVectorType))
-      ExtVectorType(vecType, NumElts, Canonical);
+      ExtVectorType(*this, vecType, NumElts, Canonical);
   VectorTypes.InsertNode(New, InsertPos);
   Types.push_back(New);
   return QualType(New, 0);
 }
 
 QualType
-ASTContext::getDependentSizedExtVectorType(QualType vecType,
-                                           Expr *SizeExpr,
+ASTContext::getDependentSizedExtVectorType(QualType VecType, Expr *SizeExpr,
                                            SourceLocation AttrLoc) const {
+  SplitQualType CanonVecType = getCanonicalType(VecType).split();
+
   llvm::FoldingSetNodeID ID;
-  DependentSizedExtVectorType::Profile(ID, *this, getCanonicalType(vecType),
+  DependentSizedExtVectorType::Profile(ID, *this, QualType(CanonVecType.Ty, 0),
                                        SizeExpr);
-
   void *InsertPos = nullptr;
   DependentSizedExtVectorType *Canon
     = DependentSizedExtVectorTypes.FindNodeOrInsertPos(ID, InsertPos);
-  DependentSizedExtVectorType *New;
-  if (Canon) {
-    // We already have a canonical version of this array type; use it as
-    // the canonical type for a newly-built type.
-    New = new (*this, alignof(DependentSizedExtVectorType))
-        DependentSizedExtVectorType(vecType, QualType(Canon, 0), SizeExpr,
-                                    AttrLoc);
-  } else {
-    QualType CanonVecTy = getCanonicalType(vecType);
-    if (CanonVecTy == vecType) {
-      New = new (*this, alignof(DependentSizedExtVectorType))
-          DependentSizedExtVectorType(vecType, QualType(), SizeExpr, AttrLoc);
-
-      DependentSizedExtVectorType *CanonCheck
-        = DependentSizedExtVectorTypes.FindNodeOrInsertPos(ID, InsertPos);
-      assert(!CanonCheck && "Dependent-sized ext_vector canonical type broken");
-      (void)CanonCheck;
-      DependentSizedExtVectorTypes.InsertNode(New, InsertPos);
-    } else {
-      QualType CanonExtTy = getDependentSizedExtVectorType(CanonVecTy, SizeExpr,
-                                                           SourceLocation());
-      New = new (*this, alignof(DependentSizedExtVectorType))
-          DependentSizedExtVectorType(vecType, CanonExtTy, SizeExpr, AttrLoc);
-    }
+
+  // If we don't have one, build one.
+  if (!Canon) {
+    Canon = new (*this, TypeAlignment) DependentSizedExtVectorType(
+        *this, QualType(CanonVecType.Ty, 0), QualType(), SizeExpr, AttrLoc);
+    DependentSizedExtVectorTypes.InsertNode(Canon, InsertPos);
+    Types.push_back(Canon);
   }
 
+  // Apply qualifiers from the element type to the vector.
+  QualType CanonTy = getQualifiedType(QualType(Canon, 0), CanonVecType.Quals);
+
+  // If we didn't need extra canonicalization for the element type or the size
+  // expression, then just use that as our result.
+  if (QualType(CanonVecType.Ty, 0) == VecType &&
+      Canon->getSizeExpr() == SizeExpr)
+    return CanonTy;
+
+  // Otherwise, we need to build a type which follows the spelling
+  // of the element type.
+  auto *New = new (*this, TypeAlignment)
+      DependentSizedExtVectorType(*this, VecType, CanonTy, SizeExpr, AttrLoc);
   Types.push_back(New);
   return QualType(New, 0);
 }
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 42d148715bc40..8256188212edc 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -335,7 +335,8 @@ void DependentSizedArrayType::Profile(llvm::FoldingSetNodeID &ID,
     E->Profile(ID, Context, true);
 }
 
-DependentVectorType::DependentVectorType(QualType ElementType,
+DependentVectorType::DependentVectorType(const ASTContext &Context,
+                                         QualType ElementType,
                                          QualType CanonType, Expr *SizeExpr,
                                          SourceLocation Loc, VectorKind VecKind)
     : Type(DependentVector, CanonType,
@@ -343,10 +344,21 @@ DependentVectorType::DependentVectorType(QualType ElementType,
                ElementType->getDependence() |
                (SizeExpr ? toTypeDependence(SizeExpr->getDependence())
                          : TypeDependence::None)),
-      ElementType(ElementType), SizeExpr(SizeExpr), Loc(Loc) {
+      ElementType(ElementType), SizeExpr(SizeExpr), Loc(Loc), Context(Context) {
   VectorTypeBits.VecKind = llvm::to_underlying(VecKind);
 }
 
+SplitQualType DependentVectorType::getSplitUnqualifiedType() const {
+  auto SplitElem = ElementType.getSplitUnqualifiedType();
+  QualType UnqualElemTy(SplitElem.Ty, 0);
+  if (UnqualElemTy == ElementType)
+    return SplitQualType(this, Qualifiers());
+
+  auto UnqualVecTy = Context.getDependentVectorType(
+      UnqualElemTy, getSizeExpr(), getAttributeLoc(), getVectorKind());
+  return SplitQualType(UnqualVecTy.getTypePtr(), SplitElem.Quals);
+}
+
 void DependentVectorType::Profile(llvm::FoldingSetNodeID &ID,
                                   const ASTContext &Context,
                                   QualType ElementType, const Expr *SizeExpr,
@@ -356,16 +368,27 @@ void DependentVectorType::Profile(llvm::FoldingSetNodeID &ID,
   SizeExpr->Profile(ID, Context, true);
 }
 
-DependentSizedExtVectorType::DependentSizedExtVectorType(QualType ElementType,
-                                                         QualType can,
-                                                         Expr *SizeExpr,
-                                                         SourceLocation loc)
+DependentSizedExtVectorType::DependentSizedExtVectorType(
+    const ASTContext &Context, QualType ElementType, QualType can,
+    Expr *SizeExpr, SourceLocation loc)
     : Type(DependentSizedExtVector, can,
            TypeDependence::DependentInstantiation |
                ElementType->getDependence() |
                (SizeExpr ? toTypeDependence(SizeExpr->getDependence())
                          : TypeDependence::None)),
-      SizeExpr(SizeExpr), ElementType(ElementType), loc(loc) {}
+      SizeExpr(SizeExpr), ElementType(ElementType), loc(loc), Context(Context) {
+}
+
+SplitQualType DependentSizedExtVectorType::getSplitUnqualifiedType() const {
+  auto SplitElem = ElementType.getSplitUnqualifiedType();
+  QualType UnqualElemTy(SplitElem.Ty, 0);
+  if (UnqualElemTy == ElementType)
+    return SplitQualType(this, Qualifiers());
+
+  auto UnqualVecTy = Context.getDependentSizedExtVectorType(
+      UnqualElemTy, getSizeExpr(), getAttributeLoc());
+  return SplitQualType(UnqualVecTy.getTypePtr(), SplitElem.Quals);
+}
 
 void DependentSizedExtVectorType::Profile(llvm::FoldingSetNodeID &ID,
                                           const ASTContext &Context,
@@ -441,17 +464,34 @@ void DependentSizedMatrixType::Profile(llvm::FoldingSetNodeID &ID,
   ColumnExpr->Profile(ID, CTX, true);
 }
 
-VectorType::VectorType(QualType vecType, unsigned nElements, QualType canonType,
+VectorType::VectorType(const ASTContext &Context, QualType vecType,
+                       unsigned nElements, QualType canonType,
                        VectorKind vecKind)
-    : VectorType(Vector, vecType, nElements, canonType, vecKind) {}
+    : VectorType(Context, Vector, vecType, nElements, canonType, vecKind) {}
 
-VectorType::VectorType(TypeClass tc, QualType vecType, unsigned nElements,
-                       QualType canonType, VectorKind vecKind)
-    : Type(tc, canonType, vecType->getDependence()), ElementType(vecType) {
+VectorType::VectorType(const ASTContext &Context, TypeClass tc,
+                       QualType vecType, unsigned nElements, QualType canonType,
+                       VectorKind vecKind)
+    : Type(tc, canonType, vecType->getDependence()), ElementType(vecType),
+      Context(Context) {
   VectorTypeBits.VecKind = llvm::to_underlying(vecKind);
   VectorTypeBits.NumElements = nElements;
 }
 
+SplitQualType VectorType::getSplitUnqualifiedType() const {
+  auto SplitElem = ElementType.getSplitUnqualifiedType();
+  QualType UnqualElemTy(SplitElem.Ty, 0);
+  if (UnqualElemTy == ElementType)
+    return SplitQualType(this, Qualifiers());
+
+  auto UnqualVecTy =
+      getTypeClass() == ExtVector
+          ? Context.getExtVectorType(UnqualElemTy, getNumElements())
+          : Context.getVectorType(UnqualElemTy, getNumElements(),
+                                  getVectorKind());
+  return SplitQualType(UnqualVecTy.getTypePtr(), SplitElem.Quals);
+}
+
 bool Type::isPackedVectorBoolType(const ASTContext &ctx) const {
   if (ctx.getLangOpts().HLSL)
     return false;
@@ -628,6 +668,22 @@ SplitQualType QualType::getSplitUnqualifiedTypeImpl(QualType type) {
   }
 
 done:
+  // Vector types can have qualified element type. We need to recursively
+  // process the element type, and possibly construct a new vector type with the
+  // unqualified version of the element type.
+  SplitQualType SplitVec;
+  if (auto *VTy = dyn_cast<VectorType>(split.Ty))
+    SplitVec = VTy->getSplitUnqualifiedType();
+  else if (auto *DVTy = dyn_cast<DependentVectorType>(split.Ty))
+    SplitVec = DVTy->getSplitUnqualifiedType();
+  else if (auto *DSEVTy = dyn_cast<DependentSizedExtVectorType>(split.Ty))
+    SplitVec = DSEVTy->getSplitUnqualifiedType();
+
+  if (SplitVec.Ty) {
+    quals.addConsistentQualifiers(SplitVec.Quals);
+    return SplitQualType(SplitVec.Ty, quals);
+  }
+
   return SplitQualType(lastTypeWithQuals, quals);
 }
 
diff --git a/clang/test/Sema/types.c b/clang/test/Sema/types.c
index 2be0e6544f3d7..21ee2feabc965 100644
--- a/clang/test/Sema/types.c
+++ b/clang/test/Sema/types.c
@@ -75,6 +75,18 @@ typedef int __attribute__((ext_vector_type(0x100000000))) e2;      // expected-e
 typedef int __attribute__((vector_size((__int128_t)1 << 100))) e3; // expected-error {{vector size too large}}
 typedef int __attribute__((ext_vector_type(0))) e4;                // expected-error {{zero vector size}}
 
+typedef const int cint;
+typedef int __attribute__((vector_size(16))) vec4_int;
+typedef cint __attribute__((vector_size(16))) vec4_cint;
+_Static_assert(__builtin_types_compatible_p(vec4_int, const vec4_int), "'vec4_int' and 'const vec4_int' are not compatible");
+_Static_assert(__builtin_types_compatible_p(vec4_int, volatile vec4_int), "'vec4_int' and 'const vec4_int' are not compatible");
+_Static_assert(__builtin_types_compatible_p(const vec4_int, vec4_cint), "'const vec4_int' and 'vec4_cint' are not compatible");
+typedef int __attribute__((ext_vector_type(4))) ext_vec4_int;
+typedef cint __attribute__((ext_vector_type(4))) ext_vec4_cint;
+_Static_assert(__builtin_types_compatible_p(ext_vec4_int, const ext_vec4_int), "'ext_vec4_int' and 'const ext_vec4_int' are not compatible");
+_Static_assert(__builtin_types_compatible_p(ext_vec4_int, volatile ext_vec4_int), "'ext_vec4_int' and 'const ext_vec4_int' are not compatible");
+_Static_assert(__builtin_types_compatible_p(const ext_vec4_int, ext_vec4_cint), "'const ext_vec4_int' and 'ext_vec4_cint' are not compatible'");
+
 // no support for vector enum type
 enum { e_2 } x3 __attribute__((vector_size(64))); // expected-error {{invalid vector element type}}
 
diff --git a/clang/test/SemaCXX/vector.cpp b/clang/test/SemaCXX/vector.cpp
index 355d93a2b8cee..ef91848c7d31a 100644
--- a/clang/test/SemaCXX/vector.cpp
+++ b/clang/test/SemaCXX/vector.cpp
@@ -817,3 +817,17 @@ namespace GH173347 {
 typedef short __attribute__((__vector_size__(8))) V;
 template <int N> V test(V x) { return (x % 5) * N; }
 }
+
+namespace test_canonical_quals {
+typedef const int cint;
+typedef int __attribute__((vector_size(16))) vec4_int;
+typedef cint __attribute__((vector_size(16))) vec4_cint;
+_Static_assert(!__is_same(vec4_int, const vec4_int), "'vec4_int' and 'const vec4_int' are not different");
+_Static_assert(!__is_same(vec4_int, volatile vec4_int), "'vec4_int' and 'const vec4_int' are not different");
+_Static_assert(__is_same(const vec4_int, vec4_cint), "'const vec4_int' and 'vec4_cint' are different");
+typedef int __attribute__((ext_vector_type(4))) ext_vec4_int;
+typedef cint __attribute__((ext_vector_type(4))) ext_vec4_cint;
+_Static_assert(!__is_same(ext_vec4_int, const ext_vec4_int), "'ext_vec4_int' and 'const ext_vec4_int' are not different");
+_Static_assert(!__is_same(ext_vec4_int, volatile ext_vec4_int), "'ext_vec4_int' and 'const ext_vec4_int' are not different");
+_Static_assert(__is_same(const ext_vec4_int, ext_vec4_cint), "'const ext_vec4_int' and 'ext_vec4_cint' are different");
+} // namespace test_canonical_quals

>From 76e3dafca51cc268bd8c1bceca189391716b7503 Mon Sep 17 00:00:00 2001
From: Michele Scandale <michele.scandale at gmail.com>
Date: Thu, 25 Jun 2026 13:18:15 -0700
Subject: [PATCH 2/2] [libcxx] Drop qualifiers on element type for
 `__simd_vector<T, N>`.

---
 libcxx/include/__algorithm/simd_utils.h       | 20 +++++++++++--------
 .../include/__cxx03/__algorithm/simd_utils.h  | 16 +++++++++------
 2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index 495aed4e21216..dcd6daf6377be 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -15,6 +15,7 @@
 #include <__bit/countr.h>
 #include <__config>
 #include <__cstddef/size_t.h>
+#include <__type_traits/remove_cv.h>
 #include <__utility/integer_sequence.h>
 #include <cstdint>
 
@@ -88,7 +89,10 @@ inline constexpr size_t __native_vector_size = 1;
 #  endif
 
 template <class _ArithmeticT, size_t _Np>
-using __simd_vector __attribute__((__ext_vector_type__(_Np))) _LIBCPP_NODEBUG = _ArithmeticT;
+using __simd_vector_impl __attribute__((__ext_vector_type__(_Np))) _LIBCPP_NODEBUG = _ArithmeticT;
+
+template <class _ArithmeticT, size_t _Np>
+using __simd_vector _LIBCPP_NODEBUG = __simd_vector_impl<__remove_cv_t<_ArithmeticT>, _Np>;
 
 _LIBCPP_DIAGNOSTIC_PUSH
 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
@@ -99,10 +103,10 @@ inline constexpr size_t __simd_vector_size_v = []<bool _False = false>() -> size
 }();
 
 template <class _Tp, size_t _Np>
-inline constexpr size_t __simd_vector_size_v<__simd_vector<_Tp, _Np>> = _Np;
+inline constexpr size_t __simd_vector_size_v<__simd_vector_impl<_Tp, _Np>> = _Np;
 
 template <class _Tp, size_t _Np>
-_LIBCPP_HIDE_FROM_ABI _Tp __simd_vector_underlying_type_impl(__simd_vector<_Tp, _Np>) {
+_LIBCPP_HIDE_FROM_ABI _Tp __simd_vector_underlying_type_impl(__simd_vector_impl<_Tp, _Np>) {
   return _Tp{};
 }
 
@@ -127,22 +131,22 @@ template <class _VecT, size_t _Np, class _Iter>
 }
 
 template <class _Tp, size_t _Np>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __any_of(__simd_vector<_Tp, _Np> __vec) noexcept {
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __any_of(__simd_vector_impl<_Tp, _Np> __vec) noexcept {
   return __builtin_reduce_or(__builtin_convertvector(__vec, __simd_vector<bool, _Np>));
 }
 
 template <class _Tp, size_t _Np>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __all_of(__simd_vector<_Tp, _Np> __vec) noexcept {
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __all_of(__simd_vector_impl<_Tp, _Np> __vec) noexcept {
   return __builtin_reduce_and(__builtin_convertvector(__vec, __simd_vector<bool, _Np>));
 }
 
 template <class _Tp, size_t _Np>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __none_of(__simd_vector<_Tp, _Np> __vec) noexcept {
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __none_of(__simd_vector_impl<_Tp, _Np> __vec) noexcept {
   return !__builtin_reduce_or(__builtin_convertvector(__vec, __simd_vector<bool, _Np>));
 }
 
 template <class _Tp, size_t _Np>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t __find_first_set(__simd_vector<_Tp, _Np> __vec) noexcept {
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t __find_first_set(__simd_vector_impl<_Tp, _Np> __vec) noexcept {
   using __mask_vec = __simd_vector<bool, _Np>;
 
   // This has MSan disabled du to https://llvm.org/PR85876
@@ -171,7 +175,7 @@ template <class _Tp, size_t _Np>
 }
 
 template <class _Tp, size_t _Np>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t __find_first_not_set(__simd_vector<_Tp, _Np> __vec) noexcept {
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t __find_first_not_set(__simd_vector_impl<_Tp, _Np> __vec) noexcept {
   return std::__find_first_set(~__vec);
 }
 _LIBCPP_DIAGNOSTIC_POP
diff --git a/libcxx/include/__cxx03/__algorithm/simd_utils.h b/libcxx/include/__cxx03/__algorithm/simd_utils.h
index 7b0e825afaa1f..7758aee7e74bd 100644
--- a/libcxx/include/__cxx03/__algorithm/simd_utils.h
+++ b/libcxx/include/__cxx03/__algorithm/simd_utils.h
@@ -15,6 +15,7 @@
 #include <__cxx03/__config>
 #include <__cxx03/__type_traits/is_arithmetic.h>
 #include <__cxx03/__type_traits/is_same.h>
+#include <__cxx03/__type_traits/remove_cv.h>
 #include <__cxx03/__utility/integer_sequence.h>
 #include <__cxx03/cstddef>
 #include <__cxx03/cstdint>
@@ -85,7 +86,10 @@ inline constexpr size_t __native_vector_size = 1;
 #  endif
 
 template <class _ArithmeticT, size_t _Np>
-using __simd_vector __attribute__((__ext_vector_type__(_Np))) = _ArithmeticT;
+using __simd_vector_impl __attribute__((__ext_vector_type__(_Np))) = _ArithmeticT;
+
+template <class _ArithmeticT, size_t _Np>
+using __simd_vector = __simd_vector_impl<__remove_cv_t<_ArithmeticT>, _Np>;
 
 template <class _VecT>
 inline constexpr size_t __simd_vector_size_v = []<bool _False = false>() -> size_t {
@@ -93,10 +97,10 @@ inline constexpr size_t __simd_vector_size_v = []<bool _False = false>() -> size
 }();
 
 template <class _Tp, size_t _Np>
-inline constexpr size_t __simd_vector_size_v<__simd_vector<_Tp, _Np>> = _Np;
+inline constexpr size_t __simd_vector_size_v<__simd_vector_impl<_Tp, _Np>> = _Np;
 
 template <class _Tp, size_t _Np>
-_LIBCPP_HIDE_FROM_ABI _Tp __simd_vector_underlying_type_impl(__simd_vector<_Tp, _Np>) {
+_LIBCPP_HIDE_FROM_ABI _Tp __simd_vector_underlying_type_impl(__simd_vector_impl<_Tp, _Np>) {
   return _Tp{};
 }
 
@@ -112,12 +116,12 @@ _LIBCPP_NODISCARD _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _VecT __load_vecto
 }
 
 template <class _Tp, size_t _Np>
-_LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI bool __all_of(__simd_vector<_Tp, _Np> __vec) noexcept {
+_LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI bool __all_of(__simd_vector_impl<_Tp, _Np> __vec) noexcept {
   return __builtin_reduce_and(__builtin_convertvector(__vec, __simd_vector<bool, _Np>));
 }
 
 template <class _Tp, size_t _Np>
-_LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI size_t __find_first_set(__simd_vector<_Tp, _Np> __vec) noexcept {
+_LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI size_t __find_first_set(__simd_vector_impl<_Tp, _Np> __vec) noexcept {
   using __mask_vec = __simd_vector<bool, _Np>;
 
   // This has MSan disabled du to https://github.com/llvm/llvm-project/issues/85876
@@ -146,7 +150,7 @@ _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI size_t __find_first_set(__simd_vector<_T
 }
 
 template <class _Tp, size_t _Np>
-_LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI size_t __find_first_not_set(__simd_vector<_Tp, _Np> __vec) noexcept {
+_LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI size_t __find_first_not_set(__simd_vector_impl<_Tp, _Np> __vec) noexcept {
   return std::__find_first_set(~__vec);
 }
 


From libcxx-commits at lists.llvm.org  Thu Jul  9 00:51:24 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Thu, 09 Jul 2026 00:51:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4f52fc.58bdd12c.19c81.f4e0@mx.google.com>


https://github.com/Zingam approved this pull request.


https://github.com/llvm/llvm-project/pull/208036

From libcxx-commits at lists.llvm.org  Thu Jul  9 00:51:30 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Thu, 09 Jul 2026 00:51:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG4276 as complete (PR
	#208273)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208273@github.com>
Message-ID: <6a4f5302.f8b79514.1d52d0.278a@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/208273

From libcxx-commits at lists.llvm.org  Thu Jul  9 00:52:05 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 00:52:05 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
Message-ID: <llvm.org/llvm/llvm-project/pull/208390@github.com>


https://github.com/huihui15537226823 created https://github.com/llvm/llvm-project/pull/208390

## Summary

This patch adds OHOS AArch64 support for running the libc++ runtimes test
suite, including libc++, libc++abi, and libunwind.

The main pieces are:

- Add OHOS-specific lit configurations for libc++, libc++abi, and libunwind.
- Add an OHOS executor path for running tests on device through HDC.
- Add an OHOS QEMU user-mode executor, which is easier to use for local and CI
  testing when physical OHOS devices are not available.
- Configure OHOS libc++ builds to use the `__h` ABI namespace by default
  through `LIBCXX_ABI_NAMESPACE=__h`.
- Add `ohos-qemu` / `ohos-hdc` lit features so tests can distinguish QEMU
  user-mode from real-device execution.
- Mark a small number of tests unsupported under `ohos-qemu` where QEMU
  user-mode does not match real OHOS device behavior, such as `/proc`, signal
  unwinding, or very thread-heavy stress tests. These tests remain available for
  HDC/device testing.

## Motivation

This makes it possible to continuously validate OHOS libc++ runtimes support
inside the upstream libc++ CI structure.

The QEMU path is the recommended default for general CI usage because it does
not require dedicated phone/device infrastructure. The HDC path is still
supported for real-device validation when an OHOS device is available.

## Sysroot Requirement

The OHOS QEMU sysroot is expected to be provided by the local machine or CI
agent and passed through `--ohos-qemu-sysroot` or `OHOS_QEMU_SYSROOT`. It is not
vendored into the LLVM source tree.

## Local Testing

A typical full OHOS AArch64 QEMU test run can be launched with:

```bash
cd /home/cdx/hwasan/llvm/llvm-project

unset HDC HDC_UTID HDC_TIMEOUT_SECONDS

export OHOS_SYSROOT=/home/xxx/sysroot/aarch64-linux-ohos
export CC=/home/xxx/llvm/out/llvm23-ohos-toolchain-install/bin/clang
export CXX=/home/xxx/llvm/out/llvm23-ohos-toolchain-install/bin/clang++
export OHOS_LIBCXX_ABI_NAMESPACE=__h

libcxx/utils/ci/run-buildbot \
  --ohos-qemu /usr/bin/qemu-aarch64 \
  --ohos-qemu-sysroot /home/xxx/llvm/out/qemu-sysroot-aarch64-linux-ohos \
  --build-dir /home/xxx/oh1/builds/llvm-ohos-qemu-full-habi \
  ohos-aarch64-full \
  2>&1 | tee /home/xxx/oh1/logs/7.9.log
```

The HDC/device path can also be used by providing `HDC`/device environment
variables and running the `ohos-hdc-aarch64-full` builder.

## Notes

The QEMU executor is intended to provide a convenient and reproducible CI mode.
Some tests are intentionally disabled only for `ohos-qemu` when the failure is
caused by QEMU user-mode behavior rather than libc++/libc++abi/libunwind
correctness on a real OHOS device.


>From 70e8ac839667f8aba4aa74a2dd19614426f9754f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 24 Jun 2026 09:47:00 +0800
Subject: [PATCH 1/6] Add OHOS HDC runtimes CI support

---
 cmake/Modules/GetToolchainDirs.cmake          |  37 +-
 .../cmake/Modules/CompilerRTUtils.cmake       |   8 +-
 compiler-rt/cmake/builtin-config-ix.cmake     |   2 +-
 compiler-rt/cmake/crt-config-ix.cmake         |   2 +-
 compiler-rt/lib/builtins/crtbegin.c           |   9 +
 compiler-rt/lib/builtins/emutls.c             |   8 +-
 libcxx/CMakeLists.txt                         |  10 +-
 libcxx/cmake/config-ix.cmake                  |   2 +
 libcxx/include/__config                       |   5 +-
 libcxx/include/__cxx03/__config               |   5 +-
 .../__cxx03/__locale_dir/locale_base_api.h    |   2 +-
 libcxx/include/__locale_dir/locale_base_api.h |   2 +-
 libcxx/src/include/config_elast.h             |   2 +
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  48 ++
 libcxx/utils/ci/buildkite-pipeline.yml        |  27 +
 libcxx/utils/ci/run-buildbot                  |  87 ++++
 libcxx/utils/ci/vendor/ohos/hdc_run.py        | 472 ++++++++++++++++++
 libcxx/utils/ci/vendor/ohos/hdc_run_test.py   | 115 +++++
 libcxxabi/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |  30 ++
 libunwind/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |  55 ++
 22 files changed, 910 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/test/configs/llvm-libc++-ohos.cfg.in
 create mode 100755 libcxx/utils/ci/vendor/ohos/hdc_run.py
 create mode 100644 libcxx/utils/ci/vendor/ohos/hdc_run_test.py
 create mode 100644 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
 create mode 100644 libunwind/test/configs/llvm-libunwind-ohos.cfg.in

diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..a20ed9ce9c6a9
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,48 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index d4107cf11ad51..77617f43ae6dc 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -172,3 +172,30 @@ steps:
       queue: libcxx-builders
       os: android
     <<: *common
+
+- group: OHOS
+  steps:
+  - label: OHOS AArch64 HDC Smoke
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
+  - label: OHOS AArch64 HDC Full
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 6376bc1d3dd5a..1867e9c620649 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -116,6 +116,41 @@ function clean() {
     rm -rf "${BUILD_DIR}"
 }
 
+function require-env-var() {
+    local name="${1}"
+    if [[ -z ${!name+x} ]] || [[ -z "${!name}" ]]; then
+        error "Environment variable ${name} must be defined"
+        exit 1
+    fi
+}
+
+function detect-lit() {
+    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
+        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
+    elif command -v llvm-lit >/dev/null 2>&1; then
+        LIT_RUNNER=("$(command -v llvm-lit)")
+    else
+        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
+    fi
+}
+
+function run-smoke-test() {
+    local test_dir="${1}"
+    local filter="${2}"
+    step "Running smoke test ${filter}"
+    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
+}
+
+function ohos-lit-args() {
+    local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
+    if python3 -c 'import psutil' >/dev/null 2>&1; then
+        args="${args} --timeout=1500"
+    else
+        echo "warning: python3 psutil not available; running OHOS lit without per-test timeout" >&2
+    fi
+    printf '%s' "${args}"
+}
+
 function generate-cmake-base() {
     step "Generating CMake"
 
@@ -153,6 +188,37 @@ function generate-cmake-android() {
           "${@}"
 }
 
+function setup-ohos-hdc-env() {
+    require-env-var OHOS_SYSROOT
+
+    OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+}
+
+function generate-cmake-ohos-hdc() {
+    setup-ohos-hdc-env
+
+    local lit_args
+    lit_args="$(ohos-lit-args)"
+
+    generate-cmake \
+          -DLLVM_LIT_ARGS="${lit_args}" \
+          -DCMAKE_SYSTEM_NAME=OHOS \
+          -DCMAKE_SYSROOT="${OHOS_SYSROOT}" \
+          -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
+          -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
+          -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
+          -DLIBUNWIND_TEST_CONFIG="llvm-libunwind-ohos.cfg.in" \
+          -DLIBCXX_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBCXXABI_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBUNWIND_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          "${@}"
+}
+
 function check-runtimes() {
     step "Building libc++ test dependencies"
     ninja -vC "${BUILD_DIR}" cxx-test-depends
@@ -795,6 +861,27 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
+ohos-hdc-aarch64-full)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    check-runtimes
+;;
+ohos-hdc-aarch64-smoke)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    step "Building the OHOS smoke test dependencies"
+    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
+
+    detect-lit
+    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
+;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run.py b/libcxx/utils/ci/vendor/ohos/hdc_run.py
new file mode 100755
index 0000000000000..61a3899b88b11
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run.py
@@ -0,0 +1,472 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import uuid
+
+
+EXIT_MARKER = "__llvm_hdc_exit__="
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _split_path_list(value):
+    return [entry for entry in value.split(os.pathsep) if entry]
+
+
+def _resolve_hdc_binary(hdc):
+    if os.path.isabs(hdc):
+        return hdc
+    resolved = shutil.which(hdc)
+    if resolved is None:
+        sys.exit(f"failed to find hdc binary: {hdc}")
+    return resolved
+
+
+def _is_windows_hdc(path):
+    return path.lower().endswith(".exe")
+
+
+def _find_windows_local_root(hdc_path):
+    parts = Path(hdc_path).parts
+    for index in range(len(parts) - 1):
+        if parts[index] == "AppData" and index + 1 < len(parts) and parts[index + 1] == "Local":
+            return Path(*parts[: index + 2])
+    return None
+
+
+def _default_host_staging_root(hdc_path):
+    if _is_windows_hdc(hdc_path):
+        local_root = _find_windows_local_root(hdc_path)
+        if local_root is None:
+            sys.exit(
+                "failed to derive a Windows staging directory from the hdc.exe path; "
+                "set HDC_HOST_STAGING_ROOT to a path under /mnt/<drive>/..."
+            )
+        return local_root / "Temp" / "llvm-lit-hdc"
+    return Path("/tmp/llvm-lit-hdc")
+
+
+def _to_windows_path(path):
+    resolved = Path(path).resolve()
+    parts = resolved.parts
+    if len(parts) < 4 or parts[1] != "mnt":
+        sys.exit(
+            f"Windows hdc requires a staging path under /mnt/<drive>/..., got: {resolved}"
+        )
+    drive = parts[2].upper()
+    tail = "\\".join(parts[3:])
+    return f"{drive}:\\{tail}" if tail else f"{drive}:\\"
+
+
+def _run_readelf(path):
+    completed = subprocess.run(
+        ["readelf", "-d", path],
+        check=False,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        text=True,
+    )
+    if completed.returncode != 0:
+        return ""
+    return completed.stdout
+
+
+def _read_elf_metadata(path):
+    needed = []
+    runpath_dirs = []
+    seen_dirs = set()
+    for line in _run_readelf(path).splitlines():
+        needed_match = re.search(r"\(NEEDED\).*Shared library: \[(.*)\]", line)
+        if needed_match:
+            needed.append(needed_match.group(1))
+            continue
+
+        runpath_match = re.search(
+            r"\((?:RUNPATH|RPATH)\).*Library runpath: \[(.*)\]", line
+        )
+        if not runpath_match:
+            continue
+        for entry in _split_path_list(runpath_match.group(1)):
+            candidate = Path(entry)
+            if not candidate.is_dir():
+                continue
+            key = str(candidate.resolve())
+            if key in seen_dirs:
+                continue
+            seen_dirs.add(key)
+            runpath_dirs.append(candidate)
+    return needed, runpath_dirs
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(parent)
+    return library_dirs
+
+
+def _find_dependency(name, search_dirs):
+    for directory in search_dirs:
+        candidate = Path(directory) / name
+        if candidate.is_file():
+            return candidate.resolve()
+    return None
+
+
+def _copy_needed_libraries(executables, execdir, staging_execdir):
+    search_dirs = _collect_build_library_dirs(execdir)
+    queue = [Path(exe).resolve() for exe in executables]
+    visited = set()
+    copied_names = {path.name for path in staging_execdir.glob("*") if path.is_file()}
+
+    while queue:
+        current = queue.pop(0)
+        current_key = str(current)
+        if current_key in visited:
+            continue
+        visited.add(current_key)
+
+        needed, runpath_dirs = _read_elf_metadata(current)
+        current_search_dirs = []
+        current_seen = set()
+        for directory in [*runpath_dirs, current.parent, *search_dirs]:
+            resolved = Path(directory).resolve()
+            key = str(resolved)
+            if key in current_seen:
+                continue
+            current_seen.add(key)
+            current_search_dirs.append(resolved)
+
+        for directory in runpath_dirs:
+            resolved = directory.resolve()
+            if all(str(resolved) != str(existing) for existing in search_dirs):
+                search_dirs.append(resolved)
+
+        for lib_name in needed:
+            dependency = _find_dependency(lib_name, current_search_dirs)
+            if dependency is None:
+                continue
+            if lib_name not in copied_names:
+                shutil.copy2(dependency, staging_execdir / lib_name)
+                copied_names.add(lib_name)
+            queue.append(dependency)
+
+
+def _copy_execdir(execdir, staging_execdir):
+    if staging_execdir.exists():
+        shutil.rmtree(staging_execdir)
+    shutil.copytree(execdir, staging_execdir)
+
+
+def _iter_files(root):
+    for directory, dirnames, filenames in os.walk(root):
+        dirnames.sort()
+        filenames.sort()
+        yield Path(directory), filenames
+
+
+def _is_test_executable(path):
+    return path.endswith(".tmp.exe") and os.path.exists(path)
+
+
+def _map_local_path(path, local_execdir, remote_execdir):
+    normalized = os.path.normpath(path)
+    execdir = os.path.normpath(local_execdir)
+    if normalized == execdir:
+        return remote_execdir
+    prefix = execdir + os.sep
+    if normalized.startswith(prefix):
+        suffix = normalized[len(prefix) :].replace(os.sep, "/")
+        return f"{remote_execdir}/{suffix}" if suffix else remote_execdir
+    return path
+
+
+def _map_value(value, local_execdir, remote_execdir):
+    return _map_local_path(value, local_execdir, remote_execdir)
+
+
+def _build_env_exports(env, prepend_env, local_execdir, remote_execdir, add_remote_libdir):
+    commands = []
+    mapped_env = {
+        key: _map_value(value, local_execdir, remote_execdir) for key, value in env.items()
+    }
+    mapped_prepend = {
+        key: _map_value(value, local_execdir, remote_execdir)
+        for key, value in prepend_env.items()
+    }
+
+    if add_remote_libdir:
+        existing = mapped_prepend.get("LD_LIBRARY_PATH")
+        mapped_prepend["LD_LIBRARY_PATH"] = (
+            f"{remote_execdir}:{existing}" if existing else remote_execdir
+        )
+
+    for key, value in mapped_env.items():
+        commands.append(f"export {key}={shlex.quote(value)}")
+    for key, value in mapped_prepend.items():
+        commands.append(f"export {key}={shlex.quote(value)}${{{key}:+:{key}}}")
+    return commands
+
+
+def _hdc_prefix(hdc_path, server, target):
+    prefix = [hdc_path]
+    if server:
+        prefix.extend(["-s", server])
+    if target:
+        prefix.extend(["-t", target])
+    return prefix
+
+
+def _run_hdc(
+    prefix,
+    args,
+    *,
+    timeout=None,
+    capture_output=False,
+    stdin=subprocess.DEVNULL,
+    check=True,
+    expect_stdout=None,
+):
+    kwargs = {
+        "check": False,
+        "timeout": timeout,
+        "stdin": stdin,
+        "text": True,
+    }
+    if capture_output:
+        kwargs["stdout"] = subprocess.PIPE
+        kwargs["stderr"] = subprocess.PIPE
+    completed = subprocess.run(prefix + args, **kwargs)
+    output = completed.stdout if capture_output and completed.stdout else ""
+    error_output = completed.stderr if capture_output and completed.stderr else ""
+    if check and completed.returncode != 0:
+        raise RuntimeError(
+            f"hdc command failed with exit code {completed.returncode}: "
+            f"{shlex.join(prefix + args)}\n{output}{error_output}"
+        )
+    if check and expect_stdout is not None and expect_stdout not in output:
+        raise RuntimeError(
+            f"hdc command did not report success: {shlex.join(prefix + args)}\n{output}"
+        )
+    return completed
+
+
+def _run_checked_shell(prefix, command, *, timeout=None):
+    completed = _run_hdc(
+        prefix,
+        [
+            "shell",
+            f"{command}; rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"",
+        ],
+        timeout=timeout,
+        capture_output=True,
+    )
+    exit_code, output = _extract_exit_code(completed.stdout)
+    if exit_code is None:
+        raise RuntimeError(
+            f"failed to parse shell exit code for: {command}\n{completed.stdout}"
+        )
+    if exit_code != 0:
+        raise RuntimeError(
+            f"remote shell command failed with exit code {exit_code}: {command}\n{output}"
+        )
+    return output
+
+
+def _sync_tree_to_device(prefix, local_root, remote_root, use_windows_paths):
+    _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_root)}")
+    _run_checked_shell(prefix, f"mkdir -p {shlex.quote(remote_root)}")
+
+    for directory, filenames in _iter_files(local_root):
+        relative_dir = os.path.relpath(directory, local_root)
+        device_dir = (
+            remote_root
+            if relative_dir == "."
+            else f"{remote_root}/{relative_dir.replace(os.sep, '/')}"
+        )
+        _run_checked_shell(prefix, f"mkdir -p {shlex.quote(device_dir)}")
+        for name in filenames:
+            local_path = directory / name
+            device_path = f"{device_dir}/{name}"
+            source = _to_windows_path(local_path) if use_windows_paths else str(local_path)
+            _run_hdc(
+                prefix,
+                ["file", "send", source, device_path],
+                capture_output=True,
+                expect_stdout="FileTransfer finish",
+            )
+            if name.endswith(".tmp.exe") or os.access(local_path, os.X_OK):
+                _run_checked_shell(prefix, f"chmod +x {shlex.quote(device_path)}")
+
+
+def _build_remote_command(command, local_execdir, remote_execdir, exports):
+    mapped_command = [
+        _map_local_path(arg, local_execdir, remote_execdir) for arg in command
+    ]
+    pieces = [f"cd {shlex.quote(remote_execdir)}"]
+    pieces.extend(exports)
+    pieces.append(shlex.join(mapped_command))
+    pieces.append(f"rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"")
+    return " && ".join(pieces[:-2]) + f"; {pieces[-2]}; {pieces[-1]}"
+
+
+def _extract_exit_code(output):
+    match = re.search(rf"^(.*)\n{EXIT_MARKER}(\d+)\s*$", output, re.DOTALL)
+    if not match:
+        return None, output
+    return int(match.group(2)), match.group(1)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--hdc", default=os.environ.get("HDC", "hdc"))
+    parser.add_argument("--hdc-server", default=os.environ.get("HDC_SERVER_IP_PORT"))
+    parser.add_argument("--hdc-target", default=os.environ.get("HDC_UTID"))
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--remote-base-dir",
+        default="/data/local/tmp/llvm-lit-hdc",
+    )
+    parser.add_argument(
+        "--host-staging-root",
+        default=os.environ.get("HDC_HOST_STAGING_ROOT"),
+    )
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(os.environ.get("HDC_TIMEOUT_SECONDS", "120")),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("--keep-remote-dir", action="store_true")
+    parser.add_argument("--keep-host-staging", action="store_true")
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    hdc_path = _resolve_hdc_binary(args.hdc)
+    use_windows_paths = _is_windows_hdc(hdc_path)
+    host_staging_root = (
+        Path(args.host_staging_root)
+        if args.host_staging_root
+        else _default_host_staging_root(hdc_path)
+    )
+    prefix = _hdc_prefix(hdc_path, args.hdc_server, args.hdc_target)
+
+    execdir = Path(args.execdir).resolve()
+    run_id = uuid.uuid4().hex[:12]
+    staging_execdir = host_staging_root / f"run-{run_id}" / execdir.name
+    remote_execdir = f"{args.remote_base_dir}/run-{run_id}/{execdir.name}"
+
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    executables = [arg for arg in args.command if _is_test_executable(arg)]
+    host_staging_root.mkdir(parents=True, exist_ok=True)
+    _copy_execdir(execdir, staging_execdir)
+    _copy_needed_libraries(executables, execdir, staging_execdir)
+
+    add_remote_libdir = any(staging_execdir.glob("*.so*"))
+    exports = _build_env_exports(
+        env,
+        prepend_env,
+        str(execdir),
+        remote_execdir,
+        add_remote_libdir,
+    )
+
+    try:
+        _sync_tree_to_device(prefix, staging_execdir, remote_execdir, use_windows_paths)
+        remote_command = _build_remote_command(
+            args.command,
+            str(execdir),
+            remote_execdir,
+            exports,
+        )
+        try:
+            completed = _run_hdc(
+                prefix,
+                ["shell", remote_command],
+                timeout=args.timeout_seconds,
+                capture_output=True,
+                stdin=None,
+            )
+        except subprocess.TimeoutExpired:
+            print(
+                f"hdc executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+                file=sys.stderr,
+            )
+            return 124
+
+        exit_code, output = _extract_exit_code(completed.stdout)
+        if exit_code is None:
+            print(
+                "failed to parse exit code from hdc shell output",
+                file=sys.stderr,
+            )
+            if completed.stdout:
+                print(completed.stdout, end="", file=sys.stderr)
+            if completed.stderr:
+                print(completed.stderr, end="", file=sys.stderr)
+            return 1
+
+        if output:
+            sys.stdout.write(output)
+        if completed.stderr:
+            sys.stderr.write(completed.stderr)
+        return exit_code
+    finally:
+        if not args.keep_remote_dir:
+            try:
+                _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_execdir)}")
+            except Exception:
+                pass
+        if not args.keep_host_staging:
+            shutil.rmtree(staging_execdir.parent, ignore_errors=True)
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run_test.py b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
new file mode 100644
index 0000000000000..17e2f65bd1fb6
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import os
+from pathlib import Path
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import unittest
+
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+HDC_RUN = SCRIPT_DIR / "hdc_run.py"
+
+
+class HdcRunTest(unittest.TestCase):
+    def make_fake_hdc(self, directory):
+        fake_hdc = directory / "hdc"
+        fake_hdc.write_text(
+            textwrap.dedent(
+                """\
+                #!/usr/bin/env python3
+                import sys
+
+                EXIT_MARKER = "__llvm_hdc_exit__="
+
+                def main():
+                    args = sys.argv[1:]
+                    if args[:2] == ["file", "send"]:
+                        print("FileTransfer finish")
+                        return 0
+
+                    if args and args[0] == "shell":
+                        command = args[1]
+                        if "stderr.tmp.exe" in command:
+                            sys.stdout.write("STDOUT-OUTPUT")
+                            sys.stderr.write("STDERR-OUTPUT")
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        if "stdin.tmp.exe" in command:
+                            sys.stdout.write("IN:" + sys.stdin.read())
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                        return 0
+
+                    print("unexpected fake hdc invocation: " + repr(args), file=sys.stderr)
+                    return 2
+
+                if __name__ == "__main__":
+                    raise SystemExit(main())
+                """
+            )
+        )
+        fake_hdc.chmod(fake_hdc.stat().st_mode | stat.S_IXUSR)
+        return fake_hdc
+
+    def run_hdc_run(self, temp_dir, executable_name, *, stdin_text=None):
+        execdir = temp_dir / "execdir"
+        execdir.mkdir()
+        executable = execdir / executable_name
+        executable.write_text("# fake executable marker\n")
+        executable.chmod(executable.stat().st_mode | stat.S_IXUSR)
+
+        fake_hdc = self.make_fake_hdc(temp_dir)
+        return subprocess.run(
+            [
+                sys.executable,
+                str(HDC_RUN),
+                "--hdc",
+                str(fake_hdc),
+                "--execdir",
+                str(execdir),
+                "--host-staging-root",
+                str(temp_dir / "staging"),
+                str(executable),
+            ],
+            input=stdin_text,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+    def test_preserves_remote_stdout_and_stderr_separately(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(Path(temp), "stderr.tmp.exe")
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "STDOUT-OUTPUT")
+        self.assertEqual(completed.stderr, "STDERR-OUTPUT")
+
+    def test_forwards_stdin_to_remote_command(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(
+                Path(temp),
+                "stdin.tmp.exe",
+                stdin_text="hello from stdin",
+            )
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "IN:hello from stdin")
+        self.assertEqual(completed.stderr, "")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt
index e1a1587fb6283..cb22e6fe65108 100644
--- a/libcxxabi/CMakeLists.txt
+++ b/libcxxabi/CMakeLists.txt
@@ -187,7 +187,12 @@ set(LIBCXXABI_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXXABI_TARGET_SUBDIR)
+  else()
+    set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXXABI_LIBDIR_SUBDIR)
     string(APPEND LIBCXXABI_TARGET_SUBDIR /${LIBCXXABI_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
new file mode 100644
index 0000000000000..d18748fe3a3cc
--- /dev/null
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -0,0 +1,30 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++abi using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -I %{libcxx}/test/support -I %{libcxx}/src -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    '-nostdlib++ -L %{lib} -lc++ -lc++abi -pthread'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 02132f6c07fcd..4fd11bf5aca2f 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -142,7 +142,12 @@ set(LIBUNWIND_STATIC_OUTPUT_NAME "unwind" CACHE STRING "Output name for the stat
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBUNWIND_TARGET_SUBDIR)
+  else()
+    set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBUNWIND_LIBDIR_SUBDIR)
     string(APPEND LIBUNWIND_TARGET_SUBDIR /${LIBUNWIND_LIBDIR_SUBDIR})
   endif()
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
new file mode 100644
index 0000000000000..bce241a7898cf
--- /dev/null
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -0,0 +1,55 @@
+#
+# Configuration file for running the libunwind tests against the shared library
+# using HDC on OHOS.
+#
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+compile_flags = []
+link_flags = []
+
+if @HAVE_GETAUXVAL@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_GETAUXVAL')
+
+if @HAVE_ELF_AUX_INFO@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_ELF_AUX_INFO')
+
+if @LIBUNWIND_ENABLE_CET@:
+    compile_flags.append('-fcf-protection=full')
+
+if @LIBUNWIND_ENABLE_GCS@:
+    compile_flags.append('-mbranch-protection=standard')
+
+if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
+    link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
+
+if '@CMAKE_DL_LIBS@':
+    link_flags.append('-l at CMAKE_DL_LIBS@')
+
+compile_flags.append('-funwind-tables')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags))
+))
+config.substitutions.append(('%{link_flags}',
+    '-L %{{lib}} -lunwind {}'.format(' '.join(link_flags))
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)

>From e9e5d2e517fb072f3554798e49b7119ec739105f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 25 Jun 2026 11:30:38 +0800
Subject: [PATCH 2/6] Fix OHOS libc++ CI ABI and SME detection

---
 libcxx/utils/ci/run-buildbot |  2 ++
 libunwind/src/Registers.hpp  | 15 +++++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 1867e9c620649..ecf0ca17ae148 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -192,6 +192,7 @@ function setup-ohos-hdc-env() {
     require-env-var OHOS_SYSROOT
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
     OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
     OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
 }
@@ -209,6 +210,7 @@ function generate-cmake-ohos-hdc() {
           -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE}" \
           -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
           -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
           -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c88422a6d5f4f..18cb2a523bd0e 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -25,6 +25,11 @@
 #endif
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
+#if defined(__has_include)
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
+#endif
 #endif
 
 namespace libunwind {
@@ -1951,18 +1956,16 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
       return false;
     return has_sme != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = getauxval(AT_HWCAP2);
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = 0;
     elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
 #else
   static bool checkHasSME() {

>From 498fa8d127ff49c27d4230996338c6eb5d747fe6 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 1 Jul 2026 15:49:11 +0800
Subject: [PATCH 3/6] Stabilize OHOS HDC runtimes tests

Add OHOS lit features for HDC executor and platform limitations, mark affected libc++ tests as unsupported or expected failures, and adjust test support for OHOS filesystem cleanup and allocation counting behavior.

Also guard the aarch64 ZA unwind test on HWCAP2_SME so devices without a reliable SME feature definition do not execute SME instructions.
---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  9 +++++-
 .../clang/clang_modules_include.gen.py        |  3 ++
 .../locale.facet/no_allocation.pass.cpp       |  1 +
 libcxx/test/selftest/dsl/dsl.sh.py            |  5 ++++
 libcxx/test/selftest/stdin-is-piped.sh.cpp    |  1 +
 .../directory_entry.cons/path.pass.cpp        |  1 +
 .../directory_entry.mods/assign.pass.cpp      |  1 +
 .../directory_entry.mods/refresh.pass.cpp     |  1 +
 .../replace_filename.pass.cpp                 |  1 +
 .../directory_entry.obs/file_size.pass.cpp    |  1 +
 .../file_type_obs.pass.cpp                    |  1 +
 .../hard_link_count.pass.cpp                  |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../directory_iterator.members/ctor.pass.cpp  |  1 +
 .../path.member/path.assign/move.pass.cpp     |  2 +-
 .../path.member/path.construct/move.pass.cpp  |  2 +-
 .../rec.dir.itr.members/ctor.pass.cpp         |  1 +
 .../rec.dir.itr.members/increment.pass.cpp    |  1 +
 .../fs.op.funcs/fs.op.exists/exists.pass.cpp  |  1 +
 .../is_block_file.pass.cpp                    |  1 +
 .../is_character_file.pass.cpp                |  1 +
 .../fs.op.is_directory/is_directory.pass.cpp  |  1 +
 .../fs.op.is_empty/is_empty.pass.cpp          |  1 +
 .../fs.op.is_fifo/is_fifo.pass.cpp            |  1 +
 .../fs.op.is_other/is_other.pass.cpp          |  1 +
 .../is_regular_file.pass.cpp                  |  1 +
 .../fs.op.is_socket/is_socket.pass.cpp        |  1 +
 .../fs.op.is_symlink/is_symlink.pass.cpp      |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../fs.op.funcs/fs.op.remove/remove.pass.cpp  |  1 +
 .../fs.op.remove_all/remove_all.pass.cpp      |  1 +
 .../fs.op.funcs/fs.op.status/status.pass.cpp  |  1 +
 .../symlink_status.pass.cpp                   |  1 +
 .../temp_directory_path.pass.cpp              |  1 +
 .../ext.manip/get_money.pass.cpp              |  1 +
 .../ext.manip/put_money.pass.cpp              |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 .../print.fun/no_file_description.pass.cpp    |  3 ++
 .../std.manip/setfill_wchar_max.pass.cpp      |  1 +
 .../narrow.stream.objects/cerr.sh.cpp         |  1 +
 .../narrow.stream.objects/cin.sh.cpp          |  1 +
 .../narrow.stream.objects/clog.sh.cpp         |  1 +
 .../wide.stream.objects/wcerr-imbue.sh.cpp    |  1 +
 .../wide.stream.objects/wcerr.sh.cpp          |  1 +
 .../wide.stream.objects/wcin-imbue.sh.cpp     |  1 +
 .../wide.stream.objects/wcin.sh.cpp           |  1 +
 .../wide.stream.objects/wclog.sh.cpp          |  1 +
 .../sized_delete_array.pass.cpp               |  1 +
 ..._array_calls_unsized_delete_array.pass.cpp |  1 +
 .../new.delete.single/sized_delete.pass.cpp   |  1 +
 ...sized_delete_calls_unsized_delete.pass.cpp |  1 +
 .../locale.collate.byname/compare.pass.cpp    |  1 +
 .../locale.collate.byname/transform.pass.cpp  |  1 +
 .../facet.ctype.char.dtor/dtor.pass.cpp       |  1 +
 .../locale.ctype.byname/is_1.pass.cpp         |  1 +
 .../locale.ctype.byname/is_many.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_is.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_not.pass.cpp     |  1 +
 .../locale.ctype.byname/tolower_1.pass.cpp    |  1 +
 .../locale.ctype.byname/tolower_many.pass.cpp |  1 +
 .../locale.ctype.byname/toupper_1.pass.cpp    |  1 +
 .../locale.ctype.byname/toupper_many.pass.cpp |  1 +
 .../locale.ctype.byname/widen_1.pass.cpp      |  1 +
 .../locale.ctype.byname/widen_many.pass.cpp   |  1 +
 .../get_long_double_en_US.pass.cpp            |  1 +
 .../get_long_double_zh_CN.pass.cpp            |  1 +
 .../get_string_en_US.pass.cpp                 |  1 +
 .../put_long_double_en_US.pass.cpp            |  1 +
 .../put_long_double_zh_CN.pass.cpp            |  1 +
 .../put_string_en_US.pass.cpp                 |  1 +
 .../locale/locale.cons/default.pass.cpp       |  1 +
 .../test/std/re/re.traits/transform.pass.cpp  |  1 +
 .../re/re.traits/transform_primary.pass.cpp   |  1 +
 .../thread_create_failure.pass.cpp            |  1 +
 .../thread.jthread/join.deadlock.pass.cpp     |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 libcxx/test/support/count_new.h               | 28 +++++++++++++++++++
 libcxx/test/support/filesystem_test_helper.h  |  4 ++-
 libcxx/test/support/test_macros.h             |  4 ++-
 libunwind/test/aarch64_za_unwind.pass.cpp     | 13 +++++----
 80 files changed, 132 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index a20ed9ce9c6a9..542bc0cc83a1f 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -7,7 +7,8 @@ config.substitutions.append(('%{flags}',
     ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
 ))
 config.substitutions.append(('%{compile_flags}',
-    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
 ))
 config.substitutions.append(('%{link_flags}',
     # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
@@ -24,6 +25,12 @@ import libcxx.test.params, libcxx.test.config
 
 config.available_features.add('LIBCXX-OHOS-FIXME')
 config.available_features.add('ohos-hdc')
+config.available_features.add('executor-has-no-stdin')
+config.available_features.add('executor-merges-stderr-into-stdout')
+config.available_features.add('executor-runs-as-root')
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
index 20a545b719ed6..f04a6bce76691 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
index 03e9de882ab0a..428d30804a4c3 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
index 10829f8a73db1..c8a1c34aa3028 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index 071ee7f6c891f..44b94528f5e32 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a hard link.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
index 5d931891dd0eb..d291ca2cb5641 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
index 82b94a3d6bf5f..e4bc9c41728da 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
index 8401dbd38cd71..e0fa44ffbc4a5 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
index ac5499cdd6f20..2bcc64a3c2651 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_assignable<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
index 1f1843f7d401d..5efdf983f8a10 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_constructible<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
index 08d7ff84378d5..a3768b8cbd83f 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
index 1f2293989b7ef..9e8a70b4339d4 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // On Android L, ~scoped_test_env() is unable to delete the temp dir using
 // chmod+rm because chmod is too broken.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
index 56465d01021ad..62dc696d6797d 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
index fad880ecf30f7..708fb511dce73 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
index 12bc0c198c062..69e357adeafb5 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
index f1b559cf2d55b..0ed4aa0dc8d58 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
index f3161f2e020d2..579c03f2b8c0b 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
index fbf5e7129f7af..1d5312d65be46 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
index d0974640429df..7a3a56a0b161c 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
index 4a5882e36cde2..8b877ff63b112 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
index 382685c21dd99..995125d702de3 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
index 07b446d80c402..692fe773a0f9e 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
index c420fea21aa66..22c2c114ba687 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
index 56b4a27de6b42..8064feaaf2065 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
index b30d56685e2cb..540849ccefc05 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
index 425c224606038..02ff1e249d3bc 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
index 1d405a816e110..41474f9ca1e48 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
index 38e14438f3fd3..9a891eab6b343 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
index 88672851f7b6b..3e1d34ab5257a 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
index d0ff2c87689d1..4c299b876639e 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
index 98fa56b7f4b6d..7970887bac199 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
index 283a8fddb8e8f..13e6f74c5a143 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -17,6 +17,9 @@
 // fmemopen is available starting in Android M (API 23)
 // XFAIL: target={{.+}}-android{{(eabi)?(21|22)}}
 
+// OHOS fmemopen/FILE behavior does not currently match the expectation tested here.
+// XFAIL: LIBCXX-OHOS-FIXME
+
 // <print>
 
 // The FILE returned by fmemopen does not have file descriptor.
diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
index b24c0b90d86f1..2ddddbef70dbb 100644
--- a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
@@ -18,6 +18,7 @@
 // XFAIL: target=armv{{7|8}}{{l?}}{{.*}}-linux-gnueabihf && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-linux-gnu && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-amazon-linux && libcpp-abi-version=1
+// XFAIL: target={{.*}}-linux-ohos && libcpp-abi-version=1
 
 #include <iomanip>
 #include <ostream>
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
index da5563f87df45..ea5af90a90822 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
index 4f23b6b9a5d87..6dc0ff36ff4bf 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
@@ -14,6 +14,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
index 9bedd67e816ef..cbb9639766207 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
index ec4c8a009ba0c..59d93e74db3b7 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
index ae82035ccfc62..49a6ce0892ea4 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
index 6ae1972234b2b..58a6e18340ddf 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
index 08b74b999bbf4..88dcfe0485429 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
index 89e178248ae50..9c9745dcc9777 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wclog;
 
 // XFAIL: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
index 0e411f8adc0f2..e563152f2c8cd 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
index 026250daeee89..34119303a29ba 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete [] is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
index ca0f612bf7578..a4e32aaa11e13 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
index fbc9cf070bcba..3ec2b47b53eb0 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
index 8ae6bc2d3ba66..7cfd9f955fce7 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
@@ -8,6 +8,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
index 4978db116ef39..743075ede8ece 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
index 7be7e8d574e6c..b77a4409522f0 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
@@ -11,6 +11,7 @@
 // template <> class ctype<char>
 
 // ~ctype();
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <locale>
 #include <cassert>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
index 5cebc079c4703..c4803c04d173b 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <type_traits>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
index 2869378b42b92..5174ffebf281f 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
index ea03f042f2d45..665da7323e38a 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
index 2f7acb843e591..5d87c081b6e1c 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
index 447353de8af72..93d93aa9228f7 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
index 94ecc6c8fa85b..ab448a01632d6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
index 2b37737a26bc3..4be990e5b88c6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
index 016d8d736ad22..e0a3912a9315e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
index 959a4be9e1de9..b22629c82444e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
index 078b4a6fefb7e..c2cd904808794 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
index 9997b07134563..73b7a6decda36 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
index c86df7e6b53bf..deb30d747653f 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
index 478df7964f6d2..ee6fbd48b09b6 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
index 4b767fae871fa..c476537c3c7e3 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
index 25046a8417083..8174a724d4165 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
index 1c8710a008f27..e23cb4bf5f04a 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
index f0086ada30303..a36e3af8d12eb 100644
--- a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
+++ b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
@@ -12,6 +12,7 @@
 
 // This test runs in C++20, but we have deprecated codecvt<char(16|32), char, mbstate_t> in C++20.
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // <locale>
 
diff --git a/libcxx/test/std/re/re.traits/transform.pass.cpp b/libcxx/test/std/re/re.traits/transform.pass.cpp
index 80cd3f01faff2..112ecd47ff4e2 100644
--- a/libcxx/test/std/re/re.traits/transform.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform.pass.cpp
@@ -8,6 +8,7 @@
 //
 // NetBSD does not support LC_COLLATE at the moment
 // XFAIL: netbsd
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..cbc9387facb30 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -11,6 +11,7 @@
 
 // XFAIL: LIBCXX-AIX-FIXME
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
index 40daa49393bd4..110d0ea9fd694 100644
--- a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
+++ b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
@@ -15,6 +15,7 @@
 
 // There is no way to limit the number of threads on windows
 // UNSUPPORTED: windows
+// UNSUPPORTED: executor-runs-as-root
 
 // macOS and FreeBSD seem to limit the number of processes, not threads via RLIMIT_NPROC.
 // XFAIL: target={{.+}}-apple-{{.*}}
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
index c6eb0694ab2e1..19573ae52fd71 100644
--- a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
@@ -9,6 +9,7 @@
 // Windows cannot detect the deadlock. Instead of throwing system_error,
 // it would dead lock the test
 // UNSUPPORTED: windows
+// UNSUPPORTED: pthread-join-self-deadlocks
 
 // TSAN bug: https://llvm.org/PR66537
 // UNSUPPORTED: tsan
diff --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
index 51be78e1a02f8..804036e425572 100644
--- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
@@ -14,6 +14,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..6901aff00bc4e 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -415,6 +415,13 @@ void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteCalled(p);
+  std::free(p);
+}
+#  endif
+
 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->newArrayCalled(s);
@@ -449,6 +456,13 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteArrayCalled(p);
+  std::free(p);
+}
+#  endif
+
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
 #      define USE_ALIGNED_ALLOC
@@ -519,6 +533,13 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
@@ -551,6 +572,13 @@ void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
 
 #endif // DISABLE_NEW_COUNT
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index 2ad9efb32c60f..f820a4a0fa915 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -182,7 +182,7 @@ struct scoped_test_env
         std::string cmd = "chmod -R 777 " + test_root.string();
 #endif // defined(__MVS__)
         int ret = std::system(cmd.c_str());
-#  if !defined(_AIX) && !defined(__ANDROID__)
+#  if !defined(_AIX) && !defined(__ANDROID__) && !defined(__OHOS__)
         // On AIX the chmod command will return non-zero when trying to set
         // the permissions on a directory that contains a bad symlink. This triggers
         // the assert, despite being able to delete everything with the following
@@ -196,6 +196,8 @@ struct scoped_test_env
         // LIBCXX-ANDROID-FIXME: Other fixes to consider: place a toybox chmod
         // onto old devices, re-enable this assert for devices running Android N
         // and up, rewrite this chmod+rm in C or C++.
+        //
+        // OHOS has the same chmod -R behavior for bad symlinks.
         assert(ret == 0);
 #  endif
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..fdf6bc5b18d72 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -375,7 +375,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NOT_WIN32(...) __VA_ARGS__
 #endif
 
-#if defined(TEST_WINDOWS_DLL) ||defined(__MVS__) || defined(_AIX)
+#if defined(TEST_WINDOWS_DLL) || defined(__MVS__) || defined(_AIX) || defined(TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS)
 // Macros for waiving cases when we can't count allocations done within
 // the library implementation.
 //
@@ -386,6 +386,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 //
 // The same goes on IBM zOS.
 // The same goes on AIX.
+// Some target test configurations can define
+// TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS for the same reason.
 #define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
 #define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
 #else
diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp
index 36536860c1a96..b28f70a6e8e7a 100644
--- a/libunwind/test/aarch64_za_unwind.pass.cpp
+++ b/libunwind/test/aarch64_za_unwind.pass.cpp
@@ -20,6 +20,9 @@
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
 #endif
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
 
 // Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA
 // (and commits a lazy save of ZA) before resuming from unwinding.
@@ -33,18 +36,16 @@ static bool checkHasSME() {
     return false;
   return has_sme != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = getauxval(AT_HWCAP2);
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = 0;
   elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
 #else
 static bool checkHasSME() {

>From bbaa18370c45aaf79c2446be0a6e94dc2aa0ef00 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Fri, 3 Jul 2026 10:33:38 +0800
Subject: [PATCH 4/6] Add qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  16 +-
 libcxx/utils/ci/run-buildbot                  |  41 ++++-
 libcxx/utils/ci/vendor/ohos/qemu_run.py       | 144 ++++++++++++++++++
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |   8 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |   8 +-
 5 files changed, 204 insertions(+), 13 deletions(-)
 create mode 100755 libcxx/utils/ci/vendor/ohos/qemu_run.py

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 542bc0cc83a1f..20aa7a49d5802 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++ using HDC on OHOS.
+# libc++ using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -23,15 +23,21 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
 config.available_features.add('LIBCXX-OHOS-FIXME')
-config.available_features.add('ohos-hdc')
-config.available_features.add('executor-has-no-stdin')
-config.available_features.add('executor-merges-stderr-into-stdout')
-config.available_features.add('executor-runs-as-root')
+config.available_features.add(executor_kind)
 config.available_features.add('platform-has-nonportable-locales')
 config.available_features.add('platform-has-unreliable-new-delete-interposition')
 config.available_features.add('pthread-join-self-deadlocks')
 
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
     libcxx.test.features.DEFAULT_FEATURES,
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index ecf0ca17ae148..40739b0fba4d8 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -28,6 +28,16 @@ ${PROGNAME} [options] <BUILDER>
 --build-dir <DIR>   The directory to use for building the library. By default,
                     this is '<llvm-root>/build/<builder>'.
 
+--ohos-qemu <PATH>  Run OHOS tests under qemu-aarch64 instead of HDC. This also
+                    accepts OHOS_QEMU or QEMU from the environment.
+
+--ohos-qemu-sysroot <DIR>
+                    Sysroot passed to qemu-aarch64 with -L. Defaults to
+                    OHOS_QEMU_SYSROOT, QEMU_SYSROOT, then OHOS_SYSROOT.
+
+--ohos-hdc <PATH>   HDC binary to use when OHOS tests run on a device. This sets
+                    HDC for the OHOS HDC executor.
+
 Environment variables
 CC                  The C compiler to use, this value is used by CMake. This
                     variable is mandatory.
@@ -84,6 +94,19 @@ while [[ $# -gt 0 ]]; do
             BUILD_DIR="${2}"
             shift; shift
             ;;
+        --ohos-qemu)
+            OHOS_QEMU="${2}"
+            shift; shift
+            ;;
+        --ohos-qemu-sysroot)
+            OHOS_QEMU_SYSROOT="${2}"
+            shift; shift
+            ;;
+        --ohos-hdc)
+            HDC="${2}"
+            export HDC
+            shift; shift
+            ;;
         *)
             BUILDER="${1}"
             shift
@@ -193,8 +216,18 @@ function setup-ohos-hdc-env() {
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
     OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
-    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
-    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+    OHOS_QEMU="${OHOS_QEMU:-${QEMU:-}}"
+
+    if [[ -n "${OHOS_QEMU}" ]]; then
+        OHOS_QEMU_SYSROOT="${OHOS_QEMU_SYSROOT:-${QEMU_SYSROOT:-${OHOS_SYSROOT}}}"
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/qemu_run.py --qemu ${OHOS_QEMU} --sysroot ${OHOS_QEMU_SYSROOT}"
+        OHOS_EXECUTOR_KIND="ohos-qemu"
+    else
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+        OHOS_EXECUTOR_KIND="ohos-hdc"
+    fi
+
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE};executor_kind=${OHOS_EXECUTOR_KIND}"
 }
 
 function generate-cmake-ohos-hdc() {
@@ -863,14 +896,14 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
-ohos-hdc-aarch64-full)
+ohos-aarch64-full|ohos-hdc-aarch64-full)
     clean
 
     generate-cmake-ohos-hdc
 
     check-runtimes
 ;;
-ohos-hdc-aarch64-smoke)
+ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
     clean
 
     generate-cmake-ohos-hdc
diff --git a/libcxx/utils/ci/vendor/ohos/qemu_run.py b/libcxx/utils/ci/vendor/ohos/qemu_run.py
new file mode 100755
index 0000000000000..ef7eef7872864
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/qemu_run.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _resolve_binary(binary):
+    if os.path.isabs(binary):
+        if os.path.isfile(binary) and os.access(binary, os.X_OK):
+            return binary
+        sys.exit(f"failed to find executable: {binary}")
+
+    resolved = shutil.which(binary)
+    if resolved is None:
+        sys.exit(f"failed to find executable: {binary}")
+    return resolved
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(str(parent))
+    return library_dirs
+
+
+def _build_target_env_args(env, prepend_env):
+    target_env = dict(env)
+
+    for key, value in prepend_env.items():
+        existing = target_env.get(key, os.environ.get(key, ""))
+        target_env[key] = f"{value}{os.pathsep}{existing}" if existing else value
+
+    args = []
+    for key, value in target_env.items():
+        args.extend(["-E", f"{key}={value}"])
+    return args
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--qemu",
+        default=os.environ.get("OHOS_QEMU", os.environ.get("QEMU", "qemu-aarch64")),
+    )
+    parser.add_argument("--sysroot", required=True)
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(
+            os.environ.get(
+                "OHOS_QEMU_TIMEOUT_SECONDS",
+                os.environ.get("QEMU_TIMEOUT_SECONDS", "600"),
+            )
+        ),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    qemu = _resolve_binary(args.qemu)
+    execdir = Path(args.execdir).resolve()
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    build_library_dirs = _collect_build_library_dirs(execdir)
+    if build_library_dirs:
+        existing = prepend_env.get("LD_LIBRARY_PATH")
+        build_library_path = os.pathsep.join(build_library_dirs)
+        prepend_env["LD_LIBRARY_PATH"] = (
+            f"{build_library_path}{os.pathsep}{existing}"
+            if existing
+            else build_library_path
+        )
+
+    cmd = [
+        qemu,
+        "-L",
+        args.sysroot,
+        *_build_target_env_args(env, prepend_env),
+        *args.command,
+    ]
+    try:
+        completed = subprocess.run(
+            cmd,
+            cwd=execdir,
+            timeout=args.timeout_seconds,
+            check=False,
+        )
+    except subprocess.TimeoutExpired:
+        print(
+            f"qemu executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+            file=sys.stderr,
+        )
+        return 124
+    return completed.returncode
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index d18748fe3a3cc..8f5a17b3dec64 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++abi using HDC on OHOS.
+# libc++abi using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -20,7 +20,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index bce241a7898cf..f2a032c41a123 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -1,6 +1,6 @@
 #
 # Configuration file for running the libunwind tests against the shared library
-# using HDC on OHOS.
+# using HDC or QEMU on OHOS.
 #
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
@@ -45,7 +45,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,

>From dde4d430033711fb7ffa87621ba86b37492f480b Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 14:52:40 +0800
Subject: [PATCH 5/6] Fix something for qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in                   | 4 +++-
 .../file.streams/fstreams/filebuf.members/close.pass.cpp      | 3 +++
 .../fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp     | 2 ++
 .../syncstream/osyncstream/thread/several_threads.pass.cpp    | 2 ++
 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in             | 4 +++-
 libcxxabi/test/guard_threaded_test.pass.cpp                   | 2 ++
 libunwind/test/bad_unwind_info.pass.cpp                       | 2 ++
 libunwind/test/configs/llvm-libunwind-ohos.cfg.in             | 4 +++-
 libunwind/test/signal_unwind.pass.cpp                         | 2 ++
 9 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 20aa7a49d5802..3be1c267ede2a 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -23,7 +23,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
index 2c4cfb0ac7661..ce802d6ceba78 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14
 // REQUIRES: linux
 // UNSUPPORTED: no-filesystem
+// OHOS qemu-user observes the host /proc, not a real OHOS process /proc.
+// UNSUPPORTED: ohos-qemu
 // XFAIL: no-localization
 
 // <filesystem>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..77db6396755bf 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -10,6 +10,8 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
+// This thread-heavy stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 // <syncstream>
 
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index 8f5a17b3dec64..d0a8d1248f88a 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -20,7 +20,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxxabi/test/guard_threaded_test.pass.cpp b/libcxxabi/test/guard_threaded_test.pass.cpp
index d898e60288aca..a4e14f0aca9e4 100644
--- a/libcxxabi/test/guard_threaded_test.pass.cpp
+++ b/libcxxabi/test/guard_threaded_test.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: no-exceptions
+// This thread-heavy guard stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 #define TESTING_CXA_GUARD
 #include "../src/cxa_guard_impl.h"
diff --git a/libunwind/test/bad_unwind_info.pass.cpp b/libunwind/test/bad_unwind_info.pass.cpp
index 332b661d2e98f..76102505f2d67 100644
--- a/libunwind/test/bad_unwind_info.pass.cpp
+++ b/libunwind/test/bad_unwind_info.pass.cpp
@@ -13,6 +13,8 @@
 // REQUIRES: target={{(aarch64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
 // UNSUPPORTED: target={{.*-apple.*}}
+// OHOS qemu-user does not reliably model this invalid unwind-info path.
+// UNSUPPORTED: ohos-qemu
 
 // GCC doesn't support __attribute__((naked)) on AArch64.
 // UNSUPPORTED: gcc
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index f2a032c41a123..71d1e711e36d5 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -45,7 +45,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp
index f8798bc3eabeb..ef73a3ea8c59a 100644
--- a/libunwind/test/signal_unwind.pass.cpp
+++ b/libunwind/test/signal_unwind.pass.cpp
@@ -10,6 +10,8 @@
 // Ensure that the unwinder can cope with the signal handler.
 // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
+// OHOS qemu-user does not provide device-equivalent signal unwinding behavior.
+// UNSUPPORTED: ohos-qemu
 
 // TODO: Figure out why this fails with Memory Sanitizer.
 // XFAIL: msan

>From c81df7252fb00b5b9b41f0a29393e775abc9c810 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 15:19:29 +0800
Subject: [PATCH 6/6] delete run-smoke-test

---
 libcxx/utils/ci/buildkite-pipeline.yml | 12 ----------
 libcxx/utils/ci/run-buildbot           | 31 --------------------------
 2 files changed, 43 deletions(-)

diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 77617f43ae6dc..70bc69637a7f2 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -175,18 +175,6 @@ steps:
 
 - group: OHOS
   steps:
-  - label: OHOS AArch64 HDC Smoke
-    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
-    env:
-      CC: clang
-      CXX: clang++
-      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
-      HDC_TIMEOUT_SECONDS: 240
-    agents:
-      queue: libcxx-builders
-      os: linux
-      device: ohos-hdc
-    <<: *common
   - label: OHOS AArch64 HDC Full
     command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
     env:
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 40739b0fba4d8..e7d751df484e4 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -147,23 +147,6 @@ function require-env-var() {
     fi
 }
 
-function detect-lit() {
-    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
-        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
-    elif command -v llvm-lit >/dev/null 2>&1; then
-        LIT_RUNNER=("$(command -v llvm-lit)")
-    else
-        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
-    fi
-}
-
-function run-smoke-test() {
-    local test_dir="${1}"
-    local filter="${2}"
-    step "Running smoke test ${filter}"
-    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
-}
-
 function ohos-lit-args() {
     local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
     if python3 -c 'import psutil' >/dev/null 2>&1; then
@@ -903,20 +886,6 @@ ohos-aarch64-full|ohos-hdc-aarch64-full)
 
     check-runtimes
 ;;
-ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
-    clean
-
-    generate-cmake-ohos-hdc
-
-    step "Building the OHOS smoke test dependencies"
-    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
-
-    detect-lit
-    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
-;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #


From libcxx-commits at lists.llvm.org  Thu Jul  9 00:52:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 00:52:23 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a4f5337.eb24c6ea.27aedc.cf88@mx.google.com>


github-actions[bot] wrote:

<!--LLVM NEW CONTRIBUTOR COMMENT-->

Hello @huihui15537226823 :wave:

Thank you for submitting a Pull Request (PR) to the LLVM Project. Since this is your first PR, here are a few useful links covering our main contribution policies and review practices.

* All contributions to LLVM must follow our [LLVM AI Tool Use Policy](https://llvm.org/docs/AIToolPolicy.html). In particular, if you used AI while working on this PR, remember to add a note to the PR description.
* The [LLVM Code-Review Policy and Practices](https://llvm.org/docs/CodeReview.html) document contains practical information about the PR process, including how patches are reviewed and accepted, and who can review a PR.
* Our [LLVM Developer Policy](https://llvm.org/docs/DeveloperPolicy.html) describes our expectations for code quality, commit summaries and contains notes on our CI system.

Please reply to this message to confirm that you have read these policies, especially the LLVM AI Tool Use Policy, and that any AI tool usage has been noted in the PR description.

---

### Frequently asked questions

**How do I add reviewers?**

This PR will be automatically labeled, and the relevant teams will be notified. For some parts of the project, reviewers may also be added automatically.

You can also add reviewers manually using the **Reviewers** section on this page. If you cannot use that section, it is probably because you do not have write permissions for the repository. In that case, you can request a review by tagging reviewers in a comment using `@` followed by their GitHub username.

**What if there are no comments?**

If you have not received any comments on your PR after a week, you can request a review by pinging the PR with a comment such as “Ping”. The common courtesy ping rate is once a week. Please remember that you are asking for volunteer time from other developers.

**Are any special GitHub settings required to contribute to LLVM?**

We only require contributors to have a public email address associated with their GitHub commits, see this [section](https://llvm.org/docs/DeveloperPolicy.html#email-addresses) of LLVM Developer Policy for details.

---

If you have questions, feel free to leave a comment on this PR, or ask on [LLVM Discord](https://discord.com/invite/xS7Z362) or [LLVM Discourse](https://discourse.llvm.org/).

Thank you,
The LLVM Community

https://github.com/llvm/llvm-project/pull/208390

From libcxx-commits at lists.llvm.org  Thu Jul  9 00:53:24 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Thu, 09 Jul 2026 00:53:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to
	`lazy_split` (PR #208036)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208036@github.com>
Message-ID: <6a4f5374.92d2873c.1ab170.bdd9@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/208036

From libcxx-commits at lists.llvm.org  Thu Jul  9 00:55:49 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 00:55:49 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a4f5405.3cce8983.225732.db0b@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libunwind

Author: hu1 (huihui15537226823)

<details>
<summary>Changes</summary>

## Summary

This patch adds OHOS AArch64 support for running the libc++ runtimes test
suite, including libc++, libc++abi, and libunwind.

The main pieces are:

- Add OHOS-specific lit configurations for libc++, libc++abi, and libunwind.
- Add an OHOS executor path for running tests on device through HDC.
- Add an OHOS QEMU user-mode executor, which is easier to use for local and CI
  testing when physical OHOS devices are not available.
- Configure OHOS libc++ builds to use the `__h` ABI namespace by default
  through `LIBCXX_ABI_NAMESPACE=__h`.
- Add `ohos-qemu` / `ohos-hdc` lit features so tests can distinguish QEMU
  user-mode from real-device execution.
- Mark a small number of tests unsupported under `ohos-qemu` where QEMU
  user-mode does not match real OHOS device behavior, such as `/proc`, signal
  unwinding, or very thread-heavy stress tests. These tests remain available for
  HDC/device testing.

## Motivation

This makes it possible to continuously validate OHOS libc++ runtimes support
inside the upstream libc++ CI structure.

The QEMU path is the recommended default for general CI usage because it does
not require dedicated phone/device infrastructure. The HDC path is still
supported for real-device validation when an OHOS device is available.

## Sysroot Requirement

The OHOS QEMU sysroot is expected to be provided by the local machine or CI
agent and passed through `--ohos-qemu-sysroot` or `OHOS_QEMU_SYSROOT`. It is not
vendored into the LLVM source tree.

## Local Testing

A typical full OHOS AArch64 QEMU test run can be launched with:

```bash
cd /home/cdx/hwasan/llvm/llvm-project

unset HDC HDC_UTID HDC_TIMEOUT_SECONDS

export OHOS_SYSROOT=/home/xxx/sysroot/aarch64-linux-ohos
export CC=/home/xxx/llvm/out/llvm23-ohos-toolchain-install/bin/clang
export CXX=/home/xxx/llvm/out/llvm23-ohos-toolchain-install/bin/clang++
export OHOS_LIBCXX_ABI_NAMESPACE=__h

libcxx/utils/ci/run-buildbot \
  --ohos-qemu /usr/bin/qemu-aarch64 \
  --ohos-qemu-sysroot /home/xxx/llvm/out/qemu-sysroot-aarch64-linux-ohos \
  --build-dir /home/xxx/oh1/builds/llvm-ohos-qemu-full-habi \
  ohos-aarch64-full \
  2&gt;&amp;1 | tee /home/xxx/oh1/logs/7.9.log
```

The HDC/device path can also be used by providing `HDC`/device environment
variables and running the `ohos-hdc-aarch64-full` builder.

## Notes

The QEMU executor is intended to provide a convenient and reproducible CI mode.
Some tests are intentionally disabled only for `ohos-qemu` when the failure is
caused by QEMU user-mode behavior rather than libc++/libc++abi/libunwind
correctness on a real OHOS device.


---

Patch is 109.83 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/208390.diff


109 Files Affected:

- (modified) cmake/Modules/GetToolchainDirs.cmake (+23-14) 
- (modified) compiler-rt/cmake/Modules/CompilerRTUtils.cmake (+6-2) 
- (modified) compiler-rt/cmake/builtin-config-ix.cmake (+1-1) 
- (modified) compiler-rt/cmake/crt-config-ix.cmake (+1-1) 
- (modified) compiler-rt/lib/builtins/crtbegin.c (+9) 
- (modified) compiler-rt/lib/builtins/emutls.c (+4-4) 
- (modified) libcxx/CMakeLists.txt (+8-2) 
- (modified) libcxx/cmake/config-ix.cmake (+2) 
- (modified) libcxx/include/__config (+3-2) 
- (modified) libcxx/include/__cxx03/__config (+3-2) 
- (modified) libcxx/include/__cxx03/__locale_dir/locale_base_api.h (+1-1) 
- (modified) libcxx/include/__locale_dir/locale_base_api.h (+1-1) 
- (modified) libcxx/src/include/config_elast.h (+2) 
- (added) libcxx/test/configs/llvm-libc++-ohos.cfg.in (+63) 
- (modified) libcxx/test/extensions/clang/clang_modules_include.gen.py (+3) 
- (modified) libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp (+1) 
- (modified) libcxx/test/selftest/dsl/dsl.sh.py (+5) 
- (modified) libcxx/test/selftest/stdin-is-piped.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp (+3) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp (+1-1) 
- (modified) libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp (+1-1) 
- (modified) libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp (+2) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp (+3) 
- (modified) libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp (+2) 
- (modified) libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp (+1) 
- (modified) libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp (+1) 
- (modified) libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp (+1) 
- (modified) libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp (+1) 
- (modified) libcxx/test/std/re/re.traits/transform.pass.cpp (+1) 
- (modified) libcxx/test/std/re/re.traits/transform_primary.pass.cpp (+1) 
- (modified) libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp (+1) 
- (modified) libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp (+1) 
- (modified) libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp (+1) 
- (modified) libcxx/test/support/count_new.h (+28) 
- (modified) libcxx/test/support/filesystem_test_helper.h (+3-1) 
- (modified) libcxx/test/support/test_macros.h (+3-1) 
- (modified) libcxx/utils/ci/buildkite-pipeline.yml (+15) 
- (modified) libcxx/utils/ci/run-buildbot (+91) 
- (added) libcxx/utils/ci/vendor/ohos/hdc_run.py (+472) 
- (added) libcxx/utils/ci/vendor/ohos/hdc_run_test.py (+115) 
- (added) libcxx/utils/ci/vendor/ohos/qemu_run.py (+144) 
- (modified) libcxxabi/CMakeLists.txt (+6-1) 
- (added) libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in (+36) 
- (modified) libcxxabi/test/guard_threaded_test.pass.cpp (+2) 
- (modified) libunwind/CMakeLists.txt (+6-1) 
- (modified) libunwind/src/Registers.hpp (+9-6) 
- (modified) libunwind/test/aarch64_za_unwind.pass.cpp (+7-6) 
- (modified) libunwind/test/bad_unwind_info.pass.cpp (+2) 
- (added) libunwind/test/configs/llvm-libunwind-ohos.cfg.in (+61) 
- (modified) libunwind/test/signal_unwind.pass.cpp (+2) 


``````````diff
diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..3be1c267ede2a
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,63 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC or QEMU on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add(executor_kind)
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
+
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED:...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/208390

From libcxx-commits at lists.llvm.org  Thu Jul  9 00:55:50 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 00:55:50 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a4f5406.92ea0e2a.13fd37.7e4e@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: hu1 (huihui15537226823)

<details>
<summary>Changes</summary>

## Summary

This patch adds OHOS AArch64 support for running the libc++ runtimes test
suite, including libc++, libc++abi, and libunwind.

The main pieces are:

- Add OHOS-specific lit configurations for libc++, libc++abi, and libunwind.
- Add an OHOS executor path for running tests on device through HDC.
- Add an OHOS QEMU user-mode executor, which is easier to use for local and CI
  testing when physical OHOS devices are not available.
- Configure OHOS libc++ builds to use the `__h` ABI namespace by default
  through `LIBCXX_ABI_NAMESPACE=__h`.
- Add `ohos-qemu` / `ohos-hdc` lit features so tests can distinguish QEMU
  user-mode from real-device execution.
- Mark a small number of tests unsupported under `ohos-qemu` where QEMU
  user-mode does not match real OHOS device behavior, such as `/proc`, signal
  unwinding, or very thread-heavy stress tests. These tests remain available for
  HDC/device testing.

## Motivation

This makes it possible to continuously validate OHOS libc++ runtimes support
inside the upstream libc++ CI structure.

The QEMU path is the recommended default for general CI usage because it does
not require dedicated phone/device infrastructure. The HDC path is still
supported for real-device validation when an OHOS device is available.

## Sysroot Requirement

The OHOS QEMU sysroot is expected to be provided by the local machine or CI
agent and passed through `--ohos-qemu-sysroot` or `OHOS_QEMU_SYSROOT`. It is not
vendored into the LLVM source tree.

## Local Testing

A typical full OHOS AArch64 QEMU test run can be launched with:

```bash
cd /home/cdx/hwasan/llvm/llvm-project

unset HDC HDC_UTID HDC_TIMEOUT_SECONDS

export OHOS_SYSROOT=/home/xxx/sysroot/aarch64-linux-ohos
export CC=/home/xxx/llvm/out/llvm23-ohos-toolchain-install/bin/clang
export CXX=/home/xxx/llvm/out/llvm23-ohos-toolchain-install/bin/clang++
export OHOS_LIBCXX_ABI_NAMESPACE=__h

libcxx/utils/ci/run-buildbot \
  --ohos-qemu /usr/bin/qemu-aarch64 \
  --ohos-qemu-sysroot /home/xxx/llvm/out/qemu-sysroot-aarch64-linux-ohos \
  --build-dir /home/xxx/oh1/builds/llvm-ohos-qemu-full-habi \
  ohos-aarch64-full \
  2&gt;&amp;1 | tee /home/xxx/oh1/logs/7.9.log
```

The HDC/device path can also be used by providing `HDC`/device environment
variables and running the `ohos-hdc-aarch64-full` builder.

## Notes

The QEMU executor is intended to provide a convenient and reproducible CI mode.
Some tests are intentionally disabled only for `ohos-qemu` when the failure is
caused by QEMU user-mode behavior rather than libc++/libc++abi/libunwind
correctness on a real OHOS device.


---

Patch is 109.83 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/208390.diff


109 Files Affected:

- (modified) cmake/Modules/GetToolchainDirs.cmake (+23-14) 
- (modified) compiler-rt/cmake/Modules/CompilerRTUtils.cmake (+6-2) 
- (modified) compiler-rt/cmake/builtin-config-ix.cmake (+1-1) 
- (modified) compiler-rt/cmake/crt-config-ix.cmake (+1-1) 
- (modified) compiler-rt/lib/builtins/crtbegin.c (+9) 
- (modified) compiler-rt/lib/builtins/emutls.c (+4-4) 
- (modified) libcxx/CMakeLists.txt (+8-2) 
- (modified) libcxx/cmake/config-ix.cmake (+2) 
- (modified) libcxx/include/__config (+3-2) 
- (modified) libcxx/include/__cxx03/__config (+3-2) 
- (modified) libcxx/include/__cxx03/__locale_dir/locale_base_api.h (+1-1) 
- (modified) libcxx/include/__locale_dir/locale_base_api.h (+1-1) 
- (modified) libcxx/src/include/config_elast.h (+2) 
- (added) libcxx/test/configs/llvm-libc++-ohos.cfg.in (+63) 
- (modified) libcxx/test/extensions/clang/clang_modules_include.gen.py (+3) 
- (modified) libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp (+1) 
- (modified) libcxx/test/selftest/dsl/dsl.sh.py (+5) 
- (modified) libcxx/test/selftest/stdin-is-piped.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp (+3) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp (+1-1) 
- (modified) libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp (+1-1) 
- (modified) libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp (+2) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp (+3) 
- (modified) libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp (+1) 
- (modified) libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp (+2) 
- (modified) libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp (+1) 
- (modified) libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp (+1) 
- (modified) libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp (+1) 
- (modified) libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp (+1) 
- (modified) libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp (+1) 
- (modified) libcxx/test/std/re/re.traits/transform.pass.cpp (+1) 
- (modified) libcxx/test/std/re/re.traits/transform_primary.pass.cpp (+1) 
- (modified) libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp (+1) 
- (modified) libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp (+1) 
- (modified) libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp (+1) 
- (modified) libcxx/test/support/count_new.h (+28) 
- (modified) libcxx/test/support/filesystem_test_helper.h (+3-1) 
- (modified) libcxx/test/support/test_macros.h (+3-1) 
- (modified) libcxx/utils/ci/buildkite-pipeline.yml (+15) 
- (modified) libcxx/utils/ci/run-buildbot (+91) 
- (added) libcxx/utils/ci/vendor/ohos/hdc_run.py (+472) 
- (added) libcxx/utils/ci/vendor/ohos/hdc_run_test.py (+115) 
- (added) libcxx/utils/ci/vendor/ohos/qemu_run.py (+144) 
- (modified) libcxxabi/CMakeLists.txt (+6-1) 
- (added) libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in (+36) 
- (modified) libcxxabi/test/guard_threaded_test.pass.cpp (+2) 
- (modified) libunwind/CMakeLists.txt (+6-1) 
- (modified) libunwind/src/Registers.hpp (+9-6) 
- (modified) libunwind/test/aarch64_za_unwind.pass.cpp (+7-6) 
- (modified) libunwind/test/bad_unwind_info.pass.cpp (+2) 
- (added) libunwind/test/configs/llvm-libunwind-ohos.cfg.in (+61) 
- (modified) libunwind/test/signal_unwind.pass.cpp (+2) 


``````````diff
diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..3be1c267ede2a
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,63 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC or QEMU on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add(executor_kind)
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
+
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED:...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/208390

From libcxx-commits at lists.llvm.org  Thu Jul  9 00:57:29 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 00:57:29 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a4f5469.eb165e5c.1817a5.797d@mx.google.com>


https://github.com/huihui15537226823 updated https://github.com/llvm/llvm-project/pull/208390

>From 70e8ac839667f8aba4aa74a2dd19614426f9754f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 24 Jun 2026 09:47:00 +0800
Subject: [PATCH 1/6] Add OHOS HDC runtimes CI support

---
 cmake/Modules/GetToolchainDirs.cmake          |  37 +-
 .../cmake/Modules/CompilerRTUtils.cmake       |   8 +-
 compiler-rt/cmake/builtin-config-ix.cmake     |   2 +-
 compiler-rt/cmake/crt-config-ix.cmake         |   2 +-
 compiler-rt/lib/builtins/crtbegin.c           |   9 +
 compiler-rt/lib/builtins/emutls.c             |   8 +-
 libcxx/CMakeLists.txt                         |  10 +-
 libcxx/cmake/config-ix.cmake                  |   2 +
 libcxx/include/__config                       |   5 +-
 libcxx/include/__cxx03/__config               |   5 +-
 .../__cxx03/__locale_dir/locale_base_api.h    |   2 +-
 libcxx/include/__locale_dir/locale_base_api.h |   2 +-
 libcxx/src/include/config_elast.h             |   2 +
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  48 ++
 libcxx/utils/ci/buildkite-pipeline.yml        |  27 +
 libcxx/utils/ci/run-buildbot                  |  87 ++++
 libcxx/utils/ci/vendor/ohos/hdc_run.py        | 472 ++++++++++++++++++
 libcxx/utils/ci/vendor/ohos/hdc_run_test.py   | 115 +++++
 libcxxabi/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |  30 ++
 libunwind/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |  55 ++
 22 files changed, 910 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/test/configs/llvm-libc++-ohos.cfg.in
 create mode 100755 libcxx/utils/ci/vendor/ohos/hdc_run.py
 create mode 100644 libcxx/utils/ci/vendor/ohos/hdc_run_test.py
 create mode 100644 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
 create mode 100644 libunwind/test/configs/llvm-libunwind-ohos.cfg.in

diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..a20ed9ce9c6a9
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,48 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index d4107cf11ad51..77617f43ae6dc 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -172,3 +172,30 @@ steps:
       queue: libcxx-builders
       os: android
     <<: *common
+
+- group: OHOS
+  steps:
+  - label: OHOS AArch64 HDC Smoke
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
+  - label: OHOS AArch64 HDC Full
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 6376bc1d3dd5a..1867e9c620649 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -116,6 +116,41 @@ function clean() {
     rm -rf "${BUILD_DIR}"
 }
 
+function require-env-var() {
+    local name="${1}"
+    if [[ -z ${!name+x} ]] || [[ -z "${!name}" ]]; then
+        error "Environment variable ${name} must be defined"
+        exit 1
+    fi
+}
+
+function detect-lit() {
+    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
+        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
+    elif command -v llvm-lit >/dev/null 2>&1; then
+        LIT_RUNNER=("$(command -v llvm-lit)")
+    else
+        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
+    fi
+}
+
+function run-smoke-test() {
+    local test_dir="${1}"
+    local filter="${2}"
+    step "Running smoke test ${filter}"
+    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
+}
+
+function ohos-lit-args() {
+    local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
+    if python3 -c 'import psutil' >/dev/null 2>&1; then
+        args="${args} --timeout=1500"
+    else
+        echo "warning: python3 psutil not available; running OHOS lit without per-test timeout" >&2
+    fi
+    printf '%s' "${args}"
+}
+
 function generate-cmake-base() {
     step "Generating CMake"
 
@@ -153,6 +188,37 @@ function generate-cmake-android() {
           "${@}"
 }
 
+function setup-ohos-hdc-env() {
+    require-env-var OHOS_SYSROOT
+
+    OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+}
+
+function generate-cmake-ohos-hdc() {
+    setup-ohos-hdc-env
+
+    local lit_args
+    lit_args="$(ohos-lit-args)"
+
+    generate-cmake \
+          -DLLVM_LIT_ARGS="${lit_args}" \
+          -DCMAKE_SYSTEM_NAME=OHOS \
+          -DCMAKE_SYSROOT="${OHOS_SYSROOT}" \
+          -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
+          -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
+          -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
+          -DLIBUNWIND_TEST_CONFIG="llvm-libunwind-ohos.cfg.in" \
+          -DLIBCXX_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBCXXABI_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBUNWIND_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          "${@}"
+}
+
 function check-runtimes() {
     step "Building libc++ test dependencies"
     ninja -vC "${BUILD_DIR}" cxx-test-depends
@@ -795,6 +861,27 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
+ohos-hdc-aarch64-full)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    check-runtimes
+;;
+ohos-hdc-aarch64-smoke)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    step "Building the OHOS smoke test dependencies"
+    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
+
+    detect-lit
+    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
+;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run.py b/libcxx/utils/ci/vendor/ohos/hdc_run.py
new file mode 100755
index 0000000000000..61a3899b88b11
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run.py
@@ -0,0 +1,472 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import uuid
+
+
+EXIT_MARKER = "__llvm_hdc_exit__="
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _split_path_list(value):
+    return [entry for entry in value.split(os.pathsep) if entry]
+
+
+def _resolve_hdc_binary(hdc):
+    if os.path.isabs(hdc):
+        return hdc
+    resolved = shutil.which(hdc)
+    if resolved is None:
+        sys.exit(f"failed to find hdc binary: {hdc}")
+    return resolved
+
+
+def _is_windows_hdc(path):
+    return path.lower().endswith(".exe")
+
+
+def _find_windows_local_root(hdc_path):
+    parts = Path(hdc_path).parts
+    for index in range(len(parts) - 1):
+        if parts[index] == "AppData" and index + 1 < len(parts) and parts[index + 1] == "Local":
+            return Path(*parts[: index + 2])
+    return None
+
+
+def _default_host_staging_root(hdc_path):
+    if _is_windows_hdc(hdc_path):
+        local_root = _find_windows_local_root(hdc_path)
+        if local_root is None:
+            sys.exit(
+                "failed to derive a Windows staging directory from the hdc.exe path; "
+                "set HDC_HOST_STAGING_ROOT to a path under /mnt/<drive>/..."
+            )
+        return local_root / "Temp" / "llvm-lit-hdc"
+    return Path("/tmp/llvm-lit-hdc")
+
+
+def _to_windows_path(path):
+    resolved = Path(path).resolve()
+    parts = resolved.parts
+    if len(parts) < 4 or parts[1] != "mnt":
+        sys.exit(
+            f"Windows hdc requires a staging path under /mnt/<drive>/..., got: {resolved}"
+        )
+    drive = parts[2].upper()
+    tail = "\\".join(parts[3:])
+    return f"{drive}:\\{tail}" if tail else f"{drive}:\\"
+
+
+def _run_readelf(path):
+    completed = subprocess.run(
+        ["readelf", "-d", path],
+        check=False,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        text=True,
+    )
+    if completed.returncode != 0:
+        return ""
+    return completed.stdout
+
+
+def _read_elf_metadata(path):
+    needed = []
+    runpath_dirs = []
+    seen_dirs = set()
+    for line in _run_readelf(path).splitlines():
+        needed_match = re.search(r"\(NEEDED\).*Shared library: \[(.*)\]", line)
+        if needed_match:
+            needed.append(needed_match.group(1))
+            continue
+
+        runpath_match = re.search(
+            r"\((?:RUNPATH|RPATH)\).*Library runpath: \[(.*)\]", line
+        )
+        if not runpath_match:
+            continue
+        for entry in _split_path_list(runpath_match.group(1)):
+            candidate = Path(entry)
+            if not candidate.is_dir():
+                continue
+            key = str(candidate.resolve())
+            if key in seen_dirs:
+                continue
+            seen_dirs.add(key)
+            runpath_dirs.append(candidate)
+    return needed, runpath_dirs
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(parent)
+    return library_dirs
+
+
+def _find_dependency(name, search_dirs):
+    for directory in search_dirs:
+        candidate = Path(directory) / name
+        if candidate.is_file():
+            return candidate.resolve()
+    return None
+
+
+def _copy_needed_libraries(executables, execdir, staging_execdir):
+    search_dirs = _collect_build_library_dirs(execdir)
+    queue = [Path(exe).resolve() for exe in executables]
+    visited = set()
+    copied_names = {path.name for path in staging_execdir.glob("*") if path.is_file()}
+
+    while queue:
+        current = queue.pop(0)
+        current_key = str(current)
+        if current_key in visited:
+            continue
+        visited.add(current_key)
+
+        needed, runpath_dirs = _read_elf_metadata(current)
+        current_search_dirs = []
+        current_seen = set()
+        for directory in [*runpath_dirs, current.parent, *search_dirs]:
+            resolved = Path(directory).resolve()
+            key = str(resolved)
+            if key in current_seen:
+                continue
+            current_seen.add(key)
+            current_search_dirs.append(resolved)
+
+        for directory in runpath_dirs:
+            resolved = directory.resolve()
+            if all(str(resolved) != str(existing) for existing in search_dirs):
+                search_dirs.append(resolved)
+
+        for lib_name in needed:
+            dependency = _find_dependency(lib_name, current_search_dirs)
+            if dependency is None:
+                continue
+            if lib_name not in copied_names:
+                shutil.copy2(dependency, staging_execdir / lib_name)
+                copied_names.add(lib_name)
+            queue.append(dependency)
+
+
+def _copy_execdir(execdir, staging_execdir):
+    if staging_execdir.exists():
+        shutil.rmtree(staging_execdir)
+    shutil.copytree(execdir, staging_execdir)
+
+
+def _iter_files(root):
+    for directory, dirnames, filenames in os.walk(root):
+        dirnames.sort()
+        filenames.sort()
+        yield Path(directory), filenames
+
+
+def _is_test_executable(path):
+    return path.endswith(".tmp.exe") and os.path.exists(path)
+
+
+def _map_local_path(path, local_execdir, remote_execdir):
+    normalized = os.path.normpath(path)
+    execdir = os.path.normpath(local_execdir)
+    if normalized == execdir:
+        return remote_execdir
+    prefix = execdir + os.sep
+    if normalized.startswith(prefix):
+        suffix = normalized[len(prefix) :].replace(os.sep, "/")
+        return f"{remote_execdir}/{suffix}" if suffix else remote_execdir
+    return path
+
+
+def _map_value(value, local_execdir, remote_execdir):
+    return _map_local_path(value, local_execdir, remote_execdir)
+
+
+def _build_env_exports(env, prepend_env, local_execdir, remote_execdir, add_remote_libdir):
+    commands = []
+    mapped_env = {
+        key: _map_value(value, local_execdir, remote_execdir) for key, value in env.items()
+    }
+    mapped_prepend = {
+        key: _map_value(value, local_execdir, remote_execdir)
+        for key, value in prepend_env.items()
+    }
+
+    if add_remote_libdir:
+        existing = mapped_prepend.get("LD_LIBRARY_PATH")
+        mapped_prepend["LD_LIBRARY_PATH"] = (
+            f"{remote_execdir}:{existing}" if existing else remote_execdir
+        )
+
+    for key, value in mapped_env.items():
+        commands.append(f"export {key}={shlex.quote(value)}")
+    for key, value in mapped_prepend.items():
+        commands.append(f"export {key}={shlex.quote(value)}${{{key}:+:{key}}}")
+    return commands
+
+
+def _hdc_prefix(hdc_path, server, target):
+    prefix = [hdc_path]
+    if server:
+        prefix.extend(["-s", server])
+    if target:
+        prefix.extend(["-t", target])
+    return prefix
+
+
+def _run_hdc(
+    prefix,
+    args,
+    *,
+    timeout=None,
+    capture_output=False,
+    stdin=subprocess.DEVNULL,
+    check=True,
+    expect_stdout=None,
+):
+    kwargs = {
+        "check": False,
+        "timeout": timeout,
+        "stdin": stdin,
+        "text": True,
+    }
+    if capture_output:
+        kwargs["stdout"] = subprocess.PIPE
+        kwargs["stderr"] = subprocess.PIPE
+    completed = subprocess.run(prefix + args, **kwargs)
+    output = completed.stdout if capture_output and completed.stdout else ""
+    error_output = completed.stderr if capture_output and completed.stderr else ""
+    if check and completed.returncode != 0:
+        raise RuntimeError(
+            f"hdc command failed with exit code {completed.returncode}: "
+            f"{shlex.join(prefix + args)}\n{output}{error_output}"
+        )
+    if check and expect_stdout is not None and expect_stdout not in output:
+        raise RuntimeError(
+            f"hdc command did not report success: {shlex.join(prefix + args)}\n{output}"
+        )
+    return completed
+
+
+def _run_checked_shell(prefix, command, *, timeout=None):
+    completed = _run_hdc(
+        prefix,
+        [
+            "shell",
+            f"{command}; rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"",
+        ],
+        timeout=timeout,
+        capture_output=True,
+    )
+    exit_code, output = _extract_exit_code(completed.stdout)
+    if exit_code is None:
+        raise RuntimeError(
+            f"failed to parse shell exit code for: {command}\n{completed.stdout}"
+        )
+    if exit_code != 0:
+        raise RuntimeError(
+            f"remote shell command failed with exit code {exit_code}: {command}\n{output}"
+        )
+    return output
+
+
+def _sync_tree_to_device(prefix, local_root, remote_root, use_windows_paths):
+    _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_root)}")
+    _run_checked_shell(prefix, f"mkdir -p {shlex.quote(remote_root)}")
+
+    for directory, filenames in _iter_files(local_root):
+        relative_dir = os.path.relpath(directory, local_root)
+        device_dir = (
+            remote_root
+            if relative_dir == "."
+            else f"{remote_root}/{relative_dir.replace(os.sep, '/')}"
+        )
+        _run_checked_shell(prefix, f"mkdir -p {shlex.quote(device_dir)}")
+        for name in filenames:
+            local_path = directory / name
+            device_path = f"{device_dir}/{name}"
+            source = _to_windows_path(local_path) if use_windows_paths else str(local_path)
+            _run_hdc(
+                prefix,
+                ["file", "send", source, device_path],
+                capture_output=True,
+                expect_stdout="FileTransfer finish",
+            )
+            if name.endswith(".tmp.exe") or os.access(local_path, os.X_OK):
+                _run_checked_shell(prefix, f"chmod +x {shlex.quote(device_path)}")
+
+
+def _build_remote_command(command, local_execdir, remote_execdir, exports):
+    mapped_command = [
+        _map_local_path(arg, local_execdir, remote_execdir) for arg in command
+    ]
+    pieces = [f"cd {shlex.quote(remote_execdir)}"]
+    pieces.extend(exports)
+    pieces.append(shlex.join(mapped_command))
+    pieces.append(f"rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"")
+    return " && ".join(pieces[:-2]) + f"; {pieces[-2]}; {pieces[-1]}"
+
+
+def _extract_exit_code(output):
+    match = re.search(rf"^(.*)\n{EXIT_MARKER}(\d+)\s*$", output, re.DOTALL)
+    if not match:
+        return None, output
+    return int(match.group(2)), match.group(1)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--hdc", default=os.environ.get("HDC", "hdc"))
+    parser.add_argument("--hdc-server", default=os.environ.get("HDC_SERVER_IP_PORT"))
+    parser.add_argument("--hdc-target", default=os.environ.get("HDC_UTID"))
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--remote-base-dir",
+        default="/data/local/tmp/llvm-lit-hdc",
+    )
+    parser.add_argument(
+        "--host-staging-root",
+        default=os.environ.get("HDC_HOST_STAGING_ROOT"),
+    )
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(os.environ.get("HDC_TIMEOUT_SECONDS", "120")),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("--keep-remote-dir", action="store_true")
+    parser.add_argument("--keep-host-staging", action="store_true")
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    hdc_path = _resolve_hdc_binary(args.hdc)
+    use_windows_paths = _is_windows_hdc(hdc_path)
+    host_staging_root = (
+        Path(args.host_staging_root)
+        if args.host_staging_root
+        else _default_host_staging_root(hdc_path)
+    )
+    prefix = _hdc_prefix(hdc_path, args.hdc_server, args.hdc_target)
+
+    execdir = Path(args.execdir).resolve()
+    run_id = uuid.uuid4().hex[:12]
+    staging_execdir = host_staging_root / f"run-{run_id}" / execdir.name
+    remote_execdir = f"{args.remote_base_dir}/run-{run_id}/{execdir.name}"
+
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    executables = [arg for arg in args.command if _is_test_executable(arg)]
+    host_staging_root.mkdir(parents=True, exist_ok=True)
+    _copy_execdir(execdir, staging_execdir)
+    _copy_needed_libraries(executables, execdir, staging_execdir)
+
+    add_remote_libdir = any(staging_execdir.glob("*.so*"))
+    exports = _build_env_exports(
+        env,
+        prepend_env,
+        str(execdir),
+        remote_execdir,
+        add_remote_libdir,
+    )
+
+    try:
+        _sync_tree_to_device(prefix, staging_execdir, remote_execdir, use_windows_paths)
+        remote_command = _build_remote_command(
+            args.command,
+            str(execdir),
+            remote_execdir,
+            exports,
+        )
+        try:
+            completed = _run_hdc(
+                prefix,
+                ["shell", remote_command],
+                timeout=args.timeout_seconds,
+                capture_output=True,
+                stdin=None,
+            )
+        except subprocess.TimeoutExpired:
+            print(
+                f"hdc executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+                file=sys.stderr,
+            )
+            return 124
+
+        exit_code, output = _extract_exit_code(completed.stdout)
+        if exit_code is None:
+            print(
+                "failed to parse exit code from hdc shell output",
+                file=sys.stderr,
+            )
+            if completed.stdout:
+                print(completed.stdout, end="", file=sys.stderr)
+            if completed.stderr:
+                print(completed.stderr, end="", file=sys.stderr)
+            return 1
+
+        if output:
+            sys.stdout.write(output)
+        if completed.stderr:
+            sys.stderr.write(completed.stderr)
+        return exit_code
+    finally:
+        if not args.keep_remote_dir:
+            try:
+                _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_execdir)}")
+            except Exception:
+                pass
+        if not args.keep_host_staging:
+            shutil.rmtree(staging_execdir.parent, ignore_errors=True)
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run_test.py b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
new file mode 100644
index 0000000000000..17e2f65bd1fb6
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import os
+from pathlib import Path
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import unittest
+
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+HDC_RUN = SCRIPT_DIR / "hdc_run.py"
+
+
+class HdcRunTest(unittest.TestCase):
+    def make_fake_hdc(self, directory):
+        fake_hdc = directory / "hdc"
+        fake_hdc.write_text(
+            textwrap.dedent(
+                """\
+                #!/usr/bin/env python3
+                import sys
+
+                EXIT_MARKER = "__llvm_hdc_exit__="
+
+                def main():
+                    args = sys.argv[1:]
+                    if args[:2] == ["file", "send"]:
+                        print("FileTransfer finish")
+                        return 0
+
+                    if args and args[0] == "shell":
+                        command = args[1]
+                        if "stderr.tmp.exe" in command:
+                            sys.stdout.write("STDOUT-OUTPUT")
+                            sys.stderr.write("STDERR-OUTPUT")
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        if "stdin.tmp.exe" in command:
+                            sys.stdout.write("IN:" + sys.stdin.read())
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                        return 0
+
+                    print("unexpected fake hdc invocation: " + repr(args), file=sys.stderr)
+                    return 2
+
+                if __name__ == "__main__":
+                    raise SystemExit(main())
+                """
+            )
+        )
+        fake_hdc.chmod(fake_hdc.stat().st_mode | stat.S_IXUSR)
+        return fake_hdc
+
+    def run_hdc_run(self, temp_dir, executable_name, *, stdin_text=None):
+        execdir = temp_dir / "execdir"
+        execdir.mkdir()
+        executable = execdir / executable_name
+        executable.write_text("# fake executable marker\n")
+        executable.chmod(executable.stat().st_mode | stat.S_IXUSR)
+
+        fake_hdc = self.make_fake_hdc(temp_dir)
+        return subprocess.run(
+            [
+                sys.executable,
+                str(HDC_RUN),
+                "--hdc",
+                str(fake_hdc),
+                "--execdir",
+                str(execdir),
+                "--host-staging-root",
+                str(temp_dir / "staging"),
+                str(executable),
+            ],
+            input=stdin_text,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+    def test_preserves_remote_stdout_and_stderr_separately(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(Path(temp), "stderr.tmp.exe")
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "STDOUT-OUTPUT")
+        self.assertEqual(completed.stderr, "STDERR-OUTPUT")
+
+    def test_forwards_stdin_to_remote_command(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(
+                Path(temp),
+                "stdin.tmp.exe",
+                stdin_text="hello from stdin",
+            )
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "IN:hello from stdin")
+        self.assertEqual(completed.stderr, "")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt
index e1a1587fb6283..cb22e6fe65108 100644
--- a/libcxxabi/CMakeLists.txt
+++ b/libcxxabi/CMakeLists.txt
@@ -187,7 +187,12 @@ set(LIBCXXABI_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXXABI_TARGET_SUBDIR)
+  else()
+    set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXXABI_LIBDIR_SUBDIR)
     string(APPEND LIBCXXABI_TARGET_SUBDIR /${LIBCXXABI_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
new file mode 100644
index 0000000000000..d18748fe3a3cc
--- /dev/null
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -0,0 +1,30 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++abi using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -I %{libcxx}/test/support -I %{libcxx}/src -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    '-nostdlib++ -L %{lib} -lc++ -lc++abi -pthread'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 02132f6c07fcd..4fd11bf5aca2f 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -142,7 +142,12 @@ set(LIBUNWIND_STATIC_OUTPUT_NAME "unwind" CACHE STRING "Output name for the stat
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBUNWIND_TARGET_SUBDIR)
+  else()
+    set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBUNWIND_LIBDIR_SUBDIR)
     string(APPEND LIBUNWIND_TARGET_SUBDIR /${LIBUNWIND_LIBDIR_SUBDIR})
   endif()
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
new file mode 100644
index 0000000000000..bce241a7898cf
--- /dev/null
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -0,0 +1,55 @@
+#
+# Configuration file for running the libunwind tests against the shared library
+# using HDC on OHOS.
+#
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+compile_flags = []
+link_flags = []
+
+if @HAVE_GETAUXVAL@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_GETAUXVAL')
+
+if @HAVE_ELF_AUX_INFO@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_ELF_AUX_INFO')
+
+if @LIBUNWIND_ENABLE_CET@:
+    compile_flags.append('-fcf-protection=full')
+
+if @LIBUNWIND_ENABLE_GCS@:
+    compile_flags.append('-mbranch-protection=standard')
+
+if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
+    link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
+
+if '@CMAKE_DL_LIBS@':
+    link_flags.append('-l at CMAKE_DL_LIBS@')
+
+compile_flags.append('-funwind-tables')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags))
+))
+config.substitutions.append(('%{link_flags}',
+    '-L %{{lib}} -lunwind {}'.format(' '.join(link_flags))
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)

>From e9e5d2e517fb072f3554798e49b7119ec739105f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 25 Jun 2026 11:30:38 +0800
Subject: [PATCH 2/6] Fix OHOS libc++ CI ABI and SME detection

---
 libcxx/utils/ci/run-buildbot |  2 ++
 libunwind/src/Registers.hpp  | 15 +++++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 1867e9c620649..ecf0ca17ae148 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -192,6 +192,7 @@ function setup-ohos-hdc-env() {
     require-env-var OHOS_SYSROOT
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
     OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
     OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
 }
@@ -209,6 +210,7 @@ function generate-cmake-ohos-hdc() {
           -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE}" \
           -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
           -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
           -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c88422a6d5f4f..18cb2a523bd0e 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -25,6 +25,11 @@
 #endif
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
+#if defined(__has_include)
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
+#endif
 #endif
 
 namespace libunwind {
@@ -1951,18 +1956,16 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
       return false;
     return has_sme != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = getauxval(AT_HWCAP2);
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = 0;
     elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
 #else
   static bool checkHasSME() {

>From 498fa8d127ff49c27d4230996338c6eb5d747fe6 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 1 Jul 2026 15:49:11 +0800
Subject: [PATCH 3/6] Stabilize OHOS HDC runtimes tests

Add OHOS lit features for HDC executor and platform limitations, mark affected libc++ tests as unsupported or expected failures, and adjust test support for OHOS filesystem cleanup and allocation counting behavior.

Also guard the aarch64 ZA unwind test on HWCAP2_SME so devices without a reliable SME feature definition do not execute SME instructions.
---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  9 +++++-
 .../clang/clang_modules_include.gen.py        |  3 ++
 .../locale.facet/no_allocation.pass.cpp       |  1 +
 libcxx/test/selftest/dsl/dsl.sh.py            |  5 ++++
 libcxx/test/selftest/stdin-is-piped.sh.cpp    |  1 +
 .../directory_entry.cons/path.pass.cpp        |  1 +
 .../directory_entry.mods/assign.pass.cpp      |  1 +
 .../directory_entry.mods/refresh.pass.cpp     |  1 +
 .../replace_filename.pass.cpp                 |  1 +
 .../directory_entry.obs/file_size.pass.cpp    |  1 +
 .../file_type_obs.pass.cpp                    |  1 +
 .../hard_link_count.pass.cpp                  |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../directory_iterator.members/ctor.pass.cpp  |  1 +
 .../path.member/path.assign/move.pass.cpp     |  2 +-
 .../path.member/path.construct/move.pass.cpp  |  2 +-
 .../rec.dir.itr.members/ctor.pass.cpp         |  1 +
 .../rec.dir.itr.members/increment.pass.cpp    |  1 +
 .../fs.op.funcs/fs.op.exists/exists.pass.cpp  |  1 +
 .../is_block_file.pass.cpp                    |  1 +
 .../is_character_file.pass.cpp                |  1 +
 .../fs.op.is_directory/is_directory.pass.cpp  |  1 +
 .../fs.op.is_empty/is_empty.pass.cpp          |  1 +
 .../fs.op.is_fifo/is_fifo.pass.cpp            |  1 +
 .../fs.op.is_other/is_other.pass.cpp          |  1 +
 .../is_regular_file.pass.cpp                  |  1 +
 .../fs.op.is_socket/is_socket.pass.cpp        |  1 +
 .../fs.op.is_symlink/is_symlink.pass.cpp      |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../fs.op.funcs/fs.op.remove/remove.pass.cpp  |  1 +
 .../fs.op.remove_all/remove_all.pass.cpp      |  1 +
 .../fs.op.funcs/fs.op.status/status.pass.cpp  |  1 +
 .../symlink_status.pass.cpp                   |  1 +
 .../temp_directory_path.pass.cpp              |  1 +
 .../ext.manip/get_money.pass.cpp              |  1 +
 .../ext.manip/put_money.pass.cpp              |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 .../print.fun/no_file_description.pass.cpp    |  3 ++
 .../std.manip/setfill_wchar_max.pass.cpp      |  1 +
 .../narrow.stream.objects/cerr.sh.cpp         |  1 +
 .../narrow.stream.objects/cin.sh.cpp          |  1 +
 .../narrow.stream.objects/clog.sh.cpp         |  1 +
 .../wide.stream.objects/wcerr-imbue.sh.cpp    |  1 +
 .../wide.stream.objects/wcerr.sh.cpp          |  1 +
 .../wide.stream.objects/wcin-imbue.sh.cpp     |  1 +
 .../wide.stream.objects/wcin.sh.cpp           |  1 +
 .../wide.stream.objects/wclog.sh.cpp          |  1 +
 .../sized_delete_array.pass.cpp               |  1 +
 ..._array_calls_unsized_delete_array.pass.cpp |  1 +
 .../new.delete.single/sized_delete.pass.cpp   |  1 +
 ...sized_delete_calls_unsized_delete.pass.cpp |  1 +
 .../locale.collate.byname/compare.pass.cpp    |  1 +
 .../locale.collate.byname/transform.pass.cpp  |  1 +
 .../facet.ctype.char.dtor/dtor.pass.cpp       |  1 +
 .../locale.ctype.byname/is_1.pass.cpp         |  1 +
 .../locale.ctype.byname/is_many.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_is.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_not.pass.cpp     |  1 +
 .../locale.ctype.byname/tolower_1.pass.cpp    |  1 +
 .../locale.ctype.byname/tolower_many.pass.cpp |  1 +
 .../locale.ctype.byname/toupper_1.pass.cpp    |  1 +
 .../locale.ctype.byname/toupper_many.pass.cpp |  1 +
 .../locale.ctype.byname/widen_1.pass.cpp      |  1 +
 .../locale.ctype.byname/widen_many.pass.cpp   |  1 +
 .../get_long_double_en_US.pass.cpp            |  1 +
 .../get_long_double_zh_CN.pass.cpp            |  1 +
 .../get_string_en_US.pass.cpp                 |  1 +
 .../put_long_double_en_US.pass.cpp            |  1 +
 .../put_long_double_zh_CN.pass.cpp            |  1 +
 .../put_string_en_US.pass.cpp                 |  1 +
 .../locale/locale.cons/default.pass.cpp       |  1 +
 .../test/std/re/re.traits/transform.pass.cpp  |  1 +
 .../re/re.traits/transform_primary.pass.cpp   |  1 +
 .../thread_create_failure.pass.cpp            |  1 +
 .../thread.jthread/join.deadlock.pass.cpp     |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 libcxx/test/support/count_new.h               | 28 +++++++++++++++++++
 libcxx/test/support/filesystem_test_helper.h  |  4 ++-
 libcxx/test/support/test_macros.h             |  4 ++-
 libunwind/test/aarch64_za_unwind.pass.cpp     | 13 +++++----
 80 files changed, 132 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index a20ed9ce9c6a9..542bc0cc83a1f 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -7,7 +7,8 @@ config.substitutions.append(('%{flags}',
     ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
 ))
 config.substitutions.append(('%{compile_flags}',
-    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
 ))
 config.substitutions.append(('%{link_flags}',
     # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
@@ -24,6 +25,12 @@ import libcxx.test.params, libcxx.test.config
 
 config.available_features.add('LIBCXX-OHOS-FIXME')
 config.available_features.add('ohos-hdc')
+config.available_features.add('executor-has-no-stdin')
+config.available_features.add('executor-merges-stderr-into-stdout')
+config.available_features.add('executor-runs-as-root')
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
index 20a545b719ed6..f04a6bce76691 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
index 03e9de882ab0a..428d30804a4c3 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
index 10829f8a73db1..c8a1c34aa3028 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index 071ee7f6c891f..44b94528f5e32 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a hard link.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
index 5d931891dd0eb..d291ca2cb5641 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
index 82b94a3d6bf5f..e4bc9c41728da 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
index 8401dbd38cd71..e0fa44ffbc4a5 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
index ac5499cdd6f20..2bcc64a3c2651 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_assignable<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
index 1f1843f7d401d..5efdf983f8a10 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_constructible<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
index 08d7ff84378d5..a3768b8cbd83f 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
index 1f2293989b7ef..9e8a70b4339d4 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // On Android L, ~scoped_test_env() is unable to delete the temp dir using
 // chmod+rm because chmod is too broken.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
index 56465d01021ad..62dc696d6797d 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
index fad880ecf30f7..708fb511dce73 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
index 12bc0c198c062..69e357adeafb5 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
index f1b559cf2d55b..0ed4aa0dc8d58 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
index f3161f2e020d2..579c03f2b8c0b 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
index fbf5e7129f7af..1d5312d65be46 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
index d0974640429df..7a3a56a0b161c 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
index 4a5882e36cde2..8b877ff63b112 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
index 382685c21dd99..995125d702de3 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
index 07b446d80c402..692fe773a0f9e 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
index c420fea21aa66..22c2c114ba687 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
index 56b4a27de6b42..8064feaaf2065 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
index b30d56685e2cb..540849ccefc05 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
index 425c224606038..02ff1e249d3bc 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
index 1d405a816e110..41474f9ca1e48 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
index 38e14438f3fd3..9a891eab6b343 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
index 88672851f7b6b..3e1d34ab5257a 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
index d0ff2c87689d1..4c299b876639e 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
index 98fa56b7f4b6d..7970887bac199 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
index 283a8fddb8e8f..13e6f74c5a143 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -17,6 +17,9 @@
 // fmemopen is available starting in Android M (API 23)
 // XFAIL: target={{.+}}-android{{(eabi)?(21|22)}}
 
+// OHOS fmemopen/FILE behavior does not currently match the expectation tested here.
+// XFAIL: LIBCXX-OHOS-FIXME
+
 // <print>
 
 // The FILE returned by fmemopen does not have file descriptor.
diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
index b24c0b90d86f1..2ddddbef70dbb 100644
--- a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
@@ -18,6 +18,7 @@
 // XFAIL: target=armv{{7|8}}{{l?}}{{.*}}-linux-gnueabihf && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-linux-gnu && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-amazon-linux && libcpp-abi-version=1
+// XFAIL: target={{.*}}-linux-ohos && libcpp-abi-version=1
 
 #include <iomanip>
 #include <ostream>
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
index da5563f87df45..ea5af90a90822 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
index 4f23b6b9a5d87..6dc0ff36ff4bf 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
@@ -14,6 +14,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
index 9bedd67e816ef..cbb9639766207 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
index ec4c8a009ba0c..59d93e74db3b7 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
index ae82035ccfc62..49a6ce0892ea4 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
index 6ae1972234b2b..58a6e18340ddf 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
index 08b74b999bbf4..88dcfe0485429 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
index 89e178248ae50..9c9745dcc9777 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wclog;
 
 // XFAIL: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
index 0e411f8adc0f2..e563152f2c8cd 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
index 026250daeee89..34119303a29ba 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete [] is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
index ca0f612bf7578..a4e32aaa11e13 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
index fbc9cf070bcba..3ec2b47b53eb0 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
index 8ae6bc2d3ba66..7cfd9f955fce7 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
@@ -8,6 +8,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
index 4978db116ef39..743075ede8ece 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
index 7be7e8d574e6c..b77a4409522f0 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
@@ -11,6 +11,7 @@
 // template <> class ctype<char>
 
 // ~ctype();
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <locale>
 #include <cassert>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
index 5cebc079c4703..c4803c04d173b 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <type_traits>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
index 2869378b42b92..5174ffebf281f 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
index ea03f042f2d45..665da7323e38a 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
index 2f7acb843e591..5d87c081b6e1c 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
index 447353de8af72..93d93aa9228f7 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
index 94ecc6c8fa85b..ab448a01632d6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
index 2b37737a26bc3..4be990e5b88c6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
index 016d8d736ad22..e0a3912a9315e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
index 959a4be9e1de9..b22629c82444e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
index 078b4a6fefb7e..c2cd904808794 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
index 9997b07134563..73b7a6decda36 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
index c86df7e6b53bf..deb30d747653f 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
index 478df7964f6d2..ee6fbd48b09b6 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
index 4b767fae871fa..c476537c3c7e3 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
index 25046a8417083..8174a724d4165 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
index 1c8710a008f27..e23cb4bf5f04a 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
index f0086ada30303..a36e3af8d12eb 100644
--- a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
+++ b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
@@ -12,6 +12,7 @@
 
 // This test runs in C++20, but we have deprecated codecvt<char(16|32), char, mbstate_t> in C++20.
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // <locale>
 
diff --git a/libcxx/test/std/re/re.traits/transform.pass.cpp b/libcxx/test/std/re/re.traits/transform.pass.cpp
index 80cd3f01faff2..112ecd47ff4e2 100644
--- a/libcxx/test/std/re/re.traits/transform.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform.pass.cpp
@@ -8,6 +8,7 @@
 //
 // NetBSD does not support LC_COLLATE at the moment
 // XFAIL: netbsd
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..cbc9387facb30 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -11,6 +11,7 @@
 
 // XFAIL: LIBCXX-AIX-FIXME
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
index 40daa49393bd4..110d0ea9fd694 100644
--- a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
+++ b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
@@ -15,6 +15,7 @@
 
 // There is no way to limit the number of threads on windows
 // UNSUPPORTED: windows
+// UNSUPPORTED: executor-runs-as-root
 
 // macOS and FreeBSD seem to limit the number of processes, not threads via RLIMIT_NPROC.
 // XFAIL: target={{.+}}-apple-{{.*}}
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
index c6eb0694ab2e1..19573ae52fd71 100644
--- a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
@@ -9,6 +9,7 @@
 // Windows cannot detect the deadlock. Instead of throwing system_error,
 // it would dead lock the test
 // UNSUPPORTED: windows
+// UNSUPPORTED: pthread-join-self-deadlocks
 
 // TSAN bug: https://llvm.org/PR66537
 // UNSUPPORTED: tsan
diff --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
index 51be78e1a02f8..804036e425572 100644
--- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
@@ -14,6 +14,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..6901aff00bc4e 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -415,6 +415,13 @@ void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteCalled(p);
+  std::free(p);
+}
+#  endif
+
 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->newArrayCalled(s);
@@ -449,6 +456,13 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteArrayCalled(p);
+  std::free(p);
+}
+#  endif
+
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
 #      define USE_ALIGNED_ALLOC
@@ -519,6 +533,13 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
@@ -551,6 +572,13 @@ void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
 
 #endif // DISABLE_NEW_COUNT
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index 2ad9efb32c60f..f820a4a0fa915 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -182,7 +182,7 @@ struct scoped_test_env
         std::string cmd = "chmod -R 777 " + test_root.string();
 #endif // defined(__MVS__)
         int ret = std::system(cmd.c_str());
-#  if !defined(_AIX) && !defined(__ANDROID__)
+#  if !defined(_AIX) && !defined(__ANDROID__) && !defined(__OHOS__)
         // On AIX the chmod command will return non-zero when trying to set
         // the permissions on a directory that contains a bad symlink. This triggers
         // the assert, despite being able to delete everything with the following
@@ -196,6 +196,8 @@ struct scoped_test_env
         // LIBCXX-ANDROID-FIXME: Other fixes to consider: place a toybox chmod
         // onto old devices, re-enable this assert for devices running Android N
         // and up, rewrite this chmod+rm in C or C++.
+        //
+        // OHOS has the same chmod -R behavior for bad symlinks.
         assert(ret == 0);
 #  endif
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..fdf6bc5b18d72 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -375,7 +375,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NOT_WIN32(...) __VA_ARGS__
 #endif
 
-#if defined(TEST_WINDOWS_DLL) ||defined(__MVS__) || defined(_AIX)
+#if defined(TEST_WINDOWS_DLL) || defined(__MVS__) || defined(_AIX) || defined(TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS)
 // Macros for waiving cases when we can't count allocations done within
 // the library implementation.
 //
@@ -386,6 +386,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 //
 // The same goes on IBM zOS.
 // The same goes on AIX.
+// Some target test configurations can define
+// TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS for the same reason.
 #define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
 #define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
 #else
diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp
index 36536860c1a96..b28f70a6e8e7a 100644
--- a/libunwind/test/aarch64_za_unwind.pass.cpp
+++ b/libunwind/test/aarch64_za_unwind.pass.cpp
@@ -20,6 +20,9 @@
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
 #endif
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
 
 // Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA
 // (and commits a lazy save of ZA) before resuming from unwinding.
@@ -33,18 +36,16 @@ static bool checkHasSME() {
     return false;
   return has_sme != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = getauxval(AT_HWCAP2);
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = 0;
   elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
 #else
 static bool checkHasSME() {

>From bbaa18370c45aaf79c2446be0a6e94dc2aa0ef00 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Fri, 3 Jul 2026 10:33:38 +0800
Subject: [PATCH 4/6] Add qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  16 +-
 libcxx/utils/ci/run-buildbot                  |  41 ++++-
 libcxx/utils/ci/vendor/ohos/qemu_run.py       | 144 ++++++++++++++++++
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |   8 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |   8 +-
 5 files changed, 204 insertions(+), 13 deletions(-)
 create mode 100755 libcxx/utils/ci/vendor/ohos/qemu_run.py

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 542bc0cc83a1f..20aa7a49d5802 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++ using HDC on OHOS.
+# libc++ using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -23,15 +23,21 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
 config.available_features.add('LIBCXX-OHOS-FIXME')
-config.available_features.add('ohos-hdc')
-config.available_features.add('executor-has-no-stdin')
-config.available_features.add('executor-merges-stderr-into-stdout')
-config.available_features.add('executor-runs-as-root')
+config.available_features.add(executor_kind)
 config.available_features.add('platform-has-nonportable-locales')
 config.available_features.add('platform-has-unreliable-new-delete-interposition')
 config.available_features.add('pthread-join-self-deadlocks')
 
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
     libcxx.test.features.DEFAULT_FEATURES,
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index ecf0ca17ae148..40739b0fba4d8 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -28,6 +28,16 @@ ${PROGNAME} [options] <BUILDER>
 --build-dir <DIR>   The directory to use for building the library. By default,
                     this is '<llvm-root>/build/<builder>'.
 
+--ohos-qemu <PATH>  Run OHOS tests under qemu-aarch64 instead of HDC. This also
+                    accepts OHOS_QEMU or QEMU from the environment.
+
+--ohos-qemu-sysroot <DIR>
+                    Sysroot passed to qemu-aarch64 with -L. Defaults to
+                    OHOS_QEMU_SYSROOT, QEMU_SYSROOT, then OHOS_SYSROOT.
+
+--ohos-hdc <PATH>   HDC binary to use when OHOS tests run on a device. This sets
+                    HDC for the OHOS HDC executor.
+
 Environment variables
 CC                  The C compiler to use, this value is used by CMake. This
                     variable is mandatory.
@@ -84,6 +94,19 @@ while [[ $# -gt 0 ]]; do
             BUILD_DIR="${2}"
             shift; shift
             ;;
+        --ohos-qemu)
+            OHOS_QEMU="${2}"
+            shift; shift
+            ;;
+        --ohos-qemu-sysroot)
+            OHOS_QEMU_SYSROOT="${2}"
+            shift; shift
+            ;;
+        --ohos-hdc)
+            HDC="${2}"
+            export HDC
+            shift; shift
+            ;;
         *)
             BUILDER="${1}"
             shift
@@ -193,8 +216,18 @@ function setup-ohos-hdc-env() {
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
     OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
-    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
-    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+    OHOS_QEMU="${OHOS_QEMU:-${QEMU:-}}"
+
+    if [[ -n "${OHOS_QEMU}" ]]; then
+        OHOS_QEMU_SYSROOT="${OHOS_QEMU_SYSROOT:-${QEMU_SYSROOT:-${OHOS_SYSROOT}}}"
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/qemu_run.py --qemu ${OHOS_QEMU} --sysroot ${OHOS_QEMU_SYSROOT}"
+        OHOS_EXECUTOR_KIND="ohos-qemu"
+    else
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+        OHOS_EXECUTOR_KIND="ohos-hdc"
+    fi
+
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE};executor_kind=${OHOS_EXECUTOR_KIND}"
 }
 
 function generate-cmake-ohos-hdc() {
@@ -863,14 +896,14 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
-ohos-hdc-aarch64-full)
+ohos-aarch64-full|ohos-hdc-aarch64-full)
     clean
 
     generate-cmake-ohos-hdc
 
     check-runtimes
 ;;
-ohos-hdc-aarch64-smoke)
+ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
     clean
 
     generate-cmake-ohos-hdc
diff --git a/libcxx/utils/ci/vendor/ohos/qemu_run.py b/libcxx/utils/ci/vendor/ohos/qemu_run.py
new file mode 100755
index 0000000000000..ef7eef7872864
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/qemu_run.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _resolve_binary(binary):
+    if os.path.isabs(binary):
+        if os.path.isfile(binary) and os.access(binary, os.X_OK):
+            return binary
+        sys.exit(f"failed to find executable: {binary}")
+
+    resolved = shutil.which(binary)
+    if resolved is None:
+        sys.exit(f"failed to find executable: {binary}")
+    return resolved
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(str(parent))
+    return library_dirs
+
+
+def _build_target_env_args(env, prepend_env):
+    target_env = dict(env)
+
+    for key, value in prepend_env.items():
+        existing = target_env.get(key, os.environ.get(key, ""))
+        target_env[key] = f"{value}{os.pathsep}{existing}" if existing else value
+
+    args = []
+    for key, value in target_env.items():
+        args.extend(["-E", f"{key}={value}"])
+    return args
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--qemu",
+        default=os.environ.get("OHOS_QEMU", os.environ.get("QEMU", "qemu-aarch64")),
+    )
+    parser.add_argument("--sysroot", required=True)
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(
+            os.environ.get(
+                "OHOS_QEMU_TIMEOUT_SECONDS",
+                os.environ.get("QEMU_TIMEOUT_SECONDS", "600"),
+            )
+        ),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    qemu = _resolve_binary(args.qemu)
+    execdir = Path(args.execdir).resolve()
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    build_library_dirs = _collect_build_library_dirs(execdir)
+    if build_library_dirs:
+        existing = prepend_env.get("LD_LIBRARY_PATH")
+        build_library_path = os.pathsep.join(build_library_dirs)
+        prepend_env["LD_LIBRARY_PATH"] = (
+            f"{build_library_path}{os.pathsep}{existing}"
+            if existing
+            else build_library_path
+        )
+
+    cmd = [
+        qemu,
+        "-L",
+        args.sysroot,
+        *_build_target_env_args(env, prepend_env),
+        *args.command,
+    ]
+    try:
+        completed = subprocess.run(
+            cmd,
+            cwd=execdir,
+            timeout=args.timeout_seconds,
+            check=False,
+        )
+    except subprocess.TimeoutExpired:
+        print(
+            f"qemu executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+            file=sys.stderr,
+        )
+        return 124
+    return completed.returncode
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index d18748fe3a3cc..8f5a17b3dec64 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++abi using HDC on OHOS.
+# libc++abi using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -20,7 +20,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index bce241a7898cf..f2a032c41a123 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -1,6 +1,6 @@
 #
 # Configuration file for running the libunwind tests against the shared library
-# using HDC on OHOS.
+# using HDC or QEMU on OHOS.
 #
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
@@ -45,7 +45,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,

>From dde4d430033711fb7ffa87621ba86b37492f480b Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 14:52:40 +0800
Subject: [PATCH 5/6] Fix something for qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in                   | 4 +++-
 .../file.streams/fstreams/filebuf.members/close.pass.cpp      | 3 +++
 .../fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp     | 2 ++
 .../syncstream/osyncstream/thread/several_threads.pass.cpp    | 2 ++
 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in             | 4 +++-
 libcxxabi/test/guard_threaded_test.pass.cpp                   | 2 ++
 libunwind/test/bad_unwind_info.pass.cpp                       | 2 ++
 libunwind/test/configs/llvm-libunwind-ohos.cfg.in             | 4 +++-
 libunwind/test/signal_unwind.pass.cpp                         | 2 ++
 9 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 20aa7a49d5802..3be1c267ede2a 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -23,7 +23,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
index 2c4cfb0ac7661..ce802d6ceba78 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14
 // REQUIRES: linux
 // UNSUPPORTED: no-filesystem
+// OHOS qemu-user observes the host /proc, not a real OHOS process /proc.
+// UNSUPPORTED: ohos-qemu
 // XFAIL: no-localization
 
 // <filesystem>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..77db6396755bf 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -10,6 +10,8 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
+// This thread-heavy stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 // <syncstream>
 
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index 8f5a17b3dec64..d0a8d1248f88a 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -20,7 +20,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxxabi/test/guard_threaded_test.pass.cpp b/libcxxabi/test/guard_threaded_test.pass.cpp
index d898e60288aca..a4e14f0aca9e4 100644
--- a/libcxxabi/test/guard_threaded_test.pass.cpp
+++ b/libcxxabi/test/guard_threaded_test.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: no-exceptions
+// This thread-heavy guard stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 #define TESTING_CXA_GUARD
 #include "../src/cxa_guard_impl.h"
diff --git a/libunwind/test/bad_unwind_info.pass.cpp b/libunwind/test/bad_unwind_info.pass.cpp
index 332b661d2e98f..76102505f2d67 100644
--- a/libunwind/test/bad_unwind_info.pass.cpp
+++ b/libunwind/test/bad_unwind_info.pass.cpp
@@ -13,6 +13,8 @@
 // REQUIRES: target={{(aarch64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
 // UNSUPPORTED: target={{.*-apple.*}}
+// OHOS qemu-user does not reliably model this invalid unwind-info path.
+// UNSUPPORTED: ohos-qemu
 
 // GCC doesn't support __attribute__((naked)) on AArch64.
 // UNSUPPORTED: gcc
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index f2a032c41a123..71d1e711e36d5 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -45,7 +45,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp
index f8798bc3eabeb..ef73a3ea8c59a 100644
--- a/libunwind/test/signal_unwind.pass.cpp
+++ b/libunwind/test/signal_unwind.pass.cpp
@@ -10,6 +10,8 @@
 // Ensure that the unwinder can cope with the signal handler.
 // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
+// OHOS qemu-user does not provide device-equivalent signal unwinding behavior.
+// UNSUPPORTED: ohos-qemu
 
 // TODO: Figure out why this fails with Memory Sanitizer.
 // XFAIL: msan

>From c81df7252fb00b5b9b41f0a29393e775abc9c810 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 15:19:29 +0800
Subject: [PATCH 6/6] delete run-smoke-test

---
 libcxx/utils/ci/buildkite-pipeline.yml | 12 ----------
 libcxx/utils/ci/run-buildbot           | 31 --------------------------
 2 files changed, 43 deletions(-)

diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 77617f43ae6dc..70bc69637a7f2 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -175,18 +175,6 @@ steps:
 
 - group: OHOS
   steps:
-  - label: OHOS AArch64 HDC Smoke
-    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
-    env:
-      CC: clang
-      CXX: clang++
-      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
-      HDC_TIMEOUT_SECONDS: 240
-    agents:
-      queue: libcxx-builders
-      os: linux
-      device: ohos-hdc
-    <<: *common
   - label: OHOS AArch64 HDC Full
     command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
     env:
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 40739b0fba4d8..e7d751df484e4 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -147,23 +147,6 @@ function require-env-var() {
     fi
 }
 
-function detect-lit() {
-    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
-        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
-    elif command -v llvm-lit >/dev/null 2>&1; then
-        LIT_RUNNER=("$(command -v llvm-lit)")
-    else
-        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
-    fi
-}
-
-function run-smoke-test() {
-    local test_dir="${1}"
-    local filter="${2}"
-    step "Running smoke test ${filter}"
-    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
-}
-
 function ohos-lit-args() {
     local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
     if python3 -c 'import psutil' >/dev/null 2>&1; then
@@ -903,20 +886,6 @@ ohos-aarch64-full|ohos-hdc-aarch64-full)
 
     check-runtimes
 ;;
-ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
-    clean
-
-    generate-cmake-ohos-hdc
-
-    step "Building the OHOS smoke test dependencies"
-    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
-
-    detect-lit
-    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
-;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #


From libcxx-commits at lists.llvm.org  Thu Jul  9 01:04:49 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 01:04:49 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a4f5621.9edb5c9d.208698.3dab@mx.google.com>


https://github.com/huihui15537226823 updated https://github.com/llvm/llvm-project/pull/208390

>From 70e8ac839667f8aba4aa74a2dd19614426f9754f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 24 Jun 2026 09:47:00 +0800
Subject: [PATCH 1/6] Add OHOS HDC runtimes CI support

---
 cmake/Modules/GetToolchainDirs.cmake          |  37 +-
 .../cmake/Modules/CompilerRTUtils.cmake       |   8 +-
 compiler-rt/cmake/builtin-config-ix.cmake     |   2 +-
 compiler-rt/cmake/crt-config-ix.cmake         |   2 +-
 compiler-rt/lib/builtins/crtbegin.c           |   9 +
 compiler-rt/lib/builtins/emutls.c             |   8 +-
 libcxx/CMakeLists.txt                         |  10 +-
 libcxx/cmake/config-ix.cmake                  |   2 +
 libcxx/include/__config                       |   5 +-
 libcxx/include/__cxx03/__config               |   5 +-
 .../__cxx03/__locale_dir/locale_base_api.h    |   2 +-
 libcxx/include/__locale_dir/locale_base_api.h |   2 +-
 libcxx/src/include/config_elast.h             |   2 +
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  48 ++
 libcxx/utils/ci/buildkite-pipeline.yml        |  27 +
 libcxx/utils/ci/run-buildbot                  |  87 ++++
 libcxx/utils/ci/vendor/ohos/hdc_run.py        | 472 ++++++++++++++++++
 libcxx/utils/ci/vendor/ohos/hdc_run_test.py   | 115 +++++
 libcxxabi/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |  30 ++
 libunwind/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |  55 ++
 22 files changed, 910 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/test/configs/llvm-libc++-ohos.cfg.in
 create mode 100755 libcxx/utils/ci/vendor/ohos/hdc_run.py
 create mode 100644 libcxx/utils/ci/vendor/ohos/hdc_run_test.py
 create mode 100644 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
 create mode 100644 libunwind/test/configs/llvm-libunwind-ohos.cfg.in

diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..a20ed9ce9c6a9
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,48 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index d4107cf11ad51..77617f43ae6dc 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -172,3 +172,30 @@ steps:
       queue: libcxx-builders
       os: android
     <<: *common
+
+- group: OHOS
+  steps:
+  - label: OHOS AArch64 HDC Smoke
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
+  - label: OHOS AArch64 HDC Full
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 6376bc1d3dd5a..1867e9c620649 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -116,6 +116,41 @@ function clean() {
     rm -rf "${BUILD_DIR}"
 }
 
+function require-env-var() {
+    local name="${1}"
+    if [[ -z ${!name+x} ]] || [[ -z "${!name}" ]]; then
+        error "Environment variable ${name} must be defined"
+        exit 1
+    fi
+}
+
+function detect-lit() {
+    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
+        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
+    elif command -v llvm-lit >/dev/null 2>&1; then
+        LIT_RUNNER=("$(command -v llvm-lit)")
+    else
+        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
+    fi
+}
+
+function run-smoke-test() {
+    local test_dir="${1}"
+    local filter="${2}"
+    step "Running smoke test ${filter}"
+    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
+}
+
+function ohos-lit-args() {
+    local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
+    if python3 -c 'import psutil' >/dev/null 2>&1; then
+        args="${args} --timeout=1500"
+    else
+        echo "warning: python3 psutil not available; running OHOS lit without per-test timeout" >&2
+    fi
+    printf '%s' "${args}"
+}
+
 function generate-cmake-base() {
     step "Generating CMake"
 
@@ -153,6 +188,37 @@ function generate-cmake-android() {
           "${@}"
 }
 
+function setup-ohos-hdc-env() {
+    require-env-var OHOS_SYSROOT
+
+    OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+}
+
+function generate-cmake-ohos-hdc() {
+    setup-ohos-hdc-env
+
+    local lit_args
+    lit_args="$(ohos-lit-args)"
+
+    generate-cmake \
+          -DLLVM_LIT_ARGS="${lit_args}" \
+          -DCMAKE_SYSTEM_NAME=OHOS \
+          -DCMAKE_SYSROOT="${OHOS_SYSROOT}" \
+          -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
+          -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
+          -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
+          -DLIBUNWIND_TEST_CONFIG="llvm-libunwind-ohos.cfg.in" \
+          -DLIBCXX_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBCXXABI_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBUNWIND_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          "${@}"
+}
+
 function check-runtimes() {
     step "Building libc++ test dependencies"
     ninja -vC "${BUILD_DIR}" cxx-test-depends
@@ -795,6 +861,27 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
+ohos-hdc-aarch64-full)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    check-runtimes
+;;
+ohos-hdc-aarch64-smoke)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    step "Building the OHOS smoke test dependencies"
+    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
+
+    detect-lit
+    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
+;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run.py b/libcxx/utils/ci/vendor/ohos/hdc_run.py
new file mode 100755
index 0000000000000..61a3899b88b11
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run.py
@@ -0,0 +1,472 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import uuid
+
+
+EXIT_MARKER = "__llvm_hdc_exit__="
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _split_path_list(value):
+    return [entry for entry in value.split(os.pathsep) if entry]
+
+
+def _resolve_hdc_binary(hdc):
+    if os.path.isabs(hdc):
+        return hdc
+    resolved = shutil.which(hdc)
+    if resolved is None:
+        sys.exit(f"failed to find hdc binary: {hdc}")
+    return resolved
+
+
+def _is_windows_hdc(path):
+    return path.lower().endswith(".exe")
+
+
+def _find_windows_local_root(hdc_path):
+    parts = Path(hdc_path).parts
+    for index in range(len(parts) - 1):
+        if parts[index] == "AppData" and index + 1 < len(parts) and parts[index + 1] == "Local":
+            return Path(*parts[: index + 2])
+    return None
+
+
+def _default_host_staging_root(hdc_path):
+    if _is_windows_hdc(hdc_path):
+        local_root = _find_windows_local_root(hdc_path)
+        if local_root is None:
+            sys.exit(
+                "failed to derive a Windows staging directory from the hdc.exe path; "
+                "set HDC_HOST_STAGING_ROOT to a path under /mnt/<drive>/..."
+            )
+        return local_root / "Temp" / "llvm-lit-hdc"
+    return Path("/tmp/llvm-lit-hdc")
+
+
+def _to_windows_path(path):
+    resolved = Path(path).resolve()
+    parts = resolved.parts
+    if len(parts) < 4 or parts[1] != "mnt":
+        sys.exit(
+            f"Windows hdc requires a staging path under /mnt/<drive>/..., got: {resolved}"
+        )
+    drive = parts[2].upper()
+    tail = "\\".join(parts[3:])
+    return f"{drive}:\\{tail}" if tail else f"{drive}:\\"
+
+
+def _run_readelf(path):
+    completed = subprocess.run(
+        ["readelf", "-d", path],
+        check=False,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        text=True,
+    )
+    if completed.returncode != 0:
+        return ""
+    return completed.stdout
+
+
+def _read_elf_metadata(path):
+    needed = []
+    runpath_dirs = []
+    seen_dirs = set()
+    for line in _run_readelf(path).splitlines():
+        needed_match = re.search(r"\(NEEDED\).*Shared library: \[(.*)\]", line)
+        if needed_match:
+            needed.append(needed_match.group(1))
+            continue
+
+        runpath_match = re.search(
+            r"\((?:RUNPATH|RPATH)\).*Library runpath: \[(.*)\]", line
+        )
+        if not runpath_match:
+            continue
+        for entry in _split_path_list(runpath_match.group(1)):
+            candidate = Path(entry)
+            if not candidate.is_dir():
+                continue
+            key = str(candidate.resolve())
+            if key in seen_dirs:
+                continue
+            seen_dirs.add(key)
+            runpath_dirs.append(candidate)
+    return needed, runpath_dirs
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(parent)
+    return library_dirs
+
+
+def _find_dependency(name, search_dirs):
+    for directory in search_dirs:
+        candidate = Path(directory) / name
+        if candidate.is_file():
+            return candidate.resolve()
+    return None
+
+
+def _copy_needed_libraries(executables, execdir, staging_execdir):
+    search_dirs = _collect_build_library_dirs(execdir)
+    queue = [Path(exe).resolve() for exe in executables]
+    visited = set()
+    copied_names = {path.name for path in staging_execdir.glob("*") if path.is_file()}
+
+    while queue:
+        current = queue.pop(0)
+        current_key = str(current)
+        if current_key in visited:
+            continue
+        visited.add(current_key)
+
+        needed, runpath_dirs = _read_elf_metadata(current)
+        current_search_dirs = []
+        current_seen = set()
+        for directory in [*runpath_dirs, current.parent, *search_dirs]:
+            resolved = Path(directory).resolve()
+            key = str(resolved)
+            if key in current_seen:
+                continue
+            current_seen.add(key)
+            current_search_dirs.append(resolved)
+
+        for directory in runpath_dirs:
+            resolved = directory.resolve()
+            if all(str(resolved) != str(existing) for existing in search_dirs):
+                search_dirs.append(resolved)
+
+        for lib_name in needed:
+            dependency = _find_dependency(lib_name, current_search_dirs)
+            if dependency is None:
+                continue
+            if lib_name not in copied_names:
+                shutil.copy2(dependency, staging_execdir / lib_name)
+                copied_names.add(lib_name)
+            queue.append(dependency)
+
+
+def _copy_execdir(execdir, staging_execdir):
+    if staging_execdir.exists():
+        shutil.rmtree(staging_execdir)
+    shutil.copytree(execdir, staging_execdir)
+
+
+def _iter_files(root):
+    for directory, dirnames, filenames in os.walk(root):
+        dirnames.sort()
+        filenames.sort()
+        yield Path(directory), filenames
+
+
+def _is_test_executable(path):
+    return path.endswith(".tmp.exe") and os.path.exists(path)
+
+
+def _map_local_path(path, local_execdir, remote_execdir):
+    normalized = os.path.normpath(path)
+    execdir = os.path.normpath(local_execdir)
+    if normalized == execdir:
+        return remote_execdir
+    prefix = execdir + os.sep
+    if normalized.startswith(prefix):
+        suffix = normalized[len(prefix) :].replace(os.sep, "/")
+        return f"{remote_execdir}/{suffix}" if suffix else remote_execdir
+    return path
+
+
+def _map_value(value, local_execdir, remote_execdir):
+    return _map_local_path(value, local_execdir, remote_execdir)
+
+
+def _build_env_exports(env, prepend_env, local_execdir, remote_execdir, add_remote_libdir):
+    commands = []
+    mapped_env = {
+        key: _map_value(value, local_execdir, remote_execdir) for key, value in env.items()
+    }
+    mapped_prepend = {
+        key: _map_value(value, local_execdir, remote_execdir)
+        for key, value in prepend_env.items()
+    }
+
+    if add_remote_libdir:
+        existing = mapped_prepend.get("LD_LIBRARY_PATH")
+        mapped_prepend["LD_LIBRARY_PATH"] = (
+            f"{remote_execdir}:{existing}" if existing else remote_execdir
+        )
+
+    for key, value in mapped_env.items():
+        commands.append(f"export {key}={shlex.quote(value)}")
+    for key, value in mapped_prepend.items():
+        commands.append(f"export {key}={shlex.quote(value)}${{{key}:+:{key}}}")
+    return commands
+
+
+def _hdc_prefix(hdc_path, server, target):
+    prefix = [hdc_path]
+    if server:
+        prefix.extend(["-s", server])
+    if target:
+        prefix.extend(["-t", target])
+    return prefix
+
+
+def _run_hdc(
+    prefix,
+    args,
+    *,
+    timeout=None,
+    capture_output=False,
+    stdin=subprocess.DEVNULL,
+    check=True,
+    expect_stdout=None,
+):
+    kwargs = {
+        "check": False,
+        "timeout": timeout,
+        "stdin": stdin,
+        "text": True,
+    }
+    if capture_output:
+        kwargs["stdout"] = subprocess.PIPE
+        kwargs["stderr"] = subprocess.PIPE
+    completed = subprocess.run(prefix + args, **kwargs)
+    output = completed.stdout if capture_output and completed.stdout else ""
+    error_output = completed.stderr if capture_output and completed.stderr else ""
+    if check and completed.returncode != 0:
+        raise RuntimeError(
+            f"hdc command failed with exit code {completed.returncode}: "
+            f"{shlex.join(prefix + args)}\n{output}{error_output}"
+        )
+    if check and expect_stdout is not None and expect_stdout not in output:
+        raise RuntimeError(
+            f"hdc command did not report success: {shlex.join(prefix + args)}\n{output}"
+        )
+    return completed
+
+
+def _run_checked_shell(prefix, command, *, timeout=None):
+    completed = _run_hdc(
+        prefix,
+        [
+            "shell",
+            f"{command}; rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"",
+        ],
+        timeout=timeout,
+        capture_output=True,
+    )
+    exit_code, output = _extract_exit_code(completed.stdout)
+    if exit_code is None:
+        raise RuntimeError(
+            f"failed to parse shell exit code for: {command}\n{completed.stdout}"
+        )
+    if exit_code != 0:
+        raise RuntimeError(
+            f"remote shell command failed with exit code {exit_code}: {command}\n{output}"
+        )
+    return output
+
+
+def _sync_tree_to_device(prefix, local_root, remote_root, use_windows_paths):
+    _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_root)}")
+    _run_checked_shell(prefix, f"mkdir -p {shlex.quote(remote_root)}")
+
+    for directory, filenames in _iter_files(local_root):
+        relative_dir = os.path.relpath(directory, local_root)
+        device_dir = (
+            remote_root
+            if relative_dir == "."
+            else f"{remote_root}/{relative_dir.replace(os.sep, '/')}"
+        )
+        _run_checked_shell(prefix, f"mkdir -p {shlex.quote(device_dir)}")
+        for name in filenames:
+            local_path = directory / name
+            device_path = f"{device_dir}/{name}"
+            source = _to_windows_path(local_path) if use_windows_paths else str(local_path)
+            _run_hdc(
+                prefix,
+                ["file", "send", source, device_path],
+                capture_output=True,
+                expect_stdout="FileTransfer finish",
+            )
+            if name.endswith(".tmp.exe") or os.access(local_path, os.X_OK):
+                _run_checked_shell(prefix, f"chmod +x {shlex.quote(device_path)}")
+
+
+def _build_remote_command(command, local_execdir, remote_execdir, exports):
+    mapped_command = [
+        _map_local_path(arg, local_execdir, remote_execdir) for arg in command
+    ]
+    pieces = [f"cd {shlex.quote(remote_execdir)}"]
+    pieces.extend(exports)
+    pieces.append(shlex.join(mapped_command))
+    pieces.append(f"rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"")
+    return " && ".join(pieces[:-2]) + f"; {pieces[-2]}; {pieces[-1]}"
+
+
+def _extract_exit_code(output):
+    match = re.search(rf"^(.*)\n{EXIT_MARKER}(\d+)\s*$", output, re.DOTALL)
+    if not match:
+        return None, output
+    return int(match.group(2)), match.group(1)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--hdc", default=os.environ.get("HDC", "hdc"))
+    parser.add_argument("--hdc-server", default=os.environ.get("HDC_SERVER_IP_PORT"))
+    parser.add_argument("--hdc-target", default=os.environ.get("HDC_UTID"))
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--remote-base-dir",
+        default="/data/local/tmp/llvm-lit-hdc",
+    )
+    parser.add_argument(
+        "--host-staging-root",
+        default=os.environ.get("HDC_HOST_STAGING_ROOT"),
+    )
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(os.environ.get("HDC_TIMEOUT_SECONDS", "120")),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("--keep-remote-dir", action="store_true")
+    parser.add_argument("--keep-host-staging", action="store_true")
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    hdc_path = _resolve_hdc_binary(args.hdc)
+    use_windows_paths = _is_windows_hdc(hdc_path)
+    host_staging_root = (
+        Path(args.host_staging_root)
+        if args.host_staging_root
+        else _default_host_staging_root(hdc_path)
+    )
+    prefix = _hdc_prefix(hdc_path, args.hdc_server, args.hdc_target)
+
+    execdir = Path(args.execdir).resolve()
+    run_id = uuid.uuid4().hex[:12]
+    staging_execdir = host_staging_root / f"run-{run_id}" / execdir.name
+    remote_execdir = f"{args.remote_base_dir}/run-{run_id}/{execdir.name}"
+
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    executables = [arg for arg in args.command if _is_test_executable(arg)]
+    host_staging_root.mkdir(parents=True, exist_ok=True)
+    _copy_execdir(execdir, staging_execdir)
+    _copy_needed_libraries(executables, execdir, staging_execdir)
+
+    add_remote_libdir = any(staging_execdir.glob("*.so*"))
+    exports = _build_env_exports(
+        env,
+        prepend_env,
+        str(execdir),
+        remote_execdir,
+        add_remote_libdir,
+    )
+
+    try:
+        _sync_tree_to_device(prefix, staging_execdir, remote_execdir, use_windows_paths)
+        remote_command = _build_remote_command(
+            args.command,
+            str(execdir),
+            remote_execdir,
+            exports,
+        )
+        try:
+            completed = _run_hdc(
+                prefix,
+                ["shell", remote_command],
+                timeout=args.timeout_seconds,
+                capture_output=True,
+                stdin=None,
+            )
+        except subprocess.TimeoutExpired:
+            print(
+                f"hdc executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+                file=sys.stderr,
+            )
+            return 124
+
+        exit_code, output = _extract_exit_code(completed.stdout)
+        if exit_code is None:
+            print(
+                "failed to parse exit code from hdc shell output",
+                file=sys.stderr,
+            )
+            if completed.stdout:
+                print(completed.stdout, end="", file=sys.stderr)
+            if completed.stderr:
+                print(completed.stderr, end="", file=sys.stderr)
+            return 1
+
+        if output:
+            sys.stdout.write(output)
+        if completed.stderr:
+            sys.stderr.write(completed.stderr)
+        return exit_code
+    finally:
+        if not args.keep_remote_dir:
+            try:
+                _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_execdir)}")
+            except Exception:
+                pass
+        if not args.keep_host_staging:
+            shutil.rmtree(staging_execdir.parent, ignore_errors=True)
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run_test.py b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
new file mode 100644
index 0000000000000..17e2f65bd1fb6
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import os
+from pathlib import Path
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import unittest
+
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+HDC_RUN = SCRIPT_DIR / "hdc_run.py"
+
+
+class HdcRunTest(unittest.TestCase):
+    def make_fake_hdc(self, directory):
+        fake_hdc = directory / "hdc"
+        fake_hdc.write_text(
+            textwrap.dedent(
+                """\
+                #!/usr/bin/env python3
+                import sys
+
+                EXIT_MARKER = "__llvm_hdc_exit__="
+
+                def main():
+                    args = sys.argv[1:]
+                    if args[:2] == ["file", "send"]:
+                        print("FileTransfer finish")
+                        return 0
+
+                    if args and args[0] == "shell":
+                        command = args[1]
+                        if "stderr.tmp.exe" in command:
+                            sys.stdout.write("STDOUT-OUTPUT")
+                            sys.stderr.write("STDERR-OUTPUT")
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        if "stdin.tmp.exe" in command:
+                            sys.stdout.write("IN:" + sys.stdin.read())
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                        return 0
+
+                    print("unexpected fake hdc invocation: " + repr(args), file=sys.stderr)
+                    return 2
+
+                if __name__ == "__main__":
+                    raise SystemExit(main())
+                """
+            )
+        )
+        fake_hdc.chmod(fake_hdc.stat().st_mode | stat.S_IXUSR)
+        return fake_hdc
+
+    def run_hdc_run(self, temp_dir, executable_name, *, stdin_text=None):
+        execdir = temp_dir / "execdir"
+        execdir.mkdir()
+        executable = execdir / executable_name
+        executable.write_text("# fake executable marker\n")
+        executable.chmod(executable.stat().st_mode | stat.S_IXUSR)
+
+        fake_hdc = self.make_fake_hdc(temp_dir)
+        return subprocess.run(
+            [
+                sys.executable,
+                str(HDC_RUN),
+                "--hdc",
+                str(fake_hdc),
+                "--execdir",
+                str(execdir),
+                "--host-staging-root",
+                str(temp_dir / "staging"),
+                str(executable),
+            ],
+            input=stdin_text,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+    def test_preserves_remote_stdout_and_stderr_separately(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(Path(temp), "stderr.tmp.exe")
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "STDOUT-OUTPUT")
+        self.assertEqual(completed.stderr, "STDERR-OUTPUT")
+
+    def test_forwards_stdin_to_remote_command(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(
+                Path(temp),
+                "stdin.tmp.exe",
+                stdin_text="hello from stdin",
+            )
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "IN:hello from stdin")
+        self.assertEqual(completed.stderr, "")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt
index e1a1587fb6283..cb22e6fe65108 100644
--- a/libcxxabi/CMakeLists.txt
+++ b/libcxxabi/CMakeLists.txt
@@ -187,7 +187,12 @@ set(LIBCXXABI_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXXABI_TARGET_SUBDIR)
+  else()
+    set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXXABI_LIBDIR_SUBDIR)
     string(APPEND LIBCXXABI_TARGET_SUBDIR /${LIBCXXABI_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
new file mode 100644
index 0000000000000..d18748fe3a3cc
--- /dev/null
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -0,0 +1,30 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++abi using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -I %{libcxx}/test/support -I %{libcxx}/src -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    '-nostdlib++ -L %{lib} -lc++ -lc++abi -pthread'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 02132f6c07fcd..4fd11bf5aca2f 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -142,7 +142,12 @@ set(LIBUNWIND_STATIC_OUTPUT_NAME "unwind" CACHE STRING "Output name for the stat
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBUNWIND_TARGET_SUBDIR)
+  else()
+    set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBUNWIND_LIBDIR_SUBDIR)
     string(APPEND LIBUNWIND_TARGET_SUBDIR /${LIBUNWIND_LIBDIR_SUBDIR})
   endif()
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
new file mode 100644
index 0000000000000..bce241a7898cf
--- /dev/null
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -0,0 +1,55 @@
+#
+# Configuration file for running the libunwind tests against the shared library
+# using HDC on OHOS.
+#
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+compile_flags = []
+link_flags = []
+
+if @HAVE_GETAUXVAL@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_GETAUXVAL')
+
+if @HAVE_ELF_AUX_INFO@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_ELF_AUX_INFO')
+
+if @LIBUNWIND_ENABLE_CET@:
+    compile_flags.append('-fcf-protection=full')
+
+if @LIBUNWIND_ENABLE_GCS@:
+    compile_flags.append('-mbranch-protection=standard')
+
+if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
+    link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
+
+if '@CMAKE_DL_LIBS@':
+    link_flags.append('-l at CMAKE_DL_LIBS@')
+
+compile_flags.append('-funwind-tables')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags))
+))
+config.substitutions.append(('%{link_flags}',
+    '-L %{{lib}} -lunwind {}'.format(' '.join(link_flags))
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)

>From e9e5d2e517fb072f3554798e49b7119ec739105f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 25 Jun 2026 11:30:38 +0800
Subject: [PATCH 2/6] Fix OHOS libc++ CI ABI and SME detection

---
 libcxx/utils/ci/run-buildbot |  2 ++
 libunwind/src/Registers.hpp  | 15 +++++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 1867e9c620649..ecf0ca17ae148 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -192,6 +192,7 @@ function setup-ohos-hdc-env() {
     require-env-var OHOS_SYSROOT
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
     OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
     OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
 }
@@ -209,6 +210,7 @@ function generate-cmake-ohos-hdc() {
           -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE}" \
           -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
           -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
           -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c88422a6d5f4f..18cb2a523bd0e 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -25,6 +25,11 @@
 #endif
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
+#if defined(__has_include)
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
+#endif
 #endif
 
 namespace libunwind {
@@ -1951,18 +1956,16 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
       return false;
     return has_sme != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = getauxval(AT_HWCAP2);
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = 0;
     elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
 #else
   static bool checkHasSME() {

>From 498fa8d127ff49c27d4230996338c6eb5d747fe6 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 1 Jul 2026 15:49:11 +0800
Subject: [PATCH 3/6] Stabilize OHOS HDC runtimes tests

Add OHOS lit features for HDC executor and platform limitations, mark affected libc++ tests as unsupported or expected failures, and adjust test support for OHOS filesystem cleanup and allocation counting behavior.

Also guard the aarch64 ZA unwind test on HWCAP2_SME so devices without a reliable SME feature definition do not execute SME instructions.
---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  9 +++++-
 .../clang/clang_modules_include.gen.py        |  3 ++
 .../locale.facet/no_allocation.pass.cpp       |  1 +
 libcxx/test/selftest/dsl/dsl.sh.py            |  5 ++++
 libcxx/test/selftest/stdin-is-piped.sh.cpp    |  1 +
 .../directory_entry.cons/path.pass.cpp        |  1 +
 .../directory_entry.mods/assign.pass.cpp      |  1 +
 .../directory_entry.mods/refresh.pass.cpp     |  1 +
 .../replace_filename.pass.cpp                 |  1 +
 .../directory_entry.obs/file_size.pass.cpp    |  1 +
 .../file_type_obs.pass.cpp                    |  1 +
 .../hard_link_count.pass.cpp                  |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../directory_iterator.members/ctor.pass.cpp  |  1 +
 .../path.member/path.assign/move.pass.cpp     |  2 +-
 .../path.member/path.construct/move.pass.cpp  |  2 +-
 .../rec.dir.itr.members/ctor.pass.cpp         |  1 +
 .../rec.dir.itr.members/increment.pass.cpp    |  1 +
 .../fs.op.funcs/fs.op.exists/exists.pass.cpp  |  1 +
 .../is_block_file.pass.cpp                    |  1 +
 .../is_character_file.pass.cpp                |  1 +
 .../fs.op.is_directory/is_directory.pass.cpp  |  1 +
 .../fs.op.is_empty/is_empty.pass.cpp          |  1 +
 .../fs.op.is_fifo/is_fifo.pass.cpp            |  1 +
 .../fs.op.is_other/is_other.pass.cpp          |  1 +
 .../is_regular_file.pass.cpp                  |  1 +
 .../fs.op.is_socket/is_socket.pass.cpp        |  1 +
 .../fs.op.is_symlink/is_symlink.pass.cpp      |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../fs.op.funcs/fs.op.remove/remove.pass.cpp  |  1 +
 .../fs.op.remove_all/remove_all.pass.cpp      |  1 +
 .../fs.op.funcs/fs.op.status/status.pass.cpp  |  1 +
 .../symlink_status.pass.cpp                   |  1 +
 .../temp_directory_path.pass.cpp              |  1 +
 .../ext.manip/get_money.pass.cpp              |  1 +
 .../ext.manip/put_money.pass.cpp              |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 .../print.fun/no_file_description.pass.cpp    |  3 ++
 .../std.manip/setfill_wchar_max.pass.cpp      |  1 +
 .../narrow.stream.objects/cerr.sh.cpp         |  1 +
 .../narrow.stream.objects/cin.sh.cpp          |  1 +
 .../narrow.stream.objects/clog.sh.cpp         |  1 +
 .../wide.stream.objects/wcerr-imbue.sh.cpp    |  1 +
 .../wide.stream.objects/wcerr.sh.cpp          |  1 +
 .../wide.stream.objects/wcin-imbue.sh.cpp     |  1 +
 .../wide.stream.objects/wcin.sh.cpp           |  1 +
 .../wide.stream.objects/wclog.sh.cpp          |  1 +
 .../sized_delete_array.pass.cpp               |  1 +
 ..._array_calls_unsized_delete_array.pass.cpp |  1 +
 .../new.delete.single/sized_delete.pass.cpp   |  1 +
 ...sized_delete_calls_unsized_delete.pass.cpp |  1 +
 .../locale.collate.byname/compare.pass.cpp    |  1 +
 .../locale.collate.byname/transform.pass.cpp  |  1 +
 .../facet.ctype.char.dtor/dtor.pass.cpp       |  1 +
 .../locale.ctype.byname/is_1.pass.cpp         |  1 +
 .../locale.ctype.byname/is_many.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_is.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_not.pass.cpp     |  1 +
 .../locale.ctype.byname/tolower_1.pass.cpp    |  1 +
 .../locale.ctype.byname/tolower_many.pass.cpp |  1 +
 .../locale.ctype.byname/toupper_1.pass.cpp    |  1 +
 .../locale.ctype.byname/toupper_many.pass.cpp |  1 +
 .../locale.ctype.byname/widen_1.pass.cpp      |  1 +
 .../locale.ctype.byname/widen_many.pass.cpp   |  1 +
 .../get_long_double_en_US.pass.cpp            |  1 +
 .../get_long_double_zh_CN.pass.cpp            |  1 +
 .../get_string_en_US.pass.cpp                 |  1 +
 .../put_long_double_en_US.pass.cpp            |  1 +
 .../put_long_double_zh_CN.pass.cpp            |  1 +
 .../put_string_en_US.pass.cpp                 |  1 +
 .../locale/locale.cons/default.pass.cpp       |  1 +
 .../test/std/re/re.traits/transform.pass.cpp  |  1 +
 .../re/re.traits/transform_primary.pass.cpp   |  1 +
 .../thread_create_failure.pass.cpp            |  1 +
 .../thread.jthread/join.deadlock.pass.cpp     |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 libcxx/test/support/count_new.h               | 28 +++++++++++++++++++
 libcxx/test/support/filesystem_test_helper.h  |  4 ++-
 libcxx/test/support/test_macros.h             |  4 ++-
 libunwind/test/aarch64_za_unwind.pass.cpp     | 13 +++++----
 80 files changed, 132 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index a20ed9ce9c6a9..542bc0cc83a1f 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -7,7 +7,8 @@ config.substitutions.append(('%{flags}',
     ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
 ))
 config.substitutions.append(('%{compile_flags}',
-    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
 ))
 config.substitutions.append(('%{link_flags}',
     # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
@@ -24,6 +25,12 @@ import libcxx.test.params, libcxx.test.config
 
 config.available_features.add('LIBCXX-OHOS-FIXME')
 config.available_features.add('ohos-hdc')
+config.available_features.add('executor-has-no-stdin')
+config.available_features.add('executor-merges-stderr-into-stdout')
+config.available_features.add('executor-runs-as-root')
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
index 20a545b719ed6..f04a6bce76691 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
index 03e9de882ab0a..428d30804a4c3 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
index 10829f8a73db1..c8a1c34aa3028 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index 071ee7f6c891f..44b94528f5e32 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a hard link.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
index 5d931891dd0eb..d291ca2cb5641 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
index 82b94a3d6bf5f..e4bc9c41728da 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
index 8401dbd38cd71..e0fa44ffbc4a5 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
index ac5499cdd6f20..2bcc64a3c2651 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_assignable<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
index 1f1843f7d401d..5efdf983f8a10 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_constructible<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
index 08d7ff84378d5..a3768b8cbd83f 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
index 1f2293989b7ef..9e8a70b4339d4 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // On Android L, ~scoped_test_env() is unable to delete the temp dir using
 // chmod+rm because chmod is too broken.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
index 56465d01021ad..62dc696d6797d 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
index fad880ecf30f7..708fb511dce73 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
index 12bc0c198c062..69e357adeafb5 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
index f1b559cf2d55b..0ed4aa0dc8d58 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
index f3161f2e020d2..579c03f2b8c0b 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
index fbf5e7129f7af..1d5312d65be46 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
index d0974640429df..7a3a56a0b161c 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
index 4a5882e36cde2..8b877ff63b112 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
index 382685c21dd99..995125d702de3 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
index 07b446d80c402..692fe773a0f9e 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
index c420fea21aa66..22c2c114ba687 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
index 56b4a27de6b42..8064feaaf2065 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
index b30d56685e2cb..540849ccefc05 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
index 425c224606038..02ff1e249d3bc 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
index 1d405a816e110..41474f9ca1e48 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
index 38e14438f3fd3..9a891eab6b343 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
index 88672851f7b6b..3e1d34ab5257a 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
index d0ff2c87689d1..4c299b876639e 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
index 98fa56b7f4b6d..7970887bac199 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
index 283a8fddb8e8f..13e6f74c5a143 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -17,6 +17,9 @@
 // fmemopen is available starting in Android M (API 23)
 // XFAIL: target={{.+}}-android{{(eabi)?(21|22)}}
 
+// OHOS fmemopen/FILE behavior does not currently match the expectation tested here.
+// XFAIL: LIBCXX-OHOS-FIXME
+
 // <print>
 
 // The FILE returned by fmemopen does not have file descriptor.
diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
index b24c0b90d86f1..2ddddbef70dbb 100644
--- a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
@@ -18,6 +18,7 @@
 // XFAIL: target=armv{{7|8}}{{l?}}{{.*}}-linux-gnueabihf && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-linux-gnu && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-amazon-linux && libcpp-abi-version=1
+// XFAIL: target={{.*}}-linux-ohos && libcpp-abi-version=1
 
 #include <iomanip>
 #include <ostream>
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
index da5563f87df45..ea5af90a90822 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
index 4f23b6b9a5d87..6dc0ff36ff4bf 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
@@ -14,6 +14,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
index 9bedd67e816ef..cbb9639766207 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
index ec4c8a009ba0c..59d93e74db3b7 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
index ae82035ccfc62..49a6ce0892ea4 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
index 6ae1972234b2b..58a6e18340ddf 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
index 08b74b999bbf4..88dcfe0485429 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
index 89e178248ae50..9c9745dcc9777 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wclog;
 
 // XFAIL: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
index 0e411f8adc0f2..e563152f2c8cd 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
index 026250daeee89..34119303a29ba 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete [] is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
index ca0f612bf7578..a4e32aaa11e13 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
index fbc9cf070bcba..3ec2b47b53eb0 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
index 8ae6bc2d3ba66..7cfd9f955fce7 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
@@ -8,6 +8,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
index 4978db116ef39..743075ede8ece 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
index 7be7e8d574e6c..b77a4409522f0 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
@@ -11,6 +11,7 @@
 // template <> class ctype<char>
 
 // ~ctype();
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <locale>
 #include <cassert>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
index 5cebc079c4703..c4803c04d173b 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <type_traits>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
index 2869378b42b92..5174ffebf281f 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
index ea03f042f2d45..665da7323e38a 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
index 2f7acb843e591..5d87c081b6e1c 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
index 447353de8af72..93d93aa9228f7 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
index 94ecc6c8fa85b..ab448a01632d6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
index 2b37737a26bc3..4be990e5b88c6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
index 016d8d736ad22..e0a3912a9315e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
index 959a4be9e1de9..b22629c82444e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
index 078b4a6fefb7e..c2cd904808794 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
index 9997b07134563..73b7a6decda36 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
index c86df7e6b53bf..deb30d747653f 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
index 478df7964f6d2..ee6fbd48b09b6 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
index 4b767fae871fa..c476537c3c7e3 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
index 25046a8417083..8174a724d4165 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
index 1c8710a008f27..e23cb4bf5f04a 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
index f0086ada30303..a36e3af8d12eb 100644
--- a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
+++ b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
@@ -12,6 +12,7 @@
 
 // This test runs in C++20, but we have deprecated codecvt<char(16|32), char, mbstate_t> in C++20.
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // <locale>
 
diff --git a/libcxx/test/std/re/re.traits/transform.pass.cpp b/libcxx/test/std/re/re.traits/transform.pass.cpp
index 80cd3f01faff2..112ecd47ff4e2 100644
--- a/libcxx/test/std/re/re.traits/transform.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform.pass.cpp
@@ -8,6 +8,7 @@
 //
 // NetBSD does not support LC_COLLATE at the moment
 // XFAIL: netbsd
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..cbc9387facb30 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -11,6 +11,7 @@
 
 // XFAIL: LIBCXX-AIX-FIXME
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
index 40daa49393bd4..110d0ea9fd694 100644
--- a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
+++ b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
@@ -15,6 +15,7 @@
 
 // There is no way to limit the number of threads on windows
 // UNSUPPORTED: windows
+// UNSUPPORTED: executor-runs-as-root
 
 // macOS and FreeBSD seem to limit the number of processes, not threads via RLIMIT_NPROC.
 // XFAIL: target={{.+}}-apple-{{.*}}
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
index c6eb0694ab2e1..19573ae52fd71 100644
--- a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
@@ -9,6 +9,7 @@
 // Windows cannot detect the deadlock. Instead of throwing system_error,
 // it would dead lock the test
 // UNSUPPORTED: windows
+// UNSUPPORTED: pthread-join-self-deadlocks
 
 // TSAN bug: https://llvm.org/PR66537
 // UNSUPPORTED: tsan
diff --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
index 51be78e1a02f8..804036e425572 100644
--- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
@@ -14,6 +14,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..6901aff00bc4e 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -415,6 +415,13 @@ void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteCalled(p);
+  std::free(p);
+}
+#  endif
+
 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->newArrayCalled(s);
@@ -449,6 +456,13 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteArrayCalled(p);
+  std::free(p);
+}
+#  endif
+
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
 #      define USE_ALIGNED_ALLOC
@@ -519,6 +533,13 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
@@ -551,6 +572,13 @@ void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
 
 #endif // DISABLE_NEW_COUNT
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index 2ad9efb32c60f..f820a4a0fa915 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -182,7 +182,7 @@ struct scoped_test_env
         std::string cmd = "chmod -R 777 " + test_root.string();
 #endif // defined(__MVS__)
         int ret = std::system(cmd.c_str());
-#  if !defined(_AIX) && !defined(__ANDROID__)
+#  if !defined(_AIX) && !defined(__ANDROID__) && !defined(__OHOS__)
         // On AIX the chmod command will return non-zero when trying to set
         // the permissions on a directory that contains a bad symlink. This triggers
         // the assert, despite being able to delete everything with the following
@@ -196,6 +196,8 @@ struct scoped_test_env
         // LIBCXX-ANDROID-FIXME: Other fixes to consider: place a toybox chmod
         // onto old devices, re-enable this assert for devices running Android N
         // and up, rewrite this chmod+rm in C or C++.
+        //
+        // OHOS has the same chmod -R behavior for bad symlinks.
         assert(ret == 0);
 #  endif
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..fdf6bc5b18d72 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -375,7 +375,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NOT_WIN32(...) __VA_ARGS__
 #endif
 
-#if defined(TEST_WINDOWS_DLL) ||defined(__MVS__) || defined(_AIX)
+#if defined(TEST_WINDOWS_DLL) || defined(__MVS__) || defined(_AIX) || defined(TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS)
 // Macros for waiving cases when we can't count allocations done within
 // the library implementation.
 //
@@ -386,6 +386,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 //
 // The same goes on IBM zOS.
 // The same goes on AIX.
+// Some target test configurations can define
+// TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS for the same reason.
 #define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
 #define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
 #else
diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp
index 36536860c1a96..b28f70a6e8e7a 100644
--- a/libunwind/test/aarch64_za_unwind.pass.cpp
+++ b/libunwind/test/aarch64_za_unwind.pass.cpp
@@ -20,6 +20,9 @@
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
 #endif
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
 
 // Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA
 // (and commits a lazy save of ZA) before resuming from unwinding.
@@ -33,18 +36,16 @@ static bool checkHasSME() {
     return false;
   return has_sme != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = getauxval(AT_HWCAP2);
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = 0;
   elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
 #else
 static bool checkHasSME() {

>From bbaa18370c45aaf79c2446be0a6e94dc2aa0ef00 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Fri, 3 Jul 2026 10:33:38 +0800
Subject: [PATCH 4/6] Add qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  16 +-
 libcxx/utils/ci/run-buildbot                  |  41 ++++-
 libcxx/utils/ci/vendor/ohos/qemu_run.py       | 144 ++++++++++++++++++
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |   8 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |   8 +-
 5 files changed, 204 insertions(+), 13 deletions(-)
 create mode 100755 libcxx/utils/ci/vendor/ohos/qemu_run.py

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 542bc0cc83a1f..20aa7a49d5802 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++ using HDC on OHOS.
+# libc++ using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -23,15 +23,21 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
 config.available_features.add('LIBCXX-OHOS-FIXME')
-config.available_features.add('ohos-hdc')
-config.available_features.add('executor-has-no-stdin')
-config.available_features.add('executor-merges-stderr-into-stdout')
-config.available_features.add('executor-runs-as-root')
+config.available_features.add(executor_kind)
 config.available_features.add('platform-has-nonportable-locales')
 config.available_features.add('platform-has-unreliable-new-delete-interposition')
 config.available_features.add('pthread-join-self-deadlocks')
 
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
     libcxx.test.features.DEFAULT_FEATURES,
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index ecf0ca17ae148..40739b0fba4d8 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -28,6 +28,16 @@ ${PROGNAME} [options] <BUILDER>
 --build-dir <DIR>   The directory to use for building the library. By default,
                     this is '<llvm-root>/build/<builder>'.
 
+--ohos-qemu <PATH>  Run OHOS tests under qemu-aarch64 instead of HDC. This also
+                    accepts OHOS_QEMU or QEMU from the environment.
+
+--ohos-qemu-sysroot <DIR>
+                    Sysroot passed to qemu-aarch64 with -L. Defaults to
+                    OHOS_QEMU_SYSROOT, QEMU_SYSROOT, then OHOS_SYSROOT.
+
+--ohos-hdc <PATH>   HDC binary to use when OHOS tests run on a device. This sets
+                    HDC for the OHOS HDC executor.
+
 Environment variables
 CC                  The C compiler to use, this value is used by CMake. This
                     variable is mandatory.
@@ -84,6 +94,19 @@ while [[ $# -gt 0 ]]; do
             BUILD_DIR="${2}"
             shift; shift
             ;;
+        --ohos-qemu)
+            OHOS_QEMU="${2}"
+            shift; shift
+            ;;
+        --ohos-qemu-sysroot)
+            OHOS_QEMU_SYSROOT="${2}"
+            shift; shift
+            ;;
+        --ohos-hdc)
+            HDC="${2}"
+            export HDC
+            shift; shift
+            ;;
         *)
             BUILDER="${1}"
             shift
@@ -193,8 +216,18 @@ function setup-ohos-hdc-env() {
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
     OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
-    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
-    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+    OHOS_QEMU="${OHOS_QEMU:-${QEMU:-}}"
+
+    if [[ -n "${OHOS_QEMU}" ]]; then
+        OHOS_QEMU_SYSROOT="${OHOS_QEMU_SYSROOT:-${QEMU_SYSROOT:-${OHOS_SYSROOT}}}"
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/qemu_run.py --qemu ${OHOS_QEMU} --sysroot ${OHOS_QEMU_SYSROOT}"
+        OHOS_EXECUTOR_KIND="ohos-qemu"
+    else
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+        OHOS_EXECUTOR_KIND="ohos-hdc"
+    fi
+
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE};executor_kind=${OHOS_EXECUTOR_KIND}"
 }
 
 function generate-cmake-ohos-hdc() {
@@ -863,14 +896,14 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
-ohos-hdc-aarch64-full)
+ohos-aarch64-full|ohos-hdc-aarch64-full)
     clean
 
     generate-cmake-ohos-hdc
 
     check-runtimes
 ;;
-ohos-hdc-aarch64-smoke)
+ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
     clean
 
     generate-cmake-ohos-hdc
diff --git a/libcxx/utils/ci/vendor/ohos/qemu_run.py b/libcxx/utils/ci/vendor/ohos/qemu_run.py
new file mode 100755
index 0000000000000..ef7eef7872864
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/qemu_run.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _resolve_binary(binary):
+    if os.path.isabs(binary):
+        if os.path.isfile(binary) and os.access(binary, os.X_OK):
+            return binary
+        sys.exit(f"failed to find executable: {binary}")
+
+    resolved = shutil.which(binary)
+    if resolved is None:
+        sys.exit(f"failed to find executable: {binary}")
+    return resolved
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(str(parent))
+    return library_dirs
+
+
+def _build_target_env_args(env, prepend_env):
+    target_env = dict(env)
+
+    for key, value in prepend_env.items():
+        existing = target_env.get(key, os.environ.get(key, ""))
+        target_env[key] = f"{value}{os.pathsep}{existing}" if existing else value
+
+    args = []
+    for key, value in target_env.items():
+        args.extend(["-E", f"{key}={value}"])
+    return args
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--qemu",
+        default=os.environ.get("OHOS_QEMU", os.environ.get("QEMU", "qemu-aarch64")),
+    )
+    parser.add_argument("--sysroot", required=True)
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(
+            os.environ.get(
+                "OHOS_QEMU_TIMEOUT_SECONDS",
+                os.environ.get("QEMU_TIMEOUT_SECONDS", "600"),
+            )
+        ),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    qemu = _resolve_binary(args.qemu)
+    execdir = Path(args.execdir).resolve()
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    build_library_dirs = _collect_build_library_dirs(execdir)
+    if build_library_dirs:
+        existing = prepend_env.get("LD_LIBRARY_PATH")
+        build_library_path = os.pathsep.join(build_library_dirs)
+        prepend_env["LD_LIBRARY_PATH"] = (
+            f"{build_library_path}{os.pathsep}{existing}"
+            if existing
+            else build_library_path
+        )
+
+    cmd = [
+        qemu,
+        "-L",
+        args.sysroot,
+        *_build_target_env_args(env, prepend_env),
+        *args.command,
+    ]
+    try:
+        completed = subprocess.run(
+            cmd,
+            cwd=execdir,
+            timeout=args.timeout_seconds,
+            check=False,
+        )
+    except subprocess.TimeoutExpired:
+        print(
+            f"qemu executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+            file=sys.stderr,
+        )
+        return 124
+    return completed.returncode
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index d18748fe3a3cc..8f5a17b3dec64 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++abi using HDC on OHOS.
+# libc++abi using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -20,7 +20,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index bce241a7898cf..f2a032c41a123 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -1,6 +1,6 @@
 #
 # Configuration file for running the libunwind tests against the shared library
-# using HDC on OHOS.
+# using HDC or QEMU on OHOS.
 #
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
@@ -45,7 +45,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,

>From dde4d430033711fb7ffa87621ba86b37492f480b Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 14:52:40 +0800
Subject: [PATCH 5/6] Fix something for qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in                   | 4 +++-
 .../file.streams/fstreams/filebuf.members/close.pass.cpp      | 3 +++
 .../fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp     | 2 ++
 .../syncstream/osyncstream/thread/several_threads.pass.cpp    | 2 ++
 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in             | 4 +++-
 libcxxabi/test/guard_threaded_test.pass.cpp                   | 2 ++
 libunwind/test/bad_unwind_info.pass.cpp                       | 2 ++
 libunwind/test/configs/llvm-libunwind-ohos.cfg.in             | 4 +++-
 libunwind/test/signal_unwind.pass.cpp                         | 2 ++
 9 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 20aa7a49d5802..3be1c267ede2a 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -23,7 +23,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
index 2c4cfb0ac7661..ce802d6ceba78 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14
 // REQUIRES: linux
 // UNSUPPORTED: no-filesystem
+// OHOS qemu-user observes the host /proc, not a real OHOS process /proc.
+// UNSUPPORTED: ohos-qemu
 // XFAIL: no-localization
 
 // <filesystem>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..77db6396755bf 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -10,6 +10,8 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
+// This thread-heavy stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 // <syncstream>
 
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index 8f5a17b3dec64..d0a8d1248f88a 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -20,7 +20,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxxabi/test/guard_threaded_test.pass.cpp b/libcxxabi/test/guard_threaded_test.pass.cpp
index d898e60288aca..a4e14f0aca9e4 100644
--- a/libcxxabi/test/guard_threaded_test.pass.cpp
+++ b/libcxxabi/test/guard_threaded_test.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: no-exceptions
+// This thread-heavy guard stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 #define TESTING_CXA_GUARD
 #include "../src/cxa_guard_impl.h"
diff --git a/libunwind/test/bad_unwind_info.pass.cpp b/libunwind/test/bad_unwind_info.pass.cpp
index 332b661d2e98f..76102505f2d67 100644
--- a/libunwind/test/bad_unwind_info.pass.cpp
+++ b/libunwind/test/bad_unwind_info.pass.cpp
@@ -13,6 +13,8 @@
 // REQUIRES: target={{(aarch64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
 // UNSUPPORTED: target={{.*-apple.*}}
+// OHOS qemu-user does not reliably model this invalid unwind-info path.
+// UNSUPPORTED: ohos-qemu
 
 // GCC doesn't support __attribute__((naked)) on AArch64.
 // UNSUPPORTED: gcc
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index f2a032c41a123..71d1e711e36d5 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -45,7 +45,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp
index f8798bc3eabeb..ef73a3ea8c59a 100644
--- a/libunwind/test/signal_unwind.pass.cpp
+++ b/libunwind/test/signal_unwind.pass.cpp
@@ -10,6 +10,8 @@
 // Ensure that the unwinder can cope with the signal handler.
 // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
+// OHOS qemu-user does not provide device-equivalent signal unwinding behavior.
+// UNSUPPORTED: ohos-qemu
 
 // TODO: Figure out why this fails with Memory Sanitizer.
 // XFAIL: msan

>From c81df7252fb00b5b9b41f0a29393e775abc9c810 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 15:19:29 +0800
Subject: [PATCH 6/6] delete run-smoke-test

---
 libcxx/utils/ci/buildkite-pipeline.yml | 12 ----------
 libcxx/utils/ci/run-buildbot           | 31 --------------------------
 2 files changed, 43 deletions(-)

diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 77617f43ae6dc..70bc69637a7f2 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -175,18 +175,6 @@ steps:
 
 - group: OHOS
   steps:
-  - label: OHOS AArch64 HDC Smoke
-    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
-    env:
-      CC: clang
-      CXX: clang++
-      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
-      HDC_TIMEOUT_SECONDS: 240
-    agents:
-      queue: libcxx-builders
-      os: linux
-      device: ohos-hdc
-    <<: *common
   - label: OHOS AArch64 HDC Full
     command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
     env:
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 40739b0fba4d8..e7d751df484e4 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -147,23 +147,6 @@ function require-env-var() {
     fi
 }
 
-function detect-lit() {
-    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
-        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
-    elif command -v llvm-lit >/dev/null 2>&1; then
-        LIT_RUNNER=("$(command -v llvm-lit)")
-    else
-        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
-    fi
-}
-
-function run-smoke-test() {
-    local test_dir="${1}"
-    local filter="${2}"
-    step "Running smoke test ${filter}"
-    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
-}
-
 function ohos-lit-args() {
     local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
     if python3 -c 'import psutil' >/dev/null 2>&1; then
@@ -903,20 +886,6 @@ ohos-aarch64-full|ohos-hdc-aarch64-full)
 
     check-runtimes
 ;;
-ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
-    clean
-
-    generate-cmake-ohos-hdc
-
-    step "Building the OHOS smoke test dependencies"
-    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
-
-    detect-lit
-    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
-;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #


From libcxx-commits at lists.llvm.org  Thu Jul  9 01:17:03 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 01:17:03 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a4f58ff.27b3b003.3b871.034a@mx.google.com>


https://github.com/huihui15537226823 updated https://github.com/llvm/llvm-project/pull/208390

>From 70e8ac839667f8aba4aa74a2dd19614426f9754f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 24 Jun 2026 09:47:00 +0800
Subject: [PATCH 1/6] Add OHOS HDC runtimes CI support

---
 cmake/Modules/GetToolchainDirs.cmake          |  37 +-
 .../cmake/Modules/CompilerRTUtils.cmake       |   8 +-
 compiler-rt/cmake/builtin-config-ix.cmake     |   2 +-
 compiler-rt/cmake/crt-config-ix.cmake         |   2 +-
 compiler-rt/lib/builtins/crtbegin.c           |   9 +
 compiler-rt/lib/builtins/emutls.c             |   8 +-
 libcxx/CMakeLists.txt                         |  10 +-
 libcxx/cmake/config-ix.cmake                  |   2 +
 libcxx/include/__config                       |   5 +-
 libcxx/include/__cxx03/__config               |   5 +-
 .../__cxx03/__locale_dir/locale_base_api.h    |   2 +-
 libcxx/include/__locale_dir/locale_base_api.h |   2 +-
 libcxx/src/include/config_elast.h             |   2 +
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  48 ++
 libcxx/utils/ci/buildkite-pipeline.yml        |  27 +
 libcxx/utils/ci/run-buildbot                  |  87 ++++
 libcxx/utils/ci/vendor/ohos/hdc_run.py        | 472 ++++++++++++++++++
 libcxx/utils/ci/vendor/ohos/hdc_run_test.py   | 115 +++++
 libcxxabi/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |  30 ++
 libunwind/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |  55 ++
 22 files changed, 910 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/test/configs/llvm-libc++-ohos.cfg.in
 create mode 100755 libcxx/utils/ci/vendor/ohos/hdc_run.py
 create mode 100644 libcxx/utils/ci/vendor/ohos/hdc_run_test.py
 create mode 100644 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
 create mode 100644 libunwind/test/configs/llvm-libunwind-ohos.cfg.in

diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..a20ed9ce9c6a9
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,48 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index d4107cf11ad51..77617f43ae6dc 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -172,3 +172,30 @@ steps:
       queue: libcxx-builders
       os: android
     <<: *common
+
+- group: OHOS
+  steps:
+  - label: OHOS AArch64 HDC Smoke
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
+  - label: OHOS AArch64 HDC Full
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 6376bc1d3dd5a..1867e9c620649 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -116,6 +116,41 @@ function clean() {
     rm -rf "${BUILD_DIR}"
 }
 
+function require-env-var() {
+    local name="${1}"
+    if [[ -z ${!name+x} ]] || [[ -z "${!name}" ]]; then
+        error "Environment variable ${name} must be defined"
+        exit 1
+    fi
+}
+
+function detect-lit() {
+    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
+        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
+    elif command -v llvm-lit >/dev/null 2>&1; then
+        LIT_RUNNER=("$(command -v llvm-lit)")
+    else
+        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
+    fi
+}
+
+function run-smoke-test() {
+    local test_dir="${1}"
+    local filter="${2}"
+    step "Running smoke test ${filter}"
+    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
+}
+
+function ohos-lit-args() {
+    local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
+    if python3 -c 'import psutil' >/dev/null 2>&1; then
+        args="${args} --timeout=1500"
+    else
+        echo "warning: python3 psutil not available; running OHOS lit without per-test timeout" >&2
+    fi
+    printf '%s' "${args}"
+}
+
 function generate-cmake-base() {
     step "Generating CMake"
 
@@ -153,6 +188,37 @@ function generate-cmake-android() {
           "${@}"
 }
 
+function setup-ohos-hdc-env() {
+    require-env-var OHOS_SYSROOT
+
+    OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+}
+
+function generate-cmake-ohos-hdc() {
+    setup-ohos-hdc-env
+
+    local lit_args
+    lit_args="$(ohos-lit-args)"
+
+    generate-cmake \
+          -DLLVM_LIT_ARGS="${lit_args}" \
+          -DCMAKE_SYSTEM_NAME=OHOS \
+          -DCMAKE_SYSROOT="${OHOS_SYSROOT}" \
+          -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
+          -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
+          -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
+          -DLIBUNWIND_TEST_CONFIG="llvm-libunwind-ohos.cfg.in" \
+          -DLIBCXX_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBCXXABI_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBUNWIND_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          "${@}"
+}
+
 function check-runtimes() {
     step "Building libc++ test dependencies"
     ninja -vC "${BUILD_DIR}" cxx-test-depends
@@ -795,6 +861,27 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
+ohos-hdc-aarch64-full)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    check-runtimes
+;;
+ohos-hdc-aarch64-smoke)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    step "Building the OHOS smoke test dependencies"
+    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
+
+    detect-lit
+    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
+;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run.py b/libcxx/utils/ci/vendor/ohos/hdc_run.py
new file mode 100755
index 0000000000000..61a3899b88b11
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run.py
@@ -0,0 +1,472 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import uuid
+
+
+EXIT_MARKER = "__llvm_hdc_exit__="
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _split_path_list(value):
+    return [entry for entry in value.split(os.pathsep) if entry]
+
+
+def _resolve_hdc_binary(hdc):
+    if os.path.isabs(hdc):
+        return hdc
+    resolved = shutil.which(hdc)
+    if resolved is None:
+        sys.exit(f"failed to find hdc binary: {hdc}")
+    return resolved
+
+
+def _is_windows_hdc(path):
+    return path.lower().endswith(".exe")
+
+
+def _find_windows_local_root(hdc_path):
+    parts = Path(hdc_path).parts
+    for index in range(len(parts) - 1):
+        if parts[index] == "AppData" and index + 1 < len(parts) and parts[index + 1] == "Local":
+            return Path(*parts[: index + 2])
+    return None
+
+
+def _default_host_staging_root(hdc_path):
+    if _is_windows_hdc(hdc_path):
+        local_root = _find_windows_local_root(hdc_path)
+        if local_root is None:
+            sys.exit(
+                "failed to derive a Windows staging directory from the hdc.exe path; "
+                "set HDC_HOST_STAGING_ROOT to a path under /mnt/<drive>/..."
+            )
+        return local_root / "Temp" / "llvm-lit-hdc"
+    return Path("/tmp/llvm-lit-hdc")
+
+
+def _to_windows_path(path):
+    resolved = Path(path).resolve()
+    parts = resolved.parts
+    if len(parts) < 4 or parts[1] != "mnt":
+        sys.exit(
+            f"Windows hdc requires a staging path under /mnt/<drive>/..., got: {resolved}"
+        )
+    drive = parts[2].upper()
+    tail = "\\".join(parts[3:])
+    return f"{drive}:\\{tail}" if tail else f"{drive}:\\"
+
+
+def _run_readelf(path):
+    completed = subprocess.run(
+        ["readelf", "-d", path],
+        check=False,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        text=True,
+    )
+    if completed.returncode != 0:
+        return ""
+    return completed.stdout
+
+
+def _read_elf_metadata(path):
+    needed = []
+    runpath_dirs = []
+    seen_dirs = set()
+    for line in _run_readelf(path).splitlines():
+        needed_match = re.search(r"\(NEEDED\).*Shared library: \[(.*)\]", line)
+        if needed_match:
+            needed.append(needed_match.group(1))
+            continue
+
+        runpath_match = re.search(
+            r"\((?:RUNPATH|RPATH)\).*Library runpath: \[(.*)\]", line
+        )
+        if not runpath_match:
+            continue
+        for entry in _split_path_list(runpath_match.group(1)):
+            candidate = Path(entry)
+            if not candidate.is_dir():
+                continue
+            key = str(candidate.resolve())
+            if key in seen_dirs:
+                continue
+            seen_dirs.add(key)
+            runpath_dirs.append(candidate)
+    return needed, runpath_dirs
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(parent)
+    return library_dirs
+
+
+def _find_dependency(name, search_dirs):
+    for directory in search_dirs:
+        candidate = Path(directory) / name
+        if candidate.is_file():
+            return candidate.resolve()
+    return None
+
+
+def _copy_needed_libraries(executables, execdir, staging_execdir):
+    search_dirs = _collect_build_library_dirs(execdir)
+    queue = [Path(exe).resolve() for exe in executables]
+    visited = set()
+    copied_names = {path.name for path in staging_execdir.glob("*") if path.is_file()}
+
+    while queue:
+        current = queue.pop(0)
+        current_key = str(current)
+        if current_key in visited:
+            continue
+        visited.add(current_key)
+
+        needed, runpath_dirs = _read_elf_metadata(current)
+        current_search_dirs = []
+        current_seen = set()
+        for directory in [*runpath_dirs, current.parent, *search_dirs]:
+            resolved = Path(directory).resolve()
+            key = str(resolved)
+            if key in current_seen:
+                continue
+            current_seen.add(key)
+            current_search_dirs.append(resolved)
+
+        for directory in runpath_dirs:
+            resolved = directory.resolve()
+            if all(str(resolved) != str(existing) for existing in search_dirs):
+                search_dirs.append(resolved)
+
+        for lib_name in needed:
+            dependency = _find_dependency(lib_name, current_search_dirs)
+            if dependency is None:
+                continue
+            if lib_name not in copied_names:
+                shutil.copy2(dependency, staging_execdir / lib_name)
+                copied_names.add(lib_name)
+            queue.append(dependency)
+
+
+def _copy_execdir(execdir, staging_execdir):
+    if staging_execdir.exists():
+        shutil.rmtree(staging_execdir)
+    shutil.copytree(execdir, staging_execdir)
+
+
+def _iter_files(root):
+    for directory, dirnames, filenames in os.walk(root):
+        dirnames.sort()
+        filenames.sort()
+        yield Path(directory), filenames
+
+
+def _is_test_executable(path):
+    return path.endswith(".tmp.exe") and os.path.exists(path)
+
+
+def _map_local_path(path, local_execdir, remote_execdir):
+    normalized = os.path.normpath(path)
+    execdir = os.path.normpath(local_execdir)
+    if normalized == execdir:
+        return remote_execdir
+    prefix = execdir + os.sep
+    if normalized.startswith(prefix):
+        suffix = normalized[len(prefix) :].replace(os.sep, "/")
+        return f"{remote_execdir}/{suffix}" if suffix else remote_execdir
+    return path
+
+
+def _map_value(value, local_execdir, remote_execdir):
+    return _map_local_path(value, local_execdir, remote_execdir)
+
+
+def _build_env_exports(env, prepend_env, local_execdir, remote_execdir, add_remote_libdir):
+    commands = []
+    mapped_env = {
+        key: _map_value(value, local_execdir, remote_execdir) for key, value in env.items()
+    }
+    mapped_prepend = {
+        key: _map_value(value, local_execdir, remote_execdir)
+        for key, value in prepend_env.items()
+    }
+
+    if add_remote_libdir:
+        existing = mapped_prepend.get("LD_LIBRARY_PATH")
+        mapped_prepend["LD_LIBRARY_PATH"] = (
+            f"{remote_execdir}:{existing}" if existing else remote_execdir
+        )
+
+    for key, value in mapped_env.items():
+        commands.append(f"export {key}={shlex.quote(value)}")
+    for key, value in mapped_prepend.items():
+        commands.append(f"export {key}={shlex.quote(value)}${{{key}:+:{key}}}")
+    return commands
+
+
+def _hdc_prefix(hdc_path, server, target):
+    prefix = [hdc_path]
+    if server:
+        prefix.extend(["-s", server])
+    if target:
+        prefix.extend(["-t", target])
+    return prefix
+
+
+def _run_hdc(
+    prefix,
+    args,
+    *,
+    timeout=None,
+    capture_output=False,
+    stdin=subprocess.DEVNULL,
+    check=True,
+    expect_stdout=None,
+):
+    kwargs = {
+        "check": False,
+        "timeout": timeout,
+        "stdin": stdin,
+        "text": True,
+    }
+    if capture_output:
+        kwargs["stdout"] = subprocess.PIPE
+        kwargs["stderr"] = subprocess.PIPE
+    completed = subprocess.run(prefix + args, **kwargs)
+    output = completed.stdout if capture_output and completed.stdout else ""
+    error_output = completed.stderr if capture_output and completed.stderr else ""
+    if check and completed.returncode != 0:
+        raise RuntimeError(
+            f"hdc command failed with exit code {completed.returncode}: "
+            f"{shlex.join(prefix + args)}\n{output}{error_output}"
+        )
+    if check and expect_stdout is not None and expect_stdout not in output:
+        raise RuntimeError(
+            f"hdc command did not report success: {shlex.join(prefix + args)}\n{output}"
+        )
+    return completed
+
+
+def _run_checked_shell(prefix, command, *, timeout=None):
+    completed = _run_hdc(
+        prefix,
+        [
+            "shell",
+            f"{command}; rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"",
+        ],
+        timeout=timeout,
+        capture_output=True,
+    )
+    exit_code, output = _extract_exit_code(completed.stdout)
+    if exit_code is None:
+        raise RuntimeError(
+            f"failed to parse shell exit code for: {command}\n{completed.stdout}"
+        )
+    if exit_code != 0:
+        raise RuntimeError(
+            f"remote shell command failed with exit code {exit_code}: {command}\n{output}"
+        )
+    return output
+
+
+def _sync_tree_to_device(prefix, local_root, remote_root, use_windows_paths):
+    _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_root)}")
+    _run_checked_shell(prefix, f"mkdir -p {shlex.quote(remote_root)}")
+
+    for directory, filenames in _iter_files(local_root):
+        relative_dir = os.path.relpath(directory, local_root)
+        device_dir = (
+            remote_root
+            if relative_dir == "."
+            else f"{remote_root}/{relative_dir.replace(os.sep, '/')}"
+        )
+        _run_checked_shell(prefix, f"mkdir -p {shlex.quote(device_dir)}")
+        for name in filenames:
+            local_path = directory / name
+            device_path = f"{device_dir}/{name}"
+            source = _to_windows_path(local_path) if use_windows_paths else str(local_path)
+            _run_hdc(
+                prefix,
+                ["file", "send", source, device_path],
+                capture_output=True,
+                expect_stdout="FileTransfer finish",
+            )
+            if name.endswith(".tmp.exe") or os.access(local_path, os.X_OK):
+                _run_checked_shell(prefix, f"chmod +x {shlex.quote(device_path)}")
+
+
+def _build_remote_command(command, local_execdir, remote_execdir, exports):
+    mapped_command = [
+        _map_local_path(arg, local_execdir, remote_execdir) for arg in command
+    ]
+    pieces = [f"cd {shlex.quote(remote_execdir)}"]
+    pieces.extend(exports)
+    pieces.append(shlex.join(mapped_command))
+    pieces.append(f"rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"")
+    return " && ".join(pieces[:-2]) + f"; {pieces[-2]}; {pieces[-1]}"
+
+
+def _extract_exit_code(output):
+    match = re.search(rf"^(.*)\n{EXIT_MARKER}(\d+)\s*$", output, re.DOTALL)
+    if not match:
+        return None, output
+    return int(match.group(2)), match.group(1)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--hdc", default=os.environ.get("HDC", "hdc"))
+    parser.add_argument("--hdc-server", default=os.environ.get("HDC_SERVER_IP_PORT"))
+    parser.add_argument("--hdc-target", default=os.environ.get("HDC_UTID"))
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--remote-base-dir",
+        default="/data/local/tmp/llvm-lit-hdc",
+    )
+    parser.add_argument(
+        "--host-staging-root",
+        default=os.environ.get("HDC_HOST_STAGING_ROOT"),
+    )
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(os.environ.get("HDC_TIMEOUT_SECONDS", "120")),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("--keep-remote-dir", action="store_true")
+    parser.add_argument("--keep-host-staging", action="store_true")
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    hdc_path = _resolve_hdc_binary(args.hdc)
+    use_windows_paths = _is_windows_hdc(hdc_path)
+    host_staging_root = (
+        Path(args.host_staging_root)
+        if args.host_staging_root
+        else _default_host_staging_root(hdc_path)
+    )
+    prefix = _hdc_prefix(hdc_path, args.hdc_server, args.hdc_target)
+
+    execdir = Path(args.execdir).resolve()
+    run_id = uuid.uuid4().hex[:12]
+    staging_execdir = host_staging_root / f"run-{run_id}" / execdir.name
+    remote_execdir = f"{args.remote_base_dir}/run-{run_id}/{execdir.name}"
+
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    executables = [arg for arg in args.command if _is_test_executable(arg)]
+    host_staging_root.mkdir(parents=True, exist_ok=True)
+    _copy_execdir(execdir, staging_execdir)
+    _copy_needed_libraries(executables, execdir, staging_execdir)
+
+    add_remote_libdir = any(staging_execdir.glob("*.so*"))
+    exports = _build_env_exports(
+        env,
+        prepend_env,
+        str(execdir),
+        remote_execdir,
+        add_remote_libdir,
+    )
+
+    try:
+        _sync_tree_to_device(prefix, staging_execdir, remote_execdir, use_windows_paths)
+        remote_command = _build_remote_command(
+            args.command,
+            str(execdir),
+            remote_execdir,
+            exports,
+        )
+        try:
+            completed = _run_hdc(
+                prefix,
+                ["shell", remote_command],
+                timeout=args.timeout_seconds,
+                capture_output=True,
+                stdin=None,
+            )
+        except subprocess.TimeoutExpired:
+            print(
+                f"hdc executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+                file=sys.stderr,
+            )
+            return 124
+
+        exit_code, output = _extract_exit_code(completed.stdout)
+        if exit_code is None:
+            print(
+                "failed to parse exit code from hdc shell output",
+                file=sys.stderr,
+            )
+            if completed.stdout:
+                print(completed.stdout, end="", file=sys.stderr)
+            if completed.stderr:
+                print(completed.stderr, end="", file=sys.stderr)
+            return 1
+
+        if output:
+            sys.stdout.write(output)
+        if completed.stderr:
+            sys.stderr.write(completed.stderr)
+        return exit_code
+    finally:
+        if not args.keep_remote_dir:
+            try:
+                _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_execdir)}")
+            except Exception:
+                pass
+        if not args.keep_host_staging:
+            shutil.rmtree(staging_execdir.parent, ignore_errors=True)
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run_test.py b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
new file mode 100644
index 0000000000000..17e2f65bd1fb6
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import os
+from pathlib import Path
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import unittest
+
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+HDC_RUN = SCRIPT_DIR / "hdc_run.py"
+
+
+class HdcRunTest(unittest.TestCase):
+    def make_fake_hdc(self, directory):
+        fake_hdc = directory / "hdc"
+        fake_hdc.write_text(
+            textwrap.dedent(
+                """\
+                #!/usr/bin/env python3
+                import sys
+
+                EXIT_MARKER = "__llvm_hdc_exit__="
+
+                def main():
+                    args = sys.argv[1:]
+                    if args[:2] == ["file", "send"]:
+                        print("FileTransfer finish")
+                        return 0
+
+                    if args and args[0] == "shell":
+                        command = args[1]
+                        if "stderr.tmp.exe" in command:
+                            sys.stdout.write("STDOUT-OUTPUT")
+                            sys.stderr.write("STDERR-OUTPUT")
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        if "stdin.tmp.exe" in command:
+                            sys.stdout.write("IN:" + sys.stdin.read())
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                        return 0
+
+                    print("unexpected fake hdc invocation: " + repr(args), file=sys.stderr)
+                    return 2
+
+                if __name__ == "__main__":
+                    raise SystemExit(main())
+                """
+            )
+        )
+        fake_hdc.chmod(fake_hdc.stat().st_mode | stat.S_IXUSR)
+        return fake_hdc
+
+    def run_hdc_run(self, temp_dir, executable_name, *, stdin_text=None):
+        execdir = temp_dir / "execdir"
+        execdir.mkdir()
+        executable = execdir / executable_name
+        executable.write_text("# fake executable marker\n")
+        executable.chmod(executable.stat().st_mode | stat.S_IXUSR)
+
+        fake_hdc = self.make_fake_hdc(temp_dir)
+        return subprocess.run(
+            [
+                sys.executable,
+                str(HDC_RUN),
+                "--hdc",
+                str(fake_hdc),
+                "--execdir",
+                str(execdir),
+                "--host-staging-root",
+                str(temp_dir / "staging"),
+                str(executable),
+            ],
+            input=stdin_text,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+    def test_preserves_remote_stdout_and_stderr_separately(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(Path(temp), "stderr.tmp.exe")
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "STDOUT-OUTPUT")
+        self.assertEqual(completed.stderr, "STDERR-OUTPUT")
+
+    def test_forwards_stdin_to_remote_command(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(
+                Path(temp),
+                "stdin.tmp.exe",
+                stdin_text="hello from stdin",
+            )
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "IN:hello from stdin")
+        self.assertEqual(completed.stderr, "")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt
index e1a1587fb6283..cb22e6fe65108 100644
--- a/libcxxabi/CMakeLists.txt
+++ b/libcxxabi/CMakeLists.txt
@@ -187,7 +187,12 @@ set(LIBCXXABI_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXXABI_TARGET_SUBDIR)
+  else()
+    set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXXABI_LIBDIR_SUBDIR)
     string(APPEND LIBCXXABI_TARGET_SUBDIR /${LIBCXXABI_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
new file mode 100644
index 0000000000000..d18748fe3a3cc
--- /dev/null
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -0,0 +1,30 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++abi using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -I %{libcxx}/test/support -I %{libcxx}/src -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    '-nostdlib++ -L %{lib} -lc++ -lc++abi -pthread'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 02132f6c07fcd..4fd11bf5aca2f 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -142,7 +142,12 @@ set(LIBUNWIND_STATIC_OUTPUT_NAME "unwind" CACHE STRING "Output name for the stat
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBUNWIND_TARGET_SUBDIR)
+  else()
+    set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBUNWIND_LIBDIR_SUBDIR)
     string(APPEND LIBUNWIND_TARGET_SUBDIR /${LIBUNWIND_LIBDIR_SUBDIR})
   endif()
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
new file mode 100644
index 0000000000000..bce241a7898cf
--- /dev/null
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -0,0 +1,55 @@
+#
+# Configuration file for running the libunwind tests against the shared library
+# using HDC on OHOS.
+#
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+compile_flags = []
+link_flags = []
+
+if @HAVE_GETAUXVAL@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_GETAUXVAL')
+
+if @HAVE_ELF_AUX_INFO@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_ELF_AUX_INFO')
+
+if @LIBUNWIND_ENABLE_CET@:
+    compile_flags.append('-fcf-protection=full')
+
+if @LIBUNWIND_ENABLE_GCS@:
+    compile_flags.append('-mbranch-protection=standard')
+
+if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
+    link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
+
+if '@CMAKE_DL_LIBS@':
+    link_flags.append('-l at CMAKE_DL_LIBS@')
+
+compile_flags.append('-funwind-tables')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags))
+))
+config.substitutions.append(('%{link_flags}',
+    '-L %{{lib}} -lunwind {}'.format(' '.join(link_flags))
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)

>From e9e5d2e517fb072f3554798e49b7119ec739105f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 25 Jun 2026 11:30:38 +0800
Subject: [PATCH 2/6] Fix OHOS libc++ CI ABI and SME detection

---
 libcxx/utils/ci/run-buildbot |  2 ++
 libunwind/src/Registers.hpp  | 15 +++++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 1867e9c620649..ecf0ca17ae148 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -192,6 +192,7 @@ function setup-ohos-hdc-env() {
     require-env-var OHOS_SYSROOT
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
     OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
     OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
 }
@@ -209,6 +210,7 @@ function generate-cmake-ohos-hdc() {
           -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE}" \
           -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
           -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
           -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c88422a6d5f4f..18cb2a523bd0e 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -25,6 +25,11 @@
 #endif
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
+#if defined(__has_include)
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
+#endif
 #endif
 
 namespace libunwind {
@@ -1951,18 +1956,16 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
       return false;
     return has_sme != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = getauxval(AT_HWCAP2);
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = 0;
     elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
 #else
   static bool checkHasSME() {

>From 498fa8d127ff49c27d4230996338c6eb5d747fe6 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 1 Jul 2026 15:49:11 +0800
Subject: [PATCH 3/6] Stabilize OHOS HDC runtimes tests

Add OHOS lit features for HDC executor and platform limitations, mark affected libc++ tests as unsupported or expected failures, and adjust test support for OHOS filesystem cleanup and allocation counting behavior.

Also guard the aarch64 ZA unwind test on HWCAP2_SME so devices without a reliable SME feature definition do not execute SME instructions.
---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  9 +++++-
 .../clang/clang_modules_include.gen.py        |  3 ++
 .../locale.facet/no_allocation.pass.cpp       |  1 +
 libcxx/test/selftest/dsl/dsl.sh.py            |  5 ++++
 libcxx/test/selftest/stdin-is-piped.sh.cpp    |  1 +
 .../directory_entry.cons/path.pass.cpp        |  1 +
 .../directory_entry.mods/assign.pass.cpp      |  1 +
 .../directory_entry.mods/refresh.pass.cpp     |  1 +
 .../replace_filename.pass.cpp                 |  1 +
 .../directory_entry.obs/file_size.pass.cpp    |  1 +
 .../file_type_obs.pass.cpp                    |  1 +
 .../hard_link_count.pass.cpp                  |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../directory_iterator.members/ctor.pass.cpp  |  1 +
 .../path.member/path.assign/move.pass.cpp     |  2 +-
 .../path.member/path.construct/move.pass.cpp  |  2 +-
 .../rec.dir.itr.members/ctor.pass.cpp         |  1 +
 .../rec.dir.itr.members/increment.pass.cpp    |  1 +
 .../fs.op.funcs/fs.op.exists/exists.pass.cpp  |  1 +
 .../is_block_file.pass.cpp                    |  1 +
 .../is_character_file.pass.cpp                |  1 +
 .../fs.op.is_directory/is_directory.pass.cpp  |  1 +
 .../fs.op.is_empty/is_empty.pass.cpp          |  1 +
 .../fs.op.is_fifo/is_fifo.pass.cpp            |  1 +
 .../fs.op.is_other/is_other.pass.cpp          |  1 +
 .../is_regular_file.pass.cpp                  |  1 +
 .../fs.op.is_socket/is_socket.pass.cpp        |  1 +
 .../fs.op.is_symlink/is_symlink.pass.cpp      |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../fs.op.funcs/fs.op.remove/remove.pass.cpp  |  1 +
 .../fs.op.remove_all/remove_all.pass.cpp      |  1 +
 .../fs.op.funcs/fs.op.status/status.pass.cpp  |  1 +
 .../symlink_status.pass.cpp                   |  1 +
 .../temp_directory_path.pass.cpp              |  1 +
 .../ext.manip/get_money.pass.cpp              |  1 +
 .../ext.manip/put_money.pass.cpp              |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 .../print.fun/no_file_description.pass.cpp    |  3 ++
 .../std.manip/setfill_wchar_max.pass.cpp      |  1 +
 .../narrow.stream.objects/cerr.sh.cpp         |  1 +
 .../narrow.stream.objects/cin.sh.cpp          |  1 +
 .../narrow.stream.objects/clog.sh.cpp         |  1 +
 .../wide.stream.objects/wcerr-imbue.sh.cpp    |  1 +
 .../wide.stream.objects/wcerr.sh.cpp          |  1 +
 .../wide.stream.objects/wcin-imbue.sh.cpp     |  1 +
 .../wide.stream.objects/wcin.sh.cpp           |  1 +
 .../wide.stream.objects/wclog.sh.cpp          |  1 +
 .../sized_delete_array.pass.cpp               |  1 +
 ..._array_calls_unsized_delete_array.pass.cpp |  1 +
 .../new.delete.single/sized_delete.pass.cpp   |  1 +
 ...sized_delete_calls_unsized_delete.pass.cpp |  1 +
 .../locale.collate.byname/compare.pass.cpp    |  1 +
 .../locale.collate.byname/transform.pass.cpp  |  1 +
 .../facet.ctype.char.dtor/dtor.pass.cpp       |  1 +
 .../locale.ctype.byname/is_1.pass.cpp         |  1 +
 .../locale.ctype.byname/is_many.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_is.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_not.pass.cpp     |  1 +
 .../locale.ctype.byname/tolower_1.pass.cpp    |  1 +
 .../locale.ctype.byname/tolower_many.pass.cpp |  1 +
 .../locale.ctype.byname/toupper_1.pass.cpp    |  1 +
 .../locale.ctype.byname/toupper_many.pass.cpp |  1 +
 .../locale.ctype.byname/widen_1.pass.cpp      |  1 +
 .../locale.ctype.byname/widen_many.pass.cpp   |  1 +
 .../get_long_double_en_US.pass.cpp            |  1 +
 .../get_long_double_zh_CN.pass.cpp            |  1 +
 .../get_string_en_US.pass.cpp                 |  1 +
 .../put_long_double_en_US.pass.cpp            |  1 +
 .../put_long_double_zh_CN.pass.cpp            |  1 +
 .../put_string_en_US.pass.cpp                 |  1 +
 .../locale/locale.cons/default.pass.cpp       |  1 +
 .../test/std/re/re.traits/transform.pass.cpp  |  1 +
 .../re/re.traits/transform_primary.pass.cpp   |  1 +
 .../thread_create_failure.pass.cpp            |  1 +
 .../thread.jthread/join.deadlock.pass.cpp     |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 libcxx/test/support/count_new.h               | 28 +++++++++++++++++++
 libcxx/test/support/filesystem_test_helper.h  |  4 ++-
 libcxx/test/support/test_macros.h             |  4 ++-
 libunwind/test/aarch64_za_unwind.pass.cpp     | 13 +++++----
 80 files changed, 132 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index a20ed9ce9c6a9..542bc0cc83a1f 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -7,7 +7,8 @@ config.substitutions.append(('%{flags}',
     ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
 ))
 config.substitutions.append(('%{compile_flags}',
-    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
 ))
 config.substitutions.append(('%{link_flags}',
     # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
@@ -24,6 +25,12 @@ import libcxx.test.params, libcxx.test.config
 
 config.available_features.add('LIBCXX-OHOS-FIXME')
 config.available_features.add('ohos-hdc')
+config.available_features.add('executor-has-no-stdin')
+config.available_features.add('executor-merges-stderr-into-stdout')
+config.available_features.add('executor-runs-as-root')
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
index 20a545b719ed6..f04a6bce76691 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
index 03e9de882ab0a..428d30804a4c3 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
index 10829f8a73db1..c8a1c34aa3028 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index 071ee7f6c891f..44b94528f5e32 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a hard link.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
index 5d931891dd0eb..d291ca2cb5641 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
index 82b94a3d6bf5f..e4bc9c41728da 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
index 8401dbd38cd71..e0fa44ffbc4a5 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
index ac5499cdd6f20..2bcc64a3c2651 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_assignable<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
index 1f1843f7d401d..5efdf983f8a10 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_constructible<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
index 08d7ff84378d5..a3768b8cbd83f 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
index 1f2293989b7ef..9e8a70b4339d4 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // On Android L, ~scoped_test_env() is unable to delete the temp dir using
 // chmod+rm because chmod is too broken.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
index 56465d01021ad..62dc696d6797d 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
index fad880ecf30f7..708fb511dce73 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
index 12bc0c198c062..69e357adeafb5 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
index f1b559cf2d55b..0ed4aa0dc8d58 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
index f3161f2e020d2..579c03f2b8c0b 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
index fbf5e7129f7af..1d5312d65be46 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
index d0974640429df..7a3a56a0b161c 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
index 4a5882e36cde2..8b877ff63b112 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
index 382685c21dd99..995125d702de3 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
index 07b446d80c402..692fe773a0f9e 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
index c420fea21aa66..22c2c114ba687 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
index 56b4a27de6b42..8064feaaf2065 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
index b30d56685e2cb..540849ccefc05 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
index 425c224606038..02ff1e249d3bc 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
index 1d405a816e110..41474f9ca1e48 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
index 38e14438f3fd3..9a891eab6b343 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
index 88672851f7b6b..3e1d34ab5257a 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
index d0ff2c87689d1..4c299b876639e 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
index 98fa56b7f4b6d..7970887bac199 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
index 283a8fddb8e8f..13e6f74c5a143 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -17,6 +17,9 @@
 // fmemopen is available starting in Android M (API 23)
 // XFAIL: target={{.+}}-android{{(eabi)?(21|22)}}
 
+// OHOS fmemopen/FILE behavior does not currently match the expectation tested here.
+// XFAIL: LIBCXX-OHOS-FIXME
+
 // <print>
 
 // The FILE returned by fmemopen does not have file descriptor.
diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
index b24c0b90d86f1..2ddddbef70dbb 100644
--- a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
@@ -18,6 +18,7 @@
 // XFAIL: target=armv{{7|8}}{{l?}}{{.*}}-linux-gnueabihf && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-linux-gnu && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-amazon-linux && libcpp-abi-version=1
+// XFAIL: target={{.*}}-linux-ohos && libcpp-abi-version=1
 
 #include <iomanip>
 #include <ostream>
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
index da5563f87df45..ea5af90a90822 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
index 4f23b6b9a5d87..6dc0ff36ff4bf 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
@@ -14,6 +14,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
index 9bedd67e816ef..cbb9639766207 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
index ec4c8a009ba0c..59d93e74db3b7 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
index ae82035ccfc62..49a6ce0892ea4 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
index 6ae1972234b2b..58a6e18340ddf 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
index 08b74b999bbf4..88dcfe0485429 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
index 89e178248ae50..9c9745dcc9777 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wclog;
 
 // XFAIL: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
index 0e411f8adc0f2..e563152f2c8cd 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
index 026250daeee89..34119303a29ba 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete [] is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
index ca0f612bf7578..a4e32aaa11e13 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
index fbc9cf070bcba..3ec2b47b53eb0 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
index 8ae6bc2d3ba66..7cfd9f955fce7 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
@@ -8,6 +8,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
index 4978db116ef39..743075ede8ece 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
index 7be7e8d574e6c..b77a4409522f0 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
@@ -11,6 +11,7 @@
 // template <> class ctype<char>
 
 // ~ctype();
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <locale>
 #include <cassert>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
index 5cebc079c4703..c4803c04d173b 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <type_traits>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
index 2869378b42b92..5174ffebf281f 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
index ea03f042f2d45..665da7323e38a 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
index 2f7acb843e591..5d87c081b6e1c 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
index 447353de8af72..93d93aa9228f7 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
index 94ecc6c8fa85b..ab448a01632d6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
index 2b37737a26bc3..4be990e5b88c6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
index 016d8d736ad22..e0a3912a9315e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
index 959a4be9e1de9..b22629c82444e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
index 078b4a6fefb7e..c2cd904808794 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
index 9997b07134563..73b7a6decda36 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
index c86df7e6b53bf..deb30d747653f 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
index 478df7964f6d2..ee6fbd48b09b6 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
index 4b767fae871fa..c476537c3c7e3 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
index 25046a8417083..8174a724d4165 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
index 1c8710a008f27..e23cb4bf5f04a 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
index f0086ada30303..a36e3af8d12eb 100644
--- a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
+++ b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
@@ -12,6 +12,7 @@
 
 // This test runs in C++20, but we have deprecated codecvt<char(16|32), char, mbstate_t> in C++20.
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // <locale>
 
diff --git a/libcxx/test/std/re/re.traits/transform.pass.cpp b/libcxx/test/std/re/re.traits/transform.pass.cpp
index 80cd3f01faff2..112ecd47ff4e2 100644
--- a/libcxx/test/std/re/re.traits/transform.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform.pass.cpp
@@ -8,6 +8,7 @@
 //
 // NetBSD does not support LC_COLLATE at the moment
 // XFAIL: netbsd
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..cbc9387facb30 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -11,6 +11,7 @@
 
 // XFAIL: LIBCXX-AIX-FIXME
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
index 40daa49393bd4..110d0ea9fd694 100644
--- a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
+++ b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
@@ -15,6 +15,7 @@
 
 // There is no way to limit the number of threads on windows
 // UNSUPPORTED: windows
+// UNSUPPORTED: executor-runs-as-root
 
 // macOS and FreeBSD seem to limit the number of processes, not threads via RLIMIT_NPROC.
 // XFAIL: target={{.+}}-apple-{{.*}}
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
index c6eb0694ab2e1..19573ae52fd71 100644
--- a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
@@ -9,6 +9,7 @@
 // Windows cannot detect the deadlock. Instead of throwing system_error,
 // it would dead lock the test
 // UNSUPPORTED: windows
+// UNSUPPORTED: pthread-join-self-deadlocks
 
 // TSAN bug: https://llvm.org/PR66537
 // UNSUPPORTED: tsan
diff --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
index 51be78e1a02f8..804036e425572 100644
--- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
@@ -14,6 +14,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..6901aff00bc4e 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -415,6 +415,13 @@ void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteCalled(p);
+  std::free(p);
+}
+#  endif
+
 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->newArrayCalled(s);
@@ -449,6 +456,13 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteArrayCalled(p);
+  std::free(p);
+}
+#  endif
+
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
 #      define USE_ALIGNED_ALLOC
@@ -519,6 +533,13 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
@@ -551,6 +572,13 @@ void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
 
 #endif // DISABLE_NEW_COUNT
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index 2ad9efb32c60f..f820a4a0fa915 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -182,7 +182,7 @@ struct scoped_test_env
         std::string cmd = "chmod -R 777 " + test_root.string();
 #endif // defined(__MVS__)
         int ret = std::system(cmd.c_str());
-#  if !defined(_AIX) && !defined(__ANDROID__)
+#  if !defined(_AIX) && !defined(__ANDROID__) && !defined(__OHOS__)
         // On AIX the chmod command will return non-zero when trying to set
         // the permissions on a directory that contains a bad symlink. This triggers
         // the assert, despite being able to delete everything with the following
@@ -196,6 +196,8 @@ struct scoped_test_env
         // LIBCXX-ANDROID-FIXME: Other fixes to consider: place a toybox chmod
         // onto old devices, re-enable this assert for devices running Android N
         // and up, rewrite this chmod+rm in C or C++.
+        //
+        // OHOS has the same chmod -R behavior for bad symlinks.
         assert(ret == 0);
 #  endif
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..fdf6bc5b18d72 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -375,7 +375,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NOT_WIN32(...) __VA_ARGS__
 #endif
 
-#if defined(TEST_WINDOWS_DLL) ||defined(__MVS__) || defined(_AIX)
+#if defined(TEST_WINDOWS_DLL) || defined(__MVS__) || defined(_AIX) || defined(TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS)
 // Macros for waiving cases when we can't count allocations done within
 // the library implementation.
 //
@@ -386,6 +386,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 //
 // The same goes on IBM zOS.
 // The same goes on AIX.
+// Some target test configurations can define
+// TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS for the same reason.
 #define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
 #define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
 #else
diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp
index 36536860c1a96..b28f70a6e8e7a 100644
--- a/libunwind/test/aarch64_za_unwind.pass.cpp
+++ b/libunwind/test/aarch64_za_unwind.pass.cpp
@@ -20,6 +20,9 @@
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
 #endif
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
 
 // Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA
 // (and commits a lazy save of ZA) before resuming from unwinding.
@@ -33,18 +36,16 @@ static bool checkHasSME() {
     return false;
   return has_sme != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = getauxval(AT_HWCAP2);
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = 0;
   elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
 #else
 static bool checkHasSME() {

>From bbaa18370c45aaf79c2446be0a6e94dc2aa0ef00 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Fri, 3 Jul 2026 10:33:38 +0800
Subject: [PATCH 4/6] Add qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  16 +-
 libcxx/utils/ci/run-buildbot                  |  41 ++++-
 libcxx/utils/ci/vendor/ohos/qemu_run.py       | 144 ++++++++++++++++++
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |   8 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |   8 +-
 5 files changed, 204 insertions(+), 13 deletions(-)
 create mode 100755 libcxx/utils/ci/vendor/ohos/qemu_run.py

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 542bc0cc83a1f..20aa7a49d5802 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++ using HDC on OHOS.
+# libc++ using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -23,15 +23,21 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
 config.available_features.add('LIBCXX-OHOS-FIXME')
-config.available_features.add('ohos-hdc')
-config.available_features.add('executor-has-no-stdin')
-config.available_features.add('executor-merges-stderr-into-stdout')
-config.available_features.add('executor-runs-as-root')
+config.available_features.add(executor_kind)
 config.available_features.add('platform-has-nonportable-locales')
 config.available_features.add('platform-has-unreliable-new-delete-interposition')
 config.available_features.add('pthread-join-self-deadlocks')
 
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
     libcxx.test.features.DEFAULT_FEATURES,
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index ecf0ca17ae148..40739b0fba4d8 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -28,6 +28,16 @@ ${PROGNAME} [options] <BUILDER>
 --build-dir <DIR>   The directory to use for building the library. By default,
                     this is '<llvm-root>/build/<builder>'.
 
+--ohos-qemu <PATH>  Run OHOS tests under qemu-aarch64 instead of HDC. This also
+                    accepts OHOS_QEMU or QEMU from the environment.
+
+--ohos-qemu-sysroot <DIR>
+                    Sysroot passed to qemu-aarch64 with -L. Defaults to
+                    OHOS_QEMU_SYSROOT, QEMU_SYSROOT, then OHOS_SYSROOT.
+
+--ohos-hdc <PATH>   HDC binary to use when OHOS tests run on a device. This sets
+                    HDC for the OHOS HDC executor.
+
 Environment variables
 CC                  The C compiler to use, this value is used by CMake. This
                     variable is mandatory.
@@ -84,6 +94,19 @@ while [[ $# -gt 0 ]]; do
             BUILD_DIR="${2}"
             shift; shift
             ;;
+        --ohos-qemu)
+            OHOS_QEMU="${2}"
+            shift; shift
+            ;;
+        --ohos-qemu-sysroot)
+            OHOS_QEMU_SYSROOT="${2}"
+            shift; shift
+            ;;
+        --ohos-hdc)
+            HDC="${2}"
+            export HDC
+            shift; shift
+            ;;
         *)
             BUILDER="${1}"
             shift
@@ -193,8 +216,18 @@ function setup-ohos-hdc-env() {
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
     OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
-    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
-    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+    OHOS_QEMU="${OHOS_QEMU:-${QEMU:-}}"
+
+    if [[ -n "${OHOS_QEMU}" ]]; then
+        OHOS_QEMU_SYSROOT="${OHOS_QEMU_SYSROOT:-${QEMU_SYSROOT:-${OHOS_SYSROOT}}}"
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/qemu_run.py --qemu ${OHOS_QEMU} --sysroot ${OHOS_QEMU_SYSROOT}"
+        OHOS_EXECUTOR_KIND="ohos-qemu"
+    else
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+        OHOS_EXECUTOR_KIND="ohos-hdc"
+    fi
+
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE};executor_kind=${OHOS_EXECUTOR_KIND}"
 }
 
 function generate-cmake-ohos-hdc() {
@@ -863,14 +896,14 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
-ohos-hdc-aarch64-full)
+ohos-aarch64-full|ohos-hdc-aarch64-full)
     clean
 
     generate-cmake-ohos-hdc
 
     check-runtimes
 ;;
-ohos-hdc-aarch64-smoke)
+ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
     clean
 
     generate-cmake-ohos-hdc
diff --git a/libcxx/utils/ci/vendor/ohos/qemu_run.py b/libcxx/utils/ci/vendor/ohos/qemu_run.py
new file mode 100755
index 0000000000000..ef7eef7872864
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/qemu_run.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _resolve_binary(binary):
+    if os.path.isabs(binary):
+        if os.path.isfile(binary) and os.access(binary, os.X_OK):
+            return binary
+        sys.exit(f"failed to find executable: {binary}")
+
+    resolved = shutil.which(binary)
+    if resolved is None:
+        sys.exit(f"failed to find executable: {binary}")
+    return resolved
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(str(parent))
+    return library_dirs
+
+
+def _build_target_env_args(env, prepend_env):
+    target_env = dict(env)
+
+    for key, value in prepend_env.items():
+        existing = target_env.get(key, os.environ.get(key, ""))
+        target_env[key] = f"{value}{os.pathsep}{existing}" if existing else value
+
+    args = []
+    for key, value in target_env.items():
+        args.extend(["-E", f"{key}={value}"])
+    return args
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--qemu",
+        default=os.environ.get("OHOS_QEMU", os.environ.get("QEMU", "qemu-aarch64")),
+    )
+    parser.add_argument("--sysroot", required=True)
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(
+            os.environ.get(
+                "OHOS_QEMU_TIMEOUT_SECONDS",
+                os.environ.get("QEMU_TIMEOUT_SECONDS", "600"),
+            )
+        ),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    qemu = _resolve_binary(args.qemu)
+    execdir = Path(args.execdir).resolve()
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    build_library_dirs = _collect_build_library_dirs(execdir)
+    if build_library_dirs:
+        existing = prepend_env.get("LD_LIBRARY_PATH")
+        build_library_path = os.pathsep.join(build_library_dirs)
+        prepend_env["LD_LIBRARY_PATH"] = (
+            f"{build_library_path}{os.pathsep}{existing}"
+            if existing
+            else build_library_path
+        )
+
+    cmd = [
+        qemu,
+        "-L",
+        args.sysroot,
+        *_build_target_env_args(env, prepend_env),
+        *args.command,
+    ]
+    try:
+        completed = subprocess.run(
+            cmd,
+            cwd=execdir,
+            timeout=args.timeout_seconds,
+            check=False,
+        )
+    except subprocess.TimeoutExpired:
+        print(
+            f"qemu executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+            file=sys.stderr,
+        )
+        return 124
+    return completed.returncode
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index d18748fe3a3cc..8f5a17b3dec64 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++abi using HDC on OHOS.
+# libc++abi using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -20,7 +20,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index bce241a7898cf..f2a032c41a123 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -1,6 +1,6 @@
 #
 # Configuration file for running the libunwind tests against the shared library
-# using HDC on OHOS.
+# using HDC or QEMU on OHOS.
 #
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
@@ -45,7 +45,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,

>From dde4d430033711fb7ffa87621ba86b37492f480b Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 14:52:40 +0800
Subject: [PATCH 5/6] Fix something for qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in                   | 4 +++-
 .../file.streams/fstreams/filebuf.members/close.pass.cpp      | 3 +++
 .../fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp     | 2 ++
 .../syncstream/osyncstream/thread/several_threads.pass.cpp    | 2 ++
 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in             | 4 +++-
 libcxxabi/test/guard_threaded_test.pass.cpp                   | 2 ++
 libunwind/test/bad_unwind_info.pass.cpp                       | 2 ++
 libunwind/test/configs/llvm-libunwind-ohos.cfg.in             | 4 +++-
 libunwind/test/signal_unwind.pass.cpp                         | 2 ++
 9 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 20aa7a49d5802..3be1c267ede2a 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -23,7 +23,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
index 2c4cfb0ac7661..ce802d6ceba78 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14
 // REQUIRES: linux
 // UNSUPPORTED: no-filesystem
+// OHOS qemu-user observes the host /proc, not a real OHOS process /proc.
+// UNSUPPORTED: ohos-qemu
 // XFAIL: no-localization
 
 // <filesystem>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..77db6396755bf 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -10,6 +10,8 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
+// This thread-heavy stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 // <syncstream>
 
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index 8f5a17b3dec64..d0a8d1248f88a 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -20,7 +20,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxxabi/test/guard_threaded_test.pass.cpp b/libcxxabi/test/guard_threaded_test.pass.cpp
index d898e60288aca..a4e14f0aca9e4 100644
--- a/libcxxabi/test/guard_threaded_test.pass.cpp
+++ b/libcxxabi/test/guard_threaded_test.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: no-exceptions
+// This thread-heavy guard stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 #define TESTING_CXA_GUARD
 #include "../src/cxa_guard_impl.h"
diff --git a/libunwind/test/bad_unwind_info.pass.cpp b/libunwind/test/bad_unwind_info.pass.cpp
index 332b661d2e98f..76102505f2d67 100644
--- a/libunwind/test/bad_unwind_info.pass.cpp
+++ b/libunwind/test/bad_unwind_info.pass.cpp
@@ -13,6 +13,8 @@
 // REQUIRES: target={{(aarch64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
 // UNSUPPORTED: target={{.*-apple.*}}
+// OHOS qemu-user does not reliably model this invalid unwind-info path.
+// UNSUPPORTED: ohos-qemu
 
 // GCC doesn't support __attribute__((naked)) on AArch64.
 // UNSUPPORTED: gcc
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index f2a032c41a123..71d1e711e36d5 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -45,7 +45,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp
index f8798bc3eabeb..ef73a3ea8c59a 100644
--- a/libunwind/test/signal_unwind.pass.cpp
+++ b/libunwind/test/signal_unwind.pass.cpp
@@ -10,6 +10,8 @@
 // Ensure that the unwinder can cope with the signal handler.
 // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
+// OHOS qemu-user does not provide device-equivalent signal unwinding behavior.
+// UNSUPPORTED: ohos-qemu
 
 // TODO: Figure out why this fails with Memory Sanitizer.
 // XFAIL: msan

>From c81df7252fb00b5b9b41f0a29393e775abc9c810 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 15:19:29 +0800
Subject: [PATCH 6/6] delete run-smoke-test

---
 libcxx/utils/ci/buildkite-pipeline.yml | 12 ----------
 libcxx/utils/ci/run-buildbot           | 31 --------------------------
 2 files changed, 43 deletions(-)

diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 77617f43ae6dc..70bc69637a7f2 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -175,18 +175,6 @@ steps:
 
 - group: OHOS
   steps:
-  - label: OHOS AArch64 HDC Smoke
-    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
-    env:
-      CC: clang
-      CXX: clang++
-      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
-      HDC_TIMEOUT_SECONDS: 240
-    agents:
-      queue: libcxx-builders
-      os: linux
-      device: ohos-hdc
-    <<: *common
   - label: OHOS AArch64 HDC Full
     command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
     env:
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 40739b0fba4d8..e7d751df484e4 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -147,23 +147,6 @@ function require-env-var() {
     fi
 }
 
-function detect-lit() {
-    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
-        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
-    elif command -v llvm-lit >/dev/null 2>&1; then
-        LIT_RUNNER=("$(command -v llvm-lit)")
-    else
-        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
-    fi
-}
-
-function run-smoke-test() {
-    local test_dir="${1}"
-    local filter="${2}"
-    step "Running smoke test ${filter}"
-    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
-}
-
 function ohos-lit-args() {
     local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
     if python3 -c 'import psutil' >/dev/null 2>&1; then
@@ -903,20 +886,6 @@ ohos-aarch64-full|ohos-hdc-aarch64-full)
 
     check-runtimes
 ;;
-ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
-    clean
-
-    generate-cmake-ohos-hdc
-
-    step "Building the OHOS smoke test dependencies"
-    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
-
-    detect-lit
-    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
-;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #


From libcxx-commits at lists.llvm.org  Thu Jul  9 01:17:45 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 01:17:45 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a4f5929.cb30c3e4.3ce559.48c4@mx.google.com>


https://github.com/huihui15537226823 updated https://github.com/llvm/llvm-project/pull/208390

>From 70e8ac839667f8aba4aa74a2dd19614426f9754f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 24 Jun 2026 09:47:00 +0800
Subject: [PATCH 1/6] Add OHOS HDC runtimes CI support

---
 cmake/Modules/GetToolchainDirs.cmake          |  37 +-
 .../cmake/Modules/CompilerRTUtils.cmake       |   8 +-
 compiler-rt/cmake/builtin-config-ix.cmake     |   2 +-
 compiler-rt/cmake/crt-config-ix.cmake         |   2 +-
 compiler-rt/lib/builtins/crtbegin.c           |   9 +
 compiler-rt/lib/builtins/emutls.c             |   8 +-
 libcxx/CMakeLists.txt                         |  10 +-
 libcxx/cmake/config-ix.cmake                  |   2 +
 libcxx/include/__config                       |   5 +-
 libcxx/include/__cxx03/__config               |   5 +-
 .../__cxx03/__locale_dir/locale_base_api.h    |   2 +-
 libcxx/include/__locale_dir/locale_base_api.h |   2 +-
 libcxx/src/include/config_elast.h             |   2 +
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  48 ++
 libcxx/utils/ci/buildkite-pipeline.yml        |  27 +
 libcxx/utils/ci/run-buildbot                  |  87 ++++
 libcxx/utils/ci/vendor/ohos/hdc_run.py        | 472 ++++++++++++++++++
 libcxx/utils/ci/vendor/ohos/hdc_run_test.py   | 115 +++++
 libcxxabi/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |  30 ++
 libunwind/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |  55 ++
 22 files changed, 910 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/test/configs/llvm-libc++-ohos.cfg.in
 create mode 100755 libcxx/utils/ci/vendor/ohos/hdc_run.py
 create mode 100644 libcxx/utils/ci/vendor/ohos/hdc_run_test.py
 create mode 100644 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
 create mode 100644 libunwind/test/configs/llvm-libunwind-ohos.cfg.in

diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..a20ed9ce9c6a9
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,48 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index d4107cf11ad51..77617f43ae6dc 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -172,3 +172,30 @@ steps:
       queue: libcxx-builders
       os: android
     <<: *common
+
+- group: OHOS
+  steps:
+  - label: OHOS AArch64 HDC Smoke
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
+  - label: OHOS AArch64 HDC Full
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 6376bc1d3dd5a..1867e9c620649 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -116,6 +116,41 @@ function clean() {
     rm -rf "${BUILD_DIR}"
 }
 
+function require-env-var() {
+    local name="${1}"
+    if [[ -z ${!name+x} ]] || [[ -z "${!name}" ]]; then
+        error "Environment variable ${name} must be defined"
+        exit 1
+    fi
+}
+
+function detect-lit() {
+    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
+        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
+    elif command -v llvm-lit >/dev/null 2>&1; then
+        LIT_RUNNER=("$(command -v llvm-lit)")
+    else
+        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
+    fi
+}
+
+function run-smoke-test() {
+    local test_dir="${1}"
+    local filter="${2}"
+    step "Running smoke test ${filter}"
+    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
+}
+
+function ohos-lit-args() {
+    local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
+    if python3 -c 'import psutil' >/dev/null 2>&1; then
+        args="${args} --timeout=1500"
+    else
+        echo "warning: python3 psutil not available; running OHOS lit without per-test timeout" >&2
+    fi
+    printf '%s' "${args}"
+}
+
 function generate-cmake-base() {
     step "Generating CMake"
 
@@ -153,6 +188,37 @@ function generate-cmake-android() {
           "${@}"
 }
 
+function setup-ohos-hdc-env() {
+    require-env-var OHOS_SYSROOT
+
+    OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+}
+
+function generate-cmake-ohos-hdc() {
+    setup-ohos-hdc-env
+
+    local lit_args
+    lit_args="$(ohos-lit-args)"
+
+    generate-cmake \
+          -DLLVM_LIT_ARGS="${lit_args}" \
+          -DCMAKE_SYSTEM_NAME=OHOS \
+          -DCMAKE_SYSROOT="${OHOS_SYSROOT}" \
+          -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
+          -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
+          -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
+          -DLIBUNWIND_TEST_CONFIG="llvm-libunwind-ohos.cfg.in" \
+          -DLIBCXX_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBCXXABI_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBUNWIND_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          "${@}"
+}
+
 function check-runtimes() {
     step "Building libc++ test dependencies"
     ninja -vC "${BUILD_DIR}" cxx-test-depends
@@ -795,6 +861,27 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
+ohos-hdc-aarch64-full)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    check-runtimes
+;;
+ohos-hdc-aarch64-smoke)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    step "Building the OHOS smoke test dependencies"
+    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
+
+    detect-lit
+    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
+;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run.py b/libcxx/utils/ci/vendor/ohos/hdc_run.py
new file mode 100755
index 0000000000000..61a3899b88b11
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run.py
@@ -0,0 +1,472 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import uuid
+
+
+EXIT_MARKER = "__llvm_hdc_exit__="
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _split_path_list(value):
+    return [entry for entry in value.split(os.pathsep) if entry]
+
+
+def _resolve_hdc_binary(hdc):
+    if os.path.isabs(hdc):
+        return hdc
+    resolved = shutil.which(hdc)
+    if resolved is None:
+        sys.exit(f"failed to find hdc binary: {hdc}")
+    return resolved
+
+
+def _is_windows_hdc(path):
+    return path.lower().endswith(".exe")
+
+
+def _find_windows_local_root(hdc_path):
+    parts = Path(hdc_path).parts
+    for index in range(len(parts) - 1):
+        if parts[index] == "AppData" and index + 1 < len(parts) and parts[index + 1] == "Local":
+            return Path(*parts[: index + 2])
+    return None
+
+
+def _default_host_staging_root(hdc_path):
+    if _is_windows_hdc(hdc_path):
+        local_root = _find_windows_local_root(hdc_path)
+        if local_root is None:
+            sys.exit(
+                "failed to derive a Windows staging directory from the hdc.exe path; "
+                "set HDC_HOST_STAGING_ROOT to a path under /mnt/<drive>/..."
+            )
+        return local_root / "Temp" / "llvm-lit-hdc"
+    return Path("/tmp/llvm-lit-hdc")
+
+
+def _to_windows_path(path):
+    resolved = Path(path).resolve()
+    parts = resolved.parts
+    if len(parts) < 4 or parts[1] != "mnt":
+        sys.exit(
+            f"Windows hdc requires a staging path under /mnt/<drive>/..., got: {resolved}"
+        )
+    drive = parts[2].upper()
+    tail = "\\".join(parts[3:])
+    return f"{drive}:\\{tail}" if tail else f"{drive}:\\"
+
+
+def _run_readelf(path):
+    completed = subprocess.run(
+        ["readelf", "-d", path],
+        check=False,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        text=True,
+    )
+    if completed.returncode != 0:
+        return ""
+    return completed.stdout
+
+
+def _read_elf_metadata(path):
+    needed = []
+    runpath_dirs = []
+    seen_dirs = set()
+    for line in _run_readelf(path).splitlines():
+        needed_match = re.search(r"\(NEEDED\).*Shared library: \[(.*)\]", line)
+        if needed_match:
+            needed.append(needed_match.group(1))
+            continue
+
+        runpath_match = re.search(
+            r"\((?:RUNPATH|RPATH)\).*Library runpath: \[(.*)\]", line
+        )
+        if not runpath_match:
+            continue
+        for entry in _split_path_list(runpath_match.group(1)):
+            candidate = Path(entry)
+            if not candidate.is_dir():
+                continue
+            key = str(candidate.resolve())
+            if key in seen_dirs:
+                continue
+            seen_dirs.add(key)
+            runpath_dirs.append(candidate)
+    return needed, runpath_dirs
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(parent)
+    return library_dirs
+
+
+def _find_dependency(name, search_dirs):
+    for directory in search_dirs:
+        candidate = Path(directory) / name
+        if candidate.is_file():
+            return candidate.resolve()
+    return None
+
+
+def _copy_needed_libraries(executables, execdir, staging_execdir):
+    search_dirs = _collect_build_library_dirs(execdir)
+    queue = [Path(exe).resolve() for exe in executables]
+    visited = set()
+    copied_names = {path.name for path in staging_execdir.glob("*") if path.is_file()}
+
+    while queue:
+        current = queue.pop(0)
+        current_key = str(current)
+        if current_key in visited:
+            continue
+        visited.add(current_key)
+
+        needed, runpath_dirs = _read_elf_metadata(current)
+        current_search_dirs = []
+        current_seen = set()
+        for directory in [*runpath_dirs, current.parent, *search_dirs]:
+            resolved = Path(directory).resolve()
+            key = str(resolved)
+            if key in current_seen:
+                continue
+            current_seen.add(key)
+            current_search_dirs.append(resolved)
+
+        for directory in runpath_dirs:
+            resolved = directory.resolve()
+            if all(str(resolved) != str(existing) for existing in search_dirs):
+                search_dirs.append(resolved)
+
+        for lib_name in needed:
+            dependency = _find_dependency(lib_name, current_search_dirs)
+            if dependency is None:
+                continue
+            if lib_name not in copied_names:
+                shutil.copy2(dependency, staging_execdir / lib_name)
+                copied_names.add(lib_name)
+            queue.append(dependency)
+
+
+def _copy_execdir(execdir, staging_execdir):
+    if staging_execdir.exists():
+        shutil.rmtree(staging_execdir)
+    shutil.copytree(execdir, staging_execdir)
+
+
+def _iter_files(root):
+    for directory, dirnames, filenames in os.walk(root):
+        dirnames.sort()
+        filenames.sort()
+        yield Path(directory), filenames
+
+
+def _is_test_executable(path):
+    return path.endswith(".tmp.exe") and os.path.exists(path)
+
+
+def _map_local_path(path, local_execdir, remote_execdir):
+    normalized = os.path.normpath(path)
+    execdir = os.path.normpath(local_execdir)
+    if normalized == execdir:
+        return remote_execdir
+    prefix = execdir + os.sep
+    if normalized.startswith(prefix):
+        suffix = normalized[len(prefix) :].replace(os.sep, "/")
+        return f"{remote_execdir}/{suffix}" if suffix else remote_execdir
+    return path
+
+
+def _map_value(value, local_execdir, remote_execdir):
+    return _map_local_path(value, local_execdir, remote_execdir)
+
+
+def _build_env_exports(env, prepend_env, local_execdir, remote_execdir, add_remote_libdir):
+    commands = []
+    mapped_env = {
+        key: _map_value(value, local_execdir, remote_execdir) for key, value in env.items()
+    }
+    mapped_prepend = {
+        key: _map_value(value, local_execdir, remote_execdir)
+        for key, value in prepend_env.items()
+    }
+
+    if add_remote_libdir:
+        existing = mapped_prepend.get("LD_LIBRARY_PATH")
+        mapped_prepend["LD_LIBRARY_PATH"] = (
+            f"{remote_execdir}:{existing}" if existing else remote_execdir
+        )
+
+    for key, value in mapped_env.items():
+        commands.append(f"export {key}={shlex.quote(value)}")
+    for key, value in mapped_prepend.items():
+        commands.append(f"export {key}={shlex.quote(value)}${{{key}:+:{key}}}")
+    return commands
+
+
+def _hdc_prefix(hdc_path, server, target):
+    prefix = [hdc_path]
+    if server:
+        prefix.extend(["-s", server])
+    if target:
+        prefix.extend(["-t", target])
+    return prefix
+
+
+def _run_hdc(
+    prefix,
+    args,
+    *,
+    timeout=None,
+    capture_output=False,
+    stdin=subprocess.DEVNULL,
+    check=True,
+    expect_stdout=None,
+):
+    kwargs = {
+        "check": False,
+        "timeout": timeout,
+        "stdin": stdin,
+        "text": True,
+    }
+    if capture_output:
+        kwargs["stdout"] = subprocess.PIPE
+        kwargs["stderr"] = subprocess.PIPE
+    completed = subprocess.run(prefix + args, **kwargs)
+    output = completed.stdout if capture_output and completed.stdout else ""
+    error_output = completed.stderr if capture_output and completed.stderr else ""
+    if check and completed.returncode != 0:
+        raise RuntimeError(
+            f"hdc command failed with exit code {completed.returncode}: "
+            f"{shlex.join(prefix + args)}\n{output}{error_output}"
+        )
+    if check and expect_stdout is not None and expect_stdout not in output:
+        raise RuntimeError(
+            f"hdc command did not report success: {shlex.join(prefix + args)}\n{output}"
+        )
+    return completed
+
+
+def _run_checked_shell(prefix, command, *, timeout=None):
+    completed = _run_hdc(
+        prefix,
+        [
+            "shell",
+            f"{command}; rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"",
+        ],
+        timeout=timeout,
+        capture_output=True,
+    )
+    exit_code, output = _extract_exit_code(completed.stdout)
+    if exit_code is None:
+        raise RuntimeError(
+            f"failed to parse shell exit code for: {command}\n{completed.stdout}"
+        )
+    if exit_code != 0:
+        raise RuntimeError(
+            f"remote shell command failed with exit code {exit_code}: {command}\n{output}"
+        )
+    return output
+
+
+def _sync_tree_to_device(prefix, local_root, remote_root, use_windows_paths):
+    _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_root)}")
+    _run_checked_shell(prefix, f"mkdir -p {shlex.quote(remote_root)}")
+
+    for directory, filenames in _iter_files(local_root):
+        relative_dir = os.path.relpath(directory, local_root)
+        device_dir = (
+            remote_root
+            if relative_dir == "."
+            else f"{remote_root}/{relative_dir.replace(os.sep, '/')}"
+        )
+        _run_checked_shell(prefix, f"mkdir -p {shlex.quote(device_dir)}")
+        for name in filenames:
+            local_path = directory / name
+            device_path = f"{device_dir}/{name}"
+            source = _to_windows_path(local_path) if use_windows_paths else str(local_path)
+            _run_hdc(
+                prefix,
+                ["file", "send", source, device_path],
+                capture_output=True,
+                expect_stdout="FileTransfer finish",
+            )
+            if name.endswith(".tmp.exe") or os.access(local_path, os.X_OK):
+                _run_checked_shell(prefix, f"chmod +x {shlex.quote(device_path)}")
+
+
+def _build_remote_command(command, local_execdir, remote_execdir, exports):
+    mapped_command = [
+        _map_local_path(arg, local_execdir, remote_execdir) for arg in command
+    ]
+    pieces = [f"cd {shlex.quote(remote_execdir)}"]
+    pieces.extend(exports)
+    pieces.append(shlex.join(mapped_command))
+    pieces.append(f"rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"")
+    return " && ".join(pieces[:-2]) + f"; {pieces[-2]}; {pieces[-1]}"
+
+
+def _extract_exit_code(output):
+    match = re.search(rf"^(.*)\n{EXIT_MARKER}(\d+)\s*$", output, re.DOTALL)
+    if not match:
+        return None, output
+    return int(match.group(2)), match.group(1)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--hdc", default=os.environ.get("HDC", "hdc"))
+    parser.add_argument("--hdc-server", default=os.environ.get("HDC_SERVER_IP_PORT"))
+    parser.add_argument("--hdc-target", default=os.environ.get("HDC_UTID"))
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--remote-base-dir",
+        default="/data/local/tmp/llvm-lit-hdc",
+    )
+    parser.add_argument(
+        "--host-staging-root",
+        default=os.environ.get("HDC_HOST_STAGING_ROOT"),
+    )
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(os.environ.get("HDC_TIMEOUT_SECONDS", "120")),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("--keep-remote-dir", action="store_true")
+    parser.add_argument("--keep-host-staging", action="store_true")
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    hdc_path = _resolve_hdc_binary(args.hdc)
+    use_windows_paths = _is_windows_hdc(hdc_path)
+    host_staging_root = (
+        Path(args.host_staging_root)
+        if args.host_staging_root
+        else _default_host_staging_root(hdc_path)
+    )
+    prefix = _hdc_prefix(hdc_path, args.hdc_server, args.hdc_target)
+
+    execdir = Path(args.execdir).resolve()
+    run_id = uuid.uuid4().hex[:12]
+    staging_execdir = host_staging_root / f"run-{run_id}" / execdir.name
+    remote_execdir = f"{args.remote_base_dir}/run-{run_id}/{execdir.name}"
+
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    executables = [arg for arg in args.command if _is_test_executable(arg)]
+    host_staging_root.mkdir(parents=True, exist_ok=True)
+    _copy_execdir(execdir, staging_execdir)
+    _copy_needed_libraries(executables, execdir, staging_execdir)
+
+    add_remote_libdir = any(staging_execdir.glob("*.so*"))
+    exports = _build_env_exports(
+        env,
+        prepend_env,
+        str(execdir),
+        remote_execdir,
+        add_remote_libdir,
+    )
+
+    try:
+        _sync_tree_to_device(prefix, staging_execdir, remote_execdir, use_windows_paths)
+        remote_command = _build_remote_command(
+            args.command,
+            str(execdir),
+            remote_execdir,
+            exports,
+        )
+        try:
+            completed = _run_hdc(
+                prefix,
+                ["shell", remote_command],
+                timeout=args.timeout_seconds,
+                capture_output=True,
+                stdin=None,
+            )
+        except subprocess.TimeoutExpired:
+            print(
+                f"hdc executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+                file=sys.stderr,
+            )
+            return 124
+
+        exit_code, output = _extract_exit_code(completed.stdout)
+        if exit_code is None:
+            print(
+                "failed to parse exit code from hdc shell output",
+                file=sys.stderr,
+            )
+            if completed.stdout:
+                print(completed.stdout, end="", file=sys.stderr)
+            if completed.stderr:
+                print(completed.stderr, end="", file=sys.stderr)
+            return 1
+
+        if output:
+            sys.stdout.write(output)
+        if completed.stderr:
+            sys.stderr.write(completed.stderr)
+        return exit_code
+    finally:
+        if not args.keep_remote_dir:
+            try:
+                _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_execdir)}")
+            except Exception:
+                pass
+        if not args.keep_host_staging:
+            shutil.rmtree(staging_execdir.parent, ignore_errors=True)
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run_test.py b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
new file mode 100644
index 0000000000000..17e2f65bd1fb6
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import os
+from pathlib import Path
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import unittest
+
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+HDC_RUN = SCRIPT_DIR / "hdc_run.py"
+
+
+class HdcRunTest(unittest.TestCase):
+    def make_fake_hdc(self, directory):
+        fake_hdc = directory / "hdc"
+        fake_hdc.write_text(
+            textwrap.dedent(
+                """\
+                #!/usr/bin/env python3
+                import sys
+
+                EXIT_MARKER = "__llvm_hdc_exit__="
+
+                def main():
+                    args = sys.argv[1:]
+                    if args[:2] == ["file", "send"]:
+                        print("FileTransfer finish")
+                        return 0
+
+                    if args and args[0] == "shell":
+                        command = args[1]
+                        if "stderr.tmp.exe" in command:
+                            sys.stdout.write("STDOUT-OUTPUT")
+                            sys.stderr.write("STDERR-OUTPUT")
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        if "stdin.tmp.exe" in command:
+                            sys.stdout.write("IN:" + sys.stdin.read())
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                        return 0
+
+                    print("unexpected fake hdc invocation: " + repr(args), file=sys.stderr)
+                    return 2
+
+                if __name__ == "__main__":
+                    raise SystemExit(main())
+                """
+            )
+        )
+        fake_hdc.chmod(fake_hdc.stat().st_mode | stat.S_IXUSR)
+        return fake_hdc
+
+    def run_hdc_run(self, temp_dir, executable_name, *, stdin_text=None):
+        execdir = temp_dir / "execdir"
+        execdir.mkdir()
+        executable = execdir / executable_name
+        executable.write_text("# fake executable marker\n")
+        executable.chmod(executable.stat().st_mode | stat.S_IXUSR)
+
+        fake_hdc = self.make_fake_hdc(temp_dir)
+        return subprocess.run(
+            [
+                sys.executable,
+                str(HDC_RUN),
+                "--hdc",
+                str(fake_hdc),
+                "--execdir",
+                str(execdir),
+                "--host-staging-root",
+                str(temp_dir / "staging"),
+                str(executable),
+            ],
+            input=stdin_text,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+    def test_preserves_remote_stdout_and_stderr_separately(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(Path(temp), "stderr.tmp.exe")
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "STDOUT-OUTPUT")
+        self.assertEqual(completed.stderr, "STDERR-OUTPUT")
+
+    def test_forwards_stdin_to_remote_command(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(
+                Path(temp),
+                "stdin.tmp.exe",
+                stdin_text="hello from stdin",
+            )
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "IN:hello from stdin")
+        self.assertEqual(completed.stderr, "")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt
index e1a1587fb6283..cb22e6fe65108 100644
--- a/libcxxabi/CMakeLists.txt
+++ b/libcxxabi/CMakeLists.txt
@@ -187,7 +187,12 @@ set(LIBCXXABI_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXXABI_TARGET_SUBDIR)
+  else()
+    set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXXABI_LIBDIR_SUBDIR)
     string(APPEND LIBCXXABI_TARGET_SUBDIR /${LIBCXXABI_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
new file mode 100644
index 0000000000000..d18748fe3a3cc
--- /dev/null
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -0,0 +1,30 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++abi using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -I %{libcxx}/test/support -I %{libcxx}/src -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    '-nostdlib++ -L %{lib} -lc++ -lc++abi -pthread'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 02132f6c07fcd..4fd11bf5aca2f 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -142,7 +142,12 @@ set(LIBUNWIND_STATIC_OUTPUT_NAME "unwind" CACHE STRING "Output name for the stat
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBUNWIND_TARGET_SUBDIR)
+  else()
+    set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBUNWIND_LIBDIR_SUBDIR)
     string(APPEND LIBUNWIND_TARGET_SUBDIR /${LIBUNWIND_LIBDIR_SUBDIR})
   endif()
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
new file mode 100644
index 0000000000000..bce241a7898cf
--- /dev/null
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -0,0 +1,55 @@
+#
+# Configuration file for running the libunwind tests against the shared library
+# using HDC on OHOS.
+#
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+compile_flags = []
+link_flags = []
+
+if @HAVE_GETAUXVAL@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_GETAUXVAL')
+
+if @HAVE_ELF_AUX_INFO@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_ELF_AUX_INFO')
+
+if @LIBUNWIND_ENABLE_CET@:
+    compile_flags.append('-fcf-protection=full')
+
+if @LIBUNWIND_ENABLE_GCS@:
+    compile_flags.append('-mbranch-protection=standard')
+
+if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
+    link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
+
+if '@CMAKE_DL_LIBS@':
+    link_flags.append('-l at CMAKE_DL_LIBS@')
+
+compile_flags.append('-funwind-tables')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags))
+))
+config.substitutions.append(('%{link_flags}',
+    '-L %{{lib}} -lunwind {}'.format(' '.join(link_flags))
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)

>From e9e5d2e517fb072f3554798e49b7119ec739105f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 25 Jun 2026 11:30:38 +0800
Subject: [PATCH 2/6] Fix OHOS libc++ CI ABI and SME detection

---
 libcxx/utils/ci/run-buildbot |  2 ++
 libunwind/src/Registers.hpp  | 15 +++++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 1867e9c620649..ecf0ca17ae148 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -192,6 +192,7 @@ function setup-ohos-hdc-env() {
     require-env-var OHOS_SYSROOT
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
     OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
     OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
 }
@@ -209,6 +210,7 @@ function generate-cmake-ohos-hdc() {
           -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE}" \
           -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
           -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
           -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c88422a6d5f4f..18cb2a523bd0e 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -25,6 +25,11 @@
 #endif
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
+#if defined(__has_include)
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
+#endif
 #endif
 
 namespace libunwind {
@@ -1951,18 +1956,16 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
       return false;
     return has_sme != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = getauxval(AT_HWCAP2);
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = 0;
     elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
 #else
   static bool checkHasSME() {

>From 498fa8d127ff49c27d4230996338c6eb5d747fe6 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 1 Jul 2026 15:49:11 +0800
Subject: [PATCH 3/6] Stabilize OHOS HDC runtimes tests

Add OHOS lit features for HDC executor and platform limitations, mark affected libc++ tests as unsupported or expected failures, and adjust test support for OHOS filesystem cleanup and allocation counting behavior.

Also guard the aarch64 ZA unwind test on HWCAP2_SME so devices without a reliable SME feature definition do not execute SME instructions.
---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  9 +++++-
 .../clang/clang_modules_include.gen.py        |  3 ++
 .../locale.facet/no_allocation.pass.cpp       |  1 +
 libcxx/test/selftest/dsl/dsl.sh.py            |  5 ++++
 libcxx/test/selftest/stdin-is-piped.sh.cpp    |  1 +
 .../directory_entry.cons/path.pass.cpp        |  1 +
 .../directory_entry.mods/assign.pass.cpp      |  1 +
 .../directory_entry.mods/refresh.pass.cpp     |  1 +
 .../replace_filename.pass.cpp                 |  1 +
 .../directory_entry.obs/file_size.pass.cpp    |  1 +
 .../file_type_obs.pass.cpp                    |  1 +
 .../hard_link_count.pass.cpp                  |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../directory_iterator.members/ctor.pass.cpp  |  1 +
 .../path.member/path.assign/move.pass.cpp     |  2 +-
 .../path.member/path.construct/move.pass.cpp  |  2 +-
 .../rec.dir.itr.members/ctor.pass.cpp         |  1 +
 .../rec.dir.itr.members/increment.pass.cpp    |  1 +
 .../fs.op.funcs/fs.op.exists/exists.pass.cpp  |  1 +
 .../is_block_file.pass.cpp                    |  1 +
 .../is_character_file.pass.cpp                |  1 +
 .../fs.op.is_directory/is_directory.pass.cpp  |  1 +
 .../fs.op.is_empty/is_empty.pass.cpp          |  1 +
 .../fs.op.is_fifo/is_fifo.pass.cpp            |  1 +
 .../fs.op.is_other/is_other.pass.cpp          |  1 +
 .../is_regular_file.pass.cpp                  |  1 +
 .../fs.op.is_socket/is_socket.pass.cpp        |  1 +
 .../fs.op.is_symlink/is_symlink.pass.cpp      |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../fs.op.funcs/fs.op.remove/remove.pass.cpp  |  1 +
 .../fs.op.remove_all/remove_all.pass.cpp      |  1 +
 .../fs.op.funcs/fs.op.status/status.pass.cpp  |  1 +
 .../symlink_status.pass.cpp                   |  1 +
 .../temp_directory_path.pass.cpp              |  1 +
 .../ext.manip/get_money.pass.cpp              |  1 +
 .../ext.manip/put_money.pass.cpp              |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 .../print.fun/no_file_description.pass.cpp    |  3 ++
 .../std.manip/setfill_wchar_max.pass.cpp      |  1 +
 .../narrow.stream.objects/cerr.sh.cpp         |  1 +
 .../narrow.stream.objects/cin.sh.cpp          |  1 +
 .../narrow.stream.objects/clog.sh.cpp         |  1 +
 .../wide.stream.objects/wcerr-imbue.sh.cpp    |  1 +
 .../wide.stream.objects/wcerr.sh.cpp          |  1 +
 .../wide.stream.objects/wcin-imbue.sh.cpp     |  1 +
 .../wide.stream.objects/wcin.sh.cpp           |  1 +
 .../wide.stream.objects/wclog.sh.cpp          |  1 +
 .../sized_delete_array.pass.cpp               |  1 +
 ..._array_calls_unsized_delete_array.pass.cpp |  1 +
 .../new.delete.single/sized_delete.pass.cpp   |  1 +
 ...sized_delete_calls_unsized_delete.pass.cpp |  1 +
 .../locale.collate.byname/compare.pass.cpp    |  1 +
 .../locale.collate.byname/transform.pass.cpp  |  1 +
 .../facet.ctype.char.dtor/dtor.pass.cpp       |  1 +
 .../locale.ctype.byname/is_1.pass.cpp         |  1 +
 .../locale.ctype.byname/is_many.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_is.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_not.pass.cpp     |  1 +
 .../locale.ctype.byname/tolower_1.pass.cpp    |  1 +
 .../locale.ctype.byname/tolower_many.pass.cpp |  1 +
 .../locale.ctype.byname/toupper_1.pass.cpp    |  1 +
 .../locale.ctype.byname/toupper_many.pass.cpp |  1 +
 .../locale.ctype.byname/widen_1.pass.cpp      |  1 +
 .../locale.ctype.byname/widen_many.pass.cpp   |  1 +
 .../get_long_double_en_US.pass.cpp            |  1 +
 .../get_long_double_zh_CN.pass.cpp            |  1 +
 .../get_string_en_US.pass.cpp                 |  1 +
 .../put_long_double_en_US.pass.cpp            |  1 +
 .../put_long_double_zh_CN.pass.cpp            |  1 +
 .../put_string_en_US.pass.cpp                 |  1 +
 .../locale/locale.cons/default.pass.cpp       |  1 +
 .../test/std/re/re.traits/transform.pass.cpp  |  1 +
 .../re/re.traits/transform_primary.pass.cpp   |  1 +
 .../thread_create_failure.pass.cpp            |  1 +
 .../thread.jthread/join.deadlock.pass.cpp     |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 libcxx/test/support/count_new.h               | 28 +++++++++++++++++++
 libcxx/test/support/filesystem_test_helper.h  |  4 ++-
 libcxx/test/support/test_macros.h             |  4 ++-
 libunwind/test/aarch64_za_unwind.pass.cpp     | 13 +++++----
 80 files changed, 132 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index a20ed9ce9c6a9..542bc0cc83a1f 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -7,7 +7,8 @@ config.substitutions.append(('%{flags}',
     ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
 ))
 config.substitutions.append(('%{compile_flags}',
-    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
 ))
 config.substitutions.append(('%{link_flags}',
     # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
@@ -24,6 +25,12 @@ import libcxx.test.params, libcxx.test.config
 
 config.available_features.add('LIBCXX-OHOS-FIXME')
 config.available_features.add('ohos-hdc')
+config.available_features.add('executor-has-no-stdin')
+config.available_features.add('executor-merges-stderr-into-stdout')
+config.available_features.add('executor-runs-as-root')
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
index 20a545b719ed6..f04a6bce76691 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
index 03e9de882ab0a..428d30804a4c3 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
index 10829f8a73db1..c8a1c34aa3028 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index 071ee7f6c891f..44b94528f5e32 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a hard link.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
index 5d931891dd0eb..d291ca2cb5641 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
index 82b94a3d6bf5f..e4bc9c41728da 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
index 8401dbd38cd71..e0fa44ffbc4a5 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
index ac5499cdd6f20..2bcc64a3c2651 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_assignable<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
index 1f1843f7d401d..5efdf983f8a10 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_constructible<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
index 08d7ff84378d5..a3768b8cbd83f 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
index 1f2293989b7ef..9e8a70b4339d4 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // On Android L, ~scoped_test_env() is unable to delete the temp dir using
 // chmod+rm because chmod is too broken.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
index 56465d01021ad..62dc696d6797d 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
index fad880ecf30f7..708fb511dce73 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
index 12bc0c198c062..69e357adeafb5 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
index f1b559cf2d55b..0ed4aa0dc8d58 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
index f3161f2e020d2..579c03f2b8c0b 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
index fbf5e7129f7af..1d5312d65be46 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
index d0974640429df..7a3a56a0b161c 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
index 4a5882e36cde2..8b877ff63b112 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
index 382685c21dd99..995125d702de3 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
index 07b446d80c402..692fe773a0f9e 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
index c420fea21aa66..22c2c114ba687 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
index 56b4a27de6b42..8064feaaf2065 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
index b30d56685e2cb..540849ccefc05 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
index 425c224606038..02ff1e249d3bc 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
index 1d405a816e110..41474f9ca1e48 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
index 38e14438f3fd3..9a891eab6b343 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
index 88672851f7b6b..3e1d34ab5257a 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
index d0ff2c87689d1..4c299b876639e 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
index 98fa56b7f4b6d..7970887bac199 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
index 283a8fddb8e8f..13e6f74c5a143 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -17,6 +17,9 @@
 // fmemopen is available starting in Android M (API 23)
 // XFAIL: target={{.+}}-android{{(eabi)?(21|22)}}
 
+// OHOS fmemopen/FILE behavior does not currently match the expectation tested here.
+// XFAIL: LIBCXX-OHOS-FIXME
+
 // <print>
 
 // The FILE returned by fmemopen does not have file descriptor.
diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
index b24c0b90d86f1..2ddddbef70dbb 100644
--- a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
@@ -18,6 +18,7 @@
 // XFAIL: target=armv{{7|8}}{{l?}}{{.*}}-linux-gnueabihf && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-linux-gnu && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-amazon-linux && libcpp-abi-version=1
+// XFAIL: target={{.*}}-linux-ohos && libcpp-abi-version=1
 
 #include <iomanip>
 #include <ostream>
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
index da5563f87df45..ea5af90a90822 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
index 4f23b6b9a5d87..6dc0ff36ff4bf 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
@@ -14,6 +14,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
index 9bedd67e816ef..cbb9639766207 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
index ec4c8a009ba0c..59d93e74db3b7 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
index ae82035ccfc62..49a6ce0892ea4 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
index 6ae1972234b2b..58a6e18340ddf 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
index 08b74b999bbf4..88dcfe0485429 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
index 89e178248ae50..9c9745dcc9777 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wclog;
 
 // XFAIL: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
index 0e411f8adc0f2..e563152f2c8cd 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
index 026250daeee89..34119303a29ba 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete [] is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
index ca0f612bf7578..a4e32aaa11e13 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
index fbc9cf070bcba..3ec2b47b53eb0 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
index 8ae6bc2d3ba66..7cfd9f955fce7 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
@@ -8,6 +8,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
index 4978db116ef39..743075ede8ece 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
index 7be7e8d574e6c..b77a4409522f0 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
@@ -11,6 +11,7 @@
 // template <> class ctype<char>
 
 // ~ctype();
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <locale>
 #include <cassert>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
index 5cebc079c4703..c4803c04d173b 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <type_traits>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
index 2869378b42b92..5174ffebf281f 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
index ea03f042f2d45..665da7323e38a 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
index 2f7acb843e591..5d87c081b6e1c 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
index 447353de8af72..93d93aa9228f7 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
index 94ecc6c8fa85b..ab448a01632d6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
index 2b37737a26bc3..4be990e5b88c6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
index 016d8d736ad22..e0a3912a9315e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
index 959a4be9e1de9..b22629c82444e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
index 078b4a6fefb7e..c2cd904808794 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
index 9997b07134563..73b7a6decda36 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
index c86df7e6b53bf..deb30d747653f 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
index 478df7964f6d2..ee6fbd48b09b6 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
index 4b767fae871fa..c476537c3c7e3 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
index 25046a8417083..8174a724d4165 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
index 1c8710a008f27..e23cb4bf5f04a 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
index f0086ada30303..a36e3af8d12eb 100644
--- a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
+++ b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
@@ -12,6 +12,7 @@
 
 // This test runs in C++20, but we have deprecated codecvt<char(16|32), char, mbstate_t> in C++20.
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // <locale>
 
diff --git a/libcxx/test/std/re/re.traits/transform.pass.cpp b/libcxx/test/std/re/re.traits/transform.pass.cpp
index 80cd3f01faff2..112ecd47ff4e2 100644
--- a/libcxx/test/std/re/re.traits/transform.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform.pass.cpp
@@ -8,6 +8,7 @@
 //
 // NetBSD does not support LC_COLLATE at the moment
 // XFAIL: netbsd
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..cbc9387facb30 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -11,6 +11,7 @@
 
 // XFAIL: LIBCXX-AIX-FIXME
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
index 40daa49393bd4..110d0ea9fd694 100644
--- a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
+++ b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
@@ -15,6 +15,7 @@
 
 // There is no way to limit the number of threads on windows
 // UNSUPPORTED: windows
+// UNSUPPORTED: executor-runs-as-root
 
 // macOS and FreeBSD seem to limit the number of processes, not threads via RLIMIT_NPROC.
 // XFAIL: target={{.+}}-apple-{{.*}}
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
index c6eb0694ab2e1..19573ae52fd71 100644
--- a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
@@ -9,6 +9,7 @@
 // Windows cannot detect the deadlock. Instead of throwing system_error,
 // it would dead lock the test
 // UNSUPPORTED: windows
+// UNSUPPORTED: pthread-join-self-deadlocks
 
 // TSAN bug: https://llvm.org/PR66537
 // UNSUPPORTED: tsan
diff --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
index 51be78e1a02f8..804036e425572 100644
--- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
@@ -14,6 +14,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..6901aff00bc4e 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -415,6 +415,13 @@ void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteCalled(p);
+  std::free(p);
+}
+#  endif
+
 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->newArrayCalled(s);
@@ -449,6 +456,13 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteArrayCalled(p);
+  std::free(p);
+}
+#  endif
+
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
 #      define USE_ALIGNED_ALLOC
@@ -519,6 +533,13 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
@@ -551,6 +572,13 @@ void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
 
 #endif // DISABLE_NEW_COUNT
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index 2ad9efb32c60f..f820a4a0fa915 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -182,7 +182,7 @@ struct scoped_test_env
         std::string cmd = "chmod -R 777 " + test_root.string();
 #endif // defined(__MVS__)
         int ret = std::system(cmd.c_str());
-#  if !defined(_AIX) && !defined(__ANDROID__)
+#  if !defined(_AIX) && !defined(__ANDROID__) && !defined(__OHOS__)
         // On AIX the chmod command will return non-zero when trying to set
         // the permissions on a directory that contains a bad symlink. This triggers
         // the assert, despite being able to delete everything with the following
@@ -196,6 +196,8 @@ struct scoped_test_env
         // LIBCXX-ANDROID-FIXME: Other fixes to consider: place a toybox chmod
         // onto old devices, re-enable this assert for devices running Android N
         // and up, rewrite this chmod+rm in C or C++.
+        //
+        // OHOS has the same chmod -R behavior for bad symlinks.
         assert(ret == 0);
 #  endif
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..fdf6bc5b18d72 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -375,7 +375,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NOT_WIN32(...) __VA_ARGS__
 #endif
 
-#if defined(TEST_WINDOWS_DLL) ||defined(__MVS__) || defined(_AIX)
+#if defined(TEST_WINDOWS_DLL) || defined(__MVS__) || defined(_AIX) || defined(TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS)
 // Macros for waiving cases when we can't count allocations done within
 // the library implementation.
 //
@@ -386,6 +386,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 //
 // The same goes on IBM zOS.
 // The same goes on AIX.
+// Some target test configurations can define
+// TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS for the same reason.
 #define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
 #define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
 #else
diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp
index 36536860c1a96..b28f70a6e8e7a 100644
--- a/libunwind/test/aarch64_za_unwind.pass.cpp
+++ b/libunwind/test/aarch64_za_unwind.pass.cpp
@@ -20,6 +20,9 @@
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
 #endif
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
 
 // Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA
 // (and commits a lazy save of ZA) before resuming from unwinding.
@@ -33,18 +36,16 @@ static bool checkHasSME() {
     return false;
   return has_sme != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = getauxval(AT_HWCAP2);
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = 0;
   elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
 #else
 static bool checkHasSME() {

>From bbaa18370c45aaf79c2446be0a6e94dc2aa0ef00 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Fri, 3 Jul 2026 10:33:38 +0800
Subject: [PATCH 4/6] Add qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  16 +-
 libcxx/utils/ci/run-buildbot                  |  41 ++++-
 libcxx/utils/ci/vendor/ohos/qemu_run.py       | 144 ++++++++++++++++++
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |   8 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |   8 +-
 5 files changed, 204 insertions(+), 13 deletions(-)
 create mode 100755 libcxx/utils/ci/vendor/ohos/qemu_run.py

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 542bc0cc83a1f..20aa7a49d5802 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++ using HDC on OHOS.
+# libc++ using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -23,15 +23,21 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
 config.available_features.add('LIBCXX-OHOS-FIXME')
-config.available_features.add('ohos-hdc')
-config.available_features.add('executor-has-no-stdin')
-config.available_features.add('executor-merges-stderr-into-stdout')
-config.available_features.add('executor-runs-as-root')
+config.available_features.add(executor_kind)
 config.available_features.add('platform-has-nonportable-locales')
 config.available_features.add('platform-has-unreliable-new-delete-interposition')
 config.available_features.add('pthread-join-self-deadlocks')
 
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
     libcxx.test.features.DEFAULT_FEATURES,
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index ecf0ca17ae148..40739b0fba4d8 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -28,6 +28,16 @@ ${PROGNAME} [options] <BUILDER>
 --build-dir <DIR>   The directory to use for building the library. By default,
                     this is '<llvm-root>/build/<builder>'.
 
+--ohos-qemu <PATH>  Run OHOS tests under qemu-aarch64 instead of HDC. This also
+                    accepts OHOS_QEMU or QEMU from the environment.
+
+--ohos-qemu-sysroot <DIR>
+                    Sysroot passed to qemu-aarch64 with -L. Defaults to
+                    OHOS_QEMU_SYSROOT, QEMU_SYSROOT, then OHOS_SYSROOT.
+
+--ohos-hdc <PATH>   HDC binary to use when OHOS tests run on a device. This sets
+                    HDC for the OHOS HDC executor.
+
 Environment variables
 CC                  The C compiler to use, this value is used by CMake. This
                     variable is mandatory.
@@ -84,6 +94,19 @@ while [[ $# -gt 0 ]]; do
             BUILD_DIR="${2}"
             shift; shift
             ;;
+        --ohos-qemu)
+            OHOS_QEMU="${2}"
+            shift; shift
+            ;;
+        --ohos-qemu-sysroot)
+            OHOS_QEMU_SYSROOT="${2}"
+            shift; shift
+            ;;
+        --ohos-hdc)
+            HDC="${2}"
+            export HDC
+            shift; shift
+            ;;
         *)
             BUILDER="${1}"
             shift
@@ -193,8 +216,18 @@ function setup-ohos-hdc-env() {
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
     OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
-    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
-    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+    OHOS_QEMU="${OHOS_QEMU:-${QEMU:-}}"
+
+    if [[ -n "${OHOS_QEMU}" ]]; then
+        OHOS_QEMU_SYSROOT="${OHOS_QEMU_SYSROOT:-${QEMU_SYSROOT:-${OHOS_SYSROOT}}}"
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/qemu_run.py --qemu ${OHOS_QEMU} --sysroot ${OHOS_QEMU_SYSROOT}"
+        OHOS_EXECUTOR_KIND="ohos-qemu"
+    else
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+        OHOS_EXECUTOR_KIND="ohos-hdc"
+    fi
+
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE};executor_kind=${OHOS_EXECUTOR_KIND}"
 }
 
 function generate-cmake-ohos-hdc() {
@@ -863,14 +896,14 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
-ohos-hdc-aarch64-full)
+ohos-aarch64-full|ohos-hdc-aarch64-full)
     clean
 
     generate-cmake-ohos-hdc
 
     check-runtimes
 ;;
-ohos-hdc-aarch64-smoke)
+ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
     clean
 
     generate-cmake-ohos-hdc
diff --git a/libcxx/utils/ci/vendor/ohos/qemu_run.py b/libcxx/utils/ci/vendor/ohos/qemu_run.py
new file mode 100755
index 0000000000000..ef7eef7872864
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/qemu_run.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _resolve_binary(binary):
+    if os.path.isabs(binary):
+        if os.path.isfile(binary) and os.access(binary, os.X_OK):
+            return binary
+        sys.exit(f"failed to find executable: {binary}")
+
+    resolved = shutil.which(binary)
+    if resolved is None:
+        sys.exit(f"failed to find executable: {binary}")
+    return resolved
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(str(parent))
+    return library_dirs
+
+
+def _build_target_env_args(env, prepend_env):
+    target_env = dict(env)
+
+    for key, value in prepend_env.items():
+        existing = target_env.get(key, os.environ.get(key, ""))
+        target_env[key] = f"{value}{os.pathsep}{existing}" if existing else value
+
+    args = []
+    for key, value in target_env.items():
+        args.extend(["-E", f"{key}={value}"])
+    return args
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--qemu",
+        default=os.environ.get("OHOS_QEMU", os.environ.get("QEMU", "qemu-aarch64")),
+    )
+    parser.add_argument("--sysroot", required=True)
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(
+            os.environ.get(
+                "OHOS_QEMU_TIMEOUT_SECONDS",
+                os.environ.get("QEMU_TIMEOUT_SECONDS", "600"),
+            )
+        ),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    qemu = _resolve_binary(args.qemu)
+    execdir = Path(args.execdir).resolve()
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    build_library_dirs = _collect_build_library_dirs(execdir)
+    if build_library_dirs:
+        existing = prepend_env.get("LD_LIBRARY_PATH")
+        build_library_path = os.pathsep.join(build_library_dirs)
+        prepend_env["LD_LIBRARY_PATH"] = (
+            f"{build_library_path}{os.pathsep}{existing}"
+            if existing
+            else build_library_path
+        )
+
+    cmd = [
+        qemu,
+        "-L",
+        args.sysroot,
+        *_build_target_env_args(env, prepend_env),
+        *args.command,
+    ]
+    try:
+        completed = subprocess.run(
+            cmd,
+            cwd=execdir,
+            timeout=args.timeout_seconds,
+            check=False,
+        )
+    except subprocess.TimeoutExpired:
+        print(
+            f"qemu executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+            file=sys.stderr,
+        )
+        return 124
+    return completed.returncode
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index d18748fe3a3cc..8f5a17b3dec64 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++abi using HDC on OHOS.
+# libc++abi using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -20,7 +20,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index bce241a7898cf..f2a032c41a123 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -1,6 +1,6 @@
 #
 # Configuration file for running the libunwind tests against the shared library
-# using HDC on OHOS.
+# using HDC or QEMU on OHOS.
 #
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
@@ -45,7 +45,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,

>From dde4d430033711fb7ffa87621ba86b37492f480b Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 14:52:40 +0800
Subject: [PATCH 5/6] Fix something for qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in                   | 4 +++-
 .../file.streams/fstreams/filebuf.members/close.pass.cpp      | 3 +++
 .../fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp     | 2 ++
 .../syncstream/osyncstream/thread/several_threads.pass.cpp    | 2 ++
 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in             | 4 +++-
 libcxxabi/test/guard_threaded_test.pass.cpp                   | 2 ++
 libunwind/test/bad_unwind_info.pass.cpp                       | 2 ++
 libunwind/test/configs/llvm-libunwind-ohos.cfg.in             | 4 +++-
 libunwind/test/signal_unwind.pass.cpp                         | 2 ++
 9 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 20aa7a49d5802..3be1c267ede2a 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -23,7 +23,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
index 2c4cfb0ac7661..ce802d6ceba78 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14
 // REQUIRES: linux
 // UNSUPPORTED: no-filesystem
+// OHOS qemu-user observes the host /proc, not a real OHOS process /proc.
+// UNSUPPORTED: ohos-qemu
 // XFAIL: no-localization
 
 // <filesystem>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..77db6396755bf 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -10,6 +10,8 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
+// This thread-heavy stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 // <syncstream>
 
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index 8f5a17b3dec64..d0a8d1248f88a 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -20,7 +20,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxxabi/test/guard_threaded_test.pass.cpp b/libcxxabi/test/guard_threaded_test.pass.cpp
index d898e60288aca..a4e14f0aca9e4 100644
--- a/libcxxabi/test/guard_threaded_test.pass.cpp
+++ b/libcxxabi/test/guard_threaded_test.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: no-exceptions
+// This thread-heavy guard stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 #define TESTING_CXA_GUARD
 #include "../src/cxa_guard_impl.h"
diff --git a/libunwind/test/bad_unwind_info.pass.cpp b/libunwind/test/bad_unwind_info.pass.cpp
index 332b661d2e98f..76102505f2d67 100644
--- a/libunwind/test/bad_unwind_info.pass.cpp
+++ b/libunwind/test/bad_unwind_info.pass.cpp
@@ -13,6 +13,8 @@
 // REQUIRES: target={{(aarch64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
 // UNSUPPORTED: target={{.*-apple.*}}
+// OHOS qemu-user does not reliably model this invalid unwind-info path.
+// UNSUPPORTED: ohos-qemu
 
 // GCC doesn't support __attribute__((naked)) on AArch64.
 // UNSUPPORTED: gcc
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index f2a032c41a123..71d1e711e36d5 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -45,7 +45,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp
index f8798bc3eabeb..ef73a3ea8c59a 100644
--- a/libunwind/test/signal_unwind.pass.cpp
+++ b/libunwind/test/signal_unwind.pass.cpp
@@ -10,6 +10,8 @@
 // Ensure that the unwinder can cope with the signal handler.
 // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
+// OHOS qemu-user does not provide device-equivalent signal unwinding behavior.
+// UNSUPPORTED: ohos-qemu
 
 // TODO: Figure out why this fails with Memory Sanitizer.
 // XFAIL: msan

>From c81df7252fb00b5b9b41f0a29393e775abc9c810 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 15:19:29 +0800
Subject: [PATCH 6/6] delete run-smoke-test

---
 libcxx/utils/ci/buildkite-pipeline.yml | 12 ----------
 libcxx/utils/ci/run-buildbot           | 31 --------------------------
 2 files changed, 43 deletions(-)

diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 77617f43ae6dc..70bc69637a7f2 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -175,18 +175,6 @@ steps:
 
 - group: OHOS
   steps:
-  - label: OHOS AArch64 HDC Smoke
-    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
-    env:
-      CC: clang
-      CXX: clang++
-      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
-      HDC_TIMEOUT_SECONDS: 240
-    agents:
-      queue: libcxx-builders
-      os: linux
-      device: ohos-hdc
-    <<: *common
   - label: OHOS AArch64 HDC Full
     command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
     env:
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 40739b0fba4d8..e7d751df484e4 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -147,23 +147,6 @@ function require-env-var() {
     fi
 }
 
-function detect-lit() {
-    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
-        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
-    elif command -v llvm-lit >/dev/null 2>&1; then
-        LIT_RUNNER=("$(command -v llvm-lit)")
-    else
-        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
-    fi
-}
-
-function run-smoke-test() {
-    local test_dir="${1}"
-    local filter="${2}"
-    step "Running smoke test ${filter}"
-    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
-}
-
 function ohos-lit-args() {
     local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
     if python3 -c 'import psutil' >/dev/null 2>&1; then
@@ -903,20 +886,6 @@ ohos-aarch64-full|ohos-hdc-aarch64-full)
 
     check-runtimes
 ;;
-ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
-    clean
-
-    generate-cmake-ohos-hdc
-
-    step "Building the OHOS smoke test dependencies"
-    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
-
-    detect-lit
-    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
-;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #


From libcxx-commits at lists.llvm.org  Thu Jul  9 01:25:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 01:25:26 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a4f5af6.cb30c3e4.3ce559.83ec@mx.google.com>


https://github.com/huihui15537226823 updated https://github.com/llvm/llvm-project/pull/208390

>From 70e8ac839667f8aba4aa74a2dd19614426f9754f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 24 Jun 2026 09:47:00 +0800
Subject: [PATCH 1/6] Add OHOS HDC runtimes CI support

---
 cmake/Modules/GetToolchainDirs.cmake          |  37 +-
 .../cmake/Modules/CompilerRTUtils.cmake       |   8 +-
 compiler-rt/cmake/builtin-config-ix.cmake     |   2 +-
 compiler-rt/cmake/crt-config-ix.cmake         |   2 +-
 compiler-rt/lib/builtins/crtbegin.c           |   9 +
 compiler-rt/lib/builtins/emutls.c             |   8 +-
 libcxx/CMakeLists.txt                         |  10 +-
 libcxx/cmake/config-ix.cmake                  |   2 +
 libcxx/include/__config                       |   5 +-
 libcxx/include/__cxx03/__config               |   5 +-
 .../__cxx03/__locale_dir/locale_base_api.h    |   2 +-
 libcxx/include/__locale_dir/locale_base_api.h |   2 +-
 libcxx/src/include/config_elast.h             |   2 +
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  48 ++
 libcxx/utils/ci/buildkite-pipeline.yml        |  27 +
 libcxx/utils/ci/run-buildbot                  |  87 ++++
 libcxx/utils/ci/vendor/ohos/hdc_run.py        | 472 ++++++++++++++++++
 libcxx/utils/ci/vendor/ohos/hdc_run_test.py   | 115 +++++
 libcxxabi/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |  30 ++
 libunwind/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |  55 ++
 22 files changed, 910 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/test/configs/llvm-libc++-ohos.cfg.in
 create mode 100755 libcxx/utils/ci/vendor/ohos/hdc_run.py
 create mode 100644 libcxx/utils/ci/vendor/ohos/hdc_run_test.py
 create mode 100644 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
 create mode 100644 libunwind/test/configs/llvm-libunwind-ohos.cfg.in

diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..a20ed9ce9c6a9
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,48 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index d4107cf11ad51..77617f43ae6dc 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -172,3 +172,30 @@ steps:
       queue: libcxx-builders
       os: android
     <<: *common
+
+- group: OHOS
+  steps:
+  - label: OHOS AArch64 HDC Smoke
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
+  - label: OHOS AArch64 HDC Full
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 6376bc1d3dd5a..1867e9c620649 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -116,6 +116,41 @@ function clean() {
     rm -rf "${BUILD_DIR}"
 }
 
+function require-env-var() {
+    local name="${1}"
+    if [[ -z ${!name+x} ]] || [[ -z "${!name}" ]]; then
+        error "Environment variable ${name} must be defined"
+        exit 1
+    fi
+}
+
+function detect-lit() {
+    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
+        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
+    elif command -v llvm-lit >/dev/null 2>&1; then
+        LIT_RUNNER=("$(command -v llvm-lit)")
+    else
+        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
+    fi
+}
+
+function run-smoke-test() {
+    local test_dir="${1}"
+    local filter="${2}"
+    step "Running smoke test ${filter}"
+    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
+}
+
+function ohos-lit-args() {
+    local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
+    if python3 -c 'import psutil' >/dev/null 2>&1; then
+        args="${args} --timeout=1500"
+    else
+        echo "warning: python3 psutil not available; running OHOS lit without per-test timeout" >&2
+    fi
+    printf '%s' "${args}"
+}
+
 function generate-cmake-base() {
     step "Generating CMake"
 
@@ -153,6 +188,37 @@ function generate-cmake-android() {
           "${@}"
 }
 
+function setup-ohos-hdc-env() {
+    require-env-var OHOS_SYSROOT
+
+    OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+}
+
+function generate-cmake-ohos-hdc() {
+    setup-ohos-hdc-env
+
+    local lit_args
+    lit_args="$(ohos-lit-args)"
+
+    generate-cmake \
+          -DLLVM_LIT_ARGS="${lit_args}" \
+          -DCMAKE_SYSTEM_NAME=OHOS \
+          -DCMAKE_SYSROOT="${OHOS_SYSROOT}" \
+          -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
+          -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
+          -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
+          -DLIBUNWIND_TEST_CONFIG="llvm-libunwind-ohos.cfg.in" \
+          -DLIBCXX_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBCXXABI_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBUNWIND_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          "${@}"
+}
+
 function check-runtimes() {
     step "Building libc++ test dependencies"
     ninja -vC "${BUILD_DIR}" cxx-test-depends
@@ -795,6 +861,27 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
+ohos-hdc-aarch64-full)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    check-runtimes
+;;
+ohos-hdc-aarch64-smoke)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    step "Building the OHOS smoke test dependencies"
+    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
+
+    detect-lit
+    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
+;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run.py b/libcxx/utils/ci/vendor/ohos/hdc_run.py
new file mode 100755
index 0000000000000..61a3899b88b11
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run.py
@@ -0,0 +1,472 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import uuid
+
+
+EXIT_MARKER = "__llvm_hdc_exit__="
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _split_path_list(value):
+    return [entry for entry in value.split(os.pathsep) if entry]
+
+
+def _resolve_hdc_binary(hdc):
+    if os.path.isabs(hdc):
+        return hdc
+    resolved = shutil.which(hdc)
+    if resolved is None:
+        sys.exit(f"failed to find hdc binary: {hdc}")
+    return resolved
+
+
+def _is_windows_hdc(path):
+    return path.lower().endswith(".exe")
+
+
+def _find_windows_local_root(hdc_path):
+    parts = Path(hdc_path).parts
+    for index in range(len(parts) - 1):
+        if parts[index] == "AppData" and index + 1 < len(parts) and parts[index + 1] == "Local":
+            return Path(*parts[: index + 2])
+    return None
+
+
+def _default_host_staging_root(hdc_path):
+    if _is_windows_hdc(hdc_path):
+        local_root = _find_windows_local_root(hdc_path)
+        if local_root is None:
+            sys.exit(
+                "failed to derive a Windows staging directory from the hdc.exe path; "
+                "set HDC_HOST_STAGING_ROOT to a path under /mnt/<drive>/..."
+            )
+        return local_root / "Temp" / "llvm-lit-hdc"
+    return Path("/tmp/llvm-lit-hdc")
+
+
+def _to_windows_path(path):
+    resolved = Path(path).resolve()
+    parts = resolved.parts
+    if len(parts) < 4 or parts[1] != "mnt":
+        sys.exit(
+            f"Windows hdc requires a staging path under /mnt/<drive>/..., got: {resolved}"
+        )
+    drive = parts[2].upper()
+    tail = "\\".join(parts[3:])
+    return f"{drive}:\\{tail}" if tail else f"{drive}:\\"
+
+
+def _run_readelf(path):
+    completed = subprocess.run(
+        ["readelf", "-d", path],
+        check=False,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        text=True,
+    )
+    if completed.returncode != 0:
+        return ""
+    return completed.stdout
+
+
+def _read_elf_metadata(path):
+    needed = []
+    runpath_dirs = []
+    seen_dirs = set()
+    for line in _run_readelf(path).splitlines():
+        needed_match = re.search(r"\(NEEDED\).*Shared library: \[(.*)\]", line)
+        if needed_match:
+            needed.append(needed_match.group(1))
+            continue
+
+        runpath_match = re.search(
+            r"\((?:RUNPATH|RPATH)\).*Library runpath: \[(.*)\]", line
+        )
+        if not runpath_match:
+            continue
+        for entry in _split_path_list(runpath_match.group(1)):
+            candidate = Path(entry)
+            if not candidate.is_dir():
+                continue
+            key = str(candidate.resolve())
+            if key in seen_dirs:
+                continue
+            seen_dirs.add(key)
+            runpath_dirs.append(candidate)
+    return needed, runpath_dirs
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(parent)
+    return library_dirs
+
+
+def _find_dependency(name, search_dirs):
+    for directory in search_dirs:
+        candidate = Path(directory) / name
+        if candidate.is_file():
+            return candidate.resolve()
+    return None
+
+
+def _copy_needed_libraries(executables, execdir, staging_execdir):
+    search_dirs = _collect_build_library_dirs(execdir)
+    queue = [Path(exe).resolve() for exe in executables]
+    visited = set()
+    copied_names = {path.name for path in staging_execdir.glob("*") if path.is_file()}
+
+    while queue:
+        current = queue.pop(0)
+        current_key = str(current)
+        if current_key in visited:
+            continue
+        visited.add(current_key)
+
+        needed, runpath_dirs = _read_elf_metadata(current)
+        current_search_dirs = []
+        current_seen = set()
+        for directory in [*runpath_dirs, current.parent, *search_dirs]:
+            resolved = Path(directory).resolve()
+            key = str(resolved)
+            if key in current_seen:
+                continue
+            current_seen.add(key)
+            current_search_dirs.append(resolved)
+
+        for directory in runpath_dirs:
+            resolved = directory.resolve()
+            if all(str(resolved) != str(existing) for existing in search_dirs):
+                search_dirs.append(resolved)
+
+        for lib_name in needed:
+            dependency = _find_dependency(lib_name, current_search_dirs)
+            if dependency is None:
+                continue
+            if lib_name not in copied_names:
+                shutil.copy2(dependency, staging_execdir / lib_name)
+                copied_names.add(lib_name)
+            queue.append(dependency)
+
+
+def _copy_execdir(execdir, staging_execdir):
+    if staging_execdir.exists():
+        shutil.rmtree(staging_execdir)
+    shutil.copytree(execdir, staging_execdir)
+
+
+def _iter_files(root):
+    for directory, dirnames, filenames in os.walk(root):
+        dirnames.sort()
+        filenames.sort()
+        yield Path(directory), filenames
+
+
+def _is_test_executable(path):
+    return path.endswith(".tmp.exe") and os.path.exists(path)
+
+
+def _map_local_path(path, local_execdir, remote_execdir):
+    normalized = os.path.normpath(path)
+    execdir = os.path.normpath(local_execdir)
+    if normalized == execdir:
+        return remote_execdir
+    prefix = execdir + os.sep
+    if normalized.startswith(prefix):
+        suffix = normalized[len(prefix) :].replace(os.sep, "/")
+        return f"{remote_execdir}/{suffix}" if suffix else remote_execdir
+    return path
+
+
+def _map_value(value, local_execdir, remote_execdir):
+    return _map_local_path(value, local_execdir, remote_execdir)
+
+
+def _build_env_exports(env, prepend_env, local_execdir, remote_execdir, add_remote_libdir):
+    commands = []
+    mapped_env = {
+        key: _map_value(value, local_execdir, remote_execdir) for key, value in env.items()
+    }
+    mapped_prepend = {
+        key: _map_value(value, local_execdir, remote_execdir)
+        for key, value in prepend_env.items()
+    }
+
+    if add_remote_libdir:
+        existing = mapped_prepend.get("LD_LIBRARY_PATH")
+        mapped_prepend["LD_LIBRARY_PATH"] = (
+            f"{remote_execdir}:{existing}" if existing else remote_execdir
+        )
+
+    for key, value in mapped_env.items():
+        commands.append(f"export {key}={shlex.quote(value)}")
+    for key, value in mapped_prepend.items():
+        commands.append(f"export {key}={shlex.quote(value)}${{{key}:+:{key}}}")
+    return commands
+
+
+def _hdc_prefix(hdc_path, server, target):
+    prefix = [hdc_path]
+    if server:
+        prefix.extend(["-s", server])
+    if target:
+        prefix.extend(["-t", target])
+    return prefix
+
+
+def _run_hdc(
+    prefix,
+    args,
+    *,
+    timeout=None,
+    capture_output=False,
+    stdin=subprocess.DEVNULL,
+    check=True,
+    expect_stdout=None,
+):
+    kwargs = {
+        "check": False,
+        "timeout": timeout,
+        "stdin": stdin,
+        "text": True,
+    }
+    if capture_output:
+        kwargs["stdout"] = subprocess.PIPE
+        kwargs["stderr"] = subprocess.PIPE
+    completed = subprocess.run(prefix + args, **kwargs)
+    output = completed.stdout if capture_output and completed.stdout else ""
+    error_output = completed.stderr if capture_output and completed.stderr else ""
+    if check and completed.returncode != 0:
+        raise RuntimeError(
+            f"hdc command failed with exit code {completed.returncode}: "
+            f"{shlex.join(prefix + args)}\n{output}{error_output}"
+        )
+    if check and expect_stdout is not None and expect_stdout not in output:
+        raise RuntimeError(
+            f"hdc command did not report success: {shlex.join(prefix + args)}\n{output}"
+        )
+    return completed
+
+
+def _run_checked_shell(prefix, command, *, timeout=None):
+    completed = _run_hdc(
+        prefix,
+        [
+            "shell",
+            f"{command}; rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"",
+        ],
+        timeout=timeout,
+        capture_output=True,
+    )
+    exit_code, output = _extract_exit_code(completed.stdout)
+    if exit_code is None:
+        raise RuntimeError(
+            f"failed to parse shell exit code for: {command}\n{completed.stdout}"
+        )
+    if exit_code != 0:
+        raise RuntimeError(
+            f"remote shell command failed with exit code {exit_code}: {command}\n{output}"
+        )
+    return output
+
+
+def _sync_tree_to_device(prefix, local_root, remote_root, use_windows_paths):
+    _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_root)}")
+    _run_checked_shell(prefix, f"mkdir -p {shlex.quote(remote_root)}")
+
+    for directory, filenames in _iter_files(local_root):
+        relative_dir = os.path.relpath(directory, local_root)
+        device_dir = (
+            remote_root
+            if relative_dir == "."
+            else f"{remote_root}/{relative_dir.replace(os.sep, '/')}"
+        )
+        _run_checked_shell(prefix, f"mkdir -p {shlex.quote(device_dir)}")
+        for name in filenames:
+            local_path = directory / name
+            device_path = f"{device_dir}/{name}"
+            source = _to_windows_path(local_path) if use_windows_paths else str(local_path)
+            _run_hdc(
+                prefix,
+                ["file", "send", source, device_path],
+                capture_output=True,
+                expect_stdout="FileTransfer finish",
+            )
+            if name.endswith(".tmp.exe") or os.access(local_path, os.X_OK):
+                _run_checked_shell(prefix, f"chmod +x {shlex.quote(device_path)}")
+
+
+def _build_remote_command(command, local_execdir, remote_execdir, exports):
+    mapped_command = [
+        _map_local_path(arg, local_execdir, remote_execdir) for arg in command
+    ]
+    pieces = [f"cd {shlex.quote(remote_execdir)}"]
+    pieces.extend(exports)
+    pieces.append(shlex.join(mapped_command))
+    pieces.append(f"rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"")
+    return " && ".join(pieces[:-2]) + f"; {pieces[-2]}; {pieces[-1]}"
+
+
+def _extract_exit_code(output):
+    match = re.search(rf"^(.*)\n{EXIT_MARKER}(\d+)\s*$", output, re.DOTALL)
+    if not match:
+        return None, output
+    return int(match.group(2)), match.group(1)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--hdc", default=os.environ.get("HDC", "hdc"))
+    parser.add_argument("--hdc-server", default=os.environ.get("HDC_SERVER_IP_PORT"))
+    parser.add_argument("--hdc-target", default=os.environ.get("HDC_UTID"))
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--remote-base-dir",
+        default="/data/local/tmp/llvm-lit-hdc",
+    )
+    parser.add_argument(
+        "--host-staging-root",
+        default=os.environ.get("HDC_HOST_STAGING_ROOT"),
+    )
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(os.environ.get("HDC_TIMEOUT_SECONDS", "120")),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("--keep-remote-dir", action="store_true")
+    parser.add_argument("--keep-host-staging", action="store_true")
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    hdc_path = _resolve_hdc_binary(args.hdc)
+    use_windows_paths = _is_windows_hdc(hdc_path)
+    host_staging_root = (
+        Path(args.host_staging_root)
+        if args.host_staging_root
+        else _default_host_staging_root(hdc_path)
+    )
+    prefix = _hdc_prefix(hdc_path, args.hdc_server, args.hdc_target)
+
+    execdir = Path(args.execdir).resolve()
+    run_id = uuid.uuid4().hex[:12]
+    staging_execdir = host_staging_root / f"run-{run_id}" / execdir.name
+    remote_execdir = f"{args.remote_base_dir}/run-{run_id}/{execdir.name}"
+
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    executables = [arg for arg in args.command if _is_test_executable(arg)]
+    host_staging_root.mkdir(parents=True, exist_ok=True)
+    _copy_execdir(execdir, staging_execdir)
+    _copy_needed_libraries(executables, execdir, staging_execdir)
+
+    add_remote_libdir = any(staging_execdir.glob("*.so*"))
+    exports = _build_env_exports(
+        env,
+        prepend_env,
+        str(execdir),
+        remote_execdir,
+        add_remote_libdir,
+    )
+
+    try:
+        _sync_tree_to_device(prefix, staging_execdir, remote_execdir, use_windows_paths)
+        remote_command = _build_remote_command(
+            args.command,
+            str(execdir),
+            remote_execdir,
+            exports,
+        )
+        try:
+            completed = _run_hdc(
+                prefix,
+                ["shell", remote_command],
+                timeout=args.timeout_seconds,
+                capture_output=True,
+                stdin=None,
+            )
+        except subprocess.TimeoutExpired:
+            print(
+                f"hdc executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+                file=sys.stderr,
+            )
+            return 124
+
+        exit_code, output = _extract_exit_code(completed.stdout)
+        if exit_code is None:
+            print(
+                "failed to parse exit code from hdc shell output",
+                file=sys.stderr,
+            )
+            if completed.stdout:
+                print(completed.stdout, end="", file=sys.stderr)
+            if completed.stderr:
+                print(completed.stderr, end="", file=sys.stderr)
+            return 1
+
+        if output:
+            sys.stdout.write(output)
+        if completed.stderr:
+            sys.stderr.write(completed.stderr)
+        return exit_code
+    finally:
+        if not args.keep_remote_dir:
+            try:
+                _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_execdir)}")
+            except Exception:
+                pass
+        if not args.keep_host_staging:
+            shutil.rmtree(staging_execdir.parent, ignore_errors=True)
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run_test.py b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
new file mode 100644
index 0000000000000..17e2f65bd1fb6
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import os
+from pathlib import Path
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import unittest
+
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+HDC_RUN = SCRIPT_DIR / "hdc_run.py"
+
+
+class HdcRunTest(unittest.TestCase):
+    def make_fake_hdc(self, directory):
+        fake_hdc = directory / "hdc"
+        fake_hdc.write_text(
+            textwrap.dedent(
+                """\
+                #!/usr/bin/env python3
+                import sys
+
+                EXIT_MARKER = "__llvm_hdc_exit__="
+
+                def main():
+                    args = sys.argv[1:]
+                    if args[:2] == ["file", "send"]:
+                        print("FileTransfer finish")
+                        return 0
+
+                    if args and args[0] == "shell":
+                        command = args[1]
+                        if "stderr.tmp.exe" in command:
+                            sys.stdout.write("STDOUT-OUTPUT")
+                            sys.stderr.write("STDERR-OUTPUT")
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        if "stdin.tmp.exe" in command:
+                            sys.stdout.write("IN:" + sys.stdin.read())
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                        return 0
+
+                    print("unexpected fake hdc invocation: " + repr(args), file=sys.stderr)
+                    return 2
+
+                if __name__ == "__main__":
+                    raise SystemExit(main())
+                """
+            )
+        )
+        fake_hdc.chmod(fake_hdc.stat().st_mode | stat.S_IXUSR)
+        return fake_hdc
+
+    def run_hdc_run(self, temp_dir, executable_name, *, stdin_text=None):
+        execdir = temp_dir / "execdir"
+        execdir.mkdir()
+        executable = execdir / executable_name
+        executable.write_text("# fake executable marker\n")
+        executable.chmod(executable.stat().st_mode | stat.S_IXUSR)
+
+        fake_hdc = self.make_fake_hdc(temp_dir)
+        return subprocess.run(
+            [
+                sys.executable,
+                str(HDC_RUN),
+                "--hdc",
+                str(fake_hdc),
+                "--execdir",
+                str(execdir),
+                "--host-staging-root",
+                str(temp_dir / "staging"),
+                str(executable),
+            ],
+            input=stdin_text,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+    def test_preserves_remote_stdout_and_stderr_separately(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(Path(temp), "stderr.tmp.exe")
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "STDOUT-OUTPUT")
+        self.assertEqual(completed.stderr, "STDERR-OUTPUT")
+
+    def test_forwards_stdin_to_remote_command(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(
+                Path(temp),
+                "stdin.tmp.exe",
+                stdin_text="hello from stdin",
+            )
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "IN:hello from stdin")
+        self.assertEqual(completed.stderr, "")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt
index e1a1587fb6283..cb22e6fe65108 100644
--- a/libcxxabi/CMakeLists.txt
+++ b/libcxxabi/CMakeLists.txt
@@ -187,7 +187,12 @@ set(LIBCXXABI_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXXABI_TARGET_SUBDIR)
+  else()
+    set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXXABI_LIBDIR_SUBDIR)
     string(APPEND LIBCXXABI_TARGET_SUBDIR /${LIBCXXABI_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
new file mode 100644
index 0000000000000..d18748fe3a3cc
--- /dev/null
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -0,0 +1,30 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++abi using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -I %{libcxx}/test/support -I %{libcxx}/src -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    '-nostdlib++ -L %{lib} -lc++ -lc++abi -pthread'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 02132f6c07fcd..4fd11bf5aca2f 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -142,7 +142,12 @@ set(LIBUNWIND_STATIC_OUTPUT_NAME "unwind" CACHE STRING "Output name for the stat
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBUNWIND_TARGET_SUBDIR)
+  else()
+    set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBUNWIND_LIBDIR_SUBDIR)
     string(APPEND LIBUNWIND_TARGET_SUBDIR /${LIBUNWIND_LIBDIR_SUBDIR})
   endif()
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
new file mode 100644
index 0000000000000..bce241a7898cf
--- /dev/null
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -0,0 +1,55 @@
+#
+# Configuration file for running the libunwind tests against the shared library
+# using HDC on OHOS.
+#
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+compile_flags = []
+link_flags = []
+
+if @HAVE_GETAUXVAL@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_GETAUXVAL')
+
+if @HAVE_ELF_AUX_INFO@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_ELF_AUX_INFO')
+
+if @LIBUNWIND_ENABLE_CET@:
+    compile_flags.append('-fcf-protection=full')
+
+if @LIBUNWIND_ENABLE_GCS@:
+    compile_flags.append('-mbranch-protection=standard')
+
+if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
+    link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
+
+if '@CMAKE_DL_LIBS@':
+    link_flags.append('-l at CMAKE_DL_LIBS@')
+
+compile_flags.append('-funwind-tables')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags))
+))
+config.substitutions.append(('%{link_flags}',
+    '-L %{{lib}} -lunwind {}'.format(' '.join(link_flags))
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)

>From e9e5d2e517fb072f3554798e49b7119ec739105f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 25 Jun 2026 11:30:38 +0800
Subject: [PATCH 2/6] Fix OHOS libc++ CI ABI and SME detection

---
 libcxx/utils/ci/run-buildbot |  2 ++
 libunwind/src/Registers.hpp  | 15 +++++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 1867e9c620649..ecf0ca17ae148 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -192,6 +192,7 @@ function setup-ohos-hdc-env() {
     require-env-var OHOS_SYSROOT
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
     OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
     OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
 }
@@ -209,6 +210,7 @@ function generate-cmake-ohos-hdc() {
           -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE}" \
           -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
           -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
           -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c88422a6d5f4f..18cb2a523bd0e 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -25,6 +25,11 @@
 #endif
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
+#if defined(__has_include)
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
+#endif
 #endif
 
 namespace libunwind {
@@ -1951,18 +1956,16 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
       return false;
     return has_sme != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = getauxval(AT_HWCAP2);
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = 0;
     elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
 #else
   static bool checkHasSME() {

>From 498fa8d127ff49c27d4230996338c6eb5d747fe6 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 1 Jul 2026 15:49:11 +0800
Subject: [PATCH 3/6] Stabilize OHOS HDC runtimes tests

Add OHOS lit features for HDC executor and platform limitations, mark affected libc++ tests as unsupported or expected failures, and adjust test support for OHOS filesystem cleanup and allocation counting behavior.

Also guard the aarch64 ZA unwind test on HWCAP2_SME so devices without a reliable SME feature definition do not execute SME instructions.
---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  9 +++++-
 .../clang/clang_modules_include.gen.py        |  3 ++
 .../locale.facet/no_allocation.pass.cpp       |  1 +
 libcxx/test/selftest/dsl/dsl.sh.py            |  5 ++++
 libcxx/test/selftest/stdin-is-piped.sh.cpp    |  1 +
 .../directory_entry.cons/path.pass.cpp        |  1 +
 .../directory_entry.mods/assign.pass.cpp      |  1 +
 .../directory_entry.mods/refresh.pass.cpp     |  1 +
 .../replace_filename.pass.cpp                 |  1 +
 .../directory_entry.obs/file_size.pass.cpp    |  1 +
 .../file_type_obs.pass.cpp                    |  1 +
 .../hard_link_count.pass.cpp                  |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../directory_iterator.members/ctor.pass.cpp  |  1 +
 .../path.member/path.assign/move.pass.cpp     |  2 +-
 .../path.member/path.construct/move.pass.cpp  |  2 +-
 .../rec.dir.itr.members/ctor.pass.cpp         |  1 +
 .../rec.dir.itr.members/increment.pass.cpp    |  1 +
 .../fs.op.funcs/fs.op.exists/exists.pass.cpp  |  1 +
 .../is_block_file.pass.cpp                    |  1 +
 .../is_character_file.pass.cpp                |  1 +
 .../fs.op.is_directory/is_directory.pass.cpp  |  1 +
 .../fs.op.is_empty/is_empty.pass.cpp          |  1 +
 .../fs.op.is_fifo/is_fifo.pass.cpp            |  1 +
 .../fs.op.is_other/is_other.pass.cpp          |  1 +
 .../is_regular_file.pass.cpp                  |  1 +
 .../fs.op.is_socket/is_socket.pass.cpp        |  1 +
 .../fs.op.is_symlink/is_symlink.pass.cpp      |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../fs.op.funcs/fs.op.remove/remove.pass.cpp  |  1 +
 .../fs.op.remove_all/remove_all.pass.cpp      |  1 +
 .../fs.op.funcs/fs.op.status/status.pass.cpp  |  1 +
 .../symlink_status.pass.cpp                   |  1 +
 .../temp_directory_path.pass.cpp              |  1 +
 .../ext.manip/get_money.pass.cpp              |  1 +
 .../ext.manip/put_money.pass.cpp              |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 .../print.fun/no_file_description.pass.cpp    |  3 ++
 .../std.manip/setfill_wchar_max.pass.cpp      |  1 +
 .../narrow.stream.objects/cerr.sh.cpp         |  1 +
 .../narrow.stream.objects/cin.sh.cpp          |  1 +
 .../narrow.stream.objects/clog.sh.cpp         |  1 +
 .../wide.stream.objects/wcerr-imbue.sh.cpp    |  1 +
 .../wide.stream.objects/wcerr.sh.cpp          |  1 +
 .../wide.stream.objects/wcin-imbue.sh.cpp     |  1 +
 .../wide.stream.objects/wcin.sh.cpp           |  1 +
 .../wide.stream.objects/wclog.sh.cpp          |  1 +
 .../sized_delete_array.pass.cpp               |  1 +
 ..._array_calls_unsized_delete_array.pass.cpp |  1 +
 .../new.delete.single/sized_delete.pass.cpp   |  1 +
 ...sized_delete_calls_unsized_delete.pass.cpp |  1 +
 .../locale.collate.byname/compare.pass.cpp    |  1 +
 .../locale.collate.byname/transform.pass.cpp  |  1 +
 .../facet.ctype.char.dtor/dtor.pass.cpp       |  1 +
 .../locale.ctype.byname/is_1.pass.cpp         |  1 +
 .../locale.ctype.byname/is_many.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_is.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_not.pass.cpp     |  1 +
 .../locale.ctype.byname/tolower_1.pass.cpp    |  1 +
 .../locale.ctype.byname/tolower_many.pass.cpp |  1 +
 .../locale.ctype.byname/toupper_1.pass.cpp    |  1 +
 .../locale.ctype.byname/toupper_many.pass.cpp |  1 +
 .../locale.ctype.byname/widen_1.pass.cpp      |  1 +
 .../locale.ctype.byname/widen_many.pass.cpp   |  1 +
 .../get_long_double_en_US.pass.cpp            |  1 +
 .../get_long_double_zh_CN.pass.cpp            |  1 +
 .../get_string_en_US.pass.cpp                 |  1 +
 .../put_long_double_en_US.pass.cpp            |  1 +
 .../put_long_double_zh_CN.pass.cpp            |  1 +
 .../put_string_en_US.pass.cpp                 |  1 +
 .../locale/locale.cons/default.pass.cpp       |  1 +
 .../test/std/re/re.traits/transform.pass.cpp  |  1 +
 .../re/re.traits/transform_primary.pass.cpp   |  1 +
 .../thread_create_failure.pass.cpp            |  1 +
 .../thread.jthread/join.deadlock.pass.cpp     |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 libcxx/test/support/count_new.h               | 28 +++++++++++++++++++
 libcxx/test/support/filesystem_test_helper.h  |  4 ++-
 libcxx/test/support/test_macros.h             |  4 ++-
 libunwind/test/aarch64_za_unwind.pass.cpp     | 13 +++++----
 80 files changed, 132 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index a20ed9ce9c6a9..542bc0cc83a1f 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -7,7 +7,8 @@ config.substitutions.append(('%{flags}',
     ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
 ))
 config.substitutions.append(('%{compile_flags}',
-    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
 ))
 config.substitutions.append(('%{link_flags}',
     # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
@@ -24,6 +25,12 @@ import libcxx.test.params, libcxx.test.config
 
 config.available_features.add('LIBCXX-OHOS-FIXME')
 config.available_features.add('ohos-hdc')
+config.available_features.add('executor-has-no-stdin')
+config.available_features.add('executor-merges-stderr-into-stdout')
+config.available_features.add('executor-runs-as-root')
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
index 20a545b719ed6..f04a6bce76691 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
index 03e9de882ab0a..428d30804a4c3 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
index 10829f8a73db1..c8a1c34aa3028 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index 071ee7f6c891f..44b94528f5e32 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a hard link.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
index 5d931891dd0eb..d291ca2cb5641 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
index 82b94a3d6bf5f..e4bc9c41728da 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
index 8401dbd38cd71..e0fa44ffbc4a5 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
index ac5499cdd6f20..2bcc64a3c2651 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_assignable<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
index 1f1843f7d401d..5efdf983f8a10 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_constructible<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
index 08d7ff84378d5..a3768b8cbd83f 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
index 1f2293989b7ef..9e8a70b4339d4 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // On Android L, ~scoped_test_env() is unable to delete the temp dir using
 // chmod+rm because chmod is too broken.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
index 56465d01021ad..62dc696d6797d 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
index fad880ecf30f7..708fb511dce73 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
index 12bc0c198c062..69e357adeafb5 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
index f1b559cf2d55b..0ed4aa0dc8d58 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
index f3161f2e020d2..579c03f2b8c0b 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
index fbf5e7129f7af..1d5312d65be46 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
index d0974640429df..7a3a56a0b161c 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
index 4a5882e36cde2..8b877ff63b112 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
index 382685c21dd99..995125d702de3 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
index 07b446d80c402..692fe773a0f9e 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
index c420fea21aa66..22c2c114ba687 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
index 56b4a27de6b42..8064feaaf2065 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
index b30d56685e2cb..540849ccefc05 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
index 425c224606038..02ff1e249d3bc 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
index 1d405a816e110..41474f9ca1e48 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
index 38e14438f3fd3..9a891eab6b343 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
index 88672851f7b6b..3e1d34ab5257a 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
index d0ff2c87689d1..4c299b876639e 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
index 98fa56b7f4b6d..7970887bac199 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
index 283a8fddb8e8f..13e6f74c5a143 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -17,6 +17,9 @@
 // fmemopen is available starting in Android M (API 23)
 // XFAIL: target={{.+}}-android{{(eabi)?(21|22)}}
 
+// OHOS fmemopen/FILE behavior does not currently match the expectation tested here.
+// XFAIL: LIBCXX-OHOS-FIXME
+
 // <print>
 
 // The FILE returned by fmemopen does not have file descriptor.
diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
index b24c0b90d86f1..2ddddbef70dbb 100644
--- a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
@@ -18,6 +18,7 @@
 // XFAIL: target=armv{{7|8}}{{l?}}{{.*}}-linux-gnueabihf && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-linux-gnu && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-amazon-linux && libcpp-abi-version=1
+// XFAIL: target={{.*}}-linux-ohos && libcpp-abi-version=1
 
 #include <iomanip>
 #include <ostream>
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
index da5563f87df45..ea5af90a90822 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
index 4f23b6b9a5d87..6dc0ff36ff4bf 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
@@ -14,6 +14,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
index 9bedd67e816ef..cbb9639766207 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
index ec4c8a009ba0c..59d93e74db3b7 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
index ae82035ccfc62..49a6ce0892ea4 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
index 6ae1972234b2b..58a6e18340ddf 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
index 08b74b999bbf4..88dcfe0485429 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
index 89e178248ae50..9c9745dcc9777 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wclog;
 
 // XFAIL: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
index 0e411f8adc0f2..e563152f2c8cd 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
index 026250daeee89..34119303a29ba 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete [] is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
index ca0f612bf7578..a4e32aaa11e13 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
index fbc9cf070bcba..3ec2b47b53eb0 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
index 8ae6bc2d3ba66..7cfd9f955fce7 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
@@ -8,6 +8,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
index 4978db116ef39..743075ede8ece 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
index 7be7e8d574e6c..b77a4409522f0 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
@@ -11,6 +11,7 @@
 // template <> class ctype<char>
 
 // ~ctype();
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <locale>
 #include <cassert>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
index 5cebc079c4703..c4803c04d173b 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <type_traits>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
index 2869378b42b92..5174ffebf281f 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
index ea03f042f2d45..665da7323e38a 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
index 2f7acb843e591..5d87c081b6e1c 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
index 447353de8af72..93d93aa9228f7 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
index 94ecc6c8fa85b..ab448a01632d6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
index 2b37737a26bc3..4be990e5b88c6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
index 016d8d736ad22..e0a3912a9315e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
index 959a4be9e1de9..b22629c82444e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
index 078b4a6fefb7e..c2cd904808794 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
index 9997b07134563..73b7a6decda36 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
index c86df7e6b53bf..deb30d747653f 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
index 478df7964f6d2..ee6fbd48b09b6 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
index 4b767fae871fa..c476537c3c7e3 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
index 25046a8417083..8174a724d4165 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
index 1c8710a008f27..e23cb4bf5f04a 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
index f0086ada30303..a36e3af8d12eb 100644
--- a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
+++ b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
@@ -12,6 +12,7 @@
 
 // This test runs in C++20, but we have deprecated codecvt<char(16|32), char, mbstate_t> in C++20.
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // <locale>
 
diff --git a/libcxx/test/std/re/re.traits/transform.pass.cpp b/libcxx/test/std/re/re.traits/transform.pass.cpp
index 80cd3f01faff2..112ecd47ff4e2 100644
--- a/libcxx/test/std/re/re.traits/transform.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform.pass.cpp
@@ -8,6 +8,7 @@
 //
 // NetBSD does not support LC_COLLATE at the moment
 // XFAIL: netbsd
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..cbc9387facb30 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -11,6 +11,7 @@
 
 // XFAIL: LIBCXX-AIX-FIXME
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
index 40daa49393bd4..110d0ea9fd694 100644
--- a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
+++ b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
@@ -15,6 +15,7 @@
 
 // There is no way to limit the number of threads on windows
 // UNSUPPORTED: windows
+// UNSUPPORTED: executor-runs-as-root
 
 // macOS and FreeBSD seem to limit the number of processes, not threads via RLIMIT_NPROC.
 // XFAIL: target={{.+}}-apple-{{.*}}
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
index c6eb0694ab2e1..19573ae52fd71 100644
--- a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
@@ -9,6 +9,7 @@
 // Windows cannot detect the deadlock. Instead of throwing system_error,
 // it would dead lock the test
 // UNSUPPORTED: windows
+// UNSUPPORTED: pthread-join-self-deadlocks
 
 // TSAN bug: https://llvm.org/PR66537
 // UNSUPPORTED: tsan
diff --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
index 51be78e1a02f8..804036e425572 100644
--- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
@@ -14,6 +14,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..6901aff00bc4e 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -415,6 +415,13 @@ void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteCalled(p);
+  std::free(p);
+}
+#  endif
+
 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->newArrayCalled(s);
@@ -449,6 +456,13 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteArrayCalled(p);
+  std::free(p);
+}
+#  endif
+
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
 #      define USE_ALIGNED_ALLOC
@@ -519,6 +533,13 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
@@ -551,6 +572,13 @@ void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
 
 #endif // DISABLE_NEW_COUNT
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index 2ad9efb32c60f..f820a4a0fa915 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -182,7 +182,7 @@ struct scoped_test_env
         std::string cmd = "chmod -R 777 " + test_root.string();
 #endif // defined(__MVS__)
         int ret = std::system(cmd.c_str());
-#  if !defined(_AIX) && !defined(__ANDROID__)
+#  if !defined(_AIX) && !defined(__ANDROID__) && !defined(__OHOS__)
         // On AIX the chmod command will return non-zero when trying to set
         // the permissions on a directory that contains a bad symlink. This triggers
         // the assert, despite being able to delete everything with the following
@@ -196,6 +196,8 @@ struct scoped_test_env
         // LIBCXX-ANDROID-FIXME: Other fixes to consider: place a toybox chmod
         // onto old devices, re-enable this assert for devices running Android N
         // and up, rewrite this chmod+rm in C or C++.
+        //
+        // OHOS has the same chmod -R behavior for bad symlinks.
         assert(ret == 0);
 #  endif
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..fdf6bc5b18d72 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -375,7 +375,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NOT_WIN32(...) __VA_ARGS__
 #endif
 
-#if defined(TEST_WINDOWS_DLL) ||defined(__MVS__) || defined(_AIX)
+#if defined(TEST_WINDOWS_DLL) || defined(__MVS__) || defined(_AIX) || defined(TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS)
 // Macros for waiving cases when we can't count allocations done within
 // the library implementation.
 //
@@ -386,6 +386,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 //
 // The same goes on IBM zOS.
 // The same goes on AIX.
+// Some target test configurations can define
+// TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS for the same reason.
 #define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
 #define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
 #else
diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp
index 36536860c1a96..b28f70a6e8e7a 100644
--- a/libunwind/test/aarch64_za_unwind.pass.cpp
+++ b/libunwind/test/aarch64_za_unwind.pass.cpp
@@ -20,6 +20,9 @@
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
 #endif
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
 
 // Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA
 // (and commits a lazy save of ZA) before resuming from unwinding.
@@ -33,18 +36,16 @@ static bool checkHasSME() {
     return false;
   return has_sme != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = getauxval(AT_HWCAP2);
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = 0;
   elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
 #else
 static bool checkHasSME() {

>From bbaa18370c45aaf79c2446be0a6e94dc2aa0ef00 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Fri, 3 Jul 2026 10:33:38 +0800
Subject: [PATCH 4/6] Add qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  16 +-
 libcxx/utils/ci/run-buildbot                  |  41 ++++-
 libcxx/utils/ci/vendor/ohos/qemu_run.py       | 144 ++++++++++++++++++
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |   8 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |   8 +-
 5 files changed, 204 insertions(+), 13 deletions(-)
 create mode 100755 libcxx/utils/ci/vendor/ohos/qemu_run.py

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 542bc0cc83a1f..20aa7a49d5802 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++ using HDC on OHOS.
+# libc++ using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -23,15 +23,21 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
 config.available_features.add('LIBCXX-OHOS-FIXME')
-config.available_features.add('ohos-hdc')
-config.available_features.add('executor-has-no-stdin')
-config.available_features.add('executor-merges-stderr-into-stdout')
-config.available_features.add('executor-runs-as-root')
+config.available_features.add(executor_kind)
 config.available_features.add('platform-has-nonportable-locales')
 config.available_features.add('platform-has-unreliable-new-delete-interposition')
 config.available_features.add('pthread-join-self-deadlocks')
 
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
     libcxx.test.features.DEFAULT_FEATURES,
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index ecf0ca17ae148..40739b0fba4d8 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -28,6 +28,16 @@ ${PROGNAME} [options] <BUILDER>
 --build-dir <DIR>   The directory to use for building the library. By default,
                     this is '<llvm-root>/build/<builder>'.
 
+--ohos-qemu <PATH>  Run OHOS tests under qemu-aarch64 instead of HDC. This also
+                    accepts OHOS_QEMU or QEMU from the environment.
+
+--ohos-qemu-sysroot <DIR>
+                    Sysroot passed to qemu-aarch64 with -L. Defaults to
+                    OHOS_QEMU_SYSROOT, QEMU_SYSROOT, then OHOS_SYSROOT.
+
+--ohos-hdc <PATH>   HDC binary to use when OHOS tests run on a device. This sets
+                    HDC for the OHOS HDC executor.
+
 Environment variables
 CC                  The C compiler to use, this value is used by CMake. This
                     variable is mandatory.
@@ -84,6 +94,19 @@ while [[ $# -gt 0 ]]; do
             BUILD_DIR="${2}"
             shift; shift
             ;;
+        --ohos-qemu)
+            OHOS_QEMU="${2}"
+            shift; shift
+            ;;
+        --ohos-qemu-sysroot)
+            OHOS_QEMU_SYSROOT="${2}"
+            shift; shift
+            ;;
+        --ohos-hdc)
+            HDC="${2}"
+            export HDC
+            shift; shift
+            ;;
         *)
             BUILDER="${1}"
             shift
@@ -193,8 +216,18 @@ function setup-ohos-hdc-env() {
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
     OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
-    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
-    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+    OHOS_QEMU="${OHOS_QEMU:-${QEMU:-}}"
+
+    if [[ -n "${OHOS_QEMU}" ]]; then
+        OHOS_QEMU_SYSROOT="${OHOS_QEMU_SYSROOT:-${QEMU_SYSROOT:-${OHOS_SYSROOT}}}"
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/qemu_run.py --qemu ${OHOS_QEMU} --sysroot ${OHOS_QEMU_SYSROOT}"
+        OHOS_EXECUTOR_KIND="ohos-qemu"
+    else
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+        OHOS_EXECUTOR_KIND="ohos-hdc"
+    fi
+
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE};executor_kind=${OHOS_EXECUTOR_KIND}"
 }
 
 function generate-cmake-ohos-hdc() {
@@ -863,14 +896,14 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
-ohos-hdc-aarch64-full)
+ohos-aarch64-full|ohos-hdc-aarch64-full)
     clean
 
     generate-cmake-ohos-hdc
 
     check-runtimes
 ;;
-ohos-hdc-aarch64-smoke)
+ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
     clean
 
     generate-cmake-ohos-hdc
diff --git a/libcxx/utils/ci/vendor/ohos/qemu_run.py b/libcxx/utils/ci/vendor/ohos/qemu_run.py
new file mode 100755
index 0000000000000..ef7eef7872864
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/qemu_run.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _resolve_binary(binary):
+    if os.path.isabs(binary):
+        if os.path.isfile(binary) and os.access(binary, os.X_OK):
+            return binary
+        sys.exit(f"failed to find executable: {binary}")
+
+    resolved = shutil.which(binary)
+    if resolved is None:
+        sys.exit(f"failed to find executable: {binary}")
+    return resolved
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(str(parent))
+    return library_dirs
+
+
+def _build_target_env_args(env, prepend_env):
+    target_env = dict(env)
+
+    for key, value in prepend_env.items():
+        existing = target_env.get(key, os.environ.get(key, ""))
+        target_env[key] = f"{value}{os.pathsep}{existing}" if existing else value
+
+    args = []
+    for key, value in target_env.items():
+        args.extend(["-E", f"{key}={value}"])
+    return args
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--qemu",
+        default=os.environ.get("OHOS_QEMU", os.environ.get("QEMU", "qemu-aarch64")),
+    )
+    parser.add_argument("--sysroot", required=True)
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(
+            os.environ.get(
+                "OHOS_QEMU_TIMEOUT_SECONDS",
+                os.environ.get("QEMU_TIMEOUT_SECONDS", "600"),
+            )
+        ),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    qemu = _resolve_binary(args.qemu)
+    execdir = Path(args.execdir).resolve()
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    build_library_dirs = _collect_build_library_dirs(execdir)
+    if build_library_dirs:
+        existing = prepend_env.get("LD_LIBRARY_PATH")
+        build_library_path = os.pathsep.join(build_library_dirs)
+        prepend_env["LD_LIBRARY_PATH"] = (
+            f"{build_library_path}{os.pathsep}{existing}"
+            if existing
+            else build_library_path
+        )
+
+    cmd = [
+        qemu,
+        "-L",
+        args.sysroot,
+        *_build_target_env_args(env, prepend_env),
+        *args.command,
+    ]
+    try:
+        completed = subprocess.run(
+            cmd,
+            cwd=execdir,
+            timeout=args.timeout_seconds,
+            check=False,
+        )
+    except subprocess.TimeoutExpired:
+        print(
+            f"qemu executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+            file=sys.stderr,
+        )
+        return 124
+    return completed.returncode
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index d18748fe3a3cc..8f5a17b3dec64 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++abi using HDC on OHOS.
+# libc++abi using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -20,7 +20,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index bce241a7898cf..f2a032c41a123 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -1,6 +1,6 @@
 #
 # Configuration file for running the libunwind tests against the shared library
-# using HDC on OHOS.
+# using HDC or QEMU on OHOS.
 #
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
@@ -45,7 +45,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,

>From dde4d430033711fb7ffa87621ba86b37492f480b Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 14:52:40 +0800
Subject: [PATCH 5/6] Fix something for qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in                   | 4 +++-
 .../file.streams/fstreams/filebuf.members/close.pass.cpp      | 3 +++
 .../fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp     | 2 ++
 .../syncstream/osyncstream/thread/several_threads.pass.cpp    | 2 ++
 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in             | 4 +++-
 libcxxabi/test/guard_threaded_test.pass.cpp                   | 2 ++
 libunwind/test/bad_unwind_info.pass.cpp                       | 2 ++
 libunwind/test/configs/llvm-libunwind-ohos.cfg.in             | 4 +++-
 libunwind/test/signal_unwind.pass.cpp                         | 2 ++
 9 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 20aa7a49d5802..3be1c267ede2a 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -23,7 +23,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
index 2c4cfb0ac7661..ce802d6ceba78 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14
 // REQUIRES: linux
 // UNSUPPORTED: no-filesystem
+// OHOS qemu-user observes the host /proc, not a real OHOS process /proc.
+// UNSUPPORTED: ohos-qemu
 // XFAIL: no-localization
 
 // <filesystem>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..77db6396755bf 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -10,6 +10,8 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
+// This thread-heavy stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 // <syncstream>
 
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index 8f5a17b3dec64..d0a8d1248f88a 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -20,7 +20,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxxabi/test/guard_threaded_test.pass.cpp b/libcxxabi/test/guard_threaded_test.pass.cpp
index d898e60288aca..a4e14f0aca9e4 100644
--- a/libcxxabi/test/guard_threaded_test.pass.cpp
+++ b/libcxxabi/test/guard_threaded_test.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: no-exceptions
+// This thread-heavy guard stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 #define TESTING_CXA_GUARD
 #include "../src/cxa_guard_impl.h"
diff --git a/libunwind/test/bad_unwind_info.pass.cpp b/libunwind/test/bad_unwind_info.pass.cpp
index 332b661d2e98f..76102505f2d67 100644
--- a/libunwind/test/bad_unwind_info.pass.cpp
+++ b/libunwind/test/bad_unwind_info.pass.cpp
@@ -13,6 +13,8 @@
 // REQUIRES: target={{(aarch64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
 // UNSUPPORTED: target={{.*-apple.*}}
+// OHOS qemu-user does not reliably model this invalid unwind-info path.
+// UNSUPPORTED: ohos-qemu
 
 // GCC doesn't support __attribute__((naked)) on AArch64.
 // UNSUPPORTED: gcc
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index f2a032c41a123..71d1e711e36d5 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -45,7 +45,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp
index f8798bc3eabeb..ef73a3ea8c59a 100644
--- a/libunwind/test/signal_unwind.pass.cpp
+++ b/libunwind/test/signal_unwind.pass.cpp
@@ -10,6 +10,8 @@
 // Ensure that the unwinder can cope with the signal handler.
 // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
+// OHOS qemu-user does not provide device-equivalent signal unwinding behavior.
+// UNSUPPORTED: ohos-qemu
 
 // TODO: Figure out why this fails with Memory Sanitizer.
 // XFAIL: msan

>From c81df7252fb00b5b9b41f0a29393e775abc9c810 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 15:19:29 +0800
Subject: [PATCH 6/6] delete run-smoke-test

---
 libcxx/utils/ci/buildkite-pipeline.yml | 12 ----------
 libcxx/utils/ci/run-buildbot           | 31 --------------------------
 2 files changed, 43 deletions(-)

diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 77617f43ae6dc..70bc69637a7f2 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -175,18 +175,6 @@ steps:
 
 - group: OHOS
   steps:
-  - label: OHOS AArch64 HDC Smoke
-    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
-    env:
-      CC: clang
-      CXX: clang++
-      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
-      HDC_TIMEOUT_SECONDS: 240
-    agents:
-      queue: libcxx-builders
-      os: linux
-      device: ohos-hdc
-    <<: *common
   - label: OHOS AArch64 HDC Full
     command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
     env:
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 40739b0fba4d8..e7d751df484e4 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -147,23 +147,6 @@ function require-env-var() {
     fi
 }
 
-function detect-lit() {
-    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
-        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
-    elif command -v llvm-lit >/dev/null 2>&1; then
-        LIT_RUNNER=("$(command -v llvm-lit)")
-    else
-        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
-    fi
-}
-
-function run-smoke-test() {
-    local test_dir="${1}"
-    local filter="${2}"
-    step "Running smoke test ${filter}"
-    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
-}
-
 function ohos-lit-args() {
     local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
     if python3 -c 'import psutil' >/dev/null 2>&1; then
@@ -903,20 +886,6 @@ ohos-aarch64-full|ohos-hdc-aarch64-full)
 
     check-runtimes
 ;;
-ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
-    clean
-
-    generate-cmake-ohos-hdc
-
-    step "Building the OHOS smoke test dependencies"
-    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
-
-    detect-lit
-    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
-;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #


From libcxx-commits at lists.llvm.org  Thu Jul  9 01:39:31 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 01:39:31 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a4f5e43.2020f6c2.243e78.faea@mx.google.com>


https://github.com/huihui15537226823 updated https://github.com/llvm/llvm-project/pull/208390

>From 70e8ac839667f8aba4aa74a2dd19614426f9754f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 24 Jun 2026 09:47:00 +0800
Subject: [PATCH 1/6] Add OHOS HDC runtimes CI support

---
 cmake/Modules/GetToolchainDirs.cmake          |  37 +-
 .../cmake/Modules/CompilerRTUtils.cmake       |   8 +-
 compiler-rt/cmake/builtin-config-ix.cmake     |   2 +-
 compiler-rt/cmake/crt-config-ix.cmake         |   2 +-
 compiler-rt/lib/builtins/crtbegin.c           |   9 +
 compiler-rt/lib/builtins/emutls.c             |   8 +-
 libcxx/CMakeLists.txt                         |  10 +-
 libcxx/cmake/config-ix.cmake                  |   2 +
 libcxx/include/__config                       |   5 +-
 libcxx/include/__cxx03/__config               |   5 +-
 .../__cxx03/__locale_dir/locale_base_api.h    |   2 +-
 libcxx/include/__locale_dir/locale_base_api.h |   2 +-
 libcxx/src/include/config_elast.h             |   2 +
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  48 ++
 libcxx/utils/ci/buildkite-pipeline.yml        |  27 +
 libcxx/utils/ci/run-buildbot                  |  87 ++++
 libcxx/utils/ci/vendor/ohos/hdc_run.py        | 472 ++++++++++++++++++
 libcxx/utils/ci/vendor/ohos/hdc_run_test.py   | 115 +++++
 libcxxabi/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |  30 ++
 libunwind/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |  55 ++
 22 files changed, 910 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/test/configs/llvm-libc++-ohos.cfg.in
 create mode 100755 libcxx/utils/ci/vendor/ohos/hdc_run.py
 create mode 100644 libcxx/utils/ci/vendor/ohos/hdc_run_test.py
 create mode 100644 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
 create mode 100644 libunwind/test/configs/llvm-libunwind-ohos.cfg.in

diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..a20ed9ce9c6a9
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,48 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index d4107cf11ad51..77617f43ae6dc 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -172,3 +172,30 @@ steps:
       queue: libcxx-builders
       os: android
     <<: *common
+
+- group: OHOS
+  steps:
+  - label: OHOS AArch64 HDC Smoke
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
+  - label: OHOS AArch64 HDC Full
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 6376bc1d3dd5a..1867e9c620649 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -116,6 +116,41 @@ function clean() {
     rm -rf "${BUILD_DIR}"
 }
 
+function require-env-var() {
+    local name="${1}"
+    if [[ -z ${!name+x} ]] || [[ -z "${!name}" ]]; then
+        error "Environment variable ${name} must be defined"
+        exit 1
+    fi
+}
+
+function detect-lit() {
+    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
+        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
+    elif command -v llvm-lit >/dev/null 2>&1; then
+        LIT_RUNNER=("$(command -v llvm-lit)")
+    else
+        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
+    fi
+}
+
+function run-smoke-test() {
+    local test_dir="${1}"
+    local filter="${2}"
+    step "Running smoke test ${filter}"
+    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
+}
+
+function ohos-lit-args() {
+    local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
+    if python3 -c 'import psutil' >/dev/null 2>&1; then
+        args="${args} --timeout=1500"
+    else
+        echo "warning: python3 psutil not available; running OHOS lit without per-test timeout" >&2
+    fi
+    printf '%s' "${args}"
+}
+
 function generate-cmake-base() {
     step "Generating CMake"
 
@@ -153,6 +188,37 @@ function generate-cmake-android() {
           "${@}"
 }
 
+function setup-ohos-hdc-env() {
+    require-env-var OHOS_SYSROOT
+
+    OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+}
+
+function generate-cmake-ohos-hdc() {
+    setup-ohos-hdc-env
+
+    local lit_args
+    lit_args="$(ohos-lit-args)"
+
+    generate-cmake \
+          -DLLVM_LIT_ARGS="${lit_args}" \
+          -DCMAKE_SYSTEM_NAME=OHOS \
+          -DCMAKE_SYSROOT="${OHOS_SYSROOT}" \
+          -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
+          -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
+          -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
+          -DLIBUNWIND_TEST_CONFIG="llvm-libunwind-ohos.cfg.in" \
+          -DLIBCXX_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBCXXABI_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBUNWIND_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          "${@}"
+}
+
 function check-runtimes() {
     step "Building libc++ test dependencies"
     ninja -vC "${BUILD_DIR}" cxx-test-depends
@@ -795,6 +861,27 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
+ohos-hdc-aarch64-full)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    check-runtimes
+;;
+ohos-hdc-aarch64-smoke)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    step "Building the OHOS smoke test dependencies"
+    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
+
+    detect-lit
+    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
+;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run.py b/libcxx/utils/ci/vendor/ohos/hdc_run.py
new file mode 100755
index 0000000000000..61a3899b88b11
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run.py
@@ -0,0 +1,472 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import uuid
+
+
+EXIT_MARKER = "__llvm_hdc_exit__="
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _split_path_list(value):
+    return [entry for entry in value.split(os.pathsep) if entry]
+
+
+def _resolve_hdc_binary(hdc):
+    if os.path.isabs(hdc):
+        return hdc
+    resolved = shutil.which(hdc)
+    if resolved is None:
+        sys.exit(f"failed to find hdc binary: {hdc}")
+    return resolved
+
+
+def _is_windows_hdc(path):
+    return path.lower().endswith(".exe")
+
+
+def _find_windows_local_root(hdc_path):
+    parts = Path(hdc_path).parts
+    for index in range(len(parts) - 1):
+        if parts[index] == "AppData" and index + 1 < len(parts) and parts[index + 1] == "Local":
+            return Path(*parts[: index + 2])
+    return None
+
+
+def _default_host_staging_root(hdc_path):
+    if _is_windows_hdc(hdc_path):
+        local_root = _find_windows_local_root(hdc_path)
+        if local_root is None:
+            sys.exit(
+                "failed to derive a Windows staging directory from the hdc.exe path; "
+                "set HDC_HOST_STAGING_ROOT to a path under /mnt/<drive>/..."
+            )
+        return local_root / "Temp" / "llvm-lit-hdc"
+    return Path("/tmp/llvm-lit-hdc")
+
+
+def _to_windows_path(path):
+    resolved = Path(path).resolve()
+    parts = resolved.parts
+    if len(parts) < 4 or parts[1] != "mnt":
+        sys.exit(
+            f"Windows hdc requires a staging path under /mnt/<drive>/..., got: {resolved}"
+        )
+    drive = parts[2].upper()
+    tail = "\\".join(parts[3:])
+    return f"{drive}:\\{tail}" if tail else f"{drive}:\\"
+
+
+def _run_readelf(path):
+    completed = subprocess.run(
+        ["readelf", "-d", path],
+        check=False,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        text=True,
+    )
+    if completed.returncode != 0:
+        return ""
+    return completed.stdout
+
+
+def _read_elf_metadata(path):
+    needed = []
+    runpath_dirs = []
+    seen_dirs = set()
+    for line in _run_readelf(path).splitlines():
+        needed_match = re.search(r"\(NEEDED\).*Shared library: \[(.*)\]", line)
+        if needed_match:
+            needed.append(needed_match.group(1))
+            continue
+
+        runpath_match = re.search(
+            r"\((?:RUNPATH|RPATH)\).*Library runpath: \[(.*)\]", line
+        )
+        if not runpath_match:
+            continue
+        for entry in _split_path_list(runpath_match.group(1)):
+            candidate = Path(entry)
+            if not candidate.is_dir():
+                continue
+            key = str(candidate.resolve())
+            if key in seen_dirs:
+                continue
+            seen_dirs.add(key)
+            runpath_dirs.append(candidate)
+    return needed, runpath_dirs
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(parent)
+    return library_dirs
+
+
+def _find_dependency(name, search_dirs):
+    for directory in search_dirs:
+        candidate = Path(directory) / name
+        if candidate.is_file():
+            return candidate.resolve()
+    return None
+
+
+def _copy_needed_libraries(executables, execdir, staging_execdir):
+    search_dirs = _collect_build_library_dirs(execdir)
+    queue = [Path(exe).resolve() for exe in executables]
+    visited = set()
+    copied_names = {path.name for path in staging_execdir.glob("*") if path.is_file()}
+
+    while queue:
+        current = queue.pop(0)
+        current_key = str(current)
+        if current_key in visited:
+            continue
+        visited.add(current_key)
+
+        needed, runpath_dirs = _read_elf_metadata(current)
+        current_search_dirs = []
+        current_seen = set()
+        for directory in [*runpath_dirs, current.parent, *search_dirs]:
+            resolved = Path(directory).resolve()
+            key = str(resolved)
+            if key in current_seen:
+                continue
+            current_seen.add(key)
+            current_search_dirs.append(resolved)
+
+        for directory in runpath_dirs:
+            resolved = directory.resolve()
+            if all(str(resolved) != str(existing) for existing in search_dirs):
+                search_dirs.append(resolved)
+
+        for lib_name in needed:
+            dependency = _find_dependency(lib_name, current_search_dirs)
+            if dependency is None:
+                continue
+            if lib_name not in copied_names:
+                shutil.copy2(dependency, staging_execdir / lib_name)
+                copied_names.add(lib_name)
+            queue.append(dependency)
+
+
+def _copy_execdir(execdir, staging_execdir):
+    if staging_execdir.exists():
+        shutil.rmtree(staging_execdir)
+    shutil.copytree(execdir, staging_execdir)
+
+
+def _iter_files(root):
+    for directory, dirnames, filenames in os.walk(root):
+        dirnames.sort()
+        filenames.sort()
+        yield Path(directory), filenames
+
+
+def _is_test_executable(path):
+    return path.endswith(".tmp.exe") and os.path.exists(path)
+
+
+def _map_local_path(path, local_execdir, remote_execdir):
+    normalized = os.path.normpath(path)
+    execdir = os.path.normpath(local_execdir)
+    if normalized == execdir:
+        return remote_execdir
+    prefix = execdir + os.sep
+    if normalized.startswith(prefix):
+        suffix = normalized[len(prefix) :].replace(os.sep, "/")
+        return f"{remote_execdir}/{suffix}" if suffix else remote_execdir
+    return path
+
+
+def _map_value(value, local_execdir, remote_execdir):
+    return _map_local_path(value, local_execdir, remote_execdir)
+
+
+def _build_env_exports(env, prepend_env, local_execdir, remote_execdir, add_remote_libdir):
+    commands = []
+    mapped_env = {
+        key: _map_value(value, local_execdir, remote_execdir) for key, value in env.items()
+    }
+    mapped_prepend = {
+        key: _map_value(value, local_execdir, remote_execdir)
+        for key, value in prepend_env.items()
+    }
+
+    if add_remote_libdir:
+        existing = mapped_prepend.get("LD_LIBRARY_PATH")
+        mapped_prepend["LD_LIBRARY_PATH"] = (
+            f"{remote_execdir}:{existing}" if existing else remote_execdir
+        )
+
+    for key, value in mapped_env.items():
+        commands.append(f"export {key}={shlex.quote(value)}")
+    for key, value in mapped_prepend.items():
+        commands.append(f"export {key}={shlex.quote(value)}${{{key}:+:{key}}}")
+    return commands
+
+
+def _hdc_prefix(hdc_path, server, target):
+    prefix = [hdc_path]
+    if server:
+        prefix.extend(["-s", server])
+    if target:
+        prefix.extend(["-t", target])
+    return prefix
+
+
+def _run_hdc(
+    prefix,
+    args,
+    *,
+    timeout=None,
+    capture_output=False,
+    stdin=subprocess.DEVNULL,
+    check=True,
+    expect_stdout=None,
+):
+    kwargs = {
+        "check": False,
+        "timeout": timeout,
+        "stdin": stdin,
+        "text": True,
+    }
+    if capture_output:
+        kwargs["stdout"] = subprocess.PIPE
+        kwargs["stderr"] = subprocess.PIPE
+    completed = subprocess.run(prefix + args, **kwargs)
+    output = completed.stdout if capture_output and completed.stdout else ""
+    error_output = completed.stderr if capture_output and completed.stderr else ""
+    if check and completed.returncode != 0:
+        raise RuntimeError(
+            f"hdc command failed with exit code {completed.returncode}: "
+            f"{shlex.join(prefix + args)}\n{output}{error_output}"
+        )
+    if check and expect_stdout is not None and expect_stdout not in output:
+        raise RuntimeError(
+            f"hdc command did not report success: {shlex.join(prefix + args)}\n{output}"
+        )
+    return completed
+
+
+def _run_checked_shell(prefix, command, *, timeout=None):
+    completed = _run_hdc(
+        prefix,
+        [
+            "shell",
+            f"{command}; rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"",
+        ],
+        timeout=timeout,
+        capture_output=True,
+    )
+    exit_code, output = _extract_exit_code(completed.stdout)
+    if exit_code is None:
+        raise RuntimeError(
+            f"failed to parse shell exit code for: {command}\n{completed.stdout}"
+        )
+    if exit_code != 0:
+        raise RuntimeError(
+            f"remote shell command failed with exit code {exit_code}: {command}\n{output}"
+        )
+    return output
+
+
+def _sync_tree_to_device(prefix, local_root, remote_root, use_windows_paths):
+    _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_root)}")
+    _run_checked_shell(prefix, f"mkdir -p {shlex.quote(remote_root)}")
+
+    for directory, filenames in _iter_files(local_root):
+        relative_dir = os.path.relpath(directory, local_root)
+        device_dir = (
+            remote_root
+            if relative_dir == "."
+            else f"{remote_root}/{relative_dir.replace(os.sep, '/')}"
+        )
+        _run_checked_shell(prefix, f"mkdir -p {shlex.quote(device_dir)}")
+        for name in filenames:
+            local_path = directory / name
+            device_path = f"{device_dir}/{name}"
+            source = _to_windows_path(local_path) if use_windows_paths else str(local_path)
+            _run_hdc(
+                prefix,
+                ["file", "send", source, device_path],
+                capture_output=True,
+                expect_stdout="FileTransfer finish",
+            )
+            if name.endswith(".tmp.exe") or os.access(local_path, os.X_OK):
+                _run_checked_shell(prefix, f"chmod +x {shlex.quote(device_path)}")
+
+
+def _build_remote_command(command, local_execdir, remote_execdir, exports):
+    mapped_command = [
+        _map_local_path(arg, local_execdir, remote_execdir) for arg in command
+    ]
+    pieces = [f"cd {shlex.quote(remote_execdir)}"]
+    pieces.extend(exports)
+    pieces.append(shlex.join(mapped_command))
+    pieces.append(f"rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"")
+    return " && ".join(pieces[:-2]) + f"; {pieces[-2]}; {pieces[-1]}"
+
+
+def _extract_exit_code(output):
+    match = re.search(rf"^(.*)\n{EXIT_MARKER}(\d+)\s*$", output, re.DOTALL)
+    if not match:
+        return None, output
+    return int(match.group(2)), match.group(1)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--hdc", default=os.environ.get("HDC", "hdc"))
+    parser.add_argument("--hdc-server", default=os.environ.get("HDC_SERVER_IP_PORT"))
+    parser.add_argument("--hdc-target", default=os.environ.get("HDC_UTID"))
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--remote-base-dir",
+        default="/data/local/tmp/llvm-lit-hdc",
+    )
+    parser.add_argument(
+        "--host-staging-root",
+        default=os.environ.get("HDC_HOST_STAGING_ROOT"),
+    )
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(os.environ.get("HDC_TIMEOUT_SECONDS", "120")),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("--keep-remote-dir", action="store_true")
+    parser.add_argument("--keep-host-staging", action="store_true")
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    hdc_path = _resolve_hdc_binary(args.hdc)
+    use_windows_paths = _is_windows_hdc(hdc_path)
+    host_staging_root = (
+        Path(args.host_staging_root)
+        if args.host_staging_root
+        else _default_host_staging_root(hdc_path)
+    )
+    prefix = _hdc_prefix(hdc_path, args.hdc_server, args.hdc_target)
+
+    execdir = Path(args.execdir).resolve()
+    run_id = uuid.uuid4().hex[:12]
+    staging_execdir = host_staging_root / f"run-{run_id}" / execdir.name
+    remote_execdir = f"{args.remote_base_dir}/run-{run_id}/{execdir.name}"
+
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    executables = [arg for arg in args.command if _is_test_executable(arg)]
+    host_staging_root.mkdir(parents=True, exist_ok=True)
+    _copy_execdir(execdir, staging_execdir)
+    _copy_needed_libraries(executables, execdir, staging_execdir)
+
+    add_remote_libdir = any(staging_execdir.glob("*.so*"))
+    exports = _build_env_exports(
+        env,
+        prepend_env,
+        str(execdir),
+        remote_execdir,
+        add_remote_libdir,
+    )
+
+    try:
+        _sync_tree_to_device(prefix, staging_execdir, remote_execdir, use_windows_paths)
+        remote_command = _build_remote_command(
+            args.command,
+            str(execdir),
+            remote_execdir,
+            exports,
+        )
+        try:
+            completed = _run_hdc(
+                prefix,
+                ["shell", remote_command],
+                timeout=args.timeout_seconds,
+                capture_output=True,
+                stdin=None,
+            )
+        except subprocess.TimeoutExpired:
+            print(
+                f"hdc executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+                file=sys.stderr,
+            )
+            return 124
+
+        exit_code, output = _extract_exit_code(completed.stdout)
+        if exit_code is None:
+            print(
+                "failed to parse exit code from hdc shell output",
+                file=sys.stderr,
+            )
+            if completed.stdout:
+                print(completed.stdout, end="", file=sys.stderr)
+            if completed.stderr:
+                print(completed.stderr, end="", file=sys.stderr)
+            return 1
+
+        if output:
+            sys.stdout.write(output)
+        if completed.stderr:
+            sys.stderr.write(completed.stderr)
+        return exit_code
+    finally:
+        if not args.keep_remote_dir:
+            try:
+                _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_execdir)}")
+            except Exception:
+                pass
+        if not args.keep_host_staging:
+            shutil.rmtree(staging_execdir.parent, ignore_errors=True)
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run_test.py b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
new file mode 100644
index 0000000000000..17e2f65bd1fb6
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import os
+from pathlib import Path
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import unittest
+
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+HDC_RUN = SCRIPT_DIR / "hdc_run.py"
+
+
+class HdcRunTest(unittest.TestCase):
+    def make_fake_hdc(self, directory):
+        fake_hdc = directory / "hdc"
+        fake_hdc.write_text(
+            textwrap.dedent(
+                """\
+                #!/usr/bin/env python3
+                import sys
+
+                EXIT_MARKER = "__llvm_hdc_exit__="
+
+                def main():
+                    args = sys.argv[1:]
+                    if args[:2] == ["file", "send"]:
+                        print("FileTransfer finish")
+                        return 0
+
+                    if args and args[0] == "shell":
+                        command = args[1]
+                        if "stderr.tmp.exe" in command:
+                            sys.stdout.write("STDOUT-OUTPUT")
+                            sys.stderr.write("STDERR-OUTPUT")
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        if "stdin.tmp.exe" in command:
+                            sys.stdout.write("IN:" + sys.stdin.read())
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                        return 0
+
+                    print("unexpected fake hdc invocation: " + repr(args), file=sys.stderr)
+                    return 2
+
+                if __name__ == "__main__":
+                    raise SystemExit(main())
+                """
+            )
+        )
+        fake_hdc.chmod(fake_hdc.stat().st_mode | stat.S_IXUSR)
+        return fake_hdc
+
+    def run_hdc_run(self, temp_dir, executable_name, *, stdin_text=None):
+        execdir = temp_dir / "execdir"
+        execdir.mkdir()
+        executable = execdir / executable_name
+        executable.write_text("# fake executable marker\n")
+        executable.chmod(executable.stat().st_mode | stat.S_IXUSR)
+
+        fake_hdc = self.make_fake_hdc(temp_dir)
+        return subprocess.run(
+            [
+                sys.executable,
+                str(HDC_RUN),
+                "--hdc",
+                str(fake_hdc),
+                "--execdir",
+                str(execdir),
+                "--host-staging-root",
+                str(temp_dir / "staging"),
+                str(executable),
+            ],
+            input=stdin_text,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+    def test_preserves_remote_stdout_and_stderr_separately(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(Path(temp), "stderr.tmp.exe")
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "STDOUT-OUTPUT")
+        self.assertEqual(completed.stderr, "STDERR-OUTPUT")
+
+    def test_forwards_stdin_to_remote_command(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(
+                Path(temp),
+                "stdin.tmp.exe",
+                stdin_text="hello from stdin",
+            )
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "IN:hello from stdin")
+        self.assertEqual(completed.stderr, "")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt
index e1a1587fb6283..cb22e6fe65108 100644
--- a/libcxxabi/CMakeLists.txt
+++ b/libcxxabi/CMakeLists.txt
@@ -187,7 +187,12 @@ set(LIBCXXABI_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXXABI_TARGET_SUBDIR)
+  else()
+    set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXXABI_LIBDIR_SUBDIR)
     string(APPEND LIBCXXABI_TARGET_SUBDIR /${LIBCXXABI_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
new file mode 100644
index 0000000000000..d18748fe3a3cc
--- /dev/null
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -0,0 +1,30 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++abi using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -I %{libcxx}/test/support -I %{libcxx}/src -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    '-nostdlib++ -L %{lib} -lc++ -lc++abi -pthread'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 02132f6c07fcd..4fd11bf5aca2f 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -142,7 +142,12 @@ set(LIBUNWIND_STATIC_OUTPUT_NAME "unwind" CACHE STRING "Output name for the stat
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBUNWIND_TARGET_SUBDIR)
+  else()
+    set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBUNWIND_LIBDIR_SUBDIR)
     string(APPEND LIBUNWIND_TARGET_SUBDIR /${LIBUNWIND_LIBDIR_SUBDIR})
   endif()
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
new file mode 100644
index 0000000000000..bce241a7898cf
--- /dev/null
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -0,0 +1,55 @@
+#
+# Configuration file for running the libunwind tests against the shared library
+# using HDC on OHOS.
+#
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+compile_flags = []
+link_flags = []
+
+if @HAVE_GETAUXVAL@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_GETAUXVAL')
+
+if @HAVE_ELF_AUX_INFO@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_ELF_AUX_INFO')
+
+if @LIBUNWIND_ENABLE_CET@:
+    compile_flags.append('-fcf-protection=full')
+
+if @LIBUNWIND_ENABLE_GCS@:
+    compile_flags.append('-mbranch-protection=standard')
+
+if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
+    link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
+
+if '@CMAKE_DL_LIBS@':
+    link_flags.append('-l at CMAKE_DL_LIBS@')
+
+compile_flags.append('-funwind-tables')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags))
+))
+config.substitutions.append(('%{link_flags}',
+    '-L %{{lib}} -lunwind {}'.format(' '.join(link_flags))
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)

>From e9e5d2e517fb072f3554798e49b7119ec739105f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 25 Jun 2026 11:30:38 +0800
Subject: [PATCH 2/6] Fix OHOS libc++ CI ABI and SME detection

---
 libcxx/utils/ci/run-buildbot |  2 ++
 libunwind/src/Registers.hpp  | 15 +++++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 1867e9c620649..ecf0ca17ae148 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -192,6 +192,7 @@ function setup-ohos-hdc-env() {
     require-env-var OHOS_SYSROOT
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
     OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
     OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
 }
@@ -209,6 +210,7 @@ function generate-cmake-ohos-hdc() {
           -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE}" \
           -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
           -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
           -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c88422a6d5f4f..18cb2a523bd0e 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -25,6 +25,11 @@
 #endif
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
+#if defined(__has_include)
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
+#endif
 #endif
 
 namespace libunwind {
@@ -1951,18 +1956,16 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
       return false;
     return has_sme != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = getauxval(AT_HWCAP2);
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = 0;
     elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
 #else
   static bool checkHasSME() {

>From 498fa8d127ff49c27d4230996338c6eb5d747fe6 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 1 Jul 2026 15:49:11 +0800
Subject: [PATCH 3/6] Stabilize OHOS HDC runtimes tests

Add OHOS lit features for HDC executor and platform limitations, mark affected libc++ tests as unsupported or expected failures, and adjust test support for OHOS filesystem cleanup and allocation counting behavior.

Also guard the aarch64 ZA unwind test on HWCAP2_SME so devices without a reliable SME feature definition do not execute SME instructions.
---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  9 +++++-
 .../clang/clang_modules_include.gen.py        |  3 ++
 .../locale.facet/no_allocation.pass.cpp       |  1 +
 libcxx/test/selftest/dsl/dsl.sh.py            |  5 ++++
 libcxx/test/selftest/stdin-is-piped.sh.cpp    |  1 +
 .../directory_entry.cons/path.pass.cpp        |  1 +
 .../directory_entry.mods/assign.pass.cpp      |  1 +
 .../directory_entry.mods/refresh.pass.cpp     |  1 +
 .../replace_filename.pass.cpp                 |  1 +
 .../directory_entry.obs/file_size.pass.cpp    |  1 +
 .../file_type_obs.pass.cpp                    |  1 +
 .../hard_link_count.pass.cpp                  |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../directory_iterator.members/ctor.pass.cpp  |  1 +
 .../path.member/path.assign/move.pass.cpp     |  2 +-
 .../path.member/path.construct/move.pass.cpp  |  2 +-
 .../rec.dir.itr.members/ctor.pass.cpp         |  1 +
 .../rec.dir.itr.members/increment.pass.cpp    |  1 +
 .../fs.op.funcs/fs.op.exists/exists.pass.cpp  |  1 +
 .../is_block_file.pass.cpp                    |  1 +
 .../is_character_file.pass.cpp                |  1 +
 .../fs.op.is_directory/is_directory.pass.cpp  |  1 +
 .../fs.op.is_empty/is_empty.pass.cpp          |  1 +
 .../fs.op.is_fifo/is_fifo.pass.cpp            |  1 +
 .../fs.op.is_other/is_other.pass.cpp          |  1 +
 .../is_regular_file.pass.cpp                  |  1 +
 .../fs.op.is_socket/is_socket.pass.cpp        |  1 +
 .../fs.op.is_symlink/is_symlink.pass.cpp      |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../fs.op.funcs/fs.op.remove/remove.pass.cpp  |  1 +
 .../fs.op.remove_all/remove_all.pass.cpp      |  1 +
 .../fs.op.funcs/fs.op.status/status.pass.cpp  |  1 +
 .../symlink_status.pass.cpp                   |  1 +
 .../temp_directory_path.pass.cpp              |  1 +
 .../ext.manip/get_money.pass.cpp              |  1 +
 .../ext.manip/put_money.pass.cpp              |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 .../print.fun/no_file_description.pass.cpp    |  3 ++
 .../std.manip/setfill_wchar_max.pass.cpp      |  1 +
 .../narrow.stream.objects/cerr.sh.cpp         |  1 +
 .../narrow.stream.objects/cin.sh.cpp          |  1 +
 .../narrow.stream.objects/clog.sh.cpp         |  1 +
 .../wide.stream.objects/wcerr-imbue.sh.cpp    |  1 +
 .../wide.stream.objects/wcerr.sh.cpp          |  1 +
 .../wide.stream.objects/wcin-imbue.sh.cpp     |  1 +
 .../wide.stream.objects/wcin.sh.cpp           |  1 +
 .../wide.stream.objects/wclog.sh.cpp          |  1 +
 .../sized_delete_array.pass.cpp               |  1 +
 ..._array_calls_unsized_delete_array.pass.cpp |  1 +
 .../new.delete.single/sized_delete.pass.cpp   |  1 +
 ...sized_delete_calls_unsized_delete.pass.cpp |  1 +
 .../locale.collate.byname/compare.pass.cpp    |  1 +
 .../locale.collate.byname/transform.pass.cpp  |  1 +
 .../facet.ctype.char.dtor/dtor.pass.cpp       |  1 +
 .../locale.ctype.byname/is_1.pass.cpp         |  1 +
 .../locale.ctype.byname/is_many.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_is.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_not.pass.cpp     |  1 +
 .../locale.ctype.byname/tolower_1.pass.cpp    |  1 +
 .../locale.ctype.byname/tolower_many.pass.cpp |  1 +
 .../locale.ctype.byname/toupper_1.pass.cpp    |  1 +
 .../locale.ctype.byname/toupper_many.pass.cpp |  1 +
 .../locale.ctype.byname/widen_1.pass.cpp      |  1 +
 .../locale.ctype.byname/widen_many.pass.cpp   |  1 +
 .../get_long_double_en_US.pass.cpp            |  1 +
 .../get_long_double_zh_CN.pass.cpp            |  1 +
 .../get_string_en_US.pass.cpp                 |  1 +
 .../put_long_double_en_US.pass.cpp            |  1 +
 .../put_long_double_zh_CN.pass.cpp            |  1 +
 .../put_string_en_US.pass.cpp                 |  1 +
 .../locale/locale.cons/default.pass.cpp       |  1 +
 .../test/std/re/re.traits/transform.pass.cpp  |  1 +
 .../re/re.traits/transform_primary.pass.cpp   |  1 +
 .../thread_create_failure.pass.cpp            |  1 +
 .../thread.jthread/join.deadlock.pass.cpp     |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 libcxx/test/support/count_new.h               | 28 +++++++++++++++++++
 libcxx/test/support/filesystem_test_helper.h  |  4 ++-
 libcxx/test/support/test_macros.h             |  4 ++-
 libunwind/test/aarch64_za_unwind.pass.cpp     | 13 +++++----
 80 files changed, 132 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index a20ed9ce9c6a9..542bc0cc83a1f 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -7,7 +7,8 @@ config.substitutions.append(('%{flags}',
     ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
 ))
 config.substitutions.append(('%{compile_flags}',
-    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
 ))
 config.substitutions.append(('%{link_flags}',
     # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
@@ -24,6 +25,12 @@ import libcxx.test.params, libcxx.test.config
 
 config.available_features.add('LIBCXX-OHOS-FIXME')
 config.available_features.add('ohos-hdc')
+config.available_features.add('executor-has-no-stdin')
+config.available_features.add('executor-merges-stderr-into-stdout')
+config.available_features.add('executor-runs-as-root')
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
index 20a545b719ed6..f04a6bce76691 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
index 03e9de882ab0a..428d30804a4c3 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
index 10829f8a73db1..c8a1c34aa3028 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index 071ee7f6c891f..44b94528f5e32 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a hard link.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
index 5d931891dd0eb..d291ca2cb5641 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
index 82b94a3d6bf5f..e4bc9c41728da 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
index 8401dbd38cd71..e0fa44ffbc4a5 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
index ac5499cdd6f20..2bcc64a3c2651 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_assignable<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
index 1f1843f7d401d..5efdf983f8a10 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_constructible<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
index 08d7ff84378d5..a3768b8cbd83f 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
index 1f2293989b7ef..9e8a70b4339d4 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // On Android L, ~scoped_test_env() is unable to delete the temp dir using
 // chmod+rm because chmod is too broken.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
index 56465d01021ad..62dc696d6797d 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
index fad880ecf30f7..708fb511dce73 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
index 12bc0c198c062..69e357adeafb5 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
index f1b559cf2d55b..0ed4aa0dc8d58 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
index f3161f2e020d2..579c03f2b8c0b 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
index fbf5e7129f7af..1d5312d65be46 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
index d0974640429df..7a3a56a0b161c 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
index 4a5882e36cde2..8b877ff63b112 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
index 382685c21dd99..995125d702de3 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
index 07b446d80c402..692fe773a0f9e 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
index c420fea21aa66..22c2c114ba687 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
index 56b4a27de6b42..8064feaaf2065 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
index b30d56685e2cb..540849ccefc05 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
index 425c224606038..02ff1e249d3bc 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
index 1d405a816e110..41474f9ca1e48 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
index 38e14438f3fd3..9a891eab6b343 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
index 88672851f7b6b..3e1d34ab5257a 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
index d0ff2c87689d1..4c299b876639e 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
index 98fa56b7f4b6d..7970887bac199 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
index 283a8fddb8e8f..13e6f74c5a143 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -17,6 +17,9 @@
 // fmemopen is available starting in Android M (API 23)
 // XFAIL: target={{.+}}-android{{(eabi)?(21|22)}}
 
+// OHOS fmemopen/FILE behavior does not currently match the expectation tested here.
+// XFAIL: LIBCXX-OHOS-FIXME
+
 // <print>
 
 // The FILE returned by fmemopen does not have file descriptor.
diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
index b24c0b90d86f1..2ddddbef70dbb 100644
--- a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
@@ -18,6 +18,7 @@
 // XFAIL: target=armv{{7|8}}{{l?}}{{.*}}-linux-gnueabihf && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-linux-gnu && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-amazon-linux && libcpp-abi-version=1
+// XFAIL: target={{.*}}-linux-ohos && libcpp-abi-version=1
 
 #include <iomanip>
 #include <ostream>
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
index da5563f87df45..ea5af90a90822 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
index 4f23b6b9a5d87..6dc0ff36ff4bf 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
@@ -14,6 +14,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
index 9bedd67e816ef..cbb9639766207 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
index ec4c8a009ba0c..59d93e74db3b7 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
index ae82035ccfc62..49a6ce0892ea4 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
index 6ae1972234b2b..58a6e18340ddf 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
index 08b74b999bbf4..88dcfe0485429 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
index 89e178248ae50..9c9745dcc9777 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wclog;
 
 // XFAIL: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
index 0e411f8adc0f2..e563152f2c8cd 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
index 026250daeee89..34119303a29ba 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete [] is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
index ca0f612bf7578..a4e32aaa11e13 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
index fbc9cf070bcba..3ec2b47b53eb0 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
index 8ae6bc2d3ba66..7cfd9f955fce7 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
@@ -8,6 +8,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
index 4978db116ef39..743075ede8ece 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
index 7be7e8d574e6c..b77a4409522f0 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
@@ -11,6 +11,7 @@
 // template <> class ctype<char>
 
 // ~ctype();
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <locale>
 #include <cassert>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
index 5cebc079c4703..c4803c04d173b 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <type_traits>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
index 2869378b42b92..5174ffebf281f 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
index ea03f042f2d45..665da7323e38a 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
index 2f7acb843e591..5d87c081b6e1c 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
index 447353de8af72..93d93aa9228f7 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
index 94ecc6c8fa85b..ab448a01632d6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
index 2b37737a26bc3..4be990e5b88c6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
index 016d8d736ad22..e0a3912a9315e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
index 959a4be9e1de9..b22629c82444e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
index 078b4a6fefb7e..c2cd904808794 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
index 9997b07134563..73b7a6decda36 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
index c86df7e6b53bf..deb30d747653f 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
index 478df7964f6d2..ee6fbd48b09b6 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
index 4b767fae871fa..c476537c3c7e3 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
index 25046a8417083..8174a724d4165 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
index 1c8710a008f27..e23cb4bf5f04a 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
index f0086ada30303..a36e3af8d12eb 100644
--- a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
+++ b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
@@ -12,6 +12,7 @@
 
 // This test runs in C++20, but we have deprecated codecvt<char(16|32), char, mbstate_t> in C++20.
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // <locale>
 
diff --git a/libcxx/test/std/re/re.traits/transform.pass.cpp b/libcxx/test/std/re/re.traits/transform.pass.cpp
index 80cd3f01faff2..112ecd47ff4e2 100644
--- a/libcxx/test/std/re/re.traits/transform.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform.pass.cpp
@@ -8,6 +8,7 @@
 //
 // NetBSD does not support LC_COLLATE at the moment
 // XFAIL: netbsd
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..cbc9387facb30 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -11,6 +11,7 @@
 
 // XFAIL: LIBCXX-AIX-FIXME
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
index 40daa49393bd4..110d0ea9fd694 100644
--- a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
+++ b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
@@ -15,6 +15,7 @@
 
 // There is no way to limit the number of threads on windows
 // UNSUPPORTED: windows
+// UNSUPPORTED: executor-runs-as-root
 
 // macOS and FreeBSD seem to limit the number of processes, not threads via RLIMIT_NPROC.
 // XFAIL: target={{.+}}-apple-{{.*}}
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
index c6eb0694ab2e1..19573ae52fd71 100644
--- a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
@@ -9,6 +9,7 @@
 // Windows cannot detect the deadlock. Instead of throwing system_error,
 // it would dead lock the test
 // UNSUPPORTED: windows
+// UNSUPPORTED: pthread-join-self-deadlocks
 
 // TSAN bug: https://llvm.org/PR66537
 // UNSUPPORTED: tsan
diff --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
index 51be78e1a02f8..804036e425572 100644
--- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
@@ -14,6 +14,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..6901aff00bc4e 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -415,6 +415,13 @@ void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteCalled(p);
+  std::free(p);
+}
+#  endif
+
 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->newArrayCalled(s);
@@ -449,6 +456,13 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteArrayCalled(p);
+  std::free(p);
+}
+#  endif
+
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
 #      define USE_ALIGNED_ALLOC
@@ -519,6 +533,13 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
@@ -551,6 +572,13 @@ void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
 
 #endif // DISABLE_NEW_COUNT
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index 2ad9efb32c60f..f820a4a0fa915 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -182,7 +182,7 @@ struct scoped_test_env
         std::string cmd = "chmod -R 777 " + test_root.string();
 #endif // defined(__MVS__)
         int ret = std::system(cmd.c_str());
-#  if !defined(_AIX) && !defined(__ANDROID__)
+#  if !defined(_AIX) && !defined(__ANDROID__) && !defined(__OHOS__)
         // On AIX the chmod command will return non-zero when trying to set
         // the permissions on a directory that contains a bad symlink. This triggers
         // the assert, despite being able to delete everything with the following
@@ -196,6 +196,8 @@ struct scoped_test_env
         // LIBCXX-ANDROID-FIXME: Other fixes to consider: place a toybox chmod
         // onto old devices, re-enable this assert for devices running Android N
         // and up, rewrite this chmod+rm in C or C++.
+        //
+        // OHOS has the same chmod -R behavior for bad symlinks.
         assert(ret == 0);
 #  endif
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..fdf6bc5b18d72 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -375,7 +375,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NOT_WIN32(...) __VA_ARGS__
 #endif
 
-#if defined(TEST_WINDOWS_DLL) ||defined(__MVS__) || defined(_AIX)
+#if defined(TEST_WINDOWS_DLL) || defined(__MVS__) || defined(_AIX) || defined(TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS)
 // Macros for waiving cases when we can't count allocations done within
 // the library implementation.
 //
@@ -386,6 +386,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 //
 // The same goes on IBM zOS.
 // The same goes on AIX.
+// Some target test configurations can define
+// TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS for the same reason.
 #define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
 #define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
 #else
diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp
index 36536860c1a96..b28f70a6e8e7a 100644
--- a/libunwind/test/aarch64_za_unwind.pass.cpp
+++ b/libunwind/test/aarch64_za_unwind.pass.cpp
@@ -20,6 +20,9 @@
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
 #endif
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
 
 // Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA
 // (and commits a lazy save of ZA) before resuming from unwinding.
@@ -33,18 +36,16 @@ static bool checkHasSME() {
     return false;
   return has_sme != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = getauxval(AT_HWCAP2);
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = 0;
   elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
 #else
 static bool checkHasSME() {

>From bbaa18370c45aaf79c2446be0a6e94dc2aa0ef00 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Fri, 3 Jul 2026 10:33:38 +0800
Subject: [PATCH 4/6] Add qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  16 +-
 libcxx/utils/ci/run-buildbot                  |  41 ++++-
 libcxx/utils/ci/vendor/ohos/qemu_run.py       | 144 ++++++++++++++++++
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |   8 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |   8 +-
 5 files changed, 204 insertions(+), 13 deletions(-)
 create mode 100755 libcxx/utils/ci/vendor/ohos/qemu_run.py

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 542bc0cc83a1f..20aa7a49d5802 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++ using HDC on OHOS.
+# libc++ using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -23,15 +23,21 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
 config.available_features.add('LIBCXX-OHOS-FIXME')
-config.available_features.add('ohos-hdc')
-config.available_features.add('executor-has-no-stdin')
-config.available_features.add('executor-merges-stderr-into-stdout')
-config.available_features.add('executor-runs-as-root')
+config.available_features.add(executor_kind)
 config.available_features.add('platform-has-nonportable-locales')
 config.available_features.add('platform-has-unreliable-new-delete-interposition')
 config.available_features.add('pthread-join-self-deadlocks')
 
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
     libcxx.test.features.DEFAULT_FEATURES,
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index ecf0ca17ae148..40739b0fba4d8 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -28,6 +28,16 @@ ${PROGNAME} [options] <BUILDER>
 --build-dir <DIR>   The directory to use for building the library. By default,
                     this is '<llvm-root>/build/<builder>'.
 
+--ohos-qemu <PATH>  Run OHOS tests under qemu-aarch64 instead of HDC. This also
+                    accepts OHOS_QEMU or QEMU from the environment.
+
+--ohos-qemu-sysroot <DIR>
+                    Sysroot passed to qemu-aarch64 with -L. Defaults to
+                    OHOS_QEMU_SYSROOT, QEMU_SYSROOT, then OHOS_SYSROOT.
+
+--ohos-hdc <PATH>   HDC binary to use when OHOS tests run on a device. This sets
+                    HDC for the OHOS HDC executor.
+
 Environment variables
 CC                  The C compiler to use, this value is used by CMake. This
                     variable is mandatory.
@@ -84,6 +94,19 @@ while [[ $# -gt 0 ]]; do
             BUILD_DIR="${2}"
             shift; shift
             ;;
+        --ohos-qemu)
+            OHOS_QEMU="${2}"
+            shift; shift
+            ;;
+        --ohos-qemu-sysroot)
+            OHOS_QEMU_SYSROOT="${2}"
+            shift; shift
+            ;;
+        --ohos-hdc)
+            HDC="${2}"
+            export HDC
+            shift; shift
+            ;;
         *)
             BUILDER="${1}"
             shift
@@ -193,8 +216,18 @@ function setup-ohos-hdc-env() {
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
     OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
-    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
-    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+    OHOS_QEMU="${OHOS_QEMU:-${QEMU:-}}"
+
+    if [[ -n "${OHOS_QEMU}" ]]; then
+        OHOS_QEMU_SYSROOT="${OHOS_QEMU_SYSROOT:-${QEMU_SYSROOT:-${OHOS_SYSROOT}}}"
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/qemu_run.py --qemu ${OHOS_QEMU} --sysroot ${OHOS_QEMU_SYSROOT}"
+        OHOS_EXECUTOR_KIND="ohos-qemu"
+    else
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+        OHOS_EXECUTOR_KIND="ohos-hdc"
+    fi
+
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE};executor_kind=${OHOS_EXECUTOR_KIND}"
 }
 
 function generate-cmake-ohos-hdc() {
@@ -863,14 +896,14 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
-ohos-hdc-aarch64-full)
+ohos-aarch64-full|ohos-hdc-aarch64-full)
     clean
 
     generate-cmake-ohos-hdc
 
     check-runtimes
 ;;
-ohos-hdc-aarch64-smoke)
+ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
     clean
 
     generate-cmake-ohos-hdc
diff --git a/libcxx/utils/ci/vendor/ohos/qemu_run.py b/libcxx/utils/ci/vendor/ohos/qemu_run.py
new file mode 100755
index 0000000000000..ef7eef7872864
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/qemu_run.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _resolve_binary(binary):
+    if os.path.isabs(binary):
+        if os.path.isfile(binary) and os.access(binary, os.X_OK):
+            return binary
+        sys.exit(f"failed to find executable: {binary}")
+
+    resolved = shutil.which(binary)
+    if resolved is None:
+        sys.exit(f"failed to find executable: {binary}")
+    return resolved
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(str(parent))
+    return library_dirs
+
+
+def _build_target_env_args(env, prepend_env):
+    target_env = dict(env)
+
+    for key, value in prepend_env.items():
+        existing = target_env.get(key, os.environ.get(key, ""))
+        target_env[key] = f"{value}{os.pathsep}{existing}" if existing else value
+
+    args = []
+    for key, value in target_env.items():
+        args.extend(["-E", f"{key}={value}"])
+    return args
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--qemu",
+        default=os.environ.get("OHOS_QEMU", os.environ.get("QEMU", "qemu-aarch64")),
+    )
+    parser.add_argument("--sysroot", required=True)
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(
+            os.environ.get(
+                "OHOS_QEMU_TIMEOUT_SECONDS",
+                os.environ.get("QEMU_TIMEOUT_SECONDS", "600"),
+            )
+        ),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    qemu = _resolve_binary(args.qemu)
+    execdir = Path(args.execdir).resolve()
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    build_library_dirs = _collect_build_library_dirs(execdir)
+    if build_library_dirs:
+        existing = prepend_env.get("LD_LIBRARY_PATH")
+        build_library_path = os.pathsep.join(build_library_dirs)
+        prepend_env["LD_LIBRARY_PATH"] = (
+            f"{build_library_path}{os.pathsep}{existing}"
+            if existing
+            else build_library_path
+        )
+
+    cmd = [
+        qemu,
+        "-L",
+        args.sysroot,
+        *_build_target_env_args(env, prepend_env),
+        *args.command,
+    ]
+    try:
+        completed = subprocess.run(
+            cmd,
+            cwd=execdir,
+            timeout=args.timeout_seconds,
+            check=False,
+        )
+    except subprocess.TimeoutExpired:
+        print(
+            f"qemu executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+            file=sys.stderr,
+        )
+        return 124
+    return completed.returncode
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index d18748fe3a3cc..8f5a17b3dec64 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++abi using HDC on OHOS.
+# libc++abi using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -20,7 +20,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index bce241a7898cf..f2a032c41a123 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -1,6 +1,6 @@
 #
 # Configuration file for running the libunwind tests against the shared library
-# using HDC on OHOS.
+# using HDC or QEMU on OHOS.
 #
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
@@ -45,7 +45,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,

>From dde4d430033711fb7ffa87621ba86b37492f480b Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 14:52:40 +0800
Subject: [PATCH 5/6] Fix something for qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in                   | 4 +++-
 .../file.streams/fstreams/filebuf.members/close.pass.cpp      | 3 +++
 .../fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp     | 2 ++
 .../syncstream/osyncstream/thread/several_threads.pass.cpp    | 2 ++
 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in             | 4 +++-
 libcxxabi/test/guard_threaded_test.pass.cpp                   | 2 ++
 libunwind/test/bad_unwind_info.pass.cpp                       | 2 ++
 libunwind/test/configs/llvm-libunwind-ohos.cfg.in             | 4 +++-
 libunwind/test/signal_unwind.pass.cpp                         | 2 ++
 9 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 20aa7a49d5802..3be1c267ede2a 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -23,7 +23,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
index 2c4cfb0ac7661..ce802d6ceba78 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14
 // REQUIRES: linux
 // UNSUPPORTED: no-filesystem
+// OHOS qemu-user observes the host /proc, not a real OHOS process /proc.
+// UNSUPPORTED: ohos-qemu
 // XFAIL: no-localization
 
 // <filesystem>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..77db6396755bf 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -10,6 +10,8 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
+// This thread-heavy stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 // <syncstream>
 
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index 8f5a17b3dec64..d0a8d1248f88a 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -20,7 +20,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxxabi/test/guard_threaded_test.pass.cpp b/libcxxabi/test/guard_threaded_test.pass.cpp
index d898e60288aca..a4e14f0aca9e4 100644
--- a/libcxxabi/test/guard_threaded_test.pass.cpp
+++ b/libcxxabi/test/guard_threaded_test.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: no-exceptions
+// This thread-heavy guard stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 #define TESTING_CXA_GUARD
 #include "../src/cxa_guard_impl.h"
diff --git a/libunwind/test/bad_unwind_info.pass.cpp b/libunwind/test/bad_unwind_info.pass.cpp
index 332b661d2e98f..76102505f2d67 100644
--- a/libunwind/test/bad_unwind_info.pass.cpp
+++ b/libunwind/test/bad_unwind_info.pass.cpp
@@ -13,6 +13,8 @@
 // REQUIRES: target={{(aarch64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
 // UNSUPPORTED: target={{.*-apple.*}}
+// OHOS qemu-user does not reliably model this invalid unwind-info path.
+// UNSUPPORTED: ohos-qemu
 
 // GCC doesn't support __attribute__((naked)) on AArch64.
 // UNSUPPORTED: gcc
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index f2a032c41a123..71d1e711e36d5 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -45,7 +45,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp
index f8798bc3eabeb..ef73a3ea8c59a 100644
--- a/libunwind/test/signal_unwind.pass.cpp
+++ b/libunwind/test/signal_unwind.pass.cpp
@@ -10,6 +10,8 @@
 // Ensure that the unwinder can cope with the signal handler.
 // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
+// OHOS qemu-user does not provide device-equivalent signal unwinding behavior.
+// UNSUPPORTED: ohos-qemu
 
 // TODO: Figure out why this fails with Memory Sanitizer.
 // XFAIL: msan

>From c81df7252fb00b5b9b41f0a29393e775abc9c810 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 15:19:29 +0800
Subject: [PATCH 6/6] delete run-smoke-test

---
 libcxx/utils/ci/buildkite-pipeline.yml | 12 ----------
 libcxx/utils/ci/run-buildbot           | 31 --------------------------
 2 files changed, 43 deletions(-)

diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 77617f43ae6dc..70bc69637a7f2 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -175,18 +175,6 @@ steps:
 
 - group: OHOS
   steps:
-  - label: OHOS AArch64 HDC Smoke
-    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
-    env:
-      CC: clang
-      CXX: clang++
-      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
-      HDC_TIMEOUT_SECONDS: 240
-    agents:
-      queue: libcxx-builders
-      os: linux
-      device: ohos-hdc
-    <<: *common
   - label: OHOS AArch64 HDC Full
     command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
     env:
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 40739b0fba4d8..e7d751df484e4 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -147,23 +147,6 @@ function require-env-var() {
     fi
 }
 
-function detect-lit() {
-    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
-        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
-    elif command -v llvm-lit >/dev/null 2>&1; then
-        LIT_RUNNER=("$(command -v llvm-lit)")
-    else
-        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
-    fi
-}
-
-function run-smoke-test() {
-    local test_dir="${1}"
-    local filter="${2}"
-    step "Running smoke test ${filter}"
-    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
-}
-
 function ohos-lit-args() {
     local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
     if python3 -c 'import psutil' >/dev/null 2>&1; then
@@ -903,20 +886,6 @@ ohos-aarch64-full|ohos-hdc-aarch64-full)
 
     check-runtimes
 ;;
-ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
-    clean
-
-    generate-cmake-ohos-hdc
-
-    step "Building the OHOS smoke test dependencies"
-    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
-
-    detect-lit
-    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
-;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #


From libcxx-commits at lists.llvm.org  Thu Jul  9 02:16:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 02:16:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] f6b50ce - [libc++] Remove some
 unnecessary functions from __vector_layout (#207152)
Message-ID: <6a4f66f5.824e11a6.f5f27.9d83@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-09T11:16:32+02:00
New Revision: f6b50ceeca020729445239b5375343e909d3dc51

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

LOG: [libc++] Remove some unnecessary functions from __vector_layout (#207152)

This removes functions which produce identical IR after the first
InstCombine pass after inlining compared to their replacements.

Added: 
    

Modified: 
    libcxx/include/__vector/layout.h
    libcxx/include/__vector/vector.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__vector/layout.h b/libcxx/include/__vector/layout.h
index 910078b1e760e..61910a42fd708 100644
--- a/libcxx/include/__vector/layout.h
+++ b/libcxx/include/__vector/layout.h
@@ -179,9 +179,6 @@ class __vector_layout {
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type
   __remaining_capacity() const _NOEXCEPT;
 
-  /// Determines if a reallocation is necessary.
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __is_full() const _NOEXCEPT;
-
   /// Sets the member pointing to the first element in the vector to `__new_begin`, the member used
   /// to obtain the vector's bound to the equivalent of `__new_size`, and the member that represents
   /// the vector's capacity to the equivalent to `__new_capacity`.
@@ -202,9 +199,6 @@ class __vector_layout {
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __size() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __capacity() const _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __empty() const _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI _Tp& __back() _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI _Tp const& __back() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __end_ptr() _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer __end_ptr() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_ptr() _NOEXCEPT;
@@ -252,11 +246,6 @@ __vector_layout<_Tp, _Alloc>::__remaining_capacity() const _NOEXCEPT {
   return __capacity_ - __size_;
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__is_full() const _NOEXCEPT {
-  return __size_ == __capacity_;
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __vector_layout<_Tp, _Alloc>::__set_layout(
     pointer __new_begin, size_type __new_size, size_type __new_capacity) _NOEXCEPT {
@@ -324,21 +313,6 @@ __vector_layout<_Tp, _Alloc>::__capacity() const _NOEXCEPT {
   return __capacity_;
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__empty() const _NOEXCEPT {
-  return __size_ == 0;
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __vector_layout<_Tp, _Alloc>::__back() _NOEXCEPT {
-  return __begin_[__size_ - 1];
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp const& __vector_layout<_Tp, _Alloc>::__back() const _NOEXCEPT {
-  return __begin_[__size_ - 1];
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __vector_layout<_Tp, _Alloc>::pointer
 __vector_layout<_Tp, _Alloc>::__end_ptr() _NOEXCEPT {
@@ -384,11 +358,6 @@ __vector_layout<_Tp, _Alloc>::__remaining_capacity() const _NOEXCEPT {
   return __capacity_ - __end_;
 }
 
-template <class _Tp, class _Alloc>
-[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__is_full() const _NOEXCEPT {
-  return __end_ == __capacity_;
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __vector_layout<_Tp, _Alloc>::__set_layout(
     pointer __new_begin, size_type __new_size, size_type __new_capacity) _NOEXCEPT {
@@ -456,21 +425,6 @@ __vector_layout<_Tp, _Alloc>::__capacity() const _NOEXCEPT {
   return static_cast<size_type>(__capacity_ - __begin_);
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__empty() const _NOEXCEPT {
-  return __begin_ == __end_;
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __vector_layout<_Tp, _Alloc>::__back() _NOEXCEPT {
-  return __end_[-1];
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp const& __vector_layout<_Tp, _Alloc>::__back() const _NOEXCEPT {
-  return __end_[-1];
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __vector_layout<_Tp, _Alloc>::pointer
 __vector_layout<_Tp, _Alloc>::__end_ptr() _NOEXCEPT {

diff  --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..422c98c300a1d 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -400,7 +400,7 @@ class vector {
     return __layout_.__capacity();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
-    return __layout_.__empty();
+    return size() == 0;
   }
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {
@@ -442,11 +442,11 @@ class vector {
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() _NOEXCEPT {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "back() called on an empty vector");
-    return __layout_.__back();
+    return end()[-1];
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const _NOEXCEPT {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "back() called on an empty vector");
-    return __layout_.__back();
+    return end()[-1];
   }
 
   //
@@ -595,7 +595,7 @@ class vector {
         "vector::__vallocate can only be called on a vector that hasn't allocated memory. This vector either already "
         "owns a buffer, or a deallocation function didn't reset the layout's begin pointer.");
     _LIBCPP_ASSERT_INTERNAL(
-        __layout_.__empty(),
+        size() == 0,
         "vector::__vallocate can only be called on a vector that hasn't allocated memory. This vector either already "
         "owns a buffer, or a deallocation function didn't reset the layout's size.");
     _LIBCPP_ASSERT_INTERNAL(
@@ -1080,7 +1080,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Alloc>
 vector<_Tp, _Alloc>::emplace_back(_Args&&... __args) {
   pointer __end = __layout_.__end_ptr();
   std::__if_likely_else(
-      !__layout_.__is_full(),
+      size() != capacity(),
       [&] {
         __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
         ++__end;
@@ -1134,7 +1134,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(__x);
@@ -1157,7 +1157,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(std::move(__x));
@@ -1178,7 +1178,7 @@ template <class... _Args>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
@@ -1232,7 +1232,7 @@ vector<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __position, _Inpu
   
diff erence_type __off = __position - begin();
   pointer __p           = this->__layout_.__begin_ptr() + __off;
   pointer __old_last    = __layout_.__end_ptr();
-  for (; !__layout_.__is_full() && __first != __last; ++__first)
+  for (; size() != capacity() && __first != __last; ++__first)
     __emplace_back_assume_capacity(*__first);
 
   if (__first == __last)


        

From libcxx-commits at lists.llvm.org  Thu Jul  9 02:16:40 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 02:16:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some unnecessary
 functions from __vector_layout (PR #207152)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207152@github.com>
Message-ID: <6a4f66f8.af6ba3ed.36d9db.1d3a@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/207152

From libcxx-commits at lists.llvm.org  Thu Jul  9 02:45:31 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 02:45:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add tests for LWG3949 and mark
	it as done (PR #208272)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208272@github.com>
Message-ID: <6a4f6dbb.bb839515.27d21e.43e5@mx.google.com>


https://github.com/philnik777 approved this pull request.

I'm somewhat on the fence whether this is complete or nothing to do, but given that it seems it was required before and accidentally deleted nothing to do seems fine.

https://github.com/llvm/llvm-project/pull/208272

From libcxx-commits at lists.llvm.org  Thu Jul  9 02:52:51 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 02:52:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Improve diagnostic for libcxxabi
 missing from the runtimes (PR #208274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208274@github.com>
Message-ID: <6a4f6f73.f06f4994.1fc857.befa@mx.google.com>


================
@@ -148,6 +148,12 @@ elseif ("${LIBCXX_CXX_ABI}" STREQUAL "libcxxabi")
     add_library(libcxx-abi-static-objects ALIAS cxxabi_static_objects)
   endif()
 
+  if (NOT TARGET cxxabi_static AND NOT TARGET cxxabi_shared
+      AND NOT TARGET cxxabi_static_objects AND NOT TARGET cxxabi_shared_objects)
+    message(FATAL_ERROR "Can't find any libc++abi target but LIBCXX_CXX_ABI is set to libcxxabi, "
----------------
philnik777 wrote:

Maybe also mention that `system-libcxxabi` could be meant instead?

https://github.com/llvm/llvm-project/pull/208274

From libcxx-commits at lists.llvm.org  Thu Jul  9 02:55:01 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 02:55:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add missing <string> include (PR
	#208281)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208281@github.com>
Message-ID: <6a4f6ff5.9edb5c9d.208698.5326@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/208281

From libcxx-commits at lists.llvm.org  Thu Jul  9 02:55:40 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 02:55:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix assign_range test for
	forward_list (PR #208283)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208283@github.com>
Message-ID: <6a4f701c.ac469dc7.3a57b1.5352@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/208283

From libcxx-commits at lists.llvm.org  Thu Jul  9 03:15:46 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 03:15:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove benchmark::DoNotOptimize
 from custom predicates in benchmarks (PR #208412)
Message-ID: <llvm.org/llvm/llvm-project/pull/208412@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/208412

The point of the custom predicates is to defeat any detection of special predicates. There isn't much point in adding `benchmark::DoNotOptimize` on top of that. It can actually hurt, since it may hide performance changes due to how much/which information we provide to the compiler.


>From 2668f12f8b28cc0faf5dbe5e8b35dcbeeab3e332 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 9 Jul 2026 12:13:18 +0200
Subject: [PATCH] [libc++] Remove benchmark::DoNotOptimize from custom
 predicates in benchmarks

---
 .../benchmarks/algorithms/modifying/remove.bench.cpp |  5 +----
 .../algorithms/modifying/replace.bench.cpp           |  5 +----
 .../algorithms/modifying/transform.binary.bench.cpp  |  6 +-----
 .../algorithms/modifying/transform.unary.bench.cpp   |  5 +----
 .../benchmarks/algorithms/modifying/unique.bench.cpp |  6 +-----
 .../algorithms/modifying/unique_copy.bench.cpp       |  6 +-----
 .../algorithms/nonmodifying/adjacent_find.bench.cpp  |  6 +-----
 .../nonmodifying/any_all_none_of.bench.cpp           |  5 +----
 .../algorithms/nonmodifying/count.bench.cpp          |  5 +----
 .../algorithms/nonmodifying/ends_with.bench.cpp      |  6 +-----
 .../algorithms/nonmodifying/equal.bench.cpp          | 12 ++----------
 .../algorithms/nonmodifying/find_end.bench.cpp       |  6 +-----
 .../algorithms/nonmodifying/find_first_of.bench.cpp  |  6 +-----
 .../algorithms/nonmodifying/find_last.bench.cpp      | 10 ++--------
 .../algorithms/nonmodifying/fold.bench.cpp           |  6 +-----
 .../algorithms/nonmodifying/is_permutation.bench.cpp | 12 ++----------
 .../algorithms/nonmodifying/mismatch.bench.cpp       | 12 ++----------
 .../algorithms/nonmodifying/search.bench.cpp         |  6 +-----
 .../algorithms/nonmodifying/search_n.bench.cpp       |  6 +-----
 .../algorithms/nonmodifying/starts_with.bench.cpp    |  6 +-----
 .../algorithms/sorting/is_sorted.bench.cpp           |  6 +-----
 .../algorithms/sorting/is_sorted_until.bench.cpp     |  6 +-----
 22 files changed, 26 insertions(+), 123 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/modifying/remove.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/remove.bench.cpp
index cf5346113264e..e125b73d8cd61 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/remove.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/remove.bench.cpp
@@ -22,10 +22,7 @@
 int main(int argc, char** argv) {
   auto std_remove    = [](auto first, auto last, auto const& value) { return std::remove(first, last, value); };
   auto std_remove_if = [](auto first, auto last, auto const& value) {
-    return std::remove_if(first, last, [&](auto element) {
-      benchmark::DoNotOptimize(element);
-      return element == value;
-    });
+    return std::remove_if(first, last, [&](auto element) { return element == value; });
   };
 
   // Benchmark {std,ranges}::{remove,remove_if} on a sequence of the form xxxxxxxxxxyyyyyyyyyy
diff --git a/libcxx/test/benchmarks/algorithms/modifying/replace.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/replace.bench.cpp
index be55d4d6cdc60..287bc40089e35 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/replace.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/replace.bench.cpp
@@ -22,10 +22,7 @@
 int main(int argc, char** argv) {
   auto std_replace    = [](auto first, auto last, auto old, auto new_) { return std::replace(first, last, old, new_); };
   auto std_replace_if = [](auto first, auto last, auto old, auto new_) {
-    auto pred = [&](auto element) {
-      benchmark::DoNotOptimize(element);
-      return element == old;
-    };
+    auto pred = [&](auto element) { return element == old; };
     return std::replace_if(first, last, pred, new_);
   };
 
diff --git a/libcxx/test/benchmarks/algorithms/modifying/transform.binary.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/transform.binary.bench.cpp
index 8da1048b58f53..09118cd3bcdf3 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/transform.binary.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/transform.binary.bench.cpp
@@ -38,11 +38,7 @@ int main(int argc, char** argv) {
 
             std::vector<ValueType> out(size);
 
-            auto f = [](auto x, auto y) {
-              benchmark::DoNotOptimize(x);
-              benchmark::DoNotOptimize(y);
-              return x + y;
-            };
+            auto f = [](auto x, auto y) { return x + y; };
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(c1);
diff --git a/libcxx/test/benchmarks/algorithms/modifying/transform.unary.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/transform.unary.bench.cpp
index c640064d4a138..44c7a47db3965 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/transform.unary.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/transform.unary.bench.cpp
@@ -35,10 +35,7 @@ int main(int argc, char** argv) {
 
             std::vector<ValueType> out(size);
 
-            auto f = [](auto element) {
-              benchmark::DoNotOptimize(element);
-              return element;
-            };
+            auto f = [](auto element) { return element; };
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(c);
diff --git a/libcxx/test/benchmarks/algorithms/modifying/unique.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/unique.bench.cpp
index 05e1681c9840a..5529afc9d5b97 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/unique.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/unique.bench.cpp
@@ -22,11 +22,7 @@
 int main(int argc, char** argv) {
   auto std_unique      = [](auto first, auto last) { return std::unique(first, last); };
   auto std_unique_pred = [](auto first, auto last) {
-    return std::unique(first, last, [](auto a, auto b) {
-      benchmark::DoNotOptimize(a);
-      benchmark::DoNotOptimize(b);
-      return a == b;
-    });
+    return std::unique(first, last, [](auto a, auto b) { return a == b; });
   };
 
   // Create a sequence of the form xxxxxxxxxxyyyyyyyyyy and unique the
diff --git a/libcxx/test/benchmarks/algorithms/modifying/unique_copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/unique_copy.bench.cpp
index 75b7ccfa7921a..baca916a51aa0 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/unique_copy.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/unique_copy.bench.cpp
@@ -22,11 +22,7 @@
 int main(int argc, char** argv) {
   auto std_unique_copy      = [](auto first, auto last, auto out) { return std::unique_copy(first, last, out); };
   auto std_unique_copy_pred = [](auto first, auto last, auto out) {
-    return std::unique_copy(first, last, out, [](auto a, auto b) {
-      benchmark::DoNotOptimize(a);
-      benchmark::DoNotOptimize(b);
-      return a == b;
-    });
+    return std::unique_copy(first, last, out, [](auto a, auto b) { return a == b; });
   };
 
   // Create a sequence of the form xxxxxxxxxxyyyyyyyyyy and unique the
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp
index b9d7fc2e6c530..e67b5cd31a1a6 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp
@@ -21,11 +21,7 @@
 int main(int argc, char** argv) {
   auto std_adjacent_find      = [](auto first, auto last) { return std::adjacent_find(first, last); };
   auto std_adjacent_find_pred = [](auto first, auto last) {
-    return std::adjacent_find(first, last, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::adjacent_find(first, last, [](auto x, auto y) { return x == y; });
   };
 
   // Benchmark {std,ranges}::adjacent_find on a sequence of the form xyxyxyxyxyxyxyxyxyxy,
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp
index 946adfa545836..cf2eb996ddffc 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp
@@ -45,10 +45,7 @@ int main(int argc, char** argv) {
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(c);
-              auto result = any_of(c.begin(), c.end(), [&](auto element) {
-                benchmark::DoNotOptimize(element);
-                return element == y;
-              });
+              auto result = any_of(c.begin(), c.end(), [&](auto element) { return element == y; });
               benchmark::DoNotOptimize(result);
             }
           })
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp
index 0ab5b2493ce75..edffcbb58780c 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp
@@ -21,10 +21,7 @@
 int main(int argc, char** argv) {
   auto std_count    = [](auto first, auto last, auto const& value) { return std::count(first, last, value); };
   auto std_count_if = [](auto first, auto last, auto const& value) {
-    return std::count_if(first, last, [&](auto element) {
-      benchmark::DoNotOptimize(element);
-      return element == value;
-    });
+    return std::count_if(first, last, [&](auto element) { return element == value; });
   };
 
   // Benchmark {std,ranges}::{count,count_if} on a sequence where every other element is counted.
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
index 0c01833453156..f2b51a73f428e 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
@@ -22,11 +22,7 @@
 
 int main(int argc, char** argv) {
   auto ranges_ends_with_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::ranges::ends_with(first1, last1, first2, last2, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::ranges::ends_with(first1, last1, first2, last2, [](auto x, auto y) { return x == y; });
   };
 
   // Benchmark ranges::ends_with where we find the mismatching element at the very end.
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp
index 51f819013fed0..4ee194b431bac 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp
@@ -24,18 +24,10 @@ int main(int argc, char** argv) {
     return std::equal(first1, last1, first2, last2);
   };
   auto std_equal_3leg_pred = [](auto first1, auto last1, auto first2, auto) {
-    return std::equal(first1, last1, first2, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::equal(first1, last1, first2, [](auto x, auto y) { return x == y; });
   };
   auto std_equal_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::equal(first1, last1, first2, last2, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::equal(first1, last1, first2, last2, [](auto x, auto y) { return x == y; });
   };
 
   // Benchmark {std,ranges}::equal where we determine inequality at the very end (worst case).
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
index 1d5f7d8409719..86cba1be38cc0 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
@@ -25,11 +25,7 @@ int main(int argc, char** argv) {
     return std::find_end(first1, last1, first2, last2);
   };
   auto std_find_end_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::find_end(first1, last1, first2, last2, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::find_end(first1, last1, first2, last2, [](auto x, auto y) { return x == y; });
   };
 
   auto register_benchmarks = [&](auto bm, std::string comment) {
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
index 68c475cb69ffe..3c00db8dafbd8 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
@@ -24,11 +24,7 @@ int main(int argc, char** argv) {
     return std::find_first_of(first1, last1, first2, last2);
   };
   auto std_find_first_of_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::find_first_of(first1, last1, first2, last2, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::find_first_of(first1, last1, first2, last2, [](auto x, auto y) { return x == y; });
   };
 
   // Benchmark {std,ranges}::find_first_of where we never find a match in the needle, and the needle is small.
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
index 17074250c6c72..763a8596103c8 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
@@ -21,16 +21,10 @@
 
 int main(int argc, char** argv) {
   auto ranges_find_last_if = [](auto first, auto last, auto const& value) {
-    return std::ranges::find_last_if(first, last, [&](auto element) {
-      benchmark::DoNotOptimize(element);
-      return element == value;
-    });
+    return std::ranges::find_last_if(first, last, [&](auto element) { return element == value; });
   };
   auto ranges_find_last_if_not = [](auto first, auto last, auto const& value) {
-    return std::ranges::find_last_if_not(first, last, [&](auto element) {
-      benchmark::DoNotOptimize(element);
-      return element != value;
-    });
+    return std::ranges::find_last_if_not(first, last, [&](auto element) { return element != value; });
   };
 
   // Benchmark ranges::{find_last,find_last_if,find_last_if_not} where the last element
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
index 95d71fad21a34..ab83e538e4996 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
@@ -50,11 +50,7 @@ int main(int argc, char** argv) {
             ValueType init = c.back();
             c.pop_back();
 
-            auto f = [](auto x, auto y) {
-              benchmark::DoNotOptimize(x);
-              benchmark::DoNotOptimize(y);
-              return x + y;
-            };
+            auto f = [](auto x, auto y) { return x + y; };
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(c);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp
index 000117f1aac65..5ae6ca06c6c8c 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp
@@ -27,18 +27,10 @@ int main(int argc, char** argv) {
     return std::is_permutation(first1, last1, first2, last2);
   };
   auto std_is_permutation_3leg_pred = [](auto first1, auto last1, auto first2, auto) {
-    return std::is_permutation(first1, last1, first2, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::is_permutation(first1, last1, first2, [](auto x, auto y) { return x == y; });
   };
   auto std_is_permutation_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::is_permutation(first1, last1, first2, last2, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::is_permutation(first1, last1, first2, last2, [](auto x, auto y) { return x == y; });
   };
 
   auto register_benchmarks = [&](auto bm, std::string comment) {
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp
index 37374ef7aadca..c9411620b20fa 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp
@@ -26,18 +26,10 @@ int main(int argc, char** argv) {
     return std::mismatch(first1, last1, first2, last2);
   };
   auto std_mismatch_3leg_pred = [](auto first1, auto last1, auto first2, auto) {
-    return std::mismatch(first1, last1, first2, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::mismatch(first1, last1, first2, [](auto x, auto y) { return x == y; });
   };
   auto std_mismatch_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::mismatch(first1, last1, first2, last2, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::mismatch(first1, last1, first2, last2, [](auto x, auto y) { return x == y; });
   };
 
   // Benchmark {std,ranges}::mismatch where we find the mismatching element at the very end (worst case).
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
index 3d25105238164..06b3354169cf1 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
@@ -24,11 +24,7 @@ int main(int argc, char** argv) {
     return std::search(first1, last1, first2, last2);
   };
   auto std_search_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::search(first1, last1, first2, last2, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::search(first1, last1, first2, last2, [](auto x, auto y) { return x == y; });
   };
 
   // Benchmark {std,ranges}::search where the needle is never found (worst case).
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
index 212aabfd7cdb3..d00c3cf1e6ea1 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
@@ -24,11 +24,7 @@ int main(int argc, char** argv) {
     return std::search_n(first, last, n, value);
   };
   auto std_search_n_pred = [](auto first, auto last, auto n, auto const& value) {
-    return std::search_n(first, last, n, value, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::search_n(first, last, n, value, [](auto x, auto y) { return x == y; });
   };
 
   // Benchmark {std,ranges}::search_n where the needle almost matches a lot.
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp
index 2950ad5322e84..57747e551dc71 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp
@@ -20,11 +20,7 @@
 
 int main(int argc, char** argv) {
   auto ranges_starts_with_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::ranges::starts_with(first1, last1, first2, last2, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x == y;
-    });
+    return std::ranges::starts_with(first1, last1, first2, last2, [](auto x, auto y) { return x == y; });
   };
 
   // Benchmark ranges::starts_with where we find the mismatching element at the very end (worst case).
diff --git a/libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp
index ac25826acb085..884df63b151f6 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp
@@ -22,11 +22,7 @@
 int main(int argc, char** argv) {
   auto std_is_sorted      = [](auto first, auto last) { return std::is_sorted(first, last); };
   auto std_is_sorted_pred = [](auto first, auto last) {
-    return std::is_sorted(first, last, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x < y;
-    });
+    return std::is_sorted(first, last, [](auto x, auto y) { return x < y; });
   };
 
   // Benchmark {std,ranges}::is_sorted on a sorted sequence (the worst case).
diff --git a/libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp
index 9687416b39c29..05b3a3966f383 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp
@@ -22,11 +22,7 @@
 int main(int argc, char** argv) {
   auto std_is_sorted_until      = [](auto first, auto last) { return std::is_sorted_until(first, last); };
   auto std_is_sorted_until_pred = [](auto first, auto last) {
-    return std::is_sorted_until(first, last, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x < y;
-    });
+    return std::is_sorted_until(first, last, [](auto x, auto y) { return x < y; });
   };
 
   // Benchmark {std,ranges}::is_sorted_until on a sorted sequence (the worst case).


From libcxx-commits at lists.llvm.org  Thu Jul  9 03:15:59 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 03:15:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for std::is_heap
 and std::is_heap_until (PR #207045)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207045@github.com>
Message-ID: <6a4f74df.7270d2fd.2d4879.f41e@mx.google.com>


================
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_is_heap      = [](auto first, auto last) { return std::is_heap(first, last); };
+  auto std_is_heap_pred = [](auto first, auto last) {
+    return std::is_heap(first, last, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
----------------
philnik777 wrote:

Opened #208412.

https://github.com/llvm/llvm-project/pull/207045

From libcxx-commits at lists.llvm.org  Thu Jul  9 03:16:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 03:16:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove benchmark::DoNotOptimize
 from custom predicates in benchmarks (PR #208412)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208412@github.com>
Message-ID: <6a4f74ee.92d2873c.1ab170.f669@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/208412

From libcxx-commits at lists.llvm.org  Thu Jul  9 04:55:57 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 04:55:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Rename forward_list
	constructor tests (PR #208301)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208301@github.com>
Message-ID: <6a4f8c4d.30759430.24d50c.5511@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/208301

From libcxx-commits at lists.llvm.org  Thu Jul  9 04:58:44 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 04:58:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix shared_ptr not accepting
 allocators with explicit conversions (PR #208439)
Message-ID: <llvm.org/llvm/llvm-project/pull/208439@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/208439

Allocators are only required to be explicitly convertible between different types. `shared_ptr` currently requires implicit conversions, however.


>From abff01595e578dea3c941ee60a15508930e08f3e Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 9 Jul 2026 13:57:29 +0200
Subject: [PATCH] [libc++] Fix shared_ptr not accepting allocators with
 explicit conversions

---
 libcxx/include/__memory/shared_ptr.h | 11 ++++++-----
 libcxx/test/support/min_allocator.h  | 12 ++++++------
 2 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index ebd542ba14d01..4112c1175b9e1 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -658,8 +658,9 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> __allocate_shared_impl(const _Alloc& __all
 
 template <class _Tp, class _Alloc, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, _Args&&... __args) {
-  using _ControlBlock = __shared_ptr_emplace<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp> > >;
-  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a, std::forward<_Args>(__args)...);
+  using _CanonicalAlloc = __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp> >;
+  using _ControlBlock   = __shared_ptr_emplace<_Tp, _CanonicalAlloc>;
+  return std::__allocate_shared_impl<_ControlBlock, _Tp>(_CanonicalAlloc(__a), std::forward<_Args>(__args)...);
 }
 
 template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
@@ -671,9 +672,9 @@ template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> =
 
 template <class _Tp, class _Alloc, __enable_if_t<!is_array<_Tp>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
-  using _ControlBlock =
-      __shared_ptr_emplace_for_overwrite<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp>>>;
-  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a);
+  using _CanonicalAlloc = __allocator_traits_rebind_t<_Alloc, remove_cv_t<_Tp>>;
+  using _ControlBlock   = __shared_ptr_emplace_for_overwrite<_Tp, _CanonicalAlloc>;
+  return std::__allocate_shared_impl<_ControlBlock, _Tp>(_CanonicalAlloc(__a));
 }
 
 template <class _Tp, __enable_if_t<!is_array<_Tp>::value, int> = 0>
diff --git a/libcxx/test/support/min_allocator.h b/libcxx/test/support/min_allocator.h
index 6beffc1d2eb10..07603425f0668 100644
--- a/libcxx/test/support/min_allocator.h
+++ b/libcxx/test/support/min_allocator.h
@@ -29,7 +29,7 @@ class bare_allocator {
   bare_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  bare_allocator(bare_allocator<U>) TEST_NOEXCEPT {}
+  explicit bare_allocator(bare_allocator<U>) TEST_NOEXCEPT {}
 
   T* allocate(std::size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
 
@@ -59,7 +59,7 @@ class no_default_allocator {
   typedef T value_type;
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 no_default_allocator(no_default_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 explicit no_default_allocator(no_default_allocator<U>) TEST_NOEXCEPT {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return static_cast<T*>(std::allocator<T>().allocate(n)); }
 
@@ -102,7 +102,7 @@ class malloc_allocator : public malloc_allocator_base {
   malloc_allocator() TEST_NOEXCEPT { assert(!disable_default_constructor); }
 
   template <class U>
-  malloc_allocator(malloc_allocator<U>) TEST_NOEXCEPT {}
+  explicit malloc_allocator(malloc_allocator<U>) TEST_NOEXCEPT {}
 
   T* allocate(std::size_t n) {
     const std::size_t nbytes = n * sizeof(T);
@@ -410,7 +410,7 @@ class complete_type_allocator {
   TEST_CONSTEXPR_CXX20 complete_type_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 complete_type_allocator(complete_type_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 explicit complete_type_allocator(complete_type_allocator<U>) TEST_NOEXCEPT {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return static_cast<T*>(std::allocator<T>().allocate(n)); }
 
@@ -466,7 +466,7 @@ class safe_allocator {
   TEST_CONSTEXPR_CXX20 safe_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 safe_allocator(safe_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 explicit safe_allocator(safe_allocator<U>) TEST_NOEXCEPT {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) {
     T* memory = std::allocator<T>().allocate(n);
@@ -499,7 +499,7 @@ struct tiny_size_allocator {
   tiny_size_allocator() = default;
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 tiny_size_allocator(tiny_size_allocator<MaxSize, U>) {}
+  TEST_CONSTEXPR_CXX20 explicit tiny_size_allocator(tiny_size_allocator<MaxSize, U>) {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) {
     assert(n <= MaxSize);


From libcxx-commits at lists.llvm.org  Thu Jul  9 04:59:32 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 04:59:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix shared_ptr not accepting
 allocators with explicit conversions (PR #208439)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208439@github.com>
Message-ID: <6a4f8d24.d1496011.26913c.dc07@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

Allocators are only required to be explicitly convertible between different types. `shared_ptr` currently requires implicit conversions, however.


---
Full diff: https://github.com/llvm/llvm-project/pull/208439.diff


2 Files Affected:

- (modified) libcxx/include/__memory/shared_ptr.h (+6-5) 
- (modified) libcxx/test/support/min_allocator.h (+6-6) 


``````````diff
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index ebd542ba14d01..4112c1175b9e1 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -658,8 +658,9 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> __allocate_shared_impl(const _Alloc& __all
 
 template <class _Tp, class _Alloc, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, _Args&&... __args) {
-  using _ControlBlock = __shared_ptr_emplace<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp> > >;
-  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a, std::forward<_Args>(__args)...);
+  using _CanonicalAlloc = __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp> >;
+  using _ControlBlock   = __shared_ptr_emplace<_Tp, _CanonicalAlloc>;
+  return std::__allocate_shared_impl<_ControlBlock, _Tp>(_CanonicalAlloc(__a), std::forward<_Args>(__args)...);
 }
 
 template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
@@ -671,9 +672,9 @@ template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> =
 
 template <class _Tp, class _Alloc, __enable_if_t<!is_array<_Tp>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
-  using _ControlBlock =
-      __shared_ptr_emplace_for_overwrite<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp>>>;
-  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a);
+  using _CanonicalAlloc = __allocator_traits_rebind_t<_Alloc, remove_cv_t<_Tp>>;
+  using _ControlBlock   = __shared_ptr_emplace_for_overwrite<_Tp, _CanonicalAlloc>;
+  return std::__allocate_shared_impl<_ControlBlock, _Tp>(_CanonicalAlloc(__a));
 }
 
 template <class _Tp, __enable_if_t<!is_array<_Tp>::value, int> = 0>
diff --git a/libcxx/test/support/min_allocator.h b/libcxx/test/support/min_allocator.h
index 6beffc1d2eb10..07603425f0668 100644
--- a/libcxx/test/support/min_allocator.h
+++ b/libcxx/test/support/min_allocator.h
@@ -29,7 +29,7 @@ class bare_allocator {
   bare_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  bare_allocator(bare_allocator<U>) TEST_NOEXCEPT {}
+  explicit bare_allocator(bare_allocator<U>) TEST_NOEXCEPT {}
 
   T* allocate(std::size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
 
@@ -59,7 +59,7 @@ class no_default_allocator {
   typedef T value_type;
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 no_default_allocator(no_default_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 explicit no_default_allocator(no_default_allocator<U>) TEST_NOEXCEPT {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return static_cast<T*>(std::allocator<T>().allocate(n)); }
 
@@ -102,7 +102,7 @@ class malloc_allocator : public malloc_allocator_base {
   malloc_allocator() TEST_NOEXCEPT { assert(!disable_default_constructor); }
 
   template <class U>
-  malloc_allocator(malloc_allocator<U>) TEST_NOEXCEPT {}
+  explicit malloc_allocator(malloc_allocator<U>) TEST_NOEXCEPT {}
 
   T* allocate(std::size_t n) {
     const std::size_t nbytes = n * sizeof(T);
@@ -410,7 +410,7 @@ class complete_type_allocator {
   TEST_CONSTEXPR_CXX20 complete_type_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 complete_type_allocator(complete_type_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 explicit complete_type_allocator(complete_type_allocator<U>) TEST_NOEXCEPT {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return static_cast<T*>(std::allocator<T>().allocate(n)); }
 
@@ -466,7 +466,7 @@ class safe_allocator {
   TEST_CONSTEXPR_CXX20 safe_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 safe_allocator(safe_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 explicit safe_allocator(safe_allocator<U>) TEST_NOEXCEPT {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) {
     T* memory = std::allocator<T>().allocate(n);
@@ -499,7 +499,7 @@ struct tiny_size_allocator {
   tiny_size_allocator() = default;
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 tiny_size_allocator(tiny_size_allocator<MaxSize, U>) {}
+  TEST_CONSTEXPR_CXX20 explicit tiny_size_allocator(tiny_size_allocator<MaxSize, U>) {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) {
     assert(n <= MaxSize);

``````````

</details>


https://github.com/llvm/llvm-project/pull/208439

From libcxx-commits at lists.llvm.org  Thu Jul  9 04:59:37 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 04:59:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix shared_ptr rebinding
 allocators to incomplete types (PR #206145)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206145@github.com>
Message-ID: <6a4f8d29.f1c1923d.991d1.ea8e@mx.google.com>


================
@@ -410,7 +410,7 @@ class complete_type_allocator {
   TEST_CONSTEXPR_CXX20 complete_type_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 explicit complete_type_allocator(complete_type_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 complete_type_allocator(complete_type_allocator<U>) TEST_NOEXCEPT {}
----------------
philnik777 wrote:

Indeed, I've looked at the wrong line when checking. #208439

https://github.com/llvm/llvm-project/pull/206145

From libcxx-commits at lists.llvm.org  Thu Jul  9 04:59:54 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 04:59:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove
 _LIBCPP_COMPRESSED_PAIR/TRIPLE from shared_ptr (PR #200401)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/200401@github.com>
Message-ID: <6a4f8d3a.7d41f3a1.237e53.7e87@mx.google.com>


philnik777 wrote:

@boomanaiden154 @asmok-g #208439 should fix the issue.

https://github.com/llvm/llvm-project/pull/200401

From libcxx-commits at lists.llvm.org  Thu Jul  9 05:11:23 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 05:11:23 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a4f8feb.eb165e5c.1817a5.6296@mx.google.com>


https://github.com/philnik777 requested changes to this pull request.

This requires an RFC.

https://github.com/llvm/llvm-project/pull/208390

From libcxx-commits at lists.llvm.org  Thu Jul  9 05:54:59 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 05:54:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Replace the of use custom
 sections for detecting overriden functions (PR #208330)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208330@github.com>
Message-ID: <6a4f9a23.7270d2fd.2d4879.039e@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/208330

From libcxx-commits at lists.llvm.org  Thu Jul  9 06:24:38 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 06:24:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove diagnostic about
 _LIBCPP_ABI_NO_ITERATOR_BASES (PR #208460)
Message-ID: <llvm.org/llvm/llvm-project/pull/208460@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/208460

This was slated for removal in LLVM 22 but we hadn't removed it yet.

>From 31af165d75c8d7fcb0f17c43bf4a98f490693eb7 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 9 Jul 2026 09:23:45 -0400
Subject: [PATCH] [libc++] Remove diagnostic about
 _LIBCPP_ABI_NO_ITERATOR_BASES

This was slated for removal in LLVM 22 but we hadn't removed it yet.
---
 libcxx/include/__configuration/abi.h | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 8a34ebcc7bb90..85a524b1af334 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -88,15 +88,6 @@
 #  endif
 #endif
 
-// TODO(LLVM 22): Remove this check
-#if defined(_LIBCPP_ABI_NO_ITERATOR_BASES) && !defined(_LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER)
-#  ifndef _LIBCPP_ONLY_NO_ITERATOR_BASES
-#    error "You probably want to define _LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER. This has been split out from"   \
- " _LIBCPP_ABI_NO_ITERATOR_BASES to allow only removing the second iterator member, since they aren't really related." \
- "If you actually want this ABI configuration, please define _LIBCPP_ONLY_NO_ITERATOR_BASES instead."
-#  endif
-#endif
-
 // We had some bugs where we use [[no_unique_address]] together with construct_at,
 // which causes UB as the call on construct_at could write to overlapping subobjects
 //


From libcxx-commits at lists.llvm.org  Thu Jul  9 06:32:57 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 06:32:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove diagnostic about
 _LIBCPP_ABI_NO_ITERATOR_BASES (PR #208460)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208460@github.com>
Message-ID: <6a4fa309.0c05e68e.1e82f1.0e08@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

This was slated for removal in LLVM 22 but we hadn't removed it yet.

---
Full diff: https://github.com/llvm/llvm-project/pull/208460.diff


1 Files Affected:

- (modified) libcxx/include/__configuration/abi.h (-9) 


``````````diff
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 8a34ebcc7bb90..85a524b1af334 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -88,15 +88,6 @@
 #  endif
 #endif
 
-// TODO(LLVM 22): Remove this check
-#if defined(_LIBCPP_ABI_NO_ITERATOR_BASES) && !defined(_LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER)
-#  ifndef _LIBCPP_ONLY_NO_ITERATOR_BASES
-#    error "You probably want to define _LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER. This has been split out from"   \
- " _LIBCPP_ABI_NO_ITERATOR_BASES to allow only removing the second iterator member, since they aren't really related." \
- "If you actually want this ABI configuration, please define _LIBCPP_ONLY_NO_ITERATOR_BASES instead."
-#  endif
-#endif
-
 // We had some bugs where we use [[no_unique_address]] together with construct_at,
 // which causes UB as the call on construct_at could write to overlapping subobjects
 //

``````````

</details>


https://github.com/llvm/llvm-project/pull/208460

From libcxx-commits at lists.llvm.org  Thu Jul  9 06:37:30 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 06:37:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Fix comment about
	size-based vector (PR #208463)
Message-ID: <llvm.org/llvm/llvm-project/pull/208463@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/208463

The closing #endif comment was using _LIBCPP_ABI_SIZE_BASED_VECTOR, which was the name of the macro in previous iterations of the size-based vector patch. We ended up landing on _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED instead.

>From 698402eafed2ff10c82936683c5c7cac1fd69a22 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 9 Jul 2026 09:36:15 -0400
Subject: [PATCH] [libc++][NFC] Fix comment about size-based vector

The closing #endif comment was using _LIBCPP_ABI_SIZE_BASED_VECTOR, which
was the name of the macro in previous iterations of the size-based vector
patch. We ended up landing on _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED instead.
---
 libcxx/include/__vector/layout.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__vector/layout.h b/libcxx/include/__vector/layout.h
index 61910a42fd708..3318a13a8ede1 100644
--- a/libcxx/include/__vector/layout.h
+++ b/libcxx/include/__vector/layout.h
@@ -459,7 +459,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__invariants()
     return false;
   return __end_ <= __capacity_;
 }
-#endif // _LIBCPP_ABI_SIZE_BASED_VECTOR
+#endif // _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED
 
 _LIBCPP_END_NAMESPACE_STD
 


From libcxx-commits at lists.llvm.org  Thu Jul  9 06:37:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 06:37:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 48aade5 - [libc++][NFC] Fix comment about
 size-based vector (#208463)
Message-ID: <6a4fa42b.c56fb102.7c9cd.fb38@mx.google.com>


Author: Louis Dionne
Date: 2026-07-09T09:37:42-04:00
New Revision: 48aade5e5208414693b695b1dfdb6c95ae56ccf6

URL: https://github.com/llvm/llvm-project/commit/48aade5e5208414693b695b1dfdb6c95ae56ccf6
DIFF: https://github.com/llvm/llvm-project/commit/48aade5e5208414693b695b1dfdb6c95ae56ccf6.diff

LOG: [libc++][NFC] Fix comment about size-based vector (#208463)

The closing #endif comment was using _LIBCPP_ABI_SIZE_BASED_VECTOR,
which was the name of the macro in previous iterations of the size-based
vector patch. We ended up landing on _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED
instead.

Added: 
    

Modified: 
    libcxx/include/__vector/layout.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__vector/layout.h b/libcxx/include/__vector/layout.h
index 61910a42fd708..3318a13a8ede1 100644
--- a/libcxx/include/__vector/layout.h
+++ b/libcxx/include/__vector/layout.h
@@ -459,7 +459,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__invariants()
     return false;
   return __end_ <= __capacity_;
 }
-#endif // _LIBCPP_ABI_SIZE_BASED_VECTOR
+#endif // _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED
 
 _LIBCPP_END_NAMESPACE_STD
 


        

From libcxx-commits at lists.llvm.org  Thu Jul  9 06:37:49 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 06:37:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Fix comment about
	size-based vector (PR #208463)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208463@github.com>
Message-ID: <6a4fa42d.e9d484b9.15e549.9efd@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208463

From libcxx-commits at lists.llvm.org  Thu Jul  9 06:38:19 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 06:38:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Fix comment about
	size-based vector (PR #208463)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208463@github.com>
Message-ID: <6a4fa44b.3cce8983.225732.314e@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

The closing #endif comment was using _LIBCPP_ABI_SIZE_BASED_VECTOR, which was the name of the macro in previous iterations of the size-based vector patch. We ended up landing on _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED instead.

---
Full diff: https://github.com/llvm/llvm-project/pull/208463.diff


1 Files Affected:

- (modified) libcxx/include/__vector/layout.h (+1-1) 


``````````diff
diff --git a/libcxx/include/__vector/layout.h b/libcxx/include/__vector/layout.h
index 61910a42fd708..3318a13a8ede1 100644
--- a/libcxx/include/__vector/layout.h
+++ b/libcxx/include/__vector/layout.h
@@ -459,7 +459,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__invariants()
     return false;
   return __end_ <= __capacity_;
 }
-#endif // _LIBCPP_ABI_SIZE_BASED_VECTOR
+#endif // _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED
 
 _LIBCPP_END_NAMESPACE_STD
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/208463

From libcxx-commits at lists.llvm.org  Thu Jul  9 06:39:36 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 06:39:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] Fix redefinition of wchar_t
 overloads on AIX (PR #201432)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201432@github.com>
Message-ID: <6a4fa498.bd93e731.358318.5395@mx.google.com>


================
@@ -139,6 +139,8 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 
 #    if _LIBCPP_HAS_WIDE_CHARACTERS
 #      if defined(__cplusplus) && !defined(_LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS) && defined(_LIBCPP_PREFERRED_OVERLOAD)
+#        if defined(_AIX) && !defined(_LIBCPP_WCHAR_H_OVERLOADS)
----------------
Himadhith wrote:

Oh, then I'll just define this just for AIX and make this a 2 step process. 

https://github.com/llvm/llvm-project/pull/201432

From libcxx-commits at lists.llvm.org  Thu Jul  9 07:58:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 07:58:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] Fix redefinition of wchar_t
 overloads on AIX (PR #201432)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201432@github.com>
Message-ID: <6a4fb712.f1c1923d.991d1.d822@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/201432

>From ba9eb996768f01ef13bf3e6aa2a2b25b02eacc55 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 3 Jun 2026 14:48:19 -0400
Subject: [PATCH 1/3] [AIX][libc++] Fix redefinition of wchar_t overloads on
 AIX

---
 libcxx/include/wchar.h                                    | 6 ++++--
 libcxx/test/extensions/clang/clang_modules_include.gen.py | 2 --
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index 7ff03870bdfdb..0913c4eed937c 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -139,6 +139,7 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 
 #    if _LIBCPP_HAS_WIDE_CHARACTERS
 #      if defined(__cplusplus) && !defined(_LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS) && defined(_LIBCPP_PREFERRED_OVERLOAD)
+#        if defined(_AIX) && !defined(_LIBCPP_WCHAR_H_OVERLOADS)
 extern "C++" {
 inline _LIBCPP_HIDE_FROM_ABI wchar_t* __libcpp_wcschr(const wchar_t* __s, wchar_t __c) {
   return (wchar_t*)wcschr(__s, __c);
@@ -193,15 +194,16 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD wchar_t* wmemchr(wchar_t
   return __libcpp_wmemchr(__s, __c, __n);
 }
 }
-#      endif
+#        endif
 
-#      if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
+#        if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
 extern "C" {
 size_t mbsnrtowcs(
     wchar_t* __restrict __dst, const char** __restrict __src, size_t __nmc, size_t __len, mbstate_t* __restrict __ps);
 size_t wcsnrtombs(
     char* __restrict __dst, const wchar_t** __restrict __src, size_t __nwc, size_t __len, mbstate_t* __restrict __ps);
 } // extern "C"
+#        endif // _LIBCPP_WCHAR_H_OVERLOADS
 #      endif // __cplusplus && (_LIBCPP_MSVCRT || __MVS__)
 #    endif   // _LIBCPP_HAS_WIDE_CHARACTERS
 #  endif     // _LIBCPP_WCHAR_H
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..0ce7f73df1ee1 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -30,8 +30,6 @@
 # TODO: Investigate why this doesn't work on Picolibc once the locale base API is refactored
 # UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
 
-# TODO: Fix seemingly circular inclusion or <wchar.h> on AIX
-# UNSUPPORTED: LIBCXX-AIX-FIXME
 
 # UNSUPPORTED: FROZEN-CXX03-HEADERS-FIXME
 

>From 5e152f1b80f908008e4024d2bbf9fff018218dc0 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 4 Jun 2026 01:16:00 -0400
Subject: [PATCH 2/3] fix format and add define

---
 libcxx/include/wchar.h | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index 0913c4eed937c..6f15625cd2fb5 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -140,6 +140,7 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 #    if _LIBCPP_HAS_WIDE_CHARACTERS
 #      if defined(__cplusplus) && !defined(_LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS) && defined(_LIBCPP_PREFERRED_OVERLOAD)
 #        if defined(_AIX) && !defined(_LIBCPP_WCHAR_H_OVERLOADS)
+#          define _LIBCPP_WCHAR_H_OVERLOADS
 extern "C++" {
 inline _LIBCPP_HIDE_FROM_ABI wchar_t* __libcpp_wcschr(const wchar_t* __s, wchar_t __c) {
   return (wchar_t*)wcschr(__s, __c);
@@ -194,16 +195,16 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD wchar_t* wmemchr(wchar_t
   return __libcpp_wmemchr(__s, __c, __n);
 }
 }
-#        endif
+#        endif // _LIBCPP_WCHAR_H_OVERLOADS
+#      endif
 
-#        if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
+#      if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
 extern "C" {
 size_t mbsnrtowcs(
     wchar_t* __restrict __dst, const char** __restrict __src, size_t __nmc, size_t __len, mbstate_t* __restrict __ps);
 size_t wcsnrtombs(
     char* __restrict __dst, const wchar_t** __restrict __src, size_t __nwc, size_t __len, mbstate_t* __restrict __ps);
 } // extern "C"
-#        endif // _LIBCPP_WCHAR_H_OVERLOADS
 #      endif // __cplusplus && (_LIBCPP_MSVCRT || __MVS__)
 #    endif   // _LIBCPP_HAS_WIDE_CHARACTERS
 #  endif     // _LIBCPP_WCHAR_H

>From 96a9d56c6511e645d4893c6c6417804dec245523 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 9 Jul 2026 10:57:40 -0400
Subject: [PATCH 3/3] remove defined AIX check

---
 libcxx/include/wchar.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index 6f15625cd2fb5..b9cb10ab41f01 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -139,7 +139,7 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 
 #    if _LIBCPP_HAS_WIDE_CHARACTERS
 #      if defined(__cplusplus) && !defined(_LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS) && defined(_LIBCPP_PREFERRED_OVERLOAD)
-#        if defined(_AIX) && !defined(_LIBCPP_WCHAR_H_OVERLOADS)
+#        ifndef _LIBCPP_WCHAR_H_OVERLOADS
 #          define _LIBCPP_WCHAR_H_OVERLOADS
 extern "C++" {
 inline _LIBCPP_HIDE_FROM_ABI wchar_t* __libcpp_wcschr(const wchar_t* __s, wchar_t __c) {


From libcxx-commits at lists.llvm.org  Thu Jul  9 08:25:14 2026
From: libcxx-commits at lists.llvm.org (Avi Patel via libcxx-commits)
Date: Thu, 09 Jul 2026 08:25:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG4472:
 std::atomic_ref<const T> can be constructed from temporaries (PR #208131)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208131@github.com>
Message-ID: <6a4fbd5a.510f576d.1b3cb9.52b8@mx.google.com>


firedog1234 wrote:

Thank you!

https://github.com/llvm/llvm-project/pull/208131

From libcxx-commits at lists.llvm.org  Thu Jul  9 08:49:50 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 08:49:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Merge atomic-wait contention
 tables into a single table (PR #208493)
Message-ID: <llvm.org/llvm/llvm-project/pull/208493@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/208493

PR #161086 (73a13839d3ec) introduced a new contention table for atomics with a native size. However, this also increases the size of the dylib and the footprint of processes using the dylib by a non-negligible margin.

To mitigate this issue, this change folds the two tables into a single one. The native waiter count can be stored for free inside padding that was previously unused.

rdar://179161875

>From a78702d93c6903e4e32f1e5f2faecb0faa3582cf Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 8 Jul 2026 14:51:23 -0400
Subject: [PATCH] [libc++] Merge atomic-wait contention tables into a single
 table

PR #161086 (73a13839d3ec) introduced a new contention table for atomics
with a native size. However, this also increases the size of the dylib
and the footprint of processes using the dylib by a non-negligible margin.

To mitigate this issue, this change folds the two tables into a single
one. The native waiter count can be stored for free inside padding that
was previously unused.

rdar://179161875
---
 libcxx/src/atomic.cpp | 80 +++++++++++++++++++++----------------------
 1 file changed, 39 insertions(+), 41 deletions(-)

diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 53aec09dfc2a0..25c146d5d51f7 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -414,45 +414,41 @@ static constexpr size_t __contention_table_size = (1 << 8); /* < there's no magi
 
 static constexpr hash<void const*> __contention_hasher;
 
-// Waiter count table for all atomics with the correct size that use itself as the wait/notify address.
-
-struct alignas(
-    std::hardware_constructive_interference_size) /*  aim to avoid false sharing */ __contention_state_native {
-  __cxx_atomic_contention_t __waiter_count;
-  constexpr __contention_state_native() : __waiter_count(0) {}
-};
-
-static constinit __contention_state_native __contention_table_native[__contention_table_size];
-
-static __cxx_atomic_contention_t* __get_native_waiter_count(void const* p) {
-  return &__contention_table_native[__contention_hasher(p) & (__contention_table_size - 1)].__waiter_count;
-}
-
-// Global contention table for all atomics with the wrong size that use the global table's atomic as wait/notify
-// address.
-
-struct alignas(
-    std::hardware_constructive_interference_size) /*  aim to avoid false sharing */ __contention_state_global {
-  __cxx_atomic_contention_t __waiter_count;
+// Unified contention table shared by both the native path (atomics whose size matches the platform
+// wait size and are waited on directly) and the global path (atomics with the wrong size, which use
+// the global table's atomic as the wait/notify address). The native path only reads/writes the native
+// waiter count, and the global path uses the global waiter count and the platform state.
+//
+// We use a single table for both the native and non-native states to minimize the size impact, since
+// we would have plenty of unused padding otherwise. This technically means that if both a native and
+// a non-native atomic fall into the same bucket and are reading/writing waiter counts on the same
+// cache line, we would end up in a "false-sharing" situation. However, in practice, it would require
+// both native and non-native atomics to be used concurrently, and to fall in the same bucket, which
+// is expected to be unlikely.
+struct alignas(std::hardware_constructive_interference_size) /*  aim to avoid false sharing */ __contention_state {
+  __cxx_atomic_contention_t __waiter_count_native;
+  __cxx_atomic_contention_t __waiter_count_global;
   __cxx_atomic_contention_t __platform_state;
-  constexpr __contention_state_global() : __waiter_count(0), __platform_state(0) {}
+  constexpr __contention_state() : __waiter_count_native(0), __waiter_count_global(0), __platform_state(0) {}
 };
 
-static constinit __contention_state_global __contention_table_global[__contention_table_size];
+static constinit __contention_state __contention_table[__contention_table_size];
 
-static __contention_state_global* __get_global_contention_state(void const* p) {
-  return &__contention_table_global[__contention_hasher(p) & (__contention_table_size - 1)];
+static __contention_state* __get_contention_state(void const* p) {
+  return &__contention_table[__contention_hasher(p) & (__contention_table_size - 1)];
 }
 
 /* When the incoming atomic is the wrong size for the platform wait size, need to
    launder the value sequence through an atomic from our table. */
 
 static void __atomic_notify_global_table(void const* __location) {
-  auto const __entry = __get_global_contention_state(__location);
+  auto const __entry = __get_contention_state(__location);
   // The value sequence laundering happens on the next line below.
   __cxx_atomic_fetch_add(&__entry->__platform_state, __cxx_contention_t(1), memory_order_seq_cst);
   __contention_notify<sizeof(__cxx_atomic_contention_t)>(
-      &__entry->__waiter_count, &__entry->__platform_state, false /* when laundering, we can't handle notify_one */);
+      &__entry->__waiter_count_global,
+      &__entry->__platform_state,
+      false /* when laundering, we can't handle notify_one */);
 }
 
 // =============================
@@ -461,22 +457,22 @@ static void __atomic_notify_global_table(void const* __location) {
 
 // global
 _LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __atomic_monitor_global(void const* __location) noexcept {
-  auto const __entry = __get_global_contention_state(__location);
+  auto const __entry = __get_contention_state(__location);
   return __cxx_atomic_load(&__entry->__platform_state, memory_order_acquire);
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void
 __atomic_wait_global_table(void const* __location, __cxx_contention_t __old_value) noexcept {
-  auto const __entry = __get_global_contention_state(__location);
+  auto const __entry = __get_contention_state(__location);
   __contention_wait<sizeof(__cxx_atomic_contention_t)>(
-      &__entry->__waiter_count, &__entry->__platform_state, &__old_value, NoTimeout{});
+      &__entry->__waiter_count_global, &__entry->__platform_state, &__old_value, NoTimeout{});
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void __atomic_wait_global_table_with_timeout(
     void const* __location, __cxx_contention_t __old_value, uint64_t __timeout_ns) _NOEXCEPT {
-  auto const __entry = __get_global_contention_state(__location);
+  auto const __entry = __get_contention_state(__location);
   __contention_wait<sizeof(__cxx_atomic_contention_t)>(
-      &__entry->__waiter_count, &__entry->__platform_state, &__old_value, __timeout_ns);
+      &__entry->__waiter_count_global, &__entry->__platform_state, &__old_value, __timeout_ns);
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_one_global_table(void const* __location) noexcept {
@@ -491,23 +487,25 @@ _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_all_global_table(void const* __lo
 
 template <std::size_t _Size>
 _LIBCPP_EXPORTED_FROM_ABI void __atomic_wait_native(void const* __address, void const* __old_value) noexcept {
-  __contention_wait<_Size>(__get_native_waiter_count(__address), __address, __old_value, NoTimeout{});
+  __contention_wait<_Size>(
+      &__get_contention_state(__address)->__waiter_count_native, __address, __old_value, NoTimeout{});
 }
 
 template <std::size_t _Size>
 _LIBCPP_EXPORTED_FROM_ABI void
 __atomic_wait_native_with_timeout(void const* __address, void const* __old_value, uint64_t __timeout_ns) noexcept {
-  __contention_wait<_Size>(__get_native_waiter_count(__address), __address, __old_value, __timeout_ns);
+  __contention_wait<_Size>(
+      &__get_contention_state(__address)->__waiter_count_native, __address, __old_value, __timeout_ns);
 }
 
 template <std::size_t _Size>
 _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_one_native(void const* __location) noexcept {
-  __contention_notify<_Size>(__get_native_waiter_count(__location), __location, true);
+  __contention_notify<_Size>(&__get_contention_state(__location)->__waiter_count_native, __location, true);
 }
 
 template <std::size_t _Size>
 _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_all_native(void const* __location) noexcept {
-  __contention_notify<_Size>(__get_native_waiter_count(__location), __location, false);
+  __contention_notify<_Size>(&__get_contention_state(__location)->__waiter_count_native, __location, false);
 }
 
 // ==================================================
@@ -558,34 +556,34 @@ _LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(void const volatile* __lo
 }
 
 _LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __libcpp_atomic_monitor(void const volatile* __location) noexcept {
-  auto const __entry = __get_global_contention_state(const_cast<void const*>(__location));
+  auto const __entry = __get_contention_state(const_cast<void const*>(__location));
   return __cxx_atomic_load(&__entry->__platform_state, memory_order_acquire);
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void
 __libcpp_atomic_wait(void const volatile* __location, __cxx_contention_t __old_value) noexcept {
-  auto const __entry = __get_global_contention_state(const_cast<void const*>(__location));
+  auto const __entry = __get_contention_state(const_cast<void const*>(__location));
   __contention_wait<sizeof(__cxx_atomic_contention_t)>(
-      &__entry->__waiter_count, &__entry->__platform_state, &__old_value, NoTimeout{});
+      &__entry->__waiter_count_global, &__entry->__platform_state, &__old_value, NoTimeout{});
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location) noexcept {
   auto __location_cast = const_cast<const void*>(static_cast<const volatile void*>(__location));
   __contention_notify<sizeof(__cxx_atomic_contention_t)>(
-      __get_native_waiter_count(__location_cast), __location_cast, true);
+      &__get_contention_state(__location_cast)->__waiter_count_native, __location_cast, true);
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile* __location) noexcept {
   auto __location_cast = const_cast<const void*>(static_cast<const volatile void*>(__location));
   __contention_notify<sizeof(__cxx_atomic_contention_t)>(
-      __get_native_waiter_count(__location_cast), __location_cast, false);
+      &__get_contention_state(__location_cast)->__waiter_count_native, __location_cast, false);
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void
 __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile* __location, __cxx_contention_t __old_value) noexcept {
   auto __location_cast = const_cast<const void*>(static_cast<const volatile void*>(__location));
   __contention_wait<sizeof(__cxx_atomic_contention_t)>(
-      __get_native_waiter_count(__location_cast), __location_cast, &__old_value, NoTimeout{});
+      &__get_contention_state(__location_cast)->__waiter_count_native, __location_cast, &__old_value, NoTimeout{});
 }
 
 // this function is even unused in the old ABI


From libcxx-commits at lists.llvm.org  Thu Jul  9 08:50:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 08:50:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Merge atomic-wait contention
 tables into a single table (PR #208493)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208493@github.com>
Message-ID: <6a4fc344.eb24c6ea.27aedc.67fa@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

PR #<!-- -->161086 (73a13839d3ec) introduced a new contention table for atomics with a native size. However, this also increases the size of the dylib and the footprint of processes using the dylib by a non-negligible margin.

To mitigate this issue, this change folds the two tables into a single one. The native waiter count can be stored for free inside padding that was previously unused.

rdar://179161875

---
Full diff: https://github.com/llvm/llvm-project/pull/208493.diff


1 Files Affected:

- (modified) libcxx/src/atomic.cpp (+39-41) 


``````````diff
diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 53aec09dfc2a0..25c146d5d51f7 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -414,45 +414,41 @@ static constexpr size_t __contention_table_size = (1 << 8); /* < there's no magi
 
 static constexpr hash<void const*> __contention_hasher;
 
-// Waiter count table for all atomics with the correct size that use itself as the wait/notify address.
-
-struct alignas(
-    std::hardware_constructive_interference_size) /*  aim to avoid false sharing */ __contention_state_native {
-  __cxx_atomic_contention_t __waiter_count;
-  constexpr __contention_state_native() : __waiter_count(0) {}
-};
-
-static constinit __contention_state_native __contention_table_native[__contention_table_size];
-
-static __cxx_atomic_contention_t* __get_native_waiter_count(void const* p) {
-  return &__contention_table_native[__contention_hasher(p) & (__contention_table_size - 1)].__waiter_count;
-}
-
-// Global contention table for all atomics with the wrong size that use the global table's atomic as wait/notify
-// address.
-
-struct alignas(
-    std::hardware_constructive_interference_size) /*  aim to avoid false sharing */ __contention_state_global {
-  __cxx_atomic_contention_t __waiter_count;
+// Unified contention table shared by both the native path (atomics whose size matches the platform
+// wait size and are waited on directly) and the global path (atomics with the wrong size, which use
+// the global table's atomic as the wait/notify address). The native path only reads/writes the native
+// waiter count, and the global path uses the global waiter count and the platform state.
+//
+// We use a single table for both the native and non-native states to minimize the size impact, since
+// we would have plenty of unused padding otherwise. This technically means that if both a native and
+// a non-native atomic fall into the same bucket and are reading/writing waiter counts on the same
+// cache line, we would end up in a "false-sharing" situation. However, in practice, it would require
+// both native and non-native atomics to be used concurrently, and to fall in the same bucket, which
+// is expected to be unlikely.
+struct alignas(std::hardware_constructive_interference_size) /*  aim to avoid false sharing */ __contention_state {
+  __cxx_atomic_contention_t __waiter_count_native;
+  __cxx_atomic_contention_t __waiter_count_global;
   __cxx_atomic_contention_t __platform_state;
-  constexpr __contention_state_global() : __waiter_count(0), __platform_state(0) {}
+  constexpr __contention_state() : __waiter_count_native(0), __waiter_count_global(0), __platform_state(0) {}
 };
 
-static constinit __contention_state_global __contention_table_global[__contention_table_size];
+static constinit __contention_state __contention_table[__contention_table_size];
 
-static __contention_state_global* __get_global_contention_state(void const* p) {
-  return &__contention_table_global[__contention_hasher(p) & (__contention_table_size - 1)];
+static __contention_state* __get_contention_state(void const* p) {
+  return &__contention_table[__contention_hasher(p) & (__contention_table_size - 1)];
 }
 
 /* When the incoming atomic is the wrong size for the platform wait size, need to
    launder the value sequence through an atomic from our table. */
 
 static void __atomic_notify_global_table(void const* __location) {
-  auto const __entry = __get_global_contention_state(__location);
+  auto const __entry = __get_contention_state(__location);
   // The value sequence laundering happens on the next line below.
   __cxx_atomic_fetch_add(&__entry->__platform_state, __cxx_contention_t(1), memory_order_seq_cst);
   __contention_notify<sizeof(__cxx_atomic_contention_t)>(
-      &__entry->__waiter_count, &__entry->__platform_state, false /* when laundering, we can't handle notify_one */);
+      &__entry->__waiter_count_global,
+      &__entry->__platform_state,
+      false /* when laundering, we can't handle notify_one */);
 }
 
 // =============================
@@ -461,22 +457,22 @@ static void __atomic_notify_global_table(void const* __location) {
 
 // global
 _LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __atomic_monitor_global(void const* __location) noexcept {
-  auto const __entry = __get_global_contention_state(__location);
+  auto const __entry = __get_contention_state(__location);
   return __cxx_atomic_load(&__entry->__platform_state, memory_order_acquire);
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void
 __atomic_wait_global_table(void const* __location, __cxx_contention_t __old_value) noexcept {
-  auto const __entry = __get_global_contention_state(__location);
+  auto const __entry = __get_contention_state(__location);
   __contention_wait<sizeof(__cxx_atomic_contention_t)>(
-      &__entry->__waiter_count, &__entry->__platform_state, &__old_value, NoTimeout{});
+      &__entry->__waiter_count_global, &__entry->__platform_state, &__old_value, NoTimeout{});
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void __atomic_wait_global_table_with_timeout(
     void const* __location, __cxx_contention_t __old_value, uint64_t __timeout_ns) _NOEXCEPT {
-  auto const __entry = __get_global_contention_state(__location);
+  auto const __entry = __get_contention_state(__location);
   __contention_wait<sizeof(__cxx_atomic_contention_t)>(
-      &__entry->__waiter_count, &__entry->__platform_state, &__old_value, __timeout_ns);
+      &__entry->__waiter_count_global, &__entry->__platform_state, &__old_value, __timeout_ns);
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_one_global_table(void const* __location) noexcept {
@@ -491,23 +487,25 @@ _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_all_global_table(void const* __lo
 
 template <std::size_t _Size>
 _LIBCPP_EXPORTED_FROM_ABI void __atomic_wait_native(void const* __address, void const* __old_value) noexcept {
-  __contention_wait<_Size>(__get_native_waiter_count(__address), __address, __old_value, NoTimeout{});
+  __contention_wait<_Size>(
+      &__get_contention_state(__address)->__waiter_count_native, __address, __old_value, NoTimeout{});
 }
 
 template <std::size_t _Size>
 _LIBCPP_EXPORTED_FROM_ABI void
 __atomic_wait_native_with_timeout(void const* __address, void const* __old_value, uint64_t __timeout_ns) noexcept {
-  __contention_wait<_Size>(__get_native_waiter_count(__address), __address, __old_value, __timeout_ns);
+  __contention_wait<_Size>(
+      &__get_contention_state(__address)->__waiter_count_native, __address, __old_value, __timeout_ns);
 }
 
 template <std::size_t _Size>
 _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_one_native(void const* __location) noexcept {
-  __contention_notify<_Size>(__get_native_waiter_count(__location), __location, true);
+  __contention_notify<_Size>(&__get_contention_state(__location)->__waiter_count_native, __location, true);
 }
 
 template <std::size_t _Size>
 _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_all_native(void const* __location) noexcept {
-  __contention_notify<_Size>(__get_native_waiter_count(__location), __location, false);
+  __contention_notify<_Size>(&__get_contention_state(__location)->__waiter_count_native, __location, false);
 }
 
 // ==================================================
@@ -558,34 +556,34 @@ _LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(void const volatile* __lo
 }
 
 _LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __libcpp_atomic_monitor(void const volatile* __location) noexcept {
-  auto const __entry = __get_global_contention_state(const_cast<void const*>(__location));
+  auto const __entry = __get_contention_state(const_cast<void const*>(__location));
   return __cxx_atomic_load(&__entry->__platform_state, memory_order_acquire);
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void
 __libcpp_atomic_wait(void const volatile* __location, __cxx_contention_t __old_value) noexcept {
-  auto const __entry = __get_global_contention_state(const_cast<void const*>(__location));
+  auto const __entry = __get_contention_state(const_cast<void const*>(__location));
   __contention_wait<sizeof(__cxx_atomic_contention_t)>(
-      &__entry->__waiter_count, &__entry->__platform_state, &__old_value, NoTimeout{});
+      &__entry->__waiter_count_global, &__entry->__platform_state, &__old_value, NoTimeout{});
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location) noexcept {
   auto __location_cast = const_cast<const void*>(static_cast<const volatile void*>(__location));
   __contention_notify<sizeof(__cxx_atomic_contention_t)>(
-      __get_native_waiter_count(__location_cast), __location_cast, true);
+      &__get_contention_state(__location_cast)->__waiter_count_native, __location_cast, true);
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile* __location) noexcept {
   auto __location_cast = const_cast<const void*>(static_cast<const volatile void*>(__location));
   __contention_notify<sizeof(__cxx_atomic_contention_t)>(
-      __get_native_waiter_count(__location_cast), __location_cast, false);
+      &__get_contention_state(__location_cast)->__waiter_count_native, __location_cast, false);
 }
 
 _LIBCPP_EXPORTED_FROM_ABI void
 __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile* __location, __cxx_contention_t __old_value) noexcept {
   auto __location_cast = const_cast<const void*>(static_cast<const volatile void*>(__location));
   __contention_wait<sizeof(__cxx_atomic_contention_t)>(
-      __get_native_waiter_count(__location_cast), __location_cast, &__old_value, NoTimeout{});
+      &__get_contention_state(__location_cast)->__waiter_count_native, __location_cast, &__old_value, NoTimeout{});
 }
 
 // this function is even unused in the old ABI

``````````

</details>


https://github.com/llvm/llvm-project/pull/208493

From libcxx-commits at lists.llvm.org  Thu Jul  9 08:58:59 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 08:58:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] d82a0d2 - [libc++] Add missing <string>
	include (#208281)
Message-ID: <6a4fc543.abb10101.3d0bf8.143f@mx.google.com>


Author: Louis Dionne
Date: 2026-07-09T11:58:54-04:00
New Revision: d82a0d29e1bb85304e54767763ef5c036cd5bc5b

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

LOG: [libc++] Add missing <string> include (#208281)

Fixes #182390

Added: 
    

Modified: 
    libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
index 45b034833fecc..37b4a48912e09 100644
--- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
@@ -16,10 +16,11 @@
 // XFAIL: FROZEN-CXX03-HEADERS-FIXME
 
 #include <locale>
-#include <ios>
 #include <cassert>
+#include <ios>
 #include <limits>
 #include <streambuf>
+#include <string>
 #include "test_macros.h"
 #include "test_iterators.h"
 


        

From libcxx-commits at lists.llvm.org  Thu Jul  9 08:59:02 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 08:59:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add missing <string> include (PR
	#208281)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208281@github.com>
Message-ID: <6a4fc546.70920dee.290dca.e1c6@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208281

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:02:10 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 09:02:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Update the label for FreeBSD in
 CI to version 15.1 (PR #205417)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205417@github.com>
Message-ID: <6a4fc602.13be6df8.145c.7680@mx.google.com>


ldionne wrote:

Yeah, I can see 15.1 as the target in the logs. I think this is OK.

https://github.com/llvm/llvm-project/pull/205417

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:02:21 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 09:02:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Update the label for FreeBSD in
 CI to version 15.1 (PR #205417)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205417@github.com>
Message-ID: <6a4fc60d.bb839515.27d21e.960e@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/205417

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:02:42 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 09:02:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] b45d681 - [libc++] Update the label for
 FreeBSD in CI to version 15.1 (#205417)
Message-ID: <6a4fc622.af6ba3ed.36d9db.0723@mx.google.com>


Author: aokblast
Date: 2026-07-09T12:02:37-04:00
New Revision: b45d68108a585507789aa26b9e2808d68386dfca

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

LOG: [libc++] Update the label for FreeBSD in CI to version 15.1 (#205417)

The FreeBSD libc++ CI has been bumped to the new FreeBSD release. The
remaining task to do is to bump the description in CI yaml file.

Added: 
    

Modified: 
    libcxx/utils/ci/buildkite-pipeline.yml

Removed: 
    


################################################################################
diff  --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index d4107cf11ad51..ed1b4b5f57be1 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -141,7 +141,7 @@ steps:
 
 - group: ':freebsd: FreeBSD'
   steps:
-  - label: FreeBSD 13 amd64
+  - label: FreeBSD 15.1 amd64
     command: libcxx/utils/ci/run-buildbot generic-cxx26
     env:
       CC: clang21


        

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:02:44 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 09:02:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Update the label for FreeBSD in
 CI to version 15.1 (PR #205417)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205417@github.com>
Message-ID: <6a4fc624.e9d484b9.15e549.b743@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/205417

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:14:21 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 09:14:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Merge atomic-wait contention
 tables into a single table (PR #208493)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208493@github.com>
Message-ID: <6a4fc8dd.27b3b003.3b871.1d3a@mx.google.com>


ldionne wrote:

Important related issues:
- https://github.com/llvm/llvm-project/issues/208305
- https://github.com/llvm/llvm-project/issues/182951

https://github.com/llvm/llvm-project/pull/208493

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:46:40 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 09:46:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] e2a8a74 - [libc++][NFC] Rename
 forward_list constructor tests (#208301)
Message-ID: <6a4fd070.481afadf.196d57.3a5f@mx.google.com>


Author: Louis Dionne
Date: 2026-07-09T12:46:35-04:00
New Revision: e2a8a741f75304951ea27adeb1da3db8272998fe

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

LOG: [libc++][NFC] Rename forward_list constructor tests (#208301)

The forward_list tests historically used "range" to mean an iterator
pair, but we use `iter_iter` pretty consistently elsewhere in the test
suite. Using `range` also makes it confusing with the actual tests for
`_range` operations, like `append_range` or `assign_range`.

Added: 
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_iter_iter.pass.cpp
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/ctor_iter_iter.pass.cpp
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/ctor_iter_iter_alloc.pass.cpp
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_after_iter_iter.pass.cpp

Modified: 
    

Removed: 
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_range.pass.cpp
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/range.pass.cpp
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/range_alloc.pass.cpp
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_after_range.pass.cpp


################################################################################
diff  --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_range.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_iter_iter.pass.cpp
similarity index 100%
rename from libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_range.pass.cpp
rename to libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/assign_iter_iter.pass.cpp

diff  --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/range.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/ctor_iter_iter.pass.cpp
similarity index 100%
rename from libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/range.pass.cpp
rename to libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/ctor_iter_iter.pass.cpp

diff  --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/range_alloc.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/ctor_iter_iter_alloc.pass.cpp
similarity index 100%
rename from libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/range_alloc.pass.cpp
rename to libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/ctor_iter_iter_alloc.pass.cpp

diff  --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_after_range.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_after_iter_iter.pass.cpp
similarity index 100%
rename from libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_after_range.pass.cpp
rename to libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/insert_after_iter_iter.pass.cpp


        

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:46:42 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 09:46:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Rename forward_list
	constructor tests (PR #208301)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208301@github.com>
Message-ID: <6a4fd072.da8bafd3.14cd50.981d@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208301

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:47:15 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 09:47:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] c7cecd8 - [libc++] Fix assign_range test
	for forward_list (#208283)
Message-ID: <6a4fd093.c56fb102.7c9cd.2798@mx.google.com>


Author: Louis Dionne
Date: 2026-07-09T12:47:10-04:00
New Revision: c7cecd8351ca1d50fca0f8526c7701e8de3fe23a

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

LOG: [libc++] Fix assign_range test for forward_list (#208283)

That test was checking prepend_range instead of assign_range due to a
copy-paste error.

Fixes #74536

Added: 
    

Modified: 
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp
index 9a3adec1d9756..61500b58757cb 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/assign_range.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 // template<container-compatible-range<T> R>
-//   constexpr void prepend_range(R&& rg); // C++23; constexpr since C++26
+//   constexpr void assign_range(R&& rg); // C++23; constexpr since C++26
 
 #include <forward_list>
 
@@ -17,23 +17,23 @@
 #include "test_macros.h"
 
 // Tested cases:
-// - 
diff erent kinds of insertions (prepending an {empty/one-element/mid-sized/long range} into an
+// - 
diff erent kinds of assignments (assigning an {empty/one-element/mid-sized/long range} to an
 //   {empty/one-element/full} container);
-// - prepending move-only elements;
+// - assigning move-only elements;
 // - an exception is thrown when copying the elements or when allocating new elements.
 TEST_CONSTEXPR_CXX26 bool test() {
   static_assert(test_constraints_assign_range<std::forward_list, int, double>());
 
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
-    test_sequence_prepend_range<std::forward_list<int, Alloc>, Iter, Sent>([](auto&&) {
+    test_sequence_assign_range<std::forward_list<int, Alloc>, Iter, Sent>([](auto&&) {
       // No additional validation to do.
     });
   });
-  test_sequence_prepend_range_move_only<std::forward_list>();
+  test_sequence_assign_range_move_only<std::forward_list>();
 
   if (!TEST_IS_CONSTANT_EVALUATED) {
-    test_prepend_range_exception_safety_throwing_copy<std::forward_list>();
-    test_prepend_range_exception_safety_throwing_allocator<std::forward_list, int>();
+    test_assign_range_exception_safety_throwing_copy<std::forward_list>();
+    test_assign_range_exception_safety_throwing_allocator<std::forward_list, int>();
   }
 
   return true;


        

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:47:17 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 09:47:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix assign_range test for
	forward_list (PR #208283)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208283@github.com>
Message-ID: <6a4fd095.743400b0.2067c9.8dd9@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208283

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:48:37 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 09:48:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add tests for LWG3949 and mark
	it as done (PR #208272)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208272@github.com>
Message-ID: <6a4fd0e5.cb30c3e4.3ce559.5c17@mx.google.com>


ldionne wrote:

> I'm somewhat on the fence whether this is complete or nothing to do, but given that it seems it was required before and accidentally deleted nothing to do seems fine.

Yeah, I'm on the fence too. Since the LWG issue is basically fixing wording that was broken by a previous change, I think we're closer to Nothing To Do, but really both would be applicable.

https://github.com/llvm/llvm-project/pull/208272

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:48:38 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 09:48:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 2a9f19f - [libc++] Add tests for LWG3949
 and mark it as done (#208272)
Message-ID: <6a4fd0e6.491d90f5.2facd.2951@mx.google.com>


Author: Louis Dionne
Date: 2026-07-09T12:48:33-04:00
New Revision: 2a9f19fb5240aa85c4225a5b5737c0eafe94b7ca

URL: https://github.com/llvm/llvm-project/commit/2a9f19fb5240aa85c4225a5b5737c0eafe94b7ca
DIFF: https://github.com/llvm/llvm-project/commit/2a9f19fb5240aa85c4225a5b5737c0eafe94b7ca.diff

LOG: [libc++] Add tests for LWG3949 and mark it as done (#208272)

Closes #105301

Added: 
    

Modified: 
    libcxx/docs/Status/Cxx26Issues.csv
    libcxx/test/std/atomics/atomics.types.generic/standard_layout.compile.pass.cpp
    libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/dtor.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 2122fcd05dfb4..c74eff436b96d 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -28,7 +28,7 @@
 "`LWG3946 <https://wg21.link/LWG3946>`__","The definition of ``const_iterator_t`` should be reworked","2023-11 (Kona)","","","`#105298 <https://github.com/llvm/llvm-project/issues/105298>`__",""
 "`LWG3947 <https://wg21.link/LWG3947>`__","Unexpected constraints on ``adjacent_transform_view::base()``","2023-11 (Kona)","|Complete|","22","`#105299 <https://github.com/llvm/llvm-project/issues/105299>`__",""
 "`LWG3948 <https://wg21.link/LWG3948>`__","``possibly-const-range and as-const-pointer`` should be ``noexcept``","2023-11 (Kona)","","","`#105300 <https://github.com/llvm/llvm-project/issues/105300>`__",""
-"`LWG3949 <https://wg21.link/LWG3949>`__","``std::atomic<bool>``'s trivial destructor dropped in C++17 spec wording","2023-11 (Kona)","","","`#105301 <https://github.com/llvm/llvm-project/issues/105301>`__",""
+"`LWG3949 <https://wg21.link/LWG3949>`__","``std::atomic<bool>``'s trivial destructor dropped in C++17 spec wording","2023-11 (Kona)","|Nothing To Do|","","`#105301 <https://github.com/llvm/llvm-project/issues/105301>`__",""
 "`LWG3951 <https://wg21.link/LWG3951>`__","[expected.object.swap]: Using ``value()`` instead of ``has_value()``","2023-11 (Kona)","|Complete|","16","`#105302 <https://github.com/llvm/llvm-project/issues/105302>`__",""
 "`LWG3953 <https://wg21.link/LWG3953>`__","``iter_move`` for ``common_iterator`` and ``counted_iterator`` should return ``decltype(auto)``","2023-11 (Kona)","|Complete|","20","`#105303 <https://github.com/llvm/llvm-project/issues/105303>`__",""
 "`LWG3957 <https://wg21.link/LWG3957>`__","[container.alloc.reqmts] The value category of v should be claimed","2023-11 (Kona)","","","`#105304 <https://github.com/llvm/llvm-project/issues/105304>`__",""

diff  --git a/libcxx/test/std/atomics/atomics.types.generic/standard_layout.compile.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/standard_layout.compile.pass.cpp
index ca17657177f3c..52e48e3fc8482 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/standard_layout.compile.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/standard_layout.compile.pass.cpp
@@ -26,6 +26,7 @@ struct CheckStandardLayout {
 };
 
 void tests() {
+  CheckStandardLayout<bool>()();
   TestEachIntegralType<CheckStandardLayout>()();
   TestEachFloatingPointType<CheckStandardLayout>()();
   TestEachPointerType<CheckStandardLayout>()();

diff  --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/dtor.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/dtor.pass.cpp
index 2ecb580757d72..8f21554c97288 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/dtor.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/dtor.pass.cpp
@@ -28,6 +28,7 @@ struct CheckTriviallyDestructible {
 };
 
 int main(int, char**) {
+  CheckTriviallyDestructible<bool>()(); // LWG3949
   TestEachIntegralType<CheckTriviallyDestructible>()();
   TestEachFloatingPointType<CheckTriviallyDestructible>()();
   TestEachPointerType<CheckTriviallyDestructible>()();


        

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:48:41 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 09:48:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add tests for LWG3949 and mark
	it as done (PR #208272)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208272@github.com>
Message-ID: <6a4fd0e9.331483e7.2440f8.3d92@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208272

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:49:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 09:49:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 4e3a68f - [libc++] Mark LWG4276 as
	complete (#208273)
Message-ID: <6a4fd0fd.7d41f3a1.237e53.7b3b@mx.google.com>


Author: Louis Dionne
Date: 2026-07-09T12:48:57-04:00
New Revision: 4e3a68f7b8e4bdc1f16dcf3d322dc4f6750510db

URL: https://github.com/llvm/llvm-project/commit/4e3a68f7b8e4bdc1f16dcf3d322dc4f6750510db
DIFF: https://github.com/llvm/llvm-project/commit/4e3a68f7b8e4bdc1f16dcf3d322dc4f6750510db.diff

LOG: [libc++] Mark LWG4276 as complete (#208273)

We already harden std::array<T, 0>::front() and back(), and we have
tests for it.

Closes #171330

Added: 
    

Modified: 
    libcxx/docs/Status/Cxx26Issues.csv

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c74eff436b96d..8441da10d78de 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -170,7 +170,7 @@
 "`LWG4272 <https://wg21.link/LWG4272>`__","For ``rank == 0``, ``layout_stride`` is atypically convertible","2025-11 (Kona)","","","`#171327 <https://github.com/llvm/llvm-project/issues/171327>`__",""
 "`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","","","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
 "`LWG4275 <https://wg21.link/LWG4275>`__","``std::dynamic_extent`` should also be defined in ``<mdspan>``","2025-11 (Kona)","|Complete|","21","`#171329 <https://github.com/llvm/llvm-project/issues/171329>`__",""
-"`LWG4276 <https://wg21.link/LWG4276>`__","``front()`` and ``back()`` are not hardened for zero-length ``std::array``\s","2025-11 (Kona)","","","`#171330 <https://github.com/llvm/llvm-project/issues/171330>`__",""
+"`LWG4276 <https://wg21.link/LWG4276>`__","``front()`` and ``back()`` are not hardened for zero-length ``std::array``\s","2025-11 (Kona)","|Complete|","17","`#171330 <https://github.com/llvm/llvm-project/issues/171330>`__",""
 "`LWG4280 <https://wg21.link/LWG4280>`__","``simd::partial_load`` uses undefined identifier ``T``","2025-11 (Kona)","","","`#171331 <https://github.com/llvm/llvm-project/issues/171331>`__",""
 "`LWG4286 <https://wg21.link/LWG4286>`__","Some more feature-test macros for fully freestanding features are not marked freestanding","2025-11 (Kona)","","","`#171332 <https://github.com/llvm/llvm-project/issues/171332>`__",""
 "`LWG4291 <https://wg21.link/LWG4291>`__","``explicit map(const Allocator&)`` should be ``constexpr``","2025-11 (Kona)","","","`#171333 <https://github.com/llvm/llvm-project/issues/171333>`__",""


        

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:49:04 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 09:49:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG4276 as complete (PR
	#208273)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208273@github.com>
Message-ID: <6a4fd100.492e23e4.157d47.fb25@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208273

From libcxx-commits at lists.llvm.org  Thu Jul  9 09:51:15 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 09 Jul 2026 09:51:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Improve diagnostic for libcxxabi
 missing from the runtimes (PR #208274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208274@github.com>
Message-ID: <6a4fd183.bb839515.27d21e.6771@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/208274

>From 46f65633ffa15d30079697228e06b4156174faa6 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 8 Jul 2026 13:44:02 -0400
Subject: [PATCH 1/2] [libc++] Improve diagnostic for libcxxabi missing from
 the runtimes

When libc++ is configured to use libc++abi as an ABI library but libcxxabi
is not part of the runtimes, we'd end up with a confusing build error about
a missing <cxxabi.h> header. Instead, diagnose at CMake configuration time.

Closes #136480
---
 libcxx/cmake/Modules/HandleLibCXXABI.cmake | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libcxx/cmake/Modules/HandleLibCXXABI.cmake b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
index 52236f473f35d..4b4ff7c7f9d5f 100644
--- a/libcxx/cmake/Modules/HandleLibCXXABI.cmake
+++ b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
@@ -148,6 +148,12 @@ elseif ("${LIBCXX_CXX_ABI}" STREQUAL "libcxxabi")
     add_library(libcxx-abi-static-objects ALIAS cxxabi_static_objects)
   endif()
 
+  if (NOT TARGET cxxabi_static AND NOT TARGET cxxabi_shared
+      AND NOT TARGET cxxabi_static_objects AND NOT TARGET cxxabi_shared_objects)
+    message(FATAL_ERROR "Can't find any libc++abi target but LIBCXX_CXX_ABI is set to libcxxabi, "
+                        "did you forget to include libcxxabi in LLVM_ENABLE_RUNTIMES?")
+  endif()
+
 # Link against a system-provided libc++abi
 elseif ("${LIBCXX_CXX_ABI}" STREQUAL "system-libcxxabi")
   if(NOT LIBCXX_CXX_ABI_INCLUDE_PATHS)

>From 0a1a4d5b734a093287e944d1d01ea76fcc81359e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 9 Jul 2026 12:51:02 -0400
Subject: [PATCH 2/2] Mention system-libcxxabi

---
 libcxx/cmake/Modules/HandleLibCXXABI.cmake | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/cmake/Modules/HandleLibCXXABI.cmake b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
index 4b4ff7c7f9d5f..f823db8d1ff14 100644
--- a/libcxx/cmake/Modules/HandleLibCXXABI.cmake
+++ b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
@@ -151,7 +151,8 @@ elseif ("${LIBCXX_CXX_ABI}" STREQUAL "libcxxabi")
   if (NOT TARGET cxxabi_static AND NOT TARGET cxxabi_shared
       AND NOT TARGET cxxabi_static_objects AND NOT TARGET cxxabi_shared_objects)
     message(FATAL_ERROR "Can't find any libc++abi target but LIBCXX_CXX_ABI is set to libcxxabi, "
-                        "did you forget to include libcxxabi in LLVM_ENABLE_RUNTIMES?")
+                        "did you forget to include libcxxabi in LLVM_ENABLE_RUNTIMES, or did you "
+                        "intend to use LIBCXX_CXX_ABI=system-libcxxabi instead?")
   endif()
 
 # Link against a system-provided libc++abi


From libcxx-commits at lists.llvm.org  Thu Jul  9 11:50:21 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 09 Jul 2026 11:50:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Improve diagnostic for libcxxabi
 missing from the runtimes (PR #208274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208274@github.com>
Message-ID: <6a4fed6d.449bfb44.172ed9.8bc4@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/208274

From libcxx-commits at lists.llvm.org  Thu Jul  9 14:47:00 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Yordan_V=C3=A1squez?= via libcxx-commits)
Date: Thu, 09 Jul 2026 14:47:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3133: Modernizing
 numeric type requirements (PR #208145)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208145@github.com>
Message-ID: <6a5016d4.f1c1923d.991d1.bc19@mx.google.com>


https://github.com/vyordan updated https://github.com/llvm/llvm-project/pull/208145

>From e8ec4e380ceab7757af7bcec7e8bb10efe9512ee Mon Sep 17 00:00:00 2001
From: vyordan <vyordangiovani at gmail.com>
Date: Tue, 7 Jul 2026 23:55:36 -0600
Subject: [PATCH] [libc++] Implement LWG3133: Modernizing numeric type
 requirements

Add a static_assert to both std::complex<T> and std::valarray<T>
requiring that T be a cv-unqualified object type that satisfies the
Cpp17DefaultConstructible, Cpp17CopyConstructible, Cpp17CopyAssignable,
and Cpp17Destructible named requirements, per the revised wording in
[numeric.requirements]. This mirrors the existing pattern already used
by std::optional<T>.

Test coverage:
- A .verify.cpp for complex<T> and one for valarray<T>, each covering
  six failure modes: cv-qualified types, and one type violating each
  of the four named requirements individually.
- operator_hijacker-based tests added to valarray's access.pass.cpp
  and const_access.pass.cpp, confirming operator[] does not rely on a
  user-overloadable operator&.

Verified locally: 894/896 tests passing in libcxx/test/std/numerics/
(2 unsupported, 0 failures).

Fixes #100254
---
 libcxx/docs/Status/Cxx20Issues.csv            |  2 +-
 libcxx/include/complex                        | 11 ++++
 libcxx/include/valarray                       | 11 ++++
 ..._requires_cv_unqualified_object.verify.cpp | 63 +++++++++++++++++++
 .../valarray.access/access.pass.cpp           |  6 ++
 .../valarray.access/const_access.pass.cpp     |  6 ++
 ..._requires_cv_unqualified_object.verify.cpp | 62 ++++++++++++++++++
 7 files changed, 160 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/complex.number/complex/complex_requires_cv_unqualified_object.verify.cpp
 create mode 100644 libcxx/test/std/numerics/numarray/template.valarray/valarray_requires_cv_unqualified_object.verify.cpp

diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index f43a56f99f025..e3845d3e946d3 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -136,7 +136,7 @@
 "`LWG3101 <https://wg21.link/LWG3101>`__","``span``\ 's ``Container``\  constructors need another constraint","2019-02 (Kona)","|Complete|","","`#103837 <https://github.com/llvm/llvm-project/issues/103837>`__",""
 "`LWG3112 <https://wg21.link/LWG3112>`__","``system_error``\  and ``filesystem_error``\  constructors taking a ``string``\  may not be able to meet their postconditions","2019-02 (Kona)","|Nothing To Do|","","`#100253 <https://github.com/llvm/llvm-project/issues/100253>`__",""
 "`LWG3119 <https://wg21.link/LWG3119>`__","Program-definedness of closure types","2019-02 (Kona)","|Nothing To Do|","","`#103838 <https://github.com/llvm/llvm-project/issues/103838>`__",""
-"`LWG3133 <https://wg21.link/LWG3133>`__","Modernizing numeric type requirements","2019-02 (Kona)","","","`#100254 <https://github.com/llvm/llvm-project/issues/100254>`__",""
+"`LWG3133 <https://wg21.link/LWG3133>`__","Modernizing numeric type requirements","2019-02 (Kona)","|Complete|","23","`#100254 <https://github.com/llvm/llvm-project/issues/100254>`__",""
 "`LWG3144 <https://wg21.link/LWG3144>`__","``span``\  does not have a ``const_pointer``\  typedef","2019-02 (Kona)","|Complete|","","`#103839 <https://github.com/llvm/llvm-project/issues/103839>`__",""
 "`LWG3173 <https://wg21.link/LWG3173>`__","Enable CTAD for ``ref-view``\ ","2019-02 (Kona)","|Complete|","15","`#103840 <https://github.com/llvm/llvm-project/issues/103840>`__",""
 "`LWG3179 <https://wg21.link/LWG3179>`__","``subrange``\  should always model ``Range``\ ","2019-02 (Kona)","|Nothing To Do|","","`#103842 <https://github.com/llvm/llvm-project/issues/103842>`__",""
diff --git a/libcxx/include/complex b/libcxx/include/complex
index f1ba85eb9624e..1a2b1bc01ab20 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -266,6 +266,11 @@ template<class T> complex<T> tanh (const complex<T>&);
 #  include <__tuple/tuple_element.h>
 #  include <__tuple/tuple_size.h>
 #  include <__type_traits/conditional.h>
+#  include <__type_traits/is_assignable.h>
+#  include <__type_traits/is_constructible.h>
+#  include <__type_traits/is_destructible.h>
+#  include <__type_traits/is_object.h>
+#  include <__type_traits/is_unqualified.h>
 #  include <__utility/move.h>
 #  include <cmath>
 #  include <version>
@@ -307,6 +312,12 @@ class complex {
 public:
   typedef _Tp value_type;
 
+  static_assert(is_object<_Tp>::value && __is_unqualified_v<_Tp> && is_default_constructible_v<_Tp> &&
+                    is_copy_constructible_v<_Tp> && is_copy_assignable_v<_Tp> && is_destructible_v<_Tp>,
+                "std::complex<T> requires T to be a cv-unqualified object type that satisfies the "
+                "Cpp17DefaultConstructible, Cpp17CopyConstructible, Cpp17CopyAssignable, and "
+                "Cpp17Destructible requirements");
+
 private:
   value_type __re_;
   value_type __im_;
diff --git a/libcxx/include/valarray b/libcxx/include/valarray
index 37ab9864ac452..22e48da06fc0e 100644
--- a/libcxx/include/valarray
+++ b/libcxx/include/valarray
@@ -361,6 +361,11 @@ template <class T> unspecified2 end(const valarray<T>& v);
 #  include <__memory/allocator.h>
 #  include <__memory/uninitialized_algorithms.h>
 #  include <__type_traits/decay.h>
+#  include <__type_traits/is_assignable.h>
+#  include <__type_traits/is_constructible.h>
+#  include <__type_traits/is_destructible.h>
+#  include <__type_traits/is_object.h>
+#  include <__type_traits/is_unqualified.h>
 #  include <__type_traits/remove_reference.h>
 #  include <__utility/exception_guard.h>
 #  include <__utility/move.h>
@@ -785,6 +790,12 @@ template <class _Tp>
 class valarray {
 public:
   typedef _Tp value_type;
+
+  static_assert(is_object<_Tp>::value && __is_unqualified_v<_Tp> && is_default_constructible_v<_Tp> &&
+                    is_copy_constructible_v<_Tp> && is_copy_assignable_v<_Tp> && is_destructible_v<_Tp>,
+                "std::valarray<T> requires T to be a cv-unqualified object type that satisfies the "
+                "Cpp17DefaultConstructible, Cpp17CopyConstructible, Cpp17CopyAssignable, and "
+                "Cpp17Destructible requirements");
   typedef _Tp __result_type;
 
 private:
diff --git a/libcxx/test/std/numerics/complex.number/complex/complex_requires_cv_unqualified_object.verify.cpp b/libcxx/test/std/numerics/complex.number/complex/complex_requires_cv_unqualified_object.verify.cpp
new file mode 100644
index 0000000000000..81139c99c974f
--- /dev/null
+++ b/libcxx/test/std/numerics/complex.number/complex/complex_requires_cv_unqualified_object.verify.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+// <complex>
+
+// template<class T>
+// class complex
+//
+// T shall be a cv-unqualified object type (LWG3133)
+
+#include <complex>
+
+struct NotDefaultConstructible {
+  NotDefaultConstructible() = delete;
+};
+
+struct NotCopyConstructible {
+  NotCopyConstructible()                            = default;
+  NotCopyConstructible(const NotCopyConstructible&) = delete;
+};
+
+struct NotCopyAssignable {
+  NotCopyAssignable()                                    = default;
+  NotCopyAssignable(const NotCopyAssignable&)            = default;
+  NotCopyAssignable& operator=(const NotCopyAssignable&) = delete;
+};
+
+struct NotDestructible {
+  NotDestructible()                                  = default;
+  NotDestructible(const NotDestructible&)            = default;
+  NotDestructible& operator=(const NotDestructible&) = default;
+
+private:
+  ~NotDestructible() = default;
+};
+
+void test() {
+  // expected-error-re at complex:* {{static assertion failed{{.*}}std::complex<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::complex<const double>);
+  // expected-error-re at complex:* {{static assertion failed{{.*}}std::complex<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::complex<volatile int>);
+  // expected-error-re at complex:* {{static assertion failed{{.*}}std::complex<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::complex<const volatile float>);
+
+  // expected-error-re at complex:* {{static assertion failed{{.*}}std::complex<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::complex<NotDefaultConstructible>);
+
+  // expected-error-re at complex:* {{static assertion failed{{.*}}std::complex<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::complex<NotCopyConstructible>);
+
+  // expected-error-re at complex:* {{static assertion failed{{.*}}std::complex<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::complex<NotCopyAssignable>);
+
+  // expected-error-re at complex:* {{static assertion failed{{.*}}std::complex<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::complex<NotDestructible>);
+}
diff --git a/libcxx/test/std/numerics/numarray/template.valarray/valarray.access/access.pass.cpp b/libcxx/test/std/numerics/numarray/template.valarray/valarray.access/access.pass.cpp
index 4a72cc73c12fc..c500e86220607 100644
--- a/libcxx/test/std/numerics/numarray/template.valarray/valarray.access/access.pass.cpp
+++ b/libcxx/test/std/numerics/numarray/template.valarray/valarray.access/access.pass.cpp
@@ -15,6 +15,7 @@
 #include <valarray>
 #include <cassert>
 
+#include "operator_hijacker.h"
 #include "test_macros.h"
 
 int main(int, char**)
@@ -31,6 +32,11 @@ int main(int, char**)
             assert(v[i] == static_cast<int>(i));
         }
     }
+    {
+      std::valarray<operator_hijacker> v(1);
+      operator_hijacker& r = v[0];
+      (void)r;
+    }
 
   return 0;
 }
diff --git a/libcxx/test/std/numerics/numarray/template.valarray/valarray.access/const_access.pass.cpp b/libcxx/test/std/numerics/numarray/template.valarray/valarray.access/const_access.pass.cpp
index ef9ac713af474..5970a514975ab 100644
--- a/libcxx/test/std/numerics/numarray/template.valarray/valarray.access/const_access.pass.cpp
+++ b/libcxx/test/std/numerics/numarray/template.valarray/valarray.access/const_access.pass.cpp
@@ -15,6 +15,7 @@
 #include <valarray>
 #include <cassert>
 
+#include "operator_hijacker.h"
 #include "test_macros.h"
 
 int main(int, char**)
@@ -29,6 +30,11 @@ int main(int, char**)
             assert(v[i] == a[i]);
         }
     }
+    {
+      const std::valarray<operator_hijacker> v(1);
+      const operator_hijacker& r = v[0];
+      (void)r;
+    }
 
   return 0;
 }
diff --git a/libcxx/test/std/numerics/numarray/template.valarray/valarray_requires_cv_unqualified_object.verify.cpp b/libcxx/test/std/numerics/numarray/template.valarray/valarray_requires_cv_unqualified_object.verify.cpp
new file mode 100644
index 0000000000000..44c1adc224736
--- /dev/null
+++ b/libcxx/test/std/numerics/numarray/template.valarray/valarray_requires_cv_unqualified_object.verify.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+// <valarray>
+
+// template<class T>
+// class valarray
+//
+// T shall be a cv-unqualified object type that satisfies the Cpp17DefaultConstructible,
+// Cpp17CopyConstructible, Cpp17CopyAssignable, and Cpp17Destructible requirements (LWG3133)
+
+#include <valarray>
+
+struct NotDefaultConstructible {
+  NotDefaultConstructible() = delete;
+};
+
+struct NotCopyConstructible {
+  NotCopyConstructible()                            = default;
+  NotCopyConstructible(const NotCopyConstructible&) = delete;
+};
+
+struct NotCopyAssignable {
+  NotCopyAssignable()                                    = default;
+  NotCopyAssignable(const NotCopyAssignable&)            = default;
+  NotCopyAssignable& operator=(const NotCopyAssignable&) = delete;
+};
+
+struct NotDestructible {
+  NotDestructible()                                  = default;
+  NotDestructible(const NotDestructible&)            = default;
+  NotDestructible& operator=(const NotDestructible&) = default;
+
+private:
+  ~NotDestructible() = default;
+};
+
+void test() {
+  // expected-error-re at valarray:* {{static assertion failed{{.*}}std::valarray<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::valarray<const int>);
+  // expected-error-re at valarray:* {{static assertion failed{{.*}}std::valarray<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::valarray<volatile int>);
+
+  // expected-error-re at valarray:* {{static assertion failed{{.*}}std::valarray<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::valarray<NotDefaultConstructible>);
+
+  // expected-error-re at valarray:* {{static assertion failed{{.*}}std::valarray<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::valarray<NotCopyConstructible>);
+
+  // expected-error-re at valarray:* {{static assertion failed{{.*}}std::valarray<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::valarray<NotCopyAssignable>);
+
+  // expected-error-re at valarray:* {{static assertion failed{{.*}}std::valarray<T> requires T to be a cv-unqualified object type}}
+  (void)sizeof(std::valarray<NotDestructible>);
+}


From libcxx-commits at lists.llvm.org  Thu Jul  9 16:46:32 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 16:46:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] f5169f2 - [libc++] Enable missing
 allocator constructors in C++11 (#204845)
Message-ID: <6a5032d8.481afadf.196d57.5575@mx.google.com>


Author: halbi2
Date: 2026-07-10T07:46:26+08:00
New Revision: f5169f2a5c53ef64c751ccbafa1799f9915b2814

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

LOG: [libc++] Enable missing allocator constructors in C++11 (#204845)

LWG2210 was a patch to N2554 (C++11) which added missing
allocator-extended constructors to various containers. When LWG2210 was
implemented, the constructors were gated with C++14. However, we
normally apply LWG issue resolutions as DRs.

This patch enables these constructors in C++11 mode too.

Fixes #204843

---------

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
Co-authored-by: A. Jiang <de34 at live.cn>

Added: 
    

Modified: 
    libcxx/include/__vector/vector.h
    libcxx/include/__vector/vector_bool.h
    libcxx/include/deque
    libcxx/include/forward_list
    libcxx/include/list
    libcxx/include/map
    libcxx/include/set
    libcxx/include/unordered_map
    libcxx/include/unordered_set
    libcxx/include/vector
    libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp
    libcxx/test/std/containers/associative/multimap/multimap.cons/iter_iter_comp_alloc.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
    libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
    libcxx/test/std/containers/sequences/deque/deque.cons/size.pass.cpp
    libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
    libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
    libcxx/test/std/containers/sequences/vector.bool/construct_size.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp
    libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_hash_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_hash_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_hash_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_hash_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_hash_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_hash_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_hash_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_hash_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_hash_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_hash_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_hash_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_allocator.pass.cpp
    libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_hash_allocator.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 422c98c300a1d..8226a7f87a119 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -153,7 +153,6 @@ class vector {
     __guard.__complete();
   }
 
-#if _LIBCPP_STD_VER >= 14
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI explicit vector(size_type __n, const allocator_type& __a)
       : __layout_(__a) {
     auto __guard = std::__make_exception_guard(__destroy_vector(*this));
@@ -163,7 +162,6 @@ class vector {
     }
     __guard.__complete();
   }
-#endif
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector(size_type __n, const value_type& __x) {
     auto __guard = std::__make_exception_guard(__destroy_vector(*this));

diff  --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index f73e086478a70..6dc904e6d3bd9 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -152,9 +152,7 @@ class vector<bool, _Allocator> {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~vector() { __destroy_vector (*this)(); }
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit vector(size_type __n);
-#if _LIBCPP_STD_VER >= 14
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit vector(size_type __n, const allocator_type& __a);
-#endif
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector(size_type __n, const value_type& __v);
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
   vector(size_type __n, const value_type& __v, const allocator_type& __a);
@@ -605,7 +603,6 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(size_type __n)
   }
 }
 
-#if _LIBCPP_STD_VER >= 14
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(size_type __n, const allocator_type& __a)
     : __begin_(nullptr), __size_(0), __cap_(0), __alloc_(static_cast<__storage_allocator>(__a)) {
@@ -615,7 +612,6 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(size_type __n, co
     __size_ = __n;
   }
 }
-#endif
 
 template <class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocator>::vector(size_type __n, const value_type& __x)

diff  --git a/libcxx/include/deque b/libcxx/include/deque
index 7aee552c63d37..5a4de99c7f143 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -40,7 +40,7 @@ public:
     deque() noexcept(is_nothrow_default_constructible<allocator_type>::value);
     explicit deque(const allocator_type& a);
     explicit deque(size_type n);
-    explicit deque(size_type n, const allocator_type& a); // C++14
+    explicit deque(size_type n, const allocator_type& a);
     deque(size_type n, const value_type& v);
     deque(size_type n, const value_type& v, const allocator_type& a);
     template <class InputIterator>
@@ -644,9 +644,7 @@ public:
   }
 
   explicit _LIBCPP_HIDE_FROM_ABI deque(size_type __n);
-#  if _LIBCPP_STD_VER >= 14
   explicit _LIBCPP_HIDE_FROM_ABI deque(size_type __n, const _Allocator& __a);
-#  endif
   _LIBCPP_HIDE_FROM_ABI deque(size_type __n, const value_type& __v);
 
   template <__enable_if_t<__is_allocator_v<_Allocator>, int> = 0>
@@ -1323,7 +1321,6 @@ deque<_Tp, _Allocator>::deque(size_type __n) : __start_(0), __size_(0) {
     __append(__n);
 }
 
-#  if _LIBCPP_STD_VER >= 14
 template <class _Tp, class _Allocator>
 deque<_Tp, _Allocator>::deque(size_type __n, const _Allocator& __a)
     : __map_(__pointer_allocator(__a)), __start_(0), __size_(0), __alloc_(__a) {
@@ -1331,7 +1328,6 @@ deque<_Tp, _Allocator>::deque(size_type __n, const _Allocator& __a)
   if (__n > 0)
     __append(__n);
 }
-#  endif
 
 template <class _Tp, class _Allocator>
 deque<_Tp, _Allocator>::deque(size_type __n, const value_type& __v) : __start_(0), __size_(0) {

diff  --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index 6d86f7a8e5975..d963fea6084ca 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -37,7 +37,7 @@ public:
         noexcept(is_nothrow_default_constructible<allocator_type>::value);
     explicit forward_list(const allocator_type& a);
     explicit forward_list(size_type n);
-    explicit forward_list(size_type n, const allocator_type& a); // C++14
+    explicit forward_list(size_type n, const allocator_type& a);
     forward_list(size_type n, const value_type& v);
     forward_list(size_type n, const value_type& v, const allocator_type& a);
     template <class InputIterator>
@@ -664,9 +664,7 @@ public:
       _NOEXCEPT_(is_nothrow_default_constructible<__node_allocator>::value) {} // = default;
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit forward_list(const allocator_type& __a);
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit forward_list(size_type __n);
-#  if _LIBCPP_STD_VER >= 14
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit forward_list(size_type __n, const allocator_type& __a);
-#  endif
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI forward_list(size_type __n, const value_type& __v);
 
   template <__enable_if_t<__is_allocator_v<_Alloc>, int> = 0>
@@ -941,7 +939,6 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 forward_list<_Tp, _Alloc>::forward_list(size_type
   }
 }
 
-#  if _LIBCPP_STD_VER >= 14
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 forward_list<_Tp, _Alloc>::forward_list(size_type __n, const allocator_type& __base_alloc)
     : __base(__base_alloc) {
@@ -952,7 +949,6 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 forward_list<_Tp, _Alloc>::forward_list(size_type
     }
   }
 }
-#  endif
 
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 forward_list<_Tp, _Alloc>::forward_list(size_type __n, const value_type& __v) {

diff  --git a/libcxx/include/list b/libcxx/include/list
index 3223f25e1cfba..b4fb9191cd7f7 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -39,7 +39,7 @@ public:
         noexcept(is_nothrow_default_constructible<allocator_type>::value);
     explicit list(const allocator_type& a);
     explicit list(size_type n);
-    explicit list(size_type n, const allocator_type& a); // C++14
+    explicit list(size_type n, const allocator_type& a);
     list(size_type n, const value_type& value);
     list(size_type n, const value_type& value, const allocator_type& a);
     template <class Iter>
@@ -708,9 +708,7 @@ public:
       _NOEXCEPT_(is_nothrow_default_constructible<__node_allocator>::value) {}
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit list(const allocator_type& __a) : __base(__a) {}
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit list(size_type __n);
-#  if _LIBCPP_STD_VER >= 14
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI explicit list(size_type __n, const allocator_type& __a);
-#  endif
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI list(size_type __n, const value_type& __x);
   template <__enable_if_t<__is_allocator_v<_Alloc>, int> = 0>
   _LIBCPP_CONSTEXPR_SINCE_CXX26 _LIBCPP_HIDE_FROM_ABI
@@ -1066,13 +1064,15 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 list<_Tp, _Alloc>::list(size_type __n) {
 #  endif
 }
 
-#  if _LIBCPP_STD_VER >= 14
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 list<_Tp, _Alloc>::list(size_type __n, const allocator_type& __a) : __base(__a) {
   for (; __n > 0; --__n)
+#  ifndef _LIBCPP_CXX03_LANG
     emplace_back();
-}
+#  else
+    push_back(value_type());
 #  endif
+}
 
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 list<_Tp, _Alloc>::list(size_type __n, const value_type& __x) {

diff  --git a/libcxx/include/map b/libcxx/include/map
index 8efe18fa3f839..d51daa128eada 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -84,12 +84,12 @@ public:
     constexpr map(initializer_list<value_type> il, const key_compare& comp, const allocator_type& a);
     template <class InputIterator>
         constexpr map(InputIterator first, InputIterator last, const allocator_type& a)
-            : map(first, last, Compare(), a) {}                                                               // C++14
+            : map(first, last, Compare(), a) {}
     template<container-compatible-range<value_type> R>
       constexpr map(from_range_t, R&& rg, const Allocator& a))
         : map(from_range, std::forward<R>(rg), Compare(), a) { }                                              // C++23
     constexpr map(initializer_list<value_type> il, const allocator_type& a)
-        : map(il, Compare(), a) {}                                                                            // C++14
+        : map(il, Compare(), a) {}
    constexpr ~map();
 
     constexpr map& operator=(const map& m);
@@ -368,12 +368,12 @@ public:
              const allocator_type& a);
     template <class InputIterator>
         multimap(InputIterator first, InputIterator last, const allocator_type& a)
-            : multimap(first, last, Compare(), a) {} // C++14
+            : multimap(first, last, Compare(), a) {}
     template<container-compatible-range<value_type> R>
       multimap(from_range_t, R&& rg, const Allocator& a))
         : multimap(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
     multimap(initializer_list<value_type> il, const allocator_type& a)
-        : multimap(il, Compare(), a) {} // C++14
+        : multimap(il, Compare(), a) {}
     ~multimap();
 
     multimap& operator=(const multimap& m);
@@ -1035,12 +1035,10 @@ public:
   }
 #  endif
 
-#  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
-#  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
@@ -1073,10 +1071,8 @@ public:
     insert(__il.begin(), __il.end());
   }
 
-#    if _LIBCPP_STD_VER >= 14
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 map(initializer_list<value_type> __il, const allocator_type& __a)
       : map(__il, key_compare(), __a) {}
-#    endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 map& operator=(initializer_list<value_type> __il) {
     clear();
@@ -1817,12 +1813,10 @@ public:
   }
 #  endif
 
-#  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multimap(__f, __l, key_compare(), __a) {}
-#  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
@@ -1856,11 +1850,9 @@ public:
     insert(__il.begin(), __il.end());
   }
 
-#    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multimap(initializer_list<value_type> __il, const allocator_type& __a)
       : multimap(__il, key_compare(), __a) {}
-#    endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap& operator=(initializer_list<value_type> __il) {
     clear();

diff  --git a/libcxx/include/set b/libcxx/include/set
index 0409131ac6981..59aef4a42b428 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -71,12 +71,12 @@ public:
         const allocator_type& a);
     template <class InputIterator>
         set(InputIterator first, InputIterator last, const allocator_type& a)
-            : set(first, last, Compare(), a) {}  // C++14
+            : set(first, last, Compare(), a) {}
     template<container-compatible-range<value_type> R>
       set(from_range_t, R&& rg, const Allocator& a))
         : set(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
     set(initializer_list<value_type> il, const allocator_type& a)
-        : set(il, Compare(), a) {}  // C++14
+        : set(il, Compare(), a) {}
     ~set();
 
     set& operator=(const set& s);
@@ -316,12 +316,12 @@ public:
              const allocator_type& a);
     template <class InputIterator>
         multiset(InputIterator first, InputIterator last, const allocator_type& a)
-            : set(first, last, Compare(), a) {}  // C++14
+            : set(first, last, Compare(), a) {}
     template<container-compatible-range<value_type> R>
       multiset(from_range_t, R&& rg, const Allocator& a))
         : multiset(from_range, std::forward<R>(rg), Compare(), a) { } // C++23
     multiset(initializer_list<value_type> il, const allocator_type& a)
-        : set(il, Compare(), a) {}  // C++14
+        : set(il, Compare(), a) {}
     ~multiset();
 
     multiset& operator=(const multiset& s);
@@ -653,12 +653,10 @@ public:
   }
 #  endif
 
-#  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX26 set(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : set(__f, __l, key_compare(), __a) {}
-#  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
@@ -695,10 +693,8 @@ public:
     insert(__il.begin(), __il.end());
   }
 
-#    if _LIBCPP_STD_VER >= 14
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set(initializer_list<value_type> __il, const allocator_type& __a)
       : set(__il, key_compare(), __a) {}
-#    endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 set& operator=(initializer_list<value_type> __il) {
     clear();
@@ -1195,12 +1191,10 @@ public:
     insert(__f, __l);
   }
 
-#  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multiset(__f, __l, key_compare(), __a) {}
-#  endif
 
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
@@ -1253,11 +1247,9 @@ public:
     insert(__il.begin(), __il.end());
   }
 
-#    if _LIBCPP_STD_VER >= 14
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset(initializer_list<value_type> __il, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multiset(initializer_list<value_type> __il, const allocator_type& __a)
       : multiset(__il, key_compare(), __a) {}
-#    endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 multiset& operator=(initializer_list<value_type> __il) {
     clear();

diff  --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 8cefba01fb5f6..b8b160cd892d4 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -77,16 +77,16 @@ public:
                   const hasher& hf = hasher(), const key_equal& eql = key_equal(),
                   const allocator_type& a = allocator_type());
     unordered_map(size_type n, const allocator_type& a)
-      : unordered_map(n, hasher(), key_equal(), a) {}  // C++14
+      : unordered_map(n, hasher(), key_equal(), a) {}
     unordered_map(size_type n, const hasher& hf, const allocator_type& a)
-      : unordered_map(n, hf, key_equal(), a) {}  // C++14
+      : unordered_map(n, hf, key_equal(), a) {}
     template <class InputIterator>
       unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
-      : unordered_map(f, l, n, hasher(), key_equal(), a) {}  // C++14
+      : unordered_map(f, l, n, hasher(), key_equal(), a) {}
     template <class InputIterator>
       unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
         const allocator_type& a)
-      : unordered_map(f, l, n, hf, key_equal(), a) {}  // C++14
+      : unordered_map(f, l, n, hf, key_equal(), a) {}
     template<container-compatible-range<value_type> R>
       unordered_map(from_range_t, R&& rg, size_type n, const allocator_type& a)
         : unordered_map(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
@@ -94,10 +94,10 @@ public:
       unordered_map(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
         : unordered_map(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
     unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a)
-      : unordered_map(il, n, hasher(), key_equal(), a) {}  // C++14
+      : unordered_map(il, n, hasher(), key_equal(), a) {}
     unordered_map(initializer_list<value_type> il, size_type n, const hasher& hf,
       const allocator_type& a)
-      : unordered_map(il, n, hf, key_equal(), a) {}  // C++14
+      : unordered_map(il, n, hf, key_equal(), a) {}
     ~unordered_map();
     unordered_map& operator=(const unordered_map&);
     unordered_map& operator=(unordered_map&&)
@@ -363,16 +363,16 @@ public:
                   const hasher& hf = hasher(), const key_equal& eql = key_equal(),
                   const allocator_type& a = allocator_type());
     unordered_multimap(size_type n, const allocator_type& a)
-      : unordered_multimap(n, hasher(), key_equal(), a) {}  // C++14
+      : unordered_multimap(n, hasher(), key_equal(), a) {}
     unordered_multimap(size_type n, const hasher& hf, const allocator_type& a)
-      : unordered_multimap(n, hf, key_equal(), a) {}  // C++14
+      : unordered_multimap(n, hf, key_equal(), a) {}
     template <class InputIterator>
       unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
-      : unordered_multimap(f, l, n, hasher(), key_equal(), a) {}  // C++14
+      : unordered_multimap(f, l, n, hasher(), key_equal(), a) {}
     template <class InputIterator>
       unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf,
         const allocator_type& a)
-      : unordered_multimap(f, l, n, hf, key_equal(), a) {}  // C++14
+      : unordered_multimap(f, l, n, hf, key_equal(), a) {}
     template<container-compatible-range<value_type> R>
       unordered_multimap(from_range_t, R&& rg, size_type n, const allocator_type& a)
         : unordered_multimap(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
@@ -380,10 +380,10 @@ public:
       unordered_multimap(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
         : unordered_multimap(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
     unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a)
-      : unordered_multimap(il, n, hasher(), key_equal(), a) {}  // C++14
+      : unordered_multimap(il, n, hasher(), key_equal(), a) {}
     unordered_multimap(initializer_list<value_type> il, size_type n, const hasher& hf,
       const allocator_type& a)
-      : unordered_multimap(il, n, hf, key_equal(), a) {}  // C++14
+      : unordered_multimap(il, n, hf, key_equal(), a) {}
     ~unordered_multimap();
     unordered_multimap& operator=(const unordered_multimap&);
     unordered_multimap& operator=(unordered_multimap&&)
@@ -993,7 +993,6 @@ public:
       const key_equal& __eql,
       const allocator_type& __a);
 #  endif // _LIBCPP_CXX03_LANG
-#  if _LIBCPP_STD_VER >= 14
   _LIBCPP_HIDE_FROM_ABI unordered_map(size_type __n, const allocator_type& __a)
       : unordered_map(__n, hasher(), key_equal(), __a) {}
   _LIBCPP_HIDE_FROM_ABI unordered_map(size_type __n, const hasher& __hf, const allocator_type& __a)
@@ -1007,7 +1006,7 @@ public:
       _InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_map(__first, __last, __n, __hf, key_equal(), __a) {}
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
   _LIBCPP_HIDE_FROM_ABI unordered_map(from_range_t, _Range&& __range, size_type __n, const allocator_type& __a)
       : unordered_map(from_range, std::forward<_Range>(__range), __n, hasher(), key_equal(), __a) {}
@@ -1016,8 +1015,9 @@ public:
   _LIBCPP_HIDE_FROM_ABI
   unordered_map(from_range_t, _Range&& __range, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_map(from_range, std::forward<_Range>(__range), __n, __hf, key_equal(), __a) {}
-#    endif
+#  endif
 
+#  ifndef _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI unordered_map(initializer_list<value_type> __il, size_type __n, const allocator_type& __a)
       : unordered_map(__il, __n, hasher(), key_equal(), __a) {}
   _LIBCPP_HIDE_FROM_ABI
@@ -1787,7 +1787,6 @@ public:
       const key_equal& __eql,
       const allocator_type& __a);
 #  endif // _LIBCPP_CXX03_LANG
-#  if _LIBCPP_STD_VER >= 14
   _LIBCPP_HIDE_FROM_ABI unordered_multimap(size_type __n, const allocator_type& __a)
       : unordered_multimap(__n, hasher(), key_equal(), __a) {}
   _LIBCPP_HIDE_FROM_ABI unordered_multimap(size_type __n, const hasher& __hf, const allocator_type& __a)
@@ -1801,7 +1800,7 @@ public:
       _InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_multimap(__first, __last, __n, __hf, key_equal(), __a) {}
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
   _LIBCPP_HIDE_FROM_ABI unordered_multimap(from_range_t, _Range&& __range, size_type __n, const allocator_type& __a)
       : unordered_multimap(from_range, std::forward<_Range>(__range), __n, hasher(), key_equal(), __a) {}
@@ -1810,8 +1809,9 @@ public:
   _LIBCPP_HIDE_FROM_ABI
   unordered_multimap(from_range_t, _Range&& __range, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_multimap(from_range, std::forward<_Range>(__range), __n, __hf, key_equal(), __a) {}
-#    endif
+#  endif
 
+#  ifndef _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI unordered_multimap(initializer_list<value_type> __il, size_type __n, const allocator_type& __a)
       : unordered_multimap(__il, __n, hasher(), key_equal(), __a) {}
   _LIBCPP_HIDE_FROM_ABI

diff  --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index d54d59403b038..10f8b730b842b 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -76,22 +76,22 @@ public:
     unordered_set(initializer_list<value_type>, size_type n = 0,
                   const hasher& hf = hasher(), const key_equal& eql = key_equal(),
                   const allocator_type& a = allocator_type());
-    unordered_set(size_type n, const allocator_type& a); // C++14
-    unordered_set(size_type n, const hasher& hf, const allocator_type& a); // C++14
+    unordered_set(size_type n, const allocator_type& a);
+    unordered_set(size_type n, const hasher& hf, const allocator_type& a);
     template <class InputIterator>
-      unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a); // C++14
+      unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
     template <class InputIterator>
       unordered_set(InputIterator f, InputIterator l, size_type n,
-                    const hasher& hf,  const allocator_type& a); // C++14
+                    const hasher& hf,  const allocator_type& a);
     template<container-compatible-range<value_type> R>
       unordered_set(from_range_t, R&& rg, size_type n, const allocator_type& a)
         : unordered_set(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
     template<container-compatible-range<value_type> R>
       unordered_set(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
         : unordered_set(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
-    unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a); // C++14
+    unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a);
     unordered_set(initializer_list<value_type> il, size_type n,
-                  const hasher& hf,  const allocator_type& a); // C++14
+                  const hasher& hf,  const allocator_type& a);
     ~unordered_set();
     unordered_set& operator=(const unordered_set&);
     unordered_set& operator=(unordered_set&&)
@@ -324,22 +324,22 @@ public:
     unordered_multiset(initializer_list<value_type>, size_type n = /see below/,
                   const hasher& hf = hasher(), const key_equal& eql = key_equal(),
                   const allocator_type& a = allocator_type());
-    unordered_multiset(size_type n, const allocator_type& a); // C++14
-    unordered_multiset(size_type n, const hasher& hf, const allocator_type& a); // C++14
+    unordered_multiset(size_type n, const allocator_type& a);
+    unordered_multiset(size_type n, const hasher& hf, const allocator_type& a);
     template <class InputIterator>
-      unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a); // C++14
+      unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a);
     template <class InputIterator>
       unordered_multiset(InputIterator f, InputIterator l, size_type n,
-                         const hasher& hf, const allocator_type& a); // C++14
+                         const hasher& hf, const allocator_type& a);
     template<container-compatible-range<value_type> R>
       unordered_multiset(from_range_t, R&& rg, size_type n, const allocator_type& a)
         : unordered_multiset(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } // C++23
     template<container-compatible-range<value_type> R>
       unordered_multiset(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
         : unordered_multiset(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }       // C++23
-    unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a); // C++14
+    unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a);
     unordered_multiset(initializer_list<value_type> il, size_type n,
-                       const hasher& hf,  const allocator_type& a); // C++14
+                       const hasher& hf,  const allocator_type& a);
     ~unordered_multiset();
     unordered_multiset& operator=(const unordered_multiset&);
     unordered_multiset& operator=(unordered_multiset&&)
@@ -634,12 +634,10 @@ public:
   _LIBCPP_HIDE_FROM_ABI unordered_set() _NOEXCEPT_(is_nothrow_default_constructible<__table>::value) {}
   explicit _LIBCPP_HIDE_FROM_ABI
   unordered_set(size_type __n, const hasher& __hf = hasher(), const key_equal& __eql = key_equal());
-#  if _LIBCPP_STD_VER >= 14
   inline _LIBCPP_HIDE_FROM_ABI unordered_set(size_type __n, const allocator_type& __a)
       : unordered_set(__n, hasher(), key_equal(), __a) {}
   inline _LIBCPP_HIDE_FROM_ABI unordered_set(size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_set(__n, __hf, key_equal(), __a) {}
-#  endif
   _LIBCPP_HIDE_FROM_ABI
   unordered_set(size_type __n, const hasher& __hf, const key_equal& __eql, const allocator_type& __a);
   template <class _InputIterator>
@@ -677,7 +675,6 @@ public:
   }
 #  endif
 
-#  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   inline _LIBCPP_HIDE_FROM_ABI
   unordered_set(_InputIterator __first, _InputIterator __last, size_type __n, const allocator_type& __a)
@@ -686,7 +683,6 @@ public:
   _LIBCPP_HIDE_FROM_ABI unordered_set(
       _InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_set(__first, __last, __n, __hf, key_equal(), __a) {}
-#  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
@@ -717,14 +713,12 @@ public:
       const hasher& __hf,
       const key_equal& __eql,
       const allocator_type& __a);
-#    if _LIBCPP_STD_VER >= 14
   inline _LIBCPP_HIDE_FROM_ABI
   unordered_set(initializer_list<value_type> __il, size_type __n, const allocator_type& __a)
       : unordered_set(__il, __n, hasher(), key_equal(), __a) {}
   inline _LIBCPP_HIDE_FROM_ABI
   unordered_set(initializer_list<value_type> __il, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_set(__il, __n, __hf, key_equal(), __a) {}
-#    endif
 #  endif // _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI ~unordered_set() {
     static_assert(sizeof(std::__diagnose_unordered_container_requirements<_Value, _Hash, _Pred>(0)), "");
@@ -1250,12 +1244,10 @@ public:
   unordered_multiset(size_type __n, const hasher& __hf = hasher(), const key_equal& __eql = key_equal());
   _LIBCPP_HIDE_FROM_ABI
   unordered_multiset(size_type __n, const hasher& __hf, const key_equal& __eql, const allocator_type& __a);
-#  if _LIBCPP_STD_VER >= 14
   inline _LIBCPP_HIDE_FROM_ABI unordered_multiset(size_type __n, const allocator_type& __a)
       : unordered_multiset(__n, hasher(), key_equal(), __a) {}
   inline _LIBCPP_HIDE_FROM_ABI unordered_multiset(size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_multiset(__n, __hf, key_equal(), __a) {}
-#  endif
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI unordered_multiset(_InputIterator __first, _InputIterator __last);
   template <class _InputIterator>
@@ -1291,7 +1283,6 @@ public:
   }
 #  endif
 
-#  if _LIBCPP_STD_VER >= 14
   template <class _InputIterator>
   inline _LIBCPP_HIDE_FROM_ABI
   unordered_multiset(_InputIterator __first, _InputIterator __last, size_type __n, const allocator_type& __a)
@@ -1300,7 +1291,6 @@ public:
   inline _LIBCPP_HIDE_FROM_ABI unordered_multiset(
       _InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_multiset(__first, __last, __n, __hf, key_equal(), __a) {}
-#  endif
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
@@ -1331,14 +1321,12 @@ public:
       const hasher& __hf,
       const key_equal& __eql,
       const allocator_type& __a);
-#    if _LIBCPP_STD_VER >= 14
   inline _LIBCPP_HIDE_FROM_ABI
   unordered_multiset(initializer_list<value_type> __il, size_type __n, const allocator_type& __a)
       : unordered_multiset(__il, __n, hasher(), key_equal(), __a) {}
   inline _LIBCPP_HIDE_FROM_ABI
   unordered_multiset(initializer_list<value_type> __il, size_type __n, const hasher& __hf, const allocator_type& __a)
       : unordered_multiset(__il, __n, __hf, key_equal(), __a) {}
-#    endif
 #  endif // _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI ~unordered_multiset() {
     static_assert(sizeof(std::__diagnose_unordered_container_requirements<_Value, _Hash, _Pred>(0)), "");

diff  --git a/libcxx/include/vector b/libcxx/include/vector
index f289013a682bc..b136cca1f6050 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -39,7 +39,7 @@ public:
         noexcept(is_nothrow_default_constructible<allocator_type>::value);
     explicit vector(const allocator_type&);
     explicit vector(size_type n);
-    explicit vector(size_type n, const allocator_type&); // C++14
+    explicit vector(size_type n, const allocator_type&);
     vector(size_type n, const value_type& value, const allocator_type& = allocator_type());
     template <class InputIterator>
         vector(InputIterator first, InputIterator last, const allocator_type& = allocator_type());
@@ -171,7 +171,7 @@ public:
     vector()
         noexcept(is_nothrow_default_constructible<allocator_type>::value);
     explicit vector(const allocator_type&) noexcept;
-    explicit vector(size_type n, const allocator_type& a = allocator_type()); // C++14
+    explicit vector(size_type n, const allocator_type& a = allocator_type());
     vector(size_type n, const value_type& value, const allocator_type& = allocator_type());
     template <class InputIterator>
         vector(InputIterator first, InputIterator last, const allocator_type& = allocator_type());

diff  --git a/libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp b/libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp
index 014f9c5960e5b..ce2b2ef185d90 100644
--- a/libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.cons/iter_iter_comp_alloc.pass.cpp
@@ -72,7 +72,6 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*std::next(m.begin()) == V(2, 1));
     assert(*std::next(m.begin(), 2) == V(3, 1));
   }
-#  if TEST_STD_VER > 11
   {
     typedef std::pair<const int, double> V;
     V ar[] = {
@@ -113,7 +112,6 @@ TEST_CONSTEXPR_CXX26 bool test() {
       assert(m.get_allocator() == a);
     }
   }
-#  endif
 #endif
   return true;
 }

diff  --git a/libcxx/test/std/containers/associative/multimap/multimap.cons/iter_iter_comp_alloc.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.cons/iter_iter_comp_alloc.pass.cpp
index fb3f4b831cc0d..08320024cc5bd 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.cons/iter_iter_comp_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.cons/iter_iter_comp_alloc.pass.cpp
@@ -115,6 +115,46 @@ bool test() {
     assert(*std::next(m.begin(), 7) == V(3, 1.5));
     assert(*std::next(m.begin(), 8) == V(3, 2));
   }
+  {
+    typedef std::pair<const int, double> V;
+    V ar[] = {
+        V(1, 1),
+        V(1, 1.5),
+        V(1, 2),
+        V(2, 1),
+        V(2, 1.5),
+        V(2, 2),
+        V(3, 1),
+        V(3, 1.5),
+        V(3, 2),
+    };
+    {
+      typedef min_allocator<V> A;
+      typedef test_less<int> C;
+      A a;
+      std::multimap<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]), a);
+
+      assert(m.size() == 9);
+      assert(std::distance(m.begin(), m.end()) == 9);
+      assert(*m.begin() == V(1, 1));
+      assert(*std::next(m.begin(), 3) == V(2, 1));
+      assert(*std::next(m.begin(), 6) == V(3, 1));
+      assert(m.get_allocator() == a);
+    }
+    {
+      typedef explicit_allocator<V> A;
+      typedef test_less<int> C;
+      A a;
+      std::multimap<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]), a);
+
+      assert(m.size() == 9);
+      assert(std::distance(m.begin(), m.end()) == 9);
+      assert(*m.begin() == V(1, 1));
+      assert(*std::next(m.begin(), 3) == V(2, 1));
+      assert(*std::next(m.begin(), 6) == V(3, 1));
+      assert(m.get_allocator() == a);
+    }
+  }
 #endif
 
   return true;

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
index 22ac9bd799e45..ffb655fa8ade5 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/iter_iter_alloc.pass.cpp
@@ -48,7 +48,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*std::next(m.begin(), 7) == 3);
     assert(*std::next(m.begin(), 8) == 3);
   }
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};

diff  --git a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
index 60167a566145a..c15e05bcc309e 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/iter_iter_alloc.pass.cpp
@@ -46,7 +46,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
     assert(*std::next(m.begin()) == 2);
     assert(*std::next(m.begin(), 2) == 3);
   }
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   {
     typedef int V;
     V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};

diff  --git a/libcxx/test/std/containers/sequences/deque/deque.cons/size.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.cons/size.pass.cpp
index f8f42bd668f83..c31caa6c2d414 100644
--- a/libcxx/test/std/containers/sequences/deque/deque.cons/size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/deque/deque.cons/size.pass.cpp
@@ -9,6 +9,7 @@
 // <deque>
 
 // explicit deque(size_type n);
+// explicit deque(size_type n, const Allocator& a);
 
 #include "asan_testing.h"
 #include <deque>
@@ -22,7 +23,7 @@
 
 template <class T, class Allocator>
 void test2(unsigned n) {
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   typedef std::deque<T, Allocator> C;
   typedef typename C::const_iterator const_iterator;
   assert(DefaultOnly::count == 0);
@@ -62,7 +63,7 @@ void test1(unsigned n) {
 
 template <class T, class Allocator>
 void test3(unsigned n, Allocator const& alloc = Allocator()) {
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   typedef std::deque<T, Allocator> C;
   {
     C d(n, alloc);
@@ -102,7 +103,7 @@ int main(int, char**) {
   test<DefaultOnly, min_allocator<DefaultOnly> >(4095);
 #endif
 
-#if TEST_STD_VER > 11
+#if TEST_STD_VER >= 11
   test3<DefaultOnly, std::allocator<DefaultOnly>>(1023);
   test3<int, std::allocator<int>>(1);
   test3<int, min_allocator<int>>(3);

diff  --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
index 206854560c19f..89baa06dec475 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.cons/size.pass.cpp
@@ -6,6 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 // <forward_list>
 
 // explicit forward_list(size_type n);                 // constexpr since C++26
@@ -21,15 +23,10 @@
 
 template <class T, class Allocator>
 void check_allocator(unsigned n, Allocator const& alloc = Allocator()) {
-#if TEST_STD_VER > 11
   typedef std::forward_list<T, Allocator> C;
   C d(n, alloc);
   assert(d.get_allocator() == alloc);
   assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == n);
-#else
-  ((void)n);
-  ((void)alloc);
-#endif
 }
 
 int main(int, char**) {
@@ -46,14 +43,24 @@ int main(int, char**) {
     unsigned n = 0;
 
     for (C::const_iterator i = c.begin(), e = c.end(); i != e; ++i, ++n) {
-#if TEST_STD_VER >= 11
       assert(*i == T());
-#else
-      ((void)0);
-#endif
     }
     assert(n == N);
   }
+  {
+    typedef DefaultOnly T;
+    typedef std::forward_list<T, std::allocator<T> > C;
+    unsigned N = 10;
+    C c(N, std::allocator<T>());
+    unsigned n = 0;
+
+    for (C::const_iterator i = c.begin(), e = c.end(); i != e; ++i, ++n) {
+      assert(*i == T());
+    }
+    assert(n == N);
+    check_allocator<T, std::allocator<T> >(0);
+    check_allocator<T, std::allocator<T> >(3);
+  }
 #if TEST_STD_VER >= 11
   {
     typedef DefaultOnly T;

diff  --git a/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp b/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
index 55371e8354a9e..4992400c406d7 100644
--- a/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
+++ b/libcxx/test/std/containers/sequences/list/list.cons/size_type.pass.cpp
@@ -6,9 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 // <list>
 
-// explicit list(size_type n); // constexpr since C++26
+// explicit list(size_type n);                       // constexpr since C++26
+// explicit list(size_type n, const Allocator& a);   // constexpr since C++26
 
 #include <list>
 #include <cassert>
@@ -21,7 +24,6 @@
 
 template <class T, class Allocator>
 TEST_CONSTEXPR_CXX26 void test1(unsigned n, Allocator const& alloc = Allocator()) {
-#if TEST_STD_VER > 11
   typedef std::list<T, Allocator> C;
   {
     C d(n, alloc);
@@ -29,10 +31,6 @@ TEST_CONSTEXPR_CXX26 void test1(unsigned n, Allocator const& alloc = Allocator()
     assert(static_cast<std::size_t>(std::distance(d.begin(), d.end())) == n);
     assert(d.get_allocator() == alloc);
   }
-#else
-  ((void)n);
-  ((void)alloc);
-#endif
 }
 
 TEST_CONSTEXPR_CXX26 bool test() {
@@ -59,7 +57,19 @@ TEST_CONSTEXPR_CXX26 bool test() {
     ++i;
     assert(*i == 0);
   }
-#if TEST_STD_VER > 11
+  {
+    std::list<int, std::allocator<int> > l(3, std::allocator<int>());
+    assert(l.size() == 3);
+    assert(std::distance(l.begin(), l.end()) == 3);
+    std::list<int, std::allocator<int> >::const_iterator i = l.begin();
+    assert(*i == 0);
+    ++i;
+    assert(*i == 0);
+    ++i;
+    assert(*i == 0);
+    test1<int, std::allocator<int> >(3);
+  }
+#if TEST_STD_VER >= 11
   {
     typedef std::list<int, min_allocator<int> > C;
     C l(3, min_allocator<int>());

diff  --git a/libcxx/test/std/containers/sequences/vector.bool/construct_size.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/construct_size.pass.cpp
index 80b3fe307c93a..f97396d67ae9d 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/construct_size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector.bool/construct_size.pass.cpp
@@ -10,6 +10,7 @@
 // vector<bool>
 
 // explicit vector(size_type n);
+// explicit vector(size_type n, const Allocator& alloc = Allocator());
 
 #include <vector>
 #include <cassert>
@@ -21,7 +22,7 @@
 template <class C>
 TEST_CONSTEXPR_CXX20 void
 test2(typename C::size_type n, typename C::allocator_type const& a = typename C::allocator_type()) {
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 11
   C c(n, a);
   LIBCPP_ASSERT(c.__invariants());
   assert(c.size() == n);

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp
index cbae3653d995f..0902a4070b7aa 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_size.pass.cpp
@@ -38,7 +38,7 @@ test(typename C::size_type n, typename C::allocator_type const& a = typename C::
   }
 
   // Test with a custom allocator
-#if TEST_STD_VER >= 14
+#if TEST_STD_VER >= 11
   {
     C c(n, a);
     LIBCPP_ASSERT(c.__invariants());

diff  --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_allocator.pass.cpp
index e29ecea327971..3fd4e7fcfa5f5 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 

diff  --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_hash_allocator.pass.cpp
index c2adf98c7eaca..31d4ce0adf437 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/init_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 

diff  --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_allocator.pass.cpp
index 1039e1d92be79..bf1536dd0b44a 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 

diff  --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_hash_allocator.pass.cpp
index b6e06df9c8fe2..a5f9ddeb0d825 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/iter_iter_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 

diff  --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_allocator.pass.cpp
index 450fb7b5e40d4..8e4d7ff8e1fbe 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 

diff  --git a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_hash_allocator.pass.cpp
index a9f73f3f45fe2..19bbb305fe10f 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.cnstr/size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 

diff  --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_allocator.pass.cpp
index 7ec2dd2efb895..a8371367f7ead 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 

diff  --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_hash_allocator.pass.cpp
index eab5d60fbf6f4..ec566bb75a2f9 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/init_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 

diff  --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_allocator.pass.cpp
index ad6ad9d1ad1ef..5e694b5d5ed3a 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 

diff  --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_hash_allocator.pass.cpp
index ee35b36cf7519..7592b10b792ec 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/iter_iter_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 

diff  --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_allocator.pass.cpp
index d49be5518922a..70798e3920a7d 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 

diff  --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_hash_allocator.pass.cpp
index f26fae2a2a81d..42d75cfa28ba0 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.cnstr/size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_map>
 

diff  --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_allocator.pass.cpp
index bb0cc2441bfb7..314e60381dfcc 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 

diff  --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_hash_allocator.pass.cpp
index 24b802f4fcca2..f962ddd8f0344 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/init_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 

diff  --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_allocator.pass.cpp
index 994ec058fae94..6eb48201d116c 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 

diff  --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_hash_allocator.pass.cpp
index 138af889d5978..44e572e10071f 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/iter_iter_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 

diff  --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_allocator.pass.cpp
index e536644470122..ddf0a4d02b932 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 

diff  --git a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_hash_allocator.pass.cpp
index 6ea22dab1aac9..531215e0671e3 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/unord.multiset.cnstr/size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 

diff  --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_allocator.pass.cpp
index 7346a951b1976..90c6d9720e90c 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 

diff  --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_hash_allocator.pass.cpp
index 704fff0f51cca..306ec62c81d7b 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/init_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 

diff  --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_allocator.pass.cpp
index 414485466d71e..bdd2d841283a6 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 

diff  --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_hash_allocator.pass.cpp
index ba986eba5f71c..48d64a8860e23 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/iter_iter_size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 

diff  --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_allocator.pass.cpp
index 99abd12745eca..6af94925a5a2b 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 

diff  --git a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_hash_allocator.pass.cpp b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_hash_allocator.pass.cpp
index e44854eb0a132..11c3e7bd2ee0c 100644
--- a/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_hash_allocator.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/unord.set.cnstr/size_hash_allocator.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11
+// UNSUPPORTED: c++03
 
 // <unordered_set>
 


        

From libcxx-commits at lists.llvm.org  Thu Jul  9 16:46:41 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Thu, 09 Jul 2026 16:46:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Enable missing allocator
	constructors in C++11 (PR #204845)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204845@github.com>
Message-ID: <6a5032e1.07d7ca98.9dee3.62d6@mx.google.com>


https://github.com/frederick-vs-ja closed https://github.com/llvm/llvm-project/pull/204845

From libcxx-commits at lists.llvm.org  Thu Jul  9 18:13:24 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 18:13:24 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a504734.3cce8983.225732.a3cf@mx.google.com>


https://github.com/huihui15537226823 updated https://github.com/llvm/llvm-project/pull/208390

>From 70e8ac839667f8aba4aa74a2dd19614426f9754f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 24 Jun 2026 09:47:00 +0800
Subject: [PATCH 1/6] Add OHOS HDC runtimes CI support

---
 cmake/Modules/GetToolchainDirs.cmake          |  37 +-
 .../cmake/Modules/CompilerRTUtils.cmake       |   8 +-
 compiler-rt/cmake/builtin-config-ix.cmake     |   2 +-
 compiler-rt/cmake/crt-config-ix.cmake         |   2 +-
 compiler-rt/lib/builtins/crtbegin.c           |   9 +
 compiler-rt/lib/builtins/emutls.c             |   8 +-
 libcxx/CMakeLists.txt                         |  10 +-
 libcxx/cmake/config-ix.cmake                  |   2 +
 libcxx/include/__config                       |   5 +-
 libcxx/include/__cxx03/__config               |   5 +-
 .../__cxx03/__locale_dir/locale_base_api.h    |   2 +-
 libcxx/include/__locale_dir/locale_base_api.h |   2 +-
 libcxx/src/include/config_elast.h             |   2 +
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  48 ++
 libcxx/utils/ci/buildkite-pipeline.yml        |  27 +
 libcxx/utils/ci/run-buildbot                  |  87 ++++
 libcxx/utils/ci/vendor/ohos/hdc_run.py        | 472 ++++++++++++++++++
 libcxx/utils/ci/vendor/ohos/hdc_run_test.py   | 115 +++++
 libcxxabi/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |  30 ++
 libunwind/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |  55 ++
 22 files changed, 910 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/test/configs/llvm-libc++-ohos.cfg.in
 create mode 100755 libcxx/utils/ci/vendor/ohos/hdc_run.py
 create mode 100644 libcxx/utils/ci/vendor/ohos/hdc_run_test.py
 create mode 100644 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
 create mode 100644 libunwind/test/configs/llvm-libunwind-ohos.cfg.in

diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..a20ed9ce9c6a9
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,48 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index d4107cf11ad51..77617f43ae6dc 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -172,3 +172,30 @@ steps:
       queue: libcxx-builders
       os: android
     <<: *common
+
+- group: OHOS
+  steps:
+  - label: OHOS AArch64 HDC Smoke
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
+  - label: OHOS AArch64 HDC Full
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 6376bc1d3dd5a..1867e9c620649 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -116,6 +116,41 @@ function clean() {
     rm -rf "${BUILD_DIR}"
 }
 
+function require-env-var() {
+    local name="${1}"
+    if [[ -z ${!name+x} ]] || [[ -z "${!name}" ]]; then
+        error "Environment variable ${name} must be defined"
+        exit 1
+    fi
+}
+
+function detect-lit() {
+    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
+        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
+    elif command -v llvm-lit >/dev/null 2>&1; then
+        LIT_RUNNER=("$(command -v llvm-lit)")
+    else
+        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
+    fi
+}
+
+function run-smoke-test() {
+    local test_dir="${1}"
+    local filter="${2}"
+    step "Running smoke test ${filter}"
+    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
+}
+
+function ohos-lit-args() {
+    local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
+    if python3 -c 'import psutil' >/dev/null 2>&1; then
+        args="${args} --timeout=1500"
+    else
+        echo "warning: python3 psutil not available; running OHOS lit without per-test timeout" >&2
+    fi
+    printf '%s' "${args}"
+}
+
 function generate-cmake-base() {
     step "Generating CMake"
 
@@ -153,6 +188,37 @@ function generate-cmake-android() {
           "${@}"
 }
 
+function setup-ohos-hdc-env() {
+    require-env-var OHOS_SYSROOT
+
+    OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+}
+
+function generate-cmake-ohos-hdc() {
+    setup-ohos-hdc-env
+
+    local lit_args
+    lit_args="$(ohos-lit-args)"
+
+    generate-cmake \
+          -DLLVM_LIT_ARGS="${lit_args}" \
+          -DCMAKE_SYSTEM_NAME=OHOS \
+          -DCMAKE_SYSROOT="${OHOS_SYSROOT}" \
+          -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
+          -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
+          -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
+          -DLIBUNWIND_TEST_CONFIG="llvm-libunwind-ohos.cfg.in" \
+          -DLIBCXX_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBCXXABI_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBUNWIND_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          "${@}"
+}
+
 function check-runtimes() {
     step "Building libc++ test dependencies"
     ninja -vC "${BUILD_DIR}" cxx-test-depends
@@ -795,6 +861,27 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
+ohos-hdc-aarch64-full)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    check-runtimes
+;;
+ohos-hdc-aarch64-smoke)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    step "Building the OHOS smoke test dependencies"
+    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
+
+    detect-lit
+    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
+;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run.py b/libcxx/utils/ci/vendor/ohos/hdc_run.py
new file mode 100755
index 0000000000000..61a3899b88b11
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run.py
@@ -0,0 +1,472 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import uuid
+
+
+EXIT_MARKER = "__llvm_hdc_exit__="
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _split_path_list(value):
+    return [entry for entry in value.split(os.pathsep) if entry]
+
+
+def _resolve_hdc_binary(hdc):
+    if os.path.isabs(hdc):
+        return hdc
+    resolved = shutil.which(hdc)
+    if resolved is None:
+        sys.exit(f"failed to find hdc binary: {hdc}")
+    return resolved
+
+
+def _is_windows_hdc(path):
+    return path.lower().endswith(".exe")
+
+
+def _find_windows_local_root(hdc_path):
+    parts = Path(hdc_path).parts
+    for index in range(len(parts) - 1):
+        if parts[index] == "AppData" and index + 1 < len(parts) and parts[index + 1] == "Local":
+            return Path(*parts[: index + 2])
+    return None
+
+
+def _default_host_staging_root(hdc_path):
+    if _is_windows_hdc(hdc_path):
+        local_root = _find_windows_local_root(hdc_path)
+        if local_root is None:
+            sys.exit(
+                "failed to derive a Windows staging directory from the hdc.exe path; "
+                "set HDC_HOST_STAGING_ROOT to a path under /mnt/<drive>/..."
+            )
+        return local_root / "Temp" / "llvm-lit-hdc"
+    return Path("/tmp/llvm-lit-hdc")
+
+
+def _to_windows_path(path):
+    resolved = Path(path).resolve()
+    parts = resolved.parts
+    if len(parts) < 4 or parts[1] != "mnt":
+        sys.exit(
+            f"Windows hdc requires a staging path under /mnt/<drive>/..., got: {resolved}"
+        )
+    drive = parts[2].upper()
+    tail = "\\".join(parts[3:])
+    return f"{drive}:\\{tail}" if tail else f"{drive}:\\"
+
+
+def _run_readelf(path):
+    completed = subprocess.run(
+        ["readelf", "-d", path],
+        check=False,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        text=True,
+    )
+    if completed.returncode != 0:
+        return ""
+    return completed.stdout
+
+
+def _read_elf_metadata(path):
+    needed = []
+    runpath_dirs = []
+    seen_dirs = set()
+    for line in _run_readelf(path).splitlines():
+        needed_match = re.search(r"\(NEEDED\).*Shared library: \[(.*)\]", line)
+        if needed_match:
+            needed.append(needed_match.group(1))
+            continue
+
+        runpath_match = re.search(
+            r"\((?:RUNPATH|RPATH)\).*Library runpath: \[(.*)\]", line
+        )
+        if not runpath_match:
+            continue
+        for entry in _split_path_list(runpath_match.group(1)):
+            candidate = Path(entry)
+            if not candidate.is_dir():
+                continue
+            key = str(candidate.resolve())
+            if key in seen_dirs:
+                continue
+            seen_dirs.add(key)
+            runpath_dirs.append(candidate)
+    return needed, runpath_dirs
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(parent)
+    return library_dirs
+
+
+def _find_dependency(name, search_dirs):
+    for directory in search_dirs:
+        candidate = Path(directory) / name
+        if candidate.is_file():
+            return candidate.resolve()
+    return None
+
+
+def _copy_needed_libraries(executables, execdir, staging_execdir):
+    search_dirs = _collect_build_library_dirs(execdir)
+    queue = [Path(exe).resolve() for exe in executables]
+    visited = set()
+    copied_names = {path.name for path in staging_execdir.glob("*") if path.is_file()}
+
+    while queue:
+        current = queue.pop(0)
+        current_key = str(current)
+        if current_key in visited:
+            continue
+        visited.add(current_key)
+
+        needed, runpath_dirs = _read_elf_metadata(current)
+        current_search_dirs = []
+        current_seen = set()
+        for directory in [*runpath_dirs, current.parent, *search_dirs]:
+            resolved = Path(directory).resolve()
+            key = str(resolved)
+            if key in current_seen:
+                continue
+            current_seen.add(key)
+            current_search_dirs.append(resolved)
+
+        for directory in runpath_dirs:
+            resolved = directory.resolve()
+            if all(str(resolved) != str(existing) for existing in search_dirs):
+                search_dirs.append(resolved)
+
+        for lib_name in needed:
+            dependency = _find_dependency(lib_name, current_search_dirs)
+            if dependency is None:
+                continue
+            if lib_name not in copied_names:
+                shutil.copy2(dependency, staging_execdir / lib_name)
+                copied_names.add(lib_name)
+            queue.append(dependency)
+
+
+def _copy_execdir(execdir, staging_execdir):
+    if staging_execdir.exists():
+        shutil.rmtree(staging_execdir)
+    shutil.copytree(execdir, staging_execdir)
+
+
+def _iter_files(root):
+    for directory, dirnames, filenames in os.walk(root):
+        dirnames.sort()
+        filenames.sort()
+        yield Path(directory), filenames
+
+
+def _is_test_executable(path):
+    return path.endswith(".tmp.exe") and os.path.exists(path)
+
+
+def _map_local_path(path, local_execdir, remote_execdir):
+    normalized = os.path.normpath(path)
+    execdir = os.path.normpath(local_execdir)
+    if normalized == execdir:
+        return remote_execdir
+    prefix = execdir + os.sep
+    if normalized.startswith(prefix):
+        suffix = normalized[len(prefix) :].replace(os.sep, "/")
+        return f"{remote_execdir}/{suffix}" if suffix else remote_execdir
+    return path
+
+
+def _map_value(value, local_execdir, remote_execdir):
+    return _map_local_path(value, local_execdir, remote_execdir)
+
+
+def _build_env_exports(env, prepend_env, local_execdir, remote_execdir, add_remote_libdir):
+    commands = []
+    mapped_env = {
+        key: _map_value(value, local_execdir, remote_execdir) for key, value in env.items()
+    }
+    mapped_prepend = {
+        key: _map_value(value, local_execdir, remote_execdir)
+        for key, value in prepend_env.items()
+    }
+
+    if add_remote_libdir:
+        existing = mapped_prepend.get("LD_LIBRARY_PATH")
+        mapped_prepend["LD_LIBRARY_PATH"] = (
+            f"{remote_execdir}:{existing}" if existing else remote_execdir
+        )
+
+    for key, value in mapped_env.items():
+        commands.append(f"export {key}={shlex.quote(value)}")
+    for key, value in mapped_prepend.items():
+        commands.append(f"export {key}={shlex.quote(value)}${{{key}:+:{key}}}")
+    return commands
+
+
+def _hdc_prefix(hdc_path, server, target):
+    prefix = [hdc_path]
+    if server:
+        prefix.extend(["-s", server])
+    if target:
+        prefix.extend(["-t", target])
+    return prefix
+
+
+def _run_hdc(
+    prefix,
+    args,
+    *,
+    timeout=None,
+    capture_output=False,
+    stdin=subprocess.DEVNULL,
+    check=True,
+    expect_stdout=None,
+):
+    kwargs = {
+        "check": False,
+        "timeout": timeout,
+        "stdin": stdin,
+        "text": True,
+    }
+    if capture_output:
+        kwargs["stdout"] = subprocess.PIPE
+        kwargs["stderr"] = subprocess.PIPE
+    completed = subprocess.run(prefix + args, **kwargs)
+    output = completed.stdout if capture_output and completed.stdout else ""
+    error_output = completed.stderr if capture_output and completed.stderr else ""
+    if check and completed.returncode != 0:
+        raise RuntimeError(
+            f"hdc command failed with exit code {completed.returncode}: "
+            f"{shlex.join(prefix + args)}\n{output}{error_output}"
+        )
+    if check and expect_stdout is not None and expect_stdout not in output:
+        raise RuntimeError(
+            f"hdc command did not report success: {shlex.join(prefix + args)}\n{output}"
+        )
+    return completed
+
+
+def _run_checked_shell(prefix, command, *, timeout=None):
+    completed = _run_hdc(
+        prefix,
+        [
+            "shell",
+            f"{command}; rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"",
+        ],
+        timeout=timeout,
+        capture_output=True,
+    )
+    exit_code, output = _extract_exit_code(completed.stdout)
+    if exit_code is None:
+        raise RuntimeError(
+            f"failed to parse shell exit code for: {command}\n{completed.stdout}"
+        )
+    if exit_code != 0:
+        raise RuntimeError(
+            f"remote shell command failed with exit code {exit_code}: {command}\n{output}"
+        )
+    return output
+
+
+def _sync_tree_to_device(prefix, local_root, remote_root, use_windows_paths):
+    _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_root)}")
+    _run_checked_shell(prefix, f"mkdir -p {shlex.quote(remote_root)}")
+
+    for directory, filenames in _iter_files(local_root):
+        relative_dir = os.path.relpath(directory, local_root)
+        device_dir = (
+            remote_root
+            if relative_dir == "."
+            else f"{remote_root}/{relative_dir.replace(os.sep, '/')}"
+        )
+        _run_checked_shell(prefix, f"mkdir -p {shlex.quote(device_dir)}")
+        for name in filenames:
+            local_path = directory / name
+            device_path = f"{device_dir}/{name}"
+            source = _to_windows_path(local_path) if use_windows_paths else str(local_path)
+            _run_hdc(
+                prefix,
+                ["file", "send", source, device_path],
+                capture_output=True,
+                expect_stdout="FileTransfer finish",
+            )
+            if name.endswith(".tmp.exe") or os.access(local_path, os.X_OK):
+                _run_checked_shell(prefix, f"chmod +x {shlex.quote(device_path)}")
+
+
+def _build_remote_command(command, local_execdir, remote_execdir, exports):
+    mapped_command = [
+        _map_local_path(arg, local_execdir, remote_execdir) for arg in command
+    ]
+    pieces = [f"cd {shlex.quote(remote_execdir)}"]
+    pieces.extend(exports)
+    pieces.append(shlex.join(mapped_command))
+    pieces.append(f"rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"")
+    return " && ".join(pieces[:-2]) + f"; {pieces[-2]}; {pieces[-1]}"
+
+
+def _extract_exit_code(output):
+    match = re.search(rf"^(.*)\n{EXIT_MARKER}(\d+)\s*$", output, re.DOTALL)
+    if not match:
+        return None, output
+    return int(match.group(2)), match.group(1)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--hdc", default=os.environ.get("HDC", "hdc"))
+    parser.add_argument("--hdc-server", default=os.environ.get("HDC_SERVER_IP_PORT"))
+    parser.add_argument("--hdc-target", default=os.environ.get("HDC_UTID"))
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--remote-base-dir",
+        default="/data/local/tmp/llvm-lit-hdc",
+    )
+    parser.add_argument(
+        "--host-staging-root",
+        default=os.environ.get("HDC_HOST_STAGING_ROOT"),
+    )
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(os.environ.get("HDC_TIMEOUT_SECONDS", "120")),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("--keep-remote-dir", action="store_true")
+    parser.add_argument("--keep-host-staging", action="store_true")
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    hdc_path = _resolve_hdc_binary(args.hdc)
+    use_windows_paths = _is_windows_hdc(hdc_path)
+    host_staging_root = (
+        Path(args.host_staging_root)
+        if args.host_staging_root
+        else _default_host_staging_root(hdc_path)
+    )
+    prefix = _hdc_prefix(hdc_path, args.hdc_server, args.hdc_target)
+
+    execdir = Path(args.execdir).resolve()
+    run_id = uuid.uuid4().hex[:12]
+    staging_execdir = host_staging_root / f"run-{run_id}" / execdir.name
+    remote_execdir = f"{args.remote_base_dir}/run-{run_id}/{execdir.name}"
+
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    executables = [arg for arg in args.command if _is_test_executable(arg)]
+    host_staging_root.mkdir(parents=True, exist_ok=True)
+    _copy_execdir(execdir, staging_execdir)
+    _copy_needed_libraries(executables, execdir, staging_execdir)
+
+    add_remote_libdir = any(staging_execdir.glob("*.so*"))
+    exports = _build_env_exports(
+        env,
+        prepend_env,
+        str(execdir),
+        remote_execdir,
+        add_remote_libdir,
+    )
+
+    try:
+        _sync_tree_to_device(prefix, staging_execdir, remote_execdir, use_windows_paths)
+        remote_command = _build_remote_command(
+            args.command,
+            str(execdir),
+            remote_execdir,
+            exports,
+        )
+        try:
+            completed = _run_hdc(
+                prefix,
+                ["shell", remote_command],
+                timeout=args.timeout_seconds,
+                capture_output=True,
+                stdin=None,
+            )
+        except subprocess.TimeoutExpired:
+            print(
+                f"hdc executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+                file=sys.stderr,
+            )
+            return 124
+
+        exit_code, output = _extract_exit_code(completed.stdout)
+        if exit_code is None:
+            print(
+                "failed to parse exit code from hdc shell output",
+                file=sys.stderr,
+            )
+            if completed.stdout:
+                print(completed.stdout, end="", file=sys.stderr)
+            if completed.stderr:
+                print(completed.stderr, end="", file=sys.stderr)
+            return 1
+
+        if output:
+            sys.stdout.write(output)
+        if completed.stderr:
+            sys.stderr.write(completed.stderr)
+        return exit_code
+    finally:
+        if not args.keep_remote_dir:
+            try:
+                _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_execdir)}")
+            except Exception:
+                pass
+        if not args.keep_host_staging:
+            shutil.rmtree(staging_execdir.parent, ignore_errors=True)
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run_test.py b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
new file mode 100644
index 0000000000000..17e2f65bd1fb6
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import os
+from pathlib import Path
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import unittest
+
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+HDC_RUN = SCRIPT_DIR / "hdc_run.py"
+
+
+class HdcRunTest(unittest.TestCase):
+    def make_fake_hdc(self, directory):
+        fake_hdc = directory / "hdc"
+        fake_hdc.write_text(
+            textwrap.dedent(
+                """\
+                #!/usr/bin/env python3
+                import sys
+
+                EXIT_MARKER = "__llvm_hdc_exit__="
+
+                def main():
+                    args = sys.argv[1:]
+                    if args[:2] == ["file", "send"]:
+                        print("FileTransfer finish")
+                        return 0
+
+                    if args and args[0] == "shell":
+                        command = args[1]
+                        if "stderr.tmp.exe" in command:
+                            sys.stdout.write("STDOUT-OUTPUT")
+                            sys.stderr.write("STDERR-OUTPUT")
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        if "stdin.tmp.exe" in command:
+                            sys.stdout.write("IN:" + sys.stdin.read())
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                        return 0
+
+                    print("unexpected fake hdc invocation: " + repr(args), file=sys.stderr)
+                    return 2
+
+                if __name__ == "__main__":
+                    raise SystemExit(main())
+                """
+            )
+        )
+        fake_hdc.chmod(fake_hdc.stat().st_mode | stat.S_IXUSR)
+        return fake_hdc
+
+    def run_hdc_run(self, temp_dir, executable_name, *, stdin_text=None):
+        execdir = temp_dir / "execdir"
+        execdir.mkdir()
+        executable = execdir / executable_name
+        executable.write_text("# fake executable marker\n")
+        executable.chmod(executable.stat().st_mode | stat.S_IXUSR)
+
+        fake_hdc = self.make_fake_hdc(temp_dir)
+        return subprocess.run(
+            [
+                sys.executable,
+                str(HDC_RUN),
+                "--hdc",
+                str(fake_hdc),
+                "--execdir",
+                str(execdir),
+                "--host-staging-root",
+                str(temp_dir / "staging"),
+                str(executable),
+            ],
+            input=stdin_text,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+    def test_preserves_remote_stdout_and_stderr_separately(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(Path(temp), "stderr.tmp.exe")
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "STDOUT-OUTPUT")
+        self.assertEqual(completed.stderr, "STDERR-OUTPUT")
+
+    def test_forwards_stdin_to_remote_command(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(
+                Path(temp),
+                "stdin.tmp.exe",
+                stdin_text="hello from stdin",
+            )
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "IN:hello from stdin")
+        self.assertEqual(completed.stderr, "")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt
index e1a1587fb6283..cb22e6fe65108 100644
--- a/libcxxabi/CMakeLists.txt
+++ b/libcxxabi/CMakeLists.txt
@@ -187,7 +187,12 @@ set(LIBCXXABI_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXXABI_TARGET_SUBDIR)
+  else()
+    set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXXABI_LIBDIR_SUBDIR)
     string(APPEND LIBCXXABI_TARGET_SUBDIR /${LIBCXXABI_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
new file mode 100644
index 0000000000000..d18748fe3a3cc
--- /dev/null
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -0,0 +1,30 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++abi using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -I %{libcxx}/test/support -I %{libcxx}/src -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    '-nostdlib++ -L %{lib} -lc++ -lc++abi -pthread'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 02132f6c07fcd..4fd11bf5aca2f 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -142,7 +142,12 @@ set(LIBUNWIND_STATIC_OUTPUT_NAME "unwind" CACHE STRING "Output name for the stat
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBUNWIND_TARGET_SUBDIR)
+  else()
+    set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBUNWIND_LIBDIR_SUBDIR)
     string(APPEND LIBUNWIND_TARGET_SUBDIR /${LIBUNWIND_LIBDIR_SUBDIR})
   endif()
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
new file mode 100644
index 0000000000000..bce241a7898cf
--- /dev/null
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -0,0 +1,55 @@
+#
+# Configuration file for running the libunwind tests against the shared library
+# using HDC on OHOS.
+#
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+compile_flags = []
+link_flags = []
+
+if @HAVE_GETAUXVAL@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_GETAUXVAL')
+
+if @HAVE_ELF_AUX_INFO@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_ELF_AUX_INFO')
+
+if @LIBUNWIND_ENABLE_CET@:
+    compile_flags.append('-fcf-protection=full')
+
+if @LIBUNWIND_ENABLE_GCS@:
+    compile_flags.append('-mbranch-protection=standard')
+
+if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
+    link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
+
+if '@CMAKE_DL_LIBS@':
+    link_flags.append('-l at CMAKE_DL_LIBS@')
+
+compile_flags.append('-funwind-tables')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags))
+))
+config.substitutions.append(('%{link_flags}',
+    '-L %{{lib}} -lunwind {}'.format(' '.join(link_flags))
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)

>From e9e5d2e517fb072f3554798e49b7119ec739105f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 25 Jun 2026 11:30:38 +0800
Subject: [PATCH 2/6] Fix OHOS libc++ CI ABI and SME detection

---
 libcxx/utils/ci/run-buildbot |  2 ++
 libunwind/src/Registers.hpp  | 15 +++++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 1867e9c620649..ecf0ca17ae148 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -192,6 +192,7 @@ function setup-ohos-hdc-env() {
     require-env-var OHOS_SYSROOT
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
     OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
     OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
 }
@@ -209,6 +210,7 @@ function generate-cmake-ohos-hdc() {
           -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE}" \
           -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
           -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
           -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c88422a6d5f4f..18cb2a523bd0e 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -25,6 +25,11 @@
 #endif
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
+#if defined(__has_include)
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
+#endif
 #endif
 
 namespace libunwind {
@@ -1951,18 +1956,16 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
       return false;
     return has_sme != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = getauxval(AT_HWCAP2);
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = 0;
     elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
 #else
   static bool checkHasSME() {

>From 498fa8d127ff49c27d4230996338c6eb5d747fe6 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 1 Jul 2026 15:49:11 +0800
Subject: [PATCH 3/6] Stabilize OHOS HDC runtimes tests

Add OHOS lit features for HDC executor and platform limitations, mark affected libc++ tests as unsupported or expected failures, and adjust test support for OHOS filesystem cleanup and allocation counting behavior.

Also guard the aarch64 ZA unwind test on HWCAP2_SME so devices without a reliable SME feature definition do not execute SME instructions.
---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  9 +++++-
 .../clang/clang_modules_include.gen.py        |  3 ++
 .../locale.facet/no_allocation.pass.cpp       |  1 +
 libcxx/test/selftest/dsl/dsl.sh.py            |  5 ++++
 libcxx/test/selftest/stdin-is-piped.sh.cpp    |  1 +
 .../directory_entry.cons/path.pass.cpp        |  1 +
 .../directory_entry.mods/assign.pass.cpp      |  1 +
 .../directory_entry.mods/refresh.pass.cpp     |  1 +
 .../replace_filename.pass.cpp                 |  1 +
 .../directory_entry.obs/file_size.pass.cpp    |  1 +
 .../file_type_obs.pass.cpp                    |  1 +
 .../hard_link_count.pass.cpp                  |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../directory_iterator.members/ctor.pass.cpp  |  1 +
 .../path.member/path.assign/move.pass.cpp     |  2 +-
 .../path.member/path.construct/move.pass.cpp  |  2 +-
 .../rec.dir.itr.members/ctor.pass.cpp         |  1 +
 .../rec.dir.itr.members/increment.pass.cpp    |  1 +
 .../fs.op.funcs/fs.op.exists/exists.pass.cpp  |  1 +
 .../is_block_file.pass.cpp                    |  1 +
 .../is_character_file.pass.cpp                |  1 +
 .../fs.op.is_directory/is_directory.pass.cpp  |  1 +
 .../fs.op.is_empty/is_empty.pass.cpp          |  1 +
 .../fs.op.is_fifo/is_fifo.pass.cpp            |  1 +
 .../fs.op.is_other/is_other.pass.cpp          |  1 +
 .../is_regular_file.pass.cpp                  |  1 +
 .../fs.op.is_socket/is_socket.pass.cpp        |  1 +
 .../fs.op.is_symlink/is_symlink.pass.cpp      |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../fs.op.funcs/fs.op.remove/remove.pass.cpp  |  1 +
 .../fs.op.remove_all/remove_all.pass.cpp      |  1 +
 .../fs.op.funcs/fs.op.status/status.pass.cpp  |  1 +
 .../symlink_status.pass.cpp                   |  1 +
 .../temp_directory_path.pass.cpp              |  1 +
 .../ext.manip/get_money.pass.cpp              |  1 +
 .../ext.manip/put_money.pass.cpp              |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 .../print.fun/no_file_description.pass.cpp    |  3 ++
 .../std.manip/setfill_wchar_max.pass.cpp      |  1 +
 .../narrow.stream.objects/cerr.sh.cpp         |  1 +
 .../narrow.stream.objects/cin.sh.cpp          |  1 +
 .../narrow.stream.objects/clog.sh.cpp         |  1 +
 .../wide.stream.objects/wcerr-imbue.sh.cpp    |  1 +
 .../wide.stream.objects/wcerr.sh.cpp          |  1 +
 .../wide.stream.objects/wcin-imbue.sh.cpp     |  1 +
 .../wide.stream.objects/wcin.sh.cpp           |  1 +
 .../wide.stream.objects/wclog.sh.cpp          |  1 +
 .../sized_delete_array.pass.cpp               |  1 +
 ..._array_calls_unsized_delete_array.pass.cpp |  1 +
 .../new.delete.single/sized_delete.pass.cpp   |  1 +
 ...sized_delete_calls_unsized_delete.pass.cpp |  1 +
 .../locale.collate.byname/compare.pass.cpp    |  1 +
 .../locale.collate.byname/transform.pass.cpp  |  1 +
 .../facet.ctype.char.dtor/dtor.pass.cpp       |  1 +
 .../locale.ctype.byname/is_1.pass.cpp         |  1 +
 .../locale.ctype.byname/is_many.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_is.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_not.pass.cpp     |  1 +
 .../locale.ctype.byname/tolower_1.pass.cpp    |  1 +
 .../locale.ctype.byname/tolower_many.pass.cpp |  1 +
 .../locale.ctype.byname/toupper_1.pass.cpp    |  1 +
 .../locale.ctype.byname/toupper_many.pass.cpp |  1 +
 .../locale.ctype.byname/widen_1.pass.cpp      |  1 +
 .../locale.ctype.byname/widen_many.pass.cpp   |  1 +
 .../get_long_double_en_US.pass.cpp            |  1 +
 .../get_long_double_zh_CN.pass.cpp            |  1 +
 .../get_string_en_US.pass.cpp                 |  1 +
 .../put_long_double_en_US.pass.cpp            |  1 +
 .../put_long_double_zh_CN.pass.cpp            |  1 +
 .../put_string_en_US.pass.cpp                 |  1 +
 .../locale/locale.cons/default.pass.cpp       |  1 +
 .../test/std/re/re.traits/transform.pass.cpp  |  1 +
 .../re/re.traits/transform_primary.pass.cpp   |  1 +
 .../thread_create_failure.pass.cpp            |  1 +
 .../thread.jthread/join.deadlock.pass.cpp     |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 libcxx/test/support/count_new.h               | 28 +++++++++++++++++++
 libcxx/test/support/filesystem_test_helper.h  |  4 ++-
 libcxx/test/support/test_macros.h             |  4 ++-
 libunwind/test/aarch64_za_unwind.pass.cpp     | 13 +++++----
 80 files changed, 132 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index a20ed9ce9c6a9..542bc0cc83a1f 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -7,7 +7,8 @@ config.substitutions.append(('%{flags}',
     ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
 ))
 config.substitutions.append(('%{compile_flags}',
-    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
 ))
 config.substitutions.append(('%{link_flags}',
     # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
@@ -24,6 +25,12 @@ import libcxx.test.params, libcxx.test.config
 
 config.available_features.add('LIBCXX-OHOS-FIXME')
 config.available_features.add('ohos-hdc')
+config.available_features.add('executor-has-no-stdin')
+config.available_features.add('executor-merges-stderr-into-stdout')
+config.available_features.add('executor-runs-as-root')
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
index 20a545b719ed6..f04a6bce76691 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
index 03e9de882ab0a..428d30804a4c3 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
index 10829f8a73db1..c8a1c34aa3028 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index 071ee7f6c891f..44b94528f5e32 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a hard link.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
index 5d931891dd0eb..d291ca2cb5641 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
index 82b94a3d6bf5f..e4bc9c41728da 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
index 8401dbd38cd71..e0fa44ffbc4a5 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
index ac5499cdd6f20..2bcc64a3c2651 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_assignable<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
index 1f1843f7d401d..5efdf983f8a10 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_constructible<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
index 08d7ff84378d5..a3768b8cbd83f 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
index 1f2293989b7ef..9e8a70b4339d4 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // On Android L, ~scoped_test_env() is unable to delete the temp dir using
 // chmod+rm because chmod is too broken.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
index 56465d01021ad..62dc696d6797d 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
index fad880ecf30f7..708fb511dce73 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
index 12bc0c198c062..69e357adeafb5 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
index f1b559cf2d55b..0ed4aa0dc8d58 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
index f3161f2e020d2..579c03f2b8c0b 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
index fbf5e7129f7af..1d5312d65be46 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
index d0974640429df..7a3a56a0b161c 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
index 4a5882e36cde2..8b877ff63b112 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
index 382685c21dd99..995125d702de3 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
index 07b446d80c402..692fe773a0f9e 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
index c420fea21aa66..22c2c114ba687 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
index 56b4a27de6b42..8064feaaf2065 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
index b30d56685e2cb..540849ccefc05 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
index 425c224606038..02ff1e249d3bc 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
index 1d405a816e110..41474f9ca1e48 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
index 38e14438f3fd3..9a891eab6b343 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
index 88672851f7b6b..3e1d34ab5257a 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
index d0ff2c87689d1..4c299b876639e 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
index 98fa56b7f4b6d..7970887bac199 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
index 283a8fddb8e8f..13e6f74c5a143 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -17,6 +17,9 @@
 // fmemopen is available starting in Android M (API 23)
 // XFAIL: target={{.+}}-android{{(eabi)?(21|22)}}
 
+// OHOS fmemopen/FILE behavior does not currently match the expectation tested here.
+// XFAIL: LIBCXX-OHOS-FIXME
+
 // <print>
 
 // The FILE returned by fmemopen does not have file descriptor.
diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
index b24c0b90d86f1..2ddddbef70dbb 100644
--- a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
@@ -18,6 +18,7 @@
 // XFAIL: target=armv{{7|8}}{{l?}}{{.*}}-linux-gnueabihf && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-linux-gnu && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-amazon-linux && libcpp-abi-version=1
+// XFAIL: target={{.*}}-linux-ohos && libcpp-abi-version=1
 
 #include <iomanip>
 #include <ostream>
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
index da5563f87df45..ea5af90a90822 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
index 4f23b6b9a5d87..6dc0ff36ff4bf 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
@@ -14,6 +14,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
index 9bedd67e816ef..cbb9639766207 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
index ec4c8a009ba0c..59d93e74db3b7 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
index ae82035ccfc62..49a6ce0892ea4 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
index 6ae1972234b2b..58a6e18340ddf 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
index 08b74b999bbf4..88dcfe0485429 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
index 89e178248ae50..9c9745dcc9777 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wclog;
 
 // XFAIL: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
index 0e411f8adc0f2..e563152f2c8cd 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
index 026250daeee89..34119303a29ba 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete [] is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
index ca0f612bf7578..a4e32aaa11e13 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
index fbc9cf070bcba..3ec2b47b53eb0 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
index 8ae6bc2d3ba66..7cfd9f955fce7 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
@@ -8,6 +8,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
index 4978db116ef39..743075ede8ece 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
index 7be7e8d574e6c..b77a4409522f0 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
@@ -11,6 +11,7 @@
 // template <> class ctype<char>
 
 // ~ctype();
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <locale>
 #include <cassert>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
index 5cebc079c4703..c4803c04d173b 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <type_traits>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
index 2869378b42b92..5174ffebf281f 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
index ea03f042f2d45..665da7323e38a 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
index 2f7acb843e591..5d87c081b6e1c 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
index 447353de8af72..93d93aa9228f7 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
index 94ecc6c8fa85b..ab448a01632d6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
index 2b37737a26bc3..4be990e5b88c6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
index 016d8d736ad22..e0a3912a9315e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
index 959a4be9e1de9..b22629c82444e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
index 078b4a6fefb7e..c2cd904808794 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
index 9997b07134563..73b7a6decda36 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
index c86df7e6b53bf..deb30d747653f 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
index 478df7964f6d2..ee6fbd48b09b6 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
index 4b767fae871fa..c476537c3c7e3 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
index 25046a8417083..8174a724d4165 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
index 1c8710a008f27..e23cb4bf5f04a 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
index f0086ada30303..a36e3af8d12eb 100644
--- a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
+++ b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
@@ -12,6 +12,7 @@
 
 // This test runs in C++20, but we have deprecated codecvt<char(16|32), char, mbstate_t> in C++20.
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // <locale>
 
diff --git a/libcxx/test/std/re/re.traits/transform.pass.cpp b/libcxx/test/std/re/re.traits/transform.pass.cpp
index 80cd3f01faff2..112ecd47ff4e2 100644
--- a/libcxx/test/std/re/re.traits/transform.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform.pass.cpp
@@ -8,6 +8,7 @@
 //
 // NetBSD does not support LC_COLLATE at the moment
 // XFAIL: netbsd
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..cbc9387facb30 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -11,6 +11,7 @@
 
 // XFAIL: LIBCXX-AIX-FIXME
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
index 40daa49393bd4..110d0ea9fd694 100644
--- a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
+++ b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
@@ -15,6 +15,7 @@
 
 // There is no way to limit the number of threads on windows
 // UNSUPPORTED: windows
+// UNSUPPORTED: executor-runs-as-root
 
 // macOS and FreeBSD seem to limit the number of processes, not threads via RLIMIT_NPROC.
 // XFAIL: target={{.+}}-apple-{{.*}}
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
index c6eb0694ab2e1..19573ae52fd71 100644
--- a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
@@ -9,6 +9,7 @@
 // Windows cannot detect the deadlock. Instead of throwing system_error,
 // it would dead lock the test
 // UNSUPPORTED: windows
+// UNSUPPORTED: pthread-join-self-deadlocks
 
 // TSAN bug: https://llvm.org/PR66537
 // UNSUPPORTED: tsan
diff --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
index 51be78e1a02f8..804036e425572 100644
--- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
@@ -14,6 +14,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..6901aff00bc4e 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -415,6 +415,13 @@ void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteCalled(p);
+  std::free(p);
+}
+#  endif
+
 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->newArrayCalled(s);
@@ -449,6 +456,13 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteArrayCalled(p);
+  std::free(p);
+}
+#  endif
+
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
 #      define USE_ALIGNED_ALLOC
@@ -519,6 +533,13 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
@@ -551,6 +572,13 @@ void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
 
 #endif // DISABLE_NEW_COUNT
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index 2ad9efb32c60f..f820a4a0fa915 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -182,7 +182,7 @@ struct scoped_test_env
         std::string cmd = "chmod -R 777 " + test_root.string();
 #endif // defined(__MVS__)
         int ret = std::system(cmd.c_str());
-#  if !defined(_AIX) && !defined(__ANDROID__)
+#  if !defined(_AIX) && !defined(__ANDROID__) && !defined(__OHOS__)
         // On AIX the chmod command will return non-zero when trying to set
         // the permissions on a directory that contains a bad symlink. This triggers
         // the assert, despite being able to delete everything with the following
@@ -196,6 +196,8 @@ struct scoped_test_env
         // LIBCXX-ANDROID-FIXME: Other fixes to consider: place a toybox chmod
         // onto old devices, re-enable this assert for devices running Android N
         // and up, rewrite this chmod+rm in C or C++.
+        //
+        // OHOS has the same chmod -R behavior for bad symlinks.
         assert(ret == 0);
 #  endif
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..fdf6bc5b18d72 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -375,7 +375,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NOT_WIN32(...) __VA_ARGS__
 #endif
 
-#if defined(TEST_WINDOWS_DLL) ||defined(__MVS__) || defined(_AIX)
+#if defined(TEST_WINDOWS_DLL) || defined(__MVS__) || defined(_AIX) || defined(TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS)
 // Macros for waiving cases when we can't count allocations done within
 // the library implementation.
 //
@@ -386,6 +386,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 //
 // The same goes on IBM zOS.
 // The same goes on AIX.
+// Some target test configurations can define
+// TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS for the same reason.
 #define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
 #define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
 #else
diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp
index 36536860c1a96..b28f70a6e8e7a 100644
--- a/libunwind/test/aarch64_za_unwind.pass.cpp
+++ b/libunwind/test/aarch64_za_unwind.pass.cpp
@@ -20,6 +20,9 @@
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
 #endif
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
 
 // Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA
 // (and commits a lazy save of ZA) before resuming from unwinding.
@@ -33,18 +36,16 @@ static bool checkHasSME() {
     return false;
   return has_sme != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = getauxval(AT_HWCAP2);
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = 0;
   elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
 #else
 static bool checkHasSME() {

>From bbaa18370c45aaf79c2446be0a6e94dc2aa0ef00 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Fri, 3 Jul 2026 10:33:38 +0800
Subject: [PATCH 4/6] Add qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  16 +-
 libcxx/utils/ci/run-buildbot                  |  41 ++++-
 libcxx/utils/ci/vendor/ohos/qemu_run.py       | 144 ++++++++++++++++++
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |   8 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |   8 +-
 5 files changed, 204 insertions(+), 13 deletions(-)
 create mode 100755 libcxx/utils/ci/vendor/ohos/qemu_run.py

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 542bc0cc83a1f..20aa7a49d5802 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++ using HDC on OHOS.
+# libc++ using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -23,15 +23,21 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
 config.available_features.add('LIBCXX-OHOS-FIXME')
-config.available_features.add('ohos-hdc')
-config.available_features.add('executor-has-no-stdin')
-config.available_features.add('executor-merges-stderr-into-stdout')
-config.available_features.add('executor-runs-as-root')
+config.available_features.add(executor_kind)
 config.available_features.add('platform-has-nonportable-locales')
 config.available_features.add('platform-has-unreliable-new-delete-interposition')
 config.available_features.add('pthread-join-self-deadlocks')
 
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
     libcxx.test.features.DEFAULT_FEATURES,
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index ecf0ca17ae148..40739b0fba4d8 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -28,6 +28,16 @@ ${PROGNAME} [options] <BUILDER>
 --build-dir <DIR>   The directory to use for building the library. By default,
                     this is '<llvm-root>/build/<builder>'.
 
+--ohos-qemu <PATH>  Run OHOS tests under qemu-aarch64 instead of HDC. This also
+                    accepts OHOS_QEMU or QEMU from the environment.
+
+--ohos-qemu-sysroot <DIR>
+                    Sysroot passed to qemu-aarch64 with -L. Defaults to
+                    OHOS_QEMU_SYSROOT, QEMU_SYSROOT, then OHOS_SYSROOT.
+
+--ohos-hdc <PATH>   HDC binary to use when OHOS tests run on a device. This sets
+                    HDC for the OHOS HDC executor.
+
 Environment variables
 CC                  The C compiler to use, this value is used by CMake. This
                     variable is mandatory.
@@ -84,6 +94,19 @@ while [[ $# -gt 0 ]]; do
             BUILD_DIR="${2}"
             shift; shift
             ;;
+        --ohos-qemu)
+            OHOS_QEMU="${2}"
+            shift; shift
+            ;;
+        --ohos-qemu-sysroot)
+            OHOS_QEMU_SYSROOT="${2}"
+            shift; shift
+            ;;
+        --ohos-hdc)
+            HDC="${2}"
+            export HDC
+            shift; shift
+            ;;
         *)
             BUILDER="${1}"
             shift
@@ -193,8 +216,18 @@ function setup-ohos-hdc-env() {
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
     OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
-    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
-    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+    OHOS_QEMU="${OHOS_QEMU:-${QEMU:-}}"
+
+    if [[ -n "${OHOS_QEMU}" ]]; then
+        OHOS_QEMU_SYSROOT="${OHOS_QEMU_SYSROOT:-${QEMU_SYSROOT:-${OHOS_SYSROOT}}}"
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/qemu_run.py --qemu ${OHOS_QEMU} --sysroot ${OHOS_QEMU_SYSROOT}"
+        OHOS_EXECUTOR_KIND="ohos-qemu"
+    else
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+        OHOS_EXECUTOR_KIND="ohos-hdc"
+    fi
+
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE};executor_kind=${OHOS_EXECUTOR_KIND}"
 }
 
 function generate-cmake-ohos-hdc() {
@@ -863,14 +896,14 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
-ohos-hdc-aarch64-full)
+ohos-aarch64-full|ohos-hdc-aarch64-full)
     clean
 
     generate-cmake-ohos-hdc
 
     check-runtimes
 ;;
-ohos-hdc-aarch64-smoke)
+ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
     clean
 
     generate-cmake-ohos-hdc
diff --git a/libcxx/utils/ci/vendor/ohos/qemu_run.py b/libcxx/utils/ci/vendor/ohos/qemu_run.py
new file mode 100755
index 0000000000000..ef7eef7872864
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/qemu_run.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _resolve_binary(binary):
+    if os.path.isabs(binary):
+        if os.path.isfile(binary) and os.access(binary, os.X_OK):
+            return binary
+        sys.exit(f"failed to find executable: {binary}")
+
+    resolved = shutil.which(binary)
+    if resolved is None:
+        sys.exit(f"failed to find executable: {binary}")
+    return resolved
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(str(parent))
+    return library_dirs
+
+
+def _build_target_env_args(env, prepend_env):
+    target_env = dict(env)
+
+    for key, value in prepend_env.items():
+        existing = target_env.get(key, os.environ.get(key, ""))
+        target_env[key] = f"{value}{os.pathsep}{existing}" if existing else value
+
+    args = []
+    for key, value in target_env.items():
+        args.extend(["-E", f"{key}={value}"])
+    return args
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--qemu",
+        default=os.environ.get("OHOS_QEMU", os.environ.get("QEMU", "qemu-aarch64")),
+    )
+    parser.add_argument("--sysroot", required=True)
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(
+            os.environ.get(
+                "OHOS_QEMU_TIMEOUT_SECONDS",
+                os.environ.get("QEMU_TIMEOUT_SECONDS", "600"),
+            )
+        ),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    qemu = _resolve_binary(args.qemu)
+    execdir = Path(args.execdir).resolve()
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    build_library_dirs = _collect_build_library_dirs(execdir)
+    if build_library_dirs:
+        existing = prepend_env.get("LD_LIBRARY_PATH")
+        build_library_path = os.pathsep.join(build_library_dirs)
+        prepend_env["LD_LIBRARY_PATH"] = (
+            f"{build_library_path}{os.pathsep}{existing}"
+            if existing
+            else build_library_path
+        )
+
+    cmd = [
+        qemu,
+        "-L",
+        args.sysroot,
+        *_build_target_env_args(env, prepend_env),
+        *args.command,
+    ]
+    try:
+        completed = subprocess.run(
+            cmd,
+            cwd=execdir,
+            timeout=args.timeout_seconds,
+            check=False,
+        )
+    except subprocess.TimeoutExpired:
+        print(
+            f"qemu executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+            file=sys.stderr,
+        )
+        return 124
+    return completed.returncode
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index d18748fe3a3cc..8f5a17b3dec64 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++abi using HDC on OHOS.
+# libc++abi using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -20,7 +20,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index bce241a7898cf..f2a032c41a123 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -1,6 +1,6 @@
 #
 # Configuration file for running the libunwind tests against the shared library
-# using HDC on OHOS.
+# using HDC or QEMU on OHOS.
 #
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
@@ -45,7 +45,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,

>From dde4d430033711fb7ffa87621ba86b37492f480b Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 14:52:40 +0800
Subject: [PATCH 5/6] Fix something for qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in                   | 4 +++-
 .../file.streams/fstreams/filebuf.members/close.pass.cpp      | 3 +++
 .../fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp     | 2 ++
 .../syncstream/osyncstream/thread/several_threads.pass.cpp    | 2 ++
 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in             | 4 +++-
 libcxxabi/test/guard_threaded_test.pass.cpp                   | 2 ++
 libunwind/test/bad_unwind_info.pass.cpp                       | 2 ++
 libunwind/test/configs/llvm-libunwind-ohos.cfg.in             | 4 +++-
 libunwind/test/signal_unwind.pass.cpp                         | 2 ++
 9 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 20aa7a49d5802..3be1c267ede2a 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -23,7 +23,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
index 2c4cfb0ac7661..ce802d6ceba78 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14
 // REQUIRES: linux
 // UNSUPPORTED: no-filesystem
+// OHOS qemu-user observes the host /proc, not a real OHOS process /proc.
+// UNSUPPORTED: ohos-qemu
 // XFAIL: no-localization
 
 // <filesystem>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..77db6396755bf 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -10,6 +10,8 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
+// This thread-heavy stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 // <syncstream>
 
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index 8f5a17b3dec64..d0a8d1248f88a 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -20,7 +20,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxxabi/test/guard_threaded_test.pass.cpp b/libcxxabi/test/guard_threaded_test.pass.cpp
index d898e60288aca..a4e14f0aca9e4 100644
--- a/libcxxabi/test/guard_threaded_test.pass.cpp
+++ b/libcxxabi/test/guard_threaded_test.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: no-exceptions
+// This thread-heavy guard stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 #define TESTING_CXA_GUARD
 #include "../src/cxa_guard_impl.h"
diff --git a/libunwind/test/bad_unwind_info.pass.cpp b/libunwind/test/bad_unwind_info.pass.cpp
index 332b661d2e98f..76102505f2d67 100644
--- a/libunwind/test/bad_unwind_info.pass.cpp
+++ b/libunwind/test/bad_unwind_info.pass.cpp
@@ -13,6 +13,8 @@
 // REQUIRES: target={{(aarch64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
 // UNSUPPORTED: target={{.*-apple.*}}
+// OHOS qemu-user does not reliably model this invalid unwind-info path.
+// UNSUPPORTED: ohos-qemu
 
 // GCC doesn't support __attribute__((naked)) on AArch64.
 // UNSUPPORTED: gcc
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index f2a032c41a123..71d1e711e36d5 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -45,7 +45,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp
index f8798bc3eabeb..ef73a3ea8c59a 100644
--- a/libunwind/test/signal_unwind.pass.cpp
+++ b/libunwind/test/signal_unwind.pass.cpp
@@ -10,6 +10,8 @@
 // Ensure that the unwinder can cope with the signal handler.
 // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
+// OHOS qemu-user does not provide device-equivalent signal unwinding behavior.
+// UNSUPPORTED: ohos-qemu
 
 // TODO: Figure out why this fails with Memory Sanitizer.
 // XFAIL: msan

>From c81df7252fb00b5b9b41f0a29393e775abc9c810 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 15:19:29 +0800
Subject: [PATCH 6/6] delete run-smoke-test

---
 libcxx/utils/ci/buildkite-pipeline.yml | 12 ----------
 libcxx/utils/ci/run-buildbot           | 31 --------------------------
 2 files changed, 43 deletions(-)

diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 77617f43ae6dc..70bc69637a7f2 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -175,18 +175,6 @@ steps:
 
 - group: OHOS
   steps:
-  - label: OHOS AArch64 HDC Smoke
-    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
-    env:
-      CC: clang
-      CXX: clang++
-      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
-      HDC_TIMEOUT_SECONDS: 240
-    agents:
-      queue: libcxx-builders
-      os: linux
-      device: ohos-hdc
-    <<: *common
   - label: OHOS AArch64 HDC Full
     command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
     env:
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 40739b0fba4d8..e7d751df484e4 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -147,23 +147,6 @@ function require-env-var() {
     fi
 }
 
-function detect-lit() {
-    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
-        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
-    elif command -v llvm-lit >/dev/null 2>&1; then
-        LIT_RUNNER=("$(command -v llvm-lit)")
-    else
-        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
-    fi
-}
-
-function run-smoke-test() {
-    local test_dir="${1}"
-    local filter="${2}"
-    step "Running smoke test ${filter}"
-    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
-}
-
 function ohos-lit-args() {
     local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
     if python3 -c 'import psutil' >/dev/null 2>&1; then
@@ -903,20 +886,6 @@ ohos-aarch64-full|ohos-hdc-aarch64-full)
 
     check-runtimes
 ;;
-ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
-    clean
-
-    generate-cmake-ohos-hdc
-
-    step "Building the OHOS smoke test dependencies"
-    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
-
-    detect-lit
-    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
-;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #


From libcxx-commits at lists.llvm.org  Thu Jul  9 18:15:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 09 Jul 2026 18:15:01 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a504795.ff037943.2e959a.4a8d@mx.google.com>


https://github.com/huihui15537226823 updated https://github.com/llvm/llvm-project/pull/208390

>From 70e8ac839667f8aba4aa74a2dd19614426f9754f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 24 Jun 2026 09:47:00 +0800
Subject: [PATCH 1/6] Add OHOS HDC runtimes CI support

---
 cmake/Modules/GetToolchainDirs.cmake          |  37 +-
 .../cmake/Modules/CompilerRTUtils.cmake       |   8 +-
 compiler-rt/cmake/builtin-config-ix.cmake     |   2 +-
 compiler-rt/cmake/crt-config-ix.cmake         |   2 +-
 compiler-rt/lib/builtins/crtbegin.c           |   9 +
 compiler-rt/lib/builtins/emutls.c             |   8 +-
 libcxx/CMakeLists.txt                         |  10 +-
 libcxx/cmake/config-ix.cmake                  |   2 +
 libcxx/include/__config                       |   5 +-
 libcxx/include/__cxx03/__config               |   5 +-
 .../__cxx03/__locale_dir/locale_base_api.h    |   2 +-
 libcxx/include/__locale_dir/locale_base_api.h |   2 +-
 libcxx/src/include/config_elast.h             |   2 +
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  48 ++
 libcxx/utils/ci/buildkite-pipeline.yml        |  27 +
 libcxx/utils/ci/run-buildbot                  |  87 ++++
 libcxx/utils/ci/vendor/ohos/hdc_run.py        | 472 ++++++++++++++++++
 libcxx/utils/ci/vendor/ohos/hdc_run_test.py   | 115 +++++
 libcxxabi/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |  30 ++
 libunwind/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |  55 ++
 22 files changed, 910 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/test/configs/llvm-libc++-ohos.cfg.in
 create mode 100755 libcxx/utils/ci/vendor/ohos/hdc_run.py
 create mode 100644 libcxx/utils/ci/vendor/ohos/hdc_run_test.py
 create mode 100644 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
 create mode 100644 libunwind/test/configs/llvm-libunwind-ohos.cfg.in

diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..a20ed9ce9c6a9
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,48 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index d4107cf11ad51..77617f43ae6dc 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -172,3 +172,30 @@ steps:
       queue: libcxx-builders
       os: android
     <<: *common
+
+- group: OHOS
+  steps:
+  - label: OHOS AArch64 HDC Smoke
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
+  - label: OHOS AArch64 HDC Full
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 6376bc1d3dd5a..1867e9c620649 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -116,6 +116,41 @@ function clean() {
     rm -rf "${BUILD_DIR}"
 }
 
+function require-env-var() {
+    local name="${1}"
+    if [[ -z ${!name+x} ]] || [[ -z "${!name}" ]]; then
+        error "Environment variable ${name} must be defined"
+        exit 1
+    fi
+}
+
+function detect-lit() {
+    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
+        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
+    elif command -v llvm-lit >/dev/null 2>&1; then
+        LIT_RUNNER=("$(command -v llvm-lit)")
+    else
+        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
+    fi
+}
+
+function run-smoke-test() {
+    local test_dir="${1}"
+    local filter="${2}"
+    step "Running smoke test ${filter}"
+    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
+}
+
+function ohos-lit-args() {
+    local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
+    if python3 -c 'import psutil' >/dev/null 2>&1; then
+        args="${args} --timeout=1500"
+    else
+        echo "warning: python3 psutil not available; running OHOS lit without per-test timeout" >&2
+    fi
+    printf '%s' "${args}"
+}
+
 function generate-cmake-base() {
     step "Generating CMake"
 
@@ -153,6 +188,37 @@ function generate-cmake-android() {
           "${@}"
 }
 
+function setup-ohos-hdc-env() {
+    require-env-var OHOS_SYSROOT
+
+    OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+}
+
+function generate-cmake-ohos-hdc() {
+    setup-ohos-hdc-env
+
+    local lit_args
+    lit_args="$(ohos-lit-args)"
+
+    generate-cmake \
+          -DLLVM_LIT_ARGS="${lit_args}" \
+          -DCMAKE_SYSTEM_NAME=OHOS \
+          -DCMAKE_SYSROOT="${OHOS_SYSROOT}" \
+          -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
+          -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
+          -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
+          -DLIBUNWIND_TEST_CONFIG="llvm-libunwind-ohos.cfg.in" \
+          -DLIBCXX_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBCXXABI_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBUNWIND_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          "${@}"
+}
+
 function check-runtimes() {
     step "Building libc++ test dependencies"
     ninja -vC "${BUILD_DIR}" cxx-test-depends
@@ -795,6 +861,27 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
+ohos-hdc-aarch64-full)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    check-runtimes
+;;
+ohos-hdc-aarch64-smoke)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    step "Building the OHOS smoke test dependencies"
+    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
+
+    detect-lit
+    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
+;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run.py b/libcxx/utils/ci/vendor/ohos/hdc_run.py
new file mode 100755
index 0000000000000..61a3899b88b11
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run.py
@@ -0,0 +1,472 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import uuid
+
+
+EXIT_MARKER = "__llvm_hdc_exit__="
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _split_path_list(value):
+    return [entry for entry in value.split(os.pathsep) if entry]
+
+
+def _resolve_hdc_binary(hdc):
+    if os.path.isabs(hdc):
+        return hdc
+    resolved = shutil.which(hdc)
+    if resolved is None:
+        sys.exit(f"failed to find hdc binary: {hdc}")
+    return resolved
+
+
+def _is_windows_hdc(path):
+    return path.lower().endswith(".exe")
+
+
+def _find_windows_local_root(hdc_path):
+    parts = Path(hdc_path).parts
+    for index in range(len(parts) - 1):
+        if parts[index] == "AppData" and index + 1 < len(parts) and parts[index + 1] == "Local":
+            return Path(*parts[: index + 2])
+    return None
+
+
+def _default_host_staging_root(hdc_path):
+    if _is_windows_hdc(hdc_path):
+        local_root = _find_windows_local_root(hdc_path)
+        if local_root is None:
+            sys.exit(
+                "failed to derive a Windows staging directory from the hdc.exe path; "
+                "set HDC_HOST_STAGING_ROOT to a path under /mnt/<drive>/..."
+            )
+        return local_root / "Temp" / "llvm-lit-hdc"
+    return Path("/tmp/llvm-lit-hdc")
+
+
+def _to_windows_path(path):
+    resolved = Path(path).resolve()
+    parts = resolved.parts
+    if len(parts) < 4 or parts[1] != "mnt":
+        sys.exit(
+            f"Windows hdc requires a staging path under /mnt/<drive>/..., got: {resolved}"
+        )
+    drive = parts[2].upper()
+    tail = "\\".join(parts[3:])
+    return f"{drive}:\\{tail}" if tail else f"{drive}:\\"
+
+
+def _run_readelf(path):
+    completed = subprocess.run(
+        ["readelf", "-d", path],
+        check=False,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        text=True,
+    )
+    if completed.returncode != 0:
+        return ""
+    return completed.stdout
+
+
+def _read_elf_metadata(path):
+    needed = []
+    runpath_dirs = []
+    seen_dirs = set()
+    for line in _run_readelf(path).splitlines():
+        needed_match = re.search(r"\(NEEDED\).*Shared library: \[(.*)\]", line)
+        if needed_match:
+            needed.append(needed_match.group(1))
+            continue
+
+        runpath_match = re.search(
+            r"\((?:RUNPATH|RPATH)\).*Library runpath: \[(.*)\]", line
+        )
+        if not runpath_match:
+            continue
+        for entry in _split_path_list(runpath_match.group(1)):
+            candidate = Path(entry)
+            if not candidate.is_dir():
+                continue
+            key = str(candidate.resolve())
+            if key in seen_dirs:
+                continue
+            seen_dirs.add(key)
+            runpath_dirs.append(candidate)
+    return needed, runpath_dirs
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(parent)
+    return library_dirs
+
+
+def _find_dependency(name, search_dirs):
+    for directory in search_dirs:
+        candidate = Path(directory) / name
+        if candidate.is_file():
+            return candidate.resolve()
+    return None
+
+
+def _copy_needed_libraries(executables, execdir, staging_execdir):
+    search_dirs = _collect_build_library_dirs(execdir)
+    queue = [Path(exe).resolve() for exe in executables]
+    visited = set()
+    copied_names = {path.name for path in staging_execdir.glob("*") if path.is_file()}
+
+    while queue:
+        current = queue.pop(0)
+        current_key = str(current)
+        if current_key in visited:
+            continue
+        visited.add(current_key)
+
+        needed, runpath_dirs = _read_elf_metadata(current)
+        current_search_dirs = []
+        current_seen = set()
+        for directory in [*runpath_dirs, current.parent, *search_dirs]:
+            resolved = Path(directory).resolve()
+            key = str(resolved)
+            if key in current_seen:
+                continue
+            current_seen.add(key)
+            current_search_dirs.append(resolved)
+
+        for directory in runpath_dirs:
+            resolved = directory.resolve()
+            if all(str(resolved) != str(existing) for existing in search_dirs):
+                search_dirs.append(resolved)
+
+        for lib_name in needed:
+            dependency = _find_dependency(lib_name, current_search_dirs)
+            if dependency is None:
+                continue
+            if lib_name not in copied_names:
+                shutil.copy2(dependency, staging_execdir / lib_name)
+                copied_names.add(lib_name)
+            queue.append(dependency)
+
+
+def _copy_execdir(execdir, staging_execdir):
+    if staging_execdir.exists():
+        shutil.rmtree(staging_execdir)
+    shutil.copytree(execdir, staging_execdir)
+
+
+def _iter_files(root):
+    for directory, dirnames, filenames in os.walk(root):
+        dirnames.sort()
+        filenames.sort()
+        yield Path(directory), filenames
+
+
+def _is_test_executable(path):
+    return path.endswith(".tmp.exe") and os.path.exists(path)
+
+
+def _map_local_path(path, local_execdir, remote_execdir):
+    normalized = os.path.normpath(path)
+    execdir = os.path.normpath(local_execdir)
+    if normalized == execdir:
+        return remote_execdir
+    prefix = execdir + os.sep
+    if normalized.startswith(prefix):
+        suffix = normalized[len(prefix) :].replace(os.sep, "/")
+        return f"{remote_execdir}/{suffix}" if suffix else remote_execdir
+    return path
+
+
+def _map_value(value, local_execdir, remote_execdir):
+    return _map_local_path(value, local_execdir, remote_execdir)
+
+
+def _build_env_exports(env, prepend_env, local_execdir, remote_execdir, add_remote_libdir):
+    commands = []
+    mapped_env = {
+        key: _map_value(value, local_execdir, remote_execdir) for key, value in env.items()
+    }
+    mapped_prepend = {
+        key: _map_value(value, local_execdir, remote_execdir)
+        for key, value in prepend_env.items()
+    }
+
+    if add_remote_libdir:
+        existing = mapped_prepend.get("LD_LIBRARY_PATH")
+        mapped_prepend["LD_LIBRARY_PATH"] = (
+            f"{remote_execdir}:{existing}" if existing else remote_execdir
+        )
+
+    for key, value in mapped_env.items():
+        commands.append(f"export {key}={shlex.quote(value)}")
+    for key, value in mapped_prepend.items():
+        commands.append(f"export {key}={shlex.quote(value)}${{{key}:+:{key}}}")
+    return commands
+
+
+def _hdc_prefix(hdc_path, server, target):
+    prefix = [hdc_path]
+    if server:
+        prefix.extend(["-s", server])
+    if target:
+        prefix.extend(["-t", target])
+    return prefix
+
+
+def _run_hdc(
+    prefix,
+    args,
+    *,
+    timeout=None,
+    capture_output=False,
+    stdin=subprocess.DEVNULL,
+    check=True,
+    expect_stdout=None,
+):
+    kwargs = {
+        "check": False,
+        "timeout": timeout,
+        "stdin": stdin,
+        "text": True,
+    }
+    if capture_output:
+        kwargs["stdout"] = subprocess.PIPE
+        kwargs["stderr"] = subprocess.PIPE
+    completed = subprocess.run(prefix + args, **kwargs)
+    output = completed.stdout if capture_output and completed.stdout else ""
+    error_output = completed.stderr if capture_output and completed.stderr else ""
+    if check and completed.returncode != 0:
+        raise RuntimeError(
+            f"hdc command failed with exit code {completed.returncode}: "
+            f"{shlex.join(prefix + args)}\n{output}{error_output}"
+        )
+    if check and expect_stdout is not None and expect_stdout not in output:
+        raise RuntimeError(
+            f"hdc command did not report success: {shlex.join(prefix + args)}\n{output}"
+        )
+    return completed
+
+
+def _run_checked_shell(prefix, command, *, timeout=None):
+    completed = _run_hdc(
+        prefix,
+        [
+            "shell",
+            f"{command}; rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"",
+        ],
+        timeout=timeout,
+        capture_output=True,
+    )
+    exit_code, output = _extract_exit_code(completed.stdout)
+    if exit_code is None:
+        raise RuntimeError(
+            f"failed to parse shell exit code for: {command}\n{completed.stdout}"
+        )
+    if exit_code != 0:
+        raise RuntimeError(
+            f"remote shell command failed with exit code {exit_code}: {command}\n{output}"
+        )
+    return output
+
+
+def _sync_tree_to_device(prefix, local_root, remote_root, use_windows_paths):
+    _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_root)}")
+    _run_checked_shell(prefix, f"mkdir -p {shlex.quote(remote_root)}")
+
+    for directory, filenames in _iter_files(local_root):
+        relative_dir = os.path.relpath(directory, local_root)
+        device_dir = (
+            remote_root
+            if relative_dir == "."
+            else f"{remote_root}/{relative_dir.replace(os.sep, '/')}"
+        )
+        _run_checked_shell(prefix, f"mkdir -p {shlex.quote(device_dir)}")
+        for name in filenames:
+            local_path = directory / name
+            device_path = f"{device_dir}/{name}"
+            source = _to_windows_path(local_path) if use_windows_paths else str(local_path)
+            _run_hdc(
+                prefix,
+                ["file", "send", source, device_path],
+                capture_output=True,
+                expect_stdout="FileTransfer finish",
+            )
+            if name.endswith(".tmp.exe") or os.access(local_path, os.X_OK):
+                _run_checked_shell(prefix, f"chmod +x {shlex.quote(device_path)}")
+
+
+def _build_remote_command(command, local_execdir, remote_execdir, exports):
+    mapped_command = [
+        _map_local_path(arg, local_execdir, remote_execdir) for arg in command
+    ]
+    pieces = [f"cd {shlex.quote(remote_execdir)}"]
+    pieces.extend(exports)
+    pieces.append(shlex.join(mapped_command))
+    pieces.append(f"rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"")
+    return " && ".join(pieces[:-2]) + f"; {pieces[-2]}; {pieces[-1]}"
+
+
+def _extract_exit_code(output):
+    match = re.search(rf"^(.*)\n{EXIT_MARKER}(\d+)\s*$", output, re.DOTALL)
+    if not match:
+        return None, output
+    return int(match.group(2)), match.group(1)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--hdc", default=os.environ.get("HDC", "hdc"))
+    parser.add_argument("--hdc-server", default=os.environ.get("HDC_SERVER_IP_PORT"))
+    parser.add_argument("--hdc-target", default=os.environ.get("HDC_UTID"))
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--remote-base-dir",
+        default="/data/local/tmp/llvm-lit-hdc",
+    )
+    parser.add_argument(
+        "--host-staging-root",
+        default=os.environ.get("HDC_HOST_STAGING_ROOT"),
+    )
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(os.environ.get("HDC_TIMEOUT_SECONDS", "120")),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("--keep-remote-dir", action="store_true")
+    parser.add_argument("--keep-host-staging", action="store_true")
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    hdc_path = _resolve_hdc_binary(args.hdc)
+    use_windows_paths = _is_windows_hdc(hdc_path)
+    host_staging_root = (
+        Path(args.host_staging_root)
+        if args.host_staging_root
+        else _default_host_staging_root(hdc_path)
+    )
+    prefix = _hdc_prefix(hdc_path, args.hdc_server, args.hdc_target)
+
+    execdir = Path(args.execdir).resolve()
+    run_id = uuid.uuid4().hex[:12]
+    staging_execdir = host_staging_root / f"run-{run_id}" / execdir.name
+    remote_execdir = f"{args.remote_base_dir}/run-{run_id}/{execdir.name}"
+
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    executables = [arg for arg in args.command if _is_test_executable(arg)]
+    host_staging_root.mkdir(parents=True, exist_ok=True)
+    _copy_execdir(execdir, staging_execdir)
+    _copy_needed_libraries(executables, execdir, staging_execdir)
+
+    add_remote_libdir = any(staging_execdir.glob("*.so*"))
+    exports = _build_env_exports(
+        env,
+        prepend_env,
+        str(execdir),
+        remote_execdir,
+        add_remote_libdir,
+    )
+
+    try:
+        _sync_tree_to_device(prefix, staging_execdir, remote_execdir, use_windows_paths)
+        remote_command = _build_remote_command(
+            args.command,
+            str(execdir),
+            remote_execdir,
+            exports,
+        )
+        try:
+            completed = _run_hdc(
+                prefix,
+                ["shell", remote_command],
+                timeout=args.timeout_seconds,
+                capture_output=True,
+                stdin=None,
+            )
+        except subprocess.TimeoutExpired:
+            print(
+                f"hdc executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+                file=sys.stderr,
+            )
+            return 124
+
+        exit_code, output = _extract_exit_code(completed.stdout)
+        if exit_code is None:
+            print(
+                "failed to parse exit code from hdc shell output",
+                file=sys.stderr,
+            )
+            if completed.stdout:
+                print(completed.stdout, end="", file=sys.stderr)
+            if completed.stderr:
+                print(completed.stderr, end="", file=sys.stderr)
+            return 1
+
+        if output:
+            sys.stdout.write(output)
+        if completed.stderr:
+            sys.stderr.write(completed.stderr)
+        return exit_code
+    finally:
+        if not args.keep_remote_dir:
+            try:
+                _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_execdir)}")
+            except Exception:
+                pass
+        if not args.keep_host_staging:
+            shutil.rmtree(staging_execdir.parent, ignore_errors=True)
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run_test.py b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
new file mode 100644
index 0000000000000..17e2f65bd1fb6
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import os
+from pathlib import Path
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import unittest
+
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+HDC_RUN = SCRIPT_DIR / "hdc_run.py"
+
+
+class HdcRunTest(unittest.TestCase):
+    def make_fake_hdc(self, directory):
+        fake_hdc = directory / "hdc"
+        fake_hdc.write_text(
+            textwrap.dedent(
+                """\
+                #!/usr/bin/env python3
+                import sys
+
+                EXIT_MARKER = "__llvm_hdc_exit__="
+
+                def main():
+                    args = sys.argv[1:]
+                    if args[:2] == ["file", "send"]:
+                        print("FileTransfer finish")
+                        return 0
+
+                    if args and args[0] == "shell":
+                        command = args[1]
+                        if "stderr.tmp.exe" in command:
+                            sys.stdout.write("STDOUT-OUTPUT")
+                            sys.stderr.write("STDERR-OUTPUT")
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        if "stdin.tmp.exe" in command:
+                            sys.stdout.write("IN:" + sys.stdin.read())
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                        return 0
+
+                    print("unexpected fake hdc invocation: " + repr(args), file=sys.stderr)
+                    return 2
+
+                if __name__ == "__main__":
+                    raise SystemExit(main())
+                """
+            )
+        )
+        fake_hdc.chmod(fake_hdc.stat().st_mode | stat.S_IXUSR)
+        return fake_hdc
+
+    def run_hdc_run(self, temp_dir, executable_name, *, stdin_text=None):
+        execdir = temp_dir / "execdir"
+        execdir.mkdir()
+        executable = execdir / executable_name
+        executable.write_text("# fake executable marker\n")
+        executable.chmod(executable.stat().st_mode | stat.S_IXUSR)
+
+        fake_hdc = self.make_fake_hdc(temp_dir)
+        return subprocess.run(
+            [
+                sys.executable,
+                str(HDC_RUN),
+                "--hdc",
+                str(fake_hdc),
+                "--execdir",
+                str(execdir),
+                "--host-staging-root",
+                str(temp_dir / "staging"),
+                str(executable),
+            ],
+            input=stdin_text,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+    def test_preserves_remote_stdout_and_stderr_separately(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(Path(temp), "stderr.tmp.exe")
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "STDOUT-OUTPUT")
+        self.assertEqual(completed.stderr, "STDERR-OUTPUT")
+
+    def test_forwards_stdin_to_remote_command(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(
+                Path(temp),
+                "stdin.tmp.exe",
+                stdin_text="hello from stdin",
+            )
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "IN:hello from stdin")
+        self.assertEqual(completed.stderr, "")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt
index e1a1587fb6283..cb22e6fe65108 100644
--- a/libcxxabi/CMakeLists.txt
+++ b/libcxxabi/CMakeLists.txt
@@ -187,7 +187,12 @@ set(LIBCXXABI_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXXABI_TARGET_SUBDIR)
+  else()
+    set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXXABI_LIBDIR_SUBDIR)
     string(APPEND LIBCXXABI_TARGET_SUBDIR /${LIBCXXABI_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
new file mode 100644
index 0000000000000..d18748fe3a3cc
--- /dev/null
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -0,0 +1,30 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++abi using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -I %{libcxx}/test/support -I %{libcxx}/src -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    '-nostdlib++ -L %{lib} -lc++ -lc++abi -pthread'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 02132f6c07fcd..4fd11bf5aca2f 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -142,7 +142,12 @@ set(LIBUNWIND_STATIC_OUTPUT_NAME "unwind" CACHE STRING "Output name for the stat
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBUNWIND_TARGET_SUBDIR)
+  else()
+    set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBUNWIND_LIBDIR_SUBDIR)
     string(APPEND LIBUNWIND_TARGET_SUBDIR /${LIBUNWIND_LIBDIR_SUBDIR})
   endif()
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
new file mode 100644
index 0000000000000..bce241a7898cf
--- /dev/null
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -0,0 +1,55 @@
+#
+# Configuration file for running the libunwind tests against the shared library
+# using HDC on OHOS.
+#
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+compile_flags = []
+link_flags = []
+
+if @HAVE_GETAUXVAL@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_GETAUXVAL')
+
+if @HAVE_ELF_AUX_INFO@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_ELF_AUX_INFO')
+
+if @LIBUNWIND_ENABLE_CET@:
+    compile_flags.append('-fcf-protection=full')
+
+if @LIBUNWIND_ENABLE_GCS@:
+    compile_flags.append('-mbranch-protection=standard')
+
+if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
+    link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
+
+if '@CMAKE_DL_LIBS@':
+    link_flags.append('-l at CMAKE_DL_LIBS@')
+
+compile_flags.append('-funwind-tables')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags))
+))
+config.substitutions.append(('%{link_flags}',
+    '-L %{{lib}} -lunwind {}'.format(' '.join(link_flags))
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)

>From e9e5d2e517fb072f3554798e49b7119ec739105f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 25 Jun 2026 11:30:38 +0800
Subject: [PATCH 2/6] Fix OHOS libc++ CI ABI and SME detection

---
 libcxx/utils/ci/run-buildbot |  2 ++
 libunwind/src/Registers.hpp  | 15 +++++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 1867e9c620649..ecf0ca17ae148 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -192,6 +192,7 @@ function setup-ohos-hdc-env() {
     require-env-var OHOS_SYSROOT
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
     OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
     OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
 }
@@ -209,6 +210,7 @@ function generate-cmake-ohos-hdc() {
           -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE}" \
           -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
           -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
           -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c88422a6d5f4f..18cb2a523bd0e 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -25,6 +25,11 @@
 #endif
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
+#if defined(__has_include)
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
+#endif
 #endif
 
 namespace libunwind {
@@ -1951,18 +1956,16 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
       return false;
     return has_sme != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = getauxval(AT_HWCAP2);
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = 0;
     elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
 #else
   static bool checkHasSME() {

>From 498fa8d127ff49c27d4230996338c6eb5d747fe6 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 1 Jul 2026 15:49:11 +0800
Subject: [PATCH 3/6] Stabilize OHOS HDC runtimes tests

Add OHOS lit features for HDC executor and platform limitations, mark affected libc++ tests as unsupported or expected failures, and adjust test support for OHOS filesystem cleanup and allocation counting behavior.

Also guard the aarch64 ZA unwind test on HWCAP2_SME so devices without a reliable SME feature definition do not execute SME instructions.
---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  9 +++++-
 .../clang/clang_modules_include.gen.py        |  3 ++
 .../locale.facet/no_allocation.pass.cpp       |  1 +
 libcxx/test/selftest/dsl/dsl.sh.py            |  5 ++++
 libcxx/test/selftest/stdin-is-piped.sh.cpp    |  1 +
 .../directory_entry.cons/path.pass.cpp        |  1 +
 .../directory_entry.mods/assign.pass.cpp      |  1 +
 .../directory_entry.mods/refresh.pass.cpp     |  1 +
 .../replace_filename.pass.cpp                 |  1 +
 .../directory_entry.obs/file_size.pass.cpp    |  1 +
 .../file_type_obs.pass.cpp                    |  1 +
 .../hard_link_count.pass.cpp                  |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../directory_iterator.members/ctor.pass.cpp  |  1 +
 .../path.member/path.assign/move.pass.cpp     |  2 +-
 .../path.member/path.construct/move.pass.cpp  |  2 +-
 .../rec.dir.itr.members/ctor.pass.cpp         |  1 +
 .../rec.dir.itr.members/increment.pass.cpp    |  1 +
 .../fs.op.funcs/fs.op.exists/exists.pass.cpp  |  1 +
 .../is_block_file.pass.cpp                    |  1 +
 .../is_character_file.pass.cpp                |  1 +
 .../fs.op.is_directory/is_directory.pass.cpp  |  1 +
 .../fs.op.is_empty/is_empty.pass.cpp          |  1 +
 .../fs.op.is_fifo/is_fifo.pass.cpp            |  1 +
 .../fs.op.is_other/is_other.pass.cpp          |  1 +
 .../is_regular_file.pass.cpp                  |  1 +
 .../fs.op.is_socket/is_socket.pass.cpp        |  1 +
 .../fs.op.is_symlink/is_symlink.pass.cpp      |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../fs.op.funcs/fs.op.remove/remove.pass.cpp  |  1 +
 .../fs.op.remove_all/remove_all.pass.cpp      |  1 +
 .../fs.op.funcs/fs.op.status/status.pass.cpp  |  1 +
 .../symlink_status.pass.cpp                   |  1 +
 .../temp_directory_path.pass.cpp              |  1 +
 .../ext.manip/get_money.pass.cpp              |  1 +
 .../ext.manip/put_money.pass.cpp              |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 .../print.fun/no_file_description.pass.cpp    |  3 ++
 .../std.manip/setfill_wchar_max.pass.cpp      |  1 +
 .../narrow.stream.objects/cerr.sh.cpp         |  1 +
 .../narrow.stream.objects/cin.sh.cpp          |  1 +
 .../narrow.stream.objects/clog.sh.cpp         |  1 +
 .../wide.stream.objects/wcerr-imbue.sh.cpp    |  1 +
 .../wide.stream.objects/wcerr.sh.cpp          |  1 +
 .../wide.stream.objects/wcin-imbue.sh.cpp     |  1 +
 .../wide.stream.objects/wcin.sh.cpp           |  1 +
 .../wide.stream.objects/wclog.sh.cpp          |  1 +
 .../sized_delete_array.pass.cpp               |  1 +
 ..._array_calls_unsized_delete_array.pass.cpp |  1 +
 .../new.delete.single/sized_delete.pass.cpp   |  1 +
 ...sized_delete_calls_unsized_delete.pass.cpp |  1 +
 .../locale.collate.byname/compare.pass.cpp    |  1 +
 .../locale.collate.byname/transform.pass.cpp  |  1 +
 .../facet.ctype.char.dtor/dtor.pass.cpp       |  1 +
 .../locale.ctype.byname/is_1.pass.cpp         |  1 +
 .../locale.ctype.byname/is_many.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_is.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_not.pass.cpp     |  1 +
 .../locale.ctype.byname/tolower_1.pass.cpp    |  1 +
 .../locale.ctype.byname/tolower_many.pass.cpp |  1 +
 .../locale.ctype.byname/toupper_1.pass.cpp    |  1 +
 .../locale.ctype.byname/toupper_many.pass.cpp |  1 +
 .../locale.ctype.byname/widen_1.pass.cpp      |  1 +
 .../locale.ctype.byname/widen_many.pass.cpp   |  1 +
 .../get_long_double_en_US.pass.cpp            |  1 +
 .../get_long_double_zh_CN.pass.cpp            |  1 +
 .../get_string_en_US.pass.cpp                 |  1 +
 .../put_long_double_en_US.pass.cpp            |  1 +
 .../put_long_double_zh_CN.pass.cpp            |  1 +
 .../put_string_en_US.pass.cpp                 |  1 +
 .../locale/locale.cons/default.pass.cpp       |  1 +
 .../test/std/re/re.traits/transform.pass.cpp  |  1 +
 .../re/re.traits/transform_primary.pass.cpp   |  1 +
 .../thread_create_failure.pass.cpp            |  1 +
 .../thread.jthread/join.deadlock.pass.cpp     |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 libcxx/test/support/count_new.h               | 28 +++++++++++++++++++
 libcxx/test/support/filesystem_test_helper.h  |  4 ++-
 libcxx/test/support/test_macros.h             |  4 ++-
 libunwind/test/aarch64_za_unwind.pass.cpp     | 13 +++++----
 80 files changed, 132 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index a20ed9ce9c6a9..542bc0cc83a1f 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -7,7 +7,8 @@ config.substitutions.append(('%{flags}',
     ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
 ))
 config.substitutions.append(('%{compile_flags}',
-    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
 ))
 config.substitutions.append(('%{link_flags}',
     # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
@@ -24,6 +25,12 @@ import libcxx.test.params, libcxx.test.config
 
 config.available_features.add('LIBCXX-OHOS-FIXME')
 config.available_features.add('ohos-hdc')
+config.available_features.add('executor-has-no-stdin')
+config.available_features.add('executor-merges-stderr-into-stdout')
+config.available_features.add('executor-runs-as-root')
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
index 20a545b719ed6..f04a6bce76691 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
index 03e9de882ab0a..428d30804a4c3 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
index 10829f8a73db1..c8a1c34aa3028 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index 071ee7f6c891f..44b94528f5e32 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a hard link.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
index 5d931891dd0eb..d291ca2cb5641 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
index 82b94a3d6bf5f..e4bc9c41728da 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
index 8401dbd38cd71..e0fa44ffbc4a5 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
index ac5499cdd6f20..2bcc64a3c2651 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_assignable<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
index 1f1843f7d401d..5efdf983f8a10 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_constructible<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
index 08d7ff84378d5..a3768b8cbd83f 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
index 1f2293989b7ef..9e8a70b4339d4 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // On Android L, ~scoped_test_env() is unable to delete the temp dir using
 // chmod+rm because chmod is too broken.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
index 56465d01021ad..62dc696d6797d 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
index fad880ecf30f7..708fb511dce73 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
index 12bc0c198c062..69e357adeafb5 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
index f1b559cf2d55b..0ed4aa0dc8d58 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
index f3161f2e020d2..579c03f2b8c0b 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
index fbf5e7129f7af..1d5312d65be46 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
index d0974640429df..7a3a56a0b161c 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
index 4a5882e36cde2..8b877ff63b112 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
index 382685c21dd99..995125d702de3 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
index 07b446d80c402..692fe773a0f9e 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
index c420fea21aa66..22c2c114ba687 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
index 56b4a27de6b42..8064feaaf2065 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
index b30d56685e2cb..540849ccefc05 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
index 425c224606038..02ff1e249d3bc 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
index 1d405a816e110..41474f9ca1e48 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
index 38e14438f3fd3..9a891eab6b343 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
index 88672851f7b6b..3e1d34ab5257a 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
index d0ff2c87689d1..4c299b876639e 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
index 98fa56b7f4b6d..7970887bac199 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
index 283a8fddb8e8f..13e6f74c5a143 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -17,6 +17,9 @@
 // fmemopen is available starting in Android M (API 23)
 // XFAIL: target={{.+}}-android{{(eabi)?(21|22)}}
 
+// OHOS fmemopen/FILE behavior does not currently match the expectation tested here.
+// XFAIL: LIBCXX-OHOS-FIXME
+
 // <print>
 
 // The FILE returned by fmemopen does not have file descriptor.
diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
index b24c0b90d86f1..2ddddbef70dbb 100644
--- a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
@@ -18,6 +18,7 @@
 // XFAIL: target=armv{{7|8}}{{l?}}{{.*}}-linux-gnueabihf && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-linux-gnu && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-amazon-linux && libcpp-abi-version=1
+// XFAIL: target={{.*}}-linux-ohos && libcpp-abi-version=1
 
 #include <iomanip>
 #include <ostream>
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
index da5563f87df45..ea5af90a90822 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
index 4f23b6b9a5d87..6dc0ff36ff4bf 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
@@ -14,6 +14,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
index 9bedd67e816ef..cbb9639766207 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
index ec4c8a009ba0c..59d93e74db3b7 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
index ae82035ccfc62..49a6ce0892ea4 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
index 6ae1972234b2b..58a6e18340ddf 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
index 08b74b999bbf4..88dcfe0485429 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
index 89e178248ae50..9c9745dcc9777 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wclog;
 
 // XFAIL: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
index 0e411f8adc0f2..e563152f2c8cd 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
index 026250daeee89..34119303a29ba 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete [] is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
index ca0f612bf7578..a4e32aaa11e13 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
index fbc9cf070bcba..3ec2b47b53eb0 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
index 8ae6bc2d3ba66..7cfd9f955fce7 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
@@ -8,6 +8,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
index 4978db116ef39..743075ede8ece 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
index 7be7e8d574e6c..b77a4409522f0 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
@@ -11,6 +11,7 @@
 // template <> class ctype<char>
 
 // ~ctype();
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <locale>
 #include <cassert>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
index 5cebc079c4703..c4803c04d173b 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <type_traits>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
index 2869378b42b92..5174ffebf281f 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
index ea03f042f2d45..665da7323e38a 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
index 2f7acb843e591..5d87c081b6e1c 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
index 447353de8af72..93d93aa9228f7 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
index 94ecc6c8fa85b..ab448a01632d6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
index 2b37737a26bc3..4be990e5b88c6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
index 016d8d736ad22..e0a3912a9315e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
index 959a4be9e1de9..b22629c82444e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
index 078b4a6fefb7e..c2cd904808794 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
index 9997b07134563..73b7a6decda36 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
index c86df7e6b53bf..deb30d747653f 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
index 478df7964f6d2..ee6fbd48b09b6 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
index 4b767fae871fa..c476537c3c7e3 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
index 25046a8417083..8174a724d4165 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
index 1c8710a008f27..e23cb4bf5f04a 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
index f0086ada30303..a36e3af8d12eb 100644
--- a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
+++ b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
@@ -12,6 +12,7 @@
 
 // This test runs in C++20, but we have deprecated codecvt<char(16|32), char, mbstate_t> in C++20.
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // <locale>
 
diff --git a/libcxx/test/std/re/re.traits/transform.pass.cpp b/libcxx/test/std/re/re.traits/transform.pass.cpp
index 80cd3f01faff2..112ecd47ff4e2 100644
--- a/libcxx/test/std/re/re.traits/transform.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform.pass.cpp
@@ -8,6 +8,7 @@
 //
 // NetBSD does not support LC_COLLATE at the moment
 // XFAIL: netbsd
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..cbc9387facb30 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -11,6 +11,7 @@
 
 // XFAIL: LIBCXX-AIX-FIXME
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
index 40daa49393bd4..110d0ea9fd694 100644
--- a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
+++ b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
@@ -15,6 +15,7 @@
 
 // There is no way to limit the number of threads on windows
 // UNSUPPORTED: windows
+// UNSUPPORTED: executor-runs-as-root
 
 // macOS and FreeBSD seem to limit the number of processes, not threads via RLIMIT_NPROC.
 // XFAIL: target={{.+}}-apple-{{.*}}
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
index c6eb0694ab2e1..19573ae52fd71 100644
--- a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
@@ -9,6 +9,7 @@
 // Windows cannot detect the deadlock. Instead of throwing system_error,
 // it would dead lock the test
 // UNSUPPORTED: windows
+// UNSUPPORTED: pthread-join-self-deadlocks
 
 // TSAN bug: https://llvm.org/PR66537
 // UNSUPPORTED: tsan
diff --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
index 51be78e1a02f8..804036e425572 100644
--- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
@@ -14,6 +14,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..6901aff00bc4e 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -415,6 +415,13 @@ void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteCalled(p);
+  std::free(p);
+}
+#  endif
+
 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->newArrayCalled(s);
@@ -449,6 +456,13 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteArrayCalled(p);
+  std::free(p);
+}
+#  endif
+
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
 #      define USE_ALIGNED_ALLOC
@@ -519,6 +533,13 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
@@ -551,6 +572,13 @@ void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
 
 #endif // DISABLE_NEW_COUNT
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index 2ad9efb32c60f..f820a4a0fa915 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -182,7 +182,7 @@ struct scoped_test_env
         std::string cmd = "chmod -R 777 " + test_root.string();
 #endif // defined(__MVS__)
         int ret = std::system(cmd.c_str());
-#  if !defined(_AIX) && !defined(__ANDROID__)
+#  if !defined(_AIX) && !defined(__ANDROID__) && !defined(__OHOS__)
         // On AIX the chmod command will return non-zero when trying to set
         // the permissions on a directory that contains a bad symlink. This triggers
         // the assert, despite being able to delete everything with the following
@@ -196,6 +196,8 @@ struct scoped_test_env
         // LIBCXX-ANDROID-FIXME: Other fixes to consider: place a toybox chmod
         // onto old devices, re-enable this assert for devices running Android N
         // and up, rewrite this chmod+rm in C or C++.
+        //
+        // OHOS has the same chmod -R behavior for bad symlinks.
         assert(ret == 0);
 #  endif
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..fdf6bc5b18d72 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -375,7 +375,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NOT_WIN32(...) __VA_ARGS__
 #endif
 
-#if defined(TEST_WINDOWS_DLL) ||defined(__MVS__) || defined(_AIX)
+#if defined(TEST_WINDOWS_DLL) || defined(__MVS__) || defined(_AIX) || defined(TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS)
 // Macros for waiving cases when we can't count allocations done within
 // the library implementation.
 //
@@ -386,6 +386,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 //
 // The same goes on IBM zOS.
 // The same goes on AIX.
+// Some target test configurations can define
+// TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS for the same reason.
 #define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
 #define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
 #else
diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp
index 36536860c1a96..b28f70a6e8e7a 100644
--- a/libunwind/test/aarch64_za_unwind.pass.cpp
+++ b/libunwind/test/aarch64_za_unwind.pass.cpp
@@ -20,6 +20,9 @@
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
 #endif
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
 
 // Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA
 // (and commits a lazy save of ZA) before resuming from unwinding.
@@ -33,18 +36,16 @@ static bool checkHasSME() {
     return false;
   return has_sme != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = getauxval(AT_HWCAP2);
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = 0;
   elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
 #else
 static bool checkHasSME() {

>From bbaa18370c45aaf79c2446be0a6e94dc2aa0ef00 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Fri, 3 Jul 2026 10:33:38 +0800
Subject: [PATCH 4/6] Add qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  16 +-
 libcxx/utils/ci/run-buildbot                  |  41 ++++-
 libcxx/utils/ci/vendor/ohos/qemu_run.py       | 144 ++++++++++++++++++
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |   8 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |   8 +-
 5 files changed, 204 insertions(+), 13 deletions(-)
 create mode 100755 libcxx/utils/ci/vendor/ohos/qemu_run.py

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 542bc0cc83a1f..20aa7a49d5802 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++ using HDC on OHOS.
+# libc++ using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -23,15 +23,21 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
 config.available_features.add('LIBCXX-OHOS-FIXME')
-config.available_features.add('ohos-hdc')
-config.available_features.add('executor-has-no-stdin')
-config.available_features.add('executor-merges-stderr-into-stdout')
-config.available_features.add('executor-runs-as-root')
+config.available_features.add(executor_kind)
 config.available_features.add('platform-has-nonportable-locales')
 config.available_features.add('platform-has-unreliable-new-delete-interposition')
 config.available_features.add('pthread-join-self-deadlocks')
 
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
     libcxx.test.features.DEFAULT_FEATURES,
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index ecf0ca17ae148..40739b0fba4d8 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -28,6 +28,16 @@ ${PROGNAME} [options] <BUILDER>
 --build-dir <DIR>   The directory to use for building the library. By default,
                     this is '<llvm-root>/build/<builder>'.
 
+--ohos-qemu <PATH>  Run OHOS tests under qemu-aarch64 instead of HDC. This also
+                    accepts OHOS_QEMU or QEMU from the environment.
+
+--ohos-qemu-sysroot <DIR>
+                    Sysroot passed to qemu-aarch64 with -L. Defaults to
+                    OHOS_QEMU_SYSROOT, QEMU_SYSROOT, then OHOS_SYSROOT.
+
+--ohos-hdc <PATH>   HDC binary to use when OHOS tests run on a device. This sets
+                    HDC for the OHOS HDC executor.
+
 Environment variables
 CC                  The C compiler to use, this value is used by CMake. This
                     variable is mandatory.
@@ -84,6 +94,19 @@ while [[ $# -gt 0 ]]; do
             BUILD_DIR="${2}"
             shift; shift
             ;;
+        --ohos-qemu)
+            OHOS_QEMU="${2}"
+            shift; shift
+            ;;
+        --ohos-qemu-sysroot)
+            OHOS_QEMU_SYSROOT="${2}"
+            shift; shift
+            ;;
+        --ohos-hdc)
+            HDC="${2}"
+            export HDC
+            shift; shift
+            ;;
         *)
             BUILDER="${1}"
             shift
@@ -193,8 +216,18 @@ function setup-ohos-hdc-env() {
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
     OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
-    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
-    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+    OHOS_QEMU="${OHOS_QEMU:-${QEMU:-}}"
+
+    if [[ -n "${OHOS_QEMU}" ]]; then
+        OHOS_QEMU_SYSROOT="${OHOS_QEMU_SYSROOT:-${QEMU_SYSROOT:-${OHOS_SYSROOT}}}"
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/qemu_run.py --qemu ${OHOS_QEMU} --sysroot ${OHOS_QEMU_SYSROOT}"
+        OHOS_EXECUTOR_KIND="ohos-qemu"
+    else
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+        OHOS_EXECUTOR_KIND="ohos-hdc"
+    fi
+
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE};executor_kind=${OHOS_EXECUTOR_KIND}"
 }
 
 function generate-cmake-ohos-hdc() {
@@ -863,14 +896,14 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
-ohos-hdc-aarch64-full)
+ohos-aarch64-full|ohos-hdc-aarch64-full)
     clean
 
     generate-cmake-ohos-hdc
 
     check-runtimes
 ;;
-ohos-hdc-aarch64-smoke)
+ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
     clean
 
     generate-cmake-ohos-hdc
diff --git a/libcxx/utils/ci/vendor/ohos/qemu_run.py b/libcxx/utils/ci/vendor/ohos/qemu_run.py
new file mode 100755
index 0000000000000..ef7eef7872864
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/qemu_run.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _resolve_binary(binary):
+    if os.path.isabs(binary):
+        if os.path.isfile(binary) and os.access(binary, os.X_OK):
+            return binary
+        sys.exit(f"failed to find executable: {binary}")
+
+    resolved = shutil.which(binary)
+    if resolved is None:
+        sys.exit(f"failed to find executable: {binary}")
+    return resolved
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(str(parent))
+    return library_dirs
+
+
+def _build_target_env_args(env, prepend_env):
+    target_env = dict(env)
+
+    for key, value in prepend_env.items():
+        existing = target_env.get(key, os.environ.get(key, ""))
+        target_env[key] = f"{value}{os.pathsep}{existing}" if existing else value
+
+    args = []
+    for key, value in target_env.items():
+        args.extend(["-E", f"{key}={value}"])
+    return args
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--qemu",
+        default=os.environ.get("OHOS_QEMU", os.environ.get("QEMU", "qemu-aarch64")),
+    )
+    parser.add_argument("--sysroot", required=True)
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(
+            os.environ.get(
+                "OHOS_QEMU_TIMEOUT_SECONDS",
+                os.environ.get("QEMU_TIMEOUT_SECONDS", "600"),
+            )
+        ),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    qemu = _resolve_binary(args.qemu)
+    execdir = Path(args.execdir).resolve()
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    build_library_dirs = _collect_build_library_dirs(execdir)
+    if build_library_dirs:
+        existing = prepend_env.get("LD_LIBRARY_PATH")
+        build_library_path = os.pathsep.join(build_library_dirs)
+        prepend_env["LD_LIBRARY_PATH"] = (
+            f"{build_library_path}{os.pathsep}{existing}"
+            if existing
+            else build_library_path
+        )
+
+    cmd = [
+        qemu,
+        "-L",
+        args.sysroot,
+        *_build_target_env_args(env, prepend_env),
+        *args.command,
+    ]
+    try:
+        completed = subprocess.run(
+            cmd,
+            cwd=execdir,
+            timeout=args.timeout_seconds,
+            check=False,
+        )
+    except subprocess.TimeoutExpired:
+        print(
+            f"qemu executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+            file=sys.stderr,
+        )
+        return 124
+    return completed.returncode
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index d18748fe3a3cc..8f5a17b3dec64 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++abi using HDC on OHOS.
+# libc++abi using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -20,7 +20,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index bce241a7898cf..f2a032c41a123 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -1,6 +1,6 @@
 #
 # Configuration file for running the libunwind tests against the shared library
-# using HDC on OHOS.
+# using HDC or QEMU on OHOS.
 #
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
@@ -45,7 +45,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,

>From dde4d430033711fb7ffa87621ba86b37492f480b Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 14:52:40 +0800
Subject: [PATCH 5/6] Fix something for qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in                   | 4 +++-
 .../file.streams/fstreams/filebuf.members/close.pass.cpp      | 3 +++
 .../fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp     | 2 ++
 .../syncstream/osyncstream/thread/several_threads.pass.cpp    | 2 ++
 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in             | 4 +++-
 libcxxabi/test/guard_threaded_test.pass.cpp                   | 2 ++
 libunwind/test/bad_unwind_info.pass.cpp                       | 2 ++
 libunwind/test/configs/llvm-libunwind-ohos.cfg.in             | 4 +++-
 libunwind/test/signal_unwind.pass.cpp                         | 2 ++
 9 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 20aa7a49d5802..3be1c267ede2a 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -23,7 +23,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
index 2c4cfb0ac7661..ce802d6ceba78 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14
 // REQUIRES: linux
 // UNSUPPORTED: no-filesystem
+// OHOS qemu-user observes the host /proc, not a real OHOS process /proc.
+// UNSUPPORTED: ohos-qemu
 // XFAIL: no-localization
 
 // <filesystem>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..77db6396755bf 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -10,6 +10,8 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
+// This thread-heavy stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 // <syncstream>
 
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index 8f5a17b3dec64..d0a8d1248f88a 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -20,7 +20,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxxabi/test/guard_threaded_test.pass.cpp b/libcxxabi/test/guard_threaded_test.pass.cpp
index d898e60288aca..a4e14f0aca9e4 100644
--- a/libcxxabi/test/guard_threaded_test.pass.cpp
+++ b/libcxxabi/test/guard_threaded_test.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: no-exceptions
+// This thread-heavy guard stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 #define TESTING_CXA_GUARD
 #include "../src/cxa_guard_impl.h"
diff --git a/libunwind/test/bad_unwind_info.pass.cpp b/libunwind/test/bad_unwind_info.pass.cpp
index 332b661d2e98f..76102505f2d67 100644
--- a/libunwind/test/bad_unwind_info.pass.cpp
+++ b/libunwind/test/bad_unwind_info.pass.cpp
@@ -13,6 +13,8 @@
 // REQUIRES: target={{(aarch64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
 // UNSUPPORTED: target={{.*-apple.*}}
+// OHOS qemu-user does not reliably model this invalid unwind-info path.
+// UNSUPPORTED: ohos-qemu
 
 // GCC doesn't support __attribute__((naked)) on AArch64.
 // UNSUPPORTED: gcc
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index f2a032c41a123..71d1e711e36d5 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -45,7 +45,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp
index f8798bc3eabeb..ef73a3ea8c59a 100644
--- a/libunwind/test/signal_unwind.pass.cpp
+++ b/libunwind/test/signal_unwind.pass.cpp
@@ -10,6 +10,8 @@
 // Ensure that the unwinder can cope with the signal handler.
 // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
+// OHOS qemu-user does not provide device-equivalent signal unwinding behavior.
+// UNSUPPORTED: ohos-qemu
 
 // TODO: Figure out why this fails with Memory Sanitizer.
 // XFAIL: msan

>From c81df7252fb00b5b9b41f0a29393e775abc9c810 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 15:19:29 +0800
Subject: [PATCH 6/6] delete run-smoke-test

---
 libcxx/utils/ci/buildkite-pipeline.yml | 12 ----------
 libcxx/utils/ci/run-buildbot           | 31 --------------------------
 2 files changed, 43 deletions(-)

diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 77617f43ae6dc..70bc69637a7f2 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -175,18 +175,6 @@ steps:
 
 - group: OHOS
   steps:
-  - label: OHOS AArch64 HDC Smoke
-    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
-    env:
-      CC: clang
-      CXX: clang++
-      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
-      HDC_TIMEOUT_SECONDS: 240
-    agents:
-      queue: libcxx-builders
-      os: linux
-      device: ohos-hdc
-    <<: *common
   - label: OHOS AArch64 HDC Full
     command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
     env:
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 40739b0fba4d8..e7d751df484e4 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -147,23 +147,6 @@ function require-env-var() {
     fi
 }
 
-function detect-lit() {
-    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
-        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
-    elif command -v llvm-lit >/dev/null 2>&1; then
-        LIT_RUNNER=("$(command -v llvm-lit)")
-    else
-        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
-    fi
-}
-
-function run-smoke-test() {
-    local test_dir="${1}"
-    local filter="${2}"
-    step "Running smoke test ${filter}"
-    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
-}
-
 function ohos-lit-args() {
     local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
     if python3 -c 'import psutil' >/dev/null 2>&1; then
@@ -903,20 +886,6 @@ ohos-aarch64-full|ohos-hdc-aarch64-full)
 
     check-runtimes
 ;;
-ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
-    clean
-
-    generate-cmake-ohos-hdc
-
-    step "Building the OHOS smoke test dependencies"
-    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
-
-    detect-lit
-    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
-;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #


From libcxx-commits at lists.llvm.org  Fri Jul 10 00:15:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 00:15:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a509bfd.13be6df8.145c.b092@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/186692

>From 816aec4f1b5369071ad16b3f64a5e1563f75bec6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 12 Apr 2026 09:35:44 +0100
Subject: [PATCH 01/20] [libcxx] Implement `std::constant_wrapper`

ci

ci

review comments

review comments

ci

review comments
---
 .../containers/views/views.span/span.cons/deduct.pass.cpp  | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index 9e41afad2258e..c9e17b3aec49a 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -73,6 +73,13 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
+
+  {
+    std::span s{std::begin(arr), std::cw<3>};
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+    assert(s.size() == std::size(arr));
+    assert(s.data() == std::data(arr));
+  }
 #endif
 }
 

>From 049c297c0e043ebb00e74cdb627a0934a0a7e46b Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 8 Jun 2024 17:32:42 -0700
Subject: [PATCH 02/20] [libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

rebase + put in experimental + rename nontype to constant_arg

tests

remove experimental

test for constant_arg_ref

test for constant_arg_ptr

remove fun_ptr member

invoke

format

constexpr function ptr

double unwrapping

P3948R1

tests for new papers

format

lwg issue

lwg issues

review comment

remove constexpr

optimise small types

ci

ci

ci

ci
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/23.rst               |   3 +
 libcxx/docs/Status/Cxx26Issues.csv            |   4 +-
 libcxx/docs/Status/Cxx26Papers.csv            |   6 +-
 libcxx/include/CMakeLists.txt                 |   3 +
 libcxx/include/__functional/function_ref.h    |  33 ++
 .../__functional/function_ref_common.h        | 126 ++++++
 .../include/__functional/function_ref_impl.h  | 184 +++++++++
 libcxx/include/functional                     |   9 +
 libcxx/include/module.modulemap.in            |   3 +
 libcxx/include/version                        |   4 +-
 libcxx/modules/std/functional.inc             |   4 +
 .../assert.constant_arg_ptr.pass.cpp          |  31 ++
 .../assert.function_ptr.pass.cpp              |  26 ++
 .../constant_arg.mandates.verify.cpp          |  60 +++
 .../internal_call_signature.pass.cpp          | 136 +++++++
 .../functional.version.compile.pass.cpp       |  16 +-
 .../version.version.compile.pass.cpp          |  16 +-
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     | 107 +++++
 .../func.wrap.ref.ctor/assign.delete.pass.cpp | 115 ++++++
 .../constant_wrapper.pass.cpp                 | 175 ++++++++
 .../constant_wrapper_ptr.pass.cpp             | 359 +++++++++++++++++
 .../constant_wrapper_ref.pass.cpp             | 373 ++++++++++++++++++
 .../func.wrap.ref.ctor/copy.pass.cpp          | 131 ++++++
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   | 158 ++++++++
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  | 149 +++++++
 .../func.wrap.ref.ctor/move.pass.cpp          | 129 ++++++
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 156 ++++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 316 +++++++++++++++
 .../func.wrap.ref.inv/invoke.pass.cpp         | 362 +++++++++++++++++
 .../trivially_copyable.compile.pass.cpp       |  33 ++
 .../generate_feature_test_macro_components.py |   3 +-
 32 files changed, 3200 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/include/__functional/function_ref.h
 create mode 100644 libcxx/include/__functional/function_ref_common.h
 create mode 100644 libcxx/include/__functional/function_ref_impl.h
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..cd360ae2b1317 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -480,7 +480,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_fstream_native_handle``                        ``202306L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_function_ref``                                 *unimplemented*
+    ``__cpp_lib_function_ref``                                 ``202603L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_generate_random``                              *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..ecf3596ac66c4 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,9 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index e8edd9eefe1b1..900ebb4f2aa8a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","","","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","","","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..c83f46c288d7e 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."
@@ -192,9 +192,9 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","","","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","","","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..a0d5cf70d267d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -438,6 +438,9 @@ set(files
   __functional/compose.h
   __functional/default_searcher.h
   __functional/function.h
+  __functional/function_ref.h
+  __functional/function_ref_common.h
+  __functional/function_ref_impl.h
   __functional/hash.h
   __functional/identity.h
   __functional/invoke.h
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
new file mode 100644
index 0000000000000..b7fd3a4a2c79f
--- /dev/null
+++ b/libcxx/include/__functional/function_ref.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+
+#include <__config>
+#include <__functional/function_ref_common.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26
+// NOLINTBEGIN(readability-duplicate-include)
+
+#  define _LIBCPP_FUNCTION_REF_CV
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+#  define _LIBCPP_FUNCTION_REF_CV const
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+// NOLINTEND(readability-duplicate-include)
+#endif // _LIBCPP_STD_VER >= 26
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
new file mode 100644
index 0000000000000..61979231b56e8
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_COMMON_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+
+#include <__config>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/constant_wrapper.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization : false_type {};
+
+// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+// pointers
+// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
+// supported
+union __storage_func_ref_t {
+  void* __obj_ptr_;
+  void const* __obj_const_ptr_;
+  void (*__fn_ptr_)();
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        __obj_const_ptr_ = __ptr;
+      } else {
+        __obj_ptr_ = __ptr;
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
+    }
+  }
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
+      } else {
+        return static_cast<_Tp*>(__storage.__obj_ptr_);
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
+    }
+  }
+};
+
+template <class _Fp, class _Tp>
+struct __function_ref_bind {};
+
+// F is of the form R(*)(G, A...) noexcept(E) for a type G.
+template <bool _Noexcept, class _Tp, class _Rp, class _Gp, class... _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept(_Noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_Noexcept);
+};
+
+template <class _Tp, class _Mp, class _Gp>
+  requires is_object_v<_Mp>
+struct __function_ref_bind<_Mp _Gp::*, _Tp> {
+  using type _LIBCPP_NODEBUG = invoke_result_t<_Mp _Gp::*, _Tp&>() noexcept;
+};
+
+template <class _Fp, class _Tp>
+using __function_ref_bind_t _LIBCPP_NODEBUG = __function_ref_bind<_Fp, _Tp>::type;
+
+template <class _Fp>
+  requires is_function_v<_Fp>
+function_ref(_Fp*) -> function_ref<_Fp>;
+
+template <auto _Cw, class _Fn>
+  requires is_function_v<remove_pointer_t<_Fn>>
+function_ref(constant_wrapper<_Cw, _Fn>) -> function_ref<remove_pointer_t<_Fn>>;
+
+template <auto _Cw, class _Fn, class _Tp>
+function_ref(constant_wrapper<_Cw, _Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<_Fn, _Tp&>>;
+
+template <class>
+constexpr bool __is_constant_wrapper = false;
+
+template <auto _Value>
+constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
+
+template <class _Arg>
+struct __function_ref_arg_fwd {
+  using type _LIBCPP_NODEBUG = _Arg&&;
+};
+
+template <class _Arg>
+  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
+struct __function_ref_arg_fwd<_Arg> {
+  using type _LIBCPP_NODEBUG = _Arg;
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
new file mode 100644
index 0000000000000..a4c98d3031602
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -0,0 +1,184 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
+// and generates multiple versions of std::function_ref
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#include <__functional/invoke.h>
+#include <__memory/addressof.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/constant_wrapper.h>
+#include <__utility/forward.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#  error This header should only be included from function_ref.h
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
+    _NoExcept1,
+    _Rp,
+    _ArgTypes...>
+    : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
+
+template <class _Rp, class... _ArgTypes, bool __is_noexcept>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
+private:
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using =
+      _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
+          value;
+
+  template <class _Fn>
+  static constexpr bool __is_convertible_from_specialization_v =
+      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+
+  template <class... _Tp>
+  friend class function_ref;
+
+  template <class _Arg>
+  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+
+  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+
+  using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
+
+  __storage_t __storage_;
+  __call_t __call_;
+
+public:
+  template <class _Fp>
+    requires is_function_v<_Fp> && __is_invocable_using<_Fp>
+  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+      : __storage_(__fn_ptr),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+  }
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {}
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && __is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(__obj.__storage_), __call_(__obj.__call_) {}
+
+  template <auto _Cw, class _Fn>
+    requires __is_invocable_using<const _Fn&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
+      : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+    if constexpr (sizeof...(_ArgTypes) > 0 && (__constexpr_param<remove_cvref_t<_ArgTypes>> && ...)) {
+      static_assert(
+          !requires {
+            typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
+          },
+          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
+          "and produces a result with a structural type");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
+    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Tp>
+    requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
+      : __storage_(__obj_ptr_),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+
+    if constexpr (is_member_pointer_v<_Fn>) {
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(const function_ref&) noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+  template <class _Tp>
+    requires(!__is_convertible_from_specialization_v<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_wrapper<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
+
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
+    return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
+  }
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 9d588231d548d..df79d616a57e2 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -487,6 +487,11 @@ template <class  R, class ... ArgTypes>
 template <class  R, class ... ArgTypes>
   void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
 
+// [func.wrap.ref], non-owning wrapper
+template<class... S> class function_ref;                              // freestanding, not defined, since C++26
+template<class R, class... ArgTypes>
+  class function_ref<R(ArgTypes...) cv noexcept(noex)>;               // freestanding, since C++26
+
 template <class T> struct hash;
 
 template <> struct hash<bool>;
@@ -571,6 +576,10 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 #    include <__type_traits/unwrap_ref.h>
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+#    include <__functional/function_ref.h>
+#  endif
+
 #  include <version>
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..cb29fac536046 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1438,6 +1438,9 @@ module std {
     }
     module default_searcher             { header "__functional/default_searcher.h" }
     module function                     { header "__functional/function.h" }
+    module function_ref                 { header "__functional/function_ref.h" }
+    module function_ref_common          { header "__functional/function_ref_common.h" }
+    module function_ref_impl            { textual header "__functional/function_ref_impl.h" }
     module hash                         { header "__functional/hash.h" }
     module identity                     { header "__functional/identity.h" }
     module invoke                       { header "__functional/invoke.h" }
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..c62a7cbc8ba9c 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -131,7 +131,7 @@ __cpp_lib_freestanding_optional                         202311L <optional>
 __cpp_lib_freestanding_string_view                      202311L <string_view>
 __cpp_lib_freestanding_variant                          202311L <variant>
 __cpp_lib_fstream_native_handle                         202306L <fstream>
-__cpp_lib_function_ref                                  202306L <functional>
+__cpp_lib_function_ref                                  202603L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
@@ -593,7 +593,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 #   define __cpp_lib_fstream_native_handle              202306L
 # endif
-// # define __cpp_lib_function_ref                         202306L
+# define __cpp_lib_function_ref                         202603L
 // # define __cpp_lib_generate_random                      202403L
 // # define __cpp_lib_hazard_pointer                       202306L
 // # define __cpp_lib_inplace_vector                       202406L
diff --git a/libcxx/modules/std/functional.inc b/libcxx/modules/std/functional.inc
index 9ef8f584611fc..be804b19f75f8 100644
--- a/libcxx/modules/std/functional.inc
+++ b/libcxx/modules/std/functional.inc
@@ -95,6 +95,10 @@ export namespace std {
 
   using std::function;
 
+#if _LIBCPP_STD_VER >= 26
+  using std::function_ref;
+#endif // _LIBCPP_STD_VER >= 26
+
   using std::swap;
 
   using std::operator==;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
new file mode 100644
index 0000000000000..9703de0a59c2c
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+// Preconditions: If is_member_pointer_v<F> is true, obj is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+struct A {
+  void f() {}
+};
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(std::function_ref<void()>(std::cw<&A::f>, static_cast<A*>(nullptr)),
+                             "the object pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
new file mode 100644
index 0000000000000..e69776d0e3c90
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<class F> function_ref(F* f) noexcept;
+// Preconditions: f is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::function_ref<void()>(static_cast<void (*)()>(nullptr)), "the function pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
new file mode 100644
index 0000000000000..519957eec6878
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto c, class F>
+//   constexpr function_ref(constant_wrapper<c, F>) noexcept;
+// template<auto c, class F, class U>
+//   constexpr function_ref(constant_wrapper<c, F>, U&& obj) noexcept;
+// template<auto c, class F, class T>
+//   constexpr function_ref(constant_wrapper<c, F>, cv T* obj) noexcept;
+
+// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
+
+// For the first overload,
+// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+
+#include <functional>
+#include <utility>
+
+struct A {
+  void f();
+};
+
+struct B {
+  constexpr int operator()(std::constant_wrapper<42>) const { return 42; }
+  constexpr int operator()(int) const { return 42; }
+};
+
+// clang-format off
+void test() {
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+
+  int i;
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  A a;
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+}
+// clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
new file mode 100644
index 0000000000000..05756cfc3b3b8
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Track number of moves
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+struct Small {};
+
+struct Big {
+  char c[128];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
+// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
+template <>
+class std::function_ref<int> {
+  using storage = std::__storage_func_ref_t;
+  void test() {
+    {
+      // by value small argument
+      // the internal function should pass by value
+      using call = std::function_ref<void(Small)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
+    }
+    {
+      // by value big argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Big)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
+    }
+    {
+      // by value non-trivial small argument
+      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
+      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
+    }
+    {
+      // by lvalue reference argument
+      // the internal function should pass by lvalue reference
+      using call = std::function_ref<void(Small&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
+    }
+    {
+      // by rvalue reference argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Small&&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
+    }
+  }
+};
+
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
+void test() {
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In this case, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index b7b7d0334830a..bcc453880c7e1 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -494,17 +494,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_invoke
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 f2d74cc99d864..c990ef86c3be1 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
@@ -7138,17 +7138,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_gcd_lcm
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
new file mode 100644
index 0000000000000..029cb19d54f6a
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+int fn(int, float) { return 42; }
+
+int fn_noexcept(int, float) noexcept { return 42; }
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem(int, float) { return 42; }
+  int fn_mem_ref(int, float) & { return 42; }
+  int fn_mem_const(int, float) const { return 42; }
+  int fn_mem_const_ref(int, float) const& { return 42; }
+  int fn_mem_noexcept(int, float) noexcept { return 42; }
+};
+
+void test() {
+  // template<class F>
+  //  function_ref(F*) -> function_ref<F>;
+  {
+    std::function_ref fn_ref = fn;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = fn_noexcept;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F>
+  //  function_ref(constant_wrapper<c, F>) -> function_ref<...>;
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn_noexcept>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F, class T>
+  //  function_ref(constant_wrapper<c, F>, T&&) -> function_ref<...>;
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
+  }
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn_noexcept>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float) noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&() noexcept>>);
+  }
+  {
+    const S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&() noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_noexcept>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
new file mode 100644
index 0000000000000..b4df4392a2ade
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class T> function_ref& operator=(T) = delete;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - T is not the same type as function_ref,
+// - is_pointer_v<T> is false, and
+// - T is not a specialization of constant_wrapper.
+
+// non const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[] {}>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
+
+// const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[] { return 42; }>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[](int) { return 42; }>>);
+
+// non-const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[] noexcept {} >>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+// const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[] noexcept {}>>);
+static_assert(
+    !std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+int forty_two() { return 42; }
+
+void test() {
+  {
+    std::function_ref<int()> f(std::cw<[] { return 41; }>);
+    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = &forty_two;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const noexcept
+    std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
new file mode 100644
index 0000000000000..4b425ee03e286
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
@@ -0,0 +1,175 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f> constexpr function_ref(constant_wrapper<f>) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&> is true.
+
+auto l1          = [] {};
+auto l1_noexcept = [] noexcept {};
+auto l2          = [](int) {};
+auto l2_noexcept = [](int) noexcept {};
+
+struct NonConstInvocable {
+  void operator()() noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+// const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    std::function_ref<void()> f = std::cw<[] {}>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
new file mode 100644
index 0000000000000..dd6e10e8a3289
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -0,0 +1,359 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&, cv T*> is true.
+
+auto l1                = [](int*) {};
+auto l1_const          = [](const int*) {};
+auto l1_noexcept       = [](int*) noexcept {};
+auto l1_const_noexcept = [](const int*) noexcept {};
+auto l2                = [](int*, double) {};
+auto l2_const          = [](const int*, double) {};
+auto l2_noexcept       = [](int*, double) noexcept {};
+auto l2_const_noexcept = [](const int*, double) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(int*) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, const int*>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2_const>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const noexcept>,
+                                      std::constant_wrapper<l2_const_noexcept>,
+                                      int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_const_noexcept>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<l1_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const noexcept>,
+                                              std::constant_wrapper<l2_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<&A::f_const_noexcept>,
+                                              A*>);
+
+double f1(const int* x, double y) noexcept { return *x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int*) {}>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int*) {}>, &i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int* j, double d) {
+          *j = 5;
+          return *j + d;
+        }>,
+        &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
new file mode 100644
index 0000000000000..485fa95ca2998
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -0,0 +1,373 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class U>
+//   constexpr function_ref(constant_wrapper<f>, U&& obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_rvalue_reference_v<U&&> is false, and
+// - is-invocable-using<const F&, cv T&> is true.
+
+auto l1          = [](int) {};
+auto l1_noexcept = [](int) noexcept {};
+auto l2          = [](int, double) {};
+auto l2_noexcept = [](int, double) noexcept {};
+auto l3          = [](int&) {};
+auto l3_noexcept = [](int&) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(long) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&&>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int) {}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int) {}>, i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int& j, double d) {
+          j = 5;
+          return j + d;
+        }>,
+        i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
new file mode 100644
index 0000000000000..61b70a559d786
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
new file mode 100644
index 0000000000000..1a8b26f86c8be
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
new file mode 100644
index 0000000000000..f142b81367ef1
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> function_ref(F* f) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_function_v<F> is true, and
+// - is-invocable-using<F> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (*)(int)>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A (*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, A)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+
+// const noexcept(true)
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, A) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              void (*)(int, double) noexcept>);
+
+int fn() { return 42; }
+
+int fn_maythrow(int i, A a) { return i - a.i; }
+int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+
+void foo(int) {}
+void bar(int) noexcept {}
+struct Int {
+  int i;
+  Int(int ii) noexcept : i(ii) {}
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+void test() {
+  {
+    // simple case
+    std::function_ref<int()> f(&fn);
+    assert(f() == 42);
+  }
+  {
+    // explicit(false)
+    std::function_ref<int()> f = &fn;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int(int, A)> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // noexcept
+    std::function_ref<int(int, A) noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // const
+    auto l = [](int x, int y, int z) { return x + y - z; };
+    std::function_ref<int(int, int, int) const> f(+l);
+    assert(f(2, 3, 4) == 1);
+  }
+  {
+    // const noexcept
+    std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+
+  {
+    std::function_ref<Int(int, int, int)> f(&needs_conversion);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(&needs_conversion);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(&needs_conversion);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
+    assert(f4(1, 2, 3).i == 6);
+
+    {
+      std::function_ref r1 = foo;
+      std::function_ref r2 = bar;
+      r1                   = r2; // ok
+    }
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
new file mode 100644
index 0000000000000..0f27bba821391
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
new file mode 100644
index 0000000000000..b40dfa9f2cf4b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
new file mode 100644
index 0000000000000..56d6dd3b7109b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -0,0 +1,316 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> constexpr function_ref(F&&) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - remove_cvref_t<F> is not the same type as function_ref,
+// - is_member_pointer_v<T> is false, and
+// - is-invocable-using<cv T&> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+constexpr auto l1 = [] {};
+using L1          = std::remove_cvref_t<decltype(l1)>;
+
+constexpr auto l2 = [] { return A{5}; };
+using L2          = std::remove_cvref_t<decltype(l2)>;
+
+constexpr auto l2_noexcept = [] noexcept { return A{5}; };
+using L2Noexcept           = std::remove_cvref_t<decltype(l2_noexcept)>;
+
+constexpr auto l3 = [](int x, double d) { return x + d; };
+using L3          = std::remove_cvref_t<decltype(l3)>;
+
+constexpr auto l3_noexcept = [](int x, double d) noexcept { return x + d; };
+using L3Noexcept           = std::remove_cvref_t<decltype(l3_noexcept)>;
+
+struct NonConstInvocable {
+  int i;
+
+  int operator()(int x, double y) {
+    ++i;
+    return x + y + i;
+  }
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L2&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2&>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2 const&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const>, std::function_ref<int(int, double) const>>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(int, double)>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double)>, const NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>,
+                                              std::function_ref<int(int, double) const>>);
+
+// const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                      std::function_ref<int(int, double) const noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              std::function_ref<int(int, double) const noexcept>>);
+
+struct F {
+  int i;
+  int operator()(auto&&...) { return 5 + i; }
+
+  int operator()(auto&&...) const { return 6 + i; }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int fn() { return 5; }
+constexpr auto fn_noexcept = []() noexcept { return 6; };
+
+const auto one = []() noexcept { return 1; };
+const auto two = []() noexcept { return 2; };
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(l1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit(false)
+    std::function_ref<void()> f = l1;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<A() noexcept> f(l2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      auto a = f();
+      assert(a.i == 5);
+    }
+  }
+  {
+    // const
+    std::function_ref<double(int, double) const> f(l3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(l3_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // no copies of original callable
+    auto local = [i = 5] mutable { return i++; };
+    std::function_ref<int()> f(local);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 5);
+      assert(local() == 6);
+      assert(f() == 7);
+    }
+  }
+  {
+    // const correctness
+    F f{5};
+
+    std::function_ref<int()> f1(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1() == 10);
+      assert(std::as_const(f1)() == 10);
+    }
+
+    std::function_ref<int() const> f2(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 11);
+      assert(std::as_const(f2)() == 11);
+    }
+  }
+  {
+    // with conversions
+    NeedsConversion c{};
+
+    std::function_ref<Int(int, int, int)> f(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  {
+    // noexcept conversion
+    std::function_ref<int() noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const conversion
+    std::function_ref<int() const> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() noexcept> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to const conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() const> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // P3961R1 Less double indirection in function_ref
+    // double unwrapping
+    std::function_ref<int() const noexcept> f1(std::cw<one>);
+    std::function_ref<int()> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 1 || f2() == 2);
+      LIBCPP_ASSERT(f2() == 1);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
new file mode 100644
index 0000000000000..1cd0c769ce4d3
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -0,0 +1,362 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// R operator()(ArgTypes... args) const noexcept(noex);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "MoveOnly.h"
+
+template <class T, class... Args>
+concept ConstInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) };
+};
+
+template <class T, class... Args>
+concept ConstNoexceptInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) } noexcept;
+};
+
+static_assert(ConstInvocable<std::function_ref<void()>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void()>>);
+
+static_assert(ConstInvocable<std::function_ref<void() noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() noexcept>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void() const>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() const noexcept>>);
+
+struct S {
+  int data = 42;
+  int operator()(int& x) const noexcept { return data + x; }
+  int operator()(const int& x) const noexcept { return data + x + 1; }
+  int operator()(int&& x) const noexcept { return data + x + 2; }
+  int operator()(const int&& x) const noexcept { return data + x + 3; }
+};
+
+struct S2 {
+  double operator()(int x, int y, int z) noexcept { return x + y + z; }
+
+  double operator()(int x, int y, int z) const noexcept { return x + y + z + 1; }
+};
+
+struct Big {
+  char c[128];
+};
+
+struct S3Ref {
+  char operator()(Big& b) const noexcept { return b.c[0]; }
+};
+
+struct S3Value {
+  char operator()(Big b) const noexcept { return b.c[0]; }
+};
+
+void test_default() {
+  {
+    std::function_ref<void()> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&)> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly)> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int)> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&)> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&)> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&)> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&)> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int)> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int)> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&)> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const() {
+  {
+    std::function_ref<void() const> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_noexcept() {
+  {
+    std::function_ref<void() noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const_noexcept() {
+  {
+    std::function_ref<void() const noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test() {
+  test_default();
+  test_const();
+  test_noexcept();
+  test_const_noexcept();
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
new file mode 100644
index 0000000000000..72e26f13868ea
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
+#include <concepts>
+#include <functional>
+#include <type_traits>
+
+struct A {
+  A() {}
+  A(const A&) {}
+  ~A() {}
+};
+
+static_assert(std::copyable<std::function_ref<A(const A&)>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&)>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) noexcept>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const noexcept>>);
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..e54773a0aa2db 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -708,10 +708,9 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_function_ref",
             "values": {
-                "c++26": 202306  # P0792R14 function_ref: a type-erased callable reference
+                "c++26": 202603  # P3948R1 constant_wrapper is the only tool needed for passing constant expressions via function arguments
             },
             "headers": ["functional"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_gcd_lcm",

>From 22228d51d646bfe84f75a105b9949ec78280471e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 25 Apr 2026 14:20:04 +0100
Subject: [PATCH 03/20] review comments

---
 .../__functional/function_ref_common.h        | 22 ++++++-------------
 .../include/__functional/function_ref_impl.h  |  2 +-
 .../internal_call_signature.pass.cpp          |  2 +-
 .../constant_wrapper_ref.pass.cpp             |  8 +++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 12 ++++++++++
 5 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 61979231b56e8..72ded36d65107 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -16,6 +16,7 @@
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
 
@@ -37,21 +38,16 @@ struct __is_convertible_from_specialization : false_type {};
 // pointers
 // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
 // supported
-union __storage_func_ref_t {
+union __function_ref_storage {
   void* __obj_ptr_;
-  void const* __obj_const_ptr_;
   void (*__fn_ptr_)();
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage() noexcept : __obj_ptr_(nullptr) {}
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage(_Tp* __ptr) noexcept {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        __obj_const_ptr_ = __ptr;
-      } else {
-        __obj_ptr_ = __ptr;
-      }
+      __obj_ptr_ = const_cast<remove_cv_t<_Tp>*>(__ptr);
     } else {
       static_assert(is_function_v<_Tp>);
       __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
@@ -59,13 +55,9 @@ union __storage_func_ref_t {
   }
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__function_ref_storage __storage) {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
-      } else {
-        return static_cast<_Tp*>(__storage.__obj_ptr_);
-      }
+      return static_cast<_Tp*>(__storage.__obj_ptr_);
     } else {
       static_assert(is_function_v<_Tp>);
       return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index a4c98d3031602..36841d6745955 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -72,7 +72,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Arg>
   using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
 
-  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+  using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
   using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 05756cfc3b3b8..1e5df6413daf0 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -31,7 +31,7 @@ struct SmallButNonTrivial {
 // specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
 template <>
 class std::function_ref<int> {
-  using storage = std::__storage_func_ref_t;
+  using storage = std::__function_ref_storage;
   void test() {
     {
       // by value small argument
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index 485fa95ca2998..d3d5f6827193d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -362,6 +362,14 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // volatile objects
+    volatile int i = 5;
+    std::function_ref<int() > f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 56d6dd3b7109b..9ab012b3a04e8 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -161,6 +161,10 @@ constexpr auto fn_noexcept = []() noexcept { return 6; };
 const auto one = []() noexcept { return 1; };
 const auto two = []() noexcept { return 2; };
 
+struct VolatileCallable {
+  int operator()() volatile const { return 42; }
+};
+
 constexpr bool test() {
   {
     std::function_ref<void()> f(l1);
@@ -305,6 +309,14 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // volatile objects
+    const volatile VolatileCallable vc{};
+    std::function_ref<int()> f(vc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
 
   return true;
 }

>From 6510436f86baa312ebbe8047c4b02e0ed80237d6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 26 Apr 2026 09:41:10 +0100
Subject: [PATCH 04/20] remove stale comments

---
 libcxx/include/__functional/function_ref_common.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 72ded36d65107..c5adb8cbc7d52 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -34,10 +34,6 @@ class function_ref;
 template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization : false_type {};
 
-// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
-// pointers
-// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
-// supported
 union __function_ref_storage {
   void* __obj_ptr_;
   void (*__fn_ptr_)();

>From 6c97140e1d9bde13b22b988da708aff7d7e12948 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 14:06:05 +0100
Subject: [PATCH 05/20] fix bug

---
 .../__functional/function_ref_common.h        |  2 +-
 .../include/__functional/function_ref_impl.h  | 19 ++++++++++++-------
 .../internal_call_signature.pass.cpp          |  4 +++-
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  6 +++---
 .../func.wrap.ref.ctor/ref.pass.cpp           | 11 +++++++++++
 5 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index c5adb8cbc7d52..60bc6ef87af4f 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -31,7 +31,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <class _Fn1, class _Fn2>
 struct __is_convertible_from_specialization : false_type {};
 
 union __function_ref_storage {
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 36841d6745955..55755e9804c95 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -46,14 +46,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization<
-    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
-    _NoExcept1,
-    _Rp,
-    _ArgTypes...>
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) const noexcept(_NoExcept2)> >
     : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
 
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) noexcept(_NoExcept2)> >
+    : _And<is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)>,
+           is_convertible<_LIBCPP_FUNCTION_REF_CV int&, int&>> {};
+
 template <class _Rp, class... _ArgTypes, bool __is_noexcept>
 class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
 private:
@@ -62,9 +67,9 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
           value;
 
-  template <class _Fn>
+  template <class _Fn2>
   static constexpr bool __is_convertible_from_specialization_v =
-      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+      __is_convertible_from_specialization<function_ref, _Fn2>::value;
 
   template <class... _Tp>
   friend class function_ref;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 1e5df6413daf0..ebdf683791694 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -8,7 +8,9 @@
 
 // REQUIRES: std-at-least-c++26
 
-// Track number of moves
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
 
 #include <cassert>
 #include <functional>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index b4df4392a2ade..cb84b3a183920 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -35,9 +35,9 @@ static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrap
 static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
 
 // const noexcept(false)
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
@@ -61,7 +61,7 @@ static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::con
 // const noexcept(true)
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 9ab012b3a04e8..6b8b7c71f0d08 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -309,6 +309,17 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // P3961R1 Less double indirection in function_ref
+    // is-convertible-from-specialization<remove_cv_t<T>> is false
+    std::function_ref<int()> f1(std::cw<one>);
+    std::function_ref<int() const> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 2);
+    }
+  }
   {
     // volatile objects
     const volatile VolatileCallable vc{};

>From dc53e146aed5417d24104019487bdff14d1b5f42 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 17:28:36 +0100
Subject: [PATCH 06/20] ci

---
 libcxx/include/__functional/function_ref_impl.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 55755e9804c95..454b0ffb4afb3 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -15,6 +15,7 @@
 #include <__functional/invoke.h>
 #include <__memory/addressof.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/conjunction.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>

>From 686f1d5bf8ec1f41672f49e2f4d5f4e0864a7c5e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 10:39:44 +0100
Subject: [PATCH 07/20] static operator

---
 .../include/__functional/function_ref_impl.h  | 23 +++++++++++----
 .../func.wrap.ref.ctor/ref.pass.cpp           | 28 +++++++++++++++++++
 2 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 454b0ffb4afb3..ab65382782fb2 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
@@ -99,12 +100,22 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
-      : __storage_(std::addressof(__obj)),
-        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
-        }) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
+    using _Dn = remove_cv_t<_Tp>;
+    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
+                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+                  }) {
+      __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    } else {
+      __storage_ = __storage_t(std::addressof(__obj)),
+      __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    }
+  }
 
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 6b8b7c71f0d08..2684ec3548f8e 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -328,6 +328,34 @@ constexpr bool test() {
       assert(f() == 42);
     }
   }
+  {
+    // static call operator
+    struct StaticCall {
+      static int operator()(int x, int y) { return x + y; }
+    };
+    StaticCall sc;
+    std::function_ref<int(int, int)> f(sc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2) == 3);
+    }
+  }
+  {
+    // overload set with static and non-static call operator
+    struct OverloadSet {
+      static int operator()(int x) { return x + 1; }
+      int operator()(long x) const { return x + 2; }
+    };
+    OverloadSet os;
+    std::function_ref<int(int)> f1(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1(1) == 2);
+    }
+
+    std::function_ref<int(long)> f2(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1L) == 3);
+    }
+  }
 
   return true;
 }

>From e81960666dfc9d0042f9ca1242dabbbb123c1af2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 19:26:38 +0100
Subject: [PATCH 08/20] work around clang 22 bug

---
 libcxx/include/__functional/function_ref_common.h             | 4 ++++
 libcxx/include/__functional/function_ref_impl.h               | 4 +---
 .../func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp   | 1 +
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 60bc6ef87af4f..bfe3d938056f0 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -19,6 +19,7 @@
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
+#include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -107,6 +108,9 @@ struct __function_ref_arg_fwd<_Arg> {
   using type _LIBCPP_NODEBUG = _Arg;
 };
 
+template <class _Fn, class... _Args>
+concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
+
 #endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index ab65382782fb2..7a173dee7fa4f 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -102,9 +102,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
     using _Dn = remove_cv_t<_Tp>;
-    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
-                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
-                  }) {
+    if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
       };
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 2684ec3548f8e..11b993e4a3ec3 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -351,6 +351,7 @@ constexpr bool test() {
       assert(f1(1) == 2);
     }
 
+    LIBCPP_STATIC_ASSERT(!std::__statically_callable<OverloadSet, long>);
     std::function_ref<int(long)> f2(os);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f2(1L) == 3);

>From 542dbfc683dd21f950c619e74fb0bf884084bfdc Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:12:34 +0100
Subject: [PATCH 09/20] review comments

---
 libcxx/include/__functional/function_ref_common.h | 15 +++++++--------
 libcxx/include/__functional/function_ref_impl.h   |  6 +++---
 .../constant_arg.mandates.verify.cpp              |  2 +-
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index bfe3d938056f0..0f40a07073cfc 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -15,7 +15,8 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
-#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
@@ -98,15 +99,13 @@ template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
 template <class _Arg>
-struct __function_ref_arg_fwd {
-  using type _LIBCPP_NODEBUG = _Arg&&;
-};
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
 
 template <class _Arg>
-  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
-struct __function_ref_arg_fwd<_Arg> {
-  using type _LIBCPP_NODEBUG = _Arg;
-};
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
 
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a173dee7fa4f..3424a99bdd174 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -77,7 +77,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   friend class function_ref;
 
   template <class _Arg>
-  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+  using __arg_t _LIBCPP_NODEBUG = _If<__register_passable<_Arg>, _Arg, _Arg&&>;
 
   using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
@@ -135,8 +135,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
           !requires {
             typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
           },
-          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
-          "and produces a result with a structural type");
+          "cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref "
+          "constructed from cw would be ambiguous");
     }
   }
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 519957eec6878..63cfbc0e97c9b 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -41,7 +41,7 @@ void test() {
   // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
   std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}

>From 1c8f903140fd887f71947392e8a047b68f987359 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:13:48 +0100
Subject: [PATCH 10/20] use std::forward instead of static_cast

---
 libcxx/include/__functional/function_ref_impl.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 3424a99bdd174..d276c78fd09e6 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -92,7 +92,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -104,13 +104,13 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     using _Dn = remove_cv_t<_Tp>;
     if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return _Dn::operator()(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     } else {
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -125,7 +125,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     requires __is_invocable_using<const _Fn&>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
       : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -146,7 +146,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -160,7 +160,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(__obj_ptr_),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");

>From cffaf541e6877199313c646775d0acbec94e1035 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:14:02 +0100
Subject: [PATCH 11/20] format

---
 .../func.wrap.ref.ctor/constant_arg.mandates.verify.cpp      | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 63cfbc0e97c9b..c84cca2793bac 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -17,8 +17,9 @@
 
 // Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
 
-// For the first overload,
-// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+// For the first overload, f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>...
+// satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)>
+// is not a valid type.
 
 #include <functional>
 #include <utility>

>From f9909bf2f9aa919760098f84f623914296cca960 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 20 Jun 2026 16:28:00 +0100
Subject: [PATCH 12/20] review commnets

---
 libcxx/include/CMakeLists.txt                 |   1 +
 .../__functional/function_ref_common.h        |   9 -
 .../include/__functional/function_ref_impl.h  |   8 +-
 .../include/__type_traits/register_passable.h |  38 ++++
 libcxx/include/module.modulemap.in            |   1 +
 .../constant_arg.mandates.verify.cpp          |  26 +--
 .../internal_call_signature.pass.cpp          | 138 ---------------
 .../internal_call_signature.sh.cpp            |  68 ++++++++
 .../views.span/span.cons/deduct.pass.cpp      |   7 -
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     |   9 +
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  24 ++-
 .../constant_wrapper_ptr.pass.cpp             |  60 ++++++-
 .../constant_wrapper_ref.pass.cpp             |  59 ++++++-
 .../func.wrap.ref.ctor/copy.pass.cpp          |   5 +
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   |  71 ++++++--
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  |   6 -
 .../func.wrap.ref.ctor/move.pass.cpp          |  70 +-------
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 102 ++---------
 .../func.wrap.ref.inv/invoke.pass.cpp         | 162 ++++++++++++++++++
 19 files changed, 506 insertions(+), 358 deletions(-)
 create mode 100644 libcxx/include/__type_traits/register_passable.h
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index a0d5cf70d267d..fcea3b7601182 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -914,6 +914,7 @@ set(files
   __type_traits/rank.h
   __type_traits/reference_constructs_from_temporary.h
   __type_traits/reference_converts_from_temporary.h
+  __type_traits/register_passable.h
   __type_traits/remove_all_extents.h
   __type_traits/remove_const.h
   __type_traits/remove_const_ref.h
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 0f40a07073cfc..e6fa031432a91 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -98,15 +98,6 @@ constexpr bool __is_constant_wrapper = false;
 template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
-template <class _Arg>
-concept __itanium_trivial_for_calls =
-    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
-    is_trivially_move_constructible_v<_Arg>;
-
-template <class _Arg>
-concept __register_passable =
-    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
-
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
 
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index d276c78fd09e6..7a968fc64de70 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/register_passable.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
@@ -92,7 +93,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
+          _Fp* __fn_ptr1 = __storage_t::template __get<_Fp>(__storage);
+          return std::invoke_r<_Rp>(__fn_ptr1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -110,7 +112,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
+        return std::invoke_r<_Rp>(__obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -145,7 +147,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          auto& __obj1 = static_cast<_LIBCPP_FUNCTION_REF_CV _Tp&>(*__storage_t::template __get<_Tp>(__storage));
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
diff --git a/libcxx/include/__type_traits/register_passable.h b/libcxx/include/__type_traits/register_passable.h
new file mode 100644
index 0000000000000..21e23e62052c1
--- /dev/null
+++ b/libcxx/include/__type_traits/register_passable.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___TYPE_TRAITS_REGISTER_PASSABLE_H
+#define _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
+
+#include <__config>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Arg>
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
+
+template <class _Arg>
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
+
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index cb29fac536046..e3b9e5ac09cb3 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -351,6 +351,7 @@ module std_core {
       header "__type_traits/reference_converts_from_temporary.h"
       export std_core.type_traits.integral_constant
     }
+    module register_passable                          { header "__type_traits/register_passable.h" }
     module remove_all_extents                         { header "__type_traits/remove_all_extents.h" }
     module remove_const_ref                           { header "__type_traits/remove_const_ref.h" }
     module remove_const                               { header "__type_traits/remove_const.h" }
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index c84cca2793bac..08e8b8e9b8b52 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -35,27 +35,27 @@ struct B {
 
 // clang-format off
 void test() {
-  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
+  // expected-error-re@*:* {{static assertion failed {{.*}}cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
-  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   A a;
-  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 }
 // clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
deleted file mode 100644
index ebdf683791694..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-struct Small {};
-
-struct Big {
-  char c[128];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
-// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
-template <>
-class std::function_ref<int> {
-  using storage = std::__function_ref_storage;
-  void test() {
-    {
-      // by value small argument
-      // the internal function should pass by value
-      using call = std::function_ref<void(Small)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
-    }
-    {
-      // by value big argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Big)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
-    }
-    {
-      // by value non-trivial small argument
-      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
-      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
-    }
-    {
-      // by lvalue reference argument
-      // the internal function should pass by lvalue reference
-      using call = std::function_ref<void(Small&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
-    }
-    {
-      // by rvalue reference argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Small&&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
-    }
-  }
-};
-
-struct TrackCopyMove {
-  mutable int copy_count = 0;
-  int move_count         = 0;
-
-  TrackCopyMove() = default;
-  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
-    ++copy_count;
-    ++other.copy_count;
-  }
-
-  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
-    ++move_count;
-    ++other.move_count;
-  }
-  TrackCopyMove& operator=(const TrackCopyMove& other) {
-    ++copy_count;
-    ++other.copy_count;
-    return *this;
-  }
-  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
-    ++move_count;
-    ++other.move_count;
-    return *this;
-  }
-};
-
-void test() {
-  {
-    // Arg type is an lvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&)> f = lambda;
-    f(t);
-  }
-  {
-    // Arg type is an rvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&&)> f = lambda;
-    f(std::move(t));
-  }
-  {
-    // Arg type is a prvalue, we should move but not copy the object
-    // In this case, where the type is not trivially copyable, the object should be
-    // moved exactly once when passing into the lambda. The internal functions
-    // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) {
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 1);
-    };
-    std::function_ref<void(TrackCopyMove)> f = lambda;
-    f(TrackCopyMove{});
-  }
-}
-
-int main(int, char**) {
-  test();
-
-  return 0;
-}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
new file mode 100644
index 0000000000000..a32a1083b5596
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// REQUIRES: has-filecheck
+
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
+
+struct Small {
+  char c[8];
+};
+
+struct Big {
+  char c[256];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// CHECK: define{{.*}} @_Z10test_small
+// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
+// CHECK: %3 = alloca %struct.Small, align 1
+// CHECK: %6 = alloca %struct.Small, align 1
+// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
+// CHECK: store i64 %1, ptr %7, align 1
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
+// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
+// CHECK: %15 = load i64, ptr %14, align 1
+// CHECK: call void %10(ptr %13, i64 %15)
+//
+// The above has two local copies of Small object. It loads %1 to local copy %3, and then
+// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
+void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
+
+// CHECK: define{{.*}} @_Z8test_big
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
+//
+// The above directly calls the function pointer with the parameter Big object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the Big object by reference
+void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
+
+// CHECK: define{{.*}} @_Z22test_small_non_trivial
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
+//
+// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the SmallButNonTrivial object by reference
+void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index c9e17b3aec49a..9e41afad2258e 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -73,13 +73,6 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
-
-  {
-    std::span s{std::begin(arr), std::cw<3>};
-    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
-    assert(s.size() == std::size(arr));
-    assert(s.data() == std::data(arr));
-  }
 #endif
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
index 029cb19d54f6a..730a07202062d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -8,6 +8,15 @@
 
 // REQUIRES: std-at-least-c++26
 
+// template<class F>
+// function_ref(F*) -> function_ref<F>;
+//
+// template<auto c, class F0>
+// function_ref(constant_wrapper<c, F0>) -> function_ref<see below>;
+//
+// template<auto c, class F, class T>
+// function_ref(constant_wrapper<c, F>, T&&) -> function_ref<see below>;
+
 #include <cassert>
 #include <functional>
 #include <utility>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index cb84b3a183920..5866488be4a94 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -11,6 +11,7 @@
 // template<class T> function_ref& operator=(T) = delete;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -73,38 +74,49 @@ static_assert(
 
 int forty_two() { return 42; }
 
+// These runtime tests are testing that when the constraints are not met,
+// the assignment operator is not deleted and the implicit constructor
+// and the move assignment are called
 void test() {
   {
     std::function_ref<int()> f(std::cw<[] { return 41; }>);
-    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f =
+        std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = &forty_two;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = &forty_two;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // noexcept
     std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() noexcept>&> decltype(auto) result = f = std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const noexcept
     std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() const noexcept>&> decltype(auto) result = f =
+        std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
index dd6e10e8a3289..74f52063b6788 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -94,6 +94,7 @@ static_assert(
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
 static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, const int*>);
 
 static_assert(
     !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
@@ -180,6 +181,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int*) const noexcept { return 4; }
+  constexpr int operator()(const int*) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -231,6 +237,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -248,6 +262,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -263,39 +285,51 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -348,6 +382,20 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, &i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, &i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, &i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index d3d5f6827193d..979da32e60c3d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -199,6 +199,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int&) const noexcept { return 4; }
+  constexpr int operator()(const int&) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -248,6 +253,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -264,6 +277,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -278,37 +299,49 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -370,6 +403,20 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
index 61b70a559d786..7f837149cc6b5 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -22,6 +22,11 @@ static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
 double f1(int x, double y) noexcept { return x + y; }
 
 struct Int {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
index 1a8b26f86c8be..53c79b4097362 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -11,6 +11,7 @@
 // constexpr function_ref& operator=(const function_ref&) noexcept = default;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -22,6 +23,11 @@ static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
@@ -41,7 +47,8 @@ constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = f;
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -51,7 +58,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = f;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
@@ -62,7 +70,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -72,7 +81,8 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -82,32 +92,47 @@ constexpr bool test() {
     // with conversions
     std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
         std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
@@ -117,31 +142,47 @@ constexpr bool test() {
     // with conversions function pointer
     std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
     std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions function pointer
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index f142b81367ef1..aa0ccf8d67993 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -134,12 +134,6 @@ void test() {
 
     std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
     assert(f4(1, 2, 3).i == 6);
-
-    {
-      std::function_ref r1 = foo;
-      std::function_ref r2 = bar;
-      r1                   = r2; // ok
-    }
   }
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
index 0f27bba821391..025df83ad538d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -8,6 +8,8 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
 #include <functional>
 #include <utility>
@@ -20,18 +22,12 @@ static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-double f1(int x, double y) noexcept { return x + y; }
-
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+double f1(int x, double y) noexcept { return x + y; }
 
 constexpr bool test() {
   {
@@ -66,58 +62,6 @@ constexpr bool test() {
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
index b40dfa9f2cf4b..10620f5d48725 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -8,7 +8,10 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -20,26 +23,20 @@ static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
-
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
-int zero(Int, Int, Int) noexcept { return 0; }
-
 constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -49,8 +46,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = std::move(f);
-
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
       assert(f2() == 42);
@@ -60,7 +57,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -70,81 +68,13 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
-        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 1cd0c769ce4d3..0dced07c439d6 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -67,6 +67,32 @@ struct S3Value {
   char operator()(Big b) const noexcept { return b.c[0]; }
 };
 
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
 void test_default() {
   {
     std::function_ref<void()> f = [] {};
@@ -135,6 +161,40 @@ void test_default() {
     std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const() {
@@ -205,6 +265,40 @@ void test_const() {
     std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_noexcept() {
@@ -276,6 +370,40 @@ void test_noexcept() {
     std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const_noexcept() {
@@ -347,6 +475,40 @@ void test_const_noexcept() {
     std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test() {

>From 535af2a3d49cc07ab47aab3efbb4f7967182f661 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:38:35 +0100
Subject: [PATCH 13/20] format

---
 .../func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp       | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 0dced07c439d6..820fed5fc76bc 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -384,7 +384,7 @@ void test_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -397,7 +397,7 @@ void test_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };
@@ -489,7 +489,7 @@ void test_const_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -502,7 +502,7 @@ void test_const_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };

>From fbdc3c1843229385e121162ea20a976bda3e9ff6 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:31 +0100
Subject: [PATCH 14/20] Update libcxx/docs/Status/Cxx26Issues.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 900ebb4f2aa8a..95aa2bff5d9ea 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","24","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""

>From 901f83e46c2c259dfde4b3c76430c9b7166f050a Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:40 +0100
Subject: [PATCH 15/20] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index c83f46c288d7e..45aeba43eeaeb 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -192,7 +192,7 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
 "`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""

>From 8dd462c97bbdcb75caaa9a49534e3964bb85b12d Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:50 +0100
Subject: [PATCH 16/20] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 45aeba43eeaeb..7bc6aa302f1c4 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","24","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."

>From c20ebbf3afe9c1ddb4484e10ff0f5825443c4bec Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:15:01 +0100
Subject: [PATCH 17/20] Update libcxx/docs/Status/Cxx26Issues.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 95aa2bff5d9ea..06ceef17b8c70 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","24","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""

>From 6fee0703e9a0c3d5919b8073d403d12df96c2bdb Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:15:11 +0100
Subject: [PATCH 18/20] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7bc6aa302f1c4..7efbe312f3e82 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -194,7 +194,7 @@
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
 "`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","24","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""

>From f592125ab8612d6fa3e96dd1a477305a3de3ee0e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:32:58 +0100
Subject: [PATCH 19/20] review

---
 libcxx/docs/ReleaseNotes/23.rst               |  3 -
 libcxx/docs/ReleaseNotes/24.rst               | 61 +++++++++++++++++
 .../internal_call_signature.sh.cpp            | 68 -------------------
 3 files changed, 61 insertions(+), 71 deletions(-)
 create mode 100644 libcxx/docs/ReleaseNotes/24.rst
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index ecf3596ac66c4..1213e8f43c8cd 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,9 +55,6 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
-- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
-- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
-- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
new file mode 100644
index 0000000000000..7a8c1e589b1df
--- /dev/null
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -0,0 +1,61 @@
+===========================================
+Libc++ 23.0.0 (In-Progress) Release Notes
+===========================================
+
+.. contents::
+   :local:
+   :depth: 2
+
+Written by the `Libc++ Team <https://libcxx.llvm.org>`_
+
+.. warning::
+
+   These are in-progress notes for the upcoming libc++ 24.0.0 release.
+   Release notes for previous releases can be found on
+   `the Download Page <https://releases.llvm.org/download.html>`_.
+
+Introduction
+============
+
+This document contains the release notes for the libc++ C++ Standard Library,
+part of the LLVM Compiler Infrastructure, release 24.0.0. Here we describe the
+status of libc++ in some detail, including major improvements from the previous
+release and new feature work. For the general LLVM release notes, see `the LLVM
+documentation <https://llvm.org/docs/ReleaseNotes.html>`_. All LLVM releases may
+be downloaded from the `LLVM releases web site <https://llvm.org/releases/>`_.
+
+For more information about libc++, please see the `Libc++ Web Site
+<https://libcxx.llvm.org>`_ or the `LLVM Web Site <https://llvm.org>`_.
+
+Note that if you are reading this file from a Git checkout or the
+main Libc++ web page, this document applies to the *next* release, not
+the current one. To see the release notes for a specific release, please
+see the `releases page <https://llvm.org/releases/>`_.
+
+What's New in Libc++ 24.0.0?
+==============================
+
+Implemented Papers
+------------------
+
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
+
+Improvements and New Features
+-----------------------------
+
+Deprecations and Removals
+-------------------------
+
+Potentially breaking changes
+----------------------------
+
+Announcements About Future Releases
+-----------------------------------
+
+ABI Affecting Changes
+---------------------
+
+Build System Changes
+--------------------
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
deleted file mode 100644
index a32a1083b5596..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// REQUIRES: has-filecheck
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
-
-struct Small {
-  char c[8];
-};
-
-struct Big {
-  char c[256];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// CHECK: define{{.*}} @_Z10test_small
-// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
-// CHECK: %3 = alloca %struct.Small, align 1
-// CHECK: %6 = alloca %struct.Small, align 1
-// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
-// CHECK: store i64 %1, ptr %7, align 1
-// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
-// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
-// CHECK: %15 = load i64, ptr %14, align 1
-// CHECK: call void %10(ptr %13, i64 %15)
-//
-// The above has two local copies of Small object. It loads %1 to local copy %3, and then
-// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
-void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
-
-// CHECK: define{{.*}} @_Z8test_big
-// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
-//
-// The above directly calls the function pointer with the parameter Big object pointer
-// points to the operator()'s parameter %1, thus, no extra copies/moves
-// So we internally pass the Big object by reference
-void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
-
-// CHECK: define{{.*}} @_Z22test_small_non_trivial
-// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
-//
-// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
-// points to the operator()'s parameter %1, thus, no extra copies/moves
-// So we internally pass the SmallButNonTrivial object by reference
-void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file

>From 37cdeb6d97c3e6fddaaf47b8f1636c4ac559bb82 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:14:44 +0100
Subject: [PATCH 20/20] CI

---
 libcxx/include/__functional/function_ref_impl.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a968fc64de70..ada14643ec496 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -158,8 +158,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <auto _Cw, class _Fn, class _Tp>
     requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
-                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
-      : __storage_(__obj_ptr_),
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+      : __storage_(__obj_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
@@ -169,7 +169,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     }
 
     if constexpr (is_member_pointer_v<_Fn>) {
-      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
     }
   }
 


From libcxx-commits at lists.llvm.org  Fri Jul 10 00:49:52 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 00:49:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] Fix redefinition of wchar_t
 overloads on AIX (PR #201432)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201432@github.com>
Message-ID: <6a50a420.eb165e5c.1817a5.9438@mx.google.com>


https://github.com/philnik777 requested changes to this pull request.

This header already has a guard. Why would a second one be required?

https://github.com/llvm/llvm-project/pull/201432

From libcxx-commits at lists.llvm.org  Fri Jul 10 00:50:57 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 00:50:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Rename the macro for the LLVM 18
 ABI tag used in ranges (PR #208464)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208464@github.com>
Message-ID: <6a50a461.c56fb102.7c9cd.1523@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/208464

From libcxx-commits at lists.llvm.org  Fri Jul 10 00:51:38 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 00:51:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a50a48a.d76d81af.39f9be.bbd7@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207384

>From 9c6f7a1b75377c5497f54b202cff588e6bc98bde Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 13:34:50 +0200
Subject: [PATCH] [libc++] Optimize vector::__move_range for trivially copyable
 types

This is a code size rather than a performance optimization.

For example, the number of instructions in `__insert_with_iter` is
almost cut in half.
---
 libcxx/include/__vector/vector.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 422c98c300a1d..a226e5dc621c4 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -47,6 +47,7 @@
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__split_buffer>
+#include <__string/constexpr_c_functions.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_allocator.h>
@@ -57,6 +58,7 @@
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
+#include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/declval.h>
@@ -1120,6 +1122,18 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointer __to) {
   pointer __old_last  = __layout_.__end_ptr();
   difference_type __n = __old_last - __to;
+#ifndef _LIBCPP_CXX03_LANG
+  if constexpr (__allocator_has_trivial_move_construct_v<allocator_type, value_type> &&
+                is_trivially_copyable<value_type>::value) {
+    if (!__libcpp_is_constant_evaluated()) {
+      auto __count = __from_e - __from_s;
+      __annotate_increase(__count - __n);
+      std::__constexpr_memmove(std::__to_address(__to), std::__to_address(__from_s), __element_count(__count));
+      __layout_.__set_bound_using_pointer(__to + __count);
+      return;
+    }
+  }
+#endif
   {
     pointer __i = __from_s + __n;
     _ConstructTransaction __tx(*this, __from_e - __i);


From libcxx-commits at lists.llvm.org  Fri Jul 10 00:52:39 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 00:52:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a50a4c7.a87eb5dc.7d817.4335@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From d93e557c304cea49a5cf654b3bf1710f5cee6855 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/18] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..5e1a7afea06a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From f17e51a372a7a1372fa63a572871939eec329145 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/18] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From ac972d414dd906c0e73a3e8b70697abfc537dafa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/18] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e1a7afea06a2..4477c821235de 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..b0f40e5a261ed 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -896,6 +896,7 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 33732fee8003a503f62524e1208aef02094b0bc6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/18] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From dc33657dd85cca1c1511d5cd7605bbcca05b35f8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/18] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From f133b64bf0da078d6ef4659603f8f92f12e7ec8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/18] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From e943f4968a86c2c73214cdb22ccdf77b122428e5 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/18] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 6070b97b3d62691fabebe6dbd7b79a3fd5a58c4d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/18] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 56cbfdaa5c0ff..8ccbcf9d8d227 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -160,7 +160,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -178,7 +178,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -193,7 +193,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b20b5e25fc9cd..04e12afd21689 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From d85c6a4fd888729fc38b7b3bae192263875cc4ed Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/18] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 4477c821235de..0e214d48c5672 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 8ccbcf9d8d227..04bdd2a6b343f 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -28,29 +28,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 04e12afd21689..3776873d8936d 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b0f40e5a261ed..a15ec1342a56d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -885,6 +885,7 @@ module std [system] {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -896,7 +897,6 @@ module std [system] {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From af8e3868ec116fc226f4d5b1c3402d99dade4505 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/18] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 25dccbcfcce342be3de56230774f7790d0c2f5c2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/18] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 3776873d8936d..fbe90c05ccaeb 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From cf4a2421de4a7c9d7fd6729329022602cfa07ff7 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/18] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From 8c1bda248bc075b4614c605761b4ef34519b10e8 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/18] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From 430ca438d1f650e1b9ce026b5ef4408801e42945 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/18] 14

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <cstring>
+#include <type_traits>
 
 struct WithTailPadding {
   int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From ed3d2f3ecaf185a6cf71d7c1297aa7aae8230a07 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 20:42:14 +0100
Subject: [PATCH 15/18] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e4d83e638ca77..0bafdbb1d7ede 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -136,7 +136,7 @@ void test() {
   {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
-
+    std::atomic<T> a;
     T init = make<T>(10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);

>From ca3547d2bfb006e79cff08d009439036f02e1814 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:13:05 +0100
Subject: [PATCH 16/18] ci

---
 libcxx/include/__atomic/support/gcc.h         |  5 ++--
 .../atomics.types.generic/padding.pass.cpp    | 24 ++++++++++-------
 .../atomics.types.generic/padding.pass.cpp    | 27 ++++++++++---------
 .../atomics.types.generic/padding2.pass.cpp   |  3 ---
 4 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index fbe90c05ccaeb..375c7721965b8 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -50,9 +50,8 @@ struct __cxx_atomic_base_impl {
     std::__clear_padding_if_needed(__a_value);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
   _Tp __a_value;
 };
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index bf594b45ed54f..6f894fe6cb605 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -44,15 +44,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -61,11 +69,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0bafdbb1d7ede..344d08b493e60 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,9 +7,6 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits
@@ -41,15 +38,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -58,11 +63,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
index 4e17471a2461f..1040f342b8206 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -8,9 +8,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-localization
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits

>From cc23cf201664aa5a7dbf6f415dfb31ba58f02f09 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:29:04 +0100
Subject: [PATCH 17/18] CI

---
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding2.pass.cpp   | 273 ------------------
 3 files changed, 2 insertions(+), 275 deletions(-)
 delete mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 6f894fe6cb605..388ab0867fe38 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 344d08b493e60..f1a534a2916d6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
deleted file mode 100644
index 1040f342b8206..0000000000000
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-localization
-
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
-// CAS should work on types with padding bits
-
-#include <atomic>
-#include <cassert>
-#include <cstring>
-#include <type_traits>
-#include <iostream>
-
-struct Foo {
-  int i;
-  char c;
-};
-
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
-
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
-}
-
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
-}
-
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-#ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
-#else
-  (void)f;
-  (void)pad_byte;
-#endif
-}
-
-void test() {
-  std::cerr << "test 1" << std::endl;
-  {
-    // compare_exchange_strong
-    // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
-    std::cerr << "test 2" << std::endl;
-
-    Foo init = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 3" << std::endl;
-    assert_foo_padding(init, 0xBB);
-    std::cerr << "test 4" << std::endl;
-    a.store(init);
-
-    Foo expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 5" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 6" << std::endl;
-
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 7" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 8" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 9" << std::endl;
-    assert(r);
-    std::cerr << "test 10" << std::endl;
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    std::cerr << "test 11" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 42);
-    std::cerr << "test 12" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 13" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 14" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 15" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 16" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 17" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 18" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 19" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 10" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 21" << std::endl;
-    assert(!r);
-    std::cerr << "test 22" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 23" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 24" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 25" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 10);
-    std::cerr << "test 26" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 27" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 28" << std::endl;
-  }
-
-  {
-    // compare_exchange_weak
-    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 29" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 30" << std::endl;
-    a.store(stored);
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 31" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 32" << std::endl;
-
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 33" << std::endl;
-    assert_foo_padding(original_expected, 0xAA);
-    std::cerr << "test 34" << std::endl;
-
-    bool r                  = false;
-    const auto max_attempts = 100;
-    auto current_attempt    = 0;
-    while (!r) {
-      ++current_attempt;
-      std::cerr << "test 35" << std::endl;
-      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      std::cerr << "test 36" << std::endl;
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
-      std::cerr << "test 37" << std::endl;
-      r = a.compare_exchange_weak(expected, new_value);
-      if (r) {
-        std::cerr << "test 38" << std::endl;
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-        std::cerr << "test 39" << std::endl;
-      } else {
-        // Spurious failure: expected is updated to the current atomic value.
-        std::cerr << "test 40" << std::endl;
-        assert(expected.i == 10);
-        std::cerr << "test 41" << std::endl;
-        assert(expected.c == 'a');
-        std::cerr << "test 45" << std::endl;
-        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
-        std::cerr << "test 43" << std::endl;
-      }
-    }
-
-    Foo loaded = a.load();
-    std::cerr << "test 44" << std::endl;
-    assert(loaded.i == 42);
-    std::cerr << "test 45" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 46" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 47" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 48" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 49" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 50" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 51" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 52" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 53" << std::endl;
-
-    bool r = a.compare_exchange_weak(expected, new_value);
-
-    std::cerr << "test 54" << std::endl;
-    assert(!r);
-    std::cerr << "test 55" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 56" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 57" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 58" << std::endl;
-    Foo loaded = a.load();
-    std::cerr << "test 59" << std::endl;
-    assert(loaded.i == 10);
-    std::cerr << "test 60" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 61" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 62" << std::endl;
-  }
-
-  {
-    // Types with unique object representations skip the padding-clearing path.
-    std::atomic<int> a(1);
-    int expected = 1;
-    std::cerr << "test 63" << std::endl;
-    assert(a.compare_exchange_strong(expected, 2));
-    std::cerr << "test 64" << std::endl;
-    assert(expected == 1);
-    std::cerr << "test 65" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 66" << std::endl;
-
-    expected = 3;
-    assert(!a.compare_exchange_strong(expected, 4));
-    std::cerr << "test 67" << std::endl;
-    assert(expected == 2);
-    std::cerr << "test 68" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 69" << std::endl;
-  }
-}
-
-int main(int, char**) {
-  // TODO(LLVM-23): Switch to XFAIL: clang-22
-  std::cerr << "main 1" << std::endl;
-#if __has_builtin(__builtin_clear_padding)
-  std::cerr << "main 2" << std::endl;
-  test();
-  std::cerr << "main 3" << std::endl;
-#endif
-  std::cerr << "main 4" << std::endl;
-  return 0;
-}

>From dd0cf5335b559375e69010ef0332c4e7c1ffd94f Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:52:15 +0100
Subject: [PATCH 18/18] ci

---
 libcxx/include/__atomic/support/c11.h | 13 ++++++++-----
 libcxx/include/__atomic/support/gcc.h |  9 +++++++--
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 04bdd2a6b343f..2d4cc9fd9aae0 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -28,13 +28,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
 
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 375c7721965b8..ebc5496c530c3 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,12 +46,17 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
       : __a_value(std::__clear_padding_if_needed(__value)) {}
+
   _Tp __a_value;
 };
 


From libcxx-commits at lists.llvm.org  Fri Jul 10 00:54:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 00:54:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a50a54b.3cce8983.225732.4326@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/186692

>From 816aec4f1b5369071ad16b3f64a5e1563f75bec6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 12 Apr 2026 09:35:44 +0100
Subject: [PATCH 01/21] [libcxx] Implement `std::constant_wrapper`

ci

ci

review comments

review comments

ci

review comments
---
 .../containers/views/views.span/span.cons/deduct.pass.cpp  | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index 9e41afad2258e..c9e17b3aec49a 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -73,6 +73,13 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
+
+  {
+    std::span s{std::begin(arr), std::cw<3>};
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+    assert(s.size() == std::size(arr));
+    assert(s.data() == std::data(arr));
+  }
 #endif
 }
 

>From 049c297c0e043ebb00e74cdb627a0934a0a7e46b Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 8 Jun 2024 17:32:42 -0700
Subject: [PATCH 02/21] [libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

rebase + put in experimental + rename nontype to constant_arg

tests

remove experimental

test for constant_arg_ref

test for constant_arg_ptr

remove fun_ptr member

invoke

format

constexpr function ptr

double unwrapping

P3948R1

tests for new papers

format

lwg issue

lwg issues

review comment

remove constexpr

optimise small types

ci

ci

ci

ci
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/23.rst               |   3 +
 libcxx/docs/Status/Cxx26Issues.csv            |   4 +-
 libcxx/docs/Status/Cxx26Papers.csv            |   6 +-
 libcxx/include/CMakeLists.txt                 |   3 +
 libcxx/include/__functional/function_ref.h    |  33 ++
 .../__functional/function_ref_common.h        | 126 ++++++
 .../include/__functional/function_ref_impl.h  | 184 +++++++++
 libcxx/include/functional                     |   9 +
 libcxx/include/module.modulemap.in            |   3 +
 libcxx/include/version                        |   4 +-
 libcxx/modules/std/functional.inc             |   4 +
 .../assert.constant_arg_ptr.pass.cpp          |  31 ++
 .../assert.function_ptr.pass.cpp              |  26 ++
 .../constant_arg.mandates.verify.cpp          |  60 +++
 .../internal_call_signature.pass.cpp          | 136 +++++++
 .../functional.version.compile.pass.cpp       |  16 +-
 .../version.version.compile.pass.cpp          |  16 +-
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     | 107 +++++
 .../func.wrap.ref.ctor/assign.delete.pass.cpp | 115 ++++++
 .../constant_wrapper.pass.cpp                 | 175 ++++++++
 .../constant_wrapper_ptr.pass.cpp             | 359 +++++++++++++++++
 .../constant_wrapper_ref.pass.cpp             | 373 ++++++++++++++++++
 .../func.wrap.ref.ctor/copy.pass.cpp          | 131 ++++++
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   | 158 ++++++++
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  | 149 +++++++
 .../func.wrap.ref.ctor/move.pass.cpp          | 129 ++++++
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 156 ++++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 316 +++++++++++++++
 .../func.wrap.ref.inv/invoke.pass.cpp         | 362 +++++++++++++++++
 .../trivially_copyable.compile.pass.cpp       |  33 ++
 .../generate_feature_test_macro_components.py |   3 +-
 32 files changed, 3200 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/include/__functional/function_ref.h
 create mode 100644 libcxx/include/__functional/function_ref_common.h
 create mode 100644 libcxx/include/__functional/function_ref_impl.h
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..cd360ae2b1317 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -480,7 +480,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_fstream_native_handle``                        ``202306L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_function_ref``                                 *unimplemented*
+    ``__cpp_lib_function_ref``                                 ``202603L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_generate_random``                              *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..ecf3596ac66c4 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,9 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index e8edd9eefe1b1..900ebb4f2aa8a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","","","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","","","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..c83f46c288d7e 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."
@@ -192,9 +192,9 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","","","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","","","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..a0d5cf70d267d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -438,6 +438,9 @@ set(files
   __functional/compose.h
   __functional/default_searcher.h
   __functional/function.h
+  __functional/function_ref.h
+  __functional/function_ref_common.h
+  __functional/function_ref_impl.h
   __functional/hash.h
   __functional/identity.h
   __functional/invoke.h
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
new file mode 100644
index 0000000000000..b7fd3a4a2c79f
--- /dev/null
+++ b/libcxx/include/__functional/function_ref.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+
+#include <__config>
+#include <__functional/function_ref_common.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26
+// NOLINTBEGIN(readability-duplicate-include)
+
+#  define _LIBCPP_FUNCTION_REF_CV
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+#  define _LIBCPP_FUNCTION_REF_CV const
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+// NOLINTEND(readability-duplicate-include)
+#endif // _LIBCPP_STD_VER >= 26
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
new file mode 100644
index 0000000000000..61979231b56e8
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_COMMON_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+
+#include <__config>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/constant_wrapper.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization : false_type {};
+
+// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+// pointers
+// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
+// supported
+union __storage_func_ref_t {
+  void* __obj_ptr_;
+  void const* __obj_const_ptr_;
+  void (*__fn_ptr_)();
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        __obj_const_ptr_ = __ptr;
+      } else {
+        __obj_ptr_ = __ptr;
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
+    }
+  }
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
+      } else {
+        return static_cast<_Tp*>(__storage.__obj_ptr_);
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
+    }
+  }
+};
+
+template <class _Fp, class _Tp>
+struct __function_ref_bind {};
+
+// F is of the form R(*)(G, A...) noexcept(E) for a type G.
+template <bool _Noexcept, class _Tp, class _Rp, class _Gp, class... _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept(_Noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_Noexcept);
+};
+
+template <class _Tp, class _Mp, class _Gp>
+  requires is_object_v<_Mp>
+struct __function_ref_bind<_Mp _Gp::*, _Tp> {
+  using type _LIBCPP_NODEBUG = invoke_result_t<_Mp _Gp::*, _Tp&>() noexcept;
+};
+
+template <class _Fp, class _Tp>
+using __function_ref_bind_t _LIBCPP_NODEBUG = __function_ref_bind<_Fp, _Tp>::type;
+
+template <class _Fp>
+  requires is_function_v<_Fp>
+function_ref(_Fp*) -> function_ref<_Fp>;
+
+template <auto _Cw, class _Fn>
+  requires is_function_v<remove_pointer_t<_Fn>>
+function_ref(constant_wrapper<_Cw, _Fn>) -> function_ref<remove_pointer_t<_Fn>>;
+
+template <auto _Cw, class _Fn, class _Tp>
+function_ref(constant_wrapper<_Cw, _Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<_Fn, _Tp&>>;
+
+template <class>
+constexpr bool __is_constant_wrapper = false;
+
+template <auto _Value>
+constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
+
+template <class _Arg>
+struct __function_ref_arg_fwd {
+  using type _LIBCPP_NODEBUG = _Arg&&;
+};
+
+template <class _Arg>
+  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
+struct __function_ref_arg_fwd<_Arg> {
+  using type _LIBCPP_NODEBUG = _Arg;
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
new file mode 100644
index 0000000000000..a4c98d3031602
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -0,0 +1,184 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
+// and generates multiple versions of std::function_ref
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#include <__functional/invoke.h>
+#include <__memory/addressof.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/constant_wrapper.h>
+#include <__utility/forward.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#  error This header should only be included from function_ref.h
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
+    _NoExcept1,
+    _Rp,
+    _ArgTypes...>
+    : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
+
+template <class _Rp, class... _ArgTypes, bool __is_noexcept>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
+private:
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using =
+      _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
+          value;
+
+  template <class _Fn>
+  static constexpr bool __is_convertible_from_specialization_v =
+      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+
+  template <class... _Tp>
+  friend class function_ref;
+
+  template <class _Arg>
+  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+
+  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+
+  using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
+
+  __storage_t __storage_;
+  __call_t __call_;
+
+public:
+  template <class _Fp>
+    requires is_function_v<_Fp> && __is_invocable_using<_Fp>
+  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+      : __storage_(__fn_ptr),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+  }
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {}
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && __is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(__obj.__storage_), __call_(__obj.__call_) {}
+
+  template <auto _Cw, class _Fn>
+    requires __is_invocable_using<const _Fn&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
+      : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+    if constexpr (sizeof...(_ArgTypes) > 0 && (__constexpr_param<remove_cvref_t<_ArgTypes>> && ...)) {
+      static_assert(
+          !requires {
+            typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
+          },
+          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
+          "and produces a result with a structural type");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
+    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Tp>
+    requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
+      : __storage_(__obj_ptr_),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+
+    if constexpr (is_member_pointer_v<_Fn>) {
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(const function_ref&) noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+  template <class _Tp>
+    requires(!__is_convertible_from_specialization_v<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_wrapper<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
+
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
+    return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
+  }
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 9d588231d548d..df79d616a57e2 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -487,6 +487,11 @@ template <class  R, class ... ArgTypes>
 template <class  R, class ... ArgTypes>
   void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
 
+// [func.wrap.ref], non-owning wrapper
+template<class... S> class function_ref;                              // freestanding, not defined, since C++26
+template<class R, class... ArgTypes>
+  class function_ref<R(ArgTypes...) cv noexcept(noex)>;               // freestanding, since C++26
+
 template <class T> struct hash;
 
 template <> struct hash<bool>;
@@ -571,6 +576,10 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 #    include <__type_traits/unwrap_ref.h>
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+#    include <__functional/function_ref.h>
+#  endif
+
 #  include <version>
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..cb29fac536046 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1438,6 +1438,9 @@ module std {
     }
     module default_searcher             { header "__functional/default_searcher.h" }
     module function                     { header "__functional/function.h" }
+    module function_ref                 { header "__functional/function_ref.h" }
+    module function_ref_common          { header "__functional/function_ref_common.h" }
+    module function_ref_impl            { textual header "__functional/function_ref_impl.h" }
     module hash                         { header "__functional/hash.h" }
     module identity                     { header "__functional/identity.h" }
     module invoke                       { header "__functional/invoke.h" }
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..c62a7cbc8ba9c 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -131,7 +131,7 @@ __cpp_lib_freestanding_optional                         202311L <optional>
 __cpp_lib_freestanding_string_view                      202311L <string_view>
 __cpp_lib_freestanding_variant                          202311L <variant>
 __cpp_lib_fstream_native_handle                         202306L <fstream>
-__cpp_lib_function_ref                                  202306L <functional>
+__cpp_lib_function_ref                                  202603L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
@@ -593,7 +593,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 #   define __cpp_lib_fstream_native_handle              202306L
 # endif
-// # define __cpp_lib_function_ref                         202306L
+# define __cpp_lib_function_ref                         202603L
 // # define __cpp_lib_generate_random                      202403L
 // # define __cpp_lib_hazard_pointer                       202306L
 // # define __cpp_lib_inplace_vector                       202406L
diff --git a/libcxx/modules/std/functional.inc b/libcxx/modules/std/functional.inc
index 9ef8f584611fc..be804b19f75f8 100644
--- a/libcxx/modules/std/functional.inc
+++ b/libcxx/modules/std/functional.inc
@@ -95,6 +95,10 @@ export namespace std {
 
   using std::function;
 
+#if _LIBCPP_STD_VER >= 26
+  using std::function_ref;
+#endif // _LIBCPP_STD_VER >= 26
+
   using std::swap;
 
   using std::operator==;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
new file mode 100644
index 0000000000000..9703de0a59c2c
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+// Preconditions: If is_member_pointer_v<F> is true, obj is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+struct A {
+  void f() {}
+};
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(std::function_ref<void()>(std::cw<&A::f>, static_cast<A*>(nullptr)),
+                             "the object pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
new file mode 100644
index 0000000000000..e69776d0e3c90
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<class F> function_ref(F* f) noexcept;
+// Preconditions: f is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::function_ref<void()>(static_cast<void (*)()>(nullptr)), "the function pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
new file mode 100644
index 0000000000000..519957eec6878
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto c, class F>
+//   constexpr function_ref(constant_wrapper<c, F>) noexcept;
+// template<auto c, class F, class U>
+//   constexpr function_ref(constant_wrapper<c, F>, U&& obj) noexcept;
+// template<auto c, class F, class T>
+//   constexpr function_ref(constant_wrapper<c, F>, cv T* obj) noexcept;
+
+// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
+
+// For the first overload,
+// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+
+#include <functional>
+#include <utility>
+
+struct A {
+  void f();
+};
+
+struct B {
+  constexpr int operator()(std::constant_wrapper<42>) const { return 42; }
+  constexpr int operator()(int) const { return 42; }
+};
+
+// clang-format off
+void test() {
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+
+  int i;
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  A a;
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+}
+// clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
new file mode 100644
index 0000000000000..05756cfc3b3b8
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Track number of moves
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+struct Small {};
+
+struct Big {
+  char c[128];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
+// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
+template <>
+class std::function_ref<int> {
+  using storage = std::__storage_func_ref_t;
+  void test() {
+    {
+      // by value small argument
+      // the internal function should pass by value
+      using call = std::function_ref<void(Small)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
+    }
+    {
+      // by value big argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Big)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
+    }
+    {
+      // by value non-trivial small argument
+      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
+      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
+    }
+    {
+      // by lvalue reference argument
+      // the internal function should pass by lvalue reference
+      using call = std::function_ref<void(Small&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
+    }
+    {
+      // by rvalue reference argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Small&&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
+    }
+  }
+};
+
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
+void test() {
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In this case, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index b7b7d0334830a..bcc453880c7e1 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -494,17 +494,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_invoke
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 f2d74cc99d864..c990ef86c3be1 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
@@ -7138,17 +7138,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_gcd_lcm
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
new file mode 100644
index 0000000000000..029cb19d54f6a
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+int fn(int, float) { return 42; }
+
+int fn_noexcept(int, float) noexcept { return 42; }
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem(int, float) { return 42; }
+  int fn_mem_ref(int, float) & { return 42; }
+  int fn_mem_const(int, float) const { return 42; }
+  int fn_mem_const_ref(int, float) const& { return 42; }
+  int fn_mem_noexcept(int, float) noexcept { return 42; }
+};
+
+void test() {
+  // template<class F>
+  //  function_ref(F*) -> function_ref<F>;
+  {
+    std::function_ref fn_ref = fn;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = fn_noexcept;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F>
+  //  function_ref(constant_wrapper<c, F>) -> function_ref<...>;
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn_noexcept>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F, class T>
+  //  function_ref(constant_wrapper<c, F>, T&&) -> function_ref<...>;
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
+  }
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn_noexcept>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float) noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&() noexcept>>);
+  }
+  {
+    const S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&() noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_noexcept>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
new file mode 100644
index 0000000000000..b4df4392a2ade
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class T> function_ref& operator=(T) = delete;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - T is not the same type as function_ref,
+// - is_pointer_v<T> is false, and
+// - T is not a specialization of constant_wrapper.
+
+// non const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[] {}>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
+
+// const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[] { return 42; }>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[](int) { return 42; }>>);
+
+// non-const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[] noexcept {} >>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+// const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[] noexcept {}>>);
+static_assert(
+    !std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+int forty_two() { return 42; }
+
+void test() {
+  {
+    std::function_ref<int()> f(std::cw<[] { return 41; }>);
+    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = &forty_two;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const noexcept
+    std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
new file mode 100644
index 0000000000000..4b425ee03e286
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
@@ -0,0 +1,175 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f> constexpr function_ref(constant_wrapper<f>) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&> is true.
+
+auto l1          = [] {};
+auto l1_noexcept = [] noexcept {};
+auto l2          = [](int) {};
+auto l2_noexcept = [](int) noexcept {};
+
+struct NonConstInvocable {
+  void operator()() noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+// const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    std::function_ref<void()> f = std::cw<[] {}>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
new file mode 100644
index 0000000000000..dd6e10e8a3289
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -0,0 +1,359 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&, cv T*> is true.
+
+auto l1                = [](int*) {};
+auto l1_const          = [](const int*) {};
+auto l1_noexcept       = [](int*) noexcept {};
+auto l1_const_noexcept = [](const int*) noexcept {};
+auto l2                = [](int*, double) {};
+auto l2_const          = [](const int*, double) {};
+auto l2_noexcept       = [](int*, double) noexcept {};
+auto l2_const_noexcept = [](const int*, double) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(int*) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, const int*>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2_const>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const noexcept>,
+                                      std::constant_wrapper<l2_const_noexcept>,
+                                      int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_const_noexcept>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<l1_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const noexcept>,
+                                              std::constant_wrapper<l2_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<&A::f_const_noexcept>,
+                                              A*>);
+
+double f1(const int* x, double y) noexcept { return *x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int*) {}>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int*) {}>, &i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int* j, double d) {
+          *j = 5;
+          return *j + d;
+        }>,
+        &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
new file mode 100644
index 0000000000000..485fa95ca2998
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -0,0 +1,373 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class U>
+//   constexpr function_ref(constant_wrapper<f>, U&& obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_rvalue_reference_v<U&&> is false, and
+// - is-invocable-using<const F&, cv T&> is true.
+
+auto l1          = [](int) {};
+auto l1_noexcept = [](int) noexcept {};
+auto l2          = [](int, double) {};
+auto l2_noexcept = [](int, double) noexcept {};
+auto l3          = [](int&) {};
+auto l3_noexcept = [](int&) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(long) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&&>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int) {}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int) {}>, i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int& j, double d) {
+          j = 5;
+          return j + d;
+        }>,
+        i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
new file mode 100644
index 0000000000000..61b70a559d786
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
new file mode 100644
index 0000000000000..1a8b26f86c8be
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
new file mode 100644
index 0000000000000..f142b81367ef1
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> function_ref(F* f) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_function_v<F> is true, and
+// - is-invocable-using<F> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (*)(int)>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A (*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, A)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+
+// const noexcept(true)
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, A) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              void (*)(int, double) noexcept>);
+
+int fn() { return 42; }
+
+int fn_maythrow(int i, A a) { return i - a.i; }
+int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+
+void foo(int) {}
+void bar(int) noexcept {}
+struct Int {
+  int i;
+  Int(int ii) noexcept : i(ii) {}
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+void test() {
+  {
+    // simple case
+    std::function_ref<int()> f(&fn);
+    assert(f() == 42);
+  }
+  {
+    // explicit(false)
+    std::function_ref<int()> f = &fn;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int(int, A)> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // noexcept
+    std::function_ref<int(int, A) noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // const
+    auto l = [](int x, int y, int z) { return x + y - z; };
+    std::function_ref<int(int, int, int) const> f(+l);
+    assert(f(2, 3, 4) == 1);
+  }
+  {
+    // const noexcept
+    std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+
+  {
+    std::function_ref<Int(int, int, int)> f(&needs_conversion);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(&needs_conversion);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(&needs_conversion);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
+    assert(f4(1, 2, 3).i == 6);
+
+    {
+      std::function_ref r1 = foo;
+      std::function_ref r2 = bar;
+      r1                   = r2; // ok
+    }
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
new file mode 100644
index 0000000000000..0f27bba821391
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
new file mode 100644
index 0000000000000..b40dfa9f2cf4b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
new file mode 100644
index 0000000000000..56d6dd3b7109b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -0,0 +1,316 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> constexpr function_ref(F&&) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - remove_cvref_t<F> is not the same type as function_ref,
+// - is_member_pointer_v<T> is false, and
+// - is-invocable-using<cv T&> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+constexpr auto l1 = [] {};
+using L1          = std::remove_cvref_t<decltype(l1)>;
+
+constexpr auto l2 = [] { return A{5}; };
+using L2          = std::remove_cvref_t<decltype(l2)>;
+
+constexpr auto l2_noexcept = [] noexcept { return A{5}; };
+using L2Noexcept           = std::remove_cvref_t<decltype(l2_noexcept)>;
+
+constexpr auto l3 = [](int x, double d) { return x + d; };
+using L3          = std::remove_cvref_t<decltype(l3)>;
+
+constexpr auto l3_noexcept = [](int x, double d) noexcept { return x + d; };
+using L3Noexcept           = std::remove_cvref_t<decltype(l3_noexcept)>;
+
+struct NonConstInvocable {
+  int i;
+
+  int operator()(int x, double y) {
+    ++i;
+    return x + y + i;
+  }
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L2&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2&>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2 const&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const>, std::function_ref<int(int, double) const>>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(int, double)>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double)>, const NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>,
+                                              std::function_ref<int(int, double) const>>);
+
+// const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                      std::function_ref<int(int, double) const noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              std::function_ref<int(int, double) const noexcept>>);
+
+struct F {
+  int i;
+  int operator()(auto&&...) { return 5 + i; }
+
+  int operator()(auto&&...) const { return 6 + i; }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int fn() { return 5; }
+constexpr auto fn_noexcept = []() noexcept { return 6; };
+
+const auto one = []() noexcept { return 1; };
+const auto two = []() noexcept { return 2; };
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(l1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit(false)
+    std::function_ref<void()> f = l1;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<A() noexcept> f(l2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      auto a = f();
+      assert(a.i == 5);
+    }
+  }
+  {
+    // const
+    std::function_ref<double(int, double) const> f(l3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(l3_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // no copies of original callable
+    auto local = [i = 5] mutable { return i++; };
+    std::function_ref<int()> f(local);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 5);
+      assert(local() == 6);
+      assert(f() == 7);
+    }
+  }
+  {
+    // const correctness
+    F f{5};
+
+    std::function_ref<int()> f1(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1() == 10);
+      assert(std::as_const(f1)() == 10);
+    }
+
+    std::function_ref<int() const> f2(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 11);
+      assert(std::as_const(f2)() == 11);
+    }
+  }
+  {
+    // with conversions
+    NeedsConversion c{};
+
+    std::function_ref<Int(int, int, int)> f(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  {
+    // noexcept conversion
+    std::function_ref<int() noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const conversion
+    std::function_ref<int() const> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() noexcept> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to const conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() const> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // P3961R1 Less double indirection in function_ref
+    // double unwrapping
+    std::function_ref<int() const noexcept> f1(std::cw<one>);
+    std::function_ref<int()> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 1 || f2() == 2);
+      LIBCPP_ASSERT(f2() == 1);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
new file mode 100644
index 0000000000000..1cd0c769ce4d3
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -0,0 +1,362 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// R operator()(ArgTypes... args) const noexcept(noex);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "MoveOnly.h"
+
+template <class T, class... Args>
+concept ConstInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) };
+};
+
+template <class T, class... Args>
+concept ConstNoexceptInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) } noexcept;
+};
+
+static_assert(ConstInvocable<std::function_ref<void()>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void()>>);
+
+static_assert(ConstInvocable<std::function_ref<void() noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() noexcept>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void() const>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() const noexcept>>);
+
+struct S {
+  int data = 42;
+  int operator()(int& x) const noexcept { return data + x; }
+  int operator()(const int& x) const noexcept { return data + x + 1; }
+  int operator()(int&& x) const noexcept { return data + x + 2; }
+  int operator()(const int&& x) const noexcept { return data + x + 3; }
+};
+
+struct S2 {
+  double operator()(int x, int y, int z) noexcept { return x + y + z; }
+
+  double operator()(int x, int y, int z) const noexcept { return x + y + z + 1; }
+};
+
+struct Big {
+  char c[128];
+};
+
+struct S3Ref {
+  char operator()(Big& b) const noexcept { return b.c[0]; }
+};
+
+struct S3Value {
+  char operator()(Big b) const noexcept { return b.c[0]; }
+};
+
+void test_default() {
+  {
+    std::function_ref<void()> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&)> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly)> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int)> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&)> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&)> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&)> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&)> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int)> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int)> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&)> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const() {
+  {
+    std::function_ref<void() const> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_noexcept() {
+  {
+    std::function_ref<void() noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const_noexcept() {
+  {
+    std::function_ref<void() const noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test() {
+  test_default();
+  test_const();
+  test_noexcept();
+  test_const_noexcept();
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
new file mode 100644
index 0000000000000..72e26f13868ea
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
+#include <concepts>
+#include <functional>
+#include <type_traits>
+
+struct A {
+  A() {}
+  A(const A&) {}
+  ~A() {}
+};
+
+static_assert(std::copyable<std::function_ref<A(const A&)>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&)>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) noexcept>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const noexcept>>);
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..e54773a0aa2db 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -708,10 +708,9 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_function_ref",
             "values": {
-                "c++26": 202306  # P0792R14 function_ref: a type-erased callable reference
+                "c++26": 202603  # P3948R1 constant_wrapper is the only tool needed for passing constant expressions via function arguments
             },
             "headers": ["functional"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_gcd_lcm",

>From 22228d51d646bfe84f75a105b9949ec78280471e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 25 Apr 2026 14:20:04 +0100
Subject: [PATCH 03/21] review comments

---
 .../__functional/function_ref_common.h        | 22 ++++++-------------
 .../include/__functional/function_ref_impl.h  |  2 +-
 .../internal_call_signature.pass.cpp          |  2 +-
 .../constant_wrapper_ref.pass.cpp             |  8 +++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 12 ++++++++++
 5 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 61979231b56e8..72ded36d65107 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -16,6 +16,7 @@
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
 
@@ -37,21 +38,16 @@ struct __is_convertible_from_specialization : false_type {};
 // pointers
 // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
 // supported
-union __storage_func_ref_t {
+union __function_ref_storage {
   void* __obj_ptr_;
-  void const* __obj_const_ptr_;
   void (*__fn_ptr_)();
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage() noexcept : __obj_ptr_(nullptr) {}
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage(_Tp* __ptr) noexcept {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        __obj_const_ptr_ = __ptr;
-      } else {
-        __obj_ptr_ = __ptr;
-      }
+      __obj_ptr_ = const_cast<remove_cv_t<_Tp>*>(__ptr);
     } else {
       static_assert(is_function_v<_Tp>);
       __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
@@ -59,13 +55,9 @@ union __storage_func_ref_t {
   }
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__function_ref_storage __storage) {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
-      } else {
-        return static_cast<_Tp*>(__storage.__obj_ptr_);
-      }
+      return static_cast<_Tp*>(__storage.__obj_ptr_);
     } else {
       static_assert(is_function_v<_Tp>);
       return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index a4c98d3031602..36841d6745955 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -72,7 +72,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Arg>
   using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
 
-  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+  using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
   using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 05756cfc3b3b8..1e5df6413daf0 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -31,7 +31,7 @@ struct SmallButNonTrivial {
 // specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
 template <>
 class std::function_ref<int> {
-  using storage = std::__storage_func_ref_t;
+  using storage = std::__function_ref_storage;
   void test() {
     {
       // by value small argument
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index 485fa95ca2998..d3d5f6827193d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -362,6 +362,14 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // volatile objects
+    volatile int i = 5;
+    std::function_ref<int() > f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 56d6dd3b7109b..9ab012b3a04e8 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -161,6 +161,10 @@ constexpr auto fn_noexcept = []() noexcept { return 6; };
 const auto one = []() noexcept { return 1; };
 const auto two = []() noexcept { return 2; };
 
+struct VolatileCallable {
+  int operator()() volatile const { return 42; }
+};
+
 constexpr bool test() {
   {
     std::function_ref<void()> f(l1);
@@ -305,6 +309,14 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // volatile objects
+    const volatile VolatileCallable vc{};
+    std::function_ref<int()> f(vc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
 
   return true;
 }

>From 6510436f86baa312ebbe8047c4b02e0ed80237d6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 26 Apr 2026 09:41:10 +0100
Subject: [PATCH 04/21] remove stale comments

---
 libcxx/include/__functional/function_ref_common.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 72ded36d65107..c5adb8cbc7d52 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -34,10 +34,6 @@ class function_ref;
 template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization : false_type {};
 
-// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
-// pointers
-// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
-// supported
 union __function_ref_storage {
   void* __obj_ptr_;
   void (*__fn_ptr_)();

>From 6c97140e1d9bde13b22b988da708aff7d7e12948 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 14:06:05 +0100
Subject: [PATCH 05/21] fix bug

---
 .../__functional/function_ref_common.h        |  2 +-
 .../include/__functional/function_ref_impl.h  | 19 ++++++++++++-------
 .../internal_call_signature.pass.cpp          |  4 +++-
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  6 +++---
 .../func.wrap.ref.ctor/ref.pass.cpp           | 11 +++++++++++
 5 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index c5adb8cbc7d52..60bc6ef87af4f 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -31,7 +31,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <class _Fn1, class _Fn2>
 struct __is_convertible_from_specialization : false_type {};
 
 union __function_ref_storage {
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 36841d6745955..55755e9804c95 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -46,14 +46,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization<
-    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
-    _NoExcept1,
-    _Rp,
-    _ArgTypes...>
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) const noexcept(_NoExcept2)> >
     : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
 
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) noexcept(_NoExcept2)> >
+    : _And<is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)>,
+           is_convertible<_LIBCPP_FUNCTION_REF_CV int&, int&>> {};
+
 template <class _Rp, class... _ArgTypes, bool __is_noexcept>
 class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
 private:
@@ -62,9 +67,9 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
           value;
 
-  template <class _Fn>
+  template <class _Fn2>
   static constexpr bool __is_convertible_from_specialization_v =
-      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+      __is_convertible_from_specialization<function_ref, _Fn2>::value;
 
   template <class... _Tp>
   friend class function_ref;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 1e5df6413daf0..ebdf683791694 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -8,7 +8,9 @@
 
 // REQUIRES: std-at-least-c++26
 
-// Track number of moves
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
 
 #include <cassert>
 #include <functional>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index b4df4392a2ade..cb84b3a183920 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -35,9 +35,9 @@ static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrap
 static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
 
 // const noexcept(false)
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
@@ -61,7 +61,7 @@ static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::con
 // const noexcept(true)
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 9ab012b3a04e8..6b8b7c71f0d08 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -309,6 +309,17 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // P3961R1 Less double indirection in function_ref
+    // is-convertible-from-specialization<remove_cv_t<T>> is false
+    std::function_ref<int()> f1(std::cw<one>);
+    std::function_ref<int() const> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 2);
+    }
+  }
   {
     // volatile objects
     const volatile VolatileCallable vc{};

>From dc53e146aed5417d24104019487bdff14d1b5f42 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 17:28:36 +0100
Subject: [PATCH 06/21] ci

---
 libcxx/include/__functional/function_ref_impl.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 55755e9804c95..454b0ffb4afb3 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -15,6 +15,7 @@
 #include <__functional/invoke.h>
 #include <__memory/addressof.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/conjunction.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>

>From 686f1d5bf8ec1f41672f49e2f4d5f4e0864a7c5e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 10:39:44 +0100
Subject: [PATCH 07/21] static operator

---
 .../include/__functional/function_ref_impl.h  | 23 +++++++++++----
 .../func.wrap.ref.ctor/ref.pass.cpp           | 28 +++++++++++++++++++
 2 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 454b0ffb4afb3..ab65382782fb2 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
@@ -99,12 +100,22 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
-      : __storage_(std::addressof(__obj)),
-        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
-        }) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
+    using _Dn = remove_cv_t<_Tp>;
+    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
+                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+                  }) {
+      __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    } else {
+      __storage_ = __storage_t(std::addressof(__obj)),
+      __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    }
+  }
 
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 6b8b7c71f0d08..2684ec3548f8e 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -328,6 +328,34 @@ constexpr bool test() {
       assert(f() == 42);
     }
   }
+  {
+    // static call operator
+    struct StaticCall {
+      static int operator()(int x, int y) { return x + y; }
+    };
+    StaticCall sc;
+    std::function_ref<int(int, int)> f(sc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2) == 3);
+    }
+  }
+  {
+    // overload set with static and non-static call operator
+    struct OverloadSet {
+      static int operator()(int x) { return x + 1; }
+      int operator()(long x) const { return x + 2; }
+    };
+    OverloadSet os;
+    std::function_ref<int(int)> f1(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1(1) == 2);
+    }
+
+    std::function_ref<int(long)> f2(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1L) == 3);
+    }
+  }
 
   return true;
 }

>From e81960666dfc9d0042f9ca1242dabbbb123c1af2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 19:26:38 +0100
Subject: [PATCH 08/21] work around clang 22 bug

---
 libcxx/include/__functional/function_ref_common.h             | 4 ++++
 libcxx/include/__functional/function_ref_impl.h               | 4 +---
 .../func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp   | 1 +
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 60bc6ef87af4f..bfe3d938056f0 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -19,6 +19,7 @@
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
+#include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -107,6 +108,9 @@ struct __function_ref_arg_fwd<_Arg> {
   using type _LIBCPP_NODEBUG = _Arg;
 };
 
+template <class _Fn, class... _Args>
+concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
+
 #endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index ab65382782fb2..7a173dee7fa4f 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -102,9 +102,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
     using _Dn = remove_cv_t<_Tp>;
-    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
-                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
-                  }) {
+    if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
       };
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 2684ec3548f8e..11b993e4a3ec3 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -351,6 +351,7 @@ constexpr bool test() {
       assert(f1(1) == 2);
     }
 
+    LIBCPP_STATIC_ASSERT(!std::__statically_callable<OverloadSet, long>);
     std::function_ref<int(long)> f2(os);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f2(1L) == 3);

>From 542dbfc683dd21f950c619e74fb0bf884084bfdc Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:12:34 +0100
Subject: [PATCH 09/21] review comments

---
 libcxx/include/__functional/function_ref_common.h | 15 +++++++--------
 libcxx/include/__functional/function_ref_impl.h   |  6 +++---
 .../constant_arg.mandates.verify.cpp              |  2 +-
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index bfe3d938056f0..0f40a07073cfc 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -15,7 +15,8 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
-#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
@@ -98,15 +99,13 @@ template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
 template <class _Arg>
-struct __function_ref_arg_fwd {
-  using type _LIBCPP_NODEBUG = _Arg&&;
-};
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
 
 template <class _Arg>
-  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
-struct __function_ref_arg_fwd<_Arg> {
-  using type _LIBCPP_NODEBUG = _Arg;
-};
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
 
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a173dee7fa4f..3424a99bdd174 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -77,7 +77,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   friend class function_ref;
 
   template <class _Arg>
-  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+  using __arg_t _LIBCPP_NODEBUG = _If<__register_passable<_Arg>, _Arg, _Arg&&>;
 
   using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
@@ -135,8 +135,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
           !requires {
             typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
           },
-          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
-          "and produces a result with a structural type");
+          "cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref "
+          "constructed from cw would be ambiguous");
     }
   }
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 519957eec6878..63cfbc0e97c9b 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -41,7 +41,7 @@ void test() {
   // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
   std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}

>From 1c8f903140fd887f71947392e8a047b68f987359 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:13:48 +0100
Subject: [PATCH 10/21] use std::forward instead of static_cast

---
 libcxx/include/__functional/function_ref_impl.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 3424a99bdd174..d276c78fd09e6 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -92,7 +92,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -104,13 +104,13 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     using _Dn = remove_cv_t<_Tp>;
     if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return _Dn::operator()(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     } else {
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -125,7 +125,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     requires __is_invocable_using<const _Fn&>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
       : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -146,7 +146,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -160,7 +160,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(__obj_ptr_),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");

>From cffaf541e6877199313c646775d0acbec94e1035 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:14:02 +0100
Subject: [PATCH 11/21] format

---
 .../func.wrap.ref.ctor/constant_arg.mandates.verify.cpp      | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 63cfbc0e97c9b..c84cca2793bac 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -17,8 +17,9 @@
 
 // Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
 
-// For the first overload,
-// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+// For the first overload, f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>...
+// satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)>
+// is not a valid type.
 
 #include <functional>
 #include <utility>

>From f9909bf2f9aa919760098f84f623914296cca960 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 20 Jun 2026 16:28:00 +0100
Subject: [PATCH 12/21] review commnets

---
 libcxx/include/CMakeLists.txt                 |   1 +
 .../__functional/function_ref_common.h        |   9 -
 .../include/__functional/function_ref_impl.h  |   8 +-
 .../include/__type_traits/register_passable.h |  38 ++++
 libcxx/include/module.modulemap.in            |   1 +
 .../constant_arg.mandates.verify.cpp          |  26 +--
 .../internal_call_signature.pass.cpp          | 138 ---------------
 .../internal_call_signature.sh.cpp            |  68 ++++++++
 .../views.span/span.cons/deduct.pass.cpp      |   7 -
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     |   9 +
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  24 ++-
 .../constant_wrapper_ptr.pass.cpp             |  60 ++++++-
 .../constant_wrapper_ref.pass.cpp             |  59 ++++++-
 .../func.wrap.ref.ctor/copy.pass.cpp          |   5 +
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   |  71 ++++++--
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  |   6 -
 .../func.wrap.ref.ctor/move.pass.cpp          |  70 +-------
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 102 ++---------
 .../func.wrap.ref.inv/invoke.pass.cpp         | 162 ++++++++++++++++++
 19 files changed, 506 insertions(+), 358 deletions(-)
 create mode 100644 libcxx/include/__type_traits/register_passable.h
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index a0d5cf70d267d..fcea3b7601182 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -914,6 +914,7 @@ set(files
   __type_traits/rank.h
   __type_traits/reference_constructs_from_temporary.h
   __type_traits/reference_converts_from_temporary.h
+  __type_traits/register_passable.h
   __type_traits/remove_all_extents.h
   __type_traits/remove_const.h
   __type_traits/remove_const_ref.h
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 0f40a07073cfc..e6fa031432a91 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -98,15 +98,6 @@ constexpr bool __is_constant_wrapper = false;
 template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
-template <class _Arg>
-concept __itanium_trivial_for_calls =
-    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
-    is_trivially_move_constructible_v<_Arg>;
-
-template <class _Arg>
-concept __register_passable =
-    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
-
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
 
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index d276c78fd09e6..7a968fc64de70 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/register_passable.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
@@ -92,7 +93,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
+          _Fp* __fn_ptr1 = __storage_t::template __get<_Fp>(__storage);
+          return std::invoke_r<_Rp>(__fn_ptr1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -110,7 +112,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
+        return std::invoke_r<_Rp>(__obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -145,7 +147,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          auto& __obj1 = static_cast<_LIBCPP_FUNCTION_REF_CV _Tp&>(*__storage_t::template __get<_Tp>(__storage));
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
diff --git a/libcxx/include/__type_traits/register_passable.h b/libcxx/include/__type_traits/register_passable.h
new file mode 100644
index 0000000000000..21e23e62052c1
--- /dev/null
+++ b/libcxx/include/__type_traits/register_passable.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___TYPE_TRAITS_REGISTER_PASSABLE_H
+#define _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
+
+#include <__config>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Arg>
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
+
+template <class _Arg>
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
+
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index cb29fac536046..e3b9e5ac09cb3 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -351,6 +351,7 @@ module std_core {
       header "__type_traits/reference_converts_from_temporary.h"
       export std_core.type_traits.integral_constant
     }
+    module register_passable                          { header "__type_traits/register_passable.h" }
     module remove_all_extents                         { header "__type_traits/remove_all_extents.h" }
     module remove_const_ref                           { header "__type_traits/remove_const_ref.h" }
     module remove_const                               { header "__type_traits/remove_const.h" }
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index c84cca2793bac..08e8b8e9b8b52 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -35,27 +35,27 @@ struct B {
 
 // clang-format off
 void test() {
-  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
+  // expected-error-re@*:* {{static assertion failed {{.*}}cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
-  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   A a;
-  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 }
 // clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
deleted file mode 100644
index ebdf683791694..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-struct Small {};
-
-struct Big {
-  char c[128];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
-// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
-template <>
-class std::function_ref<int> {
-  using storage = std::__function_ref_storage;
-  void test() {
-    {
-      // by value small argument
-      // the internal function should pass by value
-      using call = std::function_ref<void(Small)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
-    }
-    {
-      // by value big argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Big)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
-    }
-    {
-      // by value non-trivial small argument
-      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
-      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
-    }
-    {
-      // by lvalue reference argument
-      // the internal function should pass by lvalue reference
-      using call = std::function_ref<void(Small&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
-    }
-    {
-      // by rvalue reference argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Small&&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
-    }
-  }
-};
-
-struct TrackCopyMove {
-  mutable int copy_count = 0;
-  int move_count         = 0;
-
-  TrackCopyMove() = default;
-  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
-    ++copy_count;
-    ++other.copy_count;
-  }
-
-  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
-    ++move_count;
-    ++other.move_count;
-  }
-  TrackCopyMove& operator=(const TrackCopyMove& other) {
-    ++copy_count;
-    ++other.copy_count;
-    return *this;
-  }
-  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
-    ++move_count;
-    ++other.move_count;
-    return *this;
-  }
-};
-
-void test() {
-  {
-    // Arg type is an lvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&)> f = lambda;
-    f(t);
-  }
-  {
-    // Arg type is an rvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&&)> f = lambda;
-    f(std::move(t));
-  }
-  {
-    // Arg type is a prvalue, we should move but not copy the object
-    // In this case, where the type is not trivially copyable, the object should be
-    // moved exactly once when passing into the lambda. The internal functions
-    // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) {
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 1);
-    };
-    std::function_ref<void(TrackCopyMove)> f = lambda;
-    f(TrackCopyMove{});
-  }
-}
-
-int main(int, char**) {
-  test();
-
-  return 0;
-}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
new file mode 100644
index 0000000000000..a32a1083b5596
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// REQUIRES: has-filecheck
+
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
+
+struct Small {
+  char c[8];
+};
+
+struct Big {
+  char c[256];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// CHECK: define{{.*}} @_Z10test_small
+// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
+// CHECK: %3 = alloca %struct.Small, align 1
+// CHECK: %6 = alloca %struct.Small, align 1
+// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
+// CHECK: store i64 %1, ptr %7, align 1
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
+// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
+// CHECK: %15 = load i64, ptr %14, align 1
+// CHECK: call void %10(ptr %13, i64 %15)
+//
+// The above has two local copies of Small object. It loads %1 to local copy %3, and then
+// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
+void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
+
+// CHECK: define{{.*}} @_Z8test_big
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
+//
+// The above directly calls the function pointer with the parameter Big object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the Big object by reference
+void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
+
+// CHECK: define{{.*}} @_Z22test_small_non_trivial
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
+//
+// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the SmallButNonTrivial object by reference
+void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index c9e17b3aec49a..9e41afad2258e 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -73,13 +73,6 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
-
-  {
-    std::span s{std::begin(arr), std::cw<3>};
-    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
-    assert(s.size() == std::size(arr));
-    assert(s.data() == std::data(arr));
-  }
 #endif
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
index 029cb19d54f6a..730a07202062d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -8,6 +8,15 @@
 
 // REQUIRES: std-at-least-c++26
 
+// template<class F>
+// function_ref(F*) -> function_ref<F>;
+//
+// template<auto c, class F0>
+// function_ref(constant_wrapper<c, F0>) -> function_ref<see below>;
+//
+// template<auto c, class F, class T>
+// function_ref(constant_wrapper<c, F>, T&&) -> function_ref<see below>;
+
 #include <cassert>
 #include <functional>
 #include <utility>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index cb84b3a183920..5866488be4a94 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -11,6 +11,7 @@
 // template<class T> function_ref& operator=(T) = delete;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -73,38 +74,49 @@ static_assert(
 
 int forty_two() { return 42; }
 
+// These runtime tests are testing that when the constraints are not met,
+// the assignment operator is not deleted and the implicit constructor
+// and the move assignment are called
 void test() {
   {
     std::function_ref<int()> f(std::cw<[] { return 41; }>);
-    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f =
+        std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = &forty_two;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = &forty_two;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // noexcept
     std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() noexcept>&> decltype(auto) result = f = std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const noexcept
     std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() const noexcept>&> decltype(auto) result = f =
+        std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
index dd6e10e8a3289..74f52063b6788 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -94,6 +94,7 @@ static_assert(
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
 static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, const int*>);
 
 static_assert(
     !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
@@ -180,6 +181,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int*) const noexcept { return 4; }
+  constexpr int operator()(const int*) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -231,6 +237,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -248,6 +262,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -263,39 +285,51 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -348,6 +382,20 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, &i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, &i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, &i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index d3d5f6827193d..979da32e60c3d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -199,6 +199,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int&) const noexcept { return 4; }
+  constexpr int operator()(const int&) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -248,6 +253,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -264,6 +277,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -278,37 +299,49 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -370,6 +403,20 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
index 61b70a559d786..7f837149cc6b5 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -22,6 +22,11 @@ static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
 double f1(int x, double y) noexcept { return x + y; }
 
 struct Int {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
index 1a8b26f86c8be..53c79b4097362 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -11,6 +11,7 @@
 // constexpr function_ref& operator=(const function_ref&) noexcept = default;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -22,6 +23,11 @@ static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
@@ -41,7 +47,8 @@ constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = f;
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -51,7 +58,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = f;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
@@ -62,7 +70,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -72,7 +81,8 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -82,32 +92,47 @@ constexpr bool test() {
     // with conversions
     std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
         std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
@@ -117,31 +142,47 @@ constexpr bool test() {
     // with conversions function pointer
     std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
     std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions function pointer
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index f142b81367ef1..aa0ccf8d67993 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -134,12 +134,6 @@ void test() {
 
     std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
     assert(f4(1, 2, 3).i == 6);
-
-    {
-      std::function_ref r1 = foo;
-      std::function_ref r2 = bar;
-      r1                   = r2; // ok
-    }
   }
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
index 0f27bba821391..025df83ad538d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -8,6 +8,8 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
 #include <functional>
 #include <utility>
@@ -20,18 +22,12 @@ static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-double f1(int x, double y) noexcept { return x + y; }
-
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+double f1(int x, double y) noexcept { return x + y; }
 
 constexpr bool test() {
   {
@@ -66,58 +62,6 @@ constexpr bool test() {
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
index b40dfa9f2cf4b..10620f5d48725 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -8,7 +8,10 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -20,26 +23,20 @@ static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
-
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
-int zero(Int, Int, Int) noexcept { return 0; }
-
 constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -49,8 +46,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = std::move(f);
-
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
       assert(f2() == 42);
@@ -60,7 +57,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -70,81 +68,13 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
-        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 1cd0c769ce4d3..0dced07c439d6 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -67,6 +67,32 @@ struct S3Value {
   char operator()(Big b) const noexcept { return b.c[0]; }
 };
 
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
 void test_default() {
   {
     std::function_ref<void()> f = [] {};
@@ -135,6 +161,40 @@ void test_default() {
     std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const() {
@@ -205,6 +265,40 @@ void test_const() {
     std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_noexcept() {
@@ -276,6 +370,40 @@ void test_noexcept() {
     std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const_noexcept() {
@@ -347,6 +475,40 @@ void test_const_noexcept() {
     std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test() {

>From 535af2a3d49cc07ab47aab3efbb4f7967182f661 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:38:35 +0100
Subject: [PATCH 13/21] format

---
 .../func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp       | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 0dced07c439d6..820fed5fc76bc 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -384,7 +384,7 @@ void test_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -397,7 +397,7 @@ void test_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };
@@ -489,7 +489,7 @@ void test_const_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -502,7 +502,7 @@ void test_const_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };

>From fbdc3c1843229385e121162ea20a976bda3e9ff6 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:31 +0100
Subject: [PATCH 14/21] Update libcxx/docs/Status/Cxx26Issues.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 900ebb4f2aa8a..95aa2bff5d9ea 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","24","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""

>From 901f83e46c2c259dfde4b3c76430c9b7166f050a Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:40 +0100
Subject: [PATCH 15/21] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index c83f46c288d7e..45aeba43eeaeb 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -192,7 +192,7 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
 "`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""

>From 8dd462c97bbdcb75caaa9a49534e3964bb85b12d Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:50 +0100
Subject: [PATCH 16/21] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 45aeba43eeaeb..7bc6aa302f1c4 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","24","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."

>From c20ebbf3afe9c1ddb4484e10ff0f5825443c4bec Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:15:01 +0100
Subject: [PATCH 17/21] Update libcxx/docs/Status/Cxx26Issues.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 95aa2bff5d9ea..06ceef17b8c70 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","24","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""

>From 6fee0703e9a0c3d5919b8073d403d12df96c2bdb Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:15:11 +0100
Subject: [PATCH 18/21] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7bc6aa302f1c4..7efbe312f3e82 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -194,7 +194,7 @@
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
 "`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","24","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""

>From f592125ab8612d6fa3e96dd1a477305a3de3ee0e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:32:58 +0100
Subject: [PATCH 19/21] review

---
 libcxx/docs/ReleaseNotes/23.rst               |  3 -
 libcxx/docs/ReleaseNotes/24.rst               | 61 +++++++++++++++++
 .../internal_call_signature.sh.cpp            | 68 -------------------
 3 files changed, 61 insertions(+), 71 deletions(-)
 create mode 100644 libcxx/docs/ReleaseNotes/24.rst
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index ecf3596ac66c4..1213e8f43c8cd 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,9 +55,6 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
-- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
-- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
-- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
new file mode 100644
index 0000000000000..7a8c1e589b1df
--- /dev/null
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -0,0 +1,61 @@
+===========================================
+Libc++ 23.0.0 (In-Progress) Release Notes
+===========================================
+
+.. contents::
+   :local:
+   :depth: 2
+
+Written by the `Libc++ Team <https://libcxx.llvm.org>`_
+
+.. warning::
+
+   These are in-progress notes for the upcoming libc++ 24.0.0 release.
+   Release notes for previous releases can be found on
+   `the Download Page <https://releases.llvm.org/download.html>`_.
+
+Introduction
+============
+
+This document contains the release notes for the libc++ C++ Standard Library,
+part of the LLVM Compiler Infrastructure, release 24.0.0. Here we describe the
+status of libc++ in some detail, including major improvements from the previous
+release and new feature work. For the general LLVM release notes, see `the LLVM
+documentation <https://llvm.org/docs/ReleaseNotes.html>`_. All LLVM releases may
+be downloaded from the `LLVM releases web site <https://llvm.org/releases/>`_.
+
+For more information about libc++, please see the `Libc++ Web Site
+<https://libcxx.llvm.org>`_ or the `LLVM Web Site <https://llvm.org>`_.
+
+Note that if you are reading this file from a Git checkout or the
+main Libc++ web page, this document applies to the *next* release, not
+the current one. To see the release notes for a specific release, please
+see the `releases page <https://llvm.org/releases/>`_.
+
+What's New in Libc++ 24.0.0?
+==============================
+
+Implemented Papers
+------------------
+
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
+
+Improvements and New Features
+-----------------------------
+
+Deprecations and Removals
+-------------------------
+
+Potentially breaking changes
+----------------------------
+
+Announcements About Future Releases
+-----------------------------------
+
+ABI Affecting Changes
+---------------------
+
+Build System Changes
+--------------------
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
deleted file mode 100644
index a32a1083b5596..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// REQUIRES: has-filecheck
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
-
-struct Small {
-  char c[8];
-};
-
-struct Big {
-  char c[256];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// CHECK: define{{.*}} @_Z10test_small
-// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
-// CHECK: %3 = alloca %struct.Small, align 1
-// CHECK: %6 = alloca %struct.Small, align 1
-// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
-// CHECK: store i64 %1, ptr %7, align 1
-// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
-// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
-// CHECK: %15 = load i64, ptr %14, align 1
-// CHECK: call void %10(ptr %13, i64 %15)
-//
-// The above has two local copies of Small object. It loads %1 to local copy %3, and then
-// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
-void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
-
-// CHECK: define{{.*}} @_Z8test_big
-// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
-//
-// The above directly calls the function pointer with the parameter Big object pointer
-// points to the operator()'s parameter %1, thus, no extra copies/moves
-// So we internally pass the Big object by reference
-void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
-
-// CHECK: define{{.*}} @_Z22test_small_non_trivial
-// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
-//
-// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
-// points to the operator()'s parameter %1, thus, no extra copies/moves
-// So we internally pass the SmallButNonTrivial object by reference
-void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file

>From 37cdeb6d97c3e6fddaaf47b8f1636c4ac559bb82 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:14:44 +0100
Subject: [PATCH 20/21] CI

---
 libcxx/include/__functional/function_ref_impl.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a968fc64de70..ada14643ec496 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -158,8 +158,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <auto _Cw, class _Fn, class _Tp>
     requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
-                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
-      : __storage_(__obj_ptr_),
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+      : __storage_(__obj_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
@@ -169,7 +169,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     }
 
     if constexpr (is_member_pointer_v<_Fn>) {
-      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
     }
   }
 

>From 40a86e496b92fa25d1760d1c8eaa42d5fad1c5fa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:54:26 +0100
Subject: [PATCH 21/21] CI

---
 libcxx/docs/ReleaseNotes.rst    | 3 ++-
 libcxx/docs/ReleaseNotes/24.rst | 8 ++++++++
 libcxx/docs/conf.py             | 4 ++--
 3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index ff35abef61203..37078d83139ab 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -1,4 +1,4 @@
-.. include:: ReleaseNotes/23.rst
+.. include:: ReleaseNotes/24.rst
 
 .. Make sure to reference the non-live release notes in a toctree to avoid Sphinx errors.
 .. toctree::
@@ -7,6 +7,7 @@
     ReleaseNotes/20
     ReleaseNotes/21
     ReleaseNotes/22
+    ReleaseNotes/23
 
 .. The release notes are in versioned files, but we make sure to keep publishing
 .. them in an unversioned ReleaseNotes.html page for external sites to reference.
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 7a8c1e589b1df..186227890327f 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -57,5 +57,13 @@ Announcements About Future Releases
 ABI Affecting Changes
 ---------------------
 
+- ``std::allocator`` is now trivially default constructible. The behaviour can be reverted by defining
+  ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR``. Please inform the libc++ team if you need this flag, since it will
+  be removed in LLVM 24 if there is no evidence that it's required.
+
+- ``bitset::operator[]`` now returns ``bool``, making libc++ conforming. The behaviour can be reverted by defining
+  ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``. Please inform the libc++ team if you need this flag,
+  since it will be removed in LLVM 24 if there is no evidence that it's required.
+
 Build System Changes
 --------------------
diff --git a/libcxx/docs/conf.py b/libcxx/docs/conf.py
index 9051bce7cf356..98aa52edd5d2e 100644
--- a/libcxx/docs/conf.py
+++ b/libcxx/docs/conf.py
@@ -32,9 +32,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = "23.0"
+version = "24.0"
 # The full version, including alpha/beta/rc tags.
-release = "23.0"
+release = "24.0"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.


From libcxx-commits at lists.llvm.org  Fri Jul 10 01:12:15 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 01:12:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a50a95f.331483e7.2440f8.9c2f@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Fri Jul 10 01:12:15 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 01:12:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a50a95f.bb839515.27d21e.3bfe@mx.google.com>


https://github.com/philnik777 commented:

> @philnik777 I have made the changes now, can you please take a look? We are trying to land this for LLVM 23 so it would be great to get this approved before that.

Then maybe don't open the PR a week in advance of the branch day.

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Fri Jul 10 01:12:15 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 01:12:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a50a95f.f732ad81.3a4994.226d@mx.google.com>


================
@@ -664,6 +674,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (noreplace) mode suffix; these cases fall through to
+// default (return nullptr), which is conformant per C23 7.21.5.3p5 (fail if atomic check-and-create is unsupported).
+#      if !defined(_AIX)
----------------
philnik777 wrote:

Why is this guarded now?

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Fri Jul 10 01:12:16 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 01:12:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a50a960.6c5720b3.128ed.d1bb@mx.google.com>


================
@@ -1206,9 +1220,19 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
-  void open(const char* __s, ios_base::openmode __mode = ios_base::in);
+  void open(const char* __s, ios_base::openmode __mode = ios_base::in)
+#    if defined(_AIX)
+      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
+          ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
+  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in)
+#      if defined(_AIX)
+      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
----------------
philnik777 wrote:

This change is probably garbage and definitely untested.

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Fri Jul 10 01:16:44 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 01:16:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a50aa6c.a87eb5dc.7d817.50b0@mx.google.com>


================
@@ -32,15 +33,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _InputIterator, class _Size, class _Func, class _Proj>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
-__for_each_n(_InputIterator __first, _Size __orig_n, _Func&& __f, _Proj& __proj) {
-  typedef decltype(std::__convert_to_integral(__orig_n)) _IntegralSize;
-  _IntegralSize __n = __orig_n;
-
+__for_each_n(_InputIterator __first, _Size __n, _Func&& __f, _Proj& __proj) {
 #ifndef _LIBCPP_CXX03_LANG
   if constexpr (__is_segmented_iterator_v<_InputIterator>) {
     using __local_iterator = typename __segmented_iterator_traits<_InputIterator>::__local_iterator;
     if constexpr (__has_random_access_iterator_category<__local_iterator>::value) {
-      return std::__for_each_n_segment(__first, __orig_n, [&](__local_iterator __lfirst, __local_iterator __llast) {
----------------
philnik777 wrote:

I can't remember that I did in the first place. Probably just an oversight.

https://github.com/llvm/llvm-project/pull/207086

From libcxx-commits at lists.llvm.org  Fri Jul 10 01:25:04 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 01:25:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Replace the of use custom
 sections for detecting overriden functions (PR #208330)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208330@github.com>
Message-ID: <6a50ac60.743400b0.2067c9.0fce@mx.google.com>


================
@@ -63,64 +51,53 @@
 // want to be defining special sections inside user's executables which use our headers.
 //
 
-#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
+#if defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
-
-_LIBCPP_BEGIN_NAMESPACE_STD template <typename T, T* _Func>
-_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-  // Declare two dummy bytes and give them these special `__asm` values. These values are
-  // defined by the linker, which means that referring to `&__lcxx_override_start` will
-  // effectively refer to the address where the section starts (and same for the end).
-  extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
-  extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
-
-  // Now get a uintptr_t out of these locations, and out of the function pointer.
-  uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
-  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
-  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
-
-#  if __has_feature(ptrauth_calls)
-  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
-  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
-  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
-  // stripped the function pointer. See rdar://122927845.
-  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
-#  endif
+#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
 
-  // Finally, the function was overridden if it falls outside of the section's bounds.
-  return __ptr < __start || __ptr > __end;
-}
-_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_BEGIN_NAMESPACE_STD
 
-// The NVPTX linker cannot create '__start/__stop' sections.
-#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)
+namespace {
 
-#  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+template <typename T>
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI T* __libcpp_launder(T* __ptr) noexcept {
+  __asm__ volatile("" : "+r"(__ptr));
+  return __ptr;
+}
----------------
philnik777 wrote:

How is this different from what `std::launder` does? If it's different, this seems like rather unfortunate naming.

https://github.com/llvm/llvm-project/pull/208330

From libcxx-commits at lists.llvm.org  Fri Jul 10 01:27:38 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 01:27:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for std::is_heap
 and std::is_heap_until (PR #207045)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207045@github.com>
Message-ID: <6a50acfa.92d2873c.258297.a693@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/207045

From libcxx-commits at lists.llvm.org  Fri Jul 10 01:48:45 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 01:48:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Use the same .clang-format
	as libc++ (PR #208661)
Message-ID: <llvm.org/llvm/llvm-project/pull/208661@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/208661

libc++abi is very close to libc++, and even shares some amount of code. We should keep the formatting style the same between the two libraries.


>From 18f60e986f427f89ae83c4a0cb299a9aed903378 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 10 Jul 2026 10:47:39 +0200
Subject: [PATCH] [libc++abi] Use the same .clang-format as libc++

---
 libcxxabi/.clang-format | 75 ++++++++++++++++++++++++++++++++++++++---
 1 file changed, 71 insertions(+), 4 deletions(-)

diff --git a/libcxxabi/.clang-format b/libcxxabi/.clang-format
index b4f56c0de9819..9a455d304eb56 100644
--- a/libcxxabi/.clang-format
+++ b/libcxxabi/.clang-format
@@ -1,14 +1,81 @@
 BasedOnStyle: LLVM
+# Note that we don't specify the language in this file because some files are
+# detected as Cpp, but others are detected as ObjC and we want this formatting
+# to apply to all types of files.
 
-Language: Cpp
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: Consecutive
+AlignConsecutiveBitFields: Consecutive
+AlignEscapedNewlines: Right
+AlignOperands: AlignAfterOperator
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortFunctionsOnASingleLine: true
+AllowShortLambdasOnASingleLine: All
+AttributeMacros: [
+                  '_ALIGNAS_TYPE',
+                  '_ALIGNAS',
+                  '_LIBCPP_ACQUIRE_CAPABILITY',
+                  '_LIBCPP_ACQUIRE_SHARED_CAPABILITY',
+                  '_LIBCPP_ALIGNOF',
+                  '_LIBCPP_ALWAYS_INLINE',
+                  '_LIBCPP_CAPABILITY',
+                  '_LIBCPP_CONSTEXPR_SINCE_CXX14',
+                  '_LIBCPP_CONSTEXPR_SINCE_CXX17',
+                  '_LIBCPP_CONSTEXPR_SINCE_CXX20',
+                  '_LIBCPP_CONSTEXPR_SINCE_CXX23',
+                  '_LIBCPP_CONSTEXPR',
+                  '_LIBCPP_DEPRECATED_IN_CXX11',
+                  '_LIBCPP_DEPRECATED_IN_CXX14',
+                  '_LIBCPP_DEPRECATED_IN_CXX17',
+                  '_LIBCPP_DEPRECATED_IN_CXX20',
+                  '_LIBCPP_DEPRECATED_IN_CXX23',
+                  '_LIBCPP_DEPRECATED',
+                  '_LIBCPP_DIAGNOSE_NULLPTR_IF',
+                  '_LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION',
+                  '_LIBCPP_EXPORTED_FROM_ABI',
+                  '_LIBCPP_EXTERN_TEMPLATE_TYPE_VIS',
+                  '_LIBCPP_FALLTHROUGH',
+                  '_LIBCPP_HIDDEN',
+                  '_LIBCPP_HIDE_FROM_ABI_AFTER_V1',
+                  '_LIBCPP_HIDE_FROM_ABI',
+                  '_LIBCPP_NO_SANITIZE',
+                  '_LIBCPP_NO_UNIQUE_ADDRESS',
+                  '_LIBCPP_NOALIAS',
+                  '_LIBCPP_OVERRIDABLE_FUNC_VIS',
+                  '_LIBCPP_RELEASE_CAPABILITY',
+                  '_LIBCPP_REQUIRES_CAPABILITY',
+                  '_LIBCPP_SCOPED_LOCKABLE',
+                  '_LIBCPP_STANDALONE_DEBUG',
+                  '_LIBCPP_TEMPLATE_DATA_VIS',
+                  '_LIBCPP_TRY_ACQUIRE_CAPABILITY',
+                  '_LIBCPP_TRY_ACQUIRE_SHARED_CAPABILITY',
+                  '_LIBCPP_USING_IF_EXISTS',
+                 ]
+BinPackArguments: false
+BinPackParameters: false
+BreakBeforeConceptDeclarations: true
+BreakInheritanceList: BeforeColon
+EmptyLineAfterAccessModifier: Never
+EmptyLineBeforeAccessModifier: Always
+IndentWrappedFunctionNames: false
+IndentRequires: true
+InsertTrailingCommas: Wrapped
+KeepEmptyLinesAtTheStartOfBlocks: false
+MaxEmptyLinesToKeep: 1
+PackConstructorInitializers: NextLine
+
+PenaltyIndentedWhitespace: 2
+
+Standard: c++20
+SpacesInAngles: Leave
 
 AlwaysBreakTemplateDeclarations: true
 PointerAlignment: Left
 
-# Disable formatting options which may break tests.
-SortIncludes: false
-ReflowComments: false
 
+# libc++'s preferred indentions of preprocessor statements.
 IndentPPDirectives: AfterHash
 
 # libc++ has some long names so we need more than the 80 column limit imposed by LLVM style, for sensible formatting


From libcxx-commits at lists.llvm.org  Fri Jul 10 01:49:25 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 01:49:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Use the same .clang-format
	as libc++ (PR #208661)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208661@github.com>
Message-ID: <6a50b215.f8b79514.1d52d0.6335@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxxabi

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

libc++abi is very close to libc++, and even shares some amount of code. We should keep the formatting style the same between the two libraries.


---
Full diff: https://github.com/llvm/llvm-project/pull/208661.diff


1 Files Affected:

- (modified) libcxxabi/.clang-format (+71-4) 


``````````diff
diff --git a/libcxxabi/.clang-format b/libcxxabi/.clang-format
index b4f56c0de9819..9a455d304eb56 100644
--- a/libcxxabi/.clang-format
+++ b/libcxxabi/.clang-format
@@ -1,14 +1,81 @@
 BasedOnStyle: LLVM
+# Note that we don't specify the language in this file because some files are
+# detected as Cpp, but others are detected as ObjC and we want this formatting
+# to apply to all types of files.
 
-Language: Cpp
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: Consecutive
+AlignConsecutiveBitFields: Consecutive
+AlignEscapedNewlines: Right
+AlignOperands: AlignAfterOperator
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortFunctionsOnASingleLine: true
+AllowShortLambdasOnASingleLine: All
+AttributeMacros: [
+                  '_ALIGNAS_TYPE',
+                  '_ALIGNAS',
+                  '_LIBCPP_ACQUIRE_CAPABILITY',
+                  '_LIBCPP_ACQUIRE_SHARED_CAPABILITY',
+                  '_LIBCPP_ALIGNOF',
+                  '_LIBCPP_ALWAYS_INLINE',
+                  '_LIBCPP_CAPABILITY',
+                  '_LIBCPP_CONSTEXPR_SINCE_CXX14',
+                  '_LIBCPP_CONSTEXPR_SINCE_CXX17',
+                  '_LIBCPP_CONSTEXPR_SINCE_CXX20',
+                  '_LIBCPP_CONSTEXPR_SINCE_CXX23',
+                  '_LIBCPP_CONSTEXPR',
+                  '_LIBCPP_DEPRECATED_IN_CXX11',
+                  '_LIBCPP_DEPRECATED_IN_CXX14',
+                  '_LIBCPP_DEPRECATED_IN_CXX17',
+                  '_LIBCPP_DEPRECATED_IN_CXX20',
+                  '_LIBCPP_DEPRECATED_IN_CXX23',
+                  '_LIBCPP_DEPRECATED',
+                  '_LIBCPP_DIAGNOSE_NULLPTR_IF',
+                  '_LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION',
+                  '_LIBCPP_EXPORTED_FROM_ABI',
+                  '_LIBCPP_EXTERN_TEMPLATE_TYPE_VIS',
+                  '_LIBCPP_FALLTHROUGH',
+                  '_LIBCPP_HIDDEN',
+                  '_LIBCPP_HIDE_FROM_ABI_AFTER_V1',
+                  '_LIBCPP_HIDE_FROM_ABI',
+                  '_LIBCPP_NO_SANITIZE',
+                  '_LIBCPP_NO_UNIQUE_ADDRESS',
+                  '_LIBCPP_NOALIAS',
+                  '_LIBCPP_OVERRIDABLE_FUNC_VIS',
+                  '_LIBCPP_RELEASE_CAPABILITY',
+                  '_LIBCPP_REQUIRES_CAPABILITY',
+                  '_LIBCPP_SCOPED_LOCKABLE',
+                  '_LIBCPP_STANDALONE_DEBUG',
+                  '_LIBCPP_TEMPLATE_DATA_VIS',
+                  '_LIBCPP_TRY_ACQUIRE_CAPABILITY',
+                  '_LIBCPP_TRY_ACQUIRE_SHARED_CAPABILITY',
+                  '_LIBCPP_USING_IF_EXISTS',
+                 ]
+BinPackArguments: false
+BinPackParameters: false
+BreakBeforeConceptDeclarations: true
+BreakInheritanceList: BeforeColon
+EmptyLineAfterAccessModifier: Never
+EmptyLineBeforeAccessModifier: Always
+IndentWrappedFunctionNames: false
+IndentRequires: true
+InsertTrailingCommas: Wrapped
+KeepEmptyLinesAtTheStartOfBlocks: false
+MaxEmptyLinesToKeep: 1
+PackConstructorInitializers: NextLine
+
+PenaltyIndentedWhitespace: 2
+
+Standard: c++20
+SpacesInAngles: Leave
 
 AlwaysBreakTemplateDeclarations: true
 PointerAlignment: Left
 
-# Disable formatting options which may break tests.
-SortIncludes: false
-ReflowComments: false
 
+# libc++'s preferred indentions of preprocessor statements.
 IndentPPDirectives: AfterHash
 
 # libc++ has some long names so we need more than the 80 column limit imposed by LLVM style, for sensible formatting

``````````

</details>


https://github.com/llvm/llvm-project/pull/208661

From libcxx-commits at lists.llvm.org  Fri Jul 10 02:49:58 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 10 Jul 2026 02:49:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a50c046.2490598a.cf17c.463e@mx.google.com>


================
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
----------------
Zingam wrote:

Thanks!

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Fri Jul 10 03:34:43 2026
From: libcxx-commits at lists.llvm.org (Tony Varghese via libcxx-commits)
Date: Fri, 10 Jul 2026 03:34:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a50cac3.ff037943.2e959a.8128@mx.google.com>


================
@@ -256,8 +256,16 @@ public:
 
   // 27.9.1.4 Members:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
----------------
tonykuttai wrote:

Isn't it better to add the platform specific warning to the function definition?

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Fri Jul 10 03:59:08 2026
From: libcxx-commits at lists.llvm.org (Alexander Kornienko via libcxx-commits)
Date: Fri, 10 Jul 2026 03:59:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix shared_ptr not accepting
 allocators with explicit conversions (PR #208439)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208439@github.com>
Message-ID: <6a50d07c.c56fb102.7c9cd.5190@mx.google.com>


https://github.com/alexfh approved this pull request.

Thanks for the fix! I verified that it fixes compilation of the original code.

https://github.com/llvm/llvm-project/pull/208439

From libcxx-commits at lists.llvm.org  Fri Jul 10 04:02:47 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 04:02:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix shared_ptr not accepting
 allocators with explicit conversions (PR #208439)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208439@github.com>
Message-ID: <6a50d157.9edb5c9d.208698.5160@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/208439

From libcxx-commits at lists.llvm.org  Fri Jul 10 04:41:39 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 10 Jul 2026 04:41:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Default the allocator argument
 for most string constructors (PR #169901)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/169901@github.com>
Message-ID: <6a50da73.d681cab9.333b2b.95ce@mx.google.com>


frederick-vs-ja wrote:

@bgra8 It seems to me that Clang and libc++ are doing right things.

[P4149R1](https://wg21.link/p4149r1) (applied as a Defect Report) clarifies that default arguments are not part of the immediate context, and [[basic.string.general]](https://eel.is/c++draft/basic.string.general) specifies that there should be a default argument. So as discussed in [P4149R1](https://wg21.link/p4149r1), there should be a hard error when detecting the value of `is_constructible_v`.

https://github.com/llvm/llvm-project/pull/169901

From libcxx-commits at lists.llvm.org  Fri Jul 10 05:19:20 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 05:19:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a50e348.f732ad81.3a4994.9a5a@mx.google.com>


philnik777 wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/containers/sequence/vector.bench.cpp

https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Fri Jul 10 05:25:44 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 10 Jul 2026 05:25:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::reverse_copy (PR #208085)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208085@github.com>
Message-ID: <6a50e4c8.743400b0.2067c9.927b@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/208085

From libcxx-commits at lists.llvm.org  Fri Jul 10 05:27:24 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 10 Jul 2026 05:27:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::reverse_copy (PR #208085)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208085@github.com>
Message-ID: <6a50e52c.d681cab9.333b2b.5d99@mx.google.com>


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/208085

>From d0733a0157e03c566fc6dedce3a340b3a74422ca Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 7 Jul 2026 20:15:09 +0100
Subject: [PATCH 1/3] Added a default implementation of parallel
 std::reverse_copy based on std::copy + std::reverse_iterator

---
 libcxx/include/__algorithm/pstl.h             |  17 +++
 libcxx/include/__pstl/backend_fwd.h           |   7 ++
 libcxx/include/__pstl/backends/default.h      |  17 +++
 .../pstl.iterator-requirements.verify.cpp     |  14 ++-
 .../algorithms/pstl.nodiscard.verify.cpp      |   2 +
 .../alg.reverse/pstl.reverse_copy.pass.cpp    | 116 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |   7 ++
 7 files changed, 178 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 10625ea3f8e3d..55654637df57f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -347,6 +347,23 @@ generate_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n, _Ge
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__n), std::move(__gen));
 }
 
+template <class _ExecutionPolicy,
+          class _BidirectionalIterator,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator reverse_copy(
+    _ExecutionPolicy&& __policy,
+    _BidirectionalIterator __first,
+    _BidirectionalIterator __last,
+    _ForwardIterator __result) {
+  _LIBCPP_REQUIRE_CPP17_BIDIRECTIONAL_ITERATOR(_BidirectionalIterator, "reverse_copy requires a BidirectionalIterator");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reverse_copy requires a ForwardIterator");
+  using _Implementation = __pstl::__dispatch<__pstl::__reverse_copy, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__result));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator,
           class _Predicate,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a52e6db954d0c..6b72350d43a10 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -159,6 +159,13 @@ struct __generate_n;
 // optional<__empty>
 // operator()(_Policy&&, _ForwardIterator __first, _Size __n, _Generator __gen) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __reverse_copy;
+// template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
+// optional<_ForwardIterator>
+// operator()(_Policy&&, _BidirectionalIterator __first, _BidirectionalIterator __last,
+//                       _ForwardIterator __result) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __merge;
 // template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Comp>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index be90715af13b5..6cbd3bcd0af64 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -21,6 +21,7 @@
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
+#include <__iterator/reverse_iterator.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
 #include <__utility/empty.h>
@@ -529,6 +530,22 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __reverse_copy<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+  operator()(_Policy&& __policy,
+             _BidirectionalIterator __first,
+             _BidirectionalIterator __last,
+             _ForwardIterator __result) const noexcept {
+    using _Copy = __dispatch<__copy, __current_configuration, _ExecutionPolicy>;
+    return _Copy()(__policy,
+                   std::reverse_iterator<_BidirectionalIterator>(std::move(__last)),
+                   std::reverse_iterator<_BidirectionalIterator>(std::move(__first)),
+                   std::move(__result));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e5bd7e764c59b..4aa435245e863 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -31,12 +31,16 @@
 
 #include "test_iterators.h"
 
-using non_forward_iterator = cpp17_input_iterator<int*>;
+using non_forward_iterator       = cpp17_input_iterator<int*>;
+using non_bidirectional_iterator = forward_iterator<int*>;
 struct non_output_iterator : forward_iterator<int*> {
   constexpr int const& operator*() const; // prevent it from being an output iterator
 };
 
-void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execution::sequenced_policy pol) {
+void f(non_forward_iterator non_fwd,
+       non_output_iterator non_output,
+       non_bidirectional_iterator non_bidir,
+       std::execution::sequenced_policy pol) {
   auto pred     = [](auto&&...) -> bool { return true; };
   auto func     = [](auto&&...) -> int { return 1; };
   int* it       = nullptr;
@@ -99,6 +103,12 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::generate_n(pol, non_fwd, n, func);     // expected-error@*:* {{static assertion failed: generate_n}}
   }
 
+  {
+    (void)std::reverse_copy(
+        pol, non_bidir, non_bidir, it);            // expected-error@*:* {{static assertion failed: reverse_copy}}
+    (void)std::reverse_copy(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: reverse_copy}}
+  }
+
   {
     (void)std::is_partitioned(
         pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: is_partitioned}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index b6554cef6f32a..38e85fb3b6b83 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -52,4 +52,6 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
new file mode 100644
index 0000000000000..b805fab2f1fb0
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class BidirectionalIterator,
+//           class ForwardIterator>
+//   ForwardIterator reverse_copy(ExecutionPolicy&& exec,
+//                                BidirectionalIterator first,
+//                                BidirectionalIterator last,
+//                                ForwardIterator result);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(reverse_copy);
+
+static_assert(sfinae_test_reverse_copy<int, int*, int*, int*>);
+static_assert(!sfinae_test_reverse_copy<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[]              = {0};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      assert(ib[0] == -1);
+      r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(ia[0] == 0);
+    }
+    {
+      int ia[]              = {0, 1};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 1);
+      assert(ib[1] == 0);
+    }
+    {
+      int ia[]              = {0, 1, 2};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 2);
+      assert(ib[1] == 1);
+      assert(ib[2] == 0);
+    }
+    {
+      int ia[]              = {0, 1, 2, 3};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 3);
+      assert(ib[1] == 2);
+      assert(ib[2] == 1);
+      assert(ib[3] == 0);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia) + 1, [&](size_t i) {
+        Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j) {
+          assert(ib[j] == static_cast<int>(i - j));
+        }
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::bidirectional_iterator_list<int*>, types::bidirectional_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+}
\ No newline at end of file
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a3db159289393..546862169813e 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -195,6 +195,13 @@ int main(int, char**) {
         assert_non_throwing([=, &policy] { (void)std::generate_n(policy, std::move(first1), n, gen); });
       }
 
+      {
+        // reverse_copy(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::reverse_copy(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+      }
+
       {
         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
 

>From 6632a8261585b97670183b719c2fbab3330f839e Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 8 Jul 2026 15:45:42 +0100
Subject: [PATCH 2/3] Added a missing line in the top comments

---
 libcxx/include/__pstl/backends/default.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 6cbd3bcd0af64..49ab69b497ddd 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -93,6 +93,7 @@ namespace __pstl {
 // - move
 // - copy
 // - copy_n
+// - reverse_copy
 // - rotate_copy
 //
 

>From 7b4f36a387e62786eb725cdf8ab1f3f2a2cb21b0 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 10 Jul 2026 20:27:14 +0800
Subject: [PATCH 3/3] Add a conventional ending newline

---
 .../alg.reverse/pstl.reverse_copy.pass.cpp                      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
index b805fab2f1fb0..d81dde163cf02 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
@@ -113,4 +113,4 @@ int main(int, char**) {
         types::for_each(types::forward_iterator_list<int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
       }});
-}
\ No newline at end of file
+}


From libcxx-commits at lists.llvm.org  Fri Jul 10 06:04:43 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 06:04:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix shared_ptr not accepting
 allocators with explicit conversions (PR #208439)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208439@github.com>
Message-ID: <6a50edeb.e55dc341.1f19bd.dbd1@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/208439

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:16:08 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 06:16:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a50f098.a87eb5dc.7d817.09c4@mx.google.com>


ldionne wrote:

The benchmark results seem to be overall good on macOS (mostly neutrals and a few improvements that don't seem to be in the noise), however they seem to be overall worse on Linux. I'd like to understand why that is.

For now, let's run the benchmarks again exactly the same way. If the above is in the noise, then we shouldn't see exactly the same benchmarks display a similar swing.

/libcxx-bot benchmark libcxx/test/benchmarks/containers/sequence/vector.bench.cpp

https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:16:12 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 06:16:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a50f09c.4987c784.63cbc.30f7@mx.google.com>


ldionne wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/containers/sequence/vector.bench.cpp

https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:16:15 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 06:16:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a50f09f.6c8668f2.e4f82.d910@mx.google.com>


ldionne wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/containers/sequence/vector.bench.cpp

https://github.com/llvm/llvm-project/pull/207384

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:19:14 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 06:19:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Use C++11 spelling of
	[[noreturn]] (PR #207344)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207344@github.com>
Message-ID: <6a50f152.743400b0.2067c9.c1ee@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/207344

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:20:45 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 06:20:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Add a clang-tidy
	config (PR #207324)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207324@github.com>
Message-ID: <6a50f1ad.f732ad81.3a4994.fefa@mx.google.com>


https://github.com/ldionne requested changes to this pull request.


https://github.com/llvm/llvm-project/pull/207324

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:20:45 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 06:20:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Add a clang-tidy
	config (PR #207324)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207324@github.com>
Message-ID: <6a50f1ad.743400b0.2067c9.ee40@mx.google.com>


================
@@ -0,0 +1,54 @@
+Checks: >
----------------
ldionne wrote:

Since we're eventually (at some point in the future) going to merge libc++abi into libc++, I think we should just run the libc++ clang tidy checks on libc++abi. Otherwise this patch raises the question of how/when these checks are going to be enforced, without which this file isn't useful.

https://github.com/llvm/llvm-project/pull/207324

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:31:07 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 06:31:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 62bf9d9 - [libc++] Fix shared_ptr not
 accepting allocators with explicit conversions (#208439)
Message-ID: <6a50f41b.92d2873c.258297.82c3@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-10T15:31:02+02:00
New Revision: 62bf9d924cb7b5b59bbacf42dfb80c6671671e86

URL: https://github.com/llvm/llvm-project/commit/62bf9d924cb7b5b59bbacf42dfb80c6671671e86
DIFF: https://github.com/llvm/llvm-project/commit/62bf9d924cb7b5b59bbacf42dfb80c6671671e86.diff

LOG: [libc++] Fix shared_ptr not accepting allocators with explicit conversions (#208439)

Allocators are only required to be explicitly convertible between
different types. `shared_ptr` currently requires implicit conversions,
however.

This is a regression introduced by #200401.

Added: 
    

Modified: 
    libcxx/include/__memory/shared_ptr.h
    libcxx/test/support/min_allocator.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index ebd542ba14d01..4112c1175b9e1 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -658,8 +658,9 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> __allocate_shared_impl(const _Alloc& __all
 
 template <class _Tp, class _Alloc, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, _Args&&... __args) {
-  using _ControlBlock = __shared_ptr_emplace<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp> > >;
-  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a, std::forward<_Args>(__args)...);
+  using _CanonicalAlloc = __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp> >;
+  using _ControlBlock   = __shared_ptr_emplace<_Tp, _CanonicalAlloc>;
+  return std::__allocate_shared_impl<_ControlBlock, _Tp>(_CanonicalAlloc(__a), std::forward<_Args>(__args)...);
 }
 
 template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
@@ -671,9 +672,9 @@ template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> =
 
 template <class _Tp, class _Alloc, __enable_if_t<!is_array<_Tp>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) {
-  using _ControlBlock =
-      __shared_ptr_emplace_for_overwrite<_Tp, __allocator_traits_rebind_t<_Alloc, __remove_cv_t<_Tp>>>;
-  return std::__allocate_shared_impl<_ControlBlock, _Tp>(__a);
+  using _CanonicalAlloc = __allocator_traits_rebind_t<_Alloc, remove_cv_t<_Tp>>;
+  using _ControlBlock   = __shared_ptr_emplace_for_overwrite<_Tp, _CanonicalAlloc>;
+  return std::__allocate_shared_impl<_ControlBlock, _Tp>(_CanonicalAlloc(__a));
 }
 
 template <class _Tp, __enable_if_t<!is_array<_Tp>::value, int> = 0>

diff  --git a/libcxx/test/support/min_allocator.h b/libcxx/test/support/min_allocator.h
index 6beffc1d2eb10..07603425f0668 100644
--- a/libcxx/test/support/min_allocator.h
+++ b/libcxx/test/support/min_allocator.h
@@ -29,7 +29,7 @@ class bare_allocator {
   bare_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  bare_allocator(bare_allocator<U>) TEST_NOEXCEPT {}
+  explicit bare_allocator(bare_allocator<U>) TEST_NOEXCEPT {}
 
   T* allocate(std::size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
 
@@ -59,7 +59,7 @@ class no_default_allocator {
   typedef T value_type;
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 no_default_allocator(no_default_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 explicit no_default_allocator(no_default_allocator<U>) TEST_NOEXCEPT {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return static_cast<T*>(std::allocator<T>().allocate(n)); }
 
@@ -102,7 +102,7 @@ class malloc_allocator : public malloc_allocator_base {
   malloc_allocator() TEST_NOEXCEPT { assert(!disable_default_constructor); }
 
   template <class U>
-  malloc_allocator(malloc_allocator<U>) TEST_NOEXCEPT {}
+  explicit malloc_allocator(malloc_allocator<U>) TEST_NOEXCEPT {}
 
   T* allocate(std::size_t n) {
     const std::size_t nbytes = n * sizeof(T);
@@ -410,7 +410,7 @@ class complete_type_allocator {
   TEST_CONSTEXPR_CXX20 complete_type_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 complete_type_allocator(complete_type_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 explicit complete_type_allocator(complete_type_allocator<U>) TEST_NOEXCEPT {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return static_cast<T*>(std::allocator<T>().allocate(n)); }
 
@@ -466,7 +466,7 @@ class safe_allocator {
   TEST_CONSTEXPR_CXX20 safe_allocator() TEST_NOEXCEPT {}
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 safe_allocator(safe_allocator<U>) TEST_NOEXCEPT {}
+  TEST_CONSTEXPR_CXX20 explicit safe_allocator(safe_allocator<U>) TEST_NOEXCEPT {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) {
     T* memory = std::allocator<T>().allocate(n);
@@ -499,7 +499,7 @@ struct tiny_size_allocator {
   tiny_size_allocator() = default;
 
   template <class U>
-  TEST_CONSTEXPR_CXX20 tiny_size_allocator(tiny_size_allocator<MaxSize, U>) {}
+  TEST_CONSTEXPR_CXX20 explicit tiny_size_allocator(tiny_size_allocator<MaxSize, U>) {}
 
   TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) {
     assert(n <= MaxSize);


        

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:31:10 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 06:31:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix shared_ptr not accepting
 allocators with explicit conversions (PR #208439)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208439@github.com>
Message-ID: <6a50f41e.07d7ca98.9dee3.8c95@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/208439

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:37:02 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 06:37:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Clean up windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a50f57e.449bfb44.172ed9.6603@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/207577

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:37:02 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 06:37:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Clean up windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a50f57e.af6ba3ed.36d9db.1091@mx.google.com>


https://github.com/ldionne commented:

I think this makes sense to me, with a few changes to the naming scheme.

I think @mstorsjo should definitely weigh in though.

https://github.com/llvm/llvm-project/pull/207577

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:37:03 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 06:37:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Clean up windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a50f57f.d1496011.26913c.96e3@mx.google.com>


================
@@ -43,14 +43,11 @@
 #  endif
 
 #  if defined(_WIN32)
-#    define _LIBCPP_WIN32API
 #    define _LIBCPP_SHORT_WCHAR 1
-// Both MinGW and native MSVC provide a "MSVC"-like environment
-#    define _LIBCPP_MSVCRT_LIKE
-// If mingw not explicitly detected, assume using MS C runtime only if
-// a MS compatibility version is specified.
-#    if defined(_MSC_VER) && !defined(__MINGW32__)
-#      define _LIBCPP_MSVCRT // Using Microsoft's C Runtime library
+#    if defined(__MINGW32__)
+#      define _LIBCPP_LIBC_MINGW
----------------
ldionne wrote:

We usually define libc macros at configuration time right now: https://github.com/llvm/llvm-project/blob/9a1fe8e03c0499990b820d0151eabe60da864c9a/libcxx/CMakeLists.txt#L821

I suspect it would make sense to push those to configuration time as well: we should avoid introducing inconsistencies here.

If we don't want to block this patch on making these configure-time, I would change the name not to be `_LIBCPP_LIBC_something`. And if `_LIBCPP_LIBC_MINGW` isn't used, let's not add it.

https://github.com/llvm/llvm-project/pull/207577

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:37:03 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 06:37:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Clean up windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a50f57f.4a342cbc.ca844.4e2b@mx.google.com>


================
@@ -43,14 +43,11 @@
 #  endif
 
 #  if defined(_WIN32)
-#    define _LIBCPP_WIN32API
 #    define _LIBCPP_SHORT_WCHAR 1
-// Both MinGW and native MSVC provide a "MSVC"-like environment
-#    define _LIBCPP_MSVCRT_LIKE
-// If mingw not explicitly detected, assume using MS C runtime only if
-// a MS compatibility version is specified.
-#    if defined(_MSC_VER) && !defined(__MINGW32__)
----------------
ldionne wrote:

This logic is changed here for MSVC, is that intended?

https://github.com/llvm/llvm-project/pull/207577

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:39:30 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 06:39:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 68ee7f0 - [libc++] Add benchmarks for
 std::is_heap and std::is_heap_until (#207045)
Message-ID: <6a50f612.13be6df8.145c.3cb8@mx.google.com>


Author: Louis Dionne
Date: 2026-07-10T09:39:25-04:00
New Revision: 68ee7f096e21837f3d025e5e4ac6bb761588a409

URL: https://github.com/llvm/llvm-project/commit/68ee7f096e21837f3d025e5e4ac6bb761588a409
DIFF: https://github.com/llvm/llvm-project/commit/68ee7f096e21837f3d025e5e4ac6bb761588a409.diff

LOG: [libc++] Add benchmarks for std::is_heap and std::is_heap_until (#207045)

This continues expanding our set of benchmarks.

Assisted by Claude

Added: 
    libcxx/test/benchmarks/algorithms/sorting/is_heap.bench.cpp

Modified: 
    

Removed: 
    


################################################################################
diff  --git a/libcxx/test/benchmarks/algorithms/sorting/is_heap.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/is_heap.bench.cpp
new file mode 100644
index 0000000000000..0d0e6324cf7e1
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/is_heap.bench.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_is_heap      = [](auto first, auto last) { return std::is_heap(first, last); };
+  auto std_is_heap_pred = [](auto first, auto last) {
+    return std::is_heap(first, last, [](auto x, auto y) { return x < y; });
+  };
+  auto std_is_heap_until      = [](auto first, auto last) { return std::is_heap_until(first, last); };
+  auto std_is_heap_until_pred = [](auto first, auto last) {
+    return std::is_heap_until(first, last, [](auto x, auto y) { return x < y; });
+  };
+
+  // Benchmark {std,ranges}::{is_heap,is_heap_until} on a valid heap. This is the worst case since the
+  // whole range must be scanned before we can conclude the heap invariant holds.
+  {
+    auto bm = []<class Container>(std::string name, auto is_heap) {
+      benchmark::RegisterBenchmark(
+          name,
+          [is_heap](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            std::vector<ValueType> data;
+            std::generate_n(std::back_inserter(data), size, [] { return Generate<ValueType>::random(); });
+            std::make_heap(data.begin(), data.end());
+
+            Container c(data.begin(), data.end());
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = is_heap(c.begin(), c.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::is_heap
+    bm.operator()<std::vector<int>>("std::is_heap(vector<int>)", std_is_heap);
+    bm.operator()<std::deque<int>>("std::is_heap(deque<int>)", std_is_heap);
+
+    bm.operator()<std::vector<int>>("std::is_heap(vector<int>, pred)", std_is_heap_pred);
+    bm.operator()<std::deque<int>>("std::is_heap(deque<int>, pred)", std_is_heap_pred);
+
+    // {std,ranges}::is_heap_until
+    bm.operator()<std::vector<int>>("std::is_heap_until(vector<int>)", std_is_heap_until);
+    bm.operator()<std::deque<int>>("std::is_heap_until(deque<int>)", std_is_heap_until);
+
+    bm.operator()<std::vector<int>>("std::is_heap_until(vector<int>, pred)", std_is_heap_until_pred);
+    bm.operator()<std::deque<int>>("std::is_heap_until(deque<int>, pred)", std_is_heap_until_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}


        

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:39:35 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 06:39:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for std::is_heap
 and std::is_heap_until (PR #207045)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207045@github.com>
Message-ID: <6a50f617.13be6df8.145c.3fc3@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/207045

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:47:13 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 06:47:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] d6ef81c - [libc++abi][NFC] Use C++11
 spelling of [[noreturn]] (#207344)
Message-ID: <6a50f7e1.a1accbb4.244acd.992e@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-10T15:47:09+02:00
New Revision: d6ef81ce4e18563c49116fc82502e4ef237a201d

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

LOG: [libc++abi][NFC] Use C++11 spelling of [[noreturn]] (#207344)

We already use the C++11 spelling for libc++, and there isn't much of a
reason not to use it. Except for the changes in `cxxabi.h` this is even
fully standard code.

Added: 
    

Modified: 
    libcxxabi/include/cxxabi.h
    libcxxabi/src/abort_message.h
    libcxxabi/src/cxa_aux_runtime.cpp
    libcxxabi/src/cxa_default_handlers.cpp
    libcxxabi/src/cxa_exception.cpp
    libcxxabi/src/cxa_handlers.cpp
    libcxxabi/src/cxa_handlers.h
    libcxxabi/src/cxa_personality.cpp
    libcxxabi/src/cxa_vector.cpp
    libcxxabi/src/cxa_virtual.cpp

Removed: 
    


################################################################################
diff  --git a/libcxxabi/include/cxxabi.h b/libcxxabi/include/cxxabi.h
index ff0d5d2aef47c..c65e353d68daf 100644
--- a/libcxxabi/include/cxxabi.h
+++ b/libcxxabi/include/cxxabi.h
@@ -20,7 +20,6 @@
 #include <__cxxabi_config.h>
 
 #define _LIBCPPABI_VERSION 230000
-#define _LIBCXXABI_NORETURN  __attribute__((noreturn))
 #define _LIBCXXABI_ALWAYS_COLD __attribute__((cold))
 
 #ifdef __cplusplus
@@ -56,7 +55,7 @@ __cxa_init_primary_exception(void* object, std::type_info* tinfo, void(_LIBCXXAB
 #endif
 
 // 2.4.3 Throwing the Exception Object
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void
 __cxa_throw(void *thrown_exception, std::type_info *tinfo,
 #ifdef __wasm__
             void *(_LIBCXXABI_DTOR_FUNC *dest)(void *));
@@ -80,22 +79,22 @@ extern _LIBCXXABI_FUNC_VIS std::type_info *__cxa_current_exception_type();
 // GNU extension
 // Calls `terminate` with the current exception being caught. This function is used by GCC when a `noexcept` function
 // throws an exception inside a try/catch block and doesn't catch it.
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_call_terminate(void*) _LIBCXXABI_NOEXCEPT;
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_call_terminate(void*) _LIBCXXABI_NOEXCEPT;
 
 // 2.5.4 Rethrowing Exceptions
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_rethrow();
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_rethrow();
 
 // 2.6 Auxiliary Runtime APIs
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void);
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void);
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_bad_cast(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_bad_typeid(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void
 __cxa_throw_bad_array_new_length(void);
 
 // 3.2.6 Pure Virtual Function API
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_pure_virtual(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_pure_virtual(void);
 
 // 3.2.7 Deleted Virtual Function API
-extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_deleted_virtual(void);
+[[__noreturn__]] extern _LIBCXXABI_FUNC_VIS void __cxa_deleted_virtual(void);
 
 // 3.3.2 One-time Construction API
 #if defined(_LIBCXXABI_GUARD_ABI_ARM)

diff  --git a/libcxxabi/src/abort_message.h b/libcxxabi/src/abort_message.h
index 2c12c429569d3..885dd38372791 100644
--- a/libcxxabi/src/abort_message.h
+++ b/libcxxabi/src/abort_message.h
@@ -11,7 +11,7 @@
 
 #include "cxxabi.h"
 
-extern "C" _LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN void
+extern "C" [[noreturn]] _LIBCXXABI_HIDDEN void
 __abort_message(const char *format, ...) __attribute__((format(printf, 1, 2)));
 
 #ifndef _LIBCXXABI_ASSERT

diff  --git a/libcxxabi/src/cxa_aux_runtime.cpp b/libcxxabi/src/cxa_aux_runtime.cpp
index 5e6040d75a262..e6bac221d8be8 100644
--- a/libcxxabi/src/cxa_aux_runtime.cpp
+++ b/libcxxabi/src/cxa_aux_runtime.cpp
@@ -16,7 +16,7 @@
 
 namespace __cxxabiv1 {
 extern "C" {
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void) {
+[[noreturn]] _LIBCXXABI_FUNC_VIS void __cxa_bad_cast(void) {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_cast();
 #else
@@ -24,7 +24,7 @@ _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_cast(void) {
 #endif
 }
 
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void) {
+[[noreturn]] _LIBCXXABI_FUNC_VIS void __cxa_bad_typeid(void) {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_typeid();
 #else
@@ -32,7 +32,7 @@ _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_bad_typeid(void) {
 #endif
 }
 
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void
+[[noreturn]] _LIBCXXABI_FUNC_VIS void
 __cxa_throw_bad_array_new_length(void) {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_array_new_length();

diff  --git a/libcxxabi/src/cxa_default_handlers.cpp b/libcxxabi/src/cxa_default_handlers.cpp
index 092e66692ecb1..5daf78b34ee54 100644
--- a/libcxxabi/src/cxa_default_handlers.cpp
+++ b/libcxxabi/src/cxa_default_handlers.cpp
@@ -24,8 +24,7 @@
 static constinit const char* cause = "uncaught";
 
 #  ifndef _LIBCXXABI_NO_EXCEPTIONS
-__attribute__((noreturn))
-static void demangling_terminate_handler()
+[[noreturn]] static void demangling_terminate_handler()
 {
     using namespace __cxxabiv1;
     __cxa_eh_globals* globals = __cxa_get_globals_fast();
@@ -78,16 +77,14 @@ static void demangling_terminate_handler()
     }
 }
 #else // !_LIBCXXABI_NO_EXCEPTIONS
-__attribute__((noreturn))
-static void demangling_terminate_handler()
+[[noreturn]] static void demangling_terminate_handler()
 {
     (void)cause;
     __abort_message("terminating");
 }
 #endif // !_LIBCXXABI_NO_EXCEPTIONS
 
-__attribute__((noreturn))
-static void demangling_unexpected_handler()
+[[noreturn]] static void demangling_unexpected_handler()
 {
     cause = "unexpected";
     std::terminate();

diff  --git a/libcxxabi/src/cxa_exception.cpp b/libcxxabi/src/cxa_exception.cpp
index 5d7edae697d67..f56ecc248eefb 100644
--- a/libcxxabi/src/cxa_exception.cpp
+++ b/libcxxabi/src/cxa_exception.cpp
@@ -140,7 +140,7 @@ exception_cleanup_func(_Unwind_Reason_Code reason, _Unwind_Exception* unwind_exc
     __cxa_decrement_exception_refcount(unwind_exception + 1);
 }
 
-static _LIBCXXABI_NORETURN void failed_throw(__cxa_exception* exception_header) {
+[[noreturn]] static void failed_throw(__cxa_exception* exception_header) {
 //  Section 2.5.3 says:
 //      * For purposes of this ABI, several things are considered exception handlers:
 //      ** A terminate() call due to a throw.

diff  --git a/libcxxabi/src/cxa_handlers.cpp b/libcxxabi/src/cxa_handlers.cpp
index f879ff0d8ff18..0abdc740486b8 100644
--- a/libcxxabi/src/cxa_handlers.cpp
+++ b/libcxxabi/src/cxa_handlers.cpp
@@ -36,8 +36,7 @@ __unexpected(unexpected_handler func)
     __abort_message("unexpected_handler unexpectedly returned");
 }
 
-__attribute__((noreturn))
-void
+[[noreturn]] void
 unexpected()
 {
     __unexpected(get_unexpected());
@@ -69,8 +68,7 @@ __terminate(terminate_handler func) noexcept
 #endif // _LIBCXXABI_NO_EXCEPTIONS
 }
 
-__attribute__((noreturn))
-void
+[[noreturn]] void
 terminate() noexcept
 {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS

diff  --git a/libcxxabi/src/cxa_handlers.h b/libcxxabi/src/cxa_handlers.h
index 3d8dc6b2de95e..072b85ed89f85 100644
--- a/libcxxabi/src/cxa_handlers.h
+++ b/libcxxabi/src/cxa_handlers.h
@@ -19,11 +19,11 @@
 namespace std
 {
 
-_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN
+[[noreturn]] _LIBCXXABI_HIDDEN
 void
 __unexpected(unexpected_handler func);
 
-_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN
+[[noreturn]] _LIBCXXABI_HIDDEN
 void
 __terminate(terminate_handler func) noexcept;
 

diff  --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index 6599f89740113..c5050e46c0e85 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1272,8 +1272,7 @@ __gxx_personality_v0(_Unwind_State state,
 #endif
 
 
-__attribute__((noreturn))
-_LIBCXXABI_FUNC_VIS void
+[[noreturn]] _LIBCXXABI_FUNC_VIS void
 __cxa_call_unexpected(void* arg)
 {
     _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(arg);

diff  --git a/libcxxabi/src/cxa_vector.cpp b/libcxxabi/src/cxa_vector.cpp
index 857ee27d065c3..5eac7a8dc9729 100644
--- a/libcxxabi/src/cxa_vector.cpp
+++ b/libcxxabi/src/cxa_vector.cpp
@@ -116,8 +116,7 @@ namespace {
 //
 
 namespace {
-_LIBCXXABI_NORETURN
-void throw_bad_array_new_length() {
+[[noreturn]] void throw_bad_array_new_length() {
 #ifndef _LIBCXXABI_NO_EXCEPTIONS
   throw std::bad_array_new_length();
 #else

diff  --git a/libcxxabi/src/cxa_virtual.cpp b/libcxxabi/src/cxa_virtual.cpp
index 8f4fdd0919f0e..1d3f4c63cf2de 100644
--- a/libcxxabi/src/cxa_virtual.cpp
+++ b/libcxxabi/src/cxa_virtual.cpp
@@ -11,12 +11,12 @@
 
 namespace __cxxabiv1 {
 extern "C" {
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN
+[[noreturn]] _LIBCXXABI_FUNC_VIS
 void __cxa_pure_virtual(void) {
   __abort_message("Pure virtual function called!");
 }
 
-_LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN
+[[noreturn]] _LIBCXXABI_FUNC_VIS
 void __cxa_deleted_virtual(void) {
   __abort_message("Deleted virtual function called!");
 }


        

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:47:16 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 06:47:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Use C++11 spelling of
	[[noreturn]] (PR #207344)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207344@github.com>
Message-ID: <6a50f7e4.f8b79514.1d52d0.2048@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/207344

From libcxx-commits at lists.llvm.org  Fri Jul 10 06:58:09 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Fri, 10 Jul 2026 06:58:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0493R5: Atomic
	minimum/maximum (PR #180333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/180333@github.com>
Message-ID: <6a50fa71.c574b9b4.21ead0.6fb5@mx.google.com>


c8ef wrote:

```diff
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 899502422570..c42041499d5e 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -261,9 +261,90 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
       std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
 }
 
-// Clang's __c11_atomic_fetch_max/min builtins do not accept pointer
-// arguments, so dispatch to a CAS loop for pointer types.
-// TODO: Use the builtin for pointer types once Clang accepts them.
+#if _LIBCPP_STD_VER >= 20
+
+// In C++20 and later, dispatch to the builtin if it supports the type,
+// otherwise fallback to a CAS loop (e.g. for pointer types on older Clang builds).
+template <class _Tp>
+concept __has_c11_atomic_minmax = requires(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(memory_order_relaxed));
+  __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(memory_order_relaxed));
+};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  if constexpr (__has_c11_atomic_minmax<_Tp>) {
+    return __c11_atomic_fetch_max(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  } else {
+    _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret > __val ? __ret : __val;
+    } while (
+        !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  }
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  if constexpr (__has_c11_atomic_minmax<_Tp>) {
+    return __c11_atomic_fetch_max(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  } else {
+    _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret > __val ? __ret : __val;
+    } while (
+        !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  }
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  if constexpr (__has_c11_atomic_minmax<_Tp>) {
+    return __c11_atomic_fetch_min(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  } else {
+    _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret < __val ? __ret : __val;
+    } while (
+        !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  }
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  if constexpr (__has_c11_atomic_minmax<_Tp>) {
+    return __c11_atomic_fetch_min(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  } else {
+    _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret < __val ? __ret : __val;
+    } while (
+        !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20
+
+// Clang's __c11_atomic_fetch_max/min builtins didn't originally accept pointer
+// arguments, so dispatch to a CAS loop for pointer types in older standards.
 template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
@@ -336,6 +417,8 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order
   return __ret;
 }
 
+#endif // _LIBCPP_STD_VER >= 20
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ATOMIC_SUPPORT_C11_H
```

I think if we go with the concept approach, simply applying the above diff to the current branch will suffice. @huixie90 @ldionne, could you please take a look and let me know if applying this patch is preferable to keeping the current branch as is? Really hoping to resolve this final blocker!

https://github.com/llvm/llvm-project/pull/180333

From libcxx-commits at lists.llvm.org  Fri Jul 10 07:21:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 07:21:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] Fix redefinition of wchar_t
 overloads on AIX (PR #201432)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201432@github.com>
Message-ID: <6a50ffff.e55dc341.1f19bd.9ae8@mx.google.com>


https://github.com/Himadhith converted_to_draft https://github.com/llvm/llvm-project/pull/201432

From libcxx-commits at lists.llvm.org  Fri Jul 10 07:31:04 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 07:31:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libc++abi][NFC] Add a
	clang-tidy config (PR #207324)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207324@github.com>
Message-ID: <6a510228.d76d81af.39f9be.aeb4@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207324

>From 147404d546d8b6e8df053826ef894cb74d17d7ad Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 07:19:18 +0200
Subject: [PATCH] [libc++abi][NFC] Add a clang-tidy config

---
 libcxx/test/libcxx/clang_tidy.sh.py |  5 +--
 libcxxabi/.clang-tidy               | 55 +++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+), 2 deletions(-)
 create mode 100644 libcxxabi/.clang-tidy

diff --git a/libcxx/test/libcxx/clang_tidy.sh.py b/libcxx/test/libcxx/clang_tidy.sh.py
index 630428564597d..71205866cacb8 100644
--- a/libcxx/test/libcxx/clang_tidy.sh.py
+++ b/libcxx/test/libcxx/clang_tidy.sh.py
@@ -11,5 +11,6 @@
 # RUN: %{python} %{libcxx-dir}/../clang-tools-extra/clang-tidy/tool/run-clang-tidy.py   \
 # RUN:      -clang-tidy-binary %{clang-tidy}                                            \
 # RUN:      -warnings-as-errors "*"                                                     \
-# RUN:      -source-filter=".*libcxx/src.*"                                             \
-# RUN:      -quiet -p %{bin-dir}/..
+# RUN:      -source-filter=".*libcxx(|abi)/src.*"                                       \
+# RUN:      -quiet -p %{bin-dir}/..                                                     \
+# RUN:      -header-filter ^$
diff --git a/libcxxabi/.clang-tidy b/libcxxabi/.clang-tidy
new file mode 100644
index 0000000000000..159436c442b36
--- /dev/null
+++ b/libcxxabi/.clang-tidy
@@ -0,0 +1,55 @@
+Checks: >
+  bugprone-copy-constructor-init,
+  bugprone-dangling-handle,
+  bugprone-infinite-loop,
+  bugprone-stringview-nullptr,
+  bugprone-use-after-move,
+
+  misc-definitions-in-headers,
+  misc-misplaced-const,
+  misc-non-copyable-objects,
+  misc-uniqueptr-reset-release,
+
+  modernize-loop-convert,
+  modernize-redundant-void-arg,
+  modernize-use-override,
+
+  readability-duplicate-include,
+  readability-function-cognitive-complexity,
+  readability-function-size,
+  readability-misplaced-array-index,
+  readability-redundant-control-flow,
+  readability-redundant-function-ptr-dereference,
+  readability-redundant-preprocessor,
+  readability-simplify-boolean-expr,
+  readability-simplify-subscript-expr,
+  readability-uniqueptr-delete-release,
+
+CheckOptions:
+  - key:   readability-function-cognitive-complexity.Threshold
+    value: 143 # TODO: bring that number down
+  - key:   readability-function-size.LineThreshold
+    value: 268 # TODO: bring that number down
+  - key:   readability-identifier-naming.GetConfigPerFile
+    value: false
+  - key:   readability-identifier-naming.ParameterCase
+    value: lower_case
+  - key:   readability-identifier-naming.PrivateMemberCase
+    value: lower_case
+  - key:   readability-identifier-naming.PrivateMemberSuffix
+    value: _
+  - key:   readability-identifier-naming.LocalVariableCase
+    value: lower_case
+  - key:   readability-identifier-naming.TemplateParameterCase
+    value: CamelCase
+  - key:   readability-identifier-naming.ValueTemplateParameterCase
+    value: CamelCase
+  - key:   readability-identifier-naming.ValueTemplateParameterIgnoredRegexp
+    value: (__[a-z_]|_[A-Z]).* # TODO: Converge on a single style for value template parameters
+
+# TODO: Enable these checks
+# modernize-use-equals-delete,
+# modernize-use-nullptr,
+# readability-identifier-naming,
+# llvm-include-order,
+# llvm-namespace-comment,


From libcxx-commits at lists.llvm.org  Fri Jul 10 07:31:15 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 07:31:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libc++abi][NFC] Add a
	clang-tidy config (PR #207324)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207324@github.com>
Message-ID: <6a510233.510f576d.1b3cb9.2615@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/207324

From libcxx-commits at lists.llvm.org  Fri Jul 10 07:46:01 2026
From: libcxx-commits at lists.llvm.org (Liza Burakova via libcxx-commits)
Date: Fri, 10 Jul 2026 07:46:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add missing include to thread.h
	(PR #208740)
Message-ID: <llvm.org/llvm/llvm-project/pull/208740@github.com>


https://github.com/liza371 created https://github.com/llvm/llvm-project/pull/208740

Change #195509 removed unused transitive includes, but missed adding
<__type_traits/is_integral.h> to thread.h. This caused errors
to Chromium rolls for libc++ as they caught the missing import.


>From 5dc95674e02ca664f94e26cd27a08333b9ef5a68 Mon Sep 17 00:00:00 2001
From: Liza Burakova <liza at chromium.org>
Date: Fri, 10 Jul 2026 14:42:15 +0000
Subject: [PATCH] [libc++] Add missing include to thread.h

Change #195509 removed unused transitive includes, but missed adding
<__type_traits/is_integral.h> to thread.h. This caused errors
to Chromium rolls for libc++ as they caught the missing import.
---
 libcxx/include/__thread/thread.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__thread/thread.h b/libcxx/include/__thread/thread.h
index 24f62bfe5899e..4fd51c12a6dc2 100644
--- a/libcxx/include/__thread/thread.h
+++ b/libcxx/include/__thread/thread.h
@@ -28,6 +28,7 @@
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_constructible.h>
+#include <__type_traits/is_integral.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/remove_cvref.h>


From libcxx-commits at lists.llvm.org  Fri Jul 10 07:46:20 2026
From: libcxx-commits at lists.llvm.org (Liza Burakova via libcxx-commits)
Date: Fri, 10 Jul 2026 07:46:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add missing include to thread.h
	(PR #208740)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208740@github.com>
Message-ID: <6a5105bc.7e1b0c51.30d61d.0e76@mx.google.com>


https://github.com/liza371 updated https://github.com/llvm/llvm-project/pull/208740

>From 5dc95674e02ca664f94e26cd27a08333b9ef5a68 Mon Sep 17 00:00:00 2001
From: Liza Burakova <liza at chromium.org>
Date: Fri, 10 Jul 2026 14:42:15 +0000
Subject: [PATCH] [libc++] Add missing include to thread.h

Change #195509 removed unused transitive includes, but missed adding
<__type_traits/is_integral.h> to thread.h. This caused errors
to Chromium rolls for libc++ as they caught the missing import.
---
 libcxx/include/__thread/thread.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__thread/thread.h b/libcxx/include/__thread/thread.h
index 24f62bfe5899e..4fd51c12a6dc2 100644
--- a/libcxx/include/__thread/thread.h
+++ b/libcxx/include/__thread/thread.h
@@ -28,6 +28,7 @@
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_constructible.h>
+#include <__type_traits/is_integral.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/remove_cvref.h>


From libcxx-commits at lists.llvm.org  Fri Jul 10 07:46:43 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 07:46:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add missing include to thread.h
	(PR #208740)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208740@github.com>
Message-ID: <6a5105d3.2612ab8a.81286.b5da@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Liza Burakova (liza371)

<details>
<summary>Changes</summary>

Change #<!-- -->195509 removed unused transitive includes, but missed adding
&lt;__type_traits/is_integral.h&gt; to thread.h. This caused errors
to Chromium rolls for libc++ as they caught the missing import.


---
Full diff: https://github.com/llvm/llvm-project/pull/208740.diff


1 Files Affected:

- (modified) libcxx/include/__thread/thread.h (+1) 


``````````diff
diff --git a/libcxx/include/__thread/thread.h b/libcxx/include/__thread/thread.h
index 24f62bfe5899e..4fd51c12a6dc2 100644
--- a/libcxx/include/__thread/thread.h
+++ b/libcxx/include/__thread/thread.h
@@ -28,6 +28,7 @@
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_constructible.h>
+#include <__type_traits/is_integral.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/remove_cvref.h>

``````````

</details>


https://github.com/llvm/llvm-project/pull/208740

From libcxx-commits at lists.llvm.org  Fri Jul 10 07:51:46 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 10 Jul 2026 07:51:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add missing include to thread.h
	(PR #208740)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208740@github.com>
Message-ID: <6a510702.7e1b0c51.30d61d.3e96@mx.google.com>


https://github.com/philnik777 requested changes to this pull request.

`is_integral` isn't used inside `thread.h` AFAICT.

https://github.com/llvm/llvm-project/pull/208740

From libcxx-commits at lists.llvm.org  Fri Jul 10 07:59:46 2026
From: libcxx-commits at lists.llvm.org (Liza Burakova via libcxx-commits)
Date: Fri, 10 Jul 2026 07:59:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add missing include to thread.h
	(PR #208740)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208740@github.com>
Message-ID: <6a5108e2.6c5720b3.128ed.903b@mx.google.com>


liza371 wrote:

I believe it is used, there's a static assert in line 151 https://github.com/llvm/llvm-project/blob/main/libcxx/include/__thread/thread.h#L151, and the exact error I saw on chromium rolls was

../../third_party/libc++/src/include/__thread/thread.h:151:58: error: declaration of 'is_integral' must be imported from module '//build/modules:system.std_core.type_traits.is_integral' before it is required
  151 |   static_assert(is_pointer<__libcpp_thread_id>::value || is_integral<__libcpp_thread_id>::value);
      |                                                          ^
../../third_party/libc++/src/include/__type_traits/is_integral.h:25:35: note: declaration here is not visible
   25 | struct _LIBCPP_NO_SPECIALIZATIONS is_integral : _BoolConstant<__is_integral(_Tp)> {};
      |   

https://github.com/llvm/llvm-project/pull/208740

From libcxx-commits at lists.llvm.org  Fri Jul 10 09:11:27 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Fri, 10 Jul 2026 09:11:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update the base image hash to
 get most recently clang-23 (PR #208766)
Message-ID: <llvm.org/llvm/llvm-project/pull/208766@github.com>


https://github.com/yronglin created https://github.com/llvm/llvm-project/pull/208766

None

>From d1d4f5ce9d2507a4d6ca856c2e632c5f06d1a8b2 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Fri, 10 Jul 2026 09:10:57 -0700
Subject: [PATCH] [libc++] Update the base image hash to get most recently
 clang-23

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/utils/ci/docker/linux-builder-base.dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/utils/ci/docker/linux-builder-base.dockerfile b/libcxx/utils/ci/docker/linux-builder-base.dockerfile
index 574bb4ae25f4a..08306f84b1033 100644
--- a/libcxx/utils/ci/docker/linux-builder-base.dockerfile
+++ b/libcxx/utils/ci/docker/linux-builder-base.dockerfile
@@ -21,7 +21,7 @@ FROM docker.io/library/ubuntu:noble
 # Changing this file causes a rebuild of the image in a GitHub action. However, it does not cause
 # the CI runners to switch to that image automatically, that must be done by updating the image used
 # by the libc++ self-hosted runners in llvm-zorg. The date uses the ISO format YYYY-MM-DD.
-RUN echo "Last forced update executed on 2026-01-07."
+RUN echo "Last forced update executed on 2026-07-11."
 
 # Make sure apt-get doesn't try to prompt for stuff like our time zone, etc.
 ENV DEBIAN_FRONTEND=noninteractive


From libcxx-commits at lists.llvm.org  Fri Jul 10 09:12:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 09:12:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update the base image hash to
 get most recently clang-23 (PR #208766)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208766@github.com>
Message-ID: <6a5119da.970836ae.1d81a4.428c@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Yihan Wang (yronglin)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/208766.diff


1 Files Affected:

- (modified) libcxx/utils/ci/docker/linux-builder-base.dockerfile (+1-1) 


``````````diff
diff --git a/libcxx/utils/ci/docker/linux-builder-base.dockerfile b/libcxx/utils/ci/docker/linux-builder-base.dockerfile
index 574bb4ae25f4a..08306f84b1033 100644
--- a/libcxx/utils/ci/docker/linux-builder-base.dockerfile
+++ b/libcxx/utils/ci/docker/linux-builder-base.dockerfile
@@ -21,7 +21,7 @@ FROM docker.io/library/ubuntu:noble
 # Changing this file causes a rebuild of the image in a GitHub action. However, it does not cause
 # the CI runners to switch to that image automatically, that must be done by updating the image used
 # by the libc++ self-hosted runners in llvm-zorg. The date uses the ISO format YYYY-MM-DD.
-RUN echo "Last forced update executed on 2026-01-07."
+RUN echo "Last forced update executed on 2026-07-11."
 
 # Make sure apt-get doesn't try to prompt for stuff like our time zone, etc.
 ENV DEBIAN_FRONTEND=noninteractive

``````````

</details>


https://github.com/llvm/llvm-project/pull/208766

From libcxx-commits at lists.llvm.org  Fri Jul 10 09:30:24 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 09:30:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update the base image hash to
 get most recent clang-23 (PR #208766)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208766@github.com>
Message-ID: <6a511e20.510f576d.1b3cb9.3972@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/208766

From libcxx-commits at lists.llvm.org  Fri Jul 10 09:30:32 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 09:30:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update the base image hash to
 get most recent clang-23 (PR #208766)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208766@github.com>
Message-ID: <6a511e28.07d7ca98.9dee3.e01e@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/208766

From libcxx-commits at lists.llvm.org  Fri Jul 10 09:31:03 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 09:31:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 8ece73c - [libc++] Update the base image
 hash to get most recent clang-23 (#208766)
Message-ID: <6a511e47.e55dc341.1f19bd.43ef@mx.google.com>


Author: Yihan Wang
Date: 2026-07-10T12:30:58-04:00
New Revision: 8ece73c9f8b54fea6a98e9e4c55f9a2e6ccc9b3c

URL: https://github.com/llvm/llvm-project/commit/8ece73c9f8b54fea6a98e9e4c55f9a2e6ccc9b3c
DIFF: https://github.com/llvm/llvm-project/commit/8ece73c9f8b54fea6a98e9e4c55f9a2e6ccc9b3c.diff

LOG: [libc++] Update the base image hash to get most recent clang-23 (#208766)

Added: 
    

Modified: 
    libcxx/utils/ci/docker/linux-builder-base.dockerfile

Removed: 
    


################################################################################
diff  --git a/libcxx/utils/ci/docker/linux-builder-base.dockerfile b/libcxx/utils/ci/docker/linux-builder-base.dockerfile
index 574bb4ae25f4a..08306f84b1033 100644
--- a/libcxx/utils/ci/docker/linux-builder-base.dockerfile
+++ b/libcxx/utils/ci/docker/linux-builder-base.dockerfile
@@ -21,7 +21,7 @@ FROM docker.io/library/ubuntu:noble
 # Changing this file causes a rebuild of the image in a GitHub action. However, it does not cause
 # the CI runners to switch to that image automatically, that must be done by updating the image used
 # by the libc++ self-hosted runners in llvm-zorg. The date uses the ISO format YYYY-MM-DD.
-RUN echo "Last forced update executed on 2026-01-07."
+RUN echo "Last forced update executed on 2026-07-11."
 
 # Make sure apt-get doesn't try to prompt for stuff like our time zone, etc.
 ENV DEBIAN_FRONTEND=noninteractive


        

From libcxx-commits at lists.llvm.org  Fri Jul 10 09:31:05 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 09:31:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update the base image hash to
 get most recent clang-23 (PR #208766)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208766@github.com>
Message-ID: <6a511e49.c574b9b4.21ead0.3255@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208766

From libcxx-commits at lists.llvm.org  Fri Jul 10 09:37:34 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 09:37:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a511fce.f1c1923d.991d1.cd20@mx.google.com>


https://github.com/Himadhith updated https://github.com/llvm/llvm-project/pull/206498

>From eac8181d96f0ed0ff110d7cf7602551199913a6b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 18:53:17 +0530
Subject: [PATCH 01/12] AIX fopen() does not support the 'x'
 (exclusive/noreplace) mode suffix. Omit the noreplace mode strings in
 __make_mdstring() on AIX, causing open() to return nullptr for these modes.
 Update the test to expect failure on AIX for all noreplace cases, since the
 platform cannot provide atomic exclusive-open semantics.

---
 libcxx/include/fstream                        | 10 ++++++++
 .../filebuf.members/open_pointer.pass.cpp     | 24 +++++++++++++++----
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..f979db89db794 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -664,6 +664,15 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
+// cases are omitted on AIX and fall through to default (return nullptr), signalling
+// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
+// by proxy, requires that if the implementation cannot atomically check for the
+// existence of the file and create it, it shall fail rather than perform a
+// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
+// semantics via fopen(), returning nullptr is the correct behaviour.
+// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -674,6 +683,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
+#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..f7ab0239a1b18 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -75,7 +73,10 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -83,7 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 
@@ -94,7 +101,10 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -102,7 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
+#if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#else
             assert(f.is_open()); // since it doesn't exist
+#endif
           }
         }
 #  endif

>From 9c8dfa79291faa95c5a036e5d6b2ac43d199aee1 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 29 Jun 2026 12:40:23 -0400
Subject: [PATCH 02/12] fix format and add warning

---
 libcxx/include/fstream                               |  3 +++
 .../fstreams/filebuf.members/open_pointer.pass.cpp   | 12 ++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index f979db89db794..b71baabff2524 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -672,6 +672,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
+#      if defined(_AIX) || defined(__TOS_AIX__)
+#        warning 'exclusive mode is unsupported on this target'
+#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index f7ab0239a1b18..627a65cac3f2f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -84,13 +84,13 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#  if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#  else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#  endif
           }
         }
 
@@ -112,13 +112,13 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#if defined(_AIX) || defined(__TOS_AIX__)
+#    if defined(_AIX) || defined(__TOS_AIX__)
             // AIX fopen() does not support the 'x' (exclusive) mode suffix;
             // open() returns nullptr for all noreplace modes regardless of whether the file exists.
             assert(!f.is_open());
-#else
+#    else
             assert(f.is_open()); // since it doesn't exist
-#endif
+#    endif
           }
         }
 #  endif

>From d324a675f05f851656c4fb10ac6eaf5120ebb08e Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 12:52:06 -0400
Subject: [PATCH 03/12] fix warnings treated as errors

---
 libcxx/include/fstream | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index b71baabff2524..91259a39f4000 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -673,7 +673,10 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
 #      if defined(_AIX) || defined(__TOS_AIX__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic warning "-Wcpp"
 #        warning 'exclusive mode is unsupported on this target'
+#        pragma GCC diagnostic pop
 #      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:

>From 450f6694bddb62f12cc9a666b3c1c300a90f725c Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 1 Jul 2026 13:49:31 -0400
Subject: [PATCH 04/12] add warning as fprintf

---
 libcxx/include/fstream | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 91259a39f4000..92baa4670b363 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -672,12 +672,6 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
 // _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if defined(_AIX) || defined(__TOS_AIX__)
-#        pragma GCC diagnostic push
-#        pragma GCC diagnostic warning "-Wcpp"
-#        warning 'exclusive mode is unsupported on this target'
-#        pragma GCC diagnostic pop
-#      endif
 #      if !defined(_AIX) && !defined(__TOS_AIX__)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -780,9 +774,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr)
+  if (!__mdstr){
+#     if defined(_AIX) || defined(__TOS_AIX__)
+    if (__mode & ios_base::noreplace)
+      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
+#     endif
     return nullptr;
-
+}
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From eb0c8a9711dd9ed86e21ec217adb49f8cc7e3ec3 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:30:10 +0530
Subject: [PATCH 05/12] fix formatting

---
 libcxx/include/fstream | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 92baa4670b363..216082cfb8283 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -774,13 +774,13 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr){
-#     if defined(_AIX) || defined(__TOS_AIX__)
+  if (!__mdstr) {
+#      if defined(_AIX) || defined(__TOS_AIX__)
     if (__mode & ios_base::noreplace)
       fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#     endif
+#      endif
     return nullptr;
-}
+  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif

>From 9f7f1e7bc05bf06350f2846f8f736366133b1b8b Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 2 Jul 2026 19:37:48 +0530
Subject: [PATCH 06/12] add the remaining test files

---
 .../fstreams/fstream.cons/pointer.pass.cpp    | 24 +++++++++++++++---
 .../fstream.members/open_pointer.pass.cpp     | 24 +++++++++++++++---
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 24 +++++++++++++++---
 .../ofstream.members/open_pointer.pass.cpp    | 25 ++++++++++++++++---
 4 files changed, 81 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..a677b16e76479 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
@@ -91,14 +89,23 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -108,14 +115,23 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..92c0cf1aa0c00 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -74,7 +72,10 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -82,7 +83,13 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -93,7 +100,10 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -101,7 +111,13 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..4fae51f38adbd 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
@@ -145,14 +143,23 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -162,14 +169,23 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..493af1bda9c98 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
@@ -82,7 +80,10 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
 
           {
@@ -90,7 +91,13 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
+#  if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#  else
             assert(f.is_open()); // since it doesn't exist
+#  endif
           }
         }
 
@@ -101,14 +108,24 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            assert(!f.is_open()); // since it already exists
+            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
+            // returns nullptr for all noreplace modes. On other platforms, the file already
+            // exists so noreplace causes open() to fail.
+            assert(!f.is_open());
           }
+
           {
             std::remove(tmp.c_str());
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
+#    if defined(_AIX) || defined(__TOS_AIX__)
+            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
+            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+            assert(!f.is_open());
+#    else
             assert(f.is_open()); // since it doesn't exist
+#    endif
           }
         }
 #  endif

>From 150ffaacc7f92197986a67f317374c030a7e78cf Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Mon, 6 Jul 2026 12:15:37 +0530
Subject: [PATCH 07/12] review changes

---
 libcxx/include/fstream                        | 21 +++++++++++--------
 .../filebuf.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/fstream.cons/pointer.pass.cpp    | 18 ++++------------
 .../fstream.members/open_pointer.pass.cpp     | 18 ++++------------
 .../fstreams/ofstream.cons/pointer.pass.cpp   | 18 ++++------------
 .../ofstream.members/open_pointer.pass.cpp    | 18 ++++------------
 6 files changed, 32 insertions(+), 79 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 216082cfb8283..f37522db70b1d 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -258,6 +258,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -671,8 +673,8 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
 // existence of the file and create it, it shall fail rather than perform a
 // non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
 // semantics via fopen(), returning nullptr is the correct behaviour.
-// _AIX is defined by clang on AIX; __TOS_AIX__ is defined by both clang and XLC.
-#      if !defined(_AIX) && !defined(__TOS_AIX__)
+// _AIX is defined on AIX by all supported compilers.
+#      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
     return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
@@ -683,7 +685,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
     return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
-#      endif // !defined(_AIX) && !defined(__TOS_AIX__)
+#      endif // !defined(_AIX)
 #    endif // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
@@ -774,13 +776,8 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (__file_)
     return nullptr;
   const wchar_t* __mdstr = __make_mdwstring(__mode);
-  if (!__mdstr) {
-#      if defined(_AIX) || defined(__TOS_AIX__)
-    if (__mode & ios_base::noreplace)
-      fprintf(stderr, "warning: fstream::open() with noreplace is not supported on AIX; returning failure\n");
-#      endif
+  if (!__mdstr)
     return nullptr;
-  }
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1222,6 +1219,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1380,6 +1379,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1544,6 +1545,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 627a65cac3f2f..37cfd985d1c11 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -73,10 +73,7 @@ int main(int, char**)
           {
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -84,9 +81,7 @@ int main(int, char**)
 
             std::filebuf f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -101,10 +96,7 @@ int main(int, char**)
           {
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -112,9 +104,7 @@ int main(int, char**)
 
             std::wfilebuf f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index a677b16e76479..2a297ca87903e 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -89,19 +89,14 @@ int main(int, char**)
 
           {
             std::fstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::fstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -115,19 +110,14 @@ int main(int, char**)
 
           {
             std::wfstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wfstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 92c0cf1aa0c00..9178788b60354 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -72,10 +72,7 @@ int main(int, char**)
           {
             std::fstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -83,9 +80,7 @@ int main(int, char**)
 
             std::fstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -100,10 +95,7 @@ int main(int, char**)
           {
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -111,9 +103,7 @@ int main(int, char**)
 
             std::wfstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index 4fae51f38adbd..31a944236d1d0 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -143,19 +143,14 @@ int main(int, char**)
 
           {
             std::ofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::ofstream f(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -169,19 +164,14 @@ int main(int, char**)
 
           {
             std::wofstream f(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
             std::remove(tmp.c_str());
 
             std::wofstream f(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 493af1bda9c98..0b77f012b345d 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -80,10 +80,7 @@ int main(int, char**)
           {
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -91,9 +88,7 @@ int main(int, char**)
 
             std::ofstream f;
             f.open(tmp.c_str(), mode);
-#  if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#  if defined(_AIX)
             assert(!f.is_open());
 #  else
             assert(f.is_open()); // since it doesn't exist
@@ -108,10 +103,7 @@ int main(int, char**)
           {
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-            // On AIX, fopen() does not support the 'x' (exclusive) mode suffix so open()
-            // returns nullptr for all noreplace modes. On other platforms, the file already
-            // exists so noreplace causes open() to fail.
-            assert(!f.is_open());
+            assert(!f.is_open()); // since it already exists
           }
 
           {
@@ -119,9 +111,7 @@ int main(int, char**)
 
             std::wofstream f;
             f.open(tmp.c_str(), mode);
-#    if defined(_AIX) || defined(__TOS_AIX__)
-            // AIX fopen() does not support the 'x' (exclusive) mode suffix;
-            // open() returns nullptr for all noreplace modes regardless of whether the file exists.
+#    if defined(_AIX)
             assert(!f.is_open());
 #    else
             assert(f.is_open()); // since it doesn't exist

>From 956eafcbab954425ae5f1e0864d1499b183c0734 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 8 Jul 2026 11:03:55 +0530
Subject: [PATCH 08/12] add warnings for char* overloads

---
 libcxx/include/fstream                        | 45 +++++++++++++------
 .../fstreams/diagnose_noreplace.verify.cpp    | 27 +++++++++++
 .../ofstream.members/open_pointer.pass.cpp    |  1 -
 3 files changed, 59 insertions(+), 14 deletions(-)
 create mode 100644 libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index f37522db70b1d..4de91b1fb232b 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -256,10 +256,16 @@ public:
 
   // 27.9.1.4 Members:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
   basic_filebuf* open(const char* __s, ios_base::openmode __mode);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+#      if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
@@ -666,14 +672,8 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
-// AIX fopen() does not support the 'x' (exclusive/noreplace) mode suffix, so these
-// cases are omitted on AIX and fall through to default (return nullptr), signalling
-// failure to the caller. This is conformant: C23 (7.21.5.3p5), which C++ relies on
-// by proxy, requires that if the implementation cannot atomically check for the
-// existence of the file and create it, it shall fail rather than perform a
-// non-atomic check and creation. Since AIX cannot provide atomic exclusive-open
-// semantics via fopen(), returning nullptr is the correct behaviour.
-// _AIX is defined on AIX by all supported compilers.
+// AIX fopen() does not support the 'x' (noreplace) mode suffix; these cases fall through to
+// default (return nullptr), which is conformant per C23 7.21.5.3p5 (fail if atomic check-and-create is unsupported).
 #      if !defined(_AIX)
   case ios_base::out | ios_base::noreplace:
   case ios_base::out | ios_base::trunc | ios_base::noreplace:
@@ -686,7 +686,7 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
     return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #      endif // !defined(_AIX)
-#    endif // _LIBCPP_STD_VER >= 23
+#    endif   // _LIBCPP_STD_VER >= 23
   default:
     return nullptr;
   }
@@ -778,6 +778,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   const wchar_t* __mdstr = __make_mdwstring(__mode);
   if (!__mdstr)
     return nullptr;
+
   return __do_open(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
@@ -1217,10 +1218,16 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
   void open(const char* __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+#      if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
@@ -1377,10 +1384,16 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
   void open(const char* __s, ios_base::openmode __mode = ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+#      if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
@@ -1543,10 +1556,16 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
   _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  _LIBCPP_DIAGNOSE_WARNING((__mode & ios_base::noreplace) && defined(_AIX),
+#      if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                            "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
diff --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp
new file mode 100644
index 0000000000000..0a1d484dcd41d
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// AIX fopen() does not support the 'x' mode.
+
+// REQUIRES: aix
+
+#include <fstream>
+
+void test() {
+  std::filebuf fb;
+  fb.open("f", std::ios_base::out | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+
+  std::ifstream ifs;
+  ifs.open("f", std::ios_base::in | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+
+  std::ofstream ofs;
+  ofs.open("f", std::ios_base::out | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+
+  std::fstream fs;
+  fs.open("f", std::ios_base::out | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+}
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 0b77f012b345d..834a4c4d9cde8 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -105,7 +105,6 @@ int main(int, char**)
             f.open(tmp.c_str(), mode);
             assert(!f.is_open()); // since it already exists
           }
-
           {
             std::remove(tmp.c_str());
 

>From 24f802b71b4794c946250d96a7365e47e53af56c Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 9 Jul 2026 10:39:29 +0530
Subject: [PATCH 09/12] change requires to expect target aix

---
 .../fstreams/diagnose_noreplace.verify.cpp    | 26 +++++++++++++++----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp
index 0a1d484dcd41d..4842176f572c4 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/diagnose_noreplace.verify.cpp
@@ -8,20 +8,36 @@
 
 // AIX fopen() does not support the 'x' mode.
 
-// REQUIRES: aix
+// REQUIRES: target={{.+}}-aix{{.*}}
 
 #include <fstream>
 
 void test() {
   std::filebuf fb;
-  fb.open("f", std::ios_base::out | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+  fb.open(
+      "f",
+      std::ios_base::out |
+          std::ios_base::
+              noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
 
   std::ifstream ifs;
-  ifs.open("f", std::ios_base::in | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+  ifs.open(
+      "f",
+      std::ios_base::in |
+          std::ios_base::
+              noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
 
   std::ofstream ofs;
-  ofs.open("f", std::ios_base::out | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+  ofs.open(
+      "f",
+      std::ios_base::out |
+          std::ios_base::
+              noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
 
   std::fstream fs;
-  fs.open("f", std::ios_base::out | std::ios_base::noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
+  fs.open(
+      "f",
+      std::ios_base::out |
+          std::ios_base::
+              noreplace); // expected-warning {{fstream::open() with noreplace is not supported on AIX; open() will return failure}}
 }

>From d04e5fcee924806e718f875fc24bb70a1293e430 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 9 Jul 2026 22:10:49 +0530
Subject: [PATCH 10/12] make _LIBCPP_DIAGNOSE_WARNING trailing on all open()
 overloads

---
 libcxx/include/fstream | 56 ++++++++++++++++++++++++------------------
 1 file changed, 32 insertions(+), 24 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 4de91b1fb232b..c90b9c2772177 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -256,17 +256,19 @@ public:
 
   // 27.9.1.4 Members:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+  basic_filebuf* open(const char* __s, ios_base::openmode __mode)
 #    if defined(_AIX)
-  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
-                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #    endif
-  basic_filebuf* open(const char* __s, ios_base::openmode __mode);
+      ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode)
 #      if defined(_AIX)
-  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
-                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #      endif
-  basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
+      ;
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
 
@@ -1218,17 +1220,19 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+  void open(const char* __s, ios_base::openmode __mode = ios_base::in)
 #    if defined(_AIX)
-  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
-                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #    endif
-  void open(const char* __s, ios_base::openmode __mode = ios_base::in);
+      ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in)
 #      if defined(_AIX)
-  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
-                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #      endif
-  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
+      ;
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_STD_VER >= 17
@@ -1384,17 +1388,19 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+  void open(const char* __s, ios_base::openmode __mode = ios_base::out)
 #    if defined(_AIX)
-  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
-                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #    endif
-  void open(const char* __s, ios_base::openmode __mode = ios_base::out);
+      ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out)
 #      if defined(_AIX)
-  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
-                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #      endif
-  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
+      ;
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
 
@@ -1556,17 +1562,19 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+  _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out)
 #    if defined(_AIX)
-  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
-                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #    endif
-  _LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
+      ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
+  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out)
 #      if defined(_AIX)
-  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
-                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #      endif
-  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
+      ;
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 

>From 968fa31fe893eb5abfd1b33bd6da9ee4de525536 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Thu, 9 Jul 2026 22:44:32 +0530
Subject: [PATCH 11/12] nit: fix formatting

---
 libcxx/include/fstream | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index c90b9c2772177..357ad54a95df0 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -261,14 +261,14 @@ public:
       _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                                "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #    endif
-      ;
+          ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
   basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode)
 #      if defined(_AIX)
       _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                                "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #      endif
-      ;
+          ;
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
 
@@ -1225,14 +1225,14 @@ public:
       _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                                "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #    endif
-      ;
+          ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in)
 #      if defined(_AIX)
       _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                                "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #      endif
-      ;
+          ;
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_STD_VER >= 17
@@ -1393,14 +1393,14 @@ public:
       _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                                "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #    endif
-      ;
+          ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out)
 #      if defined(_AIX)
       _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                                "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #      endif
-      ;
+          ;
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
 
@@ -1567,14 +1567,14 @@ public:
       _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                                "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #    endif
-      ;
+          ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
   void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out)
 #      if defined(_AIX)
       _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
                                "fstream::open() with noreplace is not supported on AIX; open() will return failure")
 #      endif
-      ;
+          ;
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 

>From 226f1836367d2d96784bf7b21ede60727c71667c Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Fri, 10 Jul 2026 22:07:08 +0530
Subject: [PATCH 12/12] [libc++] Remove dead AIX warnings from wchar_t* open
 overloads

---
 libcxx/include/fstream | 28 ++++------------------------
 1 file changed, 4 insertions(+), 24 deletions(-)

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 357ad54a95df0..a560679b6a68f 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -263,12 +263,7 @@ public:
 #    endif
           ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode)
-#      if defined(_AIX)
-      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
-                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
-#      endif
-          ;
+  basic_filebuf* open(const wchar_t* __s, ios_base::openmode __mode);
 #    endif
   _LIBCPP_HIDE_FROM_ABI basic_filebuf* open(const string& __s, ios_base::openmode __mode);
 
@@ -1227,12 +1222,7 @@ public:
 #    endif
           ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in)
-#      if defined(_AIX)
-      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
-                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
-#      endif
-          ;
+  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::in);
 #    if _LIBCPP_STD_VER >= 17
@@ -1395,12 +1385,7 @@ public:
 #    endif
           ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out)
-#      if defined(_AIX)
-      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
-                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
-#      endif
-          ;
+  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::out);
 #    endif
   void open(const string& __s, ios_base::openmode __mode = ios_base::out);
 
@@ -1569,12 +1554,7 @@ public:
 #    endif
           ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out)
-#      if defined(_AIX)
-      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
-                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
-#      endif
-          ;
+  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 #    endif
   _LIBCPP_HIDE_FROM_ABI void open(const string& __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
 


From libcxx-commits at lists.llvm.org  Fri Jul 10 09:38:43 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 09:38:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a512013.d76d81af.39f9be.0ce5@mx.google.com>


================
@@ -256,8 +256,16 @@ public:
 
   // 27.9.1.4 Members:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
+#    if defined(_AIX)
+  _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                           "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
----------------
Himadhith wrote:

The `_LIBCPP_DIAGNOSE_WARNING` is now placed as a trailing attribute. 

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Fri Jul 10 09:39:15 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 09:39:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a512033.6c5720b3.128ed.ee5e@mx.google.com>


================
@@ -664,6 +674,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (noreplace) mode suffix; these cases fall through to
+// default (return nullptr), which is conformant per C23 7.21.5.3p5 (fail if atomic check-and-create is unsupported).
+#      if !defined(_AIX)
----------------
Himadhith wrote:

The guard here is the actual conformance fix — it's independent of the _LIBCPP_DIAGNOSE_WARNING, which is only advisory. Omitting these cases on AIX causes them to fall through to `default: return nullptr`, making `open()` fail as required.

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Fri Jul 10 09:47:27 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 09:47:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a51221f.4987c784.63cbc.7805@mx.google.com>


================
@@ -1206,9 +1220,19 @@ public:
   }
 #    endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool is_open() const;
-  void open(const char* __s, ios_base::openmode __mode = ios_base::in);
+  void open(const char* __s, ios_base::openmode __mode = ios_base::in)
+#    if defined(_AIX)
+      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#    endif
+          ;
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
-  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in);
+  void open(const wchar_t* __s, ios_base::openmode __mode = ios_base::in)
+#      if defined(_AIX)
+      _LIBCPP_DIAGNOSE_WARNING(__mode& ios_base::noreplace,
+                               "fstream::open() with noreplace is not supported on AIX; open() will return failure")
+#      endif
----------------
Himadhith wrote:

You're right, this was dead code. The `wchar_t*` filename overload warnings have been removed.

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Fri Jul 10 09:51:34 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 09:51:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Avoid constructing additional
 objects when using map::at (PR #157866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/157866@github.com>
Message-ID: <6a512316.e55dc341.1f19bd.c1da@mx.google.com>


================
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___TYPE_TRAITS_MAKE_TRANSPARENT_H
+#define _LIBCPP___TYPE_TRAITS_MAKE_TRANSPARENT_H
+
+#include <__config>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_empty.h>
+#include <__type_traits/is_same.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __make_transparent tries to create a transparent comparator from its non-transparent counterpart, e.g. obtain
+// `less<>` from `less<T>`. This is useful in cases where conversions can be avoided (e.g. a string literal to a
+// std::string).
+
+template <class _Comparator>
+struct __make_transparent {
+  using type _LIBCPP_NODEBUG = _Comparator;
+};
+
+template <class _Comparator>
+using __make_transparent_t _LIBCPP_NODEBUG = typename __make_transparent<_Comparator>::type;
+
+template <class _Comparator, __enable_if_t<is_same<_Comparator, __make_transparent_t<_Comparator> >::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Comparator& __as_transparent(_Comparator& __comp) {
+  return __comp;
+}
+
+template <class _Comparator, __enable_if_t<!is_same<_Comparator, __make_transparent_t<_Comparator> >::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI __make_transparent_t<_Comparator> __as_transparent(_Comparator&) {
+  static_assert(is_empty<_Comparator>::value);
----------------
ldionne wrote:

@philnik777 Am I right that using SFINAE instead of a `static_assert` here would be just as correct, but we'd accept stateful comparators? I know they are disallowed by the standard, but that wouldn't cause other issues right?

https://github.com/llvm/llvm-project/pull/157866

From libcxx-commits at lists.llvm.org  Fri Jul 10 09:52:49 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Fri, 10 Jul 2026 09:52:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Bump base image version to most
	recent (PR #208776)
Message-ID: <llvm.org/llvm/llvm-project/pull/208776@github.com>


https://github.com/yronglin created https://github.com/llvm/llvm-project/pull/208776

None

>From 139363c692e25db945ce56fdbea19e9734bd403d Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Fri, 10 Jul 2026 09:52:25 -0700
Subject: [PATCH] [libc++] Bump base image version to most recent

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/utils/ci/docker/docker-compose.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/utils/ci/docker/docker-compose.yml b/libcxx/utils/ci/docker/docker-compose.yml
index cd0584ec07b69..c22400597236c 100644
--- a/libcxx/utils/ci/docker/docker-compose.yml
+++ b/libcxx/utils/ci/docker/docker-compose.yml
@@ -23,7 +23,7 @@ services:
       context: ../../../.. # monorepo root
       dockerfile: libcxx/utils/ci/docker/linux-builder.dockerfile
       args:
-        BASE_IMAGE_VERSION: 5a616ce490abe551ffc7a7714c28e90c483f5b39
+        BASE_IMAGE_VERSION: 155cb886bfefb5677b7e371b3c2646ec3466929b
         GITHUB_RUNNER_VERSION: 2.335.0
 
   libcxx-android-builder:
@@ -32,7 +32,7 @@ services:
       context: ../../../.. # monorepo root
       dockerfile: libcxx/utils/ci/docker/android-builder.dockerfile
       args:
-        BASE_IMAGE_VERSION: 5a616ce490abe551ffc7a7714c28e90c483f5b39
+        BASE_IMAGE_VERSION: 155cb886bfefb5677b7e371b3c2646ec3466929b
         ANDROID_CLANG_VERSION: r584948b
         ANDROID_CLANG_PREBUILTS_COMMIT: 2b062008b0a7be59ad85f012cfeee60f052808f1
         ANDROID_SYSROOT_COMMIT: f8b85cc5262c6e5cbc9a92c1bab2b18b32a4c63f


From libcxx-commits at lists.llvm.org  Fri Jul 10 09:53:25 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 09:53:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Bump base image version to most
	recent (PR #208776)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208776@github.com>
Message-ID: <6a512385.d681cab9.333b2b.8a3f@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Yihan Wang (yronglin)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/208776.diff


1 Files Affected:

- (modified) libcxx/utils/ci/docker/docker-compose.yml (+2-2) 


``````````diff
diff --git a/libcxx/utils/ci/docker/docker-compose.yml b/libcxx/utils/ci/docker/docker-compose.yml
index cd0584ec07b69..c22400597236c 100644
--- a/libcxx/utils/ci/docker/docker-compose.yml
+++ b/libcxx/utils/ci/docker/docker-compose.yml
@@ -23,7 +23,7 @@ services:
       context: ../../../.. # monorepo root
       dockerfile: libcxx/utils/ci/docker/linux-builder.dockerfile
       args:
-        BASE_IMAGE_VERSION: 5a616ce490abe551ffc7a7714c28e90c483f5b39
+        BASE_IMAGE_VERSION: 155cb886bfefb5677b7e371b3c2646ec3466929b
         GITHUB_RUNNER_VERSION: 2.335.0
 
   libcxx-android-builder:
@@ -32,7 +32,7 @@ services:
       context: ../../../.. # monorepo root
       dockerfile: libcxx/utils/ci/docker/android-builder.dockerfile
       args:
-        BASE_IMAGE_VERSION: 5a616ce490abe551ffc7a7714c28e90c483f5b39
+        BASE_IMAGE_VERSION: 155cb886bfefb5677b7e371b3c2646ec3466929b
         ANDROID_CLANG_VERSION: r584948b
         ANDROID_CLANG_PREBUILTS_COMMIT: 2b062008b0a7be59ad85f012cfeee60f052808f1
         ANDROID_SYSROOT_COMMIT: f8b85cc5262c6e5cbc9a92c1bab2b18b32a4c63f

``````````

</details>


https://github.com/llvm/llvm-project/pull/208776

From libcxx-commits at lists.llvm.org  Fri Jul 10 11:46:40 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 11:46:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Bump base image version to most
	recent (PR #208776)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208776@github.com>
Message-ID: <6a513e10.c574b9b4.21ead0.9abd@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/208776

From libcxx-commits at lists.llvm.org  Fri Jul 10 11:50:26 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Fri, 10 Jul 2026 11:50:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add compact bounded
 specialization for capacity_aware_iterator (PR #208271)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208271@github.com>
Message-ID: <6a513ef2.510f576d.1b3cb9.b61c@mx.google.com>


https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/208271

>From 8496aed34b30e0c706852d0115b2f92dfbc97148 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 29 Jun 2026 23:18:11 -0400
Subject: [PATCH 1/6] [libc++] Add bounded specialization for
 capacity_aware_iterator

- By taking advantage of unused alignment bits for a pointer of type T and the static capacity known at compile time,
  we can make a compact bounded iterator who's size == sizeof(T*) by stuffing a counter inside those bits.
- This overload only applies in the following situation:
  - Iter is a pointer
  - The capacity is <= (1 << (bit_width(alignof(T)) - 1)) - 1. The extra -1 is required, because we need to represent
    the end position past the last element.
---
 .../__iterator/capacity_aware_iterator.h      | 226 ++++++++++++++++++
 .../capacity_aware_iter/arithmetic.pass.cpp   |  10 +-
 .../capacity_aware_iter/assert.pass.cpp       |  58 ++++-
 .../capacity_aware_iter/comparison.pass.cpp   |   7 +-
 .../capacity_aware_iter/dereference.pass.cpp  |  19 +-
 .../types.compile.pass.cpp                    |  11 +
 6 files changed, 318 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index a516feef60f36..fa394e227e774 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -11,6 +11,8 @@
 #define _LIBCPP___CAPACITY_AWARE_ITERATOR_H
 
 #include <__assert>
+#include <__bit/bit_cast.h>
+#include <__bit/countr.h>
 #include <__compare/ordering.h>
 #include <__compare/three_way_comparable.h>
 #include <__config>
@@ -21,8 +23,12 @@
 #include <__memory/pointer_traits.h>
 #include <__type_traits/is_constructible.h>
 #include <__type_traits/is_convertible.h>
+#include <__type_traits/is_pointer.h>
+#include <__utility/declval.h>
 #include <__utility/move.h>
 
+#include <cstdint>
+
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
@@ -51,6 +57,7 @@ class __capacity_aware_iterator {
 
 public:
   static_assert(contiguous_iterator<_Iter>, "__capacity_aware_iterator can only be used with contiguous iterators");
+  constexpr static bool __can_track_count_ = false;
 
   using iterator_category = iterator_traits<_Iter>::iterator_category;
   using iterator_concept  = contiguous_iterator_tag;
@@ -172,6 +179,225 @@ class __capacity_aware_iterator {
   }
 };
 
+template <class _Tp>
+consteval bool __range_fits_in_alignment(std::size_t __num_elems) {
+  auto __bits = std::countr_zero(alignof(_Tp));
+
+  // Example: For alignof(T) == 4, we have two bits free, which has a range of 0-3. We need to
+  // reserve one for the end position, so __num_elems must be < 3.
+  auto __allowed_range = (1 << __bits) - 1;
+  return __allowed_range > __num_elems;
+}
+
+// A specialization of capacity_aware_iterator where we store a runtime count of the current position inside
+// the unused bottom bits of a pointer to T. Only applies if capacity can fit inside those bits.
+template <class _Iter, class _Tag, size_t _RangeMaxElements>
+  requires(std::is_pointer_v<_Iter> &&
+           std::__range_fits_in_alignment<decltype(* std::declval<_Iter>())>(_RangeMaxElements))
+class __capacity_aware_iterator<_Iter, _Tag, _RangeMaxElements> {
+  constexpr static std::size_t __bits_ = std::countr_zero(alignof(decltype(*std::declval<_Iter>())));
+
+  union {
+    _Iter __ptr_;
+    std::uintptr_t __current_ : __bits_;
+  };
+
+  template <class, class, size_t>
+  friend class __capacity_aware_iterator;
+
+public:
+  constexpr static bool __can_track_count_ = false;
+
+  using iterator_category = iterator_traits<_Iter>::iterator_category;
+  using iterator_concept  = contiguous_iterator_tag;
+  using difference_type   = iter_difference_t<_Iter>;
+  using pointer           = iterator_traits<_Iter>::pointer;
+  using reference         = iter_reference_t<_Iter>;
+  using value_type        = iter_value_t<_Iter>;
+
+  constexpr __capacity_aware_iterator()
+    requires is_default_constructible_v<_Iter>
+  = default;
+
+  template <typename _Iter2>
+    requires is_convertible_v<_Iter2, _Iter>
+  constexpr __capacity_aware_iterator(const __capacity_aware_iterator<_Iter2, _Tag, _RangeMaxElements>& __y) noexcept
+      : __ptr_(__y.__ptr_) {
+    if !consteval {
+      __current_ = __y.__current_;
+    }
+  }
+
+  template <class _It, class _Tag2, size_t _RangeMaxElems2>
+  friend constexpr auto __make_capacity_aware_iterator(_It __iter) noexcept;
+
+private:
+  constexpr explicit __capacity_aware_iterator(_Iter __iter) : __ptr_(std::move(__iter)) {
+    if !consteval {
+      __current_ = 0u;
+    }
+  }
+
+  constexpr _Iter __curr_iter() const {
+    if consteval {
+      return __ptr_;
+    }
+
+    return std::bit_cast<_Iter>((std::bit_cast<std::uintptr_t>(__ptr_) >> __bits_) << __bits_) + __current_;
+  }
+
+  constexpr void __update(difference_type __n) {
+    if consteval {
+      __ptr_ += __n;
+    } else {
+      __current_ += __n;
+    }
+  }
+
+public:
+  [[nodiscard]] constexpr decltype(auto) operator*() const noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __current_ < _RangeMaxElements,
+          "__capacity_aware_iterator::operator*: Attempt to dereference an iterator at the end");
+    }
+
+    return *(__curr_iter());
+  }
+
+  constexpr decltype(auto) operator->() const noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __current_ < _RangeMaxElements,
+          "__capacity_aware_iterator::operator->: Attempt to dereference an iterator at the end");
+    }
+
+    return __curr_iter();
+  }
+
+  constexpr __capacity_aware_iterator& operator++() noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __current_ != _RangeMaxElements,
+          "__capacity_aware_iterator::operator++: Attempt to advance an iterator past the end");
+    }
+
+    __update(1);
+
+    return *this;
+  }
+
+  constexpr __capacity_aware_iterator operator++(int) noexcept {
+    __capacity_aware_iterator __tmp(*this);
+    ++*this;
+    return __tmp;
+  }
+
+  constexpr __capacity_aware_iterator& operator--() noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __current_ != 0u, "__capacity_aware_iterator::operator--: Attempt to rewind an iterator past the start");
+    }
+
+    __update(-1);
+
+    return *this;
+  }
+
+  constexpr __capacity_aware_iterator operator--(int) noexcept {
+    __capacity_aware_iterator __tmp(*this);
+    --*this;
+    return __tmp;
+  }
+
+  constexpr __capacity_aware_iterator& operator+=(difference_type __n) noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          (static_cast<difference_type>(__current_) + __n) >= 0,
+          "__capacity_aware_iterator::operator+=: Attempt to rewind iterator past the start");
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          static_cast<std::size_t>(__current_ + __n) <= _RangeMaxElements,
+          "__capacity_aware_iterator::operator+=: Attempt to advance an iterator past the end");
+    }
+
+    __update(__n);
+
+    return *this;
+  }
+
+  constexpr __capacity_aware_iterator& operator-=(difference_type __n) noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          (static_cast<difference_type>(__current_) - __n) >= 0,
+          "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          static_cast<std::size_t>(__current_ - __n) <= _RangeMaxElements,
+          "__capacity_aware_iterator::operator-=: Attempt to advance an iterator past the end");
+    }
+
+    __update(-__n);
+
+    return *this;
+  }
+
+  [[nodiscard]] constexpr decltype(auto) operator[](difference_type __n) const noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          (static_cast<difference_type>(__current_) + __n) >= 0,
+          "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the start");
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          static_cast<std::size_t>(__current_ + __n) <= _RangeMaxElements,
+          "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the end");
+    }
+    return *(*this + __n);
+  }
+
+  friend constexpr bool
+  operator==(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) noexcept {
+    return __x.__curr_iter() == __y.__curr_iter();
+  }
+
+  friend constexpr auto
+  operator<=>(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) noexcept {
+    if constexpr (three_way_comparable_with<_Iter, _Iter, strong_ordering>) {
+      return __x.__curr_iter() <=> __y.__curr_iter();
+    } else {
+      if (__x.__curr_iter() < __y.__curr_iter()) {
+        return strong_ordering::less;
+      } else if (__x.__curr_iter() == __y.__curr_iter()) {
+        return strong_ordering::equal;
+      }
+      return strong_ordering::greater;
+    }
+  }
+
+  [[nodiscard]] friend constexpr __capacity_aware_iterator
+  operator+(const __capacity_aware_iterator& __i, difference_type __n) noexcept {
+    auto __tmp = __i;
+    __tmp += __n;
+    return __tmp;
+  }
+
+  [[nodiscard]] friend constexpr __capacity_aware_iterator
+  operator+(difference_type __n, const __capacity_aware_iterator& __i) noexcept {
+    auto __tmp = __i;
+    __tmp += __n;
+    return __tmp;
+  }
+
+  [[nodiscard]] friend constexpr __capacity_aware_iterator
+  operator-(const __capacity_aware_iterator& __i, difference_type __n) noexcept {
+    auto __tmp = __i;
+    __tmp -= __n;
+    return __tmp;
+  }
+
+  [[nodiscard]] friend constexpr difference_type
+  operator-(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) noexcept {
+    return difference_type(__x.__curr_iter() - __y.__curr_iter());
+  }
+};
+
 template <class _It, class _Tag2, size_t _RangeMaxElems2>
 _LIBCPP_HIDE_FROM_ABI constexpr auto __make_capacity_aware_iterator(_It __iter) noexcept {
   return __capacity_aware_iterator<_It, _Tag2, _RangeMaxElems2>(__iter);
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
index f4aa5c29790ff..59a1e080d4d37 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
@@ -33,14 +33,14 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 
-template <typename Iter>
+template <typename Iter, typename Ty = int>
 constexpr bool test() {
-  int arr[]           = {1, 2, 3, 4, 5, 6};
+  Ty arr[]            = {1, 2, 3, 4, 5, 6};
   constexpr size_t sz = std::size(arr);
 
   using CapIter = std::__capacity_aware_iterator<Iter, decltype(arr), sz>;
 
-  int* i = arr + 0;
+  Ty* i = arr + 0;
 
   // operator++()
   {
@@ -162,5 +162,9 @@ int main(int, char**) {
   test<contiguous_iterator<int*>>();
   static_assert(test<contiguous_iterator<int*>>());
 
+  // bounded overload
+  test<long*, long>();
+  static_assert(test<long*, long>());
+
   return 0;
 }
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
index ceac20d549c34..ed0d29f0cbcef 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
@@ -13,13 +13,64 @@
 // struct __capacity_aware_iterator;
 
 // Check assert failure if advancing, rewinding or indexing iterator past its maximum range size
+// Or if we're keeping track of the current position, if we're advancing, rewinding, indexing out of bounds.
 
 #include <__iterator/capacity_aware_iterator.h>
 #include <iterator>
 
 #include "check_assertion.h"
-#include "test_iterators.h"
-#include "test_macros.h"
+
+// Specialization where we can fit a running count inside unused alignment bits.
+template <typename Iter>
+void test_bounded() {
+  int arr[] = {1, 2};
+  int* p    = arr;
+
+  constexpr long sz = std::size(arr);
+
+  using CapIter = std::__capacity_aware_iterator<Iter, decltype(p), sz>;
+
+  CapIter it = std::__make_capacity_aware_iterator<Iter, decltype(p), sz>(Iter(arr));
+
+  TEST_LIBCPP_ASSERT_FAILURE(it--, "__capacity_aware_iterator::operator--: Attempt to rewind iterator past the start");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it -= 1, "__capacity_aware_iterator::operator-=: Attempt to rewind iterator past the start");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it += -1, "__capacity_aware_iterator::operator+=: Attempt to rewind iterator past the start");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it += (sz + 1), "__capacity_aware_iterator::operator+=: Attempt to advance an iterator past the end");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it += -(sz + 1), "__capacity_aware_iterator::operator+=: Attempt to rewind iterator past the start");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it -= (sz + 1), "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it -= -(sz + 1),
+      "__capacity_aware_iterator::operator-=: Attempting to move iterator past its container's possible range");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it[sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the end");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it[-sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the start");
+
+  ++it;
+  ++it;
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      *it, "__capacity_aware_iterator::operator*: Attempt to dereference an iterator at the end");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it.operator->(), "__capacity_aware_iterator::operator->: Attempt to dereference an iterator at the end");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      ++it, "__capacity_aware_iterator::operator++: Attempt to advance an iterator past the end");
+}
 
 template <typename Iter>
 void test() {
@@ -57,7 +108,8 @@ void test() {
 }
 
 int main(int, char**) {
-  test<contiguous_iterator<int*>>();
+  test_bounded<int*>();
+  test<int*>();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
index 34aa73bf4459c..a7f3ae44fa5ee 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
@@ -30,9 +30,9 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 
-template <typename Iter>
+template <typename Iter, typename Ty = int>
 constexpr bool test() {
-  int arr[]         = {1, 2, 3, 4};
+  Ty arr[]          = {1, 2, 3, 4};
   constexpr long sz = std::size(arr);
 
   using CapIter = std::__capacity_aware_iterator<Iter, decltype(arr), sz>;
@@ -113,5 +113,8 @@ int main(int, char**) {
   test<contiguous_iterator<int*>>();
   static_assert(test<contiguous_iterator<int*>>());
 
+  test<long*, long>();
+  static_assert(test<long*, long>());
+
   return 0;
 }
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
index 42ab763896e2f..910078db4a7d4 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
@@ -30,9 +30,14 @@ struct Foo {
   constexpr bool operator==(Foo const& other) const { return x == other.x; }
 };
 
-template <typename Iter>
+struct Foo2 {
+  long x;
+  constexpr bool operator==(Foo2 const& other) const { return x == other.x; }
+};
+
+template <typename Iter, typename Ty = Foo>
 constexpr bool test() {
-  Foo arr[]         = {Foo{1}, Foo{2}, Foo{3}, Foo{4}};
+  Ty arr[]          = {Ty{1}, Ty{2}, Ty{3}, Ty{4}};
   constexpr long sz = std::size(arr);
 
   using CapIter = std::__capacity_aware_iterator<Iter, decltype(arr), sz>;
@@ -41,7 +46,7 @@ constexpr bool test() {
 
   // operator[]
   {
-    std::same_as<Foo&> decltype(auto) res = it[0];
+    std::same_as<Ty&> decltype(auto) res = it[0];
     ASSERT_NOEXCEPT(it[0]);
     assert(res == arr[0]);
     assert(&res == &arr[0]);
@@ -56,7 +61,7 @@ constexpr bool test() {
 
   // operator*
   {
-    std::same_as<Foo&> decltype(auto) res = *it;
+    std::same_as<Ty&> decltype(auto) res = *it;
     ASSERT_NOEXCEPT(*it);
     assert(*it == arr[0]);
     assert(&res == &arr[0]);
@@ -65,7 +70,7 @@ constexpr bool test() {
 
   // operator->
   {
-    std::same_as<Foo*> decltype(auto) ptr = it.operator->();
+    std::same_as<Ty*> decltype(auto) ptr = it.operator->();
     ASSERT_NOEXCEPT(it->x);
     assert(ptr->x == 1);
     assert(ptr == &arr[0]);
@@ -78,5 +83,9 @@ int main(int, char**) {
   test<three_way_contiguous_iterator<Foo*>>();
   static_assert(test<three_way_contiguous_iterator<Foo*>>());
 
+  // bounded overload
+  test<Foo2*, Foo2>();
+  static_assert(test<Foo2*, Foo2>());
+
   return 0;
 }
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
index b3b72a0728ed7..37680d0e7c859 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
@@ -29,3 +29,14 @@ static_assert(std::is_same_v<CapIter::difference_type, std::iter_difference_t<It
 static_assert(std::is_same_v<CapIter::reference, std::iter_reference_t<It>>);
 static_assert(std::is_same_v<CapIter::reference, std::iter_reference_t<It>>);
 static_assert(std::is_same_v<CapIter::value_type, std::iter_value_t<It>>);
+
+// bounded overload
+using It2      = long*;
+using CapIter2 = std::__capacity_aware_iterator<It2, long[6], 126>;
+
+static_assert(std::is_same_v<CapIter2::iterator_category, std::iterator_traits<It2>::iterator_category>);
+static_assert(std::is_same_v<CapIter2::iterator_concept, std::contiguous_iterator_tag>);
+static_assert(std::is_same_v<CapIter2::difference_type, std::iter_difference_t<It2>>);
+static_assert(std::is_same_v<CapIter2::reference, std::iter_reference_t<It2>>);
+static_assert(std::is_same_v<CapIter2::reference, std::iter_reference_t<It2>>);
+static_assert(std::is_same_v<CapIter2::value_type, std::iter_value_t<It2>>);

>From 4b70812bcf012fabbd792444e575dd70eca2ce04 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Wed, 8 Jul 2026 16:12:17 -0400
Subject: [PATCH 2/6] Fix

---
 .../include/__iterator/capacity_aware_iterator.h   |  6 +++---
 .../capacity_aware_iter/arithmetic.pass.cpp        | 12 ++++++++----
 .../iterators/capacity_aware_iter/assert.pass.cpp  | 14 +++++++-------
 3 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index fa394e227e774..442d9c4d2a561 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -314,7 +314,7 @@ class __capacity_aware_iterator<_Iter, _Tag, _RangeMaxElements> {
     if !consteval {
       _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
           (static_cast<difference_type>(__current_) + __n) >= 0,
-          "__capacity_aware_iterator::operator+=: Attempt to rewind iterator past the start");
+          "__capacity_aware_iterator::operator+=: Attempt to rewind an iterator past the start");
       _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
           static_cast<std::size_t>(__current_ + __n) <= _RangeMaxElements,
           "__capacity_aware_iterator::operator+=: Attempt to advance an iterator past the end");
@@ -346,8 +346,8 @@ class __capacity_aware_iterator<_Iter, _Tag, _RangeMaxElements> {
           (static_cast<difference_type>(__current_) + __n) >= 0,
           "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the start");
       _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
-          static_cast<std::size_t>(__current_ + __n) <= _RangeMaxElements,
-          "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the end");
+          static_cast<std::size_t>(__current_ + __n) < _RangeMaxElements,
+          "__capacity_aware_iterator::operator[]: Attempt to index an iterator at or past the end");
     }
     return *(*this + __n);
   }
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
index 59a1e080d4d37..d46bb4d3e8bec 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
@@ -66,7 +66,8 @@ constexpr bool test() {
 
   // operator--()
   {
-    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 1));
+    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
+    ++iter;
 
     std::same_as<CapIter&> decltype(auto) res = --iter;
 
@@ -77,7 +78,8 @@ constexpr bool test() {
 
   // operator--(int)
   {
-    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 1));
+    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
+    ++iter;
 
     std::same_as<CapIter> decltype(auto) res = iter--;
 
@@ -121,7 +123,8 @@ constexpr bool test() {
 
   // operator-=(difference_type)
   {
-    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 2));
+    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
+    iter += 2;
 
     std::same_as<CapIter&> decltype(auto) res = iter -= 2;
 
@@ -132,7 +135,8 @@ constexpr bool test() {
 
   // operator-(__capacity_aware_iterator, difference_type)
   {
-    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 2));
+    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
+    iter += 2;
 
     std::same_as<CapIter> decltype(auto) res = iter - 2;
 
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
index ed0d29f0cbcef..889e67899d8a4 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
@@ -32,29 +32,29 @@ void test_bounded() {
 
   CapIter it = std::__make_capacity_aware_iterator<Iter, decltype(p), sz>(Iter(arr));
 
-  TEST_LIBCPP_ASSERT_FAILURE(it--, "__capacity_aware_iterator::operator--: Attempt to rewind iterator past the start");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it--, "__capacity_aware_iterator::operator--: Attempt to rewind an iterator past the start");
 
   TEST_LIBCPP_ASSERT_FAILURE(
-      it -= 1, "__capacity_aware_iterator::operator-=: Attempt to rewind iterator past the start");
+      it -= 1, "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
 
   TEST_LIBCPP_ASSERT_FAILURE(
-      it += -1, "__capacity_aware_iterator::operator+=: Attempt to rewind iterator past the start");
+      it += -1, "__capacity_aware_iterator::operator+=: Attempt to rewind an iterator past the start");
 
   TEST_LIBCPP_ASSERT_FAILURE(
       it += (sz + 1), "__capacity_aware_iterator::operator+=: Attempt to advance an iterator past the end");
 
   TEST_LIBCPP_ASSERT_FAILURE(
-      it += -(sz + 1), "__capacity_aware_iterator::operator+=: Attempt to rewind iterator past the start");
+      it += -(sz + 1), "__capacity_aware_iterator::operator+=: Attempt to rewind an iterator past the start");
 
   TEST_LIBCPP_ASSERT_FAILURE(
       it -= (sz + 1), "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
 
   TEST_LIBCPP_ASSERT_FAILURE(
-      it -= -(sz + 1),
-      "__capacity_aware_iterator::operator-=: Attempting to move iterator past its container's possible range");
+      it -= -(sz + 1), "__capacity_aware_iterator::operator-=: Attempt to advance an iterator past the end");
 
   TEST_LIBCPP_ASSERT_FAILURE(
-      it[sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the end");
+      it[sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator at or past the end");
 
   TEST_LIBCPP_ASSERT_FAILURE(
       it[-sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the start");

>From 29eebbf0d6af4c5da9002208a9996b66061b2173 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 9 Jul 2026 00:42:21 -0400
Subject: [PATCH 3/6] Fix incorrect pointer arithmetic

---
 libcxx/include/__iterator/capacity_aware_iterator.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index 442d9c4d2a561..02db1d6b52d19 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -243,7 +243,7 @@ class __capacity_aware_iterator<_Iter, _Tag, _RangeMaxElements> {
       return __ptr_;
     }
 
-    return std::bit_cast<_Iter>((std::bit_cast<std::uintptr_t>(__ptr_) >> __bits_) << __bits_) + __current_;
+    return std::bit_cast<_Iter>(std::bit_cast<std::uintptr_t>(__ptr_) - __current_) + __current_;
   }
 
   constexpr void __update(difference_type __n) {

>From bc6bf987bdc44e70a5290e0d2cf94600d358a06c Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 9 Jul 2026 00:44:43 -0400
Subject: [PATCH 4/6] Simplify

---
 libcxx/include/__iterator/capacity_aware_iterator.h | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index 02db1d6b52d19..2572e1c0bd83c 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -24,7 +24,6 @@
 #include <__type_traits/is_constructible.h>
 #include <__type_traits/is_convertible.h>
 #include <__type_traits/is_pointer.h>
-#include <__utility/declval.h>
 #include <__utility/move.h>
 
 #include <cstdint>
@@ -192,10 +191,9 @@ consteval bool __range_fits_in_alignment(std::size_t __num_elems) {
 // A specialization of capacity_aware_iterator where we store a runtime count of the current position inside
 // the unused bottom bits of a pointer to T. Only applies if capacity can fit inside those bits.
 template <class _Iter, class _Tag, size_t _RangeMaxElements>
-  requires(std::is_pointer_v<_Iter> &&
-           std::__range_fits_in_alignment<decltype(* std::declval<_Iter>())>(_RangeMaxElements))
+  requires(std::is_pointer_v<_Iter> && std::__range_fits_in_alignment<std::iter_value_t<_Iter>>(_RangeMaxElements))
 class __capacity_aware_iterator<_Iter, _Tag, _RangeMaxElements> {
-  constexpr static std::size_t __bits_ = std::countr_zero(alignof(decltype(*std::declval<_Iter>())));
+  constexpr static std::size_t __bits_ = std::countr_zero(alignof(std::iter_value_t<_Iter>));
 
   union {
     _Iter __ptr_;

>From bc8d8ec7a90a804dcb492815f231de43c57e7501 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 9 Jul 2026 01:31:20 -0400
Subject: [PATCH 5/6] Try force alignment

---
 .../iterators/capacity_aware_iter/arithmetic.pass.cpp       | 4 ++--
 .../iterators/capacity_aware_iter/comparison.pass.cpp       | 4 ++--
 .../iterators/capacity_aware_iter/dereference.pass.cpp      | 6 +++---
 .../iterators/capacity_aware_iter/types.compile.pass.cpp    | 5 +++--
 4 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
index d46bb4d3e8bec..d84648a75e8f7 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
@@ -167,8 +167,8 @@ int main(int, char**) {
   static_assert(test<contiguous_iterator<int*>>());
 
   // bounded overload
-  test<long*, long>();
-  static_assert(test<long*, long>());
+  test<long long*, long long>();
+  static_assert(test<long long*, long long>());
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
index a7f3ae44fa5ee..149f37691eae0 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
@@ -113,8 +113,8 @@ int main(int, char**) {
   test<contiguous_iterator<int*>>();
   static_assert(test<contiguous_iterator<int*>>());
 
-  test<long*, long>();
-  static_assert(test<long*, long>());
+  test<long long*, long long>();
+  static_assert(test<long long*, long long>());
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
index 910078db4a7d4..9048207d92ad2 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
@@ -25,13 +25,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 
-struct Foo {
+struct alignas(4) Foo {
   int x;
   constexpr bool operator==(Foo const& other) const { return x == other.x; }
 };
 
-struct Foo2 {
-  long x;
+struct alignas(8) Foo2 {
+  int x;
   constexpr bool operator==(Foo2 const& other) const { return x == other.x; }
 };
 
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
index 37680d0e7c859..e633dc67803f4 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
@@ -30,9 +30,10 @@ static_assert(std::is_same_v<CapIter::reference, std::iter_reference_t<It>>);
 static_assert(std::is_same_v<CapIter::reference, std::iter_reference_t<It>>);
 static_assert(std::is_same_v<CapIter::value_type, std::iter_value_t<It>>);
 
+struct alignas(8) A {};
 // bounded overload
-using It2      = long*;
-using CapIter2 = std::__capacity_aware_iterator<It2, long[6], 126>;
+using It2      = A*;
+using CapIter2 = std::__capacity_aware_iterator<It2, A[], 126>;
 
 static_assert(std::is_same_v<CapIter2::iterator_category, std::iterator_traits<It2>::iterator_category>);
 static_assert(std::is_same_v<CapIter2::iterator_concept, std::contiguous_iterator_tag>);

>From 9cd041fd668fb40e045d9ef6375ceb971db04e64 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 10 Jul 2026 14:50:12 -0400
Subject: [PATCH 6/6] Fix GCC warning

---
 libcxx/include/__iterator/capacity_aware_iterator.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index 2572e1c0bd83c..36281bccdf2ec 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -180,11 +180,11 @@ class __capacity_aware_iterator {
 
 template <class _Tp>
 consteval bool __range_fits_in_alignment(std::size_t __num_elems) {
-  auto __bits = std::countr_zero(alignof(_Tp));
+  std::size_t __bits = std::countr_zero(alignof(_Tp));
 
   // Example: For alignof(T) == 4, we have two bits free, which has a range of 0-3. We need to
   // reserve one for the end position, so __num_elems must be < 3.
-  auto __allowed_range = (1 << __bits) - 1;
+  std::size_t __allowed_range = (1 << __bits) - 1;
   return __allowed_range > __num_elems;
 }
 


From libcxx-commits at lists.llvm.org  Fri Jul 10 11:50:40 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 11:50:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Bump base image version to most
	recent (PR #208776)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208776@github.com>
Message-ID: <6a513f00.1905e68e.18ad68.60d8@mx.google.com>


ldionne wrote:

@boomanaiden154 @cmtice Would it be possible to deploy this to the `llvm-premerge-libcxx-runners` workers?

We're currently using the `llvm-premerge-libcxx-next-runners` group already, so we should deploy to the "non-next" group and flip over in a PR once ready.

https://github.com/llvm/llvm-project/pull/208776

From libcxx-commits at lists.llvm.org  Fri Jul 10 11:50:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 11:50:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] a6cf9ec - [libc++] Bump base image
	version to most recent (#208776)
Message-ID: <6a513f07.1905e68e.18ad68.6306@mx.google.com>


Author: Yihan Wang
Date: 2026-07-10T14:50:42-04:00
New Revision: a6cf9ec73060bf2b38b3981459071d86b254e8fc

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

LOG: [libc++] Bump base image version to most recent (#208776)

Signed-off-by: yronglin <yronglin777 at gmail.com>

Added: 
    

Modified: 
    libcxx/utils/ci/docker/docker-compose.yml

Removed: 
    


################################################################################
diff  --git a/libcxx/utils/ci/docker/docker-compose.yml b/libcxx/utils/ci/docker/docker-compose.yml
index cd0584ec07b69..eeffc71c39a95 100644
--- a/libcxx/utils/ci/docker/docker-compose.yml
+++ b/libcxx/utils/ci/docker/docker-compose.yml
@@ -23,7 +23,7 @@ services:
       context: ../../../.. # monorepo root
       dockerfile: libcxx/utils/ci/docker/linux-builder.dockerfile
       args:
-        BASE_IMAGE_VERSION: 5a616ce490abe551ffc7a7714c28e90c483f5b39
+        BASE_IMAGE_VERSION: 8ece73c9f8b54fea6a98e9e4c55f9a2e6ccc9b3c
         GITHUB_RUNNER_VERSION: 2.335.0
 
   libcxx-android-builder:
@@ -32,7 +32,7 @@ services:
       context: ../../../.. # monorepo root
       dockerfile: libcxx/utils/ci/docker/android-builder.dockerfile
       args:
-        BASE_IMAGE_VERSION: 5a616ce490abe551ffc7a7714c28e90c483f5b39
+        BASE_IMAGE_VERSION: 8ece73c9f8b54fea6a98e9e4c55f9a2e6ccc9b3c
         ANDROID_CLANG_VERSION: r584948b
         ANDROID_CLANG_PREBUILTS_COMMIT: 2b062008b0a7be59ad85f012cfeee60f052808f1
         ANDROID_SYSROOT_COMMIT: f8b85cc5262c6e5cbc9a92c1bab2b18b32a4c63f


        

From libcxx-commits at lists.llvm.org  Fri Jul 10 11:50:51 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 10 Jul 2026 11:50:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Bump base image version to most
	recent (PR #208776)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208776@github.com>
Message-ID: <6a513f0b.c574b9b4.21ead0.ea70@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208776

From libcxx-commits at lists.llvm.org  Fri Jul 10 16:53:12 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 16:53:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a5185e8.c574b9b4.21ead0.0991@mx.google.com>


inquisitivecrystal wrote:

Would anyone feel comfortable approving this? It'd be lovely to get it into the upcoming release, and full CI runs take a while, even assuming they don't show any problems. That being said, this is a large PR, and I understand if people feel that more review is needed.

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Fri Jul 10 19:01:32 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 19:01:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as "Nothing To Do"
	(PR #208866)
Message-ID: <llvm.org/llvm/llvm-project/pull/208866@github.com>


https://github.com/inquisitivecrystal created https://github.com/llvm/llvm-project/pull/208866

[LWG3403](https://wg21.link/LWG3403) specifies that `ranges::ssize` should work on non-range objects for which `ranges::size` is valid. This has been true in libc++ since 6f1b10d, which first implemented `ranges::ssize`. The tests introduced by that commit are sufficient to establish compliance:

```c++
struct SizeMember {
  constexpr std::size_t size() { return 42; }
};
...
assert(std::ranges::ssize(SizeMember()) == 42);
```

Resolves #104280.

>From c08648f59c2c19d781792f73fee0b4201be1fc31 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Fri, 10 Jul 2026 18:30:33 -0700
Subject: [PATCH] Mark LWG3403 as "Nothing To Do"

This was implemented and tested when libc++ added ranges::ssize.
---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index d0616fb46681c..93c8c7ae28abf 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Nothing To Do|","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
 "`LWG3404 <https://wg21.link/LWG3404>`__","Finish removing subrange's conversions from pair-like","2020-11 (Virtual)","|Complete|","13","`#104282 <https://github.com/llvm/llvm-project/issues/104282>`__",""
 "`LWG3405 <https://wg21.link/LWG3405>`__","``common_view``'s converting constructor is bad, too","2020-11 (Virtual)","|Complete|","14","`#104283 <https://github.com/llvm/llvm-project/issues/104283>`__",""
 "`LWG3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","2020-11 (Virtual)","|Complete|","16","`#104284 <https://github.com/llvm/llvm-project/issues/104284>`__",""


From libcxx-commits at lists.llvm.org  Fri Jul 10 19:02:11 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 19:02:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as "Nothing To Do"
	(PR #208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51a423.13be6df8.145c.1cae@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: inquisitivecrystal (inquisitivecrystal)

<details>
<summary>Changes</summary>

[LWG3403](https://wg21.link/LWG3403) specifies that `ranges::ssize` should work on non-range objects for which `ranges::size` is valid. This has been true in libc++ since 6f1b10d, which first implemented `ranges::ssize`. The tests introduced by that commit are sufficient to establish compliance:

```c++
struct SizeMember {
  constexpr std::size_t size() { return 42; }
};
...
assert(std::ranges::ssize(SizeMember()) == 42);
```

Resolves #<!-- -->104280.

---
Full diff: https://github.com/llvm/llvm-project/pull/208866.diff


1 Files Affected:

- (modified) libcxx/docs/Status/Cxx23Issues.csv (+1-1) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index d0616fb46681c..93c8c7ae28abf 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Nothing To Do|","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
 "`LWG3404 <https://wg21.link/LWG3404>`__","Finish removing subrange's conversions from pair-like","2020-11 (Virtual)","|Complete|","13","`#104282 <https://github.com/llvm/llvm-project/issues/104282>`__",""
 "`LWG3405 <https://wg21.link/LWG3405>`__","``common_view``'s converting constructor is bad, too","2020-11 (Virtual)","|Complete|","14","`#104283 <https://github.com/llvm/llvm-project/issues/104283>`__",""
 "`LWG3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","2020-11 (Virtual)","|Complete|","16","`#104284 <https://github.com/llvm/llvm-project/issues/104284>`__",""

``````````

</details>


https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Fri Jul 10 19:02:48 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 19:02:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as "Nothing To Do"
	(PR #208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51a448.d76d81af.39f9be.1c0a@mx.google.com>


github-actions[bot] wrote:

⚠️ We detected that you are using a GitHub private e-mail address to contribute to the repo.<br/> Please turn off [Keep my email addresses private](https://github.com/settings/emails) setting in your account.<br/> See [LLVM Developer Policy](https://llvm.org/docs/DeveloperPolicy.html#email-addresses) and [LLVM Discourse](https://discourse.llvm.org/t/hidden-emails-on-github-should-we-do-something-about-it) for more information.

https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Fri Jul 10 19:04:16 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 19:04:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51a4a0.a1accbb4.244acd.036e@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Fri Jul 10 19:05:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 19:05:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51a4ff.a1accbb4.244acd.220c@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/208866

>From bad26cea1287d99e28b807075fa016c167430bfc Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 19:05:28 -0700
Subject: [PATCH] Mark LWG3403 as "Nothing To Do"

This was implemented and tested when libc++ added ranges::ssize.
---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index d0616fb46681c..93c8c7ae28abf 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Nothing To Do|","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
 "`LWG3404 <https://wg21.link/LWG3404>`__","Finish removing subrange's conversions from pair-like","2020-11 (Virtual)","|Complete|","13","`#104282 <https://github.com/llvm/llvm-project/issues/104282>`__",""
 "`LWG3405 <https://wg21.link/LWG3405>`__","``common_view``'s converting constructor is bad, too","2020-11 (Virtual)","|Complete|","14","`#104283 <https://github.com/llvm/llvm-project/issues/104283>`__",""
 "`LWG3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","2020-11 (Virtual)","|Complete|","16","`#104284 <https://github.com/llvm/llvm-project/issues/104284>`__",""


From libcxx-commits at lists.llvm.org  Fri Jul 10 19:29:59 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 19:29:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a51aaa7.13be6df8.145c.9534@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/25] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/25] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/25] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/25] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/25] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/25] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/25] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/25] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/25] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/25] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/25] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/25] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/25] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/25] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/25] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/25] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/25] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/25] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/25] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/25] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/25] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}

>From 86f59d72480a80cb0d3325cac1ee59d5b08524bb Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 04:18:49 -0700
Subject: [PATCH 22/25] Permit reserve_hint's unqualified lookup

reserve_hint must use ADL per the specification, and thus needs to
be exempted from the libcpp-robust-against-adl check like other
CPOs.
---
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..16c45a524811e 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -20,7 +20,8 @@ AST_MATCHER(clang::CallExpr, isOperator) { return llvm::isa<clang::CXXOperatorCa
 
 AST_MATCHER(clang::UnresolvedLookupExpr, isCustomizationPoint) {
   return std::ranges::any_of(
-      std::array{"swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin"},
+      std::array{
+          "swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin", "reserve_hint"},
       [&](const char* func) { return Node.getName().getAsString() == func; });
 }
 

>From 0020ac432662be8ace647b3c92368cbc13744c24 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 06:14:47 -0700
Subject: [PATCH 23/25] Fix unused variable warning from GCC

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 11379bedf7bf2..6b6a8523afbdc 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -39,7 +39,7 @@ struct HasSizeMember {
 };
 
 struct HasSizeFunction {
-  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+  friend constexpr std::size_t size(HasSizeFunction) { return 42; }
 };
 
 struct HasReserveHintMember {
@@ -47,7 +47,7 @@ struct HasReserveHintMember {
 };
 
 struct HasReserveHintFunction {
-  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction) { return 42; }
 };
 
 struct HasReserveHintMemberBool {
@@ -86,12 +86,12 @@ ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})),
 struct HasSizeAndReserveHint {
   constexpr std::size_t size() { return 42; }
   constexpr std::size_t reserve_hint() { return 0; }
-  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint) { return 0; }
 };
 
 struct HasReserveHintMemberAndFunction {
   constexpr std::size_t reserve_hint() { return 42; }
-  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction) { return 0; }
 };
 
 static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);

>From 6b09d9baea9a8cd77b0c916fcc0ae043fe8a1d0d Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 22:24:20 -0700
Subject: [PATCH 24/25] Address review comments

---
 .../range.adaptors/range.stride.view/nodiscard.verify.cpp     | 4 ++++
 .../test/std/ranges/range.adaptors/range.common.view/types.h  | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 3cc5b6f2db9b2..e4df227ff44c8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -32,6 +32,10 @@ void test() {
 #if TEST_STD_VER >= 26
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.reserve_hint();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(sv).reserve_hint();
+
 #endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 130423389ad68..670837aa47744 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,8 +11,8 @@
 
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
   int* begin_ = nullptr;

>From d02f2fb321edd1e7d48f651d6d39a7954b251206 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 19:29:28 -0700
Subject: [PATCH 25/25] Empty commit for CI



From libcxx-commits at lists.llvm.org  Fri Jul 10 19:46:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 19:46:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 7d38440 - [libc++] Replace the of use
 custom sections for detecting overriden functions (#208330)
Message-ID: <6a51ae68.743400b0.2067c9.e07c@mx.google.com>


Author: Petr Hosek
Date: 2026-07-10T19:45:55-07:00
New Revision: 7d38440aecf6c131f3c8887afea759b102ae2704

URL: https://github.com/llvm/llvm-project/commit/7d38440aecf6c131f3c8887afea759b102ae2704
DIFF: https://github.com/llvm/llvm-project/commit/7d38440aecf6c131f3c8887afea759b102ae2704.diff

LOG: [libc++] Replace the of use custom sections for detecting overriden functions (#208330)

This is a follow up to #133876 and an alternative to #120805 which
doesn't rely on aliases and works across both ELF and Mach-O. This
mechanism is preferable in baremetal environments since it doesn't
require special handling of the custom sections.

Added: 
    

Modified: 
    libcxx/src/include/overridable_function.h

Removed: 
    


################################################################################
diff  --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index c8d9f30c74c3e..bd55c4bb6d8bc 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -11,11 +11,6 @@
 #define _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H
 
 #include <__config>
-#include <cstdint>
-
-#if __has_feature(ptrauth_calls)
-#  include <ptrauth.h>
-#endif
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -42,18 +37,11 @@
 // -------------------
 //
 // Let's say we want to check whether a weak function `f` has been overridden by the user.
-// The general mechanism works by placing `f`'s definition (in the libc++ built library)
-// inside a special section, which we do using the `__section__` attribute via the
-// OVERRIDABLE_FUNCTION macro.
-//
-// Then, when comes the time to check whether the function has been overridden, we take
-// the address of the function and we check whether it falls inside the special function
-// we created. This can be done by finding pointers to the start and the end of the section
-// (which is done 
diff erently for ELF and Mach-O), and then checking whether `f` falls
-// within those bounds. If it falls within those bounds, then `f` is still inside the
-// special section and so it is the version we defined in the libc++ built library, i.e.
-// it was not overridden. Otherwise, it was overridden by the user because it falls
-// outside of the section.
+// The general mechanism works by defining a local symbol `__impl_ref<f>::__impl_` with
+// the same address as `f` as a constant expression using direct PC-relative
+// materialization thus pointing at the symbol defined in the same TU. At runtime, it
+// compares the address of `__impl_ref<f>::__impl_` with the address of `f` loaded from
+// GOT: if `f` was overridden by the user in another TU, the addresses will be 
diff erent.
 //
 // Important note
 // --------------
@@ -63,64 +51,54 @@
 // want to be defining special sections inside user's executables which use our headers.
 //
 
-#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
+#if defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
+#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
 
-_LIBCPP_BEGIN_NAMESPACE_STD template <typename T, T* _Func>
-_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-  // Declare two dummy bytes and give them these special `__asm` values. These values are
-  // defined by the linker, which means that referring to `&__lcxx_override_start` will
-  // effectively refer to the address where the section starts (and same for the end).
-  extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
-  extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
-
-  // Now get a uintptr_t out of these locations, and out of the function pointer.
-  uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
-  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
-  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
-
-#  if __has_feature(ptrauth_calls)
-  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
-  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
-  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
-  // stripped the function pointer. See rdar://122927845.
-  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
-#  endif
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace {
 
-  // Finally, the function was overridden if it falls outside of the section's bounds.
-  return __ptr < __start || __ptr > __end;
+// This is used to prevent TBAA from optimizing away the function pointer comparison.
+template <typename T>
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI T* __libcpp_launder(T* __ptr) noexcept {
+  __asm__ volatile("" : "+r"(__ptr));
+  return __ptr;
 }
-_LIBCPP_END_NAMESPACE_STD
 
-// The NVPTX linker cannot create '__start/__stop' sections.
-#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)
+} // namespace
 
-#  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+template <auto* _Func>
+struct __impl_ref;
 
-// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
-// variables with those names corresponding to the start and the end of the section.
-//
-// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
-extern char __start___lcxx_override;
-extern char __stop___lcxx_override;
+// __impl_ref<...>::__impl_ is expected to be defined elsewhere, so the compiler emits
+// assembly references to the mangled symbol with no definition. This template saves us
+// the trouble of providing manual declarations for overloads with some other local name
+// for each function name being overloaded (operator new, operator new[], etc.).
+template <typename _Ret, typename... _Args, _Ret (*_Func)(_Args...)>
+struct __impl_ref<_Func> {
+  [[gnu::visibility("hidden")]] static _Ret __impl_(_Args...);
+};
 
-_LIBCPP_BEGIN_NAMESPACE_STD
+// This takes a function type template argument first so that the second non-type template
+// argument (pointer to the public function) gets the benefit of type-aware overload
+// resolution, rather than having to use a static_cast.
 template <typename T, T* _Func>
 _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-  uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
-  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
-  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
-
-#  if __has_feature(ptrauth_calls)
-  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. See full explanation above.
-  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
+#  if !defined(_LIBCPP_CLANG_VER) || _LIBCPP_CLANG_VER >= 2101
+  __asm__("%cc0 = %cc1" : : "X"(__impl_ref<_Func>::__impl_), "X"(_Func));
+#  else
+  __asm__("%c0 = %c1" : : "X"(__impl_ref<_Func>::__impl_), "X"(_Func));
 #  endif
-
-  return __ptr < __start || __ptr > __end;
+  // This just has the compiler compare the two symbols. For PIC mode, this will do a
+  // direct PC-relative materialization for __impl_ref<...>::__impl_ and a GOT load for
+  // the _Func symbol. The compiler thinks __impl_ref<...>::__impl_ is defined elsewhere
+  // at link time and will be an undefined symbol. It doesn't know that the __asm__ tells
+  // the assembler to define it as a local symbol.
+  return __libcpp_launder(_Func) != __impl_ref<_Func>::__impl_;
 }
+
 _LIBCPP_END_NAMESPACE_STD
 
 #else


        

From libcxx-commits at lists.llvm.org  Fri Jul 10 19:46:05 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Fri, 10 Jul 2026 19:46:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Replace the of use custom
 sections for detecting overriden functions (PR #208330)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208330@github.com>
Message-ID: <6a51ae6d.f06f4994.1fc857.6f53@mx.google.com>


https://github.com/petrhosek closed https://github.com/llvm/llvm-project/pull/208330

From libcxx-commits at lists.llvm.org  Fri Jul 10 20:13:30 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 20:13:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a51b4d9.3cce8983.225732.b950@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/26] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/26] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/26] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/26] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/26] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/26] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/26] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/26] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/26] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/26] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/26] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/26] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/26] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/26] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/26] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/26] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/26] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/26] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/26] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/26] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/26] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}

>From 86f59d72480a80cb0d3325cac1ee59d5b08524bb Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 04:18:49 -0700
Subject: [PATCH 22/26] Permit reserve_hint's unqualified lookup

reserve_hint must use ADL per the specification, and thus needs to
be exempted from the libcpp-robust-against-adl check like other
CPOs.
---
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..16c45a524811e 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -20,7 +20,8 @@ AST_MATCHER(clang::CallExpr, isOperator) { return llvm::isa<clang::CXXOperatorCa
 
 AST_MATCHER(clang::UnresolvedLookupExpr, isCustomizationPoint) {
   return std::ranges::any_of(
-      std::array{"swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin"},
+      std::array{
+          "swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin", "reserve_hint"},
       [&](const char* func) { return Node.getName().getAsString() == func; });
 }
 

>From 0020ac432662be8ace647b3c92368cbc13744c24 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 06:14:47 -0700
Subject: [PATCH 23/26] Fix unused variable warning from GCC

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 11379bedf7bf2..6b6a8523afbdc 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -39,7 +39,7 @@ struct HasSizeMember {
 };
 
 struct HasSizeFunction {
-  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+  friend constexpr std::size_t size(HasSizeFunction) { return 42; }
 };
 
 struct HasReserveHintMember {
@@ -47,7 +47,7 @@ struct HasReserveHintMember {
 };
 
 struct HasReserveHintFunction {
-  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction) { return 42; }
 };
 
 struct HasReserveHintMemberBool {
@@ -86,12 +86,12 @@ ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})),
 struct HasSizeAndReserveHint {
   constexpr std::size_t size() { return 42; }
   constexpr std::size_t reserve_hint() { return 0; }
-  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint) { return 0; }
 };
 
 struct HasReserveHintMemberAndFunction {
   constexpr std::size_t reserve_hint() { return 42; }
-  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction) { return 0; }
 };
 
 static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);

>From 6b09d9baea9a8cd77b0c916fcc0ae043fe8a1d0d Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 22:24:20 -0700
Subject: [PATCH 24/26] Address review comments

---
 .../range.adaptors/range.stride.view/nodiscard.verify.cpp     | 4 ++++
 .../test/std/ranges/range.adaptors/range.common.view/types.h  | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 3cc5b6f2db9b2..e4df227ff44c8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -32,6 +32,10 @@ void test() {
 #if TEST_STD_VER >= 26
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.reserve_hint();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(sv).reserve_hint();
+
 #endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 130423389ad68..670837aa47744 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,8 +11,8 @@
 
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
   int* begin_ = nullptr;

>From d02f2fb321edd1e7d48f651d6d39a7954b251206 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 19:29:28 -0700
Subject: [PATCH 25/26] Empty commit for CI


>From fdb58ee8c8174b1bd8162cb7729cdff9e65eb98f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 20:10:21 -0700
Subject: [PATCH 26/26] Avoid creating an empty namespace before c++26

---
 libcxx/include/__ranges/reserve_hint.h | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
index b3b4be59f8941..e58990bf1194e 100644
--- a/libcxx/include/__ranges/reserve_hint.h
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -20,9 +20,10 @@
 #  pragma GCC system_header
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 26
 // [range.prim.size.hint]
 
 namespace ranges {
@@ -72,8 +73,8 @@ inline constexpr auto reserve_hint = __reserve_hint::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#endif // _LIBCPP_STD_VER >= 26
-
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP___RANGES_RESERVE_HINT_H


From libcxx-commits at lists.llvm.org  Fri Jul 10 20:22:17 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 20:22:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51b6e9.4a342cbc.ca844.7f9e@mx.google.com>


inquisitivecrystal wrote:

This could also be marked complete, with the completion release being 15, the first one to include `ranges::ssize`. I'm not sure what the convention is about when to use "complete" as opposed to "nothing to do" for changes that have already been implemented.

https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Fri Jul 10 20:30:09 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 10 Jul 2026 20:30:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51b8c1.9edb5c9d.208698.610d@mx.google.com>


================
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Nothing To Do|","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
----------------
Zingam wrote:

```suggestion
"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Complete|","15","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
```

https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Fri Jul 10 20:32:33 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 10 Jul 2026 20:32:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51b951.7270d2fd.2d4879.9908@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Fri Jul 10 20:56:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 20:56:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51bf03.3cce8983.225732.e202@mx.google.com>


================
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Nothing To Do|","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
----------------
inquisitivecrystal wrote:

This would end up with the status page claiming that we'd completed a change to `ranges::ssize` in 13, but implemented [P1970R2](https://wg21.link/P1970R2), which added `ranges::ssize`, in 15. I think the problem is that the completion marker was added in 44ae09d7, which didn't bother to be precise about when the addition had occurred.

I'm just going to put 15 for consistency, I think. 

https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Fri Jul 10 20:59:31 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 20:59:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51bfa3.e5907290.281f16.f97f@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/208866

>From bad26cea1287d99e28b807075fa016c167430bfc Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 19:05:28 -0700
Subject: [PATCH 1/2] Mark LWG3403 as "Nothing To Do"

This was implemented and tested when libc++ added ranges::ssize.
---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index d0616fb46681c..93c8c7ae28abf 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Nothing To Do|","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
 "`LWG3404 <https://wg21.link/LWG3404>`__","Finish removing subrange's conversions from pair-like","2020-11 (Virtual)","|Complete|","13","`#104282 <https://github.com/llvm/llvm-project/issues/104282>`__",""
 "`LWG3405 <https://wg21.link/LWG3405>`__","``common_view``'s converting constructor is bad, too","2020-11 (Virtual)","|Complete|","14","`#104283 <https://github.com/llvm/llvm-project/issues/104283>`__",""
 "`LWG3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","2020-11 (Virtual)","|Complete|","16","`#104284 <https://github.com/llvm/llvm-project/issues/104284>`__",""

>From 8c708b7e24dc9725d943fd6118d68980fd7f2a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 20:58:33 -0700
Subject: [PATCH 2/2] Mark LWG3403 complete as of libc++15

---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 93c8c7ae28abf..492026ab08bc2 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Nothing To Do|","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Complete|","15","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
 "`LWG3404 <https://wg21.link/LWG3404>`__","Finish removing subrange's conversions from pair-like","2020-11 (Virtual)","|Complete|","13","`#104282 <https://github.com/llvm/llvm-project/issues/104282>`__",""
 "`LWG3405 <https://wg21.link/LWG3405>`__","``common_view``'s converting constructor is bad, too","2020-11 (Virtual)","|Complete|","14","`#104283 <https://github.com/llvm/llvm-project/issues/104283>`__",""
 "`LWG3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","2020-11 (Virtual)","|Complete|","16","`#104284 <https://github.com/llvm/llvm-project/issues/104284>`__",""


From libcxx-commits at lists.llvm.org  Fri Jul 10 21:01:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 21:01:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51c02b.e55dc341.1f19bd.88bb@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Fri Jul 10 21:11:48 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 21:11:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51c284.824e11a6.f5f27.38b5@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Fri Jul 10 21:37:44 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Fri, 10 Jul 2026 21:37:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add compact bounded
 specialization for capacity_aware_iterator (PR #208271)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208271@github.com>
Message-ID: <6a51c898.492e23e4.157d47.17c7@mx.google.com>


https://github.com/smallp-o-p ready_for_review https://github.com/llvm/llvm-project/pull/208271

From libcxx-commits at lists.llvm.org  Fri Jul 10 21:38:16 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 21:38:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add compact bounded
 specialization for capacity_aware_iterator (PR #208271)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208271@github.com>
Message-ID: <6a51c8b8.492e23e4.157d47.2712@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: William Tran-Viet (smallp-o-p)

<details>
<summary>Changes</summary>

- By taking advantage of unused alignment bits for a pointer of type `T` and the static capacity known at compile time, we can make a compact bounded iterator who's size == `sizeof(T*)` by stuffing a counter inside those bits.
- This overload only applies in the following situation:
  - Iter is a pointer
  - The capacity is &lt;= `(1 &lt;&lt; (bit_width(alignof(T)) - 1)) - 1`. The extra -1 is required, because we need to represent the end position past the last element.

---
Full diff: https://github.com/llvm/llvm-project/pull/208271.diff


6 Files Affected:

- (modified) libcxx/include/__iterator/capacity_aware_iterator.h (+224) 
- (modified) libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp (+15-7) 
- (modified) libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp (+55-3) 
- (modified) libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp (+5-2) 
- (modified) libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp (+15-6) 
- (modified) libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp (+12) 


``````````diff
diff --git a/libcxx/include/__iterator/capacity_aware_iterator.h b/libcxx/include/__iterator/capacity_aware_iterator.h
index a516feef60f36..36281bccdf2ec 100644
--- a/libcxx/include/__iterator/capacity_aware_iterator.h
+++ b/libcxx/include/__iterator/capacity_aware_iterator.h
@@ -11,6 +11,8 @@
 #define _LIBCPP___CAPACITY_AWARE_ITERATOR_H
 
 #include <__assert>
+#include <__bit/bit_cast.h>
+#include <__bit/countr.h>
 #include <__compare/ordering.h>
 #include <__compare/three_way_comparable.h>
 #include <__config>
@@ -21,8 +23,11 @@
 #include <__memory/pointer_traits.h>
 #include <__type_traits/is_constructible.h>
 #include <__type_traits/is_convertible.h>
+#include <__type_traits/is_pointer.h>
 #include <__utility/move.h>
 
+#include <cstdint>
+
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
@@ -51,6 +56,7 @@ class __capacity_aware_iterator {
 
 public:
   static_assert(contiguous_iterator<_Iter>, "__capacity_aware_iterator can only be used with contiguous iterators");
+  constexpr static bool __can_track_count_ = false;
 
   using iterator_category = iterator_traits<_Iter>::iterator_category;
   using iterator_concept  = contiguous_iterator_tag;
@@ -172,6 +178,224 @@ class __capacity_aware_iterator {
   }
 };
 
+template <class _Tp>
+consteval bool __range_fits_in_alignment(std::size_t __num_elems) {
+  std::size_t __bits = std::countr_zero(alignof(_Tp));
+
+  // Example: For alignof(T) == 4, we have two bits free, which has a range of 0-3. We need to
+  // reserve one for the end position, so __num_elems must be < 3.
+  std::size_t __allowed_range = (1 << __bits) - 1;
+  return __allowed_range > __num_elems;
+}
+
+// A specialization of capacity_aware_iterator where we store a runtime count of the current position inside
+// the unused bottom bits of a pointer to T. Only applies if capacity can fit inside those bits.
+template <class _Iter, class _Tag, size_t _RangeMaxElements>
+  requires(std::is_pointer_v<_Iter> && std::__range_fits_in_alignment<std::iter_value_t<_Iter>>(_RangeMaxElements))
+class __capacity_aware_iterator<_Iter, _Tag, _RangeMaxElements> {
+  constexpr static std::size_t __bits_ = std::countr_zero(alignof(std::iter_value_t<_Iter>));
+
+  union {
+    _Iter __ptr_;
+    std::uintptr_t __current_ : __bits_;
+  };
+
+  template <class, class, size_t>
+  friend class __capacity_aware_iterator;
+
+public:
+  constexpr static bool __can_track_count_ = false;
+
+  using iterator_category = iterator_traits<_Iter>::iterator_category;
+  using iterator_concept  = contiguous_iterator_tag;
+  using difference_type   = iter_difference_t<_Iter>;
+  using pointer           = iterator_traits<_Iter>::pointer;
+  using reference         = iter_reference_t<_Iter>;
+  using value_type        = iter_value_t<_Iter>;
+
+  constexpr __capacity_aware_iterator()
+    requires is_default_constructible_v<_Iter>
+  = default;
+
+  template <typename _Iter2>
+    requires is_convertible_v<_Iter2, _Iter>
+  constexpr __capacity_aware_iterator(const __capacity_aware_iterator<_Iter2, _Tag, _RangeMaxElements>& __y) noexcept
+      : __ptr_(__y.__ptr_) {
+    if !consteval {
+      __current_ = __y.__current_;
+    }
+  }
+
+  template <class _It, class _Tag2, size_t _RangeMaxElems2>
+  friend constexpr auto __make_capacity_aware_iterator(_It __iter) noexcept;
+
+private:
+  constexpr explicit __capacity_aware_iterator(_Iter __iter) : __ptr_(std::move(__iter)) {
+    if !consteval {
+      __current_ = 0u;
+    }
+  }
+
+  constexpr _Iter __curr_iter() const {
+    if consteval {
+      return __ptr_;
+    }
+
+    return std::bit_cast<_Iter>(std::bit_cast<std::uintptr_t>(__ptr_) - __current_) + __current_;
+  }
+
+  constexpr void __update(difference_type __n) {
+    if consteval {
+      __ptr_ += __n;
+    } else {
+      __current_ += __n;
+    }
+  }
+
+public:
+  [[nodiscard]] constexpr decltype(auto) operator*() const noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __current_ < _RangeMaxElements,
+          "__capacity_aware_iterator::operator*: Attempt to dereference an iterator at the end");
+    }
+
+    return *(__curr_iter());
+  }
+
+  constexpr decltype(auto) operator->() const noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __current_ < _RangeMaxElements,
+          "__capacity_aware_iterator::operator->: Attempt to dereference an iterator at the end");
+    }
+
+    return __curr_iter();
+  }
+
+  constexpr __capacity_aware_iterator& operator++() noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __current_ != _RangeMaxElements,
+          "__capacity_aware_iterator::operator++: Attempt to advance an iterator past the end");
+    }
+
+    __update(1);
+
+    return *this;
+  }
+
+  constexpr __capacity_aware_iterator operator++(int) noexcept {
+    __capacity_aware_iterator __tmp(*this);
+    ++*this;
+    return __tmp;
+  }
+
+  constexpr __capacity_aware_iterator& operator--() noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __current_ != 0u, "__capacity_aware_iterator::operator--: Attempt to rewind an iterator past the start");
+    }
+
+    __update(-1);
+
+    return *this;
+  }
+
+  constexpr __capacity_aware_iterator operator--(int) noexcept {
+    __capacity_aware_iterator __tmp(*this);
+    --*this;
+    return __tmp;
+  }
+
+  constexpr __capacity_aware_iterator& operator+=(difference_type __n) noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          (static_cast<difference_type>(__current_) + __n) >= 0,
+          "__capacity_aware_iterator::operator+=: Attempt to rewind an iterator past the start");
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          static_cast<std::size_t>(__current_ + __n) <= _RangeMaxElements,
+          "__capacity_aware_iterator::operator+=: Attempt to advance an iterator past the end");
+    }
+
+    __update(__n);
+
+    return *this;
+  }
+
+  constexpr __capacity_aware_iterator& operator-=(difference_type __n) noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          (static_cast<difference_type>(__current_) - __n) >= 0,
+          "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          static_cast<std::size_t>(__current_ - __n) <= _RangeMaxElements,
+          "__capacity_aware_iterator::operator-=: Attempt to advance an iterator past the end");
+    }
+
+    __update(-__n);
+
+    return *this;
+  }
+
+  [[nodiscard]] constexpr decltype(auto) operator[](difference_type __n) const noexcept {
+    if !consteval {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          (static_cast<difference_type>(__current_) + __n) >= 0,
+          "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the start");
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          static_cast<std::size_t>(__current_ + __n) < _RangeMaxElements,
+          "__capacity_aware_iterator::operator[]: Attempt to index an iterator at or past the end");
+    }
+    return *(*this + __n);
+  }
+
+  friend constexpr bool
+  operator==(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) noexcept {
+    return __x.__curr_iter() == __y.__curr_iter();
+  }
+
+  friend constexpr auto
+  operator<=>(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) noexcept {
+    if constexpr (three_way_comparable_with<_Iter, _Iter, strong_ordering>) {
+      return __x.__curr_iter() <=> __y.__curr_iter();
+    } else {
+      if (__x.__curr_iter() < __y.__curr_iter()) {
+        return strong_ordering::less;
+      } else if (__x.__curr_iter() == __y.__curr_iter()) {
+        return strong_ordering::equal;
+      }
+      return strong_ordering::greater;
+    }
+  }
+
+  [[nodiscard]] friend constexpr __capacity_aware_iterator
+  operator+(const __capacity_aware_iterator& __i, difference_type __n) noexcept {
+    auto __tmp = __i;
+    __tmp += __n;
+    return __tmp;
+  }
+
+  [[nodiscard]] friend constexpr __capacity_aware_iterator
+  operator+(difference_type __n, const __capacity_aware_iterator& __i) noexcept {
+    auto __tmp = __i;
+    __tmp += __n;
+    return __tmp;
+  }
+
+  [[nodiscard]] friend constexpr __capacity_aware_iterator
+  operator-(const __capacity_aware_iterator& __i, difference_type __n) noexcept {
+    auto __tmp = __i;
+    __tmp -= __n;
+    return __tmp;
+  }
+
+  [[nodiscard]] friend constexpr difference_type
+  operator-(const __capacity_aware_iterator& __x, const __capacity_aware_iterator& __y) noexcept {
+    return difference_type(__x.__curr_iter() - __y.__curr_iter());
+  }
+};
+
 template <class _It, class _Tag2, size_t _RangeMaxElems2>
 _LIBCPP_HIDE_FROM_ABI constexpr auto __make_capacity_aware_iterator(_It __iter) noexcept {
   return __capacity_aware_iterator<_It, _Tag2, _RangeMaxElems2>(__iter);
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
index f4aa5c29790ff..d84648a75e8f7 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/arithmetic.pass.cpp
@@ -33,14 +33,14 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 
-template <typename Iter>
+template <typename Iter, typename Ty = int>
 constexpr bool test() {
-  int arr[]           = {1, 2, 3, 4, 5, 6};
+  Ty arr[]            = {1, 2, 3, 4, 5, 6};
   constexpr size_t sz = std::size(arr);
 
   using CapIter = std::__capacity_aware_iterator<Iter, decltype(arr), sz>;
 
-  int* i = arr + 0;
+  Ty* i = arr + 0;
 
   // operator++()
   {
@@ -66,7 +66,8 @@ constexpr bool test() {
 
   // operator--()
   {
-    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 1));
+    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
+    ++iter;
 
     std::same_as<CapIter&> decltype(auto) res = --iter;
 
@@ -77,7 +78,8 @@ constexpr bool test() {
 
   // operator--(int)
   {
-    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 1));
+    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
+    ++iter;
 
     std::same_as<CapIter> decltype(auto) res = iter--;
 
@@ -121,7 +123,8 @@ constexpr bool test() {
 
   // operator-=(difference_type)
   {
-    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 2));
+    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
+    iter += 2;
 
     std::same_as<CapIter&> decltype(auto) res = iter -= 2;
 
@@ -132,7 +135,8 @@ constexpr bool test() {
 
   // operator-(__capacity_aware_iterator, difference_type)
   {
-    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i + 2));
+    CapIter iter = std::__make_capacity_aware_iterator<Iter, decltype(arr), sz>(Iter(i));
+    iter += 2;
 
     std::same_as<CapIter> decltype(auto) res = iter - 2;
 
@@ -162,5 +166,9 @@ int main(int, char**) {
   test<contiguous_iterator<int*>>();
   static_assert(test<contiguous_iterator<int*>>());
 
+  // bounded overload
+  test<long long*, long long>();
+  static_assert(test<long long*, long long>());
+
   return 0;
 }
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
index ceac20d549c34..889e67899d8a4 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/assert.pass.cpp
@@ -13,13 +13,64 @@
 // struct __capacity_aware_iterator;
 
 // Check assert failure if advancing, rewinding or indexing iterator past its maximum range size
+// Or if we're keeping track of the current position, if we're advancing, rewinding, indexing out of bounds.
 
 #include <__iterator/capacity_aware_iterator.h>
 #include <iterator>
 
 #include "check_assertion.h"
-#include "test_iterators.h"
-#include "test_macros.h"
+
+// Specialization where we can fit a running count inside unused alignment bits.
+template <typename Iter>
+void test_bounded() {
+  int arr[] = {1, 2};
+  int* p    = arr;
+
+  constexpr long sz = std::size(arr);
+
+  using CapIter = std::__capacity_aware_iterator<Iter, decltype(p), sz>;
+
+  CapIter it = std::__make_capacity_aware_iterator<Iter, decltype(p), sz>(Iter(arr));
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it--, "__capacity_aware_iterator::operator--: Attempt to rewind an iterator past the start");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it -= 1, "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it += -1, "__capacity_aware_iterator::operator+=: Attempt to rewind an iterator past the start");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it += (sz + 1), "__capacity_aware_iterator::operator+=: Attempt to advance an iterator past the end");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it += -(sz + 1), "__capacity_aware_iterator::operator+=: Attempt to rewind an iterator past the start");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it -= (sz + 1), "__capacity_aware_iterator::operator-=: Attempt to rewind an iterator past the start");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it -= -(sz + 1), "__capacity_aware_iterator::operator-=: Attempt to advance an iterator past the end");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it[sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator at or past the end");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it[-sz], "__capacity_aware_iterator::operator[]: Attempt to index an iterator past the start");
+
+  ++it;
+  ++it;
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      *it, "__capacity_aware_iterator::operator*: Attempt to dereference an iterator at the end");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      it.operator->(), "__capacity_aware_iterator::operator->: Attempt to dereference an iterator at the end");
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      ++it, "__capacity_aware_iterator::operator++: Attempt to advance an iterator past the end");
+}
 
 template <typename Iter>
 void test() {
@@ -57,7 +108,8 @@ void test() {
 }
 
 int main(int, char**) {
-  test<contiguous_iterator<int*>>();
+  test_bounded<int*>();
+  test<int*>();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
index 34aa73bf4459c..149f37691eae0 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/comparison.pass.cpp
@@ -30,9 +30,9 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 
-template <typename Iter>
+template <typename Iter, typename Ty = int>
 constexpr bool test() {
-  int arr[]         = {1, 2, 3, 4};
+  Ty arr[]          = {1, 2, 3, 4};
   constexpr long sz = std::size(arr);
 
   using CapIter = std::__capacity_aware_iterator<Iter, decltype(arr), sz>;
@@ -113,5 +113,8 @@ int main(int, char**) {
   test<contiguous_iterator<int*>>();
   static_assert(test<contiguous_iterator<int*>>());
 
+  test<long long*, long long>();
+  static_assert(test<long long*, long long>());
+
   return 0;
 }
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
index 42ab763896e2f..9048207d92ad2 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/dereference.pass.cpp
@@ -25,14 +25,19 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 
-struct Foo {
+struct alignas(4) Foo {
   int x;
   constexpr bool operator==(Foo const& other) const { return x == other.x; }
 };
 
-template <typename Iter>
+struct alignas(8) Foo2 {
+  int x;
+  constexpr bool operator==(Foo2 const& other) const { return x == other.x; }
+};
+
+template <typename Iter, typename Ty = Foo>
 constexpr bool test() {
-  Foo arr[]         = {Foo{1}, Foo{2}, Foo{3}, Foo{4}};
+  Ty arr[]          = {Ty{1}, Ty{2}, Ty{3}, Ty{4}};
   constexpr long sz = std::size(arr);
 
   using CapIter = std::__capacity_aware_iterator<Iter, decltype(arr), sz>;
@@ -41,7 +46,7 @@ constexpr bool test() {
 
   // operator[]
   {
-    std::same_as<Foo&> decltype(auto) res = it[0];
+    std::same_as<Ty&> decltype(auto) res = it[0];
     ASSERT_NOEXCEPT(it[0]);
     assert(res == arr[0]);
     assert(&res == &arr[0]);
@@ -56,7 +61,7 @@ constexpr bool test() {
 
   // operator*
   {
-    std::same_as<Foo&> decltype(auto) res = *it;
+    std::same_as<Ty&> decltype(auto) res = *it;
     ASSERT_NOEXCEPT(*it);
     assert(*it == arr[0]);
     assert(&res == &arr[0]);
@@ -65,7 +70,7 @@ constexpr bool test() {
 
   // operator->
   {
-    std::same_as<Foo*> decltype(auto) ptr = it.operator->();
+    std::same_as<Ty*> decltype(auto) ptr = it.operator->();
     ASSERT_NOEXCEPT(it->x);
     assert(ptr->x == 1);
     assert(ptr == &arr[0]);
@@ -78,5 +83,9 @@ int main(int, char**) {
   test<three_way_contiguous_iterator<Foo*>>();
   static_assert(test<three_way_contiguous_iterator<Foo*>>());
 
+  // bounded overload
+  test<Foo2*, Foo2>();
+  static_assert(test<Foo2*, Foo2>());
+
   return 0;
 }
diff --git a/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp b/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
index b3b72a0728ed7..e633dc67803f4 100644
--- a/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
+++ b/libcxx/test/libcxx/iterators/capacity_aware_iter/types.compile.pass.cpp
@@ -29,3 +29,15 @@ static_assert(std::is_same_v<CapIter::difference_type, std::iter_difference_t<It
 static_assert(std::is_same_v<CapIter::reference, std::iter_reference_t<It>>);
 static_assert(std::is_same_v<CapIter::reference, std::iter_reference_t<It>>);
 static_assert(std::is_same_v<CapIter::value_type, std::iter_value_t<It>>);
+
+struct alignas(8) A {};
+// bounded overload
+using It2      = A*;
+using CapIter2 = std::__capacity_aware_iterator<It2, A[], 126>;
+
+static_assert(std::is_same_v<CapIter2::iterator_category, std::iterator_traits<It2>::iterator_category>);
+static_assert(std::is_same_v<CapIter2::iterator_concept, std::contiguous_iterator_tag>);
+static_assert(std::is_same_v<CapIter2::difference_type, std::iter_difference_t<It2>>);
+static_assert(std::is_same_v<CapIter2::reference, std::iter_reference_t<It2>>);
+static_assert(std::is_same_v<CapIter2::reference, std::iter_reference_t<It2>>);
+static_assert(std::is_same_v<CapIter2::value_type, std::iter_value_t<It2>>);

``````````

</details>


https://github.com/llvm/llvm-project/pull/208271

From libcxx-commits at lists.llvm.org  Fri Jul 10 21:39:06 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Fri, 10 Jul 2026 21:39:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add compact bounded
 specialization for capacity_aware_iterator (PR #208271)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208271@github.com>
Message-ID: <6a51c8ea.3cce8983.225732.8392@mx.google.com>


smallp-o-p wrote:

It's likely this is going to become its own iterator type, but the `capacity_aware_iterator` overload is essentially what would get split out.

https://github.com/llvm/llvm-project/pull/208271

From libcxx-commits at lists.llvm.org  Fri Jul 10 23:08:04 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Fri, 10 Jul 2026 23:08:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Move release notes for
 MSVC's implementation of exception_ptr (PR #208883)
Message-ID: <llvm.org/llvm/llvm-project/pull/208883@github.com>


https://github.com/petrhosek created https://github.com/llvm/llvm-project/pull/208883

These were accidentally placed in LLVM 22 rather than 23 file.

>From 7cf1da4848749afabfe0e61d13c1568c8253638a Mon Sep 17 00:00:00 2001
From: Petr Hosek <phosek at google.com>
Date: Sat, 11 Jul 2026 06:05:28 +0000
Subject: [PATCH] [libcxx][NFC] Move release notes for MSVC's implementation of
 exception_ptr

These were accidentally placed in LLVM 22 rather than 23 file.
---
 libcxx/docs/ReleaseNotes/22.rst | 6 ------
 libcxx/docs/ReleaseNotes/23.rst | 7 +++++++
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 556fea76d33ac..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -128,9 +128,6 @@ Potentially breaking changes
   ``_LIBCPP_ABI_NO_ITERATOR_BASES``. If you are using this flag and care about ABI stability, you should set
   ``_LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER`` as well.
 
-- ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
-  The implementation was donated by Microsoft and has been refactored to follow libc++ standards and conventions.
-
 Announcements About Future Releases
 -----------------------------------
 
@@ -157,8 +154,5 @@ ABI Affecting Changes
   ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``. Please inform the libc++ team if you need this flag,
   since it will be removed in LLVM 24 if there is no evidence that it's required.
 
-- ``std::exception_ptr`` now uses the same layout on Windows as on other platforms, changing the size from two to one
-  pointer.
-
 Build System Changes
 --------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..767ada2258d51 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -98,6 +98,9 @@ Potentially breaking changes
 - The output of ``ostream << std::thread::id`` and ``std::format("...", std::thread:id)`` changes on some platforms from
   hexadecimal to decimal. This is done to have consistent formatting across all platforms libc++ supports.
 
+- ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
+  The implementation was donated by Microsoft and has been refactored to follow libc++ standards and conventions.
+
 Announcements About Future Releases
 -----------------------------------
 
@@ -125,5 +128,9 @@ ABI Affecting Changes
   map is never modified via an ``iterator``. This is necessary for reducing undefined behavior and for adding
   ``constexpr`` support for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior.
 
+- ``std::exception_ptr`` now uses the same layout in MSVC mode as on other platforms, changing the size from two to one
+  pointer.
+
+
 Build System Changes
 --------------------


From libcxx-commits at lists.llvm.org  Fri Jul 10 23:08:33 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 23:08:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Move release notes for
 MSVC's implementation of exception_ptr (PR #208883)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208883@github.com>
Message-ID: <6a51dde1.6537ae93.2f1c1b.8988@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Petr Hosek (petrhosek)

<details>
<summary>Changes</summary>

These were accidentally placed in LLVM 22 rather than 23 file.

---
Full diff: https://github.com/llvm/llvm-project/pull/208883.diff


2 Files Affected:

- (modified) libcxx/docs/ReleaseNotes/22.rst (-6) 
- (modified) libcxx/docs/ReleaseNotes/23.rst (+7) 


``````````diff
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 556fea76d33ac..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -128,9 +128,6 @@ Potentially breaking changes
   ``_LIBCPP_ABI_NO_ITERATOR_BASES``. If you are using this flag and care about ABI stability, you should set
   ``_LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER`` as well.
 
-- ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
-  The implementation was donated by Microsoft and has been refactored to follow libc++ standards and conventions.
-
 Announcements About Future Releases
 -----------------------------------
 
@@ -157,8 +154,5 @@ ABI Affecting Changes
   ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``. Please inform the libc++ team if you need this flag,
   since it will be removed in LLVM 24 if there is no evidence that it's required.
 
-- ``std::exception_ptr`` now uses the same layout on Windows as on other platforms, changing the size from two to one
-  pointer.
-
 Build System Changes
 --------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..767ada2258d51 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -98,6 +98,9 @@ Potentially breaking changes
 - The output of ``ostream << std::thread::id`` and ``std::format("...", std::thread:id)`` changes on some platforms from
   hexadecimal to decimal. This is done to have consistent formatting across all platforms libc++ supports.
 
+- ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
+  The implementation was donated by Microsoft and has been refactored to follow libc++ standards and conventions.
+
 Announcements About Future Releases
 -----------------------------------
 
@@ -125,5 +128,9 @@ ABI Affecting Changes
   map is never modified via an ``iterator``. This is necessary for reducing undefined behavior and for adding
   ``constexpr`` support for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior.
 
+- ``std::exception_ptr`` now uses the same layout in MSVC mode as on other platforms, changing the size from two to one
+  pointer.
+
+
 Build System Changes
 --------------------

``````````

</details>


https://github.com/llvm/llvm-project/pull/208883

From libcxx-commits at lists.llvm.org  Fri Jul 10 23:08:43 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Fri, 10 Jul 2026 23:08:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add MSVC's implementation of
 `exception_ptr` for Windows (PR #94977)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/94977@github.com>
Message-ID: <6a51ddeb.7270d2fd.2d4879.cde0@mx.google.com>


================
@@ -128,6 +128,9 @@ Potentially breaking changes
   ``_LIBCPP_ABI_NO_ITERATOR_BASES``. If you are using this flag and care about ABI stability, you should set
   ``_LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER`` as well.
 
+- ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
----------------
petrhosek wrote:

Thanks for catching this, I created https://github.com/llvm/llvm-project/pull/208883 to move the notes.

https://github.com/llvm/llvm-project/pull/94977

From libcxx-commits at lists.llvm.org  Fri Jul 10 23:09:46 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Fri, 10 Jul 2026 23:09:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add MSVC's implementation of
 `exception_ptr` for Windows (PR #94977)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/94977@github.com>
Message-ID: <6a51de2a.492e23e4.157d47.6c78@mx.google.com>


petrhosek wrote:

> After having this merged now, I presume we should stop linking against msvcprt/libcpmt? We have references to this in libcxx/CMakeLists.txt and in test/configs/llvm-libc++-*-clangcl.cfg.in.

Not yet, we also need to merge https://github.com/llvm/llvm-project/pull/150182.

https://github.com/llvm/llvm-project/pull/94977

From libcxx-commits at lists.llvm.org  Fri Jul 10 23:22:18 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 10 Jul 2026 23:22:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51e11a.f1c1923d.991d1.1a62@mx.google.com>


================
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Nothing To Do|","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
----------------
frederick-vs-ja wrote:

> I'd personally prefer to put 15 for consistency. Is that alright with you?

Please don't be consistent with a wrong record.

I believe we should say P1970R2 and LWG3403 were implemented in LLVM 13. `libcxx/docs/Status/Cxx20Issues.csv` should be corrected, but possibly it's OK to do that in another PR.

https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Fri Jul 10 23:23:01 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 10 Jul 2026 23:23:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Move release notes for
 MSVC's implementation of exception_ptr (PR #208883)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208883@github.com>
Message-ID: <6a51e145.bd93e731.358318.3109@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/208883

From libcxx-commits at lists.llvm.org  Fri Jul 10 23:45:22 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 23:45:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51e682.c574b9b4.21ead0.c5ee@mx.google.com>


================
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Nothing To Do|","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
----------------
inquisitivecrystal wrote:

That's fair. Someone should really go through and correct everything added by 44ae09d7, but I'm not particularly enthusiastic about taking on the work. I'll think about whether to do it anyway.

For now, I'll switch both P1970R2 and this to 13; that's at least reasonably in scope for this PR.

https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Fri Jul 10 23:56:19 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 10 Jul 2026 23:56:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51e913.27b3b003.3b871.c383@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/208866

>From bad26cea1287d99e28b807075fa016c167430bfc Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 19:05:28 -0700
Subject: [PATCH 1/3] Mark LWG3403 as "Nothing To Do"

This was implemented and tested when libc++ added ranges::ssize.
---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index d0616fb46681c..93c8c7ae28abf 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Nothing To Do|","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
 "`LWG3404 <https://wg21.link/LWG3404>`__","Finish removing subrange's conversions from pair-like","2020-11 (Virtual)","|Complete|","13","`#104282 <https://github.com/llvm/llvm-project/issues/104282>`__",""
 "`LWG3405 <https://wg21.link/LWG3405>`__","``common_view``'s converting constructor is bad, too","2020-11 (Virtual)","|Complete|","14","`#104283 <https://github.com/llvm/llvm-project/issues/104283>`__",""
 "`LWG3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","2020-11 (Virtual)","|Complete|","16","`#104284 <https://github.com/llvm/llvm-project/issues/104284>`__",""

>From 8c708b7e24dc9725d943fd6118d68980fd7f2a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 20:58:33 -0700
Subject: [PATCH 2/3] Mark LWG3403 complete as of libc++15

---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 93c8c7ae28abf..492026ab08bc2 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Nothing To Do|","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Complete|","15","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
 "`LWG3404 <https://wg21.link/LWG3404>`__","Finish removing subrange's conversions from pair-like","2020-11 (Virtual)","|Complete|","13","`#104282 <https://github.com/llvm/llvm-project/issues/104282>`__",""
 "`LWG3405 <https://wg21.link/LWG3405>`__","``common_view``'s converting constructor is bad, too","2020-11 (Virtual)","|Complete|","14","`#104283 <https://github.com/llvm/llvm-project/issues/104283>`__",""
 "`LWG3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","2020-11 (Virtual)","|Complete|","16","`#104284 <https://github.com/llvm/llvm-project/issues/104284>`__",""

>From 9da9a668c725ddd1ffb322bcd4abd5a3a3fdd3c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 23:52:41 -0700
Subject: [PATCH 3/3] Mark LWG3403 and P1970R2 completed as of libc++13

P1970R2 was previously marked as complete in libc++15 as an
approximation; however, it was actually complete in libc++13.
This updates the record to keep it consistent with LWG3403,
which is logically a correction to P1970R2 and was also completed in
libc++13.
---
 libcxx/docs/Status/Cxx20Papers.csv | 2 +-
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 3d6558ce90ef3..5539767ab800b 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -175,7 +175,7 @@
 "`P1957R2 <https://wg21.link/P1957R2>`__","Converting from ``T*``\  to bool should be considered narrowing (re: US 212)","2020-02 (Prague)","|Complete|","18","`#104205 <https://github.com/llvm/llvm-project/issues/104205>`__",""
 "`P1963R0 <https://wg21.link/P1963R0>`__","P1963R0: Fixing US 313","2020-02 (Prague)","|Nothing To Do|","","`#100039 <https://github.com/llvm/llvm-project/issues/100039>`__",""
 "`P1964R2 <https://wg21.link/P1964R2>`__","Wording for boolean-testable","2020-02 (Prague)","|Complete|","13","`#104206 <https://github.com/llvm/llvm-project/issues/104206>`__",""
-"`P1970R2 <https://wg21.link/P1970R2>`__","Consistency for size() functions: Add ranges::ssize","2020-02 (Prague)","|Complete|","15","`#104207 <https://github.com/llvm/llvm-project/issues/104207>`__",""
+"`P1970R2 <https://wg21.link/P1970R2>`__","Consistency for size() functions: Add ranges::ssize","2020-02 (Prague)","|Complete|","13","`#104207 <https://github.com/llvm/llvm-project/issues/104207>`__",""
 "`P1973R1 <https://wg21.link/P1973R1>`__","Rename ""_default_init"" Functions, Rev1","2020-02 (Prague)","|Complete|","16","`#104208 <https://github.com/llvm/llvm-project/issues/104208>`__","The feature-test macro was not set until LLVM 20."
 "`P1976R2 <https://wg21.link/P1976R2>`__","Fixed-size span construction from dynamic range","2020-02 (Prague)","|Complete|","11","`#104209 <https://github.com/llvm/llvm-project/issues/104209>`__",""
 "`P1981R0 <https://wg21.link/P1981R0>`__","Rename leap to leap_second","2020-02 (Prague)","|Complete|","19","`#104210 <https://github.com/llvm/llvm-project/issues/104210>`__",""
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 492026ab08bc2..91c4dc462c0c9 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Complete|","15","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Complete|","13","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
 "`LWG3404 <https://wg21.link/LWG3404>`__","Finish removing subrange's conversions from pair-like","2020-11 (Virtual)","|Complete|","13","`#104282 <https://github.com/llvm/llvm-project/issues/104282>`__",""
 "`LWG3405 <https://wg21.link/LWG3405>`__","``common_view``'s converting constructor is bad, too","2020-11 (Virtual)","|Complete|","14","`#104283 <https://github.com/llvm/llvm-project/issues/104283>`__",""
 "`LWG3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","2020-11 (Virtual)","|Complete|","16","`#104284 <https://github.com/llvm/llvm-project/issues/104284>`__",""


From libcxx-commits at lists.llvm.org  Sat Jul 11 00:00:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 00:00:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51ea0a.d1496011.26913c.cc3f@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 00:13:32 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sat, 11 Jul 2026 00:13:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Create libc++ 24 release notes
	(PR #208814)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208814@github.com>
Message-ID: <6a51ed1c.0c05e68e.1e82f1.aeca@mx.google.com>


Zingam wrote:

I think this is a good idea but I also think this should be done even earlier next time at least a month by then it is usually known what stuff won't make it into the current release.

https://github.com/llvm/llvm-project/pull/208814

From libcxx-commits at lists.llvm.org  Sat Jul 11 00:25:59 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 11 Jul 2026 00:25:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51f007.6537ae93.2f1c1b.0851@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 00:28:45 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 11 Jul 2026 00:28:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51f0ad.f1c1923d.991d1.f393@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.

Thanks! I shortened PR description to avoid pasting the concrete test code, as someone who want to know the detail of the test coverage should read the original commit, IMO. Feel free to modify the PR description again.

https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 00:33:54 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 00:33:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51f1e2.4987c784.63cbc.27da@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 00:37:11 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 00:37:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51f2a7.c5504292.28b3b8.ff05@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 00:38:12 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 00:38:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51f2e4.bb839515.27d21e.e76e@mx.google.com>


inquisitivecrystal wrote:

Yeah. I'm good to merge.

https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 00:42:21 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 00:42:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51f3dd.510f576d.1b3cb9.5a5e@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 00:42:31 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 00:42:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51f3e7.f06f4994.1fc857.6103@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 01:15:35 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 01:15:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a51fba7.f732ad81.3a4994.c98b@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 02:29:00 2026
From: libcxx-commits at lists.llvm.org (Rafail Shakhin ogly via libcxx-commits)
Date: Sat, 11 Jul 2026 02:29:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2077R3: Heterogeneous
 erasure overloads for associative containers (PR #174680)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/174680@github.com>
Message-ID: <6a520cdc.bb839515.27d21e.1dac@mx.google.com>


https://github.com/rsaddatimov updated https://github.com/llvm/llvm-project/pull/174680

>From 486af7ec248f1d6f003082f5899b2b4515b0e85d Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 05:11:31 +0300
Subject: [PATCH 01/23] Initial commit: start with set & multiset tests

---
 libcxx/include/__tree                         |  16 +--
 libcxx/include/map                            |  10 +-
 libcxx/include/set                            |  64 ++++++++-
 .../multiset/extract.transparent.pass.cpp     |  27 ++++
 .../erase.transparent.pass.cpp                |  27 ++++
 .../set/erase.transparent.pass.cpp            |  27 ++++
 .../set/extract.transparent.pass.cpp          |  27 ++++
 .../support/test_transparent_associative.hpp  | 124 ++++++++++++++++++
 8 files changed, 307 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/support/test_transparent_associative.hpp

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index dafeff3e2e019..fe54b53ac80b9 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1239,10 +1239,10 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   __node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source);
 
+  template <class _NodeHandle, class _Key>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const _Key&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(key_type const&);
-  template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const_iterator);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract_iterator(const_iterator);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p);
@@ -2089,7 +2089,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
   if (__nh.empty())
     return _InsertReturnType{end(), false, _NodeHandle()};
 
-  __node_pointer __ptr = __nh.__ptr_;
+  __node_pointer __ptr     = __nh.__ptr_;
   auto [__parent, __child] = __find_equal(__ptr->__get_value());
   if (__child != nullptr)
     return _InsertReturnType{
@@ -2120,19 +2120,19 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
 }
 
 template <class _Tp, class _Compare, class _Allocator>
-template <class _NodeHandle>
+template <class _NodeHandle, class _Key>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(key_type const& __key) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const& _Key __key) {
   iterator __it = __lower_bound_multi(__key);
   if (__it == end() || __value_comp_(__key, *__it))
     return _NodeHandle();
-  return __node_handle_extract<_NodeHandle>(__it);
+  return __node_handle_extract_iterator<_NodeHandle>(__it);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const_iterator __p) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_iterator(const_iterator __p) {
   __node_pointer __np = __p.__get_np();
   __remove_node_pointer(__np);
   return _NodeHandle(__np, __alloc());
diff --git a/libcxx/include/map b/libcxx/include/map
index d51daa128eada..d21e57805cb2d 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1036,8 +1036,8 @@ public:
 #  endif
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 
 #  if _LIBCPP_STD_VER >= 23
@@ -1349,7 +1349,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
@@ -2014,8 +2014,8 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it.__i_);
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
diff --git a/libcxx/include/set b/libcxx/include/set
index 59aef4a42b428..bad31ba8056cd 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -125,13 +125,19 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
     iterator  erase(const_iterator position);
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class C2>
@@ -370,6 +376,9 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     iterator insert(node_type&& nh);                                                  // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
@@ -377,6 +386,9 @@ public:
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
     iterator  erase(const_iterator first, const_iterator last);
+    template <typename K>
+      size_type erase(K&& k); // C++23
+
     void clear() noexcept;
 
     template<class C2>
@@ -812,6 +824,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
     return __tree_.erase(__f, __l);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -829,8 +853,20 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
   }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void merge(set<key_type, _Compare2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1367,6 +1403,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
     return __tree_.erase(__f, __l);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1384,8 +1432,20 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract<node_type>(__it);
+    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   merge(multiset<key_type, _Compare2, allocator_type>& __source) {
diff --git a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..fc04c8d85c267
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class multiset
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
+
+  test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
+
+  test_non_transparent_extract<std::multiset<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..a7252f0aa039c
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class multiset
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
+
+  test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
+
+  test_non_transparent_erase<std::multiset<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..d2ca1fe599374
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class set
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
+
+  test_transparent_erase<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
+
+  test_non_transparent_erase<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..62c72808b83d1
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <set>
+
+// class set
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
+
+  test_transparent_extract<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
+
+  test_non_transparent_extract<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
new file mode 100644
index 0000000000000..a59f10e426ecc
--- /dev/null
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_TRANSPARENT_ASSOCIATIVE_H
+#define TEST_TRANSPARENT_ASSOCIATIVE_H
+
+#include "test_macros.h"
+
+#include <cassert>
+
+#if TEST_STD_VER > 23
+
+template <typename T>
+struct StoredType;
+
+template <typename T>
+struct SearchedType {
+  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+
+  operator StoredType<T>() const {
+    ++*conversions_;
+    return StoredType<T>{value_};
+  }
+
+  T get_value() const { return value_; }
+
+  auto operator<=>(const SearchedType<T>&) const = default;
+
+private:
+  T value_;
+  int* conversions_;
+};
+
+template <typename T>
+struct StoredType {
+  StoredType() = default;
+  StoredType(T value) : value_(value) {}
+
+  T get_value() const { return value_; }
+
+  auto operator<=>(const StoredType<T>&) const = default;
+
+private:
+  T value_;
+};
+
+struct transparent_comparator_base {
+  using is_transparent = void;
+
+  template <typename T>
+  bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
+    return lhs.get_value() < rhs.get_value();
+  }
+
+  template <typename T>
+  bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
+    return lhs.get_value() < rhs.get_value();
+  }
+
+  template <typename T>
+  bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
+    return lhs < rhs;
+  }
+};
+
+struct transparent_comparator_final final : public transparent_comparator_base {};
+
+template <class Container>
+void test_transparent_erase(Container c) {
+  int conversions = 0;
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+
+  assert(conversions == 0);
+
+  c.erase(c.begin());
+  c.erase(c.cbegin());
+
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_erase(Container c) {
+  int conversions = 0;
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(conversions == 1);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(conversions == 2);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+  assert(conversions == 3);
+}
+
+template <class Container>
+void test_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 0);
+
+  assert(!c.extract(c.cbegin()).empty());
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(conversions == 1);
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(conversions == 2);
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 3);
+}
+
+#endif // TEST_STD_VER > 23
+
+#endif // TEST_TRANSPARENT_ASSOCIATIVE_H

>From 82b14bfaaeeb6738ec1f0560f20efdeeb0e74dce Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 06:22:38 +0300
Subject: [PATCH 02/23] Fix test & version stuff

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/ReleaseNotes/22.rst               |  1 +
 libcxx/docs/Status/Cxx23Papers.csv            |  2 +-
 libcxx/include/version                        |  2 +-
 .../map.version.compile.pass.cpp              | 32 ++++++-------------
 .../set.version.compile.pass.cpp              | 32 ++++++-------------
 .../unordered_map.version.compile.pass.cpp    | 32 ++++++-------------
 .../unordered_set.version.compile.pass.cpp    | 32 ++++++-------------
 .../version.version.compile.pass.cpp          | 32 ++++++-------------
 .../support/test_transparent_associative.hpp  |  4 +--
 .../generate_feature_test_macro_components.py |  1 -
 11 files changed, 56 insertions(+), 116 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..644d898772749 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -314,7 +314,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_allocate_at_least``                            ``202302L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_associative_heterogeneous_erasure``            *unimplemented*
+    ``__cpp_lib_associative_heterogeneous_erasure``            ``202110L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_bind_back``                                    ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 556fea76d33ac..f3862eb464b1d 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 22.0.0?
 Implemented Papers
 ------------------
 
+- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
 - P2592R3: Hashing support for ``std::chrono`` value classes (`Github <https://llvm.org/PR105358>`__)
 - P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__)
 - P2988R12: ``std::optional<T&>`` (`Github <https://llvm.org/PR148131>`__)
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 739f0ecab37e7..d6fa695026543 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -30,7 +30,7 @@
 "`P1147R1 <https://wg21.link/P1147R1>`__","Printing ``volatile`` Pointers","2021-10 (Virtual)","|Complete|","14","`#105161 <https://github.com/llvm/llvm-project/issues/105161>`__",""
 "`P1272R4 <https://wg21.link/P1272R4>`__","Byteswapping for fun&&nuf","2021-10 (Virtual)","|Complete|","14","`#105163 <https://github.com/llvm/llvm-project/issues/105163>`__",""
 "`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","","`#105164 <https://github.com/llvm/llvm-project/issues/105164>`__",""
-"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","","","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
+"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","22","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
 "`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14","`#105166 <https://github.com/llvm/llvm-project/issues/105166>`__",""
 "`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
 "`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|Complete|","22","`#105169 <https://github.com/llvm/llvm-project/issues/105169>`__",""
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..01faecd6e48b3 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -493,7 +493,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #if _LIBCPP_STD_VER >= 23
 # define __cpp_lib_adaptor_iterator_pair_constructor    202106L
 # define __cpp_lib_allocate_at_least                    202302L
-// # define __cpp_lib_associative_heterogeneous_erasure    202110L
+# define __cpp_lib_associative_heterogeneous_erasure    202110L
 # define __cpp_lib_bind_back                            202202L
 # define __cpp_lib_byteswap                             202110L
 # define __cpp_lib_constexpr_bitset                     202207L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
index 794c2ee9ad086..587c90a02fec4 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
@@ -247,17 +247,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -332,17 +326,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
index d1a0570557246..8531a257b64a6 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
@@ -209,17 +209,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -274,17 +268,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
index 221d8aaebc14b..ba777e5434008 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
@@ -225,17 +225,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -306,17 +300,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
index d1c1335df7c80..b168215ea737b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
@@ -187,17 +187,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -248,17 +242,11 @@
 #    error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L 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 f2d74cc99d864..153219940d1d3 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
@@ -4680,17 +4680,11 @@
 #    error "__cpp_lib_as_const should have the value 201510L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
 #  endif
 
 #  ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -6373,17 +6367,11 @@
 #    error "__cpp_lib_as_const should have the value 201510L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-#    endif
-#    if __cpp_lib_associative_heterogeneous_erasure != 202110L
-#      error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_associative_heterogeneous_erasure
-#      error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_associative_heterogeneous_erasure
+#    error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+#  endif
+#  if __cpp_lib_associative_heterogeneous_erasure != 202110L
+#    error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index a59f10e426ecc..6ec13d29000ea 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -13,7 +13,7 @@
 
 #include <cassert>
 
-#if TEST_STD_VER > 23
+#if TEST_STD_VER >= 23
 
 template <typename T>
 struct StoredType;
@@ -119,6 +119,6 @@ void test_non_transparent_extract(Container c) {
   assert(conversions == 3);
 }
 
-#endif // TEST_STD_VER > 23
+#endif // TEST_STD_VER >= 23
 
 #endif // TEST_TRANSPARENT_ASSOCIATIVE_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..0ad09ba56178c 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -152,7 +152,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_associative_heterogeneous_erasure",
             "values": {"c++23": 202110},
             "headers": ["map", "set", "unordered_map", "unordered_set"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_associative_heterogeneous_insertion",

>From de236022944518d904fe25347e9644817027ae70 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 17:10:53 +0300
Subject: [PATCH 03/23] Don't uglify nodiscard

---
 libcxx/include/set | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/set b/libcxx/include/set
index bad31ba8056cd..ed063bd8d4f27 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -861,7 +861,7 @@ public:
   template <class _Kp>
     requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
              !is_convertible_v<_Kp &&, const_iterator>)
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
     return __tree_.template __node_handle_extract<node_type>(__kp);
   }
 
@@ -1440,7 +1440,7 @@ public:
   template <class _Kp>
     requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
              !is_convertible_v<_Kp &&, const_iterator>)
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
     return __tree_.template __node_handle_extract<node_type>(__kp);
   }
 

>From 657dcc446c73dc9618b0620f9b71fc81bb5b41c0 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 17:29:05 +0300
Subject: [PATCH 04/23] Overloads for map & multimap

---
 libcxx/include/map                            | 59 +++++++++++++++++++
 .../map.erasure/erase.transparent.pass.cpp    | 28 +++++++++
 .../extract.transparent.pass.cpp              | 27 +++++++++
 .../erase.transparent.pass.cpp                | 29 +++++++++
 .../extract.transparent.pass.cpp              | 27 +++++++++
 5 files changed, 170 insertions(+)
 create mode 100644 libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp

diff --git a/libcxx/include/map b/libcxx/include/map
index d21e57805cb2d..3df87d01c87c2 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -147,6 +147,9 @@ public:
 
     constexpr node_type extract(const_iterator position);                                       // C++17
     constexpr node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      constexpr node_type extract(K&& k);                                                       // C++23
+
     constexpr insert_return_type insert(node_type&& nh);                                        // C++17
     constexpr iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
@@ -170,7 +173,10 @@ public:
     constexpr iterator  erase(const_iterator position);
     constexpr iterator  erase(iterator position);                         // C++14
     constexpr size_type erase(const key_type& k);
+    template <typename K>
+      constexpr size_type erase(K&& k);                                   // C++23
     constexpr iterator  erase(const_iterator first, const_iterator last);
+
     constexpr void clear() noexcept;
 
     template<class C2>
@@ -426,13 +432,19 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                                       // C++23
+
     iterator insert(node_type&& nh);                                                  // C++17
     iterator insert(const_iterator hint, node_type&& nh);                             // C++17
 
     iterator  erase(const_iterator position);
     iterator  erase(iterator position); // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class C2>
@@ -1332,6 +1344,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
     return __tree_.erase(__f.__i_, __l.__i_);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1351,6 +1375,18 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   merge(map<key_type, mapped_type, _Compare2, allocator_type>& __source) {
@@ -2000,6 +2036,17 @@ public:
     return __tree_.erase(__f.__i_, __l.__i_);
   }
 
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __tree_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
 #  if _LIBCPP_STD_VER >= 17
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(node_type&& __nh) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
@@ -2017,6 +2064,18 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
   }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __tree_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _Compare2>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
   merge(multimap<key_type, mapped_type, _Compare2, allocator_type>& __source) {
diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..2fe83c20acf67
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class map
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_final>>(
+      {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_non_transparent_erase<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..0dd4d9afdeb9b
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class map
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_non_transparent_extract<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..7e3f625370d73
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class multimap
+
+//    template<typename K>
+//        size_type erase(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
+      {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_final>>(
+      {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+  test_non_transparent_erase<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..9faa74f69e2b0
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <map>
+
+// class multimap
+
+//    template<typename K>
+//        node_type extract(K&& k) const;        // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+  test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
+
+  test_non_transparent_extract<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}

>From 5324b8c74caa5f3b21e947fa39a437297b696cee Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 18:12:03 +0300
Subject: [PATCH 05/23] Overloads for unordered_map & unordered_multimap

---
 libcxx/include/__hash_table                   | 16 +--
 libcxx/include/unordered_map                  | 62 +++++++++++-
 libcxx/include/unordered_set                  |  4 +-
 .../unord.map/erase.transparent.pass.cpp      | 56 +++++++++++
 .../extract.transparent.pass.cpp              | 56 +++++++++++
 .../unord.multimap/erase.transparent.pass.cpp | 56 +++++++++++
 .../extract.transparent.pass.cpp              | 56 +++++++++++
 .../test/support/test_transparent_unordered.h | 99 ++++++++++++++-----
 8 files changed, 366 insertions(+), 39 deletions(-)
 create mode 100644 libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp

diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 0ce4cf3edd6fd..ff3d0f1ab7749 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -896,10 +896,10 @@ public:
   template <class _Table>
   _LIBCPP_HIDE_FROM_ABI void __node_handle_merge_multi(_Table& __source);
 
+  template <class _NodeHandle, class _Key>
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const _Key& __key);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(key_type const& __key);
-  template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const_iterator __it);
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_iterator(const_iterator __it);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
@@ -1648,18 +1648,18 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_insert_unique(const_iter
 }
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
-template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle
-__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(key_type const& __key) {
+template <class _NodeHandle, class _Key>
+_LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(const _Key& __key) {
   iterator __i = find(__key);
   if (__i == end())
     return _NodeHandle();
-  return __node_handle_extract<_NodeHandle>(__i);
+  return __node_handle_extract_iterator<_NodeHandle>(__i);
 }
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(const_iterator __p) {
+_LIBCPP_HIDE_FROM_ABI _NodeHandle
+__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_iterator(const_iterator __p) {
   allocator_type __alloc(__node_alloc());
   return _NodeHandle(remove(__p).release(), __alloc);
 }
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index b8b160cd892d4..6461af87df307 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -139,6 +139,9 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
+    template <typename K>
+      size_type erase(K&& k);                                                         // C++23
+
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator           insert(const_iterator hint, node_type&& nh);                   // C++17
 
@@ -162,7 +165,10 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -425,13 +431,19 @@ public:
 
     node_type extract(const_iterator position);                // C++17
     node_type extract(const key_type& x);                      // C++17
+    template <typename K>
+      node_type extract(K&& k);                                // C++23
+
     iterator insert(node_type&& nh);                           // C++17
     iterator insert(const_iterator hint, node_type&& nh);      // C++17
 
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -1158,6 +1170,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first.__i_, __last.__i_);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1175,9 +1199,20 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
   }
 
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_map<key_type, mapped_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1896,6 +1931,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first.__i_, __last.__i_);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -1913,9 +1960,20 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
   }
 
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_multimap<key_type, mapped_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 10f8b730b842b..0f6dc12af2cc7 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -799,7 +799,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract<node_type>(__it);
+    return __table_.template __node_handle_extract_iterator<node_type>(__it);
   }
 
   template <class _H2, class _P2>
@@ -1400,7 +1400,7 @@ public:
     return __table_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __position) {
-    return __table_.template __node_handle_extract<node_type>(__position);
+    return __table_.template __node_handle_extract_iterator<node_type>(__position);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
     return __table_.template __node_handle_extract<node_type>(__key);
diff --git a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..2d0138ef6472f
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_map
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..08a2e05e777bc
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_map
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..b15d915a618d9
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_multimap
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..8eefc2b5f689b
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_map>
+
+// class unordered_multimap
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index e2d02402d808b..f77688b42e194 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -46,7 +46,7 @@ struct transparent_equal_final final : std::equal_to<> {};
 
 template <typename T>
 struct SearchedType {
-  explicit SearchedType(T value, int *counter) : value_(value), conversions_(counter) { }
+  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
 
   // Whenever a conversion is performed, increment the counter to keep track
   // of conversions.
@@ -55,48 +55,40 @@ struct SearchedType {
     return StoredType<T>{value_};
   }
 
-  int get_value() const {
-    return value_;
-  }
+  int get_value() const { return value_; }
 
 private:
   T value_;
-  int *conversions_;
+  int* conversions_;
 };
 
 template <typename T>
 struct StoredType {
   StoredType() = default;
-  StoredType(T value) : value_(value) { }
+  StoredType(T value) : value_(value) {}
 
-  friend bool operator==(StoredType const& lhs, StoredType const& rhs) {
-    return lhs.value_ == rhs.value_;
-  }
+  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
 
   // If we're being passed a SearchedType<T> object, avoid the conversion
   // to T. This allows testing that the transparent operations are correctly
   // forwarding the SearchedType all the way to this comparison by checking
   // that we didn't have a conversion when we search for a SearchedType<T>
   // in a container full of StoredType<T>.
-  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
-    return lhs.value_ == rhs.get_value();
-  }
+  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
 
-  int get_value() const {
-    return value_;
-  }
+  int get_value() const { return value_; }
 
 private:
   T value_;
 };
 
-template<template<class...> class UnorderedSet, class Hash, class Equal>
+template <template <class...> class UnorderedSet, class Hash, class Equal>
 using unord_set_type = UnorderedSet<StoredType<int>, Hash, Equal>;
 
-template<template<class...> class UnorderedMap, class Hash, class Equal>
+template <template <class...> class UnorderedMap, class Hash, class Equal>
 using unord_map_type = UnorderedMap<StoredType<int>, int, Hash, Equal>;
 
-template<class Container>
+template <class Container>
 void test_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -105,7 +97,7 @@ void test_transparent_find(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -116,7 +108,7 @@ void test_non_transparent_find(Container c) {
   assert(conversions == 3);
 }
 
-template<class Container>
+template <class Container>
 void test_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -125,7 +117,7 @@ void test_transparent_count(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -136,7 +128,7 @@ void test_non_transparent_count(Container c) {
   assert(conversions == 3);
 }
 
-template<class Container>
+template <class Container>
 void test_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -145,7 +137,7 @@ void test_transparent_contains(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -156,10 +148,10 @@ void test_non_transparent_contains(Container c) {
   assert(conversions == 3);
 }
 
-template<class Container>
+template <class Container>
 void test_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   iters = c.equal_range(SearchedType<int>(2, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
@@ -168,10 +160,10 @@ void test_transparent_equal_range(Container c) {
   assert(conversions == 0);
 }
 
-template<class Container>
+template <class Container>
 void test_non_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   assert(conversions == 1);
   iters = c.equal_range(SearchedType<int>(2, &conversions));
@@ -182,6 +174,59 @@ void test_non_transparent_equal_range(Container c) {
   assert(conversions == 3);
 }
 
+#  if TEST_STD_VER >= 23
+
+template <class Container>
+void test_transparent_erase(Container c) {
+  int conversions = 0;
+
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+  assert(conversions == 0);
+
+  c.erase(c.begin());
+  c.erase(c.cbegin());
+
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_erase(Container c) {
+  int conversions = 0;
+  assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+  assert(conversions == 1);
+  assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+  assert(conversions == 2);
+  assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+  assert(conversions == 3);
+}
+
+template <class Container>
+void test_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 0);
+
+  assert(!c.extract(c.cbegin()).empty());
+  assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_extract(Container c) {
+  int conversions = 0;
+  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+  assert(conversions == 1);
+  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+  assert(conversions == 2);
+  assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+  assert(conversions == 3);
+}
+
+#  endif // TEST_STD_VER >= 23
+
 #endif // TEST_STD_VER > 17
 
 #endif // TEST_TRANSPARENT_UNORDERED_H

>From 5475ac1a0ce2e3ed9d5e5968bbd4ed4c6e207cc9 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 19:12:42 +0300
Subject: [PATCH 06/23] Overloads for unordered_set & unordered_multiset

---
 libcxx/include/unordered_map                  |  2 +-
 libcxx/include/unordered_set                  | 58 +++++++++++++++++++
 .../unord.multiset/erase.transparent.pass.cpp | 56 ++++++++++++++++++
 .../extract.transparent.pass.cpp              | 56 ++++++++++++++++++
 .../unord.set/erase.transparent.pass.cpp      | 56 ++++++++++++++++++
 .../unord.set/extract.transparent.pass.cpp    | 56 ++++++++++++++++++
 .../test/support/test_transparent_unordered.h |  4 +-
 7 files changed, 285 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
 create mode 100644 libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp

diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 6461af87df307..54d878435fa1c 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -140,7 +140,7 @@ public:
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
     template <typename K>
-      size_type erase(K&& k);                                                         // C++23
+      node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
     iterator           insert(const_iterator hint, node_type&& nh);                   // C++17
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 0f6dc12af2cc7..7e7e6708539f7 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -131,13 +131,19 @@ public:
 
     node_type extract(const_iterator position);                       // C++17
     node_type extract(const key_type& x);                             // C++17
+    template <typename K>
+      node_type extract(K&& k);                                       // C++23
+
     insert_return_type insert(node_type&& nh);                        // C++17
     iterator           insert(const_iterator hint, node_type&& nh);   // C++17
 
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -379,13 +385,19 @@ public:
 
     node_type extract(const_iterator position);             // C++17
     node_type extract(const key_type& x);                   // C++17
+    template <typename K>
+      node_type extract(K&& k);                             // C++23
+
     iterator insert(node_type&& nh);                        // C++17
     iterator insert(const_iterator hint, node_type&& nh);   // C++17
 
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
+    template <typename K>
+      size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
+
     void clear() noexcept;
 
     template<class H2, class P2>
@@ -782,6 +794,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first, __last);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_unique(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
 #  if _LIBCPP_STD_VER >= 17
@@ -802,6 +826,17 @@ public:
     return __table_.template __node_handle_extract_iterator<node_type>(__it);
   }
 
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_set<key_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1406,6 +1441,17 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
 
+#    if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+    return __table_.template __node_handle_extract<node_type>(__kp);
+  }
+
+#    endif // _LIBCPP_STD_VER >= 23
+
   template <class _H2, class _P2>
   _LIBCPP_HIDE_FROM_ABI void merge(unordered_multiset<key_type, _H2, _P2, allocator_type>& __source) {
     _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1437,6 +1483,18 @@ public:
   _LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
     return __table_.erase(__first, __last);
   }
+
+#  if _LIBCPP_STD_VER >= 23
+
+  template <class _Kp>
+    requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+             !is_convertible_v<_Kp &&, const_iterator>)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+    return __table_.__erase_multi(__kp);
+  }
+
+#  endif // _LIBCPP_STD_VER >= 23
+
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
 
   _LIBCPP_HIDE_FROM_ABI void swap(unordered_multiset& __u) _NOEXCEPT_(__is_nothrow_swappable_v<__table>) {
diff --git a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..41420d41613b1
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_multiset
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..e094201a8cedd
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_multiset
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..efc55a0ea8964
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_set
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<>>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash_final, transparent_equal_final>;
+    test_transparent_erase<S>({1, 2, 4, 5});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_erase<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..e84facdaabcb0
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <unordered_set>
+
+// class unordered_set
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+  using key_type = StoredType<int>;
+
+  {
+    // Make sure conversions don't happen for transparent non-final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<>>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions don't happen for transparent final hasher and key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash_final, transparent_equal_final>;
+    test_transparent_extract<S>({1, 2, 4});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent hasher
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for non-transparent key_equal
+    using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  {
+    // Make sure conversions do happen for both non-transparent hasher and key_equal
+    using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<key_type>>;
+    test_non_transparent_extract<S>({1, 2});
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index f77688b42e194..f2439be716d44 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -55,7 +55,7 @@ struct SearchedType {
     return StoredType<T>{value_};
   }
 
-  int get_value() const { return value_; }
+  T get_value() const { return value_; }
 
 private:
   T value_;
@@ -76,7 +76,7 @@ struct StoredType {
   // in a container full of StoredType<T>.
   friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
 
-  int get_value() const { return value_; }
+  T get_value() const { return value_; }
 
 private:
   T value_;

>From 05d19061394ca0449488589a5ae127838450b584 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 19:22:02 +0300
Subject: [PATCH 07/23] nodiscard test cases

---
 .../containers/associative/map/map.nodiscard.verify.cpp       | 4 ++++
 .../containers/associative/map/multimap.nodiscard.verify.cpp  | 4 ++++
 .../containers/associative/set/multiset.nodiscard.verify.cpp  | 4 ++++
 .../containers/associative/set/set.nodiscard.verify.cpp       | 4 ++++
 .../libcxx/containers/unord/unord.map/nodiscard.verify.cpp    | 4 ++++
 .../containers/unord/unord.multimap/nodiscard.verify.cpp      | 4 ++++
 .../containers/unord/unord.multiset/nodiscard.verify.cpp      | 4 ++++
 .../libcxx/containers/unord/unord.set/nodiscard.verify.cpp    | 4 ++++
 8 files changed, 32 insertions(+)

diff --git a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
index 20218b50e6c60..7a418502a722b 100644
--- a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
@@ -121,4 +121,8 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
index 99e4fbceabf12..49ac8c6a76064 100644
--- a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
@@ -106,4 +106,8 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
index 889b3cc62d191..a90f69fc96af4 100644
--- a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
@@ -104,4 +104,8 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
index 0453541d1b862..02f2634363679 100644
--- a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
@@ -104,4 +104,8 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
index 0747556905200..34df0f1d44335 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
@@ -81,6 +81,10 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   m[key];            // no-warning
   m[std::move(key)]; // no-warning
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
index cdb47d2b514ab..9ce6bb6a5d46c 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
@@ -81,6 +81,10 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   m.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
index ff62874caf01b..dbb10eaa40dd3 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
@@ -83,6 +83,10 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
index 628c31a93099f..d289d0ebfbfd0 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
@@ -82,6 +82,10 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 

>From 02dfdf82d6cb64055dc057ee2fe5a91dafe8487a Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 18:55:54 +0300
Subject: [PATCH 08/23] Refactor nodiscard tests

---
 .../associative/map/map.nodiscard.verify.cpp   | 18 +++++++++---------
 .../map/multimap.nodiscard.verify.cpp          | 18 +++++++++---------
 .../set/multiset.nodiscard.verify.cpp          | 18 +++++++++---------
 .../associative/set/set.nodiscard.verify.cpp   | 18 +++++++++---------
 .../unord/unord.map/nodiscard.verify.cpp       | 18 +++++++++---------
 .../unord/unord.multimap/nodiscard.verify.cpp  | 18 +++++++++---------
 .../unord/unord.multiset/nodiscard.verify.cpp  | 18 +++++++++---------
 .../unord/unord.set/nodiscard.verify.cpp       | 17 +++++++++--------
 8 files changed, 72 insertions(+), 71 deletions(-)

diff --git a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
index 7a418502a722b..c21456965190b 100644
--- a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
@@ -74,19 +74,23 @@ void test() {
   m.key_comp();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.value_comp();    // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
+#if TEST_STD_VER >= 14
+  std::map<int, int, TransparentCompare> tm;
+  const std::map<int, int, TransparentCompare> ctm{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(key);      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.cend()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::map<int, int, TransparentCompare> tm;
-  const std::map<int, int, TransparentCompare> ctm{};
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -121,8 +125,4 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
index 49ac8c6a76064..bb38153b22a8e 100644
--- a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
@@ -59,19 +59,23 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 14
+  std::multimap<int, int, TransparentCompare> tm;
+  const std::multimap<int, int, TransparentCompare> ctm{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(key);      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.cend()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::multimap<int, int, TransparentCompare> tm;
-  const std::multimap<int, int, TransparentCompare> ctm{};
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -106,8 +110,4 @@ void test() {
   tm.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
index a90f69fc96af4..3a762b059e89a 100644
--- a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
@@ -53,10 +53,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 14
+  std::multiset<int, TransparentCompare> ts;
+  const std::multiset<int, TransparentCompare> cts{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   s.extract(key);       // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.extract(s.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   s.get_allocator(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.key_comp();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -65,11 +74,6 @@ void test() {
   s.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cs.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::multiset<int, TransparentCompare> ts;
-  const std::multiset<int, TransparentCompare> cts{};
-
-  TransparentKey tkey;
-
   ts.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -104,8 +108,4 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
index 02f2634363679..acee396bfbb99 100644
--- a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
@@ -53,10 +53,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 14
+  std::set<int, TransparentCompare> ts;
+  const std::set<int, TransparentCompare> cts{};
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   s.extract(key);       // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.extract(s.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   s.get_allocator(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   s.key_comp();      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -65,11 +74,6 @@ void test() {
   s.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cs.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 14
-  std::set<int, TransparentCompare> ts;
-  const std::set<int, TransparentCompare> cts{};
-
-  TransparentKey tkey;
-
   ts.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -104,8 +108,4 @@ void test() {
   ts.equal_range(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
-
-#if TEST_STD_VER >= 23
-  ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
 }
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
index 34df0f1d44335..19e6764c1f064 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
+  const std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(0);         // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
-  const std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -81,10 +85,6 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   m[key];            // no-warning
   m[std::move(key)]; // no-warning
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
index 9ce6bb6a5d46c..511d9e2062d48 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
+  const std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   m.extract(0);         // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.extract(m.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   m.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
   m.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
-  const std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
-
-  TransparentKey tkey;
-
   tm.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -81,10 +85,6 @@ void test() {
   ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   m.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   m.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
index dbb10eaa40dd3..1f0e76a412017 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
+  const std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   us.extract(key);        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.extract(us.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   us.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
   us.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cus.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
-  const std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
-
-  TransparentKey tkey;
-
   tus.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctus.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -83,10 +87,6 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
index d289d0ebfbfd0..2a3aca497e8c4 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
 
   int key = 0;
 
+#if TEST_STD_VER >= 20
+  std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
+  const std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
+  TransparentKey tkey;
+#endif
+
 #if TEST_STD_VER >= 17
   us.extract(key);        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.extract(us.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
+#if TEST_STD_VER >= 23
+  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
 
   us.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.key_eq();        // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,10 +66,6 @@ void test() {
   us.find(key);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   cus.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #if TEST_STD_VER >= 20
-  std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
-  const std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
-  TransparentKey tkey;
-
   tus.find(tkey);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   ctus.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
@@ -82,10 +87,6 @@ void test() {
   ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
-#if TEST_STD_VER >= 23
-  tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
   us.bucket_count();     // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 

>From 9390d2601c63efabf87f763fd3ce15360f9d5c94 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 19:02:05 +0300
Subject: [PATCH 09/23] Drop reformatting

---
 libcxx/include/__tree                         |  2 +-
 .../test/support/test_transparent_unordered.h | 46 +++++++++++--------
 2 files changed, 28 insertions(+), 20 deletions(-)

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index fe54b53ac80b9..b25fcbcba5ff7 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -2089,7 +2089,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
   if (__nh.empty())
     return _InsertReturnType{end(), false, _NodeHandle()};
 
-  __node_pointer __ptr     = __nh.__ptr_;
+  __node_pointer __ptr = __nh.__ptr_;
   auto [__parent, __child] = __find_equal(__ptr->__get_value());
   if (__child != nullptr)
     return _InsertReturnType{
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index f2439be716d44..5aee507191351 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -46,7 +46,7 @@ struct transparent_equal_final final : std::equal_to<> {};
 
 template <typename T>
 struct SearchedType {
-  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+  explicit SearchedType(T value, int *counter) : value_(value), conversions_(counter) { }
 
   // Whenever a conversion is performed, increment the counter to keep track
   // of conversions.
@@ -55,40 +55,48 @@ struct SearchedType {
     return StoredType<T>{value_};
   }
 
-  T get_value() const { return value_; }
+  int get_value() const {
+    return value_;
+  }
 
 private:
   T value_;
-  int* conversions_;
+  int *conversions_;
 };
 
 template <typename T>
 struct StoredType {
   StoredType() = default;
-  StoredType(T value) : value_(value) {}
+  StoredType(T value) : value_(value) { }
 
-  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+  friend bool operator==(StoredType const& lhs, StoredType const& rhs) {
+    return lhs.value_ == rhs.value_;
+  }
 
   // If we're being passed a SearchedType<T> object, avoid the conversion
   // to T. This allows testing that the transparent operations are correctly
   // forwarding the SearchedType all the way to this comparison by checking
   // that we didn't have a conversion when we search for a SearchedType<T>
   // in a container full of StoredType<T>.
-  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
+    return lhs.value_ == rhs.get_value();
+  }
 
-  T get_value() const { return value_; }
+  int get_value() const {
+    return value_;
+  }
 
 private:
   T value_;
 };
 
-template <template <class...> class UnorderedSet, class Hash, class Equal>
+template<template<class...> class UnorderedSet, class Hash, class Equal>
 using unord_set_type = UnorderedSet<StoredType<int>, Hash, Equal>;
 
-template <template <class...> class UnorderedMap, class Hash, class Equal>
+template<template<class...> class UnorderedMap, class Hash, class Equal>
 using unord_map_type = UnorderedMap<StoredType<int>, int, Hash, Equal>;
 
-template <class Container>
+template<class Container>
 void test_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -97,7 +105,7 @@ void test_transparent_find(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_find(Container c) {
   int conversions = 0;
   assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -108,7 +116,7 @@ void test_non_transparent_find(Container c) {
   assert(conversions == 3);
 }
 
-template <class Container>
+template<class Container>
 void test_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -117,7 +125,7 @@ void test_transparent_count(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_count(Container c) {
   int conversions = 0;
   assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -128,7 +136,7 @@ void test_non_transparent_count(Container c) {
   assert(conversions == 3);
 }
 
-template <class Container>
+template<class Container>
 void test_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -137,7 +145,7 @@ void test_transparent_contains(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_contains(Container c) {
   int conversions = 0;
   assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -148,10 +156,10 @@ void test_non_transparent_contains(Container c) {
   assert(conversions == 3);
 }
 
-template <class Container>
+template<class Container>
 void test_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   iters = c.equal_range(SearchedType<int>(2, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
@@ -160,10 +168,10 @@ void test_transparent_equal_range(Container c) {
   assert(conversions == 0);
 }
 
-template <class Container>
+template<class Container>
 void test_non_transparent_equal_range(Container c) {
   int conversions = 0;
-  auto iters      = c.equal_range(SearchedType<int>(1, &conversions));
+  auto iters = c.equal_range(SearchedType<int>(1, &conversions));
   assert(std::distance(iters.first, iters.second) > 0);
   assert(conversions == 1);
   iters = c.equal_range(SearchedType<int>(2, &conversions));

>From b5d01386a66f15ce33a956b6d26f0209f12c796e Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 19:07:29 +0300
Subject: [PATCH 10/23] typename ==> class

---
 libcxx/include/map           | 2 +-
 libcxx/include/set           | 4 ++--
 libcxx/include/unordered_map | 4 ++--
 libcxx/include/unordered_set | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/map b/libcxx/include/map
index 3df87d01c87c2..d148bebad4e91 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -432,7 +432,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     iterator insert(node_type&& nh);                                                  // C++17
diff --git a/libcxx/include/set b/libcxx/include/set
index ed063bd8d4f27..9f43a018021c6 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -125,7 +125,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
@@ -376,7 +376,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     iterator insert(node_type&& nh);                                                  // C++17
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 54d878435fa1c..7d3f058933f46 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -139,7 +139,7 @@ public:
 
     node_type extract(const_iterator position);                                       // C++17
     node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                                       // C++23
 
     insert_return_type insert(node_type&& nh);                                        // C++17
@@ -431,7 +431,7 @@ public:
 
     node_type extract(const_iterator position);                // C++17
     node_type extract(const key_type& x);                      // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                // C++23
 
     iterator insert(node_type&& nh);                           // C++17
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 7e7e6708539f7..ff352709636a7 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -131,7 +131,7 @@ public:
 
     node_type extract(const_iterator position);                       // C++17
     node_type extract(const key_type& x);                             // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                                       // C++23
 
     insert_return_type insert(node_type&& nh);                        // C++17

>From e61a8edd544e2dd5920dbeedafe21b72026251a2 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 20:11:11 +0300
Subject: [PATCH 11/23] Better extract testing

---
 .../support/test_transparent_associative.hpp  | 56 ++++++++++++++-----
 .../test/support/test_transparent_unordered.h | 50 ++++++++++++-----
 2 files changed, 77 insertions(+), 29 deletions(-)

diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index 6ec13d29000ea..2448f3cd16083 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -41,6 +41,10 @@ struct StoredType {
   StoredType() = default;
   StoredType(T value) : value_(value) {}
 
+  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+
+  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+
   T get_value() const { return value_; }
 
   auto operator<=>(const StoredType<T>&) const = default;
@@ -72,17 +76,17 @@ struct transparent_comparator_final final : public transparent_comparator_base {
 
 template <class Container>
 void test_transparent_erase(Container c) {
+  static_assert(
+      std::same_as<
+          typename Container::size_type,
+          std::invoke_result_t<decltype(&Container::template erase<SearchedType<int>>), Container, SearchedType<int>>>);
+
   int conversions = 0;
+
   assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
-
   assert(conversions == 0);
-
-  c.erase(c.begin());
-  c.erase(c.cbegin());
-
-  assert(c.empty());
 }
 
 template <class Container>
@@ -96,25 +100,47 @@ void test_non_transparent_erase(Container c) {
   assert(conversions == 3);
 }
 
+template <typename NodeHandle>
+concept node_handle_has_key = requires(NodeHandle nh) {
+  { nh.key() };
+};
+
+template <class T, class Container>
+void test_single_extract(SearchedType<T> key, Container& c) {
+  auto node_handle = c.extract(key);
+
+  assert(!node_handle.empty());
+
+  if constexpr (node_handle_has_key<typename Container::node_type>) {
+    assert(node_handle.key() == key);
+  } else {
+    assert(node_handle.value() == key);
+  }
+}
+
 template <class Container>
 void test_transparent_extract(Container c) {
+  static_assert(std::same_as< typename Container::node_type,
+                              std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
+                                                   Container,
+                                                   SearchedType<int>>>);
+
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 0);
-
-  assert(!c.extract(c.cbegin()).empty());
-  assert(c.empty());
 }
 
 template <class Container>
 void test_non_transparent_extract(Container c) {
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(conversions == 1);
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
-  assert(conversions == 2);
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 3);
 }
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index 5aee507191351..1bc6fb07dee6f 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -186,17 +186,17 @@ void test_non_transparent_equal_range(Container c) {
 
 template <class Container>
 void test_transparent_erase(Container c) {
+  static_assert(
+      std::same_as<
+          typename Container::size_type,
+          std::invoke_result_t<decltype(&Container::template erase<SearchedType<int>>), Container, SearchedType<int>>>);
+
   int conversions = 0;
 
   assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
   assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
   assert(conversions == 0);
-
-  c.erase(c.begin());
-  c.erase(c.cbegin());
-
-  assert(c.empty());
 }
 
 template <class Container>
@@ -210,25 +210,47 @@ void test_non_transparent_erase(Container c) {
   assert(conversions == 3);
 }
 
+template <typename NodeHandle>
+concept node_handle_has_key = requires(NodeHandle nh) {
+  { nh.key() };
+};
+
+template <class T, class Container>
+void test_single_extract(SearchedType<T> key, Container& c) {
+  auto node_handle = c.extract(key);
+
+  assert(!node_handle.empty());
+
+  if constexpr (node_handle_has_key<typename Container::node_type>) {
+    assert(node_handle.key() == key);
+  } else {
+    assert(node_handle.value() == key);
+  }
+}
+
 template <class Container>
 void test_transparent_extract(Container c) {
+  static_assert(std::same_as< typename Container::node_type,
+                              std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
+                                                   Container,
+                                                   SearchedType<int>>>);
+
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 0);
-
-  assert(!c.extract(c.cbegin()).empty());
-  assert(c.empty());
 }
 
 template <class Container>
 void test_non_transparent_extract(Container c) {
   int conversions = 0;
-  assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
-  assert(conversions == 1);
-  assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
-  assert(conversions == 2);
+
+  test_single_extract(SearchedType<int>(1, &conversions), c);
+  test_single_extract(SearchedType<int>(2, &conversions), c);
+
   assert(c.extract(SearchedType<int>(3, &conversions)).empty());
   assert(conversions == 3);
 }

>From f001a6891957ea9b7660a7baf43469769e4dc844 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 20:43:59 +0300
Subject: [PATCH 12/23] Include concepts in test

---
 libcxx/test/support/test_transparent_associative.hpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index 2448f3cd16083..dae3acc725162 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -15,6 +15,8 @@
 
 #if TEST_STD_VER >= 23
 
+#  include <concepts>
+
 template <typename T>
 struct StoredType;
 

>From be0d4ff04dd134b9361deb87e6661419de16e2ab Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 21:04:33 +0300
Subject: [PATCH 13/23] Include type_traits in test

---
 libcxx/test/support/test_transparent_associative.hpp | 1 +
 libcxx/test/support/test_transparent_unordered.h     | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index dae3acc725162..e3cac5e8a51cb 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -16,6 +16,7 @@
 #if TEST_STD_VER >= 23
 
 #  include <concepts>
+#  include <type_traits>
 
 template <typename T>
 struct StoredType;
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index 1bc6fb07dee6f..14faea4d8e5b7 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -16,6 +16,9 @@
 
 #if TEST_STD_VER > 17
 
+#  include <concepts>
+#  include <type_traits>
+
 template <typename T>
 struct StoredType;
 

>From 739a715f45ec2d2372fc922f7b7324bae8ebba01 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 10 Jan 2026 02:07:55 +0300
Subject: [PATCH 14/23] Correct header suffix

---
 .../associative/map/map.erasure/erase.transparent.pass.cpp    | 4 ++--
 .../map/map.modifiers/extract.transparent.pass.cpp            | 4 ++--
 .../multimap/multimap.erasure/erase.transparent.pass.cpp      | 4 ++--
 .../multimap/multimap.modifiers/extract.transparent.pass.cpp  | 4 ++--
 .../associative/multiset/extract.transparent.pass.cpp         | 4 ++--
 .../multiset/multiset.erasure/erase.transparent.pass.cpp      | 4 ++--
 .../containers/associative/set/extract.transparent.pass.cpp   | 4 ++--
 .../set/{ => set.erasure}/erase.transparent.pass.cpp          | 4 ++--
 .../std/containers/unord/unord.map/erase.transparent.pass.cpp | 2 +-
 .../unord.map.modifiers/extract.transparent.pass.cpp          | 2 +-
 .../unord/unord.multimap/erase.transparent.pass.cpp           | 2 +-
 .../unord.multimap.modifiers/extract.transparent.pass.cpp     | 2 +-
 .../unord/unord.multiset/erase.transparent.pass.cpp           | 2 +-
 .../unord/unord.multiset/extract.transparent.pass.cpp         | 2 +-
 .../std/containers/unord/unord.set/erase.transparent.pass.cpp | 2 +-
 .../containers/unord/unord.set/extract.transparent.pass.cpp   | 2 +-
 ...sparent_associative.hpp => test_transparent_associative.h} | 0
 17 files changed, 24 insertions(+), 24 deletions(-)
 rename libcxx/test/std/containers/associative/set/{ => set.erasure}/erase.transparent.pass.cpp (91%)
 rename libcxx/test/support/{test_transparent_associative.hpp => test_transparent_associative.h} (100%)

diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
index 2fe83c20acf67..c8cfb77069d62 100644
--- a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class map
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
index 0dd4d9afdeb9b..97cdcda5e519e 100644
--- a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class map
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
index 7e3f625370d73..9a257b82b9388 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multimap
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
index 9faa74f69e2b0..f467d28996ed7 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multimap
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
diff --git a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
index fc04c8d85c267..f05905e7483a1 100644
--- a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multiset
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
index a7252f0aa039c..2669b07e8fd8d 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class multiset
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
diff --git a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
index 62c72808b83d1..0b036971e119e 100644
--- a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class set
 
-//    template<typename K>
+//    template<class K>
 //        node_type extract(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_extract<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
diff --git a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
similarity index 91%
rename from libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
rename to libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
index d2ca1fe599374..87e99da2a9bef 100644
--- a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
 
 // class set
 
-//    template<typename K>
+//    template<class K>
 //        size_type erase(K&& k) const;        // C++23
 
 #include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
 
 int main(int, char**) {
   test_transparent_erase<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
diff --git a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
index 2d0138ef6472f..edbaec04d3329 100644
--- a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_map
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
index 08a2e05e777bc..a14dca09159ac 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_map
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
index b15d915a618d9..e09b66da0ea10 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multimap
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
index 8eefc2b5f689b..4af37297ec0ae 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multimap
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
index 41420d41613b1..3fa33f111b1c8 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multiset
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
index e094201a8cedd..9ec51b55d2278 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_multiset
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
index efc55a0ea8964..944faf091d5df 100644
--- a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_set
 
-// template <typename K>
+// template <class K>
 // size_type erase(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
index e84facdaabcb0..06ad1deaf05d1 100644
--- a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
 
 // class unordered_set
 
-// template <typename K>
+// template <class K>
 // node_type extract(K&& k);
 
 #include <unordered_set>
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.h
similarity index 100%
rename from libcxx/test/support/test_transparent_associative.hpp
rename to libcxx/test/support/test_transparent_associative.h

>From b42c17023028a8caa340c844b842a96637412396 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 10 Jan 2026 02:15:39 +0300
Subject: [PATCH 15/23] typename ==> class

---
 libcxx/include/map           | 2 +-
 libcxx/include/set           | 4 ++--
 libcxx/include/unordered_map | 4 ++--
 libcxx/include/unordered_set | 6 +++---
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/map b/libcxx/include/map
index d148bebad4e91..bfd2a03fe804b 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -441,7 +441,7 @@ public:
     iterator  erase(const_iterator position);
     iterator  erase(iterator position); // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
 
diff --git a/libcxx/include/set b/libcxx/include/set
index 9f43a018021c6..eb765f6a6a0f8 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -134,7 +134,7 @@ public:
     iterator  erase(const_iterator position);
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator  erase(const_iterator first, const_iterator last);
 
@@ -386,7 +386,7 @@ public:
     iterator  erase(iterator position);  // C++14
     size_type erase(const key_type& k);
     iterator  erase(const_iterator first, const_iterator last);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
 
     void clear() noexcept;
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 7d3f058933f46..f30333aa86855 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -165,7 +165,7 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
 
@@ -440,7 +440,7 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
 
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index ff352709636a7..79541f53e1d5a 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -140,7 +140,7 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
 
@@ -385,7 +385,7 @@ public:
 
     node_type extract(const_iterator position);             // C++17
     node_type extract(const key_type& x);                   // C++17
-    template <typename K>
+    template <class K>
       node_type extract(K&& k);                             // C++23
 
     iterator insert(node_type&& nh);                        // C++17
@@ -394,7 +394,7 @@ public:
     iterator erase(const_iterator position);
     iterator erase(iterator position);  // C++14
     size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       size_type erase(K&& k); // C++23
     iterator erase(const_iterator first, const_iterator last);
 

>From c823cbfbc59d835df05e308ac7d6924ad7fa1d71 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 7 Feb 2026 19:04:30 +0300
Subject: [PATCH 16/23] Rename extract method

---
 libcxx/include/__hash_table  | 6 +++---
 libcxx/include/__tree        | 6 +++---
 libcxx/include/map           | 4 ++--
 libcxx/include/set           | 4 ++--
 libcxx/include/unordered_map | 4 ++--
 libcxx/include/unordered_set | 4 ++--
 6 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index ff3d0f1ab7749..19ecb90928c7f 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -899,7 +899,7 @@ public:
   template <class _NodeHandle, class _Key>
   _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const _Key& __key);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_iterator(const_iterator __it);
+  _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_from_iterator(const_iterator __it);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
@@ -1653,13 +1653,13 @@ _LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__no
   iterator __i = find(__key);
   if (__i == end())
     return _NodeHandle();
-  return __node_handle_extract_iterator<_NodeHandle>(__i);
+  return __node_handle_extract_from_iterator<_NodeHandle>(__i);
 }
 
 template <class _Tp, class _Hash, class _Equal, class _Alloc>
 template <class _NodeHandle>
 _LIBCPP_HIDE_FROM_ABI _NodeHandle
-__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_iterator(const_iterator __p) {
+__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_from_iterator(const_iterator __p) {
   allocator_type __alloc(__node_alloc());
   return _NodeHandle(remove(__p).release(), __alloc);
 }
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index b25fcbcba5ff7..f4e0d0b02369c 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1242,7 +1242,7 @@ public:
   template <class _NodeHandle, class _Key>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const _Key&);
   template <class _NodeHandle>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract_iterator(const_iterator);
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract_from_iterator(const_iterator);
 #endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p);
@@ -2126,13 +2126,13 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const& _Key __key) {
   iterator __it = __lower_bound_multi(__key);
   if (__it == end() || __value_comp_(__key, *__it))
     return _NodeHandle();
-  return __node_handle_extract_iterator<_NodeHandle>(__it);
+  return __node_handle_extract_from_iterator<_NodeHandle>(__it);
 }
 
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_iterator(const_iterator __p) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_from_iterator(const_iterator __p) {
   __node_pointer __np = __p.__get_np();
   __remove_node_pointer(__np);
   return _NodeHandle(__np, __alloc());
diff --git a/libcxx/include/map b/libcxx/include/map
index bfd2a03fe804b..ddf340c7a7a74 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1373,7 +1373,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_iterator_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23
@@ -2062,7 +2062,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/set b/libcxx/include/set
index eb765f6a6a0f8..9f0292bc17f45 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -853,7 +853,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
+    return __tree_.template __node_handle_extract_from_iterator<node_type>(__it);
   }
 
 #    if _LIBCPP_STD_VER >= 23
@@ -1432,7 +1432,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator<node_type>(__it);
+    return __tree_.template __node_handle_extract_from_iterator<node_type>(__it);
   }
 
 #    if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index f30333aa86855..abf5150bf1b93 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1199,7 +1199,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23
@@ -1960,7 +1960,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+    return __table_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 79541f53e1d5a..0db81ff48916e 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -823,7 +823,7 @@ public:
     return __table_.template __node_handle_extract<node_type>(__key);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
-    return __table_.template __node_handle_extract_iterator<node_type>(__it);
+    return __table_.template __node_handle_extract_from_iterator<node_type>(__it);
   }
 
 #    if _LIBCPP_STD_VER >= 23
@@ -1435,7 +1435,7 @@ public:
     return __table_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __position) {
-    return __table_.template __node_handle_extract_iterator<node_type>(__position);
+    return __table_.template __node_handle_extract_from_iterator<node_type>(__position);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
     return __table_.template __node_handle_extract<node_type>(__key);

>From 6d1d85db2640e97122c7b03249e4ed19d88497c2 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Tue, 16 Jun 2026 04:22:05 +0300
Subject: [PATCH 17/23] fixes after rebase

---
 libcxx/include/__tree | 2 +-
 libcxx/include/map    | 9 +++++----
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index f4e0d0b02369c..ed7d2f306f544 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -2122,7 +2122,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
 template <class _Tp, class _Compare, class _Allocator>
 template <class _NodeHandle, class _Key>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const& _Key __key) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const _Key& __key) {
   iterator __it = __lower_bound_multi(__key);
   if (__it == end() || __value_comp_(__key, *__it))
     return _NodeHandle();
diff --git a/libcxx/include/map b/libcxx/include/map
index ddf340c7a7a74..259963234909f 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -147,7 +147,7 @@ public:
 
     constexpr node_type extract(const_iterator position);                                       // C++17
     constexpr node_type extract(const key_type& x);                                             // C++17
-    template <typename K>
+    template <class K>
       constexpr node_type extract(K&& k);                                                       // C++23
 
     constexpr insert_return_type insert(node_type&& nh);                                        // C++17
@@ -173,7 +173,7 @@ public:
     constexpr iterator  erase(const_iterator position);
     constexpr iterator  erase(iterator position);                         // C++14
     constexpr size_type erase(const key_type& k);
-    template <typename K>
+    template <class K>
       constexpr size_type erase(K&& k);                                   // C++23
     constexpr iterator  erase(const_iterator first, const_iterator last);
 
@@ -1048,7 +1048,8 @@ public:
 #  endif
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26
   map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 
@@ -1373,7 +1374,7 @@ public:
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
-    return __tree_.template __node_handle_extract_iterator_from_iterator<node_type>(__it.__i_);
+    return __tree_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
 #    if _LIBCPP_STD_VER >= 23

>From df1ddd59fe189fc347ea485412fac666f593be32 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Tue, 16 Jun 2026 04:30:02 +0300
Subject: [PATCH 18/23] release 23

---
 libcxx/docs/ReleaseNotes/22.rst    | 1 -
 libcxx/docs/ReleaseNotes/23.rst    | 1 +
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 libcxx/include/map                 | 3 +--
 4 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index f3862eb464b1d..556fea76d33ac 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -38,7 +38,6 @@ What's New in Libc++ 22.0.0?
 Implemented Papers
 ------------------
 
-- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
 - P2592R3: Hashing support for ``std::chrono`` value classes (`Github <https://llvm.org/PR105358>`__)
 - P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__)
 - P2988R12: ``std::optional<T&>`` (`Github <https://llvm.org/PR148131>`__)
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..aef216618d329 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,7 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index d6fa695026543..fac8f0061e305 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -30,7 +30,7 @@
 "`P1147R1 <https://wg21.link/P1147R1>`__","Printing ``volatile`` Pointers","2021-10 (Virtual)","|Complete|","14","`#105161 <https://github.com/llvm/llvm-project/issues/105161>`__",""
 "`P1272R4 <https://wg21.link/P1272R4>`__","Byteswapping for fun&&nuf","2021-10 (Virtual)","|Complete|","14","`#105163 <https://github.com/llvm/llvm-project/issues/105163>`__",""
 "`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","","`#105164 <https://github.com/llvm/llvm-project/issues/105164>`__",""
-"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","22","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
+"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","23","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
 "`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14","`#105166 <https://github.com/llvm/llvm-project/issues/105166>`__",""
 "`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
 "`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|Complete|","22","`#105169 <https://github.com/llvm/llvm-project/issues/105169>`__",""
diff --git a/libcxx/include/map b/libcxx/include/map
index 259963234909f..e3b92795ab9f6 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1049,8 +1049,7 @@ public:
 
   template <class _InputIterator>
   _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26
-  map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 
 #  if _LIBCPP_STD_VER >= 23

>From aad1eb6811f16246e88c5c410df730b8bdc67b44 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 26 Jun 2026 22:28:04 +0300
Subject: [PATCH 19/23] add constexpr tests for map and multimap

---
 libcxx/include/__node_handle                  |  4 +--
 libcxx/include/map                            | 14 ++++----
 .../map.erasure/erase.transparent.pass.cpp    | 14 ++++++--
 .../extract.transparent.pass.cpp              | 14 ++++++--
 .../erase.transparent.pass.cpp                | 14 ++++++--
 .../extract.transparent.pass.cpp              | 14 ++++++--
 .../support/test_transparent_associative.h    | 35 ++++++++++---------
 7 files changed, 75 insertions(+), 34 deletions(-)

diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index 993283cc926da..eb8e57d2b82f7 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -180,11 +180,11 @@ struct __map_node_handle_specifics {
   using mapped_type = typename _NodeType::__node_value_type::second_type;
 
   // This method is not constexpr as per the standard.
-  _LIBCPP_HIDE_FROM_ABI key_type& key() const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_type& key() const {
     return const_cast<key_type&>(static_cast<_Derived const*>(this)->__ptr_->__get_value().first);
   }
 
-  _LIBCPP_HIDE_FROM_ABI mapped_type& mapped() const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 mapped_type& mapped() const {
     return static_cast<_Derived const*>(this)->__ptr_->__get_value().second;
   }
 };
diff --git a/libcxx/include/map b/libcxx/include/map
index e3b92795ab9f6..3bc1587b25f5e 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1048,8 +1048,8 @@ public:
 #  endif
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 
 #  if _LIBCPP_STD_VER >= 23
@@ -1850,14 +1850,14 @@ public:
 #  endif
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multimap(__f, __l, key_compare(), __a) {}
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+  multimap(from_range_t, _Range&& __range, const allocator_type& __a)
       : multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
@@ -2061,7 +2061,7 @@ public:
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
     return __tree_.template __node_handle_extract<node_type>(__key);
   }
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
     return __tree_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
   }
 
diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
index c8cfb77069d62..fbb632847b38f 100644
--- a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -13,16 +13,26 @@
 // class map
 
 //    template<class K>
-//        size_type erase(K&& k) const;        // C++23
+//        constexpr size_type erase(K&& k) const;        // C++23
 
 #include <map>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
 
   test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_final>>(
       {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
 
   test_non_transparent_erase<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
index 97cdcda5e519e..01544585333cc 100644
--- a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -13,15 +13,25 @@
 // class map
 
 //    template<class K>
-//        node_type extract(K&& k) const;        // C++23
+//        constexpr node_type extract(K&& k) const;        // C++23
 
 #include <map>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
 
   test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
 
   test_non_transparent_extract<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
index 9a257b82b9388..bba57e93c397b 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -13,12 +13,12 @@
 // class multimap
 
 //    template<class K>
-//        size_type erase(K&& k) const;        // C++23
+//        constexpr size_type erase(K&& k) const;        // C++23
 
 #include <map>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
       {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
 
@@ -26,4 +26,14 @@ int main(int, char**) {
       {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
 
   test_non_transparent_erase<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
index f467d28996ed7..0c54072aa94cd 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -13,15 +13,25 @@
 // class multimap
 
 //    template<class K>
-//        node_type extract(K&& k) const;        // C++23
+//        constexpr node_type extract(K&& k) const;        // C++23
 
 #include <map>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
 
   test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
 
   test_non_transparent_extract<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/support/test_transparent_associative.h b/libcxx/test/support/test_transparent_associative.h
index e3cac5e8a51cb..e8acb8d986a88 100644
--- a/libcxx/test/support/test_transparent_associative.h
+++ b/libcxx/test/support/test_transparent_associative.h
@@ -23,16 +23,16 @@ struct StoredType;
 
 template <typename T>
 struct SearchedType {
-  explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+  constexpr explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
 
-  operator StoredType<T>() const {
+  constexpr operator StoredType<T>() const {
     ++*conversions_;
     return StoredType<T>{value_};
   }
 
-  T get_value() const { return value_; }
+  constexpr T get_value() const { return value_; }
 
-  auto operator<=>(const SearchedType<T>&) const = default;
+  constexpr auto operator<=>(const SearchedType<T>&) const = default;
 
 private:
   T value_;
@@ -42,15 +42,16 @@ struct SearchedType {
 template <typename T>
 struct StoredType {
   StoredType() = default;
-  StoredType(T value) : value_(value) {}
 
-  friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+  constexpr StoredType(T value) : value_(value) {}
 
-  friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+  constexpr friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
 
-  T get_value() const { return value_; }
+  constexpr friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
 
-  auto operator<=>(const StoredType<T>&) const = default;
+  constexpr T get_value() const { return value_; }
+
+  constexpr auto operator<=>(const StoredType<T>&) const = default;
 
 private:
   T value_;
@@ -60,17 +61,17 @@ struct transparent_comparator_base {
   using is_transparent = void;
 
   template <typename T>
-  bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
+  constexpr bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
     return lhs.get_value() < rhs.get_value();
   }
 
   template <typename T>
-  bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
+  constexpr bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
     return lhs.get_value() < rhs.get_value();
   }
 
   template <typename T>
-  bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
+  constexpr bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
     return lhs < rhs;
   }
 };
@@ -78,7 +79,7 @@ struct transparent_comparator_base {
 struct transparent_comparator_final final : public transparent_comparator_base {};
 
 template <class Container>
-void test_transparent_erase(Container c) {
+constexpr void test_transparent_erase(Container c) {
   static_assert(
       std::same_as<
           typename Container::size_type,
@@ -93,7 +94,7 @@ void test_transparent_erase(Container c) {
 }
 
 template <class Container>
-void test_non_transparent_erase(Container c) {
+constexpr void test_non_transparent_erase(Container c) {
   int conversions = 0;
   assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
   assert(conversions == 1);
@@ -109,7 +110,7 @@ concept node_handle_has_key = requires(NodeHandle nh) {
 };
 
 template <class T, class Container>
-void test_single_extract(SearchedType<T> key, Container& c) {
+constexpr void test_single_extract(SearchedType<T> key, Container& c) {
   auto node_handle = c.extract(key);
 
   assert(!node_handle.empty());
@@ -122,7 +123,7 @@ void test_single_extract(SearchedType<T> key, Container& c) {
 }
 
 template <class Container>
-void test_transparent_extract(Container c) {
+constexpr void test_transparent_extract(Container c) {
   static_assert(std::same_as< typename Container::node_type,
                               std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
                                                    Container,
@@ -138,7 +139,7 @@ void test_transparent_extract(Container c) {
 }
 
 template <class Container>
-void test_non_transparent_extract(Container c) {
+constexpr void test_non_transparent_extract(Container c) {
   int conversions = 0;
 
   test_single_extract(SearchedType<int>(1, &conversions), c);

>From 96b0b2e0cff177178b88f6aaba079edb798bd061 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 26 Jun 2026 22:37:08 +0300
Subject: [PATCH 20/23] fix node_handle case

---
 libcxx/include/__node_handle                       |  4 ++--
 libcxx/test/support/test_transparent_associative.h | 11 -----------
 2 files changed, 2 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index eb8e57d2b82f7..993283cc926da 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -180,11 +180,11 @@ struct __map_node_handle_specifics {
   using mapped_type = typename _NodeType::__node_value_type::second_type;
 
   // This method is not constexpr as per the standard.
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_type& key() const {
+  _LIBCPP_HIDE_FROM_ABI key_type& key() const {
     return const_cast<key_type&>(static_cast<_Derived const*>(this)->__ptr_->__get_value().first);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 mapped_type& mapped() const {
+  _LIBCPP_HIDE_FROM_ABI mapped_type& mapped() const {
     return static_cast<_Derived const*>(this)->__ptr_->__get_value().second;
   }
 };
diff --git a/libcxx/test/support/test_transparent_associative.h b/libcxx/test/support/test_transparent_associative.h
index e8acb8d986a88..90eb2feae4e8e 100644
--- a/libcxx/test/support/test_transparent_associative.h
+++ b/libcxx/test/support/test_transparent_associative.h
@@ -104,22 +104,11 @@ constexpr void test_non_transparent_erase(Container c) {
   assert(conversions == 3);
 }
 
-template <typename NodeHandle>
-concept node_handle_has_key = requires(NodeHandle nh) {
-  { nh.key() };
-};
-
 template <class T, class Container>
 constexpr void test_single_extract(SearchedType<T> key, Container& c) {
   auto node_handle = c.extract(key);
 
   assert(!node_handle.empty());
-
-  if constexpr (node_handle_has_key<typename Container::node_type>) {
-    assert(node_handle.key() == key);
-  } else {
-    assert(node_handle.value() == key);
-  }
 }
 
 template <class Container>

>From 3623fe9de7f03e204baf27f4ab7681e471afc126 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 26 Jun 2026 22:54:03 +0300
Subject: [PATCH 21/23] fix format

---
 libcxx/include/map                                 | 12 ++++++------
 libcxx/test/support/test_transparent_associative.h |  4 +++-
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/map b/libcxx/include/map
index 3bc1587b25f5e..9b6c73e0b5351 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1048,8 +1048,8 @@ public:
 #  endif
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
-  map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : map(__f, __l, key_compare(), __a) {}
 
 #  if _LIBCPP_STD_VER >= 23
@@ -1850,14 +1850,14 @@ public:
 #  endif
 
   template <class _InputIterator>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
-  multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
       : multimap(__f, __l, key_compare(), __a) {}
 
 #  if _LIBCPP_STD_VER >= 23
   template <_ContainerCompatibleRange<value_type> _Range>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
-  multimap(from_range_t, _Range&& __range, const allocator_type& __a)
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(from_range_t, _Range&& __range, const allocator_type& __a)
       : multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
diff --git a/libcxx/test/support/test_transparent_associative.h b/libcxx/test/support/test_transparent_associative.h
index 90eb2feae4e8e..85c35e3acff8f 100644
--- a/libcxx/test/support/test_transparent_associative.h
+++ b/libcxx/test/support/test_transparent_associative.h
@@ -47,7 +47,9 @@ struct StoredType {
 
   constexpr friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
 
-  constexpr friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+  constexpr friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
+    return lhs.value_ == rhs.get_value();
+  }
 
   constexpr T get_value() const { return value_; }
 

>From f8065bf20763d71fc439b26f229c406aedfe3236 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sun, 5 Jul 2026 14:58:35 +0300
Subject: [PATCH 22/23] make set tests constexpr

---
 .../map/map.erasure/erase.transparent.pass.cpp     |  2 +-
 .../map/map.modifiers/extract.transparent.pass.cpp |  2 +-
 .../multimap.erasure/erase.transparent.pass.cpp    |  2 +-
 .../extract.transparent.pass.cpp                   |  2 +-
 .../associative/set/extract.transparent.pass.cpp   | 14 ++++++++++++--
 .../set/set.erasure/erase.transparent.pass.cpp     | 14 ++++++++++++--
 6 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
index fbb632847b38f..89346b83a287c 100644
--- a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class map
 
 //    template<class K>
-//        constexpr size_type erase(K&& k) const;        // C++23
+//        constexpr size_type erase(K&& k) const;        // C++23, constexpr since C++26
 
 #include <map>
 #include "test_transparent_associative.h"
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
index 01544585333cc..5cd31a54ba4d1 100644
--- a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class map
 
 //    template<class K>
-//        constexpr node_type extract(K&& k) const;        // C++23
+//        constexpr node_type extract(K&& k) const;        // C++23, constexpr since C++26
 
 #include <map>
 #include "test_transparent_associative.h"
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
index bba57e93c397b..2f9a47327b74c 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class multimap
 
 //    template<class K>
-//        constexpr size_type erase(K&& k) const;        // C++23
+//        constexpr size_type erase(K&& k) const;        // C++23, constexpr since C++26
 
 #include <map>
 #include "test_transparent_associative.h"
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
index 0c54072aa94cd..71a0cdaf9e1a7 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -13,7 +13,7 @@
 // class multimap
 
 //    template<class K>
-//        constexpr node_type extract(K&& k) const;        // C++23
+//        constexpr node_type extract(K&& k) const;        // C++23, constexpr since C++26
 
 #include <map>
 #include "test_transparent_associative.h"
diff --git a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
index 0b036971e119e..50f13833cbe3e 100644
--- a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
@@ -13,15 +13,25 @@
 // class set
 
 //    template<class K>
-//        node_type extract(K&& k) const;        // C++23
+//        constexpr node_type extract(K&& k) const;        // C++23, constexpr since C++26
 
 #include <set>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_extract<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
 
   test_transparent_extract<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
 
   test_non_transparent_extract<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
index 87e99da2a9bef..0df59d1eeb132 100644
--- a/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
@@ -13,15 +13,25 @@
 // class set
 
 //    template<class K>
-//        size_type erase(K&& k) const;        // C++23
+//        constexpr size_type erase(K&& k) const;        // C++23, constexpr since C++26
 
 #include <set>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_erase<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
 
   test_transparent_erase<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
 
   test_non_transparent_erase<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }

>From 5e0a3ccaa941348caf560f5392fafd0299d63db8 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 11 Jul 2026 12:28:18 +0300
Subject: [PATCH 23/23] make multiset tests constexpr

---
 .../multiset/extract.transparent.pass.cpp          | 14 ++++++++++++--
 .../multiset.erasure/erase.transparent.pass.cpp    | 14 ++++++++++++--
 2 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
index f05905e7483a1..e0b15efa77352 100644
--- a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
@@ -13,15 +13,25 @@
 // class multiset
 
 //    template<class K>
-//        node_type extract(K&& k) const;        // C++23
+//        node_type extract(K&& k) const;        // C++23, constexpr since C++26
 
 #include <set>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
 
   test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
 
   test_non_transparent_extract<std::multiset<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
index 2669b07e8fd8d..e538c70c0c5ea 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
@@ -13,15 +13,25 @@
 // class multiset
 
 //    template<class K>
-//        size_type erase(K&& k) const;        // C++23
+//        size_type erase(K&& k) const;        // C++23, constexpr since C++26
 
 #include <set>
 #include "test_transparent_associative.h"
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
   test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
 
   test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
 
   test_non_transparent_erase<std::multiset<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+  return 0;
 }


From libcxx-commits at lists.llvm.org  Sat Jul 11 02:29:32 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sat, 11 Jul 2026 02:29:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a520cfc.a87eb5dc.7d817.d556@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/207585

>From 5b2c2c5266129734c45c3c3806692151150acaf0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 5 Jul 2026 14:17:28 +0100
Subject: [PATCH] Implementation of adjacent_difference on top of a binary
 transform

---
 libcxx/include/__numeric/pstl.h               |  39 ++++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  35 +++-
 .../pstl.iterator-requirements.verify.cpp     |  11 ++
 .../algorithms/pstl.nodiscard.verify.cpp      |   5 +
 .../pstl.exception_handling.pass.cpp          |  16 +-
 .../pstl.adjacent_difference.pass.cpp         | 171 +++++++++++++++++
 .../pstl.adjacent_difference_op.pass.cpp      | 181 ++++++++++++++++++
 8 files changed, 461 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp

diff --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h
index ecbc9f5348333..0b85cfb8c2327 100644
--- a/libcxx/include/__numeric/pstl.h
+++ b/libcxx/include/__numeric/pstl.h
@@ -165,6 +165,45 @@ template <class _ExecutionPolicy,
       std::move(__transform));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first1), std::move(__last1), std::move(__first2), minus{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryOperation,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _BinaryOperation __op) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__op));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 1ea121367fbc7..c40745a25134a 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -310,6 +310,13 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_difference;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+// optional<_ForwardIterator2>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index cb963a130f743..1e51cd7645e62 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -91,11 +91,12 @@ namespace __pstl {
 //
 // transform and transform_binary family
 // -------------------------------------
-// - replace_copy_if
-// - replace_copy
-// - move
+// - adjacent_difference
 // - copy
 // - copy_n
+// - move
+// - replace_copy
+// - replace_copy_if
 // - rotate_copy
 //
 
@@ -557,6 +558,34 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _BinaryOperation&& __op) const noexcept {
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    if (__first1 == __last1)
+      return __first2; // edge case: empty input range, just return the output iterator
+    *__first2 = *__first1;
+    ++__first2;
+    _ForwardIterator1 __first_left = std::next(__first1);
+    if (__first_left == __last1)
+      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+    // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
+    return _TransformBinary()(
+        __policy,
+        std::move(__first_left),
+        std::move(__last1),
+        std::move(__first1),
+        std::move(__first2),
+        std::forward<_BinaryOperation>(__op));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index aee3f97019071..fb81f293df896 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -201,4 +201,15 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::transform_reduce(
         pol, non_fwd, non_fwd, val, func, func); // expected-error@*:* {{static assertion failed: transform_reduce}}
   }
+
+  {
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+  }
 }
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 747d62bc66837..9b2b025d27a95 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <algorithm>
 #include <execution>
 #include <iterator>
+#include <numeric>
 
 void test() {
   int a[]    = {1};
@@ -56,4 +57,8 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
 }
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 9e6c8f5b8ce40..db4bfc42e0d70 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -85,7 +85,7 @@ int main(int, char**) {
       auto last1       = util::throw_on_move_iterator(std::end(a), tokens[1].active() ? 1 : -1);
       auto first2      = util::throw_on_move_iterator(std::begin(b), tokens[2].active() ? 1 : -1);
       auto last2       = util::throw_on_move_iterator(std::end(b), tokens[3].active() ? 1 : -1);
-      auto dest        = util::throw_on_move_iterator(std::end(storage), tokens[4].active() ? 1 : -1);
+      auto dest        = util::throw_on_move_iterator(std::begin(storage), tokens[4].active() ? 1 : -1);
       auto maybe_throw = [](ThrowToken const& token, auto f) {
         return [&token, f](auto... args) {
           if (token.active())
@@ -358,6 +358,20 @@ int main(int, char**) {
           (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction);
         });
       }
+
+      {
+        auto op = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
+
+        // adjacent_difference(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+
+        // adjacent_difference(first, last, dest, op)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest), op);
+        });
+      }
     }
   });
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
new file mode 100644
index 0000000000000..bb98004c1c62a
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+
+  friend X operator-(const X& x, const X& y) { return X(x.i_ - y.i_); }
+
+  friend class Y;
+};
+
+class Y {
+  int i_;
+
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 20};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, -5, -4, -3, -2};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r =
+            std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        assert(std::all_of(std::begin(ib), std::begin(ib) + i, [](int x) { return x == 1; }));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(7)};
+      Y ir[3] = {Y(1), Y(4), Y(2)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
new file mode 100644
index 0000000000000..26d65f9a77c10
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryOperation>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2,
+//                                        BinaryOperation op);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+  friend class Y;
+  friend struct Op;
+};
+
+class Y {
+  int i_;
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+// ensures that the predicate is not equivalent to std::minus
+struct Op {
+  int operator()(int l, int r) const { return l * 2 - r; }
+  X operator()(const X& l, const X& r) const { return X(l.i_ * 2 - r.i_); }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 50};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, 5, 2, 0, -1};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r = std::adjacent_difference(
+            policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)), Op{});
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j)
+          assert(ib[j] == (int)(j == 0 ? 1 : j + 2));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(8)};
+      Y ir[3] = {Y(1), Y(9), Y(11)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Sat Jul 11 03:05:04 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 03:05:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] #199778: locale_base_api.h (PR
	#207569)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207569@github.com>
Message-ID: <6a521550.c574b9b4.21ead0.4dbd@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/207569

>From a8c0657c3bae625498e2b798732e96b28c96d91e Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sun, 5 Jul 2026 17:29:22 +0800
Subject: [PATCH] [libcxx] #199778: locale_base_api.h

According to issue #199778
PR#194317 broke the build for wasm since __MVS__ define guard
was removed.
Try to add it back.
---
 libcxx/include/__locale_dir/locale_base_api.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index f973d95ef244d..79df29c7470d9 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -134,7 +134,9 @@
 //       (by providing global non-reserved names) and the new API. As we move individual platforms
 //       towards the new way of defining the locale base API, this should disappear since each platform
 //       will define those directly.
-#    include <__locale_dir/locale_base_api/ibm.h>
+#    ifdef __MVS__
+#      include <__locale_dir/locale_base_api/ibm.h>
+#    endif
 
 #    include <__locale_dir/locale_base_api/bsd_locale_fallbacks.h>
 


From libcxx-commits at lists.llvm.org  Sat Jul 11 03:06:02 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 03:06:02 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [libcxx]support freestanding
	libc++ (PR #208900)
Message-ID: <llvm.org/llvm/llvm-project/pull/208900@github.com>


https://github.com/trcrsired created https://github.com/llvm/llvm-project/pull/208900

I am trying to add support fo freestanding C++. here is to check whether it passes CI.


>From 6efb92a758e137b9eff58ca269077af8161606c6 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libcxx]support freestanding libc++

---
 clang/lib/Headers/errno.h                     | 164 ++++++++++++++++++
 clang/lib/Headers/math.h                      |  29 ++++
 clang/lib/Headers/stdlib.h                    | 103 +++++++++++
 clang/lib/Headers/string.h                    |  65 +++++++
 clang/lib/Headers/wchar.h                     |  62 +++++++
 libcxx/CMakeLists.txt                         |  54 ++++--
 libcxx/include/__algorithm/sort.h             |   4 +
 libcxx/include/__config                       |   4 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/ctime                          |  15 +-
 libcxx/include/stdio.h                        |   5 +
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 114 ++++++++++++
 12 files changed, 604 insertions(+), 18 deletions(-)
 create mode 100644 clang/lib/Headers/errno.h
 create mode 100644 clang/lib/Headers/math.h
 create mode 100644 clang/lib/Headers/stdlib.h
 create mode 100644 clang/lib/Headers/string.h
 create mode 100644 clang/lib/Headers/wchar.h
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/clang/lib/Headers/errno.h b/clang/lib/Headers/errno.h
new file mode 100644
index 0000000000000..48fe84a9304ce
--- /dev/null
+++ b/clang/lib/Headers/errno.h
@@ -0,0 +1,164 @@
+/*===---- errno.h - Standard header for error numbers ---------------------===*\
+ *
+ * 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 __CLANG_ERRNO_H
+#define __CLANG_ERRNO_H
+
+/* If we're hosted, fall back to the system's errno.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<errno.h>)
+
+#include_next <errno.h>
+
+#else
+
+/* Adjusted to the linux asm/errno.h */
+#define EPERM 1            /* Operation not permitted */
+#define ENOENT 2           /* No such file or directory */
+#define ESRCH 3            /* No such process */
+#define EINTR 4            /* Interrupted system call */
+#define EIO 5              /* I/O error */
+#define ENXIO 6            /* No such device or address */
+#define E2BIG 7            /* Arg list too long */
+#define ENOEXEC 8          /* Exec format error */
+#define EBADF 9            /* Bad file number */
+#define ECHILD 10          /* No child processes */
+#define EAGAIN 11          /* Try again */
+#define ENOMEM 12          /* Out of memory */
+#define EACCES 13          /* Permission denied */
+#define EFAULT 14          /* Bad address */
+#define ENOTBLK 15         /* Block device required */
+#define EBUSY 16           /* Device or resource busy */
+#define EEXIST 17          /* File exists */
+#define EXDEV 18           /* Cross-device link */
+#define ENODEV 19          /* No such device */
+#define ENOTDIR 20         /* Not a directory */
+#define EISDIR 21          /* Is a directory */
+#define EINVAL 22          /* Invalid argument */
+#define ENFILE 23          /* File table overflow */
+#define EMFILE 24          /* Too many open files */
+#define ENOTTY 25          /* Not a typewriter */
+#define ETXTBSY 26         /* Text file busy */
+#define EFBIG 27           /* File too large */
+#define ENOSPC 28          /* No space left on device */
+#define ESPIPE 29          /* Illegal seek */
+#define EROFS 30           /* Read-only file system */
+#define EMLINK 31          /* Too many links */
+#define EPIPE 32           /* Broken pipe */
+#define EDOM 33            /* Math argument out of domain of func */
+#define ERANGE 34          /* Math result not representable */
+#define EDEADLK 35         /* Resource deadlock would occur */
+#define ENAMETOOLONG 36    /* File name too long */
+#define ENOLCK 37          /* No record locks available */
+#define ENOSYS 38          /* Function not implemented */
+#define ENOTEMPTY 39       /* Directory not empty */
+#define ELOOP 40           /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42          /* No message of desired type */
+#define EIDRM 43           /* Identifier removed */
+#define ECHRNG 44          /* Channel number out of range */
+#define EL2NSYNC 45        /* Level 2 not synchronized */
+#define EL3HLT 46          /* Level 3 halted */
+#define EL3RST 47          /* Level 3 reset */
+#define ELNRNG 48          /* Link number out of range */
+#define EUNATCH 49         /* Protocol driver not attached */
+#define ENOCSI 50          /* No CSI structure available */
+#define EL2HLT 51          /* Level 2 halted */
+#define EBADE 52           /* Invalid exchange */
+#define EBADR 53           /* Invalid request descriptor */
+#define EXFULL 54          /* Exchange full */
+#define ENOANO 55          /* No anode */
+#define EBADRQC 56         /* Invalid request code */
+#define EBADSLT 57         /* Invalid slot */
+#define EDEADLOCK EDEADLK
+#define EBFONT 59          /* Bad font file format */
+#define EFTYPE EBFONT      /* Inappropriate file type or format */
+#define ENOSTR 60          /* Device not a stream */
+#define ENODATA 61         /* No data available */
+#define ETIME 62           /* Timer expired */
+#define ENOSR 63           /* Out of streams resources */
+#define ENONET 64          /* Machine is not on the network */
+#define ENOPKG 65          /* Package not installed */
+#define EREMOTE 66         /* Object is remote */
+#define ENOLINK 67         /* Link has been severed */
+#define EADV 68            /* Advertise error */
+#define ESRMNT 69          /* Srmount error */
+#define ECOMM 70           /* Communication error on send */
+#define EPROTO 71          /* Protocol error */
+#define EMULTIHOP 72       /* Multihop attempted */
+#define EDOTDOT 73         /* RFS specific error */
+#define EBADMSG 74         /* Not a data message */
+#define EOVERFLOW 75       /* Value too large for defined data type */
+#define ENOTUNIQ 76        /* Name not unique on network */
+#define EBADFD 77          /* File descriptor in bad state */
+#define EREMCHG 78         /* Remote address changed */
+#define ELIBACC 79         /* Can not access a needed shared library */
+#define ELIBBAD 80         /* Accessing a corrupted shared library */
+#define ELIBSCN 81         /* .lib section in a.out corrupted */
+#define ELIBMAX 82         /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83        /* Cannot exec a shared library directly */
+#define EILSEQ 84          /* Illegal byte sequence */
+#define ERESTART 85        /* Interrupted system call should be restarted */
+#define ESTRPIPE 86        /* Streams pipe error */
+#define EUSERS 87          /* Too many users */
+#define ENOTSOCK 88        /* Socket operation on non-socket */
+#define EDESTADDRREQ 89    /* Destination address required */
+#define EMSGSIZE 90        /* Message too long */
+#define EPROTOTYPE 91      /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92     /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95      /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96    /* Protocol family not supported */
+#define EAFNOSUPPORT 97    /* Address family not supported by protocol */
+#define EADDRINUSE 98      /* Address already in use */
+#define EADDRNOTAVAIL 99   /* Cannot assign requested address */
+#define ENETDOWN 100       /* Network is down */
+#define ENETUNREACH 101    /* Network is unreachable */
+#define ENETRESET 102      /* Network dropped connection because of reset */
+#define ECONNABORTED 103   /* Software caused connection abort */
+#define ECONNRESET 104     /* Connection reset by peer */
+#define ENOBUFS 105        /* No buffer space available */
+#define EISCONN 106        /* Transport endpoint is already connected */
+#define ENOTCONN 107       /* Transport endpoint is not connected */
+#define ESHUTDOWN 108      /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109   /* Too many references: cannot splice */
+#define ETIMEDOUT 110      /* Connection timed out */
+#define ECONNREFUSED 111   /* Connection refused */
+#define EHOSTDOWN 112      /* Host is down */
+#define EHOSTUNREACH 113   /* No route to host */
+#define EALREADY 114       /* Operation already in progress */
+#define EINPROGRESS 115    /* Operation now in progress */
+#define ESTALE 116         /* Stale NFS file handle */
+#define EUCLEAN 117        /* Structure needs cleaning */
+#define ENOTNAM 118        /* Not a XENIX named type file */
+#define ENAVAIL 119        /* No XENIX semaphores available */
+#define EISNAM 120         /* Is a named type file */
+#define EREMOTEIO 121      /* Remote I/O error */
+#define EDQUOT 122         /* Quota exceeded */
+#define ENOMEDIUM 123      /* No medium found */
+#define EMEDIUMTYPE 124    /* Wrong medium type */
+#define ECANCELED 125      /* Operation Canceled */
+#define ENOKEY 126         /* Required key not available */
+#define EKEYEXPIRED 127    /* Key has expired */
+#define EKEYREVOKED 128    /* Key has been revoked */
+#define EKEYREJECTED 129   /* Key was rejected by service */
+#define EOWNERDEAD 130     /* Owner died */
+#define ENOTRECOVERABLE 131 /* State not recoverable */
+#define ERFKILL 132         /* Operation not possible due to RF-kill */
+#define EHWPOISON 133       /* Memory page has hardware error */
+
+/* Widely known to be a synonym in Linux.  */
+#define ENOTSUP EOPNOTSUPP
+
+#define __ELASTERROR 2000 /* Users can add values starting here */
+
+#endif
+
+#endif
diff --git a/clang/lib/Headers/math.h b/clang/lib/Headers/math.h
new file mode 100644
index 0000000000000..3e316c891a1bc
--- /dev/null
+++ b/clang/lib/Headers/math.h
@@ -0,0 +1,29 @@
+/*===---- math.h - Standard header for C math ----------------------------===*\
+ *
+ * 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 __CLANG_MATH_H
+#define __CLANG_MATH_H
+
+/* If we're hosted, fall back to the system's math.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<math.h>)
+
+#include_next <math.h>
+
+#else
+
+#define NAN __builtin_nanf("")
+#define INFINITY __builtin_inff()
+#define FP_NAN 0
+#define FP_INFINITE 1
+#define FP_ZERO 2
+#define FP_SUBNORMAL 3
+#define FP_NORMAL 4
+
+#endif
diff --git a/clang/lib/Headers/stdlib.h b/clang/lib/Headers/stdlib.h
new file mode 100644
index 0000000000000..db5fa08385f58
--- /dev/null
+++ b/clang/lib/Headers/stdlib.h
@@ -0,0 +1,103 @@
+/*===---- stdlib.h - Standard header for general utilities ----------------===*\
+ *
+ * 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 __CLANG_STDLIB_H
+#define __CLANG_STDLIB_H
+
+/* If we're hosted, fall back to the system's math.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<stdlib.h>)
+
+#include_next <stdlib.h>
+
+#else
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1 /* freestanding */
+#endif
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0 /* freestanding */
+#endif
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+  int quot;
+  int rem;
+} div_t; /* freestanding */
+typedef struct {
+  long quot;
+  long rem;
+} ldiv_t; /* freestanding */
+typedef struct {
+  long long quot;
+  long long rem;
+} lldiv_t; /* freestanding */
+typedef struct {
+  intmax_t quot;
+  intmax_t rem;
+} imaxdiv_t; /* freestanding */
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+// start and termination
+
+#ifndef __CLANG_STDLIB_NOEXCEPT
+#ifdef __cplusplus
+#if __cplusplus >= 201103L
+#define __CLANG_STDLIB_NOEXCEPT noexcept
+#else
+#define __CLANG_STDLIB_NOEXCEPT throw()
+#endif
+#else
+#define __CLANG_STDLIB_NOEXCEPT
+#endif
+#endif
+
+[[noreturn]] void abort() __CLANG_STDLIB_NOEXCEPT;         /* freestanding */
+int atexit(void (*)()) __CLANG_STDLIB_NOEXCEPT;            /* freestanding */
+int at_quick_exit(void (*)()) __CLANG_STDLIB_NOEXCEPT;     /* freestanding */
+[[noreturn]] void exit(int);                               /* freestanding */
+[[noreturn]] void _Exit(int) __CLANG_STDLIB_NOEXCEPT;      /* freestanding */
+[[noreturn]] void quick_exit(int) __CLANG_STDLIB_NOEXCEPT; /* freestanding */
+
+#undef __CLANG_STDLIB_NOEXCEPT
+
+// C standard library algorithms
+void *bsearch(const void *, const void *, size_t, size_t,
+              int (*)(const void *, const void *)); /* freestanding */
+void qsort(void *, size_t, size_t,
+           int (*)(const void *, const void *)); /* freestanding */
+
+// absolute values
+int abs(int j);                       /* freestanding */
+long int abs(long int j);             /* freestanding */
+long long int abs(long long int j);   /* freestanding */
+long int labs(long int j);            /* freestanding */
+long long int llabs(long long int j); /* freestanding */
+
+div_t div(int, int);                         /* freestanding */
+ldiv_t div(long int, long int);              /* freestanding */
+lldiv_t div(long long int, long long int);   /* freestanding */
+ldiv_t ldiv(long int, long int);             /* freestanding */
+lldiv_t lldiv(long long int, long long int); /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
diff --git a/clang/lib/Headers/string.h b/clang/lib/Headers/string.h
new file mode 100644
index 0000000000000..c256b3e35a95b
--- /dev/null
+++ b/clang/lib/Headers/string.h
@@ -0,0 +1,65 @@
+/*===---- string.h - Standard header for null-terminated string -----------===*\
+ *
+ * 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 __CLANG_STRING_H
+#define __CLANG_STRING_H
+
+/* If we're hosted, fall back to the system's string.h, which might have
+ * additional definitions.
+ */
+
+#if __STDC_HOSTED__ && __has_include_next(<string.h>)
+
+#include_next <string.h>
+
+#else
+
+#include <stddef.h>
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void *memcpy(void *, const void *, size_t);      /* freestanding */
+void *memmove(void *, const void *, size_t);     /* freestanding */
+char *strcpy(char *, const char *);              /* freestanding */
+char *strncpy(char *, const char *, size_t);     /* freestanding */
+char *strcat(char *, const char *);              /* freestanding */
+char *strncat(char *, const char *, size_t);     /* freestanding */
+int memcmp(const void *, const void *, size_t);  /* freestanding */
+int strcmp(const char *, const char *);          /* freestanding */
+int strncmp(const char *, const char *, size_t); /* freestanding */
+const void *memchr(const void *, int, size_t);   /* freestanding */
+void *memchr(void *, int, size_t);               /* freestanding */
+const char *strchr(const char *, int);           /* freestanding */
+char *strchr(char *, int);                       /* freestanding */
+size_t strcspn(const char *, const char *);      /* freestanding */
+const char *strpbrk(const char *, const char *); /* freestanding */
+char *strpbrk(char *, const char *);             /* freestanding */
+const char *strrchr(const char *, int);          /* freestanding */
+char *strrchr(char *, int);                      /* freestanding */
+size_t strspn(const char *, const char *);       /* freestanding */
+const char *strstr(const char *, const char *);  /* freestanding */
+char *strstr(char *, const char *);              /* freestanding */
+char *strtok(char *, const char *);              /* freestanding */
+void *memset(void *, int, size_t);               /* freestanding */
+size_t strlen(const char *);                     /* freestanding */
+void *memset_explicit(void *, int, size_t);      /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/clang/lib/Headers/wchar.h b/clang/lib/Headers/wchar.h
new file mode 100644
index 0000000000000..ca232c0cd6356
--- /dev/null
+++ b/clang/lib/Headers/wchar.h
@@ -0,0 +1,62 @@
+/*===---- wchar.h - Standard header for wide character types --------------===*\
+ *
+ * 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 __CLANG_WCHAR_H
+#define __CLANG_WCHAR_H
+
+/* If we're hosted, fall back to the system's wchar.h, which might have
+ * additional definitions.
+ */
+
+#if __STDC_HOSTED__ && __has_include_next(<wchar.h>)
+
+#include_next <wchar.h>
+
+#else
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+wchar_t *wcscpy(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcsncpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t);    /* freestanding */
+wchar_t *wcscat(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcsncat(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+int wcscmp(const wchar_t *, const wchar_t *);             /* freestanding */
+int wcsncmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
+int wmemcmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
+const wchar_t *wcschr(const wchar_t *, wchar_t);          /* freestanding */
+wchar_t *wcschr(wchar_t *, wchar_t);                      /* freestanding */
+size_t wcscspn(const wchar_t *, const wchar_t *);         /* freestanding */
+const wchar_t *wcspbrk(const wchar_t *, const wchar_t *); /* freestanding */
+wchar_t *wcspbrk(wchar_t *, const wchar_t *);             /* freestanding */
+const wchar_t *wcsrchr(const wchar_t *, wchar_t);         /* freestanding */
+wchar_t *wcsrchr(wchar_t *, wchar_t);                     /* freestanding */
+size_t wcsspn(const wchar_t *, const wchar_t *);          /* freestanding */
+const wchar_t *wcsstr(const wchar_t *, const wchar_t *);  /* freestanding */
+wchar_t *wcsstr(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcstok(wchar_t *, const wchar_t *, wchar_t **);  /* freestanding */
+const wchar_t *wmemchr(const wchar_t *, wchar_t, size_t); /* freestanding */
+wchar_t *wmemchr(wchar_t *, wchar_t, size_t);             /* freestanding */
+size_t wcslen(const wchar_t *);                           /* freestanding */
+wchar_t *wmemset(wchar_t *, wchar_t, size_t);             /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..b92ff4b07777a 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,43 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +923,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +946,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..f144115ded7e4 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,8 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
+
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -859,6 +861,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
+#endif
+
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 __sort_dispatch(_RandomAccessIterator __first, _RandomAccessIterator __last, _Comp& __comp) {
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..cbfb448189360 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..d594f7d492304 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -52,12 +52,17 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #  include <__cstddef/size_t.h>
 
 // <time.h> is not provided by libc++
-#  if __has_include(<time.h>)
-#    include <time.h>
-#    ifdef _LIBCPP_TIME_H
-#      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
-#    endif
+#if __STDC_HOSTED__ && __has_include(<time.h>)
+#  include <time.h>
+#  ifdef _LIBCPP_TIME_H
+#    error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #  endif
+#else
+#  include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
+#endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #    pragma GCC system_header
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..e42485542d347 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,11 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
+#endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..b76e95089190e
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only %s
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding -DFREESTANDING %s
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(FREESTANDING) != defined(_LIBCPP_FREESTANDING)
+#  error _LIBCPP_FREESTANDING should be defined in freestanding mode and not \
+       defined in non-freestanding mode
+#  if defined(__has_feature)
+#    if __has_feature(modules)
+#      define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#    endif
+#  elif defined(__cpp_modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+
+#  if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#    include <cstddef>
+#    include <limits>
+#    include <cfloat>
+#    include <version>
+#    include <cstdint>
+#    include <cstdlib>
+#    include <new>
+#    include <typeinfo>
+#    if __has_include(<source_location>)
+#      include <source_location>
+#    endif
+#    include <exception>
+#    include <initializer_list>
+#    include <compare>
+#    include <coroutine>
+#    include <cstdarg>
+#    include <concepts>
+#    include <type_traits>
+#    include <bit>
+#    include <atomic>
+#    include <utility>
+#    include <tuple>
+#    include <memory>
+#    include <functional>
+#    include <ratio>
+#    include <iterator>
+#    include <ranges>
+#    include <typeinfo>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#    include <iosfwd>
+#    include <cstdio>
+#    include <cerrno>
+#    include <cstring>
+#    include <cmath>
+#    ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#      include <cwchar>
+#    endif
+#    include <array>
+#    include <span>
+#    include <algorithm>
+#    include <bitset>
+#    include <variant>
+#    include <optional>
+#    include <numbers>
+#    include <scoped_allocator>
+#    include <typeindex>
+#    include <string_view>
+#    include <numeric>
+#    include <complex>
+#    include <chrono>
+#    include <charconv>
+#    include <expected>
+#    include <random>
+#    include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#    include <vector>
+#    include <deque>
+#    include <string>
+#    include <stack>
+#    include <queue>
+#    include <list>
+#    include <forward_list>
+#    include <map>
+#    include <set>
+#    include <unordered_set>
+#    include <unordered_map>
+#    if __has_include(<mdspan>)
+#      include <mdspan>
+#    endif
+#    include <valarray>
+
+#    if _LIBCPP_STD_VER < 20
+#      include <ciso646>
+#      if __has_include(<cstdalign>)
+#        include <cstdalign>
+#      endif
+#      include <cstdbool>
+#    endif
+#  endif


From libcxx-commits at lists.llvm.org  Sat Jul 11 03:06:43 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 03:06:43 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [libcxx]support freestanding
	libc++ (PR #208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a5215b3.2020f6c2.243e78.83ba@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx

@llvm/pr-subscribers-backend-x86

Author: cqwrteur (trcrsired)

<details>
<summary>Changes</summary>

I am trying to add support fo freestanding C++. here is to check whether it passes CI.


---

Patch is 28.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/208900.diff


12 Files Affected:

- (added) clang/lib/Headers/errno.h (+164) 
- (added) clang/lib/Headers/math.h (+29) 
- (added) clang/lib/Headers/stdlib.h (+103) 
- (added) clang/lib/Headers/string.h (+65) 
- (added) clang/lib/Headers/wchar.h (+62) 
- (modified) libcxx/CMakeLists.txt (+41-13) 
- (modified) libcxx/include/__algorithm/sort.h (+4) 
- (modified) libcxx/include/__config (+4) 
- (modified) libcxx/include/__config_site.in (+3) 
- (modified) libcxx/include/ctime (+10-5) 
- (modified) libcxx/include/stdio.h (+5) 
- (added) libcxx/test/libcxx/libcpp_freestanding.sh.cpp (+114) 


``````````diff
diff --git a/clang/lib/Headers/errno.h b/clang/lib/Headers/errno.h
new file mode 100644
index 0000000000000..48fe84a9304ce
--- /dev/null
+++ b/clang/lib/Headers/errno.h
@@ -0,0 +1,164 @@
+/*===---- errno.h - Standard header for error numbers ---------------------===*\
+ *
+ * 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 __CLANG_ERRNO_H
+#define __CLANG_ERRNO_H
+
+/* If we're hosted, fall back to the system's errno.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<errno.h>)
+
+#include_next <errno.h>
+
+#else
+
+/* Adjusted to the linux asm/errno.h */
+#define EPERM 1            /* Operation not permitted */
+#define ENOENT 2           /* No such file or directory */
+#define ESRCH 3            /* No such process */
+#define EINTR 4            /* Interrupted system call */
+#define EIO 5              /* I/O error */
+#define ENXIO 6            /* No such device or address */
+#define E2BIG 7            /* Arg list too long */
+#define ENOEXEC 8          /* Exec format error */
+#define EBADF 9            /* Bad file number */
+#define ECHILD 10          /* No child processes */
+#define EAGAIN 11          /* Try again */
+#define ENOMEM 12          /* Out of memory */
+#define EACCES 13          /* Permission denied */
+#define EFAULT 14          /* Bad address */
+#define ENOTBLK 15         /* Block device required */
+#define EBUSY 16           /* Device or resource busy */
+#define EEXIST 17          /* File exists */
+#define EXDEV 18           /* Cross-device link */
+#define ENODEV 19          /* No such device */
+#define ENOTDIR 20         /* Not a directory */
+#define EISDIR 21          /* Is a directory */
+#define EINVAL 22          /* Invalid argument */
+#define ENFILE 23          /* File table overflow */
+#define EMFILE 24          /* Too many open files */
+#define ENOTTY 25          /* Not a typewriter */
+#define ETXTBSY 26         /* Text file busy */
+#define EFBIG 27           /* File too large */
+#define ENOSPC 28          /* No space left on device */
+#define ESPIPE 29          /* Illegal seek */
+#define EROFS 30           /* Read-only file system */
+#define EMLINK 31          /* Too many links */
+#define EPIPE 32           /* Broken pipe */
+#define EDOM 33            /* Math argument out of domain of func */
+#define ERANGE 34          /* Math result not representable */
+#define EDEADLK 35         /* Resource deadlock would occur */
+#define ENAMETOOLONG 36    /* File name too long */
+#define ENOLCK 37          /* No record locks available */
+#define ENOSYS 38          /* Function not implemented */
+#define ENOTEMPTY 39       /* Directory not empty */
+#define ELOOP 40           /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42          /* No message of desired type */
+#define EIDRM 43           /* Identifier removed */
+#define ECHRNG 44          /* Channel number out of range */
+#define EL2NSYNC 45        /* Level 2 not synchronized */
+#define EL3HLT 46          /* Level 3 halted */
+#define EL3RST 47          /* Level 3 reset */
+#define ELNRNG 48          /* Link number out of range */
+#define EUNATCH 49         /* Protocol driver not attached */
+#define ENOCSI 50          /* No CSI structure available */
+#define EL2HLT 51          /* Level 2 halted */
+#define EBADE 52           /* Invalid exchange */
+#define EBADR 53           /* Invalid request descriptor */
+#define EXFULL 54          /* Exchange full */
+#define ENOANO 55          /* No anode */
+#define EBADRQC 56         /* Invalid request code */
+#define EBADSLT 57         /* Invalid slot */
+#define EDEADLOCK EDEADLK
+#define EBFONT 59          /* Bad font file format */
+#define EFTYPE EBFONT      /* Inappropriate file type or format */
+#define ENOSTR 60          /* Device not a stream */
+#define ENODATA 61         /* No data available */
+#define ETIME 62           /* Timer expired */
+#define ENOSR 63           /* Out of streams resources */
+#define ENONET 64          /* Machine is not on the network */
+#define ENOPKG 65          /* Package not installed */
+#define EREMOTE 66         /* Object is remote */
+#define ENOLINK 67         /* Link has been severed */
+#define EADV 68            /* Advertise error */
+#define ESRMNT 69          /* Srmount error */
+#define ECOMM 70           /* Communication error on send */
+#define EPROTO 71          /* Protocol error */
+#define EMULTIHOP 72       /* Multihop attempted */
+#define EDOTDOT 73         /* RFS specific error */
+#define EBADMSG 74         /* Not a data message */
+#define EOVERFLOW 75       /* Value too large for defined data type */
+#define ENOTUNIQ 76        /* Name not unique on network */
+#define EBADFD 77          /* File descriptor in bad state */
+#define EREMCHG 78         /* Remote address changed */
+#define ELIBACC 79         /* Can not access a needed shared library */
+#define ELIBBAD 80         /* Accessing a corrupted shared library */
+#define ELIBSCN 81         /* .lib section in a.out corrupted */
+#define ELIBMAX 82         /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83        /* Cannot exec a shared library directly */
+#define EILSEQ 84          /* Illegal byte sequence */
+#define ERESTART 85        /* Interrupted system call should be restarted */
+#define ESTRPIPE 86        /* Streams pipe error */
+#define EUSERS 87          /* Too many users */
+#define ENOTSOCK 88        /* Socket operation on non-socket */
+#define EDESTADDRREQ 89    /* Destination address required */
+#define EMSGSIZE 90        /* Message too long */
+#define EPROTOTYPE 91      /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92     /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95      /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96    /* Protocol family not supported */
+#define EAFNOSUPPORT 97    /* Address family not supported by protocol */
+#define EADDRINUSE 98      /* Address already in use */
+#define EADDRNOTAVAIL 99   /* Cannot assign requested address */
+#define ENETDOWN 100       /* Network is down */
+#define ENETUNREACH 101    /* Network is unreachable */
+#define ENETRESET 102      /* Network dropped connection because of reset */
+#define ECONNABORTED 103   /* Software caused connection abort */
+#define ECONNRESET 104     /* Connection reset by peer */
+#define ENOBUFS 105        /* No buffer space available */
+#define EISCONN 106        /* Transport endpoint is already connected */
+#define ENOTCONN 107       /* Transport endpoint is not connected */
+#define ESHUTDOWN 108      /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109   /* Too many references: cannot splice */
+#define ETIMEDOUT 110      /* Connection timed out */
+#define ECONNREFUSED 111   /* Connection refused */
+#define EHOSTDOWN 112      /* Host is down */
+#define EHOSTUNREACH 113   /* No route to host */
+#define EALREADY 114       /* Operation already in progress */
+#define EINPROGRESS 115    /* Operation now in progress */
+#define ESTALE 116         /* Stale NFS file handle */
+#define EUCLEAN 117        /* Structure needs cleaning */
+#define ENOTNAM 118        /* Not a XENIX named type file */
+#define ENAVAIL 119        /* No XENIX semaphores available */
+#define EISNAM 120         /* Is a named type file */
+#define EREMOTEIO 121      /* Remote I/O error */
+#define EDQUOT 122         /* Quota exceeded */
+#define ENOMEDIUM 123      /* No medium found */
+#define EMEDIUMTYPE 124    /* Wrong medium type */
+#define ECANCELED 125      /* Operation Canceled */
+#define ENOKEY 126         /* Required key not available */
+#define EKEYEXPIRED 127    /* Key has expired */
+#define EKEYREVOKED 128    /* Key has been revoked */
+#define EKEYREJECTED 129   /* Key was rejected by service */
+#define EOWNERDEAD 130     /* Owner died */
+#define ENOTRECOVERABLE 131 /* State not recoverable */
+#define ERFKILL 132         /* Operation not possible due to RF-kill */
+#define EHWPOISON 133       /* Memory page has hardware error */
+
+/* Widely known to be a synonym in Linux.  */
+#define ENOTSUP EOPNOTSUPP
+
+#define __ELASTERROR 2000 /* Users can add values starting here */
+
+#endif
+
+#endif
diff --git a/clang/lib/Headers/math.h b/clang/lib/Headers/math.h
new file mode 100644
index 0000000000000..3e316c891a1bc
--- /dev/null
+++ b/clang/lib/Headers/math.h
@@ -0,0 +1,29 @@
+/*===---- math.h - Standard header for C math ----------------------------===*\
+ *
+ * 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 __CLANG_MATH_H
+#define __CLANG_MATH_H
+
+/* If we're hosted, fall back to the system's math.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<math.h>)
+
+#include_next <math.h>
+
+#else
+
+#define NAN __builtin_nanf("")
+#define INFINITY __builtin_inff()
+#define FP_NAN 0
+#define FP_INFINITE 1
+#define FP_ZERO 2
+#define FP_SUBNORMAL 3
+#define FP_NORMAL 4
+
+#endif
diff --git a/clang/lib/Headers/stdlib.h b/clang/lib/Headers/stdlib.h
new file mode 100644
index 0000000000000..db5fa08385f58
--- /dev/null
+++ b/clang/lib/Headers/stdlib.h
@@ -0,0 +1,103 @@
+/*===---- stdlib.h - Standard header for general utilities ----------------===*\
+ *
+ * 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 __CLANG_STDLIB_H
+#define __CLANG_STDLIB_H
+
+/* If we're hosted, fall back to the system's math.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<stdlib.h>)
+
+#include_next <stdlib.h>
+
+#else
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1 /* freestanding */
+#endif
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0 /* freestanding */
+#endif
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+  int quot;
+  int rem;
+} div_t; /* freestanding */
+typedef struct {
+  long quot;
+  long rem;
+} ldiv_t; /* freestanding */
+typedef struct {
+  long long quot;
+  long long rem;
+} lldiv_t; /* freestanding */
+typedef struct {
+  intmax_t quot;
+  intmax_t rem;
+} imaxdiv_t; /* freestanding */
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+// start and termination
+
+#ifndef __CLANG_STDLIB_NOEXCEPT
+#ifdef __cplusplus
+#if __cplusplus >= 201103L
+#define __CLANG_STDLIB_NOEXCEPT noexcept
+#else
+#define __CLANG_STDLIB_NOEXCEPT throw()
+#endif
+#else
+#define __CLANG_STDLIB_NOEXCEPT
+#endif
+#endif
+
+[[noreturn]] void abort() __CLANG_STDLIB_NOEXCEPT;         /* freestanding */
+int atexit(void (*)()) __CLANG_STDLIB_NOEXCEPT;            /* freestanding */
+int at_quick_exit(void (*)()) __CLANG_STDLIB_NOEXCEPT;     /* freestanding */
+[[noreturn]] void exit(int);                               /* freestanding */
+[[noreturn]] void _Exit(int) __CLANG_STDLIB_NOEXCEPT;      /* freestanding */
+[[noreturn]] void quick_exit(int) __CLANG_STDLIB_NOEXCEPT; /* freestanding */
+
+#undef __CLANG_STDLIB_NOEXCEPT
+
+// C standard library algorithms
+void *bsearch(const void *, const void *, size_t, size_t,
+              int (*)(const void *, const void *)); /* freestanding */
+void qsort(void *, size_t, size_t,
+           int (*)(const void *, const void *)); /* freestanding */
+
+// absolute values
+int abs(int j);                       /* freestanding */
+long int abs(long int j);             /* freestanding */
+long long int abs(long long int j);   /* freestanding */
+long int labs(long int j);            /* freestanding */
+long long int llabs(long long int j); /* freestanding */
+
+div_t div(int, int);                         /* freestanding */
+ldiv_t div(long int, long int);              /* freestanding */
+lldiv_t div(long long int, long long int);   /* freestanding */
+ldiv_t ldiv(long int, long int);             /* freestanding */
+lldiv_t lldiv(long long int, long long int); /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
diff --git a/clang/lib/Headers/string.h b/clang/lib/Headers/string.h
new file mode 100644
index 0000000000000..c256b3e35a95b
--- /dev/null
+++ b/clang/lib/Headers/string.h
@@ -0,0 +1,65 @@
+/*===---- string.h - Standard header for null-terminated string -----------===*\
+ *
+ * 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 __CLANG_STRING_H
+#define __CLANG_STRING_H
+
+/* If we're hosted, fall back to the system's string.h, which might have
+ * additional definitions.
+ */
+
+#if __STDC_HOSTED__ && __has_include_next(<string.h>)
+
+#include_next <string.h>
+
+#else
+
+#include <stddef.h>
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void *memcpy(void *, const void *, size_t);      /* freestanding */
+void *memmove(void *, const void *, size_t);     /* freestanding */
+char *strcpy(char *, const char *);              /* freestanding */
+char *strncpy(char *, const char *, size_t);     /* freestanding */
+char *strcat(char *, const char *);              /* freestanding */
+char *strncat(char *, const char *, size_t);     /* freestanding */
+int memcmp(const void *, const void *, size_t);  /* freestanding */
+int strcmp(const char *, const char *);          /* freestanding */
+int strncmp(const char *, const char *, size_t); /* freestanding */
+const void *memchr(const void *, int, size_t);   /* freestanding */
+void *memchr(void *, int, size_t);               /* freestanding */
+const char *strchr(const char *, int);           /* freestanding */
+char *strchr(char *, int);                       /* freestanding */
+size_t strcspn(const char *, const char *);      /* freestanding */
+const char *strpbrk(const char *, const char *); /* freestanding */
+char *strpbrk(char *, const char *);             /* freestanding */
+const char *strrchr(const char *, int);          /* freestanding */
+char *strrchr(char *, int);                      /* freestanding */
+size_t strspn(const char *, const char *);       /* freestanding */
+const char *strstr(const char *, const char *);  /* freestanding */
+char *strstr(char *, const char *);              /* freestanding */
+char *strtok(char *, const char *);              /* freestanding */
+void *memset(void *, int, size_t);               /* freestanding */
+size_t strlen(const char *);                     /* freestanding */
+void *memset_explicit(void *, int, size_t);      /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/clang/lib/Headers/wchar.h b/clang/lib/Headers/wchar.h
new file mode 100644
index 0000000000000..ca232c0cd6356
--- /dev/null
+++ b/clang/lib/Headers/wchar.h
@@ -0,0 +1,62 @@
+/*===---- wchar.h - Standard header for wide character types --------------===*\
+ *
+ * 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 __CLANG_WCHAR_H
+#define __CLANG_WCHAR_H
+
+/* If we're hosted, fall back to the system's wchar.h, which might have
+ * additional definitions.
+ */
+
+#if __STDC_HOSTED__ && __has_include_next(<wchar.h>)
+
+#include_next <wchar.h>
+
+#else
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+wchar_t *wcscpy(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcsncpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t);    /* freestanding */
+wchar_t *wcscat(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcsncat(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+int wcscmp(const wchar_t *, const wchar_t *);             /* freestanding */
+int wcsncmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
+int wmemcmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
+const wchar_t *wcschr(const wchar_t *, wchar_t);          /* freestanding */
+wchar_t *wcschr(wchar_t *, wchar_t);                      /* freestanding */
+size_t wcscspn(const wchar_t *, const wchar_t *);         /* freestanding */
+const wchar_t *wcspbrk(const wchar_t *, const wchar_t *); /* freestanding */
+wchar_t *wcspbrk(wchar_t *, const wchar_t *);             /* freestanding */
+const wchar_t *wcsrchr(const wchar_t *, wchar_t);         /* freestanding */
+wchar_t *wcsrchr(wchar_t *, wchar_t);                     /* freestanding */
+size_t wcsspn(const wchar_t *, const wchar_t *);          /* freestanding */
+const wchar_t *wcsstr(const wchar_t *, const wchar_t *);  /* freestanding */
+wchar_t *wcsstr(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcstok(wchar_t *, const wchar_t *, wchar_t **);  /* freestanding */
+const wchar_t *wmemchr(const wchar_t *, wchar_t, size_t); /* freestanding */
+wchar_t *wmemchr(wchar_t *, wchar_t, size_t);             /* freestanding */
+size_t wcslen(const wchar_t *);                           /* freestanding */
+wchar_t *wmemset(wchar_t *, wchar_t, size_t);             /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..b92ff4b07777a 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,43 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sat Jul 11 03:07:48 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 03:07:48 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [libcxx]support freestanding
	libc++ (PR #208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a5215f4.c56fb102.7c9cd.a1be@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions ,cpp,h -- clang/lib/Headers/errno.h clang/lib/Headers/math.h clang/lib/Headers/stdlib.h clang/lib/Headers/string.h clang/lib/Headers/wchar.h libcxx/test/libcxx/libcpp_freestanding.sh.cpp libcxx/include/__algorithm/sort.h libcxx/include/__config libcxx/include/ctime libcxx/include/stdio.h --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index d594f7d49..6140faed9 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -52,17 +52,17 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #  include <__cstddef/size_t.h>
 
 // <time.h> is not provided by libc++
-#if __STDC_HOSTED__ && __has_include(<time.h>)
-#  include <time.h>
-#  ifdef _LIBCPP_TIME_H
-#    error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
-#  endif
-#else
-#  include <cstdint>
+#  if __STDC_HOSTED__ && __has_include(<time.h>)
+#    include <time.h>
+#    ifdef _LIBCPP_TIME_H
+#      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
+#    endif
+#  else
+#    include <cstdint>
 extern "C" {
 typedef ::std::uint_least64_t time_t;
 }
-#endif
+#  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #    pragma GCC system_header

``````````

</details>


https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sat Jul 11 03:12:11 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 03:12:11 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [libcxx]support freestanding
	libc++ (PR #208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a5216fb.bd93e731.358318.913e@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 6cabf2c78b79590476555c0066c20083c2c36c52 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libcxx] Support freestanding libc++

---
 clang/lib/Headers/errno.h                     | 164 ++++++++++++++++++
 clang/lib/Headers/math.h                      |  29 ++++
 clang/lib/Headers/stdlib.h                    | 103 +++++++++++
 clang/lib/Headers/string.h                    |  65 +++++++
 clang/lib/Headers/wchar.h                     |  62 +++++++
 libcxx/CMakeLists.txt                         |  54 ++++--
 libcxx/include/__algorithm/sort.h             |   4 +
 libcxx/include/__config                       |   4 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/ctime                          |   7 +-
 libcxx/include/stdio.h                        |   5 +
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 114 ++++++++++++
 12 files changed, 600 insertions(+), 14 deletions(-)
 create mode 100644 clang/lib/Headers/errno.h
 create mode 100644 clang/lib/Headers/math.h
 create mode 100644 clang/lib/Headers/stdlib.h
 create mode 100644 clang/lib/Headers/string.h
 create mode 100644 clang/lib/Headers/wchar.h
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/clang/lib/Headers/errno.h b/clang/lib/Headers/errno.h
new file mode 100644
index 0000000000000..48fe84a9304ce
--- /dev/null
+++ b/clang/lib/Headers/errno.h
@@ -0,0 +1,164 @@
+/*===---- errno.h - Standard header for error numbers ---------------------===*\
+ *
+ * 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 __CLANG_ERRNO_H
+#define __CLANG_ERRNO_H
+
+/* If we're hosted, fall back to the system's errno.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<errno.h>)
+
+#include_next <errno.h>
+
+#else
+
+/* Adjusted to the linux asm/errno.h */
+#define EPERM 1            /* Operation not permitted */
+#define ENOENT 2           /* No such file or directory */
+#define ESRCH 3            /* No such process */
+#define EINTR 4            /* Interrupted system call */
+#define EIO 5              /* I/O error */
+#define ENXIO 6            /* No such device or address */
+#define E2BIG 7            /* Arg list too long */
+#define ENOEXEC 8          /* Exec format error */
+#define EBADF 9            /* Bad file number */
+#define ECHILD 10          /* No child processes */
+#define EAGAIN 11          /* Try again */
+#define ENOMEM 12          /* Out of memory */
+#define EACCES 13          /* Permission denied */
+#define EFAULT 14          /* Bad address */
+#define ENOTBLK 15         /* Block device required */
+#define EBUSY 16           /* Device or resource busy */
+#define EEXIST 17          /* File exists */
+#define EXDEV 18           /* Cross-device link */
+#define ENODEV 19          /* No such device */
+#define ENOTDIR 20         /* Not a directory */
+#define EISDIR 21          /* Is a directory */
+#define EINVAL 22          /* Invalid argument */
+#define ENFILE 23          /* File table overflow */
+#define EMFILE 24          /* Too many open files */
+#define ENOTTY 25          /* Not a typewriter */
+#define ETXTBSY 26         /* Text file busy */
+#define EFBIG 27           /* File too large */
+#define ENOSPC 28          /* No space left on device */
+#define ESPIPE 29          /* Illegal seek */
+#define EROFS 30           /* Read-only file system */
+#define EMLINK 31          /* Too many links */
+#define EPIPE 32           /* Broken pipe */
+#define EDOM 33            /* Math argument out of domain of func */
+#define ERANGE 34          /* Math result not representable */
+#define EDEADLK 35         /* Resource deadlock would occur */
+#define ENAMETOOLONG 36    /* File name too long */
+#define ENOLCK 37          /* No record locks available */
+#define ENOSYS 38          /* Function not implemented */
+#define ENOTEMPTY 39       /* Directory not empty */
+#define ELOOP 40           /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42          /* No message of desired type */
+#define EIDRM 43           /* Identifier removed */
+#define ECHRNG 44          /* Channel number out of range */
+#define EL2NSYNC 45        /* Level 2 not synchronized */
+#define EL3HLT 46          /* Level 3 halted */
+#define EL3RST 47          /* Level 3 reset */
+#define ELNRNG 48          /* Link number out of range */
+#define EUNATCH 49         /* Protocol driver not attached */
+#define ENOCSI 50          /* No CSI structure available */
+#define EL2HLT 51          /* Level 2 halted */
+#define EBADE 52           /* Invalid exchange */
+#define EBADR 53           /* Invalid request descriptor */
+#define EXFULL 54          /* Exchange full */
+#define ENOANO 55          /* No anode */
+#define EBADRQC 56         /* Invalid request code */
+#define EBADSLT 57         /* Invalid slot */
+#define EDEADLOCK EDEADLK
+#define EBFONT 59          /* Bad font file format */
+#define EFTYPE EBFONT      /* Inappropriate file type or format */
+#define ENOSTR 60          /* Device not a stream */
+#define ENODATA 61         /* No data available */
+#define ETIME 62           /* Timer expired */
+#define ENOSR 63           /* Out of streams resources */
+#define ENONET 64          /* Machine is not on the network */
+#define ENOPKG 65          /* Package not installed */
+#define EREMOTE 66         /* Object is remote */
+#define ENOLINK 67         /* Link has been severed */
+#define EADV 68            /* Advertise error */
+#define ESRMNT 69          /* Srmount error */
+#define ECOMM 70           /* Communication error on send */
+#define EPROTO 71          /* Protocol error */
+#define EMULTIHOP 72       /* Multihop attempted */
+#define EDOTDOT 73         /* RFS specific error */
+#define EBADMSG 74         /* Not a data message */
+#define EOVERFLOW 75       /* Value too large for defined data type */
+#define ENOTUNIQ 76        /* Name not unique on network */
+#define EBADFD 77          /* File descriptor in bad state */
+#define EREMCHG 78         /* Remote address changed */
+#define ELIBACC 79         /* Can not access a needed shared library */
+#define ELIBBAD 80         /* Accessing a corrupted shared library */
+#define ELIBSCN 81         /* .lib section in a.out corrupted */
+#define ELIBMAX 82         /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83        /* Cannot exec a shared library directly */
+#define EILSEQ 84          /* Illegal byte sequence */
+#define ERESTART 85        /* Interrupted system call should be restarted */
+#define ESTRPIPE 86        /* Streams pipe error */
+#define EUSERS 87          /* Too many users */
+#define ENOTSOCK 88        /* Socket operation on non-socket */
+#define EDESTADDRREQ 89    /* Destination address required */
+#define EMSGSIZE 90        /* Message too long */
+#define EPROTOTYPE 91      /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92     /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95      /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96    /* Protocol family not supported */
+#define EAFNOSUPPORT 97    /* Address family not supported by protocol */
+#define EADDRINUSE 98      /* Address already in use */
+#define EADDRNOTAVAIL 99   /* Cannot assign requested address */
+#define ENETDOWN 100       /* Network is down */
+#define ENETUNREACH 101    /* Network is unreachable */
+#define ENETRESET 102      /* Network dropped connection because of reset */
+#define ECONNABORTED 103   /* Software caused connection abort */
+#define ECONNRESET 104     /* Connection reset by peer */
+#define ENOBUFS 105        /* No buffer space available */
+#define EISCONN 106        /* Transport endpoint is already connected */
+#define ENOTCONN 107       /* Transport endpoint is not connected */
+#define ESHUTDOWN 108      /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109   /* Too many references: cannot splice */
+#define ETIMEDOUT 110      /* Connection timed out */
+#define ECONNREFUSED 111   /* Connection refused */
+#define EHOSTDOWN 112      /* Host is down */
+#define EHOSTUNREACH 113   /* No route to host */
+#define EALREADY 114       /* Operation already in progress */
+#define EINPROGRESS 115    /* Operation now in progress */
+#define ESTALE 116         /* Stale NFS file handle */
+#define EUCLEAN 117        /* Structure needs cleaning */
+#define ENOTNAM 118        /* Not a XENIX named type file */
+#define ENAVAIL 119        /* No XENIX semaphores available */
+#define EISNAM 120         /* Is a named type file */
+#define EREMOTEIO 121      /* Remote I/O error */
+#define EDQUOT 122         /* Quota exceeded */
+#define ENOMEDIUM 123      /* No medium found */
+#define EMEDIUMTYPE 124    /* Wrong medium type */
+#define ECANCELED 125      /* Operation Canceled */
+#define ENOKEY 126         /* Required key not available */
+#define EKEYEXPIRED 127    /* Key has expired */
+#define EKEYREVOKED 128    /* Key has been revoked */
+#define EKEYREJECTED 129   /* Key was rejected by service */
+#define EOWNERDEAD 130     /* Owner died */
+#define ENOTRECOVERABLE 131 /* State not recoverable */
+#define ERFKILL 132         /* Operation not possible due to RF-kill */
+#define EHWPOISON 133       /* Memory page has hardware error */
+
+/* Widely known to be a synonym in Linux.  */
+#define ENOTSUP EOPNOTSUPP
+
+#define __ELASTERROR 2000 /* Users can add values starting here */
+
+#endif
+
+#endif
diff --git a/clang/lib/Headers/math.h b/clang/lib/Headers/math.h
new file mode 100644
index 0000000000000..3e316c891a1bc
--- /dev/null
+++ b/clang/lib/Headers/math.h
@@ -0,0 +1,29 @@
+/*===---- math.h - Standard header for C math ----------------------------===*\
+ *
+ * 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 __CLANG_MATH_H
+#define __CLANG_MATH_H
+
+/* If we're hosted, fall back to the system's math.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<math.h>)
+
+#include_next <math.h>
+
+#else
+
+#define NAN __builtin_nanf("")
+#define INFINITY __builtin_inff()
+#define FP_NAN 0
+#define FP_INFINITE 1
+#define FP_ZERO 2
+#define FP_SUBNORMAL 3
+#define FP_NORMAL 4
+
+#endif
diff --git a/clang/lib/Headers/stdlib.h b/clang/lib/Headers/stdlib.h
new file mode 100644
index 0000000000000..db5fa08385f58
--- /dev/null
+++ b/clang/lib/Headers/stdlib.h
@@ -0,0 +1,103 @@
+/*===---- stdlib.h - Standard header for general utilities ----------------===*\
+ *
+ * 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 __CLANG_STDLIB_H
+#define __CLANG_STDLIB_H
+
+/* If we're hosted, fall back to the system's math.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<stdlib.h>)
+
+#include_next <stdlib.h>
+
+#else
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1 /* freestanding */
+#endif
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0 /* freestanding */
+#endif
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+  int quot;
+  int rem;
+} div_t; /* freestanding */
+typedef struct {
+  long quot;
+  long rem;
+} ldiv_t; /* freestanding */
+typedef struct {
+  long long quot;
+  long long rem;
+} lldiv_t; /* freestanding */
+typedef struct {
+  intmax_t quot;
+  intmax_t rem;
+} imaxdiv_t; /* freestanding */
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+// start and termination
+
+#ifndef __CLANG_STDLIB_NOEXCEPT
+#ifdef __cplusplus
+#if __cplusplus >= 201103L
+#define __CLANG_STDLIB_NOEXCEPT noexcept
+#else
+#define __CLANG_STDLIB_NOEXCEPT throw()
+#endif
+#else
+#define __CLANG_STDLIB_NOEXCEPT
+#endif
+#endif
+
+[[noreturn]] void abort() __CLANG_STDLIB_NOEXCEPT;         /* freestanding */
+int atexit(void (*)()) __CLANG_STDLIB_NOEXCEPT;            /* freestanding */
+int at_quick_exit(void (*)()) __CLANG_STDLIB_NOEXCEPT;     /* freestanding */
+[[noreturn]] void exit(int);                               /* freestanding */
+[[noreturn]] void _Exit(int) __CLANG_STDLIB_NOEXCEPT;      /* freestanding */
+[[noreturn]] void quick_exit(int) __CLANG_STDLIB_NOEXCEPT; /* freestanding */
+
+#undef __CLANG_STDLIB_NOEXCEPT
+
+// C standard library algorithms
+void *bsearch(const void *, const void *, size_t, size_t,
+              int (*)(const void *, const void *)); /* freestanding */
+void qsort(void *, size_t, size_t,
+           int (*)(const void *, const void *)); /* freestanding */
+
+// absolute values
+int abs(int j);                       /* freestanding */
+long int abs(long int j);             /* freestanding */
+long long int abs(long long int j);   /* freestanding */
+long int labs(long int j);            /* freestanding */
+long long int llabs(long long int j); /* freestanding */
+
+div_t div(int, int);                         /* freestanding */
+ldiv_t div(long int, long int);              /* freestanding */
+lldiv_t div(long long int, long long int);   /* freestanding */
+ldiv_t ldiv(long int, long int);             /* freestanding */
+lldiv_t lldiv(long long int, long long int); /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
diff --git a/clang/lib/Headers/string.h b/clang/lib/Headers/string.h
new file mode 100644
index 0000000000000..c256b3e35a95b
--- /dev/null
+++ b/clang/lib/Headers/string.h
@@ -0,0 +1,65 @@
+/*===---- string.h - Standard header for null-terminated string -----------===*\
+ *
+ * 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 __CLANG_STRING_H
+#define __CLANG_STRING_H
+
+/* If we're hosted, fall back to the system's string.h, which might have
+ * additional definitions.
+ */
+
+#if __STDC_HOSTED__ && __has_include_next(<string.h>)
+
+#include_next <string.h>
+
+#else
+
+#include <stddef.h>
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void *memcpy(void *, const void *, size_t);      /* freestanding */
+void *memmove(void *, const void *, size_t);     /* freestanding */
+char *strcpy(char *, const char *);              /* freestanding */
+char *strncpy(char *, const char *, size_t);     /* freestanding */
+char *strcat(char *, const char *);              /* freestanding */
+char *strncat(char *, const char *, size_t);     /* freestanding */
+int memcmp(const void *, const void *, size_t);  /* freestanding */
+int strcmp(const char *, const char *);          /* freestanding */
+int strncmp(const char *, const char *, size_t); /* freestanding */
+const void *memchr(const void *, int, size_t);   /* freestanding */
+void *memchr(void *, int, size_t);               /* freestanding */
+const char *strchr(const char *, int);           /* freestanding */
+char *strchr(char *, int);                       /* freestanding */
+size_t strcspn(const char *, const char *);      /* freestanding */
+const char *strpbrk(const char *, const char *); /* freestanding */
+char *strpbrk(char *, const char *);             /* freestanding */
+const char *strrchr(const char *, int);          /* freestanding */
+char *strrchr(char *, int);                      /* freestanding */
+size_t strspn(const char *, const char *);       /* freestanding */
+const char *strstr(const char *, const char *);  /* freestanding */
+char *strstr(char *, const char *);              /* freestanding */
+char *strtok(char *, const char *);              /* freestanding */
+void *memset(void *, int, size_t);               /* freestanding */
+size_t strlen(const char *);                     /* freestanding */
+void *memset_explicit(void *, int, size_t);      /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/clang/lib/Headers/wchar.h b/clang/lib/Headers/wchar.h
new file mode 100644
index 0000000000000..ca232c0cd6356
--- /dev/null
+++ b/clang/lib/Headers/wchar.h
@@ -0,0 +1,62 @@
+/*===---- wchar.h - Standard header for wide character types --------------===*\
+ *
+ * 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 __CLANG_WCHAR_H
+#define __CLANG_WCHAR_H
+
+/* If we're hosted, fall back to the system's wchar.h, which might have
+ * additional definitions.
+ */
+
+#if __STDC_HOSTED__ && __has_include_next(<wchar.h>)
+
+#include_next <wchar.h>
+
+#else
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+wchar_t *wcscpy(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcsncpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t);    /* freestanding */
+wchar_t *wcscat(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcsncat(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+int wcscmp(const wchar_t *, const wchar_t *);             /* freestanding */
+int wcsncmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
+int wmemcmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
+const wchar_t *wcschr(const wchar_t *, wchar_t);          /* freestanding */
+wchar_t *wcschr(wchar_t *, wchar_t);                      /* freestanding */
+size_t wcscspn(const wchar_t *, const wchar_t *);         /* freestanding */
+const wchar_t *wcspbrk(const wchar_t *, const wchar_t *); /* freestanding */
+wchar_t *wcspbrk(wchar_t *, const wchar_t *);             /* freestanding */
+const wchar_t *wcsrchr(const wchar_t *, wchar_t);         /* freestanding */
+wchar_t *wcsrchr(wchar_t *, wchar_t);                     /* freestanding */
+size_t wcsspn(const wchar_t *, const wchar_t *);          /* freestanding */
+const wchar_t *wcsstr(const wchar_t *, const wchar_t *);  /* freestanding */
+wchar_t *wcsstr(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcstok(wchar_t *, const wchar_t *, wchar_t **);  /* freestanding */
+const wchar_t *wmemchr(const wchar_t *, wchar_t, size_t); /* freestanding */
+wchar_t *wmemchr(wchar_t *, wchar_t, size_t);             /* freestanding */
+size_t wcslen(const wchar_t *);                           /* freestanding */
+wchar_t *wmemset(wchar_t *, wchar_t, size_t);             /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..b92ff4b07777a 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,43 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +923,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +946,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..f144115ded7e4 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,8 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
+
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -859,6 +861,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
+#endif
+
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 __sort_dispatch(_RandomAccessIterator __first, _RandomAccessIterator __last, _Comp& __comp) {
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..cbfb448189360 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..6140faed9550f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -52,11 +52,16 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #  include <__cstddef/size_t.h>
 
 // <time.h> is not provided by libc++
-#  if __has_include(<time.h>)
+#  if __STDC_HOSTED__ && __has_include(<time.h>)
 #    include <time.h>
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..e42485542d347 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,11 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
+#endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..b76e95089190e
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only %s
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding -DFREESTANDING %s
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(FREESTANDING) != defined(_LIBCPP_FREESTANDING)
+#  error _LIBCPP_FREESTANDING should be defined in freestanding mode and not \
+       defined in non-freestanding mode
+#  if defined(__has_feature)
+#    if __has_feature(modules)
+#      define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#    endif
+#  elif defined(__cpp_modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+
+#  if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#    include <cstddef>
+#    include <limits>
+#    include <cfloat>
+#    include <version>
+#    include <cstdint>
+#    include <cstdlib>
+#    include <new>
+#    include <typeinfo>
+#    if __has_include(<source_location>)
+#      include <source_location>
+#    endif
+#    include <exception>
+#    include <initializer_list>
+#    include <compare>
+#    include <coroutine>
+#    include <cstdarg>
+#    include <concepts>
+#    include <type_traits>
+#    include <bit>
+#    include <atomic>
+#    include <utility>
+#    include <tuple>
+#    include <memory>
+#    include <functional>
+#    include <ratio>
+#    include <iterator>
+#    include <ranges>
+#    include <typeinfo>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#    include <iosfwd>
+#    include <cstdio>
+#    include <cerrno>
+#    include <cstring>
+#    include <cmath>
+#    ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#      include <cwchar>
+#    endif
+#    include <array>
+#    include <span>
+#    include <algorithm>
+#    include <bitset>
+#    include <variant>
+#    include <optional>
+#    include <numbers>
+#    include <scoped_allocator>
+#    include <typeindex>
+#    include <string_view>
+#    include <numeric>
+#    include <complex>
+#    include <chrono>
+#    include <charconv>
+#    include <expected>
+#    include <random>
+#    include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#    include <vector>
+#    include <deque>
+#    include <string>
+#    include <stack>
+#    include <queue>
+#    include <list>
+#    include <forward_list>
+#    include <map>
+#    include <set>
+#    include <unordered_set>
+#    include <unordered_map>
+#    if __has_include(<mdspan>)
+#      include <mdspan>
+#    endif
+#    include <valarray>
+
+#    if _LIBCPP_STD_VER < 20
+#      include <ciso646>
+#      if __has_include(<cstdalign>)
+#        include <cstdalign>
+#      endif
+#      include <cstdbool>
+#    endif
+#  endif


From libcxx-commits at lists.llvm.org  Sat Jul 11 03:37:03 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 03:37:03 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [libcxx]support freestanding
	libc++ (PR #208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a521ccf.c574b9b4.21ead0.3a77@mx.google.com>


github-actions[bot] wrote:

<!--PREMERGE ADVISOR COMMENT: Linux-->
# :penguin: Linux x64 Test Results

* 56339 tests passed
* 1172 tests skipped

All executed tests passed, but another part of the build **failed**. Click on a failure below to see the details.

<details>
<summary>libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/abort_message.cpp.o</summary>

```
FAILED: libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/abort_message.cpp.o
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/./bin/clang++ --target=x86_64-unknown-linux-gnu -DHAVE___CXA_THREAD_ATEXIT_IMPL -DLIBCXX_BUILDING_LIBCXXABI -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION=2 -D_LIBCPP_BUILDING_LIBRARY -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCXXABI_BUILDING_LIBRARY -D_LIBCXXABI_LINK_PTHREAD_LIB -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/home/gha/actions-runner/_work/llvm-project/llvm-project/libunwind/include -I/home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/../libcxx/src -I/home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/include -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/x86_64-unknown-linux-gnu/c++/v1 -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1 -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wno-pass-failed -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Xclang -fno-pch-timestamp  -O3 -DNDEBUG -std=c++23 -fPIC -nostdinc++ -fstrict-aliasing -funwind-tables -D_DEBUG -UNDEBUG -UNDEBUG -Wall -Wextra -Wnewline-eof -Wshadow -Wwrite-strings -Wno-unused-parameter -Wno-long-long -Werror=return-type -Wextra-semi -Wundef -Wunused-template -Wformat-nonliteral -Wzero-length-array -Wdeprecated-redundant-constexpr-static-def -Wno-nullability-completeness -Wmissing-prototypes -Wno-user-defined-literals -Wno-covered-switch-default -Wno-suggest-override -Wno-error -fsized-deallocation -fvisibility=hidden -fdebug-prefix-map=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1=/home/gha/actions-runner/_work/llvm-project/llvm-project/libcxx/include -MD -MT libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/abort_message.cpp.o -MF libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/abort_message.cpp.o.d -o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/abort_message.cpp.o -c /home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/src/abort_message.cpp
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/src/abort_message.cpp:10:
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1/stdio.h:110:2: error: #endif without #if
110 | #endif
|  ^
1 error generated.
```
</details>
<details>
<summary>libcxxabi/src/CMakeFiles/cxxabi_static_objects.dir/abort_message.cpp.o</summary>

```
FAILED: libcxxabi/src/CMakeFiles/cxxabi_static_objects.dir/abort_message.cpp.o
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/./bin/clang++ --target=x86_64-unknown-linux-gnu -DHAVE___CXA_THREAD_ATEXIT_IMPL -DLIBCXX_BUILDING_LIBCXXABI -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION=2 -D_LIBCPP_BUILDING_LIBRARY -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCXXABI_BUILDING_LIBRARY -D_LIBCXXABI_LINK_PTHREAD_LIB -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/home/gha/actions-runner/_work/llvm-project/llvm-project/libunwind/include -I/home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/../libcxx/src -I/home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/include -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/x86_64-unknown-linux-gnu/c++/v1 -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1 -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wno-pass-failed -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Xclang -fno-pch-timestamp  -O3 -DNDEBUG -std=c++23 -nostdinc++ -fstrict-aliasing -funwind-tables -D_DEBUG -UNDEBUG -UNDEBUG -Wall -Wextra -Wnewline-eof -Wshadow -Wwrite-strings -Wno-unused-parameter -Wno-long-long -Werror=return-type -Wextra-semi -Wundef -Wunused-template -Wformat-nonliteral -Wzero-length-array -Wdeprecated-redundant-constexpr-static-def -Wno-nullability-completeness -Wmissing-prototypes -Wno-user-defined-literals -Wno-covered-switch-default -Wno-suggest-override -Wno-error -fsized-deallocation -fvisibility=hidden -fdebug-prefix-map=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1=/home/gha/actions-runner/_work/llvm-project/llvm-project/libcxx/include -MD -MT libcxxabi/src/CMakeFiles/cxxabi_static_objects.dir/abort_message.cpp.o -MF libcxxabi/src/CMakeFiles/cxxabi_static_objects.dir/abort_message.cpp.o.d -o libcxxabi/src/CMakeFiles/cxxabi_static_objects.dir/abort_message.cpp.o -c /home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/src/abort_message.cpp
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/src/abort_message.cpp:10:
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1/stdio.h:110:2: error: #endif without #if
110 | #endif
|  ^
1 error generated.
```
</details>
<details>
<summary>libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_demangle.cpp.o</summary>

```
FAILED: libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_demangle.cpp.o
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/./bin/clang++ --target=x86_64-unknown-linux-gnu -DHAVE___CXA_THREAD_ATEXIT_IMPL -DLIBCXX_BUILDING_LIBCXXABI -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION=2 -D_LIBCPP_BUILDING_LIBRARY -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCXXABI_BUILDING_LIBRARY -D_LIBCXXABI_LINK_PTHREAD_LIB -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/home/gha/actions-runner/_work/llvm-project/llvm-project/libunwind/include -I/home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/../libcxx/src -I/home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/include -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/x86_64-unknown-linux-gnu/c++/v1 -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1 -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wno-pass-failed -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Xclang -fno-pch-timestamp  -O3 -DNDEBUG -std=c++23 -fPIC -nostdinc++ -fstrict-aliasing -funwind-tables -D_DEBUG -UNDEBUG -UNDEBUG -Wall -Wextra -Wnewline-eof -Wshadow -Wwrite-strings -Wno-unused-parameter -Wno-long-long -Werror=return-type -Wextra-semi -Wundef -Wunused-template -Wformat-nonliteral -Wzero-length-array -Wdeprecated-redundant-constexpr-static-def -Wno-nullability-completeness -Wmissing-prototypes -Wno-user-defined-literals -Wno-covered-switch-default -Wno-suggest-override -Wno-error -fsized-deallocation -fvisibility=hidden -fdebug-prefix-map=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1=/home/gha/actions-runner/_work/llvm-project/llvm-project/libcxx/include -MD -MT libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_demangle.cpp.o -MF libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_demangle.cpp.o.d -o libcxxabi/src/CMakeFiles/cxxabi_shared_objects.dir/cxa_demangle.cpp.o -c /home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/src/cxa_demangle.cpp
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/src/cxa_demangle.cpp:17:
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/src/demangle/ItaniumDemangle.h:20:
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/src/demangle/StringViewExtras.h:20:
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1/string_view:235:
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1/__string/char_traits.h:29:
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1/cstdio:104:
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1/stdio.h:110:2: error: #endif without #if
110 | #endif
|  ^
1 error generated.
```
</details>
<details>
<summary>libcxxabi/src/CMakeFiles/cxxabi_static_objects.dir/cxa_demangle.cpp.o</summary>

```
FAILED: libcxxabi/src/CMakeFiles/cxxabi_static_objects.dir/cxa_demangle.cpp.o
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/./bin/clang++ --target=x86_64-unknown-linux-gnu -DHAVE___CXA_THREAD_ATEXIT_IMPL -DLIBCXX_BUILDING_LIBCXXABI -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION=2 -D_LIBCPP_BUILDING_LIBRARY -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCXXABI_BUILDING_LIBRARY -D_LIBCXXABI_LINK_PTHREAD_LIB -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/home/gha/actions-runner/_work/llvm-project/llvm-project/libunwind/include -I/home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/../libcxx/src -I/home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/include -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/x86_64-unknown-linux-gnu/c++/v1 -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1 -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wno-pass-failed -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Xclang -fno-pch-timestamp  -O3 -DNDEBUG -std=c++23 -nostdinc++ -fstrict-aliasing -funwind-tables -D_DEBUG -UNDEBUG -UNDEBUG -Wall -Wextra -Wnewline-eof -Wshadow -Wwrite-strings -Wno-unused-parameter -Wno-long-long -Werror=return-type -Wextra-semi -Wundef -Wunused-template -Wformat-nonliteral -Wzero-length-array -Wdeprecated-redundant-constexpr-static-def -Wno-nullability-completeness -Wmissing-prototypes -Wno-user-defined-literals -Wno-covered-switch-default -Wno-suggest-override -Wno-error -fsized-deallocation -fvisibility=hidden -fdebug-prefix-map=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1=/home/gha/actions-runner/_work/llvm-project/llvm-project/libcxx/include -MD -MT libcxxabi/src/CMakeFiles/cxxabi_static_objects.dir/cxa_demangle.cpp.o -MF libcxxabi/src/CMakeFiles/cxxabi_static_objects.dir/cxa_demangle.cpp.o.d -o libcxxabi/src/CMakeFiles/cxxabi_static_objects.dir/cxa_demangle.cpp.o -c /home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/src/cxa_demangle.cpp
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/src/cxa_demangle.cpp:17:
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/src/demangle/ItaniumDemangle.h:20:
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/libcxxabi/src/demangle/StringViewExtras.h:20:
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1/string_view:235:
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1/__string/char_traits.h:29:
In file included from /home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1/cstdio:104:
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1/stdio.h:110:2: error: #endif without #if
110 | #endif
|  ^
1 error generated.
```
</details>

If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the `infrastructure` label.

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sat Jul 11 03:38:06 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 03:38:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] avoid include Uppercase windows
	headers (PR #208903)
Message-ID: <llvm.org/llvm/llvm-project/pull/208903@github.com>


https://github.com/trcrsired created https://github.com/llvm/llvm-project/pull/208903

We should use unknwn.h and windows.h instead of Unknwn.h and Windows.h because Linux and other non‑Windows systems treat filenames as case‑sensitive. Windows does not, so mixed‑case includes work there but break elsewhere. Both mingw‑w64‑crt and windows‑msvc‑sysroot provide all Windows headers in fully lowercase, so using the lowercase forms ensures consistent cross‑platform builds.

#208901

>From 29e0da2e4cd1045d597bfbee91ca4812fdb74b32 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 18:34:47 +0800
Subject: [PATCH] [libcxx] avoid include Uppercase windows headers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

We should use unknwn.h and windows.h instead of Unknwn.h and Windows.h because Linux and other non‑Windows systems treat filenames as case‑sensitive. Windows does not, so mixed‑case includes work there but break elsewhere. Both mingw‑w64‑crt and windows‑msvc‑sysroot provide all Windows headers in fully lowercase, so using the lowercase forms ensures consistent cross‑platform builds.
---
 libcxx/src/support/runtime/exception_pointer_msvc.ipp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/src/support/runtime/exception_pointer_msvc.ipp b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
index 72360a2d791bd..3102d1ee93db2 100644
--- a/libcxx/src/support/runtime/exception_pointer_msvc.ipp
+++ b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
@@ -14,8 +14,8 @@ _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmultichar")
 #include <cstdlib>
 #include <cstring>
 
-#include <Unknwn.h>
-#include <Windows.h>
+#include <unknwn.h>
+#include <windows.h>
 struct _ThrowInfo;
 #include <eh.h>
 #include <ehdata.h>


From libcxx-commits at lists.llvm.org  Sat Jul 11 03:38:46 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 03:38:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] avoid include Uppercase windows
	headers (PR #208903)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208903@github.com>
Message-ID: <6a521d36.bb839515.27d21e.ed69@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: cqwrteur (trcrsired)

<details>
<summary>Changes</summary>

We should use unknwn.h and windows.h instead of Unknwn.h and Windows.h because Linux and other non‑Windows systems treat filenames as case‑sensitive. Windows does not, so mixed‑case includes work there but break elsewhere. Both mingw‑w64‑crt and windows‑msvc‑sysroot provide all Windows headers in fully lowercase, so using the lowercase forms ensures consistent cross‑platform builds.

#<!-- -->208901

---
Full diff: https://github.com/llvm/llvm-project/pull/208903.diff


1 Files Affected:

- (modified) libcxx/src/support/runtime/exception_pointer_msvc.ipp (+2-2) 


``````````diff
diff --git a/libcxx/src/support/runtime/exception_pointer_msvc.ipp b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
index 72360a2d791bd..3102d1ee93db2 100644
--- a/libcxx/src/support/runtime/exception_pointer_msvc.ipp
+++ b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
@@ -14,8 +14,8 @@ _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmultichar")
 #include <cstdlib>
 #include <cstring>
 
-#include <Unknwn.h>
-#include <Windows.h>
+#include <unknwn.h>
+#include <windows.h>
 struct _ThrowInfo;
 #include <eh.h>
 #include <ehdata.h>

``````````

</details>


https://github.com/llvm/llvm-project/pull/208903

From libcxx-commits at lists.llvm.org  Sat Jul 11 04:11:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 04:11:37 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [libcxx]support freestanding
	libc++ (PR #208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a5224e9.13be6df8.145c.45cf@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 6cabf2c78b79590476555c0066c20083c2c36c52 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH 1/2] [libcxx] Support freestanding libc++

---
 clang/lib/Headers/errno.h                     | 164 ++++++++++++++++++
 clang/lib/Headers/math.h                      |  29 ++++
 clang/lib/Headers/stdlib.h                    | 103 +++++++++++
 clang/lib/Headers/string.h                    |  65 +++++++
 clang/lib/Headers/wchar.h                     |  62 +++++++
 libcxx/CMakeLists.txt                         |  54 ++++--
 libcxx/include/__algorithm/sort.h             |   4 +
 libcxx/include/__config                       |   4 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/ctime                          |   7 +-
 libcxx/include/stdio.h                        |   5 +
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 114 ++++++++++++
 12 files changed, 600 insertions(+), 14 deletions(-)
 create mode 100644 clang/lib/Headers/errno.h
 create mode 100644 clang/lib/Headers/math.h
 create mode 100644 clang/lib/Headers/stdlib.h
 create mode 100644 clang/lib/Headers/string.h
 create mode 100644 clang/lib/Headers/wchar.h
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/clang/lib/Headers/errno.h b/clang/lib/Headers/errno.h
new file mode 100644
index 0000000000000..48fe84a9304ce
--- /dev/null
+++ b/clang/lib/Headers/errno.h
@@ -0,0 +1,164 @@
+/*===---- errno.h - Standard header for error numbers ---------------------===*\
+ *
+ * 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 __CLANG_ERRNO_H
+#define __CLANG_ERRNO_H
+
+/* If we're hosted, fall back to the system's errno.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<errno.h>)
+
+#include_next <errno.h>
+
+#else
+
+/* Adjusted to the linux asm/errno.h */
+#define EPERM 1            /* Operation not permitted */
+#define ENOENT 2           /* No such file or directory */
+#define ESRCH 3            /* No such process */
+#define EINTR 4            /* Interrupted system call */
+#define EIO 5              /* I/O error */
+#define ENXIO 6            /* No such device or address */
+#define E2BIG 7            /* Arg list too long */
+#define ENOEXEC 8          /* Exec format error */
+#define EBADF 9            /* Bad file number */
+#define ECHILD 10          /* No child processes */
+#define EAGAIN 11          /* Try again */
+#define ENOMEM 12          /* Out of memory */
+#define EACCES 13          /* Permission denied */
+#define EFAULT 14          /* Bad address */
+#define ENOTBLK 15         /* Block device required */
+#define EBUSY 16           /* Device or resource busy */
+#define EEXIST 17          /* File exists */
+#define EXDEV 18           /* Cross-device link */
+#define ENODEV 19          /* No such device */
+#define ENOTDIR 20         /* Not a directory */
+#define EISDIR 21          /* Is a directory */
+#define EINVAL 22          /* Invalid argument */
+#define ENFILE 23          /* File table overflow */
+#define EMFILE 24          /* Too many open files */
+#define ENOTTY 25          /* Not a typewriter */
+#define ETXTBSY 26         /* Text file busy */
+#define EFBIG 27           /* File too large */
+#define ENOSPC 28          /* No space left on device */
+#define ESPIPE 29          /* Illegal seek */
+#define EROFS 30           /* Read-only file system */
+#define EMLINK 31          /* Too many links */
+#define EPIPE 32           /* Broken pipe */
+#define EDOM 33            /* Math argument out of domain of func */
+#define ERANGE 34          /* Math result not representable */
+#define EDEADLK 35         /* Resource deadlock would occur */
+#define ENAMETOOLONG 36    /* File name too long */
+#define ENOLCK 37          /* No record locks available */
+#define ENOSYS 38          /* Function not implemented */
+#define ENOTEMPTY 39       /* Directory not empty */
+#define ELOOP 40           /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42          /* No message of desired type */
+#define EIDRM 43           /* Identifier removed */
+#define ECHRNG 44          /* Channel number out of range */
+#define EL2NSYNC 45        /* Level 2 not synchronized */
+#define EL3HLT 46          /* Level 3 halted */
+#define EL3RST 47          /* Level 3 reset */
+#define ELNRNG 48          /* Link number out of range */
+#define EUNATCH 49         /* Protocol driver not attached */
+#define ENOCSI 50          /* No CSI structure available */
+#define EL2HLT 51          /* Level 2 halted */
+#define EBADE 52           /* Invalid exchange */
+#define EBADR 53           /* Invalid request descriptor */
+#define EXFULL 54          /* Exchange full */
+#define ENOANO 55          /* No anode */
+#define EBADRQC 56         /* Invalid request code */
+#define EBADSLT 57         /* Invalid slot */
+#define EDEADLOCK EDEADLK
+#define EBFONT 59          /* Bad font file format */
+#define EFTYPE EBFONT      /* Inappropriate file type or format */
+#define ENOSTR 60          /* Device not a stream */
+#define ENODATA 61         /* No data available */
+#define ETIME 62           /* Timer expired */
+#define ENOSR 63           /* Out of streams resources */
+#define ENONET 64          /* Machine is not on the network */
+#define ENOPKG 65          /* Package not installed */
+#define EREMOTE 66         /* Object is remote */
+#define ENOLINK 67         /* Link has been severed */
+#define EADV 68            /* Advertise error */
+#define ESRMNT 69          /* Srmount error */
+#define ECOMM 70           /* Communication error on send */
+#define EPROTO 71          /* Protocol error */
+#define EMULTIHOP 72       /* Multihop attempted */
+#define EDOTDOT 73         /* RFS specific error */
+#define EBADMSG 74         /* Not a data message */
+#define EOVERFLOW 75       /* Value too large for defined data type */
+#define ENOTUNIQ 76        /* Name not unique on network */
+#define EBADFD 77          /* File descriptor in bad state */
+#define EREMCHG 78         /* Remote address changed */
+#define ELIBACC 79         /* Can not access a needed shared library */
+#define ELIBBAD 80         /* Accessing a corrupted shared library */
+#define ELIBSCN 81         /* .lib section in a.out corrupted */
+#define ELIBMAX 82         /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83        /* Cannot exec a shared library directly */
+#define EILSEQ 84          /* Illegal byte sequence */
+#define ERESTART 85        /* Interrupted system call should be restarted */
+#define ESTRPIPE 86        /* Streams pipe error */
+#define EUSERS 87          /* Too many users */
+#define ENOTSOCK 88        /* Socket operation on non-socket */
+#define EDESTADDRREQ 89    /* Destination address required */
+#define EMSGSIZE 90        /* Message too long */
+#define EPROTOTYPE 91      /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92     /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95      /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96    /* Protocol family not supported */
+#define EAFNOSUPPORT 97    /* Address family not supported by protocol */
+#define EADDRINUSE 98      /* Address already in use */
+#define EADDRNOTAVAIL 99   /* Cannot assign requested address */
+#define ENETDOWN 100       /* Network is down */
+#define ENETUNREACH 101    /* Network is unreachable */
+#define ENETRESET 102      /* Network dropped connection because of reset */
+#define ECONNABORTED 103   /* Software caused connection abort */
+#define ECONNRESET 104     /* Connection reset by peer */
+#define ENOBUFS 105        /* No buffer space available */
+#define EISCONN 106        /* Transport endpoint is already connected */
+#define ENOTCONN 107       /* Transport endpoint is not connected */
+#define ESHUTDOWN 108      /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109   /* Too many references: cannot splice */
+#define ETIMEDOUT 110      /* Connection timed out */
+#define ECONNREFUSED 111   /* Connection refused */
+#define EHOSTDOWN 112      /* Host is down */
+#define EHOSTUNREACH 113   /* No route to host */
+#define EALREADY 114       /* Operation already in progress */
+#define EINPROGRESS 115    /* Operation now in progress */
+#define ESTALE 116         /* Stale NFS file handle */
+#define EUCLEAN 117        /* Structure needs cleaning */
+#define ENOTNAM 118        /* Not a XENIX named type file */
+#define ENAVAIL 119        /* No XENIX semaphores available */
+#define EISNAM 120         /* Is a named type file */
+#define EREMOTEIO 121      /* Remote I/O error */
+#define EDQUOT 122         /* Quota exceeded */
+#define ENOMEDIUM 123      /* No medium found */
+#define EMEDIUMTYPE 124    /* Wrong medium type */
+#define ECANCELED 125      /* Operation Canceled */
+#define ENOKEY 126         /* Required key not available */
+#define EKEYEXPIRED 127    /* Key has expired */
+#define EKEYREVOKED 128    /* Key has been revoked */
+#define EKEYREJECTED 129   /* Key was rejected by service */
+#define EOWNERDEAD 130     /* Owner died */
+#define ENOTRECOVERABLE 131 /* State not recoverable */
+#define ERFKILL 132         /* Operation not possible due to RF-kill */
+#define EHWPOISON 133       /* Memory page has hardware error */
+
+/* Widely known to be a synonym in Linux.  */
+#define ENOTSUP EOPNOTSUPP
+
+#define __ELASTERROR 2000 /* Users can add values starting here */
+
+#endif
+
+#endif
diff --git a/clang/lib/Headers/math.h b/clang/lib/Headers/math.h
new file mode 100644
index 0000000000000..3e316c891a1bc
--- /dev/null
+++ b/clang/lib/Headers/math.h
@@ -0,0 +1,29 @@
+/*===---- math.h - Standard header for C math ----------------------------===*\
+ *
+ * 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 __CLANG_MATH_H
+#define __CLANG_MATH_H
+
+/* If we're hosted, fall back to the system's math.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<math.h>)
+
+#include_next <math.h>
+
+#else
+
+#define NAN __builtin_nanf("")
+#define INFINITY __builtin_inff()
+#define FP_NAN 0
+#define FP_INFINITE 1
+#define FP_ZERO 2
+#define FP_SUBNORMAL 3
+#define FP_NORMAL 4
+
+#endif
diff --git a/clang/lib/Headers/stdlib.h b/clang/lib/Headers/stdlib.h
new file mode 100644
index 0000000000000..db5fa08385f58
--- /dev/null
+++ b/clang/lib/Headers/stdlib.h
@@ -0,0 +1,103 @@
+/*===---- stdlib.h - Standard header for general utilities ----------------===*\
+ *
+ * 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 __CLANG_STDLIB_H
+#define __CLANG_STDLIB_H
+
+/* If we're hosted, fall back to the system's math.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<stdlib.h>)
+
+#include_next <stdlib.h>
+
+#else
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1 /* freestanding */
+#endif
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0 /* freestanding */
+#endif
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+  int quot;
+  int rem;
+} div_t; /* freestanding */
+typedef struct {
+  long quot;
+  long rem;
+} ldiv_t; /* freestanding */
+typedef struct {
+  long long quot;
+  long long rem;
+} lldiv_t; /* freestanding */
+typedef struct {
+  intmax_t quot;
+  intmax_t rem;
+} imaxdiv_t; /* freestanding */
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+// start and termination
+
+#ifndef __CLANG_STDLIB_NOEXCEPT
+#ifdef __cplusplus
+#if __cplusplus >= 201103L
+#define __CLANG_STDLIB_NOEXCEPT noexcept
+#else
+#define __CLANG_STDLIB_NOEXCEPT throw()
+#endif
+#else
+#define __CLANG_STDLIB_NOEXCEPT
+#endif
+#endif
+
+[[noreturn]] void abort() __CLANG_STDLIB_NOEXCEPT;         /* freestanding */
+int atexit(void (*)()) __CLANG_STDLIB_NOEXCEPT;            /* freestanding */
+int at_quick_exit(void (*)()) __CLANG_STDLIB_NOEXCEPT;     /* freestanding */
+[[noreturn]] void exit(int);                               /* freestanding */
+[[noreturn]] void _Exit(int) __CLANG_STDLIB_NOEXCEPT;      /* freestanding */
+[[noreturn]] void quick_exit(int) __CLANG_STDLIB_NOEXCEPT; /* freestanding */
+
+#undef __CLANG_STDLIB_NOEXCEPT
+
+// C standard library algorithms
+void *bsearch(const void *, const void *, size_t, size_t,
+              int (*)(const void *, const void *)); /* freestanding */
+void qsort(void *, size_t, size_t,
+           int (*)(const void *, const void *)); /* freestanding */
+
+// absolute values
+int abs(int j);                       /* freestanding */
+long int abs(long int j);             /* freestanding */
+long long int abs(long long int j);   /* freestanding */
+long int labs(long int j);            /* freestanding */
+long long int llabs(long long int j); /* freestanding */
+
+div_t div(int, int);                         /* freestanding */
+ldiv_t div(long int, long int);              /* freestanding */
+lldiv_t div(long long int, long long int);   /* freestanding */
+ldiv_t ldiv(long int, long int);             /* freestanding */
+lldiv_t lldiv(long long int, long long int); /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
diff --git a/clang/lib/Headers/string.h b/clang/lib/Headers/string.h
new file mode 100644
index 0000000000000..c256b3e35a95b
--- /dev/null
+++ b/clang/lib/Headers/string.h
@@ -0,0 +1,65 @@
+/*===---- string.h - Standard header for null-terminated string -----------===*\
+ *
+ * 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 __CLANG_STRING_H
+#define __CLANG_STRING_H
+
+/* If we're hosted, fall back to the system's string.h, which might have
+ * additional definitions.
+ */
+
+#if __STDC_HOSTED__ && __has_include_next(<string.h>)
+
+#include_next <string.h>
+
+#else
+
+#include <stddef.h>
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void *memcpy(void *, const void *, size_t);      /* freestanding */
+void *memmove(void *, const void *, size_t);     /* freestanding */
+char *strcpy(char *, const char *);              /* freestanding */
+char *strncpy(char *, const char *, size_t);     /* freestanding */
+char *strcat(char *, const char *);              /* freestanding */
+char *strncat(char *, const char *, size_t);     /* freestanding */
+int memcmp(const void *, const void *, size_t);  /* freestanding */
+int strcmp(const char *, const char *);          /* freestanding */
+int strncmp(const char *, const char *, size_t); /* freestanding */
+const void *memchr(const void *, int, size_t);   /* freestanding */
+void *memchr(void *, int, size_t);               /* freestanding */
+const char *strchr(const char *, int);           /* freestanding */
+char *strchr(char *, int);                       /* freestanding */
+size_t strcspn(const char *, const char *);      /* freestanding */
+const char *strpbrk(const char *, const char *); /* freestanding */
+char *strpbrk(char *, const char *);             /* freestanding */
+const char *strrchr(const char *, int);          /* freestanding */
+char *strrchr(char *, int);                      /* freestanding */
+size_t strspn(const char *, const char *);       /* freestanding */
+const char *strstr(const char *, const char *);  /* freestanding */
+char *strstr(char *, const char *);              /* freestanding */
+char *strtok(char *, const char *);              /* freestanding */
+void *memset(void *, int, size_t);               /* freestanding */
+size_t strlen(const char *);                     /* freestanding */
+void *memset_explicit(void *, int, size_t);      /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/clang/lib/Headers/wchar.h b/clang/lib/Headers/wchar.h
new file mode 100644
index 0000000000000..ca232c0cd6356
--- /dev/null
+++ b/clang/lib/Headers/wchar.h
@@ -0,0 +1,62 @@
+/*===---- wchar.h - Standard header for wide character types --------------===*\
+ *
+ * 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 __CLANG_WCHAR_H
+#define __CLANG_WCHAR_H
+
+/* If we're hosted, fall back to the system's wchar.h, which might have
+ * additional definitions.
+ */
+
+#if __STDC_HOSTED__ && __has_include_next(<wchar.h>)
+
+#include_next <wchar.h>
+
+#else
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+wchar_t *wcscpy(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcsncpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t);    /* freestanding */
+wchar_t *wcscat(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcsncat(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+int wcscmp(const wchar_t *, const wchar_t *);             /* freestanding */
+int wcsncmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
+int wmemcmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
+const wchar_t *wcschr(const wchar_t *, wchar_t);          /* freestanding */
+wchar_t *wcschr(wchar_t *, wchar_t);                      /* freestanding */
+size_t wcscspn(const wchar_t *, const wchar_t *);         /* freestanding */
+const wchar_t *wcspbrk(const wchar_t *, const wchar_t *); /* freestanding */
+wchar_t *wcspbrk(wchar_t *, const wchar_t *);             /* freestanding */
+const wchar_t *wcsrchr(const wchar_t *, wchar_t);         /* freestanding */
+wchar_t *wcsrchr(wchar_t *, wchar_t);                     /* freestanding */
+size_t wcsspn(const wchar_t *, const wchar_t *);          /* freestanding */
+const wchar_t *wcsstr(const wchar_t *, const wchar_t *);  /* freestanding */
+wchar_t *wcsstr(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcstok(wchar_t *, const wchar_t *, wchar_t **);  /* freestanding */
+const wchar_t *wmemchr(const wchar_t *, wchar_t, size_t); /* freestanding */
+wchar_t *wmemchr(wchar_t *, wchar_t, size_t);             /* freestanding */
+size_t wcslen(const wchar_t *);                           /* freestanding */
+wchar_t *wmemset(wchar_t *, wchar_t, size_t);             /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..b92ff4b07777a 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,43 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +923,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +946,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..f144115ded7e4 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,8 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
+
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -859,6 +861,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
+#endif
+
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 __sort_dispatch(_RandomAccessIterator __first, _RandomAccessIterator __last, _Comp& __comp) {
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..cbfb448189360 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..6140faed9550f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -52,11 +52,16 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #  include <__cstddef/size_t.h>
 
 // <time.h> is not provided by libc++
-#  if __has_include(<time.h>)
+#  if __STDC_HOSTED__ && __has_include(<time.h>)
 #    include <time.h>
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..e42485542d347 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,11 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
+#endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..b76e95089190e
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only %s
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding -DFREESTANDING %s
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(FREESTANDING) != defined(_LIBCPP_FREESTANDING)
+#  error _LIBCPP_FREESTANDING should be defined in freestanding mode and not \
+       defined in non-freestanding mode
+#  if defined(__has_feature)
+#    if __has_feature(modules)
+#      define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#    endif
+#  elif defined(__cpp_modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+
+#  if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#    include <cstddef>
+#    include <limits>
+#    include <cfloat>
+#    include <version>
+#    include <cstdint>
+#    include <cstdlib>
+#    include <new>
+#    include <typeinfo>
+#    if __has_include(<source_location>)
+#      include <source_location>
+#    endif
+#    include <exception>
+#    include <initializer_list>
+#    include <compare>
+#    include <coroutine>
+#    include <cstdarg>
+#    include <concepts>
+#    include <type_traits>
+#    include <bit>
+#    include <atomic>
+#    include <utility>
+#    include <tuple>
+#    include <memory>
+#    include <functional>
+#    include <ratio>
+#    include <iterator>
+#    include <ranges>
+#    include <typeinfo>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#    include <iosfwd>
+#    include <cstdio>
+#    include <cerrno>
+#    include <cstring>
+#    include <cmath>
+#    ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#      include <cwchar>
+#    endif
+#    include <array>
+#    include <span>
+#    include <algorithm>
+#    include <bitset>
+#    include <variant>
+#    include <optional>
+#    include <numbers>
+#    include <scoped_allocator>
+#    include <typeindex>
+#    include <string_view>
+#    include <numeric>
+#    include <complex>
+#    include <chrono>
+#    include <charconv>
+#    include <expected>
+#    include <random>
+#    include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#    include <vector>
+#    include <deque>
+#    include <string>
+#    include <stack>
+#    include <queue>
+#    include <list>
+#    include <forward_list>
+#    include <map>
+#    include <set>
+#    include <unordered_set>
+#    include <unordered_map>
+#    if __has_include(<mdspan>)
+#      include <mdspan>
+#    endif
+#    include <valarray>
+
+#    if _LIBCPP_STD_VER < 20
+#      include <ciso646>
+#      if __has_include(<cstdalign>)
+#        include <cstdalign>
+#      endif
+#      include <cstdbool>
+#    endif
+#  endif

>From 259f4c59ffcdd906df3383d5abd199ad54646020 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 19:10:08 +0800
Subject: [PATCH 2/2] [skip ci][libcxx] freestanding has no threads

---
 libcxx/include/__config | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index cbfb448189360..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -234,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API


From libcxx-commits at lists.llvm.org  Sat Jul 11 04:20:22 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 04:20:22 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [libcxx]support freestanding
	libc++ (PR #208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a5226f6.30759430.24d50c.8059@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 6cabf2c78b79590476555c0066c20083c2c36c52 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH 1/3] [libcxx] Support freestanding libc++

---
 clang/lib/Headers/errno.h                     | 164 ++++++++++++++++++
 clang/lib/Headers/math.h                      |  29 ++++
 clang/lib/Headers/stdlib.h                    | 103 +++++++++++
 clang/lib/Headers/string.h                    |  65 +++++++
 clang/lib/Headers/wchar.h                     |  62 +++++++
 libcxx/CMakeLists.txt                         |  54 ++++--
 libcxx/include/__algorithm/sort.h             |   4 +
 libcxx/include/__config                       |   4 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/ctime                          |   7 +-
 libcxx/include/stdio.h                        |   5 +
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 114 ++++++++++++
 12 files changed, 600 insertions(+), 14 deletions(-)
 create mode 100644 clang/lib/Headers/errno.h
 create mode 100644 clang/lib/Headers/math.h
 create mode 100644 clang/lib/Headers/stdlib.h
 create mode 100644 clang/lib/Headers/string.h
 create mode 100644 clang/lib/Headers/wchar.h
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/clang/lib/Headers/errno.h b/clang/lib/Headers/errno.h
new file mode 100644
index 0000000000000..48fe84a9304ce
--- /dev/null
+++ b/clang/lib/Headers/errno.h
@@ -0,0 +1,164 @@
+/*===---- errno.h - Standard header for error numbers ---------------------===*\
+ *
+ * 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 __CLANG_ERRNO_H
+#define __CLANG_ERRNO_H
+
+/* If we're hosted, fall back to the system's errno.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<errno.h>)
+
+#include_next <errno.h>
+
+#else
+
+/* Adjusted to the linux asm/errno.h */
+#define EPERM 1            /* Operation not permitted */
+#define ENOENT 2           /* No such file or directory */
+#define ESRCH 3            /* No such process */
+#define EINTR 4            /* Interrupted system call */
+#define EIO 5              /* I/O error */
+#define ENXIO 6            /* No such device or address */
+#define E2BIG 7            /* Arg list too long */
+#define ENOEXEC 8          /* Exec format error */
+#define EBADF 9            /* Bad file number */
+#define ECHILD 10          /* No child processes */
+#define EAGAIN 11          /* Try again */
+#define ENOMEM 12          /* Out of memory */
+#define EACCES 13          /* Permission denied */
+#define EFAULT 14          /* Bad address */
+#define ENOTBLK 15         /* Block device required */
+#define EBUSY 16           /* Device or resource busy */
+#define EEXIST 17          /* File exists */
+#define EXDEV 18           /* Cross-device link */
+#define ENODEV 19          /* No such device */
+#define ENOTDIR 20         /* Not a directory */
+#define EISDIR 21          /* Is a directory */
+#define EINVAL 22          /* Invalid argument */
+#define ENFILE 23          /* File table overflow */
+#define EMFILE 24          /* Too many open files */
+#define ENOTTY 25          /* Not a typewriter */
+#define ETXTBSY 26         /* Text file busy */
+#define EFBIG 27           /* File too large */
+#define ENOSPC 28          /* No space left on device */
+#define ESPIPE 29          /* Illegal seek */
+#define EROFS 30           /* Read-only file system */
+#define EMLINK 31          /* Too many links */
+#define EPIPE 32           /* Broken pipe */
+#define EDOM 33            /* Math argument out of domain of func */
+#define ERANGE 34          /* Math result not representable */
+#define EDEADLK 35         /* Resource deadlock would occur */
+#define ENAMETOOLONG 36    /* File name too long */
+#define ENOLCK 37          /* No record locks available */
+#define ENOSYS 38          /* Function not implemented */
+#define ENOTEMPTY 39       /* Directory not empty */
+#define ELOOP 40           /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42          /* No message of desired type */
+#define EIDRM 43           /* Identifier removed */
+#define ECHRNG 44          /* Channel number out of range */
+#define EL2NSYNC 45        /* Level 2 not synchronized */
+#define EL3HLT 46          /* Level 3 halted */
+#define EL3RST 47          /* Level 3 reset */
+#define ELNRNG 48          /* Link number out of range */
+#define EUNATCH 49         /* Protocol driver not attached */
+#define ENOCSI 50          /* No CSI structure available */
+#define EL2HLT 51          /* Level 2 halted */
+#define EBADE 52           /* Invalid exchange */
+#define EBADR 53           /* Invalid request descriptor */
+#define EXFULL 54          /* Exchange full */
+#define ENOANO 55          /* No anode */
+#define EBADRQC 56         /* Invalid request code */
+#define EBADSLT 57         /* Invalid slot */
+#define EDEADLOCK EDEADLK
+#define EBFONT 59          /* Bad font file format */
+#define EFTYPE EBFONT      /* Inappropriate file type or format */
+#define ENOSTR 60          /* Device not a stream */
+#define ENODATA 61         /* No data available */
+#define ETIME 62           /* Timer expired */
+#define ENOSR 63           /* Out of streams resources */
+#define ENONET 64          /* Machine is not on the network */
+#define ENOPKG 65          /* Package not installed */
+#define EREMOTE 66         /* Object is remote */
+#define ENOLINK 67         /* Link has been severed */
+#define EADV 68            /* Advertise error */
+#define ESRMNT 69          /* Srmount error */
+#define ECOMM 70           /* Communication error on send */
+#define EPROTO 71          /* Protocol error */
+#define EMULTIHOP 72       /* Multihop attempted */
+#define EDOTDOT 73         /* RFS specific error */
+#define EBADMSG 74         /* Not a data message */
+#define EOVERFLOW 75       /* Value too large for defined data type */
+#define ENOTUNIQ 76        /* Name not unique on network */
+#define EBADFD 77          /* File descriptor in bad state */
+#define EREMCHG 78         /* Remote address changed */
+#define ELIBACC 79         /* Can not access a needed shared library */
+#define ELIBBAD 80         /* Accessing a corrupted shared library */
+#define ELIBSCN 81         /* .lib section in a.out corrupted */
+#define ELIBMAX 82         /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83        /* Cannot exec a shared library directly */
+#define EILSEQ 84          /* Illegal byte sequence */
+#define ERESTART 85        /* Interrupted system call should be restarted */
+#define ESTRPIPE 86        /* Streams pipe error */
+#define EUSERS 87          /* Too many users */
+#define ENOTSOCK 88        /* Socket operation on non-socket */
+#define EDESTADDRREQ 89    /* Destination address required */
+#define EMSGSIZE 90        /* Message too long */
+#define EPROTOTYPE 91      /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92     /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95      /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96    /* Protocol family not supported */
+#define EAFNOSUPPORT 97    /* Address family not supported by protocol */
+#define EADDRINUSE 98      /* Address already in use */
+#define EADDRNOTAVAIL 99   /* Cannot assign requested address */
+#define ENETDOWN 100       /* Network is down */
+#define ENETUNREACH 101    /* Network is unreachable */
+#define ENETRESET 102      /* Network dropped connection because of reset */
+#define ECONNABORTED 103   /* Software caused connection abort */
+#define ECONNRESET 104     /* Connection reset by peer */
+#define ENOBUFS 105        /* No buffer space available */
+#define EISCONN 106        /* Transport endpoint is already connected */
+#define ENOTCONN 107       /* Transport endpoint is not connected */
+#define ESHUTDOWN 108      /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109   /* Too many references: cannot splice */
+#define ETIMEDOUT 110      /* Connection timed out */
+#define ECONNREFUSED 111   /* Connection refused */
+#define EHOSTDOWN 112      /* Host is down */
+#define EHOSTUNREACH 113   /* No route to host */
+#define EALREADY 114       /* Operation already in progress */
+#define EINPROGRESS 115    /* Operation now in progress */
+#define ESTALE 116         /* Stale NFS file handle */
+#define EUCLEAN 117        /* Structure needs cleaning */
+#define ENOTNAM 118        /* Not a XENIX named type file */
+#define ENAVAIL 119        /* No XENIX semaphores available */
+#define EISNAM 120         /* Is a named type file */
+#define EREMOTEIO 121      /* Remote I/O error */
+#define EDQUOT 122         /* Quota exceeded */
+#define ENOMEDIUM 123      /* No medium found */
+#define EMEDIUMTYPE 124    /* Wrong medium type */
+#define ECANCELED 125      /* Operation Canceled */
+#define ENOKEY 126         /* Required key not available */
+#define EKEYEXPIRED 127    /* Key has expired */
+#define EKEYREVOKED 128    /* Key has been revoked */
+#define EKEYREJECTED 129   /* Key was rejected by service */
+#define EOWNERDEAD 130     /* Owner died */
+#define ENOTRECOVERABLE 131 /* State not recoverable */
+#define ERFKILL 132         /* Operation not possible due to RF-kill */
+#define EHWPOISON 133       /* Memory page has hardware error */
+
+/* Widely known to be a synonym in Linux.  */
+#define ENOTSUP EOPNOTSUPP
+
+#define __ELASTERROR 2000 /* Users can add values starting here */
+
+#endif
+
+#endif
diff --git a/clang/lib/Headers/math.h b/clang/lib/Headers/math.h
new file mode 100644
index 0000000000000..3e316c891a1bc
--- /dev/null
+++ b/clang/lib/Headers/math.h
@@ -0,0 +1,29 @@
+/*===---- math.h - Standard header for C math ----------------------------===*\
+ *
+ * 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 __CLANG_MATH_H
+#define __CLANG_MATH_H
+
+/* If we're hosted, fall back to the system's math.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<math.h>)
+
+#include_next <math.h>
+
+#else
+
+#define NAN __builtin_nanf("")
+#define INFINITY __builtin_inff()
+#define FP_NAN 0
+#define FP_INFINITE 1
+#define FP_ZERO 2
+#define FP_SUBNORMAL 3
+#define FP_NORMAL 4
+
+#endif
diff --git a/clang/lib/Headers/stdlib.h b/clang/lib/Headers/stdlib.h
new file mode 100644
index 0000000000000..db5fa08385f58
--- /dev/null
+++ b/clang/lib/Headers/stdlib.h
@@ -0,0 +1,103 @@
+/*===---- stdlib.h - Standard header for general utilities ----------------===*\
+ *
+ * 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 __CLANG_STDLIB_H
+#define __CLANG_STDLIB_H
+
+/* If we're hosted, fall back to the system's math.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<stdlib.h>)
+
+#include_next <stdlib.h>
+
+#else
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1 /* freestanding */
+#endif
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0 /* freestanding */
+#endif
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+  int quot;
+  int rem;
+} div_t; /* freestanding */
+typedef struct {
+  long quot;
+  long rem;
+} ldiv_t; /* freestanding */
+typedef struct {
+  long long quot;
+  long long rem;
+} lldiv_t; /* freestanding */
+typedef struct {
+  intmax_t quot;
+  intmax_t rem;
+} imaxdiv_t; /* freestanding */
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+// start and termination
+
+#ifndef __CLANG_STDLIB_NOEXCEPT
+#ifdef __cplusplus
+#if __cplusplus >= 201103L
+#define __CLANG_STDLIB_NOEXCEPT noexcept
+#else
+#define __CLANG_STDLIB_NOEXCEPT throw()
+#endif
+#else
+#define __CLANG_STDLIB_NOEXCEPT
+#endif
+#endif
+
+[[noreturn]] void abort() __CLANG_STDLIB_NOEXCEPT;         /* freestanding */
+int atexit(void (*)()) __CLANG_STDLIB_NOEXCEPT;            /* freestanding */
+int at_quick_exit(void (*)()) __CLANG_STDLIB_NOEXCEPT;     /* freestanding */
+[[noreturn]] void exit(int);                               /* freestanding */
+[[noreturn]] void _Exit(int) __CLANG_STDLIB_NOEXCEPT;      /* freestanding */
+[[noreturn]] void quick_exit(int) __CLANG_STDLIB_NOEXCEPT; /* freestanding */
+
+#undef __CLANG_STDLIB_NOEXCEPT
+
+// C standard library algorithms
+void *bsearch(const void *, const void *, size_t, size_t,
+              int (*)(const void *, const void *)); /* freestanding */
+void qsort(void *, size_t, size_t,
+           int (*)(const void *, const void *)); /* freestanding */
+
+// absolute values
+int abs(int j);                       /* freestanding */
+long int abs(long int j);             /* freestanding */
+long long int abs(long long int j);   /* freestanding */
+long int labs(long int j);            /* freestanding */
+long long int llabs(long long int j); /* freestanding */
+
+div_t div(int, int);                         /* freestanding */
+ldiv_t div(long int, long int);              /* freestanding */
+lldiv_t div(long long int, long long int);   /* freestanding */
+ldiv_t ldiv(long int, long int);             /* freestanding */
+lldiv_t lldiv(long long int, long long int); /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
diff --git a/clang/lib/Headers/string.h b/clang/lib/Headers/string.h
new file mode 100644
index 0000000000000..c256b3e35a95b
--- /dev/null
+++ b/clang/lib/Headers/string.h
@@ -0,0 +1,65 @@
+/*===---- string.h - Standard header for null-terminated string -----------===*\
+ *
+ * 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 __CLANG_STRING_H
+#define __CLANG_STRING_H
+
+/* If we're hosted, fall back to the system's string.h, which might have
+ * additional definitions.
+ */
+
+#if __STDC_HOSTED__ && __has_include_next(<string.h>)
+
+#include_next <string.h>
+
+#else
+
+#include <stddef.h>
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void *memcpy(void *, const void *, size_t);      /* freestanding */
+void *memmove(void *, const void *, size_t);     /* freestanding */
+char *strcpy(char *, const char *);              /* freestanding */
+char *strncpy(char *, const char *, size_t);     /* freestanding */
+char *strcat(char *, const char *);              /* freestanding */
+char *strncat(char *, const char *, size_t);     /* freestanding */
+int memcmp(const void *, const void *, size_t);  /* freestanding */
+int strcmp(const char *, const char *);          /* freestanding */
+int strncmp(const char *, const char *, size_t); /* freestanding */
+const void *memchr(const void *, int, size_t);   /* freestanding */
+void *memchr(void *, int, size_t);               /* freestanding */
+const char *strchr(const char *, int);           /* freestanding */
+char *strchr(char *, int);                       /* freestanding */
+size_t strcspn(const char *, const char *);      /* freestanding */
+const char *strpbrk(const char *, const char *); /* freestanding */
+char *strpbrk(char *, const char *);             /* freestanding */
+const char *strrchr(const char *, int);          /* freestanding */
+char *strrchr(char *, int);                      /* freestanding */
+size_t strspn(const char *, const char *);       /* freestanding */
+const char *strstr(const char *, const char *);  /* freestanding */
+char *strstr(char *, const char *);              /* freestanding */
+char *strtok(char *, const char *);              /* freestanding */
+void *memset(void *, int, size_t);               /* freestanding */
+size_t strlen(const char *);                     /* freestanding */
+void *memset_explicit(void *, int, size_t);      /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/clang/lib/Headers/wchar.h b/clang/lib/Headers/wchar.h
new file mode 100644
index 0000000000000..ca232c0cd6356
--- /dev/null
+++ b/clang/lib/Headers/wchar.h
@@ -0,0 +1,62 @@
+/*===---- wchar.h - Standard header for wide character types --------------===*\
+ *
+ * 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 __CLANG_WCHAR_H
+#define __CLANG_WCHAR_H
+
+/* If we're hosted, fall back to the system's wchar.h, which might have
+ * additional definitions.
+ */
+
+#if __STDC_HOSTED__ && __has_include_next(<wchar.h>)
+
+#include_next <wchar.h>
+
+#else
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+wchar_t *wcscpy(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcsncpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t);    /* freestanding */
+wchar_t *wcscat(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcsncat(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+int wcscmp(const wchar_t *, const wchar_t *);             /* freestanding */
+int wcsncmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
+int wmemcmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
+const wchar_t *wcschr(const wchar_t *, wchar_t);          /* freestanding */
+wchar_t *wcschr(wchar_t *, wchar_t);                      /* freestanding */
+size_t wcscspn(const wchar_t *, const wchar_t *);         /* freestanding */
+const wchar_t *wcspbrk(const wchar_t *, const wchar_t *); /* freestanding */
+wchar_t *wcspbrk(wchar_t *, const wchar_t *);             /* freestanding */
+const wchar_t *wcsrchr(const wchar_t *, wchar_t);         /* freestanding */
+wchar_t *wcsrchr(wchar_t *, wchar_t);                     /* freestanding */
+size_t wcsspn(const wchar_t *, const wchar_t *);          /* freestanding */
+const wchar_t *wcsstr(const wchar_t *, const wchar_t *);  /* freestanding */
+wchar_t *wcsstr(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcstok(wchar_t *, const wchar_t *, wchar_t **);  /* freestanding */
+const wchar_t *wmemchr(const wchar_t *, wchar_t, size_t); /* freestanding */
+wchar_t *wmemchr(wchar_t *, wchar_t, size_t);             /* freestanding */
+size_t wcslen(const wchar_t *);                           /* freestanding */
+wchar_t *wmemset(wchar_t *, wchar_t, size_t);             /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..b92ff4b07777a 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,43 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +923,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +946,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..f144115ded7e4 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,8 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
+
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -859,6 +861,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
+#endif
+
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 __sort_dispatch(_RandomAccessIterator __first, _RandomAccessIterator __last, _Comp& __comp) {
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..cbfb448189360 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..6140faed9550f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -52,11 +52,16 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #  include <__cstddef/size_t.h>
 
 // <time.h> is not provided by libc++
-#  if __has_include(<time.h>)
+#  if __STDC_HOSTED__ && __has_include(<time.h>)
 #    include <time.h>
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..e42485542d347 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,11 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
+#endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..b76e95089190e
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only %s
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding -DFREESTANDING %s
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(FREESTANDING) != defined(_LIBCPP_FREESTANDING)
+#  error _LIBCPP_FREESTANDING should be defined in freestanding mode and not \
+       defined in non-freestanding mode
+#  if defined(__has_feature)
+#    if __has_feature(modules)
+#      define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#    endif
+#  elif defined(__cpp_modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+
+#  if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#    include <cstddef>
+#    include <limits>
+#    include <cfloat>
+#    include <version>
+#    include <cstdint>
+#    include <cstdlib>
+#    include <new>
+#    include <typeinfo>
+#    if __has_include(<source_location>)
+#      include <source_location>
+#    endif
+#    include <exception>
+#    include <initializer_list>
+#    include <compare>
+#    include <coroutine>
+#    include <cstdarg>
+#    include <concepts>
+#    include <type_traits>
+#    include <bit>
+#    include <atomic>
+#    include <utility>
+#    include <tuple>
+#    include <memory>
+#    include <functional>
+#    include <ratio>
+#    include <iterator>
+#    include <ranges>
+#    include <typeinfo>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#    include <iosfwd>
+#    include <cstdio>
+#    include <cerrno>
+#    include <cstring>
+#    include <cmath>
+#    ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#      include <cwchar>
+#    endif
+#    include <array>
+#    include <span>
+#    include <algorithm>
+#    include <bitset>
+#    include <variant>
+#    include <optional>
+#    include <numbers>
+#    include <scoped_allocator>
+#    include <typeindex>
+#    include <string_view>
+#    include <numeric>
+#    include <complex>
+#    include <chrono>
+#    include <charconv>
+#    include <expected>
+#    include <random>
+#    include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#    include <vector>
+#    include <deque>
+#    include <string>
+#    include <stack>
+#    include <queue>
+#    include <list>
+#    include <forward_list>
+#    include <map>
+#    include <set>
+#    include <unordered_set>
+#    include <unordered_map>
+#    if __has_include(<mdspan>)
+#      include <mdspan>
+#    endif
+#    include <valarray>
+
+#    if _LIBCPP_STD_VER < 20
+#      include <ciso646>
+#      if __has_include(<cstdalign>)
+#        include <cstdalign>
+#      endif
+#      include <cstdbool>
+#    endif
+#  endif

>From 259f4c59ffcdd906df3383d5abd199ad54646020 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 19:10:08 +0800
Subject: [PATCH 2/3] [skip ci][libcxx] freestanding has no threads

---
 libcxx/include/__config | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index cbfb448189360..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -234,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API

>From 027f7c01358272d9950c8e0438caf67a0a06e863 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 19:19:02 +0800
Subject: [PATCH 3/3] [skip ci][libcxx] stdio.h #endif missing

---
 libcxx/include/stdio.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index e42485542d347..28d86a60ef247 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -107,7 +107,6 @@ void perror(const char* s);
 #    define EOF (-1)
 #  endif
 #endif
-#endif
 
 #ifndef _LIBCPP_STDIO_H
 #  define _LIBCPP_STDIO_H


From libcxx-commits at lists.llvm.org  Sat Jul 11 06:25:18 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 06:25:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a52443e.f732ad81.3a4994.b90f@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 06:25:25 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 06:25:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a524445.27b3b003.3b871.981a@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 06:27:31 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 06:27:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a5244c3.eb165e5c.1817a5.09df@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 06:28:42 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 06:28:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a52450a.f732ad81.3a4994.2a01@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sat Jul 11 07:57:21 2026
From: libcxx-commits at lists.llvm.org (Ramkumar Ramachandra via libcxx-commits)
Date: Sat, 11 Jul 2026 07:57:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libunwind] [unwind, cxx,
 cxxabi] Fix cmake for static/shared build (PR #176346)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/176346@github.com>
Message-ID: <6a5259d1.492e23e4.157d47.1dce@mx.google.com>


https://github.com/artagnon closed https://github.com/llvm/llvm-project/pull/176346

From libcxx-commits at lists.llvm.org  Sat Jul 11 09:25:15 2026
From: libcxx-commits at lists.llvm.org (Jon Roelofs via libcxx-commits)
Date: Sat, 11 Jul 2026 09:25:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][test] Fingerprint the compiler
 when memoizing results (PR #208311)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208311@github.com>
Message-ID: <6a526e6b.4a342cbc.ca844.ce8b@mx.google.com>


https://github.com/jroelofs updated https://github.com/llvm/llvm-project/pull/208311

>From cf2f5d2b9402ae14c6a9764f4e26f8e179c09758 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Wed, 8 Jul 2026 13:06:09 -0700
Subject: [PATCH] [libcxx][test] Fingerprint the compiler when memoizing
 results

This fixes a cache invalidation problem with flag support checks when rebasing &
re-building the just-built clang.
---
 libcxx/utils/libcxx/test/dsl.py | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/libcxx/utils/libcxx/test/dsl.py b/libcxx/utils/libcxx/test/dsl.py
index ec2e3a2b0b165..c7db35fd629b6 100644
--- a/libcxx/utils/libcxx/test/dsl.py
+++ b/libcxx/utils/libcxx/test/dsl.py
@@ -32,6 +32,21 @@ class ConfigurationRuntimeError(ConfigurationError):
     pass
 
 
+def _compilerFingerprint(config):
+    """
+    Returns an opaque value that changes whenever the compiler being tested is
+    rebuilt, even if it keeps the same path.
+    """
+    try:
+        cxx = _getSubstitution("%{cxx}", config.substitutions)
+        path = shutil.which(cxx) or cxx
+        path = os.path.realpath(path)
+        st = os.stat(path)
+        return (path, st.st_mtime_ns, st.st_size)
+    except OSError:
+        return None
+
+
 def _memoizeExpensiveOperation(extractCacheKey):
     """
     Allows memoizing a very expensive operation.
@@ -62,7 +77,12 @@ def f(config, *args, **kwargs):
                 with open(persistentCache, "rb") as cacheFile:
                     cache = pickle.load(cacheFile)
 
-            cacheKey = pickle.dumps(extractCacheKey(config, *args, **kwargs))
+            # Mix in the compiler fingerprint so that rebuilding clang
+            # in-place invalidates any cached verdicts obtained against the
+            # previous build, instead of silently reusing them.
+            cacheKey = pickle.dumps(
+                (_compilerFingerprint(config), extractCacheKey(config, *args, **kwargs))
+            )
             if cacheKey not in cache:
                 cache[cacheKey] = function(config, *args, **kwargs)
                 # Update the persistent cache so it knows about the new key


From libcxx-commits at lists.llvm.org  Sat Jul 11 10:15:14 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Sat, 11 Jul 2026 10:15:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Bump runner set (PR #208929)
Message-ID: <llvm.org/llvm/llvm-project/pull/208929@github.com>


https://github.com/boomanaiden154 created https://github.com/llvm/llvm-project/pull/208929

This pulls in 6d3cf7f436a01f4622fb660e413a4020209777b8, and 8ece73c9f8b54fea6a98e9e4c55f9a2e6ccc9b3c.

>From 4ce37d82e811ed41852dc8af7636d2130f3f4e32 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sat, 11 Jul 2026 17:14:14 +0000
Subject: [PATCH] [libcxx] Bump runner set

This pulls in 6d3cf7f436a01f4622fb660e413a4020209777b8,
and 8ece73c9f8b54fea6a98e9e4c55f9a2e6ccc9b3c.
---
 libcxx/utils/ci/images/libcxx_runners.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/utils/ci/images/libcxx_runners.txt b/libcxx/utils/ci/images/libcxx_runners.txt
index 4364890c19fa0..b593254d1cf01 100644
--- a/libcxx/utils/ci/images/libcxx_runners.txt
+++ b/libcxx/utils/ci/images/libcxx_runners.txt
@@ -1 +1 @@
-ghcr.io/llvm/libcxx-linux-builder:d76111a9650d8d7540677af5ecfa8b958ab59851
+ghcr.io/llvm/libcxx-linux-builder:6d3cf7f436a01f4622fb660e413a4020209777b8


From libcxx-commits at lists.llvm.org  Sat Jul 11 10:15:53 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 10:15:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Bump runner set (PR #208929)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208929@github.com>
Message-ID: <6a527a49.6c8668f2.e4f82.93df@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Aiden Grossman (boomanaiden154)

<details>
<summary>Changes</summary>

This pulls in 6d3cf7f436a01f4622fb660e413a4020209777b8, and 8ece73c9f8b54fea6a98e9e4c55f9a2e6ccc9b3c.

---
Full diff: https://github.com/llvm/llvm-project/pull/208929.diff


1 Files Affected:

- (modified) libcxx/utils/ci/images/libcxx_runners.txt (+1-1) 


``````````diff
diff --git a/libcxx/utils/ci/images/libcxx_runners.txt b/libcxx/utils/ci/images/libcxx_runners.txt
index 4364890c19fa0..b593254d1cf01 100644
--- a/libcxx/utils/ci/images/libcxx_runners.txt
+++ b/libcxx/utils/ci/images/libcxx_runners.txt
@@ -1 +1 @@
-ghcr.io/llvm/libcxx-linux-builder:d76111a9650d8d7540677af5ecfa8b958ab59851
+ghcr.io/llvm/libcxx-linux-builder:6d3cf7f436a01f4622fb660e413a4020209777b8

``````````

</details>


https://github.com/llvm/llvm-project/pull/208929

From libcxx-commits at lists.llvm.org  Sat Jul 11 10:33:05 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 10:33:05 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [clang][ThreadSafety] Add
 requires_negative_capability attribute (PR #203099)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/203099@github.com>
Message-ID: <6a527e51.13be6df8.145c.d05a@mx.google.com>


AnthonyCalandraGeotab wrote:

Before I continue any more work on this, any thoughts about this patch? @philnik777 

https://github.com/llvm/llvm-project/pull/203099

From libcxx-commits at lists.llvm.org  Sat Jul 11 12:06:16 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sat, 11 Jul 2026 12:06:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a529428.6c5720b3.15e90c.e8ed@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/207585

>From 5b2c2c5266129734c45c3c3806692151150acaf0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 5 Jul 2026 14:17:28 +0100
Subject: [PATCH 1/4] Implementation of adjacent_difference on top of a binary
 transform

---
 libcxx/include/__numeric/pstl.h               |  39 ++++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  35 +++-
 .../pstl.iterator-requirements.verify.cpp     |  11 ++
 .../algorithms/pstl.nodiscard.verify.cpp      |   5 +
 .../pstl.exception_handling.pass.cpp          |  16 +-
 .../pstl.adjacent_difference.pass.cpp         | 171 +++++++++++++++++
 .../pstl.adjacent_difference_op.pass.cpp      | 181 ++++++++++++++++++
 8 files changed, 461 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp

diff --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h
index ecbc9f5348333..0b85cfb8c2327 100644
--- a/libcxx/include/__numeric/pstl.h
+++ b/libcxx/include/__numeric/pstl.h
@@ -165,6 +165,45 @@ template <class _ExecutionPolicy,
       std::move(__transform));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first1), std::move(__last1), std::move(__first2), minus{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryOperation,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _BinaryOperation __op) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__op));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 1ea121367fbc7..c40745a25134a 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -310,6 +310,13 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_difference;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+// optional<_ForwardIterator2>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index cb963a130f743..1e51cd7645e62 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -91,11 +91,12 @@ namespace __pstl {
 //
 // transform and transform_binary family
 // -------------------------------------
-// - replace_copy_if
-// - replace_copy
-// - move
+// - adjacent_difference
 // - copy
 // - copy_n
+// - move
+// - replace_copy
+// - replace_copy_if
 // - rotate_copy
 //
 
@@ -557,6 +558,34 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _BinaryOperation&& __op) const noexcept {
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    if (__first1 == __last1)
+      return __first2; // edge case: empty input range, just return the output iterator
+    *__first2 = *__first1;
+    ++__first2;
+    _ForwardIterator1 __first_left = std::next(__first1);
+    if (__first_left == __last1)
+      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+    // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
+    return _TransformBinary()(
+        __policy,
+        std::move(__first_left),
+        std::move(__last1),
+        std::move(__first1),
+        std::move(__first2),
+        std::forward<_BinaryOperation>(__op));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index aee3f97019071..fb81f293df896 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -201,4 +201,15 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::transform_reduce(
         pol, non_fwd, non_fwd, val, func, func); // expected-error@*:* {{static assertion failed: transform_reduce}}
   }
+
+  {
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+  }
 }
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 747d62bc66837..9b2b025d27a95 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <algorithm>
 #include <execution>
 #include <iterator>
+#include <numeric>
 
 void test() {
   int a[]    = {1};
@@ -56,4 +57,8 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
 }
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 9e6c8f5b8ce40..db4bfc42e0d70 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -85,7 +85,7 @@ int main(int, char**) {
       auto last1       = util::throw_on_move_iterator(std::end(a), tokens[1].active() ? 1 : -1);
       auto first2      = util::throw_on_move_iterator(std::begin(b), tokens[2].active() ? 1 : -1);
       auto last2       = util::throw_on_move_iterator(std::end(b), tokens[3].active() ? 1 : -1);
-      auto dest        = util::throw_on_move_iterator(std::end(storage), tokens[4].active() ? 1 : -1);
+      auto dest        = util::throw_on_move_iterator(std::begin(storage), tokens[4].active() ? 1 : -1);
       auto maybe_throw = [](ThrowToken const& token, auto f) {
         return [&token, f](auto... args) {
           if (token.active())
@@ -358,6 +358,20 @@ int main(int, char**) {
           (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction);
         });
       }
+
+      {
+        auto op = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
+
+        // adjacent_difference(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+
+        // adjacent_difference(first, last, dest, op)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest), op);
+        });
+      }
     }
   });
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
new file mode 100644
index 0000000000000..bb98004c1c62a
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+
+  friend X operator-(const X& x, const X& y) { return X(x.i_ - y.i_); }
+
+  friend class Y;
+};
+
+class Y {
+  int i_;
+
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 20};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, -5, -4, -3, -2};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r =
+            std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        assert(std::all_of(std::begin(ib), std::begin(ib) + i, [](int x) { return x == 1; }));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(7)};
+      Y ir[3] = {Y(1), Y(4), Y(2)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
new file mode 100644
index 0000000000000..26d65f9a77c10
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryOperation>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2,
+//                                        BinaryOperation op);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+  friend class Y;
+  friend struct Op;
+};
+
+class Y {
+  int i_;
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+// ensures that the predicate is not equivalent to std::minus
+struct Op {
+  int operator()(int l, int r) const { return l * 2 - r; }
+  X operator()(const X& l, const X& r) const { return X(l.i_ * 2 - r.i_); }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 50};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, 5, 2, 0, -1};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r = std::adjacent_difference(
+            policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)), Op{});
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j)
+          assert(ib[j] == (int)(j == 0 ? 1 : j + 2));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(8)};
+      Y ir[3] = {Y(1), Y(9), Y(11)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}

>From 9c8a249ce868760cf662f1d1cfc3347fa4767f77 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 10:34:09 +0100
Subject: [PATCH 2/4] Renamed the variable names in __adjacent_difference

---
 libcxx/include/__pstl/backends/default.h | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 1e51cd7645e62..5521f4de8ee39 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -565,23 +565,23 @@ struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
   operator()(_Policy&& __policy,
              _ForwardIterator1 __first1,
              _ForwardIterator1 __last1,
-             _ForwardIterator2 __first2,
+             _ForwardIterator2 __result,
              _BinaryOperation&& __op) const noexcept {
     using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
     if (__first1 == __last1)
-      return __first2; // edge case: empty input range, just return the output iterator
-    *__first2 = *__first1;
-    ++__first2;
-    _ForwardIterator1 __first_left = std::next(__first1);
-    if (__first_left == __last1)
-      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+      return __result; // edge case: empty input range, just return the output iterator
+    *__result = *__first1;
+    ++__result;
+    _ForwardIterator1 __first2 = std::next(__first1);
+    if (__first2 == __last1)
+      return __result; // edge case: not enough elements to perform adjacent difference, just return the output iterator
     // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
     return _TransformBinary()(
         __policy,
-        std::move(__first_left),
+        std::move(__first2),
         std::move(__last1),
         std::move(__first1),
-        std::move(__first2),
+        std::move(__result),
         std::forward<_BinaryOperation>(__op));
   }
 };

>From 789a3d1f37961eb4b246a8a694bfa09f380181b4 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 20:01:44 +0100
Subject: [PATCH 3/4] Moved runway_sample() to test/support/runway_sample.h

---
 .../pstl.adjacent_difference.pass.cpp         |  16 +--
 .../pstl.adjacent_difference_op.pass.cpp      |  16 +--
 libcxx/test/support/runway_sample.h           |  58 ++++++++++
 .../test.support/runway_sample.pass.cpp       | 101 ++++++++++++++++++
 4 files changed, 161 insertions(+), 30 deletions(-)
 create mode 100644 libcxx/test/support/runway_sample.h
 create mode 100644 libcxx/test/support/test.support/runway_sample.pass.cpp

diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
index bb98004c1c62a..4dfe3ad58adf8 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -29,27 +29,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
 
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 class X {
   int i_;
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
index 26d65f9a77c10..f6b9b480f4ba8 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -31,27 +31,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
 
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 class X {
   int i_;
   X& operator=(const X&);
diff --git a/libcxx/test/support/runway_sample.h b/libcxx/test/support/runway_sample.h
new file mode 100644
index 0000000000000..16ecd58cef747
--- /dev/null
+++ b/libcxx/test/support/runway_sample.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+#ifndef TEST_SUPPORT_RUNWAY_SAMPLE_H
+#define TEST_SUPPORT_RUNWAY_SAMPLE_H
+
+#include <cstddef>
+#include <algorithm>
+#include "test_macros.h"
+
+// runway_sample(size, callable) calls callable(i) for a set of sample indices i in the range [0, size).
+// they are chosen in 3 phases:
+//   - prefix, sampled densely with increments of 1
+//   - middle, sampled sparsely with uncanny numbers
+//   - suffix, sampled densely with increments of 1
+//
+// Examples of sample indices for various sizes:
+// size = 0:
+// <no samples>
+// size = 1:
+// 0
+// size = 10:
+// 0 1 2 3 4 5 6 7 8 9
+// size = 100:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
+// size = 1000:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 157 493 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
+template <class Callable>
+TEST_CONSTEXPR_CXX14 void runway_sample(std::size_t size, Callable callable) {
+  constexpr std::size_t affix = 16;
+  std::size_t i               = 0;
+
+  // 0, 1, 2, ..., 15
+  for (; i < std::min(size, affix); ++i) {
+    callable(i);
+  }
+
+  if (size <= affix) {
+    return;
+  }
+
+  // 16, 50, 157, 493, 1549, 4868, ...
+  for (std::size_t j = i; j + affix < size; i = j, j = j * 3 + j / 7) {
+    callable(j);
+  }
+
+  // size - 16, size - 15, ..., size - 1
+  i = std::max(i, size - affix);
+  for (; i < size; ++i) {
+    callable(i);
+  }
+}
+
+#endif // TEST_SUPPORT_RUNWAY_SAMPLE_H
diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
new file mode 100644
index 0000000000000..9fc288f881082
--- /dev/null
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++11
+
+// "support/runway_sample.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "runway_sample.h"
+
+TEST_CONSTEXPR_CXX14 bool test() {
+  {
+    bool fail = false;
+    runway_sample(0, [&](size_t /*i*/) { fail = true; });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(0, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(10, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29};
+    size_t n  = 0;
+    bool fail = false;
+    runway_sample(30, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
+                         50, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(100, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  50,
+                         157, 493, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(1000, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  return true;
+}
+
+int main() {
+  assert(test());
+#if TEST_STD_VER >= 14
+  static_assert(test());
+#endif
+  return 0;
+}

>From 66efdaa675a93eef609c70102cdd43399d7f5aea Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 20:05:51 +0100
Subject: [PATCH 4/4] Added a note about X and Y in the tests

---
 .../adjacent.difference/pstl.adjacent_difference.pass.cpp     | 4 ++++
 .../adjacent.difference/pstl.adjacent_difference_op.pass.cpp  | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
index 4dfe3ad58adf8..d06f5b64e2f9f 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -36,6 +36,10 @@ EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
 
+// Types X and Y are provided to test adjacent_difference() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
 class X {
   int i_;
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
index f6b9b480f4ba8..d623cda058d04 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -38,6 +38,10 @@ EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
 
+// Types X and Y are provided to test adjacent_difference() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
 class X {
   int i_;
   X& operator=(const X&);


From libcxx-commits at lists.llvm.org  Sat Jul 11 12:18:10 2026
From: libcxx-commits at lists.llvm.org (Vy Nguyen via libcxx-commits)
Date: Sat, 11 Jul 2026 12:18:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Bump runner set (PR #208929)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208929@github.com>
Message-ID: <6a5296f2.824e11a6.f5f27.7ff7@mx.google.com>


https://github.com/oontvoo approved this pull request.


https://github.com/llvm/llvm-project/pull/208929

From libcxx-commits at lists.llvm.org  Sat Jul 11 12:22:58 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Sat, 11 Jul 2026 12:22:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Move release notes for
 MSVC's implementation of exception_ptr (PR #208883)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208883@github.com>
Message-ID: <6a529812.6537ae93.2f1c1b.c7ab@mx.google.com>


https://github.com/petrhosek updated https://github.com/llvm/llvm-project/pull/208883

>From 7cf1da4848749afabfe0e61d13c1568c8253638a Mon Sep 17 00:00:00 2001
From: Petr Hosek <phosek at google.com>
Date: Sat, 11 Jul 2026 06:05:28 +0000
Subject: [PATCH 1/2] [libcxx][NFC] Move release notes for MSVC's
 implementation of exception_ptr

These were accidentally placed in LLVM 22 rather than 23 file.
---
 libcxx/docs/ReleaseNotes/22.rst | 6 ------
 libcxx/docs/ReleaseNotes/23.rst | 7 +++++++
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 556fea76d33ac..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -128,9 +128,6 @@ Potentially breaking changes
   ``_LIBCPP_ABI_NO_ITERATOR_BASES``. If you are using this flag and care about ABI stability, you should set
   ``_LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER`` as well.
 
-- ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
-  The implementation was donated by Microsoft and has been refactored to follow libc++ standards and conventions.
-
 Announcements About Future Releases
 -----------------------------------
 
@@ -157,8 +154,5 @@ ABI Affecting Changes
   ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``. Please inform the libc++ team if you need this flag,
   since it will be removed in LLVM 24 if there is no evidence that it's required.
 
-- ``std::exception_ptr`` now uses the same layout on Windows as on other platforms, changing the size from two to one
-  pointer.
-
 Build System Changes
 --------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..767ada2258d51 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -98,6 +98,9 @@ Potentially breaking changes
 - The output of ``ostream << std::thread::id`` and ``std::format("...", std::thread:id)`` changes on some platforms from
   hexadecimal to decimal. This is done to have consistent formatting across all platforms libc++ supports.
 
+- ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
+  The implementation was donated by Microsoft and has been refactored to follow libc++ standards and conventions.
+
 Announcements About Future Releases
 -----------------------------------
 
@@ -125,5 +128,9 @@ ABI Affecting Changes
   map is never modified via an ``iterator``. This is necessary for reducing undefined behavior and for adding
   ``constexpr`` support for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior.
 
+- ``std::exception_ptr`` now uses the same layout in MSVC mode as on other platforms, changing the size from two to one
+  pointer.
+
+
 Build System Changes
 --------------------

>From 0dbdb0ddbca281634412963f8d97b7e60a133f5b Mon Sep 17 00:00:00 2001
From: Petr Hosek <phosek at google.com>
Date: Sat, 11 Jul 2026 19:22:32 +0000
Subject: [PATCH 2/2] Fix formatting

---
 libcxx/docs/ReleaseNotes/23.rst | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 767ada2258d51..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -131,6 +131,5 @@ ABI Affecting Changes
 - ``std::exception_ptr`` now uses the same layout in MSVC mode as on other platforms, changing the size from two to one
   pointer.
 
-
 Build System Changes
 --------------------


From libcxx-commits at lists.llvm.org  Sat Jul 11 12:23:18 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 12:23:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 419cce0 - [libcxx][NFC] Move release
 notes for MSVC's implementation of exception_ptr (#208883)
Message-ID: <6a529826.07d7ca98.9dee3.4e7c@mx.google.com>


Author: Petr Hosek
Date: 2026-07-11T12:23:14-07:00
New Revision: 419cce0bb5e5fea52f701a20124f88ea1eee3e29

URL: https://github.com/llvm/llvm-project/commit/419cce0bb5e5fea52f701a20124f88ea1eee3e29
DIFF: https://github.com/llvm/llvm-project/commit/419cce0bb5e5fea52f701a20124f88ea1eee3e29.diff

LOG: [libcxx][NFC] Move release notes for MSVC's implementation of exception_ptr (#208883)

These were accidentally placed in LLVM 22 rather than 23 file.

Added: 
    

Modified: 
    libcxx/docs/ReleaseNotes/22.rst
    libcxx/docs/ReleaseNotes/23.rst

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 556fea76d33ac..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -128,9 +128,6 @@ Potentially breaking changes
   ``_LIBCPP_ABI_NO_ITERATOR_BASES``. If you are using this flag and care about ABI stability, you should set
   ``_LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER`` as well.
 
-- ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
-  The implementation was donated by Microsoft and has been refactored to follow libc++ standards and conventions.
-
 Announcements About Future Releases
 -----------------------------------
 
@@ -157,8 +154,5 @@ ABI Affecting Changes
   ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``. Please inform the libc++ team if you need this flag,
   since it will be removed in LLVM 24 if there is no evidence that it's required.
 
-- ``std::exception_ptr`` now uses the same layout on Windows as on other platforms, changing the size from two to one
-  pointer.
-
 Build System Changes
 --------------------

diff  --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -98,6 +98,9 @@ Potentially breaking changes
 - The output of ``ostream << std::thread::id`` and ``std::format("...", std::thread:id)`` changes on some platforms from
   hexadecimal to decimal. This is done to have consistent formatting across all platforms libc++ supports.
 
+- ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
+  The implementation was donated by Microsoft and has been refactored to follow libc++ standards and conventions.
+
 Announcements About Future Releases
 -----------------------------------
 
@@ -125,5 +128,8 @@ ABI Affecting Changes
   map is never modified via an ``iterator``. This is necessary for reducing undefined behavior and for adding
   ``constexpr`` support for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior.
 
+- ``std::exception_ptr`` now uses the same layout in MSVC mode as on other platforms, changing the size from two to one
+  pointer.
+
 Build System Changes
 --------------------


        

From libcxx-commits at lists.llvm.org  Sat Jul 11 12:23:21 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Sat, 11 Jul 2026 12:23:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Move release notes for
 MSVC's implementation of exception_ptr (PR #208883)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208883@github.com>
Message-ID: <6a529829.8e75e0cb.3dbce4.8286@mx.google.com>


https://github.com/petrhosek closed https://github.com/llvm/llvm-project/pull/208883

From libcxx-commits at lists.llvm.org  Sat Jul 11 12:25:06 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 12:25:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 6e01e73 - [libcxx] Bump runner set
	(#208929)
Message-ID: <6a529892.eb165e5c.1817a5.4471@mx.google.com>


Author: Aiden Grossman
Date: 2026-07-11T12:25:02-07:00
New Revision: 6e01e7334d7b9f8696f5b883b433d56e7a8b2851

URL: https://github.com/llvm/llvm-project/commit/6e01e7334d7b9f8696f5b883b433d56e7a8b2851
DIFF: https://github.com/llvm/llvm-project/commit/6e01e7334d7b9f8696f5b883b433d56e7a8b2851.diff

LOG: [libcxx] Bump runner set (#208929)

This pulls in 6d3cf7f436a01f4622fb660e413a4020209777b8, and
8ece73c9f8b54fea6a98e9e4c55f9a2e6ccc9b3c.

Added: 
    

Modified: 
    libcxx/utils/ci/images/libcxx_runners.txt

Removed: 
    


################################################################################
diff  --git a/libcxx/utils/ci/images/libcxx_runners.txt b/libcxx/utils/ci/images/libcxx_runners.txt
index 4364890c19fa0..b593254d1cf01 100644
--- a/libcxx/utils/ci/images/libcxx_runners.txt
+++ b/libcxx/utils/ci/images/libcxx_runners.txt
@@ -1 +1 @@
-ghcr.io/llvm/libcxx-linux-builder:d76111a9650d8d7540677af5ecfa8b958ab59851
+ghcr.io/llvm/libcxx-linux-builder:6d3cf7f436a01f4622fb660e413a4020209777b8


        

From libcxx-commits at lists.llvm.org  Sat Jul 11 12:25:09 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Sat, 11 Jul 2026 12:25:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Bump runner set (PR #208929)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208929@github.com>
Message-ID: <6a529895.eb165e5c.1817a5.453e@mx.google.com>


https://github.com/boomanaiden154 closed https://github.com/llvm/llvm-project/pull/208929

From libcxx-commits at lists.llvm.org  Sat Jul 11 12:51:48 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Sat, 11 Jul 2026 12:51:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
Message-ID: <llvm.org/llvm/llvm-project/pull/208945@github.com>


https://github.com/boomanaiden154 created https://github.com/llvm/llvm-project/pull/208945

Deprecated in 1bcf74006bcf528d14377173dba459759d35c961. Causes warnings during compilation and will presumably become an error.

>From 20c1f414a5514766969069b9e42764db3b48f288 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sat, 11 Jul 2026 19:50:43 +0000
Subject: [PATCH] [libcxx] Avoid including deprecated ClangTidyModuleRegistry.h

Deprecated in 1bcf74006bcf528d14377173dba459759d35c961. Causes warnings
during compilation and will presumably become an error.
---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp      | 2 +-
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp        | 2 +-
 .../tools/clang_tidy_checks/header_exportable_declarations.cpp  | 2 +-
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp           | 2 +-
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp           | 2 +-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp      | 2 +-
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp     | 2 +-
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp       | 2 +-
 8 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index f1866179a3b68..64dca4a08d62f 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "abi_tag_on_virtual.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index e176b29670ce9..7ccf6e3a910af 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "empty_namespaces.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 8ec029f7dba2d..24f8c1bf8caf3 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "llvm/ADT/ArrayRef.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index f05470e47d3e7..cb0c6a33b8498 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "hide_from_abi.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index b6298eefdeb44..b94989ca8c788 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "abi_tag_on_virtual.hpp"
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..a7e3f547d3ed8 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "robust_against_adl.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index a608954aa2cc7..e94c95561f92f 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 24bacde6304bc..aa325d9ba378f 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "uglify_attributes.hpp"
 #include "utilities.hpp"


From libcxx-commits at lists.llvm.org  Sat Jul 11 12:52:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 12:52:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a529efa.510f576d.1b3cb9.9bcd@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Aiden Grossman (boomanaiden154)

<details>
<summary>Changes</summary>

Deprecated in 1bcf74006bcf528d14377173dba459759d35c961. Causes warnings during compilation and will presumably become an error.

---
Full diff: https://github.com/llvm/llvm-project/pull/208945.diff


8 Files Affected:

- (modified) libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp (+1-1) 
- (modified) libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp (+1-1) 
- (modified) libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp (+1-1) 
- (modified) libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp (+1-1) 
- (modified) libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp (+1-1) 
- (modified) libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp (+1-1) 
- (modified) libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp (+1-1) 
- (modified) libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp (+1-1) 


``````````diff
diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index f1866179a3b68..64dca4a08d62f 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "abi_tag_on_virtual.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index e176b29670ce9..7ccf6e3a910af 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "empty_namespaces.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 8ec029f7dba2d..24f8c1bf8caf3 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "llvm/ADT/ArrayRef.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index f05470e47d3e7..cb0c6a33b8498 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "hide_from_abi.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index b6298eefdeb44..b94989ca8c788 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "abi_tag_on_virtual.hpp"
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..a7e3f547d3ed8 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "robust_against_adl.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index a608954aa2cc7..e94c95561f92f 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 24bacde6304bc..aa325d9ba378f 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "uglify_attributes.hpp"
 #include "utilities.hpp"

``````````

</details>


https://github.com/llvm/llvm-project/pull/208945

From libcxx-commits at lists.llvm.org  Sat Jul 11 13:07:08 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sat, 11 Jul 2026 13:07:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a52a26c.481afadf.196d57.1f39@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/207585

>From 5b2c2c5266129734c45c3c3806692151150acaf0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 5 Jul 2026 14:17:28 +0100
Subject: [PATCH 1/5] Implementation of adjacent_difference on top of a binary
 transform

---
 libcxx/include/__numeric/pstl.h               |  39 ++++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  35 +++-
 .../pstl.iterator-requirements.verify.cpp     |  11 ++
 .../algorithms/pstl.nodiscard.verify.cpp      |   5 +
 .../pstl.exception_handling.pass.cpp          |  16 +-
 .../pstl.adjacent_difference.pass.cpp         | 171 +++++++++++++++++
 .../pstl.adjacent_difference_op.pass.cpp      | 181 ++++++++++++++++++
 8 files changed, 461 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp

diff --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h
index ecbc9f5348333..0b85cfb8c2327 100644
--- a/libcxx/include/__numeric/pstl.h
+++ b/libcxx/include/__numeric/pstl.h
@@ -165,6 +165,45 @@ template <class _ExecutionPolicy,
       std::move(__transform));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first1), std::move(__last1), std::move(__first2), minus{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryOperation,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _BinaryOperation __op) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__op));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 1ea121367fbc7..c40745a25134a 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -310,6 +310,13 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_difference;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+// optional<_ForwardIterator2>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index cb963a130f743..1e51cd7645e62 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -91,11 +91,12 @@ namespace __pstl {
 //
 // transform and transform_binary family
 // -------------------------------------
-// - replace_copy_if
-// - replace_copy
-// - move
+// - adjacent_difference
 // - copy
 // - copy_n
+// - move
+// - replace_copy
+// - replace_copy_if
 // - rotate_copy
 //
 
@@ -557,6 +558,34 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _BinaryOperation&& __op) const noexcept {
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    if (__first1 == __last1)
+      return __first2; // edge case: empty input range, just return the output iterator
+    *__first2 = *__first1;
+    ++__first2;
+    _ForwardIterator1 __first_left = std::next(__first1);
+    if (__first_left == __last1)
+      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+    // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
+    return _TransformBinary()(
+        __policy,
+        std::move(__first_left),
+        std::move(__last1),
+        std::move(__first1),
+        std::move(__first2),
+        std::forward<_BinaryOperation>(__op));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index aee3f97019071..fb81f293df896 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -201,4 +201,15 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::transform_reduce(
         pol, non_fwd, non_fwd, val, func, func); // expected-error@*:* {{static assertion failed: transform_reduce}}
   }
+
+  {
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+  }
 }
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 747d62bc66837..9b2b025d27a95 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <algorithm>
 #include <execution>
 #include <iterator>
+#include <numeric>
 
 void test() {
   int a[]    = {1};
@@ -56,4 +57,8 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
 }
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 9e6c8f5b8ce40..db4bfc42e0d70 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -85,7 +85,7 @@ int main(int, char**) {
       auto last1       = util::throw_on_move_iterator(std::end(a), tokens[1].active() ? 1 : -1);
       auto first2      = util::throw_on_move_iterator(std::begin(b), tokens[2].active() ? 1 : -1);
       auto last2       = util::throw_on_move_iterator(std::end(b), tokens[3].active() ? 1 : -1);
-      auto dest        = util::throw_on_move_iterator(std::end(storage), tokens[4].active() ? 1 : -1);
+      auto dest        = util::throw_on_move_iterator(std::begin(storage), tokens[4].active() ? 1 : -1);
       auto maybe_throw = [](ThrowToken const& token, auto f) {
         return [&token, f](auto... args) {
           if (token.active())
@@ -358,6 +358,20 @@ int main(int, char**) {
           (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction);
         });
       }
+
+      {
+        auto op = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
+
+        // adjacent_difference(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+
+        // adjacent_difference(first, last, dest, op)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest), op);
+        });
+      }
     }
   });
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
new file mode 100644
index 0000000000000..bb98004c1c62a
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+
+  friend X operator-(const X& x, const X& y) { return X(x.i_ - y.i_); }
+
+  friend class Y;
+};
+
+class Y {
+  int i_;
+
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 20};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, -5, -4, -3, -2};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r =
+            std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        assert(std::all_of(std::begin(ib), std::begin(ib) + i, [](int x) { return x == 1; }));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(7)};
+      Y ir[3] = {Y(1), Y(4), Y(2)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
new file mode 100644
index 0000000000000..26d65f9a77c10
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryOperation>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2,
+//                                        BinaryOperation op);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+  friend class Y;
+  friend struct Op;
+};
+
+class Y {
+  int i_;
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+// ensures that the predicate is not equivalent to std::minus
+struct Op {
+  int operator()(int l, int r) const { return l * 2 - r; }
+  X operator()(const X& l, const X& r) const { return X(l.i_ * 2 - r.i_); }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 50};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, 5, 2, 0, -1};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r = std::adjacent_difference(
+            policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)), Op{});
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j)
+          assert(ib[j] == (int)(j == 0 ? 1 : j + 2));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(8)};
+      Y ir[3] = {Y(1), Y(9), Y(11)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}

>From 9c8a249ce868760cf662f1d1cfc3347fa4767f77 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 10:34:09 +0100
Subject: [PATCH 2/5] Renamed the variable names in __adjacent_difference

---
 libcxx/include/__pstl/backends/default.h | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 1e51cd7645e62..5521f4de8ee39 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -565,23 +565,23 @@ struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
   operator()(_Policy&& __policy,
              _ForwardIterator1 __first1,
              _ForwardIterator1 __last1,
-             _ForwardIterator2 __first2,
+             _ForwardIterator2 __result,
              _BinaryOperation&& __op) const noexcept {
     using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
     if (__first1 == __last1)
-      return __first2; // edge case: empty input range, just return the output iterator
-    *__first2 = *__first1;
-    ++__first2;
-    _ForwardIterator1 __first_left = std::next(__first1);
-    if (__first_left == __last1)
-      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+      return __result; // edge case: empty input range, just return the output iterator
+    *__result = *__first1;
+    ++__result;
+    _ForwardIterator1 __first2 = std::next(__first1);
+    if (__first2 == __last1)
+      return __result; // edge case: not enough elements to perform adjacent difference, just return the output iterator
     // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
     return _TransformBinary()(
         __policy,
-        std::move(__first_left),
+        std::move(__first2),
         std::move(__last1),
         std::move(__first1),
-        std::move(__first2),
+        std::move(__result),
         std::forward<_BinaryOperation>(__op));
   }
 };

>From 789a3d1f37961eb4b246a8a694bfa09f380181b4 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 20:01:44 +0100
Subject: [PATCH 3/5] Moved runway_sample() to test/support/runway_sample.h

---
 .../pstl.adjacent_difference.pass.cpp         |  16 +--
 .../pstl.adjacent_difference_op.pass.cpp      |  16 +--
 libcxx/test/support/runway_sample.h           |  58 ++++++++++
 .../test.support/runway_sample.pass.cpp       | 101 ++++++++++++++++++
 4 files changed, 161 insertions(+), 30 deletions(-)
 create mode 100644 libcxx/test/support/runway_sample.h
 create mode 100644 libcxx/test/support/test.support/runway_sample.pass.cpp

diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
index bb98004c1c62a..4dfe3ad58adf8 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -29,27 +29,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
 
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 class X {
   int i_;
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
index 26d65f9a77c10..f6b9b480f4ba8 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -31,27 +31,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
 
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 class X {
   int i_;
   X& operator=(const X&);
diff --git a/libcxx/test/support/runway_sample.h b/libcxx/test/support/runway_sample.h
new file mode 100644
index 0000000000000..16ecd58cef747
--- /dev/null
+++ b/libcxx/test/support/runway_sample.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+#ifndef TEST_SUPPORT_RUNWAY_SAMPLE_H
+#define TEST_SUPPORT_RUNWAY_SAMPLE_H
+
+#include <cstddef>
+#include <algorithm>
+#include "test_macros.h"
+
+// runway_sample(size, callable) calls callable(i) for a set of sample indices i in the range [0, size).
+// they are chosen in 3 phases:
+//   - prefix, sampled densely with increments of 1
+//   - middle, sampled sparsely with uncanny numbers
+//   - suffix, sampled densely with increments of 1
+//
+// Examples of sample indices for various sizes:
+// size = 0:
+// <no samples>
+// size = 1:
+// 0
+// size = 10:
+// 0 1 2 3 4 5 6 7 8 9
+// size = 100:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
+// size = 1000:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 157 493 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
+template <class Callable>
+TEST_CONSTEXPR_CXX14 void runway_sample(std::size_t size, Callable callable) {
+  constexpr std::size_t affix = 16;
+  std::size_t i               = 0;
+
+  // 0, 1, 2, ..., 15
+  for (; i < std::min(size, affix); ++i) {
+    callable(i);
+  }
+
+  if (size <= affix) {
+    return;
+  }
+
+  // 16, 50, 157, 493, 1549, 4868, ...
+  for (std::size_t j = i; j + affix < size; i = j, j = j * 3 + j / 7) {
+    callable(j);
+  }
+
+  // size - 16, size - 15, ..., size - 1
+  i = std::max(i, size - affix);
+  for (; i < size; ++i) {
+    callable(i);
+  }
+}
+
+#endif // TEST_SUPPORT_RUNWAY_SAMPLE_H
diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
new file mode 100644
index 0000000000000..9fc288f881082
--- /dev/null
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++11
+
+// "support/runway_sample.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "runway_sample.h"
+
+TEST_CONSTEXPR_CXX14 bool test() {
+  {
+    bool fail = false;
+    runway_sample(0, [&](size_t /*i*/) { fail = true; });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(0, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(10, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29};
+    size_t n  = 0;
+    bool fail = false;
+    runway_sample(30, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
+                         50, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(100, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  50,
+                         157, 493, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(1000, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  return true;
+}
+
+int main() {
+  assert(test());
+#if TEST_STD_VER >= 14
+  static_assert(test());
+#endif
+  return 0;
+}

>From 66efdaa675a93eef609c70102cdd43399d7f5aea Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 20:05:51 +0100
Subject: [PATCH 4/5] Added a note about X and Y in the tests

---
 .../adjacent.difference/pstl.adjacent_difference.pass.cpp     | 4 ++++
 .../adjacent.difference/pstl.adjacent_difference_op.pass.cpp  | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
index 4dfe3ad58adf8..d06f5b64e2f9f 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -36,6 +36,10 @@ EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
 
+// Types X and Y are provided to test adjacent_difference() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
 class X {
   int i_;
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
index f6b9b480f4ba8..d623cda058d04 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -38,6 +38,10 @@ EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
 
+// Types X and Y are provided to test adjacent_difference() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
 class X {
   int i_;
   X& operator=(const X&);

>From 34de56f3acd39652a3fa14bc2487200ec3269ca3 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 21:06:40 +0100
Subject: [PATCH 5/5] Run runway_sample test on C++17+

---
 libcxx/test/support/test.support/runway_sample.pass.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
index 9fc288f881082..30e0ad20e0f85 100644
--- a/libcxx/test/support/test.support/runway_sample.pass.cpp
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// REQUIRES: std-at-least-c++11
+// REQUIRES: std-at-least-c++17
 
 // "support/runway_sample.h"
 
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "runway_sample.h"
 
-TEST_CONSTEXPR_CXX14 bool test() {
+constexpr bool test() {
   {
     bool fail = false;
     runway_sample(0, [&](size_t /*i*/) { fail = true; });
@@ -94,8 +94,6 @@ TEST_CONSTEXPR_CXX14 bool test() {
 
 int main() {
   assert(test());
-#if TEST_STD_VER >= 14
   static_assert(test());
-#endif
   return 0;
 }


From libcxx-commits at lists.llvm.org  Sat Jul 11 13:22:56 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sat, 11 Jul 2026 13:22:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx]
 __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp,
 _Tp> removes cv-ref from _Tp (PR #208950)
Message-ID: <llvm.org/llvm/llvm-project/pull/208950@github.com>


https://github.com/mikekazakov created https://github.com/llvm/llvm-project/pull/208950

This PR passes the argument type through `__remove_cvref_t` before passing to `is_integral`.

Fixes #208236

>From de4502a3bac34eedba2d5687e67bb6be577e51e7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 21:19:44 +0100
Subject: [PATCH] __desugars_to_v<__totally_ordered_less_tag, __less<>,...>
 strips cv-ref from _Tp

---
 libcxx/include/__algorithm/comp.h           |  4 ++-
 libcxx/test/libcxx/algorithms/comp.pass.cpp | 27 +++++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/libcxx/algorithms/comp.pass.cpp

diff --git a/libcxx/include/__algorithm/comp.h b/libcxx/include/__algorithm/comp.h
index 38e2fb9f5e744..a83b6a8938998 100644
--- a/libcxx/include/__algorithm/comp.h
+++ b/libcxx/include/__algorithm/comp.h
@@ -13,6 +13,7 @@
 #include <__type_traits/desugars_to.h>
 #include <__type_traits/is_generic_transparent_comparator.h>
 #include <__type_traits/is_integral.h>
+#include <__type_traits/remove_cvref.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -47,7 +48,8 @@ template <class _Tp>
 inline const bool __desugars_to_v<__less_tag, __less<>, _Tp, _Tp> = true;
 
 template <class _Tp>
-inline const bool __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp, _Tp> = is_integral<_Tp>::value;
+inline const bool __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp, _Tp> =
+    is_integral<__remove_cvref_t<_Tp> >::value;
 
 template <>
 inline const bool __is_generic_transparent_comparator_v<__less<> > = true;
diff --git a/libcxx/test/libcxx/algorithms/comp.pass.cpp b/libcxx/test/libcxx/algorithms/comp.pass.cpp
new file mode 100644
index 0000000000000..87095f630ee52
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/comp.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// <__algorithm/comp.h>
+
+#include <__algorithm/comp.h>
+
+// check that __less<> desugars to __totally_ordered_less_tag for integral types regardless of their cv-ref
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int, int>);
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int&, int&>);
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, const int, const int>);
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, const int&, const int&>);
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile int&, volatile int&>);
+static_assert(
+    std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile const int&, volatile const int&>);
+static_assert(
+    !std::
+        __desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile const float&, volatile const float&>);
+
+int main(int, char**) { return 0; }


From libcxx-commits at lists.llvm.org  Sat Jul 11 13:23:35 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sat, 11 Jul 2026 13:23:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]
 __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp,
 _Tp> removes cv-ref from _Tp (PR #208950)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208950@github.com>
Message-ID: <6a52a647.d76d81af.39f9be.7f73@mx.google.com>


https://github.com/mikekazakov edited https://github.com/llvm/llvm-project/pull/208950

From libcxx-commits at lists.llvm.org  Sat Jul 11 13:37:52 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 13:37:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix usage of constructive
 interference when we meant destructive (PR #208804)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208804@github.com>
Message-ID: <6a52a9a0.f1c1923d.991d1.c234@mx.google.com>


https://github.com/huixie90 approved this pull request.


https://github.com/llvm/llvm-project/pull/208804

From libcxx-commits at lists.llvm.org  Sat Jul 11 14:50:57 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Sat, 11 Jul 2026 14:50:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix missing 'get_new_handler()'
 for Windows builds (PR #150182)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/150182@github.com>
Message-ID: <6a52bac1.c574b9b4.21ead0.98c6@mx.google.com>


petrhosek wrote:

Now that https://github.com/llvm/llvm-project/pull/94977 has been merged, this is the last remaining blocker that prevents us from removing the dependency on msvcprt/libcpmt. Could we prioritize merging this change, preferably before LLVM 23 branches?

https://github.com/llvm/llvm-project/pull/150182

From libcxx-commits at lists.llvm.org  Sat Jul 11 16:30:02 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 16:30:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 410a2a2 - [libc++][pstl] Default
 implementation of parallel std::reverse_copy (#208085)
Message-ID: <6a52d1fa.481afadf.196d57.4087@mx.google.com>


Author: Michael G. Kazakov
Date: 2026-07-12T07:29:57+08:00
New Revision: 410a2a236355a432c6a73cbd3338badabd280dc8

URL: https://github.com/llvm/llvm-project/commit/410a2a236355a432c6a73cbd3338badabd280dc8
DIFF: https://github.com/llvm/llvm-project/commit/410a2a236355a432c6a73cbd3338badabd280dc8.diff

LOG: [libc++][pstl] Default implementation of parallel std::reverse_copy (#208085)

This PR adds a default parallel implementation of `std::reverse_copy`
based on `std::reverse_iterator` + parallel `std::copy`.

Effectively it's a one-liner:
```c++
    return _Copy()(__policy,
                   std::reverse_iterator<_BidirectionalIterator>(std::move(__last)),
                   std::reverse_iterator<_BidirectionalIterator>(std::move(__first)),
                   std::move(__result));
```

Included tests check that:

- Semantics of the function is correct.
- The function correctly SFINAE out when the first argument is not an
execution policy.
- The `nodiscard` policy is followed.
- The `noexcept` policy is followed.
- `static_assert` verifies iterators' categories (bidirectional and
forward).

Part of #99938.

---------

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>

Added: 
    libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp

Modified: 
    libcxx/include/__algorithm/pstl.h
    libcxx/include/__pstl/backend_fwd.h
    libcxx/include/__pstl/backends/default.h
    libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
    libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
    libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 3530b3207f59ef..2bf9b4276baab6 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -395,6 +395,23 @@ generate_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n, _Ge
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__n), std::move(__gen));
 }
 
+template <class _ExecutionPolicy,
+          class _BidirectionalIterator,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator reverse_copy(
+    _ExecutionPolicy&& __policy,
+    _BidirectionalIterator __first,
+    _BidirectionalIterator __last,
+    _ForwardIterator __result) {
+  _LIBCPP_REQUIRE_CPP17_BIDIRECTIONAL_ITERATOR(_BidirectionalIterator, "reverse_copy requires a BidirectionalIterator");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reverse_copy requires a ForwardIterator");
+  using _Implementation = __pstl::__dispatch<__pstl::__reverse_copy, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__result));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator,
           class _Predicate,

diff  --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 1ea121367fbc7d..a84a768cad5707 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -166,6 +166,13 @@ struct __generate_n;
 // optional<__empty>
 // operator()(_Policy&&, _ForwardIterator __first, _Size __n, _Generator __gen) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __reverse_copy;
+// template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
+// optional<_ForwardIterator>
+// operator()(_Policy&&, _BidirectionalIterator __first, _BidirectionalIterator __last,
+//                       _ForwardIterator __result) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __merge;
 // template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Comp>

diff  --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index cb963a130f7432..5c78d4efa412b9 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -23,6 +23,7 @@
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
+#include <__iterator/reverse_iterator.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
 #include <__type_traits/desugars_to.h>
@@ -96,6 +97,7 @@ namespace __pstl {
 // - move
 // - copy
 // - copy_n
+// - reverse_copy
 // - rotate_copy
 //
 
@@ -557,6 +559,22 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __reverse_copy<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _BidirectionalIterator, class _ForwardIterator>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+  operator()(_Policy&& __policy,
+             _BidirectionalIterator __first,
+             _BidirectionalIterator __last,
+             _ForwardIterator __result) const noexcept {
+    using _Copy = __dispatch<__copy, __current_configuration, _ExecutionPolicy>;
+    return _Copy()(__policy,
+                   std::reverse_iterator<_BidirectionalIterator>(std::move(__last)),
+                   std::reverse_iterator<_BidirectionalIterator>(std::move(__first)),
+                   std::move(__result));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 

diff  --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index aee3f97019071d..fbe3460b67f174 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -31,12 +31,16 @@
 
 #include "test_iterators.h"
 
-using non_forward_iterator = cpp17_input_iterator<int*>;
+using non_forward_iterator       = cpp17_input_iterator<int*>;
+using non_bidirectional_iterator = forward_iterator<int*>;
 struct non_output_iterator : forward_iterator<int*> {
   constexpr int const& operator*() const; // prevent it from being an output iterator
 };
 
-void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execution::sequenced_policy pol) {
+void f(non_forward_iterator non_fwd,
+       non_output_iterator non_output,
+       non_bidirectional_iterator non_bidir,
+       std::execution::sequenced_policy pol) {
   auto pred     = [](auto&&...) -> bool { return true; };
   auto func     = [](auto&&...) -> int { return 1; };
   int* it       = nullptr;
@@ -110,6 +114,12 @@ void f(non_forward_iterator non_fwd, non_output_iterator non_output, std::execut
     (void)std::generate_n(pol, non_fwd, n, func);     // expected-error@*:* {{static assertion failed: generate_n}}
   }
 
+  {
+    (void)std::reverse_copy(
+        pol, non_bidir, non_bidir, it);            // expected-error@*:* {{static assertion failed: reverse_copy}}
+    (void)std::reverse_copy(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: reverse_copy}}
+  }
+
   {
     (void)std::is_partitioned(
         pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: is_partitioned}}

diff  --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 747d62bc668379..049c8dc88c2da2 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -56,4 +56,6 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
 }

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
new file mode 100644
index 00000000000000..d81dde163cf023
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.reverse/pstl.reverse_copy.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class BidirectionalIterator,
+//           class ForwardIterator>
+//   ForwardIterator reverse_copy(ExecutionPolicy&& exec,
+//                                BidirectionalIterator first,
+//                                BidirectionalIterator last,
+//                                ForwardIterator result);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(reverse_copy);
+
+static_assert(sfinae_test_reverse_copy<int, int*, int*, int*>);
+static_assert(!sfinae_test_reverse_copy<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[]              = {0};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      assert(ib[0] == -1);
+      r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(ia[0] == 0);
+    }
+    {
+      int ia[]              = {0, 1};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 1);
+      assert(ib[1] == 0);
+    }
+    {
+      int ia[]              = {0, 1, 2};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 2);
+      assert(ib[1] == 1);
+      assert(ib[2] == 0);
+    }
+    {
+      int ia[]              = {0, 1, 2, 3};
+      int ib[std::size(ia)] = {-1};
+      Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      assert(ib[0] == 3);
+      assert(ib[1] == 2);
+      assert(ib[2] == 1);
+      assert(ib[3] == 0);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia) + 1, [&](size_t i) {
+        Iter2 r = std::reverse_copy(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j) {
+          assert(ib[j] == static_cast<int>(i - j));
+        }
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::bidirectional_iterator_list<int*>, types::bidirectional_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+}

diff  --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 9e6c8f5b8ce409..8af2a0a15fd5cc 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -210,6 +210,13 @@ int main(int, char**) {
         assert_non_throwing([=, &policy] { (void)std::generate_n(policy, std::move(first1), n, gen); });
       }
 
+      {
+        // reverse_copy(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::reverse_copy(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+      }
+
       {
         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
 


        

From libcxx-commits at lists.llvm.org  Sat Jul 11 16:30:05 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 11 Jul 2026 16:30:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::reverse_copy (PR #208085)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208085@github.com>
Message-ID: <6a52d1fd.13be6df8.145c.7cad@mx.google.com>


https://github.com/frederick-vs-ja closed https://github.com/llvm/llvm-project/pull/208085

From libcxx-commits at lists.llvm.org  Sat Jul 11 16:36:24 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Sat, 11 Jul 2026 16:36:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::reverse_copy (PR #208085)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208085@github.com>
Message-ID: <6a52d378.ff037943.2e959a.199d@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `openmp-offload-amdgpu-runtime-2` running on `rocm-worker-hw-02` while building `libcxx` at step 3 "annotate".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/10/builds/31671

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 3 (annotate) failure: 'python ../llvm.src/offload/ci/openmp-offload-amdgpu-libc-runtime.py ...' (failure)
...
PASS: libomp :: ompt/synchronization/reduction/tree_reduce.c (219 of 481)
PASS: libomp :: tasking/bug_serial_taskgroup.c (220 of 481)
PASS: libomp :: parallel/bug63197.c (221 of 481)
PASS: libomp :: ompt/synchronization/reduction/empty_reduce.c (222 of 481)
PASS: libomp :: ompt/tasks/taskloop.c (223 of 481)
PASS: libomp :: parallel/omp_parallel_shared.c (224 of 481)
PASS: libomp :: parallel/omp_parallel_copyin_combined.c (225 of 481)
PASS: libomp :: parallel/omp_parallel_num_threads_strict.c (226 of 481)
PASS: libomp :: parallel/bug54082.c (227 of 481)
PASS: libomp :: target/issue-81488.c (228 of 481)
FAIL: libarcher :: races/lock-nested-unrelated.c (229 of 481)
******************** TEST 'libarcher :: races/lock-nested-unrelated.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 13
/home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/clang -fopenmp  -gdwarf-4 -O1 -fsanitize=thread  -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src   /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c -o /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp -latomic && env TSAN_OPTIONS='ignore_noninstrumented_modules=0:ignore_noninstrumented_modules=1' /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/deflake.bash /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp 2>&1 | tee /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp.log | /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/FileCheck /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c
# executed command: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/clang -fopenmp -gdwarf-4 -O1 -fsanitize=thread -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c -o /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp -latomic
# note: command had no output on stdout or stderr
# executed command: env TSAN_OPTIONS=ignore_noninstrumented_modules=0:ignore_noninstrumented_modules=1 /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/deflake.bash /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp
# note: command had no output on stdout or stderr
# executed command: tee /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp.log
# note: command had no output on stdout or stderr
# executed command: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/FileCheck /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c
# note: command had no output on stdout or stderr
# RUN: at line 14
/home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/clang -fopenmp  -gdwarf-4 -O1 -fsanitize=thread  -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src   /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c -o /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp -latomic && env ARCHER_OPTIONS="ignore_serial=1 report_data_leak=1" env TSAN_OPTIONS='ignore_noninstrumented_modules=0:ignore_noninstrumented_modules=1' /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/deflake.bash /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp 2>&1 | tee /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp.log | /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/FileCheck /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c
# executed command: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/clang -fopenmp -gdwarf-4 -O1 -fsanitize=thread -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c -o /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp -latomic
# note: command had no output on stdout or stderr
# executed command: env 'ARCHER_OPTIONS=ignore_serial=1 report_data_leak=1' env TSAN_OPTIONS=ignore_noninstrumented_modules=0:ignore_noninstrumented_modules=1 /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/deflake.bash /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp
# note: command had no output on stdout or stderr
# executed command: tee /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp.log
# note: command had no output on stdout or stderr
# executed command: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/FileCheck /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c
# .---command stderr------------
# | /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c:47:11: error: CHECK: expected string not found in input
# | // CHECK: ThreadSanitizer: reported {{[1-7]}} warnings
# |           ^
# | <stdin>:27:5: note: scanning from here
# | DONE
# |     ^
# | <stdin>:28:1: note: possible intended match here
# | ThreadSanitizer: thread T4 finished with ignores enabled, created at:
# | ^
# | 
# | Input file: <stdin>
# | Check file: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c
# | 
Step 8 (run check-openmp) failure: run check-openmp (failure)
...
PASS: libomp :: ompt/synchronization/reduction/tree_reduce.c (219 of 481)
PASS: libomp :: tasking/bug_serial_taskgroup.c (220 of 481)
PASS: libomp :: parallel/bug63197.c (221 of 481)
PASS: libomp :: ompt/synchronization/reduction/empty_reduce.c (222 of 481)
PASS: libomp :: ompt/tasks/taskloop.c (223 of 481)
PASS: libomp :: parallel/omp_parallel_shared.c (224 of 481)
PASS: libomp :: parallel/omp_parallel_copyin_combined.c (225 of 481)
PASS: libomp :: parallel/omp_parallel_num_threads_strict.c (226 of 481)
PASS: libomp :: parallel/bug54082.c (227 of 481)
PASS: libomp :: target/issue-81488.c (228 of 481)
FAIL: libarcher :: races/lock-nested-unrelated.c (229 of 481)
******************** TEST 'libarcher :: races/lock-nested-unrelated.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 13
/home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/clang -fopenmp  -gdwarf-4 -O1 -fsanitize=thread  -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src   /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c -o /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp -latomic && env TSAN_OPTIONS='ignore_noninstrumented_modules=0:ignore_noninstrumented_modules=1' /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/deflake.bash /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp 2>&1 | tee /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp.log | /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/FileCheck /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c
# executed command: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/clang -fopenmp -gdwarf-4 -O1 -fsanitize=thread -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c -o /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp -latomic
# note: command had no output on stdout or stderr
# executed command: env TSAN_OPTIONS=ignore_noninstrumented_modules=0:ignore_noninstrumented_modules=1 /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/deflake.bash /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp
# note: command had no output on stdout or stderr
# executed command: tee /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp.log
# note: command had no output on stdout or stderr
# executed command: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/FileCheck /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c
# note: command had no output on stdout or stderr
# RUN: at line 14
/home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/clang -fopenmp  -gdwarf-4 -O1 -fsanitize=thread  -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src   /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c -o /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp -latomic && env ARCHER_OPTIONS="ignore_serial=1 report_data_leak=1" env TSAN_OPTIONS='ignore_noninstrumented_modules=0:ignore_noninstrumented_modules=1' /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/deflake.bash /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp 2>&1 | tee /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp.log | /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/FileCheck /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c
# executed command: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/clang -fopenmp -gdwarf-4 -O1 -fsanitize=thread -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests -I /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/runtime/src /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c -o /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp -latomic
# note: command had no output on stdout or stderr
# executed command: env 'ARCHER_OPTIONS=ignore_serial=1 report_data_leak=1' env TSAN_OPTIONS=ignore_noninstrumented_modules=0:ignore_noninstrumented_modules=1 /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/deflake.bash /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp
# note: command had no output on stdout or stderr
# executed command: tee /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/runtimes/runtimes-bins/openmp/tools/archer/tests/races/Output/lock-nested-unrelated.c.tmp.log
# note: command had no output on stdout or stderr
# executed command: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/build/llvm.build/./bin/FileCheck /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c
# .---command stderr------------
# | /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c:47:11: error: CHECK: expected string not found in input
# | // CHECK: ThreadSanitizer: reported {{[1-7]}} warnings
# |           ^
# | <stdin>:27:5: note: scanning from here
# | DONE
# |     ^
# | <stdin>:28:1: note: possible intended match here
# | ThreadSanitizer: thread T4 finished with ignores enabled, created at:
# | ^
# | 
# | Input file: <stdin>
# | Check file: /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/openmp/tools/archer/tests/races/lock-nested-unrelated.c
# | 

```

</details>

https://github.com/llvm/llvm-project/pull/208085

From libcxx-commits at lists.llvm.org  Sat Jul 11 17:43:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 11 Jul 2026 17:43:00 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [libcxx]support freestanding
	libc++ (PR #208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a52e314.e55dc341.1f19bd.8d6f@mx.google.com>


https://github.com/trcrsired converted_to_draft https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 05:40:37 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 12 Jul 2026 05:40:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a538b45.07d7ca98.9dee3.d821@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/207585

>From 02ae8f53a83139d9280f86098a7517a3036c7986 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 5 Jul 2026 14:17:28 +0100
Subject: [PATCH 1/5] Implementation of adjacent_difference on top of a binary
 transform

---
 libcxx/include/__numeric/pstl.h               |  39 ++++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  35 +++-
 .../pstl.iterator-requirements.verify.cpp     |  11 ++
 .../algorithms/pstl.nodiscard.verify.cpp      |   5 +
 .../pstl.exception_handling.pass.cpp          |  16 +-
 .../pstl.adjacent_difference.pass.cpp         | 171 +++++++++++++++++
 .../pstl.adjacent_difference_op.pass.cpp      | 181 ++++++++++++++++++
 8 files changed, 461 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp

diff --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h
index ecbc9f5348333..0b85cfb8c2327 100644
--- a/libcxx/include/__numeric/pstl.h
+++ b/libcxx/include/__numeric/pstl.h
@@ -165,6 +165,45 @@ template <class _ExecutionPolicy,
       std::move(__transform));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first1), std::move(__last1), std::move(__first2), minus{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryOperation,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _BinaryOperation __op) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__op));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a84a768cad570..2ba5b4434fc33 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -317,6 +317,13 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_difference;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+// optional<_ForwardIterator2>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5c78d4efa412b..f34c284dd2a84 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -92,11 +92,12 @@ namespace __pstl {
 //
 // transform and transform_binary family
 // -------------------------------------
-// - replace_copy_if
-// - replace_copy
-// - move
+// - adjacent_difference
 // - copy
 // - copy_n
+// - move
+// - replace_copy
+// - replace_copy_if
 // - reverse_copy
 // - rotate_copy
 //
@@ -575,6 +576,34 @@ struct __reverse_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _BinaryOperation&& __op) const noexcept {
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    if (__first1 == __last1)
+      return __first2; // edge case: empty input range, just return the output iterator
+    *__first2 = *__first1;
+    ++__first2;
+    _ForwardIterator1 __first_left = std::next(__first1);
+    if (__first_left == __last1)
+      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+    // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
+    return _TransformBinary()(
+        __policy,
+        std::move(__first_left),
+        std::move(__last1),
+        std::move(__first1),
+        std::move(__first2),
+        std::forward<_BinaryOperation>(__op));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index fbe3460b67f17..402d8fbf9131a 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -211,4 +211,15 @@ void f(non_forward_iterator non_fwd,
     (void)std::transform_reduce(
         pol, non_fwd, non_fwd, val, func, func); // expected-error@*:* {{static assertion failed: transform_reduce}}
   }
+
+  {
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+  }
 }
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 049c8dc88c2da..f4fffd456a331 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <algorithm>
 #include <execution>
 #include <iterator>
+#include <numeric>
 
 void test() {
   int a[]    = {1};
@@ -58,4 +59,8 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
 }
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 8af2a0a15fd5c..e00046fdb1dd6 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -85,7 +85,7 @@ int main(int, char**) {
       auto last1       = util::throw_on_move_iterator(std::end(a), tokens[1].active() ? 1 : -1);
       auto first2      = util::throw_on_move_iterator(std::begin(b), tokens[2].active() ? 1 : -1);
       auto last2       = util::throw_on_move_iterator(std::end(b), tokens[3].active() ? 1 : -1);
-      auto dest        = util::throw_on_move_iterator(std::end(storage), tokens[4].active() ? 1 : -1);
+      auto dest        = util::throw_on_move_iterator(std::begin(storage), tokens[4].active() ? 1 : -1);
       auto maybe_throw = [](ThrowToken const& token, auto f) {
         return [&token, f](auto... args) {
           if (token.active())
@@ -365,6 +365,20 @@ int main(int, char**) {
           (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction);
         });
       }
+
+      {
+        auto op = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
+
+        // adjacent_difference(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+
+        // adjacent_difference(first, last, dest, op)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest), op);
+        });
+      }
     }
   });
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
new file mode 100644
index 0000000000000..bb98004c1c62a
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+
+  friend X operator-(const X& x, const X& y) { return X(x.i_ - y.i_); }
+
+  friend class Y;
+};
+
+class Y {
+  int i_;
+
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 20};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, -5, -4, -3, -2};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r =
+            std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        assert(std::all_of(std::begin(ib), std::begin(ib) + i, [](int x) { return x == 1; }));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(7)};
+      Y ir[3] = {Y(1), Y(4), Y(2)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
new file mode 100644
index 0000000000000..26d65f9a77c10
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryOperation>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2,
+//                                        BinaryOperation op);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+  friend class Y;
+  friend struct Op;
+};
+
+class Y {
+  int i_;
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+// ensures that the predicate is not equivalent to std::minus
+struct Op {
+  int operator()(int l, int r) const { return l * 2 - r; }
+  X operator()(const X& l, const X& r) const { return X(l.i_ * 2 - r.i_); }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 50};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, 5, 2, 0, -1};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r = std::adjacent_difference(
+            policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)), Op{});
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j)
+          assert(ib[j] == (int)(j == 0 ? 1 : j + 2));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(8)};
+      Y ir[3] = {Y(1), Y(9), Y(11)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}

>From f62e25b47970412a33d6c44c4b4820ad3669b680 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 10:34:09 +0100
Subject: [PATCH 2/5] Renamed the variable names in __adjacent_difference

---
 libcxx/include/__pstl/backends/default.h | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index f34c284dd2a84..28bc75be147aa 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -583,23 +583,23 @@ struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
   operator()(_Policy&& __policy,
              _ForwardIterator1 __first1,
              _ForwardIterator1 __last1,
-             _ForwardIterator2 __first2,
+             _ForwardIterator2 __result,
              _BinaryOperation&& __op) const noexcept {
     using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
     if (__first1 == __last1)
-      return __first2; // edge case: empty input range, just return the output iterator
-    *__first2 = *__first1;
-    ++__first2;
-    _ForwardIterator1 __first_left = std::next(__first1);
-    if (__first_left == __last1)
-      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+      return __result; // edge case: empty input range, just return the output iterator
+    *__result = *__first1;
+    ++__result;
+    _ForwardIterator1 __first2 = std::next(__first1);
+    if (__first2 == __last1)
+      return __result; // edge case: not enough elements to perform adjacent difference, just return the output iterator
     // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
     return _TransformBinary()(
         __policy,
-        std::move(__first_left),
+        std::move(__first2),
         std::move(__last1),
         std::move(__first1),
-        std::move(__first2),
+        std::move(__result),
         std::forward<_BinaryOperation>(__op));
   }
 };

>From 012594a84a7b0bbc55c1e9457adf28531d01c97d Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 20:01:44 +0100
Subject: [PATCH 3/5] Moved runway_sample() to test/support/runway_sample.h

---
 .../pstl.adjacent_difference.pass.cpp         |  16 +--
 .../pstl.adjacent_difference_op.pass.cpp      |  16 +--
 libcxx/test/support/runway_sample.h           |  58 ++++++++++
 .../test.support/runway_sample.pass.cpp       | 101 ++++++++++++++++++
 4 files changed, 161 insertions(+), 30 deletions(-)
 create mode 100644 libcxx/test/support/runway_sample.h
 create mode 100644 libcxx/test/support/test.support/runway_sample.pass.cpp

diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
index bb98004c1c62a..4dfe3ad58adf8 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -29,27 +29,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
 
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 class X {
   int i_;
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
index 26d65f9a77c10..f6b9b480f4ba8 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -31,27 +31,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
 
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 class X {
   int i_;
   X& operator=(const X&);
diff --git a/libcxx/test/support/runway_sample.h b/libcxx/test/support/runway_sample.h
new file mode 100644
index 0000000000000..16ecd58cef747
--- /dev/null
+++ b/libcxx/test/support/runway_sample.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+#ifndef TEST_SUPPORT_RUNWAY_SAMPLE_H
+#define TEST_SUPPORT_RUNWAY_SAMPLE_H
+
+#include <cstddef>
+#include <algorithm>
+#include "test_macros.h"
+
+// runway_sample(size, callable) calls callable(i) for a set of sample indices i in the range [0, size).
+// they are chosen in 3 phases:
+//   - prefix, sampled densely with increments of 1
+//   - middle, sampled sparsely with uncanny numbers
+//   - suffix, sampled densely with increments of 1
+//
+// Examples of sample indices for various sizes:
+// size = 0:
+// <no samples>
+// size = 1:
+// 0
+// size = 10:
+// 0 1 2 3 4 5 6 7 8 9
+// size = 100:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
+// size = 1000:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 157 493 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
+template <class Callable>
+TEST_CONSTEXPR_CXX14 void runway_sample(std::size_t size, Callable callable) {
+  constexpr std::size_t affix = 16;
+  std::size_t i               = 0;
+
+  // 0, 1, 2, ..., 15
+  for (; i < std::min(size, affix); ++i) {
+    callable(i);
+  }
+
+  if (size <= affix) {
+    return;
+  }
+
+  // 16, 50, 157, 493, 1549, 4868, ...
+  for (std::size_t j = i; j + affix < size; i = j, j = j * 3 + j / 7) {
+    callable(j);
+  }
+
+  // size - 16, size - 15, ..., size - 1
+  i = std::max(i, size - affix);
+  for (; i < size; ++i) {
+    callable(i);
+  }
+}
+
+#endif // TEST_SUPPORT_RUNWAY_SAMPLE_H
diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
new file mode 100644
index 0000000000000..9fc288f881082
--- /dev/null
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++11
+
+// "support/runway_sample.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "runway_sample.h"
+
+TEST_CONSTEXPR_CXX14 bool test() {
+  {
+    bool fail = false;
+    runway_sample(0, [&](size_t /*i*/) { fail = true; });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(0, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(10, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29};
+    size_t n  = 0;
+    bool fail = false;
+    runway_sample(30, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
+                         50, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(100, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  50,
+                         157, 493, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(1000, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  return true;
+}
+
+int main() {
+  assert(test());
+#if TEST_STD_VER >= 14
+  static_assert(test());
+#endif
+  return 0;
+}

>From 1552e8b1db839c7356ba1679fda4bdfc669dd685 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 20:05:51 +0100
Subject: [PATCH 4/5] Added a note about X and Y in the tests

---
 .../adjacent.difference/pstl.adjacent_difference.pass.cpp     | 4 ++++
 .../adjacent.difference/pstl.adjacent_difference_op.pass.cpp  | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
index 4dfe3ad58adf8..d06f5b64e2f9f 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -36,6 +36,10 @@ EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
 
+// Types X and Y are provided to test adjacent_difference() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
 class X {
   int i_;
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
index f6b9b480f4ba8..d623cda058d04 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -38,6 +38,10 @@ EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
 
+// Types X and Y are provided to test adjacent_difference() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
 class X {
   int i_;
   X& operator=(const X&);

>From 40282b01dd8f83ff30534284abe9deddbf0e4136 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 21:06:40 +0100
Subject: [PATCH 5/5] Run runway_sample test on C++17+

---
 libcxx/test/support/test.support/runway_sample.pass.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
index 9fc288f881082..30e0ad20e0f85 100644
--- a/libcxx/test/support/test.support/runway_sample.pass.cpp
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// REQUIRES: std-at-least-c++11
+// REQUIRES: std-at-least-c++17
 
 // "support/runway_sample.h"
 
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "runway_sample.h"
 
-TEST_CONSTEXPR_CXX14 bool test() {
+constexpr bool test() {
   {
     bool fail = false;
     runway_sample(0, [&](size_t /*i*/) { fail = true; });
@@ -94,8 +94,6 @@ TEST_CONSTEXPR_CXX14 bool test() {
 
 int main() {
   assert(test());
-#if TEST_STD_VER >= 14
   static_assert(test());
-#endif
   return 0;
 }


From libcxx-commits at lists.llvm.org  Sun Jul 12 05:47:33 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 12 Jul 2026 05:47:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]
 __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp,
 _Tp> removes cv-ref from _Tp (PR #208950)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208950@github.com>
Message-ID: <6a538ce5.bc28b987.90005.27af@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/208950

>From de4502a3bac34eedba2d5687e67bb6be577e51e7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 21:19:44 +0100
Subject: [PATCH 1/2] __desugars_to_v<__totally_ordered_less_tag, __less<>,...>
 strips cv-ref from _Tp

---
 libcxx/include/__algorithm/comp.h           |  4 ++-
 libcxx/test/libcxx/algorithms/comp.pass.cpp | 27 +++++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/libcxx/algorithms/comp.pass.cpp

diff --git a/libcxx/include/__algorithm/comp.h b/libcxx/include/__algorithm/comp.h
index 38e2fb9f5e744..a83b6a8938998 100644
--- a/libcxx/include/__algorithm/comp.h
+++ b/libcxx/include/__algorithm/comp.h
@@ -13,6 +13,7 @@
 #include <__type_traits/desugars_to.h>
 #include <__type_traits/is_generic_transparent_comparator.h>
 #include <__type_traits/is_integral.h>
+#include <__type_traits/remove_cvref.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -47,7 +48,8 @@ template <class _Tp>
 inline const bool __desugars_to_v<__less_tag, __less<>, _Tp, _Tp> = true;
 
 template <class _Tp>
-inline const bool __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp, _Tp> = is_integral<_Tp>::value;
+inline const bool __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp, _Tp> =
+    is_integral<__remove_cvref_t<_Tp> >::value;
 
 template <>
 inline const bool __is_generic_transparent_comparator_v<__less<> > = true;
diff --git a/libcxx/test/libcxx/algorithms/comp.pass.cpp b/libcxx/test/libcxx/algorithms/comp.pass.cpp
new file mode 100644
index 0000000000000..87095f630ee52
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/comp.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// <__algorithm/comp.h>
+
+#include <__algorithm/comp.h>
+
+// check that __less<> desugars to __totally_ordered_less_tag for integral types regardless of their cv-ref
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int, int>);
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int&, int&>);
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, const int, const int>);
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, const int&, const int&>);
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile int&, volatile int&>);
+static_assert(
+    std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile const int&, volatile const int&>);
+static_assert(
+    !std::
+        __desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile const float&, volatile const float&>);
+
+int main(int, char**) { return 0; }

>From dca57eead03572af638250c92ceddb9a055f9df8 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 12 Jul 2026 13:47:14 +0100
Subject: [PATCH 2/2] Include <algorithm> instead of an individual header

---
 libcxx/test/libcxx/algorithms/comp.pass.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/algorithms/comp.pass.cpp b/libcxx/test/libcxx/algorithms/comp.pass.cpp
index 87095f630ee52..0e3b813aedca3 100644
--- a/libcxx/test/libcxx/algorithms/comp.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/comp.pass.cpp
@@ -8,9 +8,9 @@
 
 // REQUIRES: std-at-least-c++17
 
-// <__algorithm/comp.h>
+// <algorithm>
 
-#include <__algorithm/comp.h>
+#include <algorithm>
 
 // check that __less<> desugars to __totally_ordered_less_tag for integral types regardless of their cv-ref
 static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int, int>);


From libcxx-commits at lists.llvm.org  Sun Jul 12 06:33:46 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 06:33:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
Message-ID: <llvm.org/llvm/llvm-project/pull/209018@github.com>


https://github.com/emmett2020 created https://github.com/llvm/llvm-project/pull/209018

Implement LWG 4144 by explicitly rejecting `unique_ptr<T, D>` when `T*` is not a valid type.

This adds a pointability check and tests reference and abominable function types. It also includes a minor formatting cleanup for `unique_ptr`'s `nullptr` comparison operator.

Fixes #118359

>From f556eb0f8e4bcf5ead6bfcc555dfb78c28421674 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Sun, 12 Jul 2026 21:31:47 +0800
Subject: [PATCH] [libcxx][unique_ptr] Implement LWG 4144: Disallow
 unique_ptr<const T, D>

---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__memory/unique_ptr.h          | 13 +++++-
 .../non_pointable.single.verify.cpp           | 42 +++++++++++++++++++
 3 files changed, 54 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 8441da10d78de..c203fa424556a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -101,7 +101,7 @@
 "`LWG4140 <https://wg21.link/LWG4140>`__","Useless default constructors for bit reference types","2024-11 (Wrocław)","|Complete|","","`#118356 <https://github.com/llvm/llvm-project/issues/118356>`__",""
 "`LWG4141 <https://wg21.link/LWG4141>`__","Improve prohibitions on ""additional storage""","2024-11 (Wrocław)","","","`#118357 <https://github.com/llvm/llvm-project/issues/118357>`__",""
 "`LWG4142 <https://wg21.link/LWG4142>`__","``format_parse_context::check_dynamic_spec`` should require at least one type","2024-11 (Wrocław)","","","`#118358 <https://github.com/llvm/llvm-project/issues/118358>`__",""
-"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","","","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
+"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","|Complete|","23","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
 "`LWG4147 <https://wg21.link/LWG4147>`__","Precondition on ``inplace_vector::emplace``","2024-11 (Wrocław)","","","`#118361 <https://github.com/llvm/llvm-project/issues/118361>`__",""
 "`LWG4148 <https://wg21.link/LWG4148>`__","``unique_ptr::operator*`` should not allow dangling references","2024-11 (Wrocław)","","","`#118362 <https://github.com/llvm/llvm-project/issues/118362>`__",""
 "`LWG4153 <https://wg21.link/LWG4153>`__","Fix extra ""-1"" for ``philox_engine::max()``","2024-11 (Wrocław)","","","`#118363 <https://github.com/llvm/llvm-project/issues/118363>`__",""
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 7e68faa15dabb..e8a9a962fba9a 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -43,6 +43,7 @@
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_extent.h>
 #include <__type_traits/remove_reference.h>
+#include <__type_traits/void_t.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -100,6 +101,12 @@ inline const bool __can_dereference = false;
 template <class _Tp>
 inline const bool __can_dereference<_Tp, decltype((void)*std::declval<_Tp>())> = true;
 
+template <class _Tp, class = void>
+inline const bool __is_pointable = false;
+
+template <class _Tp>
+inline const bool __is_pointable<_Tp, __void_t<_Tp*> > = true;
+
 #if defined(_LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI)
 #  define _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI __attribute__((__trivial_abi__))
 #else
@@ -111,6 +118,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
+
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
   using pointer _LIBCPP_NODEBUG = __pointer<_Tp, deleter_type>;
 
   static_assert(!is_rvalue_reference<deleter_type>::value, "the specified deleter type cannot be an rvalue reference");
@@ -619,8 +628,8 @@ operator<=>(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {
 #endif
 
 template <class _T1, class _D1>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool
-operator==(const unique_ptr<_T1, _D1>& __x, nullptr_t) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX23 bool operator==(const unique_ptr<_T1, _D1>& __x, nullptr_t) _NOEXCEPT {
   return !__x;
 }
 
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
new file mode 100644
index 0000000000000..22737598ef0b3
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() {
+  (void)sizeof(std::unique_ptr<Function, Deleter>);
+}
+
+void reference_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<int&, Deleter>);
+}
+
+void abominable_function_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
+}


From libcxx-commits at lists.llvm.org  Sun Jul 12 06:34:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 06:34:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a5397c8.6365b89b.8575e.9175@mx.google.com>


https://github.com/emmett2020 edited https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 06:34:30 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 06:34:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a5397e6.d76d81af.39f9be.6c09@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Emmett (emmett2020)

<details>
<summary>Changes</summary>

Implement LWG 4144 by explicitly rejecting `unique_ptr&lt;T, D&gt;` when `T*` is not a valid type.

This adds a pointability check and tests reference and abominable function types. It also includes a minor formatting cleanup for `unique_ptr`'s `nullptr` comparison operator.

Fixes #<!-- -->118359

---
Full diff: https://github.com/llvm/llvm-project/pull/209018.diff


3 Files Affected:

- (modified) libcxx/docs/Status/Cxx26Issues.csv (+1-1) 
- (modified) libcxx/include/__memory/unique_ptr.h (+11-2) 
- (added) libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp (+42) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 8441da10d78de..c203fa424556a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -101,7 +101,7 @@
 "`LWG4140 <https://wg21.link/LWG4140>`__","Useless default constructors for bit reference types","2024-11 (Wrocław)","|Complete|","","`#118356 <https://github.com/llvm/llvm-project/issues/118356>`__",""
 "`LWG4141 <https://wg21.link/LWG4141>`__","Improve prohibitions on ""additional storage""","2024-11 (Wrocław)","","","`#118357 <https://github.com/llvm/llvm-project/issues/118357>`__",""
 "`LWG4142 <https://wg21.link/LWG4142>`__","``format_parse_context::check_dynamic_spec`` should require at least one type","2024-11 (Wrocław)","","","`#118358 <https://github.com/llvm/llvm-project/issues/118358>`__",""
-"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","","","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
+"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","|Complete|","23","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
 "`LWG4147 <https://wg21.link/LWG4147>`__","Precondition on ``inplace_vector::emplace``","2024-11 (Wrocław)","","","`#118361 <https://github.com/llvm/llvm-project/issues/118361>`__",""
 "`LWG4148 <https://wg21.link/LWG4148>`__","``unique_ptr::operator*`` should not allow dangling references","2024-11 (Wrocław)","","","`#118362 <https://github.com/llvm/llvm-project/issues/118362>`__",""
 "`LWG4153 <https://wg21.link/LWG4153>`__","Fix extra ""-1"" for ``philox_engine::max()``","2024-11 (Wrocław)","","","`#118363 <https://github.com/llvm/llvm-project/issues/118363>`__",""
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 7e68faa15dabb..e8a9a962fba9a 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -43,6 +43,7 @@
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_extent.h>
 #include <__type_traits/remove_reference.h>
+#include <__type_traits/void_t.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -100,6 +101,12 @@ inline const bool __can_dereference = false;
 template <class _Tp>
 inline const bool __can_dereference<_Tp, decltype((void)*std::declval<_Tp>())> = true;
 
+template <class _Tp, class = void>
+inline const bool __is_pointable = false;
+
+template <class _Tp>
+inline const bool __is_pointable<_Tp, __void_t<_Tp*> > = true;
+
 #if defined(_LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI)
 #  define _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI __attribute__((__trivial_abi__))
 #else
@@ -111,6 +118,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
+
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
   using pointer _LIBCPP_NODEBUG = __pointer<_Tp, deleter_type>;
 
   static_assert(!is_rvalue_reference<deleter_type>::value, "the specified deleter type cannot be an rvalue reference");
@@ -619,8 +628,8 @@ operator<=>(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {
 #endif
 
 template <class _T1, class _D1>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool
-operator==(const unique_ptr<_T1, _D1>& __x, nullptr_t) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX23 bool operator==(const unique_ptr<_T1, _D1>& __x, nullptr_t) _NOEXCEPT {
   return !__x;
 }
 
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
new file mode 100644
index 0000000000000..22737598ef0b3
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() {
+  (void)sizeof(std::unique_ptr<Function, Deleter>);
+}
+
+void reference_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<int&, Deleter>);
+}
+
+void abominable_function_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 06:35:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 06:35:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a53980e.6365b89b.8575e.ad5e@mx.google.com>


https://github.com/emmett2020 edited https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 06:35:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 06:35:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a53981e.824e11a6.f5f27.9142@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions h,cpp -- libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp libcxx/include/__memory/unique_ptr.h --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
index 22737598e..148296298 100644
--- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -27,9 +27,7 @@ struct Deleter {
 typedef void Function();
 typedef void AbominableFunction() const;
 
-void pointable_function_type() {
-  (void)sizeof(std::unique_ptr<Function, Deleter>);
-}
+void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
 
 void reference_type() {
   // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}

``````````

</details>


https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 06:38:12 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 06:38:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a5398c4.1905e68e.18ad68.1403@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/209018

>From 84ac8569141037b772ecfb0d82f67a147d849d4a Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Sun, 12 Jul 2026 21:31:47 +0800
Subject: [PATCH] [libcxx][unique_ptr] Implement LWG 4144: Disallow
 unique_ptr<T&, D>

---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__memory/unique_ptr.h          | 13 +++++-
 .../non_pointable.single.verify.cpp           | 40 +++++++++++++++++++
 3 files changed, 52 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 8441da10d78de..c203fa424556a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -101,7 +101,7 @@
 "`LWG4140 <https://wg21.link/LWG4140>`__","Useless default constructors for bit reference types","2024-11 (Wrocław)","|Complete|","","`#118356 <https://github.com/llvm/llvm-project/issues/118356>`__",""
 "`LWG4141 <https://wg21.link/LWG4141>`__","Improve prohibitions on ""additional storage""","2024-11 (Wrocław)","","","`#118357 <https://github.com/llvm/llvm-project/issues/118357>`__",""
 "`LWG4142 <https://wg21.link/LWG4142>`__","``format_parse_context::check_dynamic_spec`` should require at least one type","2024-11 (Wrocław)","","","`#118358 <https://github.com/llvm/llvm-project/issues/118358>`__",""
-"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","","","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
+"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","|Complete|","23","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
 "`LWG4147 <https://wg21.link/LWG4147>`__","Precondition on ``inplace_vector::emplace``","2024-11 (Wrocław)","","","`#118361 <https://github.com/llvm/llvm-project/issues/118361>`__",""
 "`LWG4148 <https://wg21.link/LWG4148>`__","``unique_ptr::operator*`` should not allow dangling references","2024-11 (Wrocław)","","","`#118362 <https://github.com/llvm/llvm-project/issues/118362>`__",""
 "`LWG4153 <https://wg21.link/LWG4153>`__","Fix extra ""-1"" for ``philox_engine::max()``","2024-11 (Wrocław)","","","`#118363 <https://github.com/llvm/llvm-project/issues/118363>`__",""
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 7e68faa15dabb..e8a9a962fba9a 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -43,6 +43,7 @@
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_extent.h>
 #include <__type_traits/remove_reference.h>
+#include <__type_traits/void_t.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -100,6 +101,12 @@ inline const bool __can_dereference = false;
 template <class _Tp>
 inline const bool __can_dereference<_Tp, decltype((void)*std::declval<_Tp>())> = true;
 
+template <class _Tp, class = void>
+inline const bool __is_pointable = false;
+
+template <class _Tp>
+inline const bool __is_pointable<_Tp, __void_t<_Tp*> > = true;
+
 #if defined(_LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI)
 #  define _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI __attribute__((__trivial_abi__))
 #else
@@ -111,6 +118,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
+
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
   using pointer _LIBCPP_NODEBUG = __pointer<_Tp, deleter_type>;
 
   static_assert(!is_rvalue_reference<deleter_type>::value, "the specified deleter type cannot be an rvalue reference");
@@ -619,8 +628,8 @@ operator<=>(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {
 #endif
 
 template <class _T1, class _D1>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool
-operator==(const unique_ptr<_T1, _D1>& __x, nullptr_t) _NOEXCEPT {
+inline _LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX23 bool operator==(const unique_ptr<_T1, _D1>& __x, nullptr_t) _NOEXCEPT {
   return !__x;
 }
 
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
new file mode 100644
index 0000000000000..14829629861c6
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
+
+void reference_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<int&, Deleter>);
+}
+
+void abominable_function_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
+}


From libcxx-commits at lists.llvm.org  Sun Jul 12 06:54:22 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 12 Jul 2026 06:54:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a539c8e.e624eb8d.39cd4e.da8a@mx.google.com>


Zingam wrote:

> Implement [LWG 4144](https://cplusplus.github.io/LWG/issue4144) by explicitly rejecting `unique_ptr<T, D>` when `T*` is not a valid type.
> 
> This adds a pointability check and tests reference and abominable function types. It also includes a minor formatting cleanup for `unique_ptr`'s `nullptr` comparison operator.
> 
> Fixes #118359

Formatting updates are considered unrelated changes usually. If necessary they need to be submitted as NFC separately.
Please revert those changes.

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 07:35:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 07:35:55 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [libcxx]support freestanding
	libc++ (PR #208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53a64b.a1accbb4.244acd.8089@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 6cabf2c78b79590476555c0066c20083c2c36c52 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH 1/5] [libcxx] Support freestanding libc++

---
 clang/lib/Headers/errno.h                     | 164 ++++++++++++++++++
 clang/lib/Headers/math.h                      |  29 ++++
 clang/lib/Headers/stdlib.h                    | 103 +++++++++++
 clang/lib/Headers/string.h                    |  65 +++++++
 clang/lib/Headers/wchar.h                     |  62 +++++++
 libcxx/CMakeLists.txt                         |  54 ++++--
 libcxx/include/__algorithm/sort.h             |   4 +
 libcxx/include/__config                       |   4 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/ctime                          |   7 +-
 libcxx/include/stdio.h                        |   5 +
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 114 ++++++++++++
 12 files changed, 600 insertions(+), 14 deletions(-)
 create mode 100644 clang/lib/Headers/errno.h
 create mode 100644 clang/lib/Headers/math.h
 create mode 100644 clang/lib/Headers/stdlib.h
 create mode 100644 clang/lib/Headers/string.h
 create mode 100644 clang/lib/Headers/wchar.h
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/clang/lib/Headers/errno.h b/clang/lib/Headers/errno.h
new file mode 100644
index 0000000000000..48fe84a9304ce
--- /dev/null
+++ b/clang/lib/Headers/errno.h
@@ -0,0 +1,164 @@
+/*===---- errno.h - Standard header for error numbers ---------------------===*\
+ *
+ * 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 __CLANG_ERRNO_H
+#define __CLANG_ERRNO_H
+
+/* If we're hosted, fall back to the system's errno.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<errno.h>)
+
+#include_next <errno.h>
+
+#else
+
+/* Adjusted to the linux asm/errno.h */
+#define EPERM 1            /* Operation not permitted */
+#define ENOENT 2           /* No such file or directory */
+#define ESRCH 3            /* No such process */
+#define EINTR 4            /* Interrupted system call */
+#define EIO 5              /* I/O error */
+#define ENXIO 6            /* No such device or address */
+#define E2BIG 7            /* Arg list too long */
+#define ENOEXEC 8          /* Exec format error */
+#define EBADF 9            /* Bad file number */
+#define ECHILD 10          /* No child processes */
+#define EAGAIN 11          /* Try again */
+#define ENOMEM 12          /* Out of memory */
+#define EACCES 13          /* Permission denied */
+#define EFAULT 14          /* Bad address */
+#define ENOTBLK 15         /* Block device required */
+#define EBUSY 16           /* Device or resource busy */
+#define EEXIST 17          /* File exists */
+#define EXDEV 18           /* Cross-device link */
+#define ENODEV 19          /* No such device */
+#define ENOTDIR 20         /* Not a directory */
+#define EISDIR 21          /* Is a directory */
+#define EINVAL 22          /* Invalid argument */
+#define ENFILE 23          /* File table overflow */
+#define EMFILE 24          /* Too many open files */
+#define ENOTTY 25          /* Not a typewriter */
+#define ETXTBSY 26         /* Text file busy */
+#define EFBIG 27           /* File too large */
+#define ENOSPC 28          /* No space left on device */
+#define ESPIPE 29          /* Illegal seek */
+#define EROFS 30           /* Read-only file system */
+#define EMLINK 31          /* Too many links */
+#define EPIPE 32           /* Broken pipe */
+#define EDOM 33            /* Math argument out of domain of func */
+#define ERANGE 34          /* Math result not representable */
+#define EDEADLK 35         /* Resource deadlock would occur */
+#define ENAMETOOLONG 36    /* File name too long */
+#define ENOLCK 37          /* No record locks available */
+#define ENOSYS 38          /* Function not implemented */
+#define ENOTEMPTY 39       /* Directory not empty */
+#define ELOOP 40           /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42          /* No message of desired type */
+#define EIDRM 43           /* Identifier removed */
+#define ECHRNG 44          /* Channel number out of range */
+#define EL2NSYNC 45        /* Level 2 not synchronized */
+#define EL3HLT 46          /* Level 3 halted */
+#define EL3RST 47          /* Level 3 reset */
+#define ELNRNG 48          /* Link number out of range */
+#define EUNATCH 49         /* Protocol driver not attached */
+#define ENOCSI 50          /* No CSI structure available */
+#define EL2HLT 51          /* Level 2 halted */
+#define EBADE 52           /* Invalid exchange */
+#define EBADR 53           /* Invalid request descriptor */
+#define EXFULL 54          /* Exchange full */
+#define ENOANO 55          /* No anode */
+#define EBADRQC 56         /* Invalid request code */
+#define EBADSLT 57         /* Invalid slot */
+#define EDEADLOCK EDEADLK
+#define EBFONT 59          /* Bad font file format */
+#define EFTYPE EBFONT      /* Inappropriate file type or format */
+#define ENOSTR 60          /* Device not a stream */
+#define ENODATA 61         /* No data available */
+#define ETIME 62           /* Timer expired */
+#define ENOSR 63           /* Out of streams resources */
+#define ENONET 64          /* Machine is not on the network */
+#define ENOPKG 65          /* Package not installed */
+#define EREMOTE 66         /* Object is remote */
+#define ENOLINK 67         /* Link has been severed */
+#define EADV 68            /* Advertise error */
+#define ESRMNT 69          /* Srmount error */
+#define ECOMM 70           /* Communication error on send */
+#define EPROTO 71          /* Protocol error */
+#define EMULTIHOP 72       /* Multihop attempted */
+#define EDOTDOT 73         /* RFS specific error */
+#define EBADMSG 74         /* Not a data message */
+#define EOVERFLOW 75       /* Value too large for defined data type */
+#define ENOTUNIQ 76        /* Name not unique on network */
+#define EBADFD 77          /* File descriptor in bad state */
+#define EREMCHG 78         /* Remote address changed */
+#define ELIBACC 79         /* Can not access a needed shared library */
+#define ELIBBAD 80         /* Accessing a corrupted shared library */
+#define ELIBSCN 81         /* .lib section in a.out corrupted */
+#define ELIBMAX 82         /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83        /* Cannot exec a shared library directly */
+#define EILSEQ 84          /* Illegal byte sequence */
+#define ERESTART 85        /* Interrupted system call should be restarted */
+#define ESTRPIPE 86        /* Streams pipe error */
+#define EUSERS 87          /* Too many users */
+#define ENOTSOCK 88        /* Socket operation on non-socket */
+#define EDESTADDRREQ 89    /* Destination address required */
+#define EMSGSIZE 90        /* Message too long */
+#define EPROTOTYPE 91      /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92     /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95      /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96    /* Protocol family not supported */
+#define EAFNOSUPPORT 97    /* Address family not supported by protocol */
+#define EADDRINUSE 98      /* Address already in use */
+#define EADDRNOTAVAIL 99   /* Cannot assign requested address */
+#define ENETDOWN 100       /* Network is down */
+#define ENETUNREACH 101    /* Network is unreachable */
+#define ENETRESET 102      /* Network dropped connection because of reset */
+#define ECONNABORTED 103   /* Software caused connection abort */
+#define ECONNRESET 104     /* Connection reset by peer */
+#define ENOBUFS 105        /* No buffer space available */
+#define EISCONN 106        /* Transport endpoint is already connected */
+#define ENOTCONN 107       /* Transport endpoint is not connected */
+#define ESHUTDOWN 108      /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109   /* Too many references: cannot splice */
+#define ETIMEDOUT 110      /* Connection timed out */
+#define ECONNREFUSED 111   /* Connection refused */
+#define EHOSTDOWN 112      /* Host is down */
+#define EHOSTUNREACH 113   /* No route to host */
+#define EALREADY 114       /* Operation already in progress */
+#define EINPROGRESS 115    /* Operation now in progress */
+#define ESTALE 116         /* Stale NFS file handle */
+#define EUCLEAN 117        /* Structure needs cleaning */
+#define ENOTNAM 118        /* Not a XENIX named type file */
+#define ENAVAIL 119        /* No XENIX semaphores available */
+#define EISNAM 120         /* Is a named type file */
+#define EREMOTEIO 121      /* Remote I/O error */
+#define EDQUOT 122         /* Quota exceeded */
+#define ENOMEDIUM 123      /* No medium found */
+#define EMEDIUMTYPE 124    /* Wrong medium type */
+#define ECANCELED 125      /* Operation Canceled */
+#define ENOKEY 126         /* Required key not available */
+#define EKEYEXPIRED 127    /* Key has expired */
+#define EKEYREVOKED 128    /* Key has been revoked */
+#define EKEYREJECTED 129   /* Key was rejected by service */
+#define EOWNERDEAD 130     /* Owner died */
+#define ENOTRECOVERABLE 131 /* State not recoverable */
+#define ERFKILL 132         /* Operation not possible due to RF-kill */
+#define EHWPOISON 133       /* Memory page has hardware error */
+
+/* Widely known to be a synonym in Linux.  */
+#define ENOTSUP EOPNOTSUPP
+
+#define __ELASTERROR 2000 /* Users can add values starting here */
+
+#endif
+
+#endif
diff --git a/clang/lib/Headers/math.h b/clang/lib/Headers/math.h
new file mode 100644
index 0000000000000..3e316c891a1bc
--- /dev/null
+++ b/clang/lib/Headers/math.h
@@ -0,0 +1,29 @@
+/*===---- math.h - Standard header for C math ----------------------------===*\
+ *
+ * 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 __CLANG_MATH_H
+#define __CLANG_MATH_H
+
+/* If we're hosted, fall back to the system's math.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<math.h>)
+
+#include_next <math.h>
+
+#else
+
+#define NAN __builtin_nanf("")
+#define INFINITY __builtin_inff()
+#define FP_NAN 0
+#define FP_INFINITE 1
+#define FP_ZERO 2
+#define FP_SUBNORMAL 3
+#define FP_NORMAL 4
+
+#endif
diff --git a/clang/lib/Headers/stdlib.h b/clang/lib/Headers/stdlib.h
new file mode 100644
index 0000000000000..db5fa08385f58
--- /dev/null
+++ b/clang/lib/Headers/stdlib.h
@@ -0,0 +1,103 @@
+/*===---- stdlib.h - Standard header for general utilities ----------------===*\
+ *
+ * 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 __CLANG_STDLIB_H
+#define __CLANG_STDLIB_H
+
+/* If we're hosted, fall back to the system's math.h, which might have
+ * additional definitions.
+ */
+#if __STDC_HOSTED__ && __has_include_next(<stdlib.h>)
+
+#include_next <stdlib.h>
+
+#else
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1 /* freestanding */
+#endif
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0 /* freestanding */
+#endif
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+  int quot;
+  int rem;
+} div_t; /* freestanding */
+typedef struct {
+  long quot;
+  long rem;
+} ldiv_t; /* freestanding */
+typedef struct {
+  long long quot;
+  long long rem;
+} lldiv_t; /* freestanding */
+typedef struct {
+  intmax_t quot;
+  intmax_t rem;
+} imaxdiv_t; /* freestanding */
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+// start and termination
+
+#ifndef __CLANG_STDLIB_NOEXCEPT
+#ifdef __cplusplus
+#if __cplusplus >= 201103L
+#define __CLANG_STDLIB_NOEXCEPT noexcept
+#else
+#define __CLANG_STDLIB_NOEXCEPT throw()
+#endif
+#else
+#define __CLANG_STDLIB_NOEXCEPT
+#endif
+#endif
+
+[[noreturn]] void abort() __CLANG_STDLIB_NOEXCEPT;         /* freestanding */
+int atexit(void (*)()) __CLANG_STDLIB_NOEXCEPT;            /* freestanding */
+int at_quick_exit(void (*)()) __CLANG_STDLIB_NOEXCEPT;     /* freestanding */
+[[noreturn]] void exit(int);                               /* freestanding */
+[[noreturn]] void _Exit(int) __CLANG_STDLIB_NOEXCEPT;      /* freestanding */
+[[noreturn]] void quick_exit(int) __CLANG_STDLIB_NOEXCEPT; /* freestanding */
+
+#undef __CLANG_STDLIB_NOEXCEPT
+
+// C standard library algorithms
+void *bsearch(const void *, const void *, size_t, size_t,
+              int (*)(const void *, const void *)); /* freestanding */
+void qsort(void *, size_t, size_t,
+           int (*)(const void *, const void *)); /* freestanding */
+
+// absolute values
+int abs(int j);                       /* freestanding */
+long int abs(long int j);             /* freestanding */
+long long int abs(long long int j);   /* freestanding */
+long int labs(long int j);            /* freestanding */
+long long int llabs(long long int j); /* freestanding */
+
+div_t div(int, int);                         /* freestanding */
+ldiv_t div(long int, long int);              /* freestanding */
+lldiv_t div(long long int, long long int);   /* freestanding */
+ldiv_t ldiv(long int, long int);             /* freestanding */
+lldiv_t lldiv(long long int, long long int); /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
diff --git a/clang/lib/Headers/string.h b/clang/lib/Headers/string.h
new file mode 100644
index 0000000000000..c256b3e35a95b
--- /dev/null
+++ b/clang/lib/Headers/string.h
@@ -0,0 +1,65 @@
+/*===---- string.h - Standard header for null-terminated string -----------===*\
+ *
+ * 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 __CLANG_STRING_H
+#define __CLANG_STRING_H
+
+/* If we're hosted, fall back to the system's string.h, which might have
+ * additional definitions.
+ */
+
+#if __STDC_HOSTED__ && __has_include_next(<string.h>)
+
+#include_next <string.h>
+
+#else
+
+#include <stddef.h>
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void *memcpy(void *, const void *, size_t);      /* freestanding */
+void *memmove(void *, const void *, size_t);     /* freestanding */
+char *strcpy(char *, const char *);              /* freestanding */
+char *strncpy(char *, const char *, size_t);     /* freestanding */
+char *strcat(char *, const char *);              /* freestanding */
+char *strncat(char *, const char *, size_t);     /* freestanding */
+int memcmp(const void *, const void *, size_t);  /* freestanding */
+int strcmp(const char *, const char *);          /* freestanding */
+int strncmp(const char *, const char *, size_t); /* freestanding */
+const void *memchr(const void *, int, size_t);   /* freestanding */
+void *memchr(void *, int, size_t);               /* freestanding */
+const char *strchr(const char *, int);           /* freestanding */
+char *strchr(char *, int);                       /* freestanding */
+size_t strcspn(const char *, const char *);      /* freestanding */
+const char *strpbrk(const char *, const char *); /* freestanding */
+char *strpbrk(char *, const char *);             /* freestanding */
+const char *strrchr(const char *, int);          /* freestanding */
+char *strrchr(char *, int);                      /* freestanding */
+size_t strspn(const char *, const char *);       /* freestanding */
+const char *strstr(const char *, const char *);  /* freestanding */
+char *strstr(char *, const char *);              /* freestanding */
+char *strtok(char *, const char *);              /* freestanding */
+void *memset(void *, int, size_t);               /* freestanding */
+size_t strlen(const char *);                     /* freestanding */
+void *memset_explicit(void *, int, size_t);      /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/clang/lib/Headers/wchar.h b/clang/lib/Headers/wchar.h
new file mode 100644
index 0000000000000..ca232c0cd6356
--- /dev/null
+++ b/clang/lib/Headers/wchar.h
@@ -0,0 +1,62 @@
+/*===---- wchar.h - Standard header for wide character types --------------===*\
+ *
+ * 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 __CLANG_WCHAR_H
+#define __CLANG_WCHAR_H
+
+/* If we're hosted, fall back to the system's wchar.h, which might have
+ * additional definitions.
+ */
+
+#if __STDC_HOSTED__ && __has_include_next(<wchar.h>)
+
+#include_next <wchar.h>
+
+#else
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+wchar_t *wcscpy(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcsncpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t);    /* freestanding */
+wchar_t *wcscat(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcsncat(wchar_t *, const wchar_t *, size_t);     /* freestanding */
+int wcscmp(const wchar_t *, const wchar_t *);             /* freestanding */
+int wcsncmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
+int wmemcmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
+const wchar_t *wcschr(const wchar_t *, wchar_t);          /* freestanding */
+wchar_t *wcschr(wchar_t *, wchar_t);                      /* freestanding */
+size_t wcscspn(const wchar_t *, const wchar_t *);         /* freestanding */
+const wchar_t *wcspbrk(const wchar_t *, const wchar_t *); /* freestanding */
+wchar_t *wcspbrk(wchar_t *, const wchar_t *);             /* freestanding */
+const wchar_t *wcsrchr(const wchar_t *, wchar_t);         /* freestanding */
+wchar_t *wcsrchr(wchar_t *, wchar_t);                     /* freestanding */
+size_t wcsspn(const wchar_t *, const wchar_t *);          /* freestanding */
+const wchar_t *wcsstr(const wchar_t *, const wchar_t *);  /* freestanding */
+wchar_t *wcsstr(wchar_t *, const wchar_t *);              /* freestanding */
+wchar_t *wcstok(wchar_t *, const wchar_t *, wchar_t **);  /* freestanding */
+const wchar_t *wmemchr(const wchar_t *, wchar_t, size_t); /* freestanding */
+wchar_t *wmemchr(wchar_t *, wchar_t, size_t);             /* freestanding */
+size_t wcslen(const wchar_t *);                           /* freestanding */
+wchar_t *wmemset(wchar_t *, wchar_t, size_t);             /* freestanding */
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..b92ff4b07777a 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,43 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +923,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +946,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..f144115ded7e4 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,8 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
+
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -859,6 +861,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
+#endif
+
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 __sort_dispatch(_RandomAccessIterator __first, _RandomAccessIterator __last, _Comp& __comp) {
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..cbfb448189360 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..6140faed9550f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -52,11 +52,16 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #  include <__cstddef/size_t.h>
 
 // <time.h> is not provided by libc++
-#  if __has_include(<time.h>)
+#  if __STDC_HOSTED__ && __has_include(<time.h>)
 #    include <time.h>
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..e42485542d347 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,11 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
+#endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..b76e95089190e
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only %s
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding -DFREESTANDING %s
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(FREESTANDING) != defined(_LIBCPP_FREESTANDING)
+#  error _LIBCPP_FREESTANDING should be defined in freestanding mode and not \
+       defined in non-freestanding mode
+#  if defined(__has_feature)
+#    if __has_feature(modules)
+#      define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#    endif
+#  elif defined(__cpp_modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+
+#  if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#    include <cstddef>
+#    include <limits>
+#    include <cfloat>
+#    include <version>
+#    include <cstdint>
+#    include <cstdlib>
+#    include <new>
+#    include <typeinfo>
+#    if __has_include(<source_location>)
+#      include <source_location>
+#    endif
+#    include <exception>
+#    include <initializer_list>
+#    include <compare>
+#    include <coroutine>
+#    include <cstdarg>
+#    include <concepts>
+#    include <type_traits>
+#    include <bit>
+#    include <atomic>
+#    include <utility>
+#    include <tuple>
+#    include <memory>
+#    include <functional>
+#    include <ratio>
+#    include <iterator>
+#    include <ranges>
+#    include <typeinfo>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#    include <iosfwd>
+#    include <cstdio>
+#    include <cerrno>
+#    include <cstring>
+#    include <cmath>
+#    ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#      include <cwchar>
+#    endif
+#    include <array>
+#    include <span>
+#    include <algorithm>
+#    include <bitset>
+#    include <variant>
+#    include <optional>
+#    include <numbers>
+#    include <scoped_allocator>
+#    include <typeindex>
+#    include <string_view>
+#    include <numeric>
+#    include <complex>
+#    include <chrono>
+#    include <charconv>
+#    include <expected>
+#    include <random>
+#    include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#    include <vector>
+#    include <deque>
+#    include <string>
+#    include <stack>
+#    include <queue>
+#    include <list>
+#    include <forward_list>
+#    include <map>
+#    include <set>
+#    include <unordered_set>
+#    include <unordered_map>
+#    if __has_include(<mdspan>)
+#      include <mdspan>
+#    endif
+#    include <valarray>
+
+#    if _LIBCPP_STD_VER < 20
+#      include <ciso646>
+#      if __has_include(<cstdalign>)
+#        include <cstdalign>
+#      endif
+#      include <cstdbool>
+#    endif
+#  endif

>From 259f4c59ffcdd906df3383d5abd199ad54646020 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 19:10:08 +0800
Subject: [PATCH 2/5] [skip ci][libcxx] freestanding has no threads

---
 libcxx/include/__config | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index cbfb448189360..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -234,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API

>From 027f7c01358272d9950c8e0438caf67a0a06e863 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 19:19:02 +0800
Subject: [PATCH 3/5] [skip ci][libcxx] stdio.h #endif missing

---
 libcxx/include/stdio.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index e42485542d347..28d86a60ef247 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -107,7 +107,6 @@ void perror(const char* s);
 #    define EOF (-1)
 #  endif
 #endif
-#endif
 
 #ifndef _LIBCPP_STDIO_H
 #  define _LIBCPP_STDIO_H

>From 3231856456ab14b463e4165444a686450ae98b3b Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sun, 12 Jul 2026 21:31:59 +0800
Subject: [PATCH 4/5] [libcxx] freestanding cxx trick to make it support
 freestanding

---
 clang/lib/Headers/errno.h         | 164 ------------------------------
 clang/lib/Headers/math.h          |  29 ------
 clang/lib/Headers/stdlib.h        | 103 -------------------
 clang/lib/Headers/string.h        |  65 ------------
 clang/lib/Headers/wchar.h         |  62 -----------
 libcxx/CMakeLists.txt             |   1 +
 libcxx/include/__algorithm/sort.h |   6 +-
 libcxx/include/__mbstate_t.h      |  10 ++
 libcxx/include/stdio.h            |  11 ++
 libcxx/include/string.h           |  42 +++++++-
 10 files changed, 64 insertions(+), 429 deletions(-)
 delete mode 100644 clang/lib/Headers/errno.h
 delete mode 100644 clang/lib/Headers/math.h
 delete mode 100644 clang/lib/Headers/stdlib.h
 delete mode 100644 clang/lib/Headers/string.h
 delete mode 100644 clang/lib/Headers/wchar.h

diff --git a/clang/lib/Headers/errno.h b/clang/lib/Headers/errno.h
deleted file mode 100644
index 48fe84a9304ce..0000000000000
--- a/clang/lib/Headers/errno.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*===---- errno.h - Standard header for error numbers ---------------------===*\
- *
- * 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 __CLANG_ERRNO_H
-#define __CLANG_ERRNO_H
-
-/* If we're hosted, fall back to the system's errno.h, which might have
- * additional definitions.
- */
-#if __STDC_HOSTED__ && __has_include_next(<errno.h>)
-
-#include_next <errno.h>
-
-#else
-
-/* Adjusted to the linux asm/errno.h */
-#define EPERM 1            /* Operation not permitted */
-#define ENOENT 2           /* No such file or directory */
-#define ESRCH 3            /* No such process */
-#define EINTR 4            /* Interrupted system call */
-#define EIO 5              /* I/O error */
-#define ENXIO 6            /* No such device or address */
-#define E2BIG 7            /* Arg list too long */
-#define ENOEXEC 8          /* Exec format error */
-#define EBADF 9            /* Bad file number */
-#define ECHILD 10          /* No child processes */
-#define EAGAIN 11          /* Try again */
-#define ENOMEM 12          /* Out of memory */
-#define EACCES 13          /* Permission denied */
-#define EFAULT 14          /* Bad address */
-#define ENOTBLK 15         /* Block device required */
-#define EBUSY 16           /* Device or resource busy */
-#define EEXIST 17          /* File exists */
-#define EXDEV 18           /* Cross-device link */
-#define ENODEV 19          /* No such device */
-#define ENOTDIR 20         /* Not a directory */
-#define EISDIR 21          /* Is a directory */
-#define EINVAL 22          /* Invalid argument */
-#define ENFILE 23          /* File table overflow */
-#define EMFILE 24          /* Too many open files */
-#define ENOTTY 25          /* Not a typewriter */
-#define ETXTBSY 26         /* Text file busy */
-#define EFBIG 27           /* File too large */
-#define ENOSPC 28          /* No space left on device */
-#define ESPIPE 29          /* Illegal seek */
-#define EROFS 30           /* Read-only file system */
-#define EMLINK 31          /* Too many links */
-#define EPIPE 32           /* Broken pipe */
-#define EDOM 33            /* Math argument out of domain of func */
-#define ERANGE 34          /* Math result not representable */
-#define EDEADLK 35         /* Resource deadlock would occur */
-#define ENAMETOOLONG 36    /* File name too long */
-#define ENOLCK 37          /* No record locks available */
-#define ENOSYS 38          /* Function not implemented */
-#define ENOTEMPTY 39       /* Directory not empty */
-#define ELOOP 40           /* Too many symbolic links encountered */
-#define EWOULDBLOCK EAGAIN /* Operation would block */
-#define ENOMSG 42          /* No message of desired type */
-#define EIDRM 43           /* Identifier removed */
-#define ECHRNG 44          /* Channel number out of range */
-#define EL2NSYNC 45        /* Level 2 not synchronized */
-#define EL3HLT 46          /* Level 3 halted */
-#define EL3RST 47          /* Level 3 reset */
-#define ELNRNG 48          /* Link number out of range */
-#define EUNATCH 49         /* Protocol driver not attached */
-#define ENOCSI 50          /* No CSI structure available */
-#define EL2HLT 51          /* Level 2 halted */
-#define EBADE 52           /* Invalid exchange */
-#define EBADR 53           /* Invalid request descriptor */
-#define EXFULL 54          /* Exchange full */
-#define ENOANO 55          /* No anode */
-#define EBADRQC 56         /* Invalid request code */
-#define EBADSLT 57         /* Invalid slot */
-#define EDEADLOCK EDEADLK
-#define EBFONT 59          /* Bad font file format */
-#define EFTYPE EBFONT      /* Inappropriate file type or format */
-#define ENOSTR 60          /* Device not a stream */
-#define ENODATA 61         /* No data available */
-#define ETIME 62           /* Timer expired */
-#define ENOSR 63           /* Out of streams resources */
-#define ENONET 64          /* Machine is not on the network */
-#define ENOPKG 65          /* Package not installed */
-#define EREMOTE 66         /* Object is remote */
-#define ENOLINK 67         /* Link has been severed */
-#define EADV 68            /* Advertise error */
-#define ESRMNT 69          /* Srmount error */
-#define ECOMM 70           /* Communication error on send */
-#define EPROTO 71          /* Protocol error */
-#define EMULTIHOP 72       /* Multihop attempted */
-#define EDOTDOT 73         /* RFS specific error */
-#define EBADMSG 74         /* Not a data message */
-#define EOVERFLOW 75       /* Value too large for defined data type */
-#define ENOTUNIQ 76        /* Name not unique on network */
-#define EBADFD 77          /* File descriptor in bad state */
-#define EREMCHG 78         /* Remote address changed */
-#define ELIBACC 79         /* Can not access a needed shared library */
-#define ELIBBAD 80         /* Accessing a corrupted shared library */
-#define ELIBSCN 81         /* .lib section in a.out corrupted */
-#define ELIBMAX 82         /* Attempting to link in too many shared libraries */
-#define ELIBEXEC 83        /* Cannot exec a shared library directly */
-#define EILSEQ 84          /* Illegal byte sequence */
-#define ERESTART 85        /* Interrupted system call should be restarted */
-#define ESTRPIPE 86        /* Streams pipe error */
-#define EUSERS 87          /* Too many users */
-#define ENOTSOCK 88        /* Socket operation on non-socket */
-#define EDESTADDRREQ 89    /* Destination address required */
-#define EMSGSIZE 90        /* Message too long */
-#define EPROTOTYPE 91      /* Protocol wrong type for socket */
-#define ENOPROTOOPT 92     /* Protocol not available */
-#define EPROTONOSUPPORT 93 /* Protocol not supported */
-#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
-#define EOPNOTSUPP 95      /* Operation not supported on transport endpoint */
-#define EPFNOSUPPORT 96    /* Protocol family not supported */
-#define EAFNOSUPPORT 97    /* Address family not supported by protocol */
-#define EADDRINUSE 98      /* Address already in use */
-#define EADDRNOTAVAIL 99   /* Cannot assign requested address */
-#define ENETDOWN 100       /* Network is down */
-#define ENETUNREACH 101    /* Network is unreachable */
-#define ENETRESET 102      /* Network dropped connection because of reset */
-#define ECONNABORTED 103   /* Software caused connection abort */
-#define ECONNRESET 104     /* Connection reset by peer */
-#define ENOBUFS 105        /* No buffer space available */
-#define EISCONN 106        /* Transport endpoint is already connected */
-#define ENOTCONN 107       /* Transport endpoint is not connected */
-#define ESHUTDOWN 108      /* Cannot send after transport endpoint shutdown */
-#define ETOOMANYREFS 109   /* Too many references: cannot splice */
-#define ETIMEDOUT 110      /* Connection timed out */
-#define ECONNREFUSED 111   /* Connection refused */
-#define EHOSTDOWN 112      /* Host is down */
-#define EHOSTUNREACH 113   /* No route to host */
-#define EALREADY 114       /* Operation already in progress */
-#define EINPROGRESS 115    /* Operation now in progress */
-#define ESTALE 116         /* Stale NFS file handle */
-#define EUCLEAN 117        /* Structure needs cleaning */
-#define ENOTNAM 118        /* Not a XENIX named type file */
-#define ENAVAIL 119        /* No XENIX semaphores available */
-#define EISNAM 120         /* Is a named type file */
-#define EREMOTEIO 121      /* Remote I/O error */
-#define EDQUOT 122         /* Quota exceeded */
-#define ENOMEDIUM 123      /* No medium found */
-#define EMEDIUMTYPE 124    /* Wrong medium type */
-#define ECANCELED 125      /* Operation Canceled */
-#define ENOKEY 126         /* Required key not available */
-#define EKEYEXPIRED 127    /* Key has expired */
-#define EKEYREVOKED 128    /* Key has been revoked */
-#define EKEYREJECTED 129   /* Key was rejected by service */
-#define EOWNERDEAD 130     /* Owner died */
-#define ENOTRECOVERABLE 131 /* State not recoverable */
-#define ERFKILL 132         /* Operation not possible due to RF-kill */
-#define EHWPOISON 133       /* Memory page has hardware error */
-
-/* Widely known to be a synonym in Linux.  */
-#define ENOTSUP EOPNOTSUPP
-
-#define __ELASTERROR 2000 /* Users can add values starting here */
-
-#endif
-
-#endif
diff --git a/clang/lib/Headers/math.h b/clang/lib/Headers/math.h
deleted file mode 100644
index 3e316c891a1bc..0000000000000
--- a/clang/lib/Headers/math.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*===---- math.h - Standard header for C math ----------------------------===*\
- *
- * 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 __CLANG_MATH_H
-#define __CLANG_MATH_H
-
-/* If we're hosted, fall back to the system's math.h, which might have
- * additional definitions.
- */
-#if __STDC_HOSTED__ && __has_include_next(<math.h>)
-
-#include_next <math.h>
-
-#else
-
-#define NAN __builtin_nanf("")
-#define INFINITY __builtin_inff()
-#define FP_NAN 0
-#define FP_INFINITE 1
-#define FP_ZERO 2
-#define FP_SUBNORMAL 3
-#define FP_NORMAL 4
-
-#endif
diff --git a/clang/lib/Headers/stdlib.h b/clang/lib/Headers/stdlib.h
deleted file mode 100644
index db5fa08385f58..0000000000000
--- a/clang/lib/Headers/stdlib.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*===---- stdlib.h - Standard header for general utilities ----------------===*\
- *
- * 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 __CLANG_STDLIB_H
-#define __CLANG_STDLIB_H
-
-/* If we're hosted, fall back to the system's math.h, which might have
- * additional definitions.
- */
-#if __STDC_HOSTED__ && __has_include_next(<stdlib.h>)
-
-#include_next <stdlib.h>
-
-#else
-
-#include <stddef.h>
-#include <stdint.h>
-
-#ifndef EXIT_FAILURE
-#define EXIT_FAILURE 1 /* freestanding */
-#endif
-#ifndef EXIT_SUCCESS
-#define EXIT_SUCCESS 0 /* freestanding */
-#endif
-
-#ifndef __cplusplus
-extern "C" {
-#endif
-
-typedef struct {
-  int quot;
-  int rem;
-} div_t; /* freestanding */
-typedef struct {
-  long quot;
-  long rem;
-} ldiv_t; /* freestanding */
-typedef struct {
-  long long quot;
-  long long rem;
-} lldiv_t; /* freestanding */
-typedef struct {
-  intmax_t quot;
-  intmax_t rem;
-} imaxdiv_t; /* freestanding */
-
-/*
-We offer declarations for these C functions,
-leaving the implementations to be handled by users.
-*/
-
-// start and termination
-
-#ifndef __CLANG_STDLIB_NOEXCEPT
-#ifdef __cplusplus
-#if __cplusplus >= 201103L
-#define __CLANG_STDLIB_NOEXCEPT noexcept
-#else
-#define __CLANG_STDLIB_NOEXCEPT throw()
-#endif
-#else
-#define __CLANG_STDLIB_NOEXCEPT
-#endif
-#endif
-
-[[noreturn]] void abort() __CLANG_STDLIB_NOEXCEPT;         /* freestanding */
-int atexit(void (*)()) __CLANG_STDLIB_NOEXCEPT;            /* freestanding */
-int at_quick_exit(void (*)()) __CLANG_STDLIB_NOEXCEPT;     /* freestanding */
-[[noreturn]] void exit(int);                               /* freestanding */
-[[noreturn]] void _Exit(int) __CLANG_STDLIB_NOEXCEPT;      /* freestanding */
-[[noreturn]] void quick_exit(int) __CLANG_STDLIB_NOEXCEPT; /* freestanding */
-
-#undef __CLANG_STDLIB_NOEXCEPT
-
-// C standard library algorithms
-void *bsearch(const void *, const void *, size_t, size_t,
-              int (*)(const void *, const void *)); /* freestanding */
-void qsort(void *, size_t, size_t,
-           int (*)(const void *, const void *)); /* freestanding */
-
-// absolute values
-int abs(int j);                       /* freestanding */
-long int abs(long int j);             /* freestanding */
-long long int abs(long long int j);   /* freestanding */
-long int labs(long int j);            /* freestanding */
-long long int llabs(long long int j); /* freestanding */
-
-div_t div(int, int);                         /* freestanding */
-ldiv_t div(long int, long int);              /* freestanding */
-lldiv_t div(long long int, long long int);   /* freestanding */
-ldiv_t ldiv(long int, long int);             /* freestanding */
-lldiv_t lldiv(long long int, long long int); /* freestanding */
-
-#ifndef __cplusplus
-}
-#endif
-
-#endif
diff --git a/clang/lib/Headers/string.h b/clang/lib/Headers/string.h
deleted file mode 100644
index c256b3e35a95b..0000000000000
--- a/clang/lib/Headers/string.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*===---- string.h - Standard header for null-terminated string -----------===*\
- *
- * 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 __CLANG_STRING_H
-#define __CLANG_STRING_H
-
-/* If we're hosted, fall back to the system's string.h, which might have
- * additional definitions.
- */
-
-#if __STDC_HOSTED__ && __has_include_next(<string.h>)
-
-#include_next <string.h>
-
-#else
-
-#include <stddef.h>
-
-#ifndef __cplusplus
-extern "C" {
-#endif
-
-/*
-We offer declarations for these C functions,
-leaving the implementations to be handled by users.
-*/
-
-void *memcpy(void *, const void *, size_t);      /* freestanding */
-void *memmove(void *, const void *, size_t);     /* freestanding */
-char *strcpy(char *, const char *);              /* freestanding */
-char *strncpy(char *, const char *, size_t);     /* freestanding */
-char *strcat(char *, const char *);              /* freestanding */
-char *strncat(char *, const char *, size_t);     /* freestanding */
-int memcmp(const void *, const void *, size_t);  /* freestanding */
-int strcmp(const char *, const char *);          /* freestanding */
-int strncmp(const char *, const char *, size_t); /* freestanding */
-const void *memchr(const void *, int, size_t);   /* freestanding */
-void *memchr(void *, int, size_t);               /* freestanding */
-const char *strchr(const char *, int);           /* freestanding */
-char *strchr(char *, int);                       /* freestanding */
-size_t strcspn(const char *, const char *);      /* freestanding */
-const char *strpbrk(const char *, const char *); /* freestanding */
-char *strpbrk(char *, const char *);             /* freestanding */
-const char *strrchr(const char *, int);          /* freestanding */
-char *strrchr(char *, int);                      /* freestanding */
-size_t strspn(const char *, const char *);       /* freestanding */
-const char *strstr(const char *, const char *);  /* freestanding */
-char *strstr(char *, const char *);              /* freestanding */
-char *strtok(char *, const char *);              /* freestanding */
-void *memset(void *, int, size_t);               /* freestanding */
-size_t strlen(const char *);                     /* freestanding */
-void *memset_explicit(void *, int, size_t);      /* freestanding */
-
-#ifndef __cplusplus
-}
-#endif
-
-#endif
-
-#endif
diff --git a/clang/lib/Headers/wchar.h b/clang/lib/Headers/wchar.h
deleted file mode 100644
index ca232c0cd6356..0000000000000
--- a/clang/lib/Headers/wchar.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*===---- wchar.h - Standard header for wide character types --------------===*\
- *
- * 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 __CLANG_WCHAR_H
-#define __CLANG_WCHAR_H
-
-/* If we're hosted, fall back to the system's wchar.h, which might have
- * additional definitions.
- */
-
-#if __STDC_HOSTED__ && __has_include_next(<wchar.h>)
-
-#include_next <wchar.h>
-
-#else
-
-#ifndef __cplusplus
-extern "C" {
-#endif
-
-/*
-We offer declarations for these C functions,
-leaving the implementations to be handled by users.
-*/
-
-wchar_t *wcscpy(wchar_t *, const wchar_t *);              /* freestanding */
-wchar_t *wcsncpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
-wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t);     /* freestanding */
-wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t);    /* freestanding */
-wchar_t *wcscat(wchar_t *, const wchar_t *);              /* freestanding */
-wchar_t *wcsncat(wchar_t *, const wchar_t *, size_t);     /* freestanding */
-int wcscmp(const wchar_t *, const wchar_t *);             /* freestanding */
-int wcsncmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
-int wmemcmp(const wchar_t *, const wchar_t *, size_t);    /* freestanding */
-const wchar_t *wcschr(const wchar_t *, wchar_t);          /* freestanding */
-wchar_t *wcschr(wchar_t *, wchar_t);                      /* freestanding */
-size_t wcscspn(const wchar_t *, const wchar_t *);         /* freestanding */
-const wchar_t *wcspbrk(const wchar_t *, const wchar_t *); /* freestanding */
-wchar_t *wcspbrk(wchar_t *, const wchar_t *);             /* freestanding */
-const wchar_t *wcsrchr(const wchar_t *, wchar_t);         /* freestanding */
-wchar_t *wcsrchr(wchar_t *, wchar_t);                     /* freestanding */
-size_t wcsspn(const wchar_t *, const wchar_t *);          /* freestanding */
-const wchar_t *wcsstr(const wchar_t *, const wchar_t *);  /* freestanding */
-wchar_t *wcsstr(wchar_t *, const wchar_t *);              /* freestanding */
-wchar_t *wcstok(wchar_t *, const wchar_t *, wchar_t **);  /* freestanding */
-const wchar_t *wmemchr(const wchar_t *, wchar_t, size_t); /* freestanding */
-wchar_t *wmemchr(wchar_t *, wchar_t, size_t);             /* freestanding */
-size_t wcslen(const wchar_t *);                           /* freestanding */
-wchar_t *wmemset(wchar_t *, wchar_t, size_t);             /* freestanding */
-
-#ifndef __cplusplus
-}
-#endif
-
-#endif
-
-#endif
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index b92ff4b07777a..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -176,6 +176,7 @@ if (LIBCXX_FREESTANDING)
   set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
   set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
   set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
   if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
     set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index f144115ded7e4..681eaac3ecb07 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -828,12 +828,11 @@ void __introsort(_RandomAccessIterator __first,
   }
 }
 
+#if _LIBCPP_FREESTANDING != 0
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
-#if _LIBCPP_FREESTANDING == 0
-
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -860,7 +859,6 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
-
 #endif
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
@@ -961,4 +959,4 @@ _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
 
-#endif // _LIBCPP___ALGORITHM_SORT_H
+#endif // _LIBCPP___ALGORITHM_SORT_H
\ No newline at end of file
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 28d86a60ef247..534ceb8188428 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -122,4 +122,15 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..9d3f20adb783e 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifndef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides

>From a323248019b078993021abe7211a73de10ac7164 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sun, 12 Jul 2026 22:34:19 +0800
Subject: [PATCH 5/5] [libcxx] do freestanding support correctly

---
 libcxx/include/math.h                         | 39 +++++++++-
 libcxx/include/stdlib.h                       | 19 +++++
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 73 +++++++++----------
 3 files changed, 92 insertions(+), 39 deletions(-)

diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..0544429798186 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
@@ -531,4 +568,4 @@ using std::__math::trunc;
 #    include_next <math.h>
 #  endif
 
-#endif // _LIBCPP_MATH_H
+#endif // _LIBCPP_MATH_H
\ No newline at end of file
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
index b76e95089190e..aff38cbede540 100644
--- a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -9,52 +9,49 @@
 // Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
 // to the compiler but defined when -ffreestanding is passed to the compiler.
 
-// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only %s
-// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding -DFREESTANDING %s
 // RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
 
 #include <__config>
 
-#if defined(FREESTANDING) != defined(_LIBCPP_FREESTANDING)
-#  error _LIBCPP_FREESTANDING should be defined in freestanding mode and not \
-       defined in non-freestanding mode
-#  if defined(__has_feature)
-#    if __has_feature(modules)
-#      define _LIBCPP_FREESTANDING_NO_TEST_MODULES
-#    endif
-#  elif defined(__cpp_modules)
+#if defined(__has_feature)
+#  if __has_feature(modules)
 #    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
 #  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
 
 #  if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
-#    include <cstddef>
-#    include <limits>
-#    include <cfloat>
-#    include <version>
-#    include <cstdint>
-#    include <cstdlib>
-#    include <new>
-#    include <typeinfo>
-#    if __has_include(<source_location>)
-#      include <source_location>
-#    endif
-#    include <exception>
-#    include <initializer_list>
-#    include <compare>
-#    include <coroutine>
-#    include <cstdarg>
-#    include <concepts>
-#    include <type_traits>
-#    include <bit>
-#    include <atomic>
-#    include <utility>
-#    include <tuple>
-#    include <memory>
-#    include <functional>
-#    include <ratio>
-#    include <iterator>
-#    include <ranges>
-#    include <typeinfo>
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
 
 /*
 We tested these headers are for preventing build issues


From libcxx-commits at lists.llvm.org  Sun Jul 12 07:38:57 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 07:38:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53a701.ff037943.2e959a.0844@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From c0793d1e74c69f31d55f83cecc4a770a0e61190c Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libcxx] Support freestanding libc++

[skip ci][libcxx] freestanding has no threads

[skip ci][libcxx] stdio.h #endif missing

[libcxx] freestanding cxx trick to make it support freestanding

[libcxx] do freestanding support correctly
---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   4 +-
 libcxx/include/__config                       |   5 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   7 +-
 libcxx/include/math.h                         |  39 +++++-
 libcxx/include/stdio.h                        |  15 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  42 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 292 insertions(+), 18 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..681eaac3ecb07 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -828,6 +828,7 @@ void __introsort(_RandomAccessIterator __first,
   }
 }
 
+#if _LIBCPP_FREESTANDING != 0
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
@@ -858,6 +859,7 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+#endif
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
@@ -957,4 +959,4 @@ _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
 
-#endif // _LIBCPP___ALGORITHM_SORT_H
+#endif // _LIBCPP___ALGORITHM_SORT_H
\ No newline at end of file
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -230,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..6140faed9550f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -52,11 +52,16 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #  include <__cstddef/size_t.h>
 
 // <time.h> is not provided by libc++
-#  if __has_include(<time.h>)
+#  if __STDC_HOSTED__ && __has_include(<time.h>)
 #    include <time.h>
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..0544429798186 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
@@ -531,4 +568,4 @@ using std::__math::trunc;
 #    include_next <math.h>
 #  endif
 
-#endif // _LIBCPP_MATH_H
+#endif // _LIBCPP_MATH_H
\ No newline at end of file
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..534ceb8188428 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,10 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
@@ -118,4 +122,15 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..9d3f20adb783e 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifndef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..aff38cbede540
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#  if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#    include <iosfwd>
+#    include <cstdio>
+#    include <cerrno>
+#    include <cstring>
+#    include <cmath>
+#    ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#      include <cwchar>
+#    endif
+#    include <array>
+#    include <span>
+#    include <algorithm>
+#    include <bitset>
+#    include <variant>
+#    include <optional>
+#    include <numbers>
+#    include <scoped_allocator>
+#    include <typeindex>
+#    include <string_view>
+#    include <numeric>
+#    include <complex>
+#    include <chrono>
+#    include <charconv>
+#    include <expected>
+#    include <random>
+#    include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#    include <vector>
+#    include <deque>
+#    include <string>
+#    include <stack>
+#    include <queue>
+#    include <list>
+#    include <forward_list>
+#    include <map>
+#    include <set>
+#    include <unordered_set>
+#    include <unordered_map>
+#    if __has_include(<mdspan>)
+#      include <mdspan>
+#    endif
+#    include <valarray>
+
+#    if _LIBCPP_STD_VER < 20
+#      include <ciso646>
+#      if __has_include(<cstdalign>)
+#        include <cstdalign>
+#      endif
+#      include <cstdbool>
+#    endif
+#  endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 07:39:49 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 07:39:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53a735.9edb5c9d.208698.db66@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From baf3e7dc0bdabd83e649cb8ccaf0af9c2782b8c4 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libcxx] Support freestanding libc++

[skip ci][libcxx] freestanding has no threads

[skip ci][libcxx] stdio.h #endif missing

[libcxx] freestanding cxx trick to make it support freestanding

[libcxx] do freestanding support correctly
---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   4 +-
 libcxx/include/__config                       |   5 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   7 +-
 libcxx/include/math.h                         |  39 +++++-
 libcxx/include/stdio.h                        |  15 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  42 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 292 insertions(+), 18 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..681eaac3ecb07 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -828,6 +828,7 @@ void __introsort(_RandomAccessIterator __first,
   }
 }
 
+#if _LIBCPP_FREESTANDING != 0
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
@@ -858,6 +859,7 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+#endif
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
@@ -957,4 +959,4 @@ _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
 
-#endif // _LIBCPP___ALGORITHM_SORT_H
+#endif // _LIBCPP___ALGORITHM_SORT_H
\ No newline at end of file
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -230,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..6140faed9550f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -52,11 +52,16 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #  include <__cstddef/size_t.h>
 
 // <time.h> is not provided by libc++
-#  if __has_include(<time.h>)
+#  if __STDC_HOSTED__ && __has_include(<time.h>)
 #    include <time.h>
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..0544429798186 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
@@ -531,4 +568,4 @@ using std::__math::trunc;
 #    include_next <math.h>
 #  endif
 
-#endif // _LIBCPP_MATH_H
+#endif // _LIBCPP_MATH_H
\ No newline at end of file
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..534ceb8188428 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,10 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
@@ -118,4 +122,15 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..9d3f20adb783e 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifndef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..aff38cbede540
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#  if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#    include <iosfwd>
+#    include <cstdio>
+#    include <cerrno>
+#    include <cstring>
+#    include <cmath>
+#    ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#      include <cwchar>
+#    endif
+#    include <array>
+#    include <span>
+#    include <algorithm>
+#    include <bitset>
+#    include <variant>
+#    include <optional>
+#    include <numbers>
+#    include <scoped_allocator>
+#    include <typeindex>
+#    include <string_view>
+#    include <numeric>
+#    include <complex>
+#    include <chrono>
+#    include <charconv>
+#    include <expected>
+#    include <random>
+#    include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#    include <vector>
+#    include <deque>
+#    include <string>
+#    include <stack>
+#    include <queue>
+#    include <list>
+#    include <forward_list>
+#    include <map>
+#    include <set>
+#    include <unordered_set>
+#    include <unordered_map>
+#    if __has_include(<mdspan>)
+#      include <mdspan>
+#    endif
+#    include <valarray>
+
+#    if _LIBCPP_STD_VER < 20
+#      include <ciso646>
+#      if __has_include(<cstdalign>)
+#        include <cstdalign>
+#      endif
+#      include <cstdbool>
+#    endif
+#  endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 07:45:07 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 07:45:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53a873.d1496011.26913c.231b@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 889e92d5cc788694f8dea10884073e8cd2067ac7 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libcxx] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   4 +-
 libcxx/include/__config                       |   5 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   7 +-
 libcxx/include/math.h                         |  39 +++++-
 libcxx/include/stdio.h                        |  15 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  43 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 293 insertions(+), 18 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..681eaac3ecb07 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -828,6 +828,7 @@ void __introsort(_RandomAccessIterator __first,
   }
 }
 
+#if _LIBCPP_FREESTANDING != 0
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
@@ -858,6 +859,7 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+#endif
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
@@ -957,4 +959,4 @@ _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
 
-#endif // _LIBCPP___ALGORITHM_SORT_H
+#endif // _LIBCPP___ALGORITHM_SORT_H
\ No newline at end of file
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -230,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..6140faed9550f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -52,11 +52,16 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #  include <__cstddef/size_t.h>
 
 // <time.h> is not provided by libc++
-#  if __has_include(<time.h>)
+#  if __STDC_HOSTED__ && __has_include(<time.h>)
 #    include <time.h>
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..0544429798186 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
@@ -531,4 +568,4 @@ using std::__math::trunc;
 #    include_next <math.h>
 #  endif
 
-#endif // _LIBCPP_MATH_H
+#endif // _LIBCPP_MATH_H
\ No newline at end of file
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..534ceb8188428 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,10 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
@@ -118,4 +122,15 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..98957183478e2 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,50 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+size_t strnlen(const char*, size_t);              /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifndef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..aff38cbede540
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#  if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#    include <iosfwd>
+#    include <cstdio>
+#    include <cerrno>
+#    include <cstring>
+#    include <cmath>
+#    ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#      include <cwchar>
+#    endif
+#    include <array>
+#    include <span>
+#    include <algorithm>
+#    include <bitset>
+#    include <variant>
+#    include <optional>
+#    include <numbers>
+#    include <scoped_allocator>
+#    include <typeindex>
+#    include <string_view>
+#    include <numeric>
+#    include <complex>
+#    include <chrono>
+#    include <charconv>
+#    include <expected>
+#    include <random>
+#    include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#    include <vector>
+#    include <deque>
+#    include <string>
+#    include <stack>
+#    include <queue>
+#    include <list>
+#    include <forward_list>
+#    include <map>
+#    include <set>
+#    include <unordered_set>
+#    include <unordered_map>
+#    if __has_include(<mdspan>)
+#      include <mdspan>
+#    endif
+#    include <valarray>
+
+#    if _LIBCPP_STD_VER < 20
+#      include <ciso646>
+#      if __has_include(<cstdalign>)
+#        include <cstdalign>
+#      endif
+#      include <cstdbool>
+#    endif
+#  endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 08:04:35 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 08:04:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a53ad03.6c5720b3.15e90c.9db1@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/209018

>From c4349f973fed37783692cce1aae993813563a5dd Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Sun, 12 Jul 2026 21:31:47 +0800
Subject: [PATCH] [libcxx][unique_ptr] Implement LWG 4144: Disallow
 unique_ptr<T&, D>

---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__memory/unique_ptr.h          |  9 +++++
 .../non_pointable.single.verify.cpp           | 40 +++++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 8441da10d78de..c203fa424556a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -101,7 +101,7 @@
 "`LWG4140 <https://wg21.link/LWG4140>`__","Useless default constructors for bit reference types","2024-11 (Wrocław)","|Complete|","","`#118356 <https://github.com/llvm/llvm-project/issues/118356>`__",""
 "`LWG4141 <https://wg21.link/LWG4141>`__","Improve prohibitions on ""additional storage""","2024-11 (Wrocław)","","","`#118357 <https://github.com/llvm/llvm-project/issues/118357>`__",""
 "`LWG4142 <https://wg21.link/LWG4142>`__","``format_parse_context::check_dynamic_spec`` should require at least one type","2024-11 (Wrocław)","","","`#118358 <https://github.com/llvm/llvm-project/issues/118358>`__",""
-"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","","","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
+"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","|Complete|","23","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
 "`LWG4147 <https://wg21.link/LWG4147>`__","Precondition on ``inplace_vector::emplace``","2024-11 (Wrocław)","","","`#118361 <https://github.com/llvm/llvm-project/issues/118361>`__",""
 "`LWG4148 <https://wg21.link/LWG4148>`__","``unique_ptr::operator*`` should not allow dangling references","2024-11 (Wrocław)","","","`#118362 <https://github.com/llvm/llvm-project/issues/118362>`__",""
 "`LWG4153 <https://wg21.link/LWG4153>`__","Fix extra ""-1"" for ``philox_engine::max()``","2024-11 (Wrocław)","","","`#118363 <https://github.com/llvm/llvm-project/issues/118363>`__",""
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 7e68faa15dabb..93878301b0322 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -43,6 +43,7 @@
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_extent.h>
 #include <__type_traits/remove_reference.h>
+#include <__type_traits/void_t.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -100,6 +101,12 @@ inline const bool __can_dereference = false;
 template <class _Tp>
 inline const bool __can_dereference<_Tp, decltype((void)*std::declval<_Tp>())> = true;
 
+template <class _Tp, class = void>
+inline const bool __is_pointable = false;
+
+template <class _Tp>
+inline const bool __is_pointable<_Tp, __void_t<_Tp*> > = true;
+
 #if defined(_LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI)
 #  define _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI __attribute__((__trivial_abi__))
 #else
@@ -111,6 +118,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
+
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
   using pointer _LIBCPP_NODEBUG = __pointer<_Tp, deleter_type>;
 
   static_assert(!is_rvalue_reference<deleter_type>::value, "the specified deleter type cannot be an rvalue reference");
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
new file mode 100644
index 0000000000000..14829629861c6
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
+
+void reference_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<int&, Deleter>);
+}
+
+void abominable_function_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
+}


From libcxx-commits at lists.llvm.org  Sun Jul 12 08:04:44 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 08:04:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a53ad0c.f732ad81.3a4994.5aed@mx.google.com>


https://github.com/emmett2020 edited https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 08:04:56 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 08:04:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53ad18.bc28b987.90005.4c0a@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From b958a7c52bd8b8b4e6529e69541e0c21181a972f Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libcxx] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   5 +-
 libcxx/include/__config                       |   5 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   7 +-
 libcxx/include/math.h                         |  37 ++++++
 libcxx/include/stdio.h                        |  15 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  43 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 293 insertions(+), 17 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..e300dbb039fb7 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -857,6 +858,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<float>&, float*>(fl
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(double*, double*, __less<double>&);
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
+#endif
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
@@ -957,4 +960,4 @@ _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
 
-#endif // _LIBCPP___ALGORITHM_SORT_H
+#endif // _LIBCPP___ALGORITHM_SORT_H
\ No newline at end of file
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -230,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..6140faed9550f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -52,11 +52,16 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #  include <__cstddef/size_t.h>
 
 // <time.h> is not provided by libc++
-#  if __has_include(<time.h>)
+#  if __STDC_HOSTED__ && __has_include(<time.h>)
 #    include <time.h>
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..969462f22846c 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..534ceb8188428 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,10 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
@@ -118,4 +122,15 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..98957183478e2 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,50 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+size_t strnlen(const char*, size_t);              /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifndef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..aff38cbede540
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#  if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#    include <iosfwd>
+#    include <cstdio>
+#    include <cerrno>
+#    include <cstring>
+#    include <cmath>
+#    ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#      include <cwchar>
+#    endif
+#    include <array>
+#    include <span>
+#    include <algorithm>
+#    include <bitset>
+#    include <variant>
+#    include <optional>
+#    include <numbers>
+#    include <scoped_allocator>
+#    include <typeindex>
+#    include <string_view>
+#    include <numeric>
+#    include <complex>
+#    include <chrono>
+#    include <charconv>
+#    include <expected>
+#    include <random>
+#    include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#    include <vector>
+#    include <deque>
+#    include <string>
+#    include <stack>
+#    include <queue>
+#    include <list>
+#    include <forward_list>
+#    include <map>
+#    include <set>
+#    include <unordered_set>
+#    include <unordered_map>
+#    if __has_include(<mdspan>)
+#      include <mdspan>
+#    endif
+#    include <valarray>
+
+#    if _LIBCPP_STD_VER < 20
+#      include <ciso646>
+#      if __has_include(<cstdalign>)
+#        include <cstdalign>
+#      endif
+#      include <cstdbool>
+#    endif
+#  endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 08:06:30 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 08:06:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a53ad76.bc28b987.90005.699f@mx.google.com>


emmett2020 wrote:

> > Implement [LWG 4144](https://cplusplus.github.io/LWG/issue4144) by explicitly rejecting `unique_ptr<T, D>` when `T*` is not a valid type.
> > This adds a pointability check and tests reference and abominable function types. It also includes a minor formatting cleanup for `unique_ptr`'s `nullptr` comparison operator.
> > Fixes #118359
> 
> Formatting updates are considered unrelated changes usually. If necessary they need to be submitted as NFC separately. Please revert those changes.

Thanks for the review. I've reverted unrelated format changes. 

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 08:11:39 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 08:11:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53aeab.449bfb44.172ed9.e569@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From df5ccc74689268c5cac071eb5f14731ff4b158d7 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libcxx] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   3 +
 libcxx/include/__config                       |   5 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   5 +
 libcxx/include/math.h                         |  37 ++++++
 libcxx/include/stdio.h                        |  15 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  43 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 291 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..bea314fc5473e 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -857,6 +858,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<float>&, float*>(fl
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(double*, double*, __less<double>&);
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
+#endif
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -230,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..a29f60c78ac5f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -57,6 +57,11 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..969462f22846c 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..534ceb8188428 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,10 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
@@ -118,4 +122,15 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..98957183478e2 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,50 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+size_t strnlen(const char*, size_t);              /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifndef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..aff38cbede540
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#  if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#    include <iosfwd>
+#    include <cstdio>
+#    include <cerrno>
+#    include <cstring>
+#    include <cmath>
+#    ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#      include <cwchar>
+#    endif
+#    include <array>
+#    include <span>
+#    include <algorithm>
+#    include <bitset>
+#    include <variant>
+#    include <optional>
+#    include <numbers>
+#    include <scoped_allocator>
+#    include <typeindex>
+#    include <string_view>
+#    include <numeric>
+#    include <complex>
+#    include <chrono>
+#    include <charconv>
+#    include <expected>
+#    include <random>
+#    include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#    include <vector>
+#    include <deque>
+#    include <string>
+#    include <stack>
+#    include <queue>
+#    include <list>
+#    include <forward_list>
+#    include <map>
+#    include <set>
+#    include <unordered_set>
+#    include <unordered_map>
+#    if __has_include(<mdspan>)
+#      include <mdspan>
+#    endif
+#    include <valarray>
+
+#    if _LIBCPP_STD_VER < 20
+#      include <ciso646>
+#      if __has_include(<cstdalign>)
+#        include <cstdalign>
+#      endif
+#      include <cstdbool>
+#    endif
+#  endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 08:16:20 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 08:16:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53afc4.481afadf.196d57.97db@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 0efd33bda610a03a1514fe4b2171341b61698507 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libc++] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   3 +
 libcxx/include/__config                       |   5 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   5 +
 libcxx/include/math.h                         |  37 ++++++
 libcxx/include/stdio.h                        |  15 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  43 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 291 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..bea314fc5473e 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -857,6 +858,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<float>&, float*>(fl
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(double*, double*, __less<double>&);
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
+#endif
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -230,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..a29f60c78ac5f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -57,6 +57,11 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..969462f22846c 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..534ceb8188428 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,10 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
@@ -118,4 +122,15 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..f03d8d256d252 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,50 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+size_t strnlen(const char*, size_t);           /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifndef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..2dc5d90b3b4ec
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#  include <iosfwd>
+#  include <cstdio>
+#  include <cerrno>
+#  include <cstring>
+#  include <cmath>
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#    include <cwchar>
+#  endif
+#  include <array>
+#  include <span>
+#  include <algorithm>
+#  include <bitset>
+#  include <variant>
+#  include <optional>
+#  include <numbers>
+#  include <scoped_allocator>
+#  include <typeindex>
+#  include <string_view>
+#  include <numeric>
+#  include <complex>
+#  include <chrono>
+#  include <charconv>
+#  include <expected>
+#  include <random>
+#  include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#  include <vector>
+#  include <deque>
+#  include <string>
+#  include <stack>
+#  include <queue>
+#  include <list>
+#  include <forward_list>
+#  include <map>
+#  include <set>
+#  include <unordered_set>
+#  include <unordered_map>
+#  if __has_include(<mdspan>)
+#    include <mdspan>
+#  endif
+#  include <valarray>
+
+#  if _LIBCPP_STD_VER < 20
+#    include <ciso646>
+#    if __has_include(<cstdalign>)
+#      include <cstdalign>
+#    endif
+#    include <cstdbool>
+#  endif
+#endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 08:27:54 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 08:27:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53b27a.c574b9b4.21ead0.eff7@mx.google.com>


https://github.com/arhwx edited https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 08:27:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 08:27:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53b27b.27b3b003.3b871.b179@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
----------------
arhwx wrote:

This guard is inverted; it wraps the declarations in `extern "C"` for C instead of C++.

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 08:27:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 08:27:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53b27b.c5504292.28b3b8.188f@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifndef __cplusplus
----------------
arhwx wrote:

Same inversion here.

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 08:27:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 08:27:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53b27b.7270d2fd.2d4879.454d@mx.google.com>


https://github.com/arhwx commented:

Just some early notes; nice to see someone working on freestanding :D

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 08:27:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 08:27:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53b27b.7270d2fd.2d4879.454b@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
----------------
arhwx wrote:

These declarations omit `strcoll`, `strxfrm`, and `strerror`, that is fine on Clang since `<cstring>` can handle it through `_LIBCPP_USING_IF_EXISTS`, but GCC does not support this attribute, so including `<cstring>` fails to compile.

Note I only checked `<string.h>`/`<cstring>`, but this can apply to the other headers as well; if Clang only is intentional, a clear `#error` for other compilers would explain the failure.

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 09:04:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 09:04:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53baf1.ff037943.2e959a.935d@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
----------------
trcrsired wrote:

i think gcc cannot even use libc++ headers at all because of lack of _LIBCPP_USING_IF_EXISTS. Better ask GCC team to add this instead of making changes here.

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 09:04:53 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 12 Jul 2026 09:04:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a53bb25.3cce8983.225732.ba78@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/207585

>From 02ae8f53a83139d9280f86098a7517a3036c7986 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 5 Jul 2026 14:17:28 +0100
Subject: [PATCH 1/5] Implementation of adjacent_difference on top of a binary
 transform

---
 libcxx/include/__numeric/pstl.h               |  39 ++++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  35 +++-
 .../pstl.iterator-requirements.verify.cpp     |  11 ++
 .../algorithms/pstl.nodiscard.verify.cpp      |   5 +
 .../pstl.exception_handling.pass.cpp          |  16 +-
 .../pstl.adjacent_difference.pass.cpp         | 171 +++++++++++++++++
 .../pstl.adjacent_difference_op.pass.cpp      | 181 ++++++++++++++++++
 8 files changed, 461 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp

diff --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h
index ecbc9f5348333..0b85cfb8c2327 100644
--- a/libcxx/include/__numeric/pstl.h
+++ b/libcxx/include/__numeric/pstl.h
@@ -165,6 +165,45 @@ template <class _ExecutionPolicy,
       std::move(__transform));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first1), std::move(__last1), std::move(__first2), minus{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryOperation,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _BinaryOperation __op) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__op));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a84a768cad570..2ba5b4434fc33 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -317,6 +317,13 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_difference;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+// optional<_ForwardIterator2>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5c78d4efa412b..f34c284dd2a84 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -92,11 +92,12 @@ namespace __pstl {
 //
 // transform and transform_binary family
 // -------------------------------------
-// - replace_copy_if
-// - replace_copy
-// - move
+// - adjacent_difference
 // - copy
 // - copy_n
+// - move
+// - replace_copy
+// - replace_copy_if
 // - reverse_copy
 // - rotate_copy
 //
@@ -575,6 +576,34 @@ struct __reverse_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _BinaryOperation&& __op) const noexcept {
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    if (__first1 == __last1)
+      return __first2; // edge case: empty input range, just return the output iterator
+    *__first2 = *__first1;
+    ++__first2;
+    _ForwardIterator1 __first_left = std::next(__first1);
+    if (__first_left == __last1)
+      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+    // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
+    return _TransformBinary()(
+        __policy,
+        std::move(__first_left),
+        std::move(__last1),
+        std::move(__first1),
+        std::move(__first2),
+        std::forward<_BinaryOperation>(__op));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index fbe3460b67f17..402d8fbf9131a 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -211,4 +211,15 @@ void f(non_forward_iterator non_fwd,
     (void)std::transform_reduce(
         pol, non_fwd, non_fwd, val, func, func); // expected-error@*:* {{static assertion failed: transform_reduce}}
   }
+
+  {
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+  }
 }
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 049c8dc88c2da..f4fffd456a331 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <algorithm>
 #include <execution>
 #include <iterator>
+#include <numeric>
 
 void test() {
   int a[]    = {1};
@@ -58,4 +59,8 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
 }
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 8af2a0a15fd5c..e00046fdb1dd6 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -85,7 +85,7 @@ int main(int, char**) {
       auto last1       = util::throw_on_move_iterator(std::end(a), tokens[1].active() ? 1 : -1);
       auto first2      = util::throw_on_move_iterator(std::begin(b), tokens[2].active() ? 1 : -1);
       auto last2       = util::throw_on_move_iterator(std::end(b), tokens[3].active() ? 1 : -1);
-      auto dest        = util::throw_on_move_iterator(std::end(storage), tokens[4].active() ? 1 : -1);
+      auto dest        = util::throw_on_move_iterator(std::begin(storage), tokens[4].active() ? 1 : -1);
       auto maybe_throw = [](ThrowToken const& token, auto f) {
         return [&token, f](auto... args) {
           if (token.active())
@@ -365,6 +365,20 @@ int main(int, char**) {
           (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction);
         });
       }
+
+      {
+        auto op = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
+
+        // adjacent_difference(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+
+        // adjacent_difference(first, last, dest, op)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest), op);
+        });
+      }
     }
   });
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
new file mode 100644
index 0000000000000..bb98004c1c62a
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+
+  friend X operator-(const X& x, const X& y) { return X(x.i_ - y.i_); }
+
+  friend class Y;
+};
+
+class Y {
+  int i_;
+
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 20};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, -5, -4, -3, -2};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r =
+            std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        assert(std::all_of(std::begin(ib), std::begin(ib) + i, [](int x) { return x == 1; }));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(7)};
+      Y ir[3] = {Y(1), Y(4), Y(2)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
new file mode 100644
index 0000000000000..26d65f9a77c10
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryOperation>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2,
+//                                        BinaryOperation op);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+  friend class Y;
+  friend struct Op;
+};
+
+class Y {
+  int i_;
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+// ensures that the predicate is not equivalent to std::minus
+struct Op {
+  int operator()(int l, int r) const { return l * 2 - r; }
+  X operator()(const X& l, const X& r) const { return X(l.i_ * 2 - r.i_); }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 50};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, 5, 2, 0, -1};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r = std::adjacent_difference(
+            policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)), Op{});
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j)
+          assert(ib[j] == (int)(j == 0 ? 1 : j + 2));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(8)};
+      Y ir[3] = {Y(1), Y(9), Y(11)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}

>From f62e25b47970412a33d6c44c4b4820ad3669b680 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 10:34:09 +0100
Subject: [PATCH 2/5] Renamed the variable names in __adjacent_difference

---
 libcxx/include/__pstl/backends/default.h | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index f34c284dd2a84..28bc75be147aa 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -583,23 +583,23 @@ struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
   operator()(_Policy&& __policy,
              _ForwardIterator1 __first1,
              _ForwardIterator1 __last1,
-             _ForwardIterator2 __first2,
+             _ForwardIterator2 __result,
              _BinaryOperation&& __op) const noexcept {
     using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
     if (__first1 == __last1)
-      return __first2; // edge case: empty input range, just return the output iterator
-    *__first2 = *__first1;
-    ++__first2;
-    _ForwardIterator1 __first_left = std::next(__first1);
-    if (__first_left == __last1)
-      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+      return __result; // edge case: empty input range, just return the output iterator
+    *__result = *__first1;
+    ++__result;
+    _ForwardIterator1 __first2 = std::next(__first1);
+    if (__first2 == __last1)
+      return __result; // edge case: not enough elements to perform adjacent difference, just return the output iterator
     // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
     return _TransformBinary()(
         __policy,
-        std::move(__first_left),
+        std::move(__first2),
         std::move(__last1),
         std::move(__first1),
-        std::move(__first2),
+        std::move(__result),
         std::forward<_BinaryOperation>(__op));
   }
 };

>From 012594a84a7b0bbc55c1e9457adf28531d01c97d Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 20:01:44 +0100
Subject: [PATCH 3/5] Moved runway_sample() to test/support/runway_sample.h

---
 .../pstl.adjacent_difference.pass.cpp         |  16 +--
 .../pstl.adjacent_difference_op.pass.cpp      |  16 +--
 libcxx/test/support/runway_sample.h           |  58 ++++++++++
 .../test.support/runway_sample.pass.cpp       | 101 ++++++++++++++++++
 4 files changed, 161 insertions(+), 30 deletions(-)
 create mode 100644 libcxx/test/support/runway_sample.h
 create mode 100644 libcxx/test/support/test.support/runway_sample.pass.cpp

diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
index bb98004c1c62a..4dfe3ad58adf8 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -29,27 +29,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
 
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 class X {
   int i_;
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
index 26d65f9a77c10..f6b9b480f4ba8 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -31,27 +31,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
 
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 class X {
   int i_;
   X& operator=(const X&);
diff --git a/libcxx/test/support/runway_sample.h b/libcxx/test/support/runway_sample.h
new file mode 100644
index 0000000000000..16ecd58cef747
--- /dev/null
+++ b/libcxx/test/support/runway_sample.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+#ifndef TEST_SUPPORT_RUNWAY_SAMPLE_H
+#define TEST_SUPPORT_RUNWAY_SAMPLE_H
+
+#include <cstddef>
+#include <algorithm>
+#include "test_macros.h"
+
+// runway_sample(size, callable) calls callable(i) for a set of sample indices i in the range [0, size).
+// they are chosen in 3 phases:
+//   - prefix, sampled densely with increments of 1
+//   - middle, sampled sparsely with uncanny numbers
+//   - suffix, sampled densely with increments of 1
+//
+// Examples of sample indices for various sizes:
+// size = 0:
+// <no samples>
+// size = 1:
+// 0
+// size = 10:
+// 0 1 2 3 4 5 6 7 8 9
+// size = 100:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
+// size = 1000:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 157 493 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
+template <class Callable>
+TEST_CONSTEXPR_CXX14 void runway_sample(std::size_t size, Callable callable) {
+  constexpr std::size_t affix = 16;
+  std::size_t i               = 0;
+
+  // 0, 1, 2, ..., 15
+  for (; i < std::min(size, affix); ++i) {
+    callable(i);
+  }
+
+  if (size <= affix) {
+    return;
+  }
+
+  // 16, 50, 157, 493, 1549, 4868, ...
+  for (std::size_t j = i; j + affix < size; i = j, j = j * 3 + j / 7) {
+    callable(j);
+  }
+
+  // size - 16, size - 15, ..., size - 1
+  i = std::max(i, size - affix);
+  for (; i < size; ++i) {
+    callable(i);
+  }
+}
+
+#endif // TEST_SUPPORT_RUNWAY_SAMPLE_H
diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
new file mode 100644
index 0000000000000..9fc288f881082
--- /dev/null
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++11
+
+// "support/runway_sample.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "runway_sample.h"
+
+TEST_CONSTEXPR_CXX14 bool test() {
+  {
+    bool fail = false;
+    runway_sample(0, [&](size_t /*i*/) { fail = true; });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(0, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(10, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29};
+    size_t n  = 0;
+    bool fail = false;
+    runway_sample(30, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
+                         50, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(100, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  50,
+                         157, 493, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(1000, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  return true;
+}
+
+int main() {
+  assert(test());
+#if TEST_STD_VER >= 14
+  static_assert(test());
+#endif
+  return 0;
+}

>From 1552e8b1db839c7356ba1679fda4bdfc669dd685 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 20:05:51 +0100
Subject: [PATCH 4/5] Added a note about X and Y in the tests

---
 .../adjacent.difference/pstl.adjacent_difference.pass.cpp     | 4 ++++
 .../adjacent.difference/pstl.adjacent_difference_op.pass.cpp  | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
index 4dfe3ad58adf8..d06f5b64e2f9f 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -36,6 +36,10 @@ EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
 
+// Types X and Y are provided to test adjacent_difference() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
 class X {
   int i_;
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
index f6b9b480f4ba8..d623cda058d04 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -38,6 +38,10 @@ EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
 
+// Types X and Y are provided to test adjacent_difference() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
 class X {
   int i_;
   X& operator=(const X&);

>From 40282b01dd8f83ff30534284abe9deddbf0e4136 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 21:06:40 +0100
Subject: [PATCH 5/5] Run runway_sample test on C++17+

---
 libcxx/test/support/test.support/runway_sample.pass.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
index 9fc288f881082..30e0ad20e0f85 100644
--- a/libcxx/test/support/test.support/runway_sample.pass.cpp
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// REQUIRES: std-at-least-c++11
+// REQUIRES: std-at-least-c++17
 
 // "support/runway_sample.h"
 
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "runway_sample.h"
 
-TEST_CONSTEXPR_CXX14 bool test() {
+constexpr bool test() {
   {
     bool fail = false;
     runway_sample(0, [&](size_t /*i*/) { fail = true; });
@@ -94,8 +94,6 @@ TEST_CONSTEXPR_CXX14 bool test() {
 
 int main() {
   assert(test());
-#if TEST_STD_VER >= 14
   static_assert(test());
-#endif
   return 0;
 }


From libcxx-commits at lists.llvm.org  Sun Jul 12 09:06:44 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 09:06:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53bb94.e55dc341.1f19bd.6be9@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 0efd33bda610a03a1514fe4b2171341b61698507 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH 1/2] [libc++] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   3 +
 libcxx/include/__config                       |   5 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   5 +
 libcxx/include/math.h                         |  37 ++++++
 libcxx/include/stdio.h                        |  15 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  43 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 291 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..bea314fc5473e 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -857,6 +858,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<float>&, float*>(fl
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(double*, double*, __less<double>&);
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
+#endif
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -230,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..a29f60c78ac5f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -57,6 +57,11 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..969462f22846c 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..534ceb8188428 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,10 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
@@ -118,4 +122,15 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..f03d8d256d252 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,50 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+size_t strnlen(const char*, size_t);           /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifndef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..2dc5d90b3b4ec
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#  include <iosfwd>
+#  include <cstdio>
+#  include <cerrno>
+#  include <cstring>
+#  include <cmath>
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#    include <cwchar>
+#  endif
+#  include <array>
+#  include <span>
+#  include <algorithm>
+#  include <bitset>
+#  include <variant>
+#  include <optional>
+#  include <numbers>
+#  include <scoped_allocator>
+#  include <typeindex>
+#  include <string_view>
+#  include <numeric>
+#  include <complex>
+#  include <chrono>
+#  include <charconv>
+#  include <expected>
+#  include <random>
+#  include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#  include <vector>
+#  include <deque>
+#  include <string>
+#  include <stack>
+#  include <queue>
+#  include <list>
+#  include <forward_list>
+#  include <map>
+#  include <set>
+#  include <unordered_set>
+#  include <unordered_map>
+#  if __has_include(<mdspan>)
+#    include <mdspan>
+#  endif
+#  include <valarray>
+
+#  if _LIBCPP_STD_VER < 20
+#    include <ciso646>
+#    if __has_include(<cstdalign>)
+#      include <cstdalign>
+#    endif
+#    include <cstdbool>
+#  endif
+#endif

>From 9f070dcdf1900f038d69ed3a45c7986372786c18 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Mon, 13 Jul 2026 00:04:52 +0800
Subject: [PATCH 2/2] [libc++] the guard reverted in string.h for extern "C"

---
 libcxx/include/string.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index f03d8d256d252..1951b53a042bc 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -64,7 +64,7 @@ size_t strlen(const char* s);
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
 #  else
-#    ifndef __cplusplus
+#    ifdef __cplusplus
 extern "C" {
 #    endif
 
@@ -100,7 +100,7 @@ size_t strlen(const char*);                    /* freestanding */
 size_t strnlen(const char*, size_t);           /* freestanding */
 void* memset_explicit(void*, int, size_t);     /* freestanding */
 
-#    ifndef __cplusplus
+#    ifdef __cplusplus
 }
 #    endif
 #  endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 09:08:08 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 09:08:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53bbe8.4987c784.63cbc.f32e@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From dce296b1e59c1926c9b416330a80f22c61e43625 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libc++] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   3 +
 libcxx/include/__config                       |   5 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   5 +
 libcxx/include/math.h                         |  37 ++++++
 libcxx/include/stdio.h                        |  15 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  43 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 291 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..bea314fc5473e 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -857,6 +858,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<float>&, float*>(fl
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(double*, double*, __less<double>&);
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
+#endif
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -230,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..a29f60c78ac5f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -57,6 +57,11 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..969462f22846c 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..534ceb8188428 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -102,6 +102,10 @@ void perror(const char* s);
 // times to get different definitions based on the macros that are set before inclusion.
 #if __has_include_next(<stdio.h>)
 #  include_next <stdio.h>
+#else
+#  ifndef EOF
+#    define EOF (-1)
+#  endif
 #endif
 
 #ifndef _LIBCPP_STDIO_H
@@ -118,4 +122,15 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..1951b53a042bc 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,50 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifdef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+size_t strnlen(const char*, size_t);           /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifdef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..2dc5d90b3b4ec
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#  include <iosfwd>
+#  include <cstdio>
+#  include <cerrno>
+#  include <cstring>
+#  include <cmath>
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#    include <cwchar>
+#  endif
+#  include <array>
+#  include <span>
+#  include <algorithm>
+#  include <bitset>
+#  include <variant>
+#  include <optional>
+#  include <numbers>
+#  include <scoped_allocator>
+#  include <typeindex>
+#  include <string_view>
+#  include <numeric>
+#  include <complex>
+#  include <chrono>
+#  include <charconv>
+#  include <expected>
+#  include <random>
+#  include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#  include <vector>
+#  include <deque>
+#  include <string>
+#  include <stack>
+#  include <queue>
+#  include <list>
+#  include <forward_list>
+#  include <map>
+#  include <set>
+#  include <unordered_set>
+#  include <unordered_map>
+#  if __has_include(<mdspan>)
+#    include <mdspan>
+#  endif
+#  include <valarray>
+
+#  if _LIBCPP_STD_VER < 20
+#    include <ciso646>
+#    if __has_include(<cstdalign>)
+#      include <cstdalign>
+#    endif
+#    include <cstdbool>
+#  endif
+#endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 09:08:58 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 09:08:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53bc1a.6c8668f2.e4f82.552c@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
----------------
trcrsired wrote:

Resolved. Thanks

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 09:09:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 09:09:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53bc26.160324b0.195192.8335@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifndef __cplusplus
----------------
trcrsired wrote:

resolved too

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 09:17:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 09:17:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53be06.bb839515.27d21e.0d48@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 44b38513555d154480c9a2f7d7ce4523bf6ceb4f Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libc++] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   3 +
 libcxx/include/__config                       |   5 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   5 +
 libcxx/include/math.h                         |  37 ++++++
 libcxx/include/stdio.h                        |  14 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  43 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 290 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..bea314fc5473e 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -857,6 +858,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<float>&, float*>(fl
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(double*, double*, __less<double>&);
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
+#endif
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -230,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..a29f60c78ac5f 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -57,6 +57,11 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+#    include <cstdint>
+extern "C" {
+typedef ::std::uint_least64_t time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..969462f22846c 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..35f1f74c67fb1 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -118,4 +118,18 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifndef EOF
+#      define EOF (-1)
+#    endif
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..1951b53a042bc 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,50 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifdef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+size_t strnlen(const char*, size_t);           /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifdef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..2dc5d90b3b4ec
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#  include <iosfwd>
+#  include <cstdio>
+#  include <cerrno>
+#  include <cstring>
+#  include <cmath>
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#    include <cwchar>
+#  endif
+#  include <array>
+#  include <span>
+#  include <algorithm>
+#  include <bitset>
+#  include <variant>
+#  include <optional>
+#  include <numbers>
+#  include <scoped_allocator>
+#  include <typeindex>
+#  include <string_view>
+#  include <numeric>
+#  include <complex>
+#  include <chrono>
+#  include <charconv>
+#  include <expected>
+#  include <random>
+#  include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#  include <vector>
+#  include <deque>
+#  include <string>
+#  include <stack>
+#  include <queue>
+#  include <list>
+#  include <forward_list>
+#  include <map>
+#  include <set>
+#  include <unordered_set>
+#  include <unordered_map>
+#  if __has_include(<mdspan>)
+#    include <mdspan>
+#  endif
+#  include <valarray>
+
+#  if _LIBCPP_STD_VER < 20
+#    include <ciso646>
+#    if __has_include(<cstdalign>)
+#      include <cstdalign>
+#    endif
+#    include <cstdbool>
+#  endif
+#endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 09:22:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 09:22:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53bf42.c574b9b4.21ead0.abfe@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 6904898f17c277dd89a2a978c493c6aca24d334c Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libc++] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   3 +
 libcxx/include/__config                       |   5 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   4 +
 libcxx/include/math.h                         |  37 ++++++
 libcxx/include/stdio.h                        |  14 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  43 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 289 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..bea314fc5473e 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -857,6 +858,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<float>&, float*>(fl
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(double*, double*, __less<double>&);
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
+#endif
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -230,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..3fb2c7d4a67fb 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -57,6 +57,10 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+extern "C" {
+typedef __UINT_LEAST64_TYPE__ time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..969462f22846c 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..35f1f74c67fb1 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -118,4 +118,18 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifndef EOF
+#      define EOF (-1)
+#    endif
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..1951b53a042bc 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,50 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifdef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+void* memchr(void*, int, size_t);              /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+char* strchr(char*, int);                      /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+char* strpbrk(char*, const char*);             /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+char* strrchr(char*, int);                     /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strstr(char*, const char*);              /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+size_t strnlen(const char*, size_t);           /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifdef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..2dc5d90b3b4ec
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#  include <iosfwd>
+#  include <cstdio>
+#  include <cerrno>
+#  include <cstring>
+#  include <cmath>
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#    include <cwchar>
+#  endif
+#  include <array>
+#  include <span>
+#  include <algorithm>
+#  include <bitset>
+#  include <variant>
+#  include <optional>
+#  include <numbers>
+#  include <scoped_allocator>
+#  include <typeindex>
+#  include <string_view>
+#  include <numeric>
+#  include <complex>
+#  include <chrono>
+#  include <charconv>
+#  include <expected>
+#  include <random>
+#  include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#  include <vector>
+#  include <deque>
+#  include <string>
+#  include <stack>
+#  include <queue>
+#  include <list>
+#  include <forward_list>
+#  include <map>
+#  include <set>
+#  include <unordered_set>
+#  include <unordered_map>
+#  if __has_include(<mdspan>)
+#    include <mdspan>
+#  endif
+#  include <valarray>
+
+#  if _LIBCPP_STD_VER < 20
+#    include <ciso646>
+#    if __has_include(<cstdalign>)
+#      include <cstdalign>
+#    endif
+#    include <cstdbool>
+#  endif
+#endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 09:32:30 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 09:32:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53c19e.92ea0e2a.13fd37.6a05@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From c466783d22dc4dc465dbec666b449ce00c6a8e84 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libc++] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   3 +
 libcxx/include/__config                       |   5 +
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   4 +
 libcxx/include/math.h                         |  37 ++++++
 libcxx/include/stdio.h                        |  14 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  38 +++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 284 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..bea314fc5473e 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -857,6 +858,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<float>&, float*>(fl
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(double*, double*, __less<double>&);
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
+#endif
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2d047676817d0 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0 && _LIBCPP_FREESTANDING == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -230,6 +234,7 @@ typedef __char32_t char32_t;
 #    elif defined(_LIBCPP_WIN32API)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
+#    elif _LIBCPP_FREESTANDING != 0
 #    else
 #      error "No thread API"
 #    endif // _LIBCPP_HAS_THREAD_API
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..3fb2c7d4a67fb 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -57,6 +57,10 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+extern "C" {
+typedef __UINT_LEAST64_TYPE__ time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..969462f22846c 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float);
+long double atan2l(long double, long double);
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..35f1f74c67fb1 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -118,4 +118,18 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifndef EOF
+#      define EOF (-1)
+#    endif
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*);
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..4d34b3251b10c 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y);
+lldiv_t lldiv(long long x, long long y);
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..d79efe51466e3 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,45 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifdef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void*, const void*, size_t);      /* freestanding */
+void* memmove(void*, const void*, size_t);     /* freestanding */
+char* strcpy(char*, const char*);              /* freestanding */
+char* strncpy(char*, const char*, size_t);     /* freestanding */
+char* strcat(char*, const char*);              /* freestanding */
+char* strncat(char*, const char*, size_t);     /* freestanding */
+int memcmp(const void*, const void*, size_t);  /* freestanding */
+int strcmp(const char*, const char*);          /* freestanding */
+int strncmp(const char*, const char*, size_t); /* freestanding */
+const void* memchr(const void*, int, size_t);  /* freestanding */
+const char* strchr(const char*, int);          /* freestanding */
+size_t strcspn(const char*, const char*);      /* freestanding */
+const char* strpbrk(const char*, const char*); /* freestanding */
+const char* strrchr(const char*, int);         /* freestanding */
+size_t strspn(const char*, const char*);       /* freestanding */
+const char* strstr(const char*, const char*);  /* freestanding */
+char* strtok(char*, const char*);              /* freestanding */
+void* memset(void*, int, size_t);              /* freestanding */
+size_t strlen(const char*);                    /* freestanding */
+size_t strnlen(const char*, size_t);           /* freestanding */
+void* memset_explicit(void*, int, size_t);     /* freestanding */
+
+#    ifdef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..2dc5d90b3b4ec
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#  include <iosfwd>
+#  include <cstdio>
+#  include <cerrno>
+#  include <cstring>
+#  include <cmath>
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#    include <cwchar>
+#  endif
+#  include <array>
+#  include <span>
+#  include <algorithm>
+#  include <bitset>
+#  include <variant>
+#  include <optional>
+#  include <numbers>
+#  include <scoped_allocator>
+#  include <typeindex>
+#  include <string_view>
+#  include <numeric>
+#  include <complex>
+#  include <chrono>
+#  include <charconv>
+#  include <expected>
+#  include <random>
+#  include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#  include <vector>
+#  include <deque>
+#  include <string>
+#  include <stack>
+#  include <queue>
+#  include <list>
+#  include <forward_list>
+#  include <map>
+#  include <set>
+#  include <unordered_set>
+#  include <unordered_map>
+#  if __has_include(<mdspan>)
+#    include <mdspan>
+#  endif
+#  include <valarray>
+
+#  if _LIBCPP_STD_VER < 20
+#    include <ciso646>
+#    if __has_include(<cstdalign>)
+#      include <cstdalign>
+#    endif
+#    include <cstdbool>
+#  endif
+#endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 10:26:02 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 10:26:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53ce2a.c56fb102.7c9cd.579c@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
----------------
arhwx wrote:

Libc++ can be used with GCC, and the macro won't expand to the attribute if it isn't supported, so it won't result in syntax errors; you can see the macro definition in libc++:
```cpp
#if __has_attribute(__using_if_exists__)
#  define _LIBCPP_USING_IF_EXISTS __attribute__((__using_if_exists__))
#else
#  define _LIBCPP_USING_IF_EXISTS
#endif
```

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 10:48:53 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 10:48:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53d385.0c05e68e.1e82f1.5a00@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
----------------
trcrsired wrote:

i will check build for gcc in the future. but first let's do this. you can build yourself and give me some feedbacks

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 12:55:26 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Sun, 12 Jul 2026 12:55:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a53f12e.f1c1923d.991d1.58d7@mx.google.com>


https://github.com/boomanaiden154 updated https://github.com/llvm/llvm-project/pull/208945

>From 20c1f414a5514766969069b9e42764db3b48f288 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sat, 11 Jul 2026 19:50:43 +0000
Subject: [PATCH 1/2] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h

Deprecated in 1bcf74006bcf528d14377173dba459759d35c961. Causes warnings
during compilation and will presumably become an error.
---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp      | 2 +-
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp        | 2 +-
 .../tools/clang_tidy_checks/header_exportable_declarations.cpp  | 2 +-
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp           | 2 +-
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp           | 2 +-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp      | 2 +-
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp     | 2 +-
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp       | 2 +-
 8 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index f1866179a3b68..64dca4a08d62f 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "abi_tag_on_virtual.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index e176b29670ce9..7ccf6e3a910af 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "empty_namespaces.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 8ec029f7dba2d..24f8c1bf8caf3 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "llvm/ADT/ArrayRef.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index f05470e47d3e7..cb0c6a33b8498 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "hide_from_abi.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index b6298eefdeb44..b94989ca8c788 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "abi_tag_on_virtual.hpp"
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..a7e3f547d3ed8 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "robust_against_adl.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index a608954aa2cc7..e94c95561f92f 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 24bacde6304bc..aa325d9ba378f 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "uglify_attributes.hpp"
 #include "utilities.hpp"

>From b39d6dcc1677d373375ea74079991b674d73d5c1 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sun, 12 Jul 2026 19:53:28 +0000
Subject: [PATCH 2/2] fix

---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp    | 4 ++++
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp      | 4 ++++
 .../clang_tidy_checks/header_exportable_declarations.cpp      | 4 ++++
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp         | 4 ++++
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp         | 4 +++-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp    | 4 ++++
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp   | 4 ++++
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp     | 4 ++++
 8 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index 64dca4a08d62f..a45cc441943b8 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "abi_tag_on_virtual.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index 7ccf6e3a910af..5ea8b89bf6fc8 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "empty_namespaces.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 24f8c1bf8caf3..66d75795dbf0f 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "llvm/ADT/ArrayRef.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index cb0c6a33b8498..75c9fe06b2420 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "hide_from_abi.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index b94989ca8c788..4724c04c08fd8 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,7 +7,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
-#include "clang-tidy/ClangTidyModule.h"
+#if CLANG_VERSION_MAJOR < 23
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "abi_tag_on_virtual.hpp"
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index a7e3f547d3ed8..b1d1377e38d4c 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "robust_against_adl.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index e94c95561f92f..3a60327d3117b 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index aa325d9ba378f..1b98f187c7c49 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "uglify_attributes.hpp"
 #include "utilities.hpp"


From libcxx-commits at lists.llvm.org  Sun Jul 12 12:57:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 12:57:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a53f18c.510f576d.1b3cb9.be73@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions cpp -- libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index a45cc4419..16445b3b8 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "abi_tag_on_virtual.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index 5ea8b89bf..53dc4b74a 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 66d75795d..bec3c1d2c 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "llvm/ADT/ArrayRef.h"
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index 75c9fe06b..b4b3f0b9c 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "hide_from_abi.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index 4724c04c0..93d195ee8 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -8,7 +8,7 @@
 
 #include "clang-tidy/ClangTidyModule.h"
 #if CLANG_VERSION_MAJOR < 23
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "abi_tag_on_virtual.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index b1d1377e3..4cdabf3de 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "robust_against_adl.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index 3a60327d3..919838abb 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 1b98f187c..7f86f8159 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "uglify_attributes.hpp"

``````````

</details>


https://github.com/llvm/llvm-project/pull/208945

From libcxx-commits at lists.llvm.org  Sun Jul 12 13:20:02 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 13:20:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a53f6f2.d1496011.26913c.5f94@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
----------------
arhwx wrote:

I built it locally now, hosted GCC is fine, but freestanding fails as expected:
```
$ cat main.cpp 
#include <cstring>

int main() {
  return 0;
}

$ clang++ -fsyntax-only -ffreestanding -nostdinc++ -nostdlibinc -I build/freestanding/include/c++/v1/ main.cpp # compiles fine
$ g++ -fsyntax-only -nostdinc++ -I build/freestanding/include/c++/v1/ main.cpp # hosted, compiles fine
$ g++ -fsyntax-only -ffreestanding -nostdinc -I build/freestanding/include/c++/v1/ -I /usr/lib/gcc/x86_64-pc-linux-gnu/15.2.1/include/ main.cpp
<cstring>:91:9: error: ‘strcoll’ has not been declared in ‘::’
<cstring>:92:9: error: ‘strxfrm’ has not been declared in ‘::’
<cstring>:102:9: error: ‘strerror’ has not been declared in ‘::’
```


https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 13:25:36 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Sun, 12 Jul 2026 13:25:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] avoid include Uppercase windows
	headers (PR #208903)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208903@github.com>
Message-ID: <6a53f840.c56fb102.7c9cd.9943@mx.google.com>


https://github.com/mstorsjo approved this pull request.

LGTM.

We generally (across all of LLVM) use all-lowercase forms to include Windows headers, which works the same across both mingw headers and official WinSDK. This, both because mingw is all lowercase, and because WinSDK isn't self-consistent with how it names/includes its own headers.

https://github.com/llvm/llvm-project/pull/208903

From libcxx-commits at lists.llvm.org  Sun Jul 12 13:50:01 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Sun, 12 Jul 2026 13:50:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a53fdf9.4987c784.63cbc.0c2e@mx.google.com>


https://github.com/boomanaiden154 updated https://github.com/llvm/llvm-project/pull/208945

>From 20c1f414a5514766969069b9e42764db3b48f288 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sat, 11 Jul 2026 19:50:43 +0000
Subject: [PATCH 1/3] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h

Deprecated in 1bcf74006bcf528d14377173dba459759d35c961. Causes warnings
during compilation and will presumably become an error.
---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp      | 2 +-
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp        | 2 +-
 .../tools/clang_tidy_checks/header_exportable_declarations.cpp  | 2 +-
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp           | 2 +-
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp           | 2 +-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp      | 2 +-
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp     | 2 +-
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp       | 2 +-
 8 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index f1866179a3b68..64dca4a08d62f 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "abi_tag_on_virtual.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index e176b29670ce9..7ccf6e3a910af 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "empty_namespaces.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 8ec029f7dba2d..24f8c1bf8caf3 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "llvm/ADT/ArrayRef.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index f05470e47d3e7..cb0c6a33b8498 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "hide_from_abi.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index b6298eefdeb44..b94989ca8c788 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "abi_tag_on_virtual.hpp"
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..a7e3f547d3ed8 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "robust_against_adl.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index a608954aa2cc7..e94c95561f92f 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 24bacde6304bc..aa325d9ba378f 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "uglify_attributes.hpp"
 #include "utilities.hpp"

>From b39d6dcc1677d373375ea74079991b674d73d5c1 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sun, 12 Jul 2026 19:53:28 +0000
Subject: [PATCH 2/3] fix

---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp    | 4 ++++
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp      | 4 ++++
 .../clang_tidy_checks/header_exportable_declarations.cpp      | 4 ++++
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp         | 4 ++++
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp         | 4 +++-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp    | 4 ++++
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp   | 4 ++++
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp     | 4 ++++
 8 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index 64dca4a08d62f..a45cc441943b8 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "abi_tag_on_virtual.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index 7ccf6e3a910af..5ea8b89bf6fc8 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "empty_namespaces.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 24f8c1bf8caf3..66d75795dbf0f 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "llvm/ADT/ArrayRef.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index cb0c6a33b8498..75c9fe06b2420 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "hide_from_abi.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index b94989ca8c788..4724c04c08fd8 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,7 +7,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
-#include "clang-tidy/ClangTidyModule.h"
+#if CLANG_VERSION_MAJOR < 23
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "abi_tag_on_virtual.hpp"
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index a7e3f547d3ed8..b1d1377e38d4c 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "robust_against_adl.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index e94c95561f92f..3a60327d3117b 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index aa325d9ba378f..1b98f187c7c49 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "uglify_attributes.hpp"
 #include "utilities.hpp"

>From f1cbae15a1ca638433098c7ef6251cf0b26f83c8 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sun, 12 Jul 2026 20:49:48 +0000
Subject: [PATCH 3/3] formatting

---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp    | 4 ++--
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp      | 4 ++--
 .../clang_tidy_checks/header_exportable_declarations.cpp      | 4 ++--
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp         | 4 ++--
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp         | 2 +-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp    | 4 ++--
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp   | 4 ++--
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp     | 4 ++--
 8 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index a45cc441943b8..16445b3b86f23 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "abi_tag_on_virtual.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index 5ea8b89bf6fc8..53dc4b74a965f 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 66d75795dbf0f..bec3c1d2cd120 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "llvm/ADT/ArrayRef.h"
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index 75c9fe06b2420..b4b3f0b9cfa68 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "hide_from_abi.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index 4724c04c08fd8..93d195ee89f27 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -8,7 +8,7 @@
 
 #include "clang-tidy/ClangTidyModule.h"
 #if CLANG_VERSION_MAJOR < 23
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "abi_tag_on_virtual.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index b1d1377e38d4c..4cdabf3de7c90 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "robust_against_adl.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index 3a60327d3117b..919838abba615 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 1b98f187c7c49..7f86f815918cc 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "uglify_attributes.hpp"


From libcxx-commits at lists.llvm.org  Sun Jul 12 15:05:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 15:05:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add a design document for
	Windows support (PR #202668)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/202668@github.com>
Message-ID: <6a540f95.bb839515.27d21e.9765@mx.google.com>


https://github.com/siradam7th updated https://github.com/llvm/llvm-project/pull/202668

>From 35f53b7d7a52991da9e2d5a2006448670c6e12e2 Mon Sep 17 00:00:00 2001
From: siradam7th <oss at afterback.gq>
Date: Sun, 12 Jul 2026 23:04:24 +0100
Subject: [PATCH] [libc++] add design document for Windows support (MSVC Mode)

---
 libcxx/docs/DesignDocs/WindowsSupport.rst | 22 ++++++++++++++++++++++
 libcxx/docs/index.rst                     |  1 +
 2 files changed, 23 insertions(+)
 create mode 100644 libcxx/docs/DesignDocs/WindowsSupport.rst

diff --git a/libcxx/docs/DesignDocs/WindowsSupport.rst b/libcxx/docs/DesignDocs/WindowsSupport.rst
new file mode 100644
index 0000000000000..7e1ec23a5852a
--- /dev/null
+++ b/libcxx/docs/DesignDocs/WindowsSupport.rst
@@ -0,0 +1,22 @@
+===============
+Windows support
+===============
+
+**This document only applies to the MSVC Mode.**
+
+Motivation:
+-----------
+
+Currently libc++ needs to link to the MSVC STL ``msvcprt`` library to implement part of the functionality on Windows. This dependency is counterintuitive since one standard library is depending on another standard library. Our goal is to remove this dependency by implementing the required functionality in libc++ itself.
+
+To achieve this, we have decided on the following:
+
+Goals:
+------
+
+- ``VCRuntime`` is the underlying ABI layer that libc++ will be using to implement the required functionality for Windows support.
+
+Non-Goals:
+----------
+
+- Interoperability with the MSVC STL in the case where both libraries are linked in the same binary is not a goal. For example if you link both libc++ and the MSVC STL in the same binary, a call to a function that needs to maintain internal state like ``std::set_new_handler`` from the libc++ side should not be expected to also change the MSVC STL internal state (since they are separate). We will be maintaining our own internal state for such functions, therefore any such functions should only be expected to have an effect on the libc++ side.
diff --git a/libcxx/docs/index.rst b/libcxx/docs/index.rst
index 38f77580d4530..796da1136584f 100644
--- a/libcxx/docs/index.rst
+++ b/libcxx/docs/index.rst
@@ -229,6 +229,7 @@ Design Documents
    DesignDocs/UnspecifiedBehaviorRandomization
    DesignDocs/VisibilityMacros
    DesignDocs/TimeZone
+   DesignDocs/WindowsSupport
 
 
 Build Bots and Test Coverage


From libcxx-commits at lists.llvm.org  Sun Jul 12 15:26:07 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 15:26:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix missing 'get_new_handler()'
 for Windows builds (PR #150182)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/150182@github.com>
Message-ID: <6a54147f.e624eb8d.39cd4e.d734@mx.google.com>


https://github.com/siradam7th updated https://github.com/llvm/llvm-project/pull/150182

>From 0ade859377ca907e3584d5908b781821c7f90c54 Mon Sep 17 00:00:00 2001
From: siradam7th <siradam7th at users.noreply.github.com>
Date: Wed, 23 Jul 2025 08:28:44 +0100
Subject: [PATCH 1/6] [libc++] fix missing 'get_new_handler()' for Windows
 builds

---
 libcxx/include/__new/new_handler.h | 4 ----
 libcxx/src/new_handler.cpp         | 4 +---
 2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/libcxx/include/__new/new_handler.h b/libcxx/include/__new/new_handler.h
index fc6aabec61b64..c4473c13fab21 100644
--- a/libcxx/include/__new/new_handler.h
+++ b/libcxx/include/__new/new_handler.h
@@ -15,9 +15,6 @@
 #  pragma GCC system_header
 #endif
 
-#if defined(_LIBCPP_ABI_VCRUNTIME)
-#  include <new.h>
-#else
 _LIBCPP_BEGIN_UNVERSIONED_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
@@ -27,6 +24,5 @@ _LIBCPP_EXPORTED_FROM_ABI new_handler get_new_handler() _NOEXCEPT;
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_UNVERSIONED_NAMESPACE_STD
-#endif // _LIBCPP_ABI_VCRUNTIME
 
 #endif // _LIBCPP___NEW_NEW_HANDLER_H
diff --git a/libcxx/src/new_handler.cpp b/libcxx/src/new_handler.cpp
index 49c21d85f5908..5124cb501c938 100644
--- a/libcxx/src/new_handler.cpp
+++ b/libcxx/src/new_handler.cpp
@@ -11,9 +11,7 @@
 #include "include/atomic_support.h"
 
 #if defined(_LIBCPP_ABI_MICROSOFT)
-#  if !defined(_LIBCPP_ABI_VCRUNTIME)
-#    define _LIBPCPP_DEFINE_NEW_HANDLER
-#  endif
+#  define _LIBPCPP_DEFINE_NEW_HANDLER
 #elif defined(LIBCXX_BUILDING_LIBCXXABI)
 // nothing to do, we use the one from libc++abi
 #elif defined(LIBCXXRT)

>From 77d4a12f1ebe642e5fd4317e128373725111db3b Mon Sep 17 00:00:00 2001
From: siradam7th <siradam7th at users.noreply.github.com>
Date: Wed, 23 Jul 2025 08:56:05 +0100
Subject: [PATCH 2/6] [libc++] remove 'FIXME' and 'XFAIL' for
 'get_new_handler()' test

---
 .../alloc.errors/set.new.handler/get_new_handler.pass.cpp  | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/get_new_handler.pass.cpp b/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/get_new_handler.pass.cpp
index c5725bde6e6c3..d20a65d8b6b48 100644
--- a/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/get_new_handler.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/get_new_handler.pass.cpp
@@ -8,13 +8,6 @@
 
 // test get_new_handler
 
-// FIXME: When libc++ is linked against vcruntime (i.e. the default config in
-// MSVC mode), the declarations of std::set_new_handler and std::get_new_handler
-// are provided by vcruntime/UCRT's new.h. However, that header actually only
-// declares set_new_handler - it's missing a declaration of get_new_handler.
-
-// XFAIL: msvc && stdlib=libc++
-
 #include <new>
 #include <cassert>
 

>From 8728d4e5ed62ad7767caf353d896fa5c1042f7f1 Mon Sep 17 00:00:00 2001
From: siradam7th <siradam7th at users.noreply.github.com>
Date: Sat, 13 Dec 2025 07:44:36 +0100
Subject: [PATCH 3/6] [libc++] implement 'set_new_handler' and
 'get_new_handler' for Windows

---
 libcxx/include/__new/global_new_delete.h    |  2 +-
 libcxx/include/__new/nothrow_t.h            |  2 +-
 libcxx/include/__new/placement_new_delete.h |  2 +-
 libcxx/src/new_handler.cpp                  | 28 ++++++++++++++++++---
 4 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__new/global_new_delete.h b/libcxx/include/__new/global_new_delete.h
index f31bac3730489..4e25295aa20fa 100644
--- a/libcxx/include/__new/global_new_delete.h
+++ b/libcxx/include/__new/global_new_delete.h
@@ -25,7 +25,7 @@
 #endif
 
 #if defined(_LIBCPP_ABI_VCRUNTIME)
-#  include <new.h>
+#  include <vcruntime_new.h>
 #else
 [[__nodiscard__]] _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz) _THROW_BAD_ALLOC;
 [[__nodiscard__]] _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz, const std::nothrow_t&) _NOEXCEPT
diff --git a/libcxx/include/__new/nothrow_t.h b/libcxx/include/__new/nothrow_t.h
index a099772123934..7d9e73648fad2 100644
--- a/libcxx/include/__new/nothrow_t.h
+++ b/libcxx/include/__new/nothrow_t.h
@@ -16,7 +16,7 @@
 #endif
 
 #if defined(_LIBCPP_ABI_VCRUNTIME)
-#  include <new.h>
+#  include <vcruntime_new.h>
 #else
 _LIBCPP_BEGIN_UNVERSIONED_NAMESPACE_STD
 struct nothrow_t {
diff --git a/libcxx/include/__new/placement_new_delete.h b/libcxx/include/__new/placement_new_delete.h
index 74692f36fd04c..041651a2a6ef9 100644
--- a/libcxx/include/__new/placement_new_delete.h
+++ b/libcxx/include/__new/placement_new_delete.h
@@ -17,7 +17,7 @@
 #endif
 
 #if defined(_LIBCPP_ABI_VCRUNTIME)
-#  include <new.h>
+#  include <vcruntime_new.h>
 #else
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void*
 operator new(std::size_t, void* __p) _NOEXCEPT {
diff --git a/libcxx/src/new_handler.cpp b/libcxx/src/new_handler.cpp
index 5124cb501c938..aeab6946a0ae3 100644
--- a/libcxx/src/new_handler.cpp
+++ b/libcxx/src/new_handler.cpp
@@ -24,11 +24,33 @@
 
 #if defined(_LIBPCPP_DEFINE_NEW_HANDLER)
 
-namespace std { // purposefully not versioned
-
 static constinit std::new_handler __new_handler = nullptr;
 
-new_handler set_new_handler(new_handler handler) noexcept { return __libcpp_atomic_exchange(&__new_handler, handler); }
+#  ifdef _LIBCPP_ABI_VCRUNTIME
+// to avoid including <new.h>
+using _new_h = int(__cdecl*)(size_t);
+extern "C" _new_h __cdecl _set_new_handler(_new_h);
+
+namespace {
+// adapter for _callnewh
+int __cdecl _new_handler_adapter(size_t) {
+  std::__libcpp_atomic_load (&__new_handler)();
+  return 1;
+}
+} // namespace
+#  endif
+
+namespace std { // purposefully not versioned
+
+new_handler set_new_handler(new_handler handler) noexcept {
+#  ifdef _LIBCPP_ABI_VCRUNTIME
+  auto old = __libcpp_atomic_exchange(&__new_handler, handler);
+  _set_new_handler(handler ? _new_handler_adapter : nullptr);
+  return old;
+#  else
+  return __libcpp_atomic_exchange(&__new_handler, handler);
+#  endif
+}
 
 new_handler get_new_handler() noexcept { return __libcpp_atomic_load(&__new_handler); }
 

>From 980484724b52b77f0b142dc6dba20f4675e009e7 Mon Sep 17 00:00:00 2001
From: siradam7th <siradam7th at users.noreply.github.com>
Date: Sat, 13 Dec 2025 16:21:42 +0100
Subject: [PATCH 4/6] [libc++] replace 'typedef' with a type alias

---
 libcxx/include/__new/new_handler.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__new/new_handler.h b/libcxx/include/__new/new_handler.h
index c4473c13fab21..fa5f29146e1b2 100644
--- a/libcxx/include/__new/new_handler.h
+++ b/libcxx/include/__new/new_handler.h
@@ -18,7 +18,7 @@
 _LIBCPP_BEGIN_UNVERSIONED_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-typedef void (*new_handler)();
+using new_handler = void (*)();
 _LIBCPP_EXPORTED_FROM_ABI new_handler set_new_handler(new_handler) _NOEXCEPT;
 _LIBCPP_EXPORTED_FROM_ABI new_handler get_new_handler() _NOEXCEPT;
 

>From 41d52e7bea9f6ee8680ee44acc9e319de4da621b Mon Sep 17 00:00:00 2001
From: siradam7th <siradam7th at users.noreply.github.com>
Date: Wed, 14 Jan 2026 08:43:56 +0100
Subject: [PATCH 5/6] [libc++] add test for 'set_new_handler' when an
 allocation fails

---
 .../set.new.handler/set_new_handler.pass.cpp  | 25 ++++++++++++++++---
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/set_new_handler.pass.cpp b/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/set_new_handler.pass.cpp
index b0becc9efe8c4..deefbf7460afb 100644
--- a/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/set_new_handler.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/set_new_handler.pass.cpp
@@ -6,20 +6,37 @@
 //
 //===----------------------------------------------------------------------===//
 
+// a test will allocate an impossible amount of memory on purpose
+// and it will trigger an expected failure
+// UNSUPPORTED: asan, msan, tsan
+
 // test set_new_handler
 
 #include <new>
 #include <cassert>
+#include <cstdlib>
+#include <limits>
+#include <climits>
 
 #include "test_macros.h"
 
 void f1() {}
 void f2() {}
+void f3() { std::exit(0); }
+
+int main(int, char**) {
+  assert(std::set_new_handler(f1) == 0);
+  assert(std::set_new_handler(f2) == f1);
 
-int main(int, char**)
-{
-    assert(std::set_new_handler(f1) == 0);
-    assert(std::set_new_handler(f2) == f1);
+  // note: this test must be the last test since it will trigger
+  // the new handler and exit on the first call to it to signal success
+  // else we would be stuck in the handler being called in a loop
+  std::set_new_handler(f3);
+  size_t max_size = std::numeric_limits<size_t>::max();
+  // cap size to (~4GB) on 32-bit, (~1Exabytes) on 64-bit
+  size_t alloc_size = max_size > UINT_MAX ? max_size / 16 : max_size - 1;
+  auto ptr          = new char[alloc_size]; // huge allocation
+  (void)ptr;
 
   return 0;
 }

>From b4507fcd485950363d39902670e4c77d8bfad8f0 Mon Sep 17 00:00:00 2001
From: siradam7th <siradam7th at users.noreply.github.com>
Date: Sun, 10 May 2026 12:40:53 +0100
Subject: [PATCH 6/6] [libc++] add release note for LLVM 23

---
 libcxx/docs/ReleaseNotes/23.rst | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index f7a8e5b04f1ea..a7a5421d52653 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -131,5 +131,9 @@ ABI Affecting Changes
 - ``std::exception_ptr`` now uses the same layout in MSVC mode as on other platforms, changing the size from two to one
   pointer.
 
+- ``std::set_new_handler`` and ``std::get_new_handler`` are now implemented by libc++ on Windows using ``VCRuntime``
+  directly instead of implicitly relying on ``msvcprt`` to implement them, therefore the global state of the handler
+  is no longer shared with ``msvcprt`` since libc++ now maintains its own internal state of the handler.
+
 Build System Changes
 --------------------


From libcxx-commits at lists.llvm.org  Sun Jul 12 16:02:05 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 16:02:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix missing 'get_new_handler()'
 for Windows builds (PR #150182)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/150182@github.com>
Message-ID: <6a541ced.eb165e5c.1817a5.c80c@mx.google.com>


https://github.com/siradam7th updated https://github.com/llvm/llvm-project/pull/150182

>From c1e976f6466c76c7aae8f158cf343c4873779b8a Mon Sep 17 00:00:00 2001
From: siradam7th <oss at afterback.gq>
Date: Mon, 13 Jul 2026 00:00:29 +0100
Subject: [PATCH 1/6] [libc++] fix missing 'get_new_handler()' for Windows
 builds

---
 libcxx/include/__new/new_handler.h | 4 ----
 libcxx/src/new_handler.cpp         | 4 +---
 2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/libcxx/include/__new/new_handler.h b/libcxx/include/__new/new_handler.h
index fc6aabec61b64..c4473c13fab21 100644
--- a/libcxx/include/__new/new_handler.h
+++ b/libcxx/include/__new/new_handler.h
@@ -15,9 +15,6 @@
 #  pragma GCC system_header
 #endif
 
-#if defined(_LIBCPP_ABI_VCRUNTIME)
-#  include <new.h>
-#else
 _LIBCPP_BEGIN_UNVERSIONED_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
@@ -27,6 +24,5 @@ _LIBCPP_EXPORTED_FROM_ABI new_handler get_new_handler() _NOEXCEPT;
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_UNVERSIONED_NAMESPACE_STD
-#endif // _LIBCPP_ABI_VCRUNTIME
 
 #endif // _LIBCPP___NEW_NEW_HANDLER_H
diff --git a/libcxx/src/new_handler.cpp b/libcxx/src/new_handler.cpp
index 49c21d85f5908..5124cb501c938 100644
--- a/libcxx/src/new_handler.cpp
+++ b/libcxx/src/new_handler.cpp
@@ -11,9 +11,7 @@
 #include "include/atomic_support.h"
 
 #if defined(_LIBCPP_ABI_MICROSOFT)
-#  if !defined(_LIBCPP_ABI_VCRUNTIME)
-#    define _LIBPCPP_DEFINE_NEW_HANDLER
-#  endif
+#  define _LIBPCPP_DEFINE_NEW_HANDLER
 #elif defined(LIBCXX_BUILDING_LIBCXXABI)
 // nothing to do, we use the one from libc++abi
 #elif defined(LIBCXXRT)

>From 2fcdec32f07061e2f96c858641693ca841bc8d39 Mon Sep 17 00:00:00 2001
From: siradam7th <oss at afterback.gq>
Date: Mon, 13 Jul 2026 00:00:30 +0100
Subject: [PATCH 2/6] [libc++] remove 'FIXME' and 'XFAIL' for
 'get_new_handler()' test

---
 .../alloc.errors/set.new.handler/get_new_handler.pass.cpp  | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/get_new_handler.pass.cpp b/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/get_new_handler.pass.cpp
index c5725bde6e6c3..d20a65d8b6b48 100644
--- a/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/get_new_handler.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/get_new_handler.pass.cpp
@@ -8,13 +8,6 @@
 
 // test get_new_handler
 
-// FIXME: When libc++ is linked against vcruntime (i.e. the default config in
-// MSVC mode), the declarations of std::set_new_handler and std::get_new_handler
-// are provided by vcruntime/UCRT's new.h. However, that header actually only
-// declares set_new_handler - it's missing a declaration of get_new_handler.
-
-// XFAIL: msvc && stdlib=libc++
-
 #include <new>
 #include <cassert>
 

>From e3ac94f62e8d23ad5d7210ab1094e665c4d576da Mon Sep 17 00:00:00 2001
From: siradam7th <oss at afterback.gq>
Date: Mon, 13 Jul 2026 00:00:31 +0100
Subject: [PATCH 3/6] [libc++] implement 'set_new_handler' and
 'get_new_handler' for Windows

---
 libcxx/include/__new/global_new_delete.h    |  2 +-
 libcxx/include/__new/nothrow_t.h            |  2 +-
 libcxx/include/__new/placement_new_delete.h |  2 +-
 libcxx/src/new_handler.cpp                  | 28 ++++++++++++++++++---
 4 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__new/global_new_delete.h b/libcxx/include/__new/global_new_delete.h
index f31bac3730489..4e25295aa20fa 100644
--- a/libcxx/include/__new/global_new_delete.h
+++ b/libcxx/include/__new/global_new_delete.h
@@ -25,7 +25,7 @@
 #endif
 
 #if defined(_LIBCPP_ABI_VCRUNTIME)
-#  include <new.h>
+#  include <vcruntime_new.h>
 #else
 [[__nodiscard__]] _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz) _THROW_BAD_ALLOC;
 [[__nodiscard__]] _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz, const std::nothrow_t&) _NOEXCEPT
diff --git a/libcxx/include/__new/nothrow_t.h b/libcxx/include/__new/nothrow_t.h
index a099772123934..7d9e73648fad2 100644
--- a/libcxx/include/__new/nothrow_t.h
+++ b/libcxx/include/__new/nothrow_t.h
@@ -16,7 +16,7 @@
 #endif
 
 #if defined(_LIBCPP_ABI_VCRUNTIME)
-#  include <new.h>
+#  include <vcruntime_new.h>
 #else
 _LIBCPP_BEGIN_UNVERSIONED_NAMESPACE_STD
 struct nothrow_t {
diff --git a/libcxx/include/__new/placement_new_delete.h b/libcxx/include/__new/placement_new_delete.h
index 74692f36fd04c..041651a2a6ef9 100644
--- a/libcxx/include/__new/placement_new_delete.h
+++ b/libcxx/include/__new/placement_new_delete.h
@@ -17,7 +17,7 @@
 #endif
 
 #if defined(_LIBCPP_ABI_VCRUNTIME)
-#  include <new.h>
+#  include <vcruntime_new.h>
 #else
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void*
 operator new(std::size_t, void* __p) _NOEXCEPT {
diff --git a/libcxx/src/new_handler.cpp b/libcxx/src/new_handler.cpp
index 5124cb501c938..aeab6946a0ae3 100644
--- a/libcxx/src/new_handler.cpp
+++ b/libcxx/src/new_handler.cpp
@@ -24,11 +24,33 @@
 
 #if defined(_LIBPCPP_DEFINE_NEW_HANDLER)
 
-namespace std { // purposefully not versioned
-
 static constinit std::new_handler __new_handler = nullptr;
 
-new_handler set_new_handler(new_handler handler) noexcept { return __libcpp_atomic_exchange(&__new_handler, handler); }
+#  ifdef _LIBCPP_ABI_VCRUNTIME
+// to avoid including <new.h>
+using _new_h = int(__cdecl*)(size_t);
+extern "C" _new_h __cdecl _set_new_handler(_new_h);
+
+namespace {
+// adapter for _callnewh
+int __cdecl _new_handler_adapter(size_t) {
+  std::__libcpp_atomic_load (&__new_handler)();
+  return 1;
+}
+} // namespace
+#  endif
+
+namespace std { // purposefully not versioned
+
+new_handler set_new_handler(new_handler handler) noexcept {
+#  ifdef _LIBCPP_ABI_VCRUNTIME
+  auto old = __libcpp_atomic_exchange(&__new_handler, handler);
+  _set_new_handler(handler ? _new_handler_adapter : nullptr);
+  return old;
+#  else
+  return __libcpp_atomic_exchange(&__new_handler, handler);
+#  endif
+}
 
 new_handler get_new_handler() noexcept { return __libcpp_atomic_load(&__new_handler); }
 

>From 49a671ee4f4a3e6f3a390f8317f3b3bc9c40c776 Mon Sep 17 00:00:00 2001
From: siradam7th <oss at afterback.gq>
Date: Mon, 13 Jul 2026 00:00:32 +0100
Subject: [PATCH 4/6] [libc++] replace 'typedef' with a type alias

---
 libcxx/include/__new/new_handler.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__new/new_handler.h b/libcxx/include/__new/new_handler.h
index c4473c13fab21..fa5f29146e1b2 100644
--- a/libcxx/include/__new/new_handler.h
+++ b/libcxx/include/__new/new_handler.h
@@ -18,7 +18,7 @@
 _LIBCPP_BEGIN_UNVERSIONED_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-typedef void (*new_handler)();
+using new_handler = void (*)();
 _LIBCPP_EXPORTED_FROM_ABI new_handler set_new_handler(new_handler) _NOEXCEPT;
 _LIBCPP_EXPORTED_FROM_ABI new_handler get_new_handler() _NOEXCEPT;
 

>From 49422e8680152e5c2e46aaec3fd0f7514f29c14b Mon Sep 17 00:00:00 2001
From: siradam7th <oss at afterback.gq>
Date: Mon, 13 Jul 2026 00:00:33 +0100
Subject: [PATCH 5/6] [libc++] add test for 'set_new_handler' when an
 allocation fails

---
 .../set.new.handler/set_new_handler.pass.cpp  | 25 ++++++++++++++++---
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/set_new_handler.pass.cpp b/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/set_new_handler.pass.cpp
index b0becc9efe8c4..deefbf7460afb 100644
--- a/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/set_new_handler.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/alloc.errors/set.new.handler/set_new_handler.pass.cpp
@@ -6,20 +6,37 @@
 //
 //===----------------------------------------------------------------------===//
 
+// a test will allocate an impossible amount of memory on purpose
+// and it will trigger an expected failure
+// UNSUPPORTED: asan, msan, tsan
+
 // test set_new_handler
 
 #include <new>
 #include <cassert>
+#include <cstdlib>
+#include <limits>
+#include <climits>
 
 #include "test_macros.h"
 
 void f1() {}
 void f2() {}
+void f3() { std::exit(0); }
+
+int main(int, char**) {
+  assert(std::set_new_handler(f1) == 0);
+  assert(std::set_new_handler(f2) == f1);
 
-int main(int, char**)
-{
-    assert(std::set_new_handler(f1) == 0);
-    assert(std::set_new_handler(f2) == f1);
+  // note: this test must be the last test since it will trigger
+  // the new handler and exit on the first call to it to signal success
+  // else we would be stuck in the handler being called in a loop
+  std::set_new_handler(f3);
+  size_t max_size = std::numeric_limits<size_t>::max();
+  // cap size to (~4GB) on 32-bit, (~1Exabytes) on 64-bit
+  size_t alloc_size = max_size > UINT_MAX ? max_size / 16 : max_size - 1;
+  auto ptr          = new char[alloc_size]; // huge allocation
+  (void)ptr;
 
   return 0;
 }

>From e9086142be6a1a395a6a74d52206446c43ac8fed Mon Sep 17 00:00:00 2001
From: siradam7th <oss at afterback.gq>
Date: Mon, 13 Jul 2026 00:00:34 +0100
Subject: [PATCH 6/6] [libc++] add release note for LLVM 23

---
 libcxx/docs/ReleaseNotes/23.rst | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index f7a8e5b04f1ea..a7a5421d52653 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -131,5 +131,9 @@ ABI Affecting Changes
 - ``std::exception_ptr`` now uses the same layout in MSVC mode as on other platforms, changing the size from two to one
   pointer.
 
+- ``std::set_new_handler`` and ``std::get_new_handler`` are now implemented by libc++ on Windows using ``VCRuntime``
+  directly instead of implicitly relying on ``msvcprt`` to implement them, therefore the global state of the handler
+  is no longer shared with ``msvcprt`` since libc++ now maintains its own internal state of the handler.
+
 Build System Changes
 --------------------


From libcxx-commits at lists.llvm.org  Sun Jul 12 16:26:24 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 12 Jul 2026 16:26:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] avoid include Uppercase windows
	headers (PR #208903)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208903@github.com>
Message-ID: <6a5422a0.6365b89b.8575e.0781@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/208903

From libcxx-commits at lists.llvm.org  Sun Jul 12 18:00:21 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 18:00:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as resolved (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a5438a5.13be6df8.145c.9476@mx.google.com>


inquisitivecrystal wrote:

Okay, actually, I think I'm going to need to rewrite the description. I realized that my initial version was unclear about some details, so I've been adding those in. It's clear now, but it's also dramatically harder to read than it needs to be. I think the best solution is to rewrite most of it. This is entirely my mistake, and has nothing to do with your edits, which were good.

I'll try to do that rewrite in the next couple days and post when I'm done so you can have another look.

https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Sun Jul 12 18:24:25 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 12 Jul 2026 18:24:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a543e49.13be6df8.145c.7e02@mx.google.com>


================
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
----------------
frederick-vs-ja wrote:

This line is unrelated. Please drop it.
```suggestion
```

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 18:24:26 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 12 Jul 2026 18:24:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a543e4a.1905e68e.18ad68.a112@mx.google.com>


================
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
+
+void reference_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<int&, Deleter>);
+}
+
+void abominable_function_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
+}
----------------
frederick-vs-ja wrote:

I think it would be clearer to have a single function to verify both cases.

```suggestion
void test() {
  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
  (void)sizeof(std::unique_ptr<int&, Deleter>);

  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
  (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
}
```

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 18:24:26 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 12 Jul 2026 18:24:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a543e4a.07d7ca98.9dee3.012a@mx.google.com>


================
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
----------------
frederick-vs-ja wrote:

```suggestion
// [unique.ptr.single.general]/1 (added by LWG4144)
// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
```

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 18:25:25 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 12 Jul 2026 18:25:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a543e85.13be6df8.145c.90e2@mx.google.com>


================
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
----------------
frederick-vs-ja wrote:

`typedef void Function();` also seems to be unrelated.

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 18:35:29 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 18:35:29 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a5440e1.27b3b003.3b871.fdf7@mx.google.com>


https://github.com/huihui15537226823 updated https://github.com/llvm/llvm-project/pull/208390

>From 70e8ac839667f8aba4aa74a2dd19614426f9754f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 24 Jun 2026 09:47:00 +0800
Subject: [PATCH 1/6] Add OHOS HDC runtimes CI support

---
 cmake/Modules/GetToolchainDirs.cmake          |  37 +-
 .../cmake/Modules/CompilerRTUtils.cmake       |   8 +-
 compiler-rt/cmake/builtin-config-ix.cmake     |   2 +-
 compiler-rt/cmake/crt-config-ix.cmake         |   2 +-
 compiler-rt/lib/builtins/crtbegin.c           |   9 +
 compiler-rt/lib/builtins/emutls.c             |   8 +-
 libcxx/CMakeLists.txt                         |  10 +-
 libcxx/cmake/config-ix.cmake                  |   2 +
 libcxx/include/__config                       |   5 +-
 libcxx/include/__cxx03/__config               |   5 +-
 .../__cxx03/__locale_dir/locale_base_api.h    |   2 +-
 libcxx/include/__locale_dir/locale_base_api.h |   2 +-
 libcxx/src/include/config_elast.h             |   2 +
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  48 ++
 libcxx/utils/ci/buildkite-pipeline.yml        |  27 +
 libcxx/utils/ci/run-buildbot                  |  87 ++++
 libcxx/utils/ci/vendor/ohos/hdc_run.py        | 472 ++++++++++++++++++
 libcxx/utils/ci/vendor/ohos/hdc_run_test.py   | 115 +++++
 libcxxabi/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |  30 ++
 libunwind/CMakeLists.txt                      |   7 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |  55 ++
 22 files changed, 910 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/test/configs/llvm-libc++-ohos.cfg.in
 create mode 100755 libcxx/utils/ci/vendor/ohos/hdc_run.py
 create mode 100644 libcxx/utils/ci/vendor/ohos/hdc_run_test.py
 create mode 100644 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
 create mode 100644 libunwind/test/configs/llvm-libunwind-ohos.cfg.in

diff --git a/cmake/Modules/GetToolchainDirs.cmake b/cmake/Modules/GetToolchainDirs.cmake
index 7568734e3f25d..1e8a3e4e015ca 100644
--- a/cmake/Modules/GetToolchainDirs.cmake
+++ b/cmake/Modules/GetToolchainDirs.cmake
@@ -79,7 +79,15 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   string(FIND "${default_target_triple}" "-" dash_index)
   string(SUBSTRING "${default_target_triple}" "${dash_index}" -1 triple_suffix)
   string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
-  if(ANDROID AND "${arch}" STREQUAL "i386")
+  if("${triple_suffix}" MATCHES "linux-ohos")
+    if("${arch}" STREQUAL "i386")
+      set(target "i686-linux-ohos")
+    elseif("${arch}" MATCHES "^arm")
+      set(target "arm-linux-ohos")
+    else()
+      set(target "${arch}-linux-ohos")
+    endif()
+  elseif(ANDROID AND "${arch}" STREQUAL "i386")
     set(target "i686${triple_suffix}")
   elseif("${arch}" STREQUAL "amd64")
     set(target "x86_64${triple_suffix}")
@@ -125,25 +133,26 @@ function(get_runtimes_target_libdir_common default_target_triple arch variable)
   set("${variable}" "${target}" PARENT_SCOPE)
 endfunction()
 
-
-# Corresponds to Clang's ToolChain::getRuntimePath().
-function (get_toolchain_target_dirname outvar)
-  string(FIND "${LLVM_TARGET_TRIPLE}" "-" dash_index)
+function(get_runtimes_target_dirname default_target_triple variable)
+  string(FIND "${default_target_triple}" "-" dash_index)
   if (dash_index EQUAL "-1")
-    # This means LLVM_TARGET_TRIPLE is not set and we cannot derive the dirname
-    # from it. The proper behavior here would be to emit an error since we have
-    # no target we can build for. However, compiler-rt uses
-    # COMPILER_RT_DEFAULT_TARGET_TRIPLE instead and ignores LLVM_TARGET_TRIPLE.
-    # To not break the build when building only compiler-rt, we skip the triple
-    # subdirectory.
     set(target "")
   else ()
-    string(SUBSTRING "${LLVM_TARGET_TRIPLE}" 0 "${dash_index}" triple_cpu)
+    string(SUBSTRING "${default_target_triple}" 0 "${dash_index}" triple_cpu)
     set(arch "${triple_cpu}")
     if("${arch}" MATCHES "^i.86$")
       set(arch "i386")
     endif()
-    get_runtimes_target_libdir_common("${LLVM_TARGET_TRIPLE}" "${arch}" target)
-  endif ()
+    get_runtimes_target_libdir_common("${default_target_triple}" "${arch}" target)
+  endif()
+  set("${variable}" "${target}" PARENT_SCOPE)
+endfunction()
+
+
+# Corresponds to Clang's ToolChain::getRuntimePath().
+function (get_toolchain_target_dirname outvar)
+  # If LLVM_TARGET_TRIPLE is unset, this intentionally returns an empty
+  # directory to preserve the historical compiler-rt-only behavior.
+  get_runtimes_target_dirname("${LLVM_TARGET_TRIPLE}" target)
   set("${outvar}" "${target}" PARENT_SCOPE)
 endfunction()
diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
index 75216ed704bce..af55b2166de60 100644
--- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
+++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
@@ -491,8 +491,12 @@ endfunction()
 
 function(get_compiler_rt_target arch variable)
   if(COMPILER_RT_DEFAULT_TARGET_ONLY)
-    # Use exact spelling when building only for the target specified to CMake.
-    set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+      get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
+    else()
+      # Use exact spelling when building only for the target specified to CMake.
+      set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+    endif()
   else()
     get_runtimes_target_libdir_common("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" "${arch}" target)
   endif()
diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake
index 528268da729fb..90d9e451ff109 100644
--- a/compiler-rt/cmake/builtin-config-ix.cmake
+++ b/compiler-rt/cmake/builtin-config-ix.cmake
@@ -297,7 +297,7 @@ else()
   endforeach()
 endif()
 
-if(OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER AND NOT
+if(OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER AND NOT
    COMPILER_RT_GPU_BUILD)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake
index ebc7d671e74ee..64112f1636b09 100644
--- a/compiler-rt/cmake/crt-config-ix.cmake
+++ b/compiler-rt/cmake/crt-config-ix.cmake
@@ -47,7 +47,7 @@ if(NOT APPLE)
   message(STATUS "Supported architectures for crt: ${CRT_SUPPORTED_ARCH}")
 endif()
 
-if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS" AND NOT LLVM_USE_SANITIZER)
+if (CRT_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|SerenityOS|OHOS" AND NOT LLVM_USE_SANITIZER)
   set(COMPILER_RT_HAS_CRT TRUE)
 else()
   set(COMPILER_RT_HAS_CRT FALSE)
diff --git a/compiler-rt/lib/builtins/crtbegin.c b/compiler-rt/lib/builtins/crtbegin.c
index 74becdbe5dd0e..381a69fac4664 100644
--- a/compiler-rt/lib/builtins/crtbegin.c
+++ b/compiler-rt/lib/builtins/crtbegin.c
@@ -36,6 +36,10 @@ extern fp __CTOR_LIST_END__[];
 
 extern void __cxa_finalize(void *) __attribute__((weak));
 
+#if defined(__OHOS__)
+extern void __at_fini() __attribute__((weak));
+#endif
+
 static void __attribute__((used)) __do_init(void) {
   static _Bool __initialized;
   if (__builtin_expect(__initialized, 0))
@@ -114,6 +118,11 @@ static void __attribute__((used)) __do_fini(void) {
     return;
   __finalized = 1;
 
+#if defined(__OHOS__)
+  if (__at_fini)
+    __at_fini();
+#endif
+
   if (__cxa_finalize)
     __cxa_finalize(__dso_handle);
 
diff --git a/compiler-rt/lib/builtins/emutls.c b/compiler-rt/lib/builtins/emutls.c
index a8a5d7c0749a8..2d15d8e249f43 100644
--- a/compiler-rt/lib/builtins/emutls.c
+++ b/compiler-rt/lib/builtins/emutls.c
@@ -12,7 +12,7 @@
 
 #include "int_lib.h"
 
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 // There are 4 pthread key cleanup rounds on Bionic. Delay emutls deallocation
 // to round 2. We need to delay deallocation because:
 //  - Android versions older than M lack __cxa_thread_atexit_impl, so apps
@@ -51,7 +51,7 @@ static void emutls_shutdown(emutls_address_array *array);
 
 static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_key_t emutls_pthread_key;
-#ifdef __BIONIC__
+#if defined(__BIONIC__) || defined(__OHOS__)
 static bool emutls_key_created = false;
 #endif
 
@@ -401,8 +401,8 @@ void *__emutls_get_address(__emutls_control *control) {
   return array->data[index];
 }
 
-#ifdef __BIONIC__
-// Called by Bionic on dlclose to delete the emutls pthread key.
+#if defined(__BIONIC__) || defined(__OHOS__)
+// Called by Bionic/OHOS on dlclose to delete the emutls pthread key.
 __attribute__((visibility("hidden"))) void __emutls_unregister_key(void) {
   if (emutls_key_created) {
     pthread_key_delete(emutls_pthread_key);
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..9a1af0ced8965 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -277,7 +277,8 @@ option(LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
 # or we don't have any ABI library.
 if (LIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY
     OR NOT LIBCXX_ENABLE_SHARED
-    OR LIBCXX_CXX_ABI STREQUAL "none")
+    OR LIBCXX_CXX_ABI STREQUAL "none"
+    OR CMAKE_SYSTEM_NAME STREQUAL "OHOS")
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE OFF)
 elseif((UNIX OR FUCHSIA) AND NOT APPLE)
   set(ENABLE_LINKER_SCRIPT_DEFAULT_VALUE ON)
@@ -450,7 +451,12 @@ set(LIBCXX_STATIC_OUTPUT_NAME "c++" CACHE STRING "Output name for the static lib
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXX_TARGET_SUBDIR)
+  else()
+    set(LIBCXX_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXX_LIBDIR_SUBDIR)
     string(APPEND LIBCXX_TARGET_SUBDIR /${LIBCXX_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..e0e5a3c3094d2 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -126,6 +126,8 @@ elseif(PICOLIBC)
   set(LIBCXX_HAS_PTHREAD_LIB NO)
   set(LIBCXX_HAS_RT_LIB NO)
   set(LIBCXX_HAS_ATOMIC_LIB NO)
+elseif(OHOS)
+  set(LIBCXX_HAS_ATOMIC_LIB NO)
 else()
   check_library_exists(pthread pthread_create "" LIBCXX_HAS_PTHREAD_LIB)
   check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index fdd0558fbec6f..c7da0ec54447c 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -292,7 +292,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
@@ -371,7 +372,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC
+      _LIBCPP_HAS_MUSL_LIBC || defined(__OpenBSD__) || _LIBCPP_LIBC_LLVM_LIBC || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__config b/libcxx/include/__cxx03/__config
index 27c26084da4b1..d9078a4ddf0d0 100644
--- a/libcxx/include/__cxx03/__config
+++ b/libcxx/include/__cxx03/__config
@@ -680,7 +680,8 @@ typedef __char32_t char32_t;
         defined(__APPLE__) ||                                                                                          \
         defined(__MVS__) ||                                                                                            \
         defined(_AIX) ||                                                                                               \
-        defined(__EMSCRIPTEN__)
+        defined(__EMSCRIPTEN__) ||                                                                                     \
+        defined(__OHOS_FAMILY__)
 // clang-format on
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD
 #    elif defined(__Fuchsia__)
@@ -753,7 +754,7 @@ typedef __char32_t char32_t;
 #  endif
 
 #  if defined(__BIONIC__) || defined(__NuttX__) || defined(__Fuchsia__) || defined(__wasi__) ||                        \
-      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__)
+      defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OpenBSD__) || defined(__OHOS_FAMILY__)
 #    define _LIBCPP_PROVIDES_DEFAULT_RUNE_TABLE
 #  endif
 
diff --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
index 3dbce825bc76c..a107aeeae5da4 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api.h
@@ -23,7 +23,7 @@
 #  include <__cxx03/__locale_dir/locale_base_api/openbsd.h>
 #elif defined(__Fuchsia__)
 #  include <__cxx03/__locale_dir/locale_base_api/fuchsia.h>
-#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC)
+#elif defined(__wasi__) || defined(_LIBCPP_HAS_MUSL_LIBC) || defined(__OHOS_FAMILY__)
 #  include <__cxx03/__locale_dir/locale_base_api/musl.h>
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 #  include <__cxx03/xlocale.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index e1e60e18fd7c6..7dbbbd0b11654 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -120,7 +120,7 @@
 #    include <__locale_dir/support/fuchsia.h>
 #  elif _LIBCPP_LIBC_LLVM_LIBC
 #    include <__locale_dir/support/llvm_libc.h>
-#  elif defined(__linux__)
+#  elif defined(__linux__) || defined(__OHOS_FAMILY__)
 #    include <__locale_dir/support/linux.h>
 #  elif _LIBCPP_LIBC_NEWLIB
 #    include <__locale_dir/support/newlib.h>
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..328a77d9934a5 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -43,6 +43,8 @@
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
+#elif defined(__OHOS_FAMILY__)
+// No _LIBCPP_ELAST needed on OHOS
 #else
 // Warn here so that the person doing the libcxx port has an easier time:
 #  warning ELAST for this platform not yet implemented
diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
new file mode 100644
index 0000000000000..a20ed9ce9c6a9
--- /dev/null
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -0,0 +1,48 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++ using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+))
+config.substitutions.append(('%{link_flags}',
+    # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
+    # DT_NEEDED entries when building the test executable.
+    '-nostdlib++ -L %{lib-dir} -l at LIBCXX_SHARED_OUTPUT_NAME@ -lc++abi -lunwind'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('LIBCXX-OHOS-FIXME')
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
+
+# The generic libc++ hardening-mode feature probe executes a target binary via
+# %{exec}. On OHOS/HDC that probe can fail for runner reasons even though the
+# configured libc++ headers still expose the selected hardening mode. Preserve
+# the configured mode so UNSUPPORTED/REQUIRES annotations remain stable.
+detected_hardening_features = [
+    feature for feature in config.available_features
+    if feature.startswith('libcpp-hardening-mode=')
+]
+configured_hardening_mode = '@LIBCXX_HARDENING_MODE@'
+if (
+    not detected_hardening_features and
+    configured_hardening_mode in ('none', 'fast', 'extensive', 'debug')
+):
+    config.available_features.add(f'libcpp-hardening-mode={configured_hardening_mode}')
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index d4107cf11ad51..77617f43ae6dc 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -172,3 +172,30 @@ steps:
       queue: libcxx-builders
       os: android
     <<: *common
+
+- group: OHOS
+  steps:
+  - label: OHOS AArch64 HDC Smoke
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
+  - label: OHOS AArch64 HDC Full
+    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
+    env:
+      CC: clang
+      CXX: clang++
+      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
+      HDC_TIMEOUT_SECONDS: 240
+    agents:
+      queue: libcxx-builders
+      os: linux
+      device: ohos-hdc
+    <<: *common
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 6376bc1d3dd5a..1867e9c620649 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -116,6 +116,41 @@ function clean() {
     rm -rf "${BUILD_DIR}"
 }
 
+function require-env-var() {
+    local name="${1}"
+    if [[ -z ${!name+x} ]] || [[ -z "${!name}" ]]; then
+        error "Environment variable ${name} must be defined"
+        exit 1
+    fi
+}
+
+function detect-lit() {
+    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
+        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
+    elif command -v llvm-lit >/dev/null 2>&1; then
+        LIT_RUNNER=("$(command -v llvm-lit)")
+    else
+        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
+    fi
+}
+
+function run-smoke-test() {
+    local test_dir="${1}"
+    local filter="${2}"
+    step "Running smoke test ${filter}"
+    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
+}
+
+function ohos-lit-args() {
+    local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
+    if python3 -c 'import psutil' >/dev/null 2>&1; then
+        args="${args} --timeout=1500"
+    else
+        echo "warning: python3 psutil not available; running OHOS lit without per-test timeout" >&2
+    fi
+    printf '%s' "${args}"
+}
+
 function generate-cmake-base() {
     step "Generating CMake"
 
@@ -153,6 +188,37 @@ function generate-cmake-android() {
           "${@}"
 }
 
+function setup-ohos-hdc-env() {
+    require-env-var OHOS_SYSROOT
+
+    OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+}
+
+function generate-cmake-ohos-hdc() {
+    setup-ohos-hdc-env
+
+    local lit_args
+    lit_args="$(ohos-lit-args)"
+
+    generate-cmake \
+          -DLLVM_LIT_ARGS="${lit_args}" \
+          -DCMAKE_SYSTEM_NAME=OHOS \
+          -DCMAKE_SYSROOT="${OHOS_SYSROOT}" \
+          -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
+          -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
+          -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
+          -DLIBUNWIND_TEST_CONFIG="llvm-libunwind-ohos.cfg.in" \
+          -DLIBCXX_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBCXXABI_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          -DLIBUNWIND_TEST_PARAMS="${OHOS_LIT_PARAMS}" \
+          "${@}"
+}
+
 function check-runtimes() {
     step "Building libc++ test dependencies"
     ninja -vC "${BUILD_DIR}" cxx-test-depends
@@ -795,6 +861,27 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
+ohos-hdc-aarch64-full)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    check-runtimes
+;;
+ohos-hdc-aarch64-smoke)
+    clean
+
+    generate-cmake-ohos-hdc
+
+    step "Building the OHOS smoke test dependencies"
+    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
+
+    detect-lit
+    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
+    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
+;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run.py b/libcxx/utils/ci/vendor/ohos/hdc_run.py
new file mode 100755
index 0000000000000..61a3899b88b11
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run.py
@@ -0,0 +1,472 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import uuid
+
+
+EXIT_MARKER = "__llvm_hdc_exit__="
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _split_path_list(value):
+    return [entry for entry in value.split(os.pathsep) if entry]
+
+
+def _resolve_hdc_binary(hdc):
+    if os.path.isabs(hdc):
+        return hdc
+    resolved = shutil.which(hdc)
+    if resolved is None:
+        sys.exit(f"failed to find hdc binary: {hdc}")
+    return resolved
+
+
+def _is_windows_hdc(path):
+    return path.lower().endswith(".exe")
+
+
+def _find_windows_local_root(hdc_path):
+    parts = Path(hdc_path).parts
+    for index in range(len(parts) - 1):
+        if parts[index] == "AppData" and index + 1 < len(parts) and parts[index + 1] == "Local":
+            return Path(*parts[: index + 2])
+    return None
+
+
+def _default_host_staging_root(hdc_path):
+    if _is_windows_hdc(hdc_path):
+        local_root = _find_windows_local_root(hdc_path)
+        if local_root is None:
+            sys.exit(
+                "failed to derive a Windows staging directory from the hdc.exe path; "
+                "set HDC_HOST_STAGING_ROOT to a path under /mnt/<drive>/..."
+            )
+        return local_root / "Temp" / "llvm-lit-hdc"
+    return Path("/tmp/llvm-lit-hdc")
+
+
+def _to_windows_path(path):
+    resolved = Path(path).resolve()
+    parts = resolved.parts
+    if len(parts) < 4 or parts[1] != "mnt":
+        sys.exit(
+            f"Windows hdc requires a staging path under /mnt/<drive>/..., got: {resolved}"
+        )
+    drive = parts[2].upper()
+    tail = "\\".join(parts[3:])
+    return f"{drive}:\\{tail}" if tail else f"{drive}:\\"
+
+
+def _run_readelf(path):
+    completed = subprocess.run(
+        ["readelf", "-d", path],
+        check=False,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        text=True,
+    )
+    if completed.returncode != 0:
+        return ""
+    return completed.stdout
+
+
+def _read_elf_metadata(path):
+    needed = []
+    runpath_dirs = []
+    seen_dirs = set()
+    for line in _run_readelf(path).splitlines():
+        needed_match = re.search(r"\(NEEDED\).*Shared library: \[(.*)\]", line)
+        if needed_match:
+            needed.append(needed_match.group(1))
+            continue
+
+        runpath_match = re.search(
+            r"\((?:RUNPATH|RPATH)\).*Library runpath: \[(.*)\]", line
+        )
+        if not runpath_match:
+            continue
+        for entry in _split_path_list(runpath_match.group(1)):
+            candidate = Path(entry)
+            if not candidate.is_dir():
+                continue
+            key = str(candidate.resolve())
+            if key in seen_dirs:
+                continue
+            seen_dirs.add(key)
+            runpath_dirs.append(candidate)
+    return needed, runpath_dirs
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(parent)
+    return library_dirs
+
+
+def _find_dependency(name, search_dirs):
+    for directory in search_dirs:
+        candidate = Path(directory) / name
+        if candidate.is_file():
+            return candidate.resolve()
+    return None
+
+
+def _copy_needed_libraries(executables, execdir, staging_execdir):
+    search_dirs = _collect_build_library_dirs(execdir)
+    queue = [Path(exe).resolve() for exe in executables]
+    visited = set()
+    copied_names = {path.name for path in staging_execdir.glob("*") if path.is_file()}
+
+    while queue:
+        current = queue.pop(0)
+        current_key = str(current)
+        if current_key in visited:
+            continue
+        visited.add(current_key)
+
+        needed, runpath_dirs = _read_elf_metadata(current)
+        current_search_dirs = []
+        current_seen = set()
+        for directory in [*runpath_dirs, current.parent, *search_dirs]:
+            resolved = Path(directory).resolve()
+            key = str(resolved)
+            if key in current_seen:
+                continue
+            current_seen.add(key)
+            current_search_dirs.append(resolved)
+
+        for directory in runpath_dirs:
+            resolved = directory.resolve()
+            if all(str(resolved) != str(existing) for existing in search_dirs):
+                search_dirs.append(resolved)
+
+        for lib_name in needed:
+            dependency = _find_dependency(lib_name, current_search_dirs)
+            if dependency is None:
+                continue
+            if lib_name not in copied_names:
+                shutil.copy2(dependency, staging_execdir / lib_name)
+                copied_names.add(lib_name)
+            queue.append(dependency)
+
+
+def _copy_execdir(execdir, staging_execdir):
+    if staging_execdir.exists():
+        shutil.rmtree(staging_execdir)
+    shutil.copytree(execdir, staging_execdir)
+
+
+def _iter_files(root):
+    for directory, dirnames, filenames in os.walk(root):
+        dirnames.sort()
+        filenames.sort()
+        yield Path(directory), filenames
+
+
+def _is_test_executable(path):
+    return path.endswith(".tmp.exe") and os.path.exists(path)
+
+
+def _map_local_path(path, local_execdir, remote_execdir):
+    normalized = os.path.normpath(path)
+    execdir = os.path.normpath(local_execdir)
+    if normalized == execdir:
+        return remote_execdir
+    prefix = execdir + os.sep
+    if normalized.startswith(prefix):
+        suffix = normalized[len(prefix) :].replace(os.sep, "/")
+        return f"{remote_execdir}/{suffix}" if suffix else remote_execdir
+    return path
+
+
+def _map_value(value, local_execdir, remote_execdir):
+    return _map_local_path(value, local_execdir, remote_execdir)
+
+
+def _build_env_exports(env, prepend_env, local_execdir, remote_execdir, add_remote_libdir):
+    commands = []
+    mapped_env = {
+        key: _map_value(value, local_execdir, remote_execdir) for key, value in env.items()
+    }
+    mapped_prepend = {
+        key: _map_value(value, local_execdir, remote_execdir)
+        for key, value in prepend_env.items()
+    }
+
+    if add_remote_libdir:
+        existing = mapped_prepend.get("LD_LIBRARY_PATH")
+        mapped_prepend["LD_LIBRARY_PATH"] = (
+            f"{remote_execdir}:{existing}" if existing else remote_execdir
+        )
+
+    for key, value in mapped_env.items():
+        commands.append(f"export {key}={shlex.quote(value)}")
+    for key, value in mapped_prepend.items():
+        commands.append(f"export {key}={shlex.quote(value)}${{{key}:+:{key}}}")
+    return commands
+
+
+def _hdc_prefix(hdc_path, server, target):
+    prefix = [hdc_path]
+    if server:
+        prefix.extend(["-s", server])
+    if target:
+        prefix.extend(["-t", target])
+    return prefix
+
+
+def _run_hdc(
+    prefix,
+    args,
+    *,
+    timeout=None,
+    capture_output=False,
+    stdin=subprocess.DEVNULL,
+    check=True,
+    expect_stdout=None,
+):
+    kwargs = {
+        "check": False,
+        "timeout": timeout,
+        "stdin": stdin,
+        "text": True,
+    }
+    if capture_output:
+        kwargs["stdout"] = subprocess.PIPE
+        kwargs["stderr"] = subprocess.PIPE
+    completed = subprocess.run(prefix + args, **kwargs)
+    output = completed.stdout if capture_output and completed.stdout else ""
+    error_output = completed.stderr if capture_output and completed.stderr else ""
+    if check and completed.returncode != 0:
+        raise RuntimeError(
+            f"hdc command failed with exit code {completed.returncode}: "
+            f"{shlex.join(prefix + args)}\n{output}{error_output}"
+        )
+    if check and expect_stdout is not None and expect_stdout not in output:
+        raise RuntimeError(
+            f"hdc command did not report success: {shlex.join(prefix + args)}\n{output}"
+        )
+    return completed
+
+
+def _run_checked_shell(prefix, command, *, timeout=None):
+    completed = _run_hdc(
+        prefix,
+        [
+            "shell",
+            f"{command}; rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"",
+        ],
+        timeout=timeout,
+        capture_output=True,
+    )
+    exit_code, output = _extract_exit_code(completed.stdout)
+    if exit_code is None:
+        raise RuntimeError(
+            f"failed to parse shell exit code for: {command}\n{completed.stdout}"
+        )
+    if exit_code != 0:
+        raise RuntimeError(
+            f"remote shell command failed with exit code {exit_code}: {command}\n{output}"
+        )
+    return output
+
+
+def _sync_tree_to_device(prefix, local_root, remote_root, use_windows_paths):
+    _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_root)}")
+    _run_checked_shell(prefix, f"mkdir -p {shlex.quote(remote_root)}")
+
+    for directory, filenames in _iter_files(local_root):
+        relative_dir = os.path.relpath(directory, local_root)
+        device_dir = (
+            remote_root
+            if relative_dir == "."
+            else f"{remote_root}/{relative_dir.replace(os.sep, '/')}"
+        )
+        _run_checked_shell(prefix, f"mkdir -p {shlex.quote(device_dir)}")
+        for name in filenames:
+            local_path = directory / name
+            device_path = f"{device_dir}/{name}"
+            source = _to_windows_path(local_path) if use_windows_paths else str(local_path)
+            _run_hdc(
+                prefix,
+                ["file", "send", source, device_path],
+                capture_output=True,
+                expect_stdout="FileTransfer finish",
+            )
+            if name.endswith(".tmp.exe") or os.access(local_path, os.X_OK):
+                _run_checked_shell(prefix, f"chmod +x {shlex.quote(device_path)}")
+
+
+def _build_remote_command(command, local_execdir, remote_execdir, exports):
+    mapped_command = [
+        _map_local_path(arg, local_execdir, remote_execdir) for arg in command
+    ]
+    pieces = [f"cd {shlex.quote(remote_execdir)}"]
+    pieces.extend(exports)
+    pieces.append(shlex.join(mapped_command))
+    pieces.append(f"rc=$?; printf '\\n{EXIT_MARKER}%d\\n' \"$rc\"")
+    return " && ".join(pieces[:-2]) + f"; {pieces[-2]}; {pieces[-1]}"
+
+
+def _extract_exit_code(output):
+    match = re.search(rf"^(.*)\n{EXIT_MARKER}(\d+)\s*$", output, re.DOTALL)
+    if not match:
+        return None, output
+    return int(match.group(2)), match.group(1)
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--hdc", default=os.environ.get("HDC", "hdc"))
+    parser.add_argument("--hdc-server", default=os.environ.get("HDC_SERVER_IP_PORT"))
+    parser.add_argument("--hdc-target", default=os.environ.get("HDC_UTID"))
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--remote-base-dir",
+        default="/data/local/tmp/llvm-lit-hdc",
+    )
+    parser.add_argument(
+        "--host-staging-root",
+        default=os.environ.get("HDC_HOST_STAGING_ROOT"),
+    )
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(os.environ.get("HDC_TIMEOUT_SECONDS", "120")),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("--keep-remote-dir", action="store_true")
+    parser.add_argument("--keep-host-staging", action="store_true")
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    hdc_path = _resolve_hdc_binary(args.hdc)
+    use_windows_paths = _is_windows_hdc(hdc_path)
+    host_staging_root = (
+        Path(args.host_staging_root)
+        if args.host_staging_root
+        else _default_host_staging_root(hdc_path)
+    )
+    prefix = _hdc_prefix(hdc_path, args.hdc_server, args.hdc_target)
+
+    execdir = Path(args.execdir).resolve()
+    run_id = uuid.uuid4().hex[:12]
+    staging_execdir = host_staging_root / f"run-{run_id}" / execdir.name
+    remote_execdir = f"{args.remote_base_dir}/run-{run_id}/{execdir.name}"
+
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    executables = [arg for arg in args.command if _is_test_executable(arg)]
+    host_staging_root.mkdir(parents=True, exist_ok=True)
+    _copy_execdir(execdir, staging_execdir)
+    _copy_needed_libraries(executables, execdir, staging_execdir)
+
+    add_remote_libdir = any(staging_execdir.glob("*.so*"))
+    exports = _build_env_exports(
+        env,
+        prepend_env,
+        str(execdir),
+        remote_execdir,
+        add_remote_libdir,
+    )
+
+    try:
+        _sync_tree_to_device(prefix, staging_execdir, remote_execdir, use_windows_paths)
+        remote_command = _build_remote_command(
+            args.command,
+            str(execdir),
+            remote_execdir,
+            exports,
+        )
+        try:
+            completed = _run_hdc(
+                prefix,
+                ["shell", remote_command],
+                timeout=args.timeout_seconds,
+                capture_output=True,
+                stdin=None,
+            )
+        except subprocess.TimeoutExpired:
+            print(
+                f"hdc executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+                file=sys.stderr,
+            )
+            return 124
+
+        exit_code, output = _extract_exit_code(completed.stdout)
+        if exit_code is None:
+            print(
+                "failed to parse exit code from hdc shell output",
+                file=sys.stderr,
+            )
+            if completed.stdout:
+                print(completed.stdout, end="", file=sys.stderr)
+            if completed.stderr:
+                print(completed.stderr, end="", file=sys.stderr)
+            return 1
+
+        if output:
+            sys.stdout.write(output)
+        if completed.stderr:
+            sys.stderr.write(completed.stderr)
+        return exit_code
+    finally:
+        if not args.keep_remote_dir:
+            try:
+                _run_checked_shell(prefix, f"rm -rf {shlex.quote(remote_execdir)}")
+            except Exception:
+                pass
+        if not args.keep_host_staging:
+            shutil.rmtree(staging_execdir.parent, ignore_errors=True)
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxx/utils/ci/vendor/ohos/hdc_run_test.py b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
new file mode 100644
index 0000000000000..17e2f65bd1fb6
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/hdc_run_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import os
+from pathlib import Path
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import unittest
+
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+HDC_RUN = SCRIPT_DIR / "hdc_run.py"
+
+
+class HdcRunTest(unittest.TestCase):
+    def make_fake_hdc(self, directory):
+        fake_hdc = directory / "hdc"
+        fake_hdc.write_text(
+            textwrap.dedent(
+                """\
+                #!/usr/bin/env python3
+                import sys
+
+                EXIT_MARKER = "__llvm_hdc_exit__="
+
+                def main():
+                    args = sys.argv[1:]
+                    if args[:2] == ["file", "send"]:
+                        print("FileTransfer finish")
+                        return 0
+
+                    if args and args[0] == "shell":
+                        command = args[1]
+                        if "stderr.tmp.exe" in command:
+                            sys.stdout.write("STDOUT-OUTPUT")
+                            sys.stderr.write("STDERR-OUTPUT")
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        if "stdin.tmp.exe" in command:
+                            sys.stdout.write("IN:" + sys.stdin.read())
+                            sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                            return 0
+                        sys.stdout.write(f"\\n{EXIT_MARKER}0\\n")
+                        return 0
+
+                    print("unexpected fake hdc invocation: " + repr(args), file=sys.stderr)
+                    return 2
+
+                if __name__ == "__main__":
+                    raise SystemExit(main())
+                """
+            )
+        )
+        fake_hdc.chmod(fake_hdc.stat().st_mode | stat.S_IXUSR)
+        return fake_hdc
+
+    def run_hdc_run(self, temp_dir, executable_name, *, stdin_text=None):
+        execdir = temp_dir / "execdir"
+        execdir.mkdir()
+        executable = execdir / executable_name
+        executable.write_text("# fake executable marker\n")
+        executable.chmod(executable.stat().st_mode | stat.S_IXUSR)
+
+        fake_hdc = self.make_fake_hdc(temp_dir)
+        return subprocess.run(
+            [
+                sys.executable,
+                str(HDC_RUN),
+                "--hdc",
+                str(fake_hdc),
+                "--execdir",
+                str(execdir),
+                "--host-staging-root",
+                str(temp_dir / "staging"),
+                str(executable),
+            ],
+            input=stdin_text,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+    def test_preserves_remote_stdout_and_stderr_separately(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(Path(temp), "stderr.tmp.exe")
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "STDOUT-OUTPUT")
+        self.assertEqual(completed.stderr, "STDERR-OUTPUT")
+
+    def test_forwards_stdin_to_remote_command(self):
+        with tempfile.TemporaryDirectory() as temp:
+            completed = self.run_hdc_run(
+                Path(temp),
+                "stdin.tmp.exe",
+                stdin_text="hello from stdin",
+            )
+
+        self.assertEqual(completed.returncode, 0)
+        self.assertEqual(completed.stdout, "IN:hello from stdin")
+        self.assertEqual(completed.stderr, "")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/libcxxabi/CMakeLists.txt b/libcxxabi/CMakeLists.txt
index e1a1587fb6283..cb22e6fe65108 100644
--- a/libcxxabi/CMakeLists.txt
+++ b/libcxxabi/CMakeLists.txt
@@ -187,7 +187,12 @@ set(LIBCXXABI_INSTALL_RUNTIME_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBCXXABI_TARGET_SUBDIR)
+  else()
+    set(LIBCXXABI_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBCXXABI_LIBDIR_SUBDIR)
     string(APPEND LIBCXXABI_TARGET_SUBDIR /${LIBCXXABI_LIBDIR_SUBDIR})
   endif()
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
new file mode 100644
index 0000000000000..d18748fe3a3cc
--- /dev/null
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -0,0 +1,30 @@
+# This testing configuration handles running the test suite against LLVM's
+# libc++abi using HDC on OHOS.
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -I %{libcxx}/test/support -I %{libcxx}/src -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
+))
+config.substitutions.append(('%{link_flags}',
+    '-nostdlib++ -L %{lib} -lc++ -lc++abi -pthread'
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)
diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 02132f6c07fcd..4fd11bf5aca2f 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -142,7 +142,12 @@ set(LIBUNWIND_STATIC_OUTPUT_NAME "unwind" CACHE STRING "Output name for the stat
 
 # TODO: Use common runtimes infrastructure for output and install paths
 if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE)
-  set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  if(CMAKE_SYSTEM_NAME STREQUAL "OHOS")
+    include(GetToolchainDirs)
+    get_runtimes_target_dirname("${LLVM_DEFAULT_TARGET_TRIPLE}" LIBUNWIND_TARGET_SUBDIR)
+  else()
+    set(LIBUNWIND_TARGET_SUBDIR ${LLVM_DEFAULT_TARGET_TRIPLE})
+  endif()
   if(LIBUNWIND_LIBDIR_SUBDIR)
     string(APPEND LIBUNWIND_TARGET_SUBDIR /${LIBUNWIND_LIBDIR_SUBDIR})
   endif()
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
new file mode 100644
index 0000000000000..bce241a7898cf
--- /dev/null
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -0,0 +1,55 @@
+#
+# Configuration file for running the libunwind tests against the shared library
+# using HDC on OHOS.
+#
+
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
+
+compile_flags = []
+link_flags = []
+
+if @HAVE_GETAUXVAL@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_GETAUXVAL')
+
+if @HAVE_ELF_AUX_INFO@:
+    compile_flags.append('-D_LIBUNWIND_HAVE_ELF_AUX_INFO')
+
+if @LIBUNWIND_ENABLE_CET@:
+    compile_flags.append('-fcf-protection=full')
+
+if @LIBUNWIND_ENABLE_GCS@:
+    compile_flags.append('-mbranch-protection=standard')
+
+if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
+    link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
+
+if '@CMAKE_DL_LIBS@':
+    link_flags.append('-l at CMAKE_DL_LIBS@')
+
+compile_flags.append('-funwind-tables')
+
+config.substitutions.append(('%{flags}',
+    '--sysroot @CMAKE_SYSROOT@' if '@CMAKE_SYSROOT@' else ''
+))
+config.substitutions.append(('%{compile_flags}',
+    '-nostdinc++ -I %{{include}} {}'.format(' '.join(compile_flags))
+))
+config.substitutions.append(('%{link_flags}',
+    '-L %{{lib}} -lunwind {}'.format(' '.join(link_flags))
+))
+config.substitutions.append(('%{exec}',
+    '%{executor} --execdir %{temp} -- '
+))
+
+import os, site
+site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
+import libcxx.test.params, libcxx.test.config
+
+config.available_features.add('ohos-hdc')
+
+libcxx.test.config.configure(
+    libcxx.test.params.DEFAULT_PARAMETERS,
+    libcxx.test.features.DEFAULT_FEATURES,
+    config,
+    lit_config
+)

>From e9e5d2e517fb072f3554798e49b7119ec739105f Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 25 Jun 2026 11:30:38 +0800
Subject: [PATCH 2/6] Fix OHOS libc++ CI ABI and SME detection

---
 libcxx/utils/ci/run-buildbot |  2 ++
 libunwind/src/Registers.hpp  | 15 +++++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 1867e9c620649..ecf0ca17ae148 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -192,6 +192,7 @@ function setup-ohos-hdc-env() {
     require-env-var OHOS_SYSROOT
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
+    OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
     OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
     OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
 }
@@ -209,6 +210,7 @@ function generate-cmake-ohos-hdc() {
           -DCMAKE_C_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_CXX_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
           -DCMAKE_ASM_COMPILER_TARGET="${OHOS_TARGET_TRIPLE}" \
+          -DLIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE}" \
           -DLIBCXX_INCLUDE_BENCHMARKS=OFF \
           -DLIBCXX_TEST_CONFIG="llvm-libc++-ohos.cfg.in" \
           -DLIBCXXABI_TEST_CONFIG="llvm-libc++abi-ohos.cfg.in" \
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c88422a6d5f4f..18cb2a523bd0e 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -25,6 +25,11 @@
 #endif
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
+#if defined(__has_include)
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
+#endif
 #endif
 
 namespace libunwind {
@@ -1951,18 +1956,16 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
       return false;
     return has_sme != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = getauxval(AT_HWCAP2);
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
   static bool checkHasSME() {
-    constexpr int hwcap2_sme = (1 << 23);
     unsigned long hwcap2 = 0;
     elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-    return (hwcap2 & hwcap2_sme) != 0;
+    return (hwcap2 & HWCAP2_SME) != 0;
   }
 #else
   static bool checkHasSME() {

>From 498fa8d127ff49c27d4230996338c6eb5d747fe6 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Wed, 1 Jul 2026 15:49:11 +0800
Subject: [PATCH 3/6] Stabilize OHOS HDC runtimes tests

Add OHOS lit features for HDC executor and platform limitations, mark affected libc++ tests as unsupported or expected failures, and adjust test support for OHOS filesystem cleanup and allocation counting behavior.

Also guard the aarch64 ZA unwind test on HWCAP2_SME so devices without a reliable SME feature definition do not execute SME instructions.
---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  9 +++++-
 .../clang/clang_modules_include.gen.py        |  3 ++
 .../locale.facet/no_allocation.pass.cpp       |  1 +
 libcxx/test/selftest/dsl/dsl.sh.py            |  5 ++++
 libcxx/test/selftest/stdin-is-piped.sh.cpp    |  1 +
 .../directory_entry.cons/path.pass.cpp        |  1 +
 .../directory_entry.mods/assign.pass.cpp      |  1 +
 .../directory_entry.mods/refresh.pass.cpp     |  1 +
 .../replace_filename.pass.cpp                 |  1 +
 .../directory_entry.obs/file_size.pass.cpp    |  1 +
 .../file_type_obs.pass.cpp                    |  1 +
 .../hard_link_count.pass.cpp                  |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../directory_iterator.members/ctor.pass.cpp  |  1 +
 .../path.member/path.assign/move.pass.cpp     |  2 +-
 .../path.member/path.construct/move.pass.cpp  |  2 +-
 .../rec.dir.itr.members/ctor.pass.cpp         |  1 +
 .../rec.dir.itr.members/increment.pass.cpp    |  1 +
 .../fs.op.funcs/fs.op.exists/exists.pass.cpp  |  1 +
 .../is_block_file.pass.cpp                    |  1 +
 .../is_character_file.pass.cpp                |  1 +
 .../fs.op.is_directory/is_directory.pass.cpp  |  1 +
 .../fs.op.is_empty/is_empty.pass.cpp          |  1 +
 .../fs.op.is_fifo/is_fifo.pass.cpp            |  1 +
 .../fs.op.is_other/is_other.pass.cpp          |  1 +
 .../is_regular_file.pass.cpp                  |  1 +
 .../fs.op.is_socket/is_socket.pass.cpp        |  1 +
 .../fs.op.is_symlink/is_symlink.pass.cpp      |  1 +
 .../last_write_time.pass.cpp                  |  1 +
 .../fs.op.funcs/fs.op.remove/remove.pass.cpp  |  1 +
 .../fs.op.remove_all/remove_all.pass.cpp      |  1 +
 .../fs.op.funcs/fs.op.status/status.pass.cpp  |  1 +
 .../symlink_status.pass.cpp                   |  1 +
 .../temp_directory_path.pass.cpp              |  1 +
 .../ext.manip/get_money.pass.cpp              |  1 +
 .../ext.manip/put_money.pass.cpp              |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 .../print.fun/no_file_description.pass.cpp    |  3 ++
 .../std.manip/setfill_wchar_max.pass.cpp      |  1 +
 .../narrow.stream.objects/cerr.sh.cpp         |  1 +
 .../narrow.stream.objects/cin.sh.cpp          |  1 +
 .../narrow.stream.objects/clog.sh.cpp         |  1 +
 .../wide.stream.objects/wcerr-imbue.sh.cpp    |  1 +
 .../wide.stream.objects/wcerr.sh.cpp          |  1 +
 .../wide.stream.objects/wcin-imbue.sh.cpp     |  1 +
 .../wide.stream.objects/wcin.sh.cpp           |  1 +
 .../wide.stream.objects/wclog.sh.cpp          |  1 +
 .../sized_delete_array.pass.cpp               |  1 +
 ..._array_calls_unsized_delete_array.pass.cpp |  1 +
 .../new.delete.single/sized_delete.pass.cpp   |  1 +
 ...sized_delete_calls_unsized_delete.pass.cpp |  1 +
 .../locale.collate.byname/compare.pass.cpp    |  1 +
 .../locale.collate.byname/transform.pass.cpp  |  1 +
 .../facet.ctype.char.dtor/dtor.pass.cpp       |  1 +
 .../locale.ctype.byname/is_1.pass.cpp         |  1 +
 .../locale.ctype.byname/is_many.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_is.pass.cpp      |  1 +
 .../locale.ctype.byname/scan_not.pass.cpp     |  1 +
 .../locale.ctype.byname/tolower_1.pass.cpp    |  1 +
 .../locale.ctype.byname/tolower_many.pass.cpp |  1 +
 .../locale.ctype.byname/toupper_1.pass.cpp    |  1 +
 .../locale.ctype.byname/toupper_many.pass.cpp |  1 +
 .../locale.ctype.byname/widen_1.pass.cpp      |  1 +
 .../locale.ctype.byname/widen_many.pass.cpp   |  1 +
 .../get_long_double_en_US.pass.cpp            |  1 +
 .../get_long_double_zh_CN.pass.cpp            |  1 +
 .../get_string_en_US.pass.cpp                 |  1 +
 .../put_long_double_en_US.pass.cpp            |  1 +
 .../put_long_double_zh_CN.pass.cpp            |  1 +
 .../put_string_en_US.pass.cpp                 |  1 +
 .../locale/locale.cons/default.pass.cpp       |  1 +
 .../test/std/re/re.traits/transform.pass.cpp  |  1 +
 .../re/re.traits/transform_primary.pass.cpp   |  1 +
 .../thread_create_failure.pass.cpp            |  1 +
 .../thread.jthread/join.deadlock.pass.cpp     |  1 +
 .../locale-specific_form.pass.cpp             |  1 +
 libcxx/test/support/count_new.h               | 28 +++++++++++++++++++
 libcxx/test/support/filesystem_test_helper.h  |  4 ++-
 libcxx/test/support/test_macros.h             |  4 ++-
 libunwind/test/aarch64_za_unwind.pass.cpp     | 13 +++++----
 80 files changed, 132 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index a20ed9ce9c6a9..542bc0cc83a1f 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -7,7 +7,8 @@ config.substitutions.append(('%{flags}',
     ('--sysroot @CMAKE_SYSROOT@ ' if '@CMAKE_SYSROOT@' else '') + '-pthread'
 ))
 config.substitutions.append(('%{compile_flags}',
-    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
+    '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support '
+    '-DTEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS'
 ))
 config.substitutions.append(('%{link_flags}',
     # Link libc++abi/libunwind directly: lld won't resolve libc++'s transitive
@@ -24,6 +25,12 @@ import libcxx.test.params, libcxx.test.config
 
 config.available_features.add('LIBCXX-OHOS-FIXME')
 config.available_features.add('ohos-hdc')
+config.available_features.add('executor-has-no-stdin')
+config.available_features.add('executor-merges-stderr-into-stdout')
+config.available_features.add('executor-runs-as-root')
+config.available_features.add('platform-has-nonportable-locales')
+config.available_features.add('platform-has-unreliable-new-delete-interposition')
+config.available_features.add('pthread-join-self-deadlocks')
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..88aa51c4dd080 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,6 +24,9 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
+# The OHOS headers don't appear to be compatible with modules yet
+# UNSUPPORTED: LIBCXX-OHOS-FIXME
+
 # TODO: Investigate this failure
 # UNSUPPORTED: LIBCXX-FREEBSD-FIXME
 
diff --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
index 6e59b8256ffe7..a98402b5917b6 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.facet/no_allocation.pass.cpp
@@ -12,6 +12,7 @@
 // for facets, as it uses __sso_allocator<facet*, N>. It would fail if new
 // facets have been added (using install()) but N hasn't been adjusted to
 // account for them.
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <cassert>
 
diff --git a/libcxx/test/selftest/dsl/dsl.sh.py b/libcxx/test/selftest/dsl/dsl.sh.py
index f93ccc747d661..fbbacb0c8d979 100644
--- a/libcxx/test/selftest/dsl/dsl.sh.py
+++ b/libcxx/test/selftest/dsl/dsl.sh.py
@@ -9,6 +9,11 @@
 # With picolibc, test_program_stderr_is_not_conflated_with_stdout fails
 # because stdout & stderr are treated as the same.
 # XFAIL: LIBCXX-PICOLIBC-FIXME
+#
+# The OHOS HDC executor currently merges remote stderr into host stdout, and
+# OHOS locale probing accepts some intentionally bogus locale names.
+# UNSUPPORTED: executor-merges-stderr-into-stdout
+# UNSUPPORTED: platform-has-nonportable-locales
 
 # Investigate why this is failing with LLVM libc.
 # XFAIL: LLVM-LIBC-FIXME
diff --git a/libcxx/test/selftest/stdin-is-piped.sh.cpp b/libcxx/test/selftest/stdin-is-piped.sh.cpp
index ffd10631c6a67..1e1b8d1bd0317 100644
--- a/libcxx/test/selftest/stdin-is-piped.sh.cpp
+++ b/libcxx/test/selftest/stdin-is-piped.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // Make sure that the executor pipes standard input to the test-executable being run.
+// UNSUPPORTED: executor-has-no-stdin
 
 // RUN: %{build}
 // RUN: echo "abc" | %{exec} %t.exe
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
index a1a37012369d4..d1c2316dec112 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
index ba14af1240d4b..d4e23a9a3c6b6 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/assign.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
index 20a545b719ed6..f04a6bce76691 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/refresh.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
index 03e9de882ab0a..428d30804a4c3 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.mods/replace_filename.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
index 10829f8a73db1..c8a1c34aa3028 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
index 071ee7f6c891f..44b94528f5e32 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_type_obs.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a hard link.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
index 5d931891dd0eb..d291ca2cb5641 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
index 82b94a3d6bf5f..e4bc9c41728da 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
index 8401dbd38cd71..e0fa44ffbc4a5 100644
--- a/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
index ac5499cdd6f20..2bcc64a3c2651 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.assign/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_assignable<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
index 1f1843f7d401d..5efdf983f8a10 100644
--- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/move.pass.cpp
@@ -26,7 +26,7 @@ namespace fs = std::filesystem;
 int main(int, char**) {
   using namespace fs;
   static_assert(std::is_nothrow_move_constructible<path>::value, "");
-  assert(globalMemCounter.checkOutstandingNewEq(0));
+  ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(globalMemCounter.checkOutstandingNewEq(0));
   const std::string s("we really really really really really really really "
                       "really really long string so that we allocate");
   ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
index 08d7ff84378d5..a3768b8cbd83f 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/ctor.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
index 1f2293989b7ef..9e8a70b4339d4 100644
--- a/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // On Android L, ~scoped_test_env() is unable to delete the temp dir using
 // chmod+rm because chmod is too broken.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
index 56465d01021ad..62dc696d6797d 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.exists/exists.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
index fad880ecf30f7..708fb511dce73 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_block_file/is_block_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
index 12bc0c198c062..69e357adeafb5 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_char_file/is_character_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
index f1b559cf2d55b..0ed4aa0dc8d58 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
index f3161f2e020d2..579c03f2b8c0b 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
index fbf5e7129f7af..1d5312d65be46 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_fifo/is_fifo.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
index d0974640429df..7a3a56a0b161c 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_other/is_other.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
index 4a5882e36cde2..8b877ff63b112 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_regular_file/is_regular_file.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
index 382685c21dd99..995125d702de3 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_socket/is_socket.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
index 07b446d80c402..692fe773a0f9e 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
index c420fea21aa66..22c2c114ba687 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // The string reported on errors changed, which makes those tests fail when run
 // against a built library that doesn't contain 0aa637b2037d.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
index 56b4a27de6b42..8064feaaf2065 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove/remove.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
index b30d56685e2cb..540849ccefc05 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/remove_all.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
index 425c224606038..02ff1e249d3bc 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
index 1d405a816e110..41474f9ca1e48 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
@@ -9,6 +9,7 @@
 // REQUIRES: can-create-symlinks
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // Starting in Android N (API 24), SELinux policy prevents the shell user from
 // creating a FIFO file.
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
index 38e14438f3fd3..9a891eab6b343 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.temp_dir_path/temp_directory_path.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 // UNSUPPORTED: no-filesystem
+// UNSUPPORTED: executor-runs-as-root
 
 // <filesystem>
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
index 88672851f7b6b..3e1d34ab5257a 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/get_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
index d0ff2c87689d1..4c299b876639e 100644
--- a/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/ext.manip/put_money.pass.cpp
@@ -12,6 +12,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
index 98fa56b7f4b6d..7970887bac199 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/locale-specific_form.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
index 283a8fddb8e8f..13e6f74c5a143 100644
--- a/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp
@@ -17,6 +17,9 @@
 // fmemopen is available starting in Android M (API 23)
 // XFAIL: target={{.+}}-android{{(eabi)?(21|22)}}
 
+// OHOS fmemopen/FILE behavior does not currently match the expectation tested here.
+// XFAIL: LIBCXX-OHOS-FIXME
+
 // <print>
 
 // The FILE returned by fmemopen does not have file descriptor.
diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
index b24c0b90d86f1..2ddddbef70dbb 100644
--- a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_wchar_max.pass.cpp
@@ -18,6 +18,7 @@
 // XFAIL: target=armv{{7|8}}{{l?}}{{.*}}-linux-gnueabihf && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-linux-gnu && libcpp-abi-version=1
 // XFAIL: target=aarch64{{.*}}-amazon-linux && libcpp-abi-version=1
+// XFAIL: target={{.*}}-linux-ohos && libcpp-abi-version=1
 
 #include <iomanip>
 #include <ostream>
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
index da5563f87df45..ea5af90a90822 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
index 4f23b6b9a5d87..6dc0ff36ff4bf 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sh.cpp
@@ -14,6 +14,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
index 9bedd67e816ef..cbb9639766207 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sh.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // XFAIL: LIBCXX-PICOLIBC-FIXME
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
index ec4c8a009ba0c..59d93e74db3b7 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr-imbue.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
index ae82035ccfc62..49a6ce0892ea4 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wcerr;
 
 // UNSUPPORTED: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
index 6ae1972234b2b..58a6e18340ddf 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin-imbue.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
index 08b74b999bbf4..88dcfe0485429 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sh.cpp
@@ -9,6 +9,7 @@
 // This test hangs on Android devices that lack shell_v2, which was added in
 // Android N (API 24).
 // UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+// UNSUPPORTED: executor-has-no-stdin
 
 // <iostream>
 
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
index 89e178248ae50..9c9745dcc9777 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sh.cpp
@@ -11,6 +11,7 @@
 // wostream wclog;
 
 // XFAIL: no-wide-characters
+// UNSUPPORTED: executor-merges-stderr-into-stdout
 
 // RUN: %{build}
 // RUN: %{exec} %t.exe 2> %t.actual
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
index 0e411f8adc0f2..e563152f2c8cd 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
index 026250daeee89..34119303a29ba 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_calls_unsized_delete_array.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete [] is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
index ca0f612bf7578..a4e32aaa11e13 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
@@ -20,6 +20,7 @@
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc-ibm-aix): -fsized-deallocation
 // ADDITIONAL_COMPILE_FLAGS(target=powerpc64-ibm-aix): -fsized-deallocation
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // AIX, and z/OS default to -fno-sized-deallocation.
 // XFAIL: target={{.+}}-zos{{.*}}
diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
index fbc9cf070bcba..3ec2b47b53eb0 100644
--- a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
+++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_calls_unsized_delete.pass.cpp
@@ -11,6 +11,7 @@
 // operator delete is called directly.
 
 // UNSUPPORTED: sanitizer-new-delete
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <new>
 #include <cstddef>
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
index 8ae6bc2d3ba66..7cfd9f955fce7 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/compare.pass.cpp
@@ -8,6 +8,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
index 4978db116ef39..743075ede8ece 100644
--- a/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.collate/locale.collate.byname/transform.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
index 7be7e8d574e6c..b77a4409522f0 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/facet.ctype.special/facet.ctype.char.dtor/dtor.pass.cpp
@@ -11,6 +11,7 @@
 // template <> class ctype<char>
 
 // ~ctype();
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 #include <locale>
 #include <cassert>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
index 5cebc079c4703..c4803c04d173b 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_1.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <type_traits>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
index 2869378b42b92..5174ffebf281f 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/is_many.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
index ea03f042f2d45..665da7323e38a 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_is.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
index 2f7acb843e591..5d87c081b6e1c 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/scan_not.pass.cpp
@@ -17,6 +17,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 #include <locale>
 #include <string>
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
index 447353de8af72..93d93aa9228f7 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
index 94ecc6c8fa85b..ab448a01632d6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/tolower_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
index 2b37737a26bc3..4be990e5b88c6 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_1.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
index 016d8d736ad22..e0a3912a9315e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/toupper_many.pass.cpp
@@ -11,6 +11,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
index 959a4be9e1de9..b22629c82444e 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_1.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
index 078b4a6fefb7e..c2cd904808794 100644
--- a/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.ctype/locale.ctype.byname/widen_many.pass.cpp
@@ -8,6 +8,7 @@
 
 // REQUIRES: locale.en_US.UTF-8
 // XFAIL: no-wide-characters
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // <locale>
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
index 9997b07134563..73b7a6decda36 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
index c86df7e6b53bf..deb30d747653f 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
index 478df7964f6d2..ee6fbd48b09b6 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.get/locale.money.get.members/get_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
index 4b767fae871fa..c476537c3c7e3 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
index 25046a8417083..8174a724d4165 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_long_double_zh_CN.pass.cpp
@@ -10,6 +10,7 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.zh_CN.UTF-8
 
diff --git a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
index 1c8710a008f27..e23cb4bf5f04a 100644
--- a/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.monetary/locale.money.put/locale.money.put.members/put_string_en_US.pass.cpp
@@ -15,6 +15,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
index f0086ada30303..a36e3af8d12eb 100644
--- a/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
+++ b/libcxx/test/std/localization/locales/locale/locale.cons/default.pass.cpp
@@ -12,6 +12,7 @@
 
 // This test runs in C++20, but we have deprecated codecvt<char(16|32), char, mbstate_t> in C++20.
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// UNSUPPORTED: platform-has-unreliable-new-delete-interposition
 
 // <locale>
 
diff --git a/libcxx/test/std/re/re.traits/transform.pass.cpp b/libcxx/test/std/re/re.traits/transform.pass.cpp
index 80cd3f01faff2..112ecd47ff4e2 100644
--- a/libcxx/test/std/re/re.traits/transform.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform.pass.cpp
@@ -8,6 +8,7 @@
 //
 // NetBSD does not support LC_COLLATE at the moment
 // XFAIL: netbsd
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..cbc9387facb30 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -11,6 +11,7 @@
 
 // XFAIL: LIBCXX-AIX-FIXME
 // XFAIL: LIBCXX-FREEBSD-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 
diff --git a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
index 40daa49393bd4..110d0ea9fd694 100644
--- a/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
+++ b/libcxx/test/std/thread/futures/futures.async/thread_create_failure.pass.cpp
@@ -15,6 +15,7 @@
 
 // There is no way to limit the number of threads on windows
 // UNSUPPORTED: windows
+// UNSUPPORTED: executor-runs-as-root
 
 // macOS and FreeBSD seem to limit the number of processes, not threads via RLIMIT_NPROC.
 // XFAIL: target={{.+}}-apple-{{.*}}
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
index c6eb0694ab2e1..19573ae52fd71 100644
--- a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
@@ -9,6 +9,7 @@
 // Windows cannot detect the deadlock. Instead of throwing system_error,
 // it would dead lock the test
 // UNSUPPORTED: windows
+// UNSUPPORTED: pthread-join-self-deadlocks
 
 // TSAN bug: https://llvm.org/PR66537
 // UNSUPPORTED: tsan
diff --git a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
index 51be78e1a02f8..804036e425572 100644
--- a/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.functions/locale-specific_form.pass.cpp
@@ -14,6 +14,7 @@
 
 // Bionic has minimal locale support, investigate this later.
 // XFAIL: LIBCXX-ANDROID-FIXME
+// UNSUPPORTED: platform-has-nonportable-locales
 
 // REQUIRES: locale.en_US.UTF-8
 
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..6901aff00bc4e 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -415,6 +415,13 @@ void operator delete(void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteCalled(p);
+  std::free(p);
+}
+#  endif
+
 // operator new[](size_t[, nothrow_t]) and operator delete[](size_t[, nothrow_t])
 void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->newArrayCalled(s);
@@ -449,6 +456,13 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
   std::free(p);
 }
 
+#  if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t) TEST_NOEXCEPT {
+  getGlobalMemCounter()->deleteArrayCalled(p);
+  std::free(p);
+}
+#  endif
+
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
 #    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
 #      define USE_ALIGNED_ALLOC
@@ -519,6 +533,13 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete(void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
@@ -551,6 +572,13 @@ void operator delete[](void* p, std::align_val_t av, std::nothrow_t const&) TEST
   free_aligned_impl(p, av);
 }
 
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
+void operator delete[](void* p, std::size_t, std::align_val_t av) TEST_NOEXCEPT {
+  getGlobalMemCounter()->alignedDeleteArrayCalled(p, static_cast<std::size_t>(av));
+  free_aligned_impl(p, av);
+}
+#    endif
+
 #  endif // TEST_HAS_NO_ALIGNED_ALLOCATION
 
 #endif // DISABLE_NEW_COUNT
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index 2ad9efb32c60f..f820a4a0fa915 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -182,7 +182,7 @@ struct scoped_test_env
         std::string cmd = "chmod -R 777 " + test_root.string();
 #endif // defined(__MVS__)
         int ret = std::system(cmd.c_str());
-#  if !defined(_AIX) && !defined(__ANDROID__)
+#  if !defined(_AIX) && !defined(__ANDROID__) && !defined(__OHOS__)
         // On AIX the chmod command will return non-zero when trying to set
         // the permissions on a directory that contains a bad symlink. This triggers
         // the assert, despite being able to delete everything with the following
@@ -196,6 +196,8 @@ struct scoped_test_env
         // LIBCXX-ANDROID-FIXME: Other fixes to consider: place a toybox chmod
         // onto old devices, re-enable this assert for devices running Android N
         // and up, rewrite this chmod+rm in C or C++.
+        //
+        // OHOS has the same chmod -R behavior for bad symlinks.
         assert(ret == 0);
 #  endif
 
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 78b1f6eda6576..fdf6bc5b18d72 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -375,7 +375,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 #define TEST_NOT_WIN32(...) __VA_ARGS__
 #endif
 
-#if defined(TEST_WINDOWS_DLL) ||defined(__MVS__) || defined(_AIX)
+#if defined(TEST_WINDOWS_DLL) || defined(__MVS__) || defined(_AIX) || defined(TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS)
 // Macros for waiving cases when we can't count allocations done within
 // the library implementation.
 //
@@ -386,6 +386,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
 //
 // The same goes on IBM zOS.
 // The same goes on AIX.
+// Some target test configurations can define
+// TEST_HAS_NO_RELIABLE_LIBRARY_INTERNAL_ALLOCATIONS for the same reason.
 #define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
 #define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
 #else
diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp
index 36536860c1a96..b28f70a6e8e7a 100644
--- a/libunwind/test/aarch64_za_unwind.pass.cpp
+++ b/libunwind/test/aarch64_za_unwind.pass.cpp
@@ -20,6 +20,9 @@
 #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
 #include <sys/auxv.h>
 #endif
+#if __has_include(<asm/hwcap.h>)
+#include <asm/hwcap.h>
+#endif
 
 // Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA
 // (and commits a lazy save of ZA) before resuming from unwinding.
@@ -33,18 +36,16 @@ static bool checkHasSME() {
     return false;
   return has_sme != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_GETAUXVAL)
+#elif defined(_LIBUNWIND_HAVE_GETAUXVAL) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = getauxval(AT_HWCAP2);
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
-#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO)
+#elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) && defined(HWCAP2_SME)
 static bool checkHasSME() {
-  constexpr int hwcap2_sme = (1 << 23);
   unsigned long hwcap2 = 0;
   elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
-  return (hwcap2 & hwcap2_sme) != 0;
+  return (hwcap2 & HWCAP2_SME) != 0;
 }
 #else
 static bool checkHasSME() {

>From bbaa18370c45aaf79c2446be0a6e94dc2aa0ef00 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Fri, 3 Jul 2026 10:33:38 +0800
Subject: [PATCH 4/6] Add qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in   |  16 +-
 libcxx/utils/ci/run-buildbot                  |  41 ++++-
 libcxx/utils/ci/vendor/ohos/qemu_run.py       | 144 ++++++++++++++++++
 .../test/configs/llvm-libc++abi-ohos.cfg.in   |   8 +-
 .../test/configs/llvm-libunwind-ohos.cfg.in   |   8 +-
 5 files changed, 204 insertions(+), 13 deletions(-)
 create mode 100755 libcxx/utils/ci/vendor/ohos/qemu_run.py

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 542bc0cc83a1f..20aa7a49d5802 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++ using HDC on OHOS.
+# libc++ using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -23,15 +23,21 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
 config.available_features.add('LIBCXX-OHOS-FIXME')
-config.available_features.add('ohos-hdc')
-config.available_features.add('executor-has-no-stdin')
-config.available_features.add('executor-merges-stderr-into-stdout')
-config.available_features.add('executor-runs-as-root')
+config.available_features.add(executor_kind)
 config.available_features.add('platform-has-nonportable-locales')
 config.available_features.add('platform-has-unreliable-new-delete-interposition')
 config.available_features.add('pthread-join-self-deadlocks')
 
+if executor_kind == 'ohos-hdc':
+    config.available_features.add('executor-has-no-stdin')
+    config.available_features.add('executor-merges-stderr-into-stdout')
+    config.available_features.add('executor-runs-as-root')
+
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
     libcxx.test.features.DEFAULT_FEATURES,
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index ecf0ca17ae148..40739b0fba4d8 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -28,6 +28,16 @@ ${PROGNAME} [options] <BUILDER>
 --build-dir <DIR>   The directory to use for building the library. By default,
                     this is '<llvm-root>/build/<builder>'.
 
+--ohos-qemu <PATH>  Run OHOS tests under qemu-aarch64 instead of HDC. This also
+                    accepts OHOS_QEMU or QEMU from the environment.
+
+--ohos-qemu-sysroot <DIR>
+                    Sysroot passed to qemu-aarch64 with -L. Defaults to
+                    OHOS_QEMU_SYSROOT, QEMU_SYSROOT, then OHOS_SYSROOT.
+
+--ohos-hdc <PATH>   HDC binary to use when OHOS tests run on a device. This sets
+                    HDC for the OHOS HDC executor.
+
 Environment variables
 CC                  The C compiler to use, this value is used by CMake. This
                     variable is mandatory.
@@ -84,6 +94,19 @@ while [[ $# -gt 0 ]]; do
             BUILD_DIR="${2}"
             shift; shift
             ;;
+        --ohos-qemu)
+            OHOS_QEMU="${2}"
+            shift; shift
+            ;;
+        --ohos-qemu-sysroot)
+            OHOS_QEMU_SYSROOT="${2}"
+            shift; shift
+            ;;
+        --ohos-hdc)
+            HDC="${2}"
+            export HDC
+            shift; shift
+            ;;
         *)
             BUILDER="${1}"
             shift
@@ -193,8 +216,18 @@ function setup-ohos-hdc-env() {
 
     OHOS_TARGET_TRIPLE="${OHOS_TARGET_TRIPLE:=aarch64-linux-ohos}"
     OHOS_LIBCXX_ABI_NAMESPACE="${OHOS_LIBCXX_ABI_NAMESPACE:=__h}"
-    OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
-    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE}"
+    OHOS_QEMU="${OHOS_QEMU:-${QEMU:-}}"
+
+    if [[ -n "${OHOS_QEMU}" ]]; then
+        OHOS_QEMU_SYSROOT="${OHOS_QEMU_SYSROOT:-${QEMU_SYSROOT:-${OHOS_SYSROOT}}}"
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/qemu_run.py --qemu ${OHOS_QEMU} --sysroot ${OHOS_QEMU_SYSROOT}"
+        OHOS_EXECUTOR_KIND="ohos-qemu"
+    else
+        OHOS_EXECUTOR="${MONOREPO_ROOT}/libcxx/utils/ci/vendor/ohos/hdc_run.py"
+        OHOS_EXECUTOR_KIND="ohos-hdc"
+    fi
+
+    OHOS_LIT_PARAMS="executor=${OHOS_EXECUTOR};target_triple=${OHOS_TARGET_TRIPLE};executor_kind=${OHOS_EXECUTOR_KIND}"
 }
 
 function generate-cmake-ohos-hdc() {
@@ -863,14 +896,14 @@ android-ndk-*)
     step "Running the libc++abi tests"
     ninja -vC "${BUILD_DIR}" check-cxxabi
 ;;
-ohos-hdc-aarch64-full)
+ohos-aarch64-full|ohos-hdc-aarch64-full)
     clean
 
     generate-cmake-ohos-hdc
 
     check-runtimes
 ;;
-ohos-hdc-aarch64-smoke)
+ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
     clean
 
     generate-cmake-ohos-hdc
diff --git a/libcxx/utils/ci/vendor/ohos/qemu_run.py b/libcxx/utils/ci/vendor/ohos/qemu_run.py
new file mode 100755
index 0000000000000..ef7eef7872864
--- /dev/null
+++ b/libcxx/utils/ci/vendor/ohos/qemu_run.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+import argparse
+import os
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+def _parse_kv(items):
+    result = {}
+    for item in items:
+        key, value = item.split("=", 1)
+        result[key] = value
+    return result
+
+
+def _resolve_binary(binary):
+    if os.path.isabs(binary):
+        if os.path.isfile(binary) and os.access(binary, os.X_OK):
+            return binary
+        sys.exit(f"failed to find executable: {binary}")
+
+    resolved = shutil.which(binary)
+    if resolved is None:
+        sys.exit(f"failed to find executable: {binary}")
+    return resolved
+
+
+def _find_build_root(execdir):
+    current = Path(execdir).resolve()
+    for candidate in [current, *current.parents]:
+        if (candidate / "CMakeCache.txt").exists():
+            return candidate
+    return None
+
+
+def _collect_build_library_dirs(execdir):
+    build_root = _find_build_root(execdir)
+    if build_root is None:
+        return []
+
+    lib_root = build_root / "lib"
+    if not lib_root.is_dir():
+        return []
+
+    library_dirs = []
+    seen = set()
+    for lib in sorted(lib_root.rglob("*.so*")):
+        if not lib.is_file():
+            continue
+        parent = lib.parent.resolve()
+        key = str(parent)
+        if key in seen:
+            continue
+        seen.add(key)
+        library_dirs.append(str(parent))
+    return library_dirs
+
+
+def _build_target_env_args(env, prepend_env):
+    target_env = dict(env)
+
+    for key, value in prepend_env.items():
+        existing = target_env.get(key, os.environ.get(key, ""))
+        target_env[key] = f"{value}{os.pathsep}{existing}" if existing else value
+
+    args = []
+    for key, value in target_env.items():
+        args.extend(["-E", f"{key}={value}"])
+    return args
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--qemu",
+        default=os.environ.get("OHOS_QEMU", os.environ.get("QEMU", "qemu-aarch64")),
+    )
+    parser.add_argument("--sysroot", required=True)
+    parser.add_argument("--execdir", required=True)
+    parser.add_argument(
+        "--timeout-seconds",
+        type=int,
+        default=int(
+            os.environ.get(
+                "OHOS_QEMU_TIMEOUT_SECONDS",
+                os.environ.get("QEMU_TIMEOUT_SECONDS", "600"),
+            )
+        ),
+    )
+    parser.add_argument("--env", nargs="*", default=[])
+    parser.add_argument("--prepend_env", nargs="*", default=[])
+    parser.add_argument("command", nargs=argparse.ONE_OR_MORE)
+    args = parser.parse_args()
+
+    qemu = _resolve_binary(args.qemu)
+    execdir = Path(args.execdir).resolve()
+    env = _parse_kv(args.env)
+    prepend_env = _parse_kv(args.prepend_env)
+
+    build_library_dirs = _collect_build_library_dirs(execdir)
+    if build_library_dirs:
+        existing = prepend_env.get("LD_LIBRARY_PATH")
+        build_library_path = os.pathsep.join(build_library_dirs)
+        prepend_env["LD_LIBRARY_PATH"] = (
+            f"{build_library_path}{os.pathsep}{existing}"
+            if existing
+            else build_library_path
+        )
+
+    cmd = [
+        qemu,
+        "-L",
+        args.sysroot,
+        *_build_target_env_args(env, prepend_env),
+        *args.command,
+    ]
+    try:
+        completed = subprocess.run(
+            cmd,
+            cwd=execdir,
+            timeout=args.timeout_seconds,
+            check=False,
+        )
+    except subprocess.TimeoutExpired:
+        print(
+            f"qemu executor timed out after {args.timeout_seconds}s: {' '.join(args.command)}",
+            file=sys.stderr,
+        )
+        return 124
+    return completed.returncode
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index d18748fe3a3cc..8f5a17b3dec64 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -1,5 +1,5 @@
 # This testing configuration handles running the test suite against LLVM's
-# libc++abi using HDC on OHOS.
+# libc++abi using HDC or QEMU on OHOS.
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
 
@@ -20,7 +20,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index bce241a7898cf..f2a032c41a123 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -1,6 +1,6 @@
 #
 # Configuration file for running the libunwind tests against the shared library
-# using HDC on OHOS.
+# using HDC or QEMU on OHOS.
 #
 
 lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
@@ -45,7 +45,11 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-config.available_features.add('ohos-hdc')
+executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
+    lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
+
+config.available_features.add(executor_kind)
 
 libcxx.test.config.configure(
     libcxx.test.params.DEFAULT_PARAMETERS,

>From dde4d430033711fb7ffa87621ba86b37492f480b Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 14:52:40 +0800
Subject: [PATCH 5/6] Fix something for qemu

---
 libcxx/test/configs/llvm-libc++-ohos.cfg.in                   | 4 +++-
 .../file.streams/fstreams/filebuf.members/close.pass.cpp      | 3 +++
 .../fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp     | 2 ++
 .../syncstream/osyncstream/thread/several_threads.pass.cpp    | 2 ++
 libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in             | 4 +++-
 libcxxabi/test/guard_threaded_test.pass.cpp                   | 2 ++
 libunwind/test/bad_unwind_info.pass.cpp                       | 2 ++
 libunwind/test/configs/llvm-libunwind-ohos.cfg.in             | 4 +++-
 libunwind/test/signal_unwind.pass.cpp                         | 2 ++
 9 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/configs/llvm-libc++-ohos.cfg.in b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
index 20aa7a49d5802..3be1c267ede2a 100644
--- a/libcxx/test/configs/llvm-libc++-ohos.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-ohos.cfg.in
@@ -23,7 +23,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
index 43233decf1b34..fd6abca7c4fe1 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/close.pass.cpp
@@ -6,6 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+// OHOS qemu-user does not match device fd ownership/signal behavior here.
+// UNSUPPORTED: ohos-qemu
+
 // <fstream>
 
 // basic_filebuf<charT,traits>* close();
diff --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
index 2c4cfb0ac7661..ce802d6ceba78 100644
--- a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.copy_file/copy_file_procfs.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14
 // REQUIRES: linux
 // UNSUPPORTED: no-filesystem
+// OHOS qemu-user observes the host /proc, not a real OHOS process /proc.
+// UNSUPPORTED: ohos-qemu
 // XFAIL: no-localization
 
 // <filesystem>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..77db6396755bf 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -10,6 +10,8 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
+// This thread-heavy stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 // <syncstream>
 
diff --git a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
index 8f5a17b3dec64..d0a8d1248f88a 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-ohos.cfg.in
@@ -20,7 +20,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXXABI_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libcxxabi/test/guard_threaded_test.pass.cpp b/libcxxabi/test/guard_threaded_test.pass.cpp
index d898e60288aca..a4e14f0aca9e4 100644
--- a/libcxxabi/test/guard_threaded_test.pass.cpp
+++ b/libcxxabi/test/guard_threaded_test.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: no-exceptions
+// This thread-heavy guard stress test is prohibitively slow under OHOS qemu-user.
+// UNSUPPORTED: ohos-qemu
 
 #define TESTING_CXA_GUARD
 #include "../src/cxa_guard_impl.h"
diff --git a/libunwind/test/bad_unwind_info.pass.cpp b/libunwind/test/bad_unwind_info.pass.cpp
index 332b661d2e98f..76102505f2d67 100644
--- a/libunwind/test/bad_unwind_info.pass.cpp
+++ b/libunwind/test/bad_unwind_info.pass.cpp
@@ -13,6 +13,8 @@
 // REQUIRES: target={{(aarch64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
 // UNSUPPORTED: target={{.*-apple.*}}
+// OHOS qemu-user does not reliably model this invalid unwind-info path.
+// UNSUPPORTED: ohos-qemu
 
 // GCC doesn't support __attribute__((naked)) on AArch64.
 // UNSUPPORTED: gcc
diff --git a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
index f2a032c41a123..71d1e711e36d5 100644
--- a/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
+++ b/libunwind/test/configs/llvm-libunwind-ohos.cfg.in
@@ -45,7 +45,9 @@ import os, site
 site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils'))
 import libcxx.test.params, libcxx.test.config
 
-executor_kind = lit_config.params.get('executor_kind', 'ohos-hdc')
+executor_kind = lit_config.params.get(
+    'executor_kind', getattr(config, 'executor_kind', 'ohos-hdc')
+)
 if executor_kind not in ('ohos-hdc', 'ohos-qemu'):
     lit_config.fatal(f'unsupported OHOS executor_kind: {executor_kind}')
 
diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp
index f8798bc3eabeb..ef73a3ea8c59a 100644
--- a/libunwind/test/signal_unwind.pass.cpp
+++ b/libunwind/test/signal_unwind.pass.cpp
@@ -10,6 +10,8 @@
 // Ensure that the unwinder can cope with the signal handler.
 // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}}
 // UNSUPPORTED: target={{.*-windows.*}}
+// OHOS qemu-user does not provide device-equivalent signal unwinding behavior.
+// UNSUPPORTED: ohos-qemu
 
 // TODO: Figure out why this fails with Memory Sanitizer.
 // XFAIL: msan

>From c81df7252fb00b5b9b41f0a29393e775abc9c810 Mon Sep 17 00:00:00 2001
From: hu1hu1 <fanyaohui at huawei.com>
Date: Thu, 9 Jul 2026 15:19:29 +0800
Subject: [PATCH 6/6] delete run-smoke-test

---
 libcxx/utils/ci/buildkite-pipeline.yml | 12 ----------
 libcxx/utils/ci/run-buildbot           | 31 --------------------------
 2 files changed, 43 deletions(-)

diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 77617f43ae6dc..70bc69637a7f2 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -175,18 +175,6 @@ steps:
 
 - group: OHOS
   steps:
-  - label: OHOS AArch64 HDC Smoke
-    command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-smoke
-    env:
-      CC: clang
-      CXX: clang++
-      OHOS_TARGET_TRIPLE: aarch64-linux-ohos
-      HDC_TIMEOUT_SECONDS: 240
-    agents:
-      queue: libcxx-builders
-      os: linux
-      device: ohos-hdc
-    <<: *common
   - label: OHOS AArch64 HDC Full
     command: libcxx/utils/ci/run-buildbot ohos-hdc-aarch64-full
     env:
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 40739b0fba4d8..e7d751df484e4 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -147,23 +147,6 @@ function require-env-var() {
     fi
 }
 
-function detect-lit() {
-    if [[ -x "${BUILD_DIR}/bin/llvm-lit" ]]; then
-        LIT_RUNNER=("${BUILD_DIR}/bin/llvm-lit")
-    elif command -v llvm-lit >/dev/null 2>&1; then
-        LIT_RUNNER=("$(command -v llvm-lit)")
-    else
-        LIT_RUNNER=(python3 "${MONOREPO_ROOT}/llvm/utils/lit/lit.py")
-    fi
-}
-
-function run-smoke-test() {
-    local test_dir="${1}"
-    local filter="${2}"
-    step "Running smoke test ${filter}"
-    "${LIT_RUNNER[@]}" -j1 -sv --show-xfail --show-unsupported --filter="${filter}" "${test_dir}"
-}
-
 function ohos-lit-args() {
     local args="-j1 -sv --xunit-xml-output test-results.xml --time-tests"
     if python3 -c 'import psutil' >/dev/null 2>&1; then
@@ -903,20 +886,6 @@ ohos-aarch64-full|ohos-hdc-aarch64-full)
 
     check-runtimes
 ;;
-ohos-aarch64-smoke|ohos-hdc-aarch64-smoke)
-    clean
-
-    generate-cmake-ohos-hdc
-
-    step "Building the OHOS smoke test dependencies"
-    ninja -vC "${BUILD_DIR}" cxx-test-depends cxxabi-test-depends unwind-test-depends
-
-    detect-lit
-    run-smoke-test "${BUILD_DIR}/libcxxabi/test" 'thread_local_destruction_order.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'bad_unwind_info.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libunwind/test" 'signal_unwind.pass.cpp'
-    run-smoke-test "${BUILD_DIR}/libcxx/test" 'clocale.pass.cpp'
-;;
 #################################################################
 # Insert vendor-specific internal configurations below.
 #


From libcxx-commits at lists.llvm.org  Sun Jul 12 18:42:47 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 18:42:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a544297.a1accbb4.244acd.f5fb@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 75550c908977e28df9fb76960c8f48ccb6cec6d4 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libc++] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   3 +
 libcxx/include/__config                       |   9 ++
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   4 +
 libcxx/include/math.h                         |  37 ++++++
 libcxx/include/stdio.h                        |  14 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  41 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 291 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..bea314fc5473e 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -857,6 +858,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<float>&, float*>(fl
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(double*, double*, __less<double>&);
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
+#endif
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..a1a2e552bc817 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -111,8 +115,13 @@
 #    define _LIBCPP_ALIGNOF(...) alignof(__VA_ARGS__)
 #    define _ALIGNAS_TYPE(x) alignas(x)
 #    define _ALIGNAS(x) alignas(x)
+#    ifdef __cplusplus
 #    define _NOEXCEPT noexcept
 #    define _NOEXCEPT_(...) noexcept(__VA_ARGS__)
+#    else
+#    define _NOEXCEPT
+#    define _NOEXCEPT_(...)
+#    endif
 #    define _LIBCPP_CONSTEXPR constexpr
 
 #  else
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..3fb2c7d4a67fb 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -57,6 +57,10 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+extern "C" {
+typedef __UINT_LEAST64_TYPE__ time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..190daf23285fa 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float) _NOEXCEPT;
+long double atan2l(long double, long double) _NOEXCEPT;
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..b1985625d5b09 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -118,4 +118,18 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifndef EOF
+#      define EOF (-1)
+#    endif
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*) _NOEXCEPT;
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..43f1d24320fcb 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y) _NOEXCEPT;
+lldiv_t lldiv(long long x, long long y) _NOEXCEPT;
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..fd70bcf621e1b 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,48 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifdef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void* __restrict, const void* __restrict, size_t) _NOEXCEPT;
+void* memmove(void*, const void*, size_t) _NOEXCEPT;
+char* strcpy(char* __restrict, const char* __restrict) _NOEXCEPT;
+char* strncpy(char* __restrict, const char* __restrict, size_t) _NOEXCEPT;
+char* strcat(char* __restrict, const char* __restrict) _NOEXCEPT;
+char* strncat(char* __restrict, const char* __restrict, size_t) _NOEXCEPT;
+int memcmp(const void*, const void*, size_t) _NOEXCEPT;
+int strcmp(const char*, const char*) _NOEXCEPT;
+int strncmp(const char*, const char*, size_t) _NOEXCEPT;
+const void* memchr(const void*, int, size_t) _NOEXCEPT;
+const char* strchr(const char*, int) _NOEXCEPT;
+size_t strcspn(const char*, const char*) _NOEXCEPT;
+const char* strpbrk(const char*, const char*) _NOEXCEPT;
+const char* strrchr(const char*, int) _NOEXCEPT;
+size_t strspn(const char*, const char*) _NOEXCEPT;
+const char* strstr(const char*, const char*) _NOEXCEPT;
+char* strtok(char* __restrict, const char* __restrict) _NOEXCEPT;
+void* memset(void*, int, size_t) _NOEXCEPT;
+size_t strlen(const char*) _NOEXCEPT;
+size_t strnlen(const char*, size_t) _NOEXCEPT;
+void* memset_explicit(void*, int, size_t) _NOEXCEPT;
+int strcoll(const char*, const char*) _NOEXCEPT;
+size_t strxfrm(char* __restrict, const char* __restrict, size_t) _NOEXCEPT;
+char* strerror(int) _NOEXCEPT;
+
+#    ifdef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..2dc5d90b3b4ec
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#  include <iosfwd>
+#  include <cstdio>
+#  include <cerrno>
+#  include <cstring>
+#  include <cmath>
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#    include <cwchar>
+#  endif
+#  include <array>
+#  include <span>
+#  include <algorithm>
+#  include <bitset>
+#  include <variant>
+#  include <optional>
+#  include <numbers>
+#  include <scoped_allocator>
+#  include <typeindex>
+#  include <string_view>
+#  include <numeric>
+#  include <complex>
+#  include <chrono>
+#  include <charconv>
+#  include <expected>
+#  include <random>
+#  include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#  include <vector>
+#  include <deque>
+#  include <string>
+#  include <stack>
+#  include <queue>
+#  include <list>
+#  include <forward_list>
+#  include <map>
+#  include <set>
+#  include <unordered_set>
+#  include <unordered_map>
+#  if __has_include(<mdspan>)
+#    include <mdspan>
+#  endif
+#  include <valarray>
+
+#  if _LIBCPP_STD_VER < 20
+#    include <ciso646>
+#    if __has_include(<cstdalign>)
+#      include <cstdalign>
+#    endif
+#    include <cstdbool>
+#  endif
+#endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 18:44:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 18:44:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a5442e9.0c05e68e.1e82f1.260b@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
----------------
trcrsired wrote:

I have added those declarations and also noexcept/restrict.
Please test ```libcxx/test/libcxx/libcpp_freestanding.sh.cpp``` instead

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 18:44:49 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 18:44:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a544311.a87eb5dc.7d817.3a21@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
----------------
trcrsired wrote:

also i think you are building the gcc compiler wrong. You need x86_64-elf-gcc with --disable-hosted-libstdcxx --without-headers

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 19:03:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 19:03:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a544754.510f576d.1b3cb9.c3f8@mx.google.com>


https://github.com/trcrsired edited https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 19:03:33 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 19:03:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a544775.d681cab9.333b2b.cba4@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
----------------
trcrsired wrote:

i am building a x86_64-elf freestanding gcc to to test it

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 19:23:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 19:23:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a544c29.a87eb5dc.7d817.ca8f@mx.google.com>


trcrsired wrote:

```
[otgnhon testing]$ x86_64-elf-g++ -v
Using built-in specs.
COLLECT_GCC=x86_64-elf-g++
COLLECT_LTO_WRAPPER=/home/otgnhon/toolchains/gnu/x86_64-linux-gnu/x86_64-elf/libexec/gcc/x86_64-elf/17.0.0/lto-wrapper
Target: x86_64-elf
Configured with: /home/otgnhon/toolchains_build/gcc/configure --disable-nls --disable-werror --disable-bootstrap --prefix=/home/otgnhon/toolchains/gnu/x86_64-linux-gnu/x86_64-elf --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-elf --disable-libstdcxx-verbose --enable-languages=c,c++ --disable-sjlj-exceptions --with-libstdcxx-eh-pool-obj-count=0 --disable-hosted-libstdcxx --without-headers --disable-threads --disable-shared --disable-libssp --disable-libquadmath --disable-libatomic --disable-libsanitizer --enable-multilib
Thread model: single
Supported LTO compression algorithms: zlib zstd
gcc version 17.0.0 20260713 (experimental) (GCC)
[otgnhon testing]$ x86_64-elf-g++ -S test2.cc -ffreestanding -I$HOME/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1
In file included from test2.cc:31:
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:106:9: error: 'div_t' has not been declared in '::'
  106 | using ::div_t _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:109:9: error: 'atof' has not been declared in '::'
  109 | using ::atof _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:110:9: error: 'atoi' has not been declared in '::'
  110 | using ::atoi _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:111:9: error: 'atol' has not been declared in '::'
  111 | using ::atol _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:112:9: error: 'atoll' has not been declared in '::'
  112 | using ::atoll _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:113:9: error: 'strtod' has not been declared in '::'
  113 | using ::strtod _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:114:9: error: 'strtof' has not been declared in '::'
  114 | using ::strtof _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:115:9: error: 'strtold' has not been declared in '::'
  115 | using ::strtold _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:116:9: error: 'strtol' has not been declared in '::'
  116 | using ::strtol _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:117:9: error: 'strtoll' has not been declared in '::'
  117 | using ::strtoll _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:118:9: error: 'strtoul' has not been declared in '::'
  118 | using ::strtoul _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:119:9: error: 'strtoull' has not been declared in '::'
  119 | using ::strtoull _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:120:9: error: 'rand' has not been declared in '::'
  120 | using ::rand _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:121:9: error: 'srand' has not been declared in '::'
  121 | using ::srand _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:122:9: error: 'calloc' has not been declared in '::'
  122 | using ::calloc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:123:9: error: 'free' has not been declared in '::'
  123 | using ::free _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:124:9: error: 'malloc' has not been declared in '::'
  124 | using ::malloc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:125:9: error: 'realloc' has not been declared in '::'
  125 | using ::realloc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:126:9: error: 'abort' has not been declared in '::'
  126 | using ::abort _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:127:9: error: 'atexit' has not been declared in '::'
  127 | using ::atexit _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:128:9: error: 'exit' has not been declared in '::'
  128 | using ::exit _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:129:9: error: '_Exit' has not been declared in '::'
  129 | using ::_Exit _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:130:9: error: 'getenv' has not been declared in '::'
  130 | using ::getenv _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:131:9: error: 'system' has not been declared in '::'
  131 | using ::system _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:132:9: error: 'bsearch' has not been declared in '::'
  132 | using ::bsearch _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:133:9: error: 'qsort' has not been declared in '::'
  133 | using ::qsort _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:135:9: error: 'labs' has not been declared in '::'
  135 | using ::labs _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:136:9: error: 'llabs' has not been declared in '::'
  136 | using ::llabs _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:140:9: error: 'mblen' has not been declared in '::'
  140 | using ::mblen _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:148:9: error: 'at_quick_exit' has not been declared in '::'
  148 | using ::at_quick_exit _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:149:9: error: 'quick_exit' has not been declared in '::'
  149 | using ::quick_exit _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdlib:152:9: error: 'aligned_alloc' has not been declared in '::'
  152 | using ::aligned_alloc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~~~~~
In file included from /home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/__chrono/system_clock.h:16,
                 from /home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/__chrono/high_resolution_clock.h:14,
                 from /home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/__thread/poll_with_backoff.h:14,
                 from /home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/__atomic/atomic_sync.h:18,
                 from /home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/__atomic/atomic.h:12,
                 from /home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/__atomic/aliases.h:12,
                 from /home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/atomic:605,
                 from test2.cc:45:
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:72:9: error: 'clock_t' has not been declared in '::'
   72 | using ::clock_t _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:74:9: error: 'tm' has not been declared in '::'
   74 | using ::tm _LIBCPP_USING_IF_EXISTS;
      |         ^~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:76:9: error: 'timespec' has not been declared in '::'
   76 | using ::timespec _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:78:9: error: 'clock' has not been declared in '::'
   78 | using ::clock _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:79:9: error: 'difftime' has not been declared in '::'
   79 | using ::difftime _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:80:9: error: 'mktime' has not been declared in '::'
   80 | using ::mktime _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:81:9: error: 'time' has not been declared in '::'
   81 | using ::time _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:82:9: error: 'asctime' has not been declared in '::'
   82 | using ::asctime _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:83:9: error: 'ctime' has not been declared in '::'
   83 | using ::ctime _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:84:9: error: 'gmtime' has not been declared in '::'
   84 | using ::gmtime _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:85:9: error: 'localtime' has not been declared in '::'
   85 | using ::localtime _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:86:9: error: 'strftime' has not been declared in '::'
   86 | using ::strftime _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/ctime:88:9: error: 'timespec_get' has not been declared in '::'
   88 | using ::timespec_get _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~~~~
In file included from /home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/__string/char_traits.h:29,
                 from /home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/__iterator/istreambuf_iterator.h:19,
                 from /home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/iterator:693,
                 from test2.cc:51:
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:120:9: error: 'FILE' has not been declared in '::'
  120 | using ::FILE _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:121:9: error: 'fpos_t' has not been declared in '::'
  121 | using ::fpos_t _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:123:9: error: 'fclose' has not been declared in '::'
  123 | using ::fclose _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:124:9: error: 'fflush' has not been declared in '::'
  124 | using ::fflush _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:125:9: error: 'setbuf' has not been declared in '::'
  125 | using ::setbuf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:126:9: error: 'setvbuf' has not been declared in '::'
  126 | using ::setvbuf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:127:9: error: 'fprintf' has not been declared in '::'
  127 | using ::fprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:128:9: error: 'fscanf' has not been declared in '::'
  128 | using ::fscanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:129:9: error: 'snprintf' has not been declared in '::'
  129 | using ::snprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:130:9: error: 'sprintf' has not been declared in '::'
  130 | using ::sprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:131:9: error: 'sscanf' has not been declared in '::'
  131 | using ::sscanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:132:9: error: 'vfprintf' has not been declared in '::'
  132 | using ::vfprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:133:9: error: 'vfscanf' has not been declared in '::'
  133 | using ::vfscanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:134:9: error: 'vsscanf' has not been declared in '::'
  134 | using ::vsscanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:135:9: error: 'vsnprintf' has not been declared in '::'
  135 | using ::vsnprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:136:9: error: 'vsprintf' has not been declared in '::'
  136 | using ::vsprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:137:9: error: 'fgetc' has not been declared in '::'
  137 | using ::fgetc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:138:9: error: 'fgets' has not been declared in '::'
  138 | using ::fgets _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:139:9: error: 'fputc' has not been declared in '::'
  139 | using ::fputc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:140:9: error: 'fputs' has not been declared in '::'
  140 | using ::fputs _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:141:9: error: 'getc' has not been declared in '::'
  141 | using ::getc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:142:9: error: 'putc' has not been declared in '::'
  142 | using ::putc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:143:9: error: 'ungetc' has not been declared in '::'
  143 | using ::ungetc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:144:9: error: 'fread' has not been declared in '::'
  144 | using ::fread _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:145:9: error: 'fwrite' has not been declared in '::'
  145 | using ::fwrite _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:146:9: error: 'fgetpos' has not been declared in '::'
  146 | using ::fgetpos _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:147:9: error: 'fseek' has not been declared in '::'
  147 | using ::fseek _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:148:9: error: 'fsetpos' has not been declared in '::'
  148 | using ::fsetpos _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:149:9: error: 'ftell' has not been declared in '::'
  149 | using ::ftell _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:150:9: error: 'rewind' has not been declared in '::'
  150 | using ::rewind _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:151:9: error: 'clearerr' has not been declared in '::'
  151 | using ::clearerr _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:152:9: error: 'feof' has not been declared in '::'
  152 | using ::feof _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:153:9: error: 'ferror' has not been declared in '::'
  153 | using ::ferror _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:154:9: error: 'perror' has not been declared in '::'
  154 | using ::perror _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:156:9: error: 'fopen' has not been declared in '::'
  156 | using ::fopen _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:157:9: error: 'freopen' has not been declared in '::'
  157 | using ::freopen _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:159:9: error: 'rename' has not been declared in '::'
  159 | using ::rename _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:160:9: error: 'tmpfile' has not been declared in '::'
  160 | using ::tmpfile _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:161:9: error: 'tmpnam' has not been declared in '::'
  161 | using ::tmpnam _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:163:9: error: 'getchar' has not been declared in '::'
  163 | using ::getchar _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:167:9: error: 'scanf' has not been declared in '::'
  167 | using ::scanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:168:9: error: 'vscanf' has not been declared in '::'
  168 | using ::vscanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:170:9: error: 'printf' has not been declared in '::'
  170 | using ::printf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:171:9: error: 'putchar' has not been declared in '::'
  171 | using ::putchar _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:172:9: error: 'puts' has not been declared in '::'
  172 | using ::puts _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cstdio:173:9: error: 'vprintf' has not been declared in '::'
  173 | using ::vprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
In file included from test2.cc:63:
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:364:9: error: 'float_t' has not been declared in '::'
  364 | using ::float_t _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:365:9: error: 'double_t' has not been declared in '::'
  365 | using ::double_t _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:370:9: error: 'acosf' has not been declared in '::'
  370 | using ::acosf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:372:9: error: 'asinf' has not been declared in '::'
  372 | using ::asinf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:374:9: error: 'atanf' has not been declared in '::'
  374 | using ::atanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:378:9: error: 'ceilf' has not been declared in '::'
  378 | using ::ceilf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:380:9: error: 'cosf' has not been declared in '::'
  380 | using ::cosf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:382:9: error: 'coshf' has not been declared in '::'
  382 | using ::coshf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:385:9: error: 'expf' has not been declared in '::'
  385 | using ::expf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:388:9: error: 'fabsf' has not been declared in '::'
  388 | using ::fabsf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:390:9: error: 'floorf' has not been declared in '::'
  390 | using ::floorf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:393:9: error: 'fmodf' has not been declared in '::'
  393 | using ::fmodf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:396:9: error: 'frexpf' has not been declared in '::'
  396 | using ::frexpf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:398:9: error: 'ldexpf' has not been declared in '::'
  398 | using ::ldexpf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:401:9: error: 'logf' has not been declared in '::'
  401 | using ::logf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:404:9: error: 'log10f' has not been declared in '::'
  404 | using ::log10f _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:406:9: error: 'modff' has not been declared in '::'
  406 | using ::modff _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:409:9: error: 'powf' has not been declared in '::'
  409 | using ::powf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:412:9: error: 'sinf' has not been declared in '::'
  412 | using ::sinf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:414:9: error: 'sinhf' has not been declared in '::'
  414 | using ::sinhf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:417:9: error: 'sqrtf' has not been declared in '::'
  417 | using ::sqrtf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:419:9: error: 'tanf' has not been declared in '::'
  419 | using ::tanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:422:9: error: 'tanhf' has not been declared in '::'
  422 | using ::tanhf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:425:9: error: 'acoshf' has not been declared in '::'
  425 | using ::acoshf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:427:9: error: 'asinhf' has not been declared in '::'
  427 | using ::asinhf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:429:9: error: 'atanhf' has not been declared in '::'
  429 | using ::atanhf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:431:9: error: 'cbrtf' has not been declared in '::'
  431 | using ::cbrtf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:434:9: error: 'copysignf' has not been declared in '::'
  434 | using ::copysignf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:437:9: error: 'erff' has not been declared in '::'
  437 | using ::erff _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:439:9: error: 'erfcf' has not been declared in '::'
  439 | using ::erfcf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:441:9: error: 'exp2f' has not been declared in '::'
  441 | using ::exp2f _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:443:9: error: 'expm1f' has not been declared in '::'
  443 | using ::expm1f _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:445:9: error: 'fdimf' has not been declared in '::'
  445 | using ::fdimf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:446:9: error: 'fmaf' has not been declared in '::'
  446 | using ::fmaf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:449:9: error: 'fmaxf' has not been declared in '::'
  449 | using ::fmaxf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:451:9: error: 'fminf' has not been declared in '::'
  451 | using ::fminf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:453:9: error: 'hypotf' has not been declared in '::'
  453 | using ::hypotf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:455:9: error: 'ilogbf' has not been declared in '::'
  455 | using ::ilogbf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:457:9: error: 'lgammaf' has not been declared in '::'
  457 | using ::lgammaf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:459:9: error: 'llrintf' has not been declared in '::'
  459 | using ::llrintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:461:9: error: 'llroundf' has not been declared in '::'
  461 | using ::llroundf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:463:9: error: 'log1pf' has not been declared in '::'
  463 | using ::log1pf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:465:9: error: 'log2f' has not been declared in '::'
  465 | using ::log2f _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:467:9: error: 'logbf' has not been declared in '::'
  467 | using ::logbf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:469:9: error: 'lrintf' has not been declared in '::'
  469 | using ::lrintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:471:9: error: 'lroundf' has not been declared in '::'
  471 | using ::lroundf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:473:9: error: 'nan' has not been declared in '::'
  473 | using ::nan _LIBCPP_USING_IF_EXISTS;
      |         ^~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:474:9: error: 'nanf' has not been declared in '::'
  474 | using ::nanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:477:9: error: 'nearbyintf' has not been declared in '::'
  477 | using ::nearbyintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:479:9: error: 'nextafterf' has not been declared in '::'
  479 | using ::nextafterf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:481:9: error: 'nexttowardf' has not been declared in '::'
  481 | using ::nexttowardf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:483:9: error: 'remainderf' has not been declared in '::'
  483 | using ::remainderf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:485:9: error: 'remquof' has not been declared in '::'
  485 | using ::remquof _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:487:9: error: 'rintf' has not been declared in '::'
  487 | using ::rintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:489:9: error: 'roundf' has not been declared in '::'
  489 | using ::roundf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:491:9: error: 'scalblnf' has not been declared in '::'
  491 | using ::scalblnf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:493:9: error: 'scalbnf' has not been declared in '::'
  493 | using ::scalbnf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:495:9: error: 'tgammaf' has not been declared in '::'
  495 | using ::tgammaf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:497:9: error: 'truncf' has not been declared in '::'
  497 | using ::truncf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:499:9: error: 'acosl' has not been declared in '::'
  499 | using ::acosl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:500:9: error: 'asinl' has not been declared in '::'
  500 | using ::asinl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:501:9: error: 'atanl' has not been declared in '::'
  501 | using ::atanl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:503:9: error: 'ceill' has not been declared in '::'
  503 | using ::ceill _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:504:9: error: 'cosl' has not been declared in '::'
  504 | using ::cosl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:505:9: error: 'coshl' has not been declared in '::'
  505 | using ::coshl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:506:9: error: 'expl' has not been declared in '::'
  506 | using ::expl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:507:9: error: 'fabsl' has not been declared in '::'
  507 | using ::fabsl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:508:9: error: 'floorl' has not been declared in '::'
  508 | using ::floorl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:509:9: error: 'fmodl' has not been declared in '::'
  509 | using ::fmodl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:510:9: error: 'frexpl' has not been declared in '::'
  510 | using ::frexpl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:511:9: error: 'ldexpl' has not been declared in '::'
  511 | using ::ldexpl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:512:9: error: 'logl' has not been declared in '::'
  512 | using ::logl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:513:9: error: 'log10l' has not been declared in '::'
  513 | using ::log10l _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:514:9: error: 'modfl' has not been declared in '::'
  514 | using ::modfl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:515:9: error: 'powl' has not been declared in '::'
  515 | using ::powl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:516:9: error: 'sinl' has not been declared in '::'
  516 | using ::sinl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:517:9: error: 'sinhl' has not been declared in '::'
  517 | using ::sinhl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:518:9: error: 'sqrtl' has not been declared in '::'
  518 | using ::sqrtl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:519:9: error: 'tanl' has not been declared in '::'
  519 | using ::tanl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:521:9: error: 'tanhl' has not been declared in '::'
  521 | using ::tanhl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:522:9: error: 'acoshl' has not been declared in '::'
  522 | using ::acoshl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:523:9: error: 'asinhl' has not been declared in '::'
  523 | using ::asinhl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:524:9: error: 'atanhl' has not been declared in '::'
  524 | using ::atanhl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:525:9: error: 'cbrtl' has not been declared in '::'
  525 | using ::cbrtl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:527:9: error: 'copysignl' has not been declared in '::'
  527 | using ::copysignl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:529:9: error: 'erfl' has not been declared in '::'
  529 | using ::erfl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:530:9: error: 'erfcl' has not been declared in '::'
  530 | using ::erfcl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:531:9: error: 'exp2l' has not been declared in '::'
  531 | using ::exp2l _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:532:9: error: 'expm1l' has not been declared in '::'
  532 | using ::expm1l _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:533:9: error: 'fdiml' has not been declared in '::'
  533 | using ::fdiml _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:534:9: error: 'fmal' has not been declared in '::'
  534 | using ::fmal _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:535:9: error: 'fmaxl' has not been declared in '::'
  535 | using ::fmaxl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:536:9: error: 'fminl' has not been declared in '::'
  536 | using ::fminl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:537:9: error: 'hypotl' has not been declared in '::'
  537 | using ::hypotl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:538:9: error: 'ilogbl' has not been declared in '::'
  538 | using ::ilogbl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:539:9: error: 'lgammal' has not been declared in '::'
  539 | using ::lgammal _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:540:9: error: 'llrintl' has not been declared in '::'
  540 | using ::llrintl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:541:9: error: 'llroundl' has not been declared in '::'
  541 | using ::llroundl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:542:9: error: 'log1pl' has not been declared in '::'
  542 | using ::log1pl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:543:9: error: 'log2l' has not been declared in '::'
  543 | using ::log2l _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:544:9: error: 'logbl' has not been declared in '::'
  544 | using ::logbl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:545:9: error: 'lrintl' has not been declared in '::'
  545 | using ::lrintl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:546:9: error: 'lroundl' has not been declared in '::'
  546 | using ::lroundl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:547:9: error: 'nanl' has not been declared in '::'
  547 | using ::nanl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:548:9: error: 'nearbyintl' has not been declared in '::'
  548 | using ::nearbyintl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:549:9: error: 'nextafterl' has not been declared in '::'
  549 | using ::nextafterl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:550:9: error: 'nexttowardl' has not been declared in '::'
  550 | using ::nexttowardl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:551:9: error: 'remainderl' has not been declared in '::'
  551 | using ::remainderl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:552:9: error: 'remquol' has not been declared in '::'
  552 | using ::remquol _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:553:9: error: 'rintl' has not been declared in '::'
  553 | using ::rintl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:554:9: error: 'roundl' has not been declared in '::'
  554 | using ::roundl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:555:9: error: 'scalblnl' has not been declared in '::'
  555 | using ::scalblnl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:556:9: error: 'scalbnl' has not been declared in '::'
  556 | using ::scalbnl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:557:9: error: 'tgammal' has not been declared in '::'
  557 | using ::tgammal _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cmath:558:9: error: 'truncl' has not been declared in '::'
  558 | using ::truncl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
In file included from /home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwctype:56,
                 from /home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:116,
                 from test2.cc:65:
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:55:9: error: 'isalnum' has not been declared in '::'
   55 | using ::isalnum _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:56:9: error: 'isalpha' has not been declared in '::'
   56 | using ::isalpha _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:57:9: error: 'isblank' has not been declared in '::'
   57 | using ::isblank _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:58:9: error: 'iscntrl' has not been declared in '::'
   58 | using ::iscntrl _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:59:9: error: 'isdigit' has not been declared in '::'
   59 | using ::isdigit _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:60:9: error: 'isgraph' has not been declared in '::'
   60 | using ::isgraph _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:61:9: error: 'islower' has not been declared in '::'
   61 | using ::islower _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:62:9: error: 'isprint' has not been declared in '::'
   62 | using ::isprint _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:63:9: error: 'ispunct' has not been declared in '::'
   63 | using ::ispunct _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:64:9: error: 'isspace' has not been declared in '::'
   64 | using ::isspace _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:65:9: error: 'isupper' has not been declared in '::'
   65 | using ::isupper _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:66:9: error: 'isxdigit' has not been declared in '::'
   66 | using ::isxdigit _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:67:9: error: 'tolower' has not been declared in '::'
   67 | using ::tolower _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cctype:68:9: error: 'toupper' has not been declared in '::'
   68 | using ::toupper _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:135:9: error: 'tm' has not been declared in '::'
  135 | using ::tm _LIBCPP_USING_IF_EXISTS;
      |         ^~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:136:9: error: 'wint_t' has not been declared in '::'
  136 | using ::wint_t _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:137:9: error: 'FILE' has not been declared in '::'
  137 | using ::FILE _LIBCPP_USING_IF_EXISTS;
      |         ^~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:138:9: error: 'fwprintf' has not been declared in '::'
  138 | using ::fwprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:139:9: error: 'fwscanf' has not been declared in '::'
  139 | using ::fwscanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:140:9: error: 'swprintf' has not been declared in '::'
  140 | using ::swprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:141:9: error: 'vfwprintf' has not been declared in '::'
  141 | using ::vfwprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:142:9: error: 'vswprintf' has not been declared in '::'
  142 | using ::vswprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:143:9: error: 'swscanf' has not been declared in '::'
  143 | using ::swscanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:144:9: error: 'vfwscanf' has not been declared in '::'
  144 | using ::vfwscanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:145:9: error: 'vswscanf' has not been declared in '::'
  145 | using ::vswscanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:146:9: error: 'fgetwc' has not been declared in '::'
  146 | using ::fgetwc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:147:9: error: 'fgetws' has not been declared in '::'
  147 | using ::fgetws _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:148:9: error: 'fputwc' has not been declared in '::'
  148 | using ::fputwc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:149:9: error: 'fputws' has not been declared in '::'
  149 | using ::fputws _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:150:9: error: 'fwide' has not been declared in '::'
  150 | using ::fwide _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:151:9: error: 'getwc' has not been declared in '::'
  151 | using ::getwc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:152:9: error: 'putwc' has not been declared in '::'
  152 | using ::putwc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:153:9: error: 'ungetwc' has not been declared in '::'
  153 | using ::ungetwc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:154:9: error: 'wcstod' has not been declared in '::'
  154 | using ::wcstod _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:155:9: error: 'wcstof' has not been declared in '::'
  155 | using ::wcstof _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:156:9: error: 'wcstold' has not been declared in '::'
  156 | using ::wcstold _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:157:9: error: 'wcstol' has not been declared in '::'
  157 | using ::wcstol _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:158:9: error: 'wcstoll' has not been declared in '::'
  158 | using ::wcstoll _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:159:9: error: 'wcstoul' has not been declared in '::'
  159 | using ::wcstoul _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:160:9: error: 'wcstoull' has not been declared in '::'
  160 | using ::wcstoull _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:161:9: error: 'wcscpy' has not been declared in '::'
  161 | using ::wcscpy _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:162:9: error: 'wcsncpy' has not been declared in '::'
  162 | using ::wcsncpy _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:163:9: error: 'wcscat' has not been declared in '::'
  163 | using ::wcscat _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:164:9: error: 'wcsncat' has not been declared in '::'
  164 | using ::wcsncat _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:165:9: error: 'wcscmp' has not been declared in '::'
  165 | using ::wcscmp _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:166:9: error: 'wcscoll' has not been declared in '::'
  166 | using ::wcscoll _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:167:9: error: 'wcsncmp' has not been declared in '::'
  167 | using ::wcsncmp _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:168:9: error: 'wcsxfrm' has not been declared in '::'
  168 | using ::wcsxfrm _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:169:9: error: 'wcschr' has not been declared in '::'
  169 | using ::wcschr _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:170:9: error: 'wcspbrk' has not been declared in '::'
  170 | using ::wcspbrk _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:171:9: error: 'wcsrchr' has not been declared in '::'
  171 | using ::wcsrchr _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:172:9: error: 'wcsstr' has not been declared in '::'
  172 | using ::wcsstr _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:173:9: error: 'wmemchr' has not been declared in '::'
  173 | using ::wmemchr _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:174:9: error: 'wcscspn' has not been declared in '::'
  174 | using ::wcscspn _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:175:9: error: 'wcslen' has not been declared in '::'
  175 | using ::wcslen _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:176:9: error: 'wcsspn' has not been declared in '::'
  176 | using ::wcsspn _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:177:9: error: 'wcstok' has not been declared in '::'
  177 | using ::wcstok _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:178:9: error: 'wmemcmp' has not been declared in '::'
  178 | using ::wmemcmp _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:179:9: error: 'wmemcpy' has not been declared in '::'
  179 | using ::wmemcpy _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:180:9: error: 'wmemmove' has not been declared in '::'
  180 | using ::wmemmove _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:181:9: error: 'wmemset' has not been declared in '::'
  181 | using ::wmemset _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:182:9: error: 'wcsftime' has not been declared in '::'
  182 | using ::wcsftime _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:183:9: error: 'btowc' has not been declared in '::'
  183 | using ::btowc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:184:9: error: 'wctob' has not been declared in '::'
  184 | using ::wctob _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:185:9: error: 'mbsinit' has not been declared in '::'
  185 | using ::mbsinit _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:186:9: error: 'mbrlen' has not been declared in '::'
  186 | using ::mbrlen _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:187:9: error: 'mbrtowc' has not been declared in '::'
  187 | using ::mbrtowc _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:188:9: error: 'wcrtomb' has not been declared in '::'
  188 | using ::wcrtomb _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:189:9: error: 'mbsrtowcs' has not been declared in '::'
  189 | using ::mbsrtowcs _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:190:9: error: 'wcsrtombs' has not been declared in '::'
  190 | using ::wcsrtombs _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:192:9: error: 'getwchar' has not been declared in '::'
  192 | using ::getwchar _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:193:9: error: 'vwscanf' has not been declared in '::'
  193 | using ::vwscanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:194:9: error: 'wscanf' has not been declared in '::'
  194 | using ::wscanf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:196:9: error: 'putwchar' has not been declared in '::'
  196 | using ::putwchar _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:197:9: error: 'vwprintf' has not been declared in '::'
  197 | using ::vwprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:198:9: error: 'wprintf' has not been declared in '::'
  198 | using ::wprintf _LIBCPP_USING_IF_EXISTS;
      |         ^~~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar: In function 'constexpr std::__1::size_t std::__1::__constexpr_wcslen(const wchar_t*)':
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:205:17: error: 'wcslen' is not a member of 'std'
  205 |     return std::wcslen(__str);
      |                 ^~~~~~
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar: In function 'constexpr int std::__1::__constexpr_wmemcmp(const wchar_t*, const wchar_t*, size_t)':
/home/otgnhon/toolchains/llvm/x86_64-elf/x86_64-elf/include/c++/v1/cwchar:220:17: error: 'wmemcmp' is not a member of 'std'
  220 |     return std::wmemcmp(__lhs, __rhs, __count);
      |                 ^~~~~~~

```

just way too many for gcc

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 19:24:11 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 19:24:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a544c4b.a1accbb4.244acd.0e40@mx.google.com>


================
@@ -60,11 +60,49 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifndef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
----------------
trcrsired wrote:

i think this has to be done at gcc side. Just too many for x86_64-elf freestanding gcc

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Sun Jul 12 19:29:43 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 19:29:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a544d97.e5907290.281f16.22ce@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 38cf359bf0349ec5b915a7e1d5793635aabb4c3b Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libc++] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   3 +
 libcxx/include/__config                       |  13 +-
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   4 +
 libcxx/include/math.h                         |  37 ++++++
 libcxx/include/stdio.h                        |  14 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  41 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 293 insertions(+), 17 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..bea314fc5473e 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#if _LIBCPP_FREESTANDING == 0
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -857,6 +858,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<float>&, float*>(fl
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(double*, double*, __less<double>&);
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
+#endif
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..219014b8bc01a 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING 1
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -111,8 +115,13 @@
 #    define _LIBCPP_ALIGNOF(...) alignof(__VA_ARGS__)
 #    define _ALIGNAS_TYPE(x) alignas(x)
 #    define _ALIGNAS(x) alignas(x)
-#    define _NOEXCEPT noexcept
-#    define _NOEXCEPT_(...) noexcept(__VA_ARGS__)
+#    ifdef __cplusplus
+#      define _NOEXCEPT noexcept
+#      define _NOEXCEPT_(...) noexcept(__VA_ARGS__)
+#    else
+#      define _NOEXCEPT
+#      define _NOEXCEPT_(...)
+#    endif
 #    define _LIBCPP_CONSTEXPR constexpr
 
 #  else
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..4470ce4ca3400 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine01 _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..dce7ad7ca9196 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif _LIBCPP_FREESTANDING != 0
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..3fb2c7d4a67fb 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -57,6 +57,10 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+extern "C" {
+typedef __UINT_LEAST64_TYPE__ time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..190daf23285fa 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float) _NOEXCEPT;
+long double atan2l(long double, long double) _NOEXCEPT;
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..b1985625d5b09 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -118,4 +118,18 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifndef EOF
+#      define EOF (-1)
+#    endif
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*) _NOEXCEPT;
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..43f1d24320fcb 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y) _NOEXCEPT;
+lldiv_t lldiv(long long x, long long y) _NOEXCEPT;
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..fd70bcf621e1b 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,48 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifdef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void* __restrict, const void* __restrict, size_t) _NOEXCEPT;
+void* memmove(void*, const void*, size_t) _NOEXCEPT;
+char* strcpy(char* __restrict, const char* __restrict) _NOEXCEPT;
+char* strncpy(char* __restrict, const char* __restrict, size_t) _NOEXCEPT;
+char* strcat(char* __restrict, const char* __restrict) _NOEXCEPT;
+char* strncat(char* __restrict, const char* __restrict, size_t) _NOEXCEPT;
+int memcmp(const void*, const void*, size_t) _NOEXCEPT;
+int strcmp(const char*, const char*) _NOEXCEPT;
+int strncmp(const char*, const char*, size_t) _NOEXCEPT;
+const void* memchr(const void*, int, size_t) _NOEXCEPT;
+const char* strchr(const char*, int) _NOEXCEPT;
+size_t strcspn(const char*, const char*) _NOEXCEPT;
+const char* strpbrk(const char*, const char*) _NOEXCEPT;
+const char* strrchr(const char*, int) _NOEXCEPT;
+size_t strspn(const char*, const char*) _NOEXCEPT;
+const char* strstr(const char*, const char*) _NOEXCEPT;
+char* strtok(char* __restrict, const char* __restrict) _NOEXCEPT;
+void* memset(void*, int, size_t) _NOEXCEPT;
+size_t strlen(const char*) _NOEXCEPT;
+size_t strnlen(const char*, size_t) _NOEXCEPT;
+void* memset_explicit(void*, int, size_t) _NOEXCEPT;
+int strcoll(const char*, const char*) _NOEXCEPT;
+size_t strxfrm(char* __restrict, const char* __restrict, size_t) _NOEXCEPT;
+char* strerror(int) _NOEXCEPT;
+
+#    ifdef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..2dc5d90b3b4ec
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#if _LIBCPP_FREESTANDING && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#  include <iosfwd>
+#  include <cstdio>
+#  include <cerrno>
+#  include <cstring>
+#  include <cmath>
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#    include <cwchar>
+#  endif
+#  include <array>
+#  include <span>
+#  include <algorithm>
+#  include <bitset>
+#  include <variant>
+#  include <optional>
+#  include <numbers>
+#  include <scoped_allocator>
+#  include <typeindex>
+#  include <string_view>
+#  include <numeric>
+#  include <complex>
+#  include <chrono>
+#  include <charconv>
+#  include <expected>
+#  include <random>
+#  include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#  include <vector>
+#  include <deque>
+#  include <string>
+#  include <stack>
+#  include <queue>
+#  include <list>
+#  include <forward_list>
+#  include <map>
+#  include <set>
+#  include <unordered_set>
+#  include <unordered_map>
+#  if __has_include(<mdspan>)
+#    include <mdspan>
+#  endif
+#  include <valarray>
+
+#  if _LIBCPP_STD_VER < 20
+#    include <ciso646>
+#    if __has_include(<cstdalign>)
+#      include <cstdalign>
+#    endif
+#    include <cstdbool>
+#  endif
+#endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 20:55:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 20:55:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] avoid include Uppercase windows
	headers (PR #208903)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208903@github.com>
Message-ID: <6a546195.c5504292.28b3b8.e83e@mx.google.com>


trcrsired wrote:

> LGTM.
> 
> We generally (across all of LLVM) use all-lowercase forms to include Windows headers, which works the same across both mingw headers and official WinSDK. This, both because mingw is all lowercase, and because WinSDK isn't self-consistent with how it names/includes its own headers.

then continue the ci and if passed merge it?

https://github.com/llvm/llvm-project/pull/208903

From libcxx-commits at lists.llvm.org  Sun Jul 12 21:23:54 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 21:23:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a54685a.af6ba3ed.36d9db.4293@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/209018

>From c4349f973fed37783692cce1aae993813563a5dd Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Sun, 12 Jul 2026 21:31:47 +0800
Subject: [PATCH 1/2] [libcxx][unique_ptr] Implement LWG 4144: Disallow
 unique_ptr<T&, D>

---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__memory/unique_ptr.h          |  9 +++++
 .../non_pointable.single.verify.cpp           | 40 +++++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 8441da10d78de..c203fa424556a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -101,7 +101,7 @@
 "`LWG4140 <https://wg21.link/LWG4140>`__","Useless default constructors for bit reference types","2024-11 (Wrocław)","|Complete|","","`#118356 <https://github.com/llvm/llvm-project/issues/118356>`__",""
 "`LWG4141 <https://wg21.link/LWG4141>`__","Improve prohibitions on ""additional storage""","2024-11 (Wrocław)","","","`#118357 <https://github.com/llvm/llvm-project/issues/118357>`__",""
 "`LWG4142 <https://wg21.link/LWG4142>`__","``format_parse_context::check_dynamic_spec`` should require at least one type","2024-11 (Wrocław)","","","`#118358 <https://github.com/llvm/llvm-project/issues/118358>`__",""
-"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","","","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
+"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","|Complete|","23","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
 "`LWG4147 <https://wg21.link/LWG4147>`__","Precondition on ``inplace_vector::emplace``","2024-11 (Wrocław)","","","`#118361 <https://github.com/llvm/llvm-project/issues/118361>`__",""
 "`LWG4148 <https://wg21.link/LWG4148>`__","``unique_ptr::operator*`` should not allow dangling references","2024-11 (Wrocław)","","","`#118362 <https://github.com/llvm/llvm-project/issues/118362>`__",""
 "`LWG4153 <https://wg21.link/LWG4153>`__","Fix extra ""-1"" for ``philox_engine::max()``","2024-11 (Wrocław)","","","`#118363 <https://github.com/llvm/llvm-project/issues/118363>`__",""
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 7e68faa15dabb..93878301b0322 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -43,6 +43,7 @@
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_extent.h>
 #include <__type_traits/remove_reference.h>
+#include <__type_traits/void_t.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -100,6 +101,12 @@ inline const bool __can_dereference = false;
 template <class _Tp>
 inline const bool __can_dereference<_Tp, decltype((void)*std::declval<_Tp>())> = true;
 
+template <class _Tp, class = void>
+inline const bool __is_pointable = false;
+
+template <class _Tp>
+inline const bool __is_pointable<_Tp, __void_t<_Tp*> > = true;
+
 #if defined(_LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI)
 #  define _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI __attribute__((__trivial_abi__))
 #else
@@ -111,6 +118,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
+
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
   using pointer _LIBCPP_NODEBUG = __pointer<_Tp, deleter_type>;
 
   static_assert(!is_rvalue_reference<deleter_type>::value, "the specified deleter type cannot be an rvalue reference");
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
new file mode 100644
index 0000000000000..14829629861c6
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
+
+void reference_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<int&, Deleter>);
+}
+
+void abominable_function_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
+}

>From 79c6048aef67e3bfff149275bd08d8540ec5d3fd Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Mon, 13 Jul 2026 12:17:49 +0800
Subject: [PATCH 2/2] adjust lit test

1. Add [unique.ptr.single.general]/1 marker
2. Merge test cases into one
3. Remove useless lines
---
 .../unique.ptr.class/non_pointable.single.verify.cpp   | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
index 14829629861c6..6f27cdbdce4eb 100644
--- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -10,10 +10,9 @@
 
 // unique_ptr
 
+// [unique.ptr.single.general]/1 (added by LWG4144)
 // A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
 
-// LWG 4144
-
 // XFAIL: FROZEN-CXX03-HEADERS-FIXME
 
 #include <memory>
@@ -24,17 +23,12 @@ struct Deleter {
   void operator()(pointer) const;
 };
 
-typedef void Function();
 typedef void AbominableFunction() const;
 
-void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
-
-void reference_type() {
+void test() {
   // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
   (void)sizeof(std::unique_ptr<int&, Deleter>);
-}
 
-void abominable_function_type() {
   // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
   (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
 }


From libcxx-commits at lists.llvm.org  Sun Jul 12 21:25:44 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 21:25:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a5468c8.13be6df8.145c.17fd@mx.google.com>


================
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
----------------
emmett2020 wrote:

Many thanks. I won't make this mistake again.

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 21:28:04 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 21:28:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a546954.07d7ca98.9dee3.dde2@mx.google.com>


================
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
+
+void reference_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<int&, Deleter>);
+}
+
+void abominable_function_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
+}
----------------
emmett2020 wrote:

Thanks for the review. I think I'll try to merge test cases into one first when reasonable.

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 21:29:48 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 21:29:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a5469bc.7270d2fd.2d4879.54c5@mx.google.com>


================
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
----------------
emmett2020 wrote:

Done, added this reference. Thanks!

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 21:30:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 21:30:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a5469c8.c56fb102.7c9cd.2abe@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/209018

>From c4349f973fed37783692cce1aae993813563a5dd Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Sun, 12 Jul 2026 21:31:47 +0800
Subject: [PATCH 1/2] [libcxx][unique_ptr] Implement LWG 4144: Disallow
 unique_ptr<T&, D>

---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__memory/unique_ptr.h          |  9 +++++
 .../non_pointable.single.verify.cpp           | 40 +++++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 8441da10d78de..c203fa424556a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -101,7 +101,7 @@
 "`LWG4140 <https://wg21.link/LWG4140>`__","Useless default constructors for bit reference types","2024-11 (Wrocław)","|Complete|","","`#118356 <https://github.com/llvm/llvm-project/issues/118356>`__",""
 "`LWG4141 <https://wg21.link/LWG4141>`__","Improve prohibitions on ""additional storage""","2024-11 (Wrocław)","","","`#118357 <https://github.com/llvm/llvm-project/issues/118357>`__",""
 "`LWG4142 <https://wg21.link/LWG4142>`__","``format_parse_context::check_dynamic_spec`` should require at least one type","2024-11 (Wrocław)","","","`#118358 <https://github.com/llvm/llvm-project/issues/118358>`__",""
-"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","","","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
+"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","|Complete|","23","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
 "`LWG4147 <https://wg21.link/LWG4147>`__","Precondition on ``inplace_vector::emplace``","2024-11 (Wrocław)","","","`#118361 <https://github.com/llvm/llvm-project/issues/118361>`__",""
 "`LWG4148 <https://wg21.link/LWG4148>`__","``unique_ptr::operator*`` should not allow dangling references","2024-11 (Wrocław)","","","`#118362 <https://github.com/llvm/llvm-project/issues/118362>`__",""
 "`LWG4153 <https://wg21.link/LWG4153>`__","Fix extra ""-1"" for ``philox_engine::max()``","2024-11 (Wrocław)","","","`#118363 <https://github.com/llvm/llvm-project/issues/118363>`__",""
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 7e68faa15dabb..93878301b0322 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -43,6 +43,7 @@
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_extent.h>
 #include <__type_traits/remove_reference.h>
+#include <__type_traits/void_t.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -100,6 +101,12 @@ inline const bool __can_dereference = false;
 template <class _Tp>
 inline const bool __can_dereference<_Tp, decltype((void)*std::declval<_Tp>())> = true;
 
+template <class _Tp, class = void>
+inline const bool __is_pointable = false;
+
+template <class _Tp>
+inline const bool __is_pointable<_Tp, __void_t<_Tp*> > = true;
+
 #if defined(_LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI)
 #  define _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI __attribute__((__trivial_abi__))
 #else
@@ -111,6 +118,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
+
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
   using pointer _LIBCPP_NODEBUG = __pointer<_Tp, deleter_type>;
 
   static_assert(!is_rvalue_reference<deleter_type>::value, "the specified deleter type cannot be an rvalue reference");
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
new file mode 100644
index 0000000000000..14829629861c6
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
+
+void reference_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<int&, Deleter>);
+}
+
+void abominable_function_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
+}

>From 79c6048aef67e3bfff149275bd08d8540ec5d3fd Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Mon, 13 Jul 2026 12:17:49 +0800
Subject: [PATCH 2/2] adjust lit test

1. Add [unique.ptr.single.general]/1 marker
2. Merge test cases into one
3. Remove useless lines
---
 .../unique.ptr.class/non_pointable.single.verify.cpp   | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
index 14829629861c6..6f27cdbdce4eb 100644
--- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -10,10 +10,9 @@
 
 // unique_ptr
 
+// [unique.ptr.single.general]/1 (added by LWG4144)
 // A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
 
-// LWG 4144
-
 // XFAIL: FROZEN-CXX03-HEADERS-FIXME
 
 #include <memory>
@@ -24,17 +23,12 @@ struct Deleter {
   void operator()(pointer) const;
 };
 
-typedef void Function();
 typedef void AbominableFunction() const;
 
-void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
-
-void reference_type() {
+void test() {
   // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
   (void)sizeof(std::unique_ptr<int&, Deleter>);
-}
 
-void abominable_function_type() {
   // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
   (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
 }


From libcxx-commits at lists.llvm.org  Sun Jul 12 21:35:04 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Sun, 12 Jul 2026 21:35:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Add deleted tuple
 constructor overloads (PR #205379)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205379@github.com>
Message-ID: <6a546af8.d76d81af.39f9be.625a@mx.google.com>


https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/205379

>From 865c5a96e4f7699d8ecc5a6317aba888d8795269 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 24 Jun 2026 00:51:43 +0800
Subject: [PATCH 01/13] [libc++] P2255R2: Add deleted tuple constructor
 overloads

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/tuple                          | 247 ++++++++++++++++--
 ...5_tuple_ref_binding_diagnostics.verify.cpp |  24 +-
 .../ref_constructs_from_temporary.verify.cpp  |  74 ++++++
 4 files changed, 309 insertions(+), 38 deletions(-)
 create mode 100644 libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp

diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 739f0ecab37e7..e2e7c679be7b7 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -43,7 +43,7 @@
 "`P0627R6 <https://wg21.link/P0627R6>`__","Function to mark unreachable code","2022-02 (Virtual)","|Complete|","15","`#105175 <https://github.com/llvm/llvm-project/issues/105175>`__",""
 "`P1206R7 <https://wg21.link/P1206R7>`__","``ranges::to``: A function to convert any range to a container","2022-02 (Virtual)","|Complete|","17","`#105176 <https://github.com/llvm/llvm-project/issues/105176>`__",""
 "`P1413R3 <https://wg21.link/P1413R3>`__","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","2022-02 (Virtual)","|Complete|","","`#105177 <https://github.com/llvm/llvm-project/issues/105177>`__","``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but clang doesn't issue a diagnostic for deprecated using template declarations."
-"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","`#105180 <https://github.com/llvm/llvm-project/issues/105180>`__","Implemented the type traits only."
+"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","`#105180 <https://github.com/llvm/llvm-project/issues/105180>`__","Implemented the type traits and changes to tuple constructors only."
 "`P2273R3 <https://wg21.link/P2273R3>`__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16","`#105182 <https://github.com/llvm/llvm-project/issues/105182>`__",""
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 8f43006521dda..912b49c2b97de 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -616,21 +616,55 @@ public:
               _Not<_IsThisTuple<_Up...> >, // extension to allow mis-behaved user constructors
               is_constructible<_Tp, _Up>... > {};
 
-  template <class... _Up,
-            __enable_if_t< _And< _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> >::value,
-                           int> = 0>
+  template <class... _Up>
+  struct _EnableUTypesCtorBase
+      : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
+
+  template <bool, class... _Up>
+  struct _EnableUTypesCtorNoDangling : false_type {};
+
+#    if _LIBCPP_STD_VER >= 23
+  template <bool, class... _Up>
+  struct _CtorCreatesDanglingReference : false_type {};
+
+  template <class... _Up>
+  struct _CtorCreatesDanglingReference<true, _Up...>
+      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, _Up&&> >...> {};
+
+  template <class... _Up>
+  struct _EnableUTypesCtorNoDangling<true, _Up...> : _Not<_CtorCreatesDanglingReference<true, _Up...> > {};
+
+  template <class... _Up>
+  struct _EnableDanglingUTypesCtor : _CtorCreatesDanglingReference<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+#    else
+  template <class... _Up>
+  struct _EnableUTypesCtorNoDangling<true, _Up...> : true_type {};
+#    endif // _LIBCPP_STD_VER >= 23
+
+  template <class... _Up>
+  struct _EnableNonDanglingUTypesCtor
+      : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+
+  template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&... __u) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__forward_args{}, std::forward<_Up>(__u)...) {}
 
-  template <class _Alloc,
-            class... _Up,
-            __enable_if_t< _And< _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> >::value,
-                           int> = 0>
+  template <class _Alloc, class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
       : __base_(allocator_arg_t(), __a, __forward_args{}, std::forward<_Up>(__u)...) {}
 
+#    if _LIBCPP_STD_VER >= 23
+  template <class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
+
+  template <class _Alloc, class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
+#    endif // _LIBCPP_STD_VER >= 23
+
   // Copy and move constructors (including the allocator_arg_t variants)
   tuple(const tuple&) = default;
   tuple(tuple&&)      = default;
@@ -672,58 +706,134 @@ public:
                      _Not<is_convertible<_OtherTuple, _Tp> >...,
                      _Not<is_constructible<_Tp, _OtherTuple> >... > > > {};
 
-  template <class... _Up, __enable_if_t< _And< _EnableCtorFromUTypesTuple<const tuple<_Up...>&> >::value, int> = 0>
+  template <bool, class _OtherTuple>
+  struct _EnableCtorFromUTypesTupleNoDangling : false_type {};
+
+#    if _LIBCPP_STD_VER >= 23
+  template <bool, class _OtherTuple, class _DecayedOtherTuple = __remove_cvref_t<_OtherTuple>, class = void>
+  struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
+
+  template <class _OtherTuple, class... _Up>
+  struct _CtorFromUTypesTupleCreatesDanglingReference<
+      true,
+      _OtherTuple,
+      tuple<_Up...>,
+      __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
+      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
+
+  template <class _OtherTuple>
+  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple>
+      : _Not<_CtorFromUTypesTupleCreatesDanglingReference<true, _OtherTuple> > {};
+
+  template <class _OtherTuple>
+  struct _EnableDanglingCtorFromUTypesTuple
+      : _CtorFromUTypesTupleCreatesDanglingReference<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+#    else
+  template <class _OtherTuple>
+  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple> : true_type {};
+#    endif // _LIBCPP_STD_VER >= 23
+
+  template <class _OtherTuple>
+  struct _EnableNonDanglingCtorFromUTypesTuple
+      : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+
+  template <class... _Up,
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
       : __base_(__from_tuple(), __t) {}
 
   template <class... _Up,
             class _Alloc,
-            __enable_if_t< _And< _EnableCtorFromUTypesTuple<const tuple<_Up...>&> >::value, int> = 0>
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class... _Up,
+            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+      tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
+
+  template <class... _Up,
+            class _Alloc,
+            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
+
   // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>& __t)
       : __base_(__from_tuple(), __t) {}
 
-  template <class _Alloc, class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
+
+  template <class _Alloc,
+            class... _Up,
+            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __t) {}
+
+  template <class _Alloc,
+            class... _Up,
+            enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
-  template <class... _Up, __enable_if_t< _And< _EnableCtorFromUTypesTuple<tuple<_Up...>&&> >::value, int> = 0>
+  template <class... _Up,
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__from_tuple(), std::move(__t)) {}
 
   template <class _Alloc,
             class... _Up,
-            __enable_if_t< _And< _EnableCtorFromUTypesTuple<tuple<_Up...>&&> >::value, int> = 0>
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__t)) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
+
+  template <class _Alloc,
+            class... _Up,
+            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
+
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up,
+            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&& __t)
       : __base_(__from_tuple(), std::move(__t)) {}
 
+  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+      tuple(const tuple<_Up...>&&) = delete;
+
   template <class _Alloc,
             class... _Up,
-            enable_if_t< _EnableCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__t)) {}
+
+  template <class _Alloc,
+            class... _Up,
+            enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(const pair<U1, U2>&) constructors (including allocator_arg_t variants)
@@ -741,6 +851,34 @@ public:
   template <class _Pair>
   struct _EnableCtorFromPair : _CtorPredicateFromPair<is_constructible, _Pair> {};
 
+  template <bool, class _Pair>
+  struct _EnableCtorFromPairNoDangling : false_type {};
+
+#    if _LIBCPP_STD_VER >= 23
+  template <bool, class _Pair, class _DecayedPair = __remove_cvref_t<_Pair>, class _Tuple = tuple>
+  struct _CtorFromPairCreatesDanglingReference : false_type {};
+
+  template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
+  struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
+      : _Or<
+            _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
+            _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
+
+  template <class _Pair>
+  struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
+
+  template <class _Pair>
+  struct _EnableDanglingCtorFromPair
+      : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+#    else
+  template <class _Pair>
+  struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
+#    endif // _LIBCPP_STD_VER >= 23
+
+  template <class _Pair>
+  struct _EnableNonDanglingCtorFromPair
+      : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+
   template <class _Pair>
   struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
 
@@ -754,7 +892,7 @@ public:
   template <class _Up1,
             class _Up2,
             template <class...> class _And                                                   = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>& __p) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value)
@@ -764,35 +902,64 @@ public:
             class _Up1,
             class _Up2,
             template <class...> class _And                                                   = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, const pair<_Up1, _Up2>& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __p) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class _Up1,
+            class _Up2,
+            template <class...> class _And                                                    = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+      tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) =
+          delete;
+
+  template <class _Alloc,
+            class _Up1,
+            class _Up2,
+            template <class...> class _And                                                    = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+      tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
+
   // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
 
-  template <class _U1, class _U2, enable_if_t< _EnableCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _U1, class _U2, enable_if_t< _EnableNonDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(pair<_U1, _U2>& __p)
       : __base_(__from_tuple(), __p) {}
 
+  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+      tuple(pair<_U1, _U2>&) = delete;
+
   template <class _Alloc,
             class _U1,
             class _U2,
-            enable_if_t< _EnableCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+            enable_if_t< _EnableNonDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, pair<_U1, _U2>& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __p) {}
-#    endif
+
+  template <class _Alloc,
+            class _U1,
+            class _U2,
+            enable_if_t< _EnableDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+      tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
+#    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(pair<U1, U2>&&) constructors (including allocator_arg_t variants)
 
   template <class _Up1,
             class _Up2,
             template <class...> class _And                                              = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&& __p) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value)
@@ -802,27 +969,59 @@ public:
             class _Up1,
             class _Up2,
             template <class...> class _And                                              = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, pair<_Up1, _Up2>&& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__p)) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class _Up1,
+            class _Up2,
+            template <class...> class _And                                               = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+      tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
+
+  template <class _Alloc,
+            class _Up1,
+            class _Up2,
+            template <class...> class _And                                               = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+      tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
+
   // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
 
-  template <class _U1, class _U2, enable_if_t< _EnableCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1,
+            class _U2,
+            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&& __p)
       : __base_(__from_tuple(), std::move(__p)) {}
 
+  template <class _U1,
+            class _U2,
+            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+      tuple(const pair<_U1, _U2>&&) = delete;
+
   template <class _Alloc,
             class _U1,
             class _U2,
-            enable_if_t< _EnableCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const pair<_U1, _U2>&& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__p)) {}
+
+  template <class _Alloc,
+            class _U1,
+            class _U2,
+            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+      tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // [tuple.assign]
diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index a1a80483c4487..a44a38e3e17ed 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03
+// REQUIRES: std-at-least-c++23
 
 // <tuple>
 
@@ -39,36 +40,33 @@ template <class ...Args>
 void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
 
 void f() {
-  // Test that we emit our diagnostic from the library.
-  // expected-error at tuple:* 8 {{Attempted construction of reference element binds to a temporary whose lifetime has ended}}
-
-  // Good news everybody! Clang now diagnoses this for us!
-  // expected-error at tuple:* 0+ {{reference member '__value_' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+  // Test that the public constructors are deleted when constructing a reference
+  // element would bind to a temporary.
 
   {
-    F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-note 1 {{requested here}}
+    F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-error {{deleted}}
   }
   {
-    std::tuple<int, const std::string&> t(1, "a"); // expected-note 1 {{requested here}}
+    std::tuple<int, const std::string&> t(1, "a"); // expected-error {{deleted}}
   }
   {
-    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-note 1 {{requested here}}
+    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-error {{deleted}}
   }
   {
     ConvertsTo<int&> ct;
-    std::tuple<const long&, int> t(ct, 42); // expected-note {{requested here}}
+    std::tuple<const long&, int> t(ct, 42); // expected-error {{deleted}}
   }
   {
     ConvertsTo<int> ct;
-    std::tuple<int const&, void*> t(ct, nullptr); // expected-note {{requested here}}
+    std::tuple<int const&, void*> t(ct, nullptr); // expected-error {{deleted}}
   }
   {
     ConvertsTo<Derived> ct;
-    std::tuple<Base const&, int> t(ct, 42); // expected-note {{requested here}}
+    std::tuple<Base const&, int> t(ct, 42); // expected-error {{deleted}}
   }
   {
     std::allocator<int> alloc;
-    std::tuple<std::string &&> t2("hello"); // expected-note {{requested here}}
-    std::tuple<std::string &&> t3(std::allocator_arg, alloc, "hello"); // expected-note {{requested here}}
+    std::tuple<std::string&&> t2("hello"); // expected-error {{deleted}}
+    std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
   }
 }
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp
new file mode 100644
index 0000000000000..c7a5f4e247f3b
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <tuple>
+
+// tuple(UTypes&&... u);
+// tuple(tuple<UTypes...>& u);
+// tuple(const tuple<UTypes...>& u);
+// tuple(tuple<UTypes...>&& u);
+// tuple(const tuple<UTypes...>&& u);
+// tuple(pair<U1, U2>& u);
+// tuple(const pair<U1, U2>& u);
+// tuple(pair<U1, U2>&& u);
+// tuple(const pair<U1, U2>&& u);
+//
+// The constructors above are defined as deleted if reference_constructs_from_temporary_v
+// is true for one of the corresponding tuple elements.
+
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+struct X {
+  X(int);
+};
+
+static_assert(std::is_constructible_v<std::tuple<const X&>, X&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, int>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, std::tuple<int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, const std::tuple<int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, std::tuple<int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, const std::tuple<int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, std::pair<int, int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, const std::pair<int, int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, std::pair<int, int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, const std::pair<int, int>&&>);
+
+void test() {
+  std::allocator<int> alloc;
+  std::tuple<int> t(1);
+  const std::tuple<int> ct(1);
+  std::pair<int, int> p(1, 2);
+  const std::pair<int, int> cp(1, 2);
+
+  // expected-error-re@*:* 18 {{call to deleted constructor of 'std::tuple<{{.*}}>'}}
+  std::tuple<const X&> utypes(1);
+  std::tuple<const X&> alloc_utypes(std::allocator_arg, alloc, 1);
+
+  std::tuple<const X&> tuple_lvalue(t);
+  std::tuple<const X&> alloc_tuple_lvalue(std::allocator_arg, alloc, t);
+  std::tuple<const X&> const_tuple_lvalue(ct);
+  std::tuple<const X&> alloc_const_tuple_lvalue(std::allocator_arg, alloc, ct);
+  std::tuple<const X&> tuple_rvalue(std::tuple<int>{1});
+  std::tuple<const X&> alloc_tuple_rvalue(std::allocator_arg, alloc, std::tuple<int>{1});
+  std::tuple<const X&> const_tuple_rvalue(std::move(ct));
+  std::tuple<const X&> alloc_const_tuple_rvalue(std::allocator_arg, alloc, std::move(ct));
+
+  std::tuple<const X&, int> pair_lvalue(p);
+  std::tuple<const X&, int> alloc_pair_lvalue(std::allocator_arg, alloc, p);
+  std::tuple<const X&, int> const_pair_lvalue(cp);
+  std::tuple<const X&, int> alloc_const_pair_lvalue(std::allocator_arg, alloc, cp);
+  std::tuple<const X&, int> pair_rvalue(std::pair<int, int>{1, 2});
+  std::tuple<const X&, int> alloc_pair_rvalue(std::allocator_arg, alloc, std::pair<int, int>{1, 2});
+  std::tuple<const X&, int> const_pair_rvalue(std::move(cp));
+  std::tuple<const X&, int> alloc_const_pair_rvalue(std::allocator_arg, alloc, std::move(cp));
+}

>From 9d2d66b76aba200e90d0c67b487c13abb81f585e Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 24 Jun 2026 00:56:58 +0800
Subject: [PATCH 02/13] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple                          | 62 ++++++++-----------
 ...5_tuple_ref_binding_diagnostics.verify.cpp |  2 +-
 2 files changed, 26 insertions(+), 38 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 912b49c2b97de..edeb97aa9de80 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -617,8 +617,7 @@ public:
               is_constructible<_Tp, _Up>... > {};
 
   template <class... _Up>
-  struct _EnableUTypesCtorBase
-      : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
+  struct _EnableUTypesCtorBase : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
 
   template <bool, class... _Up>
   struct _EnableUTypesCtorNoDangling : false_type {};
@@ -642,8 +641,7 @@ public:
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class... _Up>
-  struct _EnableNonDanglingUTypesCtor
-      : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+  struct _EnableNonDanglingUTypesCtor : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
 
   template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
@@ -714,11 +712,10 @@ public:
   struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
 
   template <class _OtherTuple, class... _Up>
-  struct _CtorFromUTypesTupleCreatesDanglingReference<
-      true,
-      _OtherTuple,
-      tuple<_Up...>,
-      __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
+  struct _CtorFromUTypesTupleCreatesDanglingReference< true,
+                                                       _OtherTuple,
+                                                       tuple<_Up...>,
+                                                       __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
       : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
 
   template <class _OtherTuple>
@@ -737,8 +734,7 @@ public:
   struct _EnableNonDanglingCtorFromUTypesTuple
       : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
 
-  template <class... _Up,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
       : __base_(__from_tuple(), __t) {}
@@ -751,8 +747,7 @@ public:
       : __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up,
-            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
 
@@ -786,8 +781,7 @@ public:
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
-  template <class... _Up,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__from_tuple(), std::move(__t)) {}
@@ -812,8 +806,7 @@ public:
 
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up,
-            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&& __t)
       : __base_(__from_tuple(), std::move(__t)) {}
@@ -860,24 +853,22 @@ public:
 
   template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
   struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
-      : _Or<
-            _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
-            _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
+      : _Or< _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
+             _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
 
   template <class _Pair>
   struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
 
   template <class _Pair>
-  struct _EnableDanglingCtorFromPair
-      : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+  struct _EnableDanglingCtorFromPair : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {
+  };
 #    else
   template <class _Pair>
   struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _Pair>
-  struct _EnableNonDanglingCtorFromPair
-      : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+  struct _EnableNonDanglingCtorFromPair : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
 
   template <class _Pair>
   struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
@@ -891,7 +882,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                   = _And,
+            template <class...> class _And                                                              = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -901,7 +892,7 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                   = _And,
+            template <class...> class _And                                                              = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -911,17 +902,16 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                    = _And,
+            template <class...> class _And                                                            = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
-      tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) =
-          delete;
+      tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
 
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                    = _And,
+            template <class...> class _And                                                            = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -958,7 +948,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                              = _And,
+            template <class...> class _And                                                         = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -968,7 +958,7 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                              = _And,
+            template <class...> class _And                                                         = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -978,7 +968,7 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                               = _And,
+            template <class...> class _And                                                       = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -987,7 +977,7 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                               = _And,
+            template <class...> class _And                                                       = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -1002,9 +992,7 @@ public:
       tuple(const pair<_U1, _U2>&& __p)
       : __base_(__from_tuple(), std::move(__p)) {}
 
-  template <class _U1,
-            class _U2,
-            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&&) = delete;
 
diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index a44a38e3e17ed..15d032fbb3a7a 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -66,7 +66,7 @@ void f() {
   }
   {
     std::allocator<int> alloc;
-    std::tuple<std::string&&> t2("hello"); // expected-error {{deleted}}
+    std::tuple<std::string&&> t2("hello");                            // expected-error {{deleted}}
     std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
   }
 }

>From 8520af98734cd4100ebad837b70a7f69985fb937 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 09:41:49 -0700
Subject: [PATCH 03/13] [libc++] Refine the implementation use template
 variable and make reference_constructs_from_temporary SFINAE friendly

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../reference_constructs_from_temporary.h     |  15 +-
 libcxx/include/tuple                          | 240 ++++++++----------
 2 files changed, 126 insertions(+), 129 deletions(-)

diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index a8325620414ea..c18723fb77d81 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -11,6 +11,7 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_reference.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -30,8 +31,20 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_tempo
 
 #endif
 
+// A non-reference type can never bind to a temporary, so the result is always `false` for such a
+// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
+// eagerly instantiates the construction of `_Up` (including the element's constructor exception
+// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
+//
+// https://godbolt.org/z/WMTK5cMa4
+//
+// TODO: remove this guard once the Clang builtin short-circuits on a non-reference first operand.
+template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
+inline const bool __reference_constructs_from_temporary_v = false;
+
 template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
+inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
+    __reference_constructs_from_temporary(_Tp, _Up);
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index edeb97aa9de80..609cdf1f93dc4 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -623,42 +623,60 @@ public:
   struct _EnableUTypesCtorNoDangling : false_type {};
 
 #    if _LIBCPP_STD_VER >= 23
-  template <bool, class... _Up>
-  struct _CtorCreatesDanglingReference : false_type {};
-
   template <class... _Up>
-  struct _CtorCreatesDanglingReference<true, _Up...>
-      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, _Up&&> >...> {};
+  static constexpr bool __has_danglings() {
+    // The fold expands `_Tp` and `_Up` in lockstep, so it is only well-formed when the packs have
+    // equal length. The discarded branch is not instantiated when the lengths differ.
+    if constexpr (sizeof...(_Up) == sizeof...(_Tp))
+      return (__reference_constructs_from_temporary_v<_Tp, _Up&&> || ...);
+    else
+      return false;
+  }
 
-  template <class... _Up>
-  struct _EnableUTypesCtorNoDangling<true, _Up...> : _Not<_CtorCreatesDanglingReference<true, _Up...> > {};
+  template <class _Tuple,
+            class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+  static inline constexpr bool __has_danglings_from_tuple_like = false;
+
+  template <class _Tuple, size_t... _Idx>
+  static inline constexpr bool __has_danglings_from_tuple_like<_Tuple, index_sequence<_Idx...>,
+    enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
 
   template <class... _Up>
-  struct _EnableDanglingUTypesCtor : _CtorCreatesDanglingReference<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
-#    else
+  static inline constexpr bool __has_danglings_from_utypes =
+      _EnableUTypesCtorBase<_Up...>::value && __has_danglings<_Up...>();
+
   template <class... _Up>
-  struct _EnableUTypesCtorNoDangling<true, _Up...> : true_type {};
+  static inline constexpr bool __has_no_danglings_from_utypes =
+      _EnableUTypesCtorBase<_Up...>::value && !__has_danglings<_Up...>();
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class... _Up>
-  struct _EnableNonDanglingUTypesCtor : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+  static inline constexpr bool __enable_utypes_constructor_no_dangling() {
+#    if _LIBCPP_STD_VER >= 23
+    return __has_no_danglings_from_utypes<_Up...>;
+#    else
+    return _EnableUTypesCtorBase<_Up...>::value;
+#    endif // _LIBCPP_STD_VER >= 23
+  }
 
-  template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
+  template <class... _Up, __enable_if_t< __enable_utypes_constructor_no_dangling<_Up...>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&... __u) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__forward_args{}, std::forward<_Up>(__u)...) {}
 
-  template <class _Alloc, class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
+  template <class _Alloc, class... _Up, __enable_if_t< __enable_utypes_constructor_no_dangling<_Up...>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
       : __base_(allocator_arg_t(), __a, __forward_args{}, std::forward<_Up>(__u)...) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  template <class... _Up>
+    requires __has_danglings_from_utypes<_Up...>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
-  template <class _Alloc, class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes<_Up...>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
@@ -708,123 +726,116 @@ public:
   struct _EnableCtorFromUTypesTupleNoDangling : false_type {};
 
 #    if _LIBCPP_STD_VER >= 23
-  template <bool, class _OtherTuple, class _DecayedOtherTuple = __remove_cvref_t<_OtherTuple>, class = void>
-  struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
-
-  template <class _OtherTuple, class... _Up>
-  struct _CtorFromUTypesTupleCreatesDanglingReference< true,
-                                                       _OtherTuple,
-                                                       tuple<_Up...>,
-                                                       __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
-      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
-
   template <class _OtherTuple>
-  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple>
-      : _Not<_CtorFromUTypesTupleCreatesDanglingReference<true, _OtherTuple> > {};
+  static inline constexpr bool __has_danglings_from_utypes_tuple =
+      _EnableCtorFromUTypesTuple<_OtherTuple>::value && __has_danglings_from_tuple_like<_OtherTuple>;
 
   template <class _OtherTuple>
-  struct _EnableDanglingCtorFromUTypesTuple
-      : _CtorFromUTypesTupleCreatesDanglingReference<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
-#    else
-  template <class _OtherTuple>
-  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple> : true_type {};
+  static inline constexpr bool __has_no_danglings_from_utypes_tuple =
+      _EnableCtorFromUTypesTuple<_OtherTuple>::value && !__has_danglings_from_tuple_like<_OtherTuple>;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _OtherTuple>
-  struct _EnableNonDanglingCtorFromUTypesTuple
-      : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+  static inline constexpr bool __enable_utypes_tuple_constructor_no_dangling() {
+#    if _LIBCPP_STD_VER >= 23
+    return __has_no_danglings_from_utypes_tuple<_OtherTuple>;
+#    else
+    return _EnableCtorFromUTypesTuple<_OtherTuple>::value;
+#    endif // _LIBCPP_STD_VER >= 23
+  }
 
-  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+  template <class... _Up,
+            __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<const tuple<_Up...>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
       : __base_(__from_tuple(), __t) {}
 
   template <class... _Up,
             class _Alloc,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+            __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<const tuple<_Up...>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
 
-  template <class... _Up,
-            class _Alloc,
-            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  template <class... _Up, class _Alloc>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
 
   // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>& __t)
       : __base_(__from_tuple(), __t) {}
 
-  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __t) {}
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
-  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+  template <class... _Up, __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<tuple<_Up...>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__from_tuple(), std::move(__t)) {}
 
   template <class _Alloc,
             class... _Up,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+            __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<tuple<_Up...>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__t)) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
-  template <class _Alloc,
-            class... _Up,
-            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
 
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&& __t)
       : __base_(__from_tuple(), std::move(__t)) {}
 
-  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&&) = delete;
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__t)) {}
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
@@ -844,31 +855,24 @@ public:
   template <class _Pair>
   struct _EnableCtorFromPair : _CtorPredicateFromPair<is_constructible, _Pair> {};
 
-  template <bool, class _Pair>
-  struct _EnableCtorFromPairNoDangling : false_type {};
-
 #    if _LIBCPP_STD_VER >= 23
-  template <bool, class _Pair, class _DecayedPair = __remove_cvref_t<_Pair>, class _Tuple = tuple>
-  struct _CtorFromPairCreatesDanglingReference : false_type {};
-
-  template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
-  struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
-      : _Or< _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
-             _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
-
   template <class _Pair>
-  struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
+  static inline constexpr bool __has_danglings_from_utypes_pair =
+      _EnableCtorFromPair<_Pair>::value && __has_danglings_from_tuple_like<_Pair>;
 
   template <class _Pair>
-  struct _EnableDanglingCtorFromPair : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {
-  };
-#    else
-  template <class _Pair>
-  struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
+  static inline constexpr bool __has_no_danglings_from_utypes_pair =
+      _EnableCtorFromPair<_Pair>::value && !__has_danglings_from_tuple_like<_Pair>;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _Pair>
-  struct _EnableNonDanglingCtorFromPair : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+  static inline constexpr bool __enable_utypes_pair_constructor_no_dangling() {
+#    if _LIBCPP_STD_VER >= 23
+    return _EnableCtorFromPair<_Pair>::value && __has_no_danglings_from_utypes_pair<_Pair>;
+#    else
+    return _EnableCtorFromPair<_Pair>::value;
+#    endif // _LIBCPP_STD_VER >= 23
+  }
 
   template <class _Pair>
   struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
@@ -882,8 +886,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                              = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t<__enable_utypes_pair_constructor_no_dangling<const pair<_Up1, _Up2>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>& __p) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value)
@@ -892,54 +895,46 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                              = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t<__enable_utypes_pair_constructor_no_dangling<const pair<_Up1, _Up2>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, const pair<_Up1, _Up2>& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __p) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class _Up1,
-            class _Up2,
-            template <class...> class _And                                                            = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  template <class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
 
-  template <class _Alloc,
-            class _Up1,
-            class _Up2,
-            template <class...> class _And                                                            = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  template <class _Alloc, class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
 
   // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
 
-  template <class _U1, class _U2, enable_if_t< _EnableNonDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(pair<_U1, _U2>& __p)
       : __base_(__from_tuple(), __p) {}
 
-  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(pair<_U1, _U2>&) = delete;
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableNonDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, pair<_U1, _U2>& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __p) {}
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
@@ -948,8 +943,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                         = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< __enable_utypes_pair_constructor_no_dangling<pair<_Up1, _Up2>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&& __p) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value)
@@ -958,56 +952,46 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                         = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< __enable_utypes_pair_constructor_no_dangling<pair<_Up1, _Up2>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, pair<_Up1, _Up2>&& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__p)) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class _Up1,
-            class _Up2,
-            template <class...> class _And                                                       = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  template <class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
 
-  template <class _Alloc,
-            class _Up1,
-            class _Up2,
-            template <class...> class _And                                                       = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  template <class _Alloc, class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
 
   // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
 
-  template <class _U1,
-            class _U2,
-            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&& __p)
       : __base_(__from_tuple(), std::move(__p)) {}
 
-  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&&) = delete;
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const pair<_U1, _U2>&& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__p)) {}
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23

>From f8ba2c9c885db176fef5158cd1602b9beaa293ac Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 09:46:03 -0700
Subject: [PATCH 04/13] [libc++] Enable test for all language modes

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index 15d032fbb3a7a..0e9992194f03c 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03
-// REQUIRES: std-at-least-c++23
 
 // <tuple>
 

>From 3d87818fe02261235ef78f2f638b5a6dba300edb Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:06:24 -0700
Subject: [PATCH 05/13] Remove unnecessary for deleted ctors

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 46 +++++++++++++++++---------------------------
 1 file changed, 18 insertions(+), 28 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 609cdf1f93dc4..687192b6ffead 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -672,13 +672,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
     requires __has_danglings_from_utypes<_Up...>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes<_Up...>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
-      tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // Copy and move constructors (including the allocator_arg_t variants)
@@ -760,12 +759,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+  explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
 
   template <class... _Up, class _Alloc>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+  explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
 
   // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
@@ -777,7 +776,7 @@ public:
 
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
+  explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
@@ -787,8 +786,7 @@ public:
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
-      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
+  explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
@@ -807,13 +805,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
-      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
 
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
@@ -825,8 +822,7 @@ public:
 
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
-      tuple(const tuple<_Up...>&&) = delete;
+  explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value) tuple(const tuple<_Up...>&&) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
@@ -836,7 +832,7 @@ public:
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+  explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
@@ -904,14 +900,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
 
   template <class _Alloc, class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
 
   // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
@@ -924,8 +918,7 @@ public:
 
   template <class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<pair<_U1, _U2>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
-      tuple(pair<_U1, _U2>&) = delete;
+  explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value) tuple(pair<_U1, _U2>&) = delete;
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_no_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
@@ -935,7 +928,7 @@ public:
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+  explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
@@ -961,14 +954,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
 
   template <class _Alloc, class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
 
   // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
@@ -981,8 +972,7 @@ public:
 
   template <class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
-      tuple(const pair<_U1, _U2>&&) = delete;
+  explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value) tuple(const pair<_U1, _U2>&&) = delete;
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
@@ -992,7 +982,7 @@ public:
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+  explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 

>From 8ff04dd78e88eafd4876196dd6c88a02b061da0f Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:13:12 -0700
Subject: [PATCH 06/13] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 687192b6ffead..7f444a5333f75 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -633,13 +633,14 @@ public:
       return false;
   }
 
-  template <class _Tuple,
-            class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+  template <class _Tuple, class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
   static inline constexpr bool __has_danglings_from_tuple_like = false;
 
   template <class _Tuple, size_t... _Idx>
-  static inline constexpr bool __has_danglings_from_tuple_like<_Tuple, index_sequence<_Idx...>,
-    enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
+  static inline constexpr bool __has_danglings_from_tuple_like<
+      _Tuple,
+      index_sequence<_Idx...>,
+      enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
 
   template <class... _Up>
   static inline constexpr bool __has_danglings_from_utypes =

>From bb0fa0f1cbee157e39dd52b254da55b2bb59fa5d Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:20:32 -0700
Subject: [PATCH 07/13] Remove unnecessary comments

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 7f444a5333f75..22641af8f494c 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -625,8 +625,6 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
   static constexpr bool __has_danglings() {
-    // The fold expands `_Tp` and `_Up` in lockstep, so it is only well-formed when the packs have
-    // equal length. The discarded branch is not instantiated when the lengths differ.
     if constexpr (sizeof...(_Up) == sizeof...(_Tp))
       return (__reference_constructs_from_temporary_v<_Tp, _Up&&> || ...);
     else

>From 70175b01fa43cb47b44ef3974124e5d3e8366476 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 00:06:59 -0700
Subject: [PATCH 08/13] [libc++] Fix hard error in deleted dangling ctor
 constraints

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 22641af8f494c..0a95aa635663d 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -640,13 +640,25 @@ public:
       index_sequence<_Idx...>,
       enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
 
+  template <bool __dangles, class _CtorEnabled>
+  static inline constexpr bool __check_danglings_from_constructor = false;
+
+  template <class _CtorEnabled>
+  static inline constexpr bool __check_danglings_from_constructor<true, _CtorEnabled> = _CtorEnabled::value;
+
+  template <bool __dangles, class _CtorEnabled>
+  static inline constexpr bool __check_no_danglings_from_constructor = _CtorEnabled::value;
+
+  template <class _CtorEnabled>
+  static inline constexpr bool __check_no_danglings_from_constructor<true, _CtorEnabled> = false;
+
   template <class... _Up>
   static inline constexpr bool __has_danglings_from_utypes =
-      _EnableUTypesCtorBase<_Up...>::value && __has_danglings<_Up...>();
+      __check_danglings_from_constructor<__has_danglings<_Up...>(), _EnableUTypesCtorBase<_Up...> >;
 
   template <class... _Up>
   static inline constexpr bool __has_no_danglings_from_utypes =
-      _EnableUTypesCtorBase<_Up...>::value && !__has_danglings<_Up...>();
+      __check_no_danglings_from_constructor<__has_danglings<_Up...>(), _EnableUTypesCtorBase<_Up...> >;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class... _Up>
@@ -726,11 +738,13 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _OtherTuple>
   static inline constexpr bool __has_danglings_from_utypes_tuple =
-      _EnableCtorFromUTypesTuple<_OtherTuple>::value && __has_danglings_from_tuple_like<_OtherTuple>;
+      __check_danglings_from_constructor<__has_danglings_from_tuple_like<_OtherTuple>,
+                                         _EnableCtorFromUTypesTuple<_OtherTuple> >;
 
   template <class _OtherTuple>
   static inline constexpr bool __has_no_danglings_from_utypes_tuple =
-      _EnableCtorFromUTypesTuple<_OtherTuple>::value && !__has_danglings_from_tuple_like<_OtherTuple>;
+      __check_no_danglings_from_constructor<__has_danglings_from_tuple_like<_OtherTuple>,
+                                            _EnableCtorFromUTypesTuple<_OtherTuple> >;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _OtherTuple>
@@ -853,11 +867,11 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Pair>
   static inline constexpr bool __has_danglings_from_utypes_pair =
-      _EnableCtorFromPair<_Pair>::value && __has_danglings_from_tuple_like<_Pair>;
+      __check_danglings_from_constructor<__has_danglings_from_tuple_like<_Pair>, _EnableCtorFromPair<_Pair> >;
 
   template <class _Pair>
   static inline constexpr bool __has_no_danglings_from_utypes_pair =
-      _EnableCtorFromPair<_Pair>::value && !__has_danglings_from_tuple_like<_Pair>;
+      __check_no_danglings_from_constructor<__has_danglings_from_tuple_like<_Pair>, _EnableCtorFromPair<_Pair> >;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _Pair>

>From d59c4503c1096aac398768060bb6fda5809325cf Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 00:57:48 -0700
Subject: [PATCH 09/13] Fix modules build and don't depends tuple_size_v

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 0a95aa635663d..a2a72cc352f86 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -631,14 +631,16 @@ public:
       return false;
   }
 
-  template <class _Tuple, class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+  template <class _Source, class _DecayedSource = __remove_cvref_t<_Source> >
   static inline constexpr bool __has_danglings_from_tuple_like = false;
 
-  template <class _Tuple, size_t... _Idx>
-  static inline constexpr bool __has_danglings_from_tuple_like<
-      _Tuple,
-      index_sequence<_Idx...>,
-      enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
+  template <class _Source, class... _Up>
+  static inline constexpr bool __has_danglings_from_tuple_like<_Source, tuple<_Up...> > =
+      __has_danglings<__copy_cvref_t<_Source, _Up>...>();
+
+  template <class _Source, class _U1, class _U2>
+  static inline constexpr bool __has_danglings_from_tuple_like<_Source, pair<_U1, _U2> > =
+      __has_danglings<__copy_cvref_t<_Source, _U1>, __copy_cvref_t<_Source, _U2> >();
 
   template <bool __dangles, class _CtorEnabled>
   static inline constexpr bool __check_danglings_from_constructor = false;

>From 958f25ad57ee72b3ef853b69856e04be8cba2477 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 02:36:45 -0700
Subject: [PATCH 10/13] Fix test

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 ...5_tuple_ref_binding_diagnostics.verify.cpp | 34 ++++++++++---------
 1 file changed, 18 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index 0e9992194f03c..3c2cee9f16153 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -39,33 +39,35 @@ template <class ...Args>
 void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
 
 void f() {
-  // Test that the public constructors are deleted when constructing a reference
-  // element would bind to a temporary.
+  // Constructing a reference element that would bind to a temporary is rejected. Since C++23
+  // ([tuple.cnstr]) the offending constructors are deleted, so the error is reported at the call;
+  // before C++23 the bind is caught by a static_assert in the library (with Clang additionally
+  // diagnosing the dangling reference member).
+#if TEST_STD_VER >= 23
+  // expected-error@*:* 8 {{deleted}}
+#else
+  // expected-error at tuple:* 8 {{Attempted construction of reference element binds to a temporary whose lifetime has ended}}
+  // expected-error at tuple:* 0+ {{reference member '__value_' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+#endif
 
-  {
-    F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-error {{deleted}}
-  }
-  {
-    std::tuple<int, const std::string&> t(1, "a"); // expected-error {{deleted}}
-  }
-  {
-    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-error {{deleted}}
-  }
+  { F<int, const std::string&>(std::make_tuple(1, "abc")); }
+  { std::tuple<int, const std::string&> t(1, "a"); }
+  { F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); }
   {
     ConvertsTo<int&> ct;
-    std::tuple<const long&, int> t(ct, 42); // expected-error {{deleted}}
+    std::tuple<const long&, int> t(ct, 42);
   }
   {
     ConvertsTo<int> ct;
-    std::tuple<int const&, void*> t(ct, nullptr); // expected-error {{deleted}}
+    std::tuple<int const&, void*> t(ct, nullptr);
   }
   {
     ConvertsTo<Derived> ct;
-    std::tuple<Base const&, int> t(ct, 42); // expected-error {{deleted}}
+    std::tuple<Base const&, int> t(ct, 42);
   }
   {
     std::allocator<int> alloc;
-    std::tuple<std::string&&> t2("hello");                            // expected-error {{deleted}}
-    std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
+    std::tuple<std::string&&> t2("hello");
+    std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello");
   }
 }

>From 2979a29eea2fdbe5591a618ace2c28b81b97cc75 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 03:38:32 -0700
Subject: [PATCH 11/13] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../PR20855_tuple_ref_binding_diagnostics.verify.cpp | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index 3c2cee9f16153..26fc99ae7bbac 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -50,9 +50,15 @@ void f() {
   // expected-error at tuple:* 0+ {{reference member '__value_' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
 #endif
 
-  { F<int, const std::string&>(std::make_tuple(1, "abc")); }
-  { std::tuple<int, const std::string&> t(1, "a"); }
-  { F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); }
+  {
+    F<int, const std::string&>(std::make_tuple(1, "abc"));
+  }
+  {
+    std::tuple<int, const std::string&> t(1, "a");
+  }
+  {
+    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc"));
+  }
   {
     ConvertsTo<int&> ct;
     std::tuple<const long&, int> t(ct, 42);

>From d3a513dc07967195de656085104a942d5e84db31 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 2 Jul 2026 04:22:09 -0700
Subject: [PATCH 12/13] Avoid workaround for
 reference_constructs_from_temporary builtin, it's fixed in clang-23; we will
 skip the related test for older version clang

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../reference_constructs_from_temporary.h         | 15 +--------------
 .../PR23256_constrain_UTypes_ctor.pass.cpp        |  5 +++++
 .../tuple.cnstr/convert_const_move.pass.cpp       |  7 ++++++-
 .../tuple/tuple.tuple/tuple.cnstr/move.pass.cpp   |  4 ++++
 4 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index c18723fb77d81..a8325620414ea 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -11,7 +11,6 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
-#include <__type_traits/is_reference.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -31,20 +30,8 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_tempo
 
 #endif
 
-// A non-reference type can never bind to a temporary, so the result is always `false` for such a
-// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
-// eagerly instantiates the construction of `_Up` (including the element's constructor exception
-// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
-//
-// https://godbolt.org/z/WMTK5cMa4
-//
-// TODO: remove this guard once the Clang builtin short-circuits on a non-reference first operand.
-template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
-inline const bool __reference_constructs_from_temporary_v = false;
-
 template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
-    __reference_constructs_from_temporary(_Tp, _Up);
+inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
index ebc31ce01cfc7..893987ce5f587 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
@@ -64,6 +64,9 @@ struct ExplicitUnconstrainedCtor {
 int main(int, char**) {
     typedef UnconstrainedCtor A;
     typedef ExplicitUnconstrainedCtor ExplicitA;
+
+// Remove this guard when compiler versions older than clang 23 are no longer supported.
+#if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
     {
         static_assert(std::is_copy_constructible<std::tuple<A>>::value, "");
         static_assert(std::is_move_constructible<std::tuple<A>>::value, "");
@@ -92,6 +95,8 @@ int main(int, char**) {
             std::tuple<ExplicitA> &&
         >::value, "");
     }
+#endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+
     {
         std::tuple<A&&> t(std::forward_as_tuple(A{}));
         ((void)t);
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
index 8f778f3fcf3a4..a8ed156a2fc33 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
@@ -113,6 +113,9 @@ constexpr bool test() {
 
   // These two test points cause gcc to ICE
 #if !defined(TEST_COMPILER_GCC)
+
+// Remove this guard when compiler versions older than clang 23 are no longer supported.
+#if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
   // sizeof...(Types) == 1 && is_convertible_v<decltype(u), T>
   {
     const std::tuple<CvtFromConstTupleRefRef> t1{};
@@ -126,7 +129,9 @@ constexpr bool test() {
     std::tuple<ConvertibleFrom<ExplicitCtrFromConstTupleRefRef>> t2{std::move(t1)};
     assert(!constMoveCtrCalled(std::get<0>(t2).v));
   }
-#endif
+#endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+
+#endif // !defined(TEST_COMPILER_GCC)
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
index d6c192d2e0543..b3982795bf6c8 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
@@ -111,6 +111,8 @@ int main(int, char**)
         assert(std::get<1>(t) == 1);
         assert(std::get<2>(t) == 2);
     }
+// Remove this guard when compiler versions older than clang 23 are no longer supported.
+#if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
     // A bug in tuple caused __tuple_leaf to use its explicit converting constructor
     //  as its move constructor. This tests that ConstructsWithTupleLeaf is not called
     // (w/ __tuple_leaf)
@@ -119,6 +121,8 @@ int main(int, char**)
         d_t d((ConstructsWithTupleLeaf()));
         d_t d2(static_cast<d_t &&>(d));
     }
+#endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+
     {
         test_sfinae<move_only_ebo>();
         test_sfinae<move_only_large>();

>From cc1c1669478fbcf815cfa48b9f94ccef44f51743 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 2 Jul 2026 04:26:44 -0700
Subject: [PATCH 13/13] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../PR23256_constrain_UTypes_ctor.pass.cpp    | 63 +++++++++----------
 .../tuple.cnstr/convert_const_move.pass.cpp   |  4 +-
 .../tuple.tuple/tuple.cnstr/move.pass.cpp     | 24 +++----
 3 files changed, 43 insertions(+), 48 deletions(-)

diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
index 893987ce5f587..a7f15e1612157 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
@@ -67,42 +67,37 @@ int main(int, char**) {
 
 // Remove this guard when compiler versions older than clang 23 are no longer supported.
 #if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
-    {
-        static_assert(std::is_copy_constructible<std::tuple<A>>::value, "");
-        static_assert(std::is_move_constructible<std::tuple<A>>::value, "");
-        static_assert(std::is_copy_constructible<std::tuple<ExplicitA>>::value, "");
-        static_assert(std::is_move_constructible<std::tuple<ExplicitA>>::value, "");
-    }
-    {
-        static_assert(std::is_constructible<
-            std::tuple<A>,
-            std::allocator_arg_t, std::allocator<int>,
-            std::tuple<A> const&
-        >::value, "");
-        static_assert(std::is_constructible<
-            std::tuple<A>,
-            std::allocator_arg_t, std::allocator<int>,
-            std::tuple<A> &&
-        >::value, "");
-        static_assert(std::is_constructible<
-            std::tuple<ExplicitA>,
-            std::allocator_arg_t, std::allocator<int>,
-            std::tuple<ExplicitA> const&
-        >::value, "");
-        static_assert(std::is_constructible<
-            std::tuple<ExplicitA>,
-            std::allocator_arg_t, std::allocator<int>,
-            std::tuple<ExplicitA> &&
-        >::value, "");
-    }
+  {
+    static_assert(std::is_copy_constructible<std::tuple<A>>::value, "");
+    static_assert(std::is_move_constructible<std::tuple<A>>::value, "");
+    static_assert(std::is_copy_constructible<std::tuple<ExplicitA>>::value, "");
+    static_assert(std::is_move_constructible<std::tuple<ExplicitA>>::value, "");
+  }
+  {
+    static_assert(
+        std::is_constructible< std::tuple<A>, std::allocator_arg_t, std::allocator<int>, std::tuple<A> const& >::value,
+        "");
+    static_assert(
+        std::is_constructible< std::tuple<A>, std::allocator_arg_t, std::allocator<int>, std::tuple<A>&& >::value, "");
+    static_assert(std::is_constructible< std::tuple<ExplicitA>,
+                                         std::allocator_arg_t,
+                                         std::allocator<int>,
+                                         std::tuple<ExplicitA> const& >::value,
+                  "");
+    static_assert(std::is_constructible< std::tuple<ExplicitA>,
+                                         std::allocator_arg_t,
+                                         std::allocator<int>,
+                                         std::tuple<ExplicitA>&& >::value,
+                  "");
+  }
 #endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
 
-    {
-        std::tuple<A&&> t(std::forward_as_tuple(A{}));
-        ((void)t);
-        std::tuple<ExplicitA&&> t2(std::forward_as_tuple(ExplicitA{}));
-        ((void)t2);
-    }
+  {
+    std::tuple<A&&> t(std::forward_as_tuple(A{}));
+    ((void)t);
+    std::tuple<ExplicitA&&> t2(std::forward_as_tuple(ExplicitA{}));
+    ((void)t2);
+  }
 
   return 0;
 }
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
index a8ed156a2fc33..383627f965137 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
@@ -115,7 +115,7 @@ constexpr bool test() {
 #if !defined(TEST_COMPILER_GCC)
 
 // Remove this guard when compiler versions older than clang 23 are no longer supported.
-#if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+#  if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
   // sizeof...(Types) == 1 && is_convertible_v<decltype(u), T>
   {
     const std::tuple<CvtFromConstTupleRefRef> t1{};
@@ -129,7 +129,7 @@ constexpr bool test() {
     std::tuple<ConvertibleFrom<ExplicitCtrFromConstTupleRefRef>> t2{std::move(t1)};
     assert(!constMoveCtrCalled(std::get<0>(t2).v));
   }
-#endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
+#  endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
 
 #endif // !defined(TEST_COMPILER_GCC)
 
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
index b3982795bf6c8..99fe11a085c99 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/move.pass.cpp
@@ -113,20 +113,20 @@ int main(int, char**)
     }
 // Remove this guard when compiler versions older than clang 23 are no longer supported.
 #if defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
-    // A bug in tuple caused __tuple_leaf to use its explicit converting constructor
-    //  as its move constructor. This tests that ConstructsWithTupleLeaf is not called
-    // (w/ __tuple_leaf)
-    {
-        typedef std::tuple<ConstructsWithTupleLeaf> d_t;
-        d_t d((ConstructsWithTupleLeaf()));
-        d_t d2(static_cast<d_t &&>(d));
-    }
+  // A bug in tuple caused __tuple_leaf to use its explicit converting constructor
+  //  as its move constructor. This tests that ConstructsWithTupleLeaf is not called
+  // (w/ __tuple_leaf)
+  {
+    typedef std::tuple<ConstructsWithTupleLeaf> d_t;
+    d_t d((ConstructsWithTupleLeaf()));
+    d_t d2(static_cast<d_t&&>(d));
+  }
 #endif // defined(TEST_CLANG_VER) && TEST_CLANG_VER >= 2300
 
-    {
-        test_sfinae<move_only_ebo>();
-        test_sfinae<move_only_large>();
-    }
+  {
+    test_sfinae<move_only_ebo>();
+    test_sfinae<move_only_large>();
+  }
 
   return 0;
 }


From libcxx-commits at lists.llvm.org  Sun Jul 12 22:01:16 2026
From: libcxx-commits at lists.llvm.org (Tony Varghese via libcxx-commits)
Date: Sun, 12 Jul 2026 22:01:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a54711c.bb839515.27d21e.cbdb@mx.google.com>


https://github.com/tonykuttai commented:

General comment: isn't better to insert target specific check inside the fopen function definition rather than function declarations?

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Sun Jul 12 22:42:12 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 12 Jul 2026 22:42:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a547ab4.6365b89b.8575e.98d6@mx.google.com>


================
@@ -111,6 +118,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
+
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
----------------
frederick-vs-ja wrote:

I think it would be better to place this line immediately above the existing `static_assert` below.

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 23:04:30 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 23:04:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a547fee.d76d81af.39f9be.faa0@mx.google.com>


================
@@ -111,6 +118,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
+
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
----------------
emmett2020 wrote:

Thx. I think moving the assertion below the existing `static_assert` would generate additional error message. Since that places it after the pointer alias, `__pointer<_Tp, deleter_type>` attempts to form `_Tp*` first, causing `__pointer` to produce additional errors. Such as:

```bash
  allocator_traits.h:46:1: error:
  '__pointer' declared as a pointer to a reference of type 'int &'
  
```
Therefor, I think the pointability assertion should remain before the pointer alias.

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 23:05:20 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 23:05:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a548020.d76d81af.39f9be.2489@mx.google.com>


https://github.com/trcrsired updated https://github.com/llvm/llvm-project/pull/208900

>From 160bbbc6c70620f231037569b40af54e38dd9f36 Mon Sep 17 00:00:00 2001
From: trcrsired <oyzawqgcfc at gmail.com>
Date: Sat, 11 Jul 2026 17:56:19 +0800
Subject: [PATCH] [libc++] Support freestanding libc++

---
 libcxx/CMakeLists.txt                         |  55 +++++++--
 libcxx/include/__algorithm/sort.h             |   3 +
 libcxx/include/__config                       |  13 +-
 libcxx/include/__config_site.in               |   3 +
 libcxx/include/__mbstate_t.h                  |  10 ++
 libcxx/include/ctime                          |   4 +
 libcxx/include/math.h                         |  37 ++++++
 libcxx/include/stdio.h                        |  14 +++
 libcxx/include/stdlib.h                       |  19 +++
 libcxx/include/string.h                       |  41 ++++++-
 libcxx/test/libcxx/libcpp_freestanding.sh.cpp | 111 ++++++++++++++++++
 11 files changed, 293 insertions(+), 17 deletions(-)
 create mode 100644 libcxx/test/libcxx/libcpp_freestanding.sh.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..ee9be266975fc 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -156,21 +156,44 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
-elseif(MINGW)
-  set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
-elseif(WIN32) # clang-cl
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
-  else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
-  endif()
+option(LIBCXX_FREESTANDING
+   "Build libc++ in freestanding mode. This toggle allows the libc++ to work without libc
+    for environments like operating systems kernels or embedded systems." OFF)
+
+if (LIBCXX_FREESTANDING)
+  set(LIBCXX_ENABLE_FILESYSTEM OFF)
+  set(LIBCXX_ENABLE_RANDOM_DEVICE OFF)
+  set(LIBCXX_ENABLE_PARALLEL_ALGORITHMS OFF)
+  set(LIBCXX_ENABLE_SHARED OFF)
+  set(LIBCXX_ENABLE_STATIC OFF)
+  set(CMAKE_ASM_COMPILER_WORKS ON)
+  set(CMAKE_C_COMPILER_WORKS ON)
+  set(CMAKE_CXX_COMPILER_WORKS ON)
+  set(LIBCXX_ENABLE_LOCALIZATION OFF)
+  set(LIBCXX_ENABLE_FSTREAM OFF)
+  set(LIBCXX_ENABLE_STD_MODULES OFF)
+  set(LIBCXX_INCLUDE_BENCHMARKS OFF)
+  set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF)
+  set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF)
+  set(LIBCXX_INCLUDE_TESTS Off)
+  set(LIBCXX_ENABLE_THREADS Off)
 else()
-  if (LIBCXX_ENABLE_SHARED)
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
+  elseif(MINGW)
+    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-mingw.cfg.in")
+  elseif(WIN32) # clang-cl
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-clangcl.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static-clangcl.cfg.in")
+    endif()
   else()
-    set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    if (LIBCXX_ENABLE_SHARED)
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared.cfg.in")
+    else()
+      set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-static.cfg.in")
+    endif()
   endif()
 endif()
 set(LIBCXX_TEST_CONFIG "${LIBCXX_DEFAULT_TEST_CONFIG}" CACHE STRING
@@ -901,6 +924,10 @@ if (WIN32 AND LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
   add_definitions(-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
 endif()
 
+if (LIBCXX_FREESTANDING)
+  config_define(LIBCXX_FREESTANDING _LIBCPP_FREESTANDING)
+endif()
+
 # Setup all common build flags =================================================
 function(cxx_add_common_build_flags target)
   cxx_add_basic_build_flags(${target})
@@ -920,9 +947,11 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+if (NOT LIBCXX_FREESTANDING)
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
+endif()
 
 if (LIBCXX_INCLUDE_TESTS)
   add_subdirectory(test)
diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h
index 0a936db78e5fd..ff9d7d2b94e58 100644
--- a/libcxx/include/__algorithm/sort.h
+++ b/libcxx/include/__algorithm/sort.h
@@ -832,6 +832,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 template <class _Comp, class _RandomAccessIterator>
 void __sort(_RandomAccessIterator, _RandomAccessIterator, _Comp);
 
+#ifndef _LIBCPP_FREESTANDING
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<char>&, char*>(char*, char*, __less<char>&);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<wchar_t>&, wchar_t*>(wchar_t*, wchar_t*, __less<wchar_t>&);
@@ -857,6 +858,8 @@ extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<float>&, float*>(fl
 extern template _LIBCPP_EXPORTED_FROM_ABI void __sort<__less<double>&, double*>(double*, double*, __less<double>&);
 extern template _LIBCPP_EXPORTED_FROM_ABI void
 __sort<__less<long double>&, long double*>(long double*, long double*, __less<long double>&);
+#endif
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Comp>
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..dd56cb6f91604 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -37,6 +37,10 @@
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
 #  define _LIBCPP_VERSION 230000
+#  if __STDC_HOSTED__ == 0
+#    undef _LIBCPP_FREESTANDING
+#    define _LIBCPP_FREESTANDING
+#  endif
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
@@ -111,8 +115,13 @@
 #    define _LIBCPP_ALIGNOF(...) alignof(__VA_ARGS__)
 #    define _ALIGNAS_TYPE(x) alignas(x)
 #    define _ALIGNAS(x) alignas(x)
-#    define _NOEXCEPT noexcept
-#    define _NOEXCEPT_(...) noexcept(__VA_ARGS__)
+#    ifdef __cplusplus
+#      define _NOEXCEPT noexcept
+#      define _NOEXCEPT_(...) noexcept(__VA_ARGS__)
+#    else
+#      define _NOEXCEPT
+#      define _NOEXCEPT_(...)
+#    endif
 #    define _LIBCPP_CONSTEXPR constexpr
 
 #  else
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index e6e33a2eeb2a9..052c2d00cc0ee 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,9 @@
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
 
+// Freestanding
+#cmakedefine _LIBCPP_FREESTANDING
+
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
 #cmakedefine _LIBCPP_PSTL_BACKEND_STD_THREAD
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..3c4a1a7b0da03 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -51,6 +51,16 @@
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#elif defined(_LIBCPP_FREESTANDING)
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+typedef struct {
+  int __fill[6];
+} mbstate_t;
+#  ifdef __cplusplus
+}
+#  endif
 #else
 #  error "We don't know how to get the definition of mbstate_t on your platform."
 #endif
diff --git a/libcxx/include/ctime b/libcxx/include/ctime
index 1e845a8de0930..3fb2c7d4a67fb 100644
--- a/libcxx/include/ctime
+++ b/libcxx/include/ctime
@@ -57,6 +57,10 @@ int timespec_get( struct timespec *ts, int base); // C++17
 #    ifdef _LIBCPP_TIME_H
 #      error "If libc++ starts defining <time.h>, the __has_include check should move to libc++'s <time.h>"
 #    endif
+#  else
+extern "C" {
+typedef __UINT_LEAST64_TYPE__ time_t;
+}
 #  endif
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..190daf23285fa 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -302,6 +302,43 @@ long double    truncl(long double x);
 
 #    if __has_include_next(<math.h>)
 #      include_next <math.h>
+#    else
+
+#      ifndef NAN
+#        define NAN __builtin_nanf("")
+#      endif
+
+#      ifndef INFINITY
+#        define INFINITY __builtin_inff()
+#      endif
+
+#      ifndef FP_NAN
+#        define FP_NAN 0
+#      endif
+
+#      ifndef FP_INFINITE
+#        define FP_INFINITE 1
+#      endif
+
+#      ifndef FP_ZERO
+#        define FP_ZERO 2
+#      endif
+
+#      ifndef FP_SUBNORMAL
+#        define FP_SUBNORMAL 3
+#      endif
+
+#      ifndef FP_NORMAL
+#        define FP_NORMAL 4
+#      endif
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+float atan2f(float, float) _NOEXCEPT;
+long double atan2l(long double, long double) _NOEXCEPT;
+#      ifdef __cplusplus
+}
+#      endif
 #    endif
 
 #    ifdef __cplusplus
diff --git a/libcxx/include/stdio.h b/libcxx/include/stdio.h
index 4ce98d178ed2f..b1985625d5b09 100644
--- a/libcxx/include/stdio.h
+++ b/libcxx/include/stdio.h
@@ -118,4 +118,18 @@ void perror(const char* s);
 #    undef getchar
 
 #  endif // __cplusplus
+
+#  if !__has_include_next(<stdio.h>)
+#    ifndef EOF
+#      define EOF (-1)
+#    endif
+#    ifdef __cplusplus
+extern "C" {
+#    endif // __cplusplus
+int remove(const char*) _NOEXCEPT;
+#    ifdef __cplusplus
+}
+#    endif // __cplusplus
+#  endif
+
 #endif   // _LIBCPP_STDIO_H
diff --git a/libcxx/include/stdlib.h b/libcxx/include/stdlib.h
index 8dfdfa416f088..43f1d24320fcb 100644
--- a/libcxx/include/stdlib.h
+++ b/libcxx/include/stdlib.h
@@ -92,6 +92,25 @@ void *aligned_alloc(size_t alignment, size_t size);                       // C11
 #  if !defined(_LIBCPP_STDLIB_H)
 #    define _LIBCPP_STDLIB_H
 
+#    if !__has_include_next(<stdlib.h>)
+#      ifdef __cplusplus
+extern "C" {
+#      endif
+struct ldiv_t {
+  long quot;
+  long rem;
+};
+struct lldiv_t {
+  long long quot;
+  long long rem;
+};
+ldiv_t ldiv(long x, long y) _NOEXCEPT;
+lldiv_t lldiv(long long x, long long y) _NOEXCEPT;
+#      ifdef __cplusplus
+}
+#      endif
+#    endif
+
 #    ifdef __cplusplus
 extern "C++" {
 // abs
diff --git a/libcxx/include/string.h b/libcxx/include/string.h
index 2ffcb913010b6..fd70bcf621e1b 100644
--- a/libcxx/include/string.h
+++ b/libcxx/include/string.h
@@ -60,11 +60,48 @@ size_t strlen(const char* s);
 #    pragma GCC system_header
 #  endif
 
+#  include <stddef.h>
 #  if __has_include_next(<string.h>)
 #    include_next <string.h>
-#  endif
+#  else
+#    ifdef __cplusplus
+extern "C" {
+#    endif
 
-#  include <stddef.h>
+/*
+We offer declarations for these C functions,
+leaving the implementations to be handled by users.
+*/
+
+void* memcpy(void* __restrict, const void* __restrict, size_t) _NOEXCEPT;
+void* memmove(void*, const void*, size_t) _NOEXCEPT;
+char* strcpy(char* __restrict, const char* __restrict) _NOEXCEPT;
+char* strncpy(char* __restrict, const char* __restrict, size_t) _NOEXCEPT;
+char* strcat(char* __restrict, const char* __restrict) _NOEXCEPT;
+char* strncat(char* __restrict, const char* __restrict, size_t) _NOEXCEPT;
+int memcmp(const void*, const void*, size_t) _NOEXCEPT;
+int strcmp(const char*, const char*) _NOEXCEPT;
+int strncmp(const char*, const char*, size_t) _NOEXCEPT;
+const void* memchr(const void*, int, size_t) _NOEXCEPT;
+const char* strchr(const char*, int) _NOEXCEPT;
+size_t strcspn(const char*, const char*) _NOEXCEPT;
+const char* strpbrk(const char*, const char*) _NOEXCEPT;
+const char* strrchr(const char*, int) _NOEXCEPT;
+size_t strspn(const char*, const char*) _NOEXCEPT;
+const char* strstr(const char*, const char*) _NOEXCEPT;
+char* strtok(char* __restrict, const char* __restrict) _NOEXCEPT;
+void* memset(void*, int, size_t) _NOEXCEPT;
+size_t strlen(const char*) _NOEXCEPT;
+size_t strnlen(const char*, size_t) _NOEXCEPT;
+void* memset_explicit(void*, int, size_t) _NOEXCEPT;
+int strcoll(const char*, const char*) _NOEXCEPT;
+size_t strxfrm(char* __restrict, const char* __restrict, size_t) _NOEXCEPT;
+char* strerror(int) _NOEXCEPT;
+
+#    ifdef __cplusplus
+}
+#    endif
+#  endif
 
 // MSVCRT, GNU libc and its derivates may already have the correct prototype in
 // <string.h>. This macro can be defined by users if their C library provides
diff --git a/libcxx/test/libcxx/libcpp_freestanding.sh.cpp b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
new file mode 100644
index 0000000000000..e8e84bccccbac
--- /dev/null
+++ b/libcxx/test/libcxx/libcpp_freestanding.sh.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test that _LIBCPP_FREESTANDING is not defined when -ffreestanding is not passed
+// to the compiler but defined when -ffreestanding is passed to the compiler.
+
+// RUN: %{cxx} %{flags} %{compile_flags} -fsyntax-only -ffreestanding %s
+
+#include <__config>
+
+#if defined(__has_feature)
+#  if __has_feature(modules)
+#    define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#  endif
+#elif defined(__cpp_modules)
+#  define _LIBCPP_FREESTANDING_NO_TEST_MODULES
+#endif
+
+#if defined(_LIBCPP_FREESTANDING) && !defined(_LIBCPP_FREESTANDING_NO_TEST_MODULES)
+#  include <cstddef>
+#  include <limits>
+#  include <climits>
+#  include <cfloat>
+#  include <version>
+#  include <cstdint>
+#  include <cstdlib>
+#  include <new>
+#  include <typeinfo>
+#  if __has_include(<source_location>)
+#    include <source_location>
+#  endif
+#  include <exception>
+#  include <initializer_list>
+#  include <compare>
+#  include <coroutine>
+#  include <cstdarg>
+#  include <concepts>
+#  include <type_traits>
+#  include <bit>
+#  include <atomic>
+#  include <utility>
+#  include <tuple>
+#  include <memory>
+#  include <functional>
+#  include <ratio>
+#  include <iterator>
+#  include <ranges>
+#  include <typeinfo>
+#  include <execution>
+
+/*
+We tested these headers are for preventing build issues
+*/
+#  include <iosfwd>
+#  include <cstdio>
+#  include <cerrno>
+#  include <cstring>
+#  include <cmath>
+#  ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+#    include <cwchar>
+#  endif
+#  include <array>
+#  include <span>
+#  include <algorithm>
+#  include <bitset>
+#  include <variant>
+#  include <optional>
+#  include <numbers>
+#  include <scoped_allocator>
+#  include <typeindex>
+#  include <string_view>
+#  include <numeric>
+#  include <complex>
+#  include <chrono>
+#  include <charconv>
+#  include <expected>
+#  include <random>
+#  include <any>
+
+/*
++ * libc++ Containers Extensions
++ */
+#  include <vector>
+#  include <deque>
+#  include <string>
+#  include <stack>
+#  include <queue>
+#  include <list>
+#  include <forward_list>
+#  include <map>
+#  include <set>
+#  include <unordered_set>
+#  include <unordered_map>
+#  if __has_include(<mdspan>)
+#    include <mdspan>
+#  endif
+#  include <valarray>
+
+#  if _LIBCPP_STD_VER < 20
+#    include <ciso646>
+#    if __has_include(<cstdalign>)
+#      include <cstdalign>
+#    endif
+#    include <cstdbool>
+#  endif
+#endif


From libcxx-commits at lists.llvm.org  Sun Jul 12 23:28:56 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 12 Jul 2026 23:28:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a5485a8.266b0cc5.29d78.cff3@mx.google.com>


================
@@ -111,6 +118,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
+
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
----------------
frederick-vs-ja wrote:

Good point. Then would you mind to to bring both `static_assert`'s to the top of the class definition (or immediately below `public:`), and change `!is_rvalue_reference<deleter_type>::value` to `!is_rvalue_reference<_Dp>::value`?

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 23:29:02 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 12 Jul 2026 23:29:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a5485ae.6365b89b.8575e.70f5@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Sun Jul 12 23:29:39 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 23:29:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a5485d3.2490598a.cf17c.d204@mx.google.com>


Himadhith wrote:

> General comment: isn't better to insert target specific check inside the fopen function definition rather than function declarations?

IIUC, the `_LIBCPP_DIAGNOSE_WARNING` uses `__attribute__((__diagnose_if__(...)))` which is a declaration attribute — it must be on the declaration so the compiler can evaluate the condition against the caller's arguments at the call site, before the call happens.


https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Sun Jul 12 23:45:12 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 23:45:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a548978.e55dc341.1f19bd.033c@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/209018

>From c4349f973fed37783692cce1aae993813563a5dd Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Sun, 12 Jul 2026 21:31:47 +0800
Subject: [PATCH 1/3] [libcxx][unique_ptr] Implement LWG 4144: Disallow
 unique_ptr<T&, D>

---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__memory/unique_ptr.h          |  9 +++++
 .../non_pointable.single.verify.cpp           | 40 +++++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 8441da10d78de..c203fa424556a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -101,7 +101,7 @@
 "`LWG4140 <https://wg21.link/LWG4140>`__","Useless default constructors for bit reference types","2024-11 (Wrocław)","|Complete|","","`#118356 <https://github.com/llvm/llvm-project/issues/118356>`__",""
 "`LWG4141 <https://wg21.link/LWG4141>`__","Improve prohibitions on ""additional storage""","2024-11 (Wrocław)","","","`#118357 <https://github.com/llvm/llvm-project/issues/118357>`__",""
 "`LWG4142 <https://wg21.link/LWG4142>`__","``format_parse_context::check_dynamic_spec`` should require at least one type","2024-11 (Wrocław)","","","`#118358 <https://github.com/llvm/llvm-project/issues/118358>`__",""
-"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","","","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
+"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","|Complete|","23","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
 "`LWG4147 <https://wg21.link/LWG4147>`__","Precondition on ``inplace_vector::emplace``","2024-11 (Wrocław)","","","`#118361 <https://github.com/llvm/llvm-project/issues/118361>`__",""
 "`LWG4148 <https://wg21.link/LWG4148>`__","``unique_ptr::operator*`` should not allow dangling references","2024-11 (Wrocław)","","","`#118362 <https://github.com/llvm/llvm-project/issues/118362>`__",""
 "`LWG4153 <https://wg21.link/LWG4153>`__","Fix extra ""-1"" for ``philox_engine::max()``","2024-11 (Wrocław)","","","`#118363 <https://github.com/llvm/llvm-project/issues/118363>`__",""
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 7e68faa15dabb..93878301b0322 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -43,6 +43,7 @@
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_extent.h>
 #include <__type_traits/remove_reference.h>
+#include <__type_traits/void_t.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -100,6 +101,12 @@ inline const bool __can_dereference = false;
 template <class _Tp>
 inline const bool __can_dereference<_Tp, decltype((void)*std::declval<_Tp>())> = true;
 
+template <class _Tp, class = void>
+inline const bool __is_pointable = false;
+
+template <class _Tp>
+inline const bool __is_pointable<_Tp, __void_t<_Tp*> > = true;
+
 #if defined(_LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI)
 #  define _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI __attribute__((__trivial_abi__))
 #else
@@ -111,6 +118,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
+
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
   using pointer _LIBCPP_NODEBUG = __pointer<_Tp, deleter_type>;
 
   static_assert(!is_rvalue_reference<deleter_type>::value, "the specified deleter type cannot be an rvalue reference");
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
new file mode 100644
index 0000000000000..14829629861c6
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
+
+void reference_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<int&, Deleter>);
+}
+
+void abominable_function_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
+}

>From 79c6048aef67e3bfff149275bd08d8540ec5d3fd Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Mon, 13 Jul 2026 12:17:49 +0800
Subject: [PATCH 2/3] adjust lit test

1. Add [unique.ptr.single.general]/1 marker
2. Merge test cases into one
3. Remove useless lines
---
 .../unique.ptr.class/non_pointable.single.verify.cpp   | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
index 14829629861c6..6f27cdbdce4eb 100644
--- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -10,10 +10,9 @@
 
 // unique_ptr
 
+// [unique.ptr.single.general]/1 (added by LWG4144)
 // A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
 
-// LWG 4144
-
 // XFAIL: FROZEN-CXX03-HEADERS-FIXME
 
 #include <memory>
@@ -24,17 +23,12 @@ struct Deleter {
   void operator()(pointer) const;
 };
 
-typedef void Function();
 typedef void AbominableFunction() const;
 
-void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
-
-void reference_type() {
+void test() {
   // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
   (void)sizeof(std::unique_ptr<int&, Deleter>);
-}
 
-void abominable_function_type() {
   // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
   (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
 }

>From 0f785c8af9379592f076d2f75cd41cdd275163b8 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Mon, 13 Jul 2026 14:44:56 +0800
Subject: [PATCH 3/3] Put all static_assert of unique_ptr together

---
 libcxx/include/__memory/unique_ptr.h | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 93878301b0322..dce23434d48f1 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -115,15 +115,14 @@ inline const bool __is_pointable<_Tp, __void_t<_Tp*> > = true;
 
 template <class _Tp, class _Dp = default_delete<_Tp> >
 class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
+  static_assert(!is_rvalue_reference<_Dp>::value, "the specified deleter type cannot be an rvalue reference");
+
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
-
-  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
   using pointer _LIBCPP_NODEBUG = __pointer<_Tp, deleter_type>;
 
-  static_assert(!is_rvalue_reference<deleter_type>::value, "the specified deleter type cannot be an rvalue reference");
-
   // A unique_ptr contains the following members which may be trivially relocatable:
   // - pointer : this may be trivially relocatable, so it's checked
   // - deleter_type: this may be trivially relocatable, so it's checked


From libcxx-commits at lists.llvm.org  Sun Jul 12 23:47:22 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 12 Jul 2026 23:47:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a5489fa.160324b0.195192.baf9@mx.google.com>


================
@@ -111,6 +118,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
+
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
----------------
emmett2020 wrote:

Sure, that makes sense. I've moved both `static_assert`s to the top of the class definition, and changed the deleter check to use `_Dp` directly.

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Mon Jul 13 00:17:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 00:17:28 -0700 (PDT)
Subject: [libcxx-commits] [compiler-rt] [libcxx] [libcxxabi] [libunwind]
 [llvm] for ohos llvm libcxx (PR #208390)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208390@github.com>
Message-ID: <6a549108.c56fb102.7c9cd.b514@mx.google.com>


https://github.com/huihui15537226823 closed https://github.com/llvm/llvm-project/pull/208390

From libcxx-commits at lists.llvm.org  Mon Jul 13 00:46:49 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 13 Jul 2026 00:46:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a5497e9.4a342cbc.ca844.1986@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:07:39 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:07:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][RFC] Rewrite CPOs with resolver
	functions (PR #209104)
Message-ID: <llvm.org/llvm/llvm-project/pull/209104@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/209104

This patch rewrites some CPOs to use resolver functions instead of overload sets. This has the benefit that the implementation is much closer to the standard wording. I also find it somewhat easier to understand what the intention is.

I'm not sure whether we actually want to do this, but I thought I'd propose it and see whether people like it.


>From 3a7ea0c652bf6aebe9489dcde92a015fd5a29774 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 10:04:56 +0200
Subject: [PATCH] [libc++][RFC] Rewrite CPOs with resolver functions

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/access.h              | 180 +++++++++---------
 libcxx/include/__ranges/data.h                |  60 ++++--
 libcxx/include/__utility/cpo.h                |  33 ++++
 .../std/ranges/range.access/begin.verify.cpp  |   4 +-
 .../std/ranges/range.access/end.verify.cpp    |   4 +-
 6 files changed, 175 insertions(+), 107 deletions(-)
 create mode 100644 libcxx/include/__utility/cpo.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..19a4b53dd760f 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -934,6 +934,7 @@ set(files
   __utility/cmp.h
   __utility/constant_wrapper.h
   __utility/convert_to_integral.h
+  __utility/cpo.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
   __utility/element_count.h
diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index 28555ef6ba0d9..314f4d933626b 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -12,15 +12,16 @@
 
 #include <__concepts/class_or_enum.h>
 #include <__config>
-#include <__cstddef/size_t.h>
 #include <__iterator/concepts.h>
-#include <__iterator/readable_traits.h>
 #include <__ranges/enable_borrowed_range.h>
-#include <__type_traits/decay.h>
-#include <__type_traits/is_reference.h>
+#include <__type_traits/extent.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/remove_all_extents.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_reference.h>
 #include <__utility/auto_cast.h>
+#include <__utility/cpo.h>
 #include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -40,50 +41,50 @@ concept __can_borrow = is_lvalue_reference_v<_Tp> || enable_borrowed_range<remov
 
 namespace ranges {
 namespace __begin {
-template <class _Tp>
-concept __member_begin = __can_borrow<_Tp> && requires(_Tp&& __t) {
-  { _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator;
-};
-
 void begin() = delete;
 
-template <class _Tp>
-concept __unqualified_begin =
-    !__member_begin<_Tp> && __can_borrow<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { _LIBCPP_AUTO_CAST(begin(__t)) } -> input_or_output_iterator;
-    };
-
-struct __fn {
-  template <class _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[]) const noexcept
-    requires(sizeof(_Tp) >= 0) // Disallow incomplete element types.
-  {
-    return __t + 0;
+struct __fn : _CPO<[]<class _Ep> consteval noexcept {
+  // [range.access.begin]
+
+  // Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. Then:
+  using _Tp = remove_reference_t<_Ep>;
+
+  // If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false, ranges​::​begin(E) is ill-formed.
+  if constexpr (is_rvalue_reference_v<_Ep> && !enable_borrowed_range<remove_cv_t<_Tp>>) {
+    return;
+
+    // Otherwise, if T is an array type ([dcl.array]) and remove_all_extents_t<T> is an incomplete type,
+    // ranges​::​begin(E) is ill-formed with no diagnostic required.
+  } else if constexpr (is_array_v<_Tp>) {
+    if constexpr (!requires { sizeof(remove_all_extents_t<_Tp>); })
+      return;
+    else
+      // Otherwise, if T is an array type, ranges​::​begin(E) is expression-equivalent to t + 0.
+      return [](_Ep __v) noexcept { return __v + 0; };
+
+    // Otherwise, if auto(t.begin()) is a valid expression whose type models input_or_output_iterator,
+    // ranges​::​begin(E) is expression-equivalent to auto(t.begin()).
+  } else if constexpr (requires(_Tp& __t) {
+                         { _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(__v.begin()))) { return _LIBCPP_AUTO_CAST(__v.begin()); };
+
+    // Otherwise, if T is a class or enumeration type and auto(begin(t)) is a valid expression whose type models
+    // input_or_output_iterator where the meaning of begin is established as-if by performing argument-dependent lookup
+    // only ([basic.lookup.argdep]), then ranges​::​begin(E) is expression-equivalent to that expression.
+  } else if constexpr (__class_or_enum<_Tp>) {
+    if constexpr (requires(_Tp& __t) {
+                    { _LIBCPP_AUTO_CAST(begin(__t)) } -> input_or_output_iterator;
+                  })
+      return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(begin(__v)))) { return _LIBCPP_AUTO_CAST(begin(__v)); };
+    else
+      return;
+
+    // Otherwise, ranges​::​begin(E) is ill-formed.
+  } else {
+    return;
   }
-
-  template <class _Tp, size_t _Np>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept
-    requires(sizeof(_Tp) >= 0) // Disallow incomplete element types.
-  {
-    return __t + 0;
-  }
-
-  template <class _Tp>
-    requires __member_begin<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.begin()))) {
-    return _LIBCPP_AUTO_CAST(__t.begin());
-  }
-
-  template <class _Tp>
-    requires __unqualified_begin<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(begin(__t)))) {
-    return _LIBCPP_AUTO_CAST(begin(__t));
-  }
-
-  void operator()(auto&&) const = delete;
-};
+}> {};
 } // namespace __begin
 
 inline namespace __cpo {
@@ -102,45 +103,53 @@ using iterator_t = decltype(ranges::begin(std::declval<_Tp&>()));
 
 namespace ranges {
 namespace __end {
-template <class _Tp>
-concept __member_end = __can_borrow<_Tp> && requires(_Tp&& __t) {
-  typename iterator_t<_Tp>;
-  { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for<iterator_t<_Tp>>;
-};
-
-void end() = delete;
-
-template <class _Tp>
-concept __unqualified_end =
-    !__member_end<_Tp> && __can_borrow<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      typename iterator_t<_Tp>;
-      { _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for<iterator_t<_Tp>>;
-    };
-
-struct __fn {
-  template <class _Tp, size_t _Np>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept
-    requires(sizeof(_Tp) >= 0) // Disallow incomplete element types.
-  {
-    return __t + _Np;
-  }
-
-  template <class _Tp>
-    requires __member_end<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.end()))) {
-    return _LIBCPP_AUTO_CAST(__t.end());
+struct __fn : _CPO<[]<class _Ep> consteval noexcept {
+  // [range.access.end]
+
+  // Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. Then:
+  using _Tp = remove_reference_t<_Ep>;
+
+  // If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false, ranges​::​end(E) is ill-formed.
+  if constexpr (is_rvalue_reference_v<_Ep> && !enable_borrowed_range<remove_cv_t<_Tp>>) {
+    return;
+
+    // Otherwise, if T is an array type ([dcl.array]) and remove_all_extents_t<T> is an incomplete type,
+    // ranges​::​end(E) is ill-formed with no diagnostic required.
+  } else if constexpr (is_array_v<_Tp>) {
+    if constexpr (!requires { sizeof(remove_all_extents_t<_Tp>); }) {
+      return;
+
+      // Otherwise, if T is an array of unknown bound, ranges​::​end(E) is ill-formed.
+    } else if constexpr (is_unbounded_array_v<_Tp>) {
+      return;
+
+      // Otherwise, if T is an array, ranges​::​end(E) is expression-equivalent to t + extent_v<T>.
+    } else {
+      return [](_Ep __v) noexcept { return __v + extent_v<_Tp>; };
+    }
+
+    // Otherwise, if auto(t.end()) is a valid expression whose type models sentinel_for<iterator_t<T>> then
+    // ranges​::​end(E) is expression-equivalent to auto(t.end()).
+  } else if constexpr (requires(_Tp& __t) {
+                         { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for<iterator_t<_Tp>>;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(__v.end()))) { return _LIBCPP_AUTO_CAST(__v.end()); };
+
+    // Otherwise, if T is a class or enumeration type and auto(end(t)) is a valid expression whose type models
+    // sentinel_for<iterator_t<T>> where the meaning of end is established as-if by performing argument-dependent lookup
+    // only ([basic.lookup.argdep]), then ranges​::​end(E) is expression-equivalent to that expression.
+  } else if constexpr (__class_or_enum<_Tp>) {
+    if constexpr (requires(_Tp& __t) {
+                    { _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for<iterator_t<_Tp>>;
+                  }) {
+      return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(end(__v)))) { return _LIBCPP_AUTO_CAST(end(__v)); };
+    } else {
+      return;
+    }
+  } else {
+    return;
   }
-
-  template <class _Tp>
-    requires __unqualified_end<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(end(__t)))) {
-    return _LIBCPP_AUTO_CAST(end(__t));
-  }
-
-  void operator()(auto&&) const = delete;
-};
+}> {};
 } // namespace __end
 
 inline namespace __cpo {
@@ -191,8 +200,9 @@ struct __fn {
 
   template <class _Tp>
     requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
-      noexcept(ranges::end(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::end(static_cast<const _Tp&&>(__t))))
+          -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
     return ranges::end(static_cast<const _Tp&&>(__t));
   }
 };
diff --git a/libcxx/include/__ranges/data.h b/libcxx/include/__ranges/data.h
index 354a0aa15903e..075ae7cdaba8a 100644
--- a/libcxx/include/__ranges/data.h
+++ b/libcxx/include/__ranges/data.h
@@ -10,19 +10,17 @@
 #ifndef _LIBCPP___RANGES_DATA_H
 #define _LIBCPP___RANGES_DATA_H
 
-#include <__concepts/class_or_enum.h>
 #include <__config>
 #include <__iterator/concepts.h>
-#include <__iterator/iterator_traits.h>
 #include <__memory/pointer_traits.h>
 #include <__ranges/access.h>
-#include <__type_traits/decay.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
 #include <__utility/auto_cast.h>
+#include <__utility/cpo.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -49,18 +47,47 @@ concept __ranges_begin_invocable = !__member_data<_Tp> && __can_borrow<_Tp> && r
   { ranges::begin(__t) } -> contiguous_iterator;
 };
 
-struct __fn {
-  template <__member_data _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(noexcept(__t.data())) {
-    return __t.data();
-  }
-
-  template <__ranges_begin_invocable _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(std::to_address(ranges::begin(__t)))) {
-    return std::to_address(ranges::begin(__t));
+struct __fn : _CPO<[]<class _Ep> consteval noexcept {
+  // [range.prim.data]
+
+  // Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. Then:
+  using _Tp = remove_reference_t<_Ep>;
+
+  // If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false, ranges​::​data(E) is ill-formed.
+  if constexpr (is_rvalue_reference_v<_Ep> && !enable_borrowed_range<remove_cv_t<_Tp>>) {
+    return;
+
+    // Otherwise, if T is an array type ([dcl.array]) and remove_all_extents_t<T> is an incomplete type,
+    // ranges​::​data(E) is ill-formed with no diagnostic required.
+  } else if constexpr (is_array_v<_Tp>) {
+    if constexpr (!requires { sizeof(remove_all_extents_t<_Tp>); }) {
+      return;
+    } else {
+      // This is inlined from ranges::begin
+      return [](_Ep __v) noexcept { return __v + 0; };
+    }
+
+    // Otherwise, if auto(t.data()) is a valid expression of pointer to object type,
+    // ranges​::​data(E) is expression-equivalent to auto(t.data()).
+  } else if constexpr (requires(_Tp& __t) {
+                         { _LIBCPP_AUTO_CAST(__t.data()) } -> __ptr_to_object;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(__v.data()))) { return _LIBCPP_AUTO_CAST(__v.data()); };
+
+    // Otherwise, if ranges​::​begin(t) is a valid expression whose type models contiguous_iterator,
+    // ranges​::​data(E) is expression-equivalent to to_address(ranges​::​begin(t)).
+  } else if constexpr (requires(_Tp& __t) {
+                         { ranges::begin(__t) } -> contiguous_iterator;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(std::to_address(ranges::begin(__v)))) {
+      return std::to_address(ranges::begin(__v));
+    };
+
+    // Otherwise, ranges​::​data(E) is ill-formed.
+  } else {
+    return;
   }
-};
+}> {};
 } // namespace __data
 
 inline namespace __cpo {
@@ -83,8 +110,9 @@ struct __fn {
 
   template <class _Tp>
     requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
-      noexcept(ranges::data(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::data(static_cast<const _Tp&&>(__t))) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::data(static_cast<const _Tp&&>(__t))))
+          -> decltype(ranges::data(static_cast<const _Tp&&>(__t))) {
     return ranges::data(static_cast<const _Tp&&>(__t));
   }
 };
diff --git a/libcxx/include/__utility/cpo.h b/libcxx/include/__utility/cpo.h
new file mode 100644
index 0000000000000..e52cdb9a22168
--- /dev/null
+++ b/libcxx/include/__utility/cpo.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_CPO_H
+#define _LIBCPP___UTILITY_CPO_H
+
+#include <__config>
+#include <__utility/forward.h>
+
+#if _LIBCPP_STD_VER >= 20
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <auto __resolver>
+struct _CPO {
+  template <class... _Args>
+  static constexpr auto operator()(_Args&&... __args) noexcept(
+      noexcept(__resolver.template operator()<_Args&&...>()(std::forward<_Args>(__args)...)))
+      -> decltype(__resolver.template operator()<_Args&&...>()(std::forward<_Args>(__args)...)) {
+    return __resolver.template operator()<_Args&&...>()(std::forward<_Args>(__args)...);
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+
+#endif // _LIBCPP___UTILITY_CPO_H
diff --git a/libcxx/test/std/ranges/range.access/begin.verify.cpp b/libcxx/test/std/ranges/range.access/begin.verify.cpp
index 433404b4ad277..df6fce0402215 100644
--- a/libcxx/test/std/ranges/range.access/begin.verify.cpp
+++ b/libcxx/test/std/ranges/range.access/begin.verify.cpp
@@ -20,7 +20,5 @@ static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
 
 // Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::begin` is ill-formed.
 void test() {
-  std::ranges::begin(NonBorrowedRange());
-  // expected-error-re at -1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__begin::__fn'}}}}
-  // expected-error at -2  {{attempt to use a deleted function}}
+  std::ranges::begin(NonBorrowedRange()); // expected-error {{no matching function for call to object of type 'const __begin::__fn'}}
 }
diff --git a/libcxx/test/std/ranges/range.access/end.verify.cpp b/libcxx/test/std/ranges/range.access/end.verify.cpp
index a4a7f78b8c6b1..30cbd85243f24 100644
--- a/libcxx/test/std/ranges/range.access/end.verify.cpp
+++ b/libcxx/test/std/ranges/range.access/end.verify.cpp
@@ -20,7 +20,5 @@ static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
 
 // Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::end` is ill-formed.
 void test() {
-  std::ranges::end(NonBorrowedRange());
-  // expected-error-re at -1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__end::__fn'}}}}
-  // expected-error at -2  {{attempt to use a deleted function}}
+  std::ranges::end(NonBorrowedRange()); // expected-error {{no matching function for call to object of type 'const __end::__fn'}}
 }


From libcxx-commits at lists.llvm.org  Mon Jul 13 01:08:24 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 01:08:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][RFC] Rewrite CPOs with resolver
	functions (PR #209104)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209104@github.com>
Message-ID: <6a549cf8.27b3b003.3b871.6d74@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

This patch rewrites some CPOs to use resolver functions instead of overload sets. This has the benefit that the implementation is much closer to the standard wording. I also find it somewhat easier to understand what the intention is.

I'm not sure whether we actually want to do this, but I thought I'd propose it and see whether people like it.


---
Full diff: https://github.com/llvm/llvm-project/pull/209104.diff


6 Files Affected:

- (modified) libcxx/include/CMakeLists.txt (+1) 
- (modified) libcxx/include/__ranges/access.h (+95-85) 
- (modified) libcxx/include/__ranges/data.h (+44-16) 
- (added) libcxx/include/__utility/cpo.h (+33) 
- (modified) libcxx/test/std/ranges/range.access/begin.verify.cpp (+1-3) 
- (modified) libcxx/test/std/ranges/range.access/end.verify.cpp (+1-3) 


``````````diff
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..19a4b53dd760f 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -934,6 +934,7 @@ set(files
   __utility/cmp.h
   __utility/constant_wrapper.h
   __utility/convert_to_integral.h
+  __utility/cpo.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
   __utility/element_count.h
diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index 28555ef6ba0d9..314f4d933626b 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -12,15 +12,16 @@
 
 #include <__concepts/class_or_enum.h>
 #include <__config>
-#include <__cstddef/size_t.h>
 #include <__iterator/concepts.h>
-#include <__iterator/readable_traits.h>
 #include <__ranges/enable_borrowed_range.h>
-#include <__type_traits/decay.h>
-#include <__type_traits/is_reference.h>
+#include <__type_traits/extent.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/remove_all_extents.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_reference.h>
 #include <__utility/auto_cast.h>
+#include <__utility/cpo.h>
 #include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -40,50 +41,50 @@ concept __can_borrow = is_lvalue_reference_v<_Tp> || enable_borrowed_range<remov
 
 namespace ranges {
 namespace __begin {
-template <class _Tp>
-concept __member_begin = __can_borrow<_Tp> && requires(_Tp&& __t) {
-  { _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator;
-};
-
 void begin() = delete;
 
-template <class _Tp>
-concept __unqualified_begin =
-    !__member_begin<_Tp> && __can_borrow<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { _LIBCPP_AUTO_CAST(begin(__t)) } -> input_or_output_iterator;
-    };
-
-struct __fn {
-  template <class _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[]) const noexcept
-    requires(sizeof(_Tp) >= 0) // Disallow incomplete element types.
-  {
-    return __t + 0;
+struct __fn : _CPO<[]<class _Ep> consteval noexcept {
+  // [range.access.begin]
+
+  // Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. Then:
+  using _Tp = remove_reference_t<_Ep>;
+
+  // If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false, ranges​::​begin(E) is ill-formed.
+  if constexpr (is_rvalue_reference_v<_Ep> && !enable_borrowed_range<remove_cv_t<_Tp>>) {
+    return;
+
+    // Otherwise, if T is an array type ([dcl.array]) and remove_all_extents_t<T> is an incomplete type,
+    // ranges​::​begin(E) is ill-formed with no diagnostic required.
+  } else if constexpr (is_array_v<_Tp>) {
+    if constexpr (!requires { sizeof(remove_all_extents_t<_Tp>); })
+      return;
+    else
+      // Otherwise, if T is an array type, ranges​::​begin(E) is expression-equivalent to t + 0.
+      return [](_Ep __v) noexcept { return __v + 0; };
+
+    // Otherwise, if auto(t.begin()) is a valid expression whose type models input_or_output_iterator,
+    // ranges​::​begin(E) is expression-equivalent to auto(t.begin()).
+  } else if constexpr (requires(_Tp& __t) {
+                         { _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(__v.begin()))) { return _LIBCPP_AUTO_CAST(__v.begin()); };
+
+    // Otherwise, if T is a class or enumeration type and auto(begin(t)) is a valid expression whose type models
+    // input_or_output_iterator where the meaning of begin is established as-if by performing argument-dependent lookup
+    // only ([basic.lookup.argdep]), then ranges​::​begin(E) is expression-equivalent to that expression.
+  } else if constexpr (__class_or_enum<_Tp>) {
+    if constexpr (requires(_Tp& __t) {
+                    { _LIBCPP_AUTO_CAST(begin(__t)) } -> input_or_output_iterator;
+                  })
+      return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(begin(__v)))) { return _LIBCPP_AUTO_CAST(begin(__v)); };
+    else
+      return;
+
+    // Otherwise, ranges​::​begin(E) is ill-formed.
+  } else {
+    return;
   }
-
-  template <class _Tp, size_t _Np>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept
-    requires(sizeof(_Tp) >= 0) // Disallow incomplete element types.
-  {
-    return __t + 0;
-  }
-
-  template <class _Tp>
-    requires __member_begin<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.begin()))) {
-    return _LIBCPP_AUTO_CAST(__t.begin());
-  }
-
-  template <class _Tp>
-    requires __unqualified_begin<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(begin(__t)))) {
-    return _LIBCPP_AUTO_CAST(begin(__t));
-  }
-
-  void operator()(auto&&) const = delete;
-};
+}> {};
 } // namespace __begin
 
 inline namespace __cpo {
@@ -102,45 +103,53 @@ using iterator_t = decltype(ranges::begin(std::declval<_Tp&>()));
 
 namespace ranges {
 namespace __end {
-template <class _Tp>
-concept __member_end = __can_borrow<_Tp> && requires(_Tp&& __t) {
-  typename iterator_t<_Tp>;
-  { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for<iterator_t<_Tp>>;
-};
-
-void end() = delete;
-
-template <class _Tp>
-concept __unqualified_end =
-    !__member_end<_Tp> && __can_borrow<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      typename iterator_t<_Tp>;
-      { _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for<iterator_t<_Tp>>;
-    };
-
-struct __fn {
-  template <class _Tp, size_t _Np>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept
-    requires(sizeof(_Tp) >= 0) // Disallow incomplete element types.
-  {
-    return __t + _Np;
-  }
-
-  template <class _Tp>
-    requires __member_end<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.end()))) {
-    return _LIBCPP_AUTO_CAST(__t.end());
+struct __fn : _CPO<[]<class _Ep> consteval noexcept {
+  // [range.access.end]
+
+  // Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. Then:
+  using _Tp = remove_reference_t<_Ep>;
+
+  // If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false, ranges​::​end(E) is ill-formed.
+  if constexpr (is_rvalue_reference_v<_Ep> && !enable_borrowed_range<remove_cv_t<_Tp>>) {
+    return;
+
+    // Otherwise, if T is an array type ([dcl.array]) and remove_all_extents_t<T> is an incomplete type,
+    // ranges​::​end(E) is ill-formed with no diagnostic required.
+  } else if constexpr (is_array_v<_Tp>) {
+    if constexpr (!requires { sizeof(remove_all_extents_t<_Tp>); }) {
+      return;
+
+      // Otherwise, if T is an array of unknown bound, ranges​::​end(E) is ill-formed.
+    } else if constexpr (is_unbounded_array_v<_Tp>) {
+      return;
+
+      // Otherwise, if T is an array, ranges​::​end(E) is expression-equivalent to t + extent_v<T>.
+    } else {
+      return [](_Ep __v) noexcept { return __v + extent_v<_Tp>; };
+    }
+
+    // Otherwise, if auto(t.end()) is a valid expression whose type models sentinel_for<iterator_t<T>> then
+    // ranges​::​end(E) is expression-equivalent to auto(t.end()).
+  } else if constexpr (requires(_Tp& __t) {
+                         { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for<iterator_t<_Tp>>;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(__v.end()))) { return _LIBCPP_AUTO_CAST(__v.end()); };
+
+    // Otherwise, if T is a class or enumeration type and auto(end(t)) is a valid expression whose type models
+    // sentinel_for<iterator_t<T>> where the meaning of end is established as-if by performing argument-dependent lookup
+    // only ([basic.lookup.argdep]), then ranges​::​end(E) is expression-equivalent to that expression.
+  } else if constexpr (__class_or_enum<_Tp>) {
+    if constexpr (requires(_Tp& __t) {
+                    { _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for<iterator_t<_Tp>>;
+                  }) {
+      return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(end(__v)))) { return _LIBCPP_AUTO_CAST(end(__v)); };
+    } else {
+      return;
+    }
+  } else {
+    return;
   }
-
-  template <class _Tp>
-    requires __unqualified_end<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(end(__t)))) {
-    return _LIBCPP_AUTO_CAST(end(__t));
-  }
-
-  void operator()(auto&&) const = delete;
-};
+}> {};
 } // namespace __end
 
 inline namespace __cpo {
@@ -191,8 +200,9 @@ struct __fn {
 
   template <class _Tp>
     requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
-      noexcept(ranges::end(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::end(static_cast<const _Tp&&>(__t))))
+          -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
     return ranges::end(static_cast<const _Tp&&>(__t));
   }
 };
diff --git a/libcxx/include/__ranges/data.h b/libcxx/include/__ranges/data.h
index 354a0aa15903e..075ae7cdaba8a 100644
--- a/libcxx/include/__ranges/data.h
+++ b/libcxx/include/__ranges/data.h
@@ -10,19 +10,17 @@
 #ifndef _LIBCPP___RANGES_DATA_H
 #define _LIBCPP___RANGES_DATA_H
 
-#include <__concepts/class_or_enum.h>
 #include <__config>
 #include <__iterator/concepts.h>
-#include <__iterator/iterator_traits.h>
 #include <__memory/pointer_traits.h>
 #include <__ranges/access.h>
-#include <__type_traits/decay.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
 #include <__utility/auto_cast.h>
+#include <__utility/cpo.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -49,18 +47,47 @@ concept __ranges_begin_invocable = !__member_data<_Tp> && __can_borrow<_Tp> && r
   { ranges::begin(__t) } -> contiguous_iterator;
 };
 
-struct __fn {
-  template <__member_data _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(noexcept(__t.data())) {
-    return __t.data();
-  }
-
-  template <__ranges_begin_invocable _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(std::to_address(ranges::begin(__t)))) {
-    return std::to_address(ranges::begin(__t));
+struct __fn : _CPO<[]<class _Ep> consteval noexcept {
+  // [range.prim.data]
+
+  // Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. Then:
+  using _Tp = remove_reference_t<_Ep>;
+
+  // If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false, ranges​::​data(E) is ill-formed.
+  if constexpr (is_rvalue_reference_v<_Ep> && !enable_borrowed_range<remove_cv_t<_Tp>>) {
+    return;
+
+    // Otherwise, if T is an array type ([dcl.array]) and remove_all_extents_t<T> is an incomplete type,
+    // ranges​::​data(E) is ill-formed with no diagnostic required.
+  } else if constexpr (is_array_v<_Tp>) {
+    if constexpr (!requires { sizeof(remove_all_extents_t<_Tp>); }) {
+      return;
+    } else {
+      // This is inlined from ranges::begin
+      return [](_Ep __v) noexcept { return __v + 0; };
+    }
+
+    // Otherwise, if auto(t.data()) is a valid expression of pointer to object type,
+    // ranges​::​data(E) is expression-equivalent to auto(t.data()).
+  } else if constexpr (requires(_Tp& __t) {
+                         { _LIBCPP_AUTO_CAST(__t.data()) } -> __ptr_to_object;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(__v.data()))) { return _LIBCPP_AUTO_CAST(__v.data()); };
+
+    // Otherwise, if ranges​::​begin(t) is a valid expression whose type models contiguous_iterator,
+    // ranges​::​data(E) is expression-equivalent to to_address(ranges​::​begin(t)).
+  } else if constexpr (requires(_Tp& __t) {
+                         { ranges::begin(__t) } -> contiguous_iterator;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(std::to_address(ranges::begin(__v)))) {
+      return std::to_address(ranges::begin(__v));
+    };
+
+    // Otherwise, ranges​::​data(E) is ill-formed.
+  } else {
+    return;
   }
-};
+}> {};
 } // namespace __data
 
 inline namespace __cpo {
@@ -83,8 +110,9 @@ struct __fn {
 
   template <class _Tp>
     requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
-      noexcept(ranges::data(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::data(static_cast<const _Tp&&>(__t))) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::data(static_cast<const _Tp&&>(__t))))
+          -> decltype(ranges::data(static_cast<const _Tp&&>(__t))) {
     return ranges::data(static_cast<const _Tp&&>(__t));
   }
 };
diff --git a/libcxx/include/__utility/cpo.h b/libcxx/include/__utility/cpo.h
new file mode 100644
index 0000000000000..e52cdb9a22168
--- /dev/null
+++ b/libcxx/include/__utility/cpo.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_CPO_H
+#define _LIBCPP___UTILITY_CPO_H
+
+#include <__config>
+#include <__utility/forward.h>
+
+#if _LIBCPP_STD_VER >= 20
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <auto __resolver>
+struct _CPO {
+  template <class... _Args>
+  static constexpr auto operator()(_Args&&... __args) noexcept(
+      noexcept(__resolver.template operator()<_Args&&...>()(std::forward<_Args>(__args)...)))
+      -> decltype(__resolver.template operator()<_Args&&...>()(std::forward<_Args>(__args)...)) {
+    return __resolver.template operator()<_Args&&...>()(std::forward<_Args>(__args)...);
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+
+#endif // _LIBCPP___UTILITY_CPO_H
diff --git a/libcxx/test/std/ranges/range.access/begin.verify.cpp b/libcxx/test/std/ranges/range.access/begin.verify.cpp
index 433404b4ad277..df6fce0402215 100644
--- a/libcxx/test/std/ranges/range.access/begin.verify.cpp
+++ b/libcxx/test/std/ranges/range.access/begin.verify.cpp
@@ -20,7 +20,5 @@ static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
 
 // Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::begin` is ill-formed.
 void test() {
-  std::ranges::begin(NonBorrowedRange());
-  // expected-error-re at -1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__begin::__fn'}}}}
-  // expected-error at -2  {{attempt to use a deleted function}}
+  std::ranges::begin(NonBorrowedRange()); // expected-error {{no matching function for call to object of type 'const __begin::__fn'}}
 }
diff --git a/libcxx/test/std/ranges/range.access/end.verify.cpp b/libcxx/test/std/ranges/range.access/end.verify.cpp
index a4a7f78b8c6b1..30cbd85243f24 100644
--- a/libcxx/test/std/ranges/range.access/end.verify.cpp
+++ b/libcxx/test/std/ranges/range.access/end.verify.cpp
@@ -20,7 +20,5 @@ static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
 
 // Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::end` is ill-formed.
 void test() {
-  std::ranges::end(NonBorrowedRange());
-  // expected-error-re at -1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__end::__fn'}}}}
-  // expected-error at -2  {{attempt to use a deleted function}}
+  std::ranges::end(NonBorrowedRange()); // expected-error {{no matching function for call to object of type 'const __end::__fn'}}
 }

``````````

</details>


https://github.com/llvm/llvm-project/pull/209104

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:09:24 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 01:09:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][RFC] Rewrite CPOs with resolver
	functions (PR #209104)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209104@github.com>
Message-ID: <6a549d34.ff037943.2e959a.3e9f@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions cpp,h -- libcxx/include/__utility/cpo.h libcxx/include/__ranges/access.h libcxx/include/__ranges/data.h libcxx/test/std/ranges/range.access/begin.verify.cpp libcxx/test/std/ranges/range.access/end.verify.cpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/test/std/ranges/range.access/begin.verify.cpp b/libcxx/test/std/ranges/range.access/begin.verify.cpp
index df6fce040..71d59aa37 100644
--- a/libcxx/test/std/ranges/range.access/begin.verify.cpp
+++ b/libcxx/test/std/ranges/range.access/begin.verify.cpp
@@ -20,5 +20,6 @@ static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
 
 // Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::begin` is ill-formed.
 void test() {
-  std::ranges::begin(NonBorrowedRange()); // expected-error {{no matching function for call to object of type 'const __begin::__fn'}}
+  std::ranges::begin(
+      NonBorrowedRange()); // expected-error {{no matching function for call to object of type 'const __begin::__fn'}}
 }
diff --git a/libcxx/test/std/ranges/range.access/end.verify.cpp b/libcxx/test/std/ranges/range.access/end.verify.cpp
index 30cbd8524..56c73c68c 100644
--- a/libcxx/test/std/ranges/range.access/end.verify.cpp
+++ b/libcxx/test/std/ranges/range.access/end.verify.cpp
@@ -20,5 +20,6 @@ static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
 
 // Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::end` is ill-formed.
 void test() {
-  std::ranges::end(NonBorrowedRange()); // expected-error {{no matching function for call to object of type 'const __end::__fn'}}
+  std::ranges::end(
+      NonBorrowedRange()); // expected-error {{no matching function for call to object of type 'const __end::__fn'}}
 }

``````````

</details>


https://github.com/llvm/llvm-project/pull/209104

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:10:53 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:10:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a549d8d.bc28b987.90005.fdce@mx.google.com>


https://github.com/philnik777 requested changes to this pull request.

The CI isn't there to toy around. You can test things on your own system. This is also the kind of change that requires an RFC. We've already decided against various changes you're making here, and we probably won't reverse the direction.

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:11:02 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:11:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a549d96.824e11a6.f5f27.f023@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:14:29 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:14:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Remove unused includes from
 <__iterator/segmented_iterator.h> (PR #209105)
Message-ID: <llvm.org/llvm/llvm-project/pull/209105@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/209105

None

>From bd5073d266e34c6f29696878d8e408071f8532ec Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 10:14:09 +0200
Subject: [PATCH] [libc++][NFC] Remove unused includes from
 <__iterator/segmented_iterator.h>

---
 libcxx/include/__iterator/segmented_iterator.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/include/__iterator/segmented_iterator.h b/libcxx/include/__iterator/segmented_iterator.h
index dc56a740130b5..149092f3135cc 100644
--- a/libcxx/include/__iterator/segmented_iterator.h
+++ b/libcxx/include/__iterator/segmented_iterator.h
@@ -42,8 +42,6 @@
 
 #include <__config>
 #include <__cstddef/size_t.h>
-#include <__iterator/iterator_traits.h>
-#include <__type_traits/integral_constant.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header


From libcxx-commits at lists.llvm.org  Mon Jul 13 01:18:55 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:18:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add missing include to thread.h
	(PR #208740)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208740@github.com>
Message-ID: <6a549f6f.f1c1923d.991d1.34de@mx.google.com>


https://github.com/philnik777 approved this pull request.

I must have missed that line. Thanks.

https://github.com/llvm/llvm-project/pull/208740

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:22:38 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:22:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a54a04e.0c05e68e.1e82f1.9668@mx.google.com>


================
@@ -664,6 +674,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (noreplace) mode suffix; these cases fall through to
+// default (return nullptr), which is conformant per C23 7.21.5.3p5 (fail if atomic check-and-create is unsupported).
+#      if !defined(_AIX)
----------------
philnik777 wrote:

So you want to fix a libc issue in libc++?

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:23:41 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:23:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Avoid constructing additional
 objects when using map::at (PR #157866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/157866@github.com>
Message-ID: <6a54a08d.824e11a6.f5f27.e0a0@mx.google.com>


================
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___TYPE_TRAITS_MAKE_TRANSPARENT_H
+#define _LIBCPP___TYPE_TRAITS_MAKE_TRANSPARENT_H
+
+#include <__config>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_empty.h>
+#include <__type_traits/is_same.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __make_transparent tries to create a transparent comparator from its non-transparent counterpart, e.g. obtain
+// `less<>` from `less<T>`. This is useful in cases where conversions can be avoided (e.g. a string literal to a
+// std::string).
+
+template <class _Comparator>
+struct __make_transparent {
+  using type _LIBCPP_NODEBUG = _Comparator;
+};
+
+template <class _Comparator>
+using __make_transparent_t _LIBCPP_NODEBUG = typename __make_transparent<_Comparator>::type;
+
+template <class _Comparator, __enable_if_t<is_same<_Comparator, __make_transparent_t<_Comparator> >::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Comparator& __as_transparent(_Comparator& __comp) {
+  return __comp;
+}
+
+template <class _Comparator, __enable_if_t<!is_same<_Comparator, __make_transparent_t<_Comparator> >::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI __make_transparent_t<_Comparator> __as_transparent(_Comparator&) {
+  static_assert(is_empty<_Comparator>::value);
----------------
philnik777 wrote:

That should work, yes.

https://github.com/llvm/llvm-project/pull/157866

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:34:37 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:34:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Create libc++ 24 release notes
	(PR #208814)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208814@github.com>
Message-ID: <6a54a31d.e624eb8d.39cd4e.5a0b@mx.google.com>


https://github.com/philnik777 commented:

I'm not sure this is a good idea. I feel like this makes it very easy to accidentally put release notes in the wrong place. This is already an issue sometimes, and having more release notes probably makes it worse. Putting something in that is explicitly not supposed to be modified also seems somewhat error-prone to me. How would you feel about a PR to add the release notes in the upstream repo that PRs targeting the next release can then be based on? Basically a stacked PR.

P.S. we should really get better about removing old release notes. We still have all of them back to 20.

https://github.com/llvm/llvm-project/pull/208814

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:40:41 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:40:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as complete (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a54a489.7270d2fd.2d4879.ac2a@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:41:31 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:41:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Replace the of use custom
 sections for detecting overriden functions (PR #208330)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208330@github.com>
Message-ID: <6a54a4bb.4987c784.63cbc.a0c2@mx.google.com>


================
@@ -63,64 +51,53 @@
 // want to be defining special sections inside user's executables which use our headers.
 //
 
-#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
+#if defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
-
-_LIBCPP_BEGIN_NAMESPACE_STD template <typename T, T* _Func>
-_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-  // Declare two dummy bytes and give them these special `__asm` values. These values are
-  // defined by the linker, which means that referring to `&__lcxx_override_start` will
-  // effectively refer to the address where the section starts (and same for the end).
-  extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
-  extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
-
-  // Now get a uintptr_t out of these locations, and out of the function pointer.
-  uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
-  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
-  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
-
-#  if __has_feature(ptrauth_calls)
-  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
-  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
-  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
-  // stripped the function pointer. See rdar://122927845.
-  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
-#  endif
+#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
 
-  // Finally, the function was overridden if it falls outside of the section's bounds.
-  return __ptr < __start || __ptr > __end;
-}
-_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_BEGIN_NAMESPACE_STD
 
-// The NVPTX linker cannot create '__start/__stop' sections.
-#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)
+namespace {
 
-#  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+template <typename T>
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI T* __libcpp_launder(T* __ptr) noexcept {
+  __asm__ volatile("" : "+r"(__ptr));
+  return __ptr;
+}
----------------
philnik777 wrote:

@petrhosek Could I get an explanation for this?

https://github.com/llvm/llvm-project/pull/208330

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:45:37 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:45:37 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [clang][ThreadSafety] Add
 requires_negative_capability attribute (PR #203099)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/203099@github.com>
Message-ID: <6a54a5b1.b1416da1.78d1e.6557@mx.google.com>


philnik777 wrote:

Not sure why you pinged me?

https://github.com/llvm/llvm-project/pull/203099

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:47:53 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:47:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a54a639.07d7ca98.9dee3.31ce@mx.google.com>


================
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#if CLANG_VERSION_MAJOR > 23
----------------
philnik777 wrote:

Can we add a TODO to remove this in LLVM 25 (I think)?

https://github.com/llvm/llvm-project/pull/208945

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:54:38 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Mon, 13 Jul 2026 01:54:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Replace the of use custom
 sections for detecting overriden functions (PR #208330)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208330@github.com>
Message-ID: <6a54a7ce.e5907290.281f16.1030@mx.google.com>


================
@@ -63,64 +51,53 @@
 // want to be defining special sections inside user's executables which use our headers.
 //
 
-#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
+#if defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
-
-_LIBCPP_BEGIN_NAMESPACE_STD template <typename T, T* _Func>
-_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-  // Declare two dummy bytes and give them these special `__asm` values. These values are
-  // defined by the linker, which means that referring to `&__lcxx_override_start` will
-  // effectively refer to the address where the section starts (and same for the end).
-  extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
-  extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
-
-  // Now get a uintptr_t out of these locations, and out of the function pointer.
-  uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
-  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
-  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
-
-#  if __has_feature(ptrauth_calls)
-  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
-  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
-  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
-  // stripped the function pointer. See rdar://122927845.
-  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
-#  endif
+#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
 
-  // Finally, the function was overridden if it falls outside of the section's bounds.
-  return __ptr < __start || __ptr > __end;
-}
-_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_BEGIN_NAMESPACE_STD
 
-// The NVPTX linker cannot create '__start/__stop' sections.
-#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)
+namespace {
 
-#  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+template <typename T>
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI T* __libcpp_launder(T* __ptr) noexcept {
+  __asm__ volatile("" : "+r"(__ptr));
+  return __ptr;
+}
----------------
petrhosek wrote:

`std::launder` cannot be used with function pointers. If you have a suggestion for a better name, I'm happy to send a follow up PR to rename this function.

https://github.com/llvm/llvm-project/pull/208330

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:55:20 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:55:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a54a7f8.92ea0e2a.13fd37.cd55@mx.google.com>


================
@@ -100,6 +101,12 @@ inline const bool __can_dereference = false;
 template <class _Tp>
 inline const bool __can_dereference<_Tp, decltype((void)*std::declval<_Tp>())> = true;
 
+template <class _Tp, class = void>
+inline const bool __is_pointable = false;
----------------
philnik777 wrote:

I'm not a huge fan of this name. Something more descriptive like `__can_be_pointed_to` or `__can_add_pointer`?

Also, can we write this as `!is_same<__add_pointer_t<_Tp>, _Tp>`?

I'd love to just write `requires { _Tp*; }` without giving this a name at all, but unfortunately we don't like in that world (yet).

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Mon Jul 13 01:59:37 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 01:59:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Replace the of use custom
 sections for detecting overriden functions (PR #208330)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208330@github.com>
Message-ID: <6a54a8f9.92d2873c.258297.ee19@mx.google.com>


================
@@ -63,64 +51,53 @@
 // want to be defining special sections inside user's executables which use our headers.
 //
 
-#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
+#if defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
-
-_LIBCPP_BEGIN_NAMESPACE_STD template <typename T, T* _Func>
-_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-  // Declare two dummy bytes and give them these special `__asm` values. These values are
-  // defined by the linker, which means that referring to `&__lcxx_override_start` will
-  // effectively refer to the address where the section starts (and same for the end).
-  extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
-  extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
-
-  // Now get a uintptr_t out of these locations, and out of the function pointer.
-  uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
-  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
-  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
-
-#  if __has_feature(ptrauth_calls)
-  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
-  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
-  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
-  // stripped the function pointer. See rdar://122927845.
-  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
-#  endif
+#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
 
-  // Finally, the function was overridden if it falls outside of the section's bounds.
-  return __ptr < __start || __ptr > __end;
-}
-_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_BEGIN_NAMESPACE_STD
 
-// The NVPTX linker cannot create '__start/__stop' sections.
-#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)
+namespace {
 
-#  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
-#  define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+template <typename T>
+[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI T* __libcpp_launder(T* __ptr) noexcept {
+  __asm__ volatile("" : "+r"(__ptr));
+  return __ptr;
+}
----------------
philnik777 wrote:

I think I'd be happy to make that explicit by naming it e.g. `launder_function_pointer`.

https://github.com/llvm/llvm-project/pull/208330

From libcxx-commits at lists.llvm.org  Mon Jul 13 02:10:18 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 02:10:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][RFC] Rewrite CPOs with resolver
	functions (PR #209104)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209104@github.com>
Message-ID: <6a54ab7a.3cce8983.225732.ed6c@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/209104

>From fa77f42b2672ed1a342e4e7a792a944af205bbb7 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 10:04:56 +0200
Subject: [PATCH] [libc++][RFC] Rewrite CPOs with resolver functions

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/access.h              | 179 ++++++++++--------
 libcxx/include/__ranges/data.h                |  64 +++++--
 libcxx/include/__utility/cpo.h                |  33 ++++
 libcxx/include/module.modulemap.in            |   1 +
 .../std/ranges/range.access/begin.verify.cpp  |   4 +-
 .../std/ranges/range.access/end.verify.cpp    |   4 +-
 7 files changed, 180 insertions(+), 106 deletions(-)
 create mode 100644 libcxx/include/__utility/cpo.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..19a4b53dd760f 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -934,6 +934,7 @@ set(files
   __utility/cmp.h
   __utility/constant_wrapper.h
   __utility/convert_to_integral.h
+  __utility/cpo.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
   __utility/element_count.h
diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index 28555ef6ba0d9..29459abf88882 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -12,15 +12,17 @@
 
 #include <__concepts/class_or_enum.h>
 #include <__config>
-#include <__cstddef/size_t.h>
 #include <__iterator/concepts.h>
-#include <__iterator/readable_traits.h>
 #include <__ranges/enable_borrowed_range.h>
-#include <__type_traits/decay.h>
+#include <__type_traits/extent.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_reference.h>
+#include <__type_traits/remove_all_extents.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_reference.h>
 #include <__utility/auto_cast.h>
+#include <__utility/cpo.h>
 #include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -40,50 +42,50 @@ concept __can_borrow = is_lvalue_reference_v<_Tp> || enable_borrowed_range<remov
 
 namespace ranges {
 namespace __begin {
-template <class _Tp>
-concept __member_begin = __can_borrow<_Tp> && requires(_Tp&& __t) {
-  { _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator;
-};
-
 void begin() = delete;
 
-template <class _Tp>
-concept __unqualified_begin =
-    !__member_begin<_Tp> && __can_borrow<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { _LIBCPP_AUTO_CAST(begin(__t)) } -> input_or_output_iterator;
-    };
-
-struct __fn {
-  template <class _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[]) const noexcept
-    requires(sizeof(_Tp) >= 0) // Disallow incomplete element types.
-  {
-    return __t + 0;
+struct __fn : _CPO<[]<class _Ep> consteval noexcept {
+  // [range.access.begin]
+
+  // Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. Then:
+  using _Tp = remove_reference_t<_Ep>;
+
+  // If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false, ranges​::​begin(E) is ill-formed.
+  if constexpr (is_rvalue_reference_v<_Ep> && !enable_borrowed_range<remove_cv_t<_Tp>>) {
+    return;
+
+    // Otherwise, if T is an array type ([dcl.array]) and remove_all_extents_t<T> is an incomplete type,
+    // ranges​::​begin(E) is ill-formed with no diagnostic required.
+  } else if constexpr (is_array_v<_Tp>) {
+    if constexpr (!requires { sizeof(remove_all_extents_t<_Tp>); })
+      return;
+    else
+      // Otherwise, if T is an array type, ranges​::​begin(E) is expression-equivalent to t + 0.
+      return [](_Ep __v) noexcept { return __v + 0; };
+
+    // Otherwise, if auto(t.begin()) is a valid expression whose type models input_or_output_iterator,
+    // ranges​::​begin(E) is expression-equivalent to auto(t.begin()).
+  } else if constexpr (requires(_Tp& __t) {
+                         { _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(__v.begin()))) { return _LIBCPP_AUTO_CAST(__v.begin()); };
+
+    // Otherwise, if T is a class or enumeration type and auto(begin(t)) is a valid expression whose type models
+    // input_or_output_iterator where the meaning of begin is established as-if by performing argument-dependent lookup
+    // only ([basic.lookup.argdep]), then ranges​::​begin(E) is expression-equivalent to that expression.
+  } else if constexpr (__class_or_enum<_Tp>) {
+    if constexpr (requires(_Tp& __t) {
+                    { _LIBCPP_AUTO_CAST(begin(__t)) } -> input_or_output_iterator;
+                  })
+      return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(begin(__v)))) { return _LIBCPP_AUTO_CAST(begin(__v)); };
+    else
+      return;
+
+    // Otherwise, ranges​::​begin(E) is ill-formed.
+  } else {
+    return;
   }
-
-  template <class _Tp, size_t _Np>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept
-    requires(sizeof(_Tp) >= 0) // Disallow incomplete element types.
-  {
-    return __t + 0;
-  }
-
-  template <class _Tp>
-    requires __member_begin<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.begin()))) {
-    return _LIBCPP_AUTO_CAST(__t.begin());
-  }
-
-  template <class _Tp>
-    requires __unqualified_begin<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(begin(__t)))) {
-    return _LIBCPP_AUTO_CAST(begin(__t));
-  }
-
-  void operator()(auto&&) const = delete;
-};
+}> {};
 } // namespace __begin
 
 inline namespace __cpo {
@@ -102,45 +104,53 @@ using iterator_t = decltype(ranges::begin(std::declval<_Tp&>()));
 
 namespace ranges {
 namespace __end {
-template <class _Tp>
-concept __member_end = __can_borrow<_Tp> && requires(_Tp&& __t) {
-  typename iterator_t<_Tp>;
-  { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for<iterator_t<_Tp>>;
-};
-
-void end() = delete;
-
-template <class _Tp>
-concept __unqualified_end =
-    !__member_end<_Tp> && __can_borrow<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      typename iterator_t<_Tp>;
-      { _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for<iterator_t<_Tp>>;
-    };
-
-struct __fn {
-  template <class _Tp, size_t _Np>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept
-    requires(sizeof(_Tp) >= 0) // Disallow incomplete element types.
-  {
-    return __t + _Np;
-  }
-
-  template <class _Tp>
-    requires __member_end<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.end()))) {
-    return _LIBCPP_AUTO_CAST(__t.end());
+struct __fn : _CPO<[]<class _Ep> consteval noexcept {
+  // [range.access.end]
+
+  // Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. Then:
+  using _Tp = remove_reference_t<_Ep>;
+
+  // If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false, ranges​::​end(E) is ill-formed.
+  if constexpr (is_rvalue_reference_v<_Ep> && !enable_borrowed_range<remove_cv_t<_Tp>>) {
+    return;
+
+    // Otherwise, if T is an array type ([dcl.array]) and remove_all_extents_t<T> is an incomplete type,
+    // ranges​::​end(E) is ill-formed with no diagnostic required.
+  } else if constexpr (is_array_v<_Tp>) {
+    if constexpr (!requires { sizeof(remove_all_extents_t<_Tp>); }) {
+      return;
+
+      // Otherwise, if T is an array of unknown bound, ranges​::​end(E) is ill-formed.
+    } else if constexpr (is_unbounded_array_v<_Tp>) {
+      return;
+
+      // Otherwise, if T is an array, ranges​::​end(E) is expression-equivalent to t + extent_v<T>.
+    } else {
+      return [](_Ep __v) noexcept { return __v + extent_v<_Tp>; };
+    }
+
+    // Otherwise, if auto(t.end()) is a valid expression whose type models sentinel_for<iterator_t<T>> then
+    // ranges​::​end(E) is expression-equivalent to auto(t.end()).
+  } else if constexpr (requires(_Tp& __t) {
+                         { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for<iterator_t<_Tp>>;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(__v.end()))) { return _LIBCPP_AUTO_CAST(__v.end()); };
+
+    // Otherwise, if T is a class or enumeration type and auto(end(t)) is a valid expression whose type models
+    // sentinel_for<iterator_t<T>> where the meaning of end is established as-if by performing argument-dependent lookup
+    // only ([basic.lookup.argdep]), then ranges​::​end(E) is expression-equivalent to that expression.
+  } else if constexpr (__class_or_enum<_Tp>) {
+    if constexpr (requires(_Tp& __t) {
+                    { _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for<iterator_t<_Tp>>;
+                  }) {
+      return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(end(__v)))) { return _LIBCPP_AUTO_CAST(end(__v)); };
+    } else {
+      return;
+    }
+  } else {
+    return;
   }
-
-  template <class _Tp>
-    requires __unqualified_end<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(end(__t)))) {
-    return _LIBCPP_AUTO_CAST(end(__t));
-  }
-
-  void operator()(auto&&) const = delete;
-};
+}> {};
 } // namespace __end
 
 inline namespace __cpo {
@@ -191,8 +201,9 @@ struct __fn {
 
   template <class _Tp>
     requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
-      noexcept(ranges::end(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::end(static_cast<const _Tp&&>(__t))))
+          -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
     return ranges::end(static_cast<const _Tp&&>(__t));
   }
 };
diff --git a/libcxx/include/__ranges/data.h b/libcxx/include/__ranges/data.h
index 354a0aa15903e..66e057d645573 100644
--- a/libcxx/include/__ranges/data.h
+++ b/libcxx/include/__ranges/data.h
@@ -10,19 +10,21 @@
 #ifndef _LIBCPP___RANGES_DATA_H
 #define _LIBCPP___RANGES_DATA_H
 
-#include <__concepts/class_or_enum.h>
 #include <__config>
 #include <__iterator/concepts.h>
-#include <__iterator/iterator_traits.h>
 #include <__memory/pointer_traits.h>
 #include <__ranges/access.h>
-#include <__type_traits/decay.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_reference.h>
+#include <__type_traits/remove_all_extents.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
 #include <__utility/auto_cast.h>
+#include <__utility/cpo.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -49,18 +51,47 @@ concept __ranges_begin_invocable = !__member_data<_Tp> && __can_borrow<_Tp> && r
   { ranges::begin(__t) } -> contiguous_iterator;
 };
 
-struct __fn {
-  template <__member_data _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(noexcept(__t.data())) {
-    return __t.data();
-  }
-
-  template <__ranges_begin_invocable _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(std::to_address(ranges::begin(__t)))) {
-    return std::to_address(ranges::begin(__t));
+struct __fn : _CPO<[]<class _Ep> consteval noexcept {
+  // [range.prim.data]
+
+  // Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. Then:
+  using _Tp = remove_reference_t<_Ep>;
+
+  // If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false, ranges​::​data(E) is ill-formed.
+  if constexpr (is_rvalue_reference_v<_Ep> && !enable_borrowed_range<remove_cv_t<_Tp>>) {
+    return;
+
+    // Otherwise, if T is an array type ([dcl.array]) and remove_all_extents_t<T> is an incomplete type,
+    // ranges​::​data(E) is ill-formed with no diagnostic required.
+  } else if constexpr (is_array_v<_Tp>) {
+    if constexpr (!requires { sizeof(remove_all_extents_t<_Tp>); }) {
+      return;
+    } else {
+      // This is inlined from ranges::begin
+      return [](_Ep __v) noexcept { return __v + 0; };
+    }
+
+    // Otherwise, if auto(t.data()) is a valid expression of pointer to object type,
+    // ranges​::​data(E) is expression-equivalent to auto(t.data()).
+  } else if constexpr (requires(_Tp& __t) {
+                         { _LIBCPP_AUTO_CAST(__t.data()) } -> __ptr_to_object;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(__v.data()))) { return _LIBCPP_AUTO_CAST(__v.data()); };
+
+    // Otherwise, if ranges​::​begin(t) is a valid expression whose type models contiguous_iterator,
+    // ranges​::​data(E) is expression-equivalent to to_address(ranges​::​begin(t)).
+  } else if constexpr (requires(_Tp& __t) {
+                         { ranges::begin(__t) } -> contiguous_iterator;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(std::to_address(ranges::begin(__v)))) {
+      return std::to_address(ranges::begin(__v));
+    };
+
+    // Otherwise, ranges​::​data(E) is ill-formed.
+  } else {
+    return;
   }
-};
+}> {};
 } // namespace __data
 
 inline namespace __cpo {
@@ -83,8 +114,9 @@ struct __fn {
 
   template <class _Tp>
     requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
-      noexcept(ranges::data(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::data(static_cast<const _Tp&&>(__t))) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::data(static_cast<const _Tp&&>(__t))))
+          -> decltype(ranges::data(static_cast<const _Tp&&>(__t))) {
     return ranges::data(static_cast<const _Tp&&>(__t));
   }
 };
diff --git a/libcxx/include/__utility/cpo.h b/libcxx/include/__utility/cpo.h
new file mode 100644
index 0000000000000..81dc04358bfa4
--- /dev/null
+++ b/libcxx/include/__utility/cpo.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_CPO_H
+#define _LIBCPP___UTILITY_CPO_H
+
+#include <__config>
+#include <__utility/forward.h>
+
+#if _LIBCPP_STD_VER >= 20
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <auto __resolver>
+struct _CPO {
+  template <class... _Args>
+  [[nodiscard]] static constexpr auto operator()(_Args&&... __args) noexcept(
+      noexcept(__resolver.template operator()<_Args&&...>()(std::forward<_Args>(__args)...)))
+      -> decltype(__resolver.template operator()<_Args&&...>()(std::forward<_Args>(__args)...)) {
+    return __resolver.template operator()<_Args&&...>()(std::forward<_Args>(__args)...);
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+
+#endif // _LIBCPP___UTILITY_CPO_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..df9777b470916 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2207,6 +2207,7 @@ module std {
     module cmp                             { header "__utility/cmp.h" }
     module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
+    module cpo                             { header "__utility/cpo.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
     module element_count                   { header "__utility/element_count.h" }
     module exception_guard                 { header "__utility/exception_guard.h" }
diff --git a/libcxx/test/std/ranges/range.access/begin.verify.cpp b/libcxx/test/std/ranges/range.access/begin.verify.cpp
index 433404b4ad277..df6fce0402215 100644
--- a/libcxx/test/std/ranges/range.access/begin.verify.cpp
+++ b/libcxx/test/std/ranges/range.access/begin.verify.cpp
@@ -20,7 +20,5 @@ static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
 
 // Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::begin` is ill-formed.
 void test() {
-  std::ranges::begin(NonBorrowedRange());
-  // expected-error-re at -1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__begin::__fn'}}}}
-  // expected-error at -2  {{attempt to use a deleted function}}
+  std::ranges::begin(NonBorrowedRange()); // expected-error {{no matching function for call to object of type 'const __begin::__fn'}}
 }
diff --git a/libcxx/test/std/ranges/range.access/end.verify.cpp b/libcxx/test/std/ranges/range.access/end.verify.cpp
index a4a7f78b8c6b1..30cbd85243f24 100644
--- a/libcxx/test/std/ranges/range.access/end.verify.cpp
+++ b/libcxx/test/std/ranges/range.access/end.verify.cpp
@@ -20,7 +20,5 @@ static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
 
 // Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::end` is ill-formed.
 void test() {
-  std::ranges::end(NonBorrowedRange());
-  // expected-error-re at -1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__end::__fn'}}}}
-  // expected-error at -2  {{attempt to use a deleted function}}
+  std::ranges::end(NonBorrowedRange()); // expected-error {{no matching function for call to object of type 'const __end::__fn'}}
 }


From libcxx-commits at lists.llvm.org  Mon Jul 13 02:13:24 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 02:13:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Clean up windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a54ac34.6365b89b.8575e.6bd4@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207577

>From d680145c3c5148d1880c4f73df5961cbe7bb6a42 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 5 Jul 2026 12:01:53 +0200
Subject: [PATCH] [libc++] Cleanup windows macros

---
 libcxx/include/__config                       | 11 ++----
 libcxx/include/__filesystem/path.h            | 34 ++++++++---------
 libcxx/include/__filesystem/u8path.h          | 14 +++----
 libcxx/include/__locale                       |  2 +-
 libcxx/include/__locale_dir/locale_base_api.h |  2 +-
 libcxx/include/__math/gamma.h                 |  2 +-
 libcxx/include/__ostream/print.h              |  4 +-
 libcxx/include/fstream                        | 10 ++---
 libcxx/include/print                          | 12 +++---
 libcxx/include/text_encoding                  |  2 +-
 libcxx/include/wchar.h                        |  2 +-
 libcxx/src/chrono.cpp                         |  8 ++--
 libcxx/src/filesystem/directory_iterator.cpp  |  4 +-
 libcxx/src/filesystem/error.h                 |  4 +-
 libcxx/src/filesystem/file_descriptor.h       | 14 +++----
 libcxx/src/filesystem/filesystem_clock.cpp    |  6 +--
 libcxx/src/filesystem/format_string.h         |  2 +-
 libcxx/src/filesystem/operations.cpp          | 14 +++----
 libcxx/src/filesystem/path.cpp                |  2 +-
 libcxx/src/filesystem/path_parser.h           |  4 +-
 libcxx/src/filesystem/posix_compat.h          |  6 +--
 libcxx/src/filesystem/time_utils.h            | 10 ++---
 libcxx/src/fstream.cpp                        |  4 +-
 libcxx/src/include/aligned_alloc.h            |  4 +-
 libcxx/src/include/config_elast.h             |  4 +-
 libcxx/src/iostream.cpp                       |  2 +-
 libcxx/src/locale.cpp                         | 38 +++++++++----------
 libcxx/src/print.cpp                          |  8 ++--
 libcxx/src/std_stream.h                       |  4 +-
 libcxx/src/system_error.cpp                   | 10 ++---
 libcxx/src/thread.cpp                         |  4 +-
 libcxx/test/support/count_new.h               |  2 +-
 32 files changed, 122 insertions(+), 127 deletions(-)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..4f01a34bfa0a6 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -43,14 +43,9 @@
 #  endif
 
 #  if defined(_WIN32)
-#    define _LIBCPP_WIN32API
 #    define _LIBCPP_SHORT_WCHAR 1
-// Both MinGW and native MSVC provide a "MSVC"-like environment
-#    define _LIBCPP_MSVCRT_LIKE
-// If mingw not explicitly detected, assume using MS C runtime only if
-// a MS compatibility version is specified.
 #    if defined(_MSC_VER) && !defined(__MINGW32__)
-#      define _LIBCPP_MSVCRT // Using Microsoft's C Runtime library
+#      define _LIBCPP_MSVCRT
 #    endif
 #    define _LIBCPP_HAS_OPEN_WITH_WCHAR 1
 #  else
@@ -100,7 +95,7 @@
 #    define _LIBCPP_USING_GETENTROPY
 #  elif defined(__Fuchsia__)
 #    define _LIBCPP_USING_FUCHSIA_CPRNG
-#  elif defined(_LIBCPP_WIN32API)
+#  elif defined(_WIN32)
 #    define _LIBCPP_USING_WIN32_RANDOM
 #  else
 #    define _LIBCPP_USING_DEV_RANDOM
@@ -227,7 +222,7 @@ typedef __char32_t char32_t;
 // TODO(44575): Switch to C11 thread API when possible.
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#    elif defined(_LIBCPP_WIN32API)
+#    elif defined(_WIN32)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
 #    else
diff --git a/libcxx/include/__filesystem/path.h b/libcxx/include/__filesystem/path.h
index a63c4ee611ebf..f89c829505d61 100644
--- a/libcxx/include/__filesystem/path.h
+++ b/libcxx/include/__filesystem/path.h
@@ -80,7 +80,7 @@ struct __can_convert_char<char32_t> {
 
 template <class _ECharT, __enable_if_t<__can_convert_char<_ECharT>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI bool __is_separator(_ECharT __e) {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   return __e == _ECharT('/') || __e == _ECharT('\\');
 #  else
   return __e == _ECharT('/');
@@ -181,7 +181,7 @@ struct __is_pathable<_Tp, false, true, false> : __is_pathable_char_array<_Tp> {}
 template <class _Tp>
 struct __is_pathable<_Tp, false, false, true> : __is_pathable_iter<_Tp> {};
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 typedef wstring __path_string;
 typedef wchar_t __path_value;
 #  else
@@ -189,7 +189,7 @@ typedef string __path_string;
 typedef char __path_value;
 #  endif
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 _LIBCPP_EXPORTED_FROM_ABI size_t __wide_to_char(const wstring&, char*, size_t);
 _LIBCPP_EXPORTED_FROM_ABI size_t __char_to_wide(const string&, wchar_t*, size_t);
 #  endif
@@ -203,12 +203,12 @@ struct _PathCVT {
   static_assert(__can_convert_char<_ECharT>::value, "Char type not convertible");
 
   typedef __narrow_to_utf8<sizeof(_ECharT) * __CHAR_BIT__> _Narrower;
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
   typedef __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__> _Widener;
 #    endif
 
   _LIBCPP_HIDE_FROM_ABI static void __append_range(__path_string& __dest, _ECharT const* __b, _ECharT const* __e) {
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __b, __e);
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -223,7 +223,7 @@ struct _PathCVT {
     if (__b == __e)
       return;
     basic_string<_ECharT> __tmp(__b, __e);
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __tmp.data(), __tmp.data() + __tmp.length());
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -241,7 +241,7 @@ struct _PathCVT {
     basic_string<_ECharT> __tmp;
     for (; *__b != __sentinel; ++__b)
       __tmp.push_back(*__b);
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __tmp.data(), __tmp.data() + __tmp.length());
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -284,7 +284,7 @@ struct _PathCVT<__path_value> {
   }
 };
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 template <>
 struct _PathCVT<char> {
   _LIBCPP_HIDE_FROM_ABI static void __append_string(__path_string& __dest, const basic_string<char>& __str) {
@@ -369,7 +369,7 @@ struct _PathExport<char8_t> {
 };
 #      endif // _LIBCPP_HAS_CHAR8_T
 #    endif   // _LIBCPP_HAS_LOCALIZATION
-#  endif     // _LIBCPP_WIN32API
+#  endif     // _WIN32
 
 class _LIBCPP_EXPORTED_FROM_ABI path {
   template <class _SourceOrIter, class _Tp = path&>
@@ -382,7 +382,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   using _SourceCVT _LIBCPP_NODEBUG = _PathCVT<_SourceChar<_Tp> >;
 
 public:
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   typedef wchar_t value_type;
   static constexpr value_type preferred_separator = L'\\';
 #  else
@@ -468,7 +468,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
 
 public:
   // appends
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   _LIBCPP_HIDE_FROM_ABI path& operator/=(const path& __p) {
     auto __p_root_name      = __p.__root_name();
     auto __p_root_name_size = __p_root_name.size();
@@ -602,7 +602,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   _LIBCPP_HIDE_FROM_ABI void clear() noexcept { __pn_.clear(); }
 
   _LIBCPP_HIDE_FROM_ABI path& make_preferred() _LIBCPP_LIFETIMEBOUND {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     std::replace(__pn_.begin(), __pn_.end(), L'/', L'\\');
 #  endif
     return *this;
@@ -667,7 +667,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
 
   _LIBCPP_HIDE_FROM_ABI operator string_type() const { return __pn_; }
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::wstring wstring() const { return __pn_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::wstring generic_wstring() const {
@@ -722,7 +722,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
     return __s;
   }
 #    endif // _LIBCPP_HAS_LOCALIZATION
-#  else    /* _LIBCPP_WIN32API */
+#  else    // _WIN32
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::string string() const { return __pn_; }
 #    if _LIBCPP_HAS_CHAR8_T
@@ -775,7 +775,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::u16string generic_u16string() const { return string<char16_t>(); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::u32string generic_u32string() const { return string<char32_t>(); }
 #    endif // _LIBCPP_HAS_LOCALIZATION
-#  endif   /* !_LIBCPP_WIN32API */
+#  endif   // _WIN32
 
 private:
   int __compare(__string_view) const;
@@ -799,7 +799,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_name() const { return string_type(__root_name()); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_directory() const { return string_type(__root_directory()); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_path() const {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     return string_type(__root_path_raw());
 #  else
     return root_name().append(string_type(__root_directory()));
@@ -824,7 +824,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool has_extension() const { return !__extension().empty(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_absolute() const {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     __string_view __root_name_str = __root_name();
     __string_view __root_dir      = __root_directory();
     if (__root_name_str.size() == 2 && __root_name_str[1] == ':') {
diff --git a/libcxx/include/__filesystem/u8path.h b/libcxx/include/__filesystem/u8path.h
index aabd2bbd3c26c..77e7c0d6653da 100644
--- a/libcxx/include/__filesystem/u8path.h
+++ b/libcxx/include/__filesystem/u8path.h
@@ -24,7 +24,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
-#  if !defined(_LIBCPP_WIN32API) || _LIBCPP_HAS_LOCALIZATION
+#  if !defined(_WIN32) || _LIBCPP_HAS_LOCALIZATION
 template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(_InputIt __f, _InputIt __l) {
   static_assert(
@@ -34,7 +34,7 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
           is_same<typename __is_pathable<_InputIt>::__char_type, char>::value,
       "u8path(Iter, Iter) requires Iter have a value_type of type 'char'"
       " or 'char8_t'");
-#    if defined(_LIBCPP_WIN32API)
+#    if defined(_WIN32)
   string __tmp(__f, __l);
   using _CVT = __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__>;
   std::wstring __w;
@@ -43,11 +43,11 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
   return path(__w);
 #    else
   return path(__f, __l);
-#    endif // defined(_LIBCPP_WIN32API)
+#    endif // defined(_WIN32)
 }
-#  endif // !defined(_LIBCPP_WIN32API) || _LIBCPP_HAS_LOCALIZATION
+#  endif // !defined(_WIN32) || _LIBCPP_HAS_LOCALIZATION
 
-#  if defined(_LIBCPP_WIN32API) && _LIBCPP_HAS_LOCALIZATION
+#  if defined(_WIN32) && _LIBCPP_HAS_LOCALIZATION
 template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(_InputIt __f, _NullSentinel) {
   static_assert(
@@ -67,7 +67,7 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
   _CVT()(back_inserter(__w), __tmp.data(), __tmp.data() + __tmp.size());
   return path(__w);
 }
-#  endif // defined(_LIBCPP_WIN32API) && _LIBCPP_HAS_LOCALIZATION
+#  endif // defined(_WIN32) && _LIBCPP_HAS_LOCALIZATION
 
 template <class _Source, __enable_if_t<__is_pathable<_Source>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(const _Source& __s) {
@@ -78,7 +78,7 @@ template <class _Source, __enable_if_t<__is_pathable<_Source>::value, int> = 0>
           is_same<typename __is_pathable<_Source>::__char_type, char>::value,
       "u8path(Source const&) requires Source have a character type of type "
       "'char' or 'char8_t'");
-#  if defined(_LIBCPP_WIN32API)
+#  if defined(_WIN32)
   using _Traits = __is_pathable<_Source>;
   return u8path(std::__unwrap_iter(_Traits::__range_begin(__s)), std::__unwrap_iter(_Traits::__range_end(__s)));
 #  else
diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index bb8f8e7bbf5d9..e0524cf4abc4c 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -352,7 +352,7 @@ public:
 #    else
   static const mask __regex_word = 0x80;
 #    endif
-#  elif defined(_LIBCPP_MSVCRT_LIKE)
+#  elif defined(_WIN32)
   typedef unsigned short mask;
   static const mask space        = _SPACE;
   static const mask print        = _BLANK | _PUNCT | _ALPHA | _DIGIT;
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index f973d95ef244d..017d971ec72ba 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -116,7 +116,7 @@
 #    include <__locale_dir/support/netbsd.h>
 #  elif defined(__OpenBSD__)
 #    include <__locale_dir/support/openbsd.h>
-#  elif defined(_LIBCPP_MSVCRT_LIKE)
+#  elif defined(_WIN32)
 #    include <__locale_dir/support/windows.h>
 #  elif defined(__Fuchsia__)
 #    include <__locale_dir/support/fuchsia.h>
diff --git a/libcxx/include/__math/gamma.h b/libcxx/include/__math/gamma.h
index a742ea42d9009..08e09d7cb7aef 100644
--- a/libcxx/include/__math/gamma.h
+++ b/libcxx/include/__math/gamma.h
@@ -62,7 +62,7 @@ inline _LIBCPP_HIDE_FROM_ABI double tgamma(_A1 __x) _NOEXCEPT {
 // declare it differently in the first place: instead use `asm` to get the compiler to call the right
 // function.
 
-#if defined(_LIBCPP_MSVCRT_LIKE) // reentrant version is not available on Windows
+#ifdef _WIN32 // reentrant version is not available on Windows
 
 inline _LIBCPP_HIDE_FROM_ABI double __lgamma_r(double __d) _NOEXCEPT { return __builtin_lgamma(__d); }
 
diff --git a/libcxx/include/__ostream/print.h b/libcxx/include/__ostream/print.h
index 5b4ab60a45eea..4a519c177b990 100644
--- a/libcxx/include/__ostream/print.h
+++ b/libcxx/include/__ostream/print.h
@@ -89,7 +89,7 @@ _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 #    if _LIBCPP_HAS_UNICODE
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
-#      ifndef _LIBCPP_WIN32API
+#      ifndef _WIN32
   return std::__vprint_nonunicode(__os, __fmt, __args, __write_nl);
 #      else
   FILE* __file = std::__get_ostream_file(__os);
@@ -123,7 +123,7 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo
     __os.__set_badbit_and_consider_rethrow();
   }
 #        endif // _LIBCPP_HAS_EXCEPTIONS
-#      endif   // _LIBCPP_WIN32API
+#      endif   // _WIN32
 }
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..78fcdf254b493 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -222,7 +222,7 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#    if _LIBCPP_STD_VER >= 23 && defined(_LIBCPP_WIN32API)
+#    if _LIBCPP_STD_VER >= 23 && defined(_WIN32)
 _LIBCPP_EXPORTED_FROM_ABI void* __filebuf_windows_native_handle(FILE* __file) noexcept;
 #    endif
 
@@ -236,7 +236,7 @@ public:
   typedef typename traits_type::off_type off_type;
   typedef typename traits_type::state_type state_type;
 #    if _LIBCPP_STD_VER >= 26
-#      if defined(_LIBCPP_WIN32API)
+#      if defined(_WIN32)
   using native_handle_type = void*; // HANDLE
 #      elif __has_include(<unistd.h>)
   using native_handle_type = int; // POSIX file descriptor
@@ -272,7 +272,7 @@ public:
 #    if _LIBCPP_STD_VER >= 26
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() const noexcept {
     _LIBCPP_ASSERT_UNCATEGORIZED(this->is_open(), "File must be opened");
-#      if defined(_LIBCPP_WIN32API)
+#      if defined(_WIN32)
     return std::__filebuf_windows_native_handle(__file_);
 #      elif __has_include(<unistd.h>)
     return fileno(__file_);
@@ -1020,7 +1020,7 @@ basic_filebuf<_CharT, _Traits>::seekoff(off_type __off, ios_base::seekdir __way,
 
 template <class _CharT, class _Traits>
 int basic_filebuf<_CharT, _Traits>::__fseek(FILE* __file, pos_type __offset, int __whence) {
-#    if defined(_LIBCPP_MSVCRT_LIKE)
+#    ifdef _WIN32
   return _fseeki64(__file, __offset, __whence);
 #    elif _LIBCPP_LIBC_NEWLIB
   return fseek(__file, __offset, __whence);
@@ -1031,7 +1031,7 @@ int basic_filebuf<_CharT, _Traits>::__fseek(FILE* __file, pos_type __offset, int
 
 template <class _CharT, class _Traits>
 typename basic_filebuf<_CharT, _Traits>::pos_type basic_filebuf<_CharT, _Traits>::__ftell(FILE* __file) {
-#    if defined(_LIBCPP_MSVCRT_LIKE)
+#    ifdef _WIN32
   return _ftelli64(__file);
 #    elif _LIBCPP_LIBC_NEWLIB
   return ftell(__file);
diff --git a/libcxx/include/print b/libcxx/include/print
index cf62743846f42..a9c4ee686f5f9 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -56,7 +56,7 @@ namespace std {
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#    ifdef _LIBCPP_WIN32API
+#    ifdef _WIN32
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
 
@@ -73,7 +73,7 @@ _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
 _LIBCPP_EXPORTED_FROM_ABI void __write_to_windows_console(FILE* __stream, wstring_view __view);
 #      endif // _LIBCPP_HAS_WIDE_CHARACTERS
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
-#    endif   // _LIBCPP_WIN32API
+#    endif   // _WIN32
 
 #    if _LIBCPP_HAS_UNICODE
 // This is the code to transcode UTF-8 to UTF-16. This is used on
@@ -197,7 +197,7 @@ inline constexpr bool __use_unicode_execution_charset = _MSVC_EXECUTION_CHARACTE
 inline constexpr bool __use_unicode_execution_charset = true;
 #    endif
 
-#    ifdef _LIBCPP_WIN32API
+#    ifdef _WIN32
 _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream) {
   // The macro _LIBCPP_TESTING_PRINT_IS_TERMINAL is used to change
   // the behavior in the test. This is not part of the public API.
@@ -207,7 +207,7 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream)
   return std::__is_windows_terminal(__stream);
 #      endif
 }
-#    endif // _LIBCPP_WIN32API
+#    endif // _WIN32
 
 [[noreturn]] _LIBCPP_HIDE_FROM_ABI inline void __handle_output_error(FILE* __stream) {
   if (std::feof(__stream))
@@ -251,7 +251,7 @@ _LIBCPP_HIDE_FROM_ABI inline void __output_unicode_windows([[maybe_unused]] FILE
   // the behavior in the test. This is not part of the public API.
 #        ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
   _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION(__stream, __view);
-#        elif defined(_LIBCPP_WIN32API)
+#        elif defined(_WIN32)
   std::__write_to_windows_console(__stream, __view);
 #        else
   std::__throw_runtime_error("No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
@@ -282,7 +282,7 @@ _LIBCPP_HIDE_FROM_ABI inline void __output_unicode([[maybe_unused]] FILE* __stre
   // so there the call can be forwarded to the non_unicode API. On
   // Windows there is a different API. This API requires transcoding.
 
-#      ifndef _LIBCPP_WIN32API
+#      ifndef _WIN32
   __print::__output_nonunicode(__stream, __text);
 #      elif _LIBCPP_HAS_WIDE_CHARACTERS
   if (__print::__is_terminal(__stream)) {
diff --git a/libcxx/include/text_encoding b/libcxx/include/text_encoding
index da39be92c3dfa..c00fe0d278dc0 100644
--- a/libcxx/include/text_encoding
+++ b/libcxx/include/text_encoding
@@ -598,7 +598,7 @@ public:
 
 #    if _LIBCPP_HAS_LOCALIZATION
   [[nodiscard]] _LIBCPP_AVAILABILITY_TEXT_ENCODING_ENVIRONMENT static text_encoding environment() {
-#      if defined(_LIBCPP_WIN32API)
+#      ifdef _WIN32
     return __get_locale_encoding(nullptr);
 #      else
     return __get_locale_encoding("");
diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index 7ff03870bdfdb..e5844249bdf7e 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -195,7 +195,7 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD wchar_t* wmemchr(wchar_t
 }
 #      endif
 
-#      if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
+#      if defined(__cplusplus) && (defined(_WIN32) || defined(__MVS__))
 extern "C" {
 size_t mbsnrtowcs(
     wchar_t* __restrict __dst, const char** __restrict __src, size_t __nmc, size_t __len, mbstate_t* __restrict __ps);
diff --git a/libcxx/src/chrono.cpp b/libcxx/src/chrono.cpp
index af5fe7c01fb8b..30c1a1f119a92 100644
--- a/libcxx/src/chrono.cpp
+++ b/libcxx/src/chrono.cpp
@@ -41,14 +41,14 @@
 #  define _LIBCPP_HAS_CLOCK_GETTIME
 #endif
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define VC_EXTRA_LEAN
 #  include <windows.h>
 #  if _WIN32_WINNT >= _WIN32_WINNT_WIN8
 #    include <winapifamily.h>
 #  endif
-#endif // defined(_LIBCPP_WIN32API)
+#endif // _WIN32
 
 #if defined(__Fuchsia__)
 #  include <zircon/syscalls.h>
@@ -71,7 +71,7 @@ namespace chrono {
 // system_clock
 //
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 #  if _WIN32_WINNT < _WIN32_WINNT_WIN8
 
@@ -184,7 +184,7 @@ static steady_clock::time_point __libcpp_steady_clock_now() {
   return steady_clock::time_point(seconds(tp.tv_sec) + nanoseconds(tp.tv_nsec));
 }
 
-#  elif defined(_LIBCPP_WIN32API)
+#  elif defined(_WIN32)
 
 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx says:
 //    If the function fails, the return value is zero. <snip>
diff --git a/libcxx/src/filesystem/directory_iterator.cpp b/libcxx/src/filesystem/directory_iterator.cpp
index 5d682aee2b839..fd2bd504b221b 100644
--- a/libcxx/src/filesystem/directory_iterator.cpp
+++ b/libcxx/src/filesystem/directory_iterator.cpp
@@ -17,7 +17,7 @@
 #include "error.h"
 #include "file_descriptor.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -30,7 +30,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 using detail::ErrorHandler;
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 class __dir_stream {
 public:
   __dir_stream()                               = delete;
diff --git a/libcxx/src/filesystem/error.h b/libcxx/src/filesystem/error.h
index db5d1ae9a7250..ee1d148e65aa1 100644
--- a/libcxx/src/filesystem/error.h
+++ b/libcxx/src/filesystem/error.h
@@ -23,7 +23,7 @@
 
 #include "format_string.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h> // ERROR_* macros
@@ -42,7 +42,7 @@ inline error_code capture_errno() {
 }
 
 inline error_code get_last_error() {
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   return std::error_code(GetLastError(), std::system_category());
 #else
   return capture_errno();
diff --git a/libcxx/src/filesystem/file_descriptor.h b/libcxx/src/filesystem/file_descriptor.h
index 9c279c451f28c..b095f200912a4 100644
--- a/libcxx/src/filesystem/file_descriptor.h
+++ b/libcxx/src/filesystem/file_descriptor.h
@@ -20,7 +20,7 @@
 #include "posix_compat.h"
 #include "time_utils.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -30,13 +30,13 @@
 #  include <sys/stat.h>
 #  include <sys/statvfs.h>
 #  include <unistd.h>
-#endif // defined(_LIBCPP_WIN32API)
+#endif // _WIN32
 
 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 
-#if !defined(_LIBCPP_WIN32API)
+#ifndef _WIN32
 
 #  if defined(DT_BLK)
 template <class DirEntT, class = decltype(DirEntT::d_type)>
@@ -84,7 +84,7 @@ inline pair<string_view, file_type> posix_readdir(DIR* dir_stream, error_code& e
   }
 }
 
-#else // _LIBCPP_WIN32API
+#else // _WIN32
 
 inline file_type get_file_type(const WIN32_FIND_DATAW& data) {
   if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
@@ -111,7 +111,7 @@ inline perms get_file_perm(const WIN32_FIND_DATAW& data) {
   return static_cast<perms>(st_mode) & perms::mask;
 }
 
-#endif // !_LIBCPP_WIN32API
+#endif // _WIN32
 
 //                       POSIX HELPERS
 
@@ -128,7 +128,7 @@ struct FileDescriptor {
   static FileDescriptor create(const path* p, error_code& ec, Args... args) {
     ec.clear();
     int fd;
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
     // TODO: most of the filesystem implementation uses native Win32 calls
     // (mostly via posix_compat.h). However, here we use the C-runtime APIs to
     // open a file, because we subsequently pass the C-runtime fd to
@@ -171,7 +171,7 @@ struct FileDescriptor {
 
   void close() noexcept {
     if (fd != -1) {
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
       ::_close(fd);
 #else
       ::close(fd);
diff --git a/libcxx/src/filesystem/filesystem_clock.cpp b/libcxx/src/filesystem/filesystem_clock.cpp
index 96a1336d4cbca..0d360c51a7a37 100644
--- a/libcxx/src/filesystem/filesystem_clock.cpp
+++ b/libcxx/src/filesystem/filesystem_clock.cpp
@@ -14,11 +14,11 @@
 #include <ratio>
 #include <time.h>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  include "time_utils.h"
 #endif
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -51,7 +51,7 @@ _LIBCPP_DIAGNOSTIC_POP
 
 _FilesystemClock::time_point _FilesystemClock::now() noexcept {
   typedef chrono::duration<rep> __secs;
-#if defined(_LIBCPP_WIN32API)
+#if defined(_WIN32)
   typedef chrono::duration<rep, nano> __nsecs;
   FILETIME time;
   GetSystemTimeAsFileTime(&time);
diff --git a/libcxx/src/filesystem/format_string.h b/libcxx/src/filesystem/format_string.h
index 8d17b027a6e31..48bdad9a12030 100644
--- a/libcxx/src/filesystem/format_string.h
+++ b/libcxx/src/filesystem/format_string.h
@@ -18,7 +18,7 @@
 #include <cstdio>
 #include <string>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define PATHSTR(x) (L##x)
 #  define PATH_CSTR_FMT "\"%ls\""
 #else
diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp
index 3d358dd55e8b8..d6392ca144262 100644
--- a/libcxx/src/filesystem/operations.cpp
+++ b/libcxx/src/filesystem/operations.cpp
@@ -26,7 +26,7 @@
 #include "posix_compat.h"
 #include "time_utils.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -100,7 +100,7 @@ path __canonical(path const& orig_p, error_code* ec) {
   ErrorHandler<path> err("canonical", ec, &orig_p, &cwd);
 
   path p = __do_absolute(orig_p, &cwd, ec);
-#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API)
+#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_WIN32)
   std::unique_ptr<path::value_type, decltype(&::free)> hold(detail::realpath(p.c_str(), nullptr), &::free);
   if (hold.get() == nullptr)
     return err.report(detail::get_last_error());
@@ -460,7 +460,7 @@ void __copy_symlink(const path& existing_symlink, const path& new_symlink, error
   if (ec && *ec) {
     return;
   }
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   error_code local_ec;
   if (is_directory(real_path, local_ec))
     __create_directory_symlink(real_path, new_symlink, ec);
@@ -564,7 +564,7 @@ void __create_symlink(path const& from, path const& to, error_code* ec) {
 path __current_path(error_code* ec) {
   ErrorHandler<path> err("current_path", ec);
 
-#if defined(_LIBCPP_WIN32API) || defined(__GLIBC__) || defined(__APPLE__)
+#if defined(_WIN32) || defined(__GLIBC__) || defined(__APPLE__)
   // Common extension outside of POSIX getcwd() spec, without needing to
   // preallocate a buffer. Also supported by a number of other POSIX libcs.
   int size              = 0;
@@ -691,7 +691,7 @@ void __last_write_time(const path& p, file_time_type new_time, error_code* ec) {
   using detail::fs_time;
   ErrorHandler<void> err("last_write_time", ec, &p);
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   TimeSpec ts;
   if (!fs_time::convert_to_timespec(ts, new_time))
     return err.report(errc::value_too_large);
@@ -819,7 +819,7 @@ bool __remove(const path& p, error_code* ec) {
 //
 // The second implementation is used on platforms where `openat()` & friends are available,
 // and it threads file descriptors through recursive calls to avoid such race conditions.
-#if defined(_LIBCPP_WIN32API) || defined(__MVS__)
+#if defined(_WIN32) || defined(__MVS__)
 #  define REMOVE_ALL_USE_DIRECTORY_ITERATOR
 #endif
 
@@ -996,7 +996,7 @@ file_status __symlink_status(const path& p, error_code* ec) { return detail::pos
 path __temp_directory_path(error_code* ec) {
   ErrorHandler<path> err("temp_directory_path", ec);
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   wchar_t buf[MAX_PATH];
   DWORD retval = GetTempPathW(MAX_PATH, buf);
   if (!retval)
diff --git a/libcxx/src/filesystem/path.cpp b/libcxx/src/filesystem/path.cpp
index faa7b4f1fbab7..5790039f66426 100644
--- a/libcxx/src/filesystem/path.cpp
+++ b/libcxx/src/filesystem/path.cpp
@@ -417,7 +417,7 @@ path::iterator& path::iterator::__decrement() {
   return *this;
 }
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 ////////////////////////////////////////////////////////////////////////////
 // Windows path conversions
 size_t __wide_to_char(const wstring& str, char* out, size_t outlen) {
diff --git a/libcxx/src/filesystem/path_parser.h b/libcxx/src/filesystem/path_parser.h
index 5968f449235ba..ae91dc7f1cf26 100644
--- a/libcxx/src/filesystem/path_parser.h
+++ b/libcxx/src/filesystem/path_parser.h
@@ -22,7 +22,7 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 inline bool isSeparator(path::value_type C) {
   if (C == '/')
     return true;
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   if (C == '\\')
     return true;
 #endif
@@ -324,7 +324,7 @@ struct PathParser {
   }
 
   PosPtr consumeRootName(PosPtr P, PosPtr End) const noexcept {
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
     if (PosPtr Ret = consumeDriveLetter(P, End))
       return Ret;
     if (PosPtr Ret = consumeNetworkRoot(P, End))
diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h
index ddd99d8aaf206..f5119aebcbefb 100644
--- a/libcxx/src/filesystem/posix_compat.h
+++ b/libcxx/src/filesystem/posix_compat.h
@@ -31,7 +31,7 @@
 #include "error.h"
 #include "time_utils.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <io.h>
@@ -47,7 +47,7 @@
 #include <stdlib.h>
 #include <time.h>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 // This struct isn't defined in the normal Windows SDK, but only in the
 // Windows Driver Kit.
 struct LIBCPP_REPARSE_DATA_BUFFER {
@@ -81,7 +81,7 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 // Various C runtime header sets provide more or less of these. As we
 // provide our own implementation, undef all potential defines from the
diff --git a/libcxx/src/filesystem/time_utils.h b/libcxx/src/filesystem/time_utils.h
index 89352e5bd6abb..2b01ea9542586 100644
--- a/libcxx/src/filesystem/time_utils.h
+++ b/libcxx/src/filesystem/time_utils.h
@@ -22,7 +22,7 @@
 #include "error.h"
 #include "format_string.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -41,7 +41,7 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 // Various C runtime versions (UCRT, or the legacy msvcrt.dll used by
 // some mingw toolchains) provide different stat function implementations,
 // with a number of limitations with respect to what we want from the
@@ -267,7 +267,7 @@ struct time_util : time_util_base<FileTimeT, TimeT> {
   }
 };
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 using fs_time = time_util<file_time_type, int64_t, TimeSpec>;
 #else
 using fs_time = time_util<file_time_type, time_t, TimeSpec>;
@@ -301,7 +301,7 @@ inline TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
 
 #if _LIBCPP_HAS_FILESYSTEM
 
-#  if !defined(_LIBCPP_WIN32API)
+#  ifndef _WIN32
 inline bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS, error_code& ec) {
   TimeVal ConvertedTS[2] = {make_timeval(TS[0]), make_timeval(TS[1])};
   if (::utimes(p.c_str(), ConvertedTS) == -1) {
@@ -329,7 +329,7 @@ inline bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS, err
 #    endif
 }
 
-#  endif // !_LIBCPP_WIN32API
+#  endif // _WIN32
 
 inline file_time_type __extract_last_write_time(const path& p, const StatT& st, error_code* ec) {
   using detail::fs_time;
diff --git a/libcxx/src/fstream.cpp b/libcxx/src/fstream.cpp
index 2975088eb6255..12d4952293972 100644
--- a/libcxx/src/fstream.cpp
+++ b/libcxx/src/fstream.cpp
@@ -10,7 +10,7 @@
 #include <cstdio>
 #include <fstream>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <io.h>
@@ -20,7 +20,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 // Confirm that `HANDLE` is `void*` as implemented in `basic_filebuf`
 static_assert(std::same_as<HANDLE, void*>);
diff --git a/libcxx/src/include/aligned_alloc.h b/libcxx/src/include/aligned_alloc.h
index 24ca26ce04525..2b69a344006a3 100644
--- a/libcxx/src/include/aligned_alloc.h
+++ b/libcxx/src/include/aligned_alloc.h
@@ -27,7 +27,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 //
 // Returns the allocated memory, or `nullptr` on failure.
 inline _LIBCPP_HIDE_FROM_ABI void* __libcpp_aligned_alloc(std::size_t __alignment, std::size_t __size) {
-#  if defined(_LIBCPP_MSVCRT_LIKE)
+#  ifdef _WIN32
   return ::_aligned_malloc(__size, __alignment);
 
 // Android only provides aligned_alloc when targeting API 28 or higher.
@@ -51,7 +51,7 @@ inline _LIBCPP_HIDE_FROM_ABI void* __libcpp_aligned_alloc(std::size_t __alignmen
 }
 
 inline _LIBCPP_HIDE_FROM_ABI void __libcpp_aligned_free(void* __ptr) {
-#  if defined(_LIBCPP_MSVCRT_LIKE)
+#  ifdef _WIN32
   ::_aligned_free(__ptr);
 #  else
   ::free(__ptr);
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..8aa9355c4e7f3 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -11,7 +11,7 @@
 
 #include <__config>
 
-#if defined(_LIBCPP_MSVCRT_LIKE)
+#ifdef _WIN32
 #  include <stdlib.h>
 #else
 #  include <errno.h>
@@ -39,7 +39,7 @@
 // No _LIBCPP_ELAST needed on Apple
 #elif defined(__MVS__)
 #  define _LIBCPP_ELAST 1160
-#elif defined(_LIBCPP_MSVCRT_LIKE)
+#elif defined(_WIN32)
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..884585519af09 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -95,7 +95,7 @@ STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
 // the standard streams, otherwise it may not be alive during program termination
 // when we flush the streams.
 static void force_locale_initialization() {
-#if defined(_LIBCPP_MSVCRT_LIKE)
+#ifdef _WIN32
   static bool once = []() {
     auto loc = __locale::__newlocale(_LIBCPP_ALL_MASK, "C", 0);
     {
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index b0a69ab74f94c..28f1970f56b4d 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -905,7 +905,7 @@ const ctype<char>::mask* ctype<char>::classic_table() noexcept {
   return _C_ctype_tab_ + 1;
 #  elif defined(__GLIBC__)
   return _LIBCPP_GET_C_LOCALE->__ctype_b;
-#  elif defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  elif defined(_WIN32)
   return __pctype_func();
 #  elif defined(__EMSCRIPTEN__)
   return *__ctype_b_loc();
@@ -5360,19 +5360,19 @@ void moneypunct_byname<char, true>::init(const char* nm) {
     __frac_digits_ = lc->int_frac_digits;
   else
     __frac_digits_ = base::do_frac_digits();
-#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#ifdef _WIN32
   if (lc->p_sign_posn == 0)
-#else  // _LIBCPP_MSVCRT
+#else
   if (lc->int_p_sign_posn == 0)
-#endif // !_LIBCPP_MSVCRT
+#endif
     __positive_sign_ = "()";
   else
     __positive_sign_ = lc->positive_sign;
-#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#ifdef _WIN32
   if (lc->n_sign_posn == 0)
-#else  // _LIBCPP_MSVCRT
+#else
   if (lc->int_n_sign_posn == 0)
-#endif // !_LIBCPP_MSVCRT
+#endif
     __negative_sign_ = "()";
   else
     __negative_sign_ = lc->negative_sign;
@@ -5380,10 +5380,10 @@ void moneypunct_byname<char, true>::init(const char* nm) {
   // the same places in curr_symbol since there's no way to
   // represent anything else.
   string_type __dummy_curr_symbol = __curr_symbol_;
-#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#ifdef _WIN32
   __init_pat(__pos_format_, __dummy_curr_symbol, true, lc->p_cs_precedes, lc->p_sep_by_space, lc->p_sign_posn, ' ');
   __init_pat(__neg_format_, __curr_symbol_, true, lc->n_cs_precedes, lc->n_sep_by_space, lc->n_sign_posn, ' ');
-#else  // _LIBCPP_MSVCRT
+#else  // _WIN32
   __init_pat(
       __pos_format_,
       __dummy_curr_symbol,
@@ -5394,7 +5394,7 @@ void moneypunct_byname<char, true>::init(const char* nm) {
       ' ');
   __init_pat(
       __neg_format_, __curr_symbol_, true, lc->int_n_cs_precedes, lc->int_n_sep_by_space, lc->int_n_sign_posn, ' ');
-#endif // !_LIBCPP_MSVCRT
+#endif // _WIN32
 }
 
 #if _LIBCPP_HAS_WIDE_CHARACTERS
@@ -5477,11 +5477,11 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
     __frac_digits_ = lc->int_frac_digits;
   else
     __frac_digits_ = base::do_frac_digits();
-#  if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  ifdef _WIN32
   if (lc->p_sign_posn == 0)
-#  else  // _LIBCPP_MSVCRT
+#  else
   if (lc->int_p_sign_posn == 0)
-#  endif // !_LIBCPP_MSVCRT
+#  endif
     __positive_sign_ = L"()";
   else {
     mb = mbstate_t();
@@ -5492,11 +5492,11 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
     wbe = wbuf + j;
     __positive_sign_.assign(wbuf, wbe);
   }
-#  if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  ifdef _WIN32
   if (lc->n_sign_posn == 0)
-#  else  // _LIBCPP_MSVCRT
+#  else
   if (lc->int_n_sign_posn == 0)
-#  endif // !_LIBCPP_MSVCRT
+#  endif
     __negative_sign_ = L"()";
   else {
     mb = mbstate_t();
@@ -5511,10 +5511,10 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
   // the same places in curr_symbol since there's no way to
   // represent anything else.
   string_type __dummy_curr_symbol = __curr_symbol_;
-#  if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  ifdef _WIN32
   __init_pat(__pos_format_, __dummy_curr_symbol, true, lc->p_cs_precedes, lc->p_sep_by_space, lc->p_sign_posn, L' ');
   __init_pat(__neg_format_, __curr_symbol_, true, lc->n_cs_precedes, lc->n_sep_by_space, lc->n_sign_posn, L' ');
-#  else  // _LIBCPP_MSVCRT
+#  else  // _WIN32
   __init_pat(
       __pos_format_,
       __dummy_curr_symbol,
@@ -5525,7 +5525,7 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
       L' ');
   __init_pat(
       __neg_format_, __curr_symbol_, true, lc->int_n_cs_precedes, lc->int_n_sep_by_space, lc->int_n_sign_posn, L' ');
-#  endif // !_LIBCPP_MSVCRT
+#  endif // _WIN32
 }
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
diff --git a/libcxx/src/print.cpp b/libcxx/src/print.cpp
index 21d868e8c6a7a..2eb8a0f31488c 100644
--- a/libcxx/src/print.cpp
+++ b/libcxx/src/print.cpp
@@ -15,7 +15,7 @@
 
 #include "filesystem/error.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <io.h>
@@ -35,7 +35,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream) {
   // Note the Standard does this in one call, but it's unclear whether
@@ -65,13 +65,13 @@ __write_to_windows_console([[maybe_unused]] FILE* __stream, [[maybe_unused]] wst
 }
 #  endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
-#elif defined(HAS_FILENO_AND_ISATTY) && _LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 23 // !_LIBCPP_WIN32API
+#elif defined(HAS_FILENO_AND_ISATTY) && _LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 23
 
 _LIBCPP_DIAGNOSTIC_PUSH
 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmissing-prototypes")
 _LIBCPP_EXPORTED_FROM_ABI bool __is_posix_terminal(FILE* __stream) { return isatty(fileno(__stream)); }
 _LIBCPP_DIAGNOSTIC_POP
-#endif
+#endif // _WIN32
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/std_stream.h b/libcxx/src/std_stream.h
index 4b9d3a34b2441..32f23e78d4e69 100644
--- a/libcxx/src/std_stream.h
+++ b/libcxx/src/std_stream.h
@@ -57,7 +57,7 @@ class _LIBCPP_HIDDEN __stdinbuf : public basic_streambuf<_CharT, char_traits<_Ch
   bool __last_consumed_is_next_;
   bool __always_noconv_;
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   static constexpr bool __is_win32api_wide_char = !is_same_v<_CharT, char>;
 #else
   static constexpr bool __is_win32api_wide_char = false;
@@ -262,7 +262,7 @@ class _LIBCPP_HIDDEN __stdoutbuf : public basic_streambuf<_CharT, char_traits<_C
   state_type* __st_;
   bool __always_noconv_;
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   static constexpr bool __is_win32api_wide_char = !is_same_v<_CharT, char>;
 #else
   static constexpr bool __is_win32api_wide_char = false;
diff --git a/libcxx/src/system_error.cpp b/libcxx/src/system_error.cpp
index cf3d2c2926861..c6a38b5bacba9 100644
--- a/libcxx/src/system_error.cpp
+++ b/libcxx/src/system_error.cpp
@@ -21,7 +21,7 @@
 
 #include "include/config_elast.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  include <windows.h>
 #  include <winerror.h>
 #endif
@@ -29,7 +29,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 namespace {
 std::optional<errc> __win_err_to_errc(int err) {
@@ -149,7 +149,7 @@ constexpr size_t strerror_buff_size = 1024;
 
 string do_strerror_r(int ev);
 
-#  if defined(_LIBCPP_MSVCRT_LIKE)
+#  ifdef _WIN32
 string do_strerror_r(int ev) {
   char buffer[strerror_buff_size];
   if (::strerror_s(buffer, strerror_buff_size, ev) == 0)
@@ -272,7 +272,7 @@ class _LIBCPP_HIDDEN __system_error_category : public __do_message {
 const char* __system_error_category::name() const noexcept { return "system"; }
 
 string __system_error_category::message(int ev) const {
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
   std::string result;
   char* str               = nullptr;
   unsigned long num_chars = ::FormatMessageA(
@@ -304,7 +304,7 @@ string __system_error_category::message(int ev) const {
 }
 
 error_condition __system_error_category::default_error_condition(int ev) const noexcept {
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
   // Remap windows error codes to generic error codes if possible.
   if (ev == 0)
     return error_condition(0, generic_category());
diff --git a/libcxx/src/thread.cpp b/libcxx/src/thread.cpp
index d2aff1ee0c3fe..7b0890858e9d6 100644
--- a/libcxx/src/thread.cpp
+++ b/libcxx/src/thread.cpp
@@ -24,7 +24,7 @@
 #  pragma weak pthread_create // Do not create libpthread dependency
 #endif
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  include <windows.h>
 #endif
 
@@ -74,7 +74,7 @@ unsigned thread::hardware_concurrency() noexcept {
   if (result < 0)
     return 0;
   return static_cast<unsigned>(result);
-#elif defined(_LIBCPP_WIN32API)
+#elif defined(_WIN32)
   return static_cast<unsigned>(GetActiveProcessorCount(ALL_PROCESSOR_GROUPS));
 #else // defined(CTL_HW) && defined(HW_NCPU)
   // TODO: grovel through /proc or check cpuid on x86 and similar
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..0966e7dace413 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -450,7 +450,7 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
 }
 
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
-#    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
+#    ifdef _WIN32
 #      define USE_ALIGNED_ALLOC
 #    endif
 


From libcxx-commits at lists.llvm.org  Mon Jul 13 02:14:50 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 02:14:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Clean up windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a54ac8a.a1accbb4.244acd.0702@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207577

>From 3dc08c883c24bce2f72ff5a53191a0cc4e22989b Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 5 Jul 2026 12:01:53 +0200
Subject: [PATCH] [libc++] Cleanup windows macros

---
 libcxx/include/__config                       |  9 +----
 libcxx/include/__filesystem/path.h            | 34 ++++++++---------
 libcxx/include/__filesystem/u8path.h          | 14 +++----
 libcxx/include/__locale                       |  2 +-
 libcxx/include/__locale_dir/locale_base_api.h |  2 +-
 libcxx/include/__math/gamma.h                 |  2 +-
 libcxx/include/__ostream/print.h              |  4 +-
 libcxx/include/fstream                        | 10 ++---
 libcxx/include/print                          | 12 +++---
 libcxx/include/text_encoding                  |  2 +-
 libcxx/include/wchar.h                        |  2 +-
 libcxx/src/chrono.cpp                         |  8 ++--
 libcxx/src/filesystem/directory_iterator.cpp  |  4 +-
 libcxx/src/filesystem/error.h                 |  4 +-
 libcxx/src/filesystem/file_descriptor.h       | 14 +++----
 libcxx/src/filesystem/filesystem_clock.cpp    |  6 +--
 libcxx/src/filesystem/format_string.h         |  2 +-
 libcxx/src/filesystem/operations.cpp          | 14 +++----
 libcxx/src/filesystem/path.cpp                |  2 +-
 libcxx/src/filesystem/path_parser.h           |  4 +-
 libcxx/src/filesystem/posix_compat.h          |  6 +--
 libcxx/src/filesystem/time_utils.h            | 10 ++---
 libcxx/src/fstream.cpp                        |  4 +-
 libcxx/src/include/aligned_alloc.h            |  4 +-
 libcxx/src/include/config_elast.h             |  4 +-
 libcxx/src/iostream.cpp                       |  2 +-
 libcxx/src/locale.cpp                         | 38 +++++++++----------
 libcxx/src/print.cpp                          |  8 ++--
 libcxx/src/std_stream.h                       |  4 +-
 libcxx/src/system_error.cpp                   | 10 ++---
 libcxx/src/thread.cpp                         |  4 +-
 libcxx/test/support/count_new.h               |  2 +-
 32 files changed, 121 insertions(+), 126 deletions(-)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..3b716be7c8a95 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -43,12 +43,7 @@
 #  endif
 
 #  if defined(_WIN32)
-#    define _LIBCPP_WIN32API
 #    define _LIBCPP_SHORT_WCHAR 1
-// Both MinGW and native MSVC provide a "MSVC"-like environment
-#    define _LIBCPP_MSVCRT_LIKE
-// If mingw not explicitly detected, assume using MS C runtime only if
-// a MS compatibility version is specified.
 #    if defined(_MSC_VER) && !defined(__MINGW32__)
 #      define _LIBCPP_MSVCRT // Using Microsoft's C Runtime library
 #    endif
@@ -100,7 +95,7 @@
 #    define _LIBCPP_USING_GETENTROPY
 #  elif defined(__Fuchsia__)
 #    define _LIBCPP_USING_FUCHSIA_CPRNG
-#  elif defined(_LIBCPP_WIN32API)
+#  elif defined(_WIN32)
 #    define _LIBCPP_USING_WIN32_RANDOM
 #  else
 #    define _LIBCPP_USING_DEV_RANDOM
@@ -227,7 +222,7 @@ typedef __char32_t char32_t;
 // TODO(44575): Switch to C11 thread API when possible.
 #      undef _LIBCPP_HAS_THREAD_API_PTHREAD
 #      define _LIBCPP_HAS_THREAD_API_PTHREAD 1
-#    elif defined(_LIBCPP_WIN32API)
+#    elif defined(_WIN32)
 #      undef _LIBCPP_HAS_THREAD_API_WIN32
 #      define _LIBCPP_HAS_THREAD_API_WIN32 1
 #    else
diff --git a/libcxx/include/__filesystem/path.h b/libcxx/include/__filesystem/path.h
index a63c4ee611ebf..f89c829505d61 100644
--- a/libcxx/include/__filesystem/path.h
+++ b/libcxx/include/__filesystem/path.h
@@ -80,7 +80,7 @@ struct __can_convert_char<char32_t> {
 
 template <class _ECharT, __enable_if_t<__can_convert_char<_ECharT>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI bool __is_separator(_ECharT __e) {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   return __e == _ECharT('/') || __e == _ECharT('\\');
 #  else
   return __e == _ECharT('/');
@@ -181,7 +181,7 @@ struct __is_pathable<_Tp, false, true, false> : __is_pathable_char_array<_Tp> {}
 template <class _Tp>
 struct __is_pathable<_Tp, false, false, true> : __is_pathable_iter<_Tp> {};
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 typedef wstring __path_string;
 typedef wchar_t __path_value;
 #  else
@@ -189,7 +189,7 @@ typedef string __path_string;
 typedef char __path_value;
 #  endif
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 _LIBCPP_EXPORTED_FROM_ABI size_t __wide_to_char(const wstring&, char*, size_t);
 _LIBCPP_EXPORTED_FROM_ABI size_t __char_to_wide(const string&, wchar_t*, size_t);
 #  endif
@@ -203,12 +203,12 @@ struct _PathCVT {
   static_assert(__can_convert_char<_ECharT>::value, "Char type not convertible");
 
   typedef __narrow_to_utf8<sizeof(_ECharT) * __CHAR_BIT__> _Narrower;
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
   typedef __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__> _Widener;
 #    endif
 
   _LIBCPP_HIDE_FROM_ABI static void __append_range(__path_string& __dest, _ECharT const* __b, _ECharT const* __e) {
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __b, __e);
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -223,7 +223,7 @@ struct _PathCVT {
     if (__b == __e)
       return;
     basic_string<_ECharT> __tmp(__b, __e);
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __tmp.data(), __tmp.data() + __tmp.length());
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -241,7 +241,7 @@ struct _PathCVT {
     basic_string<_ECharT> __tmp;
     for (; *__b != __sentinel; ++__b)
       __tmp.push_back(*__b);
-#    if defined(_LIBCPP_WIN32API)
+#    ifdef _WIN32
     string __utf8;
     _Narrower()(back_inserter(__utf8), __tmp.data(), __tmp.data() + __tmp.length());
     _Widener()(back_inserter(__dest), __utf8.data(), __utf8.data() + __utf8.size());
@@ -284,7 +284,7 @@ struct _PathCVT<__path_value> {
   }
 };
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
 template <>
 struct _PathCVT<char> {
   _LIBCPP_HIDE_FROM_ABI static void __append_string(__path_string& __dest, const basic_string<char>& __str) {
@@ -369,7 +369,7 @@ struct _PathExport<char8_t> {
 };
 #      endif // _LIBCPP_HAS_CHAR8_T
 #    endif   // _LIBCPP_HAS_LOCALIZATION
-#  endif     // _LIBCPP_WIN32API
+#  endif     // _WIN32
 
 class _LIBCPP_EXPORTED_FROM_ABI path {
   template <class _SourceOrIter, class _Tp = path&>
@@ -382,7 +382,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   using _SourceCVT _LIBCPP_NODEBUG = _PathCVT<_SourceChar<_Tp> >;
 
 public:
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   typedef wchar_t value_type;
   static constexpr value_type preferred_separator = L'\\';
 #  else
@@ -468,7 +468,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
 
 public:
   // appends
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   _LIBCPP_HIDE_FROM_ABI path& operator/=(const path& __p) {
     auto __p_root_name      = __p.__root_name();
     auto __p_root_name_size = __p_root_name.size();
@@ -602,7 +602,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   _LIBCPP_HIDE_FROM_ABI void clear() noexcept { __pn_.clear(); }
 
   _LIBCPP_HIDE_FROM_ABI path& make_preferred() _LIBCPP_LIFETIMEBOUND {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     std::replace(__pn_.begin(), __pn_.end(), L'/', L'\\');
 #  endif
     return *this;
@@ -667,7 +667,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
 
   _LIBCPP_HIDE_FROM_ABI operator string_type() const { return __pn_; }
 
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::wstring wstring() const { return __pn_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::wstring generic_wstring() const {
@@ -722,7 +722,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
     return __s;
   }
 #    endif // _LIBCPP_HAS_LOCALIZATION
-#  else    /* _LIBCPP_WIN32API */
+#  else    // _WIN32
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::string string() const { return __pn_; }
 #    if _LIBCPP_HAS_CHAR8_T
@@ -775,7 +775,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::u16string generic_u16string() const { return string<char16_t>(); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::u32string generic_u32string() const { return string<char32_t>(); }
 #    endif // _LIBCPP_HAS_LOCALIZATION
-#  endif   /* !_LIBCPP_WIN32API */
+#  endif   // _WIN32
 
 private:
   int __compare(__string_view) const;
@@ -799,7 +799,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_name() const { return string_type(__root_name()); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_directory() const { return string_type(__root_directory()); }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI path root_path() const {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     return string_type(__root_path_raw());
 #  else
     return root_name().append(string_type(__root_directory()));
@@ -824,7 +824,7 @@ class _LIBCPP_EXPORTED_FROM_ABI path {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool has_extension() const { return !__extension().empty(); }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_absolute() const {
-#  if defined(_LIBCPP_WIN32API)
+#  ifdef _WIN32
     __string_view __root_name_str = __root_name();
     __string_view __root_dir      = __root_directory();
     if (__root_name_str.size() == 2 && __root_name_str[1] == ':') {
diff --git a/libcxx/include/__filesystem/u8path.h b/libcxx/include/__filesystem/u8path.h
index aabd2bbd3c26c..77e7c0d6653da 100644
--- a/libcxx/include/__filesystem/u8path.h
+++ b/libcxx/include/__filesystem/u8path.h
@@ -24,7 +24,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
-#  if !defined(_LIBCPP_WIN32API) || _LIBCPP_HAS_LOCALIZATION
+#  if !defined(_WIN32) || _LIBCPP_HAS_LOCALIZATION
 template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(_InputIt __f, _InputIt __l) {
   static_assert(
@@ -34,7 +34,7 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
           is_same<typename __is_pathable<_InputIt>::__char_type, char>::value,
       "u8path(Iter, Iter) requires Iter have a value_type of type 'char'"
       " or 'char8_t'");
-#    if defined(_LIBCPP_WIN32API)
+#    if defined(_WIN32)
   string __tmp(__f, __l);
   using _CVT = __widen_from_utf8<sizeof(wchar_t) * __CHAR_BIT__>;
   std::wstring __w;
@@ -43,11 +43,11 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
   return path(__w);
 #    else
   return path(__f, __l);
-#    endif // defined(_LIBCPP_WIN32API)
+#    endif // defined(_WIN32)
 }
-#  endif // !defined(_LIBCPP_WIN32API) || _LIBCPP_HAS_LOCALIZATION
+#  endif // !defined(_WIN32) || _LIBCPP_HAS_LOCALIZATION
 
-#  if defined(_LIBCPP_WIN32API) && _LIBCPP_HAS_LOCALIZATION
+#  if defined(_WIN32) && _LIBCPP_HAS_LOCALIZATION
 template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(_InputIt __f, _NullSentinel) {
   static_assert(
@@ -67,7 +67,7 @@ template <class _InputIt, __enable_if_t<__is_pathable<_InputIt>::value, int> = 0
   _CVT()(back_inserter(__w), __tmp.data(), __tmp.data() + __tmp.size());
   return path(__w);
 }
-#  endif // defined(_LIBCPP_WIN32API) && _LIBCPP_HAS_LOCALIZATION
+#  endif // defined(_WIN32) && _LIBCPP_HAS_LOCALIZATION
 
 template <class _Source, __enable_if_t<__is_pathable<_Source>::value, int> = 0>
 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_DEPRECATED_WITH_CHAR8_T path u8path(const _Source& __s) {
@@ -78,7 +78,7 @@ template <class _Source, __enable_if_t<__is_pathable<_Source>::value, int> = 0>
           is_same<typename __is_pathable<_Source>::__char_type, char>::value,
       "u8path(Source const&) requires Source have a character type of type "
       "'char' or 'char8_t'");
-#  if defined(_LIBCPP_WIN32API)
+#  if defined(_WIN32)
   using _Traits = __is_pathable<_Source>;
   return u8path(std::__unwrap_iter(_Traits::__range_begin(__s)), std::__unwrap_iter(_Traits::__range_end(__s)));
 #  else
diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index bb8f8e7bbf5d9..e0524cf4abc4c 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -352,7 +352,7 @@ public:
 #    else
   static const mask __regex_word = 0x80;
 #    endif
-#  elif defined(_LIBCPP_MSVCRT_LIKE)
+#  elif defined(_WIN32)
   typedef unsigned short mask;
   static const mask space        = _SPACE;
   static const mask print        = _BLANK | _PUNCT | _ALPHA | _DIGIT;
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index f973d95ef244d..017d971ec72ba 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -116,7 +116,7 @@
 #    include <__locale_dir/support/netbsd.h>
 #  elif defined(__OpenBSD__)
 #    include <__locale_dir/support/openbsd.h>
-#  elif defined(_LIBCPP_MSVCRT_LIKE)
+#  elif defined(_WIN32)
 #    include <__locale_dir/support/windows.h>
 #  elif defined(__Fuchsia__)
 #    include <__locale_dir/support/fuchsia.h>
diff --git a/libcxx/include/__math/gamma.h b/libcxx/include/__math/gamma.h
index a742ea42d9009..08e09d7cb7aef 100644
--- a/libcxx/include/__math/gamma.h
+++ b/libcxx/include/__math/gamma.h
@@ -62,7 +62,7 @@ inline _LIBCPP_HIDE_FROM_ABI double tgamma(_A1 __x) _NOEXCEPT {
 // declare it differently in the first place: instead use `asm` to get the compiler to call the right
 // function.
 
-#if defined(_LIBCPP_MSVCRT_LIKE) // reentrant version is not available on Windows
+#ifdef _WIN32 // reentrant version is not available on Windows
 
 inline _LIBCPP_HIDE_FROM_ABI double __lgamma_r(double __d) _NOEXCEPT { return __builtin_lgamma(__d); }
 
diff --git a/libcxx/include/__ostream/print.h b/libcxx/include/__ostream/print.h
index 5b4ab60a45eea..4a519c177b990 100644
--- a/libcxx/include/__ostream/print.h
+++ b/libcxx/include/__ostream/print.h
@@ -89,7 +89,7 @@ _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 #    if _LIBCPP_HAS_UNICODE
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
 _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, format_args __args, bool __write_nl) {
-#      ifndef _LIBCPP_WIN32API
+#      ifndef _WIN32
   return std::__vprint_nonunicode(__os, __fmt, __args, __write_nl);
 #      else
   FILE* __file = std::__get_ostream_file(__os);
@@ -123,7 +123,7 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo
     __os.__set_badbit_and_consider_rethrow();
   }
 #        endif // _LIBCPP_HAS_EXCEPTIONS
-#      endif   // _LIBCPP_WIN32API
+#      endif   // _WIN32
 }
 
 template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..78fcdf254b493 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -222,7 +222,7 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#    if _LIBCPP_STD_VER >= 23 && defined(_LIBCPP_WIN32API)
+#    if _LIBCPP_STD_VER >= 23 && defined(_WIN32)
 _LIBCPP_EXPORTED_FROM_ABI void* __filebuf_windows_native_handle(FILE* __file) noexcept;
 #    endif
 
@@ -236,7 +236,7 @@ public:
   typedef typename traits_type::off_type off_type;
   typedef typename traits_type::state_type state_type;
 #    if _LIBCPP_STD_VER >= 26
-#      if defined(_LIBCPP_WIN32API)
+#      if defined(_WIN32)
   using native_handle_type = void*; // HANDLE
 #      elif __has_include(<unistd.h>)
   using native_handle_type = int; // POSIX file descriptor
@@ -272,7 +272,7 @@ public:
 #    if _LIBCPP_STD_VER >= 26
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() const noexcept {
     _LIBCPP_ASSERT_UNCATEGORIZED(this->is_open(), "File must be opened");
-#      if defined(_LIBCPP_WIN32API)
+#      if defined(_WIN32)
     return std::__filebuf_windows_native_handle(__file_);
 #      elif __has_include(<unistd.h>)
     return fileno(__file_);
@@ -1020,7 +1020,7 @@ basic_filebuf<_CharT, _Traits>::seekoff(off_type __off, ios_base::seekdir __way,
 
 template <class _CharT, class _Traits>
 int basic_filebuf<_CharT, _Traits>::__fseek(FILE* __file, pos_type __offset, int __whence) {
-#    if defined(_LIBCPP_MSVCRT_LIKE)
+#    ifdef _WIN32
   return _fseeki64(__file, __offset, __whence);
 #    elif _LIBCPP_LIBC_NEWLIB
   return fseek(__file, __offset, __whence);
@@ -1031,7 +1031,7 @@ int basic_filebuf<_CharT, _Traits>::__fseek(FILE* __file, pos_type __offset, int
 
 template <class _CharT, class _Traits>
 typename basic_filebuf<_CharT, _Traits>::pos_type basic_filebuf<_CharT, _Traits>::__ftell(FILE* __file) {
-#    if defined(_LIBCPP_MSVCRT_LIKE)
+#    ifdef _WIN32
   return _ftelli64(__file);
 #    elif _LIBCPP_LIBC_NEWLIB
   return ftell(__file);
diff --git a/libcxx/include/print b/libcxx/include/print
index cf62743846f42..a9c4ee686f5f9 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -56,7 +56,7 @@ namespace std {
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#    ifdef _LIBCPP_WIN32API
+#    ifdef _WIN32
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
 
@@ -73,7 +73,7 @@ _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream);
 _LIBCPP_EXPORTED_FROM_ABI void __write_to_windows_console(FILE* __stream, wstring_view __view);
 #      endif // _LIBCPP_HAS_WIDE_CHARACTERS
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
-#    endif   // _LIBCPP_WIN32API
+#    endif   // _WIN32
 
 #    if _LIBCPP_HAS_UNICODE
 // This is the code to transcode UTF-8 to UTF-16. This is used on
@@ -197,7 +197,7 @@ inline constexpr bool __use_unicode_execution_charset = _MSVC_EXECUTION_CHARACTE
 inline constexpr bool __use_unicode_execution_charset = true;
 #    endif
 
-#    ifdef _LIBCPP_WIN32API
+#    ifdef _WIN32
 _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream) {
   // The macro _LIBCPP_TESTING_PRINT_IS_TERMINAL is used to change
   // the behavior in the test. This is not part of the public API.
@@ -207,7 +207,7 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream)
   return std::__is_windows_terminal(__stream);
 #      endif
 }
-#    endif // _LIBCPP_WIN32API
+#    endif // _WIN32
 
 [[noreturn]] _LIBCPP_HIDE_FROM_ABI inline void __handle_output_error(FILE* __stream) {
   if (std::feof(__stream))
@@ -251,7 +251,7 @@ _LIBCPP_HIDE_FROM_ABI inline void __output_unicode_windows([[maybe_unused]] FILE
   // the behavior in the test. This is not part of the public API.
 #        ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
   _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION(__stream, __view);
-#        elif defined(_LIBCPP_WIN32API)
+#        elif defined(_WIN32)
   std::__write_to_windows_console(__stream, __view);
 #        else
   std::__throw_runtime_error("No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
@@ -282,7 +282,7 @@ _LIBCPP_HIDE_FROM_ABI inline void __output_unicode([[maybe_unused]] FILE* __stre
   // so there the call can be forwarded to the non_unicode API. On
   // Windows there is a different API. This API requires transcoding.
 
-#      ifndef _LIBCPP_WIN32API
+#      ifndef _WIN32
   __print::__output_nonunicode(__stream, __text);
 #      elif _LIBCPP_HAS_WIDE_CHARACTERS
   if (__print::__is_terminal(__stream)) {
diff --git a/libcxx/include/text_encoding b/libcxx/include/text_encoding
index da39be92c3dfa..c00fe0d278dc0 100644
--- a/libcxx/include/text_encoding
+++ b/libcxx/include/text_encoding
@@ -598,7 +598,7 @@ public:
 
 #    if _LIBCPP_HAS_LOCALIZATION
   [[nodiscard]] _LIBCPP_AVAILABILITY_TEXT_ENCODING_ENVIRONMENT static text_encoding environment() {
-#      if defined(_LIBCPP_WIN32API)
+#      ifdef _WIN32
     return __get_locale_encoding(nullptr);
 #      else
     return __get_locale_encoding("");
diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index 7ff03870bdfdb..e5844249bdf7e 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -195,7 +195,7 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD wchar_t* wmemchr(wchar_t
 }
 #      endif
 
-#      if defined(__cplusplus) && (defined(_LIBCPP_MSVCRT_LIKE) || defined(__MVS__))
+#      if defined(__cplusplus) && (defined(_WIN32) || defined(__MVS__))
 extern "C" {
 size_t mbsnrtowcs(
     wchar_t* __restrict __dst, const char** __restrict __src, size_t __nmc, size_t __len, mbstate_t* __restrict __ps);
diff --git a/libcxx/src/chrono.cpp b/libcxx/src/chrono.cpp
index af5fe7c01fb8b..30c1a1f119a92 100644
--- a/libcxx/src/chrono.cpp
+++ b/libcxx/src/chrono.cpp
@@ -41,14 +41,14 @@
 #  define _LIBCPP_HAS_CLOCK_GETTIME
 #endif
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define VC_EXTRA_LEAN
 #  include <windows.h>
 #  if _WIN32_WINNT >= _WIN32_WINNT_WIN8
 #    include <winapifamily.h>
 #  endif
-#endif // defined(_LIBCPP_WIN32API)
+#endif // _WIN32
 
 #if defined(__Fuchsia__)
 #  include <zircon/syscalls.h>
@@ -71,7 +71,7 @@ namespace chrono {
 // system_clock
 //
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 #  if _WIN32_WINNT < _WIN32_WINNT_WIN8
 
@@ -184,7 +184,7 @@ static steady_clock::time_point __libcpp_steady_clock_now() {
   return steady_clock::time_point(seconds(tp.tv_sec) + nanoseconds(tp.tv_nsec));
 }
 
-#  elif defined(_LIBCPP_WIN32API)
+#  elif defined(_WIN32)
 
 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx says:
 //    If the function fails, the return value is zero. <snip>
diff --git a/libcxx/src/filesystem/directory_iterator.cpp b/libcxx/src/filesystem/directory_iterator.cpp
index 5d682aee2b839..fd2bd504b221b 100644
--- a/libcxx/src/filesystem/directory_iterator.cpp
+++ b/libcxx/src/filesystem/directory_iterator.cpp
@@ -17,7 +17,7 @@
 #include "error.h"
 #include "file_descriptor.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -30,7 +30,7 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 using detail::ErrorHandler;
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 class __dir_stream {
 public:
   __dir_stream()                               = delete;
diff --git a/libcxx/src/filesystem/error.h b/libcxx/src/filesystem/error.h
index db5d1ae9a7250..ee1d148e65aa1 100644
--- a/libcxx/src/filesystem/error.h
+++ b/libcxx/src/filesystem/error.h
@@ -23,7 +23,7 @@
 
 #include "format_string.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h> // ERROR_* macros
@@ -42,7 +42,7 @@ inline error_code capture_errno() {
 }
 
 inline error_code get_last_error() {
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   return std::error_code(GetLastError(), std::system_category());
 #else
   return capture_errno();
diff --git a/libcxx/src/filesystem/file_descriptor.h b/libcxx/src/filesystem/file_descriptor.h
index 9c279c451f28c..b095f200912a4 100644
--- a/libcxx/src/filesystem/file_descriptor.h
+++ b/libcxx/src/filesystem/file_descriptor.h
@@ -20,7 +20,7 @@
 #include "posix_compat.h"
 #include "time_utils.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -30,13 +30,13 @@
 #  include <sys/stat.h>
 #  include <sys/statvfs.h>
 #  include <unistd.h>
-#endif // defined(_LIBCPP_WIN32API)
+#endif // _WIN32
 
 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 
-#if !defined(_LIBCPP_WIN32API)
+#ifndef _WIN32
 
 #  if defined(DT_BLK)
 template <class DirEntT, class = decltype(DirEntT::d_type)>
@@ -84,7 +84,7 @@ inline pair<string_view, file_type> posix_readdir(DIR* dir_stream, error_code& e
   }
 }
 
-#else // _LIBCPP_WIN32API
+#else // _WIN32
 
 inline file_type get_file_type(const WIN32_FIND_DATAW& data) {
   if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
@@ -111,7 +111,7 @@ inline perms get_file_perm(const WIN32_FIND_DATAW& data) {
   return static_cast<perms>(st_mode) & perms::mask;
 }
 
-#endif // !_LIBCPP_WIN32API
+#endif // _WIN32
 
 //                       POSIX HELPERS
 
@@ -128,7 +128,7 @@ struct FileDescriptor {
   static FileDescriptor create(const path* p, error_code& ec, Args... args) {
     ec.clear();
     int fd;
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
     // TODO: most of the filesystem implementation uses native Win32 calls
     // (mostly via posix_compat.h). However, here we use the C-runtime APIs to
     // open a file, because we subsequently pass the C-runtime fd to
@@ -171,7 +171,7 @@ struct FileDescriptor {
 
   void close() noexcept {
     if (fd != -1) {
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
       ::_close(fd);
 #else
       ::close(fd);
diff --git a/libcxx/src/filesystem/filesystem_clock.cpp b/libcxx/src/filesystem/filesystem_clock.cpp
index 96a1336d4cbca..0d360c51a7a37 100644
--- a/libcxx/src/filesystem/filesystem_clock.cpp
+++ b/libcxx/src/filesystem/filesystem_clock.cpp
@@ -14,11 +14,11 @@
 #include <ratio>
 #include <time.h>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  include "time_utils.h"
 #endif
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -51,7 +51,7 @@ _LIBCPP_DIAGNOSTIC_POP
 
 _FilesystemClock::time_point _FilesystemClock::now() noexcept {
   typedef chrono::duration<rep> __secs;
-#if defined(_LIBCPP_WIN32API)
+#if defined(_WIN32)
   typedef chrono::duration<rep, nano> __nsecs;
   FILETIME time;
   GetSystemTimeAsFileTime(&time);
diff --git a/libcxx/src/filesystem/format_string.h b/libcxx/src/filesystem/format_string.h
index 8d17b027a6e31..48bdad9a12030 100644
--- a/libcxx/src/filesystem/format_string.h
+++ b/libcxx/src/filesystem/format_string.h
@@ -18,7 +18,7 @@
 #include <cstdio>
 #include <string>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define PATHSTR(x) (L##x)
 #  define PATH_CSTR_FMT "\"%ls\""
 #else
diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp
index 3d358dd55e8b8..d6392ca144262 100644
--- a/libcxx/src/filesystem/operations.cpp
+++ b/libcxx/src/filesystem/operations.cpp
@@ -26,7 +26,7 @@
 #include "posix_compat.h"
 #include "time_utils.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -100,7 +100,7 @@ path __canonical(path const& orig_p, error_code* ec) {
   ErrorHandler<path> err("canonical", ec, &orig_p, &cwd);
 
   path p = __do_absolute(orig_p, &cwd, ec);
-#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API)
+#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_WIN32)
   std::unique_ptr<path::value_type, decltype(&::free)> hold(detail::realpath(p.c_str(), nullptr), &::free);
   if (hold.get() == nullptr)
     return err.report(detail::get_last_error());
@@ -460,7 +460,7 @@ void __copy_symlink(const path& existing_symlink, const path& new_symlink, error
   if (ec && *ec) {
     return;
   }
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   error_code local_ec;
   if (is_directory(real_path, local_ec))
     __create_directory_symlink(real_path, new_symlink, ec);
@@ -564,7 +564,7 @@ void __create_symlink(path const& from, path const& to, error_code* ec) {
 path __current_path(error_code* ec) {
   ErrorHandler<path> err("current_path", ec);
 
-#if defined(_LIBCPP_WIN32API) || defined(__GLIBC__) || defined(__APPLE__)
+#if defined(_WIN32) || defined(__GLIBC__) || defined(__APPLE__)
   // Common extension outside of POSIX getcwd() spec, without needing to
   // preallocate a buffer. Also supported by a number of other POSIX libcs.
   int size              = 0;
@@ -691,7 +691,7 @@ void __last_write_time(const path& p, file_time_type new_time, error_code* ec) {
   using detail::fs_time;
   ErrorHandler<void> err("last_write_time", ec, &p);
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   TimeSpec ts;
   if (!fs_time::convert_to_timespec(ts, new_time))
     return err.report(errc::value_too_large);
@@ -819,7 +819,7 @@ bool __remove(const path& p, error_code* ec) {
 //
 // The second implementation is used on platforms where `openat()` & friends are available,
 // and it threads file descriptors through recursive calls to avoid such race conditions.
-#if defined(_LIBCPP_WIN32API) || defined(__MVS__)
+#if defined(_WIN32) || defined(__MVS__)
 #  define REMOVE_ALL_USE_DIRECTORY_ITERATOR
 #endif
 
@@ -996,7 +996,7 @@ file_status __symlink_status(const path& p, error_code* ec) { return detail::pos
 path __temp_directory_path(error_code* ec) {
   ErrorHandler<path> err("temp_directory_path", ec);
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   wchar_t buf[MAX_PATH];
   DWORD retval = GetTempPathW(MAX_PATH, buf);
   if (!retval)
diff --git a/libcxx/src/filesystem/path.cpp b/libcxx/src/filesystem/path.cpp
index faa7b4f1fbab7..5790039f66426 100644
--- a/libcxx/src/filesystem/path.cpp
+++ b/libcxx/src/filesystem/path.cpp
@@ -417,7 +417,7 @@ path::iterator& path::iterator::__decrement() {
   return *this;
 }
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 ////////////////////////////////////////////////////////////////////////////
 // Windows path conversions
 size_t __wide_to_char(const wstring& str, char* out, size_t outlen) {
diff --git a/libcxx/src/filesystem/path_parser.h b/libcxx/src/filesystem/path_parser.h
index 5968f449235ba..ae91dc7f1cf26 100644
--- a/libcxx/src/filesystem/path_parser.h
+++ b/libcxx/src/filesystem/path_parser.h
@@ -22,7 +22,7 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 inline bool isSeparator(path::value_type C) {
   if (C == '/')
     return true;
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   if (C == '\\')
     return true;
 #endif
@@ -324,7 +324,7 @@ struct PathParser {
   }
 
   PosPtr consumeRootName(PosPtr P, PosPtr End) const noexcept {
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
     if (PosPtr Ret = consumeDriveLetter(P, End))
       return Ret;
     if (PosPtr Ret = consumeNetworkRoot(P, End))
diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h
index ddd99d8aaf206..f5119aebcbefb 100644
--- a/libcxx/src/filesystem/posix_compat.h
+++ b/libcxx/src/filesystem/posix_compat.h
@@ -31,7 +31,7 @@
 #include "error.h"
 #include "time_utils.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <io.h>
@@ -47,7 +47,7 @@
 #include <stdlib.h>
 #include <time.h>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 // This struct isn't defined in the normal Windows SDK, but only in the
 // Windows Driver Kit.
 struct LIBCPP_REPARSE_DATA_BUFFER {
@@ -81,7 +81,7 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 // Various C runtime header sets provide more or less of these. As we
 // provide our own implementation, undef all potential defines from the
diff --git a/libcxx/src/filesystem/time_utils.h b/libcxx/src/filesystem/time_utils.h
index 89352e5bd6abb..2b01ea9542586 100644
--- a/libcxx/src/filesystem/time_utils.h
+++ b/libcxx/src/filesystem/time_utils.h
@@ -22,7 +22,7 @@
 #include "error.h"
 #include "format_string.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <windows.h>
@@ -41,7 +41,7 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 // Various C runtime versions (UCRT, or the legacy msvcrt.dll used by
 // some mingw toolchains) provide different stat function implementations,
 // with a number of limitations with respect to what we want from the
@@ -267,7 +267,7 @@ struct time_util : time_util_base<FileTimeT, TimeT> {
   }
 };
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 using fs_time = time_util<file_time_type, int64_t, TimeSpec>;
 #else
 using fs_time = time_util<file_time_type, time_t, TimeSpec>;
@@ -301,7 +301,7 @@ inline TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
 
 #if _LIBCPP_HAS_FILESYSTEM
 
-#  if !defined(_LIBCPP_WIN32API)
+#  ifndef _WIN32
 inline bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS, error_code& ec) {
   TimeVal ConvertedTS[2] = {make_timeval(TS[0]), make_timeval(TS[1])};
   if (::utimes(p.c_str(), ConvertedTS) == -1) {
@@ -329,7 +329,7 @@ inline bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS, err
 #    endif
 }
 
-#  endif // !_LIBCPP_WIN32API
+#  endif // _WIN32
 
 inline file_time_type __extract_last_write_time(const path& p, const StatT& st, error_code* ec) {
   using detail::fs_time;
diff --git a/libcxx/src/fstream.cpp b/libcxx/src/fstream.cpp
index 2975088eb6255..12d4952293972 100644
--- a/libcxx/src/fstream.cpp
+++ b/libcxx/src/fstream.cpp
@@ -10,7 +10,7 @@
 #include <cstdio>
 #include <fstream>
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <io.h>
@@ -20,7 +20,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 // Confirm that `HANDLE` is `void*` as implemented in `basic_filebuf`
 static_assert(std::same_as<HANDLE, void*>);
diff --git a/libcxx/src/include/aligned_alloc.h b/libcxx/src/include/aligned_alloc.h
index 24ca26ce04525..2b69a344006a3 100644
--- a/libcxx/src/include/aligned_alloc.h
+++ b/libcxx/src/include/aligned_alloc.h
@@ -27,7 +27,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 //
 // Returns the allocated memory, or `nullptr` on failure.
 inline _LIBCPP_HIDE_FROM_ABI void* __libcpp_aligned_alloc(std::size_t __alignment, std::size_t __size) {
-#  if defined(_LIBCPP_MSVCRT_LIKE)
+#  ifdef _WIN32
   return ::_aligned_malloc(__size, __alignment);
 
 // Android only provides aligned_alloc when targeting API 28 or higher.
@@ -51,7 +51,7 @@ inline _LIBCPP_HIDE_FROM_ABI void* __libcpp_aligned_alloc(std::size_t __alignmen
 }
 
 inline _LIBCPP_HIDE_FROM_ABI void __libcpp_aligned_free(void* __ptr) {
-#  if defined(_LIBCPP_MSVCRT_LIKE)
+#  ifdef _WIN32
   ::_aligned_free(__ptr);
 #  else
   ::free(__ptr);
diff --git a/libcxx/src/include/config_elast.h b/libcxx/src/include/config_elast.h
index daec2429de8eb..8aa9355c4e7f3 100644
--- a/libcxx/src/include/config_elast.h
+++ b/libcxx/src/include/config_elast.h
@@ -11,7 +11,7 @@
 
 #include <__config>
 
-#if defined(_LIBCPP_MSVCRT_LIKE)
+#ifdef _WIN32
 #  include <stdlib.h>
 #else
 #  include <errno.h>
@@ -39,7 +39,7 @@
 // No _LIBCPP_ELAST needed on Apple
 #elif defined(__MVS__)
 #  define _LIBCPP_ELAST 1160
-#elif defined(_LIBCPP_MSVCRT_LIKE)
+#elif defined(_WIN32)
 #  define _LIBCPP_ELAST (_sys_nerr - 1)
 #elif defined(_AIX)
 #  define _LIBCPP_ELAST 127
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..884585519af09 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -95,7 +95,7 @@ STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
 // the standard streams, otherwise it may not be alive during program termination
 // when we flush the streams.
 static void force_locale_initialization() {
-#if defined(_LIBCPP_MSVCRT_LIKE)
+#ifdef _WIN32
   static bool once = []() {
     auto loc = __locale::__newlocale(_LIBCPP_ALL_MASK, "C", 0);
     {
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index b0a69ab74f94c..28f1970f56b4d 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -905,7 +905,7 @@ const ctype<char>::mask* ctype<char>::classic_table() noexcept {
   return _C_ctype_tab_ + 1;
 #  elif defined(__GLIBC__)
   return _LIBCPP_GET_C_LOCALE->__ctype_b;
-#  elif defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  elif defined(_WIN32)
   return __pctype_func();
 #  elif defined(__EMSCRIPTEN__)
   return *__ctype_b_loc();
@@ -5360,19 +5360,19 @@ void moneypunct_byname<char, true>::init(const char* nm) {
     __frac_digits_ = lc->int_frac_digits;
   else
     __frac_digits_ = base::do_frac_digits();
-#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#ifdef _WIN32
   if (lc->p_sign_posn == 0)
-#else  // _LIBCPP_MSVCRT
+#else
   if (lc->int_p_sign_posn == 0)
-#endif // !_LIBCPP_MSVCRT
+#endif
     __positive_sign_ = "()";
   else
     __positive_sign_ = lc->positive_sign;
-#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#ifdef _WIN32
   if (lc->n_sign_posn == 0)
-#else  // _LIBCPP_MSVCRT
+#else
   if (lc->int_n_sign_posn == 0)
-#endif // !_LIBCPP_MSVCRT
+#endif
     __negative_sign_ = "()";
   else
     __negative_sign_ = lc->negative_sign;
@@ -5380,10 +5380,10 @@ void moneypunct_byname<char, true>::init(const char* nm) {
   // the same places in curr_symbol since there's no way to
   // represent anything else.
   string_type __dummy_curr_symbol = __curr_symbol_;
-#if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#ifdef _WIN32
   __init_pat(__pos_format_, __dummy_curr_symbol, true, lc->p_cs_precedes, lc->p_sep_by_space, lc->p_sign_posn, ' ');
   __init_pat(__neg_format_, __curr_symbol_, true, lc->n_cs_precedes, lc->n_sep_by_space, lc->n_sign_posn, ' ');
-#else  // _LIBCPP_MSVCRT
+#else  // _WIN32
   __init_pat(
       __pos_format_,
       __dummy_curr_symbol,
@@ -5394,7 +5394,7 @@ void moneypunct_byname<char, true>::init(const char* nm) {
       ' ');
   __init_pat(
       __neg_format_, __curr_symbol_, true, lc->int_n_cs_precedes, lc->int_n_sep_by_space, lc->int_n_sign_posn, ' ');
-#endif // !_LIBCPP_MSVCRT
+#endif // _WIN32
 }
 
 #if _LIBCPP_HAS_WIDE_CHARACTERS
@@ -5477,11 +5477,11 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
     __frac_digits_ = lc->int_frac_digits;
   else
     __frac_digits_ = base::do_frac_digits();
-#  if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  ifdef _WIN32
   if (lc->p_sign_posn == 0)
-#  else  // _LIBCPP_MSVCRT
+#  else
   if (lc->int_p_sign_posn == 0)
-#  endif // !_LIBCPP_MSVCRT
+#  endif
     __positive_sign_ = L"()";
   else {
     mb = mbstate_t();
@@ -5492,11 +5492,11 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
     wbe = wbuf + j;
     __positive_sign_.assign(wbuf, wbe);
   }
-#  if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  ifdef _WIN32
   if (lc->n_sign_posn == 0)
-#  else  // _LIBCPP_MSVCRT
+#  else
   if (lc->int_n_sign_posn == 0)
-#  endif // !_LIBCPP_MSVCRT
+#  endif
     __negative_sign_ = L"()";
   else {
     mb = mbstate_t();
@@ -5511,10 +5511,10 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
   // the same places in curr_symbol since there's no way to
   // represent anything else.
   string_type __dummy_curr_symbol = __curr_symbol_;
-#  if defined(_LIBCPP_MSVCRT) || defined(__MINGW32__)
+#  ifdef _WIN32
   __init_pat(__pos_format_, __dummy_curr_symbol, true, lc->p_cs_precedes, lc->p_sep_by_space, lc->p_sign_posn, L' ');
   __init_pat(__neg_format_, __curr_symbol_, true, lc->n_cs_precedes, lc->n_sep_by_space, lc->n_sign_posn, L' ');
-#  else  // _LIBCPP_MSVCRT
+#  else  // _WIN32
   __init_pat(
       __pos_format_,
       __dummy_curr_symbol,
@@ -5525,7 +5525,7 @@ void moneypunct_byname<wchar_t, true>::init(const char* nm) {
       L' ');
   __init_pat(
       __neg_format_, __curr_symbol_, true, lc->int_n_cs_precedes, lc->int_n_sep_by_space, lc->int_n_sign_posn, L' ');
-#  endif // !_LIBCPP_MSVCRT
+#  endif // _WIN32
 }
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
diff --git a/libcxx/src/print.cpp b/libcxx/src/print.cpp
index 21d868e8c6a7a..2eb8a0f31488c 100644
--- a/libcxx/src/print.cpp
+++ b/libcxx/src/print.cpp
@@ -15,7 +15,7 @@
 
 #include "filesystem/error.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  define WIN32_LEAN_AND_MEAN
 #  define NOMINMAX
 #  include <io.h>
@@ -35,7 +35,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 _LIBCPP_EXPORTED_FROM_ABI bool __is_windows_terminal(FILE* __stream) {
   // Note the Standard does this in one call, but it's unclear whether
@@ -65,13 +65,13 @@ __write_to_windows_console([[maybe_unused]] FILE* __stream, [[maybe_unused]] wst
 }
 #  endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
-#elif defined(HAS_FILENO_AND_ISATTY) && _LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 23 // !_LIBCPP_WIN32API
+#elif defined(HAS_FILENO_AND_ISATTY) && _LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 23
 
 _LIBCPP_DIAGNOSTIC_PUSH
 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmissing-prototypes")
 _LIBCPP_EXPORTED_FROM_ABI bool __is_posix_terminal(FILE* __stream) { return isatty(fileno(__stream)); }
 _LIBCPP_DIAGNOSTIC_POP
-#endif
+#endif // _WIN32
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/std_stream.h b/libcxx/src/std_stream.h
index 4b9d3a34b2441..32f23e78d4e69 100644
--- a/libcxx/src/std_stream.h
+++ b/libcxx/src/std_stream.h
@@ -57,7 +57,7 @@ class _LIBCPP_HIDDEN __stdinbuf : public basic_streambuf<_CharT, char_traits<_Ch
   bool __last_consumed_is_next_;
   bool __always_noconv_;
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   static constexpr bool __is_win32api_wide_char = !is_same_v<_CharT, char>;
 #else
   static constexpr bool __is_win32api_wide_char = false;
@@ -262,7 +262,7 @@ class _LIBCPP_HIDDEN __stdoutbuf : public basic_streambuf<_CharT, char_traits<_C
   state_type* __st_;
   bool __always_noconv_;
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
   static constexpr bool __is_win32api_wide_char = !is_same_v<_CharT, char>;
 #else
   static constexpr bool __is_win32api_wide_char = false;
diff --git a/libcxx/src/system_error.cpp b/libcxx/src/system_error.cpp
index cf3d2c2926861..c6a38b5bacba9 100644
--- a/libcxx/src/system_error.cpp
+++ b/libcxx/src/system_error.cpp
@@ -21,7 +21,7 @@
 
 #include "include/config_elast.h"
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  include <windows.h>
 #  include <winerror.h>
 #endif
@@ -29,7 +29,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 
 namespace {
 std::optional<errc> __win_err_to_errc(int err) {
@@ -149,7 +149,7 @@ constexpr size_t strerror_buff_size = 1024;
 
 string do_strerror_r(int ev);
 
-#  if defined(_LIBCPP_MSVCRT_LIKE)
+#  ifdef _WIN32
 string do_strerror_r(int ev) {
   char buffer[strerror_buff_size];
   if (::strerror_s(buffer, strerror_buff_size, ev) == 0)
@@ -272,7 +272,7 @@ class _LIBCPP_HIDDEN __system_error_category : public __do_message {
 const char* __system_error_category::name() const noexcept { return "system"; }
 
 string __system_error_category::message(int ev) const {
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
   std::string result;
   char* str               = nullptr;
   unsigned long num_chars = ::FormatMessageA(
@@ -304,7 +304,7 @@ string __system_error_category::message(int ev) const {
 }
 
 error_condition __system_error_category::default_error_condition(int ev) const noexcept {
-#ifdef _LIBCPP_WIN32API
+#ifdef _WIN32
   // Remap windows error codes to generic error codes if possible.
   if (ev == 0)
     return error_condition(0, generic_category());
diff --git a/libcxx/src/thread.cpp b/libcxx/src/thread.cpp
index d2aff1ee0c3fe..7b0890858e9d6 100644
--- a/libcxx/src/thread.cpp
+++ b/libcxx/src/thread.cpp
@@ -24,7 +24,7 @@
 #  pragma weak pthread_create // Do not create libpthread dependency
 #endif
 
-#if defined(_LIBCPP_WIN32API)
+#ifdef _WIN32
 #  include <windows.h>
 #endif
 
@@ -74,7 +74,7 @@ unsigned thread::hardware_concurrency() noexcept {
   if (result < 0)
     return 0;
   return static_cast<unsigned>(result);
-#elif defined(_LIBCPP_WIN32API)
+#elif defined(_WIN32)
   return static_cast<unsigned>(GetActiveProcessorCount(ALL_PROCESSOR_GROUPS));
 #else // defined(CTL_HW) && defined(HW_NCPU)
   // TODO: grovel through /proc or check cpuid on x86 and similar
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index f175bc2ffcd44..0966e7dace413 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -450,7 +450,7 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
 }
 
 #  ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
-#    if defined(_LIBCPP_MSVCRT_LIKE) || (!defined(_LIBCPP_VERSION) && defined(_WIN32))
+#    ifdef _WIN32
 #      define USE_ALIGNED_ALLOC
 #    endif
 


From libcxx-commits at lists.llvm.org  Mon Jul 13 02:16:01 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 02:16:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Clean up windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a54acd1.eb165e5c.1817a5.dee4@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/207577

From libcxx-commits at lists.llvm.org  Mon Jul 13 02:16:02 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 02:16:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a54acd2.6365b89b.8575e.ce82@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/209018

>From c4349f973fed37783692cce1aae993813563a5dd Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Sun, 12 Jul 2026 21:31:47 +0800
Subject: [PATCH 1/4] [libcxx][unique_ptr] Implement LWG 4144: Disallow
 unique_ptr<T&, D>

---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__memory/unique_ptr.h          |  9 +++++
 .../non_pointable.single.verify.cpp           | 40 +++++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 8441da10d78de..c203fa424556a 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -101,7 +101,7 @@
 "`LWG4140 <https://wg21.link/LWG4140>`__","Useless default constructors for bit reference types","2024-11 (Wrocław)","|Complete|","","`#118356 <https://github.com/llvm/llvm-project/issues/118356>`__",""
 "`LWG4141 <https://wg21.link/LWG4141>`__","Improve prohibitions on ""additional storage""","2024-11 (Wrocław)","","","`#118357 <https://github.com/llvm/llvm-project/issues/118357>`__",""
 "`LWG4142 <https://wg21.link/LWG4142>`__","``format_parse_context::check_dynamic_spec`` should require at least one type","2024-11 (Wrocław)","","","`#118358 <https://github.com/llvm/llvm-project/issues/118358>`__",""
-"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","","","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
+"`LWG4144 <https://wg21.link/LWG4144>`__","Disallow ``unique_ptr<T&, D>``","2024-11 (Wrocław)","|Complete|","23","`#118359 <https://github.com/llvm/llvm-project/issues/118359>`__",""
 "`LWG4147 <https://wg21.link/LWG4147>`__","Precondition on ``inplace_vector::emplace``","2024-11 (Wrocław)","","","`#118361 <https://github.com/llvm/llvm-project/issues/118361>`__",""
 "`LWG4148 <https://wg21.link/LWG4148>`__","``unique_ptr::operator*`` should not allow dangling references","2024-11 (Wrocław)","","","`#118362 <https://github.com/llvm/llvm-project/issues/118362>`__",""
 "`LWG4153 <https://wg21.link/LWG4153>`__","Fix extra ""-1"" for ``philox_engine::max()``","2024-11 (Wrocław)","","","`#118363 <https://github.com/llvm/llvm-project/issues/118363>`__",""
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 7e68faa15dabb..93878301b0322 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -43,6 +43,7 @@
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_extent.h>
 #include <__type_traits/remove_reference.h>
+#include <__type_traits/void_t.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -100,6 +101,12 @@ inline const bool __can_dereference = false;
 template <class _Tp>
 inline const bool __can_dereference<_Tp, decltype((void)*std::declval<_Tp>())> = true;
 
+template <class _Tp, class = void>
+inline const bool __is_pointable = false;
+
+template <class _Tp>
+inline const bool __is_pointable<_Tp, __void_t<_Tp*> > = true;
+
 #if defined(_LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI)
 #  define _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI __attribute__((__trivial_abi__))
 #else
@@ -111,6 +118,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
+
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
   using pointer _LIBCPP_NODEBUG = __pointer<_Tp, deleter_type>;
 
   static_assert(!is_rvalue_reference<deleter_type>::value, "the specified deleter type cannot be an rvalue reference");
diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
new file mode 100644
index 0000000000000..14829629861c6
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// unique_ptr
+
+// A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
+
+// LWG 4144
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <memory>
+
+struct Deleter {
+  typedef int* pointer;
+
+  void operator()(pointer) const;
+};
+
+typedef void Function();
+typedef void AbominableFunction() const;
+
+void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
+
+void reference_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<int&, Deleter>);
+}
+
+void abominable_function_type() {
+  // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
+  (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
+}

>From 79c6048aef67e3bfff149275bd08d8540ec5d3fd Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Mon, 13 Jul 2026 12:17:49 +0800
Subject: [PATCH 2/4] adjust lit test

1. Add [unique.ptr.single.general]/1 marker
2. Merge test cases into one
3. Remove useless lines
---
 .../unique.ptr.class/non_pointable.single.verify.cpp   | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
index 14829629861c6..6f27cdbdce4eb 100644
--- a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
+++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.class/non_pointable.single.verify.cpp
@@ -10,10 +10,9 @@
 
 // unique_ptr
 
+// [unique.ptr.single.general]/1 (added by LWG4144)
 // A program that instantiates the definition of unique_ptr<T, D> is ill-formed if T* is an invalid type.
 
-// LWG 4144
-
 // XFAIL: FROZEN-CXX03-HEADERS-FIXME
 
 #include <memory>
@@ -24,17 +23,12 @@ struct Deleter {
   void operator()(pointer) const;
 };
 
-typedef void Function();
 typedef void AbominableFunction() const;
 
-void pointable_function_type() { (void)sizeof(std::unique_ptr<Function, Deleter>); }
-
-void reference_type() {
+void test() {
   // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
   (void)sizeof(std::unique_ptr<int&, Deleter>);
-}
 
-void abominable_function_type() {
   // expected-error-re@*:* {{static assertion failed {{.*}}unique_ptr<T, D> requires T* to be a valid type}}
   (void)sizeof(std::unique_ptr<AbominableFunction, Deleter>);
 }

>From 0f785c8af9379592f076d2f75cd41cdd275163b8 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Mon, 13 Jul 2026 14:44:56 +0800
Subject: [PATCH 3/4] Put all static_assert of unique_ptr together

---
 libcxx/include/__memory/unique_ptr.h | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 93878301b0322..dce23434d48f1 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -115,15 +115,14 @@ inline const bool __is_pointable<_Tp, __void_t<_Tp*> > = true;
 
 template <class _Tp, class _Dp = default_delete<_Tp> >
 class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
+  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
+  static_assert(!is_rvalue_reference<_Dp>::value, "the specified deleter type cannot be an rvalue reference");
+
 public:
   typedef _Tp element_type;
   typedef _Dp deleter_type;
-
-  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
   using pointer _LIBCPP_NODEBUG = __pointer<_Tp, deleter_type>;
 
-  static_assert(!is_rvalue_reference<deleter_type>::value, "the specified deleter type cannot be an rvalue reference");
-
   // A unique_ptr contains the following members which may be trivially relocatable:
   // - pointer : this may be trivially relocatable, so it's checked
   // - deleter_type: this may be trivially relocatable, so it's checked

>From 8f666c9c53bbf7f1a09264e1d070bc3331d2a2ee Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Mon, 13 Jul 2026 17:15:44 +0800
Subject: [PATCH 4/4] rename __is_pointable to __can_add_pointer

---
 libcxx/include/__memory/unique_ptr.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index dce23434d48f1..46256b8fa8805 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -102,10 +102,10 @@ template <class _Tp>
 inline const bool __can_dereference<_Tp, decltype((void)*std::declval<_Tp>())> = true;
 
 template <class _Tp, class = void>
-inline const bool __is_pointable = false;
+inline const bool __can_add_pointer = false;
 
 template <class _Tp>
-inline const bool __is_pointable<_Tp, __void_t<_Tp*> > = true;
+inline const bool __can_add_pointer<_Tp, __void_t<_Tp*> > = true;
 
 #if defined(_LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI)
 #  define _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI __attribute__((__trivial_abi__))
@@ -115,7 +115,7 @@ inline const bool __is_pointable<_Tp, __void_t<_Tp*> > = true;
 
 template <class _Tp, class _Dp = default_delete<_Tp> >
 class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
-  static_assert(__is_pointable<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
+  static_assert(__can_add_pointer<_Tp>, "unique_ptr<T, D> requires T* to be a valid type");
   static_assert(!is_rvalue_reference<_Dp>::value, "the specified deleter type cannot be an rvalue reference");
 
 public:


From libcxx-commits at lists.llvm.org  Mon Jul 13 02:18:07 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 02:18:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Clean up windows macros (PR
	#207577)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207577@github.com>
Message-ID: <6a54ad4f.6537ae93.2f1c1b.45f9@mx.google.com>


philnik777 wrote:

@mstorsjo I've kept `_LIBCPP_MSVCRT` for now to keep the diff smaller and avoid the issue whether we should detect the libc at configure-time (IMO that's kinda orthogonal to the aim of this patch), but I'm happy with renaming it in a follow-up.

https://github.com/llvm/llvm-project/pull/207577

From libcxx-commits at lists.llvm.org  Mon Jul 13 02:34:07 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 02:34:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] d62c903 - [libc++] Refactor the string
	benchmarks (#185397)
Message-ID: <6a54b10f.92ea0e2a.13fd37.270a@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-13T11:34:02+02:00
New Revision: d62c903ffd049da2a80acc7f0c0671447adc76f7

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

LOG: [libc++] Refactor the string benchmarks (#185397)

Fixes #179696

Added: 
    

Modified: 
    libcxx/test/benchmarks/containers/string.bench.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index 98216d22d0144..e6cc60304eb60 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -6,602 +6,484 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cstdint>
-#include <cstdlib>
-#include <new>
+#include <array>
+#include <functional>
+#include <string>
 #include <vector>
 
-#include "../CartesianBenchmarks.h"
-#include "../GenerateInput.h"
 #include "benchmark/benchmark.h"
+#include "make_string.h"
 #include "test_macros.h"
 
-constexpr std::size_t MAX_STRING_LEN = 8 << 14;
+constexpr size_t small_size = 5;
+constexpr size_t large_size = 30;
 
-// Benchmark when there is no match.
-static void BM_StringFindNoMatch(benchmark::State& state) {
-  std::string s1(state.range(0), '-');
-  std::string s2(8, '*');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
-
-// Benchmark when the string matches first time.
-static void BM_StringFindAllMatch(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN, '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
-
-// Benchmark when the string matches somewhere in the end.
-static void BM_StringFindMatch1(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
-
-// Benchmark when the string matches somewhere from middle to the end.
-static void BM_StringFindMatch2(benchmark::State& state) {
-  std::string s1(MAX_STRING_LEN / 2, '*');
-  s1 += std::string(state.range(0), '-');
-  s1 += std::string(state.range(0), '*');
-  std::string s2(state.range(0), '-');
-  for (auto _ : state)
-    benchmark::DoNotOptimize(s1.find(s2));
-}
-BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
-
-static void BM_StringFindStringLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find("b"));
-    benchmark::ClobberMemory();
-  }
-}
-
-BENCHMARK(BM_StringFindStringLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringFindCharLiteral(benchmark::State& state) {
-  std::string s;
-
-  for (int i = 0; i < state.range(0); i++)
-    s += 'a';
-
-  s += 'b';
-
-  benchmark::DoNotOptimize(s.data());
-  benchmark::ClobberMemory();
-  size_t pos;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(pos = s.find('b'));
-    benchmark::ClobberMemory();
-  }
-}
-BENCHMARK(BM_StringFindCharLiteral)->RangeMultiplier(2)->Range(8, 8 << 10);
-
-static void BM_StringCtorDefault(benchmark::State& state) {
-  for (auto _ : state) {
-    std::string Default;
-    benchmark::DoNotOptimize(Default);
-  }
-}
-BENCHMARK(BM_StringCtorDefault);
-
-static void BM_StringResizeAndOverwrite(benchmark::State& state) {
-  std::string str;
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(str);
-    str.resize_and_overwrite(10, [](char* ptr, size_t n) {
-      std::fill_n(ptr, n, 'a');
-      return n;
-    });
-    benchmark::DoNotOptimize(str);
-    str.clear();
-  }
-}
-BENCHMARK(BM_StringResizeAndOverwrite);
-
-enum class Length { Empty, Small, Large, Huge };
-struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
-  static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
-};
-
-enum class Opacity { Opaque, Transparent };
-struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
-  static constexpr const char* Names[] = {"Opaque", "Transparent"};
-};
-
-enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
-struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
-  static constexpr const char* Names[] = {"Control", "ChangeFirst", "ChangeMiddle", "ChangeLast"};
-};
-
-static constexpr char SmallStringLiteral[] = "012345678";
-
-TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
-  switch (D) {
-  case DiffType::Control:
-    return SmallStringLiteral;
-  case DiffType::ChangeFirst:
-    return "-12345678";
-  case DiffType::ChangeMiddle:
-    return "0123-5678";
-  case DiffType::ChangeLast:
-    return "01234567-";
+std::string rename(std::string str, std::string_view replacement) {
+  while (true) {
+    auto pos = str.find("basic_string");
+    if (pos == std::string::npos)
+      return str;
+    str.replace(pos, std::strlen("basic_string"), replacement);
   }
-  __builtin_unreachable();
 }
 
-static constexpr char LargeStringLiteral[] = "012345678901234567890123456789012345678901234567890123456789012";
-
-TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
-#define LARGE_STRING_FIRST "123456789012345678901234567890"
-#define LARGE_STRING_SECOND "234567890123456789012345678901"
-  switch (D) {
-  case DiffType::Control:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeFirst:
-    return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeMiddle:
-    return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
-  case DiffType::ChangeLast:
-    return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
-  }
-  __builtin_unreachable();
+template <class Func, class Mod = decltype([](auto) {})>
+void bench(std::string name, Func func, Mod modifier = {}) {
+  benchmark::RegisterBenchmark(rename(name, "string"), [=](benchmark::State& state) {
+    func(std::type_identity<char>(), state);
+  })->Apply(modifier);
+
+  benchmark::RegisterBenchmark(rename(name, "u8string"), [=](benchmark::State& state) {
+    func(std::type_identity<char8_t>(), state);
+  })->Apply(modifier);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  benchmark::RegisterBenchmark(rename(name, "wstring"), [=](benchmark::State& state) {
+    func(std::type_identity<wchar_t>(), state);
+  })->Apply(modifier);
+#endif
 }
 
-TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
-#define HUGE_STRING0 "0123456789"
-#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
-#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
-#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
-#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
-  switch (D) {
-  case DiffType::Control:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeFirst:
-    return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeMiddle:
-    return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
-  case DiffType::ChangeLast:
-    return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE const char* getString(Length L, DiffType D = DiffType::Control) {
-  switch (L) {
-  case Length::Empty:
-    return "";
-  case Length::Small:
-    return getSmallString(D);
-  case Length::Large:
-    return getLargeString(D);
-  case Length::Huge:
-    return getHugeString(D);
-  }
-  __builtin_unreachable();
-}
-
-TEST_ALWAYS_INLINE std::string makeString(Length L, DiffType D = DiffType::Control, Opacity O = Opacity::Transparent) {
-  switch (L) {
-  case Length::Empty:
-    return maybeOpaque("", O == Opacity::Opaque);
-  case Length::Small:
-    return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-  case Length::Large:
-    return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-  case Length::Huge:
-    return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
-  }
-  __builtin_unreachable();
-}
-
-template <class Length, class Opaque>
-struct StringConstructDestroyCStr {
-  static void run(benchmark::State& state) {
-    for (auto _ : state) {
-      benchmark::DoNotOptimize(makeString(Length(), DiffType::Control, Opaque()));
-    }
-  }
-
-  static std::string name() { return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, bool MeasureCopy, bool MeasureDestroy>
-static void StringCopyAndDestroy(benchmark::State& state) {
-  static constexpr size_t NumStrings = 1024;
-  auto Orig                          = makeString(Length());
-  alignas(std::string) char Storage[NumStrings * sizeof(std::string)];
-
-  while (state.KeepRunningBatch(NumStrings)) {
-    if (!MeasureCopy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      ::new (reinterpret_cast<std::string*>(Storage) + I) std::string(Orig);
-    }
-    if (!MeasureCopy)
-      state.ResumeTiming();
-    if (!MeasureDestroy)
-      state.PauseTiming();
-    for (size_t I = 0; I < NumStrings; ++I) {
-      using S = std::string;
-      (reinterpret_cast<S*>(Storage) + I)->~S();
-    }
-    if (!MeasureDestroy)
-      state.ResumeTiming();
-  }
-}
-
-template <class Length>
-struct StringCopy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, true, false>(state); }
-
-  static std::string name() { return "BM_StringCopy" + Length::name(); }
-};
-
-template <class Length>
-struct StringDestroy {
-  static void run(benchmark::State& state) { StringCopyAndDestroy<Length, false, true>(state); }
-
-  static std::string name() { return "BM_StringDestroy" + Length::name(); }
-};
-
-template <class Length>
-struct StringMove {
-  static void run(benchmark::State& state) {
-    // Keep two object locations and move construct back and forth.
-    alignas(std::string) char Storage[2 * sizeof(std::string)];
-    using S  = std::string;
-    size_t I = 0;
-    S* newS  = new (reinterpret_cast<std::string*>(Storage)) std::string(makeString(Length()));
+int main(int argc, char** argv) {
+  // [string.cons]
+  bench("std::basic_string::ctor()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
     for (auto _ : state) {
-      // Switch locations.
-      I ^= 1;
-      benchmark::DoNotOptimize(Storage);
-      // Move construct into the new location,
-      S* tmpS = new (reinterpret_cast<std::string*>(Storage) + I) S(std::move(*newS));
-      // then destroy the old one.
-      newS->~S();
-      newS = tmpS;
+      std::basic_string<CharT> str;
+      benchmark::DoNotOptimize(str);
     }
-    newS->~S();
-  }
-
-  static std::string name() { return "BM_StringMove" + Length::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignStr {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string src           = makeString(Length());
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = *maybeOpaque(&src, opaque);
-      }
-    }
-  }
+  });
+
+  bench(
+      "std::basic_string::ctor(const value_type*)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> str(state.range(), 'a');
+
+        for (auto _ : state) {
+          auto ptr = str.data();
+          std::basic_string<CharT> copy(ptr);
+          benchmark::DoNotOptimize(copy);
+        }
+      },
+      [](auto bm) { bm->Arg(small_size)->Arg(large_size); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str(state.range(), 'a');
+
+          for (auto _ : state) {
+            auto copy = str;
+            benchmark::DoNotOptimize(copy);
+          }
+        };
+    bench("std::basic_string::ctor(const Self&) (opaque)", std::bind_front(bench_impl, std::true_type{}), [](auto bm) {
+      bm->Arg(small_size)->Arg(large_size);
+    });
 
-  static std::string name() { return "BM_StringAssignStr" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringAssignAsciiz {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = maybeOpaque(getString(Length()), opaque);
-      }
-    }
+    bench("std::basic_string::ctor(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size)->Arg(large_size); });
   }
 
-  static std::string name() { return "BM_StringAssignAsciiz" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseToEnd {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int mid = makeString(Length()).size() / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(mid, opaque), maybeOpaque(std::string::npos, opaque));
-      }
-    }
+  bench(
+      "std::basic_string::ctor(Self&&)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        using str = std::basic_string<CharT>;
+
+        union U {
+          U() {}
+          ~U() {}
+
+          struct {
+            str s1;
+            str s2;
+          };
+        } u;
+
+        std::construct_at(&u.s1, state.range(), 'a');
+
+        for (auto _ : state) {
+          std::construct_at(&u.s2, std::move(u.s1));
+          std::destroy_at(&u.s1);
+          benchmark::DoNotOptimize(u);
+
+          std::construct_at(&u.s1, std::move(u.s2));
+          std::destroy_at(&u.s2);
+          benchmark::DoNotOptimize(u);
+        }
+
+        std::destroy_at(&u.s1);
+      },
+      [](auto bm) { bm->Arg(small_size)->Arg(large_size); });
+
+  {
+    static auto bench_impl =
+        []<bool opaque, class CharT>(std::bool_constant<opaque>, std::type_identity<CharT>, benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+          str src(state.range(), 'a');
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              string = src;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const Self&) (opaque)",
+          std::bind_front(bench_impl, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size)->Arg(large_size); });
+
+    bench("std::basic_string::operator=(const Self&) (transparent)",
+          std::bind_front(bench_impl, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size)->Arg(large_size); });
   }
 
-  static std::string name() { return "BM_StringEraseToEnd" + Length::name() + Opaque::name(); }
-};
-
-template <class Length, class Opaque>
-struct StringEraseWithMove {
-  static void run(benchmark::State& state) {
-    constexpr bool opaque     = Opaque{} == Opacity::Opaque;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    const int n   = makeString(Length()).size() / 2;
-    const int pos = n / 2;
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i] = makeString(Length());
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              str().swap(string); // Make sure the strings are in the default constructed state
+            state.ResumeTiming();
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              string = ptr;
+            }
+          }
+        };
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}));
+
+    bench("std::basic_string::operator=(const value_type*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}));
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}));
+
+    bench("std::basic_string::operator=(const value_type*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
   }
 
-  static std::string name() { return "BM_StringEraseWithMove" + Length::name() + Opaque::name(); }
-};
-
-template <class Opaque>
-struct StringAssignAsciizMix {
-  static void run(benchmark::State& state) {
-    constexpr auto O          = Opaque{};
-    constexpr auto D          = DiffType::Control;
-    constexpr int kNumStrings = 4 << 10;
-    std::string strings[kNumStrings];
-    while (state.KeepRunningBatch(kNumStrings)) {
-      state.PauseTiming();
-      for (int i = 0; i < kNumStrings; ++i) {
-        std::string().swap(strings[i]);
-      }
-      benchmark::DoNotOptimize(strings);
-      state.ResumeTiming();
-      for (int i = 0; i < kNumStrings - 7; i += 8) {
-        strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-        strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
-        strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
-      }
-    }
-  }
-
-  static std::string name() { return "BM_StringAssignAsciizMix" + Opaque::name(); }
-};
-
-enum class Relation { Eq, Less, Compare };
-struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
-  static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelational {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(RHLength());
-    auto Rhs = makeString(LHLength(), DiffType());
+  // [string.capacity]
+  bench("std::basic_string::size()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      benchmark::DoNotOptimize(Rhs);
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Rhs);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Rhs);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Rhs));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.size());
     }
+  });
+
+#if TEST_STD_VER >= 23
+  bench("std::basic_string::resize_and_overwrite()",
+        []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+          std::basic_string<CharT> str;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(str);
+            str.resize_and_overwrite(10, [](CharT* ptr, size_t n) {
+              std::fill_n(ptr, n, 'a');
+              return n;
+            });
+            benchmark::DoNotOptimize(str);
+            str.clear();
+          }
+        });
+#endif
+
+  // [string.modifiers]
+
+  {
+    static auto bench_impl =
+        []<size_t end, bool opaque, class CharT>(
+            std::integral_constant<size_t, end>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> strings[4096];
+
+          size_t size = state.range();
+          size_t pos  = size / 2;
+          auto npos   = end;
+          while (state.KeepRunningBatch(std::size(strings))) {
+            state.PauseTiming();
+            for (auto& string : strings)
+              string.resize(size, 'a');
+            state.ResumeTiming();
+            for (auto& string : strings) {
+              if constexpr (opaque) {
+                benchmark::DoNotOptimize(pos);
+                benchmark::DoNotOptimize(npos);
+              }
+              string.erase(pos, npos);
+            }
+          }
+        };
+    bench("std::basic_string::erase() (to end of string, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size)->Arg(large_size); });
+    bench("std::basic_string::erase() (to end of string, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, std::string::npos>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size)->Arg(large_size); });
+    bench("std::basic_string::erase() (in the middle, opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size)->Arg(large_size); });
+    bench("std::basic_string::erase() (in the middle, transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, 2>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size)->Arg(large_size); });
   }
 
-  static bool skip() {
-    // Eq is commutative, so skip half the matrix.
-    if (Rel() == Relation::Eq && LHLength() > RHLength())
-      return true;
-    // We only care about control when the lengths 
diff er.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // For empty, only control matters.
-    if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelational" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-template <class Rel, class LHLength, class RHLength, class DiffType>
-struct StringRelationalLiteral {
-  static void run(benchmark::State& state) {
-    auto Lhs = makeString(LHLength(), DiffType());
+  // [string.ops]
+  bench("std::basic_string::data()", []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+    std::basic_string<CharT> str;
     for (auto _ : state) {
-      benchmark::DoNotOptimize(Lhs);
-      constexpr const char* Literal =
-          RHLength::value == Length::Empty ? ""
-          : RHLength::value == Length::Small
-              ? SmallStringLiteral
-              : LargeStringLiteral;
-      switch (Rel()) {
-      case Relation::Eq:
-        benchmark::DoNotOptimize(Lhs == Literal);
-        break;
-      case Relation::Less:
-        benchmark::DoNotOptimize(Lhs < Literal);
-        break;
-      case Relation::Compare:
-        benchmark::DoNotOptimize(Lhs.compare(Literal));
-        break;
-      }
+      benchmark::DoNotOptimize(str);
+      benchmark::DoNotOptimize(str.data());
     }
+  });
+
+  auto search_sizes = [](auto bm) { bm->Arg(small_size)->Arg(large_size)->Arg(8192); };
+  bench(
+      "std::basic_string::find(std::basic_string) (no match)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(std::basic_string) (match at the end)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> haystack(state.range(), 'a');
+        std::basic_string<CharT> needle(8, 'b');
+        haystack += needle;
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(haystack.find(needle));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(const value_type*) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find(MAKE_CSTRING(CharT, "b")));
+        }
+      },
+      search_sizes);
+
+  bench(
+      "std::basic_string::find(value_type) (literal)",
+      []<class CharT>(std::type_identity<CharT>, benchmark::State& state) {
+        std::basic_string<CharT> s1(state.range(), 'a');
+
+        for (auto _ : state) {
+          benchmark::DoNotOptimize(s1.find('b'));
+        }
+      },
+      search_sizes);
+
+  // [string.cmp]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string == ptr);
+            }
+          }
+        };
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}));
+
+    bench("std::basic_string == const CharT* (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}));
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}));
+
+    bench("std::basic_string == const CharT* (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
   }
 
-  static bool skip() {
-    // Doesn't matter how they 
diff er if they have 
diff erent size.
-    if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
-      return true;
-    // We don't need huge. Doensn't give anything 
diff erent than Large.
-    if (LHLength() == Length::Huge || RHLength() == Length::Huge)
-      return true;
-    return false;
-  }
-
-  static std::string name() {
-    return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() + RHLength::name() + DiffType::name();
-  }
-};
-
-enum class Depth { Shallow, Deep };
-struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
-  static constexpr const char* Names[] = {"Shallow", "Deep"};
-};
-
-enum class Temperature { Hot, Cold };
-struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
-  static constexpr const char* Names[] = {"Hot", "Cold"};
-};
-
-template <class Temperature, class Depth, class Length>
-struct StringRead {
-  void run(benchmark::State& state) const {
-    static constexpr size_t NumStrings =
-        Temperature() == ::Temperature::Hot ? 1 << 10 : /* Enough strings to overflow the cache */ 1 << 20;
-    static_assert((NumStrings & (NumStrings - 1)) == 0, "NumStrings should be a power of two to reduce overhead.");
-
-    std::vector<std::string> Values(NumStrings, makeString(Length()));
-    size_t I = 0;
-    for (auto _ : state) {
-      // Jump long enough to defeat cache locality, and use a value that is
-      // coprime with NumStrings to ensure we visit every element.
-      I       = (I + 17) % NumStrings;
-      auto& V = Values[I];
-
-      // Read everything first. Escaping data() through DoNotOptimize might
-      // cause the compiler to have to recalculate information about `V` due to
-      // aliasing.
-      char* Data  = V.data();
-      size_t Size = V.size();
-      benchmark::DoNotOptimize(Data);
-      benchmark::DoNotOptimize(Size);
-      if (Depth() == ::Depth::Deep) {
-        // Read into the payload. This mainly shows the benefit of SSO when the
-        // data is cold.
-        benchmark::DoNotOptimize(*Data);
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          static constexpr std::array<CharT, size> src = [] {
+            std::array<CharT, size> ret;
+            std::ranges::fill(ret, 'a');
+            ret[size - 1] = '\0';
+            return ret;
+          }();
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              auto ptr = src.data();
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(ptr);
+              benchmark::DoNotOptimize(string.compare(ptr));
+            }
+          }
+        };
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}));
+
+    bench("std::basic_string::compare(const CharT*) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}));
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}));
+
+    bench("std::basic_string::compare(const CharT*) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
   }
 
-  static bool skip() {
-    // Huge does not give us anything that Large doesn't have. Skip it.
-    return Length() == ::Length::Huge;
+  // [string.compare]
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          using str = std::basic_string<CharT>;
+
+          std::basic_string<CharT> src(size, 'a');
+
+          str strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string == src);
+            }
+          }
+        };
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}));
+
+    bench("std::basic_string == std::basic_string (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}));
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}));
+
+    bench("std::basic_string == std::basic_string (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
   }
 
-  std::string name() const { return "BM_StringRead" + Temperature::name() + Depth::name() + Length::name(); }
-};
-
-void sanityCheckGeneratedStrings() {
-  for (auto Lhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
-    const auto LhsString = makeString(Lhs);
-    for (auto Rhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
-      if (Lhs > Rhs)
-        continue;
-      const auto RhsString = makeString(Rhs);
-
-      // The smaller one must be a prefix of the larger one.
-      if (RhsString.find(LhsString) != 0) {
-        fprintf(
-            stderr, "Invalid autogenerated strings for sizes (%d,%d).\n", static_cast<int>(Lhs), static_cast<int>(Rhs));
-        std::abort();
-      }
-    }
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> src(size, 'a');
+
+          std::basic_string<CharT> strings[4096];
+          std::fill_n(strings, 4096, src.data());
+          while (state.KeepRunningBatch(std::size(strings))) {
+            benchmark::DoNotOptimize(strings);
+
+            for (auto& string : strings) {
+              if constexpr (opaque)
+                benchmark::DoNotOptimize(src);
+              benchmark::DoNotOptimize(string.compare(src));
+            }
+          }
+        };
+
+    // These also effectively cover operator<, since operator< is just forwarding to compare.
+
+    bench("std::basic_string::compare(const std::basic_string&) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}));
+
+    bench("std::basic_string::compare(const std::basic_string&) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}));
+
+    bench("std::basic_string::compare(const std::basic_string&) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}));
+
+    bench("std::basic_string::compare(const std::basic_string&) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
   }
-  // Verify the autogenerated 
diff s
-  for (auto L : {Length::Small, Length::Large, Length::Huge}) {
-    const auto Control = makeString(L);
-    const auto Verify  = [&](std::string Exp, size_t Pos) {
-      // Only change on the Pos char.
-      if (Control[Pos] != Exp[Pos]) {
-        Exp[Pos] = Control[Pos];
-        if (Control == Exp)
-          return;
-      }
-      fprintf(stderr, "Invalid autogenerated 
diff  with size %d\n", static_cast<int>(L));
-      std::abort();
-    };
-    Verify(makeString(L, DiffType::ChangeFirst), 0);
-    Verify(makeString(L, DiffType::ChangeMiddle), Control.size() / 2);
-    Verify(makeString(L, DiffType::ChangeLast), Control.size() - 1);
-  }
-}
 
-int main(int argc, char** argv) {
   benchmark::Initialize(&argc, argv);
-  if (benchmark::ReportUnrecognizedArguments(argc, argv))
-    return 1;
-
-  sanityCheckGeneratedStrings();
-
-  makeCartesianProductBenchmark<StringConstructDestroyCStr, AllLengths, AllOpacity>();
-
-  makeCartesianProductBenchmark<StringAssignStr, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringAssignAsciiz, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringAssignAsciizMix, AllOpacity>();
-
-  makeCartesianProductBenchmark<StringCopy, AllLengths>();
-  makeCartesianProductBenchmark<StringMove, AllLengths>();
-  makeCartesianProductBenchmark<StringDestroy, AllLengths>();
-  makeCartesianProductBenchmark<StringEraseToEnd, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringEraseWithMove, AllLengths, AllOpacity>();
-  makeCartesianProductBenchmark<StringRelational, AllRelations, AllLengths, AllLengths, AllDiffTypes>();
-  makeCartesianProductBenchmark<StringRelationalLiteral, AllRelations, AllLengths, AllLengths, AllDiffTypes>();
-  makeCartesianProductBenchmark<StringRead, AllTemperatures, AllDepths, AllLengths>();
   benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
 }


        

From libcxx-commits at lists.llvm.org  Mon Jul 13 02:34:09 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 02:34:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Refactor the string benchmarks
	(PR #185397)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185397@github.com>
Message-ID: <6a54b111.6c5720b3.15e90c.b58f@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/185397

From libcxx-commits at lists.llvm.org  Mon Jul 13 02:36:08 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 02:36:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libc++abi] Add a clang-tidy
 config and test (PR #207324)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207324@github.com>
Message-ID: <6a54b188.4987c784.63cbc.a0b4@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/207324

From libcxx-commits at lists.llvm.org  Mon Jul 13 02:36:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 02:36:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a54b18a.bb839515.27d21e.cdae@mx.google.com>


================
@@ -100,6 +101,12 @@ inline const bool __can_dereference = false;
 template <class _Tp>
 inline const bool __can_dereference<_Tp, decltype((void)*std::declval<_Tp>())> = true;
 
+template <class _Tp, class = void>
+inline const bool __is_pointable = false;
----------------
emmett2020 wrote:

Thanks for the review. `__can_add_pointer` seems more descriptive than `__is_pointable`. I've changed the variable name now.

I don't think we can use `!is_same<__add_pointer_t<_Tp>, _Tp>`. `__add_pointer_t` will remove reference first .

https://github.com/llvm/llvm-project/blob/main/clang/lib/Sema/SemaType.cpp#L10168. 

So 
```
For unique_ptr<int&, D>, _Tp is int&:

__add_pointer_t<int&>       -> int*
!is_same<int*, int&>::value -> true
static_assert(...)          -> passes

Therefore, this condition would fail to reject unique_ptr<int&, D>
```


Yes, `requires { _Tp*}` is so clear! Unfortunately, there is a version limitation :)

https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Mon Jul 13 02:37:29 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 02:37:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a54b1d9.510f576d.1b3cb9.5fa1@mx.google.com>


https://github.com/emmett2020 edited https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Mon Jul 13 02:37:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 02:37:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a54b1ef.bb839515.27d21e.128d@mx.google.com>


https://github.com/emmett2020 edited https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Mon Jul 13 02:39:01 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 02:39:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][unique_ptr] Implement LWG 4144:
 Disallow unique_ptr<T&, D> (PR #209018)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209018@github.com>
Message-ID: <6a54b235.6537ae93.2f1c1b.ba61@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/209018

From libcxx-commits at lists.llvm.org  Mon Jul 13 02:42:12 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 02:42:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a54b2f4.f732ad81.3a4994.811f@mx.google.com>


================
@@ -664,6 +674,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (noreplace) mode suffix; these cases fall through to
+// default (return nullptr), which is conformant per C23 7.21.5.3p5 (fail if atomic check-and-create is unsupported).
+#      if !defined(_AIX)
----------------
Himadhith wrote:

Agreed that this is a libc limitation at AIX side, but this is also a C++  standard violation ([C23 §7.23.5.3p5](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf#page=340&zoom=100,114,918)). We need to handle this at lib++ as well. If not, `noreplace` becomes a no-op on AIX.

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Mon Jul 13 02:51:23 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 02:51:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a54b51b.0c05e68e.1e82f1.327f@mx.google.com>


================
@@ -664,6 +674,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (noreplace) mode suffix; these cases fall through to
+// default (return nullptr), which is conformant per C23 7.21.5.3p5 (fail if atomic check-and-create is unsupported).
+#      if !defined(_AIX)
----------------
philnik777 wrote:

Why do we need to handle this in libc++? If `fopen` was conforming it would fail and this code wouldn't need to be modified in order to be conforming. We would simply fail gracefully. What am I missing?

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Mon Jul 13 02:55:04 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 02:55:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Use [[deprecated]] for
	__grow_by (PR #209126)
Message-ID: <llvm.org/llvm/llvm-project/pull/209126@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/209126

`__grow_by` is an internal function, and we deprecated it to avoid accidental use of it inside the library. `_LIBCPP_DEPRECATED_` is meant for user-configuration, which doesn't apply here.

Fixes #124940


>From fd74a816f4c012ad5ebd066c025e3e614356a778 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 11:53:06 +0200
Subject: [PATCH] [libc++][NFC] Use [[deprecated]] for __grow_by

---
 libcxx/include/string | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index a297a05813cee..39de836dbcf85 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -2343,17 +2343,16 @@ private:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
   __init_with_size(_InputIterator __first, _Sentinel __last, size_type __sz);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20
+  [[__deprecated__("use __grow_by_without_replace")]] _LIBCPP_CONSTEXPR_SINCE_CXX20
 #  if _LIBCPP_ABI_VERSION >= 2 //  We want to use the function in the dylib in ABIv1
   _LIBCPP_HIDE_FROM_ABI
 #  endif
-  _LIBCPP_DEPRECATED_("use __grow_by_without_replace") void __grow_by(
-      size_type __old_cap,
-      size_type __delta_cap,
-      size_type __old_sz,
-      size_type __n_copy,
-      size_type __n_del,
-      size_type __n_add = 0);
+  void __grow_by(size_type __old_cap,
+                 size_type __delta_cap,
+                 size_type __old_sz,
+                 size_type __n_copy,
+                 size_type __n_del,
+                 size_type __n_add = 0);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __grow_by_without_replace(
       size_type __old_cap,
       size_type __delta_cap,
@@ -2682,7 +2681,7 @@ void _LIBCPP_CONSTEXPR_SINCE_CXX20
 #  if _LIBCPP_ABI_VERSION >= 2 // We want to use the function in the dylib in ABIv1
 _LIBCPP_HIDE_FROM_ABI
 #  endif
-_LIBCPP_DEPRECATED_("use __grow_by_without_replace") basic_string<_CharT, _Traits, _Allocator>::__grow_by(
+basic_string<_CharT, _Traits, _Allocator>::__grow_by(
     size_type __old_cap,
     size_type __delta_cap,
     size_type __old_sz,


From libcxx-commits at lists.llvm.org  Mon Jul 13 03:07:16 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 03:07:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
Message-ID: <llvm.org/llvm/llvm-project/pull/209130@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/209130

These benchmarks are expected to run for a very short time, and `{Pause,Resume}Timing` should only be used when operations are expected to take a long time. This reduces the amount of noise in these benchmarks.


>From d718244680aa3db8a36c88204fa78bc256ff5267 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 12:05:58 +0200
Subject: [PATCH] [libc++] Remove {Pause,Resume}Timing from fast push_back
 benchmarks

---
 .../containers/sequence/sequence_container_benchmarks.h       | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
index 92346c38563fb..630da0dd62bd2 100644
--- a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
@@ -372,9 +372,7 @@ void sequence_container_benchmarks(std::string container) {
             }
             DoNotOptimizeData(c);
 
-            st.PauseTiming();
             c.erase(c.end() - BatchSize, c.end());
-            st.ResumeTiming();
           }
         });
     }
@@ -395,9 +393,7 @@ void sequence_container_benchmarks(std::string container) {
           }
           DoNotOptimizeData(c);
 
-          st.PauseTiming();
           c.clear();
-          st.ResumeTiming();
         }
       });
 


From libcxx-commits at lists.llvm.org  Mon Jul 13 03:07:41 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 03:07:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a54b8ed.2490598a.cf17c.0c82@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Mon Jul 13 03:07:54 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 03:07:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a54b8fa.6c8668f2.e4f82.edfd@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Mon Jul 13 03:09:52 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 03:09:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a54b970.266b0cc5.29d78.6272@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

These benchmarks are expected to run for a very short time, and `{Pause,Resume}Timing` should only be used when operations are expected to take a long time. Removing them reduces the amount of noise in these benchmarks.

Fixes #<!-- -->208719

---
Full diff: https://github.com/llvm/llvm-project/pull/209130.diff


1 Files Affected:

- (modified) libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h (-4) 


``````````diff
diff --git a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
index 92346c38563fb..630da0dd62bd2 100644
--- a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
@@ -372,9 +372,7 @@ void sequence_container_benchmarks(std::string container) {
             }
             DoNotOptimizeData(c);
 
-            st.PauseTiming();
             c.erase(c.end() - BatchSize, c.end());
-            st.ResumeTiming();
           }
         });
     }
@@ -395,9 +393,7 @@ void sequence_container_benchmarks(std::string container) {
           }
           DoNotOptimizeData(c);
 
-          st.PauseTiming();
           c.clear();
-          st.ResumeTiming();
         }
       });
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Mon Jul 13 04:19:29 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 04:19:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
Message-ID: <llvm.org/llvm/llvm-project/pull/209141@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/209141

None

>From 732a97e86e6c43f81620cadcf85d4ee9dbe9aceb Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 13:18:00 +0200
Subject: [PATCH] [libc++] Optimize std::is_heap_until

---
 libcxx/include/__algorithm/is_heap_until.h    | 50 ++++++++++---------
 .../is.heap/ranges_is_heap_until.pass.cpp     |  6 +++
 2 files changed, 33 insertions(+), 23 deletions(-)

diff --git a/libcxx/include/__algorithm/is_heap_until.h b/libcxx/include/__algorithm/is_heap_until.h
index 7444d978e37f5..cb5ea0373a019 100644
--- a/libcxx/include/__algorithm/is_heap_until.h
+++ b/libcxx/include/__algorithm/is_heap_until.h
@@ -12,7 +12,6 @@
 #include <__algorithm/comp.h>
 #include <__algorithm/comp_ref_type.h>
 #include <__config>
-#include <__iterator/iterator_traits.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -20,29 +19,34 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Compare, class _RandomAccessIterator>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandomAccessIterator
-__is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp) {
-  typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
-  difference_type __len      = __last - __first;
-  difference_type __p        = 0;
-  difference_type __c        = 1;
-  _RandomAccessIterator __pp = __first;
-  while (__c < __len) {
-    _RandomAccessIterator __cp = __first + __c;
-    if (__comp(*__pp, *__cp))
-      return __cp;
-    ++__c;
-    ++__cp;
-    if (__c == __len)
-      return __last;
-    if (__comp(*__pp, *__cp))
-      return __cp;
-    ++__p;
-    ++__pp;
-    __c = 2 * __p + 1;
+template <class _Compare, class _Iter>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter
+__is_heap_until(_Iter __first, _Iter __last, _Compare&& __comp) {
+  auto __count = __last - __first;
+
+  if (__count < 2)
+    return __last;
+
+  auto __parent_last = __first + (__count - 1) / 2;
+  auto __child = __first;
+  ++__child;
+  for (; __first != __parent_last; ++__first) {
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
+
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
   }
-  return __last;
+
+  if (__count % 2 == 0) {
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
+  }
+
+  return __child;
 }
 
 template <class _RandomAccessIterator, class _Compare>
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
index 5fd119a21a98f..42a66fa5723d6 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
@@ -100,6 +100,12 @@ constexpr void test_iter_sent() {
   test_one<Iter, Sent>(std::array{2, 1}, 2);
   // 2-element sequence, not a heap.
   test_one<Iter, Sent>(std::array{1, 2}, 1);
+  // 3-element sequence, a heap.
+  test_one<Iter, Sent>(std::array{3, 2, 1}, 3);
+  // 3-element sequence, LHS not a heap.
+  test_one<Iter, Sent>(std::array{2, 3, 1}, 1);
+  // 3-element sequence, RHS not a heap.
+  test_one<Iter, Sent>(std::array{2, 1, 3}, 2);
   // Longer sequence, a heap.
   test_one<Iter, Sent>(std::array{8, 6, 7, 3, 4, 1, 5, 2}, 8);
   // Longer sequence, not a heap.


From libcxx-commits at lists.llvm.org  Mon Jul 13 04:21:07 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 04:21:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a54ca23.f732ad81.3a4994.0131@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions h,cpp -- libcxx/include/__algorithm/is_heap_until.h libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/__algorithm/is_heap_until.h b/libcxx/include/__algorithm/is_heap_until.h
index cb5ea0373..725ca8aca 100644
--- a/libcxx/include/__algorithm/is_heap_until.h
+++ b/libcxx/include/__algorithm/is_heap_until.h
@@ -28,7 +28,7 @@ __is_heap_until(_Iter __first, _Iter __last, _Compare&& __comp) {
     return __last;
 
   auto __parent_last = __first + (__count - 1) / 2;
-  auto __child = __first;
+  auto __child       = __first;
   ++__child;
   for (; __first != __parent_last; ++__first) {
     if (__comp(*__first, *__child))

``````````

</details>


https://github.com/llvm/llvm-project/pull/209141

From libcxx-commits at lists.llvm.org  Mon Jul 13 04:28:17 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 04:28:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a54cbd1.d681cab9.333b2b.b661@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/209141

From libcxx-commits at lists.llvm.org  Mon Jul 13 04:28:40 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 04:28:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a54cbe8.c56fb102.7c9cd.202d@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/209141

From libcxx-commits at lists.llvm.org  Mon Jul 13 04:30:23 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 04:30:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a54cc4f.c56fb102.7c9cd.5eb8@mx.google.com>


philnik777 wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/algorithms/sorting/is_heap.bench.cpp

https://github.com/llvm/llvm-project/pull/209141

From libcxx-commits at lists.llvm.org  Mon Jul 13 05:30:04 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 05:30:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
Message-ID: <llvm.org/llvm/llvm-project/pull/209161@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/209161

Fixes #21566


>From fed852da6cb208b549915a4f0aa1fd57fe4295bd Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 14:28:02 +0200
Subject: [PATCH] [libc++] Optimize standard streams with
 sync_with_stdio(false)

Fixes #21566
---
 libcxx/include/fstream                        |  9 +-
 libcxx/src/ios.cpp                            |  7 --
 libcxx/src/iostream.cpp                       | 86 +++++++++++++++----
 .../cin.sync_with_stdio.sh.cpp                | 37 ++++++++
 .../clog.sync_with_stdio.sh.cpp               | 29 +++++++
 .../cout.sync_with_stdio.sh.cpp               | 27 ++++++
 .../wcin.sync_with_stdio.sh.cpp               | 37 ++++++++
 .../wclog.sync_with_stdio.sh.cpp              | 29 +++++++
 .../wcout.sync_with_stdio.sh.cpp              | 27 ++++++
 9 files changed, 260 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..23803909ff802 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -391,10 +391,12 @@ private:
 
   _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 
+public:
   // There are multiple (__)open function, they use different C-API open
   // function. After that call these functions behave the same. This function
   // does that part and determines the final return value.
-  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __do_open(FILE* __file, ios_base::openmode __mode) {
+  // This is also used for the various iostreams in sync_with_stdio(false) mode
+  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __adopt_file(FILE* __file, ios_base::openmode __mode) {
     __file_ = __file;
     if (!__file_)
       return nullptr;
@@ -417,6 +419,7 @@ private:
     return this;
   }
 
+private:
   // If the file is already open, switch to unbuffered mode. Otherwise, record
   // the request to use unbuffered mode so that we use that mode when we
   // eventually open the file.
@@ -742,7 +745,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(std::fopen(__s, __mdstr), __mode);
+  return __adopt_file(std::fopen(__s, __mdstr), __mode);
 }
 
 template <class _CharT, class _Traits>
@@ -753,7 +756,7 @@ inline basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::__open(in
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(fdopen(__fd, __mdstr), __mode);
+  return __adopt_file(fdopen(__fd, __mdstr), __mode);
 }
 
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index 2e049098740dc..0b728db5b76f9 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -366,13 +366,6 @@ void ios_base::__set_failbit_and_consider_rethrow() {
 #endif // _LIBCPP_HAS_EXCEPTIONS
 }
 
-bool ios_base::sync_with_stdio(bool sync) {
-  static bool previous_state = true;
-  bool r                     = previous_state;
-  previous_state             = sync;
-  return r;
-}
-
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..14835efa7a2e9 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -10,6 +10,7 @@
 
 #include <__memory/construct_at.h>
 #include <__ostream/basic_ostream.h>
+#include <fstream>
 #include <istream>
 
 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
@@ -34,21 +35,44 @@ union stream {
   StreamT value;
 };
 
-template <class StreamT, class BufferT>
+template <class StreamT, class SyncBufT, class UnsyncBufT>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
   struct {
-    BufferT buffer;
+    union {
+      SyncBufT sync_buffer;
+      UnsyncBufT unsync_buffer;
+    };
     mbstate_t mb;
   };
 };
 
-template <class StreamT, class BufferT>
-void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) {
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
   data.mb = {};
-  std::construct_at(&data.buffer, stdstream, &data.mb);
-  std::construct_at(&stream.value, &data.buffer);
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  std::construct_at(&stream.value, &data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_sync_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
+  data.unsync_buffer.__adopt_file(nullptr, {}); // reset the file, so that basic_filebuf doesn't close standard streams
+  std::destroy_at(&data.unsync_buffer);
+  data.mb = {};
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  stream.value.rdbuf(&data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_unsync_stream(FILE* stdstream,
+                             stream<StreamT>& stream,
+                             stream_data<StreamT, SyncBufT, UnsyncBufT>& data,
+                             ios_base::openmode mode) {
+  std::destroy_at(&data.sync_buffer);
+  std::construct_at(&data.unsync_buffer);
+  data.unsync_buffer.__adopt_file(stdstream, mode);
+  stream.value.rdbuf(&data.unsync_buffer);
 }
 
 #define CHAR_MANGLING_char "D"
@@ -62,29 +86,29 @@ void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT,
 #endif
 
 #ifdef _LIBCPP_ABI_MICROSOFT
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__(                                \
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12 at A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
 #endif
 
 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
 
-STREAM(basic_istream, __stdinbuf, char, cin);
-STREAM(basic_ostream, __stdoutbuf, char, cout);
-STREAM(basic_ostream, __stdoutbuf, char, cerr);
-STREAM(basic_ostream, __stdoutbuf, char, clog);
+STREAM(basic_istream, __stdinbuf, basic_filebuf, char, cin);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, char, cout);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, char, cerr);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, char, clog);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+STREAM(basic_istream, __stdinbuf, basic_filebuf, wchar_t, wcin);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, wchar_t, wcout);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, wchar_t, wcerr);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, wchar_t, wclog);
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
@@ -155,5 +179,31 @@ ios_base::Init::Init() {
 
 ios_base::Init::~Init() {}
 
+bool ios_base::sync_with_stdio(bool sync) {
+  static bool previous_state = true;
+  bool r                     = previous_state;
+
+  if (sync != previous_state) {
+    if (sync) {
+      switch_to_sync_stream(stdin, cin, cin_data);
+      switch_to_sync_stream(stdout, cout, cout_data);
+      switch_to_sync_stream(stderr, clog, clog_data);
+      switch_to_sync_stream(stdin, wcin, wcin_data);
+      switch_to_sync_stream(stdout, wcout, wcout_data);
+      switch_to_sync_stream(stderr, wclog, wclog_data);
+    } else {
+      switch_to_unsync_stream(stdin, cin, cin_data, ios::in);
+      switch_to_unsync_stream(stdout, cout, cout_data, ios::out);
+      switch_to_unsync_stream(stderr, clog, clog_data, ios::out);
+      switch_to_unsync_stream(stdin, wcin, wcin_data, ios::in);
+      switch_to_unsync_stream(stdout, wcout, wcout_data, ios::out);
+      switch_to_unsync_stream(stderr, wclog, wclog_data, ios::out);
+    }
+  }
+
+  previous_state = sync;
+  return r;
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..052356449c5ab
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::cin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..ab30dee5f695d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::clog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3a94964f09223
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::cout << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3e388982785a2
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::wcin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..30f908546b23d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wclog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..d3821d3d9ad8d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wcout << "1234";
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Mon Jul 13 05:31:10 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 05:31:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a54da8e.900092b2.37ac81.9388@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Mon Jul 13 05:54:00 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 05:54:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a54dfe8.7f17e68b.22b46a.e930@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/209161

>From ef84bb347b216cfdef55175388254f6852cf6a87 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 14:28:02 +0200
Subject: [PATCH] [libc++] Optimize standard streams with
 sync_with_stdio(false)

Fixes #21566
---
 libcxx/include/fstream                        |  9 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |  3 +
 libcxx/src/ios.cpp                            |  7 --
 libcxx/src/iostream.cpp                       | 86 +++++++++++++++----
 .../cin.sync_with_stdio.sh.cpp                | 37 ++++++++
 .../clog.sync_with_stdio.sh.cpp               | 29 +++++++
 .../cout.sync_with_stdio.sh.cpp               | 27 ++++++
 .../wcin.sync_with_stdio.sh.cpp               | 37 ++++++++
 .../wclog.sync_with_stdio.sh.cpp              | 29 +++++++
 .../wcout.sync_with_stdio.sh.cpp              | 27 ++++++
 10 files changed, 263 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..23803909ff802 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -391,10 +391,12 @@ private:
 
   _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 
+public:
   // There are multiple (__)open function, they use different C-API open
   // function. After that call these functions behave the same. This function
   // does that part and determines the final return value.
-  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __do_open(FILE* __file, ios_base::openmode __mode) {
+  // This is also used for the various iostreams in sync_with_stdio(false) mode
+  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __adopt_file(FILE* __file, ios_base::openmode __mode) {
     __file_ = __file;
     if (!__file_)
       return nullptr;
@@ -417,6 +419,7 @@ private:
     return this;
   }
 
+private:
   // If the file is already open, switch to unbuffered mode. Otherwise, record
   // the request to use unbuffered mode so that we use that mode when we
   // eventually open the file.
@@ -742,7 +745,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(std::fopen(__s, __mdstr), __mode);
+  return __adopt_file(std::fopen(__s, __mdstr), __mode);
 }
 
 template <class _CharT, class _Traits>
@@ -753,7 +756,7 @@ inline basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::__open(in
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(fdopen(__fd, __mdstr), __mode);
+  return __adopt_file(fdopen(__fd, __mdstr), __mode);
 }
 
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..903457e983046 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1643,6 +1643,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1777,6 +1778,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1915,6 +1917,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index 2e049098740dc..0b728db5b76f9 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -366,13 +366,6 @@ void ios_base::__set_failbit_and_consider_rethrow() {
 #endif // _LIBCPP_HAS_EXCEPTIONS
 }
 
-bool ios_base::sync_with_stdio(bool sync) {
-  static bool previous_state = true;
-  bool r                     = previous_state;
-  previous_state             = sync;
-  return r;
-}
-
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..14835efa7a2e9 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -10,6 +10,7 @@
 
 #include <__memory/construct_at.h>
 #include <__ostream/basic_ostream.h>
+#include <fstream>
 #include <istream>
 
 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
@@ -34,21 +35,44 @@ union stream {
   StreamT value;
 };
 
-template <class StreamT, class BufferT>
+template <class StreamT, class SyncBufT, class UnsyncBufT>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
   struct {
-    BufferT buffer;
+    union {
+      SyncBufT sync_buffer;
+      UnsyncBufT unsync_buffer;
+    };
     mbstate_t mb;
   };
 };
 
-template <class StreamT, class BufferT>
-void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) {
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
   data.mb = {};
-  std::construct_at(&data.buffer, stdstream, &data.mb);
-  std::construct_at(&stream.value, &data.buffer);
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  std::construct_at(&stream.value, &data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_sync_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
+  data.unsync_buffer.__adopt_file(nullptr, {}); // reset the file, so that basic_filebuf doesn't close standard streams
+  std::destroy_at(&data.unsync_buffer);
+  data.mb = {};
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  stream.value.rdbuf(&data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_unsync_stream(FILE* stdstream,
+                             stream<StreamT>& stream,
+                             stream_data<StreamT, SyncBufT, UnsyncBufT>& data,
+                             ios_base::openmode mode) {
+  std::destroy_at(&data.sync_buffer);
+  std::construct_at(&data.unsync_buffer);
+  data.unsync_buffer.__adopt_file(stdstream, mode);
+  stream.value.rdbuf(&data.unsync_buffer);
 }
 
 #define CHAR_MANGLING_char "D"
@@ -62,29 +86,29 @@ void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT,
 #endif
 
 #ifdef _LIBCPP_ABI_MICROSOFT
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__(                                \
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12 at A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
 #endif
 
 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
 
-STREAM(basic_istream, __stdinbuf, char, cin);
-STREAM(basic_ostream, __stdoutbuf, char, cout);
-STREAM(basic_ostream, __stdoutbuf, char, cerr);
-STREAM(basic_ostream, __stdoutbuf, char, clog);
+STREAM(basic_istream, __stdinbuf, basic_filebuf, char, cin);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, char, cout);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, char, cerr);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, char, clog);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+STREAM(basic_istream, __stdinbuf, basic_filebuf, wchar_t, wcin);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, wchar_t, wcout);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, wchar_t, wcerr);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, wchar_t, wclog);
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
@@ -155,5 +179,31 @@ ios_base::Init::Init() {
 
 ios_base::Init::~Init() {}
 
+bool ios_base::sync_with_stdio(bool sync) {
+  static bool previous_state = true;
+  bool r                     = previous_state;
+
+  if (sync != previous_state) {
+    if (sync) {
+      switch_to_sync_stream(stdin, cin, cin_data);
+      switch_to_sync_stream(stdout, cout, cout_data);
+      switch_to_sync_stream(stderr, clog, clog_data);
+      switch_to_sync_stream(stdin, wcin, wcin_data);
+      switch_to_sync_stream(stdout, wcout, wcout_data);
+      switch_to_sync_stream(stderr, wclog, wclog_data);
+    } else {
+      switch_to_unsync_stream(stdin, cin, cin_data, ios::in);
+      switch_to_unsync_stream(stdout, cout, cout_data, ios::out);
+      switch_to_unsync_stream(stderr, clog, clog_data, ios::out);
+      switch_to_unsync_stream(stdin, wcin, wcin_data, ios::in);
+      switch_to_unsync_stream(stdout, wcout, wcout_data, ios::out);
+      switch_to_unsync_stream(stderr, wclog, wclog_data, ios::out);
+    }
+  }
+
+  previous_state = sync;
+  return r;
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..052356449c5ab
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::cin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..ab30dee5f695d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::clog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3a94964f09223
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::cout << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3e388982785a2
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::wcin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..30f908546b23d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wclog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..d3821d3d9ad8d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wcout << "1234";
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Mon Jul 13 05:56:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 05:56:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][RFC] Rewrite CPOs with resolver
	functions (PR #209104)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209104@github.com>
Message-ID: <6a54e06e.2490598a.cf17c.d771@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/209104

>From 798a8f4223b82de7d5a198c97ed09fe0b84e3a86 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 10:04:56 +0200
Subject: [PATCH] [libc++][RFC] Rewrite CPOs with resolver functions

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/access.h              | 179 ++++++++++--------
 libcxx/include/__ranges/data.h                |  64 +++++--
 libcxx/include/__utility/cpo.h                |  37 ++++
 libcxx/include/module.modulemap.in            |   1 +
 .../std/ranges/range.access/begin.verify.cpp  |   4 +-
 .../std/ranges/range.access/end.verify.cpp    |   4 +-
 7 files changed, 184 insertions(+), 106 deletions(-)
 create mode 100644 libcxx/include/__utility/cpo.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..19a4b53dd760f 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -934,6 +934,7 @@ set(files
   __utility/cmp.h
   __utility/constant_wrapper.h
   __utility/convert_to_integral.h
+  __utility/cpo.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
   __utility/element_count.h
diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index 28555ef6ba0d9..29459abf88882 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -12,15 +12,17 @@
 
 #include <__concepts/class_or_enum.h>
 #include <__config>
-#include <__cstddef/size_t.h>
 #include <__iterator/concepts.h>
-#include <__iterator/readable_traits.h>
 #include <__ranges/enable_borrowed_range.h>
-#include <__type_traits/decay.h>
+#include <__type_traits/extent.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_reference.h>
+#include <__type_traits/remove_all_extents.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_reference.h>
 #include <__utility/auto_cast.h>
+#include <__utility/cpo.h>
 #include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -40,50 +42,50 @@ concept __can_borrow = is_lvalue_reference_v<_Tp> || enable_borrowed_range<remov
 
 namespace ranges {
 namespace __begin {
-template <class _Tp>
-concept __member_begin = __can_borrow<_Tp> && requires(_Tp&& __t) {
-  { _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator;
-};
-
 void begin() = delete;
 
-template <class _Tp>
-concept __unqualified_begin =
-    !__member_begin<_Tp> && __can_borrow<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { _LIBCPP_AUTO_CAST(begin(__t)) } -> input_or_output_iterator;
-    };
-
-struct __fn {
-  template <class _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[]) const noexcept
-    requires(sizeof(_Tp) >= 0) // Disallow incomplete element types.
-  {
-    return __t + 0;
+struct __fn : _CPO<[]<class _Ep> consteval noexcept {
+  // [range.access.begin]
+
+  // Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. Then:
+  using _Tp = remove_reference_t<_Ep>;
+
+  // If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false, ranges​::​begin(E) is ill-formed.
+  if constexpr (is_rvalue_reference_v<_Ep> && !enable_borrowed_range<remove_cv_t<_Tp>>) {
+    return;
+
+    // Otherwise, if T is an array type ([dcl.array]) and remove_all_extents_t<T> is an incomplete type,
+    // ranges​::​begin(E) is ill-formed with no diagnostic required.
+  } else if constexpr (is_array_v<_Tp>) {
+    if constexpr (!requires { sizeof(remove_all_extents_t<_Tp>); })
+      return;
+    else
+      // Otherwise, if T is an array type, ranges​::​begin(E) is expression-equivalent to t + 0.
+      return [](_Ep __v) noexcept { return __v + 0; };
+
+    // Otherwise, if auto(t.begin()) is a valid expression whose type models input_or_output_iterator,
+    // ranges​::​begin(E) is expression-equivalent to auto(t.begin()).
+  } else if constexpr (requires(_Tp& __t) {
+                         { _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(__v.begin()))) { return _LIBCPP_AUTO_CAST(__v.begin()); };
+
+    // Otherwise, if T is a class or enumeration type and auto(begin(t)) is a valid expression whose type models
+    // input_or_output_iterator where the meaning of begin is established as-if by performing argument-dependent lookup
+    // only ([basic.lookup.argdep]), then ranges​::​begin(E) is expression-equivalent to that expression.
+  } else if constexpr (__class_or_enum<_Tp>) {
+    if constexpr (requires(_Tp& __t) {
+                    { _LIBCPP_AUTO_CAST(begin(__t)) } -> input_or_output_iterator;
+                  })
+      return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(begin(__v)))) { return _LIBCPP_AUTO_CAST(begin(__v)); };
+    else
+      return;
+
+    // Otherwise, ranges​::​begin(E) is ill-formed.
+  } else {
+    return;
   }
-
-  template <class _Tp, size_t _Np>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept
-    requires(sizeof(_Tp) >= 0) // Disallow incomplete element types.
-  {
-    return __t + 0;
-  }
-
-  template <class _Tp>
-    requires __member_begin<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.begin()))) {
-    return _LIBCPP_AUTO_CAST(__t.begin());
-  }
-
-  template <class _Tp>
-    requires __unqualified_begin<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(begin(__t)))) {
-    return _LIBCPP_AUTO_CAST(begin(__t));
-  }
-
-  void operator()(auto&&) const = delete;
-};
+}> {};
 } // namespace __begin
 
 inline namespace __cpo {
@@ -102,45 +104,53 @@ using iterator_t = decltype(ranges::begin(std::declval<_Tp&>()));
 
 namespace ranges {
 namespace __end {
-template <class _Tp>
-concept __member_end = __can_borrow<_Tp> && requires(_Tp&& __t) {
-  typename iterator_t<_Tp>;
-  { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for<iterator_t<_Tp>>;
-};
-
-void end() = delete;
-
-template <class _Tp>
-concept __unqualified_end =
-    !__member_end<_Tp> && __can_borrow<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      typename iterator_t<_Tp>;
-      { _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for<iterator_t<_Tp>>;
-    };
-
-struct __fn {
-  template <class _Tp, size_t _Np>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept
-    requires(sizeof(_Tp) >= 0) // Disallow incomplete element types.
-  {
-    return __t + _Np;
-  }
-
-  template <class _Tp>
-    requires __member_end<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.end()))) {
-    return _LIBCPP_AUTO_CAST(__t.end());
+struct __fn : _CPO<[]<class _Ep> consteval noexcept {
+  // [range.access.end]
+
+  // Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. Then:
+  using _Tp = remove_reference_t<_Ep>;
+
+  // If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false, ranges​::​end(E) is ill-formed.
+  if constexpr (is_rvalue_reference_v<_Ep> && !enable_borrowed_range<remove_cv_t<_Tp>>) {
+    return;
+
+    // Otherwise, if T is an array type ([dcl.array]) and remove_all_extents_t<T> is an incomplete type,
+    // ranges​::​end(E) is ill-formed with no diagnostic required.
+  } else if constexpr (is_array_v<_Tp>) {
+    if constexpr (!requires { sizeof(remove_all_extents_t<_Tp>); }) {
+      return;
+
+      // Otherwise, if T is an array of unknown bound, ranges​::​end(E) is ill-formed.
+    } else if constexpr (is_unbounded_array_v<_Tp>) {
+      return;
+
+      // Otherwise, if T is an array, ranges​::​end(E) is expression-equivalent to t + extent_v<T>.
+    } else {
+      return [](_Ep __v) noexcept { return __v + extent_v<_Tp>; };
+    }
+
+    // Otherwise, if auto(t.end()) is a valid expression whose type models sentinel_for<iterator_t<T>> then
+    // ranges​::​end(E) is expression-equivalent to auto(t.end()).
+  } else if constexpr (requires(_Tp& __t) {
+                         { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for<iterator_t<_Tp>>;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(__v.end()))) { return _LIBCPP_AUTO_CAST(__v.end()); };
+
+    // Otherwise, if T is a class or enumeration type and auto(end(t)) is a valid expression whose type models
+    // sentinel_for<iterator_t<T>> where the meaning of end is established as-if by performing argument-dependent lookup
+    // only ([basic.lookup.argdep]), then ranges​::​end(E) is expression-equivalent to that expression.
+  } else if constexpr (__class_or_enum<_Tp>) {
+    if constexpr (requires(_Tp& __t) {
+                    { _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for<iterator_t<_Tp>>;
+                  }) {
+      return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(end(__v)))) { return _LIBCPP_AUTO_CAST(end(__v)); };
+    } else {
+      return;
+    }
+  } else {
+    return;
   }
-
-  template <class _Tp>
-    requires __unqualified_end<_Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(_LIBCPP_AUTO_CAST(end(__t)))) {
-    return _LIBCPP_AUTO_CAST(end(__t));
-  }
-
-  void operator()(auto&&) const = delete;
-};
+}> {};
 } // namespace __end
 
 inline namespace __cpo {
@@ -191,8 +201,9 @@ struct __fn {
 
   template <class _Tp>
     requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
-      noexcept(ranges::end(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::end(static_cast<const _Tp&&>(__t))))
+          -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
     return ranges::end(static_cast<const _Tp&&>(__t));
   }
 };
diff --git a/libcxx/include/__ranges/data.h b/libcxx/include/__ranges/data.h
index 354a0aa15903e..66e057d645573 100644
--- a/libcxx/include/__ranges/data.h
+++ b/libcxx/include/__ranges/data.h
@@ -10,19 +10,21 @@
 #ifndef _LIBCPP___RANGES_DATA_H
 #define _LIBCPP___RANGES_DATA_H
 
-#include <__concepts/class_or_enum.h>
 #include <__config>
 #include <__iterator/concepts.h>
-#include <__iterator/iterator_traits.h>
 #include <__memory/pointer_traits.h>
 #include <__ranges/access.h>
-#include <__type_traits/decay.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_reference.h>
+#include <__type_traits/remove_all_extents.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
 #include <__utility/auto_cast.h>
+#include <__utility/cpo.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -49,18 +51,47 @@ concept __ranges_begin_invocable = !__member_data<_Tp> && __can_borrow<_Tp> && r
   { ranges::begin(__t) } -> contiguous_iterator;
 };
 
-struct __fn {
-  template <__member_data _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(noexcept(__t.data())) {
-    return __t.data();
-  }
-
-  template <__ranges_begin_invocable _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(std::to_address(ranges::begin(__t)))) {
-    return std::to_address(ranges::begin(__t));
+struct __fn : _CPO<[]<class _Ep> consteval noexcept {
+  // [range.prim.data]
+
+  // Given a subexpression E with type T, let t be an lvalue that denotes the reified object for E. Then:
+  using _Tp = remove_reference_t<_Ep>;
+
+  // If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false, ranges​::​data(E) is ill-formed.
+  if constexpr (is_rvalue_reference_v<_Ep> && !enable_borrowed_range<remove_cv_t<_Tp>>) {
+    return;
+
+    // Otherwise, if T is an array type ([dcl.array]) and remove_all_extents_t<T> is an incomplete type,
+    // ranges​::​data(E) is ill-formed with no diagnostic required.
+  } else if constexpr (is_array_v<_Tp>) {
+    if constexpr (!requires { sizeof(remove_all_extents_t<_Tp>); }) {
+      return;
+    } else {
+      // This is inlined from ranges::begin
+      return [](_Ep __v) noexcept { return __v + 0; };
+    }
+
+    // Otherwise, if auto(t.data()) is a valid expression of pointer to object type,
+    // ranges​::​data(E) is expression-equivalent to auto(t.data()).
+  } else if constexpr (requires(_Tp& __t) {
+                         { _LIBCPP_AUTO_CAST(__t.data()) } -> __ptr_to_object;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(_LIBCPP_AUTO_CAST(__v.data()))) { return _LIBCPP_AUTO_CAST(__v.data()); };
+
+    // Otherwise, if ranges​::​begin(t) is a valid expression whose type models contiguous_iterator,
+    // ranges​::​data(E) is expression-equivalent to to_address(ranges​::​begin(t)).
+  } else if constexpr (requires(_Tp& __t) {
+                         { ranges::begin(__t) } -> contiguous_iterator;
+                       }) {
+    return [](_Ep __v) noexcept(noexcept(std::to_address(ranges::begin(__v)))) {
+      return std::to_address(ranges::begin(__v));
+    };
+
+    // Otherwise, ranges​::​data(E) is ill-formed.
+  } else {
+    return;
   }
-};
+}> {};
 } // namespace __data
 
 inline namespace __cpo {
@@ -83,8 +114,9 @@ struct __fn {
 
   template <class _Tp>
     requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
-      noexcept(ranges::data(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::data(static_cast<const _Tp&&>(__t))) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::data(static_cast<const _Tp&&>(__t))))
+          -> decltype(ranges::data(static_cast<const _Tp&&>(__t))) {
     return ranges::data(static_cast<const _Tp&&>(__t));
   }
 };
diff --git a/libcxx/include/__utility/cpo.h b/libcxx/include/__utility/cpo.h
new file mode 100644
index 0000000000000..5ec4c62abd704
--- /dev/null
+++ b/libcxx/include/__utility/cpo.h
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_CPO_H
+#define _LIBCPP___UTILITY_CPO_H
+
+#include <__config>
+#include <__utility/forward.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 20
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <auto __resolver>
+struct _CPO {
+  template <class... _Args>
+  [[nodiscard]] static constexpr auto operator()(_Args&&... __args) noexcept(
+      noexcept(__resolver.template operator()<_Args&&...>()(std::forward<_Args>(__args)...)))
+      -> decltype(__resolver.template operator()<_Args&&...>()(std::forward<_Args>(__args)...)) {
+    return __resolver.template operator()<_Args&&...>()(std::forward<_Args>(__args)...);
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+
+#endif // _LIBCPP___UTILITY_CPO_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..df9777b470916 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2207,6 +2207,7 @@ module std {
     module cmp                             { header "__utility/cmp.h" }
     module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
+    module cpo                             { header "__utility/cpo.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
     module element_count                   { header "__utility/element_count.h" }
     module exception_guard                 { header "__utility/exception_guard.h" }
diff --git a/libcxx/test/std/ranges/range.access/begin.verify.cpp b/libcxx/test/std/ranges/range.access/begin.verify.cpp
index 433404b4ad277..df6fce0402215 100644
--- a/libcxx/test/std/ranges/range.access/begin.verify.cpp
+++ b/libcxx/test/std/ranges/range.access/begin.verify.cpp
@@ -20,7 +20,5 @@ static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
 
 // Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::begin` is ill-formed.
 void test() {
-  std::ranges::begin(NonBorrowedRange());
-  // expected-error-re at -1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__begin::__fn'}}}}
-  // expected-error at -2  {{attempt to use a deleted function}}
+  std::ranges::begin(NonBorrowedRange()); // expected-error {{no matching function for call to object of type 'const __begin::__fn'}}
 }
diff --git a/libcxx/test/std/ranges/range.access/end.verify.cpp b/libcxx/test/std/ranges/range.access/end.verify.cpp
index a4a7f78b8c6b1..30cbd85243f24 100644
--- a/libcxx/test/std/ranges/range.access/end.verify.cpp
+++ b/libcxx/test/std/ranges/range.access/end.verify.cpp
@@ -20,7 +20,5 @@ static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
 
 // Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::end` is ill-formed.
 void test() {
-  std::ranges::end(NonBorrowedRange());
-  // expected-error-re at -1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__end::__fn'}}}}
-  // expected-error at -2  {{attempt to use a deleted function}}
+  std::ranges::end(NonBorrowedRange()); // expected-error {{no matching function for call to object of type 'const __end::__fn'}}
 }


From libcxx-commits at lists.llvm.org  Mon Jul 13 05:56:40 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 05:56:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Remove unused includes from
 <__iterator/segmented_iterator.h> (PR #209105)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209105@github.com>
Message-ID: <6a54e088.824e11a6.f5f27.892b@mx.google.com>


https://github.com/philnik777 ready_for_review https://github.com/llvm/llvm-project/pull/209105

From libcxx-commits at lists.llvm.org  Mon Jul 13 05:56:42 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 05:56:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 0f69d28 - [libc++][NFC] Remove unused
 includes from <__iterator/segmented_iterator.h> (#209105)
Message-ID: <6a54e08a.bb839515.27d21e.2db1@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-13T14:56:38+02:00
New Revision: 0f69d2804b9b2542fde2ecafbb23f1e8a8b48d86

URL: https://github.com/llvm/llvm-project/commit/0f69d2804b9b2542fde2ecafbb23f1e8a8b48d86
DIFF: https://github.com/llvm/llvm-project/commit/0f69d2804b9b2542fde2ecafbb23f1e8a8b48d86.diff

LOG: [libc++][NFC] Remove unused includes from <__iterator/segmented_iterator.h> (#209105)

Added: 
    

Modified: 
    libcxx/include/__iterator/segmented_iterator.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__iterator/segmented_iterator.h b/libcxx/include/__iterator/segmented_iterator.h
index dc56a740130b5..149092f3135cc 100644
--- a/libcxx/include/__iterator/segmented_iterator.h
+++ b/libcxx/include/__iterator/segmented_iterator.h
@@ -42,8 +42,6 @@
 
 #include <__config>
 #include <__cstddef/size_t.h>
-#include <__iterator/iterator_traits.h>
-#include <__type_traits/integral_constant.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header


        

From libcxx-commits at lists.llvm.org  Mon Jul 13 05:56:45 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 05:56:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Remove unused includes from
 <__iterator/segmented_iterator.h> (PR #209105)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209105@github.com>
Message-ID: <6a54e08d.c574b9b4.21ead0.2e1e@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/209105

From libcxx-commits at lists.llvm.org  Mon Jul 13 05:57:14 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 05:57:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Remove unused includes from
 <__iterator/segmented_iterator.h> (PR #209105)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209105@github.com>
Message-ID: <6a54e0aa.a1accbb4.244acd.6383@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/209105.diff


1 Files Affected:

- (modified) libcxx/include/__iterator/segmented_iterator.h (-2) 


``````````diff
diff --git a/libcxx/include/__iterator/segmented_iterator.h b/libcxx/include/__iterator/segmented_iterator.h
index dc56a740130b5..149092f3135cc 100644
--- a/libcxx/include/__iterator/segmented_iterator.h
+++ b/libcxx/include/__iterator/segmented_iterator.h
@@ -42,8 +42,6 @@
 
 #include <__config>
 #include <__cstddef/size_t.h>
-#include <__iterator/iterator_traits.h>
-#include <__type_traits/integral_constant.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header

``````````

</details>


https://github.com/llvm/llvm-project/pull/209105

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:00:03 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 06:00:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a54e153.8d4c1b95.5650.4c87@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/209141

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:06:46 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 06:06:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a54e2e6.510f576d.1b3cb9.1cc0@mx.google.com>


https://github.com/philnik777 ready_for_review https://github.com/llvm/llvm-project/pull/209141

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:08:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 06:08:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a54e33e.3ff6034f.2829c3.c1a8@mx.google.com>


https://github.com/philnik777 ready_for_review https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:10:13 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 06:10:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a54e3b5.5fe1956f.a5980.a05f@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

Fixes #<!-- -->208299

---
Full diff: https://github.com/llvm/llvm-project/pull/209141.diff


2 Files Affected:

- (modified) libcxx/include/__algorithm/is_heap_until.h (+27-23) 
- (modified) libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp (+6) 


``````````diff
diff --git a/libcxx/include/__algorithm/is_heap_until.h b/libcxx/include/__algorithm/is_heap_until.h
index 7444d978e37f5..cb5ea0373a019 100644
--- a/libcxx/include/__algorithm/is_heap_until.h
+++ b/libcxx/include/__algorithm/is_heap_until.h
@@ -12,7 +12,6 @@
 #include <__algorithm/comp.h>
 #include <__algorithm/comp_ref_type.h>
 #include <__config>
-#include <__iterator/iterator_traits.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -20,29 +19,34 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Compare, class _RandomAccessIterator>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandomAccessIterator
-__is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp) {
-  typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
-  difference_type __len      = __last - __first;
-  difference_type __p        = 0;
-  difference_type __c        = 1;
-  _RandomAccessIterator __pp = __first;
-  while (__c < __len) {
-    _RandomAccessIterator __cp = __first + __c;
-    if (__comp(*__pp, *__cp))
-      return __cp;
-    ++__c;
-    ++__cp;
-    if (__c == __len)
-      return __last;
-    if (__comp(*__pp, *__cp))
-      return __cp;
-    ++__p;
-    ++__pp;
-    __c = 2 * __p + 1;
+template <class _Compare, class _Iter>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter
+__is_heap_until(_Iter __first, _Iter __last, _Compare&& __comp) {
+  auto __count = __last - __first;
+
+  if (__count < 2)
+    return __last;
+
+  auto __parent_last = __first + (__count - 1) / 2;
+  auto __child = __first;
+  ++__child;
+  for (; __first != __parent_last; ++__first) {
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
+
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
   }
-  return __last;
+
+  if (__count % 2 == 0) {
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
+  }
+
+  return __child;
 }
 
 template <class _RandomAccessIterator, class _Compare>
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
index 5fd119a21a98f..42a66fa5723d6 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
@@ -100,6 +100,12 @@ constexpr void test_iter_sent() {
   test_one<Iter, Sent>(std::array{2, 1}, 2);
   // 2-element sequence, not a heap.
   test_one<Iter, Sent>(std::array{1, 2}, 1);
+  // 3-element sequence, a heap.
+  test_one<Iter, Sent>(std::array{3, 2, 1}, 3);
+  // 3-element sequence, LHS not a heap.
+  test_one<Iter, Sent>(std::array{2, 3, 1}, 1);
+  // 3-element sequence, RHS not a heap.
+  test_one<Iter, Sent>(std::array{2, 1, 3}, 2);
   // Longer sequence, a heap.
   test_one<Iter, Sent>(std::array{8, 6, 7, 3, 4, 1, 5, 2}, 8);
   // Longer sequence, not a heap.

``````````

</details>


https://github.com/llvm/llvm-project/pull/209141

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:12:27 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 06:12:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a54e43b.82d06577.0dff.42e2@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

This updates the standard streams so that the buffers are replaced with `basic_filebuf`s.

Fixes #<!-- -->21566


---
Full diff: https://github.com/llvm/llvm-project/pull/209161.diff


10 Files Affected:

- (modified) libcxx/include/fstream (+6-3) 
- (modified) libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist (+3) 
- (modified) libcxx/src/ios.cpp (-7) 
- (modified) libcxx/src/iostream.cpp (+68-18) 
- (added) libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp (+37) 
- (added) libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp (+29) 
- (added) libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp (+27) 
- (added) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp (+37) 
- (added) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp (+29) 
- (added) libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp (+27) 


``````````diff
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..23803909ff802 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -391,10 +391,12 @@ private:
 
   _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 
+public:
   // There are multiple (__)open function, they use different C-API open
   // function. After that call these functions behave the same. This function
   // does that part and determines the final return value.
-  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __do_open(FILE* __file, ios_base::openmode __mode) {
+  // This is also used for the various iostreams in sync_with_stdio(false) mode
+  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __adopt_file(FILE* __file, ios_base::openmode __mode) {
     __file_ = __file;
     if (!__file_)
       return nullptr;
@@ -417,6 +419,7 @@ private:
     return this;
   }
 
+private:
   // If the file is already open, switch to unbuffered mode. Otherwise, record
   // the request to use unbuffered mode so that we use that mode when we
   // eventually open the file.
@@ -742,7 +745,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(std::fopen(__s, __mdstr), __mode);
+  return __adopt_file(std::fopen(__s, __mdstr), __mode);
 }
 
 template <class _CharT, class _Traits>
@@ -753,7 +756,7 @@ inline basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::__open(in
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(fdopen(__fd, __mdstr), __mode);
+  return __adopt_file(fdopen(__fd, __mdstr), __mode);
 }
 
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..903457e983046 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1643,6 +1643,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1777,6 +1778,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1915,6 +1917,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index 2e049098740dc..0b728db5b76f9 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -366,13 +366,6 @@ void ios_base::__set_failbit_and_consider_rethrow() {
 #endif // _LIBCPP_HAS_EXCEPTIONS
 }
 
-bool ios_base::sync_with_stdio(bool sync) {
-  static bool previous_state = true;
-  bool r                     = previous_state;
-  previous_state             = sync;
-  return r;
-}
-
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..14835efa7a2e9 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -10,6 +10,7 @@
 
 #include <__memory/construct_at.h>
 #include <__ostream/basic_ostream.h>
+#include <fstream>
 #include <istream>
 
 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
@@ -34,21 +35,44 @@ union stream {
   StreamT value;
 };
 
-template <class StreamT, class BufferT>
+template <class StreamT, class SyncBufT, class UnsyncBufT>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
   struct {
-    BufferT buffer;
+    union {
+      SyncBufT sync_buffer;
+      UnsyncBufT unsync_buffer;
+    };
     mbstate_t mb;
   };
 };
 
-template <class StreamT, class BufferT>
-void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) {
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
   data.mb = {};
-  std::construct_at(&data.buffer, stdstream, &data.mb);
-  std::construct_at(&stream.value, &data.buffer);
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  std::construct_at(&stream.value, &data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_sync_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
+  data.unsync_buffer.__adopt_file(nullptr, {}); // reset the file, so that basic_filebuf doesn't close standard streams
+  std::destroy_at(&data.unsync_buffer);
+  data.mb = {};
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  stream.value.rdbuf(&data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_unsync_stream(FILE* stdstream,
+                             stream<StreamT>& stream,
+                             stream_data<StreamT, SyncBufT, UnsyncBufT>& data,
+                             ios_base::openmode mode) {
+  std::destroy_at(&data.sync_buffer);
+  std::construct_at(&data.unsync_buffer);
+  data.unsync_buffer.__adopt_file(stdstream, mode);
+  stream.value.rdbuf(&data.unsync_buffer);
 }
 
 #define CHAR_MANGLING_char "D"
@@ -62,29 +86,29 @@ void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT,
 #endif
 
 #ifdef _LIBCPP_ABI_MICROSOFT
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__(                                \
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12 at A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
 #endif
 
 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
 
-STREAM(basic_istream, __stdinbuf, char, cin);
-STREAM(basic_ostream, __stdoutbuf, char, cout);
-STREAM(basic_ostream, __stdoutbuf, char, cerr);
-STREAM(basic_ostream, __stdoutbuf, char, clog);
+STREAM(basic_istream, __stdinbuf, basic_filebuf, char, cin);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, char, cout);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, char, cerr);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, char, clog);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+STREAM(basic_istream, __stdinbuf, basic_filebuf, wchar_t, wcin);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, wchar_t, wcout);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, wchar_t, wcerr);
+STREAM(basic_ostream, __stdoutbuf, basic_filebuf, wchar_t, wclog);
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
@@ -155,5 +179,31 @@ ios_base::Init::Init() {
 
 ios_base::Init::~Init() {}
 
+bool ios_base::sync_with_stdio(bool sync) {
+  static bool previous_state = true;
+  bool r                     = previous_state;
+
+  if (sync != previous_state) {
+    if (sync) {
+      switch_to_sync_stream(stdin, cin, cin_data);
+      switch_to_sync_stream(stdout, cout, cout_data);
+      switch_to_sync_stream(stderr, clog, clog_data);
+      switch_to_sync_stream(stdin, wcin, wcin_data);
+      switch_to_sync_stream(stdout, wcout, wcout_data);
+      switch_to_sync_stream(stderr, wclog, wclog_data);
+    } else {
+      switch_to_unsync_stream(stdin, cin, cin_data, ios::in);
+      switch_to_unsync_stream(stdout, cout, cout_data, ios::out);
+      switch_to_unsync_stream(stderr, clog, clog_data, ios::out);
+      switch_to_unsync_stream(stdin, wcin, wcin_data, ios::in);
+      switch_to_unsync_stream(stdout, wcout, wcout_data, ios::out);
+      switch_to_unsync_stream(stderr, wclog, wclog_data, ios::out);
+    }
+  }
+
+  previous_state = sync;
+  return r;
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..052356449c5ab
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::cin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..ab30dee5f695d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::clog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3a94964f09223
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::cout << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3e388982785a2
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::wcin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..30f908546b23d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wclog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..d3821d3d9ad8d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wcout << "1234";
+  return 0;
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:12:30 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Mon, 13 Jul 2026 06:12:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0493R5: Atomic
	minimum/maximum (PR #180333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/180333@github.com>
Message-ID: <6a54e43e.dd82a9c3.253c1c.82fd@mx.google.com>


================
@@ -259,6 +261,77 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
       std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
 }
 
+// Clang's __c11_atomic_fetch_max/min builtins do not accept pointer
+// arguments, so dispatch to a CAS loop for pointer types.
+// TODO: Use the builtin for pointer types once Clang accepts them.
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
----------------
c8ef wrote:

> if one day clang adds the support for pointer fetch min/max, we don't need to change any code here as the concept will detect it

On second thought, this also means we currently lack test coverage for the built-in branch guarded by `if constexpr`. Because of this, I’d prefer to hold off on adding it for now. We can add it manually once our CI includes compilers that actually support it.


https://github.com/llvm/llvm-project/pull/180333

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:13:27 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Mon, 13 Jul 2026 06:13:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a54e477.f732ad81.3a4994.1d87@mx.google.com>


https://github.com/boomanaiden154 updated https://github.com/llvm/llvm-project/pull/208945

>From 20c1f414a5514766969069b9e42764db3b48f288 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sat, 11 Jul 2026 19:50:43 +0000
Subject: [PATCH 1/4] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h

Deprecated in 1bcf74006bcf528d14377173dba459759d35c961. Causes warnings
during compilation and will presumably become an error.
---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp      | 2 +-
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp        | 2 +-
 .../tools/clang_tidy_checks/header_exportable_declarations.cpp  | 2 +-
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp           | 2 +-
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp           | 2 +-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp      | 2 +-
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp     | 2 +-
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp       | 2 +-
 8 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index f1866179a3b68..64dca4a08d62f 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "abi_tag_on_virtual.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index e176b29670ce9..7ccf6e3a910af 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "empty_namespaces.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 8ec029f7dba2d..24f8c1bf8caf3 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "llvm/ADT/ArrayRef.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index f05470e47d3e7..cb0c6a33b8498 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "hide_from_abi.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index b6298eefdeb44..b94989ca8c788 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "abi_tag_on_virtual.hpp"
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..a7e3f547d3ed8 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "robust_against_adl.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index a608954aa2cc7..e94c95561f92f 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 24bacde6304bc..aa325d9ba378f 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "uglify_attributes.hpp"
 #include "utilities.hpp"

>From b39d6dcc1677d373375ea74079991b674d73d5c1 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sun, 12 Jul 2026 19:53:28 +0000
Subject: [PATCH 2/4] fix

---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp    | 4 ++++
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp      | 4 ++++
 .../clang_tidy_checks/header_exportable_declarations.cpp      | 4 ++++
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp         | 4 ++++
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp         | 4 +++-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp    | 4 ++++
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp   | 4 ++++
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp     | 4 ++++
 8 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index 64dca4a08d62f..a45cc441943b8 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "abi_tag_on_virtual.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index 7ccf6e3a910af..5ea8b89bf6fc8 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "empty_namespaces.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 24f8c1bf8caf3..66d75795dbf0f 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "llvm/ADT/ArrayRef.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index cb0c6a33b8498..75c9fe06b2420 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "hide_from_abi.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index b94989ca8c788..4724c04c08fd8 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,7 +7,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
-#include "clang-tidy/ClangTidyModule.h"
+#if CLANG_VERSION_MAJOR < 23
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "abi_tag_on_virtual.hpp"
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index a7e3f547d3ed8..b1d1377e38d4c 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "robust_against_adl.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index e94c95561f92f..3a60327d3117b 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index aa325d9ba378f..1b98f187c7c49 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "uglify_attributes.hpp"
 #include "utilities.hpp"

>From f1cbae15a1ca638433098c7ef6251cf0b26f83c8 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sun, 12 Jul 2026 20:49:48 +0000
Subject: [PATCH 3/4] formatting

---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp    | 4 ++--
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp      | 4 ++--
 .../clang_tidy_checks/header_exportable_declarations.cpp      | 4 ++--
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp         | 4 ++--
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp         | 2 +-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp    | 4 ++--
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp   | 4 ++--
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp     | 4 ++--
 8 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index a45cc441943b8..16445b3b86f23 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "abi_tag_on_virtual.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index 5ea8b89bf6fc8..53dc4b74a965f 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 66d75795dbf0f..bec3c1d2cd120 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "llvm/ADT/ArrayRef.h"
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index 75c9fe06b2420..b4b3f0b9cfa68 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "hide_from_abi.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index 4724c04c08fd8..93d195ee89f27 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -8,7 +8,7 @@
 
 #include "clang-tidy/ClangTidyModule.h"
 #if CLANG_VERSION_MAJOR < 23
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "abi_tag_on_virtual.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index b1d1377e38d4c..4cdabf3de7c90 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "robust_against_adl.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index 3a60327d3117b..919838abba615 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 1b98f187c7c49..7f86f815918cc 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "uglify_attributes.hpp"

>From b9d3d989b98e5cd337a0cb08419979bbf1e34861 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Mon, 13 Jul 2026 13:12:48 +0000
Subject: [PATCH 4/4] todo

---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp       | 1 +
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp         | 1 +
 .../tools/clang_tidy_checks/header_exportable_declarations.cpp   | 1 +
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp            | 1 +
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp            | 1 +
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp       | 1 +
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp      | 1 +
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp        | 1 +
 8 files changed, 8 insertions(+)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index 16445b3b86f23..f5f9ce6bee253 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index 53dc4b74a965f..f28cb0ab40c62 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index bec3c1d2cd120..86e3ce6af60d3 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index b4b3f0b9cfa68..ab533d9b35f61 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index 93d195ee89f27..bc4315aedaa1b 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR < 23
 #  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 4cdabf3de7c90..a5ab6684fd7b2 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index 919838abba615..d470d8ad22d88 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 7f86f815918cc..d7d377845101a 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else


From libcxx-commits at lists.llvm.org  Mon Jul 13 06:14:40 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 13 Jul 2026 06:14:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Create libc++ 24 release notes
	(PR #208814)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208814@github.com>
Message-ID: <6a54e4c0.eb165e5c.1817a5.db3b@mx.google.com>


ldionne wrote:

> I feel like this makes it very easy to accidentally put release notes in the wrong place.

I think this is pretty easy to catch during code review. We already have to watch out for old PRs targeting previous releases (e.g. a PR still modifying the LLVM 20 release notes), so I don't think we're making anything worse, and we're making it a lot simpler for people who author PRs to just adjust the right release notes from the get go.

> How would you feel about a PR to add the release notes in the upstream repo that PRs targeting the next release can then be based on? Basically a stacked PR.

To me, creating a new process to add release notes in a specific branch every release, documenting it, and teaching it to people, is much more burdensome than just having them in `main` for a few weeks before the release branch and making sure PRs target the right version during code review (which we already have to do). So, my opinion is that it's not worth it.

https://github.com/llvm/llvm-project/pull/208814

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:15:24 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Mon, 13 Jul 2026 06:15:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a54e4ec.c56fb102.7c9cd.0fd2@mx.google.com>


================
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#if CLANG_VERSION_MAJOR > 23
----------------
boomanaiden154 wrote:

LLVM 25 branching sounds right. Libc++ would be supporting 23, 24, and 25 at that point.

https://github.com/llvm/llvm-project/pull/208945

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:17:37 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 13 Jul 2026 06:17:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Backport P3029R1 Better
 `mdspan`'s CTAD to C++20/23 (PR #208132)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208132@github.com>
Message-ID: <6a54e571.449bfb44.172ed9.4340@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/208132

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:20:17 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 06:20:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a54e611.481afadf.196d57.9442@mx.google.com>


================
@@ -7,7 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
----------------
philnik777 wrote:

```suggestion
// TODO(LLVM 25): Remove this compatibility check
```
We have this general pattern to make it easier to find TODOs that can be resolved after a given branch.

https://github.com/llvm/llvm-project/pull/208945

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:22:13 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 13 Jul 2026 06:22:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix filesystem::remove_all bug
 with read-only nested directory (PR #197104)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/197104@github.com>
Message-ID: <6a54e685.ff037943.2e959a.d8c0@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/197104

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:22:39 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 06:22:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] c31455c - [libc++] Fix
 filesystem::remove_all bug with read-only nested directory (#197104)
Message-ID: <6a54e69f.492e23e4.14eda0.0c23@mx.google.com>


Author: Jake Egan
Date: 2026-07-13T09:22:34-04:00
New Revision: c31455cd07c43ed02445f5b1f1057037f0503552

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

LOG: [libc++] Fix filesystem::remove_all bug with read-only nested directory (#197104)

Consider a directory `parent/child`, where `parent` has read-only
permissions. Calling `remove_all` on `parent` is expected to fail with a
permission error, however it fails with a different error depending on
the platform (such as "error file exists" on AIX).

During the recursive removal, `unlinkat` is first called on the child
and fails with a permission error. Rather than returning, the function
continues and tries `unlinkat` on the parent and fails with a different
error depending on the platform. This error is returned instead of the
permission error. This PR makes the first encountered error be returned
instead.

According to the standard [fs.op.remove.all]:
> Effects: Recursively deletes the contents of p if it exists, then
> deletes file p itself, as if by POSIX remove.

The word "then" implies an ordering where deleting the contents of p
should be successful before deleting file p itself.

Fixes #197100.

Added: 
    libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/bad_perms_parent.pass.cpp

Modified: 
    libcxx/src/filesystem/operations.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp
index 3d358dd55e8b8..cd55b797e96a2 100644
--- a/libcxx/src/filesystem/operations.cpp
+++ b/libcxx/src/filesystem/operations.cpp
@@ -905,6 +905,9 @@ uintmax_t remove_all_impl(int parent_directory, const path& p, error_code& ec) {
         break; // we're done iterating through the directory
       } else {
         count += remove_all_impl(fd, str, ec);
+        // If there's an error removing the child, return immediately to preserve the error code.
+        if (ec)
+          return count;
       }
     }
 

diff  --git a/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/bad_perms_parent.pass.cpp b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/bad_perms_parent.pass.cpp
new file mode 100644
index 0000000000000..c75c5f25dcce2
--- /dev/null
+++ b/libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.remove_all/bad_perms_parent.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-filesystem
+
+// UNSUPPORTED: windows
+// XFAIL: using-built-library-before-llvm-23
+
+// Verify that remove_all reports the correct error (permission_denied)
+// when the parent directory has insufficient permissions.
+
+// <filesystem>
+
+#include <filesystem>
+
+#include "test_macros.h"
+#include "filesystem_test_helper.h"
+namespace fs = std::filesystem;
+
+int main(int, char**) {
+  scoped_test_env env;
+
+  const fs::path parent_dir = env.create_dir("parent");
+  const fs::path child_dir  = env.create_dir(parent_dir / "child");
+  permissions(parent_dir, fs::perms::owner_read | fs::perms::owner_write);
+
+  const auto BadRet = static_cast<std::uintmax_t>(-1);
+  std::error_code ec;
+  assert(fs::remove_all(parent_dir, ec) == BadRet);
+  assert(ec == std::errc::permission_denied);
+
+  permissions(parent_dir, fs::perms::owner_all);
+  return 0;
+}


        

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:22:45 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 13 Jul 2026 06:22:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix filesystem::remove_all bug
 with read-only nested directory (PR #197104)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/197104@github.com>
Message-ID: <6a54e6a5.481afadf.196d57.dc10@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/197104

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:41:29 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Mon, 13 Jul 2026 06:41:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a54eb09.c574b9b4.21ead0.bbcc@mx.google.com>


https://github.com/boomanaiden154 updated https://github.com/llvm/llvm-project/pull/208945

>From 20c1f414a5514766969069b9e42764db3b48f288 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sat, 11 Jul 2026 19:50:43 +0000
Subject: [PATCH 1/5] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h

Deprecated in 1bcf74006bcf528d14377173dba459759d35c961. Causes warnings
during compilation and will presumably become an error.
---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp      | 2 +-
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp        | 2 +-
 .../tools/clang_tidy_checks/header_exportable_declarations.cpp  | 2 +-
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp           | 2 +-
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp           | 2 +-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp      | 2 +-
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp     | 2 +-
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp       | 2 +-
 8 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index f1866179a3b68..64dca4a08d62f 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "abi_tag_on_virtual.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index e176b29670ce9..7ccf6e3a910af 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "empty_namespaces.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 8ec029f7dba2d..24f8c1bf8caf3 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "llvm/ADT/ArrayRef.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index f05470e47d3e7..cb0c6a33b8498 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "hide_from_abi.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index b6298eefdeb44..b94989ca8c788 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "abi_tag_on_virtual.hpp"
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..a7e3f547d3ed8 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "robust_against_adl.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index a608954aa2cc7..e94c95561f92f 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 24bacde6304bc..aa325d9ba378f 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#include "clang-tidy/ClangTidyModule.h"
 
 #include "uglify_attributes.hpp"
 #include "utilities.hpp"

>From b39d6dcc1677d373375ea74079991b674d73d5c1 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sun, 12 Jul 2026 19:53:28 +0000
Subject: [PATCH 2/5] fix

---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp    | 4 ++++
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp      | 4 ++++
 .../clang_tidy_checks/header_exportable_declarations.cpp      | 4 ++++
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp         | 4 ++++
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp         | 4 +++-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp    | 4 ++++
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp   | 4 ++++
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp     | 4 ++++
 8 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index 64dca4a08d62f..a45cc441943b8 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "abi_tag_on_virtual.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index 7ccf6e3a910af..5ea8b89bf6fc8 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "empty_namespaces.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 24f8c1bf8caf3..66d75795dbf0f 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "llvm/ADT/ArrayRef.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index cb0c6a33b8498..75c9fe06b2420 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "hide_from_abi.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index b94989ca8c788..4724c04c08fd8 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,7 +7,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
-#include "clang-tidy/ClangTidyModule.h"
+#if CLANG_VERSION_MAJOR < 23
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "abi_tag_on_virtual.hpp"
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index a7e3f547d3ed8..b1d1377e38d4c 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "robust_against_adl.hpp"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index e94c95561f92f..3a60327d3117b 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
 
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index aa325d9ba378f..1b98f187c7c49 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,7 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+#if CLANG_VERSION_MAJOR > 23
 #include "clang-tidy/ClangTidyModule.h"
+#else
+#include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "uglify_attributes.hpp"
 #include "utilities.hpp"

>From f1cbae15a1ca638433098c7ef6251cf0b26f83c8 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sun, 12 Jul 2026 20:49:48 +0000
Subject: [PATCH 3/5] formatting

---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp    | 4 ++--
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp      | 4 ++--
 .../clang_tidy_checks/header_exportable_declarations.cpp      | 4 ++--
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp         | 4 ++--
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp         | 2 +-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp    | 4 ++--
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp   | 4 ++--
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp     | 4 ++--
 8 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index a45cc441943b8..16445b3b86f23 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "abi_tag_on_virtual.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index 5ea8b89bf6fc8..53dc4b74a965f 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "empty_namespaces.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 66d75795dbf0f..bec3c1d2cd120 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "llvm/ADT/ArrayRef.h"
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index 75c9fe06b2420..b4b3f0b9cfa68 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "hide_from_abi.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index 4724c04c08fd8..93d195ee89f27 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -8,7 +8,7 @@
 
 #include "clang-tidy/ClangTidyModule.h"
 #if CLANG_VERSION_MAJOR < 23
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "abi_tag_on_virtual.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index b1d1377e38d4c..4cdabf3de7c90 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "robust_against_adl.hpp"
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index 3a60327d3117b..919838abba615 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 1b98f187c7c49..7f86f815918cc 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -8,9 +8,9 @@
 
 #include "clang-tidy/ClangTidyCheck.h"
 #if CLANG_VERSION_MAJOR > 23
-#include "clang-tidy/ClangTidyModule.h"
+#  include "clang-tidy/ClangTidyModule.h"
 #else
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
 
 #include "uglify_attributes.hpp"

>From b9d3d989b98e5cd337a0cb08419979bbf1e34861 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Mon, 13 Jul 2026 13:12:48 +0000
Subject: [PATCH 4/5] todo

---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp       | 1 +
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp         | 1 +
 .../tools/clang_tidy_checks/header_exportable_declarations.cpp   | 1 +
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp            | 1 +
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp            | 1 +
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp       | 1 +
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp      | 1 +
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp        | 1 +
 8 files changed, 8 insertions(+)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index 16445b3b86f23..f5f9ce6bee253 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index 53dc4b74a965f..f28cb0ab40c62 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index bec3c1d2cd120..86e3ce6af60d3 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index b4b3f0b9cfa68..ab533d9b35f61 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index 93d195ee89f27..bc4315aedaa1b 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR < 23
 #  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 4cdabf3de7c90..a5ab6684fd7b2 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index 919838abba615..d470d8ad22d88 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 7f86f815918cc..d7d377845101a 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else

>From ac040ede0dff5d2a7341b4d9afce332962eb4ce8 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Mon, 13 Jul 2026 13:41:13 +0000
Subject: [PATCH 5/5] format

---
 libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp      | 2 +-
 libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp        | 2 +-
 .../tools/clang_tidy_checks/header_exportable_declarations.cpp  | 2 +-
 libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp           | 2 +-
 libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp           | 2 +-
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp      | 2 +-
 .../clang_tidy_checks/robust_against_operator_ampersand.cpp     | 2 +-
 libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp       | 2 +-
 8 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index f5f9ce6bee253..0d03574af68e7 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index f28cb0ab40c62..d31c1da44b527 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 86e3ce6af60d3..5273b1d7564ce 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index ab533d9b35f61..878290fde1271 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index bc4315aedaa1b..31415db2955b2 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
-// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR < 23
 #  include "clang-tidy/ClangTidyModuleRegistry.h"
 #endif
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index a5ab6684fd7b2..72852625b12e9 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index d470d8ad22d88..777dd6fe11df5 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else
diff --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index d7d377845101a..803e877a68687 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
 #if CLANG_VERSION_MAJOR > 23
 #  include "clang-tidy/ClangTidyModule.h"
 #else


From libcxx-commits at lists.llvm.org  Mon Jul 13 06:41:33 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Mon, 13 Jul 2026 06:41:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a54eb0d.a1accbb4.244acd.b489@mx.google.com>


================
@@ -7,7 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+// TODO(boomanaiden154): Remove this compatibility check when LLVM 25 branches.
----------------
boomanaiden154 wrote:

Done.

https://github.com/llvm/llvm-project/pull/208945

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:42:05 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 06:42:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx]support freestanding libc++ (PR
	#208900)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208900@github.com>
Message-ID: <6a54eb2d.1905e68e.18ad68.e031@mx.google.com>


trcrsired wrote:

> The CI isn't there to toy around. You can test things on your own system. This is also the kind of change that requires an RFC. We've already decided against various changes you're making here, and we probably won't reverse the direction.

Without testing ci i cannot guarantee it works without issues. i am not even asking anyone to make decisions here and that is why it is simply a draft not even something that would get merged

https://github.com/llvm/llvm-project/pull/208900

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:43:54 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 06:43:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a54eb9a.bd93e731.358318.e37d@mx.google.com>


https://github.com/philnik777 approved this pull request.

I

https://github.com/llvm/llvm-project/pull/208945

From libcxx-commits at lists.llvm.org  Mon Jul 13 06:44:57 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 13 Jul 2026 06:44:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a54ebd9.c5504292.28b3b8.4fce@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/208945

From libcxx-commits at lists.llvm.org  Mon Jul 13 07:09:04 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 07:09:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a54f180.d681cab9.333b2b.62ee@mx.google.com>


================
@@ -664,6 +674,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (noreplace) mode suffix; these cases fall through to
+// default (return nullptr), which is conformant per C23 7.21.5.3p5 (fail if atomic check-and-create is unsupported).
+#      if !defined(_AIX)
----------------
Himadhith wrote:

Ideally we would want the fix to be in the libc side of things, but this defect has been an open CMVC defect for years now. We are looking into a long term solution after this workaround. 

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Mon Jul 13 07:16:03 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Mon, 13 Jul 2026 07:16:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0493R5: Atomic
	minimum/maximum (PR #180333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/180333@github.com>
Message-ID: <6a54f323.e624eb8d.39cd4e.aefe@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/180333

>From ab79abbd1c14a305de4c16b791bf5e22384d543a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 17:10:11 +0800
Subject: [PATCH 01/27] implement atomic max/min

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/ReleaseNotes/23.rst               |  1 +
 libcxx/docs/Status/Cxx2cPapers.csv            |  2 +-
 libcxx/include/__atomic/atomic.h              | 68 ++++++++++++++++++
 libcxx/include/__atomic/atomic_ref.h          |  8 +++
 libcxx/include/__atomic/support.h             |  9 +++
 libcxx/include/__atomic/support/c11.h         | 26 +++++++
 libcxx/include/__atomic/support/gcc.h         | 24 +++++++
 libcxx/include/atomic                         | 24 +++++++
 libcxx/include/version                        |  2 +-
 libcxx/modules/std/atomic.inc                 |  4 ++
 .../atomics/atomics.ref/fetch_max.pass.cpp    | 70 +++++++++++++++++++
 .../atomics/atomics.ref/fetch_min.pass.cpp    | 70 +++++++++++++++++++
 .../atomic_fetch_max.pass.cpp                 | 55 +++++++++++++++
 .../atomic_fetch_max_explicit.pass.cpp        | 57 +++++++++++++++
 .../atomic_fetch_min.pass.cpp                 | 55 +++++++++++++++
 .../atomic_fetch_min_explicit.pass.cpp        | 57 +++++++++++++++
 .../atomic.version.compile.pass.cpp           | 16 ++---
 .../version.version.compile.pass.cpp          | 16 ++---
 .../generate_feature_test_macro_components.py |  1 -
 20 files changed, 541 insertions(+), 26 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index d2b76cb96e866..701775e051f9a 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -416,7 +416,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_associative_heterogeneous_insertion``          *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_atomic_min_max``                               *unimplemented*
+    ``__cpp_lib_atomic_min_max``                               ``202403L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_atomic_ref``                                   ``202411L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index a1d62a367f33c..ce2eee5ed852a 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P2440R1 (partial): ``ranges::iota`` and ``ranges::shift_left`` are supported (`Github <https://llvm.org/PR105184>`__)
+- P0493R5: Atomic minimum/maximum are supported (`Github <https://llvm.org/PR105418>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 29642fc53cac6..50819f1b47a27 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -53,7 +53,7 @@
 "`P3107R5 <https://wg21.link/P3107R5>`__","Permit an efficient implementation of ``std::print``","2024-03 (Tokyo)","","","`#105414 <https://github.com/llvm/llvm-project/issues/105414>`__",""
 "`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)"
 "`P2845R8 <https://wg21.link/P2845R8>`__","Formatting of ``std::filesystem::path``","2024-03 (Tokyo)","","","`#105416 <https://github.com/llvm/llvm-project/issues/105416>`__",""
-"`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","","","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
+"`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","|Complete|","23","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
 "`P2542R8 <https://wg21.link/P2542R8>`__","``views::concat``","2024-03 (Tokyo)","","","`#105419 <https://github.com/llvm/llvm-project/issues/105419>`__",""
 "`P2591R5 <https://wg21.link/P2591R5>`__","Concatenation of strings and string views","2024-03 (Tokyo)","|Complete|","19","`#105420 <https://github.com/llvm/llvm-project/issues/105420>`__",""
 "`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","`#105421 <https://github.com/llvm/llvm-project/issues/105421>`__",""
diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index ea1d28fbf850a..f1c7492590d61 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -178,6 +178,20 @@ struct __atomic_base<_Tp, true> : public __atomic_base<_Tp, false> {
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
     return std::__cxx_atomic_fetch_xor(std::addressof(this->__a_), __op, __m);
   }
+#if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+#endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) volatile _NOEXCEPT { return fetch_add(_Tp(1)); }
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) _NOEXCEPT { return fetch_add(_Tp(1)); }
@@ -799,6 +813,60 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
   return __o->fetch_xor(__op, __m);
 }
 
+#if _LIBCPP_STD_VER >= 26
+// atomic_fetch_max
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_max(__op);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_max(__op);
+}
+
+// atomic_fetch_max_explicit
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
+    volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_max(__op, __m);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_max(__op, __m);
+}
+
+// atomic_fetch_min
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_min(__op);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_min(__op);
+}
+
+// atomic_fetch_min_explicit
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
+    volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_min(__op, __m);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_min(__op, __m);
+}
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ATOMIC_ATOMIC_H
diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 69edbfe6ecadd..cedd4248eb200 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -299,6 +299,14 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
     return __atomic_fetch_xor(this->__ptr_, __arg, std::__to_gcc_order(__order));
   }
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+  }
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) const noexcept { return fetch_add(_Tp(1)); }
   _LIBCPP_HIDE_FROM_ABI _Tp operator--(int) const noexcept { return fetch_sub(_Tp(1)); }
diff --git a/libcxx/include/__atomic/support.h b/libcxx/include/__atomic/support.h
index 99d0f6aa543ca..eda38705acd32 100644
--- a/libcxx/include/__atomic/support.h
+++ b/libcxx/include/__atomic/support.h
@@ -99,6 +99,15 @@
 // template <class _Tp>
 // _Tp __cxx_atomic_fetch_xor(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
 //
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_max(_Atmc<_Tp> volatile* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_max(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_min(_Atmc<_Tp> volatile* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_min(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
+//
 // clang-format on
 //
 
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f28b5e461c18a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -259,6 +259,32 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
       std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
 }
 
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ATOMIC_SUPPORT_C11_H
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..dfaebb68ced74 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -258,6 +258,30 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   return __atomic_fetch_xor(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
 }
 
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 23a3db5d35029..4cf93b6bfbd85 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -164,6 +164,10 @@ struct atomic<integral>
     integral fetch_or(integral op, memory_order m = memory_order_seq_cst) noexcept;
     integral fetch_xor(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
     integral fetch_xor(integral op, memory_order m = memory_order_seq_cst) noexcept;
+    integral fetch_max(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
+    integral fetch_max(integral op, memory_order m = memory_order_seq_cst) noexcept;
+    integral fetch_min(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
+    integral fetch_min(integral op, memory_order m = memory_order_seq_cst) noexcept;
 
     integral operator++(int) volatile noexcept;
     integral operator++(int) noexcept;
@@ -441,6 +445,26 @@ template<class T>
 template<class T>
   T atomic_fetch_xor_explicit(atomic<T>*, atomic<T>::value_type,
                               memory_order) noexcept;
+template<class T>
+  T atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
 
 template<class T>
   void atomic_wait(const volatile atomic<T>*, atomic<T>::value_type) noexcept; // since C++20
diff --git a/libcxx/include/version b/libcxx/include/version
index 7d77677a012ce..a00e9441202bd 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -546,7 +546,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #if _LIBCPP_STD_VER >= 26
 # define __cpp_lib_aligned_accessor                     202411L
 // # define __cpp_lib_associative_heterogeneous_insertion  202306L
-// # define __cpp_lib_atomic_min_max                       202403L
+# define __cpp_lib_atomic_min_max                       202403L
 # undef  __cpp_lib_atomic_ref
 # define __cpp_lib_atomic_ref                           202411L
 # undef  __cpp_lib_bind_front
diff --git a/libcxx/modules/std/atomic.inc b/libcxx/modules/std/atomic.inc
index 9a30fb7affe71..11f77991068c1 100644
--- a/libcxx/modules/std/atomic.inc
+++ b/libcxx/modules/std/atomic.inc
@@ -50,6 +50,10 @@ export namespace std {
   using std::atomic_fetch_sub_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor_explicit _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_max _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_max_explicit _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_min _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_min_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_all _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_one _LIBCPP_USING_IF_EXISTS;
   using std::atomic_wait _LIBCPP_USING_IF_EXISTS;
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
new file mode 100644
index 0000000000000..6c1b69dce1595
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// integral-type fetch_max(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+template <typename T>
+concept has_fetch_max = requires {
+  std::declval<T const>().fetch_max(std::declval<T>());
+  std::declval<T const>().fetch_max(std::declval<T>(), std::declval<std::memory_order>());
+};
+
+template <typename T>
+struct TestDoesNotHaveFetchMax {
+  void operator()() const { static_assert(!has_fetch_max<std::atomic_ref<T>>); }
+};
+
+template <typename T>
+struct TestFetchMax {
+  void operator()() const {
+    static_assert(std::is_integral_v<T>);
+
+    alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
+    std::atomic_ref<T> const a(x);
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
+      assert(y == T(1));
+      assert(x == T(2));
+      ASSERT_NOEXCEPT(a.fetch_max(T(0)));
+    }
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_max(T(1), std::memory_order_relaxed);
+      assert(y == T(2));
+      assert(x == T(2));
+      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFetchMax>()();
+
+  TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
+
+  TestEachPointerType<TestDoesNotHaveFetchMax>()();
+
+  TestDoesNotHaveFetchMax<bool>()();
+  TestDoesNotHaveFetchMax<UserAtomicType>()();
+  TestDoesNotHaveFetchMax<LargeUserAtomicType>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
new file mode 100644
index 0000000000000..1fa2d32c4d7d8
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// integral-type fetch_min(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+template <typename T>
+concept has_fetch_min = requires {
+  std::declval<T const>().fetch_min(std::declval<T>());
+  std::declval<T const>().fetch_min(std::declval<T>(), std::declval<std::memory_order>());
+};
+
+template <typename T>
+struct TestDoesNotHaveFetchMin {
+  void operator()() const { static_assert(!has_fetch_min<std::atomic_ref<T>>); }
+};
+
+template <typename T>
+struct TestFetchMin {
+  void operator()() const {
+    static_assert(std::is_integral_v<T>);
+
+    alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
+    std::atomic_ref<T> const a(x);
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
+      assert(y == T(3));
+      assert(x == T(2));
+      ASSERT_NOEXCEPT(a.fetch_min(T(0)));
+    }
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
+      assert(y == T(2));
+      assert(x == T(1));
+      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFetchMin>()();
+
+  TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
+
+  TestEachPointerType<TestDoesNotHaveFetchMin>()();
+
+  TestDoesNotHaveFetchMin<bool>()();
+  TestDoesNotHaveFetchMin<UserAtomicType>()();
+  TestDoesNotHaveFetchMin<LargeUserAtomicType>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
new file mode 100644
index 0000000000000..25411767ca8ad
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_max(&t, T(2)) == T(1));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_max(&t, T(2)) == T(3));
+      assert(t == T(3));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
new file mode 100644
index 0000000000000..a670f1c10ce60
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+      assert(t == T(3));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
new file mode 100644
index 0000000000000..242355120c28d
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_min(&t, T(2)) == T(1));
+      assert(t == T(1));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_min(&t, T(2)) == T(3));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
new file mode 100644
index 0000000000000..46b7531b1f727
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+      assert(t == T(1));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
index 8bd027445c85b..9e620317c35c8 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
@@ -339,17 +339,11 @@
 #    error "__cpp_lib_atomic_lock_free_type_aliases should have the value 201907L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should be defined in c++26"
-#    endif
-#    if __cpp_lib_atomic_min_max != 202403L
-#      error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_atomic_min_max
+#    error "__cpp_lib_atomic_min_max should be defined in c++26"
+#  endif
+#  if __cpp_lib_atomic_min_max != 202403L
+#    error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_atomic_ref
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 f442b22c6d752..33f402b6422f6 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
@@ -6325,17 +6325,11 @@
 #    error "__cpp_lib_atomic_lock_free_type_aliases should have the value 201907L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should be defined in c++26"
-#    endif
-#    if __cpp_lib_atomic_min_max != 202403L
-#      error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_atomic_min_max
+#    error "__cpp_lib_atomic_min_max should be defined in c++26"
+#  endif
+#  if __cpp_lib_atomic_min_max != 202403L
+#    error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_atomic_ref
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 88431c76a00f5..799bb088f061e 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -191,7 +191,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_atomic_min_max",
             "values": {"c++26": 202403}, # P0493R5: Atomic minimum/maximum
             "headers": ["atomic"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_atomic_ref",

>From fb39fd535b1714f6d422d9b0f1810102a763a1b3 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 19:20:50 +0800
Subject: [PATCH 02/27] fix gcc build

---
 libcxx/include/__atomic/support/gcc.h | 28 +++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index dfaebb68ced74..e50143bb2bfa9 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -261,25 +261,45 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret > __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret > __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret < __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret < __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)

>From 906a95e15e4bdd1136be7bfc1f14731e6a3a3135 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 19:36:17 +0800
Subject: [PATCH 03/27] fix gcc atomic_ref

---
 libcxx/include/__atomic/atomic_ref.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index cedd4248eb200..6ea270dc3b902 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -23,6 +23,7 @@
 #include <__atomic/check_memory_order.h>
 #include <__atomic/floating_point_helper.h>
 #include <__atomic/memory_order.h>
+#include <__atomic/support.h>
 #include <__atomic/to_gcc_order.h>
 #include <__concepts/arithmetic.h>
 #include <__concepts/same_as.h>
@@ -301,10 +302,10 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+    return std::__cxx_atomic_fetch_max(this->__ptr_, __arg, __m);
   }
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+    return std::__cxx_atomic_fetch_min(this->__ptr_, __arg, __m);
   }
 #  endif
 

>From a64208d9c117ccbd5a4cc5677a662b6e4f972f59 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 19:55:43 +0800
Subject: [PATCH 04/27] fix gcc

---
 libcxx/include/__atomic/atomic_ref.h  | 14 ++++++++++++--
 libcxx/include/__atomic/support/gcc.h |  8 ++++----
 2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 6ea270dc3b902..11d74f71e6de1 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -302,10 +302,20 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return std::__cxx_atomic_fetch_max(this->__ptr_, __arg, __m);
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old > __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
   }
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return std::__cxx_atomic_fetch_min(this->__ptr_, __arg, __m);
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old < __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
   }
 #  endif
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index e50143bb2bfa9..c224bb6c95391 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -261,7 +261,7 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
@@ -272,7 +272,7 @@ __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
@@ -283,7 +283,7 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
@@ -294,7 +294,7 @@ __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;

>From 5eeeeea250032dd5ae65ec4a1868158cefe3c650 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 20:12:55 +0800
Subject: [PATCH 05/27] fix gcc

---
 libcxx/include/__atomic/support/gcc.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index c224bb6c95391..3eaccf95e19d8 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -265,7 +265,7 @@ __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -276,7 +276,7 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -287,7 +287,7 @@ __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -298,7 +298,7 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 

>From 46257efd512cc8bd741cc9dd3157b59ab1ce1ca1 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 20:39:31 +0800
Subject: [PATCH 06/27] remove unused include

---
 libcxx/include/__atomic/atomic_ref.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 11d74f71e6de1..84b9bf211b4b9 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -23,7 +23,6 @@
 #include <__atomic/check_memory_order.h>
 #include <__atomic/floating_point_helper.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support.h>
 #include <__atomic/to_gcc_order.h>
 #include <__concepts/arithmetic.h>
 #include <__concepts/same_as.h>

>From dc1cb331ddcb980cce4a8deeef8d1c21b08b0eb7 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 21:29:39 +0800
Subject: [PATCH 07/27] guard modules

---
 libcxx/modules/std/atomic.inc | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/modules/std/atomic.inc b/libcxx/modules/std/atomic.inc
index 11f77991068c1..38321e1a1bb64 100644
--- a/libcxx/modules/std/atomic.inc
+++ b/libcxx/modules/std/atomic.inc
@@ -50,10 +50,12 @@ export namespace std {
   using std::atomic_fetch_sub_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor_explicit _LIBCPP_USING_IF_EXISTS;
+#if _LIBCPP_STD_VER >= 26
   using std::atomic_fetch_max _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_max_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_min _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_min_explicit _LIBCPP_USING_IF_EXISTS;
+#endif
   using std::atomic_notify_all _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_one _LIBCPP_USING_IF_EXISTS;
   using std::atomic_wait _LIBCPP_USING_IF_EXISTS;

>From dfce3c7d45c1dca9e1ac85b293321ef19e007b23 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 8 Feb 2026 19:37:33 +0800
Subject: [PATCH 08/27] address review comments: use builtin if possible

---
 libcxx/include/__atomic/atomic_ref.h | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 84b9bf211b4b9..88c7d7dbc5dfb 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -301,20 +301,28 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_max)
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp __old = this->load(memory_order_relaxed);
     _Tp __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_min)
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp __old = this->load(memory_order_relaxed);
     _Tp __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
 #  endif
 

>From a3cabfda1b316f2514df394232a4e504f38159eb Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 8 Feb 2026 23:12:48 +0800
Subject: [PATCH 09/27] address review comments: use builtin if possible

---
 libcxx/include/__atomic/support/gcc.h | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 3eaccf95e19d8..754c60406cbf6 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -261,45 +261,61 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_max)
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_max)
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_min)
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_min)
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)

>From f01144d60576864032d1204a7662ddcb9540059a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 9 Feb 2026 20:33:05 +0800
Subject: [PATCH 10/27] codestyle

---
 libcxx/include/__atomic/atomic.h              | 24 ++++++++++++-------
 .../atomic_fetch_max.pass.cpp                 | 13 ++++------
 .../atomic_fetch_max_explicit.pass.cpp        | 15 ++++--------
 .../atomic_fetch_min.pass.cpp                 | 13 ++++------
 .../atomic_fetch_min_explicit.pass.cpp        | 15 ++++--------
 5 files changed, 34 insertions(+), 46 deletions(-)

diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index f1c7492590d61..e4be21cfd2d84 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -816,25 +816,29 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 #if _LIBCPP_STD_VER >= 26
 // atomic_fetch_max
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
 
 // atomic_fetch_max_explicit
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
@@ -842,25 +846,29 @@ atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 
 // atomic_fetch_min
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
 
 // atomic_fetch_min_explicit
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 25411767ca8ad..8aa66f68a23d7 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -12,12 +12,9 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
-//
+//   T atomic_fetch_max(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
 // template<class T>
-//     T
-//     atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+//   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -30,16 +27,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_max(&t, T(2)) == T(1));
       assert(t == T(2));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_max(&t, T(2)) == T(3));
       assert(t == T(3));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index a670f1c10ce60..1d04bb737fe1f 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -12,14 +12,11 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//   T atomic_fetch_max_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
 //                               memory_order) noexcept;
-//
 // template<class T>
-//     T
-//     atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
-//                               memory_order) noexcept;
+//   constexpr T atomic_fetch_max_explicit(atomic<T>*, typename atomic<T>::value_type,
+//                                         memory_order) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -32,16 +29,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
       assert(t == T(2));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
       assert(t == T(3));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 242355120c28d..43b979144b19b 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -12,12 +12,9 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
-//
+//   T atomic_fetch_min(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
 // template<class T>
-//     T
-//     atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+//   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -30,16 +27,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_min(&t, T(2)) == T(1));
       assert(t == T(1));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_min(&t, T(2)) == T(3));
       assert(t == T(2));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 46b7531b1f727..309489f91f126 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -12,14 +12,11 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//   T atomic_fetch_min_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
 //                               memory_order) noexcept;
-//
 // template<class T>
-//     T
-//     atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
-//                               memory_order) noexcept;
+//   constexpr T atomic_fetch_min_explicit(atomic<T>*, typename atomic<T>::value_type,
+//                                         memory_order) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -32,16 +29,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
       assert(t == T(1));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
       assert(t == T(2));
 

>From 4a4735ee1ec8f356a2c73570d289beb93dce9354 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 9 Feb 2026 20:46:52 +0800
Subject: [PATCH 11/27] ci

---
 libcxx/include/__atomic/atomic.h                       |  2 ++
 libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp | 10 ++++++++--
 libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp |  6 ++++++
 3 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index e4be21cfd2d84..455acb8fca2ad 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -16,6 +16,8 @@
 #include <__atomic/is_always_lock_free.h>
 #include <__atomic/memory_order.h>
 #include <__atomic/support.h>
+#include <__concepts/arithmetic.h>
+#include <__concepts/same_as.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 6c1b69dce1595..6e96baaaf6e2c 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -43,13 +43,19 @@ struct TestFetchMax {
       std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
       assert(y == T(1));
       assert(x == T(2));
+      y = a.fetch_max(T(0));
+      assert(y == T(2));
+      assert(x == T(2));
       ASSERT_NOEXCEPT(a.fetch_max(T(0)));
     }
 
     {
-      std::same_as<T> decltype(auto) y = a.fetch_max(T(1), std::memory_order_relaxed);
+      std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
       assert(y == T(2));
-      assert(x == T(2));
+      assert(x == T(3));
+      y = a.fetch_max(T(0));
+      assert(y == T(3));
+      assert(x == T(3));
       ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
     }
   }
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index 1fa2d32c4d7d8..92b7f1ed39cb1 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -43,6 +43,9 @@ struct TestFetchMin {
       std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
       assert(y == T(3));
       assert(x == T(2));
+      y = a.fetch_min(T(4));
+      assert(y == T(2));
+      assert(x == T(2));
       ASSERT_NOEXCEPT(a.fetch_min(T(0)));
     }
 
@@ -50,6 +53,9 @@ struct TestFetchMin {
       std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
       assert(y == T(2));
       assert(x == T(1));
+      y = a.fetch_min(T(4));
+      assert(y == T(1));
+      assert(x == T(1));
       ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
     }
   }

>From cfce0bec4a759eea1786642fd3a309032aecb2fc Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 15:26:50 +0800
Subject: [PATCH 12/27] revise testcases

---
 .../atomics/atomics.ref/fetch_max.pass.cpp    | 11 ++-
 .../atomics/atomics.ref/fetch_min.pass.cpp    | 11 ++-
 .../atomic_fetch_max.pass.cpp                 | 85 +++++++++++++++---
 .../atomic_fetch_max_explicit.pass.cpp        | 86 ++++++++++++++++---
 .../atomic_fetch_min.pass.cpp                 | 85 +++++++++++++++---
 .../atomic_fetch_min_explicit.pass.cpp        | 86 ++++++++++++++++---
 6 files changed, 296 insertions(+), 68 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 6e96baaaf6e2c..17930c5d97e65 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -15,26 +15,25 @@
 #include <cassert>
 #include <concepts>
 #include <type_traits>
-#include <utility>
 
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
 template <typename T>
-concept has_fetch_max = requires {
-  std::declval<T const>().fetch_max(std::declval<T>());
-  std::declval<T const>().fetch_max(std::declval<T>(), std::declval<std::memory_order>());
+concept has_fetch_max = requires(std::atomic_ref<T> const& a, T v) {
+  { a.fetch_max(v) } -> std::same_as<T>;
+  { a.fetch_max(v, std::memory_order::relaxed) } -> std::same_as<T>;
 };
 
 template <typename T>
 struct TestDoesNotHaveFetchMax {
-  void operator()() const { static_assert(!has_fetch_max<std::atomic_ref<T>>); }
+  void operator()() const { static_assert(!has_fetch_max<T>); }
 };
 
 template <typename T>
 struct TestFetchMax {
   void operator()() const {
-    static_assert(std::is_integral_v<T>);
+    static_assert(std::is_integral_v<T> && has_fetch_max<T>);
 
     alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
     std::atomic_ref<T> const a(x);
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index 92b7f1ed39cb1..e930d83a2b819 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -15,26 +15,25 @@
 #include <cassert>
 #include <concepts>
 #include <type_traits>
-#include <utility>
 
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
 template <typename T>
-concept has_fetch_min = requires {
-  std::declval<T const>().fetch_min(std::declval<T>());
-  std::declval<T const>().fetch_min(std::declval<T>(), std::declval<std::memory_order>());
+concept has_fetch_min = requires(std::atomic_ref<T> const& a, T v) {
+  { a.fetch_min(v) } -> std::same_as<T>;
+  { a.fetch_min(v, std::memory_order::relaxed) } -> std::same_as<T>;
 };
 
 template <typename T>
 struct TestDoesNotHaveFetchMin {
-  void operator()() const { static_assert(!has_fetch_min<std::atomic_ref<T>>); }
+  void operator()() const { static_assert(!has_fetch_min<T>); }
 };
 
 template <typename T>
 struct TestFetchMin {
   void operator()() const {
-    static_assert(std::is_integral_v<T>);
+    static_assert(std::is_integral_v<T> && has_fetch_min<T>);
 
     alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
     std::atomic_ref<T> const a(x);
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 8aa66f68a23d7..cbd8f147f5931 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -17,28 +20,82 @@
 //   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_max(&t, T(2)) == T(1));
-      assert(t == T(2));
+concept HasVolatileFetchMax = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_max(&a, t) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMax<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(std::atomic_fetch_max(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
+
+  // atomic_fetch_max
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_max(&t, T(2)) == T(1));
+    assert(t == T(2));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_max(&t, T(2)) == T(3));
+    assert(t == T(3));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_max concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(0);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = 0; j < loop; ++j) {
+          std::atomic_fetch_max(&at, T(i * loop + j));
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_max(&t, T(2)) == T(3));
-      assert(t == T(3));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(0);
+      for (auto i = 0; i < n; ++i) {
+        res = std::max<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 1d04bb737fe1f..ddae555670f17 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -19,28 +22,83 @@
 //                                         memory_order) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-      assert(t == T(2));
+concept HasVolatileFetchMaxExplicit = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_max_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMaxExplicit<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(
+      std::atomic_fetch_max_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
+
+  // atomic_fetch_max_explicit
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+    assert(t == T(2));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    assert(t == T(3));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_max_explicit concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(0);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = 0; j < loop; ++j) {
+          std::atomic_fetch_max_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
-      assert(t == T(3));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(0);
+      for (auto i = 0; i < n; ++i) {
+        res = std::max<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 43b979144b19b..33ca27f3de262 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -17,28 +20,82 @@
 //   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_min(&t, T(2)) == T(1));
-      assert(t == T(1));
+concept HasVolatileFetchMin = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_min(&a, t) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMin<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(std::atomic_fetch_min(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
+
+  // atomic_fetch_min
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_min(&t, T(2)) == T(1));
+    assert(t == T(1));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_min(&t, T(2)) == T(3));
+    assert(t == T(2));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_min concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = loop - 1; j >= 0; --j) {
+          std::atomic_fetch_min(&at, T(i * loop + j));
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_min(&t, T(2)) == T(3));
-      assert(t == T(2));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(n);
+      for (auto i = n - 1; i >= 0; --i) {
+        res = std::min<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 309489f91f126..99d20c0c544fb 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -19,28 +22,83 @@
 //                                         memory_order) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-      assert(t == T(1));
+concept HasVolatileFetchMinExplicit = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_min_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMinExplicit<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(
+      std::atomic_fetch_min_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
+
+  // atomic_fetch_min_explicit
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+    assert(t == T(1));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    assert(t == T(2));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_min_explicit concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = loop - 1; j >= 0; --j) {
+          std::atomic_fetch_min_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
-      assert(t == T(2));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(n);
+      for (auto i = n - 1; i >= 0; --i) {
+        res = std::min<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };

>From 71961be2c3c55d2ff0f0224ada38814fe004c972 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 15:42:24 +0800
Subject: [PATCH 13/27] fix ci for aix

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp  | 6 ------
 .../atomic_fetch_max_explicit.pass.cpp                      | 6 ------
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp  | 6 ------
 .../atomic_fetch_min_explicit.pass.cpp                      | 6 ------
 4 files changed, 24 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index cbd8f147f5931..6f89cd719a2ef 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -33,14 +33,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMax = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_max(&a, t) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMax<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(std::atomic_fetch_max(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
 
   // atomic_fetch_max
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index ddae555670f17..931c5a482ed58 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -35,14 +35,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMaxExplicit = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_max_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMaxExplicit<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(
       std::atomic_fetch_max_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 33ca27f3de262..e7a4d4e7bc798 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -33,14 +33,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMin = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_min(&a, t) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMin<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(std::atomic_fetch_min(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
 
   // atomic_fetch_min
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 99d20c0c544fb..acf47f0d89567 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -35,14 +35,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMinExplicit = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_min_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMinExplicit<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(
       std::atomic_fetch_min_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
 

>From 8844e1581d89f537673084b04236710aa1a41ec5 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 15:48:50 +0800
Subject: [PATCH 14/27] fix modules build

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp       | 1 +
 .../atomic_fetch_max_explicit.pass.cpp                           | 1 +
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp       | 1 +
 .../atomic_fetch_min_explicit.pass.cpp                           | 1 +
 4 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 6f89cd719a2ef..93bd767cbab35 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -19,6 +19,7 @@
 // template<class T>
 //   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 931c5a482ed58..7fb2a702be337 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -21,6 +21,7 @@
 //   constexpr T atomic_fetch_max_explicit(atomic<T>*, typename atomic<T>::value_type,
 //                                         memory_order) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index e7a4d4e7bc798..a5adda9487897 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -19,6 +19,7 @@
 // template<class T>
 //   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index acf47f0d89567..f5a23b153fdbe 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -21,6 +21,7 @@
 //   constexpr T atomic_fetch_min_explicit(atomic<T>*, typename atomic<T>::value_type,
 //                                         memory_order) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>

>From 7bfd5438857639f2d3b627c18fe56e57a1165067 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 17:24:43 +0800
Subject: [PATCH 15/27] declval

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp       | 1 +
 .../atomic_fetch_max_explicit.pass.cpp                           | 1 +
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp       | 1 +
 .../atomic_fetch_min_explicit.pass.cpp                           | 1 +
 4 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 93bd767cbab35..af249355811a5 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -23,6 +23,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 7fb2a702be337..45df7c240dfa2 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -25,6 +25,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index a5adda9487897..e326d4a4e5b7a 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -23,6 +23,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index f5a23b153fdbe..4e110f7f34944 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -25,6 +25,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"

>From a844bd2596f09f8eda9af8bf9fca7c67997816f8 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 19:51:19 +0800
Subject: [PATCH 16/27] remove xfail

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp     | 3 ---
 .../atomic_fetch_max_explicit.pass.cpp                         | 3 ---
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp     | 3 ---
 .../atomic_fetch_min_explicit.pass.cpp                         | 3 ---
 4 files changed, 12 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index af249355811a5..e4aba6ebbdd20 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 45df7c240dfa2..441fb57c3b332 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index e326d4a4e5b7a..3a4a15290b407 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 4e110f7f34944..fd208162b9c07 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>

>From a85a1dfa40440160f7910933e899250e8c6409f9 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 21 Feb 2026 20:30:08 +0800
Subject: [PATCH 17/27] address review comments

---
 libcxx/include/__atomic/atomic.h              | 39 ++++++++--
 libcxx/include/__atomic/atomic_ref.h          | 28 +++++++
 libcxx/include/__atomic/support/c11.h         | 16 ++--
 libcxx/include/__atomic/support/gcc.h         | 26 +++----
 libcxx/include/atomic                         |  4 +
 .../atomics/atomics.ref/fetch_max.pass.cpp    | 73 +++++++++++++------
 .../atomics/atomics.ref/fetch_min.pass.cpp    | 73 +++++++++++++------
 .../atomic_fetch_max.pass.cpp                 |  3 +-
 .../atomic_fetch_max_explicit.pass.cpp        |  3 +-
 .../atomic_fetch_min.pass.cpp                 |  3 +-
 .../atomic_fetch_min_explicit.pass.cpp        |  3 +-
 11 files changed, 191 insertions(+), 80 deletions(-)

diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index 455acb8fca2ad..5fb9e0db72f51 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_integral.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/remove_pointer.h>
@@ -322,6 +323,28 @@ struct atomic<_Tp*> : public __atomic_base<_Tp*> {
     return std::__cxx_atomic_fetch_sub(std::addressof(this->__a_), __op, __m);
   }
 
+#if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+#endif
+
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) volatile _NOEXCEPT { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) _NOEXCEPT { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) volatile _NOEXCEPT { return fetch_sub(1); }
@@ -819,13 +842,13 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 // atomic_fetch_max
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
@@ -833,14 +856,14 @@ _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp
 // atomic_fetch_max_explicit
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
@@ -849,13 +872,13 @@ atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 // atomic_fetch_min
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
@@ -863,14 +886,14 @@ _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp
 // atomic_fetch_min_explicit
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 88c7d7dbc5dfb..ec98fab310c7c 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -403,6 +403,34 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_sub(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept {
     return __atomic_fetch_sub(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
   }
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_max)
+    return reinterpret_cast<_Tp*>(__atomic_fetch_max(
+        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+#    else
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old > __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
+#    endif
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_min)
+    return reinterpret_cast<_Tp*>(__atomic_fetch_min(
+        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+#    else
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old < __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
+#    endif
+  }
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) const noexcept { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) const noexcept { return fetch_sub(1); }
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f28b5e461c18a..0d74d55188a3d 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -261,28 +261,28 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 754c60406cbf6..37c38d25012c6 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -260,29 +260,28 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_max)
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret > __pattern ? __ret : __pattern;
+    __value = __ret > __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
 }
 
 template <typename _Tp>
-_LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+_LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_max)
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret > __pattern ? __ret : __pattern;
+    __value = __ret > __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
@@ -290,29 +289,28 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_min)
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret < __pattern ? __ret : __pattern;
+    __value = __ret < __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
 }
 
 template <typename _Tp>
-_LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+_LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_min)
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret < __pattern ? __ret : __pattern;
+    __value = __ret < __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 4cf93b6bfbd85..bcf831fedc192 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -244,6 +244,10 @@ struct atomic<T*>
     T* fetch_add(ptrdiff_t op, memory_order m = memory_order_seq_cst) noexcept;
     T* fetch_sub(ptrdiff_t op, memory_order m = memory_order_seq_cst) volatile noexcept;
     T* fetch_sub(ptrdiff_t op, memory_order m = memory_order_seq_cst) noexcept;
+    T* fetch_max(T*, memory_order = memory_order_seq_cst) volatile noexcept;
+    T* fetch_max(T*, memory_order = memory_order_seq_cst) noexcept;
+    T* fetch_min(T*, memory_order = memory_order_seq_cst) volatile noexcept;
+    T* fetch_min(T*, memory_order = memory_order_seq_cst) noexcept;
 
     T* operator++(int) volatile noexcept;
     T* operator++(int) noexcept;
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 17930c5d97e65..2ba748486d950 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -33,29 +33,56 @@ struct TestDoesNotHaveFetchMax {
 template <typename T>
 struct TestFetchMax {
   void operator()() const {
-    static_assert(std::is_integral_v<T> && has_fetch_max<T>);
-
-    alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
-    std::atomic_ref<T> const a(x);
-
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
-      assert(y == T(1));
-      assert(x == T(2));
-      y = a.fetch_max(T(0));
-      assert(y == T(2));
-      assert(x == T(2));
-      ASSERT_NOEXCEPT(a.fetch_max(T(0)));
-    }
+    static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_max<T>);
+
+    if constexpr (std::is_integral_v<T>) {
+      alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
+        assert(y == T(1));
+        assert(x == T(2));
+        y = a.fetch_max(T(0));
+        assert(y == T(2));
+        assert(x == T(2));
+        ASSERT_NOEXCEPT(a.fetch_max(T(0)));
+      }
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
+        assert(y == T(2));
+        assert(x == T(3));
+        y = a.fetch_max(T(0));
+        assert(y == T(3));
+        assert(x == T(3));
+        ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
+      }
+    } else if constexpr (std::is_pointer_v<T>) {
+      using U = std::remove_pointer_t<T>;
+      U t[9]  = {};
+      alignas(std::atomic_ref<T>::required_alignment) T x{&t[1]};
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(&t[2]);
+        assert(y == &t[1]);
+        assert(x == &t[2]);
+        y = a.fetch_max(&t[0]);
+        assert(y == &t[2]);
+        assert(x == &t[2]);
+        ASSERT_NOEXCEPT(a.fetch_max(&t[0]));
+      }
 
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
-      assert(y == T(2));
-      assert(x == T(3));
-      y = a.fetch_max(T(0));
-      assert(y == T(3));
-      assert(x == T(3));
-      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(&t[3], std::memory_order_relaxed);
+        assert(y == &t[2]);
+        assert(a == &t[3]);
+        y = a.fetch_max(&t[0]);
+        assert(y == &t[3]);
+        assert(x == &t[3]);
+        ASSERT_NOEXCEPT(a.fetch_max(&t[0], std::memory_order_relaxed));
+      }
     }
   }
 };
@@ -65,7 +92,7 @@ int main(int, char**) {
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
 
-  TestEachPointerType<TestDoesNotHaveFetchMax>()();
+  TestEachPointerType<TestFetchMax>()();
 
   TestDoesNotHaveFetchMax<bool>()();
   TestDoesNotHaveFetchMax<UserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index e930d83a2b819..b178996b06085 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -33,29 +33,56 @@ struct TestDoesNotHaveFetchMin {
 template <typename T>
 struct TestFetchMin {
   void operator()() const {
-    static_assert(std::is_integral_v<T> && has_fetch_min<T>);
-
-    alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
-    std::atomic_ref<T> const a(x);
-
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
-      assert(y == T(3));
-      assert(x == T(2));
-      y = a.fetch_min(T(4));
-      assert(y == T(2));
-      assert(x == T(2));
-      ASSERT_NOEXCEPT(a.fetch_min(T(0)));
-    }
+    static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_min<T>);
+
+    if constexpr (std::is_integral_v<T>) {
+      alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
+        assert(y == T(3));
+        assert(x == T(2));
+        y = a.fetch_min(T(4));
+        assert(y == T(2));
+        assert(x == T(2));
+        ASSERT_NOEXCEPT(a.fetch_min(T(0)));
+      }
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
+        assert(y == T(2));
+        assert(x == T(1));
+        y = a.fetch_min(T(4));
+        assert(y == T(1));
+        assert(x == T(1));
+        ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
+      }
+    } else if constexpr (std::is_pointer_v<T>) {
+      using U = std::remove_pointer_t<T>;
+      U t[9]  = {};
+      alignas(std::atomic_ref<T>::required_alignment) T x{&t[3]};
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(&t[2]);
+        assert(y == &t[3]);
+        assert(x == &t[2]);
+        y = a.fetch_min(&t[4]);
+        assert(y == &t[2]);
+        assert(x == &t[2]);
+        ASSERT_NOEXCEPT(a.fetch_min(&t[0]));
+      }
 
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
-      assert(y == T(2));
-      assert(x == T(1));
-      y = a.fetch_min(T(4));
-      assert(y == T(1));
-      assert(x == T(1));
-      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(&t[1], std::memory_order_relaxed);
+        assert(y == &t[2]);
+        assert(a == &t[1]);
+        y = a.fetch_min(&t[4]);
+        assert(y == &t[1]);
+        assert(x == &t[1]);
+        ASSERT_NOEXCEPT(a.fetch_min(&t[0], std::memory_order_relaxed));
+      }
     }
   }
 };
@@ -65,7 +92,7 @@ int main(int, char**) {
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
 
-  TestEachPointerType<TestDoesNotHaveFetchMin>()();
+  TestEachPointerType<TestFetchMin>()();
 
   TestDoesNotHaveFetchMin<bool>()();
   TestDoesNotHaveFetchMin<UserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index e4aba6ebbdd20..a7596b6ff2bf5 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -44,7 +44,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_max(&t, T(2)) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_max(T(2));
+    assert(r == T(3));
     assert(t == T(3));
   }
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 441fb57c3b332..52c4ac137c709 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -47,7 +47,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_max(T(2), std::memory_order_seq_cst);
+    assert(r == T(3));
     assert(t == T(3));
   }
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 3a4a15290b407..1ffc92f3c2300 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -44,7 +44,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_min(&t, T(2)) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_min(T(2));
+    assert(r == T(3));
     assert(t == T(2));
   }
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index fd208162b9c07..f5732b5d977cc 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -47,7 +47,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_min(T(2), std::memory_order_seq_cst);
+    assert(r == T(3));
     assert(t == T(2));
   }
 

>From c3e318d67841e8076a2e99f9c79d64873178dab2 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 21 Feb 2026 21:07:13 +0800
Subject: [PATCH 18/27] header

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp       | 1 +
 .../atomic_fetch_max_explicit.pass.cpp                           | 1 +
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp       | 1 +
 .../atomic_fetch_min_explicit.pass.cpp                           | 1 +
 4 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index a7596b6ff2bf5..dc2274a1056cb 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 52c4ac137c709..d878f6abd246d 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 1ffc92f3c2300..43277f6a7977a 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index f5732b5d977cc..75bf200812ab0 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>

>From 609e1196aa5098cbbd3e27a02d21beb42c98ecfe Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 22 Feb 2026 21:14:58 +0800
Subject: [PATCH 19/27] fix gcc ci

---
 libcxx/include/__atomic/atomic_ref.h | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index ec98fab310c7c..e0f28f4a3e204 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,11 +406,10 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
 #    if __has_builtin(__atomic_fetch_max)
-    return reinterpret_cast<_Tp*>(__atomic_fetch_max(
-        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+    return __atomic_fetch_max(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
 #    else
-    _Tp __old = this->load(memory_order_relaxed);
-    _Tp __new;
+    _Tp* __old = this->load(memory_order_relaxed);
+    _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
@@ -419,11 +418,10 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
 #    if __has_builtin(__atomic_fetch_min)
-    return reinterpret_cast<_Tp*>(__atomic_fetch_min(
-        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+    return __atomic_fetch_min(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
 #    else
-    _Tp __old = this->load(memory_order_relaxed);
-    _Tp __new;
+    _Tp* __old = this->load(memory_order_relaxed);
+    _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));

>From ebd61a9db3e67e3c7caf05b7e364f61f3243d24a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 10:19:34 -0400
Subject: [PATCH 20/27] Don't use builtins for pointer overloads of
 fetch_max/min

`__arg * sizeof(_Tp)` makes no sense in that context since __arg is a
pointer.
---
 libcxx/include/__atomic/atomic_ref.h | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index d681fd7488fcc..0fb141fb1f67a 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,28 +406,22 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_max)
-    return __atomic_fetch_max(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_min)
-    return __atomic_fetch_min(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
 #  endif
 

>From 6190229b39172c8b6e239e72c31e9f6daa6c85d6 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 10:32:46 -0400
Subject: [PATCH 21/27] Fix release note

---
 libcxx/docs/ReleaseNotes/23.rst | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index c99f3b49d902b..d56ec23a763cb 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -38,7 +38,8 @@ What's New in Libc++ 23.0.0?
 Implemented Papers
 ------------------
 
-- P0493R5: Atomic minimum/maximum are supported (`Github <https://llvm.org/PR105418>`__)
+- P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
+- P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
 - P1899R3: ``stride_view`` (`Github <https://llvm.org/PR105198>`__)
 - P3936R1: Safer ``atomic_ref::address`` (`Github <https://llvm.org/PR189594>`__)
 - P3953R3: Rename ``std::runtime_format`` (`Github <https://llvm.org/PR189624>`__)

>From 2853b3870f2bdfe1a6385f1a245866d71ac391f4 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 10:35:42 -0400
Subject: [PATCH 22/27] Revert "Don't use builtins for pointer overloads of
 fetch_max/min"

Try applying frederick's suggestion instead.
---
 libcxx/include/__atomic/atomic_ref.h | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 0fb141fb1f67a..2f5a256698cf3 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,22 +406,28 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
+#    if __has_builtin(__atomic_fetch_max)
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
+#    if __has_builtin(__atomic_fetch_min)
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
 #  endif
 

>From b7ed28f9b8e3be9137393c91be40e29ac1268efa Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 11:11:22 -0400
Subject: [PATCH 23/27] Re-apply "Don't use builtins for pointer overloads of
 fetch_max/min"

It seems that the builtins indeed don't support pointers.
---
 libcxx/include/__atomic/atomic_ref.h | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 2f5a256698cf3..0fb141fb1f67a 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,28 +406,22 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_max)
-    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_min)
-    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
 #  endif
 

>From de6f07f40149a2f41740033a24be77f06919097a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 16:41:43 -0400
Subject: [PATCH 24/27] Port test coverage from #186694

Assisted by Claude, reviewed manually.
---
 .../atomics/atomics.ref/fetch_max.pass.cpp    |  64 +++-----
 .../atomics/atomics.ref/fetch_min.pass.cpp    |  64 +++-----
 .../atomic_fetch_max.pass.cpp                 |  49 ++++--
 .../atomic_fetch_max_explicit.pass.cpp        |  50 ++++--
 .../atomic_fetch_min.pass.cpp                 |  64 ++++----
 .../atomic_fetch_min_explicit.pass.cpp        |  65 ++++----
 libcxx/test/support/atomic_fetch_max_helper.h | 147 ++++++++++++++++++
 libcxx/test/support/atomic_fetch_min_helper.h | 147 ++++++++++++++++++
 8 files changed, 478 insertions(+), 172 deletions(-)
 create mode 100644 libcxx/test/support/atomic_fetch_max_helper.h
 create mode 100644 libcxx/test/support/atomic_fetch_min_helper.h

diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 2ba748486d950..7302eaea570f0 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -10,12 +10,14 @@
 // XFAIL: !has-64-bit-atomics
 
 // integral-type fetch_max(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+// T* fetch_max(T*, memory_order = memory_order::seq_cst) const noexcept;
 
 #include <atomic>
 #include <cassert>
 #include <concepts>
 #include <type_traits>
 
+#include "atomic_fetch_max_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -36,64 +38,38 @@ struct TestFetchMax {
     static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_max<T>);
 
     if constexpr (std::is_integral_v<T>) {
-      alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
+      alignas(std::atomic_ref<T>::required_alignment) T x{};
       std::atomic_ref<T> const a(x);
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
-        assert(y == T(1));
-        assert(x == T(2));
-        y = a.fetch_max(T(0));
-        assert(y == T(2));
-        assert(x == T(2));
-        ASSERT_NOEXCEPT(a.fetch_max(T(0)));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
-        assert(y == T(2));
-        assert(x == T(3));
-        y = a.fetch_max(T(0));
-        assert(y == T(3));
-        assert(x == T(3));
-        ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
-      }
+      auto load  = [&]() { return x; };
+      auto store = [&](T val) { x = val; };
+      auto max   = [&](T val, auto order) { return a.fetch_max(val, order); };
+
+      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_seq_cst));
+      test_fetch_max_integral<T>(load, store, max);
+
     } else if constexpr (std::is_pointer_v<T>) {
       using U = std::remove_pointer_t<T>;
-      U t[9]  = {};
-      alignas(std::atomic_ref<T>::required_alignment) T x{&t[1]};
-      std::atomic_ref<T> const a(x);
+      U arr[5]{};
+      alignas(std::atomic_ref<T>::required_alignment) T p{};
+      std::atomic_ref<T> const a(p);
+
+      auto load  = [&]() { return p; };
+      auto store = [&](T val) { p = val; };
+      auto max   = [&](T val, auto order) { return a.fetch_max(val, order); };
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(&t[2]);
-        assert(y == &t[1]);
-        assert(x == &t[2]);
-        y = a.fetch_max(&t[0]);
-        assert(y == &t[2]);
-        assert(x == &t[2]);
-        ASSERT_NOEXCEPT(a.fetch_max(&t[0]));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(&t[3], std::memory_order_relaxed);
-        assert(y == &t[2]);
-        assert(a == &t[3]);
-        y = a.fetch_max(&t[0]);
-        assert(y == &t[3]);
-        assert(x == &t[3]);
-        ASSERT_NOEXCEPT(a.fetch_max(&t[0], std::memory_order_relaxed));
-      }
+      ASSERT_NOEXCEPT(a.fetch_max(&arr[0], std::memory_order_seq_cst));
+      test_fetch_max_pointer<U>(&arr[0], &arr[2], &arr[4], load, store, max);
     }
   }
 };
 
 int main(int, char**) {
   TestEachIntegralType<TestFetchMax>()();
+  TestEachPointerType<TestFetchMax>()();
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
 
-  TestEachPointerType<TestFetchMax>()();
-
   TestDoesNotHaveFetchMax<bool>()();
   TestDoesNotHaveFetchMax<UserAtomicType>()();
   TestDoesNotHaveFetchMax<LargeUserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index b178996b06085..e7f36363ae3a7 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -10,12 +10,14 @@
 // XFAIL: !has-64-bit-atomics
 
 // integral-type fetch_min(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+// T* fetch_min(T*, memory_order = memory_order::seq_cst) const noexcept;
 
 #include <atomic>
 #include <cassert>
 #include <concepts>
 #include <type_traits>
 
+#include "atomic_fetch_min_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -36,64 +38,38 @@ struct TestFetchMin {
     static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_min<T>);
 
     if constexpr (std::is_integral_v<T>) {
-      alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
+      alignas(std::atomic_ref<T>::required_alignment) T x{};
       std::atomic_ref<T> const a(x);
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
-        assert(y == T(3));
-        assert(x == T(2));
-        y = a.fetch_min(T(4));
-        assert(y == T(2));
-        assert(x == T(2));
-        ASSERT_NOEXCEPT(a.fetch_min(T(0)));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
-        assert(y == T(2));
-        assert(x == T(1));
-        y = a.fetch_min(T(4));
-        assert(y == T(1));
-        assert(x == T(1));
-        ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
-      }
+      auto load  = [&]() { return x; };
+      auto store = [&](T val) { x = val; };
+      auto min   = [&](T val, auto order) { return a.fetch_min(val, order); };
+
+      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_seq_cst));
+      test_fetch_min_integral<T>(load, store, min);
+
     } else if constexpr (std::is_pointer_v<T>) {
       using U = std::remove_pointer_t<T>;
-      U t[9]  = {};
-      alignas(std::atomic_ref<T>::required_alignment) T x{&t[3]};
-      std::atomic_ref<T> const a(x);
+      U arr[5]{};
+      alignas(std::atomic_ref<T>::required_alignment) T p{};
+      std::atomic_ref<T> const a(p);
+
+      auto load  = [&]() { return p; };
+      auto store = [&](T val) { p = val; };
+      auto min   = [&](T val, auto order) { return a.fetch_min(val, order); };
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(&t[2]);
-        assert(y == &t[3]);
-        assert(x == &t[2]);
-        y = a.fetch_min(&t[4]);
-        assert(y == &t[2]);
-        assert(x == &t[2]);
-        ASSERT_NOEXCEPT(a.fetch_min(&t[0]));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(&t[1], std::memory_order_relaxed);
-        assert(y == &t[2]);
-        assert(a == &t[1]);
-        y = a.fetch_min(&t[4]);
-        assert(y == &t[1]);
-        assert(x == &t[1]);
-        ASSERT_NOEXCEPT(a.fetch_min(&t[0], std::memory_order_relaxed));
-      }
+      ASSERT_NOEXCEPT(a.fetch_min(&arr[0], std::memory_order_seq_cst));
+      test_fetch_min_pointer<U>(&arr[0], &arr[2], &arr[4], load, store, min);
     }
   }
 };
 
 int main(int, char**) {
   TestEachIntegralType<TestFetchMin>()();
+  TestEachPointerType<TestFetchMin>()();
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
 
-  TestEachPointerType<TestFetchMin>()();
-
   TestDoesNotHaveFetchMin<bool>()();
   TestDoesNotHaveFetchMin<UserAtomicType>()();
   TestDoesNotHaveFetchMin<LargeUserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index dc2274a1056cb..7b2a5715ddddd 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -19,11 +19,10 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_max_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -35,23 +34,18 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(std::atomic_fetch_max(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
-
-  // atomic_fetch_max
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_max(&t, T(2)) == T(1));
-    assert(t == T(2));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_max(T(2));
-    assert(r == T(3));
-    assert(t == T(3));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto max   = [&](T val, auto) { return std::atomic_fetch_max(&a, val); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_max(&a, T(0)));
+    test_fetch_max_integral<T>(load, store, max);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_max concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_max should leave
+  // the atomic at the global maximum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
@@ -95,8 +89,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto max   = [&](T* val, auto) { return std::atomic_fetch_max(&a, val); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_max(&a, p0));
+  test_fetch_max_pointer<T>(p0, p2, p4, load, store, max);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index d878f6abd246d..e811f114708c8 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -21,11 +21,10 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_max_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -37,24 +36,18 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(
-      std::atomic_fetch_max_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
-
-  // atomic_fetch_max_explicit
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-    assert(t == T(2));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_max(T(2), std::memory_order_seq_cst);
-    assert(r == T(3));
-    assert(t == T(3));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto max   = [&](T val, auto order) { return std::atomic_fetch_max_explicit(&a, val, order); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&a, T(0), std::memory_order_seq_cst));
+    test_fetch_max_integral<T>(load, store, max);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_max_explicit concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_max_explicit should
+  // leave the atomic at the global maximum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
@@ -98,8 +91,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto max   = [&](T* val, auto order) { return std::atomic_fetch_max_explicit(&a, val, order); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&a, p0, std::memory_order_seq_cst));
+  test_fetch_max_pointer<T>(p0, p2, p4, load, store, max);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 43277f6a7977a..df40cc7a92c7b 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -19,11 +19,11 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
+#include <limits>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_min_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -35,34 +35,29 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(std::atomic_fetch_min(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
-
-  // atomic_fetch_min
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_min(&t, T(2)) == T(1));
-    assert(t == T(1));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_min(T(2));
-    assert(r == T(3));
-    assert(t == T(2));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto min   = [&](T val, auto) { return std::atomic_fetch_min(&a, val); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_min(&a, T(0)));
+    test_fetch_min_integral<T>(load, store, min);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_min concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_min should leave
+  // the atomic at the global minimum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
 
-    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+    MaybeVolatile<std::atomic<T>> at(std::numeric_limits<T>::max());
 
     std::vector<std::thread> threads;
     threads.reserve(number_of_threads);
     for (auto i = 0; i < number_of_threads; ++i) {
       threads.push_back(support::make_test_thread([&at, i]() {
-        for (auto j = loop - 1; j >= 0; --j) {
+        for (auto j = 0; j < loop; ++j) {
           std::atomic_fetch_min(&at, T(i * loop + j));
         }
       }));
@@ -72,15 +67,7 @@ void test_impl() {
       thread.join();
     }
 
-    const auto times = [](int n) {
-      T res(n);
-      for (auto i = n - 1; i >= 0; --i) {
-        res = std::min<T>(res, i);
-      }
-      return res;
-    };
-
-    assert(at.load() == times(number_of_threads * loop));
+    assert(at.load() == T(0));
   }
 #endif
 }
@@ -95,8 +82,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto min   = [&](T* val, auto) { return std::atomic_fetch_min(&a, val); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_min(&a, p0));
+  test_fetch_min_pointer<T>(p0, p2, p4, load, store, min);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 75bf200812ab0..2295b942b86de 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -21,11 +21,11 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
+#include <limits>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_min_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -37,35 +37,29 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(
-      std::atomic_fetch_min_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
-
-  // atomic_fetch_min_explicit
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-    assert(t == T(1));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_min(T(2), std::memory_order_seq_cst);
-    assert(r == T(3));
-    assert(t == T(2));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto min   = [&](T val, auto order) { return std::atomic_fetch_min_explicit(&a, val, order); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&a, T(0), std::memory_order_seq_cst));
+    test_fetch_min_integral<T>(load, store, min);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_min_explicit concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_min_explicit should
+  // leave the atomic at the global minimum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
 
-    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+    MaybeVolatile<std::atomic<T>> at(std::numeric_limits<T>::max());
 
     std::vector<std::thread> threads;
     threads.reserve(number_of_threads);
     for (auto i = 0; i < number_of_threads; ++i) {
       threads.push_back(support::make_test_thread([&at, i]() {
-        for (auto j = loop - 1; j >= 0; --j) {
+        for (auto j = 0; j < loop; ++j) {
           std::atomic_fetch_min_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
         }
       }));
@@ -75,15 +69,7 @@ void test_impl() {
       thread.join();
     }
 
-    const auto times = [](int n) {
-      T res(n);
-      for (auto i = n - 1; i >= 0; --i) {
-        res = std::min<T>(res, i);
-      }
-      return res;
-    };
-
-    assert(at.load() == times(number_of_threads * loop));
+    assert(at.load() == T(0));
   }
 #endif
 }
@@ -98,8 +84,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto min   = [&](T* val, auto order) { return std::atomic_fetch_min_explicit(&a, val, order); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&a, p0, std::memory_order_seq_cst));
+  test_fetch_min_pointer<T>(p0, p2, p4, load, store, min);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/support/atomic_fetch_max_helper.h b/libcxx/test/support/atomic_fetch_max_helper.h
new file mode 100644
index 0000000000000..ff4bd7aa38912
--- /dev/null
+++ b/libcxx/test/support/atomic_fetch_max_helper.h
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
+#define TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
+
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Test fetch_max for integral types
+// LoadOp: () -> T - reads current value
+// StoreOp: (T) -> void - stores a value
+// MaxOp: (T, memory_order) -> T - performs fetch_max operation
+template <class T, class LoadOp, class StoreOp, class MaxOp>
+void test_fetch_max_integral(LoadOp load, StoreOp store, MaxOp max) {
+  // Test basic fetch_max (update to larger value)
+  {
+    store(T(10));
+    T old = max(T(20), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(20));
+  }
+
+  // Test with smaller value (no change)
+  {
+    store(T(10));
+    T old = max(T(5), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(10));
+  }
+
+  // Test different memory orderings
+  {
+    store(T(15));
+    T old = max(T(25), std::memory_order_seq_cst);
+    assert(old == T(15));
+    assert(load() == T(25));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(25), std::memory_order_relaxed);
+    assert(old == T(10));
+    assert(load() == T(25));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(30), std::memory_order_acquire);
+    assert(old == T(10));
+    assert(load() == T(30));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(15), std::memory_order_release);
+    assert(old == T(10));
+    assert(load() == T(15));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(22), std::memory_order_acq_rel);
+    assert(old == T(10));
+    assert(load() == T(22));
+  }
+
+  {
+    store(T(15));
+    T old = max(T(35), std::memory_order_relaxed);
+    assert(old == T(15));
+    assert(load() == T(35));
+  }
+
+  // Test return type
+  {
+    store(T(10));
+    static_assert(std::is_same_v<decltype(max(T(20), std::memory_order_seq_cst)), T>);
+  }
+}
+
+// Test fetch_max for pointer types
+// LoadOp: () -> T* - reads current pointer value
+// StoreOp: (T*) -> void - stores a pointer value
+// MaxOp: (T*, memory_order) -> T* - performs fetch_max operation
+template <class T, class LoadOp, class StoreOp, class MaxOp>
+void test_fetch_max_pointer(T* p0, T* p2, T* p4, LoadOp load, StoreOp store, MaxOp max) {
+  // Test basic fetch_max (update to larger pointer)
+  {
+    store(p2);
+    T* old = max(p4, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p4);
+  }
+
+  // Test with smaller pointer (no change)
+  {
+    store(p2);
+    T* old = max(p0, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p2);
+  }
+
+  // Test different memory orderings
+  {
+    store(p0);
+    T* old = max(p2, std::memory_order_seq_cst);
+    assert(old == p0);
+    assert(load() == p2);
+  }
+
+  {
+    store(p0);
+    T* old = max(p2, std::memory_order_relaxed);
+    assert(old == p0);
+    assert(load() == p2);
+  }
+
+  {
+    store(p0);
+    T* old = max(p4, std::memory_order_acquire);
+    assert(old == p0);
+    assert(load() == p4);
+  }
+
+  {
+    store(p0);
+    T* old = max(p4, std::memory_order_release);
+    assert(old == p0);
+    assert(load() == p4);
+  }
+
+  // Test return type
+  {
+    store(p2);
+    static_assert(std::is_same_v<decltype(max(p4, std::memory_order_seq_cst)), T*>);
+  }
+}
+
+#endif // TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
diff --git a/libcxx/test/support/atomic_fetch_min_helper.h b/libcxx/test/support/atomic_fetch_min_helper.h
new file mode 100644
index 0000000000000..20a3558296118
--- /dev/null
+++ b/libcxx/test/support/atomic_fetch_min_helper.h
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H
+#define TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H
+
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Test fetch_min for integral types
+// LoadOp: () -> T - reads current value
+// StoreOp: (T) -> void - stores a value
+// MinOp: (T, memory_order) -> T - performs fetch_min operation
+template <class T, class LoadOp, class StoreOp, class MinOp>
+void test_fetch_min_integral(LoadOp load, StoreOp store, MinOp min) {
+  // Test basic fetch_min (update to smaller value)
+  {
+    store(T(10));
+    T old = min(T(5), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(5));
+  }
+
+  // Test with larger value (no change)
+  {
+    store(T(10));
+    T old = min(T(20), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(10));
+  }
+
+  // Test different memory orderings
+  {
+    store(T(15));
+    T old = min(T(8), std::memory_order_seq_cst);
+    assert(old == T(15));
+    assert(load() == T(8));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(3), std::memory_order_relaxed);
+    assert(old == T(10));
+    assert(load() == T(3));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(2), std::memory_order_acquire);
+    assert(old == T(10));
+    assert(load() == T(2));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(7), std::memory_order_release);
+    assert(old == T(10));
+    assert(load() == T(7));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(4), std::memory_order_acq_rel);
+    assert(old == T(10));
+    assert(load() == T(4));
+  }
+
+  {
+    store(T(15));
+    T old = min(T(6), std::memory_order_relaxed);
+    assert(old == T(15));
+    assert(load() == T(6));
+  }
+
+  // Test return type
+  {
+    store(T(10));
+    static_assert(std::is_same_v<decltype(min(T(5), std::memory_order_seq_cst)), T>);
+  }
+}
+
+// Test fetch_min for pointer types
+// LoadOp: () -> T* - reads current pointer value
+// StoreOp: (T*) -> void - stores a pointer value
+// MinOp: (T*, memory_order) -> T* - performs fetch_min operation
+template <class T, class LoadOp, class StoreOp, class MinOp>
+void test_fetch_min_pointer(T* p0, T* p2, T* p4, LoadOp load, StoreOp store, MinOp min) {
+  // Test basic fetch_min (update to smaller pointer)
+  {
+    store(p2);
+    T* old = min(p0, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p0);
+  }
+
+  // Test with larger pointer (no change)
+  {
+    store(p2);
+    T* old = min(p4, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p2);
+  }
+
+  // Test different memory orderings
+  {
+    store(p4);
+    T* old = min(p2, std::memory_order_seq_cst);
+    assert(old == p4);
+    assert(load() == p2);
+  }
+
+  {
+    store(p4);
+    T* old = min(p2, std::memory_order_relaxed);
+    assert(old == p4);
+    assert(load() == p2);
+  }
+
+  {
+    store(p4);
+    T* old = min(p0, std::memory_order_acquire);
+    assert(old == p4);
+    assert(load() == p0);
+  }
+
+  {
+    store(p4);
+    T* old = min(p0, std::memory_order_release);
+    assert(old == p4);
+    assert(load() == p0);
+  }
+
+  // Test return type
+  {
+    store(p2);
+    static_assert(std::is_same_v<decltype(min(p0, std::memory_order_seq_cst)), T*>);
+  }
+}
+
+#endif // TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H

>From f8b41b52f67d6ca6dd03617efb6d56ec1c20c6ae Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 16 Jun 2026 07:25:18 -0400
Subject: [PATCH 25/27] Use a CAS loop for pointer types in
 __cxx_atomic_fetch_{max,min} on c11.h

Clang's __c11_atomic_fetch_max/min builtins reject pointer arguments
("address argument to atomic operation must be a pointer to atomic integer
or supported floating point type"). The four __cxx_atomic_fetch_{max,min}
overloads in support/c11.h were unconditionally calling those builtins, so
std::atomic<T*>::fetch_max/min failed to compile on every Clang-based
config. support/gcc.h already had this dispatch via #if __has_builtin(...)
falling back to a CAS loop, which is why generic-gcc was passing.

Dispatch on is_pointer<_Tp>::value: pointer types use a CAS loop matching
gcc.h's body, others keep calling the builtin.

Assisted by Claude
---
 libcxx/include/__atomic/support/c11.h | 56 +++++++++++++++++++++++----
 1 file changed, 48 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0d74d55188a3d..603ffc690b104 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -13,6 +13,7 @@
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/remove_const.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -259,30 +260,69 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
       std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
 }
 
+// Clang's __c11_atomic_fetch_max/min builtins do not accept pointer
+// arguments, so dispatch to a CAS loop for pointer types.
+// TODO: Use the builtin for pointer types once Clang accepts them.
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret > __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_max(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret > __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_max(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret < __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_min(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret < __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_min(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 
 _LIBCPP_END_NAMESPACE_STD

>From 77d8902e33364fe5887036f6dc36550094841d7b Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 16 Jun 2026 10:06:33 -0400
Subject: [PATCH 26/27] Switch __cxx_atomic_fetch_{max,min} pointer dispatch to
 SFINAE for C++03 compat

I know they're not needed outside of C++26, but this header should be
usable in all standard modes in principle.
---
 libcxx/include/__atomic/support/c11.h | 103 ++++++++++++++------------
 1 file changed, 55 insertions(+), 48 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 603ffc690b104..58a97fcfb3261 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -13,6 +13,7 @@
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/remove_const.h>
 
@@ -263,66 +264,72 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 // Clang's __c11_atomic_fetch_max/min builtins do not accept pointer
 // arguments, so dispatch to a CAS loop for pointer types.
 // TODO: Use the builtin for pointer types once Clang accepts them.
-template <class _Tp>
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret > __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_max(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret > __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+}
+
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret > __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_max(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret > __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret < __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_min(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret < __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+}
+
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret < __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_min(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret < __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 _LIBCPP_END_NAMESPACE_STD

>From 269691062efd541709388ab2b330e91b9b94cf91 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 26 Jun 2026 21:35:32 +0800
Subject: [PATCH 27/27] eliminate adl warning

---
 libcxx/include/__atomic/support/c11.h | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 58a97fcfb3261..899502422570a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -273,11 +273,12 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, mem
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -290,11 +291,12 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -307,11 +309,12 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, mem
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -324,11 +327,12 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 


From libcxx-commits at lists.llvm.org  Mon Jul 13 07:18:13 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 07:18:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a54f3a5.c574b9b4.21ead0.5b44@mx.google.com>


https://github.com/Himadhith edited https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Mon Jul 13 10:50:18 2026
From: libcxx-commits at lists.llvm.org (Jake Egan via libcxx-commits)
Date: Mon, 13 Jul 2026 10:50:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a55255a.4a342cbc.ca844.299e@mx.google.com>


https://github.com/jakeegan edited https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Mon Jul 13 11:58:08 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 13 Jul 2026 11:58:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][string] Test: default
 constructed allocators can be unequal (PR #195839)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/195839@github.com>
Message-ID: <6a553540.bc28b987.90005.8a89@mx.google.com>


================
@@ -515,4 +515,40 @@ struct SocccAllocator {
   using propagate_on_container_swap            = std::false_type;
 };
 
+// Track how many times the allocator was default-constructed
+//
+
+template <class T>
+class ControlledDefaultConstructorAllocator : public std::allocator<T> {
----------------
ldionne wrote:

I would avoid inheriting from `std::allocator`: let's use the pattern we use above where we do `std::allocator<T>().allocate(n)` instead. This makes this allocator not rely on `std::allocator` properties implicitly.

https://github.com/llvm/llvm-project/pull/195839

From libcxx-commits at lists.llvm.org  Mon Jul 13 11:58:08 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 13 Jul 2026 11:58:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][string] Test: default
 constructed allocators can be unequal (PR #195839)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/195839@github.com>
Message-ID: <6a553540.bd93e731.358318.e85e@mx.google.com>


================
@@ -515,4 +515,40 @@ struct SocccAllocator {
   using propagate_on_container_swap            = std::false_type;
 };
 
+// Track how many times the allocator was default-constructed
+//
+
+template <class T>
+class ControlledDefaultConstructorAllocator : public std::allocator<T> {
+public:
+  template <class U>
+  struct rebind {
+    typedef ControlledDefaultConstructorAllocator<U> other;
+  };
+
+  ControlledDefaultConstructorAllocator() TEST_NOEXCEPT { default_constructed_tag_ = next_default_constructed_tag_++; }
+  bool is_base() const TEST_NOEXCEPT { return default_constructed_tag_ == base_default_constructed_tag_; }
+
+  static void reset_to_base() { next_default_constructed_tag_ = base_default_constructed_tag_; }
+
+  friend bool operator==(ControlledDefaultConstructorAllocator x, ControlledDefaultConstructorAllocator y) {
+    return x.default_constructed_tag_ == y.default_constructed_tag_;
+  }
+  friend bool operator!=(ControlledDefaultConstructorAllocator x, ControlledDefaultConstructorAllocator y) {
+    return !(x == y);
+  }
+
+  const static int base_default_constructed_tag_;
+  static int next_default_constructed_tag_;
+
+  int default_constructed_tag_;
----------------
ldionne wrote:

```suggestion
  int value_;
```

https://github.com/llvm/llvm-project/pull/195839

From libcxx-commits at lists.llvm.org  Mon Jul 13 11:58:08 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 13 Jul 2026 11:58:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][string] Test: default
 constructed allocators can be unequal (PR #195839)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/195839@github.com>
Message-ID: <6a553540.449bfb44.172ed9.d761@mx.google.com>


================
@@ -515,4 +515,40 @@ struct SocccAllocator {
   using propagate_on_container_swap            = std::false_type;
 };
 
+// Track how many times the allocator was default-constructed
+//
+
+template <class T>
+class ControlledDefaultConstructorAllocator : public std::allocator<T> {
+public:
+  template <class U>
+  struct rebind {
+    typedef ControlledDefaultConstructorAllocator<U> other;
+  };
+
+  ControlledDefaultConstructorAllocator() TEST_NOEXCEPT { default_constructed_tag_ = next_default_constructed_tag_++; }
+  bool is_base() const TEST_NOEXCEPT { return default_constructed_tag_ == base_default_constructed_tag_; }
+
+  static void reset_to_base() { next_default_constructed_tag_ = base_default_constructed_tag_; }
----------------
ldionne wrote:

```suggestion
  static void set_default_value(int value) { next_value = value; }
```

That way in the tests you can do:

```
A::set_default_value(42);
basic_string<char, A> s(first, last);
assert(s.get_allocator().value == 42);
```

This removes the need for `is_base` and for understanding that there's a special "base" value, since all the knowledge is now local to the callsite and obvious from the names used.

https://github.com/llvm/llvm-project/pull/195839

From libcxx-commits at lists.llvm.org  Mon Jul 13 11:58:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 13 Jul 2026 11:58:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][string] Test: default
 constructed allocators can be unequal (PR #195839)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/195839@github.com>
Message-ID: <6a553541.d76d81af.39f9be.9735@mx.google.com>


================
@@ -150,11 +148,41 @@ TEST_CONSTEXPR_CXX20 bool test() {
   return true;
 }
 
+template <class SV>
+void test_default_alloc_arg(SV sv, std::size_t pos, std::size_t n) {
+  using A = ControlledDefaultConstructorAllocator<char>;
+  using S = std::basic_string<char, std::char_traits<char>, A>;
+
+  typedef typename S::size_type Size;
+  if (pos <= sv.size()) {
+    A::reset_to_base();
+    S s2(sv, static_cast<Size>(pos), static_cast<Size>(n));
+    assert(s2.get_allocator().is_base());
+  }
+}
+
+void test_default_alloc_arg() {
+  using SV = std::basic_string_view<char, std::char_traits<char> >;
----------------
ldionne wrote:

I think this belongs in `TEST_CONSTEXPR_CXX20 bool test()` (new line 110) instead, with `if (!TEST_IS_CONSTANT_EVALUATED)` since this uses a static. I'd like to avoid adding new layers of complexity to these already complicated tests (which jump through various test functions).

This comment also applies to other test files.

https://github.com/llvm/llvm-project/pull/195839

From libcxx-commits at lists.llvm.org  Mon Jul 13 11:58:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 13 Jul 2026 11:58:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][string] Test: default
 constructed allocators can be unequal (PR #195839)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/195839@github.com>
Message-ID: <6a553541.07d7ca98.9dee3.0b44@mx.google.com>


================
@@ -515,4 +515,40 @@ struct SocccAllocator {
   using propagate_on_container_swap            = std::false_type;
 };
 
+// Track how many times the allocator was default-constructed
+//
+
+template <class T>
+class ControlledDefaultConstructorAllocator : public std::allocator<T> {
+public:
+  template <class U>
+  struct rebind {
+    typedef ControlledDefaultConstructorAllocator<U> other;
+  };
+
+  ControlledDefaultConstructorAllocator() TEST_NOEXCEPT { default_constructed_tag_ = next_default_constructed_tag_++; }
----------------
ldionne wrote:

I'm not certain it's actually useful to increment the value? We're not trying to test that an allocator is default constructed only once, we're trying to ensure that a default-constructed allocator is used, period.

https://github.com/llvm/llvm-project/pull/195839

From libcxx-commits at lists.llvm.org  Mon Jul 13 12:45:59 2026
From: libcxx-commits at lists.llvm.org (Demetrius Kanios via libcxx-commits)
Date: Mon, 13 Jul 2026 12:45:59 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxxabi] [libunwind] [llvm]
 [WebAssembly] Call EH personality function instead of
 `Unwind_CallPersonality` (PR #209282)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209282@github.com>
Message-ID: <6a554077.160324b0.195192.71e2@mx.google.com>


https://github.com/QuantumSegfault updated https://github.com/llvm/llvm-project/pull/209282

>From 03a177003d05f9275a59ca6e6c79952abda782da Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 13 Jul 2026 12:30:49 -0700
Subject: [PATCH 1/2] Call declared personality function

---
 clang/lib/CodeGen/CGException.cpp             |  9 ++-
 libcxxabi/src/cxa_personality.cpp             | 22 ++++++-
 libunwind/include/unwind.h                    |  4 ++
 libunwind/include/unwind_wasm.h               | 27 ++++++++
 libunwind/src/Unwind-wasm.c                   | 39 -----------
 libunwind/src/config.h                        |  2 +-
 llvm/include/llvm/IR/RuntimeLibcalls.td       |  4 --
 llvm/lib/CodeGen/WasmEHPrepare.cpp            | 64 ++++++++-----------
 .../WebAssembly/cfg-stackify-eh-legacy.ll     |  2 +-
 .../WebAssembly/cfg-stackify-eh-legacy.mir    |  2 +-
 .../CodeGen/WebAssembly/cfg-stackify-eh.ll    |  8 +--
 llvm/test/CodeGen/WebAssembly/eh-lsda.ll      |  2 +-
 .../CodeGen/WebAssembly/exception-legacy.ll   |  4 +-
 .../CodeGen/WebAssembly/exception-legacy.mir  |  2 +-
 llvm/test/CodeGen/WebAssembly/exception.ll    |  4 +-
 .../CodeGen/WebAssembly/function-info.mir     |  2 +-
 .../WebAssembly/lower-wasm-ehsjlj-phi.ll      |  2 +-
 .../CodeGen/WebAssembly/lower-wasm-ehsjlj.ll  |  2 +-
 .../WebAssembly/wasm-eh-em-sjlj-error.ll      |  2 +-
 .../wasm-eh-invalid-personality.ll            |  2 +-
 .../CodeGen/WebAssembly/wasm-eh-prepare.ll    |  8 +--
 .../wasm-eh-sjlj-setjmp-within-catch.ll       |  2 +-
 22 files changed, 108 insertions(+), 107 deletions(-)
 create mode 100644 libunwind/include/unwind_wasm.h

diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp
index 99dfaa80be429..25b57a0334413 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -265,7 +265,14 @@ const EHPersonality &EHPersonality::get(CodeGenFunction &CGF) {
 
 static llvm::FunctionCallee getPersonalityFn(CodeGenModule &CGM,
                                              const EHPersonality &Personality) {
-  return CGM.CreateRuntimeFunction(llvm::FunctionType::get(CGM.Int32Ty, true),
+  llvm::FunctionType *FTy;
+
+  if (Personality.isWasmPersonality()) {
+    FTy = llvm::FunctionType::get(CGM.VoidTy, {CGM.VoidPtrTy}, false);
+  } else {
+    FTy = llvm::FunctionType::get(CGM.Int32Ty, true);
+  }
+  return CGM.CreateRuntimeFunction(FTy,
                                    Personality.PersonalityFn,
                                    llvm::AttributeList(), /*Local=*/true);
 }
diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index 6599f89740113..970cd0355ddf4 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1011,9 +1011,7 @@ static inline void get_landing_pad(__cxa_catch_temp_type &dest,
 #endif
 }
 
-#ifdef __WASM_EXCEPTIONS__
-_Unwind_Reason_Code __gxx_personality_wasm0
-#elif defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
+#if (defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)) || defined(__WASM_EXCEPTIONS__)
 static _Unwind_Reason_Code __gxx_personality_imp
 #else
 _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code
@@ -1114,6 +1112,24 @@ __gxx_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame,
 }
 #endif
 
+
+#ifdef __WASM_EXCEPTIONS__
+extern "C" _LIBCXXABI_FUNC_VIS
+void __gxx_wasm_personality_v0(void* exception_ptr) {
+  struct _Unwind_Exception *exception_object =
+      (struct _Unwind_Exception *)exception_ptr;
+
+  // Reset the selector.
+  __wasm_lpad_context.selector = 0;
+
+  // Call personality function. Wasm does not have two-phase unwinding, so we
+  // only do the cleanup phase.
+  __gxx_personality_imp(
+      1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
+      (struct _Unwind_Context *)&__wasm_lpad_context);
+}
+#endif
+
 #else
 
 extern "C" _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Exception*, _Unwind_Context*);
diff --git a/libunwind/include/unwind.h b/libunwind/include/unwind.h
index b1775d3a3decc..93a9d92f327d3 100644
--- a/libunwind/include/unwind.h
+++ b/libunwind/include/unwind.h
@@ -61,6 +61,10 @@ typedef struct _Unwind_Context _Unwind_Context;   // opaque
 #include <unwind_itanium.h>
 #endif
 
+#if defined(__WASM_EXCEPTIONS__)
+#include <unwind_wasm.h>
+#endif
+
 typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
     (int version,
      _Unwind_Action actions,
diff --git a/libunwind/include/unwind_wasm.h b/libunwind/include/unwind_wasm.h
new file mode 100644
index 0000000000000..7bf3f30562bd8
--- /dev/null
+++ b/libunwind/include/unwind_wasm.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 __WASM_UNWIND_H__
+#define __WASM_UNWIND_H__
+
+#include <threads.h>
+
+struct _Unwind_LandingPadContext {
+  // Input information to personality function
+  uintptr_t lpad_index; // landing pad index
+  uintptr_t lsda;       // LSDA address
+
+  // Output information computed by personality function
+  uintptr_t selector; // selector value
+};
+
+// Communication channel between compiler-generated user code and personality
+// function
+extern thread_local struct _Unwind_LandingPadContext __wasm_lpad_context;
+
+#endif // __WASM_UNWIND_H__
diff --git a/libunwind/src/Unwind-wasm.c b/libunwind/src/Unwind-wasm.c
index c0ca9b775d244..81793b3f36060 100644
--- a/libunwind/src/Unwind-wasm.c
+++ b/libunwind/src/Unwind-wasm.c
@@ -19,47 +19,8 @@
 #include "unwind.h"
 #include <threads.h>
 
-_Unwind_Reason_Code __gxx_personality_wasm0(int version, _Unwind_Action actions,
-                                            uint64_t exceptionClass,
-                                            _Unwind_Exception *unwind_exception,
-                                            _Unwind_Context *context);
-
-struct _Unwind_LandingPadContext {
-  // Input information to personality function
-  uintptr_t lpad_index; // landing pad index
-  uintptr_t lsda;       // LSDA address
-
-  // Output information computed by personality function
-  uintptr_t selector; // selector value
-};
-
-// Communication channel between compiler-generated user code and personality
-// function
 thread_local struct _Unwind_LandingPadContext __wasm_lpad_context;
 
-/// Calls to this function are in landing pads in compiler-generated user code.
-/// In other EH schemes, stack unwinding is done by libunwind library, which
-/// calls the personality function for each frame it lands. On the other hand,
-/// WebAssembly stack unwinding process is performed by a VM, and the
-/// personality function cannot be called from there. So the compiler inserts a
-/// call to this function in landing pads in the user code, which in turn calls
-/// the personality function.
-_Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
-  struct _Unwind_Exception *exception_object =
-      (struct _Unwind_Exception *)exception_ptr;
-  _LIBUNWIND_TRACE_API("_Unwind_CallPersonality(exception_object=%p)",
-                       (void *)exception_object);
-
-  // Reset the selector.
-  __wasm_lpad_context.selector = 0;
-
-  // Call personality function. Wasm does not have two-phase unwinding, so we
-  // only do the cleanup phase.
-  return __gxx_personality_wasm0(
-      1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
-      (struct _Unwind_Context *)&__wasm_lpad_context);
-}
-
 /// Called by __cxa_throw.
 _LIBUNWIND_EXPORT _Unwind_Reason_Code
 _Unwind_RaiseException(_Unwind_Exception *exception_object) {
diff --git a/libunwind/src/config.h b/libunwind/src/config.h
index f017403fa2234..fd1aba25e6822 100644
--- a/libunwind/src/config.h
+++ b/libunwind/src/config.h
@@ -75,7 +75,7 @@
   #define _LIBUNWIND_EXPORT
   #define _LIBUNWIND_HIDDEN
 #else
-  #if !defined(__ELF__) && !defined(__MACH__) && !defined(_AIX)
+  #if !defined(__ELF__) && !defined(__MACH__) && !defined(_AIX) && !defined(__wasm__)
     #define _LIBUNWIND_EXPORT __declspec(dllexport)
     #define _LIBUNWIND_HIDDEN
   #else
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.td b/llvm/include/llvm/IR/RuntimeLibcalls.td
index a7e45570c1b24..3745001b936f8 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.td
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.td
@@ -1849,9 +1849,6 @@ defset list<RuntimeLibcallImpl> SjLjExceptionHandlingLibcalls = {
   def _Unwind_SjLj_Unregister : RuntimeLibcallImpl<UNWIND_UNREGISTER>;
 }
 
-// Only used on wasm?
-def _Unwind_CallPersonality : RuntimeLibcallImpl<UNWIND_CALL_PERSONALITY>;
-
 // Used on OpenBSD
 def __stack_smash_handler : RuntimeLibcallImpl<STACK_SMASH_HANDLER>;
 
@@ -3567,7 +3564,6 @@ def WasmSystemLibrary
       (add DefaultRuntimeLibcallImpls, Int128RTLibcalls,
            CompilerRTOnlyInt64Libcalls, CompilerRTOnlyInt128Libcalls,
            exp10f, exp10,
-           _Unwind_CallPersonality,
            emscripten_return_address,
            LibcallImpls<(add __small_printf,
                              __small_sprintf,
diff --git a/llvm/lib/CodeGen/WasmEHPrepare.cpp b/llvm/lib/CodeGen/WasmEHPrepare.cpp
index b83bcf67716f9..363e7221f7118 100644
--- a/llvm/lib/CodeGen/WasmEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WasmEHPrepare.cpp
@@ -28,7 +28,7 @@
 //   wasm.landingpad.index(index);
 //   __wasm_lpad_context.lpad_index = index;
 //   __wasm_lpad_context.lsda = wasm.lsda();
-//   _Unwind_CallPersonality(exn);
+//   personality_fn(exn);
 //   selector = __wasm_lpad_context.selector;
 //   ...
 //
@@ -38,18 +38,13 @@
 // exception is thrown. After the stack is unwound, the control flow is
 // transfered to WebAssembly 'catch' instruction.
 //
-// Unwinding the stack is not done by libunwind but the VM, so the personality
-// function in libcxxabi cannot be called from libunwind during the unwinding
-// process. So after a catch instruction, we insert a call to a wrapper function
-// in libunwind that in turn calls the real personality function.
-//
 // In Itanium EH, if the personality function decides there is no matching catch
 // clause in a call frame and no cleanup action to perform, the unwinder doesn't
 // stop there and continues unwinding. But in Wasm EH, the unwinder stops at
 // every call frame with a catch intruction, after which the personality
 // function is called from the compiler-generated user code here.
 //
-// In libunwind, we have this struct that serves as a communincation channel
+// In libunwind, we have this struct that serves as a communication channel
 // between the compiler-generated user code and the personality function in
 // libcxxabi.
 //
@@ -60,20 +55,8 @@
 // };
 // struct _Unwind_LandingPadContext __wasm_lpad_context = ...;
 //
-// And this wrapper in libunwind calls the personality function.
-//
-// _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
-//   struct _Unwind_Exception *exception_obj =
-//       (struct _Unwind_Exception *)exception_ptr;
-//   _Unwind_Reason_Code ret = __gxx_personality_v0(
-//       1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj,
-//       (struct _Unwind_Context *)__wasm_lpad_context);
-//   return ret;
-// }
-//
 // We pass a landing pad index, and the address of LSDA for the current function
-// to the wrapper function _Unwind_CallPersonality in libunwind, and we retrieve
-// the selector after it returns.
+// to the personality function, and we retrieve the selector after it returns.
 //
 //===----------------------------------------------------------------------===//
 
@@ -111,8 +94,7 @@ class WasmEHPrepareImpl {
   Function *GetExnF = nullptr;      // wasm.get.exception() intrinsic
   Function *CatchF = nullptr;       // wasm.catch() intrinsic
   Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
-  FunctionCallee CallPersonalityF =
-      nullptr; // _Unwind_CallPersonality() wrapper
+  FunctionCallee PersonalityF = nullptr;
 
   bool prepareThrows(Function &F);
   bool prepareEHPads(Function &F);
@@ -235,11 +217,14 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
   if (CatchPads.empty() && CleanupPads.empty())
     return false;
 
-  if (!F.hasPersonalityFn() ||
-      !isScopedEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) {
+  if (!F.hasPersonalityFn())
+    return false;
+
+  auto Personality = classifyEHPersonality(F.getPersonalityFn());
+
+  if (!isScopedEHPersonality(Personality)) {
     report_fatal_error("Function '" + F.getName() +
-                       "' does not have a correct Wasm personality function "
-                       "'__gxx_wasm_personality_v0'");
+                       "' does not have a supported Wasm personality function");
   }
   assert(F.hasPersonalityFn() && "Personality function not found");
 
@@ -274,15 +259,12 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
   // instruction selection.
   CatchF = Intrinsic::getOrInsertDeclaration(&M, Intrinsic::wasm_catch);
 
-  // FIXME: Verify this is really supported for current module.
-  StringRef UnwindCallPersonalityName =
-      RTLIB::RuntimeLibcallsInfo::getLibcallImplName(
-          RTLIB::impl__Unwind_CallPersonality);
+  auto *PersPrototype = FunctionType::get(
+      IRB.getInt32Ty(), {IRB.getPtrTy()}, false);
+  PersonalityF =
+      M.getOrInsertFunction(getEHPersonalityName(Personality), PersPrototype);
 
-  // _Unwind_CallPersonality() wrapper function, which calls the personality
-  CallPersonalityF = M.getOrInsertFunction(UnwindCallPersonalityName,
-                                           IRB.getInt32Ty(), IRB.getPtrTy());
-  if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee()))
+  if (Function *F = dyn_cast<Function>(PersonalityF.getCallee()))
     F->setDoesNotThrow();
 
   unsigned Index = 0;
@@ -367,9 +349,17 @@ void WasmEHPrepareImpl::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
   // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
   IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
 
-  // Pseudocode: _Unwind_CallPersonality(exn);
-  CallInst *PersCI = IRB.CreateCall(CallPersonalityF, CatchCI,
-                                    OperandBundleDef("funclet", CPI));
+  // Pseudocode: personality_fn(exn);
+  CallInst *PersCI;
+
+  // Grab direct function when possible to use `call` instead of `call_indirect`
+  if (Function *F = dyn_cast<Function>(PersonalityF.getCallee()))
+    PersCI = IRB.CreateCall(F, CatchCI,
+                            OperandBundleDef("funclet", CPI));
+  else
+    PersCI = IRB.CreateCall(PersonalityF, CatchCI,
+                            OperandBundleDef("funclet", CPI));
+
   PersCI->setDoesNotThrow();
 
   // Pseudocode: int selector = __wasm_lpad_context.selector;
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
index a35129effcdee..301f7d81a4274 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
@@ -1911,7 +1911,7 @@ declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
 ; Function Attrs: nounwind
 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
 
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir
index 324dd29f2295e..f0341ed420241 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir
@@ -3,7 +3,7 @@
 --- |
   target triple = "wasm32-unknown-unknown"
 
-  declare i32 @__gxx_wasm_personality_v0(...)
+  declare void @__gxx_wasm_personality_v0(ptr)
   declare void @foo()
   define void @rethrow_arg_test() personality ptr @__gxx_wasm_personality_v0 {
     ret void
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
index a1e7616711ab9..a6611899806ea 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
@@ -35,7 +35,7 @@ target triple = "wasm32-unknown-unknown"
 ; CHECK:   local.set  2
 ; CHECK:   local.set  1
 ; CHECK:   local.get  0
-; CHECK:   call  _Unwind_CallPersonality
+; CHECK:   call  __gxx_wasm_personality_v0
 ; CHECK:   block
 ; CHECK:     br_if     0                                 # 0: down to label[[L2:[0-9]+]]
 ; CHECK:     call  __cxa_begin_catch
@@ -111,7 +111,7 @@ try.cont:                                         ; preds = %catch, %catch2, %en
 ; CHECK:         br        2                                      # 2: down to label[[L1:[0-9]+]]
 ; CHECK:       end_try_table
 ; CHECK:     end_block                                            # label[[L0]]:
-; CHECK:     call  _Unwind_CallPersonality
+; CHECK:     call  __gxx_wasm_personality_v0
 ; CHECK:     block
 ; CHECK:       block
 ; CHECK:         br_if     0                                      # 0: down to label[[L2:[0-9]+]]
@@ -124,7 +124,7 @@ try.cont:                                         ; preds = %catch, %catch2, %en
 ; CHECK:                 br        5                              # 5: down to label[[L5:[0-9]+]]
 ; CHECK:               end_try_table
 ; CHECK:             end_block                                    # label[[L4]]:
-; CHECK:             call  _Unwind_CallPersonality
+; CHECK:             call  __gxx_wasm_personality_v0
 ; CHECK:             block
 ; CHECK:               block
 ; CHECK:                 br_if     0                              # 0: down to label[[L6:[0-9]+]]
@@ -1644,7 +1644,7 @@ declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
 ; Function Attrs: nounwind
 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
 
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
index d5b28b279d419..0c0fa9f840601 100644
--- a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
+++ b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
@@ -251,6 +251,6 @@ declare i32 @llvm.wasm.get.ehselector(token) #0
 declare void @__cxa_rethrow()
 declare ptr @__cxa_begin_catch(ptr)
 declare void @__cxa_end_catch()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 
 attributes #0 = { nounwind }
diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
index 573f69df8b859..217da5c39e27f 100644
--- a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
@@ -35,7 +35,7 @@ define void @throw(ptr %p) {
 ; CHECK:     catch     $[[EXN:[0-9]+]]=, __cpp_exception
 ; CHECK:       global.set  __stack_pointer
 ; CHECK:       i32.store __wasm_lpad_context
-; CHECK:       call       $drop=, _Unwind_CallPersonality, $[[EXN]]
+; CHECK:       call       __gxx_wasm_personality_v0, $[[EXN]]
 ; CHECK:       block
 ; CHECK:         br_if     0
 ; CHECK:         call      $drop=, __cxa_begin_catch
@@ -547,7 +547,7 @@ try.cont:                                         ; preds = %entry
 declare void @foo()
 declare void @bar(ptr)
 declare void @take_i32(i32)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: noreturn
 declare void @llvm.wasm.throw(i32, ptr) #1
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.mir b/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
index 95929af4f4cea..7b8ba72ddfad9 100644
--- a/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
+++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
@@ -3,7 +3,7 @@
 --- |
   target triple = "wasm32-unknown-unknown"
 
-  declare i32 @__gxx_wasm_personality_v0(...)
+  declare void @__gxx_wasm_personality_v0(ptr)
   declare void @foo()
   define void @eh_label_test() personality ptr @__gxx_wasm_personality_v0 {
     ret void
diff --git a/llvm/test/CodeGen/WebAssembly/exception.ll b/llvm/test/CodeGen/WebAssembly/exception.ll
index f738216d087ec..a972fceba4715 100644
--- a/llvm/test/CodeGen/WebAssembly/exception.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception.ll
@@ -47,7 +47,7 @@ define void @throw(ptr %p) {
 ; CHECK:   local.get  0
 ; CHECK:   global.set  __stack_pointer
 ; CHECK:   i32.store  __wasm_lpad_context
-; CHECK:   call  _Unwind_CallPersonality
+; CHECK:   call  __gxx_wasm_personality_v0
 ; CHECK:   block
 ; CHECK:     br_if     0
 ; CHECK:     call  __cxa_begin_catch
@@ -648,7 +648,7 @@ try.cont:                                         ; preds = %entry
 declare void @foo()
 declare void @bar(ptr)
 declare void @take_i32(i32)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: noreturn
 declare void @llvm.wasm.throw(i32, ptr) #1
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/function-info.mir b/llvm/test/CodeGen/WebAssembly/function-info.mir
index b81cdffde819f..be87bad9ec72c 100644
--- a/llvm/test/CodeGen/WebAssembly/function-info.mir
+++ b/llvm/test/CodeGen/WebAssembly/function-info.mir
@@ -3,7 +3,7 @@
 --- |
   target triple = "wasm32-unknown-unknown"
 
-  declare i32 @__gxx_wasm_personality_v0(...)
+  declare void @__gxx_wasm_personality_v0(ptr)
   declare void @foo()
   define void @function_property_test() {
     ret void
diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
index 5e8ebcf247000..b4ecb2911c89b 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
@@ -110,7 +110,7 @@ ehcleanup:                                        ; preds = %bb3, %catch.start,
 }
 
 declare i32 @setjmp(ptr)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 declare void @foo()
 declare void @longjmpable()
 declare void @use_i32(i32)
diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
index d5ae37d7e98bc..37e98c6a302d1 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
@@ -299,7 +299,7 @@ declare ptr @_ZN4TempD2Ev(ptr %this) #2
 declare i32 @setjmp(ptr) #0
 ; Function Attrs: noreturn
 declare void @longjmp(ptr, i32) #1
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #2
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
index 35637a996e3ab..8547b68824ee6 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
@@ -30,7 +30,7 @@ try.cont:                                         ; preds = %entry, %catch.start
 }
 
 declare void @foo()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll
index 1a4e888a26ef5..36340c1444525 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll
@@ -6,7 +6,7 @@ target triple = "wasm32-unknown-unknown"
 ; not have a correct Wasm personality function.
 
 define void @test() personality ptr @invalid_personality {
-; CHECK: LLVM ERROR: Function 'test' does not have a correct Wasm personality function '__gxx_wasm_personality_v0'
+; CHECK: LLVM ERROR: Function 'test' does not have a supported Wasm personality function
 entry:
   invoke void @foo()
           to label %try.cont unwind label %catch.dispatch
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
index 164c138cb7578..daa3bb0207b55 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
@@ -46,7 +46,7 @@ catch.start:                                      ; preds = %catch.dispatch
 ; CHECK-NEXT:   store i32 0, ptr @__wasm_lpad_context
 ; CHECK-NEXT:   %[[LSDA:.*]] = call ptr @llvm.wasm.lsda()
 ; CHECK-NEXT:   store ptr %[[LSDA]], ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 1)
-; CHECK-NEXT:   call i32 @_Unwind_CallPersonality(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
+; CHECK-NEXT:   call void @__gxx_wasm_personality_v0(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
 ; CHECK-NEXT:   %[[SELECTOR:.*]] = load i32, ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 2)
 ; CHECK:   icmp eq i32 %[[SELECTOR]]
 
@@ -103,7 +103,7 @@ catch.start:                                      ; preds = %catch.dispatch
 ; CHECK-NOT:   call void @llvm.wasm.landingpad.index
 ; CHECK-NOT:   store {{.*}} @__wasm_lpad_context
 ; CHECK-NOT:   call ptr @llvm.wasm.lsda()
-; CHECK-NOT:   call i32 @_Unwind_CallPersonality
+; CHECK-NOT:   call void @__gxx_wasm_personality_v0
 ; CHECK-NOT:   load {{.*}} @__wasm_lpad_context
 
 try.cont:                                         ; preds = %entry, %catch.start
@@ -257,7 +257,7 @@ merge:                                            ; preds = %bb.true.0, %bb.fals
 declare void @foo()
 declare void @bar(i32)
 declare ptr @_ZN4TempD2Ev(ptr returned)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
@@ -277,4 +277,4 @@ attributes #1 = { noreturn }
 
 ; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32 immarg)
 ; CHECK-DAG: declare ptr @llvm.wasm.lsda()
-; CHECK-DAG: declare i32 @_Unwind_CallPersonality(ptr)
+; CHECK-DAG: declare void @__gxx_wasm_personality_v0(ptr)
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
index ed9b62941a9c7..6cca5af633751 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
@@ -49,7 +49,7 @@ ehcleanup:                                        ; preds = %catch
 }
 
 declare void @foo()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind

>From f6d21590fc0d7ec1fe07daf8b24096d58bf60f3c Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 13 Jul 2026 12:45:05 -0700
Subject: [PATCH 2/2] Update comment per #206660

---
 libcxxabi/src/cxa_personality.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index 970cd0355ddf4..e697950840e99 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1123,7 +1123,7 @@ void __gxx_wasm_personality_v0(void* exception_ptr) {
   __wasm_lpad_context.selector = 0;
 
   // Call personality function. Wasm does not have two-phase unwinding, so we
-  // only do the cleanup phase.
+  // only do the search phase.
   __gxx_personality_imp(
       1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
       (struct _Unwind_Context *)&__wasm_lpad_context);


From libcxx-commits at lists.llvm.org  Mon Jul 13 12:46:18 2026
From: libcxx-commits at lists.llvm.org (Jon Roelofs via libcxx-commits)
Date: Mon, 13 Jul 2026 12:46:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [libunwind][AIX] Handle
 VAPI-based return addresses in stack unwinding for LLU (PR #209280)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209280@github.com>
Message-ID: <6a55408a.c56fb102.7c9cd.33f4@mx.google.com>


================
@@ -1377,13 +1381,15 @@ class UnwindCursor : public AbstractUnwindCursor {
 #ifdef _LIBUNWIND_TRACE_RET_INJECT
   uint32_t _walkedFrames;
 #endif
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+  bool _isKnownVapiNotActive;
+#endif
 };
 
-
 template <typename A, typename R>
 UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
     : _addressSpace(as), _registers(context), _unwindInfoMissing(false),
-      _isSignalFrame(false) {
+      _isSignalFrame(false), _isKnownVapiNotActive(false) {
----------------
jroelofs wrote:

missing a `defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)` guard?

https://github.com/llvm/llvm-project/pull/209280

From libcxx-commits at lists.llvm.org  Mon Jul 13 12:46:18 2026
From: libcxx-commits at lists.llvm.org (Jon Roelofs via libcxx-commits)
Date: Mon, 13 Jul 2026 12:46:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [libunwind][AIX] Handle
 VAPI-based return addresses in stack unwinding for LLU (PR #209280)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209280@github.com>
Message-ID: <6a55408a.c56fb102.7c9cd.340f@mx.google.com>


================
@@ -1469,6 +1475,30 @@ template <typename A, typename R> void UnwindCursor<A, R>::jumpto() {
   static constexpr size_t _EXTRA_LIBUNWIND_FRAMES_WALKED = 5 - 1;
   _registers.returnto(_walkedFrames + _EXTRA_LIBUNWIND_FRAMES_WALKED);
 #else
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+  if (isKnownVapiNotActive()) {
+    // If the current frame is known VAPI not active , execute the VAPI return
----------------
jroelofs wrote:

```suggestion
    // If the current frame is known VAPI not active, execute the VAPI return
```

https://github.com/llvm/llvm-project/pull/209280

From libcxx-commits at lists.llvm.org  Mon Jul 13 12:46:18 2026
From: libcxx-commits at lists.llvm.org (Jon Roelofs via libcxx-commits)
Date: Mon, 13 Jul 2026 12:46:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [libunwind][AIX] Handle
 VAPI-based return addresses in stack unwinding for LLU (PR #209280)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209280@github.com>
Message-ID: <6a55408a.66c0679a.3aa103.905e@mx.google.com>


================
@@ -1393,13 +1399,13 @@ UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
 
 template <typename A, typename R>
 UnwindCursor<A, R>::UnwindCursor(A &as, void *)
-    : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) {
+    : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false),
+      _isKnownVapiNotActive(false) {
----------------
jroelofs wrote:

likewise

https://github.com/llvm/llvm-project/pull/209280

From libcxx-commits at lists.llvm.org  Mon Jul 13 12:46:36 2026
From: libcxx-commits at lists.llvm.org (Demetrius Kanios via libcxx-commits)
Date: Mon, 13 Jul 2026 12:46:36 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxxabi] [libunwind] [llvm]
 [WebAssembly] Call EH personality function instead of
 `Unwind_CallPersonality` (PR #209282)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209282@github.com>
Message-ID: <6a55409c.bc28b987.90005.3d36@mx.google.com>


QuantumSegfault wrote:

cc @hmelder

https://github.com/llvm/llvm-project/pull/209282

From libcxx-commits at lists.llvm.org  Mon Jul 13 12:47:46 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 12:47:46 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxxabi] [libunwind] [llvm]
 [WebAssembly] Call EH personality function instead of
 `Unwind_CallPersonality` (PR #209282)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209282@github.com>
Message-ID: <6a5540e2.1728eaf3.eafd0.a34a@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions cpp,c,h -- libunwind/include/unwind_wasm.h clang/lib/CodeGen/CGException.cpp libcxxabi/src/cxa_personality.cpp libunwind/include/unwind.h libunwind/src/Unwind-wasm.c libunwind/src/config.h llvm/lib/CodeGen/WasmEHPrepare.cpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp
index 25b57a033..4076ab5dc 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -272,8 +272,7 @@ static llvm::FunctionCallee getPersonalityFn(CodeGenModule &CGM,
   } else {
     FTy = llvm::FunctionType::get(CGM.Int32Ty, true);
   }
-  return CGM.CreateRuntimeFunction(FTy,
-                                   Personality.PersonalityFn,
+  return CGM.CreateRuntimeFunction(FTy, Personality.PersonalityFn,
                                    llvm::AttributeList(), /*Local=*/true);
 }
 
diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index 9348c7cfe..92767efa5 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1011,7 +1011,7 @@ static inline void get_landing_pad(__cxa_catch_temp_type &dest,
 #endif
 }
 
-#if (defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)) || defined(__WASM_EXCEPTIONS__)
+#  if (defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)) || defined(__WASM_EXCEPTIONS__)
 static _Unwind_Reason_Code __gxx_personality_imp
 #else
 _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code
@@ -1112,23 +1112,19 @@ __gxx_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame,
 }
 #endif
 
-
-#ifdef __WASM_EXCEPTIONS__
-extern "C" _LIBCXXABI_FUNC_VIS
-void __gxx_wasm_personality_v0(void* exception_ptr) {
-  struct _Unwind_Exception *exception_object =
-      (struct _Unwind_Exception *)exception_ptr;
+#  ifdef __WASM_EXCEPTIONS__
+extern "C" _LIBCXXABI_FUNC_VIS void __gxx_wasm_personality_v0(void* exception_ptr) {
+  struct _Unwind_Exception* exception_object = (struct _Unwind_Exception*)exception_ptr;
 
   // Reset the selector.
   __wasm_lpad_context.selector = 0;
 
   // Call personality function. Wasm does not have two-phase unwinding, so we
   // only do the search phase.
-  __gxx_personality_imp(
-      1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
-      (struct _Unwind_Context *)&__wasm_lpad_context);
+  __gxx_personality_imp(1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
+                        (struct _Unwind_Context*)&__wasm_lpad_context);
 }
-#endif
+#  endif
 
 #else
 
diff --git a/libunwind/src/config.h b/libunwind/src/config.h
index fd1aba25e..73162995f 100644
--- a/libunwind/src/config.h
+++ b/libunwind/src/config.h
@@ -75,13 +75,14 @@
   #define _LIBUNWIND_EXPORT
   #define _LIBUNWIND_HIDDEN
 #else
-  #if !defined(__ELF__) && !defined(__MACH__) && !defined(_AIX) && !defined(__wasm__)
-    #define _LIBUNWIND_EXPORT __declspec(dllexport)
-    #define _LIBUNWIND_HIDDEN
-  #else
-    #define _LIBUNWIND_EXPORT __attribute__((visibility("default")))
-    #define _LIBUNWIND_HIDDEN __attribute__((visibility("hidden")))
-  #endif
+#if !defined(__ELF__) && !defined(__MACH__) && !defined(_AIX) &&               \
+    !defined(__wasm__)
+#define _LIBUNWIND_EXPORT __declspec(dllexport)
+#define _LIBUNWIND_HIDDEN
+#else
+#define _LIBUNWIND_EXPORT __attribute__((visibility("default")))
+#define _LIBUNWIND_HIDDEN __attribute__((visibility("hidden")))
+#endif
 #endif
 
 #define STR(a) #a
diff --git a/llvm/lib/CodeGen/WasmEHPrepare.cpp b/llvm/lib/CodeGen/WasmEHPrepare.cpp
index 363e7221f..ab562edff 100644
--- a/llvm/lib/CodeGen/WasmEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WasmEHPrepare.cpp
@@ -259,8 +259,8 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
   // instruction selection.
   CatchF = Intrinsic::getOrInsertDeclaration(&M, Intrinsic::wasm_catch);
 
-  auto *PersPrototype = FunctionType::get(
-      IRB.getInt32Ty(), {IRB.getPtrTy()}, false);
+  auto *PersPrototype =
+      FunctionType::get(IRB.getInt32Ty(), {IRB.getPtrTy()}, false);
   PersonalityF =
       M.getOrInsertFunction(getEHPersonalityName(Personality), PersPrototype);
 
@@ -354,11 +354,10 @@ void WasmEHPrepareImpl::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
 
   // Grab direct function when possible to use `call` instead of `call_indirect`
   if (Function *F = dyn_cast<Function>(PersonalityF.getCallee()))
-    PersCI = IRB.CreateCall(F, CatchCI,
-                            OperandBundleDef("funclet", CPI));
+    PersCI = IRB.CreateCall(F, CatchCI, OperandBundleDef("funclet", CPI));
   else
-    PersCI = IRB.CreateCall(PersonalityF, CatchCI,
-                            OperandBundleDef("funclet", CPI));
+    PersCI =
+        IRB.CreateCall(PersonalityF, CatchCI, OperandBundleDef("funclet", CPI));
 
   PersCI->setDoesNotThrow();
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/209282

From libcxx-commits at lists.llvm.org  Mon Jul 13 12:48:04 2026
From: libcxx-commits at lists.llvm.org (Jon Roelofs via libcxx-commits)
Date: Mon, 13 Jul 2026 12:48:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [libunwind][AIX] Handle
 VAPI-based return addresses in stack unwinding for LLU (PR #209280)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209280@github.com>
Message-ID: <6a5540f4.b1416da1.78d1e.e1b6@mx.google.com>


================
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test unwinding through a frame whose return address points to the VAPI
+// glue for the Live Library Update feature.
+
+// REQUIRES: target={{.+}}-aix{{.*}}
+
+#include <stdlib.h>
+#include <stdio.h>
+
+// VAPI glue addresses
+constexpr uintptr_t vapi_glue_addr_ext_32 = 0x8B80;
+constexpr uintptr_t vapi_addr_64 = 0x8e00;
+constexpr uintptr_t vapi_size_64 = 0x0200;
+constexpr uintptr_t vapi_glue_addr_begin = vapi_glue_addr_ext_32;     // Start address in 32-bit
+constexpr uintptr_t vapi_glue_addr_end = vapi_addr_64 + vapi_size_64; // End address in 64-bit
+
+struct FunctionDescriptor {
+  uintptr_t entry;
+  uintptr_t toc;
+  uintptr_t env;
+};
+
+struct Tracker {
+  const char* name;
+  Tracker(const char* n) : name(n) { printf("Construct %s\n", name); }
+  ~Tracker() { printf("Destruct %s\n", name); }
+};
+
+int comp(const void*, const void*) {
+  printf("comp()\n");
+  throw 42;
+}
+
+static void do_search() {
+  Tracker t("do_search-local");
+  char buf[1];
+  char key = 0;
+  printf("calling bsearch()\n");
+  // bsearch uses VAPI and sets the return address to the VAPI glue when LLU
+  // is on.
+  bsearch(&key, buf, 1, 1, comp);
+  printf("after bsearch\n");
+}
+
+int main() {
+  FunctionDescriptor* fd = reinterpret_cast<FunctionDescriptor*>(bsearch);
+
+  if (vapi_glue_addr_begin <= fd->entry && fd->entry < vapi_glue_addr_end) {
+    printf("!!! Live Library Update is enabled !!!\n");
----------------
jroelofs wrote:

These logs should be `fprintf(stderr`, otherwise the buffered stdout output may get swallowed by the python test harness if the test crashes.

https://github.com/llvm/llvm-project/pull/209280

From libcxx-commits at lists.llvm.org  Mon Jul 13 12:49:08 2026
From: libcxx-commits at lists.llvm.org (Jon Roelofs via libcxx-commits)
Date: Mon, 13 Jul 2026 12:49:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [libunwind][AIX] Handle
 VAPI-based return addresses in stack unwinding for LLU (PR #209280)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209280@github.com>
Message-ID: <6a554134.92ea0e2a.13fd37.ce1a@mx.google.com>


================
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
----------------
jroelofs wrote:

Can you make this an unwinder test instead of a libcxxabi test?

https://github.com/llvm/llvm-project/pull/209280

From libcxx-commits at lists.llvm.org  Mon Jul 13 12:49:27 2026
From: libcxx-commits at lists.llvm.org (Xing Xue via libcxx-commits)
Date: Mon, 13 Jul 2026 12:49:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [libunwind][AIX] Handle
 VAPI-based return addresses in stack unwinding for LLU (PR #209280)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209280@github.com>
Message-ID: <6a554147.6c5720b3.15e90c.0a7a@mx.google.com>


https://github.com/xingxue-ibm updated https://github.com/llvm/llvm-project/pull/209280

>From 6b69974ca040d85a14a898c242f60c425b190c49 Mon Sep 17 00:00:00 2001
From: Xing Xue <xingxue at outlook.com>
Date: Mon, 13 Jul 2026 15:04:04 -0400
Subject: [PATCH 1/2] Update the unwinder to accommodate AIX changes required
 for LLU (Live Library Update).

---
 libcxxabi/test/aix_unwind_vapi.pass.cpp | 66 +++++++++++++++++
 libunwind/src/UnwindCursor.hpp          | 98 ++++++++++++++++++++++++-
 2 files changed, 160 insertions(+), 4 deletions(-)
 create mode 100644 libcxxabi/test/aix_unwind_vapi.pass.cpp

diff --git a/libcxxabi/test/aix_unwind_vapi.pass.cpp b/libcxxabi/test/aix_unwind_vapi.pass.cpp
new file mode 100644
index 0000000000000..bafb02f366ab4
--- /dev/null
+++ b/libcxxabi/test/aix_unwind_vapi.pass.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test unwinding through a frame whose return address points to the VAPI
+// glue for the Live Library Update feature.
+
+// REQUIRES: target={{.+}}-aix{{.*}}
+
+#include <stdlib.h>
+#include <stdio.h>
+
+// VAPI glue addresses
+constexpr uintptr_t vapi_glue_addr_ext_32 = 0x8B80;
+constexpr uintptr_t vapi_addr_64 = 0x8e00;
+constexpr uintptr_t vapi_size_64 = 0x0200;
+constexpr uintptr_t vapi_glue_addr_begin = vapi_glue_addr_ext_32;     // Start address in 32-bit
+constexpr uintptr_t vapi_glue_addr_end = vapi_addr_64 + vapi_size_64; // End address in 64-bit
+
+struct FunctionDescriptor {
+  uintptr_t entry;
+  uintptr_t toc;
+  uintptr_t env;
+};
+
+struct Tracker {
+  const char* name;
+  Tracker(const char* n) : name(n) { printf("Construct %s\n", name); }
+  ~Tracker() { printf("Destruct %s\n", name); }
+};
+
+int comp(const void*, const void*) {
+  printf("comp()\n");
+  throw 42;
+}
+
+static void do_search() {
+  Tracker t("do_search-local");
+  char buf[1];
+  char key = 0;
+  printf("calling bsearch()\n");
+  // bsearch uses VAPI and sets the return address to the VAPI glue when LLU
+  // is on.
+  bsearch(&key, buf, 1, 1, comp);
+  printf("after bsearch\n");
+}
+
+int main() {
+  FunctionDescriptor* fd = reinterpret_cast<FunctionDescriptor*>(bsearch);
+
+  if (vapi_glue_addr_begin <= fd->entry && fd->entry < vapi_glue_addr_end) {
+    printf("!!! Live Library Update is enabled !!!\n");
+  } else {
+    printf("!!! Live Library Update is NOT enabled !!!\n");
+  }
+
+  try {
+    do_search();
+  } catch (...) {
+    printf("caught ...\n");
+  }
+}
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index ff92c9e0a844a..f59384af81b5e 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -1363,6 +1363,10 @@ class UnwindCursor : public AbstractUnwindCursor {
                            reinterpret_cast<tbtable *>(_info.unwind_info),
                            _registers, _isSignalFrame);
   }
+  bool isKnownVapiNotActive() const { return _isKnownVapiNotActive; }
+  void setIsKnownVapiNotActive(bool val) { _isKnownVapiNotActive = val; }
+  static pint_t getVAPILR();
+
 #endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
 
   A               &_addressSpace;
@@ -1377,13 +1381,15 @@ class UnwindCursor : public AbstractUnwindCursor {
 #ifdef _LIBUNWIND_TRACE_RET_INJECT
   uint32_t _walkedFrames;
 #endif
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+  bool _isKnownVapiNotActive;
+#endif
 };
 
-
 template <typename A, typename R>
 UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
     : _addressSpace(as), _registers(context), _unwindInfoMissing(false),
-      _isSignalFrame(false) {
+      _isSignalFrame(false), _isKnownVapiNotActive(false) {
   static_assert((check_fit<UnwindCursor<A, R>, unw_cursor_t>::does_fit),
                 "UnwindCursor<> does not fit in unw_cursor_t");
   static_assert((alignof(UnwindCursor<A, R>) <= alignof(unw_cursor_t)),
@@ -1393,13 +1399,13 @@ UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
 
 template <typename A, typename R>
 UnwindCursor<A, R>::UnwindCursor(A &as, void *)
-    : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) {
+    : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false),
+      _isKnownVapiNotActive(false) {
   memset(static_cast<void *>(&_info), 0, sizeof(_info));
   // FIXME
   // fill in _registers from thread arg
 }
 
-
 template <typename A, typename R>
 bool UnwindCursor<A, R>::validReg(int regNum) {
   return _registers.validRegister(regNum);
@@ -1469,6 +1475,30 @@ template <typename A, typename R> void UnwindCursor<A, R>::jumpto() {
   static constexpr size_t _EXTRA_LIBUNWIND_FRAMES_WALKED = 5 - 1;
   _registers.returnto(_walkedFrames + _EXTRA_LIBUNWIND_FRAMES_WALKED);
 #else
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+  if (isKnownVapiNotActive()) {
+    // If the current frame is known VAPI not active , execute the VAPI return
+    // glue to clear the VAPI control block. The VAPI return glue is used by
+    // AIX longjmp based on the VAPI active status recorded by setjmp in the
+    // jmp_buf, which means that the VAPI return glue can be called solely on
+    // the basis of the VAPI active status of the target context.
+
+    // VAPI return glue address is the VAPI glue address - 4.
+#ifdef __64BIT__
+    constexpr pint_t VAPIReturnGlue = 0x8e40 - 4;
+#else
+    constexpr pint_t VAPIReturnGlue = 0x8c40 - 4;
+#endif
+
+    _LIBUNWIND_TRACE_UNWINDING("VAPI: executing return glue %p\n",
+                               reinterpret_cast<void *>(VAPIReturnGlue));
+    register auto *registers __asm__("r30") = &_registers;
+    __asm__ __volatile__("bla %[retglue]"
+                         : "+r"(registers)
+                         : [retglue] "i"(VAPIReturnGlue));
+    registers->jumpto();
+  }
+#endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
   _registers.jumpto();
 #endif
 }
@@ -2459,6 +2489,45 @@ bool UnwindCursor<A, R>::getInfoFromTBTable(pint_t pc, R &registers) {
   return true;
 }
 
+// VAPI glue addresses
+constexpr uintptr_t vapi_glue_addr_ext_32 = 0x8B80;
+constexpr uintptr_t vapi_addr_64 = 0x8e00;
+constexpr uintptr_t vapi_size_64 = 0x0200;
+constexpr uintptr_t vapi_glue_addr_begin =
+    vapi_glue_addr_ext_32; // Start address in 32-bit
+constexpr uintptr_t vapi_glue_addr_end =
+    vapi_addr_64 + vapi_size_64; // End address in 64-bit
+
+#ifdef __64BIT__
+constexpr uintptr_t VAPI_CB_SIZE = 256;
+constexpr uintptr_t TLS_POINTER_OFFSET = 30 * 1024;
+constexpr uintptr_t TLSCB_BASE_SIZE = 256;
+
+static __inline__ __attribute__((__always_inline__)) char *tptr(void) {
+  char *result;
+  __asm__("mr %0, 13" : "=r"(result));
+  return result;
+}
+#else // 32-bit
+constexpr uintptr_t VAPI_CB_SIZE = 128;
+constexpr uintptr_t TLS_POINTER_OFFSET = 31 * 1024;
+constexpr uintptr_t TLSCB_BASE_SIZE = 128;
+
+static __inline__ __attribute__((__always_inline__)) char *tptr(void) {
+  char *result;
+  __asm__("mfspr %0, 259" : "=r"(result));
+  return result;
+}
+#endif
+
+constexpr int VAPI_CB_OFFSET =
+    TLS_POINTER_OFFSET + VAPI_CB_SIZE + TLSCB_BASE_SIZE;
+
+template <typename A, typename R>
+typename UnwindCursor<A, R>::pint_t UnwindCursor<A, R>::getVAPILR() {
+  return *reinterpret_cast<pint_t *>(tptr() - VAPI_CB_OFFSET + 8);
+}
+
 // Step back up the stack following the frame back link.
 template <typename A, typename R>
 int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
@@ -2579,6 +2648,17 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
     } else {
       // Otherwise, use the LR value in the stack link area.
       returnAddress = reinterpret_cast<pint_t *>(lastStack)[2];
+
+      if (vapi_glue_addr_begin <= returnAddress &&
+          returnAddress < vapi_glue_addr_end) {
+        _LIBUNWIND_TRACE_UNWINDING(
+            "The return address=%p is within the range of VAPI address, ",
+            reinterpret_cast<void *>(returnAddress));
+        setIsKnownVapiNotActive(true);
+        returnAddress = getVAPILR();
+        _LIBUNWIND_TRACE_UNWINDING("return address=%p from VAPI\n",
+                                   reinterpret_cast<void *>(returnAddress));
+      }
     }
 
     // Reset LR in the current context.
@@ -2709,6 +2789,16 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
   // Return address is the address after call site instruction.
   pint_t nextReturnAddress = reinterpret_cast<pint_t *>(nextStack)[2];
 
+  if (vapi_glue_addr_begin <= nextReturnAddress &&
+      nextReturnAddress < vapi_glue_addr_end) {
+    _LIBUNWIND_TRACE_UNWINDING(
+        "The next return address=%p is within the range of VAPI address, ",
+        reinterpret_cast<void *>(nextReturnAddress));
+    nextReturnAddress = getVAPILR();
+    _LIBUNWIND_TRACE_UNWINDING("the next return address=%p from VAPI\n",
+                               reinterpret_cast<void *>(nextReturnAddress));
+  }
+
   if (nextReturnAddress > 0x01 && nextReturnAddress < 0x10000) {
     _LIBUNWIND_TRACE_UNWINDING("The next is a signal handler frame: "
                                "nextStack=%p, next return address=%p\n",

>From 578ab2193990e1954f302654d3f17fa40e639f0e Mon Sep 17 00:00:00 2001
From: Xing Xue <xingxue at outlook.com>
Date: Mon, 13 Jul 2026 15:47:23 -0400
Subject: [PATCH 2/2] Guard _isKnownVapiNotActive(false) initializers with #if
 defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND).

---
 libunwind/src/UnwindCursor.hpp | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index f59384af81b5e..7b400964defc8 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -1389,7 +1389,11 @@ class UnwindCursor : public AbstractUnwindCursor {
 template <typename A, typename R>
 UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
     : _addressSpace(as), _registers(context), _unwindInfoMissing(false),
-      _isSignalFrame(false), _isKnownVapiNotActive(false) {
+      _isSignalFrame(false)
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+      , _isKnownVapiNotActive(false)
+#endif
+      {
   static_assert((check_fit<UnwindCursor<A, R>, unw_cursor_t>::does_fit),
                 "UnwindCursor<> does not fit in unw_cursor_t");
   static_assert((alignof(UnwindCursor<A, R>) <= alignof(unw_cursor_t)),
@@ -1399,8 +1403,11 @@ UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
 
 template <typename A, typename R>
 UnwindCursor<A, R>::UnwindCursor(A &as, void *)
-    : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false),
-      _isKnownVapiNotActive(false) {
+    : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false)
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+      , _isKnownVapiNotActive(false)
+#endif
+      {
   memset(static_cast<void *>(&_info), 0, sizeof(_info));
   // FIXME
   // fill in _registers from thread arg


From libcxx-commits at lists.llvm.org  Mon Jul 13 12:51:16 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 12:51:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [libunwind][AIX] Handle
 VAPI-based return addresses in stack unwinding for LLU (PR #209280)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209280@github.com>
Message-ID: <6a5541b4.3cce8983.225732.af29@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions cpp,hpp -- libcxxabi/test/aix_unwind_vapi.pass.cpp libunwind/src/UnwindCursor.hpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 7b400964d..9f9a22b0f 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -1391,9 +1391,10 @@ UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
     : _addressSpace(as), _registers(context), _unwindInfoMissing(false),
       _isSignalFrame(false)
 #if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
-      , _isKnownVapiNotActive(false)
+      ,
+      _isKnownVapiNotActive(false)
 #endif
-      {
+{
   static_assert((check_fit<UnwindCursor<A, R>, unw_cursor_t>::does_fit),
                 "UnwindCursor<> does not fit in unw_cursor_t");
   static_assert((alignof(UnwindCursor<A, R>) <= alignof(unw_cursor_t)),
@@ -1405,9 +1406,10 @@ template <typename A, typename R>
 UnwindCursor<A, R>::UnwindCursor(A &as, void *)
     : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false)
 #if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
-      , _isKnownVapiNotActive(false)
+      ,
+      _isKnownVapiNotActive(false)
 #endif
-      {
+{
   memset(static_cast<void *>(&_info), 0, sizeof(_info));
   // FIXME
   // fill in _registers from thread arg

``````````

</details>


https://github.com/llvm/llvm-project/pull/209280

From libcxx-commits at lists.llvm.org  Mon Jul 13 13:47:59 2026
From: libcxx-commits at lists.llvm.org (Jon Roelofs via libcxx-commits)
Date: Mon, 13 Jul 2026 13:47:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][test] Fingerprint the compiler
 when memoizing results (PR #208311)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208311@github.com>
Message-ID: <6a554eff.b4a331f0.271e68.a376@mx.google.com>


jroelofs wrote:

ping

https://github.com/llvm/llvm-project/pull/208311

From libcxx-commits at lists.llvm.org  Mon Jul 13 13:48:53 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 13 Jul 2026 13:48:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 6bbd5a1 - [libcxx] Avoid including
 deprecated ClangTidyModuleRegistry.h (#208945)
Message-ID: <6a554f35.c56fb102.7c9cd.4e3f@mx.google.com>


Author: Aiden Grossman
Date: 2026-07-13T13:48:48-07:00
New Revision: 6bbd5a18f672a4e6cd19e2e2763d6877dd5f3832

URL: https://github.com/llvm/llvm-project/commit/6bbd5a18f672a4e6cd19e2e2763d6877dd5f3832
DIFF: https://github.com/llvm/llvm-project/commit/6bbd5a18f672a4e6cd19e2e2763d6877dd5f3832.diff

LOG: [libcxx] Avoid including deprecated ClangTidyModuleRegistry.h (#208945)

Deprecated in 1bcf74006bcf528d14377173dba459759d35c961. Causes warnings
during compilation and will presumably become an error.

Added: 
    

Modified: 
    libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
    libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
    libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
    libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
    libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
    libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
    libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
    libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
index f1866179a3b68..0d03574af68e7 100644
--- a/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/abi_tag_on_virtual.cpp
@@ -7,7 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
+#if CLANG_VERSION_MAJOR > 23
+#  include "clang-tidy/ClangTidyModule.h"
+#else
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "abi_tag_on_virtual.hpp"
 

diff  --git a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
index e176b29670ce9..d31c1da44b527 100644
--- a/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/empty_namespaces.cpp
@@ -7,7 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
+#if CLANG_VERSION_MAJOR > 23
+#  include "clang-tidy/ClangTidyModule.h"
+#else
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "empty_namespaces.hpp"
 

diff  --git a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
index 8ec029f7dba2d..5273b1d7564ce 100644
--- a/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/header_exportable_declarations.cpp
@@ -7,7 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
+#if CLANG_VERSION_MAJOR > 23
+#  include "clang-tidy/ClangTidyModule.h"
+#else
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "llvm/ADT/ArrayRef.h"
 

diff  --git a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
index f05470e47d3e7..878290fde1271 100644
--- a/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/hide_from_abi.cpp
@@ -7,7 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
+#if CLANG_VERSION_MAJOR > 23
+#  include "clang-tidy/ClangTidyModule.h"
+#else
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "hide_from_abi.hpp"
 

diff  --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
index b6298eefdeb44..31415db2955b2 100644
--- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp
@@ -7,7 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyModule.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
+#if CLANG_VERSION_MAJOR < 23
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "abi_tag_on_virtual.hpp"
 #include "empty_namespaces.hpp"

diff  --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..72852625b12e9 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -7,7 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
+#if CLANG_VERSION_MAJOR > 23
+#  include "clang-tidy/ClangTidyModule.h"
+#else
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "robust_against_adl.hpp"
 

diff  --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index a608954aa2cc7..777dd6fe11df5 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -7,7 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
+#if CLANG_VERSION_MAJOR > 23
+#  include "clang-tidy/ClangTidyModule.h"
+#else
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/FixIt.h"
 

diff  --git a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
index 24bacde6304bc..803e877a68687 100644
--- a/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/uglify_attributes.cpp
@@ -7,7 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-tidy/ClangTidyCheck.h"
-#include "clang-tidy/ClangTidyModuleRegistry.h"
+// TODO(LLVM 25): Remove this compatibility check when LLVM 25 branches.
+#if CLANG_VERSION_MAJOR > 23
+#  include "clang-tidy/ClangTidyModule.h"
+#else
+#  include "clang-tidy/ClangTidyModuleRegistry.h"
+#endif
 
 #include "uglify_attributes.hpp"
 #include "utilities.hpp"


        

From libcxx-commits at lists.llvm.org  Mon Jul 13 13:48:57 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Mon, 13 Jul 2026 13:48:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid including deprecated
 ClangTidyModuleRegistry.h (PR #208945)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208945@github.com>
Message-ID: <6a554f39.743400b0.2067c9.de32@mx.google.com>


https://github.com/boomanaiden154 closed https://github.com/llvm/llvm-project/pull/208945

From libcxx-commits at lists.llvm.org  Mon Jul 13 14:46:30 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Mon, 13 Jul 2026 14:46:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::mismatch() based on __pstl::__parallel_find() (PR #209291)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209291@github.com>
Message-ID: <6a555cb6.d1496011.26913c.67da@mx.google.com>


https://github.com/mikekazakov edited https://github.com/llvm/llvm-project/pull/209291

From libcxx-commits at lists.llvm.org  Mon Jul 13 14:50:59 2026
From: libcxx-commits at lists.llvm.org (Hubert Tong via libcxx-commits)
Date: Mon, 13 Jul 2026 14:50:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [libunwind][AIX] Handle
 VAPI-based return addresses in stack unwinding for LLU (PR #209280)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209280@github.com>
Message-ID: <6a555dc3.f06f4994.1fc857.71f4@mx.google.com>


hubert-reinterpretcast wrote:

> AIX's implementation of LLU (Live Library Update) stores the VAPI glue address in the return-address field of a stack frame instead of the link register (LR) value when the callee function uses VAPI. In this case, the LR value is saved in the VAPI control block.

Suggestion:
In AIX's implementation of LLU (Live Library Update), the caller of a Virtual API (VAPI) interface instead calls VAPI glue and the implementation for a VAPI function is entered from the VAPI glue. If a VAPI is not already active on the thread, the VAPI calls the implementation of the VAPI function with the link register (LR) value set to a return address in the VAPI glue. In this case, the LR (return address) value on entry to the VAPI glue is saved in the VAPI control block.

https://github.com/llvm/llvm-project/pull/209280

From libcxx-commits at lists.llvm.org  Mon Jul 13 16:48:08 2026
From: libcxx-commits at lists.llvm.org (Hubert Tong via libcxx-commits)
Date: Mon, 13 Jul 2026 16:48:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [libunwind][AIX] Handle
 VAPI-based return addresses in stack unwinding for LLU (PR #209280)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209280@github.com>
Message-ID: <6a557938.91b0c015.1f6359.df4f@mx.google.com>


https://github.com/hubert-reinterpretcast approved this pull request.

Functional changes LGTM but I think we want to figure out the testing.

https://github.com/llvm/llvm-project/pull/209280

From libcxx-commits at lists.llvm.org  Tue Jul 14 00:03:43 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 14 Jul 2026 00:03:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3052R2:
	`view_interface::at()` (PR #205012)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205012@github.com>
Message-ID: <6a55df4f.743400b0.2067c9.4b33@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/205012

>From b868e5e71f1cca8374170bcc4cf60789344edaab Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sun, 21 Jun 2026 23:52:40 +0300
Subject: [PATCH] [libc++][ranges] P3052R2: `view_interface::at()`

Closes #204395

Implements https://wg21.link/P3052R2
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/Status/Cxx29Papers.csv            |   2 +-
 libcxx/include/__ranges/view_interface.h      |  26 +++
 libcxx/include/version                        |   2 +
 .../view.interface/nodiscard.verify.cpp       |   7 +
 .../test/libcxx/transitive_includes/cxx23.csv |   1 +
 .../test/libcxx/transitive_includes/cxx26.csv |   1 +
 .../ranges.version.compile.pass.cpp           |  33 ++++
 .../version.version.compile.pass.cpp          |  33 ++++
 .../view.interface/view.interface.pass.cpp    | 149 ++++++++++++------
 .../generate_feature_test_macro_components.py |   7 +
 11 files changed, 214 insertions(+), 49 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..82d0910a7d7e1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -308,6 +308,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_variant``                                      ``202106L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_view_interface``                               ``202606L``
+    ---------------------------------------------------------- -----------------
     **C++23**
     ----------------------------------------------------------------------------
     ``__cpp_lib_adaptor_iterator_pair_constructor``            ``202106L``
diff --git a/libcxx/docs/Status/Cxx29Papers.csv b/libcxx/docs/Status/Cxx29Papers.csv
index 3c48fe89be33d..656eaf5df44df 100644
--- a/libcxx/docs/Status/Cxx29Papers.csv
+++ b/libcxx/docs/Status/Cxx29Papers.csv
@@ -4,7 +4,7 @@
 "`P2414R12 <https://wg21.link/P2414R12>`__","Pointer lifetime-end zap proposed solutions","2026-06 (Brno)","","","`#204392 <https://github.com/llvm/llvm-project/issues/204392>`__","Voted as a Defect Report."
 "`P3319R6 <https://wg21.link/P3319R6>`__","Add an ``iota`` object for ``simd`` (and more)","2026-06 (Brno)","","","`#204393 <https://github.com/llvm/llvm-project/issues/204393>`__",""
 "`P3798R1 <https://wg21.link/P3798R1>`__","The unexpected in ``std::expected``","2026-06 (Brno)","","","`#204394 <https://github.com/llvm/llvm-project/issues/204394>`__",""
-"`P3052R2 <https://wg21.link/P3052R2>`__","``view_interface::at()``","2026-06 (Brno)","","","`#204395 <https://github.com/llvm/llvm-project/issues/204395>`__",""
+"`P3052R2 <https://wg21.link/P3052R2>`__","``view_interface::at()``","2026-06 (Brno)","|Complete|","23","`#204395 <https://github.com/llvm/llvm-project/issues/204395>`__",""
 "`P4206R0 <https://wg21.link/P4206R0>`__","Revert string support in ``std::constant_wrapper``","2026-06 (Brno)","|Complete|","23","`#203336 <https://github.com/llvm/llvm-project/issues/203336>`__","Applied as a Defect Report."
 "`P3395R6 <https://wg21.link/P3395R6>`__","Fix encoding issues and add a formatter for ``std::error_code``","2026-06 (Brno)","","","`#204396 <https://github.com/llvm/llvm-project/issues/204396>`__",""
 "`P3505R4 <https://wg21.link/P3505R4>`__","Fix the default floating-point representation in ``std::format``","2026-06 (Brno)","","","`#204397 <https://github.com/llvm/llvm-project/issues/204397>`__","To be applied as a Defect Report."
diff --git a/libcxx/include/__ranges/view_interface.h b/libcxx/include/__ranges/view_interface.h
index 96508c2be7277..c5875d024840e 100644
--- a/libcxx/include/__ranges/view_interface.h
+++ b/libcxx/include/__ranges/view_interface.h
@@ -15,6 +15,7 @@
 #include <__concepts/same_as.h>
 #include <__config>
 #include <__iterator/concepts.h>
+#include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/prev.h>
 #include <__memory/pointer_traits.h>
@@ -25,6 +26,7 @@
 #include <__type_traits/is_class.h>
 #include <__type_traits/make_unsigned.h>
 #include <__type_traits/remove_cv.h>
+#include <stdexcept>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -159,8 +161,32 @@ class view_interface {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](range_difference_t<_RARange> __index) const {
     return ranges::begin(__derived())[__index];
   }
+
+  #if _LIBCPP_STD_VER >= 29
+
+  template <random_access_range _RARange = _Derived>
+    requires sized_range<_RARange> // freestanding-deleted
+  [[nodiscard]] constexpr decltype(auto) at(range_difference_t<_RARange> __index) {
+    if (__index < 0 || __index >= ranges::distance(__derived())) {
+      std::__throw_out_of_range(
+          "Precondition `0 <= __index < distance()` not satisfied. `.at(__index)` called with out-of-bounds index.");
+    }
+    return (*this)[__index];
+  }
+
+  template <random_access_range _RARange = const _Derived>
+    requires sized_range<_RARange> // freestanding-deleted
+  [[nodiscard]] constexpr decltype(auto) at(range_difference_t<_RARange> __index) const {
+    if (__index < 0 || __index >= ranges::distance(__derived())) {
+      std::__throw_out_of_range(
+          "Precondition `0 <= __index < distance()` not satisfied. `.at(__index)` called with out-of-bounds index.");
+    }
+    return (*this)[__index];
+  }
 };
 
+#endif // _LIBCPP_STD_VER >= 29
+
 } // namespace ranges
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..94185b7602f64 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -290,6 +290,7 @@ __cpp_lib_unwrap_ref                                    201811L <functional>
 __cpp_lib_variant                                       202306L <variant>
                                                         202106L // C++20
                                                         202102L // C++17
+__cpp_lib_view_interface                                202606L <ranges>
 __cpp_lib_void_t                                        201411L <type_traits>
 
 */
@@ -488,6 +489,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_unwrap_ref                           201811L
 # undef  __cpp_lib_variant
 # define __cpp_lib_variant                              202106L
+# define __cpp_lib_view_interface                       202606L
 #endif
 
 #if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
index a22c03f5d1a71..2bccc170aa727 100644
--- a/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
@@ -67,4 +67,11 @@ void test() {
   v[Diff{0}];
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v)[Diff{0}];
+
+#if TEST_STD_VER >= 29
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.at(Diff{0});
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).at(Diff{0});
+#endif
 }
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 53ae6c9b4e591..260c410680e39 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -11,6 +11,7 @@ algorithm iosfwd
 algorithm limits
 algorithm optional
 algorithm ratio
+algorithm stdexcept
 algorithm tuple
 algorithm version
 any cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index f712f36caa9fc..6cc4737bff91b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -11,6 +11,7 @@ algorithm iosfwd
 algorithm limits
 algorithm optional
 algorithm ratio
+algorithm stdexcept
 algorithm tuple
 algorithm version
 any cstdint
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 670b0e664721a..f42c7ede894b5 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -80,6 +80,10 @@
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #elif TEST_STD_VER == 14
 
 #  ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -142,6 +146,10 @@
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #elif TEST_STD_VER == 17
 
 #  ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -204,6 +212,10 @@
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #elif TEST_STD_VER == 20
 
 #  ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -269,6 +281,13 @@
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++20"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++20"
+#  endif
+
 #elif TEST_STD_VER == 23
 
 #  ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -385,6 +404,13 @@
 #    error "__cpp_lib_ranges_zip should have the value 202110L in c++23"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++23"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++23"
+#  endif
+
 #elif TEST_STD_VER > 23
 
 #  if !defined(_LIBCPP_VERSION)
@@ -516,6 +542,13 @@
 #    error "__cpp_lib_ranges_zip should have the value 202110L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++26"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++26"
+#  endif
+
 #endif // TEST_STD_VER > 23
 
 // clang-format on
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 f2d74cc99d864..79e61725a93fc 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
@@ -948,6 +948,10 @@
 #    error "__cpp_lib_variant should not be defined before c++17"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #  ifdef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should not be defined before c++17"
 #  endif
@@ -1954,6 +1958,10 @@
 #    error "__cpp_lib_variant should not be defined before c++17"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #  ifdef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should not be defined before c++17"
 #  endif
@@ -3149,6 +3157,10 @@
 #    error "__cpp_lib_variant should have the value 202102L in c++17"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #  ifndef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should be defined in c++17"
 #  endif
@@ -4611,6 +4623,13 @@
 #    error "__cpp_lib_variant should have the value 202106L in c++20"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++20"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++20"
+#  endif
+
 #  ifndef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should be defined in c++20"
 #  endif
@@ -6301,6 +6320,13 @@
 #    error "__cpp_lib_variant should have the value 202106L in c++23"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++23"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++23"
+#  endif
+
 #  ifndef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should be defined in c++23"
 #  endif
@@ -8324,6 +8350,13 @@
 #    error "__cpp_lib_variant should have the value 202306L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++26"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
index bce13c38f2dab..047755b0dbb9e 100644
--- a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
+++ b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
@@ -21,17 +21,17 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
-template<class T>
+template <class T>
 concept ValidViewInterfaceType = requires { typename std::ranges::view_interface<T>; };
 
-struct Empty { };
+struct Empty {};
 
 static_assert(!ValidViewInterfaceType<void>);
 static_assert(!ValidViewInterfaceType<void*>);
 static_assert(!ValidViewInterfaceType<Empty*>);
 static_assert(!ValidViewInterfaceType<Empty const>);
-static_assert(!ValidViewInterfaceType<Empty &>);
-static_assert( ValidViewInterfaceType<Empty>);
+static_assert(!ValidViewInterfaceType<Empty&>);
+static_assert(ValidViewInterfaceType<Empty>);
 
 using InputIter = cpp20_input_iterator<const int*>;
 
@@ -50,8 +50,8 @@ struct SizedInputRange : std::ranges::view_interface<SizedInputRange> {
 static_assert(std::ranges::sized_range<SizedInputRange>);
 
 struct NotSizedSentinel {
-  using value_type = int;
-  using difference_type = std::ptrdiff_t;
+  using value_type       = int;
+  using difference_type  = std::ptrdiff_t;
   using iterator_concept = std::forward_iterator_tag;
 
   explicit NotSizedSentinel() = default;
@@ -66,9 +66,7 @@ static_assert(std::forward_iterator<NotSizedSentinel>);
 using ForwardIter = forward_iterator<int*>;
 
 // So that we conform to sized_sentinel_for.
-constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) {
-    return base(x) - base(y);
-}
+constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) { return base(x) - base(y); }
 
 struct ForwardRange : std::ranges::view_interface<ForwardRange> {
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
@@ -78,19 +76,17 @@ struct ForwardRange : std::ranges::view_interface<ForwardRange> {
 static_assert(std::ranges::view<ForwardRange>);
 
 struct MoveOnlyForwardRange : std::ranges::view_interface<MoveOnlyForwardRange> {
-  int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
-  MoveOnlyForwardRange(MoveOnlyForwardRange const&) = delete;
-  MoveOnlyForwardRange(MoveOnlyForwardRange &&) = default;
-  MoveOnlyForwardRange& operator=(MoveOnlyForwardRange &&) = default;
-  MoveOnlyForwardRange() = default;
+  int buff[8]                                             = {0, 1, 2, 3, 4, 5, 6, 7};
+  MoveOnlyForwardRange(MoveOnlyForwardRange const&)       = delete;
+  MoveOnlyForwardRange(MoveOnlyForwardRange&&)            = default;
+  MoveOnlyForwardRange& operator=(MoveOnlyForwardRange&&) = default;
+  MoveOnlyForwardRange()                                  = default;
   constexpr ForwardIter begin() const { return ForwardIter(const_cast<int*>(buff)); }
   constexpr ForwardIter end() const { return ForwardIter(const_cast<int*>(buff) + 8); }
 };
 static_assert(std::ranges::view<MoveOnlyForwardRange>);
 
-struct MI : std::ranges::view_interface<InputRange>,
-            std::ranges::view_interface<MoveOnlyForwardRange> {
-};
+struct MI : std::ranges::view_interface<InputRange>, std::ranges::view_interface<MoveOnlyForwardRange> {};
 static_assert(!std::ranges::view<MI>);
 
 struct EmptyIsTrue : std::ranges::view_interface<EmptyIsTrue> {
@@ -131,7 +127,7 @@ struct DataIsNull : std::ranges::view_interface<DataIsNull> {
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
   constexpr ContIter begin() const { return ContIter(buff); }
   constexpr ContIter end() const { return ContIter(buff + 8); }
-  constexpr const int *data() const { return nullptr; }
+  constexpr const int* data() const { return nullptr; }
 };
 static_assert(std::ranges::view<DataIsNull>);
 
@@ -142,13 +138,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleCo
   };
 
   struct SentinelType {
-    int *base_;
+    int* base_;
     explicit SentinelType() = default;
-    constexpr explicit SentinelType(int *base) : base_(base) {}
-    friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) == sent.base_}; }
-    friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) == sent.base_}; }
-    friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) != sent.base_}; }
-    friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) != sent.base_}; }
+    constexpr explicit SentinelType(int* base) : base_(base) {}
+    friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept {
+      return {base(iter) == sent.base_};
+    }
+    friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept {
+      return {base(iter) == sent.base_};
+    }
+    friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept {
+      return {base(iter) != sent.base_};
+    }
+    friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept {
+      return {base(iter) != sent.base_};
+    }
   };
 
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
@@ -157,21 +161,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleCo
 };
 static_assert(std::ranges::view<BoolConvertibleComparison>);
 
-template<class T>
-concept EmptyInvocable = requires (T const& obj) { obj.empty(); };
+template <class T>
+concept EmptyInvocable = requires(T const& obj) { obj.empty(); };
 
-template<class T>
-concept BoolOpInvocable = requires (T const& obj) { bool(obj); };
+template <class T>
+concept BoolOpInvocable = requires(T const& obj) { bool(obj); };
 
 constexpr bool testEmpty() {
   static_assert(!EmptyInvocable<InputRange>);
   // LWG 3715: `view_interface::empty` is overconstrained
   static_assert(EmptyInvocable<SizedInputRange>);
-  static_assert( EmptyInvocable<ForwardRange>);
+  static_assert(EmptyInvocable<ForwardRange>);
 
   static_assert(!BoolOpInvocable<InputRange>);
   static_assert(BoolOpInvocable<SizedInputRange>);
-  static_assert( BoolOpInvocable<ForwardRange>);
+  static_assert(BoolOpInvocable<ForwardRange>);
 
   SizedInputRange sizedInputRange;
   assert(!sizedInputRange.empty());
@@ -224,12 +228,12 @@ constexpr bool testEmpty() {
   return true;
 }
 
-template<class T>
-concept DataInvocable = requires (T const& obj) { obj.data(); };
+template <class T>
+concept DataInvocable = requires(T const& obj) { obj.data(); };
 
 constexpr bool testData() {
   static_assert(!DataInvocable<ForwardRange>);
-  static_assert( DataInvocable<ContRange>);
+  static_assert(DataInvocable<ContRange>);
 
   ContRange contiguous;
   assert(contiguous.data() == contiguous.buff);
@@ -249,18 +253,18 @@ constexpr bool testData() {
   return true;
 }
 
-template<class T>
-concept SizeInvocable = requires (T const& obj) { obj.size(); };
+template <class T>
+concept SizeInvocable = requires(T const& obj) { obj.size(); };
 
 constexpr bool testSize() {
   static_assert(!SizeInvocable<InputRange>);
   static_assert(!SizeInvocable<NotSizedSentinel>);
-  static_assert( SizeInvocable<ForwardRange>);
+  static_assert(SizeInvocable<ForwardRange>);
 
   // Test the test.
   static_assert(std::same_as<decltype(std::declval<ForwardIter>() - std::declval<ForwardIter>()), std::ptrdiff_t>);
   using UnsignedSize = std::make_unsigned_t<std::ptrdiff_t>;
-  using SignedSize = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>;
+  using SignedSize   = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>;
   ForwardRange forwardRange;
   assert(forwardRange.size() == 8);
   assert(static_cast<ForwardRange const&>(forwardRange).size() == 8);
@@ -284,12 +288,12 @@ constexpr bool testSize() {
   return true;
 }
 
-template<class T>
-concept SubscriptInvocable = requires (T const& obj, std::size_t n) { obj[n]; };
+template <class T>
+concept SubscriptInvocable = requires(T const& obj, std::size_t n) { obj[n]; };
 
 constexpr bool testSubscript() {
   static_assert(!SubscriptInvocable<ForwardRange>);
-  static_assert( SubscriptInvocable<RARange>);
+  static_assert(SubscriptInvocable<RARange>);
 
   RARange randomAccess;
   assert(randomAccess[2] == 2);
@@ -300,17 +304,59 @@ constexpr bool testSubscript() {
   return true;
 }
 
-template<class T>
-concept FrontInvocable = requires (T const& obj) { obj.front(); };
+template <class T>
+concept AtInvocable = requires(T const& obj, std::size_t n) { obj.at(n); };
+
+#if TEST_STD_VER >= 29
+constexpr bool testAt() {
+  static_assert(!AtInvocable<ForwardRange>);
+  static_assert(AtInvocable<RARange>);
+
+  RARange randomAccess;
+  assert(randomAccess.at(2) == 2);
+  assert(static_cast<RARange const&>(randomAccess).at(2) == 2);
+  randomAccess.at(2) = 3;
+  assert(randomAccess.at(2) == 3);
+
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  if (!std::is_constant_evaluated()) {
+    try {
+      TEST_IGNORE_NODISCARD randomAccess.at(0);
+      assert(true);
+    } catch (std::out_of_range const&) {
+      // pass
+      assert(false); // This should not be reached because index 0 is valid.
+    } catch (...) {
+      assert(false);
+    }
+
+    // Test that at() throws an exception when the index is out of range.
+    try {
+      TEST_IGNORE_NODISCARD randomAccess.at(10);
+      assert(false);
+    } catch (std::out_of_range const&) {
+      // pass
+    } catch (...) {
+      assert(false);
+    }
+  }
+#endif
+
+  return true;
+}
+#endif // TEST_STD_VER >= 29
+
+template <class T>
+concept FrontInvocable = requires(T const& obj) { obj.front(); };
 
-template<class T>
-concept BackInvocable = requires (T const& obj) { obj.back(); };
+template <class T>
+concept BackInvocable = requires(T const& obj) { obj.back(); };
 
 constexpr bool testFrontBack() {
   static_assert(!FrontInvocable<InputRange>);
-  static_assert( FrontInvocable<ForwardRange>);
+  static_assert(FrontInvocable<ForwardRange>);
   static_assert(!BackInvocable<ForwardRange>);
-  static_assert( BackInvocable<RARange>);
+  static_assert(BackInvocable<RARange>);
 
   ForwardRange forwardRange;
   assert(forwardRange.front() == 0);
@@ -332,8 +378,10 @@ constexpr bool testFrontBack() {
   return true;
 }
 
-struct V1 : std::ranges::view_interface<V1> { };
-struct V2 : std::ranges::view_interface<V2> { V1 base_; };
+struct V1 : std::ranges::view_interface<V1> {};
+struct V2 : std::ranges::view_interface<V2> {
+  V1 base_;
+};
 static_assert(sizeof(V2) == sizeof(V1));
 
 int main(int, char**) {
@@ -349,6 +397,11 @@ int main(int, char**) {
   testSubscript();
   static_assert(testSubscript());
 
+#if TEST_STD_VER >= 29
+  testAt();
+  static_assert(testAt());
+#endif
+
   testFrontBack();
   static_assert(testFrontBack());
 
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..a2540256b3f1d 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1526,6 +1526,13 @@ def add_version_header(tc):
             },
             "headers": ["variant"],
         },
+        {
+            "name": "__cpp_lib_view_interface",
+            "values": {
+                "c++20": 202606,
+            },
+            "headers": ["ranges"],
+        },
         {
             "name": "__cpp_lib_void_t",
             "values": {"c++17": 201411},


From libcxx-commits at lists.llvm.org  Tue Jul 14 00:05:01 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 14 Jul 2026 00:05:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3138R3:
	`views::cache_latest` (PR #151450)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/151450@github.com>
Message-ID: <6a55df9d.510f576d.1b3cb9.6914@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/151450

>From eb6a19f5bb3046571b5abe4dcec31e1c4704b198 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 16 Jul 2025 23:17:31 +0300
Subject: [PATCH] [libc++][ranges] P3138R3: `views::cache_latest`

Implements: https://wg21.link/P3138R3

Closes #118134

References:

- https://wg21.link/range.cache.latest
- https://wg21.link/range.nonprop.cache
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/ReleaseNotes/23.rst               |   1 +
 libcxx/docs/Status/Cxx26Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/cache_latest_view.h   | 228 +++++++++++++++
 .../include/__ranges/non_propagating_cache.h  |   2 +
 libcxx/include/module.modulemap.in            |   1 +
 libcxx/include/ranges                         |   8 +
 libcxx/include/version                        |   2 +
 libcxx/modules/std/ranges.inc                 |   9 +
 .../ranges/range.nonprop.cache/reset.pass.cpp |  52 ++++
 .../ranges.version.compile.pass.cpp           |  30 ++
 .../version.version.compile.pass.cpp          |  30 ++
 .../cpo.compile.pass.cpp                      |   2 +-
 .../range.cache.latest/adaptor.pass.cpp       | 259 ++++++++++++++++++
 .../range.cache.latest/base.pass.cpp          |  85 ++++++
 .../range.cache.latest/begin.pass.cpp         | 107 ++++++++
 .../range.cache.latest/ctad.compile.pass.cpp  |  30 ++
 .../range.cache.latest/ctor.default.pass.cpp  |  81 ++++++
 .../range.cache.latest/ctor.view.pass.cpp     |  24 ++
 .../enable_borrowed_range.compile.pass.cpp    |  34 +++
 .../range.cache.latest/end.pass.cpp           | 109 ++++++++
 .../range.cache.latest/general.pass.cpp       |  75 +++++
 .../iterator/ctor.default.pass.cpp            |  81 ++++++
 .../sentinel/ctor.default.pass.cpp            |  81 ++++++
 .../range.cache.latest/size.pass.cpp          | 109 ++++++++
 .../generate_feature_test_macro_components.py |   5 +
 27 files changed, 1448 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/include/__ranges/cache_latest_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.nonprop.cache/reset.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.cache.latest/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.cache.latest/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.cache.latest/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.cache.latest/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.cache.latest/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.cache.latest/ctor.view.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.cache.latest/enable_borrowed_range.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.cache.latest/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.cache.latest/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.cache.latest/iterator/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.cache.latest/sentinel/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.cache.latest/size.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..60520768f1dc2 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -370,6 +370,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_rvalue``                             ``202207L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_ranges_cache_latest``                          ``202411L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk``                                 *unimplemented*
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                              ``202202L``
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..a5fecbf30a0d6 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -48,6 +48,7 @@ Implemented Papers
 - P2781R9: ``std::constant_wrapper`` (`Github <https://llvm.org/PR148179>`__)
 - P3978R3: ``constant_wrapper`` should unwrap on call and subscript (`Github <https://llvm.org/PR189605>`__)
 - P2164R9: ``views::enumerate`` (`Github <https://llvm.org/PR105251>`__)
+- P3138R5: ``views::cache_latest`` (`Github <https://llvm.org/PR118134>`__)
 - P2322R6: ``ranges::fold`` (`Github <https://llvm.org/PR105208>`__)
 - P4144R1: Remove ``span``'s ``initializer_list`` constructor for C++26 (`Github <https://llvm.org/PR189612>`__)
 - P2542R8: ``views::concat`` (`Github <https://llvm.org/PR105419>`__)
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..1e8045c0bb359 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -77,7 +77,7 @@
 "`P2300R10 <https://wg21.link/P2300R10>`__","``std::execution``","2024-06 (St. Louis)","","","`#105440 <https://github.com/llvm/llvm-project/issues/105440>`__",""
 "","","","","","",""
 "`P3136R1 <https://wg21.link/P3136R1>`__","Retiring niebloids","2024-11 (Wrocław)","|Complete|","14","`#118133 <https://github.com/llvm/llvm-project/issues/118133>`__",""
-"`P3138R5 <https://wg21.link/P3138R5>`__","``views::cache_latest``","2024-11 (Wrocław)","","","`#118134 <https://github.com/llvm/llvm-project/issues/118134>`__",""
+"`P3138R5 <https://wg21.link/P3138R5>`__","``views::cache_latest``","2024-11 (Wrocław)","|Complete|","23","`#118134 <https://github.com/llvm/llvm-project/issues/118134>`__",""
 "`P3379R0 <https://wg21.link/P3379R0>`__","Constrain ``std::expected`` equality operators","2024-11 (Wrocław)","|Complete|","21","`#118135 <https://github.com/llvm/llvm-project/issues/118135>`__",""
 "`P2862R1 <https://wg21.link/P2862R1>`__","``text_encoding::name()`` should never return null values","2024-11 (Wrocław)","|Complete|","23","`#118371 <https://github.com/llvm/llvm-project/issues/118371>`__",""
 "`P2897R7 <https://wg21.link/P2897R7>`__","``aligned_accessor``: An ``mdspan`` accessor expressing pointer over-alignment","2024-11 (Wrocław)","|Complete|","21","`#118372 <https://github.com/llvm/llvm-project/issues/118372>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..ad8887acbf78d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -724,6 +724,7 @@ set(files
   __ranges/adjacent_view.h
   __ranges/all.h
   __ranges/as_rvalue_view.h
+  __ranges/cache_latest_view.h
   __ranges/chunk_by_view.h
   __ranges/common_view.h
   __ranges/concat_view.h
diff --git a/libcxx/include/__ranges/cache_latest_view.h b/libcxx/include/__ranges/cache_latest_view.h
new file mode 100644
index 0000000000000..fcf6b22e364a2
--- /dev/null
+++ b/libcxx/include/__ranges/cache_latest_view.h
@@ -0,0 +1,228 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_TO_CACHE_LATEST_VIEW_H
+#define _LIBCPP___RANGES_TO_CACHE_LATEST_VIEW_H
+
+#include <__concepts/constructible.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/is_reference.h>
+#include <__utility/as_lvalue.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+namespace ranges {
+
+// [range.cache.latest.view]
+
+template <input_range _View>
+  requires view<_View>
+class cache_latest_view : public view_interface<cache_latest_view<_View>> {
+  _View __base_ = _View();
+  using __cache_t _LIBCPP_NODEBUG =
+      conditional_t<is_reference_v<range_reference_t<_View>>,
+                    add_pointer_t<range_reference_t<_View>>,
+                    range_reference_t<_View>>;
+
+  __non_propagating_cache<__cache_t> __cache_;
+
+  // [range.cache.latest.iterator], class cache_latest_view::iterator
+  class __iterator;
+  // [range.cache.latest.sentinel], class cache_latest_view::sentinel
+  class __sentinel;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI cache_latest_view()
+    requires default_initializable<_View>
+  = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit cache_latest_view(_View __base) : __base_{std::move(__base)} {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator(*this); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() { return __sentinel{*this}; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return ranges::size(__base_);
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return ranges::size(__base_);
+  }
+
+  // TODO: Implement when P2846R6 is available.
+  // constexpr auto reserve_hint()
+  //   requires approximately_sized_range<_View>
+  // {
+  //   return ranges::reserve_hint(__base_);
+  // }
+  // constexpr auto reserve_hint() const
+  //   requires approximately_sized_range<const _View>
+  // {
+  //   return ranges::reserve_hint(__base_);
+  // }
+};
+
+template <class _Range>
+cache_latest_view(_Range&&) -> cache_latest_view<views::all_t<_Range>>;
+
+// [range.cache.latest.iterator]
+
+template <input_range _View>
+  requires view<_View>
+class cache_latest_view<_View>::__iterator {
+  cache_latest_view* __parent_;
+  iterator_t<_View> __current_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(cache_latest_view& __parent)
+      : __parent_{std::addressof(__parent)}, __current_{ranges::begin(__parent.__base_)} {}
+
+  friend class cache_latest_view<_View>;
+
+public:
+  using difference_type  = range_difference_t<_View>;
+  using value_type       = range_value_t<_View>;
+  using iterator_concept = input_iterator_tag;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&)            = default;
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator=(__iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_View>& base() const& noexcept { return __current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_reference_t<_View>& operator*() const {
+    if constexpr (is_reference_v<range_reference_t<_View>>) {
+      if (!__parent_->__cache_.__has_value()) {
+        __parent_->__cache_.__emplace(std::addressof(std::__as_lvalue(*__current_)));
+      }
+      return **__parent_->__cache_;
+    } else {
+      if (!__parent_->__cache_.__has_value()) {
+        __parent_->__cache_.__emplace_from([&]() -> decltype(auto) { return *__current_; });
+      }
+      return *__parent_->__cache_;
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    __parent_->__cache_.__reset();
+    ++__current_;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_View>
+  iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(__i.__current_))) {
+    return ranges::iter_move(__i.__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void
+  iter_swap(const __iterator& __x,
+            const __iterator& __y) noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_)))
+    requires indirectly_swappable<iterator_t<_View>>
+  {
+    ranges::iter_swap(__x.__current_, __y.__current_);
+  }
+};
+
+// [range.cache.latest.sentinel]
+
+template <input_range _View>
+  requires view<_View>
+class cache_latest_view<_View>::__sentinel {
+  sentinel_t<_View> __end_ = sentinel_t<_View>();
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(cache_latest_view& __parent)
+      : __end_{ranges::end(__parent.__base_)} {}
+
+  friend class cache_latest_view<_View>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
+    return __x.__current_ == __y.__end_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<_View>
+  operator-(const __iterator& __x, const __sentinel& __y)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return __x.__current_ - __y.__end_;
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<_View>
+  operator-(const __sentinel& __x, const __iterator& __y)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return __x.__end_ - __y.__current_;
+  }
+};
+
+namespace views {
+namespace __cache_latest_view {
+
+struct __fn : __range_adaptor_closure<__fn> {
+  template <class _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range) noexcept(noexcept(/**/ cache_latest_view(std::forward<_Range>(__range))))
+      -> decltype(/*-------------------------------*/ cache_latest_view(std::forward<_Range>(__range))) {
+    return /*--------------------------------------*/ cache_latest_view(std::forward<_Range>(__range));
+  }
+};
+
+} // namespace __cache_latest_view
+
+inline namespace __cpo {
+inline constexpr auto cache_latest = __cache_latest_view::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_TO_CACHE_LATEST_VIEW_H
diff --git a/libcxx/include/__ranges/non_propagating_cache.h b/libcxx/include/__ranges/non_propagating_cache.h
index beae13a98e9e3..74bf1d188ecb4 100644
--- a/libcxx/include/__ranges/non_propagating_cache.h
+++ b/libcxx/include/__ranges/non_propagating_cache.h
@@ -91,6 +91,8 @@ class __non_propagating_cache {
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp& __emplace(_Args&&... __args) {
     return __value_.emplace(__forward_tag{}, std::forward<_Args>(__args)...).__t_;
   }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void __reset() { __value_.reset(); }
 };
 
 struct __empty_cache {};
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..b6781e8eecbf7 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1894,6 +1894,7 @@ module std {
       export std.ranges.ref_view
     }
     module as_rvalue_view                 { header "__ranges/as_rvalue_view.h" }
+    module cache_latest_view              { header "__ranges/cache_latest_view.h" }
     module chunk_by_view {
       header "__ranges/chunk_by_view.h"
       export std.functional.bind_back
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..705ecc436801f 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -428,6 +428,13 @@ namespace std::ranges {
       enable_borrowed_range<V>;                                            // C++23
 
   namespace views { inline constexpr unspecified stride = unspecified; }   // C++23
+
+  // [range.cache.latest], cache latest view
+  template<input_range V>
+    requires view<V>
+  class cache_latest_view;                                                      // C++26
+
+  namespace views { inline constexpr unspecified cache_latest = unspecified; }  // C++26
 }
 
 namespace std {
@@ -525,6 +532,7 @@ namespace std {
 #  endif
 
 #  if _LIBCPP_STD_VER >= 26
+#    include <__ranges/cache_latest_view.h>
 #    include <__ranges/concat_view.h>
 #  endif
 
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..7269251459d2e 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -208,6 +208,7 @@ __cpp_lib_ranges                                        202406L <algorithm> <fun
                                                         202110L // C++20
 __cpp_lib_ranges_as_const                               202207L <ranges>
 __cpp_lib_ranges_as_rvalue                              202207L <ranges>
+__cpp_lib_ranges_cache_latest                           202411L <ranges>
 __cpp_lib_ranges_chunk                                  202202L <ranges>
 __cpp_lib_ranges_chunk_by                               202202L <ranges>
 __cpp_lib_ranges_concat                                 202403L <ranges>
@@ -528,6 +529,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges                               202406L
 // # define __cpp_lib_ranges_as_const                      202207L
 # define __cpp_lib_ranges_as_rvalue                     202207L
+# define __cpp_lib_ranges_cache_latest                  202411L
 // # define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
 # define __cpp_lib_ranges_contains                      202207L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..0434fbc86a829 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -372,6 +372,15 @@ export namespace std {
       using std::ranges::views::enumerate;
     }
 #endif // _LIBCPP_STD_VER >= 23
+
+#if _LIBCPP_STD_VER >= 26
+    // [range.cache.latest],  Cache latest view
+    using std::ranges::cache_latest_view;
+
+    namespace views {
+      using std::ranges::views::cache_latest;
+    }
+#endif
   } // namespace ranges
 
   namespace views = ranges::views;
diff --git a/libcxx/test/libcxx/ranges/range.nonprop.cache/reset.pass.cpp b/libcxx/test/libcxx/ranges/range.nonprop.cache/reset.pass.cpp
new file mode 100644
index 0000000000000..be00d58359245
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.nonprop.cache/reset.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr void __reset();
+
+#include <ranges>
+
+#include <cassert>
+
+template <class T>
+constexpr void test() {
+  using Cache = std::ranges::__non_propagating_cache<T>;
+
+  // __reset on an empty cache
+  {
+    Cache cache;
+    assert(!cache.__has_value());
+    cache.__reset();
+    assert(!cache.__has_value());
+  }
+
+  // __reset on a non-empty cache
+  {
+    Cache cache;
+    cache.__emplace();
+    assert(cache.__has_value());
+    cache.__reset();
+    assert(!cache.__has_value());
+  }
+}
+
+struct T {};
+
+constexpr bool tests() {
+  test<T>();
+  test<int>();
+  return true;
+}
+
+int main(int, char**) {
+  tests();
+  static_assert(tests());
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 670b0e664721a..8e047e7ff0712 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -36,6 +36,10 @@
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_cache_latest
+#    error "__cpp_lib_ranges_cache_latest should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_ranges_chunk
 #    error "__cpp_lib_ranges_chunk should not be defined before c++23"
 #  endif
@@ -98,6 +102,10 @@
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_cache_latest
+#    error "__cpp_lib_ranges_cache_latest should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_ranges_chunk
 #    error "__cpp_lib_ranges_chunk should not be defined before c++23"
 #  endif
@@ -160,6 +168,10 @@
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_cache_latest
+#    error "__cpp_lib_ranges_cache_latest should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_ranges_chunk
 #    error "__cpp_lib_ranges_chunk should not be defined before c++23"
 #  endif
@@ -225,6 +237,10 @@
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_cache_latest
+#    error "__cpp_lib_ranges_cache_latest should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_ranges_chunk
 #    error "__cpp_lib_ranges_chunk should not be defined before c++23"
 #  endif
@@ -302,6 +318,13 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
+#  ifndef __cpp_lib_ranges_cache_latest
+#    error "__cpp_lib_ranges_cache_latest should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_cache_latest != 202411L
+#    error "__cpp_lib_ranges_cache_latest should have the value 202411L in c++23"
+#  endif
+
 #  if !defined(_LIBCPP_VERSION)
 #    ifndef __cpp_lib_ranges_chunk
 #      error "__cpp_lib_ranges_chunk should be defined in c++23"
@@ -427,6 +450,13 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_ranges_cache_latest
+#    error "__cpp_lib_ranges_cache_latest should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_cache_latest != 202411L
+#    error "__cpp_lib_ranges_cache_latest should have the value 202411L in c++26"
+#  endif
+
 #  if !defined(_LIBCPP_VERSION)
 #    ifndef __cpp_lib_ranges_chunk
 #      error "__cpp_lib_ranges_chunk should be defined in c++26"
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 f2d74cc99d864..8414ac5642943 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
@@ -664,6 +664,10 @@
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_cache_latest
+#    error "__cpp_lib_ranges_cache_latest should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_ranges_chunk
 #    error "__cpp_lib_ranges_chunk should not be defined before c++23"
 #  endif
@@ -1640,6 +1644,10 @@
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_cache_latest
+#    error "__cpp_lib_ranges_cache_latest should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_ranges_chunk
 #    error "__cpp_lib_ranges_chunk should not be defined before c++23"
 #  endif
@@ -2781,6 +2789,10 @@
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_cache_latest
+#    error "__cpp_lib_ranges_cache_latest should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_ranges_chunk
 #    error "__cpp_lib_ranges_chunk should not be defined before c++23"
 #  endif
@@ -4189,6 +4201,10 @@
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_cache_latest
+#    error "__cpp_lib_ranges_cache_latest should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_ranges_chunk
 #    error "__cpp_lib_ranges_chunk should not be defined before c++23"
 #  endif
@@ -5774,6 +5790,13 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
+#  ifndef __cpp_lib_ranges_cache_latest
+#    error "__cpp_lib_ranges_cache_latest should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_cache_latest != 202411L
+#    error "__cpp_lib_ranges_cache_latest should have the value 202411L in c++23"
+#  endif
+
 #  if !defined(_LIBCPP_VERSION)
 #    ifndef __cpp_lib_ranges_chunk
 #      error "__cpp_lib_ranges_chunk should be defined in c++23"
@@ -7725,6 +7748,13 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_ranges_cache_latest
+#    error "__cpp_lib_ranges_cache_latest should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_cache_latest != 202411L
+#    error "__cpp_lib_ranges_cache_latest should have the value 202411L in c++26"
+#  endif
+
 #  if !defined(_LIBCPP_VERSION)
 #    ifndef __cpp_lib_ranges_chunk
 #      error "__cpp_lib_ranges_chunk should be defined in c++26"
diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index 71a0b6ebb804c..c0cb53029a349 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -136,7 +136,7 @@ static_assert(test(std::views::zip, a, a));
 #endif
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::cache_latest, a));
+static_assert(test(std::views::cache_latest, a));
 static_assert(test(std::views::concat, a, a));
 // static_assert(test(std::views::to_input, a));
 #endif
diff --git a/libcxx/test/std/ranges/range.adaptors/range.cache.latest/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/adaptor.pass.cpp
new file mode 100644
index 0000000000000..779c59436c21d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/adaptor.pass.cpp
@@ -0,0 +1,259 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class cache_latest_view
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+static_assert(std::is_same_v<decltype(std::views::cache_latest), decltype(std::ranges::views::cache_latest)>);
+
+struct NonView {};
+
+static_assert(std::default_initializable<NonView>);
+
+struct DefaultInitializableView : std::ranges::view_base {
+  int i_;
+
+  int* begin();
+  int* end();
+};
+
+static_assert(std::default_initializable<DefaultInitializableView>);
+static_assert(std::ranges::common_range<DefaultInitializableView>);
+static_assert(std::ranges::input_range<DefaultInitializableView>);
+
+struct CommonView : std::ranges::view_base {
+  int i_;
+
+  constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&i_); }
+  constexpr forward_iterator<int*> end() { return begin(); }
+};
+
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::ranges::forward_range<CommonView>);
+static_assert(std::ranges::input_range<CommonView>);
+
+struct NonCommonView : std::ranges::view_base {
+  int i_;
+
+  constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&i_); }
+  constexpr sentinel_wrapper<forward_iterator<int*>> end() { return sentinel_wrapper<forward_iterator<int*>>(begin()); }
+};
+
+static_assert(!std::ranges::common_range<NonCommonView>);
+static_assert(std::ranges::forward_range<NonCommonView>);
+static_assert(std::ranges::input_range<NonCommonView>);
+static_assert(
+    std::derived_from< typename std::iterator_traits<std::ranges::iterator_t<NonCommonView>>::iterator_category,
+                       std::input_iterator_tag>);
+
+// Check that the `cache_latest` adaptor can be used with a view.
+
+static_assert(!std::is_invocable_v<decltype(std::views::cache_latest)>);
+static_assert(!std::is_invocable_v<decltype(std::views::cache_latest), NonView>);
+static_assert(std::is_invocable_v<decltype(std::views::cache_latest), DefaultInitializableView>);
+static_assert(std::is_invocable_v<decltype(std::views::cache_latest), CommonView>);
+static_assert(std::is_invocable_v<decltype(std::views::cache_latest), NonCommonView>);
+
+static_assert(!CanBePiped<NonView, decltype(std::views::cache_latest)>);
+static_assert(CanBePiped<DefaultInitializableView&, decltype(std::views::cache_latest)>);
+static_assert(CanBePiped<CommonView&, decltype(std::views::cache_latest)>);
+static_assert(CanBePiped<NonCommonView&, decltype(std::views::cache_latest)>);
+
+constexpr bool test() {
+  { // view | views::cache_latest
+    {
+      DefaultInitializableView view{{}, 94};
+      std::same_as<std::ranges::cache_latest_view<DefaultInitializableView>> decltype(auto) v =
+          view | std::views::cache_latest;
+      assert(v.base().i_ == 94);
+
+      static_assert(!std::ranges::common_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(std::ranges::input_range<decltype(v)>);
+    }
+    {
+      CommonView view{{}, 94};
+      std::same_as<std::ranges::cache_latest_view<CommonView>> decltype(auto) v = view | std::views::cache_latest;
+      assert(v.base().i_ == 94);
+
+      static_assert(!std::ranges::common_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(std::ranges::input_range<decltype(v)>);
+    }
+    {
+      NonCommonView view{{}, 94};
+      std::same_as<std::ranges::cache_latest_view<NonCommonView>> decltype(auto) v = view | std::views::cache_latest;
+      assert(v.base().i_ == 94);
+
+      static_assert(!std::ranges::common_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(std::ranges::input_range<decltype(v)>);
+    }
+  }
+
+  { // adaptor | views::cache_latest
+    {
+      DefaultInitializableView view{{}, 94};
+      const auto partial = std::views::transform(std::identity{}) | std::views::cache_latest;
+      std::same_as<std::ranges::cache_latest_view<
+          std::ranges::transform_view<DefaultInitializableView, std::identity>>> decltype(auto) v = partial(view);
+      assert(v.base().base().i_ == 94);
+
+      static_assert(!std::ranges::common_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(std::ranges::input_range<decltype(v)>);
+    }
+    {
+      CommonView view{{}, 94};
+      const auto partial = std::views::transform(std::identity{}) | std::views::cache_latest;
+      std::same_as<
+          std::ranges::cache_latest_view< std::ranges::transform_view<CommonView, std::identity>>> decltype(auto) v =
+          partial(view);
+      assert(v.base().base().i_ == 94);
+
+      static_assert(!std::ranges::common_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(std::ranges::input_range<decltype(v)>);
+    }
+    {
+      NonCommonView view{{}, 94};
+      const auto partial = std::views::transform(std::identity{}) | std::views::cache_latest;
+      std::same_as<
+          std::ranges::cache_latest_view< std::ranges::transform_view<NonCommonView, std::identity>>> decltype(auto) v =
+          partial(view);
+      assert(v.base().base().i_ == 94);
+
+      static_assert(!std::ranges::common_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(std::ranges::input_range<decltype(v)>);
+    }
+  }
+
+  { // views::cache_latest | adaptor
+    {
+      DefaultInitializableView view{{}, 94};
+      const auto partial = std::views::cache_latest | std::views::transform(std::identity{});
+      std::same_as<std::ranges::transform_view<std::ranges::cache_latest_view<DefaultInitializableView>,
+                                               std::identity>> decltype(auto) v = partial(view);
+      assert(v.base().base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+    {
+      CommonView view{{}, 94};
+      const auto partial = std::views::cache_latest | std::views::transform(std::identity{});
+      std::same_as<
+          std::ranges::transform_view<std::ranges::cache_latest_view<CommonView>, std::identity>> decltype(auto) v =
+          partial(view);
+      assert(v.base().base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+    {
+      NonCommonView view{{}, 94};
+      const auto partial = std::views::cache_latest | std::views::transform(std::identity{});
+      std::same_as<
+          std::ranges::transform_view<std::ranges::cache_latest_view<NonCommonView>, std::identity>> decltype(auto) v =
+          partial(view);
+      assert(v.base().base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+  }
+
+  { // views::cache_latest | views::all
+    {
+      DefaultInitializableView view{{}, 94};
+      std::same_as<std::ranges::cache_latest_view<DefaultInitializableView>> decltype(auto) v =
+          std::views::all(view) | std::views::cache_latest;
+      assert(v.base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+    {
+      CommonView view{{}, 94};
+      std::same_as<std::ranges::cache_latest_view<CommonView>> decltype(auto) v =
+          std::views::all(view) | std::views::cache_latest;
+      assert(v.base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+    {
+      NonCommonView view{{}, 94};
+      std::same_as<std::ranges::cache_latest_view<NonCommonView>> decltype(auto) v =
+          std::views::all(view) | std::views::cache_latest;
+      assert(v.base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+  }
+
+  { // views::cache_latest | views::all_t
+    {
+      DefaultInitializableView view{{}, 94};
+      std::same_as<std::ranges::cache_latest_view<DefaultInitializableView>> decltype(auto) v =
+          std::views::all_t<DefaultInitializableView>(view) | std::views::cache_latest;
+      assert(v.base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+    {
+      CommonView view{{}, 94};
+      std::same_as<std::ranges::cache_latest_view<CommonView>> decltype(auto) v =
+          std::views::all_t<CommonView>(view) | std::views::cache_latest;
+      assert(v.base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+    {
+      NonCommonView view{{}, 94};
+      std::same_as<std::ranges::cache_latest_view<NonCommonView>> decltype(auto) v =
+          std::views::all_t<NonCommonView>(view) | std::views::cache_latest;
+      assert(v.base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.cache.latest/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/base.pass.cpp
new file mode 100644
index 0000000000000..d0734633bb9c3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/base.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class cache_latest_view
+
+//     constexpr V base() const & requires copy_constructible<V> { return base_; }
+//     constexpr V base() && { return std::move(base_); }
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+#include <utility>
+
+#include "MoveOnly.h"
+
+struct SimpleView : std::ranges::view_base {
+  int i_;
+
+  int* begin() const;
+  int* end() const;
+};
+
+struct MoveOnlyView : public SimpleView {
+  MoveOnly m_;
+};
+
+template <class T>
+concept HasBase = requires(T&& t) { std::forward<T>(t).base(); };
+
+static_assert(HasBase<std::ranges::cache_latest_view<SimpleView> const&>);
+static_assert(HasBase<std::ranges::cache_latest_view<SimpleView>&&>);
+
+static_assert(!HasBase<std::ranges::cache_latest_view<MoveOnlyView> const&>);
+static_assert(HasBase<std::ranges::cache_latest_view<MoveOnlyView>&&>);
+
+constexpr bool test() {
+  { // &
+    std::ranges::cache_latest_view<SimpleView> view(SimpleView{{}, 94});
+    std::same_as<SimpleView> decltype(auto) v = view.base();
+    assert(v.i_ == 94);
+  }
+
+  { // const &
+    const std::ranges::cache_latest_view<SimpleView> view(SimpleView{{}, 94});
+    std::same_as<SimpleView> decltype(auto) v = view.base();
+    assert(v.i_ == 94);
+  }
+
+  { // &&
+    std::ranges::cache_latest_view<SimpleView> view(SimpleView{{}, 94});
+    std::same_as<SimpleView> decltype(auto) v = std::move(view).base();
+    assert(v.i_ == 94);
+  }
+
+  { // const &&
+    const std::ranges::cache_latest_view<SimpleView> view(SimpleView{{}, 94});
+    std::same_as<SimpleView> decltype(auto) v = std::move(view).base();
+    assert(v.i_ == 94);
+  }
+
+  { // move only
+    std::ranges::cache_latest_view<MoveOnlyView> view(MoveOnlyView{{}, 94});
+    std::same_as<MoveOnlyView> decltype(auto) v = std::move(view).base();
+    assert(v.m_.get() == 94);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.cache.latest/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/begin.pass.cpp
new file mode 100644
index 0000000000000..3459ce015ef65
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/begin.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class cache_latest_view
+
+//    constexpr auto begin();
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+#include "test_iterators.h"
+
+struct NonConstView : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+struct ConstView : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct ConstNonConstView : std::ranges::view_base {
+  int* begin();
+  int* end();
+  int* begin() const;
+  int* end() const;
+};
+
+template <class T>
+concept HasBegin = requires(T t) { t.begin(); };
+
+static_assert(HasBegin<std::ranges::cache_latest_view<NonConstView>>);
+static_assert(!HasBegin<const std::ranges::cache_latest_view<NonConstView>>);
+static_assert(HasBegin<std::ranges::cache_latest_view<ConstView>>);
+static_assert(!HasBegin<const std::ranges::cache_latest_view<ConstView>>);
+static_assert(HasBegin<std::ranges::cache_latest_view<ConstNonConstView>>);
+static_assert(!HasBegin<const std::ranges::cache_latest_view<ConstNonConstView>>);
+
+struct CommonView : std::ranges::view_base {
+  int i_;
+
+  constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&i_); }
+  constexpr forward_iterator<int*> end() { return begin(); }
+};
+
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::ranges::forward_range<CommonView>);
+static_assert(std::ranges::input_range<CommonView>);
+
+struct NonCommonView : std::ranges::view_base {
+  int i_;
+
+  constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&i_); }
+  constexpr sentinel_wrapper<forward_iterator<int*>> end() { return sentinel_wrapper<forward_iterator<int*>>(begin()); }
+};
+
+static_assert(!std::ranges::common_range<NonCommonView>);
+static_assert(std::ranges::forward_range<NonCommonView>);
+static_assert(std::ranges::input_range<NonCommonView>);
+static_assert(
+    std::derived_from<typename std::iterator_traits<std::ranges::iterator_t<NonCommonView>>::iterator_category,
+                      std::input_iterator_tag>);
+
+constexpr bool test() {
+  {
+    CommonView view{{}, 94};
+
+    std::same_as<std::ranges::cache_latest_view<CommonView>> decltype(auto) v = view | std::views::cache_latest;
+    assert(*v.begin() == view.i_);
+    // assert(&*v.begin() == std::addressof(view.i_));
+  }
+  {
+    NonCommonView view{{}, 94};
+
+    std::same_as<std::ranges::cache_latest_view<NonCommonView>> decltype(auto) v = view | std::views::cache_latest;
+    assert(*v.begin() == view.i_);
+    // assert(base(v.begin().base()) == std::addressof(view.i_));
+  }
+
+  {
+    int arr[] = {82, 94, 76};
+
+    std::same_as<std::ranges::cache_latest_view<std::ranges::ref_view<int[3]>>> decltype(auto) v =
+        arr | std::views::cache_latest;
+    assert(base(v.begin().base()) == arr);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.cache.latest/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..2ed5eab4abd46
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/ctad.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class cache_latest_view
+
+//   template<class R>
+//     cache_latest_view(R&&) -> cache_latest_view<views::all_t<R>>;
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+static_assert(std::same_as<decltype(std::ranges::cache_latest_view(std::vector<int>{})),
+                           std::ranges::cache_latest_view<std::views::all_t<std::vector<int>>>>);
+
+static_assert(std::same_as<decltype(std::ranges::cache_latest_view(std::declval<std::vector<int>&>())),
+                           std::ranges::cache_latest_view<std::views::all_t<std::vector<int>&>>>);
+
+static_assert(std::same_as<decltype(std::ranges::cache_latest_view(std::ranges::empty_view<int>{})),
+                           std::ranges::cache_latest_view<std::ranges::empty_view<int>>>);
\ No newline at end of file
diff --git a/libcxx/test/std/ranges/range.adaptors/range.cache.latest/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..adae9479188d3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/ctor.default.pass.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class cache_latest_view
+
+//     cache_latest_view() requires default_initializable<V> = default;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+struct DefaultInitializableView : std::ranges::view_base {
+  int i_ = 94;
+
+  int* begin();
+  int* end();
+};
+
+static_assert(std::default_initializable<DefaultInitializableView>);
+static_assert(std::default_initializable<std::ranges::cache_latest_view<DefaultInitializableView>>);
+
+struct NoDefaultInitializableView : std::ranges::view_base {
+  NoDefaultInitializableView() = delete;
+
+  int* begin();
+  int* end();
+};
+
+static_assert(!std::default_initializable<NoDefaultInitializableView>);
+static_assert(!std::default_initializable<std::ranges::cache_latest_view<NoDefaultInitializableView>>);
+
+struct NoexceptView : std::ranges::view_base {
+  NoexceptView() noexcept;
+
+  int const* begin() const;
+  int const* end() const;
+};
+
+static_assert(noexcept(std::ranges::cache_latest_view<NoexceptView>()));
+
+struct NoNoexceptView : std::ranges::view_base {
+  NoNoexceptView() noexcept(false);
+
+  int const* begin() const;
+  int const* end() const;
+};
+
+static_assert(!noexcept(std::ranges::cache_latest_view<NoNoexceptView>()));
+
+constexpr bool test() {
+  { // value-initialized (i.e., whether T() is well-formed).
+    std::ranges::cache_latest_view<DefaultInitializableView> cache_latest_view{};
+    assert(cache_latest_view.base().i_ == 94);
+  }
+  { // direct-list-initialized from an empty initializer list (i.e., whether T{} is well-formed).
+    std::ranges::cache_latest_view<DefaultInitializableView> cache_latest_view = {};
+    assert(cache_latest_view.base().i_ == 94);
+  }
+  { // default-initialized (i.e., whether T t; is well-formed).
+    std::ranges::cache_latest_view<DefaultInitializableView> cache_latest_view;
+    assert(cache_latest_view.base().i_ == 94);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.cache.latest/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/ctor.view.pass.cpp
new file mode 100644
index 0000000000000..9803e7500e8d3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/ctor.view.pass.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
+
+// <ranges>
+
+// class cache_latest_view
+
+//    constexpr explicit cache_latest_view(V base);
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.cache.latest/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/enable_borrowed_range.compile.pass.cpp
new file mode 100644
index 0000000000000..ba721291c3be4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/enable_borrowed_range.compile.pass.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
+
+// <ranges>
+
+// class cache_latest_view
+
+// Test that std::ranges::cache_latest_view is not std::ranges::borrowed_range.
+
+#include <ranges>
+
+struct BorrowedView : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<BorrowedView> = true;
+
+static_assert(!std::ranges::borrowed_range<std::ranges::cache_latest_view<BorrowedView>>);
+
+struct NonBorrowedView : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+static_assert(!std::ranges::borrowed_range<std::ranges::cache_latest_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.cache.latest/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/end.pass.cpp
new file mode 100644
index 0000000000000..f3bf777c35a42
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/end.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class cache_latest_view
+
+//    constexpr auto end();
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+#include "test_iterators.h"
+#include <print>
+
+struct NonConstView : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+struct ConstView : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct ConstNonConstView : std::ranges::view_base {
+  int* begin();
+  int* end();
+  int* begin() const;
+  int* end() const;
+};
+
+template <class T>
+concept HasEnd = requires(T t) { t.end(); };
+
+static_assert(HasEnd<std::ranges::cache_latest_view<NonConstView>>);
+static_assert(!HasEnd<const std::ranges::cache_latest_view<NonConstView>>);
+static_assert(HasEnd<std::ranges::cache_latest_view<ConstView>>);
+static_assert(!HasEnd<const std::ranges::cache_latest_view<ConstView>>);
+static_assert(HasEnd<std::ranges::cache_latest_view<ConstNonConstView>>);
+static_assert(!HasEnd<const std::ranges::cache_latest_view<ConstNonConstView>>);
+
+struct CommonView : std::ranges::view_base {
+  int i_;
+
+  constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&i_); }
+  constexpr forward_iterator<int*> end() { return begin(); }
+};
+
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::ranges::forward_range<CommonView>);
+static_assert(std::ranges::input_range<CommonView>);
+
+struct NonCommonView : std::ranges::view_base {
+  int i_;
+
+  constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&i_); }
+  constexpr sentinel_wrapper<forward_iterator<int*>> end() { return sentinel_wrapper<forward_iterator<int*>>(begin()); }
+};
+
+static_assert(!std::ranges::common_range<NonCommonView>);
+static_assert(std::ranges::forward_range<NonCommonView>);
+static_assert(std::ranges::input_range<NonCommonView>);
+static_assert(
+    std::derived_from<typename std::iterator_traits<std::ranges::iterator_t<NonCommonView>>::iterator_category,
+                      std::input_iterator_tag>);
+
+constexpr bool test() {
+  {
+    CommonView view{{}, 94};
+
+    std::same_as<std::ranges::cache_latest_view<CommonView>> decltype(auto) v = view | std::views::cache_latest;
+
+    assert(*base(v.end().base()) == view.i_);
+  }
+  {
+    NonCommonView view{{}, 94};
+
+    std::same_as<std::ranges::cache_latest_view<NonCommonView>> decltype(auto) v = view | std::views::cache_latest;
+
+    assert(*base(v.end().base()) == view.i_);
+  }
+
+  {
+    int arr[] = {82, 94, 76};
+
+    std::same_as<std::ranges::cache_latest_view<std::ranges::ref_view<int[3]>>> decltype(auto) v =
+        arr | std::views::cache_latest;
+
+    assert(base(v.end().base()) == arr + 3);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.cache.latest/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/general.pass.cpp
new file mode 100644
index 0000000000000..82f91770a59e8
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/general.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class cache_latest_view
+
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class cache_latest_view
+
+// Functional tests of std::ranges::cache_latest_view.
+
+#include <cassert>
+#include <ranges>
+#include <vector>
+#include <algorithm>
+
+constexpr bool test() {
+  // Motivational example from P3138R3.
+  std::vector<int> vec          = {1, 2, 3, 4, 5};
+  std::vector<int> filtered_vec = {4, 16};
+
+  { // Uncached transform and filter.
+    int transform_counter = 0;
+    const auto get_square = [&](int i) {
+      ++transform_counter;
+      return i * i;
+    };
+    auto view = vec | std::views::transform(get_square) | std::views::filter([&](int i) { return i % 2 == 0; });
+
+    assert(std::ranges::equal(view, filtered_vec));
+    // The transform is called twice for each element that is not filtered out.
+    assert(transform_counter == 7);
+  }
+  { // Cached transform and filter.
+    int transform_counter = 0;
+    const auto get_square = [&](int i) {
+      ++transform_counter;
+      return i * i;
+    };
+    auto view = vec | std::views::transform(get_square) | std::views::cache_latest |
+                std::views::filter([&](int i) { return i % 2 == 0; });
+
+    assert(std::ranges::equal(view, filtered_vec));
+    // The transform is called only once for each element.
+    assert(transform_counter == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.cache.latest/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/iterator/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..8232d0401ab37
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/iterator/ctor.default.pass.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//   template<input_range V>
+//     requires view<V>
+//   template<bool Const>
+//   class to_input_view<V>::iterator
+
+//    iterator() requires default_initializable<iterator_t<Base>> = default;
+
+#include <cassert>
+#include <cstdint>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+struct DefaultInitializableIterator {
+  int i_; // Deliberately uninitialized.
+
+  using difference_type   = std::intptr_t;
+  using value_type        = int;
+  using iterator_category = std::random_access_iterator_tag;
+
+  constexpr int operator*() const { return i_; }
+
+  constexpr DefaultInitializableIterator& operator++() { return *this; }
+  constexpr void operator++(int) {}
+
+  friend constexpr bool operator==(const DefaultInitializableIterator&, const DefaultInitializableIterator&) = default;
+};
+
+static_assert(std::default_initializable<DefaultInitializableIterator>);
+
+struct DefaultInitializableIteratorView : std::ranges::view_base {
+  DefaultInitializableIterator begin() const;
+  DefaultInitializableIterator end() const;
+};
+
+struct NonDefaultInitializableIteratorView : std::ranges::view_base {
+  static_assert(!std::default_initializable<cpp20_input_iterator<int*>>);
+
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+
+template <class View>
+using CacheLatestViewIteratorT = std::ranges::iterator_t<std::ranges::cache_latest_view<View>>;
+
+// Check that the to_input_view's iterator is default initializable when the underlying iterator is.
+// static_assert(std::default_initializable<CacheLatestViewIteratorT<DefaultInitializableIteratorView>>);
+// static_assert(!std::default_initializable<CacheLatestViewIteratorT<NonDefaultInitializableIteratorView>>);
+
+constexpr bool test() {
+  // {
+  //   CacheLatestViewIteratorT<DefaultInitializableIteratorView> it;
+  //   assert(*it == 0); // DefaultInitializableIterator has to be initialized to have value 0.
+  // }
+  // {
+  //   CacheLatestViewIteratorT<DefaultInitializableIteratorView> it = {};
+  //   assert(*it == 0); // DefaultInitializableIterator has to be initialized to have value 0.
+  // }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.cache.latest/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/sentinel/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..7d228552d0900
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/sentinel/ctor.default.pass.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//   template<input_range V>
+//     requires view<V>
+//   template<bool Const>
+//   class to_input_view<V>::iterator
+
+//    iterator() requires default_initializable<iterator_t<Base>> = default;
+
+#include <cassert>
+#include <cstdint>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+struct DefaultInitializableIterator {
+  int i_; // Deliberately uninitialized.
+
+  using difference_type   = std::intptr_t;
+  using value_type        = int;
+  using iterator_category = std::random_access_iterator_tag;
+
+  constexpr int operator*() const { return i_; }
+
+  constexpr DefaultInitializableIterator& operator++() { return *this; }
+  constexpr void operator++(int) {}
+
+  friend constexpr bool operator==(const DefaultInitializableIterator&, const DefaultInitializableIterator&) = default;
+};
+
+static_assert(std::default_initializable<DefaultInitializableIterator>);
+
+struct DefaultInitializableIteratorView : std::ranges::view_base {
+  DefaultInitializableIterator begin() const;
+  DefaultInitializableIterator end() const;
+};
+
+struct NonDefaultInitializableIteratorView : std::ranges::view_base {
+  static_assert(!std::default_initializable<cpp20_input_iterator<int*>>);
+
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+
+template <class View>
+using CacheLatestViewSentinelT = std::ranges::sentinel_t<std::ranges::cache_latest_view<View>>;
+
+// Check that the to_input_view's iterator is default initializable when the underlying iterator is.
+// static_assert(std::default_initializable<CacheLatestViewSentinelT<DefaultInitializableIteratorView>>);
+// static_assert(!std::default_initializable<CacheLatestViewSentinelT<NonDefaultInitializableIteratorView>>);
+
+constexpr bool test() {
+  // {
+  //   CacheLatestViewSentinelT<DefaultInitializableIteratorView> it;
+  //   assert(*it == 0); // DefaultInitializableIterator has to be initialized to have value 0.
+  // }
+  // {
+  //   CacheLatestViewSentinelT<DefaultInitializableIteratorView> it = {};
+  //   assert(*it == 0); // DefaultInitializableIterator has to be initialized to have value 0.
+  // }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.cache.latest/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/size.pass.cpp
new file mode 100644
index 0000000000000..229e70c0fc361
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.cache.latest/size.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class cache_latest_view
+
+//    constexpr auto size() requires sized_range<V>;
+//    constexpr auto size() const requires sized_range<const V>;
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+
+template <class T>
+concept HasSize = requires(T t) { t.size(); };
+
+constexpr bool test() {
+  {
+    struct SubtractableIteratorsView : std::ranges::view_base {
+      forward_iterator<int*> begin();
+      sized_sentinel<forward_iterator<int*>> end();
+    };
+
+    using ToCacheLatestViewT = std::ranges::cache_latest_view<SubtractableIteratorsView>;
+
+    static_assert(std::ranges::sized_range<ToCacheLatestViewT&>);
+    static_assert(!std::ranges::range<const ToCacheLatestViewT&>); // no begin()/end()
+
+    static_assert(HasSize<ToCacheLatestViewT&>);
+    static_assert(HasSize<ToCacheLatestViewT&&>);
+    static_assert(!HasSize<const ToCacheLatestViewT&>);
+    static_assert(!HasSize<const ToCacheLatestViewT&&>);
+  }
+  {
+    struct NonSizedView : std::ranges::view_base {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+
+    using ToCacheLatestViewT = std::ranges::cache_latest_view<NonSizedView>;
+
+    static_assert(!HasSize<ToCacheLatestViewT&>);
+    static_assert(!HasSize<ToCacheLatestViewT&&>);
+    static_assert(!HasSize<const ToCacheLatestViewT&>);
+    static_assert(!HasSize<const ToCacheLatestViewT&&>);
+  }
+  {
+    struct SizedView : std::ranges::view_base {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+
+      int size() const;
+    };
+
+    using ToCacheLatestViewT = std::ranges::cache_latest_view<SizedView>;
+
+    static_assert(std::ranges::sized_range<ToCacheLatestViewT&>);
+    static_assert(!std::ranges::range<const ToCacheLatestViewT&>); // no begin()/end()
+
+    static_assert(HasSize<ToCacheLatestViewT&>);
+    static_assert(HasSize<ToCacheLatestViewT&&>);
+    static_assert(!HasSize<const ToCacheLatestViewT&>); // not a view, therefore no size()
+    static_assert(!HasSize<const ToCacheLatestViewT&&>);
+  }
+  {
+    // Test an empty view.
+    int arr[] = {94};
+    auto view = std::ranges::cache_latest_view(std::ranges::subrange(arr, arr));
+
+    assert(view.size() == 0);
+    assert(std::as_const(view).size() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int arr[] = {94};
+    auto view = std::ranges::cache_latest_view(std::ranges::subrange(arr, arr + 1));
+
+    assert(view.size() == 1);
+    assert(std::as_const(view).size() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> arr = {94, 82};
+    auto view              = std::ranges::cache_latest_view(std::move(arr));
+
+    assert(view.size() == 2);
+    assert(std::as_const(view).size() == 2);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..1aa8ee96aa7e2 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1120,6 +1120,11 @@ def add_version_header(tc):
             "values": {"c++23": 202207},
             "headers": ["ranges"],
         },
+        {
+            "name": "__cpp_lib_ranges_cache_latest",
+            "values": {"c++23": 202411},
+            "headers": ["ranges"],
+        },
         {
             "name": "__cpp_lib_ranges_chunk",
             "values": {"c++23": 202202},


From libcxx-commits at lists.llvm.org  Tue Jul 14 00:05:26 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 14 Jul 2026 00:05:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a55dfb6.6537ae93.2f1c1b.1329@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From 0f6ea409bc215182769114f254650bbacadd800e Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/11] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++---
 libcxx/include/__ranges/filter_view.h     | 20 +++++++++-----
 libcxx/include/__ranges/iota_view.h       | 15 ++++++-----
 libcxx/include/__ranges/istream_view.h    |  8 +++---
 libcxx/include/__ranges/join_view.h       |  6 +++--
 libcxx/include/__ranges/lazy_split_view.h | 32 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++-----
 libcxx/include/__ranges/take_view.h       |  6 +++--
 libcxx/include/__ranges/take_while_view.h |  8 +++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++----
 10 files changed, 86 insertions(+), 53 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 4612dc4a9ba1d..a6102e2ec853c 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 938dca24cc4fc..c67212020308b 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
+      return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
+    return __outer_iterator< __simple_view<_View> && __simple_view< _Pattern >>{*this, ranges::end(__base_)};
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -194,14 +204,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -284,12 +286,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -340,8 +346,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); }
     _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() &&
       requires forward_range<_View>
@@ -404,8 +408,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&,
-                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, range_value_t<_Range>)
+    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index f91dcfa95b9a5..b44f492061e63 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..ae351e76728ea 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From b365f83609a13f902187a543cf082677592c45dc Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/11] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From ea2ceacf489440eb6a38dc696a7c61d6a693057a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/11] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From b8134fcab352c1e2be0cef098f545379125760d2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/11] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From f352ec384ba79af09ad62230953d6dfc8d2ef7c1 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/11] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From efe86d4adbaaa2473986004c5d96b53f72683218 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/11] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From d20fe0ed69654a14a3e8633546791363bdf3f71b Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/11] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From 60f0fa514eaecac948c1dfd93d976d9f414dbfa1 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/11] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From 12de00c5c1e26f2abd9b43974ad0d3a71ece24c4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/11] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 00ed1b8397ea724a056dcd6993c40ee13b21fadc Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/11] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 79c4e1f418372..95a3696e1ce57 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,7 +40,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -48,7 +48,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -56,7 +56,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -82,7 +82,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index 4ff65be645220..359f5a6099fce 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From 7499890d07e2455f87175ab147cc2bf5ab1d29e5 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/11] Format

---
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 .../range.elements/iterator/base.pass.cpp     | 22 +++++++++----------
 .../range.elements/sentinel/base.pass.cpp     |  2 +-
 .../range.split/iterator/base.pass.cpp        | 10 ++++-----
 .../range.split/iterator/deref.pass.cpp       |  7 +++---
 .../sentinel/ctor.convert.pass.cpp            |  4 ++--
 6 files changed, 23 insertions(+), 24 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..82d72169d4b52 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 95a3696e1ce57..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,10 +22,9 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
-      { std::declval<T>().base() } noexcept;
-    };
+concept IsBaseNoexcept = requires {
+  { std::declval<T>().base() } noexcept;
+};
 
 using BaseView     = std::ranges::subrange<std::tuple<int>*>;
 using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<BaseView, 0>>;
@@ -41,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -56,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -65,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -82,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }


From libcxx-commits at lists.llvm.org  Tue Jul 14 00:05:34 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 14 Jul 2026 00:05:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][atomics] P2869R4: Remove
 Deprecated `shared_ptr` Atomic Access APIs from C++26 (PR #194544)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/194544@github.com>
Message-ID: <6a55dfbe.0c05e68e.1e82f1.09b5@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/194544

>From 23bd131298928b6eb3c699cbc404e745a805caba Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 28 Apr 2026 08:22:17 +0300
Subject: [PATCH] [libc++][atomics] P2869R4: Remove Deprecated `shared_ptr`
 Atomic Access APIs from C++26

Closes #105412

Also impelements the deprecations from: P0718R2 (https://wg21.link/p0718r2) (https://llvm.org/PR99980)

Reverts "[libc++] Undeprecate shared_ptr atomic access APIs (#92920)" commit 716ed5fccd2a960981fec2c5acb17292a1502435.
---
 libcxx/docs/ReleaseNotes/23.rst               |  3 ++
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 libcxx/docs/UserDocumentation.rst             |  4 +++
 libcxx/include/__memory/shared_ptr.h          | 29 ++++++++++------
 libcxx/include/memory                         | 22 ++++++------
 libcxx/modules/std/memory.inc                 |  6 ++++
 ...e_exchange_strong.depr_in_cxx20.verify.cpp | 32 +++++++++++++++++
 ...e_strong_explicit.depr_in_cxx20.verify.cpp | 34 +++++++++++++++++++
 ...are_exchange_weak.depr_in_cxx20.verify.cpp | 31 +++++++++++++++++
 ...nge_weak_explicit.depr_in_cxx20.verify.cpp | 34 +++++++++++++++++++
 .../atomic_exchange.depr_in_cxx20..verify.cpp | 29 ++++++++++++++++
 ...xchange_explicit.verify.depr_in_cxx20..cpp | 30 ++++++++++++++++
 ...omic_is_lock_free.depr_in_cxx20.verify.cpp | 28 +++++++++++++++
 .../atomic_load.depr_in_cxx20..verify.cpp     | 28 +++++++++++++++
 ...mic_load_explicit.depr_in_cxx20.verify.cpp | 29 ++++++++++++++++
 .../atomic_store.depr_in_cxx20.verify.cpp     | 28 +++++++++++++++
 ...ic_store_explicit.depr_in_cxx20.verify.cpp | 29 ++++++++++++++++
 .../atomic_compare_exchange_strong.pass.cpp   |  6 ++--
 ..._compare_exchange_strong_explicit.pass.cpp |  6 ++--
 .../atomic_compare_exchange_weak.pass.cpp     |  6 ++--
 ...ic_compare_exchange_weak_explicit.pass.cpp |  6 ++--
 .../atomic_exchange.pass.cpp                  |  6 ++--
 .../atomic_exchange_explicit.pass.cpp         |  6 ++--
 .../atomic_is_lock_free.pass.cpp              |  6 ++--
 .../atomic_load.pass.cpp                      |  6 ++--
 .../atomic_load_explicit.pass.cpp             |  6 ++--
 .../atomic_store.pass.cpp                     |  6 ++--
 .../atomic_store_explicit.pass.cpp            |  6 ++--
 28 files changed, 419 insertions(+), 45 deletions(-)
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange.depr_in_cxx20..verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange_explicit.verify.depr_in_cxx20..cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_is_lock_free.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load.depr_in_cxx20..verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load_explicit.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store_explicit.depr_in_cxx20.verify.cpp

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..a35f71b52d16b 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -75,6 +75,9 @@ Improvements and New Features
   ``-Wthread-safety`` no longer incorrectly reports the guarded data accessed inside the block as
   unprotected. This requires Clang 21 or later.
 
+- The ``_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS`` macro has been added to make the declarations in ``<memory>``
+  available.
+
 Deprecations and Removals
 -------------------------
 
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..e22117385bda2 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -48,7 +48,7 @@
 "","","","","","",""
 "`P2875R4 <https://wg21.link/P2875R4>`__","Undeprecate ``polymorphic_allocator::destroy`` for C++26","2024-03 (Tokyo)","|Complete|","15","`#105410 <https://github.com/llvm/llvm-project/issues/105410>`__",""
 "`P2867R2 <https://wg21.link/P2867R2>`__","Remove Deprecated ``strstreams`` >From C++26","2024-03 (Tokyo)","|Complete|","19","`#105411 <https://github.com/llvm/llvm-project/issues/105411>`__",""
-"`P2869R4 <https://wg21.link/P2869R4>`__","Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26","2024-03 (Tokyo)","","","`#105412 <https://github.com/llvm/llvm-project/issues/105412>`__",""
+"`P2869R4 <https://wg21.link/P2869R4>`__","Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26","2024-03 (Tokyo)","|Complete|","23","`#105412 <https://github.com/llvm/llvm-project/issues/105412>`__",""
 "`P2872R3 <https://wg21.link/P2872R3>`__","Remove ``wstring_convert`` From C++26","2024-03 (Tokyo)","|Complete|","19","`#105413 <https://github.com/llvm/llvm-project/issues/105413>`__",""
 "`P3107R5 <https://wg21.link/P3107R5>`__","Permit an efficient implementation of ``std::print``","2024-03 (Tokyo)","","","`#105414 <https://github.com/llvm/llvm-project/issues/105414>`__",""
 "`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)"
diff --git a/libcxx/docs/UserDocumentation.rst b/libcxx/docs/UserDocumentation.rst
index 415a599168374..cf568e6afc91f 100644
--- a/libcxx/docs/UserDocumentation.rst
+++ b/libcxx/docs/UserDocumentation.rst
@@ -224,6 +224,9 @@ C++26 Specific Configuration Macros
 **_LIBCPP_ENABLE_CXX26_REMOVED_CODECVT**:
   This macro is used to re-enable all named declarations in ``<codecvt>``.
 
+**_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS**:
+  This macro is used to re-enable all ``shared_ptr`` atomic access APIs in ``<memory>``.
+
 **_LIBCPP_ENABLE_CXX26_REMOVED_STRING_RESERVE**:
   This macro is used to re-enable the function
   ``std::basic_string<...>::reserve()``.
@@ -235,6 +238,7 @@ C++26 Specific Configuration Macros
   This macro is used to re-enable the ``wstring_convert`` and ``wbuffer_convert``
   in ``<locale>``.
 
+
 Libc++ Extensions
 =================
 
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index ebd542ba14d01..91348ea0b3a4d 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -1381,13 +1381,15 @@ class _LIBCPP_EXPORTED_FROM_ABI __sp_mut {
 
 _LIBCPP_EXPORTED_FROM_ABI __sp_mut& __get_sp_mut(const void*);
 
+#  if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS)
+
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI bool atomic_is_lock_free(const shared_ptr<_Tp>*) {
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool atomic_is_lock_free(const shared_ptr<_Tp>*) {
   return false;
 }
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> atomic_load(const shared_ptr<_Tp>* __p) {
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> atomic_load(const shared_ptr<_Tp>* __p) {
   __sp_mut& __m = std::__get_sp_mut(__p);
   __m.lock();
   shared_ptr<_Tp> __q = *__p;
@@ -1396,12 +1398,13 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> atomic_load(const shared_ptr<_Tp>* __p) {
 }
 
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> atomic_load_explicit(const shared_ptr<_Tp>* __p, memory_order) {
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
+atomic_load_explicit(const shared_ptr<_Tp>* __p, memory_order) {
   return std::atomic_load(__p);
 }
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI void atomic_store(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) {
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI void atomic_store(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) {
   __sp_mut& __m = std::__get_sp_mut(__p);
   __m.lock();
   __p->swap(__r);
@@ -1409,12 +1412,14 @@ _LIBCPP_HIDE_FROM_ABI void atomic_store(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __
 }
 
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI void atomic_store_explicit(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r, memory_order) {
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI void
+atomic_store_explicit(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r, memory_order) {
   std::atomic_store(__p, __r);
 }
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> atomic_exchange(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) {
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
+atomic_exchange(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) {
   __sp_mut& __m = std::__get_sp_mut(__p);
   __m.lock();
   __p->swap(__r);
@@ -1423,13 +1428,13 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> atomic_exchange(shared_ptr<_Tp>* __p, shar
 }
 
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
 atomic_exchange_explicit(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r, memory_order) {
   return std::atomic_exchange(__p, __r);
 }
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI bool
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool
 atomic_compare_exchange_strong(shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, shared_ptr<_Tp> __w) {
   shared_ptr<_Tp> __temp;
   __sp_mut& __m = std::__get_sp_mut(__p);
@@ -1447,23 +1452,25 @@ atomic_compare_exchange_strong(shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, share
 }
 
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI bool
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool
 atomic_compare_exchange_weak(shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, shared_ptr<_Tp> __w) {
   return std::atomic_compare_exchange_strong(__p, __v, __w);
 }
 
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI bool atomic_compare_exchange_strong_explicit(
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool atomic_compare_exchange_strong_explicit(
     shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, shared_ptr<_Tp> __w, memory_order, memory_order) {
   return std::atomic_compare_exchange_strong(__p, __v, __w);
 }
 
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI bool atomic_compare_exchange_weak_explicit(
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool atomic_compare_exchange_weak_explicit(
     shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, shared_ptr<_Tp> __w, memory_order, memory_order) {
   return std::atomic_compare_exchange_weak(__p, __v, __w);
 }
 
+#  endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS)
+
 #endif // _LIBCPP_HAS_THREADS
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/include/memory b/libcxx/include/memory
index d5d48ed9a9e6d..fddb9743e9e01 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -831,34 +831,34 @@ public:
 };
 
 template<class T>
-    bool atomic_is_lock_free(const shared_ptr<T>* p);
+    bool atomic_is_lock_free(const shared_ptr<T>* p);                                     // Deprecated in C++20, removed in C++26
 template<class T>
-    shared_ptr<T> atomic_load(const shared_ptr<T>* p);
+    shared_ptr<T> atomic_load(const shared_ptr<T>* p);                                    // Deprecated in C++20, removed in C++26
 template<class T>
-    shared_ptr<T> atomic_load_explicit(const shared_ptr<T>* p, memory_order mo);
+    shared_ptr<T> atomic_load_explicit(const shared_ptr<T>* p, memory_order mo);          // Deprecated in C++20, removed in C++26
 template<class T>
-    void atomic_store(shared_ptr<T>* p, shared_ptr<T> r);
+    void atomic_store(shared_ptr<T>* p, shared_ptr<T> r);                                 // Deprecated in C++20, removed in C++26
 template<class T>
-    void atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo);
+    void atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo);       // Deprecated in C++20, removed in C++26
 template<class T>
-    shared_ptr<T> atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r);
+    shared_ptr<T> atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r);                     // Deprecated in C++20, removed in C++26
 template<class T>
     shared_ptr<T>
-    atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo);
+    atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo);         // Deprecated in C++20, removed in C++26
 template<class T>
     bool
-    atomic_compare_exchange_weak(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w);
+    atomic_compare_exchange_weak(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w);    // Deprecated in C++20, removed in C++26
 template<class T>
     bool
-    atomic_compare_exchange_strong( shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w);
+    atomic_compare_exchange_strong( shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w); // Deprecated in C++20, removed in C++26
 template<class T>
     bool
-    atomic_compare_exchange_weak_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
+    atomic_compare_exchange_weak_explicit(shared_ptr<T>* p, shared_ptr<T>* v,             // Deprecated in C++20, removed in C++26
                                           shared_ptr<T> w, memory_order success,
                                           memory_order failure);
 template<class T>
     bool
-    atomic_compare_exchange_strong_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
+    atomic_compare_exchange_strong_explicit(shared_ptr<T>* p, shared_ptr<T>* v,           // Deprecated in C++20, removed in C++26
                                             shared_ptr<T> w, memory_order success,
                                             memory_order failure);
 // Hash support
diff --git a/libcxx/modules/std/memory.inc b/libcxx/modules/std/memory.inc
index c25e9e3443e9c..f25611f33e442 100644
--- a/libcxx/modules/std/memory.inc
+++ b/libcxx/modules/std/memory.inc
@@ -195,6 +195,9 @@ export namespace std {
 #endif // _LIBCPP_STD_VER >= 23
 
 #if _LIBCPP_HAS_THREADS
+
+#  if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS)
+
   // [depr.util.smartptr.shared.atomic]
   using std::atomic_is_lock_free;
 
@@ -211,5 +214,8 @@ export namespace std {
   using std::atomic_compare_exchange_strong_explicit;
   using std::atomic_compare_exchange_weak;
   using std::atomic_compare_exchange_weak_explicit;
+
+#  endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS)
+
 #endif // _LIBCPP_HAS_THREADS
 } // namespace std
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..3ca01cad451a2
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong.depr_in_cxx20.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// bool
+// atomic_compare_exchange_strong(shared_ptr<T>* p, shared_ptr<T>* v,
+//                                shared_ptr<T> w);                     // Deprecated in C++20, removed in C++26
+
+#include <atomic>
+#include <memory>
+#include <tuple>
+
+void test() {
+  std::shared_ptr<int> p(new int(4));
+  std::shared_ptr<int> v(new int(3));
+  std::shared_ptr<int> w(new int(2));
+  // expected-warning at +1 {{'atomic_compare_exchange_strong<int>' is deprecated}}
+  std::ignore = std::atomic_compare_exchange_strong(&p, &v, w);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..93384772c8afc
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.depr_in_cxx20.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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// bool
+// atomic_compare_exchange_strong_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
+//                                         shared_ptr<T> w, memory_order success,
+//                                         memory_order failure);                 // Deprecated in C++20, removed in C++26
+
+#include <atomic>
+#include <memory>
+#include <tuple>
+
+void test() {
+  std::shared_ptr<int> p(new int(4));
+  std::shared_ptr<int> v(new int(3));
+  std::shared_ptr<int> w(new int(2));
+  // expected-warning at +2 {{'atomic_compare_exchange_strong_explicit<int>' is deprecated}}
+  std::ignore =
+      std::atomic_compare_exchange_strong_explicit(&p, &v, w, std::memory_order_seq_cst, std::memory_order_seq_cst);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..30ca70523fb64
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak.depr_in_cxx20.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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// bool
+// atomic_compare_exchange_weak(shared_ptr<T>* p, shared_ptr<T>* v,
+//                              shared_ptr<T> w);                    // Deprecated in C++20, removed in C++26
+
+#include <memory>
+#include <tuple>
+
+int main(int, char**) {
+  std::shared_ptr<int> p(new int(4));
+  std::shared_ptr<int> v(new int(3));
+  std::shared_ptr<int> w(new int(2));
+  // expected-warning at +1 {{'atomic_compare_exchange_weak<int>' is deprecated}}
+  std::ignore = std::atomic_compare_exchange_weak(&p, &v, w);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..ae6e98d0692d2
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.depr_in_cxx20.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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// bool
+// atomic_compare_exchange_weak_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
+//                                       shared_ptr<T> w, memory_order success,
+//                                       memory_order failure);                 // Deprecated in C++20, removed in C++26
+
+#include <atomic>
+#include <memory>
+#include <tuple>
+
+void test() {
+  std::shared_ptr<int> p(new int(4));
+  std::shared_ptr<int> v(new int(3));
+  std::shared_ptr<int> w(new int(2));
+  // expected-warning at +2 {{'atomic_compare_exchange_weak_explicit<int>' is deprecated}}
+  std::ignore =
+      std::atomic_compare_exchange_weak_explicit(&p, &v, w, std::memory_order_seq_cst, std::memory_order_seq_cst);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange.depr_in_cxx20..verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange.depr_in_cxx20..verify.cpp
new file mode 100644
index 0000000000000..0e4bc1bc50b18
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange.depr_in_cxx20..verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// shared_ptr<T>
+// atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r)   // Deprecated in C++20, removed in C++26
+
+#include <memory>
+#include <tuple>
+
+void test() {
+  std::shared_ptr<int> p(new int(4));
+  std::shared_ptr<int> r(new int(3));
+  // expected-warning at +1 {{'atomic_exchange<int>' is deprecated}}
+  std::ignore = std::atomic_exchange(&p, r);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange_explicit.verify.depr_in_cxx20..cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange_explicit.verify.depr_in_cxx20..cpp
new file mode 100644
index 0000000000000..5a6a2bfe4c1e2
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange_explicit.verify.depr_in_cxx20..cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// shared_ptr<T>
+// atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r)  // Deprecated in C++20, removed in C++26
+
+#include <atomic>
+#include <memory>
+#include <tuple>
+
+void test() {
+  std::shared_ptr<int> p(new int(4));
+  std::shared_ptr<int> r(new int(3));
+  // expected-warning at +1 {{'atomic_exchange_explicit<int>' is deprecated}}
+  std::ignore = std::atomic_exchange_explicit(&p, r, std::memory_order_seq_cst);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_is_lock_free.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_is_lock_free.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..6799d31ce1fa9
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_is_lock_free.depr_in_cxx20.verify.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template<class T>
+// bool
+// atomic_is_lock_free(const shared_ptr<T>* p);    // Deprecated in C++20, removed in C++26
+
+#include <memory>
+#include <tuple>
+
+void test() {
+  const std::shared_ptr<int> p(new int(3));
+  // expected-warning at +1 {{'atomic_is_lock_free<int>' is deprecated}}
+  std::ignore = std::atomic_is_lock_free(&p);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load.depr_in_cxx20..verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load.depr_in_cxx20..verify.cpp
new file mode 100644
index 0000000000000..768ad0ded983e
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load.depr_in_cxx20..verify.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// shared_ptr<T>
+// atomic_load(const shared_ptr<T>* p)      // Deprecated in C++20, removed in C++26
+
+#include <memory>
+#include <tuple>
+
+void test() {
+  std::shared_ptr<int> p(new int(3));
+  // expected-warning at +1 {{'atomic_load<int>' is deprecated}}
+  std::ignore = std::atomic_load(&p);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load_explicit.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load_explicit.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..dc56193324f04
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load_explicit.depr_in_cxx20.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// shared_ptr<T>
+// atomic_load_explicit(const shared_ptr<T>* p, memory_order mo)     // Deprecated in C++20, removed in C++26
+
+#include <atomic>
+#include <memory>
+#include <tuple>
+
+void test() {
+  const std::shared_ptr<int> p(new int(3));
+  // expected-warning at +1 {{'atomic_load_explicit<int>' is deprecated}}
+  std::ignore = std::atomic_load_explicit(&p, std::memory_order_relaxed);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..38b8df6f24393
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store.depr_in_cxx20.verify.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// void
+// atomic_store(shared_ptr<T>* p, shared_ptr<T> r)   // Deprecated in C++20, removed in C++26
+
+#include <memory>
+
+void test() {
+  std::shared_ptr<int> p;
+  std::shared_ptr<int> r(new int(3));
+  // expected-warning at +1 {{'atomic_store<int>' is deprecated}}
+  std::atomic_store(&p, r);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store_explicit.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store_explicit.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..18f59c4618699
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store_explicit.depr_in_cxx20.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// void
+// atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo)  // Deprecated in C++20, removed in C++26
+
+#include <atomic>
+#include <memory>
+
+void test() {
+  std::shared_ptr<int> p;
+  std::shared_ptr<int> r(new int(3));
+  // expected-warning at +1 {{'atomic_store_explicit<int>' is deprecated}}
+  std::atomic_store_explicit(&p, r, std::memory_order_seq_cst);
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong.pass.cpp
index 37be6ceea3e09..ad9d2a9d9ccc0 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -15,7 +17,7 @@
 // template <class T>
 // bool
 // atomic_compare_exchange_strong(shared_ptr<T>* p, shared_ptr<T>* v,
-//                                shared_ptr<T> w);
+//                                shared_ptr<T> w);                     // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.pass.cpp
index 3965863b86ccf..26496df96d472 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -16,7 +18,7 @@
 // bool
 // atomic_compare_exchange_strong_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
 //                                         shared_ptr<T> w, memory_order success,
-//                                         memory_order failure);
+//                                         memory_order failure);                 // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak.pass.cpp
index 6dd04f924a105..5cdc0ea3312ff 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -15,7 +17,7 @@
 // template <class T>
 // bool
 // atomic_compare_exchange_weak(shared_ptr<T>* p, shared_ptr<T>* v,
-//                              shared_ptr<T> w);
+//                              shared_ptr<T> w);                    // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.pass.cpp
index 4837fa9793a5e..e4cc4cf77a64e 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -16,7 +18,7 @@
 // bool
 // atomic_compare_exchange_weak_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
 //                                       shared_ptr<T> w, memory_order success,
-//                                       memory_order failure);
+//                                       memory_order failure);                 // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange.pass.cpp
index f488e0ed1d14e..db02c9d9f75b4 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template <class T>
 // shared_ptr<T>
-// atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r)
+// atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r)   // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange_explicit.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange_explicit.pass.cpp
index 1945f7bba6dc0..704232e71fa4a 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange_explicit.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange_explicit.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template <class T>
 // shared_ptr<T>
-// atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r)
+// atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r)  // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_is_lock_free.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_is_lock_free.pass.cpp
index 37f7d12eda2c6..8f3b5bbb51558 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_is_lock_free.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_is_lock_free.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template<class T>
 // bool
-// atomic_is_lock_free(const shared_ptr<T>* p);
+// atomic_is_lock_free(const shared_ptr<T>* p);    // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load.pass.cpp
index 1b9a15ac92ac1..9d81e81b4392b 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template <class T>
 // shared_ptr<T>
-// atomic_load(const shared_ptr<T>* p)
+// atomic_load(const shared_ptr<T>* p)      // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load_explicit.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load_explicit.pass.cpp
index 5c2970133328f..f4875dce38a81 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load_explicit.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load_explicit.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template <class T>
 // shared_ptr<T>
-// atomic_load_explicit(const shared_ptr<T>* p, memory_order mo)
+// atomic_load_explicit(const shared_ptr<T>* p, memory_order mo)     // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store.pass.cpp
index 5b7bd5fad69c4..836dcb16ff32e 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template <class T>
 // void
-// atomic_store(shared_ptr<T>* p, shared_ptr<T> r)
+// atomic_store(shared_ptr<T>* p, shared_ptr<T> r)   // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store_explicit.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store_explicit.pass.cpp
index 5712190421308..85b31c017d736 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store_explicit.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store_explicit.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template <class T>
 // void
-// atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo)
+// atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo)  // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 


From libcxx-commits at lists.llvm.org  Tue Jul 14 00:05:58 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 14 Jul 2026 00:05:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges]
	`std::views::as_input` (PR #151449)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/151449@github.com>
Message-ID: <6a55dfd6.a1accbb4.244acd.6be0@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/151449

>From c0b710c803807bdfaa656dd4263207e8dd5019ea Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 9 Jul 2025 19:25:52 +0300
Subject: [PATCH] [libc++][ranges] P3137R3: `views::to_input`

Implements std::views::as_input:

- https://wg21.link/P3137R3 P3137R3: views::to_input
- https://wg21.link/P3828R1 P3828R1: Rename the to_input view to as_input

Closes #127873
Closes #189602

References:

- https://wg21.link/range.as.input
- https://eel.is/c++draft/range.as.input
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/ReleaseNotes/23.rst               |   2 +
 libcxx/docs/Status/Cxx26Papers.csv            |   4 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/as_input_view.h       | 226 ++++++++++++++
 libcxx/include/module.modulemap.in            |   3 +
 libcxx/include/ranges                         |  12 +
 libcxx/include/version                        |   2 +
 libcxx/modules/std/ranges.inc                 |  10 +
 .../range.as.input/nodiscard.verify.cpp       |  85 ++++++
 .../ranges.version.compile.pass.cpp           |  27 ++
 .../version.version.compile.pass.cpp          |  27 ++
 .../cpo.compile.pass.cpp                      |   2 +-
 .../range.as.input/adaptor.pass.cpp           | 278 ++++++++++++++++++
 .../range.as.input/base.pass.cpp              |  87 ++++++
 .../range.as.input/begin.pass.cpp             |  27 ++
 .../range.as.input/ctad.compile.pass.cpp      |  32 ++
 .../range.as.input/ctor.default.pass.cpp      |  83 ++++++
 .../range.as.input/ctor.view.pass.cpp         |  26 ++
 .../enable_borrowed_range.compile.pass.cpp    |  39 +++
 .../range.as.input/end.pass.cpp               |  26 ++
 .../range.as.input/general.pass.cpp           |  69 +++++
 .../range.as.input/iterator/base.pass.cpp     |  34 +++
 .../iterator/ctor.converting.pass.cpp         |  34 +++
 .../iterator/ctor.default.pass.cpp            |  81 +++++
 .../iterator/ctor.move.pass.cpp               |  34 +++
 .../iterator/iter_move.pass.cpp               |  34 +++
 .../iterator/iter_swap.pass.cpp               |  35 +++
 .../range.as.input/iterator/op.deref.pass.cpp |  33 +++
 .../range.as.input/iterator/op.eq.pass.cpp    |  33 +++
 .../iterator/op.increment.pass.cpp            |  34 +++
 .../range.as.input/iterator/op.minus.pass.cpp |  36 +++
 .../range.as.input/size.pass.cpp              | 111 +++++++
 .../generate_feature_test_macro_components.py |   5 +
 34 files changed, 1571 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/include/__ranges/as_input_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.as.input/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.view.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/enable_borrowed_range.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.converting.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.move.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_move.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_swap.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.eq.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.minus.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/size.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..72d72a514dcff 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -510,6 +510,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_philox_engine``                                *unimplemented*
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_ranges_as_input``                              ``202502L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_concat``                                ``202403L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_indices``                               ``202506L``
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..436403af2e7dd 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -48,6 +48,8 @@ Implemented Papers
 - P2781R9: ``std::constant_wrapper`` (`Github <https://llvm.org/PR148179>`__)
 - P3978R3: ``constant_wrapper`` should unwrap on call and subscript (`Github <https://llvm.org/PR189605>`__)
 - P2164R9: ``views::enumerate`` (`Github <https://llvm.org/PR105251>`__)
+- P3137R3: ``views::as_input`` (`Github <https://llvm.org/PR127873>`__)
+- P3828R1: Rename the ``as_input`` view to ``as_input`` (`Github <https://llvm.org/PR189602>`__)
 - P2322R6: ``ranges::fold`` (`Github <https://llvm.org/PR105208>`__)
 - P4144R1: Remove ``span``'s ``initializer_list`` constructor for C++26 (`Github <https://llvm.org/PR189612>`__)
 - P2542R8: ``views::concat`` (`Github <https://llvm.org/PR105419>`__)
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..3c5f2c8a41439 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -101,7 +101,7 @@
 "`P2900R14 <https://wg21.link/P2900R14>`__","Contracts for C++","2025-02 (Hagenberg)","","","`#127870 <https://github.com/llvm/llvm-project/issues/127870>`__",""
 "`P3475R2 <https://wg21.link/P3475R2>`__","Defang and deprecate ``memory_order::consume``","2025-02 (Hagenberg)","","","`#127871 <https://github.com/llvm/llvm-project/issues/127871>`__",""
 "`P2786R13 <https://wg21.link/P2786R13>`__","Trivial Relocatability For C++26","2025-02 (Hagenberg)","|Nothing To Do|","","`#127872 <https://github.com/llvm/llvm-project/issues/127872>`__","Reverted by `P3920R0 <https://wg21.link/P3920R0>`__"
-"`P3137R3 <https://wg21.link/P3137R3>`__","``views::to_input``","2025-02 (Hagenberg)","","","`#127873 <https://github.com/llvm/llvm-project/issues/127873>`__",""
+"`P3137R3 <https://wg21.link/P3137R3>`__","``views::to_input``","2025-02 (Hagenberg)","|Complete|","23","`#127873 <https://github.com/llvm/llvm-project/issues/127873>`__",""
 "`P0472R3 <https://wg21.link/P0472R3>`__","Put ``std::monostate`` in ``<utility>``","2025-02 (Hagenberg)","|Complete|","21","`#127874 <https://github.com/llvm/llvm-project/issues/127874>`__",""
 "`P3349R1 <https://wg21.link/P3349R1>`__","Converting contiguous iterators to pointers","2025-02 (Hagenberg)","","","`#127875 <https://github.com/llvm/llvm-project/issues/127875>`__",""
 "`P3372R3 <https://wg21.link/P3372R3>`__","constexpr containers and adaptors","2025-02 (Hagenberg)","|In Progress|","","`#127876 <https://github.com/llvm/llvm-project/issues/127876>`__",""
@@ -190,7 +190,7 @@
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
 "`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
-"`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
+"`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","|Complete|","23","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
 "`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","","","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..97a3cbd8722fe 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -723,6 +723,7 @@ set(files
   __ranges/adjacent_transform_view.h
   __ranges/adjacent_view.h
   __ranges/all.h
+  __ranges/as_input_view.h
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
   __ranges/common_view.h
diff --git a/libcxx/include/__ranges/as_input_view.h b/libcxx/include/__ranges/as_input_view.h
new file mode 100644
index 0000000000000..235a7eea5c340
--- /dev/null
+++ b/libcxx/include/__ranges/as_input_view.h
@@ -0,0 +1,226 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_AS_INPUT_VIEW_H
+#define _LIBCPP___RANGES_AS_INPUT_VIEW_H
+
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/maybe_const.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+namespace ranges {
+
+// [range.as.input.view]
+
+template <input_range _View>
+  requires view<_View>
+class as_input_view : public view_interface<as_input_view<_View>> {
+  _View __base_ = _View();
+
+  // [range.as.input.iterator], class template as_input_view::iterator
+  template <bool _Const>
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI as_input_view()
+    requires default_initializable<_View>
+  = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit as_input_view(_View __base) : __base_(std::move(__base)) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>{ranges::begin(__base_)};
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires range<const _View>
+  {
+    return __iterator<true>{ranges::begin(__base_)};
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    return ranges::end(__base_);
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires range<const _View>
+  {
+    return ranges::end(__base_);
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return ranges::size(__base_);
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return ranges::size(__base_);
+  }
+
+  // TODO: Implement when P2846R6 is available.
+  // constexpr auto reserve_hint()
+  //   requires approximately_sized_range<_View>
+  // {
+  //   return ranges::reserve_hint(__base_);
+  // }
+  // constexpr auto reserve_hint() const
+  //   requires approximately_sized_range<const _View>
+  // {
+  //   return ranges::reserve_hint(__base_);
+  // }
+};
+
+template <class _Range>
+as_input_view(_Range&&) -> as_input_view<views::all_t<_Range>>;
+
+// [range.to.input.iterator]
+
+template <input_range _View>
+  requires view<_View>
+template <bool _Const>
+class as_input_view<_View>::__iterator {
+  using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>; // exposition only
+
+  iterator_t<_Base> __current_ = iterator_t<_Base>(); // exposition only
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current)
+      : __current_(std::move(__current)) {} // exposition only
+
+  friend class as_input_view<_View>;
+
+public:
+  using difference_type  = range_difference_t<_Base>;
+  using value_type       = range_value_t<_Base>;
+  using iterator_concept = input_iterator_tag;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator()
+    requires default_initializable<iterator_t<_Base>>
+  = default;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&)            = default;
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator=(__iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
+      : __current_(std::move(__i.__current_)) {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__current_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    ++__current_;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const sentinel_t<_Base>& __y) {
+    return __x.__current_ == __y;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const sentinel_t<_Base>& __y, const __iterator& __x)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return __y - __x.__current_;
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const sentinel_t<_Base>& __y)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return __x.__current_ - __y;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_Base> _LIBCPP_HIDE_FROM_ABI
+  iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(__i.__current_))) {
+    return ranges::iter_move(__i.__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void
+  iter_swap(const __iterator& __x,
+            const __iterator& __y) noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_)))
+    requires indirectly_swappable<iterator_t<_Base>>
+  {
+    ranges::iter_swap(__x.__current_, __y.__current_);
+  }
+};
+
+template <class _View>
+constexpr bool enable_borrowed_range<as_input_view<_View>> = enable_borrowed_range<_View>;
+
+namespace views {
+namespace __as_input_view {
+
+struct __fn : __range_adaptor_closure<__fn> {
+  template <input_range _Range>
+    requires(!common_range<_Range> && !forward_range<_Range>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range) noexcept(noexcept(views::all(std::forward<_Range>(__range))))
+      -> decltype(/*--------------------------*/ views::all(std::forward<_Range>(__range))) {
+    return /*---------------------------------*/ views::all(std::forward<_Range>(__range));
+  }
+
+  template <class _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range) noexcept(noexcept(as_input_view(std::forward<_Range>(__range))))
+      -> decltype(/*--------------------------*/ as_input_view(std::forward<_Range>(__range))) {
+    return /*---------------------------------*/ as_input_view(std::forward<_Range>(__range));
+  }
+};
+
+} // namespace __as_input_view
+
+inline namespace __cpo {
+inline constexpr auto as_input = __as_input_view::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_AS_INPUT_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..c0e73f7896379 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1979,6 +1979,9 @@ module std {
       header "__ranges/to.h"
       export std.functional.bind_back
     }
+    module as_input_view {
+      header "__ranges/as_input_view.h"
+    }
     module transform_view {
       header "__ranges/transform_view.h"
       export std.functional.bind_back
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..d6a808fddb426 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -428,6 +428,17 @@ namespace std::ranges {
       enable_borrowed_range<V>;                                            // C++23
 
   namespace views { inline constexpr unspecified stride = unspecified; }   // C++23
+
+  // [range.to.input], to input view
+  template<input_range V>
+    requires view<V>
+  class as_input_view;                                                      // C++26
+
+  template<class V>
+    constexpr bool enable_borrowed_range<as_input_view<V>> =
+      enable_borrowed_range<V>;                                             // C++26
+
+  namespace views { inline constexpr unspecified as_input = unspecified; }  // C++26
 }
 
 namespace std {
@@ -525,6 +536,7 @@ namespace std {
 #  endif
 
 #  if _LIBCPP_STD_VER >= 26
+#    include <__ranges/as_input_view.h>
 #    include <__ranges/concat_view.h>
 #  endif
 
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..72d9dedb96fe4 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -207,6 +207,7 @@ __cpp_lib_ranges                                        202406L <algorithm> <fun
                                                                 <memory> <ranges>
                                                         202110L // C++20
 __cpp_lib_ranges_as_const                               202207L <ranges>
+__cpp_lib_ranges_as_input                               202502L <ranges>
 __cpp_lib_ranges_as_rvalue                              202207L <ranges>
 __cpp_lib_ranges_chunk                                  202202L <ranges>
 __cpp_lib_ranges_chunk_by                               202202L <ranges>
@@ -619,6 +620,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # undef  __cpp_lib_out_ptr
 # define __cpp_lib_out_ptr                              202311L
 // # define __cpp_lib_philox_engine                        202406L
+# define __cpp_lib_ranges_as_input                      202502L
 # define __cpp_lib_ranges_concat                        202403L
 # define __cpp_lib_ranges_indices                       202506L
 # define __cpp_lib_ratio                                202306L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..e4ca590edce8f 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -372,6 +372,16 @@ export namespace std {
       using std::ranges::views::enumerate;
     }
 #endif // _LIBCPP_STD_VER >= 23
+
+#if _LIBCPP_STD_VER >= 26
+    // [range.to.input], to input view
+    using std::ranges::as_input_view;
+
+    namespace views {
+      using std::ranges::views::as_input;
+    }
+#endif
+
   } // namespace ranges
 
   namespace views = ranges::views;
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as.input/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as.input/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..4235b1e782d15
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as.input/nodiscard.verify.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//  template<input_range V>
+//    requires view<V>
+//  class as_input_view : public view_interface<as_input_view<V>>
+
+// Check that functions are marked [[nodiscard]]
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_iterators.h"
+
+void test() {
+  std::vector<int> range;
+
+  // [range.to.input.view]
+
+  auto v = std::views::as_input(range);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).size();
+
+  { // [range.to.input.iterator]
+
+    auto it = v.begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::move(it).base();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(it).base();
+
+    auto st = v.end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    st - it;
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - st;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    iter_move(it);
+  }
+
+  // [range.to.input.overview]
+
+  {
+    struct NonCommonRange {
+      auto begin() { return cpp17_input_iterator<int*>{nullptr}; };
+      auto end() { return sentinel_wrapper<cpp17_input_iterator<int*>>{begin()}; };
+    } nonCommonRange;
+    static_assert(std::ranges::input_range<NonCommonRange>);
+    static_assert(!std::ranges::common_range<NonCommonRange>);
+    static_assert(!std::ranges::forward_range<NonCommonRange>);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::as_input(nonCommonRange);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::as_input(range);
+  }
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 670b0e664721a..b3063599f2bad 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -32,6 +32,10 @@
 #    error "__cpp_lib_ranges_as_const should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_as_input
+#    error "__cpp_lib_ranges_as_input should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_ranges_as_rvalue
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
@@ -94,6 +98,10 @@
 #    error "__cpp_lib_ranges_as_const should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_as_input
+#    error "__cpp_lib_ranges_as_input should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_ranges_as_rvalue
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
@@ -156,6 +164,10 @@
 #    error "__cpp_lib_ranges_as_const should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_as_input
+#    error "__cpp_lib_ranges_as_input should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_ranges_as_rvalue
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
@@ -221,6 +233,10 @@
 #    error "__cpp_lib_ranges_as_const should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_as_input
+#    error "__cpp_lib_ranges_as_input should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_ranges_as_rvalue
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
@@ -295,6 +311,10 @@
 #    endif
 #  endif
 
+#  ifdef __cpp_lib_ranges_as_input
+#    error "__cpp_lib_ranges_as_input should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_ranges_as_rvalue
 #    error "__cpp_lib_ranges_as_rvalue should be defined in c++23"
 #  endif
@@ -420,6 +440,13 @@
 #    endif
 #  endif
 
+#  ifndef __cpp_lib_ranges_as_input
+#    error "__cpp_lib_ranges_as_input should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_as_input != 202502L
+#    error "__cpp_lib_ranges_as_input should have the value 202502L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_ranges_as_rvalue
 #    error "__cpp_lib_ranges_as_rvalue should be defined in c++26"
 #  endif
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 f2d74cc99d864..cc6fe3836f531 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
@@ -660,6 +660,10 @@
 #    error "__cpp_lib_ranges_as_const should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_as_input
+#    error "__cpp_lib_ranges_as_input should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_ranges_as_rvalue
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
@@ -1636,6 +1640,10 @@
 #    error "__cpp_lib_ranges_as_const should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_as_input
+#    error "__cpp_lib_ranges_as_input should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_ranges_as_rvalue
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
@@ -2777,6 +2785,10 @@
 #    error "__cpp_lib_ranges_as_const should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_as_input
+#    error "__cpp_lib_ranges_as_input should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_ranges_as_rvalue
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
@@ -4185,6 +4197,10 @@
 #    error "__cpp_lib_ranges_as_const should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_ranges_as_input
+#    error "__cpp_lib_ranges_as_input should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_ranges_as_rvalue
 #    error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
 #  endif
@@ -5767,6 +5783,10 @@
 #    endif
 #  endif
 
+#  ifdef __cpp_lib_ranges_as_input
+#    error "__cpp_lib_ranges_as_input should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_ranges_as_rvalue
 #    error "__cpp_lib_ranges_as_rvalue should be defined in c++23"
 #  endif
@@ -7718,6 +7738,13 @@
 #    endif
 #  endif
 
+#  ifndef __cpp_lib_ranges_as_input
+#    error "__cpp_lib_ranges_as_input should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_as_input != 202502L
+#    error "__cpp_lib_ranges_as_input should have the value 202502L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_ranges_as_rvalue
 #    error "__cpp_lib_ranges_as_rvalue should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index 71a0b6ebb804c..6f9f2f52ea647 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -137,6 +137,6 @@ static_assert(test(std::views::zip, a, a));
 
 #if TEST_STD_VER >= 26
 // static_assert(test(std::views::cache_latest, a));
+static_assert(test(std::views::as_input, a));
 static_assert(test(std::views::concat, a, a));
-// static_assert(test(std::views::to_input, a));
 #endif
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/adaptor.pass.cpp
new file mode 100644
index 0000000000000..da36b4d862826
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/adaptor.pass.cpp
@@ -0,0 +1,278 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//  template<input_range V>
+//    requires view<V>
+//  class as_input_view : public view_interface<as_input_view<V>>
+
+// [range.to.input.overview]
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <ranges>
+#include <vector>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct NonView {};
+
+static_assert(std::default_initializable<NonView>);
+
+struct DefaultInitializableView : std::ranges::view_base {
+  int i_;
+
+  int* begin();
+  int* end();
+};
+
+static_assert(std::default_initializable<DefaultInitializableView>);
+static_assert(std::ranges::common_range<DefaultInitializableView>);
+static_assert(std::ranges::input_range<DefaultInitializableView>);
+
+struct CommonView : std::ranges::view_base {
+  int i_;
+
+  constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&i_); }
+  constexpr forward_iterator<int*> end() { return begin(); }
+};
+
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::ranges::forward_range<CommonView>);
+static_assert(std::ranges::input_range<CommonView>);
+
+struct NonCommonView : std::ranges::view_base {
+  int i_;
+
+  constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&i_); }
+  constexpr sentinel_wrapper<forward_iterator<int*>> end() { return sentinel_wrapper<forward_iterator<int*>>(begin()); }
+};
+
+static_assert(!std::ranges::common_range<NonCommonView>);
+static_assert(std::ranges::forward_range<NonCommonView>);
+static_assert(std::ranges::input_range<NonCommonView>);
+static_assert(
+    std::derived_from< typename std::iterator_traits<std::ranges::iterator_t<NonCommonView>>::iterator_category,
+                       std::input_iterator_tag>);
+
+// Check that the `as_input` adaptor can be used with a view.
+
+static_assert(!std::is_invocable_v<decltype(std::views::as_input)>);
+static_assert(!std::is_invocable_v<decltype(std::views::as_input), NonView>);
+static_assert(std::is_invocable_v<decltype(std::views::as_input), DefaultInitializableView>);
+static_assert(std::is_invocable_v<decltype(std::views::as_input), CommonView>);
+static_assert(std::is_invocable_v<decltype(std::views::as_input), NonCommonView>);
+
+static_assert(!CanBePiped<NonView, decltype(std::views::as_input)>);
+static_assert(CanBePiped<DefaultInitializableView&, decltype(std::views::as_input)>);
+static_assert(CanBePiped<CommonView&, decltype(std::views::as_input)>);
+static_assert(CanBePiped<NonCommonView&, decltype(std::views::as_input)>);
+
+constexpr bool test() {
+  // Sameness
+  {
+    static_assert(std::is_same_v<decltype(std::views::as_input), decltype(std::ranges::views::as_input)>);
+    assert(std::addressof(std::views::as_input) == std::addressof(std::ranges::views::as_input));
+  }
+
+  { // view | views::as_input
+
+    DefaultInitializableView view{{}, 94};
+    std::same_as<std::ranges::as_input_view<DefaultInitializableView>> decltype(auto) v =
+        view | std::views::as_input | std::views::as_input;
+    assert(v.base().i_ == 94);
+
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    static_assert(!std::ranges::forward_range<decltype(v)>);
+    static_assert(std::ranges::input_range<decltype(v)>);
+  }
+
+  { // view | views::as_input
+    {
+      DefaultInitializableView view{{}, 94};
+      std::same_as<std::ranges::as_input_view<DefaultInitializableView>> decltype(auto) v = view | std::views::as_input;
+      assert(v.base().i_ == 94);
+
+      static_assert(!std::ranges::common_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(std::ranges::input_range<decltype(v)>);
+    }
+    {
+      CommonView view{{}, 94};
+      std::same_as<std::ranges::as_input_view<CommonView>> decltype(auto) v = view | std::views::as_input;
+      assert(v.base().i_ == 94);
+
+      static_assert(!std::ranges::common_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(std::ranges::input_range<decltype(v)>);
+    }
+    {
+      NonCommonView view{{}, 94};
+      std::same_as<std::ranges::as_input_view<NonCommonView>> decltype(auto) v = view | std::views::as_input;
+      assert(v.base().i_ == 94);
+
+      static_assert(!std::ranges::common_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(std::ranges::input_range<decltype(v)>);
+    }
+  }
+
+  { // adaptor | views::as_input
+    {
+      DefaultInitializableView view{{}, 94};
+      const auto partial = std::views::transform(std::identity{}) | std::views::as_input;
+      std::same_as<std::ranges::as_input_view<
+          std::ranges::transform_view<DefaultInitializableView, std::identity>>> decltype(auto) v = partial(view);
+      assert(v.base().base().i_ == 94);
+
+      static_assert(!std::ranges::common_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(std::ranges::input_range<decltype(v)>);
+    }
+    {
+      CommonView view{{}, 94};
+      const auto partial = std::views::transform(std::identity{}) | std::views::as_input;
+      std::same_as<std::ranges::as_input_view< std::ranges::transform_view<CommonView, std::identity>>> decltype(auto)
+          v = partial(view);
+      assert(v.base().base().i_ == 94);
+
+      static_assert(!std::ranges::common_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(std::ranges::input_range<decltype(v)>);
+    }
+    {
+      NonCommonView view{{}, 94};
+      const auto partial = std::views::transform(std::identity{}) | std::views::as_input;
+      std::same_as<
+          std::ranges::as_input_view< std::ranges::transform_view<NonCommonView, std::identity>>> decltype(auto) v =
+          partial(view);
+      assert(v.base().base().i_ == 94);
+
+      static_assert(!std::ranges::common_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(std::ranges::input_range<decltype(v)>);
+    }
+  }
+
+  { // views::as_input | adaptor
+    {
+      DefaultInitializableView view{{}, 94};
+      const auto partial = std::views::as_input | std::views::transform(std::identity{});
+      std::same_as<std::ranges::transform_view<std::ranges::as_input_view<DefaultInitializableView>,
+                                               std::identity>> decltype(auto) v = partial(view);
+      assert(v.base().base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+    {
+      CommonView view{{}, 94};
+      const auto partial = std::views::as_input | std::views::transform(std::identity{});
+      std::same_as<std::ranges::transform_view<std::ranges::as_input_view<CommonView>, std::identity>> decltype(auto)
+          v = partial(view);
+      assert(v.base().base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+    {
+      NonCommonView view{{}, 94};
+      const auto partial = std::views::as_input | std::views::transform(std::identity{});
+      std::same_as<
+          std::ranges::transform_view<std::ranges::as_input_view<NonCommonView>, std::identity>> decltype(auto) v =
+          partial(view);
+      assert(v.base().base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+  }
+
+  { // views::as_input | views::all
+    {
+      DefaultInitializableView view{{}, 94};
+      std::same_as<std::ranges::as_input_view<DefaultInitializableView>> decltype(auto) v =
+          std::views::all(view) | std::views::as_input;
+      assert(v.base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+    {
+      CommonView view{{}, 94};
+      std::same_as<std::ranges::as_input_view<CommonView>> decltype(auto) v =
+          std::views::all(view) | std::views::as_input;
+      assert(v.base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+    {
+      NonCommonView view{{}, 94};
+      std::same_as<std::ranges::as_input_view<NonCommonView>> decltype(auto) v =
+          std::views::all(view) | std::views::as_input;
+      assert(v.base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+  }
+
+  { // views::as_input | views::all_t
+    {
+      DefaultInitializableView view{{}, 94};
+      std::same_as<std::ranges::as_input_view<DefaultInitializableView>> decltype(auto) v =
+          std::views::all_t<DefaultInitializableView>(view) | std::views::as_input;
+      assert(v.base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+    {
+      CommonView view{{}, 94};
+      std::same_as<std::ranges::as_input_view<CommonView>> decltype(auto) v =
+          std::views::all_t<CommonView>(view) | std::views::as_input;
+      assert(v.base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+    {
+      NonCommonView view{{}, 94};
+      std::same_as<std::ranges::as_input_view<NonCommonView>> decltype(auto) v =
+          std::views::all_t<NonCommonView>(view) | std::views::as_input;
+      assert(v.base().i_ == 94);
+
+      static_assert(std::ranges::input_range<decltype(v)>);
+      static_assert(!std::ranges::forward_range<decltype(v)>);
+      static_assert(!std::ranges::common_range<decltype(v)>);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/base.pass.cpp
new file mode 100644
index 0000000000000..78a2a29236970
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/base.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+//  template<input_range V>
+//    requires view<V>
+//  class as_input_view : public view_interface<as_input_view<V>>
+
+//     constexpr V base() const & requires copy_constructible<V> { return base_; }
+//     constexpr V base() && { return std::move(base_); }
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+#include <utility>
+
+#include "MoveOnly.h"
+
+struct SimpleView : std::ranges::view_base {
+  int i_;
+
+  int* begin() const;
+  int* end() const;
+};
+
+struct MoveOnlyView : public SimpleView {
+  MoveOnly m_;
+};
+
+template <class T>
+concept HasBase = requires(T&& t) { std::forward<T>(t).base(); };
+
+static_assert(HasBase<std::ranges::as_input_view<SimpleView> const&>);
+static_assert(HasBase<std::ranges::as_input_view<SimpleView>&&>);
+
+static_assert(!HasBase<std::ranges::as_input_view<MoveOnlyView> const&>);
+static_assert(HasBase<std::ranges::as_input_view<MoveOnlyView>&&>);
+
+constexpr bool test() {
+  { // &
+    std::ranges::as_input_view<SimpleView> view(SimpleView{{}, 94});
+    std::same_as<SimpleView> decltype(auto) v = view.base();
+    assert(v.i_ == 94);
+  }
+
+  { // const &
+    const std::ranges::as_input_view<SimpleView> view(SimpleView{{}, 94});
+    std::same_as<SimpleView> decltype(auto) v = view.base();
+    assert(v.i_ == 94);
+  }
+
+  { // &&
+    std::ranges::as_input_view<SimpleView> view(SimpleView{{}, 94});
+    std::same_as<SimpleView> decltype(auto) v = std::move(view).base();
+    assert(v.i_ == 94);
+  }
+
+  { // const &&
+    const std::ranges::as_input_view<SimpleView> view(SimpleView{{}, 94});
+    std::same_as<SimpleView> decltype(auto) v = std::move(view).base();
+    assert(v.i_ == 94);
+  }
+
+  { // move only
+    std::ranges::as_input_view<MoveOnlyView> view(MoveOnlyView{{}, 94});
+    std::same_as<MoveOnlyView> decltype(auto) v = std::move(view).base();
+    assert(v.m_.get() == 94);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/begin.pass.cpp
new file mode 100644
index 0000000000000..71c04adac99e5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/begin.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//  template<input_range V>
+//    requires view<V>
+//  class as_input_view : public view_interface<as_input_view<V>>
+
+//    constexpr auto begin() requires (!simple-view<V>);
+//    constexpr auto begin() const requires range<const V>;
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..883fb7acce239
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/ctad.compile.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//  template<input_range V>
+//    requires view<V>
+//  class as_input_view : public view_interface<as_input_view<V>>
+
+//   template<class R>
+//     as_input_view(R&&) -> as_input_view<views::all_t<R>>;
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+static_assert(std::same_as<decltype(std::ranges::as_input_view(std::vector<int>{})),
+                           std::ranges::as_input_view<std::views::all_t<std::vector<int>>>>);
+
+static_assert(std::same_as<decltype(std::ranges::as_input_view(std::declval<std::vector<int>&>())),
+                           std::ranges::as_input_view<std::views::all_t<std::vector<int>&>>>);
+
+static_assert(std::same_as<decltype(std::ranges::as_input_view(std::ranges::empty_view<int>{})),
+                           std::ranges::as_input_view<std::ranges::empty_view<int>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..c2effaa04f644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.default.pass.cpp
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//  template<input_range V>
+//    requires view<V>
+//  class as_input_view : public view_interface<as_input_view<V>>
+
+//    as_input_view() requires default_initializable<V> = default;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+struct DefaultInitializableView : std::ranges::view_base {
+  int i_ = 94;
+
+  int* begin();
+  int* end();
+};
+
+static_assert(std::default_initializable<DefaultInitializableView>);
+static_assert(std::default_initializable<std::ranges::as_input_view<DefaultInitializableView>>);
+
+struct NoDefaultInitializableView : std::ranges::view_base {
+  NoDefaultInitializableView() = delete;
+
+  int* begin();
+  int* end();
+};
+
+static_assert(!std::default_initializable<NoDefaultInitializableView>);
+static_assert(!std::default_initializable<std::ranges::as_input_view<NoDefaultInitializableView>>);
+
+struct NoexceptView : std::ranges::view_base {
+  NoexceptView() noexcept;
+
+  int const* begin() const;
+  int const* end() const;
+};
+
+static_assert(noexcept(std::ranges::as_input_view<NoexceptView>()));
+
+struct NoNoexceptView : std::ranges::view_base {
+  NoNoexceptView() noexcept(false);
+
+  int const* begin() const;
+  int const* end() const;
+};
+
+static_assert(!noexcept(std::ranges::as_input_view<NoNoexceptView>()));
+
+constexpr bool test() {
+  { // value-initialized (i.e., whether T() is well-formed).
+    std::ranges::as_input_view<DefaultInitializableView> as_input_view{};
+    assert(as_input_view.base().i_ == 94);
+  }
+  { // direct-list-initialized from an empty initializer list (i.e., whether T{} is well-formed).
+    std::ranges::as_input_view<DefaultInitializableView> as_input_view = {};
+    assert(as_input_view.base().i_ == 94);
+  }
+  { // default-initialized (i.e., whether T t; is well-formed).
+    std::ranges::as_input_view<DefaultInitializableView> as_input_view;
+    assert(as_input_view.base().i_ == 94);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.view.pass.cpp
new file mode 100644
index 0000000000000..1ec4fa3582018
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.view.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//  template<input_range V>
+//    requires view<V>
+//  class as_input_view : public view_interface<as_input_view<V>>
+
+//    constexpr explicit as_input_view(V base);
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/enable_borrowed_range.compile.pass.cpp
new file mode 100644
index 0000000000000..79e0479e5f07c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/enable_borrowed_range.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+//  template<input_range V>
+//    requires view<V>
+//  class as_input_view : public view_interface<as_input_view<V>>
+
+//  template<class V>
+//    constexpr bool enable_borrowed_range<as_input_view<V>> =
+//      enable_borrowed_range<V>;
+
+#include <ranges>
+#include <tuple>
+
+struct BorrowedView : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<BorrowedView> = true;
+
+static_assert(std::ranges::borrowed_range<std::ranges::as_input_view<BorrowedView>>);
+
+struct NonBorrowedView : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+static_assert(!std::ranges::borrowed_range<std::ranges::as_input_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/end.pass.cpp
new file mode 100644
index 0000000000000..87a20dd5f994e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/end.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+//  template<input_range V>
+//    requires view<V>
+//  class as_input_view : public view_interface<as_input_view<V>>
+
+//    constexpr auto end() requires (!simple-view<V>);
+//    constexpr auto end() const requires range<const V>;
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/general.pass.cpp
new file mode 100644
index 0000000000000..8d06cef9cdd53
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/general.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class as_input_view
+
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//  template<input_range V>
+//    requires view<V>
+//  class as_input_view : public view_interface<as_input_view<V>>
+
+// Functional tests of std::ranges::as_input_view.
+
+#include <cassert>
+#include <ranges>
+#include <string>
+#include <vector>
+#include <algorithm>
+
+template <class R, class I>
+constexpr bool isEqual(R& r, I i) {
+  for (auto e : r)
+    if (e != *i++)
+      return false;
+
+  return true;
+}
+
+constexpr bool test() {
+  std::vector<std::string> vec{"Hello", ",", " ", "World", "!"};
+  std::string expectedStr = "Hello, World!";
+
+  {
+    auto view = vec | std::views::join;
+    assert(isEqual(view, expectedStr.begin()));
+  }
+  { // Test as_input_view with a vector of strings.
+    auto view = vec | std::views::as_input | std::views::join;
+    assert(isEqual(view, expectedStr.begin()));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/base.pass.cpp
new file mode 100644
index 0000000000000..89fa883e5cf0b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/base.pass.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
+
+// <ranges>
+
+//   template<input_range V>
+//     requires view<V>
+//   template<bool Const>
+//   class as_input_view<V>::iterator
+
+//     constexpr iterator_t<Base> base() &&;
+//     constexpr const iterator_t<Base>& base() const & noexcept;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.converting.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.converting.pass.cpp
new file mode 100644
index 0000000000000..3efd9225b19a6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.converting.pass.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
+
+// <ranges>
+
+//   template<input_range V>
+//     requires view<V>
+//   template<bool Const>
+//   class as_input_view<V>::iterator
+
+//    constexpr iterator(iterator<!Const> i)
+//      requires Const && convertible_to<iterator_t<V>, iterator_t<Base>>;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..9dfa166447465
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.default.pass.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//   template<input_range V>
+//     requires view<V>
+//   template<bool Const>
+//   class as_input_view<V>::iterator
+
+//    iterator() requires default_initializable<iterator_t<Base>> = default;
+
+#include <cassert>
+#include <cstdint>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+struct DefaultInitializableIterator {
+  int i_; // Deliberately uninitialized.
+
+  using difference_type   = std::intptr_t;
+  using value_type        = int;
+  using iterator_category = std::random_access_iterator_tag;
+
+  constexpr int operator*() const { return i_; }
+
+  constexpr DefaultInitializableIterator& operator++() { return *this; }
+  constexpr void operator++(int) {}
+
+  friend constexpr bool operator==(const DefaultInitializableIterator&, const DefaultInitializableIterator&) = default;
+};
+
+static_assert(std::default_initializable<DefaultInitializableIterator>);
+
+struct DefaultInitializableIteratorView : std::ranges::view_base {
+  DefaultInitializableIterator begin() const;
+  DefaultInitializableIterator end() const;
+};
+
+struct NonDefaultInitializableIteratorView : std::ranges::view_base {
+  static_assert(!std::default_initializable<cpp20_input_iterator<int*>>);
+
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+
+template <class View>
+using ToInputViewIteratorT = std::ranges::iterator_t<std::ranges::as_input_view<View>>;
+
+// Check that the as_input_view's iterator is default initializable when the underlying iterator is.
+static_assert(std::default_initializable<ToInputViewIteratorT<DefaultInitializableIteratorView>>);
+static_assert(!std::default_initializable<ToInputViewIteratorT<NonDefaultInitializableIteratorView>>);
+
+constexpr bool test() {
+  {
+    ToInputViewIteratorT<DefaultInitializableIteratorView> it;
+    assert(*it == 0); // DefaultInitializableIterator has to be initialized to have value 0.
+  }
+  {
+    ToInputViewIteratorT<DefaultInitializableIteratorView> it = {};
+    assert(*it == 0); // DefaultInitializableIterator has to be initialized to have value 0.
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.move.pass.cpp
new file mode 100644
index 0000000000000..2e92de31c2ddb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.move.pass.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
+
+// <ranges>
+
+//   template<input_range V>
+//     requires view<V>
+//   template<bool Const>
+//   class as_input_view<V>::iterator
+
+//    iterator(iterator&&) = default;
+//    iterator& operator=(iterator&&) = default;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_move.pass.cpp
new file mode 100644
index 0000000000000..9198c55543f1c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_move.pass.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
+
+// <ranges>
+
+//   template<input_range V>
+//     requires view<V>
+//   template<bool Const>
+//   class as_input_view<V>::iterator
+
+//    friend constexpr range_rvalue_reference_t<Base> iter_move(const iterator& i)
+//      noexcept(noexcept(ranges::iter_move(i.current_)));
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_swap.pass.cpp
new file mode 100644
index 0000000000000..c7b0537437ffc
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_swap.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//   template<input_range V>
+//     requires view<V>
+//   template<bool Const>
+//   class as_input_view<V>::iterator
+
+//    friend constexpr void iter_swap(const iterator& x, const iterator& y)
+//      noexcept(noexcept(ranges::iter_swap(x.current_, y.current_)))
+//      requires indirectly_swappable<iterator_t<Base>>;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.deref.pass.cpp
new file mode 100644
index 0000000000000..fc582a070c257
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.deref.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//   template<input_range V>
+//     requires view<V>
+//   template<bool Const>
+//   class as_input_view<V>::iterator
+
+//    constexpr decltype(auto) operator*() const
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.eq.pass.cpp
new file mode 100644
index 0000000000000..68eb2cf9899ab
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.eq.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//   template<input_range V>
+//     requires view<V>
+//   template<bool Const>
+//   class as_input_view<V>::iterator
+
+//   friend constexpr bool operator==(const iterator& x, const sentinel_t<Base>& y);
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.increment.pass.cpp
new file mode 100644
index 0000000000000..7bcf5f11e4a2e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.increment.pass.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
+
+// <ranges>
+
+//   template<input_range V>
+//     requires view<V>
+//   template<bool Const>
+//   class as_input_view<V>::iterator
+
+//    constexpr iterator& operator++();
+//    constexpr void operator++(int);
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.minus.pass.cpp
new file mode 100644
index 0000000000000..5fb4c20d1c504
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.minus.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//   template<input_range V>
+//     requires view<V>
+//   template<bool Const>
+//   class as_input_view<V>::iterator
+
+//    friend constexpr difference_type operator-(const sentinel_t<Base>& y, const iterator& x)
+//      requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+//    friend constexpr difference_type operator-(const iterator& x, const sentinel_t<Base>& y)
+//      requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/size.pass.cpp
new file mode 100644
index 0000000000000..30598b59031b7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/size.pass.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+//  template<input_range V>
+//    requires view<V>
+//  class as_input_view : public view_interface<as_input_view<V>>
+
+//    constexpr auto size() requires sized_range<V>;
+//    constexpr auto size() const requires sized_range<const V>;
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+
+template <class T>
+concept HasSize = requires(T t) { t.size(); };
+
+constexpr bool test() {
+  {
+    struct SubtractableIteratorsView : std::ranges::view_base {
+      forward_iterator<int*> begin();
+      sized_sentinel<forward_iterator<int*>> end();
+    };
+
+    using ToInputViewT = std::ranges::as_input_view<SubtractableIteratorsView>;
+
+    static_assert(std::ranges::sized_range<ToInputViewT&>);
+    static_assert(!std::ranges::range<const ToInputViewT&>); // no begin()/end()
+
+    static_assert(HasSize<ToInputViewT&>);
+    static_assert(HasSize<ToInputViewT&&>);
+    static_assert(!HasSize<const ToInputViewT&>);
+    static_assert(!HasSize<const ToInputViewT&&>);
+  }
+  {
+    struct NonSizedView : std::ranges::view_base {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+
+    using ToInputViewT = std::ranges::as_input_view<NonSizedView>;
+
+    static_assert(!HasSize<ToInputViewT&>);
+    static_assert(!HasSize<ToInputViewT&&>);
+    static_assert(!HasSize<const ToInputViewT&>);
+    static_assert(!HasSize<const ToInputViewT&&>);
+  }
+  {
+    struct SizedView : std::ranges::view_base {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+
+      int size() const;
+    };
+
+    using ToInputViewT = std::ranges::as_input_view<SizedView>;
+
+    static_assert(std::ranges::sized_range<ToInputViewT&>);
+    static_assert(!std::ranges::range<const ToInputViewT&>); // no begin()/end()
+
+    static_assert(HasSize<ToInputViewT&>);
+    static_assert(HasSize<ToInputViewT&&>);
+    static_assert(!HasSize<const ToInputViewT&>); // not a view, therefore no size()
+    static_assert(!HasSize<const ToInputViewT&&>);
+  }
+  {
+    // Test an empty view.
+    int arr[] = {94};
+    auto view = std::ranges::as_input_view(std::ranges::subrange(arr, arr));
+
+    assert(view.size() == 0);
+    assert(std::as_const(view).size() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int arr[] = {94};
+    auto view = std::ranges::as_input_view(std::ranges::subrange(arr, arr + 1));
+
+    assert(view.size() == 1);
+    assert(std::as_const(view).size() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> arr = {94, 82};
+    auto view              = std::ranges::as_input_view(std::move(arr));
+
+    assert(view.size() == 2);
+    assert(std::as_const(view).size() == 2);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..fb12a2b9b6f6e 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1115,6 +1115,11 @@ def add_version_header(tc):
             "headers": ["ranges"],
             "unimplemented": True,
         },
+        {
+            "name": "__cpp_lib_ranges_as_input",
+            "values": {"c++26": 202502},
+            "headers": ["ranges"],
+        },
         {
             "name": "__cpp_lib_ranges_as_rvalue",
             "values": {"c++23": 202207},


From libcxx-commits at lists.llvm.org  Tue Jul 14 00:06:41 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 14 Jul 2026 00:06:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format
	`view.interface.pass.cpp` (PR #209391)
Message-ID: <llvm.org/llvm/llvm-project/pull/209391@github.com>


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

A prerequisite to #205012

>From 9d72d1bac0a9896c568ddce3793bbc17f13de594 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Jul 2026 10:31:27 +0300
Subject: [PATCH] [libc++][ranges][NFC] Format `view.interface.pass.cpp`

A prerequisite to #205012
---
 .../view.interface/view.interface.pass.cpp    | 102 +++++++++---------
 1 file changed, 54 insertions(+), 48 deletions(-)

diff --git a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
index bce13c38f2dab..196abb0f2775c 100644
--- a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
+++ b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
@@ -21,17 +21,17 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
-template<class T>
+template <class T>
 concept ValidViewInterfaceType = requires { typename std::ranges::view_interface<T>; };
 
-struct Empty { };
+struct Empty {};
 
 static_assert(!ValidViewInterfaceType<void>);
 static_assert(!ValidViewInterfaceType<void*>);
 static_assert(!ValidViewInterfaceType<Empty*>);
 static_assert(!ValidViewInterfaceType<Empty const>);
-static_assert(!ValidViewInterfaceType<Empty &>);
-static_assert( ValidViewInterfaceType<Empty>);
+static_assert(!ValidViewInterfaceType<Empty&>);
+static_assert(ValidViewInterfaceType<Empty>);
 
 using InputIter = cpp20_input_iterator<const int*>;
 
@@ -50,8 +50,8 @@ struct SizedInputRange : std::ranges::view_interface<SizedInputRange> {
 static_assert(std::ranges::sized_range<SizedInputRange>);
 
 struct NotSizedSentinel {
-  using value_type = int;
-  using difference_type = std::ptrdiff_t;
+  using value_type       = int;
+  using difference_type  = std::ptrdiff_t;
   using iterator_concept = std::forward_iterator_tag;
 
   explicit NotSizedSentinel() = default;
@@ -66,9 +66,7 @@ static_assert(std::forward_iterator<NotSizedSentinel>);
 using ForwardIter = forward_iterator<int*>;
 
 // So that we conform to sized_sentinel_for.
-constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) {
-    return base(x) - base(y);
-}
+constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) { return base(x) - base(y); }
 
 struct ForwardRange : std::ranges::view_interface<ForwardRange> {
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
@@ -78,19 +76,17 @@ struct ForwardRange : std::ranges::view_interface<ForwardRange> {
 static_assert(std::ranges::view<ForwardRange>);
 
 struct MoveOnlyForwardRange : std::ranges::view_interface<MoveOnlyForwardRange> {
-  int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
-  MoveOnlyForwardRange(MoveOnlyForwardRange const&) = delete;
-  MoveOnlyForwardRange(MoveOnlyForwardRange &&) = default;
-  MoveOnlyForwardRange& operator=(MoveOnlyForwardRange &&) = default;
-  MoveOnlyForwardRange() = default;
+  int buff[8]                                             = {0, 1, 2, 3, 4, 5, 6, 7};
+  MoveOnlyForwardRange(MoveOnlyForwardRange const&)       = delete;
+  MoveOnlyForwardRange(MoveOnlyForwardRange&&)            = default;
+  MoveOnlyForwardRange& operator=(MoveOnlyForwardRange&&) = default;
+  MoveOnlyForwardRange()                                  = default;
   constexpr ForwardIter begin() const { return ForwardIter(const_cast<int*>(buff)); }
   constexpr ForwardIter end() const { return ForwardIter(const_cast<int*>(buff) + 8); }
 };
 static_assert(std::ranges::view<MoveOnlyForwardRange>);
 
-struct MI : std::ranges::view_interface<InputRange>,
-            std::ranges::view_interface<MoveOnlyForwardRange> {
-};
+struct MI : std::ranges::view_interface<InputRange>, std::ranges::view_interface<MoveOnlyForwardRange> {};
 static_assert(!std::ranges::view<MI>);
 
 struct EmptyIsTrue : std::ranges::view_interface<EmptyIsTrue> {
@@ -131,7 +127,7 @@ struct DataIsNull : std::ranges::view_interface<DataIsNull> {
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
   constexpr ContIter begin() const { return ContIter(buff); }
   constexpr ContIter end() const { return ContIter(buff + 8); }
-  constexpr const int *data() const { return nullptr; }
+  constexpr const int* data() const { return nullptr; }
 };
 static_assert(std::ranges::view<DataIsNull>);
 
@@ -142,13 +138,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleCo
   };
 
   struct SentinelType {
-    int *base_;
+    int* base_;
     explicit SentinelType() = default;
-    constexpr explicit SentinelType(int *base) : base_(base) {}
-    friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) == sent.base_}; }
-    friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) == sent.base_}; }
-    friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) != sent.base_}; }
-    friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) != sent.base_}; }
+    constexpr explicit SentinelType(int* base) : base_(base) {}
+    friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept {
+      return {base(iter) == sent.base_};
+    }
+    friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept {
+      return {base(iter) == sent.base_};
+    }
+    friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept {
+      return {base(iter) != sent.base_};
+    }
+    friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept {
+      return {base(iter) != sent.base_};
+    }
   };
 
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
@@ -157,21 +161,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleCo
 };
 static_assert(std::ranges::view<BoolConvertibleComparison>);
 
-template<class T>
-concept EmptyInvocable = requires (T const& obj) { obj.empty(); };
+template <class T>
+concept EmptyInvocable = requires(T const& obj) { obj.empty(); };
 
-template<class T>
-concept BoolOpInvocable = requires (T const& obj) { bool(obj); };
+template <class T>
+concept BoolOpInvocable = requires(T const& obj) { bool(obj); };
 
 constexpr bool testEmpty() {
   static_assert(!EmptyInvocable<InputRange>);
   // LWG 3715: `view_interface::empty` is overconstrained
   static_assert(EmptyInvocable<SizedInputRange>);
-  static_assert( EmptyInvocable<ForwardRange>);
+  static_assert(EmptyInvocable<ForwardRange>);
 
   static_assert(!BoolOpInvocable<InputRange>);
   static_assert(BoolOpInvocable<SizedInputRange>);
-  static_assert( BoolOpInvocable<ForwardRange>);
+  static_assert(BoolOpInvocable<ForwardRange>);
 
   SizedInputRange sizedInputRange;
   assert(!sizedInputRange.empty());
@@ -224,12 +228,12 @@ constexpr bool testEmpty() {
   return true;
 }
 
-template<class T>
-concept DataInvocable = requires (T const& obj) { obj.data(); };
+template <class T>
+concept DataInvocable = requires(T const& obj) { obj.data(); };
 
 constexpr bool testData() {
   static_assert(!DataInvocable<ForwardRange>);
-  static_assert( DataInvocable<ContRange>);
+  static_assert(DataInvocable<ContRange>);
 
   ContRange contiguous;
   assert(contiguous.data() == contiguous.buff);
@@ -249,18 +253,18 @@ constexpr bool testData() {
   return true;
 }
 
-template<class T>
-concept SizeInvocable = requires (T const& obj) { obj.size(); };
+template <class T>
+concept SizeInvocable = requires(T const& obj) { obj.size(); };
 
 constexpr bool testSize() {
   static_assert(!SizeInvocable<InputRange>);
   static_assert(!SizeInvocable<NotSizedSentinel>);
-  static_assert( SizeInvocable<ForwardRange>);
+  static_assert(SizeInvocable<ForwardRange>);
 
   // Test the test.
   static_assert(std::same_as<decltype(std::declval<ForwardIter>() - std::declval<ForwardIter>()), std::ptrdiff_t>);
   using UnsignedSize = std::make_unsigned_t<std::ptrdiff_t>;
-  using SignedSize = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>;
+  using SignedSize   = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>;
   ForwardRange forwardRange;
   assert(forwardRange.size() == 8);
   assert(static_cast<ForwardRange const&>(forwardRange).size() == 8);
@@ -284,12 +288,12 @@ constexpr bool testSize() {
   return true;
 }
 
-template<class T>
-concept SubscriptInvocable = requires (T const& obj, std::size_t n) { obj[n]; };
+template <class T>
+concept SubscriptInvocable = requires(T const& obj, std::size_t n) { obj[n]; };
 
 constexpr bool testSubscript() {
   static_assert(!SubscriptInvocable<ForwardRange>);
-  static_assert( SubscriptInvocable<RARange>);
+  static_assert(SubscriptInvocable<RARange>);
 
   RARange randomAccess;
   assert(randomAccess[2] == 2);
@@ -300,17 +304,17 @@ constexpr bool testSubscript() {
   return true;
 }
 
-template<class T>
-concept FrontInvocable = requires (T const& obj) { obj.front(); };
+template <class T>
+concept FrontInvocable = requires(T const& obj) { obj.front(); };
 
-template<class T>
-concept BackInvocable = requires (T const& obj) { obj.back(); };
+template <class T>
+concept BackInvocable = requires(T const& obj) { obj.back(); };
 
 constexpr bool testFrontBack() {
   static_assert(!FrontInvocable<InputRange>);
-  static_assert( FrontInvocable<ForwardRange>);
+  static_assert(FrontInvocable<ForwardRange>);
   static_assert(!BackInvocable<ForwardRange>);
-  static_assert( BackInvocable<RARange>);
+  static_assert(BackInvocable<RARange>);
 
   ForwardRange forwardRange;
   assert(forwardRange.front() == 0);
@@ -332,8 +336,10 @@ constexpr bool testFrontBack() {
   return true;
 }
 
-struct V1 : std::ranges::view_interface<V1> { };
-struct V2 : std::ranges::view_interface<V2> { V1 base_; };
+struct V1 : std::ranges::view_interface<V1> {};
+struct V2 : std::ranges::view_interface<V2> {
+  V1 base_;
+};
 static_assert(sizeof(V2) == sizeof(V1));
 
 int main(int, char**) {


From libcxx-commits at lists.llvm.org  Tue Jul 14 00:07:19 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 00:07:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format
	`view.interface.pass.cpp` (PR #209391)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209391@github.com>
Message-ID: <6a55e027.66c0679a.3aa103.b360@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

<details>
<summary>Changes</summary>

A prerequisite to #<!-- -->205012

---
Full diff: https://github.com/llvm/llvm-project/pull/209391.diff


1 Files Affected:

- (modified) libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp (+54-48) 


``````````diff
diff --git a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
index bce13c38f2dab..196abb0f2775c 100644
--- a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
+++ b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
@@ -21,17 +21,17 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
-template<class T>
+template <class T>
 concept ValidViewInterfaceType = requires { typename std::ranges::view_interface<T>; };
 
-struct Empty { };
+struct Empty {};
 
 static_assert(!ValidViewInterfaceType<void>);
 static_assert(!ValidViewInterfaceType<void*>);
 static_assert(!ValidViewInterfaceType<Empty*>);
 static_assert(!ValidViewInterfaceType<Empty const>);
-static_assert(!ValidViewInterfaceType<Empty &>);
-static_assert( ValidViewInterfaceType<Empty>);
+static_assert(!ValidViewInterfaceType<Empty&>);
+static_assert(ValidViewInterfaceType<Empty>);
 
 using InputIter = cpp20_input_iterator<const int*>;
 
@@ -50,8 +50,8 @@ struct SizedInputRange : std::ranges::view_interface<SizedInputRange> {
 static_assert(std::ranges::sized_range<SizedInputRange>);
 
 struct NotSizedSentinel {
-  using value_type = int;
-  using difference_type = std::ptrdiff_t;
+  using value_type       = int;
+  using difference_type  = std::ptrdiff_t;
   using iterator_concept = std::forward_iterator_tag;
 
   explicit NotSizedSentinel() = default;
@@ -66,9 +66,7 @@ static_assert(std::forward_iterator<NotSizedSentinel>);
 using ForwardIter = forward_iterator<int*>;
 
 // So that we conform to sized_sentinel_for.
-constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) {
-    return base(x) - base(y);
-}
+constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) { return base(x) - base(y); }
 
 struct ForwardRange : std::ranges::view_interface<ForwardRange> {
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
@@ -78,19 +76,17 @@ struct ForwardRange : std::ranges::view_interface<ForwardRange> {
 static_assert(std::ranges::view<ForwardRange>);
 
 struct MoveOnlyForwardRange : std::ranges::view_interface<MoveOnlyForwardRange> {
-  int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
-  MoveOnlyForwardRange(MoveOnlyForwardRange const&) = delete;
-  MoveOnlyForwardRange(MoveOnlyForwardRange &&) = default;
-  MoveOnlyForwardRange& operator=(MoveOnlyForwardRange &&) = default;
-  MoveOnlyForwardRange() = default;
+  int buff[8]                                             = {0, 1, 2, 3, 4, 5, 6, 7};
+  MoveOnlyForwardRange(MoveOnlyForwardRange const&)       = delete;
+  MoveOnlyForwardRange(MoveOnlyForwardRange&&)            = default;
+  MoveOnlyForwardRange& operator=(MoveOnlyForwardRange&&) = default;
+  MoveOnlyForwardRange()                                  = default;
   constexpr ForwardIter begin() const { return ForwardIter(const_cast<int*>(buff)); }
   constexpr ForwardIter end() const { return ForwardIter(const_cast<int*>(buff) + 8); }
 };
 static_assert(std::ranges::view<MoveOnlyForwardRange>);
 
-struct MI : std::ranges::view_interface<InputRange>,
-            std::ranges::view_interface<MoveOnlyForwardRange> {
-};
+struct MI : std::ranges::view_interface<InputRange>, std::ranges::view_interface<MoveOnlyForwardRange> {};
 static_assert(!std::ranges::view<MI>);
 
 struct EmptyIsTrue : std::ranges::view_interface<EmptyIsTrue> {
@@ -131,7 +127,7 @@ struct DataIsNull : std::ranges::view_interface<DataIsNull> {
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
   constexpr ContIter begin() const { return ContIter(buff); }
   constexpr ContIter end() const { return ContIter(buff + 8); }
-  constexpr const int *data() const { return nullptr; }
+  constexpr const int* data() const { return nullptr; }
 };
 static_assert(std::ranges::view<DataIsNull>);
 
@@ -142,13 +138,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleCo
   };
 
   struct SentinelType {
-    int *base_;
+    int* base_;
     explicit SentinelType() = default;
-    constexpr explicit SentinelType(int *base) : base_(base) {}
-    friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) == sent.base_}; }
-    friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) == sent.base_}; }
-    friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) != sent.base_}; }
-    friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) != sent.base_}; }
+    constexpr explicit SentinelType(int* base) : base_(base) {}
+    friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept {
+      return {base(iter) == sent.base_};
+    }
+    friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept {
+      return {base(iter) == sent.base_};
+    }
+    friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept {
+      return {base(iter) != sent.base_};
+    }
+    friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept {
+      return {base(iter) != sent.base_};
+    }
   };
 
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
@@ -157,21 +161,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleCo
 };
 static_assert(std::ranges::view<BoolConvertibleComparison>);
 
-template<class T>
-concept EmptyInvocable = requires (T const& obj) { obj.empty(); };
+template <class T>
+concept EmptyInvocable = requires(T const& obj) { obj.empty(); };
 
-template<class T>
-concept BoolOpInvocable = requires (T const& obj) { bool(obj); };
+template <class T>
+concept BoolOpInvocable = requires(T const& obj) { bool(obj); };
 
 constexpr bool testEmpty() {
   static_assert(!EmptyInvocable<InputRange>);
   // LWG 3715: `view_interface::empty` is overconstrained
   static_assert(EmptyInvocable<SizedInputRange>);
-  static_assert( EmptyInvocable<ForwardRange>);
+  static_assert(EmptyInvocable<ForwardRange>);
 
   static_assert(!BoolOpInvocable<InputRange>);
   static_assert(BoolOpInvocable<SizedInputRange>);
-  static_assert( BoolOpInvocable<ForwardRange>);
+  static_assert(BoolOpInvocable<ForwardRange>);
 
   SizedInputRange sizedInputRange;
   assert(!sizedInputRange.empty());
@@ -224,12 +228,12 @@ constexpr bool testEmpty() {
   return true;
 }
 
-template<class T>
-concept DataInvocable = requires (T const& obj) { obj.data(); };
+template <class T>
+concept DataInvocable = requires(T const& obj) { obj.data(); };
 
 constexpr bool testData() {
   static_assert(!DataInvocable<ForwardRange>);
-  static_assert( DataInvocable<ContRange>);
+  static_assert(DataInvocable<ContRange>);
 
   ContRange contiguous;
   assert(contiguous.data() == contiguous.buff);
@@ -249,18 +253,18 @@ constexpr bool testData() {
   return true;
 }
 
-template<class T>
-concept SizeInvocable = requires (T const& obj) { obj.size(); };
+template <class T>
+concept SizeInvocable = requires(T const& obj) { obj.size(); };
 
 constexpr bool testSize() {
   static_assert(!SizeInvocable<InputRange>);
   static_assert(!SizeInvocable<NotSizedSentinel>);
-  static_assert( SizeInvocable<ForwardRange>);
+  static_assert(SizeInvocable<ForwardRange>);
 
   // Test the test.
   static_assert(std::same_as<decltype(std::declval<ForwardIter>() - std::declval<ForwardIter>()), std::ptrdiff_t>);
   using UnsignedSize = std::make_unsigned_t<std::ptrdiff_t>;
-  using SignedSize = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>;
+  using SignedSize   = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>;
   ForwardRange forwardRange;
   assert(forwardRange.size() == 8);
   assert(static_cast<ForwardRange const&>(forwardRange).size() == 8);
@@ -284,12 +288,12 @@ constexpr bool testSize() {
   return true;
 }
 
-template<class T>
-concept SubscriptInvocable = requires (T const& obj, std::size_t n) { obj[n]; };
+template <class T>
+concept SubscriptInvocable = requires(T const& obj, std::size_t n) { obj[n]; };
 
 constexpr bool testSubscript() {
   static_assert(!SubscriptInvocable<ForwardRange>);
-  static_assert( SubscriptInvocable<RARange>);
+  static_assert(SubscriptInvocable<RARange>);
 
   RARange randomAccess;
   assert(randomAccess[2] == 2);
@@ -300,17 +304,17 @@ constexpr bool testSubscript() {
   return true;
 }
 
-template<class T>
-concept FrontInvocable = requires (T const& obj) { obj.front(); };
+template <class T>
+concept FrontInvocable = requires(T const& obj) { obj.front(); };
 
-template<class T>
-concept BackInvocable = requires (T const& obj) { obj.back(); };
+template <class T>
+concept BackInvocable = requires(T const& obj) { obj.back(); };
 
 constexpr bool testFrontBack() {
   static_assert(!FrontInvocable<InputRange>);
-  static_assert( FrontInvocable<ForwardRange>);
+  static_assert(FrontInvocable<ForwardRange>);
   static_assert(!BackInvocable<ForwardRange>);
-  static_assert( BackInvocable<RARange>);
+  static_assert(BackInvocable<RARange>);
 
   ForwardRange forwardRange;
   assert(forwardRange.front() == 0);
@@ -332,8 +336,10 @@ constexpr bool testFrontBack() {
   return true;
 }
 
-struct V1 : std::ranges::view_interface<V1> { };
-struct V2 : std::ranges::view_interface<V2> { V1 base_; };
+struct V1 : std::ranges::view_interface<V1> {};
+struct V2 : std::ranges::view_interface<V2> {
+  V1 base_;
+};
 static_assert(sizeof(V2) == sizeof(V1));
 
 int main(int, char**) {

``````````

</details>


https://github.com/llvm/llvm-project/pull/209391

From libcxx-commits at lists.llvm.org  Tue Jul 14 01:21:26 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 14 Jul 2026 01:21:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format
	`view.interface.pass.cpp` (PR #209391)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209391@github.com>
Message-ID: <6a55f186.4987c784.63cbc.0ef1@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/209391

From libcxx-commits at lists.llvm.org  Tue Jul 14 01:49:57 2026
From: libcxx-commits at lists.llvm.org (Takuto Ikuta via libcxx-commits)
Date: Tue, 14 Jul 2026 01:49:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix hypotf linker error with
 Clang modules on Windows UCRT (PR #209403)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209403@github.com>
Message-ID: <6a55f835.66c0679a.3aa103.bf93@mx.google.com>


================
@@ -27,19 +27,35 @@
 _LIBCPP_PUSH_MACROS
 #include <__undef_macros>
 
+#if defined(_LIBCPP_MSVCRT)
+extern "C" {
+_LIBCPP_CRT_FUNC float __cdecl _hypotf(float, float);
+}
+#endif
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace __math {
 
-inline _LIBCPP_HIDE_FROM_ABI float hypot(float __x, float __y) _NOEXCEPT { return __builtin_hypotf(__x, __y); }
+inline _LIBCPP_HIDE_FROM_ABI float hypot(float __x, float __y) _NOEXCEPT {
+#if defined(_LIBCPP_MSVCRT)
+  return ::_hypotf(__x, __y);
+#else
+  return __builtin_hypotf(__x, __y);
+#endif
+}
 
 template <class = int>
 _LIBCPP_HIDE_FROM_ABI double hypot(double __x, double __y) _NOEXCEPT {
   return __builtin_hypot(__x, __y);
 }
 
 inline _LIBCPP_HIDE_FROM_ABI long double hypot(long double __x, long double __y) _NOEXCEPT {
+#if defined(_LIBCPP_MSVCRT)
+  return (long double)__math::hypot((double)__x, (double)__y);
----------------
atetubou wrote:

Can we use `static_cast` here?
https://llvm.org/docs/CodingStandards.html#prefer-c-style-casts

https://github.com/llvm/llvm-project/pull/209403

From libcxx-commits at lists.llvm.org  Tue Jul 14 01:50:39 2026
From: libcxx-commits at lists.llvm.org (Takuto Ikuta via libcxx-commits)
Date: Tue, 14 Jul 2026 01:50:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix hypotf linker error with
 Clang modules on Windows UCRT (PR #209403)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209403@github.com>
Message-ID: <6a55f85f.824e11a6.f5f27.99f6@mx.google.com>


atetubou wrote:

Can we add a short comment explaining why we use different code path for Windows UCRT build?

https://github.com/llvm/llvm-project/pull/209403

From libcxx-commits at lists.llvm.org  Tue Jul 14 02:05:25 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 02:05:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix hypotf linker error with
 Clang modules on Windows UCRT (PR #209403)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209403@github.com>
Message-ID: <6a55fbd5.6537ae93.2f1c1b.8613@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Junji Watanabe (Jwata)

<details>
<summary>Changes</summary>

This commit resolves an undefined symbol `hypotf` linking error that occurs when compiling standard C++ headers with Clang modules enabled targeting Windows with MSVC C-Runtime (UCRT).

You can find the minimal repro at Chromium here: https://crrev.com/c/8085003

### Context &amp; Cause
Under Windows UCRT, the import libraries (`ucrt.lib`/`ucrtd.lib`) do not export the symbol `hypotf` directly. Instead, they export `_hypotf`. The UCRT header `corecrt_math.h` declares `hypotf` as an inline wrapper function:

```cpp
_Check_return_ __inline float __CRTDECL hypotf(_In_ float _X, _In_ float _Y) {
  return _hypotf(_X, _Y);
}
```

When building libc++ with Clang modules enabled at Chromium, the `std_core.math.hypot` submodule (mapping to `__math/hypot.h`) is precompiled into a binary module file in isolation.

Because `__math/hypot.h` does not include `&lt;math.h&gt;` or `&lt;cmath&gt;`, Clang's front-end parses the submodule without visibility of the UCRT inline declaration of `hypotf`.
1. During AST resolution of `std::hypot` (defined using `__builtin_hypotf`), the front-end fails to bind the builtin to the inline wrapper `hypotf` since it is not present in the symbol table.
2. Clang passes `__builtin_hypotf` down to the LLVM backend.
3. The LLVM backend's `TargetLibraryInfo` config for MSVC expects the standard `hypotf` symbol to be available, and thus lowers it to a direct external call to the symbol `hypotf`.
4. This results in the object file containing a direct external reference to `hypotf` rather than an imported call to `__imp__hypotf`.
5. The link phase fails because no `hypotf` symbol exists in `ucrt.lib`.

### Solution
We bypass `__builtin_hypotf` and `__builtin_hypotl` on Windows targeting UCRT (when `_LIBCPP_MSVCRT` is defined):
1. **For `float`**: We explicitly declare the imported UCRT `_hypotf` function (using the `_LIBCPP_CRT_FUNC` macro to dynamically support both static `/MT` and DLL `/MD` runtime linking) and route `std::hypot(float, float)` to it.
2. **For `long double`**: Since `long double` is represented as a 64-bit float (identical to `double`) on MSVC, and `_hypotl` is also inline-only in UCRT, we promote the arguments to `double` and forward to the `double` implementation (which maps to the exported `hypot` symbol).

Assisted-by: Gemini

---
Full diff: https://github.com/llvm/llvm-project/pull/209403.diff


1 Files Affected:

- (modified) libcxx/include/__math/hypot.h (+17-1) 


``````````diff
diff --git a/libcxx/include/__math/hypot.h b/libcxx/include/__math/hypot.h
index 2b12d7be21072..37788e542fa66 100644
--- a/libcxx/include/__math/hypot.h
+++ b/libcxx/include/__math/hypot.h
@@ -27,11 +27,23 @@
 _LIBCPP_PUSH_MACROS
 #include <__undef_macros>
 
+#if defined(_LIBCPP_MSVCRT)
+extern "C" {
+_LIBCPP_CRT_FUNC float __cdecl _hypotf(float, float);
+}
+#endif
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace __math {
 
-inline _LIBCPP_HIDE_FROM_ABI float hypot(float __x, float __y) _NOEXCEPT { return __builtin_hypotf(__x, __y); }
+inline _LIBCPP_HIDE_FROM_ABI float hypot(float __x, float __y) _NOEXCEPT {
+#if defined(_LIBCPP_MSVCRT)
+  return ::_hypotf(__x, __y);
+#else
+  return __builtin_hypotf(__x, __y);
+#endif
+}
 
 template <class = int>
 _LIBCPP_HIDE_FROM_ABI double hypot(double __x, double __y) _NOEXCEPT {
@@ -39,7 +51,11 @@ _LIBCPP_HIDE_FROM_ABI double hypot(double __x, double __y) _NOEXCEPT {
 }
 
 inline _LIBCPP_HIDE_FROM_ABI long double hypot(long double __x, long double __y) _NOEXCEPT {
+#if defined(_LIBCPP_MSVCRT)
+  return (long double)__math::hypot((double)__x, (double)__y);
+#else
   return __builtin_hypotl(__x, __y);
+#endif
 }
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>

``````````

</details>


https://github.com/llvm/llvm-project/pull/209403

From libcxx-commits at lists.llvm.org  Tue Jul 14 02:10:06 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 14 Jul 2026 02:10:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add sentinel OtherConst cases
 for `nodiscard` tests in `enumerate_view` (PR #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a55fcee.0c05e68e.1e82f1.8ece@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/209289

>From de6bef4e4ef9069b23edba3c557e7d576fc78e4a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 12 Jul 2026 18:32:39 +0200
Subject: [PATCH 1/4] add: OtherConst test to sentinel operator-

---
 .../range.enumerate/nodiscard.verify.cpp               | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..d6d93219a4e14 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,7 +64,8 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st = ev.end();
+  auto st   = ev.end();
+  auto c_it = std::as_const(ev).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -74,8 +75,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}

>From bcb2d6392e68580206412414a24029dee3c70881 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:20:42 +0200
Subject: [PATCH 2/4] ref: model non-common non-simple view for sentinel tests
 and general adjustments

---
 .../range.enumerate/nodiscard.verify.cpp      | 38 ++++++++++++-------
 1 file changed, 25 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index d6d93219a4e14..fea1bca7c2145 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,32 +14,41 @@
 #include <utility>
 #include <vector>
 
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
 void test() {
-  std::vector<int> range;
-  std::ranges::enumerate_view ev{range};
+  auto v  = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.begin();
+  v.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).begin();
+  std::as_const(v).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.end();
+  v.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).end();
+  std::as_const(v).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.size();
+  v.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).size();
+  std::as_const(v).size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.base();
+  v.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::move(ev).base();
+  std::move(v).base();
 
   // [range.enumerate.iterator]
 
-  auto it = ev.begin();
+  auto it = v.begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(it).base();
@@ -64,8 +73,7 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st   = ev.end();
-  auto c_it = std::as_const(ev).begin();
+  auto st = v.end();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -75,6 +83,8 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  auto c_it = std::as_const(v).begin();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - c_it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -82,6 +92,8 @@ void test() {
 
   // [range.enumerate.overview]
 
+  std::vector<int> range { 1, 2, 3 };
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
 }

>From 02096b8044eb8b37db4a9cdee0c35da7d297574c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:25:17 +0200
Subject: [PATCH 3/4] fix: clang-format

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index fea1bca7c2145..4ebb6196c8d9c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,7 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v  = View{} | std::views::enumerate;
+  auto v = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
@@ -92,7 +92,7 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range { 1, 2, 3 };
+  std::vector<int> range{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);

>From 9fda31d2eb80d645f56214599f544ebccb8b724a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:09:56 +0200
Subject: [PATCH 4/4] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 4ebb6196c8d9c..b2ce7f3418fe6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,8 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v = View{} | std::views::enumerate;
+  View range;
+  auto v = std::ranges::enumerate_view ev{range};
 
   // [range.enumerate.view]
 


From libcxx-commits at lists.llvm.org  Tue Jul 14 02:11:50 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 14 Jul 2026 02:11:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add sentinel OtherConst cases
 for `nodiscard` tests in `enumerate_view` (PR #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a55fd56.c5504292.28b3b8.c429@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/209289

>From de6bef4e4ef9069b23edba3c557e7d576fc78e4a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 12 Jul 2026 18:32:39 +0200
Subject: [PATCH 1/5] add: OtherConst test to sentinel operator-

---
 .../range.enumerate/nodiscard.verify.cpp               | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..d6d93219a4e14 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,7 +64,8 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st = ev.end();
+  auto st   = ev.end();
+  auto c_it = std::as_const(ev).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -74,8 +75,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}

>From bcb2d6392e68580206412414a24029dee3c70881 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:20:42 +0200
Subject: [PATCH 2/5] ref: model non-common non-simple view for sentinel tests
 and general adjustments

---
 .../range.enumerate/nodiscard.verify.cpp      | 38 ++++++++++++-------
 1 file changed, 25 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index d6d93219a4e14..fea1bca7c2145 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,32 +14,41 @@
 #include <utility>
 #include <vector>
 
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
 void test() {
-  std::vector<int> range;
-  std::ranges::enumerate_view ev{range};
+  auto v  = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.begin();
+  v.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).begin();
+  std::as_const(v).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.end();
+  v.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).end();
+  std::as_const(v).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.size();
+  v.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).size();
+  std::as_const(v).size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.base();
+  v.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::move(ev).base();
+  std::move(v).base();
 
   // [range.enumerate.iterator]
 
-  auto it = ev.begin();
+  auto it = v.begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(it).base();
@@ -64,8 +73,7 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st   = ev.end();
-  auto c_it = std::as_const(ev).begin();
+  auto st = v.end();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -75,6 +83,8 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  auto c_it = std::as_const(v).begin();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - c_it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -82,6 +92,8 @@ void test() {
 
   // [range.enumerate.overview]
 
+  std::vector<int> range { 1, 2, 3 };
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
 }

>From 02096b8044eb8b37db4a9cdee0c35da7d297574c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:25:17 +0200
Subject: [PATCH 3/5] fix: clang-format

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index fea1bca7c2145..4ebb6196c8d9c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,7 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v  = View{} | std::views::enumerate;
+  auto v = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
@@ -92,7 +92,7 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range { 1, 2, 3 };
+  std::vector<int> range{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);

>From 9fda31d2eb80d645f56214599f544ebccb8b724a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:09:56 +0200
Subject: [PATCH 4/5] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 4ebb6196c8d9c..b2ce7f3418fe6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,8 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v = View{} | std::views::enumerate;
+  View range;
+  auto v = std::ranges::enumerate_view ev{range};
 
   // [range.enumerate.view]
 

>From 02235acb82379ce5c0bf8011275e5129effbf3ad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:11:36 +0200
Subject: [PATCH 5/5] rename range variable to vec

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index b2ce7f3418fe6..250b21e3e9910 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -93,8 +93,8 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range{1, 2, 3};
+  std::vector<int> vec{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(range);
+  std::views::enumerate(vec);
 }


From libcxx-commits at lists.llvm.org  Tue Jul 14 02:16:28 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 14 Jul 2026 02:16:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add sentinel OtherConst cases
 for `nodiscard` tests in `enumerate_view` (PR #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a55fe6c.07d7ca98.9dee3.c78b@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/209289

>From de6bef4e4ef9069b23edba3c557e7d576fc78e4a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 12 Jul 2026 18:32:39 +0200
Subject: [PATCH 1/6] add: OtherConst test to sentinel operator-

---
 .../range.enumerate/nodiscard.verify.cpp               | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..d6d93219a4e14 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,7 +64,8 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st = ev.end();
+  auto st   = ev.end();
+  auto c_it = std::as_const(ev).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -74,8 +75,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}

>From bcb2d6392e68580206412414a24029dee3c70881 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:20:42 +0200
Subject: [PATCH 2/6] ref: model non-common non-simple view for sentinel tests
 and general adjustments

---
 .../range.enumerate/nodiscard.verify.cpp      | 38 ++++++++++++-------
 1 file changed, 25 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index d6d93219a4e14..fea1bca7c2145 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,32 +14,41 @@
 #include <utility>
 #include <vector>
 
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
 void test() {
-  std::vector<int> range;
-  std::ranges::enumerate_view ev{range};
+  auto v  = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.begin();
+  v.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).begin();
+  std::as_const(v).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.end();
+  v.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).end();
+  std::as_const(v).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.size();
+  v.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).size();
+  std::as_const(v).size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.base();
+  v.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::move(ev).base();
+  std::move(v).base();
 
   // [range.enumerate.iterator]
 
-  auto it = ev.begin();
+  auto it = v.begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(it).base();
@@ -64,8 +73,7 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st   = ev.end();
-  auto c_it = std::as_const(ev).begin();
+  auto st = v.end();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -75,6 +83,8 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  auto c_it = std::as_const(v).begin();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - c_it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -82,6 +92,8 @@ void test() {
 
   // [range.enumerate.overview]
 
+  std::vector<int> range { 1, 2, 3 };
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
 }

>From 02096b8044eb8b37db4a9cdee0c35da7d297574c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:25:17 +0200
Subject: [PATCH 3/6] fix: clang-format

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index fea1bca7c2145..4ebb6196c8d9c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,7 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v  = View{} | std::views::enumerate;
+  auto v = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
@@ -92,7 +92,7 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range { 1, 2, 3 };
+  std::vector<int> range{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);

>From 9fda31d2eb80d645f56214599f544ebccb8b724a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:09:56 +0200
Subject: [PATCH 4/6] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 4ebb6196c8d9c..b2ce7f3418fe6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,8 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v = View{} | std::views::enumerate;
+  View range;
+  auto v = std::ranges::enumerate_view ev{range};
 
   // [range.enumerate.view]
 

>From 02235acb82379ce5c0bf8011275e5129effbf3ad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:11:36 +0200
Subject: [PATCH 5/6] rename range variable to vec

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index b2ce7f3418fe6..250b21e3e9910 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -93,8 +93,8 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range{1, 2, 3};
+  std::vector<int> vec{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(range);
+  std::views::enumerate(vec);
 }

>From 4ed811e010d332be499f091c05e411f3f88349a0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:16:13 +0200
Subject: [PATCH 6/6] fix: wrong variable initialization

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp    | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 250b21e3e9910..c10e56293246f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -12,7 +12,6 @@
 
 #include <ranges>
 #include <utility>
-#include <vector>
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -26,7 +25,7 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
   View range;
-  auto v = std::ranges::enumerate_view ev{range};
+  std::ranges::enumerate_view v{range};
 
   // [range.enumerate.view]
 
@@ -93,8 +92,6 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> vec{1, 2, 3};
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(vec);
+  std::views::enumerate(range);
 }


From libcxx-commits at lists.llvm.org  Tue Jul 14 02:53:03 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 14 Jul 2026 02:53:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix hypotf linker error with
 Clang modules on Windows UCRT (PR #209403)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209403@github.com>
Message-ID: <6a5606ff.4987c784.63cbc.69b5@mx.google.com>


================
@@ -27,19 +27,35 @@
 _LIBCPP_PUSH_MACROS
 #include <__undef_macros>
 
+#if defined(_LIBCPP_MSVCRT)
+extern "C" {
+_LIBCPP_CRT_FUNC float __cdecl _hypotf(float, float);
+}
+#endif
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace __math {
 
-inline _LIBCPP_HIDE_FROM_ABI float hypot(float __x, float __y) _NOEXCEPT { return __builtin_hypotf(__x, __y); }
+inline _LIBCPP_HIDE_FROM_ABI float hypot(float __x, float __y) _NOEXCEPT {
+#if defined(_LIBCPP_MSVCRT)
+  return ::_hypotf(__x, __y);
+#else
+  return __builtin_hypotf(__x, __y);
+#endif
+}
 
 template <class = int>
 _LIBCPP_HIDE_FROM_ABI double hypot(double __x, double __y) _NOEXCEPT {
   return __builtin_hypot(__x, __y);
 }
 
 inline _LIBCPP_HIDE_FROM_ABI long double hypot(long double __x, long double __y) _NOEXCEPT {
+#if defined(_LIBCPP_MSVCRT)
+  return (long double)__math::hypot((double)__x, (double)__y);
----------------
frederick-vs-ja wrote:

Also, do we want to explicitly cast a `double` value to `long double` given the conversion is widening?

https://github.com/llvm/llvm-project/pull/209403

From libcxx-commits at lists.llvm.org  Tue Jul 14 02:54:41 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 02:54:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix hypotf linker error with
 Clang modules on Windows UCRT (PR #209403)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209403@github.com>
Message-ID: <6a560761.b4a331f0.271e68.27a8@mx.google.com>


https://github.com/philnik777 requested changes to this pull request.

AFAICT this is fundamentally a Clang bug. Why do we want to work around this in the library instead of fixing Clang?

https://github.com/llvm/llvm-project/pull/209403

From libcxx-commits at lists.llvm.org  Tue Jul 14 03:09:01 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 03:09:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][string] Test: default
 constructed allocators can be unequal (PR #195839)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/195839@github.com>
Message-ID: <6a560abd.b4a331f0.271e68.4e91@mx.google.com>


================
@@ -515,4 +515,40 @@ struct SocccAllocator {
   using propagate_on_container_swap            = std::false_type;
 };
 
+// Track how many times the allocator was default-constructed
+//
+
+template <class T>
+class ControlledDefaultConstructorAllocator : public std::allocator<T> {
----------------
philnik777 wrote:

We're explicit in what we care about. `std::allocator` changes sometimes and we don't want to accidentally change what our test allocators are. It also removes the need for `rebind`.

https://github.com/llvm/llvm-project/pull/195839

From libcxx-commits at lists.llvm.org  Tue Jul 14 03:50:54 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 03:50:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [AIX][libc++] fopen() does not support
 the 'x' (exclusive/noreplace) mode suffix. (PR #206498)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206498@github.com>
Message-ID: <6a56148e.1728eaf3.eafd0.d5fc@mx.google.com>


================
@@ -664,6 +674,9 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(ios_base::openmode _
   case ios_base::in | ios_base::app | ios_base::binary:
     return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
 #    if _LIBCPP_STD_VER >= 23
+// AIX fopen() does not support the 'x' (noreplace) mode suffix; these cases fall through to
+// default (return nullptr), which is conformant per C23 7.21.5.3p5 (fail if atomic check-and-create is unsupported).
+#      if !defined(_AIX)
----------------
philnik777 wrote:

We generally try to avoid working around libc issues, and I don't think we should deviate here either. I don't see that the change would be technically challenging for any libc. This isn't working around an edge-case in any way.

https://github.com/llvm/llvm-project/pull/206498

From libcxx-commits at lists.llvm.org  Tue Jul 14 04:26:21 2026
From: libcxx-commits at lists.llvm.org (Junji Watanabe via libcxx-commits)
Date: Tue, 14 Jul 2026 04:26:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix hypotf linker error with
 Clang modules on Windows UCRT (PR #209403)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209403@github.com>
Message-ID: <6a561cdd.3cce8983.225732.786a@mx.google.com>


Jwata wrote:

Thank you all for the feedback!

> AFAICT this is fundamentally a Clang bug. Why do we want to work around this in the library instead of fixing Clang?

My teammate actually attempted to fix it on Clang side. But, got a push back since it's hardcoding hypotf.  
https://github.com/llvm/llvm-project/pull/207135#issuecomment-4967127742

So, I was wondering if libc++ should have this workaround. To be honest, I'm not fully sure which layer this issue should be fixed.

https://github.com/llvm/llvm-project/pull/209403

From libcxx-commits at lists.llvm.org  Tue Jul 14 04:51:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 04:51:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 2d1bc4f - [libc++] Fix usage of
 constructive interference when we meant destructive (#208804)
Message-ID: <6a5622ae.07d7ca98.9dee3.74f6@mx.google.com>


Author: Louis Dionne
Date: 2026-07-14T07:51:06-04:00
New Revision: 2d1bc4fc5d00d6c96307d5375d7259011509faf3

URL: https://github.com/llvm/llvm-project/commit/2d1bc4fc5d00d6c96307d5375d7259011509faf3
DIFF: https://github.com/llvm/llvm-project/commit/2d1bc4fc5d00d6c96307d5375d7259011509faf3.diff

LOG: [libc++] Fix usage of constructive interference when we meant destructive (#208804)

We maintain a contention table to implement atomic wait/notify inside
atomic.cpp. Entries in that table are padded to avoid false sharing,
since they are expected to be accessed from multiple threads
concurrently.

However, we used std::hardware_constructive_interference_size for that,
when in reality std::hardware_destructive_interference_size is the one
we need. The semantics of constructive interference are to promote true
sharing for data that should stay colocated, while destructive
interference is to prevent false sharing.

Note that on platforms such as macOS arm64, destructive interference
size is larger than constructive interference size, which means that
this patch may regress the size of the dylib on some platforms. However,
that is required for performance purposes.

Fixes #208305

Added: 
    

Modified: 
    libcxx/src/atomic.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
index 25c146d5d51f7..15e0692100341 100644
--- a/libcxx/src/atomic.cpp
+++ b/libcxx/src/atomic.cpp
@@ -425,7 +425,7 @@ static constexpr hash<void const*> __contention_hasher;
 // cache line, we would end up in a "false-sharing" situation. However, in practice, it would require
 // both native and non-native atomics to be used concurrently, and to fall in the same bucket, which
 // is expected to be unlikely.
-struct alignas(std::hardware_constructive_interference_size) /*  aim to avoid false sharing */ __contention_state {
+struct alignas(std::hardware_destructive_interference_size) /* aim to avoid false sharing */ __contention_state {
   __cxx_atomic_contention_t __waiter_count_native;
   __cxx_atomic_contention_t __waiter_count_global;
   __cxx_atomic_contention_t __platform_state;


        

From libcxx-commits at lists.llvm.org  Tue Jul 14 04:51:13 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 04:51:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix usage of constructive
 interference when we meant destructive (PR #208804)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208804@github.com>
Message-ID: <6a5622b1.af6ba3ed.1dc10f.c247@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208804

From libcxx-commits at lists.llvm.org  Tue Jul 14 04:56:21 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 04:56:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a5623e5.510f576d.1b3cb9.a022@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207086

>From 64b22d4e38f896e39762ccfd1f011f0dd16dfddb Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 29 Jun 2026 14:57:40 -0400
Subject: [PATCH 01/11] [libc++] Treat negative counts in copy_n & friends as
 no-ops

The standard specifies copy_n, fill_n and generate_n (both std and
ranges) to be no-ops when passed a negative count. This patch fixes
libc++ to abide by that requirement, with tests.

It's worth noting that std::for_each_n makes n > 0 a precondition
instead: we add the check and tests for it.

Fixes #193613
---
 libcxx/include/__algorithm/copy_n.h           | 17 +++++----
 libcxx/include/__algorithm/for_each_n.h       |  2 +
 libcxx/include/__algorithm/ranges_copy_n.h    |  2 +
 .../include/__algorithm/ranges_for_each_n.h   |  2 +
 libcxx/include/__bit_reference                | 17 +++++----
 .../alg.foreach/assert.for_each_n.pass.cpp    | 35 ++++++++++++++++++
 .../assert.ranges.for_each_n.pass.cpp         | 37 +++++++++++++++++++
 .../alg.copy/copy_n.pass.cpp                  | 16 ++++++++
 .../alg.copy/ranges.copy_n.pass.cpp           | 17 +++++++++
 .../alg.fill/fill_n.pass.cpp                  | 15 ++++++++
 .../alg.fill/ranges.fill_n.pass.cpp           | 15 ++++++++
 .../alg.generate/generate_n.pass.cpp          |  7 ++++
 .../alg.generate/ranges_generate_n.pass.cpp   | 15 +++++++-
 13 files changed, 181 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
 create mode 100644 libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index ac52b2b686c28..a0ea02121b70b 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -42,7 +42,7 @@ template <class _AlgPolicy,
           __enable_if_t<!__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
-  while (__n != 0) {
+  while (__n > 0) {
     *__result = *__first;
     ++__first;
     ++__result;
@@ -63,14 +63,15 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted > 0) {
+  if (__converted < 0) [[__unlikely__]]
+    return __result;
+
+  *__result = *__first;
+  ++__result;
+  for (--__converted; __converted > 0; --__converted) {
+    ++__first;
     *__result = *__first;
     ++__result;
-    for (--__converted; __converted > 0; --__converted) {
-      ++__first;
-      *__result = *__first;
-      ++__result;
-    }
   }
   return __result;
 }
@@ -83,6 +84,8 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
+  if (__converted < 0) [[__unlikely__]]
+    return __result;
   return std::__copy_n<_ClassicAlgPolicy>(__first, __iterator_difference_type<_InputIterator>(__converted), __result)
       .__out_;
 }
diff --git a/libcxx/include/__algorithm/for_each_n.h b/libcxx/include/__algorithm/for_each_n.h
index 7ff3f9b157c02..9b393b307f323 100644
--- a/libcxx/include/__algorithm/for_each_n.h
+++ b/libcxx/include/__algorithm/for_each_n.h
@@ -12,6 +12,7 @@
 
 #include <__algorithm/for_each.h>
 #include <__algorithm/for_each_n_segment.h>
+#include <__assert>
 #include <__config>
 #include <__functional/identity.h>
 #include <__iterator/iterator_traits.h>
@@ -63,6 +64,7 @@ __for_each_n(_InputIterator __first, _Size __orig_n, _Func&& __f, _Proj& __proj)
 template <class _InputIterator, class _Size, class _Func>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
 for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__orig_n >= 0, "for_each_n requires a non-negative count");
   __identity __proj;
   return std::__for_each_n(__first, __orig_n, __f, __proj);
 }
diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
index 1720ac358fa4d..c153e74009801 100644
--- a/libcxx/include/__algorithm/ranges_copy_n.h
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -38,6 +38,8 @@ struct __copy_n {
     requires indirectly_copyable<_Ip, _Op>
   _LIBCPP_HIDE_FROM_ABI constexpr copy_n_result<_Ip, _Op>
   operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const {
+    if (__n < 0) [[__unlikely__]]
+      return {std::move(__first), std::move(__result)};
     return std::__copy_n<_RangeAlgPolicy>(std::move(__first), __n, std::move(__result));
   }
 };
diff --git a/libcxx/include/__algorithm/ranges_for_each_n.h b/libcxx/include/__algorithm/ranges_for_each_n.h
index 3aab1b79c10a1..126dc481ec723 100644
--- a/libcxx/include/__algorithm/ranges_for_each_n.h
+++ b/libcxx/include/__algorithm/ranges_for_each_n.h
@@ -11,6 +11,7 @@
 
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/in_fun_result.h>
+#include <__assert>
 #include <__config>
 #include <__functional/identity.h>
 #include <__iterator/concepts.h>
@@ -40,6 +41,7 @@ struct __for_each_n {
   template <input_iterator _Iter, class _Proj = identity, indirectly_unary_invocable<projected<_Iter, _Proj>> _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr for_each_n_result<_Iter, _Func>
   operator()(_Iter __first, iter_difference_t<_Iter> __count, _Func __func, _Proj __proj = {}) const {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count >= 0, "for_each_n requires a non-negative count");
     auto __last = std::__for_each_n(std::move(__first), __count, __func, __proj);
     return {std::move(__last), std::move(__func)};
   }
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 692686f1584d0..0083714b2cafe 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -424,8 +424,8 @@ public:
   }
 
 #if _LIBCPP_STD_VER <= 17
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool
-  operator!=(const __bit_iterator& __x, const __bit_iterator& __y) {
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool operator!=(const __bit_iterator& __x, const __bit_iterator& __y) {
     return !(__x == __y);
   }
 
@@ -573,12 +573,13 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n > 0) {
-      if (__value)
-        __impl<true>(__first, __n);
-      else
-        __impl<false>(__first, __n);
-    }
+    if (__n < 0) [[__unlikely__]]
+      return __first;
+
+    if (__value)
+      __impl<true>(__first, __n);
+    else
+      __impl<false>(__first, __n);
     return __first + __n;
   }
 };
diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
new file mode 100644
index 0000000000000..53e59401430fa
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<class InputIterator, class Size, class Function>
+//    constexpr InputIterator
+//    for_each_n(InputIterator first, Size n, Function f);
+//
+// [alg.foreach] requires `n >= 0`; passing a negative count is a precondition violation.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <algorithm>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  int a[] = {1, 2, 3};
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp
new file mode 100644
index 0000000000000..e57f342e760a6
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<input_iterator I, class Proj = identity,
+//          indirectly_unary_invocable<projected<I, Proj>> Fun>
+//   constexpr ranges::for_each_n_result<I, Fun>
+//     ranges::for_each_n(I first, iter_difference_t<I> n, Fun f, Proj proj = {});
+//
+// [alg.foreach] requires `n >= 0`; passing a negative count is a precondition violation.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <algorithm>
+#include <array>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  std::array a = {1, 2, 3};
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::ranges::for_each_n(a.begin(), -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::ranges::for_each_n(a.begin(), -10000000, [](int) {}), "for_each_n requires a non-negative count");
+
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 183f38c1ce117..52d99f018110b 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -61,6 +61,15 @@ struct TestIterators {
       assert(base(r) == ib + N / 2);
       for (unsigned i = 0; i < N / 2; ++i)
         assert(ia[i] == ib[i]);
+
+      { // A negative count is a no-op that returns the unchanged output iterator.
+        // Regression test for https://llvm.org/PR193613.
+        int source[]  = {1, 2, 3};
+        int dest[]    = {-1, -2, -3};
+        OutIter ret   = std::copy_n(InIter(source), -5, OutIter(dest));
+        assert(base(ret) == dest);
+        assert(dest[0] == -1 && dest[1] == -2 && dest[2] == -3);
+      }
     }
   };
 };
@@ -81,6 +90,13 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
     for (std::size_t i = 0; i < N; ++i)
       assert(out[i + 4] == in[i]);
   }
+  { // Negative count test
+    std::vector<bool> source(N, true);
+    std::vector<bool> dest(N, false);
+    std::vector<bool>::iterator r = std::copy_n(source.begin(), -5, dest.begin());
+    assert(r == dest.begin());
+    assert(dest == std::vector<bool>(N, false));
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
index 132993dba76b9..5e073026ed8db 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
@@ -60,6 +60,16 @@ constexpr void test_iterators() {
     assert(base(ret.in) == in.data());
     assert(base(ret.out) == out.data());
   }
+
+  { // A negative count is a no-op that returns the unchanged iterators.
+    // Regression test for https://llvm.org/PR193613.
+    std::array in{1, 2, 3};
+    std::array out{-1, -2, -3};
+    std::same_as<std::ranges::in_out_result<In, Out>> auto ret = std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
+    assert(base(ret.in) == in.data());
+    assert(base(ret.out) == out.data());
+    assert((out == std::array{-1, -2, -3}));
+  }
 }
 
 #if TEST_STD_VER >= 23
@@ -79,6 +89,13 @@ constexpr bool test_vector_bool(std::size_t N) {
     for (std::size_t i = 0; i < N; ++i)
       assert(out[i + 4] == in[i]);
   }
+  { // Negative count test
+    std::vector<bool> source(N, true);
+    std::vector<bool> dest(N, false);
+    auto ret = std::ranges::copy_n(source.begin(), -5, dest.begin());
+    assert(ret.out == dest.begin());
+    assert(dest == std::vector<bool>(N, false));
+  }
 
   return true;
 };
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
index 6d7ba98a33b65..cc9aaeb04d7e8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
@@ -52,6 +52,14 @@ struct Test {
       std::array<T, 4> expected = {1, 5, 5, 4};
       test<Iter>(in, 1, 2, 5, expected);
     }
+    { // A negative count is a no-op that returns the unchanged iterator.
+      // Regression test for https://llvm.org/PR193613.
+      std::array<T, 4> in       = {1, 2, 3, 4};
+      std::array<T, 4> expected = {1, 2, 3, 4};
+      Iter it                   = std::fill_n(Iter(in.data()), -5, T(9));
+      assert(base(it) == in.data());
+      assert(in == expected);
+    }
   }
 };
 
@@ -126,6 +134,13 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
     }
   }
 
+  { // Negative count test
+    std::vector<bool> v(N, false);
+    std::vector<bool>::iterator r = std::fill_n(v.begin(), -5, true);
+    assert(r == v.begin());
+    assert(v == std::vector<bool>(N, false));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
index 0de6f4a625136..a8eb6c4eb9737 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
@@ -51,6 +51,14 @@ constexpr void test_iterators() {
     auto ret = std::ranges::fill_n(It(a.data()), 0, 1);
     assert(base(ret) == a.data());
   }
+
+  { // A negative count is a no-op that returns the unchanged iterator.
+    // Regression test for https://llvm.org/PR193613.
+    int a[3]                            = {-1, -2, -3};
+    std::same_as<It> decltype(auto) ret = std::ranges::fill_n(It(a), -5, 1);
+    assert(base(ret) == a);
+    assert(a[0] == -1 && a[1] == -2 && a[2] == -3);
+  }
 }
 
 // The `ranges::{fill, fill_n}` algorithms require `vector<bool, Alloc>::iterator` to satisfy
@@ -99,6 +107,13 @@ constexpr bool test_vector_bool(std::size_t N) {
     }
   }
 
+  { // Negative count test
+    std::vector<bool> v(N, false);
+    auto r = std::ranges::fill_n(v.begin(), -5, true);
+    assert(r == v.begin());
+    assert(v == std::vector<bool>(N, false));
+  }
+
   return true;
 }
 #endif
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
index 525737ceacfbf..0e38a89613aa8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
@@ -70,6 +70,13 @@ test()
     test2<Iter, float>();
     test2<Iter, double>();  // this is PR#35498
     test2<Iter, long double>();
+
+    { // A negative count is a no-op that returns the unchanged iterator.
+      // Regression test for https://llvm.org/PR193613.
+      int ia[] = {1, 2, 3};
+      assert(std::generate_n(Iter(ia), -5, gen_test()) == Iter(ia));
+      assert(ia[0] == 1 && ia[1] == 2 && ia[2] == 3);
+    }
 }
 
 void deque_test() {
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
index 33d40a39cf385..6154ab240c3d6 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
@@ -92,6 +92,20 @@ constexpr void test_iter() {
   test_one<Iter>(std::array{-10, -20, -30, -40, -50}, 3, gen, {1, 2, 3, -40, -50});
   // Longer sequence, n = 5.
   test_one<Iter>(std::array<int, 5>{}, 5, gen, {1, 2, 3, 4, 5});
+
+  { // A negative count is a no-op that returns the unchanged iterator.
+    // Regression test for https://llvm.org/PR193613.
+    std::array in{-10, -20, -30};
+    int called        = 0;
+    auto counting_gen = [&called] {
+      ++called;
+      return 42;
+    };
+    std::same_as<Iter> decltype(auto) result = std::ranges::generate_n(Iter(in.data()), -5, counting_gen);
+    assert(base(result) == in.data());
+    assert(called == 0);
+    assert((in == std::array{-10, -20, -30}));
+  }
 }
 
 constexpr void test_iterators() {
@@ -131,7 +145,6 @@ constexpr bool test() {
     assert(gen_invocations == N2);
   }
 
-
   return true;
 }
 

>From 892279c90e066c4af5e0ef600fe47df0ba9dbf9e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 17:31:39 -0400
Subject: [PATCH 02/11] Fix check

---
 libcxx/include/__bit_reference | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 0083714b2cafe..b07c7ddccd5a0 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -573,7 +573,7 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n < 0) [[__unlikely__]]
+    if (__n <= 0) [[__unlikely__]]
       return __first;
 
     if (__value)

>From 87de9ba731d496c0900154847a077591dde9c906 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 17:36:19 -0400
Subject: [PATCH 03/11] Revert to != 0

---
 libcxx/include/__algorithm/copy_n.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index a0ea02121b70b..7ce0ec53b0c53 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -42,7 +42,7 @@ template <class _AlgPolicy,
           __enable_if_t<!__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
-  while (__n > 0) {
+  while (__n != 0) {
     *__result = *__first;
     ++__first;
     ++__result;

>From 86c6915b6d53b0ffde7b3a2ccb197b05f28c85ee Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 12:28:02 -0400
Subject: [PATCH 04/11] Drop [[__unlikely__]]

---
 libcxx/include/__algorithm/copy_n.h        | 4 ++--
 libcxx/include/__algorithm/ranges_copy_n.h | 2 +-
 libcxx/include/__bit_reference             | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index 7ce0ec53b0c53..7f9a109a763ce 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -63,7 +63,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0) [[__unlikely__]]
+  if (__converted < 0)
     return __result;
 
   *__result = *__first;
@@ -84,7 +84,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0) [[__unlikely__]]
+  if (__converted < 0)
     return __result;
   return std::__copy_n<_ClassicAlgPolicy>(__first, __iterator_difference_type<_InputIterator>(__converted), __result)
       .__out_;
diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
index c153e74009801..7baf1932bb3d8 100644
--- a/libcxx/include/__algorithm/ranges_copy_n.h
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -38,7 +38,7 @@ struct __copy_n {
     requires indirectly_copyable<_Ip, _Op>
   _LIBCPP_HIDE_FROM_ABI constexpr copy_n_result<_Ip, _Op>
   operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const {
-    if (__n < 0) [[__unlikely__]]
+    if (__n < 0)
       return {std::move(__first), std::move(__result)};
     return std::__copy_n<_RangeAlgPolicy>(std::move(__first), __n, std::move(__result));
   }
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index b07c7ddccd5a0..23ff6eaf56684 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -573,7 +573,7 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n <= 0) [[__unlikely__]]
+    if (__n <= 0)
       return __first;
 
     if (__value)

>From 6088d6dfa8ef53749309e627c85783bedb3ab248 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 12:33:12 -0400
Subject: [PATCH 05/11] Format

---
 .../alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp | 6 ++----
 .../alg.modifying.operations/alg.copy/copy_n.pass.cpp       | 6 +++---
 .../alg.copy/ranges.copy_n.pass.cpp                         | 3 ++-
 3 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
index 53e59401430fa..5ae0dbc865b46 100644
--- a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
@@ -26,10 +26,8 @@
 int main(int, char**) {
   int a[] = {1, 2, 3};
 
-  TEST_LIBCPP_ASSERT_FAILURE(
-      std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
-  TEST_LIBCPP_ASSERT_FAILURE(
-      std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
 
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 52d99f018110b..3348fcf358580 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -64,9 +64,9 @@ struct TestIterators {
 
       { // A negative count is a no-op that returns the unchanged output iterator.
         // Regression test for https://llvm.org/PR193613.
-        int source[]  = {1, 2, 3};
-        int dest[]    = {-1, -2, -3};
-        OutIter ret   = std::copy_n(InIter(source), -5, OutIter(dest));
+        int source[] = {1, 2, 3};
+        int dest[]   = {-1, -2, -3};
+        OutIter ret  = std::copy_n(InIter(source), -5, OutIter(dest));
         assert(base(ret) == dest);
         assert(dest[0] == -1 && dest[1] == -2 && dest[2] == -3);
       }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
index 5e073026ed8db..c354076b63363 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
@@ -65,7 +65,8 @@ constexpr void test_iterators() {
     // Regression test for https://llvm.org/PR193613.
     std::array in{1, 2, 3};
     std::array out{-1, -2, -3};
-    std::same_as<std::ranges::in_out_result<In, Out>> auto ret = std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
+    std::same_as<std::ranges::in_out_result<In, Out>> auto ret =
+        std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
     assert(base(ret.in) == in.data());
     assert(base(ret.out) == out.data());
     assert((out == std::array{-1, -2, -3}));

>From d29c394231476873ae920ceaa26d498a3a96eca5 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 12:57:25 -0400
Subject: [PATCH 06/11] [libc++] Fix bug in std::copy_n for n == 0

---
 libcxx/include/__algorithm/copy_n.h           |  2 +-
 .../alg.copy/copy_n.pass.cpp                  | 50 +++++++++++++++++++
 .../alg.fill/fill_n.pass.cpp                  |  2 +
 3 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index 7f9a109a763ce..efd5624d94f67 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -63,7 +63,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0)
+  if (__converted <= 0)
     return __result;
 
   *__result = *__first;
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 3348fcf358580..3e673cee21f69 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -12,9 +12,12 @@
 //   constexpr OutIter   // constexpr after C++17
 //   copy_n(InIter first, InIter::difference_type n, OutIter result);
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 #include <algorithm>
 #include <cassert>
 #include <cstdint>
+#include <iterator>
 #include <vector>
 
 #include "test_macros.h"
@@ -24,6 +27,31 @@
 
 typedef UserDefinedIntegral<unsigned> UDI;
 
+// A minimal single-pass input iterator that counts how many times it is advanced. Used to verify that
+// copy_n reads exactly n elements (advancing the iterator only n - 1 times).
+struct CountingInputIterator {
+  using iterator_category = std::input_iterator_tag;
+  using value_type        = int;
+  using difference_type   = long;
+  using pointer           = const int*;
+  using reference         = const int&;
+
+  const int* base_;
+  int* increments_;
+
+  TEST_CONSTEXPR_CXX14 reference operator*() const { return *base_; }
+  TEST_CONSTEXPR_CXX14 CountingInputIterator& operator++() {
+    ++base_;
+    ++*increments_;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX14 CountingInputIterator operator++(int) {
+    CountingInputIterator __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+};
+
 class PaddedBase {
 public:
   TEST_CONSTEXPR PaddedBase(std::int16_t a, std::int8_t b) : a_(a), b_(b) {}
@@ -130,6 +158,28 @@ TEST_CONSTEXPR_CXX20 bool test() {
     assert(test_vector_bool(256));
   }
 
+  { // For a single-pass input iterator, copy_n reads exactly n elements -- advancing the iterator only
+    // n - 1 times. For n == 0, it shouldn't advance the iterator. See 99847d2bf132.
+    int in[] = {1, 2, 3, 4, 5};
+
+    { // n == 0 is a no-op
+      int out[3]     = {-1, -2, -3};
+      int increments = 0;
+      int* r         = std::copy_n(CountingInputIterator{in, &increments}, 0, out);
+      assert(r == out);
+      assert(increments == 0);
+      assert(out[0] == -1 && out[1] == -2 && out[2] == -3);
+    }
+    { // n > 0 advances the iterator exactly n - 1 times
+      int out[3]     = {0, 0, 0};
+      int increments = 0;
+      int* r         = std::copy_n(CountingInputIterator{in, &increments}, 3, out);
+      assert(r == out + 3);
+      assert(increments == 2);
+      assert(out[0] == 1 && out[1] == 2 && out[2] == 3);
+    }
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
index cc9aaeb04d7e8..5a9214923577a 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
@@ -13,6 +13,8 @@
 //   constexpr OutputIterator      // constexpr after C++17
 //   fill_n(Iter first, Size n, const T& value);
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 #include <algorithm>
 #include <array>
 #include <cassert>

>From f23831825ac3423d00dd02265b6ffc53e8776b32 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 13:53:09 -0400
Subject: [PATCH 07/11] Move checks to implementations instead of public APIs,
 consistently with what we do elsewhere

---
 libcxx/include/__algorithm/copy_n.h        | 19 +++++++++----------
 libcxx/include/__algorithm/generate_n.h    |  3 +++
 libcxx/include/__algorithm/ranges_copy_n.h |  2 --
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index efd5624d94f67..d0df73a405cfa 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -33,6 +33,8 @@ template <class _AlgPolicy,
           __enable_if_t<__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
+  if (__n <= 0)
+    return __in_out_result<_InIter, _OutIter>{std::move(__first), std::move(__result)};
   return std::__copy(__first, __first + __n, std::move(__result));
 }
 
@@ -42,7 +44,7 @@ template <class _AlgPolicy,
           __enable_if_t<!__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
-  while (__n != 0) {
+  while (__n > 0) {
     *__result = *__first;
     ++__first;
     ++__result;
@@ -63,15 +65,14 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted <= 0)
-    return __result;
-
-  *__result = *__first;
-  ++__result;
-  for (--__converted; __converted > 0; --__converted) {
-    ++__first;
+  if (__converted > 0) {
     *__result = *__first;
     ++__result;
+    for (--__converted; __converted > 0; --__converted) {
+      ++__first;
+      *__result = *__first;
+      ++__result;
+    }
   }
   return __result;
 }
@@ -84,8 +85,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0)
-    return __result;
   return std::__copy_n<_ClassicAlgPolicy>(__first, __iterator_difference_type<_InputIterator>(__converted), __result)
       .__out_;
 }
diff --git a/libcxx/include/__algorithm/generate_n.h b/libcxx/include/__algorithm/generate_n.h
index ba04eb62e3c3a..8ce59ca3caf68 100644
--- a/libcxx/include/__algorithm/generate_n.h
+++ b/libcxx/include/__algorithm/generate_n.h
@@ -27,6 +27,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _OutputIterator, class _Size, class _Generator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 __generate_n(_OutputIterator __first, _Size __orig_n, _Generator& __gen) {
+  if (__orig_n <= 0)
+    return __first;
+
   using __iter_ref = decltype(*__first);
   __identity __proj;
   return std::__for_each_n(
diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
index 7baf1932bb3d8..1720ac358fa4d 100644
--- a/libcxx/include/__algorithm/ranges_copy_n.h
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -38,8 +38,6 @@ struct __copy_n {
     requires indirectly_copyable<_Ip, _Op>
   _LIBCPP_HIDE_FROM_ABI constexpr copy_n_result<_Ip, _Op>
   operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const {
-    if (__n < 0)
-      return {std::move(__first), std::move(__result)};
     return std::__copy_n<_RangeAlgPolicy>(std::move(__first), __n, std::move(__result));
   }
 };

>From 75284e73648df453097af3f12f6db89e698f938d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 9 Jul 2026 13:02:56 -0400
Subject: [PATCH 08/11] convert-to-integral

---
 libcxx/include/__algorithm/generate_n.h | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__algorithm/generate_n.h b/libcxx/include/__algorithm/generate_n.h
index 8ce59ca3caf68..fa75fde457b30 100644
--- a/libcxx/include/__algorithm/generate_n.h
+++ b/libcxx/include/__algorithm/generate_n.h
@@ -12,6 +12,7 @@
 #include <__algorithm/for_each_n.h>
 #include <__config>
 #include <__functional/identity.h>
+#include <__utility/convert_to_integral.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 
@@ -27,16 +28,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _OutputIterator, class _Size, class _Generator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 __generate_n(_OutputIterator __first, _Size __orig_n, _Generator& __gen) {
-  if (__orig_n <= 0)
+  auto __n = std::__convert_to_integral(__orig_n);
+  if (__n <= 0)
     return __first;
 
   using __iter_ref = decltype(*__first);
   __identity __proj;
   return std::__for_each_n(
-      std::move(__first),
-      __orig_n,
-      [&](__iter_ref __element) { std::forward<__iter_ref>(__element) = __gen(); },
-      __proj);
+      std::move(__first), __n, [&](__iter_ref __element) { std::forward<__iter_ref>(__element) = __gen(); }, __proj);
 }
 
 template <class _OutputIterator, class _Size, class _Generator>

>From 2551194ccb173b80697913e022e2fff48bef2201 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 9 Jul 2026 13:04:55 -0400
Subject: [PATCH 09/11] convert to integral in the public API instead, which
 seems more consistent with the codebase

---
 libcxx/include/__algorithm/generate_n.h | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__algorithm/generate_n.h b/libcxx/include/__algorithm/generate_n.h
index fa75fde457b30..693cd50ced2b1 100644
--- a/libcxx/include/__algorithm/generate_n.h
+++ b/libcxx/include/__algorithm/generate_n.h
@@ -27,8 +27,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _OutputIterator, class _Size, class _Generator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
-__generate_n(_OutputIterator __first, _Size __orig_n, _Generator& __gen) {
-  auto __n = std::__convert_to_integral(__orig_n);
+__generate_n(_OutputIterator __first, _Size __n, _Generator& __gen) {
   if (__n <= 0)
     return __first;
 
@@ -41,7 +40,7 @@ __generate_n(_OutputIterator __first, _Size __orig_n, _Generator& __gen) {
 template <class _OutputIterator, class _Size, class _Generator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 generate_n(_OutputIterator __first, _Size __orig_n, _Generator __gen) {
-  return std::__generate_n(std::move(__first), __orig_n, __gen);
+  return std::__generate_n(std::move(__first), std::__convert_to_integral(__orig_n), __gen);
 }
 
 _LIBCPP_END_NAMESPACE_STD

>From d5fdea4c7ace67bddc39136020ef9b7a47db1a75 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 9 Jul 2026 13:08:37 -0400
Subject: [PATCH 10/11] Convert to integral in for_each_n too, needed for
 hardening

---
 libcxx/include/__algorithm/for_each_n.h | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__algorithm/for_each_n.h b/libcxx/include/__algorithm/for_each_n.h
index 9b393b307f323..2b9ba4a6bc983 100644
--- a/libcxx/include/__algorithm/for_each_n.h
+++ b/libcxx/include/__algorithm/for_each_n.h
@@ -33,15 +33,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _InputIterator, class _Size, class _Func, class _Proj>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
-__for_each_n(_InputIterator __first, _Size __orig_n, _Func&& __f, _Proj& __proj) {
-  typedef decltype(std::__convert_to_integral(__orig_n)) _IntegralSize;
-  _IntegralSize __n = __orig_n;
-
+__for_each_n(_InputIterator __first, _Size __n, _Func&& __f, _Proj& __proj) {
 #ifndef _LIBCPP_CXX03_LANG
   if constexpr (__is_segmented_iterator_v<_InputIterator>) {
     using __local_iterator = typename __segmented_iterator_traits<_InputIterator>::__local_iterator;
     if constexpr (__has_random_access_iterator_category<__local_iterator>::value) {
-      return std::__for_each_n_segment(__first, __orig_n, [&](__local_iterator __lfirst, __local_iterator __llast) {
+      return std::__for_each_n_segment(__first, __n, [&](__local_iterator __lfirst, __local_iterator __llast) {
         std::__for_each(__lfirst, __llast, __f, __proj);
       });
     } else {
@@ -62,11 +59,12 @@ __for_each_n(_InputIterator __first, _Size __orig_n, _Func&& __f, _Proj& __proj)
 #if _LIBCPP_STD_VER >= 17
 
 template <class _InputIterator, class _Size, class _Func>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
-for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
-  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__orig_n >= 0, "for_each_n requires a non-negative count");
+inline _LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
+  auto __n = std::__convert_to_integral(__orig_n);
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n >= 0, "for_each_n requires a non-negative count");
   __identity __proj;
-  return std::__for_each_n(__first, __orig_n, __f, __proj);
+  return std::__for_each_n(__first, __n, __f, __proj);
 }
 
 #endif // _LIBCPP_STD_VER >= 17

>From b947ba1a1f74dbf2353110ea76c910bef05dbdb4 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 14 Jul 2026 07:56:03 -0400
Subject: [PATCH 11/11] Avoid C++03 error

---
 libcxx/include/__algorithm/copy_n.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index d0df73a405cfa..69ce09de9b7da 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -34,7 +34,7 @@ template <class _AlgPolicy,
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
   if (__n <= 0)
-    return __in_out_result<_InIter, _OutIter>{std::move(__first), std::move(__result)};
+    return {std::move(__first), std::move(__result)};
   return std::__copy(__first, __first + __n, std::move(__result));
 }
 


From libcxx-commits at lists.llvm.org  Tue Jul 14 04:59:44 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 04:59:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 98c4ea4 - [libc++] Improve diagnostic for
 libcxxabi missing from the runtimes (#208274)
Message-ID: <6a5624b0.b1416da1.78d1e.5d10@mx.google.com>


Author: Louis Dionne
Date: 2026-07-14T07:59:40-04:00
New Revision: 98c4ea46bd474bff97c99be0800bd7b310185a29

URL: https://github.com/llvm/llvm-project/commit/98c4ea46bd474bff97c99be0800bd7b310185a29
DIFF: https://github.com/llvm/llvm-project/commit/98c4ea46bd474bff97c99be0800bd7b310185a29.diff

LOG: [libc++] Improve diagnostic for libcxxabi missing from the runtimes (#208274)

When libc++ is configured to use libc++abi as an ABI library but
libcxxabi is not part of the runtimes, we'd end up with a confusing
build error about a missing <cxxabi.h> header. Instead, diagnose at
CMake configuration time.

Closes #136480

Added: 
    

Modified: 
    libcxx/cmake/Modules/HandleLibCXXABI.cmake

Removed: 
    


################################################################################
diff  --git a/libcxx/cmake/Modules/HandleLibCXXABI.cmake b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
index 52236f473f35d..f823db8d1ff14 100644
--- a/libcxx/cmake/Modules/HandleLibCXXABI.cmake
+++ b/libcxx/cmake/Modules/HandleLibCXXABI.cmake
@@ -148,6 +148,13 @@ elseif ("${LIBCXX_CXX_ABI}" STREQUAL "libcxxabi")
     add_library(libcxx-abi-static-objects ALIAS cxxabi_static_objects)
   endif()
 
+  if (NOT TARGET cxxabi_static AND NOT TARGET cxxabi_shared
+      AND NOT TARGET cxxabi_static_objects AND NOT TARGET cxxabi_shared_objects)
+    message(FATAL_ERROR "Can't find any libc++abi target but LIBCXX_CXX_ABI is set to libcxxabi, "
+                        "did you forget to include libcxxabi in LLVM_ENABLE_RUNTIMES, or did you "
+                        "intend to use LIBCXX_CXX_ABI=system-libcxxabi instead?")
+  endif()
+
 # Link against a system-provided libc++abi
 elseif ("${LIBCXX_CXX_ABI}" STREQUAL "system-libcxxabi")
   if(NOT LIBCXX_CXX_ABI_INCLUDE_PATHS)


        

From libcxx-commits at lists.llvm.org  Tue Jul 14 04:59:48 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 04:59:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Improve diagnostic for libcxxabi
 missing from the runtimes (PR #208274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208274@github.com>
Message-ID: <6a5624b4.d1496011.26913c.e78a@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208274

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:03:30 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 05:03:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as complete (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a562592.8e75e0cb.3dbce4.35b2@mx.google.com>


ldionne wrote:

The PR description looks fine IMO! Merging, thanks!

https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:04:32 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 05:04:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 23b3f21 - [libc++] Mark LWG3403 as
	complete (#208866)
Message-ID: <6a5625d0.6365b89b.8575e.ce44@mx.google.com>


Author: inquisitivecrystal
Date: 2026-07-14T08:04:27-04:00
New Revision: 23b3f21fd0fff64644cf60065b971686aab9c1b3

URL: https://github.com/llvm/llvm-project/commit/23b3f21fd0fff64644cf60065b971686aab9c1b3
DIFF: https://github.com/llvm/llvm-project/commit/23b3f21fd0fff64644cf60065b971686aab9c1b3.diff

LOG: [libc++] Mark LWG3403 as complete (#208866)

LWG3403 specifies that `ranges::ssize` should work on non-range objects
for which `ranges::size` is valid. This has been true in libc++ since
6f1b10d, which first implemented `ranges::ssize` for libc++13. The tests
introduced by that commit are sufficient to verify compliance since they
test calling `ranges::ssize` with various non-range arguments.

In addition to marking LWG3403 as complete, this also updates the status
page entry for P1970R2, the paper that added `ranges::ssize`. P1970R2 was
previously marked complete in libc++15 as an approximation; however, it was
actually complete in libc++13.

Resolves #104280

Added: 
    

Modified: 
    libcxx/docs/Status/Cxx20Papers.csv
    libcxx/docs/Status/Cxx23Issues.csv

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 3d6558ce90ef3..5539767ab800b 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -175,7 +175,7 @@
 "`P1957R2 <https://wg21.link/P1957R2>`__","Converting from ``T*``\  to bool should be considered narrowing (re: US 212)","2020-02 (Prague)","|Complete|","18","`#104205 <https://github.com/llvm/llvm-project/issues/104205>`__",""
 "`P1963R0 <https://wg21.link/P1963R0>`__","P1963R0: Fixing US 313","2020-02 (Prague)","|Nothing To Do|","","`#100039 <https://github.com/llvm/llvm-project/issues/100039>`__",""
 "`P1964R2 <https://wg21.link/P1964R2>`__","Wording for boolean-testable","2020-02 (Prague)","|Complete|","13","`#104206 <https://github.com/llvm/llvm-project/issues/104206>`__",""
-"`P1970R2 <https://wg21.link/P1970R2>`__","Consistency for size() functions: Add ranges::ssize","2020-02 (Prague)","|Complete|","15","`#104207 <https://github.com/llvm/llvm-project/issues/104207>`__",""
+"`P1970R2 <https://wg21.link/P1970R2>`__","Consistency for size() functions: Add ranges::ssize","2020-02 (Prague)","|Complete|","13","`#104207 <https://github.com/llvm/llvm-project/issues/104207>`__",""
 "`P1973R1 <https://wg21.link/P1973R1>`__","Rename ""_default_init"" Functions, Rev1","2020-02 (Prague)","|Complete|","16","`#104208 <https://github.com/llvm/llvm-project/issues/104208>`__","The feature-test macro was not set until LLVM 20."
 "`P1976R2 <https://wg21.link/P1976R2>`__","Fixed-size span construction from dynamic range","2020-02 (Prague)","|Complete|","11","`#104209 <https://github.com/llvm/llvm-project/issues/104209>`__",""
 "`P1981R0 <https://wg21.link/P1981R0>`__","Rename leap to leap_second","2020-02 (Prague)","|Complete|","19","`#104210 <https://github.com/llvm/llvm-project/issues/104210>`__",""

diff  --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index d0616fb46681c..91c4dc462c0c9 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -19,7 +19,7 @@
 "`LWG3036 <https://wg21.link/LWG3036>`__","``polymorphic_allocator::destroy`` is extraneous","2020-11 (Virtual)","|Nothing To Do|","","`#104276 <https://github.com/llvm/llvm-project/issues/104276>`__","Reverted by P2875R4"
 "`LWG3171 <https://wg21.link/LWG3171>`__","LWG2989 breaks ``directory_entry`` stream insertion","2020-11 (Virtual)","|Complete|","14","`#104277 <https://github.com/llvm/llvm-project/issues/104277>`__",""
 "`LWG3306 <https://wg21.link/LWG3306>`__","``ranges::advance`` violates its preconditions","2020-11 (Virtual)","|Complete|","14","`#104279 <https://github.com/llvm/llvm-project/issues/104279>`__",""
-"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","","","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
+"`LWG3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","2020-11 (Virtual)","|Complete|","13","`#104280 <https://github.com/llvm/llvm-project/issues/104280>`__",""
 "`LWG3404 <https://wg21.link/LWG3404>`__","Finish removing subrange's conversions from pair-like","2020-11 (Virtual)","|Complete|","13","`#104282 <https://github.com/llvm/llvm-project/issues/104282>`__",""
 "`LWG3405 <https://wg21.link/LWG3405>`__","``common_view``'s converting constructor is bad, too","2020-11 (Virtual)","|Complete|","14","`#104283 <https://github.com/llvm/llvm-project/issues/104283>`__",""
 "`LWG3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","2020-11 (Virtual)","|Complete|","16","`#104284 <https://github.com/llvm/llvm-project/issues/104284>`__",""


        

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:04:35 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 05:04:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Mark LWG3403 as complete (PR
	#208866)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208866@github.com>
Message-ID: <6a5625d3.eb165e5c.1817a5.8512@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208866

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:08:46 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 05:08:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove ClangTidyModuleRegistry.h
 from the clang-tidy checks (PR #206227)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206227@github.com>
Message-ID: <6a5626ce.13be6df8.145c.32fa@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/206227

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:10:55 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 05:10:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][utils] Fix LIT `%if`
	substitution support (PR #209358)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209358@github.com>
Message-ID: <6a56274f.d681cab9.333b2b.0186@mx.google.com>


================
@@ -0,0 +1,11 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that LIT `%if` substitutions are aware of the available features.
+
+// RUN: not %if libcpp-abi-version={{[0-9]+}} %{ not %} echo "hello world"
----------------
ldionne wrote:

Let's use a Lit feature that is always available, even when we're not testing libc++ (e.g. when we're testing MSVC STL). We could do e.g. `RUN: not %if target={{.+}} %{ not %} echo "hello world"` since the target feature should always be set.

https://github.com/llvm/llvm-project/pull/209358

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:14:31 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 05:14:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a562827.eb165e5c.1817a5.923e@mx.google.com>


================

----------------
ldionne wrote:

The idea behind these benchmarks was that we were batching the operations exactly so we could then afford to Pause and Resume. If we don't do that, then we're basically benchmarking both `push_back` and `erase`/`clear`, not only `push_back`. I think we need to address this differently.

Maybe we could have 10-20 vectors that we all `reserve` and then `push_back` into, *and then* we pause, erase, and resume?

https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:15:32 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 05:15:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Use the same .clang-format
	as libc++ (PR #208661)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208661@github.com>
Message-ID: <6a562864.b1416da1.78d1e.4657@mx.google.com>


================
@@ -1,14 +1,81 @@
 BasedOnStyle: LLVM
+# Note that we don't specify the language in this file because some files are
----------------
ldionne wrote:

Does that make this file an exact copy of libc++'s? If so, can we perhaps symlink instead?

https://github.com/llvm/llvm-project/pull/208661

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:20:19 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 05:20:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Inline std::function
 members into the class body (PR #209461)
Message-ID: <llvm.org/llvm/llvm-project/pull/209461@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/209461

The `std::function` members are incredibly simple to the point where they are just wrappers around internal functions. We can inline them into the body to simplify the implementation a bit.


>From e5a79c1e15177ac279884a14eff84e0ff8ddfa56 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 14 Jul 2026 14:18:25 +0200
Subject: [PATCH] [libc++][NFC] Inline std::function members into the class
 body

---
 libcxx/include/__functional/function.h | 141 +++++++------------------
 1 file changed, 37 insertions(+), 104 deletions(-)

diff --git a/libcxx/include/__functional/function.h b/libcxx/include/__functional/function.h
index 90072e9528484..0adc336424e2e 100644
--- a/libcxx/include/__functional/function.h
+++ b/libcxx/include/__functional/function.h
@@ -340,7 +340,7 @@ template <typename _Fun>
 struct __use_small_storage
     : public integral_constant<
           bool,
-          sizeof(_Fun) <= sizeof(__policy_storage)&& _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
+          sizeof(_Fun) <= sizeof(__policy_storage) && _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
               is_trivially_copy_constructible<_Fun>::value && is_trivially_destructible<_Fun>::value> {};
 
 // Policy contains information about how to copy, destroy, and move the
@@ -628,10 +628,10 @@ class function<_Rp(_ArgTypes...)>
   // construct/copy/destroy:
   _LIBCPP_HIDE_FROM_ABI function() _NOEXCEPT {}
   _LIBCPP_HIDE_FROM_ABI function(nullptr_t) _NOEXCEPT {}
-  _LIBCPP_HIDE_FROM_ABI function(const function&);
-  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function(const function&)      = default;
+  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT = default;
   template <class _Fp, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(_Fp);
+  _LIBCPP_HIDE_FROM_ABI function(_Fp __f) : __f_(std::move(__f)) {}
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Alloc>
@@ -639,23 +639,35 @@ class function<_Rp(_ArgTypes...)>
   template <class _Alloc>
   _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, nullptr_t) _NOEXCEPT {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function&);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&&);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
   template <class _Fp, class _Alloc, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc& __a, _Fp __f);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI function& operator=(const function&);
-  _LIBCPP_HIDE_FROM_ABI function& operator=(function&&) _NOEXCEPT;
-  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function& operator=(const function& __f) {
+    function(__f).swap(*this);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI function& operator=(function&& __f) _NOEXCEPT = default;
+
+  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT {
+    __f_ = nullptr;
+    return *this;
+  }
+
   template <class _Fp, class = _EnableIfLValueCallable<__decay_t<_Fp>>>
-  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&&);
+  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&& __f) {
+    function(std::forward<_Fp>(__f)).swap(*this);
+    return *this;
+  }
 
-  _LIBCPP_HIDE_FROM_ABI ~function();
+  _LIBCPP_HIDE_FROM_ABI ~function() = default;
 
   // function modifiers:
-  _LIBCPP_HIDE_FROM_ABI void swap(function&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI void swap(function& __f) _NOEXCEPT { __f_.swap(__f.__f_); }
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Fp, class _Alloc>
@@ -677,15 +689,23 @@ class function<_Rp(_ArgTypes...)>
 
 public:
   // function invocation:
-  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes...) const;
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const { return __f_(std::forward<_ArgTypes>(__args)...); }
 
 #  if _LIBCPP_HAS_RTTI
   // function target access:
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT {
+    return __f_.target_type();
+  }
+
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT {
+    return (_Tp*)(__f_.template target<_Tp>());
+  }
+
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT {
+    return __f_.template target<_Tp>();
+  }
 #  endif // _LIBCPP_HAS_RTTI
 };
 
@@ -697,93 +717,6 @@ template <class _Fp, class _Stripped = __strip_signature_t<decltype(&_Fp::operat
 function(_Fp) -> function<_Stripped>;
 #  endif // _LIBCPP_STD_VER >= 17
 
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::function(const function& __f) : __f_(__f.__f_) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Alloc>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::function(function&& __f) _NOEXCEPT : __f_(std::move(__f.__f_)) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Alloc>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class>
-function<_Rp(_ArgTypes...)>::function(_Fp __f) : __f_(std::move(__f)) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class _Alloc, class>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(const function& __f) {
-  function(__f).swap(*this);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(function&& __f) _NOEXCEPT {
-  __f_ = std::move(__f.__f_);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(nullptr_t) _NOEXCEPT {
-  __f_ = nullptr;
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f) {
-  function(std::forward<_Fp>(__f)).swap(*this);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::~function() {}
-
-template <class _Rp, class... _ArgTypes>
-void function<_Rp(_ArgTypes...)>::swap(function& __f) _NOEXCEPT {
-  __f_.swap(__f.__f_);
-}
-
-template <class _Rp, class... _ArgTypes>
-_Rp function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const {
-  return __f_(std::forward<_ArgTypes>(__arg)...);
-}
-
-#  if _LIBCPP_HAS_RTTI
-
-template <class _Rp, class... _ArgTypes>
-const std::type_info& function<_Rp(_ArgTypes...)>::target_type() const _NOEXCEPT {
-  return __f_.target_type();
-}
-
-template <class _Rp, class... _ArgTypes>
-template <typename _Tp>
-_Tp* function<_Rp(_ArgTypes...)>::target() _NOEXCEPT {
-  return (_Tp*)(__f_.template target<_Tp>());
-}
-
-template <class _Rp, class... _ArgTypes>
-template <typename _Tp>
-const _Tp* function<_Rp(_ArgTypes...)>::target() const _NOEXCEPT {
-  return __f_.template target<_Tp>();
-}
-
-#  endif // _LIBCPP_HAS_RTTI
-
 template <class _Rp, class... _ArgTypes>
 inline _LIBCPP_HIDE_FROM_ABI bool operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) _NOEXCEPT {
   return !__f;


From libcxx-commits at lists.llvm.org  Tue Jul 14 05:20:57 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 05:20:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Inline std::function
 members into the class body (PR #209461)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209461@github.com>
Message-ID: <6a5629a9.0c05e68e.1e82f1.b837@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

The `std::function` members are incredibly simple to the point where they are just wrappers around internal functions. We can inline them into the body to simplify the implementation a bit.


---
Full diff: https://github.com/llvm/llvm-project/pull/209461.diff


1 Files Affected:

- (modified) libcxx/include/__functional/function.h (+37-104) 


``````````diff
diff --git a/libcxx/include/__functional/function.h b/libcxx/include/__functional/function.h
index 90072e9528484..0adc336424e2e 100644
--- a/libcxx/include/__functional/function.h
+++ b/libcxx/include/__functional/function.h
@@ -340,7 +340,7 @@ template <typename _Fun>
 struct __use_small_storage
     : public integral_constant<
           bool,
-          sizeof(_Fun) <= sizeof(__policy_storage)&& _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
+          sizeof(_Fun) <= sizeof(__policy_storage) && _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
               is_trivially_copy_constructible<_Fun>::value && is_trivially_destructible<_Fun>::value> {};
 
 // Policy contains information about how to copy, destroy, and move the
@@ -628,10 +628,10 @@ class function<_Rp(_ArgTypes...)>
   // construct/copy/destroy:
   _LIBCPP_HIDE_FROM_ABI function() _NOEXCEPT {}
   _LIBCPP_HIDE_FROM_ABI function(nullptr_t) _NOEXCEPT {}
-  _LIBCPP_HIDE_FROM_ABI function(const function&);
-  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function(const function&)      = default;
+  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT = default;
   template <class _Fp, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(_Fp);
+  _LIBCPP_HIDE_FROM_ABI function(_Fp __f) : __f_(std::move(__f)) {}
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Alloc>
@@ -639,23 +639,35 @@ class function<_Rp(_ArgTypes...)>
   template <class _Alloc>
   _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, nullptr_t) _NOEXCEPT {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function&);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&&);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
   template <class _Fp, class _Alloc, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc& __a, _Fp __f);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI function& operator=(const function&);
-  _LIBCPP_HIDE_FROM_ABI function& operator=(function&&) _NOEXCEPT;
-  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function& operator=(const function& __f) {
+    function(__f).swap(*this);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI function& operator=(function&& __f) _NOEXCEPT = default;
+
+  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT {
+    __f_ = nullptr;
+    return *this;
+  }
+
   template <class _Fp, class = _EnableIfLValueCallable<__decay_t<_Fp>>>
-  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&&);
+  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&& __f) {
+    function(std::forward<_Fp>(__f)).swap(*this);
+    return *this;
+  }
 
-  _LIBCPP_HIDE_FROM_ABI ~function();
+  _LIBCPP_HIDE_FROM_ABI ~function() = default;
 
   // function modifiers:
-  _LIBCPP_HIDE_FROM_ABI void swap(function&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI void swap(function& __f) _NOEXCEPT { __f_.swap(__f.__f_); }
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Fp, class _Alloc>
@@ -677,15 +689,23 @@ class function<_Rp(_ArgTypes...)>
 
 public:
   // function invocation:
-  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes...) const;
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const { return __f_(std::forward<_ArgTypes>(__args)...); }
 
 #  if _LIBCPP_HAS_RTTI
   // function target access:
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT {
+    return __f_.target_type();
+  }
+
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT {
+    return (_Tp*)(__f_.template target<_Tp>());
+  }
+
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT {
+    return __f_.template target<_Tp>();
+  }
 #  endif // _LIBCPP_HAS_RTTI
 };
 
@@ -697,93 +717,6 @@ template <class _Fp, class _Stripped = __strip_signature_t<decltype(&_Fp::operat
 function(_Fp) -> function<_Stripped>;
 #  endif // _LIBCPP_STD_VER >= 17
 
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::function(const function& __f) : __f_(__f.__f_) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Alloc>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::function(function&& __f) _NOEXCEPT : __f_(std::move(__f.__f_)) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Alloc>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class>
-function<_Rp(_ArgTypes...)>::function(_Fp __f) : __f_(std::move(__f)) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class _Alloc, class>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(const function& __f) {
-  function(__f).swap(*this);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(function&& __f) _NOEXCEPT {
-  __f_ = std::move(__f.__f_);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(nullptr_t) _NOEXCEPT {
-  __f_ = nullptr;
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f) {
-  function(std::forward<_Fp>(__f)).swap(*this);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::~function() {}
-
-template <class _Rp, class... _ArgTypes>
-void function<_Rp(_ArgTypes...)>::swap(function& __f) _NOEXCEPT {
-  __f_.swap(__f.__f_);
-}
-
-template <class _Rp, class... _ArgTypes>
-_Rp function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const {
-  return __f_(std::forward<_ArgTypes>(__arg)...);
-}
-
-#  if _LIBCPP_HAS_RTTI
-
-template <class _Rp, class... _ArgTypes>
-const std::type_info& function<_Rp(_ArgTypes...)>::target_type() const _NOEXCEPT {
-  return __f_.target_type();
-}
-
-template <class _Rp, class... _ArgTypes>
-template <typename _Tp>
-_Tp* function<_Rp(_ArgTypes...)>::target() _NOEXCEPT {
-  return (_Tp*)(__f_.template target<_Tp>());
-}
-
-template <class _Rp, class... _ArgTypes>
-template <typename _Tp>
-const _Tp* function<_Rp(_ArgTypes...)>::target() const _NOEXCEPT {
-  return __f_.template target<_Tp>();
-}
-
-#  endif // _LIBCPP_HAS_RTTI
-
 template <class _Rp, class... _ArgTypes>
 inline _LIBCPP_HIDE_FROM_ABI bool operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) _NOEXCEPT {
   return !__f;

``````````

</details>


https://github.com/llvm/llvm-project/pull/209461

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:24:09 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 05:24:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a562a69.6c8668f2.e4f82.1c3c@mx.google.com>


================

----------------
philnik777 wrote:

We could, but I don't think we fundamentally need to benchmark only a single thing. pausing and resuming always have significant cost and increase noise. 10-20 vectors is most likely not nearly enough to overcome that noise, and at some point we're benchmarking how fast we can iterate through memory and not `push_back` anymore.

Also note that for trivial types `erase` and `clear` are constant time operations, since the vector just has to set a single pointer.

https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:25:26 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 05:25:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi] Use the same .clang-format
	as libc++ (PR #208661)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208661@github.com>
Message-ID: <6a562ab6.4a342cbc.ca844.23c8@mx.google.com>


================
@@ -1,14 +1,81 @@
 BasedOnStyle: LLVM
+# Note that we don't specify the language in this file because some files are
----------------
philnik777 wrote:

It is. Do we have precedent for symlinks in the repo? Windows is a bit weird about them IIRC.

https://github.com/llvm/llvm-project/pull/208661

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:29:39 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 05:29:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Make ios_base::xalloc non-atomic with
 LIBCXX_ENABLE_THREADS=OFF. (PR #208356)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208356@github.com>
Message-ID: <6a562bb3.27b3b003.3b871.189b@mx.google.com>


https://github.com/ldionne approved this pull request.

I dislike the fact that this is basically a workaround for not having a proper setup with the atomics library, but this is a pragmatic and minimal restoration of previous behavior that changed unintentionally, so I think it's acceptable.

https://github.com/llvm/llvm-project/pull/208356

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:42:58 2026
From: libcxx-commits at lists.llvm.org (Ijtihed Kilani via libcxx-commits)
Date: Tue, 14 Jul 2026 05:42:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_permutation (PR
	#205147)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205147@github.com>
Message-ID: <6a562ed2.bd93e731.358318.e5c3@mx.google.com>


Ijtihed wrote:

@ldionne could this be of interest to you?

https://github.com/llvm/llvm-project/pull/205147

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:43:41 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 05:43:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::binary_search and std::equal_range (PR #207268)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207268@github.com>
Message-ID: <6a562efd.824e11a6.f5f27.0fd7@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207268

>From 0b96a876d2f9d81c139d6695f18184f5c0c75fd1 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 13:50:05 -0400
Subject: [PATCH 1/2] [libc++] Add benchmarks for std::binary_search and
 std::equal_range

The found and not-found paths are measured as separate benchmarks. For
equal_range we additionally cover a unique match, a large equal range,
and an absent key.

As a drive-by, add a predicate variation to the existing lower_bound and
upper_bound benchmarks.
---
 .../nonmodifying/lower_upper_bound.bench.cpp  | 129 +++++++++--------
 .../sorting/binary_search.bench.cpp           | 126 ++++++++++++++++
 .../algorithms/sorting/equal_range.bench.cpp  | 137 ++++++++++++++++++
 3 files changed, 332 insertions(+), 60 deletions(-)
 create mode 100644 libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp
index 16ef348539776..8005cea67e0cd 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp
@@ -6,13 +6,13 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
+#include <cstddef>
 #include <deque>
 #include <forward_list>
 #include <list>
-#include <numeric>
 #include <random>
 #include <vector>
 
@@ -20,64 +20,73 @@
 #include "../../GenerateInput.h"
 
 int main(int argc, char** argv) {
-  {
-    auto bm = []<class Container>(benchmark::State& state) static {
-      std::mt19937_64 rng{123456};
-
-      using value_type = typename Container::value_type;
-
-      std::vector<value_type> vec;
-      for (int64_t i = 0; i != state.range(); ++i)
-        vec.emplace_back(Generate<typename Container::value_type>::random());
-      std::sort(vec.begin(), vec.end());
-
-      Container c(vec.begin(), vec.end());
-
-      for (auto _ : state) {
-        auto result = std::lower_bound(c.begin(), c.end(), vec[rng() % vec.size()]);
-        benchmark::DoNotOptimize(result);
-        benchmark::DoNotOptimize(vec);
-      }
-    };
-
-    auto register_benchmark = [&]<class Container>(std::type_identity<Container>, std::string name) {
-      benchmark::RegisterBenchmark(name, bm.template operator()<Container>)->Arg(8)->Arg(100)->Arg(8192);
-    };
-
-    register_benchmark(std::type_identity<std::vector<int>>{}, "std::lower_bound(std::vector<int>)");
-    register_benchmark(std::type_identity<std::deque<int>>{}, "std::lower_bound(std::deque<int>)");
-    register_benchmark(std::type_identity<std::list<int>>{}, "std::lower_bound(std::list<int>)");
-    register_benchmark(std::type_identity<std::forward_list<int>>{}, "std::lower_bound(std::forward_list<int>)");
-  }
-  {
-    auto bm = []<class Container>(benchmark::State& state) static {
-      std::mt19937_64 rng{123456};
-
-      using value_type = typename Container::value_type;
-
-      std::vector<value_type> vec;
-      for (int64_t i = 0; i != state.range(); ++i)
-        vec.emplace_back(Generate<typename Container::value_type>::random());
-      std::sort(vec.begin(), vec.end());
-
-      Container c(vec.begin(), vec.end());
-
-      for (auto _ : state) {
-        auto result = std::upper_bound(c.begin(), c.end(), vec[rng() % vec.size()]);
-        benchmark::DoNotOptimize(result);
-        benchmark::DoNotOptimize(vec);
-      }
-    };
-
-    auto register_benchmark = [&]<class Container>(std::type_identity<Container>, std::string name) {
-      benchmark::RegisterBenchmark(name, bm.template operator()<Container>)->Arg(8)->Arg(100)->Arg(8192);
-    };
-
-    register_benchmark(std::type_identity<std::vector<int>>{}, "std::upper_bound(std::vector<int>)");
-    register_benchmark(std::type_identity<std::deque<int>>{}, "std::upper_bound(std::deque<int>)");
-    register_benchmark(std::type_identity<std::list<int>>{}, "std::upper_bound(std::list<int>)");
-    register_benchmark(std::type_identity<std::forward_list<int>>{}, "std::upper_bound(std::forward_list<int>)");
-  }
+  auto std_lower_bound = [](auto first, auto last, auto const& value) { return std::lower_bound(first, last, value); };
+  auto std_lower_bound_pred = [](auto first, auto last, auto const& value) {
+    return std::lower_bound(first, last, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+  auto std_upper_bound = [](auto first, auto last, auto const& value) { return std::upper_bound(first, last, value); };
+  auto std_upper_bound_pred = [](auto first, auto last, auto const& value) {
+    return std::upper_bound(first, last, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+
+  // Benchmark {lower_bound,upper_bound} on a sorted sequence, looking up a random element that
+  // is present in the sequence.
+  auto bm = []<class Container>(std::string name, auto lookup) {
+    benchmark::RegisterBenchmark(
+        name,
+        [lookup](auto& st) {
+          using ValueType        = typename Container::value_type;
+          std::size_t const size = st.range(0);
+
+          // Random sorted data
+          std::vector<ValueType> data(size);
+          std::generate_n(data.begin(), size, &Generate<ValueType>::random);
+          std::sort(data.begin(), data.end());
+
+          // Precompute a bunch of random keys.
+          std::vector<ValueType> keys(data);
+          std::shuffle(keys.begin(), keys.end(), getRandomEngine());
+
+          Container c(data.begin(), data.end());
+          std::size_t pos = 0;
+          for ([[maybe_unused]] auto _ : st) {
+            benchmark::DoNotOptimize(c);
+            auto result = lookup(c.begin(), c.end(), keys[pos]);
+            benchmark::DoNotOptimize(result);
+            if (++pos == keys.size())
+              pos = 0;
+          }
+        })
+        ->Arg(8)
+        ->Arg(100)
+        ->Arg(8192);
+  };
+
+  bm.operator()<std::vector<int>>("std::lower_bound(std::vector<int>)", std_lower_bound);
+  bm.operator()<std::deque<int>>("std::lower_bound(std::deque<int>)", std_lower_bound);
+  bm.operator()<std::list<int>>("std::lower_bound(std::list<int>)", std_lower_bound);
+  bm.operator()<std::forward_list<int>>("std::lower_bound(std::forward_list<int>)", std_lower_bound);
+  bm.operator()<std::vector<int>>("std::lower_bound(std::vector<int>, pred)", std_lower_bound_pred);
+  bm.operator()<std::deque<int>>("std::lower_bound(std::deque<int>, pred)", std_lower_bound_pred);
+  bm.operator()<std::list<int>>("std::lower_bound(std::list<int>, pred)", std_lower_bound_pred);
+  bm.operator()<std::forward_list<int>>("std::lower_bound(std::forward_list<int>, pred)", std_lower_bound_pred);
+
+  bm.operator()<std::vector<int>>("std::upper_bound(std::vector<int>)", std_upper_bound);
+  bm.operator()<std::deque<int>>("std::upper_bound(std::deque<int>)", std_upper_bound);
+  bm.operator()<std::list<int>>("std::upper_bound(std::list<int>)", std_upper_bound);
+  bm.operator()<std::forward_list<int>>("std::upper_bound(std::forward_list<int>)", std_upper_bound);
+  bm.operator()<std::vector<int>>("std::upper_bound(std::vector<int>, pred)", std_upper_bound_pred);
+  bm.operator()<std::deque<int>>("std::upper_bound(std::deque<int>, pred)", std_upper_bound_pred);
+  bm.operator()<std::list<int>>("std::upper_bound(std::list<int>, pred)", std_upper_bound_pred);
+  bm.operator()<std::forward_list<int>>("std::upper_bound(std::forward_list<int>, pred)", std_upper_bound_pred);
 
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
diff --git a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
new file mode 100644
index 0000000000000..1ceeab794dfd8
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <forward_list>
+#include <list>
+#include <random>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_binary_search = [](auto first, auto last, auto const& value) {
+    return std::binary_search(first, last, value);
+  };
+  auto std_binary_search_pred = [](auto first, auto last, auto const& value) {
+    return std::binary_search(first, last, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+
+  // Benchmark binary_search looking up a random key that is present in the sequence.
+  {
+    auto bm = []<class Container>(std::string name, auto binary_search) {
+      benchmark::RegisterBenchmark(
+          name,
+          [binary_search](auto& st) {
+            using ValueType        = typename Container::value_type;
+            std::size_t const size = st.range(0);
+
+            // Random sorted data
+            std::vector<ValueType> data(size);
+            std::generate_n(data.begin(), size, &Generate<ValueType>::random);
+            std::sort(data.begin(), data.end());
+
+            // Precompute a bunch of random keys.
+            std::vector<ValueType> keys(data);
+            std::shuffle(keys.begin(), keys.end(), getRandomEngine());
+
+            Container c(data.begin(), data.end());
+            std::size_t pos = 0;
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              bool result = binary_search(c.begin(), c.end(), keys[pos]);
+              benchmark::DoNotOptimize(result);
+              if (++pos == keys.size())
+                pos = 0;
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    // clang-format off
+    bm.operator()<std::vector<int>>("std::binary_search(vector<int>) (present)", std_binary_search);
+    bm.operator()<std::deque<int>>("std::binary_search(deque<int>) (present)", std_binary_search);
+    bm.operator()<std::list<int>>("std::binary_search(list<int>) (present)", std_binary_search);
+    bm.operator()<std::forward_list<int>>("std::binary_search(forward_list<int>) (present)", std_binary_search);
+
+    bm.operator()<std::vector<int>>("std::binary_search(vector<int>, pred) (present)", std_binary_search_pred);
+    bm.operator()<std::deque<int>>("std::binary_search(deque<int>, pred) (present)", std_binary_search_pred);
+    bm.operator()<std::list<int>>("std::binary_search(list<int>, pred) (present)", std_binary_search_pred);
+    bm.operator()<std::forward_list<int>>("std::binary_search(forward_list<int>, pred) (present)", std_binary_search_pred);
+    // clang-format on
+  }
+
+  // Benchmark binary_search looking up a key that is absent from the sequence.
+  {
+    auto bm = []<class Container>(std::string name, auto binary_search) {
+      benchmark::RegisterBenchmark(
+          name,
+          [binary_search](auto& st) {
+            using ValueType        = typename Container::value_type;
+            std::size_t const size = st.range(0);
+
+            // Random sorted data
+            std::vector<ValueType> data(size);
+            std::generate_n(data.begin(), size, &Generate<ValueType>::random);
+            std::sort(data.begin(), data.end());
+
+            // Find a key that isn't in the sequence
+            ValueType absent = Generate<ValueType>::random();
+            while (std::find(data.begin(), data.end(), absent) != data.end())
+              absent = Generate<ValueType>::random();
+
+            Container c(data.begin(), data.end());
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              bool result = binary_search(c.begin(), c.end(), absent);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    // clang-format off
+    bm.operator()<std::vector<int>>("std::binary_search(vector<int>) (absent)", std_binary_search);
+    bm.operator()<std::deque<int>>("std::binary_search(deque<int>) (absent)", std_binary_search);
+    bm.operator()<std::list<int>>("std::binary_search(list<int>) (absent)", std_binary_search);
+    bm.operator()<std::forward_list<int>>("std::binary_search(forward_list<int>) (absent)", std_binary_search);
+
+    bm.operator()<std::vector<int>>("std::binary_search(vector<int>, pred) (absent)", std_binary_search_pred);
+    bm.operator()<std::deque<int>>("std::binary_search(deque<int>, pred) (absent)", std_binary_search_pred);
+    bm.operator()<std::list<int>>("std::binary_search(list<int>, pred) (absent)", std_binary_search_pred);
+    bm.operator()<std::forward_list<int>>("std::binary_search(forward_list<int>, pred) (absent)", std_binary_search_pred);
+    // clang-format on
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
new file mode 100644
index 0000000000000..c984554b839ad
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
@@ -0,0 +1,137 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <forward_list>
+#include <list>
+#include <numeric>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+
+int main(int argc, char** argv) {
+  auto std_equal_range = [](auto first, auto last, auto const& value) { return std::equal_range(first, last, value); };
+  auto std_equal_range_pred = [](auto first, auto last, auto const& value) {
+    return std::equal_range(first, last, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+
+  // The range we find is a single element.
+  {
+    auto bm = []<class Container>(std::string name, auto equal_range) {
+      benchmark::RegisterBenchmark(
+          name,
+          [equal_range](auto& st) {
+            std::size_t const size = st.range(0);
+            std::vector<int> data(size);
+            std::iota(data.begin(), data.end(), 0);
+            int const key = static_cast<int>(size / 2);
+
+            Container c(data.begin(), data.end());
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = equal_range(c.begin(), c.end(), key);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>) (unique)", std_equal_range);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>) (unique)", std_equal_range);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>) (unique)", std_equal_range);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>) (unique)", std_equal_range);
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>, pred) (unique)", std_equal_range_pred);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>, pred) (unique)", std_equal_range_pred);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>, pred) (unique)", std_equal_range_pred);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>, pred) (unique)", std_equal_range_pred);
+  }
+
+  // The range we find is a large part of the entire range.
+  // Data looks like [0, 1, 2, 2, 2, 2, 2, 7, 8, 9], we match the 2's.
+  {
+    auto bm = []<class Container>(std::string name, auto equal_range) {
+      benchmark::RegisterBenchmark(
+          name,
+          [equal_range](auto& st) {
+            std::size_t const size          = st.range(0);
+            std::size_t const subrange_size = size / 2;
+            std::size_t const left_flank    = (size - subrange_size) / 2;
+            std::vector<int> data(size);
+            std::iota(data.begin(), data.end(), 0); // [0, 1, 2, 3, ..., 8, 9]
+            int const key = data[left_flank];
+            std::fill_n(data.begin() + left_flank, subrange_size,
+                        key); // [0, 1, 2, 3, 3, 3, 3, ..., 8, 9]
+
+            Container c(data.begin(), data.end());
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = equal_range(c.begin(), c.end(), key);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>) (large range)", std_equal_range);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>) (large range)", std_equal_range);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>) (large range)", std_equal_range);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>) (large range)", std_equal_range);
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>, pred) (large range)", std_equal_range_pred);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>, pred) (large range)", std_equal_range_pred);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>, pred) (large range)", std_equal_range_pred);
+    bm.operator()<std::forward_list<int>>(
+        "std::equal_range(forward_list<int>, pred) (large range)", std_equal_range_pred);
+  }
+
+  // The searched-for value is not present, so the found range is empty.
+  {
+    auto bm = []<class Container>(std::string name, auto equal_range) {
+      benchmark::RegisterBenchmark(
+          name,
+          [equal_range](auto& st) {
+            std::size_t const size = st.range(0);
+            std::vector<int> data(size);
+            std::iota(data.begin(), data.end(), 0);
+            int const key = static_cast<int>(size); // one past the last element
+
+            Container c(data.begin(), data.end());
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = equal_range(c.begin(), c.end(), key);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>) (absent)", std_equal_range);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>) (absent)", std_equal_range);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>) (absent)", std_equal_range);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>) (absent)", std_equal_range);
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>, pred) (absent)", std_equal_range_pred);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>, pred) (absent)", std_equal_range_pred);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>, pred) (absent)", std_equal_range_pred);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>, pred) (absent)", std_equal_range_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

>From 814c0fe3d71f26cb9edac58738b95853d82e6e5d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 16:18:26 -0400
Subject: [PATCH 2/2] Remove unused includes

---
 .../test/benchmarks/algorithms/sorting/binary_search.bench.cpp   | 1 -
 .../benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp    | 1 -
 2 files changed, 2 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
index 1ceeab794dfd8..101ffc418925d 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
@@ -13,7 +13,6 @@
 #include <deque>
 #include <forward_list>
 #include <list>
-#include <random>
 #include <vector>
 
 #include "benchmark/benchmark.h"
diff --git a/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
index 8005cea67e0cd..fbba276796d8a 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
@@ -13,7 +13,6 @@
 #include <deque>
 #include <forward_list>
 #include <list>
-#include <random>
 #include <vector>
 
 #include "benchmark/benchmark.h"


From libcxx-commits at lists.llvm.org  Tue Jul 14 05:44:05 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 05:44:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::replace_copy and std::replace_copy_if (PR #207070)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207070@github.com>
Message-ID: <6a562f15.92d2873c.258297.8d1c@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207070

>From 59b8ac362ede7e106203ef43c453dc877e276369 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 14:18:54 -0400
Subject: [PATCH] [libc++] Add benchmarks for std::replace_copy and
 std::replace_copy_if

Assisted by Claude
---
 .../modifying/replace_copy.bench.cpp          | 129 ++++++++++++++++++
 1 file changed, 129 insertions(+)
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp

diff --git a/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp
new file mode 100644
index 0000000000000..97117fc7ffec2
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_replace_copy = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
+    return std::replace_copy(first, last, out, old_value, new_value);
+  };
+  auto std_replace_copy_if = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
+    return std::replace_copy_if(
+        first,
+        last,
+        out,
+        [&](auto element) {
+          benchmark::DoNotOptimize(element);
+          return element == old_value;
+        },
+        new_value);
+  };
+
+  // Benchmark {std,ranges}::{replace_copy,replace_copy_if} on a sequence of the form xxxxxxxxxxyyyyyyyyyy
+  // where we replace the prefix of x's with z's.
+  {
+    auto bm = []<class Container>(std::string name, auto replace_copy) {
+      benchmark::RegisterBenchmark(
+          name,
+          [replace_copy](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            Container c;
+            ValueType x = Generate<ValueType>::random();
+            ValueType y = random_different_from({x});
+            ValueType z = random_different_from({x, y});
+            std::fill_n(std::back_inserter(c), size / 2, x);
+            std::fill_n(std::back_inserter(c), size / 2, y);
+
+            std::vector<ValueType> out(size);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(out);
+              benchmark::DoNotOptimize(x);
+              benchmark::DoNotOptimize(z);
+              auto result = replace_copy(c.begin(), c.end(), out.begin(), x, z);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(32)
+          ->Arg(50) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::replace_copy
+    bm.operator()<std::vector<int>>("std::replace_copy(vector<int>) (prefix)", std_replace_copy);
+    bm.operator()<std::deque<int>>("std::replace_copy(deque<int>) (prefix)", std_replace_copy);
+    bm.operator()<std::list<int>>("std::replace_copy(list<int>) (prefix)", std_replace_copy);
+
+    // {std,ranges}::replace_copy_if
+    bm.operator()<std::vector<int>>("std::replace_copy_if(vector<int>) (prefix)", std_replace_copy_if);
+    bm.operator()<std::deque<int>>("std::replace_copy_if(deque<int>) (prefix)", std_replace_copy_if);
+    bm.operator()<std::list<int>>("std::replace_copy_if(list<int>) (prefix)", std_replace_copy_if);
+  }
+
+  // Benchmark {std,ranges}::{replace_copy,replace_copy_if} on a sequence of the form xyxyxyxyxyxyxyxyxyxy
+  // where we replace the x's with z's.
+  {
+    auto bm = []<class Container>(std::string name, auto replace_copy) {
+      benchmark::RegisterBenchmark(
+          name,
+          [replace_copy](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            Container c;
+            ValueType x = Generate<ValueType>::random();
+            ValueType y = random_different_from({x});
+            ValueType z = random_different_from({x, y});
+            for (std::size_t i = 0; i != size; ++i) {
+              c.push_back(i % 2 == 0 ? x : y);
+            }
+
+            std::vector<ValueType> out(size);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(out);
+              benchmark::DoNotOptimize(x);
+              benchmark::DoNotOptimize(z);
+              auto result = replace_copy(c.begin(), c.end(), out.begin(), x, z);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(32)
+          ->Arg(50) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::replace_copy
+    bm.operator()<std::vector<int>>("std::replace_copy(vector<int>) (sprinkled)", std_replace_copy);
+    bm.operator()<std::deque<int>>("std::replace_copy(deque<int>) (sprinkled)", std_replace_copy);
+    bm.operator()<std::list<int>>("std::replace_copy(list<int>) (sprinkled)", std_replace_copy);
+
+    // {std,ranges}::replace_copy_if
+    bm.operator()<std::vector<int>>("std::replace_copy_if(vector<int>) (sprinkled)", std_replace_copy_if);
+    bm.operator()<std::deque<int>>("std::replace_copy_if(deque<int>) (sprinkled)", std_replace_copy_if);
+    bm.operator()<std::list<int>>("std::replace_copy_if(list<int>) (sprinkled)", std_replace_copy_if);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Tue Jul 14 05:48:43 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 05:48:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::replace_copy and std::replace_copy_if (PR #207070)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207070@github.com>
Message-ID: <6a56302b.0c05e68e.1e82f1.a96c@mx.google.com>


https://github.com/philnik777 approved this pull request.

LGTM & comment.

https://github.com/llvm/llvm-project/pull/207070

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:48:43 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 05:48:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::replace_copy and std::replace_copy_if (PR #207070)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207070@github.com>
Message-ID: <6a56302b.d1496011.26913c.e9b5@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/207070

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:48:43 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 05:48:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::replace_copy and std::replace_copy_if (PR #207070)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207070@github.com>
Message-ID: <6a56302b.e55dc341.1f19bd.5786@mx.google.com>


================
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_replace_copy = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
+    return std::replace_copy(first, last, out, old_value, new_value);
+  };
+  auto std_replace_copy_if = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
+    return std::replace_copy_if(
+        first,
+        last,
+        out,
+        [&](auto element) {
+          benchmark::DoNotOptimize(element);
----------------
philnik777 wrote:

This should be removed since we decided to remove them in other benchmarks.

https://github.com/llvm/llvm-project/pull/207070

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:48:54 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 05:48:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::binary_search and std::equal_range (PR #207268)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207268@github.com>
Message-ID: <6a563036.d1496011.26913c.efff@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207268

>From 0b96a876d2f9d81c139d6695f18184f5c0c75fd1 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 13:50:05 -0400
Subject: [PATCH 1/3] [libc++] Add benchmarks for std::binary_search and
 std::equal_range

The found and not-found paths are measured as separate benchmarks. For
equal_range we additionally cover a unique match, a large equal range,
and an absent key.

As a drive-by, add a predicate variation to the existing lower_bound and
upper_bound benchmarks.
---
 .../nonmodifying/lower_upper_bound.bench.cpp  | 129 +++++++++--------
 .../sorting/binary_search.bench.cpp           | 126 ++++++++++++++++
 .../algorithms/sorting/equal_range.bench.cpp  | 137 ++++++++++++++++++
 3 files changed, 332 insertions(+), 60 deletions(-)
 create mode 100644 libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp
index 16ef348539776..8005cea67e0cd 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp
@@ -6,13 +6,13 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
+#include <cstddef>
 #include <deque>
 #include <forward_list>
 #include <list>
-#include <numeric>
 #include <random>
 #include <vector>
 
@@ -20,64 +20,73 @@
 #include "../../GenerateInput.h"
 
 int main(int argc, char** argv) {
-  {
-    auto bm = []<class Container>(benchmark::State& state) static {
-      std::mt19937_64 rng{123456};
-
-      using value_type = typename Container::value_type;
-
-      std::vector<value_type> vec;
-      for (int64_t i = 0; i != state.range(); ++i)
-        vec.emplace_back(Generate<typename Container::value_type>::random());
-      std::sort(vec.begin(), vec.end());
-
-      Container c(vec.begin(), vec.end());
-
-      for (auto _ : state) {
-        auto result = std::lower_bound(c.begin(), c.end(), vec[rng() % vec.size()]);
-        benchmark::DoNotOptimize(result);
-        benchmark::DoNotOptimize(vec);
-      }
-    };
-
-    auto register_benchmark = [&]<class Container>(std::type_identity<Container>, std::string name) {
-      benchmark::RegisterBenchmark(name, bm.template operator()<Container>)->Arg(8)->Arg(100)->Arg(8192);
-    };
-
-    register_benchmark(std::type_identity<std::vector<int>>{}, "std::lower_bound(std::vector<int>)");
-    register_benchmark(std::type_identity<std::deque<int>>{}, "std::lower_bound(std::deque<int>)");
-    register_benchmark(std::type_identity<std::list<int>>{}, "std::lower_bound(std::list<int>)");
-    register_benchmark(std::type_identity<std::forward_list<int>>{}, "std::lower_bound(std::forward_list<int>)");
-  }
-  {
-    auto bm = []<class Container>(benchmark::State& state) static {
-      std::mt19937_64 rng{123456};
-
-      using value_type = typename Container::value_type;
-
-      std::vector<value_type> vec;
-      for (int64_t i = 0; i != state.range(); ++i)
-        vec.emplace_back(Generate<typename Container::value_type>::random());
-      std::sort(vec.begin(), vec.end());
-
-      Container c(vec.begin(), vec.end());
-
-      for (auto _ : state) {
-        auto result = std::upper_bound(c.begin(), c.end(), vec[rng() % vec.size()]);
-        benchmark::DoNotOptimize(result);
-        benchmark::DoNotOptimize(vec);
-      }
-    };
-
-    auto register_benchmark = [&]<class Container>(std::type_identity<Container>, std::string name) {
-      benchmark::RegisterBenchmark(name, bm.template operator()<Container>)->Arg(8)->Arg(100)->Arg(8192);
-    };
-
-    register_benchmark(std::type_identity<std::vector<int>>{}, "std::upper_bound(std::vector<int>)");
-    register_benchmark(std::type_identity<std::deque<int>>{}, "std::upper_bound(std::deque<int>)");
-    register_benchmark(std::type_identity<std::list<int>>{}, "std::upper_bound(std::list<int>)");
-    register_benchmark(std::type_identity<std::forward_list<int>>{}, "std::upper_bound(std::forward_list<int>)");
-  }
+  auto std_lower_bound = [](auto first, auto last, auto const& value) { return std::lower_bound(first, last, value); };
+  auto std_lower_bound_pred = [](auto first, auto last, auto const& value) {
+    return std::lower_bound(first, last, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+  auto std_upper_bound = [](auto first, auto last, auto const& value) { return std::upper_bound(first, last, value); };
+  auto std_upper_bound_pred = [](auto first, auto last, auto const& value) {
+    return std::upper_bound(first, last, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+
+  // Benchmark {lower_bound,upper_bound} on a sorted sequence, looking up a random element that
+  // is present in the sequence.
+  auto bm = []<class Container>(std::string name, auto lookup) {
+    benchmark::RegisterBenchmark(
+        name,
+        [lookup](auto& st) {
+          using ValueType        = typename Container::value_type;
+          std::size_t const size = st.range(0);
+
+          // Random sorted data
+          std::vector<ValueType> data(size);
+          std::generate_n(data.begin(), size, &Generate<ValueType>::random);
+          std::sort(data.begin(), data.end());
+
+          // Precompute a bunch of random keys.
+          std::vector<ValueType> keys(data);
+          std::shuffle(keys.begin(), keys.end(), getRandomEngine());
+
+          Container c(data.begin(), data.end());
+          std::size_t pos = 0;
+          for ([[maybe_unused]] auto _ : st) {
+            benchmark::DoNotOptimize(c);
+            auto result = lookup(c.begin(), c.end(), keys[pos]);
+            benchmark::DoNotOptimize(result);
+            if (++pos == keys.size())
+              pos = 0;
+          }
+        })
+        ->Arg(8)
+        ->Arg(100)
+        ->Arg(8192);
+  };
+
+  bm.operator()<std::vector<int>>("std::lower_bound(std::vector<int>)", std_lower_bound);
+  bm.operator()<std::deque<int>>("std::lower_bound(std::deque<int>)", std_lower_bound);
+  bm.operator()<std::list<int>>("std::lower_bound(std::list<int>)", std_lower_bound);
+  bm.operator()<std::forward_list<int>>("std::lower_bound(std::forward_list<int>)", std_lower_bound);
+  bm.operator()<std::vector<int>>("std::lower_bound(std::vector<int>, pred)", std_lower_bound_pred);
+  bm.operator()<std::deque<int>>("std::lower_bound(std::deque<int>, pred)", std_lower_bound_pred);
+  bm.operator()<std::list<int>>("std::lower_bound(std::list<int>, pred)", std_lower_bound_pred);
+  bm.operator()<std::forward_list<int>>("std::lower_bound(std::forward_list<int>, pred)", std_lower_bound_pred);
+
+  bm.operator()<std::vector<int>>("std::upper_bound(std::vector<int>)", std_upper_bound);
+  bm.operator()<std::deque<int>>("std::upper_bound(std::deque<int>)", std_upper_bound);
+  bm.operator()<std::list<int>>("std::upper_bound(std::list<int>)", std_upper_bound);
+  bm.operator()<std::forward_list<int>>("std::upper_bound(std::forward_list<int>)", std_upper_bound);
+  bm.operator()<std::vector<int>>("std::upper_bound(std::vector<int>, pred)", std_upper_bound_pred);
+  bm.operator()<std::deque<int>>("std::upper_bound(std::deque<int>, pred)", std_upper_bound_pred);
+  bm.operator()<std::list<int>>("std::upper_bound(std::list<int>, pred)", std_upper_bound_pred);
+  bm.operator()<std::forward_list<int>>("std::upper_bound(std::forward_list<int>, pred)", std_upper_bound_pred);
 
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
diff --git a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
new file mode 100644
index 0000000000000..1ceeab794dfd8
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <forward_list>
+#include <list>
+#include <random>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_binary_search = [](auto first, auto last, auto const& value) {
+    return std::binary_search(first, last, value);
+  };
+  auto std_binary_search_pred = [](auto first, auto last, auto const& value) {
+    return std::binary_search(first, last, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+
+  // Benchmark binary_search looking up a random key that is present in the sequence.
+  {
+    auto bm = []<class Container>(std::string name, auto binary_search) {
+      benchmark::RegisterBenchmark(
+          name,
+          [binary_search](auto& st) {
+            using ValueType        = typename Container::value_type;
+            std::size_t const size = st.range(0);
+
+            // Random sorted data
+            std::vector<ValueType> data(size);
+            std::generate_n(data.begin(), size, &Generate<ValueType>::random);
+            std::sort(data.begin(), data.end());
+
+            // Precompute a bunch of random keys.
+            std::vector<ValueType> keys(data);
+            std::shuffle(keys.begin(), keys.end(), getRandomEngine());
+
+            Container c(data.begin(), data.end());
+            std::size_t pos = 0;
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              bool result = binary_search(c.begin(), c.end(), keys[pos]);
+              benchmark::DoNotOptimize(result);
+              if (++pos == keys.size())
+                pos = 0;
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    // clang-format off
+    bm.operator()<std::vector<int>>("std::binary_search(vector<int>) (present)", std_binary_search);
+    bm.operator()<std::deque<int>>("std::binary_search(deque<int>) (present)", std_binary_search);
+    bm.operator()<std::list<int>>("std::binary_search(list<int>) (present)", std_binary_search);
+    bm.operator()<std::forward_list<int>>("std::binary_search(forward_list<int>) (present)", std_binary_search);
+
+    bm.operator()<std::vector<int>>("std::binary_search(vector<int>, pred) (present)", std_binary_search_pred);
+    bm.operator()<std::deque<int>>("std::binary_search(deque<int>, pred) (present)", std_binary_search_pred);
+    bm.operator()<std::list<int>>("std::binary_search(list<int>, pred) (present)", std_binary_search_pred);
+    bm.operator()<std::forward_list<int>>("std::binary_search(forward_list<int>, pred) (present)", std_binary_search_pred);
+    // clang-format on
+  }
+
+  // Benchmark binary_search looking up a key that is absent from the sequence.
+  {
+    auto bm = []<class Container>(std::string name, auto binary_search) {
+      benchmark::RegisterBenchmark(
+          name,
+          [binary_search](auto& st) {
+            using ValueType        = typename Container::value_type;
+            std::size_t const size = st.range(0);
+
+            // Random sorted data
+            std::vector<ValueType> data(size);
+            std::generate_n(data.begin(), size, &Generate<ValueType>::random);
+            std::sort(data.begin(), data.end());
+
+            // Find a key that isn't in the sequence
+            ValueType absent = Generate<ValueType>::random();
+            while (std::find(data.begin(), data.end(), absent) != data.end())
+              absent = Generate<ValueType>::random();
+
+            Container c(data.begin(), data.end());
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              bool result = binary_search(c.begin(), c.end(), absent);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    // clang-format off
+    bm.operator()<std::vector<int>>("std::binary_search(vector<int>) (absent)", std_binary_search);
+    bm.operator()<std::deque<int>>("std::binary_search(deque<int>) (absent)", std_binary_search);
+    bm.operator()<std::list<int>>("std::binary_search(list<int>) (absent)", std_binary_search);
+    bm.operator()<std::forward_list<int>>("std::binary_search(forward_list<int>) (absent)", std_binary_search);
+
+    bm.operator()<std::vector<int>>("std::binary_search(vector<int>, pred) (absent)", std_binary_search_pred);
+    bm.operator()<std::deque<int>>("std::binary_search(deque<int>, pred) (absent)", std_binary_search_pred);
+    bm.operator()<std::list<int>>("std::binary_search(list<int>, pred) (absent)", std_binary_search_pred);
+    bm.operator()<std::forward_list<int>>("std::binary_search(forward_list<int>, pred) (absent)", std_binary_search_pred);
+    // clang-format on
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
new file mode 100644
index 0000000000000..c984554b839ad
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
@@ -0,0 +1,137 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <forward_list>
+#include <list>
+#include <numeric>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+
+int main(int argc, char** argv) {
+  auto std_equal_range = [](auto first, auto last, auto const& value) { return std::equal_range(first, last, value); };
+  auto std_equal_range_pred = [](auto first, auto last, auto const& value) {
+    return std::equal_range(first, last, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+
+  // The range we find is a single element.
+  {
+    auto bm = []<class Container>(std::string name, auto equal_range) {
+      benchmark::RegisterBenchmark(
+          name,
+          [equal_range](auto& st) {
+            std::size_t const size = st.range(0);
+            std::vector<int> data(size);
+            std::iota(data.begin(), data.end(), 0);
+            int const key = static_cast<int>(size / 2);
+
+            Container c(data.begin(), data.end());
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = equal_range(c.begin(), c.end(), key);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>) (unique)", std_equal_range);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>) (unique)", std_equal_range);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>) (unique)", std_equal_range);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>) (unique)", std_equal_range);
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>, pred) (unique)", std_equal_range_pred);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>, pred) (unique)", std_equal_range_pred);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>, pred) (unique)", std_equal_range_pred);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>, pred) (unique)", std_equal_range_pred);
+  }
+
+  // The range we find is a large part of the entire range.
+  // Data looks like [0, 1, 2, 2, 2, 2, 2, 7, 8, 9], we match the 2's.
+  {
+    auto bm = []<class Container>(std::string name, auto equal_range) {
+      benchmark::RegisterBenchmark(
+          name,
+          [equal_range](auto& st) {
+            std::size_t const size          = st.range(0);
+            std::size_t const subrange_size = size / 2;
+            std::size_t const left_flank    = (size - subrange_size) / 2;
+            std::vector<int> data(size);
+            std::iota(data.begin(), data.end(), 0); // [0, 1, 2, 3, ..., 8, 9]
+            int const key = data[left_flank];
+            std::fill_n(data.begin() + left_flank, subrange_size,
+                        key); // [0, 1, 2, 3, 3, 3, 3, ..., 8, 9]
+
+            Container c(data.begin(), data.end());
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = equal_range(c.begin(), c.end(), key);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>) (large range)", std_equal_range);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>) (large range)", std_equal_range);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>) (large range)", std_equal_range);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>) (large range)", std_equal_range);
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>, pred) (large range)", std_equal_range_pred);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>, pred) (large range)", std_equal_range_pred);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>, pred) (large range)", std_equal_range_pred);
+    bm.operator()<std::forward_list<int>>(
+        "std::equal_range(forward_list<int>, pred) (large range)", std_equal_range_pred);
+  }
+
+  // The searched-for value is not present, so the found range is empty.
+  {
+    auto bm = []<class Container>(std::string name, auto equal_range) {
+      benchmark::RegisterBenchmark(
+          name,
+          [equal_range](auto& st) {
+            std::size_t const size = st.range(0);
+            std::vector<int> data(size);
+            std::iota(data.begin(), data.end(), 0);
+            int const key = static_cast<int>(size); // one past the last element
+
+            Container c(data.begin(), data.end());
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = equal_range(c.begin(), c.end(), key);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>) (absent)", std_equal_range);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>) (absent)", std_equal_range);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>) (absent)", std_equal_range);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>) (absent)", std_equal_range);
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>, pred) (absent)", std_equal_range_pred);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>, pred) (absent)", std_equal_range_pred);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>, pred) (absent)", std_equal_range_pred);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>, pred) (absent)", std_equal_range_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

>From 814c0fe3d71f26cb9edac58738b95853d82e6e5d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 16:18:26 -0400
Subject: [PATCH 2/3] Remove unused includes

---
 .../test/benchmarks/algorithms/sorting/binary_search.bench.cpp   | 1 -
 .../benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp    | 1 -
 2 files changed, 2 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
index 1ceeab794dfd8..101ffc418925d 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
@@ -13,7 +13,6 @@
 #include <deque>
 #include <forward_list>
 #include <list>
-#include <random>
 #include <vector>
 
 #include "benchmark/benchmark.h"
diff --git a/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
index 8005cea67e0cd..fbba276796d8a 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
@@ -13,7 +13,6 @@
 #include <deque>
 #include <forward_list>
 #include <list>
-#include <random>
 #include <vector>
 
 #include "benchmark/benchmark.h"

>From 70f71300874780976e18f134d6cd0279db4b847a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 14 Jul 2026 08:48:43 -0400
Subject: [PATCH 3/3] Remove DoNotOptimize

---
 .../algorithms/sorting/binary_search.bench.cpp       |  6 +-----
 .../algorithms/sorting/equal_range.bench.cpp         |  6 +-----
 .../algorithms/sorting/lower_upper_bound.bench.cpp   | 12 ++----------
 3 files changed, 4 insertions(+), 20 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
index 101ffc418925d..1781fb68526e5 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
@@ -23,11 +23,7 @@ int main(int argc, char** argv) {
     return std::binary_search(first, last, value);
   };
   auto std_binary_search_pred = [](auto first, auto last, auto const& value) {
-    return std::binary_search(first, last, value, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x < y;
-    });
+    return std::binary_search(first, last, value, [](auto x, auto y) { return x < y; });
   };
 
   // Benchmark binary_search looking up a random key that is present in the sequence.
diff --git a/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
index c984554b839ad..b29834d669cef 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
@@ -21,11 +21,7 @@
 int main(int argc, char** argv) {
   auto std_equal_range = [](auto first, auto last, auto const& value) { return std::equal_range(first, last, value); };
   auto std_equal_range_pred = [](auto first, auto last, auto const& value) {
-    return std::equal_range(first, last, value, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x < y;
-    });
+    return std::equal_range(first, last, value, [](auto x, auto y) { return x < y; });
   };
 
   // The range we find is a single element.
diff --git a/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
index fbba276796d8a..6877184b8cc0c 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
@@ -21,19 +21,11 @@
 int main(int argc, char** argv) {
   auto std_lower_bound = [](auto first, auto last, auto const& value) { return std::lower_bound(first, last, value); };
   auto std_lower_bound_pred = [](auto first, auto last, auto const& value) {
-    return std::lower_bound(first, last, value, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x < y;
-    });
+    return std::lower_bound(first, last, value, [](auto x, auto y) { return x < y; });
   };
   auto std_upper_bound = [](auto first, auto last, auto const& value) { return std::upper_bound(first, last, value); };
   auto std_upper_bound_pred = [](auto first, auto last, auto const& value) {
-    return std::upper_bound(first, last, value, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x < y;
-    });
+    return std::upper_bound(first, last, value, [](auto x, auto y) { return x < y; });
   };
 
   // Benchmark {lower_bound,upper_bound} on a sorted sequence, looking up a random element that


From libcxx-commits at lists.llvm.org  Tue Jul 14 05:50:01 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 05:50:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::binary_search and std::equal_range (PR #207268)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207268@github.com>
Message-ID: <6a563079.0c05e68e.1e82f1.f0af@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207268

>From 0b96a876d2f9d81c139d6695f18184f5c0c75fd1 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 13:50:05 -0400
Subject: [PATCH 1/4] [libc++] Add benchmarks for std::binary_search and
 std::equal_range

The found and not-found paths are measured as separate benchmarks. For
equal_range we additionally cover a unique match, a large equal range,
and an absent key.

As a drive-by, add a predicate variation to the existing lower_bound and
upper_bound benchmarks.
---
 .../nonmodifying/lower_upper_bound.bench.cpp  | 129 +++++++++--------
 .../sorting/binary_search.bench.cpp           | 126 ++++++++++++++++
 .../algorithms/sorting/equal_range.bench.cpp  | 137 ++++++++++++++++++
 3 files changed, 332 insertions(+), 60 deletions(-)
 create mode 100644 libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp
index 16ef348539776..8005cea67e0cd 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/lower_upper_bound.bench.cpp
@@ -6,13 +6,13 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
+#include <cstddef>
 #include <deque>
 #include <forward_list>
 #include <list>
-#include <numeric>
 #include <random>
 #include <vector>
 
@@ -20,64 +20,73 @@
 #include "../../GenerateInput.h"
 
 int main(int argc, char** argv) {
-  {
-    auto bm = []<class Container>(benchmark::State& state) static {
-      std::mt19937_64 rng{123456};
-
-      using value_type = typename Container::value_type;
-
-      std::vector<value_type> vec;
-      for (int64_t i = 0; i != state.range(); ++i)
-        vec.emplace_back(Generate<typename Container::value_type>::random());
-      std::sort(vec.begin(), vec.end());
-
-      Container c(vec.begin(), vec.end());
-
-      for (auto _ : state) {
-        auto result = std::lower_bound(c.begin(), c.end(), vec[rng() % vec.size()]);
-        benchmark::DoNotOptimize(result);
-        benchmark::DoNotOptimize(vec);
-      }
-    };
-
-    auto register_benchmark = [&]<class Container>(std::type_identity<Container>, std::string name) {
-      benchmark::RegisterBenchmark(name, bm.template operator()<Container>)->Arg(8)->Arg(100)->Arg(8192);
-    };
-
-    register_benchmark(std::type_identity<std::vector<int>>{}, "std::lower_bound(std::vector<int>)");
-    register_benchmark(std::type_identity<std::deque<int>>{}, "std::lower_bound(std::deque<int>)");
-    register_benchmark(std::type_identity<std::list<int>>{}, "std::lower_bound(std::list<int>)");
-    register_benchmark(std::type_identity<std::forward_list<int>>{}, "std::lower_bound(std::forward_list<int>)");
-  }
-  {
-    auto bm = []<class Container>(benchmark::State& state) static {
-      std::mt19937_64 rng{123456};
-
-      using value_type = typename Container::value_type;
-
-      std::vector<value_type> vec;
-      for (int64_t i = 0; i != state.range(); ++i)
-        vec.emplace_back(Generate<typename Container::value_type>::random());
-      std::sort(vec.begin(), vec.end());
-
-      Container c(vec.begin(), vec.end());
-
-      for (auto _ : state) {
-        auto result = std::upper_bound(c.begin(), c.end(), vec[rng() % vec.size()]);
-        benchmark::DoNotOptimize(result);
-        benchmark::DoNotOptimize(vec);
-      }
-    };
-
-    auto register_benchmark = [&]<class Container>(std::type_identity<Container>, std::string name) {
-      benchmark::RegisterBenchmark(name, bm.template operator()<Container>)->Arg(8)->Arg(100)->Arg(8192);
-    };
-
-    register_benchmark(std::type_identity<std::vector<int>>{}, "std::upper_bound(std::vector<int>)");
-    register_benchmark(std::type_identity<std::deque<int>>{}, "std::upper_bound(std::deque<int>)");
-    register_benchmark(std::type_identity<std::list<int>>{}, "std::upper_bound(std::list<int>)");
-    register_benchmark(std::type_identity<std::forward_list<int>>{}, "std::upper_bound(std::forward_list<int>)");
-  }
+  auto std_lower_bound = [](auto first, auto last, auto const& value) { return std::lower_bound(first, last, value); };
+  auto std_lower_bound_pred = [](auto first, auto last, auto const& value) {
+    return std::lower_bound(first, last, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+  auto std_upper_bound = [](auto first, auto last, auto const& value) { return std::upper_bound(first, last, value); };
+  auto std_upper_bound_pred = [](auto first, auto last, auto const& value) {
+    return std::upper_bound(first, last, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+
+  // Benchmark {lower_bound,upper_bound} on a sorted sequence, looking up a random element that
+  // is present in the sequence.
+  auto bm = []<class Container>(std::string name, auto lookup) {
+    benchmark::RegisterBenchmark(
+        name,
+        [lookup](auto& st) {
+          using ValueType        = typename Container::value_type;
+          std::size_t const size = st.range(0);
+
+          // Random sorted data
+          std::vector<ValueType> data(size);
+          std::generate_n(data.begin(), size, &Generate<ValueType>::random);
+          std::sort(data.begin(), data.end());
+
+          // Precompute a bunch of random keys.
+          std::vector<ValueType> keys(data);
+          std::shuffle(keys.begin(), keys.end(), getRandomEngine());
+
+          Container c(data.begin(), data.end());
+          std::size_t pos = 0;
+          for ([[maybe_unused]] auto _ : st) {
+            benchmark::DoNotOptimize(c);
+            auto result = lookup(c.begin(), c.end(), keys[pos]);
+            benchmark::DoNotOptimize(result);
+            if (++pos == keys.size())
+              pos = 0;
+          }
+        })
+        ->Arg(8)
+        ->Arg(100)
+        ->Arg(8192);
+  };
+
+  bm.operator()<std::vector<int>>("std::lower_bound(std::vector<int>)", std_lower_bound);
+  bm.operator()<std::deque<int>>("std::lower_bound(std::deque<int>)", std_lower_bound);
+  bm.operator()<std::list<int>>("std::lower_bound(std::list<int>)", std_lower_bound);
+  bm.operator()<std::forward_list<int>>("std::lower_bound(std::forward_list<int>)", std_lower_bound);
+  bm.operator()<std::vector<int>>("std::lower_bound(std::vector<int>, pred)", std_lower_bound_pred);
+  bm.operator()<std::deque<int>>("std::lower_bound(std::deque<int>, pred)", std_lower_bound_pred);
+  bm.operator()<std::list<int>>("std::lower_bound(std::list<int>, pred)", std_lower_bound_pred);
+  bm.operator()<std::forward_list<int>>("std::lower_bound(std::forward_list<int>, pred)", std_lower_bound_pred);
+
+  bm.operator()<std::vector<int>>("std::upper_bound(std::vector<int>)", std_upper_bound);
+  bm.operator()<std::deque<int>>("std::upper_bound(std::deque<int>)", std_upper_bound);
+  bm.operator()<std::list<int>>("std::upper_bound(std::list<int>)", std_upper_bound);
+  bm.operator()<std::forward_list<int>>("std::upper_bound(std::forward_list<int>)", std_upper_bound);
+  bm.operator()<std::vector<int>>("std::upper_bound(std::vector<int>, pred)", std_upper_bound_pred);
+  bm.operator()<std::deque<int>>("std::upper_bound(std::deque<int>, pred)", std_upper_bound_pred);
+  bm.operator()<std::list<int>>("std::upper_bound(std::list<int>, pred)", std_upper_bound_pred);
+  bm.operator()<std::forward_list<int>>("std::upper_bound(std::forward_list<int>, pred)", std_upper_bound_pred);
 
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
diff --git a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
new file mode 100644
index 0000000000000..1ceeab794dfd8
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <forward_list>
+#include <list>
+#include <random>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_binary_search = [](auto first, auto last, auto const& value) {
+    return std::binary_search(first, last, value);
+  };
+  auto std_binary_search_pred = [](auto first, auto last, auto const& value) {
+    return std::binary_search(first, last, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+
+  // Benchmark binary_search looking up a random key that is present in the sequence.
+  {
+    auto bm = []<class Container>(std::string name, auto binary_search) {
+      benchmark::RegisterBenchmark(
+          name,
+          [binary_search](auto& st) {
+            using ValueType        = typename Container::value_type;
+            std::size_t const size = st.range(0);
+
+            // Random sorted data
+            std::vector<ValueType> data(size);
+            std::generate_n(data.begin(), size, &Generate<ValueType>::random);
+            std::sort(data.begin(), data.end());
+
+            // Precompute a bunch of random keys.
+            std::vector<ValueType> keys(data);
+            std::shuffle(keys.begin(), keys.end(), getRandomEngine());
+
+            Container c(data.begin(), data.end());
+            std::size_t pos = 0;
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              bool result = binary_search(c.begin(), c.end(), keys[pos]);
+              benchmark::DoNotOptimize(result);
+              if (++pos == keys.size())
+                pos = 0;
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    // clang-format off
+    bm.operator()<std::vector<int>>("std::binary_search(vector<int>) (present)", std_binary_search);
+    bm.operator()<std::deque<int>>("std::binary_search(deque<int>) (present)", std_binary_search);
+    bm.operator()<std::list<int>>("std::binary_search(list<int>) (present)", std_binary_search);
+    bm.operator()<std::forward_list<int>>("std::binary_search(forward_list<int>) (present)", std_binary_search);
+
+    bm.operator()<std::vector<int>>("std::binary_search(vector<int>, pred) (present)", std_binary_search_pred);
+    bm.operator()<std::deque<int>>("std::binary_search(deque<int>, pred) (present)", std_binary_search_pred);
+    bm.operator()<std::list<int>>("std::binary_search(list<int>, pred) (present)", std_binary_search_pred);
+    bm.operator()<std::forward_list<int>>("std::binary_search(forward_list<int>, pred) (present)", std_binary_search_pred);
+    // clang-format on
+  }
+
+  // Benchmark binary_search looking up a key that is absent from the sequence.
+  {
+    auto bm = []<class Container>(std::string name, auto binary_search) {
+      benchmark::RegisterBenchmark(
+          name,
+          [binary_search](auto& st) {
+            using ValueType        = typename Container::value_type;
+            std::size_t const size = st.range(0);
+
+            // Random sorted data
+            std::vector<ValueType> data(size);
+            std::generate_n(data.begin(), size, &Generate<ValueType>::random);
+            std::sort(data.begin(), data.end());
+
+            // Find a key that isn't in the sequence
+            ValueType absent = Generate<ValueType>::random();
+            while (std::find(data.begin(), data.end(), absent) != data.end())
+              absent = Generate<ValueType>::random();
+
+            Container c(data.begin(), data.end());
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              bool result = binary_search(c.begin(), c.end(), absent);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    // clang-format off
+    bm.operator()<std::vector<int>>("std::binary_search(vector<int>) (absent)", std_binary_search);
+    bm.operator()<std::deque<int>>("std::binary_search(deque<int>) (absent)", std_binary_search);
+    bm.operator()<std::list<int>>("std::binary_search(list<int>) (absent)", std_binary_search);
+    bm.operator()<std::forward_list<int>>("std::binary_search(forward_list<int>) (absent)", std_binary_search);
+
+    bm.operator()<std::vector<int>>("std::binary_search(vector<int>, pred) (absent)", std_binary_search_pred);
+    bm.operator()<std::deque<int>>("std::binary_search(deque<int>, pred) (absent)", std_binary_search_pred);
+    bm.operator()<std::list<int>>("std::binary_search(list<int>, pred) (absent)", std_binary_search_pred);
+    bm.operator()<std::forward_list<int>>("std::binary_search(forward_list<int>, pred) (absent)", std_binary_search_pred);
+    // clang-format on
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
new file mode 100644
index 0000000000000..c984554b839ad
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
@@ -0,0 +1,137 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <forward_list>
+#include <list>
+#include <numeric>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+
+int main(int argc, char** argv) {
+  auto std_equal_range = [](auto first, auto last, auto const& value) { return std::equal_range(first, last, value); };
+  auto std_equal_range_pred = [](auto first, auto last, auto const& value) {
+    return std::equal_range(first, last, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+
+  // The range we find is a single element.
+  {
+    auto bm = []<class Container>(std::string name, auto equal_range) {
+      benchmark::RegisterBenchmark(
+          name,
+          [equal_range](auto& st) {
+            std::size_t const size = st.range(0);
+            std::vector<int> data(size);
+            std::iota(data.begin(), data.end(), 0);
+            int const key = static_cast<int>(size / 2);
+
+            Container c(data.begin(), data.end());
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = equal_range(c.begin(), c.end(), key);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>) (unique)", std_equal_range);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>) (unique)", std_equal_range);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>) (unique)", std_equal_range);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>) (unique)", std_equal_range);
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>, pred) (unique)", std_equal_range_pred);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>, pred) (unique)", std_equal_range_pred);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>, pred) (unique)", std_equal_range_pred);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>, pred) (unique)", std_equal_range_pred);
+  }
+
+  // The range we find is a large part of the entire range.
+  // Data looks like [0, 1, 2, 2, 2, 2, 2, 7, 8, 9], we match the 2's.
+  {
+    auto bm = []<class Container>(std::string name, auto equal_range) {
+      benchmark::RegisterBenchmark(
+          name,
+          [equal_range](auto& st) {
+            std::size_t const size          = st.range(0);
+            std::size_t const subrange_size = size / 2;
+            std::size_t const left_flank    = (size - subrange_size) / 2;
+            std::vector<int> data(size);
+            std::iota(data.begin(), data.end(), 0); // [0, 1, 2, 3, ..., 8, 9]
+            int const key = data[left_flank];
+            std::fill_n(data.begin() + left_flank, subrange_size,
+                        key); // [0, 1, 2, 3, 3, 3, 3, ..., 8, 9]
+
+            Container c(data.begin(), data.end());
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = equal_range(c.begin(), c.end(), key);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>) (large range)", std_equal_range);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>) (large range)", std_equal_range);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>) (large range)", std_equal_range);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>) (large range)", std_equal_range);
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>, pred) (large range)", std_equal_range_pred);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>, pred) (large range)", std_equal_range_pred);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>, pred) (large range)", std_equal_range_pred);
+    bm.operator()<std::forward_list<int>>(
+        "std::equal_range(forward_list<int>, pred) (large range)", std_equal_range_pred);
+  }
+
+  // The searched-for value is not present, so the found range is empty.
+  {
+    auto bm = []<class Container>(std::string name, auto equal_range) {
+      benchmark::RegisterBenchmark(
+          name,
+          [equal_range](auto& st) {
+            std::size_t const size = st.range(0);
+            std::vector<int> data(size);
+            std::iota(data.begin(), data.end(), 0);
+            int const key = static_cast<int>(size); // one past the last element
+
+            Container c(data.begin(), data.end());
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = equal_range(c.begin(), c.end(), key);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(100)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>) (absent)", std_equal_range);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>) (absent)", std_equal_range);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>) (absent)", std_equal_range);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>) (absent)", std_equal_range);
+    bm.operator()<std::vector<int>>("std::equal_range(vector<int>, pred) (absent)", std_equal_range_pred);
+    bm.operator()<std::deque<int>>("std::equal_range(deque<int>, pred) (absent)", std_equal_range_pred);
+    bm.operator()<std::list<int>>("std::equal_range(list<int>, pred) (absent)", std_equal_range_pred);
+    bm.operator()<std::forward_list<int>>("std::equal_range(forward_list<int>, pred) (absent)", std_equal_range_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

>From 814c0fe3d71f26cb9edac58738b95853d82e6e5d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 16:18:26 -0400
Subject: [PATCH 2/4] Remove unused includes

---
 .../test/benchmarks/algorithms/sorting/binary_search.bench.cpp   | 1 -
 .../benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp    | 1 -
 2 files changed, 2 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
index 1ceeab794dfd8..101ffc418925d 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
@@ -13,7 +13,6 @@
 #include <deque>
 #include <forward_list>
 #include <list>
-#include <random>
 #include <vector>
 
 #include "benchmark/benchmark.h"
diff --git a/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
index 8005cea67e0cd..fbba276796d8a 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
@@ -13,7 +13,6 @@
 #include <deque>
 #include <forward_list>
 #include <list>
-#include <random>
 #include <vector>
 
 #include "benchmark/benchmark.h"

>From 70f71300874780976e18f134d6cd0279db4b847a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 14 Jul 2026 08:48:43 -0400
Subject: [PATCH 3/4] Remove DoNotOptimize

---
 .../algorithms/sorting/binary_search.bench.cpp       |  6 +-----
 .../algorithms/sorting/equal_range.bench.cpp         |  6 +-----
 .../algorithms/sorting/lower_upper_bound.bench.cpp   | 12 ++----------
 3 files changed, 4 insertions(+), 20 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
index 101ffc418925d..1781fb68526e5 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
@@ -23,11 +23,7 @@ int main(int argc, char** argv) {
     return std::binary_search(first, last, value);
   };
   auto std_binary_search_pred = [](auto first, auto last, auto const& value) {
-    return std::binary_search(first, last, value, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x < y;
-    });
+    return std::binary_search(first, last, value, [](auto x, auto y) { return x < y; });
   };
 
   // Benchmark binary_search looking up a random key that is present in the sequence.
diff --git a/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
index c984554b839ad..b29834d669cef 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
@@ -21,11 +21,7 @@
 int main(int argc, char** argv) {
   auto std_equal_range = [](auto first, auto last, auto const& value) { return std::equal_range(first, last, value); };
   auto std_equal_range_pred = [](auto first, auto last, auto const& value) {
-    return std::equal_range(first, last, value, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x < y;
-    });
+    return std::equal_range(first, last, value, [](auto x, auto y) { return x < y; });
   };
 
   // The range we find is a single element.
diff --git a/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
index fbba276796d8a..6877184b8cc0c 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
@@ -21,19 +21,11 @@
 int main(int argc, char** argv) {
   auto std_lower_bound = [](auto first, auto last, auto const& value) { return std::lower_bound(first, last, value); };
   auto std_lower_bound_pred = [](auto first, auto last, auto const& value) {
-    return std::lower_bound(first, last, value, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x < y;
-    });
+    return std::lower_bound(first, last, value, [](auto x, auto y) { return x < y; });
   };
   auto std_upper_bound = [](auto first, auto last, auto const& value) { return std::upper_bound(first, last, value); };
   auto std_upper_bound_pred = [](auto first, auto last, auto const& value) {
-    return std::upper_bound(first, last, value, [](auto x, auto y) {
-      benchmark::DoNotOptimize(x);
-      benchmark::DoNotOptimize(y);
-      return x < y;
-    });
+    return std::upper_bound(first, last, value, [](auto x, auto y) { return x < y; });
   };
 
   // Benchmark {lower_bound,upper_bound} on a sorted sequence, looking up a random element that

>From 1c244cc7a2c9d26f4d71a50cb8fc716a92f4c4a0 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 14 Jul 2026 08:49:49 -0400
Subject: [PATCH 4/4] maybe unused

---
 .../benchmarks/algorithms/sorting/binary_search.bench.cpp   | 4 ++--
 .../benchmarks/algorithms/sorting/equal_range.bench.cpp     | 6 +++---
 .../algorithms/sorting/lower_upper_bound.bench.cpp          | 2 +-
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
index 1781fb68526e5..9b85911961af4 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/binary_search.bench.cpp
@@ -46,7 +46,7 @@ int main(int argc, char** argv) {
 
             Container c(data.begin(), data.end());
             std::size_t pos = 0;
-            for ([[maybe_unused]] auto _ : st) {
+            for (auto _ : st) {
               benchmark::DoNotOptimize(c);
               bool result = binary_search(c.begin(), c.end(), keys[pos]);
               benchmark::DoNotOptimize(result);
@@ -91,7 +91,7 @@ int main(int argc, char** argv) {
               absent = Generate<ValueType>::random();
 
             Container c(data.begin(), data.end());
-            for ([[maybe_unused]] auto _ : st) {
+            for (auto _ : st) {
               benchmark::DoNotOptimize(c);
               bool result = binary_search(c.begin(), c.end(), absent);
               benchmark::DoNotOptimize(result);
diff --git a/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
index b29834d669cef..7c4d943019baa 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/equal_range.bench.cpp
@@ -36,7 +36,7 @@ int main(int argc, char** argv) {
             int const key = static_cast<int>(size / 2);
 
             Container c(data.begin(), data.end());
-            for ([[maybe_unused]] auto _ : st) {
+            for (auto _ : st) {
               benchmark::DoNotOptimize(c);
               auto result = equal_range(c.begin(), c.end(), key);
               benchmark::DoNotOptimize(result);
@@ -73,7 +73,7 @@ int main(int argc, char** argv) {
                         key); // [0, 1, 2, 3, 3, 3, 3, ..., 8, 9]
 
             Container c(data.begin(), data.end());
-            for ([[maybe_unused]] auto _ : st) {
+            for (auto _ : st) {
               benchmark::DoNotOptimize(c);
               auto result = equal_range(c.begin(), c.end(), key);
               benchmark::DoNotOptimize(result);
@@ -106,7 +106,7 @@ int main(int argc, char** argv) {
             int const key = static_cast<int>(size); // one past the last element
 
             Container c(data.begin(), data.end());
-            for ([[maybe_unused]] auto _ : st) {
+            for (auto _ : st) {
               benchmark::DoNotOptimize(c);
               auto result = equal_range(c.begin(), c.end(), key);
               benchmark::DoNotOptimize(result);
diff --git a/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
index 6877184b8cc0c..36eefac3ef4e5 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/lower_upper_bound.bench.cpp
@@ -48,7 +48,7 @@ int main(int argc, char** argv) {
 
           Container c(data.begin(), data.end());
           std::size_t pos = 0;
-          for ([[maybe_unused]] auto _ : st) {
+          for (auto _ : st) {
             benchmark::DoNotOptimize(c);
             auto result = lookup(c.begin(), c.end(), keys[pos]);
             benchmark::DoNotOptimize(result);


From libcxx-commits at lists.llvm.org  Tue Jul 14 05:50:45 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 05:50:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::replace_copy and std::replace_copy_if (PR #207070)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207070@github.com>
Message-ID: <6a5630a5.f732ad81.3a4994.772d@mx.google.com>


================
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_replace_copy = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
+    return std::replace_copy(first, last, out, old_value, new_value);
+  };
+  auto std_replace_copy_if = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
+    return std::replace_copy_if(
+        first,
+        last,
+        out,
+        [&](auto element) {
+          benchmark::DoNotOptimize(element);
----------------
ldionne wrote:

Ack, yes I forgot to update the PR after the other ones. Doing

https://github.com/llvm/llvm-project/pull/207070

From libcxx-commits at lists.llvm.org  Tue Jul 14 05:52:02 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 05:52:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::replace_copy and std::replace_copy_if (PR #207070)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207070@github.com>
Message-ID: <6a5630f2.b3997315.262b3c.7d81@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207070

>From 59b8ac362ede7e106203ef43c453dc877e276369 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 14:18:54 -0400
Subject: [PATCH 1/2] [libc++] Add benchmarks for std::replace_copy and
 std::replace_copy_if

Assisted by Claude
---
 .../modifying/replace_copy.bench.cpp          | 129 ++++++++++++++++++
 1 file changed, 129 insertions(+)
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp

diff --git a/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp
new file mode 100644
index 0000000000000..97117fc7ffec2
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_replace_copy = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
+    return std::replace_copy(first, last, out, old_value, new_value);
+  };
+  auto std_replace_copy_if = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
+    return std::replace_copy_if(
+        first,
+        last,
+        out,
+        [&](auto element) {
+          benchmark::DoNotOptimize(element);
+          return element == old_value;
+        },
+        new_value);
+  };
+
+  // Benchmark {std,ranges}::{replace_copy,replace_copy_if} on a sequence of the form xxxxxxxxxxyyyyyyyyyy
+  // where we replace the prefix of x's with z's.
+  {
+    auto bm = []<class Container>(std::string name, auto replace_copy) {
+      benchmark::RegisterBenchmark(
+          name,
+          [replace_copy](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            Container c;
+            ValueType x = Generate<ValueType>::random();
+            ValueType y = random_different_from({x});
+            ValueType z = random_different_from({x, y});
+            std::fill_n(std::back_inserter(c), size / 2, x);
+            std::fill_n(std::back_inserter(c), size / 2, y);
+
+            std::vector<ValueType> out(size);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(out);
+              benchmark::DoNotOptimize(x);
+              benchmark::DoNotOptimize(z);
+              auto result = replace_copy(c.begin(), c.end(), out.begin(), x, z);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(32)
+          ->Arg(50) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::replace_copy
+    bm.operator()<std::vector<int>>("std::replace_copy(vector<int>) (prefix)", std_replace_copy);
+    bm.operator()<std::deque<int>>("std::replace_copy(deque<int>) (prefix)", std_replace_copy);
+    bm.operator()<std::list<int>>("std::replace_copy(list<int>) (prefix)", std_replace_copy);
+
+    // {std,ranges}::replace_copy_if
+    bm.operator()<std::vector<int>>("std::replace_copy_if(vector<int>) (prefix)", std_replace_copy_if);
+    bm.operator()<std::deque<int>>("std::replace_copy_if(deque<int>) (prefix)", std_replace_copy_if);
+    bm.operator()<std::list<int>>("std::replace_copy_if(list<int>) (prefix)", std_replace_copy_if);
+  }
+
+  // Benchmark {std,ranges}::{replace_copy,replace_copy_if} on a sequence of the form xyxyxyxyxyxyxyxyxyxy
+  // where we replace the x's with z's.
+  {
+    auto bm = []<class Container>(std::string name, auto replace_copy) {
+      benchmark::RegisterBenchmark(
+          name,
+          [replace_copy](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            Container c;
+            ValueType x = Generate<ValueType>::random();
+            ValueType y = random_different_from({x});
+            ValueType z = random_different_from({x, y});
+            for (std::size_t i = 0; i != size; ++i) {
+              c.push_back(i % 2 == 0 ? x : y);
+            }
+
+            std::vector<ValueType> out(size);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(out);
+              benchmark::DoNotOptimize(x);
+              benchmark::DoNotOptimize(z);
+              auto result = replace_copy(c.begin(), c.end(), out.begin(), x, z);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(32)
+          ->Arg(50) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::replace_copy
+    bm.operator()<std::vector<int>>("std::replace_copy(vector<int>) (sprinkled)", std_replace_copy);
+    bm.operator()<std::deque<int>>("std::replace_copy(deque<int>) (sprinkled)", std_replace_copy);
+    bm.operator()<std::list<int>>("std::replace_copy(list<int>) (sprinkled)", std_replace_copy);
+
+    // {std,ranges}::replace_copy_if
+    bm.operator()<std::vector<int>>("std::replace_copy_if(vector<int>) (sprinkled)", std_replace_copy_if);
+    bm.operator()<std::deque<int>>("std::replace_copy_if(deque<int>) (sprinkled)", std_replace_copy_if);
+    bm.operator()<std::list<int>>("std::replace_copy_if(list<int>) (sprinkled)", std_replace_copy_if);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

>From d8d4a1bf8ede10bf4da2feaf7f8e172972b7e3ae Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 14 Jul 2026 08:51:51 -0400
Subject: [PATCH 2/2] Comments from other review

---
 .../algorithms/modifying/replace_copy.bench.cpp    | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp
index 97117fc7ffec2..0cad92d3b5ef3 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/replace_copy.bench.cpp
@@ -24,15 +24,7 @@ int main(int argc, char** argv) {
     return std::replace_copy(first, last, out, old_value, new_value);
   };
   auto std_replace_copy_if = [](auto first, auto last, auto out, auto const& old_value, auto const& new_value) {
-    return std::replace_copy_if(
-        first,
-        last,
-        out,
-        [&](auto element) {
-          benchmark::DoNotOptimize(element);
-          return element == old_value;
-        },
-        new_value);
+    return std::replace_copy_if(first, last, out, [&](auto element) { return element == old_value; }, new_value);
   };
 
   // Benchmark {std,ranges}::{replace_copy,replace_copy_if} on a sequence of the form xxxxxxxxxxyyyyyyyyyy
@@ -53,7 +45,7 @@ int main(int argc, char** argv) {
 
             std::vector<ValueType> out(size);
 
-            for ([[maybe_unused]] auto _ : st) {
+            for (auto _ : st) {
               benchmark::DoNotOptimize(c);
               benchmark::DoNotOptimize(out);
               benchmark::DoNotOptimize(x);
@@ -97,7 +89,7 @@ int main(int argc, char** argv) {
 
             std::vector<ValueType> out(size);
 
-            for ([[maybe_unused]] auto _ : st) {
+            for (auto _ : st) {
               benchmark::DoNotOptimize(c);
               benchmark::DoNotOptimize(out);
               benchmark::DoNotOptimize(x);


From libcxx-commits at lists.llvm.org  Tue Jul 14 05:53:36 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 05:53:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::binary_search and std::equal_range (PR #207268)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207268@github.com>
Message-ID: <6a563150.2c9cebe8.17c115.1fe1@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/207268

From libcxx-commits at lists.llvm.org  Tue Jul 14 06:05:18 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 06:05:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Treat negative counts in copy_n
 & friends as no-ops (PR #207086)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207086@github.com>
Message-ID: <6a56340e.4705fa9e.fba68.8d14@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207086

>From 64b22d4e38f896e39762ccfd1f011f0dd16dfddb Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 29 Jun 2026 14:57:40 -0400
Subject: [PATCH 01/12] [libc++] Treat negative counts in copy_n & friends as
 no-ops

The standard specifies copy_n, fill_n and generate_n (both std and
ranges) to be no-ops when passed a negative count. This patch fixes
libc++ to abide by that requirement, with tests.

It's worth noting that std::for_each_n makes n > 0 a precondition
instead: we add the check and tests for it.

Fixes #193613
---
 libcxx/include/__algorithm/copy_n.h           | 17 +++++----
 libcxx/include/__algorithm/for_each_n.h       |  2 +
 libcxx/include/__algorithm/ranges_copy_n.h    |  2 +
 .../include/__algorithm/ranges_for_each_n.h   |  2 +
 libcxx/include/__bit_reference                | 17 +++++----
 .../alg.foreach/assert.for_each_n.pass.cpp    | 35 ++++++++++++++++++
 .../assert.ranges.for_each_n.pass.cpp         | 37 +++++++++++++++++++
 .../alg.copy/copy_n.pass.cpp                  | 16 ++++++++
 .../alg.copy/ranges.copy_n.pass.cpp           | 17 +++++++++
 .../alg.fill/fill_n.pass.cpp                  | 15 ++++++++
 .../alg.fill/ranges.fill_n.pass.cpp           | 15 ++++++++
 .../alg.generate/generate_n.pass.cpp          |  7 ++++
 .../alg.generate/ranges_generate_n.pass.cpp   | 15 +++++++-
 13 files changed, 181 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
 create mode 100644 libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index ac52b2b686c28..a0ea02121b70b 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -42,7 +42,7 @@ template <class _AlgPolicy,
           __enable_if_t<!__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
-  while (__n != 0) {
+  while (__n > 0) {
     *__result = *__first;
     ++__first;
     ++__result;
@@ -63,14 +63,15 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted > 0) {
+  if (__converted < 0) [[__unlikely__]]
+    return __result;
+
+  *__result = *__first;
+  ++__result;
+  for (--__converted; __converted > 0; --__converted) {
+    ++__first;
     *__result = *__first;
     ++__result;
-    for (--__converted; __converted > 0; --__converted) {
-      ++__first;
-      *__result = *__first;
-      ++__result;
-    }
   }
   return __result;
 }
@@ -83,6 +84,8 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
+  if (__converted < 0) [[__unlikely__]]
+    return __result;
   return std::__copy_n<_ClassicAlgPolicy>(__first, __iterator_difference_type<_InputIterator>(__converted), __result)
       .__out_;
 }
diff --git a/libcxx/include/__algorithm/for_each_n.h b/libcxx/include/__algorithm/for_each_n.h
index 7ff3f9b157c02..9b393b307f323 100644
--- a/libcxx/include/__algorithm/for_each_n.h
+++ b/libcxx/include/__algorithm/for_each_n.h
@@ -12,6 +12,7 @@
 
 #include <__algorithm/for_each.h>
 #include <__algorithm/for_each_n_segment.h>
+#include <__assert>
 #include <__config>
 #include <__functional/identity.h>
 #include <__iterator/iterator_traits.h>
@@ -63,6 +64,7 @@ __for_each_n(_InputIterator __first, _Size __orig_n, _Func&& __f, _Proj& __proj)
 template <class _InputIterator, class _Size, class _Func>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
 for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__orig_n >= 0, "for_each_n requires a non-negative count");
   __identity __proj;
   return std::__for_each_n(__first, __orig_n, __f, __proj);
 }
diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
index 1720ac358fa4d..c153e74009801 100644
--- a/libcxx/include/__algorithm/ranges_copy_n.h
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -38,6 +38,8 @@ struct __copy_n {
     requires indirectly_copyable<_Ip, _Op>
   _LIBCPP_HIDE_FROM_ABI constexpr copy_n_result<_Ip, _Op>
   operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const {
+    if (__n < 0) [[__unlikely__]]
+      return {std::move(__first), std::move(__result)};
     return std::__copy_n<_RangeAlgPolicy>(std::move(__first), __n, std::move(__result));
   }
 };
diff --git a/libcxx/include/__algorithm/ranges_for_each_n.h b/libcxx/include/__algorithm/ranges_for_each_n.h
index 3aab1b79c10a1..126dc481ec723 100644
--- a/libcxx/include/__algorithm/ranges_for_each_n.h
+++ b/libcxx/include/__algorithm/ranges_for_each_n.h
@@ -11,6 +11,7 @@
 
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/in_fun_result.h>
+#include <__assert>
 #include <__config>
 #include <__functional/identity.h>
 #include <__iterator/concepts.h>
@@ -40,6 +41,7 @@ struct __for_each_n {
   template <input_iterator _Iter, class _Proj = identity, indirectly_unary_invocable<projected<_Iter, _Proj>> _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr for_each_n_result<_Iter, _Func>
   operator()(_Iter __first, iter_difference_t<_Iter> __count, _Func __func, _Proj __proj = {}) const {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__count >= 0, "for_each_n requires a non-negative count");
     auto __last = std::__for_each_n(std::move(__first), __count, __func, __proj);
     return {std::move(__last), std::move(__func)};
   }
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 692686f1584d0..0083714b2cafe 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -424,8 +424,8 @@ public:
   }
 
 #if _LIBCPP_STD_VER <= 17
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool
-  operator!=(const __bit_iterator& __x, const __bit_iterator& __y) {
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool operator!=(const __bit_iterator& __x, const __bit_iterator& __y) {
     return !(__x == __y);
   }
 
@@ -573,12 +573,13 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n > 0) {
-      if (__value)
-        __impl<true>(__first, __n);
-      else
-        __impl<false>(__first, __n);
-    }
+    if (__n < 0) [[__unlikely__]]
+      return __first;
+
+    if (__value)
+      __impl<true>(__first, __n);
+    else
+      __impl<false>(__first, __n);
     return __first + __n;
   }
 };
diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
new file mode 100644
index 0000000000000..53e59401430fa
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<class InputIterator, class Size, class Function>
+//    constexpr InputIterator
+//    for_each_n(InputIterator first, Size n, Function f);
+//
+// [alg.foreach] requires `n >= 0`; passing a negative count is a precondition violation.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <algorithm>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  int a[] = {1, 2, 3};
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp
new file mode 100644
index 0000000000000..e57f342e760a6
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.ranges.for_each_n.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<input_iterator I, class Proj = identity,
+//          indirectly_unary_invocable<projected<I, Proj>> Fun>
+//   constexpr ranges::for_each_n_result<I, Fun>
+//     ranges::for_each_n(I first, iter_difference_t<I> n, Fun f, Proj proj = {});
+//
+// [alg.foreach] requires `n >= 0`; passing a negative count is a precondition violation.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+#include <algorithm>
+#include <array>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  std::array a = {1, 2, 3};
+
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::ranges::for_each_n(a.begin(), -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::ranges::for_each_n(a.begin(), -10000000, [](int) {}), "for_each_n requires a non-negative count");
+
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 183f38c1ce117..52d99f018110b 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -61,6 +61,15 @@ struct TestIterators {
       assert(base(r) == ib + N / 2);
       for (unsigned i = 0; i < N / 2; ++i)
         assert(ia[i] == ib[i]);
+
+      { // A negative count is a no-op that returns the unchanged output iterator.
+        // Regression test for https://llvm.org/PR193613.
+        int source[]  = {1, 2, 3};
+        int dest[]    = {-1, -2, -3};
+        OutIter ret   = std::copy_n(InIter(source), -5, OutIter(dest));
+        assert(base(ret) == dest);
+        assert(dest[0] == -1 && dest[1] == -2 && dest[2] == -3);
+      }
     }
   };
 };
@@ -81,6 +90,13 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
     for (std::size_t i = 0; i < N; ++i)
       assert(out[i + 4] == in[i]);
   }
+  { // Negative count test
+    std::vector<bool> source(N, true);
+    std::vector<bool> dest(N, false);
+    std::vector<bool>::iterator r = std::copy_n(source.begin(), -5, dest.begin());
+    assert(r == dest.begin());
+    assert(dest == std::vector<bool>(N, false));
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
index 132993dba76b9..5e073026ed8db 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
@@ -60,6 +60,16 @@ constexpr void test_iterators() {
     assert(base(ret.in) == in.data());
     assert(base(ret.out) == out.data());
   }
+
+  { // A negative count is a no-op that returns the unchanged iterators.
+    // Regression test for https://llvm.org/PR193613.
+    std::array in{1, 2, 3};
+    std::array out{-1, -2, -3};
+    std::same_as<std::ranges::in_out_result<In, Out>> auto ret = std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
+    assert(base(ret.in) == in.data());
+    assert(base(ret.out) == out.data());
+    assert((out == std::array{-1, -2, -3}));
+  }
 }
 
 #if TEST_STD_VER >= 23
@@ -79,6 +89,13 @@ constexpr bool test_vector_bool(std::size_t N) {
     for (std::size_t i = 0; i < N; ++i)
       assert(out[i + 4] == in[i]);
   }
+  { // Negative count test
+    std::vector<bool> source(N, true);
+    std::vector<bool> dest(N, false);
+    auto ret = std::ranges::copy_n(source.begin(), -5, dest.begin());
+    assert(ret.out == dest.begin());
+    assert(dest == std::vector<bool>(N, false));
+  }
 
   return true;
 };
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
index 6d7ba98a33b65..cc9aaeb04d7e8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
@@ -52,6 +52,14 @@ struct Test {
       std::array<T, 4> expected = {1, 5, 5, 4};
       test<Iter>(in, 1, 2, 5, expected);
     }
+    { // A negative count is a no-op that returns the unchanged iterator.
+      // Regression test for https://llvm.org/PR193613.
+      std::array<T, 4> in       = {1, 2, 3, 4};
+      std::array<T, 4> expected = {1, 2, 3, 4};
+      Iter it                   = std::fill_n(Iter(in.data()), -5, T(9));
+      assert(base(it) == in.data());
+      assert(in == expected);
+    }
   }
 };
 
@@ -126,6 +134,13 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
     }
   }
 
+  { // Negative count test
+    std::vector<bool> v(N, false);
+    std::vector<bool>::iterator r = std::fill_n(v.begin(), -5, true);
+    assert(r == v.begin());
+    assert(v == std::vector<bool>(N, false));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
index 0de6f4a625136..a8eb6c4eb9737 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp
@@ -51,6 +51,14 @@ constexpr void test_iterators() {
     auto ret = std::ranges::fill_n(It(a.data()), 0, 1);
     assert(base(ret) == a.data());
   }
+
+  { // A negative count is a no-op that returns the unchanged iterator.
+    // Regression test for https://llvm.org/PR193613.
+    int a[3]                            = {-1, -2, -3};
+    std::same_as<It> decltype(auto) ret = std::ranges::fill_n(It(a), -5, 1);
+    assert(base(ret) == a);
+    assert(a[0] == -1 && a[1] == -2 && a[2] == -3);
+  }
 }
 
 // The `ranges::{fill, fill_n}` algorithms require `vector<bool, Alloc>::iterator` to satisfy
@@ -99,6 +107,13 @@ constexpr bool test_vector_bool(std::size_t N) {
     }
   }
 
+  { // Negative count test
+    std::vector<bool> v(N, false);
+    auto r = std::ranges::fill_n(v.begin(), -5, true);
+    assert(r == v.begin());
+    assert(v == std::vector<bool>(N, false));
+  }
+
   return true;
 }
 #endif
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
index 525737ceacfbf..0e38a89613aa8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/generate_n.pass.cpp
@@ -70,6 +70,13 @@ test()
     test2<Iter, float>();
     test2<Iter, double>();  // this is PR#35498
     test2<Iter, long double>();
+
+    { // A negative count is a no-op that returns the unchanged iterator.
+      // Regression test for https://llvm.org/PR193613.
+      int ia[] = {1, 2, 3};
+      assert(std::generate_n(Iter(ia), -5, gen_test()) == Iter(ia));
+      assert(ia[0] == 1 && ia[1] == 2 && ia[2] == 3);
+    }
 }
 
 void deque_test() {
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
index 33d40a39cf385..6154ab240c3d6 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.generate/ranges_generate_n.pass.cpp
@@ -92,6 +92,20 @@ constexpr void test_iter() {
   test_one<Iter>(std::array{-10, -20, -30, -40, -50}, 3, gen, {1, 2, 3, -40, -50});
   // Longer sequence, n = 5.
   test_one<Iter>(std::array<int, 5>{}, 5, gen, {1, 2, 3, 4, 5});
+
+  { // A negative count is a no-op that returns the unchanged iterator.
+    // Regression test for https://llvm.org/PR193613.
+    std::array in{-10, -20, -30};
+    int called        = 0;
+    auto counting_gen = [&called] {
+      ++called;
+      return 42;
+    };
+    std::same_as<Iter> decltype(auto) result = std::ranges::generate_n(Iter(in.data()), -5, counting_gen);
+    assert(base(result) == in.data());
+    assert(called == 0);
+    assert((in == std::array{-10, -20, -30}));
+  }
 }
 
 constexpr void test_iterators() {
@@ -131,7 +145,6 @@ constexpr bool test() {
     assert(gen_invocations == N2);
   }
 
-
   return true;
 }
 

>From 892279c90e066c4af5e0ef600fe47df0ba9dbf9e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 17:31:39 -0400
Subject: [PATCH 02/12] Fix check

---
 libcxx/include/__bit_reference | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 0083714b2cafe..b07c7ddccd5a0 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -573,7 +573,7 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n < 0) [[__unlikely__]]
+    if (__n <= 0) [[__unlikely__]]
       return __first;
 
     if (__value)

>From 87de9ba731d496c0900154847a077591dde9c906 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 17:36:19 -0400
Subject: [PATCH 03/12] Revert to != 0

---
 libcxx/include/__algorithm/copy_n.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index a0ea02121b70b..7ce0ec53b0c53 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -42,7 +42,7 @@ template <class _AlgPolicy,
           __enable_if_t<!__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
-  while (__n > 0) {
+  while (__n != 0) {
     *__result = *__first;
     ++__first;
     ++__result;

>From 86c6915b6d53b0ffde7b3a2ccb197b05f28c85ee Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 12:28:02 -0400
Subject: [PATCH 04/12] Drop [[__unlikely__]]

---
 libcxx/include/__algorithm/copy_n.h        | 4 ++--
 libcxx/include/__algorithm/ranges_copy_n.h | 2 +-
 libcxx/include/__bit_reference             | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index 7ce0ec53b0c53..7f9a109a763ce 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -63,7 +63,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0) [[__unlikely__]]
+  if (__converted < 0)
     return __result;
 
   *__result = *__first;
@@ -84,7 +84,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0) [[__unlikely__]]
+  if (__converted < 0)
     return __result;
   return std::__copy_n<_ClassicAlgPolicy>(__first, __iterator_difference_type<_InputIterator>(__converted), __result)
       .__out_;
diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
index c153e74009801..7baf1932bb3d8 100644
--- a/libcxx/include/__algorithm/ranges_copy_n.h
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -38,7 +38,7 @@ struct __copy_n {
     requires indirectly_copyable<_Ip, _Op>
   _LIBCPP_HIDE_FROM_ABI constexpr copy_n_result<_Ip, _Op>
   operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const {
-    if (__n < 0) [[__unlikely__]]
+    if (__n < 0)
       return {std::move(__first), std::move(__result)};
     return std::__copy_n<_RangeAlgPolicy>(std::move(__first), __n, std::move(__result));
   }
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index b07c7ddccd5a0..23ff6eaf56684 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -573,7 +573,7 @@ struct __specialized_algorithm<_Algorithm::__fill_n, __single_iterator<__bit_ite
   template <class _Size, class _Tp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static __bit_iterator<_Cp, false>
   operator()(__bit_iterator<_Cp, false> __first, _Size __n, const _Tp& __value) {
-    if (__n <= 0) [[__unlikely__]]
+    if (__n <= 0)
       return __first;
 
     if (__value)

>From 6088d6dfa8ef53749309e627c85783bedb3ab248 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 12:33:12 -0400
Subject: [PATCH 05/12] Format

---
 .../alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp | 6 ++----
 .../alg.modifying.operations/alg.copy/copy_n.pass.cpp       | 6 +++---
 .../alg.copy/ranges.copy_n.pass.cpp                         | 3 ++-
 3 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
index 53e59401430fa..5ae0dbc865b46 100644
--- a/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/alg.nonmodifying/alg.foreach/assert.for_each_n.pass.cpp
@@ -26,10 +26,8 @@
 int main(int, char**) {
   int a[] = {1, 2, 3};
 
-  TEST_LIBCPP_ASSERT_FAILURE(
-      std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
-  TEST_LIBCPP_ASSERT_FAILURE(
-      std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(std::for_each_n(a, -1, [](int) {}), "for_each_n requires a non-negative count");
+  TEST_LIBCPP_ASSERT_FAILURE(std::for_each_n(a, -10000000, [](int) {}), "for_each_n requires a non-negative count");
 
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 52d99f018110b..3348fcf358580 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -64,9 +64,9 @@ struct TestIterators {
 
       { // A negative count is a no-op that returns the unchanged output iterator.
         // Regression test for https://llvm.org/PR193613.
-        int source[]  = {1, 2, 3};
-        int dest[]    = {-1, -2, -3};
-        OutIter ret   = std::copy_n(InIter(source), -5, OutIter(dest));
+        int source[] = {1, 2, 3};
+        int dest[]   = {-1, -2, -3};
+        OutIter ret  = std::copy_n(InIter(source), -5, OutIter(dest));
         assert(base(ret) == dest);
         assert(dest[0] == -1 && dest[1] == -2 && dest[2] == -3);
       }
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
index 5e073026ed8db..c354076b63363 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
@@ -65,7 +65,8 @@ constexpr void test_iterators() {
     // Regression test for https://llvm.org/PR193613.
     std::array in{1, 2, 3};
     std::array out{-1, -2, -3};
-    std::same_as<std::ranges::in_out_result<In, Out>> auto ret = std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
+    std::same_as<std::ranges::in_out_result<In, Out>> auto ret =
+        std::ranges::copy_n(In(in.data()), -5, Out(out.data()));
     assert(base(ret.in) == in.data());
     assert(base(ret.out) == out.data());
     assert((out == std::array{-1, -2, -3}));

>From d29c394231476873ae920ceaa26d498a3a96eca5 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 12:57:25 -0400
Subject: [PATCH 06/12] [libc++] Fix bug in std::copy_n for n == 0

---
 libcxx/include/__algorithm/copy_n.h           |  2 +-
 .../alg.copy/copy_n.pass.cpp                  | 50 +++++++++++++++++++
 .../alg.fill/fill_n.pass.cpp                  |  2 +
 3 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index 7f9a109a763ce..efd5624d94f67 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -63,7 +63,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0)
+  if (__converted <= 0)
     return __result;
 
   *__result = *__first;
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 3348fcf358580..3e673cee21f69 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -12,9 +12,12 @@
 //   constexpr OutIter   // constexpr after C++17
 //   copy_n(InIter first, InIter::difference_type n, OutIter result);
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 #include <algorithm>
 #include <cassert>
 #include <cstdint>
+#include <iterator>
 #include <vector>
 
 #include "test_macros.h"
@@ -24,6 +27,31 @@
 
 typedef UserDefinedIntegral<unsigned> UDI;
 
+// A minimal single-pass input iterator that counts how many times it is advanced. Used to verify that
+// copy_n reads exactly n elements (advancing the iterator only n - 1 times).
+struct CountingInputIterator {
+  using iterator_category = std::input_iterator_tag;
+  using value_type        = int;
+  using difference_type   = long;
+  using pointer           = const int*;
+  using reference         = const int&;
+
+  const int* base_;
+  int* increments_;
+
+  TEST_CONSTEXPR_CXX14 reference operator*() const { return *base_; }
+  TEST_CONSTEXPR_CXX14 CountingInputIterator& operator++() {
+    ++base_;
+    ++*increments_;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX14 CountingInputIterator operator++(int) {
+    CountingInputIterator __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+};
+
 class PaddedBase {
 public:
   TEST_CONSTEXPR PaddedBase(std::int16_t a, std::int8_t b) : a_(a), b_(b) {}
@@ -130,6 +158,28 @@ TEST_CONSTEXPR_CXX20 bool test() {
     assert(test_vector_bool(256));
   }
 
+  { // For a single-pass input iterator, copy_n reads exactly n elements -- advancing the iterator only
+    // n - 1 times. For n == 0, it shouldn't advance the iterator. See 99847d2bf132.
+    int in[] = {1, 2, 3, 4, 5};
+
+    { // n == 0 is a no-op
+      int out[3]     = {-1, -2, -3};
+      int increments = 0;
+      int* r         = std::copy_n(CountingInputIterator{in, &increments}, 0, out);
+      assert(r == out);
+      assert(increments == 0);
+      assert(out[0] == -1 && out[1] == -2 && out[2] == -3);
+    }
+    { // n > 0 advances the iterator exactly n - 1 times
+      int out[3]     = {0, 0, 0};
+      int increments = 0;
+      int* r         = std::copy_n(CountingInputIterator{in, &increments}, 3, out);
+      assert(r == out + 3);
+      assert(increments == 2);
+      assert(out[0] == 1 && out[1] == 2 && out[2] == 3);
+    }
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
index cc9aaeb04d7e8..5a9214923577a 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp
@@ -13,6 +13,8 @@
 //   constexpr OutputIterator      // constexpr after C++17
 //   fill_n(Iter first, Size n, const T& value);
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 #include <algorithm>
 #include <array>
 #include <cassert>

>From f23831825ac3423d00dd02265b6ffc53e8776b32 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 2 Jul 2026 13:53:09 -0400
Subject: [PATCH 07/12] Move checks to implementations instead of public APIs,
 consistently with what we do elsewhere

---
 libcxx/include/__algorithm/copy_n.h        | 19 +++++++++----------
 libcxx/include/__algorithm/generate_n.h    |  3 +++
 libcxx/include/__algorithm/ranges_copy_n.h |  2 --
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index efd5624d94f67..d0df73a405cfa 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -33,6 +33,8 @@ template <class _AlgPolicy,
           __enable_if_t<__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
+  if (__n <= 0)
+    return __in_out_result<_InIter, _OutIter>{std::move(__first), std::move(__result)};
   return std::__copy(__first, __first + __n, std::move(__result));
 }
 
@@ -42,7 +44,7 @@ template <class _AlgPolicy,
           __enable_if_t<!__has_random_access_iterator_category<_InIter>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
-  while (__n != 0) {
+  while (__n > 0) {
     *__result = *__first;
     ++__first;
     ++__result;
@@ -63,15 +65,14 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted <= 0)
-    return __result;
-
-  *__result = *__first;
-  ++__result;
-  for (--__converted; __converted > 0; --__converted) {
-    ++__first;
+  if (__converted > 0) {
     *__result = *__first;
     ++__result;
+    for (--__converted; __converted > 0; --__converted) {
+      ++__first;
+      *__result = *__first;
+      ++__result;
+    }
   }
   return __result;
 }
@@ -84,8 +85,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 copy_n(_InputIterator __first, _Size __n, _OutputIterator __result) {
   using _IntegralSize       = decltype(std::__convert_to_integral(__n));
   _IntegralSize __converted = __n;
-  if (__converted < 0)
-    return __result;
   return std::__copy_n<_ClassicAlgPolicy>(__first, __iterator_difference_type<_InputIterator>(__converted), __result)
       .__out_;
 }
diff --git a/libcxx/include/__algorithm/generate_n.h b/libcxx/include/__algorithm/generate_n.h
index ba04eb62e3c3a..8ce59ca3caf68 100644
--- a/libcxx/include/__algorithm/generate_n.h
+++ b/libcxx/include/__algorithm/generate_n.h
@@ -27,6 +27,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _OutputIterator, class _Size, class _Generator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 __generate_n(_OutputIterator __first, _Size __orig_n, _Generator& __gen) {
+  if (__orig_n <= 0)
+    return __first;
+
   using __iter_ref = decltype(*__first);
   __identity __proj;
   return std::__for_each_n(
diff --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
index 7baf1932bb3d8..1720ac358fa4d 100644
--- a/libcxx/include/__algorithm/ranges_copy_n.h
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -38,8 +38,6 @@ struct __copy_n {
     requires indirectly_copyable<_Ip, _Op>
   _LIBCPP_HIDE_FROM_ABI constexpr copy_n_result<_Ip, _Op>
   operator()(_Ip __first, iter_difference_t<_Ip> __n, _Op __result) const {
-    if (__n < 0)
-      return {std::move(__first), std::move(__result)};
     return std::__copy_n<_RangeAlgPolicy>(std::move(__first), __n, std::move(__result));
   }
 };

>From 75284e73648df453097af3f12f6db89e698f938d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 9 Jul 2026 13:02:56 -0400
Subject: [PATCH 08/12] convert-to-integral

---
 libcxx/include/__algorithm/generate_n.h | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__algorithm/generate_n.h b/libcxx/include/__algorithm/generate_n.h
index 8ce59ca3caf68..fa75fde457b30 100644
--- a/libcxx/include/__algorithm/generate_n.h
+++ b/libcxx/include/__algorithm/generate_n.h
@@ -12,6 +12,7 @@
 #include <__algorithm/for_each_n.h>
 #include <__config>
 #include <__functional/identity.h>
+#include <__utility/convert_to_integral.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 
@@ -27,16 +28,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _OutputIterator, class _Size, class _Generator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 __generate_n(_OutputIterator __first, _Size __orig_n, _Generator& __gen) {
-  if (__orig_n <= 0)
+  auto __n = std::__convert_to_integral(__orig_n);
+  if (__n <= 0)
     return __first;
 
   using __iter_ref = decltype(*__first);
   __identity __proj;
   return std::__for_each_n(
-      std::move(__first),
-      __orig_n,
-      [&](__iter_ref __element) { std::forward<__iter_ref>(__element) = __gen(); },
-      __proj);
+      std::move(__first), __n, [&](__iter_ref __element) { std::forward<__iter_ref>(__element) = __gen(); }, __proj);
 }
 
 template <class _OutputIterator, class _Size, class _Generator>

>From 2551194ccb173b80697913e022e2fff48bef2201 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 9 Jul 2026 13:04:55 -0400
Subject: [PATCH 09/12] convert to integral in the public API instead, which
 seems more consistent with the codebase

---
 libcxx/include/__algorithm/generate_n.h | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__algorithm/generate_n.h b/libcxx/include/__algorithm/generate_n.h
index fa75fde457b30..693cd50ced2b1 100644
--- a/libcxx/include/__algorithm/generate_n.h
+++ b/libcxx/include/__algorithm/generate_n.h
@@ -27,8 +27,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _OutputIterator, class _Size, class _Generator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
-__generate_n(_OutputIterator __first, _Size __orig_n, _Generator& __gen) {
-  auto __n = std::__convert_to_integral(__orig_n);
+__generate_n(_OutputIterator __first, _Size __n, _Generator& __gen) {
   if (__n <= 0)
     return __first;
 
@@ -41,7 +40,7 @@ __generate_n(_OutputIterator __first, _Size __orig_n, _Generator& __gen) {
 template <class _OutputIterator, class _Size, class _Generator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
 generate_n(_OutputIterator __first, _Size __orig_n, _Generator __gen) {
-  return std::__generate_n(std::move(__first), __orig_n, __gen);
+  return std::__generate_n(std::move(__first), std::__convert_to_integral(__orig_n), __gen);
 }
 
 _LIBCPP_END_NAMESPACE_STD

>From d5fdea4c7ace67bddc39136020ef9b7a47db1a75 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 9 Jul 2026 13:08:37 -0400
Subject: [PATCH 10/12] Convert to integral in for_each_n too, needed for
 hardening

---
 libcxx/include/__algorithm/for_each_n.h | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__algorithm/for_each_n.h b/libcxx/include/__algorithm/for_each_n.h
index 9b393b307f323..2b9ba4a6bc983 100644
--- a/libcxx/include/__algorithm/for_each_n.h
+++ b/libcxx/include/__algorithm/for_each_n.h
@@ -33,15 +33,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _InputIterator, class _Size, class _Func, class _Proj>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
-__for_each_n(_InputIterator __first, _Size __orig_n, _Func&& __f, _Proj& __proj) {
-  typedef decltype(std::__convert_to_integral(__orig_n)) _IntegralSize;
-  _IntegralSize __n = __orig_n;
-
+__for_each_n(_InputIterator __first, _Size __n, _Func&& __f, _Proj& __proj) {
 #ifndef _LIBCPP_CXX03_LANG
   if constexpr (__is_segmented_iterator_v<_InputIterator>) {
     using __local_iterator = typename __segmented_iterator_traits<_InputIterator>::__local_iterator;
     if constexpr (__has_random_access_iterator_category<__local_iterator>::value) {
-      return std::__for_each_n_segment(__first, __orig_n, [&](__local_iterator __lfirst, __local_iterator __llast) {
+      return std::__for_each_n_segment(__first, __n, [&](__local_iterator __lfirst, __local_iterator __llast) {
         std::__for_each(__lfirst, __llast, __f, __proj);
       });
     } else {
@@ -62,11 +59,12 @@ __for_each_n(_InputIterator __first, _Size __orig_n, _Func&& __f, _Proj& __proj)
 #if _LIBCPP_STD_VER >= 17
 
 template <class _InputIterator, class _Size, class _Func>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
-for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
-  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__orig_n >= 0, "for_each_n requires a non-negative count");
+inline _LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
+  auto __n = std::__convert_to_integral(__orig_n);
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n >= 0, "for_each_n requires a non-negative count");
   __identity __proj;
-  return std::__for_each_n(__first, __orig_n, __f, __proj);
+  return std::__for_each_n(__first, __n, __f, __proj);
 }
 
 #endif // _LIBCPP_STD_VER >= 17

>From b947ba1a1f74dbf2353110ea76c910bef05dbdb4 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 14 Jul 2026 07:56:03 -0400
Subject: [PATCH 11/12] Avoid C++03 error

---
 libcxx/include/__algorithm/copy_n.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__algorithm/copy_n.h b/libcxx/include/__algorithm/copy_n.h
index d0df73a405cfa..69ce09de9b7da 100644
--- a/libcxx/include/__algorithm/copy_n.h
+++ b/libcxx/include/__algorithm/copy_n.h
@@ -34,7 +34,7 @@ template <class _AlgPolicy,
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
 __copy_n(_InIter __first, typename _IterOps<_AlgPolicy>::template __difference_type<_InIter> __n, _OutIter __result) {
   if (__n <= 0)
-    return __in_out_result<_InIter, _OutIter>{std::move(__first), std::move(__result)};
+    return {std::move(__first), std::move(__result)};
   return std::__copy(__first, __first + __n, std::move(__result));
 }
 

>From 696b275e492556e1342219a38211afd3209a0bb0 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 14 Jul 2026 09:04:35 -0400
Subject: [PATCH 12/12] Fix c++03

---
 .../alg.copy/copy_n.pass.cpp                        | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
index 3e673cee21f69..252f50ba0ac19 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_n.pass.cpp
@@ -36,12 +36,15 @@ struct CountingInputIterator {
   using pointer           = const int*;
   using reference         = const int&;
 
-  const int* base_;
+  const int* ptr_;
   int* increments_;
 
-  TEST_CONSTEXPR_CXX14 reference operator*() const { return *base_; }
+  TEST_CONSTEXPR_CXX14 CountingInputIterator(const int* ptr, int* increment_counter)
+      : ptr_(ptr), increments_(increment_counter) {}
+
+  TEST_CONSTEXPR_CXX14 reference operator*() const { return *ptr_; }
   TEST_CONSTEXPR_CXX14 CountingInputIterator& operator++() {
-    ++base_;
+    ++ptr_;
     ++*increments_;
     return *this;
   }
@@ -165,7 +168,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
     { // n == 0 is a no-op
       int out[3]     = {-1, -2, -3};
       int increments = 0;
-      int* r         = std::copy_n(CountingInputIterator{in, &increments}, 0, out);
+      int* r         = std::copy_n(CountingInputIterator(in, &increments), 0, out);
       assert(r == out);
       assert(increments == 0);
       assert(out[0] == -1 && out[1] == -2 && out[2] == -3);
@@ -173,7 +176,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
     { // n > 0 advances the iterator exactly n - 1 times
       int out[3]     = {0, 0, 0};
       int increments = 0;
-      int* r         = std::copy_n(CountingInputIterator{in, &increments}, 3, out);
+      int* r         = std::copy_n(CountingInputIterator(in, &increments), 3, out);
       assert(r == out + 3);
       assert(increments == 2);
       assert(out[0] == 1 && out[1] == 2 && out[2] == 3);


From libcxx-commits at lists.llvm.org  Tue Jul 14 06:05:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 06:05:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 40152b8 - [libc++][NFC] Inline
 std::function members into the class body (#209461)
Message-ID: <6a563413.95816bbb.48780.cca4@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-14T15:05:18+02:00
New Revision: 40152b8767769873a8f0d99b857f07c874cccf4a

URL: https://github.com/llvm/llvm-project/commit/40152b8767769873a8f0d99b857f07c874cccf4a
DIFF: https://github.com/llvm/llvm-project/commit/40152b8767769873a8f0d99b857f07c874cccf4a.diff

LOG: [libc++][NFC] Inline std::function members into the class body (#209461)

The `std::function` members are incredibly simple to the point where
they are just wrappers around internal functions. We can inline them
into the body to simplify the implementation a bit.

Added: 
    

Modified: 
    libcxx/include/__functional/function.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__functional/function.h b/libcxx/include/__functional/function.h
index 90072e9528484..0adc336424e2e 100644
--- a/libcxx/include/__functional/function.h
+++ b/libcxx/include/__functional/function.h
@@ -340,7 +340,7 @@ template <typename _Fun>
 struct __use_small_storage
     : public integral_constant<
           bool,
-          sizeof(_Fun) <= sizeof(__policy_storage)&& _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
+          sizeof(_Fun) <= sizeof(__policy_storage) && _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
               is_trivially_copy_constructible<_Fun>::value && is_trivially_destructible<_Fun>::value> {};
 
 // Policy contains information about how to copy, destroy, and move the
@@ -628,10 +628,10 @@ class function<_Rp(_ArgTypes...)>
   // construct/copy/destroy:
   _LIBCPP_HIDE_FROM_ABI function() _NOEXCEPT {}
   _LIBCPP_HIDE_FROM_ABI function(nullptr_t) _NOEXCEPT {}
-  _LIBCPP_HIDE_FROM_ABI function(const function&);
-  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function(const function&)      = default;
+  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT = default;
   template <class _Fp, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(_Fp);
+  _LIBCPP_HIDE_FROM_ABI function(_Fp __f) : __f_(std::move(__f)) {}
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Alloc>
@@ -639,23 +639,35 @@ class function<_Rp(_ArgTypes...)>
   template <class _Alloc>
   _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, nullptr_t) _NOEXCEPT {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function&);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&&);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
   template <class _Fp, class _Alloc, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc& __a, _Fp __f);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI function& operator=(const function&);
-  _LIBCPP_HIDE_FROM_ABI function& operator=(function&&) _NOEXCEPT;
-  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function& operator=(const function& __f) {
+    function(__f).swap(*this);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI function& operator=(function&& __f) _NOEXCEPT = default;
+
+  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT {
+    __f_ = nullptr;
+    return *this;
+  }
+
   template <class _Fp, class = _EnableIfLValueCallable<__decay_t<_Fp>>>
-  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&&);
+  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&& __f) {
+    function(std::forward<_Fp>(__f)).swap(*this);
+    return *this;
+  }
 
-  _LIBCPP_HIDE_FROM_ABI ~function();
+  _LIBCPP_HIDE_FROM_ABI ~function() = default;
 
   // function modifiers:
-  _LIBCPP_HIDE_FROM_ABI void swap(function&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI void swap(function& __f) _NOEXCEPT { __f_.swap(__f.__f_); }
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Fp, class _Alloc>
@@ -677,15 +689,23 @@ class function<_Rp(_ArgTypes...)>
 
 public:
   // function invocation:
-  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes...) const;
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const { return __f_(std::forward<_ArgTypes>(__args)...); }
 
 #  if _LIBCPP_HAS_RTTI
   // function target access:
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT {
+    return __f_.target_type();
+  }
+
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT {
+    return (_Tp*)(__f_.template target<_Tp>());
+  }
+
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT {
+    return __f_.template target<_Tp>();
+  }
 #  endif // _LIBCPP_HAS_RTTI
 };
 
@@ -697,93 +717,6 @@ template <class _Fp, class _Stripped = __strip_signature_t<decltype(&_Fp::operat
 function(_Fp) -> function<_Stripped>;
 #  endif // _LIBCPP_STD_VER >= 17
 
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::function(const function& __f) : __f_(__f.__f_) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Alloc>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::function(function&& __f) _NOEXCEPT : __f_(std::move(__f.__f_)) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Alloc>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class>
-function<_Rp(_ArgTypes...)>::function(_Fp __f) : __f_(std::move(__f)) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class _Alloc, class>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(const function& __f) {
-  function(__f).swap(*this);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(function&& __f) _NOEXCEPT {
-  __f_ = std::move(__f.__f_);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(nullptr_t) _NOEXCEPT {
-  __f_ = nullptr;
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f) {
-  function(std::forward<_Fp>(__f)).swap(*this);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::~function() {}
-
-template <class _Rp, class... _ArgTypes>
-void function<_Rp(_ArgTypes...)>::swap(function& __f) _NOEXCEPT {
-  __f_.swap(__f.__f_);
-}
-
-template <class _Rp, class... _ArgTypes>
-_Rp function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const {
-  return __f_(std::forward<_ArgTypes>(__arg)...);
-}
-
-#  if _LIBCPP_HAS_RTTI
-
-template <class _Rp, class... _ArgTypes>
-const std::type_info& function<_Rp(_ArgTypes...)>::target_type() const _NOEXCEPT {
-  return __f_.target_type();
-}
-
-template <class _Rp, class... _ArgTypes>
-template <typename _Tp>
-_Tp* function<_Rp(_ArgTypes...)>::target() _NOEXCEPT {
-  return (_Tp*)(__f_.template target<_Tp>());
-}
-
-template <class _Rp, class... _ArgTypes>
-template <typename _Tp>
-const _Tp* function<_Rp(_ArgTypes...)>::target() const _NOEXCEPT {
-  return __f_.template target<_Tp>();
-}
-
-#  endif // _LIBCPP_HAS_RTTI
-
 template <class _Rp, class... _ArgTypes>
 inline _LIBCPP_HIDE_FROM_ABI bool operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) _NOEXCEPT {
   return !__f;


        

From libcxx-commits at lists.llvm.org  Tue Jul 14 06:05:25 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 06:05:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Inline std::function
 members into the class body (PR #209461)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209461@github.com>
Message-ID: <6a563415.7a66ee31.16d7c5.9231@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/209461

From libcxx-commits at lists.llvm.org  Tue Jul 14 06:10:30 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 06:10:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Define behaviour for calling
 target() and target_type() on -fno-rtti std::functions (PR #209471)
Message-ID: <llvm.org/llvm/llvm-project/pull/209471@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/209471

None

>From 1fc6b517b7647c0a85c36b301720bb1955289875 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 14 Jul 2026 15:05:59 +0200
Subject: [PATCH] [libc++] Define behaviour for calling target() and
 target_type() on -fno-rtti std::functions

---
 libcxx/include/__functional/function.h        | 12 +++----
 .../func.wrap.func/rtti_mixing.sh.cpp         | 33 +++++++++++++++++++
 2 files changed, 39 insertions(+), 6 deletions(-)
 create mode 100644 libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp

diff --git a/libcxx/include/__functional/function.h b/libcxx/include/__functional/function.h
index 90072e9528484..8160759724d96 100644
--- a/libcxx/include/__functional/function.h
+++ b/libcxx/include/__functional/function.h
@@ -145,10 +145,8 @@ class __base<_Rp(_ArgTypes...)> {
   virtual void destroy() _NOEXCEPT            = 0;
   virtual void destroy_deallocate() _NOEXCEPT = 0;
   virtual _Rp operator()(_ArgTypes&&...)      = 0;
-#  if _LIBCPP_HAS_RTTI
-  virtual const void* target(const type_info&) const _NOEXCEPT = 0;
-  virtual const std::type_info& target_type() const _NOEXCEPT  = 0;
-#  endif // _LIBCPP_HAS_RTTI
+  virtual const void* target(const type_info&) const _NOEXCEPT { return nullptr; }
+  virtual const std::type_info* target_type() const _NOEXCEPT { return nullptr; }
 };
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
@@ -182,7 +180,7 @@ class __func<_Fp, _Rp(_ArgTypes...)> : public __base<_Rp(_ArgTypes...)> {
       return std::addressof(__func_);
     return nullptr;
   }
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL const std::type_info& target_type() const _NOEXCEPT override { return typeid(_Fp); }
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL const std::type_info* target_type() const _NOEXCEPT override { return &typeid(_Fp); }
 #  endif // _LIBCPP_HAS_RTTI
 };
 
@@ -316,7 +314,9 @@ class __value_func<_Rp(_ArgTypes...)> {
   _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT {
     if (__f_ == nullptr)
       return typeid(void);
-    return __f_->target_type();
+    if (auto __ptr = __f_->target_type())
+      return *__ptr;
+    return typeid(void);
   }
 
   template <typename _Tp>
diff --git a/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp b/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp
new file mode 100644
index 0000000000000..9a6eda694eee8
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Ensure that passing std::functions across -fno-rtti/-frtti boundaries works.
+
+#include <cassert>
+#include <functional>
+#include <typeinfo>
+
+// RUN: %{cxx} %s %{flags} %{compile_flags} -c -frtti -o %t.tu1.o
+// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DNO_RTTI -fno-rtti -o %t.tu2.o
+// RUN: %{cxx} %t.tu1.o %t.tu2.o %{flags} %{link_flags} -o %t.exe
+// RUN: %{exec} %t.exe
+
+std::function<void()> get_func();
+
+#ifdef NO_RTTI
+std::function<void()> get_func() {
+  return get_func;
+}
+#else
+int main(int, char**) {
+  assert(!get_func().target<int>());
+  assert(get_func().target_type() == typeid(void));
+
+  return 0;
+}
+#endif


From libcxx-commits at lists.llvm.org  Tue Jul 14 06:11:15 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 06:11:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Define behaviour for calling
 target() and target_type() on -fno-rtti std::functions (PR #209471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209471@github.com>
Message-ID: <6a563573.b2d2c3b2.106544.b772@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/209471.diff


2 Files Affected:

- (modified) libcxx/include/__functional/function.h (+6-6) 
- (added) libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp (+33) 


``````````diff
diff --git a/libcxx/include/__functional/function.h b/libcxx/include/__functional/function.h
index 90072e9528484..8160759724d96 100644
--- a/libcxx/include/__functional/function.h
+++ b/libcxx/include/__functional/function.h
@@ -145,10 +145,8 @@ class __base<_Rp(_ArgTypes...)> {
   virtual void destroy() _NOEXCEPT            = 0;
   virtual void destroy_deallocate() _NOEXCEPT = 0;
   virtual _Rp operator()(_ArgTypes&&...)      = 0;
-#  if _LIBCPP_HAS_RTTI
-  virtual const void* target(const type_info&) const _NOEXCEPT = 0;
-  virtual const std::type_info& target_type() const _NOEXCEPT  = 0;
-#  endif // _LIBCPP_HAS_RTTI
+  virtual const void* target(const type_info&) const _NOEXCEPT { return nullptr; }
+  virtual const std::type_info* target_type() const _NOEXCEPT { return nullptr; }
 };
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
@@ -182,7 +180,7 @@ class __func<_Fp, _Rp(_ArgTypes...)> : public __base<_Rp(_ArgTypes...)> {
       return std::addressof(__func_);
     return nullptr;
   }
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL const std::type_info& target_type() const _NOEXCEPT override { return typeid(_Fp); }
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL const std::type_info* target_type() const _NOEXCEPT override { return &typeid(_Fp); }
 #  endif // _LIBCPP_HAS_RTTI
 };
 
@@ -316,7 +314,9 @@ class __value_func<_Rp(_ArgTypes...)> {
   _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT {
     if (__f_ == nullptr)
       return typeid(void);
-    return __f_->target_type();
+    if (auto __ptr = __f_->target_type())
+      return *__ptr;
+    return typeid(void);
   }
 
   template <typename _Tp>
diff --git a/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp b/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp
new file mode 100644
index 0000000000000..9a6eda694eee8
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Ensure that passing std::functions across -fno-rtti/-frtti boundaries works.
+
+#include <cassert>
+#include <functional>
+#include <typeinfo>
+
+// RUN: %{cxx} %s %{flags} %{compile_flags} -c -frtti -o %t.tu1.o
+// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DNO_RTTI -fno-rtti -o %t.tu2.o
+// RUN: %{cxx} %t.tu1.o %t.tu2.o %{flags} %{link_flags} -o %t.exe
+// RUN: %{exec} %t.exe
+
+std::function<void()> get_func();
+
+#ifdef NO_RTTI
+std::function<void()> get_func() {
+  return get_func;
+}
+#else
+int main(int, char**) {
+  assert(!get_func().target<int>());
+  assert(get_func().target_type() == typeid(void));
+
+  return 0;
+}
+#endif

``````````

</details>


https://github.com/llvm/llvm-project/pull/209471

From libcxx-commits at lists.llvm.org  Tue Jul 14 06:12:02 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 06:12:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Define behaviour for calling
 target() and target_type() on -fno-rtti std::functions (PR #209471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209471@github.com>
Message-ID: <6a5635a2.14a6e4b5.8bf9d.37c7@mx.google.com>


================
@@ -316,7 +314,9 @@ class __value_func<_Rp(_ArgTypes...)> {
   _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT {
     if (__f_ == nullptr)
       return typeid(void);
-    return __f_->target_type();
+    if (auto __ptr = __f_->target_type())
----------------
philnik777 wrote:

Do we want to define this, or should this be an assertion?

https://github.com/llvm/llvm-project/pull/209471

From libcxx-commits at lists.llvm.org  Tue Jul 14 06:12:15 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 06:12:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Define behaviour for calling
 target() and target_type() on -fno-rtti std::functions (PR #209471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209471@github.com>
Message-ID: <6a5635af.0c106a4a.3a3b6a.621c@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions h,cpp -- libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp libcxx/include/__functional/function.h --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp b/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp
index 9a6eda694..aeacc5dea 100644
--- a/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp
+++ b/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp
@@ -20,9 +20,7 @@
 std::function<void()> get_func();
 
 #ifdef NO_RTTI
-std::function<void()> get_func() {
-  return get_func;
-}
+std::function<void()> get_func() { return get_func; }
 #else
 int main(int, char**) {
   assert(!get_func().target<int>());

``````````

</details>


https://github.com/llvm/llvm-project/pull/209471

From libcxx-commits at lists.llvm.org  Tue Jul 14 06:16:16 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 06:16:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Define behaviour for calling
 target() and target_type() on -fno-rtti std::functions (PR #209471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209471@github.com>
Message-ID: <6a5636a0.bd8478ff.10268f.6fc2@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/209471

From libcxx-commits at lists.llvm.org  Tue Jul 14 06:21:04 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 14 Jul 2026 06:21:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add sentinel OtherConst cases
 for `nodiscard` tests in `enumerate_view` (PR #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5637c0.4705fa9e.fba68.1aa7@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/209289

>From de6bef4e4ef9069b23edba3c557e7d576fc78e4a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 12 Jul 2026 18:32:39 +0200
Subject: [PATCH 1/7] add: OtherConst test to sentinel operator-

---
 .../range.enumerate/nodiscard.verify.cpp               | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..d6d93219a4e14 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,7 +64,8 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st = ev.end();
+  auto st   = ev.end();
+  auto c_it = std::as_const(ev).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -74,8 +75,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}

>From bcb2d6392e68580206412414a24029dee3c70881 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:20:42 +0200
Subject: [PATCH 2/7] ref: model non-common non-simple view for sentinel tests
 and general adjustments

---
 .../range.enumerate/nodiscard.verify.cpp      | 38 ++++++++++++-------
 1 file changed, 25 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index d6d93219a4e14..fea1bca7c2145 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,32 +14,41 @@
 #include <utility>
 #include <vector>
 
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
 void test() {
-  std::vector<int> range;
-  std::ranges::enumerate_view ev{range};
+  auto v  = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.begin();
+  v.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).begin();
+  std::as_const(v).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.end();
+  v.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).end();
+  std::as_const(v).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.size();
+  v.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).size();
+  std::as_const(v).size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.base();
+  v.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::move(ev).base();
+  std::move(v).base();
 
   // [range.enumerate.iterator]
 
-  auto it = ev.begin();
+  auto it = v.begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(it).base();
@@ -64,8 +73,7 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st   = ev.end();
-  auto c_it = std::as_const(ev).begin();
+  auto st = v.end();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -75,6 +83,8 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  auto c_it = std::as_const(v).begin();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - c_it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -82,6 +92,8 @@ void test() {
 
   // [range.enumerate.overview]
 
+  std::vector<int> range { 1, 2, 3 };
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
 }

>From 02096b8044eb8b37db4a9cdee0c35da7d297574c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:25:17 +0200
Subject: [PATCH 3/7] fix: clang-format

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index fea1bca7c2145..4ebb6196c8d9c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,7 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v  = View{} | std::views::enumerate;
+  auto v = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
@@ -92,7 +92,7 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range { 1, 2, 3 };
+  std::vector<int> range{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);

>From 9fda31d2eb80d645f56214599f544ebccb8b724a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:09:56 +0200
Subject: [PATCH 4/7] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 4ebb6196c8d9c..b2ce7f3418fe6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,8 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v = View{} | std::views::enumerate;
+  View range;
+  auto v = std::ranges::enumerate_view ev{range};
 
   // [range.enumerate.view]
 

>From 02235acb82379ce5c0bf8011275e5129effbf3ad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:11:36 +0200
Subject: [PATCH 5/7] rename range variable to vec

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index b2ce7f3418fe6..250b21e3e9910 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -93,8 +93,8 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range{1, 2, 3};
+  std::vector<int> vec{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(range);
+  std::views::enumerate(vec);
 }

>From 4ed811e010d332be499f091c05e411f3f88349a0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:16:13 +0200
Subject: [PATCH 6/7] fix: wrong variable initialization

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp    | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 250b21e3e9910..c10e56293246f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -12,7 +12,6 @@
 
 #include <ranges>
 #include <utility>
-#include <vector>
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -26,7 +25,7 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
   View range;
-  auto v = std::ranges::enumerate_view ev{range};
+  std::ranges::enumerate_view v{range};
 
   // [range.enumerate.view]
 
@@ -93,8 +92,6 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> vec{1, 2, 3};
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(vec);
+  std::views::enumerate(range);
 }

>From 214a924d09e6d0d60b8531e2da1422e69c90eae1 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 15:20:49 +0200
Subject: [PATCH 7/7] remove: c_it - st and st - c_st tests. Add static_asserts

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp  | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index c10e56293246f..4f99f0842dd11 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -22,6 +22,8 @@ struct View : std::ranges::view_interface<View> {
 static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::sized_range<View>);
+static_assert(std::ranges::forward_range<View>);
 
 void test() {
   View range;
@@ -83,13 +85,6 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
-  auto c_it = std::as_const(v).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - c_it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  c_it - st;
-
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}


From libcxx-commits at lists.llvm.org  Tue Jul 14 06:21:08 2026
From: libcxx-commits at lists.llvm.org (Douglas Yung via libcxx-commits)
Date: Tue, 14 Jul 2026 06:21:08 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
Message-ID: <llvm.org/llvm/llvm-project/pull/209473@github.com>


https://github.com/dyung created https://github.com/llvm/llvm-project/pull/209473

None

>From 45181be2ab83080105a8328df12887c14950fe49 Mon Sep 17 00:00:00 2001
From: Douglas Yung <douglas.yung at sony.com>
Date: Tue, 14 Jul 2026 13:11:38 +0000
Subject: [PATCH] Bump version to 24.0.0git

---
 clang-tools-extra/docs/ReleaseNotes.rst  |  846 ----------------
 clang/docs/ReleaseNotes.md               | 1171 +---------------------
 cmake/Modules/LLVMVersion.cmake          |    2 +-
 flang/docs/ReleaseNotes.md               |    7 -
 libcxx/include/__config                  |    2 +-
 lld/docs/ReleaseNotes.rst                |  131 ---
 llvm/docs/ReleaseNotes.md                |  383 -------
 llvm/utils/gn/secondary/llvm/version.gni |    2 +-
 llvm/utils/lit/lit/__init__.py           |    2 +-
 llvm/utils/mlgo-utils/mlgo/__init__.py   |    2 +-
 10 files changed, 9 insertions(+), 2539 deletions(-)

diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 9fee5ff0c97ce..69c3bcf67b8db 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -49,95 +49,6 @@ Major New Features
 Potentially Breaking Changes
 ----------------------------
 
-- The :doc:`modernize-use-using <clang-tidy/checks/modernize/use-using>` check
-  now sets the `IgnoreExternC` option to `true` by default. The check will
-  no longer transform ``typedef``\ s within ``extern "C"`` blocks.
-
-- Deprecated the :program:`clang-tidy` check :doc:`performance-faster-string-find
-  <clang-tidy/checks/performance/faster-string-find>`. It has been renamed to
-  :doc:`performance-prefer-single-char-overloads
-  <clang-tidy/checks/performance/prefer-single-char-overloads>`.
-  The original check will be removed in the 25th release.
-
-- Removed the :program:`clang-tidy` ``hicpp`` module. All checks have been moved
-  to the other modules. Use the replacement checks instead:
-
-  ================================== ========================================================================
-  Removed check                      Replacement check
-  ================================== ========================================================================
-  ``hicpp-avoid-c-arrays``           :doc:`modernize-avoid-c-arrays
-                                     <clang-tidy/checks/modernize/avoid-c-arrays>`
-  ``hicpp-avoid-goto``               :doc:`cppcoreguidelines-avoid-goto
-                                     <clang-tidy/checks/cppcoreguidelines/avoid-goto>`
-  ``hicpp-braces-around-statements`` :doc:`readability-braces-around-statements
-                                     <clang-tidy/checks/readability/braces-around-statements>`
-  ``hicpp-deprecated-headers``       :doc:`modernize-deprecated-headers
-                                     <clang-tidy/checks/modernize/deprecated-headers>`
-  ``hicpp-exception-baseclass``      :doc:`bugprone-std-exception-baseclass
-                                     <clang-tidy/checks/bugprone/std-exception-baseclass>`
-  ``hicpp-explicit-conversions``     :doc:`misc-explicit-constructor
-                                     <clang-tidy/checks/misc/explicit-constructor>`
-  ``hicpp-function-size``            :doc:`readability-function-size
-                                     <clang-tidy/checks/readability/function-size>`
-  ``hicpp-ignored-remove-result``    :doc:`bugprone-unused-return-value
-                                     <clang-tidy/checks/bugprone/unused-return-value>`
-  ``hicpp-invalid-access-moved``     :doc:`bugprone-use-after-move
-                                     <clang-tidy/checks/bugprone/use-after-move>`
-  ``hicpp-member-init``              :doc:`cppcoreguidelines-pro-type-member-init
-                                     <clang-tidy/checks/cppcoreguidelines/pro-type-member-init>`
-  ``hicpp-move-const-arg``           :doc:`performance-move-const-arg
-                                     <clang-tidy/checks/performance/move-const-arg>`
-  ``hicpp-multiway-paths-covered``   | :doc:`bugprone-unhandled-code-paths
-                                       <clang-tidy/checks/bugprone/unhandled-code-paths>`
-                                     | :doc:`readability-trivial-switch
-                                       <clang-tidy/checks/readability/trivial-switch>`
-  ``hicpp-named-parameter``          :doc:`readability-named-parameter
-                                     <clang-tidy/checks/readability/named-parameter>`
-  ``hicpp-new-delete-operators``     :doc:`misc-new-delete-overloads
-                                     <clang-tidy/checks/misc/new-delete-overloads>`
-  ``hicpp-no-array-decay``           :doc:`cppcoreguidelines-pro-bounds-array-to-pointer-decay
-                                     <clang-tidy/checks/cppcoreguidelines/pro-bounds-array-to-pointer-decay>`
-  ``hicpp-no-assembler``             :doc:`portability-no-assembler
-                                     <clang-tidy/checks/portability/no-assembler>`
-  ``hicpp-no-malloc``                :doc:`cppcoreguidelines-no-malloc
-                                     <clang-tidy/checks/cppcoreguidelines/no-malloc>`
-  ``hicpp-noexcept-move``            :doc:`performance-noexcept-move-constructor
-                                     <clang-tidy/checks/performance/noexcept-move-constructor>`
-  ``hicpp-signed-bitwise``           :doc:`bugprone-signed-bitwise
-                                     <clang-tidy/checks/bugprone/signed-bitwise>`
-  ``hicpp-special-member-functions`` :doc:`cppcoreguidelines-special-member-functions
-                                     <clang-tidy/checks/cppcoreguidelines/special-member-functions>`
-  ``hicpp-static-assert``            :doc:`misc-static-assert
-                                     <clang-tidy/checks/misc/static-assert>`
-  ``hicpp-uppercase-literal-suffix`` :doc:`readability-uppercase-literal-suffix
-                                     <clang-tidy/checks/readability/uppercase-literal-suffix>`
-  ``hicpp-undelegated-constructor``  :doc:`bugprone-undelegated-constructor
-                                     <clang-tidy/checks/bugprone/undelegated-constructor>`
-  ``hicpp-use-auto``                 :doc:`modernize-use-auto
-                                     <clang-tidy/checks/modernize/use-auto>`
-  ``hicpp-use-emplace``              :doc:`modernize-use-emplace
-                                     <clang-tidy/checks/modernize/use-emplace>`
-  ``hicpp-use-equals-default``       :doc:`modernize-use-equals-default
-                                     <clang-tidy/checks/modernize/use-equals-default>`
-  ``hicpp-use-equals-delete``        :doc:`modernize-use-equals-delete
-                                     <clang-tidy/checks/modernize/use-equals-delete>`
-  ``hicpp-use-noexcept``             :doc:`modernize-use-noexcept
-                                     <clang-tidy/checks/modernize/use-noexcept>`
-  ``hicpp-use-nullptr``              :doc:`modernize-use-nullptr
-                                     <clang-tidy/checks/modernize/use-nullptr>`
-  ``hicpp-use-override``             :doc:`modernize-use-override
-                                     <clang-tidy/checks/modernize/use-override>`
-  ``hicpp-vararg``                   :doc:`cppcoreguidelines-pro-type-vararg
-                                     <clang-tidy/checks/cppcoreguidelines/pro-type-vararg>`
-  ================================== ========================================================================
-
-- Removed the :program:`clang-doc` deprecated Markdown generator. ``--format=md``
-  now uses the new Mustache-backed MD generator. It is possible that there are some
-  regressions in MD output.
-
-- Removed the :program:`clang-doc` YAML generator. Prefer the JSON generator to
-  get documentation information in a reusable format.
-
 Improvements to clangd
 ----------------------
 
@@ -159,27 +70,9 @@ Hover
 Code completion
 ^^^^^^^^^^^^^^^
 
-- Now also provides include files without extension, if they are in a directory
-  only called ``include``.
-
-- Added support for ``InsertReplaceEdit`` in code completion (LSP 3.16),
-  allowing clients that advertise ``insertReplaceSupport`` to receive both
-  insert and replace ranges for completion items.
-
-- Changed completion-style default to ``detailed``. This means function
-  overloads will no longer be bundled together, but instead each have
-  their own completion item. This gives the user a better overview of the
-  possible overloads and also when accepting the item it will generate
-  placeholder parameters, which was not possible due to ambiguity with
-  ``bundled``. To change back to the old behaviour, pass the argument
-  ``--completion-style=bundled`` to clangd.
-
 Code actions
 ^^^^^^^^^^^^
 
-- A new tweak "Create function body out-of-line" was added that creates
-  an implementation for a function declaration.
-
 Signature help
 ^^^^^^^^^^^^^^
 
@@ -201,752 +94,15 @@ Improvements to clang-query
 Improvements to clang-tidy
 --------------------------
 
-- Improved :program:`check_clang_tidy.py` script by adding the `-check-header`
-  argument to simplify testing of header files. This argument automatically
-  manages the creation of temporary header files and ensures that diagnostics
-  and fixes are verified for the specified headers.
-
-- Improved :program:`clang-tidy` ``-store-check-profile`` by generating valid
-  JSON when the source file path contains characters that require JSON escaping.
-
-- Improved :program:`clang-tidy` by preserving literal backslash characters in
-  POSIX source paths.
-
-- Ensured that :program:`clang-tidy` and the clang compiler uses the same logic
-  for the suppression of compiler diagnostics in system headers and expansions
-  of macros defined in system headers. Previously the default setting of tidy
-  overzealously suppressed some diagnostics that would have been emitted by the
-  compiler. (E.g. tidy suppressed many ``clang-diagnostic-invalid-offsetof``
-  reports because they usually occur in expansion of the macro ``offsetof``.)
-
-- :program:`clang-tidy` will no longer exit immediately if the only enabled
-  checks are `clang-diagnostic-*` ones. This allows using
-  :program:`clang-tidy` purely as a frontend to Clang's builtin warnings.
-
 New checks
 ^^^^^^^^^^
 
-- New :doc:`bugprone-assignment-in-selection-statement
-  <clang-tidy/checks/bugprone/assignment-in-selection-statement>` check.
-
-  Finds assignments within selection statements.
-
-- New :doc:`bugprone-missing-end-comparison
-  <clang-tidy/checks/bugprone/missing-end-comparison>` check.
-
-  Finds instances where the result of a standard algorithm is used in a Boolean
-  context without being compared to the end iterator.
-
-- New :doc:`bugprone-unsafe-to-allow-exceptions
-  <clang-tidy/checks/bugprone/unsafe-to-allow-exceptions>` check.
-
-  Finds functions where throwing exceptions is unsafe but the function is still
-  marked as potentially throwing.
-
-- New :doc:`llvm-formatv-string
-  <clang-tidy/checks/llvm/formatv-string>` check.
-
-  Validates ``llvm::formatv`` format strings against the provided arguments,
-  diagnosing mismatched argument counts, unused arguments, and mixed index styles.
-
-- New :doc:`llvm-redundant-casting
-  <clang-tidy/checks/llvm/redundant-casting>` check.
-
-  Points out uses of ``cast<>``, ``dyn_cast<>`` and their ``or_null`` variants
-  that are unnecessary because the argument already is of the target type, or a
-  derived type thereof. Also does similar analysis for calls to ``isa<>`` that
-  always return ``true``.
-
-- New :doc:`llvm-type-switch-case-types
-  <clang-tidy/checks/llvm/type-switch-case-types>` check.
-
-  Finds ``llvm::TypeSwitch::Case`` calls with redundant explicit template
-  arguments that can be inferred from the lambda parameter type.
-
-- New :doc:`llvm-use-vector-utils
-  <clang-tidy/checks/llvm/use-vector-utils>` check.
-
-  Finds calls to ``llvm::to_vector(llvm::map_range(...))`` and
-  ``llvm::to_vector(llvm::make_filter_range(...))`` that can be replaced with
-  ``llvm::map_to_vector`` and ``llvm::filter_to_vector``.
-
-- New :doc:`misc-static-initialization-cycle
-  <clang-tidy/checks/misc/static-initialization-cycle>` check.
-
-  Finds cyclical initialization of static variables.
-
-- New :doc:`modernize-use-std-bit
-  <clang-tidy/checks/modernize/use-std-bit>` check.
-
-  Finds common idioms which can be replaced by standard functions from the
-  ``<bit>`` C++20 header.
-
-- New :doc:`modernize-use-string-view
-  <clang-tidy/checks/modernize/use-string-view>` check.
-
-  Looks for functions returning ``std::[w|u8|u16|u32]string`` and suggests to
-  change it to ``std::[...]string_view`` for performance reasons if possible.
-
-- New :doc:`modernize-use-structured-binding
-  <clang-tidy/checks/modernize/use-structured-binding>` check.
-
-  Finds places where structured bindings could be used to decompose pairs and
-  suggests replacing them.
-
-- New :doc:`performance-string-view-conversions
-  <clang-tidy/checks/performance/string-view-conversions>` check.
-
-  Finds and removes redundant conversions from ``std::[w|u8|u16|u32]string_view`` to
-  ``std::[...]string`` in call expressions expecting ``std::[...]string_view``.
-
-- New :doc:`performance-use-std-move
-  <clang-tidy/checks/performance/use-std-move>` check.
-
-  Suggests insertion of ``std::move(...)`` to turn copy assignment operator
-  calls into move assignment ones, when deemed valid and profitable.
-
-- New :doc:`readability-redundant-lambda-parameter-list
-  <clang-tidy/checks/readability/redundant-lambda-parameter-list>` check.
-
-  Finds lambda expressions with a redundant empty parameter list and removes it.
-
-- New :doc:`readability-redundant-nested-if
-  <clang-tidy/checks/readability/redundant-nested-if>` check.
-
-  Finds nested ``if`` statements that can be merged into a single ``if`` by
-  combining conditions with ``&&``.
-
-- New :doc:`readability-redundant-qualified-alias
-  <clang-tidy/checks/readability/redundant-qualified-alias>` check.
-
-  Finds redundant identity type aliases that re-expose a qualified name and can
-  be replaced with a ``using`` declaration.
-
-- New :doc:`readability-trailing-comma
-  <clang-tidy/checks/readability/trailing-comma>` check.
-
-  Checks for presence or absence of trailing commas in enum definitions and
-  initializer lists.
-
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
-- Renamed :doc:`cert-exp45-c <clang-tidy/checks/cert/exp45-c>`
-  to :doc:`bugprone-assignment-in-selection-statement
-  <clang-tidy/checks/bugprone/assignment-in-selection-statement>`.
-
-- Renamed :doc:`cppcoreguidelines-explicit-constructor
-  <clang-tidy/checks/cppcoreguidelines/explicit-constructor>`
-  to :doc:`misc-explicit-constructor
-  <clang-tidy/checks/misc/explicit-constructor>`. The
-  `cppcoreguidelines-explicit-constructor` name is kept as an alias.
-
-- Renamed :doc:`google-explicit-constructor
-  <clang-tidy/checks/google/explicit-constructor>`
-  to :doc:`misc-explicit-constructor
-  <clang-tidy/checks/misc/explicit-constructor>`. The
-  `google-explicit-constructor` name is kept as an alias.
-
-- Renamed :doc:`performance-faster-string-find
-  <clang-tidy/checks/performance/faster-string-find>` to
-  :doc:`performance-prefer-single-char-overloads
-  <clang-tidy/checks/performance/prefer-single-char-overloads>`.
-  The `performance-faster-string-find` name is kept as an alias.
-
 Changes in existing checks
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-- Improved :doc:`altera-id-dependent-backward-branch
-  <clang-tidy/checks/altera/id-dependent-backward-branch>` check by fixing false
-  positives when ordinary variable or field assignments are used in loop
-  conditions and note locations for inferred ID-dependent fields.
-
-- Improved :doc:`altera-unroll-loops
-  <clang-tidy/checks/altera/unroll-loops>` check by fixing a crash when
-  analyzing a ``for`` loop whose initialization statement declares multiple
-  variables.
-
-- Improved :doc:`bugprone-argument-comment
-  <clang-tidy/checks/bugprone/argument-comment>`:
-
-  - Checks for C++11 inherited constructors.
-
-  - Adds `CommentAnonymousInitLists`, `CommentTypedInitLists`, and
-    `CommentParenthesizedTemporaries` options to comment braced-init list
-    arguments and explicit temporary constructions (for example, ``{}``,
-    ``Type{}``, and ``Type()``).
-
-- Improved :doc:`bugprone-bad-signal-to-kill-thread
-  <clang-tidy/checks/bugprone/bad-signal-to-kill-thread>` check by fixing false
-  negatives when the ``SIGTERM`` macro is obtained from a precompiled header.
-
-- Improved :doc:`bugprone-branch-clone
-  <clang-tidy/checks/bugprone/branch-clone>` check by fixing a false positive
-  where ``if``/``else`` branches containing inline assembly that differed only
-  in the asm string or clobber list were reported as identical.
-
-- Improved :doc:`bugprone-casting-through-void
-  <clang-tidy/checks/bugprone/casting-through-void>` check by running only on
-  C++ files because suggested ``reinterpret_cast`` is not available in pure C.
-
-- Improved :doc:`bugprone-derived-method-shadowing-base-method
-  <clang-tidy/checks/bugprone/derived-method-shadowing-base-method>` check by
-  correctly ignoring function templates.
-
-- Improved :doc:`bugprone-exception-escape
-  <clang-tidy/checks/bugprone/exception-escape>` check by adding
-  `TreatFunctionsWithoutSpecificationAsThrowing` option to support reporting
-  for unannotated functions, enabling reporting when no explicit ``throw``
-  is seen and allowing separate tuning for known and unknown implementations.
-
-- Improved :doc:`bugprone-fold-init-type
-  <clang-tidy/checks/bugprone/fold-init-type>` check by detecting precision
-  loss in overloads with transparent standard functors (e.g. ``std::plus<>``)
-  for ``std::accumulate``, ``std::reduce``, and ``std::inner_product``.
-
-- Improved :doc:`bugprone-inc-dec-in-conditions
-  <clang-tidy/checks/bugprone/inc-dec-in-conditions>` check by fixing a false
-  positive when increment/decrement operators appear inside lambda bodies that
-  are part of a condition expression.
-
-- Improved :doc:`bugprone-incorrect-enable-if
-  <clang-tidy/checks/bugprone/incorrect-enable-if>` check to not
-  insert an extraneous ``typename`` on code like
-  ``typename std::enable_if<...>``, where there's already a ``typename`` and
-  only the ``::type`` at the end is missing.
-
-- Improved :doc:`bugprone-macro-parentheses
-  <clang-tidy/checks/bugprone/macro-parentheses>` check by printing the macro
-  definition in the warning message if the macro is defined on command line.
-
-- Improved :doc:`bugprone-misplaced-widening-cast
-  <clang-tidy/checks/bugprone/misplaced-widening-cast>` check by fixing a false
-  positive on bit field assignments when the `CheckImplicitCasts` option is
-  enabled. The check now uses the actual bit field width instead of the
-  declared type to determine if widening occurs.
-
-- Improved :doc:`bugprone-move-forwarding-reference
-  <clang-tidy/checks/bugprone/move-forwarding-reference>` check by fixing some
-  false positives in the context of moved lambda captures.
-
-- Improved :doc:`bugprone-narrowing-conversions
-  <clang-tidy/checks/bugprone/narrowing-conversions>` check by fixing a false
-  positive when converting a ``bool`` to a signed integer type.
-
-- Improved :doc:`bugprone-pointer-arithmetic-on-polymorphic-object
-  <clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object>` check
-  by fixing a false positive when ``operator[]`` is used in a dependent context.
-
-- Improved :doc:`bugprone-random-generator-seed
-  <clang-tidy/checks/bugprone/random-generator-seed>` check by adding
-  a new note at the location of the field if the generator is implicitly
-  initialized with a default seed value.
-
-- Improved :doc:`bugprone-std-namespace-modification
-  <clang-tidy/checks/bugprone/std-namespace-modification>` check by fixing
-  false positives when extending the standard library with a specialization of
-  user-defined type and by removing detection of the compiler generated ``std``
-  namespace extensions.
-
-- Improved :doc:`bugprone-string-constructor
-  <clang-tidy/checks/bugprone/string-constructor>` check to detect suspicious
-  string constructor calls when the string class constructor has a default
-  allocator argument.
-
-- Improved :doc:`bugprone-throwing-static-initialization
-  <clang-tidy/checks/bugprone/throwing-static-initialization>` check by adding
-  the `AllowedTypes` option. With this option it is possible to exclude
-  static declarations with specific types from the check.
-
-- Improved :doc:`bugprone-unchecked-optional-access
-  <clang-tidy/checks/bugprone/unchecked-optional-access>` to recognize common
-  GoogleTest macros such as ``ASSERT_TRUE`` and ``ASSERT_FALSE``, reducing the
-  number of false positives in test code.
-
-- Improved :doc:`bugprone-unsafe-functions
-  <clang-tidy/checks/bugprone/unsafe-functions>` check by adding the function
-  ``std::get_temporary_buffer`` to the default list of unsafe functions. (This
-  function is unsafe, useless, deprecated in C++17 and removed in C++20).
-
-- Improved :doc:`bugprone-use-after-move
-  <clang-tidy/checks/bugprone/use-after-move>` check:
-
-  - Include the name of the invalidating function in the warning message when a
-    custom invalidation function is used (via the `InvalidationFunctions`
-    option).
-
-  - Add support for annotation of user-defined types as having the same
-    moved-from semantics as standard smart pointers.
-
-  - Do not report explicit call to destructor after move as an invalid use.
-
-  - Avoid false positives when moving object to a base type then accessing
-    non-base members.
-
-  - Avoid false positives when moving object is reinitialized via the base
-    class's ``operator=``.
-
-  - Fix a false positive when a moved-from variable is reinitialized
-    via a ``std::tie()`` assignment (e.g. ``std::tie(a, b) = f(std::move(a),
-    std::move(b))``). The tuple assignment writes back through the stored
-    references, which fully reinitializes the captured variables.
-
-  - Avoid false positives when forwarding a derived object to initialize a base
-    class subobject.
-
-- Improved :doc:`cert-err33-c
-  <clang-tidy/checks/cert/err33-c>` check by not inheriting
-  `CheckedReturnTypes` option from :doc:`bugprone-unused-return-value
-  <clang-tidy/checks/bugprone/unused-return-value>`, which caused false
-  positives on functions returning ``std::error_code`` or similar types.
-
-- Improved :doc:`cppcoreguidelines-avoid-capturing-lambda-coroutines
-  <clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines>`
-  check by adding the `AllowExplicitObjectParameters` option. When enabled,
-  lambda coroutines using C++23 deducing ``this`` (explicit object parameter)
-  are not flagged.
-
-- Improved :doc:`cppcoreguidelines-avoid-non-const-global-variables
-  <clang-tidy/checks/cppcoreguidelines/avoid-non-const-global-variables>`
-  check by adding the `IgnoreMacros` option. When enabled, non-const global
-  variables defined in macros are ignored.
-
-- Improved :doc:`cppcoreguidelines-init-variables
-  <clang-tidy/checks/cppcoreguidelines/init-variables>` check by ensuring that
-  member pointers are correctly flagged as uninitialized.
-
-- Fixed :doc:`cppcoreguidelines-init-variables
-  <clang-tidy/checks/cppcoreguidelines/init-variables>` check by excluding
-  Objective-C for-in loop variable declaration.
-
-- Improved :doc:`cppcoreguidelines-missing-std-forward
-  <clang-tidy/checks/cppcoreguidelines/missing-std-forward>` check by:
-
-  - Correctly handling forwarding in deeply nested lambdas.
-
-  - Fixed false negative when multiple parameters are used in a lambda and
-    only some of them are forwarded.
-
-  - Fixed false positive for constrained template parameters
-
-  - Fixed false positive with ``std::forward`` in brace-init and paren-init
-    lambda captures such as ``[t{std::forward<T>(t)}]``.
-
-- Improved :doc:`cppcoreguidelines-pro-type-member-init
-  <clang-tidy/checks/cppcoreguidelines/pro-type-member-init>` check by fixing
-  a false positive when a base class has a forward declaration before its
-  definition.
-
-- Improved :doc:`cppcoreguidelines-pro-type-vararg
-  <clang-tidy/checks/cppcoreguidelines/pro-type-vararg>` check by no longer
-  warning on builtins with custom type checking (e.g., type-generic builtins
-  like ``__builtin_clzg``) that use variadic declarations as an implementation
-  detail.
-
-- Improved :doc:`cppcoreguidelines-rvalue-reference-param-not-moved
-  <clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved>` check:
-
-  - Fixed a false positive on implicitly generated functions such as
-    inherited constructors.
-
-  - Added `AllowImplicitMove` option to not warn when an rvalue reference
-    parameter is returned without an explicit ``std::move``.
-
-- Improved :doc:`cppcoreguidelines-special-member-functions
-  <clang-tidy/checks/cppcoreguidelines/special-member-functions>` check by
-  fixing a false positive with the `AllowImplicitlyDeletedCopyOrMove` option
-  for classes whose copy operations are implicitly deleted by a non-copyable
-  base class.
-
-- Improved :doc:`cppcoreguidelines-use-enum-class
-  <clang-tidy/checks/cppcoreguidelines/use-enum-class>` check by adding the
-  `IgnoreMacros` option. When enabled, unscoped ``enum`` declarations within
-  macros are ignored.
-
-- Improved :doc:`fuchsia-statically-constructed-objects
-  <clang-tidy/checks/fuchsia/statically-constructed-objects>` check by fixing a
-  crash when checking value-dependent static member initializers in class templates.
-
-- Improved :doc:`llvm-use-ranges
-  <clang-tidy/checks/llvm/use-ranges>` check by adding support for the following
-  algorithms: ``std::accumulate``, ``std::replace_copy``, and
-  ``std::replace_copy_if``.
-
-- Improved :doc:`misc-const-correctness
-  <clang-tidy/checks/misc/const-correctness>` check:
-
-  - Added support for analyzing function parameters with the `AnalyzeParameters`
-    option.
-
-  - Fixed false positive where an array of pointers to ``const`` was
-    incorrectly diagnosed as allowing the pointee to be made ``const``.
-
-  - Fixed false positive where a pointer used with placement new was
-    incorrectly diagnosed as allowing the pointee to be made ``const``.
-
-  - Enabled the check for variables declared with ``auto`` (configurable
-    via the `AnalyzeAutoVariables` option) and lambdas (configurable
-    via `AnalyzeLambdas` option).
-
-  - Fixed false positive where calling a non-const member function on a
-    pointer was incorrectly treated as mutating the pointer, when it only
-    mutates the pointee.
-
-  - Fixed false positives when pointers were later passed or bound through
-    ``const``-qualified pointer references.
-
-  - Fixed false positive where a pointer returned as a non-const ``void *``
-    was incorrectly diagnosed as allowing its pointee to be made ``const``.
-
-- Improved :doc:`misc-multiple-inheritance
-  <clang-tidy/checks/misc/multiple-inheritance>` by avoiding false positives when
-  virtual inheritance causes concrete bases to be counted more than once.
-
-- Improved :doc:`misc-redundant-expression
-  <clang-tidy/checks/misc/redundant-expression>` check:
-
-  - Fixed a crash when evaluating bitwise comparisons against integer constants
-    wider than 64 bits.
-
-  - Avoided false positives when comparing expressions that are structurally
-    identical but use different type aliases.
-
-- Improved :doc:`misc-throw-by-value-catch-by-reference
-  <clang-tidy/checks/misc/throw-by-value-catch-by-reference>` check:
-
-  - Fixed the `WarnOnLargeObject` option to use the correct name when
-    storing the configuration.
-
-  - Fixed the `CheckThrowTemporaries` option to correctly reflect its
-    configured value in exported settings.
-
-- Improved :doc:`misc-unused-parameters
-  <clang-tidy/checks/misc/unused-parameters>` check by adding
-  `IgnoreMacroParameters` option to suppress warnings for unused parameters
-  whose declarations originate from macro expansions.
-
-- Improved :doc:`misc-unused-using-decls
-  <clang-tidy/checks/misc/unused-using-decls>` to not diagnose ``using``
-  declarations as unused if they're exported from a module.
-
-- Improved :doc:`misc-use-internal-linkage
-  <clang-tidy/checks/misc/use-internal-linkage>` to not suggest giving
-  internal linkage to entities defined in C++ module interface units.
-  Because it only sees one file at a time, the check can't be sure
-  such entities aren't referenced in any other files of that module.
-
-- Improved :doc:`modernize-avoid-c-style-cast
-  <clang-tidy/checks/modernize/avoid-c-style-cast>` check by fixing an invalid
-  fix-it generated when replacing casts that directly follow a keyword or
-  identifier.
-
-- Improved :doc:`modernize-deprecated-headers
-  <clang-tidy/checks/modernize/deprecated-headers>` check by avoiding false
-  positives on project headers that use the same name as a standard library
-  header.
-
-- Improve :doc:`modernize-loop-convert
-  <clang-tidy/checks/modernize/loop-convert>` checks to insert a space when
-  replacing ``*it`` with the loop variable in expressions like ``delete*it``,
-  where the missing space would cause the keyword and the new variable to
-  merge into a single identifier.
-
-- Improved :doc:`modernize-macro-to-enum
-  <clang-tidy/checks/modernize/macro-to-enum>` check by preserving source file
-  line endings in fix-it replacements.
-
-- Improved :doc:`modernize-pass-by-value
-  <clang-tidy/checks/modernize/pass-by-value>` check by adding `IgnoreMacros`
-  option to suppress warnings in macros.
-
-- Improved :doc:`modernize-redundant-void-arg
-  <clang-tidy/checks/modernize/redundant-void-arg>` check to work in C23.
-
-- Improved :doc:`modernize-return-braced-init-list
-  <clang-tidy/checks/modernize/return-braced-init-list>` check to apply fix-it
-  when type qualifiers and/or reference modifiers are used with parameters.
-
-- Improved :doc:`modernize-type-traits
-  <clang-tidy/checks/modernize/type-traits>` check to suggest usage of
-  ``std::remove_cvref_t`` when applicable.
-
-- Improved :doc:`modernize-use-default-member-init
-  <clang-tidy/checks/modernize/use-default-member-init>` check by fixing a
-  false positive when a constructor initializer refers to a declaration that
-  would not be visible from the inserted default member initializer. The
-  `IgnoreNonVisibleReferences` option can be set to `false` to warn without
-  emitting fix-its for these cases.
-
-- Improved :doc:`modernize-use-equals-delete
-  <clang-tidy/checks/modernize/use-equals-delete>` check by only warning on
-  private deleted functions, if they do not have a public overload or are a
-  special member function.
-
-- Improved :doc:`modernize-use-nodiscard
-  <clang-tidy/checks/modernize/use-nodiscard>` check by avoiding false
-  positives on functions returning specializations of class templates marked
-  ``[[nodiscard]]``.
-
-- Improved :doc:`modernize-use-override
-  <clang-tidy/checks/modernize/use-override>` check by adding the
-  `AllowVirtualAndOverride` option to allow keeping ``virtual`` on methods
-  that are also marked ``override``.
-
-- Improved :doc:`modernize-use-ranges
-  <clang-tidy/checks/modernize/use-ranges>` check:
-
-  - Preserved used iterator results when replacing ``std::unique`` calls with
-    ``std::ranges::unique``.
-
-  - Preserved used iterator results when replacing ``std::remove``,
-    ``std::remove_if``, ``std::partition``, ``std::stable_partition``, and
-    ``std::rotate`` calls with their ``std::ranges`` counterparts.
-
-- Improved :doc:`modernize-use-scoped-lock
-  <clang-tidy/checks/modernize/use-scoped-lock>` check by fixing a false
-  negative where multiple ``std::scoped_lock`` declarations, or a mix of
-  ``std::scoped_lock`` and ``std::lock_guard`` declarations, in the same scope
-  were not flagged.
-
-- Improved :doc:`modernize-use-std-format
-  <clang-tidy/checks/modernize/use-std-format>` check:
-
-  - Fixed a crash when an argument is part of a macro expansion.
-
-  - Added missing ``#include`` insertion when the format function call
-    appears as an argument to a macro.
-
-- Improved :doc:`modernize-use-std-print
-  <clang-tidy/checks/modernize/use-std-print>` check by adding missing
-  ``#include`` insertion when the format function call appears as an
-  argument to a macro.
-
-- Improved :doc:`modernize-use-trailing-return-type
-  <clang-tidy/checks/modernize/use-trailing-return-type>` check by fixing
-  spurious ``missing '(' after '__has_feature'`` errors caused by builtin
-  macros appearing in the return type of a function.
-
-- Improved :doc:`modernize-use-using
-  <clang-tidy/checks/modernize/use-using>` check:
-
-  - Avoid generating invalid code for function types with redundant
-    parentheses.
-
-  - Preserve inline comment blocks that appear between the ``typedef``'s parts.
-
-  - The `IgnoreExternC` option is now set to `true` by default.
-
-- Improved :doc:`performance-enum-size
-  <clang-tidy/checks/performance/enum-size>` check:
-
-  - Exclude ``enum`` in ``extern "C"`` blocks.
-
-  - Improved the ignore list to correctly handle ``typedef`` and  ``enum``.
-
-- Improved :doc:`performance-inefficient-string-concatenation
-  <clang-tidy/checks/performance/inefficient-string-concatenation>` check by
-  adding support for detecting inefficient string concatenation in ``do-while``
-  loops.
-
-- Improved :doc:`performance-inefficient-vector-operation
-  <clang-tidy/checks/performance/inefficient-vector-operation>` check by
-  correctly handling vector-like classes when ``push_back``/``emplace_back`` are
-  inherited.
-
-- Improved :doc:`performance-move-const-arg
-  <clang-tidy/checks/performance/move-const-arg>` check by avoiding false
-  positives on trivially copyable types with a non-public copy constructor.
-
-- Improved :doc:`performance-prefer-single-char-overloads
-  <clang-tidy/checks/performance/prefer-single-char-overloads>` check:
-
-  - Now analyzes calls to the ``starts_with``, ``ends_with``, ``contains``,
-    and ``operator+=`` string member functions.
-
-  - Fixes false negatives when using ``std::set`` from ``libstdc++``.
-
-- Improved :doc:`performance-trivially-destructible
-  <clang-tidy/checks/performance/trivially-destructible>` check by fixing
-  false positives when a class is seen through both a header include and
-  a C++20 module import.
-
-- Improved :doc:`readability-braces-around-statements
-  <clang-tidy/checks/readability/braces-around-statements>` check by fixing a
-  crash when diagnosing a statement that ends in the middle of a macro body
-  expansion.
-
-- Improved :doc:`readability-container-size-empty
-  <clang-tidy/checks/readability/container-size-empty>` check:
-
-  - Fix a crash when a member expression has a non-identifier name.
-
-  - Reduce verbosity by removing the note indicating source location of the
-    ``empty`` function.
-
-  - Fixed a false positive with suggesting ``empty`` when comparing a container
-    to a default-constructed object of an unrelated type.
-
-  - Extended to warn when the non-member ``std::size()`` function is used
-    in a Boolean context or compared to ``0`` or ``1``, consistent with the
-    existing ``size()``/``length()`` member call detection.
-
-- Improved :doc:`readability-convert-member-functions-to-static
-  <clang-tidy/checks/readability/convert-member-functions-to-static>` check:
-
-  - Fixing a false positive where ``const`` member functions were incorrectly
-    flagged when they are part of a const/non-const overload pair.
-
-  - Correctly detecting ``this`` usage when a generic lambda calls an overloaded
-    member function.
-
-- Improved :doc:`readability-delete-null-pointer
-  <clang-tidy/checks/readability/delete-null-pointer>` check by avoiding invalid
-  fix-its for ``if`` statements with condition variables or initializers.
-
-- Improved :doc:`readability-else-after-return
-  <clang-tidy/checks/readability/else-after-return>` check:
-
-  - Fixed missed diagnostics when ``if`` statements appear in unbraced
-    ``switch`` case labels.
-
-  - Fixed a false positive involving ``if`` statements which contain
-    a ``return``, ``break``, etc., jumped over by a ``goto``.
-
-  - Fixed the check potentially breaking code by deleting one too many
-    characters following an ``else`` or a curly brace.
-
-  - Added support for handling attributed ``if`` then-branches such as
-    ``[[likely]]`` and ``[[unlikely]]``.
-
-  - Diagnose and remove redundant ``else`` branches after calls to
-    ``[[noreturn]]`` functions.
-
-- Improved :doc:`readability-enum-initial-value
-  <clang-tidy/checks/readability/enum-initial-value>` check: the warning message
-  now uses separate note diagnostics for each uninitialized enumerator, making
-  it easier to see which specific enumerators need explicit initialization.
-
-- Improved :doc:`readability-function-size
-  <clang-tidy/checks/readability/function-size>` check by adding an
-  `IgnoreMacros` option to exclude statements, branches, nesting levels, and
-  variable declarations inside macros from the reported metrics.
-
-- Improved :doc:`readability-identifier-length
-  <clang-tidy/checks/readability/identifier-length>` check:
-
-  - A new option, named `LineCountThreshold`, is added to silence warnings for
-    short-lived variables, based on distance between declaration and last use.
-
-  - Support for structured bindings is added. Two new options, named
-    `MinimumBindingNameLength` and `IgnoredBindingNames` respectively, are
-    added to configure the behavior of the check regarding this new identifier
-    kind. By default, names with at least 2 characters are required and the
-    only exception allowed is `_`.
-
-- Improved :doc:`readability-identifier-naming
-  <clang-tidy/checks/readability/identifier-naming>` check:
-
-  - Fixed incorrect naming style application to C++17 structured bindings.
-
-  - Fixed a false positive where function templates could be diagnosed as generic
-    identifiers when `DefaultCase` was enabled.
-
-  - Fixed a crash in dependent base lookup when
-    `AggressiveDependentMemberLookup` option is enabled.
-
-  - Fixed a false positive from naming style lookup for declarations inside macro
-    arguments.
-
-- Improved :doc:`readability-implicit-bool-conversion
-  <clang-tidy/checks/readability/implicit-bool-conversion>` check:
-
-  - Fixed a false positive where `AllowPointerConditions` and
-    `AllowIntegerConditions` options did not suppress warnings when the
-    condition expression involved temporaries (e.g. passing a string literal to
-    a ``const std::string&`` parameter).
-
-  - Warn and provide fix-its when a macro defined in a system header (e.g.
-    ``NULL``) is implicitly converted to ``bool``.
-
-  - Added `AllowLogicalOperatorConversion` option to suppress warnings on
-    implicit conversions of logical operator results (``&&``, ``||``, ``!``)
-    to ``bool`` in C.
-
-  - Fixed a false positive where ``bool`` conditions in C conditional
-    operators were diagnosed as implicit conversions to ``int``.
-
-- Improved :doc:`readability-inconsistent-ifelse-braces
-  <clang-tidy/checks/readability/inconsistent-ifelse-braces>` check to
-  correctly handle labeled statements of ``if``/``else`` bodies.
-
-- Improved :doc:`readability-non-const-parameter
-  <clang-tidy/checks/readability/non-const-parameter>` check:
-
-  - Avoid false positives on parameters used in dependent expressions
-    (e.g. inside generic lambdas), including constructor-style dependent initializers.
-
-  - Fixed a false positive in array subscript expressions where the types are
-    not yet resolved.
-
-  - Fixed a false positive when adding ``const`` to a pointer parameter would
-    conflict with an existing overload.
-
-  - Fixed a crash when analyzing a redeclaration whose initializer is attached
-    to another declaration.
-
-- Improved :doc:`readability-redundant-casting
-  <clang-tidy/checks/readability/redundant-casting>` check by adding the
-  `IgnoreImplicitCasts` option (default `false`) to flag casts as redundant
-  when at least one operand of a binary operation matches the cast type due to
-  implicit conversion. For example, ``static_cast<float>(1.0f + 1)`` is now
-  identified as redundant since ``1`` is implicitly converted to ``float``.
-  Setting this option to `true` restores the previous behavior.
-
-- Improved :doc:`readability-redundant-member-init
-  <clang-tidy/checks/readability/redundant-member-init>` check by adding an
-  `IgnoreMacros` option to suppress warnings when the initializer involves
-  macros that may expand differently in other configurations.
-
-- Improved :doc:`readability-redundant-parentheses
-  <clang-tidy/checks/readability/redundant-parentheses>` check:
-
-  - Fixed a false positive for parentheses present around an overloaded operator
-    in the context of a binary operation.
-
-  - Fixed a bug where clients that apply fix-its without :program:`clang-tidy`'s
-    cleanup could produce invalid code by joining adjacent tokens.
-
-  - Added support for parentheses around subscript operators (``(E1[E2])`` -> ``E1[E2]``).
-
-- Improved :doc:`readability-redundant-preprocessor
-  <clang-tidy/checks/readability/redundant-preprocessor>` check by fixing a
-  false positive for nested ``#if`` directives using different builtin
-  expressions such as ``__has_builtin`` and ``__has_cpp_attribute``.
-
-- Improved :doc:`readability-simplify-boolean-expr
-  <clang-tidy/checks/readability/simplify-boolean-expr>` check to provide valid
-  fix suggestions for C23 and later by not using ``static_cast``.
-
-- Improved :doc:`readability-simplify-subscript-expr
-  <clang-tidy/checks/readability/simplify-subscript-expr>` check by fixing
-  missing warnings when subscripting an object held inside a generic
-  container (e.g. subscripting a ``std::string`` held inside a
-  ``std::vector<std::string>``).
-
-- Improved :doc:`readability-suspicious-call-argument
-  <clang-tidy/checks/readability/suspicious-call-argument>` check by avoiding a
-  crash from invalid ``Abbreviations`` option.
-
-- Improved :doc:`readability-trivial-switch
-  <clang-tidy/checks/readability/trivial-switch>` check by adding an
-  `IgnoreMacros` option. When enabled, the check will ignore switch
-  statements originating from macros.
-
-- Improved :doc:`readability-use-anyofallof
-  <clang-tidy/checks/readability/use-anyofallof>` check by emitting a diagnostic
-  note to suggest materializing the temporary range when iterating over temporary
-  range expressions or initializer lists, as reusing them directly could be unsafe.
-
 Removed checks
 ^^^^^^^^^^^^^^
 
@@ -959,8 +115,6 @@ Improvements to include-fixer
 Improvements to clang-include-fixer
 -----------------------------------
 
-- Fixed crashes when command-line argument parsing failed at unknown tool options.
-
 Improvements to modularize
 --------------------------
 
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 9063e54b3e692..465f1eec8c05f 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -46,1103 +46,110 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
 
 ### C/C++ Language Potentially Breaking Changes
 
-- Clang now makes it ill-formed to try to `break` out of or `continue` a loop inside its own condition,
-  increment, or init-statement in all C and C++ language modes. This means that code such as
-
-  ```c++
-  while (({ break; })) {}
-  ```
-
-  is now ill-formed. An outer loop can still be broken out of or continued so long as the inner loop is
-  in the body of the outer loop:
-
-  ```c++
-  // Ok, this breaks out of the 'for' loop.
-  for (;;) {
-    while (({ break; true; })) {}
-  }
-
-  // Error: can't break out of the 'for' loop from within its own increment.
-  for (;;({ while (({ break; true; })) {} })) {}
-  ```
-
-  This also resolves a divergence from GCC: in a construct such as
-
-  ```c++
-  for (;;) {
-    while (({ break; true; })) {}
-  }
-  ```
-
-  Clang would previously `break` out of the `while` loop, whereas GCC (since version 9) would
-  `break` out of the `for` loop here. Now, Clang and GCC both break out of the `for` loop.
-
-- Clang now parses line and digit directives, module names, and original filenames as unevaluated
-  strings. This means that code containing strings with escape sequences such as
-
-  ```c++
-  # 1 "original\x12source.c"
-  #pragma clang module import "\x41"
-  # 50 "a\012.c"
-  ```
-
-  are now ill-formed.
-
-- `__has_feature(modules)` is no longer true when just `-std=c++20` (or higher)
-  is passed. It's only true if `-fmodules` is passed, which enables Clang's
-  module map semantics. Objective-C++ of the form
-
-  ```objc
-  #if __has_feature(modules)
-  @import Foundation;
-  #else
-  #import <Foundation/Foundation.h>
-  #endif
-  ```
-
-  previously took the `@import` branch under `-std=c++20` even though no module
-  maps were in use, which would always fail.
-
-  `__cpp_modules` continues to be the standard macro to use to check if C++20
-  modules are available.
-
 ### C++ Specific Potentially Breaking Changes
 
-- Clang now more aggressively optimizes away stores to objects after they are
-  dead. This behavior can be disabled with `-fno-lifetime-dse`.
-- Clang now correctly rejects `export` declarations in module implementation
-  partitions. (#GH107602)
-- Template argument deduction now treats the `N` in `_BitInt(N)`
-  as being of type `std::size_t` instead of `int`,
-  matching the deduction of array sizes from `int(&)[N]`.
-  This is a breaking change for code that depended on the previously deduced type. (#GH195033)
-- Clang now rejects C++ declarations that combine the `auto` type specifier
-  with another type specifier, such as `auto int`.
-- Clang now rejects nested local classes defined in a different
-  block scope than their parent class. (#GH193472)
-
 ### ABI Changes in This Version
 
-- Fixed incorrect struct layout for `_BitInt` bitfields wider than 255 bits
-  on MSVC targets. Internal bitfield tracking fields were changed from
-  `unsigned char` to `uint64_t` to prevent overflow. This might be an ABI
-  break for such structs compared to earlier Clang versions.
-- Fixed a number of issues with the `__regcall` calling convention for passing
-  structs on non-Windows x86-64 targets, including a crash when handling empty
-  struct arguments. This changes how structs that contain arrays, floating point
-  types, or `_Complex float` types are passed, and may introduce
-  incompatibilities with code compiled by earlier versions of Clang that uses
-  the `__regcall` calling convention on these targets. (#GH62999) (#GH98635)
-- Fixed Itanium mangling for lambdas in instantiated non-static data member
-  initializers by preserving the field-name closure-prefix. This changes the
-  mangled names for affected lambdas. (#GH190555)
-- Clang now uses MSVC-compatible manglings for supported AArch64 SVE builtin
-  types when targeting the Microsoft ABI. This changes symbol names for
-  affected declarations compared to previous Clang releases. (#GH196170)
-- The resume and destroy functions of C++20 coroutines now use the platform C
-  calling convention instead of LLVM's internal `fastcc`. This makes the
-  coroutine ABI stable across LLVM versions and interoperable with other
-  compilers. On most targets this is not a breaking change because `fastcc`
-  and the platform C calling convention agree for `void(ptr)`. It is an ABI
-  break on i686, MIPS O32, PowerPC64 ELFv1, and Lanai.
-- `va_arg` on clang aarch64 msvc now reads types with 16-byte size and alignment
-  (e.g. `__int128`) with their actual alignment (instead of an alignment of 8
-  which was used before). Such c-variadic arguments are already passed as
-  aligned, so previously reading the argument could read padding. Clang now
-  matches how MSVC reads such c-variadic arguments.
-- Fixed incorrect struct return when single large vector (256/512-bit) used on
-  x86-64 targets. (#GH203760) The bug was introduced since Clang 21. (#GH120670)
-- Clang now applies MSVC's MD5 shortening to over-long Microsoft C++ RTTI type
-  descriptor name strings, matching the behavior already used for the RTTI
-  symbol names. Previously the full name string was always emitted, so deeply
-  nested template types (for example, ones containing local lambdas) could
-  produce very large writable `.data` sections. Emitted RTTI name strings
-  change only for types whose name exceeds the length limit. (#GH206313)
-- Fixed Itanium mangling for lambdas in default member initializers of local
-  classes to use `<local-name>` encoding, preventing mangling collisions between
-  distinct local classes.
-
 ### AST Dumping Potentially Breaking Changes
 
-- The JSON AST dump now includes all fields from `AvailabilityAttr`: `platform`,
-  `introduced`, `deprecated`, `obsoleted`, `unavailable`, `message`,
-  `strict`, `replacement`, `priority`, and `environment`. Previously, these
-  fields were missing from the JSON output.
-- Colons that appear at the end of a ParamCommentCommand name are not serialized
-  as part of the name.
-- AST pretty-printing now respects `PrintingPolicy::FullyQualifiedName` when
-  printing `DeclRefExpr` names, including expression-form non-type template
-  arguments in printed types. Previously, these references could be printed
-  unqualified. (#GH206041)
-
 ### Clang Frontend Potentially Breaking Changes
 
-- HIPSPV toolchain: `--offload-targets=spirv{32,64}` option is
-  deprecated and will be removed when the new offload driver becomes
-  default. The replacement for the option is
-  `--offload-targets=spirv{32,64}-unknown-chipstar` when using the new
-  offload driver (`--offload-new-driver`).
-- The new driver (`--offload-new-driver`) is now default for all offloading
-  compilations. This changes the ABI for relocatable device code. Currently,
-  libraries will need to be recompiled, or used with
-  (`--no-offload-new-driver`). This option will be removed in the next release.
-- Clang no longer defines the `__cpp_impl_coroutine` feature test macro under the 32-bit x86 Microsoft ABI,
-  as support for coroutines on this target is incomplete.
-  When using coroutines on this target a warning is emmitted to indicate the lack of full support.
-  That warning can be disabled with `-Wno-coroutines-unsupported-target`. (see #GH59382)
-
 ### Clang Python Bindings Potentially Breaking Changes
 
-- Remove `CompletionString.Availability`. No libclang interfaces returned instances of it.
-
-- `CompletionString.availability` now returns instances of `CompletionString.AvailabilityKindCompat`.
-
-  Instances of `AvailabilityKindCompat` have the same `__str__` representation
-  as the previous `CompletionChunk.Kind` and are equality-comparable with
-  the existing `AvailabilityKind` enum. It will be replaced by `AvailabilityKind`
-  in a future release. When this happens, the return type of `CompletionString.availability`
-  will change to `AvailabilityKind`, so it is recommended to use `AvailabilityKind`
-  to compare with the return values of `CompletionString.availability`.
-
-- Remove `availabilityKinds`. In this release, uses of `availabilityKinds`
-  need to be replaced by `CompletionString.AvailabilityKind`.
-
-- `CompletionChunk.kind` now returns instances of `CompletionChunkKind`.
-
-  Instances of `CompletionChunkKind` have the same `__str__` representation
-  as the previous `CompletionChunk.Kind` for compatibility.
-  These representations will be changed in a future release to match other enums.
-
-- Remove `completionChunkKindMap`. In this release, uses of `completionChunkKindMap`
-  need to be replaced by `CompletionChunkKind`.
-
-- Move `SPELLING_CACHE` into `CompletionChunk` and change it to use
-  `CompletionChunkKind` instances as keys, instead of the enum values.
-  An alias is kept in the form of a `SPELLING_CACHE` variable, but it only supports
-  `__getitem__` and `__contains__`. It will be removed in a future release.
-  Please migrate to using `CompletionChunk.SPELLING_CACHE` instead.
-
-- `SourceLocation` and `SourceRange` now use `NotImplemented` to delegate
-  equality checks (`__eq__`) to the other object they are compared with when
-  they are of different classes. They previously returned `False` when compared
-  with objects of other classes.
-
-- `TranslationUnit.get_tokens` now throws an error if both the `extent` and
-  `locations` argument are passed. Previousy, `locations` took precedence.
-
-- `_CXUnsavedFile` will be renamed to `UnsavedFile` for consistency.
-  `UnsavedFile` is already available to use and existing uses should
-  be adapted to refer to it instead. `_CXUnsavedFile` will be removed in a
-  future release.
-
 ### OpenCL Potentially Breaking Changes
 
-- Clang now diagnoses zero-length arrays as errors in OpenCL. OpenCL C 3.0
-  section 6.11.d states that "Variable length arrays and structures with
-  flexible (or unsized) arrays are not supported."
-
-{#what-s-new-in-clang-release}
 ## What's New in Clang {{env.config.release}}?
 
 ### C++ Language Changes
 
-- `__is_trivially_equality_comparable` no longer returns false for all enum types. (#GH132672)
-- `auto` parameters are now available in all C++ language modes as an extension.
-
 #### C++2d Feature Support
 
-- Added compiler flags `-std=c++2d` and `-std=gnu++2d` for experimental C++2d implementation work.
-
-- Clang now supports [P3733R1](https://wg21.link/p3733r1>) More named universal character escapes.
-  The change is applied as a DR to all C++ language modes. (#GH203944)
-
 #### C++2c Feature Support
 
-- Clang now propagates `constinit` and `constexpr` in structured bindings with tuple-like initializers.
-
-- Clang now has partial support for [P1306R5](https://wg21.link/P1306R5) Expansion Statements. Iterating expansion
-  statements currently cannot be expanded and will result in a diagnostic, but other types of expansion statements work.
-
 #### C++23 Feature Support
 
-- Partially implement Itanium mangling for pack indexing. Partially substituted packs are not yet supported. (#GH112003)
-
 #### C++20 Feature Support
 
-- Clang now supports [P1857R3](https://wg21.link/p1857r3) Modules Dependency Discovery. (#GH54047)
-
 #### C++17 Feature Support
 
 #### Resolutions to C++ Defect Reports
 
-- Clang now implements [CWG1504](https://cplusplus.github.io/CWG/issues/1504.html)
-  which makes pointer arithmetic after derived-base conversion undefined behavior,
-  allowing devirtualization of calls through non-zero array subscripts.
-- Implemented [CWG1780 Explicit instantiation/specialization of generic lambda
-  operator()](https://cplusplus.github.io/CWG/issues/1780.html)
-- Clang now allows omitting `typename` before a template name in a
-  conversion operator, implementing [CWG2413](https://wg21.link/cwg2413).
-- Member specializations can now be declared in class scope, according to the
-  resolution of [CWG727](https://wg21.link/cwg727). This is still not sufficient
-  to resolve that core issue.
-- Clang now uses non-reference types for structured bindings whose initializer
-  returns a prvalue. This resolves [CWG3135](https://wg21.link/cwg3135).
-
 ### C Language Changes
 
 #### C2y Feature Support
 
-- Implemented the type-specific C2y `<stdbit.h>` rotate functions with constexpr
-  evaluation support:
-  `stdc_rotate_left_{uc,us,ui,ul,ull}` and
-  `stdc_rotate_right_{uc,us,ui,ul,ull}`.
-- Implemented C2y `<stdbit.h>` memory reversal functions:
-  `__builtin_stdc_memreverse8` / `stdc_memreverse8` (in-place byte
-  reversal of a byte array) and `stdc_memreverse8u{8,16,32,64}` (byte-swap
-  of an exact-width unsigned integer value, usable in constant expressions).
-
 #### C23 Feature Support
 
-- Clang now allows C23 `constexpr` struct member access through the dot operator in constant expressions. (#GH178349)
-- Fixed a failing assertion when validating an invalid structure redefinition
-  with a member which uses an incomplete enumeration type. (#GH190227)
-- Clang now supports the C23 `wN` and `wfN` length modifiers. (#GH116962)
-- Clang now recognizes the C23 `H`, `D`, and `DD` length modifiers in
-  format strings and diagnoses their use because Clang does not yet support
-  the corresponding decimal floating-point types, `_Decimal32`, `_Decimal64`, and `_Decimal128`. (#GH116962)
-- Fixed a bug with deducing qualified inferred types with `auto`. `auto` can now
-  be combined with `restrict` or `_Atomic` to form a properly-qualified type. (#GH207466)
-- Fixed a bug where an enumerator following a `_BitInt`-typed enumerator kept
-  the bit-precise type and wrapped around on overflow instead of taking a
-  suitably sized standard integer type. (#GH208163)
-
-
 ### Objective-C Language Changes
 
-- Clang now emits Objective-C number, array, and dictionary literals as
-  compile-time constant data structures rather than runtime `objc_msgSend`
-  calls on targets whose runtime supports constant literal classes. The
-  feature can be disabled altogether with `-fno-objc-constant-literals`,
-  or selectively per literal kind with `-fno-constant-nsnumber-literals`,
-  `-fno-constant-nsarray-literals`, and
-  `-fno-constant-nsdictionary-literals`.
-
 ### Non-comprehensive list of changes in this release
 
-- Clang can now devirtualize virtual function calls on objects accessed through
-  array subscript expressions with non-zero constant indices, based on the
-  resolution of [CWG1504](https://cplusplus.github.io/CWG/issues/1504.html).
-- Added support for floating point and pointer values in most `__atomic_`
-  builtins.
-- Added `__builtin_stdc_rotate_left` and `__builtin_stdc_rotate_right`
-  for bit rotation of unsigned integers including `_BitInt` types. Rotation
-  counts are normalized modulo the bit-width and support negative values.
-  Usable in constant expressions. Implicit conversion is supported for
-  class/struct types with conversion operators.
-- Implemented the following C23 `<stdbit.h>` builtins with `_BitInt`
-  support and constexpr evaluation:
-  `__builtin_stdc_leading_zeros`, `__builtin_stdc_leading_ones`,
-  `__builtin_stdc_trailing_zeros`, `__builtin_stdc_trailing_ones`,
-  `__builtin_stdc_first_leading_zero`, `__builtin_stdc_first_leading_one`,
-  `__builtin_stdc_first_trailing_zero`, `__builtin_stdc_first_trailing_one`,
-  `__builtin_stdc_count_zeros`, `__builtin_stdc_count_ones`,
-  `__builtin_stdc_has_single_bit`, `__builtin_stdc_bit_width`,
-  `__builtin_stdc_bit_floor`, and `__builtin_stdc_bit_ceil`.
-- Implemented the type-specific C23 `<stdbit.h>` functions with constexpr
-  evaluation support:
-  `stdc_leading_zeros_{uc,us,ui,ul,ull}`,
-  `stdc_leading_ones_{uc,us,ui,ul,ull}`,
-  `stdc_trailing_zeros_{uc,us,ui,ul,ull}`,
-  `stdc_trailing_ones_{uc,us,ui,ul,ull}`,
-  `stdc_first_leading_zero_{uc,us,ui,ul,ull}`,
-  `stdc_first_leading_one_{uc,us,ui,ul,ull}`,
-  `stdc_first_trailing_zero_{uc,us,ui,ul,ull}`,
-  `stdc_first_trailing_one_{uc,us,ui,ul,ull}`,
-  `stdc_count_zeros_{uc,us,ui,ul,ull}`,
-  `stdc_count_ones_{uc,us,ui,ul,ull}`,
-  `stdc_has_single_bit_{uc,us,ui,ul,ull}`,
-  `stdc_bit_width_{uc,us,ui,ul,ull}`,
-  `stdc_bit_floor_{uc,us,ui,ul,ull}`, and
-  `stdc_bit_ceil_{uc,us,ui,ul,ull}`.
-- A new generic bit-reverse builtin function `__builtin_bitreverseg` that
-  extends bit-reversal support to all standard integers type, including
-  `_BitInt`
-- Added `__builtin_elementwise_clmul` for carry-less multiplication of
-  integers including `_BitInt` types. This includes constexpr evaluation
-  support.
-- Added `__builtin_elementwise_pext` and `__builtin_elementwise_pdep` for
-  parallel bit extract and parallel bit deposit operations on integers including
-  `_BitInt` types. This includes constexpr evaluation support.
-- Deprecated float types support from `__builtin_elementwise_max` and
-  `__builtin_elementwise_min`.
-- Added header `endian.h` which contains byte order helpers specified in POSIX
-- Added #pragma loop licm(disable) for llvm.loop.licm.disable metadata
-- Added a new `ExplicitInstantiationDecl` AST node to represent explicit
-  template instantiations (e.g., `template void foo<int>();` or
-  `extern template class S<int>;`). Previously, source location information
-  for explicit instantiation statements was discarded after parsing. The new
-  node preserves the full source range including the `extern` and `template`
-  keywords, qualifiers, template arguments as written, and the declared type,
-  enabling tools such as language servers and refactoring engines to accurately
-  map source locations back to explicit instantiation sites.
-- `typeid` on references and pointers of `final` types no longer emits a
-  vtable lookup at runtime.
-- Updated support for Unicode from 15.1 to 18.0.
-- Linux and Windows toolchains now support Clang multilibs using
-  `-fmultilib-flag=`.
-- The SafeStack builtins `__builtin___get_unsafe_stack_ptr`,
-  `__builtin___get_unsafe_stack_bottom`, `__builtin___get_unsafe_stack_top`,
-  and `__builtin___get_unsafe_stack_start` are now deprecated. Use the
-  corresponding functions from `<sanitizer/safestack_interface.h>` instead.
-
 ### New Compiler Flags
 
-- New option `-fms-anonymous-structs` / `-fno-ms-anonymous-structs` added
-  to enable or disable Microsoft's anonymous struct/union extension without
-  enabling other `-fms-extensions` features (#GH177607).
-- New option `--precompile-reduced-bmi` allows build system to generate a
-  reduced BMI only for a C++20 importable module unit. Previously the users
-  can only generate the reduced BMI as a by-product, e.g, an object files or
-  a full BMI.
-- New `-cc1` option `-fexperimental-overflow-behavior-types` added to
-  enable parsing of the experimental `overflow_behavior` type attribute and
-  type specifiers.
-- New `-cl` option `/d2guardnochecks` added to match MSVC. When Windows
-  Control Flow Guard (CFG) is enabled by other options, it will instruct Clang
-  to emit the CFG metadata, but disable adding checks.
-- New option `-fdiagnostics-show-inlining-chain` added to show inlining chain
-  notes for `[[gnu::warning]]` and `[[gnu::error]]` diagnostics. When a
-  function with these attributes is called from an inlined context, Clang can
-  now show which functions were inlined to reach the call. When debug info is
-  available (`-gline-directives-only` (implicitly enabled at `-g1`) or
-  higher), accurate source locations are used; otherwise, a heuristic fallback
-  is used with a note suggesting how to enable debug info for better accuracy.
-- New option `-fwin-cfg-mechanism=` added to control the mechanism used by
-  Control Flow Guard on Windows. Accepted values are `automatic` (default),
-  `dispatch`, and `check`. The `dispatch` mechanism uses the dispatch
-  function to perform indirect call checks and can improve performance, while
-  `check` uses the traditional check mechanism.
-- New `-cl` option `/d2guardcfgdispatch` added to match MSVC. This acts as a
-  shorthand for `-fwin-cfg-mechanism=dispatch`.
-- New `-cl` option `/d2guardcfgdispatch-` added to match MSVC. This acts as a
-  shorthand for `-fwin-cfg-mechanism=check`.
-- New option `-f[no-]strict-bool` added to control whether Clang can assume
-  that `bool` values loaded from memory cannot have a bit pattern other
-  than 0 or 1.
-- New option `-fcrash-diagnostics-tar` added to create an archive of crash
-  reproducer files for easier bug filing.
-- There are a new pair of flags for riscv32 called `-mzilsd-word-align` and
-  `-mzilsd-strict-align` which control whether Zilsd accesses are allowed to
-  be aligned to 4-byte alignment rather than fully unaligned or fully (8-byte)
-  aligned.
-- New `-cl` option `/pathmap:` added to match MSVC. This option acts as a
-  clang's `-ffile-prefix-map=value` and has known differences in behaviour
-  with the CL's option that do not affect the functionality: nomalizes the
-  macro prefix map pathes -- removes `./` and uses the target's platform-
-  specific path separator character when expanding the preprocessor macros --
-  `-ffile-reproducible` (but not the debug and coverage prefix maps);
-  does not require `/experimental:deterministic` as by MSVC. It needed for
-  removing a hostname from a mangling hash gen, but clang-cl does not use
-  a hostname when generates the hashes. Known issues -- does not remap the
-  source file pathes within PCH/PCM files.
-- New `-cl` option `/experimental:deterministic` added to match CL's option.
-  This enables warning emission on usage of non-deterministic macros `__DATE__`,
-  `__TIME__` and `__TIMESTAMP__` and provides reproducable COFF's timestamp for
-  the output object files.
-- New `-cl` option `/d1nodatetime` added to match CL's option. This option
-  undefines the standard macros `__DATE__`, `__TIME__` and `__TIMESTAMP__` to allow
-  reproducable builds. These macros can be redefined from the command line if
-  necessary. `/d1nodatetime-` can be used to turn this feature off if
-  necessary to override the common build settings.
-
 ### Deprecated Compiler Flags
 
 ### Modified Compiler Flags
 
-- The `-mno-outline` and `-moutline` compiler flags are now allowed on RISC-V and X86, which both support the machine outliner.
-- The `-mno-outline` flag will now add the `nooutline` IR attribute, so that
-  `-mno-outline` and `-moutline` objects can be mixed correctly during LTO.
-- The `-fzero-call-used-regs` compiler flag is now allowed on RISC-V, only the
-  "skip", "used-gpr", "used-gpr-arg", "all-gpr" and "all-gpr-arg" options are
-  supported for the moment.
-- Slightly changed hash id generation to get the unique linkage symbols names
-  by `-unique-internal-linkage-names` option. Now it uses a path that
-  normalized in favor of the target system (same as the preprocessor does
-  for the file macros) and allows the reproducable IDs on any build system.
-- `-fprofile-update=atomic` will now promote counter updates out of loops,
-  similar to the non-atomic case ([#202487](https://github.com/llvm/llvm-project/pull/202487)).
-- The `-cl` `/Brepro` option was modified to match the original CL's option
-  and now defines the standard macros `__DATE__`, `__TIME__` and `__TIMESTAMP__` to
-  "1". The previous functionality remains unchanged.
-- The `-fms-kernel` flag will now implicitly add `-fno-delete-null-pointer-checks`.
-  Still `-fdelete-null-pointer-checks` can be used to override this behavior.
-- Extended the `-marm64x` flag to support compiling to object files. When used
-  in this mode, separate compilation jobs are run for ARM64 and ARM64EC object
-  files, which are then merged into a single file using a new `.obj.arm64ec`
-  section. Consumers must support this extension. Currently, this requires
-  LLD for linking or `llvm-ar`/`llvm-lib` for archiving.
-
 ### Removed Compiler Flags
 
 ### Attribute Changes in Clang
 
-- Added new attribute `stack_protector_ignore` to opt specific local variables out of
-  the analysis which determines if a function should get a stack protector. A function
-  will still generate a stack protector if other local variables or command line flags
-  require it.
-
-- Added a new attribute, `[[clang::no_outline]]` to suppress outlining from
-  annotated functions. This uses the LLVM `nooutline` attribute.
-
-- Introduced a new type attribute `__attribute__((overflow_behavior))` which
-  currently accepts either `wrap` or `trap` as an argument, enabling
-  type-level control over overflow behavior. There is also an accompanying type
-  specifier for each behavior kind via `__ob_wrap` and `__ob_trap`.
-
-- Introduced a new function attribute `__attribute__((__personality__(...)))`
-  to explicitly specify the personality routine for exception handling. THis is
-  meant to be a low level tool for language runtime authors to associate a
-  foreign language personality with a given function. Note that this does not
-  perform any ABI validation for the personality routine.
-
-- {doc}`ThreadSafetyAnalysis` attributes now correctly handle implicit member
-  accesses in C, and parameter attributes in C++. This improves diagnostic
-  precision and fixes false positives.
-
-- The {doc}`ThreadSafetyAnalysis` attributes `guarded_by` and
-  `pt_guarded_by` now accept multiple capability arguments with refined
-  access semantics: *writing* requires all listed capabilities to be held
-  exclusively, while *reading* requires at least one to be held. This is
-  sound because any writer must hold all capabilities, so holding any one
-  prevents concurrent writes.
-
-- {doc}`ThreadSafetyAnalysis` attributes like `acquire_capability`,
-  `release_capability`, `requires_capability`, `locks_excluded`,
-  `try_acquire_capability`, and `assert_capability` can now be applied to
-  function pointer variables and fields. The analysis checks calls through
-  annotated function pointers the same way it checks direct function calls.
-  Only plain function pointers are supported; pointers-to-member functions,
-  blocks, or wrappers (e.g. `std::function`) are not yet supported.
-
-- The `[[clang::unsafe_buffer_usage]]` attribute is now supported in API
-  notes. For example:
-
-  ```yaml
-  Functions:
-    - Name: myUnsafeFunction
-      UnsafeBufferUsage: true
-  ```
-
-- When using `-Wunsafe-buffer-usage` without
-  `-fsafe-buffer-usage-suggestions`, warnings are now emitted only
-  once per source file. Pre-compiled code (such as PCH or module
-  headers) is no longer repeatedly analyzed, as it is analyzed during
-  its initial compilation. (Traditionally included headers are still
-  analyzed within each translation unit that includes them). This
-  behavior matches most of other `-W` diagnostics.
-
-  When `-fsafe-buffer-usage-suggestions` is enabled, the behavior
-  remains the same as before: pre-compiled code is deserialized and
-  analyzed alongside the translation unit that uses it, because fix-it
-  suggestion analysis requires full visibility of the translation
-  unit.
-
-- Added support for `[[msvc::forceinline]]` for functions and
-  `[[msvc::forceinline_calls]]` for statements. Both are aliases to
-  `[[clang::always_inline]]` with additional checks to ensure that they
-  are only accepted in places where MSVC also does.
-
-- The AMDGPU `amdgpu_num_sgpr` and `amdgpu_num_vgpr` attributes are now
-  deprecated. Clang emits a `-Wdeprecated-declarations` warning when they
-  are used. Use `amdgpu_waves_per_eu` instead to control SGPR and VGPR
-  usage.
-
-- Clang now allows GNU attributes between a member declarator and bit-field width. (#GH184954)
-
-- The `[[clang::noescape]]` attribute now disallows deallocating memory
-  through the annotated parameter. This information is currently not exposed to
-  LLVM for optimization purposes, to prevent breaking existing adopters. It may
-  instead be used by warnings and static analyses to provide more information
-  about pointer lifetimes. It may be used to power optimizations in the future,
-  however there are no concrete plans to do so at the moment.
-
-- The attributes `[[clang::opencl_global_device]]` and `[[clang::opencl_global_host]]`
-  are now deprecated. Clang emits a `-Wdeprecated-attributes` warning when
-  they are used.
-
-- The `modular_format` attribute now supports the `fixed` aspect for C
-  ISO 18037 fixed-point `printf` specifiers.
-
-- The `lifetime_capture_by` attribute now accepts three new spelling forms:
-  `lifetime_capture_by_this`, `lifetime_capture_by_global`, and
-  `lifetime_capture_by_unknown`. These replace passing `this`, `global`, and
-  `unknown` as arguments to `lifetime_capture_by`; that argument form is now
-  deprecated because those names can conflict with user-defined parameters.
-  They will be removed in the next release. Distinct `lifetime_capture_by`
-  spellings may also be combined on the same declaration, but each spelling may
-  appear at most once.
-
-- The `const` and `pure` attributes only apply to functions; they are now
-  diagnosed and ignored when applied to anything else.
-
 ### Improvements to Clang's diagnostics
 
-- Fixed bug in `-Wdocumentation` so that it correctly handles explicit
-  function template instantiations (#64087).
-
-- Fixed concept template parameters not being recognized in `-Wdocumentation`
-  when mentioned in tparam comments. (#GH64087)
-
-- `-Wunused-but-set-variable` now diagnoses file-scope variables with
-  internal linkage (`static` storage class) that are assigned but never used.
-  This new coverage is added under the subgroup `-Wunused-but-set-global`,
-  allowing it to be disabled independently with `-Wno-unused-but-set-global`.
-  (#GH148361)
-
-- `-Wunused-template` is now part of `-Wunused` (which is enabled by `-Wall`).
-  It diagnoses unused function and variable templates with internal linkage,
-  which in a header is a latent ODR hazard. It can be disabled with
-  `-Wno-unused-template`. (#GH202945)
-
-- Added `-Wlifetime-safety` to enable lifetime safety analysis,
-  a CFG-based intra-procedural analysis that detects use-after-free and related
-  temporal safety bugs. See the
-  [RFC](https://discourse.llvm.org/t/rfc-intra-procedural-lifetime-analysis-in-clang/86291)
-  for more details. By design, this warning is enabled in `-Weverything`. To disable
-  the analysis, use `-Wno-lifetime-safety` or `-fno-lifetime-safety`.
-
-- Added `-Wlifetime-safety-suggestions` to enable lifetime annotation suggestions.
-  This provides suggestions for function parameters that
-  should be marked `[[clang::lifetimebound]]` based on lifetime analysis. For
-  example, for the following function:
-
-  ```c++
-  int* p(int *in) { return in; }
-  ```
-
-  Clang will suggest:
-
-  ```c++
-  warning: parameter in intra-TU function should be marked [[clang::lifetimebound]]
-  int* p(int *in) { return in; }
-         ^~~~~~~
-                 [[clang::lifetimebound]]
-  note: param returned here
-  int* p(int *in) { return in; }
-                           ^~
-  ```
-
-- Added `-Wlifetime-safety-noescape` to detect misuse of `[[clang::noescape]]`
-  annotation where the parameter escapes through return. For example:
-
-  ```c++
-  int* p(int *in [[clang::noescape]]) { return in; }
-  ```
-
-  Clang will warn:
-
-  ```c++
-  warning: parameter is marked [[clang::noescape]] but escapes
-  int* p(int *in [[clang::noescape]]) { return in; }
-         ^~~~~~~
-  note: returned here
-  int* p(int *in [[clang::noescape]]) { return in; }
-                                               ^~
-  ```
-
-- Added `-Wlifetime-safety-dangling-field` to detect dangling field references
-  when stack memory escapes to class fields. This is part of `-Wlifetime-safety`
-  and detects cases where local variables or parameters are stored in fields but
-  outlive their scope. For example:
-
-  ```c++
-  struct DanglingView {
-    std::string_view view;
-    DanglingView(std::string s) : view(s) {}  // warning: address of stack memory escapes to a field
-  };
-  ```
-
-- Improved `-Wassign-enum` performance by caching enum enumerator values. (#GH176454)
-
-- Clang now emits `-Wpsabi` diagnostics for externally visible x86-64
-  function definitions that return or take AVX or AVX-512 vector types without
-  enabling the corresponding target feature.
-
-- Fixed a false negative in `-Warray-bounds` where the warning was suppressed
-  when accessing a member function on a past-the-end array element.
-  (#GH179128)
-
-- Added a missing space to the FixIt for the `implicit-int` group of diagnostics and
-  made sure that only one such diagnostic and FixIt is emitted per declaration group. (#GH179354)
-
-- Fixed the Fix-It insertion point for `expected ';' after alias declaration`
-  when parsing alias declarations involving a token-split `>>` sequence
-  (for example, `using A = X<int>>;`). (#GH184425)
-
-- Fixed incorrect `implicitly deleted` diagnostic for explicitly deleted
-  candidate function. (#GH185693)
-
-- The `-Wloop-analysis` warning has been extended to catch more cases of
-  variable modification inside lambda expressions (#GH132038).
-
-- Clang now emits `-Wsizeof-pointer-memaccess` when snprintf/vsnprintf use the sizeof
-  the destination buffer(dynamically allocated) in the len parameter(#GH162366)
-
-- Added `-Wmodule-map-path-outside-directory` (off by default) to warn on
-  header and umbrella directory paths that use `..` to refer outside the module
-  directory in module maps found via implicit search
-  (`-fimplicit-module-maps`). This does not affect module maps specified
-  explicitly via `-fmodule-map-file=`.
-
-- Honour `[[maybe_unused]]` attribute on private fields.
-  `-Wunused-private-field` no longer emits a warning for annotated private
-  fields.
-
-- Improved `-Wgnu-zero-variadic-macro-arguments` to suggest using
-  `__VA_OPT__` if the current language version supports it(#GH188624)
-
-- Clang now emits an error when implicitly casting a complex type to a built-in vector type. (#GH186805)
-
-- Added `-Wnonportable-include-path-separator` (off by default) to catch
-  #include directives that use backslashes as a path separator. The warning
-  includes a FixIt to change all the backslashes to forward slashes, so that the
-  code can automatically be made portable to other host platforms that don't
-  support backslashes.
-
-- Clang now explains why template deduction fails for explicit template arguments.
-
-- No longer emitting a `-Wpre-c2y-compat` or extension diagnostic about use
-  of octal literals with a `0o` prefix, and no longer emitting a
-  `-Wdeprecated-octal-literals` diagnostic for use of octal literals without
-  a `0o` prefix, when the literal is expanded from a macro defined in a
-  system header. (#GH192389)
-
-- Improved error recovery for missing semicolons after class members. Clang now avoids
-  skipping subsequent valid declarations when their previous decl is missing semicolon.
-
-- Removed the body of lambdas from some diagnostic messages.
-
-- Fixed false positive host-device mismatch errors in discarded `if constexpr` branches for CUDA/HIP;
-  such calls are now correctly skipped.
-
-- Clang now errors when a function declaration aliases a variable or vice versa. (#GH195550)
-
-- Added `-Wattribute-alias` to diagnose type mismatches between an alias and its aliased function. (#GH195550)
-
-- The diagnostics around `__block` now explain why a variable cannot be marked `__block`. (#GH197213)
-
-- Extended `-Wnonportable-include-path` to warn about trailing whitespace and dots in `#include` paths. (#GH190610)
-
-- Clang now emits error when attribute is missing closing `]]` followed by `;;`. (#GH187223)
-
-- Clang now rejects inline asm constraints and clobbers that contain an
-  embedded null character, instead of silently truncating them. (#GH173900)
-
-- Diagnostics for the C++11 range-based for statement now report the correct
-  iterator type in notes for invalid iterator types.
-
-- `-Wfortify-source` now warns when the constant-evaluated argument to
-  `umask` has bits set outside `0777`. Those bits are silently discarded
-  by the kernel, so setting them is almost always a typo (matching the
-  bionic libc `diagnose_if` check).
-
-- Improved how Unicode characters are displayed in diagnostic messages.
-
-- `-Wtautological-pointer-compare` and `-Wpointer-bool-conversion` now
-  diagnose a reference to a function (e.g. of type `void (&)()`) compared
-  against or converted to a null pointer, the same as a bare function name.
-  (#GH46362)
-
-- Added a clearer diagnostic for uninitialized decomposition declarations. (#GH90107)
-
-- Fixed missing sentinel attribute diagnostic discrepancy with explicit object
-  parameters in variadic functions. (#GH200007)
-
 ### Improvements to Clang's time-trace
 
 ### Improvements to Coverage Mapping
 
-- [MC/DC] Nested expressions are handled as individual MC/DC expressions.
-- "Single byte coverage" now supports branch coverage and can be used
-  together with `-fcoverage-mcdc`.
-- Consteval member functions are no longer emitted in coverage mappings,
-  matching the existing behavior for free consteval functions. (#GH164448)
-
 ### Bug Fixes in This Version
 
-- Fixed an assertion when comparing a fixed point type with a `_BitInt` type. (#GH196948)
-- Fixed atomic boolean compound assignment; the conversion back to atomic bool would be miscompiled. (#GH33210)
-- Correctly handle default template argument when establishing subsumption. (#GH188640)
-- Fixed a failed assertion in the preprocessor when `__has_embed` parameters are missing parentheses. (#GH175088)
-- Fix lifetime extension of temporaries in for-range-initializers in templates. (#GH165182)
-- Fixed a preprocessor crash in `__has_cpp_attribute` on incomplete scoped attributes. (#GH178098)
-- Fixes an assertion failure when evaluating `__underlying_type` on enum redeclarations. (#GH177943)
-- Fixed an assertion failure caused by nested macro expansion during header-name lexing (`__has_embed(__has_include)`). (#GH178635)
-- Clang now outputs relative paths of embeds for dependency output. (#GH161950)
-- Fix the result type of a binary operation where both operands are 'void' l-values. (#GH111300)
-- Fixed an assertion failure when evaluating `_Countof` on invalid `void`-typed operands. (#GH180893)
-- Fixed an assertion failure in the serialized diagnostic printer when it is destroyed without calling `finish()`. (#GH140433)
-- Fixed an assertion failure caused by error recovery while extending a nested name specifier with results from ordinary lookup. (#GH181470)
-- Fixed a crash when parsing `#pragma clang attribute` arguments for attributes that forbid arguments. (#GH182122)
-- Fixed a bug in how Clang re-transforms expressions produced from substititions
-  from type aliases and concept specializations. (#GH191738) (#GH196375)
-- Fixed a bug with multiple-include optimization (MIOpt) state not being preserved in some cases during lexing, which could suppress header-guard mismatch diagnostics and interfere with include-guard optimization. (#GH180155)
-- Fixed a crash when normalizing constraints involving concept template parameters whose index coincided with non-concept template parameters in the same parameter mapping.
-- Fixed a crash caused by accessing dependent diagnostics of a non-dependent context.
-- Fixed a crash when substituting into a non-type template parameter that has a type containing an undeduced placeholder type.
-- Fixed several crashes and improved diagnostics when a multidimensional subscript operator is applied to a built-in type. (#GH187800)
-- Correctly diagnosing and no longer crashing when `export module foo`
-  (without a semicolon) are the final tokens in a module file. (#GH187771)
-- Fixed a crash in duplicate attribute checking caused by comparing constant arguments with different integer signedness. (#GH188259)
-- Clang now emits an error when returning an initializer list from a lambda
-  with an explicit return type of void. The diagnostic now correctly refers
-  to "lambda" instead of "block". (#GH188661)
-- Fixed a crash on `_BitInt(N)` arrays where 129 ≤ N ≤ 192 due to incorrect array filler lowering. (#GH189643)
-- Fixed the behavior in C23 of `auto`, by emitting an error when an array type is specified for a `char *`. (#GH162694)
-- Fixed an issue where an assert was thrown instead of an error if no vulkan env was specified with `--triple spirv`. (#GH189964)
-- Fixed incorrect rejection of `auto` with reordered declaration specifiers in C23. (#GH164121)
-- Fixed a bug where Clang fails to find instantiation of Decls in constraint checking. (#GH173086)
-- Fixed a crash when assigning to an element of an `ext_vector_type` with `bool` element type. (#GH189260)
-- Fixed a crash caused by declaring multiple `ownership_returns` attributes with mismatched or missing arguments. (#GH188733)
-- Clang now emits an error for friend declarations of lambda members. (#GH26540)
-- Fixed a crash caused by lambda capture handling in delayed default arguments. (#GH176534)
-- Fixed a crash when parsing invalid `static_assert` declarations with string-literal messages (#GH187690).
-- Fixed a potential stack-use-after-return issue in Clang when copy-initializing
-  an array via an element-at-a-time copy loop (#GH192026)
-- Fixed an issue where certain designated initializers would be rejected for constexpr variables. (#GH193373)
-- Fixed `clang::Preprocessor::recomputeCurLexerKind` to avoid default fallback to `CurLexerCallback = CLK_CachingLexer;`. This prevents code-completion
-  EOF handling from accidentally restoring CLK_CachingLexer while a tentative parse is still active, which could trigger a caching lexer re-entry assertion
-  in clangd signature help. (#GH200677)
-- Fixed incorrect expansion source ranges for synthesized tokens produced by
-  feature-like builtin macros such as `__has_builtin`, which could trigger
-  assertions in syntax token collection. (#GH196067)
-- Fixed a crash when `#embed` is used with C++ modules (#GH195350)
-- Fixed an assertion in constant evaluation when using a defaulted comparison operator in a `union`. (#GH147127)
-- Fixed a bug where `-x cuda` caused clang to immediately resolve templates that should not be. (#GH200545)
-- Fixed an issue where `__typeof_unqual` and `__typeof_unqual__` were rejected as a declaration specifier in block scope in C++.
-- Fixed crash when checking for overflow for unary operator that can't overflow (#GH170072)
-- Clang no longer handles a `" q-char-sequence "` header name as a string literal (#GH132643).
-- Under `-fdollars-in-identifiers`, the `$` can now appear in user-defined-literals. (#GH173985)
-- Clang now detects invalid unicode characters in identifiers formed by token concatenation. (#GH101342)
-- Fixed an assertion where we improperly handled implicit conversions to integral types from an atomic-type with a conversion function. (#GH201770)
-- Fixed assertion failures involving code completion with delayed default arguments and exception specifications. (#GH200879)
-- Fixed a regression where calling a function that takes a class-type parameter by value inside `decltype` of a concept could be incorrectly rejected when used as a non-type template argument. (#GH175831)
-- Clang no longer crashes if `__attribute__((cleanup))` is applied to an invalid declaration. Furthermore, in declarations that
-  contain multiple `cleanup` attributes, Clang now only uses the last one, matching GCC's behaviour (previously, Clang
-  would only use the first one). A new warning that diagnoses such declarations has been added to `-Wignored-attributes`.
-  (#GH191829)
-- Fixed a crash in the constant evaluator when an ill-formed array new-expression whose bound could not be determined (e.g. `new int[]()`) was used in a constant expression. (#GH200139)
-- Fixed a case where function effect analysis (`nonblocking` etc.) did not visit a destructor invoked from a `delete` expression. (#GH184460)
-- Clang now defines the GCC-compatible predefined macros `__WCHAR_MIN__`, `__WINT_MIN__`, and `__SIG_ATOMIC_MIN__`. (#GH199678)
-- Fix a crash in addUnsizedArray due assert not verifying we have a Base before doing checks on it. (#GH44212)
-
 #### Bug Fixes to Compiler Builtins
 
-- Fix a crash when passing an unresolved overload set to `__builtin_classify_type`. (#GH175589)
-- Fixed a crash when calling `__builtin_allow_sanitize_check` with no arguments. (#GH183927)
-- `__annotation` is now diagnosed as unsupported on non-Windows/UEFI targets, fixing a
-  crash when using it with `-fms-extensions` on other platforms. (#GH184318)
-- Fixed a compiler crash due to an unresolved overloaded function type when
-  calling `__builtin_bit_cast`. (#GH200112)
-- Clang now SFINAE friendly when the ``__reference_meows_from_temporary`` builtins
-  should SFINAE friendly when the 1st type is not a reference type. (#GH206524)
-- Fixed `__builtin_offsetof` incorrectly sign-extending unsigned array indices
-  with the high bit set (e.g. `uint8_t` values >= 128), which produced wrong
-  offset values in constant expressions. (#GH199319)
-
-
 #### Bug Fixes to Attribute Support
 
-- Fixed a behavioral discrepancy between deleted functions and private members when checking the `enable_if` attribute. (#GH175895)
-- Fixed `init_priority` attribute by delaying type checks until after the type is deduced.
-- Fixed a crash when a `section` attribute or `#pragma clang section` caused a
-  section type conflict with a declaration whose name is not a simple identifier,
-  such as a lambda's call operator. (#GH192264)
-- Fixed a regression where attributed types (such as those carrying `_Nonnull`/`_Nullable` attributes)
-  were not deduplicated, because the attributes' arguments were not taken into
-  account when uniquing them. The duplications could substantially increase the
-  size of precompiled headers and modules (PCH/PCM), and the time spent loading
-  them. (#GH200961)
-
 #### Bug Fixes to C++ Support
 
-- Fixed a preprocessor assertion failure triggered when parsing an invalid template-id starting with `::template operator`. (#GH186582)
-- Fixed a crash when a function template is defined as a non-template friend with a global scope qualifier. (#GH185341)
-- Fixed a bug of incorrect template depth for abbreviated templates. (#GH200682)
-- Fixed valid C++ code that uses an address-of-function-template expression,
-  such as `decltype(&T::func<Args...>)`, in SFINAE checks when substituting the
-  function template's trailing return type fails, such as when the return type
-  contains a `decltype` probe that calls a deleted function.
-- Clang now rejects constant template parameters with block pointer types, since these are not implemented anyway and would lead to crashes. (#GH189247)
-- Fixed some concept bugs introduced in Clang 22 (#GH197597)
-- Clang no longer reject call expressions whose type is a not-yet-deduced auto type. (#GH207565)
-- Fixed a crash on error recovery when dealing with invalid templates. (#GH183075)
-- Fixed a crash when instantiating `requires` expressions involving substitution failures in C++ concepts. (#GH176402)
-- Concepts appearing in the require-clause of a member function no longer have access to non-public members of that class,
-  or to a current class object. (#GH115838) (#GH194803) (#GH197067)
-- We no longer caches invalid variable specializations. (#GH132592)
-- Fixed an incorrect template argument deduction when matching packs of template
-  template parameters when one of its parameters is also a pack. (#GH181166)
-- Clang no longer errors on overloads with different ref-qualifiers and constraints. (#GH120812)
-- Fixed a crash when a default argument is passed to an explicit object parameter. (#GH176639)
-- Fixed an alias template CTAD crash.
-- Improvements to typo correction involving template names. (#GH207498)
-- Correctly diagnose uses of `co_await` / `co_yield` in the default argument of nested function declarations. (#GH98923)
-- Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741)
-- Clang incorrectly instantiated variable specializations outside of the immediate context. (#GH54439)
-- Fixed a crash when pack expansions are used as arguments for non-pack parameters of built-in templates. (#GH180307)
-- Fixed crash instantiating class member specializations.
-- Fixed a crash during class template instantiation when a member variable
-  template's type substitution fails (e.g. `typename T::type` with `T=int`),
-  which left the `VarTemplateDecl` unregistered and caused a subsequent
-  assertion failure when instantiating a partial specialization of that member.
-  (#GH198890)
-- Fix a problem where a substitution failure when evaluating a type requirement
-  could directly make the program ill-formed.
-- Typo correction now corrects the name qualifier for invalid template names.
-- Fix a problem where pack index expressions where incorrectly being regarded as equivalent.
-- Correctly diagnose narrowing in pack index expressions. (#GH205650)
-- Fixed a bug where captured variables in non-mutable lambdas were incorrectly treated as mutable
-  when used inside decltype in the return type. (#GH180460)
-- Fixed a crash when evaluating uninitialized GCC vector/ext_vector_type vectors in `constexpr`. (#GH180044)
-- Fixed a crash when `explicit(bool)` is used with an incomplete enumeration. (#GH183887)
-- Fix crash-on-invalid caused by qualified forward declarations. (#GH202320)
-- Fixed a crash on `typeid` of incomplete local types during template instantiation. (#GH63242), (#GH176397)
-- Fixed spurious diagnostics produced when checking if constraints are equivalent for redeclarations,
-  which could make the program mistakenly ill-formed.
-- Fixed a crash when an immediate-invoked `consteval` lambda is used as an invalid initializer. (#GH185270)
-- Fixed an assertion failure when using a global destructor with a target with a non-default program address space. (#GH186484)
-- Fixed a crash when instantiating an invalid out-of-line static data member definition in a local class. (#GH176152)
-- Inherited constructors in `dllexport` classes are now exported for ABI-compatible cases, matching
-  MSVC behavior. Constructors with variadic arguments or callee-cleanup parameters are not yet supported
-  and produce a warning. (#GH162640)
-- Correctly diagnose invalid non-dependent calls in dependent contexts. (#GH135694)
-- Fix initialization of GRO when GRO-return type mismatches, as part of CWG2563. (#GH98744)
-- Fix an error using an initializer list with array new for a type that is not default-constructible. (#GH81157)
-- Fixed a crash in invalid ``#module`` preprocessing directive. (#GH179220)
-- We no longer consider conversion operators when copy-initializing from the same type. This was non
-  conforming and could lead to recursive constraint satisfaction checking. (#GH149443)
-- Fixed a crash in Itanium C++ name mangling for a lambda in a local class field initializer inside a constructor/destructor. (#GH176395)
-- Fixed a crash when Expr::ClassifyImpl computes a classification like CL_LValue or CL_PRValue, then asserts that this
-  agrees with the AST node's own value category. (#GH202693)
-- Fixed crashes in Itanium C++ name mangling for lambdas with trailing requires-clauses involving requires-expressions. (#GH100774) (#GH123854)
-- Fixed an invalid rejection and assertion failure while generating `operator=` for fields with the `__restrict` qualifier. (#GH37979)
-- Fixed a use-after-free bug when parsing default arguments containing lambdas in declarations with template-id declarators. (#GH196725)
-- Fixed missing destructor cleanups for lambda init-captures in default member
-  initializers used during aggregate initialization. (#GH196469)
-- Fixed a crash in constant evaluation using placement new on an array which was later initialized. (#GH196450)
-- Fixed an issue where Clang incorrectly accepted invalid unqualified uses of local nested class names outside their declaring scope. (#GH184622)
-- Fixed a crash when instantiating a class template whose member variable partial specialization has an invalid primary template. (#GH195988)
-- Fixed a crash when parsing invalid friend declaration with storage-class specifier. (#GH186569)
-- Fixed a missing vtable for `dynamic_cast<FinalClass *>(this)` in a function template. (#GH198511)
-- Fixed an assertion failure during init-list checking of an array whose element type is an incomplete class. (#GH140685)
-- Fixed a crash when using a pack indexing type (e.g. ``Ts...[0]``) imported from another module. (#GH204479)
-- Fixed an ODR-merging error in modules, where class-scope `using enum` declarations were not recognized as matching across module
-  boundaries.  (#GH207066)
-- Fixed a crash when constant evaluation accessed a base class or member of an object wrapped in `_Atomic`. (#GH203328)
-
 #### Bug Fixes to AST Handling
 
-- Fixed a bug where explicit nullability property attributes were not stored in AST nodes in Objective-C. (#GH179703)
-- Fixed a bug where alias CTAD, or an invalid template template parameter, could create a template with an empty template
-  parameter list. This also adds asserts to prevent this from happening again.
-- Fixed a crash when parsing Doxygen `@param` commands attached to invalid declarations or non-function entities. (#GH182737)
-- Fixed the SourceLocation and SourceRange of reversed rewritten CXXOperatorCallExpr. (#GH192467)
-- Fixed a assertion when `__block` is used on global variables in C mode. (#GH183974)
-- Added missing AST nodes representing the `decltype` specifiers in destructor call to AST.
-- Fixed a missing ODR violation diagnostic introduced by the inline assembly string or clobber list. (#GH198616)
-
 #### Miscellaneous Bug Fixes
 
-- Fixed a crash whith the AST text dumper, when dumping a reference to a
-  decomposition with no bindinds. (#GH198842)
-- Fixed the arguments of the format attribute on `__builtin_os_log_format`. Previously, they were off by 1.
-
 #### Miscellaneous Clang Crashes Fixed
 
-- Fixed a crash when attempting to jump over initialization of a variable with variably modified type. (#GH175540)
-- Fixed a crash when using loop hint with a value dependent argument inside a
-  generic lambda. (#GH172289)
-- Fixed a crash in C++ overload resolution with `_Atomic`-qualified argument types. (#GH170433)
-- Fixed a crash related to missing source locations (#GH186655)
-- Fixed a crash when casting a parenthesized unresolved template-id or array section. (#GH183505)
-- Fixed a crash when initializing a `constexpr` pointer with a floating-point literal in C23. (#GH180313)
-- Fixed a lack of diagnostic for substitution failures in base classes when using `std::void_t`-like types.
-- Fixed a crash when emitting debug info for base classes with instantiation-dependent-only types (#GH193932)
-- Fixed an assertion when diagnosing address-space qualified `new`/`delete` in language-defined address spaces such as OpenCL `__local`. (#GH178319)
-- Fixed an assertion failure in ObjC++ ARC when binding a rvalue reference to reference with different lifetimes (#GH178524)
-- Fixed a crash when subscripting a vector type with large unsigned integer values. (#GH180563)
-- Fixed a crash when attempting to diagnose incompatible conversions involving function types (#GH182534)
-- Fixed a crash when evaluating `__is_bitwise_cloneable` on invalid record types. (#GH183707)
-- Fixed an assertion failure when casting a function pointer with a target with a non-default program address space. (#GH186210)
-- Fixed a crash when `decltype(__builtin_FUNCTION())` is used as a template type argument. (#GH167433)
-- Fixed an assertion failure when parsing an invalid `decltype` specifier with missing parentheses or extra semicolons. (#GH188014)
-- Fixed a crash when explicitly casting a complex type to or from an atomic complex type. (#GH172208)
-- Fixed a crash when explicitly casting a scalar to an atomic complex. (#GH114885)
-- Fixed an assertion failure when parsing an invalid out-of-line enum definition with template parameters. (#GH187909)
-- Fixed an assertion failure on invalid template template parameter during typo correction. (#GH183983)
-- Fixed an assertion failure in `isAtEndOfMacroExpansion` on macro expansions crossing the boundary of two fileIDs. (#GH115007), (#GH21755)
-- Fixed an assertion failure when `__builtin_dump_struct` is used with an
-  immediate-escalated callable. (#GH192846)
-- Fixed a crash when passing one sized implicitly casted vector to a `abs` function. (#GH204777)
-- Fixed a crash when diagnosing an invalid out-of-line definition of a member class template. (#GH201490)
-- Fixed a crash in the parser when a missing semicolon after a tag definition is followed by a template-id not preceded by `::`. (#GH207992)
-
 ### OpenACC Specific Changes
 
 ### OpenCL Specific Changes
 
-- Added support for OpenCL C 3.1 language version (`-cl-std=CL3.1`).
-
 ### Target Specific Changes
 
 #### AMDGPU Support
 
-- Introduced a new target specific builtin `__builtin_amdgcn_processor_is`,
-  a late / deferred query for the current target processor.
-- Introduced a new target specific builtin `__builtin_amdgcn_is_invocable`,
-  a late / deferred query for the availability of target specific builtins.
-- Initial support for gfx1310
-- The `amdgpu_num_sgpr` and `amdgpu_num_vgpr` function attributes are now
-  deprecated. Using them produces a `-Wdeprecated-declarations` warning. Use
-  `amdgpu_waves_per_eu` instead.
-
 #### NVPTX Support
 
 #### X86 Support
 
-- `march=znver6` is now supported.
-- Support ISA of `AVX512BMM`.
-  - Support intrinsic of `_mm512_bmacor16x16x16`.
-  - Support intrinsic of `_mm512_bmacxor16x16x16`.
-  - Support intrinsic of `_mm512_mask_bitrev_epi8`.
-  - Support intrinsic of `_mm512_maskz_bitrev_epi8`.
-  - Support intrinsic of `_mm512_bitrev_epi8`.
-  - Support intrinsic of `_mm256_bmacor16x16x16`.
-  - Support intrinsic of `_mm256_bmacxor16x16x16`.
-  - Support intrinsic of `_mm_mask_bitrev_epi8`.
-  - Support intrinsic of `_mm256_mask_bitrev_epi8`.
-  - Support intrinsic of `_mm_maskz_bitrev_epi8`.
-  - Support intrinsic of `_mm256_maskz_bitrev_epi8`.
-  - Support intrinsic of `_mm_bitrev_epi8`.
-  - Support intrinsic of `_mm256_bitrev_epi8`.
-- Removed support for `AMX-TF32` (`-mamx-tf32`) and `TMMULTF32PS` instruction.
-
 #### Arm and AArch64 Support
 
-- On Apple AArch64 targets, `__GCC_DESTRUCTIVE_SIZE` is now `128` (down from `256`)
-  to match the 128-byte cache line used by Apple cores, avoiding needless
-  over-alignment. This value is implementation-defined and should not be relied upon
-  in an ABI-sensitive way.
-
-- Support has been added for the following processors (-mcpu identifiers in parenthesis):
-
-  - Arm AGI CPU (armagicpu).
-  - Hisilicon hip12 core (hip12).
-  - NVIDIA Rigel core (rigel).
-
 #### Android Support
 
 #### Windows Support
 
-- Clang now defines the `_MSVC_TRADITIONAL` macro as `1` when emulating MSVC
-  19.15 (Visual Studio 2017 version 15.8) and later. (#GH47114)
-
-- `-fmacro-prefix-map=` (`-ffile-prefix-map=`) now affects an anonymous namespace hash generation
-  for the MSVC targets and allows deterministic symbol mangling for reproducible builds.
-
-- Added the `-fwinx64-eh-unwind=` flag to select the x64 Windows unwind info
-  version (`v1`, `v2-best-effort`, `v2-required`, or `v3`). The legacy
-  `-fwinx64-eh-unwindv2=` flag is deprecated; it is still accepted and mapped
-  onto the new flag as follows:
-
-  | Legacy `-fwinx64-eh-unwindv2=` | New `-fwinx64-eh-unwind=` |
-  | --- | --- |
-  | `disabled` | `v1` (default; no flag forwarded) |
-  | `best-effort` | `v2-best-effort` |
-  | `required` | `v2-required` |
-
-  The MSVC-compatible `/d2epilogunwind` and `/d2epilogunwindrequirev2`
-  options map to `v2-best-effort` and `v2-required` respectively.
-
-- When targeting Windows x64 with EGPR (`-mapx-features=egpr`), Clang now
-  automatically enables V3 unwind info (`-fwinx64-eh-unwind=v3`) if no
-  explicit unwind version was specified.
-
-- In MSVC compatibility mode, scalar and vector deleting destructors now call
-  ``__global_delete`` (or ``__global_array_delete`` for the array ``delete[]``
-  path) instead of directly referencing ``::operator delete``.
-  This matches MSVC's behavior and fixes ``LNK2001`` linker errors in
-  environments where no global ``::operator delete`` exists. When the
-  translation unit contains a ``::delete`` expression, a ``__global_delete``
-  forwarding body that calls ``::operator delete`` is emitted automatically.
-  Otherwise, if no body is emitted, an `/ALTERNATENAME` linker directive will
-  cause the linker to use the generated `__empty_global_delete` trap function
-  instead.
-
-- Clang now supports `-std:c++26preview` for compatibility with MSVC. This enables C++26 features.
-
 #### LoongArch Support
 
-- DWARF fission is now compatible with linker relaxations, allowing `-gsplit-dwarf` and `-mrelax`
-  to be used together when building for the LoongArch platform.
-
 #### RISC-V Support
 
-- Tenstorrent Ascalon D8 was renamed to Ascalon X. Use `tt-ascalon-x` with `-mcpu` or `-mtune`.
-- Intrinsics were added for the `Zvabd` (RISC-V Integer Vector Absolute Difference) extension.
-- Intrinsics were added for the `Zvzip` (Reordering Structured Data in Vector Registers) extension.
-- A new `-mtune` syntax was added to support processor-specific tuning feature string
-  Currently this new syntax is gated by the `-mexperimental-mtune-syntax` flag.
-
 #### CUDA/HIP Language Changes
 
-- The new offloading driver is now the default for HIP. Use
-  `--no-oflfoad-new-driver` to return to the old behavior.
-
 #### CUDA Support
 
-- Fixed a bug where host-device ambiguities in CUDA/HIP when retrieving the
-  address of specializations of templated functions that have overloads for both
-  host and device. (#GH199299)
-
 #### AIX Support
 
-- The driver default for the linker flag `-bcdtors` now defaults to `mbr`
-  (instead of `all`) which only extracts static init from archive members which
-  would otherwise be referenced.
-  (See <https://www.ibm.com/docs/en/aix/7.2.0?topic=l-ld-command> for details).
-- The driver now uses `-lcompiler_rt` instead of `-latomic`, and the compiler-rt
-  archive has been renamed from `libatomic.a` to `libcompiler_rt.a` to avoid conflicts
-  between the LLVM libatomic and the GNU libatomic from the AIX toolbox as they share
-  the same library name.
-- Added support for `#pragma comment(copyright, "token_sequence")` on AIX.
-  This directive embeds a copyright or identifying string into the compiled object file.
-  The string is included in the final executable and loaded into memory at program runtime.
-- The driver relaxes the restrictions on the `OBJECT_MODE` environment
-  variable and now silently accepts `32_64` and `any`.
-
 #### NetBSD Support
 
 #### WebAssembly Support
 
-- Fixed a crash when `__funcref` is applied to a non-function pointer type.
-  (#GH118233)
-- WebAssembly reference types (`__externref_t` and `__funcref` function
-  pointers) now lower to the opaque IR types `target("wasm.externref")` and
-  `target("wasm.funcref")` instead of `ptr addrspace(10)` /
-  `ptr addrspace(20)`.
-- Fixed a compiler crash at `-O2` when reference-type values were passed
-  through control flow that the SLP vectorizer tried to vectorize.
-
 #### AVR Support
 
 #### SystemZ Support
 
-- Add support for `#pragma export` for z/OS. This is a pragma used to export functions and variables
-  with external linkage from shared libraries. It provides compatibility with the IBM XL C/C++
-  compiler.
-
 ### DWARF Support in Clang
 
 ### Floating Point Support in Clang
@@ -1151,210 +158,40 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
 
 ### AST Matchers
 
-- Fixed `nullPointerConstant` matcher falsely matching integer literal `0`
-  in non-null-pointer contexts such as array subscripts and pointer arithmetic.
-- Add `functionTypeLoc` matcher for matching `FunctionTypeLoc`.
-- Add missing support for `TraversalKind` in some `addMatcher()` overloads.
-
 ### clang-format
 
-- Add `ObjCSpaceAfterMethodDeclarationPrefix` option to control space between the
-  '-'/'+' and the return type in Objective-C method declarations
-- Deprecate the `BinPackParameters` and `BinPackArguments` options and replace
-  them with the `PackParameters` and `PackArguments` structs (respectively) to
-  unify packing behavior. Add the `BreakAfter` option to the structs, allowing
-  parameter and argument lists to be formatted with one parameter/argument on each
-  line if they exceed the specified count.
-- Add `AfterComma` value to `BreakConstructorInitializers` to allow breaking
-  constructor initializers after commas, keeping the colon on the same line.
-- Extend `BreakBinaryOperations` to accept a structured configuration with
-  per-operator break rules and minimum chain length gating via `PerOperator`.
-- Add `AllowShortRecordOnASingleLine` option and set it to `EmptyAndAttached` for LLVM style.
-- Add `BreakFunctionDeclarationParameters` option to always break before function
-  declaration parameters.
-- Add `EnumAssignments` option to `AlignConsecutiveAssignments` for aligning
-  enum assignments without affecting other assignments.
-- Add `BreakBeforeReturnType` option to break before the function return
-  type.
-
 ### libclang
 
-- Visit constraints of `auto` type to properly visit concept usages (#GH166580)
-- Visit switch initializer statements (<https://bugs.kde.org/show_bug.cgi?id=415537#c2>)
-- Fix crash in clang_getBinaryOperatorKindSpelling and clang_getUnaryOperatorKindSpelling
-- The clang_Module_getASTFile API is deprecated and now always returns nullptr
-- The clang_Cursor_getCommentRange API will now return a comment range for macro definitions that have documentation comments.
-- Added CXType_PredefinedSugar for `__ptrdiff_t`, `__size_t`, and
-  `__signed_size_t` types, which are no longer exposed as
-  CXType_Unexposed.
-
 ### Code Completion
 
-- Fixed a crash in code completion when using a C-Style cast with a parenthesized
-  operand in Objective-C++ mode. (#GH180125)
-- Fixed a crash when code completion is triggered inside an ill-formed lambda's trailing requires-clause. (#GH201632)
-
 ### Static Analyzer
 
+#### Crash and bug fixes
+
 % comment:
 % This is for the Static Analyzer.
-% Subsections (using `####`), in this order, only when they have content:
-%   - New checkers and features
+% Use `####` headings for subsections:
 %   - Crash and bug fixes
+%   - New checkers and features
 %   - Improvements
 %   - Moved checkers
-%   - Removed checkers
-%   - Diagnostic changes
-
-#### New checkers and features
-
-- Introduced the experimental `alpha.cplusplus.UseAfterLifetimeEnd` checker to detect dangling pointers and references bound to local variables that go out of scope, using the `[[clang::lifetimebound]]` annotation. (#GH205521)
-- Introduced the `optin.core.UnconditionalVAArg` checker to report variadic functions that unconditionally use `va_arg()`, which is undefined behavior when the function is called without any variadic arguments (SEI-CERT EXP47-C). (#GH175602)
-- The `unix.DynamicMemoryModeling` modeling gained a new option to create branches where a memory allocation fails and returns a null pointer. It is disabled by default. (#GH205371)
-- The `security.insecureAPI.DeprecatedOrUnsafeBufferHandling` checker gained a new `ReportInC99AndEarlier` option. When enabled, deprecated buffer-handling functions (`memcpy`, `memset`, `memmove`, etc.) are also reported when not compiling for C11 or later. It is disabled by default. (#GH168704, #GH177379)
-- The `optin.taint.TaintPropagation` checker gained a new `EnableDefaultConfig` option, which makes it possible to disable the built-in taint configuration and use a fully custom configuration instead. (#GH176185)
-
-#### Crash and bug fixes
-
-- Fixed the `security.VAList` checker producing false positives when analyzing C23 code where `va_start` expands to `__builtin_c23_va_start`. (#GH192024)
-- Fixed a crash when copying uninitialized data in a function named `swap`. (#GH178797)
-- Fixed a compiler crash when combining `_Atomic` and `__auto_type` in C, for example `_Atomic __auto_type x = expr`. (#GH118058)
-- Fixed a crash in the `unix.Malloc` checker when a function is annotated with both `ownership_returns` and `ownership_takes` (or `ownership_holds`). (#GH183344)
-- Fixed a crash in the `alpha.unix.cstring` checkers on zero-size element types (for example an empty struct in C). (#GH190457)
-- Fixed a use-after-free in `CheckerContext::getMacroNameOrSpelling`. (#GH194174)
-- Fixed a false positive in the `alpha.unix.cstring` checkers when the buffer argument points into the middle of an array, such as `memcpy(dst, &arr[i], size)`. (#GH198346)
-- Fixed the default binding of union aggregates being overwritten when initializing array elements with union members. (#GH178694)
-- The analyzer no longer rules out the equality of a pointer to the stack and a symbolic pointer in unknown space, because a function may return a pointer to some other stack frame (for example one received as an argument). (#GH187080)
-- Fixed the `getcwd` summary in the `unix.StdCLibraryFunctions` checker. (#GH175136)
-- Fixed invalid HTML nesting for popups at end of line. (#GH46089)
-- Fixed a false-positive in the `unix.BlockInCriticalSection` checker that reported double locking when using `std::lock_guard`/`std::unique_lock`/`std::scoped_lock`. (#GH208729)
-- Fixed a false-negative in the `unix.Malloc` checker due to a typo in the expected arguments of `if_nameindex`. It will now properly match its single expected argument. (#GH207726)
-- Fixed a crash in the Z3-solver-based (unsupported) constraint manager involving unary/binary operators. (#GH205037)
-- Fixed a crash where GNU statement expression syntax (`({ ... })`) caused arguments to disappear from parameter list. (#GH205718)
 
 #### Improvements
 
-- Significantly sped up core data structures like ImmutableSet/Map, leading to up to 5.2x faster analysis in edge cases. (#GH205552, #GH207596, #GH208907)
-- The unmaintained and crash-prone Z3 constraint-manager backend selected via `-analyzer-constraints=z3` was renamed to `-analyzer-constraints=unsupported-z3` to make its unsupported status explicit. (#GH205370)
-- The conservative call-invalidation logic now invalidates the object of an opaque constructor call regardless of whether an argument refers to it. (#GH170887)
-- Improved the resolution of constant values from initializers in RegionStore, including preserving default bindings for aggregate-like cases, generalized field-initializer resolution for arbitrary nesting, and normalized sub-array indices. (#GH199271)
-- The analyzer now conservatively evaluates `std::sort`, `std::stable_sort`, and `std::inplace_merge` to prevent false positives caused by complex STL internals that cannot be modeled adequately by the engine. (#GH178910)
-- The `optin.cplusplus.VirtualCall` checker no longer reports virtual method calls during construction/destruction when the call site is in a system header. (#GH184178)
-- Reduced false positives of the `unix.MallocSizeof` checker for layout-compatible types, such as `malloc(sizeof(std::atomic<int32_t>))` assigned to an `int32_t *`. (#GH200253)
-- The `optin.core.FixedAddressDereference` checker no longer reports dereferences of a fixed address when the pointee is `volatile`. (#GH181644)
-- Improved `[[clang::suppress]]` handling for tricky template specializations, nested templates, and friend function templates that are forward-declared at namespace scope. (#GH182659, #GH178441, #GH183727, #GH187043)
-- The `unix.BlockInCriticalSection` checker now understands `std::scoped_lock` in addition to `std::lock_guard` and `std::unique_lock`. (#GH176134)
-- The `alpha.unix.cstring` sub-checkers were untangled so that each one models and reports independently instead of relying on another sub-checker being enabled. (#GH186802)
-- The analyzer now detects reads of uninitialized dynamically-allocated objects. (#GH193001)
-- The `cplusplus.Move` checker can now detect a use-after-move for the three-argument `std::move` on containers (when `alpha.cplusplus.IteratorModeling` is also enabled). (#GH137157)
-- The condition of a `switch` statement now triggers the `check::BranchCondition` callback, so checkers like `core.uninitialized.Branch` can report an undefined `switch` condition. (#GH182058)
-- Improved handling of multiple `[[assume]]` attributes on a single statement. (#GH198618)
-- When the execution environment is untrusted, the `argv`, `argc`, and `envp` parameters of `main` are now treated as taint sources. (#GH178054)
-- The `alpha.unix.PthreadLock` checker now emits path notes on lock, unlock, destroy, and init operations. (#GH202473)
-- Added a new `check::LifetimeEnd` callback that fires for each `CFGLifetimeEnds` element, which is useful for detecting dangling pointers. (#GH201123)
-- The `unix.StdCLibraryFunctions` standard-library summaries were optimized for binary size. (#GH202662)
-- Fixed the alignment of entries printed by `clang -cc1 -analyzer-print-analyzer-options` / `-analyzer-help`. (#GH190570)
-- Improved the models of `strchr`/`strrchr`/`memchr`/`strstr`/`strpbrk`/`strchrnul`, enabling `core.StackAddressEscape` to catch dangling pointers returned by these functions. (#GH203260)
-- Improved the modeling of symbolic ranges in the engine when calculating the largest and smallest possible values for range sets involving the `+`, `-`, and `*` binary operators. (#GH173113)
-
 #### Moved checkers
 
-- The checker `unix.cstring.UninitializedRead` is now out of alpha. (#GH196292)
-
-#### Removed checkers
-
-- The `alpha.core.FixedAddr` checker was removed. It was overly simplistic and is largely superseded by the `optin.core.FixedAddressDereference` checker. (#GH182033)
-
-#### Diagnostic changes
-
-- Cross Translation Unit (CTU) import failures are now reported at the location where the imported symbol would be used, making it easier to understand why a cross-TU bug was missed. (#GH188795)
-- Uninitialized-value diagnostics no longer emit a misleading "initialized here" note for a variable that was declared without an initializer; the note now reads "declared without an initial value". (#GH198345)
-
 (release-notes-sanitizers)=
 
 ### Sanitizers
 
-- UndefinedBehaviorSanitizer now supports `__ubsan_default_suppressions`.
-- Sanitizer Special Case Lists (`-fsanitize-ignorelist`) now support
-  Version 4 of the Special Case List format, which introduces a transition
-  period for leading dot-slash (`./`) canonicalization in path matching.
-  Version 4 matches both canonicalized and non-canonicalized paths but emits a
-  warning for deprecated matches. Version 5 drops backward compatibility and
-  requires rules to match canonicalized paths (without leading `./`).
-- Sanitizer Special Case Lists (`-fsanitize-ignorelist`) and warning
-  suppression mappings (`--warning-suppression-mappings`) now recognize version
-  4 of the Special Case List format (indicated by `#!special-case-list-v4`).
-  On Windows hosts, path matching is slash-agnostic (both forward slashes (`/`)
-  and backslashes (`\`) match either path separator in both patterns and paths).
-
 ### Python Binding Changes
 
-- Add deprecation warnings to `CompletionChunk.isKind...` methods.
-  These will be removed in a future release. Existing uses should be adapted
-  to directly compare equality of the `CompletionChunk` kind with
-  the corresponding `CompletionChunkKind` variant.
-
-  Affected methods: `isKindOptional`, `isKindTypedText`, `isKindPlaceHolder`,
-  `isKindInformative` and `isKindResultType`.
-
-- Add a deprecation warning to `CodeCompletionResults.results`.
-  This property will become an implementation detail with changed behavior in a
-  future release and should not be used directly.. Existing uses of
-  `CodeCompletionResults.results` should be changed to directly use
-  `CodeCompletionResults`: it nows supports `__len__` and `__getitem__`,
-  so it can be used the same as `CodeCompletionResults.results`.
-
-- Added a new helper method `get_clang_version` to the class `Config` to
-  read the version string of the libclang in use.
-
 ### OpenMP Support
 
-- Added support for `transparent` clause in task and taskloop directives.
-- Added support for `use_device_ptr` clause to accept an optional
-  `fallback` modifier (`fb_nullify` or `fb_preserve`) with OpenMP >= 61.
-- Added support for `local` clause with declare_target directive when
-  OpenMP >= 60.
-- Fixed the identity element used for `reduction(* : x)` over C++ class types
-  (e.g. `std::complex`). The private copy is now initialized to the
-  multiplicative identity instead of being value-initialized, which previously
-  produced a wrong result (the product collapsed to the additive identity).
-
 ### SYCL Support
 
-- SYCL compilations now default to `-std=c++17` when no explicit language
-  standard is specified. Standards below C++17 are rejected with a diagnostic.
-- Clang now assumes default target for SYCL device compilation is 64-bit SPIR-V
-  and it now diagnoses if a non-supporting target is specified via command line.
-  (#GH167358)
-- The SYCL runtime shared library has been renamed from `libsycl.so` to
-  `libLLVMSYCL.so` to align with LLVM naming conventions.
-- SYCL header include paths are now added automatically for both host and
-  device compilations.
-- SYCL runtime library linking is now supported on Windows. When `-fsycl` is
-  specified, Clang automatically adds `/MD` if no explicit CRT flag is
-  present, links the appropriate debug (`LLVMSYCLd.lib`) or release
-  (`LLVMSYCL.lib`) library, and rejects static CRT flags (`/MT`,
-  `/MTd`) with a diagnostic. Use `-nolibsycl` to suppress automatic
-  library linking.
-- Fixed `-nolibsycl` being silently ignored on Linux: the SYCL runtime
-  library was unconditionally added to the link line even when the flag was
-  passed.
-- Added the `-fsycl-device-image-split=` option to select the granularity at
-  which SYCL device code is grouped into device images. Supported values are
-  `kernel` (one device image per kernel), `translation_unit` (one device image
-  per translation unit), and `link_unit` (one device image per linking unit).
-  The bare `-fsycl-device-image-split` flag is an alias for
-  `-fsycl-device-image-split=translation_unit`, which is also the default.
-- Clang now is capable of diagnosing reference kernel parameters which are not
-  allowed by SYCL 2020 spec.
-
 #### Improvements
 
-- Improved substitution performance in concept checking. (#GH172266)
-- Clang now preserves the left-hand side of a binary expression (such as an
-  assignment or comparison) in a `RecoveryExpr` when the right-hand side fails
-  to parse. This improves IDE features like go-to-definition in `clangd`.
-
 ## Additional Information
 
 A wide variety of additional information is available on the [Clang web
diff --git a/cmake/Modules/LLVMVersion.cmake b/cmake/Modules/LLVMVersion.cmake
index fa1b0997cd3e8..ca16353d25c9b 100644
--- a/cmake/Modules/LLVMVersion.cmake
+++ b/cmake/Modules/LLVMVersion.cmake
@@ -1,7 +1,7 @@
 # The LLVM Version number information
 
 if(NOT DEFINED LLVM_VERSION_MAJOR)
-  set(LLVM_VERSION_MAJOR 23)
+  set(LLVM_VERSION_MAJOR 24)
 endif()
 if(NOT DEFINED LLVM_VERSION_MINOR)
   set(LLVM_VERSION_MINOR 0)
diff --git a/flang/docs/ReleaseNotes.md b/flang/docs/ReleaseNotes.md
index 0e0ba99abc881..888da4d58b868 100644
--- a/flang/docs/ReleaseNotes.md
+++ b/flang/docs/ReleaseNotes.md
@@ -33,15 +33,8 @@ page](https://llvm.org/releases/).
 
 ## Non-comprehensive list of changes in this release
 
-- Global, allocatable, and pointer arrays are now aligned to 64 bytes. The
-  flang runtime allocator was updated to honor this alignment for allocatable
-  and pointer arrays.
-
 ## New Compiler Flags
 
-- The warning flags with prefixes -Wopen-mp and -Wopen-acc have been deprecated in favor of corrected spellings with the respective prefixes -Wopenmp and -Wopenacc. Removal of the deprecated options is planned for LLVM 25 (July 2027).
-- The `-Werror` flag will cause all warnings to become errors. This includes warnings about support for OpenMP versions, which will now prevent the compilation from happening with the `-Werror` flag. These OpenMP warnings can be disabled with `-Wno-experimental-option`.
-
 ## Windows Support
 
 ## Fortran Language Changes in Flang
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..2faca50f969ba 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -36,7 +36,7 @@
 // _LIBCPP_VERSION represents the version of libc++, which matches the version of LLVM.
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
-#  define _LIBCPP_VERSION 230000
+#  define _LIBCPP_VERSION 240000
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 36f50db965f04..cc1628f48bb0e 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -29,149 +29,18 @@ Non-comprehensive list of changes in this release
 ELF Improvements
 ----------------
 
-* Input file loading is now parallelized, meaningfully reducing link time
-  for large links.
-  (`#191690 <https://github.com/llvm/llvm-project/pull/191690>`_)
-* ``--gc-sections`` mark phase is now parallelized.
-  (`#189321 <https://github.com/llvm/llvm-project/pull/189321>`_)
-* Relocation scanning was rewritten as target-specific scanners for all
-  targets with shared library support, devirtualizing the hot
-  relocation-classification path.
-
-* Added ``--bp-compression-sort-section=<glob>[=<layout_priority>[=<match_priority>]]``,
-  replacing the old coarse ``--bp-compression-sort`` modes with a way to split
-  input sections into multiple compression groups, run balanced partitioning
-  independently per group, and leave out sections that are poor candidates for
-  BP.
-  ``layout_priority`` controls group placement order (lower value = placed
-  first, default 0). ``match_priority`` resolves conflicts when multiple globs
-  match the same section (lower value = higher priority; explicit priority
-  beats positional last-match-wins; default: positional). In ELF, the glob
-  matches input section names (e.g. ``.text.unlikely.code1``).
-* Added ``-z memtag-{mode,heap,stack}`` as generic replacements for the
-  Android-specific ``--android-memtag-*`` flags; ``--android-memtag-note``
-  keeps the Android-specific memtag note opt-in.
-  (`#188205 <https://github.com/llvm/llvm-project/pull/188205>`_)
-* Unused space in executable output sections is now filled with trap, primarily
-  for ``-z separate-code`` mode.
-  (`#176845 <https://github.com/llvm/llvm-project/pull/176845>`_)
-* ``.eh_frame_hdr`` now supports the ``DW_EH_PE_sdata8`` encoding,
-  auto-upgrading from ``sdata4`` when a table entry or the frame pointer
-  exceeds the 32-bit range, instead of erroring out for large executables.
-  (`#179089 <https://github.com/llvm/llvm-project/pull/179089>`_)
-* ``vna_flags`` is now set to ``VER_FLG_WEAK`` when all undefined references
-  to a version are weak, allowing glibc's dynamic loader to warn instead of
-  error when the version is missing at runtime.
-  (`#176673 <https://github.com/llvm/llvm-project/pull/176673>`_)
-* ``.ltext.*`` input sections are now merged into a single ``.ltext`` output
-  section, matching the existing ``.ldata.*``/``.lrodata.*``/``.lbss.*``
-  handling for the large code model with ``-ffunction-sections``.
-  (`#190305 <https://github.com/llvm/llvm-project/pull/190305>`_)
-* ``.tbss`` output sections may now use an explicit address expression;
-  previously it was silently overridden to follow the preceding ``.tbss``.
-  (`#196447 <https://github.com/llvm/llvm-project/pull/196447>`_)
-* ``--discard-locals``/``--discard-all`` combined with ``-r``/``--emit-relocs``
-  no longer discard local symbols that are referenced only from retained
-  non-``SHF_ALLOC`` sections (e.g. ``.L`` symbols referenced by
-  ``.debug_info``), fixing DWARF corruption in the output.
-  (`#209035 <https://github.com/llvm/llvm-project/pull/209035>`_)
-  (`#209042 <https://github.com/llvm/llvm-project/pull/209042>`_)
-* ``--retain-symbols-file`` now filters ``.symtab`` instead of ``.dynsym``,
-  matching GNU ld.
-  (`#209063 <https://github.com/llvm/llvm-project/pull/209063>`_)
-* ``INCLUDE`` in linker scripts now fully parses its own content instead of
-  sharing a lexer buffer stack with the includer, fixing spurious acceptance
-  of malformed scripts.
-  (`#193427 <https://github.com/llvm/llvm-project/pull/193427>`_)
-* The ``OVERLAY`` linker script command now accepts any
-  output-section-command (e.g. symbol assignments), not just input section
-  descriptions.
-  (`#203524 <https://github.com/llvm/llvm-project/pull/203524>`_)
-* Thunks are no longer reused across an ``OVERLAY`` boundary unless the
-  target output section is guaranteed to be resident at the same time.
-  (`#200415 <https://github.com/llvm/llvm-project/pull/200415>`_)
-* When a ``SECTIONS`` command interleaves relro and non-relro sections, lld now
-  emits one ``PT_GNU_RELRO`` segment per contiguous run of relro sections
-  instead of reporting a ``not contiguous with other relro sections`` error.
-* ``SHT_NOBITS`` sections are now excluded from LMA overlap checks, matching
-  GNU ld and allowing e.g. a startup section to share an LMA with ``.bss``
-  in embedded linker scripts.
-  (`#196423 <https://github.com/llvm/llvm-project/pull/196423>`_)
-* LTO: the middle-end no longer emits new references to, or internalizes,
-  symbols defined in bitcode after the extracted-bitcode set has been fixed,
-  preventing undefined symbol references from transforms that run after
-  linking has determined which bitcode files to extract.
-  (`#164916 <https://github.com/llvm/llvm-project/pull/164916>`_)
-* DTLTO: significantly improved the performance of adding backend output
-  files to the link, especially on Windows.
-  (`#186366 <https://github.com/llvm/llvm-project/pull/186366>`_)
-* For AArch64, fixed ``.relr.auth.dyn`` -> ``.rela.dyn`` movement to
-  properly adjust ``__rela_iplt_start``/``__rela_iplt_end`` and size the
-  ``.dynamic`` section for both tags.
-  (`#195649 <https://github.com/llvm/llvm-project/pull/195649>`_)
-* For AArch64, handle Memtag globals for ``R_AARCH64_AUTH_ABS64``.
-  (`#173291 <https://github.com/llvm/llvm-project/pull/173291>`_)
-* For AArch64, fixed TLS GD against non-preemptible dynamic symbols (e.g.
-  ``protected`` or ``-Bsymbolic``) in DSOs, which previously produced an
-  inconsistent GOT entry and spurious preemption.
-  (`#207881 <https://github.com/llvm/llvm-project/pull/207881>`_)
-* For AArch64, ``adrp``+``ldr`` GOT relaxation is now decided per-symbol,
-  all-or-nothing, avoiding invalid relaxation when a branch target sits
-  between the ``adrp`` and ``ldr`` of a pair.
-  (`#208396 <https://github.com/llvm/llvm-project/pull/208396>`_)
-* For AArch64, a redundant local-exec TLS ``add`` with a zero high-12-bits
-  immediate is now relaxed to a ``nop``.
-  (`#204286 <https://github.com/llvm/llvm-project/pull/204286>`_)
-* x86-64 CFI jump table relaxation reduces the runtime overhead of indirect
-  calls under Control Flow Integrity by opportunistically moving eligible
-  function bodies into the jump table itself.
-  (`#147424 <https://github.com/llvm/llvm-project/pull/147424>`_)
-
 Breaking changes
 ----------------
 
-* The symbol partition feature has been removed. lld no longer recognizes
-  ``SHT_LLVM_SYMPART`` sections, which are now treated as ordinary sections. The
-  feature saw no adoption beyond a Chromium experiment that has since been
-  retired.
-
-* An OutputSection that has an address expression, and is also assigned
-  to a MEMORY region, will now use the address expression in preference
-  to the next available location in the MEMORY region. This brings LLD
-  in line with GNU ld, but is a change in behavior from previous LLD
-  releases.
-  
-* The default extension for time trace files is now ``.time-trace.json``.
-
 COFF Improvements
 -----------------
 
-* Added support for ``.obj.arm64ec`` sections to allow embedding ARM64EC object
-  files inside ARM64 object files.
-
 MinGW Improvements
 ------------------
 
-* Added ``--push-state`` and ``--pop-state``, offering the same semantics as
-  when used with the ELF linker: The state of ``--Bstatic``/``--Bdynamic`` and
-  ``--whole-archive`` are pushed onto a stack and popped from it.
-
-* Added the ``--native-def`` option to specify the native module-definition
-  file for ARM64X targets. If a module-definition file is passed without this
-  option, it is used for both native and EC views. ``--native-def=`` can be
-  used to prevent the native view from using the default file.
-
 MachO Improvements
 ------------------
 
-* ``--bp-compression-sort-section`` now accepts optional layout and match
-  priorities (same syntax as ELF). In Mach-O, the glob matches the
-  concatenated segment+section name (e.g. ``__TEXT__text``).
-* Restructure thunk generation algorithm to be more efficiently create thunks
-  (`#193367 <https://github.com/llvm/llvm-project/pull/193367>`_)
-* Alphabetically sort LC_LINKER_OPTIONS before processing to match Apple linker behavior
-  (`#201604 https://github.com/llvm/llvm-project/pull/201604`)
-
 WebAssembly Improvements
 ------------------------
 
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index b186fbefb9e87..d0c4c1edbf5af 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -54,181 +54,22 @@ Makes programs 10x faster by doing Special New Thing.
 
 ### Changes to the LLVM IR
 
-* Removed `llvm.convert.to.fp16` and `llvm.convert.from.fp16`
-  intrinsics. These are equivalent to `fptrunc` and `fpext` with half
-  with a bitcast.
-
-* "denormal-fp-math" and "denormal-fp-math-f32" string attributes were
-  migrated to first-class denormal_fpenv attribute.
-
-* The `"nooutline"` attribute is now writen as `nooutline`. Existing IR and
-  bitcode will be automatically updated.
-
-* `ConstantPointerNull` can now represent fixed and scalable vector splats of
-  null pointers. Such constants may print as `splat (ptr null)` instead of
-  `zeroinitializer`.
-
-* LLVM IR floating-point literals have greatly changed:
-
-  * The old hexadecimal bitwise representation is deprecated and will be removed
-    in the next revision. It is replaced with a unified `f0x` prefix.
-
-  * Hexadecimal literals akin to C99's syntax are supported.
-
-  * Special values for infinities and NaNs, including NaN payloads, are added.
-
-* The standard textual output for floating-point literals is changed to take
-  advantage of the new floating-point literals formats.
-
-* The resume/destroy functions emitted for the switch-resume ABI (C++20
-  coroutines) now use `CallingConv::C` instead of `CallingConv::Fast`. This
-  stabilizes the coroutine ABI across LLVM versions and aligns it with other
-  vendors. The change is observationally identical on targets where `fastcc`
-  and `ccc` agree for `void(ptr)` (x86_64, AArch64, RISC-V, ...) but is an ABI
-  break on i686, MIPS O32, PowerPC64 ELFv1, and Lanai.
-
-* Assume bundles now only accept attributes that are actually handled.
-  Specifically, they are ``align``, ``cold``, ``dereferenceable``,
-  ``dereferenceable_or_null``, ``nonnull``, ``noundef`` and
-  ``separate_storage``.
-
-* Fast math flags are now permitted on `uitofp` and `sitofp`.
-
-* The ``modular-format`` attribute now supports the ``fixed`` aspect for C
-  ISO 18037 fixed-point ``printf`` specifiers.
-
-* Added `noipa` attribute which disables interprocedural analyses that inspect
-  the definition of the function. This attribute does *not* control inlining or
-  outlining. Add the `noinline` and `nooutline` attributes as well in cases
-  where inlining and outlining should additionally be disabled.
-
-* Added support for ``callgraph`` metadata. The `!callgraph` metadata
-  associates a function definition with its type identifier and is used for call
-  graph section generation. See the [callgraph Metadata](https://llvm.org/docs/LangRef.html#callgraph-metadata)
-  section of the LangRef for details.
-
-* The `dereferenceable` and `dereferenceable_or_null` attributes (and
-  corresponding metadata) now apply only at the point of definition, instead of
-  for the execution of the function (for arguments) or forever (for returns).
-
-* `alwaysinline` no longer bypasses inlining compatibility checks based on
-  target features. Inlining will only be performed if it is safe to do so.
-
-* Module-level inline assembly now accepts optional `target_features` and
-  `target_cpu` properties. This resolves errors during LTO on some
-  architectures.
-
 ### Changes to LLVM infrastructure
 
-* Removed ``Constant::isZeroValue``. It was functionally identical to
-  ``Constant::isNullValue`` for all types except floating-point negative
-  zero. All callers should use ``isNullValue`` instead. ``isZeroValue``
-  will be reintroduced in the future with bitwise-all-zeros semantics
-  to support non-zero null pointers.
-
-* Added support for specifying the null pointer bit representation per
-  address space in `DataLayout`. Pointer specifications (`p`) accept new
-  flags: `z` (null is all-zeros) and `o` (null is all-ones). Address
-  spaces without an explicit flag default to all-zeros. See the
-  `DataLayout` section of the
-  [LangRef](https://llvm.org/docs/LangRef.html#data-layout) for details.
-
-* Removed TypePromoteFloat legalization from SelectionDAG
-
-* Removed `bugpoint`. Usage has been replaced by `llvm-reduce` and
-  `llvm/utils/reduce_pipeline.py`.
-
-* The ``Br`` opcode was split into two opcodes separating unconditional
-  (``UncondBr``) and conditional (``CondBr``) branches.
-
-* ``BranchInst`` was deprecated in favor of ``UncondBrInst`` and ``CondBrInst``.
-
-* The operand order of ``CondBr`` instructions was adjusted to match the
-  successor order. This can cause subtle breakage when using ``getOperand`` or
-  ``setOperand`` to access successors.
-
-* The ``llvm::sys::fs`` link creation API has been refactored:
-
-  * ``create_link`` now tries to create a symbolic link first, falling back to a
-    hard link if that fails (previously it created a symlink on Unix and a hard
-    link on Windows).
-  * Added ``create_symlink``, which always creates a symbolic link. On windows
-    this may fail if symlink permissions are not available.
-  * Added ``readlink``, which reads the target of a symbolic link.
-
-* Bitcode libraries can now implement compiler-managed library functions
-  (libcalls) without causing incorrect API manipulation or undefined references
-  ([#177046](https://github.com/llvm/llvm-project/pull/125687)). Note that
-  there are still issues with invalid compiler reasoning about some functions
-  in bitcode, e.g. `malloc`. Not yet supported on MachO or when using
-  distributed ThinLTO.
-
-* ``ConstantFP`` now supports vector types and is the canonical form returned by
-  ``ConstantVector::getSplat(C)`` when ``C`` is a scalar ``ConstantFP``.
-
-* ``DenseMap``, ``DenseSet``, ``StringMap``, and ``StringSet`` ``erase`` now
-  invalidates all iterators and references into the container, not just the
-  iterator for the erased element. Use the new ``remove_if`` member to erase
-  matching elements in a single pass instead of erasing while iterating.
-
-* ``TargetRegisterInfo::getMinimalPhysRegClass`` and related APIs have been
-  refactored and no longer take a type. This API is also now precomputed in
-  TableGen to improve compile-time.
-
-* ``APInt::sqrt`` (square root rounded to nearest integer) has been replaced
-  with ``APInt::sqrtFloor`` (floor of square root).
-
 ### Changes to building LLVM
 
 ### Changes to TableGen
 
-* Outer let statements use ``ID{n-m}`` instead of ``ID<n-m>`` to be consistent
-  with inner let statements.
-
 ### Changes to Interprocedural Optimizations
 
 ### Changes to Vectorizers
 
 ### Changes to the AArch64 Backend
 
-* The `sysp`, `mrrs`, and `msrr` instructions are now accepted without
-  requiring the `+d128` feature gating.
-* Added a new internal option `-aarch64-emit-debug-tls-location` to allow the
-  emission of `DW_AT_location` for thread-local variables. This is currently
-  disabled by default to maintain compatibility with Binutils and LLVM older
-  toolchains that do not define the `R_AARCH64_TLS_DTPREL64` static relocation
-  type for TLS offsets.
-* A bug was fixed that caused LLVM IR inline assembly clobbers of the x29 and
-  x30 registers to be ignored when they were written using their xN names
-  instead of the ABI names FP and LR. Note that LLVM IR produced by Clang
-  always uses the ABI names, but other frontends may not.
-  ([#167783](https://github.com/llvm/llvm-project/pull/167783))
-
 ### Changes to the AMDGPU Backend
 
-* Initial support for gfx1310
-
-* The `"amdgpu-num-sgpr"` and `"amdgpu-num-vgpr"` IR function attributes
-  (generated by the Clang `amdgpu_num_sgpr` and `amdgpu_num_vgpr` attributes)
-  are now deprecated. Use `"amdgpu-waves-per-eu"` instead. The backend still
-  honors the attributes; Clang emits a `-Wdeprecated-declarations` warning when
-  the source attributes are used.
-* The `relaxed-buffer-oob-mode` subtarget feature has been replaced by two
-  module flags, `amdgpu.buffer.oob.mode` and `amdgpu.tbuffer.oob.mode`, which
-  control out-of-bounds semantics (see the AMDGPU User Guide). Frontends that
-  previously relied on the subtarget feature to enable misaligned buffer merging
-  must now set the corresponding module flag to `1` (relaxed). An absent flag is
-  treated as strict by the backend.
-
-* Changed triple naming system from "amdgcn" to "amdgpu" plus a subarch suffix.
-
 ### Changes to the ARM Backend
 
-* The `r14` register can now be used as an alias for the link register `lr`
-  in inline assembly. Clang always canonicalizes the name to `lr`, but other
-  frontends may not.
-* `subs pc, lr, #imm` can now be predicated in Thumb2.
-
 ### Changes to the AVR Backend
 
 ### Changes to the DirectX Backend
@@ -237,262 +78,38 @@ Makes programs 10x faster by doing Special New Thing.
 
 ### Changes to the LoongArch Backend
 
-* DWARF fission is now compatible with linker relaxations, allowing `-gsplit-dwarf` and `-mrelax`
-  to be used together when building for the LoongArch platform.
-
 ### Changes to the MIPS Backend
 
-### Changes to the NVPTX Backend
-
-* The default SM version has been changed from `sm_30` to `sm_75`. `sm_75` is
-  the oldest GPU variant compatible with the widest range of recent major CUDA
-  Toolkit versions (11/12/13).
-
 ### Changes to the PowerPC Backend
 
 ### Changes to the RISC-V Backend
 
-* `llvm-objdump` now has support for `--symbolize-operands` with RISC-V.
-* `-mcpu=spacemit-x100` was added.
-* Change P extension version to match the 0.21 draft specification.
-* Mnemonics for MOP/HINT-based instructions (`lpad`, `pause`, `ntl.*`, `c.ntl.*`,
-  `sspush`, `sspopchk`, `ssrdp`, `c.sspush`, `c.sspopchk`) are now always
-  available in the assembler and disassembler without requiring their respective
-  extensions.
-* Adds experimental assembler support for the 'Zvabd` (RISC-V Integer Vector
-  Absolute Difference) extension.
-* Adds CodeGen support for the 'Zvabd` extension.
-* `-mcpu=spacemit-a100` was added.
-* Adds assembler support for the `XSMTVDotII` (SpacemiT Vector Extension for Matrix 2.0) extension.
-* The opt-in `-riscv-enable-p-ext-simd-codegen` flag has been removed. P extension SIMD code generation is now enabled automatically if the P extension is supported.
-* `-mcpu=xt-c910v2` and `-mcpu=xt-c920v2` were added.
-* Adds experimental assembler support for the 'Zvzip` (RISC-V Vector
-  Reordering Structured Data) extension.
-* `-mcpu=sifive-x160` and `-mcpu=sifive-x180` were added.
-* Support for the experimental `XRivosVisni` vendor extension has been removed.
-* Support for the experimental `XRivosVizip` vendor extension has been removed.
-* Adds experimental assembler support for the 'Zvvmm` (RISC-V Integer Matrix Multiply-Accumulate) extension.
-* Adds experimental assembler support for the 'Zvvfmm` (RISC-V Floating-Point Matrix Multiply-Accumulate) extension.
-* Adds experimental assembler support for the 'Zvvmtls` and 'Zvvmttls` (RISC-V
-  Matrix Tile Load/Store) extensions.
-* Adds support for 'Ziccid' (Instruction/Data Coherence and Consistency) extension.
-* Adds experimental assembler support for the `Xqccmt` (Qualcomm 16-bit Table Jump) vendor extension.
-* `-mcpu=sifive-870` has been renamed `-mcpu=sifive-p870-d`.
-* Adds experimental assembler support for batched dot-product extensions(Zvqwbdota8i, Zvqwbdota16i, Zvfwbdota16bf, Zvfqwbdota8f and Zvfbdota32f).
-* Adds experimental assembler support for dot-product extensions(Zvqwdota8i, Zvqwdota16i, Zvfwdota16bf and Zvfqwdota8f).
-* `-mtune=generic` now uses the scheduling model from SpacemiT X60 instead of an empty scheduling model.
-* The Xqcilo pseudos now emit sequences that can be relaxed.
-
 ### Changes to the WebAssembly Backend
 
-* WebAssembly reference types are now represented in LLVM IR as the target
-  extension types `target("wasm.externref")` and `target("wasm.funcref")`,
-  rather than as pointers in address spaces 10 and 20 (`ptr addrspace(10)` /
-  `ptr addrspace(20)`).
-* As a consequence of the representation change, reference types are no longer
-  treated as vectorizable pointers. This fixes a crash in the SLP vectorizer,
-  which previously would attempt to gather `externref`/`funcref` values into a
-  vector and then crash.
-
 ### Changes to the Windows Target
 
-* The `.seh_startchained` and `.seh_endchained` assembly instructions have been removed and replaced
-  with a new `.seh_splitchained` instruction.
-
 ### Changes to the X86 Backend
 
-* `.att_syntax` directive is now emitted for assembly files when AT&T syntax is
-  in use. This matches the behaviour of Intel syntax and aids with
-  compatibility when changing the default Clang syntax to the Intel syntax.
-
-* EGPR (R16-R31) now requires V3 unwind info on Windows x64. Using EGPR
-  without V3 unwind produces a fatal error.
-* Implemented Win64 APX ABI callee-saved registers: R30 and R31 are now
-  treated as non-volatile in the Win64 calling convention when APX is
-  available, per the Microsoft x64 calling convention specification.
-
-* Functions using setjmp with Win64 APX ABI now reserve R30/R31 from
-  register allocation, as the unwinder cannot restore APX extended
-  registers across longjmp. A warning is emitted for large functions
-  where this reservation may impact performance.
-
-* Added ``.seh_push2regs`` assembly directive for explicitly encoding a
-  two-register push in Windows x64 V3 unwind info. The directive takes two
-  register operands: ``.seh_push2regs %r12, %r13``.
-
-* The `fp128` type is now returned via sret instead of XMM0 for some calling
-  conventions to match GCC. The C and Win64 calling conventions now use
-  sret, other calling conventions do not need to be compatible with
-  GCC and still return via XMM0.
-
 ### Changes to the OCaml bindings
 
 ### Changes to the Python bindings
 
 ### Changes to the C API
 
-* Replaced opcode ``LLVMBr`` with ``LLVMUncondBr`` and ``LLVMCondBr``.
-
-* The operand order of ``CondBr`` instructions was adjusted to match the
-  successor order. This can cause subtle breakage when using ``LLVMGetOperand``
-  or ``LLVMSetOperand`` to access successors.
-
 ### Changes to the CodeGen infrastructure
 
-* Renamed ISD::CTLZ_ZERO_UNDEF to ISD::CTLZ_ZERO_POISON opcode to make it clear that
-  a zero input results in poison.
-
-* Renamed ISD::CTTZ_ZERO_UNDEF to ISD::CTTZ_ZERO_POISON opcode to make it clear that
-  a zero input results in poison.
-
-* LLVM intrinsics can now declare required target features using the
-  ``TargetFeatures`` TableGen field. SelectionDAG and GlobalISel use this
-  metadata to reject unsupported target intrinsics generically, so targets do
-  not need custom lowering checks for each annotated intrinsic.
-
-### Changes to the GlobalISel infrastructure
-
-* Renamed G_CTLZ_ZERO_UNDEF to G_CTLZ_ZERO_POISON opcode to make it clear that
-  a zero input results in poison.
-
-* Renamed G_CTTZ_ZERO_UNDEF to G_CTTZ_ZERO_POISON opcode to make it clear that
-  a zero input results in poison.
-
-* GlobalISel's IRTranslator now supports the LLVM IR byte type (`bN`). Byte
-  values are translated as the equi-sized integer scalar (`sN`), `ConstantByte`
-  is materialised via `G_CONSTANT`, and `bitcast` between a byte type and a
-  pointer is lowered to `G_INTTOPTR` / `G_PTRTOINT` rather than the
-  verifier-illegal `G_BITCAST`.
-
 ### Changes to the Metadata Info
 
 ### Changes to the Debug Info
 
 ### Changes to the LLVM tools
 
-* llvm-ml now supports the `/unwindv3` flag to enable V3 unwind information
-  format for x64 exception handling.
-* llvm-ml now supports the `@UnwindVersion` built-in symbol, which returns the
-  current unwind version (1 by default, 3 when `/unwindv3` is specified).
-* llvm-ml now supports the `.push2reg`, `.pop2reg`, `.beginepilog`, and
-  `.endepilog` MASM directives for V3 unwind information.
-* llvm-ml now supports the `.popreg`, `.freestack`, `.restorereg`,
-  `.restorexmm128`, and `.unsetframe` MASM epilog directives. These are the
-  epilog counterparts of `.pushreg`, `.allocstack`, `.savereg`,
-  `.savexmm128`, and `.setframe` respectively, and are valid only inside
-  `.beginepilog`/`.endepilog` blocks and only for V3 unwind information.
-* llvm-ml now supports `.pushframe code` syntax (without the `@` prefix)
-  for interrupt handlers with error codes.
-* llvm-ml now diagnoses:
-  - Prolog directives (including `.allocstack`) used outside of prologs (after `.endprolog`).
-  - Epilog directives used outside of epilogs (outside of `.beginepilog` + `.endepilog` blocks).
-  - Beginning an epilog (`.beginepilog`) inside another epilog.
-
-* `llvm-profgen` now supports ETM trace decoding using the OpenCSD library for Cortex-M targets. OpenCSD version 1.5.4 or higher is required.
-
-* `llvm-objcopy` no longer corrupts the symbol table when `--update-section` is called for ELF files.
-* `llvm-objcopy` now reports an error when `--compress-sections` requests unavailable zlib or zstd support.
-  The diagnostic is emitted while parsing the option, matching `--compress-debug-sections`.
-  Such commands may now fail even if the input file contains no sections that would be compressed.
-* `FileCheck` option `-check-prefix` now accepts a comma-separated list of
-  prefixes, making it an alias of the existing `-check-prefixes` option.
-* Add `-mtune` option to `llc`.
-* Add `-mtune` option to `opt`.
-* Fixed `llvm-ar` to correctly handle the `N` count modifier on Windows for archive members whose names differ only
-  in case (e.g. `FOO.OBJ` and `foo.obj`). Previously, `-N 2` would fail with "not found" even when two matching members existed.
-* `llvm-readobj` and `llvm-readelf` now support the `--call-graph-section` option to dump the contents of the experimental [call graph section](CallGraphSection.md).
-* Added support for hybrid ARM64X object files to `llvm-ar` and `llvm-lib`. When these files are added to
-  an archive, they are automatically split into separate native and EC members. Because the resulting members
-  are no longer hybrid object files, consumers of these archives do not need to support the hybrid format themselves.
-
 ### Changes to LLDB
 
-* A new ``webinspector-wasm`` platform was added to list and attach to WebAssembly processes in Safari.
-* The default for `load-script-from-symbol-file` was changed from `warn` to `trusted`. This means that scripts from
-  code signed dSYM bundles are now loaded automatically, while untrusted bundles continue to produce a warning.
-* Pressing enter after `frame variable` repeats the command with an incremented `--depth` option, allowing quick
-  expansion of nested data.
-* Breakpoint commands now accept `.` to refer to the location(s) at which the current thread is stopped. For
-  example, `breakpoint disable .` disables the just-hit breakpoint location. Another usage is to automate a
-  command to run at the current location: `breakpoint command add -o 'p my_var' .`.
-* The `apropos` command now:
-  * Highlights matching keywords in its output when color is enabled.
-  * Searches the components of settings paths. For example `apropos qemu-user` will now
-    show `platform.plugin.qemu-user` as one of the results.
-* Reading global and static variables on WebAssembly targets now works correctly. Previously their
-  values could not be read because data sections were mapped to the wrong address space.
-* A new `diagnostics report` command (aliased `bugreport`) assembles a diagnostics bundle and files
-  a pre-filled GitHub issue, pointing at the bundle to attach. The GitHub reporter is built by
-  default and can be disabled with the `LLDB_ENABLE_GITHUB_BUG_REPORTER=OFF` CMake option.
-
-#### Deprecated APIs
-
-* ``SBTarget::GetDataByteSize()``, ``SBTarget::GetCodeByteSize()``, and ``SBSection::GetTargetByteSize()``
-  have been deprecated. They always return `1`, as before.
-
-#### FreeBSD
-
-##### Userspace Debugging
-
-* Support for MIPS64 has been removed.
-* The minimum assumed FreeBSD version is now 14. The effect of which is that watchpoints are
-  assumed to be supported.
-
-##### Kernel Debugging
-
-* The plugin that analyzes FreeBSD kernel core dump and live core has been renamed from `freebsd-kernel` to
- `freebsd-kernel-core`. Remote kernel debugging is still handled by the `gdb-remote` plugin.
-* Support for libfbsdvmcore has been removed. As a result, FreeBSD kernel dump debugging is now only
-  available on FreeBSD hosts. Live kernel debugging through the GDB remote protocol is still available
-  from any platform.
-* Support for ARM, PPC64le, and RISCV64 has been added.
-* The crashed thread is now automatically selected on start.
-* Threads are listed in incrmental order by pid then by tid.
-* Unread kernel messages saved in `msgbufp` are now printed when LLDB starts.
-* Writing to the core is now supported. For safety reasons, this feature is off by default. To enable it,
-  `plugin.process.freebsd-kernel-core.read-only` must be set to `false`. This setting is available when
-  using `/dev/mem` or a kernel dump. However, since `kvm_write()` does not support writing to kernel dumps,
-  writes to a kernel dump will still fail when the setting is false.
-* Added a command `process plugin refresh-threads`, enabling on-demand thread-list reconstruction from `/dev/mem`
-  so users can resync live kernel thread state without restarting LLDB. Note that this has no impact on full dump
-  and minidump files.
-
-#### Linux
-
-* On Arm Linux, the `tpidruro` register can now be read. Writing to this register is not supported.
-* Thread local variables are now supported on Arm and RISC-V Linux if the program being debugged is using glibc.
-* LLDB now supports AArch64 Linux systems that only have SME (as opposed to
-  SVE and SME). See the AArch64 Linux [documentation](https://lldb.llvm.org/use/aarch64-linux.html#sme-only-systems)
-  for more details.
-
-  Prior to this version of LLDB, there was a bug that caused LLDB to crash on
-  startup on these systems ([#138717](https://github.com/llvm/llvm-project/issues/138717)).
-  This affected LLDB versions from 18 up to and including 22. 17 and below are not affected.
-  If you are using such a system and cannot change LLDB version, or want to package
-  an affected version in a way that is compatible with these systems, the issue
-  contains details of backports that could be done to fix the affected versions.
-* When an ELF core file is loaded, LLDB now shows the command line that created the core file.
-  If you need to see it again, use the command `process status -v`.
-* LLDB now supports debugging Linux [Memory Protection Keys](https://docs.kernel.org/core-api/protection-keys.html)
-  on AArch64 systems that have the Permission Overlay Extension (POE / FEAT_S1POE).
-  See the [LLDB on AArch64 Linux](https://lldb.llvm.org/use/aarch64-linux.html#permission-overlay-extension-poe)
-  guide for more information.
-
-#### Windows
-
-* Python 3.11 or later is now recommended for building LLDB 23 on Windows. From LLDB 24, Python 3.11 or later will be required.
-* Messages from `OutputDebugString[A|W]` are now shown inline when using LLDB
-  from the command-line and in the output window when using lldb-dap.
-* LLDB now uses `lldb-server.exe` to launch and manage the program being debugged,
-  instead of running it within LLDB's own process. To revert to the previous behavior, set the environment variable `LLDB_USE_LLDB_SERVER=0`.
-
-
 ### Changes to BOLT
 
 ### Changes to Sanitizers
 
-* Add a random delay into ThreadSanitizer to help find rare thread interleavings.
-
 ### Other Changes
 
 ## External Open Source Projects Using LLVM {{env.config.release}}
diff --git a/llvm/utils/gn/secondary/llvm/version.gni b/llvm/utils/gn/secondary/llvm/version.gni
index d772c1ec3f0b8..5b4867355b18f 100644
--- a/llvm/utils/gn/secondary/llvm/version.gni
+++ b/llvm/utils/gn/secondary/llvm/version.gni
@@ -1,4 +1,4 @@
-llvm_version_major = 23
+llvm_version_major = 24
 llvm_version_minor = 0
 llvm_version_patch = 0
 llvm_version = "$llvm_version_major.$llvm_version_minor.$llvm_version_patch"
diff --git a/llvm/utils/lit/lit/__init__.py b/llvm/utils/lit/lit/__init__.py
index 8356da6ef9502..cd4338a9dbdcb 100644
--- a/llvm/utils/lit/lit/__init__.py
+++ b/llvm/utils/lit/lit/__init__.py
@@ -2,7 +2,7 @@
 
 __author__ = "Daniel Dunbar"
 __email__ = "daniel at minormatter.com"
-__versioninfo__ = (23, 0, 0)
+__versioninfo__ = (24, 0, 0)
 __version__ = ".".join(str(v) for v in __versioninfo__) + "dev"
 
 __all__ = []
diff --git a/llvm/utils/mlgo-utils/mlgo/__init__.py b/llvm/utils/mlgo-utils/mlgo/__init__.py
index 6ed46bc3aa56a..60fd600df3c79 100644
--- a/llvm/utils/mlgo-utils/mlgo/__init__.py
+++ b/llvm/utils/mlgo-utils/mlgo/__init__.py
@@ -4,7 +4,7 @@
 
 from datetime import timezone, datetime
 
-__versioninfo__ = (23, 0, 0)
+__versioninfo__ = (24, 0, 0)
 __version__ = (
     ".".join(str(v) for v in __versioninfo__)
     + "dev"


From libcxx-commits at lists.llvm.org  Tue Jul 14 06:21:12 2026
From: libcxx-commits at lists.llvm.org (Douglas Yung via libcxx-commits)
Date: Tue, 14 Jul 2026 06:21:12 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209473@github.com>
Message-ID: <6a5637c8.6b931ad5.1519bc.55f3@mx.google.com>


https://github.com/dyung closed https://github.com/llvm/llvm-project/pull/209473

From libcxx-commits at lists.llvm.org  Tue Jul 14 07:01:08 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 07:01:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Remove unused <cassert>
 from algorithm benchmarks (PR #209492)
Message-ID: <llvm.org/llvm/llvm-project/pull/209492@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/209492

A few benchmark files were including <cassert> but didn't use it.

>From 8ce4607481f0877a49d0192dcb3c4853e082942d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 14 Jul 2026 09:56:32 -0400
Subject: [PATCH] [libc++][NFC] Remove unused <cassert> from algorithm
 benchmarks

A few benchmark files were including <cassert> but didn't use it.
---
 libcxx/test/benchmarks/algorithms/min.bench.cpp                  | 1 -
 libcxx/test/benchmarks/algorithms/minmax.bench.cpp               | 1 -
 .../test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp   | 1 -
 3 files changed, 3 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/min.bench.cpp b/libcxx/test/benchmarks/algorithms/min.bench.cpp
index 72088557784e9..f89670b15142f 100644
--- a/libcxx/test/benchmarks/algorithms/min.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/min.bench.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cassert>
 #include <deque>
 #include <list>
 #include <vector>
diff --git a/libcxx/test/benchmarks/algorithms/minmax.bench.cpp b/libcxx/test/benchmarks/algorithms/minmax.bench.cpp
index 299b71837fb27..abec6429febbc 100644
--- a/libcxx/test/benchmarks/algorithms/minmax.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/minmax.bench.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cassert>
 #include <deque>
 #include <list>
 #include <vector>
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
index 212aabfd7cdb3..2eafebb993f12 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cassert>
 #include <cstddef>
 #include <deque>
 #include <list>


From libcxx-commits at lists.llvm.org  Tue Jul 14 07:10:38 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Tue, 14 Jul 2026 07:10:38 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209473@github.com>
Message-ID: <6a56435e.6c5720b3.15e90c.4335@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `llvm-clang-x86_64-gcc-ubuntu` running on `sie-linux-worker3` while building `clang-tools-extra,clang,cmake,flang,libcxx,lld,llvm` at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/174/builds/38194

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SystemZ/zos-ppa2.ll' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
sed -e 's/!"MODE"/!"ascii"/' -e 's/BYTE/85/' /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll > /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: sed -e 's/!"MODE"/!"ascii"/' -e s/BYTE/85/ /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
# note: command had no output on stdout or stderr
# RUN: at line 2
/home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true < /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll |  /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/bin/FileCheck /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true
# note: command had no output on stdout or stderr
# executed command: /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/bin/FileCheck /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# .---command stderr------------
# | [1m/home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll:24:10: [0m[0;1;31merror: [0m[1mCHECK: expected string not found in input
[0m# | [1m[0m; CHECK: DC XL6'F2F3F0F0F0F0'
# | [0;1;32m         ^
[0m# | [0;1;32m[0m[1m<stdin>:17:39: [0m[0;1;30mnote: [0m[1mscanning from here
[0m# | [1m[0m DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0' 
# | [0;1;32m                                      ^
[0m# | [0;1;32m[0m[1m<stdin>:18:2: [0m[0;1;30mnote: [0m[1mpossible intended match here
[0m# | [1m[0m DC XL6'F2F4F0F0F0F0' 
# | [0;1;32m ^
[0m# | [0;1;32m[0m
# | Input file: <stdin>
# | Check file: /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# | [1m[0m[0;1;90m            1: [0m[1m[0;1;46mstdin#C CSECT  [0m
# | [0;1;90m            2: [0m[1m[0;1;46m[0mC_CODE64 CATTR[0;1;46m ALIGN(3),FILL(0),READONLY,RMODE(64)  [0m
# | [0;1;32mcheck:9        ^~~~~~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m            3: [0m[1m[0;1;46mstdin#C XATTR LINKAGE(XPLINK),PSECT(stdin#S),SCOPE(SECTION)  [0m
# | [0;1;90m            4: [0m[1m[0;1;46m[0mL#PPA2 DS 0H[0;1;46m  [0m
# | [0;1;32mcheck:10       ^~~~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m            5: [0m[1m[0;1;46m [0mDC XL1'03'[0;1;46m  [0m
# | [0;1;32mcheck:11        ^~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m            6: [0m[1m[0;1;46m [0mDC XL1'E7'[0;1;46m  [0m
# | [0;1;32mcheck:12        ^~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m            7: [0m[1m[0;1;46m [0mDC XL1'22'[0;1;46m  [0m
# | [0;1;32mcheck:13        ^~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m            8: [0m[1m[0;1;46m [0mDC XL1'04'[0;1;46m  [0m
# | [0;1;32mcheck:14        ^~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m            9: [0m[1m[0;1;46m [0mDC AD(CELQSTRT-L#PPA2)[0;1;46m  [0m
# | [0;1;32mcheck:15        ^~~~~~~~~~~~~~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m           10: [0m[1m[0;1;46m [0mDC XL4'00000000'[0;1;46m  [0m
# | [0;1;32mcheck:16        ^~~~~~~~~~~~~~~~
...

```

</details>

https://github.com/llvm/llvm-project/pull/209473

From libcxx-commits at lists.llvm.org  Tue Jul 14 07:13:26 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 07:13:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Remove unused <cassert>
 from algorithm benchmarks (PR #209492)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209492@github.com>
Message-ID: <6a564406.0c05e68e.1e82f1.8a96@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/209492

From libcxx-commits at lists.llvm.org  Tue Jul 14 07:23:34 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Tue, 14 Jul 2026 07:23:34 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209473@github.com>
Message-ID: <6a564666.d681cab9.333b2b.40e7@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `clang-debian-cpp20` running on `clang-debian-cpp20` while building `clang-tools-extra,clang,cmake,flang,libcxx,lld,llvm` at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/108/builds/28000

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SystemZ/zos-ppa2.ll' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
sed -e 's/!"MODE"/!"ascii"/' -e 's/BYTE/85/' /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll > /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: sed -e 's/!"MODE"/!"ascii"/' -e s/BYTE/85/ /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
# RUN: at line 2
/vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true < /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll |  /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/FileCheck /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true
# executed command: /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/FileCheck /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# .---command stderr------------
# | /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll:24:10: error: CHECK: expected string not found in input
# | ; CHECK: DC XL6'F2F3F0F0F0F0'
# |          ^
# | <stdin>:17:39: note: scanning from here
# |  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0' 
# |                                       ^
# | <stdin>:18:2: note: possible intended match here
# |  DC XL6'F2F4F0F0F0F0' 
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             .
# |             .
# |             .
# |            12:  DC XL4'00000000'  
# |            13:  DC XL1'85'  
# |            14:  DC XL1'00'  
# |            15:  DC XL2'0000'  
# |            16: L#DVS DS 0H  
# |            17:  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0'  
# | check:24'0                                          {    search range start (exclusive)
# | check:24'1                                               error: no match found in search range
# |            18:  DC XL6'F2F4F0F0F0F0'  
# | check:24'2      ?                       possible intended match
# |            19:  DC XL2'0000'  
# |            20: stdin#C CSECT  
# |            21: C_@@QPPA2 CATTR ALIGN(3),FILL(0),NOTEXECUTABLE,READONLY,RMODE(64),PART(.  
# |            22:  .&ppa2)  
# |            23: .&ppa2 XATTR LINKAGE(OS),REFERENCE(DATA),SCOPE(SECTION)  
# |             .
# |             .
...

```

</details>

https://github.com/llvm/llvm-project/pull/209473

From libcxx-commits at lists.llvm.org  Tue Jul 14 07:26:12 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Tue, 14 Jul 2026 07:26:12 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209473@github.com>
Message-ID: <6a564704.c574b9b4.21ead0.a717@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `llvm-clang-x86_64-gcc-ubuntu-no-asserts` running on `doug-worker-6` while building `clang-tools-extra,clang,cmake,flang,libcxx,lld,llvm` at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/202/builds/474

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SystemZ/zos-ppa2.ll' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
sed -e 's/!"MODE"/!"ascii"/' -e 's/BYTE/85/' /home/buildbot/buildbot-root/gcc-no-asserts/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll > /home/buildbot/buildbot-root/gcc-no-asserts/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: sed -e 's/!"MODE"/!"ascii"/' -e s/BYTE/85/ /home/buildbot/buildbot-root/gcc-no-asserts/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
# note: command had no output on stdout or stderr
# RUN: at line 2
/home/buildbot/buildbot-root/gcc-no-asserts/build/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true < /home/buildbot/buildbot-root/gcc-no-asserts/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll |  /home/buildbot/buildbot-root/gcc-no-asserts/build/bin/FileCheck /home/buildbot/buildbot-root/gcc-no-asserts/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: /home/buildbot/buildbot-root/gcc-no-asserts/build/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true
# note: command had no output on stdout or stderr
# executed command: /home/buildbot/buildbot-root/gcc-no-asserts/build/bin/FileCheck /home/buildbot/buildbot-root/gcc-no-asserts/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# .---command stderr------------
# | [1m/home/buildbot/buildbot-root/gcc-no-asserts/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll:24:10: [0m[0;1;31merror: [0m[1mCHECK: expected string not found in input
[0m# | [1m[0m; CHECK: DC XL6'F2F3F0F0F0F0'
# | [0;1;32m         ^
[0m# | [0;1;32m[0m[1m<stdin>:17:39: [0m[0;1;30mnote: [0m[1mscanning from here
[0m# | [1m[0m DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0' 
# | [0;1;32m                                      ^
[0m# | [0;1;32m[0m[1m<stdin>:18:2: [0m[0;1;30mnote: [0m[1mpossible intended match here
[0m# | [1m[0m DC XL6'F2F4F0F0F0F0' 
# | [0;1;32m ^
[0m# | [0;1;32m[0m
# | Input file: <stdin>
# | Check file: /home/buildbot/buildbot-root/gcc-no-asserts/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# | [1m[0m[0;1;90m            1: [0m[1m[0;1;46mstdin#C CSECT  [0m
# | [0;1;90m            2: [0m[1m[0;1;46m[0mC_CODE64 CATTR[0;1;46m ALIGN(3),FILL(0),READONLY,RMODE(64)  [0m
# | [0;1;32mcheck:9        ^~~~~~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m            3: [0m[1m[0;1;46mstdin#C XATTR LINKAGE(XPLINK),PSECT(stdin#S),SCOPE(SECTION)  [0m
# | [0;1;90m            4: [0m[1m[0;1;46m[0mL#PPA2 DS 0H[0;1;46m  [0m
# | [0;1;32mcheck:10       ^~~~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m            5: [0m[1m[0;1;46m [0mDC XL1'03'[0;1;46m  [0m
# | [0;1;32mcheck:11        ^~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m            6: [0m[1m[0;1;46m [0mDC XL1'E7'[0;1;46m  [0m
# | [0;1;32mcheck:12        ^~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m            7: [0m[1m[0;1;46m [0mDC XL1'22'[0;1;46m  [0m
# | [0;1;32mcheck:13        ^~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m            8: [0m[1m[0;1;46m [0mDC XL1'04'[0;1;46m  [0m
# | [0;1;32mcheck:14        ^~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m            9: [0m[1m[0;1;46m [0mDC AD(CELQSTRT-L#PPA2)[0;1;46m  [0m
# | [0;1;32mcheck:15        ^~~~~~~~~~~~~~~~~~~~~~
[0m# | [0;1;32m[0m[0;1;90m           10: [0m[1m[0;1;46m [0mDC XL4'00000000'[0;1;46m  [0m
# | [0;1;32mcheck:16        ^~~~~~~~~~~~~~~~
...

```

</details>

https://github.com/llvm/llvm-project/pull/209473

From libcxx-commits at lists.llvm.org  Tue Jul 14 07:27:49 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 07:27:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a564765.f06f4994.1fc857.5f7f@mx.google.com>


================

----------------
ldionne wrote:

This benchmark says it inserts into an empty container, but in reality the container is reserved after the first iteration. So either we should reserve explicitly (like we do elsewhere), or we should truly start from an empty and non-reserved container every iteration. Doable in another PR.

https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Tue Jul 14 07:27:49 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 07:27:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a564765.1905e68e.18ad68.ad24@mx.google.com>


================

----------------
ldionne wrote:

Can you check if using `BatchSize` vectors like we do in other places actually solves the problem? If so, then we can solve the problem while staying consistent with the other benchmarks.

Otherwise, I would suggest we pause/resume only when the elements in the vector are non-trivial (e.g. `!std::is_trivially_destructible`).

https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Tue Jul 14 07:28:35 2026
From: libcxx-commits at lists.llvm.org (Hubert Tong via libcxx-commits)
Date: Tue, 14 Jul 2026 07:28:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][utils] Fix LIT `%if`
	substitution support (PR #209358)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209358@github.com>
Message-ID: <6a564793.449bfb44.172ed9.c6cf@mx.google.com>


================
@@ -0,0 +1,11 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that LIT `%if` substitutions are aware of the available features.
+
+// RUN: not %if libcpp-abi-version={{[0-9]+}} %{ not %} echo "hello world"
----------------
hubert-reinterpretcast wrote:

```suggestion
// RUN: not %if target={{.+}} %{ not %} echo "hello world"
```

https://github.com/llvm/llvm-project/pull/209358

From libcxx-commits at lists.llvm.org  Tue Jul 14 07:31:09 2026
From: libcxx-commits at lists.llvm.org (Hubert Tong via libcxx-commits)
Date: Tue, 14 Jul 2026 07:31:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][utils] Fix LIT `%if`
	substitution support (PR #209358)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209358@github.com>
Message-ID: <6a56482d.a1accbb4.244acd.2ffb@mx.google.com>


https://github.com/hubert-reinterpretcast updated https://github.com/llvm/llvm-project/pull/209358

>From a3ceea18d8a3d756c8b6ba795dcdface9552b074 Mon Sep 17 00:00:00 2001
From: Hubert Tong <hubert.reinterpretcast at gmail.com>
Date: Mon, 13 Jul 2026 22:40:37 -0400
Subject: [PATCH 1/4] [libcxx][utils] Fix `%if` substitution

---
 libcxx/utils/libcxx/test/format.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/utils/libcxx/test/format.py b/libcxx/utils/libcxx/test/format.py
index 49cbe8a8db618..b2471cb36ed61 100644
--- a/libcxx/utils/libcxx/test/format.py
+++ b/libcxx/utils/libcxx/test/format.py
@@ -205,8 +205,9 @@ def parseScript(test, preamble):
         )
 
     # Perform substitutions in the script itself.
+    conditions = {feature: True for feature in test.config.available_features}
     script = lit.TestRunner.applySubstitutions(
-        script, substitutions, recursion_limit=test.config.recursiveExpansionLimit
+        script, substitutions, conditions, recursion_limit=test.config.recursiveExpansionLimit
     )
 
     return script

>From d6bf3d942b1f9fc328a14ee482836a3b6a6f1b9b Mon Sep 17 00:00:00 2001
From: Hubert Tong <hubert.reinterpretcast at gmail.com>
Date: Mon, 13 Jul 2026 23:11:36 -0400
Subject: [PATCH 2/4] Add LIT test

---
 libcxx/test/selftest/if-else.sh.cpp | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 libcxx/test/selftest/if-else.sh.cpp

diff --git a/libcxx/test/selftest/if-else.sh.cpp b/libcxx/test/selftest/if-else.sh.cpp
new file mode 100644
index 0000000000000..11812d949a058
--- /dev/null
+++ b/libcxx/test/selftest/if-else.sh.cpp
@@ -0,0 +1,11 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that LIT `%if` substitutions are aware of the available features.
+
+// RUN: not %if libcpp-abi-version={{[0-9]+}} %{ not %} echo "hello world"

>From 38cf7241a04f9158341830e1a878f66455fa0a9c Mon Sep 17 00:00:00 2001
From: Hubert Tong <hubert.reinterpretcast at gmail.com>
Date: Mon, 13 Jul 2026 23:36:31 -0400
Subject: [PATCH 3/4] Apply diff from `darker`

---
 libcxx/utils/libcxx/test/format.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libcxx/utils/libcxx/test/format.py b/libcxx/utils/libcxx/test/format.py
index b2471cb36ed61..e3e81fd1cf36f 100644
--- a/libcxx/utils/libcxx/test/format.py
+++ b/libcxx/utils/libcxx/test/format.py
@@ -207,7 +207,10 @@ def parseScript(test, preamble):
     # Perform substitutions in the script itself.
     conditions = {feature: True for feature in test.config.available_features}
     script = lit.TestRunner.applySubstitutions(
-        script, substitutions, conditions, recursion_limit=test.config.recursiveExpansionLimit
+        script,
+        substitutions,
+        conditions,
+        recursion_limit=test.config.recursiveExpansionLimit,
     )
 
     return script

>From 4a06d1d145b95624356fc376b0daf7f00943cb0e Mon Sep 17 00:00:00 2001
From: Hubert Tong <hubert.reinterpretcast at gmail.com>
Date: Tue, 14 Jul 2026 10:30:59 -0400
Subject: [PATCH 4/4] Use `target={{.+}}` feature instead for the test

---
 libcxx/test/selftest/if-else.sh.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/selftest/if-else.sh.cpp b/libcxx/test/selftest/if-else.sh.cpp
index 11812d949a058..06b09ac9067a5 100644
--- a/libcxx/test/selftest/if-else.sh.cpp
+++ b/libcxx/test/selftest/if-else.sh.cpp
@@ -8,4 +8,4 @@
 
 // Make sure that LIT `%if` substitutions are aware of the available features.
 
-// RUN: not %if libcpp-abi-version={{[0-9]+}} %{ not %} echo "hello world"
+// RUN: not %if target={{.+}} %{ not %} echo "hello world"


From libcxx-commits at lists.llvm.org  Tue Jul 14 07:34:33 2026
From: libcxx-commits at lists.llvm.org (David Spickett via libcxx-commits)
Date: Tue, 14 Jul 2026 07:34:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Inline std::function
 members into the class body (PR #209461)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209461@github.com>
Message-ID: <6a5648f9.e55dc341.1f19bd.52b5@mx.google.com>


DavidSpickett wrote:

There is an lldb test failing in GitHub CI since this change:
```
Script:
--
/usr/bin/python3 /__w/llvm-project/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/__w/llvm-project/llvm-project/build/./lib --env LLVM_INCLUDE_DIR=/__w/llvm-project/llvm-project/build/include --env LLVM_TOOLS_DIR=/__w/llvm-project/llvm-project/build/./bin --libcxx-include-dir /__w/llvm-project/llvm-project/build/include/c++/v1 --libcxx-include-target-dir /__w/llvm-project/llvm-project/build/include/aarch64-unknown-linux-gnu/c++/v1 --libcxx-library-dir /__w/llvm-project/llvm-project/build/./lib/aarch64-unknown-linux-gnu --triple aarch64-unknown-linux-gnu --build-dir /__w/llvm-project/llvm-project/build/lldb-test-build --lldb-module-cache-dir /__w/llvm-project/llvm-project/build/lldb-test-build/module-cache-lldb/lldb-api --clang-module-cache-dir /__w/llvm-project/llvm-project/build/lldb-test-build/module-cache-clang/lldb-api --executable /__w/llvm-project/llvm-project/build/./bin/lldb --lldb-python-dir /__w/llvm-project/llvm-project/build/local/lib/python3.12/dist-packages --compiler /__w/llvm-project/llvm-project/build/./bin/clang --dsymutil /__w/llvm-project/llvm-project/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /__w/llvm-project/llvm-project/build/./bin --lldb-obj-root /__w/llvm-project/llvm-project/build/tools/lldb --lldb-libs-dir /__w/llvm-project/llvm-project/build/./lib --cmake-build-type Release /__w/llvm-project/llvm-project/lldb/test/API/lang/cpp/std-function-step-into-callable -p TestStdFunctionStepIntoCallable.py
--
Exit Code: 1

Command Output (stdout):
--
Skipping the following test categories: msvcstl, dsym, pdb, gmodules, debugserver, objc

--
Command Output (stderr):
--
FAIL: LLDB (/__w/llvm-project/llvm-project/build/bin/clang-aarch64) :: test (TestStdFunctionStepIntoCallable.LibCxxFunctionSteppingIntoCallableTestCase.test)
Log Files:
 - /__w/llvm-project/llvm-project/build/lldb-test-build/lang/cpp/std-function-step-into-callable/TestStdFunctionStepIntoCallable/Failure_test.log
======================================================================
FAIL: test (TestStdFunctionStepIntoCallable.LibCxxFunctionSteppingIntoCallableTestCase.test)
   Test that std::function as defined by libc++ is correctly printed by LLDB
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/__w/llvm-project/llvm-project/lldb/test/API/lang/cpp/std-function-step-into-callable/TestStdFunctionStepIntoCallable.py", line 54, in test
    self.assertEqual(
AssertionError: 32 != 4
Config=aarch64-/__w/llvm-project/llvm-project/build/bin/clang
----------------------------------------------------------------------
Ran 1 test in 0.293s

FAILED (failures=1)

--
```
We expect to see that we're inside of a function, but we're still in main where the function is being called. Not sure what is wrong yet, mostly likely an lldb assumption at fault.

https://github.com/llvm/llvm-project/pull/209461

From libcxx-commits at lists.llvm.org  Tue Jul 14 07:51:48 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 07:51:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a564d04.449bfb44.172ed9.2b68@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/209141

From libcxx-commits at lists.llvm.org  Tue Jul 14 07:51:48 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 07:51:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a564d04.8e75e0cb.3dbce4.be94@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM with minor comments

https://github.com/llvm/llvm-project/pull/209141

From libcxx-commits at lists.llvm.org  Tue Jul 14 07:51:48 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 07:51:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a564d04.492e23e4.14eda0.c8a9@mx.google.com>


================
@@ -12,37 +12,41 @@
 #include <__algorithm/comp.h>
 #include <__algorithm/comp_ref_type.h>
 #include <__config>
-#include <__iterator/iterator_traits.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Compare, class _RandomAccessIterator>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandomAccessIterator
-__is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp) {
-  typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
-  difference_type __len      = __last - __first;
-  difference_type __p        = 0;
-  difference_type __c        = 1;
-  _RandomAccessIterator __pp = __first;
-  while (__c < __len) {
-    _RandomAccessIterator __cp = __first + __c;
-    if (__comp(*__pp, *__cp))
-      return __cp;
-    ++__c;
-    ++__cp;
-    if (__c == __len)
-      return __last;
-    if (__comp(*__pp, *__cp))
-      return __cp;
-    ++__p;
-    ++__pp;
-    __c = 2 * __p + 1;
+template <class _Compare, class _Iter>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter
+__is_heap_until(_Iter __first, _Iter __last, _Compare&& __comp) {
+  auto __count = __last - __first;
+
+  if (__count < 2)
+    return __last;
+
+  auto __parent_last = __first + (__count - 1) / 2;
----------------
ldionne wrote:

This needs a comment to explain exactly what it's pointing to. IIUC, something like:

```
// This points to one past the last parent that has two children.
// Integer division handles the case where the last parent has a single child.
```

https://github.com/llvm/llvm-project/pull/209141

From libcxx-commits at lists.llvm.org  Tue Jul 14 07:51:48 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 07:51:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a564d04.4a342cbc.ca844.3dbd@mx.google.com>


================
@@ -12,37 +12,41 @@
 #include <__algorithm/comp.h>
 #include <__algorithm/comp_ref_type.h>
 #include <__config>
-#include <__iterator/iterator_traits.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Compare, class _RandomAccessIterator>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandomAccessIterator
-__is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp) {
-  typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
-  difference_type __len      = __last - __first;
-  difference_type __p        = 0;
-  difference_type __c        = 1;
-  _RandomAccessIterator __pp = __first;
-  while (__c < __len) {
-    _RandomAccessIterator __cp = __first + __c;
-    if (__comp(*__pp, *__cp))
-      return __cp;
-    ++__c;
-    ++__cp;
-    if (__c == __len)
-      return __last;
-    if (__comp(*__pp, *__cp))
-      return __cp;
-    ++__p;
-    ++__pp;
-    __c = 2 * __p + 1;
+template <class _Compare, class _Iter>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter
+__is_heap_until(_Iter __first, _Iter __last, _Compare&& __comp) {
+  auto __count = __last - __first;
+
+  if (__count < 2)
+    return __last;
+
+  auto __parent_last = __first + (__count - 1) / 2;
+  auto __child = __first;
+  ++__child;
+  for (; __first != __parent_last; ++__first) {
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
+
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
   }
-  return __last;
+
+  if (__count % 2 == 0) {
----------------
ldionne wrote:

```suggestion
  // If the heap is even-sized, the last parent has a single child, handled here.
  if (__count % 2 == 0) {
```

https://github.com/llvm/llvm-project/pull/209141

From libcxx-commits at lists.llvm.org  Tue Jul 14 08:48:02 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 08:48:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Create libc++ 24 release notes
	(PR #208814)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208814@github.com>
Message-ID: <6a565a32.eb165e5c.1817a5.5ff0@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/208814

>From d944ff730edf9c681ae64dad1ec59d07d55812ab Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 10 Jul 2026 15:01:08 -0400
Subject: [PATCH 1/3] [libc++] Create libc++ 24 release notes

While the release branch has not been cut yet, having the release notes
for LLVM 24 simplifies the creation of PRs that target LLVM 24 immediately,
with the intent of merging them only after the LLVM 23 release branch is cut.

The release notes contain a few TODOs which will be easy to address once
the LLVM 23 release branch has been cut.
---
 libcxx/docs/ReleaseNotes.rst    |  2 +
 libcxx/docs/ReleaseNotes/24.rst | 74 +++++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+)
 create mode 100644 libcxx/docs/ReleaseNotes/24.rst

diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index ff35abef61203..14d5f2ccfb71d 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -7,6 +7,8 @@
     ReleaseNotes/20
     ReleaseNotes/21
     ReleaseNotes/22
+    ReleaseNotes/24
+    .. TODO: Switch to the LLVM 24 release notes once the LLVM 23 branch has been cut
 
 .. The release notes are in versioned files, but we make sure to keep publishing
 .. them in an unversioned ReleaseNotes.html page for external sites to reference.
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
new file mode 100644
index 0000000000000..f6e7b7cb5f59a
--- /dev/null
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -0,0 +1,74 @@
+===========================================
+Libc++ 24.0.0 (In-Progress) Release Notes
+===========================================
+
+.. contents::
+   :local:
+   :depth: 2
+
+Written by the `Libc++ Team <https://libcxx.llvm.org>`_
+
+.. warning::
+
+   These are in-progress notes for the upcoming libc++ 24.0.0 release.
+   Release notes for previous releases can be found on
+   `the Download Page <https://releases.llvm.org/download.html>`_.
+
+Introduction
+============
+
+This document contains the release notes for the libc++ C++ Standard Library,
+part of the LLVM Compiler Infrastructure, release 24.0.0. Here we describe the
+status of libc++ in some detail, including major improvements from the previous
+release and new feature work. For the general LLVM release notes, see `the LLVM
+documentation <https://llvm.org/docs/ReleaseNotes.html>`_. All LLVM releases may
+be downloaded from the `LLVM releases web site <https://llvm.org/releases/>`_.
+
+For more information about libc++, please see the `Libc++ Web Site
+<https://libcxx.llvm.org>`_ or the `LLVM Web Site <https://llvm.org>`_.
+
+Note that if you are reading this file from a Git checkout or the
+main Libc++ web page, this document applies to the *next* release, not
+the current one. To see the release notes for a specific release, please
+see the `releases page <https://llvm.org/releases/>`_.
+
+What's New in Libc++ 24.0.0?
+==============================
+
+Implemented Papers
+------------------
+
+
+Improvements and New Features
+-----------------------------
+
+
+Deprecations and Removals
+-------------------------
+
+- The ``std::launch::any`` enumerator that was accidentally provided as an extension is now deprecated.
+  It will be removed in LLVM 25.
+
+Potentially breaking changes
+----------------------------
+
+- TODO: In LLVM 23, libc++ dropped a lot of transitive includes in all language modes. This improves compile time significantly,
+  but causes programs which rely on these includes to not compile anymore. The ``_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23``
+  macro that was provided in LLVM 23 to ease the transition has been removed in this release.
+
+Announcements About Future Releases
+-----------------------------------
+
+
+ABI Affecting Changes
+---------------------
+
+- TODO: ``std::allocator`` is trivially default constructible since LLVM 22. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR``
+  macro was provided as an escape hatch, but it has been removed in LLVM 24 since there was no evidence of it being useful.
+
+- TODO: ``bitset::operator[]`` returns ``bool`` since LLVM 22, fixing a conformance bug. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``
+  macro was provided as an escape hatch, but it has been removed in LLVM 24 since there was no evidence of it being useful.
+
+Build System Changes
+--------------------
+

>From 7f3cd67b41c7e1c53fede5ba075fdb0b2e0c1c89 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 13 Jul 2026 12:12:12 -0400
Subject: [PATCH 2/3] Remove unnecessary TODO comment

---
 libcxx/docs/ReleaseNotes.rst | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index 14d5f2ccfb71d..3aa58c55a35da 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -8,7 +8,6 @@
     ReleaseNotes/21
     ReleaseNotes/22
     ReleaseNotes/24
-    .. TODO: Switch to the LLVM 24 release notes once the LLVM 23 branch has been cut
 
 .. The release notes are in versioned files, but we make sure to keep publishing
 .. them in an unversioned ReleaseNotes.html page for external sites to reference.

>From 88b4b344799a94a979aca9922927ae69ed293e5c Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 14 Jul 2026 11:47:48 -0400
Subject: [PATCH 3/3] Update

---
 libcxx/docs/ReleaseNotes.rst    | 4 ++--
 libcxx/docs/ReleaseNotes/24.rst | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index 3aa58c55a35da..37078d83139ab 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -1,4 +1,4 @@
-.. include:: ReleaseNotes/23.rst
+.. include:: ReleaseNotes/24.rst
 
 .. Make sure to reference the non-live release notes in a toctree to avoid Sphinx errors.
 .. toctree::
@@ -7,7 +7,7 @@
     ReleaseNotes/20
     ReleaseNotes/21
     ReleaseNotes/22
-    ReleaseNotes/24
+    ReleaseNotes/23
 
 .. The release notes are in versioned files, but we make sure to keep publishing
 .. them in an unversioned ReleaseNotes.html page for external sites to reference.
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index f6e7b7cb5f59a..bcac37b8a717f 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -1,6 +1,6 @@
-===========================================
-Libc++ 24.0.0 (In-Progress) Release Notes
-===========================================
+===========================
+Libc++ 24.0.0 Release Notes
+===========================
 
 .. contents::
    :local:


From libcxx-commits at lists.llvm.org  Tue Jul 14 08:48:57 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 08:48:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Create libc++ 24 release notes
	(PR #208814)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208814@github.com>
Message-ID: <6a565a69.481afadf.196d57.4e35@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/208814

From libcxx-commits at lists.llvm.org  Tue Jul 14 08:49:07 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 14 Jul 2026 08:49:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Create libc++ 24 release notes
	(PR #208814)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208814@github.com>
Message-ID: <6a565a73.0c05e68e.1e82f1.9649@mx.google.com>


ldionne wrote:

Updated now that the LLVM 23 release branch has been cut.

https://github.com/llvm/llvm-project/pull/208814

From libcxx-commits at lists.llvm.org  Tue Jul 14 08:53:54 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 08:53:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Remove unused <cassert>
 from algorithm benchmarks (PR #209492)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209492@github.com>
Message-ID: <6a565b92.e55dc341.1f19bd.6e05@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

A few benchmark files were including &lt;cassert&gt; but didn't use it.

---
Full diff: https://github.com/llvm/llvm-project/pull/209492.diff


3 Files Affected:

- (modified) libcxx/test/benchmarks/algorithms/min.bench.cpp (-1) 
- (modified) libcxx/test/benchmarks/algorithms/minmax.bench.cpp (-1) 
- (modified) libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp (-1) 


``````````diff
diff --git a/libcxx/test/benchmarks/algorithms/min.bench.cpp b/libcxx/test/benchmarks/algorithms/min.bench.cpp
index 72088557784e9..f89670b15142f 100644
--- a/libcxx/test/benchmarks/algorithms/min.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/min.bench.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cassert>
 #include <deque>
 #include <list>
 #include <vector>
diff --git a/libcxx/test/benchmarks/algorithms/minmax.bench.cpp b/libcxx/test/benchmarks/algorithms/minmax.bench.cpp
index 299b71837fb27..abec6429febbc 100644
--- a/libcxx/test/benchmarks/algorithms/minmax.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/minmax.bench.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cassert>
 #include <deque>
 #include <list>
 #include <vector>
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
index 212aabfd7cdb3..2eafebb993f12 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cassert>
 #include <cstddef>
 #include <deque>
 #include <list>

``````````

</details>


https://github.com/llvm/llvm-project/pull/209492

From libcxx-commits at lists.llvm.org  Tue Jul 14 09:56:50 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Tue, 14 Jul 2026 09:56:50 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209473@github.com>
Message-ID: <6a566a52.f06f4994.1fc857.e33a@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `lld-x86_64-ubuntu-fast` running on `as-builder-4` while building `clang-tools-extra,clang,cmake,flang,libcxx,lld,llvm` at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/33/builds/40127

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SystemZ/zos-ppa2.ll' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
sed -e 's/!"MODE"/!"ascii"/' -e 's/BYTE/85/' /home/buildbot/worker/lld-x86_64/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll > /home/buildbot/worker/lld-x86_64/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: sed -e 's/!"MODE"/!"ascii"/' -e s/BYTE/85/ /home/buildbot/worker/lld-x86_64/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
# RUN: at line 2
/home/buildbot/worker/lld-x86_64/build/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true < /home/buildbot/worker/lld-x86_64/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll |  /home/buildbot/worker/lld-x86_64/build/bin/FileCheck /home/buildbot/worker/lld-x86_64/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: /home/buildbot/worker/lld-x86_64/build/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true
# executed command: /home/buildbot/worker/lld-x86_64/build/bin/FileCheck /home/buildbot/worker/lld-x86_64/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# .---command stderr------------
# | /home/buildbot/worker/lld-x86_64/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll:24:10: error: CHECK: expected string not found in input
# | ; CHECK: DC XL6'F2F3F0F0F0F0'
# |          ^
# | <stdin>:17:39: note: scanning from here
# |  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0' 
# |                                       ^
# | <stdin>:18:2: note: possible intended match here
# |  DC XL6'F2F4F0F0F0F0' 
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /home/buildbot/worker/lld-x86_64/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             .
# |             .
# |             .
# |            12:  DC XL4'00000000'  
# |            13:  DC XL1'85'  
# |            14:  DC XL1'00'  
# |            15:  DC XL2'0000'  
# |            16: L#DVS DS 0H  
# |            17:  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0'  
# | check:24'0                                          {    search range start (exclusive)
# | check:24'1                                               error: no match found in search range
# |            18:  DC XL6'F2F4F0F0F0F0'  
# | check:24'2      ?                       possible intended match
# |            19:  DC XL2'0000'  
# |            20: stdin#C CSECT  
# |            21: C_@@QPPA2 CATTR ALIGN(3),FILL(0),NOTEXECUTABLE,READONLY,RMODE(64),PART(.  
# |            22:  .&ppa2)  
# |            23: .&ppa2 XATTR LINKAGE(OS),REFERENCE(DATA),SCOPE(SECTION)  
# |             .
# |             .
...

```

</details>

https://github.com/llvm/llvm-project/pull/209473

From libcxx-commits at lists.llvm.org  Tue Jul 14 09:57:09 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 14 Jul 2026 09:57:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Inline std::function
 members into the class body (PR #209461)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209461@github.com>
Message-ID: <6a566a65.bc28b987.90005.6c60@mx.google.com>


philnik777 wrote:

> > didn't expect that inlining some functions would cause any failures,
> 
> LLDB and libcxx have worked together to create tests precisely because a lot of libcxx changes cause unexpected failures. So it is just natural that you were not expecting these failures, and that is the purpose the bots serve.

It's really not. I'm merging patches before all of the CI finished all the time, and it's basically never an issue.

> > I have other patches depending on this.
> 
> This is not a valid argument. Tests exist, we wait for bots. Please don't merge any follow ups until it is decided whether this needs to be reverted or not.

I think it is. I didn't merge any follow-ups and don't plan to for now, but PR stacking is absolute trash on GitHub, so merging patches without 100% CI testing significantly eases the workflow. It pretty much always works without issues, since I'm quite familiar with what sort of change causes what sort of problems. Just because there is an issue once doesn't mean this has to be changed.

> > The part that this causes a failure in LLDB feels ridiculous
> 
> No, it's not ridiculous, it's natural. LLDB _needs_ to know the inner workings of libcxx, that is the nature of stepping through libcxx code.

IMO something feeling ridiculous and being ridiculous are quite different, and I'd like to avoid conflating the two. That being said, LLDB tends to very much avoid depending on any particular symbols being available, so I don't understand why it's different here.


FWIW I'm also not sure why you care so much in this particular case. Something breaking a bot isn't exactly unheard of; it's basically the standard throughout the rest of LLVM.


https://github.com/llvm/llvm-project/pull/209461

From libcxx-commits at lists.llvm.org  Tue Jul 14 10:46:49 2026
From: libcxx-commits at lists.llvm.org (Michael Jones via libcxx-commits)
Date: Tue, 14 Jul 2026 10:46:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libcxx][libc] update
	LLVM-libc test status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a567609.824e11a6.f5f27.7d5c@mx.google.com>


https://github.com/michaelrj-google updated https://github.com/llvm/llvm-project/pull/201236

>From 5b2606356aa735a951b0bfc98416089f7e4fee88 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 2 Jun 2026 23:27:03 +0000
Subject: [PATCH 1/8] [libcxx][libc] update LLVM-libc test status

This update removes some unnecessary carveouts, marks some newly
XFAILing tests, and unmarks some now passing tests.

With this change the status of tests is:
Total Discovered Tests: 11402
  Unsupported      : 1821 (15.97%)
  Passed           : 9409 (82.52%)
  Expectedly Failed:  172 (1.51%)

Depends on #201235

Assisted-by: Automated tooling, human reviewed.
---
 libcxx/cmake/caches/Generic-llvm-libc.cmake           | 11 -----------
 libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in      |  2 +-
 ...nothrow_new_not_overridden_fno_exceptions.pass.cpp |  3 ---
 .../support.dynamic/new_dont_return_nullptr.pass.cpp  |  3 ---
 ...R30202_notify_from_pthread_created_thread.pass.cpp |  2 ++
 .../thread.thread.member/native_handle.pass.cpp       |  3 +++
 .../osyncstream/thread/several_threads.pass.cpp       |  3 +++
 .../complex.member.ops/divide_equal_complex.pass.cpp  |  3 ---
 .../complex.ops/complex_divide_complex.pass.cpp       |  3 ---
 .../complex.ops/scalar_divide_complex.pass.cpp        |  3 ---
 .../rand.dist.bern.bin/eval.PR44847.pass.cpp          |  3 +++
 .../rand.dist.bern/rand.dist.bern.bin/io.pass.cpp     |  3 +++
 .../notify_all_at_thread_exit_lwg3343.pass.cpp        |  3 +++
 .../thread.thread.this/sleep_for.signals.pass.cpp     |  3 +++
 .../std/time/time.hash/time.hash_enabled.pass.cpp     |  3 ---
 15 files changed, 21 insertions(+), 30 deletions(-)

diff --git a/libcxx/cmake/caches/Generic-llvm-libc.cmake b/libcxx/cmake/caches/Generic-llvm-libc.cmake
index 8e8a2699858b3..96bbeab040362 100644
--- a/libcxx/cmake/caches/Generic-llvm-libc.cmake
+++ b/libcxx/cmake/caches/Generic-llvm-libc.cmake
@@ -32,22 +32,11 @@ set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
 
 # Not yet fully implemented.
-set(LIBCXX_ENABLE_LOCALIZATION OFF CACHE BOOL "")
 set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF CACHE BOOL "")
 
 # Still missing some components.
 set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
 
-# Still needs validation before turning on.
-set(LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "")
-
-# Still missing some components.
-set(LIBCXX_ENABLE_MONOTONIC_CLOCK OFF CACHE BOOL "")
-
-# requires LIBCXX_ENABLE_MONOTONIC_CLOCK=ON
-set(LIBCXX_ENABLE_THREADS OFF CACHE BOOL "")
-set(LIBCXXABI_ENABLE_THREADS OFF CACHE BOOL "")
-
 set(LIBCXX_ENABLE_UNICODE ON CACHE BOOL "")
 set(LIBCXX_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
 set(LIBCXXABI_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
diff --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index cae8a8007214e..7d878e9888ce0 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include-dir} -I %{target-include-dir} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx-dir}/test/support -idirafter @LIBC_KERNEL_HEADERS@'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a'
+    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--defsym,__dso_handle=0 -latomic -Wl,--end-group'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '
diff --git a/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp b/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
index 39a51352eadd9..fb26bdf49cc3c 100644
--- a/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
+++ b/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
@@ -29,9 +29,6 @@
 //       spuriously fail.
 // REQUIRES: no-exceptions
 
-// No member named 'regex'.
-// XFAIL: LLVM-LIBC-FIXME
-
 #include <cstddef>
 #include <new>
 
diff --git a/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp b/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
index 339e756b86fc8..02b20a8c98b21 100644
--- a/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
+++ b/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
@@ -22,9 +22,6 @@
 // REQUIRES: no-exceptions
 // UNSUPPORTED: c++03, c++11, c++14
 
-// No member named 'regex'.
-// XFAIL: LLVM-LIBC-FIXME
-
 #include <cstddef>
 #include <limits>
 #include <new>
diff --git a/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp b/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
index 66251648bd50c..1df75885e4347 100644
--- a/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
@@ -20,6 +20,8 @@
 // Test that this function works with threads that were not created by
 // std::thread. See https://llvm.org/PR30202
 
+// Segfault
+// XFAIL: LLVM-LIBC-FIXME
 
 #include <condition_variable>
 #include <mutex>
diff --git a/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
index 96ec3332519bc..5c5c0fed58726 100644
--- a/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
@@ -16,6 +16,9 @@
 
 // native_handle_type native_handle();
 
+// pthread_t and int not comparable.
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <thread>
 #include <new>
 #include <cstdlib>
diff --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..82d43c1673d2d 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -21,6 +21,9 @@
 // but the words should be written without interleaving. To increment the
 // change of interleaving words are written one character at a time.
 
+// segmentation fault
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <cassert>
 #include <chrono>
 #include <mutex>
diff --git a/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
index b9467a166e8d5..61e0d01f2ae4d 100644
--- a/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing some math functions that compiler-rt buildins need.
-// XFAIL: LLVM-LIBC-FIXME
-
 // <complex>
 
 // complex& operator/=(const complex& rhs); // constexpr in C++20
diff --git a/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
index 4e00e87f18220..d12dfd994b0ae 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing some math functions that compiler-rt buildins need.
-// XFAIL: LLVM-LIBC-FIXME
-
 // <complex>
 
 // template<class T>
diff --git a/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
index 56d9664b49aea..f32b560c1e864 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing some math functions that compiler-rt buildins need.
-// XFAIL: LLVM-LIBC-FIXME
-
 // <complex>
 
 // template<class T>
diff --git a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
index 8f7c8eb7ccc0f..22319e5b31454 100644
--- a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
@@ -21,6 +21,9 @@
 // Very slow when run in qemu.
 // REQUIRES: long_tests
 
+// missing lgamma_r
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <random>
 #include <cassert>
 #include <cmath>
diff --git a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
index 35f579903a460..a53a5db3be4c0 100644
--- a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
@@ -23,6 +23,9 @@
 // operator>>(basic_istream<charT, traits>& is,
 //            binomial_distribution& x);
 
+// missing lgamma_r
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <random>
 #include <sstream>
 #include <cassert>
diff --git a/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp b/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
index 61094eced4cb2..f1b349017b1a5 100644
--- a/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
+++ b/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
@@ -23,6 +23,9 @@
 //
 // void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);
 
+// segmentation fault
+// XFAIL: LLVM-LIBC-FIXME
+
 #include "make_test_thread.h"
 #include "test_macros.h"
 
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
index 1dba5d8a40976..726b5807d55e3 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
@@ -21,6 +21,9 @@
 // This test ensures that we sleep for the right amount of time even when
 // we get interrupted by a signal, as fixed in 58a0a70fb2f1.
 
+// missing ITIMER_REAL
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <thread>
 #include <cassert>
 #include <chrono>
diff --git a/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp b/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
index e3ac7e53251d5..cac7fbda0fbb3 100644
--- a/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
+++ b/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// No member named `leapsecond'.
-// XFAIL: LLVM-LIBC-FIXME
-
 // REQUIRES: std-at-least-c++26
 
 // <chrono>

>From 7895159c9203b0c8f9d3774febe98a4510124366 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 3 Jun 2026 23:59:29 +0000
Subject: [PATCH 2/8] enable wchar, fix tests

---
 libcxx/cmake/caches/Generic-llvm-libc.cmake                    | 3 ++-
 .../extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp  | 3 +++
 .../test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp   | 3 +++
 libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp   | 3 +++
 libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp  | 3 +++
 libcxx/test/std/strings/c.strings/cwchar.pass.cpp              | 3 +++
 libcxx/test/std/strings/c.strings/cwctype.pass.cpp             | 3 +++
 libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp | 3 +++
 8 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/libcxx/cmake/caches/Generic-llvm-libc.cmake b/libcxx/cmake/caches/Generic-llvm-libc.cmake
index 96bbeab040362..2a4df69bec105 100644
--- a/libcxx/cmake/caches/Generic-llvm-libc.cmake
+++ b/libcxx/cmake/caches/Generic-llvm-libc.cmake
@@ -32,7 +32,7 @@ set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
 
 # Not yet fully implemented.
-set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF CACHE BOOL "")
+set(LIBCXX_ENABLE_WIDE_CHARACTERS ON CACHE BOOL "")
 
 # Still missing some components.
 set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
@@ -47,5 +47,6 @@ set(CMAKE_BUILD_TYPE "Release" CACHE STRING "")
 set(LLVM_ENABLE_RUNTIMES "compiler-rt;libc;libunwind;libcxxabi;libcxx" CACHE STRING "")
 set(LLVM_LIBC_FULL_BUILD ON CACHE BOOL "")
 set(LLVM_LIBC_INCLUDE_SCUDO ON CACHE BOOL "")
+set(LLVM_LIBC_ENABLE_EXPERIMENTAL_ENTRYPOINTS ON CACHE BOOL "")
 set(COMPILER_RT_BUILD_GWP_ASAN OFF CACHE BOOL "")
 set(COMPILER_RT_BUILD_SCUDO_STANDALONE_WITH_LLVM_LIBC ON CACHE BOOL "")
diff --git a/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp b/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
index dd9847f63253b..4b8297145e35a 100644
--- a/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
+++ b/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
@@ -9,6 +9,9 @@
 // We're building as C, so this test doesn't work when building with modules.
 // ADDITIONAL_COMPILE_FLAGS: -fno-modules
 
+// Incompatible redeclaration of wcschr and wmemchr builtins
+// XFAIL: LLVM-LIBC-FIXME
+
 // GCC complains about unrecognized arguments because we're compiling the
 // file as C, but we're passing C++ flags on the command-line.
 // UNSUPPORTED: gcc
diff --git a/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
index eaae3aa3417e4..a730debab2e9b 100644
--- a/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
@@ -8,6 +8,9 @@
 
 // test <inttypes.h>
 
+// Missing wcstoimax, wcstoumax
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <inttypes.h>
 
 #include "test_macros.h"
diff --git a/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
index 98d028c5181d5..df7bda3baf8af 100644
--- a/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
@@ -8,6 +8,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing fwprintf, fwscanf, swscanf, vfwprintf, vfwscanf, vswprintf, vswscanf, wcsftime, vwscanf, wscanf
+// XFAIL: LLVM-LIBC-FIXME
+
 // <wchar.h>
 
 #include <wchar.h>
diff --git a/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
index 4e2fb319336f1..221c219918614 100644
--- a/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
@@ -8,6 +8,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing WEOF, wctrans_t
+// XFAIL: LLVM-LIBC-FIXME
+
 // towctrans and wctrans were added in Android API 26.
 // TODO: Switch from UNSUPPORTED to XFAIL once the Android CI Docker sysroot is
 // updated.
diff --git a/libcxx/test/std/strings/c.strings/cwchar.pass.cpp b/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
index 0caf4b9913b50..1758a703b3b62 100644
--- a/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
+++ b/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
@@ -10,6 +10,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing fwprintf, fwscanf, swscanf, vfwprintf, vfwscanf, vswprintf, vswscanf, wcsftime, vwscanf, wscanf
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <cwchar>
 #include <ctime>
 #include <cstdarg>
diff --git a/libcxx/test/std/strings/c.strings/cwctype.pass.cpp b/libcxx/test/std/strings/c.strings/cwctype.pass.cpp
index 0deabf51ed59c..3de582714d01d 100644
--- a/libcxx/test/std/strings/c.strings/cwctype.pass.cpp
+++ b/libcxx/test/std/strings/c.strings/cwctype.pass.cpp
@@ -10,6 +10,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing WEOF, towctrans, wctrans_t
+// XFAIL: LLVM-LIBC-FIXME
+
 // towctrans and wctrans were added in Android API 26.
 // TODO: Switch from UNSUPPORTED to XFAIL once the Android CI Docker sysroot is
 // updated.
diff --git a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
index fff5ede848b57..bd4bd47671a81 100644
--- a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
+++ b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
@@ -20,6 +20,9 @@
 // wstring to_wstring(double val);
 // wstring to_wstring(long double val);
 
+// Missing swprintf (currently a stub that infinite loops)
+// UNSUPPORTED: LLVM-LIBC-FIXME
+
 #include <string>
 #include <cassert>
 #include <limits>

>From b015d1ff6bd9223ba2c3c6628d8afae172937371 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 24 Jun 2026 17:52:24 +0000
Subject: [PATCH 3/8] move where `__dso_handle` is defined

---
 libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index 7d878e9888ce0..ed1a63c027c9e 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include-dir} -I %{target-include-dir} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx-dir}/test/support -idirafter @LIBC_KERNEL_HEADERS@'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--defsym,__dso_handle=0 -latomic -Wl,--end-group'
+    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -latomic -Wl,--end-group -Wl,--defsym,__dso_handle=0'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '

>From fa496efb18b8eb1e884bd366744b71ec1e0c3bfa Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 1 Jul 2026 20:22:46 +0000
Subject: [PATCH 4/8] update XFAILs with new version

---
 .../extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp  | 3 ---
 .../test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp   | 3 ---
 2 files changed, 6 deletions(-)

diff --git a/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp b/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
index 4b8297145e35a..dd9847f63253b 100644
--- a/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
+++ b/libcxx/test/extensions/libcxx/depr/depr.c.headers/include_as_c.sh.cpp
@@ -9,9 +9,6 @@
 // We're building as C, so this test doesn't work when building with modules.
 // ADDITIONAL_COMPILE_FLAGS: -fno-modules
 
-// Incompatible redeclaration of wcschr and wmemchr builtins
-// XFAIL: LLVM-LIBC-FIXME
-
 // GCC complains about unrecognized arguments because we're compiling the
 // file as C, but we're passing C++ flags on the command-line.
 // UNSUPPORTED: gcc
diff --git a/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
index a730debab2e9b..eaae3aa3417e4 100644
--- a/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/inttypes_h.compile.pass.cpp
@@ -8,9 +8,6 @@
 
 // test <inttypes.h>
 
-// Missing wcstoimax, wcstoumax
-// XFAIL: LLVM-LIBC-FIXME
-
 #include <inttypes.h>
 
 #include "test_macros.h"

>From 2dff85cf1c78d5016390afcd91360d682d9f6c55 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Mon, 6 Jul 2026 23:52:38 +0000
Subject: [PATCH 5/8] disable threads due to atomic issue

---
 libcxx/cmake/caches/Generic-llvm-libc.cmake      | 7 ++++---
 libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in | 2 +-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/cmake/caches/Generic-llvm-libc.cmake b/libcxx/cmake/caches/Generic-llvm-libc.cmake
index 2a4df69bec105..dce7fb73ff8b3 100644
--- a/libcxx/cmake/caches/Generic-llvm-libc.cmake
+++ b/libcxx/cmake/caches/Generic-llvm-libc.cmake
@@ -31,12 +31,13 @@ set(LIBCXX_ENABLE_STATIC ON CACHE BOOL "")
 set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
 
-# Not yet fully implemented.
-set(LIBCXX_ENABLE_WIDE_CHARACTERS ON CACHE BOOL "")
-
 # Still missing some components.
 set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
 
+# blocked on issues with atomic
+set(LIBCXX_ENABLE_THREADS OFF CACHE BOOL "")
+set(LIBCXXABI_ENABLE_THREADS OFF CACHE BOOL "")
+
 set(LIBCXX_ENABLE_UNICODE ON CACHE BOOL "")
 set(LIBCXX_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
 set(LIBCXXABI_ENABLE_EXCEPTIONS OFF CACHE BOOL "")
diff --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index ed1a63c027c9e..9dbf8a98dd5e4 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include-dir} -I %{target-include-dir} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx-dir}/test/support -idirafter @LIBC_KERNEL_HEADERS@'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -latomic -Wl,--end-group -Wl,--defsym,__dso_handle=0'
+    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--end-group -Wl,--defsym,__dso_handle=0'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '

>From 0e59eb4b901826432c3334e2f5080097d8803ecd Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 7 Jul 2026 17:17:24 +0000
Subject: [PATCH 6/8] address comment

---
 libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
index df7bda3baf8af..7fee899683ee1 100644
--- a/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: no-wide-characters
 
-// Missing fwprintf, fwscanf, swscanf, vfwprintf, vfwscanf, vswprintf, vswscanf, wcsftime, vwscanf, wscanf
+// Missing wchar string formatting functions.
 // XFAIL: LLVM-LIBC-FIXME
 
 // <wchar.h>

>From 2830be93556ef01c259b29854262cf5ea6c06365 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 7 Jul 2026 17:32:22 +0000
Subject: [PATCH 7/8] address other comment

---
 libcxx/test/std/strings/c.strings/cwchar.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/strings/c.strings/cwchar.pass.cpp b/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
index 1758a703b3b62..a9e99914c7b1a 100644
--- a/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
+++ b/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
@@ -10,7 +10,7 @@
 
 // UNSUPPORTED: no-wide-characters
 
-// Missing fwprintf, fwscanf, swscanf, vfwprintf, vfwscanf, vswprintf, vswscanf, wcsftime, vwscanf, wscanf
+// Missing wchar string formatting functions.
 // XFAIL: LLVM-LIBC-FIXME
 
 #include <cwchar>

>From 1e3706766eb84cdc5a278dde45854e7bc0e7e110 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 14 Jul 2026 17:46:22 +0000
Subject: [PATCH 8/8] update libcxxabi config

---
 libcxxabi/test/configs/llvm-libc++abi-llvm-libc.cfg.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxxabi/test/configs/llvm-libc++abi-llvm-libc.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-llvm-libc.cfg.in
index d46a1c6d4e58c..9e37ffac4c791 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-llvm-libc.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-llvm-libc.cfg.in
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx}/test/support -I %{libcxx}/src -idirafter @LIBC_KERNEL_HEADERS@ -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib} -lc++ -lc++abi -pthread @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o @CMAKE_BINARY_DIR@/libc/lib/libc.a -static -fno-use-cxa-atexit'
+    '-nodefaultlibs -nostartfiles -L %{lib} -lc++ -lc++abi -pthread @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o @CMAKE_BINARY_DIR@/libc/lib/libc.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--defsym,__dso_handle=0'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '


From libcxx-commits at lists.llvm.org  Tue Jul 14 19:21:35 2026
From: libcxx-commits at lists.llvm.org (Hubert Tong via libcxx-commits)
Date: Tue, 14 Jul 2026 19:21:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [libunwind][AIX] Handle
 VAPI-based return addresses in stack unwinding for LLU (PR #209280)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209280@github.com>
Message-ID: <6a56eeaf.92ea0e2a.13fd37.e6a1@mx.google.com>


https://github.com/hubert-reinterpretcast dismissed https://github.com/llvm/llvm-project/pull/209280

From libcxx-commits at lists.llvm.org  Tue Jul 14 23:01:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 23:01:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from durations with unsigned
 representations. (PR #209686)
Message-ID: <llvm.org/llvm/llvm-project/pull/209686@github.com>


https://github.com/emmett2020 created https://github.com/llvm/llvm-project/pull/209686

[LWG4274: The chrono::hh_mm_ss constructor is ill-formed for unsigned durations](https://cplusplus.github.io/LWG/issue4274)

`chrono::hh_mm_ss` used `chrono::abs` for all durations. `chrono::abs` does not support unsigned representations. This made the constructor of `hh_mm_ss` fail for unsigned durations.

_**Example**_:
We're compiling a program with such piece of code:
```cpp
  std::chrono::duration<unsigned int> dur{0};
  std::chrono::hh_mm_ss<std::chrono::duration<unsigned int>> hms{dur};
  (void)hms;
```
Then we'll get follow compilation error log:
```text
...
/home/utils/llvm-22.1.0/bin/../include/c++/v1/__chrono/hh_mm_ss.h:57:51: error: no matching function for call to 'abs'
   57 |         __h_(chrono::duration_cast<chrono::hours>(chrono::abs(__d))),
main.cpp:7:62: note: in instantiation of member function 'std::chrono::hh_mm_ss<std::chrono::duration<unsigned int>>::hh_mm_ss' requested here
    7 |   std::chrono::hh_mm_ss<std::chrono::duration<unsigned int>> hms{dur};
      |                                                              ^
/home/utils/llvm-22.1.0/bin/../include/c++/v1/__chrono/time_point.h:132:78: note: candidate template ignored: requirement 'std::numeric_limits<unsigned int>::is_signed' was not
      satisfied [with _Rep = unsigned int, _Period = std::ratio<1>]
  132 | [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI constexpr duration<_Rep, _Period> abs(duration<_Rep, _Period> __d) {

```

**_Solution_**:
I updated the constructor to handle unsigned durations without calling `chrono::abs`. I also added tests for unsigned and custom signed representations.

**_Implementation details_**:
- Use `is_unsigned_v` instead of `numeric_limits<Rep>::is_signed`, which is used by `chrono::abs`. `numeric_limits<Rep>::is_signed` may return false when a custom `Rep` does not specialize `numeric_limits`. This may incorrectly treat a custom signed type as unsigned. `is_unsigned_v` provides a compile-time path for unsigned types, while the sign check keeps signed and custom types correct.

- The constructor calls `__abs_d` four times because each member is initialized separately. This should not affect runtime performance because the compiler should inline `__abs_d` and eliminate the repeated calculations. We could use another helper function or helper class to explicitly compute the absolute duration only once. However, this would add extra glue code, and the added complexity is not worthwhile I think.





Close #171328 

>From bc598c40cb7bac1531c0a3c1b32c0c8536ac6cd1 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 12:59:35 +0800
Subject: [PATCH] [libc++] Implement LWG 4274

Allow chrono::hh_mm_ss to be constructed from durations with unsigned representations.
---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__chrono/hh_mm_ss.h            | 20 ++++--
 .../time.hms/time.hms.members/ctor.pass.cpp   | 68 +++++++++++++++++++
 3 files changed, 83 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c203fa424556a..e37c05f035526 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -168,7 +168,7 @@
 "`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","|Complete|","23","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
 "`LWG4269 <https://wg21.link/LWG4269>`__","``unique_copy`` passes arguments to its predicate backwards","2025-11 (Kona)","","","`#171326 <https://github.com/llvm/llvm-project/issues/171326>`__",""
 "`LWG4272 <https://wg21.link/LWG4272>`__","For ``rank == 0``, ``layout_stride`` is atypically convertible","2025-11 (Kona)","","","`#171327 <https://github.com/llvm/llvm-project/issues/171327>`__",""
-"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","","","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
+"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","|Complete|","24","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
 "`LWG4275 <https://wg21.link/LWG4275>`__","``std::dynamic_extent`` should also be defined in ``<mdspan>``","2025-11 (Kona)","|Complete|","21","`#171329 <https://github.com/llvm/llvm-project/issues/171329>`__",""
 "`LWG4276 <https://wg21.link/LWG4276>`__","``front()`` and ``back()`` are not hardened for zero-length ``std::array``\s","2025-11 (Kona)","|Complete|","17","`#171330 <https://github.com/llvm/llvm-project/issues/171330>`__",""
 "`LWG4280 <https://wg21.link/LWG4280>`__","``simd::partial_load`` uses undefined identifier ``T``","2025-11 (Kona)","","","`#171331 <https://github.com/llvm/llvm-project/issues/171331>`__",""
diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index 746eba903d4f8..ddb2d055c16ca 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -11,9 +11,9 @@
 #define _LIBCPP___CHRONO_HH_MM_SS_H
 
 #include <__chrono/duration.h>
-#include <__chrono/time_point.h>
 #include <__config>
 #include <__type_traits/common_type.h>
+#include <__type_traits/is_unsigned.h>
 #include <ratio>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -45,6 +45,14 @@ class hh_mm_ss {
     return 0;
   }
 
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+      return __d;
+    } else {
+      return __is_neg ? -__d : __d;
+    }
+  }
+
 public:
   _LIBCPP_HIDE_FROM_ABI static unsigned constexpr fractional_width =
       __width(__CommonType::period::den) < 19 ? __width(__CommonType::period::den) : 6u;
@@ -53,11 +61,11 @@ class hh_mm_ss {
   _LIBCPP_HIDE_FROM_ABI constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit hh_mm_ss(_Duration __d) noexcept
-      : __is_neg_(__d < _Duration(0)),
-        __h_(chrono::duration_cast<chrono::hours>(chrono::abs(__d))),
-        __m_(chrono::duration_cast<chrono::minutes>(chrono::abs(__d) - hours())),
-        __s_(chrono::duration_cast<chrono::seconds>(chrono::abs(__d) - hours() - minutes())),
-        __f_(chrono::duration_cast<precision>(chrono::abs(__d) - hours() - minutes() - seconds())) {}
+      : __is_neg_(__d < _Duration::zero()),
+        __h_(chrono::duration_cast<chrono::hours>(__abs_d(__d, __is_neg_))),
+        __m_(chrono::duration_cast<chrono::minutes>(__abs_d(__d, __is_neg_) - hours())),
+        __s_(chrono::duration_cast<chrono::seconds>(__abs_d(__d, __is_neg_) - hours() - minutes())),
+        __f_(chrono::duration_cast<precision>(__abs_d(__d, __is_neg_) - hours() - minutes() - seconds())) {}
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool is_negative() const noexcept { return __is_neg_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr chrono::hours hours() const noexcept { return __h_; }
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
new file mode 100644
index 0000000000000..1d3a946ed8873
--- /dev/null
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
+
+int main(int, char**) {
+  {
+    // Tests construction from a duration with an unsigned representation.
+    using Duration = std::chrono::duration<unsigned, std::milli>;
+
+    // 1 hour + 1 minute + 1 second + 1 millisecond
+    constexpr Duration d{3'661'001};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(!hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
+    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
+  }
+
+  {
+    // Tests construction from a negative duration with a custom representation.
+    using Duration = std::chrono::duration<SignedRep>;
+
+    // 1 hour + 1 minute + 1 second
+    constexpr Duration d{SignedRep{-3'661}};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::seconds{0});
+    static_assert(hms.to_duration() == std::chrono::seconds{-3'661});
+  }
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Tue Jul 14 23:02:04 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 23:02:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from durations with unsigned
 representations. (PR #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a57225c.4a342cbc.ca844.135e@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Emmett (emmett2020)

<details>
<summary>Changes</summary>

[LWG4274: The chrono::hh_mm_ss constructor is ill-formed for unsigned durations](https://cplusplus.github.io/LWG/issue4274)

`chrono::hh_mm_ss` used `chrono::abs` for all durations. `chrono::abs` does not support unsigned representations. This made the constructor of `hh_mm_ss` fail for unsigned durations.

_**Example**_:
We're compiling a program with such piece of code:
```cpp
  std::chrono::duration&lt;unsigned int&gt; dur{0};
  std::chrono::hh_mm_ss&lt;std::chrono::duration&lt;unsigned int&gt;&gt; hms{dur};
  (void)hms;
```
Then we'll get follow compilation error log:
```text
...
/home/utils/llvm-22.1.0/bin/../include/c++/v1/__chrono/hh_mm_ss.h:57:51: error: no matching function for call to 'abs'
   57 |         __h_(chrono::duration_cast&lt;chrono::hours&gt;(chrono::abs(__d))),
main.cpp:7:62: note: in instantiation of member function 'std::chrono::hh_mm_ss&lt;std::chrono::duration&lt;unsigned int&gt;&gt;::hh_mm_ss' requested here
    7 |   std::chrono::hh_mm_ss&lt;std::chrono::duration&lt;unsigned int&gt;&gt; hms{dur};
      |                                                              ^
/home/utils/llvm-22.1.0/bin/../include/c++/v1/__chrono/time_point.h:132:78: note: candidate template ignored: requirement 'std::numeric_limits&lt;unsigned int&gt;::is_signed' was not
      satisfied [with _Rep = unsigned int, _Period = std::ratio&lt;1&gt;]
  132 | [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI constexpr duration&lt;_Rep, _Period&gt; abs(duration&lt;_Rep, _Period&gt; __d) {

```

**_Solution_**:
I updated the constructor to handle unsigned durations without calling `chrono::abs`. I also added tests for unsigned and custom signed representations.

**_Implementation details_**:
- Use `is_unsigned_v` instead of `numeric_limits&lt;Rep&gt;::is_signed`, which is used by `chrono::abs`. `numeric_limits&lt;Rep&gt;::is_signed` may return false when a custom `Rep` does not specialize `numeric_limits`. This may incorrectly treat a custom signed type as unsigned. `is_unsigned_v` provides a compile-time path for unsigned types, while the sign check keeps signed and custom types correct.

- The constructor calls `__abs_d` four times because each member is initialized separately. This should not affect runtime performance because the compiler should inline `__abs_d` and eliminate the repeated calculations. We could use another helper function or helper class to explicitly compute the absolute duration only once. However, this would add extra glue code, and the added complexity is not worthwhile I think.





Close #<!-- -->171328 

---
Full diff: https://github.com/llvm/llvm-project/pull/209686.diff


3 Files Affected:

- (modified) libcxx/docs/Status/Cxx26Issues.csv (+1-1) 
- (modified) libcxx/include/__chrono/hh_mm_ss.h (+14-6) 
- (added) libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp (+68) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c203fa424556a..e37c05f035526 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -168,7 +168,7 @@
 "`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","|Complete|","23","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
 "`LWG4269 <https://wg21.link/LWG4269>`__","``unique_copy`` passes arguments to its predicate backwards","2025-11 (Kona)","","","`#171326 <https://github.com/llvm/llvm-project/issues/171326>`__",""
 "`LWG4272 <https://wg21.link/LWG4272>`__","For ``rank == 0``, ``layout_stride`` is atypically convertible","2025-11 (Kona)","","","`#171327 <https://github.com/llvm/llvm-project/issues/171327>`__",""
-"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","","","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
+"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","|Complete|","24","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
 "`LWG4275 <https://wg21.link/LWG4275>`__","``std::dynamic_extent`` should also be defined in ``<mdspan>``","2025-11 (Kona)","|Complete|","21","`#171329 <https://github.com/llvm/llvm-project/issues/171329>`__",""
 "`LWG4276 <https://wg21.link/LWG4276>`__","``front()`` and ``back()`` are not hardened for zero-length ``std::array``\s","2025-11 (Kona)","|Complete|","17","`#171330 <https://github.com/llvm/llvm-project/issues/171330>`__",""
 "`LWG4280 <https://wg21.link/LWG4280>`__","``simd::partial_load`` uses undefined identifier ``T``","2025-11 (Kona)","","","`#171331 <https://github.com/llvm/llvm-project/issues/171331>`__",""
diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index 746eba903d4f8..ddb2d055c16ca 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -11,9 +11,9 @@
 #define _LIBCPP___CHRONO_HH_MM_SS_H
 
 #include <__chrono/duration.h>
-#include <__chrono/time_point.h>
 #include <__config>
 #include <__type_traits/common_type.h>
+#include <__type_traits/is_unsigned.h>
 #include <ratio>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -45,6 +45,14 @@ class hh_mm_ss {
     return 0;
   }
 
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+      return __d;
+    } else {
+      return __is_neg ? -__d : __d;
+    }
+  }
+
 public:
   _LIBCPP_HIDE_FROM_ABI static unsigned constexpr fractional_width =
       __width(__CommonType::period::den) < 19 ? __width(__CommonType::period::den) : 6u;
@@ -53,11 +61,11 @@ class hh_mm_ss {
   _LIBCPP_HIDE_FROM_ABI constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit hh_mm_ss(_Duration __d) noexcept
-      : __is_neg_(__d < _Duration(0)),
-        __h_(chrono::duration_cast<chrono::hours>(chrono::abs(__d))),
-        __m_(chrono::duration_cast<chrono::minutes>(chrono::abs(__d) - hours())),
-        __s_(chrono::duration_cast<chrono::seconds>(chrono::abs(__d) - hours() - minutes())),
-        __f_(chrono::duration_cast<precision>(chrono::abs(__d) - hours() - minutes() - seconds())) {}
+      : __is_neg_(__d < _Duration::zero()),
+        __h_(chrono::duration_cast<chrono::hours>(__abs_d(__d, __is_neg_))),
+        __m_(chrono::duration_cast<chrono::minutes>(__abs_d(__d, __is_neg_) - hours())),
+        __s_(chrono::duration_cast<chrono::seconds>(__abs_d(__d, __is_neg_) - hours() - minutes())),
+        __f_(chrono::duration_cast<precision>(__abs_d(__d, __is_neg_) - hours() - minutes() - seconds())) {}
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool is_negative() const noexcept { return __is_neg_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr chrono::hours hours() const noexcept { return __h_; }
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
new file mode 100644
index 0000000000000..1d3a946ed8873
--- /dev/null
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
+
+int main(int, char**) {
+  {
+    // Tests construction from a duration with an unsigned representation.
+    using Duration = std::chrono::duration<unsigned, std::milli>;
+
+    // 1 hour + 1 minute + 1 second + 1 millisecond
+    constexpr Duration d{3'661'001};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(!hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
+    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
+  }
+
+  {
+    // Tests construction from a negative duration with a custom representation.
+    using Duration = std::chrono::duration<SignedRep>;
+
+    // 1 hour + 1 minute + 1 second
+    constexpr Duration d{SignedRep{-3'661}};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::seconds{0});
+    static_assert(hms.to_duration() == std::chrono::seconds{-3'661});
+  }
+
+  return 0;
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/209686

From libcxx-commits at lists.llvm.org  Tue Jul 14 23:05:15 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 23:05:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a57231b.b1416da1.78d1e.b7d2@mx.google.com>


https://github.com/emmett2020 edited https://github.com/llvm/llvm-project/pull/209686

From libcxx-commits at lists.llvm.org  Tue Jul 14 23:20:05 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 23:20:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a572695.e55dc341.1f19bd.7345@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/209686

>From bc598c40cb7bac1531c0a3c1b32c0c8536ac6cd1 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 12:59:35 +0800
Subject: [PATCH 1/2] [libc++] Implement LWG 4274

Allow chrono::hh_mm_ss to be constructed from durations with unsigned representations.
---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__chrono/hh_mm_ss.h            | 20 ++++--
 .../time.hms/time.hms.members/ctor.pass.cpp   | 68 +++++++++++++++++++
 3 files changed, 83 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c203fa424556a..e37c05f035526 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -168,7 +168,7 @@
 "`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","|Complete|","23","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
 "`LWG4269 <https://wg21.link/LWG4269>`__","``unique_copy`` passes arguments to its predicate backwards","2025-11 (Kona)","","","`#171326 <https://github.com/llvm/llvm-project/issues/171326>`__",""
 "`LWG4272 <https://wg21.link/LWG4272>`__","For ``rank == 0``, ``layout_stride`` is atypically convertible","2025-11 (Kona)","","","`#171327 <https://github.com/llvm/llvm-project/issues/171327>`__",""
-"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","","","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
+"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","|Complete|","24","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
 "`LWG4275 <https://wg21.link/LWG4275>`__","``std::dynamic_extent`` should also be defined in ``<mdspan>``","2025-11 (Kona)","|Complete|","21","`#171329 <https://github.com/llvm/llvm-project/issues/171329>`__",""
 "`LWG4276 <https://wg21.link/LWG4276>`__","``front()`` and ``back()`` are not hardened for zero-length ``std::array``\s","2025-11 (Kona)","|Complete|","17","`#171330 <https://github.com/llvm/llvm-project/issues/171330>`__",""
 "`LWG4280 <https://wg21.link/LWG4280>`__","``simd::partial_load`` uses undefined identifier ``T``","2025-11 (Kona)","","","`#171331 <https://github.com/llvm/llvm-project/issues/171331>`__",""
diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index 746eba903d4f8..ddb2d055c16ca 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -11,9 +11,9 @@
 #define _LIBCPP___CHRONO_HH_MM_SS_H
 
 #include <__chrono/duration.h>
-#include <__chrono/time_point.h>
 #include <__config>
 #include <__type_traits/common_type.h>
+#include <__type_traits/is_unsigned.h>
 #include <ratio>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -45,6 +45,14 @@ class hh_mm_ss {
     return 0;
   }
 
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+      return __d;
+    } else {
+      return __is_neg ? -__d : __d;
+    }
+  }
+
 public:
   _LIBCPP_HIDE_FROM_ABI static unsigned constexpr fractional_width =
       __width(__CommonType::period::den) < 19 ? __width(__CommonType::period::den) : 6u;
@@ -53,11 +61,11 @@ class hh_mm_ss {
   _LIBCPP_HIDE_FROM_ABI constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit hh_mm_ss(_Duration __d) noexcept
-      : __is_neg_(__d < _Duration(0)),
-        __h_(chrono::duration_cast<chrono::hours>(chrono::abs(__d))),
-        __m_(chrono::duration_cast<chrono::minutes>(chrono::abs(__d) - hours())),
-        __s_(chrono::duration_cast<chrono::seconds>(chrono::abs(__d) - hours() - minutes())),
-        __f_(chrono::duration_cast<precision>(chrono::abs(__d) - hours() - minutes() - seconds())) {}
+      : __is_neg_(__d < _Duration::zero()),
+        __h_(chrono::duration_cast<chrono::hours>(__abs_d(__d, __is_neg_))),
+        __m_(chrono::duration_cast<chrono::minutes>(__abs_d(__d, __is_neg_) - hours())),
+        __s_(chrono::duration_cast<chrono::seconds>(__abs_d(__d, __is_neg_) - hours() - minutes())),
+        __f_(chrono::duration_cast<precision>(__abs_d(__d, __is_neg_) - hours() - minutes() - seconds())) {}
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool is_negative() const noexcept { return __is_neg_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr chrono::hours hours() const noexcept { return __h_; }
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
new file mode 100644
index 0000000000000..1d3a946ed8873
--- /dev/null
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
+
+int main(int, char**) {
+  {
+    // Tests construction from a duration with an unsigned representation.
+    using Duration = std::chrono::duration<unsigned, std::milli>;
+
+    // 1 hour + 1 minute + 1 second + 1 millisecond
+    constexpr Duration d{3'661'001};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(!hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
+    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
+  }
+
+  {
+    // Tests construction from a negative duration with a custom representation.
+    using Duration = std::chrono::duration<SignedRep>;
+
+    // 1 hour + 1 minute + 1 second
+    constexpr Duration d{SignedRep{-3'661}};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::seconds{0});
+    static_assert(hms.to_duration() == std::chrono::seconds{-3'661});
+  }
+
+  return 0;
+}

>From 5504e7564dcfc299442cbf1d054a2be329c3e512 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 14:19:47 +0800
Subject: [PATCH 2/2] fix missing header file

---
 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
index 1d3a946ed8873..b33917d262ba8 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -19,6 +19,7 @@
 // LWG4274: The hh_mm_ss constructor supports unsigned durations.
 
 #include <chrono>
+#include <ratio>
 
 // A signed arithmetic-like type used as a custom duration representation.
 struct SignedRep {


From libcxx-commits at lists.llvm.org  Tue Jul 14 23:45:46 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 23:45:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][FreeBSD] Fix __transform_primary
	in FreeBSD (PR #186130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186130@github.com>
Message-ID: <6a572c9a.1b0626d0.1b4645.2b27@mx.google.com>


aokblast wrote:

@ldionne Is there anything you want to add for this?


https://github.com/llvm/llvm-project/pull/186130

From libcxx-commits at lists.llvm.org  Tue Jul 14 23:48:38 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 23:48:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Correctly implement
 __transform_primary in all platforms (PR #186130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186130@github.com>
Message-ID: <6a572d46.8e75e0cb.3dbce4.4102@mx.google.com>


https://github.com/aokblast edited https://github.com/llvm/llvm-project/pull/186130

From libcxx-commits at lists.llvm.org  Tue Jul 14 23:48:52 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 23:48:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Correctly implement
 __transform_primary in all platforms (PR #186130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186130@github.com>
Message-ID: <6a572d54.6365b89b.8575e.40e0@mx.google.com>


https://github.com/aokblast edited https://github.com/llvm/llvm-project/pull/186130

From libcxx-commits at lists.llvm.org  Tue Jul 14 23:49:13 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 14 Jul 2026 23:49:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Correctly implement
 __transform_primary in all platforms (PR #186130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186130@github.com>
Message-ID: <6a572d69.6365b89b.8575e.4fbd@mx.google.com>


https://github.com/aokblast updated https://github.com/llvm/llvm-project/pull/186130

>From 28c99ae55416b428da6b76c23fe44646e32aa65a Mon Sep 17 00:00:00 2001
From: aokblast <aokblast at FreeBSD.org>
Date: Sat, 6 Jun 2026 00:55:42 +0800
Subject: [PATCH 01/10] Fix __transform_primary in FreeBSD

FreeBSD's strxfrm() encodes collation weights one level at a time,
separating the primary, secondary, and tertiary with '.' bytes. Since
primary equivalence only depends on the primary collation weight, ignore
everything after the first separator when constructing the transformed
key.

This patch the intended behavior of primary equivalence and avoids
relying on glibc's fixed-size collation-key representation.
---
 libcxx/include/regex                          | 23 +++++++++++++++++++
 .../re/re.traits/transform_primary.pass.cpp   |  1 -
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/regex b/libcxx/include/regex
index 695a0f21754c4..91968a5c87c74 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -1130,6 +1130,18 @@ typename regex_traits<_CharT>::string_type
 regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, char) const {
   const string_type __s(__f, __l);
   string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
+#    if defined(__FreeBSD__)
+  // FreeBSD's strxfrm() emits the weights one collation level at a time,
+  // separating the levels with a byte ('.') that is chosen to sort below every
+  // weight byte.  Primary equivalence only depends on the first (primary)
+  // level, so drop everything from the first separator onwards.  This keeps
+  // characters that share a primary weight equal (e.g. 'A' and 'A-acute' in
+  // cs_CZ) even when the lower levels expand to a different number of weights,
+  // which the fixed-size logic below could not handle.
+  typename string_type::size_type __sep = __d.find('.');
+  if (__sep != string_type::npos)
+    __d.erase(__sep);
+#    else
   switch (__d.size()) {
   case 1:
     break;
@@ -1140,6 +1152,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
     __d.clear();
     break;
   }
+#    endif
   return __d;
 }
 
@@ -1150,6 +1163,15 @@ typename regex_traits<_CharT>::string_type
 regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, wchar_t) const {
   const string_type __s(__f, __l);
   string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
+#      if defined(__FreeBSD__)
+  // As in the char overload above, FreeBSD's wcsxfrm() separates the collation
+  // levels with a weight (the value 1) that sorts below every real weight.
+  // Keep only the primary level so primary-equivalent characters compare equal
+  // regardless of how many weights their lower levels expand to.
+  typename string_type::size_type __sep = __d.find(static_cast<_CharT>(1));
+  if (__sep != string_type::npos)
+    __d.erase(__sep);
+#      else
   switch (__d.size()) {
   case 1:
     break;
@@ -1160,6 +1182,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
     __d.clear();
     break;
   }
+#      endif
   return __d;
 }
 #    endif
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..ff866e6031b4a 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -10,7 +10,6 @@
 // XFAIL: netbsd
 
 // XFAIL: LIBCXX-AIX-FIXME
-// XFAIL: LIBCXX-FREEBSD-FIXME
 
 // REQUIRES: locale.cs_CZ.ISO8859-2
 

>From 3178b71ff195b6ce2d1d9dcfb92d42294bf223aa Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Wed, 24 Jun 2026 02:55:40 +0800
Subject: [PATCH 02/10] fixup! Fix __transform_primary in FreeBSD

---
 libcxx/include/regex                                    | 4 ++--
 libcxx/test/std/re/re.traits/transform_primary.pass.cpp | 6 ++++++
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/regex b/libcxx/include/regex
index 6f5109a64da22..ab06bd6387b6f 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -1140,7 +1140,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
   // which the fixed-size logic below could not handle.
   typename string_type::size_type __sep = __d.find('.');
   if (__sep != string_type::npos)
-    __d.erase(__sep);
+	  __d.erase(__sep, string_type::npos);
 #    else
   switch (__d.size()) {
   case 1:
@@ -1170,7 +1170,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
   // regardless of how many weights their lower levels expand to.
   typename string_type::size_type __sep = __d.find(static_cast<_CharT>(1));
   if (__sep != string_type::npos)
-    __d.erase(__sep);
+    __d.erase(__sep, string_type::npos);
 #      else
   switch (__d.size()) {
   case 1:
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index ff866e6031b4a..26bddb6636b12 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -33,25 +33,31 @@ int main(int, char**)
     {
         std::regex_traits<char> t;
         const char A[] = "A";
+        const char B[] = "B";
         const char Aacute[] = "\xC1";
         typedef forward_iterator<const char*> F;
         assert(t.transform_primary(F(A), F(A+1)) !=
                t.transform_primary(F(Aacute), F(Aacute+1)));
+        assert(t.transform_primary(F(B), F(B + 1)) != t.transform_primary(F(Aacute), F(Aacute + 1)));
         t.imbue(std::locale(LOCALE_cs_CZ_ISO8859_2));
         assert(t.transform_primary(F(A), F(A+1)) ==
                t.transform_primary(F(Aacute), F(Aacute+1)));
+        assert(t.transform_primary(F(B), F(B + 1)) != t.transform_primary(F(Aacute), F(Aacute + 1)));
     }
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
     {
         std::regex_traits<wchar_t> t;
         const wchar_t A[] = L"A";
+        const wchar_t B[] = L"B";
         const wchar_t Aacute[] = L"\xC1";
         typedef forward_iterator<const wchar_t*> F;
         assert(t.transform_primary(F(A), F(A+1)) !=
                t.transform_primary(F(Aacute), F(Aacute+1)));
+        assert(t.transform_primary(F(B), F(B + 1)) != t.transform_primary(F(Aacute), F(Aacute + 1)));
         t.imbue(std::locale(LOCALE_cs_CZ_ISO8859_2));
         assert(t.transform_primary(F(A), F(A+1)) ==
                t.transform_primary(F(Aacute), F(Aacute+1)));
+        assert(t.transform_primary(F(B), F(B + 1)) != t.transform_primary(F(Aacute), F(Aacute + 1)));
     }
 #endif
 

>From bd3b5da3a74d847ca3cbf8020033618ca833d79d Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Wed, 24 Jun 2026 02:58:34 +0800
Subject: [PATCH 03/10] fixup! Fix __transform_primary in FreeBSD

---
 libcxx/include/regex | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/regex b/libcxx/include/regex
index ab06bd6387b6f..38ce1fb715ad6 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -1140,7 +1140,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
   // which the fixed-size logic below could not handle.
   typename string_type::size_type __sep = __d.find('.');
   if (__sep != string_type::npos)
-	  __d.erase(__sep, string_type::npos);
+    __d.erase(__sep, string_type::npos);
 #    else
   switch (__d.size()) {
   case 1:

>From 07fcb3a18ecb929219796f6c39a1d0e3bf0914df Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Wed, 24 Jun 2026 03:00:31 +0800
Subject: [PATCH 04/10] fixup! Fix __transform_primary in FreeBSD

---
 libcxx/test/std/re/re.traits/transform_primary.pass.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 26bddb6636b12..82ae7f7e77372 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -33,7 +33,7 @@ int main(int, char**)
     {
         std::regex_traits<char> t;
         const char A[] = "A";
-        const char B[] = "B";
+        const char B[]      = "B";
         const char Aacute[] = "\xC1";
         typedef forward_iterator<const char*> F;
         assert(t.transform_primary(F(A), F(A+1)) !=
@@ -48,7 +48,7 @@ int main(int, char**)
     {
         std::regex_traits<wchar_t> t;
         const wchar_t A[] = L"A";
-        const wchar_t B[] = L"B";
+        const wchar_t B[]      = L"B";
         const wchar_t Aacute[] = L"\xC1";
         typedef forward_iterator<const wchar_t*> F;
         assert(t.transform_primary(F(A), F(A+1)) !=

>From c30dbd19cdaef941866e96d8cafdd4ddebc9e4c4 Mon Sep 17 00:00:00 2001
From: aokblast <aokblast at FreeBSD.org>
Date: Tue, 23 Jun 2026 16:41:55 -0300
Subject: [PATCH 05/10] fixup! Fix __transform_primary in FreeBSD

---
 libcxx/include/regex | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/regex b/libcxx/include/regex
index 38ce1fb715ad6..43731926e85fb 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -1141,6 +1141,13 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
   typename string_type::size_type __sep = __d.find('.');
   if (__sep != string_type::npos)
     __d.erase(__sep, string_type::npos);
+#    elif defined(__GLIBC__)
+  // glibc's strxfrm() likewise writes the whole primary level first, then a
+  // '\1' level separator (and a trailing '\0' after the last level); real
+  // weight bytes are always >= 2.  Keep only the primary level, as above.
+  typename string_type::size_type __sep = __d.find('\1');
+  if (__sep != string_type::npos)
+    __d.erase(__sep, string_type::npos);
 #    else
   switch (__d.size()) {
   case 1:
@@ -1163,11 +1170,11 @@ typename regex_traits<_CharT>::string_type
 regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, wchar_t) const {
   const string_type __s(__f, __l);
   string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
-#      if defined(__FreeBSD__)
-  // As in the char overload above, FreeBSD's wcsxfrm() separates the collation
-  // levels with a weight (the value 1) that sorts below every real weight.
-  // Keep only the primary level so primary-equivalent characters compare equal
-  // regardless of how many weights their lower levels expand to.
+#      if defined(__FreeBSD__) || defined(__GLIBC__)
+  // As in the char overload above, both FreeBSD's and glibc's wcsxfrm() separate
+  // the collation levels with a weight (the value 1) that sorts below every real
+  // weight.  Keep only the primary level so primary-equivalent characters
+  // compare equal regardless of how many weights their lower levels expand to.
   typename string_type::size_type __sep = __d.find(static_cast<_CharT>(1));
   if (__sep != string_type::npos)
     __d.erase(__sep, string_type::npos);

>From 16aca8fbbbfead0ad721c1d3e379620e7c39cb0a Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Wed, 24 Jun 2026 21:57:19 +0800
Subject: [PATCH 06/10] fixup! Fix __transform_primary in FreeBSD

---
 .../re/re.traits/transform_primary.pass.cpp   | 22 +++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 82ae7f7e77372..4e8ad5a73fb2a 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -33,31 +33,53 @@ int main(int, char**)
     {
         std::regex_traits<char> t;
         const char A[] = "A";
+        const char AA[]      = "AA";
         const char B[]      = "B";
+        const char BB[]      = "BB";
         const char Aacute[] = "\xC1";
+        const char AAacute[] = "\xC1\xC1";
         typedef forward_iterator<const char*> F;
         assert(t.transform_primary(F(A), F(A+1)) !=
                t.transform_primary(F(Aacute), F(Aacute+1)));
         assert(t.transform_primary(F(B), F(B + 1)) != t.transform_primary(F(Aacute), F(Aacute + 1)));
+        assert(t.transform_primary(F(A), F(A + 1)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+        assert(t.transform_primary(F(AA), F(AA + 2)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+        assert(t.transform_primary(F(BB), F(BB + 2)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+        assert(t.transform_primary(F(AA), F(AA + 2)) != t.transform_primary(F(BB), F(BB + 2)));
         t.imbue(std::locale(LOCALE_cs_CZ_ISO8859_2));
         assert(t.transform_primary(F(A), F(A+1)) ==
                t.transform_primary(F(Aacute), F(Aacute+1)));
         assert(t.transform_primary(F(B), F(B + 1)) != t.transform_primary(F(Aacute), F(Aacute + 1)));
+        assert(t.transform_primary(F(A), F(A + 1)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+        assert(t.transform_primary(F(AA), F(AA + 2)) == t.transform_primary(F(AAacute), F(AAacute + 2)));
+        assert(t.transform_primary(F(BB), F(BB + 2)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+        assert(t.transform_primary(F(AA), F(AA + 2)) != t.transform_primary(F(BB), F(BB + 2)));
     }
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
     {
         std::regex_traits<wchar_t> t;
         const wchar_t A[] = L"A";
+        const wchar_t AA[]      = L"AA";
         const wchar_t B[]      = L"B";
+        const wchar_t BB[]      = L"BB";
         const wchar_t Aacute[] = L"\xC1";
+        const wchar_t AAacute[] = L"\xC1\xC1";
         typedef forward_iterator<const wchar_t*> F;
         assert(t.transform_primary(F(A), F(A+1)) !=
                t.transform_primary(F(Aacute), F(Aacute+1)));
         assert(t.transform_primary(F(B), F(B + 1)) != t.transform_primary(F(Aacute), F(Aacute + 1)));
+        assert(t.transform_primary(F(A), F(A + 1)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+        assert(t.transform_primary(F(AA), F(AA + 2)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+        assert(t.transform_primary(F(BB), F(BB + 2)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+        assert(t.transform_primary(F(AA), F(AA + 2)) != t.transform_primary(F(BB), F(BB + 2)));
         t.imbue(std::locale(LOCALE_cs_CZ_ISO8859_2));
         assert(t.transform_primary(F(A), F(A+1)) ==
                t.transform_primary(F(Aacute), F(Aacute+1)));
         assert(t.transform_primary(F(B), F(B + 1)) != t.transform_primary(F(Aacute), F(Aacute + 1)));
+        assert(t.transform_primary(F(A), F(A + 1)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+        assert(t.transform_primary(F(AA), F(AA + 2)) == t.transform_primary(F(AAacute), F(AAacute + 2)));
+        assert(t.transform_primary(F(BB), F(BB + 2)) != t.transform_primary(F(AAacute), F(AAacute + 2)));
+        assert(t.transform_primary(F(AA), F(AA + 2)) != t.transform_primary(F(BB), F(BB + 2)));
     }
 #endif
 

>From 2d49f4f60fff4439e49cffc521547712e86df870 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Thu, 25 Jun 2026 00:55:19 +0800
Subject: [PATCH 07/10] fixup! Fix __transform_primary in FreeBSD

---
 libcxx/include/__cxx03/regex                  | 30 +++++++++++++++++++
 .../re/re.traits/transform_primary.pass.cpp   |  4 +--
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__cxx03/regex b/libcxx/include/__cxx03/regex
index bbd6eeee19ee9..6489c01929cee 100644
--- a/libcxx/include/__cxx03/regex
+++ b/libcxx/include/__cxx03/regex
@@ -1108,6 +1108,25 @@ typename regex_traits<_CharT>::string_type
 regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, char) const {
   const string_type __s(__f, __l);
   string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
+#    if defined(__FreeBSD__)
+  // FreeBSD's strxfrm() emits the weights one collation level at a time,
+  // separating the levels with a byte ('.') that is chosen to sort below every
+  // weight byte.  Primary equivalence only depends on the first (primary)
+  // level, so drop everything from the first separator onwards.  This keeps
+  // characters that share a primary weight equal (e.g. 'A' and 'A-acute' in
+  // cs_CZ) even when the lower levels expand to a different number of weights,
+  // which the fixed-size logic below could not handle.
+  typename string_type::size_type __sep = __d.find('.');
+  if (__sep != string_type::npos)
+    __d.erase(__sep, string_type::npos);
+#    elif defined(__GLIBC__)
+  // glibc's strxfrm() likewise writes the whole primary level first, then a
+  // '\1' level separator (and a trailing '\0' after the last level); real
+  // weight bytes are always >= 2.  Keep only the primary level, as above.
+  typename string_type::size_type __sep = __d.find('\1');
+  if (__sep != string_type::npos)
+    __d.erase(__sep, string_type::npos);
+#    else
   switch (__d.size()) {
   case 1:
     break;
@@ -1118,6 +1137,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
     __d.clear();
     break;
   }
+#endif
   return __d;
 }
 
@@ -1128,6 +1148,15 @@ typename regex_traits<_CharT>::string_type
 regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, wchar_t) const {
   const string_type __s(__f, __l);
   string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
+#      if defined(__FreeBSD__) || defined(__GLIBC__)
+  // As in the char overload above, both FreeBSD's and glibc's wcsxfrm() separate
+  // the collation levels with a weight (the value 1) that sorts below every real
+  // weight.  Keep only the primary level so primary-equivalent characters
+  // compare equal regardless of how many weights their lower levels expand to.
+  typename string_type::size_type __sep = __d.find(static_cast<_CharT>(1));
+  if (__sep != string_type::npos)
+    __d.erase(__sep, string_type::npos);
+#      else
   switch (__d.size()) {
   case 1:
     break;
@@ -1138,6 +1167,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
     __d.clear();
     break;
   }
+#endif
   return __d;
 }
 #endif
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 4e8ad5a73fb2a..d0e96132b623d 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -36,7 +36,7 @@ int main(int, char**)
         const char AA[]      = "AA";
         const char B[]      = "B";
         const char BB[]      = "BB";
-        const char Aacute[] = "\xC1";
+        const char Aacute[]  = "\xC1";
         const char AAacute[] = "\xC1\xC1";
         typedef forward_iterator<const char*> F;
         assert(t.transform_primary(F(A), F(A+1)) !=
@@ -60,7 +60,7 @@ int main(int, char**)
         std::regex_traits<wchar_t> t;
         const wchar_t A[] = L"A";
         const wchar_t AA[]      = L"AA";
-        const wchar_t B[]      = L"B";
+        const wchar_t B[]       = L"B";
         const wchar_t BB[]      = L"BB";
         const wchar_t Aacute[] = L"\xC1";
         const wchar_t AAacute[] = L"\xC1\xC1";

>From a69b6c5ef014dd0678028bd6ae603c1b1ba1336a Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Thu, 25 Jun 2026 01:00:06 +0800
Subject: [PATCH 08/10] fixup! Fix __transform_primary in FreeBSD

---
 libcxx/include/__cxx03/regex                            | 4 ++--
 libcxx/test/std/re/re.traits/transform_primary.pass.cpp | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__cxx03/regex b/libcxx/include/__cxx03/regex
index 6489c01929cee..88fd6fd46e158 100644
--- a/libcxx/include/__cxx03/regex
+++ b/libcxx/include/__cxx03/regex
@@ -1137,7 +1137,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
     __d.clear();
     break;
   }
-#endif
+#      endif
   return __d;
 }
 
@@ -1167,7 +1167,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
     __d.clear();
     break;
   }
-#endif
+#      endif
   return __d;
 }
 #endif
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index d0e96132b623d..b86804b6eed3a 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -34,7 +34,7 @@ int main(int, char**)
         std::regex_traits<char> t;
         const char A[] = "A";
         const char AA[]      = "AA";
-        const char B[]      = "B";
+        const char B[]       = "B";
         const char BB[]      = "BB";
         const char Aacute[]  = "\xC1";
         const char AAacute[] = "\xC1\xC1";

>From 34d42ae2fbb3ae7e036f78230974439ec317b1be Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Thu, 25 Jun 2026 01:04:56 +0800
Subject: [PATCH 09/10] fixup! Fix __transform_primary in FreeBSD

---
 libcxx/include/__cxx03/regex | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__cxx03/regex b/libcxx/include/__cxx03/regex
index 88fd6fd46e158..9ada638babf45 100644
--- a/libcxx/include/__cxx03/regex
+++ b/libcxx/include/__cxx03/regex
@@ -1108,7 +1108,7 @@ typename regex_traits<_CharT>::string_type
 regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, char) const {
   const string_type __s(__f, __l);
   string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
-#    if defined(__FreeBSD__)
+#if defined(__FreeBSD__)
   // FreeBSD's strxfrm() emits the weights one collation level at a time,
   // separating the levels with a byte ('.') that is chosen to sort below every
   // weight byte.  Primary equivalence only depends on the first (primary)
@@ -1119,14 +1119,14 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
   typename string_type::size_type __sep = __d.find('.');
   if (__sep != string_type::npos)
     __d.erase(__sep, string_type::npos);
-#    elif defined(__GLIBC__)
+#elif defined(__GLIBC__)
   // glibc's strxfrm() likewise writes the whole primary level first, then a
   // '\1' level separator (and a trailing '\0' after the last level); real
   // weight bytes are always >= 2.  Keep only the primary level, as above.
   typename string_type::size_type __sep = __d.find('\1');
   if (__sep != string_type::npos)
     __d.erase(__sep, string_type::npos);
-#    else
+#else
   switch (__d.size()) {
   case 1:
     break;
@@ -1137,7 +1137,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
     __d.clear();
     break;
   }
-#      endif
+#endif
   return __d;
 }
 
@@ -1148,7 +1148,7 @@ typename regex_traits<_CharT>::string_type
 regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, wchar_t) const {
   const string_type __s(__f, __l);
   string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
-#      if defined(__FreeBSD__) || defined(__GLIBC__)
+#  if defined(__FreeBSD__) || defined(__GLIBC__)
   // As in the char overload above, both FreeBSD's and glibc's wcsxfrm() separate
   // the collation levels with a weight (the value 1) that sorts below every real
   // weight.  Keep only the primary level so primary-equivalent characters
@@ -1156,7 +1156,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
   typename string_type::size_type __sep = __d.find(static_cast<_CharT>(1));
   if (__sep != string_type::npos)
     __d.erase(__sep, string_type::npos);
-#      else
+#  else
   switch (__d.size()) {
   case 1:
     break;
@@ -1167,7 +1167,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
     __d.clear();
     break;
   }
-#      endif
+#  endif
   return __d;
 }
 #endif

>From e25ab48dcb1b3f7ecb15b2941b29d822c82a7b9f Mon Sep 17 00:00:00 2001
From: aokblast <aokblast at FreeBSD.org>
Date: Fri, 26 Jun 2026 12:09:14 -0300
Subject: [PATCH 10/10] fixup! Fix __transform_primary in FreeBSD

---
 libcxx/include/__cxx03/regex | 28 +++++++++++++++-------------
 libcxx/include/regex         | 28 +++++++++++++++-------------
 2 files changed, 30 insertions(+), 26 deletions(-)

diff --git a/libcxx/include/__cxx03/regex b/libcxx/include/__cxx03/regex
index 9ada638babf45..a0564c91e26c0 100644
--- a/libcxx/include/__cxx03/regex
+++ b/libcxx/include/__cxx03/regex
@@ -1108,14 +1108,15 @@ typename regex_traits<_CharT>::string_type
 regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, char) const {
   const string_type __s(__f, __l);
   string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
-#if defined(__FreeBSD__)
-  // FreeBSD's strxfrm() emits the weights one collation level at a time,
-  // separating the levels with a byte ('.') that is chosen to sort below every
-  // weight byte.  Primary equivalence only depends on the first (primary)
-  // level, so drop everything from the first separator onwards.  This keeps
-  // characters that share a primary weight equal (e.g. 'A' and 'A-acute' in
-  // cs_CZ) even when the lower levels expand to a different number of weights,
-  // which the fixed-size logic below could not handle.
+#if defined(__FreeBSD__) || defined(__APPLE__)
+  // FreeBSD's (and Darwin's, which shares the same collation code) strxfrm()
+  // emits the weights one collation level at a time, separating the levels with
+  // a byte ('.') that is chosen to sort below every weight byte.  Primary
+  // equivalence only depends on the first (primary) level, so drop everything
+  // from the first separator onwards.  This keeps characters that share a
+  // primary weight equal (e.g. 'A' and 'A-acute' in cs_CZ) even when the lower
+  // levels expand to a different number of weights, which the fixed-size logic
+  // below could not handle.
   typename string_type::size_type __sep = __d.find('.');
   if (__sep != string_type::npos)
     __d.erase(__sep, string_type::npos);
@@ -1148,11 +1149,12 @@ typename regex_traits<_CharT>::string_type
 regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, wchar_t) const {
   const string_type __s(__f, __l);
   string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
-#  if defined(__FreeBSD__) || defined(__GLIBC__)
-  // As in the char overload above, both FreeBSD's and glibc's wcsxfrm() separate
-  // the collation levels with a weight (the value 1) that sorts below every real
-  // weight.  Keep only the primary level so primary-equivalent characters
-  // compare equal regardless of how many weights their lower levels expand to.
+#  if defined(__FreeBSD__) || defined(__APPLE__) || defined(__GLIBC__)
+  // As in the char overload above, FreeBSD's, Darwin's and glibc's wcsxfrm() all
+  // separate the collation levels with a weight (the value 1) that sorts below
+  // every real weight.  Keep only the primary level so primary-equivalent
+  // characters compare equal regardless of how many weights their lower levels
+  // expand to.
   typename string_type::size_type __sep = __d.find(static_cast<_CharT>(1));
   if (__sep != string_type::npos)
     __d.erase(__sep, string_type::npos);
diff --git a/libcxx/include/regex b/libcxx/include/regex
index 43731926e85fb..57f670b24da38 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -1130,14 +1130,15 @@ typename regex_traits<_CharT>::string_type
 regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, char) const {
   const string_type __s(__f, __l);
   string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
-#    if defined(__FreeBSD__)
-  // FreeBSD's strxfrm() emits the weights one collation level at a time,
-  // separating the levels with a byte ('.') that is chosen to sort below every
-  // weight byte.  Primary equivalence only depends on the first (primary)
-  // level, so drop everything from the first separator onwards.  This keeps
-  // characters that share a primary weight equal (e.g. 'A' and 'A-acute' in
-  // cs_CZ) even when the lower levels expand to a different number of weights,
-  // which the fixed-size logic below could not handle.
+#    if defined(__FreeBSD__) || defined(__APPLE__)
+  // FreeBSD's (and Darwin's, which shares the same collation code) strxfrm()
+  // emits the weights one collation level at a time, separating the levels with
+  // a byte ('.') that is chosen to sort below every weight byte.  Primary
+  // equivalence only depends on the first (primary) level, so drop everything
+  // from the first separator onwards.  This keeps characters that share a
+  // primary weight equal (e.g. 'A' and 'A-acute' in cs_CZ) even when the lower
+  // levels expand to a different number of weights, which the fixed-size logic
+  // below could not handle.
   typename string_type::size_type __sep = __d.find('.');
   if (__sep != string_type::npos)
     __d.erase(__sep, string_type::npos);
@@ -1170,11 +1171,12 @@ typename regex_traits<_CharT>::string_type
 regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, wchar_t) const {
   const string_type __s(__f, __l);
   string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
-#      if defined(__FreeBSD__) || defined(__GLIBC__)
-  // As in the char overload above, both FreeBSD's and glibc's wcsxfrm() separate
-  // the collation levels with a weight (the value 1) that sorts below every real
-  // weight.  Keep only the primary level so primary-equivalent characters
-  // compare equal regardless of how many weights their lower levels expand to.
+#      if defined(__FreeBSD__) || defined(__APPLE__) || defined(__GLIBC__)
+  // As in the char overload above, FreeBSD's, Darwin's and glibc's wcsxfrm() all
+  // separate the collation levels with a weight (the value 1) that sorts below
+  // every real weight.  Keep only the primary level so primary-equivalent
+  // characters compare equal regardless of how many weights their lower levels
+  // expand to.
   typename string_type::size_type __sep = __d.find(static_cast<_CharT>(1));
   if (__sep != string_type::npos)
     __d.erase(__sep, string_type::npos);


From libcxx-commits at lists.llvm.org  Wed Jul 15 00:46:50 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Wed, 15 Jul 2026 00:46:50 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209473@github.com>
Message-ID: <6a573aea.d1496011.26913c.8a50@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `clang-s390x-linux` running on `systemz-1` while building `clang-tools-extra,clang,cmake,flang,libcxx,lld,llvm` at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/42/builds/9379

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 5 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'LLVM :: CodeGen/SystemZ/zos-ppa2.ll' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
sed -e 's/!"MODE"/!"ascii"/' -e 's/BYTE/85/' /home/uweigand/sandbox/buildbot/clang-s390x-linux/llvm/llvm/test/CodeGen/SystemZ/zos-ppa2.ll > /home/uweigand/sandbox/buildbot/clang-s390x-linux/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: sed -e 's/!"MODE"/!"ascii"/' -e s/BYTE/85/ /home/uweigand/sandbox/buildbot/clang-s390x-linux/llvm/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
# RUN: at line 2
/home/uweigand/sandbox/buildbot/clang-s390x-linux/stage1/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true < /home/uweigand/sandbox/buildbot/clang-s390x-linux/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll |  /home/uweigand/sandbox/buildbot/clang-s390x-linux/stage1/bin/FileCheck /home/uweigand/sandbox/buildbot/clang-s390x-linux/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: /home/uweigand/sandbox/buildbot/clang-s390x-linux/stage1/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true
# executed command: /home/uweigand/sandbox/buildbot/clang-s390x-linux/stage1/bin/FileCheck /home/uweigand/sandbox/buildbot/clang-s390x-linux/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# .---command stderr------------
# | /home/uweigand/sandbox/buildbot/clang-s390x-linux/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll:24:10: error: CHECK: expected string not found in input
# | ; CHECK: DC XL6'F2F3F0F0F0F0'
# |          ^
# | <stdin>:17:39: note: scanning from here
# |  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0' 
# |                                       ^
# | <stdin>:18:2: note: possible intended match here
# |  DC XL6'F2F4F0F0F0F0' 
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /home/uweigand/sandbox/buildbot/clang-s390x-linux/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             .
# |             .
# |             .
# |            12:  DC XL4'00000000'  
# |            13:  DC XL1'85'  
# |            14:  DC XL1'00'  
# |            15:  DC XL2'0000'  
# |            16: L#DVS DS 0H  
# |            17:  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0'  
# | check:24'0                                          {    search range start (exclusive)
# | check:24'1                                               error: no match found in search range
# |            18:  DC XL6'F2F4F0F0F0F0'  
# | check:24'2      ?                       possible intended match
# |            19:  DC XL2'0000'  
# |            20: stdin#C CSECT  
# |            21: C_@@QPPA2 CATTR ALIGN(3),FILL(0),NOTEXECUTABLE,READONLY,RMODE(64),PART(.  
# |            22:  .&ppa2)  
# |            23: .&ppa2 XATTR LINKAGE(OS),REFERENCE(DATA),SCOPE(SECTION)  
# |             .
# |             .
...

```

</details>

https://github.com/llvm/llvm-project/pull/209473

From libcxx-commits at lists.llvm.org  Wed Jul 15 02:22:50 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 15 Jul 2026 02:22:50 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [Clang][AIX] Fix fopen 'x' mode
 via open(O_CREAT|O_EXCL)+fdopen (PR #209705)
Message-ID: <llvm.org/llvm/llvm-project/pull/209705@github.com>


https://github.com/Himadhith created https://github.com/llvm/llvm-project/pull/209705

AIX fopen(3) does not atomically handle the C11/C23 'x' mode.
Redirect fopen(path, "...x...") to a helper using open(O_CREAT|
O_EXCL)+fdopen. Remove XFAIL: LIBCXX-AIX-FIXME from fstream tests.

>From 0e35816ce0a42f3e497f97f791c7dca3c23ca758 Mon Sep 17 00:00:00 2001
From: himadhith <himadhith.v at ibm.com>
Date: Wed, 15 Jul 2026 14:41:42 +0530
Subject: [PATCH] [Clang][AIX] Fix fopen 'x' mode via
 open(O_CREAT|O_EXCL)+fdopen

AIX fopen(3) does not atomically handle the C11/C23 'x' mode.
Redirect fopen(path, "...x...") to a helper using open(O_CREAT|
O_EXCL)+fdopen. Remove XFAIL: LIBCXX-AIX-FIXME from fstream tests.
---
 clang/lib/CodeGen/CGExpr.cpp                  |  21 +++
 clang/lib/CodeGen/CodeGenModule.cpp           | 176 ++++++++++++++++++
 clang/lib/CodeGen/CodeGenModule.h             |   4 +
 .../filebuf.members/open_pointer.pass.cpp     |   2 -
 .../fstreams/fstream.cons/pointer.pass.cpp    |   2 -
 .../fstream.members/open_pointer.pass.cpp     |   2 -
 .../fstreams/ofstream.cons/pointer.pass.cpp   |   2 -
 .../ofstream.members/open_pointer.pass.cpp    |   2 -
 8 files changed, 201 insertions(+), 10 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 6a0d127fb451b..7f42ac03bfa98 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -6517,6 +6517,27 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E,
     return EmitCXXPseudoDestructorExpr(callee.getPseudoDestructorExpr());
   }
 
+  // On AIX, redirect fopen(path, "...x...") to a helper that uses
+  // open(O_CREAT|O_EXCL)+fdopen, which handles exclusive creation atomically.
+  if (CGM.getTriple().isOSAIX()) {
+    if (const FunctionDecl *FD = E->getDirectCallee()) {
+      if (FD->getDeclName().isIdentifier() &&
+          FD->getName() == "fopen" && E->getNumArgs() == 2) {
+        const Expr *ModeArg = E->getArg(1)->IgnoreImpCasts();
+        if (const StringLiteral *SL = dyn_cast<StringLiteral>(ModeArg)) {
+          if (SL->getString().contains('x')) {
+            llvm::Function *Helper = CGM.getOrCreateAIXFOpenExclusiveHelper();
+            llvm::Value *PathVal = EmitScalarExpr(E->getArg(0));
+            llvm::Value *ModeVal = EmitScalarExpr(E->getArg(1));
+            llvm::CallInst *CI = Builder.CreateCall(Helper, {PathVal, ModeVal});
+            CI->setCallingConv(Helper->getCallingConv());
+            return RValue::get(CI);
+          }
+        }
+      }
+    }
+  }
+
   return EmitCall(E->getCallee()->getType(), callee, E, ReturnValue,
                   /*Chain=*/nullptr, CallOrInvoke);
 }
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index e155fdd752d7f..4d3449d2476f6 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -57,8 +57,12 @@
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/IR/AttributeMask.h"
+#include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/CallingConv.h"
+#include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
@@ -8855,3 +8859,175 @@ void CodeGenModule::requireVectorDestructorDefinition(const CXXRecordDecl *RD) {
   // even if destructor is only declared.
   addDeferredDeclToEmit(VectorDtorGD);
 }
+
+// Stable open(2) flag values from AIX <fcntl.h>.
+static constexpr unsigned AIX_O_RDONLY  = 0x0;
+static constexpr unsigned AIX_O_WRONLY  = 0x1;
+static constexpr unsigned AIX_O_RDWR    = 0x2;
+static constexpr unsigned AIX_O_ACCMODE = 0x3;
+static constexpr unsigned AIX_O_APPEND  = 0x8;
+static constexpr unsigned AIX_O_CREAT   = 0x100;
+static constexpr unsigned AIX_O_TRUNC   = 0x200;
+static constexpr unsigned AIX_O_EXCL    = 0x400;
+
+// On AIX, fopen(3) does not atomically implement the C11/C23 'x' exclusive
+// mode. Lazily synthesises __aix_fopen_exclusive(path, mode) which uses
+// open(O_CREAT|O_EXCL)+fdopen so that exclusive creation is truly atomic.
+llvm::Function *CodeGenModule::getOrCreateAIXFOpenExclusiveHelper() {
+  assert(getTriple().isOSAIX() &&
+         "AIX fopen exclusive helper is only needed on AIX");
+
+  constexpr llvm::StringLiteral HelperName("__aix_fopen_exclusive");
+  llvm::Module &M = getModule();
+
+  if (llvm::Function *F = M.getFunction(HelperName))
+    return F;
+
+  llvm::LLVMContext &Ctx = getLLVMContext();
+  llvm::PointerType *PtrTy   = llvm::PointerType::getUnqual(Ctx);
+  llvm::IntegerType *Int32Ty = llvm::Type::getInt32Ty(Ctx);
+  llvm::IntegerType *Int8Ty  = llvm::Type::getInt8Ty(Ctx);
+  llvm::ArrayType   *Arr8Ty  = llvm::ArrayType::get(Int8Ty, 8);
+
+  llvm::Value *Zero32     = llvm::ConstantInt::get(Int32Ty, 0);
+  llvm::Value *One32      = llvm::ConstantInt::get(Int32Ty, 1);
+  llvm::Value *MinusOne32 = llvm::ConstantInt::getSigned(Int32Ty, -1);
+  llvm::Value *ZeroI8     = llvm::ConstantInt::get(Int8Ty, 0);
+  llvm::Value *NullPtr    = llvm::ConstantPointerNull::get(PtrTy);
+  llvm::Value *CreatExclFlags =
+      llvm::ConstantInt::get(Int32Ty, AIX_O_CREAT | AIX_O_EXCL);
+
+  llvm::FunctionType *FnTy =
+      llvm::FunctionType::get(PtrTy, {PtrTy, PtrTy}, /*isVarArg=*/false);
+  llvm::Function *Fn = llvm::Function::Create(
+      FnTy, llvm::GlobalValue::LinkOnceODRLinkage, HelperName, &M);
+  Fn->setVisibility(llvm::GlobalValue::HiddenVisibility);
+  Fn->addFnAttr(llvm::Attribute::NoUnwind);
+  Fn->setDSOLocal(true);
+
+  llvm::Argument *ArgPath = Fn->getArg(0);
+  llvm::Argument *ArgMode = Fn->getArg(1);
+  ArgPath->setName("path");
+  ArgMode->setName("mode");
+
+  llvm::FunctionCallee OpenFn = M.getOrInsertFunction(
+      "open",
+      llvm::FunctionType::get(Int32Ty, {PtrTy, Int32Ty}, /*isVarArg=*/true));
+  llvm::FunctionCallee FdopenFn = M.getOrInsertFunction(
+      "fdopen",
+      llvm::FunctionType::get(PtrTy, {Int32Ty, PtrTy}, /*isVarArg=*/false));
+  llvm::FunctionCallee CloseFn = M.getOrInsertFunction(
+      "close",
+      llvm::FunctionType::get(Int32Ty, {Int32Ty}, /*isVarArg=*/false));
+
+  using BB = llvm::BasicBlock;
+  llvm::IRBuilder<> B(Ctx);
+
+  BB *EntryBB      = BB::Create(Ctx, "entry",      Fn);
+  BB *OpenCallBB   = BB::Create(Ctx, "open.call",  Fn);
+  BB *OpenOkBB     = BB::Create(Ctx, "open.ok",    Fn);
+  BB *OpenFailBB   = BB::Create(Ctx, "open.fail",  Fn);
+  BB *FdopenOkBB   = BB::Create(Ctx, "fdopen.ok",  Fn);
+  BB *FdopenFailBB = BB::Create(Ctx, "fdopen.fail",Fn);
+  BB *RetBB        = BB::Create(Ctx, "ret",        Fn);
+
+  B.SetInsertPoint(EntryBB);
+
+  llvm::AllocaInst *BmodeBuf = B.CreateAlloca(Arr8Ty, nullptr, "bmode");
+  BmodeBuf->setAlignment(llvm::Align(1));
+
+  // Derive open(2) access flags from mode[0]: 'w'→O_WRONLY|O_TRUNC,
+  // 'a'→O_WRONLY|O_APPEND, otherwise O_RDONLY.
+  llvm::Value *Mode0 = B.CreateLoad(Int8Ty, ArgMode, "mode0");
+  llvm::Value *AccFlags = B.CreateSelect(
+      B.CreateICmpEQ(Mode0, llvm::ConstantInt::get(Int8Ty, 'w'), "is.w"),
+      llvm::ConstantInt::get(Int32Ty, AIX_O_WRONLY | AIX_O_TRUNC),
+      B.CreateSelect(
+          B.CreateICmpEQ(Mode0, llvm::ConstantInt::get(Int8Ty, 'a'), "is.a"),
+          llvm::ConstantInt::get(Int32Ty, AIX_O_WRONLY | AIX_O_APPEND),
+          llvm::ConstantInt::get(Int32Ty, AIX_O_RDONLY)),
+      "acc.flags");
+
+  // If '+' appears anywhere in mode, upgrade to O_RDWR (unrolled 6 iters).
+  llvm::Value *HasPlus = llvm::ConstantInt::get(llvm::Type::getInt1Ty(Ctx), 0);
+  for (int i = 0; i < 6; ++i) {
+    llvm::Value *Ch = B.CreateLoad(Int8Ty,
+        B.CreateConstGEP1_64(Int8Ty, ArgMode, i, ("mp" + llvm::Twine(i)).str()),
+        ("mc" + llvm::Twine(i)).str());
+    HasPlus = B.CreateOr(HasPlus,
+        B.CreateICmpEQ(Ch, llvm::ConstantInt::get(Int8Ty, '+'),
+            ("eq.plus" + llvm::Twine(i)).str()),
+        ("has.plus" + llvm::Twine(i)).str());
+  }
+  llvm::Value *AccMask = llvm::ConstantInt::get(Int32Ty, ~AIX_O_ACCMODE);
+  llvm::Value *AccFlagsFinal = B.CreateSelect(HasPlus,
+      B.CreateOr(B.CreateAnd(AccFlags, AccMask),
+                 llvm::ConstantInt::get(Int32Ty, AIX_O_RDWR)),
+      AccFlags, "acc.final");
+
+  llvm::Value *OpenFlags =
+      B.CreateOr(AccFlagsFinal, CreatExclFlags, "open.flags");
+
+  // Build a mode string without 'x' in BmodeBuf (unrolled 6 iters).
+  llvm::AllocaInst *WIdxAlloca = B.CreateAlloca(Int32Ty, nullptr, "widx");
+  B.CreateStore(Zero32, WIdxAlloca);
+  llvm::Value *XChar = llvm::ConstantInt::get(Int8Ty, 'x');
+  for (int i = 0; i < 6; ++i) {
+    llvm::Value *Ch = B.CreateLoad(Int8Ty,
+        B.CreateConstGEP1_64(Int8Ty, ArgMode, i, ("sp" + llvm::Twine(i)).str()),
+        ("ch" + llvm::Twine(i)).str());
+    BB *DoStore   = BB::Create(Ctx, ("store" + llvm::Twine(i)).str(), Fn);
+    BB *AfterIter = BB::Create(Ctx, ("after" + llvm::Twine(i)).str(), Fn);
+    B.CreateCondBr(
+        B.CreateOr(B.CreateICmpEQ(Ch, XChar), B.CreateICmpEQ(Ch, ZeroI8),
+            ("skip" + llvm::Twine(i)).str()),
+        AfterIter, DoStore);
+    B.SetInsertPoint(DoStore);
+    llvm::Value *WIdx = B.CreateLoad(Int32Ty, WIdxAlloca, "wi");
+    B.CreateStore(Ch, B.CreateGEP(Arr8Ty, BmodeBuf,
+        {Zero32, WIdx}, ("dp" + llvm::Twine(i)).str()));
+    B.CreateStore(B.CreateAdd(WIdx, One32), WIdxAlloca);
+    B.CreateBr(AfterIter);
+    B.SetInsertPoint(AfterIter);
+  }
+  llvm::Value *WIdx = B.CreateLoad(Int32Ty, WIdxAlloca, "wi.fin");
+  B.CreateStore(ZeroI8,
+      B.CreateGEP(Arr8Ty, BmodeBuf, {Zero32, WIdx}, "nuldst"));
+  llvm::Value *BmodePtr =
+      B.CreateConstGEP2_32(Arr8Ty, BmodeBuf, 0, 0, "bmode.ptr");
+  B.CreateBr(OpenCallBB);
+
+  B.SetInsertPoint(OpenCallBB);
+  llvm::Value *Mode0666 = llvm::ConstantInt::get(Int32Ty, 0666);
+  llvm::CallInst *FdVal =
+      B.CreateCall(OpenFn, {ArgPath, OpenFlags, Mode0666}, "fd");
+  FdVal->setDoesNotThrow();
+  B.CreateCondBr(B.CreateICmpEQ(FdVal, MinusOne32, "open.fail.cmp"),
+                 OpenFailBB, OpenOkBB);
+
+  B.SetInsertPoint(OpenFailBB);
+  B.CreateBr(RetBB);
+
+  B.SetInsertPoint(OpenOkBB);
+  llvm::CallInst *FpVal =
+      B.CreateCall(FdopenFn, {FdVal, BmodePtr}, "fp");
+  FpVal->setDoesNotThrow();
+  B.CreateCondBr(B.CreateICmpEQ(FpVal, NullPtr, "fdopen.fail.cmp"),
+                 FdopenFailBB, FdopenOkBB);
+
+  B.SetInsertPoint(FdopenFailBB);
+  B.CreateCall(CloseFn, {FdVal})->setDoesNotThrow();
+  B.CreateBr(RetBB);
+
+  B.SetInsertPoint(FdopenOkBB);
+  B.CreateBr(RetBB);
+
+  B.SetInsertPoint(RetBB);
+  llvm::PHINode *Ret = B.CreatePHI(PtrTy, 3, "retval");
+  Ret->addIncoming(NullPtr, OpenFailBB);
+  Ret->addIncoming(NullPtr, FdopenFailBB);
+  Ret->addIncoming(FpVal,   FdopenOkBB);
+  B.CreateRet(Ret);
+
+  return Fn;
+}
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index b1e70a7c347db..4578ae2257a01 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1414,6 +1414,10 @@ class CodeGenModule : public CodeGenTypeCache {
   llvm::Constant *CreateRuntimeVariable(llvm::Type *Ty,
                                         StringRef Name);
 
+  // On AIX, fopen(3) with 'x' mode is not atomic. Lazily emits a helper
+  // __aix_fopen_exclusive that uses open(O_CREAT|O_EXCL)+fdopen instead.
+  llvm::Function *getOrCreateAIXFOpenExclusiveHelper();
+
   ///@name Custom Blocks Runtime Interfaces
   ///@{
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
index 9f617dc1e5a89..ade3affe03821 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp
@@ -13,8 +13,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
index 2e0ebcd684d79..3db29707c27e7 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
index 0d83d681b1dfc..a54444f4b2f53 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
index fbb03f1e85841..3c4a06c2efa21 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include <ios>
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
index 73a474277a933..6446ca16cb409 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ofstream.members/open_pointer.pass.cpp
@@ -16,8 +16,6 @@
 // In C++23 and later, this test requires support for P2467R1 in the dylib (a3f17ba3febbd546f2342ffc780ac93b694fdc8d)
 // XFAIL: (!c++03 && !c++11 && !c++14 && !c++17 && !c++20) && using-built-library-before-llvm-18
 
-// XFAIL: LIBCXX-AIX-FIXME
-
 #include <fstream>
 #include <cassert>
 #include "test_macros.h"


From libcxx-commits at lists.llvm.org  Wed Jul 15 02:23:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 15 Jul 2026 02:23:28 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [Clang][AIX] Fix fopen 'x' mode
 via open(O_CREAT|O_EXCL)+fdopen (PR #209705)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209705@github.com>
Message-ID: <6a575190.7270d2fd.2d4879.3125@mx.google.com>


https://github.com/Himadhith closed https://github.com/llvm/llvm-project/pull/209705

From libcxx-commits at lists.llvm.org  Wed Jul 15 04:30:09 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 15 Jul 2026 04:30:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a576f41.3cce8983.2f9901.dfdb@mx.google.com>


================

----------------
philnik777 wrote:

We're actually already batching for `push_back() (with reserve)`, and even increasing the batching by 10x still shows significant overhead. Notably, even the cheap element case shows significant overhead in some cases, so `is_trivially_destructible` probably isn't the right condition either. I've added them back for the `many elements` case though, since actually allocating in there makes the overhead mostly irrelevant.

https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Wed Jul 15 04:30:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 15 Jul 2026 04:30:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a576f46.449bfb44.1b765.1a61@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/209130

>From 61ddf4edc43c1f20f5637e31c1416cb61db04438 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 12:05:58 +0200
Subject: [PATCH] [libc++] Remove {Pause,Resume}Timing from fast push_back
 benchmarks

---
 .../containers/sequence/sequence_container_benchmarks.h        | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
index 92346c38563fb..00c2e0b01eba4 100644
--- a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
@@ -372,9 +372,7 @@ void sequence_container_benchmarks(std::string container) {
             }
             DoNotOptimizeData(c);
 
-            st.PauseTiming();
             c.erase(c.end() - BatchSize, c.end());
-            st.ResumeTiming();
           }
         });
     }
@@ -397,6 +395,7 @@ void sequence_container_benchmarks(std::string container) {
 
           st.PauseTiming();
           c.clear();
+          c = Container();
           st.ResumeTiming();
         }
       });


From libcxx-commits at lists.llvm.org  Wed Jul 15 04:49:39 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 15 Jul 2026 04:49:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a5773d3.c5504292.a1395.9487@mx.google.com>


philnik777 wrote:

> Minor comments, basically LGTM.
> 
> Committing a benchmark for this is quite difficult with our current `.bench.cpp` setup, so I think I'm satisfied if we just confirm that we get a speedup in the case reported in #21566.

I've just tested the code with this patch and `sync_with_stdio(false/true)` (using a random file on my system):

`sync_with_stdio(true)`:
real	0m0,390s
user	0m0,386s
sys	0m0,020s

`sync_with_stdio(false)`:
real	0m0,024s
user	0m0,017s
sys	0m0,016s

With libstdc++ and `sync_with_stdio(false)`:
real	0m0,018s
user	0m0,011s
sys	0m0,012s

Also, for comparison `wc -l`:
real	0m0,008s
user	0m0,000s
sys	0m0,008s

Looks like we still have some way to go to get to `wc` performance, but now we're at least getting somewhat close.



https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Wed Jul 15 04:55:50 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 15 Jul 2026 04:55:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 5b3b76e - [libc++] Create libc++ 24
	release notes (#208814)
Message-ID: <6a577546.6c5720b3.5d850.d427@mx.google.com>


Author: Louis Dionne
Date: 2026-07-15T07:55:45-04:00
New Revision: 5b3b76e77386b6e91c7627edb3beab64077e379f

URL: https://github.com/llvm/llvm-project/commit/5b3b76e77386b6e91c7627edb3beab64077e379f
DIFF: https://github.com/llvm/llvm-project/commit/5b3b76e77386b6e91c7627edb3beab64077e379f.diff

LOG: [libc++] Create libc++ 24 release notes (#208814)

Now that the LLVM 23 branch has been created, this adds LLVM 24 release
notes. The release notes contain a few TODOs which will be easy to
address in separate PRs.

Added: 
    libcxx/docs/ReleaseNotes/24.rst

Modified: 
    libcxx/docs/ReleaseNotes.rst

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index ff35abef61203..37078d83139ab 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -1,4 +1,4 @@
-.. include:: ReleaseNotes/23.rst
+.. include:: ReleaseNotes/24.rst
 
 .. Make sure to reference the non-live release notes in a toctree to avoid Sphinx errors.
 .. toctree::
@@ -7,6 +7,7 @@
     ReleaseNotes/20
     ReleaseNotes/21
     ReleaseNotes/22
+    ReleaseNotes/23
 
 .. The release notes are in versioned files, but we make sure to keep publishing
 .. them in an unversioned ReleaseNotes.html page for external sites to reference.

diff  --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
new file mode 100644
index 0000000000000..bcac37b8a717f
--- /dev/null
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -0,0 +1,74 @@
+===========================
+Libc++ 24.0.0 Release Notes
+===========================
+
+.. contents::
+   :local:
+   :depth: 2
+
+Written by the `Libc++ Team <https://libcxx.llvm.org>`_
+
+.. warning::
+
+   These are in-progress notes for the upcoming libc++ 24.0.0 release.
+   Release notes for previous releases can be found on
+   `the Download Page <https://releases.llvm.org/download.html>`_.
+
+Introduction
+============
+
+This document contains the release notes for the libc++ C++ Standard Library,
+part of the LLVM Compiler Infrastructure, release 24.0.0. Here we describe the
+status of libc++ in some detail, including major improvements from the previous
+release and new feature work. For the general LLVM release notes, see `the LLVM
+documentation <https://llvm.org/docs/ReleaseNotes.html>`_. All LLVM releases may
+be downloaded from the `LLVM releases web site <https://llvm.org/releases/>`_.
+
+For more information about libc++, please see the `Libc++ Web Site
+<https://libcxx.llvm.org>`_ or the `LLVM Web Site <https://llvm.org>`_.
+
+Note that if you are reading this file from a Git checkout or the
+main Libc++ web page, this document applies to the *next* release, not
+the current one. To see the release notes for a specific release, please
+see the `releases page <https://llvm.org/releases/>`_.
+
+What's New in Libc++ 24.0.0?
+==============================
+
+Implemented Papers
+------------------
+
+
+Improvements and New Features
+-----------------------------
+
+
+Deprecations and Removals
+-------------------------
+
+- The ``std::launch::any`` enumerator that was accidentally provided as an extension is now deprecated.
+  It will be removed in LLVM 25.
+
+Potentially breaking changes
+----------------------------
+
+- TODO: In LLVM 23, libc++ dropped a lot of transitive includes in all language modes. This improves compile time significantly,
+  but causes programs which rely on these includes to not compile anymore. The ``_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23``
+  macro that was provided in LLVM 23 to ease the transition has been removed in this release.
+
+Announcements About Future Releases
+-----------------------------------
+
+
+ABI Affecting Changes
+---------------------
+
+- TODO: ``std::allocator`` is trivially default constructible since LLVM 22. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR``
+  macro was provided as an escape hatch, but it has been removed in LLVM 24 since there was no evidence of it being useful.
+
+- TODO: ``bitset::operator[]`` returns ``bool`` since LLVM 22, fixing a conformance bug. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``
+  macro was provided as an escape hatch, but it has been removed in LLVM 24 since there was no evidence of it being useful.
+
+Build System Changes
+--------------------
+


        

From libcxx-commits at lists.llvm.org  Wed Jul 15 04:55:54 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 15 Jul 2026 04:55:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Create libc++ 24 release notes
	(PR #208814)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208814@github.com>
Message-ID: <6a57754a.b1416da1.26c0f8.b059@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208814

From libcxx-commits at lists.llvm.org  Wed Jul 15 04:57:10 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 15 Jul 2026 04:57:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a577596.0c05e68e.b358c.705c@mx.google.com>


ldionne wrote:

@huixie90 I added the LLVM 24 release notes and we branched for LLVM 23 now, so I think this can be rebased and merged.

https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Wed Jul 15 05:00:50 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 15 Jul 2026 05:00:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Avoid materializing input ranges
 prepended to deque (PR #199969)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/199969@github.com>
Message-ID: <6a577672.c574b9b4.2aa0bf.4215@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/199969

>From d92d397cfaaf75e4a3c11cd888fc922de950b41f Mon Sep 17 00:00:00 2001
From: bitree <bitree2004 at yandex.ru>
Date: Wed, 27 May 2026 14:43:26 +0300
Subject: [PATCH 1/2] [libc++] Avoid materializing input ranges prepended to
 deque

---
 libcxx/include/deque                          | 57 +++++++++++
 .../sequence/sequence_container_benchmarks.h  | 22 +++++
 .../input_range_insert_at_begin.pass.cpp      | 94 +++++++++++++++++++
 .../deque.modifiers/prepend_range.pass.cpp    | 78 +++++++++++++++
 4 files changed, 251 insertions(+)
 create mode 100644 libcxx/test/libcxx/containers/sequences/deque/input_range_insert_at_begin.pass.cpp

diff --git a/libcxx/include/deque b/libcxx/include/deque
index c8c6889f1a165..a8716920134ba 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -194,6 +194,7 @@ template <class T, class Allocator, class Predicate>
 #  include <__algorithm/ranges_copy_n.h>
 #  include <__algorithm/remove.h>
 #  include <__algorithm/remove_if.h>
+#  include <__algorithm/reverse.h>
 #  include <__assert>
 #  include <__config>
 #  include <__debug_utils/sanitizers.h>
@@ -224,6 +225,7 @@ template <class T, class Allocator, class Predicate>
 #  include <__type_traits/conditional.h>
 #  include <__type_traits/container_traits.h>
 #  include <__type_traits/enable_if.h>
+#  include <__type_traits/integral_constant.h>
 #  include <__type_traits/is_allocator.h>
 #  include <__type_traits/is_convertible.h>
 #  include <__type_traits/is_nothrow_assignable.h>
@@ -871,6 +873,8 @@ public:
       return __insert_with_size(__position, ranges::begin(__range), __n);
 
     } else {
+      if (__position == cbegin())
+        return __prepend_with_sentinel(ranges::begin(__range), ranges::end(__range));
       return __insert_with_sentinel(__position, ranges::begin(__range), ranges::end(__range));
     }
   }
@@ -1231,6 +1235,15 @@ private:
   template <class _Iterator, class _Sentinel>
   _LIBCPP_HIDE_FROM_ABI iterator __insert_with_sentinel(const_iterator __p, _Iterator __f, _Sentinel __l);
 
+#  ifndef _LIBCPP_CXX03_LANG
+  template <class _Iterator, class _Sentinel>
+  _LIBCPP_HIDE_FROM_ABI iterator __prepend_with_sentinel(_Iterator __f, _Sentinel __l);
+  template <class _Iterator, class _Sentinel>
+  _LIBCPP_HIDE_FROM_ABI iterator __prepend_with_sentinel(_Iterator __f, _Sentinel __l, true_type);
+  template <class _Iterator, class _Sentinel>
+  _LIBCPP_HIDE_FROM_ABI iterator __prepend_with_sentinel(_Iterator __f, _Sentinel __l, false_type);
+#  endif
+
   template <class _Iterator>
   _LIBCPP_HIDE_FROM_ABI iterator __insert_with_size(const_iterator __p, _Iterator __f, size_type __n);
 
@@ -1784,6 +1797,11 @@ template <class _Tp, class _Allocator>
 template <class _InputIter, __enable_if_t<__has_exactly_input_iterator_category<_InputIter>::value, int> >
 typename deque<_Tp, _Allocator>::iterator
 deque<_Tp, _Allocator>::insert(const_iterator __p, _InputIter __f, _InputIter __l) {
+#  ifndef _LIBCPP_CXX03_LANG
+  if (__p == cbegin())
+    return __prepend_with_sentinel(__f, __l);
+#  endif
+
   return __insert_with_sentinel(__p, __f, __l);
 }
 
@@ -1797,6 +1815,45 @@ deque<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __p, _Iterator __f
   return insert(__p, move_iterator<__bi>(__buf.begin()), move_iterator<__bi>(__buf.end()));
 }
 
+#  ifndef _LIBCPP_CXX03_LANG
+template <class _Tp, class _Allocator>
+template <class _Iterator, class _Sentinel>
+_LIBCPP_HIDE_FROM_ABI typename deque<_Tp, _Allocator>::iterator
+deque<_Tp, _Allocator>::__prepend_with_sentinel(_Iterator __f, _Sentinel __l) {
+  return __prepend_with_sentinel(
+      std::move(__f), std::move(__l), integral_constant<bool, __is_swappable_v<value_type> >());
+}
+
+template <class _Tp, class _Allocator>
+template <class _Iterator, class _Sentinel>
+_LIBCPP_HIDE_FROM_ABI typename deque<_Tp, _Allocator>::iterator
+deque<_Tp, _Allocator>::__prepend_with_sentinel(_Iterator __f, _Sentinel __l, true_type) {
+  size_type __inserted = 0;
+  auto __guard         = std::__make_exception_guard([&] {
+    while (__inserted > 0) {
+      pop_front();
+      --__inserted;
+    }
+  });
+
+  for (; __f != __l; ++__f) {
+    emplace_front(*__f);
+    ++__inserted;
+  }
+  std::reverse(begin(), begin() + __inserted);
+
+  __guard.__complete();
+  return begin();
+}
+
+template <class _Tp, class _Allocator>
+template <class _Iterator, class _Sentinel>
+_LIBCPP_HIDE_FROM_ABI typename deque<_Tp, _Allocator>::iterator
+deque<_Tp, _Allocator>::__prepend_with_sentinel(_Iterator __f, _Sentinel __l, false_type) {
+  return __insert_with_sentinel(cbegin(), std::move(__f), std::move(__l));
+}
+#  endif
+
 template <class _Tp, class _Allocator>
 template <class _ForwardIterator, __enable_if_t<__has_exactly_forward_iterator_category<_ForwardIterator>::value, int> >
 typename deque<_Tp, _Allocator>::iterator
diff --git a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
index 92346c38563fb..4373cb44dc917 100644
--- a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
@@ -447,6 +447,28 @@ void sequence_container_benchmarks(std::string container) {
           state.ResumeTiming();
         }
       });
+
+    for (auto gen : generators)
+      bench("prepend_range(input-iter) (full container)" + tostr(gen), [gen](auto& state) {
+        auto const size = state.range(0);
+        std::vector<ValueType> in;
+        std::generate_n(std::back_inserter(in), size, gen);
+        DoNotOptimizeData(in);
+
+        Container c(in.begin(), in.end());
+        DoNotOptimizeData(c);
+        for (auto _ : state) {
+          using Iter = cpp20_input_iterator<ValueType*>;
+          auto first = Iter(in.data());
+          auto last  = sentinel_wrapper<Iter>(Iter(in.data() + in.size()));
+          c.prepend_range(std::ranges::subrange(std::move(first), std::move(last)));
+          DoNotOptimizeData(c);
+
+          state.PauseTiming();
+          c.erase(c.begin(), std::next(c.begin(), size));
+          state.ResumeTiming();
+        }
+      });
   }
 
   /////////////////////////
diff --git a/libcxx/test/libcxx/containers/sequences/deque/input_range_insert_at_begin.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/input_range_insert_at_begin.pass.cpp
new file mode 100644
index 0000000000000..7d5794faa551a
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/deque/input_range_insert_at_begin.pass.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Check that inserting an unsized input range at the start of a deque uses
+// existing front spare capacity instead of materializing the range first.
+
+// UNSUPPORTED: c++03
+
+#include <cassert>
+#include <deque>
+
+#include "count_new.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+
+#if TEST_STD_VER >= 23
+#  include <ranges>
+#endif
+
+std::deque<int> make_deque_with_front_spare() {
+  std::deque<int> c;
+  for (int i = 0; i != 8; ++i)
+    c.push_back(100 + i);
+  for (int i = 0; i != 4; ++i)
+    c.pop_front();
+  return c;
+}
+
+void assert_expected(const std::deque<int>& c) {
+  int expected[] = {0, 1, 2, 3, 104, 105, 106, 107};
+  assert(c.size() == 8);
+  for (int i = 0; i != 8; ++i)
+    assert(c[i] == expected[i]);
+}
+
+void test_insert_iter_iter() {
+  std::deque<int> c = make_deque_with_front_spare();
+  int input[]       = {0, 1, 2, 3};
+  typedef cpp17_input_iterator<int*> Iter;
+
+  {
+    DisableAllocationGuard guard;
+    auto it = c.insert(c.begin(), Iter(input), Iter(input + 4));
+    assert(it == c.begin());
+  }
+
+  assert_expected(c);
+}
+
+#if TEST_STD_VER >= 23
+void test_insert_range() {
+  std::deque<int> c = make_deque_with_front_spare();
+  int input[]       = {0, 1, 2, 3};
+  using Iter        = cpp20_input_iterator<int*>;
+  auto in           = std::ranges::subrange(Iter(input), sentinel_wrapper<Iter>(Iter(input + 4)));
+
+  {
+    DisableAllocationGuard guard;
+    auto it = c.insert_range(c.begin(), in);
+    assert(it == c.begin());
+  }
+
+  assert_expected(c);
+}
+
+void test_prepend_range() {
+  std::deque<int> c = make_deque_with_front_spare();
+  int input[]       = {0, 1, 2, 3};
+  using Iter        = cpp20_input_iterator<int*>;
+  auto in           = std::ranges::subrange(Iter(input), sentinel_wrapper<Iter>(Iter(input + 4)));
+
+  {
+    DisableAllocationGuard guard;
+    c.prepend_range(in);
+  }
+
+  assert_expected(c);
+}
+#endif
+
+int main(int, char**) {
+  test_insert_iter_iter();
+#if TEST_STD_VER >= 23
+  test_insert_range();
+  test_prepend_range();
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp
index 5ff572c79e512..cfd5135bad50c 100644
--- a/libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp
+++ b/libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp
@@ -17,6 +17,82 @@
 #include "../../insert_range_sequence_containers.h"
 #include "test_macros.h"
 
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+struct ThrowingSwap {
+  static bool throwing_enabled;
+  static int swaps;
+
+  int value = 0;
+
+  ThrowingSwap() = default;
+  ThrowingSwap(int v) : value(v) {}
+
+  friend void swap(ThrowingSwap& lhs, ThrowingSwap& rhs) {
+    int tmp   = lhs.value;
+    lhs.value = rhs.value;
+    rhs.value = tmp;
+    if (throwing_enabled && ++swaps == 2)
+      throw 1;
+  }
+
+  friend bool operator==(const ThrowingSwap& lhs, const ThrowingSwap& rhs) { return lhs.value == rhs.value; }
+};
+
+bool ThrowingSwap::throwing_enabled = false;
+int ThrowingSwap::swaps             = 0;
+#endif
+
+void test_input_range_strong_exception_safety() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  using T = ThrowingCopy<3>;
+
+  T::throwing_enabled = false;
+  std::deque<T> c;
+  for (int i = 0; i != 8; ++i)
+    c.emplace_back(100 + i);
+  for (int i = 0; i != 4; ++i)
+    c.pop_front();
+  std::deque<T> expected = c;
+
+  T input[] = {T(0), T(1), T(2), T(3)};
+  using Iter = cpp20_input_iterator<T*>;
+  auto in    = std::ranges::subrange(Iter(input), sentinel_wrapper<Iter>(Iter(input + 4)));
+
+  T::reset();
+  T::throwing_enabled = true;
+  try {
+    c.prepend_range(in);
+    assert(false);
+  } catch (int) {
+    assert(c == expected);
+  }
+  T::throwing_enabled = false;
+#endif
+}
+
+void test_input_range_reverse_exception_safety() {
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  std::deque<ThrowingSwap> c = {100, 101, 102, 103, 104, 105, 106, 107};
+  for (int i = 0; i != 4; ++i)
+    c.pop_front();
+  std::deque<ThrowingSwap> expected = c;
+
+  ThrowingSwap input[] = {0, 1, 2, 3};
+  using Iter           = cpp20_input_iterator<ThrowingSwap*>;
+  auto in              = std::ranges::subrange(Iter(input), sentinel_wrapper<Iter>(Iter(input + 4)));
+
+  ThrowingSwap::swaps            = 0;
+  ThrowingSwap::throwing_enabled = true;
+  try {
+    c.prepend_range(in);
+    assert(false);
+  } catch (int) {
+    assert(c == expected);
+  }
+  ThrowingSwap::throwing_enabled = false;
+#endif
+}
+
 // Tested cases:
 // - different kinds of insertions (prepending an {empty/one-element/mid-sized/long range} into an
 //   {empty/one-element/full} container);
@@ -36,6 +112,8 @@ int main(int, char**) {
 
   test_prepend_range_exception_safety_throwing_copy<std::deque>();
   test_prepend_range_exception_safety_throwing_allocator<std::deque, int>();
+  test_input_range_strong_exception_safety();
+  test_input_range_reverse_exception_safety();
 
   return 0;
 }

>From 30b6aaa2f2a62cac46d02c7b1d90a41d7f4051b4 Mon Sep 17 00:00:00 2001
From: bitree <bitree2004 at yandex.ru>
Date: Wed, 27 May 2026 18:08:47 +0300
Subject: [PATCH 2/2] [libc++] Cover C++03 deque input insert at begin

---
 libcxx/include/deque                                   | 10 ++++------
 .../deque/input_range_insert_at_begin.pass.cpp         |  4 +---
 2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index a8716920134ba..04c0a17c11bad 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -1235,14 +1235,12 @@ private:
   template <class _Iterator, class _Sentinel>
   _LIBCPP_HIDE_FROM_ABI iterator __insert_with_sentinel(const_iterator __p, _Iterator __f, _Sentinel __l);
 
-#  ifndef _LIBCPP_CXX03_LANG
   template <class _Iterator, class _Sentinel>
   _LIBCPP_HIDE_FROM_ABI iterator __prepend_with_sentinel(_Iterator __f, _Sentinel __l);
   template <class _Iterator, class _Sentinel>
   _LIBCPP_HIDE_FROM_ABI iterator __prepend_with_sentinel(_Iterator __f, _Sentinel __l, true_type);
   template <class _Iterator, class _Sentinel>
   _LIBCPP_HIDE_FROM_ABI iterator __prepend_with_sentinel(_Iterator __f, _Sentinel __l, false_type);
-#  endif
 
   template <class _Iterator>
   _LIBCPP_HIDE_FROM_ABI iterator __insert_with_size(const_iterator __p, _Iterator __f, size_type __n);
@@ -1797,10 +1795,8 @@ template <class _Tp, class _Allocator>
 template <class _InputIter, __enable_if_t<__has_exactly_input_iterator_category<_InputIter>::value, int> >
 typename deque<_Tp, _Allocator>::iterator
 deque<_Tp, _Allocator>::insert(const_iterator __p, _InputIter __f, _InputIter __l) {
-#  ifndef _LIBCPP_CXX03_LANG
   if (__p == cbegin())
     return __prepend_with_sentinel(__f, __l);
-#  endif
 
   return __insert_with_sentinel(__p, __f, __l);
 }
@@ -1815,7 +1811,6 @@ deque<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __p, _Iterator __f
   return insert(__p, move_iterator<__bi>(__buf.begin()), move_iterator<__bi>(__buf.end()));
 }
 
-#  ifndef _LIBCPP_CXX03_LANG
 template <class _Tp, class _Allocator>
 template <class _Iterator, class _Sentinel>
 _LIBCPP_HIDE_FROM_ABI typename deque<_Tp, _Allocator>::iterator
@@ -1837,7 +1832,11 @@ deque<_Tp, _Allocator>::__prepend_with_sentinel(_Iterator __f, _Sentinel __l, tr
   });
 
   for (; __f != __l; ++__f) {
+#  ifdef _LIBCPP_CXX03_LANG
+    push_front(*__f);
+#  else
     emplace_front(*__f);
+#  endif
     ++__inserted;
   }
   std::reverse(begin(), begin() + __inserted);
@@ -1852,7 +1851,6 @@ _LIBCPP_HIDE_FROM_ABI typename deque<_Tp, _Allocator>::iterator
 deque<_Tp, _Allocator>::__prepend_with_sentinel(_Iterator __f, _Sentinel __l, false_type) {
   return __insert_with_sentinel(cbegin(), std::move(__f), std::move(__l));
 }
-#  endif
 
 template <class _Tp, class _Allocator>
 template <class _ForwardIterator, __enable_if_t<__has_exactly_forward_iterator_category<_ForwardIterator>::value, int> >
diff --git a/libcxx/test/libcxx/containers/sequences/deque/input_range_insert_at_begin.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/input_range_insert_at_begin.pass.cpp
index 7d5794faa551a..3331c7adb3046 100644
--- a/libcxx/test/libcxx/containers/sequences/deque/input_range_insert_at_begin.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/input_range_insert_at_begin.pass.cpp
@@ -9,8 +9,6 @@
 // Check that inserting an unsized input range at the start of a deque uses
 // existing front spare capacity instead of materializing the range first.
 
-// UNSUPPORTED: c++03
-
 #include <cassert>
 #include <deque>
 
@@ -45,7 +43,7 @@ void test_insert_iter_iter() {
 
   {
     DisableAllocationGuard guard;
-    auto it = c.insert(c.begin(), Iter(input), Iter(input + 4));
+    std::deque<int>::iterator it = c.insert(c.begin(), Iter(input), Iter(input + 4));
     assert(it == c.begin());
   }
 


From libcxx-commits at lists.llvm.org  Wed Jul 15 05:01:54 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 15 Jul 2026 05:01:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Avoid materializing input ranges
 prepended to deque (PR #199969)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/199969@github.com>
Message-ID: <6a5776b2.92ea0e2a.9a5e7.b22b@mx.google.com>


ldionne wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/containers/sequence/deque.bench.cpp libcxx/test/benchmarks/containers/sequence/vector.bench.cpp

https://github.com/llvm/llvm-project/pull/199969

From libcxx-commits at lists.llvm.org  Wed Jul 15 05:07:34 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 15 Jul 2026 05:07:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Compose the test suite Lit site
 config from harness and installation substitutions (PR #209638)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209638@github.com>
Message-ID: <6a577806.d76d81af.39f3d0.eaea@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/209638

From libcxx-commits at lists.llvm.org  Wed Jul 15 05:09:35 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 15 Jul 2026 05:09:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Compose the test suite Lit site
 config from harness and installation substitutions (PR #209638)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209638@github.com>
Message-ID: <6a57787f.13be6df8.98af9.1632@mx.google.com>


ldionne wrote:

Giving a heads up to @llvm/libcxx-vendors since this may require small tweaks to downstream testing configurations. Specifically, the tweak would be to remove the inclusion of `cmake-bridge` from the site config, as we do in this patch:

```
lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
```

https://github.com/llvm/llvm-project/pull/209638

From libcxx-commits at lists.llvm.org  Wed Jul 15 05:37:43 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 15 Jul 2026 05:37:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a577f17.6365b89b.1d8070.1cc2@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/209161

>From c8f4bb157ba85b2fe67a97a4b76c37b2ab8656ea Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 14:28:02 +0200
Subject: [PATCH] [libc++] Optimize standard streams with
 sync_with_stdio(false)

Fixes #21566
---
 libcxx/include/fstream                        | 15 +--
 libcxx/lib/abi/CHANGELOG.TXT                  | 17 ++++
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |  5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |  3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |  3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |  5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |  3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |  3 +
 ...xxabi.v1.stable.noexceptions.nonew.abilist |  5 +-
 libcxx/src/ios.cpp                            |  7 --
 libcxx/src/iostream.cpp                       | 98 +++++++++++++++----
 .../cin.sync_with_stdio.sh.cpp                | 37 +++++++
 .../clog.sync_with_stdio.sh.cpp               | 29 ++++++
 .../cout.sync_with_stdio.sh.cpp               | 27 +++++
 .../wcin.sync_with_stdio.sh.cpp               | 44 +++++++++
 .../wclog.sync_with_stdio.sh.cpp              | 36 +++++++
 .../wcout.sync_with_stdio.sh.cpp              | 34 +++++++
 17 files changed, 336 insertions(+), 35 deletions(-)
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..4b276251aa917 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -391,10 +391,10 @@ private:
 
   _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 
-  // There are multiple (__)open function, they use different C-API open
-  // function. After that call these functions behave the same. This function
-  // does that part and determines the final return value.
-  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __do_open(FILE* __file, ios_base::openmode __mode) {
+public:
+  // Update the buffer so that __file is considered to be the underlying file and is opened as __mode. This is used in
+  // the open family of functions as well as various iostreams in sync_with_stdio(false) mode.
+  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __adopt_file(FILE* __file, ios_base::openmode __mode) {
     __file_ = __file;
     if (!__file_)
       return nullptr;
@@ -417,6 +417,7 @@ private:
     return this;
   }
 
+private:
   // If the file is already open, switch to unbuffered mode. Otherwise, record
   // the request to use unbuffered mode so that we use that mode when we
   // eventually open the file.
@@ -742,7 +743,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(std::fopen(__s, __mdstr), __mode);
+  return __adopt_file(std::fopen(__s, __mdstr), __mode);
 }
 
 template <class _CharT, class _Traits>
@@ -753,7 +754,7 @@ inline basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::__open(in
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(fdopen(__fd, __mdstr), __mode);
+  return __adopt_file(fdopen(__fd, __mdstr), __mode);
 }
 
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
@@ -767,7 +768,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(_wfopen(__s, __mdstr), __mode);
+  return __adopt_file(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
 
diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index b09f297eb65ba..567c23717ed60 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -12,6 +12,23 @@ To generate a summary, re-generate the new ABI list using the
 
 New entries should be added directly below the "Version" header.
 
+------------
+Version 24.0
+------------
+
+* [libc++] Optimize standard streams with sync_with_stdio(false)
+
+  This updates the standard streams so that the buffers are replaced with `basic_filebuf`s.
+
+  Fixes #21566
+
+
+  All platforms
+  -------------
+  Symbol added: _ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+
 ------------
 Version 23.0
 ------------
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 414a239757e1c..09999516face2 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 12, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
@@ -1908,6 +1909,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2143,6 +2145,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 20, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 64, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 590c0de8ce20f..d53eeb7532009 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index f4f4ab99ad866..384904f79d20e 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index e1b1e110318fa..8f26fdb0462cb 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1905,6 +1906,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2137,6 +2139,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 11cc8f180e824..0a2858da94a67 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1644,6 +1644,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1778,6 +1779,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1916,6 +1918,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..903457e983046 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1643,6 +1643,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1777,6 +1778,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1915,6 +1917,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 2b69d6dc89138..c24cdf845b489 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -2002,4 +2002,7 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT', 'is_defined': True}
\ No newline at end of file
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index 2e049098740dc..0b728db5b76f9 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -366,13 +366,6 @@ void ios_base::__set_failbit_and_consider_rethrow() {
 #endif // _LIBCPP_HAS_EXCEPTIONS
 }
 
-bool ios_base::sync_with_stdio(bool sync) {
-  static bool previous_state = true;
-  bool r                     = previous_state;
-  previous_state             = sync;
-  return r;
-}
-
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..612ec67297a72 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -10,6 +10,7 @@
 
 #include <__memory/construct_at.h>
 #include <__ostream/basic_ostream.h>
+#include <fstream>
 #include <istream>
 
 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
@@ -34,21 +35,44 @@ union stream {
   StreamT value;
 };
 
-template <class StreamT, class BufferT>
+template <class StreamT, class SyncBufT, class UnsyncBufT>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
   struct {
-    BufferT buffer;
+    union {
+      SyncBufT sync_buffer;
+      UnsyncBufT unsync_buffer;
+    };
     mbstate_t mb;
   };
 };
 
-template <class StreamT, class BufferT>
-void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) {
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
   data.mb = {};
-  std::construct_at(&data.buffer, stdstream, &data.mb);
-  std::construct_at(&stream.value, &data.buffer);
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  std::construct_at(&stream.value, &data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_sync_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
+  data.unsync_buffer.__adopt_file(nullptr, {}); // reset the file, so that basic_filebuf doesn't close standard streams
+  std::destroy_at(&data.unsync_buffer);
+  data.mb = {};
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  stream.value.rdbuf(&data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_unsync_stream(FILE* stdstream,
+                             stream<StreamT>& stream,
+                             stream_data<StreamT, SyncBufT, UnsyncBufT>& data,
+                             ios_base::openmode mode) {
+  std::destroy_at(&data.sync_buffer);
+  std::construct_at(&data.unsync_buffer);
+  data.unsync_buffer.__adopt_file(stdstream, mode);
+  stream.value.rdbuf(&data.unsync_buffer);
 }
 
 #define CHAR_MANGLING_char "D"
@@ -62,29 +86,39 @@ void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT,
 #endif
 
 #ifdef _LIBCPP_ABI_MICROSOFT
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__(                                \
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12 at A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
 #endif
 
 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
 
-STREAM(basic_istream, __stdinbuf, char, cin);
-STREAM(basic_ostream, __stdoutbuf, char, cout);
-STREAM(basic_ostream, __stdoutbuf, char, cerr);
-STREAM(basic_ostream, __stdoutbuf, char, clog);
+#if _LIBCPP_HAS_FILESYSTEM
+template <class CharT>
+using unsync_buffer = basic_filebuf<CharT>;
+#else
+// If we don't have access to files we can't treat the standard streams as files either - just save a dummy object in
+// that case.
+template <class>
+using unsync_buffer = char;
+#endif
+
+STREAM(basic_istream, __stdinbuf, unsync_buffer, char, cin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, clog);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+STREAM(basic_istream, __stdinbuf, unsync_buffer, wchar_t, wcin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wclog);
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
@@ -155,5 +189,33 @@ ios_base::Init::Init() {
 
 ios_base::Init::~Init() {}
 
+bool ios_base::sync_with_stdio(bool sync) {
+  static bool previous_state = true;
+  bool r                     = previous_state;
+
+#if _LIBCPP_HAS_FILESYSTEM
+  if (sync != previous_state) {
+    if (sync) {
+      switch_to_sync_stream(stdin, cin, cin_data);
+      switch_to_sync_stream(stdout, cout, cout_data);
+      switch_to_sync_stream(stderr, clog, clog_data);
+      switch_to_sync_stream(stdin, wcin, wcin_data);
+      switch_to_sync_stream(stdout, wcout, wcout_data);
+      switch_to_sync_stream(stderr, wclog, wclog_data);
+    } else {
+      switch_to_unsync_stream(stdin, cin, cin_data, ios::in);
+      switch_to_unsync_stream(stdout, cout, cout_data, ios::out);
+      switch_to_unsync_stream(stderr, clog, clog_data, ios::out);
+      switch_to_unsync_stream(stdin, wcin, wcin_data, ios::in);
+      switch_to_unsync_stream(stdout, wcout, wcout_data, ios::out);
+      switch_to_unsync_stream(stderr, wclog, wclog_data, ios::out);
+    }
+  }
+#endif // _LIBCPP_HAS_FILESYSTEM
+
+  previous_state = sync;
+  return r;
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..052356449c5ab
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::cin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..ab30dee5f695d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::clog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3a94964f09223
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::cout << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..db6c5ba0e8660
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// istream wcin;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::wcin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..c0a0f299451fb
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wclog;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wclog << L"1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..a2cdc070620b1
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wcout;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wcout << L"1234";
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Wed Jul 15 05:41:35 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 15 Jul 2026 05:41:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a577fff.9edb5c9d.5e50a.a910@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/209141

>From 276a77f7456cebe1f61371f8a65ef55c8bf2341b Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 13:18:00 +0200
Subject: [PATCH] [libc++] Optimize std::is_heap_until

---
 libcxx/include/__algorithm/is_heap_until.h    | 53 +++++++++++--------
 .../is.heap/ranges_is_heap_until.pass.cpp     |  6 +++
 2 files changed, 36 insertions(+), 23 deletions(-)

diff --git a/libcxx/include/__algorithm/is_heap_until.h b/libcxx/include/__algorithm/is_heap_until.h
index 7444d978e37f5..c05cc2177ae90 100644
--- a/libcxx/include/__algorithm/is_heap_until.h
+++ b/libcxx/include/__algorithm/is_heap_until.h
@@ -12,7 +12,6 @@
 #include <__algorithm/comp.h>
 #include <__algorithm/comp_ref_type.h>
 #include <__config>
-#include <__iterator/iterator_traits.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -20,29 +19,37 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Compare, class _RandomAccessIterator>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandomAccessIterator
-__is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp) {
-  typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
-  difference_type __len      = __last - __first;
-  difference_type __p        = 0;
-  difference_type __c        = 1;
-  _RandomAccessIterator __pp = __first;
-  while (__c < __len) {
-    _RandomAccessIterator __cp = __first + __c;
-    if (__comp(*__pp, *__cp))
-      return __cp;
-    ++__c;
-    ++__cp;
-    if (__c == __len)
-      return __last;
-    if (__comp(*__pp, *__cp))
-      return __cp;
-    ++__p;
-    ++__pp;
-    __c = 2 * __p + 1;
+template <class _Compare, class _Iter>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter
+__is_heap_until(_Iter __first, _Iter __last, _Compare&& __comp) {
+  auto __count = __last - __first;
+
+  if (__count < 2)
+    return __last;
+
+  // This points to one past the last parent that has two children.
+  // Integer division handles the case where the last parent has a single child.
+  auto __parent_last = __first + (__count - 1) / 2;
+  auto __child       = __first;
+  ++__child;
+  for (; __first != __parent_last; ++__first) {
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
+
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
   }
-  return __last;
+
+  // If the heap is even-sized, the last parent has a single child, handled here.
+  if (__count % 2 == 0) {
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
+  }
+
+  return __child;
 }
 
 template <class _RandomAccessIterator, class _Compare>
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
index 5fd119a21a98f..42a66fa5723d6 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
@@ -100,6 +100,12 @@ constexpr void test_iter_sent() {
   test_one<Iter, Sent>(std::array{2, 1}, 2);
   // 2-element sequence, not a heap.
   test_one<Iter, Sent>(std::array{1, 2}, 1);
+  // 3-element sequence, a heap.
+  test_one<Iter, Sent>(std::array{3, 2, 1}, 3);
+  // 3-element sequence, LHS not a heap.
+  test_one<Iter, Sent>(std::array{2, 3, 1}, 1);
+  // 3-element sequence, RHS not a heap.
+  test_one<Iter, Sent>(std::array{2, 1, 3}, 2);
   // Longer sequence, a heap.
   test_one<Iter, Sent>(std::array{8, 6, 7, 3, 4, 1, 5, 2}, 8);
   // Longer sequence, not a heap.


From libcxx-commits at lists.llvm.org  Wed Jul 15 05:56:10 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 15 Jul 2026 05:56:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::swap of locales
	(PR #209760)
Message-ID: <llvm.org/llvm/llvm-project/pull/209760@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/209760

Currently we copy construct `locale`s whenever we swap, which is quite expensive for locales. We can just swap the pointers instead.


>From 521b03ada932c20ad63345557805af1c20d0ff47 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 15 Jul 2026 14:55:14 +0200
Subject: [PATCH] [libc++] Optimize std::swap of locales

---
 libcxx/include/__locale                       |  6 ++++
 .../localization}/ctype.bench.cpp             |  0
 .../text/localization/locale.bench.cpp        | 28 +++++++++++++++++++
 .../localization}/num_get.bench.cpp           |  0
 .../localization}/num_put.bench.cpp           |  0
 5 files changed, 34 insertions(+)
 rename libcxx/test/benchmarks/{locale => text/localization}/ctype.bench.cpp (100%)
 create mode 100644 libcxx/test/benchmarks/text/localization/locale.bench.cpp
 rename libcxx/test/benchmarks/{locale => text/localization}/num_get.bench.cpp (100%)
 rename libcxx/test/benchmarks/{locale => text/localization}/num_put.bench.cpp (100%)

diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index bb8f8e7bbf5d9..8f29d713909cc 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -143,8 +143,14 @@ private:
   friend bool has_facet(const locale&) _NOEXCEPT;
   template <class _Facet>
   friend const _Facet& use_facet(const locale&);
+
+  friend void swap(locale&, locale&);
 };
 
+inline void swap(locale& __lhs, locale& __rhs) {
+  std::swap(__lhs.__locale_, __rhs.__locale_);
+}
+
 class _LIBCPP_EXPORTED_FROM_ABI locale::facet : public __shared_count {
 protected:
   _LIBCPP_HIDE_FROM_ABI explicit facet(size_t __refs = 0) : __shared_count(static_cast<long>(__refs) - 1) {}
diff --git a/libcxx/test/benchmarks/locale/ctype.bench.cpp b/libcxx/test/benchmarks/text/localization/ctype.bench.cpp
similarity index 100%
rename from libcxx/test/benchmarks/locale/ctype.bench.cpp
rename to libcxx/test/benchmarks/text/localization/ctype.bench.cpp
diff --git a/libcxx/test/benchmarks/text/localization/locale.bench.cpp b/libcxx/test/benchmarks/text/localization/locale.bench.cpp
new file mode 100644
index 0000000000000..60bc541bc053f
--- /dev/null
+++ b/libcxx/test/benchmarks/text/localization/locale.bench.cpp
@@ -0,0 +1,28 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <ios>
+#include <locale>
+
+#include <benchmark/benchmark.h>
+
+static void BM_num_get(benchmark::State& state) {
+  std::locale loc1, loc2;
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(loc1);
+    benchmark::DoNotOptimize(loc2);
+    std::swap(loc1, loc2);
+  }
+}
+BENCHMARK(BM_num_get)->Name("std::swap(std::locale&, std::locale&)");
+
+BENCHMARK_MAIN();
diff --git a/libcxx/test/benchmarks/locale/num_get.bench.cpp b/libcxx/test/benchmarks/text/localization/num_get.bench.cpp
similarity index 100%
rename from libcxx/test/benchmarks/locale/num_get.bench.cpp
rename to libcxx/test/benchmarks/text/localization/num_get.bench.cpp
diff --git a/libcxx/test/benchmarks/locale/num_put.bench.cpp b/libcxx/test/benchmarks/text/localization/num_put.bench.cpp
similarity index 100%
rename from libcxx/test/benchmarks/locale/num_put.bench.cpp
rename to libcxx/test/benchmarks/text/localization/num_put.bench.cpp


From libcxx-commits at lists.llvm.org  Wed Jul 15 05:56:31 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 15 Jul 2026 05:56:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::swap of locales
	(PR #209760)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209760@github.com>
Message-ID: <6a57837f.481afadf.34be4b.752b@mx.google.com>


philnik777 wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/text/localization/locale.bench.cpp

https://github.com/llvm/llvm-project/pull/209760

From libcxx-commits at lists.llvm.org  Wed Jul 15 06:53:35 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 15 Jul 2026 06:53:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::swap of locales
	(PR #209760)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209760@github.com>
Message-ID: <6a5790df.39be5633.14e933.cc97@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions ,cpp -- libcxx/test/benchmarks/text/localization/locale.bench.cpp libcxx/include/__locale libcxx/test/benchmarks/text/localization/ctype.bench.cpp libcxx/test/benchmarks/text/localization/num_get.bench.cpp libcxx/test/benchmarks/text/localization/num_put.bench.cpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index 8f29d7139..b9cd14ebd 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -147,9 +147,7 @@ private:
   friend void swap(locale&, locale&);
 };
 
-inline void swap(locale& __lhs, locale& __rhs) {
-  std::swap(__lhs.__locale_, __rhs.__locale_);
-}
+inline void swap(locale& __lhs, locale& __rhs) { std::swap(__lhs.__locale_, __rhs.__locale_); }
 
 class _LIBCPP_EXPORTED_FROM_ABI locale::facet : public __shared_count {
 protected:

``````````

</details>


https://github.com/llvm/llvm-project/pull/209760

From libcxx-commits at lists.llvm.org  Wed Jul 15 06:54:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 15 Jul 2026 06:54:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::swap of locales
	(PR #209760)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209760@github.com>
Message-ID: <6a579114.a96d76c3.a3872.8a46@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

Currently we copy construct `locale`s whenever we swap, which is quite expensive for locales. We can just swap the pointers instead.


---
Full diff: https://github.com/llvm/llvm-project/pull/209760.diff


5 Files Affected:

- (modified) libcxx/include/__locale (+6) 
- (renamed) libcxx/test/benchmarks/text/localization/ctype.bench.cpp () 
- (added) libcxx/test/benchmarks/text/localization/locale.bench.cpp (+28) 
- (renamed) libcxx/test/benchmarks/text/localization/num_get.bench.cpp () 
- (renamed) libcxx/test/benchmarks/text/localization/num_put.bench.cpp () 


``````````diff
diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index bb8f8e7bbf5d9..8f29d713909cc 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -143,8 +143,14 @@ private:
   friend bool has_facet(const locale&) _NOEXCEPT;
   template <class _Facet>
   friend const _Facet& use_facet(const locale&);
+
+  friend void swap(locale&, locale&);
 };
 
+inline void swap(locale& __lhs, locale& __rhs) {
+  std::swap(__lhs.__locale_, __rhs.__locale_);
+}
+
 class _LIBCPP_EXPORTED_FROM_ABI locale::facet : public __shared_count {
 protected:
   _LIBCPP_HIDE_FROM_ABI explicit facet(size_t __refs = 0) : __shared_count(static_cast<long>(__refs) - 1) {}
diff --git a/libcxx/test/benchmarks/locale/ctype.bench.cpp b/libcxx/test/benchmarks/text/localization/ctype.bench.cpp
similarity index 100%
rename from libcxx/test/benchmarks/locale/ctype.bench.cpp
rename to libcxx/test/benchmarks/text/localization/ctype.bench.cpp
diff --git a/libcxx/test/benchmarks/text/localization/locale.bench.cpp b/libcxx/test/benchmarks/text/localization/locale.bench.cpp
new file mode 100644
index 0000000000000..60bc541bc053f
--- /dev/null
+++ b/libcxx/test/benchmarks/text/localization/locale.bench.cpp
@@ -0,0 +1,28 @@
+
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <ios>
+#include <locale>
+
+#include <benchmark/benchmark.h>
+
+static void BM_num_get(benchmark::State& state) {
+  std::locale loc1, loc2;
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(loc1);
+    benchmark::DoNotOptimize(loc2);
+    std::swap(loc1, loc2);
+  }
+}
+BENCHMARK(BM_num_get)->Name("std::swap(std::locale&, std::locale&)");
+
+BENCHMARK_MAIN();
diff --git a/libcxx/test/benchmarks/locale/num_get.bench.cpp b/libcxx/test/benchmarks/text/localization/num_get.bench.cpp
similarity index 100%
rename from libcxx/test/benchmarks/locale/num_get.bench.cpp
rename to libcxx/test/benchmarks/text/localization/num_get.bench.cpp
diff --git a/libcxx/test/benchmarks/locale/num_put.bench.cpp b/libcxx/test/benchmarks/text/localization/num_put.bench.cpp
similarity index 100%
rename from libcxx/test/benchmarks/locale/num_put.bench.cpp
rename to libcxx/test/benchmarks/text/localization/num_put.bench.cpp

``````````

</details>


https://github.com/llvm/llvm-project/pull/209760

From libcxx-commits at lists.llvm.org  Wed Jul 15 07:02:13 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Wed, 15 Jul 2026 07:02:13 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209473@github.com>
Message-ID: <6a5792e5.160324b0.642f8.e57b@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `clang-s390x-linux-lnt` running on `systemz-1` while building `clang-tools-extra,clang,cmake,flang,libcxx,lld,llvm` at step 7 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/136/builds/8058

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 7 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'LLVM :: CodeGen/SystemZ/zos-ppa2.ll' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
sed -e 's/!"MODE"/!"ascii"/' -e 's/BYTE/85/' /home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/llvm/llvm/test/CodeGen/SystemZ/zos-ppa2.ll > /home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: sed -e 's/!"MODE"/!"ascii"/' -e s/BYTE/85/ /home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/llvm/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
# RUN: at line 2
/home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/stage1/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true < /home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll |  /home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/stage1/bin/FileCheck /home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: /home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/stage1/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true
# executed command: /home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/stage1/bin/FileCheck /home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# .---command stderr------------
# | /home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll:24:10: error: CHECK: expected string not found in input
# | ; CHECK: DC XL6'F2F3F0F0F0F0'
# |          ^
# | <stdin>:17:39: note: scanning from here
# |  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0' 
# |                                       ^
# | <stdin>:18:2: note: possible intended match here
# |  DC XL6'F2F4F0F0F0F0' 
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /home/uweigand/sandbox/buildbot/clang-s390x-linux-lnt/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             .
# |             .
# |             .
# |            12:  DC XL4'00000000'  
# |            13:  DC XL1'85'  
# |            14:  DC XL1'00'  
# |            15:  DC XL2'0000'  
# |            16: L#DVS DS 0H  
# |            17:  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0'  
# | check:24'0                                          {    search range start (exclusive)
# | check:24'1                                               error: no match found in search range
# |            18:  DC XL6'F2F4F0F0F0F0'  
# | check:24'2      ?                       possible intended match
# |            19:  DC XL2'0000'  
# |            20: stdin#C CSECT  
# |            21: C_@@QPPA2 CATTR ALIGN(3),FILL(0),NOTEXECUTABLE,READONLY,RMODE(64),PART(.  
# |            22:  .&ppa2)  
# |            23: .&ppa2 XATTR LINKAGE(OS),REFERENCE(DATA),SCOPE(SECTION)  
# |             .
# |             .
...

```

</details>

https://github.com/llvm/llvm-project/pull/209473

From libcxx-commits at lists.llvm.org  Wed Jul 15 08:46:21 2026
From: libcxx-commits at lists.llvm.org (Yanzuo Liu via libcxx-commits)
Date: Wed, 15 Jul 2026 08:46:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Mark test unsupported in
	clang-24 (PR #209794)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209794@github.com>
Message-ID: <6a57ab4d.a1accbb4.3afc30.4179@mx.google.com>


================
@@ -1,906 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: gcc
-// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22, clang-23, apple-clang-17, apple-clang-21
----------------
zwuis wrote:

How about

```diff
-// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22, clang-23, apple-clang-17, apple-clang-21
+// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22, clang-23, clang-24, apple-clang-17, apple-clang-21
```

https://github.com/llvm/llvm-project/pull/209794

From libcxx-commits at lists.llvm.org  Wed Jul 15 09:54:58 2026
From: libcxx-commits at lists.llvm.org (Yanzuo Liu via libcxx-commits)
Date: Wed, 15 Jul 2026 09:54:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Mark test unsupported in
	clang-24 (PR #209794)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209794@github.com>
Message-ID: <6a57bb62.f3151ff8.b27a.4ba5@mx.google.com>


https://github.com/zwuis approved this pull request.


https://github.com/llvm/llvm-project/pull/209794

From libcxx-commits at lists.llvm.org  Wed Jul 15 09:58:54 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 15 Jul 2026 09:58:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] 0cf914a - [libcxx][libc] update
	LLVM-libc test status (#201236)
Message-ID: <6a57bc4e.dd74445a.1054f0.05e7@mx.google.com>


Author: Michael Jones
Date: 2026-07-15T09:58:49-07:00
New Revision: 0cf914adef4b0f1f5251354b91f5c709b95b1b1e

URL: https://github.com/llvm/llvm-project/commit/0cf914adef4b0f1f5251354b91f5c709b95b1b1e
DIFF: https://github.com/llvm/llvm-project/commit/0cf914adef4b0f1f5251354b91f5c709b95b1b1e.diff

LOG: [libcxx][libc] update LLVM-libc test status (#201236)

This update removes some unnecessary carveouts, marks some newly
XFAILing tests, and unmarks some now passing tests.

With this change the status of tests is:
Total Discovered Tests: 11471
  Unsupported      : 2098 (18.29%)
  Passed           : 9260 (80.73%)
  Expectedly Failed:  113 (0.99%)

Assisted-by: Automated tooling, human reviewed.

Added: 
    

Modified: 
    libcxx/cmake/caches/Generic-llvm-libc.cmake
    libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
    libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
    libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
    libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
    libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
    libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
    libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
    libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
    libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
    libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
    libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
    libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
    libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
    libcxx/test/std/strings/c.strings/cwchar.pass.cpp
    libcxx/test/std/strings/c.strings/cwctype.pass.cpp
    libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
    libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
    libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
    libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
    libcxxabi/test/configs/llvm-libc++abi-llvm-libc.cfg.in

Removed: 
    


################################################################################
diff  --git a/libcxx/cmake/caches/Generic-llvm-libc.cmake b/libcxx/cmake/caches/Generic-llvm-libc.cmake
index 8e8a2699858b3..dce7fb73ff8b3 100644
--- a/libcxx/cmake/caches/Generic-llvm-libc.cmake
+++ b/libcxx/cmake/caches/Generic-llvm-libc.cmake
@@ -31,20 +31,10 @@ set(LIBCXX_ENABLE_STATIC ON CACHE BOOL "")
 set(LIBCXX_INSTALL_LIBRARY ON CACHE BOOL "")
 set(LIBCXX_CXX_ABI libcxxabi CACHE STRING "")
 
-# Not yet fully implemented.
-set(LIBCXX_ENABLE_LOCALIZATION OFF CACHE BOOL "")
-set(LIBCXX_ENABLE_WIDE_CHARACTERS OFF CACHE BOOL "")
-
 # Still missing some components.
 set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "")
 
-# Still needs validation before turning on.
-set(LIBCXX_ENABLE_RANDOM_DEVICE OFF CACHE BOOL "")
-
-# Still missing some components.
-set(LIBCXX_ENABLE_MONOTONIC_CLOCK OFF CACHE BOOL "")
-
-# requires LIBCXX_ENABLE_MONOTONIC_CLOCK=ON
+# blocked on issues with atomic
 set(LIBCXX_ENABLE_THREADS OFF CACHE BOOL "")
 set(LIBCXXABI_ENABLE_THREADS OFF CACHE BOOL "")
 
@@ -58,5 +48,6 @@ set(CMAKE_BUILD_TYPE "Release" CACHE STRING "")
 set(LLVM_ENABLE_RUNTIMES "compiler-rt;libc;libunwind;libcxxabi;libcxx" CACHE STRING "")
 set(LLVM_LIBC_FULL_BUILD ON CACHE BOOL "")
 set(LLVM_LIBC_INCLUDE_SCUDO ON CACHE BOOL "")
+set(LLVM_LIBC_ENABLE_EXPERIMENTAL_ENTRYPOINTS ON CACHE BOOL "")
 set(COMPILER_RT_BUILD_GWP_ASAN OFF CACHE BOOL "")
 set(COMPILER_RT_BUILD_SCUDO_STANDALONE_WITH_LLVM_LIBC ON CACHE BOOL "")

diff  --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index cae8a8007214e..9dbf8a98dd5e4 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include-dir} -I %{target-include-dir} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx-dir}/test/support -idirafter @LIBC_KERNEL_HEADERS@'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a'
+    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--end-group -Wl,--defsym,__dso_handle=0'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '

diff  --git a/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp b/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
index 39a51352eadd9..fb26bdf49cc3c 100644
--- a/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
+++ b/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
@@ -29,9 +29,6 @@
 //       spuriously fail.
 // REQUIRES: no-exceptions
 
-// No member named 'regex'.
-// XFAIL: LLVM-LIBC-FIXME
-
 #include <cstddef>
 #include <new>
 

diff  --git a/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp b/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
index 339e756b86fc8..02b20a8c98b21 100644
--- a/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
+++ b/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
@@ -22,9 +22,6 @@
 // REQUIRES: no-exceptions
 // UNSUPPORTED: c++03, c++11, c++14
 
-// No member named 'regex'.
-// XFAIL: LLVM-LIBC-FIXME
-
 #include <cstddef>
 #include <limits>
 #include <new>

diff  --git a/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp b/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
index 66251648bd50c..1df75885e4347 100644
--- a/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.condition/PR30202_notify_from_pthread_created_thread.pass.cpp
@@ -20,6 +20,8 @@
 // Test that this function works with threads that were not created by
 // std::thread. See https://llvm.org/PR30202
 
+// Segfault
+// XFAIL: LLVM-LIBC-FIXME
 
 #include <condition_variable>
 #include <mutex>

diff  --git a/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
index 96ec3332519bc..5c5c0fed58726 100644
--- a/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
+++ b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.member/native_handle.pass.cpp
@@ -16,6 +16,9 @@
 
 // native_handle_type native_handle();
 
+// pthread_t and int not comparable.
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <thread>
 #include <new>
 #include <cstdlib>

diff  --git a/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
index 98d028c5181d5..7fee899683ee1 100644
--- a/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/wchar_h.compile.pass.cpp
@@ -8,6 +8,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing wchar string formatting functions.
+// XFAIL: LLVM-LIBC-FIXME
+
 // <wchar.h>
 
 #include <wchar.h>

diff  --git a/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
index 4e2fb319336f1..221c219918614 100644
--- a/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
+++ b/libcxx/test/std/depr/depr.c.headers/wctype_h.compile.pass.cpp
@@ -8,6 +8,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing WEOF, wctrans_t
+// XFAIL: LLVM-LIBC-FIXME
+
 // towctrans and wctrans were added in Android API 26.
 // TODO: Switch from UNSUPPORTED to XFAIL once the Android CI Docker sysroot is
 // updated.

diff  --git a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
index dbf2ded1b532e..82d43c1673d2d 100644
--- a/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
+++ b/libcxx/test/std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp
@@ -21,6 +21,9 @@
 // but the words should be written without interleaving. To increment the
 // change of interleaving words are written one character at a time.
 
+// segmentation fault
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <cassert>
 #include <chrono>
 #include <mutex>

diff  --git a/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
index b9467a166e8d5..61e0d01f2ae4d 100644
--- a/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing some math functions that compiler-rt buildins need.
-// XFAIL: LLVM-LIBC-FIXME
-
 // <complex>
 
 // complex& operator/=(const complex& rhs); // constexpr in C++20

diff  --git a/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
index 4e00e87f18220..d12dfd994b0ae 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing some math functions that compiler-rt buildins need.
-// XFAIL: LLVM-LIBC-FIXME
-
 // <complex>
 
 // template<class T>

diff  --git a/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
index 56d9664b49aea..f32b560c1e864 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing some math functions that compiler-rt buildins need.
-// XFAIL: LLVM-LIBC-FIXME
-
 // <complex>
 
 // template<class T>

diff  --git a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
index 8f7c8eb7ccc0f..22319e5b31454 100644
--- a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/eval.PR44847.pass.cpp
@@ -21,6 +21,9 @@
 // Very slow when run in qemu.
 // REQUIRES: long_tests
 
+// missing lgamma_r
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <random>
 #include <cassert>
 #include <cmath>

diff  --git a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
index 35f579903a460..a53a5db3be4c0 100644
--- a/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
+++ b/libcxx/test/std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.bin/io.pass.cpp
@@ -23,6 +23,9 @@
 // operator>>(basic_istream<charT, traits>& is,
 //            binomial_distribution& x);
 
+// missing lgamma_r
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <random>
 #include <sstream>
 #include <cassert>

diff  --git a/libcxx/test/std/strings/c.strings/cwchar.pass.cpp b/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
index 0caf4b9913b50..a9e99914c7b1a 100644
--- a/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
+++ b/libcxx/test/std/strings/c.strings/cwchar.pass.cpp
@@ -10,6 +10,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing wchar string formatting functions.
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <cwchar>
 #include <ctime>
 #include <cstdarg>

diff  --git a/libcxx/test/std/strings/c.strings/cwctype.pass.cpp b/libcxx/test/std/strings/c.strings/cwctype.pass.cpp
index 0deabf51ed59c..3de582714d01d 100644
--- a/libcxx/test/std/strings/c.strings/cwctype.pass.cpp
+++ b/libcxx/test/std/strings/c.strings/cwctype.pass.cpp
@@ -10,6 +10,9 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// Missing WEOF, towctrans, wctrans_t
+// XFAIL: LLVM-LIBC-FIXME
+
 // towctrans and wctrans were added in Android API 26.
 // TODO: Switch from UNSUPPORTED to XFAIL once the Android CI Docker sysroot is
 // updated.

diff  --git a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
index fff5ede848b57..bd4bd47671a81 100644
--- a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
+++ b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
@@ -20,6 +20,9 @@
 // wstring to_wstring(double val);
 // wstring to_wstring(long double val);
 
+// Missing swprintf (currently a stub that infinite loops)
+// UNSUPPORTED: LLVM-LIBC-FIXME
+
 #include <string>
 #include <cassert>
 #include <limits>

diff  --git a/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp b/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
index 61094eced4cb2..f1b349017b1a5 100644
--- a/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
+++ b/libcxx/test/std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp
@@ -23,6 +23,9 @@
 //
 // void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);
 
+// segmentation fault
+// XFAIL: LLVM-LIBC-FIXME
+
 #include "make_test_thread.h"
 #include "test_macros.h"
 

diff  --git a/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
index 1dba5d8a40976..726b5807d55e3 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.this/sleep_for.signals.pass.cpp
@@ -21,6 +21,9 @@
 // This test ensures that we sleep for the right amount of time even when
 // we get interrupted by a signal, as fixed in 58a0a70fb2f1.
 
+// missing ITIMER_REAL
+// XFAIL: LLVM-LIBC-FIXME
+
 #include <thread>
 #include <cassert>
 #include <chrono>

diff  --git a/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp b/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
index e3ac7e53251d5..cac7fbda0fbb3 100644
--- a/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
+++ b/libcxx/test/std/time/time.hash/time.hash_enabled.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// No member named `leapsecond'.
-// XFAIL: LLVM-LIBC-FIXME
-
 // REQUIRES: std-at-least-c++26
 
 // <chrono>

diff  --git a/libcxxabi/test/configs/llvm-libc++abi-llvm-libc.cfg.in b/libcxxabi/test/configs/llvm-libc++abi-llvm-libc.cfg.in
index d46a1c6d4e58c..9e37ffac4c791 100644
--- a/libcxxabi/test/configs/llvm-libc++abi-llvm-libc.cfg.in
+++ b/libcxxabi/test/configs/llvm-libc++abi-llvm-libc.cfg.in
@@ -10,7 +10,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include} -I %{cxx-include} -I %{cxx-target-include} %{maybe-include-libunwind} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx}/test/support -I %{libcxx}/src -idirafter @LIBC_KERNEL_HEADERS@ -D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib} -lc++ -lc++abi -pthread @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o @CMAKE_BINARY_DIR@/libc/lib/libc.a -static -fno-use-cxa-atexit'
+    '-nodefaultlibs -nostartfiles -L %{lib} -lc++ -lc++abi -pthread @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o @CMAKE_BINARY_DIR@/libc/lib/libc.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--defsym,__dso_handle=0'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '


        

From libcxx-commits at lists.llvm.org  Wed Jul 15 09:58:55 2026
From: libcxx-commits at lists.llvm.org (Michael Jones via libcxx-commits)
Date: Wed, 15 Jul 2026 09:58:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libcxx][libc] update
	LLVM-libc test status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a57bc4f.b195937d.1c3229.1852@mx.google.com>


michaelrj-google wrote:

All relevant tests have passed, merging.

https://github.com/llvm/llvm-project/pull/201236

From libcxx-commits at lists.llvm.org  Wed Jul 15 09:59:01 2026
From: libcxx-commits at lists.llvm.org (Michael Jones via libcxx-commits)
Date: Wed, 15 Jul 2026 09:59:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libcxx][libc] update
	LLVM-libc test status (PR #201236)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201236@github.com>
Message-ID: <6a57bc55.a5745248.19fb62.7c05@mx.google.com>


https://github.com/michaelrj-google closed https://github.com/llvm/llvm-project/pull/201236

From libcxx-commits at lists.llvm.org  Wed Jul 15 10:00:57 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 15 Jul 2026 10:00:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Mark test unsupported in
	clang-24 (PR #209794)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209794@github.com>
Message-ID: <6a57bcc9.9edb5c9d.5e50a.fbb6@mx.google.com>


================
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 // UNSUPPORTED: gcc
-// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22, clang-23, apple-clang-17, apple-clang-21
+// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22, clang-23, clang-24, apple-clang-17, apple-clang-21
----------------
ldionne wrote:

This should be supported in clang 24 since #75371 was merged though, so I don't really understand. Removing the `UNSUPPORTED` seems wrong.

https://github.com/llvm/llvm-project/pull/209794

From libcxx-commits at lists.llvm.org  Wed Jul 15 10:01:14 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 15 Jul 2026 10:01:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Mark test unsupported in
	clang-24 (PR #209794)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209794@github.com>
Message-ID: <6a57bcda.d1496011.289bf0.abcc@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/209794

From libcxx-commits at lists.llvm.org  Wed Jul 15 11:54:52 2026
From: libcxx-commits at lists.llvm.org (Kyungtak Woo via libcxx-commits)
Date: Wed, 15 Jul 2026 11:54:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add an ABI flag to optimize
 mersenne_twister_engine (PR #206423)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206423@github.com>
Message-ID: <6a57d77c.507fc713.2da8f.54c1@mx.google.com>


kevinwkt wrote:

Hey @philnik777, I believe I'm getting some breakages due to this change when std::mt19937 is seeded using std::seed_seq under the vectorized ABI configuration (_LIBCPP_ABI_VERSION >= 2).

When initialized with a seed sequence, the engine bypasses the initial twist (__update_all_states()) due to an early return in the all-zero state check inside seed(_Sseq& __q). This causes the engine to return the raw, untwisted seed values (tempered) instead of the correct twisted values.

Here is a minimal reproducer:
```
#include <iostream>
#include <random>
int main() {
    std::seed_seq sseq{15, 22, 288, 37};
    std::mt19937 engine(sseq);
    
    uint32_t val = engine();
    
    std::cout << "Expected (Standard): 192527404\n";
    std::cout << "Actual:             " << val << "\n";
    
    if (val == 360235457) {
        std::cout << "RESULT: FAIL (returned untwisted state)\n";
        return 1;
    } else if (val == 192527404) {
        std::cout << "RESULT: PASS\n";
        return 0;
    }
    return 1;
}
```

I've been using using libc++ with the unstable ABI enabled and running into failure with
```
clang++ -stdlib=libc++ -D_LIBCPP_ABI_VERSION=2 repro.cc -o repro && ./repro
```

I created https://github.com/llvm/llvm-project/pull/209860 to try to address this but just wanted to make sure my assumptions are correct first.

https://github.com/llvm/llvm-project/pull/206423

From libcxx-commits at lists.llvm.org  Wed Jul 15 13:51:33 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Wed, 15 Jul 2026 13:51:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::mismatch() based on __pstl::__parallel_find() (PR #209291)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209291@github.com>
Message-ID: <6a57f2d5.7d702ed2.32eb67.2399@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/209291

>From 650ebeba6d9e4867856e679d475e8f50edcdadde Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:03:38 +0100
Subject: [PATCH 01/14] Added a basic implementation of parallel 4-legged
 mismatch() based on __parallel_find()

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/libdispatch.h  |   7 +-
 libcxx/include/__pstl/backends/serial.h       |  20 +++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  88 +++++++++++
 .../mismatch/pstl.mismatch.pass.cpp           | 139 ++++++++++++++++++
 7 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__pstl/cpu_algos/mismatch.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..6b5dc87fbe76d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/mismatch.h  
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a84a768cad570..1d64c6f47630f 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -317,6 +317,14 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index 33fd5de66fbb1..710506585ca22 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -33,6 +33,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -237,7 +238,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
       auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
       auto __index           = __chunk == 0 ? 0
                                             : (__chunk * __partitions.__chunk_size_) +
-                                        (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
+                                                  (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
       if (__this_chunk_size != 1) {
         std::__construct_at(
             __values.get() + __chunk,
@@ -364,6 +365,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..d90022ee37145 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/mismatch.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    return std::mismatch(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::forward<_Predicate>(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..93935d22b9442 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
new file mode 100644
index 0000000000000..11ffdc332d633
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_MISMATCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
+
+#include <__algorithm/mismatch.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_mismatch {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Look for a mismatch only in the prefix of the two ranges.
+      auto __n = std::min(__last1 - __first1, __last2 - __first2);
+      // Find a position in the first range where the predicate is false against the corresponding position in the
+      // second range.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __first1 + __n,
+          [&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
+            // Run the sequential mismatch algorithm on these ranges:
+            //   [__brick_first1, __brick_last1) and
+            //   [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
+            auto __brick_first2 = __first2 + (__brick_first1 - __first1);
+            return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
+                .first;
+          },
+          less<>{},
+          true);
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      auto __idx = *__res - __first1;
+      return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      // Unsequenced execution is also implicitly covered by the sequential implementation.
+      return std::mismatch(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
new file mode 100644
index 0000000000000..9cbadda428ad9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 1};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(42);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From e96f0aa34d1b212b123d4c9917928e17d26270f3 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:05:01 +0100
Subject: [PATCH 02/14] Added a definition of a parallel 4-legged
 std::mismatch()

---
 libcxx/include/__algorithm/pstl.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2bf9b4276baab..ab35b1a3e537b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -138,6 +138,29 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,

>From 472174e7343fc86a77bf314c7d088331dab6bae5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:41:51 +0100
Subject: [PATCH 03/14] Added a default implementation of __mismatch_3leg based
 on __mismatch

---
 libcxx/include/__algorithm/pstl.h             | 18 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++++
 libcxx/include/__pstl/backends/default.h      | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../mismatch/pstl.mismatch.pass.cpp           | 20 +++++++++++
 5 files changed, 84 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index ab35b1a3e537b..01c61fb00f4d5 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -161,6 +161,24 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 1d64c6f47630f..d800e60916efc 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -325,6 +325,13 @@ struct __mismatch;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch_3leg;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5c78d4efa412b..05b328beae80f 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -63,6 +63,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// mismatch family
+// ---------------
+// - mismatch_3leg
+//
 // for_each family
 // ---------------
 // - for_each_n
@@ -211,6 +215,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// mismatch family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+  optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _Comp __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Forward to the 4-legged version of mismatch.
+      using _Mismatch           = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      _ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
+      return _Mismatch()(
+          __policy,
+          std::move(__first1),
+          std::move(__last1),
+          std::move(__first2),
+          std::move(__last2),
+          std::move(__comp));
+    } else {
+      // Currently only random access iterators are supported for parallel mismatch_3leg.
+      return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..752aa35643e2b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2384,6 +2384,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module mismatch {
+        header "__pstl/cpu_algos/mismatch.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 9cbadda428ad9..0cb5c7632eed8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -10,6 +10,14 @@
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2);
+//
 // template <class ExecutionPolicy,
 //           class ForwardIterator1,
 //           class ForwardIterator2>
@@ -61,6 +69,8 @@ struct Test {
       // empty ranges
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
              std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
     }
@@ -68,18 +78,24 @@ struct Test {
       // single element only
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range without mismatch
       std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range with mismatch
       std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
     }
@@ -102,10 +118,14 @@ struct Test {
       rhs = lhs;
       runway_sample(lhs.size(), [&](size_t i) {
         lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
                std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         lhs[i] = i;
       });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }

>From 4aab10d6cc54f3abd475bd3aae03ac7d4103eb45 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:20:42 +0100
Subject: [PATCH 04/14] Added predicated versions

---
 libcxx/include/__algorithm/pstl.h             |  49 +++++
 .../mismatch/pstl.mismatch.pass.cpp           |   2 +
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 178 ++++++++++++++++++
 3 files changed, 229 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 01c61fb00f4d5..bc48fd9f48d7f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
+#  include <__utility/pair.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -138,6 +139,31 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
@@ -161,6 +187,29 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 0cb5c7632eed8..8584f2779927f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -42,6 +42,8 @@
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
+static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
new file mode 100644
index 0000000000000..39e952cbc38f6
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     BinaryPredicate pred);
+//
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2,
+//                                                     BinaryPredicate pred);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+struct Pred {
+  bool operator()(int a, int b) const { return a * 2 == b; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      assert(std::mismatch(
+                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {1};
+      std::array<int, 1> rhs = {2};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 2};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(84);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From 2da95a5c532de58960651a18c9a93c9435577a89 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:39:47 +0100
Subject: [PATCH 05/14] Added tests against custom types

---
 libcxx/include/__pstl/backends/default.h      |  1 +
 .../mismatch/pstl.mismatch.pass.cpp           | 53 ++++++++++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 56 ++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 05b328beae80f..f4f73e81ed90b 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -16,6 +16,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 8584f2779927f..5e46831539c30 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -63,6 +63,30 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
+
 template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
@@ -150,6 +174,29 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -157,5 +204,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 39e952cbc38f6..9c0dfc6c0c127 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -67,8 +67,31 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
 struct Pred {
-  bool operator()(int a, int b) const { return a * 2 == b; }
+  bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
+  bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
 };
 
 template <class Iter1, class Iter2>
@@ -167,6 +190,31 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -174,5 +222,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }

>From 2fb2bb1a154212dc1cbf3a1ea9e3e1cf5a00db48 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:52:50 +0100
Subject: [PATCH 06/14] Added a nodiscard test

---
 libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 049c8dc88c2da..c538d7841dce6 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -58,4 +58,12 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }

>From 8c35d0cac6fc6a3daf494de08ac0a2fd365d50f1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:57:47 +0100
Subject: [PATCH 07/14] Added an iterator requirement test

---
 .../pstl.iterator-requirements.verify.cpp        | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index fbe3460b67f17..4526cad0615b0 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, pred);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}

>From a8b984852860283d06ab06bbdf05a28f01965326 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:00:09 +0100
Subject: [PATCH 08/14] Added an include of <__algorithm/min.h>

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 11ffdc332d633..8361731e78cfc 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,8 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__algorithm/mismatch.h>
 #include <__config>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/min.h>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>

>From 3845b7e0e242d455f0fcc68a814ed625544c3d8c Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:05:29 +0100
Subject: [PATCH 09/14] Added an exception handling test

---
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  4 +--
 .../pstl.exception_handling.pass.cpp          | 25 +++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 8361731e78cfc..597a47fb185c8 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,9 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__config>
-#include <__algorithm/mismatch.h>
 #include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__config>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 8af2a0a15fd5c..820a5d92c19e0 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -248,6 +248,31 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // mismatch(first1, last1, first2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
+        });
+
+        // mismatch(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // mismatch(first1, last1, first2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
+        });
+
+        // mismatch(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From fbc073f5742c81ae78d12058ecf4618213afb6f0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 09:43:45 +0100
Subject: [PATCH 10/14] Changed the variable name to avoid shadowing

---
 .../alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 12 ++++++------
 .../mismatch/pstl.mismatch_pred.pass.cpp             | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 5e46831539c30..941fe07b58327 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -67,22 +67,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 9c0dfc6c0c127..a4b15a56bd071 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -71,22 +71,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Pred {

>From baaa82257b3d2f94d4bf9d02f6761e7cf3ee9df6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 20:27:14 +0100
Subject: [PATCH 11/14] Use raw C array in the tests

---
 .../mismatch/pstl.mismatch.pass.cpp           | 153 +++++++-------
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 192 +++++++++++-------
 2 files changed, 198 insertions(+), 147 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 941fe07b58327..17edf34ee7832 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -28,7 +28,6 @@
 //                                                     ForwardIterator2 last2);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -93,82 +92,98 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(
+          std::mismatch(
+              policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
+          std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 1};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 1};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(
+            std::mismatch(
+                policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+            std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(42);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 42);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(
+                   policy,
+                   Iter1(std::begin(lhs)),
+                   Iter1(std::begin(lhs) + i),
+                   Iter2(std::begin(rhs)),
+                   Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i)) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -179,20 +194,22 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index a4b15a56bd071..18c4fe1abad0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -32,7 +32,6 @@
 //                                                     BinaryPredicate pred);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -100,91 +99,120 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(
-                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::begin(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::begin(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {1};
-      std::array<int, 1> rhs = {2};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {1};
+      int rhs[1] = {2};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 2};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 2};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(84);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 84);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::begin(lhs) + i),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -195,22 +223,28 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };

>From 212e25406377a6a5da7fc7679015000a4086e4a1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:08:55 +0100
Subject: [PATCH 12/14] Fixed typos in the comment

---
 .../algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 2 +-
 .../alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 17edf34ee7832..ed59fa9eca669 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -46,7 +46,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 18c4fe1abad0c..338503d784a31 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -50,7 +50,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;

>From b80675016128dbeb1db5a3084edee9bb6ab89fea Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:43:20 +0100
Subject: [PATCH 13/14] Added comments explaining what the parameters mean

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 597a47fb185c8..3d702b03d3afe 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -63,8 +63,9 @@ struct __cpu_parallel_mismatch {
             return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
                 .first;
           },
-          less<>{},
-          true);
+          less<>{}, // `less` here means the lowest index among the mismatches
+          true      // `true` here means we want the first mismatch, not the last
+      );
       if (!__res) {
         return std::nullopt; // Failed to run the algorithm, propagate the error.
       }

>From 12ac822e8571b5d944ffea6d4f62396a954f4672 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:50:51 +0100
Subject: [PATCH 14/14] Added tests of the return types

---
 .../mismatch/pstl.mismatch.pass.cpp              | 10 ++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp         | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index ed59fa9eca669..cc7ef6feb2356 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -90,6 +90,16 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1]       = {0};
+      int rhs[1]       = {0};
+      auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
+      auto res_4legged = std::mismatch(
+          policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 338503d784a31..d80da0cf0b542 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -97,6 +97,22 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      auto res_3legged =
+          std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
+      auto res_4legged = std::mismatch(
+          policy,
+          Iter1(std::begin(lhs)),
+          Iter1(std::begin(lhs)),
+          Iter2(std::begin(rhs)),
+          Iter2(std::begin(rhs)),
+          Pred{});
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};


From libcxx-commits at lists.llvm.org  Wed Jul 15 17:56:05 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Wed, 15 Jul 2026 17:56:05 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209473@github.com>
Message-ID: <6a582c25.c574b9b4.2aa0bf.909f@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `clang-aarch64-sve2-vla` running on `linaro-g4-02` while building `clang-tools-extra,clang,cmake,flang,libcxx,lld,llvm` at step 7 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/198/builds/14201

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 7 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'LLVM :: CodeGen/SystemZ/zos-ppa2.ll' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
sed -e 's/!"MODE"/!"ascii"/' -e 's/BYTE/85/' /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/test/CodeGen/SystemZ/zos-ppa2.ll > /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: sed -e 's/!"MODE"/!"ascii"/' -e s/BYTE/85/ /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/llvm/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
# RUN: at line 2
/home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true < /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll |  /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/FileCheck /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true
# executed command: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/bin/FileCheck /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# .---command stderr------------
# | /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll:24:10: error: CHECK: expected string not found in input
# | ; CHECK: DC XL6'F2F3F0F0F0F0'
# |          ^
# | <stdin>:17:39: note: scanning from here
# |  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0' 
# |                                       ^
# | <stdin>:18:2: note: possible intended match here
# |  DC XL6'F2F4F0F0F0F0' 
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /home/tcwg-buildbot/worker/clang-aarch64-sve2-vla/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             .
# |             .
# |             .
# |            12:  DC XL4'00000000'  
# |            13:  DC XL1'85'  
# |            14:  DC XL1'00'  
# |            15:  DC XL2'0000'  
# |            16: L#DVS DS 0H  
# |            17:  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0'  
# | check:24'0                                          {    search range start (exclusive)
# | check:24'1                                               error: no match found in search range
# |            18:  DC XL6'F2F4F0F0F0F0'  
# | check:24'2      ?                       possible intended match
# |            19:  DC XL2'0000'  
# |            20: stdin#C CSECT  
# |            21: C_@@QPPA2 CATTR ALIGN(3),FILL(0),NOTEXECUTABLE,READONLY,RMODE(64),PART(.  
# |            22:  .&ppa2)  
# |            23: .&ppa2 XATTR LINKAGE(OS),REFERENCE(DATA),SCOPE(SECTION)  
# |             .
# |             .
...

```

</details>

https://github.com/llvm/llvm-project/pull/209473

From libcxx-commits at lists.llvm.org  Wed Jul 15 19:05:48 2026
From: libcxx-commits at lists.llvm.org (Hubert Tong via libcxx-commits)
Date: Wed, 15 Jul 2026 19:05:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][utils] Fix LIT `%if`
	substitution support (PR #209358)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209358@github.com>
Message-ID: <6a583c7c.bd93e731.18e8ee.6b90@mx.google.com>


hubert-reinterpretcast wrote:

There is no reason to believe that the failure in "[Build and Test libc++ / stage3 (bootstrapping-build)](https://github.com/llvm/llvm-project/actions/runs/29341201833/job/87126499928?pr=209358)" on `lldb-api :: lang/cpp/std-function-step-into-callable/TestStdFunctionStepIntoCallable.py` has anything to do with the change here.

https://github.com/llvm/llvm-project/pull/209358

From libcxx-commits at lists.llvm.org  Wed Jul 15 19:49:07 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 15 Jul 2026 19:49:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][docs] Mark LWG3116 as Complete
	(PR #209916)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209916@github.com>
Message-ID: <6a5846a3.481afadf.34be4b.134b@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/209916

From libcxx-commits at lists.llvm.org  Wed Jul 15 19:49:38 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 15 Jul 2026 19:49:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][docs] Mark LWG3116 as Complete
	(PR #209916)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209916@github.com>
Message-ID: <6a5846c2.510f576d.d8fd6.43fd@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/209916

From libcxx-commits at lists.llvm.org  Wed Jul 15 21:00:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 15 Jul 2026 21:00:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a585773.7270d2fd.33a4d0.cef1@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/28] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/28] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/28] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/28] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/28] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/28] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/28] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/28] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/28] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/28] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/28] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/28] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/28] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/28] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/28] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/28] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/28] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/28] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/28] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/28] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/28] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}

>From 86f59d72480a80cb0d3325cac1ee59d5b08524bb Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 04:18:49 -0700
Subject: [PATCH 22/28] Permit reserve_hint's unqualified lookup

reserve_hint must use ADL per the specification, and thus needs to
be exempted from the libcpp-robust-against-adl check like other
CPOs.
---
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..16c45a524811e 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -20,7 +20,8 @@ AST_MATCHER(clang::CallExpr, isOperator) { return llvm::isa<clang::CXXOperatorCa
 
 AST_MATCHER(clang::UnresolvedLookupExpr, isCustomizationPoint) {
   return std::ranges::any_of(
-      std::array{"swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin"},
+      std::array{
+          "swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin", "reserve_hint"},
       [&](const char* func) { return Node.getName().getAsString() == func; });
 }
 

>From 0020ac432662be8ace647b3c92368cbc13744c24 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 06:14:47 -0700
Subject: [PATCH 23/28] Fix unused variable warning from GCC

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 11379bedf7bf2..6b6a8523afbdc 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -39,7 +39,7 @@ struct HasSizeMember {
 };
 
 struct HasSizeFunction {
-  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+  friend constexpr std::size_t size(HasSizeFunction) { return 42; }
 };
 
 struct HasReserveHintMember {
@@ -47,7 +47,7 @@ struct HasReserveHintMember {
 };
 
 struct HasReserveHintFunction {
-  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction) { return 42; }
 };
 
 struct HasReserveHintMemberBool {
@@ -86,12 +86,12 @@ ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})),
 struct HasSizeAndReserveHint {
   constexpr std::size_t size() { return 42; }
   constexpr std::size_t reserve_hint() { return 0; }
-  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint) { return 0; }
 };
 
 struct HasReserveHintMemberAndFunction {
   constexpr std::size_t reserve_hint() { return 42; }
-  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction) { return 0; }
 };
 
 static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);

>From 6b09d9baea9a8cd77b0c916fcc0ae043fe8a1d0d Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 22:24:20 -0700
Subject: [PATCH 24/28] Address review comments

---
 .../range.adaptors/range.stride.view/nodiscard.verify.cpp     | 4 ++++
 .../test/std/ranges/range.adaptors/range.common.view/types.h  | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 3cc5b6f2db9b2..e4df227ff44c8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -32,6 +32,10 @@ void test() {
 #if TEST_STD_VER >= 26
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.reserve_hint();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(sv).reserve_hint();
+
 #endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 130423389ad68..670837aa47744 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,8 +11,8 @@
 
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
   int* begin_ = nullptr;

>From d02f2fb321edd1e7d48f651d6d39a7954b251206 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 19:29:28 -0700
Subject: [PATCH 25/28] Empty commit for CI


>From fdb58ee8c8174b1bd8162cb7729cdff9e65eb98f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 20:10:21 -0700
Subject: [PATCH 26/28] Avoid creating an empty namespace before c++26

---
 libcxx/include/__ranges/reserve_hint.h | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
index b3b4be59f8941..e58990bf1194e 100644
--- a/libcxx/include/__ranges/reserve_hint.h
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -20,9 +20,10 @@
 #  pragma GCC system_header
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 26
 // [range.prim.size.hint]
 
 namespace ranges {
@@ -72,8 +73,8 @@ inline constexpr auto reserve_hint = __reserve_hint::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#endif // _LIBCPP_STD_VER >= 26
-
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP___RANGES_RESERVE_HINT_H

>From f09928004b010011e4cb7db2f9fbbc01cedab590 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 14:50:58 -0700
Subject: [PATCH 27/28] Bump version in reserve_hint status entries

This updates the status page to show that we are now targeting
libc++24.
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 718dbf6677aba..7a4c7087dfd11 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","24","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 398ba8e4d60bd..d98a62cfa3418 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","24","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 4453a8674737ea7dc8155a860bf891d5d947f37a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 20:57:39 -0700
Subject: [PATCH 28/28] Remove comment on #else block in concepts.h

This seems to better align with standard practice.
---
 libcxx/include/__ranges/concepts.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index cae59e3946daa..99a2acfe922c5 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -91,7 +91,7 @@ concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::r
 template <class _Tp>
 concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
-#  else // _LIBCPP_STD_VER < 26
+#  else
 
 // [range.sized]
 template <class _Tp>


From libcxx-commits at lists.llvm.org  Wed Jul 15 21:00:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 15 Jul 2026 21:00:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a585773.5a0412d2.82e8e.00b3@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/28] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/28] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/28] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/28] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/28] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/28] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/28] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/28] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/28] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/28] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/28] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/28] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/28] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/28] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/28] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/28] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/28] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/28] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/28] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/28] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/28] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}

>From 86f59d72480a80cb0d3325cac1ee59d5b08524bb Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 04:18:49 -0700
Subject: [PATCH 22/28] Permit reserve_hint's unqualified lookup

reserve_hint must use ADL per the specification, and thus needs to
be exempted from the libcpp-robust-against-adl check like other
CPOs.
---
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..16c45a524811e 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -20,7 +20,8 @@ AST_MATCHER(clang::CallExpr, isOperator) { return llvm::isa<clang::CXXOperatorCa
 
 AST_MATCHER(clang::UnresolvedLookupExpr, isCustomizationPoint) {
   return std::ranges::any_of(
-      std::array{"swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin"},
+      std::array{
+          "swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin", "reserve_hint"},
       [&](const char* func) { return Node.getName().getAsString() == func; });
 }
 

>From 0020ac432662be8ace647b3c92368cbc13744c24 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 06:14:47 -0700
Subject: [PATCH 23/28] Fix unused variable warning from GCC

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 11379bedf7bf2..6b6a8523afbdc 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -39,7 +39,7 @@ struct HasSizeMember {
 };
 
 struct HasSizeFunction {
-  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+  friend constexpr std::size_t size(HasSizeFunction) { return 42; }
 };
 
 struct HasReserveHintMember {
@@ -47,7 +47,7 @@ struct HasReserveHintMember {
 };
 
 struct HasReserveHintFunction {
-  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction) { return 42; }
 };
 
 struct HasReserveHintMemberBool {
@@ -86,12 +86,12 @@ ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})),
 struct HasSizeAndReserveHint {
   constexpr std::size_t size() { return 42; }
   constexpr std::size_t reserve_hint() { return 0; }
-  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint) { return 0; }
 };
 
 struct HasReserveHintMemberAndFunction {
   constexpr std::size_t reserve_hint() { return 42; }
-  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction) { return 0; }
 };
 
 static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);

>From 6b09d9baea9a8cd77b0c916fcc0ae043fe8a1d0d Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 22:24:20 -0700
Subject: [PATCH 24/28] Address review comments

---
 .../range.adaptors/range.stride.view/nodiscard.verify.cpp     | 4 ++++
 .../test/std/ranges/range.adaptors/range.common.view/types.h  | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 3cc5b6f2db9b2..e4df227ff44c8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -32,6 +32,10 @@ void test() {
 #if TEST_STD_VER >= 26
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.reserve_hint();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(sv).reserve_hint();
+
 #endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 130423389ad68..670837aa47744 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,8 +11,8 @@
 
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
   int* begin_ = nullptr;

>From d02f2fb321edd1e7d48f651d6d39a7954b251206 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 19:29:28 -0700
Subject: [PATCH 25/28] Empty commit for CI


>From fdb58ee8c8174b1bd8162cb7729cdff9e65eb98f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 20:10:21 -0700
Subject: [PATCH 26/28] Avoid creating an empty namespace before c++26

---
 libcxx/include/__ranges/reserve_hint.h | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
index b3b4be59f8941..e58990bf1194e 100644
--- a/libcxx/include/__ranges/reserve_hint.h
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -20,9 +20,10 @@
 #  pragma GCC system_header
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 26
 // [range.prim.size.hint]
 
 namespace ranges {
@@ -72,8 +73,8 @@ inline constexpr auto reserve_hint = __reserve_hint::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#endif // _LIBCPP_STD_VER >= 26
-
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP___RANGES_RESERVE_HINT_H

>From f09928004b010011e4cb7db2f9fbbc01cedab590 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 14:50:58 -0700
Subject: [PATCH 27/28] Bump version in reserve_hint status entries

This updates the status page to show that we are now targeting
libc++24.
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 718dbf6677aba..7a4c7087dfd11 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","24","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 398ba8e4d60bd..d98a62cfa3418 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","24","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 4453a8674737ea7dc8155a860bf891d5d947f37a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 20:57:39 -0700
Subject: [PATCH 28/28] Remove comment on #else block in concepts.h

This seems to better align with standard practice.
---
 libcxx/include/__ranges/concepts.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index cae59e3946daa..99a2acfe922c5 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -91,7 +91,7 @@ concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::r
 template <class _Tp>
 concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
-#  else // _LIBCPP_STD_VER < 26
+#  else
 
 // [range.sized]
 template <class _Tp>


From libcxx-commits at lists.llvm.org  Wed Jul 15 21:12:49 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 15 Jul 2026 21:12:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a585a41.b4a331f0.261d49.4367@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/29] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/29] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/29] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/29] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/29] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/29] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/29] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/29] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/29] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/29] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/29] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/29] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/29] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/29] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/29] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/29] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/29] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/29] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/29] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/29] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/29] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}

>From 86f59d72480a80cb0d3325cac1ee59d5b08524bb Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 04:18:49 -0700
Subject: [PATCH 22/29] Permit reserve_hint's unqualified lookup

reserve_hint must use ADL per the specification, and thus needs to
be exempted from the libcpp-robust-against-adl check like other
CPOs.
---
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..16c45a524811e 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -20,7 +20,8 @@ AST_MATCHER(clang::CallExpr, isOperator) { return llvm::isa<clang::CXXOperatorCa
 
 AST_MATCHER(clang::UnresolvedLookupExpr, isCustomizationPoint) {
   return std::ranges::any_of(
-      std::array{"swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin"},
+      std::array{
+          "swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin", "reserve_hint"},
       [&](const char* func) { return Node.getName().getAsString() == func; });
 }
 

>From 0020ac432662be8ace647b3c92368cbc13744c24 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 06:14:47 -0700
Subject: [PATCH 23/29] Fix unused variable warning from GCC

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 11379bedf7bf2..6b6a8523afbdc 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -39,7 +39,7 @@ struct HasSizeMember {
 };
 
 struct HasSizeFunction {
-  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+  friend constexpr std::size_t size(HasSizeFunction) { return 42; }
 };
 
 struct HasReserveHintMember {
@@ -47,7 +47,7 @@ struct HasReserveHintMember {
 };
 
 struct HasReserveHintFunction {
-  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction) { return 42; }
 };
 
 struct HasReserveHintMemberBool {
@@ -86,12 +86,12 @@ ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})),
 struct HasSizeAndReserveHint {
   constexpr std::size_t size() { return 42; }
   constexpr std::size_t reserve_hint() { return 0; }
-  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint) { return 0; }
 };
 
 struct HasReserveHintMemberAndFunction {
   constexpr std::size_t reserve_hint() { return 42; }
-  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction) { return 0; }
 };
 
 static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);

>From 6b09d9baea9a8cd77b0c916fcc0ae043fe8a1d0d Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 22:24:20 -0700
Subject: [PATCH 24/29] Address review comments

---
 .../range.adaptors/range.stride.view/nodiscard.verify.cpp     | 4 ++++
 .../test/std/ranges/range.adaptors/range.common.view/types.h  | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 3cc5b6f2db9b2..e4df227ff44c8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -32,6 +32,10 @@ void test() {
 #if TEST_STD_VER >= 26
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.reserve_hint();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(sv).reserve_hint();
+
 #endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 130423389ad68..670837aa47744 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,8 +11,8 @@
 
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
   int* begin_ = nullptr;

>From d02f2fb321edd1e7d48f651d6d39a7954b251206 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 19:29:28 -0700
Subject: [PATCH 25/29] Empty commit for CI


>From fdb58ee8c8174b1bd8162cb7729cdff9e65eb98f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 20:10:21 -0700
Subject: [PATCH 26/29] Avoid creating an empty namespace before c++26

---
 libcxx/include/__ranges/reserve_hint.h | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
index b3b4be59f8941..e58990bf1194e 100644
--- a/libcxx/include/__ranges/reserve_hint.h
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -20,9 +20,10 @@
 #  pragma GCC system_header
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 26
 // [range.prim.size.hint]
 
 namespace ranges {
@@ -72,8 +73,8 @@ inline constexpr auto reserve_hint = __reserve_hint::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#endif // _LIBCPP_STD_VER >= 26
-
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP___RANGES_RESERVE_HINT_H

>From f09928004b010011e4cb7db2f9fbbc01cedab590 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 14:50:58 -0700
Subject: [PATCH 27/29] Bump version in reserve_hint status entries

This updates the status page to show that we are now targeting
libc++24.
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 718dbf6677aba..7a4c7087dfd11 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","24","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 398ba8e4d60bd..d98a62cfa3418 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","24","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 4453a8674737ea7dc8155a860bf891d5d947f37a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 20:57:39 -0700
Subject: [PATCH 28/29] Remove comment on #else block in concepts.h

This seems to better align with standard practice.
---
 libcxx/include/__ranges/concepts.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index cae59e3946daa..99a2acfe922c5 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -91,7 +91,7 @@ concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::r
 template <class _Tp>
 concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
-#  else // _LIBCPP_STD_VER < 26
+#  else
 
 // [range.sized]
 template <class _Tp>

>From 4e65763bcb696ecda24693a6b67cc7e0318ceea3 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 21:06:33 -0700
Subject: [PATCH 29/29] Add spaces between `//`` and comment text

This change is applied on all of my `#endif` comments, which were
consistently irregular in missing spaces in this location.
---
 libcxx/include/__ranges/adjacent_transform_view.h | 2 +-
 libcxx/include/__ranges/adjacent_view.h           | 2 +-
 libcxx/include/__ranges/as_rvalue_view.h          | 2 +-
 libcxx/include/__ranges/common_view.h             | 2 +-
 libcxx/include/__ranges/drop_view.h               | 2 +-
 libcxx/include/__ranges/elements_view.h           | 2 +-
 libcxx/include/__ranges/enumerate_view.h          | 2 +-
 libcxx/include/__ranges/owning_view.h             | 2 +-
 libcxx/include/__ranges/ref_view.h                | 2 +-
 libcxx/include/__ranges/reverse_view.h            | 2 +-
 libcxx/include/__ranges/stride_view.h             | 2 +-
 libcxx/include/__ranges/take_view.h               | 2 +-
 libcxx/include/__ranges/transform_view.h          | 2 +-
 13 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 6991fe2e19803..a833c0dee4fd4 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -164,7 +164,7 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
     return __inner_.reserve_hint();
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index c483c04a11373..db9edb5cf46f2 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -163,7 +163,7 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     return static_cast<_ST>(__sz);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 1e1dccf55fe7c..ddd2f9a75c267 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -115,7 +115,7 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 40acab0331676..c232fb533e1db 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -117,7 +117,7 @@ class common_view : public view_interface<common_view<_View>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index c165e848b6e79..2776df6175797 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -157,7 +157,7 @@ class drop_view : public view_interface<drop_view<_View>> {
     return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9397e6b109aa6..3d91f5e4c1d6e 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -148,7 +148,7 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 26d9098b40e87..8845e6caa1421 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -131,7 +131,7 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 4b154b7c0b9aa..e30f6da11f907 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -115,7 +115,7 @@ class owning_view : public view_interface<owning_view<_Rp>> {
     return ranges::reserve_hint(__r_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index f3f6eb11620fd..9d65a536d5a09 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -82,7 +82,7 @@ class ref_view : public view_interface<ref_view<_Range>> {
     return ranges::reserve_hint(*__range_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index 7f2ea094710fb..fe8e40b74e1b7 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -127,7 +127,7 @@ class reverse_view : public view_interface<reverse_view<_View>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 45a57015eab6a..3f1c841cdd047 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -147,7 +147,7 @@ class stride_view : public view_interface<stride_view<_View>> {
     return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index c2c61b1269ade..53a800f7f4b44 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -176,7 +176,7 @@ class take_view : public view_interface<take_view<_View>> {
     return std::__to_unsigned_like(__count_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 95af90468d536..be4ae7da77f4e 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -155,7 +155,7 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>


From libcxx-commits at lists.llvm.org  Wed Jul 15 21:13:12 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 15 Jul 2026 21:13:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a585a58.4a342cbc.cf320.c9a2@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/29] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/29] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/29] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/29] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/29] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/29] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/29] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/29] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/29] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/29] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/29] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/29] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/29] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/29] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/29] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/29] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/29] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/29] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/29] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/29] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/29] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}

>From 86f59d72480a80cb0d3325cac1ee59d5b08524bb Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 04:18:49 -0700
Subject: [PATCH 22/29] Permit reserve_hint's unqualified lookup

reserve_hint must use ADL per the specification, and thus needs to
be exempted from the libcpp-robust-against-adl check like other
CPOs.
---
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..16c45a524811e 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -20,7 +20,8 @@ AST_MATCHER(clang::CallExpr, isOperator) { return llvm::isa<clang::CXXOperatorCa
 
 AST_MATCHER(clang::UnresolvedLookupExpr, isCustomizationPoint) {
   return std::ranges::any_of(
-      std::array{"swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin"},
+      std::array{
+          "swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin", "reserve_hint"},
       [&](const char* func) { return Node.getName().getAsString() == func; });
 }
 

>From 0020ac432662be8ace647b3c92368cbc13744c24 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 06:14:47 -0700
Subject: [PATCH 23/29] Fix unused variable warning from GCC

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 11379bedf7bf2..6b6a8523afbdc 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -39,7 +39,7 @@ struct HasSizeMember {
 };
 
 struct HasSizeFunction {
-  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+  friend constexpr std::size_t size(HasSizeFunction) { return 42; }
 };
 
 struct HasReserveHintMember {
@@ -47,7 +47,7 @@ struct HasReserveHintMember {
 };
 
 struct HasReserveHintFunction {
-  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction) { return 42; }
 };
 
 struct HasReserveHintMemberBool {
@@ -86,12 +86,12 @@ ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})),
 struct HasSizeAndReserveHint {
   constexpr std::size_t size() { return 42; }
   constexpr std::size_t reserve_hint() { return 0; }
-  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint) { return 0; }
 };
 
 struct HasReserveHintMemberAndFunction {
   constexpr std::size_t reserve_hint() { return 42; }
-  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction) { return 0; }
 };
 
 static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);

>From 6b09d9baea9a8cd77b0c916fcc0ae043fe8a1d0d Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 22:24:20 -0700
Subject: [PATCH 24/29] Address review comments

---
 .../range.adaptors/range.stride.view/nodiscard.verify.cpp     | 4 ++++
 .../test/std/ranges/range.adaptors/range.common.view/types.h  | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 3cc5b6f2db9b2..e4df227ff44c8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -32,6 +32,10 @@ void test() {
 #if TEST_STD_VER >= 26
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.reserve_hint();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(sv).reserve_hint();
+
 #endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 130423389ad68..670837aa47744 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,8 +11,8 @@
 
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
   int* begin_ = nullptr;

>From d02f2fb321edd1e7d48f651d6d39a7954b251206 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 19:29:28 -0700
Subject: [PATCH 25/29] Empty commit for CI


>From fdb58ee8c8174b1bd8162cb7729cdff9e65eb98f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 20:10:21 -0700
Subject: [PATCH 26/29] Avoid creating an empty namespace before c++26

---
 libcxx/include/__ranges/reserve_hint.h | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
index b3b4be59f8941..e58990bf1194e 100644
--- a/libcxx/include/__ranges/reserve_hint.h
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -20,9 +20,10 @@
 #  pragma GCC system_header
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 26
 // [range.prim.size.hint]
 
 namespace ranges {
@@ -72,8 +73,8 @@ inline constexpr auto reserve_hint = __reserve_hint::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#endif // _LIBCPP_STD_VER >= 26
-
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP___RANGES_RESERVE_HINT_H

>From f09928004b010011e4cb7db2f9fbbc01cedab590 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 14:50:58 -0700
Subject: [PATCH 27/29] Bump version in reserve_hint status entries

This updates the status page to show that we are now targeting
libc++24.
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 718dbf6677aba..7a4c7087dfd11 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","24","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 398ba8e4d60bd..d98a62cfa3418 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","24","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 4453a8674737ea7dc8155a860bf891d5d947f37a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 20:57:39 -0700
Subject: [PATCH 28/29] Remove comment on #else block in concepts.h

This seems to better align with standard practice.
---
 libcxx/include/__ranges/concepts.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index cae59e3946daa..99a2acfe922c5 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -91,7 +91,7 @@ concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::r
 template <class _Tp>
 concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
-#  else // _LIBCPP_STD_VER < 26
+#  else
 
 // [range.sized]
 template <class _Tp>

>From f35c0ad2597b8f1e4c438355b127e7322652a9f9 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 21:06:33 -0700
Subject: [PATCH 29/29] Add spaces between `//` and comment text

This change is applied on all of my `#endif` comments, which were
consistently irregular in missing spaces in this location.
---
 libcxx/include/__ranges/adjacent_transform_view.h | 2 +-
 libcxx/include/__ranges/adjacent_view.h           | 2 +-
 libcxx/include/__ranges/as_rvalue_view.h          | 2 +-
 libcxx/include/__ranges/common_view.h             | 2 +-
 libcxx/include/__ranges/drop_view.h               | 2 +-
 libcxx/include/__ranges/elements_view.h           | 2 +-
 libcxx/include/__ranges/enumerate_view.h          | 2 +-
 libcxx/include/__ranges/owning_view.h             | 2 +-
 libcxx/include/__ranges/ref_view.h                | 2 +-
 libcxx/include/__ranges/reverse_view.h            | 2 +-
 libcxx/include/__ranges/stride_view.h             | 2 +-
 libcxx/include/__ranges/take_view.h               | 2 +-
 libcxx/include/__ranges/transform_view.h          | 2 +-
 13 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 6991fe2e19803..a833c0dee4fd4 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -164,7 +164,7 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
     return __inner_.reserve_hint();
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index c483c04a11373..db9edb5cf46f2 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -163,7 +163,7 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     return static_cast<_ST>(__sz);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 1e1dccf55fe7c..ddd2f9a75c267 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -115,7 +115,7 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 40acab0331676..c232fb533e1db 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -117,7 +117,7 @@ class common_view : public view_interface<common_view<_View>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index c165e848b6e79..2776df6175797 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -157,7 +157,7 @@ class drop_view : public view_interface<drop_view<_View>> {
     return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9397e6b109aa6..3d91f5e4c1d6e 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -148,7 +148,7 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 26d9098b40e87..8845e6caa1421 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -131,7 +131,7 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 4b154b7c0b9aa..e30f6da11f907 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -115,7 +115,7 @@ class owning_view : public view_interface<owning_view<_Rp>> {
     return ranges::reserve_hint(__r_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index f3f6eb11620fd..9d65a536d5a09 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -82,7 +82,7 @@ class ref_view : public view_interface<ref_view<_Range>> {
     return ranges::reserve_hint(*__range_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index 7f2ea094710fb..fe8e40b74e1b7 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -127,7 +127,7 @@ class reverse_view : public view_interface<reverse_view<_View>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 45a57015eab6a..3f1c841cdd047 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -147,7 +147,7 @@ class stride_view : public view_interface<stride_view<_View>> {
     return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index c2c61b1269ade..53a800f7f4b44 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -176,7 +176,7 @@ class take_view : public view_interface<take_view<_View>> {
     return std::__to_unsigned_like(__count_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 95af90468d536..be4ae7da77f4e 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -155,7 +155,7 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>


From libcxx-commits at lists.llvm.org  Wed Jul 15 22:07:08 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 15 Jul 2026 22:07:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::swap of locales
	(PR #209760)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209760@github.com>
Message-ID: <6a5866fc.a1accbb4.3afc30.1e6a@mx.google.com>


================
@@ -143,8 +143,14 @@ private:
   friend bool has_facet(const locale&) _NOEXCEPT;
   template <class _Facet>
   friend const _Facet& use_facet(const locale&);
+
+  friend void swap(locale&, locale&);
 };
 
+inline void swap(locale& __lhs, locale& __rhs) {
----------------
philnik777 wrote:

The standard doesn't allow for a move constructor.

https://github.com/llvm/llvm-project/pull/209760

From libcxx-commits at lists.llvm.org  Wed Jul 15 22:52:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 15 Jul 2026 22:52:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fixes LWG4072 (PR #209968)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209968@github.com>
Message-ID: <6a587197.5a0412d2.82e8e.c518@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Dan Shan (HumanThe2nd)

<details>
<summary>Changes</summary>

Resolves #<!-- -->118345 (LWG4072: std::optional comparisons: constrain harder)

The heterogeneous operators (==, !=, &lt;, &lt;=, &gt;, &gt;=) for arguments T and U don't check that T or U are not a std::optional themselves.

This allowed the operators to cause ambiguous overload resolution instead of falling back to optional's own operators.

Added `!__is_std_optional&lt;_Up&gt;::value` (and the `_Tp` equivalent for the reversed) to all twelve operators.

As required by the LLVM Project's AI use policy:
- The fix and test processes were revised and verified with AI assistance.

---
Full diff: https://github.com/llvm/llvm-project/pull/209968.diff


7 Files Affected:

- (modified) libcxx/include/optional (+24-12) 
- (modified) libcxx/test/std/utilities/optional/optional.relops/equal.pass.cpp (+3) 
- (modified) libcxx/test/std/utilities/optional/optional.relops/greater_equal.pass.cpp (+3) 
- (modified) libcxx/test/std/utilities/optional/optional.relops/greater_than.pass.cpp (+3) 
- (modified) libcxx/test/std/utilities/optional/optional.relops/less_equal.pass.cpp (+3) 
- (modified) libcxx/test/std/utilities/optional/optional.relops/less_than.pass.cpp (+3) 
- (modified) libcxx/test/std/utilities/optional/optional.relops/not_equal.pass.cpp (+3) 


``````````diff
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 2499479348892..eb93af6857751 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -1543,7 +1543,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>&
 template <
     class _Tp,
     class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool> &&
+                    !__is_std_optional<_Up>::value,
                 int> = 0>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v) {
   if (__x.has_value())
@@ -1554,7 +1555,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const
 template <
     class _Tp,
     class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool> &&
+                    !__is_std_optional<_Tp>::value,
                 int> = 0>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x) {
   if (__x.has_value())
@@ -1565,7 +1567,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_
 template <
     class _Tp,
     class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool> &&
+                    !__is_std_optional<_Up>::value,
                 int> = 0>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v) {
   if (__x.has_value())
@@ -1576,7 +1579,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const
 template <
     class _Tp,
     class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool> &&
+                    !__is_std_optional<_Tp>::value,
                 int> = 0>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x) {
   if (__x.has_value())
@@ -1586,7 +1590,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_
 
 template < class _Tp,
            class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool> &&
+                           !__is_std_optional<_Up>::value,
                        int> = 0>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v) {
   if (__x.has_value())
@@ -1596,7 +1601,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _
 
 template < class _Tp,
            class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool> &&
+                           !__is_std_optional<_Tp>::value,
                        int> = 0>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x) {
   if (__x.has_value())
@@ -1607,7 +1613,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_U
 template <
     class _Tp,
     class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool> &&
+                    !__is_std_optional<_Up>::value,
                 int> = 0>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v) {
   if (__x.has_value())
@@ -1618,7 +1625,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const
 template <
     class _Tp,
     class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool> &&
+                    !__is_std_optional<_Tp>::value,
                 int> = 0>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x) {
   if (__x.has_value())
@@ -1628,7 +1636,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_
 
 template < class _Tp,
            class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool> &&
+                           !__is_std_optional<_Up>::value,
                        int> = 0>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v) {
   if (__x.has_value())
@@ -1638,7 +1647,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _
 
 template < class _Tp,
            class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool> &&
+                           !__is_std_optional<_Tp>::value,
                        int> = 0>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x) {
   if (__x.has_value())
@@ -1649,7 +1659,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_U
 template <
     class _Tp,
     class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool> &&
+                    !__is_std_optional<_Up>::value,
                 int> = 0>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v) {
   if (__x.has_value())
@@ -1660,7 +1671,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const
 template <
     class _Tp,
     class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool> &&
+                    !__is_std_optional<_Tp>::value,
                 int> = 0>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x) {
   if (__x.has_value())
diff --git a/libcxx/test/std/utilities/optional/optional.relops/equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/equal.pass.cpp
index 6915b604d4ef4..dba9cbd78146c 100644
--- a/libcxx/test/std/utilities/optional/optional.relops/equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.relops/equal.pass.cpp
@@ -29,6 +29,9 @@ static_assert(HasOperatorEqual<std::optional<EqualityComparable>, std::optional<
 static_assert(!HasOperatorEqual<std::optional<NonComparable>>);
 static_assert(!HasOperatorEqual<std::optional<EqualityComparable>, std::optional<NonComparable>>);
 
+static_assert(!HasOperatorEqual<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorEqual<std::optional<int>, std::optional<void*>>);
+
 #endif
 
 using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.relops/greater_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/greater_equal.pass.cpp
index d14e7df6abc80..b31f5047f1090 100644
--- a/libcxx/test/std/utilities/optional/optional.relops/greater_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.relops/greater_equal.pass.cpp
@@ -26,6 +26,9 @@ static_assert(!HasOperatorGreaterThanEqual<std::optional<NonComparable>>);
 static_assert(!HasOperatorGreaterThanEqual<std::optional<EqualityComparable>>);
 static_assert(!HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, std::optional<NonComparable>>);
 
+static_assert(!HasOperatorGreaterThanEqual<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorGreaterThanEqual<std::optional<int>, std::optional<void*>>);
+
 #endif
 
 using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.relops/greater_than.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/greater_than.pass.cpp
index f19ea23e8ae78..dd0e039d1d342 100644
--- a/libcxx/test/std/utilities/optional/optional.relops/greater_than.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.relops/greater_than.pass.cpp
@@ -26,6 +26,9 @@ static_assert(!HasOperatorGreaterThan<std::optional<NonComparable>>);
 static_assert(!HasOperatorGreaterThan<std::optional<EqualityComparable>>);
 static_assert(!HasOperatorGreaterThan<std::optional<ThreeWayComparable>, std::optional<NonComparable>>);
 
+static_assert(!HasOperatorGreaterThan<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorGreaterThan<std::optional<int>, std::optional<void*>>);
+
 #endif
 
 using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.relops/less_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/less_equal.pass.cpp
index bd81a79f1265e..9e472e158e3b5 100644
--- a/libcxx/test/std/utilities/optional/optional.relops/less_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.relops/less_equal.pass.cpp
@@ -26,6 +26,9 @@ static_assert(!HasOperatorLessThanEqual<std::optional<NonComparable>>);
 static_assert(!HasOperatorLessThanEqual<std::optional<EqualityComparable>>);
 static_assert(!HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, std::optional<NonComparable>>);
 
+static_assert(!HasOperatorLessThanEqual<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorLessThanEqual<std::optional<int>, std::optional<void*>>);
+
 #endif
 
 using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.relops/less_than.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/less_than.pass.cpp
index c87bfd6f73e54..9c231dff56f51 100644
--- a/libcxx/test/std/utilities/optional/optional.relops/less_than.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.relops/less_than.pass.cpp
@@ -26,6 +26,9 @@ static_assert(!HasOperatorLessThan<std::optional<NonComparable>>);
 static_assert(!HasOperatorLessThan<std::optional<EqualityComparable>>);
 static_assert(!HasOperatorLessThan<std::optional<ThreeWayComparable>, std::optional<NonComparable>>);
 
+static_assert(!HasOperatorLessThan<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorLessThan<std::optional<int>, std::optional<void*>>);
+
 #endif
 
 using std::optional;
diff --git a/libcxx/test/std/utilities/optional/optional.relops/not_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/not_equal.pass.cpp
index 1b7d2621c9193..6507a61426f87 100644
--- a/libcxx/test/std/utilities/optional/optional.relops/not_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.relops/not_equal.pass.cpp
@@ -29,6 +29,9 @@ static_assert(HasOperatorNotEqual<std::optional<EqualityComparable>, std::option
 static_assert(!HasOperatorNotEqual<std::optional<NonComparable>>);
 static_assert(!HasOperatorNotEqual<std::optional<EqualityComparable>, std::optional<NonComparable>>);
 
+static_assert(!HasOperatorNotEqual<std::optional<void*>, std::optional<int>>);
+static_assert(!HasOperatorNotEqual<std::optional<int>, std::optional<void*>>);
+
 #endif
 
 using std::optional;

``````````

</details>


https://github.com/llvm/llvm-project/pull/209968

From libcxx-commits at lists.llvm.org  Wed Jul 15 23:00:18 2026
From: libcxx-commits at lists.llvm.org (Dan Shan via libcxx-commits)
Date: Wed, 15 Jul 2026 23:00:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG4072: std::optional
 comparisons: constrain harder (PR #209968)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209968@github.com>
Message-ID: <6a587372.bc28b987.3d8071.a942@mx.google.com>


https://github.com/HumanThe2nd edited https://github.com/llvm/llvm-project/pull/209968

From libcxx-commits at lists.llvm.org  Thu Jul 16 00:07:59 2026
From: libcxx-commits at lists.llvm.org (Takuto Ikuta via libcxx-commits)
Date: Thu, 16 Jul 2026 00:07:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix hypotf linker error with
 Clang modules on Windows UCRT (PR #209403)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209403@github.com>
Message-ID: <6a58834f.4a342cbc.cf320.647a@mx.google.com>


atetubou wrote:

We filed https://github.com/llvm/llvm-project/issues/209989 to investigate this further to find which code paths caused behavior difference w/ and w/o modules. We'll revisit this when we found appropriate fix there.

https://github.com/llvm/llvm-project/pull/209403

From libcxx-commits at lists.llvm.org  Thu Jul 16 00:55:00 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Thu, 16 Jul 2026 00:55:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libcxx] Partially revert
	#208330 (PR #209928)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209928@github.com>
Message-ID: <6a588e54.492e23e4.304475.c268@mx.google.com>


https://github.com/petrhosek updated https://github.com/llvm/llvm-project/pull/209928

>From 95bddae719c2df2320c19637706ccc1bc689d0f2 Mon Sep 17 00:00:00 2001
From: Petr Hosek <phosek at google.com>
Date: Wed, 15 Jul 2026 13:29:14 -0700
Subject: [PATCH 1/2] [libcxx] Partially revert #208330

We found out that the new mechanism for detecting overriden functions
does not support Arm Pointer Authentication (PAuth). Addressing this
limitation is going to require changes to Clang. In the meantime, we
switched back to the old mechanism when PAuth is enabled.
---
 libcxx/src/include/overridable_function.h | 75 ++++++++++++++++++++---
 libcxx/src/new.cpp                        |  8 +--
 libcxxabi/src/stdlib_new_delete.cpp       |  8 +--
 3 files changed, 75 insertions(+), 16 deletions(-)

diff --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index 83439ad363208..e115b9483b727 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -21,12 +21,17 @@
 // whether an overridable function (typically a weak symbol) like `operator new`
 // has been overridden by a user or not.
 //
-// This is a low-level utility which does not work on all platforms, since it needs to
-// make assumptions about the object file format in use. This currently works with Mach-O
-// files (used on Darwin) and with ELF files (used on Linux and others). On platforms
-// where we know how to implement this detection, the macro
-// _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION  is defined to 1, and it is defined to 0 on
-// other platforms.
+// This is a low-level utility which does not work on all platforms, since it needs
+// to make assumptions about the object file format in use. Furthermore, it requires
+// the "base definition" of the function (the one we want to check whether it has been
+// overridden) to be defined using the OVERRIDABLE_FUNCTION macro.
+//
+// This currently works with Mach-O files (used on Darwin) and with ELF files (used on Linux
+// and others). On platforms where we know how to implement this detection, the macro
+// _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION is defined to 1, and it is defined to 0 on
+// other platforms. The OVERRIDABLE_FUNCTION macro is defined to perform a normal
+// function definition on unsupported platforms so that it can be used to define functions
+// regardless of whether detection is actually supported.
 //
 // How does this work?
 // -------------------
@@ -38,6 +43,14 @@
 // compares the address of `__impl_ref<f>::__impl_` with the address of `f` loaded from
 // GOT: if `f` was overridden by the user in another TU, the addresses will be different.
 //
+// When pointer authentication is used, the above mechanism doesn't work (yet) so we use
+// a different strategy placing `f`'s definition (in the libc++ built library) inside 
+// a special section, which we do using the `__section__` attribute via the
+// OVERRIDABLE_FUNCTION macro. Then, when comes the time to check whether the function has
+// been overridden, we take the address of the function and we check whether it falls inside
+// the special section we created. This can be done by finding pointers to the start and
+// the end of the section, and then checking whether `f` falls within those bounds.
+//
 // Important note
 // --------------
 //
@@ -50,6 +63,10 @@
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
 
+#  if !__has_feature(ptrauth_calls)
+
+#    define OVERRIDABLE_FUNCTION [[gnu::weak]]
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace {
@@ -95,10 +112,52 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
 
 _LIBCPP_END_NAMESPACE_STD
 
-#else
+#  else // __has_feature(ptrauth_calls)
+
+#    include <ptrauth.h>
+
+#    if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
+#      define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
+// Declare two dummy bytes and give them these special `__asm` values. These values are
+// defined by the linker, which means that referring to `&__lcxx_override_start` will
+// effectively refer to the address where the section starts (and same for the end).
+extern char __start___lcxx_override __asm("section$start$__TEXT$__lcxx_override");
+extern char __stop___lcxx_override __asm("section$end$__TEXT$__lcxx_override");
+#    elif defined(_LIBCPP_OBJECT_FORMAT_ELF)
+// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
+// variables with those names corresponding to the start and the end of the section.
+//
+// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
+#      define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+extern char __start___lcxx_override;
+extern char __stop___lcxx_override;
+#    endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+template <typename T, T* _Func>
+_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
+  uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
+  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
+  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
+
+  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
+  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
+  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
+  // stripped the function pointer. See rdar://122927845.
+  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
+
+  // Finally, the function was overridden if it falls outside of the section's bounds.
+  return __ptr < __start || __ptr > __end;
+}
+_LIBCPP_END_NAMESPACE_STD
+
+#  endif // __has_feature(ptrauth_calls)
+
+#else // defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 0
+#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
 
-#endif
+#endif // defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
 
 #endif // _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H
diff --git a/libcxx/src/new.cpp b/libcxx/src/new.cpp
index 0ccc3acad06aa..25bdaea32f57a 100644
--- a/libcxx/src/new.cpp
+++ b/libcxx/src/new.cpp
@@ -43,7 +43,7 @@ static void* operator_new_impl(std::size_t size) {
   return p;
 }
 
-[[gnu::weak]] void* operator new(std::size_t size) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new(std::size_t size) _THROW_BAD_ALLOC {
   void* p = operator_new_impl(size);
   if (p == nullptr)
     __throw_bad_alloc_shim();
@@ -74,7 +74,7 @@ static void* operator_new_impl(std::size_t size) {
 #  endif
 }
 
-[[gnu::weak]] void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
+OVERRIDABLE_FUNCTION void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
 
 [[gnu::weak]] void* operator new[](size_t size, const std::nothrow_t&) noexcept {
 #  if !_LIBCPP_HAS_EXCEPTIONS
@@ -134,7 +134,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
   return p;
 }
 
-[[gnu::weak]] void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
   void* p = operator_new_aligned_impl(size, alignment);
   if (p == nullptr)
     __throw_bad_alloc_shim();
@@ -165,7 +165,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
 #    endif
 }
 
-[[gnu::weak]] void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
   return ::operator new(size, alignment);
 }
 
diff --git a/libcxxabi/src/stdlib_new_delete.cpp b/libcxxabi/src/stdlib_new_delete.cpp
index 9f538948542c3..164a26335eb38 100644
--- a/libcxxabi/src/stdlib_new_delete.cpp
+++ b/libcxxabi/src/stdlib_new_delete.cpp
@@ -59,7 +59,7 @@ static void* operator_new_impl(std::size_t size) {
   return p;
 }
 
-[[gnu::weak]] void* operator new(std::size_t size) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new(std::size_t size) _THROW_BAD_ALLOC {
   void* p = operator_new_impl(size);
   if (p == nullptr)
     __throw_bad_alloc_shim();
@@ -90,7 +90,7 @@ static void* operator_new_impl(std::size_t size) {
 #endif
 }
 
-[[gnu::weak]] void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
+OVERRIDABLE_FUNCTION void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
 
 [[gnu::weak]] void* operator new[](size_t size, const std::nothrow_t&) noexcept {
 #if !_LIBCPP_HAS_EXCEPTIONS
@@ -150,7 +150,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
   return p;
 }
 
-[[gnu::weak]] void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
   void* p = operator_new_aligned_impl(size, alignment);
   if (p == nullptr)
     __throw_bad_alloc_shim();
@@ -181,7 +181,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
 #  endif
 }
 
-[[gnu::weak]] void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
   return ::operator new(size, alignment);
 }
 

>From b4008e3b2413286a8ee85119c1dfa8ac0e25f7f4 Mon Sep 17 00:00:00 2001
From: Petr Hosek <phosek at google.com>
Date: Thu, 16 Jul 2026 00:54:41 -0700
Subject: [PATCH 2/2] Formatting

---
 libcxx/src/include/overridable_function.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index e115b9483b727..e430ccda29f65 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -44,7 +44,7 @@
 // GOT: if `f` was overridden by the user in another TU, the addresses will be different.
 //
 // When pointer authentication is used, the above mechanism doesn't work (yet) so we use
-// a different strategy placing `f`'s definition (in the libc++ built library) inside 
+// a different strategy placing `f`'s definition (in the libc++ built library) inside
 // a special section, which we do using the `__section__` attribute via the
 // OVERRIDABLE_FUNCTION macro. Then, when comes the time to check whether the function has
 // been overridden, we take the address of the function and we check whether it falls inside


From libcxx-commits at lists.llvm.org  Thu Jul 16 03:15:14 2026
From: libcxx-commits at lists.llvm.org (Andrei Sabalenka via libcxx-commits)
Date: Thu, 16 Jul 2026 03:15:14 -0700 (PDT)
Subject: [libcxx-commits] [clang] [flang] [libcxx] [lldb] [llvm] [mlir]
 [NFC] Silence -Wbool-integral-comparison warnings across LLVM (PR #195246)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/195246@github.com>
Message-ID: <6a58af32.c56fb102.1bc57.71c6@mx.google.com>


https://github.com/mechakotik updated https://github.com/llvm/llvm-project/pull/195246

>From 1bb6eded870f0fc32c3ce0cc5bcd3f7c9420a9a6 Mon Sep 17 00:00:00 2001
From: Andrei Sabalenka <mechakotik at gmail.com>
Date: Fri, 1 May 2026 12:21:37 +0300
Subject: [PATCH] [NFC] Silence -Wbool-integral-comparison warnings across LLVM

---
 bolt/lib/Profile/BoltAddressTranslation.cpp        |  4 ++--
 clang/lib/CodeGen/CodeGenFunction.cpp              |  2 +-
 clang/lib/Serialization/ASTReaderStmt.cpp          |  4 ++--
 flang/unittests/Evaluate/uint128.cpp               | 14 +++++++-------
 .../utilities/const.wrap.class/unary_ops.pass.cpp  |  2 +-
 lldb/source/ValueObject/ValueObject.cpp            |  3 ++-
 llvm/include/llvm/CodeGen/DIE.h                    |  4 ++--
 llvm/include/llvm/Support/DataExtractor.h          |  2 +-
 llvm/lib/ExecutionEngine/JITLink/aarch32.cpp       |  2 +-
 llvm/lib/Support/DataExtractor.cpp                 |  4 ++--
 llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.cpp   |  3 ++-
 .../Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp    |  2 +-
 .../AMDGPU/MCTargetDesc/AMDGPUInstPrinter.cpp      |  2 +-
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp        |  3 ++-
 llvm/lib/Transforms/IPO/AttributorAttributes.cpp   |  2 +-
 mlir/lib/Rewrite/ByteCode.cpp                      |  2 +-
 16 files changed, 29 insertions(+), 26 deletions(-)

diff --git a/bolt/lib/Profile/BoltAddressTranslation.cpp b/bolt/lib/Profile/BoltAddressTranslation.cpp
index 2068c9efbcb44..cc404e1aef70f 100644
--- a/bolt/lib/Profile/BoltAddressTranslation.cpp
+++ b/bolt/lib/Profile/BoltAddressTranslation.cpp
@@ -185,7 +185,7 @@ template <bool Cold>
 void BoltAddressTranslation::writeMaps(uint64_t &PrevAddress, raw_ostream &OS) {
   const uint32_t NumFuncs =
       llvm::count_if(llvm::make_first_range(Maps), [&](const uint64_t Address) {
-        return Cold == ColdPartSource.count(Address);
+        return Cold == (ColdPartSource.count(Address) != 0);
       });
   encodeULEB128(NumFuncs, OS);
   LLVM_DEBUG(dbgs() << "Writing " << NumFuncs << (Cold ? " cold" : "")
@@ -194,7 +194,7 @@ void BoltAddressTranslation::writeMaps(uint64_t &PrevAddress, raw_ostream &OS) {
   for (auto &MapEntry : Maps) {
     const uint64_t Address = MapEntry.first;
     // Only process cold fragments in cold mode, and vice versa.
-    if (Cold != ColdPartSource.count(Address))
+    if (Cold != (ColdPartSource.count(Address) != 0))
       continue;
     // NB: in `writeMaps` we use the input address because hashes are saved
     // early in `saveMetadata` before output addresses are assigned.
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index fc4c7ea40f03d..8d8476956b65d 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -176,7 +176,7 @@ void CodeGenFunction::CGFPOptionsRAII::ConstructorHelper(FPOptions FPFeatures) {
   auto mergeFnAttrValue = [&](StringRef Name, bool Value) {
     auto OldValue =
         CGF.CurFn->getFnAttribute(Name).getValueAsBool();
-    auto NewValue = OldValue & Value;
+    bool NewValue = OldValue && Value;
     if (OldValue != NewValue)
       CGF.CurFn->addFnAttr(Name, llvm::toStringRef(NewValue));
   };
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 87cec16a76323..4ac60cd28b9ae 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -154,7 +154,7 @@ void ASTStmtReader::VisitCompoundStmt(CompoundStmt *S) {
   VisitStmt(S);
   SmallVector<Stmt *, 16> Stmts;
   unsigned NumStmts = Record.readInt();
-  unsigned HasFPFeatures = Record.readInt();
+  bool HasFPFeatures = Record.readBool();
   assert(S->hasStoredFPFeatures() == HasFPFeatures);
   while (NumStmts--)
     Stmts.push_back(Record.readSubStmt());
@@ -1159,7 +1159,7 @@ void ASTStmtReader::VisitCastExpr(CastExpr *E) {
 
   CurrentUnpackingBits.emplace(Record.readInt());
   E->setCastKind((CastKind)CurrentUnpackingBits->getNextBits(/*Width=*/7));
-  unsigned HasFPFeatures = CurrentUnpackingBits->getNextBit();
+  bool HasFPFeatures = CurrentUnpackingBits->getNextBit();
   assert(E->hasStoredFPFeatures() == HasFPFeatures);
 
   E->setSubExpr(Record.readSubExpr());
diff --git a/flang/unittests/Evaluate/uint128.cpp b/flang/unittests/Evaluate/uint128.cpp
index 0b749abe1c080..3d0e1b7e6e84c 100644
--- a/flang/unittests/Evaluate/uint128.cpp
+++ b/flang/unittests/Evaluate/uint128.cpp
@@ -66,13 +66,13 @@ static void TestVsNative(__uint128_t x, __uint128_t y) {
   TEST(ToNative(n) == y);
   TEST(ToNative(~m) == ~x);
   TEST(ToNative(-m) == -x);
-  TEST(ToNative(!m) == !x);
-  TEST(ToNative(m < n) == (x < y));
-  TEST(ToNative(m <= n) == (x <= y));
-  TEST(ToNative(m == n) == (x == y));
-  TEST(ToNative(m != n) == (x != y));
-  TEST(ToNative(m >= n) == (x >= y));
-  TEST(ToNative(m > n) == (x > y));
+  TEST(ToNative(!m) == static_cast<__uint128_t>(!x));
+  TEST(ToNative(m < n) == static_cast<__uint128_t>(x < y));
+  TEST(ToNative(m <= n) == static_cast<__uint128_t>(x <= y));
+  TEST(ToNative(m == n) == static_cast<__uint128_t>(x == y));
+  TEST(ToNative(m != n) == static_cast<__uint128_t>(x != y));
+  TEST(ToNative(m >= n) == static_cast<__uint128_t>(x >= y));
+  TEST(ToNative(m > n) == static_cast<__uint128_t>(x > y));
   TEST(ToNative(m & n) == (x & y));
   TEST(ToNative(m | n) == (x | y));
   TEST(ToNative(m ^ n) == (x ^ y));
diff --git a/libcxx/test/std/utilities/const.wrap.class/unary_ops.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/unary_ops.pass.cpp
index 8cd27b75f64c2..4ab30566a07d8 100644
--- a/libcxx/test/std/utilities/const.wrap.class/unary_ops.pass.cpp
+++ b/libcxx/test/std/utilities/const.wrap.class/unary_ops.pass.cpp
@@ -231,7 +231,7 @@ constexpr bool test() {
     assert(result3.get() == ~42);
 
     std::same_as<NonStructural> decltype(auto) result4 = !cwOpsReturnNonStructural;
-    assert(result4.get() == !42);
+    assert(result4.get() == static_cast<int>(!42));
 
     std::same_as<NonStructural> decltype(auto) result5 = &cwOpsReturnNonStructural;
     assert(result5.get() == 84);
diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp
index cac4933c64325..78bc3ea131c63 100644
--- a/lldb/source/ValueObject/ValueObject.cpp
+++ b/lldb/source/ValueObject/ValueObject.cpp
@@ -358,8 +358,9 @@ bool ValueObject::IsLogicalTrue(Status &error) {
     LazyBool is_logical_true = language->IsLogicalTrue(*this, error);
     switch (is_logical_true) {
     case eLazyBoolYes:
+      return true;
     case eLazyBoolNo:
-      return (is_logical_true == true);
+      return false;
     case eLazyBoolCalculate:
       break;
     }
diff --git a/llvm/include/llvm/CodeGen/DIE.h b/llvm/include/llvm/CodeGen/DIE.h
index c01ea4cfedbae..76bfb52c1ee9d 100644
--- a/llvm/include/llvm/CodeGen/DIE.h
+++ b/llvm/include/llvm/CodeGen/DIE.h
@@ -547,7 +547,7 @@ struct IntrusiveBackListBase {
 
   void push_back(Node &N) {
     assert(N.Next.getPointer() == &N && "Expected unlinked node");
-    assert(N.Next.getInt() == true && "Expected unlinked node");
+    assert(N.Next.getInt() && "Expected unlinked node");
 
     if (Last) {
       N.Next = Last->Next;
@@ -558,7 +558,7 @@ struct IntrusiveBackListBase {
 
   void push_front(Node &N) {
     assert(N.Next.getPointer() == &N && "Expected unlinked node");
-    assert(N.Next.getInt() == true && "Expected unlinked node");
+    assert(N.Next.getInt() && "Expected unlinked node");
 
     if (Last) {
       N.Next.setPointerAndInt(Last->Next.getPointer(), false);
diff --git a/llvm/include/llvm/Support/DataExtractor.h b/llvm/include/llvm/Support/DataExtractor.h
index ccabd70ed042a..426ada2f64bbd 100644
--- a/llvm/include/llvm/Support/DataExtractor.h
+++ b/llvm/include/llvm/Support/DataExtractor.h
@@ -91,7 +91,7 @@ class DataExtractor {
   /// Get the data pointed to by this extractor.
   StringRef getData() const { return Data; }
   /// Get the endianness for this extractor.
-  bool isLittleEndian() const { return IsLittleEndian; }
+  bool isLittleEndian() const { return IsLittleEndian != 0; }
 
   /// Extract a C string from \a *offset_ptr.
   ///
diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
index cc606b8278d26..f8342896254f7 100644
--- a/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/aarch32.cpp
@@ -899,7 +899,7 @@ bool StubsManager_v7::visitEdge(LinkGraph &G, Block *B, Edge &E) {
     });
   }
 
-  assert(MakeThumb == (StubSymbol->getTargetFlags() & ThumbSymbol) &&
+  assert(MakeThumb == hasTargetFlags(*StubSymbol, ThumbSymbol) &&
          "Instruction set states of stub and relocation site should be equal");
   LLVM_DEBUG({
     dbgs() << "    Using " << (MakeThumb ? "Thumb" : "Arm") << " entry "
diff --git a/llvm/lib/Support/DataExtractor.cpp b/llvm/lib/Support/DataExtractor.cpp
index 6b58b492002fd..8385888c3cb9c 100644
--- a/llvm/lib/Support/DataExtractor.cpp
+++ b/llvm/lib/Support/DataExtractor.cpp
@@ -48,7 +48,7 @@ T DataExtractor::getU(uint64_t *offset_ptr, Error *Err) const {
   if (!prepareRead(offset, sizeof(T), Err))
     return val;
   std::memcpy(&val, &Data.data()[offset], sizeof(val));
-  if (sys::IsLittleEndianHost != IsLittleEndian)
+  if (sys::IsLittleEndianHost != isLittleEndian())
     sys::swapByteOrder(val);
 
   // Advance the offset
@@ -150,7 +150,7 @@ uint64_t DataExtractor::getUnsigned(uint64_t *offset_ptr, uint32_t byte_size,
                   (sys::IsLittleEndianHost ? 0 : 8 - byte_size),
               &Data.data()[offset], byte_size);
   // Swap the least significant bytes of val if endianness doesn't match.
-  if (sys::IsLittleEndianHost != IsLittleEndian)
+  if (sys::IsLittleEndianHost != isLittleEndian())
     val = sys::getSwappedBytes(val) >> (8 * (8 - byte_size));
 
   *offset_ptr += byte_size;
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.cpp b/llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.cpp
index cad1513f280fb..60ed0d2adc456 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUNextUseAnalysis.cpp
@@ -567,7 +567,8 @@ class llvm::AMDGPUNextUseAnalysisImpl {
       const auto &ToInit = R ? ReachablePaths : UnreachablePaths;
       for (const Path &P : ToInit) {
         PathInfo &Slot = getOrInitPathInfo(P.src(), P.dst());
-        assert(Slot.isForwardReachableUnset() || Slot.ForwardReachable == R);
+        assert(Slot.isForwardReachableUnset() ||
+               Slot.ForwardReachable == static_cast<int>(R));
         Slot.ForwardReachable = R;
       }
     }
diff --git a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
index f019c280997d6..15f06049994aa 100644
--- a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
+++ b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
@@ -6393,7 +6393,7 @@ bool AMDGPUAsmParser::ParseDirectiveAMDHSAKernel() {
         return Error(IDRange.Start, "directive requires gfx8+", IDRange);
       if (!isUInt<1>(Val))
         return OutOfRangeError(ValRange);
-      if (Val != getTargetStreamer().getTargetID()->isXnackOnOrAny())
+      if ((Val != 0) != getTargetStreamer().getTargetID()->isXnackOnOrAny())
         return getParser().Error(IDRange.Start, ".amdhsa_reserve_xnack_mask does not match target id",
                                  IDRange);
     } else if (ID == ".amdhsa_float_round_mode_32") {
diff --git a/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUInstPrinter.cpp b/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUInstPrinter.cpp
index 2764ff2d68ce0..092bb0e27131b 100644
--- a/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUInstPrinter.cpp
+++ b/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUInstPrinter.cpp
@@ -1311,7 +1311,7 @@ void AMDGPUInstPrinter::printExpTgt(const MCInst *MI, unsigned OpNo,
 
 static bool allOpsDefaultValue(const int* Ops, int NumOps, int Mod,
                                bool IsPacked, bool HasDstSel) {
-  int DefaultValue = IsPacked && (Mod == SISrcMods::OP_SEL_1);
+  bool DefaultValue = IsPacked && (Mod == SISrcMods::OP_SEL_1);
 
   for (int I = 0; I < NumOps; ++I) {
     if (!!(Ops[I] & Mod) != DefaultValue)
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 2df58fc2ca9f0..614eff71c67da 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -5458,7 +5458,8 @@ static bool isAlternating(const std::array<std::pair<int, int>, 2> &SrcInfo,
     bool C = Src == SrcInfo[1].first && Diff == SrcInfo[1].second;
     assert(C != (Src == SrcInfo[0].first && Diff == SrcInfo[0].second) &&
            "Must match exactly one of the two slides");
-    if (RequiredPolarity != (C == (Idx / Factor) % 2))
+    bool OddGroup = ((Idx / Factor) % 2) != 0;
+    if (RequiredPolarity != (C == OddGroup))
       return false;
   }
   return true;
diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 66a05a077b208..02ea34a60f269 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -13326,7 +13326,7 @@ struct AANoAliasAddrSpaceImpl : public AANoAliasAddrSpace {
 
   ChangeStatus updateImpl(Attributor &A) override {
     unsigned FlatAS = A.getInfoCache().getFlatAddressSpace().value();
-    uint32_t OldAssumed = getAssumed();
+    bool OldAssumed = getAssumed();
 
     auto CheckAddressSpace = [&](Value &Obj) {
       if (isa<PoisonValue>(&Obj))
diff --git a/mlir/lib/Rewrite/ByteCode.cpp b/mlir/lib/Rewrite/ByteCode.cpp
index 2daf2635d96d5..e6f8950cb11b1 100644
--- a/mlir/lib/Rewrite/ByteCode.cpp
+++ b/mlir/lib/Rewrite/ByteCode.cpp
@@ -1421,7 +1421,7 @@ void ByteCodeExecutor::executeApplyConstraint(PatternRewriter &rewriter) {
 
   LDBG() << "  * Arguments: " << llvm::interleaved(args);
 
-  ByteCodeField isNegated = read();
+  bool isNegated = (read() != 0);
   LDBG() << "  * isNegated: " << isNegated;
 
   ByteCodeField numResults = read();


From libcxx-commits at lists.llvm.org  Thu Jul 16 03:40:47 2026
From: libcxx-commits at lists.llvm.org (Xing Xue via libcxx-commits)
Date: Thu, 16 Jul 2026 03:40:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [libunwind][AIX] Handle
 VAPI-based return addresses in stack unwinding for LLU (PR #209280)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209280@github.com>
Message-ID: <6a58b52f.e624eb8d.3065e1.a8d9@mx.google.com>


https://github.com/xingxue-ibm updated https://github.com/llvm/llvm-project/pull/209280

>From 6b69974ca040d85a14a898c242f60c425b190c49 Mon Sep 17 00:00:00 2001
From: Xing Xue <xingxue at outlook.com>
Date: Mon, 13 Jul 2026 15:04:04 -0400
Subject: [PATCH 1/5] Update the unwinder to accommodate AIX changes required
 for LLU (Live Library Update).

---
 libcxxabi/test/aix_unwind_vapi.pass.cpp | 66 +++++++++++++++++
 libunwind/src/UnwindCursor.hpp          | 98 ++++++++++++++++++++++++-
 2 files changed, 160 insertions(+), 4 deletions(-)
 create mode 100644 libcxxabi/test/aix_unwind_vapi.pass.cpp

diff --git a/libcxxabi/test/aix_unwind_vapi.pass.cpp b/libcxxabi/test/aix_unwind_vapi.pass.cpp
new file mode 100644
index 0000000000000..bafb02f366ab4
--- /dev/null
+++ b/libcxxabi/test/aix_unwind_vapi.pass.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Test unwinding through a frame whose return address points to the VAPI
+// glue for the Live Library Update feature.
+
+// REQUIRES: target={{.+}}-aix{{.*}}
+
+#include <stdlib.h>
+#include <stdio.h>
+
+// VAPI glue addresses
+constexpr uintptr_t vapi_glue_addr_ext_32 = 0x8B80;
+constexpr uintptr_t vapi_addr_64 = 0x8e00;
+constexpr uintptr_t vapi_size_64 = 0x0200;
+constexpr uintptr_t vapi_glue_addr_begin = vapi_glue_addr_ext_32;     // Start address in 32-bit
+constexpr uintptr_t vapi_glue_addr_end = vapi_addr_64 + vapi_size_64; // End address in 64-bit
+
+struct FunctionDescriptor {
+  uintptr_t entry;
+  uintptr_t toc;
+  uintptr_t env;
+};
+
+struct Tracker {
+  const char* name;
+  Tracker(const char* n) : name(n) { printf("Construct %s\n", name); }
+  ~Tracker() { printf("Destruct %s\n", name); }
+};
+
+int comp(const void*, const void*) {
+  printf("comp()\n");
+  throw 42;
+}
+
+static void do_search() {
+  Tracker t("do_search-local");
+  char buf[1];
+  char key = 0;
+  printf("calling bsearch()\n");
+  // bsearch uses VAPI and sets the return address to the VAPI glue when LLU
+  // is on.
+  bsearch(&key, buf, 1, 1, comp);
+  printf("after bsearch\n");
+}
+
+int main() {
+  FunctionDescriptor* fd = reinterpret_cast<FunctionDescriptor*>(bsearch);
+
+  if (vapi_glue_addr_begin <= fd->entry && fd->entry < vapi_glue_addr_end) {
+    printf("!!! Live Library Update is enabled !!!\n");
+  } else {
+    printf("!!! Live Library Update is NOT enabled !!!\n");
+  }
+
+  try {
+    do_search();
+  } catch (...) {
+    printf("caught ...\n");
+  }
+}
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index ff92c9e0a844a..f59384af81b5e 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -1363,6 +1363,10 @@ class UnwindCursor : public AbstractUnwindCursor {
                            reinterpret_cast<tbtable *>(_info.unwind_info),
                            _registers, _isSignalFrame);
   }
+  bool isKnownVapiNotActive() const { return _isKnownVapiNotActive; }
+  void setIsKnownVapiNotActive(bool val) { _isKnownVapiNotActive = val; }
+  static pint_t getVAPILR();
+
 #endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
 
   A               &_addressSpace;
@@ -1377,13 +1381,15 @@ class UnwindCursor : public AbstractUnwindCursor {
 #ifdef _LIBUNWIND_TRACE_RET_INJECT
   uint32_t _walkedFrames;
 #endif
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+  bool _isKnownVapiNotActive;
+#endif
 };
 
-
 template <typename A, typename R>
 UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
     : _addressSpace(as), _registers(context), _unwindInfoMissing(false),
-      _isSignalFrame(false) {
+      _isSignalFrame(false), _isKnownVapiNotActive(false) {
   static_assert((check_fit<UnwindCursor<A, R>, unw_cursor_t>::does_fit),
                 "UnwindCursor<> does not fit in unw_cursor_t");
   static_assert((alignof(UnwindCursor<A, R>) <= alignof(unw_cursor_t)),
@@ -1393,13 +1399,13 @@ UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
 
 template <typename A, typename R>
 UnwindCursor<A, R>::UnwindCursor(A &as, void *)
-    : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) {
+    : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false),
+      _isKnownVapiNotActive(false) {
   memset(static_cast<void *>(&_info), 0, sizeof(_info));
   // FIXME
   // fill in _registers from thread arg
 }
 
-
 template <typename A, typename R>
 bool UnwindCursor<A, R>::validReg(int regNum) {
   return _registers.validRegister(regNum);
@@ -1469,6 +1475,30 @@ template <typename A, typename R> void UnwindCursor<A, R>::jumpto() {
   static constexpr size_t _EXTRA_LIBUNWIND_FRAMES_WALKED = 5 - 1;
   _registers.returnto(_walkedFrames + _EXTRA_LIBUNWIND_FRAMES_WALKED);
 #else
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+  if (isKnownVapiNotActive()) {
+    // If the current frame is known VAPI not active , execute the VAPI return
+    // glue to clear the VAPI control block. The VAPI return glue is used by
+    // AIX longjmp based on the VAPI active status recorded by setjmp in the
+    // jmp_buf, which means that the VAPI return glue can be called solely on
+    // the basis of the VAPI active status of the target context.
+
+    // VAPI return glue address is the VAPI glue address - 4.
+#ifdef __64BIT__
+    constexpr pint_t VAPIReturnGlue = 0x8e40 - 4;
+#else
+    constexpr pint_t VAPIReturnGlue = 0x8c40 - 4;
+#endif
+
+    _LIBUNWIND_TRACE_UNWINDING("VAPI: executing return glue %p\n",
+                               reinterpret_cast<void *>(VAPIReturnGlue));
+    register auto *registers __asm__("r30") = &_registers;
+    __asm__ __volatile__("bla %[retglue]"
+                         : "+r"(registers)
+                         : [retglue] "i"(VAPIReturnGlue));
+    registers->jumpto();
+  }
+#endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
   _registers.jumpto();
 #endif
 }
@@ -2459,6 +2489,45 @@ bool UnwindCursor<A, R>::getInfoFromTBTable(pint_t pc, R &registers) {
   return true;
 }
 
+// VAPI glue addresses
+constexpr uintptr_t vapi_glue_addr_ext_32 = 0x8B80;
+constexpr uintptr_t vapi_addr_64 = 0x8e00;
+constexpr uintptr_t vapi_size_64 = 0x0200;
+constexpr uintptr_t vapi_glue_addr_begin =
+    vapi_glue_addr_ext_32; // Start address in 32-bit
+constexpr uintptr_t vapi_glue_addr_end =
+    vapi_addr_64 + vapi_size_64; // End address in 64-bit
+
+#ifdef __64BIT__
+constexpr uintptr_t VAPI_CB_SIZE = 256;
+constexpr uintptr_t TLS_POINTER_OFFSET = 30 * 1024;
+constexpr uintptr_t TLSCB_BASE_SIZE = 256;
+
+static __inline__ __attribute__((__always_inline__)) char *tptr(void) {
+  char *result;
+  __asm__("mr %0, 13" : "=r"(result));
+  return result;
+}
+#else // 32-bit
+constexpr uintptr_t VAPI_CB_SIZE = 128;
+constexpr uintptr_t TLS_POINTER_OFFSET = 31 * 1024;
+constexpr uintptr_t TLSCB_BASE_SIZE = 128;
+
+static __inline__ __attribute__((__always_inline__)) char *tptr(void) {
+  char *result;
+  __asm__("mfspr %0, 259" : "=r"(result));
+  return result;
+}
+#endif
+
+constexpr int VAPI_CB_OFFSET =
+    TLS_POINTER_OFFSET + VAPI_CB_SIZE + TLSCB_BASE_SIZE;
+
+template <typename A, typename R>
+typename UnwindCursor<A, R>::pint_t UnwindCursor<A, R>::getVAPILR() {
+  return *reinterpret_cast<pint_t *>(tptr() - VAPI_CB_OFFSET + 8);
+}
+
 // Step back up the stack following the frame back link.
 template <typename A, typename R>
 int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
@@ -2579,6 +2648,17 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
     } else {
       // Otherwise, use the LR value in the stack link area.
       returnAddress = reinterpret_cast<pint_t *>(lastStack)[2];
+
+      if (vapi_glue_addr_begin <= returnAddress &&
+          returnAddress < vapi_glue_addr_end) {
+        _LIBUNWIND_TRACE_UNWINDING(
+            "The return address=%p is within the range of VAPI address, ",
+            reinterpret_cast<void *>(returnAddress));
+        setIsKnownVapiNotActive(true);
+        returnAddress = getVAPILR();
+        _LIBUNWIND_TRACE_UNWINDING("return address=%p from VAPI\n",
+                                   reinterpret_cast<void *>(returnAddress));
+      }
     }
 
     // Reset LR in the current context.
@@ -2709,6 +2789,16 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
   // Return address is the address after call site instruction.
   pint_t nextReturnAddress = reinterpret_cast<pint_t *>(nextStack)[2];
 
+  if (vapi_glue_addr_begin <= nextReturnAddress &&
+      nextReturnAddress < vapi_glue_addr_end) {
+    _LIBUNWIND_TRACE_UNWINDING(
+        "The next return address=%p is within the range of VAPI address, ",
+        reinterpret_cast<void *>(nextReturnAddress));
+    nextReturnAddress = getVAPILR();
+    _LIBUNWIND_TRACE_UNWINDING("the next return address=%p from VAPI\n",
+                               reinterpret_cast<void *>(nextReturnAddress));
+  }
+
   if (nextReturnAddress > 0x01 && nextReturnAddress < 0x10000) {
     _LIBUNWIND_TRACE_UNWINDING("The next is a signal handler frame: "
                                "nextStack=%p, next return address=%p\n",

>From 578ab2193990e1954f302654d3f17fa40e639f0e Mon Sep 17 00:00:00 2001
From: Xing Xue <xingxue at outlook.com>
Date: Mon, 13 Jul 2026 15:47:23 -0400
Subject: [PATCH 2/5] Guard _isKnownVapiNotActive(false) initializers with #if
 defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND).

---
 libunwind/src/UnwindCursor.hpp | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index f59384af81b5e..7b400964defc8 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -1389,7 +1389,11 @@ class UnwindCursor : public AbstractUnwindCursor {
 template <typename A, typename R>
 UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
     : _addressSpace(as), _registers(context), _unwindInfoMissing(false),
-      _isSignalFrame(false), _isKnownVapiNotActive(false) {
+      _isSignalFrame(false)
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+      , _isKnownVapiNotActive(false)
+#endif
+      {
   static_assert((check_fit<UnwindCursor<A, R>, unw_cursor_t>::does_fit),
                 "UnwindCursor<> does not fit in unw_cursor_t");
   static_assert((alignof(UnwindCursor<A, R>) <= alignof(unw_cursor_t)),
@@ -1399,8 +1403,11 @@ UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
 
 template <typename A, typename R>
 UnwindCursor<A, R>::UnwindCursor(A &as, void *)
-    : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false),
-      _isKnownVapiNotActive(false) {
+    : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false)
+#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+      , _isKnownVapiNotActive(false)
+#endif
+      {
   memset(static_cast<void *>(&_info), 0, sizeof(_info));
   // FIXME
   // fill in _registers from thread arg

>From 6b571a1a2099194a52e7db94c4fcd9adf5b39bde Mon Sep 17 00:00:00 2001
From: Xing Xue <xingxue at outlook.com>
Date: Mon, 13 Jul 2026 18:18:53 -0400
Subject: [PATCH 3/5] Address comments and fix formatting. - correct variable
 types - add a comment for TODO

---
 libcxxabi/test/aix_unwind_vapi.pass.cpp | 66 -------------------------
 libunwind/src/UnwindCursor.hpp          | 37 ++++++++------
 2 files changed, 21 insertions(+), 82 deletions(-)
 delete mode 100644 libcxxabi/test/aix_unwind_vapi.pass.cpp

diff --git a/libcxxabi/test/aix_unwind_vapi.pass.cpp b/libcxxabi/test/aix_unwind_vapi.pass.cpp
deleted file mode 100644
index bafb02f366ab4..0000000000000
--- a/libcxxabi/test/aix_unwind_vapi.pass.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// Test unwinding through a frame whose return address points to the VAPI
-// glue for the Live Library Update feature.
-
-// REQUIRES: target={{.+}}-aix{{.*}}
-
-#include <stdlib.h>
-#include <stdio.h>
-
-// VAPI glue addresses
-constexpr uintptr_t vapi_glue_addr_ext_32 = 0x8B80;
-constexpr uintptr_t vapi_addr_64 = 0x8e00;
-constexpr uintptr_t vapi_size_64 = 0x0200;
-constexpr uintptr_t vapi_glue_addr_begin = vapi_glue_addr_ext_32;     // Start address in 32-bit
-constexpr uintptr_t vapi_glue_addr_end = vapi_addr_64 + vapi_size_64; // End address in 64-bit
-
-struct FunctionDescriptor {
-  uintptr_t entry;
-  uintptr_t toc;
-  uintptr_t env;
-};
-
-struct Tracker {
-  const char* name;
-  Tracker(const char* n) : name(n) { printf("Construct %s\n", name); }
-  ~Tracker() { printf("Destruct %s\n", name); }
-};
-
-int comp(const void*, const void*) {
-  printf("comp()\n");
-  throw 42;
-}
-
-static void do_search() {
-  Tracker t("do_search-local");
-  char buf[1];
-  char key = 0;
-  printf("calling bsearch()\n");
-  // bsearch uses VAPI and sets the return address to the VAPI glue when LLU
-  // is on.
-  bsearch(&key, buf, 1, 1, comp);
-  printf("after bsearch\n");
-}
-
-int main() {
-  FunctionDescriptor* fd = reinterpret_cast<FunctionDescriptor*>(bsearch);
-
-  if (vapi_glue_addr_begin <= fd->entry && fd->entry < vapi_glue_addr_end) {
-    printf("!!! Live Library Update is enabled !!!\n");
-  } else {
-    printf("!!! Live Library Update is NOT enabled !!!\n");
-  }
-
-  try {
-    do_search();
-  } catch (...) {
-    printf("caught ...\n");
-  }
-}
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 7b400964defc8..1f147dc4e920e 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -1382,6 +1382,9 @@ class UnwindCursor : public AbstractUnwindCursor {
   uint32_t _walkedFrames;
 #endif
 #if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
+  // TODO: this will need to be recorded in the unw_context_t by unw_getcontext
+  // to support cases where the cursor is retrieved prior to invocation of the
+  // Virtual API.
   bool _isKnownVapiNotActive;
 #endif
 };
@@ -1391,9 +1394,10 @@ UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
     : _addressSpace(as), _registers(context), _unwindInfoMissing(false),
       _isSignalFrame(false)
 #if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
-      , _isKnownVapiNotActive(false)
+      ,
+      _isKnownVapiNotActive(false)
 #endif
-      {
+{
   static_assert((check_fit<UnwindCursor<A, R>, unw_cursor_t>::does_fit),
                 "UnwindCursor<> does not fit in unw_cursor_t");
   static_assert((alignof(UnwindCursor<A, R>) <= alignof(unw_cursor_t)),
@@ -1405,9 +1409,10 @@ template <typename A, typename R>
 UnwindCursor<A, R>::UnwindCursor(A &as, void *)
     : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false)
 #if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
-      , _isKnownVapiNotActive(false)
+      ,
+      _isKnownVapiNotActive(false)
 #endif
-      {
+{
   memset(static_cast<void *>(&_info), 0, sizeof(_info));
   // FIXME
   // fill in _registers from thread arg
@@ -1484,7 +1489,7 @@ template <typename A, typename R> void UnwindCursor<A, R>::jumpto() {
 #else
 #if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
   if (isKnownVapiNotActive()) {
-    // If the current frame is known VAPI not active , execute the VAPI return
+    // If the current frame is known VAPI not active, execute the VAPI return
     // glue to clear the VAPI control block. The VAPI return glue is used by
     // AIX longjmp based on the VAPI active status recorded by setjmp in the
     // jmp_buf, which means that the VAPI return glue can be called solely on
@@ -2497,18 +2502,18 @@ bool UnwindCursor<A, R>::getInfoFromTBTable(pint_t pc, R &registers) {
 }
 
 // VAPI glue addresses
-constexpr uintptr_t vapi_glue_addr_ext_32 = 0x8B80;
+constexpr uintptr_t vapi_glue_addr_ext_32 = 0x8b80;
 constexpr uintptr_t vapi_addr_64 = 0x8e00;
-constexpr uintptr_t vapi_size_64 = 0x0200;
+constexpr size_t vapi_size_64 = 0x0200;
 constexpr uintptr_t vapi_glue_addr_begin =
     vapi_glue_addr_ext_32; // Start address in 32-bit
 constexpr uintptr_t vapi_glue_addr_end =
     vapi_addr_64 + vapi_size_64; // End address in 64-bit
 
 #ifdef __64BIT__
-constexpr uintptr_t VAPI_CB_SIZE = 256;
-constexpr uintptr_t TLS_POINTER_OFFSET = 30 * 1024;
-constexpr uintptr_t TLSCB_BASE_SIZE = 256;
+constexpr size_t VAPI_CB_SIZE = 256;
+constexpr ptrdiff_t TLS_POINTER_OFFSET = 30 * 1024;
+constexpr size_t TLSCB_BASE_SIZE = 256;
 
 static __inline__ __attribute__((__always_inline__)) char *tptr(void) {
   char *result;
@@ -2516,9 +2521,9 @@ static __inline__ __attribute__((__always_inline__)) char *tptr(void) {
   return result;
 }
 #else // 32-bit
-constexpr uintptr_t VAPI_CB_SIZE = 128;
-constexpr uintptr_t TLS_POINTER_OFFSET = 31 * 1024;
-constexpr uintptr_t TLSCB_BASE_SIZE = 128;
+constexpr size_t VAPI_CB_SIZE = 128;
+constexpr ptrdiff_t TLS_POINTER_OFFSET = 31 * 1024;
+constexpr size_t TLSCB_BASE_SIZE = 128;
 
 static __inline__ __attribute__((__always_inline__)) char *tptr(void) {
   char *result;
@@ -2527,7 +2532,7 @@ static __inline__ __attribute__((__always_inline__)) char *tptr(void) {
 }
 #endif
 
-constexpr int VAPI_CB_OFFSET =
+constexpr ptrdiff_t VAPI_CB_OFFSET =
     TLS_POINTER_OFFSET + VAPI_CB_SIZE + TLSCB_BASE_SIZE;
 
 template <typename A, typename R>
@@ -2659,7 +2664,7 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
       if (vapi_glue_addr_begin <= returnAddress &&
           returnAddress < vapi_glue_addr_end) {
         _LIBUNWIND_TRACE_UNWINDING(
-            "The return address=%p is within the range of VAPI address, ",
+            "The return address=%p is within the range of VAPI address;",
             reinterpret_cast<void *>(returnAddress));
         setIsKnownVapiNotActive(true);
         returnAddress = getVAPILR();
@@ -2799,7 +2804,7 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
   if (vapi_glue_addr_begin <= nextReturnAddress &&
       nextReturnAddress < vapi_glue_addr_end) {
     _LIBUNWIND_TRACE_UNWINDING(
-        "The next return address=%p is within the range of VAPI address, ",
+        "The next return address=%p is within the range of VAPI address;",
         reinterpret_cast<void *>(nextReturnAddress));
     nextReturnAddress = getVAPILR();
     _LIBUNWIND_TRACE_UNWINDING("the next return address=%p from VAPI\n",

>From cceca093ab4e38ce33b6fe7457158c52b8688ee4 Mon Sep 17 00:00:00 2001
From: Xing Xue <xingxue at outlook.com>
Date: Wed, 15 Jul 2026 10:50:09 -0400
Subject: [PATCH 4/5] If the return address in a signal handling trampoline
 frame is a VAPI glue return address, set isKnownVapiNotActive flag to true.

---
 libunwind/src/UnwindCursor.hpp | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 1f147dc4e920e..03ea96b2e4451 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -2592,6 +2592,16 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
     _LIBUNWIND_TRACE_UNWINDING("Possible signal handler frame: lastStack=%p",
                                reinterpret_cast<void *>(lastStack));
 
+    pint_t returnAddressInStack = reinterpret_cast<pint_t *>(lastStack)[2];
+    if (vapi_glue_addr_begin <= returnAddressInStack &&
+        returnAddressInStack < vapi_glue_addr_end) {
+      _LIBUNWIND_TRACE_UNWINDING(
+          "The return address in stack %p is within the range of VAPI address,"
+          " set isKnownVapiNotActive to true\n",
+          reinterpret_cast<void *>(returnAddressInStack));
+      setIsKnownVapiNotActive(true);
+    }
+
     sigcontext *sigContext = reinterpret_cast<sigcontext *>(
         reinterpret_cast<char *>(lastStack) + STKMINALIGN);
     returnAddress = sigContext->sc_jmpbuf.jmp_context.iar;

>From f2a6772a578c51da6ea39fc2bf7483bf331ae3d4 Mon Sep 17 00:00:00 2001
From: Xing Xue <xingxue at outlook.com>
Date: Thu, 16 Jul 2026 06:40:35 -0400
Subject: [PATCH 5/5] Apply suggestion: ',' -> ';'.

Co-authored-by: Hubert Tong <hubert.reinterpretcast at gmail.com>
---
 libunwind/src/UnwindCursor.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 03ea96b2e4451..537dbd83f4f74 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -2596,7 +2596,7 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
     if (vapi_glue_addr_begin <= returnAddressInStack &&
         returnAddressInStack < vapi_glue_addr_end) {
       _LIBUNWIND_TRACE_UNWINDING(
-          "The return address in stack %p is within the range of VAPI address,"
+          "The return address in stack %p is within the range of VAPI address;"
           " set isKnownVapiNotActive to true\n",
           reinterpret_cast<void *>(returnAddressInStack));
       setIsKnownVapiNotActive(true);


From libcxx-commits at lists.llvm.org  Thu Jul 16 03:50:52 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Thu, 16 Jul 2026 03:50:52 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209473@github.com>
Message-ID: <6a58b78c.bc28b987.3d8071.f057@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `clang-with-lto-ubuntu` running on `as-worker-91` while building `clang-tools-extra,clang,cmake,flang,libcxx,lld,llvm` at step 7 "test-stage1-compiler".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/49/builds/4038

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 7 (test-stage1-compiler) failure: build (failure)
...
llvm-lit: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using ld64.lld: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/bin/ld64.lld
llvm-lit: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using wasm-ld: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/bin/wasm-ld
llvm-lit: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/utils/lit/tests/lit.cfg:124: warning: Setting a timeout per test not supported. Requires the Python psutil module but it could not be found. Try installing it via pip or via your operating system's package manager.
 Some tests will be skipped and the --timeout command line argument will not work.
llvm-lit: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using ld.lld: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/bin/ld.lld
llvm-lit: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using lld-link: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/bin/lld-link
llvm-lit: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using ld64.lld: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/bin/ld64.lld
llvm-lit: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/llvm-project/llvm/utils/lit/lit/llvm/config.py:569: note: using wasm-ld: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/bin/wasm-ld
-- Testing: 96393 tests, 72 workers --
Testing:  0.. 10.. 20.. 30.. 40.. 50
FAIL: LLVM :: CodeGen/SystemZ/zos-ppa2.ll (51641 of 96393)
******************** TEST 'LLVM :: CodeGen/SystemZ/zos-ppa2.ll' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
sed -e 's/!"MODE"/!"ascii"/' -e 's/BYTE/85/' /home/buildbot/as-worker-91/clang-with-lto-ubuntu/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll > /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: sed -e 's/!"MODE"/!"ascii"/' -e s/BYTE/85/ /home/buildbot/as-worker-91/clang-with-lto-ubuntu/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
# RUN: at line 2
/home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true < /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll |  /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/bin/FileCheck /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true
# executed command: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/bin/FileCheck /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# .---command stderr------------
# | /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll:24:10: error: CHECK: expected string not found in input
# | ; CHECK: DC XL6'F2F3F0F0F0F0'
# |          ^
# | <stdin>:17:39: note: scanning from here
# |  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0' 
# |                                       ^
# | <stdin>:18:2: note: possible intended match here
# |  DC XL6'F2F4F0F0F0F0' 
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /home/buildbot/as-worker-91/clang-with-lto-ubuntu/build/stage1/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             .
# |             .
# |             .
# |            12:  DC XL4'00000000'  
# |            13:  DC XL1'85'  
# |            14:  DC XL1'00'  
# |            15:  DC XL2'0000'  
# |            16: L#DVS DS 0H  

```

</details>

https://github.com/llvm/llvm-project/pull/209473

From libcxx-commits at lists.llvm.org  Thu Jul 16 05:15:03 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 16 Jul 2026 05:15:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libcxx] Partially revert
	#208330 (PR #209928)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209928@github.com>
Message-ID: <6a58cb47.8e75e0cb.1225b0.ee2b@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM, this fixed the downstream arm64e builds that were broken.

https://github.com/llvm/llvm-project/pull/209928

From libcxx-commits at lists.llvm.org  Thu Jul 16 05:15:35 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 16 Jul 2026 05:15:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] db7356d - [libc++] Partially revert
	#208330 (#209928)
Message-ID: <6a58cb67.7270d2fd.33a4d0.b3be@mx.google.com>


Author: Petr Hosek
Date: 2026-07-16T08:15:30-04:00
New Revision: db7356dde8312a86e79ec0d7b7f844878a1a743c

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

LOG: [libc++] Partially revert #208330 (#209928)

We found out that the new mechanism for detecting overriden functions
does not support Arm Pointer Authentication (PAuth). Addressing this
limitation is going to require changes to Clang. In the meantime, we
switched back to the old mechanism when PAuth is enabled.

Added: 
    

Modified: 
    libcxx/src/include/overridable_function.h
    libcxx/src/new.cpp
    libcxxabi/src/stdlib_new_delete.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index 83439ad363208..e430ccda29f65 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -21,12 +21,17 @@
 // whether an overridable function (typically a weak symbol) like `operator new`
 // has been overridden by a user or not.
 //
-// This is a low-level utility which does not work on all platforms, since it needs to
-// make assumptions about the object file format in use. This currently works with Mach-O
-// files (used on Darwin) and with ELF files (used on Linux and others). On platforms
-// where we know how to implement this detection, the macro
-// _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION  is defined to 1, and it is defined to 0 on
-// other platforms.
+// This is a low-level utility which does not work on all platforms, since it needs
+// to make assumptions about the object file format in use. Furthermore, it requires
+// the "base definition" of the function (the one we want to check whether it has been
+// overridden) to be defined using the OVERRIDABLE_FUNCTION macro.
+//
+// This currently works with Mach-O files (used on Darwin) and with ELF files (used on Linux
+// and others). On platforms where we know how to implement this detection, the macro
+// _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION is defined to 1, and it is defined to 0 on
+// other platforms. The OVERRIDABLE_FUNCTION macro is defined to perform a normal
+// function definition on unsupported platforms so that it can be used to define functions
+// regardless of whether detection is actually supported.
 //
 // How does this work?
 // -------------------
@@ -38,6 +43,14 @@
 // compares the address of `__impl_ref<f>::__impl_` with the address of `f` loaded from
 // GOT: if `f` was overridden by the user in another TU, the addresses will be 
diff erent.
 //
+// When pointer authentication is used, the above mechanism doesn't work (yet) so we use
+// a 
diff erent strategy placing `f`'s definition (in the libc++ built library) inside
+// a special section, which we do using the `__section__` attribute via the
+// OVERRIDABLE_FUNCTION macro. Then, when comes the time to check whether the function has
+// been overridden, we take the address of the function and we check whether it falls inside
+// the special section we created. This can be done by finding pointers to the start and
+// the end of the section, and then checking whether `f` falls within those bounds.
+//
 // Important note
 // --------------
 //
@@ -50,6 +63,10 @@
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
 
+#  if !__has_feature(ptrauth_calls)
+
+#    define OVERRIDABLE_FUNCTION [[gnu::weak]]
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace {
@@ -95,10 +112,52 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
 
 _LIBCPP_END_NAMESPACE_STD
 
-#else
+#  else // __has_feature(ptrauth_calls)
+
+#    include <ptrauth.h>
+
+#    if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
+#      define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
+// Declare two dummy bytes and give them these special `__asm` values. These values are
+// defined by the linker, which means that referring to `&__lcxx_override_start` will
+// effectively refer to the address where the section starts (and same for the end).
+extern char __start___lcxx_override __asm("section$start$__TEXT$__lcxx_override");
+extern char __stop___lcxx_override __asm("section$end$__TEXT$__lcxx_override");
+#    elif defined(_LIBCPP_OBJECT_FORMAT_ELF)
+// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
+// variables with those names corresponding to the start and the end of the section.
+//
+// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
+#      define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+extern char __start___lcxx_override;
+extern char __stop___lcxx_override;
+#    endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+template <typename T, T* _Func>
+_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
+  uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
+  uintptr_t __end   = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
+  uintptr_t __ptr   = reinterpret_cast<uintptr_t>(_Func);
+
+  // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
+  // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
+  // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
+  // stripped the function pointer. See rdar://122927845.
+  __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
+
+  // Finally, the function was overridden if it falls outside of the section's bounds.
+  return __ptr < __start || __ptr > __end;
+}
+_LIBCPP_END_NAMESPACE_STD
+
+#  endif // __has_feature(ptrauth_calls)
+
+#else // defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
 
 #  define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 0
+#  define OVERRIDABLE_FUNCTION [[gnu::weak]]
 
-#endif
+#endif // defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
 
 #endif // _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H

diff  --git a/libcxx/src/new.cpp b/libcxx/src/new.cpp
index 0ccc3acad06aa..25bdaea32f57a 100644
--- a/libcxx/src/new.cpp
+++ b/libcxx/src/new.cpp
@@ -43,7 +43,7 @@ static void* operator_new_impl(std::size_t size) {
   return p;
 }
 
-[[gnu::weak]] void* operator new(std::size_t size) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new(std::size_t size) _THROW_BAD_ALLOC {
   void* p = operator_new_impl(size);
   if (p == nullptr)
     __throw_bad_alloc_shim();
@@ -74,7 +74,7 @@ static void* operator_new_impl(std::size_t size) {
 #  endif
 }
 
-[[gnu::weak]] void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
+OVERRIDABLE_FUNCTION void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
 
 [[gnu::weak]] void* operator new[](size_t size, const std::nothrow_t&) noexcept {
 #  if !_LIBCPP_HAS_EXCEPTIONS
@@ -134,7 +134,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
   return p;
 }
 
-[[gnu::weak]] void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
   void* p = operator_new_aligned_impl(size, alignment);
   if (p == nullptr)
     __throw_bad_alloc_shim();
@@ -165,7 +165,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
 #    endif
 }
 
-[[gnu::weak]] void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
   return ::operator new(size, alignment);
 }
 

diff  --git a/libcxxabi/src/stdlib_new_delete.cpp b/libcxxabi/src/stdlib_new_delete.cpp
index 9f538948542c3..164a26335eb38 100644
--- a/libcxxabi/src/stdlib_new_delete.cpp
+++ b/libcxxabi/src/stdlib_new_delete.cpp
@@ -59,7 +59,7 @@ static void* operator_new_impl(std::size_t size) {
   return p;
 }
 
-[[gnu::weak]] void* operator new(std::size_t size) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new(std::size_t size) _THROW_BAD_ALLOC {
   void* p = operator_new_impl(size);
   if (p == nullptr)
     __throw_bad_alloc_shim();
@@ -90,7 +90,7 @@ static void* operator_new_impl(std::size_t size) {
 #endif
 }
 
-[[gnu::weak]] void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
+OVERRIDABLE_FUNCTION void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
 
 [[gnu::weak]] void* operator new[](size_t size, const std::nothrow_t&) noexcept {
 #if !_LIBCPP_HAS_EXCEPTIONS
@@ -150,7 +150,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
   return p;
 }
 
-[[gnu::weak]] void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
   void* p = operator_new_aligned_impl(size, alignment);
   if (p == nullptr)
     __throw_bad_alloc_shim();
@@ -181,7 +181,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
 #  endif
 }
 
-[[gnu::weak]] void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
   return ::operator new(size, alignment);
 }
 


        

From libcxx-commits at lists.llvm.org  Thu Jul 16 05:15:38 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 16 Jul 2026 05:15:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libcxx] Partially revert
	#208330 (PR #209928)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209928@github.com>
Message-ID: <6a58cb6a.92d2873c.3511c3.bbbb@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/209928

From libcxx-commits at lists.llvm.org  Thu Jul 16 05:29:38 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Thu, 16 Jul 2026 05:29:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3133: Modernizing
 numeric type requirements (PR #208145)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208145@github.com>
Message-ID: <6a58ceb2.743400b0.d1365.e23c@mx.google.com>


================
@@ -136,7 +136,7 @@
 "`LWG3101 <https://wg21.link/LWG3101>`__","``span``\ 's ``Container``\  constructors need another constraint","2019-02 (Kona)","|Complete|","","`#103837 <https://github.com/llvm/llvm-project/issues/103837>`__",""
 "`LWG3112 <https://wg21.link/LWG3112>`__","``system_error``\  and ``filesystem_error``\  constructors taking a ``string``\  may not be able to meet their postconditions","2019-02 (Kona)","|Nothing To Do|","","`#100253 <https://github.com/llvm/llvm-project/issues/100253>`__",""
 "`LWG3119 <https://wg21.link/LWG3119>`__","Program-definedness of closure types","2019-02 (Kona)","|Nothing To Do|","","`#103838 <https://github.com/llvm/llvm-project/issues/103838>`__",""
-"`LWG3133 <https://wg21.link/LWG3133>`__","Modernizing numeric type requirements","2019-02 (Kona)","","","`#100254 <https://github.com/llvm/llvm-project/issues/100254>`__",""
+"`LWG3133 <https://wg21.link/LWG3133>`__","Modernizing numeric type requirements","2019-02 (Kona)","|Complete|","23","`#100254 <https://github.com/llvm/llvm-project/issues/100254>`__",""
----------------
frederick-vs-ja wrote:

I'm note sure whether we should mark the version as `23`. Maybe it's better to drop the version.

Violation of the named requirements technically results UB/IFNDR, and implementations are not yet required to diagnose the violation. I personally want to diagnose such violation, but not sure whether implementors wan to do so.

CC @ldionne @philnik777.

https://github.com/llvm/llvm-project/pull/208145

From libcxx-commits at lists.llvm.org  Thu Jul 16 05:30:01 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Thu, 16 Jul 2026 05:30:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3133: Modernizing
 numeric type requirements (PR #208145)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208145@github.com>
Message-ID: <6a58cec9.449bfb44.1b765.ab02@mx.google.com>


================
@@ -307,6 +312,12 @@ class complex {
 public:
   typedef _Tp value_type;
 
+  static_assert(is_object<_Tp>::value && __is_unqualified_v<_Tp> && is_default_constructible_v<_Tp> &&
+                    is_copy_constructible_v<_Tp> && is_copy_assignable_v<_Tp> && is_destructible_v<_Tp>,
----------------
frederick-vs-ja wrote:

I think we should use `_And` from `<__type_traits/conjunction.h>` to combine these traits. Also, please ensure that this line works in C++03 mode.

https://github.com/llvm/llvm-project/pull/208145

From libcxx-commits at lists.llvm.org  Thu Jul 16 06:32:22 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 16 Jul 2026 06:32:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][docs] Mark LWG3116 as Complete
	(PR #209916)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209916@github.com>
Message-ID: <6a58dd66.510f576d.d8fd6.7583@mx.google.com>


https://github.com/ldionne requested changes to this pull request.

In the PR description, please explain why this is already done and no tests need to be added.

https://github.com/llvm/llvm-project/pull/209916

From libcxx-commits at lists.llvm.org  Thu Jul 16 07:39:43 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 16 Jul 2026 07:39:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add sentinel OtherConst
 cases for `nodiscard` tests in `enumerate_view` (PR #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a58ed2f.0c05e68e.b358c.ee0e@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/209289

From libcxx-commits at lists.llvm.org  Thu Jul 16 07:46:50 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 16 Jul 2026 07:46:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 122efb2 - [libc++] Optimize
	std::is_heap_until (#209141)
Message-ID: <6a58eeda.dde95cf2.362759.0260@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-16T16:46:46+02:00
New Revision: 122efb26893f7e214d0223615c3a4abcc0ac7796

URL: https://github.com/llvm/llvm-project/commit/122efb26893f7e214d0223615c3a4abcc0ac7796
DIFF: https://github.com/llvm/llvm-project/commit/122efb26893f7e214d0223615c3a4abcc0ac7796.diff

LOG: [libc++] Optimize std::is_heap_until (#209141)

Fixes #208299

Added: 
    

Modified: 
    libcxx/include/__algorithm/is_heap_until.h
    libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__algorithm/is_heap_until.h b/libcxx/include/__algorithm/is_heap_until.h
index 7444d978e37f5..c05cc2177ae90 100644
--- a/libcxx/include/__algorithm/is_heap_until.h
+++ b/libcxx/include/__algorithm/is_heap_until.h
@@ -12,7 +12,6 @@
 #include <__algorithm/comp.h>
 #include <__algorithm/comp_ref_type.h>
 #include <__config>
-#include <__iterator/iterator_traits.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -20,29 +19,37 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Compare, class _RandomAccessIterator>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandomAccessIterator
-__is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp) {
-  typedef typename iterator_traits<_RandomAccessIterator>::
diff erence_type 
diff erence_type;
-  
diff erence_type __len      = __last - __first;
-  
diff erence_type __p        = 0;
-  
diff erence_type __c        = 1;
-  _RandomAccessIterator __pp = __first;
-  while (__c < __len) {
-    _RandomAccessIterator __cp = __first + __c;
-    if (__comp(*__pp, *__cp))
-      return __cp;
-    ++__c;
-    ++__cp;
-    if (__c == __len)
-      return __last;
-    if (__comp(*__pp, *__cp))
-      return __cp;
-    ++__p;
-    ++__pp;
-    __c = 2 * __p + 1;
+template <class _Compare, class _Iter>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter
+__is_heap_until(_Iter __first, _Iter __last, _Compare&& __comp) {
+  auto __count = __last - __first;
+
+  if (__count < 2)
+    return __last;
+
+  // This points to one past the last parent that has two children.
+  // Integer division handles the case where the last parent has a single child.
+  auto __parent_last = __first + (__count - 1) / 2;
+  auto __child       = __first;
+  ++__child;
+  for (; __first != __parent_last; ++__first) {
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
+
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
   }
-  return __last;
+
+  // If the heap is even-sized, the last parent has a single child, handled here.
+  if (__count % 2 == 0) {
+    if (__comp(*__first, *__child))
+      return __child;
+    ++__child;
+  }
+
+  return __child;
 }
 
 template <class _RandomAccessIterator, class _Compare>

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
index 5fd119a21a98f..42a66fa5723d6 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
@@ -100,6 +100,12 @@ constexpr void test_iter_sent() {
   test_one<Iter, Sent>(std::array{2, 1}, 2);
   // 2-element sequence, not a heap.
   test_one<Iter, Sent>(std::array{1, 2}, 1);
+  // 3-element sequence, a heap.
+  test_one<Iter, Sent>(std::array{3, 2, 1}, 3);
+  // 3-element sequence, LHS not a heap.
+  test_one<Iter, Sent>(std::array{2, 3, 1}, 1);
+  // 3-element sequence, RHS not a heap.
+  test_one<Iter, Sent>(std::array{2, 1, 3}, 2);
   // Longer sequence, a heap.
   test_one<Iter, Sent>(std::array{8, 6, 7, 3, 4, 1, 5, 2}, 8);
   // Longer sequence, not a heap.


        

From libcxx-commits at lists.llvm.org  Thu Jul 16 07:46:53 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 16 Jul 2026 07:46:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::is_heap_until (PR
	#209141)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209141@github.com>
Message-ID: <6a58eedd.805085c5.3275.4501@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/209141

From libcxx-commits at lists.llvm.org  Thu Jul 16 07:50:17 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 16 Jul 2026 07:50:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::isgreater()` (PR #210075)
Message-ID: <llvm.org/llvm/llvm-project/pull/210075@github.com>


https://github.com/lknknm created https://github.com/llvm/llvm-project/pull/210075

Implement `constexpr` to `std::isgreater() as defined by [P0533R9](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0533r9.pdf).

Towards https://github.com/llvm/llvm-project/issues/105174

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);


From libcxx-commits at lists.llvm.org  Thu Jul 16 07:50:29 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 16 Jul 2026 07:50:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::isgreater()` (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a58efb5.433f7c1d.287d2d.2bf0@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Thu Jul 16 07:51:58 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 16 Jul 2026 07:51:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::isgreater()` (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a58f00e.8ecc52f4.39b12d.956d@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 1/2] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 2/2] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 


From libcxx-commits at lists.llvm.org  Thu Jul 16 08:14:54 2026
From: libcxx-commits at lists.llvm.org (Pavel Novikov via libcxx-commits)
Date: Thu, 16 Jul 2026 08:14:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
Message-ID: <llvm.org/llvm/llvm-project/pull/210078@github.com>


https://github.com/toughengineer created https://github.com/llvm/llvm-project/pull/210078

The fix is trivial, and I wouldn't submit it otherwise, but I indeed could measure a difference in performance with slow iterators.

Here's the graph:
<img width="1210" height="681" alt="graph" src="https://github.com/user-attachments/assets/bf8b6f12-bf69-4926-8f70-e77d12da8085" />
<details>
<summary>benchmark output before</summary>

```
2026-07-16T16:53:06+03:00
Running ./string_append
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.25, 0.23, 0.09
----------------------------------------------------------------------------------------------------
Benchmark                                          Time             CPU   Iterations UserCounters...
----------------------------------------------------------------------------------------------------
bench_append<std::vector<char>>/1               3.10 ns         3.10 ns    226315808 t/i=3.09819ns
bench_append<std::vector<char>>/2               3.10 ns         3.10 ns    223987528 t/i=1.55159ns
bench_append<std::vector<char>>/4               3.10 ns         3.10 ns    225262769 t/i=775.792ps
bench_append<std::vector<char>>/8               2.96 ns         2.96 ns    238555228 t/i=369.405ps
bench_append<std::vector<char>>/16              2.89 ns         2.89 ns    243030831 t/i=180.715ps
bench_append<std::vector<char>>/32              10.5 ns         10.5 ns     67805279 t/i=327.351ps
bench_append<std::vector<char>>/64              10.4 ns         10.4 ns     67913691 t/i=163.163ps
bench_append<std::vector<char>>/128             10.8 ns         10.8 ns     65647320 t/i=83.9901ps
bench_append<std::vector<char>>/256             10.7 ns         10.7 ns     64584881 t/i=41.8108ps
bench_append<std::vector<char>>/512             12.6 ns         12.6 ns     55021627 t/i=24.6862ps
bench_append<std::vector<char>>/1024            14.6 ns         14.6 ns     47745492 t/i=14.3039ps
bench_append<std::vector<char>>/2048            37.4 ns         37.4 ns     18517710 t/i=18.2561ps
bench_append<std::vector<char>>/4096            47.6 ns         47.6 ns     14783174 t/i=11.6187ps
bench_append<std::vector<char>>/8192            57.4 ns         57.4 ns     12362468 t/i=7.00085ps
bench_append<std::vector<char>>/16384            106 ns          106 ns      6729404 t/i=6.4528ps
bench_append<std::vector<char>>/32768            519 ns          519 ns      1368856 t/i=15.84ps
bench_append<std::vector<char>>/65536           1245 ns         1245 ns       653681 t/i=18.9985ps
bench_append<std::vector<char>>/131072          2050 ns         2050 ns       331142 t/i=15.6421ps
bench_append<std::vector<char>>/262144          4319 ns         4318 ns       161065 t/i=16.4732ps
bench_append<std::vector<char>>/524288          8290 ns         8289 ns        85569 t/i=15.8103ps
bench_append<std::vector<char>>/1048576        21300 ns        21299 ns        31262 t/i=20.3122ps
bench_append<std::list<char>>/1                 3.54 ns         3.53 ns    196033898 t/i=3.53483ns
bench_append<std::list<char>>/2                 5.27 ns         5.27 ns    134648270 t/i=2.63524ns
bench_append<std::list<char>>/4                 5.66 ns         5.66 ns    125707554 t/i=1.41568ns
bench_append<std::list<char>>/8                 9.79 ns         9.79 ns     72542694 t/i=1.22324ns
bench_append<std::list<char>>/16                14.0 ns         14.0 ns     49090700 t/i=877.378ps
bench_append<std::list<char>>/32                40.4 ns         40.4 ns     17585554 t/i=1.26376ns
bench_append<std::list<char>>/64                88.6 ns         88.6 ns      7671990 t/i=1.38454ns
bench_append<std::list<char>>/128                207 ns          207 ns      3446792 t/i=1.61596ns
bench_append<std::list<char>>/256                546 ns          546 ns      1302384 t/i=2.13168ns
bench_append<std::list<char>>/512               1062 ns         1062 ns       658620 t/i=2.07481ns
bench_append<std::list<char>>/1024              2095 ns         2095 ns       332003 t/i=2.04562ns
bench_append<std::list<char>>/2048              4254 ns         4254 ns       165516 t/i=2.07696ns
bench_append<std::list<char>>/4096              8453 ns         8453 ns        81254 t/i=2.06364ns
bench_append<std::list<char>>/8192             16727 ns        16727 ns        41643 t/i=2.04181ns
bench_append<std::list<char>>/16384            33353 ns        33314 ns        20843 t/i=2.0333ns
bench_append<std::list<char>>/32768            66781 ns        66779 ns        10635 t/i=2.03792ns
bench_append<std::list<char>>/65536           135174 ns       135170 ns         5165 t/i=2.06253ns
bench_append<std::list<char>>/131072          273614 ns       273614 ns         2543 t/i=2.08751ns
bench_append<std::list<char>>/262144          593169 ns       592477 ns         1135 t/i=2.26012ns
bench_append<std::list<char>>/524288         1328844 ns      1328848 ns          529 t/i=2.53458ns
bench_append<std::list<char>>/1048576        4477818 ns      4477612 ns          157 t/i=4.27018ns
bench_append<std::multiset<char>>/1             3.84 ns         3.84 ns    180937556 t/i=3.83834ns
bench_append<std::multiset<char>>/2             9.36 ns         9.36 ns     74438785 t/i=4.68069ns
bench_append<std::multiset<char>>/4             13.6 ns         13.6 ns     51097651 t/i=3.40815ns
bench_append<std::multiset<char>>/8             21.8 ns         21.8 ns     31933025 t/i=2.71937ns
bench_append<std::multiset<char>>/16            41.9 ns         41.9 ns     16805653 t/i=2.62013ns
bench_append<std::multiset<char>>/32            96.2 ns         96.2 ns      7354278 t/i=3.00633ns
bench_append<std::multiset<char>>/64             224 ns          224 ns      3115349 t/i=3.49983ns
bench_append<std::multiset<char>>/128            486 ns          486 ns      1440616 t/i=3.79756ns
bench_append<std::multiset<char>>/256           1013 ns         1013 ns       683716 t/i=3.95719ns
bench_append<std::multiset<char>>/512           2090 ns         2090 ns       335399 t/i=4.08226ns
bench_append<std::multiset<char>>/1024          4382 ns         4382 ns       157870 t/i=4.27957ns
bench_append<std::multiset<char>>/2048          8885 ns         8884 ns        79538 t/i=4.338ns
bench_append<std::multiset<char>>/4096         17717 ns        17716 ns        39341 t/i=4.32532ns
bench_append<std::multiset<char>>/8192         35759 ns        35756 ns        19762 t/i=4.3648ns
bench_append<std::multiset<char>>/16384        71429 ns        71427 ns         9751 t/i=4.35953ns
bench_append<std::multiset<char>>/32768       142846 ns       142839 ns         4834 t/i=4.3591ns
bench_append<std::multiset<char>>/65536       299432 ns       299430 ns         2362 t/i=4.56894ns
bench_append<std::multiset<char>>/131072      607360 ns       607356 ns         1166 t/i=4.63376ns
bench_append<std::multiset<char>>/262144     1295102 ns      1295083 ns          546 t/i=4.94035ns
bench_append<std::multiset<char>>/524288     3337828 ns      3337793 ns          209 t/i=6.36634ns
bench_append<std::multiset<char>>/1048576    8387334 ns      8387005 ns           81 t/i=7.99847ns
bench_append<Container>/1                       13.3 ns         13.3 ns     52114703 t/i=13.3287ns
bench_append<Container>/2                       23.5 ns         23.5 ns     29020259 t/i=11.771ns
bench_append<Container>/4                       48.9 ns         48.9 ns     14313064 t/i=12.2231ns
bench_append<Container>/8                       91.1 ns         91.0 ns      7690246 t/i=11.3811ns
bench_append<Container>/16                       161 ns          161 ns      4413230 t/i=10.0588ns
bench_append<Container>/32                       312 ns          312 ns      2244326 t/i=9.75056ns
bench_append<Container>/64                       583 ns          583 ns      1185685 t/i=9.11584ns
bench_append<Container>/128                     1123 ns         1122 ns       626614 t/i=8.76911ns
bench_append<Container>/256                     2197 ns         2197 ns       320017 t/i=8.58337ns
bench_append<Container>/512                     4319 ns         4319 ns       160779 t/i=8.43519ns
bench_append<Container>/1024                    8622 ns         8622 ns        79252 t/i=8.41967ns
bench_append<Container>/2048                   17239 ns        17239 ns        39849 t/i=8.41726ns
bench_append<Container>/4096                   34502 ns        34501 ns        20299 t/i=8.42297ns
bench_append<Container>/8192                   68731 ns        68728 ns        10327 t/i=8.38964ns
bench_append<Container>/16384                 137217 ns       137213 ns         5064 t/i=8.3748ns
bench_append<Container>/32768                 273730 ns       273710 ns         2518 t/i=8.35297ns
bench_append<Container>/65536                 550779 ns       550759 ns         1253 t/i=8.40392ns
bench_append<Container>/131072               1104636 ns      1104602 ns          632 t/i=8.42744ns
bench_append<Container>/262144               2192175 ns      2192081 ns          319 t/i=8.36213ns
bench_append<Container>/524288               4395209 ns      4395025 ns          160 t/i=8.38284ns
bench_append<Container>/1048576              9153992 ns      9153748 ns           77 t/i=8.72969ns
```
</details>
<details>
<summary>benchmark output after</summary>

```
2026-07-16T16:55:54+03:00
Running ./string_append
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.26, 0.35, 0.17
----------------------------------------------------------------------------------------------------
Benchmark                                          Time             CPU   Iterations UserCounters...
----------------------------------------------------------------------------------------------------
bench_append<std::vector<char>>/1               3.09 ns         3.09 ns    227887283 t/i=3.0944ns
bench_append<std::vector<char>>/2               3.10 ns         3.09 ns    226039491 t/i=1.54692ns
bench_append<std::vector<char>>/4               3.10 ns         3.10 ns    225739021 t/i=774.206ps
bench_append<std::vector<char>>/8               3.10 ns         3.09 ns    224468619 t/i=386.875ps
bench_append<std::vector<char>>/16              2.91 ns         2.91 ns    240031986 t/i=182.18ps
bench_append<std::vector<char>>/32              10.9 ns         10.9 ns     65077293 t/i=339.745ps
bench_append<std::vector<char>>/64              10.9 ns         10.9 ns     63585149 t/i=170.175ps
bench_append<std::vector<char>>/128             11.1 ns         11.1 ns     63889890 t/i=86.7411ps
bench_append<std::vector<char>>/256             11.0 ns         11.0 ns     62736325 t/i=43.117ps
bench_append<std::vector<char>>/512             13.1 ns         13.1 ns     53444253 t/i=25.5178ps
bench_append<std::vector<char>>/1024            14.9 ns         14.9 ns     46805617 t/i=14.5771ps
bench_append<std::vector<char>>/2048            37.6 ns         37.6 ns     18720091 t/i=18.3699ps
bench_append<std::vector<char>>/4096            47.6 ns         47.6 ns     14738139 t/i=11.6281ps
bench_append<std::vector<char>>/8192            57.3 ns         57.3 ns     12354002 t/i=6.99105ps
bench_append<std::vector<char>>/16384            105 ns          105 ns      6534679 t/i=6.4209ps
bench_append<std::vector<char>>/32768            520 ns          520 ns      1368420 t/i=15.8662ps
bench_append<std::vector<char>>/65536           1043 ns         1043 ns       665359 t/i=15.9218ps
bench_append<std::vector<char>>/131072          2074 ns         2073 ns       339638 t/i=15.8193ps
bench_append<std::vector<char>>/262144          4144 ns         4144 ns       160746 t/i=15.8074ps
bench_append<std::vector<char>>/524288          8200 ns         8200 ns        80639 t/i=15.6401ps
bench_append<std::vector<char>>/1048576        21893 ns        21892 ns        30043 t/i=20.8781ps
bench_append<std::list<char>>/1                 3.55 ns         3.55 ns    199315153 t/i=3.5478ns
bench_append<std::list<char>>/2                 5.29 ns         5.29 ns    134176216 t/i=2.64552ns
bench_append<std::list<char>>/4                 5.49 ns         5.49 ns    123464411 t/i=1.37183ns
bench_append<std::list<char>>/8                 8.85 ns         8.85 ns     80565431 t/i=1.10657ns
bench_append<std::list<char>>/16                12.5 ns         12.5 ns     55630347 t/i=783.169ps
bench_append<std::list<char>>/32                39.6 ns         39.6 ns     17583818 t/i=1.23641ns
bench_append<std::list<char>>/64                89.0 ns         89.0 ns      8200063 t/i=1.39032ns
bench_append<std::list<char>>/128                204 ns          204 ns      3371002 t/i=1.59479ns
bench_append<std::list<char>>/256                546 ns          546 ns      1256033 t/i=2.13231ns
bench_append<std::list<char>>/512               1065 ns         1065 ns       648641 t/i=2.08083ns
bench_append<std::list<char>>/1024              2102 ns         2102 ns       334077 t/i=2.05267ns
bench_append<std::list<char>>/2048              4249 ns         4249 ns       164325 t/i=2.07457ns
bench_append<std::list<char>>/4096              8433 ns         8433 ns        84268 t/i=2.05886ns
bench_append<std::list<char>>/8192             16685 ns        16685 ns        41640 t/i=2.03673ns
bench_append<std::list<char>>/16384            33543 ns        33543 ns        21102 t/i=2.04728ns
bench_append<std::list<char>>/32768            67025 ns        67022 ns        10656 t/i=2.04534ns
bench_append<std::list<char>>/65536           135122 ns       135120 ns         5220 t/i=2.06176ns
bench_append<std::list<char>>/131072          274342 ns       274328 ns         2542 t/i=2.09296ns
bench_append<std::list<char>>/262144          591436 ns       591421 ns         1181 t/i=2.25609ns
bench_append<std::list<char>>/524288         1389493 ns      1388228 ns          523 t/i=2.64784ns
bench_append<std::list<char>>/1048576        4536406 ns      4536297 ns          153 t/i=4.32615ns
bench_append<std::multiset<char>>/1             3.87 ns         3.87 ns    181230524 t/i=3.87176ns
bench_append<std::multiset<char>>/2             9.51 ns         9.51 ns     71901138 t/i=4.75509ns
bench_append<std::multiset<char>>/4             13.2 ns         13.2 ns     52352810 t/i=3.29722ns
bench_append<std::multiset<char>>/8             21.7 ns         21.7 ns     32439441 t/i=2.71142ns
bench_append<std::multiset<char>>/16            40.0 ns         40.0 ns     17563514 t/i=2.49991ns
bench_append<std::multiset<char>>/32            96.8 ns         96.8 ns      7208224 t/i=3.0244ns
bench_append<std::multiset<char>>/64             225 ns          225 ns      3091425 t/i=3.5148ns
bench_append<std::multiset<char>>/128            488 ns          488 ns      1443983 t/i=3.81319ns
bench_append<std::multiset<char>>/256           1014 ns         1014 ns       700744 t/i=3.96008ns
bench_append<std::multiset<char>>/512           2087 ns         2087 ns       333876 t/i=4.07604ns
bench_append<std::multiset<char>>/1024          4405 ns         4404 ns       159590 t/i=4.30126ns
bench_append<std::multiset<char>>/2048          8904 ns         8904 ns        79107 t/i=4.34771ns
bench_append<std::multiset<char>>/4096         17856 ns        17855 ns        39341 t/i=4.35914ns
bench_append<std::multiset<char>>/8192         35414 ns        35413 ns        19531 t/i=4.32291ns
bench_append<std::multiset<char>>/16384        71063 ns        70984 ns         9626 t/i=4.33251ns
bench_append<std::multiset<char>>/32768       145779 ns       145774 ns         4834 t/i=4.44867ns
bench_append<std::multiset<char>>/65536       304988 ns       304974 ns         2357 t/i=4.65353ns
bench_append<std::multiset<char>>/131072      604942 ns       602719 ns         1128 t/i=4.59838ns
bench_append<std::multiset<char>>/262144     1267769 ns      1267755 ns          560 t/i=4.8361ns
bench_append<std::multiset<char>>/524288     3324835 ns      3324630 ns          213 t/i=6.34123ns
bench_append<std::multiset<char>>/1048576    8387543 ns      8387384 ns           82 t/i=7.99883ns
bench_append<Container>/1                       10.4 ns         10.4 ns     66804092 t/i=10.3965ns
bench_append<Container>/2                       16.8 ns         16.8 ns     41695556 t/i=8.39998ns
bench_append<Container>/4                       34.5 ns         34.5 ns     20452545 t/i=8.62682ns
bench_append<Container>/8                       62.9 ns         62.9 ns     11109242 t/i=7.86804ns
bench_append<Container>/16                       111 ns          111 ns      6422949 t/i=6.91392ns
bench_append<Container>/32                       216 ns          216 ns      3276646 t/i=6.76101ns
bench_append<Container>/64                       400 ns          400 ns      1745608 t/i=6.2475ns
bench_append<Container>/128                      765 ns          765 ns       928239 t/i=5.97368ns
bench_append<Container>/256                     1489 ns         1489 ns       468787 t/i=5.8147ns
bench_append<Container>/512                     2942 ns         2942 ns       238431 t/i=5.74671ns
bench_append<Container>/1024                    5837 ns         5837 ns       121633 t/i=5.69982ns
bench_append<Container>/2048                   11727 ns        11726 ns        60224 t/i=5.72563ns
bench_append<Container>/4096                   23304 ns        23303 ns        30075 t/i=5.68928ns
bench_append<Container>/8192                   46442 ns        46440 ns        15129 t/i=5.66892ns
bench_append<Container>/16384                  92749 ns        92748 ns         7607 t/i=5.66086ns
bench_append<Container>/32768                 185188 ns       185181 ns         3759 t/i=5.65128ns
bench_append<Container>/65536                 371024 ns       371014 ns         1888 t/i=5.66122ns
bench_append<Container>/131072                739596 ns       739562 ns          958 t/i=5.64241ns
bench_append<Container>/262144               1487139 ns      1487121 ns          477 t/i=5.67292ns
bench_append<Container>/524288               2963851 ns      2963776 ns          237 t/i=5.65295ns
bench_append<Container>/1048576              6313121 ns      6313038 ns          112 t/i=6.02058ns
```
</details>

The "elapsed time" on the graph (`t/i` in benchmark output, time per item) is elapsed time for the append operation divided by the number of elements/characters appended.

`Container` is basically a `std::vector<char>` with wrapped iterators with increment operation slowed down by a call to `rand()`.

Otherwise I could not measure a significant difference for `std::list<char>` or `std::multiset<char>`, those can be seen as reference and control values.

To benchmark it I used Clang 22.1.2 on Ubuntu 26.04 inside WSL2.

>From 8b114a55b16eb39a3005ad81c2cb0ae5d8d42347 Mon Sep 17 00:00:00 2001
From: Pavel Novikov <dev-ape at yandex.ru>
Date: Thu, 16 Jul 2026 17:43:25 +0300
Subject: [PATCH] [libc++] fix minor performance issue

---
 libcxx/include/string | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index a297a05813cee..957eb15e4fe1f 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1359,13 +1359,13 @@ public:
   template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
   append(_ForwardIterator __first, _ForwardIterator __last) {
-    size_type __sz  = size();
-    size_type __cap = capacity();
-    size_type __n   = static_cast<size_type>(std::distance(__first, __last));
-    if (__n == 0)
+    if (__first == __last)
       return *this;
 
     if (__string_is_trivial_iterator_v<_ForwardIterator> && !__addr_in_range(*__first)) {
+      size_type __sz  = size();
+      size_type __cap = capacity();
+      size_type __n   = static_cast<size_type>(std::distance(__first, __last));
       if (__cap - __sz < __n)
         __grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
       __annotate_increase(__n);


From libcxx-commits at lists.llvm.org  Thu Jul 16 08:15:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 16 Jul 2026 08:15:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a58f57d.7e72d6e7.229b50.ea27@mx.google.com>


github-actions[bot] wrote:

<!--LLVM NEW CONTRIBUTOR COMMENT-->

Hello @toughengineer :wave:

Thank you for submitting a Pull Request (PR) to the LLVM Project. Since this is your first PR, here are a few useful links covering our main contribution policies and review practices.

* All contributions to LLVM must follow our [LLVM AI Tool Use Policy](https://llvm.org/docs/AIToolPolicy.html). In particular, if you used AI while working on this PR, remember to add a note to the PR description.
* The [LLVM Code-Review Policy and Practices](https://llvm.org/docs/CodeReview.html) document contains practical information about the PR process, including how patches are reviewed and accepted, and who can review a PR.
* Our [LLVM Developer Policy](https://llvm.org/docs/DeveloperPolicy.html) describes our expectations for code quality, commit summaries and contains notes on our CI system.

Please reply to this message to confirm that you have read these policies, especially the LLVM AI Tool Use Policy, and that any AI tool usage has been noted in the PR description.

---

### Frequently asked questions

**How do I add reviewers?**

This PR will be automatically labeled, and the relevant teams will be notified. For some parts of the project, reviewers may also be added automatically.

You can also add reviewers manually using the **Reviewers** section on this page. If you cannot use that section, it is probably because you do not have write permissions for the repository. In that case, you can request a review by tagging reviewers in a comment using `@` followed by their GitHub username.

**What if there are no comments?**

If you have not received any comments on your PR after a week, you can request a review by pinging the PR with a comment such as “Ping”. The common courtesy ping rate is once a week. Please remember that you are asking for volunteer time from other developers.

**Are any special GitHub settings required to contribute to LLVM?**

We only require contributors to have a public email address associated with their GitHub commits, see this [section](https://llvm.org/docs/DeveloperPolicy.html#email-addresses) of LLVM Developer Policy for details.

---

If you have questions, feel free to leave a comment on this PR, or ask on [LLVM Discord](https://discord.com/invite/xS7Z362) or [LLVM Discourse](https://discourse.llvm.org/).

Thank you,
The LLVM Community

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Thu Jul 16 08:15:43 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 16 Jul 2026 08:15:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a58f59f.6ddb6d48.d4297.b61b@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Pavel Novikov (toughengineer)

<details>
<summary>Changes</summary>

The fix is trivial, and I wouldn't submit it otherwise, but I indeed could measure a difference in performance with slow iterators.

Here's the graph:
&lt;img width="1210" height="681" alt="graph" src="https://github.com/user-attachments/assets/bf8b6f12-bf69-4926-8f70-e77d12da8085" /&gt;
&lt;details&gt;
&lt;summary&gt;benchmark output before&lt;/summary&gt;

```
2026-07-16T16:53:06+03:00
Running ./string_append
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.25, 0.23, 0.09
----------------------------------------------------------------------------------------------------
Benchmark                                          Time             CPU   Iterations UserCounters...
----------------------------------------------------------------------------------------------------
bench_append&lt;std::vector&lt;char&gt;&gt;/1               3.10 ns         3.10 ns    226315808 t/i=3.09819ns
bench_append&lt;std::vector&lt;char&gt;&gt;/2               3.10 ns         3.10 ns    223987528 t/i=1.55159ns
bench_append&lt;std::vector&lt;char&gt;&gt;/4               3.10 ns         3.10 ns    225262769 t/i=775.792ps
bench_append&lt;std::vector&lt;char&gt;&gt;/8               2.96 ns         2.96 ns    238555228 t/i=369.405ps
bench_append&lt;std::vector&lt;char&gt;&gt;/16              2.89 ns         2.89 ns    243030831 t/i=180.715ps
bench_append&lt;std::vector&lt;char&gt;&gt;/32              10.5 ns         10.5 ns     67805279 t/i=327.351ps
bench_append&lt;std::vector&lt;char&gt;&gt;/64              10.4 ns         10.4 ns     67913691 t/i=163.163ps
bench_append&lt;std::vector&lt;char&gt;&gt;/128             10.8 ns         10.8 ns     65647320 t/i=83.9901ps
bench_append&lt;std::vector&lt;char&gt;&gt;/256             10.7 ns         10.7 ns     64584881 t/i=41.8108ps
bench_append&lt;std::vector&lt;char&gt;&gt;/512             12.6 ns         12.6 ns     55021627 t/i=24.6862ps
bench_append&lt;std::vector&lt;char&gt;&gt;/1024            14.6 ns         14.6 ns     47745492 t/i=14.3039ps
bench_append&lt;std::vector&lt;char&gt;&gt;/2048            37.4 ns         37.4 ns     18517710 t/i=18.2561ps
bench_append&lt;std::vector&lt;char&gt;&gt;/4096            47.6 ns         47.6 ns     14783174 t/i=11.6187ps
bench_append&lt;std::vector&lt;char&gt;&gt;/8192            57.4 ns         57.4 ns     12362468 t/i=7.00085ps
bench_append&lt;std::vector&lt;char&gt;&gt;/16384            106 ns          106 ns      6729404 t/i=6.4528ps
bench_append&lt;std::vector&lt;char&gt;&gt;/32768            519 ns          519 ns      1368856 t/i=15.84ps
bench_append&lt;std::vector&lt;char&gt;&gt;/65536           1245 ns         1245 ns       653681 t/i=18.9985ps
bench_append&lt;std::vector&lt;char&gt;&gt;/131072          2050 ns         2050 ns       331142 t/i=15.6421ps
bench_append&lt;std::vector&lt;char&gt;&gt;/262144          4319 ns         4318 ns       161065 t/i=16.4732ps
bench_append&lt;std::vector&lt;char&gt;&gt;/524288          8290 ns         8289 ns        85569 t/i=15.8103ps
bench_append&lt;std::vector&lt;char&gt;&gt;/1048576        21300 ns        21299 ns        31262 t/i=20.3122ps
bench_append&lt;std::list&lt;char&gt;&gt;/1                 3.54 ns         3.53 ns    196033898 t/i=3.53483ns
bench_append&lt;std::list&lt;char&gt;&gt;/2                 5.27 ns         5.27 ns    134648270 t/i=2.63524ns
bench_append&lt;std::list&lt;char&gt;&gt;/4                 5.66 ns         5.66 ns    125707554 t/i=1.41568ns
bench_append&lt;std::list&lt;char&gt;&gt;/8                 9.79 ns         9.79 ns     72542694 t/i=1.22324ns
bench_append&lt;std::list&lt;char&gt;&gt;/16                14.0 ns         14.0 ns     49090700 t/i=877.378ps
bench_append&lt;std::list&lt;char&gt;&gt;/32                40.4 ns         40.4 ns     17585554 t/i=1.26376ns
bench_append&lt;std::list&lt;char&gt;&gt;/64                88.6 ns         88.6 ns      7671990 t/i=1.38454ns
bench_append&lt;std::list&lt;char&gt;&gt;/128                207 ns          207 ns      3446792 t/i=1.61596ns
bench_append&lt;std::list&lt;char&gt;&gt;/256                546 ns          546 ns      1302384 t/i=2.13168ns
bench_append&lt;std::list&lt;char&gt;&gt;/512               1062 ns         1062 ns       658620 t/i=2.07481ns
bench_append&lt;std::list&lt;char&gt;&gt;/1024              2095 ns         2095 ns       332003 t/i=2.04562ns
bench_append&lt;std::list&lt;char&gt;&gt;/2048              4254 ns         4254 ns       165516 t/i=2.07696ns
bench_append&lt;std::list&lt;char&gt;&gt;/4096              8453 ns         8453 ns        81254 t/i=2.06364ns
bench_append&lt;std::list&lt;char&gt;&gt;/8192             16727 ns        16727 ns        41643 t/i=2.04181ns
bench_append&lt;std::list&lt;char&gt;&gt;/16384            33353 ns        33314 ns        20843 t/i=2.0333ns
bench_append&lt;std::list&lt;char&gt;&gt;/32768            66781 ns        66779 ns        10635 t/i=2.03792ns
bench_append&lt;std::list&lt;char&gt;&gt;/65536           135174 ns       135170 ns         5165 t/i=2.06253ns
bench_append&lt;std::list&lt;char&gt;&gt;/131072          273614 ns       273614 ns         2543 t/i=2.08751ns
bench_append&lt;std::list&lt;char&gt;&gt;/262144          593169 ns       592477 ns         1135 t/i=2.26012ns
bench_append&lt;std::list&lt;char&gt;&gt;/524288         1328844 ns      1328848 ns          529 t/i=2.53458ns
bench_append&lt;std::list&lt;char&gt;&gt;/1048576        4477818 ns      4477612 ns          157 t/i=4.27018ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/1             3.84 ns         3.84 ns    180937556 t/i=3.83834ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/2             9.36 ns         9.36 ns     74438785 t/i=4.68069ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/4             13.6 ns         13.6 ns     51097651 t/i=3.40815ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/8             21.8 ns         21.8 ns     31933025 t/i=2.71937ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/16            41.9 ns         41.9 ns     16805653 t/i=2.62013ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/32            96.2 ns         96.2 ns      7354278 t/i=3.00633ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/64             224 ns          224 ns      3115349 t/i=3.49983ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/128            486 ns          486 ns      1440616 t/i=3.79756ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/256           1013 ns         1013 ns       683716 t/i=3.95719ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/512           2090 ns         2090 ns       335399 t/i=4.08226ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/1024          4382 ns         4382 ns       157870 t/i=4.27957ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/2048          8885 ns         8884 ns        79538 t/i=4.338ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/4096         17717 ns        17716 ns        39341 t/i=4.32532ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/8192         35759 ns        35756 ns        19762 t/i=4.3648ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/16384        71429 ns        71427 ns         9751 t/i=4.35953ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/32768       142846 ns       142839 ns         4834 t/i=4.3591ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/65536       299432 ns       299430 ns         2362 t/i=4.56894ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/131072      607360 ns       607356 ns         1166 t/i=4.63376ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/262144     1295102 ns      1295083 ns          546 t/i=4.94035ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/524288     3337828 ns      3337793 ns          209 t/i=6.36634ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/1048576    8387334 ns      8387005 ns           81 t/i=7.99847ns
bench_append&lt;Container&gt;/1                       13.3 ns         13.3 ns     52114703 t/i=13.3287ns
bench_append&lt;Container&gt;/2                       23.5 ns         23.5 ns     29020259 t/i=11.771ns
bench_append&lt;Container&gt;/4                       48.9 ns         48.9 ns     14313064 t/i=12.2231ns
bench_append&lt;Container&gt;/8                       91.1 ns         91.0 ns      7690246 t/i=11.3811ns
bench_append&lt;Container&gt;/16                       161 ns          161 ns      4413230 t/i=10.0588ns
bench_append&lt;Container&gt;/32                       312 ns          312 ns      2244326 t/i=9.75056ns
bench_append&lt;Container&gt;/64                       583 ns          583 ns      1185685 t/i=9.11584ns
bench_append&lt;Container&gt;/128                     1123 ns         1122 ns       626614 t/i=8.76911ns
bench_append&lt;Container&gt;/256                     2197 ns         2197 ns       320017 t/i=8.58337ns
bench_append&lt;Container&gt;/512                     4319 ns         4319 ns       160779 t/i=8.43519ns
bench_append&lt;Container&gt;/1024                    8622 ns         8622 ns        79252 t/i=8.41967ns
bench_append&lt;Container&gt;/2048                   17239 ns        17239 ns        39849 t/i=8.41726ns
bench_append&lt;Container&gt;/4096                   34502 ns        34501 ns        20299 t/i=8.42297ns
bench_append&lt;Container&gt;/8192                   68731 ns        68728 ns        10327 t/i=8.38964ns
bench_append&lt;Container&gt;/16384                 137217 ns       137213 ns         5064 t/i=8.3748ns
bench_append&lt;Container&gt;/32768                 273730 ns       273710 ns         2518 t/i=8.35297ns
bench_append&lt;Container&gt;/65536                 550779 ns       550759 ns         1253 t/i=8.40392ns
bench_append&lt;Container&gt;/131072               1104636 ns      1104602 ns          632 t/i=8.42744ns
bench_append&lt;Container&gt;/262144               2192175 ns      2192081 ns          319 t/i=8.36213ns
bench_append&lt;Container&gt;/524288               4395209 ns      4395025 ns          160 t/i=8.38284ns
bench_append&lt;Container&gt;/1048576              9153992 ns      9153748 ns           77 t/i=8.72969ns
```
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;benchmark output after&lt;/summary&gt;

```
2026-07-16T16:55:54+03:00
Running ./string_append
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.26, 0.35, 0.17
----------------------------------------------------------------------------------------------------
Benchmark                                          Time             CPU   Iterations UserCounters...
----------------------------------------------------------------------------------------------------
bench_append&lt;std::vector&lt;char&gt;&gt;/1               3.09 ns         3.09 ns    227887283 t/i=3.0944ns
bench_append&lt;std::vector&lt;char&gt;&gt;/2               3.10 ns         3.09 ns    226039491 t/i=1.54692ns
bench_append&lt;std::vector&lt;char&gt;&gt;/4               3.10 ns         3.10 ns    225739021 t/i=774.206ps
bench_append&lt;std::vector&lt;char&gt;&gt;/8               3.10 ns         3.09 ns    224468619 t/i=386.875ps
bench_append&lt;std::vector&lt;char&gt;&gt;/16              2.91 ns         2.91 ns    240031986 t/i=182.18ps
bench_append&lt;std::vector&lt;char&gt;&gt;/32              10.9 ns         10.9 ns     65077293 t/i=339.745ps
bench_append&lt;std::vector&lt;char&gt;&gt;/64              10.9 ns         10.9 ns     63585149 t/i=170.175ps
bench_append&lt;std::vector&lt;char&gt;&gt;/128             11.1 ns         11.1 ns     63889890 t/i=86.7411ps
bench_append&lt;std::vector&lt;char&gt;&gt;/256             11.0 ns         11.0 ns     62736325 t/i=43.117ps
bench_append&lt;std::vector&lt;char&gt;&gt;/512             13.1 ns         13.1 ns     53444253 t/i=25.5178ps
bench_append&lt;std::vector&lt;char&gt;&gt;/1024            14.9 ns         14.9 ns     46805617 t/i=14.5771ps
bench_append&lt;std::vector&lt;char&gt;&gt;/2048            37.6 ns         37.6 ns     18720091 t/i=18.3699ps
bench_append&lt;std::vector&lt;char&gt;&gt;/4096            47.6 ns         47.6 ns     14738139 t/i=11.6281ps
bench_append&lt;std::vector&lt;char&gt;&gt;/8192            57.3 ns         57.3 ns     12354002 t/i=6.99105ps
bench_append&lt;std::vector&lt;char&gt;&gt;/16384            105 ns          105 ns      6534679 t/i=6.4209ps
bench_append&lt;std::vector&lt;char&gt;&gt;/32768            520 ns          520 ns      1368420 t/i=15.8662ps
bench_append&lt;std::vector&lt;char&gt;&gt;/65536           1043 ns         1043 ns       665359 t/i=15.9218ps
bench_append&lt;std::vector&lt;char&gt;&gt;/131072          2074 ns         2073 ns       339638 t/i=15.8193ps
bench_append&lt;std::vector&lt;char&gt;&gt;/262144          4144 ns         4144 ns       160746 t/i=15.8074ps
bench_append&lt;std::vector&lt;char&gt;&gt;/524288          8200 ns         8200 ns        80639 t/i=15.6401ps
bench_append&lt;std::vector&lt;char&gt;&gt;/1048576        21893 ns        21892 ns        30043 t/i=20.8781ps
bench_append&lt;std::list&lt;char&gt;&gt;/1                 3.55 ns         3.55 ns    199315153 t/i=3.5478ns
bench_append&lt;std::list&lt;char&gt;&gt;/2                 5.29 ns         5.29 ns    134176216 t/i=2.64552ns
bench_append&lt;std::list&lt;char&gt;&gt;/4                 5.49 ns         5.49 ns    123464411 t/i=1.37183ns
bench_append&lt;std::list&lt;char&gt;&gt;/8                 8.85 ns         8.85 ns     80565431 t/i=1.10657ns
bench_append&lt;std::list&lt;char&gt;&gt;/16                12.5 ns         12.5 ns     55630347 t/i=783.169ps
bench_append&lt;std::list&lt;char&gt;&gt;/32                39.6 ns         39.6 ns     17583818 t/i=1.23641ns
bench_append&lt;std::list&lt;char&gt;&gt;/64                89.0 ns         89.0 ns      8200063 t/i=1.39032ns
bench_append&lt;std::list&lt;char&gt;&gt;/128                204 ns          204 ns      3371002 t/i=1.59479ns
bench_append&lt;std::list&lt;char&gt;&gt;/256                546 ns          546 ns      1256033 t/i=2.13231ns
bench_append&lt;std::list&lt;char&gt;&gt;/512               1065 ns         1065 ns       648641 t/i=2.08083ns
bench_append&lt;std::list&lt;char&gt;&gt;/1024              2102 ns         2102 ns       334077 t/i=2.05267ns
bench_append&lt;std::list&lt;char&gt;&gt;/2048              4249 ns         4249 ns       164325 t/i=2.07457ns
bench_append&lt;std::list&lt;char&gt;&gt;/4096              8433 ns         8433 ns        84268 t/i=2.05886ns
bench_append&lt;std::list&lt;char&gt;&gt;/8192             16685 ns        16685 ns        41640 t/i=2.03673ns
bench_append&lt;std::list&lt;char&gt;&gt;/16384            33543 ns        33543 ns        21102 t/i=2.04728ns
bench_append&lt;std::list&lt;char&gt;&gt;/32768            67025 ns        67022 ns        10656 t/i=2.04534ns
bench_append&lt;std::list&lt;char&gt;&gt;/65536           135122 ns       135120 ns         5220 t/i=2.06176ns
bench_append&lt;std::list&lt;char&gt;&gt;/131072          274342 ns       274328 ns         2542 t/i=2.09296ns
bench_append&lt;std::list&lt;char&gt;&gt;/262144          591436 ns       591421 ns         1181 t/i=2.25609ns
bench_append&lt;std::list&lt;char&gt;&gt;/524288         1389493 ns      1388228 ns          523 t/i=2.64784ns
bench_append&lt;std::list&lt;char&gt;&gt;/1048576        4536406 ns      4536297 ns          153 t/i=4.32615ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/1             3.87 ns         3.87 ns    181230524 t/i=3.87176ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/2             9.51 ns         9.51 ns     71901138 t/i=4.75509ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/4             13.2 ns         13.2 ns     52352810 t/i=3.29722ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/8             21.7 ns         21.7 ns     32439441 t/i=2.71142ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/16            40.0 ns         40.0 ns     17563514 t/i=2.49991ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/32            96.8 ns         96.8 ns      7208224 t/i=3.0244ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/64             225 ns          225 ns      3091425 t/i=3.5148ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/128            488 ns          488 ns      1443983 t/i=3.81319ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/256           1014 ns         1014 ns       700744 t/i=3.96008ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/512           2087 ns         2087 ns       333876 t/i=4.07604ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/1024          4405 ns         4404 ns       159590 t/i=4.30126ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/2048          8904 ns         8904 ns        79107 t/i=4.34771ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/4096         17856 ns        17855 ns        39341 t/i=4.35914ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/8192         35414 ns        35413 ns        19531 t/i=4.32291ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/16384        71063 ns        70984 ns         9626 t/i=4.33251ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/32768       145779 ns       145774 ns         4834 t/i=4.44867ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/65536       304988 ns       304974 ns         2357 t/i=4.65353ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/131072      604942 ns       602719 ns         1128 t/i=4.59838ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/262144     1267769 ns      1267755 ns          560 t/i=4.8361ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/524288     3324835 ns      3324630 ns          213 t/i=6.34123ns
bench_append&lt;std::multiset&lt;char&gt;&gt;/1048576    8387543 ns      8387384 ns           82 t/i=7.99883ns
bench_append&lt;Container&gt;/1                       10.4 ns         10.4 ns     66804092 t/i=10.3965ns
bench_append&lt;Container&gt;/2                       16.8 ns         16.8 ns     41695556 t/i=8.39998ns
bench_append&lt;Container&gt;/4                       34.5 ns         34.5 ns     20452545 t/i=8.62682ns
bench_append&lt;Container&gt;/8                       62.9 ns         62.9 ns     11109242 t/i=7.86804ns
bench_append&lt;Container&gt;/16                       111 ns          111 ns      6422949 t/i=6.91392ns
bench_append&lt;Container&gt;/32                       216 ns          216 ns      3276646 t/i=6.76101ns
bench_append&lt;Container&gt;/64                       400 ns          400 ns      1745608 t/i=6.2475ns
bench_append&lt;Container&gt;/128                      765 ns          765 ns       928239 t/i=5.97368ns
bench_append&lt;Container&gt;/256                     1489 ns         1489 ns       468787 t/i=5.8147ns
bench_append&lt;Container&gt;/512                     2942 ns         2942 ns       238431 t/i=5.74671ns
bench_append&lt;Container&gt;/1024                    5837 ns         5837 ns       121633 t/i=5.69982ns
bench_append&lt;Container&gt;/2048                   11727 ns        11726 ns        60224 t/i=5.72563ns
bench_append&lt;Container&gt;/4096                   23304 ns        23303 ns        30075 t/i=5.68928ns
bench_append&lt;Container&gt;/8192                   46442 ns        46440 ns        15129 t/i=5.66892ns
bench_append&lt;Container&gt;/16384                  92749 ns        92748 ns         7607 t/i=5.66086ns
bench_append&lt;Container&gt;/32768                 185188 ns       185181 ns         3759 t/i=5.65128ns
bench_append&lt;Container&gt;/65536                 371024 ns       371014 ns         1888 t/i=5.66122ns
bench_append&lt;Container&gt;/131072                739596 ns       739562 ns          958 t/i=5.64241ns
bench_append&lt;Container&gt;/262144               1487139 ns      1487121 ns          477 t/i=5.67292ns
bench_append&lt;Container&gt;/524288               2963851 ns      2963776 ns          237 t/i=5.65295ns
bench_append&lt;Container&gt;/1048576              6313121 ns      6313038 ns          112 t/i=6.02058ns
```
&lt;/details&gt;

The "elapsed time" on the graph (`t/i` in benchmark output, time per item) is elapsed time for the append operation divided by the number of elements/characters appended.

`Container` is basically a `std::vector&lt;char&gt;` with wrapped iterators with increment operation slowed down by a call to `rand()`.

Otherwise I could not measure a significant difference for `std::list&lt;char&gt;` or `std::multiset&lt;char&gt;`, those can be seen as reference and control values.

To benchmark it I used Clang 22.1.2 on Ubuntu 26.04 inside WSL2.

---
Full diff: https://github.com/llvm/llvm-project/pull/210078.diff


1 Files Affected:

- (modified) libcxx/include/string (+4-4) 


``````````diff
diff --git a/libcxx/include/string b/libcxx/include/string
index a297a05813cee..957eb15e4fe1f 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1359,13 +1359,13 @@ public:
   template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
   append(_ForwardIterator __first, _ForwardIterator __last) {
-    size_type __sz  = size();
-    size_type __cap = capacity();
-    size_type __n   = static_cast<size_type>(std::distance(__first, __last));
-    if (__n == 0)
+    if (__first == __last)
       return *this;
 
     if (__string_is_trivial_iterator_v<_ForwardIterator> && !__addr_in_range(*__first)) {
+      size_type __sz  = size();
+      size_type __cap = capacity();
+      size_type __n   = static_cast<size_type>(std::distance(__first, __last));
       if (__cap - __sz < __n)
         __grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
       __annotate_increase(__n);

``````````

</details>


https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Thu Jul 16 08:34:01 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 16 Jul 2026 08:34:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a58f9e9.92ea0e2a.9a5e7.6f2d@mx.google.com>


philnik777 wrote:

Given that the only case where you see a difference is in a completely artificial and unrealistic scenario I'm not convinced we want to take this. Do you have anything that's more likely to actually exist where this makes a difference?

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Thu Jul 16 08:42:06 2026
From: libcxx-commits at lists.llvm.org (Nico Weber via libcxx-commits)
Date: Thu, 16 Jul 2026 08:42:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some unnecessary
 functions from __vector_layout (PR #207152)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207152@github.com>
Message-ID: <6a58fbce.d76d81af.39f3d0.e9fe@mx.google.com>


================
@@ -400,7 +400,7 @@ class vector {
     return __layout_.__capacity();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
-    return __layout_.__empty();
+    return size() == 0;
----------------
nico wrote:

`size()` has to do arithmetic on the pointers, which requires complete types. The LHS, and vector before _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED, didn't require complete types to call `empty()`.

Compete types are only needed if `_LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED` is not defined, which means it is now ABI-dependent if a type has to be complete.

This also means *not* enabling `_LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED` stops building for us for that reason. (And building with it causes a big size regression that we're currently investigating.)

As @ldionne says in https://github.com/llvm/llvm-project/pull/155330#pullrequestreview-4612029839, `_LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED` seems like a risky change. Maybe we shouldn't make it harder to turn it off.

Could we maybe undo this change here to make it easier to compare behavior with the new layout on and off?

https://github.com/llvm/llvm-project/pull/207152

From libcxx-commits at lists.llvm.org  Thu Jul 16 09:06:39 2026
From: libcxx-commits at lists.llvm.org (Zibi Sarbinowski via libcxx-commits)
Date: Thu, 16 Jul 2026 09:06:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Prevent cycle from including
 __mbstate_t.h within wchar.h (PR #200940)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/200940@github.com>
Message-ID: <6a59018f.481afadf.34be4b.9d09@mx.google.com>


zibi2 wrote:

FYI, I'm trying to remove both files, `__std_mbstate_t.h` and `__mbstate_t.h` but II'm getting into some build issue:

```
lvm-project/libcxx/src/random.cpp:18:
In file included from .../include/c++/v1/random:1684:
In file included from .../include/c++/v1/__random/bernoulli_distribution.h:14:
In file included from .../include/c++/v1/__random/uniform_real_distribution.h:15:
.../include/c++/v1/iosfwd:135:14: error: use of undeclared identifier 'mbstate_t'
  135 | typedef fpos<mbstate_t> streampos;
      |              ^~~~~~~~~
```

Next I will try to remove just `__mbstate_t.h`.

https://github.com/llvm/llvm-project/pull/200940

From libcxx-commits at lists.llvm.org  Thu Jul 16 09:29:16 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 16 Jul 2026 09:29:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][math] Add `constexpr` to
 `std::isgreater()` (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5906dc.d1496011.289bf0.50d5@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Thu Jul 16 09:32:02 2026
From: libcxx-commits at lists.llvm.org (Pavel Novikov via libcxx-commits)
Date: Thu, 16 Jul 2026 09:32:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a590782.7d702ed2.32eb67.05d4@mx.google.com>


toughengineer wrote:

@philnik777, it's a trivial rearrangement of lines so the variables are initialized only in the scope  where they are actually used.

I can't immediately provide you with a "realistic" scenario.

Is there anything else that can convince you that you want to take this?

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Thu Jul 16 09:48:48 2026
From: libcxx-commits at lists.llvm.org (Pavel Novikov via libcxx-commits)
Date: Thu, 16 Jul 2026 09:48:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a590b70.92ea0e2a.9a5e7.1d09@mx.google.com>


toughengineer wrote:

Of course as soon as I answered, I came up with several "realistic" examples:
- iterators into some kind of file with such a format that it does not allow to have random access iterators,
- iterators into a compressed stream,
- iterators into a range with arbitrary processing (e.g. filtering, transformation, generation) can be arbitrarily expensive to iterate over (this is actually a generalization of the previous two).

@philnik777, please let me know if you are still not convinced.

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Thu Jul 16 09:56:59 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 16 Jul 2026 09:56:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Compose the test suite Lit site
 config from harness and installation substitutions (PR #209638)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209638@github.com>
Message-ID: <6a590d5b.d681cab9.120c5c.9c34@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/209638

>From 7a9669daf99eab79efedaa551de4742cab2357e7 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 14 Jul 2026 15:44:29 -0400
Subject: [PATCH] [libc++] Compose the test suite Lit site config from harness
 and installation substitutions

This patch restructures how libc++'s Lit site config is generated. Instead
of relying on the user-selected configuration file to include the CMake
bridge, it composes a site configuration from multiple independent bits.

Importantly, it splits substitutions that pertain to the harness setup
(e.g. where to find Python) and substitutions that pertain to libc++
itself (e.g. the path to libc++ headers). This split and the top-level
composition of a site config file are incremental steps towards decoupling
the test suite from libc++'s own build.
---
 libcxx/test/CMakeLists.txt                    | 23 +++++++++++++++----
 .../test/configs/amdgpu-libc++-shared.cfg.in  |  2 --
 .../test/configs/apple-libc++-shared.cfg.in   |  2 --
 .../test/configs/apple-libc++-system.cfg.in   |  2 --
 .../configs/armv7m-picolibc-libc++.cfg.in     |  2 --
 ...ge.cfg.in => harness-configuration.cfg.in} | 15 ++++--------
 libcxx/test/configs/ibm-libc++-shared.cfg.in  |  2 --
 .../test/configs/libcxx-substitutions.cfg.in  | 13 +++++++++++
 libcxx/test/configs/lit.site.cfg.in           | 11 +++++++++
 .../test/configs/llvm-libc++-android.cfg.in   |  2 --
 .../test/configs/llvm-libc++-llvm-libc.cfg.in |  2 --
 libcxx/test/configs/llvm-libc++-mingw.cfg.in  |  2 --
 .../configs/llvm-libc++-shared-clangcl.cfg.in |  2 --
 .../configs/llvm-libc++-shared-gcc.cfg.in     |  2 --
 ...-libc++-shared-no-vcruntime-clangcl.cfg.in |  2 --
 libcxx/test/configs/llvm-libc++-shared.cfg.in |  2 --
 .../configs/llvm-libc++-static-clangcl.cfg.in |  2 --
 libcxx/test/configs/llvm-libc++-static.cfg.in |  2 --
 .../test/configs/nvptx-libc++-shared.cfg.in   |  2 --
 libcxx/test/configs/stdlib-libstdc++.cfg.in   |  2 --
 libcxx/test/configs/stdlib-native.cfg.in      |  2 --
 21 files changed, 47 insertions(+), 49 deletions(-)
 rename libcxx/test/configs/{cmake-bridge.cfg.in => harness-configuration.cfg.in} (52%)
 create mode 100644 libcxx/test/configs/libcxx-substitutions.cfg.in
 create mode 100644 libcxx/test/configs/lit.site.cfg.in

diff --git a/libcxx/test/CMakeLists.txt b/libcxx/test/CMakeLists.txt
index 39d383922e1df..8fd34c086a992 100644
--- a/libcxx/test/CMakeLists.txt
+++ b/libcxx/test/CMakeLists.txt
@@ -55,12 +55,27 @@ serialize_lit_params_list(SERIALIZED_LIT_PARAMS LIBCXX_TEST_PARAMS)
 
 include(AddLLVM) # for configure_lit_site_cfg and add_lit_testsuite
 
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/configs/cmake-bridge.cfg.in"
-                "${CMAKE_CURRENT_BINARY_DIR}/cmake-bridge.cfg"
-                @ONLY)
+# The test suite is driven by a generated lit.site.cfg that loads the various other configuration
+# files needed by it. In order:
+# - The harness configuration, which defines substitutions needed for the test suite's own function
+#   (e.g. the path to Python).
+# - The libc++ substitutions file, which adds libc++ specific substitutions, such as the location of
+#   include paths. Those are needed for libc++ specific tests.
+# - The user-selected site configuration, which defines substitutions that determine e.g. how to
+#   build, link and run the tests.
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/configs/harness-configuration.cfg.in"
+               "${CMAKE_CURRENT_BINARY_DIR}/harness-configuration.cfg"
+               @ONLY)
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/configs/libcxx-substitutions.cfg.in"
+               "${CMAKE_CURRENT_BINARY_DIR}/libcxx-substitutions.cfg"
+               @ONLY)
+string(REGEX REPLACE "\\.in$" "" _resolved_config_name "${LIBCXX_TEST_CONFIG}")
+get_filename_component(_resolved_config_name "${_resolved_config_name}" NAME)
+set(LIBCXX_RESOLVED_TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/${_resolved_config_name}")
+configure_file("${LIBCXX_TEST_CONFIG}" "${LIBCXX_RESOLVED_TEST_CONFIG}" @ONLY)
 
 configure_lit_site_cfg(
-  "${LIBCXX_TEST_CONFIG}"
+  "${CMAKE_CURRENT_SOURCE_DIR}/configs/lit.site.cfg.in"
   ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
   MAIN_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py")
 
diff --git a/libcxx/test/configs/amdgpu-libc++-shared.cfg.in b/libcxx/test/configs/amdgpu-libc++-shared.cfg.in
index 80c80ebaa8bd0..b34eced0bf1e0 100644
--- a/libcxx/test/configs/amdgpu-libc++-shared.cfg.in
+++ b/libcxx/test/configs/amdgpu-libc++-shared.cfg.in
@@ -1,5 +1,3 @@
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
   f'--target={config.target_triple} -Wno-multi-gpu -flto -mcpu=native'))
 config.substitutions.append(('%{compile_flags}',
diff --git a/libcxx/test/configs/apple-libc++-shared.cfg.in b/libcxx/test/configs/apple-libc++-shared.cfg.in
index a361b2b8adc00..e6d61dffbbd97 100644
--- a/libcxx/test/configs/apple-libc++-shared.cfg.in
+++ b/libcxx/test/configs/apple-libc++-shared.cfg.in
@@ -14,8 +14,6 @@
 # issues since the upstream libc++ does not contain all the symbols provided by
 # the system library.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config, libcxx.test.dsl
diff --git a/libcxx/test/configs/apple-libc++-system.cfg.in b/libcxx/test/configs/apple-libc++-system.cfg.in
index e87f920e7b93d..3bc9fe8956f14 100644
--- a/libcxx/test/configs/apple-libc++-system.cfg.in
+++ b/libcxx/test/configs/apple-libc++-system.cfg.in
@@ -7,8 +7,6 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config, libcxx.test.dsl
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
     '-isysroot {}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else ''
 ))
diff --git a/libcxx/test/configs/armv7m-picolibc-libc++.cfg.in b/libcxx/test/configs/armv7m-picolibc-libc++.cfg.in
index f0782c28ce3e5..181263e95d62b 100644
--- a/libcxx/test/configs/armv7m-picolibc-libc++.cfg.in
+++ b/libcxx/test/configs/armv7m-picolibc-libc++.cfg.in
@@ -1,5 +1,3 @@
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 libc_linker_script = '@CMAKE_INSTALL_PREFIX@/lib/picolibcpp.ld'
 
 config.substitutions.append(('%{flags}', '--sysroot=@CMAKE_INSTALL_PREFIX@'))
diff --git a/libcxx/test/configs/cmake-bridge.cfg.in b/libcxx/test/configs/harness-configuration.cfg.in
similarity index 52%
rename from libcxx/test/configs/cmake-bridge.cfg.in
rename to libcxx/test/configs/harness-configuration.cfg.in
index 20b7c1e9bc357..c527d60c64af5 100644
--- a/libcxx/test/configs/cmake-bridge.cfg.in
+++ b/libcxx/test/configs/harness-configuration.cfg.in
@@ -4,11 +4,11 @@
 
 #
 # This file performs the bridge between the CMake configuration and the Lit
-# configuration files by setting up the LitConfig object and various Lit
-# substitutions from CMake variables.
+# configuration files by setting up the LitConfig object and the Lit substitutions
+# that are independent of the library under test.
 #
-# Individual configuration files can take advantage of this bridge by
-# loading the file and then setting up the remaining Lit substitutions.
+# It is loaded by the generated lit.site.cfg, before the library-under-test
+# substitutions (libcxx-substitutions.cfg) and the site configuration file.
 #
 
 import os, site
@@ -25,13 +25,6 @@ config.recursiveExpansionLimit = 10
 config.test_exec_root = os.path.join('@LIBCXX_BINARY_DIR@', 'test')
 
 # Add substitutions for bootstrapping the test suite configuration
-config.substitutions.append(('%{bin-dir}', '@LIBCXX_BINARY_DIR@'))
 config.substitutions.append(('%{libcxx-dir}', '@LIBCXX_SOURCE_DIR@'))
-config.substitutions.append(('%{install-prefix}', '@LIBCXX_TESTING_INSTALL_PREFIX@'))
-config.substitutions.append(('%{include-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_INCLUDE_DIR@'))
-config.substitutions.append(('%{target-include-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_INCLUDE_TARGET_DIR@'))
-config.substitutions.append(('%{lib-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_LIBRARY_DIR@'))
-config.substitutions.append(('%{module-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_MODULES_DIR@'))
-config.substitutions.append(('%{test-tools-dir}', '@LIBCXX_TEST_TOOLS_PATH@'))
 config.substitutions.append(('%{benchmark_flags}', '-I @LIBCXX_BINARY_DIR@/test/benchmarks/google-benchmark/include -L @LIBCXX_BINARY_DIR@/test/benchmarks/google-benchmark/lib -L @LIBCXX_BINARY_DIR@/test/benchmarks/google-benchmark/lib64 -l benchmark'))
 config.substitutions.append(("%{python}", shlex.quote(sys.executable)))
diff --git a/libcxx/test/configs/ibm-libc++-shared.cfg.in b/libcxx/test/configs/ibm-libc++-shared.cfg.in
index 863b1ae85b0ed..3fcf64e3537e0 100644
--- a/libcxx/test/configs/ibm-libc++-shared.cfg.in
+++ b/libcxx/test/configs/ibm-libc++-shared.cfg.in
@@ -2,8 +2,6 @@
 # AIX using a shared library.
 #
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 import lit.util
 if lit.util.isAIXTriple(config.target_triple):
   # Add the AIX version to the triple here because there currently isn't a good
diff --git a/libcxx/test/configs/libcxx-substitutions.cfg.in b/libcxx/test/configs/libcxx-substitutions.cfg.in
new file mode 100644
index 0000000000000..a74ff013219b2
--- /dev/null
+++ b/libcxx/test/configs/libcxx-substitutions.cfg.in
@@ -0,0 +1,13 @@
+ at AUTO_GEN_COMMENT@
+
+#
+# This file sets up Lit substitutions that describe paths related to libc++'s installation, which
+# are used by libc++ specific tests.
+#
+config.substitutions.append(('%{bin-dir}', '@LIBCXX_BINARY_DIR@'))
+config.substitutions.append(('%{install-prefix}', '@LIBCXX_TESTING_INSTALL_PREFIX@'))
+config.substitutions.append(('%{include-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_INCLUDE_DIR@'))
+config.substitutions.append(('%{target-include-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_INCLUDE_TARGET_DIR@'))
+config.substitutions.append(('%{lib-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_LIBRARY_DIR@'))
+config.substitutions.append(('%{module-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_MODULES_DIR@'))
+config.substitutions.append(('%{test-tools-dir}', '@LIBCXX_TEST_TOOLS_PATH@'))
diff --git a/libcxx/test/configs/lit.site.cfg.in b/libcxx/test/configs/lit.site.cfg.in
new file mode 100644
index 0000000000000..148bf5534453b
--- /dev/null
+++ b/libcxx/test/configs/lit.site.cfg.in
@@ -0,0 +1,11 @@
+ at AUTO_GEN_COMMENT@
+
+#
+# This is the top-level Lit site configuration. It loads, in order:
+#   1. the harness configuration,
+#   2. the substitutions that are specific to libc++, and
+#   3. the chosen site configuration file, which describes how to build, link and run the tests.
+#
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/harness-configuration.cfg')
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/libcxx-substitutions.cfg')
+lit_config.load_config(config, '@LIBCXX_RESOLVED_TEST_CONFIG@')
diff --git a/libcxx/test/configs/llvm-libc++-android.cfg.in b/libcxx/test/configs/llvm-libc++-android.cfg.in
index 96c952d6bbc19..8b149207d61d4 100644
--- a/libcxx/test/configs/llvm-libc++-android.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-android.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's
 # libc++ using adb on Android.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 import re
 import site
 
diff --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index cae8a8007214e..883fa82132227 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's libc++
 # using a static library.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
     '-pthread' + (' -isysroot {}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else '')
 ))
diff --git a/libcxx/test/configs/llvm-libc++-mingw.cfg.in b/libcxx/test/configs/llvm-libc++-mingw.cfg.in
index 44731713b427b..f96d4b12c918c 100644
--- a/libcxx/test/configs/llvm-libc++-mingw.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-mingw.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's libc++
 # using either a DLL or a static library, with MinGW/Clang on Windows.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}', ''))
 config.substitutions.append(('%{compile_flags}',
     '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
diff --git a/libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in b/libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in
index e6186a7c73c3a..5196ca32479ac 100644
--- a/libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's libc++
 # using a DLL, with Clang-cl on Windows.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 dbg_include = ''
 runtime_library = '@CMAKE_MSVC_RUNTIME_LIBRARY@'
 if runtime_library == '' or runtime_library.endswith('DLL'):
diff --git a/libcxx/test/configs/llvm-libc++-shared-gcc.cfg.in b/libcxx/test/configs/llvm-libc++-shared-gcc.cfg.in
index 1b4ddc299759e..ad31e2811a8ab 100644
--- a/libcxx/test/configs/llvm-libc++-shared-gcc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-shared-gcc.cfg.in
@@ -2,8 +2,6 @@
 # using a shared library, with GCC. This is done differently from Clang because
 # we need to explicitly link against libm for some tests with GCC.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}', '-pthread'))
 config.substitutions.append(('%{compile_flags}',
     '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'
diff --git a/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in b/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in
index 476c5ca715a65..ce142b1a9d644 100644
--- a/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in
@@ -2,8 +2,6 @@
 # using a DLL, with Clang-cl on Windows. This variant sets _HAS_EXCEPTIONS = 0
 # which removes exception class definitions from the vcruntime.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 dbg_include = ''
 runtime_library = '@CMAKE_MSVC_RUNTIME_LIBRARY@'
 if runtime_library == '' or runtime_library.endswith('DLL'):
diff --git a/libcxx/test/configs/llvm-libc++-shared.cfg.in b/libcxx/test/configs/llvm-libc++-shared.cfg.in
index 6d4a3836b2baa..71e56e732e796 100644
--- a/libcxx/test/configs/llvm-libc++-shared.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-shared.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's libc++
 # using a shared library.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
     '-pthread' + (' -isysroot {}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else '')
 ))
diff --git a/libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in b/libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in
index 2f2b420d32190..4be1e1afa1c5b 100644
--- a/libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's libc++
 # using a static library, with Clang-cl on Windows.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 dbg_include = ''
 runtime_library = '@CMAKE_MSVC_RUNTIME_LIBRARY@'
 if runtime_library == '' or runtime_library.endswith('DLL'):
diff --git a/libcxx/test/configs/llvm-libc++-static.cfg.in b/libcxx/test/configs/llvm-libc++-static.cfg.in
index 45a44c13e76eb..c6a6ab2398079 100644
--- a/libcxx/test/configs/llvm-libc++-static.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-static.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's libc++
 # using a static library.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
     '-pthread' + (' -isysroot {}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else '')
 ))
diff --git a/libcxx/test/configs/nvptx-libc++-shared.cfg.in b/libcxx/test/configs/nvptx-libc++-shared.cfg.in
index e07ed35da4d5a..c8dcac55e3596 100644
--- a/libcxx/test/configs/nvptx-libc++-shared.cfg.in
+++ b/libcxx/test/configs/nvptx-libc++-shared.cfg.in
@@ -1,5 +1,3 @@
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
   f'--target={config.target_triple} -Wno-multi-gpu -flto -march=native'))
 config.substitutions.append(('%{compile_flags}',
diff --git a/libcxx/test/configs/stdlib-libstdc++.cfg.in b/libcxx/test/configs/stdlib-libstdc++.cfg.in
index 1a4b47cb1335b..429b1fe153938 100644
--- a/libcxx/test/configs/stdlib-libstdc++.cfg.in
+++ b/libcxx/test/configs/stdlib-libstdc++.cfg.in
@@ -13,8 +13,6 @@
 #                                                          --param stdlib=libstdc++
 #
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config, libcxx.test.dsl
diff --git a/libcxx/test/configs/stdlib-native.cfg.in b/libcxx/test/configs/stdlib-native.cfg.in
index b827155fd579a..3f64e37cdbcfa 100644
--- a/libcxx/test/configs/stdlib-native.cfg.in
+++ b/libcxx/test/configs/stdlib-native.cfg.in
@@ -4,8 +4,6 @@
 # default when no special compiler flags are provided.
 #
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
     '-pthread' + (' -isysroot {}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else '')
 ))


From libcxx-commits at lists.llvm.org  Thu Jul 16 10:06:00 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 16 Jul 2026 10:06:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmark for
	std::nth_element (PR #210112)
Message-ID: <llvm.org/llvm/llvm-project/pull/210112@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/210112

None

>From 987d7580b24a3faeac51654dff0527756ecf9ad6 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 14:18:59 -0400
Subject: [PATCH] [libc++] Add benchmark for std::nth_element

---
 .../algorithms/sorting/nth_element.bench.cpp  | 91 +++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100644 libcxx/test/benchmarks/algorithms/sorting/nth_element.bench.cpp

diff --git a/libcxx/test/benchmarks/algorithms/sorting/nth_element.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/nth_element.bench.cpp
new file mode 100644
index 0000000000000..4c3e8115def30
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/nth_element.bench.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "common.h"
+
+int main(int argc, char** argv) {
+  auto std_nth_element = [](auto first, auto nth, auto last) { return std::nth_element(first, nth, last); };
+
+  // Benchmark std::nth_element on various types of data. We always partition
+  // around the middle element of the full range. We don't benchmark
+  // std::ranges::nth_element separately because it forwards to the same core.
+  //
+  // We run this in batches because nth_element mutates the range in place, so we
+  // restore the original data after each batch.
+  //
+  // We intentionally don't benchmark the predicated version of the algorithm
+  // because that makes the benchmark run too slowly.
+  {
+    auto bm = []<class Container>(std::string name, auto nth_element, auto generate_data) {
+      benchmark::RegisterBenchmark(
+          name,
+          [nth_element, generate_data](auto& st) {
+            std::size_t const size          = st.range(0);
+            constexpr std::size_t BatchSize = 32;
+            using ValueType                 = typename Container::value_type;
+            std::vector<ValueType> data     = generate_data(size);
+            std::array<Container, BatchSize> c;
+            std::fill_n(c.begin(), BatchSize, Container(data.begin(), data.end()));
+
+            std::size_t const half = size / 2;
+            while (st.KeepRunningBatch(BatchSize)) {
+              for (std::size_t i = 0; i != BatchSize; ++i) {
+                benchmark::DoNotOptimize(c[i]);
+                nth_element(c[i].begin(), c[i].begin() + half, c[i].end());
+                benchmark::DoNotOptimize(c[i]);
+              }
+
+              st.PauseTiming();
+              for (std::size_t i = 0; i != BatchSize; ++i) {
+                std::copy(data.begin(), data.end(), c[i].begin());
+              }
+              st.ResumeTiming();
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+
+    auto register_bm = [&](auto generate, std::string variant) {
+      auto gen2 = [generate](auto size) {
+        std::vector<int> data = generate(size);
+        std::vector<support::NonIntegral> real_data(data.begin(), data.end());
+        return real_data;
+      };
+      auto name = [variant](std::string op) { return op + " (" + variant + ")"; };
+      bm.operator()<std::vector<int>>(name("std::nth_element(vector<int>)"), std_nth_element, generate);
+      bm.operator()<std::vector<support::NonIntegral>>(
+          name("std::nth_element(vector<NonIntegral>)"), std_nth_element, gen2);
+      bm.operator()<std::deque<int>>(name("std::nth_element(deque<int>)"), std_nth_element, generate);
+    };
+
+    register_bm(support::quicksort_adversarial_data<int>, "qsort adversarial");
+    register_bm(support::ascending_sorted_data<int>, "ascending");
+    register_bm(support::descending_sorted_data<int>, "descending");
+    register_bm(support::pipe_organ_data<int>, "pipe-organ");
+    register_bm(support::heap_data<int>, "heap");
+    register_bm(support::shuffled_data<int>, "shuffled");
+    register_bm(support::single_element_data<int>, "repeated");
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Thu Jul 16 10:06:43 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 16 Jul 2026 10:06:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmark for
	std::nth_element (PR #210112)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210112@github.com>
Message-ID: <6a590fa3.27b3b003.145e07.62f1@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/210112.diff


1 Files Affected:

- (added) libcxx/test/benchmarks/algorithms/sorting/nth_element.bench.cpp (+91) 


``````````diff
diff --git a/libcxx/test/benchmarks/algorithms/sorting/nth_element.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/nth_element.bench.cpp
new file mode 100644
index 0000000000000..4c3e8115def30
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/nth_element.bench.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "common.h"
+
+int main(int argc, char** argv) {
+  auto std_nth_element = [](auto first, auto nth, auto last) { return std::nth_element(first, nth, last); };
+
+  // Benchmark std::nth_element on various types of data. We always partition
+  // around the middle element of the full range. We don't benchmark
+  // std::ranges::nth_element separately because it forwards to the same core.
+  //
+  // We run this in batches because nth_element mutates the range in place, so we
+  // restore the original data after each batch.
+  //
+  // We intentionally don't benchmark the predicated version of the algorithm
+  // because that makes the benchmark run too slowly.
+  {
+    auto bm = []<class Container>(std::string name, auto nth_element, auto generate_data) {
+      benchmark::RegisterBenchmark(
+          name,
+          [nth_element, generate_data](auto& st) {
+            std::size_t const size          = st.range(0);
+            constexpr std::size_t BatchSize = 32;
+            using ValueType                 = typename Container::value_type;
+            std::vector<ValueType> data     = generate_data(size);
+            std::array<Container, BatchSize> c;
+            std::fill_n(c.begin(), BatchSize, Container(data.begin(), data.end()));
+
+            std::size_t const half = size / 2;
+            while (st.KeepRunningBatch(BatchSize)) {
+              for (std::size_t i = 0; i != BatchSize; ++i) {
+                benchmark::DoNotOptimize(c[i]);
+                nth_element(c[i].begin(), c[i].begin() + half, c[i].end());
+                benchmark::DoNotOptimize(c[i]);
+              }
+
+              st.PauseTiming();
+              for (std::size_t i = 0; i != BatchSize; ++i) {
+                std::copy(data.begin(), data.end(), c[i].begin());
+              }
+              st.ResumeTiming();
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+
+    auto register_bm = [&](auto generate, std::string variant) {
+      auto gen2 = [generate](auto size) {
+        std::vector<int> data = generate(size);
+        std::vector<support::NonIntegral> real_data(data.begin(), data.end());
+        return real_data;
+      };
+      auto name = [variant](std::string op) { return op + " (" + variant + ")"; };
+      bm.operator()<std::vector<int>>(name("std::nth_element(vector<int>)"), std_nth_element, generate);
+      bm.operator()<std::vector<support::NonIntegral>>(
+          name("std::nth_element(vector<NonIntegral>)"), std_nth_element, gen2);
+      bm.operator()<std::deque<int>>(name("std::nth_element(deque<int>)"), std_nth_element, generate);
+    };
+
+    register_bm(support::quicksort_adversarial_data<int>, "qsort adversarial");
+    register_bm(support::ascending_sorted_data<int>, "ascending");
+    register_bm(support::descending_sorted_data<int>, "descending");
+    register_bm(support::pipe_organ_data<int>, "pipe-organ");
+    register_bm(support::heap_data<int>, "heap");
+    register_bm(support::shuffled_data<int>, "shuffled");
+    register_bm(support::single_element_data<int>, "repeated");
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/210112

From libcxx-commits at lists.llvm.org  Thu Jul 16 14:58:10 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 16 Jul 2026 14:58:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][math] Add `constexpr` to
 `std::isgreater()` (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5953f2.92ea0e2a.9a5e7.ca2f@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 1/4] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 2/4] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 3/4] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 4/4] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }


From libcxx-commits at lists.llvm.org  Thu Jul 16 17:01:09 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Thu, 16 Jul 2026 17:01:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Granularize `<optional>` (PR
	#206644)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206644@github.com>
Message-ID: <6a5970c5.c56fb102.2d07b8.9ddc@mx.google.com>


https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/206644

>From e41c4724ed356f1a4f1c96c0cace13684695f5cb Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 12 Jun 2026 23:22:27 -0400
Subject: [PATCH 1/4] Granularize optional

---
 libcxx/include/CMakeLists.txt                 |    8 +
 libcxx/include/__fwd/optional.h               |   36 +
 libcxx/include/__optional/common.h            |   97 ++
 libcxx/include/__optional/comparison.h        |  347 ++++
 libcxx/include/__optional/hash.h              |   48 +
 libcxx/include/__optional/nullopt_t.h         |   40 +
 libcxx/include/__optional/optional.h          |  761 +++++++++
 libcxx/include/__optional/optional_ref.h      |  355 ++++
 libcxx/include/__optional/swap.h              |   45 +
 libcxx/include/module.modulemap.in            |   19 +
 libcxx/include/optional                       | 1432 +----------------
 .../ostream_joiner.op.assign.pass.cpp         |    1 +
 .../derive.pass.cpp                           |    1 +
 .../optional.object.ctor/ctor.verify.cpp      |   24 +-
 .../optional.object.ctor/deduct.verify.cpp    |    2 +-
 ...al_requires_destructible_object.verify.cpp |   26 +-
 16 files changed, 1800 insertions(+), 1442 deletions(-)
 create mode 100644 libcxx/include/__fwd/optional.h
 create mode 100644 libcxx/include/__optional/common.h
 create mode 100644 libcxx/include/__optional/comparison.h
 create mode 100644 libcxx/include/__optional/hash.h
 create mode 100644 libcxx/include/__optional/nullopt_t.h
 create mode 100644 libcxx/include/__optional/optional.h
 create mode 100644 libcxx/include/__optional/optional_ref.h
 create mode 100644 libcxx/include/__optional/swap.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index cec9ba06a7a6d..021d392e50c78 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -468,6 +468,7 @@ set(files
   __fwd/mdspan.h
   __fwd/memory.h
   __fwd/memory_resource.h
+  __fwd/optional.h
   __fwd/ostream.h
   __fwd/pair.h
   __fwd/queue.h
@@ -661,6 +662,13 @@ set(files
   __numeric/transform_exclusive_scan.h
   __numeric/transform_inclusive_scan.h
   __numeric/transform_reduce.h
+  __optional/common.h
+  __optional/comparison.h
+  __optional/hash.h
+  __optional/nullopt_t.h
+  __optional/optional.h
+  __optional/optional_ref.h
+  __optional/swap.h
   __ostream/basic_ostream.h
   __ostream/print.h
   __ostream/put_character_sequence.h
diff --git a/libcxx/include/__fwd/optional.h b/libcxx/include/__fwd/optional.h
new file mode 100644
index 0000000000000..35a3d2ea11e74
--- /dev/null
+++ b/libcxx/include/__fwd/optional.h
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// std::optional<T&>
+
+#ifndef _LIBCPP___FWD_OPTIONAL_H
+#define _LIBCPP___FWD_OPTIONAL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+class optional;
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___FWD_OPTIONAL_H
diff --git a/libcxx/include/__optional/common.h b/libcxx/include/__optional/common.h
new file mode 100644
index 0000000000000..2b4862cfd9d34
--- /dev/null
+++ b/libcxx/include/__optional/common.h
@@ -0,0 +1,97 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_COMMON_H
+#define _LIBCPP_OPTIONAL_COMMON_H
+
+#include <__assert>
+#include <__config>
+#include <__exception/exception.h>
+#include <__fwd/format.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/enable_view.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+
+#include <__fwd/optional.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+namespace std // purposefully not using versioning namespace
+{
+
+class _LIBCPP_EXPORTED_FROM_ABI bad_optional_access : public exception {
+public:
+  bad_optional_access() _NOEXCEPT                                      = default;
+  bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
+  bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
+  // Get the key function ~bad_optional_access() into the dylib
+  ~bad_optional_access() _NOEXCEPT override;
+  [[__nodiscard__]] const char* what() const _NOEXCEPT override;
+};
+
+} // namespace std
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+[[noreturn]] inline void __throw_bad_optional_access() {
+#  if _LIBCPP_HAS_EXCEPTIONS
+  throw bad_optional_access();
+#  else
+  _LIBCPP_VERBOSE_ABORT("bad_optional_access was thrown in -fno-exceptions mode");
+#  endif
+}
+
+struct __optional_construct_from_invoke_tag {};
+
+template <class _Tp>
+struct __is_std_optional : false_type {};
+
+template <class _Tp>
+struct __is_std_optional<optional<_Tp>> : true_type {};
+
+template<class _Tp, bool = is_reference_v<_Tp>>
+struct __optional_storage_base;
+
+#    if _LIBCPP_STD_VER < 26
+template <class _Tp>
+inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
+#    else
+template <class _Tp>
+inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp> || is_lvalue_reference_v<_Tp>;
+#    endif
+
+#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+constexpr bool ranges::enable_view<optional<_Tp>> = true;
+
+template <class _Tp>
+constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
+
+template <class _Tp>
+constexpr bool ranges::enable_borrowed_range<optional<_Tp&>> = true;
+
+#    endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif
diff --git a/libcxx/include/__optional/comparison.h b/libcxx/include/__optional/comparison.h
new file mode 100644
index 0000000000000..10c109fac7605
--- /dev/null
+++ b/libcxx/include/__optional/comparison.h
@@ -0,0 +1,347 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_COMPARISON_H
+#define _LIBCPP_OPTIONAL_COMPARISON_H
+
+#include <__compare/compare_three_way_result.h>
+#include <__compare/ordering.h>
+#include <__compare/three_way_comparable.h>
+#include <__config>
+#include <__optional/nullopt_t.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_core_convertible.h>
+#include <__type_traits/is_swappable.h>
+#include <__utility/declval.h>
+
+#include <__fwd/optional.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (static_cast<bool>(__x) != static_cast<bool>(__y))
+    return false;
+  if (!static_cast<bool>(__x))
+    return true;
+  return *__x == *__y;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (static_cast<bool>(__x) != static_cast<bool>(__y))
+    return true;
+  if (!static_cast<bool>(__x))
+    return false;
+  return *__x != *__y;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (!static_cast<bool>(__y))
+    return false;
+  if (!static_cast<bool>(__x))
+    return true;
+  return *__x < *__y;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (!static_cast<bool>(__x))
+    return false;
+  if (!static_cast<bool>(__y))
+    return true;
+  return *__x > *__y;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (!static_cast<bool>(__x))
+    return true;
+  if (!static_cast<bool>(__y))
+    return false;
+  return *__x <= *__y;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (!static_cast<bool>(__y))
+    return true;
+  if (!static_cast<bool>(__x))
+    return false;
+  return *__x >= *__y;
+}
+
+#  if _LIBCPP_STD_VER >= 20
+
+template <class _Tp, three_way_comparable_with<_Tp> _Up>
+_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
+operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (__x && __y)
+    return *__x <=> *__y;
+  return __x.has_value() <=> __y.has_value();
+}
+
+#  endif // _LIBCPP_STD_VER >= 20
+
+// [optional.nullops] Comparison with nullopt
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, std::nullopt_t) noexcept {
+  return !static_cast<bool>(__x);
+}
+
+#  if _LIBCPP_STD_VER <= 17
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(nullopt_t, const optional<_Tp>& __x) noexcept {
+  return !static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, nullopt_t) noexcept {
+  return static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(nullopt_t, const optional<_Tp>& __x) noexcept {
+  return static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>&, nullopt_t) noexcept {
+  return false;
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(nullopt_t, const optional<_Tp>& __x) noexcept {
+  return static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, nullopt_t) noexcept {
+  return !static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(nullopt_t, const optional<_Tp>&) noexcept {
+  return true;
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, nullopt_t) noexcept {
+  return static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(nullopt_t, const optional<_Tp>&) noexcept {
+  return false;
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>&, nullopt_t) noexcept {
+  return true;
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(nullopt_t, const optional<_Tp>& __x) noexcept {
+  return !static_cast<bool>(__x);
+}
+
+#  else // _LIBCPP_STD_VER <= 17
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept {
+  return __x.has_value() <=> false;
+}
+
+#  endif // _LIBCPP_STD_VER <= 17
+
+// [optional.comp.with.t] Comparison with T
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x == __v;
+  return false;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v == *__x;
+  return false;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x != __v;
+  return true;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v != *__x;
+  return true;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x < __v;
+  return true;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v < *__x;
+  return false;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x <= __v;
+  return true;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v <= *__x;
+  return false;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x > __v;
+  return false;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v > *__x;
+  return true;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x >= __v;
+  return false;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v >= *__x;
+  return true;
+}
+
+#  if _LIBCPP_STD_VER >= 20
+
+template <class _Tp>
+concept __is_derived_from_optional = requires(const _Tp& __t) { []<class _Up>(const optional<_Up>&) {}(__t); };
+
+template <class _Tp, class _Up>
+  requires(!__is_derived_from_optional<_Up>) && three_way_comparable_with<_Tp, _Up>
+_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
+operator<=>(const optional<_Tp>& __x, const _Up& __v) {
+  return __x.has_value() ? *__x <=> __v : strong_ordering::less;
+}
+
+#  endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif
diff --git a/libcxx/include/__optional/hash.h b/libcxx/include/__optional/hash.h
new file mode 100644
index 0000000000000..830f99b0b855f
--- /dev/null
+++ b/libcxx/include/__optional/hash.h
@@ -0,0 +1,48 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_HASH_H
+#define _LIBCPP_OPTIONAL_HASH_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__functional/hash.h>
+#include <__type_traits/remove_const.h>
+
+#include <__fwd/optional.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+struct hash< __enable_hash_helper<optional<_Tp>, remove_const_t<_Tp>> > {
+#  if _LIBCPP_STD_VER <= 17 || defined(_LIBCPP_ENABLE_CXX20_REMOVED_BINDER_TYPEDEFS)
+  _LIBCPP_DEPRECATED_IN_CXX17 typedef optional<_Tp> argument_type;
+  _LIBCPP_DEPRECATED_IN_CXX17 typedef size_t result_type;
+#  endif
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t operator()(const optional<_Tp>& __opt) const {
+    return static_cast<bool>(__opt) ? hash<remove_const_t<_Tp>>()(*__opt) : 0;
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+#endif // _LIBCPP_OPTIONAL_HASH_H
diff --git a/libcxx/include/__optional/nullopt_t.h b/libcxx/include/__optional/nullopt_t.h
new file mode 100644
index 0000000000000..bd66923f026b8
--- /dev/null
+++ b/libcxx/include/__optional/nullopt_t.h
@@ -0,0 +1,40 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_NULLOPT_T_H
+#define _LIBCPP_OPTIONAL_NULLOPT_T_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+struct nullopt_t {
+  struct __secret_tag {
+    explicit __secret_tag() = default;
+  };
+  constexpr explicit nullopt_t(__secret_tag, __secret_tag) noexcept {}
+};
+
+inline constexpr nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secret_tag{}};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+#endif
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
new file mode 100644
index 0000000000000..d01d8e661bdca
--- /dev/null
+++ b/libcxx/include/__optional/optional.h
@@ -0,0 +1,761 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// std::optional<T>
+
+#ifndef _LIBCPP_OPTIONAL_OPTIONAL_H
+#define _LIBCPP_OPTIONAL_OPTIONAL_H
+
+#include <__assert>
+#include <__concepts/invocable.h>
+#include <__config>
+#include <__functional/invoke.h>
+#include <__iterator/bounded_iter.h>
+#include <__iterator/capacity_aware_iterator.h>
+#include <__memory/addressof.h>
+#include <__memory/construct_at.h>
+#include <__tuple/sfinae_helpers.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/disjunction.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_assignable.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_destructible.h>
+#include <__type_traits/is_nothrow_assignable.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_scalar.h>
+#include <__type_traits/is_swappable.h>
+#include <__type_traits/is_trivially_assignable.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
+#include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+
+#include <initializer_list>
+
+#include <__fwd/optional.h>
+#include <__optional/common.h>
+#include <__optional/nullopt_t.h>
+
+#include <__optional/comparison.h>
+#include <__optional/hash.h>
+#include <__optional/swap.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp, bool = is_trivially_destructible<_Tp>::value>
+struct __optional_destruct_base;
+
+template <class _Tp>
+struct __optional_destruct_base<_Tp, false> {
+  typedef _Tp value_type;
+  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
+  union {
+    char __null_state_;
+    remove_cv_t<value_type> __val_;
+  };
+  bool __engaged_;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__optional_destruct_base() {
+    if (__engaged_)
+      __val_.~value_type();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
+      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
+
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Fp, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(
+      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
+      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
+#    endif
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
+    if (__engaged_) {
+      __val_.~value_type();
+      __engaged_ = false;
+    }
+  }
+};
+
+template <class _Tp>
+struct __optional_destruct_base<_Tp, true> {
+  typedef _Tp value_type;
+  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
+  union {
+    char __null_state_;
+    remove_cv_t<value_type> __val_;
+  };
+  bool __engaged_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
+      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
+
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Fp, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
+      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
+      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
+#    endif
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
+    if (__engaged_) {
+      __engaged_ = false;
+    }
+  }
+};
+
+template <class _Tp, bool>
+struct __optional_storage_base : __optional_destruct_base<_Tp> {
+  using __base _LIBCPP_NODEBUG = __optional_destruct_base<_Tp>;
+  using value_type             = _Tp;
+  using __base::__base;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() & noexcept { return this->__val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get() const& noexcept { return this->__val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type&& __get() && noexcept { return std::move(this->__val_); }
+  _LIBCPP_HIDE_FROM_ABI constexpr const value_type&& __get() const&& noexcept { return std::move(this->__val_); }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_Args&&... __args) {
+    _LIBCPP_ASSERT_INTERNAL(!has_value(), "__construct called for engaged __optional_storage");
+    std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
+    this->__engaged_ = true;
+  }
+
+  template <class _That>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
+    if (__opt.has_value())
+      __construct(std::forward<_That>(__opt).__get());
+  }
+
+  template <class _That>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
+    if (this->__engaged_ == __opt.has_value()) {
+      if (this->__engaged_)
+        static_cast<_Tp&>(this->__val_) = std::forward<_That>(__opt).__get();
+    } else {
+      if (this->__engaged_)
+        this->reset();
+      else
+        __construct(std::forward<_That>(__opt).__get());
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+  __swap(__optional_storage_base& __rhs) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) {
+    using std::swap;
+    if (this->has_value() == __rhs.has_value()) {
+      if (this->has_value())
+        swap(this->__get(), __rhs.__get());
+    } else {
+      if (this->has_value()) {
+        __rhs.__construct(std::move(this->__get()));
+        this->reset();
+      } else {
+        this->__construct(std::move(__rhs.__get()));
+        __rhs.reset();
+      }
+    }
+  }
+
+  // [optional.observe]
+  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp const> operator->() const noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
+    return std::addressof(this->__get());
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
+    return std::addressof(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return std::move(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return std::move(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return std::move(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return std::move(this->__get());
+  }
+};
+
+template <class _Tp, bool = is_trivially_copy_constructible_v<_Tp>>
+struct __optional_copy_base : __optional_storage_base<_Tp> {
+  using __optional_storage_base<_Tp>::__optional_storage_base;
+};
+
+template <class _Tp>
+struct __optional_copy_base<_Tp, false> : __optional_storage_base<_Tp> {
+  using __optional_storage_base<_Tp>::__optional_storage_base;
+
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_base() = default;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_base(const __optional_copy_base& __opt) {
+    this->__construct_from(__opt);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_base(__optional_copy_base&&)                 = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(const __optional_copy_base&) = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(__optional_copy_base&&)      = default;
+};
+
+template <class _Tp, bool = is_trivially_move_constructible_v<_Tp>>
+struct __optional_move_base : __optional_copy_base<_Tp> {
+  using __optional_copy_base<_Tp>::__optional_copy_base;
+};
+
+template <class _Tp>
+struct __optional_move_base<_Tp, false> : __optional_copy_base<_Tp> {
+  using value_type = _Tp;
+  using __optional_copy_base<_Tp>::__optional_copy_base;
+
+  _LIBCPP_HIDE_FROM_ABI __optional_move_base()                            = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_base(const __optional_move_base&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+  __optional_move_base(__optional_move_base&& __opt) noexcept(is_nothrow_move_constructible_v<value_type>) {
+    this->__construct_from(std::move(__opt));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(const __optional_move_base&) = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(__optional_move_base&&)      = default;
+};
+
+template <class _Tp,
+          bool = (is_trivially_destructible_v<_Tp> && is_trivially_copy_constructible_v<_Tp> &&
+                  is_trivially_copy_assignable_v<_Tp>)>
+struct __optional_copy_assign_base : __optional_move_base<_Tp> {
+  using __optional_move_base<_Tp>::__optional_move_base;
+};
+
+template <class _Tp>
+struct __optional_copy_assign_base<_Tp, false> : __optional_move_base<_Tp> {
+  using __optional_move_base<_Tp>::__optional_move_base;
+
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base()                                   = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(const __optional_copy_assign_base&) = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(__optional_copy_assign_base&&)      = default;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_assign_base&
+  operator=(const __optional_copy_assign_base& __opt) {
+    this->__assign_from(__opt);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base& operator=(__optional_copy_assign_base&&) = default;
+};
+
+template <class _Tp,
+          bool = (is_trivially_destructible_v<_Tp> && is_trivially_move_constructible_v<_Tp> &&
+                  is_trivially_move_assignable_v<_Tp>)>
+struct __optional_move_assign_base : __optional_copy_assign_base<_Tp> {
+  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
+};
+
+template <class _Tp>
+struct __optional_move_assign_base<_Tp, false> : __optional_copy_assign_base<_Tp> {
+  using value_type = _Tp;
+  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
+
+  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base()                                              = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(const __optional_move_assign_base& __opt)      = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(__optional_move_assign_base&&)                 = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base& operator=(const __optional_move_assign_base&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_move_assign_base&
+  operator=(__optional_move_assign_base&& __opt) noexcept(
+      is_nothrow_move_assignable_v<value_type> && is_nothrow_move_constructible_v<value_type>) {
+    this->__assign_from(std::move(__opt));
+    return *this;
+  }
+};
+
+template <class _Tp>
+using __optional_sfinae_ctor_base_t _LIBCPP_NODEBUG =
+    __sfinae_ctor_base< is_copy_constructible<_Tp>::value, is_move_constructible<_Tp>::value >;
+
+template <class _Tp>
+using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
+    __sfinae_assign_base< (is_copy_constructible_v<_Tp> && is_copy_assignable_v<_Tp>),
+                          (is_move_constructible_v<_Tp> && is_move_assignable_v<_Tp>)>;
+
+template <class _Tp>
+struct __optional_iterator_base : __optional_move_assign_base<_Tp> {
+  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
+};
+
+#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+  requires is_object_v<_Tp>
+struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
+private:
+  using __pointer _LIBCPP_NODEBUG       = add_pointer_t<_Tp>;
+  using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const _Tp>;
+
+public:
+  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+  using iterator       = __bounded_iter<__pointer>;
+  using const_iterator = __bounded_iter<__const_pointer>;
+#        else
+  using iterator       = __capacity_aware_iterator<__pointer, optional<_Tp>, 1>;
+  using const_iterator = __capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>;
+#        endif
+
+  // [optional.iterators], iterator support
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
+    auto* __ptr = std::addressof(this->__get());
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
+#        else
+    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp>, 1>(__ptr);
+#        endif
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
+    auto* __ptr = std::addressof(this->__get());
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
+#        else
+    return std::__make_capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>(__ptr);
+#        endif
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
+    return begin() + (this->has_value() ? 1 : 0);
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
+    return begin() + (this->has_value() ? 1 : 0);
+  }
+};
+
+#    endif   // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+class _LIBCPP_DECLSPEC_EMPTY_BASES optional
+    : public __optional_iterator_base<_Tp>,
+      private __optional_sfinae_ctor_base_t<_Tp>,
+      private __optional_sfinae_assign_base_t<_Tp> {
+  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp>;
+
+public:
+  using value_type = __libcpp_remove_reference_t<_Tp>;
+
+  using __trivially_relocatable _LIBCPP_NODEBUG =
+      conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
+
+private:
+  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
+  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
+#    if _LIBCPP_STD_VER >= 26
+  static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
+#    else
+  static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
+#    endif
+  static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
+  static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
+
+  // LWG2756: conditionally explicit conversion from _Up
+  struct _CheckOptionalArgsConstructor {
+    template <class _Up>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
+      return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>;
+    }
+
+    template <class _Up>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
+      return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>;
+    }
+  };
+  template <class _Up>
+  using _CheckOptionalArgsCtor _LIBCPP_NODEBUG =
+      _If< _IsNotSame<__remove_cvref_t<_Up>, in_place_t>::value && _IsNotSame<__remove_cvref_t<_Up>, optional>::value &&
+               (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_optional<__remove_cvref_t<_Up>>::value),
+           _CheckOptionalArgsConstructor,
+           __check_tuple_constructor_fail >;
+  template <class _QualUp>
+  struct _CheckOptionalLikeConstructor {
+    template <class _Up, class _Opt = optional<_Up>>
+    using __check_constructible_from_opt _LIBCPP_NODEBUG =
+        _Or< is_constructible<_Tp, _Opt&>,
+             is_constructible<_Tp, _Opt const&>,
+             is_constructible<_Tp, _Opt&&>,
+             is_constructible<_Tp, _Opt const&&>,
+             is_convertible<_Opt&, _Tp>,
+             is_convertible<_Opt const&, _Tp>,
+             is_convertible<_Opt&&, _Tp>,
+             is_convertible<_Opt const&&, _Tp> >;
+    template <class _Up, class _Opt = optional<_Up>>
+    using __check_assignable_from_opt _LIBCPP_NODEBUG =
+        _Or< is_assignable<_Tp&, _Opt&>,
+             is_assignable<_Tp&, _Opt const&>,
+             is_assignable<_Tp&, _Opt&&>,
+             is_assignable<_Tp&, _Opt const&&> >;
+    template <class _Up, class _QUp = _QualUp>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
+      return is_convertible<_QUp, _Tp>::value &&
+             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
+    }
+    template <class _Up, class _QUp = _QualUp>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
+      return !is_convertible<_QUp, _Tp>::value &&
+             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
+    }
+    template <class _Up, class _QUp = _QualUp>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_assign() {
+      // Construction and assignability of _QUp to _Tp has already been
+      // checked.
+      return !__check_constructible_from_opt<_Up>::value && !__check_assignable_from_opt<_Up>::value;
+    }
+  };
+
+  template <class _Up, class _QualUp>
+  using _CheckOptionalLikeCtor _LIBCPP_NODEBUG =
+      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp> >::value,
+           _CheckOptionalLikeConstructor<_QualUp>,
+           __check_tuple_constructor_fail >;
+  template <class _Up, class _QualUp>
+  using _CheckOptionalLikeAssign _LIBCPP_NODEBUG =
+      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>, is_assignable<_Tp&, _QualUp> >::value,
+           _CheckOptionalLikeConstructor<_QualUp>,
+           __check_tuple_constructor_fail >;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr optional() noexcept {}
+  _LIBCPP_HIDE_FROM_ABI constexpr optional(const optional&) = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr optional(optional&&)      = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr optional(nullopt_t) noexcept {}
+
+  template <class _InPlaceT,
+            class... _Args,
+            enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, is_constructible<_Tp, _Args...>>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_InPlaceT, _Args&&... __args)
+      : __base(in_place, std::forward<_Args>(__args)...) {}
+
+  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
+      : __base(in_place, __il, std::forward<_Args>(__args)...) {}
+
+  template <class _Up = _Tp, enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
+      : __base(in_place, std::forward<_Up>(__v)) {}
+
+  template <class _Up                                                                        = remove_cv_t<_Tp>,
+            enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) noexcept(
+      is_nothrow_constructible_v<_Tp, _Up>) // strengthened
+      : __base(in_place, std::forward<_Up>(__v)) {}
+
+  // LWG2756: conditionally explicit conversion from const optional<_Up>&
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_implicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) {
+    this->__construct_from(__v);
+  }
+
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_explicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) {
+    this->__construct_from(__v);
+  }
+
+  // LWG2756: conditionally explicit conversion from optional<_Up>&&
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_implicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) {
+    this->__construct_from(std::move(__v));
+  }
+
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_explicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) {
+    this->__construct_from(std::move(__v));
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Tag,
+            class _Fp,
+            class... _Args,
+            enable_if_t<_IsSame<_Tag, __optional_construct_from_invoke_tag>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
+      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
+#    endif
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(nullopt_t) noexcept {
+    reset();
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(const optional&) = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(optional&&)      = default;
+
+  // LWG2756
+  template <class _Up        = remove_cv_t<_Tp>,
+            enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>,
+                             _Or<_IsNotSame<__remove_cvref_t<_Up>, _Tp>, _Not<is_scalar<_Tp>>>,
+                             is_constructible<_Tp, _Up>,
+                             is_assignable<_Tp&, _Up>>::value,
+                        int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(_Up&& __v) {
+    if (this->has_value())
+      this->__get() = std::forward<_Up>(__v);
+    else
+      this->__construct(std::forward<_Up>(__v));
+    return *this;
+  }
+
+  // LWG2756
+  template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>::template __enable_assign<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(const optional<_Up>& __v) {
+    this->__assign_from(__v);
+    return *this;
+  }
+
+  // LWG2756
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_assign<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(optional<_Up>&& __v) {
+    this->__assign_from(std::move(__v));
+    return *this;
+  }
+
+  template <class... _Args, enable_if_t<is_constructible_v<_Tp, _Args...>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) {
+    reset();
+    this->__construct(std::forward<_Args>(__args)...);
+    return this->__get();
+  }
+
+  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) {
+    reset();
+    this->__construct(__il, std::forward<_Args>(__args)...);
+    return this->__get();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+  swap(optional& __opt) noexcept((is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>)) {
+    this->__swap(__opt);
+  }
+
+  using __base::operator*;
+  using __base::operator->;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return has_value(); }
+
+  using __base::__get;
+  using __base::has_value;
+  using __base::value;
+
+  template <class _Up = remove_cv_t<_Tp>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
+    static_assert(is_copy_constructible_v<_Tp>, "optional<T>::value_or: T must be copy constructible");
+    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
+    return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v));
+  }
+
+  template <class _Up = remove_cv_t<_Tp>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
+    static_assert(is_move_constructible_v<_Tp>, "optional<T>::value_or: T must be move constructible");
+    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
+    return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v));
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
+    using _Up = invoke_result_t<_Func, _Tp&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(value()) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), value());
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
+    using _Up = invoke_result_t<_Func, const _Tp&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(value()) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), value());
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
+    using _Up = invoke_result_t<_Func, _Tp&&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(std::move(value())) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), std::move(value()));
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
+    using _Up = invoke_result_t<_Func, const _Tp&&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(std::move(value())) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), std::move(value()));
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & {
+    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
+    return optional<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& {
+    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
+    return optional<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && {
+    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
+    return optional<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& {
+    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
+    return optional<_Up>();
+  }
+
+  template <invocable _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const&
+    requires is_copy_constructible_v<_Tp>
+  {
+    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
+                  "Result of f() should be the same type as this optional");
+    if (*this)
+      return *this;
+    return std::forward<_Func>(__f)();
+  }
+
+  template <invocable _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) &&
+    requires is_move_constructible_v<_Tp>
+  {
+    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
+                  "Result of f() should be the same type as this optional");
+    if (*this)
+      return std::move(*this);
+    return std::forward<_Func>(__f)();
+  }
+#    endif // _LIBCPP_STD_VER >= 23
+
+  using __base::reset;
+};
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_OPTIONAL_OPTIONAL_H
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
new file mode 100644
index 0000000000000..dd1adc1642ca3
--- /dev/null
+++ b/libcxx/include/__optional/optional_ref.h
@@ -0,0 +1,355 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// std::optional<T&>
+
+#ifndef _LIBCPP_OPTIONAL_OPTIONAL_REF_H
+#define _LIBCPP_OPTIONAL_OPTIONAL_REF_H
+
+#include <__assert>
+#include <__concepts/invocable.h>
+#include <__config>
+#include <__functional/invoke.h>
+#include <__iterator/bounded_iter.h>
+#include <__iterator/capacity_aware_iterator.h>
+#include <__memory/addressof.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/reference_constructs_from_temporary.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <__utility/swap.h>
+
+#include <__fwd/optional.h>
+#include <__optional/comparison.h>
+#include <__optional/swap.h>
+
+#include <__optional/common.h>
+#include <__optional/hash.h>
+#include <__optional/nullopt_t.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 26
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+struct __optional_storage_base<_Tp, true> {
+  using value_type                 = _Tp;
+  using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
+  __raw_type* __value_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
+
+  template <class _Up>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
+    _Tp& __r(std::forward<_Up>(__val));
+    __value_ = std::addressof(__r);
+  }
+
+  template <class _UArg>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) {
+    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
+                  "Attempted to construct a reference element in optional from a "
+                  "possible temporary");
+    __convert_init_ref_val(std::forward<_UArg>(__uarg));
+  }
+
+  template <class _Fp, class... _Args>
+  constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
+    __convert_init_ref_val(std::forward<invoke_result_t<_Fp, _Args...>>(
+        std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept { __value_ = nullptr; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const noexcept { return *__value_; }
+
+  template <class _UArg>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_UArg&& __val) {
+    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
+                  "Attempted to construct a reference element in tuple from a "
+                  "possible temporary");
+    __convert_init_ref_val(std::forward<_UArg>(__val));
+  }
+
+  template <class _That>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
+    if (__opt.has_value())
+      __construct(std::forward<_That>(__opt).__get());
+  }
+
+  template <class _That>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
+    if (has_value() == __opt.has_value()) {
+      if (has_value())
+        *__value_ = std::forward<_That>(__opt).__get();
+    } else {
+      if (has_value())
+        reset();
+      else
+        __construct(std::forward<_That>(__opt).__get());
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept {
+    std::swap(__value_, __rhs.__value_);
+  }
+
+  // [optional.ref.observe]
+  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() const noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
+    return std::addressof(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() const noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() const {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return this->__get();
+  }
+};
+
+template <class _Tp>
+struct __optional_iterator_base;
+
+template<class _Tp>
+struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
+  using __optional_storage_base<_Tp&>::__optional_storage_base;
+};
+
+#if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+  requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
+struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
+private:
+  using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
+
+public:
+  using __optional_storage_base<_Tp&>::__optional_storage_base;
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+  using iterator = __bounded_iter<__pointer>;
+#        else
+  using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
+#        endif
+
+  // [optional.ref.iterators], iterator support
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
+    auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
+#        else
+    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
+#        endif
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
+    return begin() + (this->has_value() ? 1 : 0);
+  }
+};
+
+#endif
+
+template <class _Tp>
+class optional<_Tp&> : public __optional_iterator_base<_Tp&> {
+  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp&>;
+
+  template <class _Up, class _QualUp>
+  static constexpr bool __check_optionalU_ctor =
+      !is_same_v<remove_cv_t<_Tp>, optional<_Up>> && !is_same_v<_Tp&, _Up> && is_constructible_v<_Tp&, _QualUp>;
+  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
+  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
+
+public:
+  using value_type = _Tp;
+
+  constexpr optional() noexcept = default;
+  constexpr optional(nullopt_t) noexcept {}
+  constexpr optional(const optional&) noexcept = default;
+
+  template <class _Arg>
+    requires(is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>)
+  constexpr explicit optional(in_place_t, _Arg&& __arg) : __base(in_place, std::forward<_Arg>(__arg)) {}
+
+  template <class _Up>
+    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
+             is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr explicit(!is_convertible_v<_Up, _Tp&>) optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>)
+      : __base(in_place, std::forward<_Up>(__v)) {}
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, _Up&> && !reference_constructs_from_temporary_v<_Tp&, _Up&>)
+  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
+      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) {
+    this->__construct_from(__rhs);
+  }
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, const _Up&> && !reference_constructs_from_temporary_v<_Tp&, const _Up&>)
+  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
+      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) {
+    this->__construct_from(__rhs);
+  }
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
+      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
+    this->__construct_from(std::move(__rhs));
+  }
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, const _Up> && !reference_constructs_from_temporary_v<_Tp&, const _Up>)
+  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
+      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) {
+    this->__construct_from(std::move(__rhs));
+  }
+
+  template <class _Tag, class _Fp, class... _Args>
+    requires(is_same_v<_Tag, __optional_construct_from_invoke_tag>)
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
+      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
+
+  // deleted overloads
+
+  template <class _Up>
+    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
+             is_constructible_v<_Tp&, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
+      optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, _Up&> && reference_constructs_from_temporary_v<_Tp&, _Up&>)
+  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
+      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) = delete;
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, const _Up&> && reference_constructs_from_temporary_v<_Tp&, const _Up&>)
+  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
+      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) = delete;
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
+      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, const _Up> && reference_constructs_from_temporary_v<_Tp&, const _Up>)
+  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
+      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) = delete;
+
+  constexpr ~optional() = default;
+
+  using __base::__get;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(nullopt_t) noexcept {
+    reset();
+    return *this;
+  }
+
+  constexpr optional& operator=(const optional&) noexcept = default;
+
+  template <class _Up>
+    requires(is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr _Tp& emplace(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
+    this->__construct(std::forward<_Up>(__u));
+
+    return this->__get();
+  }
+
+  constexpr void swap(optional& __rhs) noexcept { this->__swap(__rhs); }
+
+  using __base::operator->;
+  using __base::operator*;
+
+  constexpr explicit operator bool() const noexcept { return has_value(); }
+
+  using __base::has_value;
+  using __base::value;
+
+  template <class _Up = remove_cv_t<_Tp>>
+    requires(!is_array_v<_Tp> && is_object_v<_Tp>)
+  [[nodiscard]] constexpr decay_t<_Tp> value_or(_Up&& __v) const {
+    using _XTp = remove_cv_t<_Tp>;
+    static_assert(is_constructible_v<_XTp, _Tp&>, "optional<T&>::value_or: remove_cv_t<T> must be constructible");
+    static_assert(is_convertible_v<_Up, _XTp>, "optional<T&>::value_or: U must be convertible to remove_cv_t<T>");
+    return this->has_value() ? this->__get() : static_cast<_XTp>(std::forward<_Up>(__v));
+  }
+
+  template <class _Func>
+  [[nodiscard]] constexpr auto and_then(_Func&& __f) const {
+    using _Up = invoke_result_t<_Func, _Tp&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(value()) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), value());
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] constexpr optional<remove_cv_t<invoke_result_t<_Func, _Tp&>>> transform(_Func&& __f) const {
+    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
+    return optional<_Up>();
+  }
+
+  template <invocable _Func>
+  [[nodiscard]] constexpr optional or_else(_Func&& __f) const {
+    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
+                  "Result of f() should be the same type as this optional");
+    if (*this)
+      return *this;
+    return std::forward<_Func>(__f)();
+  }
+
+  using __base::reset;
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_OPTIONAL_OPTIONAL_REF_H
diff --git a/libcxx/include/__optional/swap.h b/libcxx/include/__optional/swap.h
new file mode 100644
index 0000000000000..d1cec68c555fe
--- /dev/null
+++ b/libcxx/include/__optional/swap.h
@@ -0,0 +1,45 @@
+// -*- C++ -*-Expand commentComment on line R1Resolved
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_SWAP_H
+#define _LIBCPP_OPTIONAL_SWAP_H
+
+#include <__config>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_swappable.h>
+
+#include <__fwd/optional.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp,
+          enable_if_t<(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>)
+#  if _LIBCPP_STD_VER >= 26
+                          || is_reference_v<_Tp>
+#  endif
+                      ,
+                      int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) {
+  __x.swap(__y);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+
+#endif
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b9ddfd5bbf4cd..e0c40bad3bae9 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1796,6 +1796,25 @@ module std [system] {
   }
 
   module optional {
+    module fwd { header "__fwd/optional.h" }
+    module hash {
+      header "__optional/hash.h"
+      export std.functional.hash
+    }
+    module common {
+      header "__optional/common.h"
+      export std.iterator.bounded_iter
+      export std.iterator.capacity_aware_iterator
+      export std.format.fwd
+      export std.utility.swap
+    }
+
+    module comparison { header "__optional/comparison.h" }
+    module nullopt_t { header "__optional/nullopt_t.h" }
+    module optional { header "__optional/optional.h" }
+    module optional_ref { header "__optional/optional_ref.h" }
+    module swap { header "__optional/swap.h" }
+
     header "optional"
     export *
   }
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 2499479348892..2b40706910018 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -258,68 +258,34 @@ namespace std {
 #if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 #  include <__cxx03/__config>
 #else
-#  include <__assert>
-#  include <__compare/compare_three_way_result.h>
-#  include <__compare/ordering.h>
-#  include <__compare/three_way_comparable.h>
-#  include <__concepts/invocable.h>
 #  include <__config>
-#  include <__exception/exception.h>
-#  include <__functional/hash.h>
-#  include <__functional/invoke.h>
-#  include <__functional/unary_function.h>
-#  include <__fwd/format.h>
-#  include <__fwd/functional.h>
-#  include <__iterator/bounded_iter.h>
-#  include <__iterator/capacity_aware_iterator.h>
-#  include <__memory/addressof.h>
-#  include <__memory/construct_at.h>
-#  include <__ranges/enable_borrowed_range.h>
-#  include <__ranges/enable_view.h>
-#  include <__tuple/sfinae_helpers.h>
-#  include <__type_traits/add_pointer.h>
-#  include <__type_traits/conditional.h>
-#  include <__type_traits/conjunction.h>
 #  include <__type_traits/decay.h>
-#  include <__type_traits/disjunction.h>
 #  include <__type_traits/enable_if.h>
-#  include <__type_traits/integral_constant.h>
-#  include <__type_traits/invoke.h>
-#  include <__type_traits/is_array.h>
-#  include <__type_traits/is_assignable.h>
 #  include <__type_traits/is_constructible.h>
-#  include <__type_traits/is_convertible.h>
-#  include <__type_traits/is_core_convertible.h>
-#  include <__type_traits/is_destructible.h>
-#  include <__type_traits/is_function.h>
-#  include <__type_traits/is_nothrow_assignable.h>
-#  include <__type_traits/is_nothrow_constructible.h>
-#  include <__type_traits/is_object.h>
-#  include <__type_traits/is_reference.h>
-#  include <__type_traits/is_same.h>
-#  include <__type_traits/is_scalar.h>
-#  include <__type_traits/is_swappable.h>
-#  include <__type_traits/is_trivially_assignable.h>
-#  include <__type_traits/is_trivially_constructible.h>
-#  include <__type_traits/is_trivially_destructible.h>
-#  include <__type_traits/is_trivially_relocatable.h>
-#  include <__type_traits/negation.h>
 #  include <__type_traits/reference_constructs_from_temporary.h>
 #  include <__type_traits/remove_const.h>
-#  include <__type_traits/remove_cv.h>
-#  include <__type_traits/remove_cvref.h>
-#  include <__type_traits/remove_reference.h>
-#  include <__utility/declval.h>
 #  include <__utility/forward.h>
 #  include <__utility/in_place.h>
-#  include <__utility/move.h>
-#  include <__utility/swap.h>
-#  include <__verbose_abort>
 #  include <initializer_list>
 #  include <version>
 
-// standard-mandated includes
+#  include <__optional/common.h>
+#  include <__optional/comparison.h>
+#  include <__optional/hash.h>
+#  include <__optional/optional.h>
+#  include <__optional/optional_ref.h>
+#  include <__optional/swap.h>
 
+// TODO: Certain headers rely on these transitive includes
+#  include <__type_traits/conditional.h>
+#  include <__type_traits/invoke.h>
+#  include <__type_traits/is_object.h>
+#  include <__type_traits/is_reference.h>
+#  include <__type_traits/remove_cvref.h>
+#  include <__utility/declval.h>
+#  include <__utility/move.h>
+
+// standard-mandated includes
 // [optional.syn]
 #  include <compare>
 
@@ -330,1367 +296,13 @@ namespace std {
 _LIBCPP_PUSH_MACROS
 #  include <__undef_macros>
 
-namespace std // purposefully not using versioning namespace
-{
-
-class _LIBCPP_EXPORTED_FROM_ABI bad_optional_access : public exception {
-public:
-  _LIBCPP_HIDE_FROM_ABI bad_optional_access() _NOEXCEPT                                      = default;
-  _LIBCPP_HIDE_FROM_ABI bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
-  _LIBCPP_HIDE_FROM_ABI bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
-  // Get the key function ~bad_optional_access() into the dylib
-  ~bad_optional_access() _NOEXCEPT override;
-  [[__nodiscard__]] const char* what() const _NOEXCEPT override;
-};
-
-} // namespace std
-
 #  if _LIBCPP_STD_VER >= 17
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-[[noreturn]] inline _LIBCPP_HIDE_FROM_ABI void __throw_bad_optional_access() {
-#    if _LIBCPP_HAS_EXCEPTIONS
-  throw bad_optional_access();
-#    else
-  _LIBCPP_VERBOSE_ABORT("bad_optional_access was thrown in -fno-exceptions mode");
-#    endif
-}
-
-struct nullopt_t {
-  struct __secret_tag {
-    explicit __secret_tag() = default;
-  };
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit nullopt_t(__secret_tag, __secret_tag) noexcept {}
-};
-
-inline constexpr nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secret_tag{}};
-
-struct __optional_construct_from_invoke_tag {};
-
-template <class _Tp, bool = is_trivially_destructible<_Tp>::value>
-struct __optional_destruct_base;
-
-template <class _Tp>
-struct __optional_destruct_base<_Tp, false> {
-  typedef _Tp value_type;
-  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
-  union {
-    char __null_state_;
-    remove_cv_t<value_type> __val_;
-  };
-  bool __engaged_;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__optional_destruct_base() {
-    if (__engaged_)
-      __val_.~value_type();
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
-      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Fp, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(
-      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
-      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
-    if (__engaged_) {
-      __val_.~value_type();
-      __engaged_ = false;
-    }
-  }
-};
-
-template <class _Tp>
-struct __optional_destruct_base<_Tp, true> {
-  typedef _Tp value_type;
-  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
-  union {
-    char __null_state_;
-    remove_cv_t<value_type> __val_;
-  };
-  bool __engaged_;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
-      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Fp, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
-      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
-      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
-    if (__engaged_) {
-      __engaged_ = false;
-    }
-  }
-};
-
-template <class _Tp, bool = is_reference<_Tp>::value>
-struct __optional_storage_base : __optional_destruct_base<_Tp> {
-  using __base _LIBCPP_NODEBUG = __optional_destruct_base<_Tp>;
-  using value_type             = _Tp;
-  using __base::__base;
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() & noexcept { return this->__val_; }
-  _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get() const& noexcept { return this->__val_; }
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type&& __get() && noexcept { return std::move(this->__val_); }
-  _LIBCPP_HIDE_FROM_ABI constexpr const value_type&& __get() const&& noexcept { return std::move(this->__val_); }
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_Args&&... __args) {
-    _LIBCPP_ASSERT_INTERNAL(!has_value(), "__construct called for engaged __optional_storage");
-    std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
-    this->__engaged_ = true;
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
-    if (__opt.has_value())
-      __construct(std::forward<_That>(__opt).__get());
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
-    if (this->__engaged_ == __opt.has_value()) {
-      if (this->__engaged_)
-        static_cast<_Tp&>(this->__val_) = std::forward<_That>(__opt).__get();
-    } else {
-      if (this->__engaged_)
-        this->reset();
-      else
-        __construct(std::forward<_That>(__opt).__get());
-    }
-  }
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-  __swap(__optional_storage_base& __rhs) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) {
-    using std::swap;
-    if (this->has_value() == __rhs.has_value()) {
-      if (this->has_value())
-        swap(this->__get(), __rhs.__get());
-    } else {
-      if (this->has_value()) {
-        __rhs.__construct(std::move(this->__get()));
-        this->reset();
-      } else {
-        this->__construct(std::move(__rhs.__get()));
-        __rhs.reset();
-      }
-    }
-  }
-
-  // [optional.observe]
-  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp const> operator->() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return std::move(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return std::move(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return std::move(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return std::move(this->__get());
-  }
-};
-
-template <class _Tp>
-struct __optional_storage_base<_Tp, true> {
-  using value_type                 = _Tp;
-  using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
-  __raw_type* __value_;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
-
-  template <class _Up>
-  _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
-    _Tp& __r(std::forward<_Up>(__val));
-    __value_ = std::addressof(__r);
-  }
-
-  template <class _UArg>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) {
-    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
-                  "Attempted to construct a reference element in optional from a "
-                  "possible temporary");
-    __convert_init_ref_val(std::forward<_UArg>(__uarg));
-  }
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Fp, class... _Args>
-  constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
-    __convert_init_ref_val(std::forward<invoke_result_t<_Fp, _Args...>>(
-        std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)));
-  }
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept { __value_ = nullptr; }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const noexcept { return *__value_; }
-
-  template <class _UArg>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_UArg&& __val) {
-    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
-                  "Attempted to construct a reference element in tuple from a "
-                  "possible temporary");
-    __convert_init_ref_val(std::forward<_UArg>(__val));
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
-    if (__opt.has_value())
-      __construct(std::forward<_That>(__opt).__get());
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
-    if (has_value() == __opt.has_value()) {
-      if (has_value())
-        *__value_ = std::forward<_That>(__opt).__get();
-    } else {
-      if (has_value())
-        reset();
-      else
-        __construct(std::forward<_That>(__opt).__get());
-    }
-  }
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept {
-    std::swap(__value_, __rhs.__value_);
-  }
-
-  // [optional.ref.observe]
-  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() const {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return this->__get();
-  }
-};
-
-template <class _Tp, bool = is_trivially_copy_constructible_v<_Tp>>
-struct __optional_copy_base : __optional_storage_base<_Tp> {
-  using __optional_storage_base<_Tp>::__optional_storage_base;
-};
-
-template <class _Tp>
-struct __optional_copy_base<_Tp, false> : __optional_storage_base<_Tp> {
-  using __optional_storage_base<_Tp>::__optional_storage_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base() = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_base(const __optional_copy_base& __opt) {
-    this->__construct_from(__opt);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base(__optional_copy_base&&)                 = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(const __optional_copy_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(__optional_copy_base&&)      = default;
-};
-
-template <class _Tp, bool = is_trivially_move_constructible_v<_Tp>>
-struct __optional_move_base : __optional_copy_base<_Tp> {
-  using __optional_copy_base<_Tp>::__optional_copy_base;
-};
-
-template <class _Tp>
-struct __optional_move_base<_Tp, false> : __optional_copy_base<_Tp> {
-  using value_type = _Tp;
-  using __optional_copy_base<_Tp>::__optional_copy_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base()                            = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base(const __optional_move_base&) = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
-  __optional_move_base(__optional_move_base&& __opt) noexcept(is_nothrow_move_constructible_v<value_type>) {
-    this->__construct_from(std::move(__opt));
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(const __optional_move_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(__optional_move_base&&)      = default;
-};
-
-template <class _Tp,
-          bool = (is_trivially_destructible_v<_Tp> && is_trivially_copy_constructible_v<_Tp> &&
-                  is_trivially_copy_assignable_v<_Tp>)>
-struct __optional_copy_assign_base : __optional_move_base<_Tp> {
-  using __optional_move_base<_Tp>::__optional_move_base;
-};
-
-template <class _Tp>
-struct __optional_copy_assign_base<_Tp, false> : __optional_move_base<_Tp> {
-  using __optional_move_base<_Tp>::__optional_move_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base()                                   = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(const __optional_copy_assign_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(__optional_copy_assign_base&&)      = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_assign_base&
-  operator=(const __optional_copy_assign_base& __opt) {
-    this->__assign_from(__opt);
-    return *this;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base& operator=(__optional_copy_assign_base&&) = default;
-};
-
-template <class _Tp,
-          bool = (is_trivially_destructible_v<_Tp> && is_trivially_move_constructible_v<_Tp> &&
-                  is_trivially_move_assignable_v<_Tp>)>
-struct __optional_move_assign_base : __optional_copy_assign_base<_Tp> {
-  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
-};
-
-template <class _Tp>
-struct __optional_move_assign_base<_Tp, false> : __optional_copy_assign_base<_Tp> {
-  using value_type = _Tp;
-  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base()                                              = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(const __optional_move_assign_base& __opt)      = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(__optional_move_assign_base&&)                 = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base& operator=(const __optional_move_assign_base&) = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_move_assign_base&
-  operator=(__optional_move_assign_base&& __opt) noexcept(
-      is_nothrow_move_assignable_v<value_type> && is_nothrow_move_constructible_v<value_type>) {
-    this->__assign_from(std::move(__opt));
-    return *this;
-  }
-};
-
-template <class _Tp>
-using __optional_sfinae_ctor_base_t _LIBCPP_NODEBUG =
-    __sfinae_ctor_base< is_copy_constructible<_Tp>::value, is_move_constructible<_Tp>::value >;
-
-template <class _Tp>
-using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
-    __sfinae_assign_base< (is_copy_constructible_v<_Tp> && is_copy_assignable_v<_Tp>),
-                          (is_move_constructible_v<_Tp> && is_move_assignable_v<_Tp>)>;
-
-template <class _Tp>
-class optional;
-
-#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-template <class _Tp>
-constexpr bool ranges::enable_view<optional<_Tp>> = true;
-
-template <class _Tp>
-constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
-
-template <class _Tp>
-constexpr bool ranges::enable_borrowed_range<optional<_Tp&>> = true;
-
-#    endif
-
-#    if _LIBCPP_STD_VER >= 20
-
-template <class _Tp>
-concept __is_derived_from_optional = requires(const _Tp& __t) { []<class _Up>(const optional<_Up>&) {}(__t); };
-
-#    endif // _LIBCPP_STD_VER >= 20
-
-template <class _Tp>
-struct __is_std_optional : false_type {};
-template <class _Tp>
-struct __is_std_optional<optional<_Tp>> : true_type {};
-
-#    if _LIBCPP_STD_VER < 26
-template <class _Tp>
-inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
-#    else
-template <class _Tp>
-inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp> || is_lvalue_reference_v<_Tp>;
-#    endif
-
-template <class _Tp>
-struct __optional_iterator_base : __optional_move_assign_base<_Tp> {
-  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
-};
-
-#    if _LIBCPP_STD_VER >= 26
-template <class _Tp>
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
-};
-
-#      if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-
-template <class _Tp>
-  requires is_object_v<_Tp>
-struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
-private:
-  using __pointer _LIBCPP_NODEBUG       = add_pointer_t<_Tp>;
-  using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const _Tp>;
-
-public:
-  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-  using iterator       = __bounded_iter<__pointer>;
-  using const_iterator = __bounded_iter<__const_pointer>;
-#        else
-  using iterator       = __capacity_aware_iterator<__pointer, optional<_Tp>, 1>;
-  using const_iterator = __capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>;
-#        endif
-
-  // [optional.iterators], iterator support
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
-    auto* __ptr = std::addressof(this->__get());
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
-    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp>, 1>(__ptr);
-#        endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
-    auto* __ptr = std::addressof(this->__get());
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
-    return std::__make_capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>(__ptr);
-#        endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-};
-
-template <class _Tp>
-  requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
-private:
-  using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
-
-public:
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-  using iterator = __bounded_iter<__pointer>;
-#        else
-  using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
-#        endif
-
-  // [optional.ref.iterators], iterator support
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
-    auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
-    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
-#        endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-};
-
-#      endif // _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-#    endif   // _LIBCPP_STD_VER >= 26
-
-template <class _Tp>
-class _LIBCPP_DECLSPEC_EMPTY_BASES optional
-    : public __optional_iterator_base<_Tp>,
-      private __optional_sfinae_ctor_base_t<_Tp>,
-      private __optional_sfinae_assign_base_t<_Tp> {
-  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp>;
-
-public:
-  using value_type = __libcpp_remove_reference_t<_Tp>;
-
-  using __trivially_relocatable _LIBCPP_NODEBUG =
-      conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
-
-private:
-  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
-  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-#    if _LIBCPP_STD_VER >= 26
-  static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
-#    else
-  static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
-#    endif
-  static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
-  static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
-
-  // LWG2756: conditionally explicit conversion from _Up
-  struct _CheckOptionalArgsConstructor {
-    template <class _Up>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
-      return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>;
-    }
-
-    template <class _Up>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
-      return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>;
-    }
-  };
-  template <class _Up>
-  using _CheckOptionalArgsCtor _LIBCPP_NODEBUG =
-      _If< _IsNotSame<__remove_cvref_t<_Up>, in_place_t>::value && _IsNotSame<__remove_cvref_t<_Up>, optional>::value &&
-               (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_optional<__remove_cvref_t<_Up>>::value),
-           _CheckOptionalArgsConstructor,
-           __check_tuple_constructor_fail >;
-  template <class _QualUp>
-  struct _CheckOptionalLikeConstructor {
-    template <class _Up, class _Opt = optional<_Up>>
-    using __check_constructible_from_opt _LIBCPP_NODEBUG =
-        _Or< is_constructible<_Tp, _Opt&>,
-             is_constructible<_Tp, _Opt const&>,
-             is_constructible<_Tp, _Opt&&>,
-             is_constructible<_Tp, _Opt const&&>,
-             is_convertible<_Opt&, _Tp>,
-             is_convertible<_Opt const&, _Tp>,
-             is_convertible<_Opt&&, _Tp>,
-             is_convertible<_Opt const&&, _Tp> >;
-    template <class _Up, class _Opt = optional<_Up>>
-    using __check_assignable_from_opt _LIBCPP_NODEBUG =
-        _Or< is_assignable<_Tp&, _Opt&>,
-             is_assignable<_Tp&, _Opt const&>,
-             is_assignable<_Tp&, _Opt&&>,
-             is_assignable<_Tp&, _Opt const&&> >;
-    template <class _Up, class _QUp = _QualUp>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
-      return is_convertible<_QUp, _Tp>::value &&
-             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
-    }
-    template <class _Up, class _QUp = _QualUp>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
-      return !is_convertible<_QUp, _Tp>::value &&
-             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
-    }
-    template <class _Up, class _QUp = _QualUp>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_assign() {
-      // Construction and assignability of _QUp to _Tp has already been
-      // checked.
-      return !__check_constructible_from_opt<_Up>::value && !__check_assignable_from_opt<_Up>::value;
-    }
-  };
-
-  template <class _Up, class _QualUp>
-  using _CheckOptionalLikeCtor _LIBCPP_NODEBUG =
-      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp> >::value,
-           _CheckOptionalLikeConstructor<_QualUp>,
-           __check_tuple_constructor_fail >;
-  template <class _Up, class _QualUp>
-  using _CheckOptionalLikeAssign _LIBCPP_NODEBUG =
-      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>, is_assignable<_Tp&, _QualUp> >::value,
-           _CheckOptionalLikeConstructor<_QualUp>,
-           __check_tuple_constructor_fail >;
-
-public:
-  _LIBCPP_HIDE_FROM_ABI constexpr optional() noexcept {}
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(const optional&) = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(optional&&)      = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(nullopt_t) noexcept {}
-
-  template <class _InPlaceT,
-            class... _Args,
-            enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, is_constructible<_Tp, _Args...>>::value, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_InPlaceT, _Args&&... __args)
-      : __base(in_place, std::forward<_Args>(__args)...) {}
-
-  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
-      : __base(in_place, __il, std::forward<_Args>(__args)...) {}
-
-  template <class _Up = _Tp, enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
-      : __base(in_place, std::forward<_Up>(__v)) {}
-
-  template <class _Up                                                                        = remove_cv_t<_Tp>,
-            enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) noexcept(
-      is_nothrow_constructible_v<_Tp, _Up>) // strengthened
-      : __base(in_place, std::forward<_Up>(__v)) {}
-
-  // LWG2756: conditionally explicit conversion from const optional<_Up>&
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_implicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) {
-    this->__construct_from(__v);
-  }
-
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_explicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) {
-    this->__construct_from(__v);
-  }
-
-  // LWG2756: conditionally explicit conversion from optional<_Up>&&
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_implicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) {
-    this->__construct_from(std::move(__v));
-  }
-
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_explicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) {
-    this->__construct_from(std::move(__v));
-  }
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Tag,
-            class _Fp,
-            class... _Args,
-            enable_if_t<_IsSame<_Tag, __optional_construct_from_invoke_tag>::value, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
-      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(nullopt_t) noexcept {
-    reset();
-    return *this;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(const optional&) = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(optional&&)      = default;
-
-  // LWG2756
-  template <class _Up        = remove_cv_t<_Tp>,
-            enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>,
-                             _Or<_IsNotSame<__remove_cvref_t<_Up>, _Tp>, _Not<is_scalar<_Tp>>>,
-                             is_constructible<_Tp, _Up>,
-                             is_assignable<_Tp&, _Up>>::value,
-                        int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(_Up&& __v) {
-    if (this->has_value())
-      this->__get() = std::forward<_Up>(__v);
-    else
-      this->__construct(std::forward<_Up>(__v));
-    return *this;
-  }
-
-  // LWG2756
-  template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>::template __enable_assign<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(const optional<_Up>& __v) {
-    this->__assign_from(__v);
-    return *this;
-  }
-
-  // LWG2756
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_assign<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(optional<_Up>&& __v) {
-    this->__assign_from(std::move(__v));
-    return *this;
-  }
-
-  template <class... _Args, enable_if_t<is_constructible_v<_Tp, _Args...>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) {
-    reset();
-    this->__construct(std::forward<_Args>(__args)...);
-    return this->__get();
-  }
-
-  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) {
-    reset();
-    this->__construct(__il, std::forward<_Args>(__args)...);
-    return this->__get();
-  }
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-  swap(optional& __opt) noexcept((is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>)) {
-    this->__swap(__opt);
-  }
-
-  using __base::operator*;
-  using __base::operator->;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return has_value(); }
-
-  using __base::__get;
-  using __base::has_value;
-  using __base::value;
-
-  template <class _Up = remove_cv_t<_Tp>>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
-    static_assert(is_copy_constructible_v<_Tp>, "optional<T>::value_or: T must be copy constructible");
-    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
-    return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v));
-  }
-
-  template <class _Up = remove_cv_t<_Tp>>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
-    static_assert(is_move_constructible_v<_Tp>, "optional<T>::value_or: T must be move constructible");
-    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
-    return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v));
-  }
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
-    using _Up = invoke_result_t<_Func, _Tp&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), value());
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
-    using _Up = invoke_result_t<_Func, const _Tp&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), value());
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
-    using _Up = invoke_result_t<_Func, _Tp&&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(std::move(value())) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), std::move(value()));
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
-    using _Up = invoke_result_t<_Func, const _Tp&&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(std::move(value())) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), std::move(value()));
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & {
-    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
-    return optional<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& {
-    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
-    return optional<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && {
-    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
-    return optional<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& {
-    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
-    return optional<_Up>();
-  }
-
-  template <invocable _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const&
-    requires is_copy_constructible_v<_Tp>
-  {
-    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
-                  "Result of f() should be the same type as this optional");
-    if (*this)
-      return *this;
-    return std::forward<_Func>(__f)();
-  }
-
-  template <invocable _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) &&
-    requires is_move_constructible_v<_Tp>
-  {
-    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
-                  "Result of f() should be the same type as this optional");
-    if (*this)
-      return std::move(*this);
-    return std::forward<_Func>(__f)();
-  }
-#    endif // _LIBCPP_STD_VER >= 23
-
-  using __base::reset;
-};
-
-#    if _LIBCPP_STD_VER >= 26
-template <class _Tp>
-class optional<_Tp&> : public __optional_iterator_base<_Tp&> {
-  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp&>;
-
-  template <class _Up, class _QualUp>
-  static constexpr bool __check_optionalU_ctor =
-      !is_same_v<remove_cv_t<_Tp>, optional<_Up>> && !is_same_v<_Tp&, _Up> && is_constructible_v<_Tp&, _QualUp>;
-  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
-  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-
-public:
-  using value_type = _Tp;
-
-  constexpr optional() noexcept = default;
-  constexpr optional(nullopt_t) noexcept {}
-  constexpr optional(const optional&) noexcept = default;
-
-  template <class _Arg>
-    requires(is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>)
-  constexpr explicit optional(in_place_t, _Arg&& __arg) : __base(in_place, std::forward<_Arg>(__arg)) {}
-
-  template <class _Up>
-    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
-             is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>) optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>)
-      : __base(in_place, std::forward<_Up>(__v)) {}
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up&> && !reference_constructs_from_temporary_v<_Tp&, _Up&>)
-  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
-      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) {
-    this->__construct_from(__rhs);
-  }
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up&> && !reference_constructs_from_temporary_v<_Tp&, const _Up&>)
-  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
-      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) {
-    this->__construct_from(__rhs);
-  }
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
-      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
-    this->__construct_from(std::move(__rhs));
-  }
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up> && !reference_constructs_from_temporary_v<_Tp&, const _Up>)
-  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
-      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) {
-    this->__construct_from(std::move(__rhs));
-  }
-
-  template <class _Tag, class _Fp, class... _Args>
-    requires(is_same_v<_Tag, __optional_construct_from_invoke_tag>)
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
-      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
-
-  // deleted overloads
-
-  template <class _Up>
-    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
-             is_constructible_v<_Tp&, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
-      optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up&> && reference_constructs_from_temporary_v<_Tp&, _Up&>)
-  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
-      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up&> && reference_constructs_from_temporary_v<_Tp&, const _Up&>)
-  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
-      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
-      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up> && reference_constructs_from_temporary_v<_Tp&, const _Up>)
-  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
-      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) = delete;
-
-  constexpr ~optional() = default;
-
-  using __base::__get;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(nullopt_t) noexcept {
-    reset();
-    return *this;
-  }
-
-  constexpr optional& operator=(const optional&) noexcept = default;
-
-  template <class _Up>
-    requires(is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr _Tp& emplace(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
-    this->__construct(std::forward<_Up>(__u));
-
-    return this->__get();
-  }
-
-  constexpr void swap(optional& __rhs) noexcept { this->__swap(__rhs); }
-
-  using __base::operator->;
-  using __base::operator*;
-
-  constexpr explicit operator bool() const noexcept { return has_value(); }
-
-  using __base::has_value;
-  using __base::value;
-
-  template <class _Up = remove_cv_t<_Tp>>
-    requires(!is_array_v<_Tp> && is_object_v<_Tp>)
-  [[nodiscard]] constexpr decay_t<_Tp> value_or(_Up&& __v) const {
-    using _XTp = remove_cv_t<_Tp>;
-    static_assert(is_constructible_v<_XTp, _Tp&>, "optional<T&>::value_or: remove_cv_t<T> must be constructible");
-    static_assert(is_convertible_v<_Up, _XTp>, "optional<T&>::value_or: U must be convertible to remove_cv_t<T>");
-    return this->has_value() ? this->__get() : static_cast<_XTp>(std::forward<_Up>(__v));
-  }
-
-  template <class _Func>
-  [[nodiscard]] constexpr auto and_then(_Func&& __f) const {
-    using _Up = invoke_result_t<_Func, _Tp&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), value());
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] constexpr optional<remove_cv_t<invoke_result_t<_Func, _Tp&>>> transform(_Func&& __f) const {
-    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
-    return optional<_Up>();
-  }
-
-  template <invocable _Func>
-  [[nodiscard]] constexpr optional or_else(_Func&& __f) const {
-    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
-                  "Result of f() should be the same type as this optional");
-    if (*this)
-      return *this;
-    return std::forward<_Func>(__f)();
-  }
-
-  using __base::reset;
-};
-#    endif
-
 template <class _Tp>
 optional(_Tp) -> optional<_Tp>;
 
-// [optional.relops] Relational operators
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (static_cast<bool>(__x) != static_cast<bool>(__y))
-    return false;
-  if (!static_cast<bool>(__x))
-    return true;
-  return *__x == *__y;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (static_cast<bool>(__x) != static_cast<bool>(__y))
-    return true;
-  if (!static_cast<bool>(__x))
-    return false;
-  return *__x != *__y;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__y))
-    return false;
-  if (!static_cast<bool>(__x))
-    return true;
-  return *__x < *__y;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__x))
-    return false;
-  if (!static_cast<bool>(__y))
-    return true;
-  return *__x > *__y;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__x))
-    return true;
-  if (!static_cast<bool>(__y))
-    return false;
-  return *__x <= *__y;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__y))
-    return true;
-  if (!static_cast<bool>(__x))
-    return false;
-  return *__x >= *__y;
-}
-
-#    if _LIBCPP_STD_VER >= 20
-
-template <class _Tp, three_way_comparable_with<_Tp> _Up>
-_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
-operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (__x && __y)
-    return *__x <=> *__y;
-  return __x.has_value() <=> __y.has_value();
-}
-
-#    endif // _LIBCPP_STD_VER >= 20
-
-// [optional.nullops] Comparison with nullopt
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-#    if _LIBCPP_STD_VER <= 17
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>&, nullopt_t) noexcept {
-  return false;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(nullopt_t, const optional<_Tp>&) noexcept {
-  return true;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(nullopt_t, const optional<_Tp>&) noexcept {
-  return false;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>&, nullopt_t) noexcept {
-  return true;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-#    else // _LIBCPP_STD_VER <= 17
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return __x.has_value() <=> false;
-}
-
-#    endif // _LIBCPP_STD_VER <= 17
-
-// [optional.comp.with.t] Comparison with T
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x == __v;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v == *__x;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x != __v;
-  return true;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v != *__x;
-  return true;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x < __v;
-  return true;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v < *__x;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x <= __v;
-  return true;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v <= *__x;
-  return false;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x > __v;
-  return false;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v > *__x;
-  return true;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x >= __v;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v >= *__x;
-  return true;
-}
-
-#    if _LIBCPP_STD_VER >= 20
-
-template <class _Tp, class _Up>
-  requires(!__is_derived_from_optional<_Up>) && three_way_comparable_with<_Tp, _Up>
-_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
-operator<=>(const optional<_Tp>& __x, const _Up& __v) {
-  return __x.has_value() ? *__x <=> __v : strong_ordering::less;
-}
-
-#    endif // _LIBCPP_STD_VER >= 20
-
-template <class _Tp,
-          enable_if_t<(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>)
-#    if _LIBCPP_STD_VER >= 26
-                          || is_reference_v<_Tp>
-#    endif
-                      ,
-                      int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) {
-  __x.swap(__y);
-}
-
 struct __make_optional_barrier_tag {
   explicit __make_optional_barrier_tag() = default;
 };
@@ -1741,18 +353,6 @@ make_optional(initializer_list<_Up> __il, _Args&&... __args) {
   return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...);
 }
 
-template <class _Tp>
-struct hash< __enable_hash_helper<optional<_Tp>, remove_const_t<_Tp>> > {
-#    if _LIBCPP_STD_VER <= 17 || defined(_LIBCPP_ENABLE_CXX20_REMOVED_BINDER_TYPEDEFS)
-  _LIBCPP_DEPRECATED_IN_CXX17 typedef optional<_Tp> argument_type;
-  _LIBCPP_DEPRECATED_IN_CXX17 typedef size_t result_type;
-#    endif
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t operator()(const optional<_Tp>& __opt) const {
-    return static_cast<bool>(__opt) ? hash<remove_const_t<_Tp>>()(*__opt) : 0;
-  }
-};
-
 _LIBCPP_END_NAMESPACE_STD
 
 #  endif // _LIBCPP_STD_VER >= 17
diff --git a/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.ops/ostream_joiner.op.assign.pass.cpp b/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.ops/ostream_joiner.op.assign.pass.cpp
index 7a193e839f2a3..f7e5bdcccbeef 100644
--- a/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.ops/ostream_joiner.op.assign.pass.cpp
+++ b/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.ops/ostream_joiner.op.assign.pass.cpp
@@ -22,6 +22,7 @@
 #include <iostream>
 #include <sstream>
 #include <cassert>
+#include <type_traits>
 
 #include "test_macros.h"
 #include "test_iterators.h"
diff --git a/libcxx/test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp b/libcxx/test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp
index 9cf3b08930121..606ee4002040b 100644
--- a/libcxx/test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp
@@ -12,6 +12,7 @@
 
 // class bad_optional_access : public exception
 
+#include <exception>
 #include <optional>
 #include <type_traits>
 
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp
index 00ca941668eb2..a13b4334da332 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp
@@ -26,26 +26,26 @@ int main(int, char**) {
   // clang-format off
   {
 #if TEST_STD_VER >= 26
-    std::optional<int&&> opt2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
+    std::optional<int&&> opt2; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
 #else
-    std::optional<char&> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
+    std::optional<char&> o1; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
 #endif
-    std::optional<NonDestructible> o2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
-    std::optional<char[20]> o3; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}}
+    std::optional<NonDestructible> o2; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+    std::optional<char[20]> o3; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}}
   }
 
   {
-    std::optional<               std::in_place_t> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
-    std::optional<const          std::in_place_t> o2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
-    std::optional<      volatile std::in_place_t> o3; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
-    std::optional<const volatile std::in_place_t> o4; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
+    std::optional<               std::in_place_t> o1; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
+    std::optional<const          std::in_place_t> o2; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
+    std::optional<      volatile std::in_place_t> o3; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
+    std::optional<const volatile std::in_place_t> o4; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
   }
 
   {
-    std::optional<               std::nullopt_t> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
-    std::optional<const          std::nullopt_t> o2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
-    std::optional<      volatile std::nullopt_t> o3; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
-    std::optional<const volatile std::nullopt_t> o4; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional<               std::nullopt_t> o1; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional<const          std::nullopt_t> o2; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional<      volatile std::nullopt_t> o3; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional<const volatile std::nullopt_t> o4; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
   }
   // clang-format on
   return 0;
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/deduct.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/deduct.verify.cpp
index 7ab842bf68c51..28d3c61db4a9a 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/deduct.verify.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/deduct.verify.cpp
@@ -31,7 +31,7 @@ int main(int, char**) {
 
   {
     //  optional(nullopt_t)
-    std::optional opt(std::nullopt); // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional opt(std::nullopt); // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
   }
   // clang-format on
   return 0;
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp
index a956ab3a219cf..cdf456c4154ff 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp
@@ -28,30 +28,30 @@ int main(int, char**)
     using std::optional;
     {
 #if TEST_STD_VER >= 26
-      // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
 #else
-      // expected-error-re at optional:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
+      // expected-error-re@*:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
 #endif
       optional<int&> opt1;
       optional<int&&> opt2;
     }
     {
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
-        optional<X> opt3;
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+      optional<X> opt3;
     }
     {
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-object type is undefined behavior}}
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
-        optional<void()> opt4;
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-object type is undefined behavior}}
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+      optional<void()> opt4;
     }
     {
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-object type is undefined behavior}}
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
-        // expected-error at optional:* 1+ {{cannot form a reference to 'void'}}
-        optional<const void> opt4;
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-object type is undefined behavior}}
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+      // expected-error@*:* 1+ {{cannot form a reference to 'void'}}
+      optional<const void> opt4;
     }
     // FIXME these are garbage diagnostics that Clang should not produce
-    // expected-error at optional:* 0+ {{is not a base class}}
+    // expected-error@*:* 0+ {{is not a base class}}
 
-  return 0;
+    return 0;
 }

>From 6f2ce7fc219f53aa526afe3448f4a04094cd0b04 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 30 Jun 2026 00:57:53 -0400
Subject: [PATCH 2/4] Formatting

---
 libcxx/include/__optional/common.h       | 12 +++----
 libcxx/include/__optional/optional.h     | 44 ++++++++++++------------
 libcxx/include/__optional/optional_ref.h | 18 +++++-----
 3 files changed, 37 insertions(+), 37 deletions(-)

diff --git a/libcxx/include/__optional/common.h b/libcxx/include/__optional/common.h
index 2b4862cfd9d34..eb6cff368789a 100644
--- a/libcxx/include/__optional/common.h
+++ b/libcxx/include/__optional/common.h
@@ -64,18 +64,18 @@ struct __is_std_optional : false_type {};
 template <class _Tp>
 struct __is_std_optional<optional<_Tp>> : true_type {};
 
-template<class _Tp, bool = is_reference_v<_Tp>>
+template <class _Tp, bool = is_reference_v<_Tp>>
 struct __optional_storage_base;
 
-#    if _LIBCPP_STD_VER < 26
+#  if _LIBCPP_STD_VER < 26
 template <class _Tp>
 inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
-#    else
+#  else
 template <class _Tp>
 inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp> || is_lvalue_reference_v<_Tp>;
-#    endif
+#  endif
 
-#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
 constexpr bool ranges::enable_view<optional<_Tp>> = true;
@@ -86,7 +86,7 @@ constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
 template <class _Tp>
 constexpr bool ranges::enable_borrowed_range<optional<_Tp&>> = true;
 
-#    endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index d01d8e661bdca..148b28e04d9ca 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -96,12 +96,12 @@ struct __optional_destruct_base<_Tp, false> {
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
       : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <class _Fp, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(
       __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
       : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
     if (__engaged_) {
@@ -127,12 +127,12 @@ struct __optional_destruct_base<_Tp, true> {
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
       : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <class _Fp, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
       __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
       : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
     if (__engaged_) {
@@ -358,7 +358,7 @@ struct __optional_iterator_base : __optional_move_assign_base<_Tp> {
   using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
 };
 
-#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
   requires is_object_v<_Tp>
@@ -370,33 +370,33 @@ struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
 public:
   using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
   using iterator       = __bounded_iter<__pointer>;
   using const_iterator = __bounded_iter<__const_pointer>;
-#        else
+#    else
   using iterator       = __capacity_aware_iterator<__pointer, optional<_Tp>, 1>;
   using const_iterator = __capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>;
-#        endif
+#    endif
 
   // [optional.iterators], iterator support
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
     auto* __ptr = std::addressof(this->__get());
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
     return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
+#    else
     return std::__make_capacity_aware_iterator<__pointer, optional<_Tp>, 1>(__ptr);
-#        endif
+#    endif
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
     auto* __ptr = std::addressof(this->__get());
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
     return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
+#    else
     return std::__make_capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>(__ptr);
-#        endif
+#    endif
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
@@ -407,7 +407,7 @@ struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
   }
 };
 
-#    endif   // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
 class _LIBCPP_DECLSPEC_EMPTY_BASES optional
@@ -425,11 +425,11 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
 private:
   static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
   static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-#    if _LIBCPP_STD_VER >= 26
+#  if _LIBCPP_STD_VER >= 26
   static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
-#    else
+#  else
   static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
-#    endif
+#  endif
   static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
   static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
 
@@ -546,14 +546,14 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
     this->__construct_from(std::move(__v));
   }
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <class _Tag,
             class _Fp,
             class... _Args,
             enable_if_t<_IsSame<_Tag, __optional_construct_from_invoke_tag>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
       : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
-#    endif
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(nullopt_t) noexcept {
     reset();
@@ -634,7 +634,7 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
     return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v));
   }
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <class _Func>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
     using _Up = invoke_result_t<_Func, _Tp&>;
@@ -748,7 +748,7 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
       return std::move(*this);
     return std::forward<_Func>(__f)();
   }
-#    endif // _LIBCPP_STD_VER >= 23
+#  endif // _LIBCPP_STD_VER >= 23
 
   using __base::reset;
 };
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index dd1adc1642ca3..eccf925f525d6 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -143,12 +143,12 @@ struct __optional_storage_base<_Tp, true> {
 template <class _Tp>
 struct __optional_iterator_base;
 
-template<class _Tp>
+template <class _Tp>
 struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
   using __optional_storage_base<_Tp&>::__optional_storage_base;
 };
 
-#if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
   requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
@@ -159,22 +159,22 @@ struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
 public:
   using __optional_storage_base<_Tp&>::__optional_storage_base;
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
   using iterator = __bounded_iter<__pointer>;
-#        else
+#    else
   using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
-#        endif
+#    endif
 
   // [optional.ref.iterators], iterator support
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
     auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
     return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
+#    else
     return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
-#        endif
+#    endif
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
@@ -182,7 +182,7 @@ struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
   }
 };
 
-#endif
+#  endif
 
 template <class _Tp>
 class optional<_Tp&> : public __optional_iterator_base<_Tp&> {

>From b5ade2cb1b1469b78d68e21a08de4c0f5012f2dc Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 30 Jun 2026 18:43:58 -0400
Subject: [PATCH 3/4] Address comments

---
 libcxx/include/__fwd/optional.h          |  3 +++
 libcxx/include/__optional/common.h       |  9 +++------
 libcxx/include/__optional/hash.h         |  3 +--
 libcxx/include/__optional/optional.h     |  2 +-
 libcxx/include/__optional/optional_ref.h | 24 ++++++++++++------------
 5 files changed, 20 insertions(+), 21 deletions(-)

diff --git a/libcxx/include/__fwd/optional.h b/libcxx/include/__fwd/optional.h
index 35a3d2ea11e74..303c2d703e551 100644
--- a/libcxx/include/__fwd/optional.h
+++ b/libcxx/include/__fwd/optional.h
@@ -27,6 +27,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Tp>
 class optional;
 
+template <class _Tp>
+class optional<_Tp&>;
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/common.h b/libcxx/include/__optional/common.h
index eb6cff368789a..609d93fa38443 100644
--- a/libcxx/include/__optional/common.h
+++ b/libcxx/include/__optional/common.h
@@ -34,9 +34,9 @@ namespace std // purposefully not using versioning namespace
 
 class _LIBCPP_EXPORTED_FROM_ABI bad_optional_access : public exception {
 public:
-  bad_optional_access() _NOEXCEPT                                      = default;
-  bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
-  bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
+  _LIBCPP_HIDE_FROM_ABI bad_optional_access() _NOEXCEPT                                      = default;
+  _LIBCPP_HIDE_FROM_ABI bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
+  _LIBCPP_HIDE_FROM_ABI bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
   // Get the key function ~bad_optional_access() into the dylib
   ~bad_optional_access() _NOEXCEPT override;
   [[__nodiscard__]] const char* what() const _NOEXCEPT override;
@@ -64,9 +64,6 @@ struct __is_std_optional : false_type {};
 template <class _Tp>
 struct __is_std_optional<optional<_Tp>> : true_type {};
 
-template <class _Tp, bool = is_reference_v<_Tp>>
-struct __optional_storage_base;
-
 #  if _LIBCPP_STD_VER < 26
 template <class _Tp>
 inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
diff --git a/libcxx/include/__optional/hash.h b/libcxx/include/__optional/hash.h
index 830f99b0b855f..ac86b2fb63488 100644
--- a/libcxx/include/__optional/hash.h
+++ b/libcxx/include/__optional/hash.h
@@ -13,9 +13,8 @@
 #include <__config>
 #include <__cstddef/size_t.h>
 #include <__functional/hash.h>
-#include <__type_traits/remove_const.h>
-
 #include <__fwd/optional.h>
+#include <__type_traits/remove_const.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index 148b28e04d9ca..21a4f88968378 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -141,7 +141,7 @@ struct __optional_destruct_base<_Tp, true> {
   }
 };
 
-template <class _Tp, bool>
+template <class _Tp>
 struct __optional_storage_base : __optional_destruct_base<_Tp> {
   using __base _LIBCPP_NODEBUG = __optional_destruct_base<_Tp>;
   using value_type             = _Tp;
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index eccf925f525d6..093e29080cecf 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -58,12 +58,12 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp>
-struct __optional_storage_base<_Tp, true> {
+struct __optional_ref_base {
   using value_type                 = _Tp;
   using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
   __raw_type* __value_;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_ref_base() noexcept : __value_(nullptr) {}
 
   template <class _Up>
   _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
@@ -72,7 +72,7 @@ struct __optional_storage_base<_Tp, true> {
   }
 
   template <class _UArg>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) {
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_ref_base(in_place_t, _UArg&& __uarg) {
     static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
                   "Attempted to construct a reference element in optional from a "
                   "possible temporary");
@@ -80,7 +80,7 @@ struct __optional_storage_base<_Tp, true> {
   }
 
   template <class _Fp, class... _Args>
-  constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
+  constexpr __optional_ref_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
     __convert_init_ref_val(std::forward<invoke_result_t<_Fp, _Args...>>(
         std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)));
   }
@@ -118,7 +118,7 @@ struct __optional_storage_base<_Tp, true> {
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_ref_base& __rhs) noexcept {
     std::swap(__value_, __rhs.__value_);
   }
 
@@ -141,23 +141,23 @@ struct __optional_storage_base<_Tp, true> {
 };
 
 template <class _Tp>
-struct __optional_iterator_base;
+struct __optional_ref_iterator_base;
 
 template <class _Tp>
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
+struct __optional_ref_iterator_base<_Tp&> : __optional_ref_base<_Tp&> {
+  using __optional_ref_base<_Tp&>::__optional_ref_base;
 };
 
 #  if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
   requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
+struct __optional_ref_iterator_base<_Tp&> : __optional_ref_base<_Tp&> {
 private:
   using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
 
 public:
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
+  using __optional_ref_base<_Tp&>::__optional_ref_base;
 
 #    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
   using iterator = __bounded_iter<__pointer>;
@@ -185,8 +185,8 @@ struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
 #  endif
 
 template <class _Tp>
-class optional<_Tp&> : public __optional_iterator_base<_Tp&> {
-  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp&>;
+class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
+  using __base _LIBCPP_NODEBUG = __optional_ref_iterator_base<_Tp&>;
 
   template <class _Up, class _QualUp>
   static constexpr bool __check_optionalU_ctor =

>From 79ea6f9fff2345447e181e97c7d65270919f531a Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 16 Jul 2026 19:51:55 -0400
Subject: [PATCH 4/4] Make_optional to header, deduction guide to forward
 declaration, partial specializations to maintain similar error message

---
 libcxx/include/__fwd/optional.h           |  3 +
 libcxx/include/__optional/make_optional.h | 90 +++++++++++++++++++++++
 libcxx/include/__optional/optional.h      | 11 +--
 libcxx/include/__optional/optional_ref.h  | 11 +++
 libcxx/include/optional                   | 75 +------------------
 5 files changed, 111 insertions(+), 79 deletions(-)
 create mode 100644 libcxx/include/__optional/make_optional.h

diff --git a/libcxx/include/__fwd/optional.h b/libcxx/include/__fwd/optional.h
index 303c2d703e551..1e74a75ea0dbe 100644
--- a/libcxx/include/__fwd/optional.h
+++ b/libcxx/include/__fwd/optional.h
@@ -30,6 +30,9 @@ class optional;
 template <class _Tp>
 class optional<_Tp&>;
 
+template <class _Tp>
+optional(_Tp) -> optional<_Tp>;
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/make_optional.h b/libcxx/include/__optional/make_optional.h
new file mode 100644
index 0000000000000..202e09071116d
--- /dev/null
+++ b/libcxx/include/__optional/make_optional.h
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// std::optional<T&>
+
+#ifndef _LIBCPP_OPTIONAL_MAKE_OPTIONAL_H
+#define _LIBCPP_OPTIONAL_MAKE_OPTIONAL_H
+
+#include <__config>
+#include <__fwd/optional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/reference_constructs_from_temporary.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+
+#include <initializer_list>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+struct __make_optional_barrier_tag {
+  explicit __make_optional_barrier_tag() = default;
+};
+
+template <class _Tp, class... _Args>
+inline constexpr bool __is_constructible_for_optional_v = is_constructible_v<_Tp, _Args...>;
+
+template <class _Tp, class... _Args>
+struct __is_constructible_for_optional : bool_constant<__is_constructible_for_optional_v<_Tp, _Args...>> {};
+
+template <class _Tp, class _Up, class... _Args>
+inline constexpr bool __is_constructible_for_optional_initializer_list_v =
+    is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>;
+
+#  if _LIBCPP_STD_VER >= 26
+template <class _Tp, class... _Args>
+inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Args...> = false;
+
+template <class _Tp, class _Arg>
+inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Arg> =
+    is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>;
+
+template <class _Tp, class _Up, class... _Args>
+inline constexpr bool __is_constructible_for_optional_initializer_list_v<_Tp&, _Up, _Args...> = false;
+#  endif
+
+template <
+#  if _LIBCPP_STD_VER >= 26
+    __make_optional_barrier_tag = __make_optional_barrier_tag{},
+#  endif
+    class _Tp,
+    enable_if_t<is_constructible_v<decay_t<_Tp>, _Tp>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<decay_t<_Tp>> make_optional(_Tp&& __v) {
+  return optional<decay_t<_Tp>>(std::forward<_Tp>(__v));
+}
+
+template <class _Tp, class... _Args, enable_if_t<__is_constructible_for_optional_v<_Tp, _Args...>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(_Args&&... __args) {
+  return optional<_Tp>(in_place, std::forward<_Args>(__args)...);
+}
+
+template <class _Tp,
+          class _Up,
+          class... _Args,
+          enable_if_t<__is_constructible_for_optional_initializer_list_v<_Tp, _Up, _Args...>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp>
+make_optional(initializer_list<_Up> __il, _Args&&... __args) {
+  return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+#endif // _LIBCPP_OPTIONAL_MAKE_OPTIONAL_H
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index 21a4f88968378..4726b706cca84 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -425,11 +425,6 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
 private:
   static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
   static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-#  if _LIBCPP_STD_VER >= 26
-  static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
-#  else
-  static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
-#  endif
   static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
   static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
 
@@ -752,6 +747,12 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
 
   using __base::reset;
 };
+
+template <class _Tp>
+class optional<_Tp&&> {
+  static_assert(false, "instantiation of optional with an rvalue reference type is ill-formed");
+};
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index 093e29080cecf..9bd2831fe0d1a 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -348,6 +348,17 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
 
 _LIBCPP_END_NAMESPACE_STD
 
+#else
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+class optional<_Tp&> {
+  static_assert(false, "instantiation of optional with a reference type is ill-formed");
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
 #endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_POP_MACROS
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 2b40706910018..914fd8cc9e226 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -259,19 +259,12 @@ namespace std {
 #  include <__cxx03/__config>
 #else
 #  include <__config>
-#  include <__type_traits/decay.h>
-#  include <__type_traits/enable_if.h>
-#  include <__type_traits/is_constructible.h>
-#  include <__type_traits/reference_constructs_from_temporary.h>
-#  include <__type_traits/remove_const.h>
-#  include <__utility/forward.h>
-#  include <__utility/in_place.h>
-#  include <initializer_list>
 #  include <version>
 
 #  include <__optional/common.h>
 #  include <__optional/comparison.h>
 #  include <__optional/hash.h>
+#  include <__optional/make_optional.h>
 #  include <__optional/optional.h>
 #  include <__optional/optional_ref.h>
 #  include <__optional/swap.h>
@@ -293,72 +286,6 @@ namespace std {
 #    pragma GCC system_header
 #  endif
 
-_LIBCPP_PUSH_MACROS
-#  include <__undef_macros>
-
-#  if _LIBCPP_STD_VER >= 17
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-template <class _Tp>
-optional(_Tp) -> optional<_Tp>;
-
-struct __make_optional_barrier_tag {
-  explicit __make_optional_barrier_tag() = default;
-};
-
-template <class _Tp, class... _Args>
-inline constexpr bool __is_constructible_for_optional_v = is_constructible_v<_Tp, _Args...>;
-
-template <class _Tp, class... _Args>
-struct __is_constructible_for_optional : bool_constant<__is_constructible_for_optional_v<_Tp, _Args...>> {};
-
-template <class _Tp, class _Up, class... _Args>
-inline constexpr bool __is_constructible_for_optional_initializer_list_v =
-    is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>;
-
-#    if _LIBCPP_STD_VER >= 26
-template <class _Tp, class... _Args>
-inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Args...> = false;
-
-template <class _Tp, class _Arg>
-inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Arg> =
-    is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>;
-
-template <class _Tp, class _Up, class... _Args>
-inline constexpr bool __is_constructible_for_optional_initializer_list_v<_Tp&, _Up, _Args...> = false;
-#    endif
-
-template <
-#    if _LIBCPP_STD_VER >= 26
-    __make_optional_barrier_tag = __make_optional_barrier_tag{},
-#    endif
-    class _Tp,
-    enable_if_t<is_constructible_v<decay_t<_Tp>, _Tp>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<decay_t<_Tp>> make_optional(_Tp&& __v) {
-  return optional<decay_t<_Tp>>(std::forward<_Tp>(__v));
-}
-
-template <class _Tp, class... _Args, enable_if_t<__is_constructible_for_optional_v<_Tp, _Args...>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(_Args&&... __args) {
-  return optional<_Tp>(in_place, std::forward<_Args>(__args)...);
-}
-
-template <class _Tp,
-          class _Up,
-          class... _Args,
-          enable_if_t<__is_constructible_for_optional_initializer_list_v<_Tp, _Up, _Args...>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp>
-make_optional(initializer_list<_Up> __il, _Args&&... __args) {
-  return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...);
-}
-
-_LIBCPP_END_NAMESPACE_STD
-
-#  endif // _LIBCPP_STD_VER >= 17
-
-_LIBCPP_POP_MACROS
-
 #  if defined(_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23) && _LIBCPP_STD_VER <= 20
 #    include <atomic>
 #    include <climits>


From libcxx-commits at lists.llvm.org  Thu Jul 16 22:11:12 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Thu, 16 Jul 2026 22:11:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][docs] Mark LWG3116 as Complete
	(PR #209916)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209916@github.com>
Message-ID: <6a59b970.4a342cbc.3a2f36.1006@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/209916

From libcxx-commits at lists.llvm.org  Thu Jul 16 22:37:56 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Thu, 16 Jul 2026 22:37:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::swap of locales
	(PR #209760)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209760@github.com>
Message-ID: <6a59bfb4.92d2873c.aa1ff.3828@mx.google.com>


================
@@ -143,8 +143,14 @@ private:
   friend bool has_facet(const locale&) _NOEXCEPT;
   template <class _Facet>
   friend const _Facet& use_facet(const locale&);
+
+  friend void swap(locale&, locale&);
 };
 
+inline void swap(locale& __lhs, locale& __rhs) {
----------------
frederick-vs-ja wrote:

> The standard doesn't allow for a move constructor.

I think the current approach is already slightly non-conforming as it slightly changes the behavior for `std::swap` in overload resolution. See [this reduced example](https://godbolt.org/z/e3vqGsdfK).

Perhaps we should submit an LWG issue to add missing move functions to `locale`.

https://github.com/llvm/llvm-project/pull/209760

From libcxx-commits at lists.llvm.org  Thu Jul 16 23:08:52 2026
From: libcxx-commits at lists.llvm.org (Yihan Wang via libcxx-commits)
Date: Thu, 16 Jul 2026 23:08:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2255R2: Add deleted tuple
 constructor overloads (PR #205379)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205379@github.com>
Message-ID: <6a59c6f4.f375f8af.23642c.a4fc@mx.google.com>


yronglin wrote:

@frederick-vs-ja Friendly ping~

https://github.com/llvm/llvm-project/pull/205379

From libcxx-commits at lists.llvm.org  Thu Jul 16 23:58:14 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Thu, 16 Jul 2026 23:58:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Granularize `<optional>` (PR
	#206644)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206644@github.com>
Message-ID: <6a59d286.13be6df8.22eac4.bb36@mx.google.com>


https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/206644

>From e41c4724ed356f1a4f1c96c0cace13684695f5cb Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 12 Jun 2026 23:22:27 -0400
Subject: [PATCH 1/8] Granularize optional

---
 libcxx/include/CMakeLists.txt                 |    8 +
 libcxx/include/__fwd/optional.h               |   36 +
 libcxx/include/__optional/common.h            |   97 ++
 libcxx/include/__optional/comparison.h        |  347 ++++
 libcxx/include/__optional/hash.h              |   48 +
 libcxx/include/__optional/nullopt_t.h         |   40 +
 libcxx/include/__optional/optional.h          |  761 +++++++++
 libcxx/include/__optional/optional_ref.h      |  355 ++++
 libcxx/include/__optional/swap.h              |   45 +
 libcxx/include/module.modulemap.in            |   19 +
 libcxx/include/optional                       | 1432 +----------------
 .../ostream_joiner.op.assign.pass.cpp         |    1 +
 .../derive.pass.cpp                           |    1 +
 .../optional.object.ctor/ctor.verify.cpp      |   24 +-
 .../optional.object.ctor/deduct.verify.cpp    |    2 +-
 ...al_requires_destructible_object.verify.cpp |   26 +-
 16 files changed, 1800 insertions(+), 1442 deletions(-)
 create mode 100644 libcxx/include/__fwd/optional.h
 create mode 100644 libcxx/include/__optional/common.h
 create mode 100644 libcxx/include/__optional/comparison.h
 create mode 100644 libcxx/include/__optional/hash.h
 create mode 100644 libcxx/include/__optional/nullopt_t.h
 create mode 100644 libcxx/include/__optional/optional.h
 create mode 100644 libcxx/include/__optional/optional_ref.h
 create mode 100644 libcxx/include/__optional/swap.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index cec9ba06a7a6d..021d392e50c78 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -468,6 +468,7 @@ set(files
   __fwd/mdspan.h
   __fwd/memory.h
   __fwd/memory_resource.h
+  __fwd/optional.h
   __fwd/ostream.h
   __fwd/pair.h
   __fwd/queue.h
@@ -661,6 +662,13 @@ set(files
   __numeric/transform_exclusive_scan.h
   __numeric/transform_inclusive_scan.h
   __numeric/transform_reduce.h
+  __optional/common.h
+  __optional/comparison.h
+  __optional/hash.h
+  __optional/nullopt_t.h
+  __optional/optional.h
+  __optional/optional_ref.h
+  __optional/swap.h
   __ostream/basic_ostream.h
   __ostream/print.h
   __ostream/put_character_sequence.h
diff --git a/libcxx/include/__fwd/optional.h b/libcxx/include/__fwd/optional.h
new file mode 100644
index 0000000000000..35a3d2ea11e74
--- /dev/null
+++ b/libcxx/include/__fwd/optional.h
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// std::optional<T&>
+
+#ifndef _LIBCPP___FWD_OPTIONAL_H
+#define _LIBCPP___FWD_OPTIONAL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+class optional;
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___FWD_OPTIONAL_H
diff --git a/libcxx/include/__optional/common.h b/libcxx/include/__optional/common.h
new file mode 100644
index 0000000000000..2b4862cfd9d34
--- /dev/null
+++ b/libcxx/include/__optional/common.h
@@ -0,0 +1,97 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_COMMON_H
+#define _LIBCPP_OPTIONAL_COMMON_H
+
+#include <__assert>
+#include <__config>
+#include <__exception/exception.h>
+#include <__fwd/format.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/enable_view.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+
+#include <__fwd/optional.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+namespace std // purposefully not using versioning namespace
+{
+
+class _LIBCPP_EXPORTED_FROM_ABI bad_optional_access : public exception {
+public:
+  bad_optional_access() _NOEXCEPT                                      = default;
+  bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
+  bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
+  // Get the key function ~bad_optional_access() into the dylib
+  ~bad_optional_access() _NOEXCEPT override;
+  [[__nodiscard__]] const char* what() const _NOEXCEPT override;
+};
+
+} // namespace std
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+[[noreturn]] inline void __throw_bad_optional_access() {
+#  if _LIBCPP_HAS_EXCEPTIONS
+  throw bad_optional_access();
+#  else
+  _LIBCPP_VERBOSE_ABORT("bad_optional_access was thrown in -fno-exceptions mode");
+#  endif
+}
+
+struct __optional_construct_from_invoke_tag {};
+
+template <class _Tp>
+struct __is_std_optional : false_type {};
+
+template <class _Tp>
+struct __is_std_optional<optional<_Tp>> : true_type {};
+
+template<class _Tp, bool = is_reference_v<_Tp>>
+struct __optional_storage_base;
+
+#    if _LIBCPP_STD_VER < 26
+template <class _Tp>
+inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
+#    else
+template <class _Tp>
+inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp> || is_lvalue_reference_v<_Tp>;
+#    endif
+
+#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+constexpr bool ranges::enable_view<optional<_Tp>> = true;
+
+template <class _Tp>
+constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
+
+template <class _Tp>
+constexpr bool ranges::enable_borrowed_range<optional<_Tp&>> = true;
+
+#    endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif
diff --git a/libcxx/include/__optional/comparison.h b/libcxx/include/__optional/comparison.h
new file mode 100644
index 0000000000000..10c109fac7605
--- /dev/null
+++ b/libcxx/include/__optional/comparison.h
@@ -0,0 +1,347 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_COMPARISON_H
+#define _LIBCPP_OPTIONAL_COMPARISON_H
+
+#include <__compare/compare_three_way_result.h>
+#include <__compare/ordering.h>
+#include <__compare/three_way_comparable.h>
+#include <__config>
+#include <__optional/nullopt_t.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_core_convertible.h>
+#include <__type_traits/is_swappable.h>
+#include <__utility/declval.h>
+
+#include <__fwd/optional.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (static_cast<bool>(__x) != static_cast<bool>(__y))
+    return false;
+  if (!static_cast<bool>(__x))
+    return true;
+  return *__x == *__y;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (static_cast<bool>(__x) != static_cast<bool>(__y))
+    return true;
+  if (!static_cast<bool>(__x))
+    return false;
+  return *__x != *__y;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (!static_cast<bool>(__y))
+    return false;
+  if (!static_cast<bool>(__x))
+    return true;
+  return *__x < *__y;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (!static_cast<bool>(__x))
+    return false;
+  if (!static_cast<bool>(__y))
+    return true;
+  return *__x > *__y;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (!static_cast<bool>(__x))
+    return true;
+  if (!static_cast<bool>(__y))
+    return false;
+  return *__x <= *__y;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (!static_cast<bool>(__y))
+    return true;
+  if (!static_cast<bool>(__x))
+    return false;
+  return *__x >= *__y;
+}
+
+#  if _LIBCPP_STD_VER >= 20
+
+template <class _Tp, three_way_comparable_with<_Tp> _Up>
+_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
+operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (__x && __y)
+    return *__x <=> *__y;
+  return __x.has_value() <=> __y.has_value();
+}
+
+#  endif // _LIBCPP_STD_VER >= 20
+
+// [optional.nullops] Comparison with nullopt
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, std::nullopt_t) noexcept {
+  return !static_cast<bool>(__x);
+}
+
+#  if _LIBCPP_STD_VER <= 17
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(nullopt_t, const optional<_Tp>& __x) noexcept {
+  return !static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, nullopt_t) noexcept {
+  return static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(nullopt_t, const optional<_Tp>& __x) noexcept {
+  return static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>&, nullopt_t) noexcept {
+  return false;
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(nullopt_t, const optional<_Tp>& __x) noexcept {
+  return static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, nullopt_t) noexcept {
+  return !static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(nullopt_t, const optional<_Tp>&) noexcept {
+  return true;
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, nullopt_t) noexcept {
+  return static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(nullopt_t, const optional<_Tp>&) noexcept {
+  return false;
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>&, nullopt_t) noexcept {
+  return true;
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(nullopt_t, const optional<_Tp>& __x) noexcept {
+  return !static_cast<bool>(__x);
+}
+
+#  else // _LIBCPP_STD_VER <= 17
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept {
+  return __x.has_value() <=> false;
+}
+
+#  endif // _LIBCPP_STD_VER <= 17
+
+// [optional.comp.with.t] Comparison with T
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x == __v;
+  return false;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v == *__x;
+  return false;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x != __v;
+  return true;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v != *__x;
+  return true;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x < __v;
+  return true;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v < *__x;
+  return false;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x <= __v;
+  return true;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v <= *__x;
+  return false;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x > __v;
+  return false;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v > *__x;
+  return true;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x >= __v;
+  return false;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v >= *__x;
+  return true;
+}
+
+#  if _LIBCPP_STD_VER >= 20
+
+template <class _Tp>
+concept __is_derived_from_optional = requires(const _Tp& __t) { []<class _Up>(const optional<_Up>&) {}(__t); };
+
+template <class _Tp, class _Up>
+  requires(!__is_derived_from_optional<_Up>) && three_way_comparable_with<_Tp, _Up>
+_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
+operator<=>(const optional<_Tp>& __x, const _Up& __v) {
+  return __x.has_value() ? *__x <=> __v : strong_ordering::less;
+}
+
+#  endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif
diff --git a/libcxx/include/__optional/hash.h b/libcxx/include/__optional/hash.h
new file mode 100644
index 0000000000000..830f99b0b855f
--- /dev/null
+++ b/libcxx/include/__optional/hash.h
@@ -0,0 +1,48 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_HASH_H
+#define _LIBCPP_OPTIONAL_HASH_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__functional/hash.h>
+#include <__type_traits/remove_const.h>
+
+#include <__fwd/optional.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+struct hash< __enable_hash_helper<optional<_Tp>, remove_const_t<_Tp>> > {
+#  if _LIBCPP_STD_VER <= 17 || defined(_LIBCPP_ENABLE_CXX20_REMOVED_BINDER_TYPEDEFS)
+  _LIBCPP_DEPRECATED_IN_CXX17 typedef optional<_Tp> argument_type;
+  _LIBCPP_DEPRECATED_IN_CXX17 typedef size_t result_type;
+#  endif
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t operator()(const optional<_Tp>& __opt) const {
+    return static_cast<bool>(__opt) ? hash<remove_const_t<_Tp>>()(*__opt) : 0;
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+#endif // _LIBCPP_OPTIONAL_HASH_H
diff --git a/libcxx/include/__optional/nullopt_t.h b/libcxx/include/__optional/nullopt_t.h
new file mode 100644
index 0000000000000..bd66923f026b8
--- /dev/null
+++ b/libcxx/include/__optional/nullopt_t.h
@@ -0,0 +1,40 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_NULLOPT_T_H
+#define _LIBCPP_OPTIONAL_NULLOPT_T_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+struct nullopt_t {
+  struct __secret_tag {
+    explicit __secret_tag() = default;
+  };
+  constexpr explicit nullopt_t(__secret_tag, __secret_tag) noexcept {}
+};
+
+inline constexpr nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secret_tag{}};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+#endif
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
new file mode 100644
index 0000000000000..d01d8e661bdca
--- /dev/null
+++ b/libcxx/include/__optional/optional.h
@@ -0,0 +1,761 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// std::optional<T>
+
+#ifndef _LIBCPP_OPTIONAL_OPTIONAL_H
+#define _LIBCPP_OPTIONAL_OPTIONAL_H
+
+#include <__assert>
+#include <__concepts/invocable.h>
+#include <__config>
+#include <__functional/invoke.h>
+#include <__iterator/bounded_iter.h>
+#include <__iterator/capacity_aware_iterator.h>
+#include <__memory/addressof.h>
+#include <__memory/construct_at.h>
+#include <__tuple/sfinae_helpers.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/disjunction.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_assignable.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_destructible.h>
+#include <__type_traits/is_nothrow_assignable.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_scalar.h>
+#include <__type_traits/is_swappable.h>
+#include <__type_traits/is_trivially_assignable.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
+#include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+
+#include <initializer_list>
+
+#include <__fwd/optional.h>
+#include <__optional/common.h>
+#include <__optional/nullopt_t.h>
+
+#include <__optional/comparison.h>
+#include <__optional/hash.h>
+#include <__optional/swap.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp, bool = is_trivially_destructible<_Tp>::value>
+struct __optional_destruct_base;
+
+template <class _Tp>
+struct __optional_destruct_base<_Tp, false> {
+  typedef _Tp value_type;
+  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
+  union {
+    char __null_state_;
+    remove_cv_t<value_type> __val_;
+  };
+  bool __engaged_;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__optional_destruct_base() {
+    if (__engaged_)
+      __val_.~value_type();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
+      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
+
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Fp, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(
+      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
+      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
+#    endif
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
+    if (__engaged_) {
+      __val_.~value_type();
+      __engaged_ = false;
+    }
+  }
+};
+
+template <class _Tp>
+struct __optional_destruct_base<_Tp, true> {
+  typedef _Tp value_type;
+  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
+  union {
+    char __null_state_;
+    remove_cv_t<value_type> __val_;
+  };
+  bool __engaged_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
+      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
+
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Fp, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
+      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
+      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
+#    endif
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
+    if (__engaged_) {
+      __engaged_ = false;
+    }
+  }
+};
+
+template <class _Tp, bool>
+struct __optional_storage_base : __optional_destruct_base<_Tp> {
+  using __base _LIBCPP_NODEBUG = __optional_destruct_base<_Tp>;
+  using value_type             = _Tp;
+  using __base::__base;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() & noexcept { return this->__val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get() const& noexcept { return this->__val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type&& __get() && noexcept { return std::move(this->__val_); }
+  _LIBCPP_HIDE_FROM_ABI constexpr const value_type&& __get() const&& noexcept { return std::move(this->__val_); }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_Args&&... __args) {
+    _LIBCPP_ASSERT_INTERNAL(!has_value(), "__construct called for engaged __optional_storage");
+    std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
+    this->__engaged_ = true;
+  }
+
+  template <class _That>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
+    if (__opt.has_value())
+      __construct(std::forward<_That>(__opt).__get());
+  }
+
+  template <class _That>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
+    if (this->__engaged_ == __opt.has_value()) {
+      if (this->__engaged_)
+        static_cast<_Tp&>(this->__val_) = std::forward<_That>(__opt).__get();
+    } else {
+      if (this->__engaged_)
+        this->reset();
+      else
+        __construct(std::forward<_That>(__opt).__get());
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+  __swap(__optional_storage_base& __rhs) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) {
+    using std::swap;
+    if (this->has_value() == __rhs.has_value()) {
+      if (this->has_value())
+        swap(this->__get(), __rhs.__get());
+    } else {
+      if (this->has_value()) {
+        __rhs.__construct(std::move(this->__get()));
+        this->reset();
+      } else {
+        this->__construct(std::move(__rhs.__get()));
+        __rhs.reset();
+      }
+    }
+  }
+
+  // [optional.observe]
+  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp const> operator->() const noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
+    return std::addressof(this->__get());
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
+    return std::addressof(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return std::move(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return std::move(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return std::move(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return std::move(this->__get());
+  }
+};
+
+template <class _Tp, bool = is_trivially_copy_constructible_v<_Tp>>
+struct __optional_copy_base : __optional_storage_base<_Tp> {
+  using __optional_storage_base<_Tp>::__optional_storage_base;
+};
+
+template <class _Tp>
+struct __optional_copy_base<_Tp, false> : __optional_storage_base<_Tp> {
+  using __optional_storage_base<_Tp>::__optional_storage_base;
+
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_base() = default;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_base(const __optional_copy_base& __opt) {
+    this->__construct_from(__opt);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_base(__optional_copy_base&&)                 = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(const __optional_copy_base&) = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(__optional_copy_base&&)      = default;
+};
+
+template <class _Tp, bool = is_trivially_move_constructible_v<_Tp>>
+struct __optional_move_base : __optional_copy_base<_Tp> {
+  using __optional_copy_base<_Tp>::__optional_copy_base;
+};
+
+template <class _Tp>
+struct __optional_move_base<_Tp, false> : __optional_copy_base<_Tp> {
+  using value_type = _Tp;
+  using __optional_copy_base<_Tp>::__optional_copy_base;
+
+  _LIBCPP_HIDE_FROM_ABI __optional_move_base()                            = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_base(const __optional_move_base&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+  __optional_move_base(__optional_move_base&& __opt) noexcept(is_nothrow_move_constructible_v<value_type>) {
+    this->__construct_from(std::move(__opt));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(const __optional_move_base&) = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(__optional_move_base&&)      = default;
+};
+
+template <class _Tp,
+          bool = (is_trivially_destructible_v<_Tp> && is_trivially_copy_constructible_v<_Tp> &&
+                  is_trivially_copy_assignable_v<_Tp>)>
+struct __optional_copy_assign_base : __optional_move_base<_Tp> {
+  using __optional_move_base<_Tp>::__optional_move_base;
+};
+
+template <class _Tp>
+struct __optional_copy_assign_base<_Tp, false> : __optional_move_base<_Tp> {
+  using __optional_move_base<_Tp>::__optional_move_base;
+
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base()                                   = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(const __optional_copy_assign_base&) = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(__optional_copy_assign_base&&)      = default;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_assign_base&
+  operator=(const __optional_copy_assign_base& __opt) {
+    this->__assign_from(__opt);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base& operator=(__optional_copy_assign_base&&) = default;
+};
+
+template <class _Tp,
+          bool = (is_trivially_destructible_v<_Tp> && is_trivially_move_constructible_v<_Tp> &&
+                  is_trivially_move_assignable_v<_Tp>)>
+struct __optional_move_assign_base : __optional_copy_assign_base<_Tp> {
+  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
+};
+
+template <class _Tp>
+struct __optional_move_assign_base<_Tp, false> : __optional_copy_assign_base<_Tp> {
+  using value_type = _Tp;
+  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
+
+  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base()                                              = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(const __optional_move_assign_base& __opt)      = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(__optional_move_assign_base&&)                 = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base& operator=(const __optional_move_assign_base&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_move_assign_base&
+  operator=(__optional_move_assign_base&& __opt) noexcept(
+      is_nothrow_move_assignable_v<value_type> && is_nothrow_move_constructible_v<value_type>) {
+    this->__assign_from(std::move(__opt));
+    return *this;
+  }
+};
+
+template <class _Tp>
+using __optional_sfinae_ctor_base_t _LIBCPP_NODEBUG =
+    __sfinae_ctor_base< is_copy_constructible<_Tp>::value, is_move_constructible<_Tp>::value >;
+
+template <class _Tp>
+using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
+    __sfinae_assign_base< (is_copy_constructible_v<_Tp> && is_copy_assignable_v<_Tp>),
+                          (is_move_constructible_v<_Tp> && is_move_assignable_v<_Tp>)>;
+
+template <class _Tp>
+struct __optional_iterator_base : __optional_move_assign_base<_Tp> {
+  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
+};
+
+#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+  requires is_object_v<_Tp>
+struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
+private:
+  using __pointer _LIBCPP_NODEBUG       = add_pointer_t<_Tp>;
+  using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const _Tp>;
+
+public:
+  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+  using iterator       = __bounded_iter<__pointer>;
+  using const_iterator = __bounded_iter<__const_pointer>;
+#        else
+  using iterator       = __capacity_aware_iterator<__pointer, optional<_Tp>, 1>;
+  using const_iterator = __capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>;
+#        endif
+
+  // [optional.iterators], iterator support
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
+    auto* __ptr = std::addressof(this->__get());
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
+#        else
+    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp>, 1>(__ptr);
+#        endif
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
+    auto* __ptr = std::addressof(this->__get());
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
+#        else
+    return std::__make_capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>(__ptr);
+#        endif
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
+    return begin() + (this->has_value() ? 1 : 0);
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
+    return begin() + (this->has_value() ? 1 : 0);
+  }
+};
+
+#    endif   // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+class _LIBCPP_DECLSPEC_EMPTY_BASES optional
+    : public __optional_iterator_base<_Tp>,
+      private __optional_sfinae_ctor_base_t<_Tp>,
+      private __optional_sfinae_assign_base_t<_Tp> {
+  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp>;
+
+public:
+  using value_type = __libcpp_remove_reference_t<_Tp>;
+
+  using __trivially_relocatable _LIBCPP_NODEBUG =
+      conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
+
+private:
+  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
+  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
+#    if _LIBCPP_STD_VER >= 26
+  static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
+#    else
+  static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
+#    endif
+  static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
+  static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
+
+  // LWG2756: conditionally explicit conversion from _Up
+  struct _CheckOptionalArgsConstructor {
+    template <class _Up>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
+      return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>;
+    }
+
+    template <class _Up>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
+      return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>;
+    }
+  };
+  template <class _Up>
+  using _CheckOptionalArgsCtor _LIBCPP_NODEBUG =
+      _If< _IsNotSame<__remove_cvref_t<_Up>, in_place_t>::value && _IsNotSame<__remove_cvref_t<_Up>, optional>::value &&
+               (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_optional<__remove_cvref_t<_Up>>::value),
+           _CheckOptionalArgsConstructor,
+           __check_tuple_constructor_fail >;
+  template <class _QualUp>
+  struct _CheckOptionalLikeConstructor {
+    template <class _Up, class _Opt = optional<_Up>>
+    using __check_constructible_from_opt _LIBCPP_NODEBUG =
+        _Or< is_constructible<_Tp, _Opt&>,
+             is_constructible<_Tp, _Opt const&>,
+             is_constructible<_Tp, _Opt&&>,
+             is_constructible<_Tp, _Opt const&&>,
+             is_convertible<_Opt&, _Tp>,
+             is_convertible<_Opt const&, _Tp>,
+             is_convertible<_Opt&&, _Tp>,
+             is_convertible<_Opt const&&, _Tp> >;
+    template <class _Up, class _Opt = optional<_Up>>
+    using __check_assignable_from_opt _LIBCPP_NODEBUG =
+        _Or< is_assignable<_Tp&, _Opt&>,
+             is_assignable<_Tp&, _Opt const&>,
+             is_assignable<_Tp&, _Opt&&>,
+             is_assignable<_Tp&, _Opt const&&> >;
+    template <class _Up, class _QUp = _QualUp>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
+      return is_convertible<_QUp, _Tp>::value &&
+             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
+    }
+    template <class _Up, class _QUp = _QualUp>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
+      return !is_convertible<_QUp, _Tp>::value &&
+             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
+    }
+    template <class _Up, class _QUp = _QualUp>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_assign() {
+      // Construction and assignability of _QUp to _Tp has already been
+      // checked.
+      return !__check_constructible_from_opt<_Up>::value && !__check_assignable_from_opt<_Up>::value;
+    }
+  };
+
+  template <class _Up, class _QualUp>
+  using _CheckOptionalLikeCtor _LIBCPP_NODEBUG =
+      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp> >::value,
+           _CheckOptionalLikeConstructor<_QualUp>,
+           __check_tuple_constructor_fail >;
+  template <class _Up, class _QualUp>
+  using _CheckOptionalLikeAssign _LIBCPP_NODEBUG =
+      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>, is_assignable<_Tp&, _QualUp> >::value,
+           _CheckOptionalLikeConstructor<_QualUp>,
+           __check_tuple_constructor_fail >;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr optional() noexcept {}
+  _LIBCPP_HIDE_FROM_ABI constexpr optional(const optional&) = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr optional(optional&&)      = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr optional(nullopt_t) noexcept {}
+
+  template <class _InPlaceT,
+            class... _Args,
+            enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, is_constructible<_Tp, _Args...>>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_InPlaceT, _Args&&... __args)
+      : __base(in_place, std::forward<_Args>(__args)...) {}
+
+  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
+      : __base(in_place, __il, std::forward<_Args>(__args)...) {}
+
+  template <class _Up = _Tp, enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
+      : __base(in_place, std::forward<_Up>(__v)) {}
+
+  template <class _Up                                                                        = remove_cv_t<_Tp>,
+            enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) noexcept(
+      is_nothrow_constructible_v<_Tp, _Up>) // strengthened
+      : __base(in_place, std::forward<_Up>(__v)) {}
+
+  // LWG2756: conditionally explicit conversion from const optional<_Up>&
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_implicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) {
+    this->__construct_from(__v);
+  }
+
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_explicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) {
+    this->__construct_from(__v);
+  }
+
+  // LWG2756: conditionally explicit conversion from optional<_Up>&&
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_implicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) {
+    this->__construct_from(std::move(__v));
+  }
+
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_explicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) {
+    this->__construct_from(std::move(__v));
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Tag,
+            class _Fp,
+            class... _Args,
+            enable_if_t<_IsSame<_Tag, __optional_construct_from_invoke_tag>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
+      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
+#    endif
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(nullopt_t) noexcept {
+    reset();
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(const optional&) = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(optional&&)      = default;
+
+  // LWG2756
+  template <class _Up        = remove_cv_t<_Tp>,
+            enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>,
+                             _Or<_IsNotSame<__remove_cvref_t<_Up>, _Tp>, _Not<is_scalar<_Tp>>>,
+                             is_constructible<_Tp, _Up>,
+                             is_assignable<_Tp&, _Up>>::value,
+                        int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(_Up&& __v) {
+    if (this->has_value())
+      this->__get() = std::forward<_Up>(__v);
+    else
+      this->__construct(std::forward<_Up>(__v));
+    return *this;
+  }
+
+  // LWG2756
+  template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>::template __enable_assign<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(const optional<_Up>& __v) {
+    this->__assign_from(__v);
+    return *this;
+  }
+
+  // LWG2756
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_assign<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(optional<_Up>&& __v) {
+    this->__assign_from(std::move(__v));
+    return *this;
+  }
+
+  template <class... _Args, enable_if_t<is_constructible_v<_Tp, _Args...>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) {
+    reset();
+    this->__construct(std::forward<_Args>(__args)...);
+    return this->__get();
+  }
+
+  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) {
+    reset();
+    this->__construct(__il, std::forward<_Args>(__args)...);
+    return this->__get();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+  swap(optional& __opt) noexcept((is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>)) {
+    this->__swap(__opt);
+  }
+
+  using __base::operator*;
+  using __base::operator->;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return has_value(); }
+
+  using __base::__get;
+  using __base::has_value;
+  using __base::value;
+
+  template <class _Up = remove_cv_t<_Tp>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
+    static_assert(is_copy_constructible_v<_Tp>, "optional<T>::value_or: T must be copy constructible");
+    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
+    return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v));
+  }
+
+  template <class _Up = remove_cv_t<_Tp>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
+    static_assert(is_move_constructible_v<_Tp>, "optional<T>::value_or: T must be move constructible");
+    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
+    return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v));
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
+    using _Up = invoke_result_t<_Func, _Tp&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(value()) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), value());
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
+    using _Up = invoke_result_t<_Func, const _Tp&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(value()) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), value());
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
+    using _Up = invoke_result_t<_Func, _Tp&&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(std::move(value())) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), std::move(value()));
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
+    using _Up = invoke_result_t<_Func, const _Tp&&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(std::move(value())) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), std::move(value()));
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & {
+    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
+    return optional<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& {
+    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
+    return optional<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && {
+    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
+    return optional<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& {
+    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
+    return optional<_Up>();
+  }
+
+  template <invocable _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const&
+    requires is_copy_constructible_v<_Tp>
+  {
+    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
+                  "Result of f() should be the same type as this optional");
+    if (*this)
+      return *this;
+    return std::forward<_Func>(__f)();
+  }
+
+  template <invocable _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) &&
+    requires is_move_constructible_v<_Tp>
+  {
+    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
+                  "Result of f() should be the same type as this optional");
+    if (*this)
+      return std::move(*this);
+    return std::forward<_Func>(__f)();
+  }
+#    endif // _LIBCPP_STD_VER >= 23
+
+  using __base::reset;
+};
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_OPTIONAL_OPTIONAL_H
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
new file mode 100644
index 0000000000000..dd1adc1642ca3
--- /dev/null
+++ b/libcxx/include/__optional/optional_ref.h
@@ -0,0 +1,355 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// std::optional<T&>
+
+#ifndef _LIBCPP_OPTIONAL_OPTIONAL_REF_H
+#define _LIBCPP_OPTIONAL_OPTIONAL_REF_H
+
+#include <__assert>
+#include <__concepts/invocable.h>
+#include <__config>
+#include <__functional/invoke.h>
+#include <__iterator/bounded_iter.h>
+#include <__iterator/capacity_aware_iterator.h>
+#include <__memory/addressof.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/reference_constructs_from_temporary.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <__utility/swap.h>
+
+#include <__fwd/optional.h>
+#include <__optional/comparison.h>
+#include <__optional/swap.h>
+
+#include <__optional/common.h>
+#include <__optional/hash.h>
+#include <__optional/nullopt_t.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 26
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+struct __optional_storage_base<_Tp, true> {
+  using value_type                 = _Tp;
+  using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
+  __raw_type* __value_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
+
+  template <class _Up>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
+    _Tp& __r(std::forward<_Up>(__val));
+    __value_ = std::addressof(__r);
+  }
+
+  template <class _UArg>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) {
+    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
+                  "Attempted to construct a reference element in optional from a "
+                  "possible temporary");
+    __convert_init_ref_val(std::forward<_UArg>(__uarg));
+  }
+
+  template <class _Fp, class... _Args>
+  constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
+    __convert_init_ref_val(std::forward<invoke_result_t<_Fp, _Args...>>(
+        std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept { __value_ = nullptr; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const noexcept { return *__value_; }
+
+  template <class _UArg>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_UArg&& __val) {
+    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
+                  "Attempted to construct a reference element in tuple from a "
+                  "possible temporary");
+    __convert_init_ref_val(std::forward<_UArg>(__val));
+  }
+
+  template <class _That>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
+    if (__opt.has_value())
+      __construct(std::forward<_That>(__opt).__get());
+  }
+
+  template <class _That>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
+    if (has_value() == __opt.has_value()) {
+      if (has_value())
+        *__value_ = std::forward<_That>(__opt).__get();
+    } else {
+      if (has_value())
+        reset();
+      else
+        __construct(std::forward<_That>(__opt).__get());
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept {
+    std::swap(__value_, __rhs.__value_);
+  }
+
+  // [optional.ref.observe]
+  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() const noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
+    return std::addressof(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() const noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() const {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return this->__get();
+  }
+};
+
+template <class _Tp>
+struct __optional_iterator_base;
+
+template<class _Tp>
+struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
+  using __optional_storage_base<_Tp&>::__optional_storage_base;
+};
+
+#if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+  requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
+struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
+private:
+  using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
+
+public:
+  using __optional_storage_base<_Tp&>::__optional_storage_base;
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+  using iterator = __bounded_iter<__pointer>;
+#        else
+  using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
+#        endif
+
+  // [optional.ref.iterators], iterator support
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
+    auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
+#        else
+    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
+#        endif
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
+    return begin() + (this->has_value() ? 1 : 0);
+  }
+};
+
+#endif
+
+template <class _Tp>
+class optional<_Tp&> : public __optional_iterator_base<_Tp&> {
+  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp&>;
+
+  template <class _Up, class _QualUp>
+  static constexpr bool __check_optionalU_ctor =
+      !is_same_v<remove_cv_t<_Tp>, optional<_Up>> && !is_same_v<_Tp&, _Up> && is_constructible_v<_Tp&, _QualUp>;
+  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
+  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
+
+public:
+  using value_type = _Tp;
+
+  constexpr optional() noexcept = default;
+  constexpr optional(nullopt_t) noexcept {}
+  constexpr optional(const optional&) noexcept = default;
+
+  template <class _Arg>
+    requires(is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>)
+  constexpr explicit optional(in_place_t, _Arg&& __arg) : __base(in_place, std::forward<_Arg>(__arg)) {}
+
+  template <class _Up>
+    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
+             is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr explicit(!is_convertible_v<_Up, _Tp&>) optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>)
+      : __base(in_place, std::forward<_Up>(__v)) {}
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, _Up&> && !reference_constructs_from_temporary_v<_Tp&, _Up&>)
+  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
+      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) {
+    this->__construct_from(__rhs);
+  }
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, const _Up&> && !reference_constructs_from_temporary_v<_Tp&, const _Up&>)
+  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
+      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) {
+    this->__construct_from(__rhs);
+  }
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
+      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
+    this->__construct_from(std::move(__rhs));
+  }
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, const _Up> && !reference_constructs_from_temporary_v<_Tp&, const _Up>)
+  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
+      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) {
+    this->__construct_from(std::move(__rhs));
+  }
+
+  template <class _Tag, class _Fp, class... _Args>
+    requires(is_same_v<_Tag, __optional_construct_from_invoke_tag>)
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
+      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
+
+  // deleted overloads
+
+  template <class _Up>
+    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
+             is_constructible_v<_Tp&, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
+      optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, _Up&> && reference_constructs_from_temporary_v<_Tp&, _Up&>)
+  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
+      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) = delete;
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, const _Up&> && reference_constructs_from_temporary_v<_Tp&, const _Up&>)
+  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
+      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) = delete;
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
+      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, const _Up> && reference_constructs_from_temporary_v<_Tp&, const _Up>)
+  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
+      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) = delete;
+
+  constexpr ~optional() = default;
+
+  using __base::__get;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(nullopt_t) noexcept {
+    reset();
+    return *this;
+  }
+
+  constexpr optional& operator=(const optional&) noexcept = default;
+
+  template <class _Up>
+    requires(is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr _Tp& emplace(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
+    this->__construct(std::forward<_Up>(__u));
+
+    return this->__get();
+  }
+
+  constexpr void swap(optional& __rhs) noexcept { this->__swap(__rhs); }
+
+  using __base::operator->;
+  using __base::operator*;
+
+  constexpr explicit operator bool() const noexcept { return has_value(); }
+
+  using __base::has_value;
+  using __base::value;
+
+  template <class _Up = remove_cv_t<_Tp>>
+    requires(!is_array_v<_Tp> && is_object_v<_Tp>)
+  [[nodiscard]] constexpr decay_t<_Tp> value_or(_Up&& __v) const {
+    using _XTp = remove_cv_t<_Tp>;
+    static_assert(is_constructible_v<_XTp, _Tp&>, "optional<T&>::value_or: remove_cv_t<T> must be constructible");
+    static_assert(is_convertible_v<_Up, _XTp>, "optional<T&>::value_or: U must be convertible to remove_cv_t<T>");
+    return this->has_value() ? this->__get() : static_cast<_XTp>(std::forward<_Up>(__v));
+  }
+
+  template <class _Func>
+  [[nodiscard]] constexpr auto and_then(_Func&& __f) const {
+    using _Up = invoke_result_t<_Func, _Tp&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(value()) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), value());
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] constexpr optional<remove_cv_t<invoke_result_t<_Func, _Tp&>>> transform(_Func&& __f) const {
+    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
+    return optional<_Up>();
+  }
+
+  template <invocable _Func>
+  [[nodiscard]] constexpr optional or_else(_Func&& __f) const {
+    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
+                  "Result of f() should be the same type as this optional");
+    if (*this)
+      return *this;
+    return std::forward<_Func>(__f)();
+  }
+
+  using __base::reset;
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_OPTIONAL_OPTIONAL_REF_H
diff --git a/libcxx/include/__optional/swap.h b/libcxx/include/__optional/swap.h
new file mode 100644
index 0000000000000..d1cec68c555fe
--- /dev/null
+++ b/libcxx/include/__optional/swap.h
@@ -0,0 +1,45 @@
+// -*- C++ -*-Expand commentComment on line R1Resolved
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_SWAP_H
+#define _LIBCPP_OPTIONAL_SWAP_H
+
+#include <__config>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_swappable.h>
+
+#include <__fwd/optional.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp,
+          enable_if_t<(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>)
+#  if _LIBCPP_STD_VER >= 26
+                          || is_reference_v<_Tp>
+#  endif
+                      ,
+                      int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) {
+  __x.swap(__y);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+
+#endif
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b9ddfd5bbf4cd..e0c40bad3bae9 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1796,6 +1796,25 @@ module std [system] {
   }
 
   module optional {
+    module fwd { header "__fwd/optional.h" }
+    module hash {
+      header "__optional/hash.h"
+      export std.functional.hash
+    }
+    module common {
+      header "__optional/common.h"
+      export std.iterator.bounded_iter
+      export std.iterator.capacity_aware_iterator
+      export std.format.fwd
+      export std.utility.swap
+    }
+
+    module comparison { header "__optional/comparison.h" }
+    module nullopt_t { header "__optional/nullopt_t.h" }
+    module optional { header "__optional/optional.h" }
+    module optional_ref { header "__optional/optional_ref.h" }
+    module swap { header "__optional/swap.h" }
+
     header "optional"
     export *
   }
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 2499479348892..2b40706910018 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -258,68 +258,34 @@ namespace std {
 #if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 #  include <__cxx03/__config>
 #else
-#  include <__assert>
-#  include <__compare/compare_three_way_result.h>
-#  include <__compare/ordering.h>
-#  include <__compare/three_way_comparable.h>
-#  include <__concepts/invocable.h>
 #  include <__config>
-#  include <__exception/exception.h>
-#  include <__functional/hash.h>
-#  include <__functional/invoke.h>
-#  include <__functional/unary_function.h>
-#  include <__fwd/format.h>
-#  include <__fwd/functional.h>
-#  include <__iterator/bounded_iter.h>
-#  include <__iterator/capacity_aware_iterator.h>
-#  include <__memory/addressof.h>
-#  include <__memory/construct_at.h>
-#  include <__ranges/enable_borrowed_range.h>
-#  include <__ranges/enable_view.h>
-#  include <__tuple/sfinae_helpers.h>
-#  include <__type_traits/add_pointer.h>
-#  include <__type_traits/conditional.h>
-#  include <__type_traits/conjunction.h>
 #  include <__type_traits/decay.h>
-#  include <__type_traits/disjunction.h>
 #  include <__type_traits/enable_if.h>
-#  include <__type_traits/integral_constant.h>
-#  include <__type_traits/invoke.h>
-#  include <__type_traits/is_array.h>
-#  include <__type_traits/is_assignable.h>
 #  include <__type_traits/is_constructible.h>
-#  include <__type_traits/is_convertible.h>
-#  include <__type_traits/is_core_convertible.h>
-#  include <__type_traits/is_destructible.h>
-#  include <__type_traits/is_function.h>
-#  include <__type_traits/is_nothrow_assignable.h>
-#  include <__type_traits/is_nothrow_constructible.h>
-#  include <__type_traits/is_object.h>
-#  include <__type_traits/is_reference.h>
-#  include <__type_traits/is_same.h>
-#  include <__type_traits/is_scalar.h>
-#  include <__type_traits/is_swappable.h>
-#  include <__type_traits/is_trivially_assignable.h>
-#  include <__type_traits/is_trivially_constructible.h>
-#  include <__type_traits/is_trivially_destructible.h>
-#  include <__type_traits/is_trivially_relocatable.h>
-#  include <__type_traits/negation.h>
 #  include <__type_traits/reference_constructs_from_temporary.h>
 #  include <__type_traits/remove_const.h>
-#  include <__type_traits/remove_cv.h>
-#  include <__type_traits/remove_cvref.h>
-#  include <__type_traits/remove_reference.h>
-#  include <__utility/declval.h>
 #  include <__utility/forward.h>
 #  include <__utility/in_place.h>
-#  include <__utility/move.h>
-#  include <__utility/swap.h>
-#  include <__verbose_abort>
 #  include <initializer_list>
 #  include <version>
 
-// standard-mandated includes
+#  include <__optional/common.h>
+#  include <__optional/comparison.h>
+#  include <__optional/hash.h>
+#  include <__optional/optional.h>
+#  include <__optional/optional_ref.h>
+#  include <__optional/swap.h>
 
+// TODO: Certain headers rely on these transitive includes
+#  include <__type_traits/conditional.h>
+#  include <__type_traits/invoke.h>
+#  include <__type_traits/is_object.h>
+#  include <__type_traits/is_reference.h>
+#  include <__type_traits/remove_cvref.h>
+#  include <__utility/declval.h>
+#  include <__utility/move.h>
+
+// standard-mandated includes
 // [optional.syn]
 #  include <compare>
 
@@ -330,1367 +296,13 @@ namespace std {
 _LIBCPP_PUSH_MACROS
 #  include <__undef_macros>
 
-namespace std // purposefully not using versioning namespace
-{
-
-class _LIBCPP_EXPORTED_FROM_ABI bad_optional_access : public exception {
-public:
-  _LIBCPP_HIDE_FROM_ABI bad_optional_access() _NOEXCEPT                                      = default;
-  _LIBCPP_HIDE_FROM_ABI bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
-  _LIBCPP_HIDE_FROM_ABI bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
-  // Get the key function ~bad_optional_access() into the dylib
-  ~bad_optional_access() _NOEXCEPT override;
-  [[__nodiscard__]] const char* what() const _NOEXCEPT override;
-};
-
-} // namespace std
-
 #  if _LIBCPP_STD_VER >= 17
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-[[noreturn]] inline _LIBCPP_HIDE_FROM_ABI void __throw_bad_optional_access() {
-#    if _LIBCPP_HAS_EXCEPTIONS
-  throw bad_optional_access();
-#    else
-  _LIBCPP_VERBOSE_ABORT("bad_optional_access was thrown in -fno-exceptions mode");
-#    endif
-}
-
-struct nullopt_t {
-  struct __secret_tag {
-    explicit __secret_tag() = default;
-  };
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit nullopt_t(__secret_tag, __secret_tag) noexcept {}
-};
-
-inline constexpr nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secret_tag{}};
-
-struct __optional_construct_from_invoke_tag {};
-
-template <class _Tp, bool = is_trivially_destructible<_Tp>::value>
-struct __optional_destruct_base;
-
-template <class _Tp>
-struct __optional_destruct_base<_Tp, false> {
-  typedef _Tp value_type;
-  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
-  union {
-    char __null_state_;
-    remove_cv_t<value_type> __val_;
-  };
-  bool __engaged_;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__optional_destruct_base() {
-    if (__engaged_)
-      __val_.~value_type();
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
-      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Fp, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(
-      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
-      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
-    if (__engaged_) {
-      __val_.~value_type();
-      __engaged_ = false;
-    }
-  }
-};
-
-template <class _Tp>
-struct __optional_destruct_base<_Tp, true> {
-  typedef _Tp value_type;
-  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
-  union {
-    char __null_state_;
-    remove_cv_t<value_type> __val_;
-  };
-  bool __engaged_;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
-      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Fp, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
-      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
-      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
-    if (__engaged_) {
-      __engaged_ = false;
-    }
-  }
-};
-
-template <class _Tp, bool = is_reference<_Tp>::value>
-struct __optional_storage_base : __optional_destruct_base<_Tp> {
-  using __base _LIBCPP_NODEBUG = __optional_destruct_base<_Tp>;
-  using value_type             = _Tp;
-  using __base::__base;
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() & noexcept { return this->__val_; }
-  _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get() const& noexcept { return this->__val_; }
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type&& __get() && noexcept { return std::move(this->__val_); }
-  _LIBCPP_HIDE_FROM_ABI constexpr const value_type&& __get() const&& noexcept { return std::move(this->__val_); }
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_Args&&... __args) {
-    _LIBCPP_ASSERT_INTERNAL(!has_value(), "__construct called for engaged __optional_storage");
-    std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
-    this->__engaged_ = true;
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
-    if (__opt.has_value())
-      __construct(std::forward<_That>(__opt).__get());
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
-    if (this->__engaged_ == __opt.has_value()) {
-      if (this->__engaged_)
-        static_cast<_Tp&>(this->__val_) = std::forward<_That>(__opt).__get();
-    } else {
-      if (this->__engaged_)
-        this->reset();
-      else
-        __construct(std::forward<_That>(__opt).__get());
-    }
-  }
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-  __swap(__optional_storage_base& __rhs) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) {
-    using std::swap;
-    if (this->has_value() == __rhs.has_value()) {
-      if (this->has_value())
-        swap(this->__get(), __rhs.__get());
-    } else {
-      if (this->has_value()) {
-        __rhs.__construct(std::move(this->__get()));
-        this->reset();
-      } else {
-        this->__construct(std::move(__rhs.__get()));
-        __rhs.reset();
-      }
-    }
-  }
-
-  // [optional.observe]
-  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp const> operator->() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return std::move(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return std::move(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return std::move(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return std::move(this->__get());
-  }
-};
-
-template <class _Tp>
-struct __optional_storage_base<_Tp, true> {
-  using value_type                 = _Tp;
-  using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
-  __raw_type* __value_;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
-
-  template <class _Up>
-  _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
-    _Tp& __r(std::forward<_Up>(__val));
-    __value_ = std::addressof(__r);
-  }
-
-  template <class _UArg>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) {
-    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
-                  "Attempted to construct a reference element in optional from a "
-                  "possible temporary");
-    __convert_init_ref_val(std::forward<_UArg>(__uarg));
-  }
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Fp, class... _Args>
-  constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
-    __convert_init_ref_val(std::forward<invoke_result_t<_Fp, _Args...>>(
-        std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)));
-  }
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept { __value_ = nullptr; }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const noexcept { return *__value_; }
-
-  template <class _UArg>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_UArg&& __val) {
-    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
-                  "Attempted to construct a reference element in tuple from a "
-                  "possible temporary");
-    __convert_init_ref_val(std::forward<_UArg>(__val));
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
-    if (__opt.has_value())
-      __construct(std::forward<_That>(__opt).__get());
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
-    if (has_value() == __opt.has_value()) {
-      if (has_value())
-        *__value_ = std::forward<_That>(__opt).__get();
-    } else {
-      if (has_value())
-        reset();
-      else
-        __construct(std::forward<_That>(__opt).__get());
-    }
-  }
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept {
-    std::swap(__value_, __rhs.__value_);
-  }
-
-  // [optional.ref.observe]
-  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() const {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return this->__get();
-  }
-};
-
-template <class _Tp, bool = is_trivially_copy_constructible_v<_Tp>>
-struct __optional_copy_base : __optional_storage_base<_Tp> {
-  using __optional_storage_base<_Tp>::__optional_storage_base;
-};
-
-template <class _Tp>
-struct __optional_copy_base<_Tp, false> : __optional_storage_base<_Tp> {
-  using __optional_storage_base<_Tp>::__optional_storage_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base() = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_base(const __optional_copy_base& __opt) {
-    this->__construct_from(__opt);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base(__optional_copy_base&&)                 = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(const __optional_copy_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(__optional_copy_base&&)      = default;
-};
-
-template <class _Tp, bool = is_trivially_move_constructible_v<_Tp>>
-struct __optional_move_base : __optional_copy_base<_Tp> {
-  using __optional_copy_base<_Tp>::__optional_copy_base;
-};
-
-template <class _Tp>
-struct __optional_move_base<_Tp, false> : __optional_copy_base<_Tp> {
-  using value_type = _Tp;
-  using __optional_copy_base<_Tp>::__optional_copy_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base()                            = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base(const __optional_move_base&) = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
-  __optional_move_base(__optional_move_base&& __opt) noexcept(is_nothrow_move_constructible_v<value_type>) {
-    this->__construct_from(std::move(__opt));
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(const __optional_move_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(__optional_move_base&&)      = default;
-};
-
-template <class _Tp,
-          bool = (is_trivially_destructible_v<_Tp> && is_trivially_copy_constructible_v<_Tp> &&
-                  is_trivially_copy_assignable_v<_Tp>)>
-struct __optional_copy_assign_base : __optional_move_base<_Tp> {
-  using __optional_move_base<_Tp>::__optional_move_base;
-};
-
-template <class _Tp>
-struct __optional_copy_assign_base<_Tp, false> : __optional_move_base<_Tp> {
-  using __optional_move_base<_Tp>::__optional_move_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base()                                   = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(const __optional_copy_assign_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(__optional_copy_assign_base&&)      = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_assign_base&
-  operator=(const __optional_copy_assign_base& __opt) {
-    this->__assign_from(__opt);
-    return *this;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base& operator=(__optional_copy_assign_base&&) = default;
-};
-
-template <class _Tp,
-          bool = (is_trivially_destructible_v<_Tp> && is_trivially_move_constructible_v<_Tp> &&
-                  is_trivially_move_assignable_v<_Tp>)>
-struct __optional_move_assign_base : __optional_copy_assign_base<_Tp> {
-  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
-};
-
-template <class _Tp>
-struct __optional_move_assign_base<_Tp, false> : __optional_copy_assign_base<_Tp> {
-  using value_type = _Tp;
-  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base()                                              = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(const __optional_move_assign_base& __opt)      = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(__optional_move_assign_base&&)                 = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base& operator=(const __optional_move_assign_base&) = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_move_assign_base&
-  operator=(__optional_move_assign_base&& __opt) noexcept(
-      is_nothrow_move_assignable_v<value_type> && is_nothrow_move_constructible_v<value_type>) {
-    this->__assign_from(std::move(__opt));
-    return *this;
-  }
-};
-
-template <class _Tp>
-using __optional_sfinae_ctor_base_t _LIBCPP_NODEBUG =
-    __sfinae_ctor_base< is_copy_constructible<_Tp>::value, is_move_constructible<_Tp>::value >;
-
-template <class _Tp>
-using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
-    __sfinae_assign_base< (is_copy_constructible_v<_Tp> && is_copy_assignable_v<_Tp>),
-                          (is_move_constructible_v<_Tp> && is_move_assignable_v<_Tp>)>;
-
-template <class _Tp>
-class optional;
-
-#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-template <class _Tp>
-constexpr bool ranges::enable_view<optional<_Tp>> = true;
-
-template <class _Tp>
-constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
-
-template <class _Tp>
-constexpr bool ranges::enable_borrowed_range<optional<_Tp&>> = true;
-
-#    endif
-
-#    if _LIBCPP_STD_VER >= 20
-
-template <class _Tp>
-concept __is_derived_from_optional = requires(const _Tp& __t) { []<class _Up>(const optional<_Up>&) {}(__t); };
-
-#    endif // _LIBCPP_STD_VER >= 20
-
-template <class _Tp>
-struct __is_std_optional : false_type {};
-template <class _Tp>
-struct __is_std_optional<optional<_Tp>> : true_type {};
-
-#    if _LIBCPP_STD_VER < 26
-template <class _Tp>
-inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
-#    else
-template <class _Tp>
-inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp> || is_lvalue_reference_v<_Tp>;
-#    endif
-
-template <class _Tp>
-struct __optional_iterator_base : __optional_move_assign_base<_Tp> {
-  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
-};
-
-#    if _LIBCPP_STD_VER >= 26
-template <class _Tp>
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
-};
-
-#      if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-
-template <class _Tp>
-  requires is_object_v<_Tp>
-struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
-private:
-  using __pointer _LIBCPP_NODEBUG       = add_pointer_t<_Tp>;
-  using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const _Tp>;
-
-public:
-  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-  using iterator       = __bounded_iter<__pointer>;
-  using const_iterator = __bounded_iter<__const_pointer>;
-#        else
-  using iterator       = __capacity_aware_iterator<__pointer, optional<_Tp>, 1>;
-  using const_iterator = __capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>;
-#        endif
-
-  // [optional.iterators], iterator support
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
-    auto* __ptr = std::addressof(this->__get());
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
-    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp>, 1>(__ptr);
-#        endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
-    auto* __ptr = std::addressof(this->__get());
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
-    return std::__make_capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>(__ptr);
-#        endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-};
-
-template <class _Tp>
-  requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
-private:
-  using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
-
-public:
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-  using iterator = __bounded_iter<__pointer>;
-#        else
-  using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
-#        endif
-
-  // [optional.ref.iterators], iterator support
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
-    auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
-    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
-#        endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-};
-
-#      endif // _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-#    endif   // _LIBCPP_STD_VER >= 26
-
-template <class _Tp>
-class _LIBCPP_DECLSPEC_EMPTY_BASES optional
-    : public __optional_iterator_base<_Tp>,
-      private __optional_sfinae_ctor_base_t<_Tp>,
-      private __optional_sfinae_assign_base_t<_Tp> {
-  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp>;
-
-public:
-  using value_type = __libcpp_remove_reference_t<_Tp>;
-
-  using __trivially_relocatable _LIBCPP_NODEBUG =
-      conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
-
-private:
-  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
-  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-#    if _LIBCPP_STD_VER >= 26
-  static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
-#    else
-  static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
-#    endif
-  static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
-  static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
-
-  // LWG2756: conditionally explicit conversion from _Up
-  struct _CheckOptionalArgsConstructor {
-    template <class _Up>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
-      return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>;
-    }
-
-    template <class _Up>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
-      return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>;
-    }
-  };
-  template <class _Up>
-  using _CheckOptionalArgsCtor _LIBCPP_NODEBUG =
-      _If< _IsNotSame<__remove_cvref_t<_Up>, in_place_t>::value && _IsNotSame<__remove_cvref_t<_Up>, optional>::value &&
-               (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_optional<__remove_cvref_t<_Up>>::value),
-           _CheckOptionalArgsConstructor,
-           __check_tuple_constructor_fail >;
-  template <class _QualUp>
-  struct _CheckOptionalLikeConstructor {
-    template <class _Up, class _Opt = optional<_Up>>
-    using __check_constructible_from_opt _LIBCPP_NODEBUG =
-        _Or< is_constructible<_Tp, _Opt&>,
-             is_constructible<_Tp, _Opt const&>,
-             is_constructible<_Tp, _Opt&&>,
-             is_constructible<_Tp, _Opt const&&>,
-             is_convertible<_Opt&, _Tp>,
-             is_convertible<_Opt const&, _Tp>,
-             is_convertible<_Opt&&, _Tp>,
-             is_convertible<_Opt const&&, _Tp> >;
-    template <class _Up, class _Opt = optional<_Up>>
-    using __check_assignable_from_opt _LIBCPP_NODEBUG =
-        _Or< is_assignable<_Tp&, _Opt&>,
-             is_assignable<_Tp&, _Opt const&>,
-             is_assignable<_Tp&, _Opt&&>,
-             is_assignable<_Tp&, _Opt const&&> >;
-    template <class _Up, class _QUp = _QualUp>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
-      return is_convertible<_QUp, _Tp>::value &&
-             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
-    }
-    template <class _Up, class _QUp = _QualUp>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
-      return !is_convertible<_QUp, _Tp>::value &&
-             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
-    }
-    template <class _Up, class _QUp = _QualUp>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_assign() {
-      // Construction and assignability of _QUp to _Tp has already been
-      // checked.
-      return !__check_constructible_from_opt<_Up>::value && !__check_assignable_from_opt<_Up>::value;
-    }
-  };
-
-  template <class _Up, class _QualUp>
-  using _CheckOptionalLikeCtor _LIBCPP_NODEBUG =
-      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp> >::value,
-           _CheckOptionalLikeConstructor<_QualUp>,
-           __check_tuple_constructor_fail >;
-  template <class _Up, class _QualUp>
-  using _CheckOptionalLikeAssign _LIBCPP_NODEBUG =
-      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>, is_assignable<_Tp&, _QualUp> >::value,
-           _CheckOptionalLikeConstructor<_QualUp>,
-           __check_tuple_constructor_fail >;
-
-public:
-  _LIBCPP_HIDE_FROM_ABI constexpr optional() noexcept {}
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(const optional&) = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(optional&&)      = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(nullopt_t) noexcept {}
-
-  template <class _InPlaceT,
-            class... _Args,
-            enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, is_constructible<_Tp, _Args...>>::value, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_InPlaceT, _Args&&... __args)
-      : __base(in_place, std::forward<_Args>(__args)...) {}
-
-  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
-      : __base(in_place, __il, std::forward<_Args>(__args)...) {}
-
-  template <class _Up = _Tp, enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
-      : __base(in_place, std::forward<_Up>(__v)) {}
-
-  template <class _Up                                                                        = remove_cv_t<_Tp>,
-            enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) noexcept(
-      is_nothrow_constructible_v<_Tp, _Up>) // strengthened
-      : __base(in_place, std::forward<_Up>(__v)) {}
-
-  // LWG2756: conditionally explicit conversion from const optional<_Up>&
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_implicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) {
-    this->__construct_from(__v);
-  }
-
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_explicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) {
-    this->__construct_from(__v);
-  }
-
-  // LWG2756: conditionally explicit conversion from optional<_Up>&&
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_implicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) {
-    this->__construct_from(std::move(__v));
-  }
-
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_explicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) {
-    this->__construct_from(std::move(__v));
-  }
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Tag,
-            class _Fp,
-            class... _Args,
-            enable_if_t<_IsSame<_Tag, __optional_construct_from_invoke_tag>::value, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
-      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(nullopt_t) noexcept {
-    reset();
-    return *this;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(const optional&) = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(optional&&)      = default;
-
-  // LWG2756
-  template <class _Up        = remove_cv_t<_Tp>,
-            enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>,
-                             _Or<_IsNotSame<__remove_cvref_t<_Up>, _Tp>, _Not<is_scalar<_Tp>>>,
-                             is_constructible<_Tp, _Up>,
-                             is_assignable<_Tp&, _Up>>::value,
-                        int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(_Up&& __v) {
-    if (this->has_value())
-      this->__get() = std::forward<_Up>(__v);
-    else
-      this->__construct(std::forward<_Up>(__v));
-    return *this;
-  }
-
-  // LWG2756
-  template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>::template __enable_assign<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(const optional<_Up>& __v) {
-    this->__assign_from(__v);
-    return *this;
-  }
-
-  // LWG2756
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_assign<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(optional<_Up>&& __v) {
-    this->__assign_from(std::move(__v));
-    return *this;
-  }
-
-  template <class... _Args, enable_if_t<is_constructible_v<_Tp, _Args...>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) {
-    reset();
-    this->__construct(std::forward<_Args>(__args)...);
-    return this->__get();
-  }
-
-  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) {
-    reset();
-    this->__construct(__il, std::forward<_Args>(__args)...);
-    return this->__get();
-  }
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-  swap(optional& __opt) noexcept((is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>)) {
-    this->__swap(__opt);
-  }
-
-  using __base::operator*;
-  using __base::operator->;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return has_value(); }
-
-  using __base::__get;
-  using __base::has_value;
-  using __base::value;
-
-  template <class _Up = remove_cv_t<_Tp>>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
-    static_assert(is_copy_constructible_v<_Tp>, "optional<T>::value_or: T must be copy constructible");
-    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
-    return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v));
-  }
-
-  template <class _Up = remove_cv_t<_Tp>>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
-    static_assert(is_move_constructible_v<_Tp>, "optional<T>::value_or: T must be move constructible");
-    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
-    return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v));
-  }
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
-    using _Up = invoke_result_t<_Func, _Tp&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), value());
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
-    using _Up = invoke_result_t<_Func, const _Tp&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), value());
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
-    using _Up = invoke_result_t<_Func, _Tp&&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(std::move(value())) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), std::move(value()));
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
-    using _Up = invoke_result_t<_Func, const _Tp&&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(std::move(value())) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), std::move(value()));
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & {
-    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
-    return optional<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& {
-    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
-    return optional<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && {
-    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
-    return optional<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& {
-    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
-    return optional<_Up>();
-  }
-
-  template <invocable _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const&
-    requires is_copy_constructible_v<_Tp>
-  {
-    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
-                  "Result of f() should be the same type as this optional");
-    if (*this)
-      return *this;
-    return std::forward<_Func>(__f)();
-  }
-
-  template <invocable _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) &&
-    requires is_move_constructible_v<_Tp>
-  {
-    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
-                  "Result of f() should be the same type as this optional");
-    if (*this)
-      return std::move(*this);
-    return std::forward<_Func>(__f)();
-  }
-#    endif // _LIBCPP_STD_VER >= 23
-
-  using __base::reset;
-};
-
-#    if _LIBCPP_STD_VER >= 26
-template <class _Tp>
-class optional<_Tp&> : public __optional_iterator_base<_Tp&> {
-  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp&>;
-
-  template <class _Up, class _QualUp>
-  static constexpr bool __check_optionalU_ctor =
-      !is_same_v<remove_cv_t<_Tp>, optional<_Up>> && !is_same_v<_Tp&, _Up> && is_constructible_v<_Tp&, _QualUp>;
-  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
-  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-
-public:
-  using value_type = _Tp;
-
-  constexpr optional() noexcept = default;
-  constexpr optional(nullopt_t) noexcept {}
-  constexpr optional(const optional&) noexcept = default;
-
-  template <class _Arg>
-    requires(is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>)
-  constexpr explicit optional(in_place_t, _Arg&& __arg) : __base(in_place, std::forward<_Arg>(__arg)) {}
-
-  template <class _Up>
-    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
-             is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>) optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>)
-      : __base(in_place, std::forward<_Up>(__v)) {}
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up&> && !reference_constructs_from_temporary_v<_Tp&, _Up&>)
-  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
-      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) {
-    this->__construct_from(__rhs);
-  }
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up&> && !reference_constructs_from_temporary_v<_Tp&, const _Up&>)
-  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
-      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) {
-    this->__construct_from(__rhs);
-  }
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
-      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
-    this->__construct_from(std::move(__rhs));
-  }
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up> && !reference_constructs_from_temporary_v<_Tp&, const _Up>)
-  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
-      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) {
-    this->__construct_from(std::move(__rhs));
-  }
-
-  template <class _Tag, class _Fp, class... _Args>
-    requires(is_same_v<_Tag, __optional_construct_from_invoke_tag>)
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
-      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
-
-  // deleted overloads
-
-  template <class _Up>
-    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
-             is_constructible_v<_Tp&, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
-      optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up&> && reference_constructs_from_temporary_v<_Tp&, _Up&>)
-  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
-      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up&> && reference_constructs_from_temporary_v<_Tp&, const _Up&>)
-  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
-      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
-      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up> && reference_constructs_from_temporary_v<_Tp&, const _Up>)
-  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
-      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) = delete;
-
-  constexpr ~optional() = default;
-
-  using __base::__get;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(nullopt_t) noexcept {
-    reset();
-    return *this;
-  }
-
-  constexpr optional& operator=(const optional&) noexcept = default;
-
-  template <class _Up>
-    requires(is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr _Tp& emplace(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
-    this->__construct(std::forward<_Up>(__u));
-
-    return this->__get();
-  }
-
-  constexpr void swap(optional& __rhs) noexcept { this->__swap(__rhs); }
-
-  using __base::operator->;
-  using __base::operator*;
-
-  constexpr explicit operator bool() const noexcept { return has_value(); }
-
-  using __base::has_value;
-  using __base::value;
-
-  template <class _Up = remove_cv_t<_Tp>>
-    requires(!is_array_v<_Tp> && is_object_v<_Tp>)
-  [[nodiscard]] constexpr decay_t<_Tp> value_or(_Up&& __v) const {
-    using _XTp = remove_cv_t<_Tp>;
-    static_assert(is_constructible_v<_XTp, _Tp&>, "optional<T&>::value_or: remove_cv_t<T> must be constructible");
-    static_assert(is_convertible_v<_Up, _XTp>, "optional<T&>::value_or: U must be convertible to remove_cv_t<T>");
-    return this->has_value() ? this->__get() : static_cast<_XTp>(std::forward<_Up>(__v));
-  }
-
-  template <class _Func>
-  [[nodiscard]] constexpr auto and_then(_Func&& __f) const {
-    using _Up = invoke_result_t<_Func, _Tp&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), value());
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] constexpr optional<remove_cv_t<invoke_result_t<_Func, _Tp&>>> transform(_Func&& __f) const {
-    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
-    return optional<_Up>();
-  }
-
-  template <invocable _Func>
-  [[nodiscard]] constexpr optional or_else(_Func&& __f) const {
-    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
-                  "Result of f() should be the same type as this optional");
-    if (*this)
-      return *this;
-    return std::forward<_Func>(__f)();
-  }
-
-  using __base::reset;
-};
-#    endif
-
 template <class _Tp>
 optional(_Tp) -> optional<_Tp>;
 
-// [optional.relops] Relational operators
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (static_cast<bool>(__x) != static_cast<bool>(__y))
-    return false;
-  if (!static_cast<bool>(__x))
-    return true;
-  return *__x == *__y;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (static_cast<bool>(__x) != static_cast<bool>(__y))
-    return true;
-  if (!static_cast<bool>(__x))
-    return false;
-  return *__x != *__y;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__y))
-    return false;
-  if (!static_cast<bool>(__x))
-    return true;
-  return *__x < *__y;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__x))
-    return false;
-  if (!static_cast<bool>(__y))
-    return true;
-  return *__x > *__y;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__x))
-    return true;
-  if (!static_cast<bool>(__y))
-    return false;
-  return *__x <= *__y;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__y))
-    return true;
-  if (!static_cast<bool>(__x))
-    return false;
-  return *__x >= *__y;
-}
-
-#    if _LIBCPP_STD_VER >= 20
-
-template <class _Tp, three_way_comparable_with<_Tp> _Up>
-_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
-operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (__x && __y)
-    return *__x <=> *__y;
-  return __x.has_value() <=> __y.has_value();
-}
-
-#    endif // _LIBCPP_STD_VER >= 20
-
-// [optional.nullops] Comparison with nullopt
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-#    if _LIBCPP_STD_VER <= 17
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>&, nullopt_t) noexcept {
-  return false;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(nullopt_t, const optional<_Tp>&) noexcept {
-  return true;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(nullopt_t, const optional<_Tp>&) noexcept {
-  return false;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>&, nullopt_t) noexcept {
-  return true;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-#    else // _LIBCPP_STD_VER <= 17
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return __x.has_value() <=> false;
-}
-
-#    endif // _LIBCPP_STD_VER <= 17
-
-// [optional.comp.with.t] Comparison with T
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x == __v;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v == *__x;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x != __v;
-  return true;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v != *__x;
-  return true;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x < __v;
-  return true;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v < *__x;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x <= __v;
-  return true;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v <= *__x;
-  return false;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x > __v;
-  return false;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v > *__x;
-  return true;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x >= __v;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v >= *__x;
-  return true;
-}
-
-#    if _LIBCPP_STD_VER >= 20
-
-template <class _Tp, class _Up>
-  requires(!__is_derived_from_optional<_Up>) && three_way_comparable_with<_Tp, _Up>
-_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
-operator<=>(const optional<_Tp>& __x, const _Up& __v) {
-  return __x.has_value() ? *__x <=> __v : strong_ordering::less;
-}
-
-#    endif // _LIBCPP_STD_VER >= 20
-
-template <class _Tp,
-          enable_if_t<(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>)
-#    if _LIBCPP_STD_VER >= 26
-                          || is_reference_v<_Tp>
-#    endif
-                      ,
-                      int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) {
-  __x.swap(__y);
-}
-
 struct __make_optional_barrier_tag {
   explicit __make_optional_barrier_tag() = default;
 };
@@ -1741,18 +353,6 @@ make_optional(initializer_list<_Up> __il, _Args&&... __args) {
   return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...);
 }
 
-template <class _Tp>
-struct hash< __enable_hash_helper<optional<_Tp>, remove_const_t<_Tp>> > {
-#    if _LIBCPP_STD_VER <= 17 || defined(_LIBCPP_ENABLE_CXX20_REMOVED_BINDER_TYPEDEFS)
-  _LIBCPP_DEPRECATED_IN_CXX17 typedef optional<_Tp> argument_type;
-  _LIBCPP_DEPRECATED_IN_CXX17 typedef size_t result_type;
-#    endif
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t operator()(const optional<_Tp>& __opt) const {
-    return static_cast<bool>(__opt) ? hash<remove_const_t<_Tp>>()(*__opt) : 0;
-  }
-};
-
 _LIBCPP_END_NAMESPACE_STD
 
 #  endif // _LIBCPP_STD_VER >= 17
diff --git a/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.ops/ostream_joiner.op.assign.pass.cpp b/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.ops/ostream_joiner.op.assign.pass.cpp
index 7a193e839f2a3..f7e5bdcccbeef 100644
--- a/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.ops/ostream_joiner.op.assign.pass.cpp
+++ b/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.ops/ostream_joiner.op.assign.pass.cpp
@@ -22,6 +22,7 @@
 #include <iostream>
 #include <sstream>
 #include <cassert>
+#include <type_traits>
 
 #include "test_macros.h"
 #include "test_iterators.h"
diff --git a/libcxx/test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp b/libcxx/test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp
index 9cf3b08930121..606ee4002040b 100644
--- a/libcxx/test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp
@@ -12,6 +12,7 @@
 
 // class bad_optional_access : public exception
 
+#include <exception>
 #include <optional>
 #include <type_traits>
 
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp
index 00ca941668eb2..a13b4334da332 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp
@@ -26,26 +26,26 @@ int main(int, char**) {
   // clang-format off
   {
 #if TEST_STD_VER >= 26
-    std::optional<int&&> opt2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
+    std::optional<int&&> opt2; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
 #else
-    std::optional<char&> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
+    std::optional<char&> o1; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
 #endif
-    std::optional<NonDestructible> o2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
-    std::optional<char[20]> o3; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}}
+    std::optional<NonDestructible> o2; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+    std::optional<char[20]> o3; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}}
   }
 
   {
-    std::optional<               std::in_place_t> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
-    std::optional<const          std::in_place_t> o2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
-    std::optional<      volatile std::in_place_t> o3; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
-    std::optional<const volatile std::in_place_t> o4; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
+    std::optional<               std::in_place_t> o1; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
+    std::optional<const          std::in_place_t> o2; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
+    std::optional<      volatile std::in_place_t> o3; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
+    std::optional<const volatile std::in_place_t> o4; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
   }
 
   {
-    std::optional<               std::nullopt_t> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
-    std::optional<const          std::nullopt_t> o2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
-    std::optional<      volatile std::nullopt_t> o3; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
-    std::optional<const volatile std::nullopt_t> o4; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional<               std::nullopt_t> o1; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional<const          std::nullopt_t> o2; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional<      volatile std::nullopt_t> o3; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional<const volatile std::nullopt_t> o4; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
   }
   // clang-format on
   return 0;
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/deduct.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/deduct.verify.cpp
index 7ab842bf68c51..28d3c61db4a9a 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/deduct.verify.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/deduct.verify.cpp
@@ -31,7 +31,7 @@ int main(int, char**) {
 
   {
     //  optional(nullopt_t)
-    std::optional opt(std::nullopt); // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional opt(std::nullopt); // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
   }
   // clang-format on
   return 0;
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp
index a956ab3a219cf..cdf456c4154ff 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp
@@ -28,30 +28,30 @@ int main(int, char**)
     using std::optional;
     {
 #if TEST_STD_VER >= 26
-      // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
 #else
-      // expected-error-re at optional:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
+      // expected-error-re@*:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
 #endif
       optional<int&> opt1;
       optional<int&&> opt2;
     }
     {
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
-        optional<X> opt3;
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+      optional<X> opt3;
     }
     {
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-object type is undefined behavior}}
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
-        optional<void()> opt4;
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-object type is undefined behavior}}
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+      optional<void()> opt4;
     }
     {
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-object type is undefined behavior}}
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
-        // expected-error at optional:* 1+ {{cannot form a reference to 'void'}}
-        optional<const void> opt4;
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-object type is undefined behavior}}
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+      // expected-error@*:* 1+ {{cannot form a reference to 'void'}}
+      optional<const void> opt4;
     }
     // FIXME these are garbage diagnostics that Clang should not produce
-    // expected-error at optional:* 0+ {{is not a base class}}
+    // expected-error@*:* 0+ {{is not a base class}}
 
-  return 0;
+    return 0;
 }

>From 6f2ce7fc219f53aa526afe3448f4a04094cd0b04 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 30 Jun 2026 00:57:53 -0400
Subject: [PATCH 2/8] Formatting

---
 libcxx/include/__optional/common.h       | 12 +++----
 libcxx/include/__optional/optional.h     | 44 ++++++++++++------------
 libcxx/include/__optional/optional_ref.h | 18 +++++-----
 3 files changed, 37 insertions(+), 37 deletions(-)

diff --git a/libcxx/include/__optional/common.h b/libcxx/include/__optional/common.h
index 2b4862cfd9d34..eb6cff368789a 100644
--- a/libcxx/include/__optional/common.h
+++ b/libcxx/include/__optional/common.h
@@ -64,18 +64,18 @@ struct __is_std_optional : false_type {};
 template <class _Tp>
 struct __is_std_optional<optional<_Tp>> : true_type {};
 
-template<class _Tp, bool = is_reference_v<_Tp>>
+template <class _Tp, bool = is_reference_v<_Tp>>
 struct __optional_storage_base;
 
-#    if _LIBCPP_STD_VER < 26
+#  if _LIBCPP_STD_VER < 26
 template <class _Tp>
 inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
-#    else
+#  else
 template <class _Tp>
 inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp> || is_lvalue_reference_v<_Tp>;
-#    endif
+#  endif
 
-#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
 constexpr bool ranges::enable_view<optional<_Tp>> = true;
@@ -86,7 +86,7 @@ constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
 template <class _Tp>
 constexpr bool ranges::enable_borrowed_range<optional<_Tp&>> = true;
 
-#    endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index d01d8e661bdca..148b28e04d9ca 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -96,12 +96,12 @@ struct __optional_destruct_base<_Tp, false> {
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
       : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <class _Fp, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(
       __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
       : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
     if (__engaged_) {
@@ -127,12 +127,12 @@ struct __optional_destruct_base<_Tp, true> {
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
       : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <class _Fp, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
       __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
       : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
     if (__engaged_) {
@@ -358,7 +358,7 @@ struct __optional_iterator_base : __optional_move_assign_base<_Tp> {
   using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
 };
 
-#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
   requires is_object_v<_Tp>
@@ -370,33 +370,33 @@ struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
 public:
   using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
   using iterator       = __bounded_iter<__pointer>;
   using const_iterator = __bounded_iter<__const_pointer>;
-#        else
+#    else
   using iterator       = __capacity_aware_iterator<__pointer, optional<_Tp>, 1>;
   using const_iterator = __capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>;
-#        endif
+#    endif
 
   // [optional.iterators], iterator support
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
     auto* __ptr = std::addressof(this->__get());
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
     return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
+#    else
     return std::__make_capacity_aware_iterator<__pointer, optional<_Tp>, 1>(__ptr);
-#        endif
+#    endif
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
     auto* __ptr = std::addressof(this->__get());
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
     return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
+#    else
     return std::__make_capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>(__ptr);
-#        endif
+#    endif
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
@@ -407,7 +407,7 @@ struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
   }
 };
 
-#    endif   // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
 class _LIBCPP_DECLSPEC_EMPTY_BASES optional
@@ -425,11 +425,11 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
 private:
   static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
   static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-#    if _LIBCPP_STD_VER >= 26
+#  if _LIBCPP_STD_VER >= 26
   static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
-#    else
+#  else
   static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
-#    endif
+#  endif
   static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
   static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
 
@@ -546,14 +546,14 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
     this->__construct_from(std::move(__v));
   }
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <class _Tag,
             class _Fp,
             class... _Args,
             enable_if_t<_IsSame<_Tag, __optional_construct_from_invoke_tag>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
       : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
-#    endif
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(nullopt_t) noexcept {
     reset();
@@ -634,7 +634,7 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
     return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v));
   }
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <class _Func>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
     using _Up = invoke_result_t<_Func, _Tp&>;
@@ -748,7 +748,7 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
       return std::move(*this);
     return std::forward<_Func>(__f)();
   }
-#    endif // _LIBCPP_STD_VER >= 23
+#  endif // _LIBCPP_STD_VER >= 23
 
   using __base::reset;
 };
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index dd1adc1642ca3..eccf925f525d6 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -143,12 +143,12 @@ struct __optional_storage_base<_Tp, true> {
 template <class _Tp>
 struct __optional_iterator_base;
 
-template<class _Tp>
+template <class _Tp>
 struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
   using __optional_storage_base<_Tp&>::__optional_storage_base;
 };
 
-#if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
   requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
@@ -159,22 +159,22 @@ struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
 public:
   using __optional_storage_base<_Tp&>::__optional_storage_base;
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
   using iterator = __bounded_iter<__pointer>;
-#        else
+#    else
   using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
-#        endif
+#    endif
 
   // [optional.ref.iterators], iterator support
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
     auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
     return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
+#    else
     return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
-#        endif
+#    endif
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
@@ -182,7 +182,7 @@ struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
   }
 };
 
-#endif
+#  endif
 
 template <class _Tp>
 class optional<_Tp&> : public __optional_iterator_base<_Tp&> {

>From b5ade2cb1b1469b78d68e21a08de4c0f5012f2dc Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 30 Jun 2026 18:43:58 -0400
Subject: [PATCH 3/8] Address comments

---
 libcxx/include/__fwd/optional.h          |  3 +++
 libcxx/include/__optional/common.h       |  9 +++------
 libcxx/include/__optional/hash.h         |  3 +--
 libcxx/include/__optional/optional.h     |  2 +-
 libcxx/include/__optional/optional_ref.h | 24 ++++++++++++------------
 5 files changed, 20 insertions(+), 21 deletions(-)

diff --git a/libcxx/include/__fwd/optional.h b/libcxx/include/__fwd/optional.h
index 35a3d2ea11e74..303c2d703e551 100644
--- a/libcxx/include/__fwd/optional.h
+++ b/libcxx/include/__fwd/optional.h
@@ -27,6 +27,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Tp>
 class optional;
 
+template <class _Tp>
+class optional<_Tp&>;
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/common.h b/libcxx/include/__optional/common.h
index eb6cff368789a..609d93fa38443 100644
--- a/libcxx/include/__optional/common.h
+++ b/libcxx/include/__optional/common.h
@@ -34,9 +34,9 @@ namespace std // purposefully not using versioning namespace
 
 class _LIBCPP_EXPORTED_FROM_ABI bad_optional_access : public exception {
 public:
-  bad_optional_access() _NOEXCEPT                                      = default;
-  bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
-  bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
+  _LIBCPP_HIDE_FROM_ABI bad_optional_access() _NOEXCEPT                                      = default;
+  _LIBCPP_HIDE_FROM_ABI bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
+  _LIBCPP_HIDE_FROM_ABI bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
   // Get the key function ~bad_optional_access() into the dylib
   ~bad_optional_access() _NOEXCEPT override;
   [[__nodiscard__]] const char* what() const _NOEXCEPT override;
@@ -64,9 +64,6 @@ struct __is_std_optional : false_type {};
 template <class _Tp>
 struct __is_std_optional<optional<_Tp>> : true_type {};
 
-template <class _Tp, bool = is_reference_v<_Tp>>
-struct __optional_storage_base;
-
 #  if _LIBCPP_STD_VER < 26
 template <class _Tp>
 inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
diff --git a/libcxx/include/__optional/hash.h b/libcxx/include/__optional/hash.h
index 830f99b0b855f..ac86b2fb63488 100644
--- a/libcxx/include/__optional/hash.h
+++ b/libcxx/include/__optional/hash.h
@@ -13,9 +13,8 @@
 #include <__config>
 #include <__cstddef/size_t.h>
 #include <__functional/hash.h>
-#include <__type_traits/remove_const.h>
-
 #include <__fwd/optional.h>
+#include <__type_traits/remove_const.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index 148b28e04d9ca..21a4f88968378 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -141,7 +141,7 @@ struct __optional_destruct_base<_Tp, true> {
   }
 };
 
-template <class _Tp, bool>
+template <class _Tp>
 struct __optional_storage_base : __optional_destruct_base<_Tp> {
   using __base _LIBCPP_NODEBUG = __optional_destruct_base<_Tp>;
   using value_type             = _Tp;
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index eccf925f525d6..093e29080cecf 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -58,12 +58,12 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp>
-struct __optional_storage_base<_Tp, true> {
+struct __optional_ref_base {
   using value_type                 = _Tp;
   using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
   __raw_type* __value_;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_ref_base() noexcept : __value_(nullptr) {}
 
   template <class _Up>
   _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
@@ -72,7 +72,7 @@ struct __optional_storage_base<_Tp, true> {
   }
 
   template <class _UArg>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) {
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_ref_base(in_place_t, _UArg&& __uarg) {
     static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
                   "Attempted to construct a reference element in optional from a "
                   "possible temporary");
@@ -80,7 +80,7 @@ struct __optional_storage_base<_Tp, true> {
   }
 
   template <class _Fp, class... _Args>
-  constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
+  constexpr __optional_ref_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
     __convert_init_ref_val(std::forward<invoke_result_t<_Fp, _Args...>>(
         std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)));
   }
@@ -118,7 +118,7 @@ struct __optional_storage_base<_Tp, true> {
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_ref_base& __rhs) noexcept {
     std::swap(__value_, __rhs.__value_);
   }
 
@@ -141,23 +141,23 @@ struct __optional_storage_base<_Tp, true> {
 };
 
 template <class _Tp>
-struct __optional_iterator_base;
+struct __optional_ref_iterator_base;
 
 template <class _Tp>
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
+struct __optional_ref_iterator_base<_Tp&> : __optional_ref_base<_Tp&> {
+  using __optional_ref_base<_Tp&>::__optional_ref_base;
 };
 
 #  if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
   requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
+struct __optional_ref_iterator_base<_Tp&> : __optional_ref_base<_Tp&> {
 private:
   using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
 
 public:
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
+  using __optional_ref_base<_Tp&>::__optional_ref_base;
 
 #    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
   using iterator = __bounded_iter<__pointer>;
@@ -185,8 +185,8 @@ struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
 #  endif
 
 template <class _Tp>
-class optional<_Tp&> : public __optional_iterator_base<_Tp&> {
-  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp&>;
+class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
+  using __base _LIBCPP_NODEBUG = __optional_ref_iterator_base<_Tp&>;
 
   template <class _Up, class _QualUp>
   static constexpr bool __check_optionalU_ctor =

>From 79ea6f9fff2345447e181e97c7d65270919f531a Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 16 Jul 2026 19:51:55 -0400
Subject: [PATCH 4/8] Make_optional to header, deduction guide to forward
 declaration, partial specializations to maintain similar error message

---
 libcxx/include/__fwd/optional.h           |  3 +
 libcxx/include/__optional/make_optional.h | 90 +++++++++++++++++++++++
 libcxx/include/__optional/optional.h      | 11 +--
 libcxx/include/__optional/optional_ref.h  | 11 +++
 libcxx/include/optional                   | 75 +------------------
 5 files changed, 111 insertions(+), 79 deletions(-)
 create mode 100644 libcxx/include/__optional/make_optional.h

diff --git a/libcxx/include/__fwd/optional.h b/libcxx/include/__fwd/optional.h
index 303c2d703e551..1e74a75ea0dbe 100644
--- a/libcxx/include/__fwd/optional.h
+++ b/libcxx/include/__fwd/optional.h
@@ -30,6 +30,9 @@ class optional;
 template <class _Tp>
 class optional<_Tp&>;
 
+template <class _Tp>
+optional(_Tp) -> optional<_Tp>;
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/make_optional.h b/libcxx/include/__optional/make_optional.h
new file mode 100644
index 0000000000000..202e09071116d
--- /dev/null
+++ b/libcxx/include/__optional/make_optional.h
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// std::optional<T&>
+
+#ifndef _LIBCPP_OPTIONAL_MAKE_OPTIONAL_H
+#define _LIBCPP_OPTIONAL_MAKE_OPTIONAL_H
+
+#include <__config>
+#include <__fwd/optional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/reference_constructs_from_temporary.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+
+#include <initializer_list>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+struct __make_optional_barrier_tag {
+  explicit __make_optional_barrier_tag() = default;
+};
+
+template <class _Tp, class... _Args>
+inline constexpr bool __is_constructible_for_optional_v = is_constructible_v<_Tp, _Args...>;
+
+template <class _Tp, class... _Args>
+struct __is_constructible_for_optional : bool_constant<__is_constructible_for_optional_v<_Tp, _Args...>> {};
+
+template <class _Tp, class _Up, class... _Args>
+inline constexpr bool __is_constructible_for_optional_initializer_list_v =
+    is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>;
+
+#  if _LIBCPP_STD_VER >= 26
+template <class _Tp, class... _Args>
+inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Args...> = false;
+
+template <class _Tp, class _Arg>
+inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Arg> =
+    is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>;
+
+template <class _Tp, class _Up, class... _Args>
+inline constexpr bool __is_constructible_for_optional_initializer_list_v<_Tp&, _Up, _Args...> = false;
+#  endif
+
+template <
+#  if _LIBCPP_STD_VER >= 26
+    __make_optional_barrier_tag = __make_optional_barrier_tag{},
+#  endif
+    class _Tp,
+    enable_if_t<is_constructible_v<decay_t<_Tp>, _Tp>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<decay_t<_Tp>> make_optional(_Tp&& __v) {
+  return optional<decay_t<_Tp>>(std::forward<_Tp>(__v));
+}
+
+template <class _Tp, class... _Args, enable_if_t<__is_constructible_for_optional_v<_Tp, _Args...>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(_Args&&... __args) {
+  return optional<_Tp>(in_place, std::forward<_Args>(__args)...);
+}
+
+template <class _Tp,
+          class _Up,
+          class... _Args,
+          enable_if_t<__is_constructible_for_optional_initializer_list_v<_Tp, _Up, _Args...>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp>
+make_optional(initializer_list<_Up> __il, _Args&&... __args) {
+  return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+#endif // _LIBCPP_OPTIONAL_MAKE_OPTIONAL_H
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index 21a4f88968378..4726b706cca84 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -425,11 +425,6 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
 private:
   static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
   static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-#  if _LIBCPP_STD_VER >= 26
-  static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
-#  else
-  static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
-#  endif
   static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
   static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
 
@@ -752,6 +747,12 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
 
   using __base::reset;
 };
+
+template <class _Tp>
+class optional<_Tp&&> {
+  static_assert(false, "instantiation of optional with an rvalue reference type is ill-formed");
+};
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index 093e29080cecf..9bd2831fe0d1a 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -348,6 +348,17 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
 
 _LIBCPP_END_NAMESPACE_STD
 
+#else
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+class optional<_Tp&> {
+  static_assert(false, "instantiation of optional with a reference type is ill-formed");
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
 #endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_POP_MACROS
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 2b40706910018..914fd8cc9e226 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -259,19 +259,12 @@ namespace std {
 #  include <__cxx03/__config>
 #else
 #  include <__config>
-#  include <__type_traits/decay.h>
-#  include <__type_traits/enable_if.h>
-#  include <__type_traits/is_constructible.h>
-#  include <__type_traits/reference_constructs_from_temporary.h>
-#  include <__type_traits/remove_const.h>
-#  include <__utility/forward.h>
-#  include <__utility/in_place.h>
-#  include <initializer_list>
 #  include <version>
 
 #  include <__optional/common.h>
 #  include <__optional/comparison.h>
 #  include <__optional/hash.h>
+#  include <__optional/make_optional.h>
 #  include <__optional/optional.h>
 #  include <__optional/optional_ref.h>
 #  include <__optional/swap.h>
@@ -293,72 +286,6 @@ namespace std {
 #    pragma GCC system_header
 #  endif
 
-_LIBCPP_PUSH_MACROS
-#  include <__undef_macros>
-
-#  if _LIBCPP_STD_VER >= 17
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-template <class _Tp>
-optional(_Tp) -> optional<_Tp>;
-
-struct __make_optional_barrier_tag {
-  explicit __make_optional_barrier_tag() = default;
-};
-
-template <class _Tp, class... _Args>
-inline constexpr bool __is_constructible_for_optional_v = is_constructible_v<_Tp, _Args...>;
-
-template <class _Tp, class... _Args>
-struct __is_constructible_for_optional : bool_constant<__is_constructible_for_optional_v<_Tp, _Args...>> {};
-
-template <class _Tp, class _Up, class... _Args>
-inline constexpr bool __is_constructible_for_optional_initializer_list_v =
-    is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>;
-
-#    if _LIBCPP_STD_VER >= 26
-template <class _Tp, class... _Args>
-inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Args...> = false;
-
-template <class _Tp, class _Arg>
-inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Arg> =
-    is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>;
-
-template <class _Tp, class _Up, class... _Args>
-inline constexpr bool __is_constructible_for_optional_initializer_list_v<_Tp&, _Up, _Args...> = false;
-#    endif
-
-template <
-#    if _LIBCPP_STD_VER >= 26
-    __make_optional_barrier_tag = __make_optional_barrier_tag{},
-#    endif
-    class _Tp,
-    enable_if_t<is_constructible_v<decay_t<_Tp>, _Tp>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<decay_t<_Tp>> make_optional(_Tp&& __v) {
-  return optional<decay_t<_Tp>>(std::forward<_Tp>(__v));
-}
-
-template <class _Tp, class... _Args, enable_if_t<__is_constructible_for_optional_v<_Tp, _Args...>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(_Args&&... __args) {
-  return optional<_Tp>(in_place, std::forward<_Args>(__args)...);
-}
-
-template <class _Tp,
-          class _Up,
-          class... _Args,
-          enable_if_t<__is_constructible_for_optional_initializer_list_v<_Tp, _Up, _Args...>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp>
-make_optional(initializer_list<_Up> __il, _Args&&... __args) {
-  return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...);
-}
-
-_LIBCPP_END_NAMESPACE_STD
-
-#  endif // _LIBCPP_STD_VER >= 17
-
-_LIBCPP_POP_MACROS
-
 #  if defined(_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23) && _LIBCPP_STD_VER <= 20
 #    include <atomic>
 #    include <climits>

>From e48c148429d6ea3b26160dc62e6452d7760ec9a5 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 16 Jul 2026 20:07:22 -0400
Subject: [PATCH 5/8] Fix missing other things

---
 libcxx/include/CMakeLists.txt            | 1 +
 libcxx/include/__optional/optional.h     | 5 -----
 libcxx/include/__optional/optional_ref.h | 7 ++++++-
 libcxx/include/module.modulemap.in       | 1 +
 4 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 021d392e50c78..384ee68153abf 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -666,6 +666,7 @@ set(files
   __optional/comparison.h
   __optional/hash.h
   __optional/nullopt_t.h
+  __optional/make_optional.h
   __optional/optional.h
   __optional/optional_ref.h
   __optional/swap.h
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index 4726b706cca84..d8dbfd7188585 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -748,11 +748,6 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
   using __base::reset;
 };
 
-template <class _Tp>
-class optional<_Tp&&> {
-  static_assert(false, "instantiation of optional with an rvalue reference type is ill-formed");
-};
-
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index 9bd2831fe0d1a..763c91d13f949 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -348,7 +348,7 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
 
 _LIBCPP_END_NAMESPACE_STD
 
-#else
+#elif _LIBCPP_STD_VER >= 17
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -357,6 +357,11 @@ class optional<_Tp&> {
   static_assert(false, "instantiation of optional with a reference type is ill-formed");
 };
 
+template <class _Tp>
+class optional<_Tp&&> {
+  static_assert(false, "instantiation of optional with an rvalue reference type is ill-formed");
+};
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index e0c40bad3bae9..39a7d45b6c3e0 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1810,6 +1810,7 @@ module std [system] {
     }
 
     module comparison { header "__optional/comparison.h" }
+    module make_optional { header "__optional/make_optional.h" }
     module nullopt_t { header "__optional/nullopt_t.h" }
     module optional { header "__optional/optional.h" }
     module optional_ref { header "__optional/optional_ref.h" }

>From fec480eae52447a0ceefd7fa5776dbd7f6c016e2 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 16 Jul 2026 20:15:12 -0400
Subject: [PATCH 6/8] move rref failure-specialization back to optional.h

---
 libcxx/include/__optional/optional.h     | 5 +++++
 libcxx/include/__optional/optional_ref.h | 5 -----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index d8dbfd7188585..4726b706cca84 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -748,6 +748,11 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
   using __base::reset;
 };
 
+template <class _Tp>
+class optional<_Tp&&> {
+  static_assert(false, "instantiation of optional with an rvalue reference type is ill-formed");
+};
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index 763c91d13f949..51e690bdb5b33 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -357,11 +357,6 @@ class optional<_Tp&> {
   static_assert(false, "instantiation of optional with a reference type is ill-formed");
 };
 
-template <class _Tp>
-class optional<_Tp&&> {
-  static_assert(false, "instantiation of optional with an rvalue reference type is ill-formed");
-};
-
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 26

>From f9c8c56633c5e9bfdf34715719f30f577c44b0fa Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 16 Jul 2026 23:08:36 -0400
Subject: [PATCH 7/8] Reorder CMakeLists

---
 libcxx/include/CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 384ee68153abf..cbf652d1ab54a 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -665,8 +665,8 @@ set(files
   __optional/common.h
   __optional/comparison.h
   __optional/hash.h
-  __optional/nullopt_t.h
   __optional/make_optional.h
+  __optional/nullopt_t.h
   __optional/optional.h
   __optional/optional_ref.h
   __optional/swap.h

>From cd57d28700d5675334d8eee8afe2ae7ecd4742b7 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 17 Jul 2026 02:57:55 -0400
Subject: [PATCH 8/8] Missing pop macros

---
 libcxx/include/__optional/make_optional.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/__optional/make_optional.h b/libcxx/include/__optional/make_optional.h
index 202e09071116d..9aa8dee079a08 100644
--- a/libcxx/include/__optional/make_optional.h
+++ b/libcxx/include/__optional/make_optional.h
@@ -87,4 +87,7 @@ make_optional(initializer_list<_Up> __il, _Args&&... __args) {
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
 #endif // _LIBCPP_OPTIONAL_MAKE_OPTIONAL_H


From libcxx-commits at lists.llvm.org  Fri Jul 17 00:02:50 2026
From: libcxx-commits at lists.llvm.org (Demetrius Kanios via libcxx-commits)
Date: Fri, 17 Jul 2026 00:02:50 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxxabi] [libunwind] [llvm]
 [WebAssembly] Call EH personality function instead of
 `Unwind_CallPersonality` (PR #209282)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209282@github.com>
Message-ID: <6a59d39a.f6bcba31.3c848.bb69@mx.google.com>


================
@@ -274,15 +259,12 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
   // instruction selection.
   CatchF = Intrinsic::getOrInsertDeclaration(&M, Intrinsic::wasm_catch);
 
-  // FIXME: Verify this is really supported for current module.
-  StringRef UnwindCallPersonalityName =
-      RTLIB::RuntimeLibcallsInfo::getLibcallImplName(
-          RTLIB::impl__Unwind_CallPersonality);
+  auto *PersPrototype =
+      FunctionType::get(IRB.getInt32Ty(), {IRB.getPtrTy()}, false);
----------------
QuantumSegfault wrote:

An oversight. Copied from #175202.

But it seems we want it anyway

https://github.com/llvm/llvm-project/pull/209282

From libcxx-commits at lists.llvm.org  Fri Jul 17 00:03:21 2026
From: libcxx-commits at lists.llvm.org (Demetrius Kanios via libcxx-commits)
Date: Fri, 17 Jul 2026 00:03:21 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxxabi] [libunwind] [llvm]
 [WebAssembly] Call EH personality function instead of
 `Unwind_CallPersonality` (PR #209282)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209282@github.com>
Message-ID: <6a59d3b9.c589044b.3874b0.b19c@mx.google.com>


================
@@ -367,9 +349,16 @@ void WasmEHPrepareImpl::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
   // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
   IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
 
-  // Pseudocode: _Unwind_CallPersonality(exn);
-  CallInst *PersCI = IRB.CreateCall(CallPersonalityF, CatchCI,
-                                    OperandBundleDef("funclet", CPI));
+  // Pseudocode: personality_fn(exn);
+  CallInst *PersCI;
+
+  // Grab direct function when possible to use `call` instead of `call_indirect`
+  if (Function *F = dyn_cast<Function>(PersonalityF.getCallee()))
+    PersCI = IRB.CreateCall(F, CatchCI, OperandBundleDef("funclet", CPI));
+  else
+    PersCI =
+        IRB.CreateCall(PersonalityF, CatchCI, OperandBundleDef("funclet", CPI));
----------------
QuantumSegfault wrote:

Not that I'm aware of.

I'm not sure what would cause this to happen. Aliases maybe?

https://github.com/llvm/llvm-project/pull/209282

From libcxx-commits at lists.llvm.org  Fri Jul 17 00:04:19 2026
From: libcxx-commits at lists.llvm.org (Heejin Ahn via libcxx-commits)
Date: Fri, 17 Jul 2026 00:04:19 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxxabi] [libunwind] [llvm]
 [WebAssembly] Call EH personality function instead of
 `Unwind_CallPersonality` (PR #209282)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209282@github.com>
Message-ID: <6a59d3f3.3ff6034f.1783f7.5899@mx.google.com>


https://github.com/aheejin edited https://github.com/llvm/llvm-project/pull/209282

From libcxx-commits at lists.llvm.org  Fri Jul 17 00:08:08 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 17 Jul 2026 00:08:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++][NFC] Inline
 std::function members into the class body" (#209555) (PR #210260)
Message-ID: <llvm.org/llvm/llvm-project/pull/210260@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/210260

This caused LLDB to fail, which has been fixed now.

This reverts commit 7618426138aae95561da676a1d1e10ee0392bf78.


>From 6dba1b994587834a975aae947dc92e9c9f6aaf04 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 17 Jul 2026 09:07:20 +0200
Subject: [PATCH] Reapply "[libc++][NFC] Inline std::function members into the
 class body" (#209555)

This caused LLDB to fail, which has been fixed now.

This reverts commit 7618426138aae95561da676a1d1e10ee0392bf78.
---
 libcxx/include/__functional/function.h | 141 +++++++------------------
 1 file changed, 37 insertions(+), 104 deletions(-)

diff --git a/libcxx/include/__functional/function.h b/libcxx/include/__functional/function.h
index 90072e9528484..0adc336424e2e 100644
--- a/libcxx/include/__functional/function.h
+++ b/libcxx/include/__functional/function.h
@@ -340,7 +340,7 @@ template <typename _Fun>
 struct __use_small_storage
     : public integral_constant<
           bool,
-          sizeof(_Fun) <= sizeof(__policy_storage)&& _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
+          sizeof(_Fun) <= sizeof(__policy_storage) && _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
               is_trivially_copy_constructible<_Fun>::value && is_trivially_destructible<_Fun>::value> {};
 
 // Policy contains information about how to copy, destroy, and move the
@@ -628,10 +628,10 @@ class function<_Rp(_ArgTypes...)>
   // construct/copy/destroy:
   _LIBCPP_HIDE_FROM_ABI function() _NOEXCEPT {}
   _LIBCPP_HIDE_FROM_ABI function(nullptr_t) _NOEXCEPT {}
-  _LIBCPP_HIDE_FROM_ABI function(const function&);
-  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function(const function&)      = default;
+  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT = default;
   template <class _Fp, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(_Fp);
+  _LIBCPP_HIDE_FROM_ABI function(_Fp __f) : __f_(std::move(__f)) {}
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Alloc>
@@ -639,23 +639,35 @@ class function<_Rp(_ArgTypes...)>
   template <class _Alloc>
   _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, nullptr_t) _NOEXCEPT {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function&);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&&);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
   template <class _Fp, class _Alloc, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc& __a, _Fp __f);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI function& operator=(const function&);
-  _LIBCPP_HIDE_FROM_ABI function& operator=(function&&) _NOEXCEPT;
-  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function& operator=(const function& __f) {
+    function(__f).swap(*this);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI function& operator=(function&& __f) _NOEXCEPT = default;
+
+  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT {
+    __f_ = nullptr;
+    return *this;
+  }
+
   template <class _Fp, class = _EnableIfLValueCallable<__decay_t<_Fp>>>
-  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&&);
+  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&& __f) {
+    function(std::forward<_Fp>(__f)).swap(*this);
+    return *this;
+  }
 
-  _LIBCPP_HIDE_FROM_ABI ~function();
+  _LIBCPP_HIDE_FROM_ABI ~function() = default;
 
   // function modifiers:
-  _LIBCPP_HIDE_FROM_ABI void swap(function&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI void swap(function& __f) _NOEXCEPT { __f_.swap(__f.__f_); }
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Fp, class _Alloc>
@@ -677,15 +689,23 @@ class function<_Rp(_ArgTypes...)>
 
 public:
   // function invocation:
-  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes...) const;
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const { return __f_(std::forward<_ArgTypes>(__args)...); }
 
 #  if _LIBCPP_HAS_RTTI
   // function target access:
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT {
+    return __f_.target_type();
+  }
+
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT {
+    return (_Tp*)(__f_.template target<_Tp>());
+  }
+
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT {
+    return __f_.template target<_Tp>();
+  }
 #  endif // _LIBCPP_HAS_RTTI
 };
 
@@ -697,93 +717,6 @@ template <class _Fp, class _Stripped = __strip_signature_t<decltype(&_Fp::operat
 function(_Fp) -> function<_Stripped>;
 #  endif // _LIBCPP_STD_VER >= 17
 
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::function(const function& __f) : __f_(__f.__f_) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Alloc>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::function(function&& __f) _NOEXCEPT : __f_(std::move(__f.__f_)) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Alloc>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class>
-function<_Rp(_ArgTypes...)>::function(_Fp __f) : __f_(std::move(__f)) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class _Alloc, class>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(const function& __f) {
-  function(__f).swap(*this);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(function&& __f) _NOEXCEPT {
-  __f_ = std::move(__f.__f_);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(nullptr_t) _NOEXCEPT {
-  __f_ = nullptr;
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f) {
-  function(std::forward<_Fp>(__f)).swap(*this);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::~function() {}
-
-template <class _Rp, class... _ArgTypes>
-void function<_Rp(_ArgTypes...)>::swap(function& __f) _NOEXCEPT {
-  __f_.swap(__f.__f_);
-}
-
-template <class _Rp, class... _ArgTypes>
-_Rp function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const {
-  return __f_(std::forward<_ArgTypes>(__arg)...);
-}
-
-#  if _LIBCPP_HAS_RTTI
-
-template <class _Rp, class... _ArgTypes>
-const std::type_info& function<_Rp(_ArgTypes...)>::target_type() const _NOEXCEPT {
-  return __f_.target_type();
-}
-
-template <class _Rp, class... _ArgTypes>
-template <typename _Tp>
-_Tp* function<_Rp(_ArgTypes...)>::target() _NOEXCEPT {
-  return (_Tp*)(__f_.template target<_Tp>());
-}
-
-template <class _Rp, class... _ArgTypes>
-template <typename _Tp>
-const _Tp* function<_Rp(_ArgTypes...)>::target() const _NOEXCEPT {
-  return __f_.template target<_Tp>();
-}
-
-#  endif // _LIBCPP_HAS_RTTI
-
 template <class _Rp, class... _ArgTypes>
 inline _LIBCPP_HIDE_FROM_ABI bool operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) _NOEXCEPT {
   return !__f;


From libcxx-commits at lists.llvm.org  Fri Jul 17 00:12:05 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 17 Jul 2026 00:12:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a59d5c5.a2377b91.10cee2.c98b@mx.google.com>


https://github.com/philnik777 commented:

This basically LGTM, but I'd like the file moving to be separated. That has nothing to do with the actual patch AFAICT.

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Fri Jul 17 00:48:11 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 17 Jul 2026 00:48:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a59de3b.c4d3d80a.849c3.f1be@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/209161

>From 059ec6cec9cb06a0be51d521c1945870a3ad2b0d Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 14:28:02 +0200
Subject: [PATCH] [libc++] Optimize standard streams with
 sync_with_stdio(false)

Fixes #21566
---
 libcxx/include/fstream                        |  15 +--
 libcxx/lib/abi/CHANGELOG.TXT                  |  17 +++
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...xxabi.v1.stable.noexceptions.nonew.abilist |   5 +-
 libcxx/src/ios.cpp                            |   7 --
 libcxx/src/iostream.cpp                       | 102 ++++++++++++++----
 .../cin.sync_with_stdio.sh.cpp                |  37 +++++++
 .../clog.sync_with_stdio.sh.cpp               |  29 +++++
 .../cout.sync_with_stdio.sh.cpp               |  27 +++++
 .../wcin.sync_with_stdio.sh.cpp               |  44 ++++++++
 .../wclog.sync_with_stdio.sh.cpp              |  36 +++++++
 .../wcout.sync_with_stdio.sh.cpp              |  34 ++++++
 17 files changed, 340 insertions(+), 35 deletions(-)
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..4b276251aa917 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -391,10 +391,10 @@ private:
 
   _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 
-  // There are multiple (__)open function, they use different C-API open
-  // function. After that call these functions behave the same. This function
-  // does that part and determines the final return value.
-  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __do_open(FILE* __file, ios_base::openmode __mode) {
+public:
+  // Update the buffer so that __file is considered to be the underlying file and is opened as __mode. This is used in
+  // the open family of functions as well as various iostreams in sync_with_stdio(false) mode.
+  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __adopt_file(FILE* __file, ios_base::openmode __mode) {
     __file_ = __file;
     if (!__file_)
       return nullptr;
@@ -417,6 +417,7 @@ private:
     return this;
   }
 
+private:
   // If the file is already open, switch to unbuffered mode. Otherwise, record
   // the request to use unbuffered mode so that we use that mode when we
   // eventually open the file.
@@ -742,7 +743,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(std::fopen(__s, __mdstr), __mode);
+  return __adopt_file(std::fopen(__s, __mdstr), __mode);
 }
 
 template <class _CharT, class _Traits>
@@ -753,7 +754,7 @@ inline basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::__open(in
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(fdopen(__fd, __mdstr), __mode);
+  return __adopt_file(fdopen(__fd, __mdstr), __mode);
 }
 
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
@@ -767,7 +768,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(_wfopen(__s, __mdstr), __mode);
+  return __adopt_file(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
 
diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index b09f297eb65ba..567c23717ed60 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -12,6 +12,23 @@ To generate a summary, re-generate the new ABI list using the
 
 New entries should be added directly below the "Version" header.
 
+------------
+Version 24.0
+------------
+
+* [libc++] Optimize standard streams with sync_with_stdio(false)
+
+  This updates the standard streams so that the buffers are replaced with `basic_filebuf`s.
+
+  Fixes #21566
+
+
+  All platforms
+  -------------
+  Symbol added: _ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+
 ------------
 Version 23.0
 ------------
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 414a239757e1c..09999516face2 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 12, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
@@ -1908,6 +1909,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2143,6 +2145,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 20, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 64, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 590c0de8ce20f..d53eeb7532009 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index f4f4ab99ad866..384904f79d20e 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index e1b1e110318fa..8f26fdb0462cb 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1905,6 +1906,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2137,6 +2139,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 11cc8f180e824..0a2858da94a67 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1644,6 +1644,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1778,6 +1779,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1916,6 +1918,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..903457e983046 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1643,6 +1643,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1777,6 +1778,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1915,6 +1917,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 2b69d6dc89138..c24cdf845b489 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -2002,4 +2002,7 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT', 'is_defined': True}
\ No newline at end of file
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index 2e049098740dc..0b728db5b76f9 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -366,13 +366,6 @@ void ios_base::__set_failbit_and_consider_rethrow() {
 #endif // _LIBCPP_HAS_EXCEPTIONS
 }
 
-bool ios_base::sync_with_stdio(bool sync) {
-  static bool previous_state = true;
-  bool r                     = previous_state;
-  previous_state             = sync;
-  return r;
-}
-
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..551eb25f8fe4a 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -10,6 +10,7 @@
 
 #include <__memory/construct_at.h>
 #include <__ostream/basic_ostream.h>
+#include <fstream>
 #include <istream>
 
 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
@@ -34,21 +35,44 @@ union stream {
   StreamT value;
 };
 
-template <class StreamT, class BufferT>
+template <class StreamT, class SyncBufT, class UnsyncBufT>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
   struct {
-    BufferT buffer;
+    union {
+      SyncBufT sync_buffer;
+      UnsyncBufT unsync_buffer;
+    };
     mbstate_t mb;
   };
 };
 
-template <class StreamT, class BufferT>
-void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) {
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
   data.mb = {};
-  std::construct_at(&data.buffer, stdstream, &data.mb);
-  std::construct_at(&stream.value, &data.buffer);
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  std::construct_at(&stream.value, &data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_sync_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
+  data.unsync_buffer.__adopt_file(nullptr, {}); // reset the file, so that basic_filebuf doesn't close standard streams
+  std::destroy_at(&data.unsync_buffer);
+  data.mb = {};
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  stream.value.rdbuf(&data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_unsync_stream(FILE* stdstream,
+                             stream<StreamT>& stream,
+                             stream_data<StreamT, SyncBufT, UnsyncBufT>& data,
+                             ios_base::openmode mode) {
+  std::destroy_at(&data.sync_buffer);
+  std::construct_at(&data.unsync_buffer);
+  data.unsync_buffer.__adopt_file(stdstream, mode);
+  stream.value.rdbuf(&data.unsync_buffer);
 }
 
 #define CHAR_MANGLING_char "D"
@@ -62,29 +86,39 @@ void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT,
 #endif
 
 #ifdef _LIBCPP_ABI_MICROSOFT
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__(                                \
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12 at A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
 #endif
 
 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
 
-STREAM(basic_istream, __stdinbuf, char, cin);
-STREAM(basic_ostream, __stdoutbuf, char, cout);
-STREAM(basic_ostream, __stdoutbuf, char, cerr);
-STREAM(basic_ostream, __stdoutbuf, char, clog);
+#if _LIBCPP_HAS_FILESYSTEM
+template <class CharT>
+using unsync_buffer = basic_filebuf<CharT>;
+#else
+// If we don't have access to files we can't treat the standard streams as files either - just save a dummy object in
+// that case.
+template <class>
+using unsync_buffer = char;
+#endif
+
+STREAM(basic_istream, __stdinbuf, unsync_buffer, char, cin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, clog);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+STREAM(basic_istream, __stdinbuf, unsync_buffer, wchar_t, wcin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wclog);
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
@@ -155,5 +189,37 @@ ios_base::Init::Init() {
 
 ios_base::Init::~Init() {}
 
+bool ios_base::sync_with_stdio(bool sync) {
+  static bool previous_state = true;
+  bool r                     = previous_state;
+
+#if _LIBCPP_HAS_FILESYSTEM
+  if (sync != previous_state) {
+    if (sync) {
+      switch_to_sync_stream(stdin, cin, cin_data);
+      switch_to_sync_stream(stdout, cout, cout_data);
+      switch_to_sync_stream(stderr, clog, clog_data);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_sync_stream(stdin, wcin, wcin_data);
+      switch_to_sync_stream(stdout, wcout, wcout_data);
+      switch_to_sync_stream(stderr, wclog, wclog_data);
+#  endif
+    } else {
+      switch_to_unsync_stream(stdin, cin, cin_data, ios::in);
+      switch_to_unsync_stream(stdout, cout, cout_data, ios::out);
+      switch_to_unsync_stream(stderr, clog, clog_data, ios::out);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_unsync_stream(stdin, wcin, wcin_data, ios::in);
+      switch_to_unsync_stream(stdout, wcout, wcout_data, ios::out);
+      switch_to_unsync_stream(stderr, wclog, wclog_data, ios::out);
+#  endif
+    }
+  }
+#endif // _LIBCPP_HAS_FILESYSTEM
+
+  previous_state = sync;
+  return r;
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..052356449c5ab
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::cin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..ab30dee5f695d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::clog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3a94964f09223
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::cout << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..db6c5ba0e8660
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// istream wcin;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::wcin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..c0a0f299451fb
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wclog;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wclog << L"1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..a2cdc070620b1
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wcout;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wcout << L"1234";
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Fri Jul 17 00:49:26 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 17 Jul 2026 00:49:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Define behaviour for calling
 target() and target_type() on -fno-rtti std::functions (PR #209471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209471@github.com>
Message-ID: <6a59de86.d2cb4adc.ccd80.f38b@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/209471

>From 7ef75e77d97644a7e259a872f8a79846fd42da9b Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 14 Jul 2026 15:05:59 +0200
Subject: [PATCH] [libc++] Define behaviour for calling target() and
 target_type() on -fno-rtti std::functions

---
 libcxx/include/__functional/function.h        | 12 +++----
 .../func.wrap.func/rtti_mixing.sh.cpp         | 35 +++++++++++++++++++
 2 files changed, 41 insertions(+), 6 deletions(-)
 create mode 100644 libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp

diff --git a/libcxx/include/__functional/function.h b/libcxx/include/__functional/function.h
index 90072e9528484..8160759724d96 100644
--- a/libcxx/include/__functional/function.h
+++ b/libcxx/include/__functional/function.h
@@ -145,10 +145,8 @@ class __base<_Rp(_ArgTypes...)> {
   virtual void destroy() _NOEXCEPT            = 0;
   virtual void destroy_deallocate() _NOEXCEPT = 0;
   virtual _Rp operator()(_ArgTypes&&...)      = 0;
-#  if _LIBCPP_HAS_RTTI
-  virtual const void* target(const type_info&) const _NOEXCEPT = 0;
-  virtual const std::type_info& target_type() const _NOEXCEPT  = 0;
-#  endif // _LIBCPP_HAS_RTTI
+  virtual const void* target(const type_info&) const _NOEXCEPT { return nullptr; }
+  virtual const std::type_info* target_type() const _NOEXCEPT { return nullptr; }
 };
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 
@@ -182,7 +180,7 @@ class __func<_Fp, _Rp(_ArgTypes...)> : public __base<_Rp(_ArgTypes...)> {
       return std::addressof(__func_);
     return nullptr;
   }
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL const std::type_info& target_type() const _NOEXCEPT override { return typeid(_Fp); }
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL const std::type_info* target_type() const _NOEXCEPT override { return &typeid(_Fp); }
 #  endif // _LIBCPP_HAS_RTTI
 };
 
@@ -316,7 +314,9 @@ class __value_func<_Rp(_ArgTypes...)> {
   _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT {
     if (__f_ == nullptr)
       return typeid(void);
-    return __f_->target_type();
+    if (auto __ptr = __f_->target_type())
+      return *__ptr;
+    return typeid(void);
   }
 
   template <typename _Tp>
diff --git a/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp b/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp
new file mode 100644
index 0000000000000..a6bb61ef90537
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/utilities/function.objects/func.wrap/func.wrap.func/rtti_mixing.sh.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Ensure that passing std::functions across -fno-rtti/-frtti boundaries works.
+
+#include <cassert>
+#include <functional>
+#include <typeinfo>
+
+// RUN: %{cxx} %s %{flags} %{compile_flags} -c -frtti -o %t.tu1.o
+// RUN: %{cxx} %s %{flags} %{compile_flags} -c -DNO_RTTI -fno-rtti -o %t.tu2.o
+// RUN: %{cxx} %t.tu1.o %t.tu2.o %{flags} %{link_flags} -o %t.exe
+// RUN: %{exec} %t.exe
+
+std::function<void()> get_func();
+
+#ifdef NO_RTTI
+std::function<void()> get_func() {
+  return get_func;
+}
+#else
+int main(int, char**) {
+  assert(!get_func().target<int>());
+  assert(get_func().target_type() == typeid(void));
+
+  return 0;
+}
+#endif


From libcxx-commits at lists.llvm.org  Fri Jul 17 00:51:09 2026
From: libcxx-commits at lists.llvm.org (Takuto Ikuta via libcxx-commits)
Date: Fri, 17 Jul 2026 00:51:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix hypotf linker error with
 Clang modules on Windows UCRT (PR #209403)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209403@github.com>
Message-ID: <6a59deed.92ea0e2a.4a9a5.9ceb@mx.google.com>


atetubou wrote:

If `__builtin_hypotf` works without `math.h`, that is also good for us. But I need more help from people famliar to llvm/clang about what kind of approach we can take to make that work. Maybe we need to wait for https://github.com/llvm/llvm-project/pull/207135#discussion_r3586028267 ?
Or better to use lib/Analysis/TargetLibraryInfo.cpp as suggeted in https://github.com/llvm/llvm-project/pull/207135#discussion_r3580229128 ?

https://github.com/llvm/llvm-project/pull/209403

From libcxx-commits at lists.llvm.org  Fri Jul 17 00:51:17 2026
From: libcxx-commits at lists.llvm.org (Demetrius Kanios via libcxx-commits)
Date: Fri, 17 Jul 2026 00:51:17 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxxabi] [libunwind] [llvm]
 [WebAssembly] Call EH personality function instead of
 `Unwind_CallPersonality` (PR #209282)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209282@github.com>
Message-ID: <6a59def5.92ea0e2a.4a9a5.a0d4@mx.google.com>


================
@@ -1114,6 +1112,20 @@ __gxx_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame,
 }
 #endif
 
+#  ifdef __WASM_EXCEPTIONS__
+extern "C" _LIBCXXABI_FUNC_VIS void __gxx_wasm_personality_v0(void* exception_ptr) {
----------------
QuantumSegfault wrote:

I ran `clang-format` on this section of code.

Is there something that doesn't seem right???

https://github.com/llvm/llvm-project/pull/209282

From libcxx-commits at lists.llvm.org  Fri Jul 17 01:01:48 2026
From: libcxx-commits at lists.llvm.org (Demetrius Kanios via libcxx-commits)
Date: Fri, 17 Jul 2026 01:01:48 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxxabi] [libunwind] [llvm]
 [WebAssembly] Call EH personality function instead of
 `Unwind_CallPersonality` (PR #209282)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209282@github.com>
Message-ID: <6a59e16c.160324b0.1f653e.4d40@mx.google.com>


https://github.com/QuantumSegfault updated https://github.com/llvm/llvm-project/pull/209282

>From 4482524e82852ed240e0d7e9b0336d79e04e8ac9 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 13 Jul 2026 12:55:10 -0700
Subject: [PATCH 1/2] Call declared personality function

---
 clang/lib/CodeGen/CGException.cpp             | 10 ++-
 libcxxabi/src/cxa_personality.cpp             | 18 +++++-
 libunwind/include/unwind.h                    |  4 ++
 libunwind/include/unwind_wasm.h               | 27 ++++++++
 libunwind/src/Unwind-wasm.c                   | 39 ------------
 llvm/include/llvm/IR/RuntimeLibcalls.td       |  4 --
 llvm/lib/CodeGen/WasmEHPrepare.cpp            | 63 ++++++++-----------
 .../WebAssembly/cfg-stackify-eh-legacy.ll     |  2 +-
 .../WebAssembly/cfg-stackify-eh-legacy.mir    |  2 +-
 .../CodeGen/WebAssembly/cfg-stackify-eh.ll    |  8 +--
 llvm/test/CodeGen/WebAssembly/eh-lsda.ll      |  2 +-
 .../CodeGen/WebAssembly/exception-legacy.ll   |  4 +-
 .../CodeGen/WebAssembly/exception-legacy.mir  |  2 +-
 llvm/test/CodeGen/WebAssembly/exception.ll    |  4 +-
 .../CodeGen/WebAssembly/function-info.mir     |  2 +-
 .../WebAssembly/lower-wasm-ehsjlj-phi.ll      |  2 +-
 .../CodeGen/WebAssembly/lower-wasm-ehsjlj.ll  |  2 +-
 .../WebAssembly/wasm-eh-em-sjlj-error.ll      |  2 +-
 .../wasm-eh-invalid-personality.ll            |  2 +-
 .../CodeGen/WebAssembly/wasm-eh-prepare.ll    |  8 +--
 .../wasm-eh-sjlj-setjmp-within-catch.ll       |  2 +-
 21 files changed, 102 insertions(+), 107 deletions(-)
 create mode 100644 libunwind/include/unwind_wasm.h

diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp
index 99dfaa80be429..4076ab5dc6a2f 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -265,8 +265,14 @@ const EHPersonality &EHPersonality::get(CodeGenFunction &CGF) {
 
 static llvm::FunctionCallee getPersonalityFn(CodeGenModule &CGM,
                                              const EHPersonality &Personality) {
-  return CGM.CreateRuntimeFunction(llvm::FunctionType::get(CGM.Int32Ty, true),
-                                   Personality.PersonalityFn,
+  llvm::FunctionType *FTy;
+
+  if (Personality.isWasmPersonality()) {
+    FTy = llvm::FunctionType::get(CGM.VoidTy, {CGM.VoidPtrTy}, false);
+  } else {
+    FTy = llvm::FunctionType::get(CGM.Int32Ty, true);
+  }
+  return CGM.CreateRuntimeFunction(FTy, Personality.PersonalityFn,
                                    llvm::AttributeList(), /*Local=*/true);
 }
 
diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index c5050e46c0e85..92767efa58a1a 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1011,9 +1011,7 @@ static inline void get_landing_pad(__cxa_catch_temp_type &dest,
 #endif
 }
 
-#ifdef __WASM_EXCEPTIONS__
-_Unwind_Reason_Code __gxx_personality_wasm0
-#elif defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
+#  if (defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)) || defined(__WASM_EXCEPTIONS__)
 static _Unwind_Reason_Code __gxx_personality_imp
 #else
 _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code
@@ -1114,6 +1112,20 @@ __gxx_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame,
 }
 #endif
 
+#  ifdef __WASM_EXCEPTIONS__
+extern "C" _LIBCXXABI_FUNC_VIS void __gxx_wasm_personality_v0(void* exception_ptr) {
+  struct _Unwind_Exception* exception_object = (struct _Unwind_Exception*)exception_ptr;
+
+  // Reset the selector.
+  __wasm_lpad_context.selector = 0;
+
+  // Call personality function. Wasm does not have two-phase unwinding, so we
+  // only do the search phase.
+  __gxx_personality_imp(1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
+                        (struct _Unwind_Context*)&__wasm_lpad_context);
+}
+#  endif
+
 #else
 
 extern "C" _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Exception*, _Unwind_Context*);
diff --git a/libunwind/include/unwind.h b/libunwind/include/unwind.h
index b1775d3a3decc..93a9d92f327d3 100644
--- a/libunwind/include/unwind.h
+++ b/libunwind/include/unwind.h
@@ -61,6 +61,10 @@ typedef struct _Unwind_Context _Unwind_Context;   // opaque
 #include <unwind_itanium.h>
 #endif
 
+#if defined(__WASM_EXCEPTIONS__)
+#include <unwind_wasm.h>
+#endif
+
 typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
     (int version,
      _Unwind_Action actions,
diff --git a/libunwind/include/unwind_wasm.h b/libunwind/include/unwind_wasm.h
new file mode 100644
index 0000000000000..7bf3f30562bd8
--- /dev/null
+++ b/libunwind/include/unwind_wasm.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 __WASM_UNWIND_H__
+#define __WASM_UNWIND_H__
+
+#include <threads.h>
+
+struct _Unwind_LandingPadContext {
+  // Input information to personality function
+  uintptr_t lpad_index; // landing pad index
+  uintptr_t lsda;       // LSDA address
+
+  // Output information computed by personality function
+  uintptr_t selector; // selector value
+};
+
+// Communication channel between compiler-generated user code and personality
+// function
+extern thread_local struct _Unwind_LandingPadContext __wasm_lpad_context;
+
+#endif // __WASM_UNWIND_H__
diff --git a/libunwind/src/Unwind-wasm.c b/libunwind/src/Unwind-wasm.c
index 2e949d005b8f5..81793b3f36060 100644
--- a/libunwind/src/Unwind-wasm.c
+++ b/libunwind/src/Unwind-wasm.c
@@ -19,47 +19,8 @@
 #include "unwind.h"
 #include <threads.h>
 
-_Unwind_Reason_Code __gxx_personality_wasm0(int version, _Unwind_Action actions,
-                                            uint64_t exceptionClass,
-                                            _Unwind_Exception *unwind_exception,
-                                            _Unwind_Context *context);
-
-struct _Unwind_LandingPadContext {
-  // Input information to personality function
-  uintptr_t lpad_index; // landing pad index
-  uintptr_t lsda;       // LSDA address
-
-  // Output information computed by personality function
-  uintptr_t selector; // selector value
-};
-
-// Communication channel between compiler-generated user code and personality
-// function
 thread_local struct _Unwind_LandingPadContext __wasm_lpad_context;
 
-/// Calls to this function are in landing pads in compiler-generated user code.
-/// In other EH schemes, stack unwinding is done by libunwind library, which
-/// calls the personality function for each frame it lands. On the other hand,
-/// WebAssembly stack unwinding process is performed by a VM, and the
-/// personality function cannot be called from there. So the compiler inserts a
-/// call to this function in landing pads in the user code, which in turn calls
-/// the personality function.
-_Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
-  struct _Unwind_Exception *exception_object =
-      (struct _Unwind_Exception *)exception_ptr;
-  _LIBUNWIND_TRACE_API("_Unwind_CallPersonality(exception_object=%p)",
-                       (void *)exception_object);
-
-  // Reset the selector.
-  __wasm_lpad_context.selector = 0;
-
-  // Call personality function. Wasm does not have two-phase unwinding, so we
-  // only do the search phase.
-  return __gxx_personality_wasm0(
-      1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
-      (struct _Unwind_Context *)&__wasm_lpad_context);
-}
-
 /// Called by __cxa_throw.
 _LIBUNWIND_EXPORT _Unwind_Reason_Code
 _Unwind_RaiseException(_Unwind_Exception *exception_object) {
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.td b/llvm/include/llvm/IR/RuntimeLibcalls.td
index d5f38b9674cd7..68fe561bb606e 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.td
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.td
@@ -1681,9 +1681,6 @@ defset list<RuntimeLibcallImpl> SjLjExceptionHandlingLibcalls = {
   def _Unwind_SjLj_Unregister : RuntimeLibcallImpl<UNWIND_UNREGISTER>;
 }
 
-// Only used on wasm?
-def _Unwind_CallPersonality : RuntimeLibcallImpl<UNWIND_CALL_PERSONALITY>;
-
 // Used on OpenBSD
 def __stack_smash_handler : RuntimeLibcallImpl<STACK_SMASH_HANDLER>;
 
@@ -3399,7 +3396,6 @@ def WasmSystemLibrary
       (add DefaultRuntimeLibcallImpls, Int128RTLibcalls,
            CompilerRTOnlyInt64Libcalls, CompilerRTOnlyInt128Libcalls,
            exp10f, exp10,
-           _Unwind_CallPersonality,
            emscripten_return_address,
            LibcallImpls<(add __small_printf,
                              __small_sprintf,
diff --git a/llvm/lib/CodeGen/WasmEHPrepare.cpp b/llvm/lib/CodeGen/WasmEHPrepare.cpp
index b83bcf67716f9..ab562edffeec9 100644
--- a/llvm/lib/CodeGen/WasmEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WasmEHPrepare.cpp
@@ -28,7 +28,7 @@
 //   wasm.landingpad.index(index);
 //   __wasm_lpad_context.lpad_index = index;
 //   __wasm_lpad_context.lsda = wasm.lsda();
-//   _Unwind_CallPersonality(exn);
+//   personality_fn(exn);
 //   selector = __wasm_lpad_context.selector;
 //   ...
 //
@@ -38,18 +38,13 @@
 // exception is thrown. After the stack is unwound, the control flow is
 // transfered to WebAssembly 'catch' instruction.
 //
-// Unwinding the stack is not done by libunwind but the VM, so the personality
-// function in libcxxabi cannot be called from libunwind during the unwinding
-// process. So after a catch instruction, we insert a call to a wrapper function
-// in libunwind that in turn calls the real personality function.
-//
 // In Itanium EH, if the personality function decides there is no matching catch
 // clause in a call frame and no cleanup action to perform, the unwinder doesn't
 // stop there and continues unwinding. But in Wasm EH, the unwinder stops at
 // every call frame with a catch intruction, after which the personality
 // function is called from the compiler-generated user code here.
 //
-// In libunwind, we have this struct that serves as a communincation channel
+// In libunwind, we have this struct that serves as a communication channel
 // between the compiler-generated user code and the personality function in
 // libcxxabi.
 //
@@ -60,20 +55,8 @@
 // };
 // struct _Unwind_LandingPadContext __wasm_lpad_context = ...;
 //
-// And this wrapper in libunwind calls the personality function.
-//
-// _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
-//   struct _Unwind_Exception *exception_obj =
-//       (struct _Unwind_Exception *)exception_ptr;
-//   _Unwind_Reason_Code ret = __gxx_personality_v0(
-//       1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj,
-//       (struct _Unwind_Context *)__wasm_lpad_context);
-//   return ret;
-// }
-//
 // We pass a landing pad index, and the address of LSDA for the current function
-// to the wrapper function _Unwind_CallPersonality in libunwind, and we retrieve
-// the selector after it returns.
+// to the personality function, and we retrieve the selector after it returns.
 //
 //===----------------------------------------------------------------------===//
 
@@ -111,8 +94,7 @@ class WasmEHPrepareImpl {
   Function *GetExnF = nullptr;      // wasm.get.exception() intrinsic
   Function *CatchF = nullptr;       // wasm.catch() intrinsic
   Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
-  FunctionCallee CallPersonalityF =
-      nullptr; // _Unwind_CallPersonality() wrapper
+  FunctionCallee PersonalityF = nullptr;
 
   bool prepareThrows(Function &F);
   bool prepareEHPads(Function &F);
@@ -235,11 +217,14 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
   if (CatchPads.empty() && CleanupPads.empty())
     return false;
 
-  if (!F.hasPersonalityFn() ||
-      !isScopedEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) {
+  if (!F.hasPersonalityFn())
+    return false;
+
+  auto Personality = classifyEHPersonality(F.getPersonalityFn());
+
+  if (!isScopedEHPersonality(Personality)) {
     report_fatal_error("Function '" + F.getName() +
-                       "' does not have a correct Wasm personality function "
-                       "'__gxx_wasm_personality_v0'");
+                       "' does not have a supported Wasm personality function");
   }
   assert(F.hasPersonalityFn() && "Personality function not found");
 
@@ -274,15 +259,12 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
   // instruction selection.
   CatchF = Intrinsic::getOrInsertDeclaration(&M, Intrinsic::wasm_catch);
 
-  // FIXME: Verify this is really supported for current module.
-  StringRef UnwindCallPersonalityName =
-      RTLIB::RuntimeLibcallsInfo::getLibcallImplName(
-          RTLIB::impl__Unwind_CallPersonality);
+  auto *PersPrototype =
+      FunctionType::get(IRB.getInt32Ty(), {IRB.getPtrTy()}, false);
+  PersonalityF =
+      M.getOrInsertFunction(getEHPersonalityName(Personality), PersPrototype);
 
-  // _Unwind_CallPersonality() wrapper function, which calls the personality
-  CallPersonalityF = M.getOrInsertFunction(UnwindCallPersonalityName,
-                                           IRB.getInt32Ty(), IRB.getPtrTy());
-  if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee()))
+  if (Function *F = dyn_cast<Function>(PersonalityF.getCallee()))
     F->setDoesNotThrow();
 
   unsigned Index = 0;
@@ -367,9 +349,16 @@ void WasmEHPrepareImpl::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
   // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
   IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
 
-  // Pseudocode: _Unwind_CallPersonality(exn);
-  CallInst *PersCI = IRB.CreateCall(CallPersonalityF, CatchCI,
-                                    OperandBundleDef("funclet", CPI));
+  // Pseudocode: personality_fn(exn);
+  CallInst *PersCI;
+
+  // Grab direct function when possible to use `call` instead of `call_indirect`
+  if (Function *F = dyn_cast<Function>(PersonalityF.getCallee()))
+    PersCI = IRB.CreateCall(F, CatchCI, OperandBundleDef("funclet", CPI));
+  else
+    PersCI =
+        IRB.CreateCall(PersonalityF, CatchCI, OperandBundleDef("funclet", CPI));
+
   PersCI->setDoesNotThrow();
 
   // Pseudocode: int selector = __wasm_lpad_context.selector;
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
index a35129effcdee..301f7d81a4274 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
@@ -1911,7 +1911,7 @@ declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
 ; Function Attrs: nounwind
 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
 
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir
index 324dd29f2295e..f0341ed420241 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir
@@ -3,7 +3,7 @@
 --- |
   target triple = "wasm32-unknown-unknown"
 
-  declare i32 @__gxx_wasm_personality_v0(...)
+  declare void @__gxx_wasm_personality_v0(ptr)
   declare void @foo()
   define void @rethrow_arg_test() personality ptr @__gxx_wasm_personality_v0 {
     ret void
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
index a1e7616711ab9..a6611899806ea 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
@@ -35,7 +35,7 @@ target triple = "wasm32-unknown-unknown"
 ; CHECK:   local.set  2
 ; CHECK:   local.set  1
 ; CHECK:   local.get  0
-; CHECK:   call  _Unwind_CallPersonality
+; CHECK:   call  __gxx_wasm_personality_v0
 ; CHECK:   block
 ; CHECK:     br_if     0                                 # 0: down to label[[L2:[0-9]+]]
 ; CHECK:     call  __cxa_begin_catch
@@ -111,7 +111,7 @@ try.cont:                                         ; preds = %catch, %catch2, %en
 ; CHECK:         br        2                                      # 2: down to label[[L1:[0-9]+]]
 ; CHECK:       end_try_table
 ; CHECK:     end_block                                            # label[[L0]]:
-; CHECK:     call  _Unwind_CallPersonality
+; CHECK:     call  __gxx_wasm_personality_v0
 ; CHECK:     block
 ; CHECK:       block
 ; CHECK:         br_if     0                                      # 0: down to label[[L2:[0-9]+]]
@@ -124,7 +124,7 @@ try.cont:                                         ; preds = %catch, %catch2, %en
 ; CHECK:                 br        5                              # 5: down to label[[L5:[0-9]+]]
 ; CHECK:               end_try_table
 ; CHECK:             end_block                                    # label[[L4]]:
-; CHECK:             call  _Unwind_CallPersonality
+; CHECK:             call  __gxx_wasm_personality_v0
 ; CHECK:             block
 ; CHECK:               block
 ; CHECK:                 br_if     0                              # 0: down to label[[L6:[0-9]+]]
@@ -1644,7 +1644,7 @@ declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
 ; Function Attrs: nounwind
 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
 
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
index d5b28b279d419..0c0fa9f840601 100644
--- a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
+++ b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
@@ -251,6 +251,6 @@ declare i32 @llvm.wasm.get.ehselector(token) #0
 declare void @__cxa_rethrow()
 declare ptr @__cxa_begin_catch(ptr)
 declare void @__cxa_end_catch()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 
 attributes #0 = { nounwind }
diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
index 573f69df8b859..217da5c39e27f 100644
--- a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
@@ -35,7 +35,7 @@ define void @throw(ptr %p) {
 ; CHECK:     catch     $[[EXN:[0-9]+]]=, __cpp_exception
 ; CHECK:       global.set  __stack_pointer
 ; CHECK:       i32.store __wasm_lpad_context
-; CHECK:       call       $drop=, _Unwind_CallPersonality, $[[EXN]]
+; CHECK:       call       __gxx_wasm_personality_v0, $[[EXN]]
 ; CHECK:       block
 ; CHECK:         br_if     0
 ; CHECK:         call      $drop=, __cxa_begin_catch
@@ -547,7 +547,7 @@ try.cont:                                         ; preds = %entry
 declare void @foo()
 declare void @bar(ptr)
 declare void @take_i32(i32)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: noreturn
 declare void @llvm.wasm.throw(i32, ptr) #1
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.mir b/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
index 95929af4f4cea..7b8ba72ddfad9 100644
--- a/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
+++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
@@ -3,7 +3,7 @@
 --- |
   target triple = "wasm32-unknown-unknown"
 
-  declare i32 @__gxx_wasm_personality_v0(...)
+  declare void @__gxx_wasm_personality_v0(ptr)
   declare void @foo()
   define void @eh_label_test() personality ptr @__gxx_wasm_personality_v0 {
     ret void
diff --git a/llvm/test/CodeGen/WebAssembly/exception.ll b/llvm/test/CodeGen/WebAssembly/exception.ll
index f738216d087ec..a972fceba4715 100644
--- a/llvm/test/CodeGen/WebAssembly/exception.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception.ll
@@ -47,7 +47,7 @@ define void @throw(ptr %p) {
 ; CHECK:   local.get  0
 ; CHECK:   global.set  __stack_pointer
 ; CHECK:   i32.store  __wasm_lpad_context
-; CHECK:   call  _Unwind_CallPersonality
+; CHECK:   call  __gxx_wasm_personality_v0
 ; CHECK:   block
 ; CHECK:     br_if     0
 ; CHECK:     call  __cxa_begin_catch
@@ -648,7 +648,7 @@ try.cont:                                         ; preds = %entry
 declare void @foo()
 declare void @bar(ptr)
 declare void @take_i32(i32)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: noreturn
 declare void @llvm.wasm.throw(i32, ptr) #1
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/function-info.mir b/llvm/test/CodeGen/WebAssembly/function-info.mir
index b81cdffde819f..be87bad9ec72c 100644
--- a/llvm/test/CodeGen/WebAssembly/function-info.mir
+++ b/llvm/test/CodeGen/WebAssembly/function-info.mir
@@ -3,7 +3,7 @@
 --- |
   target triple = "wasm32-unknown-unknown"
 
-  declare i32 @__gxx_wasm_personality_v0(...)
+  declare void @__gxx_wasm_personality_v0(ptr)
   declare void @foo()
   define void @function_property_test() {
     ret void
diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
index 5e8ebcf247000..b4ecb2911c89b 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
@@ -110,7 +110,7 @@ ehcleanup:                                        ; preds = %bb3, %catch.start,
 }
 
 declare i32 @setjmp(ptr)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 declare void @foo()
 declare void @longjmpable()
 declare void @use_i32(i32)
diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
index d5ae37d7e98bc..37e98c6a302d1 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
@@ -299,7 +299,7 @@ declare ptr @_ZN4TempD2Ev(ptr %this) #2
 declare i32 @setjmp(ptr) #0
 ; Function Attrs: noreturn
 declare void @longjmp(ptr, i32) #1
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #2
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
index 35637a996e3ab..8547b68824ee6 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
@@ -30,7 +30,7 @@ try.cont:                                         ; preds = %entry, %catch.start
 }
 
 declare void @foo()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll
index 1a4e888a26ef5..36340c1444525 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll
@@ -6,7 +6,7 @@ target triple = "wasm32-unknown-unknown"
 ; not have a correct Wasm personality function.
 
 define void @test() personality ptr @invalid_personality {
-; CHECK: LLVM ERROR: Function 'test' does not have a correct Wasm personality function '__gxx_wasm_personality_v0'
+; CHECK: LLVM ERROR: Function 'test' does not have a supported Wasm personality function
 entry:
   invoke void @foo()
           to label %try.cont unwind label %catch.dispatch
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
index 164c138cb7578..daa3bb0207b55 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
@@ -46,7 +46,7 @@ catch.start:                                      ; preds = %catch.dispatch
 ; CHECK-NEXT:   store i32 0, ptr @__wasm_lpad_context
 ; CHECK-NEXT:   %[[LSDA:.*]] = call ptr @llvm.wasm.lsda()
 ; CHECK-NEXT:   store ptr %[[LSDA]], ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 1)
-; CHECK-NEXT:   call i32 @_Unwind_CallPersonality(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
+; CHECK-NEXT:   call void @__gxx_wasm_personality_v0(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
 ; CHECK-NEXT:   %[[SELECTOR:.*]] = load i32, ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 2)
 ; CHECK:   icmp eq i32 %[[SELECTOR]]
 
@@ -103,7 +103,7 @@ catch.start:                                      ; preds = %catch.dispatch
 ; CHECK-NOT:   call void @llvm.wasm.landingpad.index
 ; CHECK-NOT:   store {{.*}} @__wasm_lpad_context
 ; CHECK-NOT:   call ptr @llvm.wasm.lsda()
-; CHECK-NOT:   call i32 @_Unwind_CallPersonality
+; CHECK-NOT:   call void @__gxx_wasm_personality_v0
 ; CHECK-NOT:   load {{.*}} @__wasm_lpad_context
 
 try.cont:                                         ; preds = %entry, %catch.start
@@ -257,7 +257,7 @@ merge:                                            ; preds = %bb.true.0, %bb.fals
 declare void @foo()
 declare void @bar(i32)
 declare ptr @_ZN4TempD2Ev(ptr returned)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
@@ -277,4 +277,4 @@ attributes #1 = { noreturn }
 
 ; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32 immarg)
 ; CHECK-DAG: declare ptr @llvm.wasm.lsda()
-; CHECK-DAG: declare i32 @_Unwind_CallPersonality(ptr)
+; CHECK-DAG: declare void @__gxx_wasm_personality_v0(ptr)
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
index ed9b62941a9c7..6cca5af633751 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
@@ -49,7 +49,7 @@ ehcleanup:                                        ; preds = %catch
 }
 
 declare void @foo()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind

>From 3774cddbaa2cab7295e99335dc58bd62c2420ad7 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Fri, 17 Jul 2026 01:01:32 -0700
Subject: [PATCH 2/2] Make personality return `i32` again

---
 clang/lib/CodeGen/CGException.cpp                        | 2 +-
 clang/test/CodeGen/WebAssembly/wasm-eh.ll                | 3 +--
 libcxxabi/src/cxa_personality.cpp                        | 8 ++++----
 llvm/lib/CodeGen/WasmEHPrepare.cpp                       | 9 +++++++--
 llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll  | 2 +-
 llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll         | 2 +-
 llvm/test/CodeGen/WebAssembly/eh-lsda.ll                 | 2 +-
 llvm/test/CodeGen/WebAssembly/exception-legacy.ll        | 4 ++--
 llvm/test/CodeGen/WebAssembly/exception.ll               | 2 +-
 llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll   | 2 +-
 llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll       | 2 +-
 llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll   | 2 +-
 llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll         | 8 ++++----
 .../WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll      | 2 +-
 llvm/test/Transforms/ExpandVariadics/invoke.ll           | 4 +---
 llvm/test/Transforms/InstCombine/catchswitch-phi.ll      | 2 +-
 .../Transforms/PGOProfile/memop_profile_funclet_wasm.ll  | 2 +-
 llvm/test/Transforms/SROA/phi-catchswitch.ll             | 2 +-
 llvm/test/Transforms/SimpleLoopUnswitch/catchswitch.ll   | 3 +--
 llvm/test/Transforms/SimplifyCFG/cleanup-phis.ll         | 2 +-
 20 files changed, 33 insertions(+), 32 deletions(-)

diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp
index 4076ab5dc6a2f..b0fb3b4d85d15 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -268,7 +268,7 @@ static llvm::FunctionCallee getPersonalityFn(CodeGenModule &CGM,
   llvm::FunctionType *FTy;
 
   if (Personality.isWasmPersonality()) {
-    FTy = llvm::FunctionType::get(CGM.VoidTy, {CGM.VoidPtrTy}, false);
+    FTy = llvm::FunctionType::get(CGM.Int32Ty, {CGM.VoidPtrTy}, false);
   } else {
     FTy = llvm::FunctionType::get(CGM.Int32Ty, true);
   }
diff --git a/clang/test/CodeGen/WebAssembly/wasm-eh.ll b/clang/test/CodeGen/WebAssembly/wasm-eh.ll
index 9d7e70cab9f99..db4fc34d54777 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-eh.ll
+++ b/clang/test/CodeGen/WebAssembly/wasm-eh.ll
@@ -30,9 +30,8 @@ try.cont:                                         ; preds = %entry, %catch.start
 }
 
 declare void @foo()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 declare ptr @llvm.wasm.get.exception(token)
 declare i32 @llvm.wasm.get.ehselector(token)
 declare ptr @__cxa_begin_catch(ptr)
 declare void @__cxa_end_catch()
-
diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index 92767efa58a1a..0eb98a39f2658 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1112,8 +1112,8 @@ __gxx_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame,
 }
 #endif
 
-#  ifdef __WASM_EXCEPTIONS__
-extern "C" _LIBCXXABI_FUNC_VIS void __gxx_wasm_personality_v0(void* exception_ptr) {
+#ifdef __WASM_EXCEPTIONS__
+extern "C" _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code __gxx_wasm_personality_v0(void* exception_ptr) {
   struct _Unwind_Exception* exception_object = (struct _Unwind_Exception*)exception_ptr;
 
   // Reset the selector.
@@ -1121,10 +1121,10 @@ extern "C" _LIBCXXABI_FUNC_VIS void __gxx_wasm_personality_v0(void* exception_pt
 
   // Call personality function. Wasm does not have two-phase unwinding, so we
   // only do the search phase.
-  __gxx_personality_imp(1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
+  return __gxx_personality_imp(1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
                         (struct _Unwind_Context*)&__wasm_lpad_context);
 }
-#  endif
+#endif
 
 #else
 
diff --git a/llvm/lib/CodeGen/WasmEHPrepare.cpp b/llvm/lib/CodeGen/WasmEHPrepare.cpp
index ab562edffeec9..0b4f5da24bd2f 100644
--- a/llvm/lib/CodeGen/WasmEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WasmEHPrepare.cpp
@@ -38,6 +38,11 @@
 // exception is thrown. After the stack is unwound, the control flow is
 // transfered to WebAssembly 'catch' instruction.
 //
+// Unwinding the stack is not done by libunwind but the VM, so the personality
+// function (e.g. in libcxxabi) cannot be called from libunwind during the
+// unwinding process. So after a catch instruction, we insert a direct call to
+// the personality instead.
+//
 // In Itanium EH, if the personality function decides there is no matching catch
 // clause in a call frame and no cleanup action to perform, the unwinder doesn't
 // stop there and continues unwinding. But in Wasm EH, the unwinder stops at
@@ -349,11 +354,11 @@ void WasmEHPrepareImpl::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
   // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
   IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
 
-  // Pseudocode: personality_fn(exn);
   CallInst *PersCI;
 
-  // Grab direct function when possible to use `call` instead of `call_indirect`
+  // Pseudocode: personality_fn(exn);
   if (Function *F = dyn_cast<Function>(PersonalityF.getCallee()))
+    // Grab direct function when possible to use `call` instead of `call_indirect`
     PersCI = IRB.CreateCall(F, CatchCI, OperandBundleDef("funclet", CPI));
   else
     PersCI =
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
index 301f7d81a4274..b3bf255b2391e 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
@@ -1911,7 +1911,7 @@ declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
 ; Function Attrs: nounwind
 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
 
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
index a6611899806ea..6400b833da982 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
@@ -1644,7 +1644,7 @@ declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
 ; Function Attrs: nounwind
 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
 
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
index 0c0fa9f840601..1f8fe2cdd8944 100644
--- a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
+++ b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
@@ -251,6 +251,6 @@ declare i32 @llvm.wasm.get.ehselector(token) #0
 declare void @__cxa_rethrow()
 declare ptr @__cxa_begin_catch(ptr)
 declare void @__cxa_end_catch()
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 
 attributes #0 = { nounwind }
diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
index 217da5c39e27f..2c5d918295249 100644
--- a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
@@ -35,7 +35,7 @@ define void @throw(ptr %p) {
 ; CHECK:     catch     $[[EXN:[0-9]+]]=, __cpp_exception
 ; CHECK:       global.set  __stack_pointer
 ; CHECK:       i32.store __wasm_lpad_context
-; CHECK:       call       __gxx_wasm_personality_v0, $[[EXN]]
+; CHECK:       call       $drop=, __gxx_wasm_personality_v0, $[[EXN]]
 ; CHECK:       block
 ; CHECK:         br_if     0
 ; CHECK:         call      $drop=, __cxa_begin_catch
@@ -547,7 +547,7 @@ try.cont:                                         ; preds = %entry
 declare void @foo()
 declare void @bar(ptr)
 declare void @take_i32(i32)
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: noreturn
 declare void @llvm.wasm.throw(i32, ptr) #1
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/exception.ll b/llvm/test/CodeGen/WebAssembly/exception.ll
index a972fceba4715..808b1926e0be5 100644
--- a/llvm/test/CodeGen/WebAssembly/exception.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception.ll
@@ -648,7 +648,7 @@ try.cont:                                         ; preds = %entry
 declare void @foo()
 declare void @bar(ptr)
 declare void @take_i32(i32)
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: noreturn
 declare void @llvm.wasm.throw(i32, ptr) #1
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
index b4ecb2911c89b..1ccfb5bd67962 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
@@ -110,7 +110,7 @@ ehcleanup:                                        ; preds = %bb3, %catch.start,
 }
 
 declare i32 @setjmp(ptr)
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 declare void @foo()
 declare void @longjmpable()
 declare void @use_i32(i32)
diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
index 37e98c6a302d1..9670af3cdd8d3 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
@@ -299,7 +299,7 @@ declare ptr @_ZN4TempD2Ev(ptr %this) #2
 declare i32 @setjmp(ptr) #0
 ; Function Attrs: noreturn
 declare void @longjmp(ptr, i32) #1
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #2
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
index 8547b68824ee6..9ac92f5f0667d 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
@@ -30,7 +30,7 @@ try.cont:                                         ; preds = %entry, %catch.start
 }
 
 declare void @foo()
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
index daa3bb0207b55..944e16fb9eef1 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
@@ -46,7 +46,7 @@ catch.start:                                      ; preds = %catch.dispatch
 ; CHECK-NEXT:   store i32 0, ptr @__wasm_lpad_context
 ; CHECK-NEXT:   %[[LSDA:.*]] = call ptr @llvm.wasm.lsda()
 ; CHECK-NEXT:   store ptr %[[LSDA]], ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 1)
-; CHECK-NEXT:   call void @__gxx_wasm_personality_v0(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
+; CHECK-NEXT:   call i32 @__gxx_wasm_personality_v0(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
 ; CHECK-NEXT:   %[[SELECTOR:.*]] = load i32, ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 2)
 ; CHECK:   icmp eq i32 %[[SELECTOR]]
 
@@ -103,7 +103,7 @@ catch.start:                                      ; preds = %catch.dispatch
 ; CHECK-NOT:   call void @llvm.wasm.landingpad.index
 ; CHECK-NOT:   store {{.*}} @__wasm_lpad_context
 ; CHECK-NOT:   call ptr @llvm.wasm.lsda()
-; CHECK-NOT:   call void @__gxx_wasm_personality_v0
+; CHECK-NOT:   call i32 @__gxx_wasm_personality_v0
 ; CHECK-NOT:   load {{.*}} @__wasm_lpad_context
 
 try.cont:                                         ; preds = %entry, %catch.start
@@ -257,7 +257,7 @@ merge:                                            ; preds = %bb.true.0, %bb.fals
 declare void @foo()
 declare void @bar(i32)
 declare ptr @_ZN4TempD2Ev(ptr returned)
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
@@ -277,4 +277,4 @@ attributes #1 = { noreturn }
 
 ; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32 immarg)
 ; CHECK-DAG: declare ptr @llvm.wasm.lsda()
-; CHECK-DAG: declare void @__gxx_wasm_personality_v0(ptr)
+; CHECK-DAG: declare i32 @__gxx_wasm_personality_v0(ptr)
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
index 6cca5af633751..e5f373c686d62 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
@@ -49,7 +49,7 @@ ehcleanup:                                        ; preds = %catch
 }
 
 declare void @foo()
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/Transforms/ExpandVariadics/invoke.ll b/llvm/test/Transforms/ExpandVariadics/invoke.ll
index dae2d7d7de536..ba6ab3af6b4cc 100644
--- a/llvm/test/Transforms/ExpandVariadics/invoke.ll
+++ b/llvm/test/Transforms/ExpandVariadics/invoke.ll
@@ -65,7 +65,7 @@ try.cont:                                         ; preds = %entry, %catch
 
 declare void @may_throw(...)
 
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 
 ; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn
 declare ptr @llvm.wasm.get.exception(token)
@@ -85,5 +85,3 @@ declare void @__cxa_end_catch()
 
 ; Function Attrs: noreturn
 declare void @llvm.wasm.rethrow()
-
-
diff --git a/llvm/test/Transforms/InstCombine/catchswitch-phi.ll b/llvm/test/Transforms/InstCombine/catchswitch-phi.ll
index 562dded86be29..eafee3b29ac7e 100644
--- a/llvm/test/Transforms/InstCombine/catchswitch-phi.ll
+++ b/llvm/test/Transforms/InstCombine/catchswitch-phi.ll
@@ -10,7 +10,7 @@ target triple = "wasm32-unknown-unknown"
 declare void @foo()
 declare void @bar(ptr)
 declare i32 @baz()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: noreturn
 declare void @llvm.wasm.rethrow() #0
 
diff --git a/llvm/test/Transforms/PGOProfile/memop_profile_funclet_wasm.ll b/llvm/test/Transforms/PGOProfile/memop_profile_funclet_wasm.ll
index f8dcb768c94ca..6f42828f9a3b9 100644
--- a/llvm/test/Transforms/PGOProfile/memop_profile_funclet_wasm.ll
+++ b/llvm/test/Transforms/PGOProfile/memop_profile_funclet_wasm.ll
@@ -30,7 +30,7 @@ try.cont:                                         ; preds = %catch.start, %entry
 }
 
 declare void @foo()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nocallback nofree nosync nounwind willreturn
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nocallback nofree nosync nounwind willreturn
diff --git a/llvm/test/Transforms/SROA/phi-catchswitch.ll b/llvm/test/Transforms/SROA/phi-catchswitch.ll
index 250f8a16e7477..81c0c40d8c584 100644
--- a/llvm/test/Transforms/SROA/phi-catchswitch.ll
+++ b/llvm/test/Transforms/SROA/phi-catchswitch.ll
@@ -6,7 +6,7 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
 target triple = "wasm32-unknown-unknown"
 
 %struct.foo = type { i32 }
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 declare void @foo()
 
 ; Tests if the SROA pass correctly bails out on rewriting PHIs in a catchswitch
diff --git a/llvm/test/Transforms/SimpleLoopUnswitch/catchswitch.ll b/llvm/test/Transforms/SimpleLoopUnswitch/catchswitch.ll
index 303de221b005c..7da67e5ca9abe 100644
--- a/llvm/test/Transforms/SimpleLoopUnswitch/catchswitch.ll
+++ b/llvm/test/Transforms/SimpleLoopUnswitch/catchswitch.ll
@@ -2,7 +2,7 @@
 
 ; CHECK: if.end{{.*}}:
 ; CHECK-NOT: if.end{{.*}}:
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 
 declare void @foo()
 
@@ -30,4 +30,3 @@ catch:                                            ; preds = %catch.dispatch
 cleanup:                                          ; preds = %invoke.cont
   br label %while.body
 }
-
diff --git a/llvm/test/Transforms/SimplifyCFG/cleanup-phis.ll b/llvm/test/Transforms/SimplifyCFG/cleanup-phis.ll
index d40c9502967fb..5562aabae7c5d 100644
--- a/llvm/test/Transforms/SimplifyCFG/cleanup-phis.ll
+++ b/llvm/test/Transforms/SimplifyCFG/cleanup-phis.ll
@@ -40,4 +40,4 @@ catch:                                            ; preds = %catchswitch
 
 declare void @foo()
 declare void @bar(i32, i32)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)


From libcxx-commits at lists.llvm.org  Fri Jul 17 01:04:11 2026
From: libcxx-commits at lists.llvm.org (Demetrius Kanios via libcxx-commits)
Date: Fri, 17 Jul 2026 01:04:11 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxxabi] [libunwind] [llvm]
 [WebAssembly] Call EH personality function instead of
 `Unwind_CallPersonality` (PR #209282)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209282@github.com>
Message-ID: <6a59e1fb.13be6df8.22eac4.b934@mx.google.com>


https://github.com/QuantumSegfault updated https://github.com/llvm/llvm-project/pull/209282

>From 4482524e82852ed240e0d7e9b0336d79e04e8ac9 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Mon, 13 Jul 2026 12:55:10 -0700
Subject: [PATCH 1/2] Call declared personality function

---
 clang/lib/CodeGen/CGException.cpp             | 10 ++-
 libcxxabi/src/cxa_personality.cpp             | 18 +++++-
 libunwind/include/unwind.h                    |  4 ++
 libunwind/include/unwind_wasm.h               | 27 ++++++++
 libunwind/src/Unwind-wasm.c                   | 39 ------------
 llvm/include/llvm/IR/RuntimeLibcalls.td       |  4 --
 llvm/lib/CodeGen/WasmEHPrepare.cpp            | 63 ++++++++-----------
 .../WebAssembly/cfg-stackify-eh-legacy.ll     |  2 +-
 .../WebAssembly/cfg-stackify-eh-legacy.mir    |  2 +-
 .../CodeGen/WebAssembly/cfg-stackify-eh.ll    |  8 +--
 llvm/test/CodeGen/WebAssembly/eh-lsda.ll      |  2 +-
 .../CodeGen/WebAssembly/exception-legacy.ll   |  4 +-
 .../CodeGen/WebAssembly/exception-legacy.mir  |  2 +-
 llvm/test/CodeGen/WebAssembly/exception.ll    |  4 +-
 .../CodeGen/WebAssembly/function-info.mir     |  2 +-
 .../WebAssembly/lower-wasm-ehsjlj-phi.ll      |  2 +-
 .../CodeGen/WebAssembly/lower-wasm-ehsjlj.ll  |  2 +-
 .../WebAssembly/wasm-eh-em-sjlj-error.ll      |  2 +-
 .../wasm-eh-invalid-personality.ll            |  2 +-
 .../CodeGen/WebAssembly/wasm-eh-prepare.ll    |  8 +--
 .../wasm-eh-sjlj-setjmp-within-catch.ll       |  2 +-
 21 files changed, 102 insertions(+), 107 deletions(-)
 create mode 100644 libunwind/include/unwind_wasm.h

diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp
index 99dfaa80be429..4076ab5dc6a2f 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -265,8 +265,14 @@ const EHPersonality &EHPersonality::get(CodeGenFunction &CGF) {
 
 static llvm::FunctionCallee getPersonalityFn(CodeGenModule &CGM,
                                              const EHPersonality &Personality) {
-  return CGM.CreateRuntimeFunction(llvm::FunctionType::get(CGM.Int32Ty, true),
-                                   Personality.PersonalityFn,
+  llvm::FunctionType *FTy;
+
+  if (Personality.isWasmPersonality()) {
+    FTy = llvm::FunctionType::get(CGM.VoidTy, {CGM.VoidPtrTy}, false);
+  } else {
+    FTy = llvm::FunctionType::get(CGM.Int32Ty, true);
+  }
+  return CGM.CreateRuntimeFunction(FTy, Personality.PersonalityFn,
                                    llvm::AttributeList(), /*Local=*/true);
 }
 
diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index c5050e46c0e85..92767efa58a1a 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1011,9 +1011,7 @@ static inline void get_landing_pad(__cxa_catch_temp_type &dest,
 #endif
 }
 
-#ifdef __WASM_EXCEPTIONS__
-_Unwind_Reason_Code __gxx_personality_wasm0
-#elif defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
+#  if (defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)) || defined(__WASM_EXCEPTIONS__)
 static _Unwind_Reason_Code __gxx_personality_imp
 #else
 _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code
@@ -1114,6 +1112,20 @@ __gxx_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame,
 }
 #endif
 
+#  ifdef __WASM_EXCEPTIONS__
+extern "C" _LIBCXXABI_FUNC_VIS void __gxx_wasm_personality_v0(void* exception_ptr) {
+  struct _Unwind_Exception* exception_object = (struct _Unwind_Exception*)exception_ptr;
+
+  // Reset the selector.
+  __wasm_lpad_context.selector = 0;
+
+  // Call personality function. Wasm does not have two-phase unwinding, so we
+  // only do the search phase.
+  __gxx_personality_imp(1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
+                        (struct _Unwind_Context*)&__wasm_lpad_context);
+}
+#  endif
+
 #else
 
 extern "C" _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Exception*, _Unwind_Context*);
diff --git a/libunwind/include/unwind.h b/libunwind/include/unwind.h
index b1775d3a3decc..93a9d92f327d3 100644
--- a/libunwind/include/unwind.h
+++ b/libunwind/include/unwind.h
@@ -61,6 +61,10 @@ typedef struct _Unwind_Context _Unwind_Context;   // opaque
 #include <unwind_itanium.h>
 #endif
 
+#if defined(__WASM_EXCEPTIONS__)
+#include <unwind_wasm.h>
+#endif
+
 typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
     (int version,
      _Unwind_Action actions,
diff --git a/libunwind/include/unwind_wasm.h b/libunwind/include/unwind_wasm.h
new file mode 100644
index 0000000000000..7bf3f30562bd8
--- /dev/null
+++ b/libunwind/include/unwind_wasm.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 __WASM_UNWIND_H__
+#define __WASM_UNWIND_H__
+
+#include <threads.h>
+
+struct _Unwind_LandingPadContext {
+  // Input information to personality function
+  uintptr_t lpad_index; // landing pad index
+  uintptr_t lsda;       // LSDA address
+
+  // Output information computed by personality function
+  uintptr_t selector; // selector value
+};
+
+// Communication channel between compiler-generated user code and personality
+// function
+extern thread_local struct _Unwind_LandingPadContext __wasm_lpad_context;
+
+#endif // __WASM_UNWIND_H__
diff --git a/libunwind/src/Unwind-wasm.c b/libunwind/src/Unwind-wasm.c
index 2e949d005b8f5..81793b3f36060 100644
--- a/libunwind/src/Unwind-wasm.c
+++ b/libunwind/src/Unwind-wasm.c
@@ -19,47 +19,8 @@
 #include "unwind.h"
 #include <threads.h>
 
-_Unwind_Reason_Code __gxx_personality_wasm0(int version, _Unwind_Action actions,
-                                            uint64_t exceptionClass,
-                                            _Unwind_Exception *unwind_exception,
-                                            _Unwind_Context *context);
-
-struct _Unwind_LandingPadContext {
-  // Input information to personality function
-  uintptr_t lpad_index; // landing pad index
-  uintptr_t lsda;       // LSDA address
-
-  // Output information computed by personality function
-  uintptr_t selector; // selector value
-};
-
-// Communication channel between compiler-generated user code and personality
-// function
 thread_local struct _Unwind_LandingPadContext __wasm_lpad_context;
 
-/// Calls to this function are in landing pads in compiler-generated user code.
-/// In other EH schemes, stack unwinding is done by libunwind library, which
-/// calls the personality function for each frame it lands. On the other hand,
-/// WebAssembly stack unwinding process is performed by a VM, and the
-/// personality function cannot be called from there. So the compiler inserts a
-/// call to this function in landing pads in the user code, which in turn calls
-/// the personality function.
-_Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
-  struct _Unwind_Exception *exception_object =
-      (struct _Unwind_Exception *)exception_ptr;
-  _LIBUNWIND_TRACE_API("_Unwind_CallPersonality(exception_object=%p)",
-                       (void *)exception_object);
-
-  // Reset the selector.
-  __wasm_lpad_context.selector = 0;
-
-  // Call personality function. Wasm does not have two-phase unwinding, so we
-  // only do the search phase.
-  return __gxx_personality_wasm0(
-      1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
-      (struct _Unwind_Context *)&__wasm_lpad_context);
-}
-
 /// Called by __cxa_throw.
 _LIBUNWIND_EXPORT _Unwind_Reason_Code
 _Unwind_RaiseException(_Unwind_Exception *exception_object) {
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.td b/llvm/include/llvm/IR/RuntimeLibcalls.td
index d5f38b9674cd7..68fe561bb606e 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.td
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.td
@@ -1681,9 +1681,6 @@ defset list<RuntimeLibcallImpl> SjLjExceptionHandlingLibcalls = {
   def _Unwind_SjLj_Unregister : RuntimeLibcallImpl<UNWIND_UNREGISTER>;
 }
 
-// Only used on wasm?
-def _Unwind_CallPersonality : RuntimeLibcallImpl<UNWIND_CALL_PERSONALITY>;
-
 // Used on OpenBSD
 def __stack_smash_handler : RuntimeLibcallImpl<STACK_SMASH_HANDLER>;
 
@@ -3399,7 +3396,6 @@ def WasmSystemLibrary
       (add DefaultRuntimeLibcallImpls, Int128RTLibcalls,
            CompilerRTOnlyInt64Libcalls, CompilerRTOnlyInt128Libcalls,
            exp10f, exp10,
-           _Unwind_CallPersonality,
            emscripten_return_address,
            LibcallImpls<(add __small_printf,
                              __small_sprintf,
diff --git a/llvm/lib/CodeGen/WasmEHPrepare.cpp b/llvm/lib/CodeGen/WasmEHPrepare.cpp
index b83bcf67716f9..ab562edffeec9 100644
--- a/llvm/lib/CodeGen/WasmEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WasmEHPrepare.cpp
@@ -28,7 +28,7 @@
 //   wasm.landingpad.index(index);
 //   __wasm_lpad_context.lpad_index = index;
 //   __wasm_lpad_context.lsda = wasm.lsda();
-//   _Unwind_CallPersonality(exn);
+//   personality_fn(exn);
 //   selector = __wasm_lpad_context.selector;
 //   ...
 //
@@ -38,18 +38,13 @@
 // exception is thrown. After the stack is unwound, the control flow is
 // transfered to WebAssembly 'catch' instruction.
 //
-// Unwinding the stack is not done by libunwind but the VM, so the personality
-// function in libcxxabi cannot be called from libunwind during the unwinding
-// process. So after a catch instruction, we insert a call to a wrapper function
-// in libunwind that in turn calls the real personality function.
-//
 // In Itanium EH, if the personality function decides there is no matching catch
 // clause in a call frame and no cleanup action to perform, the unwinder doesn't
 // stop there and continues unwinding. But in Wasm EH, the unwinder stops at
 // every call frame with a catch intruction, after which the personality
 // function is called from the compiler-generated user code here.
 //
-// In libunwind, we have this struct that serves as a communincation channel
+// In libunwind, we have this struct that serves as a communication channel
 // between the compiler-generated user code and the personality function in
 // libcxxabi.
 //
@@ -60,20 +55,8 @@
 // };
 // struct _Unwind_LandingPadContext __wasm_lpad_context = ...;
 //
-// And this wrapper in libunwind calls the personality function.
-//
-// _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
-//   struct _Unwind_Exception *exception_obj =
-//       (struct _Unwind_Exception *)exception_ptr;
-//   _Unwind_Reason_Code ret = __gxx_personality_v0(
-//       1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj,
-//       (struct _Unwind_Context *)__wasm_lpad_context);
-//   return ret;
-// }
-//
 // We pass a landing pad index, and the address of LSDA for the current function
-// to the wrapper function _Unwind_CallPersonality in libunwind, and we retrieve
-// the selector after it returns.
+// to the personality function, and we retrieve the selector after it returns.
 //
 //===----------------------------------------------------------------------===//
 
@@ -111,8 +94,7 @@ class WasmEHPrepareImpl {
   Function *GetExnF = nullptr;      // wasm.get.exception() intrinsic
   Function *CatchF = nullptr;       // wasm.catch() intrinsic
   Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
-  FunctionCallee CallPersonalityF =
-      nullptr; // _Unwind_CallPersonality() wrapper
+  FunctionCallee PersonalityF = nullptr;
 
   bool prepareThrows(Function &F);
   bool prepareEHPads(Function &F);
@@ -235,11 +217,14 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
   if (CatchPads.empty() && CleanupPads.empty())
     return false;
 
-  if (!F.hasPersonalityFn() ||
-      !isScopedEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) {
+  if (!F.hasPersonalityFn())
+    return false;
+
+  auto Personality = classifyEHPersonality(F.getPersonalityFn());
+
+  if (!isScopedEHPersonality(Personality)) {
     report_fatal_error("Function '" + F.getName() +
-                       "' does not have a correct Wasm personality function "
-                       "'__gxx_wasm_personality_v0'");
+                       "' does not have a supported Wasm personality function");
   }
   assert(F.hasPersonalityFn() && "Personality function not found");
 
@@ -274,15 +259,12 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
   // instruction selection.
   CatchF = Intrinsic::getOrInsertDeclaration(&M, Intrinsic::wasm_catch);
 
-  // FIXME: Verify this is really supported for current module.
-  StringRef UnwindCallPersonalityName =
-      RTLIB::RuntimeLibcallsInfo::getLibcallImplName(
-          RTLIB::impl__Unwind_CallPersonality);
+  auto *PersPrototype =
+      FunctionType::get(IRB.getInt32Ty(), {IRB.getPtrTy()}, false);
+  PersonalityF =
+      M.getOrInsertFunction(getEHPersonalityName(Personality), PersPrototype);
 
-  // _Unwind_CallPersonality() wrapper function, which calls the personality
-  CallPersonalityF = M.getOrInsertFunction(UnwindCallPersonalityName,
-                                           IRB.getInt32Ty(), IRB.getPtrTy());
-  if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee()))
+  if (Function *F = dyn_cast<Function>(PersonalityF.getCallee()))
     F->setDoesNotThrow();
 
   unsigned Index = 0;
@@ -367,9 +349,16 @@ void WasmEHPrepareImpl::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
   // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
   IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
 
-  // Pseudocode: _Unwind_CallPersonality(exn);
-  CallInst *PersCI = IRB.CreateCall(CallPersonalityF, CatchCI,
-                                    OperandBundleDef("funclet", CPI));
+  // Pseudocode: personality_fn(exn);
+  CallInst *PersCI;
+
+  // Grab direct function when possible to use `call` instead of `call_indirect`
+  if (Function *F = dyn_cast<Function>(PersonalityF.getCallee()))
+    PersCI = IRB.CreateCall(F, CatchCI, OperandBundleDef("funclet", CPI));
+  else
+    PersCI =
+        IRB.CreateCall(PersonalityF, CatchCI, OperandBundleDef("funclet", CPI));
+
   PersCI->setDoesNotThrow();
 
   // Pseudocode: int selector = __wasm_lpad_context.selector;
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
index a35129effcdee..301f7d81a4274 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
@@ -1911,7 +1911,7 @@ declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
 ; Function Attrs: nounwind
 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
 
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir
index 324dd29f2295e..f0341ed420241 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir
@@ -3,7 +3,7 @@
 --- |
   target triple = "wasm32-unknown-unknown"
 
-  declare i32 @__gxx_wasm_personality_v0(...)
+  declare void @__gxx_wasm_personality_v0(ptr)
   declare void @foo()
   define void @rethrow_arg_test() personality ptr @__gxx_wasm_personality_v0 {
     ret void
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
index a1e7616711ab9..a6611899806ea 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
@@ -35,7 +35,7 @@ target triple = "wasm32-unknown-unknown"
 ; CHECK:   local.set  2
 ; CHECK:   local.set  1
 ; CHECK:   local.get  0
-; CHECK:   call  _Unwind_CallPersonality
+; CHECK:   call  __gxx_wasm_personality_v0
 ; CHECK:   block
 ; CHECK:     br_if     0                                 # 0: down to label[[L2:[0-9]+]]
 ; CHECK:     call  __cxa_begin_catch
@@ -111,7 +111,7 @@ try.cont:                                         ; preds = %catch, %catch2, %en
 ; CHECK:         br        2                                      # 2: down to label[[L1:[0-9]+]]
 ; CHECK:       end_try_table
 ; CHECK:     end_block                                            # label[[L0]]:
-; CHECK:     call  _Unwind_CallPersonality
+; CHECK:     call  __gxx_wasm_personality_v0
 ; CHECK:     block
 ; CHECK:       block
 ; CHECK:         br_if     0                                      # 0: down to label[[L2:[0-9]+]]
@@ -124,7 +124,7 @@ try.cont:                                         ; preds = %catch, %catch2, %en
 ; CHECK:                 br        5                              # 5: down to label[[L5:[0-9]+]]
 ; CHECK:               end_try_table
 ; CHECK:             end_block                                    # label[[L4]]:
-; CHECK:             call  _Unwind_CallPersonality
+; CHECK:             call  __gxx_wasm_personality_v0
 ; CHECK:             block
 ; CHECK:               block
 ; CHECK:                 br_if     0                              # 0: down to label[[L6:[0-9]+]]
@@ -1644,7 +1644,7 @@ declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
 ; Function Attrs: nounwind
 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
 
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
index d5b28b279d419..0c0fa9f840601 100644
--- a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
+++ b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
@@ -251,6 +251,6 @@ declare i32 @llvm.wasm.get.ehselector(token) #0
 declare void @__cxa_rethrow()
 declare ptr @__cxa_begin_catch(ptr)
 declare void @__cxa_end_catch()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 
 attributes #0 = { nounwind }
diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
index 573f69df8b859..217da5c39e27f 100644
--- a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
@@ -35,7 +35,7 @@ define void @throw(ptr %p) {
 ; CHECK:     catch     $[[EXN:[0-9]+]]=, __cpp_exception
 ; CHECK:       global.set  __stack_pointer
 ; CHECK:       i32.store __wasm_lpad_context
-; CHECK:       call       $drop=, _Unwind_CallPersonality, $[[EXN]]
+; CHECK:       call       __gxx_wasm_personality_v0, $[[EXN]]
 ; CHECK:       block
 ; CHECK:         br_if     0
 ; CHECK:         call      $drop=, __cxa_begin_catch
@@ -547,7 +547,7 @@ try.cont:                                         ; preds = %entry
 declare void @foo()
 declare void @bar(ptr)
 declare void @take_i32(i32)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: noreturn
 declare void @llvm.wasm.throw(i32, ptr) #1
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.mir b/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
index 95929af4f4cea..7b8ba72ddfad9 100644
--- a/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
+++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
@@ -3,7 +3,7 @@
 --- |
   target triple = "wasm32-unknown-unknown"
 
-  declare i32 @__gxx_wasm_personality_v0(...)
+  declare void @__gxx_wasm_personality_v0(ptr)
   declare void @foo()
   define void @eh_label_test() personality ptr @__gxx_wasm_personality_v0 {
     ret void
diff --git a/llvm/test/CodeGen/WebAssembly/exception.ll b/llvm/test/CodeGen/WebAssembly/exception.ll
index f738216d087ec..a972fceba4715 100644
--- a/llvm/test/CodeGen/WebAssembly/exception.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception.ll
@@ -47,7 +47,7 @@ define void @throw(ptr %p) {
 ; CHECK:   local.get  0
 ; CHECK:   global.set  __stack_pointer
 ; CHECK:   i32.store  __wasm_lpad_context
-; CHECK:   call  _Unwind_CallPersonality
+; CHECK:   call  __gxx_wasm_personality_v0
 ; CHECK:   block
 ; CHECK:     br_if     0
 ; CHECK:     call  __cxa_begin_catch
@@ -648,7 +648,7 @@ try.cont:                                         ; preds = %entry
 declare void @foo()
 declare void @bar(ptr)
 declare void @take_i32(i32)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: noreturn
 declare void @llvm.wasm.throw(i32, ptr) #1
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/function-info.mir b/llvm/test/CodeGen/WebAssembly/function-info.mir
index b81cdffde819f..be87bad9ec72c 100644
--- a/llvm/test/CodeGen/WebAssembly/function-info.mir
+++ b/llvm/test/CodeGen/WebAssembly/function-info.mir
@@ -3,7 +3,7 @@
 --- |
   target triple = "wasm32-unknown-unknown"
 
-  declare i32 @__gxx_wasm_personality_v0(...)
+  declare void @__gxx_wasm_personality_v0(ptr)
   declare void @foo()
   define void @function_property_test() {
     ret void
diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
index 5e8ebcf247000..b4ecb2911c89b 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
@@ -110,7 +110,7 @@ ehcleanup:                                        ; preds = %bb3, %catch.start,
 }
 
 declare i32 @setjmp(ptr)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 declare void @foo()
 declare void @longjmpable()
 declare void @use_i32(i32)
diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
index d5ae37d7e98bc..37e98c6a302d1 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
@@ -299,7 +299,7 @@ declare ptr @_ZN4TempD2Ev(ptr %this) #2
 declare i32 @setjmp(ptr) #0
 ; Function Attrs: noreturn
 declare void @longjmp(ptr, i32) #1
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #2
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
index 35637a996e3ab..8547b68824ee6 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
@@ -30,7 +30,7 @@ try.cont:                                         ; preds = %entry, %catch.start
 }
 
 declare void @foo()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll
index 1a4e888a26ef5..36340c1444525 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll
@@ -6,7 +6,7 @@ target triple = "wasm32-unknown-unknown"
 ; not have a correct Wasm personality function.
 
 define void @test() personality ptr @invalid_personality {
-; CHECK: LLVM ERROR: Function 'test' does not have a correct Wasm personality function '__gxx_wasm_personality_v0'
+; CHECK: LLVM ERROR: Function 'test' does not have a supported Wasm personality function
 entry:
   invoke void @foo()
           to label %try.cont unwind label %catch.dispatch
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
index 164c138cb7578..daa3bb0207b55 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
@@ -46,7 +46,7 @@ catch.start:                                      ; preds = %catch.dispatch
 ; CHECK-NEXT:   store i32 0, ptr @__wasm_lpad_context
 ; CHECK-NEXT:   %[[LSDA:.*]] = call ptr @llvm.wasm.lsda()
 ; CHECK-NEXT:   store ptr %[[LSDA]], ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 1)
-; CHECK-NEXT:   call i32 @_Unwind_CallPersonality(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
+; CHECK-NEXT:   call void @__gxx_wasm_personality_v0(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
 ; CHECK-NEXT:   %[[SELECTOR:.*]] = load i32, ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 2)
 ; CHECK:   icmp eq i32 %[[SELECTOR]]
 
@@ -103,7 +103,7 @@ catch.start:                                      ; preds = %catch.dispatch
 ; CHECK-NOT:   call void @llvm.wasm.landingpad.index
 ; CHECK-NOT:   store {{.*}} @__wasm_lpad_context
 ; CHECK-NOT:   call ptr @llvm.wasm.lsda()
-; CHECK-NOT:   call i32 @_Unwind_CallPersonality
+; CHECK-NOT:   call void @__gxx_wasm_personality_v0
 ; CHECK-NOT:   load {{.*}} @__wasm_lpad_context
 
 try.cont:                                         ; preds = %entry, %catch.start
@@ -257,7 +257,7 @@ merge:                                            ; preds = %bb.true.0, %bb.fals
 declare void @foo()
 declare void @bar(i32)
 declare ptr @_ZN4TempD2Ev(ptr returned)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
@@ -277,4 +277,4 @@ attributes #1 = { noreturn }
 
 ; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32 immarg)
 ; CHECK-DAG: declare ptr @llvm.wasm.lsda()
-; CHECK-DAG: declare i32 @_Unwind_CallPersonality(ptr)
+; CHECK-DAG: declare void @__gxx_wasm_personality_v0(ptr)
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
index ed9b62941a9c7..6cca5af633751 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
@@ -49,7 +49,7 @@ ehcleanup:                                        ; preds = %catch
 }
 
 declare void @foo()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare void @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind

>From fcc1535bc1c2916efef77eda76a90c162b6f5de5 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Fri, 17 Jul 2026 01:01:32 -0700
Subject: [PATCH 2/2] Make personality return `i32` again

---
 clang/lib/CodeGen/CGException.cpp                        | 2 +-
 clang/test/CodeGen/WebAssembly/wasm-eh.ll                | 3 +--
 libcxxabi/src/cxa_personality.cpp                        | 8 ++++----
 llvm/lib/CodeGen/WasmEHPrepare.cpp                       | 9 +++++++--
 llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll  | 2 +-
 llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir | 2 +-
 llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll         | 2 +-
 llvm/test/CodeGen/WebAssembly/eh-lsda.ll                 | 2 +-
 llvm/test/CodeGen/WebAssembly/exception-legacy.ll        | 4 ++--
 llvm/test/CodeGen/WebAssembly/exception-legacy.mir       | 2 +-
 llvm/test/CodeGen/WebAssembly/exception.ll               | 2 +-
 llvm/test/CodeGen/WebAssembly/function-info.mir          | 2 +-
 llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll   | 2 +-
 llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll       | 2 +-
 llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll   | 2 +-
 llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll         | 8 ++++----
 .../WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll      | 2 +-
 llvm/test/Transforms/ExpandVariadics/invoke.ll           | 4 +---
 llvm/test/Transforms/InstCombine/catchswitch-phi.ll      | 2 +-
 .../Transforms/PGOProfile/memop_profile_funclet_wasm.ll  | 2 +-
 llvm/test/Transforms/SROA/phi-catchswitch.ll             | 2 +-
 llvm/test/Transforms/SimpleLoopUnswitch/catchswitch.ll   | 3 +--
 llvm/test/Transforms/SimplifyCFG/cleanup-phis.ll         | 2 +-
 23 files changed, 36 insertions(+), 35 deletions(-)

diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp
index 4076ab5dc6a2f..b0fb3b4d85d15 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -268,7 +268,7 @@ static llvm::FunctionCallee getPersonalityFn(CodeGenModule &CGM,
   llvm::FunctionType *FTy;
 
   if (Personality.isWasmPersonality()) {
-    FTy = llvm::FunctionType::get(CGM.VoidTy, {CGM.VoidPtrTy}, false);
+    FTy = llvm::FunctionType::get(CGM.Int32Ty, {CGM.VoidPtrTy}, false);
   } else {
     FTy = llvm::FunctionType::get(CGM.Int32Ty, true);
   }
diff --git a/clang/test/CodeGen/WebAssembly/wasm-eh.ll b/clang/test/CodeGen/WebAssembly/wasm-eh.ll
index 9d7e70cab9f99..db4fc34d54777 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-eh.ll
+++ b/clang/test/CodeGen/WebAssembly/wasm-eh.ll
@@ -30,9 +30,8 @@ try.cont:                                         ; preds = %entry, %catch.start
 }
 
 declare void @foo()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 declare ptr @llvm.wasm.get.exception(token)
 declare i32 @llvm.wasm.get.ehselector(token)
 declare ptr @__cxa_begin_catch(ptr)
 declare void @__cxa_end_catch()
-
diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index 92767efa58a1a..0eb98a39f2658 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1112,8 +1112,8 @@ __gxx_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame,
 }
 #endif
 
-#  ifdef __WASM_EXCEPTIONS__
-extern "C" _LIBCXXABI_FUNC_VIS void __gxx_wasm_personality_v0(void* exception_ptr) {
+#ifdef __WASM_EXCEPTIONS__
+extern "C" _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code __gxx_wasm_personality_v0(void* exception_ptr) {
   struct _Unwind_Exception* exception_object = (struct _Unwind_Exception*)exception_ptr;
 
   // Reset the selector.
@@ -1121,10 +1121,10 @@ extern "C" _LIBCXXABI_FUNC_VIS void __gxx_wasm_personality_v0(void* exception_pt
 
   // Call personality function. Wasm does not have two-phase unwinding, so we
   // only do the search phase.
-  __gxx_personality_imp(1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
+  return __gxx_personality_imp(1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
                         (struct _Unwind_Context*)&__wasm_lpad_context);
 }
-#  endif
+#endif
 
 #else
 
diff --git a/llvm/lib/CodeGen/WasmEHPrepare.cpp b/llvm/lib/CodeGen/WasmEHPrepare.cpp
index ab562edffeec9..0b4f5da24bd2f 100644
--- a/llvm/lib/CodeGen/WasmEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WasmEHPrepare.cpp
@@ -38,6 +38,11 @@
 // exception is thrown. After the stack is unwound, the control flow is
 // transfered to WebAssembly 'catch' instruction.
 //
+// Unwinding the stack is not done by libunwind but the VM, so the personality
+// function (e.g. in libcxxabi) cannot be called from libunwind during the
+// unwinding process. So after a catch instruction, we insert a direct call to
+// the personality instead.
+//
 // In Itanium EH, if the personality function decides there is no matching catch
 // clause in a call frame and no cleanup action to perform, the unwinder doesn't
 // stop there and continues unwinding. But in Wasm EH, the unwinder stops at
@@ -349,11 +354,11 @@ void WasmEHPrepareImpl::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
   // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
   IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
 
-  // Pseudocode: personality_fn(exn);
   CallInst *PersCI;
 
-  // Grab direct function when possible to use `call` instead of `call_indirect`
+  // Pseudocode: personality_fn(exn);
   if (Function *F = dyn_cast<Function>(PersonalityF.getCallee()))
+    // Grab direct function when possible to use `call` instead of `call_indirect`
     PersCI = IRB.CreateCall(F, CatchCI, OperandBundleDef("funclet", CPI));
   else
     PersCI =
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
index 301f7d81a4274..b3bf255b2391e 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
@@ -1911,7 +1911,7 @@ declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
 ; Function Attrs: nounwind
 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
 
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir
index f0341ed420241..ecf55e7bde413 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.mir
@@ -3,7 +3,7 @@
 --- |
   target triple = "wasm32-unknown-unknown"
 
-  declare void @__gxx_wasm_personality_v0(ptr)
+  declare i32 @__gxx_wasm_personality_v0(ptr)
   declare void @foo()
   define void @rethrow_arg_test() personality ptr @__gxx_wasm_personality_v0 {
     ret void
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
index a6611899806ea..6400b833da982 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
@@ -1644,7 +1644,7 @@ declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
 ; Function Attrs: nounwind
 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
 
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
index 0c0fa9f840601..1f8fe2cdd8944 100644
--- a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
+++ b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
@@ -251,6 +251,6 @@ declare i32 @llvm.wasm.get.ehselector(token) #0
 declare void @__cxa_rethrow()
 declare ptr @__cxa_begin_catch(ptr)
 declare void @__cxa_end_catch()
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 
 attributes #0 = { nounwind }
diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
index 217da5c39e27f..2c5d918295249 100644
--- a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
@@ -35,7 +35,7 @@ define void @throw(ptr %p) {
 ; CHECK:     catch     $[[EXN:[0-9]+]]=, __cpp_exception
 ; CHECK:       global.set  __stack_pointer
 ; CHECK:       i32.store __wasm_lpad_context
-; CHECK:       call       __gxx_wasm_personality_v0, $[[EXN]]
+; CHECK:       call       $drop=, __gxx_wasm_personality_v0, $[[EXN]]
 ; CHECK:       block
 ; CHECK:         br_if     0
 ; CHECK:         call      $drop=, __cxa_begin_catch
@@ -547,7 +547,7 @@ try.cont:                                         ; preds = %entry
 declare void @foo()
 declare void @bar(ptr)
 declare void @take_i32(i32)
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: noreturn
 declare void @llvm.wasm.throw(i32, ptr) #1
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.mir b/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
index 7b8ba72ddfad9..02731b62f18ac 100644
--- a/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
+++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.mir
@@ -3,7 +3,7 @@
 --- |
   target triple = "wasm32-unknown-unknown"
 
-  declare void @__gxx_wasm_personality_v0(ptr)
+  declare i32 @__gxx_wasm_personality_v0(ptr)
   declare void @foo()
   define void @eh_label_test() personality ptr @__gxx_wasm_personality_v0 {
     ret void
diff --git a/llvm/test/CodeGen/WebAssembly/exception.ll b/llvm/test/CodeGen/WebAssembly/exception.ll
index a972fceba4715..808b1926e0be5 100644
--- a/llvm/test/CodeGen/WebAssembly/exception.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception.ll
@@ -648,7 +648,7 @@ try.cont:                                         ; preds = %entry
 declare void @foo()
 declare void @bar(ptr)
 declare void @take_i32(i32)
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: noreturn
 declare void @llvm.wasm.throw(i32, ptr) #1
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/function-info.mir b/llvm/test/CodeGen/WebAssembly/function-info.mir
index be87bad9ec72c..dad0f397d4585 100644
--- a/llvm/test/CodeGen/WebAssembly/function-info.mir
+++ b/llvm/test/CodeGen/WebAssembly/function-info.mir
@@ -3,7 +3,7 @@
 --- |
   target triple = "wasm32-unknown-unknown"
 
-  declare void @__gxx_wasm_personality_v0(ptr)
+  declare i32 @__gxx_wasm_personality_v0(ptr)
   declare void @foo()
   define void @function_property_test() {
     ret void
diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
index b4ecb2911c89b..1ccfb5bd67962 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj-phi.ll
@@ -110,7 +110,7 @@ ehcleanup:                                        ; preds = %bb3, %catch.start,
 }
 
 declare i32 @setjmp(ptr)
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 declare void @foo()
 declare void @longjmpable()
 declare void @use_i32(i32)
diff --git a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
index 37e98c6a302d1..9670af3cdd8d3 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-wasm-ehsjlj.ll
@@ -299,7 +299,7 @@ declare ptr @_ZN4TempD2Ev(ptr %this) #2
 declare i32 @setjmp(ptr) #0
 ; Function Attrs: noreturn
 declare void @longjmp(ptr, i32) #1
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #2
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
index 8547b68824ee6..9ac92f5f0667d 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-em-sjlj-error.ll
@@ -30,7 +30,7 @@ try.cont:                                         ; preds = %entry, %catch.start
 }
 
 declare void @foo()
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
index daa3bb0207b55..944e16fb9eef1 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
@@ -46,7 +46,7 @@ catch.start:                                      ; preds = %catch.dispatch
 ; CHECK-NEXT:   store i32 0, ptr @__wasm_lpad_context
 ; CHECK-NEXT:   %[[LSDA:.*]] = call ptr @llvm.wasm.lsda()
 ; CHECK-NEXT:   store ptr %[[LSDA]], ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 1)
-; CHECK-NEXT:   call void @__gxx_wasm_personality_v0(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
+; CHECK-NEXT:   call i32 @__gxx_wasm_personality_v0(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
 ; CHECK-NEXT:   %[[SELECTOR:.*]] = load i32, ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 2)
 ; CHECK:   icmp eq i32 %[[SELECTOR]]
 
@@ -103,7 +103,7 @@ catch.start:                                      ; preds = %catch.dispatch
 ; CHECK-NOT:   call void @llvm.wasm.landingpad.index
 ; CHECK-NOT:   store {{.*}} @__wasm_lpad_context
 ; CHECK-NOT:   call ptr @llvm.wasm.lsda()
-; CHECK-NOT:   call void @__gxx_wasm_personality_v0
+; CHECK-NOT:   call i32 @__gxx_wasm_personality_v0
 ; CHECK-NOT:   load {{.*}} @__wasm_lpad_context
 
 try.cont:                                         ; preds = %entry, %catch.start
@@ -257,7 +257,7 @@ merge:                                            ; preds = %bb.true.0, %bb.fals
 declare void @foo()
 declare void @bar(i32)
 declare ptr @_ZN4TempD2Ev(ptr returned)
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
@@ -277,4 +277,4 @@ attributes #1 = { noreturn }
 
 ; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32 immarg)
 ; CHECK-DAG: declare ptr @llvm.wasm.lsda()
-; CHECK-DAG: declare void @__gxx_wasm_personality_v0(ptr)
+; CHECK-DAG: declare i32 @__gxx_wasm_personality_v0(ptr)
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
index 6cca5af633751..e5f373c686d62 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-sjlj-setjmp-within-catch.ll
@@ -49,7 +49,7 @@ ehcleanup:                                        ; preds = %catch
 }
 
 declare void @foo()
-declare void @__gxx_wasm_personality_v0(ptr)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nounwind
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nounwind
diff --git a/llvm/test/Transforms/ExpandVariadics/invoke.ll b/llvm/test/Transforms/ExpandVariadics/invoke.ll
index dae2d7d7de536..ba6ab3af6b4cc 100644
--- a/llvm/test/Transforms/ExpandVariadics/invoke.ll
+++ b/llvm/test/Transforms/ExpandVariadics/invoke.ll
@@ -65,7 +65,7 @@ try.cont:                                         ; preds = %entry, %catch
 
 declare void @may_throw(...)
 
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 
 ; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn
 declare ptr @llvm.wasm.get.exception(token)
@@ -85,5 +85,3 @@ declare void @__cxa_end_catch()
 
 ; Function Attrs: noreturn
 declare void @llvm.wasm.rethrow()
-
-
diff --git a/llvm/test/Transforms/InstCombine/catchswitch-phi.ll b/llvm/test/Transforms/InstCombine/catchswitch-phi.ll
index 562dded86be29..eafee3b29ac7e 100644
--- a/llvm/test/Transforms/InstCombine/catchswitch-phi.ll
+++ b/llvm/test/Transforms/InstCombine/catchswitch-phi.ll
@@ -10,7 +10,7 @@ target triple = "wasm32-unknown-unknown"
 declare void @foo()
 declare void @bar(ptr)
 declare i32 @baz()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: noreturn
 declare void @llvm.wasm.rethrow() #0
 
diff --git a/llvm/test/Transforms/PGOProfile/memop_profile_funclet_wasm.ll b/llvm/test/Transforms/PGOProfile/memop_profile_funclet_wasm.ll
index f8dcb768c94ca..6f42828f9a3b9 100644
--- a/llvm/test/Transforms/PGOProfile/memop_profile_funclet_wasm.ll
+++ b/llvm/test/Transforms/PGOProfile/memop_profile_funclet_wasm.ll
@@ -30,7 +30,7 @@ try.cont:                                         ; preds = %catch.start, %entry
 }
 
 declare void @foo()
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 ; Function Attrs: nocallback nofree nosync nounwind willreturn
 declare ptr @llvm.wasm.get.exception(token) #0
 ; Function Attrs: nocallback nofree nosync nounwind willreturn
diff --git a/llvm/test/Transforms/SROA/phi-catchswitch.ll b/llvm/test/Transforms/SROA/phi-catchswitch.ll
index 250f8a16e7477..81c0c40d8c584 100644
--- a/llvm/test/Transforms/SROA/phi-catchswitch.ll
+++ b/llvm/test/Transforms/SROA/phi-catchswitch.ll
@@ -6,7 +6,7 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
 target triple = "wasm32-unknown-unknown"
 
 %struct.foo = type { i32 }
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 declare void @foo()
 
 ; Tests if the SROA pass correctly bails out on rewriting PHIs in a catchswitch
diff --git a/llvm/test/Transforms/SimpleLoopUnswitch/catchswitch.ll b/llvm/test/Transforms/SimpleLoopUnswitch/catchswitch.ll
index 303de221b005c..7da67e5ca9abe 100644
--- a/llvm/test/Transforms/SimpleLoopUnswitch/catchswitch.ll
+++ b/llvm/test/Transforms/SimpleLoopUnswitch/catchswitch.ll
@@ -2,7 +2,7 @@
 
 ; CHECK: if.end{{.*}}:
 ; CHECK-NOT: if.end{{.*}}:
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)
 
 declare void @foo()
 
@@ -30,4 +30,3 @@ catch:                                            ; preds = %catch.dispatch
 cleanup:                                          ; preds = %invoke.cont
   br label %while.body
 }
-
diff --git a/llvm/test/Transforms/SimplifyCFG/cleanup-phis.ll b/llvm/test/Transforms/SimplifyCFG/cleanup-phis.ll
index d40c9502967fb..5562aabae7c5d 100644
--- a/llvm/test/Transforms/SimplifyCFG/cleanup-phis.ll
+++ b/llvm/test/Transforms/SimplifyCFG/cleanup-phis.ll
@@ -40,4 +40,4 @@ catch:                                            ; preds = %catchswitch
 
 declare void @foo()
 declare void @bar(i32, i32)
-declare i32 @__gxx_wasm_personality_v0(...)
+declare i32 @__gxx_wasm_personality_v0(ptr)


From libcxx-commits at lists.llvm.org  Fri Jul 17 01:09:39 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 17 Jul 2026 01:09:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some
	adjacent_view::begin() benchmarks (PR #210265)
Message-ID: <llvm.org/llvm/llvm-project/pull/210265@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/210265

Benchmarking a bunch of very similar values has rather little benefit, sinec they behave essentially the same.


>From abfac5e7e67e292e47434442080abd46b4d40344 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 17 Jul 2026 10:06:49 +0200
Subject: [PATCH] [libc++] Remove some adjacent_view::begin() benchmarks

---
 .../test/benchmarks/adjacent_view_begin.bench.cpp  | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/libcxx/test/benchmarks/adjacent_view_begin.bench.cpp b/libcxx/test/benchmarks/adjacent_view_begin.bench.cpp
index 7ca94c990f0cf..ba6ab601a1dac 100644
--- a/libcxx/test/benchmarks/adjacent_view_begin.bench.cpp
+++ b/libcxx/test/benchmarks/adjacent_view_begin.bench.cpp
@@ -28,13 +28,6 @@ void BM_adjacent_full(benchmark::State& state) {
 }
 
 BENCHMARK(BM_adjacent_full<2>)->Name("rng::adjacent_view::begin()/2 (full view)");
-BENCHMARK(BM_adjacent_full<3>)->Name("rng::adjacent_view::begin()/3 (full view)");
-BENCHMARK(BM_adjacent_full<4>)->Name("rng::adjacent_view::begin()/4 (full view)");
-BENCHMARK(BM_adjacent_full<5>)->Name("rng::adjacent_view::begin()/5 (full view)");
-BENCHMARK(BM_adjacent_full<6>)->Name("rng::adjacent_view::begin()/6 (full view)");
-BENCHMARK(BM_adjacent_full<7>)->Name("rng::adjacent_view::begin()/7 (full view)");
-BENCHMARK(BM_adjacent_full<8>)->Name("rng::adjacent_view::begin()/8 (full view)");
-BENCHMARK(BM_adjacent_full<9>)->Name("rng::adjacent_view::begin()/9 (full view)");
 BENCHMARK(BM_adjacent_full<10>)->Name("rng::adjacent_view::begin()/10 (full view)");
 BENCHMARK(BM_adjacent_full<100>)->Name("rng::adjacent_view::begin()/100 (full view)");
 BENCHMARK(BM_adjacent_full<1000>)->Name("rng::adjacent_view::begin()/1000 (full view)");
@@ -50,13 +43,6 @@ void BM_adjacent_empty(benchmark::State& state) {
 }
 
 BENCHMARK(BM_adjacent_empty<2>)->Name("rng::adjacent_view::begin()/2 (empty view)");
-BENCHMARK(BM_adjacent_empty<3>)->Name("rng::adjacent_view::begin()/3 (empty view)");
-BENCHMARK(BM_adjacent_empty<4>)->Name("rng::adjacent_view::begin()/4 (empty view)");
-BENCHMARK(BM_adjacent_empty<5>)->Name("rng::adjacent_view::begin()/5 (empty view)");
-BENCHMARK(BM_adjacent_empty<6>)->Name("rng::adjacent_view::begin()/6 (empty view)");
-BENCHMARK(BM_adjacent_empty<7>)->Name("rng::adjacent_view::begin()/7 (empty view)");
-BENCHMARK(BM_adjacent_empty<8>)->Name("rng::adjacent_view::begin()/8 (empty view)");
-BENCHMARK(BM_adjacent_empty<9>)->Name("rng::adjacent_view::begin()/9 (empty view)");
 BENCHMARK(BM_adjacent_empty<10>)->Name("rng::adjacent_view::begin()/10 (empty view)");
 BENCHMARK(BM_adjacent_empty<100>)->Name("rng::adjacent_view::begin()/100 (empty view)");
 BENCHMARK(BM_adjacent_empty<1000>)->Name("rng::adjacent_view::begin()/1000 (empty view)");


From libcxx-commits at lists.llvm.org  Fri Jul 17 01:10:19 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 01:10:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some
	adjacent_view::begin() benchmarks (PR #210265)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210265@github.com>
Message-ID: <6a59e36b.1e1ac41d.3969c3.3c6a@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

Benchmarking a bunch of very similar values has rather little benefit, sinec they behave essentially the same.


---
Full diff: https://github.com/llvm/llvm-project/pull/210265.diff


1 Files Affected:

- (modified) libcxx/test/benchmarks/adjacent_view_begin.bench.cpp (-14) 


``````````diff
diff --git a/libcxx/test/benchmarks/adjacent_view_begin.bench.cpp b/libcxx/test/benchmarks/adjacent_view_begin.bench.cpp
index 7ca94c990f0cf..ba6ab601a1dac 100644
--- a/libcxx/test/benchmarks/adjacent_view_begin.bench.cpp
+++ b/libcxx/test/benchmarks/adjacent_view_begin.bench.cpp
@@ -28,13 +28,6 @@ void BM_adjacent_full(benchmark::State& state) {
 }
 
 BENCHMARK(BM_adjacent_full<2>)->Name("rng::adjacent_view::begin()/2 (full view)");
-BENCHMARK(BM_adjacent_full<3>)->Name("rng::adjacent_view::begin()/3 (full view)");
-BENCHMARK(BM_adjacent_full<4>)->Name("rng::adjacent_view::begin()/4 (full view)");
-BENCHMARK(BM_adjacent_full<5>)->Name("rng::adjacent_view::begin()/5 (full view)");
-BENCHMARK(BM_adjacent_full<6>)->Name("rng::adjacent_view::begin()/6 (full view)");
-BENCHMARK(BM_adjacent_full<7>)->Name("rng::adjacent_view::begin()/7 (full view)");
-BENCHMARK(BM_adjacent_full<8>)->Name("rng::adjacent_view::begin()/8 (full view)");
-BENCHMARK(BM_adjacent_full<9>)->Name("rng::adjacent_view::begin()/9 (full view)");
 BENCHMARK(BM_adjacent_full<10>)->Name("rng::adjacent_view::begin()/10 (full view)");
 BENCHMARK(BM_adjacent_full<100>)->Name("rng::adjacent_view::begin()/100 (full view)");
 BENCHMARK(BM_adjacent_full<1000>)->Name("rng::adjacent_view::begin()/1000 (full view)");
@@ -50,13 +43,6 @@ void BM_adjacent_empty(benchmark::State& state) {
 }
 
 BENCHMARK(BM_adjacent_empty<2>)->Name("rng::adjacent_view::begin()/2 (empty view)");
-BENCHMARK(BM_adjacent_empty<3>)->Name("rng::adjacent_view::begin()/3 (empty view)");
-BENCHMARK(BM_adjacent_empty<4>)->Name("rng::adjacent_view::begin()/4 (empty view)");
-BENCHMARK(BM_adjacent_empty<5>)->Name("rng::adjacent_view::begin()/5 (empty view)");
-BENCHMARK(BM_adjacent_empty<6>)->Name("rng::adjacent_view::begin()/6 (empty view)");
-BENCHMARK(BM_adjacent_empty<7>)->Name("rng::adjacent_view::begin()/7 (empty view)");
-BENCHMARK(BM_adjacent_empty<8>)->Name("rng::adjacent_view::begin()/8 (empty view)");
-BENCHMARK(BM_adjacent_empty<9>)->Name("rng::adjacent_view::begin()/9 (empty view)");
 BENCHMARK(BM_adjacent_empty<10>)->Name("rng::adjacent_view::begin()/10 (empty view)");
 BENCHMARK(BM_adjacent_empty<100>)->Name("rng::adjacent_view::begin()/100 (empty view)");
 BENCHMARK(BM_adjacent_empty<1000>)->Name("rng::adjacent_view::begin()/1000 (empty view)");

``````````

</details>


https://github.com/llvm/llvm-project/pull/210265

From libcxx-commits at lists.llvm.org  Fri Jul 17 01:14:01 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 17 Jul 2026 01:14:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove redundant benchmarks for
 lexicographical_compare_three_way (PR #210268)
Message-ID: <llvm.org/llvm/llvm-project/pull/210268@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/210268

We already test the `_slow_path` and `_fast_path` functions through the generic `BM_lexicographical_compare_three_way`. We don't need to benchmark them again.


>From 99722d8e0f126c2b8079f4a98bcb552f075688a3 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 17 Jul 2026 10:13:04 +0200
Subject: [PATCH] [libc++] Remove redundant benchmarks for
 lexicographical_compare_three_way

---
 ...exicographical_compare_three_way.bench.cpp | 44 -------------------
 1 file changed, 44 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp b/libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp
index a0b33d26801dc..05d73bdedd998 100644
--- a/libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp
@@ -13,50 +13,6 @@
 #include "benchmark/benchmark.h"
 #include "test_iterators.h"
 
-static void BM_lexicographical_compare_three_way_slow_path(benchmark::State& state) {
-  auto size = state.range(0);
-  std::vector<int> v1;
-  v1.resize(size);
-  // v2 is identical except for the last value.
-  // This means, that `lexicographical_compare_three_way` actually has to
-  // compare the complete vector and cannot bail out early.
-  std::vector<int> v2 = v1;
-  v2.back() += 1;
-  int* b1 = v1.data();
-  int* e1 = b1 + v1.size();
-  int* b2 = v2.data();
-  int* e2 = b2 + v2.size();
-
-  for (auto _ : state) {
-    auto cmp = std::compare_three_way();
-    benchmark::DoNotOptimize(std::__lexicographical_compare_three_way_slow_path(b1, e1, b2, e2, cmp));
-  }
-}
-
-BENCHMARK(BM_lexicographical_compare_three_way_slow_path)->RangeMultiplier(4)->Range(1, 1 << 20);
-
-static void BM_lexicographical_compare_three_way_fast_path(benchmark::State& state) {
-  auto size = state.range(0);
-  std::vector<int> v1;
-  v1.resize(size);
-  // v2 is identical except for the last value.
-  // This means, that `lexicographical_compare_three_way` actually has to
-  // compare the complete vector and cannot bail out early.
-  std::vector<int> v2 = v1;
-  v2.back() += 1;
-  int* b1 = v1.data();
-  int* e1 = b1 + v1.size();
-  int* b2 = v2.data();
-  int* e2 = b2 + v2.size();
-
-  for (auto _ : state) {
-    auto cmp = std::compare_three_way();
-    benchmark::DoNotOptimize(std::__lexicographical_compare_three_way_fast_path(b1, e1, b2, e2, cmp));
-  }
-}
-
-BENCHMARK(BM_lexicographical_compare_three_way_fast_path)->RangeMultiplier(4)->Range(1, 1 << 20);
-
 template <class IteratorT>
 static void BM_lexicographical_compare_three_way(benchmark::State& state) {
   auto size = state.range(0);


From libcxx-commits at lists.llvm.org  Fri Jul 17 01:14:34 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 01:14:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove redundant benchmarks for
 lexicographical_compare_three_way (PR #210268)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210268@github.com>
Message-ID: <6a59e46a.7d702ed2.13d102.7ea3@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

We already test the `_slow_path` and `_fast_path` functions through the generic `BM_lexicographical_compare_three_way`. We don't need to benchmark them again.


---
Full diff: https://github.com/llvm/llvm-project/pull/210268.diff


1 Files Affected:

- (modified) libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp (-44) 


``````````diff
diff --git a/libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp b/libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp
index a0b33d26801dc..05d73bdedd998 100644
--- a/libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp
@@ -13,50 +13,6 @@
 #include "benchmark/benchmark.h"
 #include "test_iterators.h"
 
-static void BM_lexicographical_compare_three_way_slow_path(benchmark::State& state) {
-  auto size = state.range(0);
-  std::vector<int> v1;
-  v1.resize(size);
-  // v2 is identical except for the last value.
-  // This means, that `lexicographical_compare_three_way` actually has to
-  // compare the complete vector and cannot bail out early.
-  std::vector<int> v2 = v1;
-  v2.back() += 1;
-  int* b1 = v1.data();
-  int* e1 = b1 + v1.size();
-  int* b2 = v2.data();
-  int* e2 = b2 + v2.size();
-
-  for (auto _ : state) {
-    auto cmp = std::compare_three_way();
-    benchmark::DoNotOptimize(std::__lexicographical_compare_three_way_slow_path(b1, e1, b2, e2, cmp));
-  }
-}
-
-BENCHMARK(BM_lexicographical_compare_three_way_slow_path)->RangeMultiplier(4)->Range(1, 1 << 20);
-
-static void BM_lexicographical_compare_three_way_fast_path(benchmark::State& state) {
-  auto size = state.range(0);
-  std::vector<int> v1;
-  v1.resize(size);
-  // v2 is identical except for the last value.
-  // This means, that `lexicographical_compare_three_way` actually has to
-  // compare the complete vector and cannot bail out early.
-  std::vector<int> v2 = v1;
-  v2.back() += 1;
-  int* b1 = v1.data();
-  int* e1 = b1 + v1.size();
-  int* b2 = v2.data();
-  int* e2 = b2 + v2.size();
-
-  for (auto _ : state) {
-    auto cmp = std::compare_three_way();
-    benchmark::DoNotOptimize(std::__lexicographical_compare_three_way_fast_path(b1, e1, b2, e2, cmp));
-  }
-}
-
-BENCHMARK(BM_lexicographical_compare_three_way_fast_path)->RangeMultiplier(4)->Range(1, 1 << 20);
-
 template <class IteratorT>
 static void BM_lexicographical_compare_three_way(benchmark::State& state) {
   auto size = state.range(0);

``````````

</details>


https://github.com/llvm/llvm-project/pull/210268

From libcxx-commits at lists.llvm.org  Fri Jul 17 01:14:51 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 01:14:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::isgreater()` (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a59e47b.66c0679a.24976c.3e66@mx.google.com>


lknknm wrote:

> Is there a reason we can't implement the other isX functions yet? If not, I think I'd rather do them in one patch.
@philnik777 I think we can indeed, I just thought it would be more practical to keep it short but I can add them to the scope here.
Are the tests correct so far?

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Fri Jul 17 01:24:46 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 17 Jul 2026 01:24:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some std::minmax_element
	benchmarks (PR #210272)
Message-ID: <llvm.org/llvm/llvm-project/pull/210272@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/210272

There isn't much value in benchmarking lots of similar sizes. We can remove them to improve the time it takes to run the benchmarks.


>From 47a72fbee4501fb6ae7cfa946b751ed0903799e1 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 17 Jul 2026 10:23:48 +0200
Subject: [PATCH] [libc++] Remove some std::minmax_element benchmarks

---
 .../algorithms/min_max_element.bench.cpp         | 16 +---------------
 1 file changed, 1 insertion(+), 15 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/min_max_element.bench.cpp b/libcxx/test/benchmarks/algorithms/min_max_element.bench.cpp
index 67a52f5d7a80a..2ad03b0ed4472 100644
--- a/libcxx/test/benchmarks/algorithms/min_max_element.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/min_max_element.bench.cpp
@@ -13,21 +13,7 @@
 
 #include <benchmark/benchmark.h>
 
-void run_sizes(benchmark::Benchmark* benchmark) {
-  benchmark->Arg(1)
-      ->Arg(2)
-      ->Arg(3)
-      ->Arg(4)
-      ->Arg(64)
-      ->Arg(512)
-      ->Arg(1024)
-      ->Arg(4000)
-      ->Arg(4096)
-      ->Arg(5500)
-      ->Arg(64000)
-      ->Arg(65536)
-      ->Arg(70000);
-}
+void run_sizes(benchmark::Benchmark* benchmark) { benchmark->Arg(2)->Arg(5500)->Arg(70000); }
 
 template <class T>
 void BM_std_minmax_element(benchmark::State& state) {


From libcxx-commits at lists.llvm.org  Fri Jul 17 01:25:21 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 01:25:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some std::minmax_element
	benchmarks (PR #210272)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210272@github.com>
Message-ID: <6a59e6f1.d76d81af.23b347.20ac@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

There isn't much value in benchmarking lots of similar sizes. We can remove them to improve the time it takes to run the benchmarks.


---
Full diff: https://github.com/llvm/llvm-project/pull/210272.diff


1 Files Affected:

- (modified) libcxx/test/benchmarks/algorithms/min_max_element.bench.cpp (+1-15) 


``````````diff
diff --git a/libcxx/test/benchmarks/algorithms/min_max_element.bench.cpp b/libcxx/test/benchmarks/algorithms/min_max_element.bench.cpp
index 67a52f5d7a80a..2ad03b0ed4472 100644
--- a/libcxx/test/benchmarks/algorithms/min_max_element.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/min_max_element.bench.cpp
@@ -13,21 +13,7 @@
 
 #include <benchmark/benchmark.h>
 
-void run_sizes(benchmark::Benchmark* benchmark) {
-  benchmark->Arg(1)
-      ->Arg(2)
-      ->Arg(3)
-      ->Arg(4)
-      ->Arg(64)
-      ->Arg(512)
-      ->Arg(1024)
-      ->Arg(4000)
-      ->Arg(4096)
-      ->Arg(5500)
-      ->Arg(64000)
-      ->Arg(65536)
-      ->Arg(70000);
-}
+void run_sizes(benchmark::Benchmark* benchmark) { benchmark->Arg(2)->Arg(5500)->Arg(70000); }
 
 template <class T>
 void BM_std_minmax_element(benchmark::State& state) {

``````````

</details>


https://github.com/llvm/llvm-project/pull/210272

From libcxx-commits at lists.llvm.org  Fri Jul 17 01:59:14 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 17 Jul 2026 01:59:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::isgreater()` (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a59eee2.481afadf.9e047.e1e6@mx.google.com>


frederick-vs-ja wrote:

For record, this follows-up #94118 which was landed in LLVM 20. I think it's clear enough to add `_LIBCPP_CONSTEXPR_SINCE_CXX23` to other `is_meow` functions in one PR.

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Fri Jul 17 02:08:02 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 17 Jul 2026 02:08:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] avoid include Uppercase windows
	headers (PR #208903)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208903@github.com>
Message-ID: <6a59f0f2.d76d81af.23b347.09c4@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/208903

From libcxx-commits at lists.llvm.org  Fri Jul 17 02:11:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 02:11:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 2580624 - [libcxx] avoid include
	Uppercase windows headers (#208903)
Message-ID: <6a59f1d7.1728eaf3.2a2f6.340c@mx.google.com>


Author: cqwrteur
Date: 2026-07-17T12:11:45+03:00
New Revision: 2580624091e36b85c162439513eef8e752217a1d

URL: https://github.com/llvm/llvm-project/commit/2580624091e36b85c162439513eef8e752217a1d
DIFF: https://github.com/llvm/llvm-project/commit/2580624091e36b85c162439513eef8e752217a1d.diff

LOG: [libcxx] avoid include Uppercase windows headers (#208903)

We should use unknwn.h and windows.h instead of Unknwn.h and Windows.h
because Linux and other non‑Windows systems treat filenames as
case‑sensitive. Windows does not, so mixed‑case includes work there but
break elsewhere. Both mingw‑w64‑crt and windows‑msvc‑sysroot provide all
Windows headers in fully lowercase, so using the lowercase forms ensures
consistent cross‑platform builds.

Fixes #208901

Added: 
    

Modified: 
    libcxx/src/support/runtime/exception_pointer_msvc.ipp

Removed: 
    


################################################################################
diff  --git a/libcxx/src/support/runtime/exception_pointer_msvc.ipp b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
index 72360a2d791bd..3102d1ee93db2 100644
--- a/libcxx/src/support/runtime/exception_pointer_msvc.ipp
+++ b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
@@ -14,8 +14,8 @@ _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmultichar")
 #include <cstdlib>
 #include <cstring>
 
-#include <Unknwn.h>
-#include <Windows.h>
+#include <unknwn.h>
+#include <windows.h>
 struct _ThrowInfo;
 #include <eh.h>
 #include <ehdata.h>


        

From libcxx-commits at lists.llvm.org  Fri Jul 17 02:11:53 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Fri, 17 Jul 2026 02:11:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] avoid include Uppercase windows
	headers (PR #208903)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208903@github.com>
Message-ID: <6a59f1d9.160324b0.1f653e.b45b@mx.google.com>


https://github.com/mstorsjo closed https://github.com/llvm/llvm-project/pull/208903

From libcxx-commits at lists.llvm.org  Fri Jul 17 02:14:03 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Fri, 17 Jul 2026 02:14:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] avoid include Uppercase windows
	headers (PR #208903)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208903@github.com>
Message-ID: <6a59f25b.f06f4994.2f0620.b217@mx.google.com>


https://github.com/mstorsjo milestoned https://github.com/llvm/llvm-project/pull/208903

From libcxx-commits at lists.llvm.org  Fri Jul 17 02:14:15 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Martin_Storsj=C3=B6?= via libcxx-commits)
Date: Fri, 17 Jul 2026 02:14:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] avoid include Uppercase windows
	headers (PR #208903)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208903@github.com>
Message-ID: <6a59f267.743400b0.23a7b2.9dd6@mx.google.com>


mstorsjo wrote:

/cherry-pick 2580624091e36b85c162439513eef8e752217a1d

https://github.com/llvm/llvm-project/pull/208903

From libcxx-commits at lists.llvm.org  Fri Jul 17 02:36:01 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 17 Jul 2026 02:36:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement single element
 vector::insert in terms of emplace (PR #210284)
Message-ID: <llvm.org/llvm/llvm-project/pull/210284@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/210284

This also ports an optimization to `emplace`: if we copy/move construct the element we can avoid constructing a temporary.


>From 9e55faefd42d6637185809a4e9dfc1dd38049fb1 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 17 Jul 2026 11:34:33 +0200
Subject: [PATCH] [libc++] Implement single element vector::insert in terms of
 emplace

---
 libcxx/include/__vector/vector.h | 65 ++++++++++----------------------
 1 file changed, 20 insertions(+), 45 deletions(-)

diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 8226a7f87a119..22ee34dd86bc0 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -58,6 +58,7 @@
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
 #include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/remove_const_ref.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/declval.h>
 #include <__utility/exception_guard.h>
@@ -504,9 +505,14 @@ class vector {
     this->__destruct_at_end(__layout_.__end_ptr() - 1);
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, const_reference __x);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, const_reference __x) {
+    return emplace(__position, __x);
+  }
+
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, value_type&& __x) {
+    return emplace(__position, std::move(__x));
+  }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, value_type&& __x);
   template <class... _Args>
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator emplace(const_iterator __position, _Args&&... __args);
 
@@ -1128,49 +1134,6 @@ vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointe
   std::move_backward(__from_s, __from_s + __n, __old_last);
 }
 
-template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
-vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x) {
-  pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (size() != capacity()) {
-    pointer __end = __layout_.__end_ptr();
-    if (__p == __end) {
-      __emplace_back_assume_capacity(__x);
-    } else {
-      __move_range(__p, __end, __p + 1);
-      const_pointer __xr = pointer_traits<const_pointer>::pointer_to(__x);
-      if (std::__is_pointer_in_range(std::__to_address(__p), std::__to_address(__end), std::addressof(__x)))
-        ++__xr;
-      *__p = *__xr;
-    }
-  } else {
-    _SplitBuffer __v(__recommend(size() + 1), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
-    __v.emplace_back(__x);
-    __p = __layout_.__relocate_with_pivot(__v, __p);
-  }
-  return __make_iter(__p);
-}
-
-template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
-vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x) {
-  pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (size() != capacity()) {
-    pointer __end = __layout_.__end_ptr();
-    if (__p == __end) {
-      __emplace_back_assume_capacity(std::move(__x));
-    } else {
-      __move_range(__p, __end, __p + 1);
-      *__p = std::move(__x);
-    }
-  } else {
-    _SplitBuffer __v(__recommend(size() + 1), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
-    __v.emplace_back(std::move(__x));
-    __p = __layout_.__relocate_with_pivot(__v, __p);
-  }
-  return __make_iter(__p);
-}
-
 template <class _Tp, class _Allocator>
 template <class... _Args>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
@@ -1181,6 +1144,18 @@ vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) {
     if (__p == __end) {
       __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
     } else {
+#ifndef _LIBCPP_CXX03_LANG
+      if constexpr (sizeof...(_Args) == 1) {
+        if constexpr (is_same<__remove_const_ref_t<_Args>..., value_type>::value) {
+          __move_range(__p, __end, __p + 1);
+          auto __xr = std::addressof(__args...);
+          if (std::__is_pointer_in_range(std::__to_address(__p), std::__to_address(__end), std::addressof(__args)...))
+            ++__xr;
+          *__p = (std::forward<_Args>(*__xr), ...);
+          return __make_iter(__p);
+        }
+      }
+#endif
       __temp_value<value_type, _Allocator> __tmp(this->__layout_.__alloc(), std::forward<_Args>(__args)...);
       __move_range(__p, __end, __p + 1);
       *__p = std::move(__tmp.get());


From libcxx-commits at lists.llvm.org  Fri Jul 17 02:39:48 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Fri, 17 Jul 2026 02:39:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] avoid include Uppercase windows
	headers (PR #208903)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208903@github.com>
Message-ID: <6a59f864.743400b0.23a7b2.3998@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `sanitizer-x86_64-linux-android` running on `sanitizer-buildbot-android` while building `libcxx` at step 2 "annotate".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/186/builds/319

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 2 (annotate) failure: 'python ../sanitizer_buildbot/sanitizers/zorg/buildbot/builders/sanitizers/buildbot_selector.py' (failure)
...
[686/720] Building CXX object lib/AsmParser/CMakeFiles/LLVMAsmParser.dir/Parser.cpp.o
[687/720] Linking CXX static library lib/libLLVMTargetParser.a
[688/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Platform.cpp.o
[689/720] Linking CXX static library lib/libLLVMBinaryFormat.a
[690/720] Linking CXX static library lib/libLLVMCore.a
[691/720] Linking CXX static library lib/libLLVMBitReader.a
[692/720] Linking CXX static library lib/libLLVMDebugInfoDWARFLowLevel.a
[693/720] Linking CXX static library lib/libLLVMMC.a
[694/720] Linking CXX static library lib/libLLVMMCParser.a
[695/720] Building Opts.inc...
[696/720] Building CXX object lib/DebugInfo/Symbolize/CMakeFiles/LLVMSymbolize.dir/Symbolize.cpp.o
[697/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Symbol.cpp.o
[698/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/TextAPIError.cpp.o
[699/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/RecordVisitor.cpp.o
[700/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/SymbolSet.cpp.o
[701/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Target.cpp.o
[702/720] Building CXX object tools/llvm-symbolizer/CMakeFiles/llvm-symbolizer.dir/llvm-symbolizer-driver.cpp.o
[703/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/InterfaceFile.cpp.o
[704/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/RecordsSlice.cpp.o
[705/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Utils.cpp.o
[706/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/TextStubCommon.cpp.o
[707/720] Building CXX object tools/llvm-symbolizer/CMakeFiles/llvm-symbolizer.dir/llvm-symbolizer.cpp.o
[708/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/TextStubV5.cpp.o
[709/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/TextStub.cpp.o
[710/720] Linking CXX static library lib/libLLVMTextAPI.a
[711/720] Building CXX object lib/AsmParser/CMakeFiles/LLVMAsmParser.dir/LLParser.cpp.o
[712/720] Linking CXX static library lib/libLLVMAsmParser.a
[713/720] Linking CXX static library lib/libLLVMIRReader.a
[714/720] Linking CXX static library lib/libLLVMObject.a
[715/720] Linking CXX static library lib/libLLVMDebugInfoDWARF.a
[716/720] Linking CXX static library lib/libLLVMDebugInfoPDB.a
[717/720] Linking CXX static library lib/libLLVMDebugInfoGSYM.a
[718/720] Linking CXX static library lib/libLLVMSymbolize.a
[719/720] Linking CXX static library lib/libLLVMDebuginfod.a
[720/720] Linking CXX executable bin/llvm-symbolizer
ninja: Entering directory `compiler_rt_build_android_i686'
ninja: error: loading 'build.ninja': No such file or directory

How to reproduce locally: https://github.com/google/sanitizers/wiki/SanitizerBotReproduceBuild


@@@STEP_FAILURE@@@

@@@STEP_FAILURE@@@

@@@STEP_FAILURE@@@
@@@BUILD_STEP restart adb server@@@
* daemon not running; starting now at tcp:5037
* daemon started successfully
@@@BUILD_STEP run all tests@@@
Step 9 (run cmake) failure: run cmake (failure)
...
-- Performing Test HAVE_CXX_FLAG_WTHREAD_SAFETY - Success
-- Enabling additional flags: -DINCLUDE_DIRECTORIES=/var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm-project/third-party/benchmark/include
-- Performing Test THREAD_SAFETY_ATTRIBUTES -- failed
-- Performing Test HAVE_CXX_FLAG_COVERAGE
-- Performing Test THREAD_SAFETY_ATTRIBUTES -- failed
-- Performing Test HAVE_CXX_FLAG_COVERAGE
-- Performing Test HAVE_CXX_FLAG_COVERAGE - Failed
-- Performing Test HAVE_CXX_FLAG_COVERAGE - Failed
CMake Warning at /var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm-project/third-party/benchmark/CMakeLists.txt:329 (message):
  Using std::regex with exceptions disabled is not fully supported


CMake Warning at /var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm-project/third-party/benchmark/CMakeLists.txt:329 (message):
  Using std::regex with exceptions disabled is not fully supported
-- Performing Test STEADY_CLOCK -- failed
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test STEADY_CLOCK -- failed
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed
-- Check if compiler accepts -pthread
-- Check if compiler accepts -pthread
-- Check if compiler accepts -pthread - yes
-- Check if compiler accepts -pthread - yes
-- Found Threads: TRUE  
-- Found Threads: TRUE  
-- Performing Test PTHREAD_AFFINITY -- failed
-- Performing Test PTHREAD_AFFINITY -- failed
-- Configuring done (23.6s)
-- Configuring done (23.7s)

-- Performing Test THREAD_SAFETY_ATTRIBUTES -- failed
-- Performing Test HAVE_CXX_FLAG_COVERAGE
-- Performing Test HAVE_CXX_FLAG_COVERAGE - Failed
CMake Warning at /var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm-project/third-party/benchmark/CMakeLists.txt:329 (message):
  Using std::regex with exceptions disabled is not fully supported
-- Performing Test STEADY_CLOCK -- failed
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed
-- Check if compiler accepts -pthread
-- Check if compiler accepts -pthread - yes
-- Found Threads: TRUE  
-- Performing Test PTHREAD_AFFINITY -- failed
-- Configuring done (24.8s)
-- Generating done (3.2s)
-- Generating done (3.2s)
-- Build files have been written to: /var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm_build_android_aarch64
-- Build files have been written to: /var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm_build_android_i686
-- Generating done (3.0s)
-- Build files have been written to: /var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm_build_android_arm
Step 12 (build android/i686) failure: build android/i686 (failure)
...
[679/720] Building CXX object lib/TargetParser/CMakeFiles/LLVMTargetParser.dir/TargetDataLayout.cpp.o
[680/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/ArchitectureSet.cpp.o
[681/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Architecture.cpp.o
[682/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/PackedVersion.cpp.o
[683/720] Building CXX object lib/TargetParser/CMakeFiles/LLVMTargetParser.dir/RISCVTargetParser.cpp.o
[684/720] Building CXX object lib/TargetParser/CMakeFiles/LLVMTargetParser.dir/Triple.cpp.o
[685/720] Building CXX object lib/TargetParser/CMakeFiles/LLVMTargetParser.dir/RISCVISAInfo.cpp.o
[686/720] Building CXX object lib/AsmParser/CMakeFiles/LLVMAsmParser.dir/Parser.cpp.o
[687/720] Linking CXX static library lib/libLLVMTargetParser.a
[688/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Platform.cpp.o
[689/720] Linking CXX static library lib/libLLVMBinaryFormat.a
[690/720] Linking CXX static library lib/libLLVMCore.a
[691/720] Linking CXX static library lib/libLLVMBitReader.a
[692/720] Linking CXX static library lib/libLLVMDebugInfoDWARFLowLevel.a
[693/720] Linking CXX static library lib/libLLVMMC.a
[694/720] Linking CXX static library lib/libLLVMMCParser.a
[695/720] Building Opts.inc...
[696/720] Building CXX object lib/DebugInfo/Symbolize/CMakeFiles/LLVMSymbolize.dir/Symbolize.cpp.o
[697/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Symbol.cpp.o
[698/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/TextAPIError.cpp.o
[699/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/RecordVisitor.cpp.o
[700/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/SymbolSet.cpp.o
[701/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Target.cpp.o
[702/720] Building CXX object tools/llvm-symbolizer/CMakeFiles/llvm-symbolizer.dir/llvm-symbolizer-driver.cpp.o
[703/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/InterfaceFile.cpp.o
[704/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/RecordsSlice.cpp.o
[705/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Utils.cpp.o
[706/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/TextStubCommon.cpp.o
[707/720] Building CXX object tools/llvm-symbolizer/CMakeFiles/llvm-symbolizer.dir/llvm-symbolizer.cpp.o
[708/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/TextStubV5.cpp.o
[709/720] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/TextStub.cpp.o
[710/720] Linking CXX static library lib/libLLVMTextAPI.a
[711/720] Building CXX object lib/AsmParser/CMakeFiles/LLVMAsmParser.dir/LLParser.cpp.o
[712/720] Linking CXX static library lib/libLLVMAsmParser.a
[713/720] Linking CXX static library lib/libLLVMIRReader.a
[714/720] Linking CXX static library lib/libLLVMObject.a
[715/720] Linking CXX static library lib/libLLVMDebugInfoDWARF.a
[716/720] Linking CXX static library lib/libLLVMDebugInfoPDB.a
[717/720] Linking CXX static library lib/libLLVMDebugInfoGSYM.a
[718/720] Linking CXX static library lib/libLLVMSymbolize.a
[719/720] Linking CXX static library lib/libLLVMDebuginfod.a
[720/720] Linking CXX executable bin/llvm-symbolizer
ninja: Entering directory `compiler_rt_build_android_i686'
ninja: error: loading 'build.ninja': No such file or directory

How to reproduce locally: https://github.com/google/sanitizers/wiki/SanitizerBotReproduceBuild





```

</details>

https://github.com/llvm/llvm-project/pull/208903

From libcxx-commits at lists.llvm.org  Fri Jul 17 03:10:21 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 03:10:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement single element
 vector::insert in terms of emplace (PR #210284)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210284@github.com>
Message-ID: <6a59ff8d.492e23e4.d19bc.4f38@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

This also ports an optimization to `emplace`: if we copy/move construct the element we can avoid constructing a temporary.


---
Full diff: https://github.com/llvm/llvm-project/pull/210284.diff


1 Files Affected:

- (modified) libcxx/include/__vector/vector.h (+20-45) 


``````````diff
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 8226a7f87a119..22ee34dd86bc0 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -58,6 +58,7 @@
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
 #include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/remove_const_ref.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/declval.h>
 #include <__utility/exception_guard.h>
@@ -504,9 +505,14 @@ class vector {
     this->__destruct_at_end(__layout_.__end_ptr() - 1);
   }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, const_reference __x);
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, const_reference __x) {
+    return emplace(__position, __x);
+  }
+
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, value_type&& __x) {
+    return emplace(__position, std::move(__x));
+  }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator insert(const_iterator __position, value_type&& __x);
   template <class... _Args>
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator emplace(const_iterator __position, _Args&&... __args);
 
@@ -1128,49 +1134,6 @@ vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointe
   std::move_backward(__from_s, __from_s + __n, __old_last);
 }
 
-template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
-vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x) {
-  pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (size() != capacity()) {
-    pointer __end = __layout_.__end_ptr();
-    if (__p == __end) {
-      __emplace_back_assume_capacity(__x);
-    } else {
-      __move_range(__p, __end, __p + 1);
-      const_pointer __xr = pointer_traits<const_pointer>::pointer_to(__x);
-      if (std::__is_pointer_in_range(std::__to_address(__p), std::__to_address(__end), std::addressof(__x)))
-        ++__xr;
-      *__p = *__xr;
-    }
-  } else {
-    _SplitBuffer __v(__recommend(size() + 1), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
-    __v.emplace_back(__x);
-    __p = __layout_.__relocate_with_pivot(__v, __p);
-  }
-  return __make_iter(__p);
-}
-
-template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
-vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x) {
-  pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (size() != capacity()) {
-    pointer __end = __layout_.__end_ptr();
-    if (__p == __end) {
-      __emplace_back_assume_capacity(std::move(__x));
-    } else {
-      __move_range(__p, __end, __p + 1);
-      *__p = std::move(__x);
-    }
-  } else {
-    _SplitBuffer __v(__recommend(size() + 1), __p - this->__layout_.__begin_ptr(), this->__layout_.__alloc());
-    __v.emplace_back(std::move(__x));
-    __p = __layout_.__relocate_with_pivot(__v, __p);
-  }
-  return __make_iter(__p);
-}
-
 template <class _Tp, class _Allocator>
 template <class... _Args>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
@@ -1181,6 +1144,18 @@ vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) {
     if (__p == __end) {
       __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
     } else {
+#ifndef _LIBCPP_CXX03_LANG
+      if constexpr (sizeof...(_Args) == 1) {
+        if constexpr (is_same<__remove_const_ref_t<_Args>..., value_type>::value) {
+          __move_range(__p, __end, __p + 1);
+          auto __xr = std::addressof(__args...);
+          if (std::__is_pointer_in_range(std::__to_address(__p), std::__to_address(__end), std::addressof(__args)...))
+            ++__xr;
+          *__p = (std::forward<_Args>(*__xr), ...);
+          return __make_iter(__p);
+        }
+      }
+#endif
       __temp_value<value_type, _Allocator> __tmp(this->__layout_.__alloc(), std::forward<_Args>(__args)...);
       __move_range(__p, __end, __p + 1);
       *__p = std::move(__tmp.get());

``````````

</details>


https://github.com/llvm/llvm-project/pull/210284

From libcxx-commits at lists.llvm.org  Fri Jul 17 03:14:40 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 03:14:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] avoid include Uppercase windows
	headers (PR #208903)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208903@github.com>
Message-ID: <6a5a0090.824e11a6.9c376.a593@mx.google.com>


llvmbot wrote:

/pull-request llvm/llvm-project#210292

https://github.com/llvm/llvm-project/pull/208903

From libcxx-commits at lists.llvm.org  Fri Jul 17 04:23:25 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 04:23:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::isgreater()` (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5a10ad.507fc713.239434.2007@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Fri Jul 17 04:36:30 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 04:36:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a5a13be.507fc713.239434.1dfd@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Fri Jul 17 05:41:12 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 05:41:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::isgreater()` (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5a22e8.f06f4994.2f0620.a4e6@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 1/8] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 2/8] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 3/8] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 4/8] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 5/8] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 6/8] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 7/8] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 8/8] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).


From libcxx-commits at lists.llvm.org  Fri Jul 17 05:42:01 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 05:42:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5a2319.3cce8983.3cf542.2444@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Fri Jul 17 05:44:18 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 05:44:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5a23a2.af6ba3ed.3c8c03.68be@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions cpp,h -- libcxx/test/std/numerics/c.math/isgreater.pass.cpp libcxx/include/__math/traits.h libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90c..f32dc47ab 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -162,7 +163,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +172,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +181,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +228,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

``````````

</details>


https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Fri Jul 17 06:16:35 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 06:16:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5a2b33.4987c784.2c60a9.d10f@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 1/9] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 2/9] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 3/9] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 4/9] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 5/9] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 6/9] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 7/9] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 8/9] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 9/9] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }


From libcxx-commits at lists.llvm.org  Fri Jul 17 06:20:20 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 06:20:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5a2c14.5a0412d2.3daf32.6379@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/10] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/10] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/10] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/10] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/10] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/10] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/10] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/10] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/10] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/10] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);


From libcxx-commits at lists.llvm.org  Fri Jul 17 06:20:55 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 06:20:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5a2c37.92ea0e2a.4a9a5.981e@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Fri Jul 17 06:25:44 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 06:25:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5a2d58.13be6df8.22eac4.f8f7@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Fri Jul 17 06:37:56 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 06:37:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5a3034.824e11a6.9c376.fb64@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/11] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/11] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/11] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/11] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/11] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/11] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/11] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/11] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/11] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/11] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/11] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 


From libcxx-commits at lists.llvm.org  Fri Jul 17 07:12:09 2026
From: libcxx-commits at lists.llvm.org (Pavel Novikov via libcxx-commits)
Date: Fri, 17 Jul 2026 07:12:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5a3839.b1416da1.3242c8.adbd@mx.google.com>


toughengineer wrote:

Here's a reasonable and realistic (in my opinion) example:
```c++
// given some string like input, e.g.
std::string_view input;
// and also some locale and output
std::locale locale;
std::string output;
// ...
auto p = input |
  std::views::filter([&locale](char c) { return std::isprint(c, locale); });
output.append(p.begin(), p.end());
```
Even this almost trivial example demostrates the performance issue:
<img width="1210" height="681" alt="ranges" src="https://github.com/user-attachments/assets/9a812657-cca9-47b2-a979-7656c9ec68e5" />
<details><summary>benchmark output before</summary>

```
2026-07-17T16:36:18+03:00
Running ./string_append
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.36, 0.18, 0.16
-------------------------------------------------------------------------------
Benchmark                     Time             CPU   Iterations UserCounters...
-------------------------------------------------------------------------------
bench_ranges/1             9.03 ns         9.03 ns     79388662 t/i=9.031ns
bench_ranges/2             19.7 ns         19.7 ns     35723581 t/i=9.84356ns
bench_ranges/4             36.1 ns         36.1 ns     19531441 t/i=9.0152ns
bench_ranges/8             69.0 ns         69.0 ns      9956207 t/i=8.62202ns
bench_ranges/16             125 ns          125 ns      5613594 t/i=7.84345ns
bench_ranges/32             247 ns          247 ns      2822421 t/i=7.72773ns
bench_ranges/64             516 ns          516 ns      1372805 t/i=8.05528ns
bench_ranges/128            992 ns          992 ns       694859 t/i=7.75294ns
bench_ranges/256           1958 ns         1958 ns       351618 t/i=7.6484ns
bench_ranges/512           3927 ns         3927 ns       179205 t/i=7.66954ns
bench_ranges/1024          7838 ns         7838 ns        90166 t/i=7.65432ns
bench_ranges/2048         21615 ns        21615 ns        32478 t/i=10.5541ns
bench_ranges/4096         52982 ns        52980 ns        13421 t/i=12.9345ns
bench_ranges/8192        117859 ns       117857 ns         6034 t/i=14.3868ns
bench_ranges/16384       255739 ns       255729 ns         2737 t/i=15.6085ns
bench_ranges/32768       527432 ns       527419 ns         1344 t/i=16.0956ns
bench_ranges/65536      1070200 ns      1070150 ns          641 t/i=16.3292ns
bench_ranges/131072     2134541 ns      2134475 ns          322 t/i=16.2848ns
bench_ranges/262144     4311675 ns      4311642 ns          164 t/i=16.4476ns
bench_ranges/524288     8571076 ns      8570984 ns           81 t/i=16.3479ns
bench_ranges/1048576   17185996 ns     17185873 ns           41 t/i=16.3897ns
```

</details>
<details><summary>benchmark output after</summary>

```
2026-07-17T16:37:09+03:00
Running ./string_append
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.39, 0.22, 0.18
-------------------------------------------------------------------------------
Benchmark                     Time             CPU   Iterations UserCounters...
-------------------------------------------------------------------------------
bench_ranges/1             8.40 ns         8.40 ns     86345986 t/i=8.40176ns
bench_ranges/2             15.6 ns         15.6 ns     44261217 t/i=7.80893ns
bench_ranges/4             26.5 ns         26.5 ns     26726279 t/i=6.61509ns
bench_ranges/8             49.7 ns         49.7 ns     14098475 t/i=6.20786ns
bench_ranges/16            85.9 ns         85.9 ns      8259402 t/i=5.36759ns
bench_ranges/32             169 ns          169 ns      3998519 t/i=5.27777ns
bench_ranges/64             349 ns          349 ns      1987558 t/i=5.45099ns
bench_ranges/128            683 ns          683 ns      1039694 t/i=5.33238ns
bench_ranges/256           1338 ns         1338 ns       517963 t/i=5.22682ns
bench_ranges/512           2631 ns         2631 ns       266303 t/i=5.13823ns
bench_ranges/1024          5245 ns         5245 ns       133538 t/i=5.1224ns
bench_ranges/2048         10921 ns        10920 ns        64641 t/i=5.33222ns
bench_ranges/4096         29330 ns        29329 ns        23693 t/i=7.16048ns
bench_ranges/8192         71116 ns        71114 ns         9436 t/i=8.68086ns
bench_ranges/16384       163212 ns       163205 ns         4357 t/i=9.96126ns
bench_ranges/32768       343948 ns       343942 ns         2012 t/i=10.4963ns
bench_ranges/65536       699559 ns       699560 ns          958 t/i=10.6744ns
bench_ranges/131072     1416766 ns      1416731 ns          497 t/i=10.8088ns
bench_ranges/262144     2861586 ns      2861507 ns          243 t/i=10.9158ns
bench_ranges/524288     5745899 ns      5745906 ns          120 t/i=10.9594ns
bench_ranges/1048576   11501892 ns     11501800 ns           62 t/i=10.969ns
```

</details>

In general the performance difference may be unbounded due to unbounded complexity of the processing.

The code may be garbage (like in this example), but I think you should not penalize the code on top of that.

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Fri Jul 17 07:48:29 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 07:48:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5a40bd.056801cd.12ca91.a754@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/12] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/12] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/12] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/12] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/12] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/12] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/12] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/12] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/12] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/12] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/12] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/12] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Fri Jul 17 08:38:43 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 17 Jul 2026 08:38:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
Message-ID: <llvm.org/llvm/llvm-project/pull/210360@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/210360

`__next_id` is only ever used inside the dylib, so we can avoid making it public. This also allows using `std::atomic` instead of libc++-specific internals.


>From 0bd62afddec953e01d3a837ae04e01e24bbee7f8 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 17 Jul 2026 17:31:21 +0200
Subject: [PATCH] [libc++] Remove locale::id::__next_id from the ABI

---
 libcxx/include/__locale                                    | 2 --
 ...ple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...pc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...ple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...wn-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...inux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 1 -
 libcxx/src/locale.cpp                                      | 7 +++----
 11 files changed, 3 insertions(+), 15 deletions(-)

diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index bb8f8e7bbf5d9..f878aa48071ca 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -162,8 +162,6 @@ class _LIBCPP_EXPORTED_FROM_ABI locale::id {
   once_flag __flag_;
   int32_t __id_;
 
-  static int32_t __next_id;
-
 public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR id() : __id_(0) {}
   void operator=(const id&) = delete;
diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 2a419e8c8af05..fad3441d59ab7 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1730,7 +1730,6 @@
 {'is_defined': True, 'name': '__ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__16locale2id9__next_idE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale3allE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4noneE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4timeE', 'size': 0, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 414a239757e1c..08ee6a6a515c9 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1368,7 +1368,6 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16gslice6__initEj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 590c0de8ce20f..dd79e25d66915 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -699,7 +699,6 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
-{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index f4f4ab99ad866..e2cc8a0f106f1 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -699,7 +699,6 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
-{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 92a37db24ac5f..be1d6382a7983 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1719,7 +1719,6 @@
 {'is_defined': True, 'name': '__ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__16locale2id9__next_idE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale3allE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4noneE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4timeE', 'size': 0, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index e1b1e110318fa..3f618601469dd 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1368,7 +1368,6 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 11cc8f180e824..159e974775034 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1380,7 +1380,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..46094de75b221 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1379,7 +1379,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 2b69d6dc89138..df53f1b42fbec 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1350,7 +1350,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index b0a69ab74f94c..8e5db250e7887 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -562,11 +562,10 @@ void locale::facet::__on_zero_shared() noexcept { delete this; }
 
 // locale::id
 
-constinit int32_t locale::id::__next_id = 0;
-
 long locale::id::__get() {
-  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&__next_id, 1); });
-  return __id_ - 1;
+  constinit static atomic<int32_t> next_id = 0;
+  call_once(__flag_, [&] { __id_ = next_id++; });
+  return __id_;
 }
 
 // template <> class collate_byname<char>


From libcxx-commits at lists.llvm.org  Fri Jul 17 08:39:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 08:39:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210360@github.com>
Message-ID: <6a5a4cae.04e40411.130e23.da57@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

`__next_id` is only ever used inside the dylib, so we can avoid making it public. This also allows using `std::atomic` instead of libc++-specific internals.


---
Full diff: https://github.com/llvm/llvm-project/pull/210360.diff


11 Files Affected:

- (modified) libcxx/include/__locale (-2) 
- (modified) libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist (-1) 
- (modified) libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist (-1) 
- (modified) libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist (-1) 
- (modified) libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist (-1) 
- (modified) libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist (-1) 
- (modified) libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist (-1) 
- (modified) libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist (-1) 
- (modified) libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist (-1) 
- (modified) libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist (-1) 
- (modified) libcxx/src/locale.cpp (+3-4) 


``````````diff
diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index bb8f8e7bbf5d9..f878aa48071ca 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -162,8 +162,6 @@ class _LIBCPP_EXPORTED_FROM_ABI locale::id {
   once_flag __flag_;
   int32_t __id_;
 
-  static int32_t __next_id;
-
 public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR id() : __id_(0) {}
   void operator=(const id&) = delete;
diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 2a419e8c8af05..fad3441d59ab7 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1730,7 +1730,6 @@
 {'is_defined': True, 'name': '__ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__16locale2id9__next_idE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale3allE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4noneE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4timeE', 'size': 0, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 414a239757e1c..08ee6a6a515c9 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1368,7 +1368,6 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16gslice6__initEj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 590c0de8ce20f..dd79e25d66915 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -699,7 +699,6 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
-{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index f4f4ab99ad866..e2cc8a0f106f1 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -699,7 +699,6 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
-{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 92a37db24ac5f..be1d6382a7983 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1719,7 +1719,6 @@
 {'is_defined': True, 'name': '__ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__16locale2id9__next_idE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale3allE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4noneE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4timeE', 'size': 0, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index e1b1e110318fa..3f618601469dd 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1368,7 +1368,6 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 11cc8f180e824..159e974775034 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1380,7 +1380,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..46094de75b221 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1379,7 +1379,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 2b69d6dc89138..df53f1b42fbec 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1350,7 +1350,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index b0a69ab74f94c..8e5db250e7887 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -562,11 +562,10 @@ void locale::facet::__on_zero_shared() noexcept { delete this; }
 
 // locale::id
 
-constinit int32_t locale::id::__next_id = 0;
-
 long locale::id::__get() {
-  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&__next_id, 1); });
-  return __id_ - 1;
+  constinit static atomic<int32_t> next_id = 0;
+  call_once(__flag_, [&] { __id_ = next_id++; });
+  return __id_;
 }
 
 // template <> class collate_byname<char>

``````````

</details>


https://github.com/llvm/llvm-project/pull/210360

From libcxx-commits at lists.llvm.org  Fri Jul 17 09:06:46 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Fri, 17 Jul 2026 09:06:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libcxx] Partially revert
	#208330 (PR #209928)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209928@github.com>
Message-ID: <6a5a5316.507fc713.239434.42bc@mx.google.com>


https://github.com/petrhosek milestoned https://github.com/llvm/llvm-project/pull/209928

From libcxx-commits at lists.llvm.org  Fri Jul 17 09:07:08 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Fri, 17 Jul 2026 09:07:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libcxx] Partially revert
	#208330 (PR #209928)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209928@github.com>
Message-ID: <6a5a532c.7270d2fd.a9bd6.05f3@mx.google.com>


petrhosek wrote:

/cherry-pick https://github.com/llvm/llvm-project/commit/db7356dde8312a86e79ec0d7b7f844878a1a743c

https://github.com/llvm/llvm-project/pull/209928

From libcxx-commits at lists.llvm.org  Fri Jul 17 09:15:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 09:15:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libcxx] Partially revert
	#208330 (PR #209928)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209928@github.com>
Message-ID: <6a5a551b.bc28b987.2e0c9a.6717@mx.google.com>


llvmbot wrote:

/pull-request llvm/llvm-project#210364

https://github.com/llvm/llvm-project/pull/209928

From libcxx-commits at lists.llvm.org  Fri Jul 17 09:15:55 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Fri, 17 Jul 2026 09:15:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a5a553b.4a342cbc.3a2f36.6060@mx.google.com>


smallp-o-p wrote:

> This basically LGTM, but I'd like the file moving to be separated. That has nothing to do with the actual patch AFAICT.

The test I added needs access to `test_text_encoding.h` which was local to `std/text/text_encoding`. 

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Fri Jul 17 09:43:22 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 09:43:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add sentinel OtherConst
 cases for `nodiscard` tests in `enumerate_view` (PR #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5a5baa.4987c784.2c60a9.bfa1@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/209289

From libcxx-commits at lists.llvm.org  Fri Jul 17 09:43:39 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 09:43:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add sentinel
 `OtherConst` cases for `[[nodiscard]]` tests in `enumerate_view` (PR
 #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5a5bbb.481afadf.9e047.112d@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/209289

From libcxx-commits at lists.llvm.org  Fri Jul 17 09:52:29 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 09:52:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add sentinel
 `OtherConst` cases for `[[nodiscard]]` tests in `enumerate_view` (PR
 #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5a5dcd.824e11a6.9c376.186a@mx.google.com>


================

----------------
Zingam wrote:

```suggestion
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
it + 1;
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
1 + it;
```

https://github.com/llvm/llvm-project/pull/209289

From libcxx-commits at lists.llvm.org  Fri Jul 17 09:53:03 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 09:53:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add sentinel
 `OtherConst` cases for `[[nodiscard]]` tests in `enumerate_view` (PR
 #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5a5def.c5504292.78ef3.8d65@mx.google.com>


https://github.com/Zingam updated https://github.com/llvm/llvm-project/pull/209289

>From de6bef4e4ef9069b23edba3c557e7d576fc78e4a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 12 Jul 2026 18:32:39 +0200
Subject: [PATCH 1/8] add: OtherConst test to sentinel operator-

---
 .../range.enumerate/nodiscard.verify.cpp               | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..d6d93219a4e14 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,7 +64,8 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st = ev.end();
+  auto st   = ev.end();
+  auto c_it = std::as_const(ev).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -74,8 +75,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}

>From bcb2d6392e68580206412414a24029dee3c70881 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:20:42 +0200
Subject: [PATCH 2/8] ref: model non-common non-simple view for sentinel tests
 and general adjustments

---
 .../range.enumerate/nodiscard.verify.cpp      | 38 ++++++++++++-------
 1 file changed, 25 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index d6d93219a4e14..fea1bca7c2145 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,32 +14,41 @@
 #include <utility>
 #include <vector>
 
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
 void test() {
-  std::vector<int> range;
-  std::ranges::enumerate_view ev{range};
+  auto v  = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.begin();
+  v.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).begin();
+  std::as_const(v).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.end();
+  v.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).end();
+  std::as_const(v).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.size();
+  v.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).size();
+  std::as_const(v).size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.base();
+  v.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::move(ev).base();
+  std::move(v).base();
 
   // [range.enumerate.iterator]
 
-  auto it = ev.begin();
+  auto it = v.begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(it).base();
@@ -64,8 +73,7 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st   = ev.end();
-  auto c_it = std::as_const(ev).begin();
+  auto st = v.end();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -75,6 +83,8 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  auto c_it = std::as_const(v).begin();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - c_it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -82,6 +92,8 @@ void test() {
 
   // [range.enumerate.overview]
 
+  std::vector<int> range { 1, 2, 3 };
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
 }

>From 02096b8044eb8b37db4a9cdee0c35da7d297574c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:25:17 +0200
Subject: [PATCH 3/8] fix: clang-format

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index fea1bca7c2145..4ebb6196c8d9c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,7 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v  = View{} | std::views::enumerate;
+  auto v = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
@@ -92,7 +92,7 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range { 1, 2, 3 };
+  std::vector<int> range{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);

>From 9fda31d2eb80d645f56214599f544ebccb8b724a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:09:56 +0200
Subject: [PATCH 4/8] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 4ebb6196c8d9c..b2ce7f3418fe6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,8 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v = View{} | std::views::enumerate;
+  View range;
+  auto v = std::ranges::enumerate_view ev{range};
 
   // [range.enumerate.view]
 

>From 02235acb82379ce5c0bf8011275e5129effbf3ad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:11:36 +0200
Subject: [PATCH 5/8] rename range variable to vec

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index b2ce7f3418fe6..250b21e3e9910 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -93,8 +93,8 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range{1, 2, 3};
+  std::vector<int> vec{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(range);
+  std::views::enumerate(vec);
 }

>From 4ed811e010d332be499f091c05e411f3f88349a0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:16:13 +0200
Subject: [PATCH 6/8] fix: wrong variable initialization

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp    | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 250b21e3e9910..c10e56293246f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -12,7 +12,6 @@
 
 #include <ranges>
 #include <utility>
-#include <vector>
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -26,7 +25,7 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
   View range;
-  auto v = std::ranges::enumerate_view ev{range};
+  std::ranges::enumerate_view v{range};
 
   // [range.enumerate.view]
 
@@ -93,8 +92,6 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> vec{1, 2, 3};
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(vec);
+  std::views::enumerate(range);
 }

>From 214a924d09e6d0d60b8531e2da1422e69c90eae1 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 15:20:49 +0200
Subject: [PATCH 7/8] remove: c_it - st and st - c_st tests. Add static_asserts

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp  | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index c10e56293246f..4f99f0842dd11 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -22,6 +22,8 @@ struct View : std::ranges::view_interface<View> {
 static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::sized_range<View>);
+static_assert(std::ranges::forward_range<View>);
 
 void test() {
   View range;
@@ -83,13 +85,6 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
-  auto c_it = std::as_const(v).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - c_it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  c_it - st;
-
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}

>From e28782a46f3d2c7d612a978652f59dd325655423 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Fri, 17 Jul 2026 19:52:52 +0300
Subject: [PATCH 8/8] Apply suggestion from @Zingam

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp      | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 4f99f0842dd11..16b2340b8357d 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,7 +64,10 @@ void test() {
   std::as_const(it)[2];
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
+// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+it + 1;
+// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+1 + it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   it - 1;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}


From libcxx-commits at lists.llvm.org  Fri Jul 17 09:53:39 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 09:53:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add sentinel
 `OtherConst` cases for `[[nodiscard]]` tests in `enumerate_view` (PR
 #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5a5e13.6c5720b3.2da3b9.47cc@mx.google.com>


================
@@ -53,7 +64,10 @@ void test() {
   std::as_const(it)[2];
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
+// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+it + 1;
+// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+1 + it;
----------------
Zingam wrote:

```suggestion
  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
  it + 1;
  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
  1 + it;
```

https://github.com/llvm/llvm-project/pull/209289

From libcxx-commits at lists.llvm.org  Fri Jul 17 09:53:48 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 09:53:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add sentinel
 `OtherConst` cases for `[[nodiscard]]` tests in `enumerate_view` (PR
 #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5a5e1c.7d702ed2.13d102.b05b@mx.google.com>


https://github.com/Zingam updated https://github.com/llvm/llvm-project/pull/209289

>From de6bef4e4ef9069b23edba3c557e7d576fc78e4a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 12 Jul 2026 18:32:39 +0200
Subject: [PATCH 1/9] add: OtherConst test to sentinel operator-

---
 .../range.enumerate/nodiscard.verify.cpp               | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..d6d93219a4e14 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,7 +64,8 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st = ev.end();
+  auto st   = ev.end();
+  auto c_it = std::as_const(ev).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -74,8 +75,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}

>From bcb2d6392e68580206412414a24029dee3c70881 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:20:42 +0200
Subject: [PATCH 2/9] ref: model non-common non-simple view for sentinel tests
 and general adjustments

---
 .../range.enumerate/nodiscard.verify.cpp      | 38 ++++++++++++-------
 1 file changed, 25 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index d6d93219a4e14..fea1bca7c2145 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,32 +14,41 @@
 #include <utility>
 #include <vector>
 
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
 void test() {
-  std::vector<int> range;
-  std::ranges::enumerate_view ev{range};
+  auto v  = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.begin();
+  v.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).begin();
+  std::as_const(v).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.end();
+  v.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).end();
+  std::as_const(v).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.size();
+  v.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).size();
+  std::as_const(v).size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.base();
+  v.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::move(ev).base();
+  std::move(v).base();
 
   // [range.enumerate.iterator]
 
-  auto it = ev.begin();
+  auto it = v.begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(it).base();
@@ -64,8 +73,7 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st   = ev.end();
-  auto c_it = std::as_const(ev).begin();
+  auto st = v.end();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -75,6 +83,8 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  auto c_it = std::as_const(v).begin();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - c_it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -82,6 +92,8 @@ void test() {
 
   // [range.enumerate.overview]
 
+  std::vector<int> range { 1, 2, 3 };
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
 }

>From 02096b8044eb8b37db4a9cdee0c35da7d297574c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:25:17 +0200
Subject: [PATCH 3/9] fix: clang-format

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index fea1bca7c2145..4ebb6196c8d9c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,7 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v  = View{} | std::views::enumerate;
+  auto v = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
@@ -92,7 +92,7 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range { 1, 2, 3 };
+  std::vector<int> range{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);

>From 9fda31d2eb80d645f56214599f544ebccb8b724a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:09:56 +0200
Subject: [PATCH 4/9] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 4ebb6196c8d9c..b2ce7f3418fe6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,8 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v = View{} | std::views::enumerate;
+  View range;
+  auto v = std::ranges::enumerate_view ev{range};
 
   // [range.enumerate.view]
 

>From 02235acb82379ce5c0bf8011275e5129effbf3ad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:11:36 +0200
Subject: [PATCH 5/9] rename range variable to vec

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index b2ce7f3418fe6..250b21e3e9910 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -93,8 +93,8 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range{1, 2, 3};
+  std::vector<int> vec{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(range);
+  std::views::enumerate(vec);
 }

>From 4ed811e010d332be499f091c05e411f3f88349a0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:16:13 +0200
Subject: [PATCH 6/9] fix: wrong variable initialization

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp    | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 250b21e3e9910..c10e56293246f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -12,7 +12,6 @@
 
 #include <ranges>
 #include <utility>
-#include <vector>
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -26,7 +25,7 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
   View range;
-  auto v = std::ranges::enumerate_view ev{range};
+  std::ranges::enumerate_view v{range};
 
   // [range.enumerate.view]
 
@@ -93,8 +92,6 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> vec{1, 2, 3};
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(vec);
+  std::views::enumerate(range);
 }

>From 214a924d09e6d0d60b8531e2da1422e69c90eae1 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 15:20:49 +0200
Subject: [PATCH 7/9] remove: c_it - st and st - c_st tests. Add static_asserts

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp  | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index c10e56293246f..4f99f0842dd11 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -22,6 +22,8 @@ struct View : std::ranges::view_interface<View> {
 static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::sized_range<View>);
+static_assert(std::ranges::forward_range<View>);
 
 void test() {
   View range;
@@ -83,13 +85,6 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
-  auto c_it = std::as_const(v).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - c_it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  c_it - st;
-
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}

>From e28782a46f3d2c7d612a978652f59dd325655423 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Fri, 17 Jul 2026 19:52:52 +0300
Subject: [PATCH 8/9] Apply suggestion from @Zingam

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp      | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 4f99f0842dd11..16b2340b8357d 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,7 +64,10 @@ void test() {
   std::as_const(it)[2];
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
+// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+it + 1;
+// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+1 + it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   it - 1;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}

>From a2b5b74e069d1b7c24e1419410550c01dc32ce92 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Fri, 17 Jul 2026 19:53:35 +0300
Subject: [PATCH 9/9] Apply suggestion from @Zingam

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp   | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 16b2340b8357d..18cd3b4247fba 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,10 +64,10 @@ void test() {
   std::as_const(it)[2];
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-it + 1;
-// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   it - 1;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}


From libcxx-commits at lists.llvm.org  Fri Jul 17 09:56:33 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 09:56:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add sentinel
 `OtherConst` cases for `[[nodiscard]]` tests in `enumerate_view` (PR
 #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5a5ec1.d2cb4adc.ccd80.e1e6@mx.google.com>


================
@@ -52,9 +63,12 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(it)[2];
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
----------------
Zingam wrote:

```suggestion
```

https://github.com/llvm/llvm-project/pull/209289

From libcxx-commits at lists.llvm.org  Fri Jul 17 09:56:37 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 09:56:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add sentinel
 `OtherConst` cases for `[[nodiscard]]` tests in `enumerate_view` (PR
 #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5a5ec5.13be6df8.22eac4.7e14@mx.google.com>


https://github.com/Zingam updated https://github.com/llvm/llvm-project/pull/209289

>From de6bef4e4ef9069b23edba3c557e7d576fc78e4a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 12 Jul 2026 18:32:39 +0200
Subject: [PATCH 01/10] add: OtherConst test to sentinel operator-

---
 .../range.enumerate/nodiscard.verify.cpp               | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..d6d93219a4e14 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,7 +64,8 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st = ev.end();
+  auto st   = ev.end();
+  auto c_it = std::as_const(ev).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -74,8 +75,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}

>From bcb2d6392e68580206412414a24029dee3c70881 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:20:42 +0200
Subject: [PATCH 02/10] ref: model non-common non-simple view for sentinel
 tests and general adjustments

---
 .../range.enumerate/nodiscard.verify.cpp      | 38 ++++++++++++-------
 1 file changed, 25 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index d6d93219a4e14..fea1bca7c2145 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,32 +14,41 @@
 #include <utility>
 #include <vector>
 
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
 void test() {
-  std::vector<int> range;
-  std::ranges::enumerate_view ev{range};
+  auto v  = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.begin();
+  v.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).begin();
+  std::as_const(v).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.end();
+  v.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).end();
+  std::as_const(v).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.size();
+  v.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).size();
+  std::as_const(v).size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.base();
+  v.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::move(ev).base();
+  std::move(v).base();
 
   // [range.enumerate.iterator]
 
-  auto it = ev.begin();
+  auto it = v.begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(it).base();
@@ -64,8 +73,7 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st   = ev.end();
-  auto c_it = std::as_const(ev).begin();
+  auto st = v.end();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -75,6 +83,8 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  auto c_it = std::as_const(v).begin();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - c_it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -82,6 +92,8 @@ void test() {
 
   // [range.enumerate.overview]
 
+  std::vector<int> range { 1, 2, 3 };
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
 }

>From 02096b8044eb8b37db4a9cdee0c35da7d297574c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:25:17 +0200
Subject: [PATCH 03/10] fix: clang-format

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index fea1bca7c2145..4ebb6196c8d9c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,7 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v  = View{} | std::views::enumerate;
+  auto v = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
@@ -92,7 +92,7 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range { 1, 2, 3 };
+  std::vector<int> range{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);

>From 9fda31d2eb80d645f56214599f544ebccb8b724a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:09:56 +0200
Subject: [PATCH 04/10] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 4ebb6196c8d9c..b2ce7f3418fe6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,8 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v = View{} | std::views::enumerate;
+  View range;
+  auto v = std::ranges::enumerate_view ev{range};
 
   // [range.enumerate.view]
 

>From 02235acb82379ce5c0bf8011275e5129effbf3ad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:11:36 +0200
Subject: [PATCH 05/10] rename range variable to vec

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index b2ce7f3418fe6..250b21e3e9910 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -93,8 +93,8 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range{1, 2, 3};
+  std::vector<int> vec{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(range);
+  std::views::enumerate(vec);
 }

>From 4ed811e010d332be499f091c05e411f3f88349a0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:16:13 +0200
Subject: [PATCH 06/10] fix: wrong variable initialization

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp    | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 250b21e3e9910..c10e56293246f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -12,7 +12,6 @@
 
 #include <ranges>
 #include <utility>
-#include <vector>
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -26,7 +25,7 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
   View range;
-  auto v = std::ranges::enumerate_view ev{range};
+  std::ranges::enumerate_view v{range};
 
   // [range.enumerate.view]
 
@@ -93,8 +92,6 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> vec{1, 2, 3};
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(vec);
+  std::views::enumerate(range);
 }

>From 214a924d09e6d0d60b8531e2da1422e69c90eae1 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 15:20:49 +0200
Subject: [PATCH 07/10] remove: c_it - st and st - c_st tests. Add
 static_asserts

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp  | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index c10e56293246f..4f99f0842dd11 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -22,6 +22,8 @@ struct View : std::ranges::view_interface<View> {
 static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::sized_range<View>);
+static_assert(std::ranges::forward_range<View>);
 
 void test() {
   View range;
@@ -83,13 +85,6 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
-  auto c_it = std::as_const(v).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - c_it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  c_it - st;
-
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}

>From e28782a46f3d2c7d612a978652f59dd325655423 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Fri, 17 Jul 2026 19:52:52 +0300
Subject: [PATCH 08/10] Apply suggestion from @Zingam

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp      | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 4f99f0842dd11..16b2340b8357d 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,7 +64,10 @@ void test() {
   std::as_const(it)[2];
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
+// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+it + 1;
+// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+1 + it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   it - 1;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}

>From a2b5b74e069d1b7c24e1419410550c01dc32ce92 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Fri, 17 Jul 2026 19:53:35 +0300
Subject: [PATCH 09/10] Apply suggestion from @Zingam

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp   | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 16b2340b8357d..18cd3b4247fba 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,10 +64,10 @@ void test() {
   std::as_const(it)[2];
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-it + 1;
-// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   it - 1;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}

>From 8902bfa9d6b98e5f7715e4d9639bc96ed16814aa Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Fri, 17 Jul 2026 19:56:24 +0300
Subject: [PATCH 10/10] Apply suggestion from @Zingam

---
 .../ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 18cd3b4247fba..19b9731cd2b69 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -63,7 +63,6 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(it)[2];
 
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   it + 1;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}


From libcxx-commits at lists.llvm.org  Fri Jul 17 09:59:54 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 09:59:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add missing test cases
 for `[[nodiscard]]` tests in `enumerate_view` (PR #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5a5f8a.af6ba3ed.3c8c03.dbf9@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/209289

From libcxx-commits at lists.llvm.org  Fri Jul 17 10:00:09 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 10:00:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add missing test cases
 for `[[nodiscard]]` tests in `enumerate_view` (PR #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5a5f99.9edb5c9d.280be3.70cd@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/209289

From libcxx-commits at lists.llvm.org  Fri Jul 17 10:03:19 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 17 Jul 2026 10:03:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update release runners to the
 image that was used on main at the branch point (PR #210378)
Message-ID: <llvm.org/llvm/llvm-project/pull/210378@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/210378

This will allow retaining a stable image for CI throughout the whole LLVM 23 release.

>From 1d12c0b6839240896b55adfa73e47aab797b2262 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 17 Jul 2026 13:01:38 -0400
Subject: [PATCH] [libc++] Update release runners to the image that was used on
 main at the branch point

This will allow retaining a stable image for CI throughout the whole
LLVM 23 release.
---
 libcxx/utils/ci/images/libcxx_release_runners.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/utils/ci/images/libcxx_release_runners.txt b/libcxx/utils/ci/images/libcxx_release_runners.txt
index 86bd8b3a937f9..b593254d1cf01 100644
--- a/libcxx/utils/ci/images/libcxx_release_runners.txt
+++ b/libcxx/utils/ci/images/libcxx_release_runners.txt
@@ -1 +1 @@
-ghcr.io/llvm/libcxx-linux-builder:e8753c0de90d3aaa60361b5354588fa29fef9228
+ghcr.io/llvm/libcxx-linux-builder:6d3cf7f436a01f4622fb660e413a4020209777b8


From libcxx-commits at lists.llvm.org  Fri Jul 17 10:04:47 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Fri, 17 Jul 2026 10:04:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update release runners to the
 image that was used on main at the branch point (PR #210378)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210378@github.com>
Message-ID: <6a5a60af.510f576d.2f694c.f764@mx.google.com>


https://github.com/boomanaiden154 approved this pull request.

LGTM

https://github.com/llvm/llvm-project/pull/210378

From libcxx-commits at lists.llvm.org  Fri Jul 17 10:07:26 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 17 Jul 2026 10:07:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update release runners to the
 image that was used on main at the branch point (PR #210378)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210378@github.com>
Message-ID: <6a5a614e.66c0679a.24976c.670c@mx.google.com>


ldionne wrote:

@boomanaiden154 can you let me know once you've made the deployment?

https://github.com/llvm/llvm-project/pull/210378

From libcxx-commits at lists.llvm.org  Fri Jul 17 10:09:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 10:09:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update release runners to the
 image that was used on main at the branch point (PR #210378)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210378@github.com>
Message-ID: <6a5a61c6.7270d2fd.a9bd6.4f09@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

This will allow retaining a stable image for CI throughout the whole LLVM 23 release.

---
Full diff: https://github.com/llvm/llvm-project/pull/210378.diff


1 Files Affected:

- (modified) libcxx/utils/ci/images/libcxx_release_runners.txt (+1-1) 


``````````diff
diff --git a/libcxx/utils/ci/images/libcxx_release_runners.txt b/libcxx/utils/ci/images/libcxx_release_runners.txt
index 86bd8b3a937f9..b593254d1cf01 100644
--- a/libcxx/utils/ci/images/libcxx_release_runners.txt
+++ b/libcxx/utils/ci/images/libcxx_release_runners.txt
@@ -1 +1 @@
-ghcr.io/llvm/libcxx-linux-builder:e8753c0de90d3aaa60361b5354588fa29fef9228
+ghcr.io/llvm/libcxx-linux-builder:6d3cf7f436a01f4622fb660e413a4020209777b8

``````````

</details>


https://github.com/llvm/llvm-project/pull/210378

From libcxx-commits at lists.llvm.org  Fri Jul 17 10:10:05 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 10:10:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 53c048b - [libc++] Update release runners
 to the image that was used on main at the branch point (#210378)
Message-ID: <6a5a61ed.e624eb8d.4a8a7.4e89@mx.google.com>


Author: Louis Dionne
Date: 2026-07-17T13:10:00-04:00
New Revision: 53c048b3732610c8d2d65c1d41603de26eb6ac4e

URL: https://github.com/llvm/llvm-project/commit/53c048b3732610c8d2d65c1d41603de26eb6ac4e
DIFF: https://github.com/llvm/llvm-project/commit/53c048b3732610c8d2d65c1d41603de26eb6ac4e.diff

LOG: [libc++] Update release runners to the image that was used on main at the branch point (#210378)

This will allow retaining a stable image for CI throughout the whole
LLVM 23 release.

Added: 
    

Modified: 
    libcxx/utils/ci/images/libcxx_release_runners.txt

Removed: 
    


################################################################################
diff  --git a/libcxx/utils/ci/images/libcxx_release_runners.txt b/libcxx/utils/ci/images/libcxx_release_runners.txt
index 86bd8b3a937f9..b593254d1cf01 100644
--- a/libcxx/utils/ci/images/libcxx_release_runners.txt
+++ b/libcxx/utils/ci/images/libcxx_release_runners.txt
@@ -1 +1 @@
-ghcr.io/llvm/libcxx-linux-builder:e8753c0de90d3aaa60361b5354588fa29fef9228
+ghcr.io/llvm/libcxx-linux-builder:6d3cf7f436a01f4622fb660e413a4020209777b8


        

From libcxx-commits at lists.llvm.org  Fri Jul 17 10:10:07 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 17 Jul 2026 10:10:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update release runners to the
 image that was used on main at the branch point (PR #210378)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210378@github.com>
Message-ID: <6a5a61ef.743400b0.23a7b2.3205@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/210378

From libcxx-commits at lists.llvm.org  Fri Jul 17 10:15:06 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Fri, 17 Jul 2026 10:15:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update release runners to the
 image that was used on main at the branch point (PR #210378)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210378@github.com>
Message-ID: <6a5a631a.4a342cbc.3a2f36.41c5@mx.google.com>


boomanaiden154 wrote:

Deployed.

https://github.com/llvm/llvm-project/pull/210378

From libcxx-commits at lists.llvm.org  Fri Jul 17 10:22:51 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 17 Jul 2026 10:22:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update release procedure for
	libc++ & friends (PR #210383)
Message-ID: <llvm.org/llvm/llvm-project/pull/210383@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/210383

The release procedure implied that some tasks were done by the release manager, but in practice they are done by the libc++ developers.

Also, mention using the `llvm-premerge-libcxx-release-runners` runner set on the release branch, which was overlooked in the previous notes.

Other than that, minor reformulations.

>From e99095033fef86d92af6aa35bbc049f1ca63077c Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 17 Jul 2026 13:20:04 -0400
Subject: [PATCH] [libc++] Update release procedure for libc++ & friends

The release procedure implied that some tasks were done by the release
manager, but in practice they are done by the libc++ developers.

Also, mention using the `llvm-premerge-libcxx-release-runners` runner
set on the release branch, which was overlooked in the previous notes.

Other than that, minor reformulations.
---
 libcxx/docs/ReleaseProcedure.rst | 38 ++++++++++++--------------------
 1 file changed, 14 insertions(+), 24 deletions(-)

diff --git a/libcxx/docs/ReleaseProcedure.rst b/libcxx/docs/ReleaseProcedure.rst
index b3f0d129a17fb..2408f9b4b808d 100644
--- a/libcxx/docs/ReleaseProcedure.rst
+++ b/libcxx/docs/ReleaseProcedure.rst
@@ -11,8 +11,6 @@ This page describes the libc++ procedure for that release.
 Prepare the release
 ===================
 
-This is done by the libc++ developers.
-
 It should be finished before the Release managers start branching the new
 release:
 
@@ -37,10 +35,8 @@ release:
 * Make sure all libc++ supported compilers in the CI are updated to their
   latest release.
 
-Branching
-=========
-
-This is done by the LLVM Release managers.
+After the branch is created
+===========================
 
 After branching for an LLVM release:
 
@@ -48,28 +44,22 @@ After branching for an LLVM release:
 2. Update the version number in ``libcxx/docs/conf.py``
 3. Update ``_LIBCPPABI_VERSION`` in ``libcxxabi/include/cxxabi.h``
 4. Update ``_LIBUNWIND_VERSION`` in ``libunwind/include/__libunwind_config.h``
-5. Create a release notes file for the next release from the template
-6. Point to the new release notes file from ``libcxx/docs/ReleaseNotes.rst``
-
-Post branching
-==============
-
-This is done by the libc++ developers.
-
-After branching it takes a couple of days before the new LLVM ToT version is
-available on `<https://apt.llvm.org>`_. Once it is available the pre-commit CI
-can start using the new ToT version. In order to make sure patches can be
-backported to the release branch the oldest compiler is not removed yet.
-
-The section ``Upcoming Deprecations and Removals`` is cleared by the release
-managers. Copy back the items that were in this section.
+5. Create a release notes file for the next release from the previous ones and point to it from
+   ``libcxx/docs/ReleaseNotes.rst``. Remove entries that do not apply anymore, but keep in mind
+   that some entries (such as upcoming deprecations) may still apply, may need rewording and may
+   also require follow up PRs to implement.
+6. Update the set of runners targeted by the CI on the release branch to ``llvm-premerge-libcxx-release-runners``, and
+   make sure that runner set is using the appropriate image. This ensures that the release branch CI keeps working even
+   if the main branch starts using newer images.
+7. It takes a couple of days before the new LLVM ToT version is available on `<https://apt.llvm.org>`_ after a
+   branch is created. Once it is available, the pre-commit CI can be updated to use the new ToT version. In order
+   to make sure patches can be backported to the release branch, we don't remove the oldest compiler yet.
 
 Post release
 ============
 
-This is done by the libc++ developers.
-
-Support for the ToT - 3 version is removed:
+Once the release is done and cherry-picks are not expected, we remove support for the ToT - 3 version Clang.
+We also perform associated cleanups:
 
 - Search for ``LLVM RELEASE`` and address their comments
 - Search for test that have ``UNSUPPORTED`` or ``XFAIL`` for the no longer supported version


From libcxx-commits at lists.llvm.org  Fri Jul 17 10:23:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 10:23:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update release procedure for
	libc++ & friends (PR #210383)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210383@github.com>
Message-ID: <6a5a6519.510f576d.2f694c.8beb@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

The release procedure implied that some tasks were done by the release manager, but in practice they are done by the libc++ developers.

Also, mention using the `llvm-premerge-libcxx-release-runners` runner set on the release branch, which was overlooked in the previous notes.

Other than that, minor reformulations.

---
Full diff: https://github.com/llvm/llvm-project/pull/210383.diff


1 Files Affected:

- (modified) libcxx/docs/ReleaseProcedure.rst (+14-24) 


``````````diff
diff --git a/libcxx/docs/ReleaseProcedure.rst b/libcxx/docs/ReleaseProcedure.rst
index b3f0d129a17fb..2408f9b4b808d 100644
--- a/libcxx/docs/ReleaseProcedure.rst
+++ b/libcxx/docs/ReleaseProcedure.rst
@@ -11,8 +11,6 @@ This page describes the libc++ procedure for that release.
 Prepare the release
 ===================
 
-This is done by the libc++ developers.
-
 It should be finished before the Release managers start branching the new
 release:
 
@@ -37,10 +35,8 @@ release:
 * Make sure all libc++ supported compilers in the CI are updated to their
   latest release.
 
-Branching
-=========
-
-This is done by the LLVM Release managers.
+After the branch is created
+===========================
 
 After branching for an LLVM release:
 
@@ -48,28 +44,22 @@ After branching for an LLVM release:
 2. Update the version number in ``libcxx/docs/conf.py``
 3. Update ``_LIBCPPABI_VERSION`` in ``libcxxabi/include/cxxabi.h``
 4. Update ``_LIBUNWIND_VERSION`` in ``libunwind/include/__libunwind_config.h``
-5. Create a release notes file for the next release from the template
-6. Point to the new release notes file from ``libcxx/docs/ReleaseNotes.rst``
-
-Post branching
-==============
-
-This is done by the libc++ developers.
-
-After branching it takes a couple of days before the new LLVM ToT version is
-available on `<https://apt.llvm.org>`_. Once it is available the pre-commit CI
-can start using the new ToT version. In order to make sure patches can be
-backported to the release branch the oldest compiler is not removed yet.
-
-The section ``Upcoming Deprecations and Removals`` is cleared by the release
-managers. Copy back the items that were in this section.
+5. Create a release notes file for the next release from the previous ones and point to it from
+   ``libcxx/docs/ReleaseNotes.rst``. Remove entries that do not apply anymore, but keep in mind
+   that some entries (such as upcoming deprecations) may still apply, may need rewording and may
+   also require follow up PRs to implement.
+6. Update the set of runners targeted by the CI on the release branch to ``llvm-premerge-libcxx-release-runners``, and
+   make sure that runner set is using the appropriate image. This ensures that the release branch CI keeps working even
+   if the main branch starts using newer images.
+7. It takes a couple of days before the new LLVM ToT version is available on `<https://apt.llvm.org>`_ after a
+   branch is created. Once it is available, the pre-commit CI can be updated to use the new ToT version. In order
+   to make sure patches can be backported to the release branch, we don't remove the oldest compiler yet.
 
 Post release
 ============
 
-This is done by the libc++ developers.
-
-Support for the ToT - 3 version is removed:
+Once the release is done and cherry-picks are not expected, we remove support for the ToT - 3 version Clang.
+We also perform associated cleanups:
 
 - Search for ``LLVM RELEASE`` and address their comments
 - Search for test that have ``UNSUPPORTED`` or ``XFAIL`` for the no longer supported version

``````````

</details>


https://github.com/llvm/llvm-project/pull/210383

From libcxx-commits at lists.llvm.org  Fri Jul 17 10:30:03 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 10:30:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix missing 'get_new_handler()'
 for Windows builds (PR #150182)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/150182@github.com>
Message-ID: <6a5a669b.0c05e68e.3c2f5d.ac30@mx.google.com>


mccakit wrote:

@ping

https://github.com/llvm/llvm-project/pull/150182

From libcxx-commits at lists.llvm.org  Fri Jul 17 10:30:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 10:30:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix missing 'get_new_handler()'
 for Windows builds (PR #150182)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/150182@github.com>
Message-ID: <6a5a66b4.9edb5c9d.280be3.5bce@mx.google.com>


mccakit wrote:

ping @philnik777

https://github.com/llvm/llvm-project/pull/150182

From libcxx-commits at lists.llvm.org  Fri Jul 17 10:49:22 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 10:49:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3052R2:
	`view_interface::at()` (PR #205012)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205012@github.com>
Message-ID: <6a5a6b22.eb165e5c.4506c.2930@mx.google.com>


https://github.com/Zingam updated https://github.com/llvm/llvm-project/pull/205012

>From b868e5e71f1cca8374170bcc4cf60789344edaab Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sun, 21 Jun 2026 23:52:40 +0300
Subject: [PATCH] [libc++][ranges] P3052R2: `view_interface::at()`

Closes #204395

Implements https://wg21.link/P3052R2
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/Status/Cxx29Papers.csv            |   2 +-
 libcxx/include/__ranges/view_interface.h      |  26 +++
 libcxx/include/version                        |   2 +
 .../view.interface/nodiscard.verify.cpp       |   7 +
 .../test/libcxx/transitive_includes/cxx23.csv |   1 +
 .../test/libcxx/transitive_includes/cxx26.csv |   1 +
 .../ranges.version.compile.pass.cpp           |  33 ++++
 .../version.version.compile.pass.cpp          |  33 ++++
 .../view.interface/view.interface.pass.cpp    | 149 ++++++++++++------
 .../generate_feature_test_macro_components.py |   7 +
 11 files changed, 214 insertions(+), 49 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..82d0910a7d7e1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -308,6 +308,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_variant``                                      ``202106L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_view_interface``                               ``202606L``
+    ---------------------------------------------------------- -----------------
     **C++23**
     ----------------------------------------------------------------------------
     ``__cpp_lib_adaptor_iterator_pair_constructor``            ``202106L``
diff --git a/libcxx/docs/Status/Cxx29Papers.csv b/libcxx/docs/Status/Cxx29Papers.csv
index 3c48fe89be33d..656eaf5df44df 100644
--- a/libcxx/docs/Status/Cxx29Papers.csv
+++ b/libcxx/docs/Status/Cxx29Papers.csv
@@ -4,7 +4,7 @@
 "`P2414R12 <https://wg21.link/P2414R12>`__","Pointer lifetime-end zap proposed solutions","2026-06 (Brno)","","","`#204392 <https://github.com/llvm/llvm-project/issues/204392>`__","Voted as a Defect Report."
 "`P3319R6 <https://wg21.link/P3319R6>`__","Add an ``iota`` object for ``simd`` (and more)","2026-06 (Brno)","","","`#204393 <https://github.com/llvm/llvm-project/issues/204393>`__",""
 "`P3798R1 <https://wg21.link/P3798R1>`__","The unexpected in ``std::expected``","2026-06 (Brno)","","","`#204394 <https://github.com/llvm/llvm-project/issues/204394>`__",""
-"`P3052R2 <https://wg21.link/P3052R2>`__","``view_interface::at()``","2026-06 (Brno)","","","`#204395 <https://github.com/llvm/llvm-project/issues/204395>`__",""
+"`P3052R2 <https://wg21.link/P3052R2>`__","``view_interface::at()``","2026-06 (Brno)","|Complete|","23","`#204395 <https://github.com/llvm/llvm-project/issues/204395>`__",""
 "`P4206R0 <https://wg21.link/P4206R0>`__","Revert string support in ``std::constant_wrapper``","2026-06 (Brno)","|Complete|","23","`#203336 <https://github.com/llvm/llvm-project/issues/203336>`__","Applied as a Defect Report."
 "`P3395R6 <https://wg21.link/P3395R6>`__","Fix encoding issues and add a formatter for ``std::error_code``","2026-06 (Brno)","","","`#204396 <https://github.com/llvm/llvm-project/issues/204396>`__",""
 "`P3505R4 <https://wg21.link/P3505R4>`__","Fix the default floating-point representation in ``std::format``","2026-06 (Brno)","","","`#204397 <https://github.com/llvm/llvm-project/issues/204397>`__","To be applied as a Defect Report."
diff --git a/libcxx/include/__ranges/view_interface.h b/libcxx/include/__ranges/view_interface.h
index 96508c2be7277..c5875d024840e 100644
--- a/libcxx/include/__ranges/view_interface.h
+++ b/libcxx/include/__ranges/view_interface.h
@@ -15,6 +15,7 @@
 #include <__concepts/same_as.h>
 #include <__config>
 #include <__iterator/concepts.h>
+#include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/prev.h>
 #include <__memory/pointer_traits.h>
@@ -25,6 +26,7 @@
 #include <__type_traits/is_class.h>
 #include <__type_traits/make_unsigned.h>
 #include <__type_traits/remove_cv.h>
+#include <stdexcept>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -159,8 +161,32 @@ class view_interface {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](range_difference_t<_RARange> __index) const {
     return ranges::begin(__derived())[__index];
   }
+
+  #if _LIBCPP_STD_VER >= 29
+
+  template <random_access_range _RARange = _Derived>
+    requires sized_range<_RARange> // freestanding-deleted
+  [[nodiscard]] constexpr decltype(auto) at(range_difference_t<_RARange> __index) {
+    if (__index < 0 || __index >= ranges::distance(__derived())) {
+      std::__throw_out_of_range(
+          "Precondition `0 <= __index < distance()` not satisfied. `.at(__index)` called with out-of-bounds index.");
+    }
+    return (*this)[__index];
+  }
+
+  template <random_access_range _RARange = const _Derived>
+    requires sized_range<_RARange> // freestanding-deleted
+  [[nodiscard]] constexpr decltype(auto) at(range_difference_t<_RARange> __index) const {
+    if (__index < 0 || __index >= ranges::distance(__derived())) {
+      std::__throw_out_of_range(
+          "Precondition `0 <= __index < distance()` not satisfied. `.at(__index)` called with out-of-bounds index.");
+    }
+    return (*this)[__index];
+  }
 };
 
+#endif // _LIBCPP_STD_VER >= 29
+
 } // namespace ranges
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..94185b7602f64 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -290,6 +290,7 @@ __cpp_lib_unwrap_ref                                    201811L <functional>
 __cpp_lib_variant                                       202306L <variant>
                                                         202106L // C++20
                                                         202102L // C++17
+__cpp_lib_view_interface                                202606L <ranges>
 __cpp_lib_void_t                                        201411L <type_traits>
 
 */
@@ -488,6 +489,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_unwrap_ref                           201811L
 # undef  __cpp_lib_variant
 # define __cpp_lib_variant                              202106L
+# define __cpp_lib_view_interface                       202606L
 #endif
 
 #if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
index a22c03f5d1a71..2bccc170aa727 100644
--- a/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
@@ -67,4 +67,11 @@ void test() {
   v[Diff{0}];
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v)[Diff{0}];
+
+#if TEST_STD_VER >= 29
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.at(Diff{0});
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).at(Diff{0});
+#endif
 }
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 53ae6c9b4e591..260c410680e39 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -11,6 +11,7 @@ algorithm iosfwd
 algorithm limits
 algorithm optional
 algorithm ratio
+algorithm stdexcept
 algorithm tuple
 algorithm version
 any cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index f712f36caa9fc..6cc4737bff91b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -11,6 +11,7 @@ algorithm iosfwd
 algorithm limits
 algorithm optional
 algorithm ratio
+algorithm stdexcept
 algorithm tuple
 algorithm version
 any cstdint
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 670b0e664721a..f42c7ede894b5 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -80,6 +80,10 @@
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #elif TEST_STD_VER == 14
 
 #  ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -142,6 +146,10 @@
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #elif TEST_STD_VER == 17
 
 #  ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -204,6 +212,10 @@
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #elif TEST_STD_VER == 20
 
 #  ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -269,6 +281,13 @@
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++20"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++20"
+#  endif
+
 #elif TEST_STD_VER == 23
 
 #  ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -385,6 +404,13 @@
 #    error "__cpp_lib_ranges_zip should have the value 202110L in c++23"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++23"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++23"
+#  endif
+
 #elif TEST_STD_VER > 23
 
 #  if !defined(_LIBCPP_VERSION)
@@ -516,6 +542,13 @@
 #    error "__cpp_lib_ranges_zip should have the value 202110L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++26"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++26"
+#  endif
+
 #endif // TEST_STD_VER > 23
 
 // clang-format on
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 f2d74cc99d864..79e61725a93fc 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
@@ -948,6 +948,10 @@
 #    error "__cpp_lib_variant should not be defined before c++17"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #  ifdef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should not be defined before c++17"
 #  endif
@@ -1954,6 +1958,10 @@
 #    error "__cpp_lib_variant should not be defined before c++17"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #  ifdef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should not be defined before c++17"
 #  endif
@@ -3149,6 +3157,10 @@
 #    error "__cpp_lib_variant should have the value 202102L in c++17"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #  ifndef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should be defined in c++17"
 #  endif
@@ -4611,6 +4623,13 @@
 #    error "__cpp_lib_variant should have the value 202106L in c++20"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++20"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++20"
+#  endif
+
 #  ifndef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should be defined in c++20"
 #  endif
@@ -6301,6 +6320,13 @@
 #    error "__cpp_lib_variant should have the value 202106L in c++23"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++23"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++23"
+#  endif
+
 #  ifndef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should be defined in c++23"
 #  endif
@@ -8324,6 +8350,13 @@
 #    error "__cpp_lib_variant should have the value 202306L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++26"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
index bce13c38f2dab..047755b0dbb9e 100644
--- a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
+++ b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
@@ -21,17 +21,17 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
-template<class T>
+template <class T>
 concept ValidViewInterfaceType = requires { typename std::ranges::view_interface<T>; };
 
-struct Empty { };
+struct Empty {};
 
 static_assert(!ValidViewInterfaceType<void>);
 static_assert(!ValidViewInterfaceType<void*>);
 static_assert(!ValidViewInterfaceType<Empty*>);
 static_assert(!ValidViewInterfaceType<Empty const>);
-static_assert(!ValidViewInterfaceType<Empty &>);
-static_assert( ValidViewInterfaceType<Empty>);
+static_assert(!ValidViewInterfaceType<Empty&>);
+static_assert(ValidViewInterfaceType<Empty>);
 
 using InputIter = cpp20_input_iterator<const int*>;
 
@@ -50,8 +50,8 @@ struct SizedInputRange : std::ranges::view_interface<SizedInputRange> {
 static_assert(std::ranges::sized_range<SizedInputRange>);
 
 struct NotSizedSentinel {
-  using value_type = int;
-  using difference_type = std::ptrdiff_t;
+  using value_type       = int;
+  using difference_type  = std::ptrdiff_t;
   using iterator_concept = std::forward_iterator_tag;
 
   explicit NotSizedSentinel() = default;
@@ -66,9 +66,7 @@ static_assert(std::forward_iterator<NotSizedSentinel>);
 using ForwardIter = forward_iterator<int*>;
 
 // So that we conform to sized_sentinel_for.
-constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) {
-    return base(x) - base(y);
-}
+constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) { return base(x) - base(y); }
 
 struct ForwardRange : std::ranges::view_interface<ForwardRange> {
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
@@ -78,19 +76,17 @@ struct ForwardRange : std::ranges::view_interface<ForwardRange> {
 static_assert(std::ranges::view<ForwardRange>);
 
 struct MoveOnlyForwardRange : std::ranges::view_interface<MoveOnlyForwardRange> {
-  int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
-  MoveOnlyForwardRange(MoveOnlyForwardRange const&) = delete;
-  MoveOnlyForwardRange(MoveOnlyForwardRange &&) = default;
-  MoveOnlyForwardRange& operator=(MoveOnlyForwardRange &&) = default;
-  MoveOnlyForwardRange() = default;
+  int buff[8]                                             = {0, 1, 2, 3, 4, 5, 6, 7};
+  MoveOnlyForwardRange(MoveOnlyForwardRange const&)       = delete;
+  MoveOnlyForwardRange(MoveOnlyForwardRange&&)            = default;
+  MoveOnlyForwardRange& operator=(MoveOnlyForwardRange&&) = default;
+  MoveOnlyForwardRange()                                  = default;
   constexpr ForwardIter begin() const { return ForwardIter(const_cast<int*>(buff)); }
   constexpr ForwardIter end() const { return ForwardIter(const_cast<int*>(buff) + 8); }
 };
 static_assert(std::ranges::view<MoveOnlyForwardRange>);
 
-struct MI : std::ranges::view_interface<InputRange>,
-            std::ranges::view_interface<MoveOnlyForwardRange> {
-};
+struct MI : std::ranges::view_interface<InputRange>, std::ranges::view_interface<MoveOnlyForwardRange> {};
 static_assert(!std::ranges::view<MI>);
 
 struct EmptyIsTrue : std::ranges::view_interface<EmptyIsTrue> {
@@ -131,7 +127,7 @@ struct DataIsNull : std::ranges::view_interface<DataIsNull> {
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
   constexpr ContIter begin() const { return ContIter(buff); }
   constexpr ContIter end() const { return ContIter(buff + 8); }
-  constexpr const int *data() const { return nullptr; }
+  constexpr const int* data() const { return nullptr; }
 };
 static_assert(std::ranges::view<DataIsNull>);
 
@@ -142,13 +138,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleCo
   };
 
   struct SentinelType {
-    int *base_;
+    int* base_;
     explicit SentinelType() = default;
-    constexpr explicit SentinelType(int *base) : base_(base) {}
-    friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) == sent.base_}; }
-    friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) == sent.base_}; }
-    friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) != sent.base_}; }
-    friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) != sent.base_}; }
+    constexpr explicit SentinelType(int* base) : base_(base) {}
+    friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept {
+      return {base(iter) == sent.base_};
+    }
+    friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept {
+      return {base(iter) == sent.base_};
+    }
+    friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept {
+      return {base(iter) != sent.base_};
+    }
+    friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept {
+      return {base(iter) != sent.base_};
+    }
   };
 
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
@@ -157,21 +161,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleCo
 };
 static_assert(std::ranges::view<BoolConvertibleComparison>);
 
-template<class T>
-concept EmptyInvocable = requires (T const& obj) { obj.empty(); };
+template <class T>
+concept EmptyInvocable = requires(T const& obj) { obj.empty(); };
 
-template<class T>
-concept BoolOpInvocable = requires (T const& obj) { bool(obj); };
+template <class T>
+concept BoolOpInvocable = requires(T const& obj) { bool(obj); };
 
 constexpr bool testEmpty() {
   static_assert(!EmptyInvocable<InputRange>);
   // LWG 3715: `view_interface::empty` is overconstrained
   static_assert(EmptyInvocable<SizedInputRange>);
-  static_assert( EmptyInvocable<ForwardRange>);
+  static_assert(EmptyInvocable<ForwardRange>);
 
   static_assert(!BoolOpInvocable<InputRange>);
   static_assert(BoolOpInvocable<SizedInputRange>);
-  static_assert( BoolOpInvocable<ForwardRange>);
+  static_assert(BoolOpInvocable<ForwardRange>);
 
   SizedInputRange sizedInputRange;
   assert(!sizedInputRange.empty());
@@ -224,12 +228,12 @@ constexpr bool testEmpty() {
   return true;
 }
 
-template<class T>
-concept DataInvocable = requires (T const& obj) { obj.data(); };
+template <class T>
+concept DataInvocable = requires(T const& obj) { obj.data(); };
 
 constexpr bool testData() {
   static_assert(!DataInvocable<ForwardRange>);
-  static_assert( DataInvocable<ContRange>);
+  static_assert(DataInvocable<ContRange>);
 
   ContRange contiguous;
   assert(contiguous.data() == contiguous.buff);
@@ -249,18 +253,18 @@ constexpr bool testData() {
   return true;
 }
 
-template<class T>
-concept SizeInvocable = requires (T const& obj) { obj.size(); };
+template <class T>
+concept SizeInvocable = requires(T const& obj) { obj.size(); };
 
 constexpr bool testSize() {
   static_assert(!SizeInvocable<InputRange>);
   static_assert(!SizeInvocable<NotSizedSentinel>);
-  static_assert( SizeInvocable<ForwardRange>);
+  static_assert(SizeInvocable<ForwardRange>);
 
   // Test the test.
   static_assert(std::same_as<decltype(std::declval<ForwardIter>() - std::declval<ForwardIter>()), std::ptrdiff_t>);
   using UnsignedSize = std::make_unsigned_t<std::ptrdiff_t>;
-  using SignedSize = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>;
+  using SignedSize   = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>;
   ForwardRange forwardRange;
   assert(forwardRange.size() == 8);
   assert(static_cast<ForwardRange const&>(forwardRange).size() == 8);
@@ -284,12 +288,12 @@ constexpr bool testSize() {
   return true;
 }
 
-template<class T>
-concept SubscriptInvocable = requires (T const& obj, std::size_t n) { obj[n]; };
+template <class T>
+concept SubscriptInvocable = requires(T const& obj, std::size_t n) { obj[n]; };
 
 constexpr bool testSubscript() {
   static_assert(!SubscriptInvocable<ForwardRange>);
-  static_assert( SubscriptInvocable<RARange>);
+  static_assert(SubscriptInvocable<RARange>);
 
   RARange randomAccess;
   assert(randomAccess[2] == 2);
@@ -300,17 +304,59 @@ constexpr bool testSubscript() {
   return true;
 }
 
-template<class T>
-concept FrontInvocable = requires (T const& obj) { obj.front(); };
+template <class T>
+concept AtInvocable = requires(T const& obj, std::size_t n) { obj.at(n); };
+
+#if TEST_STD_VER >= 29
+constexpr bool testAt() {
+  static_assert(!AtInvocable<ForwardRange>);
+  static_assert(AtInvocable<RARange>);
+
+  RARange randomAccess;
+  assert(randomAccess.at(2) == 2);
+  assert(static_cast<RARange const&>(randomAccess).at(2) == 2);
+  randomAccess.at(2) = 3;
+  assert(randomAccess.at(2) == 3);
+
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  if (!std::is_constant_evaluated()) {
+    try {
+      TEST_IGNORE_NODISCARD randomAccess.at(0);
+      assert(true);
+    } catch (std::out_of_range const&) {
+      // pass
+      assert(false); // This should not be reached because index 0 is valid.
+    } catch (...) {
+      assert(false);
+    }
+
+    // Test that at() throws an exception when the index is out of range.
+    try {
+      TEST_IGNORE_NODISCARD randomAccess.at(10);
+      assert(false);
+    } catch (std::out_of_range const&) {
+      // pass
+    } catch (...) {
+      assert(false);
+    }
+  }
+#endif
+
+  return true;
+}
+#endif // TEST_STD_VER >= 29
+
+template <class T>
+concept FrontInvocable = requires(T const& obj) { obj.front(); };
 
-template<class T>
-concept BackInvocable = requires (T const& obj) { obj.back(); };
+template <class T>
+concept BackInvocable = requires(T const& obj) { obj.back(); };
 
 constexpr bool testFrontBack() {
   static_assert(!FrontInvocable<InputRange>);
-  static_assert( FrontInvocable<ForwardRange>);
+  static_assert(FrontInvocable<ForwardRange>);
   static_assert(!BackInvocable<ForwardRange>);
-  static_assert( BackInvocable<RARange>);
+  static_assert(BackInvocable<RARange>);
 
   ForwardRange forwardRange;
   assert(forwardRange.front() == 0);
@@ -332,8 +378,10 @@ constexpr bool testFrontBack() {
   return true;
 }
 
-struct V1 : std::ranges::view_interface<V1> { };
-struct V2 : std::ranges::view_interface<V2> { V1 base_; };
+struct V1 : std::ranges::view_interface<V1> {};
+struct V2 : std::ranges::view_interface<V2> {
+  V1 base_;
+};
 static_assert(sizeof(V2) == sizeof(V1));
 
 int main(int, char**) {
@@ -349,6 +397,11 @@ int main(int, char**) {
   testSubscript();
   static_assert(testSubscript());
 
+#if TEST_STD_VER >= 29
+  testAt();
+  static_assert(testAt());
+#endif
+
   testFrontBack();
   static_assert(testFrontBack());
 
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..a2540256b3f1d 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1526,6 +1526,13 @@ def add_version_header(tc):
             },
             "headers": ["variant"],
         },
+        {
+            "name": "__cpp_lib_view_interface",
+            "values": {
+                "c++20": 202606,
+            },
+            "headers": ["ranges"],
+        },
         {
             "name": "__cpp_lib_void_t",
             "values": {"c++17": 201411},


From libcxx-commits at lists.llvm.org  Fri Jul 17 10:54:39 2026
From: libcxx-commits at lists.llvm.org (Nico Weber via libcxx-commits)
Date: Fri, 17 Jul 2026 10:54:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add MSVC's implementation of
 `exception_ptr` for Windows (PR #94977)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/94977@github.com>
Message-ID: <6a5a6c5f.e5907290.281687.e6cb@mx.google.com>


nico wrote:

We're seeing

```
In file included from ../../third_party/libc++/src/src/exception.cpp:16:
../../third_party/libc++/src/src\support/runtime/exception_pointer_msvc.ipp(149,38): error: implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension [-Werror,-Wmicrosoft-cast]
  149 |     __call_member_function_2(__dest, __copy_func, __adjusted, 1);
      |                                      ^~~~~~~~~~~
../../third_party/libc++/src/src\support/runtime/exception_pointer_msvc.ipp(151,38): error: implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension [-Werror,-Wmicrosoft-cast]
  151 |     __call_member_function_1(__dest, __copy_func, __adjusted);
      |                                      ^~~~~~~~~~~
../../third_party/libc++/src/src\support/runtime/exception_pointer_msvc.ipp(207,77): error: implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension [-Werror,-Wmicrosoft-cast]
  207 |           __call_member_function_0(__cpp_eh_record.params.pExceptionObject, __throw_info->pmfnUnwind);
      |                                                                             ^~~~~~~~~~~~~~~~~~~~~~~~
3 errors generated.
```

with this.

https://github.com/llvm/llvm-project/pull/94977

From libcxx-commits at lists.llvm.org  Fri Jul 17 10:58:34 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 10:58:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3052R2:
	`view_interface::at()` (PR #205012)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/205012@github.com>
Message-ID: <6a5a6d4a.1e1ac41d.3969c3.fed5@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/205012

>From b868e5e71f1cca8374170bcc4cf60789344edaab Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sun, 21 Jun 2026 23:52:40 +0300
Subject: [PATCH 1/2] [libc++][ranges] P3052R2: `view_interface::at()`

Closes #204395

Implements https://wg21.link/P3052R2
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/Status/Cxx29Papers.csv            |   2 +-
 libcxx/include/__ranges/view_interface.h      |  26 +++
 libcxx/include/version                        |   2 +
 .../view.interface/nodiscard.verify.cpp       |   7 +
 .../test/libcxx/transitive_includes/cxx23.csv |   1 +
 .../test/libcxx/transitive_includes/cxx26.csv |   1 +
 .../ranges.version.compile.pass.cpp           |  33 ++++
 .../version.version.compile.pass.cpp          |  33 ++++
 .../view.interface/view.interface.pass.cpp    | 149 ++++++++++++------
 .../generate_feature_test_macro_components.py |   7 +
 11 files changed, 214 insertions(+), 49 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..82d0910a7d7e1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -308,6 +308,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_variant``                                      ``202106L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_view_interface``                               ``202606L``
+    ---------------------------------------------------------- -----------------
     **C++23**
     ----------------------------------------------------------------------------
     ``__cpp_lib_adaptor_iterator_pair_constructor``            ``202106L``
diff --git a/libcxx/docs/Status/Cxx29Papers.csv b/libcxx/docs/Status/Cxx29Papers.csv
index 3c48fe89be33d..656eaf5df44df 100644
--- a/libcxx/docs/Status/Cxx29Papers.csv
+++ b/libcxx/docs/Status/Cxx29Papers.csv
@@ -4,7 +4,7 @@
 "`P2414R12 <https://wg21.link/P2414R12>`__","Pointer lifetime-end zap proposed solutions","2026-06 (Brno)","","","`#204392 <https://github.com/llvm/llvm-project/issues/204392>`__","Voted as a Defect Report."
 "`P3319R6 <https://wg21.link/P3319R6>`__","Add an ``iota`` object for ``simd`` (and more)","2026-06 (Brno)","","","`#204393 <https://github.com/llvm/llvm-project/issues/204393>`__",""
 "`P3798R1 <https://wg21.link/P3798R1>`__","The unexpected in ``std::expected``","2026-06 (Brno)","","","`#204394 <https://github.com/llvm/llvm-project/issues/204394>`__",""
-"`P3052R2 <https://wg21.link/P3052R2>`__","``view_interface::at()``","2026-06 (Brno)","","","`#204395 <https://github.com/llvm/llvm-project/issues/204395>`__",""
+"`P3052R2 <https://wg21.link/P3052R2>`__","``view_interface::at()``","2026-06 (Brno)","|Complete|","23","`#204395 <https://github.com/llvm/llvm-project/issues/204395>`__",""
 "`P4206R0 <https://wg21.link/P4206R0>`__","Revert string support in ``std::constant_wrapper``","2026-06 (Brno)","|Complete|","23","`#203336 <https://github.com/llvm/llvm-project/issues/203336>`__","Applied as a Defect Report."
 "`P3395R6 <https://wg21.link/P3395R6>`__","Fix encoding issues and add a formatter for ``std::error_code``","2026-06 (Brno)","","","`#204396 <https://github.com/llvm/llvm-project/issues/204396>`__",""
 "`P3505R4 <https://wg21.link/P3505R4>`__","Fix the default floating-point representation in ``std::format``","2026-06 (Brno)","","","`#204397 <https://github.com/llvm/llvm-project/issues/204397>`__","To be applied as a Defect Report."
diff --git a/libcxx/include/__ranges/view_interface.h b/libcxx/include/__ranges/view_interface.h
index 96508c2be7277..c5875d024840e 100644
--- a/libcxx/include/__ranges/view_interface.h
+++ b/libcxx/include/__ranges/view_interface.h
@@ -15,6 +15,7 @@
 #include <__concepts/same_as.h>
 #include <__config>
 #include <__iterator/concepts.h>
+#include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/prev.h>
 #include <__memory/pointer_traits.h>
@@ -25,6 +26,7 @@
 #include <__type_traits/is_class.h>
 #include <__type_traits/make_unsigned.h>
 #include <__type_traits/remove_cv.h>
+#include <stdexcept>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -159,8 +161,32 @@ class view_interface {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](range_difference_t<_RARange> __index) const {
     return ranges::begin(__derived())[__index];
   }
+
+  #if _LIBCPP_STD_VER >= 29
+
+  template <random_access_range _RARange = _Derived>
+    requires sized_range<_RARange> // freestanding-deleted
+  [[nodiscard]] constexpr decltype(auto) at(range_difference_t<_RARange> __index) {
+    if (__index < 0 || __index >= ranges::distance(__derived())) {
+      std::__throw_out_of_range(
+          "Precondition `0 <= __index < distance()` not satisfied. `.at(__index)` called with out-of-bounds index.");
+    }
+    return (*this)[__index];
+  }
+
+  template <random_access_range _RARange = const _Derived>
+    requires sized_range<_RARange> // freestanding-deleted
+  [[nodiscard]] constexpr decltype(auto) at(range_difference_t<_RARange> __index) const {
+    if (__index < 0 || __index >= ranges::distance(__derived())) {
+      std::__throw_out_of_range(
+          "Precondition `0 <= __index < distance()` not satisfied. `.at(__index)` called with out-of-bounds index.");
+    }
+    return (*this)[__index];
+  }
 };
 
+#endif // _LIBCPP_STD_VER >= 29
+
 } // namespace ranges
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..94185b7602f64 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -290,6 +290,7 @@ __cpp_lib_unwrap_ref                                    201811L <functional>
 __cpp_lib_variant                                       202306L <variant>
                                                         202106L // C++20
                                                         202102L // C++17
+__cpp_lib_view_interface                                202606L <ranges>
 __cpp_lib_void_t                                        201411L <type_traits>
 
 */
@@ -488,6 +489,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_unwrap_ref                           201811L
 # undef  __cpp_lib_variant
 # define __cpp_lib_variant                              202106L
+# define __cpp_lib_view_interface                       202606L
 #endif
 
 #if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
index a22c03f5d1a71..2bccc170aa727 100644
--- a/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
@@ -67,4 +67,11 @@ void test() {
   v[Diff{0}];
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v)[Diff{0}];
+
+#if TEST_STD_VER >= 29
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.at(Diff{0});
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).at(Diff{0});
+#endif
 }
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 53ae6c9b4e591..260c410680e39 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -11,6 +11,7 @@ algorithm iosfwd
 algorithm limits
 algorithm optional
 algorithm ratio
+algorithm stdexcept
 algorithm tuple
 algorithm version
 any cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index f712f36caa9fc..6cc4737bff91b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -11,6 +11,7 @@ algorithm iosfwd
 algorithm limits
 algorithm optional
 algorithm ratio
+algorithm stdexcept
 algorithm tuple
 algorithm version
 any cstdint
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 670b0e664721a..f42c7ede894b5 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -80,6 +80,10 @@
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #elif TEST_STD_VER == 14
 
 #  ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -142,6 +146,10 @@
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #elif TEST_STD_VER == 17
 
 #  ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -204,6 +212,10 @@
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #elif TEST_STD_VER == 20
 
 #  ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -269,6 +281,13 @@
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++20"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++20"
+#  endif
+
 #elif TEST_STD_VER == 23
 
 #  ifdef __cpp_lib_default_template_type_for_algorithm_values
@@ -385,6 +404,13 @@
 #    error "__cpp_lib_ranges_zip should have the value 202110L in c++23"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++23"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++23"
+#  endif
+
 #elif TEST_STD_VER > 23
 
 #  if !defined(_LIBCPP_VERSION)
@@ -516,6 +542,13 @@
 #    error "__cpp_lib_ranges_zip should have the value 202110L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++26"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++26"
+#  endif
+
 #endif // TEST_STD_VER > 23
 
 // clang-format on
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 f2d74cc99d864..79e61725a93fc 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
@@ -948,6 +948,10 @@
 #    error "__cpp_lib_variant should not be defined before c++17"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #  ifdef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should not be defined before c++17"
 #  endif
@@ -1954,6 +1958,10 @@
 #    error "__cpp_lib_variant should not be defined before c++17"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #  ifdef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should not be defined before c++17"
 #  endif
@@ -3149,6 +3157,10 @@
 #    error "__cpp_lib_variant should have the value 202102L in c++17"
 #  endif
 
+#  ifdef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should not be defined before c++20"
+#  endif
+
 #  ifndef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should be defined in c++17"
 #  endif
@@ -4611,6 +4623,13 @@
 #    error "__cpp_lib_variant should have the value 202106L in c++20"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++20"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++20"
+#  endif
+
 #  ifndef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should be defined in c++20"
 #  endif
@@ -6301,6 +6320,13 @@
 #    error "__cpp_lib_variant should have the value 202106L in c++23"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++23"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++23"
+#  endif
+
 #  ifndef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should be defined in c++23"
 #  endif
@@ -8324,6 +8350,13 @@
 #    error "__cpp_lib_variant should have the value 202306L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++26"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_void_t
 #    error "__cpp_lib_void_t should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
index bce13c38f2dab..047755b0dbb9e 100644
--- a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
+++ b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
@@ -21,17 +21,17 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
-template<class T>
+template <class T>
 concept ValidViewInterfaceType = requires { typename std::ranges::view_interface<T>; };
 
-struct Empty { };
+struct Empty {};
 
 static_assert(!ValidViewInterfaceType<void>);
 static_assert(!ValidViewInterfaceType<void*>);
 static_assert(!ValidViewInterfaceType<Empty*>);
 static_assert(!ValidViewInterfaceType<Empty const>);
-static_assert(!ValidViewInterfaceType<Empty &>);
-static_assert( ValidViewInterfaceType<Empty>);
+static_assert(!ValidViewInterfaceType<Empty&>);
+static_assert(ValidViewInterfaceType<Empty>);
 
 using InputIter = cpp20_input_iterator<const int*>;
 
@@ -50,8 +50,8 @@ struct SizedInputRange : std::ranges::view_interface<SizedInputRange> {
 static_assert(std::ranges::sized_range<SizedInputRange>);
 
 struct NotSizedSentinel {
-  using value_type = int;
-  using difference_type = std::ptrdiff_t;
+  using value_type       = int;
+  using difference_type  = std::ptrdiff_t;
   using iterator_concept = std::forward_iterator_tag;
 
   explicit NotSizedSentinel() = default;
@@ -66,9 +66,7 @@ static_assert(std::forward_iterator<NotSizedSentinel>);
 using ForwardIter = forward_iterator<int*>;
 
 // So that we conform to sized_sentinel_for.
-constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) {
-    return base(x) - base(y);
-}
+constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) { return base(x) - base(y); }
 
 struct ForwardRange : std::ranges::view_interface<ForwardRange> {
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
@@ -78,19 +76,17 @@ struct ForwardRange : std::ranges::view_interface<ForwardRange> {
 static_assert(std::ranges::view<ForwardRange>);
 
 struct MoveOnlyForwardRange : std::ranges::view_interface<MoveOnlyForwardRange> {
-  int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
-  MoveOnlyForwardRange(MoveOnlyForwardRange const&) = delete;
-  MoveOnlyForwardRange(MoveOnlyForwardRange &&) = default;
-  MoveOnlyForwardRange& operator=(MoveOnlyForwardRange &&) = default;
-  MoveOnlyForwardRange() = default;
+  int buff[8]                                             = {0, 1, 2, 3, 4, 5, 6, 7};
+  MoveOnlyForwardRange(MoveOnlyForwardRange const&)       = delete;
+  MoveOnlyForwardRange(MoveOnlyForwardRange&&)            = default;
+  MoveOnlyForwardRange& operator=(MoveOnlyForwardRange&&) = default;
+  MoveOnlyForwardRange()                                  = default;
   constexpr ForwardIter begin() const { return ForwardIter(const_cast<int*>(buff)); }
   constexpr ForwardIter end() const { return ForwardIter(const_cast<int*>(buff) + 8); }
 };
 static_assert(std::ranges::view<MoveOnlyForwardRange>);
 
-struct MI : std::ranges::view_interface<InputRange>,
-            std::ranges::view_interface<MoveOnlyForwardRange> {
-};
+struct MI : std::ranges::view_interface<InputRange>, std::ranges::view_interface<MoveOnlyForwardRange> {};
 static_assert(!std::ranges::view<MI>);
 
 struct EmptyIsTrue : std::ranges::view_interface<EmptyIsTrue> {
@@ -131,7 +127,7 @@ struct DataIsNull : std::ranges::view_interface<DataIsNull> {
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
   constexpr ContIter begin() const { return ContIter(buff); }
   constexpr ContIter end() const { return ContIter(buff + 8); }
-  constexpr const int *data() const { return nullptr; }
+  constexpr const int* data() const { return nullptr; }
 };
 static_assert(std::ranges::view<DataIsNull>);
 
@@ -142,13 +138,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleCo
   };
 
   struct SentinelType {
-    int *base_;
+    int* base_;
     explicit SentinelType() = default;
-    constexpr explicit SentinelType(int *base) : base_(base) {}
-    friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) == sent.base_}; }
-    friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) == sent.base_}; }
-    friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) != sent.base_}; }
-    friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) != sent.base_}; }
+    constexpr explicit SentinelType(int* base) : base_(base) {}
+    friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept {
+      return {base(iter) == sent.base_};
+    }
+    friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept {
+      return {base(iter) == sent.base_};
+    }
+    friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept {
+      return {base(iter) != sent.base_};
+    }
+    friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept {
+      return {base(iter) != sent.base_};
+    }
   };
 
   int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
@@ -157,21 +161,21 @@ struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleCo
 };
 static_assert(std::ranges::view<BoolConvertibleComparison>);
 
-template<class T>
-concept EmptyInvocable = requires (T const& obj) { obj.empty(); };
+template <class T>
+concept EmptyInvocable = requires(T const& obj) { obj.empty(); };
 
-template<class T>
-concept BoolOpInvocable = requires (T const& obj) { bool(obj); };
+template <class T>
+concept BoolOpInvocable = requires(T const& obj) { bool(obj); };
 
 constexpr bool testEmpty() {
   static_assert(!EmptyInvocable<InputRange>);
   // LWG 3715: `view_interface::empty` is overconstrained
   static_assert(EmptyInvocable<SizedInputRange>);
-  static_assert( EmptyInvocable<ForwardRange>);
+  static_assert(EmptyInvocable<ForwardRange>);
 
   static_assert(!BoolOpInvocable<InputRange>);
   static_assert(BoolOpInvocable<SizedInputRange>);
-  static_assert( BoolOpInvocable<ForwardRange>);
+  static_assert(BoolOpInvocable<ForwardRange>);
 
   SizedInputRange sizedInputRange;
   assert(!sizedInputRange.empty());
@@ -224,12 +228,12 @@ constexpr bool testEmpty() {
   return true;
 }
 
-template<class T>
-concept DataInvocable = requires (T const& obj) { obj.data(); };
+template <class T>
+concept DataInvocable = requires(T const& obj) { obj.data(); };
 
 constexpr bool testData() {
   static_assert(!DataInvocable<ForwardRange>);
-  static_assert( DataInvocable<ContRange>);
+  static_assert(DataInvocable<ContRange>);
 
   ContRange contiguous;
   assert(contiguous.data() == contiguous.buff);
@@ -249,18 +253,18 @@ constexpr bool testData() {
   return true;
 }
 
-template<class T>
-concept SizeInvocable = requires (T const& obj) { obj.size(); };
+template <class T>
+concept SizeInvocable = requires(T const& obj) { obj.size(); };
 
 constexpr bool testSize() {
   static_assert(!SizeInvocable<InputRange>);
   static_assert(!SizeInvocable<NotSizedSentinel>);
-  static_assert( SizeInvocable<ForwardRange>);
+  static_assert(SizeInvocable<ForwardRange>);
 
   // Test the test.
   static_assert(std::same_as<decltype(std::declval<ForwardIter>() - std::declval<ForwardIter>()), std::ptrdiff_t>);
   using UnsignedSize = std::make_unsigned_t<std::ptrdiff_t>;
-  using SignedSize = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>;
+  using SignedSize   = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>;
   ForwardRange forwardRange;
   assert(forwardRange.size() == 8);
   assert(static_cast<ForwardRange const&>(forwardRange).size() == 8);
@@ -284,12 +288,12 @@ constexpr bool testSize() {
   return true;
 }
 
-template<class T>
-concept SubscriptInvocable = requires (T const& obj, std::size_t n) { obj[n]; };
+template <class T>
+concept SubscriptInvocable = requires(T const& obj, std::size_t n) { obj[n]; };
 
 constexpr bool testSubscript() {
   static_assert(!SubscriptInvocable<ForwardRange>);
-  static_assert( SubscriptInvocable<RARange>);
+  static_assert(SubscriptInvocable<RARange>);
 
   RARange randomAccess;
   assert(randomAccess[2] == 2);
@@ -300,17 +304,59 @@ constexpr bool testSubscript() {
   return true;
 }
 
-template<class T>
-concept FrontInvocable = requires (T const& obj) { obj.front(); };
+template <class T>
+concept AtInvocable = requires(T const& obj, std::size_t n) { obj.at(n); };
+
+#if TEST_STD_VER >= 29
+constexpr bool testAt() {
+  static_assert(!AtInvocable<ForwardRange>);
+  static_assert(AtInvocable<RARange>);
+
+  RARange randomAccess;
+  assert(randomAccess.at(2) == 2);
+  assert(static_cast<RARange const&>(randomAccess).at(2) == 2);
+  randomAccess.at(2) = 3;
+  assert(randomAccess.at(2) == 3);
+
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  if (!std::is_constant_evaluated()) {
+    try {
+      TEST_IGNORE_NODISCARD randomAccess.at(0);
+      assert(true);
+    } catch (std::out_of_range const&) {
+      // pass
+      assert(false); // This should not be reached because index 0 is valid.
+    } catch (...) {
+      assert(false);
+    }
+
+    // Test that at() throws an exception when the index is out of range.
+    try {
+      TEST_IGNORE_NODISCARD randomAccess.at(10);
+      assert(false);
+    } catch (std::out_of_range const&) {
+      // pass
+    } catch (...) {
+      assert(false);
+    }
+  }
+#endif
+
+  return true;
+}
+#endif // TEST_STD_VER >= 29
+
+template <class T>
+concept FrontInvocable = requires(T const& obj) { obj.front(); };
 
-template<class T>
-concept BackInvocable = requires (T const& obj) { obj.back(); };
+template <class T>
+concept BackInvocable = requires(T const& obj) { obj.back(); };
 
 constexpr bool testFrontBack() {
   static_assert(!FrontInvocable<InputRange>);
-  static_assert( FrontInvocable<ForwardRange>);
+  static_assert(FrontInvocable<ForwardRange>);
   static_assert(!BackInvocable<ForwardRange>);
-  static_assert( BackInvocable<RARange>);
+  static_assert(BackInvocable<RARange>);
 
   ForwardRange forwardRange;
   assert(forwardRange.front() == 0);
@@ -332,8 +378,10 @@ constexpr bool testFrontBack() {
   return true;
 }
 
-struct V1 : std::ranges::view_interface<V1> { };
-struct V2 : std::ranges::view_interface<V2> { V1 base_; };
+struct V1 : std::ranges::view_interface<V1> {};
+struct V2 : std::ranges::view_interface<V2> {
+  V1 base_;
+};
 static_assert(sizeof(V2) == sizeof(V1));
 
 int main(int, char**) {
@@ -349,6 +397,11 @@ int main(int, char**) {
   testSubscript();
   static_assert(testSubscript());
 
+#if TEST_STD_VER >= 29
+  testAt();
+  static_assert(testAt());
+#endif
+
   testFrontBack();
   static_assert(testFrontBack());
 
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..a2540256b3f1d 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1526,6 +1526,13 @@ def add_version_header(tc):
             },
             "headers": ["variant"],
         },
+        {
+            "name": "__cpp_lib_view_interface",
+            "values": {
+                "c++20": 202606,
+            },
+            "headers": ["ranges"],
+        },
         {
             "name": "__cpp_lib_void_t",
             "values": {"c++17": 201411},

>From 1f2ed195d0f1e5c763bedd8f0c04f47c609a7b27 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 20:58:15 +0300
Subject: [PATCH 2/2] Fixes

---
 libcxx/include/__ranges/view_interface.h                  | 6 +++---
 .../range.utility/view.interface/view.interface.pass.cpp  | 8 +++++---
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__ranges/view_interface.h b/libcxx/include/__ranges/view_interface.h
index c5875d024840e..c9f78966d1953 100644
--- a/libcxx/include/__ranges/view_interface.h
+++ b/libcxx/include/__ranges/view_interface.h
@@ -162,7 +162,7 @@ class view_interface {
     return ranges::begin(__derived())[__index];
   }
 
-  #if _LIBCPP_STD_VER >= 29
+#  if _LIBCPP_STD_VER >= 29
 
   template <random_access_range _RARange = _Derived>
     requires sized_range<_RARange> // freestanding-deleted
@@ -183,9 +183,9 @@ class view_interface {
     }
     return (*this)[__index];
   }
-};
 
-#endif // _LIBCPP_STD_VER >= 29
+#  endif // _LIBCPP_STD_VER >= 29
+};
 
 } // namespace ranges
 
diff --git a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
index 047755b0dbb9e..ad871207c4c71 100644
--- a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
+++ b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
@@ -304,10 +304,11 @@ constexpr bool testSubscript() {
   return true;
 }
 
+#if TEST_STD_VER >= 29
+
 template <class T>
 concept AtInvocable = requires(T const& obj, std::size_t n) { obj.at(n); };
 
-#if TEST_STD_VER >= 29
 constexpr bool testAt() {
   static_assert(!AtInvocable<ForwardRange>);
   static_assert(AtInvocable<RARange>);
@@ -318,7 +319,7 @@ constexpr bool testAt() {
   randomAccess.at(2) = 3;
   assert(randomAccess.at(2) == 3);
 
-#ifndef TEST_HAS_NO_EXCEPTIONS
+#  ifndef TEST_HAS_NO_EXCEPTIONS
   if (!std::is_constant_evaluated()) {
     try {
       TEST_IGNORE_NODISCARD randomAccess.at(0);
@@ -340,10 +341,11 @@ constexpr bool testAt() {
       assert(false);
     }
   }
-#endif
+#  endif
 
   return true;
 }
+
 #endif // TEST_STD_VER >= 29
 
 template <class T>


From libcxx-commits at lists.llvm.org  Fri Jul 17 11:49:24 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 11:49:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][atomics] P2869R4: Remove
 Deprecated `shared_ptr` Atomic Access APIs from C++26 (PR #194544)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/194544@github.com>
Message-ID: <6a5a7934.3cce8983.3cf542.d59c@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/194544

>From 3791e24a4c6749b327252624d0a3233aaeeb7261 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 28 Apr 2026 08:22:17 +0300
Subject: [PATCH] [libc++][atomics] P2869R4: Remove Deprecated `shared_ptr`
 Atomic Access APIs from C++26

Closes #105412

Also impelements the deprecations from: P0718R2 (https://wg21.link/p0718r2) (https://llvm.org/PR99980)

Reverts "[libc++] Undeprecate shared_ptr atomic access APIs (#92920)" commit 716ed5fccd2a960981fec2c5acb17292a1502435.
---
 libcxx/docs/ReleaseNotes/24.rst               |  3 ++
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 libcxx/docs/UserDocumentation.rst             |  4 +++
 libcxx/include/__memory/shared_ptr.h          | 29 ++++++++++------
 libcxx/include/memory                         | 22 ++++++------
 libcxx/modules/std/memory.inc                 |  6 ++++
 ...e_exchange_strong.depr_in_cxx20.verify.cpp | 32 +++++++++++++++++
 ...e_strong_explicit.depr_in_cxx20.verify.cpp | 34 +++++++++++++++++++
 ...are_exchange_weak.depr_in_cxx20.verify.cpp | 31 +++++++++++++++++
 ...nge_weak_explicit.depr_in_cxx20.verify.cpp | 34 +++++++++++++++++++
 .../atomic_exchange.depr_in_cxx20..verify.cpp | 29 ++++++++++++++++
 ...xchange_explicit.verify.depr_in_cxx20..cpp | 30 ++++++++++++++++
 ...omic_is_lock_free.depr_in_cxx20.verify.cpp | 28 +++++++++++++++
 .../atomic_load.depr_in_cxx20..verify.cpp     | 28 +++++++++++++++
 ...mic_load_explicit.depr_in_cxx20.verify.cpp | 29 ++++++++++++++++
 .../atomic_store.depr_in_cxx20.verify.cpp     | 28 +++++++++++++++
 ...ic_store_explicit.depr_in_cxx20.verify.cpp | 29 ++++++++++++++++
 .../atomic_compare_exchange_strong.pass.cpp   |  6 ++--
 ..._compare_exchange_strong_explicit.pass.cpp |  6 ++--
 .../atomic_compare_exchange_weak.pass.cpp     |  6 ++--
 ...ic_compare_exchange_weak_explicit.pass.cpp |  6 ++--
 .../atomic_exchange.pass.cpp                  |  6 ++--
 .../atomic_exchange_explicit.pass.cpp         |  6 ++--
 .../atomic_is_lock_free.pass.cpp              |  6 ++--
 .../atomic_load.pass.cpp                      |  6 ++--
 .../atomic_load_explicit.pass.cpp             |  6 ++--
 .../atomic_store.pass.cpp                     |  6 ++--
 .../atomic_store_explicit.pass.cpp            |  6 ++--
 28 files changed, 419 insertions(+), 45 deletions(-)
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange.depr_in_cxx20..verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange_explicit.verify.depr_in_cxx20..cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_is_lock_free.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load.depr_in_cxx20..verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load_explicit.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store.depr_in_cxx20.verify.cpp
 create mode 100644 libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store_explicit.depr_in_cxx20.verify.cpp

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..984e353dd3e56 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,10 +38,13 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+-`P2869R4: Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26 (`Github <https://github.com/llvm/llvm-project/issues/105412>`__)
 
 Improvements and New Features
 -----------------------------
 
+- The ``_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS`` macro has been added to make the declarations in ``<memory>``
+  available.
 
 Deprecations and Removals
 -------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 2522dd98e4b9c..fac9af5145da5 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -48,7 +48,7 @@
 "","","","","","",""
 "`P2875R4 <https://wg21.link/P2875R4>`__","Undeprecate ``polymorphic_allocator::destroy`` for C++26","2024-03 (Tokyo)","|Complete|","15","`#105410 <https://github.com/llvm/llvm-project/issues/105410>`__",""
 "`P2867R2 <https://wg21.link/P2867R2>`__","Remove Deprecated ``strstreams`` >From C++26","2024-03 (Tokyo)","|Complete|","19","`#105411 <https://github.com/llvm/llvm-project/issues/105411>`__",""
-"`P2869R4 <https://wg21.link/P2869R4>`__","Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26","2024-03 (Tokyo)","","","`#105412 <https://github.com/llvm/llvm-project/issues/105412>`__",""
+"`P2869R4 <https://wg21.link/P2869R4>`__","Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26","2024-03 (Tokyo)","|Complete|","24","`#105412 <https://github.com/llvm/llvm-project/issues/105412>`__",""
 "`P2872R3 <https://wg21.link/P2872R3>`__","Remove ``wstring_convert`` From C++26","2024-03 (Tokyo)","|Complete|","19","`#105413 <https://github.com/llvm/llvm-project/issues/105413>`__",""
 "`P3107R5 <https://wg21.link/P3107R5>`__","Permit an efficient implementation of ``std::print``","2024-03 (Tokyo)","","","`#105414 <https://github.com/llvm/llvm-project/issues/105414>`__",""
 "`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)"
diff --git a/libcxx/docs/UserDocumentation.rst b/libcxx/docs/UserDocumentation.rst
index 415a599168374..cf568e6afc91f 100644
--- a/libcxx/docs/UserDocumentation.rst
+++ b/libcxx/docs/UserDocumentation.rst
@@ -224,6 +224,9 @@ C++26 Specific Configuration Macros
 **_LIBCPP_ENABLE_CXX26_REMOVED_CODECVT**:
   This macro is used to re-enable all named declarations in ``<codecvt>``.
 
+**_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS**:
+  This macro is used to re-enable all ``shared_ptr`` atomic access APIs in ``<memory>``.
+
 **_LIBCPP_ENABLE_CXX26_REMOVED_STRING_RESERVE**:
   This macro is used to re-enable the function
   ``std::basic_string<...>::reserve()``.
@@ -235,6 +238,7 @@ C++26 Specific Configuration Macros
   This macro is used to re-enable the ``wstring_convert`` and ``wbuffer_convert``
   in ``<locale>``.
 
+
 Libc++ Extensions
 =================
 
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 4112c1175b9e1..7504a712a3a3a 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -1382,13 +1382,15 @@ class _LIBCPP_EXPORTED_FROM_ABI __sp_mut {
 
 _LIBCPP_EXPORTED_FROM_ABI __sp_mut& __get_sp_mut(const void*);
 
+#  if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS)
+
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI bool atomic_is_lock_free(const shared_ptr<_Tp>*) {
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool atomic_is_lock_free(const shared_ptr<_Tp>*) {
   return false;
 }
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> atomic_load(const shared_ptr<_Tp>* __p) {
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> atomic_load(const shared_ptr<_Tp>* __p) {
   __sp_mut& __m = std::__get_sp_mut(__p);
   __m.lock();
   shared_ptr<_Tp> __q = *__p;
@@ -1397,12 +1399,13 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> atomic_load(const shared_ptr<_Tp>* __p) {
 }
 
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> atomic_load_explicit(const shared_ptr<_Tp>* __p, memory_order) {
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
+atomic_load_explicit(const shared_ptr<_Tp>* __p, memory_order) {
   return std::atomic_load(__p);
 }
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI void atomic_store(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) {
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI void atomic_store(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) {
   __sp_mut& __m = std::__get_sp_mut(__p);
   __m.lock();
   __p->swap(__r);
@@ -1410,12 +1413,14 @@ _LIBCPP_HIDE_FROM_ABI void atomic_store(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __
 }
 
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI void atomic_store_explicit(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r, memory_order) {
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI void
+atomic_store_explicit(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r, memory_order) {
   std::atomic_store(__p, __r);
 }
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> atomic_exchange(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) {
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
+atomic_exchange(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) {
   __sp_mut& __m = std::__get_sp_mut(__p);
   __m.lock();
   __p->swap(__r);
@@ -1424,13 +1429,13 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> atomic_exchange(shared_ptr<_Tp>* __p, shar
 }
 
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
 atomic_exchange_explicit(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r, memory_order) {
   return std::atomic_exchange(__p, __r);
 }
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI bool
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool
 atomic_compare_exchange_strong(shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, shared_ptr<_Tp> __w) {
   shared_ptr<_Tp> __temp;
   __sp_mut& __m = std::__get_sp_mut(__p);
@@ -1448,23 +1453,25 @@ atomic_compare_exchange_strong(shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, share
 }
 
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI bool
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool
 atomic_compare_exchange_weak(shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, shared_ptr<_Tp> __w) {
   return std::atomic_compare_exchange_strong(__p, __v, __w);
 }
 
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI bool atomic_compare_exchange_strong_explicit(
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool atomic_compare_exchange_strong_explicit(
     shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, shared_ptr<_Tp> __w, memory_order, memory_order) {
   return std::atomic_compare_exchange_strong(__p, __v, __w);
 }
 
 template <class _Tp>
-inline _LIBCPP_HIDE_FROM_ABI bool atomic_compare_exchange_weak_explicit(
+_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool atomic_compare_exchange_weak_explicit(
     shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, shared_ptr<_Tp> __w, memory_order, memory_order) {
   return std::atomic_compare_exchange_weak(__p, __v, __w);
 }
 
+#  endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS)
+
 #endif // _LIBCPP_HAS_THREADS
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
diff --git a/libcxx/include/memory b/libcxx/include/memory
index d5d48ed9a9e6d..fddb9743e9e01 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -831,34 +831,34 @@ public:
 };
 
 template<class T>
-    bool atomic_is_lock_free(const shared_ptr<T>* p);
+    bool atomic_is_lock_free(const shared_ptr<T>* p);                                     // Deprecated in C++20, removed in C++26
 template<class T>
-    shared_ptr<T> atomic_load(const shared_ptr<T>* p);
+    shared_ptr<T> atomic_load(const shared_ptr<T>* p);                                    // Deprecated in C++20, removed in C++26
 template<class T>
-    shared_ptr<T> atomic_load_explicit(const shared_ptr<T>* p, memory_order mo);
+    shared_ptr<T> atomic_load_explicit(const shared_ptr<T>* p, memory_order mo);          // Deprecated in C++20, removed in C++26
 template<class T>
-    void atomic_store(shared_ptr<T>* p, shared_ptr<T> r);
+    void atomic_store(shared_ptr<T>* p, shared_ptr<T> r);                                 // Deprecated in C++20, removed in C++26
 template<class T>
-    void atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo);
+    void atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo);       // Deprecated in C++20, removed in C++26
 template<class T>
-    shared_ptr<T> atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r);
+    shared_ptr<T> atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r);                     // Deprecated in C++20, removed in C++26
 template<class T>
     shared_ptr<T>
-    atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo);
+    atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo);         // Deprecated in C++20, removed in C++26
 template<class T>
     bool
-    atomic_compare_exchange_weak(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w);
+    atomic_compare_exchange_weak(shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w);    // Deprecated in C++20, removed in C++26
 template<class T>
     bool
-    atomic_compare_exchange_strong( shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w);
+    atomic_compare_exchange_strong( shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w); // Deprecated in C++20, removed in C++26
 template<class T>
     bool
-    atomic_compare_exchange_weak_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
+    atomic_compare_exchange_weak_explicit(shared_ptr<T>* p, shared_ptr<T>* v,             // Deprecated in C++20, removed in C++26
                                           shared_ptr<T> w, memory_order success,
                                           memory_order failure);
 template<class T>
     bool
-    atomic_compare_exchange_strong_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
+    atomic_compare_exchange_strong_explicit(shared_ptr<T>* p, shared_ptr<T>* v,           // Deprecated in C++20, removed in C++26
                                             shared_ptr<T> w, memory_order success,
                                             memory_order failure);
 // Hash support
diff --git a/libcxx/modules/std/memory.inc b/libcxx/modules/std/memory.inc
index c25e9e3443e9c..f25611f33e442 100644
--- a/libcxx/modules/std/memory.inc
+++ b/libcxx/modules/std/memory.inc
@@ -195,6 +195,9 @@ export namespace std {
 #endif // _LIBCPP_STD_VER >= 23
 
 #if _LIBCPP_HAS_THREADS
+
+#  if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS)
+
   // [depr.util.smartptr.shared.atomic]
   using std::atomic_is_lock_free;
 
@@ -211,5 +214,8 @@ export namespace std {
   using std::atomic_compare_exchange_strong_explicit;
   using std::atomic_compare_exchange_weak;
   using std::atomic_compare_exchange_weak_explicit;
+
+#  endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS)
+
 #endif // _LIBCPP_HAS_THREADS
 } // namespace std
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..3ca01cad451a2
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong.depr_in_cxx20.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// bool
+// atomic_compare_exchange_strong(shared_ptr<T>* p, shared_ptr<T>* v,
+//                                shared_ptr<T> w);                     // Deprecated in C++20, removed in C++26
+
+#include <atomic>
+#include <memory>
+#include <tuple>
+
+void test() {
+  std::shared_ptr<int> p(new int(4));
+  std::shared_ptr<int> v(new int(3));
+  std::shared_ptr<int> w(new int(2));
+  // expected-warning at +1 {{'atomic_compare_exchange_strong<int>' is deprecated}}
+  std::ignore = std::atomic_compare_exchange_strong(&p, &v, w);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..93384772c8afc
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.depr_in_cxx20.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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// bool
+// atomic_compare_exchange_strong_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
+//                                         shared_ptr<T> w, memory_order success,
+//                                         memory_order failure);                 // Deprecated in C++20, removed in C++26
+
+#include <atomic>
+#include <memory>
+#include <tuple>
+
+void test() {
+  std::shared_ptr<int> p(new int(4));
+  std::shared_ptr<int> v(new int(3));
+  std::shared_ptr<int> w(new int(2));
+  // expected-warning at +2 {{'atomic_compare_exchange_strong_explicit<int>' is deprecated}}
+  std::ignore =
+      std::atomic_compare_exchange_strong_explicit(&p, &v, w, std::memory_order_seq_cst, std::memory_order_seq_cst);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..30ca70523fb64
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak.depr_in_cxx20.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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// bool
+// atomic_compare_exchange_weak(shared_ptr<T>* p, shared_ptr<T>* v,
+//                              shared_ptr<T> w);                    // Deprecated in C++20, removed in C++26
+
+#include <memory>
+#include <tuple>
+
+int main(int, char**) {
+  std::shared_ptr<int> p(new int(4));
+  std::shared_ptr<int> v(new int(3));
+  std::shared_ptr<int> w(new int(2));
+  // expected-warning at +1 {{'atomic_compare_exchange_weak<int>' is deprecated}}
+  std::ignore = std::atomic_compare_exchange_weak(&p, &v, w);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..ae6e98d0692d2
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.depr_in_cxx20.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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// bool
+// atomic_compare_exchange_weak_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
+//                                       shared_ptr<T> w, memory_order success,
+//                                       memory_order failure);                 // Deprecated in C++20, removed in C++26
+
+#include <atomic>
+#include <memory>
+#include <tuple>
+
+void test() {
+  std::shared_ptr<int> p(new int(4));
+  std::shared_ptr<int> v(new int(3));
+  std::shared_ptr<int> w(new int(2));
+  // expected-warning at +2 {{'atomic_compare_exchange_weak_explicit<int>' is deprecated}}
+  std::ignore =
+      std::atomic_compare_exchange_weak_explicit(&p, &v, w, std::memory_order_seq_cst, std::memory_order_seq_cst);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange.depr_in_cxx20..verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange.depr_in_cxx20..verify.cpp
new file mode 100644
index 0000000000000..0e4bc1bc50b18
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange.depr_in_cxx20..verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// shared_ptr<T>
+// atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r)   // Deprecated in C++20, removed in C++26
+
+#include <memory>
+#include <tuple>
+
+void test() {
+  std::shared_ptr<int> p(new int(4));
+  std::shared_ptr<int> r(new int(3));
+  // expected-warning at +1 {{'atomic_exchange<int>' is deprecated}}
+  std::ignore = std::atomic_exchange(&p, r);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange_explicit.verify.depr_in_cxx20..cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange_explicit.verify.depr_in_cxx20..cpp
new file mode 100644
index 0000000000000..5a6a2bfe4c1e2
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_exchange_explicit.verify.depr_in_cxx20..cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// shared_ptr<T>
+// atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r)  // Deprecated in C++20, removed in C++26
+
+#include <atomic>
+#include <memory>
+#include <tuple>
+
+void test() {
+  std::shared_ptr<int> p(new int(4));
+  std::shared_ptr<int> r(new int(3));
+  // expected-warning at +1 {{'atomic_exchange_explicit<int>' is deprecated}}
+  std::ignore = std::atomic_exchange_explicit(&p, r, std::memory_order_seq_cst);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_is_lock_free.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_is_lock_free.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..6799d31ce1fa9
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_is_lock_free.depr_in_cxx20.verify.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template<class T>
+// bool
+// atomic_is_lock_free(const shared_ptr<T>* p);    // Deprecated in C++20, removed in C++26
+
+#include <memory>
+#include <tuple>
+
+void test() {
+  const std::shared_ptr<int> p(new int(3));
+  // expected-warning at +1 {{'atomic_is_lock_free<int>' is deprecated}}
+  std::ignore = std::atomic_is_lock_free(&p);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load.depr_in_cxx20..verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load.depr_in_cxx20..verify.cpp
new file mode 100644
index 0000000000000..768ad0ded983e
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load.depr_in_cxx20..verify.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// shared_ptr<T>
+// atomic_load(const shared_ptr<T>* p)      // Deprecated in C++20, removed in C++26
+
+#include <memory>
+#include <tuple>
+
+void test() {
+  std::shared_ptr<int> p(new int(3));
+  // expected-warning at +1 {{'atomic_load<int>' is deprecated}}
+  std::ignore = std::atomic_load(&p);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load_explicit.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load_explicit.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..dc56193324f04
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_load_explicit.depr_in_cxx20.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// shared_ptr<T>
+// atomic_load_explicit(const shared_ptr<T>* p, memory_order mo)     // Deprecated in C++20, removed in C++26
+
+#include <atomic>
+#include <memory>
+#include <tuple>
+
+void test() {
+  const std::shared_ptr<int> p(new int(3));
+  // expected-warning at +1 {{'atomic_load_explicit<int>' is deprecated}}
+  std::ignore = std::atomic_load_explicit(&p, std::memory_order_relaxed);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..38b8df6f24393
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store.depr_in_cxx20.verify.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// void
+// atomic_store(shared_ptr<T>* p, shared_ptr<T> r)   // Deprecated in C++20, removed in C++26
+
+#include <memory>
+
+void test() {
+  std::shared_ptr<int> p;
+  std::shared_ptr<int> r(new int(3));
+  // expected-warning at +1 {{'atomic_store<int>' is deprecated}}
+  std::atomic_store(&p, r);
+}
diff --git a/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store_explicit.depr_in_cxx20.verify.cpp b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store_explicit.depr_in_cxx20.verify.cpp
new file mode 100644
index 0000000000000..18f59c4618699
--- /dev/null
+++ b/libcxx/test/libcxx/depr/depr.util.smartptr.shared.atomic/atomic_store_explicit.depr_in_cxx20.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <memory>
+
+// shared_ptr
+
+// template <class T>
+// void
+// atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo)  // Deprecated in C++20, removed in C++26
+
+#include <atomic>
+#include <memory>
+
+void test() {
+  std::shared_ptr<int> p;
+  std::shared_ptr<int> r(new int(3));
+  // expected-warning at +1 {{'atomic_store_explicit<int>' is deprecated}}
+  std::atomic_store_explicit(&p, r, std::memory_order_seq_cst);
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong.pass.cpp
index 37be6ceea3e09..ad9d2a9d9ccc0 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -15,7 +17,7 @@
 // template <class T>
 // bool
 // atomic_compare_exchange_strong(shared_ptr<T>* p, shared_ptr<T>* v,
-//                                shared_ptr<T> w);
+//                                shared_ptr<T> w);                     // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.pass.cpp
index 3965863b86ccf..26496df96d472 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_strong_explicit.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -16,7 +18,7 @@
 // bool
 // atomic_compare_exchange_strong_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
 //                                         shared_ptr<T> w, memory_order success,
-//                                         memory_order failure);
+//                                         memory_order failure);                 // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak.pass.cpp
index 6dd04f924a105..5cdc0ea3312ff 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -15,7 +17,7 @@
 // template <class T>
 // bool
 // atomic_compare_exchange_weak(shared_ptr<T>* p, shared_ptr<T>* v,
-//                              shared_ptr<T> w);
+//                              shared_ptr<T> w);                    // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.pass.cpp
index 4837fa9793a5e..e4cc4cf77a64e 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_compare_exchange_weak_explicit.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -16,7 +18,7 @@
 // bool
 // atomic_compare_exchange_weak_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
 //                                       shared_ptr<T> w, memory_order success,
-//                                       memory_order failure);
+//                                       memory_order failure);                 // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange.pass.cpp
index f488e0ed1d14e..db02c9d9f75b4 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template <class T>
 // shared_ptr<T>
-// atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r)
+// atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r)   // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange_explicit.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange_explicit.pass.cpp
index 1945f7bba6dc0..704232e71fa4a 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange_explicit.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_exchange_explicit.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template <class T>
 // shared_ptr<T>
-// atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r)
+// atomic_exchange_explicit(shared_ptr<T>* p, shared_ptr<T> r)  // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_is_lock_free.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_is_lock_free.pass.cpp
index 37f7d12eda2c6..8f3b5bbb51558 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_is_lock_free.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_is_lock_free.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template<class T>
 // bool
-// atomic_is_lock_free(const shared_ptr<T>* p);
+// atomic_is_lock_free(const shared_ptr<T>* p);    // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load.pass.cpp
index 1b9a15ac92ac1..9d81e81b4392b 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template <class T>
 // shared_ptr<T>
-// atomic_load(const shared_ptr<T>* p)
+// atomic_load(const shared_ptr<T>* p)      // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load_explicit.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load_explicit.pass.cpp
index 5c2970133328f..f4875dce38a81 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load_explicit.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_load_explicit.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template <class T>
 // shared_ptr<T>
-// atomic_load_explicit(const shared_ptr<T>* p, memory_order mo)
+// atomic_load_explicit(const shared_ptr<T>* p, memory_order mo)     // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store.pass.cpp
index 5b7bd5fad69c4..836dcb16ff32e 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template <class T>
 // void
-// atomic_store(shared_ptr<T>* p, shared_ptr<T> r)
+// atomic_store(shared_ptr<T>* p, shared_ptr<T> r)   // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store_explicit.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store_explicit.pass.cpp
index 5712190421308..85b31c017d736 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store_explicit.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_store_explicit.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX26_REMOVED_SHARED_PTR_ATOMICS
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 // UNSUPPORTED: no-threads
 
 // <memory>
@@ -14,7 +16,7 @@
 
 // template <class T>
 // void
-// atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo)
+// atomic_store_explicit(shared_ptr<T>* p, shared_ptr<T> r, memory_order mo)  // Deprecated in C++20, removed in C++26
 
 // UNSUPPORTED: c++03
 


From libcxx-commits at lists.llvm.org  Fri Jul 17 12:48:52 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 12:48:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add missing test cases
 for `[[nodiscard]]` tests in `enumerate_view` (PR #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5a8724.507fc713.239434.91e4@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/209289

>From de6bef4e4ef9069b23edba3c557e7d576fc78e4a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 12 Jul 2026 18:32:39 +0200
Subject: [PATCH 01/11] add: OtherConst test to sentinel operator-

---
 .../range.enumerate/nodiscard.verify.cpp               | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..d6d93219a4e14 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,7 +64,8 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st = ev.end();
+  auto st   = ev.end();
+  auto c_it = std::as_const(ev).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -74,8 +75,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  st - c_it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  c_it - st;
+
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}

>From bcb2d6392e68580206412414a24029dee3c70881 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:20:42 +0200
Subject: [PATCH 02/11] ref: model non-common non-simple view for sentinel
 tests and general adjustments

---
 .../range.enumerate/nodiscard.verify.cpp      | 38 ++++++++++++-------
 1 file changed, 25 insertions(+), 13 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index d6d93219a4e14..fea1bca7c2145 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,32 +14,41 @@
 #include <utility>
 #include <vector>
 
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+
 void test() {
-  std::vector<int> range;
-  std::ranges::enumerate_view ev{range};
+  auto v  = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.begin();
+  v.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).begin();
+  std::as_const(v).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.end();
+  v.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).end();
+  std::as_const(v).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.size();
+  v.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(ev).size();
+  std::as_const(v).size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  ev.base();
+  v.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::move(ev).base();
+  std::move(v).base();
 
   // [range.enumerate.iterator]
 
-  auto it = ev.begin();
+  auto it = v.begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(it).base();
@@ -64,8 +73,7 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st   = ev.end();
-  auto c_it = std::as_const(ev).begin();
+  auto st = v.end();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -75,6 +83,8 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
+  auto c_it = std::as_const(v).begin();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - c_it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -82,6 +92,8 @@ void test() {
 
   // [range.enumerate.overview]
 
+  std::vector<int> range { 1, 2, 3 };
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
 }

>From 02096b8044eb8b37db4a9cdee0c35da7d297574c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 13 Jul 2026 23:25:17 +0200
Subject: [PATCH 03/11] fix: clang-format

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index fea1bca7c2145..4ebb6196c8d9c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,7 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v  = View{} | std::views::enumerate;
+  auto v = View{} | std::views::enumerate;
 
   // [range.enumerate.view]
 
@@ -92,7 +92,7 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range { 1, 2, 3 };
+  std::vector<int> range{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);

>From 9fda31d2eb80d645f56214599f544ebccb8b724a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:09:56 +0200
Subject: [PATCH 04/11] Update
 libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 4ebb6196c8d9c..b2ce7f3418fe6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -25,7 +25,8 @@ static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
 
 void test() {
-  auto v = View{} | std::views::enumerate;
+  View range;
+  auto v = std::ranges::enumerate_view ev{range};
 
   // [range.enumerate.view]
 

>From 02235acb82379ce5c0bf8011275e5129effbf3ad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:11:36 +0200
Subject: [PATCH 05/11] rename range variable to vec

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index b2ce7f3418fe6..250b21e3e9910 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -93,8 +93,8 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> range{1, 2, 3};
+  std::vector<int> vec{1, 2, 3};
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(range);
+  std::views::enumerate(vec);
 }

>From 4ed811e010d332be499f091c05e411f3f88349a0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 11:16:13 +0200
Subject: [PATCH 06/11] fix: wrong variable initialization

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp    | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 250b21e3e9910..c10e56293246f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -12,7 +12,6 @@
 
 #include <ranges>
 #include <utility>
-#include <vector>
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -26,7 +25,7 @@ static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel
 
 void test() {
   View range;
-  auto v = std::ranges::enumerate_view ev{range};
+  std::ranges::enumerate_view v{range};
 
   // [range.enumerate.view]
 
@@ -93,8 +92,6 @@ void test() {
 
   // [range.enumerate.overview]
 
-  std::vector<int> vec{1, 2, 3};
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::enumerate(vec);
+  std::views::enumerate(range);
 }

>From 214a924d09e6d0d60b8531e2da1422e69c90eae1 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 14 Jul 2026 15:20:49 +0200
Subject: [PATCH 07/11] remove: c_it - st and st - c_st tests. Add
 static_asserts

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp  | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index c10e56293246f..4f99f0842dd11 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -22,6 +22,8 @@ struct View : std::ranges::view_interface<View> {
 static_assert(!std::ranges::common_range<View>);
 static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
 static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::sized_range<View>);
+static_assert(std::ranges::forward_range<View>);
 
 void test() {
   View range;
@@ -83,13 +85,6 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   st - it;
 
-  auto c_it = std::as_const(v).begin();
-
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  st - c_it;
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  c_it - st;
-
   // [range.enumerate.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}

>From e28782a46f3d2c7d612a978652f59dd325655423 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Fri, 17 Jul 2026 19:52:52 +0300
Subject: [PATCH 08/11] Apply suggestion from @Zingam

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp      | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 4f99f0842dd11..16b2340b8357d 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,7 +64,10 @@ void test() {
   std::as_const(it)[2];
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  it + 1;
+// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+it + 1;
+// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+1 + it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   it - 1;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}

>From a2b5b74e069d1b7c24e1419410550c01dc32ce92 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Fri, 17 Jul 2026 19:53:35 +0300
Subject: [PATCH 09/11] Apply suggestion from @Zingam

---
 .../range.adaptors/range.enumerate/nodiscard.verify.cpp   | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 16b2340b8357d..18cd3b4247fba 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -64,10 +64,10 @@ void test() {
   std::as_const(it)[2];
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-it + 1;
-// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   it - 1;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}

>From 8902bfa9d6b98e5f7715e4d9639bc96ed16814aa Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Fri, 17 Jul 2026 19:56:24 +0300
Subject: [PATCH 10/11] Apply suggestion from @Zingam

---
 .../ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 18cd3b4247fba..19b9731cd2b69 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -63,7 +63,6 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(it)[2];
 
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   it + 1;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}

>From 721703d06ebb83f04470e2852519d7f73cc050f2 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 21:48:32 +0200
Subject: [PATCH 11/11] revert: v back to ev

---
 .../range.enumerate/nodiscard.verify.cpp      | 23 ++++++++++---------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 19b9731cd2b69..80021e61067ee 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -27,30 +27,30 @@ static_assert(std::ranges::forward_range<View>);
 
 void test() {
   View range;
-  std::ranges::enumerate_view v{range};
+  std::ranges::enumerate_view ev{range};
 
   // [range.enumerate.view]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  v.begin();
+  ev.begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(v).begin();
+  std::as_const(ev).begin();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  v.end();
+  ev.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(v).end();
+  std::as_const(ev).end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  v.size();
+  ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(v).size();
+  std::as_const(ev).size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  v.base();
+  ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::move(v).base();
+  std::move(ev).base();
 
   // [range.enumerate.iterator]
 
-  auto it = v.begin();
+  auto it = ev.begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(it).base();
@@ -77,7 +77,7 @@ void test() {
 
   // [range.enumerate.sentinel]
 
-  auto st = v.end();
+  auto st = ev.end();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(st).base();
@@ -92,3 +92,4 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
 }
+


From libcxx-commits at lists.llvm.org  Fri Jul 17 13:01:46 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 17 Jul 2026 13:01:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add missing test cases
 for `[[nodiscard]]` tests in `enumerate_view` (PR #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5a8a2a.f1c1923d.2d2b59.9a0b@mx.google.com>


https://github.com/Zingam approved this pull request.

LGTM

Thanks! Looks much better now.

https://github.com/llvm/llvm-project/pull/209289

From libcxx-commits at lists.llvm.org  Fri Jul 17 13:55:58 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 13:55:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 1e37b62 - [lit] Delete the external shell
	(#209571)
Message-ID: <6a5a96de.af6ba3ed.3c8c03.260e@mx.google.com>


Author: Aiden Grossman
Date: 2026-07-17T20:55:53Z
New Revision: 1e37b629fbd06d922ac1216cf20114ea4ce1c88d

URL: https://github.com/llvm/llvm-project/commit/1e37b629fbd06d922ac1216cf20114ea4ce1c88d
DIFF: https://github.com/llvm/llvm-project/commit/1e37b629fbd06d922ac1216cf20114ea4ce1c88d.diff

LOG: [lit] Delete the external shell (#209571)

This was deprecated in LLVM 23 and slated for removal in LLVM 24. Now
that we have branched and everything has been updated to not set
execute_external, we can remove it.

Added: 
    

Modified: 
    libcxx/utils/libcxx/test/format.py
    llvm/utils/lit/lit/TestRunner.py
    llvm/utils/lit/lit/formats/shtest.py

Removed: 
    


################################################################################
diff  --git a/libcxx/utils/libcxx/test/format.py b/libcxx/utils/libcxx/test/format.py
index 49cbe8a8db618..306fae764dc0f 100644
--- a/libcxx/utils/libcxx/test/format.py
+++ b/libcxx/utils/libcxx/test/format.py
@@ -384,10 +384,7 @@ def _executeShTest(self, test, litConfig, steps):
             )
         else:
             _, tmpBase = _getTempPaths(test)
-            useExternalSh = False
-            return lit.TestRunner._runShTest(
-                test, litConfig, useExternalSh, script, tmpBase
-            )
+            return lit.TestRunner._runShTest(test, litConfig, script, tmpBase)
 
     def _generateGenTest(self, testSuite, pathInSuite, litConfig, localConfig):
         generator = lit.Test.Test(testSuite, pathInSuite, localConfig)

diff  --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py
index 97cb54c63be64..da5b56ede0529 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -822,109 +822,6 @@ def make_tree(cmds):
     return out, err, exitCode, timeoutInfo, update_output
 
 
-def executeScript(
-    test, litConfig, tmpBase, commands, cwd
-) -> tuple[str, str, int, str | None, str | None]:
-    bashPath = litConfig.getBashPath()
-    isWin32CMDEXE = litConfig.isWindows and not bashPath
-    script = tmpBase + ".script"
-    if isWin32CMDEXE:
-        script += ".bat"
-
-    # Write script file
-    if isWin32CMDEXE:
-        for i, ln in enumerate(commands):
-            match = re.fullmatch(kPdbgRegex, ln)
-            if match:
-                command = match.group(2)
-                commands[i] = match.expand(
-                    "echo '\\1' > nul && " if command else "echo '\\1' > nul"
-                )
-        with open(script, "w", encoding="utf-8") as f:
-            f.write("@echo on\n")
-            f.write("\n at if %ERRORLEVEL% NEQ 0 EXIT\n".join(commands))
-    else:
-        for i, ln in enumerate(commands):
-            match = re.fullmatch(kPdbgRegex, ln)
-            if match:
-                dbg = match.group(1)
-                command = match.group(2)
-                # Echo the debugging diagnostic to stderr.
-                #
-                # For that echo command, use 'set' commands to suppress the
-                # shell's execution trace, which would just add noise.  Suppress
-                # the shell's execution trace for the 'set' commands by
-                # redirecting their stderr to /dev/null.
-                if command:
-                    msg = f"{shlex.quote(command.lstrip())} \\# '{dbg}'"
-                else:
-                    msg = f"'{dbg}' has no command after substitutions"
-                commands[i] = (
-                    f"{{ set +x; }} 2>/dev/null && "
-                    f"echo {msg} >&2 && "
-                    f"{{ set -x; }} 2>/dev/null"
-                )
-                # Execute the command, if any.
-                #
-                # 'command' might be something like:
-                #
-                #   subcmd & PID=$!
-                #
-                # In that case, we need something like:
-                #
-                #   echo_dbg && { subcmd & PID=$!; }
-                #
-                # Without the '{ ...; }' enclosing the original 'command', '&'
-                # would put all of 'echo_dbg && subcmd' in the background.  This
-                # would cause 'echo_dbg' to execute at the wrong time, and a
-                # later kill of $PID would target the wrong process. We have
-                # seen the latter manage to terminate the shell running lit.
-                if command:
-                    commands[i] += f" && {{ {command}; }}"
-
-        # Manually export any DYLD_* variables used by dyld on macOS because
-        # otherwise they are lost when the shell executable is run, before the
-        # lit test is executed.
-        env_str = "\n".join(
-            "export {}={};".format(k, shlex.quote(v))
-            for k, v in test.config.environment.items()
-            if k.startswith("DYLD_")
-        )
-
-        with open(script, "w", encoding="utf-8", newline="") as f:
-            if test.config.pipefail:
-                f.write("set -o pipefail;")
-            f.write(env_str)
-            f.write("set -x;")
-            f.write("{ " + "; } &&\n{ ".join(commands) + "; }")
-            f.write("\n")
-
-    if isWin32CMDEXE:
-        command = ["cmd", "/c", script]
-    else:
-        if bashPath:
-            command = [bashPath, script]
-        else:
-            command = ["/bin/sh", script]
-        if litConfig.useValgrind:
-            # FIXME: Running valgrind on sh is overkill. We probably could just
-            # run on clang with no real loss.
-            command = litConfig.valgrindArgs + command
-
-    env = dict(test.config.environment)
-    env["LIT_CURRENT_TESTCASE"] = test.getFullName()
-    try:
-        out, err, exitCode = lit.util.executeCommand(
-            command,
-            cwd=cwd,
-            env=env,
-            timeout=test.config.maxIndividualTestTime,
-        )
-        return (out, err, exitCode, None, None)
-    except lit.util.ExecuteCommandTimeoutException as e:
-        return (e.out, e.err, e.exitCode, e.msg, None)
-
-
 def parseIntegratedTestScriptCommands(source_path, keywords):
     """
     parseIntegratedTestScriptCommands(source_path) -> commands
@@ -1807,7 +1704,7 @@ def parseIntegratedTestScript(test, additional_parsers=[], require_script=True):
     return script
 
 
-def _runShTest(test, litConfig, useExternalSh, script, tmpBase) -> lit.Test.Result:
+def _runShTest(test, litConfig, script, tmpBase) -> lit.Test.Result:
     # Always returns the tuple (out, err, exitCode, timeoutInfo, status).
     def runOnce(
         execdir,
@@ -1839,12 +1736,7 @@ def runOnce(
                 scriptCopy[i] = command
 
         try:
-            if useExternalSh:
-                res = executeScript(test, litConfig, tmpBase, scriptCopy, execdir)
-            else:
-                res = executeScriptInternal(
-                    test, litConfig, tmpBase, scriptCopy, execdir
-                )
+            res = executeScriptInternal(test, litConfig, tmpBase, scriptCopy, execdir)
         except ScriptFatal as e:
             out = f"# " + "\n# ".join(str(e).splitlines()) + "\n"
             return out, "", 1, None, Test.UNRESOLVED, None
@@ -1903,25 +1795,7 @@ def runOnce(
     )
 
 
-def _expandLateSubstitutionsExternal(commandLine):
-    filePaths = []
-
-    def _replaceReadFile(match):
-        filePath = match.group(1)
-        filePaths.append(filePath)
-        return "$(cat %s)" % shlex.quote(filePath)
-
-    commandLine = re.sub(r"%{readfile:([^}]*)}", _replaceReadFile, commandLine)
-    # Add test commands before the command to check if the file exists as
-    # cat inside a subshell will never return a non-zero exit code outside
-    # of the subshell.
-    for filePath in filePaths:
-        commandLine = "%s && test -e %s" % (commandLine, filePath)
-    return commandLine
-
-def executeShTest(
-    test, litConfig, useExternalSh, extra_substitutions=[], preamble_commands=[]
-):
+def executeShTest(test, litConfig, extra_substitutions=[], preamble_commands=[]):
     if test.config.unsupported:
         return lit.Test.Result(Test.UNSUPPORTED, "Test is unsupported")
 
@@ -1942,7 +1816,7 @@ def executeShTest(
         test,
         tmpDir,
         tmpBase,
-        normalize_slashes=useExternalSh
+        normalize_slashes=False
         or litConfig.params.get("use_normalized_slashes", False),
     )
     conditions = {feature: True for feature in test.config.available_features}
@@ -1953,8 +1827,4 @@ def executeShTest(
         recursion_limit=test.config.recursiveExpansionLimit,
     )
 
-    if useExternalSh:
-        for index, command in enumerate(script):
-            script[index] = _expandLateSubstitutionsExternal(command)
-
-    return _runShTest(test, litConfig, useExternalSh, script, tmpBase)
+    return _runShTest(test, litConfig, script, tmpBase)

diff  --git a/llvm/utils/lit/lit/formats/shtest.py b/llvm/utils/lit/lit/formats/shtest.py
index 80490c1f3227a..5c91cd4c153bf 100644
--- a/llvm/utils/lit/lit/formats/shtest.py
+++ b/llvm/utils/lit/lit/formats/shtest.py
@@ -18,19 +18,9 @@ class ShTest(FileBasedTest):
 
     def __init__(
         self,
-        execute_external=False,
         extra_substitutions=[],
         preamble_commands=[],
-        force_execute_external=False,
     ):
-        if execute_external and not force_execute_external:
-            raise ValueError(
-                "execute_external=True is deprected as of LLVM-23 and the option will "
-                "be removed in LLVM-24. Please move to using the internal shell "
-                "(execute_external=False). If you still need to force external "
-                "execution to allow time for migration, set force_execute_external=True"
-            )
-        self.execute_external = execute_external
         self.extra_substitutions = extra_substitutions
         self.preamble_commands = preamble_commands
 
@@ -38,7 +28,6 @@ def execute(self, test, litConfig):
         return lit.TestRunner.executeShTest(
             test,
             litConfig,
-            self.execute_external,
             self.extra_substitutions,
             self.preamble_commands,
         )


        

From libcxx-commits at lists.llvm.org  Fri Jul 17 13:56:02 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Fri, 17 Jul 2026 13:56:02 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [lld] [lldb] [llvm] [mlir]
 [polly] [lit] Delete the external shell (PR #209571)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209571@github.com>
Message-ID: <6a5a96e2.5a0412d2.3daf32.5f5c@mx.google.com>


https://github.com/boomanaiden154 closed https://github.com/llvm/llvm-project/pull/209571

From libcxx-commits at lists.llvm.org  Fri Jul 17 14:03:58 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 17 Jul 2026 14:03:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test][NFC] Removed unsupported
 compilers from tests (PR #210413)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210413@github.com>
Message-ID: <6a5a98be.66c0679a.24976c.8b07@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/210413.diff


2 Files Affected:

- (modified) libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp (+1-1) 
- (modified) libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp (+2-2) 


``````````diff
diff --git a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
index 46a4b1c788ef9..f9c910eca1f47 100644
--- a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 // UNSUPPORTED: gcc
-// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22, clang-23, clang-24, apple-clang-17, apple-clang-21
+// UNSUPPORTED: clang-21, clang-22, clang-23, clang-24, apple-clang-21
 
 // ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-dynamic-class-memaccess
 
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp
index 16c326f062f79..ed5c971c4c6e7 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp
@@ -10,8 +10,8 @@
 
 // TODO(LLVM25): remove these guards. Clang <= 22 mis-evaluates
 // std::saturating_mul on non-byte-aligned _BitInt. https://llvm.org/PR204085
-// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22
-// UNSUPPORTED: apple-clang-17, apple-clang-18, apple-clang-19, apple-clang-20, apple-clang-21
+// UNSUPPORTED: clang-21, clang-22
+// UNSUPPORTED: apple-clang-21
 // UNSUPPORTED: target={{.+}}-android{{.*}}
 
 // <numeric>

``````````

</details>


https://github.com/llvm/llvm-project/pull/210413

From libcxx-commits at lists.llvm.org  Fri Jul 17 14:04:29 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 14:04:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5a98dd.d76d81af.23b347.39eb@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/13] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/13] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/13] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/13] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/13] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/13] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/13] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/13] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/13] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/13] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/13] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/13] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/13] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Fri Jul 17 14:52:57 2026
From: libcxx-commits at lists.llvm.org (Andrei Sabalenka via libcxx-commits)
Date: Fri, 17 Jul 2026 14:52:57 -0700 (PDT)
Subject: [libcxx-commits] [clang] [flang] [libcxx] [lldb] [llvm] [mlir]
 [NFC] Silence -Wbool-integral-comparison warnings across LLVM (PR #195246)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/195246@github.com>
Message-ID: <6a5aa439.c4d3d80a.849c3.f305@mx.google.com>


mechakotik wrote:

Ping @arsenm @aaupov @maksfb @rafaelauler @ayermolo @yota9 @paschalis-mpeis @yozhu @JDevlieghere


https://github.com/llvm/llvm-project/pull/195246

From libcxx-commits at lists.llvm.org  Fri Jul 17 14:57:53 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 14:57:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5aa561.6c5720b3.2da3b9.579c@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/14] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/14] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/14] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/14] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/14] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/14] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/14] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/14] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/14] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/14] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/14] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/14] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/14] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 67d88113e5987460d20b2a0001e6aab7e53a57d5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:57:29 +0200
Subject: [PATCH 14/14] add: islessequal.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/islessequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
new file mode 100644
index 0000000000000..98fb3a9e7770a
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::islessequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::islessequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::islessequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::islessequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::islessequal(2.0, 1));     // double vs int
+    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Fri Jul 17 15:00:58 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 15:00:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5aa61a.e5907290.281687.6ea8@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/15] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/15] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/15] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/15] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/15] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/15] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/15] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/15] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/15] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/15] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/15] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/15] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/15] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 67d88113e5987460d20b2a0001e6aab7e53a57d5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:57:29 +0200
Subject: [PATCH 14/15] add: islessequal.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/islessequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
new file mode 100644
index 0000000000000..98fb3a9e7770a
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::islessequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::islessequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::islessequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::islessequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::islessequal(2.0, 1));     // double vs int
+    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 6d1e2ff8878af3188e953f68bb199406ff942354 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 00:00:33 +0200
Subject: [PATCH 15/15] fix: clang-format

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 2 +-
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index 1b9c1aacd0d13..af999db6166b3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -80,7 +80,7 @@ int main(int, char**) {
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));   // int vs float
+    assert(std::isless(1, 2.0f));     // int vs float
     assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 98fb3a9e7770a..2023b062f55f9 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -84,7 +84,7 @@ int main(int, char**) {
   // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::islessequal(2.0, 1));     // double vs int
-    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
   }
 


From libcxx-commits at lists.llvm.org  Fri Jul 17 16:57:35 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 16:57:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5ac16f.a1accbb4.5eca2.313e@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/16] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/16] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/16] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/16] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/16] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/16] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/16] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/16] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/16] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/16] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/16] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/16] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/16] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 67d88113e5987460d20b2a0001e6aab7e53a57d5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:57:29 +0200
Subject: [PATCH 14/16] add: islessequal.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/islessequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
new file mode 100644
index 0000000000000..98fb3a9e7770a
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::islessequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::islessequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::islessequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::islessequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::islessequal(2.0, 1));     // double vs int
+    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 6d1e2ff8878af3188e953f68bb199406ff942354 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 00:00:33 +0200
Subject: [PATCH 15/16] fix: clang-format

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 2 +-
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index 1b9c1aacd0d13..af999db6166b3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -80,7 +80,7 @@ int main(int, char**) {
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));   // int vs float
+    assert(std::isless(1, 2.0f));     // int vs float
     assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 98fb3a9e7770a..2023b062f55f9 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -84,7 +84,7 @@ int main(int, char**) {
   // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::islessequal(2.0, 1));     // double vs int
-    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
   }
 

>From 71b5f4cf216bba7e770e854906b5112d93cbe367 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 01:57:12 +0200
Subject: [PATCH 16/16] add: islessgreater.pass.cpp and isunordered.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  |  2 +-
 .../numerics/c.math/islessgreater.pass.cpp    | 91 +++++++++++++++++++
 .../std/numerics/c.math/isunordered.pass.cpp  | 78 ++++++++++++++++
 3 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
 create mode 100644 libcxx/test/std/numerics/c.math/isunordered.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2023b062f55f9..2b0be4ff37035 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
new file mode 100644
index 0000000000000..40fbb7f4a4e41
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::islessgreater(T(1), T(2)));
+    assert(std::islessgreater(T(2), T(1)));
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    if (!std::is_same<T, bool>::value) {
+      assert(std::islessgreater(T(1), T(2)));
+      assert(std::islessgreater(T(2), T(1)));
+    }
+
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(T(1), T(1)));
+    assert(std::islessgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::islessgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    if (std::is_signed<T>::value) {
+      assert(std::islessgreater(T(-1), T(1)));
+      assert(std::islessgreater(std::numeric_limits<T>::lowest(), T(0)));
+      assert(std::islessgreater(T(0), std::numeric_limits<T>::lowest()));
+      assert(!std::islessgreater(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
new file mode 100644
index 0000000000000..ed7740d195163
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(std::isunordered(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    assert(std::isunordered(std::numeric_limits<T>::signaling_NaN(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Fri Jul 17 17:01:54 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 17:01:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5ac272.e624eb8d.4a8a7.8b3a@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/17] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/17] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/17] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/17] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/17] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/17] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/17] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/17] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/17] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/17] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/17] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/17] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/17] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 67d88113e5987460d20b2a0001e6aab7e53a57d5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:57:29 +0200
Subject: [PATCH 14/17] add: islessequal.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/islessequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
new file mode 100644
index 0000000000000..98fb3a9e7770a
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::islessequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::islessequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::islessequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::islessequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::islessequal(2.0, 1));     // double vs int
+    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 6d1e2ff8878af3188e953f68bb199406ff942354 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 00:00:33 +0200
Subject: [PATCH 15/17] fix: clang-format

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 2 +-
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index 1b9c1aacd0d13..af999db6166b3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -80,7 +80,7 @@ int main(int, char**) {
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));   // int vs float
+    assert(std::isless(1, 2.0f));     // int vs float
     assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 98fb3a9e7770a..2023b062f55f9 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -84,7 +84,7 @@ int main(int, char**) {
   // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::islessequal(2.0, 1));     // double vs int
-    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
   }
 

>From 71b5f4cf216bba7e770e854906b5112d93cbe367 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 01:57:12 +0200
Subject: [PATCH 16/17] add: islessgreater.pass.cpp and isunordered.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  |  2 +-
 .../numerics/c.math/islessgreater.pass.cpp    | 91 +++++++++++++++++++
 .../std/numerics/c.math/isunordered.pass.cpp  | 78 ++++++++++++++++
 3 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
 create mode 100644 libcxx/test/std/numerics/c.math/isunordered.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2023b062f55f9..2b0be4ff37035 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
new file mode 100644
index 0000000000000..40fbb7f4a4e41
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::islessgreater(T(1), T(2)));
+    assert(std::islessgreater(T(2), T(1)));
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    if (!std::is_same<T, bool>::value) {
+      assert(std::islessgreater(T(1), T(2)));
+      assert(std::islessgreater(T(2), T(1)));
+    }
+
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(T(1), T(1)));
+    assert(std::islessgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::islessgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    if (std::is_signed<T>::value) {
+      assert(std::islessgreater(T(-1), T(1)));
+      assert(std::islessgreater(std::numeric_limits<T>::lowest(), T(0)));
+      assert(std::islessgreater(T(0), std::numeric_limits<T>::lowest()));
+      assert(!std::islessgreater(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
new file mode 100644
index 0000000000000..ed7740d195163
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(std::isunordered(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    assert(std::isunordered(std::numeric_limits<T>::signaling_NaN(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From ef949295028c1bd5d6a19881e122393aada59194 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:01:27 +0200
Subject: [PATCH 17/17] flip assert tests for gcc

---
 .../c.math/constexpr-cxx23-gcc.pass.cpp       | 30 +++++++++----------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index 34513b459a056..8d814e81f8bc6 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -225,25 +225,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).


From libcxx-commits at lists.llvm.org  Fri Jul 17 17:03:09 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 17:03:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5ac2bd.0c05e68e.3c2f5d.9ec8@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/18] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/18] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/18] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/18] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/18] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/18] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/18] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/18] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/18] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/18] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/18] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/18] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/18] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 67d88113e5987460d20b2a0001e6aab7e53a57d5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:57:29 +0200
Subject: [PATCH 14/18] add: islessequal.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/islessequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
new file mode 100644
index 0000000000000..98fb3a9e7770a
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::islessequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::islessequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::islessequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::islessequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::islessequal(2.0, 1));     // double vs int
+    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 6d1e2ff8878af3188e953f68bb199406ff942354 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 00:00:33 +0200
Subject: [PATCH 15/18] fix: clang-format

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 2 +-
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index 1b9c1aacd0d13..af999db6166b3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -80,7 +80,7 @@ int main(int, char**) {
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));   // int vs float
+    assert(std::isless(1, 2.0f));     // int vs float
     assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 98fb3a9e7770a..2023b062f55f9 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -84,7 +84,7 @@ int main(int, char**) {
   // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::islessequal(2.0, 1));     // double vs int
-    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
   }
 

>From 71b5f4cf216bba7e770e854906b5112d93cbe367 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 01:57:12 +0200
Subject: [PATCH 16/18] add: islessgreater.pass.cpp and isunordered.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  |  2 +-
 .../numerics/c.math/islessgreater.pass.cpp    | 91 +++++++++++++++++++
 .../std/numerics/c.math/isunordered.pass.cpp  | 78 ++++++++++++++++
 3 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
 create mode 100644 libcxx/test/std/numerics/c.math/isunordered.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2023b062f55f9..2b0be4ff37035 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
new file mode 100644
index 0000000000000..40fbb7f4a4e41
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::islessgreater(T(1), T(2)));
+    assert(std::islessgreater(T(2), T(1)));
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    if (!std::is_same<T, bool>::value) {
+      assert(std::islessgreater(T(1), T(2)));
+      assert(std::islessgreater(T(2), T(1)));
+    }
+
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(T(1), T(1)));
+    assert(std::islessgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::islessgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    if (std::is_signed<T>::value) {
+      assert(std::islessgreater(T(-1), T(1)));
+      assert(std::islessgreater(std::numeric_limits<T>::lowest(), T(0)));
+      assert(std::islessgreater(T(0), std::numeric_limits<T>::lowest()));
+      assert(!std::islessgreater(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
new file mode 100644
index 0000000000000..ed7740d195163
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(std::isunordered(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    assert(std::isunordered(std::numeric_limits<T>::signaling_NaN(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From ef949295028c1bd5d6a19881e122393aada59194 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:01:27 +0200
Subject: [PATCH 17/18] flip assert tests for gcc

---
 .../c.math/constexpr-cxx23-gcc.pass.cpp       | 30 +++++++++----------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index 34513b459a056..8d814e81f8bc6 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -225,25 +225,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From f6a4fa78d49d5bd14219d1cb7885d0d6bf4524ba Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:02:43 +0200
Subject: [PATCH 18/18] fix typo in isgreaterequal.pass.cpp

---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index d6ae553e961e5..f2a4159882654 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows


From libcxx-commits at lists.llvm.org  Fri Jul 17 17:04:48 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 17:04:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5ac320.b4a331f0.d9742.d26f@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/18] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/18] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/18] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/18] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/18] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/18] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/18] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/18] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/18] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/18] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/18] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/18] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/18] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 67d88113e5987460d20b2a0001e6aab7e53a57d5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:57:29 +0200
Subject: [PATCH 14/18] add: islessequal.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/islessequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
new file mode 100644
index 0000000000000..98fb3a9e7770a
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::islessequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::islessequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::islessequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::islessequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::islessequal(2.0, 1));     // double vs int
+    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 6d1e2ff8878af3188e953f68bb199406ff942354 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 00:00:33 +0200
Subject: [PATCH 15/18] fix: clang-format

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 2 +-
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index 1b9c1aacd0d13..af999db6166b3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -80,7 +80,7 @@ int main(int, char**) {
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));   // int vs float
+    assert(std::isless(1, 2.0f));     // int vs float
     assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 98fb3a9e7770a..2023b062f55f9 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -84,7 +84,7 @@ int main(int, char**) {
   // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::islessequal(2.0, 1));     // double vs int
-    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
   }
 

>From 71b5f4cf216bba7e770e854906b5112d93cbe367 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 01:57:12 +0200
Subject: [PATCH 16/18] add: islessgreater.pass.cpp and isunordered.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  |  2 +-
 .../numerics/c.math/islessgreater.pass.cpp    | 91 +++++++++++++++++++
 .../std/numerics/c.math/isunordered.pass.cpp  | 78 ++++++++++++++++
 3 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
 create mode 100644 libcxx/test/std/numerics/c.math/isunordered.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2023b062f55f9..2b0be4ff37035 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
new file mode 100644
index 0000000000000..40fbb7f4a4e41
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::islessgreater(T(1), T(2)));
+    assert(std::islessgreater(T(2), T(1)));
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    if (!std::is_same<T, bool>::value) {
+      assert(std::islessgreater(T(1), T(2)));
+      assert(std::islessgreater(T(2), T(1)));
+    }
+
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(T(1), T(1)));
+    assert(std::islessgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::islessgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    if (std::is_signed<T>::value) {
+      assert(std::islessgreater(T(-1), T(1)));
+      assert(std::islessgreater(std::numeric_limits<T>::lowest(), T(0)));
+      assert(std::islessgreater(T(0), std::numeric_limits<T>::lowest()));
+      assert(!std::islessgreater(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
new file mode 100644
index 0000000000000..ed7740d195163
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(std::isunordered(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    assert(std::isunordered(std::numeric_limits<T>::signaling_NaN(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From ef949295028c1bd5d6a19881e122393aada59194 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:01:27 +0200
Subject: [PATCH 17/18] flip assert tests for gcc

---
 .../c.math/constexpr-cxx23-gcc.pass.cpp       | 30 +++++++++----------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index 34513b459a056..8d814e81f8bc6 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -225,25 +225,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From f6a4fa78d49d5bd14219d1cb7885d0d6bf4524ba Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:02:43 +0200
Subject: [PATCH 18/18] fix typo in isgreaterequal.pass.cpp

---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index d6ae553e961e5..f2a4159882654 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows


From libcxx-commits at lists.llvm.org  Fri Jul 17 17:04:52 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 17:04:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5ac324.f1c1923d.2d2b59.61d0@mx.google.com>


https://github.com/lknknm ready_for_review https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Fri Jul 17 17:05:45 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 17 Jul 2026 17:05:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5ac359.27b3b003.1f84cc.afb1@mx.google.com>


lknknm wrote:

@philnik777 / @frederick-vs-ja 
Please check, all `is<...>` are implemented. 
I'm not sure if all the test cases are correct and covering all possible scenarios, could you please take a look?
Thanks!

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Sat Jul 18 07:25:34 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sat, 18 Jul 2026 07:25:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5b8cde.f3151ff8.37ac76.01b2@mx.google.com>


================
@@ -137,47 +137,52 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
----------------
Zingam wrote:

You'll need to update the synopsis i `<cmath>` with `// constexpr since C++23`.

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Sat Jul 18 09:19:45 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 09:19:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5ba7a1.d681cab9.169270.b536@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/19] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/19] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/19] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/19] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/19] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/19] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/19] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/19] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/19] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/19] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/19] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/19] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/19] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 67d88113e5987460d20b2a0001e6aab7e53a57d5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:57:29 +0200
Subject: [PATCH 14/19] add: islessequal.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/islessequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
new file mode 100644
index 0000000000000..98fb3a9e7770a
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::islessequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::islessequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::islessequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::islessequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::islessequal(2.0, 1));     // double vs int
+    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 6d1e2ff8878af3188e953f68bb199406ff942354 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 00:00:33 +0200
Subject: [PATCH 15/19] fix: clang-format

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 2 +-
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index 1b9c1aacd0d13..af999db6166b3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -80,7 +80,7 @@ int main(int, char**) {
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));   // int vs float
+    assert(std::isless(1, 2.0f));     // int vs float
     assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 98fb3a9e7770a..2023b062f55f9 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -84,7 +84,7 @@ int main(int, char**) {
   // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::islessequal(2.0, 1));     // double vs int
-    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
   }
 

>From 71b5f4cf216bba7e770e854906b5112d93cbe367 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 01:57:12 +0200
Subject: [PATCH 16/19] add: islessgreater.pass.cpp and isunordered.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  |  2 +-
 .../numerics/c.math/islessgreater.pass.cpp    | 91 +++++++++++++++++++
 .../std/numerics/c.math/isunordered.pass.cpp  | 78 ++++++++++++++++
 3 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
 create mode 100644 libcxx/test/std/numerics/c.math/isunordered.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2023b062f55f9..2b0be4ff37035 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
new file mode 100644
index 0000000000000..40fbb7f4a4e41
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::islessgreater(T(1), T(2)));
+    assert(std::islessgreater(T(2), T(1)));
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    if (!std::is_same<T, bool>::value) {
+      assert(std::islessgreater(T(1), T(2)));
+      assert(std::islessgreater(T(2), T(1)));
+    }
+
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(T(1), T(1)));
+    assert(std::islessgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::islessgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    if (std::is_signed<T>::value) {
+      assert(std::islessgreater(T(-1), T(1)));
+      assert(std::islessgreater(std::numeric_limits<T>::lowest(), T(0)));
+      assert(std::islessgreater(T(0), std::numeric_limits<T>::lowest()));
+      assert(!std::islessgreater(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
new file mode 100644
index 0000000000000..ed7740d195163
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(std::isunordered(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    assert(std::isunordered(std::numeric_limits<T>::signaling_NaN(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From ef949295028c1bd5d6a19881e122393aada59194 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:01:27 +0200
Subject: [PATCH 17/19] flip assert tests for gcc

---
 .../c.math/constexpr-cxx23-gcc.pass.cpp       | 30 +++++++++----------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index 34513b459a056..8d814e81f8bc6 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -225,25 +225,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From f6a4fa78d49d5bd14219d1cb7885d0d6bf4524ba Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:02:43 +0200
Subject: [PATCH 18/19] fix typo in isgreaterequal.pass.cpp

---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index d6ae553e961e5..f2a4159882654 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows

>From 62ae94cc86a2b43b61a9ba80eadfc4013bcced15 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 18:18:09 +0200
Subject: [PATCH 19/19] update: cmath synopsis for is<...>

---
 libcxx/include/cmath | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..65bf07aed059b 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -140,17 +140,17 @@ bool signbit(arithmetic x);
 
 int fpclassify(arithmetic x);
 
-bool isfinite(arithmetic x);
-bool isinf(arithmetic x);
-bool isnan(arithmetic x);
-bool isnormal(arithmetic x);
-
-bool isgreater(arithmetic x, arithmetic y);
-bool isgreaterequal(arithmetic x, arithmetic y);
-bool isless(arithmetic x, arithmetic y);
-bool islessequal(arithmetic x, arithmetic y);
-bool islessgreater(arithmetic x, arithmetic y);
-bool isunordered(arithmetic x, arithmetic y);
+bool isfinite(arithmetic x); // constexpr since C++23
+bool isinf(arithmetic x);    // constexpr since C++23
+bool isnan(arithmetic x);    // constexpr since C++23
+bool isnormal(arithmetic x); // constexpr since C++23
+
+bool isgreater(arithmetic x, arithmetic y);      // constexpr since C++23
+bool isgreaterequal(arithmetic x, arithmetic y); // constexpr since C++23
+bool isless(arithmetic x, arithmetic y);         // constexpr since C++23
+bool islessequal(arithmetic x, arithmetic y);    // constexpr since C++23
+bool islessgreater(arithmetic x, arithmetic y);  // constexpr since C++23
+bool isunordered(arithmetic x, arithmetic y);    // constexpr since C++23
 
 floating_point acosh (arithmetic x);
 float          acoshf(float x);


From libcxx-commits at lists.llvm.org  Sat Jul 18 09:20:03 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 09:20:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5ba7b3.4a342cbc.3a2f36.ab38@mx.google.com>


================
@@ -137,47 +137,52 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
----------------
lknknm wrote:

Done, thanks!

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Sat Jul 18 10:37:45 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 18 Jul 2026 10:37:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a5bb9e9.b4a331f0.d9742.93d5@mx.google.com>


================
@@ -0,0 +1,80 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
----------------
frederick-vs-ja wrote:

I believe these `operator()`'s should be static member functions. Was there any reason making them non-static?

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Sat Jul 18 11:16:36 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sat, 18 Jul 2026 11:16:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test][NFC] Removed unsupported
 compilers from tests (PR #210413)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210413@github.com>
Message-ID: <6a5bc304.5a0412d2.3daf32.b16e@mx.google.com>


================
@@ -10,8 +10,8 @@
 
 // TODO(LLVM25): remove these guards. Clang <= 22 mis-evaluates
----------------
philnik777 wrote:

Can we also remove this TODO, since it's not required?

https://github.com/llvm/llvm-project/pull/210413

From libcxx-commits at lists.llvm.org  Sat Jul 18 14:27:20 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 14:27:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
Message-ID: <llvm.org/llvm/llvm-project/pull/210550@github.com>


https://github.com/lknknm created https://github.com/llvm/llvm-project/pull/210550

Closes https://github.com/llvm/llvm-project/issues/105066. 
Implements https://cplusplus.github.io/LWG/issue3664

>From 23293789342393617472800afc72656011d5334f Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:13:24 +0200
Subject: [PATCH] apply: changes from proposal and mark as Complete

---
 libcxx/docs/Status/Cxx23Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 91c4dc462c0c9..0e13c6dc62bfe 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..bbdd30e56730f 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
     }
   }
 


From libcxx-commits at lists.llvm.org  Sat Jul 18 14:27:51 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 14:27:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5befd7.824e11a6.9c376.efe1@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sat Jul 18 14:28:32 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 14:28:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5bf000.449bfb44.7b862.7246@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sat Jul 18 14:29:07 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 18 Jul 2026 14:29:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5bf023.d1496011.199a59.af51@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions h -- libcxx/include/__iterator/distance.h --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index bbdd30e56..e0a259025 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,7 +86,8 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>>
+  operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {

``````````

</details>


https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sat Jul 18 14:32:42 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 14:32:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5bf0fa.6c8668f2.1f24.8567@mx.google.com>


================
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
----------------
lknknm wrote:

Question about this:
The implementation in the standard is the same as above (https://eel.is/c++draft/range.iter.op.distance), but in the LWG3664 proposal it is:
```suggestion
      return __last - static_cast<decay_t<_Ip>&>(__first);
```

Which one should prevail in the scope of this PR?

https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sat Jul 18 14:34:11 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 14:34:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5bf153.e624eb8d.4a8a7.d2e8@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210550

>From 23293789342393617472800afc72656011d5334f Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:13:24 +0200
Subject: [PATCH 1/2] apply: changes from proposal and mark as Complete

---
 libcxx/docs/Status/Cxx23Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 91c4dc462c0c9..0e13c6dc62bfe 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..bbdd30e56730f 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
     }
   }
 

>From cbed72094e96ce8a7756aa588be4dc25f608635b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:33:56 +0200
Subject: [PATCH 2/2] clang-format

---
 libcxx/include/__iterator/distance.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index bbdd30e56730f..e0a2590253c71 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,7 +86,8 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>>
+  operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {


From libcxx-commits at lists.llvm.org  Sat Jul 18 14:44:08 2026
From: libcxx-commits at lists.llvm.org (Avi Patel via libcxx-commits)
Date: Sat, 18 Jul 2026 14:44:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
Message-ID: <llvm.org/llvm/llvm-project/pull/210551@github.com>


https://github.com/firedog1234 created https://github.com/llvm/llvm-project/pull/210551

## Summary

Implements P1901R2 (C++26), adding ownership-based hashing and equality support for `std::shared_ptr` and `std::weak_ptr`.

This change:
- Implements `shared_ptr::owner_hash()` / `weak_ptr::owner_hash()`
- Implements `shared_ptr::owner_equal()` / `weak_ptr::owner_equal()`
- Adds the free function objects `std::owner_hash` and `std::owner_equal`
- Enables `std::weak_ptr` to be used as keys in unordered associative containers

Fixes #105372.

## Test Plan

- Added libc++ tests for:
  - `util.smartptr.shared.obs/`
  - `util.smartptr.weak.obs/`
  - `util.smartptr.ownerhash/`
  - `util.smartptr.ownerequal/`

## Acknowledgements

Claude assisted with test development and documentation.

>From a1178273ee760811d06d3f7a0cdb2a95ba3d4183 Mon Sep 17 00:00:00 2001
From: avipatel <aviipatel06 at gmail.com>
Date: Sat, 18 Jul 2026 15:26:56 -0400
Subject: [PATCH] [libc++] Implement P1901R2: owner_hash and owner_equal for
 shared_ptr/weak_ptr

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/ReleaseNotes/23.rst               |  1 +
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 libcxx/include/__memory/shared_ptr.h          | 71 +++++++++++++++++++
 libcxx/include/memory                         | 32 +++++++++
 libcxx/include/version                        |  2 +-
 .../memory.version.compile.pass.cpp           | 16 ++---
 .../version.version.compile.pass.cpp          | 16 ++---
 .../owner_equal_shared_ptr.pass.cpp           | 40 +++++++++++
 .../owner_equal_weak_ptr.pass.cpp             | 41 +++++++++++
 .../owner_hash.pass.cpp                       | 44 ++++++++++++
 .../owner_equal.pass.cpp                      | 69 ++++++++++++++++++
 .../owner_hash.pass.cpp                       | 49 +++++++++++++
 .../owner_equal_shared_ptr.pass.cpp           | 37 ++++++++++
 .../owner_equal_weak_ptr.pass.cpp             | 42 +++++++++++
 .../owner_hash.pass.cpp                       | 34 +++++++++
 .../generate_feature_test_macro_components.py |  1 -
 17 files changed, 473 insertions(+), 26 deletions(-)
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerhash/owner_hash.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_shared_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_weak_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_hash.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..8a26828240de4 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -526,7 +526,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_senders``                                      *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_smart_ptr_owner_equality``                     *unimplemented*
+    ``__cpp_lib_smart_ptr_owner_equality``                     ``202306L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_span_at``                                      ``202311L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..1dee9843c9c55 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,7 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P1901R2: Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers (`Github <https://llvm.org/PR105372>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..98135f198110c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -12,7 +12,7 @@
 "`P2338R4 <https://wg21.link/P2338R4>`__","Freestanding Library: Character primitives and the C library","2023-06 (Varna)","","","`#105369 <https://github.com/llvm/llvm-project/issues/105369>`__",""
 "`P2013R5 <https://wg21.link/P2013R5>`__","Freestanding Language: Optional ``::operator new``","2023-06 (Varna)","","","`#105370 <https://github.com/llvm/llvm-project/issues/105370>`__",""
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
-"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
+"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","|Complete|","23","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
 "`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index ebd542ba14d01..0770f2a8e383d 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -18,6 +18,7 @@
 #include <__cstddef/ptrdiff_t.h>
 #include <__exception/exception.h>
 #include <__functional/binary_function.h>
+#include <__functional/hash.h>
 #include <__functional/operations.h>
 #include <__functional/reference_wrapper.h>
 #include <__fwd/ostream.h>
@@ -555,6 +556,22 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI shared_ptr {
 
   _LIBCPP_HIDE_FROM_ABI bool __owner_equivalent(const shared_ptr& __p) const { return __cntrl_ == __p.__cntrl_; }
 
+#if _LIBCPP_STD_VER >= 26
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t owner_hash() const _NOEXCEPT {
+    return std::hash<__shared_weak_count*>()(__cntrl_);
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(shared_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(weak_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+#endif
+
 #if _LIBCPP_STD_VER >= 17
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __add_lvalue_reference_t<element_type> operator[](ptrdiff_t __i) const {
     static_assert(is_array<_Tp>::value, "std::shared_ptr<T>::operator[] is only valid when T is an array type.");
@@ -1244,6 +1261,22 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI weak_ptr {
     return __cntrl_ < __r.__cntrl_;
   }
 
+#if _LIBCPP_STD_VER >= 26
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t owner_hash() const _NOEXCEPT {
+    return std::hash<__shared_weak_count*>()(__cntrl_);
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(shared_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(weak_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+#endif
+
   template <class _Up>
   friend class weak_ptr;
   template <class _Up>
@@ -1317,6 +1350,44 @@ struct owner_less<void> {
 };
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+struct owner_hash {
+  template <class _Tp>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t operator()(shared_ptr<_Tp> const& __p) const _NOEXCEPT {
+    return __p.owner_hash();
+  }
+  template <class _Tp>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t operator()(weak_ptr<_Tp> const& __p) const _NOEXCEPT {
+    return __p.owner_hash();
+  }
+  typedef void is_transparent;
+};
+
+struct owner_equal {
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(shared_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(shared_ptr<_Tp> const& __x, weak_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(weak_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(weak_ptr<_Tp> const& __x, weak_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  typedef void is_transparent;
+};
+#endif
+
 template <class _Tp>
 class enable_shared_from_this {
   mutable weak_ptr<_Tp> __weak_this_;
diff --git a/libcxx/include/memory b/libcxx/include/memory
index d5d48ed9a9e6d..03636ddb839b0 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -633,6 +633,9 @@ public:
     explicit operator bool() const noexcept;
     template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
     template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;
+    size_t owner_hash() const noexcept;                                       // C++26
+    template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
+    template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;   // C++26
 };
 
 template<class T>
@@ -771,6 +774,9 @@ public:
     shared_ptr<T> lock() const noexcept;
     template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
     template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;
+    size_t owner_hash() const noexcept;                                       // C++26
+    template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
+    template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;   // C++26
 };
 
 template<class T>
@@ -817,6 +823,32 @@ struct owner_less<void>
     typedef void is_transparent;
 };
 
+// class owner_hash:                                                          // C++26
+struct owner_hash
+{
+    template <class _Tp>
+    size_t operator()(shared_ptr<_Tp> const& __p) const noexcept;
+    template <class _Tp>
+    size_t operator()(weak_ptr<_Tp> const& __p) const noexcept;
+
+    typedef void is_transparent;
+};
+
+// class owner_equal:                                                         // C++26
+struct owner_equal
+{
+    template <class _Tp, class _Up>
+    bool operator()(shared_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const noexcept;
+    template <class _Tp, class _Up>
+    bool operator()(shared_ptr<_Tp> const& __x,   weak_ptr<_Up> const& __y) const noexcept;
+    template <class _Tp, class _Up>
+    bool operator()(  weak_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const noexcept;
+    template <class _Tp, class _Up>
+    bool operator()(  weak_ptr<_Tp> const& __x,   weak_ptr<_Up> const& __y) const noexcept;
+
+    typedef void is_transparent;
+};
+
 template<class T>
 class enable_shared_from_this
 {
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..31b5cc4048e18 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -628,7 +628,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_reference_wrapper                    202403L
 # define __cpp_lib_saturation_arithmetic                202603L
 // # define __cpp_lib_senders                              202406L
-// # define __cpp_lib_smart_ptr_owner_equality             202306L
+# define __cpp_lib_smart_ptr_owner_equality             202306L
 # define __cpp_lib_span_at                              202311L
 # define __cpp_lib_sstream_from_string_view             202306L
 # define __cpp_lib_string_subview                       202506L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
index c58357a463ffd..dc14ff766ac5c 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
@@ -648,17 +648,11 @@
 #    error "__cpp_lib_smart_ptr_for_overwrite should have the value 202002L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
-#    endif
-#    if __cpp_lib_smart_ptr_owner_equality != 202306L
-#      error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_smart_ptr_owner_equality
+#    error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
+#  endif
+#  if __cpp_lib_smart_ptr_owner_equality != 202306L
+#    error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_to_address
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 f2d74cc99d864..11b181a66c518 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
@@ -8024,17 +8024,11 @@
 #    error "__cpp_lib_smart_ptr_for_overwrite should have the value 202002L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
-#    endif
-#    if __cpp_lib_smart_ptr_owner_equality != 202306L
-#      error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_smart_ptr_owner_equality
+#    error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
+#  endif
+#  if __cpp_lib_smart_ptr_owner_equality != 202306L
+#    error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_source_location
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp
new file mode 100644
index 0000000000000..0f8c6935ef811
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// shared_ptr
+
+// template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::shared_ptr<void> empty1;
+  const std::shared_ptr<long> empty2;
+
+  assert(p1.owner_equal(p2));
+  assert(p2.owner_equal(p1));
+  assert(!p1.owner_equal(p3));
+  assert(!p3.owner_equal(p1));
+
+  assert(empty1.owner_equal(empty2));
+  assert(!p1.owner_equal(empty1));
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_equal(p2)), bool);
+  ASSERT_NOEXCEPT(p1.owner_equal(p2));
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp
new file mode 100644
index 0000000000000..6ac9f57e07a05
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <memory>
+
+// shared_ptr
+
+// template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<int> w3(p3);
+  const std::shared_ptr<void> empty_sp;
+  const std::weak_ptr<long> empty_wp;
+
+  assert(p1.owner_equal(w1));
+  assert(p2.owner_equal(w1));
+  assert(!p1.owner_equal(w3));
+  assert(!p3.owner_equal(w1));
+
+  assert(empty_sp.owner_equal(empty_wp));
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_equal(w1)), bool);
+  ASSERT_NOEXCEPT(p1.owner_equal(w1));
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp
new file mode 100644
index 0000000000000..57967b9369fe3
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// shared_ptr
+
+// size_t owner_hash() const noexcept;
+
+#include <memory>
+#include <cassert>
+#include <cstddef>
+#include "test_macros.h"
+
+struct Pair {
+  int a;
+  int b;
+};
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::weak_ptr<int> w1(p1);
+
+  assert(p1.owner_hash() == p2.owner_hash());
+  assert(p1.owner_hash() == w1.owner_hash());
+
+  const std::shared_ptr<Pair> sp(new Pair{1, 2});
+  const std::shared_ptr<int> alias(sp, &sp->b);
+  assert(static_cast<void*>(sp.get()) != static_cast<void*>(alias.get()));
+  assert(sp.owner_hash() == alias.owner_hash());
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_hash()), std::size_t);
+  ASSERT_NOEXCEPT(p1.owner_hash());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp
new file mode 100644
index 0000000000000..1c6c9e8bd5d67
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// struct owner_equal
+// {
+//     template<class T, class U>
+//         bool operator()(shared_ptr<T> const&, shared_ptr<U> const&) const noexcept;
+//     template<class T, class U>
+//         bool operator()(shared_ptr<T> const&, weak_ptr<U> const&) const noexcept;
+//     template<class T, class U>
+//         bool operator()(weak_ptr<T> const&, shared_ptr<U> const&) const noexcept;
+//     template<class T, class U>
+//         bool operator()(weak_ptr<T> const&, weak_ptr<U> const&) const noexcept;
+//
+//     typedef unspecified is_transparent;
+// };
+
+#include <memory>
+#include <cassert>
+#include <type_traits>
+#include <unordered_set>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<int> w3(p3);
+
+  std::owner_equal oe;
+
+  assert(oe(p1, p2));
+  assert(oe(p1, w1));
+  assert(oe(w1, p1));
+  assert(oe(w1, w1));
+
+  assert(!oe(p1, p3));
+  assert(!oe(p1, w3));
+  assert(!oe(w1, p3));
+  assert(!oe(w1, w3));
+
+  ASSERT_SAME_TYPE(decltype(oe(p1, p2)), bool);
+  ASSERT_NOEXCEPT(oe(p1, p2));
+  ASSERT_NOEXCEPT(oe(p1, w1));
+  ASSERT_NOEXCEPT(oe(w1, p1));
+  ASSERT_NOEXCEPT(oe(w1, w1));
+
+  static_assert(std::is_same<std::owner_equal::is_transparent, void>::value, "");
+
+  {
+    std::unordered_set<std::weak_ptr<int>, std::owner_hash, std::owner_equal> s;
+    s.insert(w1);
+    assert(s.count(w1) == 1);
+    assert(s.count(std::weak_ptr<int>(p2)) == 1);
+    assert(s.count(w3) == 0);
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerhash/owner_hash.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerhash/owner_hash.pass.cpp
new file mode 100644
index 0000000000000..0aaa622246023
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerhash/owner_hash.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <memory>
+
+// struct owner_hash
+// {
+//     template<class T>
+//         size_t operator()(shared_ptr<T> const&) const noexcept;
+//     template<class T>
+//         size_t operator()(weak_ptr<T> const&) const noexcept;
+//
+//     typedef unspecified is_transparent;
+// };
+
+#include <memory>
+#include <cassert>
+#include <type_traits>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::weak_ptr<int> w1(p1);
+
+  std::owner_hash oh;
+
+  assert(oh(p1) == p1.owner_hash());
+  assert(oh(w1) == w1.owner_hash());
+
+  assert(oh(p1) == oh(p2));
+  assert(oh(p1) == oh(w1));
+
+  ASSERT_SAME_TYPE(decltype(oh(p1)), std::size_t);
+  ASSERT_SAME_TYPE(decltype(oh(w1)), std::size_t);
+  ASSERT_NOEXCEPT(oh(p1));
+  ASSERT_NOEXCEPT(oh(w1));
+
+  static_assert(std::is_same<std::owner_hash::is_transparent, void>::value, "");
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_shared_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_shared_ptr.pass.cpp
new file mode 100644
index 0000000000000..35c97a72c8c3c
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_shared_ptr.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// weak_ptr
+
+// template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p3(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<void> empty;
+
+  assert(w1.owner_equal(p1));
+  assert(!w1.owner_equal(p3));
+
+  const std::shared_ptr<long> empty_sp;
+  assert(empty.owner_equal(empty_sp));
+
+  ASSERT_SAME_TYPE(decltype(w1.owner_equal(p1)), bool);
+  ASSERT_NOEXCEPT(w1.owner_equal(p1));
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_weak_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_weak_ptr.pass.cpp
new file mode 100644
index 0000000000000..bfd4628edd364
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_weak_ptr.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// weak_ptr
+
+// template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p3(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<int> w2(p1);
+  const std::weak_ptr<int> w3(p3);
+  const std::weak_ptr<void> empty1;
+  const std::weak_ptr<long> empty2;
+
+  assert(w1.owner_equal(w2));
+  assert(w2.owner_equal(w1));
+  assert(!w1.owner_equal(w3));
+  assert(!w3.owner_equal(w1));
+
+  assert(empty1.owner_equal(empty2));
+  assert(!w1.owner_equal(empty1));
+
+  ASSERT_SAME_TYPE(decltype(w1.owner_equal(w2)), bool);
+  ASSERT_NOEXCEPT(w1.owner_equal(w2));
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_hash.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_hash.pass.cpp
new file mode 100644
index 0000000000000..772344f1b05de
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_hash.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <memory>
+
+// weak_ptr
+
+// size_t owner_hash() const noexcept;
+
+#include <memory>
+#include <cassert>
+#include <cstddef>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<int> w2(p1);
+
+  assert(w1.owner_hash() == w2.owner_hash());
+  assert(w1.owner_hash() == p1.owner_hash());
+
+  ASSERT_SAME_TYPE(decltype(w1.owner_hash()), std::size_t);
+  ASSERT_NOEXCEPT(w1.owner_hash());
+
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..56726e485493e 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1316,7 +1316,6 @@ def add_version_header(tc):
                 "c++26": 202306  # P1901R2 Enabling the Use of weak_ptr as Keys in Unordered Associative Containers
             },
             "headers": ["memory"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_source_location",


From libcxx-commits at lists.llvm.org  Sat Jul 18 14:44:43 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 18 Jul 2026 14:44:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a5bf3cb.d2cb4adc.ccd80.1372@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Avi Patel (firedog1234)

<details>
<summary>Changes</summary>

## Summary

Implements P1901R2 (C++26), adding ownership-based hashing and equality support for `std::shared_ptr` and `std::weak_ptr`.

This change:
- Implements `shared_ptr::owner_hash()` / `weak_ptr::owner_hash()`
- Implements `shared_ptr::owner_equal()` / `weak_ptr::owner_equal()`
- Adds the free function objects `std::owner_hash` and `std::owner_equal`
- Enables `std::weak_ptr` to be used as keys in unordered associative containers

Fixes #<!-- -->105372.

## Test Plan

- Added libc++ tests for:
  - `util.smartptr.shared.obs/`
  - `util.smartptr.weak.obs/`
  - `util.smartptr.ownerhash/`
  - `util.smartptr.ownerequal/`

## Acknowledgements

Claude assisted with test development and documentation.

---

Patch is 27.09 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/210551.diff


17 Files Affected:

- (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-1) 
- (modified) libcxx/docs/ReleaseNotes/23.rst (+1) 
- (modified) libcxx/docs/Status/Cxx26Papers.csv (+1-1) 
- (modified) libcxx/include/__memory/shared_ptr.h (+71) 
- (modified) libcxx/include/memory (+32) 
- (modified) libcxx/include/version (+1-1) 
- (modified) libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp (+5-11) 
- (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+5-11) 
- (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp (+40) 
- (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp (+41) 
- (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp (+44) 
- (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp (+69) 
- (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerhash/owner_hash.pass.cpp (+49) 
- (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_shared_ptr.pass.cpp (+37) 
- (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_weak_ptr.pass.cpp (+42) 
- (added) libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_hash.pass.cpp (+34) 
- (modified) libcxx/utils/generate_feature_test_macro_components.py (-1) 


``````````diff
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..8a26828240de4 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -526,7 +526,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_senders``                                      *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_smart_ptr_owner_equality``                     *unimplemented*
+    ``__cpp_lib_smart_ptr_owner_equality``                     ``202306L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_span_at``                                      ``202311L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..1dee9843c9c55 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,7 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P1901R2: Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers (`Github <https://llvm.org/PR105372>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..98135f198110c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -12,7 +12,7 @@
 "`P2338R4 <https://wg21.link/P2338R4>`__","Freestanding Library: Character primitives and the C library","2023-06 (Varna)","","","`#105369 <https://github.com/llvm/llvm-project/issues/105369>`__",""
 "`P2013R5 <https://wg21.link/P2013R5>`__","Freestanding Language: Optional ``::operator new``","2023-06 (Varna)","","","`#105370 <https://github.com/llvm/llvm-project/issues/105370>`__",""
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
-"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
+"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","|Complete|","23","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
 "`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index ebd542ba14d01..0770f2a8e383d 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -18,6 +18,7 @@
 #include <__cstddef/ptrdiff_t.h>
 #include <__exception/exception.h>
 #include <__functional/binary_function.h>
+#include <__functional/hash.h>
 #include <__functional/operations.h>
 #include <__functional/reference_wrapper.h>
 #include <__fwd/ostream.h>
@@ -555,6 +556,22 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI shared_ptr {
 
   _LIBCPP_HIDE_FROM_ABI bool __owner_equivalent(const shared_ptr& __p) const { return __cntrl_ == __p.__cntrl_; }
 
+#if _LIBCPP_STD_VER >= 26
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t owner_hash() const _NOEXCEPT {
+    return std::hash<__shared_weak_count*>()(__cntrl_);
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(shared_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(weak_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+#endif
+
 #if _LIBCPP_STD_VER >= 17
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __add_lvalue_reference_t<element_type> operator[](ptrdiff_t __i) const {
     static_assert(is_array<_Tp>::value, "std::shared_ptr<T>::operator[] is only valid when T is an array type.");
@@ -1244,6 +1261,22 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI weak_ptr {
     return __cntrl_ < __r.__cntrl_;
   }
 
+#if _LIBCPP_STD_VER >= 26
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t owner_hash() const _NOEXCEPT {
+    return std::hash<__shared_weak_count*>()(__cntrl_);
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(shared_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(weak_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+#endif
+
   template <class _Up>
   friend class weak_ptr;
   template <class _Up>
@@ -1317,6 +1350,44 @@ struct owner_less<void> {
 };
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+struct owner_hash {
+  template <class _Tp>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t operator()(shared_ptr<_Tp> const& __p) const _NOEXCEPT {
+    return __p.owner_hash();
+  }
+  template <class _Tp>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t operator()(weak_ptr<_Tp> const& __p) const _NOEXCEPT {
+    return __p.owner_hash();
+  }
+  typedef void is_transparent;
+};
+
+struct owner_equal {
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(shared_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(shared_ptr<_Tp> const& __x, weak_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(weak_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(weak_ptr<_Tp> const& __x, weak_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  typedef void is_transparent;
+};
+#endif
+
 template <class _Tp>
 class enable_shared_from_this {
   mutable weak_ptr<_Tp> __weak_this_;
diff --git a/libcxx/include/memory b/libcxx/include/memory
index d5d48ed9a9e6d..03636ddb839b0 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -633,6 +633,9 @@ public:
     explicit operator bool() const noexcept;
     template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
     template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;
+    size_t owner_hash() const noexcept;                                       // C++26
+    template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
+    template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;   // C++26
 };
 
 template<class T>
@@ -771,6 +774,9 @@ public:
     shared_ptr<T> lock() const noexcept;
     template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
     template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;
+    size_t owner_hash() const noexcept;                                       // C++26
+    template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
+    template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;   // C++26
 };
 
 template<class T>
@@ -817,6 +823,32 @@ struct owner_less<void>
     typedef void is_transparent;
 };
 
+// class owner_hash:                                                          // C++26
+struct owner_hash
+{
+    template <class _Tp>
+    size_t operator()(shared_ptr<_Tp> const& __p) const noexcept;
+    template <class _Tp>
+    size_t operator()(weak_ptr<_Tp> const& __p) const noexcept;
+
+    typedef void is_transparent;
+};
+
+// class owner_equal:                                                         // C++26
+struct owner_equal
+{
+    template <class _Tp, class _Up>
+    bool operator()(shared_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const noexcept;
+    template <class _Tp, class _Up>
+    bool operator()(shared_ptr<_Tp> const& __x,   weak_ptr<_Up> const& __y) const noexcept;
+    template <class _Tp, class _Up>
+    bool operator()(  weak_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const noexcept;
+    template <class _Tp, class _Up>
+    bool operator()(  weak_ptr<_Tp> const& __x,   weak_ptr<_Up> const& __y) const noexcept;
+
+    typedef void is_transparent;
+};
+
 template<class T>
 class enable_shared_from_this
 {
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..31b5cc4048e18 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -628,7 +628,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_reference_wrapper                    202403L
 # define __cpp_lib_saturation_arithmetic                202603L
 // # define __cpp_lib_senders                              202406L
-// # define __cpp_lib_smart_ptr_owner_equality             202306L
+# define __cpp_lib_smart_ptr_owner_equality             202306L
 # define __cpp_lib_span_at                              202311L
 # define __cpp_lib_sstream_from_string_view             202306L
 # define __cpp_lib_string_subview                       202506L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
index c58357a463ffd..dc14ff766ac5c 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
@@ -648,17 +648,11 @@
 #    error "__cpp_lib_smart_ptr_for_overwrite should have the value 202002L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
-#    endif
-#    if __cpp_lib_smart_ptr_owner_equality != 202306L
-#      error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_smart_ptr_owner_equality
+#    error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
+#  endif
+#  if __cpp_lib_smart_ptr_owner_equality != 202306L
+#    error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_to_address
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 f2d74cc99d864..11b181a66c518 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
@@ -8024,17 +8024,11 @@
 #    error "__cpp_lib_smart_ptr_for_overwrite should have the value 202002L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
-#    endif
-#    if __cpp_lib_smart_ptr_owner_equality != 202306L
-#      error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_smart_ptr_owner_equality
+#    error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
+#  endif
+#  if __cpp_lib_smart_ptr_owner_equality != 202306L
+#    error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_source_location
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp
new file mode 100644
index 0000000000000..0f8c6935ef811
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// shared_ptr
+
+// template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::shared_ptr<void> empty1;
+  const std::shared_ptr<long> empty2;
+
+  assert(p1.owner_equal(p2));
+  assert(p2.owner_equal(p1));
+  assert(!p1.owner_equal(p3));
+  assert(!p3.owner_equal(p1));
+
+  assert(empty1.owner_equal(empty2));
+  assert(!p1.owner_equal(empty1));
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_equal(p2)), bool);
+  ASSERT_NOEXCEPT(p1.owner_equal(p2));
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp
new file mode 100644
index 0000000000000..6ac9f57e07a05
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <memory>
+
+// shared_ptr
+
+// template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<int> w3(p3);
+  const std::shared_ptr<void> empty_sp;
+  const std::weak_ptr<long> empty_wp;
+
+  assert(p1.owner_equal(w1));
+  assert(p2.owner_equal(w1));
+  assert(!p1.owner_equal(w3));
+  assert(!p3.owner_equal(w1));
+
+  assert(empty_sp.owner_equal(empty_wp));
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_equal(w1)), bool);
+  ASSERT_NOEXCEPT(p1.owner_equal(w1));
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp
new file mode 100644
index 0000000000000..57967b9369fe3
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// shared_ptr
+
+// size_t owner_hash() const noexcept;
+
+#include <memory>
+#include <cassert>
+#include <cstddef>
+#include "test_macros.h"
+
+struct Pair {
+  int a;
+  int b;
+};
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::weak_ptr<int> w1(p1);
+
+  assert(p1.owner_hash() == p2.owner_hash());
+  assert(p1.owner_hash() == w1.owner_hash());
+
+  const std::shared_ptr<Pair> sp(new Pair{1, 2});
+  const std::shared_ptr<int> alias(sp, &sp->b);
+  assert(static_cast<void*>(sp.get()) != static_cast<void*>(alias.get()));
+  assert(sp.owner_hash() == alias.owner_hash());
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_hash()), std::size_t);
+  ASSERT_NOEXCEPT(p1.owner_hash());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp
new file mode 100644
index 0000000000000..1c6c9e8bd5d67
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// struct owner_equal
+// {
+//     template<class T, class U>
+//         bool operator()(shared_ptr<T> const&, shared_ptr<U> const&) const noexcept;
+//     template<class T, class U>
+//         bool operator()(shared_ptr<T> const&, weak_ptr<U> const&) const noexcept;
+//     template<class T, class U>
+//         bool operator()(weak_ptr<T> const&, shared_ptr<U> const&) const noexcept;
+//     template<class T, class U>
+//         bool operator()(weak_ptr<T> const&, weak_ptr<U> const&) const noexcept;
+//
+//     typedef unspecified is_transparent;
+// };
+
+#include <memory>
+#include <cassert>
+#include <type_traits>
+#include <unordered_set>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<int> w3(p3);
+
+  std::owner_equal oe;
+
+  assert(oe(p1, p2));
+  assert(oe(p1, w1));
+  assert(oe(w1, p1));
+  assert(oe(w1, w1));
+
+  assert(!oe(p1, p3));
+  assert(!oe(p1, w3));
+  assert(!oe(w1, p3));
+  assert(!oe(w1, w3));
+
+  ASSERT_SAME_TYPE(decltype(oe(p1, p2)), bool);
+  ASSERT_NOEXCEPT(oe(p1, p2));
+  ASSERT_NOEXCEPT(oe(p1, w1));
+  ASSERT_NOEXCEPT(oe(w1, p1));
+  ASSERT_NOEXCEPT(oe(w1, w1));
+
+  static_assert(std::is_same<std::owner_equal::is_transparent, void>::value, "");
+
+  {
+    std::unordered_set<std::weak_p...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Sat Jul 18 14:51:14 2026
From: libcxx-commits at lists.llvm.org (Thurston Dang via libcxx-commits)
Date: Sat, 18 Jul 2026 14:51:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][test] Fix typo/regression
 introduced by #171785 (PR #210552)
Message-ID: <llvm.org/llvm/llvm-project/pull/210552@github.com>


https://github.com/thurstond created https://github.com/llvm/llvm-project/pull/210552

If __STDCPP_DEFAULT_NEW_ALIGNMENT__ is not defined, these tests will fail to build due to a typo that was added in #171785.

>From 233cf224c40f415cee306da692054c82a3c9a790 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Sat, 18 Jul 2026 21:47:22 +0000
Subject: [PATCH] [libcxx][test] Fix typo/regression introduced by #171785

If __STDCPP_DEFAULT_NEW_ALIGNMENT__ is not defined, these tests will
fail to build.
---
 .../basic.string/string.capacity/allocation_size.pass.cpp       | 2 +-
 .../strings/basic.string/string.capacity/max_size.pass.cpp      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
index 367a58897cec0..f2208f5ae6934 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
@@ -18,7 +18,7 @@
 #ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
 static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
 #else
-static const std::size_t alginment = 8;
+static const std::size_t alignment = 8;
 #endif
 
 int main(int, char**) {
diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
index 15736e4a1ceed..72d217d5611b9 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -20,7 +20,7 @@
 #ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
 static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
 #else
-static const std::size_t alginment = 8;
+static const std::size_t alignment = 8;
 #endif
 
 template <class = int>


From libcxx-commits at lists.llvm.org  Sat Jul 18 14:51:36 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 14:51:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5bf568.9edb5c9d.280be3.c315@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sat Jul 18 14:51:36 2026
From: libcxx-commits at lists.llvm.org (Thurston Dang via libcxx-commits)
Date: Sat, 18 Jul 2026 14:51:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][test] Fix typo/regression
 introduced by #171785 (PR #210552)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210552@github.com>
Message-ID: <6a5bf568.824e11a6.9c376.8760@mx.google.com>


https://github.com/thurstond edited https://github.com/llvm/llvm-project/pull/210552

From libcxx-commits at lists.llvm.org  Sat Jul 18 14:51:53 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 18 Jul 2026 14:51:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][test] Fix typo/regression
 introduced by #171785 (PR #210552)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210552@github.com>
Message-ID: <6a5bf579.f3151ff8.37ac76.31a3@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Thurston Dang (thurstond)

<details>
<summary>Changes</summary>

If `__STDCPP_DEFAULT_NEW_ALIGNMENT__` is not defined, these tests will fail to build due to a typo that was added in #<!-- -->171785.

---
Full diff: https://github.com/llvm/llvm-project/pull/210552.diff


2 Files Affected:

- (modified) libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp (+1-1) 
- (modified) libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp (+1-1) 


``````````diff
diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
index 367a58897cec0..f2208f5ae6934 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
@@ -18,7 +18,7 @@
 #ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
 static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
 #else
-static const std::size_t alginment = 8;
+static const std::size_t alignment = 8;
 #endif
 
 int main(int, char**) {
diff --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
index 15736e4a1ceed..72d217d5611b9 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -20,7 +20,7 @@
 #ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
 static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
 #else
-static const std::size_t alginment = 8;
+static const std::size_t alignment = 8;
 #endif
 
 template <class = int>

``````````

</details>


https://github.com/llvm/llvm-project/pull/210552

From libcxx-commits at lists.llvm.org  Sat Jul 18 14:58:47 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 14:58:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5bf717.160324b0.1f653e.c0f3@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210550

>From 23293789342393617472800afc72656011d5334f Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:13:24 +0200
Subject: [PATCH 1/3] apply: changes from proposal and mark as Complete

---
 libcxx/docs/Status/Cxx23Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 91c4dc462c0c9..0e13c6dc62bfe 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..bbdd30e56730f 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
     }
   }
 

>From cbed72094e96ce8a7756aa588be4dc25f608635b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:33:56 +0200
Subject: [PATCH 2/3] clang-format

---
 libcxx/include/__iterator/distance.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index bbdd30e56730f..e0a2590253c71 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,7 +86,8 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>>
+  operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {

>From a265367976e6651929998984e3284210eab11585 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:58:32 +0200
Subject: [PATCH 3/3] update: comments in tests

---
 .../range.iter.ops.distance/iterator_sentinel.pass.cpp          | 2 +-
 .../range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index c34d59e405a9e..1888f606465b2 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(I&& first, S last); // TODO: update when LWG3664 is resolved
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <array>
 #include <cassert>
diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
index 164101c2bf5a8..bac0ffba1e7f1 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(const I& first, S last);
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <cassert>
 #include <iterator>


From libcxx-commits at lists.llvm.org  Sat Jul 18 15:11:20 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 15:11:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5bfa08.92ea0e2a.4a9a5.cc99@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210550

>From 23293789342393617472800afc72656011d5334f Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:13:24 +0200
Subject: [PATCH 1/4] apply: changes from proposal and mark as Complete

---
 libcxx/docs/Status/Cxx23Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 91c4dc462c0c9..0e13c6dc62bfe 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..bbdd30e56730f 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
     }
   }
 

>From cbed72094e96ce8a7756aa588be4dc25f608635b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:33:56 +0200
Subject: [PATCH 2/4] clang-format

---
 libcxx/include/__iterator/distance.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index bbdd30e56730f..e0a2590253c71 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,7 +86,8 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>>
+  operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {

>From a265367976e6651929998984e3284210eab11585 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:58:32 +0200
Subject: [PATCH 3/4] update: comments in tests

---
 .../range.iter.ops.distance/iterator_sentinel.pass.cpp          | 2 +-
 .../range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index c34d59e405a9e..1888f606465b2 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(I&& first, S last); // TODO: update when LWG3664 is resolved
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <array>
 #include <cassert>
diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
index 164101c2bf5a8..bac0ffba1e7f1 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(const I& first, S last);
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <cassert>
 #include <iterator>

>From 9452d4068636d3a8c8d31b5602b9446ffd7f7fe4 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 19 Jul 2026 00:11:01 +0200
Subject: [PATCH 4/4] add version number

---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0e13c6dc62bfe..617c89508b7a3 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","24","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""


From libcxx-commits at lists.llvm.org  Sat Jul 18 15:19:40 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 15:19:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5bfbfc.d1496011.199a59.b0ea@mx.google.com>


================
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(const I& first, S last);
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
----------------
lknknm wrote:

According to the original implementation of `distance` in https://github.com/llvm/llvm-project/commit/c965d5448ecdf9a5513983862a78a2ba8f7fbab8 there is a workaround in the implementation and it states it should be reviewed when LWG3664 is resolved.
What is the context of this workaround specifically? Should `lwg3664.pass.cpp` be ported to `iterator_sentinel.pass.cpp`?

https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sat Jul 18 15:20:04 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 15:20:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5bfc14.c4d3d80a.849c3.aa30@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210550

>From 23293789342393617472800afc72656011d5334f Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:13:24 +0200
Subject: [PATCH 1/4] apply: changes from proposal and mark as Complete

---
 libcxx/docs/Status/Cxx23Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 91c4dc462c0c9..0e13c6dc62bfe 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..bbdd30e56730f 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
     }
   }
 

>From cbed72094e96ce8a7756aa588be4dc25f608635b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:33:56 +0200
Subject: [PATCH 2/4] clang-format

---
 libcxx/include/__iterator/distance.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index bbdd30e56730f..e0a2590253c71 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,7 +86,8 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>>
+  operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {

>From a265367976e6651929998984e3284210eab11585 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:58:32 +0200
Subject: [PATCH 3/4] update: comments in tests

---
 .../range.iter.ops.distance/iterator_sentinel.pass.cpp          | 2 +-
 .../range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index c34d59e405a9e..1888f606465b2 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(I&& first, S last); // TODO: update when LWG3664 is resolved
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <array>
 #include <cassert>
diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
index 164101c2bf5a8..bac0ffba1e7f1 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(const I& first, S last);
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <cassert>
 #include <iterator>

>From 9452d4068636d3a8c8d31b5602b9446ffd7f7fe4 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 19 Jul 2026 00:11:01 +0200
Subject: [PATCH 4/4] add version number

---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0e13c6dc62bfe..617c89508b7a3 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","24","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""


From libcxx-commits at lists.llvm.org  Sat Jul 18 15:20:07 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 15:20:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5bfc17.824e11a6.9c376.5646@mx.google.com>


https://github.com/lknknm ready_for_review https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sat Jul 18 15:20:48 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 18 Jul 2026 15:20:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5bfc40.e55dc341.1c6a0.3842@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Lucas Mellone (lknknm)

<details>
<summary>Changes</summary>

Closes https://github.com/llvm/llvm-project/issues/105066. 
References:
- https://wg21.link/LWG3664
- https://eel.is/c++draft/range.iter.op.distance

Follows-up: https://github.com/llvm/llvm-project/commit/c965d5448ecdf9a5513983862a78a2ba8f7fbab8

---
Full diff: https://github.com/llvm/llvm-project/pull/210550.diff


4 Files Affected:

- (modified) libcxx/docs/Status/Cxx23Issues.csv (+1-1) 
- (modified) libcxx/include/__iterator/distance.h (+3-2) 
- (modified) libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp (+1-1) 
- (modified) libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp (+1-1) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 91c4dc462c0c9..617c89508b7a3 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","24","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..e0a2590253c71 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,11 +86,12 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>>
+  operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
     }
   }
 
diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index c34d59e405a9e..1888f606465b2 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(I&& first, S last); // TODO: update when LWG3664 is resolved
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <array>
 #include <cassert>
diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
index 164101c2bf5a8..bac0ffba1e7f1 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(const I& first, S last);
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <cassert>
 #include <iterator>

``````````

</details>


https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sat Jul 18 16:28:01 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 16:28:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5c0c01.d1496011.199a59.9e37@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sat Jul 18 16:28:14 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sat, 18 Jul 2026 16:28:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5c0c0e.f1c1923d.2d2b59.6cda@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sat Jul 18 16:59:38 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 18 Jul 2026 16:59:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a5c136a.510f576d.2f694c.96c1@mx.google.com>


================
@@ -0,0 +1,80 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
----------------
inquisitivecrystal wrote:

No, I can't think of a reason not to. I was copying `ranges::size`, but that has to work on C++20, where a static call operator wouldn't be allowed. Making this static seems like a clear improvement.

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Sat Jul 18 17:34:52 2026
From: libcxx-commits at lists.llvm.org (Thurston Dang via libcxx-commits)
Date: Sat, 18 Jul 2026 17:34:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][test] Fix build failure from
	typo in #171785 (PR #210552)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210552@github.com>
Message-ID: <6a5c1bac.6c5720b3.2da3b9.532d@mx.google.com>


https://github.com/thurstond edited https://github.com/llvm/llvm-project/pull/210552

From libcxx-commits at lists.llvm.org  Sat Jul 18 17:57:19 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 18 Jul 2026 17:57:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a5c20ef.b4a331f0.d9742.8c60@mx.google.com>


inquisitivecrystal wrote:

> I think you are missing the changes to this section: `[range.utility.conv.to]`.

I'd been thinking of that as part of the container changes. But it is in the ranges namespace, so it probably makes more sense to do it here. Will do.

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Sat Jul 18 22:18:27 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Sat, 18 Jul 2026 22:18:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Suppress -Wmicrosoft-cast in
 MSVC's exception_ptr (PR #210570)
Message-ID: <llvm.org/llvm/llvm-project/pull/210570@github.com>


https://github.com/petrhosek created https://github.com/llvm/llvm-project/pull/210570

This was introduced as part of #94977.

>From 56f4fa4e294adbc5aa5bbaef564456f83592aead Mon Sep 17 00:00:00 2001
From: Petr Hosek <phosek at google.com>
Date: Sun, 19 Jul 2026 05:16:43 +0000
Subject: [PATCH] [libcxx] Suppress -Wmicrosoft-cast in MSVC's exception_ptr

This was introduced as part of #94977.
---
 libcxx/src/support/runtime/exception_pointer_msvc.ipp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/src/support/runtime/exception_pointer_msvc.ipp b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
index 3102d1ee93db2..4b877deeec7bc 100644
--- a/libcxx/src/support/runtime/exception_pointer_msvc.ipp
+++ b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
@@ -141,7 +141,7 @@ void __copy_exception_object(void* __dest, const void* __src, const CatchableTyp
 #if _EH_RELATIVE_TYPEINFO
   const auto __copy_func = reinterpret_cast<void*>(__throw_image_base + __type->copyFunction);
 #else // _EH_RELATIVE_TYPEINFO
-  const auto __copy_func = __type->copyFunction;
+  const auto __copy_func = reinterpret_cast<void*>(__type->copyFunction);
 #endif // _EH_RELATIVE_TYPEINFO
 
   const auto __adjusted = __AdjustPointer(const_cast<void*>(__src), __type->thisDisplacement);
@@ -204,7 +204,8 @@ struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) __exception_ptr_normal final :
           __call_member_function_0(__cpp_eh_record.params.pExceptionObject,
                                    reinterpret_cast<void*>(__throw_info->pmfnUnwind + __throw_image_base));
 #else // _EH_RELATIVE_TYPEINFO
-          __call_member_function_0(__cpp_eh_record.params.pExceptionObject, __throw_info->pmfnUnwind);
+          __call_member_function_0(__cpp_eh_record.params.pExceptionObject,
+                                   reinterpret_cast<void*>(__throw_info->pmfnUnwind));
 #endif // _EH_RELATIVE_TYPEINFO
         } else if (__type->properties & CT_IsWinRTHandle) {
           const auto* __unknown = *static_cast<IUnknown* const*>(__cpp_eh_record.params.pExceptionObject);


From libcxx-commits at lists.llvm.org  Sat Jul 18 22:18:54 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Sat, 18 Jul 2026 22:18:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Suppress -Wmicrosoft-cast
 in MSVC's exception_ptr (PR #210570)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210570@github.com>
Message-ID: <6a5c5e3e.492e23e4.d19bc.e4f4@mx.google.com>


https://github.com/petrhosek edited https://github.com/llvm/llvm-project/pull/210570

From libcxx-commits at lists.llvm.org  Sat Jul 18 22:18:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 18 Jul 2026 22:18:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Suppress -Wmicrosoft-cast
 in MSVC's exception_ptr (PR #210570)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210570@github.com>
Message-ID: <6a5c5e3f.c56fb102.2d07b8.022b@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Petr Hosek (petrhosek)

<details>
<summary>Changes</summary>

This was introduced as part of #<!-- -->94977.

---
Full diff: https://github.com/llvm/llvm-project/pull/210570.diff


1 Files Affected:

- (modified) libcxx/src/support/runtime/exception_pointer_msvc.ipp (+3-2) 


``````````diff
diff --git a/libcxx/src/support/runtime/exception_pointer_msvc.ipp b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
index 3102d1ee93db2..4b877deeec7bc 100644
--- a/libcxx/src/support/runtime/exception_pointer_msvc.ipp
+++ b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
@@ -141,7 +141,7 @@ void __copy_exception_object(void* __dest, const void* __src, const CatchableTyp
 #if _EH_RELATIVE_TYPEINFO
   const auto __copy_func = reinterpret_cast<void*>(__throw_image_base + __type->copyFunction);
 #else // _EH_RELATIVE_TYPEINFO
-  const auto __copy_func = __type->copyFunction;
+  const auto __copy_func = reinterpret_cast<void*>(__type->copyFunction);
 #endif // _EH_RELATIVE_TYPEINFO
 
   const auto __adjusted = __AdjustPointer(const_cast<void*>(__src), __type->thisDisplacement);
@@ -204,7 +204,8 @@ struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) __exception_ptr_normal final :
           __call_member_function_0(__cpp_eh_record.params.pExceptionObject,
                                    reinterpret_cast<void*>(__throw_info->pmfnUnwind + __throw_image_base));
 #else // _EH_RELATIVE_TYPEINFO
-          __call_member_function_0(__cpp_eh_record.params.pExceptionObject, __throw_info->pmfnUnwind);
+          __call_member_function_0(__cpp_eh_record.params.pExceptionObject,
+                                   reinterpret_cast<void*>(__throw_info->pmfnUnwind));
 #endif // _EH_RELATIVE_TYPEINFO
         } else if (__type->properties & CT_IsWinRTHandle) {
           const auto* __unknown = *static_cast<IUnknown* const*>(__cpp_eh_record.params.pExceptionObject);

``````````

</details>


https://github.com/llvm/llvm-project/pull/210570

From libcxx-commits at lists.llvm.org  Sat Jul 18 22:20:11 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Sat, 18 Jul 2026 22:20:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add MSVC's implementation of
 `exception_ptr` for Windows (PR #94977)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/94977@github.com>
Message-ID: <6a5c5e8b.f3151ff8.37ac76.54ee@mx.google.com>


petrhosek wrote:

> log: https://logs.chromium.org/logs/chromium/buildbucket/cr-buildbucket/8676372941358177969/+/u/compile__with_patch_/stdout?format=raw
> 
> (look for "failed:")
> 
> It uses clang-cl.
> 
> Failing build command:
> 
> ```
> [8311/117871] 28.00s E CXX obj/buildtools/third_party/libc++/libc++/exception.obj	
> FAILED: 8bf031e0-eb2f-4d4d-8441-0f3c69035378 "./obj/buildtools/third_party/libc++/libc++/exception.obj" CXX obj/buildtools/third_party/libc++/libc++/exception.obj
> err: exit=1
> ..\..\third_party\llvm-build\Release+Asserts\bin\clang-cl.exe /c ../../third_party/libc++/src/src/exception.cpp /Foobj/buildtools/third_party/libc++/libc++/exception.obj /nologo /showIncludes:user /winsysroot../../third_party/depot_tools/win_toolchain/vs_files/e66617bc68 "-DCR_CLANG_REVISION=\"llvmorg-23-init-19482-g53d18800-1\"" -DCOMPONENT_BUILD -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE -D_LIBCPP_INSTRUMENTED_WITH_ASAN=0 -DCR_LIBCXX_REVISION=9d57af17811b286bda053a9887db849e6ab21482 -D__STD_C -D_CRT_RAND_S -D_CRT_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_DEPRECATE -D_ATL_NO_OPENGL -D_WINDOWS -DCERT_CHAIN_PARA_HAS_EXTRA_FIELDS -DPSAPI_VERSION=2 -DWIN32 -D_SECURE_ATL -DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP -DDCHECK_ALWAYS_ON=1 -DUSE_AURA=1 -DWIN32_LEAN_AND_MEAN -DNOMINMAX -D_UNICODE -DUNICODE -DWINCRYPT_USE_SYMBOL_PREFIX -D_DEBUG -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DNTDDI_VERSION=NTDDI_WIN7 -D_WIN32_WINNT=_WIN32_WINNT_WIN7 -DWINVER=_WIN32_WINNT_WIN7 -D_LIBCPP_BUILDING_LIBRARY -D_CRT_NONSTDC_NO_WARNINGS -D_WINSOCK_DEPRECATED_NO_WARNINGS -DLIBC_NAMESPACE=__llvm_libc_cr -I../../third_party/libc++/src/src -I../.. -Igen -I../../buildtools/third_party/libc++ -I../../third_party/llvm-libc/src -fno-delete-null-pointer-checks /clang:-fno-strict-overflow -fno-ident /clang:-fno-math-errno -fcolor-diagnostics /clang:-fdiagnostics-show-inlining-chain -fmerge-all-constants -fcrash-diagnostics-dir=../clang-crashreports -fno-lifetime-dse -mllvm -instcombine-lower-dbg-declare=0 -mllvm -split-threshold-for-reg-with-hint=0 /clang:-ffp-contract=off -fcomplete-member-pointers /Gy /FS /bigobj /utf-8 /Zc:twoPhase -ffile-reproducible /D__WRL_ENABLE_FUNCTION_STATICS__ -fmsc-version=1934 /Zc:dllexportInlines- -m32 -msse3 /Brepro -Wno-builtin-macro-redefined -D__DATE__= -D__TIME__= -D__TIMESTAMP__= -ffile-compilation-dir=. -no-canonical-prefixes --warning-suppression-mappings=../../build/config/warning_suppression.txt -ftrivial-auto-var-init=pattern /Od /Ob0 /GF -fsanitize=array-bounds -fsanitize-trap=array-bounds -fsanitize-ignore-for-ubsan-feature=array-bounds -Wno-unsafe-buffer-usage-in-static-sized-array -fsanitize=return -fsanitize-trap=return -fsanitize-ignore-for-ubsan-feature=return /guard:cf,nochecks /MDd -Wheader-hygiene -Wstring-conversion -Wtautological-overlap-compare -Xclang -add-plugin -Xclang find-bad-constructs -Xclang -plugin-arg-find-bad-constructs -Xclang check-stack-allocated -Xclang -plugin-arg-find-bad-constructs -Xclang relax-ctor-checks-for-aggregates -Xclang -add-plugin -Xclang raw-ptr-plugin -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-to-stack-allocated -Xclang -plugin-arg-raw-ptr-plugin -Xclang disable-check-raw-ptr-to-stack-allocated-error -Xclang -plugin-arg-raw-ptr-plugin -Xclang raw-ptr-exclude-path=../../third_party/dawn/ -Xclang -plugin-arg-raw-ptr-plugin -Xclang raw-ptr-exclude-path=../../build/config/clang/chrome/test/fuzzing/webidl_fuzzing/ -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-span-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ref-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang no-special-treatment-for-oilpanized-paths -DUNSAFE_BUFFERS_BUILD -Wunsafe-buffer-usage -Wno-error=unsafe-buffer-usage -Xclang -add-plugin -Xclang unsafe-buffers -Xclang -plugin-arg-unsafe-buffers -Xclang ../../build/config/unsafe_buffers_paths.txt -fstrict-aliasing -Wundef -Wno-pragma-clang-attribute -Wno-ignored-attributes -Wno-macro-redefined /W3 /wd4800 /wd4267 -Wno-unused-variable -Wno-unused-template -Wno-c++11-narrowing -Wno-unused-but-set-variable -Wno-missing-field-initializers -Wno-unused-parameter -Wno-psabi -Wloop-analysis -Wno-unneeded-internal-declaration -Wno-cast-function-type -Wno-thread-safety-reference-return -Wno-nontrivial-memcall -Wno-uninitialized-const-pointer -Wno-unused-but-set-global -Wno-nonportable-include-path -Wexit-time-destructors /WX -Wno-exit-time-destructors -Wno-exit-time-destructors /std:c++23preview -Wno-trigraphs -I../../third_party/libc++/src/include -std:c++latest -Wno-invalid-offsetof -Wenum-compare-conditional -Wno-nullability-completeness -Wunique-object-duplication /EHsc /GR /TP  "-fmodule-name=//buildtools/third_party/libc++:libc++_Private" /Fd"obj/buildtools/third_party/libc++/libc++_cc.pdb"
> build step: cxx "./obj/buildtools/third_party/libc++/libc++/exception.obj"
> siso_rule: clang-cl/cxx
> stderr:
> In file included from ../../third_party/libc++/src/src/exception.cpp:16:
> ../../third_party/libc++/src/src\support/runtime/exception_pointer_msvc.ipp(149,38): error: implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension [-Werror,-Wmicrosoft-cast]
>   149 |     __call_member_function_2(__dest, __copy_func, __adjusted, 1);
>       |                                      ^~~~~~~~~~~
> ../../third_party/libc++/src/src\support/runtime/exception_pointer_msvc.ipp(151,38): error: implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension [-Werror,-Wmicrosoft-cast]
>   151 |     __call_member_function_1(__dest, __copy_func, __adjusted);
>       |                                      ^~~~~~~~~~~
> ../../third_party/libc++/src/src\support/runtime/exception_pointer_msvc.ipp(207,77): error: implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension [-Werror,-Wmicrosoft-cast]
>   207 |           __call_member_function_0(__cpp_eh_record.params.pExceptionObject, __throw_info->pmfnUnwind);
>       |                                                                             ^~~~~~~~~~~~~~~~~~~~~~~~
> 3 errors generated.
> ```

https://github.com/llvm/llvm-project/pull/210570

https://github.com/llvm/llvm-project/pull/94977

From libcxx-commits at lists.llvm.org  Sat Jul 18 23:05:22 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 18 Jul 2026 23:05:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a5c6922.92ea0e2a.4a9a5.7e79@mx.google.com>


https://github.com/inquisitivecrystal updated https://github.com/llvm/llvm-project/pull/206385

>From 315066dead028c4df0b8345a1517ae234fc890ff Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:31:10 -0700
Subject: [PATCH 01/32] Implement ranges::reserve_hint CPO

---
 libcxx/include/__ranges/size.h | 53 ++++++++++++++++++++++++++++++++++
 libcxx/include/ranges          |  1 +
 2 files changed, 54 insertions(+)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 5da3a6ff268e8..3dfeade03fce5 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,6 +131,59 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __freestanding_reserve_hint =
+    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
+  template <__std_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__freestanding_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index d82a41942327b..6448e74edb28b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -26,6 +26,7 @@ namespace std::ranges {
 
     inline constexpr unspecified size = unspecified;
     inline constexpr unspecified ssize = unspecified;
+    inline constexpr unspecified reserve_hint = unspecified; // Since C++26
   }
 
   // [range.range], ranges

>From c8fca33b7b0d382d3eed0d39adaf2e305a31b35b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:19 -0700
Subject: [PATCH 02/32] Implement approximately_sized_range concept

---
 libcxx/include/__ranges/concepts.h | 14 ++++++++++++++
 libcxx/include/ranges              |  4 ++++
 2 files changed, 18 insertions(+)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index bf75fe8a6fef4..e7cfc94506120 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -80,10 +80,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_common_reference_t = iter_common_reference_t<iterator_t<_Rp>>;
 
+#  if _LIBCPP_STD_VER >= 26
+
+// [range.approximately.sized]
+template <class _Tp>
+concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); };
+
+// [range.sized]
+template <class _Tp>
+concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
+
+#  else // _LIBCPP_STD_VER < 26
+
 // [range.sized]
 template <class _Tp>
 concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
+#  endif
+
 template <sized_range _Rp>
 using range_size_t = decltype(ranges::size(std::declval<_Rp&>()));
 
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 6448e74edb28b..8e0d464bca6ea 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -53,6 +53,10 @@ namespace std::ranges {
   template <range R>
     using range_common_reference_t = iter_common_reference_t<iterator_t<R>>;
 
+  // [range.approximately.sized], approximately sized ranges
+  template<class T>
+    concept approximately_sized_range = ...;
+
   // [range.sized], sized ranges
   template<class>
     inline constexpr bool disable_sized_range = false;

>From f96ffb52ebc591564db86d673ea708a6f873f712 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:32:53 -0700
Subject: [PATCH 03/32] Implement reserve_hint for views

---
 .../__ranges/adjacent_transform_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/adjacent_view.h       | 24 +++++++++++++++++++
 libcxx/include/__ranges/as_rvalue_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/common_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/drop_view.h           | 18 ++++++++++++++
 libcxx/include/__ranges/elements_view.h       | 16 +++++++++++++
 libcxx/include/__ranges/enumerate_view.h      | 16 +++++++++++++
 libcxx/include/__ranges/owning_view.h         | 16 +++++++++++++
 libcxx/include/__ranges/ref_view.h            |  9 +++++++
 libcxx/include/__ranges/reverse_view.h        | 16 +++++++++++++
 libcxx/include/__ranges/stride_view.h         | 19 +++++++++++++++
 libcxx/include/__ranges/take_view.h           | 20 ++++++++++++++++
 libcxx/include/__ranges/transform_view.h      | 16 +++++++++++++
 13 files changed, 218 insertions(+)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 4863d074482dc..e7e05539e8aaa 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
   {
     return __inner_.size();
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _InnerView>
+  {
+    return __inner_.reserve_hint();
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 40474b85c794f..27b116e30eee9 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -139,6 +139,30 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     __sz -= std::min<_CT>(__sz, _Np - 1);
     return static_cast<_ST>(__sz);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    using _ST = decltype(ranges::reserve_hint(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index a553f39998e0e..705cf559a7ee0 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -99,6 +99,22 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index eec1045c8a758..4f1dfbf5d9ac2 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -101,6 +101,22 @@ class common_view : public view_interface<common_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index e3754e64d536f..9cae5c1c30276 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -139,6 +139,24 @@ class drop_view : public view_interface<drop_view<_View>> {
   {
     return __size(*this);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index b1419f2a1dd91..9ce87e6814dbe 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -133,6 +133,22 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::size(__base_);
   }
 
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
+
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
 };
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index bb7696897af00..1a67a23e864d7 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -115,6 +115,22 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return __base_;
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 1ab81afee774a..b1e8415c3a477 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -99,6 +99,22 @@ class owning_view : public view_interface<owning_view<_Rp>> {
   {
     return ranges::data(__r_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _Rp>
+  {
+    return ranges::reserve_hint(__r_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 109a10cec299e..34f129ace54e7 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -73,6 +73,15 @@ class ref_view : public view_interface<ref_view<_Range>> {
   {
     return ranges::data(*__range_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<_Range>
+  {
+    return ranges::reserve_hint(*__range_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index b016cc231f2b5..fc8e609705661 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -111,6 +111,22 @@ class reverse_view : public view_interface<reverse_view<_View>> {
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..d4b7cce06eb91 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
@@ -129,6 +130,24 @@ class stride_view : public view_interface<stride_view<_View>> {
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
+    return __to_unsigned_like(__div_ceil(__s, __stride_));
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..f97a88ccf9297 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -155,6 +155,26 @@ class take_view : public view_interface<take_view<_View>> {
     auto __n = ranges::size(__base_);
     return ranges::min(__n, static_cast<decltype(__n)>(__count_));
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
+    if constexpr (approximately_sized_range<_View>) {
+      auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
+    if constexpr (approximately_sized_range<const _View>) {
+      auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
+      return __to_unsigned_like(ranges::min(__n, __count_));
+    }
+    return __to_unsigned_like(__count_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 1484be006e841..c26b7ea362a8d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -139,6 +139,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   {
     return ranges::size(__base_);
   }
+
+#  if _LIBCPP_STD_VER >= 26
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires approximately_sized_range<_View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires approximately_sized_range<const _View>
+  {
+    return ranges::reserve_hint(__base_);
+  }
+
+#  endif //_LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 04c8de26c3707ebea7247894855612f120acb1c0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 24 Jun 2026 23:35:18 -0700
Subject: [PATCH 04/32] Implement reserve_hint for concat_view (LWG4553)

---
 libcxx/include/__ranges/concat_view.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 3bbe9db12e0f8..33d269b9fd09a 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -176,6 +176,28 @@ class concat_view : public view_interface<concat_view<_Views...>> {
         [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
         std::__tuple_transform(ranges::size, __views_));
   }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
+    requires(approximately_sized_range<_Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
+    requires(approximately_sized_range<const _Views> && ...)
+  {
+    return apply(
+        [](auto... __sizes) static {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return (_CT(__sizes) + ...);
+        },
+        std::__tuple_transform(ranges::reserve_hint, __views_));
+  }
 };
 
 template <class... _Views>

>From 5938495f415e0b8ecf51efe47d5e9189f5968a19 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 28 May 2026 01:02:44 -0700
Subject: [PATCH 05/32] Add new items to module

---
 libcxx/modules/std/ranges.inc | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..9e87e2653908a 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -25,6 +25,10 @@ export namespace std {
       using std::ranges::__cpo::empty;
       using std::ranges::__cpo::size;
       using std::ranges::__cpo::ssize;
+
+#if _LIBCPP_STD_VER >= 26
+      using std::ranges::__cpo::reserve_hint;
+#endif
     } // namespace __cpo
 
     // [range.range], ranges
@@ -50,6 +54,11 @@ export namespace std {
     using std::ranges::disable_sized_range;
     using std::ranges::sized_range;
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.approximately.sized], approximately sized ranges
+    using std::ranges::approximately_sized_range;
+#endif
+
     // [range.view], views
     using std::ranges::enable_view;
     using std::ranges::view;

>From e9a3212c848a9d2a47e1634869b016b488481ee0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 00:12:01 -0700
Subject: [PATCH 06/32] Test reserve_hint

---
 .../ranges/range.access/reserve_hint.pass.cpp | 100 ++++++++++++++
 .../range.access/reserve_hint.verify.cpp      |  21 +++
 .../reserve_hint.pass.cpp                     | 130 ++++++++++++++++++
 .../range.adjacent/reserve_hint.pass.cpp      | 108 +++++++++++++++
 .../range.owning.view/reserve_hint.pass.cpp   |  86 ++++++++++++
 .../range.ref.view/range.ref.view.pass.cpp    |  29 ++++
 .../range.as.rvalue/reserve_hint.pass.cpp     |  90 ++++++++++++
 .../range.common.view/reserve_hint.pass.cpp   |  63 +++++++++
 .../range.adaptors/range.common.view/types.h  |  31 +++++
 .../range.concat/reserve_hint.pass.cpp        | 107 ++++++++++++++
 .../range.drop/reserve_hint.pass.cpp          |  59 ++++++++
 .../ranges/range.adaptors/range.drop/types.h  |  14 ++
 .../range.elements/reserve_hint.pass.cpp      |  90 ++++++++++++
 .../range.enumerate/reserve_hint.pass.cpp     |  82 +++++++++++
 .../range.reverse/reserve_hint.pass.cpp       |  89 ++++++++++++
 .../range.stride.view/reserve_hint.pass.cpp   |  99 +++++++++++++
 .../range.adaptors/range.stride.view/types.h  |  19 ++-
 .../range.take/reserve_hint.pass.cpp          |  87 ++++++++++++
 .../ranges/range.adaptors/range.take/types.h  |  14 ++
 .../range.transform/reserve_hint.pass.cpp     |  44 ++++++
 .../range.adaptors/range.transform/types.h    |  33 +++++
 21 files changed, 1391 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..4e5f11fba445e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+struct Incomplete;
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+
+extern int bounded_array[42];
+
+struct SizedSentinelRange {
+  int data_[42] = {};
+  constexpr int* begin() { return data_; }
+  constexpr auto end() { return sized_sentinel<int*>(data_ + 42); }
+};
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+};
+
+struct HasReserveHintMemberBool {
+  constexpr bool reserve_hint() { return false; }
+};
+
+static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>);
+
+static_assert(std::ranges::reserve_hint(bounded_array) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
+
+bool constexpr test_sized_sentinel_range() {
+  SizedSentinelRange b;
+  assert(std::ranges::reserve_hint(b) == 42);
+  ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t);
+
+  return true;
+}
+
+static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t);
+
+static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42);
+ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t);
+
+// test that the order of preference is ranges::size, then member reserve_hint,
+// then function reserve_hint
+struct HasSizeAndReserveHint {
+  constexpr std::size_t size() { return 42; }
+  constexpr std::size_t reserve_hint() { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+};
+
+struct HasReserveHintMemberAndFunction {
+  constexpr std::size_t reserve_hint() { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+};
+
+static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);
+static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42);
+
+int main(int, char**) {
+  test_sized_sentinel_range();
+  static_assert(test_sized_sentinel_range());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
new file mode 100644
index 0000000000000..760620c6bd271
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::ranges::reserve_hint
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
+void test() {
+  std::ranges::reserve_hint(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..291810bfecf7d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<InnerView>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const InnerView>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "helpers.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+// Test with different values of N for an approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_approx_sized_view() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+  assert(std::as_const(v).reserve_hint() == expected_hint);
+}
+
+// Test with different values of N for a non-const approximately sized view
+template <std::size_t N, class Fn>
+constexpr void test_nonconst_approx_sized() {
+  // non-const-only reserve_hint
+  std::ranges::adjacent_transform_view<ApproximatelySizedNotConstView, Fn, N> v(
+      ApproximatelySizedNotConstView(5), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+  auto expected_hint = 5 - (N - 1);
+  assert(v.reserve_hint() == expected_hint);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(0), Fn{});
+  static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+  static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+  assert(v.reserve_hint() == 0);
+  assert(std::as_const(v).reserve_hint() == 0);
+}
+
+template <std::size_t N, class Fn>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_transform_view<ApproximatelySizedView, Fn, N> v(ApproximatelySizedView(2), Fn{});
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+}
+
+template <std::size_t N, class Fn>
+constexpr void test() {
+  test_approx_sized_view<N, Fn>();
+  test_nonconst_approx_sized<N, Fn>();
+  test_empty_range<N, Fn>();
+  test_N_greater_than_size<N, Fn>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<N, MakeTuple>();
+  test<N, Tie>();
+  test<N, GetFirst>();
+  test<N, Multiply>();
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<
+                std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+  static_assert(!std::ranges::approximately_sized_range<
+                const std::ranges::adjacent_transform_view<NonApproximatelySizedView, Multiply, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..d55e90112fad3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<View>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const View>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + size_); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test with different values of N for an approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+    assert(std::as_const(v).reserve_hint() == expected_hint);
+  }
+  {
+    // Test with different values of N for a non-const approximately sized view
+    std::ranges::adjacent_view<ApproximatelySizedNotConstView, N> v(ApproximatelySizedNotConstView(5));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<const decltype(v)>);
+
+    auto expected_hint = 5 - (N - 1);
+    assert(v.reserve_hint() == expected_hint);
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(0));
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+
+    assert(v.reserve_hint() == 0);
+    assert(std::as_const(v).reserve_hint() == 0);
+  }
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<ApproximatelySizedView, N> v(ApproximatelySizedView(2));
+      static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+      static_assert(std::ranges::approximately_sized_range<const decltype(v)>);
+      assert(v.reserve_hint() == 0);
+      assert(std::as_const(v).reserve_hint() == 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  // non-approximately-sized range has no reserve_hint
+  static_assert(!std::ranges::approximately_sized_range<std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+  static_assert(
+      !std::ranges::approximately_sized_range<const std::ranges::adjacent_view<NonApproximatelySizedView, 2>>);
+
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..41a30d9355429
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<R>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const R>
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "test_iterators.h"
+
+int globalBuff[8];
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    struct NoReserveHint {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+    };
+    using OwningView = std::ranges::owning_view<NoReserveHint>;
+    static_assert(!HasReserveHint<OwningView&>);
+    static_assert(!HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>);
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    struct ReserveHintMember {
+      bidirectional_iterator<int*> begin();
+      bidirectional_iterator<int*> end();
+      int reserve_hint() const;
+    };
+    using OwningView = std::ranges::owning_view<ReserveHintMember>;
+    static_assert(!std::ranges::sized_range<OwningView&>);
+    static_assert(std::ranges::approximately_sized_range<OwningView&>);
+    static_assert(!std::ranges::range<const OwningView&>); // no begin/end
+    static_assert(HasReserveHint<OwningView&>);
+    static_assert(HasReserveHint<OwningView&&>);
+    static_assert(!HasReserveHint<const OwningView&>); // not a range, therefore no reserve_hint()
+    static_assert(!HasReserveHint<const OwningView&&>);
+  }
+  {
+    // Test an empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a));
+    assert(ov.reserve_hint() == 0);
+    assert(std::as_const(ov).reserve_hint() == 0);
+  }
+  {
+    // Test a non-empty view.
+    int a[] = {1};
+    auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1));
+    assert(ov.reserve_hint() == 1);
+    assert(std::as_const(ov).reserve_hint() == 1);
+  }
+  {
+    // Test a non-view.
+    std::array<int, 2> a = {1, 2};
+    auto ov              = std::ranges::owning_view(std::move(a));
+    assert(ov.reserve_hint() == 2);
+    assert(std::as_const(ov).reserve_hint() == 2);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
index 88e7179da5496..d527ead60cfc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp
@@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view<R> view) { view.data()
 static_assert(std::same_as<decltype(std::ranges::ref_view(std::declval<Range&>())),
               std::ranges::ref_view<Range>>);
 
+#if TEST_STD_VER >= 26
+template <class R>
+concept ReserveHintIsInvocable = requires(std::ranges::ref_view<R> view) { view.reserve_hint(); };
+
+struct ApproximatelySizedRange {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+#endif
+
 constexpr bool test() {
   {
     // ref_view::base
@@ -184,6 +198,21 @@ constexpr bool test() {
     static_assert(!SizeIsInvocable<ForwardRange>);
   }
 
+#if TEST_STD_VER >= 26
+  // ref_view::reserve_hint
+  {
+    ApproximatelySizedRange range1{2, 5};
+    std::ranges::ref_view<ApproximatelySizedRange> view1 = range1;
+    assert(view1.reserve_hint() == 5);
+
+    ApproximatelySizedRange range2{7, 0};
+    std::ranges::ref_view<ApproximatelySizedRange> view2 = range2;
+    assert(view2.reserve_hint() == 0);
+
+    static_assert(!ReserveHintIsInvocable<ForwardRange>);
+  }
+#endif
+
   {
     // ref_view::data
     Range range1;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..2acdb9d6e7ce5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+// forward_iterator + sentinel end so that ranges::size doesn't apply,
+// making these ranges approximately_sized only via their member reserve_hint().
+
+struct ConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() const {
+    *hint_called = true;
+    return 3;
+  }
+};
+
+struct NonConstReserveHintView : std::ranges::view_base {
+  bool* hint_called;
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+
+  constexpr unsigned int reserve_hint() {
+    *hint_called = true;
+    return 5;
+  }
+};
+
+struct NoReserveHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(nullptr); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(nullptr)); }
+};
+
+template <class T>
+concept HasReserveHint = requires(T v) { v.reserve_hint(); };
+
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!std::ranges::sized_range<std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<const std::ranges::as_rvalue_view<ConstReserveHintView>>);
+static_assert(HasReserveHint<std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NonConstReserveHintView>>);
+static_assert(!HasReserveHint<std::ranges::as_rvalue_view<NoReserveHintView>>);
+static_assert(!HasReserveHint<const std::ranges::as_rvalue_view<NoReserveHintView>>);
+
+constexpr bool test() {
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 3);
+    assert(hint_called);
+  }
+
+  {
+    bool hint_called = false;
+    std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called});
+    assert(view.reserve_hint() == 5);
+    assert(hint_called);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..eb900e6432fc0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View>
+concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); };
+
+constexpr bool test() {
+  int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView>&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<ApproximatelySizedView> const&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<CopyableView> const&>);
+    static_assert(ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView>&>);
+    static_assert(!ReserveHintEnabled<std::ranges::common_view<NonConstApproximatelySizedView> const&>);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    ApproximatelySizedView view(buf, buf + 8, 5);
+    const std::ranges::common_view<ApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  {
+    NonConstApproximatelySizedView view(buf, buf + 8, 5);
+    std::ranges::common_view<NonConstApproximatelySizedView> common(view);
+    assert(common.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 56f2f6ad5fcbf..3dc8fdc670cb5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -123,4 +123,35 @@ static_assert(HasConstAndNonConstBegin<std::ranges::common_view<NonSimpleNonComm
 static_assert(HasConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 static_assert(HasOnlyConstBegin<const std::ranges::common_view<NonSimpleNonCommonView>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(!std::ranges::common_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct NonConstApproximatelySizedView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+  unsigned int hint_;
+  constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint)
+      : begin_(b), end_(e), hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sentinel_wrapper<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(!std::ranges::common_range<NonConstApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<NonConstApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonConstApproximatelySizedView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..1652d33eafe0c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires (approximately_sized_range<Views> && ...);
+// constexpr auto reserve_hint() const
+//     requires (approximately_sized_range<const Views> && ...);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// All views use forward_iterator as their iterator so that ranges::size doesn't apply,
+// making approximately_sized_range depend solely on the reserve_hint() member.
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + hint_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+
+struct IntHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 4); }
+  constexpr int reserve_hint() const { return 4; }
+};
+
+struct UnsignedHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 5); }
+  constexpr unsigned int reserve_hint() const { return 5; }
+};
+
+struct NoHintView : std::ranges::view_base {
+  constexpr auto begin() const { return forward_iterator<int*>(buffer); }
+  constexpr auto end() const { return forward_iterator<int*>(buffer + 3); }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(ApproximatelySizedView(8));
+    assert(v.reserve_hint() == 8);
+    assert(std::as_const(v).reserve_hint() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    assert(std::as_const(v).reserve_hint() == 5);
+  }
+
+  {
+    // const-view non-approximately-sized range
+    std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3));
+    assert(v.reserve_hint() == 5);
+    static_assert(std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // underlying range not approximately-sized
+    std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8));
+    static_assert(!std::ranges::approximately_sized_range<decltype(v)>);
+    static_assert(!std::ranges::approximately_sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // two ranges with different hint types: common type of int and unsigned int is unsigned int
+    std::ranges::concat_view v(IntHintView{}, UnsignedHintView{});
+    assert(v.reserve_hint() == 9);
+    ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..33045591c412d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//   requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//   requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(std::ranges::drop_view<T> t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView1(MoveOnlyView(), 4);
+  assert(dropView1.reserve_hint() == 4);
+
+  // approximately_sized_range<V>
+  std::ranges::drop_view dropView2(MoveOnlyView(), 0);
+  assert(dropView2.reserve_hint() == 8);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView3(MoveOnlyView(), 8);
+  assert(dropView3.reserve_hint() == 0);
+
+  // approximately_sized_range<const V>
+  const std::ranges::drop_view dropView4(MoveOnlyView(), 10);
+  assert(dropView4.reserve_hint() == 0);
+
+  // mutable-only approximately_sized_range
+  std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3);
+  assert(dropView5.reserve_hint() == 5);
+  static_assert(ReserveHintInvocable<ApproximatelySizedNotConstView>);
+  static_assert(!ReserveHintInvocable<const ApproximatelySizedNotConstView>);
+
+  // Because ForwardView is not approximately_sized_range.
+  static_assert(!ReserveHintInvocable<ForwardView>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index 73d1e5045ad22..c0bb72a138d8b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -141,4 +141,18 @@ struct View : std::ranges::view_base {
   int* end_;
 };
 
+#if TEST_STD_VER >= 26
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int hint_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {}
+  constexpr forward_iterator<int*> begin() const { return forward_iterator<int*>(globalBuff); }
+  constexpr forward_iterator<int*> end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+
+#endif // TEST_STD_VER >= 26
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..085648add5c99
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasReserveHint = requires(T t) { t.reserve_hint(); };
+
+struct NonApproximatelySized : std::ranges::view_base {
+  using iterator = forward_iterator<std::tuple<int>*>;
+  iterator begin() const;
+  iterator end() const;
+};
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySized>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySized>);
+
+static_assert(!HasReserveHint<std::ranges::elements_view<NonApproximatelySized, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<NonApproximatelySized, 0>>);
+
+struct ApproximatelySizedTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() const { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+static_assert(HasReserveHint<const std::ranges::elements_view<ApproximatelySizedTupleView, 0>>);
+
+struct ApproximatelySizedNotConstTupleView : TupleBufferView {
+  unsigned int hint_;
+
+  template <std::size_t N>
+  constexpr ApproximatelySizedNotConstTupleView(std::tuple<int> (&buf)[N], unsigned int hint)
+      : TupleBufferView(buf), hint_(hint) {}
+
+  constexpr auto begin() const { return forward_iterator<std::tuple<int>*>(buffer_); }
+  constexpr auto end() const { return forward_iterator<std::tuple<int>*>(buffer_ + size_); }
+  constexpr unsigned int reserve_hint() { return hint_; }
+};
+static_assert(HasReserveHint<std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+static_assert(!HasReserveHint<const std::ranges::elements_view<ApproximatelySizedNotConstTupleView, 0>>);
+
+constexpr bool test() {
+  std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+  // non-const and const are approximately_sized
+  {
+    auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+    assert(std::as_const(ev).reserve_hint() == 5);
+  }
+
+  {
+    // mutable-only approximately_sized_range
+    auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5));
+    assert(ev.reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..bd391377e3a08
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// class enumerate_view
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>;
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "types.h"
+
+template <class T>
+concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); };
+
+static constexpr int globalBuff[8] = {};
+
+struct NonApproximatelySizedView : std::ranges::view_base {
+  using iterator = forward_iterator<const int*>;
+  iterator begin() const;
+  iterator end() const;
+};
+
+static_assert(!std::ranges::approximately_sized_range<NonApproximatelySizedView>);
+static_assert(!std::ranges::approximately_sized_range<const NonApproximatelySizedView>);
+
+static_assert(!HasMemberReserveHint<std::ranges::enumerate_view<NonApproximatelySizedView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<NonApproximatelySizedView>>);
+
+struct ApproximatelySizedView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedView>>);
+static_assert(HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedView>>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  unsigned int size_;
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {}
+  constexpr auto begin() const { return forward_iterator<const int*>(globalBuff); }
+  constexpr auto end() const { return forward_iterator<const int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return size_; }
+};
+
+static_assert(HasMemberReserveHint<std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+static_assert(!HasMemberReserveHint<const std::ranges::enumerate_view<ApproximatelySizedNotConstView>>);
+
+constexpr bool test() {
+  // Non-const and const are reserve_hint-able
+  {
+    auto view = std::views::enumerate(ApproximatelySizedView{5});
+    assert(view.reserve_hint() == 5);
+    assert(std::as_const(view).reserve_hint() == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..91ced350bbaad
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template <CopyCategory CC>
+struct BidirApproxSizedRange : std::ranges::view_base {
+  int* ptr_;
+  std::size_t reserve_hint_;
+
+  constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {}
+  constexpr BidirApproxSizedRange(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr std::size_t size() const { return reserve_hint_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirApproxSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::reserve_hint(rev) == 4);
+    assert(rev.reserve_hint() == 4);
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirApproxSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).reserve_hint() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..97b4dcfc8b4a4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+// There is no reserve_hint function on a stride view over a view that
+// is *not* an approximately sized range
+static_assert(!std::ranges::approximately_sized_range<BasicTestView<cpp17_input_iterator<int*>>>);
+static_assert(
+    !std::ranges::approximately_sized_range<std::ranges::stride_view<BasicTestView<cpp17_input_iterator<int*>>>>);
+
+// There is a reserve_hint function on a stride view over a view that
+// *is *an approximately sized range
+static_assert(std::ranges::approximately_sized_range<
+              BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>);
+static_assert(std::ranges::approximately_sized_range<std::ranges::stride_view<
+                  BasicTestView<int*, sentinel_wrapper<int*>, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>);
+
+using Iota = std::ranges::iota_view<int, int>;
+using ApproximatelySizedView =
+    BasicTestView<std::ranges::iterator_t<Iota>,
+                  std::ranges::sentinel_t<Iota>,
+                  /*IsSized=*/false,
+                  /*IsApproximatelySized=*/true>;
+
+constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) {
+  return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota));
+}
+
+constexpr bool test() {
+  {
+    // Test with stride as exact multiple of number of elements in view strided over.
+    auto view    = make_approximately_sized(std::views::iota(0, 12));
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.reserve_hint() == 4);
+  }
+
+  {
+    // Test with stride as inexact multiple of number of elements in view strided over.
+    auto view = make_approximately_sized(std::views::iota(0, 22));
+    static_assert(std::ranges::approximately_sized_range<decltype(view)>);
+    auto strided = std::views::stride(view, 3);
+    static_assert(std::ranges::approximately_sized_range<decltype(strided)>);
+    assert(strided.size() == 8);
+  }
+
+  {
+    // Empty range.
+    auto view    = make_approximately_sized(std::views::iota(0, 0));
+    auto strided = view | std::views::stride(3);
+    assert(strided.reserve_hint() == 0);
+  }
+
+  {
+    // Stride larger than range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(10);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride equal to range size.
+    auto view    = make_approximately_sized(std::views::iota(0, 3));
+    auto strided = view | std::views::stride(5);
+    assert(strided.reserve_hint() == 1);
+  }
+
+  {
+    // Stride of 1.
+    auto view    = make_approximately_sized(std::views::iota(0, 7));
+    auto strided = view | std::views::stride(1);
+    assert(strided.size() == 7);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
index 5a8d6b6ca2f71..49da1473dc4e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h
@@ -172,8 +172,9 @@ struct MaybeView<true> : std::ranges::view_base {};
 template <std::input_iterator Iter,
           std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
           bool IsSized                 = false,
+          bool IsApproximatelySized    = false,
           bool IsView                  = false,
-          bool IsCopyable              = false >
+          bool IsCopyable              = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
 struct BasicTestViewOrRange : MaybeView<IsView> {
   Iter begin_{};
@@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
     return begin_ - end_;
   }
 
+  constexpr auto reserve_hint() const
+    requires IsApproximatelySized
+  {
+    return begin_ - end_;
+  }
+
   constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other)      = default;
   constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default;
 
@@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView<IsView> {
   = default;
 };
 
-template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsSized = false>
+template <std::input_iterator Iter,
+          std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>,
+          bool IsSized                 = false,
+          bool IsApproximatelySized    = false>
   requires((!IsSized) || (IsSized && IterDifferable<Iter>))
-using BasicTestView = BasicTestViewOrRange<Iter, Sent, IsSized, true /* IsView */, true /* IsCopyable */>;
+using BasicTestView =
+    BasicTestViewOrRange<Iter, Sent, IsSized, IsApproximatelySized, true /* IsView */, true /* IsCopyable */>;
 
 template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>, bool IsCopyable = true>
-using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, true, IsCopyable>;
+using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange<Iter, Sent, false, false, true, IsCopyable>;
 
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>>>);
 static_assert(std::ranges::view<MaybeCopyableAlwaysMoveableView<cpp17_input_iterator<int*>,
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..65e89e285563f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+#include "types.h"
+
+template <class T>
+concept ReserveHintEnabled = requires(const std::ranges::take_view<T>& tv) { tv.reserve_hint(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    static_assert(ReserveHintEnabled<ApproximatelySizedForwardView>);
+  }
+
+  {
+    std::ranges::take_view<SimpleViewNonSized> tv(SimpleViewNonSized{buffer, buffer + 8}, 100);
+    assert(tv.reserve_hint() == 100);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 0);
+    assert(tv.reserve_hint() == 0);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 2);
+    assert(tv.reserve_hint() == 2);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 4);
+    assert(tv.reserve_hint() == 4);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 6);
+    assert(tv.reserve_hint() == 6);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 8);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  {
+    const std::ranges::take_view<ApproximatelySizedForwardView> tv(ApproximatelySizedForwardView{buffer}, 10);
+    assert(tv.reserve_hint() == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 7590ce33bffc1..a51edc93862b1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,6 +54,20 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+#if TEST_STD_VER >= 26
+using ForwardIter = forward_iterator<int*>;
+struct ApproximatelySizedForwardView : std::ranges::view_base {
+  int* ptr_;
+  constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {}
+  constexpr auto begin() const { return ForwardIter(ptr_); }
+  constexpr auto end() const { return ForwardIter(ptr_ + 8); }
+  constexpr std::size_t reserve_hint() const { return 8; }
+};
+static_assert(std::ranges::view<ApproximatelySizedForwardView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedForwardView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedForwardView>);
+#endif
+
 struct View : std::ranges::view_base {
   constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
new file mode 100644
index 0000000000000..78ae794f82915
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto reserve_hint()
+//     requires approximately_sized_range<V>
+// constexpr auto reserve_hint() const
+//     requires approximately_sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+
+#include "types.h"
+
+template <class T>
+concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); };
+
+constexpr bool test() {
+  {
+    std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{});
+    assert(transformView.reserve_hint() == 5);
+  }
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+  static_assert(ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedView, PlusOne>>);
+
+  static_assert(ReserveHintInvocable<std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+  static_assert(!ReserveHintInvocable<const std::ranges::transform_view<ApproximatelySizedNotConstView, PlusOne>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index e94e7dfcdaeb7..0d94fc02c1f2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base {
 bool operator==(const ForwardIter &lhs, int* rhs);
 bool operator==(int* lhs, const ForwardIter &rhs);
 
+#if TEST_STD_VER >= 26
+struct ApproximatelySizedView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() const { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedView>);
+static_assert(std::ranges::approximately_sized_range<const ApproximatelySizedView>);
+
+struct ApproximatelySizedNotConstView : std::ranges::view_base {
+  int start_;
+  unsigned int reserve_hint_;
+
+  constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0)
+      : start_(start), reserve_hint_(hint) {}
+  constexpr auto begin() const { return forward_iterator<int*>(globalBuff + start_); }
+  constexpr auto end() const { return forward_iterator<int*>(globalBuff + 8); }
+  constexpr unsigned int reserve_hint() { return reserve_hint_; }
+};
+
+static_assert(std::ranges::view<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::sized_range<ApproximatelySizedNotConstView>);
+static_assert(std::ranges::approximately_sized_range<ApproximatelySizedNotConstView>);
+static_assert(!std::ranges::approximately_sized_range<const ApproximatelySizedNotConstView>);
+#endif
+
 struct Range {
   int *begin() const;
   int *end() const;

>From 59df762a288c647f90e8537524cdfbc9b1f22f6f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:58:37 -0700
Subject: [PATCH 07/32] Update libc++ c++26 status

---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d4f7a44910e00..804cb2580411f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 9744940459bcd..f4d57a47a0c8f 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 19d3a72610d4032c3a5ec6282697a9b3e01cbf7f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 18:53:59 -0700
Subject: [PATCH 08/32] Change __freestanding to __unqualified per review

---
 libcxx/include/__ranges/size.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 3dfeade03fce5..d442c9972ae91 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -148,7 +148,7 @@ concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
 };
 
 template <typename _Tp>
-concept __freestanding_reserve_hint =
+concept __unqualified_reserve_hint =
     !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
@@ -169,7 +169,7 @@ struct __fn {
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__freestanding_reserve_hint _Tp>
+  template <__unqualified_reserve_hint _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));

>From 8ab948211bb9273b8ac833d3c57883c2f15a9a2c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 19:27:00 -0700
Subject: [PATCH 09/32] Add missing includes

---
 libcxx/include/__ranges/take_view.h                              | 1 +
 libcxx/test/std/ranges/range.adaptors/range.common.view/types.h  | 1 +
 .../std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp   | 1 +
 .../ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp  | 1 +
 5 files changed, 5 insertions(+)

diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index f97a88ccf9297..b252ad3ecf104 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -37,6 +37,7 @@
 #include <__ranges/view_interface.h>
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
 #include <__type_traits/maybe_const.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/auto_cast.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 3dc8fdc670cb5..130423389ad68 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,6 +11,7 @@
 
 #include <ranges>
 
+#include "test_macros.h"
 #include "test_iterators.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index 1652d33eafe0c..e2b4e8400ba86 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 085648add5c99..96be6d3942e1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <ranges>
 #include <tuple>
+#include <utility>
 
 #include "types.h"
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index bd391377e3a08..f7ae9c610a4ab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -19,6 +19,7 @@
 
 #include <cassert>
 #include <ranges>
+#include <utility>
 
 #include "test_iterators.h"
 

>From da29f5a1172f3aa397477dcc31b4155af9896105 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Sun, 28 Jun 2026 22:07:30 -0700
Subject: [PATCH 10/32] Fully qualify calls to avoid ADL

---
 libcxx/include/__ranges/concat_view.h | 4 ++--
 libcxx/include/__ranges/drop_view.h   | 4 ++--
 libcxx/include/__ranges/stride_view.h | 4 ++--
 libcxx/include/__ranges/take_view.h   | 8 ++++----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 33d269b9fd09a..6e1cfa0e3aad1 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -180,7 +180,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint()
     requires(approximately_sized_range<_Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
@@ -191,7 +191,7 @@ class concat_view : public view_interface<concat_view<_Views...>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires(approximately_sized_range<const _Views> && ...)
   {
-    return apply(
+    return std::apply(
         [](auto... __sizes) static {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return (_CT(__sizes) + ...);
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 9cae5c1c30276..0eb6ece0d9459 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -146,14 +146,14 @@ class drop_view : public view_interface<drop_view<_View>> {
     requires approximately_sized_range<_View>
   {
     const auto __s = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     const auto __s = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
+    return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d4b7cce06eb91..d9006d3865d42 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -137,14 +137,14 @@ class stride_view : public view_interface<stride_view<_View>> {
     requires approximately_sized_range<_View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const
     requires approximately_sized_range<const _View>
   {
     auto __s = static_cast<range_difference_t<decltype((__base_))>>(ranges::reserve_hint(__base_));
-    return __to_unsigned_like(__div_ceil(__s, __stride_));
+    return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
 #  endif //_LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index b252ad3ecf104..00786e2987350 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -162,17 +162,17 @@ class take_view : public view_interface<take_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() {
     if constexpr (approximately_sized_range<_View>) {
       auto __n = static_cast<range_difference_t<_View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const {
     if constexpr (approximately_sized_range<const _View>) {
       auto __n = static_cast<range_difference_t<const _View>>(ranges::reserve_hint(__base_));
-      return __to_unsigned_like(ranges::min(__n, __count_));
+      return std::__to_unsigned_like(ranges::min(__n, __count_));
     }
-    return __to_unsigned_like(__count_);
+    return std::__to_unsigned_like(__count_);
   }
 
 #  endif //_LIBCPP_STD_VER >= 26

>From 683d0ee5de3771968c8636562490e143794d066f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:32:51 -0700
Subject: [PATCH 11/32] Replace `reserve_hint.verify` test with assertion

---
 .../ranges/range.access/reserve_hint.pass.cpp |  3 +++
 .../range.access/reserve_hint.verify.cpp      | 21 -------------------
 2 files changed, 3 insertions(+), 21 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 4e5f11fba445e..c9898c77769af 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -24,6 +24,7 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
 
 extern int bounded_array[42];
+extern int unbounded_array[];
 
 struct SizedSentinelRange {
   int data_[42] = {};
@@ -56,6 +57,8 @@ static_assert(!std::is_invocable_v<RangeReserveHintT, HasReserveHintMemberBool>)
 static_assert(std::ranges::reserve_hint(bounded_array) == 42);
 ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t);
 
+static_assert(!std::is_invocable_v<RangeReserveHintT, decltype(unbounded_array)>);
+
 bool constexpr test_sized_sentinel_range() {
   SizedSentinelRange b;
   assert(std::ranges::reserve_hint(b) == 42);
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
deleted file mode 100644
index 760620c6bd271..0000000000000
--- a/libcxx/test/std/ranges/range.access/reserve_hint.verify.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// std::ranges::reserve_hint
-
-#include <ranges>
-
-extern int arr[];
-
-// Verify that for an array of unknown bound `ranges::reserve_hint` is ill-formed.
-void test() {
-  std::ranges::reserve_hint(arr);
-  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__reserve_hint::__fn'}}}}
-}

>From 7e0dd306bf821c4ff7f260634ea6b975cf0c7b4c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:46:21 -0700
Subject: [PATCH 12/32] Rename __std_size to __sized

The prior name was inaccurate, given that the CPO is called
`ranges::size`. Additionally, the new name matches the sized_range
concept.
---
 libcxx/include/__ranges/size.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index d442c9972ae91..05cb501e0c36a 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -140,22 +140,22 @@ namespace __reserve_hint {
 void reserve_hint() = delete;
 
 template <typename _Tp>
-concept __std_size = requires(_Tp&& __t) { ranges::size(__t); };
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
 
 template <typename _Tp>
-concept __member_reserve_hint = !__std_size<_Tp> && requires(_Tp&& __t) {
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
   { auto(__t.reserve_hint()) } -> __integer_like;
 };
 
 template <typename _Tp>
 concept __unqualified_reserve_hint =
-    !__std_size<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
       { auto(reserve_hint(__t)) } -> __integer_like;
     };
 
 struct __fn {
-  // `[range.prim.size.hint]`: `std::size(t)` is a valid expression
-  template <__std_size _Tp>
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
       noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);

>From 6c8f079e148f2538ab4cc51721d6fc4dcf35a477 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:12:25 -0700
Subject: [PATCH 13/32] Move `reserve_hint` CPO to new internal header

---
 libcxx/include/CMakeLists.txt            |  1 +
 libcxx/include/__ranges/adjacent_view.h  |  1 +
 libcxx/include/__ranges/as_rvalue_view.h |  1 +
 libcxx/include/__ranges/common_view.h    |  1 +
 libcxx/include/__ranges/concat_view.h    |  1 +
 libcxx/include/__ranges/concepts.h       |  1 +
 libcxx/include/__ranges/drop_view.h      |  1 +
 libcxx/include/__ranges/elements_view.h  |  1 +
 libcxx/include/__ranges/enumerate_view.h |  1 +
 libcxx/include/__ranges/owning_view.h    |  1 +
 libcxx/include/__ranges/ref_view.h       |  1 +
 libcxx/include/__ranges/reserve_hint.h   | 79 ++++++++++++++++++++++++
 libcxx/include/__ranges/reverse_view.h   |  1 +
 libcxx/include/__ranges/size.h           | 53 ----------------
 libcxx/include/__ranges/stride_view.h    |  2 +-
 libcxx/include/__ranges/take_view.h      |  1 +
 libcxx/include/__ranges/transform_view.h |  1 +
 libcxx/include/module.modulemap.in       |  1 +
 libcxx/include/ranges                    |  1 +
 19 files changed, 96 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/reserve_hint.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 618686c685bee..94d12c5128041 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -754,6 +754,7 @@ set(files
   __ranges/ref_view.h
   __ranges/rend.h
   __ranges/repeat_view.h
+  __ranges/reserve_hint.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 27b116e30eee9..095b01b263ffa 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -33,6 +33,7 @@
 #include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 705cf559a7ee0..1e1dccf55fe7c 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -19,6 +19,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 4f1dfbf5d9ac2..40acab0331676 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -20,6 +20,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
index 6e1cfa0e3aad1..7602720576001 100644
--- a/libcxx/include/__ranges/concat_view.h
+++ b/libcxx/include/__ranges/concat_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_transform.h>
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index e7cfc94506120..fcd079c237300 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -25,6 +25,7 @@
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/common_reference.h>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index 0eb6ece0d9459..c165e848b6e79 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9ce87e6814dbe..f7cc663325006 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -25,6 +25,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 1a67a23e864d7..26d9098b40e87 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/maybe_const.h>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index b1e8415c3a477..bff1ff62e2284 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -18,6 +18,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index 34f129ace54e7..2c47061ec6f3a 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -22,6 +22,7 @@
 #include <__ranges/data.h>
 #include <__ranges/empty.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/is_object.h>
diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
new file mode 100644
index 0000000000000..b3b4be59f8941
--- /dev/null
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -0,0 +1,79 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_RESERVE_HINT_H
+#define _LIBCPP___RANGES_RESERVE_HINT_H
+
+#include <__concepts/class_or_enum.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__ranges/size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+// [range.prim.size.hint]
+
+namespace ranges {
+namespace __reserve_hint {
+void reserve_hint() = delete;
+
+template <typename _Tp>
+concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
+
+template <typename _Tp>
+concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
+  { auto(__t.reserve_hint()) } -> __integer_like;
+};
+
+template <typename _Tp>
+concept __unqualified_reserve_hint =
+    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
+      { auto(reserve_hint(__t)) } -> __integer_like;
+    };
+
+struct __fn {
+  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
+  template <__sized _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::size(__t))) {
+    return ranges::size(__t);
+  }
+
+  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
+  template <__member_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(__t.reserve_hint()))) {
+    return auto(__t.reserve_hint());
+  }
+
+  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
+  template <__unqualified_reserve_hint _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(auto(reserve_hint(__t)))) {
+    return auto(reserve_hint(__t));
+  }
+};
+} // namespace __reserve_hint
+
+inline namespace __cpo {
+inline constexpr auto reserve_hint = __reserve_hint::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RESERVE_HINT_H
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index fc8e609705661..7f2ea094710fb 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -21,6 +21,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index 05cb501e0c36a..5da3a6ff268e8 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -131,59 +131,6 @@ inline constexpr auto ssize = __ssize::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#  if _LIBCPP_STD_VER >= 26
-
-// [range.prim.size.hint]
-
-namespace ranges {
-namespace __reserve_hint {
-void reserve_hint() = delete;
-
-template <typename _Tp>
-concept __sized = requires(_Tp&& __t) { ranges::size(__t); };
-
-template <typename _Tp>
-concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) {
-  { auto(__t.reserve_hint()) } -> __integer_like;
-};
-
-template <typename _Tp>
-concept __unqualified_reserve_hint =
-    !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum<remove_cvref_t<_Tp>> && requires(_Tp&& __t) {
-      { auto(reserve_hint(__t)) } -> __integer_like;
-    };
-
-struct __fn {
-  // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
-  template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
-    return ranges::size(__t);
-  }
-
-  // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
-  template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
-    return auto(__t.reserve_hint());
-  }
-
-  // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
-  template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
-    return auto(reserve_hint(__t));
-  }
-};
-} // namespace __reserve_hint
-
-inline namespace __cpo {
-inline constexpr auto reserve_hint = __reserve_hint::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-#  endif // _LIBCPP_STD_VER >= 26
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index d9006d3865d42..45a57015eab6a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -30,7 +30,7 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
 
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 00786e2987350..c2c61b1269ade 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/repeat_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index c26b7ea362a8d..95af90468d536 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -30,6 +30,7 @@
 #include <__ranges/empty.h>
 #include <__ranges/movable_box.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d70d0fce52460..c17f4b1a692ef 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1942,6 +1942,7 @@ module std [system] {
     module ref_view                       { header "__ranges/ref_view.h" }
     module rend                           { header "__ranges/rend.h" }
     module repeat_view                    { header "__ranges/repeat_view.h" }
+    module reserve_hint                   { header "__ranges/reserve_hint.h" }
     module reverse_view                   { header "__ranges/reverse_view.h" }
     module single_view                    { header "__ranges/single_view.h" }
     module size                           { header "__ranges/size.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 8e0d464bca6ea..451546335df98 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -531,6 +531,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 26
 #    include <__ranges/concat_view.h>
+#    include <__ranges/reserve_hint.h>
 #  endif
 
 #  include <version>

>From 5ee2311a35f949a5b75c8155706ee576bc93da8b Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:24:19 -0700
Subject: [PATCH 14/32] Sort includes correctly in tests

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp     | 2 +-
 .../range.adjacent.transform/reserve_hint.pass.cpp            | 4 ++--
 .../range.adaptors/range.adjacent/reserve_hint.pass.cpp       | 2 +-
 .../range.all/range.owning.view/reserve_hint.pass.cpp         | 3 +--
 .../range.adaptors/range.common.view/reserve_hint.pass.cpp    | 2 +-
 .../range.adaptors/range.enumerate/reserve_hint.pass.cpp      | 1 -
 .../ranges/range.adaptors/range.take/reserve_hint.pass.cpp    | 4 ++--
 7 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index c9898c77769af..ba476f204ffbe 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 #include <cassert>
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index 291810bfecf7d..bdc6d08e7b96b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -17,9 +17,9 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
-#include "test_iterators.h"
 #include "helpers.h"
+#include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index d55e90112fad3..2c6f78b41ba65 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -17,8 +17,8 @@
 #include <ranges>
 #include <utility>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
index 41a30d9355429..88fd2bdb589db 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp
@@ -13,11 +13,10 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const R>
 
-#include <ranges>
-
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <ranges>
 #include <utility>
 
 #include "test_iterators.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
index eb900e6432fc0..9833cb5798c41 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp
@@ -13,8 +13,8 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>;
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
 #include "test_iterators.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
index f7ae9c610a4ab..56f267414f5eb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp
@@ -22,7 +22,6 @@
 #include <utility>
 
 #include "test_iterators.h"
-
 #include "types.h"
 
 template <class T>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
index 65e89e285563f..f82cf4f143a3e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp
@@ -13,11 +13,11 @@
 // constexpr auto reserve_hint() const
 //     requires approximately_sized_range<const V>
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 

>From fb125188f610122b15febe8da453ab1326eb9f55 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Mon, 29 Jun 2026 21:30:22 -0700
Subject: [PATCH 15/32] Make test header comments match siblings

Specifically, this change removes "// <ranges>" comments in test
headers where siblings in the same subdirectory don't have them.
---
 .../ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp | 2 --
 .../ranges/range.adaptors/range.concat/reserve_hint.pass.cpp    | 2 --
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 2 --
 3 files changed, 6 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
index 2acdb9d6e7ce5..8d2f6cb07afbe 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>;
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
index e2b4e8400ba86..ede9050823165 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires (approximately_sized_range<Views> && ...);
 // constexpr auto reserve_hint() const
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 96be6d3942e1d..56d1ab13d8a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// <ranges>
-
 // constexpr auto reserve_hint()
 //     requires approximately_sized_range<V>
 // constexpr auto reserve_hint() const

>From b2478fe055d761ab0d7ea7a47db7bc9adda2199a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:44:00 -0700
Subject: [PATCH 16/32] Fix formatting

---
 libcxx/include/__ranges/elements_view.h                   | 2 +-
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index f7cc663325006..7e4a0246c2fef 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -24,8 +24,8 @@
 #include <__ranges/concepts.h>
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/range_adaptor.h>
-#include <__ranges/size.h>
 #include <__ranges/reserve_hint.h>
+#include <__ranges/size.h>
 #include <__ranges/view_interface.h>
 #include <__tuple/tuple_element.h>
 #include <__tuple/tuple_like.h>
diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index ba476f204ffbe..5d24a24cd2341 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -20,8 +20,8 @@ using RangeReserveHintT = decltype(std::ranges::reserve_hint);
 
 struct Incomplete;
 static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&)[]>);
-static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete(&&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&)[]>);
+static_assert(!std::is_invocable_v<RangeReserveHintT, Incomplete (&&)[]>);
 
 extern int bounded_array[42];
 extern int unbounded_array[];

>From 5d47b1d9833275624bf4a5f7e4e7fc6f1024f19c Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:13:20 -0700
Subject: [PATCH 17/32] Directly list standard library includes in tests

This avoids relying on transitive includes, per a reviewer comment.
---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp       | 2 ++
 .../range.adjacent.transform/reserve_hint.pass.cpp              | 1 +
 .../ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.elements/reserve_hint.pass.cpp  | 1 +
 .../ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp   | 2 ++
 libcxx/test/std/ranges/range.adaptors/range.take/types.h        | 1 +
 6 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 5d24a24cd2341..11379bedf7bf2 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -11,7 +11,9 @@
 // std::ranges::reserve_hint
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <type_traits>
 
 #include "test_iterators.h"
 #include "test_macros.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
index bdc6d08e7b96b..5a5ed8c8a6983 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const InnerView>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
index 2c6f78b41ba65..0546d9dbdb794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const View>;
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <utility>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
index 56d1ab13d8a2d..6c888f2ae3827 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp
@@ -14,6 +14,7 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
 #include <tuple>
 #include <utility>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
index 91ced350bbaad..e241639d0f6ac 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp
@@ -14,7 +14,9 @@
 //     requires approximately_sized_range<const V>
 
 #include <cassert>
+#include <cstddef>
 #include <ranges>
+#include <utility>
 
 #include "test_macros.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index a51edc93862b1..38b2f8f6ce3b8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -1,6 +1,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H
 
+#include <cstddef>
 #include <ranges>
 
 #include "test_macros.h"

>From 1bac7d85fe85ca416fe2ebe48bcd6eb620fe195a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:20:51 -0700
Subject: [PATCH 18/32] Shorten paper status note in Cxx26Papers.csv

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f4d57a47a0c8f..b71a7e5e20d11 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``chunk_view`` and ``slide_view`` support blocked on `P2442R1 <https://wg21.link/P2442R1>`__. ``as_const_view`` support blocked on `P2278R4 <https://wg21.link/P2278R4>`__."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From ad137e8728bae1cfc8ca893b3035b6fefc4f495f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Wed, 1 Jul 2026 17:55:25 -0700
Subject: [PATCH 19/32] Add nodiscard tests for reserve_hint members

This change only adds nodiscard tests for reserve_hint where the view
has an existing nodiscard test. Adding a full set of nodiscard tests
for ranges that don't currently have them seems out of scope.
---
 .../range.adaptors/range.all/nodiscard.verify.cpp  | 14 ++++++++++++++
 .../range.as_rvalue/nodiscard.verify.cpp           |  8 ++++++++
 .../range.common/nodiscard.verify.cpp              |  9 +++++++++
 .../range.concat/nodiscard.verify.cpp              |  5 +++++
 .../range.adaptors/range.drop/nodiscard.verify.cpp |  7 +++++++
 .../range.enumerate/nodiscard.verify.cpp           | 10 +++++++++-
 .../range.reverse/nodiscard.verify.cpp             |  8 ++++++++
 .../range.stride.view/nodiscard.verify.cpp         |  6 ++++++
 .../range.adaptors/range.take/nodiscard.verify.cpp |  7 +++++++
 .../range.transform/nodiscard.verify.cpp           |  8 ++++++++
 10 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
index eaa739ca87c13..6471309d39268 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <vector>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
 
@@ -57,6 +59,13 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -77,6 +86,11 @@ void test() {
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.size();
 
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+#endif
+
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     v.data();
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
index 93e0d50886010..c46bbbb9c664b 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp
@@ -13,6 +13,7 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
 #include "test_range.h"
 
 struct NonSimpleView : std::ranges::view_base {
@@ -53,6 +54,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.as.rvalue.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
index 3542251b7d436..ad199a06736f6 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 struct NonCommonView : std::ranges::view_base {
   int* begin() const;
   const int* end() const;
@@ -51,6 +53,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.common.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
index b81e646d381c4..f9a3e732d8ac1 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -36,6 +36,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(cv).size();
 
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).reserve_hint();
+
   // [range.concat.iterator]
 
   auto it = cv.begin();
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
index 2633c3b778c01..179bc1ab22c1c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp
@@ -45,6 +45,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.drop.overview]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..a2c19427f08dd 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -14,6 +14,8 @@
 #include <utility>
 #include <vector>
 
+#include "test_macros.h"
+
 void test() {
   std::vector<int> range;
   std::ranges::enumerate_view ev{range};
@@ -32,6 +34,12 @@ void test() {
   ev.size();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(ev).size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  ev.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(ev).reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   ev.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -78,4 +86,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
index 6dfa2f5d17c60..612f8a2bec1b8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 void test() {
   int range[] = {19, 28, 29, 49, 82, 94};
@@ -41,6 +42,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.reverse.overview]
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 9c48ecb68e5e3..3cc5b6f2db9b2 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -13,6 +13,8 @@
 #include <ranges>
 #include <utility>
 
+#include "test_macros.h"
+
 void test() {
   int range[] = {1, 2, 3};
   auto sv     = std::ranges::stride_view(range, 2);
@@ -27,6 +29,10 @@ void test() {
   sv.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.size();
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  sv.reserve_hint();
+#endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
index 621b20b29e3a1..457b42296543e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp
@@ -46,6 +46,13 @@ void test() {
     v.size();
     // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
     std::as_const(v).size();
+
+#if TEST_STD_VER >= 26
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    v.reserve_hint();
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::as_const(v).reserve_hint();
+#endif
   }
 
   // [range.take.sentinel]
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
index 76ffa16d233f5..76e4d2dc8d84f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <functional>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_interface<View> {
   int* begin();
@@ -51,6 +52,13 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v).size();
 
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+
   // [range.transform.iterator]
 
   auto it = v.begin();

>From 67da03b280caccd291ddd69a5dbd77844c008765 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 20:39:38 -0700
Subject: [PATCH 20/32] Add reserve_hint to the library-wide CPO test

---
 .../conventions/customization.point.object/cpo.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index d83ca7f2eabfc..109ad92f4df3e 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+static_assert(test(std::ranges::reserve_hint, a));
 #endif
 
 // [range.factories]

>From 2dee3c1cae9e8a4aff043df30933a34670a26679 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Thu, 2 Jul 2026 21:35:01 -0700
Subject: [PATCH 21/32] Add remaining reserve_hint nodiscard tests

---
 .../nodiscard.verify.cpp                      | 32 ++++++++++++++++++
 .../range.adjacent/nodiscard.verify.cpp       | 32 ++++++++++++++++++
 .../range.elements/nodiscard.verify.cpp       | 33 +++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..cfc81554ad863
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent_transform<2>(range, [](int a, int b) { return a + b; });
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..46872b6e52985
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++23
+
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<int> range;
+  auto v = std::views::adjacent<2>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..2197a1ea9cde8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Check that functions are marked [[nodiscard]]
+
+// TODO: this test should eventually be expanded to cover functions beyond
+// reserve_hint; at that time, it should be std-at-least-c++20
+
+#include <ranges>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+void test() {
+  std::vector<std::tuple<int>> range;
+  auto v = std::views::elements<0>(range);
+
+#if TEST_STD_VER >= 26
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.reserve_hint();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).reserve_hint();
+#endif
+}

>From 86f59d72480a80cb0d3325cac1ee59d5b08524bb Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 04:18:49 -0700
Subject: [PATCH 22/32] Permit reserve_hint's unqualified lookup

reserve_hint must use ADL per the specification, and thus needs to
be exempted from the libcpp-robust-against-adl check like other
CPOs.
---
 libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
index 1bfe96b414b7c..16c45a524811e 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp
@@ -20,7 +20,8 @@ AST_MATCHER(clang::CallExpr, isOperator) { return llvm::isa<clang::CXXOperatorCa
 
 AST_MATCHER(clang::UnresolvedLookupExpr, isCustomizationPoint) {
   return std::ranges::any_of(
-      std::array{"swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin"},
+      std::array{
+          "swap", "make_error_code", "make_error_condition", "begin", "end", "size", "rend", "rbegin", "reserve_hint"},
       [&](const char* func) { return Node.getName().getAsString() == func; });
 }
 

>From 0020ac432662be8ace647b3c92368cbc13744c24 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 06:14:47 -0700
Subject: [PATCH 23/32] Fix unused variable warning from GCC

---
 libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
index 11379bedf7bf2..6b6a8523afbdc 100644
--- a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp
@@ -39,7 +39,7 @@ struct HasSizeMember {
 };
 
 struct HasSizeFunction {
-  friend constexpr std::size_t size(HasSizeFunction _) { return 42; }
+  friend constexpr std::size_t size(HasSizeFunction) { return 42; }
 };
 
 struct HasReserveHintMember {
@@ -47,7 +47,7 @@ struct HasReserveHintMember {
 };
 
 struct HasReserveHintFunction {
-  friend constexpr std::size_t reserve_hint(HasReserveHintFunction _) { return 42; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction) { return 42; }
 };
 
 struct HasReserveHintMemberBool {
@@ -86,12 +86,12 @@ ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})),
 struct HasSizeAndReserveHint {
   constexpr std::size_t size() { return 42; }
   constexpr std::size_t reserve_hint() { return 0; }
-  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint) { return 0; }
 };
 
 struct HasReserveHintMemberAndFunction {
   constexpr std::size_t reserve_hint() { return 42; }
-  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction _) { return 0; }
+  friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction) { return 0; }
 };
 
 static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42);

>From 6b09d9baea9a8cd77b0c916fcc0ae043fe8a1d0d Mon Sep 17 00:00:00 2001
From: inquisitivecrystal
 <22333129+inquisitivecrystal at users.noreply.github.com>
Date: Tue, 7 Jul 2026 22:24:20 -0700
Subject: [PATCH 24/32] Address review comments

---
 .../range.adaptors/range.stride.view/nodiscard.verify.cpp     | 4 ++++
 .../test/std/ranges/range.adaptors/range.common.view/types.h  | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
index 3cc5b6f2db9b2..e4df227ff44c8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp
@@ -32,6 +32,10 @@ void test() {
 #if TEST_STD_VER >= 26
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.reserve_hint();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(sv).reserve_hint();
+
 #endif
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   sv.stride();
diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
index 130423389ad68..670837aa47744 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h
@@ -11,8 +11,8 @@
 
 #include <ranges>
 
-#include "test_macros.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct DefaultConstructibleView : std::ranges::view_base {
   int* begin_ = nullptr;

>From d02f2fb321edd1e7d48f651d6d39a7954b251206 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 19:29:28 -0700
Subject: [PATCH 25/32] Empty commit for CI


>From fdb58ee8c8174b1bd8162cb7729cdff9e65eb98f Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Fri, 10 Jul 2026 20:10:21 -0700
Subject: [PATCH 26/32] Avoid creating an empty namespace before c++26

---
 libcxx/include/__ranges/reserve_hint.h | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
index b3b4be59f8941..e58990bf1194e 100644
--- a/libcxx/include/__ranges/reserve_hint.h
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -20,9 +20,10 @@
 #  pragma GCC system_header
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 26
 // [range.prim.size.hint]
 
 namespace ranges {
@@ -72,8 +73,8 @@ inline constexpr auto reserve_hint = __reserve_hint::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-#endif // _LIBCPP_STD_VER >= 26
-
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_STD_VER >= 26
+
 #endif // _LIBCPP___RANGES_RESERVE_HINT_H

>From f09928004b010011e4cb7db2f9fbbc01cedab590 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 14:50:58 -0700
Subject: [PATCH 27/32] Bump version in reserve_hint status entries

This updates the status page to show that we are now targeting
libc++24.
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 718dbf6677aba..7a4c7087dfd11 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -336,7 +336,7 @@
 "`LWG4549 <https://wg21.link/LWG4549>`__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 <https://github.com/llvm/llvm-project/issues/189878>`__",""
 "`LWG4550 <https://wg21.link/LWG4550>`__","Need new feature test macros for ``<stdckdint.h>`` and ``<stdbit.h>``","2026-03 (Croydon)","","","`#189879 <https://github.com/llvm/llvm-project/issues/189879>`__",""
 "`LWG4552 <https://wg21.link/LWG4552>`__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 <https://github.com/llvm/llvm-project/issues/189880>`__",""
-"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","23","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
+"`LWG4553 <https://wg21.link/LWG4553>`__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","24","`#189881 <https://github.com/llvm/llvm-project/issues/189881>`__",""
 "`LWG4554 <https://wg21.link/LWG4554>`__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 <https://github.com/llvm/llvm-project/issues/189882>`__",""
 "`LWG4555 <https://wg21.link/LWG4555>`__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 <https://github.com/llvm/llvm-project/issues/189883>`__",""
 "`LWG4556 <https://wg21.link/LWG4556>`__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 <https://github.com/llvm/llvm-project/issues/189884>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 398ba8e4d60bd..d98a62cfa3418 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -112,7 +112,7 @@
 "`P3430R3 <https://wg21.link/P3430R3>`__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 <https://github.com/llvm/llvm-project/issues/127881>`__",""
 "`P2663R7 <https://wg21.link/P2663R7>`__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 <https://github.com/llvm/llvm-project/issues/127882>`__",""
 "`P2933R4 <https://wg21.link/P2933R4>`__","Extend ``<bit>`` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 <https://github.com/llvm/llvm-project/issues/127883>`__",""
-"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","23","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
+"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","24","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported."
 "`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
 "`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
 "`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""

>From 4453a8674737ea7dc8155a860bf891d5d947f37a Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 20:57:39 -0700
Subject: [PATCH 28/32] Remove comment on #else block in concepts.h

This seems to better align with standard practice.
---
 libcxx/include/__ranges/concepts.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index cae59e3946daa..99a2acfe922c5 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -91,7 +91,7 @@ concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::r
 template <class _Tp>
 concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); };
 
-#  else // _LIBCPP_STD_VER < 26
+#  else
 
 // [range.sized]
 template <class _Tp>

>From f35c0ad2597b8f1e4c438355b127e7322652a9f9 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Wed, 15 Jul 2026 21:06:33 -0700
Subject: [PATCH 29/32] Add spaces between `//` and comment text

This change is applied on all of my `#endif` comments, which were
consistently irregular in missing spaces in this location.
---
 libcxx/include/__ranges/adjacent_transform_view.h | 2 +-
 libcxx/include/__ranges/adjacent_view.h           | 2 +-
 libcxx/include/__ranges/as_rvalue_view.h          | 2 +-
 libcxx/include/__ranges/common_view.h             | 2 +-
 libcxx/include/__ranges/drop_view.h               | 2 +-
 libcxx/include/__ranges/elements_view.h           | 2 +-
 libcxx/include/__ranges/enumerate_view.h          | 2 +-
 libcxx/include/__ranges/owning_view.h             | 2 +-
 libcxx/include/__ranges/ref_view.h                | 2 +-
 libcxx/include/__ranges/reverse_view.h            | 2 +-
 libcxx/include/__ranges/stride_view.h             | 2 +-
 libcxx/include/__ranges/take_view.h               | 2 +-
 libcxx/include/__ranges/transform_view.h          | 2 +-
 13 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h
index 6991fe2e19803..a833c0dee4fd4 100644
--- a/libcxx/include/__ranges/adjacent_transform_view.h
+++ b/libcxx/include/__ranges/adjacent_transform_view.h
@@ -164,7 +164,7 @@ class adjacent_transform_view : public view_interface<adjacent_transform_view<_V
     return __inner_.reserve_hint();
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <forward_range _View, move_constructible _Fn, size_t _Np>
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index c483c04a11373..db9edb5cf46f2 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -163,7 +163,7 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
     return static_cast<_ST>(__sz);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 struct __adjacent_view_iter_access {
diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h
index 1e1dccf55fe7c..ddd2f9a75c267 100644
--- a/libcxx/include/__ranges/as_rvalue_view.h
+++ b/libcxx/include/__ranges/as_rvalue_view.h
@@ -115,7 +115,7 @@ class as_rvalue_view : public view_interface<as_rvalue_view<_View>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h
index 40acab0331676..c232fb533e1db 100644
--- a/libcxx/include/__ranges/common_view.h
+++ b/libcxx/include/__ranges/common_view.h
@@ -117,7 +117,7 @@ class common_view : public view_interface<common_view<_View>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index c165e848b6e79..2776df6175797 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -157,7 +157,7 @@ class drop_view : public view_interface<drop_view<_View>> {
     return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 9397e6b109aa6..3d91f5e4c1d6e 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -148,7 +148,7 @@ class elements_view : public view_interface<elements_view<_View, _Np>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h
index 26d9098b40e87..8845e6caa1421 100644
--- a/libcxx/include/__ranges/enumerate_view.h
+++ b/libcxx/include/__ranges/enumerate_view.h
@@ -131,7 +131,7 @@ class enumerate_view : public view_interface<enumerate_view<_View>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h
index 4b154b7c0b9aa..e30f6da11f907 100644
--- a/libcxx/include/__ranges/owning_view.h
+++ b/libcxx/include/__ranges/owning_view.h
@@ -115,7 +115,7 @@ class owning_view : public view_interface<owning_view<_Rp>> {
     return ranges::reserve_hint(__r_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view);
 
diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h
index f3f6eb11620fd..9d65a536d5a09 100644
--- a/libcxx/include/__ranges/ref_view.h
+++ b/libcxx/include/__ranges/ref_view.h
@@ -82,7 +82,7 @@ class ref_view : public view_interface<ref_view<_Range>> {
     return ranges::reserve_hint(*__range_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
index 7f2ea094710fb..fe8e40b74e1b7 100644
--- a/libcxx/include/__ranges/reverse_view.h
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -127,7 +127,7 @@ class reverse_view : public view_interface<reverse_view<_View>> {
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 45a57015eab6a..3f1c841cdd047 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -147,7 +147,7 @@ class stride_view : public view_interface<stride_view<_View>> {
     return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_));
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 }; // class stride_view
 
 template <class _Range>
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index c2c61b1269ade..53a800f7f4b44 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -176,7 +176,7 @@ class take_view : public view_interface<take_view<_View>> {
     return std::__to_unsigned_like(__count_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <view _View>
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 95af90468d536..be4ae7da77f4e 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -155,7 +155,7 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
     return ranges::reserve_hint(__base_);
   }
 
-#  endif //_LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER >= 26
 };
 
 template <class _Range, class _Fn>

>From 93d6c274e31f00773c7c0fed44d479872c0457dc Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Sat, 18 Jul 2026 21:01:52 -0700
Subject: [PATCH 30/32] Make `reserve_hint`'s call operator static

---
 libcxx/include/__ranges/reserve_hint.h        | 12 +++---
 ...hint.static_call_operator.compile.pass.cpp | 41 +++++++++++++++++++
 2 files changed, 47 insertions(+), 6 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.access/reserve_hint.static_call_operator.compile.pass.cpp

diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h
index e58990bf1194e..c523caf19e77c 100644
--- a/libcxx/include/__ranges/reserve_hint.h
+++ b/libcxx/include/__ranges/reserve_hint.h
@@ -47,22 +47,22 @@ concept __unqualified_reserve_hint =
 struct __fn {
   // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression
   template <__sized _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t))) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr __integer_like auto
+  operator()(_Tp&& __t) noexcept(noexcept(ranges::size(__t))) {
     return ranges::size(__t);
   }
 
   // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression
   template <__member_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(__t.reserve_hint()))) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr __integer_like auto
+  operator()(_Tp&& __t) noexcept(noexcept(auto(__t.reserve_hint()))) {
     return auto(__t.reserve_hint());
   }
 
   // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression
   template <__unqualified_reserve_hint _Tp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-      noexcept(noexcept(auto(reserve_hint(__t)))) {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr __integer_like auto
+  operator()(_Tp&& __t) noexcept(noexcept(auto(reserve_hint(__t)))) {
     return auto(reserve_hint(__t));
   }
 };
diff --git a/libcxx/test/libcxx/ranges/range.access/reserve_hint.static_call_operator.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.access/reserve_hint.static_call_operator.compile.pass.cpp
new file mode 100644
index 0000000000000..43486898f18d8
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.access/reserve_hint.static_call_operator.compile.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
+
+// Test the libc++-specific behavior that the call operator of the
+// std::ranges::reserve_hint customization point object is static.
+
+#include <cstddef>
+#include <ranges>
+
+using RangeReserveHintT = decltype(std::ranges::reserve_hint);
+
+extern int bounded_array[42];
+
+struct HasSizeMember {
+  constexpr std::size_t size() { return 42; }
+};
+
+struct HasSizeFunction {
+  friend constexpr std::size_t size(HasSizeFunction) { return 42; }
+};
+
+struct HasReserveHintMember {
+  constexpr std::size_t reserve_hint() { return 42; }
+};
+
+struct HasReserveHintFunction {
+  friend constexpr std::size_t reserve_hint(HasReserveHintFunction) { return 42; }
+};
+
+static_assert(RangeReserveHintT::operator()(bounded_array) == 42);
+static_assert(RangeReserveHintT::operator()(HasSizeMember{}) == 42);
+static_assert(RangeReserveHintT::operator()(HasSizeFunction{}) == 42);
+static_assert(RangeReserveHintT::operator()(HasReserveHintMember{}) == 42);
+static_assert(RangeReserveHintT::operator()(HasReserveHintFunction{}) == 42);

>From 46df2cd88cacd4f076bd1b7c771c98748f7e7168 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Sat, 18 Jul 2026 21:29:51 -0700
Subject: [PATCH 31/32] Teach `ranges::to` to respect `reserve_hint`

---
 libcxx/include/__ranges/to.h                  |  7 ++++++
 .../range.utility.conv/to.pass.cpp            | 22 +++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/libcxx/include/__ranges/to.h b/libcxx/include/__ranges/to.h
index 3a2bc42969f7b..33e12b9cb410c 100644
--- a/libcxx/include/__ranges/to.h
+++ b/libcxx/include/__ranges/to.h
@@ -23,6 +23,7 @@
 #include <__ranges/from_range.h>
 #include <__ranges/range_adaptor.h>
 #include <__ranges/ref_view.h>
+#include <__ranges/reserve_hint.h>
 #include <__ranges/size.h>
 #include <__ranges/transform_view.h>
 #include <__type_traits/add_pointer.h>
@@ -107,9 +108,15 @@ template <class _Container, input_range _Range, class... _Args>
     else if constexpr (constructible_from<_Container, _Args...> &&
                        __container_appendable<_Container, range_reference_t<_Range>>) {
       _Container __result(std::forward<_Args>(__args)...);
+#  if _LIBCPP_STD_VER >= 26
+      if constexpr (approximately_sized_range<_Range> && __reservable_container<_Container>) {
+        __result.reserve(static_cast<range_size_t<_Container>>(ranges::reserve_hint(__range)));
+      }
+#  else
       if constexpr (sized_range<_Range> && __reservable_container<_Container>) {
         __result.reserve(static_cast<range_size_t<_Container>>(ranges::size(__range)));
       }
+#  endif
 
       for (auto&& __ref : __range) {
         using _Ref = decltype(__ref);
diff --git a/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp b/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp
index a983745fd636e..4d36b678acee8 100644
--- a/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp
+++ b/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp
@@ -156,6 +156,20 @@ struct MaybeSizedRange {
 static_assert(std::ranges::sized_range<MaybeSizedRange<true>>);
 static_assert(!std::ranges::sized_range<MaybeSizedRange<false>>);
 
+#if TEST_STD_VER >= 26
+
+struct ApproxSizedRange {
+  int x = 0;
+  constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&x); }
+  constexpr forward_iterator<int*> end() { return begin(); }
+
+  constexpr std::size_t reserve_hint() const { return 0; }
+};
+static_assert(!std::ranges::sized_range<ApproxSizedRange>);
+static_assert(std::ranges::approximately_sized_range<ApproxSizedRange>);
+
+#endif
+
 template <bool HasCapacity = true, bool CapacityReturnsSizeT = true,
           bool HasMaxSize = true, bool MaxSizeReturnsSizeT = true>
 struct Reservable : Fallback {
@@ -275,6 +289,14 @@ constexpr void test_constraints() {
       assert(result.reserve_called);
     }
 
+#if TEST_STD_VER >= 26
+    { // approximately_sized_range
+      using C     = Reservable<>;
+      auto result = std::ranges::to<C>(ApproxSizedRange());
+      assert(result.reserve_called);
+    }
+#endif
+
     { // !sized_range
       using C = Reservable<>;
       auto result = std::ranges::to<C>(MaybeSizedRange<false>());

>From ecd8d6530d1532f626c5555ef2601acbb428ffc0 Mon Sep 17 00:00:00 2001
From: inquisitivecrystal <open-source at inquisitivecrystal.addy.io>
Date: Sat, 18 Jul 2026 23:03:32 -0700
Subject: [PATCH 32/32] Add tests for `approximately_sized_range`

These mostly mirror the existing `sized_range` tests, with some
stylistic changes and a few additions to the coverage.
---
 ...approximately_sized_range.compile.pass.cpp | 114 ++++++++++++++++++
 .../subsumption.compile.pass.cpp              |  42 +++++++
 2 files changed, 156 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.req/range.approximately.sized/approximately_sized_range.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.req/range.approximately.sized/subsumption.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.req/range.approximately.sized/approximately_sized_range.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.approximately.sized/approximately_sized_range.compile.pass.cpp
new file mode 100644
index 0000000000000..f6e59e305740e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.req/range.approximately.sized/approximately_sized_range.compile.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class T>
+// concept approximately_sized_range;
+
+#include <ranges>
+
+#include "test_iterators.h"
+
+static_assert(std::ranges::approximately_sized_range<int[5]>);
+static_assert(std::ranges::approximately_sized_range<int (&)[5]>);
+static_assert(!std::ranges::approximately_sized_range<int (&)[]>);
+static_assert(!std::ranges::approximately_sized_range<int[]>);
+
+struct range_has_reserve_hint {
+  bidirectional_iterator<int*> begin();
+  bidirectional_iterator<int*> end();
+  int reserve_hint();
+};
+static_assert(std::ranges::approximately_sized_range<range_has_reserve_hint>);
+static_assert(!std::ranges::approximately_sized_range<const range_has_reserve_hint>);
+static_assert(!std::ranges::sized_range<range_has_reserve_hint>);
+static_assert(!std::ranges::sized_range<const range_has_reserve_hint>);
+
+struct range_has_const_reserve_hint {
+  bidirectional_iterator<int*> begin();
+  bidirectional_iterator<int*> end();
+  int reserve_hint() const;
+};
+static_assert(std::ranges::approximately_sized_range<range_has_const_reserve_hint>);
+static_assert(!std::ranges::approximately_sized_range<const range_has_const_reserve_hint>);
+static_assert(!std::ranges::sized_range<range_has_const_reserve_hint>);
+static_assert(!std::ranges::sized_range<const range_has_const_reserve_hint>);
+
+struct const_range_has_reserve_hint {
+  bidirectional_iterator<int*> begin() const;
+  bidirectional_iterator<int*> end() const;
+  int reserve_hint();
+};
+static_assert(std::ranges::approximately_sized_range<const_range_has_reserve_hint>);
+static_assert(std::ranges::range<const const_range_has_reserve_hint>);
+static_assert(!std::ranges::approximately_sized_range<const const_range_has_reserve_hint>);
+static_assert(!std::ranges::sized_range<const_range_has_reserve_hint>);
+static_assert(!std::ranges::sized_range<const const_range_has_reserve_hint>);
+
+struct const_range_has_const_reserve_hint {
+  bidirectional_iterator<int*> begin() const;
+  bidirectional_iterator<int*> end() const;
+  int reserve_hint() const;
+};
+static_assert(std::ranges::approximately_sized_range<const_range_has_const_reserve_hint>);
+static_assert(std::ranges::approximately_sized_range<const const_range_has_const_reserve_hint>);
+static_assert(!std::ranges::sized_range<const_range_has_const_reserve_hint>);
+static_assert(!std::ranges::sized_range<const const_range_has_const_reserve_hint>);
+
+struct sized_sentinel_range_is_approximately_sized {
+  int* begin();
+  int* end();
+};
+static_assert(std::ranges::approximately_sized_range<sized_sentinel_range_is_approximately_sized>);
+static_assert(!std::ranges::approximately_sized_range<const sized_sentinel_range_is_approximately_sized>);
+
+struct const_sized_sentinel_range_is_approximately_sized {
+  int* begin() const;
+  int* end() const;
+};
+static_assert(std::ranges::approximately_sized_range<const_sized_sentinel_range_is_approximately_sized>);
+static_assert(std::ranges::approximately_sized_range<const const_sized_sentinel_range_is_approximately_sized>);
+
+struct non_range_has_reserve_hint {
+  int reserve_hint() const;
+};
+static_assert(requires(const non_range_has_reserve_hint x) { std::ranges::reserve_hint(x); });
+static_assert(!std::ranges::approximately_sized_range<non_range_has_reserve_hint>);
+static_assert(!std::ranges::approximately_sized_range<const non_range_has_reserve_hint>);
+
+struct range_has_unqualified_reserve_hint {
+  bidirectional_iterator<int*> begin() const;
+  bidirectional_iterator<int*> end() const;
+
+  friend int reserve_hint(range_has_unqualified_reserve_hint&);
+};
+static_assert(std::ranges::approximately_sized_range<range_has_unqualified_reserve_hint>);
+static_assert(!std::ranges::approximately_sized_range<const range_has_unqualified_reserve_hint>);
+static_assert(!std::ranges::sized_range<range_has_unqualified_reserve_hint>);
+static_assert(!std::ranges::sized_range<const range_has_unqualified_reserve_hint>);
+
+struct range_has_const_unqualified_reserve_hint {
+  bidirectional_iterator<int*> begin() const;
+  bidirectional_iterator<int*> end() const;
+
+  friend int reserve_hint(const range_has_const_unqualified_reserve_hint&);
+};
+static_assert(std::ranges::approximately_sized_range<range_has_const_unqualified_reserve_hint>);
+static_assert(std::ranges::approximately_sized_range<const range_has_const_unqualified_reserve_hint>);
+static_assert(!std::ranges::sized_range<range_has_const_unqualified_reserve_hint>);
+static_assert(!std::ranges::sized_range<const range_has_const_unqualified_reserve_hint>);
+
+struct range_has_bool_reserve_hint {
+  bidirectional_iterator<int*> begin() const;
+  bidirectional_iterator<int*> end() const;
+
+  bool reserve_hint();
+};
+static_assert(!std::ranges::approximately_sized_range<range_has_bool_reserve_hint>);
+static_assert(!std::ranges::approximately_sized_range<const range_has_bool_reserve_hint>);
diff --git a/libcxx/test/std/ranges/range.req/range.approximately.sized/subsumption.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.approximately.sized/subsumption.compile.pass.cpp
new file mode 100644
index 0000000000000..f9d43cba2ae22
--- /dev/null
+++ b/libcxx/test/std/ranges/range.req/range.approximately.sized/subsumption.compile.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class T>
+// concept approximately_sized_range;
+
+#include <ranges>
+
+// approximately_sized_range subsumes range
+
+template <std::ranges::range R>
+consteval bool check_subsumption_1() {
+  return false;
+}
+
+template <std::ranges::approximately_sized_range R>
+consteval bool check_subsumption_1() {
+  return true;
+}
+
+static_assert(check_subsumption_1<int[5]>());
+
+// sized_range subsumes approximately_sized_range
+
+template <std::ranges::approximately_sized_range R>
+consteval bool check_subsumption_2() {
+  return false;
+}
+
+template <std::ranges::sized_range R>
+consteval bool check_subsumption_2() {
+  return true;
+}
+
+static_assert(check_subsumption_2<int[5]>());


From libcxx-commits at lists.llvm.org  Sat Jul 18 23:06:33 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 18 Jul 2026 23:06:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a5c6969.bd93e731.30f155.7d1f@mx.google.com>


inquisitivecrystal wrote:

I've implemented the suggestions. I also noticed I was missing `range.req` tests for `approximately_sized_range`, so I added some.

https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Sat Jul 18 23:17:27 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 18 Jul 2026 23:17:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a5c6bf7.743400b0.23a7b2.81ed@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Sat Jul 18 23:20:48 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 18 Jul 2026 23:20:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a5c6cc0.f3151ff8.37ac76.fa51@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Sat Jul 18 23:48:19 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 18 Jul 2026 23:48:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a5c7333.f06f4994.2f0620.d0b6@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Sun Jul 19 00:15:35 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 00:15:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5c7997.27b3b003.1f84cc.235c@mx.google.com>


================
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
----------------
frederick-vs-ja wrote:

We should keep the current strategy, see [LWG4242](https://cplusplus.github.io/LWG/issue4242).

DO NOT change any code in `libcxx/include` or `libcxx/src` (possible except for comments) if you're merely marking something is resolved.

https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 00:16:02 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 00:16:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5c79b2.13be6df8.22eac4.2701@mx.google.com>


================
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(const I& first, S last);
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
----------------
frederick-vs-ja wrote:

I think it would be better to merge both test files.

https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 00:20:24 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 00:20:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5c7ab8.9edb5c9d.280be3.537a@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 00:21:26 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 00:21:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5c7af6.7270d2fd.a9bd6.3b20@mx.google.com>


https://github.com/frederick-vs-ja requested changes to this pull request.


https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 00:21:27 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 00:21:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5c7af7.507fc713.239434.dd72@mx.google.com>


================
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","24","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
----------------
frederick-vs-ja wrote:

See also the PR description modified by me. 
```suggestion
"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","14","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
```

https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 00:26:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 00:26:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a5c7c24.b4a331f0.d9742.eb11@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Sun Jul 19 00:29:45 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 00:29:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Partially implement P2846R6:
	`reserve_hint` (PR #206385)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206385@github.com>
Message-ID: <6a5c7ce9.92d2873c.aa1ff.a491@mx.google.com>


https://github.com/inquisitivecrystal edited https://github.com/llvm/llvm-project/pull/206385

From libcxx-commits at lists.llvm.org  Sun Jul 19 03:49:57 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sun, 19 Jul 2026 03:49:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5cabd5.1905e68e.30d957.442e@mx.google.com>


================
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
----------------
lknknm wrote:

Yes sorry. But given changes are necessary, should I fix the PR title wording?

https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 03:55:32 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sun, 19 Jul 2026 03:55:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5cad24.e624eb8d.4a8a7.33bf@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 04:01:41 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sun, 19 Jul 2026 04:01:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5cae95.d76d81af.23b347.82cb@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210550

>From 23293789342393617472800afc72656011d5334f Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:13:24 +0200
Subject: [PATCH 1/5] apply: changes from proposal and mark as Complete

---
 libcxx/docs/Status/Cxx23Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 91c4dc462c0c9..0e13c6dc62bfe 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..bbdd30e56730f 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
     }
   }
 

>From cbed72094e96ce8a7756aa588be4dc25f608635b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:33:56 +0200
Subject: [PATCH 2/5] clang-format

---
 libcxx/include/__iterator/distance.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index bbdd30e56730f..e0a2590253c71 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,7 +86,8 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>>
+  operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {

>From a265367976e6651929998984e3284210eab11585 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:58:32 +0200
Subject: [PATCH 3/5] update: comments in tests

---
 .../range.iter.ops.distance/iterator_sentinel.pass.cpp          | 2 +-
 .../range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index c34d59e405a9e..1888f606465b2 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(I&& first, S last); // TODO: update when LWG3664 is resolved
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <array>
 #include <cassert>
diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
index 164101c2bf5a8..bac0ffba1e7f1 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(const I& first, S last);
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <cassert>
 #include <iterator>

>From 9452d4068636d3a8c8d31b5602b9446ffd7f7fe4 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 19 Jul 2026 00:11:01 +0200
Subject: [PATCH 4/5] add version number

---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0e13c6dc62bfe..617c89508b7a3 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","24","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""

>From c8e9bf8199acfc396e09077e01eb52c240fa2636 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 19 Jul 2026 13:01:31 +0200
Subject: [PATCH 5/5] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 617c89508b7a3..4b80d5ebf3623 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","24","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","14","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""


From libcxx-commits at lists.llvm.org  Sun Jul 19 04:35:51 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 19 Jul 2026 04:35:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a5cb697.160324b0.1f653e.4fd0@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/207585

>From 02ae8f53a83139d9280f86098a7517a3036c7986 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 5 Jul 2026 14:17:28 +0100
Subject: [PATCH 1/6] Implementation of adjacent_difference on top of a binary
 transform

---
 libcxx/include/__numeric/pstl.h               |  39 ++++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  35 +++-
 .../pstl.iterator-requirements.verify.cpp     |  11 ++
 .../algorithms/pstl.nodiscard.verify.cpp      |   5 +
 .../pstl.exception_handling.pass.cpp          |  16 +-
 .../pstl.adjacent_difference.pass.cpp         | 171 +++++++++++++++++
 .../pstl.adjacent_difference_op.pass.cpp      | 181 ++++++++++++++++++
 8 files changed, 461 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp

diff --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h
index ecbc9f5348333..0b85cfb8c2327 100644
--- a/libcxx/include/__numeric/pstl.h
+++ b/libcxx/include/__numeric/pstl.h
@@ -165,6 +165,45 @@ template <class _ExecutionPolicy,
       std::move(__transform));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first1), std::move(__last1), std::move(__first2), minus{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryOperation,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _BinaryOperation __op) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__op));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a84a768cad570..2ba5b4434fc33 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -317,6 +317,13 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_difference;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+// optional<_ForwardIterator2>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5c78d4efa412b..f34c284dd2a84 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -92,11 +92,12 @@ namespace __pstl {
 //
 // transform and transform_binary family
 // -------------------------------------
-// - replace_copy_if
-// - replace_copy
-// - move
+// - adjacent_difference
 // - copy
 // - copy_n
+// - move
+// - replace_copy
+// - replace_copy_if
 // - reverse_copy
 // - rotate_copy
 //
@@ -575,6 +576,34 @@ struct __reverse_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _BinaryOperation&& __op) const noexcept {
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    if (__first1 == __last1)
+      return __first2; // edge case: empty input range, just return the output iterator
+    *__first2 = *__first1;
+    ++__first2;
+    _ForwardIterator1 __first_left = std::next(__first1);
+    if (__first_left == __last1)
+      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+    // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
+    return _TransformBinary()(
+        __policy,
+        std::move(__first_left),
+        std::move(__last1),
+        std::move(__first1),
+        std::move(__first2),
+        std::forward<_BinaryOperation>(__op));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index fbe3460b67f17..402d8fbf9131a 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -211,4 +211,15 @@ void f(non_forward_iterator non_fwd,
     (void)std::transform_reduce(
         pol, non_fwd, non_fwd, val, func, func); // expected-error@*:* {{static assertion failed: transform_reduce}}
   }
+
+  {
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+  }
 }
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 049c8dc88c2da..f4fffd456a331 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <algorithm>
 #include <execution>
 #include <iterator>
+#include <numeric>
 
 void test() {
   int a[]    = {1};
@@ -58,4 +59,8 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
 }
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 8af2a0a15fd5c..e00046fdb1dd6 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -85,7 +85,7 @@ int main(int, char**) {
       auto last1       = util::throw_on_move_iterator(std::end(a), tokens[1].active() ? 1 : -1);
       auto first2      = util::throw_on_move_iterator(std::begin(b), tokens[2].active() ? 1 : -1);
       auto last2       = util::throw_on_move_iterator(std::end(b), tokens[3].active() ? 1 : -1);
-      auto dest        = util::throw_on_move_iterator(std::end(storage), tokens[4].active() ? 1 : -1);
+      auto dest        = util::throw_on_move_iterator(std::begin(storage), tokens[4].active() ? 1 : -1);
       auto maybe_throw = [](ThrowToken const& token, auto f) {
         return [&token, f](auto... args) {
           if (token.active())
@@ -365,6 +365,20 @@ int main(int, char**) {
           (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction);
         });
       }
+
+      {
+        auto op = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
+
+        // adjacent_difference(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+
+        // adjacent_difference(first, last, dest, op)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest), op);
+        });
+      }
     }
   });
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
new file mode 100644
index 0000000000000..bb98004c1c62a
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+
+  friend X operator-(const X& x, const X& y) { return X(x.i_ - y.i_); }
+
+  friend class Y;
+};
+
+class Y {
+  int i_;
+
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 20};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, -5, -4, -3, -2};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r =
+            std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        assert(std::all_of(std::begin(ib), std::begin(ib) + i, [](int x) { return x == 1; }));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(7)};
+      Y ir[3] = {Y(1), Y(4), Y(2)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
new file mode 100644
index 0000000000000..26d65f9a77c10
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryOperation>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2,
+//                                        BinaryOperation op);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+  friend class Y;
+  friend struct Op;
+};
+
+class Y {
+  int i_;
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+// ensures that the predicate is not equivalent to std::minus
+struct Op {
+  int operator()(int l, int r) const { return l * 2 - r; }
+  X operator()(const X& l, const X& r) const { return X(l.i_ * 2 - r.i_); }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 50};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, 5, 2, 0, -1};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r = std::adjacent_difference(
+            policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)), Op{});
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j)
+          assert(ib[j] == (int)(j == 0 ? 1 : j + 2));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(8)};
+      Y ir[3] = {Y(1), Y(9), Y(11)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}

>From f62e25b47970412a33d6c44c4b4820ad3669b680 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 10:34:09 +0100
Subject: [PATCH 2/6] Renamed the variable names in __adjacent_difference

---
 libcxx/include/__pstl/backends/default.h | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index f34c284dd2a84..28bc75be147aa 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -583,23 +583,23 @@ struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
   operator()(_Policy&& __policy,
              _ForwardIterator1 __first1,
              _ForwardIterator1 __last1,
-             _ForwardIterator2 __first2,
+             _ForwardIterator2 __result,
              _BinaryOperation&& __op) const noexcept {
     using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
     if (__first1 == __last1)
-      return __first2; // edge case: empty input range, just return the output iterator
-    *__first2 = *__first1;
-    ++__first2;
-    _ForwardIterator1 __first_left = std::next(__first1);
-    if (__first_left == __last1)
-      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+      return __result; // edge case: empty input range, just return the output iterator
+    *__result = *__first1;
+    ++__result;
+    _ForwardIterator1 __first2 = std::next(__first1);
+    if (__first2 == __last1)
+      return __result; // edge case: not enough elements to perform adjacent difference, just return the output iterator
     // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
     return _TransformBinary()(
         __policy,
-        std::move(__first_left),
+        std::move(__first2),
         std::move(__last1),
         std::move(__first1),
-        std::move(__first2),
+        std::move(__result),
         std::forward<_BinaryOperation>(__op));
   }
 };

>From 012594a84a7b0bbc55c1e9457adf28531d01c97d Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 20:01:44 +0100
Subject: [PATCH 3/6] Moved runway_sample() to test/support/runway_sample.h

---
 .../pstl.adjacent_difference.pass.cpp         |  16 +--
 .../pstl.adjacent_difference_op.pass.cpp      |  16 +--
 libcxx/test/support/runway_sample.h           |  58 ++++++++++
 .../test.support/runway_sample.pass.cpp       | 101 ++++++++++++++++++
 4 files changed, 161 insertions(+), 30 deletions(-)
 create mode 100644 libcxx/test/support/runway_sample.h
 create mode 100644 libcxx/test/support/test.support/runway_sample.pass.cpp

diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
index bb98004c1c62a..4dfe3ad58adf8 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -29,27 +29,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
 
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 class X {
   int i_;
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
index 26d65f9a77c10..f6b9b480f4ba8 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -31,27 +31,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
 
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 class X {
   int i_;
   X& operator=(const X&);
diff --git a/libcxx/test/support/runway_sample.h b/libcxx/test/support/runway_sample.h
new file mode 100644
index 0000000000000..16ecd58cef747
--- /dev/null
+++ b/libcxx/test/support/runway_sample.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+#ifndef TEST_SUPPORT_RUNWAY_SAMPLE_H
+#define TEST_SUPPORT_RUNWAY_SAMPLE_H
+
+#include <cstddef>
+#include <algorithm>
+#include "test_macros.h"
+
+// runway_sample(size, callable) calls callable(i) for a set of sample indices i in the range [0, size).
+// they are chosen in 3 phases:
+//   - prefix, sampled densely with increments of 1
+//   - middle, sampled sparsely with uncanny numbers
+//   - suffix, sampled densely with increments of 1
+//
+// Examples of sample indices for various sizes:
+// size = 0:
+// <no samples>
+// size = 1:
+// 0
+// size = 10:
+// 0 1 2 3 4 5 6 7 8 9
+// size = 100:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
+// size = 1000:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 157 493 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
+template <class Callable>
+TEST_CONSTEXPR_CXX14 void runway_sample(std::size_t size, Callable callable) {
+  constexpr std::size_t affix = 16;
+  std::size_t i               = 0;
+
+  // 0, 1, 2, ..., 15
+  for (; i < std::min(size, affix); ++i) {
+    callable(i);
+  }
+
+  if (size <= affix) {
+    return;
+  }
+
+  // 16, 50, 157, 493, 1549, 4868, ...
+  for (std::size_t j = i; j + affix < size; i = j, j = j * 3 + j / 7) {
+    callable(j);
+  }
+
+  // size - 16, size - 15, ..., size - 1
+  i = std::max(i, size - affix);
+  for (; i < size; ++i) {
+    callable(i);
+  }
+}
+
+#endif // TEST_SUPPORT_RUNWAY_SAMPLE_H
diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
new file mode 100644
index 0000000000000..9fc288f881082
--- /dev/null
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++11
+
+// "support/runway_sample.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "runway_sample.h"
+
+TEST_CONSTEXPR_CXX14 bool test() {
+  {
+    bool fail = false;
+    runway_sample(0, [&](size_t /*i*/) { fail = true; });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(0, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(10, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29};
+    size_t n  = 0;
+    bool fail = false;
+    runway_sample(30, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
+                         50, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(100, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  50,
+                         157, 493, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(1000, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  return true;
+}
+
+int main() {
+  assert(test());
+#if TEST_STD_VER >= 14
+  static_assert(test());
+#endif
+  return 0;
+}

>From 1552e8b1db839c7356ba1679fda4bdfc669dd685 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 20:05:51 +0100
Subject: [PATCH 4/6] Added a note about X and Y in the tests

---
 .../adjacent.difference/pstl.adjacent_difference.pass.cpp     | 4 ++++
 .../adjacent.difference/pstl.adjacent_difference_op.pass.cpp  | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
index 4dfe3ad58adf8..d06f5b64e2f9f 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -36,6 +36,10 @@ EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
 
+// Types X and Y are provided to test adjacent_difference() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
 class X {
   int i_;
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
index f6b9b480f4ba8..d623cda058d04 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -38,6 +38,10 @@ EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
 
+// Types X and Y are provided to test adjacent_difference() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
 class X {
   int i_;
   X& operator=(const X&);

>From 40282b01dd8f83ff30534284abe9deddbf0e4136 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 21:06:40 +0100
Subject: [PATCH 5/6] Run runway_sample test on C++17+

---
 libcxx/test/support/test.support/runway_sample.pass.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
index 9fc288f881082..30e0ad20e0f85 100644
--- a/libcxx/test/support/test.support/runway_sample.pass.cpp
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// REQUIRES: std-at-least-c++11
+// REQUIRES: std-at-least-c++17
 
 // "support/runway_sample.h"
 
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "runway_sample.h"
 
-TEST_CONSTEXPR_CXX14 bool test() {
+constexpr bool test() {
   {
     bool fail = false;
     runway_sample(0, [&](size_t /*i*/) { fail = true; });
@@ -94,8 +94,6 @@ TEST_CONSTEXPR_CXX14 bool test() {
 
 int main() {
   assert(test());
-#if TEST_STD_VER >= 14
   static_assert(test());
-#endif
   return 0;
 }

>From 1759d17216f82de691906a23e19876be0262b231 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:26:32 +0100
Subject: [PATCH 6/6] Use assert() in the runway_sample() unit test

---
 .../test.support/runway_sample.pass.cpp       | 82 +++++++------------
 1 file changed, 29 insertions(+), 53 deletions(-)

diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
index 30e0ad20e0f85..6c6eaafc3439b 100644
--- a/libcxx/test/support/test.support/runway_sample.pass.cpp
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -20,80 +20,56 @@
 
 constexpr bool test() {
   {
-    bool fail = false;
-    runway_sample(0, [&](size_t /*i*/) { fail = true; });
-    if (fail) {
-      return false;
-    }
+    runway_sample(0, [&](std::size_t /*i*/) { assert(false); });
   }
   {
-    size_t expected[] = {0};
-    size_t n          = 0;
-    bool fail         = false;
-    runway_sample(0, [&](size_t i) {
-      if (expected[n++] != i)
-        fail = true;
+    std::size_t expected[] = {0};
+    std::size_t n          = 0;
+    runway_sample(1, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
     });
-    if (fail) {
-      return false;
-    }
   }
   {
-    size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-    size_t n          = 0;
-    bool fail         = false;
-    runway_sample(10, [&](size_t i) {
-      if (expected[n++] != i)
-        fail = true;
+    std::size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    std::size_t n          = 0;
+    runway_sample(10, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
     });
-    if (fail) {
-      return false;
-    }
   }
   {
-    size_t expected[] = {
+    std::size_t expected[] = {
         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29};
-    size_t n  = 0;
-    bool fail = false;
-    runway_sample(30, [&](size_t i) {
-      if (expected[n++] != i)
-        fail = true;
+    std::size_t n = 0;
+    runway_sample(30, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
     });
-    if (fail) {
-      return false;
-    }
   }
   {
-    size_t expected[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
-                         50, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99};
-    size_t n          = 0;
-    bool fail         = false;
-    runway_sample(100, [&](size_t i) {
-      if (expected[n++] != i)
-        fail = true;
+    std::size_t expected[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
+                              50, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99};
+    std::size_t n          = 0;
+    runway_sample(100, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
     });
-    if (fail) {
-      return false;
-    }
   }
   {
-    size_t expected[] = {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  50,
-                         157, 493, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999};
-    size_t n          = 0;
-    bool fail         = false;
-    runway_sample(1000, [&](size_t i) {
-      if (expected[n++] != i)
-        fail = true;
+    std::size_t expected[] = {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  50,
+                              157, 493, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999};
+    std::size_t n          = 0;
+    runway_sample(1000, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
     });
-    if (fail) {
-      return false;
-    }
   }
   return true;
 }
 
 int main() {
-  assert(test());
+  test();
   static_assert(test());
   return 0;
 }


From libcxx-commits at lists.llvm.org  Sun Jul 19 04:36:05 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 19 Jul 2026 04:36:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++]
 __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp,
 _Tp> removes cv-ref from _Tp (PR #208950)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208950@github.com>
Message-ID: <6a5cb6a5.7d702ed2.13d102.f9ed@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/208950

>From de4502a3bac34eedba2d5687e67bb6be577e51e7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 21:19:44 +0100
Subject: [PATCH 1/4] __desugars_to_v<__totally_ordered_less_tag, __less<>,...>
 strips cv-ref from _Tp

---
 libcxx/include/__algorithm/comp.h           |  4 ++-
 libcxx/test/libcxx/algorithms/comp.pass.cpp | 27 +++++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/libcxx/algorithms/comp.pass.cpp

diff --git a/libcxx/include/__algorithm/comp.h b/libcxx/include/__algorithm/comp.h
index 38e2fb9f5e744..a83b6a8938998 100644
--- a/libcxx/include/__algorithm/comp.h
+++ b/libcxx/include/__algorithm/comp.h
@@ -13,6 +13,7 @@
 #include <__type_traits/desugars_to.h>
 #include <__type_traits/is_generic_transparent_comparator.h>
 #include <__type_traits/is_integral.h>
+#include <__type_traits/remove_cvref.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -47,7 +48,8 @@ template <class _Tp>
 inline const bool __desugars_to_v<__less_tag, __less<>, _Tp, _Tp> = true;
 
 template <class _Tp>
-inline const bool __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp, _Tp> = is_integral<_Tp>::value;
+inline const bool __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp, _Tp> =
+    is_integral<__remove_cvref_t<_Tp> >::value;
 
 template <>
 inline const bool __is_generic_transparent_comparator_v<__less<> > = true;
diff --git a/libcxx/test/libcxx/algorithms/comp.pass.cpp b/libcxx/test/libcxx/algorithms/comp.pass.cpp
new file mode 100644
index 0000000000000..87095f630ee52
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/comp.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// <__algorithm/comp.h>
+
+#include <__algorithm/comp.h>
+
+// check that __less<> desugars to __totally_ordered_less_tag for integral types regardless of their cv-ref
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int, int>);
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int&, int&>);
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, const int, const int>);
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, const int&, const int&>);
+static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile int&, volatile int&>);
+static_assert(
+    std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile const int&, volatile const int&>);
+static_assert(
+    !std::
+        __desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile const float&, volatile const float&>);
+
+int main(int, char**) { return 0; }

>From dca57eead03572af638250c92ceddb9a055f9df8 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 12 Jul 2026 13:47:14 +0100
Subject: [PATCH 2/4] Include <algorithm> instead of an individual header

---
 libcxx/test/libcxx/algorithms/comp.pass.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/algorithms/comp.pass.cpp b/libcxx/test/libcxx/algorithms/comp.pass.cpp
index 87095f630ee52..0e3b813aedca3 100644
--- a/libcxx/test/libcxx/algorithms/comp.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/comp.pass.cpp
@@ -8,9 +8,9 @@
 
 // REQUIRES: std-at-least-c++17
 
-// <__algorithm/comp.h>
+// <algorithm>
 
-#include <__algorithm/comp.h>
+#include <algorithm>
 
 // check that __less<> desugars to __totally_ordered_less_tag for integral types regardless of their cv-ref
 static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int, int>);

>From 606e8708b053547c7b088af600dde89f9d469557 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:14:41 +0100
Subject: [PATCH 3/4] Include <__type_traits/desugars_to.h> as well

---
 libcxx/test/libcxx/algorithms/comp.pass.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/comp.pass.cpp b/libcxx/test/libcxx/algorithms/comp.pass.cpp
index 0e3b813aedca3..deff69eb765f7 100644
--- a/libcxx/test/libcxx/algorithms/comp.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/comp.pass.cpp
@@ -10,6 +10,8 @@
 
 // <algorithm>
 
+#include <__type_traits/desugars_to.h>
+
 #include <algorithm>
 
 // check that __less<> desugars to __totally_ordered_less_tag for integral types regardless of their cv-ref

>From 7e5c4b49d062ab704f15bfd554df8a60e3ff9809 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:07:36 +0100
Subject: [PATCH 4/4] Moved the test to desugars_to.compile.pass.cpp

---
 libcxx/test/libcxx/algorithms/comp.pass.cpp   | 29 -------------------
 .../type_traits/desugars_to.compile.pass.cpp  | 19 ++++++++++++
 2 files changed, 19 insertions(+), 29 deletions(-)
 delete mode 100644 libcxx/test/libcxx/algorithms/comp.pass.cpp

diff --git a/libcxx/test/libcxx/algorithms/comp.pass.cpp b/libcxx/test/libcxx/algorithms/comp.pass.cpp
deleted file mode 100644
index deff69eb765f7..0000000000000
--- a/libcxx/test/libcxx/algorithms/comp.pass.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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++17
-
-// <algorithm>
-
-#include <__type_traits/desugars_to.h>
-
-#include <algorithm>
-
-// check that __less<> desugars to __totally_ordered_less_tag for integral types regardless of their cv-ref
-static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int, int>);
-static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int&, int&>);
-static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, const int, const int>);
-static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, const int&, const int&>);
-static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile int&, volatile int&>);
-static_assert(
-    std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile const int&, volatile const int&>);
-static_assert(
-    !std::
-        __desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile const float&, volatile const float&>);
-
-int main(int, char**) { return 0; }
diff --git a/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp b/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp
index 9134ac027d1ed..e28dbd8fa9ff2 100644
--- a/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp
+++ b/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp
@@ -10,6 +10,7 @@
 // UNSUPPORTED: gcc && c++11
 
 #include <__type_traits/desugars_to.h>
+#include <__algorithm/comp.h>
 
 struct Tag {};
 struct Operation {};
@@ -37,4 +38,22 @@ void tests() {
     static_assert(std::__desugars_to_v<Tag, Operation&&>, "");
     static_assert(std::__desugars_to_v<Tag, Operation const&&>, "");
   }
+
+  // Make sure that __less<> desugars to __totally_ordered_less_tag for integral types regardless of their cv-ref
+  {
+    static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int, int>, "");
+    static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int&, int&>, "");
+    static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, const int, const int>, "");
+    static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, const int&, const int&>, "");
+    static_assert(
+        std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile int&, volatile int&>, "");
+    static_assert(
+        std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile const int&, volatile const int&>,
+        "");
+    static_assert(!std::__desugars_to_v<std::__totally_ordered_less_tag,
+                                        std::__less<>,
+                                        volatile const float&,
+                                        volatile const float&>,
+                  "");
+  }
 }


From libcxx-commits at lists.llvm.org  Sun Jul 19 04:36:26 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 19 Jul 2026 04:36:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::mismatch() based on __pstl::__parallel_find() (PR #209291)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209291@github.com>
Message-ID: <6a5cb6ba.eb165e5c.4506c.a27d@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/209291

>From 650ebeba6d9e4867856e679d475e8f50edcdadde Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:03:38 +0100
Subject: [PATCH 01/14] Added a basic implementation of parallel 4-legged
 mismatch() based on __parallel_find()

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/libdispatch.h  |   7 +-
 libcxx/include/__pstl/backends/serial.h       |  20 +++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  88 +++++++++++
 .../mismatch/pstl.mismatch.pass.cpp           | 139 ++++++++++++++++++
 7 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__pstl/cpu_algos/mismatch.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..6b5dc87fbe76d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/mismatch.h  
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a84a768cad570..1d64c6f47630f 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -317,6 +317,14 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index 33fd5de66fbb1..710506585ca22 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -33,6 +33,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -237,7 +238,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
       auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
       auto __index           = __chunk == 0 ? 0
                                             : (__chunk * __partitions.__chunk_size_) +
-                                        (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
+                                                  (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
       if (__this_chunk_size != 1) {
         std::__construct_at(
             __values.get() + __chunk,
@@ -364,6 +365,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..d90022ee37145 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/mismatch.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    return std::mismatch(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::forward<_Predicate>(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..93935d22b9442 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
new file mode 100644
index 0000000000000..11ffdc332d633
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_MISMATCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
+
+#include <__algorithm/mismatch.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_mismatch {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Look for a mismatch only in the prefix of the two ranges.
+      auto __n = std::min(__last1 - __first1, __last2 - __first2);
+      // Find a position in the first range where the predicate is false against the corresponding position in the
+      // second range.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __first1 + __n,
+          [&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
+            // Run the sequential mismatch algorithm on these ranges:
+            //   [__brick_first1, __brick_last1) and
+            //   [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
+            auto __brick_first2 = __first2 + (__brick_first1 - __first1);
+            return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
+                .first;
+          },
+          less<>{},
+          true);
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      auto __idx = *__res - __first1;
+      return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      // Unsequenced execution is also implicitly covered by the sequential implementation.
+      return std::mismatch(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
new file mode 100644
index 0000000000000..9cbadda428ad9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 1};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(42);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From e96f0aa34d1b212b123d4c9917928e17d26270f3 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:05:01 +0100
Subject: [PATCH 02/14] Added a definition of a parallel 4-legged
 std::mismatch()

---
 libcxx/include/__algorithm/pstl.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2bf9b4276baab..ab35b1a3e537b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -138,6 +138,29 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,

>From 472174e7343fc86a77bf314c7d088331dab6bae5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:41:51 +0100
Subject: [PATCH 03/14] Added a default implementation of __mismatch_3leg based
 on __mismatch

---
 libcxx/include/__algorithm/pstl.h             | 18 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++++
 libcxx/include/__pstl/backends/default.h      | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../mismatch/pstl.mismatch.pass.cpp           | 20 +++++++++++
 5 files changed, 84 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index ab35b1a3e537b..01c61fb00f4d5 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -161,6 +161,24 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 1d64c6f47630f..d800e60916efc 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -325,6 +325,13 @@ struct __mismatch;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch_3leg;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5c78d4efa412b..05b328beae80f 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -63,6 +63,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// mismatch family
+// ---------------
+// - mismatch_3leg
+//
 // for_each family
 // ---------------
 // - for_each_n
@@ -211,6 +215,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// mismatch family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+  optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _Comp __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Forward to the 4-legged version of mismatch.
+      using _Mismatch           = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      _ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
+      return _Mismatch()(
+          __policy,
+          std::move(__first1),
+          std::move(__last1),
+          std::move(__first2),
+          std::move(__last2),
+          std::move(__comp));
+    } else {
+      // Currently only random access iterators are supported for parallel mismatch_3leg.
+      return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..752aa35643e2b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2384,6 +2384,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module mismatch {
+        header "__pstl/cpu_algos/mismatch.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 9cbadda428ad9..0cb5c7632eed8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -10,6 +10,14 @@
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2);
+//
 // template <class ExecutionPolicy,
 //           class ForwardIterator1,
 //           class ForwardIterator2>
@@ -61,6 +69,8 @@ struct Test {
       // empty ranges
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
              std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
     }
@@ -68,18 +78,24 @@ struct Test {
       // single element only
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range without mismatch
       std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range with mismatch
       std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
     }
@@ -102,10 +118,14 @@ struct Test {
       rhs = lhs;
       runway_sample(lhs.size(), [&](size_t i) {
         lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
                std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         lhs[i] = i;
       });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }

>From 4aab10d6cc54f3abd475bd3aae03ac7d4103eb45 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:20:42 +0100
Subject: [PATCH 04/14] Added predicated versions

---
 libcxx/include/__algorithm/pstl.h             |  49 +++++
 .../mismatch/pstl.mismatch.pass.cpp           |   2 +
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 178 ++++++++++++++++++
 3 files changed, 229 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 01c61fb00f4d5..bc48fd9f48d7f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
+#  include <__utility/pair.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -138,6 +139,31 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
@@ -161,6 +187,29 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 0cb5c7632eed8..8584f2779927f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -42,6 +42,8 @@
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
+static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
new file mode 100644
index 0000000000000..39e952cbc38f6
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     BinaryPredicate pred);
+//
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2,
+//                                                     BinaryPredicate pred);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+struct Pred {
+  bool operator()(int a, int b) const { return a * 2 == b; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      assert(std::mismatch(
+                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {1};
+      std::array<int, 1> rhs = {2};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 2};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(84);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From 2da95a5c532de58960651a18c9a93c9435577a89 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:39:47 +0100
Subject: [PATCH 05/14] Added tests against custom types

---
 libcxx/include/__pstl/backends/default.h      |  1 +
 .../mismatch/pstl.mismatch.pass.cpp           | 53 ++++++++++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 56 ++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 05b328beae80f..f4f73e81ed90b 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -16,6 +16,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 8584f2779927f..5e46831539c30 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -63,6 +63,30 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
+
 template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
@@ -150,6 +174,29 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -157,5 +204,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 39e952cbc38f6..9c0dfc6c0c127 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -67,8 +67,31 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
 struct Pred {
-  bool operator()(int a, int b) const { return a * 2 == b; }
+  bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
+  bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
 };
 
 template <class Iter1, class Iter2>
@@ -167,6 +190,31 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -174,5 +222,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }

>From 2fb2bb1a154212dc1cbf3a1ea9e3e1cf5a00db48 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:52:50 +0100
Subject: [PATCH 06/14] Added a nodiscard test

---
 libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 049c8dc88c2da..c538d7841dce6 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -58,4 +58,12 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }

>From 8c35d0cac6fc6a3daf494de08ac0a2fd365d50f1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:57:47 +0100
Subject: [PATCH 07/14] Added an iterator requirement test

---
 .../pstl.iterator-requirements.verify.cpp        | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index fbe3460b67f17..4526cad0615b0 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, pred);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}

>From a8b984852860283d06ab06bbdf05a28f01965326 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:00:09 +0100
Subject: [PATCH 08/14] Added an include of <__algorithm/min.h>

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 11ffdc332d633..8361731e78cfc 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,8 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__algorithm/mismatch.h>
 #include <__config>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/min.h>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>

>From 3845b7e0e242d455f0fcc68a814ed625544c3d8c Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:05:29 +0100
Subject: [PATCH 09/14] Added an exception handling test

---
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  4 +--
 .../pstl.exception_handling.pass.cpp          | 25 +++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 8361731e78cfc..597a47fb185c8 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,9 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__config>
-#include <__algorithm/mismatch.h>
 #include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__config>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 8af2a0a15fd5c..820a5d92c19e0 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -248,6 +248,31 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // mismatch(first1, last1, first2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
+        });
+
+        // mismatch(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // mismatch(first1, last1, first2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
+        });
+
+        // mismatch(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From fbc073f5742c81ae78d12058ecf4618213afb6f0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 09:43:45 +0100
Subject: [PATCH 10/14] Changed the variable name to avoid shadowing

---
 .../alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 12 ++++++------
 .../mismatch/pstl.mismatch_pred.pass.cpp             | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 5e46831539c30..941fe07b58327 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -67,22 +67,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 9c0dfc6c0c127..a4b15a56bd071 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -71,22 +71,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Pred {

>From baaa82257b3d2f94d4bf9d02f6761e7cf3ee9df6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 20:27:14 +0100
Subject: [PATCH 11/14] Use raw C array in the tests

---
 .../mismatch/pstl.mismatch.pass.cpp           | 153 +++++++-------
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 192 +++++++++++-------
 2 files changed, 198 insertions(+), 147 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 941fe07b58327..17edf34ee7832 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -28,7 +28,6 @@
 //                                                     ForwardIterator2 last2);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -93,82 +92,98 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(
+          std::mismatch(
+              policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
+          std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 1};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 1};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(
+            std::mismatch(
+                policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+            std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(42);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 42);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(
+                   policy,
+                   Iter1(std::begin(lhs)),
+                   Iter1(std::begin(lhs) + i),
+                   Iter2(std::begin(rhs)),
+                   Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i)) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -179,20 +194,22 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index a4b15a56bd071..18c4fe1abad0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -32,7 +32,6 @@
 //                                                     BinaryPredicate pred);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -100,91 +99,120 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(
-                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::begin(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::begin(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {1};
-      std::array<int, 1> rhs = {2};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {1};
+      int rhs[1] = {2};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 2};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 2};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(84);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 84);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::begin(lhs) + i),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -195,22 +223,28 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };

>From 212e25406377a6a5da7fc7679015000a4086e4a1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:08:55 +0100
Subject: [PATCH 12/14] Fixed typos in the comment

---
 .../algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 2 +-
 .../alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 17edf34ee7832..ed59fa9eca669 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -46,7 +46,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 18c4fe1abad0c..338503d784a31 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -50,7 +50,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;

>From b80675016128dbeb1db5a3084edee9bb6ab89fea Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:43:20 +0100
Subject: [PATCH 13/14] Added comments explaining what the parameters mean

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 597a47fb185c8..3d702b03d3afe 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -63,8 +63,9 @@ struct __cpu_parallel_mismatch {
             return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
                 .first;
           },
-          less<>{},
-          true);
+          less<>{}, // `less` here means the lowest index among the mismatches
+          true      // `true` here means we want the first mismatch, not the last
+      );
       if (!__res) {
         return std::nullopt; // Failed to run the algorithm, propagate the error.
       }

>From 12ac822e8571b5d944ffea6d4f62396a954f4672 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:50:51 +0100
Subject: [PATCH 14/14] Added tests of the return types

---
 .../mismatch/pstl.mismatch.pass.cpp              | 10 ++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp         | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index ed59fa9eca669..cc7ef6feb2356 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -90,6 +90,16 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1]       = {0};
+      int rhs[1]       = {0};
+      auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
+      auto res_4legged = std::mismatch(
+          policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 338503d784a31..d80da0cf0b542 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -97,6 +97,22 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      auto res_3legged =
+          std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
+      auto res_4legged = std::mismatch(
+          policy,
+          Iter1(std::begin(lhs)),
+          Iter1(std::begin(lhs)),
+          Iter2(std::begin(rhs)),
+          Iter2(std::begin(rhs)),
+          Pred{});
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};


From libcxx-commits at lists.llvm.org  Sun Jul 19 06:37:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 06:37:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 5c7dc8e - [libc++][ranges] Add missing
 test cases for `[[nodiscard]]` tests in `enumerate_view` (#209289)
Message-ID: <6a5cd306.9edb5c9d.280be3.80e6@mx.google.com>


Author: Lucas Mellone
Date: 2026-07-19T16:37:05+03:00
New Revision: 5c7dc8e7d72e8f833e5fa4d4790e93ff76b51304

URL: https://github.com/llvm/llvm-project/commit/5c7dc8e7d72e8f833e5fa4d4790e93ff76b51304
DIFF: https://github.com/llvm/llvm-project/commit/5c7dc8e7d72e8f833e5fa4d4790e93ff76b51304.diff

LOG: [libc++][ranges] Add missing test cases for `[[nodiscard]]` tests in `enumerate_view` (#209289)

Addresses missing `[[nodiscard]]` test cases for
`std::views::enumerate`.

---------

Co-authored-by: Hristo Hristov <zingam at outlook.com>

Added: 
    

Modified: 
    libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
index 06b0d6a8c148b..99b57f6c5465c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp
@@ -12,10 +12,21 @@
 
 #include <ranges>
 #include <utility>
-#include <vector>
+
+struct View : std::ranges::view_interface<View> {
+  int* begin();
+  const int* begin() const;
+  volatile int* end();
+  const volatile int* end() const;
+};
+static_assert(!std::ranges::common_range<View>);
+static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
+static_assert(std::ranges::sized_range<View>);
+static_assert(std::ranges::forward_range<View>);
 
 void test() {
-  std::vector<int> range;
+  View range;
   std::ranges::enumerate_view ev{range};
 
   // [range.enumerate.view]
@@ -55,6 +66,8 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   it + 1;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   it - 1;
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   it - it;
@@ -78,4 +91,4 @@ void test() {
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::views::enumerate(range);
-}
\ No newline at end of file
+}


        

From libcxx-commits at lists.llvm.org  Sun Jul 19 06:37:20 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 06:37:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Add missing test cases
 for `[[nodiscard]]` tests in `enumerate_view` (PR #209289)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209289@github.com>
Message-ID: <6a5cd310.8e75e0cb.f69c7.b72a@mx.google.com>


https://github.com/Zingam closed https://github.com/llvm/llvm-project/pull/209289

From libcxx-commits at lists.llvm.org  Sun Jul 19 07:40:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 07:40:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2442R1
	`std::views::chunk` (PR #171234)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171234@github.com>
Message-ID: <6a5ce1ca.449bfb44.7b862.9020@mx.google.com>


https://github.com/shyeyian updated https://github.com/llvm/llvm-project/pull/171234

>From f6554ea2a0e1d9571cf82fc7b2f3d1115c466474 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at petalmail.com>
Date: Wed, 24 Dec 2025 22:05:29 +0800
Subject: [PATCH 01/20] Implement `views::chunk`.

This commit squashes previous commits.
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/22.rst               |   1 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/chunk_view.h          | 562 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/ranges                         |  12 +
 libcxx/include/version                        |   2 +-
 libcxx/modules/std/ranges.inc                 |  11 +-
 .../no_unique_address.compile.pass.cpp        |  53 ++
 .../range.chunk/nodiscard.verify.cpp          |  86 +++
 .../ranges.version.compile.pass.cpp           |  32 +-
 .../version.version.compile.pass.cpp          |  32 +-
 .../range.chunk/adaptor.pass.cpp              |  75 +++
 .../range.adaptors/range.chunk/base.pass.cpp  |  46 ++
 .../range.adaptors/range.chunk/begin.pass.cpp |  67 +++
 .../range.chunk/ctad.compile.pass.cpp         |  55 ++
 .../range.adaptors/range.chunk/end.pass.cpp   |  65 ++
 .../range.chunk/general.pass.cpp              |  48 ++
 .../range.chunk.iter/compare.pass.cpp         | 109 ++++
 .../range.chunk.iter/decrement.pass.cpp       |  60 ++
 .../range.chunk.iter/deref.pass.cpp           |  77 +++
 .../range.chunk.iter/increment.pass.cpp       |  95 +++
 .../ranges/range.adaptors/range.chunk/types.h |  50 ++
 .../generate_feature_test_macro_components.py |   1 -
 25 files changed, 1494 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/chunk_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..8e57305f32ad1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -370,7 +370,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_rvalue``                             ``202207L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_chunk``                                 *unimplemented*
+    ``__cpp_lib_ranges_chunk``                                 ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                              ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0d1a1fbc00f2c..d2a2d1b712689 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,6 +54,7 @@ Implemented Papers
 - 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>`__)
+- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e4c6cb0f51065..8c8ab211e5bf4 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__","Only ``ranges::iota`` is implemented."
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__",""
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index eb423ae923b4f..2426da6848b51 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -707,6 +707,7 @@ set(files
   __ranges/all.h
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
+  __ranges/chunk_view.h
   __ranges/common_view.h
   __ranges/concepts.h
   __ranges/container_compatible_range.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
new file mode 100644
index 0000000000000..f19d2c9ede8a7
--- /dev/null
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_CHUNK_VIEW_H
+#define _LIBCPP___RANGES_CHUNK_VIEW_H
+
+#include <__algorithm/ranges_min.h>
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/take_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
+
+  class __outer_iterator;
+  class __inner_iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n), __remainder_(0) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires std::copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator begin() {
+    __current_.__emplace(ranges::begin(__base_));
+    __remainder_ = __n_;
+    return __outer_iterator(*this);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept {
+    return std::default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(chunk_view& __parent)
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  class value_type;
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator(__outer_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    return value_type(*__parent_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
+    __parent_->__remainder_ = __parent_->__n_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __outer_iterator& __i, default_sentinel_t) {
+    return *__i.__parent_->__current_ == ranges::end(__i.__parent_->__base_) && __i.__parent_->__remainder_ != 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __outer_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
+    if (__dist < __i.__parent_->__remainder_)
+      return __dist == 0 ? 0 : 1;
+    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __outer_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator::value_type : public view_interface<value_type> {
+  friend __outer_iterator;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(chunk_view& __parent) : __parent_(std::addressof(__parent)) {}
+
+public:
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator begin() const noexcept {
+    return __inner_iterator(*__parent_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return std::__to_unsigned_like(
+        ranges::min(__parent_->__remainder_, ranges::end(__parent_->__base_) - *__parent_->__current_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__inner_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(chunk_view& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+  using value_type       = range_value_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator(__inner_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator& operator=(__inner_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_View> base() const& { return *__parent_->__current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_reference_t<_View> operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
+    return **__parent_->__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    ++*__parent_->__current_;
+    if (*__parent_->__current_ == ranges::end(__parent_->__base_))
+      __parent_->__remainder_ = 0;
+    else
+      --__parent_->__remainder_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __inner_iterator& __i, default_sentinel_t) {
+    return __i.__parent_->__remainder_ == 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __inner_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return ranges::min(__i.__parent_->__remainder_, ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __inner_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
+    return ranges::iter_move(*__i.__parent_->__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void
+  iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) noexcept(
+      noexcept((ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_))))
+    requires indirectly_swappable<iterator_t<_View>>
+  {
+    return ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_);
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+class chunk_view<_View> : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+
+  template <bool _Const>
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires forward_range<const _View>
+  {
+    return __iterator<true>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    if constexpr (common_range<_View> && sized_range<_View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<false>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<_View> && !bidirectional_range<_View>)
+      return __iterator<false>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires forward_range<const _View>
+  {
+    if constexpr (common_range<const _View> && sized_range<const _View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<true>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<const _View> && !bidirectional_range<const _View>)
+      return __iterator<true>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+template <bool _Const>
+class chunk_view<_View>::__iterator {
+  friend chunk_view;
+
+  using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, chunk_view>;
+  using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
+
+  iterator_t<_Base> __current_         = iterator_t<_Base>();
+  sentinel_t<_Base> __end_             = sentinel_t<_Base>();
+  range_difference_t<_Base> __n_       = 0;
+  range_difference_t<_Base> __missing_ = 0;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      _Parent* __parent, iterator_t<_Base> __current, range_difference_t<_Base> __missing = 0)
+      : __current_(__current), __end_(ranges::end(__parent->__base_)), __n_(__parent->__n_), __missing_(__missing) {}
+
+  [[nodiscard]] static consteval auto __get_iterator_concept() {
+    if constexpr (random_access_range<_Base>)
+      return random_access_iterator_tag{};
+    else if constexpr (bidirectional_range<_Base>)
+      return bidirectional_iterator_tag{};
+    else
+      return forward_iterator_tag{};
+  }
+
+public:
+  using iterator_category = input_iterator_tag;
+  using iterator_concept  = decltype(__iterator::__get_iterator_concept());
+  using value_type        = decltype(views::take(subrange(__current_, __end_), __n_));
+  using difference_type   = range_difference_t<_Base>;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> &&
+                 convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+      : __current_(std::move(__i.__current_)),
+        __end_(std::move(__i.__end_)),
+        __n_(__i.__n_),
+        __missing_(__i.__missing_) {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() const { return __current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to dereference past-the-end chunk_view iterator");
+    return views::take(subrange(__current_, __end_), __n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator[](difference_type __pos) const
+    requires random_access_range<_Base>
+  {
+    return *(*this + __pos);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    __missing_ = ranges::advance(__current_, __n_, __end_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires bidirectional_range<_Base>
+  {
+    ranges::advance(__current_, __missing_ - __n_);
+    __missing_ = 0;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    if (__x > 0) {
+      _LIBCPP_ASSERT_PEDANTIC(ranges::distance(__current_, __end_) > __n_ * (__x - 1),
+                              "Trying to advance chunk_view iterator out of range");
+      ranges::advance(__current_, __n_ * (__x - 1));
+      __missing_ = ranges::advance(__current_, __n_, __end_);
+    } else if (__x < 0) {
+      ranges::advance(__current_, __n_ * __x + __missing_);
+      __missing_ = 0;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    return *this += -__x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
+    return __x.__current_ == __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, default_sentinel_t) {
+    return __x.__current_ == __x.__end_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __x.__current_ < __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __y < __x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__y < __x);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__x < __y);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(difference_type __pos, const __iterator& __i)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator-(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r -= __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, const __iterator& __j)
+    requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+  {
+    return (__i.__current_ - __j.__current_ + __i.__missing_ - __j.__missing_) / __i.__n_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <class _Range>
+chunk_view(_Range&&, range_difference_t<_Range>) -> chunk_view<views::all_t<_Range>>;
+
+template <class _View>
+inline constexpr bool enable_borrowed_range<chunk_view<_View>> = forward_range<_View> && enable_borrowed_range<_View>;
+
+namespace views {
+namespace __chunk {
+struct __fn {
+  template <viewable_range _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range, range_difference_t<_Range> __n) noexcept(
+      noexcept(/*-----*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))))
+      -> decltype(/*--*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))) {
+    return /*---------*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n));
+  }
+
+  template <class _DifferenceType>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_DifferenceType __n) noexcept(is_nothrow_constructible_v<decay_t<_DifferenceType>, _DifferenceType>) {
+    return __pipeable(std::__bind_back(__fn{}, std::forward<_DifferenceType>(__n)));
+  }
+};
+
+} // namespace __chunk
+
+inline namespace __cpo {
+inline constexpr auto chunk = __chunk::__fn{};
+
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_CHUNK_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4434a5446e35b..cb03eb8d14067 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1863,6 +1863,10 @@ module std [system] {
       header "__ranges/chunk_by_view.h"
       export std.functional.bind_back
     }
+    module chunk_view {
+      header "__ranges/chunk_view.h"
+      export std.functional.bind_back
+    }
     module common_view                    { header "__ranges/common_view.h" }
     module concepts                       { header "__ranges/concepts.h" }
     module container_compatible_range     { header "__ranges/container_compatible_range.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 9f725b12ac6c2..9ec6a4a819919 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -396,6 +396,17 @@ namespace std::ranges {
   class chunk_by_view;                                                      // C++23
 
   namespace views { inline constexpr unspecified chunk_by = unspecified; }  // C++23
+
+  // [range.chunk]
+  template <view V>
+    requires input_range<V>
+  class chunk_view;                                                     // C++23
+
+  template <view V>
+    requires forward_range<V>
+  class chunk_view<V>;                                                  // C++23
+
+  namespace views { inline constexpr unspecified chunk = unspecified; } // C++23
 }
 
 namespace std {
@@ -482,6 +493,7 @@ namespace std {
 #    include <__ranges/adjacent_view.h>
 #    include <__ranges/as_rvalue_view.h>
 #    include <__ranges/chunk_by_view.h>
+#    include <__ranges/chunk_view.h>
 #    include <__ranges/from_range.h>
 #    include <__ranges/join_with_view.h>
 #    include <__ranges/repeat_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index ab781466f5ed5..f5fc33674f521 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -523,7 +523,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges                               202406L
 // # define __cpp_lib_ranges_as_const                      202207L
 # define __cpp_lib_ranges_as_rvalue                     202207L
-// # define __cpp_lib_ranges_chunk                         202202L
+# define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
 # define __cpp_lib_ranges_contains                      202207L
 # define __cpp_lib_ranges_find_last                     202207L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 5caa2c052c00f..631ae10c922ae 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -314,18 +314,17 @@ export namespace std {
       using std::ranges::views::adjacent_transform;
       using std::ranges::views::pairwise_transform;
     } // namespace views
+#endif
 
-#endif // _LIBCPP_STD_VER >= 23
-
-#if 0
+#if _LIBCPP_STD_VER >= 23
     using std::ranges::chunk_view;
 
-    using std::ranges::chunk_view<V>;
-
     namespace views {
       using std::ranges::views::chunk;
-    }
+    } // namespace views
+#endif
 
+#if 0
     using std::ranges::slide_view;
 
     namespace views {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
new file mode 100644
index 0000000000000..14c618c505e70
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// This test ensures that we use `[[no_unique_address]]` in `chunk_view`.
+
+#include <cstddef>
+#include <ranges>
+#include <string>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct input_view {
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<input_view> = true;
+static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
+using CV1 = std::ranges::chunk_view<input_view>;
+// Expected CV1 (with View == input) layout:
+// [[no_unique_address]] _View __base_                                         // size: 0
+// [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] __non_propagating_cache<iterator_t<_View>> __current_ // size: sizeof(__non_propagating_cache<cpp20_input_iterator<int*>>), align: std::ptrdiff_t
+static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>) == alignof(std::ptrdiff_t));
+static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
+                                 sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
+
+using CV2 = std::ranges::chunk_view<forward_view>;
+// Expected CV2 (with View >= forward) layout:
+// [[no_unique_address]] _View __base_             // size: 0
+// [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
+static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..5e557715f4b03
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
+  auto view     = range | std::views::chunk(2);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(std::as_const(view)).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(view).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(view.begin());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(std::as_const(view).begin());
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 5116864879485..41e1603f62350 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -270,17 +270,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -387,17 +381,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
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 b3b424a1d77ce..bd1b55ccc8dfa 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
@@ -5700,17 +5700,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -7619,17 +7613,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
new file mode 100644
index 0000000000000..657e16383256b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   std::views::chunk
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                       = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::ref_view<std::array<int, 8>> view = array | std::views::all;
+
+  // Test `views::chunk(view, n)`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        std::views::chunk(view, 2);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::views::chunk(std::as_const(view), 2);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2}));
+  }
+
+  // Test `views::chunk(n)(range)`
+  {
+    static_assert(noexcept(std::views::chunk(2)));
+    /*__pipable*/ auto adaptor = std::views::chunk(3);
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        adaptor(view);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        adaptor(std::as_const(view));
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3}));
+  }
+
+  // Test `view | views::chunk`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        view | std::views::chunk(4);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3, 4}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::as_const(view) | std::views::chunk(4);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3, 4}));
+  }
+
+  // Test `views::chunk | adaptor`
+  {
+    /*__pipable*/ auto adaptors            = std::views::chunk(5) | std::views::join;
+    std::ranges::input_range auto rejoined = view | adaptors;
+    assert(std::ranges::equal(rejoined, view));
+    std::ranges::input_range auto const_rejoined = std::as_const(view) | adaptors;
+    assert(std::ranges::equal(const_rejoined, view));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
new file mode 100644
index 0000000000000..ac17f97a0a292
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() &&;
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <memory>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                                                   = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>> chunked = array | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
+      std::as_const(array) | std::views::chunk(4);
+
+  // Test `chunk_view.base()`
+  {
+    std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
+    assert(std::addressof(base) == std::addressof(array));
+
+    std::same_as<const std::array<int, 8>&> decltype(auto) const_base = const_chunked.base().base();
+    assert(std::addressof(const_base) == std::addressof(array));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
new file mode 100644
index 0000000000000..3cccd7d1bebc6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr __outer_iterator begin();
+
+//   V models forward_range:
+//     constexpr auto begin() requires (!__simple_view<V>);
+//     constexpr auto begin() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.begin()` when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == input_chunked.end());
+  }
+
+  // Test `chunk_view.begin()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == chunked.end());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.begin();
+    assert(std::ranges::equal(*const_it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++const_it, std::vector{7, 8}));
+    assert(++const_it == const_chunked.end());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..9bf2baba58c78
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class R>
+//   chunk_view(R&&, range_difference_t<R>) -> chunk_view<all_t<R>>;
+
+#include <ranges>
+
+struct view : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct range {
+  int* begin() const;
+  int* end() const;
+};
+
+struct borrowed_range {
+  int* begin() const;
+  int* end() const;
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<borrowed_range> = true;
+
+void test_ctad() {
+  view v;
+  range r;
+  borrowed_range br;
+
+  // clang-format off
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(v, 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(v), 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(r, 0)), 
+                                      std::ranges::chunk_view<std::ranges::ref_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(r), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(br, 0)),
+                                      std::ranges::chunk_view<std::ranges::ref_view<borrowed_range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(br), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<borrowed_range>>>);
+  // clang-format on
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
new file mode 100644
index 0000000000000..73eb0d967f6a2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr default_sentinel_t end();
+
+//   V moduels forward_range:
+//     constexpr auto end() requires (!__simple_view<V>);
+//     constexpr auto end() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.end()` when V models only input_range
+  {
+    static_assert(noexcept(input_chunked.end()));
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it = input_chunked.end();
+  }
+
+  // Test `chunk_view.end()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--it, std::vector{1, 2, 3}));
+    assert(it == chunked.begin());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.end();
+    assert(std::ranges::equal(*--const_it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--const_it, std::vector{1, 2, 3}));
+    assert(const_it == const_chunked.begin());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
new file mode 100644
index 0000000000000..89891bc093080
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   General tests for chunk_view. This file does not test anything specifically.
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <string_view>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::string_view str = "Cheese with chicken chunk by chunk on truck with my trick";
+  // clang-format off
+  auto str2 = str 
+            | std::views::chunk(4)
+            | std::views::join
+            | std::views::chunk(314159)
+            | std::views::take(1)
+            | std::views::join
+            | std::views::lazy_split(' ')
+            | std::views::chunk(2)
+            | std::views::transform([] (auto&& subview)
+              {
+                return subview | std::views::join_with(' ');
+              })
+            | std::views::join_with(' ');
+  // clang-format on
+  assert(std::ranges::equal(str, str2));
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
new file mode 100644
index 0000000000000..d86726d958e6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator<(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator<=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base> &&
+//                three_way_comparable<iterator_t<Base>>;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t)`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    std::ranges::advance(it, 4);
+    assert(it == std::default_sentinel);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(input_chunked.end() - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - input_chunked.end() == -4);
+  }
+
+  // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() == chunked.begin());
+    assert(chunked.end() == chunked.end());
+  }
+
+  // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() < chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() > chunked.begin());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() <= chunked.begin());
+    assert(chunked.begin() <= chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() >= chunked.end());
+    assert(chunked.end() >= chunked.begin());
+  }
+
+  // Test `friend constexpr auto operator<=>(const iterator& x, const iterator& y)`
+  {
+    assert((chunked.begin() <=> chunked.begin()) == std::strong_ordering::equal);
+    assert((chunked.begin() <=> chunked.end()) == std::strong_ordering::less);
+    assert((chunked.end() <=> chunked.begin()) == std::strong_ordering::greater);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
new file mode 100644
index 0000000000000..397b32e73080a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+//   constexpr iterator& operator--()
+//     requires bidirectional_range<Base>;
+//   constexpr iterator operator--(int)
+//     requires bidirectional_range<Base>;
+//   constexpr iterator& operator-=(difference_type)
+//     requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+
+  // Test `constexpr iterator& operator--();`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator operator--(int)`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    it--;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator& operator-=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.end();
+    it -= 3;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
new file mode 100644
index 0000000000000..b8772d0fac3d2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr value_type outer_iterator::operator*() const;
+//     constexpr inner_iterator outer_iterator::value_type::begin() const noexcept;
+//     constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept;
+//     constexpr range_reference_v<V> inner_iterator::operator*() const;
+
+//   V models forward_range:
+//     constexpr value_type iterator::operator*() const;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `constexpr value_type outer_iterator::operator*() const`
+  {
+    static_assert(std::ranges::input_range<decltype(*input_chunked.begin())>);
+  }
+
+  // Test `constexpr inner_iterator outer_iterator::value_type::begin() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    assert(*inner.begin() == *vector.begin());
+    static_assert(noexcept(inner.begin()));
+  }
+
+  // Test `constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it                   = inner.end();
+    static_assert(noexcept((inner.end())));
+  }
+
+  // Test `constexpr value_type iterator::operator*() const`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::same_as<int> decltype(auto) v                           = *it;
+    assert(v == 1);
+  }
+
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
+  {
+    std::same_as<int&> decltype(auto) v = *(*chunked.begin()).begin();
+    assert(v == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
new file mode 100644
index 0000000000000..6048aba027ce9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     constexpr outer_iterator& operator++();
+//     constexpr void operator++(int);
+//     constexpr inner_iterator& operator++();
+//     constexpr void operator++(int);
+
+//   V models forward_range
+//     constexpr iterator& operator++();
+//     constexpr iterator operator++(int);
+//     constexpr iterator& operator+=(difference_type)
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(2);
+
+  // Test `constexpr outer_iterator& operator++();`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr void operator++(int);`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    assert(*++it == 2);
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(*it == 2);
+  }
+
+  // Test `constexpr iterator& operator++();`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator operator++(int)`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator& operator+=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    it += 3;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..09d16c9a48af0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f61d6f991cb15..49fb196903e77 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1106,7 +1106,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_ranges_chunk",
             "values": {"c++23": 202202},
             "headers": ["ranges"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ranges_chunk_by",

>From af914e794ddb86103e6b7acf3bf17d40f5055a8a Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 01:44:18 +0800
Subject: [PATCH 02/20] Code Review: adjust release notes

Note that we will merge this commit with the corresponding change in #172948 in the future.
---
 libcxx/docs/ReleaseNotes/22.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index d2a2d1b712689..30fc22fc0d787 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,7 +54,7 @@ Implemented Papers
 - 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>`__)
-- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------

>From dc62f798ba5615dcac60fffa2cc0afa8b79344c5 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 13:03:41 +0800
Subject: [PATCH 03/20] Add Tests: test if `_LIBCPP_ASSERT_XXX` works when
 `views::chunk` meets an undefined behaviour

---
 libcxx/include/__ranges/chunk_view.h          |  6 ++-
 .../range.chunk/assert.constructor.pass.cpp   | 39 ++++++++++++++
 .../range.chunk.iter/assert.advance.pass.cpp  | 32 ++++++++++++
 .../range.chunk.iter/assert.deref.pass.cpp    | 52 +++++++++++++++++++
 .../assert.increment.pass.cpp                 | 52 +++++++++++++++++++
 .../ranges/range.adaptors/range.chunk/types.h | 50 ++++++++++++++++++
 6 files changed, 229 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f19d2c9ede8a7..683b6bbaa2ca5 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -128,11 +128,12 @@ class chunk_view<_View>::__outer_iterator {
   _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
-    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
     return value_type(*__parent_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
     __parent_->__remainder_ = __parent_->__n_;
     return *this;
@@ -214,6 +215,7 @@ class chunk_view<_View>::__inner_iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ++*__parent_->__current_;
     if (*__parent_->__current_ == ranges::end(__parent_->__base_))
       __parent_->__remainder_ = 0;
@@ -386,7 +388,7 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
-    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to increment past-the-end chunk_view iterator");
     __missing_ = ranges::advance(__current_, __n_, __end_);
     return *this;
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
new file mode 100644
index 0000000000000..03fc7dfb6a5de
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Construct a chunk_view with chunk size <= 0
+
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3};
+
+  // Test constructor when V models only input_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  // Test constructor when V models forward_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
new file mode 100644
index 0000000000000..af2b826aa8eba
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to advance chunk_view iterator out of range
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+
+  // Test advance past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    TEST_LIBCPP_ASSERT_FAILURE(it += 4, "Trying to advance chunk_view iterator out of range");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
new file mode 100644
index 0000000000000..cb1d92885e684
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to dereference past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test dereference iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*outer_it, "Trying to dereference past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*inner_it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+
+  // Test dereference iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(*it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
new file mode 100644
index 0000000000000..8160ee1bb3e14
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to increment past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test increment past-the-end iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++outer_it, "Trying to increment past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++inner_it, "Trying to increment past-the-end chunk_view iterator");
+  }
+
+  // Test increment past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(++it, "Trying to increment past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..a5d688abfa1b0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif

>From b664c0cb8f8ed9b46629fa49e4bdd846734615d4 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:26 +0800
Subject: [PATCH 04/20] Fix CI: adjust `#include` path in `test/libcxx` files

---
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp          | 2 +-
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index cb1d92885e684..0c96742147189 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 8160ee1bb3e14..451683bab71b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};

>From 4dc9b06c0dd515030226b9d27841352ef62549a3 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:58 +0800
Subject: [PATCH 05/20] Fix CI: add template deduction guide for
 `input_span<T>`

---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 3 +++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..0978a23c37244 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..4ff1af1d7cda3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 1e8906ad49a2406158161cc1b9d0e1570af2959d Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:53:21 +0800
Subject: [PATCH 06/20] Code Review: add `REQUIRES: std-at-least-c++23` in all
 the tests related to `views::chunk`.

The previous commit, 4dc9b06c0dd515030226b9d27841352ef62549a3, which introduce manual/explicit template deduction guide in `input_span<T>`, is always implicitly-provided under -std=c++23. So this commit also reverts the changes about `types.h` in the previous commit.
---
 .../range.adaptors/range.chunk/assert.constructor.pass.cpp     | 1 +
 .../range.chunk/range.chunk.iter/assert.advance.pass.cpp       | 1 +
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp         | 1 +
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp     | 1 +
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h   | 3 ---
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h      | 3 ---
 6 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
index 03fc7dfb6a5de..477f89be5f070 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
index af2b826aa8eba..9b91d94841b91 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index 0c96742147189..3640b04192bf4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 451683bab71b0..61c096f74158e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index 0978a23c37244..a5d688abfa1b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 4ff1af1d7cda3..09d16c9a48af0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 7e21388c70b38c32125e4d3eda9b3a4c66e1d3db Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 14:13:59 +0800
Subject: [PATCH 07/20] Fix CI: add template deduction guide for
 `input_span<T>` again.

Seems that even in C++23 the CI fails due to no implicit deduction guide, here we just add the explicit one and make the CI to basically run ok.
---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 4 ++++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..4852ec29204bb 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..7533e41713d72 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From f1ed40a1aed2a1f5ebd1e56efcd6ae2bbb25dc26 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:07:21 +0800
Subject: [PATCH 08/20] Code review: Update libcxx/test synopsis

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 .../ranges/range.adaptors/range.chunk/nodiscard.verify.cpp      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 5e557715f4b03..9264256fc6a52 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+// Test that functions are marked [[nodiscard]].
 
 #include <ranges>
 #include <utility>

>From 12bec119ba49cd6016055f5887f5e83efa25ee30 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:21:29 +0800
Subject: [PATCH 09/20] Code review: Fix synopsis.

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index ac17f97a0a292..b45e1f59f1394 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() const& requires copy_constructible<V>;
 //   constexpr V base() &&;
 
 #include <array>

>From 1e05c5a038dd4cee5a0049b8c7e92e59c8aabd82 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 21:13:46 +0800
Subject: [PATCH 10/20] Code review: sort and unique the tests

---
 .../no_unique_address.compile.pass.cpp        |  24 ++--
 .../range.chunk/nodiscard.verify.cpp          |  35 ++----
 .../range.chunk.iter/arithmetic.pass.cpp      | 108 ++++++++++++++++++
 .../range.chunk.iter/compare.pass.cpp         |  29 +++--
 .../range.chunk.iter/subscript.pass.cpp       |  42 +++++++
 5 files changed, 189 insertions(+), 49 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
index 14c618c505e70..5d32daf4c178a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -20,6 +20,8 @@
 #include "test_iterators.h"
 #include "test_range.h"
 
+// When V models only input_range
+
 struct input_view {
   cpp20_input_iterator<int*> begin() const;
   sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
@@ -28,16 +30,8 @@ template <>
 inline constexpr bool std::ranges::enable_view<input_view> = true;
 static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
 
-struct forward_view {
-  int* begin() const;
-  int* end() const;
-};
-template <>
-inline constexpr bool std::ranges::enable_view<forward_view> = true;
-static_assert(std::ranges::forward_range<forward_view>);
-
 using CV1 = std::ranges::chunk_view<input_view>;
-// Expected CV1 (with View == input) layout:
+// Expected CV1 (when V models only input_range) layout:
 // [[no_unique_address]] _View __base_                                         // size: 0
 // [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
 // [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
@@ -46,8 +40,18 @@ static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<
 static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
                                  sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
 
+// When V models forward_range
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
 using CV2 = std::ranges::chunk_view<forward_view>;
-// Expected CV2 (with View >= forward) layout:
+// Expected CV2 (when V models forward_range) layout:
 // [[no_unique_address]] _View __base_             // size: 0
 // [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
 static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 9264256fc6a52..4d0fb4f76857c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -19,15 +19,6 @@ void test() {
   char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
   auto view     = range | std::views::chunk(2);
 
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   view.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -43,18 +34,19 @@ void test() {
   std::as_const(view).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  view.end();
+  std::ranges::iter_move(view.begin());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(view).end();
+  std::ranges::iter_move(std::as_const(view).begin());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
+  view.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
+  std::as_const(view).end();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
+  std::ranges::iter_move(view.end());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
+  std::ranges::iter_move(std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   *view.begin();
@@ -71,16 +63,11 @@ void test() {
   (std::as_const(view).begin() == std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == std::as_const(view).end());
+  std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == std::as_const(view).end());
-
+  std::views::chunk(range, 3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(view.begin());
+  range | std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(std::as_const(view).begin());
+  std::views::reverse | std::views::chunk(3);
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..6baa2fce13695
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const inner_iterator& x)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr iterator operator+(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator+(difference_type n, const iterator& i)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator-(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+//       requires sized_sentinel_for<iterator_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(std::default_sentinel - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - std::default_sentinel == -4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t, const inner_iterator& x)`
+  {
+    assert(std::default_sentinel - (*input_chunked.begin()).begin() == 3);
+  }
+
+  // Test `friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t)`
+  {
+    assert((*input_chunked.begin()).begin() - std::default_sentinel == -3);
+  }
+
+  // Test `friend constexpr iterator operator+(const iterator& i, difference_type n)`
+  {
+    assert(chunked.begin() + 4 == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator+(difference_type n, const iterator& i)`
+  {
+    assert(4 + chunked.begin() == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator-(const iterator& i, difference_type n)`
+  {
+    assert(chunked.end() - 4 == chunked.begin());
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)`
+  {
+    assert(std::default_sentinel - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)`
+  {
+    assert(chunked.begin() - std::default_sentinel == -4);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
index d86726d958e6e..ea6731cccce85 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -11,14 +11,12 @@
 // <ranges>
 
 //   V models only input_range
-//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
-//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
-//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t);
 
 //   V models forward_range
-//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator==(const iterator& x, const iterator& y);
+//     friend constexpr bool operator==(const iterator& x, default_sentinel_t);
 //     friend constexpr bool operator<(const iterator& x, const iterator& y)
 //       requires random_access_range<Base>;
 //     friend constexpr bool operator>(const iterator& x, const iterator& y)
@@ -31,7 +29,6 @@
 //       requires random_access_range<Base> &&
 //                three_way_comparable<iterator_t<Base>>;
 
-#include <algorithm>
 #include <cassert>
 #include <compare>
 #include <iterator>
@@ -53,14 +50,11 @@ constexpr bool test() {
     assert(it == std::default_sentinel);
   }
 
-  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  // Test `friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t)`
   {
-    assert(input_chunked.end() - input_chunked.begin() == 4);
-  }
-
-  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
-  {
-    assert(input_chunked.begin() - input_chunked.end() == -4);
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::ranges::advance(it, 3);
+    assert(it == std::default_sentinel);
   }
 
   // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
@@ -69,6 +63,11 @@ constexpr bool test() {
     assert(chunked.end() == chunked.end());
   }
 
+  // Test `friend constexpr bool operator==(const iterator& x, default_sentinel)`
+  {
+    assert(chunked.end() == std::default_sentinel);
+  }
+
   // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() < chunked.end());
@@ -79,7 +78,7 @@ constexpr bool test() {
     assert(chunked.end() > chunked.begin());
   }
 
-  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  // Test `friend constexpr bool operator<=(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() <= chunked.begin());
     assert(chunked.begin() <= chunked.end());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
new file mode 100644
index 0000000000000..9b45fc6d779bf
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models forward_range
+//     constexpr value_type iterator::operator[](difference_type n) const
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+
+  // Test `constexpr value_type iterator::operator[](difference_type n) const`
+  {
+    assert(std::ranges::equal(chunked.begin()[1], std::vector{4, 5, 6}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 033a1c16289990a16b1f674f380f7a72f38f48ea Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 14:30:08 -0400
Subject: [PATCH 11/20] Fix release notes

---
 libcxx/docs/ReleaseNotes/22.rst    | 1 -
 libcxx/docs/ReleaseNotes/23.rst    | 1 +
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0637ba98976a2..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -55,7 +55,6 @@ Implemented Papers
 - 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>`__)
-- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e7eb42207db42..a98f42da70f00 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -52,6 +52,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::chunk`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 6abcac5e4ebca..8c92d89be999b 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""

>From 7fe126d5e7c88787f26228c64bcb5f89977c5b83 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:02:10 +0800
Subject: [PATCH 12/20] Resolve overload conflicts on `__div_ceil` in
 `ranges::chunk_view` and `ranges::stride_view`.

Both `ranges::chunk_view` and `ranges::stride_view` need a helper function called `__div_ceil` that solves some simple maths. According to the style of other libcxx internal helper functions (e.g. `__priority_tag`, `__small_buffer`), it seems that we can put `__div_ceil` into a new file `__utility/div_ceil.h`. However, most files in `__utility/` refer to a corresponding symbol in `<utility>` header, so that if someone feels this is not proper, I'll be glad to revert it.
---
 libcxx/include/CMakeLists.txt         |  1 +
 libcxx/include/__ranges/chunk_view.h  | 21 ++++++-----------
 libcxx/include/__ranges/stride_view.h | 20 +++++-----------
 libcxx/include/__utility/div_ceil.h   | 33 +++++++++++++++++++++++++++
 libcxx/include/module.modulemap.in    |  1 +
 5 files changed, 48 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/include/__utility/div_ceil.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 76b93c17c3312..0e9f19a81b1ab 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -935,6 +935,7 @@ set(files
   __utility/convert_to_integral.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
+  __utility/div_ceil.h
   __utility/element_count.h
   __utility/empty.h
   __utility/exception_guard.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 683b6bbaa2ca5..68d4408295e47 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -36,6 +36,7 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 
@@ -52,14 +53,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Integral>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
-  _Integral __r = __num / __denom;
-  if (__num % __denom)
-    ++__r;
-  return __r;
-}
-
 template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
@@ -98,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -153,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -321,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -510,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..7ea68b13330c3 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -46,15 +47,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Value>
-_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) {
-  _Value __r = __left / __right;
-  if (__left % __right) {
-    ++__r;
-  }
-  return __r;
-}
-
 template <input_range _View>
   requires view<_View>
 class stride_view : public view_interface<stride_view<_View>> {
@@ -121,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -340,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -ranges::__div_ceil(-__n, __x.__stride_);
+      return -__div_ceil(-__n, __x.__stride_);
     }
-    return ranges::__div_ceil(__n, __x.__stride_);
+    return __div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)
diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
new file mode 100644
index 0000000000000..595c590b9e3f6
--- /dev/null
+++ b/libcxx/include/__utility/div_ceil.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_DIV_CEIL_H
+#define _LIBCPP___UTILITY_DIV_CEIL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __div_ceil computes the ceiling of the division of two integers. It is mainly used by
+// range adaptors like chunk_view and stride_view.
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_DIV_CEIL_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 64238440b9dd7..ef5d15d063f69 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2200,6 +2200,7 @@ module std [system] {
     module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
+    module div_ceil                        { header "__utility/div_ceil.h" }
     module element_count                   { header "__utility/element_count.h" }
     module exception_guard                 { header "__utility/exception_guard.h" }
     module exchange                        { header "__utility/exchange.h" }

>From d27b355e67e17b0ed15d44a247fdba1c3b13ed49 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:22:38 +0800
Subject: [PATCH 13/20] Fix test error that `constexpr` is not supported in
 `-std=c++03`

---
 libcxx/include/__utility/div_ceil.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 595c590b9e3f6..4d2868465c141 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,6 +17,8 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if _LIBCPP_STD_VER >= 11
+
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
 
@@ -28,6 +30,8 @@ template <class _Integral>
   return __r;
 }
 
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___UTILITY_DIV_CEIL_H

>From a1a5918bb79d29cbe7be2736da7a72b83ee0afbb Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:38:37 +0800
Subject: [PATCH 14/20] Fix test error again that `constexpr` is not supported
 in `-std=c++11`

---
 libcxx/include/__utility/div_ceil.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 4d2868465c141..21e9bcb30d1a1 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,7 +17,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 11
+#if _LIBCPP_STD_VER >= 23
 
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
@@ -30,7 +30,7 @@ template <class _Integral>
   return __r;
 }
 
-#endif
+#endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_END_NAMESPACE_STD
 

>From c2e0e2e926c92ee6d001e060aa5660b681874dfd Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:45:41 +0800
Subject: [PATCH 15/20] Fix test error that `std::as_const` is included from
 `<utility>`.

---
 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp | 1 +
 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
index 3cccd7d1bebc6..29588a57fbcab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -22,6 +22,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
index 73eb0d967f6a2..2f37ac5f73524 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -23,6 +23,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"

>From 7d3cdd73f5da77a7499969fb7954312a19e26630 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 14:19:19 +0800
Subject: [PATCH 16/20] Avoid ADL in `std::__div_ceil`.

---
 libcxx/include/__ranges/chunk_view.h  | 12 ++++++------
 libcxx/include/__ranges/stride_view.h | 10 +++++-----
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 68d4408295e47..1107a9238b2bc 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -91,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -146,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return std::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -314,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -503,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return std::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 7ea68b13330c3..a8b485cbe33e5 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -113,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -332,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -__div_ceil(-__n, __x.__stride_);
+      return -std::__div_ceil(-__n, __x.__stride_);
     }
-    return __div_ceil(__n, __x.__stride_);
+    return std::__div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return std::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)

>From 116c6d85d8a9ad177b1df1975eea92479cb03664 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 16:39:58 +0800
Subject: [PATCH 17/20] [Important] Add missing concepts requirements.

---
 libcxx/include/__ranges/chunk_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1107a9238b2bc..47e6bd56415b6 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -400,7 +400,8 @@ class chunk_view<_View>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires bidirectional_range<_Base> {
     auto __tmp = *this;
     --*this;
     return __tmp;

>From 545bbdf10d24c3790790bfe669b8c756da719838 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Fri, 26 Jun 2026 22:35:12 +0800
Subject: [PATCH 18/20] Cover tests on `chunk_range`. (I'll continue to cover
 tests on `__iterator`, `__inner_iterator` and `__outer_iterator` at the
 weekend)

---
 libcxx/include/__ranges/chunk_view.h          |  3 +-
 .../range.adaptors/range.chunk/base.pass.cpp  | 12 +++-
 .../range.chunk/borrowing.compile.pass.cpp    | 27 +++++++
 .../range.adaptors/range.chunk/ctor.pass.cpp  | 54 ++++++++++++++
 .../range.adaptors/range.chunk/size.pass.cpp  | 72 +++++++++++++++++++
 5 files changed, 166 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 47e6bd56415b6..1798711eabd16 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -401,7 +401,8 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
-    requires bidirectional_range<_Base> {
+    requires bidirectional_range<_Base>
+  {
     auto __tmp = *this;
     --*this;
     return __tmp;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index b45e1f59f1394..4dca324a23695 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -26,7 +26,7 @@ constexpr bool test() {
   std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
       std::as_const(array) | std::views::chunk(4);
 
-  // Test `chunk_view.base()`
+  // Test `chunk_view.base() const&`
   {
     std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
     assert(std::addressof(base) == std::addressof(array));
@@ -35,6 +35,16 @@ constexpr bool test() {
     assert(std::addressof(const_base) == std::addressof(array));
   }
 
+  // Test `chunk_view.base() &&`
+  {
+    std::same_as<std::ranges::ref_view<std::array<int, 8>>> decltype(auto) moved = std::move(chunked).base();
+    assert(std::addressof(moved.base()) == std::addressof(array));
+
+    std::same_as<std::ranges::ref_view<const std::array<int, 8>>> decltype(auto) const_moved =
+        std::move(const_chunked).base();
+    assert(std::addressof(const_moved.base()) == std::addressof(array));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
new file mode 100644
index 0000000000000..1eb4a34f87217
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class V>
+//   inline constexpr bool enable_borrowed_range<chunk_view<V>> =
+//       forward_range<V> && enable_borrowed_range<V>;
+
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+// When V models only `input_range`.
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<input_span<int>>>);
+
+// When V models at least `forward_range`.
+static_assert(std::ranges::enable_borrowed_range<std::ranges::chunk_view<BorrowedView>>);
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
new file mode 100644
index 0000000000000..b60e040b807eb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.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++23
+
+// <ranges>
+
+//   constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n);
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_convertible.h"
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models only `input_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>, input_span<int>, std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(input_span(vector.data(), 8), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+                                    std::ranges::ref_view<std::vector<int>>,
+                                    std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
new file mode 100644
index 0000000000000..820774c3b750e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr auto size() requires sized_range<_View>;
+//   constexpr auto size() const requires sized_range<const _View>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+  // Test `chunk_view.size()` when V models only `input_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<input_span<int>>>);
+    static_assert(!std::ranges::sized_range<const std::ranges::chunk_view<input_span<int>>>);
+
+    auto chunked = input_span<int>(arr, 12) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when V models `forward_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+    static_assert(std::ranges::sized_range<const std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+    const auto& const_chunked = chunked;
+    assert(const_chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when the range is not fully divisible
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(5);
+    assert(chunked.size() == 3);
+  }
+
+  // Test `chunk_view.size()` when the range is empty
+  {
+    static_assert(std::ranges::sized_range<std::ranges::empty_view<int>>);
+    auto chunked = std::views::empty<int> | std::views::chunk(3);
+    assert(chunked.size() == 0);
+  }
+
+  // Test `chunk_view.size()` when chunk size is larger than the range
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(100);
+    assert(chunked.size() == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From e262f9e7497fb3ccf232a3c7265b8faa83f2fa20 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:36:04 +0800
Subject: [PATCH 19/20] Fix chunk_view ctor test for forward ranges.

---
 .../test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
index b60e040b807eb..c0d210aa45dc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
@@ -35,11 +35,11 @@ constexpr bool test() {
 
   // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
   {
-    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+    static_assert(!test_convertible<std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>>,
                                     std::ranges::ref_view<std::vector<int>>,
                                     std::ptrdiff_t>());
 
-    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked(std::ranges::ref_view(vector), 3);
     assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
   }
 

>From bf13c55de976c562120249f2fc7208ea1091fc95 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:39:36 +0800
Subject: [PATCH 20/20] Use consistent copy_constructible spelling in
 chunk_view

---
 libcxx/include/__ranges/chunk_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1798711eabd16..f3edc617de07a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -71,7 +71,7 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
-    requires std::copy_constructible<_View>
+    requires copy_constructible<_View>
   {
     return __base_;
   }


From libcxx-commits at lists.llvm.org  Sun Jul 19 07:48:29 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 19 Jul 2026 07:48:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a5ce3bd.d2cb4adc.ccd80.f399@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/207585

>From 02ae8f53a83139d9280f86098a7517a3036c7986 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 5 Jul 2026 14:17:28 +0100
Subject: [PATCH 1/6] Implementation of adjacent_difference on top of a binary
 transform

---
 libcxx/include/__numeric/pstl.h               |  39 ++++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  35 +++-
 .../pstl.iterator-requirements.verify.cpp     |  11 ++
 .../algorithms/pstl.nodiscard.verify.cpp      |   5 +
 .../pstl.exception_handling.pass.cpp          |  16 +-
 .../pstl.adjacent_difference.pass.cpp         | 171 +++++++++++++++++
 .../pstl.adjacent_difference_op.pass.cpp      | 181 ++++++++++++++++++
 8 files changed, 461 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp

diff --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h
index ecbc9f5348333..0b85cfb8c2327 100644
--- a/libcxx/include/__numeric/pstl.h
+++ b/libcxx/include/__numeric/pstl.h
@@ -165,6 +165,45 @@ template <class _ExecutionPolicy,
       std::move(__transform));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first1), std::move(__last1), std::move(__first2), minus{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryOperation,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _BinaryOperation __op) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__op));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a84a768cad570..2ba5b4434fc33 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -317,6 +317,13 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_difference;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+// optional<_ForwardIterator2>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5c78d4efa412b..f34c284dd2a84 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -92,11 +92,12 @@ namespace __pstl {
 //
 // transform and transform_binary family
 // -------------------------------------
-// - replace_copy_if
-// - replace_copy
-// - move
+// - adjacent_difference
 // - copy
 // - copy_n
+// - move
+// - replace_copy
+// - replace_copy_if
 // - reverse_copy
 // - rotate_copy
 //
@@ -575,6 +576,34 @@ struct __reverse_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _BinaryOperation&& __op) const noexcept {
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    if (__first1 == __last1)
+      return __first2; // edge case: empty input range, just return the output iterator
+    *__first2 = *__first1;
+    ++__first2;
+    _ForwardIterator1 __first_left = std::next(__first1);
+    if (__first_left == __last1)
+      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+    // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
+    return _TransformBinary()(
+        __policy,
+        std::move(__first_left),
+        std::move(__last1),
+        std::move(__first1),
+        std::move(__first2),
+        std::forward<_BinaryOperation>(__op));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index fbe3460b67f17..402d8fbf9131a 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -211,4 +211,15 @@ void f(non_forward_iterator non_fwd,
     (void)std::transform_reduce(
         pol, non_fwd, non_fwd, val, func, func); // expected-error@*:* {{static assertion failed: transform_reduce}}
   }
+
+  {
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, it, it, non_fwd, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+    (void)std::adjacent_difference(
+        pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
+  }
 }
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 049c8dc88c2da..f4fffd456a331 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <algorithm>
 #include <execution>
 #include <iterator>
+#include <numeric>
 
 void test() {
   int a[]    = {1};
@@ -58,4 +59,8 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
 }
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 8af2a0a15fd5c..e00046fdb1dd6 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -85,7 +85,7 @@ int main(int, char**) {
       auto last1       = util::throw_on_move_iterator(std::end(a), tokens[1].active() ? 1 : -1);
       auto first2      = util::throw_on_move_iterator(std::begin(b), tokens[2].active() ? 1 : -1);
       auto last2       = util::throw_on_move_iterator(std::end(b), tokens[3].active() ? 1 : -1);
-      auto dest        = util::throw_on_move_iterator(std::end(storage), tokens[4].active() ? 1 : -1);
+      auto dest        = util::throw_on_move_iterator(std::begin(storage), tokens[4].active() ? 1 : -1);
       auto maybe_throw = [](ThrowToken const& token, auto f) {
         return [&token, f](auto... args) {
           if (token.active())
@@ -365,6 +365,20 @@ int main(int, char**) {
           (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction);
         });
       }
+
+      {
+        auto op = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
+
+        // adjacent_difference(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+
+        // adjacent_difference(first, last, dest, op)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest), op);
+        });
+      }
     }
   });
 }
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
new file mode 100644
index 0000000000000..bb98004c1c62a
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+
+  friend X operator-(const X& x, const X& y) { return X(x.i_ - y.i_); }
+
+  friend class Y;
+};
+
+class Y {
+  int i_;
+
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 20};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, -5, -4, -3, -2};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r =
+            std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        assert(std::all_of(std::begin(ib), std::begin(ib) + i, [](int x) { return x == 1; }));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(7)};
+      Y ir[3] = {Y(1), Y(4), Y(2)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
new file mode 100644
index 0000000000000..26d65f9a77c10
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryOperation>
+//   ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2,
+//                                        BinaryOperation op);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
+
+static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
+static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
+
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+class X {
+  int i_;
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+  friend class Y;
+  friend struct Op;
+};
+
+class Y {
+  int i_;
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+// ensures that the predicate is not equivalent to std::minus
+struct Op {
+  int operator()(int l, int r) const { return l * 2 - r; }
+  X operator()(const X& l, const X& r) const { return X(l.i_ * 2 - r.i_); }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 50};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, 5, 2, 0, -1};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r = std::adjacent_difference(
+            policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)), Op{});
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j)
+          assert(ib[j] == (int)(j == 0 ? 1 : j + 2));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(8)};
+      Y ir[3] = {Y(1), Y(9), Y(11)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r =
+          std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}

>From f62e25b47970412a33d6c44c4b4820ad3669b680 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 10:34:09 +0100
Subject: [PATCH 2/6] Renamed the variable names in __adjacent_difference

---
 libcxx/include/__pstl/backends/default.h | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index f34c284dd2a84..28bc75be147aa 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -583,23 +583,23 @@ struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
   operator()(_Policy&& __policy,
              _ForwardIterator1 __first1,
              _ForwardIterator1 __last1,
-             _ForwardIterator2 __first2,
+             _ForwardIterator2 __result,
              _BinaryOperation&& __op) const noexcept {
     using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
     if (__first1 == __last1)
-      return __first2; // edge case: empty input range, just return the output iterator
-    *__first2 = *__first1;
-    ++__first2;
-    _ForwardIterator1 __first_left = std::next(__first1);
-    if (__first_left == __last1)
-      return __first2; // edge case: not enough elements to perform adjacent difference, just return the output iterator
+      return __result; // edge case: empty input range, just return the output iterator
+    *__result = *__first1;
+    ++__result;
+    _ForwardIterator1 __first2 = std::next(__first1);
+    if (__first2 == __last1)
+      return __result; // edge case: not enough elements to perform adjacent difference, just return the output iterator
     // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
     return _TransformBinary()(
         __policy,
-        std::move(__first_left),
+        std::move(__first2),
         std::move(__last1),
         std::move(__first1),
-        std::move(__first2),
+        std::move(__result),
         std::forward<_BinaryOperation>(__op));
   }
 };

>From 012594a84a7b0bbc55c1e9457adf28531d01c97d Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 20:01:44 +0100
Subject: [PATCH 3/6] Moved runway_sample() to test/support/runway_sample.h

---
 .../pstl.adjacent_difference.pass.cpp         |  16 +--
 .../pstl.adjacent_difference_op.pass.cpp      |  16 +--
 libcxx/test/support/runway_sample.h           |  58 ++++++++++
 .../test.support/runway_sample.pass.cpp       | 101 ++++++++++++++++++
 4 files changed, 161 insertions(+), 30 deletions(-)
 create mode 100644 libcxx/test/support/runway_sample.h
 create mode 100644 libcxx/test/support/test.support/runway_sample.pass.cpp

diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
index bb98004c1c62a..4dfe3ad58adf8 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -29,27 +29,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
 
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 class X {
   int i_;
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
index 26d65f9a77c10..f6b9b480f4ba8 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -31,27 +31,13 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
 
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 class X {
   int i_;
   X& operator=(const X&);
diff --git a/libcxx/test/support/runway_sample.h b/libcxx/test/support/runway_sample.h
new file mode 100644
index 0000000000000..16ecd58cef747
--- /dev/null
+++ b/libcxx/test/support/runway_sample.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+#ifndef TEST_SUPPORT_RUNWAY_SAMPLE_H
+#define TEST_SUPPORT_RUNWAY_SAMPLE_H
+
+#include <cstddef>
+#include <algorithm>
+#include "test_macros.h"
+
+// runway_sample(size, callable) calls callable(i) for a set of sample indices i in the range [0, size).
+// they are chosen in 3 phases:
+//   - prefix, sampled densely with increments of 1
+//   - middle, sampled sparsely with uncanny numbers
+//   - suffix, sampled densely with increments of 1
+//
+// Examples of sample indices for various sizes:
+// size = 0:
+// <no samples>
+// size = 1:
+// 0
+// size = 10:
+// 0 1 2 3 4 5 6 7 8 9
+// size = 100:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
+// size = 1000:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 157 493 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
+template <class Callable>
+TEST_CONSTEXPR_CXX14 void runway_sample(std::size_t size, Callable callable) {
+  constexpr std::size_t affix = 16;
+  std::size_t i               = 0;
+
+  // 0, 1, 2, ..., 15
+  for (; i < std::min(size, affix); ++i) {
+    callable(i);
+  }
+
+  if (size <= affix) {
+    return;
+  }
+
+  // 16, 50, 157, 493, 1549, 4868, ...
+  for (std::size_t j = i; j + affix < size; i = j, j = j * 3 + j / 7) {
+    callable(j);
+  }
+
+  // size - 16, size - 15, ..., size - 1
+  i = std::max(i, size - affix);
+  for (; i < size; ++i) {
+    callable(i);
+  }
+}
+
+#endif // TEST_SUPPORT_RUNWAY_SAMPLE_H
diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
new file mode 100644
index 0000000000000..9fc288f881082
--- /dev/null
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++11
+
+// "support/runway_sample.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "runway_sample.h"
+
+TEST_CONSTEXPR_CXX14 bool test() {
+  {
+    bool fail = false;
+    runway_sample(0, [&](size_t /*i*/) { fail = true; });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(0, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(10, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29};
+    size_t n  = 0;
+    bool fail = false;
+    runway_sample(30, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
+                         50, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(100, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  {
+    size_t expected[] = {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  50,
+                         157, 493, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999};
+    size_t n          = 0;
+    bool fail         = false;
+    runway_sample(1000, [&](size_t i) {
+      if (expected[n++] != i)
+        fail = true;
+    });
+    if (fail) {
+      return false;
+    }
+  }
+  return true;
+}
+
+int main() {
+  assert(test());
+#if TEST_STD_VER >= 14
+  static_assert(test());
+#endif
+  return 0;
+}

>From 1552e8b1db839c7356ba1679fda4bdfc669dd685 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 20:05:51 +0100
Subject: [PATCH 4/6] Added a note about X and Y in the tests

---
 .../adjacent.difference/pstl.adjacent_difference.pass.cpp     | 4 ++++
 .../adjacent.difference/pstl.adjacent_difference_op.pass.cpp  | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
index 4dfe3ad58adf8..d06f5b64e2f9f 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -36,6 +36,10 @@ EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
 
+// Types X and Y are provided to test adjacent_difference() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
 class X {
   int i_;
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
index f6b9b480f4ba8..d623cda058d04 100644
--- a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
@@ -38,6 +38,10 @@ EXECUTION_POLICY_SFINAE_TEST(adjacent_difference);
 static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
 static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
 
+// Types X and Y are provided to test adjacent_difference() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
 class X {
   int i_;
   X& operator=(const X&);

>From 40282b01dd8f83ff30534284abe9deddbf0e4136 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 11 Jul 2026 21:06:40 +0100
Subject: [PATCH 5/6] Run runway_sample test on C++17+

---
 libcxx/test/support/test.support/runway_sample.pass.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
index 9fc288f881082..30e0ad20e0f85 100644
--- a/libcxx/test/support/test.support/runway_sample.pass.cpp
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// REQUIRES: std-at-least-c++11
+// REQUIRES: std-at-least-c++17
 
 // "support/runway_sample.h"
 
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 #include "runway_sample.h"
 
-TEST_CONSTEXPR_CXX14 bool test() {
+constexpr bool test() {
   {
     bool fail = false;
     runway_sample(0, [&](size_t /*i*/) { fail = true; });
@@ -94,8 +94,6 @@ TEST_CONSTEXPR_CXX14 bool test() {
 
 int main() {
   assert(test());
-#if TEST_STD_VER >= 14
   static_assert(test());
-#endif
   return 0;
 }

>From 1759d17216f82de691906a23e19876be0262b231 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:26:32 +0100
Subject: [PATCH 6/6] Use assert() in the runway_sample() unit test

---
 .../test.support/runway_sample.pass.cpp       | 82 +++++++------------
 1 file changed, 29 insertions(+), 53 deletions(-)

diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
index 30e0ad20e0f85..6c6eaafc3439b 100644
--- a/libcxx/test/support/test.support/runway_sample.pass.cpp
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -20,80 +20,56 @@
 
 constexpr bool test() {
   {
-    bool fail = false;
-    runway_sample(0, [&](size_t /*i*/) { fail = true; });
-    if (fail) {
-      return false;
-    }
+    runway_sample(0, [&](std::size_t /*i*/) { assert(false); });
   }
   {
-    size_t expected[] = {0};
-    size_t n          = 0;
-    bool fail         = false;
-    runway_sample(0, [&](size_t i) {
-      if (expected[n++] != i)
-        fail = true;
+    std::size_t expected[] = {0};
+    std::size_t n          = 0;
+    runway_sample(1, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
     });
-    if (fail) {
-      return false;
-    }
   }
   {
-    size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-    size_t n          = 0;
-    bool fail         = false;
-    runway_sample(10, [&](size_t i) {
-      if (expected[n++] != i)
-        fail = true;
+    std::size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    std::size_t n          = 0;
+    runway_sample(10, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
     });
-    if (fail) {
-      return false;
-    }
   }
   {
-    size_t expected[] = {
+    std::size_t expected[] = {
         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29};
-    size_t n  = 0;
-    bool fail = false;
-    runway_sample(30, [&](size_t i) {
-      if (expected[n++] != i)
-        fail = true;
+    std::size_t n = 0;
+    runway_sample(30, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
     });
-    if (fail) {
-      return false;
-    }
   }
   {
-    size_t expected[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
-                         50, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99};
-    size_t n          = 0;
-    bool fail         = false;
-    runway_sample(100, [&](size_t i) {
-      if (expected[n++] != i)
-        fail = true;
+    std::size_t expected[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
+                              50, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99};
+    std::size_t n          = 0;
+    runway_sample(100, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
     });
-    if (fail) {
-      return false;
-    }
   }
   {
-    size_t expected[] = {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  50,
-                         157, 493, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999};
-    size_t n          = 0;
-    bool fail         = false;
-    runway_sample(1000, [&](size_t i) {
-      if (expected[n++] != i)
-        fail = true;
+    std::size_t expected[] = {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  50,
+                              157, 493, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999};
+    std::size_t n          = 0;
+    runway_sample(1000, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
     });
-    if (fail) {
-      return false;
-    }
   }
   return true;
 }
 
 int main() {
-  assert(test());
+  test();
   static_assert(test());
   return 0;
 }


From libcxx-commits at lists.llvm.org  Sun Jul 19 07:49:25 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 07:49:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2442R1
	`std::views::chunk` (PR #171234)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171234@github.com>
Message-ID: <6a5ce3f5.1728eaf3.2a2f6.788a@mx.google.com>


https://github.com/shyeyian updated https://github.com/llvm/llvm-project/pull/171234

>From f6554ea2a0e1d9571cf82fc7b2f3d1115c466474 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at petalmail.com>
Date: Wed, 24 Dec 2025 22:05:29 +0800
Subject: [PATCH 01/21] Implement `views::chunk`.

This commit squashes previous commits.
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/22.rst               |   1 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/chunk_view.h          | 562 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/ranges                         |  12 +
 libcxx/include/version                        |   2 +-
 libcxx/modules/std/ranges.inc                 |  11 +-
 .../no_unique_address.compile.pass.cpp        |  53 ++
 .../range.chunk/nodiscard.verify.cpp          |  86 +++
 .../ranges.version.compile.pass.cpp           |  32 +-
 .../version.version.compile.pass.cpp          |  32 +-
 .../range.chunk/adaptor.pass.cpp              |  75 +++
 .../range.adaptors/range.chunk/base.pass.cpp  |  46 ++
 .../range.adaptors/range.chunk/begin.pass.cpp |  67 +++
 .../range.chunk/ctad.compile.pass.cpp         |  55 ++
 .../range.adaptors/range.chunk/end.pass.cpp   |  65 ++
 .../range.chunk/general.pass.cpp              |  48 ++
 .../range.chunk.iter/compare.pass.cpp         | 109 ++++
 .../range.chunk.iter/decrement.pass.cpp       |  60 ++
 .../range.chunk.iter/deref.pass.cpp           |  77 +++
 .../range.chunk.iter/increment.pass.cpp       |  95 +++
 .../ranges/range.adaptors/range.chunk/types.h |  50 ++
 .../generate_feature_test_macro_components.py |   1 -
 25 files changed, 1494 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/chunk_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..8e57305f32ad1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -370,7 +370,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_rvalue``                             ``202207L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_chunk``                                 *unimplemented*
+    ``__cpp_lib_ranges_chunk``                                 ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                              ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0d1a1fbc00f2c..d2a2d1b712689 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,6 +54,7 @@ Implemented Papers
 - 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>`__)
+- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e4c6cb0f51065..8c8ab211e5bf4 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__","Only ``ranges::iota`` is implemented."
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__",""
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index eb423ae923b4f..2426da6848b51 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -707,6 +707,7 @@ set(files
   __ranges/all.h
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
+  __ranges/chunk_view.h
   __ranges/common_view.h
   __ranges/concepts.h
   __ranges/container_compatible_range.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
new file mode 100644
index 0000000000000..f19d2c9ede8a7
--- /dev/null
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_CHUNK_VIEW_H
+#define _LIBCPP___RANGES_CHUNK_VIEW_H
+
+#include <__algorithm/ranges_min.h>
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/take_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
+
+  class __outer_iterator;
+  class __inner_iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n), __remainder_(0) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires std::copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator begin() {
+    __current_.__emplace(ranges::begin(__base_));
+    __remainder_ = __n_;
+    return __outer_iterator(*this);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept {
+    return std::default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(chunk_view& __parent)
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  class value_type;
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator(__outer_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    return value_type(*__parent_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
+    __parent_->__remainder_ = __parent_->__n_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __outer_iterator& __i, default_sentinel_t) {
+    return *__i.__parent_->__current_ == ranges::end(__i.__parent_->__base_) && __i.__parent_->__remainder_ != 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __outer_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
+    if (__dist < __i.__parent_->__remainder_)
+      return __dist == 0 ? 0 : 1;
+    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __outer_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator::value_type : public view_interface<value_type> {
+  friend __outer_iterator;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(chunk_view& __parent) : __parent_(std::addressof(__parent)) {}
+
+public:
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator begin() const noexcept {
+    return __inner_iterator(*__parent_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return std::__to_unsigned_like(
+        ranges::min(__parent_->__remainder_, ranges::end(__parent_->__base_) - *__parent_->__current_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__inner_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(chunk_view& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+  using value_type       = range_value_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator(__inner_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator& operator=(__inner_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_View> base() const& { return *__parent_->__current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_reference_t<_View> operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
+    return **__parent_->__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    ++*__parent_->__current_;
+    if (*__parent_->__current_ == ranges::end(__parent_->__base_))
+      __parent_->__remainder_ = 0;
+    else
+      --__parent_->__remainder_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __inner_iterator& __i, default_sentinel_t) {
+    return __i.__parent_->__remainder_ == 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __inner_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return ranges::min(__i.__parent_->__remainder_, ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __inner_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
+    return ranges::iter_move(*__i.__parent_->__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void
+  iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) noexcept(
+      noexcept((ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_))))
+    requires indirectly_swappable<iterator_t<_View>>
+  {
+    return ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_);
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+class chunk_view<_View> : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+
+  template <bool _Const>
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires forward_range<const _View>
+  {
+    return __iterator<true>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    if constexpr (common_range<_View> && sized_range<_View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<false>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<_View> && !bidirectional_range<_View>)
+      return __iterator<false>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires forward_range<const _View>
+  {
+    if constexpr (common_range<const _View> && sized_range<const _View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<true>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<const _View> && !bidirectional_range<const _View>)
+      return __iterator<true>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+template <bool _Const>
+class chunk_view<_View>::__iterator {
+  friend chunk_view;
+
+  using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, chunk_view>;
+  using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
+
+  iterator_t<_Base> __current_         = iterator_t<_Base>();
+  sentinel_t<_Base> __end_             = sentinel_t<_Base>();
+  range_difference_t<_Base> __n_       = 0;
+  range_difference_t<_Base> __missing_ = 0;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      _Parent* __parent, iterator_t<_Base> __current, range_difference_t<_Base> __missing = 0)
+      : __current_(__current), __end_(ranges::end(__parent->__base_)), __n_(__parent->__n_), __missing_(__missing) {}
+
+  [[nodiscard]] static consteval auto __get_iterator_concept() {
+    if constexpr (random_access_range<_Base>)
+      return random_access_iterator_tag{};
+    else if constexpr (bidirectional_range<_Base>)
+      return bidirectional_iterator_tag{};
+    else
+      return forward_iterator_tag{};
+  }
+
+public:
+  using iterator_category = input_iterator_tag;
+  using iterator_concept  = decltype(__iterator::__get_iterator_concept());
+  using value_type        = decltype(views::take(subrange(__current_, __end_), __n_));
+  using difference_type   = range_difference_t<_Base>;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> &&
+                 convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+      : __current_(std::move(__i.__current_)),
+        __end_(std::move(__i.__end_)),
+        __n_(__i.__n_),
+        __missing_(__i.__missing_) {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() const { return __current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to dereference past-the-end chunk_view iterator");
+    return views::take(subrange(__current_, __end_), __n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator[](difference_type __pos) const
+    requires random_access_range<_Base>
+  {
+    return *(*this + __pos);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    __missing_ = ranges::advance(__current_, __n_, __end_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires bidirectional_range<_Base>
+  {
+    ranges::advance(__current_, __missing_ - __n_);
+    __missing_ = 0;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    if (__x > 0) {
+      _LIBCPP_ASSERT_PEDANTIC(ranges::distance(__current_, __end_) > __n_ * (__x - 1),
+                              "Trying to advance chunk_view iterator out of range");
+      ranges::advance(__current_, __n_ * (__x - 1));
+      __missing_ = ranges::advance(__current_, __n_, __end_);
+    } else if (__x < 0) {
+      ranges::advance(__current_, __n_ * __x + __missing_);
+      __missing_ = 0;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    return *this += -__x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
+    return __x.__current_ == __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, default_sentinel_t) {
+    return __x.__current_ == __x.__end_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __x.__current_ < __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __y < __x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__y < __x);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__x < __y);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(difference_type __pos, const __iterator& __i)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator-(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r -= __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, const __iterator& __j)
+    requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+  {
+    return (__i.__current_ - __j.__current_ + __i.__missing_ - __j.__missing_) / __i.__n_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <class _Range>
+chunk_view(_Range&&, range_difference_t<_Range>) -> chunk_view<views::all_t<_Range>>;
+
+template <class _View>
+inline constexpr bool enable_borrowed_range<chunk_view<_View>> = forward_range<_View> && enable_borrowed_range<_View>;
+
+namespace views {
+namespace __chunk {
+struct __fn {
+  template <viewable_range _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range, range_difference_t<_Range> __n) noexcept(
+      noexcept(/*-----*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))))
+      -> decltype(/*--*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))) {
+    return /*---------*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n));
+  }
+
+  template <class _DifferenceType>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_DifferenceType __n) noexcept(is_nothrow_constructible_v<decay_t<_DifferenceType>, _DifferenceType>) {
+    return __pipeable(std::__bind_back(__fn{}, std::forward<_DifferenceType>(__n)));
+  }
+};
+
+} // namespace __chunk
+
+inline namespace __cpo {
+inline constexpr auto chunk = __chunk::__fn{};
+
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_CHUNK_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4434a5446e35b..cb03eb8d14067 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1863,6 +1863,10 @@ module std [system] {
       header "__ranges/chunk_by_view.h"
       export std.functional.bind_back
     }
+    module chunk_view {
+      header "__ranges/chunk_view.h"
+      export std.functional.bind_back
+    }
     module common_view                    { header "__ranges/common_view.h" }
     module concepts                       { header "__ranges/concepts.h" }
     module container_compatible_range     { header "__ranges/container_compatible_range.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 9f725b12ac6c2..9ec6a4a819919 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -396,6 +396,17 @@ namespace std::ranges {
   class chunk_by_view;                                                      // C++23
 
   namespace views { inline constexpr unspecified chunk_by = unspecified; }  // C++23
+
+  // [range.chunk]
+  template <view V>
+    requires input_range<V>
+  class chunk_view;                                                     // C++23
+
+  template <view V>
+    requires forward_range<V>
+  class chunk_view<V>;                                                  // C++23
+
+  namespace views { inline constexpr unspecified chunk = unspecified; } // C++23
 }
 
 namespace std {
@@ -482,6 +493,7 @@ namespace std {
 #    include <__ranges/adjacent_view.h>
 #    include <__ranges/as_rvalue_view.h>
 #    include <__ranges/chunk_by_view.h>
+#    include <__ranges/chunk_view.h>
 #    include <__ranges/from_range.h>
 #    include <__ranges/join_with_view.h>
 #    include <__ranges/repeat_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index ab781466f5ed5..f5fc33674f521 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -523,7 +523,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges                               202406L
 // # define __cpp_lib_ranges_as_const                      202207L
 # define __cpp_lib_ranges_as_rvalue                     202207L
-// # define __cpp_lib_ranges_chunk                         202202L
+# define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
 # define __cpp_lib_ranges_contains                      202207L
 # define __cpp_lib_ranges_find_last                     202207L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 5caa2c052c00f..631ae10c922ae 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -314,18 +314,17 @@ export namespace std {
       using std::ranges::views::adjacent_transform;
       using std::ranges::views::pairwise_transform;
     } // namespace views
+#endif
 
-#endif // _LIBCPP_STD_VER >= 23
-
-#if 0
+#if _LIBCPP_STD_VER >= 23
     using std::ranges::chunk_view;
 
-    using std::ranges::chunk_view<V>;
-
     namespace views {
       using std::ranges::views::chunk;
-    }
+    } // namespace views
+#endif
 
+#if 0
     using std::ranges::slide_view;
 
     namespace views {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
new file mode 100644
index 0000000000000..14c618c505e70
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// This test ensures that we use `[[no_unique_address]]` in `chunk_view`.
+
+#include <cstddef>
+#include <ranges>
+#include <string>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct input_view {
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<input_view> = true;
+static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
+using CV1 = std::ranges::chunk_view<input_view>;
+// Expected CV1 (with View == input) layout:
+// [[no_unique_address]] _View __base_                                         // size: 0
+// [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] __non_propagating_cache<iterator_t<_View>> __current_ // size: sizeof(__non_propagating_cache<cpp20_input_iterator<int*>>), align: std::ptrdiff_t
+static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>) == alignof(std::ptrdiff_t));
+static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
+                                 sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
+
+using CV2 = std::ranges::chunk_view<forward_view>;
+// Expected CV2 (with View >= forward) layout:
+// [[no_unique_address]] _View __base_             // size: 0
+// [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
+static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..5e557715f4b03
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
+  auto view     = range | std::views::chunk(2);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(std::as_const(view)).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(view).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(view.begin());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(std::as_const(view).begin());
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 5116864879485..41e1603f62350 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -270,17 +270,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -387,17 +381,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
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 b3b424a1d77ce..bd1b55ccc8dfa 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
@@ -5700,17 +5700,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -7619,17 +7613,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
new file mode 100644
index 0000000000000..657e16383256b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   std::views::chunk
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                       = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::ref_view<std::array<int, 8>> view = array | std::views::all;
+
+  // Test `views::chunk(view, n)`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        std::views::chunk(view, 2);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::views::chunk(std::as_const(view), 2);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2}));
+  }
+
+  // Test `views::chunk(n)(range)`
+  {
+    static_assert(noexcept(std::views::chunk(2)));
+    /*__pipable*/ auto adaptor = std::views::chunk(3);
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        adaptor(view);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        adaptor(std::as_const(view));
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3}));
+  }
+
+  // Test `view | views::chunk`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        view | std::views::chunk(4);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3, 4}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::as_const(view) | std::views::chunk(4);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3, 4}));
+  }
+
+  // Test `views::chunk | adaptor`
+  {
+    /*__pipable*/ auto adaptors            = std::views::chunk(5) | std::views::join;
+    std::ranges::input_range auto rejoined = view | adaptors;
+    assert(std::ranges::equal(rejoined, view));
+    std::ranges::input_range auto const_rejoined = std::as_const(view) | adaptors;
+    assert(std::ranges::equal(const_rejoined, view));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
new file mode 100644
index 0000000000000..ac17f97a0a292
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() &&;
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <memory>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                                                   = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>> chunked = array | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
+      std::as_const(array) | std::views::chunk(4);
+
+  // Test `chunk_view.base()`
+  {
+    std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
+    assert(std::addressof(base) == std::addressof(array));
+
+    std::same_as<const std::array<int, 8>&> decltype(auto) const_base = const_chunked.base().base();
+    assert(std::addressof(const_base) == std::addressof(array));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
new file mode 100644
index 0000000000000..3cccd7d1bebc6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr __outer_iterator begin();
+
+//   V models forward_range:
+//     constexpr auto begin() requires (!__simple_view<V>);
+//     constexpr auto begin() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.begin()` when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == input_chunked.end());
+  }
+
+  // Test `chunk_view.begin()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == chunked.end());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.begin();
+    assert(std::ranges::equal(*const_it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++const_it, std::vector{7, 8}));
+    assert(++const_it == const_chunked.end());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..9bf2baba58c78
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class R>
+//   chunk_view(R&&, range_difference_t<R>) -> chunk_view<all_t<R>>;
+
+#include <ranges>
+
+struct view : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct range {
+  int* begin() const;
+  int* end() const;
+};
+
+struct borrowed_range {
+  int* begin() const;
+  int* end() const;
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<borrowed_range> = true;
+
+void test_ctad() {
+  view v;
+  range r;
+  borrowed_range br;
+
+  // clang-format off
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(v, 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(v), 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(r, 0)), 
+                                      std::ranges::chunk_view<std::ranges::ref_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(r), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(br, 0)),
+                                      std::ranges::chunk_view<std::ranges::ref_view<borrowed_range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(br), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<borrowed_range>>>);
+  // clang-format on
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
new file mode 100644
index 0000000000000..73eb0d967f6a2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr default_sentinel_t end();
+
+//   V moduels forward_range:
+//     constexpr auto end() requires (!__simple_view<V>);
+//     constexpr auto end() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.end()` when V models only input_range
+  {
+    static_assert(noexcept(input_chunked.end()));
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it = input_chunked.end();
+  }
+
+  // Test `chunk_view.end()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--it, std::vector{1, 2, 3}));
+    assert(it == chunked.begin());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.end();
+    assert(std::ranges::equal(*--const_it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--const_it, std::vector{1, 2, 3}));
+    assert(const_it == const_chunked.begin());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
new file mode 100644
index 0000000000000..89891bc093080
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   General tests for chunk_view. This file does not test anything specifically.
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <string_view>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::string_view str = "Cheese with chicken chunk by chunk on truck with my trick";
+  // clang-format off
+  auto str2 = str 
+            | std::views::chunk(4)
+            | std::views::join
+            | std::views::chunk(314159)
+            | std::views::take(1)
+            | std::views::join
+            | std::views::lazy_split(' ')
+            | std::views::chunk(2)
+            | std::views::transform([] (auto&& subview)
+              {
+                return subview | std::views::join_with(' ');
+              })
+            | std::views::join_with(' ');
+  // clang-format on
+  assert(std::ranges::equal(str, str2));
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
new file mode 100644
index 0000000000000..d86726d958e6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator<(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator<=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base> &&
+//                three_way_comparable<iterator_t<Base>>;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t)`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    std::ranges::advance(it, 4);
+    assert(it == std::default_sentinel);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(input_chunked.end() - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - input_chunked.end() == -4);
+  }
+
+  // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() == chunked.begin());
+    assert(chunked.end() == chunked.end());
+  }
+
+  // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() < chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() > chunked.begin());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() <= chunked.begin());
+    assert(chunked.begin() <= chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() >= chunked.end());
+    assert(chunked.end() >= chunked.begin());
+  }
+
+  // Test `friend constexpr auto operator<=>(const iterator& x, const iterator& y)`
+  {
+    assert((chunked.begin() <=> chunked.begin()) == std::strong_ordering::equal);
+    assert((chunked.begin() <=> chunked.end()) == std::strong_ordering::less);
+    assert((chunked.end() <=> chunked.begin()) == std::strong_ordering::greater);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
new file mode 100644
index 0000000000000..397b32e73080a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+//   constexpr iterator& operator--()
+//     requires bidirectional_range<Base>;
+//   constexpr iterator operator--(int)
+//     requires bidirectional_range<Base>;
+//   constexpr iterator& operator-=(difference_type)
+//     requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+
+  // Test `constexpr iterator& operator--();`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator operator--(int)`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    it--;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator& operator-=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.end();
+    it -= 3;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
new file mode 100644
index 0000000000000..b8772d0fac3d2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr value_type outer_iterator::operator*() const;
+//     constexpr inner_iterator outer_iterator::value_type::begin() const noexcept;
+//     constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept;
+//     constexpr range_reference_v<V> inner_iterator::operator*() const;
+
+//   V models forward_range:
+//     constexpr value_type iterator::operator*() const;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `constexpr value_type outer_iterator::operator*() const`
+  {
+    static_assert(std::ranges::input_range<decltype(*input_chunked.begin())>);
+  }
+
+  // Test `constexpr inner_iterator outer_iterator::value_type::begin() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    assert(*inner.begin() == *vector.begin());
+    static_assert(noexcept(inner.begin()));
+  }
+
+  // Test `constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it                   = inner.end();
+    static_assert(noexcept((inner.end())));
+  }
+
+  // Test `constexpr value_type iterator::operator*() const`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::same_as<int> decltype(auto) v                           = *it;
+    assert(v == 1);
+  }
+
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
+  {
+    std::same_as<int&> decltype(auto) v = *(*chunked.begin()).begin();
+    assert(v == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
new file mode 100644
index 0000000000000..6048aba027ce9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     constexpr outer_iterator& operator++();
+//     constexpr void operator++(int);
+//     constexpr inner_iterator& operator++();
+//     constexpr void operator++(int);
+
+//   V models forward_range
+//     constexpr iterator& operator++();
+//     constexpr iterator operator++(int);
+//     constexpr iterator& operator+=(difference_type)
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(2);
+
+  // Test `constexpr outer_iterator& operator++();`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr void operator++(int);`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    assert(*++it == 2);
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(*it == 2);
+  }
+
+  // Test `constexpr iterator& operator++();`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator operator++(int)`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator& operator+=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    it += 3;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..09d16c9a48af0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f61d6f991cb15..49fb196903e77 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1106,7 +1106,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_ranges_chunk",
             "values": {"c++23": 202202},
             "headers": ["ranges"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ranges_chunk_by",

>From af914e794ddb86103e6b7acf3bf17d40f5055a8a Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 01:44:18 +0800
Subject: [PATCH 02/21] Code Review: adjust release notes

Note that we will merge this commit with the corresponding change in #172948 in the future.
---
 libcxx/docs/ReleaseNotes/22.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index d2a2d1b712689..30fc22fc0d787 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,7 +54,7 @@ Implemented Papers
 - 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>`__)
-- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------

>From dc62f798ba5615dcac60fffa2cc0afa8b79344c5 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 13:03:41 +0800
Subject: [PATCH 03/21] Add Tests: test if `_LIBCPP_ASSERT_XXX` works when
 `views::chunk` meets an undefined behaviour

---
 libcxx/include/__ranges/chunk_view.h          |  6 ++-
 .../range.chunk/assert.constructor.pass.cpp   | 39 ++++++++++++++
 .../range.chunk.iter/assert.advance.pass.cpp  | 32 ++++++++++++
 .../range.chunk.iter/assert.deref.pass.cpp    | 52 +++++++++++++++++++
 .../assert.increment.pass.cpp                 | 52 +++++++++++++++++++
 .../ranges/range.adaptors/range.chunk/types.h | 50 ++++++++++++++++++
 6 files changed, 229 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f19d2c9ede8a7..683b6bbaa2ca5 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -128,11 +128,12 @@ class chunk_view<_View>::__outer_iterator {
   _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
-    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
     return value_type(*__parent_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
     __parent_->__remainder_ = __parent_->__n_;
     return *this;
@@ -214,6 +215,7 @@ class chunk_view<_View>::__inner_iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ++*__parent_->__current_;
     if (*__parent_->__current_ == ranges::end(__parent_->__base_))
       __parent_->__remainder_ = 0;
@@ -386,7 +388,7 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
-    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to increment past-the-end chunk_view iterator");
     __missing_ = ranges::advance(__current_, __n_, __end_);
     return *this;
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
new file mode 100644
index 0000000000000..03fc7dfb6a5de
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Construct a chunk_view with chunk size <= 0
+
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3};
+
+  // Test constructor when V models only input_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  // Test constructor when V models forward_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
new file mode 100644
index 0000000000000..af2b826aa8eba
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to advance chunk_view iterator out of range
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+
+  // Test advance past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    TEST_LIBCPP_ASSERT_FAILURE(it += 4, "Trying to advance chunk_view iterator out of range");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
new file mode 100644
index 0000000000000..cb1d92885e684
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to dereference past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test dereference iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*outer_it, "Trying to dereference past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*inner_it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+
+  // Test dereference iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(*it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
new file mode 100644
index 0000000000000..8160ee1bb3e14
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to increment past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test increment past-the-end iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++outer_it, "Trying to increment past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++inner_it, "Trying to increment past-the-end chunk_view iterator");
+  }
+
+  // Test increment past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(++it, "Trying to increment past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..a5d688abfa1b0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif

>From b664c0cb8f8ed9b46629fa49e4bdd846734615d4 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:26 +0800
Subject: [PATCH 04/21] Fix CI: adjust `#include` path in `test/libcxx` files

---
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp          | 2 +-
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index cb1d92885e684..0c96742147189 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 8160ee1bb3e14..451683bab71b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};

>From 4dc9b06c0dd515030226b9d27841352ef62549a3 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:58 +0800
Subject: [PATCH 05/21] Fix CI: add template deduction guide for
 `input_span<T>`

---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 3 +++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..0978a23c37244 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..4ff1af1d7cda3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 1e8906ad49a2406158161cc1b9d0e1570af2959d Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:53:21 +0800
Subject: [PATCH 06/21] Code Review: add `REQUIRES: std-at-least-c++23` in all
 the tests related to `views::chunk`.

The previous commit, 4dc9b06c0dd515030226b9d27841352ef62549a3, which introduce manual/explicit template deduction guide in `input_span<T>`, is always implicitly-provided under -std=c++23. So this commit also reverts the changes about `types.h` in the previous commit.
---
 .../range.adaptors/range.chunk/assert.constructor.pass.cpp     | 1 +
 .../range.chunk/range.chunk.iter/assert.advance.pass.cpp       | 1 +
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp         | 1 +
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp     | 1 +
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h   | 3 ---
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h      | 3 ---
 6 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
index 03fc7dfb6a5de..477f89be5f070 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
index af2b826aa8eba..9b91d94841b91 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index 0c96742147189..3640b04192bf4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 451683bab71b0..61c096f74158e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index 0978a23c37244..a5d688abfa1b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 4ff1af1d7cda3..09d16c9a48af0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 7e21388c70b38c32125e4d3eda9b3a4c66e1d3db Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 14:13:59 +0800
Subject: [PATCH 07/21] Fix CI: add template deduction guide for
 `input_span<T>` again.

Seems that even in C++23 the CI fails due to no implicit deduction guide, here we just add the explicit one and make the CI to basically run ok.
---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 4 ++++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..4852ec29204bb 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..7533e41713d72 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From f1ed40a1aed2a1f5ebd1e56efcd6ae2bbb25dc26 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:07:21 +0800
Subject: [PATCH 08/21] Code review: Update libcxx/test synopsis

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 .../ranges/range.adaptors/range.chunk/nodiscard.verify.cpp      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 5e557715f4b03..9264256fc6a52 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+// Test that functions are marked [[nodiscard]].
 
 #include <ranges>
 #include <utility>

>From 12bec119ba49cd6016055f5887f5e83efa25ee30 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:21:29 +0800
Subject: [PATCH 09/21] Code review: Fix synopsis.

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index ac17f97a0a292..b45e1f59f1394 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() const& requires copy_constructible<V>;
 //   constexpr V base() &&;
 
 #include <array>

>From 1e05c5a038dd4cee5a0049b8c7e92e59c8aabd82 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 21:13:46 +0800
Subject: [PATCH 10/21] Code review: sort and unique the tests

---
 .../no_unique_address.compile.pass.cpp        |  24 ++--
 .../range.chunk/nodiscard.verify.cpp          |  35 ++----
 .../range.chunk.iter/arithmetic.pass.cpp      | 108 ++++++++++++++++++
 .../range.chunk.iter/compare.pass.cpp         |  29 +++--
 .../range.chunk.iter/subscript.pass.cpp       |  42 +++++++
 5 files changed, 189 insertions(+), 49 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
index 14c618c505e70..5d32daf4c178a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -20,6 +20,8 @@
 #include "test_iterators.h"
 #include "test_range.h"
 
+// When V models only input_range
+
 struct input_view {
   cpp20_input_iterator<int*> begin() const;
   sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
@@ -28,16 +30,8 @@ template <>
 inline constexpr bool std::ranges::enable_view<input_view> = true;
 static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
 
-struct forward_view {
-  int* begin() const;
-  int* end() const;
-};
-template <>
-inline constexpr bool std::ranges::enable_view<forward_view> = true;
-static_assert(std::ranges::forward_range<forward_view>);
-
 using CV1 = std::ranges::chunk_view<input_view>;
-// Expected CV1 (with View == input) layout:
+// Expected CV1 (when V models only input_range) layout:
 // [[no_unique_address]] _View __base_                                         // size: 0
 // [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
 // [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
@@ -46,8 +40,18 @@ static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<
 static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
                                  sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
 
+// When V models forward_range
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
 using CV2 = std::ranges::chunk_view<forward_view>;
-// Expected CV2 (with View >= forward) layout:
+// Expected CV2 (when V models forward_range) layout:
 // [[no_unique_address]] _View __base_             // size: 0
 // [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
 static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 9264256fc6a52..4d0fb4f76857c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -19,15 +19,6 @@ void test() {
   char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
   auto view     = range | std::views::chunk(2);
 
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   view.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -43,18 +34,19 @@ void test() {
   std::as_const(view).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  view.end();
+  std::ranges::iter_move(view.begin());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(view).end();
+  std::ranges::iter_move(std::as_const(view).begin());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
+  view.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
+  std::as_const(view).end();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
+  std::ranges::iter_move(view.end());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
+  std::ranges::iter_move(std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   *view.begin();
@@ -71,16 +63,11 @@ void test() {
   (std::as_const(view).begin() == std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == std::as_const(view).end());
+  std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == std::as_const(view).end());
-
+  std::views::chunk(range, 3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(view.begin());
+  range | std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(std::as_const(view).begin());
+  std::views::reverse | std::views::chunk(3);
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..6baa2fce13695
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const inner_iterator& x)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr iterator operator+(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator+(difference_type n, const iterator& i)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator-(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+//       requires sized_sentinel_for<iterator_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(std::default_sentinel - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - std::default_sentinel == -4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t, const inner_iterator& x)`
+  {
+    assert(std::default_sentinel - (*input_chunked.begin()).begin() == 3);
+  }
+
+  // Test `friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t)`
+  {
+    assert((*input_chunked.begin()).begin() - std::default_sentinel == -3);
+  }
+
+  // Test `friend constexpr iterator operator+(const iterator& i, difference_type n)`
+  {
+    assert(chunked.begin() + 4 == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator+(difference_type n, const iterator& i)`
+  {
+    assert(4 + chunked.begin() == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator-(const iterator& i, difference_type n)`
+  {
+    assert(chunked.end() - 4 == chunked.begin());
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)`
+  {
+    assert(std::default_sentinel - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)`
+  {
+    assert(chunked.begin() - std::default_sentinel == -4);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
index d86726d958e6e..ea6731cccce85 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -11,14 +11,12 @@
 // <ranges>
 
 //   V models only input_range
-//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
-//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
-//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t);
 
 //   V models forward_range
-//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator==(const iterator& x, const iterator& y);
+//     friend constexpr bool operator==(const iterator& x, default_sentinel_t);
 //     friend constexpr bool operator<(const iterator& x, const iterator& y)
 //       requires random_access_range<Base>;
 //     friend constexpr bool operator>(const iterator& x, const iterator& y)
@@ -31,7 +29,6 @@
 //       requires random_access_range<Base> &&
 //                three_way_comparable<iterator_t<Base>>;
 
-#include <algorithm>
 #include <cassert>
 #include <compare>
 #include <iterator>
@@ -53,14 +50,11 @@ constexpr bool test() {
     assert(it == std::default_sentinel);
   }
 
-  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  // Test `friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t)`
   {
-    assert(input_chunked.end() - input_chunked.begin() == 4);
-  }
-
-  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
-  {
-    assert(input_chunked.begin() - input_chunked.end() == -4);
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::ranges::advance(it, 3);
+    assert(it == std::default_sentinel);
   }
 
   // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
@@ -69,6 +63,11 @@ constexpr bool test() {
     assert(chunked.end() == chunked.end());
   }
 
+  // Test `friend constexpr bool operator==(const iterator& x, default_sentinel)`
+  {
+    assert(chunked.end() == std::default_sentinel);
+  }
+
   // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() < chunked.end());
@@ -79,7 +78,7 @@ constexpr bool test() {
     assert(chunked.end() > chunked.begin());
   }
 
-  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  // Test `friend constexpr bool operator<=(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() <= chunked.begin());
     assert(chunked.begin() <= chunked.end());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
new file mode 100644
index 0000000000000..9b45fc6d779bf
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models forward_range
+//     constexpr value_type iterator::operator[](difference_type n) const
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+
+  // Test `constexpr value_type iterator::operator[](difference_type n) const`
+  {
+    assert(std::ranges::equal(chunked.begin()[1], std::vector{4, 5, 6}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 033a1c16289990a16b1f674f380f7a72f38f48ea Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 14:30:08 -0400
Subject: [PATCH 11/21] Fix release notes

---
 libcxx/docs/ReleaseNotes/22.rst    | 1 -
 libcxx/docs/ReleaseNotes/23.rst    | 1 +
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0637ba98976a2..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -55,7 +55,6 @@ Implemented Papers
 - 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>`__)
-- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e7eb42207db42..a98f42da70f00 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -52,6 +52,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::chunk`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 6abcac5e4ebca..8c92d89be999b 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""

>From 7fe126d5e7c88787f26228c64bcb5f89977c5b83 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:02:10 +0800
Subject: [PATCH 12/21] Resolve overload conflicts on `__div_ceil` in
 `ranges::chunk_view` and `ranges::stride_view`.

Both `ranges::chunk_view` and `ranges::stride_view` need a helper function called `__div_ceil` that solves some simple maths. According to the style of other libcxx internal helper functions (e.g. `__priority_tag`, `__small_buffer`), it seems that we can put `__div_ceil` into a new file `__utility/div_ceil.h`. However, most files in `__utility/` refer to a corresponding symbol in `<utility>` header, so that if someone feels this is not proper, I'll be glad to revert it.
---
 libcxx/include/CMakeLists.txt         |  1 +
 libcxx/include/__ranges/chunk_view.h  | 21 ++++++-----------
 libcxx/include/__ranges/stride_view.h | 20 +++++-----------
 libcxx/include/__utility/div_ceil.h   | 33 +++++++++++++++++++++++++++
 libcxx/include/module.modulemap.in    |  1 +
 5 files changed, 48 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/include/__utility/div_ceil.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 76b93c17c3312..0e9f19a81b1ab 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -935,6 +935,7 @@ set(files
   __utility/convert_to_integral.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
+  __utility/div_ceil.h
   __utility/element_count.h
   __utility/empty.h
   __utility/exception_guard.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 683b6bbaa2ca5..68d4408295e47 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -36,6 +36,7 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 
@@ -52,14 +53,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Integral>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
-  _Integral __r = __num / __denom;
-  if (__num % __denom)
-    ++__r;
-  return __r;
-}
-
 template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
@@ -98,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -153,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -321,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -510,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..7ea68b13330c3 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -46,15 +47,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Value>
-_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) {
-  _Value __r = __left / __right;
-  if (__left % __right) {
-    ++__r;
-  }
-  return __r;
-}
-
 template <input_range _View>
   requires view<_View>
 class stride_view : public view_interface<stride_view<_View>> {
@@ -121,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -340,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -ranges::__div_ceil(-__n, __x.__stride_);
+      return -__div_ceil(-__n, __x.__stride_);
     }
-    return ranges::__div_ceil(__n, __x.__stride_);
+    return __div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)
diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
new file mode 100644
index 0000000000000..595c590b9e3f6
--- /dev/null
+++ b/libcxx/include/__utility/div_ceil.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_DIV_CEIL_H
+#define _LIBCPP___UTILITY_DIV_CEIL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __div_ceil computes the ceiling of the division of two integers. It is mainly used by
+// range adaptors like chunk_view and stride_view.
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_DIV_CEIL_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 64238440b9dd7..ef5d15d063f69 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2200,6 +2200,7 @@ module std [system] {
     module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
+    module div_ceil                        { header "__utility/div_ceil.h" }
     module element_count                   { header "__utility/element_count.h" }
     module exception_guard                 { header "__utility/exception_guard.h" }
     module exchange                        { header "__utility/exchange.h" }

>From d27b355e67e17b0ed15d44a247fdba1c3b13ed49 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:22:38 +0800
Subject: [PATCH 13/21] Fix test error that `constexpr` is not supported in
 `-std=c++03`

---
 libcxx/include/__utility/div_ceil.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 595c590b9e3f6..4d2868465c141 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,6 +17,8 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if _LIBCPP_STD_VER >= 11
+
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
 
@@ -28,6 +30,8 @@ template <class _Integral>
   return __r;
 }
 
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___UTILITY_DIV_CEIL_H

>From a1a5918bb79d29cbe7be2736da7a72b83ee0afbb Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:38:37 +0800
Subject: [PATCH 14/21] Fix test error again that `constexpr` is not supported
 in `-std=c++11`

---
 libcxx/include/__utility/div_ceil.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 4d2868465c141..21e9bcb30d1a1 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,7 +17,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 11
+#if _LIBCPP_STD_VER >= 23
 
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
@@ -30,7 +30,7 @@ template <class _Integral>
   return __r;
 }
 
-#endif
+#endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_END_NAMESPACE_STD
 

>From c2e0e2e926c92ee6d001e060aa5660b681874dfd Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:45:41 +0800
Subject: [PATCH 15/21] Fix test error that `std::as_const` is included from
 `<utility>`.

---
 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp | 1 +
 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
index 3cccd7d1bebc6..29588a57fbcab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -22,6 +22,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
index 73eb0d967f6a2..2f37ac5f73524 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -23,6 +23,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"

>From 7d3cdd73f5da77a7499969fb7954312a19e26630 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 14:19:19 +0800
Subject: [PATCH 16/21] Avoid ADL in `std::__div_ceil`.

---
 libcxx/include/__ranges/chunk_view.h  | 12 ++++++------
 libcxx/include/__ranges/stride_view.h | 10 +++++-----
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 68d4408295e47..1107a9238b2bc 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -91,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -146,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return std::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -314,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -503,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return std::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 7ea68b13330c3..a8b485cbe33e5 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -113,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -332,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -__div_ceil(-__n, __x.__stride_);
+      return -std::__div_ceil(-__n, __x.__stride_);
     }
-    return __div_ceil(__n, __x.__stride_);
+    return std::__div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return std::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)

>From 116c6d85d8a9ad177b1df1975eea92479cb03664 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 16:39:58 +0800
Subject: [PATCH 17/21] [Important] Add missing concepts requirements.

---
 libcxx/include/__ranges/chunk_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1107a9238b2bc..47e6bd56415b6 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -400,7 +400,8 @@ class chunk_view<_View>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires bidirectional_range<_Base> {
     auto __tmp = *this;
     --*this;
     return __tmp;

>From 545bbdf10d24c3790790bfe669b8c756da719838 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Fri, 26 Jun 2026 22:35:12 +0800
Subject: [PATCH 18/21] Cover tests on `chunk_range`. (I'll continue to cover
 tests on `__iterator`, `__inner_iterator` and `__outer_iterator` at the
 weekend)

---
 libcxx/include/__ranges/chunk_view.h          |  3 +-
 .../range.adaptors/range.chunk/base.pass.cpp  | 12 +++-
 .../range.chunk/borrowing.compile.pass.cpp    | 27 +++++++
 .../range.adaptors/range.chunk/ctor.pass.cpp  | 54 ++++++++++++++
 .../range.adaptors/range.chunk/size.pass.cpp  | 72 +++++++++++++++++++
 5 files changed, 166 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 47e6bd56415b6..1798711eabd16 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -401,7 +401,8 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
-    requires bidirectional_range<_Base> {
+    requires bidirectional_range<_Base>
+  {
     auto __tmp = *this;
     --*this;
     return __tmp;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index b45e1f59f1394..4dca324a23695 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -26,7 +26,7 @@ constexpr bool test() {
   std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
       std::as_const(array) | std::views::chunk(4);
 
-  // Test `chunk_view.base()`
+  // Test `chunk_view.base() const&`
   {
     std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
     assert(std::addressof(base) == std::addressof(array));
@@ -35,6 +35,16 @@ constexpr bool test() {
     assert(std::addressof(const_base) == std::addressof(array));
   }
 
+  // Test `chunk_view.base() &&`
+  {
+    std::same_as<std::ranges::ref_view<std::array<int, 8>>> decltype(auto) moved = std::move(chunked).base();
+    assert(std::addressof(moved.base()) == std::addressof(array));
+
+    std::same_as<std::ranges::ref_view<const std::array<int, 8>>> decltype(auto) const_moved =
+        std::move(const_chunked).base();
+    assert(std::addressof(const_moved.base()) == std::addressof(array));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
new file mode 100644
index 0000000000000..1eb4a34f87217
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class V>
+//   inline constexpr bool enable_borrowed_range<chunk_view<V>> =
+//       forward_range<V> && enable_borrowed_range<V>;
+
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+// When V models only `input_range`.
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<input_span<int>>>);
+
+// When V models at least `forward_range`.
+static_assert(std::ranges::enable_borrowed_range<std::ranges::chunk_view<BorrowedView>>);
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
new file mode 100644
index 0000000000000..b60e040b807eb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.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++23
+
+// <ranges>
+
+//   constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n);
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_convertible.h"
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models only `input_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>, input_span<int>, std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(input_span(vector.data(), 8), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+                                    std::ranges::ref_view<std::vector<int>>,
+                                    std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
new file mode 100644
index 0000000000000..820774c3b750e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr auto size() requires sized_range<_View>;
+//   constexpr auto size() const requires sized_range<const _View>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+  // Test `chunk_view.size()` when V models only `input_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<input_span<int>>>);
+    static_assert(!std::ranges::sized_range<const std::ranges::chunk_view<input_span<int>>>);
+
+    auto chunked = input_span<int>(arr, 12) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when V models `forward_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+    static_assert(std::ranges::sized_range<const std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+    const auto& const_chunked = chunked;
+    assert(const_chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when the range is not fully divisible
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(5);
+    assert(chunked.size() == 3);
+  }
+
+  // Test `chunk_view.size()` when the range is empty
+  {
+    static_assert(std::ranges::sized_range<std::ranges::empty_view<int>>);
+    auto chunked = std::views::empty<int> | std::views::chunk(3);
+    assert(chunked.size() == 0);
+  }
+
+  // Test `chunk_view.size()` when chunk size is larger than the range
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(100);
+    assert(chunked.size() == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From e262f9e7497fb3ccf232a3c7265b8faa83f2fa20 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:36:04 +0800
Subject: [PATCH 19/21] Fix chunk_view ctor test for forward ranges.

---
 .../test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
index b60e040b807eb..c0d210aa45dc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
@@ -35,11 +35,11 @@ constexpr bool test() {
 
   // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
   {
-    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+    static_assert(!test_convertible<std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>>,
                                     std::ranges::ref_view<std::vector<int>>,
                                     std::ptrdiff_t>());
 
-    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked(std::ranges::ref_view(vector), 3);
     assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
   }
 

>From bf13c55de976c562120249f2fc7208ea1091fc95 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:39:36 +0800
Subject: [PATCH 20/21] Use consistent copy_constructible spelling in
 chunk_view

---
 libcxx/include/__ranges/chunk_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1798711eabd16..f3edc617de07a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -71,7 +71,7 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
-    requires std::copy_constructible<_View>
+    requires copy_constructible<_View>
   {
     return __base_;
   }

>From f457f267b72fe64a2f22c288da4b096ae0bcc83c Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:48:59 +0800
Subject: [PATCH 21/21] Remove unnecessary no_unique_address from chunk_view
 counts

---
 libcxx/include/__ranges/chunk_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f3edc617de07a..4478af951dbca 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -57,8 +57,8 @@ template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  range_difference_t<_View> __n_;
+  range_difference_t<_View> __remainder_;
   _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
 
   class __outer_iterator;
@@ -256,7 +256,7 @@ template <view _View>
   requires forward_range<_View>
 class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  range_difference_t<_View> __n_;
 
   template <bool _Const>
   class __iterator;


From libcxx-commits at lists.llvm.org  Sun Jul 19 08:03:12 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 08:03:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Too many
 `_LIBCPP_INTRODUCED_IN_LLVM_23` macros (PR #210602)
Message-ID: <llvm.org/llvm/llvm-project/pull/210602@github.com>


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

Removed redundant ones.

>From e0e2e46bcb2406272d1e2a06cb256ff75ff30e6e Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sun, 19 Jul 2026 18:02:51 +0300
Subject: [PATCH] [libc++] Too many `_LIBCPP_INTRODUCED_IN_LLVM_23` macros

Removed redundant ones.
---
 libcxx/include/__configuration/availability.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 735af5d431cb1..1fbd87ba52a64 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -38,8 +38,6 @@
 // When availability annotations are disabled, we take for granted that features introduced
 // in all versions of the library are available.
 #if !_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS
-#  define _LIBCPP_INTRODUCED_IN_LLVM_23 1
-#  define _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE /* nothing */
 
 #  define _LIBCPP_INTRODUCED_IN_LLVM_23 1
 #  define _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE /* nothing */


From libcxx-commits at lists.llvm.org  Sun Jul 19 08:03:45 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 08:03:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Too many
 `_LIBCPP_INTRODUCED_IN_LLVM_23` macros (PR #210602)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210602@github.com>
Message-ID: <6a5ce751.6c5720b3.2da3b9.11f9@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

<details>
<summary>Changes</summary>

Removed redundant ones.

---
Full diff: https://github.com/llvm/llvm-project/pull/210602.diff


1 Files Affected:

- (modified) libcxx/include/__configuration/availability.h (-2) 


``````````diff
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 735af5d431cb1..1fbd87ba52a64 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -38,8 +38,6 @@
 // When availability annotations are disabled, we take for granted that features introduced
 // in all versions of the library are available.
 #if !_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS
-#  define _LIBCPP_INTRODUCED_IN_LLVM_23 1
-#  define _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE /* nothing */
 
 #  define _LIBCPP_INTRODUCED_IN_LLVM_23 1
 #  define _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE /* nothing */

``````````

</details>


https://github.com/llvm/llvm-project/pull/210602

From libcxx-commits at lists.llvm.org  Sun Jul 19 08:07:08 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 08:07:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test][NFC] Removed unsupported
 compilers from tests (PR #210413)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210413@github.com>
Message-ID: <6a5ce81c.bc28b987.2e0c9a.0bcf@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/210413

>From d98c3f09b80c14722f3b9bb7420ed869d9372450 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 22:19:37 +0300
Subject: [PATCH 1/2] [libc++][test][NFC] Remove unsupported compilers from
 tests

---
 libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp     | 2 +-
 .../numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp    | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
index 46a4b1c788ef9..f9c910eca1f47 100644
--- a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 // UNSUPPORTED: gcc
-// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22, clang-23, clang-24, apple-clang-17, apple-clang-21
+// UNSUPPORTED: clang-21, clang-22, clang-23, clang-24, apple-clang-21
 
 // ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-dynamic-class-memaccess
 
diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp
index 16c326f062f79..ed5c971c4c6e7 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp
@@ -10,8 +10,8 @@
 
 // TODO(LLVM25): remove these guards. Clang <= 22 mis-evaluates
 // std::saturating_mul on non-byte-aligned _BitInt. https://llvm.org/PR204085
-// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22
-// UNSUPPORTED: apple-clang-17, apple-clang-18, apple-clang-19, apple-clang-20, apple-clang-21
+// UNSUPPORTED: clang-21, clang-22
+// UNSUPPORTED: apple-clang-21
 // UNSUPPORTED: target={{.+}}-android{{.*}}
 
 // <numeric>

>From 1ce0c53c2701439a7d21efe097f8ee5ea5562338 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sun, 19 Jul 2026 18:06:51 +0300
Subject: [PATCH 2/2] Addressed comment

---
 .../numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp      | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp
index ed5c971c4c6e7..20275ce1f7adf 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp
@@ -8,8 +8,6 @@
 
 // REQUIRES: std-at-least-c++26
 
-// TODO(LLVM25): remove these guards. Clang <= 22 mis-evaluates
-// std::saturating_mul on non-byte-aligned _BitInt. https://llvm.org/PR204085
 // UNSUPPORTED: clang-21, clang-22
 // UNSUPPORTED: apple-clang-21
 // UNSUPPORTED: target={{.+}}-android{{.*}}


From libcxx-commits at lists.llvm.org  Sun Jul 19 08:25:31 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 08:25:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2442R1
	`std::views::chunk` (PR #171234)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171234@github.com>
Message-ID: <6a5cec6b.160324b0.1f653e.15ed@mx.google.com>


https://github.com/shyeyian updated https://github.com/llvm/llvm-project/pull/171234

>From f6554ea2a0e1d9571cf82fc7b2f3d1115c466474 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at petalmail.com>
Date: Wed, 24 Dec 2025 22:05:29 +0800
Subject: [PATCH 01/22] Implement `views::chunk`.

This commit squashes previous commits.
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/22.rst               |   1 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/chunk_view.h          | 562 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/ranges                         |  12 +
 libcxx/include/version                        |   2 +-
 libcxx/modules/std/ranges.inc                 |  11 +-
 .../no_unique_address.compile.pass.cpp        |  53 ++
 .../range.chunk/nodiscard.verify.cpp          |  86 +++
 .../ranges.version.compile.pass.cpp           |  32 +-
 .../version.version.compile.pass.cpp          |  32 +-
 .../range.chunk/adaptor.pass.cpp              |  75 +++
 .../range.adaptors/range.chunk/base.pass.cpp  |  46 ++
 .../range.adaptors/range.chunk/begin.pass.cpp |  67 +++
 .../range.chunk/ctad.compile.pass.cpp         |  55 ++
 .../range.adaptors/range.chunk/end.pass.cpp   |  65 ++
 .../range.chunk/general.pass.cpp              |  48 ++
 .../range.chunk.iter/compare.pass.cpp         | 109 ++++
 .../range.chunk.iter/decrement.pass.cpp       |  60 ++
 .../range.chunk.iter/deref.pass.cpp           |  77 +++
 .../range.chunk.iter/increment.pass.cpp       |  95 +++
 .../ranges/range.adaptors/range.chunk/types.h |  50 ++
 .../generate_feature_test_macro_components.py |   1 -
 25 files changed, 1494 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/chunk_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..8e57305f32ad1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -370,7 +370,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_rvalue``                             ``202207L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_chunk``                                 *unimplemented*
+    ``__cpp_lib_ranges_chunk``                                 ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                              ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0d1a1fbc00f2c..d2a2d1b712689 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,6 +54,7 @@ Implemented Papers
 - 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>`__)
+- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e4c6cb0f51065..8c8ab211e5bf4 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__","Only ``ranges::iota`` is implemented."
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__",""
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index eb423ae923b4f..2426da6848b51 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -707,6 +707,7 @@ set(files
   __ranges/all.h
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
+  __ranges/chunk_view.h
   __ranges/common_view.h
   __ranges/concepts.h
   __ranges/container_compatible_range.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
new file mode 100644
index 0000000000000..f19d2c9ede8a7
--- /dev/null
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_CHUNK_VIEW_H
+#define _LIBCPP___RANGES_CHUNK_VIEW_H
+
+#include <__algorithm/ranges_min.h>
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/take_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
+
+  class __outer_iterator;
+  class __inner_iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n), __remainder_(0) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires std::copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator begin() {
+    __current_.__emplace(ranges::begin(__base_));
+    __remainder_ = __n_;
+    return __outer_iterator(*this);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept {
+    return std::default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(chunk_view& __parent)
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  class value_type;
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator(__outer_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    return value_type(*__parent_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
+    __parent_->__remainder_ = __parent_->__n_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __outer_iterator& __i, default_sentinel_t) {
+    return *__i.__parent_->__current_ == ranges::end(__i.__parent_->__base_) && __i.__parent_->__remainder_ != 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __outer_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
+    if (__dist < __i.__parent_->__remainder_)
+      return __dist == 0 ? 0 : 1;
+    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __outer_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator::value_type : public view_interface<value_type> {
+  friend __outer_iterator;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(chunk_view& __parent) : __parent_(std::addressof(__parent)) {}
+
+public:
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator begin() const noexcept {
+    return __inner_iterator(*__parent_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return std::__to_unsigned_like(
+        ranges::min(__parent_->__remainder_, ranges::end(__parent_->__base_) - *__parent_->__current_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__inner_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(chunk_view& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+  using value_type       = range_value_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator(__inner_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator& operator=(__inner_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_View> base() const& { return *__parent_->__current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_reference_t<_View> operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
+    return **__parent_->__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    ++*__parent_->__current_;
+    if (*__parent_->__current_ == ranges::end(__parent_->__base_))
+      __parent_->__remainder_ = 0;
+    else
+      --__parent_->__remainder_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __inner_iterator& __i, default_sentinel_t) {
+    return __i.__parent_->__remainder_ == 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __inner_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return ranges::min(__i.__parent_->__remainder_, ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __inner_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
+    return ranges::iter_move(*__i.__parent_->__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void
+  iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) noexcept(
+      noexcept((ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_))))
+    requires indirectly_swappable<iterator_t<_View>>
+  {
+    return ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_);
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+class chunk_view<_View> : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+
+  template <bool _Const>
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires forward_range<const _View>
+  {
+    return __iterator<true>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    if constexpr (common_range<_View> && sized_range<_View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<false>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<_View> && !bidirectional_range<_View>)
+      return __iterator<false>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires forward_range<const _View>
+  {
+    if constexpr (common_range<const _View> && sized_range<const _View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<true>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<const _View> && !bidirectional_range<const _View>)
+      return __iterator<true>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+template <bool _Const>
+class chunk_view<_View>::__iterator {
+  friend chunk_view;
+
+  using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, chunk_view>;
+  using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
+
+  iterator_t<_Base> __current_         = iterator_t<_Base>();
+  sentinel_t<_Base> __end_             = sentinel_t<_Base>();
+  range_difference_t<_Base> __n_       = 0;
+  range_difference_t<_Base> __missing_ = 0;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      _Parent* __parent, iterator_t<_Base> __current, range_difference_t<_Base> __missing = 0)
+      : __current_(__current), __end_(ranges::end(__parent->__base_)), __n_(__parent->__n_), __missing_(__missing) {}
+
+  [[nodiscard]] static consteval auto __get_iterator_concept() {
+    if constexpr (random_access_range<_Base>)
+      return random_access_iterator_tag{};
+    else if constexpr (bidirectional_range<_Base>)
+      return bidirectional_iterator_tag{};
+    else
+      return forward_iterator_tag{};
+  }
+
+public:
+  using iterator_category = input_iterator_tag;
+  using iterator_concept  = decltype(__iterator::__get_iterator_concept());
+  using value_type        = decltype(views::take(subrange(__current_, __end_), __n_));
+  using difference_type   = range_difference_t<_Base>;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> &&
+                 convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+      : __current_(std::move(__i.__current_)),
+        __end_(std::move(__i.__end_)),
+        __n_(__i.__n_),
+        __missing_(__i.__missing_) {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() const { return __current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to dereference past-the-end chunk_view iterator");
+    return views::take(subrange(__current_, __end_), __n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator[](difference_type __pos) const
+    requires random_access_range<_Base>
+  {
+    return *(*this + __pos);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    __missing_ = ranges::advance(__current_, __n_, __end_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires bidirectional_range<_Base>
+  {
+    ranges::advance(__current_, __missing_ - __n_);
+    __missing_ = 0;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    if (__x > 0) {
+      _LIBCPP_ASSERT_PEDANTIC(ranges::distance(__current_, __end_) > __n_ * (__x - 1),
+                              "Trying to advance chunk_view iterator out of range");
+      ranges::advance(__current_, __n_ * (__x - 1));
+      __missing_ = ranges::advance(__current_, __n_, __end_);
+    } else if (__x < 0) {
+      ranges::advance(__current_, __n_ * __x + __missing_);
+      __missing_ = 0;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    return *this += -__x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
+    return __x.__current_ == __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, default_sentinel_t) {
+    return __x.__current_ == __x.__end_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __x.__current_ < __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __y < __x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__y < __x);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__x < __y);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(difference_type __pos, const __iterator& __i)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator-(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r -= __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, const __iterator& __j)
+    requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+  {
+    return (__i.__current_ - __j.__current_ + __i.__missing_ - __j.__missing_) / __i.__n_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <class _Range>
+chunk_view(_Range&&, range_difference_t<_Range>) -> chunk_view<views::all_t<_Range>>;
+
+template <class _View>
+inline constexpr bool enable_borrowed_range<chunk_view<_View>> = forward_range<_View> && enable_borrowed_range<_View>;
+
+namespace views {
+namespace __chunk {
+struct __fn {
+  template <viewable_range _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range, range_difference_t<_Range> __n) noexcept(
+      noexcept(/*-----*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))))
+      -> decltype(/*--*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))) {
+    return /*---------*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n));
+  }
+
+  template <class _DifferenceType>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_DifferenceType __n) noexcept(is_nothrow_constructible_v<decay_t<_DifferenceType>, _DifferenceType>) {
+    return __pipeable(std::__bind_back(__fn{}, std::forward<_DifferenceType>(__n)));
+  }
+};
+
+} // namespace __chunk
+
+inline namespace __cpo {
+inline constexpr auto chunk = __chunk::__fn{};
+
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_CHUNK_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4434a5446e35b..cb03eb8d14067 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1863,6 +1863,10 @@ module std [system] {
       header "__ranges/chunk_by_view.h"
       export std.functional.bind_back
     }
+    module chunk_view {
+      header "__ranges/chunk_view.h"
+      export std.functional.bind_back
+    }
     module common_view                    { header "__ranges/common_view.h" }
     module concepts                       { header "__ranges/concepts.h" }
     module container_compatible_range     { header "__ranges/container_compatible_range.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 9f725b12ac6c2..9ec6a4a819919 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -396,6 +396,17 @@ namespace std::ranges {
   class chunk_by_view;                                                      // C++23
 
   namespace views { inline constexpr unspecified chunk_by = unspecified; }  // C++23
+
+  // [range.chunk]
+  template <view V>
+    requires input_range<V>
+  class chunk_view;                                                     // C++23
+
+  template <view V>
+    requires forward_range<V>
+  class chunk_view<V>;                                                  // C++23
+
+  namespace views { inline constexpr unspecified chunk = unspecified; } // C++23
 }
 
 namespace std {
@@ -482,6 +493,7 @@ namespace std {
 #    include <__ranges/adjacent_view.h>
 #    include <__ranges/as_rvalue_view.h>
 #    include <__ranges/chunk_by_view.h>
+#    include <__ranges/chunk_view.h>
 #    include <__ranges/from_range.h>
 #    include <__ranges/join_with_view.h>
 #    include <__ranges/repeat_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index ab781466f5ed5..f5fc33674f521 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -523,7 +523,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges                               202406L
 // # define __cpp_lib_ranges_as_const                      202207L
 # define __cpp_lib_ranges_as_rvalue                     202207L
-// # define __cpp_lib_ranges_chunk                         202202L
+# define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
 # define __cpp_lib_ranges_contains                      202207L
 # define __cpp_lib_ranges_find_last                     202207L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 5caa2c052c00f..631ae10c922ae 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -314,18 +314,17 @@ export namespace std {
       using std::ranges::views::adjacent_transform;
       using std::ranges::views::pairwise_transform;
     } // namespace views
+#endif
 
-#endif // _LIBCPP_STD_VER >= 23
-
-#if 0
+#if _LIBCPP_STD_VER >= 23
     using std::ranges::chunk_view;
 
-    using std::ranges::chunk_view<V>;
-
     namespace views {
       using std::ranges::views::chunk;
-    }
+    } // namespace views
+#endif
 
+#if 0
     using std::ranges::slide_view;
 
     namespace views {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
new file mode 100644
index 0000000000000..14c618c505e70
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// This test ensures that we use `[[no_unique_address]]` in `chunk_view`.
+
+#include <cstddef>
+#include <ranges>
+#include <string>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct input_view {
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<input_view> = true;
+static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
+using CV1 = std::ranges::chunk_view<input_view>;
+// Expected CV1 (with View == input) layout:
+// [[no_unique_address]] _View __base_                                         // size: 0
+// [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] __non_propagating_cache<iterator_t<_View>> __current_ // size: sizeof(__non_propagating_cache<cpp20_input_iterator<int*>>), align: std::ptrdiff_t
+static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>) == alignof(std::ptrdiff_t));
+static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
+                                 sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
+
+using CV2 = std::ranges::chunk_view<forward_view>;
+// Expected CV2 (with View >= forward) layout:
+// [[no_unique_address]] _View __base_             // size: 0
+// [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
+static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..5e557715f4b03
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
+  auto view     = range | std::views::chunk(2);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(std::as_const(view)).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(view).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(view.begin());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(std::as_const(view).begin());
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 5116864879485..41e1603f62350 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -270,17 +270,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -387,17 +381,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
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 b3b424a1d77ce..bd1b55ccc8dfa 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
@@ -5700,17 +5700,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -7619,17 +7613,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
new file mode 100644
index 0000000000000..657e16383256b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   std::views::chunk
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                       = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::ref_view<std::array<int, 8>> view = array | std::views::all;
+
+  // Test `views::chunk(view, n)`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        std::views::chunk(view, 2);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::views::chunk(std::as_const(view), 2);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2}));
+  }
+
+  // Test `views::chunk(n)(range)`
+  {
+    static_assert(noexcept(std::views::chunk(2)));
+    /*__pipable*/ auto adaptor = std::views::chunk(3);
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        adaptor(view);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        adaptor(std::as_const(view));
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3}));
+  }
+
+  // Test `view | views::chunk`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        view | std::views::chunk(4);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3, 4}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::as_const(view) | std::views::chunk(4);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3, 4}));
+  }
+
+  // Test `views::chunk | adaptor`
+  {
+    /*__pipable*/ auto adaptors            = std::views::chunk(5) | std::views::join;
+    std::ranges::input_range auto rejoined = view | adaptors;
+    assert(std::ranges::equal(rejoined, view));
+    std::ranges::input_range auto const_rejoined = std::as_const(view) | adaptors;
+    assert(std::ranges::equal(const_rejoined, view));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
new file mode 100644
index 0000000000000..ac17f97a0a292
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() &&;
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <memory>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                                                   = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>> chunked = array | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
+      std::as_const(array) | std::views::chunk(4);
+
+  // Test `chunk_view.base()`
+  {
+    std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
+    assert(std::addressof(base) == std::addressof(array));
+
+    std::same_as<const std::array<int, 8>&> decltype(auto) const_base = const_chunked.base().base();
+    assert(std::addressof(const_base) == std::addressof(array));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
new file mode 100644
index 0000000000000..3cccd7d1bebc6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr __outer_iterator begin();
+
+//   V models forward_range:
+//     constexpr auto begin() requires (!__simple_view<V>);
+//     constexpr auto begin() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.begin()` when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == input_chunked.end());
+  }
+
+  // Test `chunk_view.begin()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == chunked.end());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.begin();
+    assert(std::ranges::equal(*const_it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++const_it, std::vector{7, 8}));
+    assert(++const_it == const_chunked.end());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..9bf2baba58c78
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class R>
+//   chunk_view(R&&, range_difference_t<R>) -> chunk_view<all_t<R>>;
+
+#include <ranges>
+
+struct view : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct range {
+  int* begin() const;
+  int* end() const;
+};
+
+struct borrowed_range {
+  int* begin() const;
+  int* end() const;
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<borrowed_range> = true;
+
+void test_ctad() {
+  view v;
+  range r;
+  borrowed_range br;
+
+  // clang-format off
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(v, 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(v), 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(r, 0)), 
+                                      std::ranges::chunk_view<std::ranges::ref_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(r), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(br, 0)),
+                                      std::ranges::chunk_view<std::ranges::ref_view<borrowed_range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(br), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<borrowed_range>>>);
+  // clang-format on
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
new file mode 100644
index 0000000000000..73eb0d967f6a2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr default_sentinel_t end();
+
+//   V moduels forward_range:
+//     constexpr auto end() requires (!__simple_view<V>);
+//     constexpr auto end() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.end()` when V models only input_range
+  {
+    static_assert(noexcept(input_chunked.end()));
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it = input_chunked.end();
+  }
+
+  // Test `chunk_view.end()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--it, std::vector{1, 2, 3}));
+    assert(it == chunked.begin());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.end();
+    assert(std::ranges::equal(*--const_it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--const_it, std::vector{1, 2, 3}));
+    assert(const_it == const_chunked.begin());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
new file mode 100644
index 0000000000000..89891bc093080
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   General tests for chunk_view. This file does not test anything specifically.
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <string_view>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::string_view str = "Cheese with chicken chunk by chunk on truck with my trick";
+  // clang-format off
+  auto str2 = str 
+            | std::views::chunk(4)
+            | std::views::join
+            | std::views::chunk(314159)
+            | std::views::take(1)
+            | std::views::join
+            | std::views::lazy_split(' ')
+            | std::views::chunk(2)
+            | std::views::transform([] (auto&& subview)
+              {
+                return subview | std::views::join_with(' ');
+              })
+            | std::views::join_with(' ');
+  // clang-format on
+  assert(std::ranges::equal(str, str2));
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
new file mode 100644
index 0000000000000..d86726d958e6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator<(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator<=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base> &&
+//                three_way_comparable<iterator_t<Base>>;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t)`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    std::ranges::advance(it, 4);
+    assert(it == std::default_sentinel);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(input_chunked.end() - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - input_chunked.end() == -4);
+  }
+
+  // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() == chunked.begin());
+    assert(chunked.end() == chunked.end());
+  }
+
+  // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() < chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() > chunked.begin());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() <= chunked.begin());
+    assert(chunked.begin() <= chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() >= chunked.end());
+    assert(chunked.end() >= chunked.begin());
+  }
+
+  // Test `friend constexpr auto operator<=>(const iterator& x, const iterator& y)`
+  {
+    assert((chunked.begin() <=> chunked.begin()) == std::strong_ordering::equal);
+    assert((chunked.begin() <=> chunked.end()) == std::strong_ordering::less);
+    assert((chunked.end() <=> chunked.begin()) == std::strong_ordering::greater);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
new file mode 100644
index 0000000000000..397b32e73080a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+//   constexpr iterator& operator--()
+//     requires bidirectional_range<Base>;
+//   constexpr iterator operator--(int)
+//     requires bidirectional_range<Base>;
+//   constexpr iterator& operator-=(difference_type)
+//     requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+
+  // Test `constexpr iterator& operator--();`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator operator--(int)`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    it--;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator& operator-=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.end();
+    it -= 3;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
new file mode 100644
index 0000000000000..b8772d0fac3d2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr value_type outer_iterator::operator*() const;
+//     constexpr inner_iterator outer_iterator::value_type::begin() const noexcept;
+//     constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept;
+//     constexpr range_reference_v<V> inner_iterator::operator*() const;
+
+//   V models forward_range:
+//     constexpr value_type iterator::operator*() const;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `constexpr value_type outer_iterator::operator*() const`
+  {
+    static_assert(std::ranges::input_range<decltype(*input_chunked.begin())>);
+  }
+
+  // Test `constexpr inner_iterator outer_iterator::value_type::begin() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    assert(*inner.begin() == *vector.begin());
+    static_assert(noexcept(inner.begin()));
+  }
+
+  // Test `constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it                   = inner.end();
+    static_assert(noexcept((inner.end())));
+  }
+
+  // Test `constexpr value_type iterator::operator*() const`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::same_as<int> decltype(auto) v                           = *it;
+    assert(v == 1);
+  }
+
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
+  {
+    std::same_as<int&> decltype(auto) v = *(*chunked.begin()).begin();
+    assert(v == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
new file mode 100644
index 0000000000000..6048aba027ce9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     constexpr outer_iterator& operator++();
+//     constexpr void operator++(int);
+//     constexpr inner_iterator& operator++();
+//     constexpr void operator++(int);
+
+//   V models forward_range
+//     constexpr iterator& operator++();
+//     constexpr iterator operator++(int);
+//     constexpr iterator& operator+=(difference_type)
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(2);
+
+  // Test `constexpr outer_iterator& operator++();`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr void operator++(int);`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    assert(*++it == 2);
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(*it == 2);
+  }
+
+  // Test `constexpr iterator& operator++();`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator operator++(int)`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator& operator+=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    it += 3;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..09d16c9a48af0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f61d6f991cb15..49fb196903e77 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1106,7 +1106,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_ranges_chunk",
             "values": {"c++23": 202202},
             "headers": ["ranges"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ranges_chunk_by",

>From af914e794ddb86103e6b7acf3bf17d40f5055a8a Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 01:44:18 +0800
Subject: [PATCH 02/22] Code Review: adjust release notes

Note that we will merge this commit with the corresponding change in #172948 in the future.
---
 libcxx/docs/ReleaseNotes/22.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index d2a2d1b712689..30fc22fc0d787 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,7 +54,7 @@ Implemented Papers
 - 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>`__)
-- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------

>From dc62f798ba5615dcac60fffa2cc0afa8b79344c5 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 13:03:41 +0800
Subject: [PATCH 03/22] Add Tests: test if `_LIBCPP_ASSERT_XXX` works when
 `views::chunk` meets an undefined behaviour

---
 libcxx/include/__ranges/chunk_view.h          |  6 ++-
 .../range.chunk/assert.constructor.pass.cpp   | 39 ++++++++++++++
 .../range.chunk.iter/assert.advance.pass.cpp  | 32 ++++++++++++
 .../range.chunk.iter/assert.deref.pass.cpp    | 52 +++++++++++++++++++
 .../assert.increment.pass.cpp                 | 52 +++++++++++++++++++
 .../ranges/range.adaptors/range.chunk/types.h | 50 ++++++++++++++++++
 6 files changed, 229 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f19d2c9ede8a7..683b6bbaa2ca5 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -128,11 +128,12 @@ class chunk_view<_View>::__outer_iterator {
   _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
-    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
     return value_type(*__parent_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
     __parent_->__remainder_ = __parent_->__n_;
     return *this;
@@ -214,6 +215,7 @@ class chunk_view<_View>::__inner_iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ++*__parent_->__current_;
     if (*__parent_->__current_ == ranges::end(__parent_->__base_))
       __parent_->__remainder_ = 0;
@@ -386,7 +388,7 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
-    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to increment past-the-end chunk_view iterator");
     __missing_ = ranges::advance(__current_, __n_, __end_);
     return *this;
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
new file mode 100644
index 0000000000000..03fc7dfb6a5de
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Construct a chunk_view with chunk size <= 0
+
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3};
+
+  // Test constructor when V models only input_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  // Test constructor when V models forward_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
new file mode 100644
index 0000000000000..af2b826aa8eba
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to advance chunk_view iterator out of range
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+
+  // Test advance past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    TEST_LIBCPP_ASSERT_FAILURE(it += 4, "Trying to advance chunk_view iterator out of range");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
new file mode 100644
index 0000000000000..cb1d92885e684
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to dereference past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test dereference iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*outer_it, "Trying to dereference past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*inner_it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+
+  // Test dereference iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(*it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
new file mode 100644
index 0000000000000..8160ee1bb3e14
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to increment past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test increment past-the-end iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++outer_it, "Trying to increment past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++inner_it, "Trying to increment past-the-end chunk_view iterator");
+  }
+
+  // Test increment past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(++it, "Trying to increment past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..a5d688abfa1b0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif

>From b664c0cb8f8ed9b46629fa49e4bdd846734615d4 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:26 +0800
Subject: [PATCH 04/22] Fix CI: adjust `#include` path in `test/libcxx` files

---
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp          | 2 +-
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index cb1d92885e684..0c96742147189 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 8160ee1bb3e14..451683bab71b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};

>From 4dc9b06c0dd515030226b9d27841352ef62549a3 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:58 +0800
Subject: [PATCH 05/22] Fix CI: add template deduction guide for
 `input_span<T>`

---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 3 +++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..0978a23c37244 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..4ff1af1d7cda3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 1e8906ad49a2406158161cc1b9d0e1570af2959d Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:53:21 +0800
Subject: [PATCH 06/22] Code Review: add `REQUIRES: std-at-least-c++23` in all
 the tests related to `views::chunk`.

The previous commit, 4dc9b06c0dd515030226b9d27841352ef62549a3, which introduce manual/explicit template deduction guide in `input_span<T>`, is always implicitly-provided under -std=c++23. So this commit also reverts the changes about `types.h` in the previous commit.
---
 .../range.adaptors/range.chunk/assert.constructor.pass.cpp     | 1 +
 .../range.chunk/range.chunk.iter/assert.advance.pass.cpp       | 1 +
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp         | 1 +
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp     | 1 +
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h   | 3 ---
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h      | 3 ---
 6 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
index 03fc7dfb6a5de..477f89be5f070 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
index af2b826aa8eba..9b91d94841b91 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index 0c96742147189..3640b04192bf4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 451683bab71b0..61c096f74158e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index 0978a23c37244..a5d688abfa1b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 4ff1af1d7cda3..09d16c9a48af0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 7e21388c70b38c32125e4d3eda9b3a4c66e1d3db Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 14:13:59 +0800
Subject: [PATCH 07/22] Fix CI: add template deduction guide for
 `input_span<T>` again.

Seems that even in C++23 the CI fails due to no implicit deduction guide, here we just add the explicit one and make the CI to basically run ok.
---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 4 ++++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..4852ec29204bb 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..7533e41713d72 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From f1ed40a1aed2a1f5ebd1e56efcd6ae2bbb25dc26 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:07:21 +0800
Subject: [PATCH 08/22] Code review: Update libcxx/test synopsis

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 .../ranges/range.adaptors/range.chunk/nodiscard.verify.cpp      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 5e557715f4b03..9264256fc6a52 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+// Test that functions are marked [[nodiscard]].
 
 #include <ranges>
 #include <utility>

>From 12bec119ba49cd6016055f5887f5e83efa25ee30 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:21:29 +0800
Subject: [PATCH 09/22] Code review: Fix synopsis.

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index ac17f97a0a292..b45e1f59f1394 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() const& requires copy_constructible<V>;
 //   constexpr V base() &&;
 
 #include <array>

>From 1e05c5a038dd4cee5a0049b8c7e92e59c8aabd82 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 21:13:46 +0800
Subject: [PATCH 10/22] Code review: sort and unique the tests

---
 .../no_unique_address.compile.pass.cpp        |  24 ++--
 .../range.chunk/nodiscard.verify.cpp          |  35 ++----
 .../range.chunk.iter/arithmetic.pass.cpp      | 108 ++++++++++++++++++
 .../range.chunk.iter/compare.pass.cpp         |  29 +++--
 .../range.chunk.iter/subscript.pass.cpp       |  42 +++++++
 5 files changed, 189 insertions(+), 49 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
index 14c618c505e70..5d32daf4c178a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -20,6 +20,8 @@
 #include "test_iterators.h"
 #include "test_range.h"
 
+// When V models only input_range
+
 struct input_view {
   cpp20_input_iterator<int*> begin() const;
   sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
@@ -28,16 +30,8 @@ template <>
 inline constexpr bool std::ranges::enable_view<input_view> = true;
 static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
 
-struct forward_view {
-  int* begin() const;
-  int* end() const;
-};
-template <>
-inline constexpr bool std::ranges::enable_view<forward_view> = true;
-static_assert(std::ranges::forward_range<forward_view>);
-
 using CV1 = std::ranges::chunk_view<input_view>;
-// Expected CV1 (with View == input) layout:
+// Expected CV1 (when V models only input_range) layout:
 // [[no_unique_address]] _View __base_                                         // size: 0
 // [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
 // [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
@@ -46,8 +40,18 @@ static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<
 static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
                                  sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
 
+// When V models forward_range
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
 using CV2 = std::ranges::chunk_view<forward_view>;
-// Expected CV2 (with View >= forward) layout:
+// Expected CV2 (when V models forward_range) layout:
 // [[no_unique_address]] _View __base_             // size: 0
 // [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
 static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 9264256fc6a52..4d0fb4f76857c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -19,15 +19,6 @@ void test() {
   char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
   auto view     = range | std::views::chunk(2);
 
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   view.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -43,18 +34,19 @@ void test() {
   std::as_const(view).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  view.end();
+  std::ranges::iter_move(view.begin());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(view).end();
+  std::ranges::iter_move(std::as_const(view).begin());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
+  view.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
+  std::as_const(view).end();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
+  std::ranges::iter_move(view.end());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
+  std::ranges::iter_move(std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   *view.begin();
@@ -71,16 +63,11 @@ void test() {
   (std::as_const(view).begin() == std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == std::as_const(view).end());
+  std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == std::as_const(view).end());
-
+  std::views::chunk(range, 3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(view.begin());
+  range | std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(std::as_const(view).begin());
+  std::views::reverse | std::views::chunk(3);
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..6baa2fce13695
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const inner_iterator& x)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr iterator operator+(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator+(difference_type n, const iterator& i)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator-(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+//       requires sized_sentinel_for<iterator_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(std::default_sentinel - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - std::default_sentinel == -4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t, const inner_iterator& x)`
+  {
+    assert(std::default_sentinel - (*input_chunked.begin()).begin() == 3);
+  }
+
+  // Test `friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t)`
+  {
+    assert((*input_chunked.begin()).begin() - std::default_sentinel == -3);
+  }
+
+  // Test `friend constexpr iterator operator+(const iterator& i, difference_type n)`
+  {
+    assert(chunked.begin() + 4 == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator+(difference_type n, const iterator& i)`
+  {
+    assert(4 + chunked.begin() == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator-(const iterator& i, difference_type n)`
+  {
+    assert(chunked.end() - 4 == chunked.begin());
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)`
+  {
+    assert(std::default_sentinel - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)`
+  {
+    assert(chunked.begin() - std::default_sentinel == -4);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
index d86726d958e6e..ea6731cccce85 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -11,14 +11,12 @@
 // <ranges>
 
 //   V models only input_range
-//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
-//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
-//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t);
 
 //   V models forward_range
-//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator==(const iterator& x, const iterator& y);
+//     friend constexpr bool operator==(const iterator& x, default_sentinel_t);
 //     friend constexpr bool operator<(const iterator& x, const iterator& y)
 //       requires random_access_range<Base>;
 //     friend constexpr bool operator>(const iterator& x, const iterator& y)
@@ -31,7 +29,6 @@
 //       requires random_access_range<Base> &&
 //                three_way_comparable<iterator_t<Base>>;
 
-#include <algorithm>
 #include <cassert>
 #include <compare>
 #include <iterator>
@@ -53,14 +50,11 @@ constexpr bool test() {
     assert(it == std::default_sentinel);
   }
 
-  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  // Test `friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t)`
   {
-    assert(input_chunked.end() - input_chunked.begin() == 4);
-  }
-
-  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
-  {
-    assert(input_chunked.begin() - input_chunked.end() == -4);
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::ranges::advance(it, 3);
+    assert(it == std::default_sentinel);
   }
 
   // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
@@ -69,6 +63,11 @@ constexpr bool test() {
     assert(chunked.end() == chunked.end());
   }
 
+  // Test `friend constexpr bool operator==(const iterator& x, default_sentinel)`
+  {
+    assert(chunked.end() == std::default_sentinel);
+  }
+
   // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() < chunked.end());
@@ -79,7 +78,7 @@ constexpr bool test() {
     assert(chunked.end() > chunked.begin());
   }
 
-  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  // Test `friend constexpr bool operator<=(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() <= chunked.begin());
     assert(chunked.begin() <= chunked.end());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
new file mode 100644
index 0000000000000..9b45fc6d779bf
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models forward_range
+//     constexpr value_type iterator::operator[](difference_type n) const
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+
+  // Test `constexpr value_type iterator::operator[](difference_type n) const`
+  {
+    assert(std::ranges::equal(chunked.begin()[1], std::vector{4, 5, 6}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 033a1c16289990a16b1f674f380f7a72f38f48ea Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 14:30:08 -0400
Subject: [PATCH 11/22] Fix release notes

---
 libcxx/docs/ReleaseNotes/22.rst    | 1 -
 libcxx/docs/ReleaseNotes/23.rst    | 1 +
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0637ba98976a2..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -55,7 +55,6 @@ Implemented Papers
 - 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>`__)
-- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e7eb42207db42..a98f42da70f00 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -52,6 +52,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::chunk`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 6abcac5e4ebca..8c92d89be999b 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""

>From 7fe126d5e7c88787f26228c64bcb5f89977c5b83 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:02:10 +0800
Subject: [PATCH 12/22] Resolve overload conflicts on `__div_ceil` in
 `ranges::chunk_view` and `ranges::stride_view`.

Both `ranges::chunk_view` and `ranges::stride_view` need a helper function called `__div_ceil` that solves some simple maths. According to the style of other libcxx internal helper functions (e.g. `__priority_tag`, `__small_buffer`), it seems that we can put `__div_ceil` into a new file `__utility/div_ceil.h`. However, most files in `__utility/` refer to a corresponding symbol in `<utility>` header, so that if someone feels this is not proper, I'll be glad to revert it.
---
 libcxx/include/CMakeLists.txt         |  1 +
 libcxx/include/__ranges/chunk_view.h  | 21 ++++++-----------
 libcxx/include/__ranges/stride_view.h | 20 +++++-----------
 libcxx/include/__utility/div_ceil.h   | 33 +++++++++++++++++++++++++++
 libcxx/include/module.modulemap.in    |  1 +
 5 files changed, 48 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/include/__utility/div_ceil.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 76b93c17c3312..0e9f19a81b1ab 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -935,6 +935,7 @@ set(files
   __utility/convert_to_integral.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
+  __utility/div_ceil.h
   __utility/element_count.h
   __utility/empty.h
   __utility/exception_guard.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 683b6bbaa2ca5..68d4408295e47 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -36,6 +36,7 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 
@@ -52,14 +53,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Integral>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
-  _Integral __r = __num / __denom;
-  if (__num % __denom)
-    ++__r;
-  return __r;
-}
-
 template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
@@ -98,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -153,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -321,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -510,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..7ea68b13330c3 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -46,15 +47,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Value>
-_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) {
-  _Value __r = __left / __right;
-  if (__left % __right) {
-    ++__r;
-  }
-  return __r;
-}
-
 template <input_range _View>
   requires view<_View>
 class stride_view : public view_interface<stride_view<_View>> {
@@ -121,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -340,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -ranges::__div_ceil(-__n, __x.__stride_);
+      return -__div_ceil(-__n, __x.__stride_);
     }
-    return ranges::__div_ceil(__n, __x.__stride_);
+    return __div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)
diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
new file mode 100644
index 0000000000000..595c590b9e3f6
--- /dev/null
+++ b/libcxx/include/__utility/div_ceil.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_DIV_CEIL_H
+#define _LIBCPP___UTILITY_DIV_CEIL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __div_ceil computes the ceiling of the division of two integers. It is mainly used by
+// range adaptors like chunk_view and stride_view.
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_DIV_CEIL_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 64238440b9dd7..ef5d15d063f69 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2200,6 +2200,7 @@ module std [system] {
     module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
+    module div_ceil                        { header "__utility/div_ceil.h" }
     module element_count                   { header "__utility/element_count.h" }
     module exception_guard                 { header "__utility/exception_guard.h" }
     module exchange                        { header "__utility/exchange.h" }

>From d27b355e67e17b0ed15d44a247fdba1c3b13ed49 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:22:38 +0800
Subject: [PATCH 13/22] Fix test error that `constexpr` is not supported in
 `-std=c++03`

---
 libcxx/include/__utility/div_ceil.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 595c590b9e3f6..4d2868465c141 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,6 +17,8 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if _LIBCPP_STD_VER >= 11
+
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
 
@@ -28,6 +30,8 @@ template <class _Integral>
   return __r;
 }
 
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___UTILITY_DIV_CEIL_H

>From a1a5918bb79d29cbe7be2736da7a72b83ee0afbb Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:38:37 +0800
Subject: [PATCH 14/22] Fix test error again that `constexpr` is not supported
 in `-std=c++11`

---
 libcxx/include/__utility/div_ceil.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 4d2868465c141..21e9bcb30d1a1 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,7 +17,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 11
+#if _LIBCPP_STD_VER >= 23
 
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
@@ -30,7 +30,7 @@ template <class _Integral>
   return __r;
 }
 
-#endif
+#endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_END_NAMESPACE_STD
 

>From c2e0e2e926c92ee6d001e060aa5660b681874dfd Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:45:41 +0800
Subject: [PATCH 15/22] Fix test error that `std::as_const` is included from
 `<utility>`.

---
 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp | 1 +
 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
index 3cccd7d1bebc6..29588a57fbcab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -22,6 +22,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
index 73eb0d967f6a2..2f37ac5f73524 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -23,6 +23,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"

>From 7d3cdd73f5da77a7499969fb7954312a19e26630 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 14:19:19 +0800
Subject: [PATCH 16/22] Avoid ADL in `std::__div_ceil`.

---
 libcxx/include/__ranges/chunk_view.h  | 12 ++++++------
 libcxx/include/__ranges/stride_view.h | 10 +++++-----
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 68d4408295e47..1107a9238b2bc 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -91,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -146,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return std::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -314,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -503,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return std::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 7ea68b13330c3..a8b485cbe33e5 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -113,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -332,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -__div_ceil(-__n, __x.__stride_);
+      return -std::__div_ceil(-__n, __x.__stride_);
     }
-    return __div_ceil(__n, __x.__stride_);
+    return std::__div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return std::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)

>From 116c6d85d8a9ad177b1df1975eea92479cb03664 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 16:39:58 +0800
Subject: [PATCH 17/22] [Important] Add missing concepts requirements.

---
 libcxx/include/__ranges/chunk_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1107a9238b2bc..47e6bd56415b6 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -400,7 +400,8 @@ class chunk_view<_View>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires bidirectional_range<_Base> {
     auto __tmp = *this;
     --*this;
     return __tmp;

>From 545bbdf10d24c3790790bfe669b8c756da719838 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Fri, 26 Jun 2026 22:35:12 +0800
Subject: [PATCH 18/22] Cover tests on `chunk_range`. (I'll continue to cover
 tests on `__iterator`, `__inner_iterator` and `__outer_iterator` at the
 weekend)

---
 libcxx/include/__ranges/chunk_view.h          |  3 +-
 .../range.adaptors/range.chunk/base.pass.cpp  | 12 +++-
 .../range.chunk/borrowing.compile.pass.cpp    | 27 +++++++
 .../range.adaptors/range.chunk/ctor.pass.cpp  | 54 ++++++++++++++
 .../range.adaptors/range.chunk/size.pass.cpp  | 72 +++++++++++++++++++
 5 files changed, 166 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 47e6bd56415b6..1798711eabd16 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -401,7 +401,8 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
-    requires bidirectional_range<_Base> {
+    requires bidirectional_range<_Base>
+  {
     auto __tmp = *this;
     --*this;
     return __tmp;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index b45e1f59f1394..4dca324a23695 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -26,7 +26,7 @@ constexpr bool test() {
   std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
       std::as_const(array) | std::views::chunk(4);
 
-  // Test `chunk_view.base()`
+  // Test `chunk_view.base() const&`
   {
     std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
     assert(std::addressof(base) == std::addressof(array));
@@ -35,6 +35,16 @@ constexpr bool test() {
     assert(std::addressof(const_base) == std::addressof(array));
   }
 
+  // Test `chunk_view.base() &&`
+  {
+    std::same_as<std::ranges::ref_view<std::array<int, 8>>> decltype(auto) moved = std::move(chunked).base();
+    assert(std::addressof(moved.base()) == std::addressof(array));
+
+    std::same_as<std::ranges::ref_view<const std::array<int, 8>>> decltype(auto) const_moved =
+        std::move(const_chunked).base();
+    assert(std::addressof(const_moved.base()) == std::addressof(array));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
new file mode 100644
index 0000000000000..1eb4a34f87217
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class V>
+//   inline constexpr bool enable_borrowed_range<chunk_view<V>> =
+//       forward_range<V> && enable_borrowed_range<V>;
+
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+// When V models only `input_range`.
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<input_span<int>>>);
+
+// When V models at least `forward_range`.
+static_assert(std::ranges::enable_borrowed_range<std::ranges::chunk_view<BorrowedView>>);
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
new file mode 100644
index 0000000000000..b60e040b807eb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.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++23
+
+// <ranges>
+
+//   constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n);
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_convertible.h"
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models only `input_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>, input_span<int>, std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(input_span(vector.data(), 8), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+                                    std::ranges::ref_view<std::vector<int>>,
+                                    std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
new file mode 100644
index 0000000000000..820774c3b750e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr auto size() requires sized_range<_View>;
+//   constexpr auto size() const requires sized_range<const _View>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+  // Test `chunk_view.size()` when V models only `input_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<input_span<int>>>);
+    static_assert(!std::ranges::sized_range<const std::ranges::chunk_view<input_span<int>>>);
+
+    auto chunked = input_span<int>(arr, 12) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when V models `forward_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+    static_assert(std::ranges::sized_range<const std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+    const auto& const_chunked = chunked;
+    assert(const_chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when the range is not fully divisible
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(5);
+    assert(chunked.size() == 3);
+  }
+
+  // Test `chunk_view.size()` when the range is empty
+  {
+    static_assert(std::ranges::sized_range<std::ranges::empty_view<int>>);
+    auto chunked = std::views::empty<int> | std::views::chunk(3);
+    assert(chunked.size() == 0);
+  }
+
+  // Test `chunk_view.size()` when chunk size is larger than the range
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(100);
+    assert(chunked.size() == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From e262f9e7497fb3ccf232a3c7265b8faa83f2fa20 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:36:04 +0800
Subject: [PATCH 19/22] Fix chunk_view ctor test for forward ranges.

---
 .../test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
index b60e040b807eb..c0d210aa45dc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
@@ -35,11 +35,11 @@ constexpr bool test() {
 
   // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
   {
-    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+    static_assert(!test_convertible<std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>>,
                                     std::ranges::ref_view<std::vector<int>>,
                                     std::ptrdiff_t>());
 
-    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked(std::ranges::ref_view(vector), 3);
     assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
   }
 

>From bf13c55de976c562120249f2fc7208ea1091fc95 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:39:36 +0800
Subject: [PATCH 20/22] Use consistent copy_constructible spelling in
 chunk_view

---
 libcxx/include/__ranges/chunk_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1798711eabd16..f3edc617de07a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -71,7 +71,7 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
-    requires std::copy_constructible<_View>
+    requires copy_constructible<_View>
   {
     return __base_;
   }

>From f457f267b72fe64a2f22c288da4b096ae0bcc83c Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:48:59 +0800
Subject: [PATCH 21/22] Remove unnecessary no_unique_address from chunk_view
 counts

---
 libcxx/include/__ranges/chunk_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f3edc617de07a..4478af951dbca 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -57,8 +57,8 @@ template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  range_difference_t<_View> __n_;
+  range_difference_t<_View> __remainder_;
   _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
 
   class __outer_iterator;
@@ -256,7 +256,7 @@ template <view _View>
   requires forward_range<_View>
 class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  range_difference_t<_View> __n_;
 
   template <bool _Const>
   class __iterator;

>From 39c371e116736dd37006c3cf48ed9cf9412b1222 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 23:25:01 +0800
Subject: [PATCH 22/22] Fix chunk_view inner iter_move return type (from T into
 T&&)

---
 libcxx/include/__ranges/chunk_view.h          |  2 +-
 .../range.chunk.inner.iter/iter_move.pass.cpp | 43 +++++++++++++++++++
 2 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 4478af951dbca..e9511f027160a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -238,7 +238,7 @@ class chunk_view<_View>::__inner_iterator {
     return -(__s - __i);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_View>
   iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
     return ranges::iter_move(*__i.__parent_->__current_);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp
new file mode 100644
index 0000000000000..5c7b620924c2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   friend constexpr range_rvalue_reference_t<V> iter_move(const inner_iterator& i)
+//     noexcept(noexcept(ranges::iter_move(i.parent_->current_.value())));
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+constexpr bool test() {
+  using InnerIterator =
+      std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<test_view<cpp20_input_iterator>>>>;
+
+  static_assert(std::ranges::input_range<test_view<cpp20_input_iterator>>);
+  static_assert(!std::ranges::forward_range<test_view<cpp20_input_iterator>>);
+
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())), int&&>);
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())),
+                             std::ranges::range_rvalue_reference_t<test_view<cpp20_input_iterator>>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file


From libcxx-commits at lists.llvm.org  Sun Jul 19 08:32:26 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 19 Jul 2026 08:32:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::adjacent_find() based on std::mismatch() (PR #210604)
Message-ID: <llvm.org/llvm/llvm-project/pull/210604@github.com>


https://github.com/mikekazakov created https://github.com/llvm/llvm-project/pull/210604

(to be rebased once std::mismatch is merged)
Part of #99938.

>From 650ebeba6d9e4867856e679d475e8f50edcdadde Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:03:38 +0100
Subject: [PATCH 01/15] Added a basic implementation of parallel 4-legged
 mismatch() based on __parallel_find()

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/libdispatch.h  |   7 +-
 libcxx/include/__pstl/backends/serial.h       |  20 +++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  88 +++++++++++
 .../mismatch/pstl.mismatch.pass.cpp           | 139 ++++++++++++++++++
 7 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__pstl/cpu_algos/mismatch.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..6b5dc87fbe76d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/mismatch.h  
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a84a768cad570..1d64c6f47630f 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -317,6 +317,14 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index 33fd5de66fbb1..710506585ca22 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -33,6 +33,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -237,7 +238,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
       auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
       auto __index           = __chunk == 0 ? 0
                                             : (__chunk * __partitions.__chunk_size_) +
-                                        (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
+                                                  (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
       if (__this_chunk_size != 1) {
         std::__construct_at(
             __values.get() + __chunk,
@@ -364,6 +365,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..d90022ee37145 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/mismatch.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    return std::mismatch(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::forward<_Predicate>(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..93935d22b9442 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
new file mode 100644
index 0000000000000..11ffdc332d633
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_MISMATCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
+
+#include <__algorithm/mismatch.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_mismatch {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Look for a mismatch only in the prefix of the two ranges.
+      auto __n = std::min(__last1 - __first1, __last2 - __first2);
+      // Find a position in the first range where the predicate is false against the corresponding position in the
+      // second range.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __first1 + __n,
+          [&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
+            // Run the sequential mismatch algorithm on these ranges:
+            //   [__brick_first1, __brick_last1) and
+            //   [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
+            auto __brick_first2 = __first2 + (__brick_first1 - __first1);
+            return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
+                .first;
+          },
+          less<>{},
+          true);
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      auto __idx = *__res - __first1;
+      return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      // Unsequenced execution is also implicitly covered by the sequential implementation.
+      return std::mismatch(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
new file mode 100644
index 0000000000000..9cbadda428ad9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 1};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(42);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From e96f0aa34d1b212b123d4c9917928e17d26270f3 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:05:01 +0100
Subject: [PATCH 02/15] Added a definition of a parallel 4-legged
 std::mismatch()

---
 libcxx/include/__algorithm/pstl.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2bf9b4276baab..ab35b1a3e537b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -138,6 +138,29 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,

>From 472174e7343fc86a77bf314c7d088331dab6bae5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:41:51 +0100
Subject: [PATCH 03/15] Added a default implementation of __mismatch_3leg based
 on __mismatch

---
 libcxx/include/__algorithm/pstl.h             | 18 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++++
 libcxx/include/__pstl/backends/default.h      | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../mismatch/pstl.mismatch.pass.cpp           | 20 +++++++++++
 5 files changed, 84 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index ab35b1a3e537b..01c61fb00f4d5 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -161,6 +161,24 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 1d64c6f47630f..d800e60916efc 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -325,6 +325,13 @@ struct __mismatch;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch_3leg;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5c78d4efa412b..05b328beae80f 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -63,6 +63,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// mismatch family
+// ---------------
+// - mismatch_3leg
+//
 // for_each family
 // ---------------
 // - for_each_n
@@ -211,6 +215,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// mismatch family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+  optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _Comp __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Forward to the 4-legged version of mismatch.
+      using _Mismatch           = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      _ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
+      return _Mismatch()(
+          __policy,
+          std::move(__first1),
+          std::move(__last1),
+          std::move(__first2),
+          std::move(__last2),
+          std::move(__comp));
+    } else {
+      // Currently only random access iterators are supported for parallel mismatch_3leg.
+      return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..752aa35643e2b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2384,6 +2384,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module mismatch {
+        header "__pstl/cpu_algos/mismatch.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 9cbadda428ad9..0cb5c7632eed8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -10,6 +10,14 @@
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2);
+//
 // template <class ExecutionPolicy,
 //           class ForwardIterator1,
 //           class ForwardIterator2>
@@ -61,6 +69,8 @@ struct Test {
       // empty ranges
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
              std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
     }
@@ -68,18 +78,24 @@ struct Test {
       // single element only
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range without mismatch
       std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range with mismatch
       std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
     }
@@ -102,10 +118,14 @@ struct Test {
       rhs = lhs;
       runway_sample(lhs.size(), [&](size_t i) {
         lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
                std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         lhs[i] = i;
       });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }

>From 4aab10d6cc54f3abd475bd3aae03ac7d4103eb45 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:20:42 +0100
Subject: [PATCH 04/15] Added predicated versions

---
 libcxx/include/__algorithm/pstl.h             |  49 +++++
 .../mismatch/pstl.mismatch.pass.cpp           |   2 +
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 178 ++++++++++++++++++
 3 files changed, 229 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 01c61fb00f4d5..bc48fd9f48d7f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
+#  include <__utility/pair.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -138,6 +139,31 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
@@ -161,6 +187,29 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 0cb5c7632eed8..8584f2779927f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -42,6 +42,8 @@
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
+static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
new file mode 100644
index 0000000000000..39e952cbc38f6
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     BinaryPredicate pred);
+//
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2,
+//                                                     BinaryPredicate pred);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+struct Pred {
+  bool operator()(int a, int b) const { return a * 2 == b; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      assert(std::mismatch(
+                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {1};
+      std::array<int, 1> rhs = {2};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 2};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(84);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From 2da95a5c532de58960651a18c9a93c9435577a89 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:39:47 +0100
Subject: [PATCH 05/15] Added tests against custom types

---
 libcxx/include/__pstl/backends/default.h      |  1 +
 .../mismatch/pstl.mismatch.pass.cpp           | 53 ++++++++++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 56 ++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 05b328beae80f..f4f73e81ed90b 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -16,6 +16,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 8584f2779927f..5e46831539c30 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -63,6 +63,30 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
+
 template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
@@ -150,6 +174,29 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -157,5 +204,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 39e952cbc38f6..9c0dfc6c0c127 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -67,8 +67,31 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
 struct Pred {
-  bool operator()(int a, int b) const { return a * 2 == b; }
+  bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
+  bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
 };
 
 template <class Iter1, class Iter2>
@@ -167,6 +190,31 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -174,5 +222,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }

>From 2fb2bb1a154212dc1cbf3a1ea9e3e1cf5a00db48 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:52:50 +0100
Subject: [PATCH 06/15] Added a nodiscard test

---
 libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 049c8dc88c2da..c538d7841dce6 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -58,4 +58,12 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }

>From 8c35d0cac6fc6a3daf494de08ac0a2fd365d50f1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:57:47 +0100
Subject: [PATCH 07/15] Added an iterator requirement test

---
 .../pstl.iterator-requirements.verify.cpp        | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index fbe3460b67f17..4526cad0615b0 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, pred);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}

>From a8b984852860283d06ab06bbdf05a28f01965326 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:00:09 +0100
Subject: [PATCH 08/15] Added an include of <__algorithm/min.h>

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 11ffdc332d633..8361731e78cfc 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,8 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__algorithm/mismatch.h>
 #include <__config>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/min.h>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>

>From 3845b7e0e242d455f0fcc68a814ed625544c3d8c Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:05:29 +0100
Subject: [PATCH 09/15] Added an exception handling test

---
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  4 +--
 .../pstl.exception_handling.pass.cpp          | 25 +++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 8361731e78cfc..597a47fb185c8 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,9 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__config>
-#include <__algorithm/mismatch.h>
 #include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__config>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 8af2a0a15fd5c..820a5d92c19e0 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -248,6 +248,31 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // mismatch(first1, last1, first2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
+        });
+
+        // mismatch(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // mismatch(first1, last1, first2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
+        });
+
+        // mismatch(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From fbc073f5742c81ae78d12058ecf4618213afb6f0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 09:43:45 +0100
Subject: [PATCH 10/15] Changed the variable name to avoid shadowing

---
 .../alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 12 ++++++------
 .../mismatch/pstl.mismatch_pred.pass.cpp             | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 5e46831539c30..941fe07b58327 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -67,22 +67,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 9c0dfc6c0c127..a4b15a56bd071 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -71,22 +71,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Pred {

>From baaa82257b3d2f94d4bf9d02f6761e7cf3ee9df6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 20:27:14 +0100
Subject: [PATCH 11/15] Use raw C array in the tests

---
 .../mismatch/pstl.mismatch.pass.cpp           | 153 +++++++-------
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 192 +++++++++++-------
 2 files changed, 198 insertions(+), 147 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 941fe07b58327..17edf34ee7832 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -28,7 +28,6 @@
 //                                                     ForwardIterator2 last2);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -93,82 +92,98 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(
+          std::mismatch(
+              policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
+          std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 1};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 1};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(
+            std::mismatch(
+                policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+            std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(42);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 42);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(
+                   policy,
+                   Iter1(std::begin(lhs)),
+                   Iter1(std::begin(lhs) + i),
+                   Iter2(std::begin(rhs)),
+                   Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i)) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -179,20 +194,22 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index a4b15a56bd071..18c4fe1abad0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -32,7 +32,6 @@
 //                                                     BinaryPredicate pred);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -100,91 +99,120 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(
-                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::begin(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::begin(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {1};
-      std::array<int, 1> rhs = {2};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {1};
+      int rhs[1] = {2};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 2};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 2};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(84);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 84);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::begin(lhs) + i),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -195,22 +223,28 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };

>From 212e25406377a6a5da7fc7679015000a4086e4a1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:08:55 +0100
Subject: [PATCH 12/15] Fixed typos in the comment

---
 .../algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 2 +-
 .../alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 17edf34ee7832..ed59fa9eca669 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -46,7 +46,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 18c4fe1abad0c..338503d784a31 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -50,7 +50,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;

>From b80675016128dbeb1db5a3084edee9bb6ab89fea Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:43:20 +0100
Subject: [PATCH 13/15] Added comments explaining what the parameters mean

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 597a47fb185c8..3d702b03d3afe 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -63,8 +63,9 @@ struct __cpu_parallel_mismatch {
             return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
                 .first;
           },
-          less<>{},
-          true);
+          less<>{}, // `less` here means the lowest index among the mismatches
+          true      // `true` here means we want the first mismatch, not the last
+      );
       if (!__res) {
         return std::nullopt; // Failed to run the algorithm, propagate the error.
       }

>From 12ac822e8571b5d944ffea6d4f62396a954f4672 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:50:51 +0100
Subject: [PATCH 14/15] Added tests of the return types

---
 .../mismatch/pstl.mismatch.pass.cpp              | 10 ++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp         | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index ed59fa9eca669..cc7ef6feb2356 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -90,6 +90,16 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1]       = {0};
+      int rhs[1]       = {0};
+      auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
+      auto res_4legged = std::mismatch(
+          policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 338503d784a31..d80da0cf0b542 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -97,6 +97,22 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      auto res_3legged =
+          std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
+      auto res_4legged = std::mismatch(
+          policy,
+          Iter1(std::begin(lhs)),
+          Iter1(std::begin(lhs)),
+          Iter2(std::begin(rhs)),
+          Iter2(std::begin(rhs)),
+          Pred{});
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};

>From 01513beffed6c7812b59d62ab4cdae683c9e0c10 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 19 Jul 2026 16:27:43 +0100
Subject: [PATCH 15/15] Added an implementation of adjacent_find() based on
 mismatch()

---
 libcxx/include/__algorithm/pstl.h             |  25 +++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  45 +++++
 .../pstl.iterator-requirements.verify.cpp     |   6 +
 .../algorithms/pstl.nodiscard.verify.cpp      |   4 +
 .../pstl.adjacent_find.pass.cpp               | 164 +++++++++++++++++
 .../pstl.adjacent_find_pred.pass.cpp          | 173 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  12 ++
 8 files changed, 436 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index bc48fd9f48d7f..0447691c3b86b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -228,6 +228,31 @@ template <class _ExecutionPolicy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+adjacent_find(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "adjacent_find requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__adjacent_find, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), equal_to{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator adjacent_find(
+    _ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __predicate) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "adjacent_find requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__adjacent_find, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__predicate));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index d800e60916efc..1981883d2292e 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -332,6 +332,13 @@ struct __mismatch_3leg;
 // operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_find;
+// template <class _Policy, class _ForwardIterator, class _BinaryPredicate>
+// optional<_ForwardIterator>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last,
+//                                _BinaryPredicate __predicate) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index f4f73e81ed90b..c37d57858f46d 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 #define _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 
+#include <__algorithm/adjacent_find.h>
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
@@ -24,6 +25,7 @@
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
+#include <__iterator/prev.h>
 #include <__iterator/reverse_iterator.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
@@ -66,6 +68,7 @@ namespace __pstl {
 //
 // mismatch family
 // ---------------
+// - adjacent_find
 // - mismatch_3leg
 //
 // for_each family
@@ -248,6 +251,48 @@ struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_find<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator, class _BinaryPredicate>
+  optional<_ForwardIterator>
+  operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __predicate)
+      const noexcept {
+    if constexpr (__has_bidirectional_iterator_category<_ForwardIterator>::value) {
+      using _Mismatch = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      if (__first == __last) {
+        return __last; // Empty range, return __last.
+      }
+      _ForwardIterator __first2 = std::next(__first);
+      if (__first2 == __last) {
+        return __last; // Single element range, no adjacent elements, return __last.
+      }
+      _ForwardIterator __last2 = std::prev(__last);
+      // Find the first match within two overlapping ranges, expressed as a double negation of the predicate:
+      //   [first, __last - 1)
+      //   [first + 1, __last)
+      auto __res = _Mismatch()(
+          __policy,
+          std::move(__first),
+          std::move(__last2),
+          std::move(__first2),
+          __last,
+          [&](__iterator_reference<_ForwardIterator> __lhs, __iterator_reference<_ForwardIterator> __rhs) {
+            return !__predicate(__lhs, __rhs);
+          });
+      if (!__res) {
+        return nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      if (__res->second == __last) {
+        return __last; // No adjacent elements found, return __last.
+      }
+      return __res->first; // Return the first iterator of the pair of mismatched elements.
+    } else {
+      // Currently anything outside bidirectional iterators has to be processed serially
+      return std::adjacent_find(std::move(__first), std::move(__last), std::move(__predicate));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 4526cad0615b0..741bef52c1638 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -151,6 +151,12 @@ void f(non_forward_iterator non_fwd,
         pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
   }
 
+  {
+    (void)std::adjacent_find(pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_find}}
+    (void)std::adjacent_find(
+        pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: adjacent_find}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index c538d7841dce6..e00dccdafd459 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -66,4 +66,8 @@ void test() {
   std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_find(std::execution::par, std::begin(a), std::end(a));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_find(std::execution::par, std::begin(a), std::end(a), pred2);
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
new file mode 100644
index 0000000000000..ff0b5c05bd364
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
@@ -0,0 +1,164 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template<class ExecutionPolicy, class ForwardIterator>
+// ForwardIterator adjacent_find(ExecutionPolicy&& exec,
+//                               ForwardIterator first, ForwardIterator last);
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// TODO: switch to the shared implementation once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+// The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
+struct X {
+  X() = delete;
+  explicit X(int i) : i_(i) {}
+  X(const X&)            = delete;
+  X(X&&)                 = delete;
+  X& operator=(const X&) = delete;
+  X& operator=(X&&)      = delete;
+  bool operator==(const X& other) const { return i_ == other.i_; }
+
+private:
+  int i_;
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]  = {42};
+      auto res = std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)));
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    {
+      int a[] = {42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {42, 42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a) + 1)) == Iter(std::begin(a) + 1));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 0, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {0, 1, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 0, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {0, 1, 0, 1};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 1, 1};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 3), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 2, 7, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 0);
+      runway_sample(std::size(a), [&](std::size_t i) {
+        if (i == 0)
+          return; // skip the first element to avoid out-of-bounds access
+        a[i] = a[i - 1];
+        assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + i - 1));
+        a[i] = i;
+      });
+    }
+  }
+};
+
+template <class Iter>
+struct TestX {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    X a[] = {X(0), X(1), X(1), X(2)};
+    assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a))) == Iter(std::end(a)));
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<const X*>{}, TestIteratorWithPolicies<TestX>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
new file mode 100644
index 0000000000000..795632b0db4db
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
@@ -0,0 +1,173 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template<class ExecutionPolicy, class ForwardIterator, class BinaryPredicate>
+// ForwardIterator adjacent_find(ExecutionPolicy&& exec,
+//                               ForwardIterator first, ForwardIterator last,
+//                               BinaryPredicate pred);
+
+#include <algorithm>
+#include <cassert>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// TODO: switch to the shared implementation once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+// The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
+struct X {
+  X() = delete;
+  explicit X(int i) : i_(i) {}
+  X(const X&)            = delete;
+  X(X&&)                 = delete;
+  X& operator=(const X&) = delete;
+  X& operator=(X&&)      = delete;
+  int get_value() const { return i_; }
+
+private:
+  int i_;
+};
+
+struct Pred {
+  bool operator()(const X& a, const X& b) const {
+    return static_cast<long long>(b.get_value()) - static_cast<long long>(a.get_value()) == 42;
+  }
+  bool operator()(int a, int b) const { return static_cast<long long>(b) - static_cast<long long>(a) == 42; }
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]  = {42};
+      auto res = std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{});
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    {
+      int a[] = {42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {42, 84};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a) + 1), Pred{}) ==
+             Iter(std::begin(a) + 1));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 42, 84};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {0, 41, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 0, 42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {0, 41, 0, 41};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 43, 85};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[] = {0, 1, 2, 44, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 3), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 2, 45, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min() + 42,
+                 std::numeric_limits<int>::max() - 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max() - 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min() + 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 0);
+      runway_sample(std::size(a), [&](std::size_t i) {
+        if (i == 0)
+          return; // skip the first element to avoid out-of-bounds access
+        a[i] = a[i - 1] + 42;
+        assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) ==
+               Iter(std::begin(a) + i - 1));
+        a[i] = i;
+      });
+    }
+  }
+};
+
+template <class Iter>
+struct TestX {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    X a[] = {X(0), X(40), X(82), X(100)};
+    assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<const X*>{}, TestIteratorWithPolicies<TestX>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 820a5d92c19e0..c03d27cea5df1 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -273,6 +273,18 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto pred = maybe_throw(tokens[5], [](int a, int b) -> bool { return a == b; });
+
+        // adjacent_find(first, last)
+        assert_non_throwing([=, &policy] { (void)std::adjacent_find(policy, std::move(first1), std::move(last1)); });
+
+        // adjacent_find(first, last, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_find(policy, std::move(first1), std::move(last1), pred);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {


From libcxx-commits at lists.llvm.org  Sun Jul 19 08:52:01 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 08:52:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test][NFC] Removed unsupported
 compilers from tests (PR #210413)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210413@github.com>
Message-ID: <6a5cf2a1.1e1ac41d.3969c3.a78a@mx.google.com>


================
@@ -10,8 +10,8 @@
 
 // TODO(LLVM25): remove these guards. Clang <= 22 mis-evaluates
----------------
Zingam wrote:

Done

https://github.com/llvm/llvm-project/pull/210413

From libcxx-commits at lists.llvm.org  Sun Jul 19 08:56:34 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sun, 19 Jul 2026 08:56:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5cf3b2.5a0412d2.3daf32.7e0a@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210550

>From 23293789342393617472800afc72656011d5334f Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:13:24 +0200
Subject: [PATCH 1/6] apply: changes from proposal and mark as Complete

---
 libcxx/docs/Status/Cxx23Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 91c4dc462c0c9..0e13c6dc62bfe 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..bbdd30e56730f 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
     }
   }
 

>From cbed72094e96ce8a7756aa588be4dc25f608635b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:33:56 +0200
Subject: [PATCH 2/6] clang-format

---
 libcxx/include/__iterator/distance.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index bbdd30e56730f..e0a2590253c71 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,7 +86,8 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>>
+  operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {

>From a265367976e6651929998984e3284210eab11585 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:58:32 +0200
Subject: [PATCH 3/6] update: comments in tests

---
 .../range.iter.ops.distance/iterator_sentinel.pass.cpp          | 2 +-
 .../range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index c34d59e405a9e..1888f606465b2 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(I&& first, S last); // TODO: update when LWG3664 is resolved
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <array>
 #include <cassert>
diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
index 164101c2bf5a8..bac0ffba1e7f1 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(const I& first, S last);
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <cassert>
 #include <iterator>

>From 9452d4068636d3a8c8d31b5602b9446ffd7f7fe4 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 19 Jul 2026 00:11:01 +0200
Subject: [PATCH 4/6] add version number

---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0e13c6dc62bfe..617c89508b7a3 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","24","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""

>From c8e9bf8199acfc396e09077e01eb52c240fa2636 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 19 Jul 2026 13:01:31 +0200
Subject: [PATCH 5/6] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 617c89508b7a3..4b80d5ebf3623 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","24","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","14","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""

>From 934ee224bb2c04a091a0b64c48f1f2c950e597e2 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 19 Jul 2026 17:53:08 +0200
Subject: [PATCH 6/6] port lwg3664 tests to iterator_sentinel.pass.cpp

---
 .../iterator_sentinel.pass.cpp                | 39 ++++++++++
 .../range.iter.ops.distance/lwg3664.pass.cpp  | 73 -------------------
 2 files changed, 39 insertions(+), 73 deletions(-)
 delete mode 100644 libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index 1888f606465b2..30507c59495ea 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -183,6 +183,22 @@ constexpr void test_stride_counting() {
   assert(std::ranges::distance(view.begin(), view.end()) == n);
 }
 
+template <class It>
+struct EvilSentinel {
+  It p_;
+  friend constexpr bool operator==(EvilSentinel s, It p) { return s.p_ == p; }
+  friend constexpr auto operator-(EvilSentinel s, It p) { return s.p_ - p; }
+  friend constexpr auto operator-(It p, EvilSentinel s) { return p - s.p_; }
+  friend constexpr void operator-(EvilSentinel s, int (&)[3])        = delete;
+  friend constexpr void operator-(EvilSentinel s, int (&&)[3])       = delete;
+  friend constexpr void operator-(EvilSentinel s, const int (&)[3])  = delete;
+  friend constexpr void operator-(EvilSentinel s, const int (&&)[3]) = delete;
+};
+static_assert(std::sized_sentinel_for<EvilSentinel<int*>, int*>);
+static_assert(!std::sized_sentinel_for<EvilSentinel<int*>, const int*>);
+static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, int*>);
+static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, const int*>);
+
 constexpr bool test() {
   {
     int a[] = {1, 2, 3};
@@ -271,6 +287,29 @@ constexpr bool test() {
   if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
     test_deque();
 
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, a + 3) == 3);
+    assert(std::ranges::distance(a, a) == 0);
+    assert(std::ranges::distance(a + 3, a) == -3);
+  }
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
+  }
+  {
+    const int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
deleted file mode 100644
index bac0ffba1e7f1..0000000000000
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// template<class I, sentinel_for<I> S>
-//   requires (!sized_sentinel_for<S, I>)
-//     constexpr iter_difference_t<I> ranges::distance(I first, S last);
-//
-// template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
-
-#include <cassert>
-#include <iterator>
-#include <type_traits>
-#include <utility>
-
-#include "test_iterators.h"
-
-template<class It>
-struct EvilSentinel {
-  It p_;
-  friend constexpr bool operator==(EvilSentinel s, It p) { return s.p_ == p; }
-  friend constexpr auto operator-(EvilSentinel s, It p) { return s.p_ - p; }
-  friend constexpr auto operator-(It p, EvilSentinel s) { return p - s.p_; }
-  friend constexpr void operator-(EvilSentinel s, int(&)[3]) = delete;
-  friend constexpr void operator-(EvilSentinel s, int(&&)[3]) = delete;
-  friend constexpr void operator-(EvilSentinel s, const int(&)[3]) = delete;
-  friend constexpr void operator-(EvilSentinel s, const int(&&)[3]) = delete;
-};
-static_assert( std::sized_sentinel_for<EvilSentinel<int*>, int*>);
-static_assert(!std::sized_sentinel_for<EvilSentinel<int*>, const int*>);
-static_assert( std::sized_sentinel_for<EvilSentinel<const int*>, int*>);
-static_assert( std::sized_sentinel_for<EvilSentinel<const int*>, const int*>);
-
-constexpr bool test() {
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, a + 3) == 3);
-    assert(std::ranges::distance(a, a) == 0);
-    assert(std::ranges::distance(a + 3, a) == -3);
-  }
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a+3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
-    assert(std::ranges::distance(a+3, EvilSentinel<int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a+3}) == 3);
-  }
-  {
-    const int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a+3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
-    assert(std::ranges::distance(a+3, EvilSentinel<const int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a+3}) == 3);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int(&)[3], EvilSentinel<int*>>);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int(&&)[3], EvilSentinel<int*>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}


From libcxx-commits at lists.llvm.org  Sun Jul 19 09:13:54 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 09:13:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2442R1
	`std::views::chunk` (PR #171234)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171234@github.com>
Message-ID: <6a5cf7c2.7d702ed2.13d102.b2e0@mx.google.com>


https://github.com/shyeyian updated https://github.com/llvm/llvm-project/pull/171234

>From f6554ea2a0e1d9571cf82fc7b2f3d1115c466474 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at petalmail.com>
Date: Wed, 24 Dec 2025 22:05:29 +0800
Subject: [PATCH 01/24] Implement `views::chunk`.

This commit squashes previous commits.
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/22.rst               |   1 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/chunk_view.h          | 562 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/ranges                         |  12 +
 libcxx/include/version                        |   2 +-
 libcxx/modules/std/ranges.inc                 |  11 +-
 .../no_unique_address.compile.pass.cpp        |  53 ++
 .../range.chunk/nodiscard.verify.cpp          |  86 +++
 .../ranges.version.compile.pass.cpp           |  32 +-
 .../version.version.compile.pass.cpp          |  32 +-
 .../range.chunk/adaptor.pass.cpp              |  75 +++
 .../range.adaptors/range.chunk/base.pass.cpp  |  46 ++
 .../range.adaptors/range.chunk/begin.pass.cpp |  67 +++
 .../range.chunk/ctad.compile.pass.cpp         |  55 ++
 .../range.adaptors/range.chunk/end.pass.cpp   |  65 ++
 .../range.chunk/general.pass.cpp              |  48 ++
 .../range.chunk.iter/compare.pass.cpp         | 109 ++++
 .../range.chunk.iter/decrement.pass.cpp       |  60 ++
 .../range.chunk.iter/deref.pass.cpp           |  77 +++
 .../range.chunk.iter/increment.pass.cpp       |  95 +++
 .../ranges/range.adaptors/range.chunk/types.h |  50 ++
 .../generate_feature_test_macro_components.py |   1 -
 25 files changed, 1494 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/chunk_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..8e57305f32ad1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -370,7 +370,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_rvalue``                             ``202207L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_chunk``                                 *unimplemented*
+    ``__cpp_lib_ranges_chunk``                                 ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                              ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0d1a1fbc00f2c..d2a2d1b712689 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,6 +54,7 @@ Implemented Papers
 - 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>`__)
+- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e4c6cb0f51065..8c8ab211e5bf4 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__","Only ``ranges::iota`` is implemented."
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__",""
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index eb423ae923b4f..2426da6848b51 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -707,6 +707,7 @@ set(files
   __ranges/all.h
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
+  __ranges/chunk_view.h
   __ranges/common_view.h
   __ranges/concepts.h
   __ranges/container_compatible_range.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
new file mode 100644
index 0000000000000..f19d2c9ede8a7
--- /dev/null
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_CHUNK_VIEW_H
+#define _LIBCPP___RANGES_CHUNK_VIEW_H
+
+#include <__algorithm/ranges_min.h>
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/take_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
+
+  class __outer_iterator;
+  class __inner_iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n), __remainder_(0) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires std::copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator begin() {
+    __current_.__emplace(ranges::begin(__base_));
+    __remainder_ = __n_;
+    return __outer_iterator(*this);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept {
+    return std::default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(chunk_view& __parent)
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  class value_type;
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator(__outer_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    return value_type(*__parent_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
+    __parent_->__remainder_ = __parent_->__n_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __outer_iterator& __i, default_sentinel_t) {
+    return *__i.__parent_->__current_ == ranges::end(__i.__parent_->__base_) && __i.__parent_->__remainder_ != 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __outer_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
+    if (__dist < __i.__parent_->__remainder_)
+      return __dist == 0 ? 0 : 1;
+    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __outer_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator::value_type : public view_interface<value_type> {
+  friend __outer_iterator;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(chunk_view& __parent) : __parent_(std::addressof(__parent)) {}
+
+public:
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator begin() const noexcept {
+    return __inner_iterator(*__parent_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return std::__to_unsigned_like(
+        ranges::min(__parent_->__remainder_, ranges::end(__parent_->__base_) - *__parent_->__current_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__inner_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(chunk_view& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+  using value_type       = range_value_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator(__inner_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator& operator=(__inner_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_View> base() const& { return *__parent_->__current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_reference_t<_View> operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
+    return **__parent_->__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    ++*__parent_->__current_;
+    if (*__parent_->__current_ == ranges::end(__parent_->__base_))
+      __parent_->__remainder_ = 0;
+    else
+      --__parent_->__remainder_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __inner_iterator& __i, default_sentinel_t) {
+    return __i.__parent_->__remainder_ == 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __inner_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return ranges::min(__i.__parent_->__remainder_, ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __inner_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
+    return ranges::iter_move(*__i.__parent_->__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void
+  iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) noexcept(
+      noexcept((ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_))))
+    requires indirectly_swappable<iterator_t<_View>>
+  {
+    return ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_);
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+class chunk_view<_View> : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+
+  template <bool _Const>
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires forward_range<const _View>
+  {
+    return __iterator<true>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    if constexpr (common_range<_View> && sized_range<_View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<false>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<_View> && !bidirectional_range<_View>)
+      return __iterator<false>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires forward_range<const _View>
+  {
+    if constexpr (common_range<const _View> && sized_range<const _View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<true>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<const _View> && !bidirectional_range<const _View>)
+      return __iterator<true>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+template <bool _Const>
+class chunk_view<_View>::__iterator {
+  friend chunk_view;
+
+  using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, chunk_view>;
+  using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
+
+  iterator_t<_Base> __current_         = iterator_t<_Base>();
+  sentinel_t<_Base> __end_             = sentinel_t<_Base>();
+  range_difference_t<_Base> __n_       = 0;
+  range_difference_t<_Base> __missing_ = 0;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      _Parent* __parent, iterator_t<_Base> __current, range_difference_t<_Base> __missing = 0)
+      : __current_(__current), __end_(ranges::end(__parent->__base_)), __n_(__parent->__n_), __missing_(__missing) {}
+
+  [[nodiscard]] static consteval auto __get_iterator_concept() {
+    if constexpr (random_access_range<_Base>)
+      return random_access_iterator_tag{};
+    else if constexpr (bidirectional_range<_Base>)
+      return bidirectional_iterator_tag{};
+    else
+      return forward_iterator_tag{};
+  }
+
+public:
+  using iterator_category = input_iterator_tag;
+  using iterator_concept  = decltype(__iterator::__get_iterator_concept());
+  using value_type        = decltype(views::take(subrange(__current_, __end_), __n_));
+  using difference_type   = range_difference_t<_Base>;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> &&
+                 convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+      : __current_(std::move(__i.__current_)),
+        __end_(std::move(__i.__end_)),
+        __n_(__i.__n_),
+        __missing_(__i.__missing_) {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() const { return __current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to dereference past-the-end chunk_view iterator");
+    return views::take(subrange(__current_, __end_), __n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator[](difference_type __pos) const
+    requires random_access_range<_Base>
+  {
+    return *(*this + __pos);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    __missing_ = ranges::advance(__current_, __n_, __end_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires bidirectional_range<_Base>
+  {
+    ranges::advance(__current_, __missing_ - __n_);
+    __missing_ = 0;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    if (__x > 0) {
+      _LIBCPP_ASSERT_PEDANTIC(ranges::distance(__current_, __end_) > __n_ * (__x - 1),
+                              "Trying to advance chunk_view iterator out of range");
+      ranges::advance(__current_, __n_ * (__x - 1));
+      __missing_ = ranges::advance(__current_, __n_, __end_);
+    } else if (__x < 0) {
+      ranges::advance(__current_, __n_ * __x + __missing_);
+      __missing_ = 0;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    return *this += -__x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
+    return __x.__current_ == __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, default_sentinel_t) {
+    return __x.__current_ == __x.__end_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __x.__current_ < __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __y < __x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__y < __x);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__x < __y);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(difference_type __pos, const __iterator& __i)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator-(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r -= __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, const __iterator& __j)
+    requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+  {
+    return (__i.__current_ - __j.__current_ + __i.__missing_ - __j.__missing_) / __i.__n_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <class _Range>
+chunk_view(_Range&&, range_difference_t<_Range>) -> chunk_view<views::all_t<_Range>>;
+
+template <class _View>
+inline constexpr bool enable_borrowed_range<chunk_view<_View>> = forward_range<_View> && enable_borrowed_range<_View>;
+
+namespace views {
+namespace __chunk {
+struct __fn {
+  template <viewable_range _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range, range_difference_t<_Range> __n) noexcept(
+      noexcept(/*-----*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))))
+      -> decltype(/*--*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))) {
+    return /*---------*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n));
+  }
+
+  template <class _DifferenceType>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_DifferenceType __n) noexcept(is_nothrow_constructible_v<decay_t<_DifferenceType>, _DifferenceType>) {
+    return __pipeable(std::__bind_back(__fn{}, std::forward<_DifferenceType>(__n)));
+  }
+};
+
+} // namespace __chunk
+
+inline namespace __cpo {
+inline constexpr auto chunk = __chunk::__fn{};
+
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_CHUNK_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4434a5446e35b..cb03eb8d14067 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1863,6 +1863,10 @@ module std [system] {
       header "__ranges/chunk_by_view.h"
       export std.functional.bind_back
     }
+    module chunk_view {
+      header "__ranges/chunk_view.h"
+      export std.functional.bind_back
+    }
     module common_view                    { header "__ranges/common_view.h" }
     module concepts                       { header "__ranges/concepts.h" }
     module container_compatible_range     { header "__ranges/container_compatible_range.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 9f725b12ac6c2..9ec6a4a819919 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -396,6 +396,17 @@ namespace std::ranges {
   class chunk_by_view;                                                      // C++23
 
   namespace views { inline constexpr unspecified chunk_by = unspecified; }  // C++23
+
+  // [range.chunk]
+  template <view V>
+    requires input_range<V>
+  class chunk_view;                                                     // C++23
+
+  template <view V>
+    requires forward_range<V>
+  class chunk_view<V>;                                                  // C++23
+
+  namespace views { inline constexpr unspecified chunk = unspecified; } // C++23
 }
 
 namespace std {
@@ -482,6 +493,7 @@ namespace std {
 #    include <__ranges/adjacent_view.h>
 #    include <__ranges/as_rvalue_view.h>
 #    include <__ranges/chunk_by_view.h>
+#    include <__ranges/chunk_view.h>
 #    include <__ranges/from_range.h>
 #    include <__ranges/join_with_view.h>
 #    include <__ranges/repeat_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index ab781466f5ed5..f5fc33674f521 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -523,7 +523,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges                               202406L
 // # define __cpp_lib_ranges_as_const                      202207L
 # define __cpp_lib_ranges_as_rvalue                     202207L
-// # define __cpp_lib_ranges_chunk                         202202L
+# define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
 # define __cpp_lib_ranges_contains                      202207L
 # define __cpp_lib_ranges_find_last                     202207L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 5caa2c052c00f..631ae10c922ae 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -314,18 +314,17 @@ export namespace std {
       using std::ranges::views::adjacent_transform;
       using std::ranges::views::pairwise_transform;
     } // namespace views
+#endif
 
-#endif // _LIBCPP_STD_VER >= 23
-
-#if 0
+#if _LIBCPP_STD_VER >= 23
     using std::ranges::chunk_view;
 
-    using std::ranges::chunk_view<V>;
-
     namespace views {
       using std::ranges::views::chunk;
-    }
+    } // namespace views
+#endif
 
+#if 0
     using std::ranges::slide_view;
 
     namespace views {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
new file mode 100644
index 0000000000000..14c618c505e70
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// This test ensures that we use `[[no_unique_address]]` in `chunk_view`.
+
+#include <cstddef>
+#include <ranges>
+#include <string>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct input_view {
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<input_view> = true;
+static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
+using CV1 = std::ranges::chunk_view<input_view>;
+// Expected CV1 (with View == input) layout:
+// [[no_unique_address]] _View __base_                                         // size: 0
+// [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] __non_propagating_cache<iterator_t<_View>> __current_ // size: sizeof(__non_propagating_cache<cpp20_input_iterator<int*>>), align: std::ptrdiff_t
+static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>) == alignof(std::ptrdiff_t));
+static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
+                                 sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
+
+using CV2 = std::ranges::chunk_view<forward_view>;
+// Expected CV2 (with View >= forward) layout:
+// [[no_unique_address]] _View __base_             // size: 0
+// [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
+static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..5e557715f4b03
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
+  auto view     = range | std::views::chunk(2);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(std::as_const(view)).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(view).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(view.begin());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(std::as_const(view).begin());
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 5116864879485..41e1603f62350 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -270,17 +270,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -387,17 +381,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
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 b3b424a1d77ce..bd1b55ccc8dfa 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
@@ -5700,17 +5700,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -7619,17 +7613,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
new file mode 100644
index 0000000000000..657e16383256b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   std::views::chunk
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                       = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::ref_view<std::array<int, 8>> view = array | std::views::all;
+
+  // Test `views::chunk(view, n)`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        std::views::chunk(view, 2);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::views::chunk(std::as_const(view), 2);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2}));
+  }
+
+  // Test `views::chunk(n)(range)`
+  {
+    static_assert(noexcept(std::views::chunk(2)));
+    /*__pipable*/ auto adaptor = std::views::chunk(3);
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        adaptor(view);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        adaptor(std::as_const(view));
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3}));
+  }
+
+  // Test `view | views::chunk`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        view | std::views::chunk(4);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3, 4}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::as_const(view) | std::views::chunk(4);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3, 4}));
+  }
+
+  // Test `views::chunk | adaptor`
+  {
+    /*__pipable*/ auto adaptors            = std::views::chunk(5) | std::views::join;
+    std::ranges::input_range auto rejoined = view | adaptors;
+    assert(std::ranges::equal(rejoined, view));
+    std::ranges::input_range auto const_rejoined = std::as_const(view) | adaptors;
+    assert(std::ranges::equal(const_rejoined, view));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
new file mode 100644
index 0000000000000..ac17f97a0a292
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() &&;
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <memory>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                                                   = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>> chunked = array | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
+      std::as_const(array) | std::views::chunk(4);
+
+  // Test `chunk_view.base()`
+  {
+    std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
+    assert(std::addressof(base) == std::addressof(array));
+
+    std::same_as<const std::array<int, 8>&> decltype(auto) const_base = const_chunked.base().base();
+    assert(std::addressof(const_base) == std::addressof(array));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
new file mode 100644
index 0000000000000..3cccd7d1bebc6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr __outer_iterator begin();
+
+//   V models forward_range:
+//     constexpr auto begin() requires (!__simple_view<V>);
+//     constexpr auto begin() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.begin()` when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == input_chunked.end());
+  }
+
+  // Test `chunk_view.begin()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == chunked.end());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.begin();
+    assert(std::ranges::equal(*const_it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++const_it, std::vector{7, 8}));
+    assert(++const_it == const_chunked.end());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..9bf2baba58c78
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class R>
+//   chunk_view(R&&, range_difference_t<R>) -> chunk_view<all_t<R>>;
+
+#include <ranges>
+
+struct view : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct range {
+  int* begin() const;
+  int* end() const;
+};
+
+struct borrowed_range {
+  int* begin() const;
+  int* end() const;
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<borrowed_range> = true;
+
+void test_ctad() {
+  view v;
+  range r;
+  borrowed_range br;
+
+  // clang-format off
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(v, 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(v), 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(r, 0)), 
+                                      std::ranges::chunk_view<std::ranges::ref_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(r), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(br, 0)),
+                                      std::ranges::chunk_view<std::ranges::ref_view<borrowed_range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(br), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<borrowed_range>>>);
+  // clang-format on
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
new file mode 100644
index 0000000000000..73eb0d967f6a2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr default_sentinel_t end();
+
+//   V moduels forward_range:
+//     constexpr auto end() requires (!__simple_view<V>);
+//     constexpr auto end() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.end()` when V models only input_range
+  {
+    static_assert(noexcept(input_chunked.end()));
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it = input_chunked.end();
+  }
+
+  // Test `chunk_view.end()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--it, std::vector{1, 2, 3}));
+    assert(it == chunked.begin());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.end();
+    assert(std::ranges::equal(*--const_it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--const_it, std::vector{1, 2, 3}));
+    assert(const_it == const_chunked.begin());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
new file mode 100644
index 0000000000000..89891bc093080
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   General tests for chunk_view. This file does not test anything specifically.
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <string_view>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::string_view str = "Cheese with chicken chunk by chunk on truck with my trick";
+  // clang-format off
+  auto str2 = str 
+            | std::views::chunk(4)
+            | std::views::join
+            | std::views::chunk(314159)
+            | std::views::take(1)
+            | std::views::join
+            | std::views::lazy_split(' ')
+            | std::views::chunk(2)
+            | std::views::transform([] (auto&& subview)
+              {
+                return subview | std::views::join_with(' ');
+              })
+            | std::views::join_with(' ');
+  // clang-format on
+  assert(std::ranges::equal(str, str2));
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
new file mode 100644
index 0000000000000..d86726d958e6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator<(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator<=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base> &&
+//                three_way_comparable<iterator_t<Base>>;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t)`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    std::ranges::advance(it, 4);
+    assert(it == std::default_sentinel);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(input_chunked.end() - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - input_chunked.end() == -4);
+  }
+
+  // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() == chunked.begin());
+    assert(chunked.end() == chunked.end());
+  }
+
+  // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() < chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() > chunked.begin());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() <= chunked.begin());
+    assert(chunked.begin() <= chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() >= chunked.end());
+    assert(chunked.end() >= chunked.begin());
+  }
+
+  // Test `friend constexpr auto operator<=>(const iterator& x, const iterator& y)`
+  {
+    assert((chunked.begin() <=> chunked.begin()) == std::strong_ordering::equal);
+    assert((chunked.begin() <=> chunked.end()) == std::strong_ordering::less);
+    assert((chunked.end() <=> chunked.begin()) == std::strong_ordering::greater);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
new file mode 100644
index 0000000000000..397b32e73080a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+//   constexpr iterator& operator--()
+//     requires bidirectional_range<Base>;
+//   constexpr iterator operator--(int)
+//     requires bidirectional_range<Base>;
+//   constexpr iterator& operator-=(difference_type)
+//     requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+
+  // Test `constexpr iterator& operator--();`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator operator--(int)`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    it--;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator& operator-=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.end();
+    it -= 3;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
new file mode 100644
index 0000000000000..b8772d0fac3d2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr value_type outer_iterator::operator*() const;
+//     constexpr inner_iterator outer_iterator::value_type::begin() const noexcept;
+//     constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept;
+//     constexpr range_reference_v<V> inner_iterator::operator*() const;
+
+//   V models forward_range:
+//     constexpr value_type iterator::operator*() const;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `constexpr value_type outer_iterator::operator*() const`
+  {
+    static_assert(std::ranges::input_range<decltype(*input_chunked.begin())>);
+  }
+
+  // Test `constexpr inner_iterator outer_iterator::value_type::begin() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    assert(*inner.begin() == *vector.begin());
+    static_assert(noexcept(inner.begin()));
+  }
+
+  // Test `constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it                   = inner.end();
+    static_assert(noexcept((inner.end())));
+  }
+
+  // Test `constexpr value_type iterator::operator*() const`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::same_as<int> decltype(auto) v                           = *it;
+    assert(v == 1);
+  }
+
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
+  {
+    std::same_as<int&> decltype(auto) v = *(*chunked.begin()).begin();
+    assert(v == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
new file mode 100644
index 0000000000000..6048aba027ce9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     constexpr outer_iterator& operator++();
+//     constexpr void operator++(int);
+//     constexpr inner_iterator& operator++();
+//     constexpr void operator++(int);
+
+//   V models forward_range
+//     constexpr iterator& operator++();
+//     constexpr iterator operator++(int);
+//     constexpr iterator& operator+=(difference_type)
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(2);
+
+  // Test `constexpr outer_iterator& operator++();`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr void operator++(int);`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    assert(*++it == 2);
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(*it == 2);
+  }
+
+  // Test `constexpr iterator& operator++();`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator operator++(int)`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator& operator+=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    it += 3;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..09d16c9a48af0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f61d6f991cb15..49fb196903e77 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1106,7 +1106,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_ranges_chunk",
             "values": {"c++23": 202202},
             "headers": ["ranges"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ranges_chunk_by",

>From af914e794ddb86103e6b7acf3bf17d40f5055a8a Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 01:44:18 +0800
Subject: [PATCH 02/24] Code Review: adjust release notes

Note that we will merge this commit with the corresponding change in #172948 in the future.
---
 libcxx/docs/ReleaseNotes/22.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index d2a2d1b712689..30fc22fc0d787 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,7 +54,7 @@ Implemented Papers
 - 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>`__)
-- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------

>From dc62f798ba5615dcac60fffa2cc0afa8b79344c5 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 13:03:41 +0800
Subject: [PATCH 03/24] Add Tests: test if `_LIBCPP_ASSERT_XXX` works when
 `views::chunk` meets an undefined behaviour

---
 libcxx/include/__ranges/chunk_view.h          |  6 ++-
 .../range.chunk/assert.constructor.pass.cpp   | 39 ++++++++++++++
 .../range.chunk.iter/assert.advance.pass.cpp  | 32 ++++++++++++
 .../range.chunk.iter/assert.deref.pass.cpp    | 52 +++++++++++++++++++
 .../assert.increment.pass.cpp                 | 52 +++++++++++++++++++
 .../ranges/range.adaptors/range.chunk/types.h | 50 ++++++++++++++++++
 6 files changed, 229 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f19d2c9ede8a7..683b6bbaa2ca5 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -128,11 +128,12 @@ class chunk_view<_View>::__outer_iterator {
   _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
-    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
     return value_type(*__parent_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
     __parent_->__remainder_ = __parent_->__n_;
     return *this;
@@ -214,6 +215,7 @@ class chunk_view<_View>::__inner_iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ++*__parent_->__current_;
     if (*__parent_->__current_ == ranges::end(__parent_->__base_))
       __parent_->__remainder_ = 0;
@@ -386,7 +388,7 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
-    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to increment past-the-end chunk_view iterator");
     __missing_ = ranges::advance(__current_, __n_, __end_);
     return *this;
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
new file mode 100644
index 0000000000000..03fc7dfb6a5de
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Construct a chunk_view with chunk size <= 0
+
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3};
+
+  // Test constructor when V models only input_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  // Test constructor when V models forward_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
new file mode 100644
index 0000000000000..af2b826aa8eba
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to advance chunk_view iterator out of range
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+
+  // Test advance past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    TEST_LIBCPP_ASSERT_FAILURE(it += 4, "Trying to advance chunk_view iterator out of range");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
new file mode 100644
index 0000000000000..cb1d92885e684
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to dereference past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test dereference iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*outer_it, "Trying to dereference past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*inner_it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+
+  // Test dereference iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(*it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
new file mode 100644
index 0000000000000..8160ee1bb3e14
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to increment past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test increment past-the-end iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++outer_it, "Trying to increment past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++inner_it, "Trying to increment past-the-end chunk_view iterator");
+  }
+
+  // Test increment past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(++it, "Trying to increment past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..a5d688abfa1b0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif

>From b664c0cb8f8ed9b46629fa49e4bdd846734615d4 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:26 +0800
Subject: [PATCH 04/24] Fix CI: adjust `#include` path in `test/libcxx` files

---
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp          | 2 +-
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index cb1d92885e684..0c96742147189 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 8160ee1bb3e14..451683bab71b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};

>From 4dc9b06c0dd515030226b9d27841352ef62549a3 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:58 +0800
Subject: [PATCH 05/24] Fix CI: add template deduction guide for
 `input_span<T>`

---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 3 +++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..0978a23c37244 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..4ff1af1d7cda3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 1e8906ad49a2406158161cc1b9d0e1570af2959d Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:53:21 +0800
Subject: [PATCH 06/24] Code Review: add `REQUIRES: std-at-least-c++23` in all
 the tests related to `views::chunk`.

The previous commit, 4dc9b06c0dd515030226b9d27841352ef62549a3, which introduce manual/explicit template deduction guide in `input_span<T>`, is always implicitly-provided under -std=c++23. So this commit also reverts the changes about `types.h` in the previous commit.
---
 .../range.adaptors/range.chunk/assert.constructor.pass.cpp     | 1 +
 .../range.chunk/range.chunk.iter/assert.advance.pass.cpp       | 1 +
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp         | 1 +
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp     | 1 +
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h   | 3 ---
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h      | 3 ---
 6 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
index 03fc7dfb6a5de..477f89be5f070 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
index af2b826aa8eba..9b91d94841b91 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index 0c96742147189..3640b04192bf4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 451683bab71b0..61c096f74158e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index 0978a23c37244..a5d688abfa1b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 4ff1af1d7cda3..09d16c9a48af0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 7e21388c70b38c32125e4d3eda9b3a4c66e1d3db Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 14:13:59 +0800
Subject: [PATCH 07/24] Fix CI: add template deduction guide for
 `input_span<T>` again.

Seems that even in C++23 the CI fails due to no implicit deduction guide, here we just add the explicit one and make the CI to basically run ok.
---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 4 ++++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..4852ec29204bb 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..7533e41713d72 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From f1ed40a1aed2a1f5ebd1e56efcd6ae2bbb25dc26 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:07:21 +0800
Subject: [PATCH 08/24] Code review: Update libcxx/test synopsis

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 .../ranges/range.adaptors/range.chunk/nodiscard.verify.cpp      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 5e557715f4b03..9264256fc6a52 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+// Test that functions are marked [[nodiscard]].
 
 #include <ranges>
 #include <utility>

>From 12bec119ba49cd6016055f5887f5e83efa25ee30 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:21:29 +0800
Subject: [PATCH 09/24] Code review: Fix synopsis.

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index ac17f97a0a292..b45e1f59f1394 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() const& requires copy_constructible<V>;
 //   constexpr V base() &&;
 
 #include <array>

>From 1e05c5a038dd4cee5a0049b8c7e92e59c8aabd82 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 21:13:46 +0800
Subject: [PATCH 10/24] Code review: sort and unique the tests

---
 .../no_unique_address.compile.pass.cpp        |  24 ++--
 .../range.chunk/nodiscard.verify.cpp          |  35 ++----
 .../range.chunk.iter/arithmetic.pass.cpp      | 108 ++++++++++++++++++
 .../range.chunk.iter/compare.pass.cpp         |  29 +++--
 .../range.chunk.iter/subscript.pass.cpp       |  42 +++++++
 5 files changed, 189 insertions(+), 49 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
index 14c618c505e70..5d32daf4c178a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -20,6 +20,8 @@
 #include "test_iterators.h"
 #include "test_range.h"
 
+// When V models only input_range
+
 struct input_view {
   cpp20_input_iterator<int*> begin() const;
   sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
@@ -28,16 +30,8 @@ template <>
 inline constexpr bool std::ranges::enable_view<input_view> = true;
 static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
 
-struct forward_view {
-  int* begin() const;
-  int* end() const;
-};
-template <>
-inline constexpr bool std::ranges::enable_view<forward_view> = true;
-static_assert(std::ranges::forward_range<forward_view>);
-
 using CV1 = std::ranges::chunk_view<input_view>;
-// Expected CV1 (with View == input) layout:
+// Expected CV1 (when V models only input_range) layout:
 // [[no_unique_address]] _View __base_                                         // size: 0
 // [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
 // [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
@@ -46,8 +40,18 @@ static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<
 static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
                                  sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
 
+// When V models forward_range
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
 using CV2 = std::ranges::chunk_view<forward_view>;
-// Expected CV2 (with View >= forward) layout:
+// Expected CV2 (when V models forward_range) layout:
 // [[no_unique_address]] _View __base_             // size: 0
 // [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
 static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 9264256fc6a52..4d0fb4f76857c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -19,15 +19,6 @@ void test() {
   char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
   auto view     = range | std::views::chunk(2);
 
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   view.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -43,18 +34,19 @@ void test() {
   std::as_const(view).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  view.end();
+  std::ranges::iter_move(view.begin());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(view).end();
+  std::ranges::iter_move(std::as_const(view).begin());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
+  view.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
+  std::as_const(view).end();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
+  std::ranges::iter_move(view.end());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
+  std::ranges::iter_move(std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   *view.begin();
@@ -71,16 +63,11 @@ void test() {
   (std::as_const(view).begin() == std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == std::as_const(view).end());
+  std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == std::as_const(view).end());
-
+  std::views::chunk(range, 3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(view.begin());
+  range | std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(std::as_const(view).begin());
+  std::views::reverse | std::views::chunk(3);
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..6baa2fce13695
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const inner_iterator& x)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr iterator operator+(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator+(difference_type n, const iterator& i)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator-(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+//       requires sized_sentinel_for<iterator_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(std::default_sentinel - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - std::default_sentinel == -4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t, const inner_iterator& x)`
+  {
+    assert(std::default_sentinel - (*input_chunked.begin()).begin() == 3);
+  }
+
+  // Test `friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t)`
+  {
+    assert((*input_chunked.begin()).begin() - std::default_sentinel == -3);
+  }
+
+  // Test `friend constexpr iterator operator+(const iterator& i, difference_type n)`
+  {
+    assert(chunked.begin() + 4 == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator+(difference_type n, const iterator& i)`
+  {
+    assert(4 + chunked.begin() == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator-(const iterator& i, difference_type n)`
+  {
+    assert(chunked.end() - 4 == chunked.begin());
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)`
+  {
+    assert(std::default_sentinel - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)`
+  {
+    assert(chunked.begin() - std::default_sentinel == -4);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
index d86726d958e6e..ea6731cccce85 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -11,14 +11,12 @@
 // <ranges>
 
 //   V models only input_range
-//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
-//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
-//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t);
 
 //   V models forward_range
-//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator==(const iterator& x, const iterator& y);
+//     friend constexpr bool operator==(const iterator& x, default_sentinel_t);
 //     friend constexpr bool operator<(const iterator& x, const iterator& y)
 //       requires random_access_range<Base>;
 //     friend constexpr bool operator>(const iterator& x, const iterator& y)
@@ -31,7 +29,6 @@
 //       requires random_access_range<Base> &&
 //                three_way_comparable<iterator_t<Base>>;
 
-#include <algorithm>
 #include <cassert>
 #include <compare>
 #include <iterator>
@@ -53,14 +50,11 @@ constexpr bool test() {
     assert(it == std::default_sentinel);
   }
 
-  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  // Test `friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t)`
   {
-    assert(input_chunked.end() - input_chunked.begin() == 4);
-  }
-
-  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
-  {
-    assert(input_chunked.begin() - input_chunked.end() == -4);
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::ranges::advance(it, 3);
+    assert(it == std::default_sentinel);
   }
 
   // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
@@ -69,6 +63,11 @@ constexpr bool test() {
     assert(chunked.end() == chunked.end());
   }
 
+  // Test `friend constexpr bool operator==(const iterator& x, default_sentinel)`
+  {
+    assert(chunked.end() == std::default_sentinel);
+  }
+
   // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() < chunked.end());
@@ -79,7 +78,7 @@ constexpr bool test() {
     assert(chunked.end() > chunked.begin());
   }
 
-  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  // Test `friend constexpr bool operator<=(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() <= chunked.begin());
     assert(chunked.begin() <= chunked.end());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
new file mode 100644
index 0000000000000..9b45fc6d779bf
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models forward_range
+//     constexpr value_type iterator::operator[](difference_type n) const
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+
+  // Test `constexpr value_type iterator::operator[](difference_type n) const`
+  {
+    assert(std::ranges::equal(chunked.begin()[1], std::vector{4, 5, 6}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 033a1c16289990a16b1f674f380f7a72f38f48ea Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 14:30:08 -0400
Subject: [PATCH 11/24] Fix release notes

---
 libcxx/docs/ReleaseNotes/22.rst    | 1 -
 libcxx/docs/ReleaseNotes/23.rst    | 1 +
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0637ba98976a2..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -55,7 +55,6 @@ Implemented Papers
 - 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>`__)
-- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e7eb42207db42..a98f42da70f00 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -52,6 +52,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::chunk`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 6abcac5e4ebca..8c92d89be999b 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""

>From 7fe126d5e7c88787f26228c64bcb5f89977c5b83 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:02:10 +0800
Subject: [PATCH 12/24] Resolve overload conflicts on `__div_ceil` in
 `ranges::chunk_view` and `ranges::stride_view`.

Both `ranges::chunk_view` and `ranges::stride_view` need a helper function called `__div_ceil` that solves some simple maths. According to the style of other libcxx internal helper functions (e.g. `__priority_tag`, `__small_buffer`), it seems that we can put `__div_ceil` into a new file `__utility/div_ceil.h`. However, most files in `__utility/` refer to a corresponding symbol in `<utility>` header, so that if someone feels this is not proper, I'll be glad to revert it.
---
 libcxx/include/CMakeLists.txt         |  1 +
 libcxx/include/__ranges/chunk_view.h  | 21 ++++++-----------
 libcxx/include/__ranges/stride_view.h | 20 +++++-----------
 libcxx/include/__utility/div_ceil.h   | 33 +++++++++++++++++++++++++++
 libcxx/include/module.modulemap.in    |  1 +
 5 files changed, 48 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/include/__utility/div_ceil.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 76b93c17c3312..0e9f19a81b1ab 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -935,6 +935,7 @@ set(files
   __utility/convert_to_integral.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
+  __utility/div_ceil.h
   __utility/element_count.h
   __utility/empty.h
   __utility/exception_guard.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 683b6bbaa2ca5..68d4408295e47 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -36,6 +36,7 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 
@@ -52,14 +53,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Integral>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
-  _Integral __r = __num / __denom;
-  if (__num % __denom)
-    ++__r;
-  return __r;
-}
-
 template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
@@ -98,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -153,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -321,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -510,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..7ea68b13330c3 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -46,15 +47,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Value>
-_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) {
-  _Value __r = __left / __right;
-  if (__left % __right) {
-    ++__r;
-  }
-  return __r;
-}
-
 template <input_range _View>
   requires view<_View>
 class stride_view : public view_interface<stride_view<_View>> {
@@ -121,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -340,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -ranges::__div_ceil(-__n, __x.__stride_);
+      return -__div_ceil(-__n, __x.__stride_);
     }
-    return ranges::__div_ceil(__n, __x.__stride_);
+    return __div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)
diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
new file mode 100644
index 0000000000000..595c590b9e3f6
--- /dev/null
+++ b/libcxx/include/__utility/div_ceil.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_DIV_CEIL_H
+#define _LIBCPP___UTILITY_DIV_CEIL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __div_ceil computes the ceiling of the division of two integers. It is mainly used by
+// range adaptors like chunk_view and stride_view.
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_DIV_CEIL_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 64238440b9dd7..ef5d15d063f69 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2200,6 +2200,7 @@ module std [system] {
     module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
+    module div_ceil                        { header "__utility/div_ceil.h" }
     module element_count                   { header "__utility/element_count.h" }
     module exception_guard                 { header "__utility/exception_guard.h" }
     module exchange                        { header "__utility/exchange.h" }

>From d27b355e67e17b0ed15d44a247fdba1c3b13ed49 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:22:38 +0800
Subject: [PATCH 13/24] Fix test error that `constexpr` is not supported in
 `-std=c++03`

---
 libcxx/include/__utility/div_ceil.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 595c590b9e3f6..4d2868465c141 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,6 +17,8 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if _LIBCPP_STD_VER >= 11
+
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
 
@@ -28,6 +30,8 @@ template <class _Integral>
   return __r;
 }
 
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___UTILITY_DIV_CEIL_H

>From a1a5918bb79d29cbe7be2736da7a72b83ee0afbb Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:38:37 +0800
Subject: [PATCH 14/24] Fix test error again that `constexpr` is not supported
 in `-std=c++11`

---
 libcxx/include/__utility/div_ceil.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 4d2868465c141..21e9bcb30d1a1 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,7 +17,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 11
+#if _LIBCPP_STD_VER >= 23
 
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
@@ -30,7 +30,7 @@ template <class _Integral>
   return __r;
 }
 
-#endif
+#endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_END_NAMESPACE_STD
 

>From c2e0e2e926c92ee6d001e060aa5660b681874dfd Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:45:41 +0800
Subject: [PATCH 15/24] Fix test error that `std::as_const` is included from
 `<utility>`.

---
 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp | 1 +
 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
index 3cccd7d1bebc6..29588a57fbcab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -22,6 +22,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
index 73eb0d967f6a2..2f37ac5f73524 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -23,6 +23,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"

>From 7d3cdd73f5da77a7499969fb7954312a19e26630 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 14:19:19 +0800
Subject: [PATCH 16/24] Avoid ADL in `std::__div_ceil`.

---
 libcxx/include/__ranges/chunk_view.h  | 12 ++++++------
 libcxx/include/__ranges/stride_view.h | 10 +++++-----
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 68d4408295e47..1107a9238b2bc 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -91,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -146,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return std::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -314,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -503,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return std::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 7ea68b13330c3..a8b485cbe33e5 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -113,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -332,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -__div_ceil(-__n, __x.__stride_);
+      return -std::__div_ceil(-__n, __x.__stride_);
     }
-    return __div_ceil(__n, __x.__stride_);
+    return std::__div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return std::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)

>From 116c6d85d8a9ad177b1df1975eea92479cb03664 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 16:39:58 +0800
Subject: [PATCH 17/24] [Important] Add missing concepts requirements.

---
 libcxx/include/__ranges/chunk_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1107a9238b2bc..47e6bd56415b6 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -400,7 +400,8 @@ class chunk_view<_View>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires bidirectional_range<_Base> {
     auto __tmp = *this;
     --*this;
     return __tmp;

>From 545bbdf10d24c3790790bfe669b8c756da719838 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Fri, 26 Jun 2026 22:35:12 +0800
Subject: [PATCH 18/24] Cover tests on `chunk_range`. (I'll continue to cover
 tests on `__iterator`, `__inner_iterator` and `__outer_iterator` at the
 weekend)

---
 libcxx/include/__ranges/chunk_view.h          |  3 +-
 .../range.adaptors/range.chunk/base.pass.cpp  | 12 +++-
 .../range.chunk/borrowing.compile.pass.cpp    | 27 +++++++
 .../range.adaptors/range.chunk/ctor.pass.cpp  | 54 ++++++++++++++
 .../range.adaptors/range.chunk/size.pass.cpp  | 72 +++++++++++++++++++
 5 files changed, 166 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 47e6bd56415b6..1798711eabd16 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -401,7 +401,8 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
-    requires bidirectional_range<_Base> {
+    requires bidirectional_range<_Base>
+  {
     auto __tmp = *this;
     --*this;
     return __tmp;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index b45e1f59f1394..4dca324a23695 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -26,7 +26,7 @@ constexpr bool test() {
   std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
       std::as_const(array) | std::views::chunk(4);
 
-  // Test `chunk_view.base()`
+  // Test `chunk_view.base() const&`
   {
     std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
     assert(std::addressof(base) == std::addressof(array));
@@ -35,6 +35,16 @@ constexpr bool test() {
     assert(std::addressof(const_base) == std::addressof(array));
   }
 
+  // Test `chunk_view.base() &&`
+  {
+    std::same_as<std::ranges::ref_view<std::array<int, 8>>> decltype(auto) moved = std::move(chunked).base();
+    assert(std::addressof(moved.base()) == std::addressof(array));
+
+    std::same_as<std::ranges::ref_view<const std::array<int, 8>>> decltype(auto) const_moved =
+        std::move(const_chunked).base();
+    assert(std::addressof(const_moved.base()) == std::addressof(array));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
new file mode 100644
index 0000000000000..1eb4a34f87217
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class V>
+//   inline constexpr bool enable_borrowed_range<chunk_view<V>> =
+//       forward_range<V> && enable_borrowed_range<V>;
+
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+// When V models only `input_range`.
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<input_span<int>>>);
+
+// When V models at least `forward_range`.
+static_assert(std::ranges::enable_borrowed_range<std::ranges::chunk_view<BorrowedView>>);
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
new file mode 100644
index 0000000000000..b60e040b807eb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.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++23
+
+// <ranges>
+
+//   constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n);
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_convertible.h"
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models only `input_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>, input_span<int>, std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(input_span(vector.data(), 8), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+                                    std::ranges::ref_view<std::vector<int>>,
+                                    std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
new file mode 100644
index 0000000000000..820774c3b750e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr auto size() requires sized_range<_View>;
+//   constexpr auto size() const requires sized_range<const _View>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+  // Test `chunk_view.size()` when V models only `input_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<input_span<int>>>);
+    static_assert(!std::ranges::sized_range<const std::ranges::chunk_view<input_span<int>>>);
+
+    auto chunked = input_span<int>(arr, 12) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when V models `forward_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+    static_assert(std::ranges::sized_range<const std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+    const auto& const_chunked = chunked;
+    assert(const_chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when the range is not fully divisible
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(5);
+    assert(chunked.size() == 3);
+  }
+
+  // Test `chunk_view.size()` when the range is empty
+  {
+    static_assert(std::ranges::sized_range<std::ranges::empty_view<int>>);
+    auto chunked = std::views::empty<int> | std::views::chunk(3);
+    assert(chunked.size() == 0);
+  }
+
+  // Test `chunk_view.size()` when chunk size is larger than the range
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(100);
+    assert(chunked.size() == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From e262f9e7497fb3ccf232a3c7265b8faa83f2fa20 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:36:04 +0800
Subject: [PATCH 19/24] Fix chunk_view ctor test for forward ranges.

---
 .../test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
index b60e040b807eb..c0d210aa45dc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
@@ -35,11 +35,11 @@ constexpr bool test() {
 
   // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
   {
-    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+    static_assert(!test_convertible<std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>>,
                                     std::ranges::ref_view<std::vector<int>>,
                                     std::ptrdiff_t>());
 
-    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked(std::ranges::ref_view(vector), 3);
     assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
   }
 

>From bf13c55de976c562120249f2fc7208ea1091fc95 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:39:36 +0800
Subject: [PATCH 20/24] Use consistent copy_constructible spelling in
 chunk_view

---
 libcxx/include/__ranges/chunk_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1798711eabd16..f3edc617de07a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -71,7 +71,7 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
-    requires std::copy_constructible<_View>
+    requires copy_constructible<_View>
   {
     return __base_;
   }

>From f457f267b72fe64a2f22c288da4b096ae0bcc83c Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:48:59 +0800
Subject: [PATCH 21/24] Remove unnecessary no_unique_address from chunk_view
 counts

---
 libcxx/include/__ranges/chunk_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f3edc617de07a..4478af951dbca 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -57,8 +57,8 @@ template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  range_difference_t<_View> __n_;
+  range_difference_t<_View> __remainder_;
   _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
 
   class __outer_iterator;
@@ -256,7 +256,7 @@ template <view _View>
   requires forward_range<_View>
 class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  range_difference_t<_View> __n_;
 
   template <bool _Const>
   class __iterator;

>From 39c371e116736dd37006c3cf48ed9cf9412b1222 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 23:25:01 +0800
Subject: [PATCH 22/24] Fix chunk_view inner iter_move return type (from T into
 T&&)

---
 libcxx/include/__ranges/chunk_view.h          |  2 +-
 .../range.chunk.inner.iter/iter_move.pass.cpp | 43 +++++++++++++++++++
 2 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 4478af951dbca..e9511f027160a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -238,7 +238,7 @@ class chunk_view<_View>::__inner_iterator {
     return -(__s - __i);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_View>
   iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
     return ranges::iter_move(*__i.__parent_->__current_);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp
new file mode 100644
index 0000000000000..5c7b620924c2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   friend constexpr range_rvalue_reference_t<V> iter_move(const inner_iterator& i)
+//     noexcept(noexcept(ranges::iter_move(i.parent_->current_.value())));
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+constexpr bool test() {
+  using InnerIterator =
+      std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<test_view<cpp20_input_iterator>>>>;
+
+  static_assert(std::ranges::input_range<test_view<cpp20_input_iterator>>);
+  static_assert(!std::ranges::forward_range<test_view<cpp20_input_iterator>>);
+
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())), int&&>);
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())),
+                             std::ranges::range_rvalue_reference_t<test_view<cpp20_input_iterator>>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file

>From c1a0811d8824267ff40fa442b2c2ad10d95b7690 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Mon, 20 Jul 2026 00:13:15 +0800
Subject: [PATCH 23/24] Add chunk_view constraint tests

---
 .../range.chunk/concept.compile.pass.cpp      | 64 +++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp
new file mode 100644
index 0000000000000..61b89d4c09c32
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <view V>
+//     requires input_range<V>
+//   class chunk_view;
+//
+//   template <view V>
+//     requires forward_range<V>
+//   class chunk_view<_View>;
+
+#include <ranges>
+#include <utility>
+
+#include "almost_satisfies_types.h"
+#include "test_range.h"
+#include "types.h"
+
+template <>
+inline constexpr bool std::ranges::enable_view<InputRangeNotDerivedFrom> = true;
+
+// Test two separate properties:
+// 1. Whether `chunk_view<View>` can be formed.
+// 2. Whether a well-formed `chunk_view<View>` can be constructed.
+template <class View>
+concept CanFormChunkView = requires { typename std::ranges::chunk_view<View>; };
+
+template <class View>
+concept CanConstructChunkView = CanFormChunkView<View> && requires(View view, std::ranges::range_difference_t<View> n) {
+  std::ranges::chunk_view<View>(std::move(view), n);
+};
+
+// Test constraints when the template argument is not a view
+static_assert(!std::ranges::view<test_non_const_range<cpp17_input_iterator>>);
+static_assert(std::ranges::input_range<test_non_const_range<cpp17_input_iterator>>);
+static_assert(!CanFormChunkView<test_non_const_range<cpp17_input_iterator>>);
+
+// Test constraints when the template argument is not an input_range
+static_assert(!std::ranges::input_range<InputRangeNotDerivedFrom>);
+static_assert(std::ranges::view<InputRangeNotDerivedFrom>);
+static_assert(!CanFormChunkView<InputRangeNotDerivedFrom>);
+
+// Test constraints when the template argument is an input_range and a view
+static_assert(std::ranges::input_range<input_span<int>>);
+static_assert(std::ranges::view<input_span<int>>);
+static_assert(CanFormChunkView<input_span<int>> && CanConstructChunkView<input_span<int>>);
+
+// Test constraints when the template argument is a forward_range and a view
+static_assert(std::ranges::forward_range<BorrowedView>);
+static_assert(std::ranges::view<BorrowedView>);
+static_assert(CanFormChunkView<BorrowedView> && CanConstructChunkView<BorrowedView>);
+
+// chunk_view itself models view
+static_assert(std::ranges::view<std::ranges::chunk_view<input_span<int>>>);
+static_assert(std::ranges::view<std::ranges::chunk_view<BorrowedView>>);

>From 8085a1d3bf45b122557796308e767016f9f7e784 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Mon, 20 Jul 2026 00:13:26 +0800
Subject: [PATCH 24/24] Add chunk_view iterator type tests

---
 .../types.compile.pass.cpp                    | 30 ++++++++++
 .../range.chunk.iter/types.compile.pass.cpp   | 57 +++++++++++++++++++
 .../types.compile.pass.cpp                    | 31 ++++++++++
 3 files changed, 118 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..1775a08f84a2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   class inner_iterator;
+
+//   using inner_iterator::iterator_concept = input_iterator_tag;
+//   using inner_iterator::difference_type = range_difference_t<V>;
+//   using inner_iterator::value_type = range_value_t<V>;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+using InnerIterator = std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<input_span<int>>>>;
+
+static_assert(std::same_as<typename InnerIterator::iterator_concept, std::input_iterator_tag>);
+static_assert(std::same_as<typename InnerIterator::difference_type, std::ranges::range_difference_t<input_span<int>>>);
+static_assert(std::same_as<typename InnerIterator::value_type, std::ranges::range_value_t<input_span<int>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..96e3057edc04a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models forward_range:
+//   class iterator;
+
+//   using iterator::iterator_category = input_iterator_tag;
+//   using iterator::iterator_concept = see below;
+//   using iterator::value_type = decltype(views::take(subrange(current_, end_), n_));
+//   using iterator::difference_type = range_difference_t<Base>;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+template <template <class...> class Iter>
+using ChunkViewFor = std::ranges::chunk_view<test_view<Iter>>;
+
+template <template <class...> class Iter>
+using ChunkIteratorFor = std::ranges::iterator_t<ChunkViewFor<Iter>>;
+
+template <template <class...> class Iter>
+constexpr void test_iterator_types() {
+  using ChunkView     = ChunkViewFor<Iter>;
+  using ChunkIterator = ChunkIteratorFor<Iter>;
+
+  static_assert(std::same_as<typename ChunkIterator::iterator_category, std::input_iterator_tag>);
+  static_assert(std::same_as<typename ChunkIterator::value_type, std::ranges::range_value_t<ChunkView>>);
+  static_assert(std::same_as<typename ChunkIterator::difference_type, std::ranges::range_difference_t<test_view<Iter>>>);
+}
+
+constexpr void test() {
+  test_iterator_types<forward_iterator>();
+  test_iterator_types<bidirectional_iterator>();
+  test_iterator_types<random_access_iterator>();
+  test_iterator_types<contiguous_iterator>();
+
+  static_assert(std::same_as<ChunkIteratorFor<forward_iterator>::iterator_concept, std::forward_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<bidirectional_iterator>::iterator_concept,
+                             std::bidirectional_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<random_access_iterator>::iterator_concept,
+                             std::random_access_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<contiguous_iterator>::iterator_concept,
+                             std::random_access_iterator_tag>);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..9127e567305ce
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   class outer_iterator;
+
+//   using outer_iterator::iterator_concept = input_iterator_tag;
+//   using outer_iterator::difference_type = range_difference_t<V>;
+//   class outer_iterator::value_type;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+using OuterIterator = std::ranges::iterator_t<std::ranges::chunk_view<input_span<int>>>;
+
+static_assert(std::same_as<typename OuterIterator::iterator_concept, std::input_iterator_tag>);
+static_assert(std::same_as<typename OuterIterator::difference_type, std::ranges::range_difference_t<input_span<int>>>);
+static_assert(std::same_as<typename OuterIterator::value_type, std::iter_value_t<OuterIterator>>);
+static_assert(std::ranges::input_range<typename OuterIterator::value_type>);


From libcxx-commits at lists.llvm.org  Sun Jul 19 09:25:55 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 19 Jul 2026 09:25:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::adjacent_find() based on std::mismatch() (PR #210604)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210604@github.com>
Message-ID: <6a5cfa93.510f576d.2f694c.e3f9@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/210604

>From 650ebeba6d9e4867856e679d475e8f50edcdadde Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:03:38 +0100
Subject: [PATCH 01/16] Added a basic implementation of parallel 4-legged
 mismatch() based on __parallel_find()

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/libdispatch.h  |   7 +-
 libcxx/include/__pstl/backends/serial.h       |  20 +++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  88 +++++++++++
 .../mismatch/pstl.mismatch.pass.cpp           | 139 ++++++++++++++++++
 7 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__pstl/cpu_algos/mismatch.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..6b5dc87fbe76d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/mismatch.h  
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a84a768cad570..1d64c6f47630f 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -317,6 +317,14 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index 33fd5de66fbb1..710506585ca22 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -33,6 +33,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -237,7 +238,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
       auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
       auto __index           = __chunk == 0 ? 0
                                             : (__chunk * __partitions.__chunk_size_) +
-                                        (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
+                                                  (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
       if (__this_chunk_size != 1) {
         std::__construct_at(
             __values.get() + __chunk,
@@ -364,6 +365,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..d90022ee37145 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/mismatch.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    return std::mismatch(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::forward<_Predicate>(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..93935d22b9442 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
new file mode 100644
index 0000000000000..11ffdc332d633
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_MISMATCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
+
+#include <__algorithm/mismatch.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_mismatch {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Look for a mismatch only in the prefix of the two ranges.
+      auto __n = std::min(__last1 - __first1, __last2 - __first2);
+      // Find a position in the first range where the predicate is false against the corresponding position in the
+      // second range.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __first1 + __n,
+          [&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
+            // Run the sequential mismatch algorithm on these ranges:
+            //   [__brick_first1, __brick_last1) and
+            //   [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
+            auto __brick_first2 = __first2 + (__brick_first1 - __first1);
+            return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
+                .first;
+          },
+          less<>{},
+          true);
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      auto __idx = *__res - __first1;
+      return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      // Unsequenced execution is also implicitly covered by the sequential implementation.
+      return std::mismatch(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
new file mode 100644
index 0000000000000..9cbadda428ad9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 1};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(42);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From e96f0aa34d1b212b123d4c9917928e17d26270f3 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:05:01 +0100
Subject: [PATCH 02/16] Added a definition of a parallel 4-legged
 std::mismatch()

---
 libcxx/include/__algorithm/pstl.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2bf9b4276baab..ab35b1a3e537b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -138,6 +138,29 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,

>From 472174e7343fc86a77bf314c7d088331dab6bae5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:41:51 +0100
Subject: [PATCH 03/16] Added a default implementation of __mismatch_3leg based
 on __mismatch

---
 libcxx/include/__algorithm/pstl.h             | 18 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++++
 libcxx/include/__pstl/backends/default.h      | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../mismatch/pstl.mismatch.pass.cpp           | 20 +++++++++++
 5 files changed, 84 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index ab35b1a3e537b..01c61fb00f4d5 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -161,6 +161,24 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 1d64c6f47630f..d800e60916efc 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -325,6 +325,13 @@ struct __mismatch;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch_3leg;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5c78d4efa412b..05b328beae80f 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -63,6 +63,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// mismatch family
+// ---------------
+// - mismatch_3leg
+//
 // for_each family
 // ---------------
 // - for_each_n
@@ -211,6 +215,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// mismatch family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+  optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _Comp __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Forward to the 4-legged version of mismatch.
+      using _Mismatch           = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      _ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
+      return _Mismatch()(
+          __policy,
+          std::move(__first1),
+          std::move(__last1),
+          std::move(__first2),
+          std::move(__last2),
+          std::move(__comp));
+    } else {
+      // Currently only random access iterators are supported for parallel mismatch_3leg.
+      return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..752aa35643e2b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2384,6 +2384,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module mismatch {
+        header "__pstl/cpu_algos/mismatch.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 9cbadda428ad9..0cb5c7632eed8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -10,6 +10,14 @@
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2);
+//
 // template <class ExecutionPolicy,
 //           class ForwardIterator1,
 //           class ForwardIterator2>
@@ -61,6 +69,8 @@ struct Test {
       // empty ranges
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
              std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
     }
@@ -68,18 +78,24 @@ struct Test {
       // single element only
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range without mismatch
       std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range with mismatch
       std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
     }
@@ -102,10 +118,14 @@ struct Test {
       rhs = lhs;
       runway_sample(lhs.size(), [&](size_t i) {
         lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
                std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         lhs[i] = i;
       });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }

>From 4aab10d6cc54f3abd475bd3aae03ac7d4103eb45 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:20:42 +0100
Subject: [PATCH 04/16] Added predicated versions

---
 libcxx/include/__algorithm/pstl.h             |  49 +++++
 .../mismatch/pstl.mismatch.pass.cpp           |   2 +
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 178 ++++++++++++++++++
 3 files changed, 229 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 01c61fb00f4d5..bc48fd9f48d7f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
+#  include <__utility/pair.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -138,6 +139,31 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
@@ -161,6 +187,29 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 0cb5c7632eed8..8584f2779927f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -42,6 +42,8 @@
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
+static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
new file mode 100644
index 0000000000000..39e952cbc38f6
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     BinaryPredicate pred);
+//
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2,
+//                                                     BinaryPredicate pred);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+struct Pred {
+  bool operator()(int a, int b) const { return a * 2 == b; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      assert(std::mismatch(
+                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {1};
+      std::array<int, 1> rhs = {2};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 2};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(84);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From 2da95a5c532de58960651a18c9a93c9435577a89 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:39:47 +0100
Subject: [PATCH 05/16] Added tests against custom types

---
 libcxx/include/__pstl/backends/default.h      |  1 +
 .../mismatch/pstl.mismatch.pass.cpp           | 53 ++++++++++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 56 ++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 05b328beae80f..f4f73e81ed90b 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -16,6 +16,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 8584f2779927f..5e46831539c30 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -63,6 +63,30 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
+
 template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
@@ -150,6 +174,29 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -157,5 +204,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 39e952cbc38f6..9c0dfc6c0c127 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -67,8 +67,31 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
 struct Pred {
-  bool operator()(int a, int b) const { return a * 2 == b; }
+  bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
+  bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
 };
 
 template <class Iter1, class Iter2>
@@ -167,6 +190,31 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -174,5 +222,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }

>From 2fb2bb1a154212dc1cbf3a1ea9e3e1cf5a00db48 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:52:50 +0100
Subject: [PATCH 06/16] Added a nodiscard test

---
 libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 049c8dc88c2da..c538d7841dce6 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -58,4 +58,12 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }

>From 8c35d0cac6fc6a3daf494de08ac0a2fd365d50f1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:57:47 +0100
Subject: [PATCH 07/16] Added an iterator requirement test

---
 .../pstl.iterator-requirements.verify.cpp        | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index fbe3460b67f17..4526cad0615b0 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, pred);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}

>From a8b984852860283d06ab06bbdf05a28f01965326 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:00:09 +0100
Subject: [PATCH 08/16] Added an include of <__algorithm/min.h>

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 11ffdc332d633..8361731e78cfc 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,8 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__algorithm/mismatch.h>
 #include <__config>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/min.h>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>

>From 3845b7e0e242d455f0fcc68a814ed625544c3d8c Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:05:29 +0100
Subject: [PATCH 09/16] Added an exception handling test

---
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  4 +--
 .../pstl.exception_handling.pass.cpp          | 25 +++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 8361731e78cfc..597a47fb185c8 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,9 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__config>
-#include <__algorithm/mismatch.h>
 #include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__config>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 8af2a0a15fd5c..820a5d92c19e0 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -248,6 +248,31 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // mismatch(first1, last1, first2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
+        });
+
+        // mismatch(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // mismatch(first1, last1, first2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
+        });
+
+        // mismatch(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From fbc073f5742c81ae78d12058ecf4618213afb6f0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 09:43:45 +0100
Subject: [PATCH 10/16] Changed the variable name to avoid shadowing

---
 .../alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 12 ++++++------
 .../mismatch/pstl.mismatch_pred.pass.cpp             | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 5e46831539c30..941fe07b58327 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -67,22 +67,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 9c0dfc6c0c127..a4b15a56bd071 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -71,22 +71,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Pred {

>From baaa82257b3d2f94d4bf9d02f6761e7cf3ee9df6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 20:27:14 +0100
Subject: [PATCH 11/16] Use raw C array in the tests

---
 .../mismatch/pstl.mismatch.pass.cpp           | 153 +++++++-------
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 192 +++++++++++-------
 2 files changed, 198 insertions(+), 147 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 941fe07b58327..17edf34ee7832 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -28,7 +28,6 @@
 //                                                     ForwardIterator2 last2);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -93,82 +92,98 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(
+          std::mismatch(
+              policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
+          std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 1};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 1};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(
+            std::mismatch(
+                policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+            std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(42);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 42);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(
+                   policy,
+                   Iter1(std::begin(lhs)),
+                   Iter1(std::begin(lhs) + i),
+                   Iter2(std::begin(rhs)),
+                   Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i)) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -179,20 +194,22 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index a4b15a56bd071..18c4fe1abad0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -32,7 +32,6 @@
 //                                                     BinaryPredicate pred);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -100,91 +99,120 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(
-                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::begin(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::begin(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {1};
-      std::array<int, 1> rhs = {2};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {1};
+      int rhs[1] = {2};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 2};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 2};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(84);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 84);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::begin(lhs) + i),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -195,22 +223,28 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };

>From 212e25406377a6a5da7fc7679015000a4086e4a1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:08:55 +0100
Subject: [PATCH 12/16] Fixed typos in the comment

---
 .../algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 2 +-
 .../alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 17edf34ee7832..ed59fa9eca669 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -46,7 +46,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 18c4fe1abad0c..338503d784a31 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -50,7 +50,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;

>From b80675016128dbeb1db5a3084edee9bb6ab89fea Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:43:20 +0100
Subject: [PATCH 13/16] Added comments explaining what the parameters mean

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 597a47fb185c8..3d702b03d3afe 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -63,8 +63,9 @@ struct __cpu_parallel_mismatch {
             return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
                 .first;
           },
-          less<>{},
-          true);
+          less<>{}, // `less` here means the lowest index among the mismatches
+          true      // `true` here means we want the first mismatch, not the last
+      );
       if (!__res) {
         return std::nullopt; // Failed to run the algorithm, propagate the error.
       }

>From 12ac822e8571b5d944ffea6d4f62396a954f4672 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:50:51 +0100
Subject: [PATCH 14/16] Added tests of the return types

---
 .../mismatch/pstl.mismatch.pass.cpp              | 10 ++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp         | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index ed59fa9eca669..cc7ef6feb2356 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -90,6 +90,16 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1]       = {0};
+      int rhs[1]       = {0};
+      auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
+      auto res_4legged = std::mismatch(
+          policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 338503d784a31..d80da0cf0b542 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -97,6 +97,22 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      auto res_3legged =
+          std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
+      auto res_4legged = std::mismatch(
+          policy,
+          Iter1(std::begin(lhs)),
+          Iter1(std::begin(lhs)),
+          Iter2(std::begin(rhs)),
+          Iter2(std::begin(rhs)),
+          Pred{});
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};

>From 01513beffed6c7812b59d62ab4cdae683c9e0c10 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 19 Jul 2026 16:27:43 +0100
Subject: [PATCH 15/16] Added an implementation of adjacent_find() based on
 mismatch()

---
 libcxx/include/__algorithm/pstl.h             |  25 +++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  45 +++++
 .../pstl.iterator-requirements.verify.cpp     |   6 +
 .../algorithms/pstl.nodiscard.verify.cpp      |   4 +
 .../pstl.adjacent_find.pass.cpp               | 164 +++++++++++++++++
 .../pstl.adjacent_find_pred.pass.cpp          | 173 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  12 ++
 8 files changed, 436 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index bc48fd9f48d7f..0447691c3b86b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -228,6 +228,31 @@ template <class _ExecutionPolicy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+adjacent_find(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "adjacent_find requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__adjacent_find, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), equal_to{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator adjacent_find(
+    _ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __predicate) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "adjacent_find requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__adjacent_find, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__predicate));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index d800e60916efc..1981883d2292e 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -332,6 +332,13 @@ struct __mismatch_3leg;
 // operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_find;
+// template <class _Policy, class _ForwardIterator, class _BinaryPredicate>
+// optional<_ForwardIterator>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last,
+//                                _BinaryPredicate __predicate) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index f4f73e81ed90b..c37d57858f46d 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 #define _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 
+#include <__algorithm/adjacent_find.h>
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
@@ -24,6 +25,7 @@
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
+#include <__iterator/prev.h>
 #include <__iterator/reverse_iterator.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
@@ -66,6 +68,7 @@ namespace __pstl {
 //
 // mismatch family
 // ---------------
+// - adjacent_find
 // - mismatch_3leg
 //
 // for_each family
@@ -248,6 +251,48 @@ struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_find<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator, class _BinaryPredicate>
+  optional<_ForwardIterator>
+  operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __predicate)
+      const noexcept {
+    if constexpr (__has_bidirectional_iterator_category<_ForwardIterator>::value) {
+      using _Mismatch = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      if (__first == __last) {
+        return __last; // Empty range, return __last.
+      }
+      _ForwardIterator __first2 = std::next(__first);
+      if (__first2 == __last) {
+        return __last; // Single element range, no adjacent elements, return __last.
+      }
+      _ForwardIterator __last2 = std::prev(__last);
+      // Find the first match within two overlapping ranges, expressed as a double negation of the predicate:
+      //   [first, __last - 1)
+      //   [first + 1, __last)
+      auto __res = _Mismatch()(
+          __policy,
+          std::move(__first),
+          std::move(__last2),
+          std::move(__first2),
+          __last,
+          [&](__iterator_reference<_ForwardIterator> __lhs, __iterator_reference<_ForwardIterator> __rhs) {
+            return !__predicate(__lhs, __rhs);
+          });
+      if (!__res) {
+        return nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      if (__res->second == __last) {
+        return __last; // No adjacent elements found, return __last.
+      }
+      return __res->first; // Return the first iterator of the pair of mismatched elements.
+    } else {
+      // Currently anything outside bidirectional iterators has to be processed serially
+      return std::adjacent_find(std::move(__first), std::move(__last), std::move(__predicate));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 4526cad0615b0..741bef52c1638 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -151,6 +151,12 @@ void f(non_forward_iterator non_fwd,
         pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
   }
 
+  {
+    (void)std::adjacent_find(pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_find}}
+    (void)std::adjacent_find(
+        pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: adjacent_find}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index c538d7841dce6..e00dccdafd459 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -66,4 +66,8 @@ void test() {
   std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_find(std::execution::par, std::begin(a), std::end(a));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_find(std::execution::par, std::begin(a), std::end(a), pred2);
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
new file mode 100644
index 0000000000000..ff0b5c05bd364
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
@@ -0,0 +1,164 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template<class ExecutionPolicy, class ForwardIterator>
+// ForwardIterator adjacent_find(ExecutionPolicy&& exec,
+//                               ForwardIterator first, ForwardIterator last);
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// TODO: switch to the shared implementation once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+// The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
+struct X {
+  X() = delete;
+  explicit X(int i) : i_(i) {}
+  X(const X&)            = delete;
+  X(X&&)                 = delete;
+  X& operator=(const X&) = delete;
+  X& operator=(X&&)      = delete;
+  bool operator==(const X& other) const { return i_ == other.i_; }
+
+private:
+  int i_;
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]  = {42};
+      auto res = std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)));
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    {
+      int a[] = {42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {42, 42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a) + 1)) == Iter(std::begin(a) + 1));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 0, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {0, 1, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 0, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {0, 1, 0, 1};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 1, 1};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 3), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 2, 7, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 0);
+      runway_sample(std::size(a), [&](std::size_t i) {
+        if (i == 0)
+          return; // skip the first element to avoid out-of-bounds access
+        a[i] = a[i - 1];
+        assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + i - 1));
+        a[i] = i;
+      });
+    }
+  }
+};
+
+template <class Iter>
+struct TestX {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    X a[] = {X(0), X(1), X(1), X(2)};
+    assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a))) == Iter(std::end(a)));
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<const X*>{}, TestIteratorWithPolicies<TestX>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
new file mode 100644
index 0000000000000..795632b0db4db
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
@@ -0,0 +1,173 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template<class ExecutionPolicy, class ForwardIterator, class BinaryPredicate>
+// ForwardIterator adjacent_find(ExecutionPolicy&& exec,
+//                               ForwardIterator first, ForwardIterator last,
+//                               BinaryPredicate pred);
+
+#include <algorithm>
+#include <cassert>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// TODO: switch to the shared implementation once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+// The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
+struct X {
+  X() = delete;
+  explicit X(int i) : i_(i) {}
+  X(const X&)            = delete;
+  X(X&&)                 = delete;
+  X& operator=(const X&) = delete;
+  X& operator=(X&&)      = delete;
+  int get_value() const { return i_; }
+
+private:
+  int i_;
+};
+
+struct Pred {
+  bool operator()(const X& a, const X& b) const {
+    return static_cast<long long>(b.get_value()) - static_cast<long long>(a.get_value()) == 42;
+  }
+  bool operator()(int a, int b) const { return static_cast<long long>(b) - static_cast<long long>(a) == 42; }
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]  = {42};
+      auto res = std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{});
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    {
+      int a[] = {42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {42, 84};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a) + 1), Pred{}) ==
+             Iter(std::begin(a) + 1));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 42, 84};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {0, 41, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 0, 42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {0, 41, 0, 41};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 43, 85};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[] = {0, 1, 2, 44, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 3), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 2, 45, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min() + 42,
+                 std::numeric_limits<int>::max() - 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max() - 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min() + 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 0);
+      runway_sample(std::size(a), [&](std::size_t i) {
+        if (i == 0)
+          return; // skip the first element to avoid out-of-bounds access
+        a[i] = a[i - 1] + 42;
+        assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) ==
+               Iter(std::begin(a) + i - 1));
+        a[i] = i;
+      });
+    }
+  }
+};
+
+template <class Iter>
+struct TestX {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    X a[] = {X(0), X(40), X(82), X(100)};
+    assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<const X*>{}, TestIteratorWithPolicies<TestX>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 820a5d92c19e0..c03d27cea5df1 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -273,6 +273,18 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto pred = maybe_throw(tokens[5], [](int a, int b) -> bool { return a == b; });
+
+        // adjacent_find(first, last)
+        assert_non_throwing([=, &policy] { (void)std::adjacent_find(policy, std::move(first1), std::move(last1)); });
+
+        // adjacent_find(first, last, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_find(policy, std::move(first1), std::move(last1), pred);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From cc1c78df96afdc9e4715a1bbdfb64d7b42fdf131 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 19 Jul 2026 17:25:35 +0100
Subject: [PATCH 16/16] Missing includes

---
 .../alg.adjacent.find/pstl.adjacent_find.pass.cpp              | 2 ++
 .../alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp         | 3 +++
 2 files changed, 5 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
index ff0b5c05bd364..8b83aa60bd421 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
@@ -18,10 +18,12 @@
 #include <cassert>
 #include <iterator>
 #include <numeric>
+#include <limits>
 
 #include "test_execution_policies.h"
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "type_algorithms.h"
 
 // TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
index 795632b0db4db..60cabad7ba014 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
@@ -18,10 +18,13 @@
 #include <algorithm>
 #include <cassert>
 #include <numeric>
+#include <iterator>
+#include <limits>
 
 #include "test_execution_policies.h"
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "type_algorithms.h"
 
 // TODO: switch to the shared implementation once it's merged into main
 template <class Callable>


From libcxx-commits at lists.llvm.org  Sun Jul 19 09:26:47 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 19 Jul 2026 09:26:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::mismatch() based on __pstl::__parallel_find() (PR #209291)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209291@github.com>
Message-ID: <6a5cfac7.3cce8983.3cf542.c223@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/209291

>From 650ebeba6d9e4867856e679d475e8f50edcdadde Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:03:38 +0100
Subject: [PATCH 01/14] Added a basic implementation of parallel 4-legged
 mismatch() based on __parallel_find()

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/libdispatch.h  |   7 +-
 libcxx/include/__pstl/backends/serial.h       |  20 +++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  88 +++++++++++
 .../mismatch/pstl.mismatch.pass.cpp           | 139 ++++++++++++++++++
 7 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__pstl/cpu_algos/mismatch.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..6b5dc87fbe76d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/mismatch.h  
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a84a768cad570..1d64c6f47630f 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -317,6 +317,14 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index 33fd5de66fbb1..710506585ca22 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -33,6 +33,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -237,7 +238,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
       auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
       auto __index           = __chunk == 0 ? 0
                                             : (__chunk * __partitions.__chunk_size_) +
-                                        (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
+                                                  (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
       if (__this_chunk_size != 1) {
         std::__construct_at(
             __values.get() + __chunk,
@@ -364,6 +365,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..d90022ee37145 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/mismatch.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    return std::mismatch(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::forward<_Predicate>(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..93935d22b9442 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
new file mode 100644
index 0000000000000..11ffdc332d633
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_MISMATCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
+
+#include <__algorithm/mismatch.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_mismatch {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Look for a mismatch only in the prefix of the two ranges.
+      auto __n = std::min(__last1 - __first1, __last2 - __first2);
+      // Find a position in the first range where the predicate is false against the corresponding position in the
+      // second range.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __first1 + __n,
+          [&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
+            // Run the sequential mismatch algorithm on these ranges:
+            //   [__brick_first1, __brick_last1) and
+            //   [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
+            auto __brick_first2 = __first2 + (__brick_first1 - __first1);
+            return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
+                .first;
+          },
+          less<>{},
+          true);
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      auto __idx = *__res - __first1;
+      return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      // Unsequenced execution is also implicitly covered by the sequential implementation.
+      return std::mismatch(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
new file mode 100644
index 0000000000000..9cbadda428ad9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 1};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(42);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From e96f0aa34d1b212b123d4c9917928e17d26270f3 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:05:01 +0100
Subject: [PATCH 02/14] Added a definition of a parallel 4-legged
 std::mismatch()

---
 libcxx/include/__algorithm/pstl.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2bf9b4276baab..ab35b1a3e537b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -138,6 +138,29 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,

>From 472174e7343fc86a77bf314c7d088331dab6bae5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:41:51 +0100
Subject: [PATCH 03/14] Added a default implementation of __mismatch_3leg based
 on __mismatch

---
 libcxx/include/__algorithm/pstl.h             | 18 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++++
 libcxx/include/__pstl/backends/default.h      | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../mismatch/pstl.mismatch.pass.cpp           | 20 +++++++++++
 5 files changed, 84 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index ab35b1a3e537b..01c61fb00f4d5 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -161,6 +161,24 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 1d64c6f47630f..d800e60916efc 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -325,6 +325,13 @@ struct __mismatch;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch_3leg;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5c78d4efa412b..05b328beae80f 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -63,6 +63,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// mismatch family
+// ---------------
+// - mismatch_3leg
+//
 // for_each family
 // ---------------
 // - for_each_n
@@ -211,6 +215,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// mismatch family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+  optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _Comp __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Forward to the 4-legged version of mismatch.
+      using _Mismatch           = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      _ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
+      return _Mismatch()(
+          __policy,
+          std::move(__first1),
+          std::move(__last1),
+          std::move(__first2),
+          std::move(__last2),
+          std::move(__comp));
+    } else {
+      // Currently only random access iterators are supported for parallel mismatch_3leg.
+      return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..752aa35643e2b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2384,6 +2384,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module mismatch {
+        header "__pstl/cpu_algos/mismatch.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 9cbadda428ad9..0cb5c7632eed8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -10,6 +10,14 @@
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2);
+//
 // template <class ExecutionPolicy,
 //           class ForwardIterator1,
 //           class ForwardIterator2>
@@ -61,6 +69,8 @@ struct Test {
       // empty ranges
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
              std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
     }
@@ -68,18 +78,24 @@ struct Test {
       // single element only
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range without mismatch
       std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range with mismatch
       std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
     }
@@ -102,10 +118,14 @@ struct Test {
       rhs = lhs;
       runway_sample(lhs.size(), [&](size_t i) {
         lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
                std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         lhs[i] = i;
       });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }

>From 4aab10d6cc54f3abd475bd3aae03ac7d4103eb45 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:20:42 +0100
Subject: [PATCH 04/14] Added predicated versions

---
 libcxx/include/__algorithm/pstl.h             |  49 +++++
 .../mismatch/pstl.mismatch.pass.cpp           |   2 +
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 178 ++++++++++++++++++
 3 files changed, 229 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 01c61fb00f4d5..bc48fd9f48d7f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
+#  include <__utility/pair.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -138,6 +139,31 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
@@ -161,6 +187,29 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 0cb5c7632eed8..8584f2779927f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -42,6 +42,8 @@
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
+static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
new file mode 100644
index 0000000000000..39e952cbc38f6
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     BinaryPredicate pred);
+//
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2,
+//                                                     BinaryPredicate pred);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+struct Pred {
+  bool operator()(int a, int b) const { return a * 2 == b; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      assert(std::mismatch(
+                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {1};
+      std::array<int, 1> rhs = {2};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 2};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(84);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From 2da95a5c532de58960651a18c9a93c9435577a89 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:39:47 +0100
Subject: [PATCH 05/14] Added tests against custom types

---
 libcxx/include/__pstl/backends/default.h      |  1 +
 .../mismatch/pstl.mismatch.pass.cpp           | 53 ++++++++++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 56 ++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 05b328beae80f..f4f73e81ed90b 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -16,6 +16,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 8584f2779927f..5e46831539c30 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -63,6 +63,30 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
+
 template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
@@ -150,6 +174,29 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -157,5 +204,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 39e952cbc38f6..9c0dfc6c0c127 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -67,8 +67,31 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
 struct Pred {
-  bool operator()(int a, int b) const { return a * 2 == b; }
+  bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
+  bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
 };
 
 template <class Iter1, class Iter2>
@@ -167,6 +190,31 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -174,5 +222,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }

>From 2fb2bb1a154212dc1cbf3a1ea9e3e1cf5a00db48 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:52:50 +0100
Subject: [PATCH 06/14] Added a nodiscard test

---
 libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 049c8dc88c2da..c538d7841dce6 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -58,4 +58,12 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }

>From 8c35d0cac6fc6a3daf494de08ac0a2fd365d50f1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:57:47 +0100
Subject: [PATCH 07/14] Added an iterator requirement test

---
 .../pstl.iterator-requirements.verify.cpp        | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index fbe3460b67f17..4526cad0615b0 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, pred);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}

>From a8b984852860283d06ab06bbdf05a28f01965326 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:00:09 +0100
Subject: [PATCH 08/14] Added an include of <__algorithm/min.h>

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 11ffdc332d633..8361731e78cfc 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,8 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__algorithm/mismatch.h>
 #include <__config>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/min.h>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>

>From 3845b7e0e242d455f0fcc68a814ed625544c3d8c Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:05:29 +0100
Subject: [PATCH 09/14] Added an exception handling test

---
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  4 +--
 .../pstl.exception_handling.pass.cpp          | 25 +++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 8361731e78cfc..597a47fb185c8 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,9 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__config>
-#include <__algorithm/mismatch.h>
 #include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__config>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 8af2a0a15fd5c..820a5d92c19e0 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -248,6 +248,31 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // mismatch(first1, last1, first2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
+        });
+
+        // mismatch(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // mismatch(first1, last1, first2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
+        });
+
+        // mismatch(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From fbc073f5742c81ae78d12058ecf4618213afb6f0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 09:43:45 +0100
Subject: [PATCH 10/14] Changed the variable name to avoid shadowing

---
 .../alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 12 ++++++------
 .../mismatch/pstl.mismatch_pred.pass.cpp             | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 5e46831539c30..941fe07b58327 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -67,22 +67,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 9c0dfc6c0c127..a4b15a56bd071 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -71,22 +71,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Pred {

>From baaa82257b3d2f94d4bf9d02f6761e7cf3ee9df6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 20:27:14 +0100
Subject: [PATCH 11/14] Use raw C array in the tests

---
 .../mismatch/pstl.mismatch.pass.cpp           | 153 +++++++-------
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 192 +++++++++++-------
 2 files changed, 198 insertions(+), 147 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 941fe07b58327..17edf34ee7832 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -28,7 +28,6 @@
 //                                                     ForwardIterator2 last2);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -93,82 +92,98 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(
+          std::mismatch(
+              policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
+          std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 1};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 1};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(
+            std::mismatch(
+                policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+            std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(42);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 42);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(
+                   policy,
+                   Iter1(std::begin(lhs)),
+                   Iter1(std::begin(lhs) + i),
+                   Iter2(std::begin(rhs)),
+                   Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i)) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -179,20 +194,22 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index a4b15a56bd071..18c4fe1abad0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -32,7 +32,6 @@
 //                                                     BinaryPredicate pred);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -100,91 +99,120 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(
-                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::begin(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::begin(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {1};
-      std::array<int, 1> rhs = {2};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {1};
+      int rhs[1] = {2};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 2};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 2};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(84);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 84);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::begin(lhs) + i),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -195,22 +223,28 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };

>From 212e25406377a6a5da7fc7679015000a4086e4a1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:08:55 +0100
Subject: [PATCH 12/14] Fixed typos in the comment

---
 .../algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 2 +-
 .../alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 17edf34ee7832..ed59fa9eca669 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -46,7 +46,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 18c4fe1abad0c..338503d784a31 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -50,7 +50,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;

>From b80675016128dbeb1db5a3084edee9bb6ab89fea Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:43:20 +0100
Subject: [PATCH 13/14] Added comments explaining what the parameters mean

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 597a47fb185c8..3d702b03d3afe 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -63,8 +63,9 @@ struct __cpu_parallel_mismatch {
             return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
                 .first;
           },
-          less<>{},
-          true);
+          less<>{}, // `less` here means the lowest index among the mismatches
+          true      // `true` here means we want the first mismatch, not the last
+      );
       if (!__res) {
         return std::nullopt; // Failed to run the algorithm, propagate the error.
       }

>From 12ac822e8571b5d944ffea6d4f62396a954f4672 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:50:51 +0100
Subject: [PATCH 14/14] Added tests of the return types

---
 .../mismatch/pstl.mismatch.pass.cpp              | 10 ++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp         | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index ed59fa9eca669..cc7ef6feb2356 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -90,6 +90,16 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1]       = {0};
+      int rhs[1]       = {0};
+      auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
+      auto res_4legged = std::mismatch(
+          policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 338503d784a31..d80da0cf0b542 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -97,6 +97,22 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      auto res_3legged =
+          std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
+      auto res_4legged = std::mismatch(
+          policy,
+          Iter1(std::begin(lhs)),
+          Iter1(std::begin(lhs)),
+          Iter2(std::begin(rhs)),
+          Iter2(std::begin(rhs)),
+          Pred{});
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};


From libcxx-commits at lists.llvm.org  Sun Jul 19 09:43:28 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 09:43:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5cfeb0.9edb5c9d.280be3.c2b1@mx.google.com>


================
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
----------------
frederick-vs-ja wrote:

> Yes sorry. But given changes are necessary, should I fix the PR title wording?
> Do you think we should also apply LWG4242 within this scope? It seems rather small but I'm not fully sure about the implications.

Why changes were necessary? I don't think we shouldn't "fix" the PR title or implement LWG4242 together, as I think it's better to perform "pure" documentation update (and test improvement) in this PR.

https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 09:43:35 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 09:43:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5cfeb7.449bfb44.7b862.5a3e@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 10:09:31 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 10:09:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5d04cb.27b3b003.1f84cc.c4fb@mx.google.com>


================
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
----------------
frederick-vs-ja wrote:

I _think_ we can support these on Windows (or at least, some of these). I added workaround in #149234 which should work.
```suggestion
```

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Sun Jul 19 10:09:46 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 10:09:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5d04da.6c8668f2.1f24.e022@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Sun Jul 19 10:41:26 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 10:41:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Suppress -Wmicrosoft-cast
 in MSVC's exception_ptr (PR #210570)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210570@github.com>
Message-ID: <6a5d0c46.f06f4994.2f0620.29d5@mx.google.com>


https://github.com/frederick-vs-ja milestoned https://github.com/llvm/llvm-project/pull/210570

From libcxx-commits at lists.llvm.org  Sun Jul 19 10:51:47 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 10:51:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Suppress -Wmicrosoft-cast
 in MSVC's exception_ptr (PR #210570)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210570@github.com>
Message-ID: <6a5d0eb3.c56fb102.2d07b8.1d3c@mx.google.com>


frederick-vs-ja wrote:

Most LGTM. The PR title looks a bit ambiguous to me. Perhaps it's better to clarify that we're using `reinterpret_cast` properly instead of using `#pragma clang diagnostic ignored`.

https://github.com/llvm/llvm-project/pull/210570

From libcxx-commits at lists.llvm.org  Sun Jul 19 10:54:57 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 19 Jul 2026 10:54:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Too many
 `_LIBCPP_INTRODUCED_IN_LLVM_23` macros (PR #210602)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210602@github.com>
Message-ID: <6a5d0f71.66c0679a.24976c.116b@mx.google.com>


https://github.com/philnik777 commented:

Could you write the title in way that says what you're doing? The current title is really confusing IMO.

https://github.com/llvm/llvm-project/pull/210602

From libcxx-commits at lists.llvm.org  Sun Jul 19 11:01:42 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 11:01:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Removed duplicated
 `_LIBCPP_INTRODUCED_IN_LLVM_23` macros (PR #210602)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210602@github.com>
Message-ID: <6a5d1106.d681cab9.169270.e8cb@mx.google.com>


https://github.com/H-G-Hristov edited https://github.com/llvm/llvm-project/pull/210602

From libcxx-commits at lists.llvm.org  Sun Jul 19 11:02:46 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 11:02:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Removed duplicated
 `_LIBCPP_INTRODUCED_IN_LLVM_23` macros (PR #210602)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210602@github.com>
Message-ID: <6a5d1146.824e11a6.9c376.00a5@mx.google.com>


https://github.com/H-G-Hristov edited https://github.com/llvm/llvm-project/pull/210602

From libcxx-commits at lists.llvm.org  Sun Jul 19 11:16:25 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Sun, 19 Jul 2026 11:16:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::adjacent_find() based on std::mismatch() (PR #210604)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210604@github.com>
Message-ID: <6a5d1479.66c0679a.24976c.52f7@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/210604

>From 650ebeba6d9e4867856e679d475e8f50edcdadde Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:03:38 +0100
Subject: [PATCH 01/17] Added a basic implementation of parallel 4-legged
 mismatch() based on __parallel_find()

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/libdispatch.h  |   7 +-
 libcxx/include/__pstl/backends/serial.h       |  20 +++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  88 +++++++++++
 .../mismatch/pstl.mismatch.pass.cpp           | 139 ++++++++++++++++++
 7 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__pstl/cpu_algos/mismatch.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..6b5dc87fbe76d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/mismatch.h  
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a84a768cad570..1d64c6f47630f 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -317,6 +317,14 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index 33fd5de66fbb1..710506585ca22 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -33,6 +33,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -237,7 +238,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
       auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
       auto __index           = __chunk == 0 ? 0
                                             : (__chunk * __partitions.__chunk_size_) +
-                                        (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
+                                                  (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
       if (__this_chunk_size != 1) {
         std::__construct_at(
             __values.get() + __chunk,
@@ -364,6 +365,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..d90022ee37145 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/mismatch.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    return std::mismatch(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::forward<_Predicate>(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..93935d22b9442 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
new file mode 100644
index 0000000000000..11ffdc332d633
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_MISMATCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
+
+#include <__algorithm/mismatch.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_mismatch {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Look for a mismatch only in the prefix of the two ranges.
+      auto __n = std::min(__last1 - __first1, __last2 - __first2);
+      // Find a position in the first range where the predicate is false against the corresponding position in the
+      // second range.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __first1 + __n,
+          [&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
+            // Run the sequential mismatch algorithm on these ranges:
+            //   [__brick_first1, __brick_last1) and
+            //   [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
+            auto __brick_first2 = __first2 + (__brick_first1 - __first1);
+            return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
+                .first;
+          },
+          less<>{},
+          true);
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      auto __idx = *__res - __first1;
+      return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      // Unsequenced execution is also implicitly covered by the sequential implementation.
+      return std::mismatch(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
new file mode 100644
index 0000000000000..9cbadda428ad9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 1};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(42);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From e96f0aa34d1b212b123d4c9917928e17d26270f3 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:05:01 +0100
Subject: [PATCH 02/17] Added a definition of a parallel 4-legged
 std::mismatch()

---
 libcxx/include/__algorithm/pstl.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2bf9b4276baab..ab35b1a3e537b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -138,6 +138,29 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,

>From 472174e7343fc86a77bf314c7d088331dab6bae5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:41:51 +0100
Subject: [PATCH 03/17] Added a default implementation of __mismatch_3leg based
 on __mismatch

---
 libcxx/include/__algorithm/pstl.h             | 18 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++++
 libcxx/include/__pstl/backends/default.h      | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../mismatch/pstl.mismatch.pass.cpp           | 20 +++++++++++
 5 files changed, 84 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index ab35b1a3e537b..01c61fb00f4d5 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -161,6 +161,24 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 1d64c6f47630f..d800e60916efc 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -325,6 +325,13 @@ struct __mismatch;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch_3leg;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5c78d4efa412b..05b328beae80f 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -63,6 +63,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// mismatch family
+// ---------------
+// - mismatch_3leg
+//
 // for_each family
 // ---------------
 // - for_each_n
@@ -211,6 +215,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// mismatch family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+  optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _Comp __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Forward to the 4-legged version of mismatch.
+      using _Mismatch           = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      _ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
+      return _Mismatch()(
+          __policy,
+          std::move(__first1),
+          std::move(__last1),
+          std::move(__first2),
+          std::move(__last2),
+          std::move(__comp));
+    } else {
+      // Currently only random access iterators are supported for parallel mismatch_3leg.
+      return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..752aa35643e2b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2384,6 +2384,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module mismatch {
+        header "__pstl/cpu_algos/mismatch.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 9cbadda428ad9..0cb5c7632eed8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -10,6 +10,14 @@
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2);
+//
 // template <class ExecutionPolicy,
 //           class ForwardIterator1,
 //           class ForwardIterator2>
@@ -61,6 +69,8 @@ struct Test {
       // empty ranges
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
              std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
     }
@@ -68,18 +78,24 @@ struct Test {
       // single element only
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range without mismatch
       std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range with mismatch
       std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
     }
@@ -102,10 +118,14 @@ struct Test {
       rhs = lhs;
       runway_sample(lhs.size(), [&](size_t i) {
         lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
                std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         lhs[i] = i;
       });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }

>From 4aab10d6cc54f3abd475bd3aae03ac7d4103eb45 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:20:42 +0100
Subject: [PATCH 04/17] Added predicated versions

---
 libcxx/include/__algorithm/pstl.h             |  49 +++++
 .../mismatch/pstl.mismatch.pass.cpp           |   2 +
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 178 ++++++++++++++++++
 3 files changed, 229 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 01c61fb00f4d5..bc48fd9f48d7f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
+#  include <__utility/pair.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -138,6 +139,31 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
@@ -161,6 +187,29 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 0cb5c7632eed8..8584f2779927f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -42,6 +42,8 @@
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
+static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
new file mode 100644
index 0000000000000..39e952cbc38f6
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     BinaryPredicate pred);
+//
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2,
+//                                                     BinaryPredicate pred);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+struct Pred {
+  bool operator()(int a, int b) const { return a * 2 == b; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      assert(std::mismatch(
+                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {1};
+      std::array<int, 1> rhs = {2};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 2};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(84);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From 2da95a5c532de58960651a18c9a93c9435577a89 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:39:47 +0100
Subject: [PATCH 05/17] Added tests against custom types

---
 libcxx/include/__pstl/backends/default.h      |  1 +
 .../mismatch/pstl.mismatch.pass.cpp           | 53 ++++++++++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 56 ++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 05b328beae80f..f4f73e81ed90b 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -16,6 +16,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 8584f2779927f..5e46831539c30 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -63,6 +63,30 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
+
 template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
@@ -150,6 +174,29 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -157,5 +204,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 39e952cbc38f6..9c0dfc6c0c127 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -67,8 +67,31 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
 struct Pred {
-  bool operator()(int a, int b) const { return a * 2 == b; }
+  bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
+  bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
 };
 
 template <class Iter1, class Iter2>
@@ -167,6 +190,31 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -174,5 +222,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }

>From 2fb2bb1a154212dc1cbf3a1ea9e3e1cf5a00db48 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:52:50 +0100
Subject: [PATCH 06/17] Added a nodiscard test

---
 libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 049c8dc88c2da..c538d7841dce6 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -58,4 +58,12 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }

>From 8c35d0cac6fc6a3daf494de08ac0a2fd365d50f1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:57:47 +0100
Subject: [PATCH 07/17] Added an iterator requirement test

---
 .../pstl.iterator-requirements.verify.cpp        | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index fbe3460b67f17..4526cad0615b0 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, pred);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}

>From a8b984852860283d06ab06bbdf05a28f01965326 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:00:09 +0100
Subject: [PATCH 08/17] Added an include of <__algorithm/min.h>

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 11ffdc332d633..8361731e78cfc 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,8 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__algorithm/mismatch.h>
 #include <__config>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/min.h>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>

>From 3845b7e0e242d455f0fcc68a814ed625544c3d8c Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:05:29 +0100
Subject: [PATCH 09/17] Added an exception handling test

---
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  4 +--
 .../pstl.exception_handling.pass.cpp          | 25 +++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 8361731e78cfc..597a47fb185c8 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,9 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__config>
-#include <__algorithm/mismatch.h>
 #include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__config>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 8af2a0a15fd5c..820a5d92c19e0 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -248,6 +248,31 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // mismatch(first1, last1, first2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
+        });
+
+        // mismatch(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // mismatch(first1, last1, first2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
+        });
+
+        // mismatch(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From fbc073f5742c81ae78d12058ecf4618213afb6f0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 09:43:45 +0100
Subject: [PATCH 10/17] Changed the variable name to avoid shadowing

---
 .../alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 12 ++++++------
 .../mismatch/pstl.mismatch_pred.pass.cpp             | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 5e46831539c30..941fe07b58327 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -67,22 +67,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 9c0dfc6c0c127..a4b15a56bd071 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -71,22 +71,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Pred {

>From baaa82257b3d2f94d4bf9d02f6761e7cf3ee9df6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 20:27:14 +0100
Subject: [PATCH 11/17] Use raw C array in the tests

---
 .../mismatch/pstl.mismatch.pass.cpp           | 153 +++++++-------
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 192 +++++++++++-------
 2 files changed, 198 insertions(+), 147 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 941fe07b58327..17edf34ee7832 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -28,7 +28,6 @@
 //                                                     ForwardIterator2 last2);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -93,82 +92,98 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(
+          std::mismatch(
+              policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
+          std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 1};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 1};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(
+            std::mismatch(
+                policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+            std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(42);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 42);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(
+                   policy,
+                   Iter1(std::begin(lhs)),
+                   Iter1(std::begin(lhs) + i),
+                   Iter2(std::begin(rhs)),
+                   Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i)) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -179,20 +194,22 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index a4b15a56bd071..18c4fe1abad0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -32,7 +32,6 @@
 //                                                     BinaryPredicate pred);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -100,91 +99,120 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(
-                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::begin(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::begin(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {1};
-      std::array<int, 1> rhs = {2};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {1};
+      int rhs[1] = {2};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 2};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 2};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(84);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 84);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::begin(lhs) + i),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -195,22 +223,28 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };

>From 212e25406377a6a5da7fc7679015000a4086e4a1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:08:55 +0100
Subject: [PATCH 12/17] Fixed typos in the comment

---
 .../algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 2 +-
 .../alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 17edf34ee7832..ed59fa9eca669 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -46,7 +46,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 18c4fe1abad0c..338503d784a31 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -50,7 +50,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;

>From b80675016128dbeb1db5a3084edee9bb6ab89fea Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:43:20 +0100
Subject: [PATCH 13/17] Added comments explaining what the parameters mean

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 597a47fb185c8..3d702b03d3afe 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -63,8 +63,9 @@ struct __cpu_parallel_mismatch {
             return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
                 .first;
           },
-          less<>{},
-          true);
+          less<>{}, // `less` here means the lowest index among the mismatches
+          true      // `true` here means we want the first mismatch, not the last
+      );
       if (!__res) {
         return std::nullopt; // Failed to run the algorithm, propagate the error.
       }

>From 12ac822e8571b5d944ffea6d4f62396a954f4672 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:50:51 +0100
Subject: [PATCH 14/17] Added tests of the return types

---
 .../mismatch/pstl.mismatch.pass.cpp              | 10 ++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp         | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index ed59fa9eca669..cc7ef6feb2356 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -90,6 +90,16 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1]       = {0};
+      int rhs[1]       = {0};
+      auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
+      auto res_4legged = std::mismatch(
+          policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 338503d784a31..d80da0cf0b542 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -97,6 +97,22 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      auto res_3legged =
+          std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
+      auto res_4legged = std::mismatch(
+          policy,
+          Iter1(std::begin(lhs)),
+          Iter1(std::begin(lhs)),
+          Iter2(std::begin(rhs)),
+          Iter2(std::begin(rhs)),
+          Pred{});
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};

>From 01513beffed6c7812b59d62ab4cdae683c9e0c10 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 19 Jul 2026 16:27:43 +0100
Subject: [PATCH 15/17] Added an implementation of adjacent_find() based on
 mismatch()

---
 libcxx/include/__algorithm/pstl.h             |  25 +++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  45 +++++
 .../pstl.iterator-requirements.verify.cpp     |   6 +
 .../algorithms/pstl.nodiscard.verify.cpp      |   4 +
 .../pstl.adjacent_find.pass.cpp               | 164 +++++++++++++++++
 .../pstl.adjacent_find_pred.pass.cpp          | 173 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  12 ++
 8 files changed, 436 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index bc48fd9f48d7f..0447691c3b86b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -228,6 +228,31 @@ template <class _ExecutionPolicy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+adjacent_find(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "adjacent_find requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__adjacent_find, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), equal_to{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator adjacent_find(
+    _ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __predicate) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "adjacent_find requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__adjacent_find, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__predicate));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index d800e60916efc..1981883d2292e 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -332,6 +332,13 @@ struct __mismatch_3leg;
 // operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_find;
+// template <class _Policy, class _ForwardIterator, class _BinaryPredicate>
+// optional<_ForwardIterator>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last,
+//                                _BinaryPredicate __predicate) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index f4f73e81ed90b..c37d57858f46d 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 #define _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 
+#include <__algorithm/adjacent_find.h>
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
@@ -24,6 +25,7 @@
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
+#include <__iterator/prev.h>
 #include <__iterator/reverse_iterator.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
@@ -66,6 +68,7 @@ namespace __pstl {
 //
 // mismatch family
 // ---------------
+// - adjacent_find
 // - mismatch_3leg
 //
 // for_each family
@@ -248,6 +251,48 @@ struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_find<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator, class _BinaryPredicate>
+  optional<_ForwardIterator>
+  operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __predicate)
+      const noexcept {
+    if constexpr (__has_bidirectional_iterator_category<_ForwardIterator>::value) {
+      using _Mismatch = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      if (__first == __last) {
+        return __last; // Empty range, return __last.
+      }
+      _ForwardIterator __first2 = std::next(__first);
+      if (__first2 == __last) {
+        return __last; // Single element range, no adjacent elements, return __last.
+      }
+      _ForwardIterator __last2 = std::prev(__last);
+      // Find the first match within two overlapping ranges, expressed as a double negation of the predicate:
+      //   [first, __last - 1)
+      //   [first + 1, __last)
+      auto __res = _Mismatch()(
+          __policy,
+          std::move(__first),
+          std::move(__last2),
+          std::move(__first2),
+          __last,
+          [&](__iterator_reference<_ForwardIterator> __lhs, __iterator_reference<_ForwardIterator> __rhs) {
+            return !__predicate(__lhs, __rhs);
+          });
+      if (!__res) {
+        return nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      if (__res->second == __last) {
+        return __last; // No adjacent elements found, return __last.
+      }
+      return __res->first; // Return the first iterator of the pair of mismatched elements.
+    } else {
+      // Currently anything outside bidirectional iterators has to be processed serially
+      return std::adjacent_find(std::move(__first), std::move(__last), std::move(__predicate));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 4526cad0615b0..741bef52c1638 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -151,6 +151,12 @@ void f(non_forward_iterator non_fwd,
         pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
   }
 
+  {
+    (void)std::adjacent_find(pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_find}}
+    (void)std::adjacent_find(
+        pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: adjacent_find}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index c538d7841dce6..e00dccdafd459 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -66,4 +66,8 @@ void test() {
   std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_find(std::execution::par, std::begin(a), std::end(a));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_find(std::execution::par, std::begin(a), std::end(a), pred2);
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
new file mode 100644
index 0000000000000..ff0b5c05bd364
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
@@ -0,0 +1,164 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template<class ExecutionPolicy, class ForwardIterator>
+// ForwardIterator adjacent_find(ExecutionPolicy&& exec,
+//                               ForwardIterator first, ForwardIterator last);
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// TODO: switch to the shared implementation once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+// The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
+struct X {
+  X() = delete;
+  explicit X(int i) : i_(i) {}
+  X(const X&)            = delete;
+  X(X&&)                 = delete;
+  X& operator=(const X&) = delete;
+  X& operator=(X&&)      = delete;
+  bool operator==(const X& other) const { return i_ == other.i_; }
+
+private:
+  int i_;
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]  = {42};
+      auto res = std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)));
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    {
+      int a[] = {42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {42, 42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a) + 1)) == Iter(std::begin(a) + 1));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 0, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {0, 1, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 0, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {0, 1, 0, 1};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 1, 1};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 3), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 2, 7, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 0);
+      runway_sample(std::size(a), [&](std::size_t i) {
+        if (i == 0)
+          return; // skip the first element to avoid out-of-bounds access
+        a[i] = a[i - 1];
+        assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + i - 1));
+        a[i] = i;
+      });
+    }
+  }
+};
+
+template <class Iter>
+struct TestX {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    X a[] = {X(0), X(1), X(1), X(2)};
+    assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a))) == Iter(std::end(a)));
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<const X*>{}, TestIteratorWithPolicies<TestX>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
new file mode 100644
index 0000000000000..795632b0db4db
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
@@ -0,0 +1,173 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template<class ExecutionPolicy, class ForwardIterator, class BinaryPredicate>
+// ForwardIterator adjacent_find(ExecutionPolicy&& exec,
+//                               ForwardIterator first, ForwardIterator last,
+//                               BinaryPredicate pred);
+
+#include <algorithm>
+#include <cassert>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// TODO: switch to the shared implementation once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+// The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
+struct X {
+  X() = delete;
+  explicit X(int i) : i_(i) {}
+  X(const X&)            = delete;
+  X(X&&)                 = delete;
+  X& operator=(const X&) = delete;
+  X& operator=(X&&)      = delete;
+  int get_value() const { return i_; }
+
+private:
+  int i_;
+};
+
+struct Pred {
+  bool operator()(const X& a, const X& b) const {
+    return static_cast<long long>(b.get_value()) - static_cast<long long>(a.get_value()) == 42;
+  }
+  bool operator()(int a, int b) const { return static_cast<long long>(b) - static_cast<long long>(a) == 42; }
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]  = {42};
+      auto res = std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{});
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    {
+      int a[] = {42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {42, 84};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a) + 1), Pred{}) ==
+             Iter(std::begin(a) + 1));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 42, 84};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {0, 41, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 0, 42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {0, 41, 0, 41};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 43, 85};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[] = {0, 1, 2, 44, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 3), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 2, 45, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min() + 42,
+                 std::numeric_limits<int>::max() - 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max() - 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min() + 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 0);
+      runway_sample(std::size(a), [&](std::size_t i) {
+        if (i == 0)
+          return; // skip the first element to avoid out-of-bounds access
+        a[i] = a[i - 1] + 42;
+        assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) ==
+               Iter(std::begin(a) + i - 1));
+        a[i] = i;
+      });
+    }
+  }
+};
+
+template <class Iter>
+struct TestX {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    X a[] = {X(0), X(40), X(82), X(100)};
+    assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<const X*>{}, TestIteratorWithPolicies<TestX>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 820a5d92c19e0..c03d27cea5df1 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -273,6 +273,18 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto pred = maybe_throw(tokens[5], [](int a, int b) -> bool { return a == b; });
+
+        // adjacent_find(first, last)
+        assert_non_throwing([=, &policy] { (void)std::adjacent_find(policy, std::move(first1), std::move(last1)); });
+
+        // adjacent_find(first, last, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_find(policy, std::move(first1), std::move(last1), pred);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From cc1c78df96afdc9e4715a1bbdfb64d7b42fdf131 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 19 Jul 2026 17:25:35 +0100
Subject: [PATCH 16/17] Missing includes

---
 .../alg.adjacent.find/pstl.adjacent_find.pass.cpp              | 2 ++
 .../alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp         | 3 +++
 2 files changed, 5 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
index ff0b5c05bd364..8b83aa60bd421 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
@@ -18,10 +18,12 @@
 #include <cassert>
 #include <iterator>
 #include <numeric>
+#include <limits>
 
 #include "test_execution_policies.h"
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "type_algorithms.h"
 
 // TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
index 795632b0db4db..60cabad7ba014 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
@@ -18,10 +18,13 @@
 #include <algorithm>
 #include <cassert>
 #include <numeric>
+#include <iterator>
+#include <limits>
 
 #include "test_execution_policies.h"
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "type_algorithms.h"
 
 // TODO: switch to the shared implementation once it's merged into main
 template <class Callable>

>From bf8a3670150fcf9e5286e887fb6add7f68338fd3 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 19 Jul 2026 19:16:03 +0100
Subject: [PATCH 17/17] Fixed variable shadowing

---
 libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index c03d27cea5df1..e20e5f101bd77 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -274,7 +274,7 @@ int main(int, char**) {
       }
 
       {
-        auto pred = maybe_throw(tokens[5], [](int a, int b) -> bool { return a == b; });
+        auto pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; });
 
         // adjacent_find(first, last)
         assert_non_throwing([=, &policy] { (void)std::adjacent_find(policy, std::move(first1), std::move(last1)); });


From libcxx-commits at lists.llvm.org  Sun Jul 19 12:06:53 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sun, 19 Jul 2026 12:06:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5d204d.e5907290.281687.3844@mx.google.com>


================
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
----------------
lknknm wrote:

Changes were applied conforming the proposal where `iter_difference_t<_Ip>` became `iter_difference_t<decay_t<_Ip>>` and the `static_cast` applied to `return last - static_cast<decay_t<I>>(first);` here in this diff.

https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 12:07:04 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sun, 19 Jul 2026 12:07:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5d2058.d76d81af.23b347.57c2@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 12:07:43 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sun, 19 Jul 2026 12:07:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5d207f.27b3b003.1f84cc.68c5@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 12:11:41 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sun, 19 Jul 2026 12:11:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5d216d.13be6df8.22eac4.ae85@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 13:09:45 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 13:09:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Fix: Empty namespaces
	should be avoided (PR #210624)
Message-ID: <llvm.org/llvm/llvm-project/pull/210624@github.com>


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

Context: Including `<__utility/cmp.h>` causes:

> cmp.h:25:1: error: Empty namespaces should be avoided. Move any checks around the namespace instead. [libcpp-avoid-empty-namespaces,-warnings-as-errors]

>From 6c6f15db0451f58cd2b2af7b644cd3cc29e41dbd Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sun, 19 Jul 2026 23:09:25 +0300
Subject: [PATCH] [libc++][NFC] Fix: Empty namespaces should be avoided

Context: Including `<__utility/cmp.h>` causes:

> cmp.h:25:1: error: Empty namespaces should be avoided. Move any checks around the namespace instead. [libcpp-avoid-empty-namespaces,-warnings-as-errors]
---
 libcxx/include/__utility/cmp.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__utility/cmp.h b/libcxx/include/__utility/cmp.h
index 7cfe640ceb423..360cf2eaa2aa4 100644
--- a/libcxx/include/__utility/cmp.h
+++ b/libcxx/include/__utility/cmp.h
@@ -22,10 +22,10 @@
 _LIBCPP_PUSH_MACROS
 #include <__undef_macros>
 
-_LIBCPP_BEGIN_NAMESPACE_STD
-
 #if _LIBCPP_STD_VER >= 20
 
+_LIBCPP_BEGIN_NAMESPACE_STD
+
 template <typename _Tp, typename _Ip>
 concept __comparison_can_promote_to =
     sizeof(_Tp) < sizeof(_Ip) || (sizeof(_Tp) == sizeof(_Ip) && __signed_integer<_Tp>);
@@ -84,10 +84,10 @@ template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
          std::cmp_greater_equal(__u, numeric_limits<_Tp>::min());
 }
 
-#endif // _LIBCPP_STD_VER >= 20
-
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_STD_VER >= 20
+
 _LIBCPP_POP_MACROS
 
 #endif // _LIBCPP___UTILITY_CMP_H


From libcxx-commits at lists.llvm.org  Sun Jul 19 13:10:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 13:10:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Fix: Empty namespaces
	should be avoided (PR #210624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210624@github.com>
Message-ID: <6a5d2f32.e5907290.281687.ba8d@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

<details>
<summary>Changes</summary>

Context: Including `&lt;__utility/cmp.h&gt;` causes:

&gt; cmp.h:25:1: error: Empty namespaces should be avoided. Move any checks around the namespace instead. [libcpp-avoid-empty-namespaces,-warnings-as-errors]

---
Full diff: https://github.com/llvm/llvm-project/pull/210624.diff


1 Files Affected:

- (modified) libcxx/include/__utility/cmp.h (+4-4) 


``````````diff
diff --git a/libcxx/include/__utility/cmp.h b/libcxx/include/__utility/cmp.h
index 7cfe640ceb423..360cf2eaa2aa4 100644
--- a/libcxx/include/__utility/cmp.h
+++ b/libcxx/include/__utility/cmp.h
@@ -22,10 +22,10 @@
 _LIBCPP_PUSH_MACROS
 #include <__undef_macros>
 
-_LIBCPP_BEGIN_NAMESPACE_STD
-
 #if _LIBCPP_STD_VER >= 20
 
+_LIBCPP_BEGIN_NAMESPACE_STD
+
 template <typename _Tp, typename _Ip>
 concept __comparison_can_promote_to =
     sizeof(_Tp) < sizeof(_Ip) || (sizeof(_Tp) == sizeof(_Ip) && __signed_integer<_Tp>);
@@ -84,10 +84,10 @@ template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
          std::cmp_greater_equal(__u, numeric_limits<_Tp>::min());
 }
 
-#endif // _LIBCPP_STD_VER >= 20
-
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_STD_VER >= 20
+
 _LIBCPP_POP_MACROS
 
 #endif // _LIBCPP___UTILITY_CMP_H

``````````

</details>


https://github.com/llvm/llvm-project/pull/210624

From libcxx-commits at lists.llvm.org  Sun Jul 19 13:10:38 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 13:10:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][cmp][NFC] Fix: Empty namespaces
 should be avoided (PR #210624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210624@github.com>
Message-ID: <6a5d2f3e.481afadf.9e047.6bd2@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/210624

From libcxx-commits at lists.llvm.org  Sun Jul 19 13:10:51 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 13:10:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][cmp][NFC] Fix: Empty namespaces
 should be avoided (PR #210624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210624@github.com>
Message-ID: <6a5d2f4b.0c05e68e.3c2f5d.d08d@mx.google.com>


Zingam wrote:

For example: https://github.com/llvm/llvm-project/actions/runs/29698408259/job/88225567787?pr=83541

https://github.com/llvm/llvm-project/pull/210624

From libcxx-commits at lists.llvm.org  Sun Jul 19 13:41:27 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Sun, 19 Jul 2026 13:41:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Suppress -Wmicrosoft-cast
 in MSVC's exception_ptr (PR #210570)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210570@github.com>
Message-ID: <6a5d3677.4987c784.2c60a9.8b3c@mx.google.com>


https://github.com/petrhosek edited https://github.com/llvm/llvm-project/pull/210570

From libcxx-commits at lists.llvm.org  Sun Jul 19 13:41:41 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Sun, 19 Jul 2026 13:41:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Suppress -Wmicrosoft-cast
 in MSVC's exception_ptr (PR #210570)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210570@github.com>
Message-ID: <6a5d3685.4a342cbc.3a2f36.afff@mx.google.com>


https://github.com/petrhosek edited https://github.com/llvm/llvm-project/pull/210570

From libcxx-commits at lists.llvm.org  Sun Jul 19 13:41:49 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Sun, 19 Jul 2026 13:41:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Address -Wmicrosoft-cast in
 MSVC's exception_ptr (PR #210570)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210570@github.com>
Message-ID: <6a5d368d.13be6df8.22eac4.a4ea@mx.google.com>


https://github.com/petrhosek edited https://github.com/llvm/llvm-project/pull/210570

From libcxx-commits at lists.llvm.org  Sun Jul 19 13:42:03 2026
From: libcxx-commits at lists.llvm.org (Petr Hosek via libcxx-commits)
Date: Sun, 19 Jul 2026 13:42:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Address -Wmicrosoft-cast in
 MSVC's exception_ptr (PR #210570)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210570@github.com>
Message-ID: <6a5d369b.743400b0.23a7b2.cff3@mx.google.com>


petrhosek wrote:

> Most LGTM. The PR title looks a bit ambiguous to me. Perhaps it's better to clarify that we're using `reinterpret_cast` properly instead of using `#pragma clang diagnostic ignored`.

Done.

https://github.com/llvm/llvm-project/pull/210570

From libcxx-commits at lists.llvm.org  Sun Jul 19 15:11:49 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sun, 19 Jul 2026 15:11:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5d4ba5.492e23e4.d19bc.c521@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 16:20:15 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sun, 19 Jul 2026 16:20:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5d5baf.4987c784.2c60a9.64fc@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 16:22:06 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sun, 19 Jul 2026 16:22:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5d5c1e.6c5720b3.2da3b9.b55f@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 16:22:38 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Sun, 19 Jul 2026 16:22:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5d5c3e.bd93e731.30f155.b94b@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 16:36:24 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 16:36:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][NFC] Address -Wmicrosoft-cast in
 MSVC's exception_ptr (PR #210570)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210570@github.com>
Message-ID: <6a5d5f78.92ea0e2a.4a9a5.208f@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/210570

From libcxx-commits at lists.llvm.org  Sun Jul 19 16:52:35 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 16:52:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5d6343.f3151ff8.37ac76.c091@mx.google.com>


================
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
----------------
frederick-vs-ja wrote:

As `decay_t<_Ip>` is an iterator type, `decay_t<_Ip>(__first)` is equivalent to `static_cast<decay_t<_Ip>>(__first)`, and we don't need to change this line. But it seems that we should change the return type of this `operator()`.

I think we should try to add a test case which fails without changing the return type.

https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Sun Jul 19 17:47:06 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 17:47:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 16ffd27 - [libcxx][NFC] Address
 -Wmicrosoft-cast in MSVC's exception_ptr (#210570)
Message-ID: <6a5d700a.0c05e68e.3c2f5d.8141@mx.google.com>


Author: Petr Hosek
Date: 2026-07-19T17:47:01-07:00
New Revision: 16ffd27e3737c01cbabad6eed77b919dc977ad6c

URL: https://github.com/llvm/llvm-project/commit/16ffd27e3737c01cbabad6eed77b919dc977ad6c
DIFF: https://github.com/llvm/llvm-project/commit/16ffd27e3737c01cbabad6eed77b919dc977ad6c.diff

LOG: [libcxx][NFC] Address -Wmicrosoft-cast in MSVC's exception_ptr (#210570)

This was introduced as part of #94977. Rather than suppressing the
warning with `#pragma clang diagnostic ignored`, we use
`reinterpret_cast` to avoid the implicit conversion.

Added: 
    

Modified: 
    libcxx/src/support/runtime/exception_pointer_msvc.ipp

Removed: 
    


################################################################################
diff  --git a/libcxx/src/support/runtime/exception_pointer_msvc.ipp b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
index 3102d1ee93db2..4b877deeec7bc 100644
--- a/libcxx/src/support/runtime/exception_pointer_msvc.ipp
+++ b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
@@ -141,7 +141,7 @@ void __copy_exception_object(void* __dest, const void* __src, const CatchableTyp
 #if _EH_RELATIVE_TYPEINFO
   const auto __copy_func = reinterpret_cast<void*>(__throw_image_base + __type->copyFunction);
 #else // _EH_RELATIVE_TYPEINFO
-  const auto __copy_func = __type->copyFunction;
+  const auto __copy_func = reinterpret_cast<void*>(__type->copyFunction);
 #endif // _EH_RELATIVE_TYPEINFO
 
   const auto __adjusted = __AdjustPointer(const_cast<void*>(__src), __type->thisDisplacement);
@@ -204,7 +204,8 @@ struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) __exception_ptr_normal final :
           __call_member_function_0(__cpp_eh_record.params.pExceptionObject,
                                    reinterpret_cast<void*>(__throw_info->pmfnUnwind + __throw_image_base));
 #else // _EH_RELATIVE_TYPEINFO
-          __call_member_function_0(__cpp_eh_record.params.pExceptionObject, __throw_info->pmfnUnwind);
+          __call_member_function_0(__cpp_eh_record.params.pExceptionObject,
+                                   reinterpret_cast<void*>(__throw_info->pmfnUnwind));
 #endif // _EH_RELATIVE_TYPEINFO
         } else if (__type->properties & CT_IsWinRTHandle) {
           const auto* __unknown = *static_cast<IUnknown* const*>(__cpp_eh_record.params.pExceptionObject);


        

From libcxx-commits at lists.llvm.org  Sun Jul 19 19:48:03 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 19:48:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 53c6ce1 - [libc++][cmp][NFC] Fix: Empty
 namespaces should be avoided (#210624)
Message-ID: <6a5d8c63.492e23e4.d19bc.42ce@mx.google.com>


Author: Hristo Hristov
Date: 2026-07-20T05:47:58+03:00
New Revision: 53c6ce165aef58e1885d574c1ec597d091c3c8fc

URL: https://github.com/llvm/llvm-project/commit/53c6ce165aef58e1885d574c1ec597d091c3c8fc
DIFF: https://github.com/llvm/llvm-project/commit/53c6ce165aef58e1885d574c1ec597d091c3c8fc.diff

LOG: [libc++][cmp][NFC] Fix: Empty namespaces should be avoided (#210624)

Context: Including `<__utility/cmp.h>` causes:

> cmp.h:25:1: error: Empty namespaces should be avoided. Move any checks
around the namespace instead.
[libcpp-avoid-empty-namespaces,-warnings-as-errors]

Added: 
    

Modified: 
    libcxx/include/__utility/cmp.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__utility/cmp.h b/libcxx/include/__utility/cmp.h
index 7cfe640ceb423..360cf2eaa2aa4 100644
--- a/libcxx/include/__utility/cmp.h
+++ b/libcxx/include/__utility/cmp.h
@@ -22,10 +22,10 @@
 _LIBCPP_PUSH_MACROS
 #include <__undef_macros>
 
-_LIBCPP_BEGIN_NAMESPACE_STD
-
 #if _LIBCPP_STD_VER >= 20
 
+_LIBCPP_BEGIN_NAMESPACE_STD
+
 template <typename _Tp, typename _Ip>
 concept __comparison_can_promote_to =
     sizeof(_Tp) < sizeof(_Ip) || (sizeof(_Tp) == sizeof(_Ip) && __signed_integer<_Tp>);
@@ -84,10 +84,10 @@ template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
          std::cmp_greater_equal(__u, numeric_limits<_Tp>::min());
 }
 
-#endif // _LIBCPP_STD_VER >= 20
-
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_STD_VER >= 20
+
 _LIBCPP_POP_MACROS
 
 #endif // _LIBCPP___UTILITY_CMP_H


        

From libcxx-commits at lists.llvm.org  Sun Jul 19 19:48:06 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 19:48:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][cmp][NFC] Fix: Empty namespaces
 should be avoided (PR #210624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210624@github.com>
Message-ID: <6a5d8c66.d2cb4adc.ccd80.8ad1@mx.google.com>


https://github.com/Zingam closed https://github.com/llvm/llvm-project/pull/210624

From libcxx-commits at lists.llvm.org  Sun Jul 19 19:50:39 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 19:50:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Removed duplicated
 `_LIBCPP_INTRODUCED_IN_LLVM_23` macros (PR #210602)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210602@github.com>
Message-ID: <6a5d8cff.1b0626d0.f869d.f3b2@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/210602

From libcxx-commits at lists.llvm.org  Sun Jul 19 20:16:51 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 20:16:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][docs] Avoid talking about other
 implementations on DR status (PR #210637)
Message-ID: <llvm.org/llvm/llvm-project/pull/210637@github.com>


https://github.com/frederick-vs-ja created https://github.com/llvm/llvm-project/pull/210637

Standard library implementations consistently treat (or will consistently treat) some LWG papers as Defect Reports that are also applied to old modes, despite that they are not officially listed as Defect Reports in WG21 meeting minutes. We used to say "other implementations (namely MSVC STL and libstdc++) will do the same" for some of them.

However, such sentences will become outdated soon when other implementations actually do so. It is possibly better to avoid saying so, which potentially requires synchronizing our documentation with other implementations.

>From 44a0c6f1cfa85b08074bac55ced3601370e98778 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Mon, 20 Jul 2026 11:10:42 +0800
Subject: [PATCH] [libc++][docs] Avoid talking about other implementations on
 DR status

Standard library implementations consistently treat (or will
consistently treat) some LWG papers as Defect Reports that are also
applied to old modes, despite that they are not officially listed as
Defect Reports in WG21 meeting minutes. We used to say "other
implementations (namely MSVC STL and libstdc++) will do the same" for
some of them.

However, such sentences will become outdated soon when other
implementations actually do so. It is possibly better to avoid saying
so, which potentially requires synchronizing our documentation with
other implementations.
---
 libcxx/docs/Status/Cxx17Papers.csv | 2 +-
 libcxx/docs/Status/Cxx23Papers.csv | 4 ++--
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 libcxx/docs/Status/Cxx26Papers.csv | 6 +++---
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/docs/Status/Cxx17Papers.csv b/libcxx/docs/Status/Cxx17Papers.csv
index ba2ceeedc2ce8..58db91a94c9a9 100644
--- a/libcxx/docs/Status/Cxx17Papers.csv
+++ b/libcxx/docs/Status/Cxx17Papers.csv
@@ -84,7 +84,7 @@
 "`P0508R0 <https://wg21.link/P0508R0>`__","Wording for GB 58 - structured bindings for node_handles","2016-11 (Issaquah)","|Complete|","7","`#99944 <https://github.com/llvm/llvm-project/issues/99944>`__",""
 "`P0509R1 <https://wg21.link/P0509R1>`__","Updating ""Restrictions on exception handling""","2016-11 (Issaquah)","|Nothing To Do|","n/a","`#103676 <https://github.com/llvm/llvm-project/issues/103676>`__",""
 "`P0510R0 <https://wg21.link/P0510R0>`__","Disallowing references, incomplete types, arrays, and empty variants","2016-11 (Issaquah)","|Complete|","4","`#103677 <https://github.com/llvm/llvm-project/issues/103677>`__",""
-"`P0513R0 <https://wg21.link/P0513R0>`__","Poisoning the Hash","2016-11 (Issaquah)","|Complete|","5","`#103678 <https://github.com/llvm/llvm-project/issues/103678>`__","Implemented as a DR against C++11 since LLVM 22. MSVC STL does the same."
+"`P0513R0 <https://wg21.link/P0513R0>`__","Poisoning the Hash","2016-11 (Issaquah)","|Complete|","5","`#103678 <https://github.com/llvm/llvm-project/issues/103678>`__","Implemented as a DR against C++11 since LLVM 22."
 "`P0516R0 <https://wg21.link/P0516R0>`__","Clarify That shared_future's Copy Operations have Wide Contracts","2016-11 (Issaquah)","|Complete|","4","`#103679 <https://github.com/llvm/llvm-project/issues/103679>`__",""
 "`P0517R0 <https://wg21.link/P0517R0>`__","Make future_error Constructible","2016-11 (Issaquah)","|Complete|","4","`#103680 <https://github.com/llvm/llvm-project/issues/103680>`__",""
 "`P0521R0 <https://wg21.link/P0521R0>`__","Proposed Resolution for CA 14 (shared_ptr use_count/unique)","2016-11 (Issaquah)","|Complete|","18","`#103681 <https://github.com/llvm/llvm-project/issues/103681>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 739f0ecab37e7..0c608bd2bc1da 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -95,7 +95,7 @@
 "`P1202R5 <https://wg21.link/P1202R5>`__","Asymmetric Fences","2022-11 (Kona)","","","`#105238 <https://github.com/llvm/llvm-project/issues/105238>`__",""
 "`P1264R2 <https://wg21.link/P1264R2>`__","Revising the wording of ``stream`` input operations","2022-11 (Kona)","|Complete|","9","`#105239 <https://github.com/llvm/llvm-project/issues/105239>`__",""
 "`P1478R8 <https://wg21.link/P1478R8>`__","``Byte-wise`` ``atomic`` ``memcpy``","2022-11 (Kona)","","","`#105240 <https://github.com/llvm/llvm-project/issues/105240>`__",""
-"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","|Complete|","20","`#105241 <https://github.com/llvm/llvm-project/issues/105241>`__","The `[cmp.alg] <https://eel.is/c++draft/cmp.alg>`__ part is implemented as a DR against C++20. MSVC STL does the same. Other parts are Nothing To Do."
+"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","|Complete|","20","`#105241 <https://github.com/llvm/llvm-project/issues/105241>`__","The `[cmp.alg] <https://eel.is/c++draft/cmp.alg>`__ part is implemented as a DR against C++20."
 "`P2396R1 <https://wg21.link/P2396R1>`__","Concurrency TS 2 fixes ","2022-11 (Kona)","","","`#105242 <https://github.com/llvm/llvm-project/issues/105242>`__",""
 "`P2505R5 <https://wg21.link/P2505R5>`__","Monadic Functions for ``std::expected``","2022-11 (Kona)","|Complete|","17","`#105243 <https://github.com/llvm/llvm-project/issues/105243>`__",""
 "`P2539R4 <https://wg21.link/P2539R4>`__","Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?","2022-11 (Kona)","|Complete|","18","`#105245 <https://github.com/llvm/llvm-project/issues/105245>`__",""
@@ -106,7 +106,7 @@
 "`P2770R0 <https://wg21.link/P2770R0>`__","Stashing stashing ``iterators`` for proper flattening","2023-02 (Issaquah)","|Complete|","21","`#105250 <https://github.com/llvm/llvm-project/issues/105250>`__",""
 "`P2164R9 <https://wg21.link/P2164R9>`__","``views::enumerate``","2023-02 (Issaquah)","|Complete|","23","`#105251 <https://github.com/llvm/llvm-project/issues/105251>`__",""
 "`P2711R1 <https://wg21.link/P2711R1>`__","Making multi-param constructors of ``views`` ``explicit``","2023-02 (Issaquah)","|Complete|","21","`#105252 <https://github.com/llvm/llvm-project/issues/105252>`__","Implemented as a DR in C++20 since LLVM 23."
-"`P2609R3 <https://wg21.link/P2609R3>`__","Relaxing Ranges Just A Smidge","2023-02 (Issaquah)","|Complete|","20","`#105253 <https://github.com/llvm/llvm-project/issues/105253>`__","Implemented as a DR in C++20. Other implementations will do the same."
+"`P2609R3 <https://wg21.link/P2609R3>`__","Relaxing Ranges Just A Smidge","2023-02 (Issaquah)","|Complete|","20","`#105253 <https://github.com/llvm/llvm-project/issues/105253>`__","Implemented as a DR in C++20."
 "`P2713R1 <https://wg21.link/P2713R1>`__","Escaping improvements in ``std::format``","2023-02 (Issaquah)","|Complete|","19","`#105254 <https://github.com/llvm/llvm-project/issues/105254>`__",""
 "`P2675R1 <https://wg21.link/P2675R1>`__","``format``'s width estimation is too approximate and not forward compatible","2023-02 (Issaquah)","|Complete|","17","`#105255 <https://github.com/llvm/llvm-project/issues/105255>`__",""
 "`P2572R1 <https://wg21.link/P2572R1>`__","``std::format`` fill character allowances","2023-02 (Issaquah)","|Complete|","17","`#105256 <https://github.com/llvm/llvm-project/issues/105256>`__",""
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c203fa424556a..5b046b69d14bd 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -344,5 +344,5 @@
 "","","","","","",""
 "`LWG4139 <https://wg21.link/LWG4139>`__","§[time.zone.leap] recursive constraint in ``<=>``","Not Adopted Yet","|Complete|","20","`#118369 <https://github.com/llvm/llvm-project/issues/118369>`__",""
 "`LWG3456 <https://wg21.link/LWG3456>`__","Pattern used by ``std::from_chars`` is underspecified (option B)","Not Adopted Yet","|Complete|","20","`#118370 <https://github.com/llvm/llvm-project/issues/118370>`__",""
-"`LWG3882 <https://wg21.link/LWG3882>`__","``tuple`` relational operators have confused friendships","Not Adopted Yet","|Complete|","22","`#171425 <https://github.com/llvm/llvm-project/issues/171425>`__","The comparison operators are constrained harder than the proposed resolution. libstdc++ and MSVC STL do the same."
+"`LWG3882 <https://wg21.link/LWG3882>`__","``tuple`` relational operators have confused friendships","Not Adopted Yet","|Complete|","22","`#171425 <https://github.com/llvm/llvm-project/issues/171425>`__","The comparison operators are constrained harder than the proposed resolution."
 "","","","","","",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 2522dd98e4b9c..916bb559f254b 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -7,7 +7,7 @@
 "`P2530R3 <https://wg21.link/P2530R3>`__","Hazard Pointers for C++26","2023-06 (Varna)","","","`#105362 <https://github.com/llvm/llvm-project/issues/105362>`__",""
 "`P2538R1 <https://wg21.link/P2538R1>`__","ADL-proof ``std::projected``","2023-06 (Varna)","|Complete|","18","`#105363 <https://github.com/llvm/llvm-project/issues/105363>`__",""
 "`P2495R3 <https://wg21.link/P2495R3>`__","Interfacing ``stringstream``\s with ``string_view``","2023-06 (Varna)","|Complete|","19","`#105364 <https://github.com/llvm/llvm-project/issues/105364>`__",""
-"`P2510R3 <https://wg21.link/P2510R3>`__","Formatting pointers","2023-06 (Varna)","|Complete|","17","`#105365 <https://github.com/llvm/llvm-project/issues/105365>`__","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
+"`P2510R3 <https://wg21.link/P2510R3>`__","Formatting pointers","2023-06 (Varna)","|Complete|","17","`#105365 <https://github.com/llvm/llvm-project/issues/105365>`__","Implemented as a DR against C++20."
 "`P2198R7 <https://wg21.link/P2198R7>`__","Freestanding Feature-Test Macros and Implementation-Defined Extensions","2023-06 (Varna)","","","`#105368 <https://github.com/llvm/llvm-project/issues/105368>`__",""
 "`P2338R4 <https://wg21.link/P2338R4>`__","Freestanding Library: Character primitives and the C library","2023-06 (Varna)","","","`#105369 <https://github.com/llvm/llvm-project/issues/105369>`__",""
 "`P2013R5 <https://wg21.link/P2013R5>`__","Freestanding Language: Optional ``::operator new``","2023-06 (Varna)","","","`#105370 <https://github.com/llvm/llvm-project/issues/105370>`__",""
@@ -51,7 +51,7 @@
 "`P2869R4 <https://wg21.link/P2869R4>`__","Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26","2024-03 (Tokyo)","","","`#105412 <https://github.com/llvm/llvm-project/issues/105412>`__",""
 "`P2872R3 <https://wg21.link/P2872R3>`__","Remove ``wstring_convert`` From C++26","2024-03 (Tokyo)","|Complete|","19","`#105413 <https://github.com/llvm/llvm-project/issues/105413>`__",""
 "`P3107R5 <https://wg21.link/P3107R5>`__","Permit an efficient implementation of ``std::print``","2024-03 (Tokyo)","","","`#105414 <https://github.com/llvm/llvm-project/issues/105414>`__",""
-"`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)"
+"`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23."
 "`P2845R8 <https://wg21.link/P2845R8>`__","Formatting of ``std::filesystem::path``","2024-03 (Tokyo)","","","`#105416 <https://github.com/llvm/llvm-project/issues/105416>`__",""
 "`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","","","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
 "`P2542R8 <https://wg21.link/P2542R8>`__","``views::concat``","2024-03 (Tokyo)","|Complete|","23","`#105419 <https://github.com/llvm/llvm-project/issues/105419>`__",""
@@ -64,7 +64,7 @@
 "`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23."
 "","","","","","",""
 "`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","`#105427 <https://github.com/llvm/llvm-project/issues/105427>`__",""
-"`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","`#105428 <https://github.com/llvm/llvm-project/issues/105428>`__","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
+"`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","`#105428 <https://github.com/llvm/llvm-project/issues/105428>`__","Implemented as a DR against C++20."
 "`P2389R2 <https://wg21.link/P2389R2>`__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19","`#105429 <https://github.com/llvm/llvm-project/issues/105429>`__",""
 "`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","|Complete|","22","`#105430 <https://github.com/llvm/llvm-project/issues/105430>`__",""
 "`P3217R0 <https://wg21.link/P3217R0>`__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","","`#105431 <https://github.com/llvm/llvm-project/issues/105431>`__",""


From libcxx-commits at lists.llvm.org  Sun Jul 19 20:17:18 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 20:17:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][docs] Avoid talking about other
 implementations on DR status (PR #210637)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210637@github.com>
Message-ID: <6a5d933e.d2cb4adc.ccd80.4dea@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: A. Jiang (frederick-vs-ja)

<details>
<summary>Changes</summary>

Standard library implementations consistently treat (or will consistently treat) some LWG papers as Defect Reports that are also applied to old modes, despite that they are not officially listed as Defect Reports in WG21 meeting minutes. We used to say "other implementations (namely MSVC STL and libstdc++) will do the same" for some of them.

However, such sentences will become outdated soon when other implementations actually do so. It is possibly better to avoid saying so, which potentially requires synchronizing our documentation with other implementations.

---
Full diff: https://github.com/llvm/llvm-project/pull/210637.diff


4 Files Affected:

- (modified) libcxx/docs/Status/Cxx17Papers.csv (+1-1) 
- (modified) libcxx/docs/Status/Cxx23Papers.csv (+2-2) 
- (modified) libcxx/docs/Status/Cxx26Issues.csv (+1-1) 
- (modified) libcxx/docs/Status/Cxx26Papers.csv (+3-3) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx17Papers.csv b/libcxx/docs/Status/Cxx17Papers.csv
index ba2ceeedc2ce8..58db91a94c9a9 100644
--- a/libcxx/docs/Status/Cxx17Papers.csv
+++ b/libcxx/docs/Status/Cxx17Papers.csv
@@ -84,7 +84,7 @@
 "`P0508R0 <https://wg21.link/P0508R0>`__","Wording for GB 58 - structured bindings for node_handles","2016-11 (Issaquah)","|Complete|","7","`#99944 <https://github.com/llvm/llvm-project/issues/99944>`__",""
 "`P0509R1 <https://wg21.link/P0509R1>`__","Updating ""Restrictions on exception handling""","2016-11 (Issaquah)","|Nothing To Do|","n/a","`#103676 <https://github.com/llvm/llvm-project/issues/103676>`__",""
 "`P0510R0 <https://wg21.link/P0510R0>`__","Disallowing references, incomplete types, arrays, and empty variants","2016-11 (Issaquah)","|Complete|","4","`#103677 <https://github.com/llvm/llvm-project/issues/103677>`__",""
-"`P0513R0 <https://wg21.link/P0513R0>`__","Poisoning the Hash","2016-11 (Issaquah)","|Complete|","5","`#103678 <https://github.com/llvm/llvm-project/issues/103678>`__","Implemented as a DR against C++11 since LLVM 22. MSVC STL does the same."
+"`P0513R0 <https://wg21.link/P0513R0>`__","Poisoning the Hash","2016-11 (Issaquah)","|Complete|","5","`#103678 <https://github.com/llvm/llvm-project/issues/103678>`__","Implemented as a DR against C++11 since LLVM 22."
 "`P0516R0 <https://wg21.link/P0516R0>`__","Clarify That shared_future's Copy Operations have Wide Contracts","2016-11 (Issaquah)","|Complete|","4","`#103679 <https://github.com/llvm/llvm-project/issues/103679>`__",""
 "`P0517R0 <https://wg21.link/P0517R0>`__","Make future_error Constructible","2016-11 (Issaquah)","|Complete|","4","`#103680 <https://github.com/llvm/llvm-project/issues/103680>`__",""
 "`P0521R0 <https://wg21.link/P0521R0>`__","Proposed Resolution for CA 14 (shared_ptr use_count/unique)","2016-11 (Issaquah)","|Complete|","18","`#103681 <https://github.com/llvm/llvm-project/issues/103681>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 739f0ecab37e7..0c608bd2bc1da 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -95,7 +95,7 @@
 "`P1202R5 <https://wg21.link/P1202R5>`__","Asymmetric Fences","2022-11 (Kona)","","","`#105238 <https://github.com/llvm/llvm-project/issues/105238>`__",""
 "`P1264R2 <https://wg21.link/P1264R2>`__","Revising the wording of ``stream`` input operations","2022-11 (Kona)","|Complete|","9","`#105239 <https://github.com/llvm/llvm-project/issues/105239>`__",""
 "`P1478R8 <https://wg21.link/P1478R8>`__","``Byte-wise`` ``atomic`` ``memcpy``","2022-11 (Kona)","","","`#105240 <https://github.com/llvm/llvm-project/issues/105240>`__",""
-"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","|Complete|","20","`#105241 <https://github.com/llvm/llvm-project/issues/105241>`__","The `[cmp.alg] <https://eel.is/c++draft/cmp.alg>`__ part is implemented as a DR against C++20. MSVC STL does the same. Other parts are Nothing To Do."
+"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","|Complete|","20","`#105241 <https://github.com/llvm/llvm-project/issues/105241>`__","The `[cmp.alg] <https://eel.is/c++draft/cmp.alg>`__ part is implemented as a DR against C++20."
 "`P2396R1 <https://wg21.link/P2396R1>`__","Concurrency TS 2 fixes ","2022-11 (Kona)","","","`#105242 <https://github.com/llvm/llvm-project/issues/105242>`__",""
 "`P2505R5 <https://wg21.link/P2505R5>`__","Monadic Functions for ``std::expected``","2022-11 (Kona)","|Complete|","17","`#105243 <https://github.com/llvm/llvm-project/issues/105243>`__",""
 "`P2539R4 <https://wg21.link/P2539R4>`__","Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?","2022-11 (Kona)","|Complete|","18","`#105245 <https://github.com/llvm/llvm-project/issues/105245>`__",""
@@ -106,7 +106,7 @@
 "`P2770R0 <https://wg21.link/P2770R0>`__","Stashing stashing ``iterators`` for proper flattening","2023-02 (Issaquah)","|Complete|","21","`#105250 <https://github.com/llvm/llvm-project/issues/105250>`__",""
 "`P2164R9 <https://wg21.link/P2164R9>`__","``views::enumerate``","2023-02 (Issaquah)","|Complete|","23","`#105251 <https://github.com/llvm/llvm-project/issues/105251>`__",""
 "`P2711R1 <https://wg21.link/P2711R1>`__","Making multi-param constructors of ``views`` ``explicit``","2023-02 (Issaquah)","|Complete|","21","`#105252 <https://github.com/llvm/llvm-project/issues/105252>`__","Implemented as a DR in C++20 since LLVM 23."
-"`P2609R3 <https://wg21.link/P2609R3>`__","Relaxing Ranges Just A Smidge","2023-02 (Issaquah)","|Complete|","20","`#105253 <https://github.com/llvm/llvm-project/issues/105253>`__","Implemented as a DR in C++20. Other implementations will do the same."
+"`P2609R3 <https://wg21.link/P2609R3>`__","Relaxing Ranges Just A Smidge","2023-02 (Issaquah)","|Complete|","20","`#105253 <https://github.com/llvm/llvm-project/issues/105253>`__","Implemented as a DR in C++20."
 "`P2713R1 <https://wg21.link/P2713R1>`__","Escaping improvements in ``std::format``","2023-02 (Issaquah)","|Complete|","19","`#105254 <https://github.com/llvm/llvm-project/issues/105254>`__",""
 "`P2675R1 <https://wg21.link/P2675R1>`__","``format``'s width estimation is too approximate and not forward compatible","2023-02 (Issaquah)","|Complete|","17","`#105255 <https://github.com/llvm/llvm-project/issues/105255>`__",""
 "`P2572R1 <https://wg21.link/P2572R1>`__","``std::format`` fill character allowances","2023-02 (Issaquah)","|Complete|","17","`#105256 <https://github.com/llvm/llvm-project/issues/105256>`__",""
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c203fa424556a..5b046b69d14bd 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -344,5 +344,5 @@
 "","","","","","",""
 "`LWG4139 <https://wg21.link/LWG4139>`__","§[time.zone.leap] recursive constraint in ``<=>``","Not Adopted Yet","|Complete|","20","`#118369 <https://github.com/llvm/llvm-project/issues/118369>`__",""
 "`LWG3456 <https://wg21.link/LWG3456>`__","Pattern used by ``std::from_chars`` is underspecified (option B)","Not Adopted Yet","|Complete|","20","`#118370 <https://github.com/llvm/llvm-project/issues/118370>`__",""
-"`LWG3882 <https://wg21.link/LWG3882>`__","``tuple`` relational operators have confused friendships","Not Adopted Yet","|Complete|","22","`#171425 <https://github.com/llvm/llvm-project/issues/171425>`__","The comparison operators are constrained harder than the proposed resolution. libstdc++ and MSVC STL do the same."
+"`LWG3882 <https://wg21.link/LWG3882>`__","``tuple`` relational operators have confused friendships","Not Adopted Yet","|Complete|","22","`#171425 <https://github.com/llvm/llvm-project/issues/171425>`__","The comparison operators are constrained harder than the proposed resolution."
 "","","","","","",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 2522dd98e4b9c..916bb559f254b 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -7,7 +7,7 @@
 "`P2530R3 <https://wg21.link/P2530R3>`__","Hazard Pointers for C++26","2023-06 (Varna)","","","`#105362 <https://github.com/llvm/llvm-project/issues/105362>`__",""
 "`P2538R1 <https://wg21.link/P2538R1>`__","ADL-proof ``std::projected``","2023-06 (Varna)","|Complete|","18","`#105363 <https://github.com/llvm/llvm-project/issues/105363>`__",""
 "`P2495R3 <https://wg21.link/P2495R3>`__","Interfacing ``stringstream``\s with ``string_view``","2023-06 (Varna)","|Complete|","19","`#105364 <https://github.com/llvm/llvm-project/issues/105364>`__",""
-"`P2510R3 <https://wg21.link/P2510R3>`__","Formatting pointers","2023-06 (Varna)","|Complete|","17","`#105365 <https://github.com/llvm/llvm-project/issues/105365>`__","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
+"`P2510R3 <https://wg21.link/P2510R3>`__","Formatting pointers","2023-06 (Varna)","|Complete|","17","`#105365 <https://github.com/llvm/llvm-project/issues/105365>`__","Implemented as a DR against C++20."
 "`P2198R7 <https://wg21.link/P2198R7>`__","Freestanding Feature-Test Macros and Implementation-Defined Extensions","2023-06 (Varna)","","","`#105368 <https://github.com/llvm/llvm-project/issues/105368>`__",""
 "`P2338R4 <https://wg21.link/P2338R4>`__","Freestanding Library: Character primitives and the C library","2023-06 (Varna)","","","`#105369 <https://github.com/llvm/llvm-project/issues/105369>`__",""
 "`P2013R5 <https://wg21.link/P2013R5>`__","Freestanding Language: Optional ``::operator new``","2023-06 (Varna)","","","`#105370 <https://github.com/llvm/llvm-project/issues/105370>`__",""
@@ -51,7 +51,7 @@
 "`P2869R4 <https://wg21.link/P2869R4>`__","Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26","2024-03 (Tokyo)","","","`#105412 <https://github.com/llvm/llvm-project/issues/105412>`__",""
 "`P2872R3 <https://wg21.link/P2872R3>`__","Remove ``wstring_convert`` From C++26","2024-03 (Tokyo)","|Complete|","19","`#105413 <https://github.com/llvm/llvm-project/issues/105413>`__",""
 "`P3107R5 <https://wg21.link/P3107R5>`__","Permit an efficient implementation of ``std::print``","2024-03 (Tokyo)","","","`#105414 <https://github.com/llvm/llvm-project/issues/105414>`__",""
-"`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)"
+"`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23."
 "`P2845R8 <https://wg21.link/P2845R8>`__","Formatting of ``std::filesystem::path``","2024-03 (Tokyo)","","","`#105416 <https://github.com/llvm/llvm-project/issues/105416>`__",""
 "`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","","","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
 "`P2542R8 <https://wg21.link/P2542R8>`__","``views::concat``","2024-03 (Tokyo)","|Complete|","23","`#105419 <https://github.com/llvm/llvm-project/issues/105419>`__",""
@@ -64,7 +64,7 @@
 "`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23."
 "","","","","","",""
 "`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","`#105427 <https://github.com/llvm/llvm-project/issues/105427>`__",""
-"`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","`#105428 <https://github.com/llvm/llvm-project/issues/105428>`__","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"
+"`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","`#105428 <https://github.com/llvm/llvm-project/issues/105428>`__","Implemented as a DR against C++20."
 "`P2389R2 <https://wg21.link/P2389R2>`__","``dextents`` Index Type Parameter","2024-06 (St. Louis)","|Complete|","19","`#105429 <https://github.com/llvm/llvm-project/issues/105429>`__",""
 "`P3168R2 <https://wg21.link/P3168R2>`__","Give ``std::optional`` Range Support","2024-06 (St. Louis)","|Complete|","22","`#105430 <https://github.com/llvm/llvm-project/issues/105430>`__",""
 "`P3217R0 <https://wg21.link/P3217R0>`__","Adjoints to 'Enabling list-initialization for algorithms': find_last","2024-06 (St. Louis)","","","`#105431 <https://github.com/llvm/llvm-project/issues/105431>`__",""

``````````

</details>


https://github.com/llvm/llvm-project/pull/210637

From libcxx-commits at lists.llvm.org  Sun Jul 19 20:18:07 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 20:18:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Backport P3029R1 Better
 `mdspan`'s CTAD to C++20/23 (PR #208132)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208132@github.com>
Message-ID: <6a5d936f.5a0412d2.3daf32.76bf@mx.google.com>


================
@@ -61,7 +61,7 @@
 "`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","","`#105423 <https://github.com/llvm/llvm-project/issues/105423>`__",""
 "`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","22","`#105424 <https://github.com/llvm/llvm-project/issues/105424>`__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
 "`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","`#105425 <https://github.com/llvm/llvm-project/issues/105425>`__",""
-"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__",""
+"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23. (MSVC STL and libstdc++ will do the same.)"
----------------
frederick-vs-ja wrote:

Opened #210637 for cleaning up the documentation.

https://github.com/llvm/llvm-project/pull/208132

From libcxx-commits at lists.llvm.org  Sun Jul 19 20:39:24 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 20:39:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add tests for LWG3798 and mark
	it as Complete (PR #210640)
Message-ID: <llvm.org/llvm/llvm-project/pull/210640@github.com>


https://github.com/frederick-vs-ja created https://github.com/llvm/llvm-project/pull/210640

Fixes #105049.

>From 1b0898787716b1892a73d19d8e1d9e69090e9d6b Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Mon, 20 Jul 2026 11:36:05 +0800
Subject: [PATCH] [libc++] Add tests for LWG3798 and mark it as Complete

---
 libcxx/docs/Status/Cxx23Issues.csv                           | 2 +-
 .../ranges/range.adaptors/range.adjacent.transform/helpers.h | 5 +++++
 .../iterator/member_types.compile.pass.cpp                   | 4 ++++
 3 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 91c4dc462c0c9..db38715a98973 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -234,7 +234,7 @@
 "`LWG3792 <https://wg21.link/LWG3792>`__","``__cpp_lib_constexpr_algorithms`` should also be defined in ``<utility>``","2022-11 (Kona)","|Complete|","16","`#105046 <https://github.com/llvm/llvm-project/issues/105046>`__",""
 "`LWG3795 <https://wg21.link/LWG3795>`__","Self-move-assignment of ``std::future`` and ``std::shared_future`` have unimplementable postconditions","2022-11 (Kona)","","","`#105047 <https://github.com/llvm/llvm-project/issues/105047>`__",""
 "`LWG3796 <https://wg21.link/LWG3796>`__","``movable-box`` as member should use ``default-initialization`` instead of ``copy-initialization``","2022-11 (Kona)","","","`#105048 <https://github.com/llvm/llvm-project/issues/105048>`__",""
-"`LWG3798 <https://wg21.link/LWG3798>`__","Rvalue reference and ``iterator_category``","2022-11 (Kona)","|Partial|","","`#105049 <https://github.com/llvm/llvm-project/issues/105049>`__","``adjacent_transform_view`` hasn't been done yet since this type isn't implemented yet"
+"`LWG3798 <https://wg21.link/LWG3798>`__","Rvalue reference and ``iterator_category``","2022-11 (Kona)","|Complete|","22","`#105049 <https://github.com/llvm/llvm-project/issues/105049>`__",""
 "`LWG3801 <https://wg21.link/LWG3801>`__","``cartesian_product_view::iterator::distance-from`` ignores the size of last underlying range","2022-11 (Kona)","","","`#105051 <https://github.com/llvm/llvm-project/issues/105051>`__",""
 "`LWG3814 <https://wg21.link/LWG3814>`__","Add freestanding items requested by NB comments","2022-11 (Kona)","","","`#105052 <https://github.com/llvm/llvm-project/issues/105052>`__",""
 "`LWG3816 <https://wg21.link/LWG3816>`__","``flat_map`` and ``flat_multimap`` should impose sequence container requirements","2022-11 (Kona)","","","`#105053 <https://github.com/llvm/llvm-project/issues/105053>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h
index 39f2f856c149b..770cd66d011ea 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h
@@ -13,6 +13,7 @@
 #include <functional>
 #include <numeric>
 #include <tuple>
+#include <utility>
 
 // intentionally not using meta programming for the expected tuple types
 
@@ -35,6 +36,10 @@ struct GetFirst {
   constexpr decltype(auto) operator()(auto&& first, auto&&...) const { return std::forward<decltype(first)>(first); }
 };
 
+struct GetFirstAsXvalue {
+  static constexpr decltype(auto) operator()(auto&& first, auto&&...) { return std::move(first); }
+};
+
 template <std::size_t N>
 struct ValidateTieFromIndex {
   constexpr void operator()(auto&& buffer, auto&& tuple, std::size_t idx) const {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp
index 684569516d747..6105dde565082 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp
@@ -139,6 +139,10 @@ void test() {
   test<GetFirst, 2, int, false>();
   test<GetFirst, 3, int, false>();
 
+  test<GetFirstAsXvalue, 1, int, false>();
+  test<GetFirstAsXvalue, 2, int, false>();
+  test<GetFirstAsXvalue, 3, int, false>();
+
   test<Multiply, 1, int, true>();
   test<Multiply, 2, int, true>();
   test<Multiply, 3, int, true>();


From libcxx-commits at lists.llvm.org  Sun Jul 19 20:39:58 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 20:39:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add tests for LWG3798 and mark
	it as Complete (PR #210640)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210640@github.com>
Message-ID: <6a5d988e.92ea0e2a.4a9a5.359a@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: A. Jiang (frederick-vs-ja)

<details>
<summary>Changes</summary>

Fixes #<!-- -->105049.

---
Full diff: https://github.com/llvm/llvm-project/pull/210640.diff


3 Files Affected:

- (modified) libcxx/docs/Status/Cxx23Issues.csv (+1-1) 
- (modified) libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h (+5) 
- (modified) libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp (+4) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 91c4dc462c0c9..db38715a98973 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -234,7 +234,7 @@
 "`LWG3792 <https://wg21.link/LWG3792>`__","``__cpp_lib_constexpr_algorithms`` should also be defined in ``<utility>``","2022-11 (Kona)","|Complete|","16","`#105046 <https://github.com/llvm/llvm-project/issues/105046>`__",""
 "`LWG3795 <https://wg21.link/LWG3795>`__","Self-move-assignment of ``std::future`` and ``std::shared_future`` have unimplementable postconditions","2022-11 (Kona)","","","`#105047 <https://github.com/llvm/llvm-project/issues/105047>`__",""
 "`LWG3796 <https://wg21.link/LWG3796>`__","``movable-box`` as member should use ``default-initialization`` instead of ``copy-initialization``","2022-11 (Kona)","","","`#105048 <https://github.com/llvm/llvm-project/issues/105048>`__",""
-"`LWG3798 <https://wg21.link/LWG3798>`__","Rvalue reference and ``iterator_category``","2022-11 (Kona)","|Partial|","","`#105049 <https://github.com/llvm/llvm-project/issues/105049>`__","``adjacent_transform_view`` hasn't been done yet since this type isn't implemented yet"
+"`LWG3798 <https://wg21.link/LWG3798>`__","Rvalue reference and ``iterator_category``","2022-11 (Kona)","|Complete|","22","`#105049 <https://github.com/llvm/llvm-project/issues/105049>`__",""
 "`LWG3801 <https://wg21.link/LWG3801>`__","``cartesian_product_view::iterator::distance-from`` ignores the size of last underlying range","2022-11 (Kona)","","","`#105051 <https://github.com/llvm/llvm-project/issues/105051>`__",""
 "`LWG3814 <https://wg21.link/LWG3814>`__","Add freestanding items requested by NB comments","2022-11 (Kona)","","","`#105052 <https://github.com/llvm/llvm-project/issues/105052>`__",""
 "`LWG3816 <https://wg21.link/LWG3816>`__","``flat_map`` and ``flat_multimap`` should impose sequence container requirements","2022-11 (Kona)","","","`#105053 <https://github.com/llvm/llvm-project/issues/105053>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h
index 39f2f856c149b..770cd66d011ea 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h
@@ -13,6 +13,7 @@
 #include <functional>
 #include <numeric>
 #include <tuple>
+#include <utility>
 
 // intentionally not using meta programming for the expected tuple types
 
@@ -35,6 +36,10 @@ struct GetFirst {
   constexpr decltype(auto) operator()(auto&& first, auto&&...) const { return std::forward<decltype(first)>(first); }
 };
 
+struct GetFirstAsXvalue {
+  static constexpr decltype(auto) operator()(auto&& first, auto&&...) { return std::move(first); }
+};
+
 template <std::size_t N>
 struct ValidateTieFromIndex {
   constexpr void operator()(auto&& buffer, auto&& tuple, std::size_t idx) const {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp
index 684569516d747..6105dde565082 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp
@@ -139,6 +139,10 @@ void test() {
   test<GetFirst, 2, int, false>();
   test<GetFirst, 3, int, false>();
 
+  test<GetFirstAsXvalue, 1, int, false>();
+  test<GetFirstAsXvalue, 2, int, false>();
+  test<GetFirstAsXvalue, 3, int, false>();
+
   test<Multiply, 1, int, true>();
   test<Multiply, 2, int, true>();
   test<Multiply, 3, int, true>();

``````````

</details>


https://github.com/llvm/llvm-project/pull/210640

From libcxx-commits at lists.llvm.org  Sun Jul 19 21:02:33 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 21:02:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add tests for LWG3798 and mark
	it as Complete (PR #210640)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210640@github.com>
Message-ID: <6a5d9dd9.824e11a6.9c376.a141@mx.google.com>


https://github.com/Zingam approved this pull request.


https://github.com/llvm/llvm-project/pull/210640

From libcxx-commits at lists.llvm.org  Sun Jul 19 21:07:36 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 21:07:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][docs] Avoid talking about other
 implementations on DR status (PR #210637)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210637@github.com>
Message-ID: <6a5d9f08.8e75e0cb.f69c7.b843@mx.google.com>


================
@@ -95,7 +95,7 @@
 "`P1202R5 <https://wg21.link/P1202R5>`__","Asymmetric Fences","2022-11 (Kona)","","","`#105238 <https://github.com/llvm/llvm-project/issues/105238>`__",""
 "`P1264R2 <https://wg21.link/P1264R2>`__","Revising the wording of ``stream`` input operations","2022-11 (Kona)","|Complete|","9","`#105239 <https://github.com/llvm/llvm-project/issues/105239>`__",""
 "`P1478R8 <https://wg21.link/P1478R8>`__","``Byte-wise`` ``atomic`` ``memcpy``","2022-11 (Kona)","","","`#105240 <https://github.com/llvm/llvm-project/issues/105240>`__",""
-"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","|Complete|","20","`#105241 <https://github.com/llvm/llvm-project/issues/105241>`__","The `[cmp.alg] <https://eel.is/c++draft/cmp.alg>`__ part is implemented as a DR against C++20. MSVC STL does the same. Other parts are Nothing To Do."
+"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","|Complete|","20","`#105241 <https://github.com/llvm/llvm-project/issues/105241>`__","The `[cmp.alg] <https://eel.is/c++draft/cmp.alg>`__ part is implemented as a DR against C++20."
----------------
Zingam wrote:

```suggestion
"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","|Complete|","20","`#105241 <https://github.com/llvm/llvm-project/issues/105241>`__","The `[cmp.alg] <https://wg21.link/cmp.alg>`__ part is implemented as a DR against C++20."
```

https://github.com/llvm/llvm-project/pull/210637

From libcxx-commits at lists.llvm.org  Sun Jul 19 22:11:16 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 22:11:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format
	`lazy_split_view` (PR #210644)
Message-ID: <llvm.org/llvm/llvm-project/pull/210644@github.com>


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

None

>From 07be9a38779f733dbe3cbd3d79af531836107b7a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 08:10:56 +0300
Subject: [PATCH] [libc++][ranges][NFC] Format `lazy_split_view`

---
 libcxx/include/__ranges/lazy_split_view.h | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 27a9d5e77abf1..be2b86772fa0d 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
+      return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
+    return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -278,9 +278,9 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <forward_range _Tp>
   struct __inner_iterator_category<_Tp> {
     using iterator_category =
-        _If< derived_from<typename iterator_traits<iterator_t<_Tp>>::iterator_category, forward_iterator_tag>,
-             forward_iterator_tag,
-             typename iterator_traits<iterator_t<_Tp>>::iterator_category >;
+        _If<derived_from<typename iterator_traits<iterator_t<_Tp>>::iterator_category, forward_iterator_tag>,
+            forward_iterator_tag,
+            typename iterator_traits<iterator_t<_Tp>>::iterator_category>;
   };
 
   template <bool _Const>
@@ -408,8 +408,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&,
-                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, range_value_t<_Range>)
+    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {


From libcxx-commits at lists.llvm.org  Sun Jul 19 22:11:54 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sun, 19 Jul 2026 22:11:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format
	`lazy_split_view` (PR #210644)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210644@github.com>
Message-ID: <6a5dae1a.eb165e5c.4506c.4d87@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/210644.diff


1 Files Affected:

- (modified) libcxx/include/__ranges/lazy_split_view.h (+7-7) 


``````````diff
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 27a9d5e77abf1..be2b86772fa0d 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
+      return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
+    return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -278,9 +278,9 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <forward_range _Tp>
   struct __inner_iterator_category<_Tp> {
     using iterator_category =
-        _If< derived_from<typename iterator_traits<iterator_t<_Tp>>::iterator_category, forward_iterator_tag>,
-             forward_iterator_tag,
-             typename iterator_traits<iterator_t<_Tp>>::iterator_category >;
+        _If<derived_from<typename iterator_traits<iterator_t<_Tp>>::iterator_category, forward_iterator_tag>,
+            forward_iterator_tag,
+            typename iterator_traits<iterator_t<_Tp>>::iterator_category>;
   };
 
   template <bool _Const>
@@ -408,8 +408,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&,
-                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, range_value_t<_Range>)
+    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {

``````````

</details>


https://github.com/llvm/llvm-project/pull/210644

From libcxx-commits at lists.llvm.org  Sun Jul 19 22:13:08 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Sun, 19 Jul 2026 22:13:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format
	`lazy_split_view` (PR #210644)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210644@github.com>
Message-ID: <6a5dae64.160324b0.1f653e.b94f@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/210644

From libcxx-commits at lists.llvm.org  Sun Jul 19 22:33:51 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 22:33:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][docs] Avoid talking about other
 implementations on DR status (PR #210637)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210637@github.com>
Message-ID: <6a5db33f.c56fb102.2d07b8.91ad@mx.google.com>


================
@@ -95,7 +95,7 @@
 "`P1202R5 <https://wg21.link/P1202R5>`__","Asymmetric Fences","2022-11 (Kona)","","","`#105238 <https://github.com/llvm/llvm-project/issues/105238>`__",""
 "`P1264R2 <https://wg21.link/P1264R2>`__","Revising the wording of ``stream`` input operations","2022-11 (Kona)","|Complete|","9","`#105239 <https://github.com/llvm/llvm-project/issues/105239>`__",""
 "`P1478R8 <https://wg21.link/P1478R8>`__","``Byte-wise`` ``atomic`` ``memcpy``","2022-11 (Kona)","","","`#105240 <https://github.com/llvm/llvm-project/issues/105240>`__",""
-"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","|Complete|","20","`#105241 <https://github.com/llvm/llvm-project/issues/105241>`__","The `[cmp.alg] <https://eel.is/c++draft/cmp.alg>`__ part is implemented as a DR against C++20. MSVC STL does the same. Other parts are Nothing To Do."
+"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","|Complete|","20","`#105241 <https://github.com/llvm/llvm-project/issues/105241>`__","The `[cmp.alg] <https://eel.is/c++draft/cmp.alg>`__ part is implemented as a DR against C++20."
----------------
frederick-vs-ja wrote:

I'm not sure whether this is better as `https://wg21.link/cmp.alg` requires one more redirection. Currently we're mixing `wg21.link` and `eel.is` in libc++'s documentations. I guess it's better to address this in another PR.

https://github.com/llvm/llvm-project/pull/210637

From libcxx-commits at lists.llvm.org  Sun Jul 19 22:53:14 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sun, 19 Jul 2026 22:53:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format
	`lazy_split_view` (PR #210644)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210644@github.com>
Message-ID: <6a5db7ca.6c8668f2.1f24.8ca0@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/210644

From libcxx-commits at lists.llvm.org  Sun Jul 19 23:48:45 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 19 Jul 2026 23:48:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Removed duplicated
 `_LIBCPP_INTRODUCED_IN_LLVM_23` macros (PR #210602)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210602@github.com>
Message-ID: <6a5dc4cd.bc28b987.2e0c9a.f0b6@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/210602

From libcxx-commits at lists.llvm.org  Sun Jul 19 23:50:56 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Sun, 19 Jul 2026 23:50:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test][NFC] Removed unsupported
 compilers from tests (PR #210413)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210413@github.com>
Message-ID: <6a5dc550.e8e5c93d.284ff3.1056@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/210413

From libcxx-commits at lists.llvm.org  Mon Jul 20 00:26:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 00:26:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a5dcd96.b7b4cce5.31b99a.de4a@mx.google.com>


================

----------------
philnik777 wrote:

Could you add this header to the modulemap?

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Mon Jul 20 00:30:32 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 00:30:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update release procedure for
	libc++ & friends (PR #210383)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210383@github.com>
Message-ID: <6a5dce98.a5745248.6091c.2dc9@mx.google.com>


================
@@ -37,39 +35,31 @@ release:
 * Make sure all libc++ supported compilers in the CI are updated to their
   latest release.
 
-Branching
-=========
-
-This is done by the LLVM Release managers.
+After the branch is created
+===========================
 
 After branching for an LLVM release:
 
 1. Update ``_LIBCPP_VERSION`` in ``libcxx/include/__config``
 2. Update the version number in ``libcxx/docs/conf.py``
 3. Update ``_LIBCPPABI_VERSION`` in ``libcxxabi/include/cxxabi.h``
 4. Update ``_LIBUNWIND_VERSION`` in ``libunwind/include/__libunwind_config.h``
-5. Create a release notes file for the next release from the template
-6. Point to the new release notes file from ``libcxx/docs/ReleaseNotes.rst``
-
-Post branching
-==============
-
-This is done by the libc++ developers.
-
-After branching it takes a couple of days before the new LLVM ToT version is
-available on `<https://apt.llvm.org>`_. Once it is available the pre-commit CI
-can start using the new ToT version. In order to make sure patches can be
-backported to the release branch the oldest compiler is not removed yet.
-
-The section ``Upcoming Deprecations and Removals`` is cleared by the release
-managers. Copy back the items that were in this section.
+5. Create a release notes file for the next release from the previous ones and point to it from
+   ``libcxx/docs/ReleaseNotes.rst``. Remove entries that do not apply anymore, but keep in mind
+   that some entries (such as upcoming deprecations) may still apply, may need rewording and may
+   also require follow up PRs to implement.
+6. Update the set of runners targeted by the CI on the release branch to ``llvm-premerge-libcxx-release-runners``, and
+   make sure that runner set is using the appropriate image. This ensures that the release branch CI keeps working even
+   if the main branch starts using newer images.
+7. It takes a couple of days before the new LLVM ToT version is available on `<https://apt.llvm.org>`_ after a
----------------
philnik777 wrote:

This isn't up-to-date anymore. We've dropped using https://apt.llvm.org in favour of pulling all our compilers from compiler-explorer.

https://github.com/llvm/llvm-project/pull/210383

From libcxx-commits at lists.llvm.org  Mon Jul 20 00:32:09 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 20 Jul 2026 00:32:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add tests for LWG3798 and mark
	it as Complete (PR #210640)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210640@github.com>
Message-ID: <6a5dcef9.83d97448.1528eb.00e0@mx.google.com>


frederick-vs-ja wrote:

Merging as CI failures were probably unrelated.

https://github.com/llvm/llvm-project/pull/210640

From libcxx-commits at lists.llvm.org  Mon Jul 20 00:32:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 00:32:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 99789ca - [libc++] Add tests for LWG3798
 and mark it as Complete (#210640)
Message-ID: <6a5dcf0c.28821aa3.105dab.bfdf@mx.google.com>


Author: A. Jiang
Date: 2026-07-20T15:32:23+08:00
New Revision: 99789ca18a7982a0b8d63e681d957590f67455c4

URL: https://github.com/llvm/llvm-project/commit/99789ca18a7982a0b8d63e681d957590f67455c4
DIFF: https://github.com/llvm/llvm-project/commit/99789ca18a7982a0b8d63e681d957590f67455c4.diff

LOG: [libc++] Add tests for LWG3798 and mark it as Complete (#210640)

Added: 
    

Modified: 
    libcxx/docs/Status/Cxx23Issues.csv
    libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h
    libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 91c4dc462c0c9..db38715a98973 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -234,7 +234,7 @@
 "`LWG3792 <https://wg21.link/LWG3792>`__","``__cpp_lib_constexpr_algorithms`` should also be defined in ``<utility>``","2022-11 (Kona)","|Complete|","16","`#105046 <https://github.com/llvm/llvm-project/issues/105046>`__",""
 "`LWG3795 <https://wg21.link/LWG3795>`__","Self-move-assignment of ``std::future`` and ``std::shared_future`` have unimplementable postconditions","2022-11 (Kona)","","","`#105047 <https://github.com/llvm/llvm-project/issues/105047>`__",""
 "`LWG3796 <https://wg21.link/LWG3796>`__","``movable-box`` as member should use ``default-initialization`` instead of ``copy-initialization``","2022-11 (Kona)","","","`#105048 <https://github.com/llvm/llvm-project/issues/105048>`__",""
-"`LWG3798 <https://wg21.link/LWG3798>`__","Rvalue reference and ``iterator_category``","2022-11 (Kona)","|Partial|","","`#105049 <https://github.com/llvm/llvm-project/issues/105049>`__","``adjacent_transform_view`` hasn't been done yet since this type isn't implemented yet"
+"`LWG3798 <https://wg21.link/LWG3798>`__","Rvalue reference and ``iterator_category``","2022-11 (Kona)","|Complete|","22","`#105049 <https://github.com/llvm/llvm-project/issues/105049>`__",""
 "`LWG3801 <https://wg21.link/LWG3801>`__","``cartesian_product_view::iterator::distance-from`` ignores the size of last underlying range","2022-11 (Kona)","","","`#105051 <https://github.com/llvm/llvm-project/issues/105051>`__",""
 "`LWG3814 <https://wg21.link/LWG3814>`__","Add freestanding items requested by NB comments","2022-11 (Kona)","","","`#105052 <https://github.com/llvm/llvm-project/issues/105052>`__",""
 "`LWG3816 <https://wg21.link/LWG3816>`__","``flat_map`` and ``flat_multimap`` should impose sequence container requirements","2022-11 (Kona)","","","`#105053 <https://github.com/llvm/llvm-project/issues/105053>`__",""

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h
index 39f2f856c149b..770cd66d011ea 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/helpers.h
@@ -13,6 +13,7 @@
 #include <functional>
 #include <numeric>
 #include <tuple>
+#include <utility>
 
 // intentionally not using meta programming for the expected tuple types
 
@@ -35,6 +36,10 @@ struct GetFirst {
   constexpr decltype(auto) operator()(auto&& first, auto&&...) const { return std::forward<decltype(first)>(first); }
 };
 
+struct GetFirstAsXvalue {
+  static constexpr decltype(auto) operator()(auto&& first, auto&&...) { return std::move(first); }
+};
+
 template <std::size_t N>
 struct ValidateTieFromIndex {
   constexpr void operator()(auto&& buffer, auto&& tuple, std::size_t idx) const {

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp
index 684569516d747..6105dde565082 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/iterator/member_types.compile.pass.cpp
@@ -139,6 +139,10 @@ void test() {
   test<GetFirst, 2, int, false>();
   test<GetFirst, 3, int, false>();
 
+  test<GetFirstAsXvalue, 1, int, false>();
+  test<GetFirstAsXvalue, 2, int, false>();
+  test<GetFirstAsXvalue, 3, int, false>();
+
   test<Multiply, 1, int, true>();
   test<Multiply, 2, int, true>();
   test<Multiply, 3, int, true>();


        

From libcxx-commits at lists.llvm.org  Mon Jul 20 00:32:30 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 20 Jul 2026 00:32:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add tests for LWG3798 and mark
	it as Complete (PR #210640)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210640@github.com>
Message-ID: <6a5dcf0e.ceee070c.33150b.81bb@mx.google.com>


https://github.com/frederick-vs-ja closed https://github.com/llvm/llvm-project/pull/210640

From libcxx-commits at lists.llvm.org  Mon Jul 20 01:09:23 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 01:09:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize to_chars integral
	overloads (PR #210659)
Message-ID: <llvm.org/llvm/llvm-project/pull/210659@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/210659

None

>From 87e575d0c78f6391f7b62099aa68d44880e86efa Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 18 Jul 2026 13:03:43 +0200
Subject: [PATCH] [libc++] Optimize to_chars integral overloads

---
 libcxx/include/__charconv/to_chars_integral.h | 86 +++++++++++++++++++
 libcxx/test/support/charconv_test_helpers.h   |  2 +
 2 files changed, 88 insertions(+)

diff --git a/libcxx/include/__charconv/to_chars_integral.h b/libcxx/include/__charconv/to_chars_integral.h
index 6d425139260b6..25d2cfe82aa92 100644
--- a/libcxx/include/__charconv/to_chars_integral.h
+++ b/libcxx/include/__charconv/to_chars_integral.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___CHARCONV_TO_CHARS_INTEGRAL_H
 
 #include <__algorithm/copy_n.h>
+#include <__algorithm/simd_utils.h>
 #include <__assert>
 #include <__bit/countl.h>
 #include <__charconv/tables.h>
@@ -22,12 +23,14 @@
 #include <__system_error/errc.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_integral.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_signed.h>
 #include <__type_traits/make_32_64_or_128_bit.h>
 #include <__type_traits/make_unsigned.h>
 #include <__utility/unreachable.h>
+#include <array>
 #include <cstdint>
 #include <limits>
 
@@ -107,6 +110,25 @@ __to_chars_integral(char* __first, char* __last, _Tp __value, int __base) {
 
 namespace __itoa {
 
+template <size_t _Duplicate, size_t _IndexCount>
+constexpr array<size_t, _IndexCount * _Duplicate> __build_indices() {
+  array<size_t, _IndexCount * _Duplicate> __ret;
+  for (size_t __i = 0; __i != _IndexCount; ++__i) {
+    for (size_t __k = 0; __k != _Duplicate; ++__k)
+      __ret[__i * _Duplicate + __k] = _IndexCount - __i - 1;
+  }
+  return __ret;
+}
+
+// This is marked `always_inline` because it interacts with simd vectors
+template <size_t _Duplicate, size_t _VecSize>
+[[__gnu__::__always_inline__]] auto __duplicate_vector_entries(__simd_vector<char, _VecSize> __vals) {
+  static constexpr auto __indices = __itoa::__build_indices<_Duplicate, _VecSize>();
+  return [&]<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+    return __builtin_shufflevector(__vals, __vals, __indices[_Indices]...);
+  }(make_index_sequence<_Duplicate * _VecSize>());
+}
+
 template <unsigned _Base>
 struct _LIBCPP_HIDDEN __integral;
 
@@ -122,6 +144,35 @@ struct _LIBCPP_HIDDEN __integral<2> {
   template <typename _Tp>
   _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
   __to_chars(char* __first, char* __last, _Tp __value) {
+#if _LIBCPP_VECTORIZE_ALGORITHMS && __has_builtin(__builtin_masked_store)
+    if (!__libcpp_is_constant_evaluated() && __last - __first >= numeric_limits<_Tp>::digits) {
+      // Move the to-be-converted bits into the high bits, so that they are printed at the start.
+      auto __char_count = __width(__value);
+      auto __shift      = std::__countl_zero(__value | 1);
+      __value <<= __shift;
+
+      // Move the value into a vector and chop it up into its constituent bytes
+      auto __chopped    = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
+
+      // Duplicate values so we can extract the appropriate bits in multiple positions
+      auto __characters = __itoa::__duplicate_vector_entries<8>(__chopped);
+
+      // This is marked `always_inline` because it interacts with simd vectors
+      auto __shifts     = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+        return __simd_vector<char, 8 * sizeof(_Tp)>{(7 - _Indices % 8)...};
+      }(make_index_sequence<8 * sizeof(_Tp)>());
+
+      // Extract the bits we want for a given offset and add to ascii '0' to get '0'/'1' depending on the set bit
+      __characters = '0' + ((__characters >> __shifts) & 1);
+
+      // Store all the characters, no matter whether they're part of the value or not. This is only safe if the buffer
+      // we've been given is large enough.
+      // TODO: Generate a mask on platforms which have native masked stores and use this code path unconditionally.
+      __builtin_masked_store(__simd_vector<bool, 8 * sizeof(_Tp)>(true), __characters, __first);
+      return {__first + __char_count, errc(0)};
+    }
+#endif
+
     ptrdiff_t __cap = __last - __first;
     int __n         = __width(__value);
     if (__n > __cap)
@@ -192,6 +243,41 @@ struct _LIBCPP_HIDDEN __integral<16> {
   template <typename _Tp>
   _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
   __to_chars(char* __first, char* __last, _Tp __value) {
+#if _LIBCPP_VECTORIZE_ALGORITHMS && __has_builtin(__builtin_masked_store)
+    if (!__libcpp_is_constant_evaluated() && __last - __first >= (numeric_limits<_Tp>::digits / 4)) {
+      // Lambdas are marked as `always_inline` because they return vectors, which aren't ABI-stable
+
+      // Move the to-be-converted bits into the high bits, so that they are printed at the start. Note that the shift is
+      // 4-bit aligned, so that the value of the characters doesn't change.
+      auto __char_count = __width(__value);
+      auto __shift      = std::__countl_zero(__value | 1) & ~3;
+      __value <<= __shift;
+
+      // Move the value into a vector and chop it up into its constituent bytes
+      auto __chopped    = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
+
+      // Duplicate values so we can extract the appropriate bits in multiple positions
+      auto __characters = __itoa::__duplicate_vector_entries<2>(__chopped);
+
+      // This is marked `always_inline` because it interacts with simd vectors
+      auto __shifts     = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+        return __simd_vector<char, 2 * sizeof(_Tp)>{(_Indices % 2 == 0 ? 4 : 0)...};
+      }(make_index_sequence<2 * sizeof(_Tp)>());
+
+      // Extract the bits we want for a given offset
+      __characters = (__characters >> __shifts) & 15;
+
+      // Convert the value into the hexadecimal character
+      __characters = (__characters >= 10 ? char('a' - 10) : '0') + __characters;
+
+      // Store all the characters, no matter whether they're part of the value or not. This is only safe if the buffer
+      // we've been given is large enough.
+      // TODO: Generate a mask on platforms which have native masked stores and use this code path unconditionally.
+      __builtin_masked_store(__simd_vector<bool, 2 * sizeof(_Tp)>(true), __characters, __first);
+      return {__first + __char_count, errc(0)};
+    }
+#endif
+
     ptrdiff_t __cap = __last - __first;
     int __n         = __width(__value);
     if (__n > __cap)
diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index fcae09478457b..264f0e28bfb12 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -113,8 +113,10 @@ struct to_chars_test_base
         std::iota(buf, buf + sizeof(buf), static_cast<unsigned char>(1));
         r = std::to_chars(buf, buf + sizeof(buf), v, args...);
         assert(r.ec == std::errc{});
+        /*
         for (std::size_t i = r.ptr - buf; i < sizeof(buf); ++i)
             assert(static_cast<unsigned char>(buf[i]) == i + 1);
+            */
         *r.ptr = '\0';
 
 #ifndef TEST_HAS_NO_INT128


From libcxx-commits at lists.llvm.org  Mon Jul 20 01:10:17 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 01:10:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize to_chars integral
	overloads (PR #210659)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210659@github.com>
Message-ID: <6a5dd7e9.8e75e0cb.f69c7.db79@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/210659

>From 679d4cce4a17732d3d6ff3a45f27a57090cd4de0 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 18 Jul 2026 13:03:43 +0200
Subject: [PATCH] [libc++] Optimize to_chars integral overloads

---
 libcxx/include/__charconv/to_chars_integral.h | 86 +++++++++++++++++++
 libcxx/test/support/charconv_test_helpers.h   |  2 -
 2 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__charconv/to_chars_integral.h b/libcxx/include/__charconv/to_chars_integral.h
index 6d425139260b6..25d2cfe82aa92 100644
--- a/libcxx/include/__charconv/to_chars_integral.h
+++ b/libcxx/include/__charconv/to_chars_integral.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___CHARCONV_TO_CHARS_INTEGRAL_H
 
 #include <__algorithm/copy_n.h>
+#include <__algorithm/simd_utils.h>
 #include <__assert>
 #include <__bit/countl.h>
 #include <__charconv/tables.h>
@@ -22,12 +23,14 @@
 #include <__system_error/errc.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_integral.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_signed.h>
 #include <__type_traits/make_32_64_or_128_bit.h>
 #include <__type_traits/make_unsigned.h>
 #include <__utility/unreachable.h>
+#include <array>
 #include <cstdint>
 #include <limits>
 
@@ -107,6 +110,25 @@ __to_chars_integral(char* __first, char* __last, _Tp __value, int __base) {
 
 namespace __itoa {
 
+template <size_t _Duplicate, size_t _IndexCount>
+constexpr array<size_t, _IndexCount * _Duplicate> __build_indices() {
+  array<size_t, _IndexCount * _Duplicate> __ret;
+  for (size_t __i = 0; __i != _IndexCount; ++__i) {
+    for (size_t __k = 0; __k != _Duplicate; ++__k)
+      __ret[__i * _Duplicate + __k] = _IndexCount - __i - 1;
+  }
+  return __ret;
+}
+
+// This is marked `always_inline` because it interacts with simd vectors
+template <size_t _Duplicate, size_t _VecSize>
+[[__gnu__::__always_inline__]] auto __duplicate_vector_entries(__simd_vector<char, _VecSize> __vals) {
+  static constexpr auto __indices = __itoa::__build_indices<_Duplicate, _VecSize>();
+  return [&]<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+    return __builtin_shufflevector(__vals, __vals, __indices[_Indices]...);
+  }(make_index_sequence<_Duplicate * _VecSize>());
+}
+
 template <unsigned _Base>
 struct _LIBCPP_HIDDEN __integral;
 
@@ -122,6 +144,35 @@ struct _LIBCPP_HIDDEN __integral<2> {
   template <typename _Tp>
   _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
   __to_chars(char* __first, char* __last, _Tp __value) {
+#if _LIBCPP_VECTORIZE_ALGORITHMS && __has_builtin(__builtin_masked_store)
+    if (!__libcpp_is_constant_evaluated() && __last - __first >= numeric_limits<_Tp>::digits) {
+      // Move the to-be-converted bits into the high bits, so that they are printed at the start.
+      auto __char_count = __width(__value);
+      auto __shift      = std::__countl_zero(__value | 1);
+      __value <<= __shift;
+
+      // Move the value into a vector and chop it up into its constituent bytes
+      auto __chopped    = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
+
+      // Duplicate values so we can extract the appropriate bits in multiple positions
+      auto __characters = __itoa::__duplicate_vector_entries<8>(__chopped);
+
+      // This is marked `always_inline` because it interacts with simd vectors
+      auto __shifts     = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+        return __simd_vector<char, 8 * sizeof(_Tp)>{(7 - _Indices % 8)...};
+      }(make_index_sequence<8 * sizeof(_Tp)>());
+
+      // Extract the bits we want for a given offset and add to ascii '0' to get '0'/'1' depending on the set bit
+      __characters = '0' + ((__characters >> __shifts) & 1);
+
+      // Store all the characters, no matter whether they're part of the value or not. This is only safe if the buffer
+      // we've been given is large enough.
+      // TODO: Generate a mask on platforms which have native masked stores and use this code path unconditionally.
+      __builtin_masked_store(__simd_vector<bool, 8 * sizeof(_Tp)>(true), __characters, __first);
+      return {__first + __char_count, errc(0)};
+    }
+#endif
+
     ptrdiff_t __cap = __last - __first;
     int __n         = __width(__value);
     if (__n > __cap)
@@ -192,6 +243,41 @@ struct _LIBCPP_HIDDEN __integral<16> {
   template <typename _Tp>
   _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
   __to_chars(char* __first, char* __last, _Tp __value) {
+#if _LIBCPP_VECTORIZE_ALGORITHMS && __has_builtin(__builtin_masked_store)
+    if (!__libcpp_is_constant_evaluated() && __last - __first >= (numeric_limits<_Tp>::digits / 4)) {
+      // Lambdas are marked as `always_inline` because they return vectors, which aren't ABI-stable
+
+      // Move the to-be-converted bits into the high bits, so that they are printed at the start. Note that the shift is
+      // 4-bit aligned, so that the value of the characters doesn't change.
+      auto __char_count = __width(__value);
+      auto __shift      = std::__countl_zero(__value | 1) & ~3;
+      __value <<= __shift;
+
+      // Move the value into a vector and chop it up into its constituent bytes
+      auto __chopped    = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
+
+      // Duplicate values so we can extract the appropriate bits in multiple positions
+      auto __characters = __itoa::__duplicate_vector_entries<2>(__chopped);
+
+      // This is marked `always_inline` because it interacts with simd vectors
+      auto __shifts     = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+        return __simd_vector<char, 2 * sizeof(_Tp)>{(_Indices % 2 == 0 ? 4 : 0)...};
+      }(make_index_sequence<2 * sizeof(_Tp)>());
+
+      // Extract the bits we want for a given offset
+      __characters = (__characters >> __shifts) & 15;
+
+      // Convert the value into the hexadecimal character
+      __characters = (__characters >= 10 ? char('a' - 10) : '0') + __characters;
+
+      // Store all the characters, no matter whether they're part of the value or not. This is only safe if the buffer
+      // we've been given is large enough.
+      // TODO: Generate a mask on platforms which have native masked stores and use this code path unconditionally.
+      __builtin_masked_store(__simd_vector<bool, 2 * sizeof(_Tp)>(true), __characters, __first);
+      return {__first + __char_count, errc(0)};
+    }
+#endif
+
     ptrdiff_t __cap = __last - __first;
     int __n         = __width(__value);
     if (__n > __cap)
diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index fcae09478457b..f6b60a2c8fe4d 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -113,8 +113,6 @@ struct to_chars_test_base
         std::iota(buf, buf + sizeof(buf), static_cast<unsigned char>(1));
         r = std::to_chars(buf, buf + sizeof(buf), v, args...);
         assert(r.ec == std::errc{});
-        for (std::size_t i = r.ptr - buf; i < sizeof(buf); ++i)
-            assert(static_cast<unsigned char>(buf[i]) == i + 1);
         *r.ptr = '\0';
 
 #ifndef TEST_HAS_NO_INT128


From libcxx-commits at lists.llvm.org  Mon Jul 20 01:12:04 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 01:12:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize to_chars integral
	overloads (PR #210659)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210659@github.com>
Message-ID: <6a5dd854.d1496011.199a59.61a4@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions h -- libcxx/include/__charconv/to_chars_integral.h libcxx/test/support/charconv_test_helpers.h --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/__charconv/to_chars_integral.h b/libcxx/include/__charconv/to_chars_integral.h
index 25d2cfe82..4a7ba0fef 100644
--- a/libcxx/include/__charconv/to_chars_integral.h
+++ b/libcxx/include/__charconv/to_chars_integral.h
@@ -152,13 +152,13 @@ struct _LIBCPP_HIDDEN __integral<2> {
       __value <<= __shift;
 
       // Move the value into a vector and chop it up into its constituent bytes
-      auto __chopped    = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
+      auto __chopped = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
 
       // Duplicate values so we can extract the appropriate bits in multiple positions
       auto __characters = __itoa::__duplicate_vector_entries<8>(__chopped);
 
       // This is marked `always_inline` because it interacts with simd vectors
-      auto __shifts     = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+      auto __shifts = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
         return __simd_vector<char, 8 * sizeof(_Tp)>{(7 - _Indices % 8)...};
       }(make_index_sequence<8 * sizeof(_Tp)>());
 
@@ -254,13 +254,13 @@ struct _LIBCPP_HIDDEN __integral<16> {
       __value <<= __shift;
 
       // Move the value into a vector and chop it up into its constituent bytes
-      auto __chopped    = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
+      auto __chopped = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
 
       // Duplicate values so we can extract the appropriate bits in multiple positions
       auto __characters = __itoa::__duplicate_vector_entries<2>(__chopped);
 
       // This is marked `always_inline` because it interacts with simd vectors
-      auto __shifts     = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+      auto __shifts = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
         return __simd_vector<char, 2 * sizeof(_Tp)>{(_Indices % 2 == 0 ? 4 : 0)...};
       }(make_index_sequence<2 * sizeof(_Tp)>());
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/210659

From libcxx-commits at lists.llvm.org  Mon Jul 20 01:12:14 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 01:12:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize to_chars integral
 overloads for base 2 and 16 (PR #210659)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210659@github.com>
Message-ID: <6a5dd85e.e5907290.281687.29a5@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/210659

From libcxx-commits at lists.llvm.org  Mon Jul 20 01:13:09 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 01:13:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][test] Fix build failure from
	typo in #171785 (PR #210552)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210552@github.com>
Message-ID: <6a5dd895.6c8668f2.1f24.9bd6@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/210552

From libcxx-commits at lists.llvm.org  Mon Jul 20 01:24:40 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 01:24:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][docs] Avoid talking about other
 implementations on DR status (PR #210637)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210637@github.com>
Message-ID: <6a5ddb48.824e11a6.9c376.8c94@mx.google.com>


https://github.com/philnik777 approved this pull request.

I think this is a good idea. The documentation is about libc++ and not other implementations. You probably want to wait for others to chime in though.

https://github.com/llvm/llvm-project/pull/210637

From libcxx-commits at lists.llvm.org  Mon Jul 20 01:35:30 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 01:35:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++][NFC] Inline
 std::function members into the class body" (#209555) (PR #210260)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210260@github.com>
Message-ID: <6a5dddd2.0c05e68e.3c2f5d.c556@mx.google.com>


https://github.com/philnik777 ready_for_review https://github.com/llvm/llvm-project/pull/210260

From libcxx-commits at lists.llvm.org  Mon Jul 20 01:35:33 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 01:35:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 4234ff6 - Reapply "[libc++][NFC] Inline
 std::function members into the class body" (#209555) (#210260)
Message-ID: <6a5dddd5.66c0679a.24976c.ce03@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-20T10:35:29+02:00
New Revision: 4234ff65326237102157875f6ddf098355864178

URL: https://github.com/llvm/llvm-project/commit/4234ff65326237102157875f6ddf098355864178
DIFF: https://github.com/llvm/llvm-project/commit/4234ff65326237102157875f6ddf098355864178.diff

LOG: Reapply "[libc++][NFC] Inline std::function members into the class body" (#209555) (#210260)

This caused LLDB to fail, which has been fixed now.

This reverts commit 7618426138aae95561da676a1d1e10ee0392bf78.

Added: 
    

Modified: 
    libcxx/include/__functional/function.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__functional/function.h b/libcxx/include/__functional/function.h
index 90072e9528484..0adc336424e2e 100644
--- a/libcxx/include/__functional/function.h
+++ b/libcxx/include/__functional/function.h
@@ -340,7 +340,7 @@ template <typename _Fun>
 struct __use_small_storage
     : public integral_constant<
           bool,
-          sizeof(_Fun) <= sizeof(__policy_storage)&& _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
+          sizeof(_Fun) <= sizeof(__policy_storage) && _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
               is_trivially_copy_constructible<_Fun>::value && is_trivially_destructible<_Fun>::value> {};
 
 // Policy contains information about how to copy, destroy, and move the
@@ -628,10 +628,10 @@ class function<_Rp(_ArgTypes...)>
   // construct/copy/destroy:
   _LIBCPP_HIDE_FROM_ABI function() _NOEXCEPT {}
   _LIBCPP_HIDE_FROM_ABI function(nullptr_t) _NOEXCEPT {}
-  _LIBCPP_HIDE_FROM_ABI function(const function&);
-  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function(const function&)      = default;
+  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT = default;
   template <class _Fp, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(_Fp);
+  _LIBCPP_HIDE_FROM_ABI function(_Fp __f) : __f_(std::move(__f)) {}
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Alloc>
@@ -639,23 +639,35 @@ class function<_Rp(_ArgTypes...)>
   template <class _Alloc>
   _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, nullptr_t) _NOEXCEPT {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function&);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&&);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
   template <class _Fp, class _Alloc, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc& __a, _Fp __f);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI function& operator=(const function&);
-  _LIBCPP_HIDE_FROM_ABI function& operator=(function&&) _NOEXCEPT;
-  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function& operator=(const function& __f) {
+    function(__f).swap(*this);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI function& operator=(function&& __f) _NOEXCEPT = default;
+
+  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT {
+    __f_ = nullptr;
+    return *this;
+  }
+
   template <class _Fp, class = _EnableIfLValueCallable<__decay_t<_Fp>>>
-  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&&);
+  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&& __f) {
+    function(std::forward<_Fp>(__f)).swap(*this);
+    return *this;
+  }
 
-  _LIBCPP_HIDE_FROM_ABI ~function();
+  _LIBCPP_HIDE_FROM_ABI ~function() = default;
 
   // function modifiers:
-  _LIBCPP_HIDE_FROM_ABI void swap(function&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI void swap(function& __f) _NOEXCEPT { __f_.swap(__f.__f_); }
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Fp, class _Alloc>
@@ -677,15 +689,23 @@ class function<_Rp(_ArgTypes...)>
 
 public:
   // function invocation:
-  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes...) const;
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const { return __f_(std::forward<_ArgTypes>(__args)...); }
 
 #  if _LIBCPP_HAS_RTTI
   // function target access:
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT {
+    return __f_.target_type();
+  }
+
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT {
+    return (_Tp*)(__f_.template target<_Tp>());
+  }
+
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT {
+    return __f_.template target<_Tp>();
+  }
 #  endif // _LIBCPP_HAS_RTTI
 };
 
@@ -697,93 +717,6 @@ template <class _Fp, class _Stripped = __strip_signature_t<decltype(&_Fp::operat
 function(_Fp) -> function<_Stripped>;
 #  endif // _LIBCPP_STD_VER >= 17
 
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::function(const function& __f) : __f_(__f.__f_) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Alloc>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::function(function&& __f) _NOEXCEPT : __f_(std::move(__f.__f_)) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Alloc>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class>
-function<_Rp(_ArgTypes...)>::function(_Fp __f) : __f_(std::move(__f)) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class _Alloc, class>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(const function& __f) {
-  function(__f).swap(*this);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(function&& __f) _NOEXCEPT {
-  __f_ = std::move(__f.__f_);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(nullptr_t) _NOEXCEPT {
-  __f_ = nullptr;
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f) {
-  function(std::forward<_Fp>(__f)).swap(*this);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::~function() {}
-
-template <class _Rp, class... _ArgTypes>
-void function<_Rp(_ArgTypes...)>::swap(function& __f) _NOEXCEPT {
-  __f_.swap(__f.__f_);
-}
-
-template <class _Rp, class... _ArgTypes>
-_Rp function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const {
-  return __f_(std::forward<_ArgTypes>(__arg)...);
-}
-
-#  if _LIBCPP_HAS_RTTI
-
-template <class _Rp, class... _ArgTypes>
-const std::type_info& function<_Rp(_ArgTypes...)>::target_type() const _NOEXCEPT {
-  return __f_.target_type();
-}
-
-template <class _Rp, class... _ArgTypes>
-template <typename _Tp>
-_Tp* function<_Rp(_ArgTypes...)>::target() _NOEXCEPT {
-  return (_Tp*)(__f_.template target<_Tp>());
-}
-
-template <class _Rp, class... _ArgTypes>
-template <typename _Tp>
-const _Tp* function<_Rp(_ArgTypes...)>::target() const _NOEXCEPT {
-  return __f_.template target<_Tp>();
-}
-
-#  endif // _LIBCPP_HAS_RTTI
-
 template <class _Rp, class... _ArgTypes>
 inline _LIBCPP_HIDE_FROM_ABI bool operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) _NOEXCEPT {
   return !__f;


        

From libcxx-commits at lists.llvm.org  Mon Jul 20 01:35:35 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 01:35:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++][NFC] Inline
 std::function members into the class body" (#209555) (PR #210260)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210260@github.com>
Message-ID: <6a5dddd7.9edb5c9d.280be3.e7cc@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/210260

From libcxx-commits at lists.llvm.org  Mon Jul 20 01:36:07 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 01:36:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Reapply "[libc++][NFC] Inline
 std::function members into the class body" (#209555) (PR #210260)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210260@github.com>
Message-ID: <6a5dddf7.160324b0.1f653e.8cfd@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

This caused LLDB to fail, which has been fixed now.

This reverts commit 7618426138aae95561da676a1d1e10ee0392bf78.


---
Full diff: https://github.com/llvm/llvm-project/pull/210260.diff


1 Files Affected:

- (modified) libcxx/include/__functional/function.h (+37-104) 


``````````diff
diff --git a/libcxx/include/__functional/function.h b/libcxx/include/__functional/function.h
index 90072e9528484..0adc336424e2e 100644
--- a/libcxx/include/__functional/function.h
+++ b/libcxx/include/__functional/function.h
@@ -340,7 +340,7 @@ template <typename _Fun>
 struct __use_small_storage
     : public integral_constant<
           bool,
-          sizeof(_Fun) <= sizeof(__policy_storage)&& _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
+          sizeof(_Fun) <= sizeof(__policy_storage) && _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
               is_trivially_copy_constructible<_Fun>::value && is_trivially_destructible<_Fun>::value> {};
 
 // Policy contains information about how to copy, destroy, and move the
@@ -628,10 +628,10 @@ class function<_Rp(_ArgTypes...)>
   // construct/copy/destroy:
   _LIBCPP_HIDE_FROM_ABI function() _NOEXCEPT {}
   _LIBCPP_HIDE_FROM_ABI function(nullptr_t) _NOEXCEPT {}
-  _LIBCPP_HIDE_FROM_ABI function(const function&);
-  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function(const function&)      = default;
+  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT = default;
   template <class _Fp, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(_Fp);
+  _LIBCPP_HIDE_FROM_ABI function(_Fp __f) : __f_(std::move(__f)) {}
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Alloc>
@@ -639,23 +639,35 @@ class function<_Rp(_ArgTypes...)>
   template <class _Alloc>
   _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, nullptr_t) _NOEXCEPT {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function&);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&&);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
   template <class _Fp, class _Alloc, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc& __a, _Fp __f);
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI function& operator=(const function&);
-  _LIBCPP_HIDE_FROM_ABI function& operator=(function&&) _NOEXCEPT;
-  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function& operator=(const function& __f) {
+    function(__f).swap(*this);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI function& operator=(function&& __f) _NOEXCEPT = default;
+
+  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT {
+    __f_ = nullptr;
+    return *this;
+  }
+
   template <class _Fp, class = _EnableIfLValueCallable<__decay_t<_Fp>>>
-  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&&);
+  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&& __f) {
+    function(std::forward<_Fp>(__f)).swap(*this);
+    return *this;
+  }
 
-  _LIBCPP_HIDE_FROM_ABI ~function();
+  _LIBCPP_HIDE_FROM_ABI ~function() = default;
 
   // function modifiers:
-  _LIBCPP_HIDE_FROM_ABI void swap(function&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI void swap(function& __f) _NOEXCEPT { __f_.swap(__f.__f_); }
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Fp, class _Alloc>
@@ -677,15 +689,23 @@ class function<_Rp(_ArgTypes...)>
 
 public:
   // function invocation:
-  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes...) const;
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const { return __f_(std::forward<_ArgTypes>(__args)...); }
 
 #  if _LIBCPP_HAS_RTTI
   // function target access:
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT {
+    return __f_.target_type();
+  }
+
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT {
+    return (_Tp*)(__f_.template target<_Tp>());
+  }
+
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT {
+    return __f_.template target<_Tp>();
+  }
 #  endif // _LIBCPP_HAS_RTTI
 };
 
@@ -697,93 +717,6 @@ template <class _Fp, class _Stripped = __strip_signature_t<decltype(&_Fp::operat
 function(_Fp) -> function<_Stripped>;
 #  endif // _LIBCPP_STD_VER >= 17
 
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::function(const function& __f) : __f_(__f.__f_) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Alloc>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::function(function&& __f) _NOEXCEPT : __f_(std::move(__f.__f_)) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Alloc>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class>
-function<_Rp(_ArgTypes...)>::function(_Fp __f) : __f_(std::move(__f)) {}
-
-#  if _LIBCPP_STD_VER <= 14
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class _Alloc, class>
-function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
-#  endif
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(const function& __f) {
-  function(__f).swap(*this);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(function&& __f) _NOEXCEPT {
-  __f_ = std::move(__f.__f_);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(nullptr_t) _NOEXCEPT {
-  __f_ = nullptr;
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-template <class _Fp, class>
-function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f) {
-  function(std::forward<_Fp>(__f)).swap(*this);
-  return *this;
-}
-
-template <class _Rp, class... _ArgTypes>
-function<_Rp(_ArgTypes...)>::~function() {}
-
-template <class _Rp, class... _ArgTypes>
-void function<_Rp(_ArgTypes...)>::swap(function& __f) _NOEXCEPT {
-  __f_.swap(__f.__f_);
-}
-
-template <class _Rp, class... _ArgTypes>
-_Rp function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const {
-  return __f_(std::forward<_ArgTypes>(__arg)...);
-}
-
-#  if _LIBCPP_HAS_RTTI
-
-template <class _Rp, class... _ArgTypes>
-const std::type_info& function<_Rp(_ArgTypes...)>::target_type() const _NOEXCEPT {
-  return __f_.target_type();
-}
-
-template <class _Rp, class... _ArgTypes>
-template <typename _Tp>
-_Tp* function<_Rp(_ArgTypes...)>::target() _NOEXCEPT {
-  return (_Tp*)(__f_.template target<_Tp>());
-}
-
-template <class _Rp, class... _ArgTypes>
-template <typename _Tp>
-const _Tp* function<_Rp(_ArgTypes...)>::target() const _NOEXCEPT {
-  return __f_.template target<_Tp>();
-}
-
-#  endif // _LIBCPP_HAS_RTTI
-
 template <class _Rp, class... _ArgTypes>
 inline _LIBCPP_HIDE_FROM_ABI bool operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) _NOEXCEPT {
   return !__f;

``````````

</details>


https://github.com/llvm/llvm-project/pull/210260

From libcxx-commits at lists.llvm.org  Mon Jul 20 01:41:03 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 01:41:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Always disable buffering of the
 stream underlying std::filebuf (PR #209872)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209872@github.com>
Message-ID: <6a5ddf1f.e55dc341.1c6a0.abd7@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/209872

>From 71721e6a480427a3410333e46cb62cd58cc30c39 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 15 Jul 2026 21:40:13 +0200
Subject: [PATCH] [libc++] Always disable buffering of the stream underlying
 std::filebuf

---
 libcxx/docs/ReleaseNotes/24.rst               |  3 +-
 libcxx/include/fstream                        | 70 +++----------------
 .../fstreams/filebuf.virtuals/setbuf.pass.cpp |  4 +-
 3 files changed, 14 insertions(+), 63 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..163a8cc6c37db 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -42,6 +42,8 @@ Implemented Papers
 Improvements and New Features
 -----------------------------
 
+- ``std::fstream`` disables buffering of the underlying stream now to avoid double buffering and to better respect the
+  users requests, improving performance by up to 2x.
 
 Deprecations and Removals
 -------------------------
@@ -71,4 +73,3 @@ ABI Affecting Changes
 
 Build System Changes
 --------------------
-
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..6c14978b6b1b5 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -341,43 +341,7 @@ private:
   state_type __st_;
   state_type __st_last_;
   ios_base::openmode __om_;
-  // There have been no file operations yet, which allows setting unbuffered
-  // I/O mode.
-  static const ios_base::openmode __no_io_operations = ios_base::trunc;
-  // Unbuffered I/O mode has been requested.
-  static const ios_base::openmode __use_unbuffered_io = ios_base::ate;
-  // Used to track the currently used mode and track whether the output should
-  // be unbuffered.
-  // [filebuf.virtuals]/12
-  //   If setbuf(0, 0) is called on a stream before any I/O has occurred on
-  //   that stream, the stream becomes unbuffered. Otherwise the results are
-  //   implementation-defined.
-  // This allows calling setbuf(0, 0)
-  // - before opening a file,
-  // - after opening a file, before
-  //   - a read
-  //   - a write
-  //   - a seek.
-  // Note that opening a file with ios_base::ate does a seek operation.
-  // Normally underflow, overflow, and sync change this flag to ios_base::in,
-  // ios_base_out, or 0.
-  //
-  // The ios_base::trunc and ios_base::ate flags are not used in __cm_. They
-  // are used to track the state of the unbuffered request. For readability
-  // they have the aliases __no_io_operations and __use_unbuffered_io
-  // respectively.
-  //
-  // The __no_io_operations and __use_unbuffered_io flags are used in the
-  // following way:
-  // - __no_io_operations is set upon construction to indicate the unbuffered
-  //   state can be set.
-  // - When requesting unbuffered output:
-  //   - If the file is open it sets the mode.
-  //   - Else places a request by adding the __use_unbuffered_io flag.
-  // - When a file is opened it checks whether both __no_io_operations and
-  //   __use_unbuffered_io are set. If so switches to unbuffered mode.
-  // - All file I/O operations change the mode effectively clearing the
-  //   __no_io_operations and __use_unbuffered_io flags.
+  // Used to track the currently used mode.
   ios_base::openmode __cm_;
   bool __owns_eb_;
   bool __owns_ib_;
@@ -400,11 +364,6 @@ private:
       return nullptr;
 
     __om_ = __mode;
-    if (__cm_ == (__no_io_operations | __use_unbuffered_io)) {
-      std::setbuf(__file_, nullptr);
-      __cm_ = 0;
-    }
-
     if (__mode & ios_base::ate) {
       __cm_ = 0;
       if (fseek(__file_, 0, SEEK_END)) {
@@ -417,18 +376,6 @@ private:
     return this;
   }
 
-  // If the file is already open, switch to unbuffered mode. Otherwise, record
-  // the request to use unbuffered mode so that we use that mode when we
-  // eventually open the file.
-  _LIBCPP_HIDE_FROM_ABI void __request_unbuffered_mode() {
-    if (__file_) {
-      std::setbuf(__file_, nullptr);
-      __cm_ = 0;
-    } else {
-      __cm_ = __no_io_operations | __use_unbuffered_io;
-    }
-  }
-
   _LIBCPP_HIDE_FROM_ABI typename traits_type::int_type __overflow_failed() {
     if (this->pptr() == this->epptr() + 1) {
       this->pbump(-1); // lose the character we overflowed above -- we don't really have a
@@ -451,7 +398,7 @@ basic_filebuf<_CharT, _Traits>::basic_filebuf()
       __st_(),
       __st_last_(),
       __om_(0),
-      __cm_(__no_io_operations),
+      __cm_(),
       __owns_eb_(false),
       __owns_ib_(false),
       __always_noconv_(false) {
@@ -742,7 +689,12 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(std::fopen(__s, __mdstr), __mode);
+  if (FILE* __file = std::fopen(__s, __mdstr)) {
+    // We already buffer - no need to buffer twice
+    std::setbuf(__file, nullptr);
+    return __do_open(__file, __mode);
+  }
+  return nullptr;
 }
 
 template <class _CharT, class _Traits>
@@ -793,7 +745,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::close() {
     // since the user may re-open the stream.
     this->setg(nullptr, nullptr, nullptr);
     this->setp(nullptr, nullptr);
-    __cm_ = __no_io_operations;
+    __cm_ = 0;
   }
   return __rt;
 }
@@ -949,9 +901,7 @@ template <class _CharT, class _Traits>
 basic_streambuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::setbuf(char_type* __s, streamsize __n) {
   this->setg(nullptr, nullptr, nullptr);
   this->setp(nullptr, nullptr);
-  // Calling setbuf(nullptr, 0) before any i/o operation switches the stream to unbuffered mode
-  if (__cm_ == __no_io_operations && __s == nullptr && __n == 0)
-    __request_unbuffered_mode();
+  __cm_ = 0;
   if (__owns_eb_)
     delete[] __extbuf_;
   if (__owns_ib_)
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/setbuf.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/setbuf.pass.cpp
index 72af0a2db1180..f1f762c095485 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/setbuf.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/setbuf.pass.cpp
@@ -97,8 +97,8 @@ static void unbuffered_request_after_open_ate() {
 
   buffer.sputc(CharT('a'));
   assert(file_size<CharT>("test.dat") <= 1);
-  // on libc++ buffering is used by default.
-  LIBCPP_ASSERT(file_size<CharT>("test.dat") == 0);
+  // libc++ doesn't buffer if the buffer is explicitly removed.
+  LIBCPP_NON_FROZEN_ASSERT(file_size<CharT>("test.dat") == 1);
 
   buffer.close();
   assert(file_size<CharT>("test.dat") == 1);


From libcxx-commits at lists.llvm.org  Mon Jul 20 02:22:07 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 02:22:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][docs] Avoid talking about other
 implementations on DR status (PR #210637)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210637@github.com>
Message-ID: <6a5de8bf.9edb5c9d.280be3.b92d@mx.google.com>


================
@@ -95,7 +95,7 @@
 "`P1202R5 <https://wg21.link/P1202R5>`__","Asymmetric Fences","2022-11 (Kona)","","","`#105238 <https://github.com/llvm/llvm-project/issues/105238>`__",""
 "`P1264R2 <https://wg21.link/P1264R2>`__","Revising the wording of ``stream`` input operations","2022-11 (Kona)","|Complete|","9","`#105239 <https://github.com/llvm/llvm-project/issues/105239>`__",""
 "`P1478R8 <https://wg21.link/P1478R8>`__","``Byte-wise`` ``atomic`` ``memcpy``","2022-11 (Kona)","","","`#105240 <https://github.com/llvm/llvm-project/issues/105240>`__",""
-"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","|Complete|","20","`#105241 <https://github.com/llvm/llvm-project/issues/105241>`__","The `[cmp.alg] <https://eel.is/c++draft/cmp.alg>`__ part is implemented as a DR against C++20. MSVC STL does the same. Other parts are Nothing To Do."
+"`P2167R3 <https://wg21.link/P2167R3>`__","Improved Proposed Wording for LWG 2114","2022-11 (Kona)","|Complete|","20","`#105241 <https://github.com/llvm/llvm-project/issues/105241>`__","The `[cmp.alg] <https://eel.is/c++draft/cmp.alg>`__ part is implemented as a DR against C++20."
----------------
H-G-Hristov wrote:

It's just a suggestion. Anyway I think using wg21.link consistently is better and seems more "official".

https://github.com/llvm/llvm-project/pull/210637

From libcxx-commits at lists.llvm.org  Mon Jul 20 02:23:11 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 02:23:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] ead8fd8 - [libc++][ranges][NFC] Format
	`lazy_split_view` (#210644)
Message-ID: <6a5de8ff.7d702ed2.13d102.6a00@mx.google.com>


Author: Hristo Hristov
Date: 2026-07-20T12:23:06+03:00
New Revision: ead8fd8e29fbf3774b7a08bf7b991c2ada30112b

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

LOG: [libc++][ranges][NFC] Format `lazy_split_view` (#210644)

Pre-requisite for: https://github.com/llvm/llvm-project/pull/193891

Added: 
    

Modified: 
    libcxx/include/__ranges/lazy_split_view.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index 27a9d5e77abf1..be2b86772fa0d 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -104,7 +104,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
     if constexpr (forward_range<_View>) {
-      return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::begin(__base_)};
+      return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::begin(__base_)};
     } else {
       __current_.__emplace(ranges::begin(__base_));
       return __outer_iterator<false>{*this};
@@ -120,7 +120,7 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires forward_range<_View> && common_range<_View>
   {
-    return __outer_iterator < __simple_view<_View> && __simple_view < _Pattern >> {*this, ranges::end(__base_)};
+    return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::end(__base_)};
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const {
@@ -278,9 +278,9 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <forward_range _Tp>
   struct __inner_iterator_category<_Tp> {
     using iterator_category =
-        _If< derived_from<typename iterator_traits<iterator_t<_Tp>>::iterator_category, forward_iterator_tag>,
-             forward_iterator_tag,
-             typename iterator_traits<iterator_t<_Tp>>::iterator_category >;
+        _If<derived_from<typename iterator_traits<iterator_t<_Tp>>::iterator_category, forward_iterator_tag>,
+            forward_iterator_tag,
+            typename iterator_traits<iterator_t<_Tp>>::iterator_category>;
   };
 
   template <bool _Const>
@@ -408,8 +408,8 @@ template <class _Range, class _Pattern>
 lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
 template <input_range _Range>
-lazy_split_view(_Range&&,
-                range_value_t<_Range>) -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+lazy_split_view(_Range&&, range_value_t<_Range>)
+    -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
 namespace views {
 namespace __lazy_split_view {


        

From libcxx-commits at lists.llvm.org  Mon Jul 20 02:23:13 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 02:23:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format
	`lazy_split_view` (PR #210644)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210644@github.com>
Message-ID: <6a5de901.d76d81af.23b347.eeaa@mx.google.com>


https://github.com/Zingam closed https://github.com/llvm/llvm-project/pull/210644

From libcxx-commits at lists.llvm.org  Mon Jul 20 02:23:34 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 02:23:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] eec2f09 - [libc++][NFC] Removed
 duplicated `_LIBCPP_INTRODUCED_IN_LLVM_23` macros (#210602)
Message-ID: <6a5de916.510f576d.2f694c.94f7@mx.google.com>


Author: Hristo Hristov
Date: 2026-07-20T12:23:30+03:00
New Revision: eec2f0916d44c33beac52f4396c7a9c98b0c9cf6

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

LOG: [libc++][NFC] Removed duplicated `_LIBCPP_INTRODUCED_IN_LLVM_23` macros (#210602)

Removed the redundant macros, which apparently were added by two
different patches.

Added: 
    

Modified: 
    libcxx/include/__configuration/availability.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 735af5d431cb1..1fbd87ba52a64 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -38,8 +38,6 @@
 // When availability annotations are disabled, we take for granted that features introduced
 // in all versions of the library are available.
 #if !_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS
-#  define _LIBCPP_INTRODUCED_IN_LLVM_23 1
-#  define _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE /* nothing */
 
 #  define _LIBCPP_INTRODUCED_IN_LLVM_23 1
 #  define _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE /* nothing */


        

From libcxx-commits at lists.llvm.org  Mon Jul 20 02:23:37 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 02:23:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Removed duplicated
 `_LIBCPP_INTRODUCED_IN_LLVM_23` macros (PR #210602)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210602@github.com>
Message-ID: <6a5de919.3cce8983.3cf542.8fbe@mx.google.com>


https://github.com/Zingam closed https://github.com/llvm/llvm-project/pull/210602

From libcxx-commits at lists.llvm.org  Mon Jul 20 02:24:02 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 02:24:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 39a2c6a - [libc++][test][NFC] Removed
 unsupported compilers from tests (#210413)
Message-ID: <6a5de932.1e1ac41d.3969c3.5485@mx.google.com>


Author: Hristo Hristov
Date: 2026-07-20T12:23:58+03:00
New Revision: 39a2c6a695f9d045b05fd5798a7cb99f3c7425a7

URL: https://github.com/llvm/llvm-project/commit/39a2c6a695f9d045b05fd5798a7cb99f3c7425a7
DIFF: https://github.com/llvm/llvm-project/commit/39a2c6a695f9d045b05fd5798a7cb99f3c7425a7.diff

LOG: [libc++][test][NFC] Removed unsupported compilers from tests (#210413)

Added: 
    

Modified: 
    libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
    libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
index 46a4b1c788ef9..f9c910eca1f47 100644
--- a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 // UNSUPPORTED: gcc
-// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22, clang-23, clang-24, apple-clang-17, apple-clang-21
+// UNSUPPORTED: clang-21, clang-22, clang-23, clang-24, apple-clang-21
 
 // ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-dynamic-class-memaccess
 

diff  --git a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp
index 16c326f062f79..20275ce1f7adf 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.ops.sat/saturating.bitint.pass.cpp
@@ -8,10 +8,8 @@
 
 // REQUIRES: std-at-least-c++26
 
-// TODO(LLVM25): remove these guards. Clang <= 22 mis-evaluates
-// std::saturating_mul on non-byte-aligned _BitInt. https://llvm.org/PR204085
-// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22
-// UNSUPPORTED: apple-clang-17, apple-clang-18, apple-clang-19, apple-clang-20, apple-clang-21
+// UNSUPPORTED: clang-21, clang-22
+// UNSUPPORTED: apple-clang-21
 // UNSUPPORTED: target={{.+}}-android{{.*}}
 
 // <numeric>


        

From libcxx-commits at lists.llvm.org  Mon Jul 20 02:24:06 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 02:24:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test][NFC] Removed unsupported
 compilers from tests (PR #210413)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210413@github.com>
Message-ID: <6a5de936.f3151ff8.37ac76.890d@mx.google.com>


https://github.com/Zingam closed https://github.com/llvm/llvm-project/pull/210413

From libcxx-commits at lists.llvm.org  Mon Jul 20 02:28:44 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 02:28:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a5dea4c.1e1ac41d.3969c3.3570@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From deea37f2081e61d37e69f89460f795aa5efe0720 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/13] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From aef95d864aae59df8f6dec2fa66ddc1a719f5638 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/13] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 23a51b634dcc3504d5b557ac93030bb0857656f2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/13] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From de998041add137b8e32fdef4863e4474dd21b5ec Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/13] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 8fcfd5da0ee11a5cdb8de5496630ef8c5ebacfd6 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/13] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 32db34b5c5169d6d705616b2e34278521ebb9885 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/13] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 62abfba3c272781d184751584632cb4cdd1e1c6c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/13] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From e4c8d234ef2affc34b7e3d16c85990904ebaf05a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/13] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From a036b2f2f08b3004f29713b9ae8fcb054cbf6a21 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/13] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From dccc322245a6507d61d3702d6141f1f4417d52bf Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/13] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 79c4e1f418372..95a3696e1ce57 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,7 +40,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -48,7 +48,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -56,7 +56,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -82,7 +82,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index 4ff65be645220..359f5a6099fce 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From ead248ba9c01d1558bb2cb0d229e9661d85eeefb Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/13] Format

---
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 .../range.elements/iterator/base.pass.cpp     | 22 +++++++++----------
 .../range.elements/sentinel/base.pass.cpp     |  2 +-
 .../range.split/iterator/base.pass.cpp        | 10 ++++-----
 .../range.split/iterator/deref.pass.cpp       |  7 +++---
 .../sentinel/ctor.convert.pass.cpp            |  4 ++--
 6 files changed, 23 insertions(+), 24 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 2522dd98e4b9c..0da468245e8cd 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 95a3696e1ce57..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,10 +22,9 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
-      { std::declval<T>().base() } noexcept;
-    };
+concept IsBaseNoexcept = requires {
+  { std::declval<T>().base() } noexcept;
+};
 
 using BaseView     = std::ranges::subrange<std::tuple<int>*>;
 using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<BaseView, 0>>;
@@ -41,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -56,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -65,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -82,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From ccc5a5ef18913e717a58b3c89d8209dbc0ebe1e2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/13] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..6b45987b15474 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 0da468245e8cd..90b8862cf382c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 19f8cb6337144ac348a237cbd3b39efd6b0067ee Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/13] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {


From libcxx-commits at lists.llvm.org  Mon Jul 20 02:37:56 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 02:37:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5dec74.6c8668f2.1f24.cdd9@mx.google.com>


Zingam wrote:

@lknknm FYI only: after you merge a PR it is advisable to keep an eye on the Chromium CI for failures: https://ci.chromium.org/p/fuchsia/g/llvm/console?limit=200

https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Mon Jul 20 03:07:59 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 20 Jul 2026 03:07:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a5df37f.c4d3d80a.849c3.d68f@mx.google.com>


================
@@ -82,7 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
----------------
frederick-vs-ja wrote:

Also, I don't think this PR should be of WIP status for so long. IIUC we just need to add some `static_assert(!std::is_constructible_v<...>);` and possibly `static_assert(!std::is_convertible_v<...>);` to verify that we've achieved (some aspects of) the intent of the P3059R2.

In a future PR we may make some internal constructors take `View*` instead of `View&`, which will slightly reduce calls to `std::addressof` and slightly save throughput.

https://github.com/llvm/llvm-project/pull/193891

From libcxx-commits at lists.llvm.org  Mon Jul 20 03:07:59 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 20 Jul 2026 03:07:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a5df37f.b4a331f0.d9742.7f39@mx.google.com>


================
@@ -82,7 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
----------------
frederick-vs-ja wrote:

Let's remove this unused alias which made GCC unhappy.
```suggestion
```

https://github.com/llvm/llvm-project/pull/193891

From libcxx-commits at lists.llvm.org  Mon Jul 20 03:08:45 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 20 Jul 2026 03:08:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a5df3ad.e5907290.281687.322d@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/193891

From libcxx-commits at lists.llvm.org  Mon Jul 20 03:27:02 2026
From: libcxx-commits at lists.llvm.org (Xavier Roche via libcxx-commits)
Date: Mon, 20 Jul 2026 03:27:02 -0700 (PDT)
Subject: [libcxx-commits] [clang] [compiler-rt] [libcxx] [Clang] Support
 atomic operations on _BitInt(N) (PR #204815)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204815@github.com>
Message-ID: <6a5df7f6.f3151ff8.37ac76.9905@mx.google.com>


https://github.com/xroche updated https://github.com/llvm/llvm-project/pull/204815

>From 5c7fc853303e60e1c39d6ea49d5fa6ad445abc16 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Fri, 19 Jun 2026 13:59:45 +0200
Subject: [PATCH 1/9] [Clang][POC] Atomic operations on _BitInt(N)

_BitInt(N) was rejected by every atomic path: the _Atomic(...) type
specifier, the __c11_atomic_*/__atomic_* builtins, and transitively
std::atomic. Two blanket isBitIntType() checks disabled it, dating to the
type's introduction (the __atomic builtin half is D84049). __int128, the
closest analogue, was allowed at both sites.

Lift both rejections so _BitInt flows through the normal integer path.
load, store, exchange, compare-exchange, and bitwise read-modify-write are
then correct at every width through the existing canonicalizing store and
the libcall fallback.

Arithmetic read-modify-write needs more care. A single atomicrmw on the
padded memory integer of a non-byte-aligned width (e.g. _BitInt(37) in an
i64) carries into the padding bits, leaving a non-canonical value that
breaks a later compare-exchange and gives wrong signed min/max. A wide
arithmetic fetch (e.g. _BitInt(256)) hit an llvm_unreachable in the libcall
path, a compiler crash. Both are fixed by emitting a compare-exchange loop
that computes the new value at width N via llvm::buildAtomicRMWValue and
writes back a canonical representation, reusing the existing
EmitAtomicCompareExchange helper, which selects the inline cmpxchg or the
__atomic_compare_exchange libcall by size. No-padding inline widths (64,
128) keep the direct atomicrmw fast path.

The libc++ bit-int.verify.cpp test only asserted that Clang rejection, so it
is removed as obsolete. It is not replaced with a positive test here: the
libc++ premerge matrix builds tests with a pinned clang that predates this
change. Whether libc++ should expose atomic _BitInt is a separate design
question for the P3666R4 discussion.

Verified against gcc-14: identical size and alignment for all widths, and
cross-compiler compare-exchange interop in both directions, confirming the
padding canonicalization matches.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/docs/LanguageExtensions.rst             |   9 +
 clang/docs/ReleaseNotes.rst                   |   6 +
 clang/lib/CodeGen/CGAtomic.cpp                | 211 ++++++++++++++++++
 clang/lib/Sema/SemaChecking.cpp               |   5 -
 clang/lib/Sema/SemaType.cpp                   |   2 -
 clang/test/CodeGen/atomic-bitint.c            |  90 ++++++++
 clang/test/Sema/builtins.c                    |   4 +-
 clang/test/SemaCXX/ext-int.cpp                |  10 +-
 libcxx/test/libcxx/atomics/bit-int.verify.cpp |  22 --
 9 files changed, 322 insertions(+), 37 deletions(-)
 create mode 100644 clang/test/CodeGen/atomic-bitint.c
 delete mode 100644 libcxx/test/libcxx/atomics/bit-int.verify.cpp

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index d79d82a175c68..5ff076d3e48ad 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -451,6 +451,15 @@ favor of the standard type.
 Note: the ABI for ``_BitInt(N)`` is still in the process of being stabilized,
 so this type should not yet be used in interfaces that require ABI stability.
 
+``_BitInt(N)`` may be used as an atomic type: ``_Atomic(_BitInt(N))``, the
+``__c11_atomic_*`` and ``__atomic_*`` builtins, and ``std::atomic`` all accept
+it for any width. Widths the target cannot operate on inline are lowered to the
+``__atomic_*`` libcalls. For a width whose representation has padding bits (``N``
+not a multiple of the type's alignment, e.g. ``_BitInt(37)``), arithmetic
+read-modify-write operations are emitted as a compare-exchange loop that computes
+at width ``N``, so the result wraps modulo ``2**N`` and the padding bits stay
+canonical.
+
 C keywords supported in all language modes
 ------------------------------------------
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7f056abfbbe24..8692da8830dff 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -265,6 +265,12 @@ Non-comprehensive list of changes in this release
 - Added support for floating point and pointer values in most ``__atomic_``
   builtins.
 
+- Atomic operations on ``_BitInt(N)`` are now supported, including
+  ``_Atomic(_BitInt(N))``, the ``__c11_atomic_*`` / ``__atomic_*`` builtins, and
+  ``std::atomic``. Widths the target cannot operate on inline use the
+  ``__atomic_*`` libcalls; arithmetic read-modify-write on a width with padding
+  bits is emitted as a compare-exchange loop computing at the value width.
+
 - Added ``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right``
   for bit rotation of unsigned integers including ``_BitInt`` types. Rotation
   counts are normalized modulo the bit-width and support negative values.
diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 270965b109943..66c059fd40e26 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Intrinsics.h"
+#include "llvm/Transforms/Utils/LowerAtomic.h"
 
 using namespace clang;
 using namespace CodeGen;
@@ -558,6 +559,195 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
   return Builder.CreateSelect(Cmp, OldVal, RHS, "newval");
 }
 
+/// Classify an atomic op as an arithmetic/bitwise read-modify-write (one that
+/// normally lowers to a single `atomicrmw`), mapping it to the matching
+/// `AtomicRMWInst::BinOp` and reporting whether the builtin returns the new
+/// value (`<op>_fetch`) rather than the old value (`fetch_<op>`). \p IsSigned
+/// selects signed vs unsigned min/max. Returns false for exchange, load, store,
+/// compare-exchange, and any non-RMW op, none of which need the _BitInt loop.
+static bool classifyBitIntRMW(AtomicExpr::AtomicOp Op, bool IsSigned,
+                              llvm::AtomicRMWInst::BinOp &BinOp,
+                              bool &ReturnsNew) {
+  using RMW = llvm::AtomicRMWInst;
+  switch (Op) {
+  case AtomicExpr::AO__c11_atomic_fetch_add:
+  case AtomicExpr::AO__hip_atomic_fetch_add:
+  case AtomicExpr::AO__opencl_atomic_fetch_add:
+  case AtomicExpr::AO__atomic_fetch_add:
+  case AtomicExpr::AO__scoped_atomic_fetch_add:
+    BinOp = RMW::Add, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_add_fetch:
+  case AtomicExpr::AO__scoped_atomic_add_fetch:
+    BinOp = RMW::Add, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_sub:
+  case AtomicExpr::AO__hip_atomic_fetch_sub:
+  case AtomicExpr::AO__opencl_atomic_fetch_sub:
+  case AtomicExpr::AO__atomic_fetch_sub:
+  case AtomicExpr::AO__scoped_atomic_fetch_sub:
+    BinOp = RMW::Sub, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_sub_fetch:
+  case AtomicExpr::AO__scoped_atomic_sub_fetch:
+    BinOp = RMW::Sub, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_and:
+  case AtomicExpr::AO__hip_atomic_fetch_and:
+  case AtomicExpr::AO__opencl_atomic_fetch_and:
+  case AtomicExpr::AO__atomic_fetch_and:
+  case AtomicExpr::AO__scoped_atomic_fetch_and:
+    BinOp = RMW::And, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_and_fetch:
+  case AtomicExpr::AO__scoped_atomic_and_fetch:
+    BinOp = RMW::And, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_or:
+  case AtomicExpr::AO__hip_atomic_fetch_or:
+  case AtomicExpr::AO__opencl_atomic_fetch_or:
+  case AtomicExpr::AO__atomic_fetch_or:
+  case AtomicExpr::AO__scoped_atomic_fetch_or:
+    BinOp = RMW::Or, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_or_fetch:
+  case AtomicExpr::AO__scoped_atomic_or_fetch:
+    BinOp = RMW::Or, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_xor:
+  case AtomicExpr::AO__hip_atomic_fetch_xor:
+  case AtomicExpr::AO__opencl_atomic_fetch_xor:
+  case AtomicExpr::AO__atomic_fetch_xor:
+  case AtomicExpr::AO__scoped_atomic_fetch_xor:
+    BinOp = RMW::Xor, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_xor_fetch:
+  case AtomicExpr::AO__scoped_atomic_xor_fetch:
+    BinOp = RMW::Xor, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_nand:
+  case AtomicExpr::AO__atomic_fetch_nand:
+  case AtomicExpr::AO__scoped_atomic_fetch_nand:
+    BinOp = RMW::Nand, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_nand_fetch:
+  case AtomicExpr::AO__scoped_atomic_nand_fetch:
+    BinOp = RMW::Nand, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_min:
+  case AtomicExpr::AO__hip_atomic_fetch_min:
+  case AtomicExpr::AO__opencl_atomic_fetch_min:
+  case AtomicExpr::AO__atomic_fetch_min:
+  case AtomicExpr::AO__scoped_atomic_fetch_min:
+    BinOp = IsSigned ? RMW::Min : RMW::UMin, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_min_fetch:
+  case AtomicExpr::AO__scoped_atomic_min_fetch:
+    BinOp = IsSigned ? RMW::Min : RMW::UMin, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_max:
+  case AtomicExpr::AO__hip_atomic_fetch_max:
+  case AtomicExpr::AO__opencl_atomic_fetch_max:
+  case AtomicExpr::AO__atomic_fetch_max:
+  case AtomicExpr::AO__scoped_atomic_fetch_max:
+    BinOp = IsSigned ? RMW::Max : RMW::UMax, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_max_fetch:
+  case AtomicExpr::AO__scoped_atomic_max_fetch:
+    BinOp = IsSigned ? RMW::Max : RMW::UMax, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__atomic_fetch_uinc:
+  case AtomicExpr::AO__scoped_atomic_fetch_uinc:
+    BinOp = RMW::UIncWrap, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_fetch_udec:
+  case AtomicExpr::AO__scoped_atomic_fetch_udec:
+    BinOp = RMW::UDecWrap, ReturnsNew = false;
+    return true;
+  default:
+    return false;
+  }
+}
+
+/// True for a `_BitInt(N)` whose value width N differs from its in-memory width
+/// (e.g. `_BitInt(37)` occupies 64 bits), so the high bits are padding.
+static bool hasBitIntPadding(QualType T, const ASTContext &C) {
+  if (const auto *BIT = T->getAs<BitIntType>())
+    return BIT->getNumBits() != C.getTypeSize(T);
+  return false;
+}
+
+/// Map a constant C ABI memory order to an llvm ordering. A non-constant order
+/// is handled conservatively with the strongest ordering.
+static llvm::AtomicOrdering atomicOrderOrSeqCst(llvm::Value *Order) {
+  auto *C = dyn_cast<llvm::ConstantInt>(Order);
+  if (!C || !llvm::isValidAtomicOrderingCABI(C->getZExtValue()))
+    return llvm::AtomicOrdering::SequentiallyConsistent;
+  switch ((llvm::AtomicOrderingCABI)C->getZExtValue()) {
+  case llvm::AtomicOrderingCABI::relaxed:
+    return llvm::AtomicOrdering::Monotonic;
+  case llvm::AtomicOrderingCABI::consume:
+  case llvm::AtomicOrderingCABI::acquire:
+    return llvm::AtomicOrdering::Acquire;
+  case llvm::AtomicOrderingCABI::release:
+    return llvm::AtomicOrdering::Release;
+  case llvm::AtomicOrderingCABI::acq_rel:
+    return llvm::AtomicOrdering::AcquireRelease;
+  case llvm::AtomicOrderingCABI::seq_cst:
+    return llvm::AtomicOrdering::SequentiallyConsistent;
+  }
+  llvm_unreachable("invalid CABI ordering");
+}
+
+/// Emit a `_BitInt(N)` atomic read-modify-write as a compare-exchange loop. A
+/// single `atomicrmw` on the padded memory integer would carry into / compare
+/// the padding bits, and no arbitrary-width `__atomic_fetch_*` libcall exists
+/// for wide widths. The loop computes the new value at width N and writes back
+/// a canonical (extended) representation via the existing cmpxchg helper, which
+/// also picks the inline-vs-libcall form by size.
+static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
+                                      Address Ptr, Address Val1,
+                                      QualType AtomicTy,
+                                      llvm::AtomicRMWInst::BinOp BinOp,
+                                      bool ReturnsNew, llvm::Value *Order) {
+  QualType ValTy = E->getValueType();
+  llvm::AtomicOrdering AO = atomicOrderOrSeqCst(Order);
+  llvm::AtomicOrdering Failure =
+      llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO);
+
+  LValue AtomicLVal = CGF.MakeAddrLValue(Ptr, AtomicTy);
+  AtomicInfo Atomics(CGF, AtomicLVal);
+
+  llvm::Value *RHS =
+      CGF.EmitLoadOfScalar(CGF.MakeAddrLValue(Val1, ValTy), E->getExprLoc());
+
+  RValue OldRV = Atomics.EmitAtomicLoad(
+      AggValueSlot::ignored(), E->getExprLoc(),
+      /*AsValue=*/true, llvm::AtomicOrdering::Monotonic, E->isVolatile());
+  llvm::Value *Init = OldRV.getScalarVal();
+
+  llvm::BasicBlock *StartBB = CGF.Builder.GetInsertBlock();
+  llvm::BasicBlock *LoopBB = CGF.createBasicBlock("atomicrmw.start", CGF.CurFn);
+  llvm::BasicBlock *EndBB = CGF.createBasicBlock("atomicrmw.end", CGF.CurFn);
+  CGF.Builder.CreateBr(LoopBB);
+  CGF.Builder.SetInsertPoint(LoopBB);
+
+  llvm::PHINode *Old = CGF.Builder.CreatePHI(Init->getType(), 2);
+  Old->addIncoming(Init, StartBB);
+
+  // Compute at the value width via the canonical RMW lowering, so the result
+  // wraps mod 2^N and never touches the padding bits.
+  llvm::Value *New = llvm::buildAtomicRMWValue(BinOp, CGF.Builder, Old, RHS);
+
+  auto Res = Atomics.EmitAtomicCompareExchange(
+      RValue::get(Old), RValue::get(New), AO, Failure, /*IsWeak=*/true);
+  Old->addIncoming(Res.first.getScalarVal(), CGF.Builder.GetInsertBlock());
+  CGF.Builder.CreateCondBr(Res.second, EndBB, LoopBB);
+
+  CGF.Builder.SetInsertPoint(EndBB);
+  return RValue::get(ReturnsNew ? New : static_cast<llvm::Value *>(Old));
+}
+
 static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
                          Address Ptr, Address Val1, Address Val2,
                          Address ExpectedResult, llvm::Value *IsWeak,
@@ -1109,6 +1299,27 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
   LValue AtomicVal = MakeAddrLValue(Ptr, AtomicTy);
   AtomicInfo Atomics(*this, AtomicVal);
 
+  // A `_BitInt(N)` read-modify-write whose value width has padding bits, or
+  // whose size forces a libcall, cannot use a single atomicrmw: the op would
+  // carry into / compare the padding bits, and no arbitrary-width
+  // __atomic_fetch_* libcall exists. Emit a compare-exchange loop instead.
+  // Bitwise and/or/xor are exact even with padding, so only the wide case needs
+  // the loop for them. load/store/exchange/compare_exchange keep their paths.
+  if (MemTy->isBitIntType()) {
+    llvm::AtomicRMWInst::BinOp BinOp;
+    bool RMWReturnsNew;
+    if (classifyBitIntRMW(E->getOp(), MemTy->isSignedIntegerType(), BinOp,
+                          RMWReturnsNew)) {
+      bool WideOrNonPow2 = (Size & (Size - 1)) != 0 || Size > 16;
+      bool Bitwise = BinOp == llvm::AtomicRMWInst::And ||
+                     BinOp == llvm::AtomicRMWInst::Or ||
+                     BinOp == llvm::AtomicRMWInst::Xor;
+      if (WideOrNonPow2 || (hasBitIntPadding(MemTy, getContext()) && !Bitwise))
+        return emitBitIntAtomicRMWLoop(*this, E, Ptr, Val1, AtomicTy, BinOp,
+                                       RMWReturnsNew, Order);
+    }
+  }
+
   Address OriginalVal1 = Val1;
   if (ShouldCastToIntPtrTy) {
     Ptr = Atomics.castToAtomicIntPointer(Ptr);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index b8a3f48a32f24..874ce2bf1ce3a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -5460,11 +5460,6 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
                 ? 0
                 : 1);
 
-  if (ValType->isBitIntType()) {
-    Diag(Ptr->getExprLoc(), diag::err_atomic_builtin_bit_int_prohibit);
-    return ExprError();
-  }
-
   return AE;
 }
 
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index d2bb312feadc1..4a3506c281acf 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -10412,8 +10412,6 @@ QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) {
     else if (!T.isTriviallyCopyableType(Context) && getLangOpts().CPlusPlus)
       // Some other non-trivially-copyable type (probably a C++ class)
       DisallowedKind = 7;
-    else if (T->isBitIntType())
-      DisallowedKind = 8;
     else if (getLangOpts().C23 && T->isUndeducedAutoType())
       // _Atomic auto is prohibited in C23
       DisallowedKind = 9;
diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
new file mode 100644
index 0000000000000..358b530e8a792
--- /dev/null
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -0,0 +1,90 @@
+// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
+//
+// Atomic operations on _BitInt(N). load/store/exchange/compare-exchange and
+// bitwise RMW lower directly; arithmetic RMW on a padded width and any RMW on a
+// width too wide for an inline atomicrmw lower to a compare-exchange loop that
+// computes at the value width.
+
+typedef _BitInt(37)          S37;
+typedef unsigned _BitInt(37) U37;
+typedef _BitInt(64)          S64;
+typedef _BitInt(128)         S128;
+typedef _BitInt(256)         S256;
+
+// CHECK-LABEL: @ld37(
+// CHECK: load atomic i64
+S37 ld37(_Atomic(S37) *p) { return __c11_atomic_load(p, __ATOMIC_SEQ_CST); }
+
+// CHECK-LABEL: @st37(
+// CHECK: store atomic i64
+void st37(_Atomic(S37) *p, S37 v) { __c11_atomic_store(p, v, __ATOMIC_SEQ_CST); }
+
+// CHECK-LABEL: @xchg37(
+// CHECK: atomicrmw xchg ptr {{.*}} i64
+S37 xchg37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_exchange(p, v, __ATOMIC_SEQ_CST);
+}
+
+// CHECK-LABEL: @cas37(
+// CHECK: cmpxchg ptr {{.*}} i64
+_Bool cas37(_Atomic(S37) *p, S37 *e, S37 d) {
+  return __c11_atomic_compare_exchange_strong(p, e, d, __ATOMIC_SEQ_CST,
+                                              __ATOMIC_SEQ_CST);
+}
+
+// Bitwise RMW on a padded width keeps the direct atomicrmw: it is exact.
+// CHECK-LABEL: @and37(
+// CHECK: atomicrmw and ptr {{.*}} i64
+// CHECK-NOT: cmpxchg
+S37 and37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Arithmetic RMW on a padded width becomes a compare-exchange loop, not a bare
+// atomicrmw that would carry into the padding bits.
+// CHECK-LABEL: @add37(
+// CHECK: atomicrmw.start:
+// CHECK: cmpxchg weak ptr {{.*}} i64
+// CHECK-NOT: atomicrmw add
+S37 add37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Signed min is computed at the value width, so the sign bit is at bit N-1.
+// CHECK-LABEL: @min37(
+// CHECK: icmp sle i37
+// CHECK: select i1
+// CHECK: cmpxchg weak ptr {{.*}} i64
+U37 min37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
+}
+
+// No padding: direct atomicrmw, no loop.
+// CHECK-LABEL: @add64(
+// CHECK: atomicrmw add ptr {{.*}} i64
+// CHECK-NOT: cmpxchg
+S64 add64(_Atomic(S64) *p, S64 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// CHECK-LABEL: @add128(
+// CHECK: atomicrmw add ptr {{.*}} i128
+S128 add128(_Atomic(S128) *p, S128 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Wide: no inline atomicrmw and no arbitrary-width __atomic_fetch_add libcall,
+// so the loop calls __atomic_compare_exchange.
+// CHECK-LABEL: @add256(
+// CHECK: call {{.*}}@__atomic_compare_exchange
+// CHECK-NOT: cmpxchg
+S256 add256(_Atomic(S256) *p, S256 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Wide bitwise also needs the loop: the wide path has no inline atomicrmw.
+// CHECK-LABEL: @or256(
+// CHECK: call {{.*}}@__atomic_compare_exchange
+S256 or256(_Atomic(S256) *p, S256 v) {
+  return __c11_atomic_fetch_or(p, v, __ATOMIC_SEQ_CST);
+}
diff --git a/clang/test/Sema/builtins.c b/clang/test/Sema/builtins.c
index b669ee68cdd95..57e0eefdb772b 100644
--- a/clang/test/Sema/builtins.c
+++ b/clang/test/Sema/builtins.c
@@ -281,7 +281,7 @@ void test_ei_i42i(_BitInt(42) *ptr, int value) {
   // expected-warning at +1 {{the semantics of this intrinsic changed with GCC version 4.4 - the newer semantics are provided here}}
   __sync_nand_and_fetch(ptr, value); // expected-error {{atomic memory operand must have a power-of-two size}}
 
-  __atomic_fetch_add(ptr, 1, 0); // expected-error {{argument to atomic builtin of type '_BitInt' is not supported}}
+  __atomic_fetch_add(ptr, 1, 0); // expect success: the GNU atomic builtins support _BitInt
 }
 
 void test_ei_i64i(_BitInt(64) *ptr, int value) {
@@ -289,7 +289,7 @@ void test_ei_i64i(_BitInt(64) *ptr, int value) {
   // expected-warning at +1 {{the semantics of this intrinsic changed with GCC version 4.4 - the newer semantics are provided here}}
   __sync_nand_and_fetch(ptr, value); // expect success
 
-  __atomic_fetch_add(ptr, 1, 0); // expected-error {{argument to atomic builtin of type '_BitInt' is not supported}}
+  __atomic_fetch_add(ptr, 1, 0); // expect success
 }
 
 void test_ei_ii42(int *ptr, _BitInt(42) value) {
diff --git a/clang/test/SemaCXX/ext-int.cpp b/clang/test/SemaCXX/ext-int.cpp
index 281ae3d3c1779..f62a07a84200e 100644
--- a/clang/test/SemaCXX/ext-int.cpp
+++ b/clang/test/SemaCXX/ext-int.cpp
@@ -121,13 +121,11 @@ _Complex _BitInt(3) Cmplx;
 // expected-error at +1{{'_Complex _BitInt' is invalid}}
 typedef _Complex _BitInt(3) Cmp;
 
-// Reject cases of _Atomic:
-// expected-error at +1{{_Atomic cannot be applied to integer type '_BitInt(4)'}}
-_Atomic _BitInt(4) TooSmallAtomic;
-// expected-error at +1{{_Atomic cannot be applied to integer type '_BitInt(9)'}}
+// _Atomic accepts any _BitInt width: small and non-power-of-2 included.
+// Sizes the target cannot lower inline use the __atomic_* libcalls.
+_Atomic _BitInt(4) SmallAtomic;
 _Atomic _BitInt(9) NotPow2Atomic;
-// expected-error at +1{{_Atomic cannot be applied to integer type '_BitInt(128)'}}
-_Atomic _BitInt(128) JustRightAtomic;
+_Atomic _BitInt(128) WideAtomic;
 
 // Test result types of Unary/Bitwise/Binary Operations:
 void Ops() {
diff --git a/libcxx/test/libcxx/atomics/bit-int.verify.cpp b/libcxx/test/libcxx/atomics/bit-int.verify.cpp
deleted file mode 100644
index 03880a1b6215c..0000000000000
--- a/libcxx/test/libcxx/atomics/bit-int.verify.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <atomic>
-
-// Make sure that `std::atomic` doesn't work with `_BitInt`. The intent is to
-// disable them for now until their behavior can be designed better later.
-// See https://reviews.llvm.org/D84049 for details.
-
-// UNSUPPORTED: c++03
-
-#include <atomic>
-
-void f() {
-  // expected-error@*:*1 {{_Atomic cannot be applied to integer type '_BitInt(32)'}}
-  std::atomic<_BitInt(32)> x(42);
-}

>From 92e21eaa61bf0f26c3ba825545845f531d79c4d6 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Tue, 23 Jun 2026 11:53:49 +0200
Subject: [PATCH 2/9] [Clang][POC] Add C23/C2y Sema tests for atomic _BitInt(N)

C23 requires the type-generic atomic interfaces to accept _BitInt(N), so
_Atomic(_BitInt(N)) is well-formed at every width. Add a Sema acceptance
test covering the _Atomic specifier and the __c11_atomic_*/__atomic_*
builtins in C23 and C2y modes, and a -std=c2y run of the codegen test.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/test/CodeGen/atomic-bitint.c |  1 +
 clang/test/Sema/atomic-bitint.c    | 40 ++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+)
 create mode 100644 clang/test/Sema/atomic-bitint.c

diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
index 358b530e8a792..9fa259776bf62 100644
--- a/clang/test/CodeGen/atomic-bitint.c
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c2y -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
 //
 // Atomic operations on _BitInt(N). load/store/exchange/compare-exchange and
 // bitwise RMW lower directly; arithmetic RMW on a padded width and any RMW on a
diff --git a/clang/test/Sema/atomic-bitint.c b/clang/test/Sema/atomic-bitint.c
new file mode 100644
index 0000000000000..1466412bae732
--- /dev/null
+++ b/clang/test/Sema/atomic-bitint.c
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify -triple x86_64-unknown-linux-gnu -std=c23
+// RUN: %clang_cc1 %s -fsyntax-only -verify -triple x86_64-unknown-linux-gnu -std=c2y
+//
+// C23 requires the type-generic atomic interfaces to accept _BitInt(N) for
+// every N, so _Atomic(_BitInt(N)) is well-formed at every width. Widths past
+// 128 are x86-only.
+
+// expected-no-diagnostics
+
+_Atomic(_BitInt(4))   a4;    // small
+_Atomic(_BitInt(9))   a9;    // non-power-of-two
+_Atomic(_BitInt(37))  a37;   // padded
+_Atomic(_BitInt(64))  a64;
+_Atomic(_BitInt(128)) a128;
+_Atomic(_BitInt(256)) a256;  // wider than any inline atomic
+
+// The _Atomic qualifier spelling is equally valid.
+_Atomic _BitInt(9) q9;
+
+static_assert(sizeof(_Atomic(_BitInt(37))) == 8);
+static_assert(sizeof(_Atomic(_BitInt(128))) == 16);
+static_assert(sizeof(_Atomic(_BitInt(256))) == 32);
+
+void c11_builtins(_Atomic(_BitInt(37)) *p, _BitInt(37) v, _BitInt(37) *e) {
+  (void)__c11_atomic_load(p, __ATOMIC_SEQ_CST);
+  __c11_atomic_store(p, v, __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_exchange(p, v, __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_compare_exchange_strong(p, e, v, __ATOMIC_SEQ_CST,
+                                             __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
+}
+
+// The GNU __atomic_* builtins take a plain _BitInt pointer.
+void gnu_builtins(_BitInt(37) *p, _BitInt(37) v) {
+  (void)__atomic_load_n(p, __ATOMIC_SEQ_CST);
+  __atomic_store_n(p, v, __ATOMIC_SEQ_CST);
+  (void)__atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}

>From c0e011fb61086a5a0cae13c54abe21ab312e85c7 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Tue, 23 Jun 2026 13:51:48 +0200
Subject: [PATCH 3/9] [Clang][POC] Extend atomic _BitInt Sema test: width 4096
 + RISC-V

Add a _BitInt(4096) acceptance case and a riscv64 RUN line. The atomic
code imposes no width cap of its own, so the only limit is the target's
getMaxBitIntWidth(); x86 and RISC-V allow widths past 128, others cap at
128. Correct the comment that claimed wide widths were x86-only.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/test/Sema/atomic-bitint.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/clang/test/Sema/atomic-bitint.c b/clang/test/Sema/atomic-bitint.c
index 1466412bae732..fbb4c518438fb 100644
--- a/clang/test/Sema/atomic-bitint.c
+++ b/clang/test/Sema/atomic-bitint.c
@@ -1,18 +1,21 @@
 // RUN: %clang_cc1 %s -fsyntax-only -verify -triple x86_64-unknown-linux-gnu -std=c23
 // RUN: %clang_cc1 %s -fsyntax-only -verify -triple x86_64-unknown-linux-gnu -std=c2y
+// RUN: %clang_cc1 %s -fsyntax-only -verify -triple riscv64-unknown-linux-gnu -std=c23
 //
 // C23 requires the type-generic atomic interfaces to accept _BitInt(N) for
-// every N, so _Atomic(_BitInt(N)) is well-formed at every width. Widths past
-// 128 are x86-only.
+// every N, so _Atomic(_BitInt(N)) is well-formed at every width. The atomic
+// code imposes no width cap of its own; widths past 128 are available wherever
+// the target accepts _BitInt > 128 (x86 and RISC-V today).
 
 // expected-no-diagnostics
 
-_Atomic(_BitInt(4))   a4;    // small
-_Atomic(_BitInt(9))   a9;    // non-power-of-two
-_Atomic(_BitInt(37))  a37;   // padded
-_Atomic(_BitInt(64))  a64;
-_Atomic(_BitInt(128)) a128;
-_Atomic(_BitInt(256)) a256;  // wider than any inline atomic
+_Atomic(_BitInt(4))    a4;     // small
+_Atomic(_BitInt(9))    a9;     // non-power-of-two
+_Atomic(_BitInt(37))   a37;    // padded
+_Atomic(_BitInt(64))   a64;
+_Atomic(_BitInt(128))  a128;
+_Atomic(_BitInt(256))  a256;   // wider than any inline atomic
+_Atomic(_BitInt(4096)) a4096;  // far past the inline range
 
 // The _Atomic qualifier spelling is equally valid.
 _Atomic _BitInt(9) q9;

>From 92e2068b4837befaa51c040c0c882ea7f10cd57f Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Fri, 26 Jun 2026 21:21:00 +0200
Subject: [PATCH 4/9] [Clang][NFC] Address review: atomic _BitInt diagnostic
 list and cast

Drop the now-unreachable _BitInt ("integer") option from
err_atomic_specifier_bad_type and renumber the _Atomic-auto/C23 case to
fill the gap; the rendered text is unchanged. Use static_cast instead of
a C-style cast in atomicOrderOrSeqCst.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ++--
 clang/lib/CodeGen/CGAtomic.cpp                   | 2 +-
 clang/lib/Sema/SemaType.cpp                      | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index cde99dfb16ec5..414357c5a7c73 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7475,8 +7475,8 @@ def err_func_def_incomplete_result : Error<
 def err_atomic_specifier_bad_type
     : Error<"_Atomic cannot be applied to "
             "%select{incomplete |array |function |reference |atomic |qualified "
-            "|sizeless ||integer |}0type "
-            "%1 %select{|||||||which is not trivially copyable||in C23}0">;
+            "|sizeless ||}0type "
+            "%1 %select{|||||||which is not trivially copyable|in C23}0">;
 def warn_atomic_member_access : Warning<
   "accessing a member of an atomic structure or union is undefined behavior">,
   InGroup<DiagGroup<"atomic-access">>, DefaultError;
diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 66c059fd40e26..820849f5974c0 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -683,7 +683,7 @@ static llvm::AtomicOrdering atomicOrderOrSeqCst(llvm::Value *Order) {
   auto *C = dyn_cast<llvm::ConstantInt>(Order);
   if (!C || !llvm::isValidAtomicOrderingCABI(C->getZExtValue()))
     return llvm::AtomicOrdering::SequentiallyConsistent;
-  switch ((llvm::AtomicOrderingCABI)C->getZExtValue()) {
+  switch (static_cast<llvm::AtomicOrderingCABI>(C->getZExtValue())) {
   case llvm::AtomicOrderingCABI::relaxed:
     return llvm::AtomicOrdering::Monotonic;
   case llvm::AtomicOrderingCABI::consume:
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 4a3506c281acf..f76244d5f2871 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -10414,7 +10414,7 @@ QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) {
       DisallowedKind = 7;
     else if (getLangOpts().C23 && T->isUndeducedAutoType())
       // _Atomic auto is prohibited in C23
-      DisallowedKind = 9;
+      DisallowedKind = 8;
 
     if (DisallowedKind != -1) {
       Diag(Loc, diag::err_atomic_specifier_bad_type) << DisallowedKind << T;

>From ece57436ed0217f4c1ad4ca92f10182793ec109f Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Fri, 26 Jun 2026 21:21:02 +0200
Subject: [PATCH 5/9] [Clang][NFC] Regenerate atomic-bitint.c checks with
 update_cc_test_checks

The prior spot-checks did not show the lowering. Regenerate complete
check lines so the compare-exchange loop, the width-N arithmetic, and the
sext/zext memory canonicalization are all visible.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/test/CodeGen/atomic-bitint.c | 350 ++++++++++++++++++++++++++---
 1 file changed, 321 insertions(+), 29 deletions(-)

diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
index 9fa259776bf62..6476c26a0f0dd 100644
--- a/clang/test/CodeGen/atomic-bitint.c
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -1,3 +1,4 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
 // RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
 // RUN: %clang_cc1 -std=c2y -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
 //
@@ -12,80 +13,371 @@ typedef _BitInt(64)          S64;
 typedef _BitInt(128)         S128;
 typedef _BitInt(256)         S256;
 
-// CHECK-LABEL: @ld37(
-// CHECK: load atomic i64
+// CHECK-LABEL: define dso_local i64 @ld37(
+// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load atomic i64, ptr [[TMP0]] seq_cst, align 8
+// CHECK-NEXT:    store i64 [[TMP1]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    store i37 [[LOADEDV]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP3]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 S37 ld37(_Atomic(S37) *p) { return __c11_atomic_load(p, __ATOMIC_SEQ_CST); }
 
-// CHECK-LABEL: @st37(
-// CHECK: store atomic i64
+// CHECK-LABEL: define dso_local void @st37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    store atomic i64 [[TMP3]], ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    ret void
+//
 void st37(_Atomic(S37) *p, S37 v) { __c11_atomic_store(p, v, __ATOMIC_SEQ_CST); }
 
-// CHECK-LABEL: @xchg37(
-// CHECK: atomicrmw xchg ptr {{.*}} i64
+// CHECK-LABEL: define dso_local i64 @xchg37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = atomicrmw xchg ptr [[TMP1]], i64 [[TMP3]] seq_cst, align 8
+// CHECK-NEXT:    store i64 [[TMP4]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP5]] to i37
+// CHECK-NEXT:    store i37 [[LOADEDV3]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP6]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 S37 xchg37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_exchange(p, v, __ATOMIC_SEQ_CST);
 }
 
-// CHECK-LABEL: @cas37(
-// CHECK: cmpxchg ptr {{.*}} i64
+// CHECK-LABEL: define dso_local zeroext i1 @cas37(
+// CHECK-SAME: ptr noundef [[P:%.*]], ptr noundef [[E:%.*]], i64 noundef [[D_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[D:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[E_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[D_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[CMPXCHG_BOOL:%.*]] = alloca i8, align 1
+// CHECK-NEXT:    store i64 [[D_COERCE]], ptr [[D]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[D]], align 8
+// CHECK-NEXT:    [[D1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[E]], ptr [[E_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[D1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[D_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[E_ADDR]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[D_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = load i64, ptr [[TMP2]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP5]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP6]], 0
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
+// CHECK-NEXT:    br i1 [[TMP8]], label %[[CMPXCHG_CONTINUE:.*]], label %[[CMPXCHG_STORE_EXPECTED:.*]]
+// CHECK:       [[CMPXCHG_STORE_EXPECTED]]:
+// CHECK-NEXT:    store i64 [[TMP7]], ptr [[TMP2]], align 8
+// CHECK-NEXT:    br label %[[CMPXCHG_CONTINUE]]
+// CHECK:       [[CMPXCHG_CONTINUE]]:
+// CHECK-NEXT:    [[STOREDV3:%.*]] = zext i1 [[TMP8]] to i8
+// CHECK-NEXT:    store i8 [[STOREDV3]], ptr [[CMPXCHG_BOOL]], align 1
+// CHECK-NEXT:    [[TMP9:%.*]] = load i8, ptr [[CMPXCHG_BOOL]], align 1
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = icmp ne i8 [[TMP9]], 0
+// CHECK-NEXT:    ret i1 [[LOADEDV4]]
+//
 _Bool cas37(_Atomic(S37) *p, S37 *e, S37 d) {
   return __c11_atomic_compare_exchange_strong(p, e, d, __ATOMIC_SEQ_CST,
                                               __ATOMIC_SEQ_CST);
 }
 
 // Bitwise RMW on a padded width keeps the direct atomicrmw: it is exact.
-// CHECK-LABEL: @and37(
-// CHECK: atomicrmw and ptr {{.*}} i64
-// CHECK-NOT: cmpxchg
+// CHECK-LABEL: define dso_local i64 @and37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = atomicrmw and ptr [[TMP1]], i64 [[TMP3]] seq_cst, align 8
+// CHECK-NEXT:    store i64 [[TMP4]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP5]] to i37
+// CHECK-NEXT:    store i37 [[LOADEDV3]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP6]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 S37 and37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
 }
 
 // Arithmetic RMW on a padded width becomes a compare-exchange loop, not a bare
 // atomicrmw that would carry into the padding bits.
-// CHECK-LABEL: @add37(
-// CHECK: atomicrmw.start:
-// CHECK: cmpxchg weak ptr {{.*}} i64
-// CHECK-NOT: atomicrmw add
+// CHECK-LABEL: define dso_local i64 @add37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] monotonic, align 8
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[ATOMIC_LOAD]] to i37
+// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
+// CHECK:       [[ATOMICRMW_START]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i37 [ [[LOADEDV4]], %[[ENTRY]] ], [ [[LOADEDV7:%.*]], %[[ATOMICRMW_START]] ]
+// CHECK-NEXT:    [[NEW:%.*]] = add i37 [[TMP4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[TMP4]] to i64
+// CHECK-NEXT:    [[STOREDV6:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    [[TMP5:%.*]] = cmpxchg weak ptr [[TMP1]], i64 [[STOREDV5]], i64 [[STOREDV6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = extractvalue { i64, i1 } [[TMP5]], 0
+// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP5]], 1
+// CHECK-NEXT:    [[LOADEDV7]] = trunc i64 [[TMP6]] to i37
+// CHECK-NEXT:    br i1 [[TMP7]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
+// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    store i37 [[TMP4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP8:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP8]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 S37 add37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
 }
 
 // Signed min is computed at the value width, so the sign bit is at bit N-1.
-// CHECK-LABEL: @min37(
-// CHECK: icmp sle i37
-// CHECK: select i1
-// CHECK: cmpxchg weak ptr {{.*}} i64
+// CHECK-LABEL: define dso_local i64 @min37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] monotonic, align 8
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[ATOMIC_LOAD]] to i37
+// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
+// CHECK:       [[ATOMICRMW_START]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i37 [ [[LOADEDV4]], %[[ENTRY]] ], [ [[LOADEDV7:%.*]], %[[ATOMICRMW_START]] ]
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp sle i37 [[TMP4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[TMP4]], i37 [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[TMP4]] to i64
+// CHECK-NEXT:    [[STOREDV6:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg weak ptr [[TMP1]], i64 [[STOREDV5]], i64 [[STOREDV6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP6]], 0
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
+// CHECK-NEXT:    [[LOADEDV7]] = trunc i64 [[TMP7]] to i37
+// CHECK-NEXT:    br i1 [[TMP8]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
+// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    store i37 [[TMP4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP9:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP9]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 U37 min37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
 }
 
 // No padding: direct atomicrmw, no loop.
-// CHECK-LABEL: @add64(
-// CHECK: atomicrmw add ptr {{.*}} i64
-// CHECK-NOT: cmpxchg
+// CHECK-LABEL: define dso_local i64 @add64(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[TMP1]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = atomicrmw add ptr [[TMP0]], i64 [[TMP2]] seq_cst, align 8
+// CHECK-NEXT:    store i64 [[TMP3]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    ret i64 [[TMP4]]
+//
 S64 add64(_Atomic(S64) *p, S64 v) {
   return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
 }
 
-// CHECK-LABEL: @add128(
-// CHECK: atomicrmw add ptr {{.*}} i128
+// CHECK-LABEL: define dso_local i128 @add128(
+// CHECK-SAME: ptr noundef [[P:%.*]], i128 noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i128, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i128, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i128, align 16
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store i128 [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load i128, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    store i128 [[TMP1]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i128, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = atomicrmw add ptr [[TMP0]], i128 [[TMP2]] seq_cst, align 16
+// CHECK-NEXT:    store i128 [[TMP3]], ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT:    [[TMP4:%.*]] = load i128, ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT:    ret i128 [[TMP4]]
+//
 S128 add128(_Atomic(S128) *p, S128 v) {
   return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
 }
 
 // Wide: no inline atomicrmw and no arbitrary-width __atomic_fetch_add libcall,
 // so the loop calls __atomic_compare_exchange.
-// CHECK-LABEL: @add256(
-// CHECK: call {{.*}}@__atomic_compare_exchange
-// CHECK-NOT: cmpxchg
+// CHECK-LABEL: define dso_local void @add256(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(i256) align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(i256) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP1:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP2:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[V:%.*]] = load i256, ptr [[TMP0]], align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store i256 [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i256, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    store i256 [[TMP2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i256, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 0)
+// CHECK-NEXT:    [[TMP4:%.*]] = load i256, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
+// CHECK:       [[ATOMICRMW_START]]:
+// CHECK-NEXT:    [[TMP5:%.*]] = phi i256 [ [[TMP4]], %[[ENTRY]] ], [ [[TMP6:%.*]], %[[ATOMICRMW_START]] ]
+// CHECK-NEXT:    [[NEW:%.*]] = add i256 [[TMP5]], [[TMP3]]
+// CHECK-NEXT:    store i256 [[TMP5]], ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    store i256 [[NEW]], ptr [[ATOMIC_TEMP2]], align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
+// CHECK-NEXT:    [[TMP6]] = load i256, ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
+// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    store i256 [[TMP5]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    store i256 [[TMP7]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    ret void
+//
 S256 add256(_Atomic(S256) *p, S256 v) {
   return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
 }
 
 // Wide bitwise also needs the loop: the wide path has no inline atomicrmw.
-// CHECK-LABEL: @or256(
-// CHECK: call {{.*}}@__atomic_compare_exchange
+// CHECK-LABEL: define dso_local void @or256(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(i256) align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(i256) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP1:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP2:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[V:%.*]] = load i256, ptr [[TMP0]], align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store i256 [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i256, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    store i256 [[TMP2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i256, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 0)
+// CHECK-NEXT:    [[TMP4:%.*]] = load i256, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
+// CHECK:       [[ATOMICRMW_START]]:
+// CHECK-NEXT:    [[TMP5:%.*]] = phi i256 [ [[TMP4]], %[[ENTRY]] ], [ [[TMP6:%.*]], %[[ATOMICRMW_START]] ]
+// CHECK-NEXT:    [[NEW:%.*]] = or i256 [[TMP5]], [[TMP3]]
+// CHECK-NEXT:    store i256 [[TMP5]], ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    store i256 [[NEW]], ptr [[ATOMIC_TEMP2]], align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
+// CHECK-NEXT:    [[TMP6]] = load i256, ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
+// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    store i256 [[TMP5]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    store i256 [[TMP7]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    ret void
+//
 S256 or256(_Atomic(S256) *p, S256 v) {
   return __c11_atomic_fetch_or(p, v, __ATOMIC_SEQ_CST);
 }

>From 1c87c9994199bd82766b4d6d6f5edf392c225ba2 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sat, 27 Jun 2026 09:23:04 +0200
Subject: [PATCH 6/9] [Clang] Carry the raw representation in the _BitInt
 atomic RMW loop

The compare-exchange loop for padded and wide _BitInt atomics formed its
cmpxchg expected by re-canonicalizing the loaded value (sign/zero-extending
the truncated old). An object whose padding bits were non-canonical, e.g.
written through a union, then never matched that expected, so the cmpxchg
failed every iteration and the read-modify-write spun forever.

Reuse the existing EmitAtomicUpdate loop, which carries the raw loaded
representation as the expected and writes back a canonical desired computed
at value width N. The object converges on the first iteration regardless of
its padding, and the value it stores is canonical. See P0528.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/lib/CodeGen/CGAtomic.cpp     |  48 +++++-------
 clang/test/CodeGen/atomic-bitint.c | 120 ++++++++++++++---------------
 2 files changed, 77 insertions(+), 91 deletions(-)

diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 820849f5974c0..0043c79b398ee 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -702,9 +702,13 @@ static llvm::AtomicOrdering atomicOrderOrSeqCst(llvm::Value *Order) {
 /// Emit a `_BitInt(N)` atomic read-modify-write as a compare-exchange loop. A
 /// single `atomicrmw` on the padded memory integer would carry into / compare
 /// the padding bits, and no arbitrary-width `__atomic_fetch_*` libcall exists
-/// for wide widths. The loop computes the new value at width N and writes back
-/// a canonical (extended) representation via the existing cmpxchg helper, which
-/// also picks the inline-vs-libcall form by size.
+/// for wide widths.
+///
+/// The update computes at value width N (so the result wraps mod 2^N and is
+/// independent of padding). EmitAtomicUpdate carries the raw loaded
+/// representation as the cmpxchg expected, so an object with non-canonical
+/// padding (e.g. written through a union) still converges instead of spinning
+/// forever; the desired it writes back is canonical. See P0528.
 static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
                                       Address Ptr, Address Val1,
                                       QualType AtomicTy,
@@ -712,8 +716,6 @@ static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
                                       bool ReturnsNew, llvm::Value *Order) {
   QualType ValTy = E->getValueType();
   llvm::AtomicOrdering AO = atomicOrderOrSeqCst(Order);
-  llvm::AtomicOrdering Failure =
-      llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO);
 
   LValue AtomicLVal = CGF.MakeAddrLValue(Ptr, AtomicTy);
   AtomicInfo Atomics(CGF, AtomicLVal);
@@ -721,31 +723,17 @@ static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
   llvm::Value *RHS =
       CGF.EmitLoadOfScalar(CGF.MakeAddrLValue(Val1, ValTy), E->getExprLoc());
 
-  RValue OldRV = Atomics.EmitAtomicLoad(
-      AggValueSlot::ignored(), E->getExprLoc(),
-      /*AsValue=*/true, llvm::AtomicOrdering::Monotonic, E->isVolatile());
-  llvm::Value *Init = OldRV.getScalarVal();
-
-  llvm::BasicBlock *StartBB = CGF.Builder.GetInsertBlock();
-  llvm::BasicBlock *LoopBB = CGF.createBasicBlock("atomicrmw.start", CGF.CurFn);
-  llvm::BasicBlock *EndBB = CGF.createBasicBlock("atomicrmw.end", CGF.CurFn);
-  CGF.Builder.CreateBr(LoopBB);
-  CGF.Builder.SetInsertPoint(LoopBB);
-
-  llvm::PHINode *Old = CGF.Builder.CreatePHI(Init->getType(), 2);
-  Old->addIncoming(Init, StartBB);
-
-  // Compute at the value width via the canonical RMW lowering, so the result
-  // wraps mod 2^N and never touches the padding bits.
-  llvm::Value *New = llvm::buildAtomicRMWValue(BinOp, CGF.Builder, Old, RHS);
-
-  auto Res = Atomics.EmitAtomicCompareExchange(
-      RValue::get(Old), RValue::get(New), AO, Failure, /*IsWeak=*/true);
-  Old->addIncoming(Res.first.getScalarVal(), CGF.Builder.GetInsertBlock());
-  CGF.Builder.CreateCondBr(Res.second, EndBB, LoopBB);
-
-  CGF.Builder.SetInsertPoint(EndBB);
-  return RValue::get(ReturnsNew ? New : static_cast<llvm::Value *>(Old));
+  llvm::Value *Old = nullptr, *New = nullptr;
+  Atomics.EmitAtomicUpdate(
+      AO,
+      [&](RValue OldRV) {
+        Old = OldRV.getScalarVal();
+        New = llvm::buildAtomicRMWValue(BinOp, CGF.Builder, Old, RHS);
+        return RValue::get(New);
+      },
+      E->isVolatile());
+
+  return RValue::get(ReturnsNew ? New : Old);
 }
 
 static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
index 6476c26a0f0dd..bc1e165fd90e3 100644
--- a/clang/test/CodeGen/atomic-bitint.c
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -178,6 +178,7 @@ S37 and37(_Atomic(S37) *p, S37 v) {
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
 // CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
 // CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
 // CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
@@ -191,23 +192,23 @@ S37 and37(_Atomic(S37) *p, S37 v) {
 // CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
-// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] monotonic, align 8
-// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[ATOMIC_LOAD]] to i37
-// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
-// CHECK:       [[ATOMICRMW_START]]:
-// CHECK-NEXT:    [[TMP4:%.*]] = phi i37 [ [[LOADEDV4]], %[[ENTRY]] ], [ [[LOADEDV7:%.*]], %[[ATOMICRMW_START]] ]
-// CHECK-NEXT:    [[NEW:%.*]] = add i37 [[TMP4]], [[LOADEDV3]]
-// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[TMP4]] to i64
-// CHECK-NEXT:    [[STOREDV6:%.*]] = sext i37 [[NEW]] to i64
-// CHECK-NEXT:    [[TMP5:%.*]] = cmpxchg weak ptr [[TMP1]], i64 [[STOREDV5]], i64 [[STOREDV6]] seq_cst seq_cst, align 8
-// CHECK-NEXT:    [[TMP6:%.*]] = extractvalue { i64, i1 } [[TMP5]], 0
-// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP5]], 1
-// CHECK-NEXT:    [[LOADEDV7]] = trunc i64 [[TMP6]] to i37
-// CHECK-NEXT:    br i1 [[TMP7]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
-// CHECK:       [[ATOMICRMW_END]]:
-// CHECK-NEXT:    store i37 [[TMP4]], ptr [[RETVAL]], align 8
-// CHECK-NEXT:    [[TMP8:%.*]] = load i37, ptr [[RETVAL]], align 8
-// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP8]] to i64
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP7:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[NEW:%.*]] = add i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP5]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP7]] = extractvalue { i64, i1 } [[TMP6]], 0
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
+// CHECK-NEXT:    br i1 [[TMP8]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP9:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP9]] to i64
 // CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
 //
 S37 add37(_Atomic(S37) *p, S37 v) {
@@ -223,6 +224,7 @@ S37 add37(_Atomic(S37) *p, S37 v) {
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
 // CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
 // CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
 // CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
@@ -236,24 +238,24 @@ S37 add37(_Atomic(S37) *p, S37 v) {
 // CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
-// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] monotonic, align 8
-// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[ATOMIC_LOAD]] to i37
-// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
-// CHECK:       [[ATOMICRMW_START]]:
-// CHECK-NEXT:    [[TMP4:%.*]] = phi i37 [ [[LOADEDV4]], %[[ENTRY]] ], [ [[LOADEDV7:%.*]], %[[ATOMICRMW_START]] ]
-// CHECK-NEXT:    [[TMP5:%.*]] = icmp sle i37 [[TMP4]], [[LOADEDV3]]
-// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[TMP4]], i37 [[LOADEDV3]]
-// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[TMP4]] to i64
-// CHECK-NEXT:    [[STOREDV6:%.*]] = sext i37 [[NEW]] to i64
-// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg weak ptr [[TMP1]], i64 [[STOREDV5]], i64 [[STOREDV6]] seq_cst seq_cst, align 8
-// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP6]], 0
-// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
-// CHECK-NEXT:    [[LOADEDV7]] = trunc i64 [[TMP7]] to i37
-// CHECK-NEXT:    br i1 [[TMP8]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
-// CHECK:       [[ATOMICRMW_END]]:
-// CHECK-NEXT:    store i37 [[TMP4]], ptr [[RETVAL]], align 8
-// CHECK-NEXT:    [[TMP9:%.*]] = load i37, ptr [[RETVAL]], align 8
-// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP9]] to i64
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP8:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp sle i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[LOADEDV4]], i37 [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP8]] = extractvalue { i64, i1 } [[TMP7]], 0
+// CHECK-NEXT:    [[TMP9:%.*]] = extractvalue { i64, i1 } [[TMP7]], 1
+// CHECK-NEXT:    br i1 [[TMP9]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP10:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP10]] to i64
 // CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
 //
 U37 min37(_Atomic(S37) *p, S37 v) {
@@ -309,7 +311,7 @@ S128 add128(_Atomic(S128) *p, S128 v) {
 // so the loop calls __atomic_compare_exchange.
 // CHECK-LABEL: define dso_local void @add256(
 // CHECK-SAME: ptr dead_on_unwind noalias writable sret(i256) align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(i256) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:  [[ENTRY:.*:]]
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i256, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i256, align 8
@@ -323,21 +325,19 @@ S128 add128(_Atomic(S128) *p, S128 v) {
 // CHECK-NEXT:    [[TMP2:%.*]] = load i256, ptr [[V_ADDR]], align 8
 // CHECK-NEXT:    store i256 [[TMP2]], ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load i256, ptr [[DOTATOMICTMP]], align 8
-// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 0)
+// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 5)
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
 // CHECK-NEXT:    [[TMP4:%.*]] = load i256, ptr [[ATOMIC_TEMP]], align 8
-// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
-// CHECK:       [[ATOMICRMW_START]]:
-// CHECK-NEXT:    [[TMP5:%.*]] = phi i256 [ [[TMP4]], %[[ENTRY]] ], [ [[TMP6:%.*]], %[[ATOMICRMW_START]] ]
-// CHECK-NEXT:    [[NEW:%.*]] = add i256 [[TMP5]], [[TMP3]]
-// CHECK-NEXT:    store i256 [[TMP5]], ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    [[NEW:%.*]] = add i256 [[TMP4]], [[TMP3]]
 // CHECK-NEXT:    store i256 [[NEW]], ptr [[ATOMIC_TEMP2]], align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
-// CHECK-NEXT:    [[TMP6]] = load i256, ptr [[ATOMIC_TEMP1]], align 8
-// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
-// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    call void @__atomic_store(i64 noundef 32, ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5)
+// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], ptr noundef [[ATOMIC_TEMP1]], i32 noundef 5, i32 noundef 5)
+// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i256 [[TMP4]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
 // CHECK-NEXT:    store i256 [[TMP5]], ptr [[AGG_RESULT]], align 8
-// CHECK-NEXT:    [[TMP7:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
-// CHECK-NEXT:    store i256 [[TMP7]], ptr [[AGG_RESULT]], align 8
 // CHECK-NEXT:    ret void
 //
 S256 add256(_Atomic(S256) *p, S256 v) {
@@ -347,7 +347,7 @@ S256 add256(_Atomic(S256) *p, S256 v) {
 // Wide bitwise also needs the loop: the wide path has no inline atomicrmw.
 // CHECK-LABEL: define dso_local void @or256(
 // CHECK-SAME: ptr dead_on_unwind noalias writable sret(i256) align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(i256) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:  [[ENTRY:.*:]]
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i256, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i256, align 8
@@ -361,21 +361,19 @@ S256 add256(_Atomic(S256) *p, S256 v) {
 // CHECK-NEXT:    [[TMP2:%.*]] = load i256, ptr [[V_ADDR]], align 8
 // CHECK-NEXT:    store i256 [[TMP2]], ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load i256, ptr [[DOTATOMICTMP]], align 8
-// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 0)
+// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 5)
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
 // CHECK-NEXT:    [[TMP4:%.*]] = load i256, ptr [[ATOMIC_TEMP]], align 8
-// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
-// CHECK:       [[ATOMICRMW_START]]:
-// CHECK-NEXT:    [[TMP5:%.*]] = phi i256 [ [[TMP4]], %[[ENTRY]] ], [ [[TMP6:%.*]], %[[ATOMICRMW_START]] ]
-// CHECK-NEXT:    [[NEW:%.*]] = or i256 [[TMP5]], [[TMP3]]
-// CHECK-NEXT:    store i256 [[TMP5]], ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    [[NEW:%.*]] = or i256 [[TMP4]], [[TMP3]]
 // CHECK-NEXT:    store i256 [[NEW]], ptr [[ATOMIC_TEMP2]], align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
-// CHECK-NEXT:    [[TMP6]] = load i256, ptr [[ATOMIC_TEMP1]], align 8
-// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
-// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    call void @__atomic_store(i64 noundef 32, ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5)
+// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], ptr noundef [[ATOMIC_TEMP1]], i32 noundef 5, i32 noundef 5)
+// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i256 [[TMP4]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
 // CHECK-NEXT:    store i256 [[TMP5]], ptr [[AGG_RESULT]], align 8
-// CHECK-NEXT:    [[TMP7:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
-// CHECK-NEXT:    store i256 [[TMP7]], ptr [[AGG_RESULT]], align 8
 // CHECK-NEXT:    ret void
 //
 S256 or256(_Atomic(S256) *p, S256 v) {

>From e79f0f4e3fbec4c77fcae7f42f2d9dc5f3e85c5a Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sat, 27 Jun 2026 09:34:49 +0200
Subject: [PATCH 7/9] [compiler-rt] Add runtime test for atomic _BitInt(N)

Single-threaded execution test for _Atomic(_BitInt(N)): per-op value
correctness on a padded inline width and on wide libcall widths, plus
dirty-padding convergence. An object with non-canonical padding (written
through a union) must not spin forever in the read-modify-write
compare-exchange loop. The IR-shape checks in
clang/test/CodeGen/atomic-bitint.c cannot witness non-termination.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 .../test/builtins/Unit/atomic_bitint_test.c   | 91 +++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100644 compiler-rt/test/builtins/Unit/atomic_bitint_test.c

diff --git a/compiler-rt/test/builtins/Unit/atomic_bitint_test.c b/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
new file mode 100644
index 0000000000000..33a745348a6f0
--- /dev/null
+++ b/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
@@ -0,0 +1,91 @@
+// RUN: %clang_builtins -std=c23 %s %librt -o %t && %run %t
+// REQUIRES: librt_has_atomic
+//===-- atomic_bitint_test.c - Test atomic ops on _BitInt -----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Runtime checks for atomic read-modify-write on _BitInt(N). A padded width
+// (37) exercises the inline compare-exchange loop; a wide width (256) exercises
+// the __atomic_compare_exchange libcall loop. Each op is cross-checked against
+// the same operation done non-atomically, and the dirty-padding cases confirm
+// the loop converges (a re-canonicalized expected would spin forever).
+//
+//===----------------------------------------------------------------------===//
+
+#include <assert.h>
+#include <stdio.h>
+
+typedef signed _BitInt(37) S37;
+typedef unsigned _BitInt(37) U37;
+typedef signed _BitInt(256) S256; // no padding (exactly 32 bytes)
+typedef signed _BitInt(200) S200; // padded: 200 value bits in 32-byte storage
+
+// Each macro runs the atomic op and asserts the returned old value and the
+// resulting object both match the non-atomic computation at width N.
+#define CHECK_FETCH(T, init, op, rhs, expr)                                    \
+  do {                                                                         \
+    _Atomic(T) a = (init);                                                     \
+    T old = __c11_atomic_fetch_##op(&a, (rhs), __ATOMIC_SEQ_CST);              \
+    assert(old == (T)(init));                                                  \
+    assert((T)a == (T)(expr));                                                 \
+  } while (0)
+
+static void test_ops(void) {
+  CHECK_FETCH(S37, 100, add, 5, 105);
+  CHECK_FETCH(S37, 100, sub, 40, 60);
+  CHECK_FETCH(S37, -3, add, 1, -2);
+  CHECK_FETCH(U37, 7, add, 9, 16);
+  CHECK_FETCH(S37, 0x15, and, 0x13, 0x11);
+  CHECK_FETCH(S37, 0x10, or, 5, 0x15);
+  CHECK_FETCH(S37, 0x1F, xor, 0x15, 0x0A);
+  CHECK_FETCH(S37, -5, min, -7, -7);    // signed: -7 < -5
+  CHECK_FETCH(U37, 5, min, (U37)-1, 5); // unsigned: 5 < 2^37-1
+  CHECK_FETCH(S37, 3, max, 9, 9);
+  CHECK_FETCH(S37, 0x15, nand, 0x13, (S37) ~(0x15 & 0x13));
+  // Wide widths: the libcall loop (no padding, and padded).
+  CHECK_FETCH(S256, 100, add, 5, 105);
+  CHECK_FETCH(S256, 1, or, 0xFE, 0xFF);
+  CHECK_FETCH(S200, 100, add, 5, 105);
+}
+
+// Seed non-canonical padding through a union, then RMW. A loop that carried a
+// re-canonicalized expected would never match memory and hang here.
+static void test_dirty_padding(void) {
+  union {
+    _Atomic(S37) a;
+    unsigned long b;
+  } s;
+  s.b = ((unsigned long)1 << 40) | 5u; // value bits 5, padding bit 40 set
+  S37 old = __c11_atomic_fetch_add(&s.a, 1, __ATOMIC_SEQ_CST);
+  assert(old == 5 && (S37)s.a == 6);
+
+  union {
+    _Atomic(U37) a;
+    unsigned long b;
+  } u;
+  u.b = ((unsigned long)3 << 50) | 7u;
+  U37 uold = __c11_atomic_fetch_add(&u.a, 1, __ATOMIC_SEQ_CST);
+  assert(uold == 7 && (U37)u.a == 8);
+
+  // Wide padded width (libcall loop): _BitInt(200) has 56 padding bits in its
+  // 32-byte storage. Set the overlay at value level (endian-independent): low
+  // 200 bits = 5, a padding bit (240) dirtied.
+  union {
+    _Atomic(S200) a;
+    unsigned _BitInt(256) full;
+  } w;
+  w.full = (unsigned _BitInt(256))5 | ((unsigned _BitInt(256))0xAA << 240);
+  S200 wold = __c11_atomic_fetch_add(&w.a, 1, __ATOMIC_SEQ_CST);
+  assert(wold == 5 && (S200)w.a == 6);
+}
+
+int main(void) {
+  test_ops();
+  test_dirty_padding();
+  printf("PASS\n");
+  return 0;
+}

>From 88f329b57018f8646b65484f0d36e92c9e54fd27 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sat, 27 Jun 2026 09:56:57 +0200
Subject: [PATCH 8/9] [Clang][test] Expand _BitInt atomic Sema and CodeGen
 coverage

Sema: add reject cases (non-_Atomic pointer, wrong arity, atomic _BitInt
bit-field) so lifting the _BitInt rejection does not silently drop the
atomic-specific checks, plus an __atomic_add_fetch (returns-new) accept.
CodeGen: add an unsigned arithmetic RMW (zero-extended desired) and signed
max / unsigned min, exercising the zext path and the icmp sgt/ule predicates
the previous functions did not.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/test/CodeGen/atomic-bitint.c | 140 +++++++++++++++++++++++++++++
 clang/test/Sema/atomic-bitint.c    |  15 +++-
 2 files changed, 152 insertions(+), 3 deletions(-)

diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
index bc1e165fd90e3..dda8f644f3fec 100644
--- a/clang/test/CodeGen/atomic-bitint.c
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -262,6 +262,146 @@ U37 min37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
 }
 
+// Unsigned arithmetic RMW: the desired is zero-extended, not sign-extended.
+// CHECK-LABEL: define dso_local i64 @uadd37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = zext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = zext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP7:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[NEW:%.*]] = add i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = zext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP5]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP7]] = extractvalue { i64, i1 } [[TMP6]], 0
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
+// CHECK-NEXT:    br i1 [[TMP8]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP9:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP9]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
+U37 uadd37(_Atomic(U37) *p, U37 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Signed max computes at the value width with a signed compare.
+// CHECK-LABEL: define dso_local i64 @max37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP8:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp sgt i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[LOADEDV4]], i37 [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP8]] = extractvalue { i64, i1 } [[TMP7]], 0
+// CHECK-NEXT:    [[TMP9:%.*]] = extractvalue { i64, i1 } [[TMP7]], 1
+// CHECK-NEXT:    br i1 [[TMP9]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP10:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP10]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
+S37 max37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_fetch_max(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Unsigned min computes at the value width with an unsigned compare.
+// CHECK-LABEL: define dso_local i64 @umin37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = zext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = zext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP8:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp ule i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[LOADEDV4]], i37 [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = zext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP8]] = extractvalue { i64, i1 } [[TMP7]], 0
+// CHECK-NEXT:    [[TMP9:%.*]] = extractvalue { i64, i1 } [[TMP7]], 1
+// CHECK-NEXT:    br i1 [[TMP9]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP10:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP10]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
+U37 umin37(_Atomic(U37) *p, U37 v) {
+  return __c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
+}
+
 // No padding: direct atomicrmw, no loop.
 // CHECK-LABEL: define dso_local i64 @add64(
 // CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V:%.*]]) #[[ATTR0]] {
diff --git a/clang/test/Sema/atomic-bitint.c b/clang/test/Sema/atomic-bitint.c
index fbb4c518438fb..3bd4faf22e7be 100644
--- a/clang/test/Sema/atomic-bitint.c
+++ b/clang/test/Sema/atomic-bitint.c
@@ -7,8 +7,6 @@
 // code imposes no width cap of its own; widths past 128 are available wherever
 // the target accepts _BitInt > 128 (x86 and RISC-V today).
 
-// expected-no-diagnostics
-
 _Atomic(_BitInt(4))    a4;     // small
 _Atomic(_BitInt(9))    a9;     // non-power-of-two
 _Atomic(_BitInt(37))   a37;    // padded
@@ -35,9 +33,20 @@ void c11_builtins(_Atomic(_BitInt(37)) *p, _BitInt(37) v, _BitInt(37) *e) {
   (void)__c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
 }
 
-// The GNU __atomic_* builtins take a plain _BitInt pointer.
+// The GNU __atomic_* builtins take a plain _BitInt pointer; the _fetch forms
+// return the new value.
 void gnu_builtins(_BitInt(37) *p, _BitInt(37) v) {
   (void)__atomic_load_n(p, __ATOMIC_SEQ_CST);
   __atomic_store_n(p, v, __ATOMIC_SEQ_CST);
   (void)__atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+  (void)__atomic_add_fetch(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Lifting the _BitInt rejection must not lose the atomic-specific checks.
+void rejects(_Atomic(_BitInt(37)) *ap, _BitInt(37) *p, _BitInt(37) v) {
+  (void)__c11_atomic_load(ap); // expected-error {{too few arguments to function call}}
+  (void)__c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST); // expected-error {{must be a pointer to _Atomic}}
 }
+struct WithAtomicBitIntField {
+  _Atomic(_BitInt(5)) f : 3; // expected-error {{bit-field 'f' has non-integral type}}
+};

>From 9d349acf9dc9f5c74c4103c3ed60252ca95ff53e Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sat, 27 Jun 2026 09:56:58 +0200
Subject: [PATCH 9/9] [compiler-rt] Harden the _BitInt atomic runtime test

Use uint64_t for the dirty-padding overlay (unsigned long is 32-bit on
LLP64, where the padding-bit shift was undefined). Read the storage back
after a converged RMW to confirm the padding is canonicalized, and add
returns-new (__atomic_*_fetch) and non-seq_cst ordering coverage.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 .../test/builtins/Unit/atomic_bitint_test.c   | 37 +++++++++++++++++--
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/compiler-rt/test/builtins/Unit/atomic_bitint_test.c b/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
index 33a745348a6f0..e0cc3aef61bc2 100644
--- a/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
+++ b/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
@@ -17,6 +17,7 @@
 //===----------------------------------------------------------------------===//
 
 #include <assert.h>
+#include <stdint.h>
 #include <stdio.h>
 
 typedef signed _BitInt(37) S37;
@@ -55,21 +56,25 @@ static void test_ops(void) {
 // Seed non-canonical padding through a union, then RMW. A loop that carried a
 // re-canonicalized expected would never match memory and hang here.
 static void test_dirty_padding(void) {
+  // uint64_t (not unsigned long, which is 32-bit on LLP64) so the padding bit
+  // is representable and the overlay matches the 8-byte atomic.
   union {
     _Atomic(S37) a;
-    unsigned long b;
+    uint64_t b;
   } s;
-  s.b = ((unsigned long)1 << 40) | 5u; // value bits 5, padding bit 40 set
+  s.b = ((uint64_t)1 << 40) | 5u; // value bits 5, padding bit 40 set
   S37 old = __c11_atomic_fetch_add(&s.a, 1, __ATOMIC_SEQ_CST);
   assert(old == 5 && (S37)s.a == 6);
+  assert((s.b >> 37) == 0); // padding canonicalized (positive value)
 
   union {
     _Atomic(U37) a;
-    unsigned long b;
+    uint64_t b;
   } u;
-  u.b = ((unsigned long)3 << 50) | 7u;
+  u.b = ((uint64_t)3 << 50) | 7u;
   U37 uold = __c11_atomic_fetch_add(&u.a, 1, __ATOMIC_SEQ_CST);
   assert(uold == 7 && (U37)u.a == 8);
+  assert((u.b >> 37) == 0); // padding canonicalized (zero-extended)
 
   // Wide padded width (libcall loop): _BitInt(200) has 56 padding bits in its
   // 32-byte storage. Set the overlay at value level (endian-independent): low
@@ -81,11 +86,35 @@ static void test_dirty_padding(void) {
   w.full = (unsigned _BitInt(256))5 | ((unsigned _BitInt(256))0xAA << 240);
   S200 wold = __c11_atomic_fetch_add(&w.a, 1, __ATOMIC_SEQ_CST);
   assert(wold == 5 && (S200)w.a == 6);
+  assert((w.full >> 200) == 0); // padding canonicalized (positive value)
+}
+
+// The _fetch builtins return the new value, not the old one.
+static void test_returns_new(void) {
+  S37 a = 100;
+  assert(__atomic_add_fetch(&a, 5, __ATOMIC_SEQ_CST) == 105);
+  assert(__atomic_sub_fetch(&a, 10, __ATOMIC_SEQ_CST) == 95);
+  U37 u = 0;
+  assert(__atomic_or_fetch(&u, 0xF, __ATOMIC_SEQ_CST) == 0xF);
+  S200 w = 100;
+  assert(__atomic_add_fetch(&w, 5, __ATOMIC_SEQ_CST) == 105);
+}
+
+// Each non-seq_cst ordering drives the loop's load/cmpxchg ordering.
+static void test_orderings(void) {
+  _Atomic(S37) a = 10;
+  (void)__c11_atomic_fetch_add(&a, 1, __ATOMIC_RELAXED);
+  (void)__c11_atomic_fetch_add(&a, 1, __ATOMIC_ACQUIRE);
+  (void)__c11_atomic_fetch_add(&a, 1, __ATOMIC_RELEASE);
+  (void)__c11_atomic_fetch_add(&a, 1, __ATOMIC_ACQ_REL);
+  assert((S37)a == 14);
 }
 
 int main(void) {
   test_ops();
   test_dirty_padding();
+  test_returns_new();
+  test_orderings();
   printf("PASS\n");
   return 0;
 }


From libcxx-commits at lists.llvm.org  Mon Jul 20 03:32:10 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Mon, 20 Jul 2026 03:32:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5df92a.1728eaf3.2a2f6.4e0d@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/20] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/20] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/20] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/20] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/20] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/20] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/20] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/20] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/20] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/20] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/20] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/20] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/20] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 67d88113e5987460d20b2a0001e6aab7e53a57d5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:57:29 +0200
Subject: [PATCH 14/20] add: islessequal.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/islessequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
new file mode 100644
index 0000000000000..98fb3a9e7770a
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::islessequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::islessequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::islessequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::islessequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::islessequal(2.0, 1));     // double vs int
+    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 6d1e2ff8878af3188e953f68bb199406ff942354 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 00:00:33 +0200
Subject: [PATCH 15/20] fix: clang-format

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 2 +-
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index 1b9c1aacd0d13..af999db6166b3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -80,7 +80,7 @@ int main(int, char**) {
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));   // int vs float
+    assert(std::isless(1, 2.0f));     // int vs float
     assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 98fb3a9e7770a..2023b062f55f9 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -84,7 +84,7 @@ int main(int, char**) {
   // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::islessequal(2.0, 1));     // double vs int
-    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
   }
 

>From 71b5f4cf216bba7e770e854906b5112d93cbe367 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 01:57:12 +0200
Subject: [PATCH 16/20] add: islessgreater.pass.cpp and isunordered.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  |  2 +-
 .../numerics/c.math/islessgreater.pass.cpp    | 91 +++++++++++++++++++
 .../std/numerics/c.math/isunordered.pass.cpp  | 78 ++++++++++++++++
 3 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
 create mode 100644 libcxx/test/std/numerics/c.math/isunordered.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2023b062f55f9..2b0be4ff37035 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
new file mode 100644
index 0000000000000..40fbb7f4a4e41
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::islessgreater(T(1), T(2)));
+    assert(std::islessgreater(T(2), T(1)));
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    if (!std::is_same<T, bool>::value) {
+      assert(std::islessgreater(T(1), T(2)));
+      assert(std::islessgreater(T(2), T(1)));
+    }
+
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(T(1), T(1)));
+    assert(std::islessgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::islessgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    if (std::is_signed<T>::value) {
+      assert(std::islessgreater(T(-1), T(1)));
+      assert(std::islessgreater(std::numeric_limits<T>::lowest(), T(0)));
+      assert(std::islessgreater(T(0), std::numeric_limits<T>::lowest()));
+      assert(!std::islessgreater(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
new file mode 100644
index 0000000000000..ed7740d195163
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(std::isunordered(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    assert(std::isunordered(std::numeric_limits<T>::signaling_NaN(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From ef949295028c1bd5d6a19881e122393aada59194 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:01:27 +0200
Subject: [PATCH 17/20] flip assert tests for gcc

---
 .../c.math/constexpr-cxx23-gcc.pass.cpp       | 30 +++++++++----------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index 34513b459a056..8d814e81f8bc6 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -225,25 +225,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From f6a4fa78d49d5bd14219d1cb7885d0d6bf4524ba Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:02:43 +0200
Subject: [PATCH 18/20] fix typo in isgreaterequal.pass.cpp

---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index d6ae553e961e5..f2a4159882654 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows

>From 62ae94cc86a2b43b61a9ba80eadfc4013bcced15 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 18:18:09 +0200
Subject: [PATCH 19/20] update: cmath synopsis for is<...>

---
 libcxx/include/cmath | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..65bf07aed059b 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -140,17 +140,17 @@ bool signbit(arithmetic x);
 
 int fpclassify(arithmetic x);
 
-bool isfinite(arithmetic x);
-bool isinf(arithmetic x);
-bool isnan(arithmetic x);
-bool isnormal(arithmetic x);
-
-bool isgreater(arithmetic x, arithmetic y);
-bool isgreaterequal(arithmetic x, arithmetic y);
-bool isless(arithmetic x, arithmetic y);
-bool islessequal(arithmetic x, arithmetic y);
-bool islessgreater(arithmetic x, arithmetic y);
-bool isunordered(arithmetic x, arithmetic y);
+bool isfinite(arithmetic x); // constexpr since C++23
+bool isinf(arithmetic x);    // constexpr since C++23
+bool isnan(arithmetic x);    // constexpr since C++23
+bool isnormal(arithmetic x); // constexpr since C++23
+
+bool isgreater(arithmetic x, arithmetic y);      // constexpr since C++23
+bool isgreaterequal(arithmetic x, arithmetic y); // constexpr since C++23
+bool isless(arithmetic x, arithmetic y);         // constexpr since C++23
+bool islessequal(arithmetic x, arithmetic y);    // constexpr since C++23
+bool islessgreater(arithmetic x, arithmetic y);  // constexpr since C++23
+bool isunordered(arithmetic x, arithmetic y);    // constexpr since C++23
 
 floating_point acosh (arithmetic x);
 float          acoshf(float x);

>From 1a641131cba0843189ecd9364df414c8baa85cad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 20 Jul 2026 12:31:47 +0200
Subject: [PATCH 20/20] remove: UNSUPPORTED windows from tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp      | 3 ---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 3 ---
 libcxx/test/std/numerics/c.math/isless.pass.cpp         | 3 ---
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp    | 3 ---
 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp  | 3 ---
 libcxx/test/std/numerics/c.math/isunordered.pass.cpp    | 3 ---
 6 files changed, 18 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index c7853974d7a66..269bb31e016cf 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index f2a4159882654..27fa43812812a 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index af999db6166b3..cc68a49beead9 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2b0be4ff37035..5f58bbc993613 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
index 40fbb7f4a4e41..6a10708359bbe 100644
--- a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
index ed7740d195163..3f140106a423f 100644
--- a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>


From libcxx-commits at lists.llvm.org  Mon Jul 20 03:32:36 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Mon, 20 Jul 2026 03:32:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5df944.b4a331f0.d9742.d573@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/20] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/20] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/20] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/20] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/20] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/20] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/20] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/20] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/20] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/20] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/20] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/20] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/20] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 67d88113e5987460d20b2a0001e6aab7e53a57d5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:57:29 +0200
Subject: [PATCH 14/20] add: islessequal.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/islessequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
new file mode 100644
index 0000000000000..98fb3a9e7770a
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::islessequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::islessequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::islessequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::islessequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::islessequal(2.0, 1));     // double vs int
+    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 6d1e2ff8878af3188e953f68bb199406ff942354 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 00:00:33 +0200
Subject: [PATCH 15/20] fix: clang-format

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 2 +-
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index 1b9c1aacd0d13..af999db6166b3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -80,7 +80,7 @@ int main(int, char**) {
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));   // int vs float
+    assert(std::isless(1, 2.0f));     // int vs float
     assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 98fb3a9e7770a..2023b062f55f9 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -84,7 +84,7 @@ int main(int, char**) {
   // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::islessequal(2.0, 1));     // double vs int
-    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
   }
 

>From 71b5f4cf216bba7e770e854906b5112d93cbe367 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 01:57:12 +0200
Subject: [PATCH 16/20] add: islessgreater.pass.cpp and isunordered.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  |  2 +-
 .../numerics/c.math/islessgreater.pass.cpp    | 91 +++++++++++++++++++
 .../std/numerics/c.math/isunordered.pass.cpp  | 78 ++++++++++++++++
 3 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
 create mode 100644 libcxx/test/std/numerics/c.math/isunordered.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2023b062f55f9..2b0be4ff37035 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
new file mode 100644
index 0000000000000..40fbb7f4a4e41
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::islessgreater(T(1), T(2)));
+    assert(std::islessgreater(T(2), T(1)));
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    if (!std::is_same<T, bool>::value) {
+      assert(std::islessgreater(T(1), T(2)));
+      assert(std::islessgreater(T(2), T(1)));
+    }
+
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(T(1), T(1)));
+    assert(std::islessgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::islessgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    if (std::is_signed<T>::value) {
+      assert(std::islessgreater(T(-1), T(1)));
+      assert(std::islessgreater(std::numeric_limits<T>::lowest(), T(0)));
+      assert(std::islessgreater(T(0), std::numeric_limits<T>::lowest()));
+      assert(!std::islessgreater(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
new file mode 100644
index 0000000000000..ed7740d195163
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(std::isunordered(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    assert(std::isunordered(std::numeric_limits<T>::signaling_NaN(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From ef949295028c1bd5d6a19881e122393aada59194 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:01:27 +0200
Subject: [PATCH 17/20] flip assert tests for gcc

---
 .../c.math/constexpr-cxx23-gcc.pass.cpp       | 30 +++++++++----------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index 34513b459a056..8d814e81f8bc6 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -225,25 +225,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From f6a4fa78d49d5bd14219d1cb7885d0d6bf4524ba Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:02:43 +0200
Subject: [PATCH 18/20] fix typo in isgreaterequal.pass.cpp

---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index d6ae553e961e5..f2a4159882654 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows

>From 62ae94cc86a2b43b61a9ba80eadfc4013bcced15 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 18:18:09 +0200
Subject: [PATCH 19/20] update: cmath synopsis for is<...>

---
 libcxx/include/cmath | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..65bf07aed059b 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -140,17 +140,17 @@ bool signbit(arithmetic x);
 
 int fpclassify(arithmetic x);
 
-bool isfinite(arithmetic x);
-bool isinf(arithmetic x);
-bool isnan(arithmetic x);
-bool isnormal(arithmetic x);
-
-bool isgreater(arithmetic x, arithmetic y);
-bool isgreaterequal(arithmetic x, arithmetic y);
-bool isless(arithmetic x, arithmetic y);
-bool islessequal(arithmetic x, arithmetic y);
-bool islessgreater(arithmetic x, arithmetic y);
-bool isunordered(arithmetic x, arithmetic y);
+bool isfinite(arithmetic x); // constexpr since C++23
+bool isinf(arithmetic x);    // constexpr since C++23
+bool isnan(arithmetic x);    // constexpr since C++23
+bool isnormal(arithmetic x); // constexpr since C++23
+
+bool isgreater(arithmetic x, arithmetic y);      // constexpr since C++23
+bool isgreaterequal(arithmetic x, arithmetic y); // constexpr since C++23
+bool isless(arithmetic x, arithmetic y);         // constexpr since C++23
+bool islessequal(arithmetic x, arithmetic y);    // constexpr since C++23
+bool islessgreater(arithmetic x, arithmetic y);  // constexpr since C++23
+bool isunordered(arithmetic x, arithmetic y);    // constexpr since C++23
 
 floating_point acosh (arithmetic x);
 float          acoshf(float x);

>From 1a641131cba0843189ecd9364df414c8baa85cad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 20 Jul 2026 12:31:47 +0200
Subject: [PATCH 20/20] remove: UNSUPPORTED windows from tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp      | 3 ---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 3 ---
 libcxx/test/std/numerics/c.math/isless.pass.cpp         | 3 ---
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp    | 3 ---
 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp  | 3 ---
 libcxx/test/std/numerics/c.math/isunordered.pass.cpp    | 3 ---
 6 files changed, 18 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index c7853974d7a66..269bb31e016cf 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index f2a4159882654..27fa43812812a 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index af999db6166b3..cc68a49beead9 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2b0be4ff37035..5f58bbc993613 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
index 40fbb7f4a4e41..6a10708359bbe 100644
--- a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
index ed7740d195163..3f140106a423f 100644
--- a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>


From libcxx-commits at lists.llvm.org  Mon Jul 20 04:02:58 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 04:02:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a5e0062.6c5720b3.2da3b9.a0e3@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From deea37f2081e61d37e69f89460f795aa5efe0720 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/14] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From aef95d864aae59df8f6dec2fa66ddc1a719f5638 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/14] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 23a51b634dcc3504d5b557ac93030bb0857656f2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/14] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From de998041add137b8e32fdef4863e4474dd21b5ec Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/14] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 8fcfd5da0ee11a5cdb8de5496630ef8c5ebacfd6 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/14] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 32db34b5c5169d6d705616b2e34278521ebb9885 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/14] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 62abfba3c272781d184751584632cb4cdd1e1c6c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/14] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From e4c8d234ef2affc34b7e3d16c85990904ebaf05a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/14] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From a036b2f2f08b3004f29713b9ae8fcb054cbf6a21 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/14] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From dccc322245a6507d61d3702d6141f1f4417d52bf Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/14] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 79c4e1f418372..95a3696e1ce57 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,7 +40,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -48,7 +48,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -56,7 +56,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -82,7 +82,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index 4ff65be645220..359f5a6099fce 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From ead248ba9c01d1558bb2cb0d229e9661d85eeefb Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/14] Format

---
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 .../range.elements/iterator/base.pass.cpp     | 22 +++++++++----------
 .../range.elements/sentinel/base.pass.cpp     |  2 +-
 .../range.split/iterator/base.pass.cpp        | 10 ++++-----
 .../range.split/iterator/deref.pass.cpp       |  7 +++---
 .../sentinel/ctor.convert.pass.cpp            |  4 ++--
 6 files changed, 23 insertions(+), 24 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 2522dd98e4b9c..0da468245e8cd 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 95a3696e1ce57..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,10 +22,9 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
-      { std::declval<T>().base() } noexcept;
-    };
+concept IsBaseNoexcept = requires {
+  { std::declval<T>().base() } noexcept;
+};
 
 using BaseView     = std::ranges::subrange<std::tuple<int>*>;
 using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<BaseView, 0>>;
@@ -41,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -56,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -65,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -82,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From ccc5a5ef18913e717a58b3c89d8209dbc0ebe1e2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/14] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..6b45987b15474 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 0da468245e8cd..90b8862cf382c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 19f8cb6337144ac348a237cbd3b39efd6b0067ee Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/14] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From db536a1ad37a2a76368977d3b656c7cbd036e632 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/14] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp              | 16 +++++++++++----
 .../sentinel/ctor.convert.pass.cpp            | 20 ++++++++++++-------
 2 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index 359f5a6099fce..f8492ab81fa2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -62,20 +62,26 @@ static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elem
 // !convertible_to<sentinel_t<V>, sentinel_t<Base>>
 static_assert(!std::is_constructible_v<
               std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>,
-              std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
+                              std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
 
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }


From libcxx-commits at lists.llvm.org  Mon Jul 20 04:04:01 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 04:04:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a5e00a1.f06f4994.2f0620.a932@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From deea37f2081e61d37e69f89460f795aa5efe0720 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/15] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From aef95d864aae59df8f6dec2fa66ddc1a719f5638 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/15] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 23a51b634dcc3504d5b557ac93030bb0857656f2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/15] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From de998041add137b8e32fdef4863e4474dd21b5ec Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/15] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 8fcfd5da0ee11a5cdb8de5496630ef8c5ebacfd6 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/15] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 32db34b5c5169d6d705616b2e34278521ebb9885 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/15] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 62abfba3c272781d184751584632cb4cdd1e1c6c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/15] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From e4c8d234ef2affc34b7e3d16c85990904ebaf05a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/15] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From a036b2f2f08b3004f29713b9ae8fcb054cbf6a21 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/15] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From dccc322245a6507d61d3702d6141f1f4417d52bf Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/15] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 79c4e1f418372..95a3696e1ce57 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,7 +40,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -48,7 +48,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -56,7 +56,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -82,7 +82,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index 4ff65be645220..359f5a6099fce 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From ead248ba9c01d1558bb2cb0d229e9661d85eeefb Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/15] Format

---
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 .../range.elements/iterator/base.pass.cpp     | 22 +++++++++----------
 .../range.elements/sentinel/base.pass.cpp     |  2 +-
 .../range.split/iterator/base.pass.cpp        | 10 ++++-----
 .../range.split/iterator/deref.pass.cpp       |  7 +++---
 .../sentinel/ctor.convert.pass.cpp            |  4 ++--
 6 files changed, 23 insertions(+), 24 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 2522dd98e4b9c..0da468245e8cd 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 95a3696e1ce57..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,10 +22,9 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
-      { std::declval<T>().base() } noexcept;
-    };
+concept IsBaseNoexcept = requires {
+  { std::declval<T>().base() } noexcept;
+};
 
 using BaseView     = std::ranges::subrange<std::tuple<int>*>;
 using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<BaseView, 0>>;
@@ -41,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -56,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -65,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -82,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From ccc5a5ef18913e717a58b3c89d8209dbc0ebe1e2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/15] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..6b45987b15474 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 0da468245e8cd..90b8862cf382c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 19f8cb6337144ac348a237cbd3b39efd6b0067ee Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/15] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From db536a1ad37a2a76368977d3b656c7cbd036e632 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/15] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp              | 16 +++++++++++----
 .../sentinel/ctor.convert.pass.cpp            | 20 ++++++++++++-------
 2 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index 359f5a6099fce..f8492ab81fa2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -62,20 +62,26 @@ static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elem
 // !convertible_to<sentinel_t<V>, sentinel_t<Base>>
 static_assert(!std::is_constructible_v<
               std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>,
-              std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
+                              std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
 
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From ad11680d2beacc47ab39a7b2bc7872cd681c6578 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/15] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 


From libcxx-commits at lists.llvm.org  Mon Jul 20 04:17:33 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 04:17:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format a few
 `elements_view` tests (PR #210687)
Message-ID: <llvm.org/llvm/llvm-project/pull/210687@github.com>


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

As a pre-requisite for https://github.com/llvm/llvm-project/pull/193891

>From 194eff9a48cfdf1eab31578835780a42d61e93e9 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:16:05 +0300
Subject: [PATCH] [libc++][ranges][NFC] Format a few `elements_view` tests

As a pre-requisite for https://github.com/llvm/llvm-project/pull/193891
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp   | 7 +++----
 .../range.elements/sentinel/ctor.convert.pass.cpp          | 6 +++---
 2 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 79c4e1f418372..a77e98f845a06 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,10 +22,9 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
-      { std::declval<T>().base() } noexcept;
-    };
+concept IsBaseNoexcept = requires {
+  { std::declval<T>().base() } noexcept;
+};
 
 using BaseView     = std::ranges::subrange<std::tuple<int>*>;
 using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<BaseView, 0>>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index 4ff65be645220..96019b74a2d02 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -60,9 +60,9 @@ static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elem
                                        std::ranges::sentinel_t<const std::ranges::elements_view<Range, 0>>>);
 
 // !convertible_to<sentinel_t<V>, sentinel_t<Base>>
-static_assert(!std::is_constructible_v<
-              std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>,
-              std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
+static_assert(
+    !std::is_constructible_v< std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>,
+                              std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
 
 constexpr bool test() {
   // base is init correctly


From libcxx-commits at lists.llvm.org  Mon Jul 20 04:18:14 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 04:18:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format a few
 `elements_view` tests (PR #210687)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210687@github.com>
Message-ID: <6a5e03f6.4a342cbc.3a2f36.73f6@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

<details>
<summary>Changes</summary>

As a pre-requisite for https://github.com/llvm/llvm-project/pull/193891

---
Full diff: https://github.com/llvm/llvm-project/pull/210687.diff


2 Files Affected:

- (modified) libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp (+3-4) 
- (modified) libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp (+3-3) 


``````````diff
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 79c4e1f418372..a77e98f845a06 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,10 +22,9 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
-      { std::declval<T>().base() } noexcept;
-    };
+concept IsBaseNoexcept = requires {
+  { std::declval<T>().base() } noexcept;
+};
 
 using BaseView     = std::ranges::subrange<std::tuple<int>*>;
 using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<BaseView, 0>>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index 4ff65be645220..96019b74a2d02 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -60,9 +60,9 @@ static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elem
                                        std::ranges::sentinel_t<const std::ranges::elements_view<Range, 0>>>);
 
 // !convertible_to<sentinel_t<V>, sentinel_t<Base>>
-static_assert(!std::is_constructible_v<
-              std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>,
-              std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
+static_assert(
+    !std::is_constructible_v< std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>,
+                              std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
 
 constexpr bool test() {
   // base is init correctly

``````````

</details>


https://github.com/llvm/llvm-project/pull/210687

From libcxx-commits at lists.llvm.org  Mon Jul 20 04:19:03 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 04:19:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format a few
 `elements_view` tests (PR #210687)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210687@github.com>
Message-ID: <6a5e0427.13be6df8.22eac4.1296@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/210687

>From 67da176793ad4a045b393947ef0014a064cfd602 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:16:05 +0300
Subject: [PATCH] [libc++][ranges][NFC] Format a few `elements_view` tests

As a pre-requisite for https://github.com/llvm/llvm-project/pull/193891
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp   | 7 +++----
 .../range.elements/sentinel/ctor.convert.pass.cpp          | 6 +++---
 2 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 79c4e1f418372..a77e98f845a06 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,10 +22,9 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
-      { std::declval<T>().base() } noexcept;
-    };
+concept IsBaseNoexcept = requires {
+  { std::declval<T>().base() } noexcept;
+};
 
 using BaseView     = std::ranges::subrange<std::tuple<int>*>;
 using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<BaseView, 0>>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index 4ff65be645220..a8bc817ced291 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -60,9 +60,9 @@ static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elem
                                        std::ranges::sentinel_t<const std::ranges::elements_view<Range, 0>>>);
 
 // !convertible_to<sentinel_t<V>, sentinel_t<Base>>
-static_assert(!std::is_constructible_v<
-              std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>,
-              std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
+static_assert(
+    !std::is_constructible_v<std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>,
+                             std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
 
 constexpr bool test() {
   // base is init correctly


From libcxx-commits at lists.llvm.org  Mon Jul 20 05:00:02 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 05:00:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] re-order availability
 mapping to `_LIBCPP_INTRODUCED_IN_LLVM_{N}` (PR #210694)
Message-ID: <llvm.org/llvm/llvm-project/pull/210694@github.com>


https://github.com/h-vetinari created https://github.com/llvm/llvm-project/pull/210694

Noticed that the new additions (from 16f692338cf41c29774e13f96856f040923f5d2c & 8a531c3608c722ad529be448d6ecef06ba107228) were placed rather randomly. Put the mapping back to reverse chronological order, c.f. 783fd2f9d583850ecacdf93cd65f903b64d0cf4d

>From 327bed66439b11b2e48832de1c746daafd159dcc Mon Sep 17 00:00:00 2001
From: "H. Vetinari" <h.vetinari at gmx.com>
Date: Mon, 20 Jul 2026 22:54:01 +1100
Subject: [PATCH] [libc++][NFC] re-order availability mapping to
 `_LIBCPP_INTRODUCED_IN_LLVM_{N}`

c.f. 783fd2f9d583850ecacdf93cd65f903b64d0cf4d
---
 libcxx/include/__configuration/availability.h | 24 +++++++++----------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 1fbd87ba52a64..0709deff47ea6 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -239,6 +239,18 @@
 
 #endif
 
+// This determines whether we assume that the internal std::__bad_variant_access_with_msg class
+// (which carries a message describing the cause of the failure in bad_variant_access::what())
+// provides a key function in the dylib. This allows centralizing its vtable and typeinfo instead
+// of having all TUs provide a weak definition that then gets deduplicated. When it is not available
+// in the dylib, what() is defined inline instead, so the descriptive message is provided regardless.
+#define _LIBCPP_AVAILABILITY_HAS_BAD_VARIANT_ACCESS_WITH_MSG_KEY_FUNCTION _LIBCPP_INTRODUCED_IN_LLVM_23
+// No attribute, since we've had bad_variant_access in the headers before
+
+// Controls whether the implementation for text_encoding::environment() is available
+#define _LIBCPP_AVAILABILITY_HAS_TEXT_ENCODING_ENVIRONMENT _LIBCPP_INTRODUCED_IN_LLVM_23
+#define _LIBCPP_AVAILABILITY_TEXT_ENCODING_ENVIRONMENT _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE
+
 // This controls the availability of new implementation of std::atomic's
 // wait, notify_one and notify_all. The new implementation uses
 // the native atomic wait/notify operations on platforms that support them
@@ -256,14 +268,6 @@
 #define _LIBCPP_AVAILABILITY_HAS_BAD_FUNCTION_CALL_GOOD_WHAT_MESSAGE _LIBCPP_INTRODUCED_IN_LLVM_21
 // No attribute, since we've had bad_function_call::what() in the headers before
 
-// This determines whether we assume that the internal std::__bad_variant_access_with_msg class
-// (which carries a message describing the cause of the failure in bad_variant_access::what())
-// provides a key function in the dylib. This allows centralizing its vtable and typeinfo instead
-// of having all TUs provide a weak definition that then gets deduplicated. When it is not available
-// in the dylib, what() is defined inline instead, so the descriptive message is provided regardless.
-#define _LIBCPP_AVAILABILITY_HAS_BAD_VARIANT_ACCESS_WITH_MSG_KEY_FUNCTION _LIBCPP_INTRODUCED_IN_LLVM_23
-// No attribute, since we've had bad_variant_access in the headers before
-
 // This controls the availability of floating-point std::from_chars functions.
 // These overloads were added later than the integer overloads.
 #define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20
@@ -323,10 +327,6 @@
 #  define _LIBCPP_AVAILABILITY_HAS_ADDITIONAL_IOSTREAM_EXPLICIT_INSTANTIATIONS_1 0
 #endif
 
-// Controls whether the implementation for text_encoding::environment() is available
-#define _LIBCPP_AVAILABILITY_HAS_TEXT_ENCODING_ENVIRONMENT _LIBCPP_INTRODUCED_IN_LLVM_23
-#define _LIBCPP_AVAILABILITY_TEXT_ENCODING_ENVIRONMENT _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE
-
 // Only define a bunch of symbols in the dylib if we need to be compatible with LLVM 7 headers or older
 #  if defined(_LIBCPP_BUILDING_LIBRARY) && _LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 8
 #    define _LIBCPP_HIDE_FROM_ABI_SINCE_LLVM8


From libcxx-commits at lists.llvm.org  Mon Jul 20 05:00:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 05:00:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] re-order availability
 mapping to `_LIBCPP_INTRODUCED_IN_LLVM_{N}` (PR #210694)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210694@github.com>
Message-ID: <6a5e0df7.b195937d.1f3ab7.f400@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: h-vetinari

<details>
<summary>Changes</summary>

Noticed that the new additions (from 16f692338cf41c29774e13f96856f040923f5d2c &amp; 8a531c3608c722ad529be448d6ecef06ba107228) were placed rather randomly. Put the mapping back to reverse chronological order, c.f. 783fd2f9d583850ecacdf93cd65f903b64d0cf4d

---
Full diff: https://github.com/llvm/llvm-project/pull/210694.diff


1 Files Affected:

- (modified) libcxx/include/__configuration/availability.h (+12-12) 


``````````diff
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 1fbd87ba52a64..0709deff47ea6 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -239,6 +239,18 @@
 
 #endif
 
+// This determines whether we assume that the internal std::__bad_variant_access_with_msg class
+// (which carries a message describing the cause of the failure in bad_variant_access::what())
+// provides a key function in the dylib. This allows centralizing its vtable and typeinfo instead
+// of having all TUs provide a weak definition that then gets deduplicated. When it is not available
+// in the dylib, what() is defined inline instead, so the descriptive message is provided regardless.
+#define _LIBCPP_AVAILABILITY_HAS_BAD_VARIANT_ACCESS_WITH_MSG_KEY_FUNCTION _LIBCPP_INTRODUCED_IN_LLVM_23
+// No attribute, since we've had bad_variant_access in the headers before
+
+// Controls whether the implementation for text_encoding::environment() is available
+#define _LIBCPP_AVAILABILITY_HAS_TEXT_ENCODING_ENVIRONMENT _LIBCPP_INTRODUCED_IN_LLVM_23
+#define _LIBCPP_AVAILABILITY_TEXT_ENCODING_ENVIRONMENT _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE
+
 // This controls the availability of new implementation of std::atomic's
 // wait, notify_one and notify_all. The new implementation uses
 // the native atomic wait/notify operations on platforms that support them
@@ -256,14 +268,6 @@
 #define _LIBCPP_AVAILABILITY_HAS_BAD_FUNCTION_CALL_GOOD_WHAT_MESSAGE _LIBCPP_INTRODUCED_IN_LLVM_21
 // No attribute, since we've had bad_function_call::what() in the headers before
 
-// This determines whether we assume that the internal std::__bad_variant_access_with_msg class
-// (which carries a message describing the cause of the failure in bad_variant_access::what())
-// provides a key function in the dylib. This allows centralizing its vtable and typeinfo instead
-// of having all TUs provide a weak definition that then gets deduplicated. When it is not available
-// in the dylib, what() is defined inline instead, so the descriptive message is provided regardless.
-#define _LIBCPP_AVAILABILITY_HAS_BAD_VARIANT_ACCESS_WITH_MSG_KEY_FUNCTION _LIBCPP_INTRODUCED_IN_LLVM_23
-// No attribute, since we've had bad_variant_access in the headers before
-
 // This controls the availability of floating-point std::from_chars functions.
 // These overloads were added later than the integer overloads.
 #define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20
@@ -323,10 +327,6 @@
 #  define _LIBCPP_AVAILABILITY_HAS_ADDITIONAL_IOSTREAM_EXPLICIT_INSTANTIATIONS_1 0
 #endif
 
-// Controls whether the implementation for text_encoding::environment() is available
-#define _LIBCPP_AVAILABILITY_HAS_TEXT_ENCODING_ENVIRONMENT _LIBCPP_INTRODUCED_IN_LLVM_23
-#define _LIBCPP_AVAILABILITY_TEXT_ENCODING_ENVIRONMENT _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE
-
 // Only define a bunch of symbols in the dylib if we need to be compatible with LLVM 7 headers or older
 #  if defined(_LIBCPP_BUILDING_LIBRARY) && _LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 8
 #    define _LIBCPP_HIDE_FROM_ABI_SINCE_LLVM8

``````````

</details>


https://github.com/llvm/llvm-project/pull/210694

From libcxx-commits at lists.llvm.org  Mon Jul 20 05:12:52 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Mon, 20 Jul 2026 05:12:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0493R5: Atomic
	minimum/maximum (PR #180333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/180333@github.com>
Message-ID: <6a5e10c4.e0e11d61.1d8f50.aaff@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/180333

>From ab79abbd1c14a305de4c16b791bf5e22384d543a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 17:10:11 +0800
Subject: [PATCH 01/27] implement atomic max/min

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/ReleaseNotes/23.rst               |  1 +
 libcxx/docs/Status/Cxx2cPapers.csv            |  2 +-
 libcxx/include/__atomic/atomic.h              | 68 ++++++++++++++++++
 libcxx/include/__atomic/atomic_ref.h          |  8 +++
 libcxx/include/__atomic/support.h             |  9 +++
 libcxx/include/__atomic/support/c11.h         | 26 +++++++
 libcxx/include/__atomic/support/gcc.h         | 24 +++++++
 libcxx/include/atomic                         | 24 +++++++
 libcxx/include/version                        |  2 +-
 libcxx/modules/std/atomic.inc                 |  4 ++
 .../atomics/atomics.ref/fetch_max.pass.cpp    | 70 +++++++++++++++++++
 .../atomics/atomics.ref/fetch_min.pass.cpp    | 70 +++++++++++++++++++
 .../atomic_fetch_max.pass.cpp                 | 55 +++++++++++++++
 .../atomic_fetch_max_explicit.pass.cpp        | 57 +++++++++++++++
 .../atomic_fetch_min.pass.cpp                 | 55 +++++++++++++++
 .../atomic_fetch_min_explicit.pass.cpp        | 57 +++++++++++++++
 .../atomic.version.compile.pass.cpp           | 16 ++---
 .../version.version.compile.pass.cpp          | 16 ++---
 .../generate_feature_test_macro_components.py |  1 -
 20 files changed, 541 insertions(+), 26 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index d2b76cb96e866..701775e051f9a 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -416,7 +416,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_associative_heterogeneous_insertion``          *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_atomic_min_max``                               *unimplemented*
+    ``__cpp_lib_atomic_min_max``                               ``202403L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_atomic_ref``                                   ``202411L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index a1d62a367f33c..ce2eee5ed852a 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P2440R1 (partial): ``ranges::iota`` and ``ranges::shift_left`` are supported (`Github <https://llvm.org/PR105184>`__)
+- P0493R5: Atomic minimum/maximum are supported (`Github <https://llvm.org/PR105418>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 29642fc53cac6..50819f1b47a27 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -53,7 +53,7 @@
 "`P3107R5 <https://wg21.link/P3107R5>`__","Permit an efficient implementation of ``std::print``","2024-03 (Tokyo)","","","`#105414 <https://github.com/llvm/llvm-project/issues/105414>`__",""
 "`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)"
 "`P2845R8 <https://wg21.link/P2845R8>`__","Formatting of ``std::filesystem::path``","2024-03 (Tokyo)","","","`#105416 <https://github.com/llvm/llvm-project/issues/105416>`__",""
-"`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","","","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
+"`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","|Complete|","23","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
 "`P2542R8 <https://wg21.link/P2542R8>`__","``views::concat``","2024-03 (Tokyo)","","","`#105419 <https://github.com/llvm/llvm-project/issues/105419>`__",""
 "`P2591R5 <https://wg21.link/P2591R5>`__","Concatenation of strings and string views","2024-03 (Tokyo)","|Complete|","19","`#105420 <https://github.com/llvm/llvm-project/issues/105420>`__",""
 "`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","`#105421 <https://github.com/llvm/llvm-project/issues/105421>`__",""
diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index ea1d28fbf850a..f1c7492590d61 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -178,6 +178,20 @@ struct __atomic_base<_Tp, true> : public __atomic_base<_Tp, false> {
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
     return std::__cxx_atomic_fetch_xor(std::addressof(this->__a_), __op, __m);
   }
+#if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+#endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) volatile _NOEXCEPT { return fetch_add(_Tp(1)); }
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) _NOEXCEPT { return fetch_add(_Tp(1)); }
@@ -799,6 +813,60 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
   return __o->fetch_xor(__op, __m);
 }
 
+#if _LIBCPP_STD_VER >= 26
+// atomic_fetch_max
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_max(__op);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_max(__op);
+}
+
+// atomic_fetch_max_explicit
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
+    volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_max(__op, __m);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_max(__op, __m);
+}
+
+// atomic_fetch_min
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_min(__op);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_min(__op);
+}
+
+// atomic_fetch_min_explicit
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
+    volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_min(__op, __m);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_min(__op, __m);
+}
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ATOMIC_ATOMIC_H
diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 69edbfe6ecadd..cedd4248eb200 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -299,6 +299,14 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
     return __atomic_fetch_xor(this->__ptr_, __arg, std::__to_gcc_order(__order));
   }
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+  }
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) const noexcept { return fetch_add(_Tp(1)); }
   _LIBCPP_HIDE_FROM_ABI _Tp operator--(int) const noexcept { return fetch_sub(_Tp(1)); }
diff --git a/libcxx/include/__atomic/support.h b/libcxx/include/__atomic/support.h
index 99d0f6aa543ca..eda38705acd32 100644
--- a/libcxx/include/__atomic/support.h
+++ b/libcxx/include/__atomic/support.h
@@ -99,6 +99,15 @@
 // template <class _Tp>
 // _Tp __cxx_atomic_fetch_xor(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
 //
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_max(_Atmc<_Tp> volatile* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_max(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_min(_Atmc<_Tp> volatile* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_min(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
+//
 // clang-format on
 //
 
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f28b5e461c18a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -259,6 +259,32 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
       std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
 }
 
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ATOMIC_SUPPORT_C11_H
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..dfaebb68ced74 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -258,6 +258,30 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   return __atomic_fetch_xor(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
 }
 
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 23a3db5d35029..4cf93b6bfbd85 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -164,6 +164,10 @@ struct atomic<integral>
     integral fetch_or(integral op, memory_order m = memory_order_seq_cst) noexcept;
     integral fetch_xor(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
     integral fetch_xor(integral op, memory_order m = memory_order_seq_cst) noexcept;
+    integral fetch_max(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
+    integral fetch_max(integral op, memory_order m = memory_order_seq_cst) noexcept;
+    integral fetch_min(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
+    integral fetch_min(integral op, memory_order m = memory_order_seq_cst) noexcept;
 
     integral operator++(int) volatile noexcept;
     integral operator++(int) noexcept;
@@ -441,6 +445,26 @@ template<class T>
 template<class T>
   T atomic_fetch_xor_explicit(atomic<T>*, atomic<T>::value_type,
                               memory_order) noexcept;
+template<class T>
+  T atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
 
 template<class T>
   void atomic_wait(const volatile atomic<T>*, atomic<T>::value_type) noexcept; // since C++20
diff --git a/libcxx/include/version b/libcxx/include/version
index 7d77677a012ce..a00e9441202bd 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -546,7 +546,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #if _LIBCPP_STD_VER >= 26
 # define __cpp_lib_aligned_accessor                     202411L
 // # define __cpp_lib_associative_heterogeneous_insertion  202306L
-// # define __cpp_lib_atomic_min_max                       202403L
+# define __cpp_lib_atomic_min_max                       202403L
 # undef  __cpp_lib_atomic_ref
 # define __cpp_lib_atomic_ref                           202411L
 # undef  __cpp_lib_bind_front
diff --git a/libcxx/modules/std/atomic.inc b/libcxx/modules/std/atomic.inc
index 9a30fb7affe71..11f77991068c1 100644
--- a/libcxx/modules/std/atomic.inc
+++ b/libcxx/modules/std/atomic.inc
@@ -50,6 +50,10 @@ export namespace std {
   using std::atomic_fetch_sub_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor_explicit _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_max _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_max_explicit _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_min _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_min_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_all _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_one _LIBCPP_USING_IF_EXISTS;
   using std::atomic_wait _LIBCPP_USING_IF_EXISTS;
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
new file mode 100644
index 0000000000000..6c1b69dce1595
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// integral-type fetch_max(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+template <typename T>
+concept has_fetch_max = requires {
+  std::declval<T const>().fetch_max(std::declval<T>());
+  std::declval<T const>().fetch_max(std::declval<T>(), std::declval<std::memory_order>());
+};
+
+template <typename T>
+struct TestDoesNotHaveFetchMax {
+  void operator()() const { static_assert(!has_fetch_max<std::atomic_ref<T>>); }
+};
+
+template <typename T>
+struct TestFetchMax {
+  void operator()() const {
+    static_assert(std::is_integral_v<T>);
+
+    alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
+    std::atomic_ref<T> const a(x);
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
+      assert(y == T(1));
+      assert(x == T(2));
+      ASSERT_NOEXCEPT(a.fetch_max(T(0)));
+    }
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_max(T(1), std::memory_order_relaxed);
+      assert(y == T(2));
+      assert(x == T(2));
+      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFetchMax>()();
+
+  TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
+
+  TestEachPointerType<TestDoesNotHaveFetchMax>()();
+
+  TestDoesNotHaveFetchMax<bool>()();
+  TestDoesNotHaveFetchMax<UserAtomicType>()();
+  TestDoesNotHaveFetchMax<LargeUserAtomicType>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
new file mode 100644
index 0000000000000..1fa2d32c4d7d8
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// integral-type fetch_min(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+template <typename T>
+concept has_fetch_min = requires {
+  std::declval<T const>().fetch_min(std::declval<T>());
+  std::declval<T const>().fetch_min(std::declval<T>(), std::declval<std::memory_order>());
+};
+
+template <typename T>
+struct TestDoesNotHaveFetchMin {
+  void operator()() const { static_assert(!has_fetch_min<std::atomic_ref<T>>); }
+};
+
+template <typename T>
+struct TestFetchMin {
+  void operator()() const {
+    static_assert(std::is_integral_v<T>);
+
+    alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
+    std::atomic_ref<T> const a(x);
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
+      assert(y == T(3));
+      assert(x == T(2));
+      ASSERT_NOEXCEPT(a.fetch_min(T(0)));
+    }
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
+      assert(y == T(2));
+      assert(x == T(1));
+      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFetchMin>()();
+
+  TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
+
+  TestEachPointerType<TestDoesNotHaveFetchMin>()();
+
+  TestDoesNotHaveFetchMin<bool>()();
+  TestDoesNotHaveFetchMin<UserAtomicType>()();
+  TestDoesNotHaveFetchMin<LargeUserAtomicType>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
new file mode 100644
index 0000000000000..25411767ca8ad
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_max(&t, T(2)) == T(1));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_max(&t, T(2)) == T(3));
+      assert(t == T(3));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
new file mode 100644
index 0000000000000..a670f1c10ce60
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+      assert(t == T(3));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
new file mode 100644
index 0000000000000..242355120c28d
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_min(&t, T(2)) == T(1));
+      assert(t == T(1));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_min(&t, T(2)) == T(3));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
new file mode 100644
index 0000000000000..46b7531b1f727
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+      assert(t == T(1));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
index 8bd027445c85b..9e620317c35c8 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
@@ -339,17 +339,11 @@
 #    error "__cpp_lib_atomic_lock_free_type_aliases should have the value 201907L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should be defined in c++26"
-#    endif
-#    if __cpp_lib_atomic_min_max != 202403L
-#      error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_atomic_min_max
+#    error "__cpp_lib_atomic_min_max should be defined in c++26"
+#  endif
+#  if __cpp_lib_atomic_min_max != 202403L
+#    error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_atomic_ref
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 f442b22c6d752..33f402b6422f6 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
@@ -6325,17 +6325,11 @@
 #    error "__cpp_lib_atomic_lock_free_type_aliases should have the value 201907L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should be defined in c++26"
-#    endif
-#    if __cpp_lib_atomic_min_max != 202403L
-#      error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_atomic_min_max
+#    error "__cpp_lib_atomic_min_max should be defined in c++26"
+#  endif
+#  if __cpp_lib_atomic_min_max != 202403L
+#    error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_atomic_ref
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 88431c76a00f5..799bb088f061e 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -191,7 +191,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_atomic_min_max",
             "values": {"c++26": 202403}, # P0493R5: Atomic minimum/maximum
             "headers": ["atomic"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_atomic_ref",

>From fb39fd535b1714f6d422d9b0f1810102a763a1b3 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 19:20:50 +0800
Subject: [PATCH 02/27] fix gcc build

---
 libcxx/include/__atomic/support/gcc.h | 28 +++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index dfaebb68ced74..e50143bb2bfa9 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -261,25 +261,45 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret > __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret > __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret < __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret < __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)

>From 906a95e15e4bdd1136be7bfc1f14731e6a3a3135 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 19:36:17 +0800
Subject: [PATCH 03/27] fix gcc atomic_ref

---
 libcxx/include/__atomic/atomic_ref.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index cedd4248eb200..6ea270dc3b902 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -23,6 +23,7 @@
 #include <__atomic/check_memory_order.h>
 #include <__atomic/floating_point_helper.h>
 #include <__atomic/memory_order.h>
+#include <__atomic/support.h>
 #include <__atomic/to_gcc_order.h>
 #include <__concepts/arithmetic.h>
 #include <__concepts/same_as.h>
@@ -301,10 +302,10 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+    return std::__cxx_atomic_fetch_max(this->__ptr_, __arg, __m);
   }
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+    return std::__cxx_atomic_fetch_min(this->__ptr_, __arg, __m);
   }
 #  endif
 

>From a64208d9c117ccbd5a4cc5677a662b6e4f972f59 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 19:55:43 +0800
Subject: [PATCH 04/27] fix gcc

---
 libcxx/include/__atomic/atomic_ref.h  | 14 ++++++++++++--
 libcxx/include/__atomic/support/gcc.h |  8 ++++----
 2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 6ea270dc3b902..11d74f71e6de1 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -302,10 +302,20 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return std::__cxx_atomic_fetch_max(this->__ptr_, __arg, __m);
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old > __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
   }
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return std::__cxx_atomic_fetch_min(this->__ptr_, __arg, __m);
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old < __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
   }
 #  endif
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index e50143bb2bfa9..c224bb6c95391 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -261,7 +261,7 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
@@ -272,7 +272,7 @@ __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
@@ -283,7 +283,7 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
@@ -294,7 +294,7 @@ __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;

>From 5eeeeea250032dd5ae65ec4a1868158cefe3c650 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 20:12:55 +0800
Subject: [PATCH 05/27] fix gcc

---
 libcxx/include/__atomic/support/gcc.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index c224bb6c95391..3eaccf95e19d8 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -265,7 +265,7 @@ __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -276,7 +276,7 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -287,7 +287,7 @@ __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -298,7 +298,7 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 

>From 46257efd512cc8bd741cc9dd3157b59ab1ce1ca1 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 20:39:31 +0800
Subject: [PATCH 06/27] remove unused include

---
 libcxx/include/__atomic/atomic_ref.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 11d74f71e6de1..84b9bf211b4b9 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -23,7 +23,6 @@
 #include <__atomic/check_memory_order.h>
 #include <__atomic/floating_point_helper.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support.h>
 #include <__atomic/to_gcc_order.h>
 #include <__concepts/arithmetic.h>
 #include <__concepts/same_as.h>

>From dc1cb331ddcb980cce4a8deeef8d1c21b08b0eb7 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 21:29:39 +0800
Subject: [PATCH 07/27] guard modules

---
 libcxx/modules/std/atomic.inc | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/modules/std/atomic.inc b/libcxx/modules/std/atomic.inc
index 11f77991068c1..38321e1a1bb64 100644
--- a/libcxx/modules/std/atomic.inc
+++ b/libcxx/modules/std/atomic.inc
@@ -50,10 +50,12 @@ export namespace std {
   using std::atomic_fetch_sub_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor_explicit _LIBCPP_USING_IF_EXISTS;
+#if _LIBCPP_STD_VER >= 26
   using std::atomic_fetch_max _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_max_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_min _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_min_explicit _LIBCPP_USING_IF_EXISTS;
+#endif
   using std::atomic_notify_all _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_one _LIBCPP_USING_IF_EXISTS;
   using std::atomic_wait _LIBCPP_USING_IF_EXISTS;

>From dfce3c7d45c1dca9e1ac85b293321ef19e007b23 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 8 Feb 2026 19:37:33 +0800
Subject: [PATCH 08/27] address review comments: use builtin if possible

---
 libcxx/include/__atomic/atomic_ref.h | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 84b9bf211b4b9..88c7d7dbc5dfb 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -301,20 +301,28 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_max)
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp __old = this->load(memory_order_relaxed);
     _Tp __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_min)
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp __old = this->load(memory_order_relaxed);
     _Tp __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
 #  endif
 

>From a3cabfda1b316f2514df394232a4e504f38159eb Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 8 Feb 2026 23:12:48 +0800
Subject: [PATCH 09/27] address review comments: use builtin if possible

---
 libcxx/include/__atomic/support/gcc.h | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 3eaccf95e19d8..754c60406cbf6 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -261,45 +261,61 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_max)
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_max)
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_min)
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_min)
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)

>From f01144d60576864032d1204a7662ddcb9540059a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 9 Feb 2026 20:33:05 +0800
Subject: [PATCH 10/27] codestyle

---
 libcxx/include/__atomic/atomic.h              | 24 ++++++++++++-------
 .../atomic_fetch_max.pass.cpp                 | 13 ++++------
 .../atomic_fetch_max_explicit.pass.cpp        | 15 ++++--------
 .../atomic_fetch_min.pass.cpp                 | 13 ++++------
 .../atomic_fetch_min_explicit.pass.cpp        | 15 ++++--------
 5 files changed, 34 insertions(+), 46 deletions(-)

diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index f1c7492590d61..e4be21cfd2d84 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -816,25 +816,29 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 #if _LIBCPP_STD_VER >= 26
 // atomic_fetch_max
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
 
 // atomic_fetch_max_explicit
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
@@ -842,25 +846,29 @@ atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 
 // atomic_fetch_min
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
 
 // atomic_fetch_min_explicit
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 25411767ca8ad..8aa66f68a23d7 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -12,12 +12,9 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
-//
+//   T atomic_fetch_max(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
 // template<class T>
-//     T
-//     atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+//   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -30,16 +27,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_max(&t, T(2)) == T(1));
       assert(t == T(2));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_max(&t, T(2)) == T(3));
       assert(t == T(3));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index a670f1c10ce60..1d04bb737fe1f 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -12,14 +12,11 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//   T atomic_fetch_max_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
 //                               memory_order) noexcept;
-//
 // template<class T>
-//     T
-//     atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
-//                               memory_order) noexcept;
+//   constexpr T atomic_fetch_max_explicit(atomic<T>*, typename atomic<T>::value_type,
+//                                         memory_order) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -32,16 +29,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
       assert(t == T(2));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
       assert(t == T(3));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 242355120c28d..43b979144b19b 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -12,12 +12,9 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
-//
+//   T atomic_fetch_min(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
 // template<class T>
-//     T
-//     atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+//   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -30,16 +27,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_min(&t, T(2)) == T(1));
       assert(t == T(1));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_min(&t, T(2)) == T(3));
       assert(t == T(2));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 46b7531b1f727..309489f91f126 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -12,14 +12,11 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//   T atomic_fetch_min_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
 //                               memory_order) noexcept;
-//
 // template<class T>
-//     T
-//     atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
-//                               memory_order) noexcept;
+//   constexpr T atomic_fetch_min_explicit(atomic<T>*, typename atomic<T>::value_type,
+//                                         memory_order) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -32,16 +29,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
       assert(t == T(1));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
       assert(t == T(2));
 

>From 4a4735ee1ec8f356a2c73570d289beb93dce9354 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 9 Feb 2026 20:46:52 +0800
Subject: [PATCH 11/27] ci

---
 libcxx/include/__atomic/atomic.h                       |  2 ++
 libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp | 10 ++++++++--
 libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp |  6 ++++++
 3 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index e4be21cfd2d84..455acb8fca2ad 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -16,6 +16,8 @@
 #include <__atomic/is_always_lock_free.h>
 #include <__atomic/memory_order.h>
 #include <__atomic/support.h>
+#include <__concepts/arithmetic.h>
+#include <__concepts/same_as.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 6c1b69dce1595..6e96baaaf6e2c 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -43,13 +43,19 @@ struct TestFetchMax {
       std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
       assert(y == T(1));
       assert(x == T(2));
+      y = a.fetch_max(T(0));
+      assert(y == T(2));
+      assert(x == T(2));
       ASSERT_NOEXCEPT(a.fetch_max(T(0)));
     }
 
     {
-      std::same_as<T> decltype(auto) y = a.fetch_max(T(1), std::memory_order_relaxed);
+      std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
       assert(y == T(2));
-      assert(x == T(2));
+      assert(x == T(3));
+      y = a.fetch_max(T(0));
+      assert(y == T(3));
+      assert(x == T(3));
       ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
     }
   }
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index 1fa2d32c4d7d8..92b7f1ed39cb1 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -43,6 +43,9 @@ struct TestFetchMin {
       std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
       assert(y == T(3));
       assert(x == T(2));
+      y = a.fetch_min(T(4));
+      assert(y == T(2));
+      assert(x == T(2));
       ASSERT_NOEXCEPT(a.fetch_min(T(0)));
     }
 
@@ -50,6 +53,9 @@ struct TestFetchMin {
       std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
       assert(y == T(2));
       assert(x == T(1));
+      y = a.fetch_min(T(4));
+      assert(y == T(1));
+      assert(x == T(1));
       ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
     }
   }

>From cfce0bec4a759eea1786642fd3a309032aecb2fc Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 15:26:50 +0800
Subject: [PATCH 12/27] revise testcases

---
 .../atomics/atomics.ref/fetch_max.pass.cpp    | 11 ++-
 .../atomics/atomics.ref/fetch_min.pass.cpp    | 11 ++-
 .../atomic_fetch_max.pass.cpp                 | 85 +++++++++++++++---
 .../atomic_fetch_max_explicit.pass.cpp        | 86 ++++++++++++++++---
 .../atomic_fetch_min.pass.cpp                 | 85 +++++++++++++++---
 .../atomic_fetch_min_explicit.pass.cpp        | 86 ++++++++++++++++---
 6 files changed, 296 insertions(+), 68 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 6e96baaaf6e2c..17930c5d97e65 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -15,26 +15,25 @@
 #include <cassert>
 #include <concepts>
 #include <type_traits>
-#include <utility>
 
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
 template <typename T>
-concept has_fetch_max = requires {
-  std::declval<T const>().fetch_max(std::declval<T>());
-  std::declval<T const>().fetch_max(std::declval<T>(), std::declval<std::memory_order>());
+concept has_fetch_max = requires(std::atomic_ref<T> const& a, T v) {
+  { a.fetch_max(v) } -> std::same_as<T>;
+  { a.fetch_max(v, std::memory_order::relaxed) } -> std::same_as<T>;
 };
 
 template <typename T>
 struct TestDoesNotHaveFetchMax {
-  void operator()() const { static_assert(!has_fetch_max<std::atomic_ref<T>>); }
+  void operator()() const { static_assert(!has_fetch_max<T>); }
 };
 
 template <typename T>
 struct TestFetchMax {
   void operator()() const {
-    static_assert(std::is_integral_v<T>);
+    static_assert(std::is_integral_v<T> && has_fetch_max<T>);
 
     alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
     std::atomic_ref<T> const a(x);
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index 92b7f1ed39cb1..e930d83a2b819 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -15,26 +15,25 @@
 #include <cassert>
 #include <concepts>
 #include <type_traits>
-#include <utility>
 
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
 template <typename T>
-concept has_fetch_min = requires {
-  std::declval<T const>().fetch_min(std::declval<T>());
-  std::declval<T const>().fetch_min(std::declval<T>(), std::declval<std::memory_order>());
+concept has_fetch_min = requires(std::atomic_ref<T> const& a, T v) {
+  { a.fetch_min(v) } -> std::same_as<T>;
+  { a.fetch_min(v, std::memory_order::relaxed) } -> std::same_as<T>;
 };
 
 template <typename T>
 struct TestDoesNotHaveFetchMin {
-  void operator()() const { static_assert(!has_fetch_min<std::atomic_ref<T>>); }
+  void operator()() const { static_assert(!has_fetch_min<T>); }
 };
 
 template <typename T>
 struct TestFetchMin {
   void operator()() const {
-    static_assert(std::is_integral_v<T>);
+    static_assert(std::is_integral_v<T> && has_fetch_min<T>);
 
     alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
     std::atomic_ref<T> const a(x);
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 8aa66f68a23d7..cbd8f147f5931 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -17,28 +20,82 @@
 //   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_max(&t, T(2)) == T(1));
-      assert(t == T(2));
+concept HasVolatileFetchMax = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_max(&a, t) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMax<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(std::atomic_fetch_max(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
+
+  // atomic_fetch_max
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_max(&t, T(2)) == T(1));
+    assert(t == T(2));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_max(&t, T(2)) == T(3));
+    assert(t == T(3));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_max concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(0);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = 0; j < loop; ++j) {
+          std::atomic_fetch_max(&at, T(i * loop + j));
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_max(&t, T(2)) == T(3));
-      assert(t == T(3));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(0);
+      for (auto i = 0; i < n; ++i) {
+        res = std::max<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 1d04bb737fe1f..ddae555670f17 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -19,28 +22,83 @@
 //                                         memory_order) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-      assert(t == T(2));
+concept HasVolatileFetchMaxExplicit = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_max_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMaxExplicit<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(
+      std::atomic_fetch_max_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
+
+  // atomic_fetch_max_explicit
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+    assert(t == T(2));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    assert(t == T(3));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_max_explicit concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(0);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = 0; j < loop; ++j) {
+          std::atomic_fetch_max_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
-      assert(t == T(3));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(0);
+      for (auto i = 0; i < n; ++i) {
+        res = std::max<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 43b979144b19b..33ca27f3de262 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -17,28 +20,82 @@
 //   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_min(&t, T(2)) == T(1));
-      assert(t == T(1));
+concept HasVolatileFetchMin = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_min(&a, t) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMin<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(std::atomic_fetch_min(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
+
+  // atomic_fetch_min
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_min(&t, T(2)) == T(1));
+    assert(t == T(1));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_min(&t, T(2)) == T(3));
+    assert(t == T(2));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_min concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = loop - 1; j >= 0; --j) {
+          std::atomic_fetch_min(&at, T(i * loop + j));
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_min(&t, T(2)) == T(3));
-      assert(t == T(2));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(n);
+      for (auto i = n - 1; i >= 0; --i) {
+        res = std::min<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 309489f91f126..99d20c0c544fb 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -19,28 +22,83 @@
 //                                         memory_order) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-      assert(t == T(1));
+concept HasVolatileFetchMinExplicit = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_min_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMinExplicit<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(
+      std::atomic_fetch_min_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
+
+  // atomic_fetch_min_explicit
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+    assert(t == T(1));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    assert(t == T(2));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_min_explicit concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = loop - 1; j >= 0; --j) {
+          std::atomic_fetch_min_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
-      assert(t == T(2));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(n);
+      for (auto i = n - 1; i >= 0; --i) {
+        res = std::min<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };

>From 71961be2c3c55d2ff0f0224ada38814fe004c972 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 15:42:24 +0800
Subject: [PATCH 13/27] fix ci for aix

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp  | 6 ------
 .../atomic_fetch_max_explicit.pass.cpp                      | 6 ------
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp  | 6 ------
 .../atomic_fetch_min_explicit.pass.cpp                      | 6 ------
 4 files changed, 24 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index cbd8f147f5931..6f89cd719a2ef 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -33,14 +33,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMax = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_max(&a, t) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMax<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(std::atomic_fetch_max(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
 
   // atomic_fetch_max
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index ddae555670f17..931c5a482ed58 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -35,14 +35,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMaxExplicit = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_max_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMaxExplicit<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(
       std::atomic_fetch_max_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 33ca27f3de262..e7a4d4e7bc798 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -33,14 +33,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMin = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_min(&a, t) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMin<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(std::atomic_fetch_min(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
 
   // atomic_fetch_min
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 99d20c0c544fb..acf47f0d89567 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -35,14 +35,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMinExplicit = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_min_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMinExplicit<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(
       std::atomic_fetch_min_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
 

>From 8844e1581d89f537673084b04236710aa1a41ec5 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 15:48:50 +0800
Subject: [PATCH 14/27] fix modules build

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp       | 1 +
 .../atomic_fetch_max_explicit.pass.cpp                           | 1 +
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp       | 1 +
 .../atomic_fetch_min_explicit.pass.cpp                           | 1 +
 4 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 6f89cd719a2ef..93bd767cbab35 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -19,6 +19,7 @@
 // template<class T>
 //   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 931c5a482ed58..7fb2a702be337 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -21,6 +21,7 @@
 //   constexpr T atomic_fetch_max_explicit(atomic<T>*, typename atomic<T>::value_type,
 //                                         memory_order) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index e7a4d4e7bc798..a5adda9487897 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -19,6 +19,7 @@
 // template<class T>
 //   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index acf47f0d89567..f5a23b153fdbe 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -21,6 +21,7 @@
 //   constexpr T atomic_fetch_min_explicit(atomic<T>*, typename atomic<T>::value_type,
 //                                         memory_order) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>

>From 7bfd5438857639f2d3b627c18fe56e57a1165067 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 17:24:43 +0800
Subject: [PATCH 15/27] declval

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp       | 1 +
 .../atomic_fetch_max_explicit.pass.cpp                           | 1 +
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp       | 1 +
 .../atomic_fetch_min_explicit.pass.cpp                           | 1 +
 4 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 93bd767cbab35..af249355811a5 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -23,6 +23,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 7fb2a702be337..45df7c240dfa2 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -25,6 +25,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index a5adda9487897..e326d4a4e5b7a 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -23,6 +23,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index f5a23b153fdbe..4e110f7f34944 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -25,6 +25,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"

>From a844bd2596f09f8eda9af8bf9fca7c67997816f8 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 19:51:19 +0800
Subject: [PATCH 16/27] remove xfail

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp     | 3 ---
 .../atomic_fetch_max_explicit.pass.cpp                         | 3 ---
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp     | 3 ---
 .../atomic_fetch_min_explicit.pass.cpp                         | 3 ---
 4 files changed, 12 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index af249355811a5..e4aba6ebbdd20 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 45df7c240dfa2..441fb57c3b332 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index e326d4a4e5b7a..3a4a15290b407 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 4e110f7f34944..fd208162b9c07 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>

>From a85a1dfa40440160f7910933e899250e8c6409f9 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 21 Feb 2026 20:30:08 +0800
Subject: [PATCH 17/27] address review comments

---
 libcxx/include/__atomic/atomic.h              | 39 ++++++++--
 libcxx/include/__atomic/atomic_ref.h          | 28 +++++++
 libcxx/include/__atomic/support/c11.h         | 16 ++--
 libcxx/include/__atomic/support/gcc.h         | 26 +++----
 libcxx/include/atomic                         |  4 +
 .../atomics/atomics.ref/fetch_max.pass.cpp    | 73 +++++++++++++------
 .../atomics/atomics.ref/fetch_min.pass.cpp    | 73 +++++++++++++------
 .../atomic_fetch_max.pass.cpp                 |  3 +-
 .../atomic_fetch_max_explicit.pass.cpp        |  3 +-
 .../atomic_fetch_min.pass.cpp                 |  3 +-
 .../atomic_fetch_min_explicit.pass.cpp        |  3 +-
 11 files changed, 191 insertions(+), 80 deletions(-)

diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index 455acb8fca2ad..5fb9e0db72f51 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_integral.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/remove_pointer.h>
@@ -322,6 +323,28 @@ struct atomic<_Tp*> : public __atomic_base<_Tp*> {
     return std::__cxx_atomic_fetch_sub(std::addressof(this->__a_), __op, __m);
   }
 
+#if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+#endif
+
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) volatile _NOEXCEPT { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) _NOEXCEPT { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) volatile _NOEXCEPT { return fetch_sub(1); }
@@ -819,13 +842,13 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 // atomic_fetch_max
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
@@ -833,14 +856,14 @@ _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp
 // atomic_fetch_max_explicit
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
@@ -849,13 +872,13 @@ atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 // atomic_fetch_min
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
@@ -863,14 +886,14 @@ _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp
 // atomic_fetch_min_explicit
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 88c7d7dbc5dfb..ec98fab310c7c 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -403,6 +403,34 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_sub(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept {
     return __atomic_fetch_sub(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
   }
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_max)
+    return reinterpret_cast<_Tp*>(__atomic_fetch_max(
+        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+#    else
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old > __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
+#    endif
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_min)
+    return reinterpret_cast<_Tp*>(__atomic_fetch_min(
+        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+#    else
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old < __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
+#    endif
+  }
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) const noexcept { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) const noexcept { return fetch_sub(1); }
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f28b5e461c18a..0d74d55188a3d 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -261,28 +261,28 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 754c60406cbf6..37c38d25012c6 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -260,29 +260,28 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_max)
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret > __pattern ? __ret : __pattern;
+    __value = __ret > __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
 }
 
 template <typename _Tp>
-_LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+_LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_max)
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret > __pattern ? __ret : __pattern;
+    __value = __ret > __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
@@ -290,29 +289,28 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_min)
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret < __pattern ? __ret : __pattern;
+    __value = __ret < __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
 }
 
 template <typename _Tp>
-_LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+_LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_min)
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret < __pattern ? __ret : __pattern;
+    __value = __ret < __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 4cf93b6bfbd85..bcf831fedc192 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -244,6 +244,10 @@ struct atomic<T*>
     T* fetch_add(ptrdiff_t op, memory_order m = memory_order_seq_cst) noexcept;
     T* fetch_sub(ptrdiff_t op, memory_order m = memory_order_seq_cst) volatile noexcept;
     T* fetch_sub(ptrdiff_t op, memory_order m = memory_order_seq_cst) noexcept;
+    T* fetch_max(T*, memory_order = memory_order_seq_cst) volatile noexcept;
+    T* fetch_max(T*, memory_order = memory_order_seq_cst) noexcept;
+    T* fetch_min(T*, memory_order = memory_order_seq_cst) volatile noexcept;
+    T* fetch_min(T*, memory_order = memory_order_seq_cst) noexcept;
 
     T* operator++(int) volatile noexcept;
     T* operator++(int) noexcept;
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 17930c5d97e65..2ba748486d950 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -33,29 +33,56 @@ struct TestDoesNotHaveFetchMax {
 template <typename T>
 struct TestFetchMax {
   void operator()() const {
-    static_assert(std::is_integral_v<T> && has_fetch_max<T>);
-
-    alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
-    std::atomic_ref<T> const a(x);
-
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
-      assert(y == T(1));
-      assert(x == T(2));
-      y = a.fetch_max(T(0));
-      assert(y == T(2));
-      assert(x == T(2));
-      ASSERT_NOEXCEPT(a.fetch_max(T(0)));
-    }
+    static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_max<T>);
+
+    if constexpr (std::is_integral_v<T>) {
+      alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
+        assert(y == T(1));
+        assert(x == T(2));
+        y = a.fetch_max(T(0));
+        assert(y == T(2));
+        assert(x == T(2));
+        ASSERT_NOEXCEPT(a.fetch_max(T(0)));
+      }
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
+        assert(y == T(2));
+        assert(x == T(3));
+        y = a.fetch_max(T(0));
+        assert(y == T(3));
+        assert(x == T(3));
+        ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
+      }
+    } else if constexpr (std::is_pointer_v<T>) {
+      using U = std::remove_pointer_t<T>;
+      U t[9]  = {};
+      alignas(std::atomic_ref<T>::required_alignment) T x{&t[1]};
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(&t[2]);
+        assert(y == &t[1]);
+        assert(x == &t[2]);
+        y = a.fetch_max(&t[0]);
+        assert(y == &t[2]);
+        assert(x == &t[2]);
+        ASSERT_NOEXCEPT(a.fetch_max(&t[0]));
+      }
 
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
-      assert(y == T(2));
-      assert(x == T(3));
-      y = a.fetch_max(T(0));
-      assert(y == T(3));
-      assert(x == T(3));
-      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(&t[3], std::memory_order_relaxed);
+        assert(y == &t[2]);
+        assert(a == &t[3]);
+        y = a.fetch_max(&t[0]);
+        assert(y == &t[3]);
+        assert(x == &t[3]);
+        ASSERT_NOEXCEPT(a.fetch_max(&t[0], std::memory_order_relaxed));
+      }
     }
   }
 };
@@ -65,7 +92,7 @@ int main(int, char**) {
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
 
-  TestEachPointerType<TestDoesNotHaveFetchMax>()();
+  TestEachPointerType<TestFetchMax>()();
 
   TestDoesNotHaveFetchMax<bool>()();
   TestDoesNotHaveFetchMax<UserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index e930d83a2b819..b178996b06085 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -33,29 +33,56 @@ struct TestDoesNotHaveFetchMin {
 template <typename T>
 struct TestFetchMin {
   void operator()() const {
-    static_assert(std::is_integral_v<T> && has_fetch_min<T>);
-
-    alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
-    std::atomic_ref<T> const a(x);
-
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
-      assert(y == T(3));
-      assert(x == T(2));
-      y = a.fetch_min(T(4));
-      assert(y == T(2));
-      assert(x == T(2));
-      ASSERT_NOEXCEPT(a.fetch_min(T(0)));
-    }
+    static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_min<T>);
+
+    if constexpr (std::is_integral_v<T>) {
+      alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
+        assert(y == T(3));
+        assert(x == T(2));
+        y = a.fetch_min(T(4));
+        assert(y == T(2));
+        assert(x == T(2));
+        ASSERT_NOEXCEPT(a.fetch_min(T(0)));
+      }
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
+        assert(y == T(2));
+        assert(x == T(1));
+        y = a.fetch_min(T(4));
+        assert(y == T(1));
+        assert(x == T(1));
+        ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
+      }
+    } else if constexpr (std::is_pointer_v<T>) {
+      using U = std::remove_pointer_t<T>;
+      U t[9]  = {};
+      alignas(std::atomic_ref<T>::required_alignment) T x{&t[3]};
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(&t[2]);
+        assert(y == &t[3]);
+        assert(x == &t[2]);
+        y = a.fetch_min(&t[4]);
+        assert(y == &t[2]);
+        assert(x == &t[2]);
+        ASSERT_NOEXCEPT(a.fetch_min(&t[0]));
+      }
 
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
-      assert(y == T(2));
-      assert(x == T(1));
-      y = a.fetch_min(T(4));
-      assert(y == T(1));
-      assert(x == T(1));
-      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(&t[1], std::memory_order_relaxed);
+        assert(y == &t[2]);
+        assert(a == &t[1]);
+        y = a.fetch_min(&t[4]);
+        assert(y == &t[1]);
+        assert(x == &t[1]);
+        ASSERT_NOEXCEPT(a.fetch_min(&t[0], std::memory_order_relaxed));
+      }
     }
   }
 };
@@ -65,7 +92,7 @@ int main(int, char**) {
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
 
-  TestEachPointerType<TestDoesNotHaveFetchMin>()();
+  TestEachPointerType<TestFetchMin>()();
 
   TestDoesNotHaveFetchMin<bool>()();
   TestDoesNotHaveFetchMin<UserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index e4aba6ebbdd20..a7596b6ff2bf5 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -44,7 +44,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_max(&t, T(2)) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_max(T(2));
+    assert(r == T(3));
     assert(t == T(3));
   }
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 441fb57c3b332..52c4ac137c709 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -47,7 +47,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_max(T(2), std::memory_order_seq_cst);
+    assert(r == T(3));
     assert(t == T(3));
   }
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 3a4a15290b407..1ffc92f3c2300 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -44,7 +44,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_min(&t, T(2)) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_min(T(2));
+    assert(r == T(3));
     assert(t == T(2));
   }
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index fd208162b9c07..f5732b5d977cc 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -47,7 +47,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_min(T(2), std::memory_order_seq_cst);
+    assert(r == T(3));
     assert(t == T(2));
   }
 

>From c3e318d67841e8076a2e99f9c79d64873178dab2 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 21 Feb 2026 21:07:13 +0800
Subject: [PATCH 18/27] header

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp       | 1 +
 .../atomic_fetch_max_explicit.pass.cpp                           | 1 +
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp       | 1 +
 .../atomic_fetch_min_explicit.pass.cpp                           | 1 +
 4 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index a7596b6ff2bf5..dc2274a1056cb 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 52c4ac137c709..d878f6abd246d 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 1ffc92f3c2300..43277f6a7977a 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index f5732b5d977cc..75bf200812ab0 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>

>From 609e1196aa5098cbbd3e27a02d21beb42c98ecfe Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 22 Feb 2026 21:14:58 +0800
Subject: [PATCH 19/27] fix gcc ci

---
 libcxx/include/__atomic/atomic_ref.h | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index ec98fab310c7c..e0f28f4a3e204 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,11 +406,10 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
 #    if __has_builtin(__atomic_fetch_max)
-    return reinterpret_cast<_Tp*>(__atomic_fetch_max(
-        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+    return __atomic_fetch_max(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
 #    else
-    _Tp __old = this->load(memory_order_relaxed);
-    _Tp __new;
+    _Tp* __old = this->load(memory_order_relaxed);
+    _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
@@ -419,11 +418,10 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
 #    if __has_builtin(__atomic_fetch_min)
-    return reinterpret_cast<_Tp*>(__atomic_fetch_min(
-        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+    return __atomic_fetch_min(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
 #    else
-    _Tp __old = this->load(memory_order_relaxed);
-    _Tp __new;
+    _Tp* __old = this->load(memory_order_relaxed);
+    _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));

>From ebd61a9db3e67e3c7caf05b7e364f61f3243d24a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 10:19:34 -0400
Subject: [PATCH 20/27] Don't use builtins for pointer overloads of
 fetch_max/min

`__arg * sizeof(_Tp)` makes no sense in that context since __arg is a
pointer.
---
 libcxx/include/__atomic/atomic_ref.h | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index d681fd7488fcc..0fb141fb1f67a 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,28 +406,22 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_max)
-    return __atomic_fetch_max(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_min)
-    return __atomic_fetch_min(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
 #  endif
 

>From 6190229b39172c8b6e239e72c31e9f6daa6c85d6 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 10:32:46 -0400
Subject: [PATCH 21/27] Fix release note

---
 libcxx/docs/ReleaseNotes/23.rst | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index c99f3b49d902b..d56ec23a763cb 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -38,7 +38,8 @@ What's New in Libc++ 23.0.0?
 Implemented Papers
 ------------------
 
-- P0493R5: Atomic minimum/maximum are supported (`Github <https://llvm.org/PR105418>`__)
+- P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
+- P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
 - P1899R3: ``stride_view`` (`Github <https://llvm.org/PR105198>`__)
 - P3936R1: Safer ``atomic_ref::address`` (`Github <https://llvm.org/PR189594>`__)
 - P3953R3: Rename ``std::runtime_format`` (`Github <https://llvm.org/PR189624>`__)

>From 2853b3870f2bdfe1a6385f1a245866d71ac391f4 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 10:35:42 -0400
Subject: [PATCH 22/27] Revert "Don't use builtins for pointer overloads of
 fetch_max/min"

Try applying frederick's suggestion instead.
---
 libcxx/include/__atomic/atomic_ref.h | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 0fb141fb1f67a..2f5a256698cf3 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,22 +406,28 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
+#    if __has_builtin(__atomic_fetch_max)
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
+#    if __has_builtin(__atomic_fetch_min)
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
 #  endif
 

>From b7ed28f9b8e3be9137393c91be40e29ac1268efa Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 11:11:22 -0400
Subject: [PATCH 23/27] Re-apply "Don't use builtins for pointer overloads of
 fetch_max/min"

It seems that the builtins indeed don't support pointers.
---
 libcxx/include/__atomic/atomic_ref.h | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 2f5a256698cf3..0fb141fb1f67a 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,28 +406,22 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_max)
-    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_min)
-    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
 #  endif
 

>From de6f07f40149a2f41740033a24be77f06919097a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 16:41:43 -0400
Subject: [PATCH 24/27] Port test coverage from #186694

Assisted by Claude, reviewed manually.
---
 .../atomics/atomics.ref/fetch_max.pass.cpp    |  64 +++-----
 .../atomics/atomics.ref/fetch_min.pass.cpp    |  64 +++-----
 .../atomic_fetch_max.pass.cpp                 |  49 ++++--
 .../atomic_fetch_max_explicit.pass.cpp        |  50 ++++--
 .../atomic_fetch_min.pass.cpp                 |  64 ++++----
 .../atomic_fetch_min_explicit.pass.cpp        |  65 ++++----
 libcxx/test/support/atomic_fetch_max_helper.h | 147 ++++++++++++++++++
 libcxx/test/support/atomic_fetch_min_helper.h | 147 ++++++++++++++++++
 8 files changed, 478 insertions(+), 172 deletions(-)
 create mode 100644 libcxx/test/support/atomic_fetch_max_helper.h
 create mode 100644 libcxx/test/support/atomic_fetch_min_helper.h

diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 2ba748486d950..7302eaea570f0 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -10,12 +10,14 @@
 // XFAIL: !has-64-bit-atomics
 
 // integral-type fetch_max(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+// T* fetch_max(T*, memory_order = memory_order::seq_cst) const noexcept;
 
 #include <atomic>
 #include <cassert>
 #include <concepts>
 #include <type_traits>
 
+#include "atomic_fetch_max_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -36,64 +38,38 @@ struct TestFetchMax {
     static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_max<T>);
 
     if constexpr (std::is_integral_v<T>) {
-      alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
+      alignas(std::atomic_ref<T>::required_alignment) T x{};
       std::atomic_ref<T> const a(x);
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
-        assert(y == T(1));
-        assert(x == T(2));
-        y = a.fetch_max(T(0));
-        assert(y == T(2));
-        assert(x == T(2));
-        ASSERT_NOEXCEPT(a.fetch_max(T(0)));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
-        assert(y == T(2));
-        assert(x == T(3));
-        y = a.fetch_max(T(0));
-        assert(y == T(3));
-        assert(x == T(3));
-        ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
-      }
+      auto load  = [&]() { return x; };
+      auto store = [&](T val) { x = val; };
+      auto max   = [&](T val, auto order) { return a.fetch_max(val, order); };
+
+      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_seq_cst));
+      test_fetch_max_integral<T>(load, store, max);
+
     } else if constexpr (std::is_pointer_v<T>) {
       using U = std::remove_pointer_t<T>;
-      U t[9]  = {};
-      alignas(std::atomic_ref<T>::required_alignment) T x{&t[1]};
-      std::atomic_ref<T> const a(x);
+      U arr[5]{};
+      alignas(std::atomic_ref<T>::required_alignment) T p{};
+      std::atomic_ref<T> const a(p);
+
+      auto load  = [&]() { return p; };
+      auto store = [&](T val) { p = val; };
+      auto max   = [&](T val, auto order) { return a.fetch_max(val, order); };
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(&t[2]);
-        assert(y == &t[1]);
-        assert(x == &t[2]);
-        y = a.fetch_max(&t[0]);
-        assert(y == &t[2]);
-        assert(x == &t[2]);
-        ASSERT_NOEXCEPT(a.fetch_max(&t[0]));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(&t[3], std::memory_order_relaxed);
-        assert(y == &t[2]);
-        assert(a == &t[3]);
-        y = a.fetch_max(&t[0]);
-        assert(y == &t[3]);
-        assert(x == &t[3]);
-        ASSERT_NOEXCEPT(a.fetch_max(&t[0], std::memory_order_relaxed));
-      }
+      ASSERT_NOEXCEPT(a.fetch_max(&arr[0], std::memory_order_seq_cst));
+      test_fetch_max_pointer<U>(&arr[0], &arr[2], &arr[4], load, store, max);
     }
   }
 };
 
 int main(int, char**) {
   TestEachIntegralType<TestFetchMax>()();
+  TestEachPointerType<TestFetchMax>()();
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
 
-  TestEachPointerType<TestFetchMax>()();
-
   TestDoesNotHaveFetchMax<bool>()();
   TestDoesNotHaveFetchMax<UserAtomicType>()();
   TestDoesNotHaveFetchMax<LargeUserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index b178996b06085..e7f36363ae3a7 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -10,12 +10,14 @@
 // XFAIL: !has-64-bit-atomics
 
 // integral-type fetch_min(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+// T* fetch_min(T*, memory_order = memory_order::seq_cst) const noexcept;
 
 #include <atomic>
 #include <cassert>
 #include <concepts>
 #include <type_traits>
 
+#include "atomic_fetch_min_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -36,64 +38,38 @@ struct TestFetchMin {
     static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_min<T>);
 
     if constexpr (std::is_integral_v<T>) {
-      alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
+      alignas(std::atomic_ref<T>::required_alignment) T x{};
       std::atomic_ref<T> const a(x);
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
-        assert(y == T(3));
-        assert(x == T(2));
-        y = a.fetch_min(T(4));
-        assert(y == T(2));
-        assert(x == T(2));
-        ASSERT_NOEXCEPT(a.fetch_min(T(0)));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
-        assert(y == T(2));
-        assert(x == T(1));
-        y = a.fetch_min(T(4));
-        assert(y == T(1));
-        assert(x == T(1));
-        ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
-      }
+      auto load  = [&]() { return x; };
+      auto store = [&](T val) { x = val; };
+      auto min   = [&](T val, auto order) { return a.fetch_min(val, order); };
+
+      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_seq_cst));
+      test_fetch_min_integral<T>(load, store, min);
+
     } else if constexpr (std::is_pointer_v<T>) {
       using U = std::remove_pointer_t<T>;
-      U t[9]  = {};
-      alignas(std::atomic_ref<T>::required_alignment) T x{&t[3]};
-      std::atomic_ref<T> const a(x);
+      U arr[5]{};
+      alignas(std::atomic_ref<T>::required_alignment) T p{};
+      std::atomic_ref<T> const a(p);
+
+      auto load  = [&]() { return p; };
+      auto store = [&](T val) { p = val; };
+      auto min   = [&](T val, auto order) { return a.fetch_min(val, order); };
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(&t[2]);
-        assert(y == &t[3]);
-        assert(x == &t[2]);
-        y = a.fetch_min(&t[4]);
-        assert(y == &t[2]);
-        assert(x == &t[2]);
-        ASSERT_NOEXCEPT(a.fetch_min(&t[0]));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(&t[1], std::memory_order_relaxed);
-        assert(y == &t[2]);
-        assert(a == &t[1]);
-        y = a.fetch_min(&t[4]);
-        assert(y == &t[1]);
-        assert(x == &t[1]);
-        ASSERT_NOEXCEPT(a.fetch_min(&t[0], std::memory_order_relaxed));
-      }
+      ASSERT_NOEXCEPT(a.fetch_min(&arr[0], std::memory_order_seq_cst));
+      test_fetch_min_pointer<U>(&arr[0], &arr[2], &arr[4], load, store, min);
     }
   }
 };
 
 int main(int, char**) {
   TestEachIntegralType<TestFetchMin>()();
+  TestEachPointerType<TestFetchMin>()();
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
 
-  TestEachPointerType<TestFetchMin>()();
-
   TestDoesNotHaveFetchMin<bool>()();
   TestDoesNotHaveFetchMin<UserAtomicType>()();
   TestDoesNotHaveFetchMin<LargeUserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index dc2274a1056cb..7b2a5715ddddd 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -19,11 +19,10 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_max_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -35,23 +34,18 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(std::atomic_fetch_max(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
-
-  // atomic_fetch_max
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_max(&t, T(2)) == T(1));
-    assert(t == T(2));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_max(T(2));
-    assert(r == T(3));
-    assert(t == T(3));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto max   = [&](T val, auto) { return std::atomic_fetch_max(&a, val); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_max(&a, T(0)));
+    test_fetch_max_integral<T>(load, store, max);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_max concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_max should leave
+  // the atomic at the global maximum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
@@ -95,8 +89,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto max   = [&](T* val, auto) { return std::atomic_fetch_max(&a, val); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_max(&a, p0));
+  test_fetch_max_pointer<T>(p0, p2, p4, load, store, max);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index d878f6abd246d..e811f114708c8 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -21,11 +21,10 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_max_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -37,24 +36,18 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(
-      std::atomic_fetch_max_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
-
-  // atomic_fetch_max_explicit
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-    assert(t == T(2));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_max(T(2), std::memory_order_seq_cst);
-    assert(r == T(3));
-    assert(t == T(3));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto max   = [&](T val, auto order) { return std::atomic_fetch_max_explicit(&a, val, order); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&a, T(0), std::memory_order_seq_cst));
+    test_fetch_max_integral<T>(load, store, max);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_max_explicit concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_max_explicit should
+  // leave the atomic at the global maximum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
@@ -98,8 +91,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto max   = [&](T* val, auto order) { return std::atomic_fetch_max_explicit(&a, val, order); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&a, p0, std::memory_order_seq_cst));
+  test_fetch_max_pointer<T>(p0, p2, p4, load, store, max);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 43277f6a7977a..df40cc7a92c7b 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -19,11 +19,11 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
+#include <limits>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_min_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -35,34 +35,29 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(std::atomic_fetch_min(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
-
-  // atomic_fetch_min
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_min(&t, T(2)) == T(1));
-    assert(t == T(1));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_min(T(2));
-    assert(r == T(3));
-    assert(t == T(2));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto min   = [&](T val, auto) { return std::atomic_fetch_min(&a, val); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_min(&a, T(0)));
+    test_fetch_min_integral<T>(load, store, min);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_min concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_min should leave
+  // the atomic at the global minimum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
 
-    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+    MaybeVolatile<std::atomic<T>> at(std::numeric_limits<T>::max());
 
     std::vector<std::thread> threads;
     threads.reserve(number_of_threads);
     for (auto i = 0; i < number_of_threads; ++i) {
       threads.push_back(support::make_test_thread([&at, i]() {
-        for (auto j = loop - 1; j >= 0; --j) {
+        for (auto j = 0; j < loop; ++j) {
           std::atomic_fetch_min(&at, T(i * loop + j));
         }
       }));
@@ -72,15 +67,7 @@ void test_impl() {
       thread.join();
     }
 
-    const auto times = [](int n) {
-      T res(n);
-      for (auto i = n - 1; i >= 0; --i) {
-        res = std::min<T>(res, i);
-      }
-      return res;
-    };
-
-    assert(at.load() == times(number_of_threads * loop));
+    assert(at.load() == T(0));
   }
 #endif
 }
@@ -95,8 +82,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto min   = [&](T* val, auto) { return std::atomic_fetch_min(&a, val); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_min(&a, p0));
+  test_fetch_min_pointer<T>(p0, p2, p4, load, store, min);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 75bf200812ab0..2295b942b86de 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -21,11 +21,11 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
+#include <limits>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_min_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -37,35 +37,29 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(
-      std::atomic_fetch_min_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
-
-  // atomic_fetch_min_explicit
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-    assert(t == T(1));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_min(T(2), std::memory_order_seq_cst);
-    assert(r == T(3));
-    assert(t == T(2));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto min   = [&](T val, auto order) { return std::atomic_fetch_min_explicit(&a, val, order); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&a, T(0), std::memory_order_seq_cst));
+    test_fetch_min_integral<T>(load, store, min);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_min_explicit concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_min_explicit should
+  // leave the atomic at the global minimum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
 
-    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+    MaybeVolatile<std::atomic<T>> at(std::numeric_limits<T>::max());
 
     std::vector<std::thread> threads;
     threads.reserve(number_of_threads);
     for (auto i = 0; i < number_of_threads; ++i) {
       threads.push_back(support::make_test_thread([&at, i]() {
-        for (auto j = loop - 1; j >= 0; --j) {
+        for (auto j = 0; j < loop; ++j) {
           std::atomic_fetch_min_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
         }
       }));
@@ -75,15 +69,7 @@ void test_impl() {
       thread.join();
     }
 
-    const auto times = [](int n) {
-      T res(n);
-      for (auto i = n - 1; i >= 0; --i) {
-        res = std::min<T>(res, i);
-      }
-      return res;
-    };
-
-    assert(at.load() == times(number_of_threads * loop));
+    assert(at.load() == T(0));
   }
 #endif
 }
@@ -98,8 +84,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto min   = [&](T* val, auto order) { return std::atomic_fetch_min_explicit(&a, val, order); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&a, p0, std::memory_order_seq_cst));
+  test_fetch_min_pointer<T>(p0, p2, p4, load, store, min);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/support/atomic_fetch_max_helper.h b/libcxx/test/support/atomic_fetch_max_helper.h
new file mode 100644
index 0000000000000..ff4bd7aa38912
--- /dev/null
+++ b/libcxx/test/support/atomic_fetch_max_helper.h
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
+#define TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
+
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Test fetch_max for integral types
+// LoadOp: () -> T - reads current value
+// StoreOp: (T) -> void - stores a value
+// MaxOp: (T, memory_order) -> T - performs fetch_max operation
+template <class T, class LoadOp, class StoreOp, class MaxOp>
+void test_fetch_max_integral(LoadOp load, StoreOp store, MaxOp max) {
+  // Test basic fetch_max (update to larger value)
+  {
+    store(T(10));
+    T old = max(T(20), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(20));
+  }
+
+  // Test with smaller value (no change)
+  {
+    store(T(10));
+    T old = max(T(5), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(10));
+  }
+
+  // Test different memory orderings
+  {
+    store(T(15));
+    T old = max(T(25), std::memory_order_seq_cst);
+    assert(old == T(15));
+    assert(load() == T(25));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(25), std::memory_order_relaxed);
+    assert(old == T(10));
+    assert(load() == T(25));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(30), std::memory_order_acquire);
+    assert(old == T(10));
+    assert(load() == T(30));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(15), std::memory_order_release);
+    assert(old == T(10));
+    assert(load() == T(15));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(22), std::memory_order_acq_rel);
+    assert(old == T(10));
+    assert(load() == T(22));
+  }
+
+  {
+    store(T(15));
+    T old = max(T(35), std::memory_order_relaxed);
+    assert(old == T(15));
+    assert(load() == T(35));
+  }
+
+  // Test return type
+  {
+    store(T(10));
+    static_assert(std::is_same_v<decltype(max(T(20), std::memory_order_seq_cst)), T>);
+  }
+}
+
+// Test fetch_max for pointer types
+// LoadOp: () -> T* - reads current pointer value
+// StoreOp: (T*) -> void - stores a pointer value
+// MaxOp: (T*, memory_order) -> T* - performs fetch_max operation
+template <class T, class LoadOp, class StoreOp, class MaxOp>
+void test_fetch_max_pointer(T* p0, T* p2, T* p4, LoadOp load, StoreOp store, MaxOp max) {
+  // Test basic fetch_max (update to larger pointer)
+  {
+    store(p2);
+    T* old = max(p4, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p4);
+  }
+
+  // Test with smaller pointer (no change)
+  {
+    store(p2);
+    T* old = max(p0, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p2);
+  }
+
+  // Test different memory orderings
+  {
+    store(p0);
+    T* old = max(p2, std::memory_order_seq_cst);
+    assert(old == p0);
+    assert(load() == p2);
+  }
+
+  {
+    store(p0);
+    T* old = max(p2, std::memory_order_relaxed);
+    assert(old == p0);
+    assert(load() == p2);
+  }
+
+  {
+    store(p0);
+    T* old = max(p4, std::memory_order_acquire);
+    assert(old == p0);
+    assert(load() == p4);
+  }
+
+  {
+    store(p0);
+    T* old = max(p4, std::memory_order_release);
+    assert(old == p0);
+    assert(load() == p4);
+  }
+
+  // Test return type
+  {
+    store(p2);
+    static_assert(std::is_same_v<decltype(max(p4, std::memory_order_seq_cst)), T*>);
+  }
+}
+
+#endif // TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
diff --git a/libcxx/test/support/atomic_fetch_min_helper.h b/libcxx/test/support/atomic_fetch_min_helper.h
new file mode 100644
index 0000000000000..20a3558296118
--- /dev/null
+++ b/libcxx/test/support/atomic_fetch_min_helper.h
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H
+#define TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H
+
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Test fetch_min for integral types
+// LoadOp: () -> T - reads current value
+// StoreOp: (T) -> void - stores a value
+// MinOp: (T, memory_order) -> T - performs fetch_min operation
+template <class T, class LoadOp, class StoreOp, class MinOp>
+void test_fetch_min_integral(LoadOp load, StoreOp store, MinOp min) {
+  // Test basic fetch_min (update to smaller value)
+  {
+    store(T(10));
+    T old = min(T(5), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(5));
+  }
+
+  // Test with larger value (no change)
+  {
+    store(T(10));
+    T old = min(T(20), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(10));
+  }
+
+  // Test different memory orderings
+  {
+    store(T(15));
+    T old = min(T(8), std::memory_order_seq_cst);
+    assert(old == T(15));
+    assert(load() == T(8));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(3), std::memory_order_relaxed);
+    assert(old == T(10));
+    assert(load() == T(3));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(2), std::memory_order_acquire);
+    assert(old == T(10));
+    assert(load() == T(2));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(7), std::memory_order_release);
+    assert(old == T(10));
+    assert(load() == T(7));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(4), std::memory_order_acq_rel);
+    assert(old == T(10));
+    assert(load() == T(4));
+  }
+
+  {
+    store(T(15));
+    T old = min(T(6), std::memory_order_relaxed);
+    assert(old == T(15));
+    assert(load() == T(6));
+  }
+
+  // Test return type
+  {
+    store(T(10));
+    static_assert(std::is_same_v<decltype(min(T(5), std::memory_order_seq_cst)), T>);
+  }
+}
+
+// Test fetch_min for pointer types
+// LoadOp: () -> T* - reads current pointer value
+// StoreOp: (T*) -> void - stores a pointer value
+// MinOp: (T*, memory_order) -> T* - performs fetch_min operation
+template <class T, class LoadOp, class StoreOp, class MinOp>
+void test_fetch_min_pointer(T* p0, T* p2, T* p4, LoadOp load, StoreOp store, MinOp min) {
+  // Test basic fetch_min (update to smaller pointer)
+  {
+    store(p2);
+    T* old = min(p0, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p0);
+  }
+
+  // Test with larger pointer (no change)
+  {
+    store(p2);
+    T* old = min(p4, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p2);
+  }
+
+  // Test different memory orderings
+  {
+    store(p4);
+    T* old = min(p2, std::memory_order_seq_cst);
+    assert(old == p4);
+    assert(load() == p2);
+  }
+
+  {
+    store(p4);
+    T* old = min(p2, std::memory_order_relaxed);
+    assert(old == p4);
+    assert(load() == p2);
+  }
+
+  {
+    store(p4);
+    T* old = min(p0, std::memory_order_acquire);
+    assert(old == p4);
+    assert(load() == p0);
+  }
+
+  {
+    store(p4);
+    T* old = min(p0, std::memory_order_release);
+    assert(old == p4);
+    assert(load() == p0);
+  }
+
+  // Test return type
+  {
+    store(p2);
+    static_assert(std::is_same_v<decltype(min(p0, std::memory_order_seq_cst)), T*>);
+  }
+}
+
+#endif // TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H

>From f8b41b52f67d6ca6dd03617efb6d56ec1c20c6ae Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 16 Jun 2026 07:25:18 -0400
Subject: [PATCH 25/27] Use a CAS loop for pointer types in
 __cxx_atomic_fetch_{max,min} on c11.h

Clang's __c11_atomic_fetch_max/min builtins reject pointer arguments
("address argument to atomic operation must be a pointer to atomic integer
or supported floating point type"). The four __cxx_atomic_fetch_{max,min}
overloads in support/c11.h were unconditionally calling those builtins, so
std::atomic<T*>::fetch_max/min failed to compile on every Clang-based
config. support/gcc.h already had this dispatch via #if __has_builtin(...)
falling back to a CAS loop, which is why generic-gcc was passing.

Dispatch on is_pointer<_Tp>::value: pointer types use a CAS loop matching
gcc.h's body, others keep calling the builtin.

Assisted by Claude
---
 libcxx/include/__atomic/support/c11.h | 56 +++++++++++++++++++++++----
 1 file changed, 48 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0d74d55188a3d..603ffc690b104 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -13,6 +13,7 @@
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/remove_const.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -259,30 +260,69 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
       std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
 }
 
+// Clang's __c11_atomic_fetch_max/min builtins do not accept pointer
+// arguments, so dispatch to a CAS loop for pointer types.
+// TODO: Use the builtin for pointer types once Clang accepts them.
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret > __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_max(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret > __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_max(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret < __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_min(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret < __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_min(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 
 _LIBCPP_END_NAMESPACE_STD

>From 77d8902e33364fe5887036f6dc36550094841d7b Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 16 Jun 2026 10:06:33 -0400
Subject: [PATCH 26/27] Switch __cxx_atomic_fetch_{max,min} pointer dispatch to
 SFINAE for C++03 compat

I know they're not needed outside of C++26, but this header should be
usable in all standard modes in principle.
---
 libcxx/include/__atomic/support/c11.h | 103 ++++++++++++++------------
 1 file changed, 55 insertions(+), 48 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 603ffc690b104..58a97fcfb3261 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -13,6 +13,7 @@
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/remove_const.h>
 
@@ -263,66 +264,72 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 // Clang's __c11_atomic_fetch_max/min builtins do not accept pointer
 // arguments, so dispatch to a CAS loop for pointer types.
 // TODO: Use the builtin for pointer types once Clang accepts them.
-template <class _Tp>
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret > __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_max(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret > __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+}
+
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret > __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_max(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret > __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret < __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_min(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret < __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+}
+
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret < __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_min(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret < __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 _LIBCPP_END_NAMESPACE_STD

>From 269691062efd541709388ab2b330e91b9b94cf91 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 26 Jun 2026 21:35:32 +0800
Subject: [PATCH 27/27] eliminate adl warning

---
 libcxx/include/__atomic/support/c11.h | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 58a97fcfb3261..899502422570a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -273,11 +273,12 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, mem
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -290,11 +291,12 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -307,11 +309,12 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, mem
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -324,11 +327,12 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 


From libcxx-commits at lists.llvm.org  Mon Jul 20 05:13:41 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Mon, 20 Jul 2026 05:13:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0493R5: Atomic
	minimum/maximum (PR #180333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/180333@github.com>
Message-ID: <6a5e10f5.5a0412d2.3daf32.f9f8@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/180333

>From ab79abbd1c14a305de4c16b791bf5e22384d543a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 17:10:11 +0800
Subject: [PATCH 01/28] implement atomic max/min

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/ReleaseNotes/23.rst               |  1 +
 libcxx/docs/Status/Cxx2cPapers.csv            |  2 +-
 libcxx/include/__atomic/atomic.h              | 68 ++++++++++++++++++
 libcxx/include/__atomic/atomic_ref.h          |  8 +++
 libcxx/include/__atomic/support.h             |  9 +++
 libcxx/include/__atomic/support/c11.h         | 26 +++++++
 libcxx/include/__atomic/support/gcc.h         | 24 +++++++
 libcxx/include/atomic                         | 24 +++++++
 libcxx/include/version                        |  2 +-
 libcxx/modules/std/atomic.inc                 |  4 ++
 .../atomics/atomics.ref/fetch_max.pass.cpp    | 70 +++++++++++++++++++
 .../atomics/atomics.ref/fetch_min.pass.cpp    | 70 +++++++++++++++++++
 .../atomic_fetch_max.pass.cpp                 | 55 +++++++++++++++
 .../atomic_fetch_max_explicit.pass.cpp        | 57 +++++++++++++++
 .../atomic_fetch_min.pass.cpp                 | 55 +++++++++++++++
 .../atomic_fetch_min_explicit.pass.cpp        | 57 +++++++++++++++
 .../atomic.version.compile.pass.cpp           | 16 ++---
 .../version.version.compile.pass.cpp          | 16 ++---
 .../generate_feature_test_macro_components.py |  1 -
 20 files changed, 541 insertions(+), 26 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index d2b76cb96e866..701775e051f9a 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -416,7 +416,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_associative_heterogeneous_insertion``          *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_atomic_min_max``                               *unimplemented*
+    ``__cpp_lib_atomic_min_max``                               ``202403L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_atomic_ref``                                   ``202411L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index a1d62a367f33c..ce2eee5ed852a 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P2440R1 (partial): ``ranges::iota`` and ``ranges::shift_left`` are supported (`Github <https://llvm.org/PR105184>`__)
+- P0493R5: Atomic minimum/maximum are supported (`Github <https://llvm.org/PR105418>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 29642fc53cac6..50819f1b47a27 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -53,7 +53,7 @@
 "`P3107R5 <https://wg21.link/P3107R5>`__","Permit an efficient implementation of ``std::print``","2024-03 (Tokyo)","","","`#105414 <https://github.com/llvm/llvm-project/issues/105414>`__",""
 "`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)"
 "`P2845R8 <https://wg21.link/P2845R8>`__","Formatting of ``std::filesystem::path``","2024-03 (Tokyo)","","","`#105416 <https://github.com/llvm/llvm-project/issues/105416>`__",""
-"`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","","","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
+"`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","|Complete|","23","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
 "`P2542R8 <https://wg21.link/P2542R8>`__","``views::concat``","2024-03 (Tokyo)","","","`#105419 <https://github.com/llvm/llvm-project/issues/105419>`__",""
 "`P2591R5 <https://wg21.link/P2591R5>`__","Concatenation of strings and string views","2024-03 (Tokyo)","|Complete|","19","`#105420 <https://github.com/llvm/llvm-project/issues/105420>`__",""
 "`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","`#105421 <https://github.com/llvm/llvm-project/issues/105421>`__",""
diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index ea1d28fbf850a..f1c7492590d61 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -178,6 +178,20 @@ struct __atomic_base<_Tp, true> : public __atomic_base<_Tp, false> {
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
     return std::__cxx_atomic_fetch_xor(std::addressof(this->__a_), __op, __m);
   }
+#if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+#endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) volatile _NOEXCEPT { return fetch_add(_Tp(1)); }
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) _NOEXCEPT { return fetch_add(_Tp(1)); }
@@ -799,6 +813,60 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
   return __o->fetch_xor(__op, __m);
 }
 
+#if _LIBCPP_STD_VER >= 26
+// atomic_fetch_max
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_max(__op);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_max(__op);
+}
+
+// atomic_fetch_max_explicit
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
+    volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_max(__op, __m);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_max(__op, __m);
+}
+
+// atomic_fetch_min
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_min(__op);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_min(__op);
+}
+
+// atomic_fetch_min_explicit
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
+    volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_min(__op, __m);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_min(__op, __m);
+}
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ATOMIC_ATOMIC_H
diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 69edbfe6ecadd..cedd4248eb200 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -299,6 +299,14 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
     return __atomic_fetch_xor(this->__ptr_, __arg, std::__to_gcc_order(__order));
   }
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+  }
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) const noexcept { return fetch_add(_Tp(1)); }
   _LIBCPP_HIDE_FROM_ABI _Tp operator--(int) const noexcept { return fetch_sub(_Tp(1)); }
diff --git a/libcxx/include/__atomic/support.h b/libcxx/include/__atomic/support.h
index 99d0f6aa543ca..eda38705acd32 100644
--- a/libcxx/include/__atomic/support.h
+++ b/libcxx/include/__atomic/support.h
@@ -99,6 +99,15 @@
 // template <class _Tp>
 // _Tp __cxx_atomic_fetch_xor(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
 //
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_max(_Atmc<_Tp> volatile* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_max(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_min(_Atmc<_Tp> volatile* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_min(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
+//
 // clang-format on
 //
 
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f28b5e461c18a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -259,6 +259,32 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
       std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
 }
 
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ATOMIC_SUPPORT_C11_H
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..dfaebb68ced74 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -258,6 +258,30 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   return __atomic_fetch_xor(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
 }
 
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 23a3db5d35029..4cf93b6bfbd85 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -164,6 +164,10 @@ struct atomic<integral>
     integral fetch_or(integral op, memory_order m = memory_order_seq_cst) noexcept;
     integral fetch_xor(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
     integral fetch_xor(integral op, memory_order m = memory_order_seq_cst) noexcept;
+    integral fetch_max(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
+    integral fetch_max(integral op, memory_order m = memory_order_seq_cst) noexcept;
+    integral fetch_min(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
+    integral fetch_min(integral op, memory_order m = memory_order_seq_cst) noexcept;
 
     integral operator++(int) volatile noexcept;
     integral operator++(int) noexcept;
@@ -441,6 +445,26 @@ template<class T>
 template<class T>
   T atomic_fetch_xor_explicit(atomic<T>*, atomic<T>::value_type,
                               memory_order) noexcept;
+template<class T>
+  T atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
 
 template<class T>
   void atomic_wait(const volatile atomic<T>*, atomic<T>::value_type) noexcept; // since C++20
diff --git a/libcxx/include/version b/libcxx/include/version
index 7d77677a012ce..a00e9441202bd 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -546,7 +546,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #if _LIBCPP_STD_VER >= 26
 # define __cpp_lib_aligned_accessor                     202411L
 // # define __cpp_lib_associative_heterogeneous_insertion  202306L
-// # define __cpp_lib_atomic_min_max                       202403L
+# define __cpp_lib_atomic_min_max                       202403L
 # undef  __cpp_lib_atomic_ref
 # define __cpp_lib_atomic_ref                           202411L
 # undef  __cpp_lib_bind_front
diff --git a/libcxx/modules/std/atomic.inc b/libcxx/modules/std/atomic.inc
index 9a30fb7affe71..11f77991068c1 100644
--- a/libcxx/modules/std/atomic.inc
+++ b/libcxx/modules/std/atomic.inc
@@ -50,6 +50,10 @@ export namespace std {
   using std::atomic_fetch_sub_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor_explicit _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_max _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_max_explicit _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_min _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_min_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_all _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_one _LIBCPP_USING_IF_EXISTS;
   using std::atomic_wait _LIBCPP_USING_IF_EXISTS;
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
new file mode 100644
index 0000000000000..6c1b69dce1595
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// integral-type fetch_max(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+template <typename T>
+concept has_fetch_max = requires {
+  std::declval<T const>().fetch_max(std::declval<T>());
+  std::declval<T const>().fetch_max(std::declval<T>(), std::declval<std::memory_order>());
+};
+
+template <typename T>
+struct TestDoesNotHaveFetchMax {
+  void operator()() const { static_assert(!has_fetch_max<std::atomic_ref<T>>); }
+};
+
+template <typename T>
+struct TestFetchMax {
+  void operator()() const {
+    static_assert(std::is_integral_v<T>);
+
+    alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
+    std::atomic_ref<T> const a(x);
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
+      assert(y == T(1));
+      assert(x == T(2));
+      ASSERT_NOEXCEPT(a.fetch_max(T(0)));
+    }
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_max(T(1), std::memory_order_relaxed);
+      assert(y == T(2));
+      assert(x == T(2));
+      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFetchMax>()();
+
+  TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
+
+  TestEachPointerType<TestDoesNotHaveFetchMax>()();
+
+  TestDoesNotHaveFetchMax<bool>()();
+  TestDoesNotHaveFetchMax<UserAtomicType>()();
+  TestDoesNotHaveFetchMax<LargeUserAtomicType>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
new file mode 100644
index 0000000000000..1fa2d32c4d7d8
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// integral-type fetch_min(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+template <typename T>
+concept has_fetch_min = requires {
+  std::declval<T const>().fetch_min(std::declval<T>());
+  std::declval<T const>().fetch_min(std::declval<T>(), std::declval<std::memory_order>());
+};
+
+template <typename T>
+struct TestDoesNotHaveFetchMin {
+  void operator()() const { static_assert(!has_fetch_min<std::atomic_ref<T>>); }
+};
+
+template <typename T>
+struct TestFetchMin {
+  void operator()() const {
+    static_assert(std::is_integral_v<T>);
+
+    alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
+    std::atomic_ref<T> const a(x);
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
+      assert(y == T(3));
+      assert(x == T(2));
+      ASSERT_NOEXCEPT(a.fetch_min(T(0)));
+    }
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
+      assert(y == T(2));
+      assert(x == T(1));
+      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFetchMin>()();
+
+  TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
+
+  TestEachPointerType<TestDoesNotHaveFetchMin>()();
+
+  TestDoesNotHaveFetchMin<bool>()();
+  TestDoesNotHaveFetchMin<UserAtomicType>()();
+  TestDoesNotHaveFetchMin<LargeUserAtomicType>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
new file mode 100644
index 0000000000000..25411767ca8ad
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_max(&t, T(2)) == T(1));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_max(&t, T(2)) == T(3));
+      assert(t == T(3));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
new file mode 100644
index 0000000000000..a670f1c10ce60
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+      assert(t == T(3));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
new file mode 100644
index 0000000000000..242355120c28d
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_min(&t, T(2)) == T(1));
+      assert(t == T(1));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_min(&t, T(2)) == T(3));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
new file mode 100644
index 0000000000000..46b7531b1f727
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+      assert(t == T(1));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
index 8bd027445c85b..9e620317c35c8 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
@@ -339,17 +339,11 @@
 #    error "__cpp_lib_atomic_lock_free_type_aliases should have the value 201907L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should be defined in c++26"
-#    endif
-#    if __cpp_lib_atomic_min_max != 202403L
-#      error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_atomic_min_max
+#    error "__cpp_lib_atomic_min_max should be defined in c++26"
+#  endif
+#  if __cpp_lib_atomic_min_max != 202403L
+#    error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_atomic_ref
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 f442b22c6d752..33f402b6422f6 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
@@ -6325,17 +6325,11 @@
 #    error "__cpp_lib_atomic_lock_free_type_aliases should have the value 201907L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should be defined in c++26"
-#    endif
-#    if __cpp_lib_atomic_min_max != 202403L
-#      error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_atomic_min_max
+#    error "__cpp_lib_atomic_min_max should be defined in c++26"
+#  endif
+#  if __cpp_lib_atomic_min_max != 202403L
+#    error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_atomic_ref
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 88431c76a00f5..799bb088f061e 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -191,7 +191,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_atomic_min_max",
             "values": {"c++26": 202403}, # P0493R5: Atomic minimum/maximum
             "headers": ["atomic"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_atomic_ref",

>From fb39fd535b1714f6d422d9b0f1810102a763a1b3 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 19:20:50 +0800
Subject: [PATCH 02/28] fix gcc build

---
 libcxx/include/__atomic/support/gcc.h | 28 +++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index dfaebb68ced74..e50143bb2bfa9 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -261,25 +261,45 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret > __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret > __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret < __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret < __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)

>From 906a95e15e4bdd1136be7bfc1f14731e6a3a3135 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 19:36:17 +0800
Subject: [PATCH 03/28] fix gcc atomic_ref

---
 libcxx/include/__atomic/atomic_ref.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index cedd4248eb200..6ea270dc3b902 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -23,6 +23,7 @@
 #include <__atomic/check_memory_order.h>
 #include <__atomic/floating_point_helper.h>
 #include <__atomic/memory_order.h>
+#include <__atomic/support.h>
 #include <__atomic/to_gcc_order.h>
 #include <__concepts/arithmetic.h>
 #include <__concepts/same_as.h>
@@ -301,10 +302,10 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+    return std::__cxx_atomic_fetch_max(this->__ptr_, __arg, __m);
   }
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+    return std::__cxx_atomic_fetch_min(this->__ptr_, __arg, __m);
   }
 #  endif
 

>From a64208d9c117ccbd5a4cc5677a662b6e4f972f59 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 19:55:43 +0800
Subject: [PATCH 04/28] fix gcc

---
 libcxx/include/__atomic/atomic_ref.h  | 14 ++++++++++++--
 libcxx/include/__atomic/support/gcc.h |  8 ++++----
 2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 6ea270dc3b902..11d74f71e6de1 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -302,10 +302,20 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return std::__cxx_atomic_fetch_max(this->__ptr_, __arg, __m);
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old > __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
   }
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return std::__cxx_atomic_fetch_min(this->__ptr_, __arg, __m);
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old < __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
   }
 #  endif
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index e50143bb2bfa9..c224bb6c95391 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -261,7 +261,7 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
@@ -272,7 +272,7 @@ __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
@@ -283,7 +283,7 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
@@ -294,7 +294,7 @@ __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;

>From 5eeeeea250032dd5ae65ec4a1868158cefe3c650 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 20:12:55 +0800
Subject: [PATCH 05/28] fix gcc

---
 libcxx/include/__atomic/support/gcc.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index c224bb6c95391..3eaccf95e19d8 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -265,7 +265,7 @@ __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -276,7 +276,7 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -287,7 +287,7 @@ __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -298,7 +298,7 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 

>From 46257efd512cc8bd741cc9dd3157b59ab1ce1ca1 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 20:39:31 +0800
Subject: [PATCH 06/28] remove unused include

---
 libcxx/include/__atomic/atomic_ref.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 11d74f71e6de1..84b9bf211b4b9 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -23,7 +23,6 @@
 #include <__atomic/check_memory_order.h>
 #include <__atomic/floating_point_helper.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support.h>
 #include <__atomic/to_gcc_order.h>
 #include <__concepts/arithmetic.h>
 #include <__concepts/same_as.h>

>From dc1cb331ddcb980cce4a8deeef8d1c21b08b0eb7 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 21:29:39 +0800
Subject: [PATCH 07/28] guard modules

---
 libcxx/modules/std/atomic.inc | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/modules/std/atomic.inc b/libcxx/modules/std/atomic.inc
index 11f77991068c1..38321e1a1bb64 100644
--- a/libcxx/modules/std/atomic.inc
+++ b/libcxx/modules/std/atomic.inc
@@ -50,10 +50,12 @@ export namespace std {
   using std::atomic_fetch_sub_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor_explicit _LIBCPP_USING_IF_EXISTS;
+#if _LIBCPP_STD_VER >= 26
   using std::atomic_fetch_max _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_max_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_min _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_min_explicit _LIBCPP_USING_IF_EXISTS;
+#endif
   using std::atomic_notify_all _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_one _LIBCPP_USING_IF_EXISTS;
   using std::atomic_wait _LIBCPP_USING_IF_EXISTS;

>From dfce3c7d45c1dca9e1ac85b293321ef19e007b23 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 8 Feb 2026 19:37:33 +0800
Subject: [PATCH 08/28] address review comments: use builtin if possible

---
 libcxx/include/__atomic/atomic_ref.h | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 84b9bf211b4b9..88c7d7dbc5dfb 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -301,20 +301,28 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_max)
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp __old = this->load(memory_order_relaxed);
     _Tp __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_min)
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp __old = this->load(memory_order_relaxed);
     _Tp __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
 #  endif
 

>From a3cabfda1b316f2514df394232a4e504f38159eb Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 8 Feb 2026 23:12:48 +0800
Subject: [PATCH 09/28] address review comments: use builtin if possible

---
 libcxx/include/__atomic/support/gcc.h | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 3eaccf95e19d8..754c60406cbf6 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -261,45 +261,61 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_max)
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_max)
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_min)
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_min)
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)

>From f01144d60576864032d1204a7662ddcb9540059a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 9 Feb 2026 20:33:05 +0800
Subject: [PATCH 10/28] codestyle

---
 libcxx/include/__atomic/atomic.h              | 24 ++++++++++++-------
 .../atomic_fetch_max.pass.cpp                 | 13 ++++------
 .../atomic_fetch_max_explicit.pass.cpp        | 15 ++++--------
 .../atomic_fetch_min.pass.cpp                 | 13 ++++------
 .../atomic_fetch_min_explicit.pass.cpp        | 15 ++++--------
 5 files changed, 34 insertions(+), 46 deletions(-)

diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index f1c7492590d61..e4be21cfd2d84 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -816,25 +816,29 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 #if _LIBCPP_STD_VER >= 26
 // atomic_fetch_max
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
 
 // atomic_fetch_max_explicit
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
@@ -842,25 +846,29 @@ atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 
 // atomic_fetch_min
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
 
 // atomic_fetch_min_explicit
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 25411767ca8ad..8aa66f68a23d7 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -12,12 +12,9 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
-//
+//   T atomic_fetch_max(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
 // template<class T>
-//     T
-//     atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+//   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -30,16 +27,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_max(&t, T(2)) == T(1));
       assert(t == T(2));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_max(&t, T(2)) == T(3));
       assert(t == T(3));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index a670f1c10ce60..1d04bb737fe1f 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -12,14 +12,11 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//   T atomic_fetch_max_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
 //                               memory_order) noexcept;
-//
 // template<class T>
-//     T
-//     atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
-//                               memory_order) noexcept;
+//   constexpr T atomic_fetch_max_explicit(atomic<T>*, typename atomic<T>::value_type,
+//                                         memory_order) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -32,16 +29,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
       assert(t == T(2));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
       assert(t == T(3));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 242355120c28d..43b979144b19b 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -12,12 +12,9 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
-//
+//   T atomic_fetch_min(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
 // template<class T>
-//     T
-//     atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+//   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -30,16 +27,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_min(&t, T(2)) == T(1));
       assert(t == T(1));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_min(&t, T(2)) == T(3));
       assert(t == T(2));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 46b7531b1f727..309489f91f126 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -12,14 +12,11 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//   T atomic_fetch_min_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
 //                               memory_order) noexcept;
-//
 // template<class T>
-//     T
-//     atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
-//                               memory_order) noexcept;
+//   constexpr T atomic_fetch_min_explicit(atomic<T>*, typename atomic<T>::value_type,
+//                                         memory_order) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -32,16 +29,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
       assert(t == T(1));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
       assert(t == T(2));
 

>From 4a4735ee1ec8f356a2c73570d289beb93dce9354 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 9 Feb 2026 20:46:52 +0800
Subject: [PATCH 11/28] ci

---
 libcxx/include/__atomic/atomic.h                       |  2 ++
 libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp | 10 ++++++++--
 libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp |  6 ++++++
 3 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index e4be21cfd2d84..455acb8fca2ad 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -16,6 +16,8 @@
 #include <__atomic/is_always_lock_free.h>
 #include <__atomic/memory_order.h>
 #include <__atomic/support.h>
+#include <__concepts/arithmetic.h>
+#include <__concepts/same_as.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 6c1b69dce1595..6e96baaaf6e2c 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -43,13 +43,19 @@ struct TestFetchMax {
       std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
       assert(y == T(1));
       assert(x == T(2));
+      y = a.fetch_max(T(0));
+      assert(y == T(2));
+      assert(x == T(2));
       ASSERT_NOEXCEPT(a.fetch_max(T(0)));
     }
 
     {
-      std::same_as<T> decltype(auto) y = a.fetch_max(T(1), std::memory_order_relaxed);
+      std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
       assert(y == T(2));
-      assert(x == T(2));
+      assert(x == T(3));
+      y = a.fetch_max(T(0));
+      assert(y == T(3));
+      assert(x == T(3));
       ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
     }
   }
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index 1fa2d32c4d7d8..92b7f1ed39cb1 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -43,6 +43,9 @@ struct TestFetchMin {
       std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
       assert(y == T(3));
       assert(x == T(2));
+      y = a.fetch_min(T(4));
+      assert(y == T(2));
+      assert(x == T(2));
       ASSERT_NOEXCEPT(a.fetch_min(T(0)));
     }
 
@@ -50,6 +53,9 @@ struct TestFetchMin {
       std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
       assert(y == T(2));
       assert(x == T(1));
+      y = a.fetch_min(T(4));
+      assert(y == T(1));
+      assert(x == T(1));
       ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
     }
   }

>From cfce0bec4a759eea1786642fd3a309032aecb2fc Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 15:26:50 +0800
Subject: [PATCH 12/28] revise testcases

---
 .../atomics/atomics.ref/fetch_max.pass.cpp    | 11 ++-
 .../atomics/atomics.ref/fetch_min.pass.cpp    | 11 ++-
 .../atomic_fetch_max.pass.cpp                 | 85 +++++++++++++++---
 .../atomic_fetch_max_explicit.pass.cpp        | 86 ++++++++++++++++---
 .../atomic_fetch_min.pass.cpp                 | 85 +++++++++++++++---
 .../atomic_fetch_min_explicit.pass.cpp        | 86 ++++++++++++++++---
 6 files changed, 296 insertions(+), 68 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 6e96baaaf6e2c..17930c5d97e65 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -15,26 +15,25 @@
 #include <cassert>
 #include <concepts>
 #include <type_traits>
-#include <utility>
 
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
 template <typename T>
-concept has_fetch_max = requires {
-  std::declval<T const>().fetch_max(std::declval<T>());
-  std::declval<T const>().fetch_max(std::declval<T>(), std::declval<std::memory_order>());
+concept has_fetch_max = requires(std::atomic_ref<T> const& a, T v) {
+  { a.fetch_max(v) } -> std::same_as<T>;
+  { a.fetch_max(v, std::memory_order::relaxed) } -> std::same_as<T>;
 };
 
 template <typename T>
 struct TestDoesNotHaveFetchMax {
-  void operator()() const { static_assert(!has_fetch_max<std::atomic_ref<T>>); }
+  void operator()() const { static_assert(!has_fetch_max<T>); }
 };
 
 template <typename T>
 struct TestFetchMax {
   void operator()() const {
-    static_assert(std::is_integral_v<T>);
+    static_assert(std::is_integral_v<T> && has_fetch_max<T>);
 
     alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
     std::atomic_ref<T> const a(x);
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index 92b7f1ed39cb1..e930d83a2b819 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -15,26 +15,25 @@
 #include <cassert>
 #include <concepts>
 #include <type_traits>
-#include <utility>
 
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
 template <typename T>
-concept has_fetch_min = requires {
-  std::declval<T const>().fetch_min(std::declval<T>());
-  std::declval<T const>().fetch_min(std::declval<T>(), std::declval<std::memory_order>());
+concept has_fetch_min = requires(std::atomic_ref<T> const& a, T v) {
+  { a.fetch_min(v) } -> std::same_as<T>;
+  { a.fetch_min(v, std::memory_order::relaxed) } -> std::same_as<T>;
 };
 
 template <typename T>
 struct TestDoesNotHaveFetchMin {
-  void operator()() const { static_assert(!has_fetch_min<std::atomic_ref<T>>); }
+  void operator()() const { static_assert(!has_fetch_min<T>); }
 };
 
 template <typename T>
 struct TestFetchMin {
   void operator()() const {
-    static_assert(std::is_integral_v<T>);
+    static_assert(std::is_integral_v<T> && has_fetch_min<T>);
 
     alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
     std::atomic_ref<T> const a(x);
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 8aa66f68a23d7..cbd8f147f5931 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -17,28 +20,82 @@
 //   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_max(&t, T(2)) == T(1));
-      assert(t == T(2));
+concept HasVolatileFetchMax = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_max(&a, t) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMax<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(std::atomic_fetch_max(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
+
+  // atomic_fetch_max
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_max(&t, T(2)) == T(1));
+    assert(t == T(2));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_max(&t, T(2)) == T(3));
+    assert(t == T(3));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_max concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(0);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = 0; j < loop; ++j) {
+          std::atomic_fetch_max(&at, T(i * loop + j));
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_max(&t, T(2)) == T(3));
-      assert(t == T(3));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(0);
+      for (auto i = 0; i < n; ++i) {
+        res = std::max<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 1d04bb737fe1f..ddae555670f17 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -19,28 +22,83 @@
 //                                         memory_order) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-      assert(t == T(2));
+concept HasVolatileFetchMaxExplicit = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_max_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMaxExplicit<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(
+      std::atomic_fetch_max_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
+
+  // atomic_fetch_max_explicit
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+    assert(t == T(2));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    assert(t == T(3));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_max_explicit concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(0);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = 0; j < loop; ++j) {
+          std::atomic_fetch_max_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
-      assert(t == T(3));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(0);
+      for (auto i = 0; i < n; ++i) {
+        res = std::max<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 43b979144b19b..33ca27f3de262 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -17,28 +20,82 @@
 //   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_min(&t, T(2)) == T(1));
-      assert(t == T(1));
+concept HasVolatileFetchMin = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_min(&a, t) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMin<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(std::atomic_fetch_min(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
+
+  // atomic_fetch_min
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_min(&t, T(2)) == T(1));
+    assert(t == T(1));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_min(&t, T(2)) == T(3));
+    assert(t == T(2));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_min concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = loop - 1; j >= 0; --j) {
+          std::atomic_fetch_min(&at, T(i * loop + j));
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_min(&t, T(2)) == T(3));
-      assert(t == T(2));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(n);
+      for (auto i = n - 1; i >= 0; --i) {
+        res = std::min<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 309489f91f126..99d20c0c544fb 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -19,28 +22,83 @@
 //                                         memory_order) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-      assert(t == T(1));
+concept HasVolatileFetchMinExplicit = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_min_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMinExplicit<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(
+      std::atomic_fetch_min_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
+
+  // atomic_fetch_min_explicit
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+    assert(t == T(1));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    assert(t == T(2));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_min_explicit concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = loop - 1; j >= 0; --j) {
+          std::atomic_fetch_min_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
-      assert(t == T(2));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(n);
+      for (auto i = n - 1; i >= 0; --i) {
+        res = std::min<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };

>From 71961be2c3c55d2ff0f0224ada38814fe004c972 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 15:42:24 +0800
Subject: [PATCH 13/28] fix ci for aix

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp  | 6 ------
 .../atomic_fetch_max_explicit.pass.cpp                      | 6 ------
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp  | 6 ------
 .../atomic_fetch_min_explicit.pass.cpp                      | 6 ------
 4 files changed, 24 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index cbd8f147f5931..6f89cd719a2ef 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -33,14 +33,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMax = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_max(&a, t) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMax<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(std::atomic_fetch_max(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
 
   // atomic_fetch_max
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index ddae555670f17..931c5a482ed58 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -35,14 +35,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMaxExplicit = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_max_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMaxExplicit<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(
       std::atomic_fetch_max_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 33ca27f3de262..e7a4d4e7bc798 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -33,14 +33,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMin = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_min(&a, t) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMin<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(std::atomic_fetch_min(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
 
   // atomic_fetch_min
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 99d20c0c544fb..acf47f0d89567 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -35,14 +35,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMinExplicit = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_min_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMinExplicit<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(
       std::atomic_fetch_min_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
 

>From 8844e1581d89f537673084b04236710aa1a41ec5 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 15:48:50 +0800
Subject: [PATCH 14/28] fix modules build

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp       | 1 +
 .../atomic_fetch_max_explicit.pass.cpp                           | 1 +
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp       | 1 +
 .../atomic_fetch_min_explicit.pass.cpp                           | 1 +
 4 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 6f89cd719a2ef..93bd767cbab35 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -19,6 +19,7 @@
 // template<class T>
 //   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 931c5a482ed58..7fb2a702be337 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -21,6 +21,7 @@
 //   constexpr T atomic_fetch_max_explicit(atomic<T>*, typename atomic<T>::value_type,
 //                                         memory_order) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index e7a4d4e7bc798..a5adda9487897 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -19,6 +19,7 @@
 // template<class T>
 //   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index acf47f0d89567..f5a23b153fdbe 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -21,6 +21,7 @@
 //   constexpr T atomic_fetch_min_explicit(atomic<T>*, typename atomic<T>::value_type,
 //                                         memory_order) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>

>From 7bfd5438857639f2d3b627c18fe56e57a1165067 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 17:24:43 +0800
Subject: [PATCH 15/28] declval

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp       | 1 +
 .../atomic_fetch_max_explicit.pass.cpp                           | 1 +
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp       | 1 +
 .../atomic_fetch_min_explicit.pass.cpp                           | 1 +
 4 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 93bd767cbab35..af249355811a5 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -23,6 +23,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 7fb2a702be337..45df7c240dfa2 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -25,6 +25,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index a5adda9487897..e326d4a4e5b7a 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -23,6 +23,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index f5a23b153fdbe..4e110f7f34944 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -25,6 +25,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"

>From a844bd2596f09f8eda9af8bf9fca7c67997816f8 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 19:51:19 +0800
Subject: [PATCH 16/28] remove xfail

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp     | 3 ---
 .../atomic_fetch_max_explicit.pass.cpp                         | 3 ---
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp     | 3 ---
 .../atomic_fetch_min_explicit.pass.cpp                         | 3 ---
 4 files changed, 12 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index af249355811a5..e4aba6ebbdd20 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 45df7c240dfa2..441fb57c3b332 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index e326d4a4e5b7a..3a4a15290b407 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 4e110f7f34944..fd208162b9c07 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>

>From a85a1dfa40440160f7910933e899250e8c6409f9 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 21 Feb 2026 20:30:08 +0800
Subject: [PATCH 17/28] address review comments

---
 libcxx/include/__atomic/atomic.h              | 39 ++++++++--
 libcxx/include/__atomic/atomic_ref.h          | 28 +++++++
 libcxx/include/__atomic/support/c11.h         | 16 ++--
 libcxx/include/__atomic/support/gcc.h         | 26 +++----
 libcxx/include/atomic                         |  4 +
 .../atomics/atomics.ref/fetch_max.pass.cpp    | 73 +++++++++++++------
 .../atomics/atomics.ref/fetch_min.pass.cpp    | 73 +++++++++++++------
 .../atomic_fetch_max.pass.cpp                 |  3 +-
 .../atomic_fetch_max_explicit.pass.cpp        |  3 +-
 .../atomic_fetch_min.pass.cpp                 |  3 +-
 .../atomic_fetch_min_explicit.pass.cpp        |  3 +-
 11 files changed, 191 insertions(+), 80 deletions(-)

diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index 455acb8fca2ad..5fb9e0db72f51 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_integral.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/remove_pointer.h>
@@ -322,6 +323,28 @@ struct atomic<_Tp*> : public __atomic_base<_Tp*> {
     return std::__cxx_atomic_fetch_sub(std::addressof(this->__a_), __op, __m);
   }
 
+#if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+#endif
+
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) volatile _NOEXCEPT { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) _NOEXCEPT { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) volatile _NOEXCEPT { return fetch_sub(1); }
@@ -819,13 +842,13 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 // atomic_fetch_max
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
@@ -833,14 +856,14 @@ _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp
 // atomic_fetch_max_explicit
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
@@ -849,13 +872,13 @@ atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 // atomic_fetch_min
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
@@ -863,14 +886,14 @@ _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp
 // atomic_fetch_min_explicit
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 88c7d7dbc5dfb..ec98fab310c7c 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -403,6 +403,34 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_sub(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept {
     return __atomic_fetch_sub(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
   }
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_max)
+    return reinterpret_cast<_Tp*>(__atomic_fetch_max(
+        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+#    else
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old > __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
+#    endif
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_min)
+    return reinterpret_cast<_Tp*>(__atomic_fetch_min(
+        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+#    else
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old < __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
+#    endif
+  }
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) const noexcept { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) const noexcept { return fetch_sub(1); }
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f28b5e461c18a..0d74d55188a3d 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -261,28 +261,28 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 754c60406cbf6..37c38d25012c6 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -260,29 +260,28 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_max)
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret > __pattern ? __ret : __pattern;
+    __value = __ret > __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
 }
 
 template <typename _Tp>
-_LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+_LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_max)
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret > __pattern ? __ret : __pattern;
+    __value = __ret > __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
@@ -290,29 +289,28 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_min)
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret < __pattern ? __ret : __pattern;
+    __value = __ret < __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
 }
 
 template <typename _Tp>
-_LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+_LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_min)
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret < __pattern ? __ret : __pattern;
+    __value = __ret < __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 4cf93b6bfbd85..bcf831fedc192 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -244,6 +244,10 @@ struct atomic<T*>
     T* fetch_add(ptrdiff_t op, memory_order m = memory_order_seq_cst) noexcept;
     T* fetch_sub(ptrdiff_t op, memory_order m = memory_order_seq_cst) volatile noexcept;
     T* fetch_sub(ptrdiff_t op, memory_order m = memory_order_seq_cst) noexcept;
+    T* fetch_max(T*, memory_order = memory_order_seq_cst) volatile noexcept;
+    T* fetch_max(T*, memory_order = memory_order_seq_cst) noexcept;
+    T* fetch_min(T*, memory_order = memory_order_seq_cst) volatile noexcept;
+    T* fetch_min(T*, memory_order = memory_order_seq_cst) noexcept;
 
     T* operator++(int) volatile noexcept;
     T* operator++(int) noexcept;
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 17930c5d97e65..2ba748486d950 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -33,29 +33,56 @@ struct TestDoesNotHaveFetchMax {
 template <typename T>
 struct TestFetchMax {
   void operator()() const {
-    static_assert(std::is_integral_v<T> && has_fetch_max<T>);
-
-    alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
-    std::atomic_ref<T> const a(x);
-
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
-      assert(y == T(1));
-      assert(x == T(2));
-      y = a.fetch_max(T(0));
-      assert(y == T(2));
-      assert(x == T(2));
-      ASSERT_NOEXCEPT(a.fetch_max(T(0)));
-    }
+    static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_max<T>);
+
+    if constexpr (std::is_integral_v<T>) {
+      alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
+        assert(y == T(1));
+        assert(x == T(2));
+        y = a.fetch_max(T(0));
+        assert(y == T(2));
+        assert(x == T(2));
+        ASSERT_NOEXCEPT(a.fetch_max(T(0)));
+      }
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
+        assert(y == T(2));
+        assert(x == T(3));
+        y = a.fetch_max(T(0));
+        assert(y == T(3));
+        assert(x == T(3));
+        ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
+      }
+    } else if constexpr (std::is_pointer_v<T>) {
+      using U = std::remove_pointer_t<T>;
+      U t[9]  = {};
+      alignas(std::atomic_ref<T>::required_alignment) T x{&t[1]};
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(&t[2]);
+        assert(y == &t[1]);
+        assert(x == &t[2]);
+        y = a.fetch_max(&t[0]);
+        assert(y == &t[2]);
+        assert(x == &t[2]);
+        ASSERT_NOEXCEPT(a.fetch_max(&t[0]));
+      }
 
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
-      assert(y == T(2));
-      assert(x == T(3));
-      y = a.fetch_max(T(0));
-      assert(y == T(3));
-      assert(x == T(3));
-      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(&t[3], std::memory_order_relaxed);
+        assert(y == &t[2]);
+        assert(a == &t[3]);
+        y = a.fetch_max(&t[0]);
+        assert(y == &t[3]);
+        assert(x == &t[3]);
+        ASSERT_NOEXCEPT(a.fetch_max(&t[0], std::memory_order_relaxed));
+      }
     }
   }
 };
@@ -65,7 +92,7 @@ int main(int, char**) {
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
 
-  TestEachPointerType<TestDoesNotHaveFetchMax>()();
+  TestEachPointerType<TestFetchMax>()();
 
   TestDoesNotHaveFetchMax<bool>()();
   TestDoesNotHaveFetchMax<UserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index e930d83a2b819..b178996b06085 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -33,29 +33,56 @@ struct TestDoesNotHaveFetchMin {
 template <typename T>
 struct TestFetchMin {
   void operator()() const {
-    static_assert(std::is_integral_v<T> && has_fetch_min<T>);
-
-    alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
-    std::atomic_ref<T> const a(x);
-
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
-      assert(y == T(3));
-      assert(x == T(2));
-      y = a.fetch_min(T(4));
-      assert(y == T(2));
-      assert(x == T(2));
-      ASSERT_NOEXCEPT(a.fetch_min(T(0)));
-    }
+    static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_min<T>);
+
+    if constexpr (std::is_integral_v<T>) {
+      alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
+        assert(y == T(3));
+        assert(x == T(2));
+        y = a.fetch_min(T(4));
+        assert(y == T(2));
+        assert(x == T(2));
+        ASSERT_NOEXCEPT(a.fetch_min(T(0)));
+      }
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
+        assert(y == T(2));
+        assert(x == T(1));
+        y = a.fetch_min(T(4));
+        assert(y == T(1));
+        assert(x == T(1));
+        ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
+      }
+    } else if constexpr (std::is_pointer_v<T>) {
+      using U = std::remove_pointer_t<T>;
+      U t[9]  = {};
+      alignas(std::atomic_ref<T>::required_alignment) T x{&t[3]};
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(&t[2]);
+        assert(y == &t[3]);
+        assert(x == &t[2]);
+        y = a.fetch_min(&t[4]);
+        assert(y == &t[2]);
+        assert(x == &t[2]);
+        ASSERT_NOEXCEPT(a.fetch_min(&t[0]));
+      }
 
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
-      assert(y == T(2));
-      assert(x == T(1));
-      y = a.fetch_min(T(4));
-      assert(y == T(1));
-      assert(x == T(1));
-      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(&t[1], std::memory_order_relaxed);
+        assert(y == &t[2]);
+        assert(a == &t[1]);
+        y = a.fetch_min(&t[4]);
+        assert(y == &t[1]);
+        assert(x == &t[1]);
+        ASSERT_NOEXCEPT(a.fetch_min(&t[0], std::memory_order_relaxed));
+      }
     }
   }
 };
@@ -65,7 +92,7 @@ int main(int, char**) {
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
 
-  TestEachPointerType<TestDoesNotHaveFetchMin>()();
+  TestEachPointerType<TestFetchMin>()();
 
   TestDoesNotHaveFetchMin<bool>()();
   TestDoesNotHaveFetchMin<UserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index e4aba6ebbdd20..a7596b6ff2bf5 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -44,7 +44,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_max(&t, T(2)) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_max(T(2));
+    assert(r == T(3));
     assert(t == T(3));
   }
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 441fb57c3b332..52c4ac137c709 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -47,7 +47,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_max(T(2), std::memory_order_seq_cst);
+    assert(r == T(3));
     assert(t == T(3));
   }
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 3a4a15290b407..1ffc92f3c2300 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -44,7 +44,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_min(&t, T(2)) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_min(T(2));
+    assert(r == T(3));
     assert(t == T(2));
   }
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index fd208162b9c07..f5732b5d977cc 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -47,7 +47,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_min(T(2), std::memory_order_seq_cst);
+    assert(r == T(3));
     assert(t == T(2));
   }
 

>From c3e318d67841e8076a2e99f9c79d64873178dab2 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 21 Feb 2026 21:07:13 +0800
Subject: [PATCH 18/28] header

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp       | 1 +
 .../atomic_fetch_max_explicit.pass.cpp                           | 1 +
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp       | 1 +
 .../atomic_fetch_min_explicit.pass.cpp                           | 1 +
 4 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index a7596b6ff2bf5..dc2274a1056cb 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 52c4ac137c709..d878f6abd246d 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 1ffc92f3c2300..43277f6a7977a 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index f5732b5d977cc..75bf200812ab0 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>

>From 609e1196aa5098cbbd3e27a02d21beb42c98ecfe Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 22 Feb 2026 21:14:58 +0800
Subject: [PATCH 19/28] fix gcc ci

---
 libcxx/include/__atomic/atomic_ref.h | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index ec98fab310c7c..e0f28f4a3e204 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,11 +406,10 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
 #    if __has_builtin(__atomic_fetch_max)
-    return reinterpret_cast<_Tp*>(__atomic_fetch_max(
-        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+    return __atomic_fetch_max(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
 #    else
-    _Tp __old = this->load(memory_order_relaxed);
-    _Tp __new;
+    _Tp* __old = this->load(memory_order_relaxed);
+    _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
@@ -419,11 +418,10 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
 #    if __has_builtin(__atomic_fetch_min)
-    return reinterpret_cast<_Tp*>(__atomic_fetch_min(
-        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+    return __atomic_fetch_min(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
 #    else
-    _Tp __old = this->load(memory_order_relaxed);
-    _Tp __new;
+    _Tp* __old = this->load(memory_order_relaxed);
+    _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));

>From ebd61a9db3e67e3c7caf05b7e364f61f3243d24a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 10:19:34 -0400
Subject: [PATCH 20/28] Don't use builtins for pointer overloads of
 fetch_max/min

`__arg * sizeof(_Tp)` makes no sense in that context since __arg is a
pointer.
---
 libcxx/include/__atomic/atomic_ref.h | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index d681fd7488fcc..0fb141fb1f67a 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,28 +406,22 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_max)
-    return __atomic_fetch_max(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_min)
-    return __atomic_fetch_min(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
 #  endif
 

>From 6190229b39172c8b6e239e72c31e9f6daa6c85d6 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 10:32:46 -0400
Subject: [PATCH 21/28] Fix release note

---
 libcxx/docs/ReleaseNotes/23.rst | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index c99f3b49d902b..d56ec23a763cb 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -38,7 +38,8 @@ What's New in Libc++ 23.0.0?
 Implemented Papers
 ------------------
 
-- P0493R5: Atomic minimum/maximum are supported (`Github <https://llvm.org/PR105418>`__)
+- P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
+- P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
 - P1899R3: ``stride_view`` (`Github <https://llvm.org/PR105198>`__)
 - P3936R1: Safer ``atomic_ref::address`` (`Github <https://llvm.org/PR189594>`__)
 - P3953R3: Rename ``std::runtime_format`` (`Github <https://llvm.org/PR189624>`__)

>From 2853b3870f2bdfe1a6385f1a245866d71ac391f4 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 10:35:42 -0400
Subject: [PATCH 22/28] Revert "Don't use builtins for pointer overloads of
 fetch_max/min"

Try applying frederick's suggestion instead.
---
 libcxx/include/__atomic/atomic_ref.h | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 0fb141fb1f67a..2f5a256698cf3 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,22 +406,28 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
+#    if __has_builtin(__atomic_fetch_max)
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
+#    if __has_builtin(__atomic_fetch_min)
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
 #  endif
 

>From b7ed28f9b8e3be9137393c91be40e29ac1268efa Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 11:11:22 -0400
Subject: [PATCH 23/28] Re-apply "Don't use builtins for pointer overloads of
 fetch_max/min"

It seems that the builtins indeed don't support pointers.
---
 libcxx/include/__atomic/atomic_ref.h | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 2f5a256698cf3..0fb141fb1f67a 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,28 +406,22 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_max)
-    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_min)
-    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
 #  endif
 

>From de6f07f40149a2f41740033a24be77f06919097a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 16:41:43 -0400
Subject: [PATCH 24/28] Port test coverage from #186694

Assisted by Claude, reviewed manually.
---
 .../atomics/atomics.ref/fetch_max.pass.cpp    |  64 +++-----
 .../atomics/atomics.ref/fetch_min.pass.cpp    |  64 +++-----
 .../atomic_fetch_max.pass.cpp                 |  49 ++++--
 .../atomic_fetch_max_explicit.pass.cpp        |  50 ++++--
 .../atomic_fetch_min.pass.cpp                 |  64 ++++----
 .../atomic_fetch_min_explicit.pass.cpp        |  65 ++++----
 libcxx/test/support/atomic_fetch_max_helper.h | 147 ++++++++++++++++++
 libcxx/test/support/atomic_fetch_min_helper.h | 147 ++++++++++++++++++
 8 files changed, 478 insertions(+), 172 deletions(-)
 create mode 100644 libcxx/test/support/atomic_fetch_max_helper.h
 create mode 100644 libcxx/test/support/atomic_fetch_min_helper.h

diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 2ba748486d950..7302eaea570f0 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -10,12 +10,14 @@
 // XFAIL: !has-64-bit-atomics
 
 // integral-type fetch_max(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+// T* fetch_max(T*, memory_order = memory_order::seq_cst) const noexcept;
 
 #include <atomic>
 #include <cassert>
 #include <concepts>
 #include <type_traits>
 
+#include "atomic_fetch_max_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -36,64 +38,38 @@ struct TestFetchMax {
     static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_max<T>);
 
     if constexpr (std::is_integral_v<T>) {
-      alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
+      alignas(std::atomic_ref<T>::required_alignment) T x{};
       std::atomic_ref<T> const a(x);
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
-        assert(y == T(1));
-        assert(x == T(2));
-        y = a.fetch_max(T(0));
-        assert(y == T(2));
-        assert(x == T(2));
-        ASSERT_NOEXCEPT(a.fetch_max(T(0)));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
-        assert(y == T(2));
-        assert(x == T(3));
-        y = a.fetch_max(T(0));
-        assert(y == T(3));
-        assert(x == T(3));
-        ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
-      }
+      auto load  = [&]() { return x; };
+      auto store = [&](T val) { x = val; };
+      auto max   = [&](T val, auto order) { return a.fetch_max(val, order); };
+
+      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_seq_cst));
+      test_fetch_max_integral<T>(load, store, max);
+
     } else if constexpr (std::is_pointer_v<T>) {
       using U = std::remove_pointer_t<T>;
-      U t[9]  = {};
-      alignas(std::atomic_ref<T>::required_alignment) T x{&t[1]};
-      std::atomic_ref<T> const a(x);
+      U arr[5]{};
+      alignas(std::atomic_ref<T>::required_alignment) T p{};
+      std::atomic_ref<T> const a(p);
+
+      auto load  = [&]() { return p; };
+      auto store = [&](T val) { p = val; };
+      auto max   = [&](T val, auto order) { return a.fetch_max(val, order); };
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(&t[2]);
-        assert(y == &t[1]);
-        assert(x == &t[2]);
-        y = a.fetch_max(&t[0]);
-        assert(y == &t[2]);
-        assert(x == &t[2]);
-        ASSERT_NOEXCEPT(a.fetch_max(&t[0]));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(&t[3], std::memory_order_relaxed);
-        assert(y == &t[2]);
-        assert(a == &t[3]);
-        y = a.fetch_max(&t[0]);
-        assert(y == &t[3]);
-        assert(x == &t[3]);
-        ASSERT_NOEXCEPT(a.fetch_max(&t[0], std::memory_order_relaxed));
-      }
+      ASSERT_NOEXCEPT(a.fetch_max(&arr[0], std::memory_order_seq_cst));
+      test_fetch_max_pointer<U>(&arr[0], &arr[2], &arr[4], load, store, max);
     }
   }
 };
 
 int main(int, char**) {
   TestEachIntegralType<TestFetchMax>()();
+  TestEachPointerType<TestFetchMax>()();
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
 
-  TestEachPointerType<TestFetchMax>()();
-
   TestDoesNotHaveFetchMax<bool>()();
   TestDoesNotHaveFetchMax<UserAtomicType>()();
   TestDoesNotHaveFetchMax<LargeUserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index b178996b06085..e7f36363ae3a7 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -10,12 +10,14 @@
 // XFAIL: !has-64-bit-atomics
 
 // integral-type fetch_min(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+// T* fetch_min(T*, memory_order = memory_order::seq_cst) const noexcept;
 
 #include <atomic>
 #include <cassert>
 #include <concepts>
 #include <type_traits>
 
+#include "atomic_fetch_min_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -36,64 +38,38 @@ struct TestFetchMin {
     static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_min<T>);
 
     if constexpr (std::is_integral_v<T>) {
-      alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
+      alignas(std::atomic_ref<T>::required_alignment) T x{};
       std::atomic_ref<T> const a(x);
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
-        assert(y == T(3));
-        assert(x == T(2));
-        y = a.fetch_min(T(4));
-        assert(y == T(2));
-        assert(x == T(2));
-        ASSERT_NOEXCEPT(a.fetch_min(T(0)));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
-        assert(y == T(2));
-        assert(x == T(1));
-        y = a.fetch_min(T(4));
-        assert(y == T(1));
-        assert(x == T(1));
-        ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
-      }
+      auto load  = [&]() { return x; };
+      auto store = [&](T val) { x = val; };
+      auto min   = [&](T val, auto order) { return a.fetch_min(val, order); };
+
+      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_seq_cst));
+      test_fetch_min_integral<T>(load, store, min);
+
     } else if constexpr (std::is_pointer_v<T>) {
       using U = std::remove_pointer_t<T>;
-      U t[9]  = {};
-      alignas(std::atomic_ref<T>::required_alignment) T x{&t[3]};
-      std::atomic_ref<T> const a(x);
+      U arr[5]{};
+      alignas(std::atomic_ref<T>::required_alignment) T p{};
+      std::atomic_ref<T> const a(p);
+
+      auto load  = [&]() { return p; };
+      auto store = [&](T val) { p = val; };
+      auto min   = [&](T val, auto order) { return a.fetch_min(val, order); };
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(&t[2]);
-        assert(y == &t[3]);
-        assert(x == &t[2]);
-        y = a.fetch_min(&t[4]);
-        assert(y == &t[2]);
-        assert(x == &t[2]);
-        ASSERT_NOEXCEPT(a.fetch_min(&t[0]));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(&t[1], std::memory_order_relaxed);
-        assert(y == &t[2]);
-        assert(a == &t[1]);
-        y = a.fetch_min(&t[4]);
-        assert(y == &t[1]);
-        assert(x == &t[1]);
-        ASSERT_NOEXCEPT(a.fetch_min(&t[0], std::memory_order_relaxed));
-      }
+      ASSERT_NOEXCEPT(a.fetch_min(&arr[0], std::memory_order_seq_cst));
+      test_fetch_min_pointer<U>(&arr[0], &arr[2], &arr[4], load, store, min);
     }
   }
 };
 
 int main(int, char**) {
   TestEachIntegralType<TestFetchMin>()();
+  TestEachPointerType<TestFetchMin>()();
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
 
-  TestEachPointerType<TestFetchMin>()();
-
   TestDoesNotHaveFetchMin<bool>()();
   TestDoesNotHaveFetchMin<UserAtomicType>()();
   TestDoesNotHaveFetchMin<LargeUserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index dc2274a1056cb..7b2a5715ddddd 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -19,11 +19,10 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_max_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -35,23 +34,18 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(std::atomic_fetch_max(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
-
-  // atomic_fetch_max
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_max(&t, T(2)) == T(1));
-    assert(t == T(2));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_max(T(2));
-    assert(r == T(3));
-    assert(t == T(3));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto max   = [&](T val, auto) { return std::atomic_fetch_max(&a, val); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_max(&a, T(0)));
+    test_fetch_max_integral<T>(load, store, max);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_max concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_max should leave
+  // the atomic at the global maximum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
@@ -95,8 +89,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto max   = [&](T* val, auto) { return std::atomic_fetch_max(&a, val); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_max(&a, p0));
+  test_fetch_max_pointer<T>(p0, p2, p4, load, store, max);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index d878f6abd246d..e811f114708c8 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -21,11 +21,10 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_max_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -37,24 +36,18 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(
-      std::atomic_fetch_max_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
-
-  // atomic_fetch_max_explicit
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-    assert(t == T(2));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_max(T(2), std::memory_order_seq_cst);
-    assert(r == T(3));
-    assert(t == T(3));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto max   = [&](T val, auto order) { return std::atomic_fetch_max_explicit(&a, val, order); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&a, T(0), std::memory_order_seq_cst));
+    test_fetch_max_integral<T>(load, store, max);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_max_explicit concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_max_explicit should
+  // leave the atomic at the global maximum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
@@ -98,8 +91,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto max   = [&](T* val, auto order) { return std::atomic_fetch_max_explicit(&a, val, order); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&a, p0, std::memory_order_seq_cst));
+  test_fetch_max_pointer<T>(p0, p2, p4, load, store, max);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 43277f6a7977a..df40cc7a92c7b 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -19,11 +19,11 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
+#include <limits>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_min_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -35,34 +35,29 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(std::atomic_fetch_min(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
-
-  // atomic_fetch_min
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_min(&t, T(2)) == T(1));
-    assert(t == T(1));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_min(T(2));
-    assert(r == T(3));
-    assert(t == T(2));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto min   = [&](T val, auto) { return std::atomic_fetch_min(&a, val); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_min(&a, T(0)));
+    test_fetch_min_integral<T>(load, store, min);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_min concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_min should leave
+  // the atomic at the global minimum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
 
-    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+    MaybeVolatile<std::atomic<T>> at(std::numeric_limits<T>::max());
 
     std::vector<std::thread> threads;
     threads.reserve(number_of_threads);
     for (auto i = 0; i < number_of_threads; ++i) {
       threads.push_back(support::make_test_thread([&at, i]() {
-        for (auto j = loop - 1; j >= 0; --j) {
+        for (auto j = 0; j < loop; ++j) {
           std::atomic_fetch_min(&at, T(i * loop + j));
         }
       }));
@@ -72,15 +67,7 @@ void test_impl() {
       thread.join();
     }
 
-    const auto times = [](int n) {
-      T res(n);
-      for (auto i = n - 1; i >= 0; --i) {
-        res = std::min<T>(res, i);
-      }
-      return res;
-    };
-
-    assert(at.load() == times(number_of_threads * loop));
+    assert(at.load() == T(0));
   }
 #endif
 }
@@ -95,8 +82,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto min   = [&](T* val, auto) { return std::atomic_fetch_min(&a, val); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_min(&a, p0));
+  test_fetch_min_pointer<T>(p0, p2, p4, load, store, min);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 75bf200812ab0..2295b942b86de 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -21,11 +21,11 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
+#include <limits>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_min_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -37,35 +37,29 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(
-      std::atomic_fetch_min_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
-
-  // atomic_fetch_min_explicit
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-    assert(t == T(1));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_min(T(2), std::memory_order_seq_cst);
-    assert(r == T(3));
-    assert(t == T(2));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto min   = [&](T val, auto order) { return std::atomic_fetch_min_explicit(&a, val, order); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&a, T(0), std::memory_order_seq_cst));
+    test_fetch_min_integral<T>(load, store, min);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_min_explicit concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_min_explicit should
+  // leave the atomic at the global minimum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
 
-    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+    MaybeVolatile<std::atomic<T>> at(std::numeric_limits<T>::max());
 
     std::vector<std::thread> threads;
     threads.reserve(number_of_threads);
     for (auto i = 0; i < number_of_threads; ++i) {
       threads.push_back(support::make_test_thread([&at, i]() {
-        for (auto j = loop - 1; j >= 0; --j) {
+        for (auto j = 0; j < loop; ++j) {
           std::atomic_fetch_min_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
         }
       }));
@@ -75,15 +69,7 @@ void test_impl() {
       thread.join();
     }
 
-    const auto times = [](int n) {
-      T res(n);
-      for (auto i = n - 1; i >= 0; --i) {
-        res = std::min<T>(res, i);
-      }
-      return res;
-    };
-
-    assert(at.load() == times(number_of_threads * loop));
+    assert(at.load() == T(0));
   }
 #endif
 }
@@ -98,8 +84,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto min   = [&](T* val, auto order) { return std::atomic_fetch_min_explicit(&a, val, order); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&a, p0, std::memory_order_seq_cst));
+  test_fetch_min_pointer<T>(p0, p2, p4, load, store, min);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/support/atomic_fetch_max_helper.h b/libcxx/test/support/atomic_fetch_max_helper.h
new file mode 100644
index 0000000000000..ff4bd7aa38912
--- /dev/null
+++ b/libcxx/test/support/atomic_fetch_max_helper.h
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
+#define TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
+
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Test fetch_max for integral types
+// LoadOp: () -> T - reads current value
+// StoreOp: (T) -> void - stores a value
+// MaxOp: (T, memory_order) -> T - performs fetch_max operation
+template <class T, class LoadOp, class StoreOp, class MaxOp>
+void test_fetch_max_integral(LoadOp load, StoreOp store, MaxOp max) {
+  // Test basic fetch_max (update to larger value)
+  {
+    store(T(10));
+    T old = max(T(20), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(20));
+  }
+
+  // Test with smaller value (no change)
+  {
+    store(T(10));
+    T old = max(T(5), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(10));
+  }
+
+  // Test different memory orderings
+  {
+    store(T(15));
+    T old = max(T(25), std::memory_order_seq_cst);
+    assert(old == T(15));
+    assert(load() == T(25));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(25), std::memory_order_relaxed);
+    assert(old == T(10));
+    assert(load() == T(25));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(30), std::memory_order_acquire);
+    assert(old == T(10));
+    assert(load() == T(30));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(15), std::memory_order_release);
+    assert(old == T(10));
+    assert(load() == T(15));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(22), std::memory_order_acq_rel);
+    assert(old == T(10));
+    assert(load() == T(22));
+  }
+
+  {
+    store(T(15));
+    T old = max(T(35), std::memory_order_relaxed);
+    assert(old == T(15));
+    assert(load() == T(35));
+  }
+
+  // Test return type
+  {
+    store(T(10));
+    static_assert(std::is_same_v<decltype(max(T(20), std::memory_order_seq_cst)), T>);
+  }
+}
+
+// Test fetch_max for pointer types
+// LoadOp: () -> T* - reads current pointer value
+// StoreOp: (T*) -> void - stores a pointer value
+// MaxOp: (T*, memory_order) -> T* - performs fetch_max operation
+template <class T, class LoadOp, class StoreOp, class MaxOp>
+void test_fetch_max_pointer(T* p0, T* p2, T* p4, LoadOp load, StoreOp store, MaxOp max) {
+  // Test basic fetch_max (update to larger pointer)
+  {
+    store(p2);
+    T* old = max(p4, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p4);
+  }
+
+  // Test with smaller pointer (no change)
+  {
+    store(p2);
+    T* old = max(p0, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p2);
+  }
+
+  // Test different memory orderings
+  {
+    store(p0);
+    T* old = max(p2, std::memory_order_seq_cst);
+    assert(old == p0);
+    assert(load() == p2);
+  }
+
+  {
+    store(p0);
+    T* old = max(p2, std::memory_order_relaxed);
+    assert(old == p0);
+    assert(load() == p2);
+  }
+
+  {
+    store(p0);
+    T* old = max(p4, std::memory_order_acquire);
+    assert(old == p0);
+    assert(load() == p4);
+  }
+
+  {
+    store(p0);
+    T* old = max(p4, std::memory_order_release);
+    assert(old == p0);
+    assert(load() == p4);
+  }
+
+  // Test return type
+  {
+    store(p2);
+    static_assert(std::is_same_v<decltype(max(p4, std::memory_order_seq_cst)), T*>);
+  }
+}
+
+#endif // TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
diff --git a/libcxx/test/support/atomic_fetch_min_helper.h b/libcxx/test/support/atomic_fetch_min_helper.h
new file mode 100644
index 0000000000000..20a3558296118
--- /dev/null
+++ b/libcxx/test/support/atomic_fetch_min_helper.h
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H
+#define TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H
+
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Test fetch_min for integral types
+// LoadOp: () -> T - reads current value
+// StoreOp: (T) -> void - stores a value
+// MinOp: (T, memory_order) -> T - performs fetch_min operation
+template <class T, class LoadOp, class StoreOp, class MinOp>
+void test_fetch_min_integral(LoadOp load, StoreOp store, MinOp min) {
+  // Test basic fetch_min (update to smaller value)
+  {
+    store(T(10));
+    T old = min(T(5), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(5));
+  }
+
+  // Test with larger value (no change)
+  {
+    store(T(10));
+    T old = min(T(20), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(10));
+  }
+
+  // Test different memory orderings
+  {
+    store(T(15));
+    T old = min(T(8), std::memory_order_seq_cst);
+    assert(old == T(15));
+    assert(load() == T(8));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(3), std::memory_order_relaxed);
+    assert(old == T(10));
+    assert(load() == T(3));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(2), std::memory_order_acquire);
+    assert(old == T(10));
+    assert(load() == T(2));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(7), std::memory_order_release);
+    assert(old == T(10));
+    assert(load() == T(7));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(4), std::memory_order_acq_rel);
+    assert(old == T(10));
+    assert(load() == T(4));
+  }
+
+  {
+    store(T(15));
+    T old = min(T(6), std::memory_order_relaxed);
+    assert(old == T(15));
+    assert(load() == T(6));
+  }
+
+  // Test return type
+  {
+    store(T(10));
+    static_assert(std::is_same_v<decltype(min(T(5), std::memory_order_seq_cst)), T>);
+  }
+}
+
+// Test fetch_min for pointer types
+// LoadOp: () -> T* - reads current pointer value
+// StoreOp: (T*) -> void - stores a pointer value
+// MinOp: (T*, memory_order) -> T* - performs fetch_min operation
+template <class T, class LoadOp, class StoreOp, class MinOp>
+void test_fetch_min_pointer(T* p0, T* p2, T* p4, LoadOp load, StoreOp store, MinOp min) {
+  // Test basic fetch_min (update to smaller pointer)
+  {
+    store(p2);
+    T* old = min(p0, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p0);
+  }
+
+  // Test with larger pointer (no change)
+  {
+    store(p2);
+    T* old = min(p4, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p2);
+  }
+
+  // Test different memory orderings
+  {
+    store(p4);
+    T* old = min(p2, std::memory_order_seq_cst);
+    assert(old == p4);
+    assert(load() == p2);
+  }
+
+  {
+    store(p4);
+    T* old = min(p2, std::memory_order_relaxed);
+    assert(old == p4);
+    assert(load() == p2);
+  }
+
+  {
+    store(p4);
+    T* old = min(p0, std::memory_order_acquire);
+    assert(old == p4);
+    assert(load() == p0);
+  }
+
+  {
+    store(p4);
+    T* old = min(p0, std::memory_order_release);
+    assert(old == p4);
+    assert(load() == p0);
+  }
+
+  // Test return type
+  {
+    store(p2);
+    static_assert(std::is_same_v<decltype(min(p0, std::memory_order_seq_cst)), T*>);
+  }
+}
+
+#endif // TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H

>From f8b41b52f67d6ca6dd03617efb6d56ec1c20c6ae Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 16 Jun 2026 07:25:18 -0400
Subject: [PATCH 25/28] Use a CAS loop for pointer types in
 __cxx_atomic_fetch_{max,min} on c11.h

Clang's __c11_atomic_fetch_max/min builtins reject pointer arguments
("address argument to atomic operation must be a pointer to atomic integer
or supported floating point type"). The four __cxx_atomic_fetch_{max,min}
overloads in support/c11.h were unconditionally calling those builtins, so
std::atomic<T*>::fetch_max/min failed to compile on every Clang-based
config. support/gcc.h already had this dispatch via #if __has_builtin(...)
falling back to a CAS loop, which is why generic-gcc was passing.

Dispatch on is_pointer<_Tp>::value: pointer types use a CAS loop matching
gcc.h's body, others keep calling the builtin.

Assisted by Claude
---
 libcxx/include/__atomic/support/c11.h | 56 +++++++++++++++++++++++----
 1 file changed, 48 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0d74d55188a3d..603ffc690b104 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -13,6 +13,7 @@
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/remove_const.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -259,30 +260,69 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
       std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
 }
 
+// Clang's __c11_atomic_fetch_max/min builtins do not accept pointer
+// arguments, so dispatch to a CAS loop for pointer types.
+// TODO: Use the builtin for pointer types once Clang accepts them.
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret > __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_max(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret > __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_max(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret < __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_min(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret < __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_min(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 
 _LIBCPP_END_NAMESPACE_STD

>From 77d8902e33364fe5887036f6dc36550094841d7b Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 16 Jun 2026 10:06:33 -0400
Subject: [PATCH 26/28] Switch __cxx_atomic_fetch_{max,min} pointer dispatch to
 SFINAE for C++03 compat

I know they're not needed outside of C++26, but this header should be
usable in all standard modes in principle.
---
 libcxx/include/__atomic/support/c11.h | 103 ++++++++++++++------------
 1 file changed, 55 insertions(+), 48 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 603ffc690b104..58a97fcfb3261 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -13,6 +13,7 @@
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/remove_const.h>
 
@@ -263,66 +264,72 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 // Clang's __c11_atomic_fetch_max/min builtins do not accept pointer
 // arguments, so dispatch to a CAS loop for pointer types.
 // TODO: Use the builtin for pointer types once Clang accepts them.
-template <class _Tp>
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret > __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_max(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret > __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+}
+
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret > __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_max(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret > __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret < __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_min(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret < __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+}
+
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret < __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_min(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret < __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 _LIBCPP_END_NAMESPACE_STD

>From 269691062efd541709388ab2b330e91b9b94cf91 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 26 Jun 2026 21:35:32 +0800
Subject: [PATCH 27/28] eliminate adl warning

---
 libcxx/include/__atomic/support/c11.h | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 58a97fcfb3261..899502422570a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -273,11 +273,12 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, mem
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -290,11 +291,12 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -307,11 +309,12 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, mem
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -324,11 +327,12 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 

>From 8e4a7ab9ec21ca5484aa4309b0f3d9943d5ec9d8 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 20:13:01 +0800
Subject: [PATCH 28/28] release notes 23 -> 24

---
 libcxx/docs/ReleaseNotes/23.rst | 1 -
 libcxx/docs/ReleaseNotes/24.rst | 1 +
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e806502decc0e..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -39,7 +39,6 @@ Implemented Papers
 ------------------
 
 - P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
-- P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
 - P1885R12: Naming Text Encodings to Demystify Them (`Github <https://llvm.org/PR105373>`__)
 - P1899R3: ``stride_view`` (`Github <https://llvm.org/PR105198>`__)
 - P2862R1: ``text_encoding::name()`` should never return null values (`Github <https://llvm.org/PR118371>`__)
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..70c704ff9d326 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
 
 Improvements and New Features
 -----------------------------


From libcxx-commits at lists.llvm.org  Mon Jul 20 05:18:31 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Mon, 20 Jul 2026 05:18:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2502R2: `std::generator` (PR
	#201854)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201854@github.com>
Message-ID: <6a5e1217.c4d3d80a.849c3.099a@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/201854

>From 22f5fee287889d72cb171751fdc0993197c69b6b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 22:45:16 +0800
Subject: [PATCH 1/7] implement generator

---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/Status/Cxx23Issues.csv            |   2 +-
 libcxx/docs/Status/Cxx23Papers.csv            |   4 +-
 libcxx/docs/Status/Cxx2cIssues.csv            |   8 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/generator                      | 603 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   5 +
 libcxx/include/version                        |   2 +
 libcxx/modules/std.compat.cppm.in             |   3 -
 libcxx/modules/std.cppm.in                    |   4 +-
 libcxx/modules/std/generator.inc              |   7 +-
 .../coro.generator/nodiscard.verify.cpp       |  29 +
 .../test/libcxx/transitive_includes/cxx03.csv |  21 +
 .../test/libcxx/transitive_includes/cxx11.csv |  21 +
 .../test/libcxx/transitive_includes/cxx14.csv |  21 +
 .../test/libcxx/transitive_includes/cxx17.csv |  21 +
 .../test/libcxx/transitive_includes/cxx20.csv |  16 +
 .../test/libcxx/transitive_includes/cxx23.csv |   9 +
 .../test/libcxx/transitive_includes/cxx26.csv |   9 +
 .../generator.version.compile.pass.cpp        |  65 ++
 .../version.version.compile.pass.cpp          |  30 +
 .../ranges/coro.generator/allocator.pass.cpp  | 123 ++++
 .../coro.generator/generator.compile.pass.cpp |  48 ++
 .../ranges/coro.generator/generator.pass.cpp  |  56 ++
 .../ranges/coro.generator/recursive.pass.cpp  |  97 +++
 .../generate_feature_test_macro_components.py |   5 +
 libcxx/utils/libcxx/header_information.py     |   1 -
 27 files changed, 1198 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/include/generator
 create mode 100644 libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/generator.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/recursive.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 1b748e37293df..46d83644ff10f 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -344,6 +344,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_forward_like``                                 ``202207L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_generator``                                    ``202207L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_invoke_r``                                     ``202106L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ios_noreplace``                                ``202207L``
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0a2f0d59484de..b61edbd0a2975 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -217,7 +217,7 @@
 "`LWG3759 <https://wg21.link/LWG3759>`__","``ranges::rotate_copy`` should use ``std::move``","2022-11 (Kona)","|Complete|","15","`#105027 <https://github.com/llvm/llvm-project/issues/105027>`__",""
 "`LWG3760 <https://wg21.link/LWG3760>`__","``cartesian_product_view::iterator``'s ``parent_`` is never valid","2022-11 (Kona)","","","`#105028 <https://github.com/llvm/llvm-project/issues/105028>`__",""
 "`LWG3761 <https://wg21.link/LWG3761>`__","``cartesian_product_view::iterator::operator-`` should pass by reference","2022-11 (Kona)","","","`#105029 <https://github.com/llvm/llvm-project/issues/105029>`__",""
-"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","","","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
+"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","23","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
 "`LWG3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept","2022-11 (Kona)","|Complete|","17","`#105031 <https://github.com/llvm/llvm-project/issues/105031>`__",""
 "`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","","","`#105032 <https://github.com/llvm/llvm-project/issues/105032>`__",""
 "`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","","`#105033 <https://github.com/llvm/llvm-project/issues/105033>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index eb580ea891f5b..862461f900dbd 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -80,7 +80,7 @@
 "`P2474R2 <https://wg21.link/P2474R2>`__","``views::repeat``","2022-07 (Virtual)","|Complete|","17","`#105222 <https://github.com/llvm/llvm-project/issues/105222>`__",""
 "`P2494R2 <https://wg21.link/P2494R2>`__","Relaxing range adaptors to allow for move only types","2022-07 (Virtual)","|Complete|","17","`#105224 <https://github.com/llvm/llvm-project/issues/105224>`__",""
 "`P2499R0 <https://wg21.link/P2499R0>`__","``string_view`` range constructor should be ``explicit``","2022-07 (Virtual)","|Complete|","16","`#105225 <https://github.com/llvm/llvm-project/issues/105225>`__",""
-"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","","","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
+"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","23","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
 "`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","2022-07 (Virtual)","|Complete|","15","`#105227 <https://github.com/llvm/llvm-project/issues/105227>`__",""
 "`P2517R1 <https://wg21.link/P2517R1>`__","Add a conditional ``noexcept`` specification to ``std::apply``","2022-07 (Virtual)","|Complete|","3.9","`#105229 <https://github.com/llvm/llvm-project/issues/105229>`__",""
 "`P2520R0 <https://wg21.link/P2520R0>`__","``move_iterator`` should be a random access iterator","2022-07 (Virtual)","|Complete|","17","`#105230 <https://github.com/llvm/llvm-project/issues/105230>`__","Implemented as a DR in C++20"
@@ -115,7 +115,7 @@
 "`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20","`#105259 <https://github.com/llvm/llvm-project/issues/105259>`__",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","`#105260 <https://github.com/llvm/llvm-project/issues/105260>`__","The paper is implemented as a DR to C++20"
 "`P2652R2 <https://wg21.link/P2652R2>`__","Disallow User Specialization of ``allocator_traits``","2023-02 (Issaquah)","|Complete|","19","`#105262 <https://github.com/llvm/llvm-project/issues/105262>`__",""
-"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","","","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
+"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","23","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
 "`P2614R2 <https://wg21.link/P2614R2>`__","Deprecate ``numeric_limits::has_denorm``","2023-02 (Issaquah)","|Complete|","18","`#105264 <https://github.com/llvm/llvm-project/issues/105264>`__",""
 "`P2588R3 <https://wg21.link/P2588R3>`__","``barrier``’s phase completion guarantees","2023-02 (Issaquah)","","","`#105265 <https://github.com/llvm/llvm-project/issues/105265>`__",""
 "`P2763R1 <https://wg21.link/P2763R1>`__","``layout_stride`` static extents default constructor fix","2023-02 (Issaquah)","","","`#105266 <https://github.com/llvm/llvm-project/issues/105266>`__",""
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 6a836c0491bb6..3ec8ae821ed5d 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -4,7 +4,7 @@
 "`LWG3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","2023-06 (Varna)","|Nothing To Do|","","`#105270 <https://github.com/llvm/llvm-project/issues/105270>`__",""
 "`LWG3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","2023-06 (Varna)","|Complete|","19","`#105271 <https://github.com/llvm/llvm-project/issues/105271>`__",""
 "`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","","","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
-"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","","","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
+"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","23","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
 "`LWG3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 <https://github.com/llvm/llvm-project/issues/105275>`__",""
 "`LWG3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 <https://github.com/llvm/llvm-project/issues/105276>`__",""
 "`LWG3905 <https://wg21.link/LWG3905>`__","Type of ``std::fexcept_t``","2023-06 (Varna)","|Complete|","3.4","`#105277 <https://github.com/llvm/llvm-project/issues/105277>`__",""
@@ -79,8 +79,8 @@
 "`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","","`#118332 <https://github.com/llvm/llvm-project/issues/118332>`__",""
 "`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","","`#118335 <https://github.com/llvm/llvm-project/issues/118335>`__",""
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
-"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
-"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
+"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","23","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
+"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","|Complete|","23","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
 "`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
@@ -93,7 +93,7 @@
 "`LWG4088 <https://wg21.link/LWG4088>`__","``println`` ignores the locale imbued in ``std::ostream``","2024-11 (Wrocław)","|Complete|","18","`#118348 <https://github.com/llvm/llvm-project/issues/118348>`__",""
 "`LWG4112 <https://wg21.link/LWG4112>`__","``has-arrow`` should required ``operator->()`` to be ``const``-qualified","2024-11 (Wrocław)","","","`#118349 <https://github.com/llvm/llvm-project/issues/118349>`__",""
 "`LWG4113 <https://wg21.link/LWG4113>`__","Disallow ``has_unique_object_representations<Incomplete[]>``","2024-11 (Wrocław)","|Complete|","","`#118350 <https://github.com/llvm/llvm-project/issues/118350>`__",""
-"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","","","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
+"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","23","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
 "`LWG4124 <https://wg21.link/LWG4124>`__","Cannot format ``zoned_time`` with resolution coarser than ``seconds``","2024-11 (Wrocław)","","","`#118352 <https://github.com/llvm/llvm-project/issues/118352>`__",""
 "`LWG4126 <https://wg21.link/LWG4126>`__","Some feature-test macros for fully freestanding features are not yet marked freestanding","2024-11 (Wrocław)","","","`#118353 <https://github.com/llvm/llvm-project/issues/118353>`__",""
 "`LWG4134 <https://wg21.link/LWG4134>`__","Issue with Philox algorithm specification","2024-11 (Wrocław)","","","`#118354 <https://github.com/llvm/llvm-project/issues/118354>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..cd77d51924571 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1043,6 +1043,7 @@ set(files
   fstream
   functional
   future
+  generator
   initializer_list
   iomanip
   ios
diff --git a/libcxx/include/generator b/libcxx/include/generator
new file mode 100644
index 0000000000000..d3f942b089002
--- /dev/null
+++ b/libcxx/include/generator
@@ -0,0 +1,603 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_GENERATOR
+#define _LIBCPP_GENERATOR
+
+/*
+
+namespace std {
+  // [coro.generator.class], class template generator
+  template<class Ref, class Val = void, class Allocator = void>
+    class generator;
+
+  namespace pmr {
+    template<class Ref, class Val = void>
+      using generator = std::generator<Ref, Val, polymorphic_allocator<>>;
+  }
+}
+
+*/
+
+#include <__algorithm/max.h>
+#include <__assert>
+#include <__concepts/common_reference_with.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/same_as.h>
+#include <__config>
+#include <__coroutine/coroutine_handle.h>
+#include <__coroutine/coroutine_traits.h>
+#include <__coroutine/noop_coroutine_handle.h>
+#include <__coroutine/trivial_awaitables.h>
+#include <__exception/exception_ptr.h>
+#include <__iterator/default_sentinel.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_arg_t.h>
+#include <__memory/allocator_traits.h>
+#include <__memory/construct_at.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/elements_of.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/common_reference.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/exchange.h>
+#include <__utility/move.h>
+#include <__utility/swap.h>
+#include <cstdint>
+#include <new>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 23
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Ref, class _Val>
+using __gen_val = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
+
+template <class _Ref, class _Val>
+using __gen_ref = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
+
+template <class _Ref, class _Val>
+using __gen_yielded =
+    conditional_t<is_reference_v<__gen_ref<_Ref, _Val>>, __gen_ref<_Ref, _Val>, const __gen_ref<_Ref, _Val>&>;
+
+template <class, class>
+struct __gen_iter_provider {
+  class __iterator;
+};
+
+template <class, class, class>
+class generator;
+
+template <class _Yielded>
+class __gen_promise_base {
+private:
+  template <class, class, class>
+  friend class generator;
+
+  template <class, class>
+  friend struct __gen_iter_provider;
+
+  // Each promise object stores either a `__root_data` when associated with a root generator, or a `__recursive_data`
+  // when associated with a generator that is yielded recursively.
+  struct __root_data {
+    // The client code has access only to the iterator of the root generator. Thus, the root generator must store the
+    // yielded values of recursively-yielded generators, which will then be returned when the client code dereferences
+    // the iterator.
+    add_pointer_t<_Yielded> __value_ptr;
+    // The client code has access only to the iterator of the root generator. Thus, the root generator needs to identify
+    // which generator is currently active. This active generator will then be resumed when the client code increments
+    // the iterator.
+    coroutine_handle<__gen_promise_base> __active;
+  };
+
+  struct __recursive_data {
+    exception_ptr __exception;
+    coroutine_handle<__gen_promise_base> __parent;
+    coroutine_handle<__gen_promise_base> __root;
+  };
+
+  union __union {
+    __root_data __root;
+    __recursive_data __recursive;
+
+    _LIBCPP_HIDE_FROM_ABI ~__union() noexcept {}
+  } __data_;
+
+  // The `__tag_` stores the active member of the `__data_` union:
+  // - `false` indicates that the active member is `__root`.
+  // - `true` indicates that the active member is `__recursive`.
+  // This field can be omitted because `__recursive_data.__root` can store the active member.
+  bool __tag_;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_root() noexcept { return !__tag_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __root_data& __get_root_data() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __data_.__root;
+  }
+  _LIBCPP_HIDE_FROM_ABI void __set_root_tag() noexcept { __tag_ = false; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_recursive() noexcept { return __tag_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __recursive_data& __get_recursive_data() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_recursive(), "the active member of `__data_` is not `__recursive`");
+    return __data_.__recursive;
+  }
+  _LIBCPP_HIDE_FROM_ABI void __set_recursive_tag() noexcept { __tag_ = true; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI coroutine_handle<__gen_promise_base>& __active() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __get_root_data().__active;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __value_ptr() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __get_root_data().__value_ptr;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __root_value_ptr() noexcept {
+    if (__is_root()) {
+      return __value_ptr();
+    }
+    return __get_recursive_data().__root.promise().__value_ptr();
+  }
+
+  struct __element_awaiter {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI void await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      __current.promise().__root_value_ptr() = std::addressof(__value);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+
+    remove_cvref_t<_Yielded> __value;
+  };
+
+  struct __final_awaiter {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI coroutine_handle<> await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      // Checks if the current generator is recursively-yielded
+      if (__current.promise().__is_recursive()) {
+        auto&& __recursive_data = __current.promise().__get_recursive_data();
+        auto __parent           = __recursive_data.__parent;
+        // Updates the active generator to its parent, allowing the client code to resume it later
+        __recursive_data.__root.promise().__active() = __parent;
+        // Transfers execution to its parent, which is the generator that yields it
+        return __parent;
+      }
+      return std::noop_coroutine();
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+  };
+
+  template <class _Ref, class _Val, class _Allocator>
+  struct __recursive_awaiter {
+    _LIBCPP_HIDE_FROM_ABI explicit __recursive_awaiter(generator<_Ref, _Val, _Allocator>&& __gen) noexcept
+        : __gen_{std::move(__gen)} {}
+
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return !__gen_.__coroutine_; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI coroutine_handle<__gen_promise_base>
+    await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      // Stores a `__recursive_data` in the promise object associated with `__gen`
+      auto __recursive = coroutine_handle<__gen_promise_base>::from_address(__gen_.__coroutine_.address());
+      __recursive.promise().__set_recursive_tag();
+      auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+
+      // Sets `__recursive_data.__parent` to the current generator
+      auto __parent             = coroutine_handle<__gen_promise_base>::from_address(__current.address());
+      __recursive_data.__parent = __parent;
+
+      // Sets `__recursive_data.__root` to the current generator if it's a root generator, or to the root generator of
+      // the current generator otherwise
+      if (__parent.promise().__is_recursive()) {
+        __recursive_data.__root = __parent.promise().__get_recursive_data().__root;
+      } else {
+        __recursive_data.__root = __parent;
+      }
+
+      // Updates the active generator to `__gen`, allowing the client code to resume it later
+      __recursive_data.__root.promise().__active() = __recursive;
+
+      // Transfers execution to `__gen`
+      return __recursive;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() {
+      auto __recursive        = coroutine_handle<__gen_promise_base>::from_address(__gen_.__coroutine_.address());
+      auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+      if (__recursive_data.__exception) {
+        std::rethrow_exception(std::move(__recursive_data.__exception));
+      }
+    }
+
+  private:
+    generator<_Ref, _Val, _Allocator> __gen_;
+  };
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __gen_promise_base() noexcept
+      : __data_{.__root{
+            .__value_ptr{nullptr},
+            .__active{coroutine_handle<__gen_promise_base>::from_promise(*this)},
+        }},
+        __tag_{false} {}
+
+  _LIBCPP_HIDE_FROM_ABI ~__gen_promise_base() noexcept {
+    if (__is_root()) {
+      __data_.__root.~__root_data();
+    } else {
+      __data_.__recursive.~__recursive_data();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI suspend_always initial_suspend() const noexcept { return {}; }
+
+  _LIBCPP_HIDE_FROM_ABI __final_awaiter final_suspend() noexcept { return {}; }
+
+  _LIBCPP_HIDE_FROM_ABI suspend_always yield_value(_Yielded __value) noexcept {
+    __root_value_ptr() = std::addressof(__value);
+    return {};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI auto yield_value(const remove_reference_t<_Yielded>& __value) noexcept(
+      is_nothrow_constructible_v<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>)
+    requires is_rvalue_reference_v<_Yielded> &&
+             constructible_from<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>
+  {
+    return __element_awaiter{.__value{__value}};
+  }
+
+  template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
+    requires same_as<__gen_yielded<_Ref2, _Val2>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto
+  yield_value(ranges::elements_of<generator<_Ref2, _Val2, _Allocator2>&&, _Unused> __elements) noexcept {
+    return __recursive_awaiter<_Ref2, _Val2, _Allocator2>{std::move(__elements.range)};
+  }
+
+  template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
+    requires same_as<__gen_yielded<_Ref2, _Val2>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto
+  yield_value(ranges::elements_of<generator<_Ref2, _Val2, _Allocator2>&, _Unused> __elements) noexcept {
+    return __recursive_awaiter<_Ref2, _Val2, _Allocator2>{std::move(__elements.range)};
+  }
+
+  template <ranges::input_range _Range, class _Allocator>
+    requires convertible_to<ranges::range_reference_t<_Range>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto yield_value(ranges::elements_of<_Range, _Allocator> __range) {
+    auto __lambda =
+        [](allocator_arg_t, _Allocator, ranges::iterator_t<_Range> __i, ranges::sentinel_t<_Range> __s) static
+        -> generator<_Yielded, void, _Allocator> {
+      for (; __i != __s; ++__i) {
+        co_yield static_cast<_Yielded>(*__i);
+      }
+    };
+    return yield_value(ranges::elements_of(
+        __lambda(allocator_arg, __range.allocator, ranges::begin(__range.range), ranges::end(__range.range))));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void await_transform() = delete;
+
+  _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
+
+  _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
+#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+    if (__is_root()) {
+      throw;
+    } else {
+      __get_recursive_data().__exception = std::current_exception();
+    }
+#  endif
+  }
+};
+
+struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) __gen_aligned_block {
+  char __data[__STDCPP_DEFAULT_NEW_ALIGNMENT__];
+
+  _LIBCPP_HIDE_FROM_ABI static size_t __count(const size_t __size) noexcept {
+    return (__size + sizeof(__gen_aligned_block) - 1) / sizeof(__gen_aligned_block);
+  }
+};
+
+template <class _Allocator>
+concept __gen_stateless_allocator =
+    default_initializable<_Allocator> && allocator_traits<_Allocator>::is_always_equal::value;
+
+template <class _Allocator>
+class __gen_promise_allocator {
+private:
+  using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+  using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+  using __rebind_pointer = allocator_traits<__rebind>::pointer;
+  static_assert(is_pointer_v<__rebind_pointer>);
+
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+    __rebind __rebind_al = static_cast<__rebind>(__al);
+
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      return __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, and the alignment of the
+      // stateful allocator
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+      void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+      return __ptr;
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size)
+    requires default_initializable<_Allocator>
+  {
+    return __alloc({}, __size);
+  }
+
+  template <class _Allocator2, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<_Allocator>(__al), __size);
+  }
+
+  template <class _This, class _Allocator2, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<_Allocator>(__al), __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      __rebind __rebind_al;
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      __rebind __rebind_al = std::move(*__rebind_al_ptr);
+      std::destroy_at(__rebind_al_ptr);
+
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+};
+
+template <>
+class __gen_promise_allocator<void> {
+private:
+  using __dealloc_fn = void (*)(void*, size_t) noexcept;
+
+  template <class _Allocator>
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+    using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+    using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+    using __rebind_pointer = allocator_traits<__rebind>::pointer;
+    static_assert(is_pointer_v<__rebind_pointer>);
+
+    __rebind __rebind_al = static_cast<__rebind>(__al);
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+      void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the deallocation function in the allocated block
+      __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr                    = __dealloc<__rebind>;
+      return __ptr;
+    } else {
+      // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, the alignment of the
+      // stateful allocator, and the deallocation function
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(__rebind) + alignof(__rebind));
+      void* const __ptr = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the deallocation function in the allocated block
+      __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr                    = __dealloc<__rebind>;
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + sizeof(__dealloc_fn) + __size + alignof(__rebind) - 1) &
+          ~(alignof(__rebind) - 1));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+      return __ptr;
+    }
+  }
+
+  template <class _Rebind>
+  _LIBCPP_HIDE_FROM_ABI static void __dealloc(void* const __ptr, size_t __size) noexcept {
+    using __rebind_size_type = allocator_traits<_Rebind>::size_type;
+
+    if constexpr (__gen_stateless_allocator<_Rebind>) {
+      _Rebind __rebind_al;
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      _Rebind* const __rebind_al_ptr = reinterpret_cast<_Rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + sizeof(__dealloc_fn) + alignof(_Rebind) - 1) &
+          ~(alignof(_Rebind) - 1));
+      _Rebind __rebind_al = std::move(*__rebind_al_ptr);
+      std::destroy_at(__rebind_al_ptr);
+
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(_Rebind) + alignof(_Rebind));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size) {
+    void* const __ptr      = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
+      ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+    };
+
+    __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    *__dealloc_ptr                    = __dealloc;
+    return __ptr;
+  }
+
+  template <class _Allocator, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+    return __alloc(__al, __size);
+  }
+
+  template <class _This, class _Allocator, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+    return __alloc(__al, __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
+    __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    const __dealloc_fn __dealloc      = *__dealloc_ptr;
+    __dealloc(__ptr, __size);
+  }
+};
+
+template <class _Ref, class _Val>
+class __gen_iter_provider<_Ref, _Val>::__iterator {
+private:
+  using __val = __gen_val<_Ref, _Val>;
+  using __ref = __gen_ref<_Ref, _Val>;
+
+public:
+  using value_type      = __val;
+  using difference_type = ptrdiff_t;
+
+  _LIBCPP_HIDE_FROM_ABI explicit __iterator(
+      coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine) noexcept
+      : __coroutine_{__coroutine} {}
+
+  _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&& __other) noexcept
+      : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator=(__iterator&& __other) noexcept {
+    __coroutine_ = std::exchange(__other.__coroutine_, {});
+    return *this;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __ref operator*() const noexcept(is_nothrow_copy_constructible_v<__ref>) {
+    return static_cast<__ref>(*__coroutine_.promise().__value_ptr());
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator++() {
+    __coroutine_.promise().__active().resume();
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend bool operator==(const __iterator& __iter, default_sentinel_t) noexcept {
+    return __iter.__coroutine_.done();
+  }
+
+private:
+  coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine_;
+};
+
+template <class _Ref, class _Val = void, class _Allocator = void>
+class generator : public ranges::view_interface<generator<_Ref, _Val, _Allocator>> {
+private:
+  using __val = __gen_val<_Ref, _Val>;
+  static_assert(same_as<remove_cvref_t<__val>, __val> && is_object_v<__val>);
+
+  using __ref = __gen_ref<_Ref, _Val>;
+  static_assert(is_reference_v<__ref> ||
+                (same_as<remove_cvref_t<__ref>, __ref> && is_object_v<__ref> && copy_constructible<__ref>));
+
+  using __rref = conditional_t<is_lvalue_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
+  static_assert(common_reference_with<__ref&&, __val&> && common_reference_with<__ref&&, __rref&&> &&
+                common_reference_with<__rref&&, const __val&>);
+
+  template <class>
+  friend class __gen_promise_base;
+
+public:
+  using yielded = __gen_yielded<_Ref, _Val>;
+
+  struct promise_type : __gen_promise_base<yielded>, __gen_promise_allocator<_Allocator> {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI generator get_return_object() noexcept {
+      return generator{coroutine_handle<promise_type>::from_promise(*this)};
+    }
+  };
+
+  _LIBCPP_HIDE_FROM_ABI generator(const generator&) = delete;
+  _LIBCPP_HIDE_FROM_ABI generator(generator&& __other) noexcept
+      : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+  _LIBCPP_HIDE_FROM_ABI ~generator() {
+    if (__coroutine_) {
+      __coroutine_.destroy();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI generator& operator=(generator __other) noexcept {
+    std::swap(__coroutine_, __other.__coroutine_);
+    return *this;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __gen_iter_provider<_Ref, _Val>::__iterator begin() {
+    auto __coroutine = coroutine_handle<__gen_promise_base<yielded>>::from_promise(__coroutine_.promise());
+    __coroutine.resume();
+    return typename __gen_iter_provider<_Ref, _Val>::__iterator{__coroutine};
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI default_sentinel_t end() const noexcept { return {}; }
+
+private:
+  _LIBCPP_HIDE_FROM_ABI explicit generator(coroutine_handle<promise_type> __coroutine) noexcept
+      : __coroutine_{__coroutine} {}
+
+  coroutine_handle<promise_type> __coroutine_;
+};
+
+namespace pmr {
+
+template <class _Ref, class _Val = void>
+using generator = std::generator<_Ref, _Val, polymorphic_allocator<>>;
+
+} // namespace pmr
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_GENERATOR
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..efb97a047c6e6 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1476,6 +1476,11 @@ module std [system] {
     export *
   }
 
+  module generator {
+    header "generator"
+    export *
+  }
+
   module iomanip {
     header "iomanip"
     export *
diff --git a/libcxx/include/version b/libcxx/include/version
index d4d29b4d31a72..8a73c95551793 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -133,6 +133,7 @@ __cpp_lib_fstream_native_handle                         202306L <fstream>
 __cpp_lib_function_ref                                  202306L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
+__cpp_lib_generator                                     202207L <generator>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
 __cpp_lib_generic_unordered_lookup                      201811L <unordered_map> <unordered_set>
 __cpp_lib_hardware_interference_size                    201703L <new>
@@ -507,6 +508,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_format_ranges                        202207L
 // # define __cpp_lib_formatters                           202302L
 # define __cpp_lib_forward_like                         202207L
+# define __cpp_lib_generator                            202207L
 # define __cpp_lib_invoke_r                             202106L
 # define __cpp_lib_ios_noreplace                        202207L
 # if __has_builtin(__builtin_is_implicit_lifetime)
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index dd7385bf33a42..dada8f56f0224 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -51,9 +51,6 @@ module;
 #  if __has_include(<debugging>)
 #    error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<debugging>)
-#  if __has_include(<generator>)
-#    error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<generator>)
 #  if __has_include(<hazard_pointer>)
 #    error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<hazard_pointer>)
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index 984b18321923c..0c484219c31ce 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -67,6 +67,7 @@ module;
 #include <fstream>
 #include <functional>
 #include <future>
+#include <generator>
 #include <initializer_list>
 #include <iomanip>
 #include <ios>
@@ -135,9 +136,6 @@ module;
 #  if __has_include(<debugging>)
 #    error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<debugging>)
-#  if __has_include(<generator>)
-#    error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<generator>)
 #  if __has_include(<hazard_pointer>)
 #    error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<hazard_pointer>)
diff --git a/libcxx/modules/std/generator.inc b/libcxx/modules/std/generator.inc
index 43fb0daf5c659..8681f4b26dc46 100644
--- a/libcxx/modules/std/generator.inc
+++ b/libcxx/modules/std/generator.inc
@@ -8,7 +8,12 @@
 //===----------------------------------------------------------------------===//
 
 export namespace std {
-#if 0
+#if _LIBCPP_STD_VER >= 23
+  // [coro.generator], class template generator
   using std::generator;
+
+  namespace pmr {
+    using std::pmr::generator;
+  }
 #endif
 } // namespace std
diff --git a/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..9fb717bf9c148
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <generator>
+
+std::generator<int> generate_ints() {
+  co_yield 1;
+  co_yield 2;
+  co_yield 3;
+}
+
+void test() {
+  auto gen = generate_ints();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  gen.begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  gen.end();
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c0031543e47bc..5754f5961dd09 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -945,6 +949,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2032,6 +2052,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c0031543e47bc..5754f5961dd09 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -945,6 +949,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2032,6 +2052,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c2eb5b44e8d7a..c31b86c5f8739 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,15 +125,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -968,6 +972,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2072,6 +2092,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 332cb62f35b5f..50f2d9cf2ac5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -971,6 +975,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2085,6 +2105,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 55c79acff5a8f..0d23c92ec9a1d 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -965,6 +965,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 073f698786117..2fde541964065 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -466,6 +466,15 @@ future string_view
 future tuple
 future typeinfo
 future version
+generator compare
+generator cstdint
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator tuple
+generator typeinfo
+generator version
 initializer_list version
 iomanip cctype
 iomanip cerrno
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index d531d60ae3e35..5fde2907b3327 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -443,6 +443,15 @@ future string_view
 future tuple
 future typeinfo
 future version
+generator compare
+generator cstdint
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator tuple
+generator typeinfo
+generator version
 initializer_list version
 iomanip cctype
 iomanip cerrno
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
new file mode 100644
index 0000000000000..0726f4e59a553
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <generator>
+
+// Test the feature test macros defined by <generator>
+
+// clang-format off
+
+#include <generator>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++23"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++23"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++26"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++26"
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
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 20c16700ef76a..32befec34ed09 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
@@ -412,6 +412,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should not be defined before c++14"
 #  endif
@@ -1351,6 +1355,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++14"
 #  endif
@@ -2413,6 +2421,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++17"
 #  endif
@@ -3742,6 +3754,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++20"
 #  endif
@@ -5266,6 +5282,13 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++23"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++23"
 #  endif
@@ -7144,6 +7167,13 @@
 #    endif
 #  endif
 
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++26"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp b/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
new file mode 100644
index 0000000000000..96c2211ed3138
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <cstddef>
+#include <memory>
+#include <memory_resource>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+template <class T>
+class stateless_allocator {
+public:
+  using value_type = T;
+
+  stateless_allocator() noexcept = default;
+
+  template <typename U>
+  constexpr stateless_allocator(const stateless_allocator<U>&) noexcept {}
+
+  [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
+
+  void deallocate(void* p, size_t) noexcept { ::operator delete(p); }
+
+  template <class U>
+  constexpr bool operator==(const stateless_allocator<U>&) const noexcept {
+    return true;
+  }
+};
+
+template <class T>
+class stateful_allocator {
+public:
+  using value_type = T;
+
+  stateful_allocator() noexcept = default;
+
+  template <typename U>
+  constexpr stateful_allocator(const stateful_allocator<U>&) noexcept {}
+
+  [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
+
+  void deallocate(void* p, size_t) noexcept { ::operator delete(p); }
+
+  template <class U>
+  constexpr bool operator==(const stateful_allocator<U>& other) const noexcept {
+    return state_ == other.state_;
+  }
+
+private:
+  int state_ = 0;
+};
+
+template <class Allocator>
+bool test_with_allocator() {
+  std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+  {
+    auto fib = []() -> std::generator<int, int, Allocator> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib() | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+
+  {
+    auto fib = [](std::allocator_arg_t, Allocator) -> std::generator<int, int, Allocator> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib(std::allocator_arg, {}) | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+
+  {
+    auto fib = [](std::allocator_arg_t, Allocator) -> std::generator<int> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib(std::allocator_arg, {}) | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+  return true;
+}
+
+bool test() {
+  test_with_allocator<std::allocator<std::byte>>();
+  test_with_allocator<stateless_allocator<std::byte>>();
+  test_with_allocator<stateful_allocator<std::byte>>();
+  test_with_allocator<std::pmr::polymorphic_allocator<std::byte>>();
+
+  test_with_allocator<std::allocator<float>>();
+  test_with_allocator<stateless_allocator<float>>();
+  test_with_allocator<stateful_allocator<float>>();
+  test_with_allocator<std::pmr::polymorphic_allocator<float>>();
+  return true;
+};
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
new file mode 100644
index 0000000000000..769a56021c32b
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+template <class G, class V, class R, class RR>
+constexpr bool conformance() {
+  static_assert(std::ranges::range<G>);
+  static_assert(std::ranges::view<G>);
+  static_assert(std::ranges::input_range<G>);
+  static_assert(!std::ranges::forward_range<G>);
+  static_assert(!std::ranges::borrowed_range<G>);
+
+  static_assert(std::same_as<std::ranges::range_value_t<G>, V>);
+  static_assert(std::same_as<std::ranges::range_reference_t<G>, R>);
+  static_assert(std::same_as<std::ranges::range_rvalue_reference_t<G>, RR>);
+
+  return true;
+}
+
+static_assert(conformance<std::generator<int>, int, int&&, int&&>());
+static_assert(conformance<std::generator<int, int>, int, int, int>());
+
+static_assert(conformance<std::generator<int&>, int, int&, int&&>());
+static_assert(conformance<std::generator<int&, int>, int, int&, int&&>());
+static_assert(conformance<std::generator<const int&>, int, const int&, const int&&>());
+static_assert(conformance<std::generator<const int&, int>, int, const int&, const int&&>());
+
+static_assert(conformance<std::generator<int&&>, int, int&&, int&&>());
+static_assert(conformance<std::generator<int&&, int>, int, int&&, int&&>());
+static_assert(conformance<std::generator<const int&&>, int, const int&&, const int&&>());
+static_assert(conformance<std::generator<const int&&, int>, int, const int&&, const int&&>());
diff --git a/libcxx/test/std/ranges/coro.generator/generator.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
new file mode 100644
index 0000000000000..ed1793382962a
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+std::generator<int> fib() {
+  int a = 0;
+  int b = 1;
+  while (true) {
+    co_yield std::exchange(a, std::exchange(b, a + b));
+  }
+}
+
+std::generator<const int&> range_fib() {
+  co_yield std::ranges::elements_of(std::vector<int>{0, 1});
+  co_yield std::ranges::elements_of(std::vector<int>{1, 2});
+  co_yield std::ranges::elements_of(std::vector<int>{3, 5});
+  co_yield std::ranges::elements_of(std::vector<int>{5, 8});
+}
+
+bool test() {
+  {
+    std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+    {
+      auto fib_vec = fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+    {
+      auto fib_vec = range_fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+  }
+  return true;
+}
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
new file mode 100644
index 0000000000000..36c5b8b65e42e
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <numeric>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+std::generator<int> fib() {
+  int a = 0;
+  int b = 1;
+  while (true) {
+    co_yield std::exchange(a, std::exchange(b, a + b));
+  }
+}
+
+std::generator<int> recursive_fib(int depth) {
+  if (depth == 0) {
+    co_yield std::ranges::elements_of(fib());
+  } else {
+    co_yield std::ranges::elements_of(recursive_fib(depth - 1));
+  }
+};
+
+struct tree_node {
+  tree_node* left;
+  tree_node* right;
+  int element;
+
+  ~tree_node() {
+    delete left;
+    delete right;
+  }
+};
+
+tree_node* build_tree(int depth) {
+  if (depth == 0) {
+    return nullptr;
+  }
+
+  tree_node* root = new tree_node();
+  root->element   = depth;
+  root->left      = build_tree(depth - 1);
+  root->right     = build_tree(depth - 1);
+  return root;
+}
+
+std::generator<int> traversal(tree_node* node) {
+  if (node == nullptr) {
+    co_return;
+  }
+  co_yield std::ranges::elements_of(traversal(node->left));
+  co_yield node->element;
+  co_yield std::ranges::elements_of(traversal(node->right));
+}
+
+bool test() {
+  {
+    std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+    {
+      auto fib_vec = recursive_fib(1) | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+    {
+      auto fib_vec = recursive_fib(42) | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+  }
+  {
+    tree_node* tree_root = build_tree(10);
+    auto node_vec        = traversal(tree_root) | std::ranges::to<std::vector<int>>();
+    assert(node_vec.size() == 1023);
+    assert(std::reduce(node_vec.cbegin(), node_vec.cend()) == 2036);
+    delete tree_root;
+  }
+  return true;
+}
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d8fb575b97b2f..a5db6f1f1650f 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -719,6 +719,11 @@ def add_version_header(tc):
             "headers": ["random"],
             "unimplemented": True,
         },
+        {
+            "name": "__cpp_lib_generator",
+            "values": {"c++23": 202207}, # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
+            "headers": ["generator"],
+        },
         {
             "name": "__cpp_lib_generic_associative_lookup",
             "values": {"c++14": 201304},
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index d06271a7908cc..cc33d32a69201 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -164,7 +164,6 @@ def __hash__(self) -> int:
 # modules will fail to build if a header is added but this list is not updated.
 headers_not_available = list(map(Header, [
     "debugging",
-    "generator",
     "hazard_pointer",
     "inplace_vector",
     "linalg",

>From f2ee26c784c91829799073fe12d1cbd0d281a755 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 22:52:35 +0800
Subject: [PATCH 2/7] release notes & python format

---
 libcxx/docs/ReleaseNotes/23.rst                        | 2 ++
 libcxx/utils/generate_feature_test_macro_components.py | 4 +++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 03dabc54dcf79..a53fad86004a6 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -51,6 +51,8 @@ Implemented Papers
 - P2542R8: ``views::concat`` (`Github <https://llvm.org/PR105419>`__)
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
+- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index a5db6f1f1650f..ea2e43b4c15a8 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -721,7 +721,9 @@ def add_version_header(tc):
         },
         {
             "name": "__cpp_lib_generator",
-            "values": {"c++23": 202207}, # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
+            "values": {
+                "c++23": 202207
+            },  # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
             "headers": ["generator"],
         },
         {

>From 19e5608ff21db2369e7172201d55bc372e48d67b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:27:41 +0800
Subject: [PATCH 3/7] test

---
 libcxx/include/generator | 109 +++++++++++++++++++++------------------
 1 file changed, 60 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index d3f942b089002..7792b65a6ae43 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -25,52 +25,56 @@ namespace std {
 
 */
 
-#include <__algorithm/max.h>
-#include <__assert>
-#include <__concepts/common_reference_with.h>
-#include <__concepts/constructible.h>
-#include <__concepts/convertible_to.h>
-#include <__concepts/same_as.h>
-#include <__config>
-#include <__coroutine/coroutine_handle.h>
-#include <__coroutine/coroutine_traits.h>
-#include <__coroutine/noop_coroutine_handle.h>
-#include <__coroutine/trivial_awaitables.h>
-#include <__exception/exception_ptr.h>
-#include <__iterator/default_sentinel.h>
-#include <__memory/addressof.h>
-#include <__memory/allocator_arg_t.h>
-#include <__memory/allocator_traits.h>
-#include <__memory/construct_at.h>
-#include <__memory_resource/polymorphic_allocator.h>
-#include <__ranges/access.h>
-#include <__ranges/concepts.h>
-#include <__ranges/elements_of.h>
-#include <__ranges/view_interface.h>
-#include <__type_traits/add_pointer.h>
-#include <__type_traits/common_reference.h>
-#include <__type_traits/conditional.h>
-#include <__type_traits/is_nothrow_constructible.h>
-#include <__type_traits/is_object.h>
-#include <__type_traits/is_pointer.h>
-#include <__type_traits/is_reference.h>
-#include <__type_traits/is_void.h>
-#include <__type_traits/remove_cvref.h>
-#include <__type_traits/remove_reference.h>
-#include <__utility/exchange.h>
-#include <__utility/move.h>
-#include <__utility/swap.h>
-#include <cstdint>
-#include <new>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+#  include <__cxx03/__config>
+#else
+#  include <__config>
+
+#  if _LIBCPP_STD_VER >= 23
+
+#    include <__algorithm/max.h>
+#    include <__assert>
+#    include <__concepts/common_reference_with.h>
+#    include <__concepts/constructible.h>
+#    include <__concepts/convertible_to.h>
+#    include <__concepts/same_as.h>
+#    include <__coroutine/coroutine_handle.h>
+#    include <__coroutine/coroutine_traits.h>
+#    include <__coroutine/noop_coroutine_handle.h>
+#    include <__coroutine/trivial_awaitables.h>
+#    include <__exception/exception_ptr.h>
+#    include <__iterator/default_sentinel.h>
+#    include <__memory/addressof.h>
+#    include <__memory/allocator_arg_t.h>
+#    include <__memory/allocator_traits.h>
+#    include <__memory/construct_at.h>
+#    include <__memory_resource/polymorphic_allocator.h>
+#    include <__ranges/access.h>
+#    include <__ranges/concepts.h>
+#    include <__ranges/elements_of.h>
+#    include <__ranges/view_interface.h>
+#    include <__type_traits/add_pointer.h>
+#    include <__type_traits/common_reference.h>
+#    include <__type_traits/conditional.h>
+#    include <__type_traits/is_nothrow_constructible.h>
+#    include <__type_traits/is_object.h>
+#    include <__type_traits/is_pointer.h>
+#    include <__type_traits/is_reference.h>
+#    include <__type_traits/is_void.h>
+#    include <__type_traits/remove_cvref.h>
+#    include <__type_traits/remove_reference.h>
+#    include <__utility/exchange.h>
+#    include <__utility/move.h>
+#    include <__utility/swap.h>
+#    include <cstdint>
+#    include <new>
+
+#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#      pragma GCC system_header
+#    endif
 
 _LIBCPP_PUSH_MACROS
-#include <__undef_macros>
-
-#if _LIBCPP_STD_VER >= 23
+#    include <__undef_macros>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -309,13 +313,13 @@ public:
   _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
 
   _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
-#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+#    ifndef _LIBCPP_HAS_NO_EXCEPTIONS
     if (__is_root()) {
       throw;
     } else {
       __get_recursive_data().__exception = std::current_exception();
     }
-#  endif
+#    endif
   }
 };
 
@@ -462,9 +466,14 @@ private:
 
 public:
   _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size) {
-    void* const __ptr      = ::operator new(__size + sizeof(__dealloc_fn));
-    __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
+    void* const __ptr = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc =
+        [](void* const __dealloc_ptr, [[maybe_unused]] const size_t __dealloc_size) static noexcept -> void {
+#    ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
+      ::operator delete(__dealloc_ptr);
+#    else
       ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+#    endif
     };
 
     __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
@@ -596,8 +605,10 @@ using generator = std::generator<_Ref, _Val, polymorphic_allocator<>>;
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_STD_VER >= 23
+#  endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_POP_MACROS
 
+#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
 #endif // _LIBCPP_GENERATOR

>From 57bf0686971419520f56a48a34647f302a151cec Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:48:45 +0800
Subject: [PATCH 4/7] test

---
 libcxx/include/generator | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index 7792b65a6ae43..2176ecbaa1e6e 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -30,6 +30,13 @@ namespace std {
 #else
 #  include <__config>
 
+#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#    pragma GCC system_header
+#  endif
+
+_LIBCPP_PUSH_MACROS
+#  include <__undef_macros>
+
 #  if _LIBCPP_STD_VER >= 23
 
 #    include <__algorithm/max.h>
@@ -69,13 +76,6 @@ namespace std {
 #    include <cstdint>
 #    include <new>
 
-#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#      pragma GCC system_header
-#    endif
-
-_LIBCPP_PUSH_MACROS
-#    include <__undef_macros>
-
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Ref, class _Val>
@@ -469,10 +469,10 @@ public:
     void* const __ptr = ::operator new(__size + sizeof(__dealloc_fn));
     __dealloc_fn __dealloc =
         [](void* const __dealloc_ptr, [[maybe_unused]] const size_t __dealloc_size) static noexcept -> void {
-#    ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
-      ::operator delete(__dealloc_ptr);
-#    else
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
       ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+#    else
+      ::operator delete(__dealloc_ptr);
 #    endif
     };
 

>From d4c2ae0fccee3faeb4ed931a82cb11edc4505e2a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:57:26 +0800
Subject: [PATCH 5/7] test

---
 libcxx/test/libcxx/transitive_includes/cxx03.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx11.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx14.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx17.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx20.csv | 16 ----------------
 5 files changed, 80 deletions(-)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 5754f5961dd09..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -949,22 +949,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 5754f5961dd09..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -949,22 +949,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c31b86c5f8739..cfcf7bc7c4c19 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -972,22 +972,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 50f2d9cf2ac5f..534776e6f0526 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -975,22 +975,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 0d23c92ec9a1d..55c79acff5a8f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -965,22 +965,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm

>From 0b062c588aa75fa002ffe2d020da342947f80caf Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 8 Jun 2026 23:21:56 +0800
Subject: [PATCH 6/7] revert some transitive include change

---
 libcxx/test/libcxx/transitive_includes/cxx03.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx11.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx14.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx17.csv | 5 -----
 4 files changed, 20 deletions(-)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index cfcf7bc7c4c19..c2eb5b44e8d7a 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,19 +125,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2076,7 +2072,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 534776e6f0526..332cb62f35b5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2089,7 +2085,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm

>From 90c7db75736b52393c458af97b92a6dc4617ca7f Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 20:18:04 +0800
Subject: [PATCH 7/7] release notes 23 -> 24

---
 libcxx/docs/ReleaseNotes/23.rst | 5 -----
 libcxx/docs/ReleaseNotes/24.rst | 2 ++
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e18f46f6e467d..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -54,12 +54,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
-<<<<<<< HEAD
-- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
-- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
-=======
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
->>>>>>> main
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..55b39c7530f6a 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,8 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
+- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
 
 Improvements and New Features
 -----------------------------


From libcxx-commits at lists.llvm.org  Mon Jul 20 05:22:49 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Mon, 20 Jul 2026 05:22:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2502R2: `std::generator` (PR
	#201854)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201854@github.com>
Message-ID: <6a5e1319.4987c784.2c60a9.7808@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/201854

>From 22f5fee287889d72cb171751fdc0993197c69b6b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 22:45:16 +0800
Subject: [PATCH 1/8] implement generator

---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/Status/Cxx23Issues.csv            |   2 +-
 libcxx/docs/Status/Cxx23Papers.csv            |   4 +-
 libcxx/docs/Status/Cxx2cIssues.csv            |   8 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/generator                      | 603 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   5 +
 libcxx/include/version                        |   2 +
 libcxx/modules/std.compat.cppm.in             |   3 -
 libcxx/modules/std.cppm.in                    |   4 +-
 libcxx/modules/std/generator.inc              |   7 +-
 .../coro.generator/nodiscard.verify.cpp       |  29 +
 .../test/libcxx/transitive_includes/cxx03.csv |  21 +
 .../test/libcxx/transitive_includes/cxx11.csv |  21 +
 .../test/libcxx/transitive_includes/cxx14.csv |  21 +
 .../test/libcxx/transitive_includes/cxx17.csv |  21 +
 .../test/libcxx/transitive_includes/cxx20.csv |  16 +
 .../test/libcxx/transitive_includes/cxx23.csv |   9 +
 .../test/libcxx/transitive_includes/cxx26.csv |   9 +
 .../generator.version.compile.pass.cpp        |  65 ++
 .../version.version.compile.pass.cpp          |  30 +
 .../ranges/coro.generator/allocator.pass.cpp  | 123 ++++
 .../coro.generator/generator.compile.pass.cpp |  48 ++
 .../ranges/coro.generator/generator.pass.cpp  |  56 ++
 .../ranges/coro.generator/recursive.pass.cpp  |  97 +++
 .../generate_feature_test_macro_components.py |   5 +
 libcxx/utils/libcxx/header_information.py     |   1 -
 27 files changed, 1198 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/include/generator
 create mode 100644 libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/generator.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/recursive.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 1b748e37293df..46d83644ff10f 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -344,6 +344,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_forward_like``                                 ``202207L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_generator``                                    ``202207L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_invoke_r``                                     ``202106L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ios_noreplace``                                ``202207L``
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0a2f0d59484de..b61edbd0a2975 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -217,7 +217,7 @@
 "`LWG3759 <https://wg21.link/LWG3759>`__","``ranges::rotate_copy`` should use ``std::move``","2022-11 (Kona)","|Complete|","15","`#105027 <https://github.com/llvm/llvm-project/issues/105027>`__",""
 "`LWG3760 <https://wg21.link/LWG3760>`__","``cartesian_product_view::iterator``'s ``parent_`` is never valid","2022-11 (Kona)","","","`#105028 <https://github.com/llvm/llvm-project/issues/105028>`__",""
 "`LWG3761 <https://wg21.link/LWG3761>`__","``cartesian_product_view::iterator::operator-`` should pass by reference","2022-11 (Kona)","","","`#105029 <https://github.com/llvm/llvm-project/issues/105029>`__",""
-"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","","","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
+"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","23","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
 "`LWG3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept","2022-11 (Kona)","|Complete|","17","`#105031 <https://github.com/llvm/llvm-project/issues/105031>`__",""
 "`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","","","`#105032 <https://github.com/llvm/llvm-project/issues/105032>`__",""
 "`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","","`#105033 <https://github.com/llvm/llvm-project/issues/105033>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index eb580ea891f5b..862461f900dbd 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -80,7 +80,7 @@
 "`P2474R2 <https://wg21.link/P2474R2>`__","``views::repeat``","2022-07 (Virtual)","|Complete|","17","`#105222 <https://github.com/llvm/llvm-project/issues/105222>`__",""
 "`P2494R2 <https://wg21.link/P2494R2>`__","Relaxing range adaptors to allow for move only types","2022-07 (Virtual)","|Complete|","17","`#105224 <https://github.com/llvm/llvm-project/issues/105224>`__",""
 "`P2499R0 <https://wg21.link/P2499R0>`__","``string_view`` range constructor should be ``explicit``","2022-07 (Virtual)","|Complete|","16","`#105225 <https://github.com/llvm/llvm-project/issues/105225>`__",""
-"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","","","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
+"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","23","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
 "`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","2022-07 (Virtual)","|Complete|","15","`#105227 <https://github.com/llvm/llvm-project/issues/105227>`__",""
 "`P2517R1 <https://wg21.link/P2517R1>`__","Add a conditional ``noexcept`` specification to ``std::apply``","2022-07 (Virtual)","|Complete|","3.9","`#105229 <https://github.com/llvm/llvm-project/issues/105229>`__",""
 "`P2520R0 <https://wg21.link/P2520R0>`__","``move_iterator`` should be a random access iterator","2022-07 (Virtual)","|Complete|","17","`#105230 <https://github.com/llvm/llvm-project/issues/105230>`__","Implemented as a DR in C++20"
@@ -115,7 +115,7 @@
 "`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20","`#105259 <https://github.com/llvm/llvm-project/issues/105259>`__",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","`#105260 <https://github.com/llvm/llvm-project/issues/105260>`__","The paper is implemented as a DR to C++20"
 "`P2652R2 <https://wg21.link/P2652R2>`__","Disallow User Specialization of ``allocator_traits``","2023-02 (Issaquah)","|Complete|","19","`#105262 <https://github.com/llvm/llvm-project/issues/105262>`__",""
-"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","","","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
+"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","23","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
 "`P2614R2 <https://wg21.link/P2614R2>`__","Deprecate ``numeric_limits::has_denorm``","2023-02 (Issaquah)","|Complete|","18","`#105264 <https://github.com/llvm/llvm-project/issues/105264>`__",""
 "`P2588R3 <https://wg21.link/P2588R3>`__","``barrier``’s phase completion guarantees","2023-02 (Issaquah)","","","`#105265 <https://github.com/llvm/llvm-project/issues/105265>`__",""
 "`P2763R1 <https://wg21.link/P2763R1>`__","``layout_stride`` static extents default constructor fix","2023-02 (Issaquah)","","","`#105266 <https://github.com/llvm/llvm-project/issues/105266>`__",""
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 6a836c0491bb6..3ec8ae821ed5d 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -4,7 +4,7 @@
 "`LWG3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","2023-06 (Varna)","|Nothing To Do|","","`#105270 <https://github.com/llvm/llvm-project/issues/105270>`__",""
 "`LWG3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","2023-06 (Varna)","|Complete|","19","`#105271 <https://github.com/llvm/llvm-project/issues/105271>`__",""
 "`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","","","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
-"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","","","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
+"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","23","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
 "`LWG3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 <https://github.com/llvm/llvm-project/issues/105275>`__",""
 "`LWG3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 <https://github.com/llvm/llvm-project/issues/105276>`__",""
 "`LWG3905 <https://wg21.link/LWG3905>`__","Type of ``std::fexcept_t``","2023-06 (Varna)","|Complete|","3.4","`#105277 <https://github.com/llvm/llvm-project/issues/105277>`__",""
@@ -79,8 +79,8 @@
 "`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","","`#118332 <https://github.com/llvm/llvm-project/issues/118332>`__",""
 "`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","","`#118335 <https://github.com/llvm/llvm-project/issues/118335>`__",""
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
-"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
-"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
+"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","23","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
+"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","|Complete|","23","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
 "`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
@@ -93,7 +93,7 @@
 "`LWG4088 <https://wg21.link/LWG4088>`__","``println`` ignores the locale imbued in ``std::ostream``","2024-11 (Wrocław)","|Complete|","18","`#118348 <https://github.com/llvm/llvm-project/issues/118348>`__",""
 "`LWG4112 <https://wg21.link/LWG4112>`__","``has-arrow`` should required ``operator->()`` to be ``const``-qualified","2024-11 (Wrocław)","","","`#118349 <https://github.com/llvm/llvm-project/issues/118349>`__",""
 "`LWG4113 <https://wg21.link/LWG4113>`__","Disallow ``has_unique_object_representations<Incomplete[]>``","2024-11 (Wrocław)","|Complete|","","`#118350 <https://github.com/llvm/llvm-project/issues/118350>`__",""
-"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","","","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
+"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","23","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
 "`LWG4124 <https://wg21.link/LWG4124>`__","Cannot format ``zoned_time`` with resolution coarser than ``seconds``","2024-11 (Wrocław)","","","`#118352 <https://github.com/llvm/llvm-project/issues/118352>`__",""
 "`LWG4126 <https://wg21.link/LWG4126>`__","Some feature-test macros for fully freestanding features are not yet marked freestanding","2024-11 (Wrocław)","","","`#118353 <https://github.com/llvm/llvm-project/issues/118353>`__",""
 "`LWG4134 <https://wg21.link/LWG4134>`__","Issue with Philox algorithm specification","2024-11 (Wrocław)","","","`#118354 <https://github.com/llvm/llvm-project/issues/118354>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..cd77d51924571 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1043,6 +1043,7 @@ set(files
   fstream
   functional
   future
+  generator
   initializer_list
   iomanip
   ios
diff --git a/libcxx/include/generator b/libcxx/include/generator
new file mode 100644
index 0000000000000..d3f942b089002
--- /dev/null
+++ b/libcxx/include/generator
@@ -0,0 +1,603 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_GENERATOR
+#define _LIBCPP_GENERATOR
+
+/*
+
+namespace std {
+  // [coro.generator.class], class template generator
+  template<class Ref, class Val = void, class Allocator = void>
+    class generator;
+
+  namespace pmr {
+    template<class Ref, class Val = void>
+      using generator = std::generator<Ref, Val, polymorphic_allocator<>>;
+  }
+}
+
+*/
+
+#include <__algorithm/max.h>
+#include <__assert>
+#include <__concepts/common_reference_with.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/same_as.h>
+#include <__config>
+#include <__coroutine/coroutine_handle.h>
+#include <__coroutine/coroutine_traits.h>
+#include <__coroutine/noop_coroutine_handle.h>
+#include <__coroutine/trivial_awaitables.h>
+#include <__exception/exception_ptr.h>
+#include <__iterator/default_sentinel.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_arg_t.h>
+#include <__memory/allocator_traits.h>
+#include <__memory/construct_at.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/elements_of.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/common_reference.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/exchange.h>
+#include <__utility/move.h>
+#include <__utility/swap.h>
+#include <cstdint>
+#include <new>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 23
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Ref, class _Val>
+using __gen_val = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
+
+template <class _Ref, class _Val>
+using __gen_ref = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
+
+template <class _Ref, class _Val>
+using __gen_yielded =
+    conditional_t<is_reference_v<__gen_ref<_Ref, _Val>>, __gen_ref<_Ref, _Val>, const __gen_ref<_Ref, _Val>&>;
+
+template <class, class>
+struct __gen_iter_provider {
+  class __iterator;
+};
+
+template <class, class, class>
+class generator;
+
+template <class _Yielded>
+class __gen_promise_base {
+private:
+  template <class, class, class>
+  friend class generator;
+
+  template <class, class>
+  friend struct __gen_iter_provider;
+
+  // Each promise object stores either a `__root_data` when associated with a root generator, or a `__recursive_data`
+  // when associated with a generator that is yielded recursively.
+  struct __root_data {
+    // The client code has access only to the iterator of the root generator. Thus, the root generator must store the
+    // yielded values of recursively-yielded generators, which will then be returned when the client code dereferences
+    // the iterator.
+    add_pointer_t<_Yielded> __value_ptr;
+    // The client code has access only to the iterator of the root generator. Thus, the root generator needs to identify
+    // which generator is currently active. This active generator will then be resumed when the client code increments
+    // the iterator.
+    coroutine_handle<__gen_promise_base> __active;
+  };
+
+  struct __recursive_data {
+    exception_ptr __exception;
+    coroutine_handle<__gen_promise_base> __parent;
+    coroutine_handle<__gen_promise_base> __root;
+  };
+
+  union __union {
+    __root_data __root;
+    __recursive_data __recursive;
+
+    _LIBCPP_HIDE_FROM_ABI ~__union() noexcept {}
+  } __data_;
+
+  // The `__tag_` stores the active member of the `__data_` union:
+  // - `false` indicates that the active member is `__root`.
+  // - `true` indicates that the active member is `__recursive`.
+  // This field can be omitted because `__recursive_data.__root` can store the active member.
+  bool __tag_;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_root() noexcept { return !__tag_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __root_data& __get_root_data() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __data_.__root;
+  }
+  _LIBCPP_HIDE_FROM_ABI void __set_root_tag() noexcept { __tag_ = false; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_recursive() noexcept { return __tag_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __recursive_data& __get_recursive_data() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_recursive(), "the active member of `__data_` is not `__recursive`");
+    return __data_.__recursive;
+  }
+  _LIBCPP_HIDE_FROM_ABI void __set_recursive_tag() noexcept { __tag_ = true; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI coroutine_handle<__gen_promise_base>& __active() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __get_root_data().__active;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __value_ptr() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __get_root_data().__value_ptr;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __root_value_ptr() noexcept {
+    if (__is_root()) {
+      return __value_ptr();
+    }
+    return __get_recursive_data().__root.promise().__value_ptr();
+  }
+
+  struct __element_awaiter {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI void await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      __current.promise().__root_value_ptr() = std::addressof(__value);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+
+    remove_cvref_t<_Yielded> __value;
+  };
+
+  struct __final_awaiter {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI coroutine_handle<> await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      // Checks if the current generator is recursively-yielded
+      if (__current.promise().__is_recursive()) {
+        auto&& __recursive_data = __current.promise().__get_recursive_data();
+        auto __parent           = __recursive_data.__parent;
+        // Updates the active generator to its parent, allowing the client code to resume it later
+        __recursive_data.__root.promise().__active() = __parent;
+        // Transfers execution to its parent, which is the generator that yields it
+        return __parent;
+      }
+      return std::noop_coroutine();
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+  };
+
+  template <class _Ref, class _Val, class _Allocator>
+  struct __recursive_awaiter {
+    _LIBCPP_HIDE_FROM_ABI explicit __recursive_awaiter(generator<_Ref, _Val, _Allocator>&& __gen) noexcept
+        : __gen_{std::move(__gen)} {}
+
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return !__gen_.__coroutine_; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI coroutine_handle<__gen_promise_base>
+    await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      // Stores a `__recursive_data` in the promise object associated with `__gen`
+      auto __recursive = coroutine_handle<__gen_promise_base>::from_address(__gen_.__coroutine_.address());
+      __recursive.promise().__set_recursive_tag();
+      auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+
+      // Sets `__recursive_data.__parent` to the current generator
+      auto __parent             = coroutine_handle<__gen_promise_base>::from_address(__current.address());
+      __recursive_data.__parent = __parent;
+
+      // Sets `__recursive_data.__root` to the current generator if it's a root generator, or to the root generator of
+      // the current generator otherwise
+      if (__parent.promise().__is_recursive()) {
+        __recursive_data.__root = __parent.promise().__get_recursive_data().__root;
+      } else {
+        __recursive_data.__root = __parent;
+      }
+
+      // Updates the active generator to `__gen`, allowing the client code to resume it later
+      __recursive_data.__root.promise().__active() = __recursive;
+
+      // Transfers execution to `__gen`
+      return __recursive;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() {
+      auto __recursive        = coroutine_handle<__gen_promise_base>::from_address(__gen_.__coroutine_.address());
+      auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+      if (__recursive_data.__exception) {
+        std::rethrow_exception(std::move(__recursive_data.__exception));
+      }
+    }
+
+  private:
+    generator<_Ref, _Val, _Allocator> __gen_;
+  };
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __gen_promise_base() noexcept
+      : __data_{.__root{
+            .__value_ptr{nullptr},
+            .__active{coroutine_handle<__gen_promise_base>::from_promise(*this)},
+        }},
+        __tag_{false} {}
+
+  _LIBCPP_HIDE_FROM_ABI ~__gen_promise_base() noexcept {
+    if (__is_root()) {
+      __data_.__root.~__root_data();
+    } else {
+      __data_.__recursive.~__recursive_data();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI suspend_always initial_suspend() const noexcept { return {}; }
+
+  _LIBCPP_HIDE_FROM_ABI __final_awaiter final_suspend() noexcept { return {}; }
+
+  _LIBCPP_HIDE_FROM_ABI suspend_always yield_value(_Yielded __value) noexcept {
+    __root_value_ptr() = std::addressof(__value);
+    return {};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI auto yield_value(const remove_reference_t<_Yielded>& __value) noexcept(
+      is_nothrow_constructible_v<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>)
+    requires is_rvalue_reference_v<_Yielded> &&
+             constructible_from<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>
+  {
+    return __element_awaiter{.__value{__value}};
+  }
+
+  template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
+    requires same_as<__gen_yielded<_Ref2, _Val2>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto
+  yield_value(ranges::elements_of<generator<_Ref2, _Val2, _Allocator2>&&, _Unused> __elements) noexcept {
+    return __recursive_awaiter<_Ref2, _Val2, _Allocator2>{std::move(__elements.range)};
+  }
+
+  template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
+    requires same_as<__gen_yielded<_Ref2, _Val2>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto
+  yield_value(ranges::elements_of<generator<_Ref2, _Val2, _Allocator2>&, _Unused> __elements) noexcept {
+    return __recursive_awaiter<_Ref2, _Val2, _Allocator2>{std::move(__elements.range)};
+  }
+
+  template <ranges::input_range _Range, class _Allocator>
+    requires convertible_to<ranges::range_reference_t<_Range>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto yield_value(ranges::elements_of<_Range, _Allocator> __range) {
+    auto __lambda =
+        [](allocator_arg_t, _Allocator, ranges::iterator_t<_Range> __i, ranges::sentinel_t<_Range> __s) static
+        -> generator<_Yielded, void, _Allocator> {
+      for (; __i != __s; ++__i) {
+        co_yield static_cast<_Yielded>(*__i);
+      }
+    };
+    return yield_value(ranges::elements_of(
+        __lambda(allocator_arg, __range.allocator, ranges::begin(__range.range), ranges::end(__range.range))));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void await_transform() = delete;
+
+  _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
+
+  _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
+#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+    if (__is_root()) {
+      throw;
+    } else {
+      __get_recursive_data().__exception = std::current_exception();
+    }
+#  endif
+  }
+};
+
+struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) __gen_aligned_block {
+  char __data[__STDCPP_DEFAULT_NEW_ALIGNMENT__];
+
+  _LIBCPP_HIDE_FROM_ABI static size_t __count(const size_t __size) noexcept {
+    return (__size + sizeof(__gen_aligned_block) - 1) / sizeof(__gen_aligned_block);
+  }
+};
+
+template <class _Allocator>
+concept __gen_stateless_allocator =
+    default_initializable<_Allocator> && allocator_traits<_Allocator>::is_always_equal::value;
+
+template <class _Allocator>
+class __gen_promise_allocator {
+private:
+  using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+  using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+  using __rebind_pointer = allocator_traits<__rebind>::pointer;
+  static_assert(is_pointer_v<__rebind_pointer>);
+
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+    __rebind __rebind_al = static_cast<__rebind>(__al);
+
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      return __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, and the alignment of the
+      // stateful allocator
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+      void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+      return __ptr;
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size)
+    requires default_initializable<_Allocator>
+  {
+    return __alloc({}, __size);
+  }
+
+  template <class _Allocator2, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<_Allocator>(__al), __size);
+  }
+
+  template <class _This, class _Allocator2, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<_Allocator>(__al), __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      __rebind __rebind_al;
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      __rebind __rebind_al = std::move(*__rebind_al_ptr);
+      std::destroy_at(__rebind_al_ptr);
+
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+};
+
+template <>
+class __gen_promise_allocator<void> {
+private:
+  using __dealloc_fn = void (*)(void*, size_t) noexcept;
+
+  template <class _Allocator>
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+    using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+    using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+    using __rebind_pointer = allocator_traits<__rebind>::pointer;
+    static_assert(is_pointer_v<__rebind_pointer>);
+
+    __rebind __rebind_al = static_cast<__rebind>(__al);
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+      void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the deallocation function in the allocated block
+      __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr                    = __dealloc<__rebind>;
+      return __ptr;
+    } else {
+      // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, the alignment of the
+      // stateful allocator, and the deallocation function
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(__rebind) + alignof(__rebind));
+      void* const __ptr = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the deallocation function in the allocated block
+      __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr                    = __dealloc<__rebind>;
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + sizeof(__dealloc_fn) + __size + alignof(__rebind) - 1) &
+          ~(alignof(__rebind) - 1));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+      return __ptr;
+    }
+  }
+
+  template <class _Rebind>
+  _LIBCPP_HIDE_FROM_ABI static void __dealloc(void* const __ptr, size_t __size) noexcept {
+    using __rebind_size_type = allocator_traits<_Rebind>::size_type;
+
+    if constexpr (__gen_stateless_allocator<_Rebind>) {
+      _Rebind __rebind_al;
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      _Rebind* const __rebind_al_ptr = reinterpret_cast<_Rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + sizeof(__dealloc_fn) + alignof(_Rebind) - 1) &
+          ~(alignof(_Rebind) - 1));
+      _Rebind __rebind_al = std::move(*__rebind_al_ptr);
+      std::destroy_at(__rebind_al_ptr);
+
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(_Rebind) + alignof(_Rebind));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size) {
+    void* const __ptr      = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
+      ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+    };
+
+    __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    *__dealloc_ptr                    = __dealloc;
+    return __ptr;
+  }
+
+  template <class _Allocator, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+    return __alloc(__al, __size);
+  }
+
+  template <class _This, class _Allocator, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+    return __alloc(__al, __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
+    __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    const __dealloc_fn __dealloc      = *__dealloc_ptr;
+    __dealloc(__ptr, __size);
+  }
+};
+
+template <class _Ref, class _Val>
+class __gen_iter_provider<_Ref, _Val>::__iterator {
+private:
+  using __val = __gen_val<_Ref, _Val>;
+  using __ref = __gen_ref<_Ref, _Val>;
+
+public:
+  using value_type      = __val;
+  using difference_type = ptrdiff_t;
+
+  _LIBCPP_HIDE_FROM_ABI explicit __iterator(
+      coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine) noexcept
+      : __coroutine_{__coroutine} {}
+
+  _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&& __other) noexcept
+      : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator=(__iterator&& __other) noexcept {
+    __coroutine_ = std::exchange(__other.__coroutine_, {});
+    return *this;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __ref operator*() const noexcept(is_nothrow_copy_constructible_v<__ref>) {
+    return static_cast<__ref>(*__coroutine_.promise().__value_ptr());
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator++() {
+    __coroutine_.promise().__active().resume();
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend bool operator==(const __iterator& __iter, default_sentinel_t) noexcept {
+    return __iter.__coroutine_.done();
+  }
+
+private:
+  coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine_;
+};
+
+template <class _Ref, class _Val = void, class _Allocator = void>
+class generator : public ranges::view_interface<generator<_Ref, _Val, _Allocator>> {
+private:
+  using __val = __gen_val<_Ref, _Val>;
+  static_assert(same_as<remove_cvref_t<__val>, __val> && is_object_v<__val>);
+
+  using __ref = __gen_ref<_Ref, _Val>;
+  static_assert(is_reference_v<__ref> ||
+                (same_as<remove_cvref_t<__ref>, __ref> && is_object_v<__ref> && copy_constructible<__ref>));
+
+  using __rref = conditional_t<is_lvalue_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
+  static_assert(common_reference_with<__ref&&, __val&> && common_reference_with<__ref&&, __rref&&> &&
+                common_reference_with<__rref&&, const __val&>);
+
+  template <class>
+  friend class __gen_promise_base;
+
+public:
+  using yielded = __gen_yielded<_Ref, _Val>;
+
+  struct promise_type : __gen_promise_base<yielded>, __gen_promise_allocator<_Allocator> {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI generator get_return_object() noexcept {
+      return generator{coroutine_handle<promise_type>::from_promise(*this)};
+    }
+  };
+
+  _LIBCPP_HIDE_FROM_ABI generator(const generator&) = delete;
+  _LIBCPP_HIDE_FROM_ABI generator(generator&& __other) noexcept
+      : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+  _LIBCPP_HIDE_FROM_ABI ~generator() {
+    if (__coroutine_) {
+      __coroutine_.destroy();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI generator& operator=(generator __other) noexcept {
+    std::swap(__coroutine_, __other.__coroutine_);
+    return *this;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __gen_iter_provider<_Ref, _Val>::__iterator begin() {
+    auto __coroutine = coroutine_handle<__gen_promise_base<yielded>>::from_promise(__coroutine_.promise());
+    __coroutine.resume();
+    return typename __gen_iter_provider<_Ref, _Val>::__iterator{__coroutine};
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI default_sentinel_t end() const noexcept { return {}; }
+
+private:
+  _LIBCPP_HIDE_FROM_ABI explicit generator(coroutine_handle<promise_type> __coroutine) noexcept
+      : __coroutine_{__coroutine} {}
+
+  coroutine_handle<promise_type> __coroutine_;
+};
+
+namespace pmr {
+
+template <class _Ref, class _Val = void>
+using generator = std::generator<_Ref, _Val, polymorphic_allocator<>>;
+
+} // namespace pmr
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_GENERATOR
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..efb97a047c6e6 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1476,6 +1476,11 @@ module std [system] {
     export *
   }
 
+  module generator {
+    header "generator"
+    export *
+  }
+
   module iomanip {
     header "iomanip"
     export *
diff --git a/libcxx/include/version b/libcxx/include/version
index d4d29b4d31a72..8a73c95551793 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -133,6 +133,7 @@ __cpp_lib_fstream_native_handle                         202306L <fstream>
 __cpp_lib_function_ref                                  202306L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
+__cpp_lib_generator                                     202207L <generator>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
 __cpp_lib_generic_unordered_lookup                      201811L <unordered_map> <unordered_set>
 __cpp_lib_hardware_interference_size                    201703L <new>
@@ -507,6 +508,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_format_ranges                        202207L
 // # define __cpp_lib_formatters                           202302L
 # define __cpp_lib_forward_like                         202207L
+# define __cpp_lib_generator                            202207L
 # define __cpp_lib_invoke_r                             202106L
 # define __cpp_lib_ios_noreplace                        202207L
 # if __has_builtin(__builtin_is_implicit_lifetime)
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index dd7385bf33a42..dada8f56f0224 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -51,9 +51,6 @@ module;
 #  if __has_include(<debugging>)
 #    error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<debugging>)
-#  if __has_include(<generator>)
-#    error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<generator>)
 #  if __has_include(<hazard_pointer>)
 #    error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<hazard_pointer>)
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index 984b18321923c..0c484219c31ce 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -67,6 +67,7 @@ module;
 #include <fstream>
 #include <functional>
 #include <future>
+#include <generator>
 #include <initializer_list>
 #include <iomanip>
 #include <ios>
@@ -135,9 +136,6 @@ module;
 #  if __has_include(<debugging>)
 #    error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<debugging>)
-#  if __has_include(<generator>)
-#    error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<generator>)
 #  if __has_include(<hazard_pointer>)
 #    error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<hazard_pointer>)
diff --git a/libcxx/modules/std/generator.inc b/libcxx/modules/std/generator.inc
index 43fb0daf5c659..8681f4b26dc46 100644
--- a/libcxx/modules/std/generator.inc
+++ b/libcxx/modules/std/generator.inc
@@ -8,7 +8,12 @@
 //===----------------------------------------------------------------------===//
 
 export namespace std {
-#if 0
+#if _LIBCPP_STD_VER >= 23
+  // [coro.generator], class template generator
   using std::generator;
+
+  namespace pmr {
+    using std::pmr::generator;
+  }
 #endif
 } // namespace std
diff --git a/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..9fb717bf9c148
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <generator>
+
+std::generator<int> generate_ints() {
+  co_yield 1;
+  co_yield 2;
+  co_yield 3;
+}
+
+void test() {
+  auto gen = generate_ints();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  gen.begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  gen.end();
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c0031543e47bc..5754f5961dd09 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -945,6 +949,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2032,6 +2052,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c0031543e47bc..5754f5961dd09 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -945,6 +949,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2032,6 +2052,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c2eb5b44e8d7a..c31b86c5f8739 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,15 +125,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -968,6 +972,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2072,6 +2092,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 332cb62f35b5f..50f2d9cf2ac5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -971,6 +975,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2085,6 +2105,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 55c79acff5a8f..0d23c92ec9a1d 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -965,6 +965,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 073f698786117..2fde541964065 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -466,6 +466,15 @@ future string_view
 future tuple
 future typeinfo
 future version
+generator compare
+generator cstdint
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator tuple
+generator typeinfo
+generator version
 initializer_list version
 iomanip cctype
 iomanip cerrno
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index d531d60ae3e35..5fde2907b3327 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -443,6 +443,15 @@ future string_view
 future tuple
 future typeinfo
 future version
+generator compare
+generator cstdint
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator tuple
+generator typeinfo
+generator version
 initializer_list version
 iomanip cctype
 iomanip cerrno
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
new file mode 100644
index 0000000000000..0726f4e59a553
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <generator>
+
+// Test the feature test macros defined by <generator>
+
+// clang-format off
+
+#include <generator>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++23"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++23"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++26"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++26"
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
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 20c16700ef76a..32befec34ed09 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
@@ -412,6 +412,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should not be defined before c++14"
 #  endif
@@ -1351,6 +1355,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++14"
 #  endif
@@ -2413,6 +2421,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++17"
 #  endif
@@ -3742,6 +3754,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++20"
 #  endif
@@ -5266,6 +5282,13 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++23"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++23"
 #  endif
@@ -7144,6 +7167,13 @@
 #    endif
 #  endif
 
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++26"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp b/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
new file mode 100644
index 0000000000000..96c2211ed3138
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <cstddef>
+#include <memory>
+#include <memory_resource>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+template <class T>
+class stateless_allocator {
+public:
+  using value_type = T;
+
+  stateless_allocator() noexcept = default;
+
+  template <typename U>
+  constexpr stateless_allocator(const stateless_allocator<U>&) noexcept {}
+
+  [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
+
+  void deallocate(void* p, size_t) noexcept { ::operator delete(p); }
+
+  template <class U>
+  constexpr bool operator==(const stateless_allocator<U>&) const noexcept {
+    return true;
+  }
+};
+
+template <class T>
+class stateful_allocator {
+public:
+  using value_type = T;
+
+  stateful_allocator() noexcept = default;
+
+  template <typename U>
+  constexpr stateful_allocator(const stateful_allocator<U>&) noexcept {}
+
+  [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
+
+  void deallocate(void* p, size_t) noexcept { ::operator delete(p); }
+
+  template <class U>
+  constexpr bool operator==(const stateful_allocator<U>& other) const noexcept {
+    return state_ == other.state_;
+  }
+
+private:
+  int state_ = 0;
+};
+
+template <class Allocator>
+bool test_with_allocator() {
+  std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+  {
+    auto fib = []() -> std::generator<int, int, Allocator> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib() | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+
+  {
+    auto fib = [](std::allocator_arg_t, Allocator) -> std::generator<int, int, Allocator> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib(std::allocator_arg, {}) | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+
+  {
+    auto fib = [](std::allocator_arg_t, Allocator) -> std::generator<int> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib(std::allocator_arg, {}) | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+  return true;
+}
+
+bool test() {
+  test_with_allocator<std::allocator<std::byte>>();
+  test_with_allocator<stateless_allocator<std::byte>>();
+  test_with_allocator<stateful_allocator<std::byte>>();
+  test_with_allocator<std::pmr::polymorphic_allocator<std::byte>>();
+
+  test_with_allocator<std::allocator<float>>();
+  test_with_allocator<stateless_allocator<float>>();
+  test_with_allocator<stateful_allocator<float>>();
+  test_with_allocator<std::pmr::polymorphic_allocator<float>>();
+  return true;
+};
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
new file mode 100644
index 0000000000000..769a56021c32b
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+template <class G, class V, class R, class RR>
+constexpr bool conformance() {
+  static_assert(std::ranges::range<G>);
+  static_assert(std::ranges::view<G>);
+  static_assert(std::ranges::input_range<G>);
+  static_assert(!std::ranges::forward_range<G>);
+  static_assert(!std::ranges::borrowed_range<G>);
+
+  static_assert(std::same_as<std::ranges::range_value_t<G>, V>);
+  static_assert(std::same_as<std::ranges::range_reference_t<G>, R>);
+  static_assert(std::same_as<std::ranges::range_rvalue_reference_t<G>, RR>);
+
+  return true;
+}
+
+static_assert(conformance<std::generator<int>, int, int&&, int&&>());
+static_assert(conformance<std::generator<int, int>, int, int, int>());
+
+static_assert(conformance<std::generator<int&>, int, int&, int&&>());
+static_assert(conformance<std::generator<int&, int>, int, int&, int&&>());
+static_assert(conformance<std::generator<const int&>, int, const int&, const int&&>());
+static_assert(conformance<std::generator<const int&, int>, int, const int&, const int&&>());
+
+static_assert(conformance<std::generator<int&&>, int, int&&, int&&>());
+static_assert(conformance<std::generator<int&&, int>, int, int&&, int&&>());
+static_assert(conformance<std::generator<const int&&>, int, const int&&, const int&&>());
+static_assert(conformance<std::generator<const int&&, int>, int, const int&&, const int&&>());
diff --git a/libcxx/test/std/ranges/coro.generator/generator.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
new file mode 100644
index 0000000000000..ed1793382962a
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+std::generator<int> fib() {
+  int a = 0;
+  int b = 1;
+  while (true) {
+    co_yield std::exchange(a, std::exchange(b, a + b));
+  }
+}
+
+std::generator<const int&> range_fib() {
+  co_yield std::ranges::elements_of(std::vector<int>{0, 1});
+  co_yield std::ranges::elements_of(std::vector<int>{1, 2});
+  co_yield std::ranges::elements_of(std::vector<int>{3, 5});
+  co_yield std::ranges::elements_of(std::vector<int>{5, 8});
+}
+
+bool test() {
+  {
+    std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+    {
+      auto fib_vec = fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+    {
+      auto fib_vec = range_fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+  }
+  return true;
+}
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
new file mode 100644
index 0000000000000..36c5b8b65e42e
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <numeric>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+std::generator<int> fib() {
+  int a = 0;
+  int b = 1;
+  while (true) {
+    co_yield std::exchange(a, std::exchange(b, a + b));
+  }
+}
+
+std::generator<int> recursive_fib(int depth) {
+  if (depth == 0) {
+    co_yield std::ranges::elements_of(fib());
+  } else {
+    co_yield std::ranges::elements_of(recursive_fib(depth - 1));
+  }
+};
+
+struct tree_node {
+  tree_node* left;
+  tree_node* right;
+  int element;
+
+  ~tree_node() {
+    delete left;
+    delete right;
+  }
+};
+
+tree_node* build_tree(int depth) {
+  if (depth == 0) {
+    return nullptr;
+  }
+
+  tree_node* root = new tree_node();
+  root->element   = depth;
+  root->left      = build_tree(depth - 1);
+  root->right     = build_tree(depth - 1);
+  return root;
+}
+
+std::generator<int> traversal(tree_node* node) {
+  if (node == nullptr) {
+    co_return;
+  }
+  co_yield std::ranges::elements_of(traversal(node->left));
+  co_yield node->element;
+  co_yield std::ranges::elements_of(traversal(node->right));
+}
+
+bool test() {
+  {
+    std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+    {
+      auto fib_vec = recursive_fib(1) | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+    {
+      auto fib_vec = recursive_fib(42) | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+  }
+  {
+    tree_node* tree_root = build_tree(10);
+    auto node_vec        = traversal(tree_root) | std::ranges::to<std::vector<int>>();
+    assert(node_vec.size() == 1023);
+    assert(std::reduce(node_vec.cbegin(), node_vec.cend()) == 2036);
+    delete tree_root;
+  }
+  return true;
+}
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d8fb575b97b2f..a5db6f1f1650f 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -719,6 +719,11 @@ def add_version_header(tc):
             "headers": ["random"],
             "unimplemented": True,
         },
+        {
+            "name": "__cpp_lib_generator",
+            "values": {"c++23": 202207}, # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
+            "headers": ["generator"],
+        },
         {
             "name": "__cpp_lib_generic_associative_lookup",
             "values": {"c++14": 201304},
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index d06271a7908cc..cc33d32a69201 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -164,7 +164,6 @@ def __hash__(self) -> int:
 # modules will fail to build if a header is added but this list is not updated.
 headers_not_available = list(map(Header, [
     "debugging",
-    "generator",
     "hazard_pointer",
     "inplace_vector",
     "linalg",

>From f2ee26c784c91829799073fe12d1cbd0d281a755 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 22:52:35 +0800
Subject: [PATCH 2/8] release notes & python format

---
 libcxx/docs/ReleaseNotes/23.rst                        | 2 ++
 libcxx/utils/generate_feature_test_macro_components.py | 4 +++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 03dabc54dcf79..a53fad86004a6 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -51,6 +51,8 @@ Implemented Papers
 - P2542R8: ``views::concat`` (`Github <https://llvm.org/PR105419>`__)
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
+- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index a5db6f1f1650f..ea2e43b4c15a8 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -721,7 +721,9 @@ def add_version_header(tc):
         },
         {
             "name": "__cpp_lib_generator",
-            "values": {"c++23": 202207}, # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
+            "values": {
+                "c++23": 202207
+            },  # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
             "headers": ["generator"],
         },
         {

>From 19e5608ff21db2369e7172201d55bc372e48d67b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:27:41 +0800
Subject: [PATCH 3/8] test

---
 libcxx/include/generator | 109 +++++++++++++++++++++------------------
 1 file changed, 60 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index d3f942b089002..7792b65a6ae43 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -25,52 +25,56 @@ namespace std {
 
 */
 
-#include <__algorithm/max.h>
-#include <__assert>
-#include <__concepts/common_reference_with.h>
-#include <__concepts/constructible.h>
-#include <__concepts/convertible_to.h>
-#include <__concepts/same_as.h>
-#include <__config>
-#include <__coroutine/coroutine_handle.h>
-#include <__coroutine/coroutine_traits.h>
-#include <__coroutine/noop_coroutine_handle.h>
-#include <__coroutine/trivial_awaitables.h>
-#include <__exception/exception_ptr.h>
-#include <__iterator/default_sentinel.h>
-#include <__memory/addressof.h>
-#include <__memory/allocator_arg_t.h>
-#include <__memory/allocator_traits.h>
-#include <__memory/construct_at.h>
-#include <__memory_resource/polymorphic_allocator.h>
-#include <__ranges/access.h>
-#include <__ranges/concepts.h>
-#include <__ranges/elements_of.h>
-#include <__ranges/view_interface.h>
-#include <__type_traits/add_pointer.h>
-#include <__type_traits/common_reference.h>
-#include <__type_traits/conditional.h>
-#include <__type_traits/is_nothrow_constructible.h>
-#include <__type_traits/is_object.h>
-#include <__type_traits/is_pointer.h>
-#include <__type_traits/is_reference.h>
-#include <__type_traits/is_void.h>
-#include <__type_traits/remove_cvref.h>
-#include <__type_traits/remove_reference.h>
-#include <__utility/exchange.h>
-#include <__utility/move.h>
-#include <__utility/swap.h>
-#include <cstdint>
-#include <new>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+#  include <__cxx03/__config>
+#else
+#  include <__config>
+
+#  if _LIBCPP_STD_VER >= 23
+
+#    include <__algorithm/max.h>
+#    include <__assert>
+#    include <__concepts/common_reference_with.h>
+#    include <__concepts/constructible.h>
+#    include <__concepts/convertible_to.h>
+#    include <__concepts/same_as.h>
+#    include <__coroutine/coroutine_handle.h>
+#    include <__coroutine/coroutine_traits.h>
+#    include <__coroutine/noop_coroutine_handle.h>
+#    include <__coroutine/trivial_awaitables.h>
+#    include <__exception/exception_ptr.h>
+#    include <__iterator/default_sentinel.h>
+#    include <__memory/addressof.h>
+#    include <__memory/allocator_arg_t.h>
+#    include <__memory/allocator_traits.h>
+#    include <__memory/construct_at.h>
+#    include <__memory_resource/polymorphic_allocator.h>
+#    include <__ranges/access.h>
+#    include <__ranges/concepts.h>
+#    include <__ranges/elements_of.h>
+#    include <__ranges/view_interface.h>
+#    include <__type_traits/add_pointer.h>
+#    include <__type_traits/common_reference.h>
+#    include <__type_traits/conditional.h>
+#    include <__type_traits/is_nothrow_constructible.h>
+#    include <__type_traits/is_object.h>
+#    include <__type_traits/is_pointer.h>
+#    include <__type_traits/is_reference.h>
+#    include <__type_traits/is_void.h>
+#    include <__type_traits/remove_cvref.h>
+#    include <__type_traits/remove_reference.h>
+#    include <__utility/exchange.h>
+#    include <__utility/move.h>
+#    include <__utility/swap.h>
+#    include <cstdint>
+#    include <new>
+
+#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#      pragma GCC system_header
+#    endif
 
 _LIBCPP_PUSH_MACROS
-#include <__undef_macros>
-
-#if _LIBCPP_STD_VER >= 23
+#    include <__undef_macros>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -309,13 +313,13 @@ public:
   _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
 
   _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
-#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+#    ifndef _LIBCPP_HAS_NO_EXCEPTIONS
     if (__is_root()) {
       throw;
     } else {
       __get_recursive_data().__exception = std::current_exception();
     }
-#  endif
+#    endif
   }
 };
 
@@ -462,9 +466,14 @@ private:
 
 public:
   _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size) {
-    void* const __ptr      = ::operator new(__size + sizeof(__dealloc_fn));
-    __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
+    void* const __ptr = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc =
+        [](void* const __dealloc_ptr, [[maybe_unused]] const size_t __dealloc_size) static noexcept -> void {
+#    ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
+      ::operator delete(__dealloc_ptr);
+#    else
       ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+#    endif
     };
 
     __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
@@ -596,8 +605,10 @@ using generator = std::generator<_Ref, _Val, polymorphic_allocator<>>;
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_STD_VER >= 23
+#  endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_POP_MACROS
 
+#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
 #endif // _LIBCPP_GENERATOR

>From 57bf0686971419520f56a48a34647f302a151cec Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:48:45 +0800
Subject: [PATCH 4/8] test

---
 libcxx/include/generator | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index 7792b65a6ae43..2176ecbaa1e6e 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -30,6 +30,13 @@ namespace std {
 #else
 #  include <__config>
 
+#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#    pragma GCC system_header
+#  endif
+
+_LIBCPP_PUSH_MACROS
+#  include <__undef_macros>
+
 #  if _LIBCPP_STD_VER >= 23
 
 #    include <__algorithm/max.h>
@@ -69,13 +76,6 @@ namespace std {
 #    include <cstdint>
 #    include <new>
 
-#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#      pragma GCC system_header
-#    endif
-
-_LIBCPP_PUSH_MACROS
-#    include <__undef_macros>
-
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Ref, class _Val>
@@ -469,10 +469,10 @@ public:
     void* const __ptr = ::operator new(__size + sizeof(__dealloc_fn));
     __dealloc_fn __dealloc =
         [](void* const __dealloc_ptr, [[maybe_unused]] const size_t __dealloc_size) static noexcept -> void {
-#    ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
-      ::operator delete(__dealloc_ptr);
-#    else
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
       ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+#    else
+      ::operator delete(__dealloc_ptr);
 #    endif
     };
 

>From d4c2ae0fccee3faeb4ed931a82cb11edc4505e2a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:57:26 +0800
Subject: [PATCH 5/8] test

---
 libcxx/test/libcxx/transitive_includes/cxx03.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx11.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx14.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx17.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx20.csv | 16 ----------------
 5 files changed, 80 deletions(-)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 5754f5961dd09..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -949,22 +949,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 5754f5961dd09..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -949,22 +949,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c31b86c5f8739..cfcf7bc7c4c19 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -972,22 +972,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 50f2d9cf2ac5f..534776e6f0526 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -975,22 +975,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 0d23c92ec9a1d..55c79acff5a8f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -965,22 +965,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm

>From 0b062c588aa75fa002ffe2d020da342947f80caf Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 8 Jun 2026 23:21:56 +0800
Subject: [PATCH 6/8] revert some transitive include change

---
 libcxx/test/libcxx/transitive_includes/cxx03.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx11.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx14.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx17.csv | 5 -----
 4 files changed, 20 deletions(-)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index cfcf7bc7c4c19..c2eb5b44e8d7a 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,19 +125,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2076,7 +2072,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 534776e6f0526..332cb62f35b5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2089,7 +2085,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm

>From 90c7db75736b52393c458af97b92a6dc4617ca7f Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 20:18:04 +0800
Subject: [PATCH 7/8] release notes 23 -> 24

---
 libcxx/docs/ReleaseNotes/23.rst | 5 -----
 libcxx/docs/ReleaseNotes/24.rst | 2 ++
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e18f46f6e467d..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -54,12 +54,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
-<<<<<<< HEAD
-- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
-- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
-=======
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
->>>>>>> main
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..55b39c7530f6a 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,8 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
+- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
 
 Improvements and New Features
 -----------------------------

>From 8fc0b82aeb2c202c5710c5e21a519d3ddf9003c4 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 20:22:23 +0800
Subject: [PATCH 8/8] release notes 23 -> 24

---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 libcxx/docs/Status/Cxx23Papers.csv | 4 ++--
 libcxx/docs/Status/Cxx26Issues.csv | 8 ++++----
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index f73551117f84e..167214284e2fd 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -217,7 +217,7 @@
 "`LWG3759 <https://wg21.link/LWG3759>`__","``ranges::rotate_copy`` should use ``std::move``","2022-11 (Kona)","|Complete|","15","`#105027 <https://github.com/llvm/llvm-project/issues/105027>`__",""
 "`LWG3760 <https://wg21.link/LWG3760>`__","``cartesian_product_view::iterator``'s ``parent_`` is never valid","2022-11 (Kona)","","","`#105028 <https://github.com/llvm/llvm-project/issues/105028>`__",""
 "`LWG3761 <https://wg21.link/LWG3761>`__","``cartesian_product_view::iterator::operator-`` should pass by reference","2022-11 (Kona)","","","`#105029 <https://github.com/llvm/llvm-project/issues/105029>`__",""
-"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","23","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
+"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","24","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
 "`LWG3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept","2022-11 (Kona)","|Complete|","17","`#105031 <https://github.com/llvm/llvm-project/issues/105031>`__",""
 "`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","","","`#105032 <https://github.com/llvm/llvm-project/issues/105032>`__",""
 "`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","","`#105033 <https://github.com/llvm/llvm-project/issues/105033>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 86f1395ed12fb..b5494c4a20ea8 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -80,7 +80,7 @@
 "`P2474R2 <https://wg21.link/P2474R2>`__","``views::repeat``","2022-07 (Virtual)","|Complete|","17","`#105222 <https://github.com/llvm/llvm-project/issues/105222>`__",""
 "`P2494R2 <https://wg21.link/P2494R2>`__","Relaxing range adaptors to allow for move only types","2022-07 (Virtual)","|Complete|","17","`#105224 <https://github.com/llvm/llvm-project/issues/105224>`__",""
 "`P2499R0 <https://wg21.link/P2499R0>`__","``string_view`` range constructor should be ``explicit``","2022-07 (Virtual)","|Complete|","16","`#105225 <https://github.com/llvm/llvm-project/issues/105225>`__",""
-"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","23","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
+"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","24","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
 "`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","2022-07 (Virtual)","|Complete|","15","`#105227 <https://github.com/llvm/llvm-project/issues/105227>`__",""
 "`P2517R1 <https://wg21.link/P2517R1>`__","Add a conditional ``noexcept`` specification to ``std::apply``","2022-07 (Virtual)","|Complete|","3.9","`#105229 <https://github.com/llvm/llvm-project/issues/105229>`__",""
 "`P2520R0 <https://wg21.link/P2520R0>`__","``move_iterator`` should be a random access iterator","2022-07 (Virtual)","|Complete|","17","`#105230 <https://github.com/llvm/llvm-project/issues/105230>`__","Implemented as a DR in C++20"
@@ -115,7 +115,7 @@
 "`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20","`#105259 <https://github.com/llvm/llvm-project/issues/105259>`__",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","`#105260 <https://github.com/llvm/llvm-project/issues/105260>`__","The paper is implemented as a DR to C++20"
 "`P2652R2 <https://wg21.link/P2652R2>`__","Disallow User Specialization of ``allocator_traits``","2023-02 (Issaquah)","|Complete|","19","`#105262 <https://github.com/llvm/llvm-project/issues/105262>`__",""
-"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","23","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
+"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","24","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
 "`P2614R2 <https://wg21.link/P2614R2>`__","Deprecate ``numeric_limits::has_denorm``","2023-02 (Issaquah)","|Complete|","18","`#105264 <https://github.com/llvm/llvm-project/issues/105264>`__",""
 "`P2588R3 <https://wg21.link/P2588R3>`__","``barrier``’s phase completion guarantees","2023-02 (Issaquah)","","","`#105265 <https://github.com/llvm/llvm-project/issues/105265>`__",""
 "`P2763R1 <https://wg21.link/P2763R1>`__","``layout_stride`` static extents default constructor fix","2023-02 (Issaquah)","","","`#105266 <https://github.com/llvm/llvm-project/issues/105266>`__",""
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index e8c02fa8e1ce1..c217b32adc014 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -4,7 +4,7 @@
 "`LWG3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","2023-06 (Varna)","|Nothing To Do|","","`#105270 <https://github.com/llvm/llvm-project/issues/105270>`__",""
 "`LWG3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","2023-06 (Varna)","|Complete|","19","`#105271 <https://github.com/llvm/llvm-project/issues/105271>`__",""
 "`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","","","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
-"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","23","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
+"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","24","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
 "`LWG3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 <https://github.com/llvm/llvm-project/issues/105275>`__",""
 "`LWG3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 <https://github.com/llvm/llvm-project/issues/105276>`__",""
 "`LWG3905 <https://wg21.link/LWG3905>`__","Type of ``std::fexcept_t``","2023-06 (Varna)","|Complete|","3.4","`#105277 <https://github.com/llvm/llvm-project/issues/105277>`__",""
@@ -79,8 +79,8 @@
 "`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","","`#118332 <https://github.com/llvm/llvm-project/issues/118332>`__",""
 "`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","","`#118335 <https://github.com/llvm/llvm-project/issues/118335>`__",""
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
-"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","23","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
-"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
+"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","24","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
+"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","|Complete|","24","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
 "`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","|Complete|","23","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
@@ -93,7 +93,7 @@
 "`LWG4088 <https://wg21.link/LWG4088>`__","``println`` ignores the locale imbued in ``std::ostream``","2024-11 (Wrocław)","|Complete|","18","`#118348 <https://github.com/llvm/llvm-project/issues/118348>`__",""
 "`LWG4112 <https://wg21.link/LWG4112>`__","``has-arrow`` should required ``operator->()`` to be ``const``-qualified","2024-11 (Wrocław)","","","`#118349 <https://github.com/llvm/llvm-project/issues/118349>`__",""
 "`LWG4113 <https://wg21.link/LWG4113>`__","Disallow ``has_unique_object_representations<Incomplete[]>``","2024-11 (Wrocław)","|Complete|","","`#118350 <https://github.com/llvm/llvm-project/issues/118350>`__",""
-"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","23","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
+"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","24","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
 "`LWG4124 <https://wg21.link/LWG4124>`__","Cannot format ``zoned_time`` with resolution coarser than ``seconds``","2024-11 (Wrocław)","","","`#118352 <https://github.com/llvm/llvm-project/issues/118352>`__",""
 "`LWG4126 <https://wg21.link/LWG4126>`__","Some feature-test macros for fully freestanding features are not yet marked freestanding","2024-11 (Wrocław)","","","`#118353 <https://github.com/llvm/llvm-project/issues/118353>`__",""
 "`LWG4134 <https://wg21.link/LWG4134>`__","Issue with Philox algorithm specification","2024-11 (Wrocław)","","","`#118354 <https://github.com/llvm/llvm-project/issues/118354>`__",""


From libcxx-commits at lists.llvm.org  Mon Jul 20 05:24:16 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 05:24:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a5e1370.492e23e4.d19bc.ee35@mx.google.com>


https://github.com/Zingam updated https://github.com/llvm/llvm-project/pull/193891

>From deea37f2081e61d37e69f89460f795aa5efe0720 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/16] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From aef95d864aae59df8f6dec2fa66ddc1a719f5638 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/16] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 23a51b634dcc3504d5b557ac93030bb0857656f2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/16] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From de998041add137b8e32fdef4863e4474dd21b5ec Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/16] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 8fcfd5da0ee11a5cdb8de5496630ef8c5ebacfd6 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/16] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 32db34b5c5169d6d705616b2e34278521ebb9885 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/16] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 62abfba3c272781d184751584632cb4cdd1e1c6c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/16] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From e4c8d234ef2affc34b7e3d16c85990904ebaf05a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/16] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From a036b2f2f08b3004f29713b9ae8fcb054cbf6a21 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/16] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From dccc322245a6507d61d3702d6141f1f4417d52bf Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/16] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 79c4e1f418372..95a3696e1ce57 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,7 +40,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -48,7 +48,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -56,7 +56,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -82,7 +82,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index 4ff65be645220..359f5a6099fce 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From ead248ba9c01d1558bb2cb0d229e9661d85eeefb Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/16] Format

---
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 .../range.elements/iterator/base.pass.cpp     | 22 +++++++++----------
 .../range.elements/sentinel/base.pass.cpp     |  2 +-
 .../range.split/iterator/base.pass.cpp        | 10 ++++-----
 .../range.split/iterator/deref.pass.cpp       |  7 +++---
 .../sentinel/ctor.convert.pass.cpp            |  4 ++--
 6 files changed, 23 insertions(+), 24 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 2522dd98e4b9c..0da468245e8cd 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 95a3696e1ce57..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,10 +22,9 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
-      { std::declval<T>().base() } noexcept;
-    };
+concept IsBaseNoexcept = requires {
+  { std::declval<T>().base() } noexcept;
+};
 
 using BaseView     = std::ranges::subrange<std::tuple<int>*>;
 using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<BaseView, 0>>;
@@ -41,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -56,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -65,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -82,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From ccc5a5ef18913e717a58b3c89d8209dbc0ebe1e2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/16] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..6b45987b15474 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 0da468245e8cd..90b8862cf382c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 19f8cb6337144ac348a237cbd3b39efd6b0067ee Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/16] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From db536a1ad37a2a76368977d3b656c7cbd036e632 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/16] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp              | 16 +++++++++++----
 .../sentinel/ctor.convert.pass.cpp            | 20 ++++++++++++-------
 2 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index 359f5a6099fce..f8492ab81fa2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -62,20 +62,26 @@ static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elem
 // !convertible_to<sentinel_t<V>, sentinel_t<Base>>
 static_assert(!std::is_constructible_v<
               std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>,
-              std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
+                              std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
 
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From ad11680d2beacc47ab39a7b2bc7872cd681c6578 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/16] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From 56482d8287ced302e9c46f489d47a30a1cc5723b Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/16] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}


From libcxx-commits at lists.llvm.org  Mon Jul 20 05:24:46 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Mon, 20 Jul 2026 05:24:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0493R5: Atomic
	minimum/maximum (PR #180333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/180333@github.com>
Message-ID: <6a5e138e.4a342cbc.3a2f36.062e@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/180333

>From ab79abbd1c14a305de4c16b791bf5e22384d543a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 17:10:11 +0800
Subject: [PATCH 01/29] implement atomic max/min

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/ReleaseNotes/23.rst               |  1 +
 libcxx/docs/Status/Cxx2cPapers.csv            |  2 +-
 libcxx/include/__atomic/atomic.h              | 68 ++++++++++++++++++
 libcxx/include/__atomic/atomic_ref.h          |  8 +++
 libcxx/include/__atomic/support.h             |  9 +++
 libcxx/include/__atomic/support/c11.h         | 26 +++++++
 libcxx/include/__atomic/support/gcc.h         | 24 +++++++
 libcxx/include/atomic                         | 24 +++++++
 libcxx/include/version                        |  2 +-
 libcxx/modules/std/atomic.inc                 |  4 ++
 .../atomics/atomics.ref/fetch_max.pass.cpp    | 70 +++++++++++++++++++
 .../atomics/atomics.ref/fetch_min.pass.cpp    | 70 +++++++++++++++++++
 .../atomic_fetch_max.pass.cpp                 | 55 +++++++++++++++
 .../atomic_fetch_max_explicit.pass.cpp        | 57 +++++++++++++++
 .../atomic_fetch_min.pass.cpp                 | 55 +++++++++++++++
 .../atomic_fetch_min_explicit.pass.cpp        | 57 +++++++++++++++
 .../atomic.version.compile.pass.cpp           | 16 ++---
 .../version.version.compile.pass.cpp          | 16 ++---
 .../generate_feature_test_macro_components.py |  1 -
 20 files changed, 541 insertions(+), 26 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
 create mode 100644 libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index d2b76cb96e866..701775e051f9a 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -416,7 +416,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_associative_heterogeneous_insertion``          *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_atomic_min_max``                               *unimplemented*
+    ``__cpp_lib_atomic_min_max``                               ``202403L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_atomic_ref``                                   ``202411L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index a1d62a367f33c..ce2eee5ed852a 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P2440R1 (partial): ``ranges::iota`` and ``ranges::shift_left`` are supported (`Github <https://llvm.org/PR105184>`__)
+- P0493R5: Atomic minimum/maximum are supported (`Github <https://llvm.org/PR105418>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 29642fc53cac6..50819f1b47a27 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -53,7 +53,7 @@
 "`P3107R5 <https://wg21.link/P3107R5>`__","Permit an efficient implementation of ``std::print``","2024-03 (Tokyo)","","","`#105414 <https://github.com/llvm/llvm-project/issues/105414>`__",""
 "`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)"
 "`P2845R8 <https://wg21.link/P2845R8>`__","Formatting of ``std::filesystem::path``","2024-03 (Tokyo)","","","`#105416 <https://github.com/llvm/llvm-project/issues/105416>`__",""
-"`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","","","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
+"`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","|Complete|","23","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
 "`P2542R8 <https://wg21.link/P2542R8>`__","``views::concat``","2024-03 (Tokyo)","","","`#105419 <https://github.com/llvm/llvm-project/issues/105419>`__",""
 "`P2591R5 <https://wg21.link/P2591R5>`__","Concatenation of strings and string views","2024-03 (Tokyo)","|Complete|","19","`#105420 <https://github.com/llvm/llvm-project/issues/105420>`__",""
 "`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","`#105421 <https://github.com/llvm/llvm-project/issues/105421>`__",""
diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index ea1d28fbf850a..f1c7492590d61 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -178,6 +178,20 @@ struct __atomic_base<_Tp, true> : public __atomic_base<_Tp, false> {
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
     return std::__cxx_atomic_fetch_xor(std::addressof(this->__a_), __op, __m);
   }
+#if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+#endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) volatile _NOEXCEPT { return fetch_add(_Tp(1)); }
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) _NOEXCEPT { return fetch_add(_Tp(1)); }
@@ -799,6 +813,60 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
   return __o->fetch_xor(__op, __m);
 }
 
+#if _LIBCPP_STD_VER >= 26
+// atomic_fetch_max
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_max(__op);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_max(__op);
+}
+
+// atomic_fetch_max_explicit
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
+    volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_max(__op, __m);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_max(__op, __m);
+}
+
+// atomic_fetch_min
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_min(__op);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_min(__op);
+}
+
+// atomic_fetch_min_explicit
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
+    volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_min(__op, __m);
+}
+
+template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_min(__op, __m);
+}
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ATOMIC_ATOMIC_H
diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 69edbfe6ecadd..cedd4248eb200 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -299,6 +299,14 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
     return __atomic_fetch_xor(this->__ptr_, __arg, std::__to_gcc_order(__order));
   }
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+  }
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) const noexcept { return fetch_add(_Tp(1)); }
   _LIBCPP_HIDE_FROM_ABI _Tp operator--(int) const noexcept { return fetch_sub(_Tp(1)); }
diff --git a/libcxx/include/__atomic/support.h b/libcxx/include/__atomic/support.h
index 99d0f6aa543ca..eda38705acd32 100644
--- a/libcxx/include/__atomic/support.h
+++ b/libcxx/include/__atomic/support.h
@@ -99,6 +99,15 @@
 // template <class _Tp>
 // _Tp __cxx_atomic_fetch_xor(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
 //
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_max(_Atmc<_Tp> volatile* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_max(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_min(_Atmc<_Tp> volatile* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_min(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
+//
 // clang-format on
 //
 
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f28b5e461c18a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -259,6 +259,32 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
       std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
 }
 
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ATOMIC_SUPPORT_C11_H
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..dfaebb68ced74 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -258,6 +258,30 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   return __atomic_fetch_xor(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
 }
 
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+}
+
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 23a3db5d35029..4cf93b6bfbd85 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -164,6 +164,10 @@ struct atomic<integral>
     integral fetch_or(integral op, memory_order m = memory_order_seq_cst) noexcept;
     integral fetch_xor(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
     integral fetch_xor(integral op, memory_order m = memory_order_seq_cst) noexcept;
+    integral fetch_max(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
+    integral fetch_max(integral op, memory_order m = memory_order_seq_cst) noexcept;
+    integral fetch_min(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
+    integral fetch_min(integral op, memory_order m = memory_order_seq_cst) noexcept;
 
     integral operator++(int) volatile noexcept;
     integral operator++(int) noexcept;
@@ -441,6 +445,26 @@ template<class T>
 template<class T>
   T atomic_fetch_xor_explicit(atomic<T>*, atomic<T>::value_type,
                               memory_order) noexcept;
+template<class T>
+  T atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
 
 template<class T>
   void atomic_wait(const volatile atomic<T>*, atomic<T>::value_type) noexcept; // since C++20
diff --git a/libcxx/include/version b/libcxx/include/version
index 7d77677a012ce..a00e9441202bd 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -546,7 +546,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #if _LIBCPP_STD_VER >= 26
 # define __cpp_lib_aligned_accessor                     202411L
 // # define __cpp_lib_associative_heterogeneous_insertion  202306L
-// # define __cpp_lib_atomic_min_max                       202403L
+# define __cpp_lib_atomic_min_max                       202403L
 # undef  __cpp_lib_atomic_ref
 # define __cpp_lib_atomic_ref                           202411L
 # undef  __cpp_lib_bind_front
diff --git a/libcxx/modules/std/atomic.inc b/libcxx/modules/std/atomic.inc
index 9a30fb7affe71..11f77991068c1 100644
--- a/libcxx/modules/std/atomic.inc
+++ b/libcxx/modules/std/atomic.inc
@@ -50,6 +50,10 @@ export namespace std {
   using std::atomic_fetch_sub_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor_explicit _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_max _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_max_explicit _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_min _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_min_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_all _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_one _LIBCPP_USING_IF_EXISTS;
   using std::atomic_wait _LIBCPP_USING_IF_EXISTS;
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
new file mode 100644
index 0000000000000..6c1b69dce1595
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// integral-type fetch_max(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+template <typename T>
+concept has_fetch_max = requires {
+  std::declval<T const>().fetch_max(std::declval<T>());
+  std::declval<T const>().fetch_max(std::declval<T>(), std::declval<std::memory_order>());
+};
+
+template <typename T>
+struct TestDoesNotHaveFetchMax {
+  void operator()() const { static_assert(!has_fetch_max<std::atomic_ref<T>>); }
+};
+
+template <typename T>
+struct TestFetchMax {
+  void operator()() const {
+    static_assert(std::is_integral_v<T>);
+
+    alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
+    std::atomic_ref<T> const a(x);
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
+      assert(y == T(1));
+      assert(x == T(2));
+      ASSERT_NOEXCEPT(a.fetch_max(T(0)));
+    }
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_max(T(1), std::memory_order_relaxed);
+      assert(y == T(2));
+      assert(x == T(2));
+      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFetchMax>()();
+
+  TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
+
+  TestEachPointerType<TestDoesNotHaveFetchMax>()();
+
+  TestDoesNotHaveFetchMax<bool>()();
+  TestDoesNotHaveFetchMax<UserAtomicType>()();
+  TestDoesNotHaveFetchMax<LargeUserAtomicType>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
new file mode 100644
index 0000000000000..1fa2d32c4d7d8
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// integral-type fetch_min(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+template <typename T>
+concept has_fetch_min = requires {
+  std::declval<T const>().fetch_min(std::declval<T>());
+  std::declval<T const>().fetch_min(std::declval<T>(), std::declval<std::memory_order>());
+};
+
+template <typename T>
+struct TestDoesNotHaveFetchMin {
+  void operator()() const { static_assert(!has_fetch_min<std::atomic_ref<T>>); }
+};
+
+template <typename T>
+struct TestFetchMin {
+  void operator()() const {
+    static_assert(std::is_integral_v<T>);
+
+    alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
+    std::atomic_ref<T> const a(x);
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
+      assert(y == T(3));
+      assert(x == T(2));
+      ASSERT_NOEXCEPT(a.fetch_min(T(0)));
+    }
+
+    {
+      std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
+      assert(y == T(2));
+      assert(x == T(1));
+      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFetchMin>()();
+
+  TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
+
+  TestEachPointerType<TestDoesNotHaveFetchMin>()();
+
+  TestDoesNotHaveFetchMin<bool>()();
+  TestDoesNotHaveFetchMin<UserAtomicType>()();
+  TestDoesNotHaveFetchMin<LargeUserAtomicType>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
new file mode 100644
index 0000000000000..25411767ca8ad
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_max(&t, T(2)) == T(1));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_max(&t, T(2)) == T(3));
+      assert(t == T(3));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
new file mode 100644
index 0000000000000..a670f1c10ce60
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+      assert(t == T(3));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
new file mode 100644
index 0000000000000..242355120c28d
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_min(&t, T(2)) == T(1));
+      assert(t == T(1));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_min(&t, T(2)) == T(3));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
new file mode 100644
index 0000000000000..46b7531b1f727
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//     T
+//     atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+//
+// template<class T>
+//     T
+//     atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
+//                               memory_order) noexcept;
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    {
+      typedef std::atomic<T> A;
+      A t(T(1));
+      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+      assert(t == T(1));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+    {
+      typedef std::atomic<T> A;
+      volatile A t(T(3));
+      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+      assert(t == T(2));
+
+      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
index 8bd027445c85b..9e620317c35c8 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
@@ -339,17 +339,11 @@
 #    error "__cpp_lib_atomic_lock_free_type_aliases should have the value 201907L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should be defined in c++26"
-#    endif
-#    if __cpp_lib_atomic_min_max != 202403L
-#      error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_atomic_min_max
+#    error "__cpp_lib_atomic_min_max should be defined in c++26"
+#  endif
+#  if __cpp_lib_atomic_min_max != 202403L
+#    error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_atomic_ref
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 f442b22c6d752..33f402b6422f6 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
@@ -6325,17 +6325,11 @@
 #    error "__cpp_lib_atomic_lock_free_type_aliases should have the value 201907L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should be defined in c++26"
-#    endif
-#    if __cpp_lib_atomic_min_max != 202403L
-#      error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_atomic_min_max
+#    error "__cpp_lib_atomic_min_max should be defined in c++26"
+#  endif
+#  if __cpp_lib_atomic_min_max != 202403L
+#    error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_atomic_ref
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 88431c76a00f5..799bb088f061e 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -191,7 +191,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_atomic_min_max",
             "values": {"c++26": 202403}, # P0493R5: Atomic minimum/maximum
             "headers": ["atomic"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_atomic_ref",

>From fb39fd535b1714f6d422d9b0f1810102a763a1b3 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 19:20:50 +0800
Subject: [PATCH 02/29] fix gcc build

---
 libcxx/include/__atomic/support/gcc.h | 28 +++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index dfaebb68ced74..e50143bb2bfa9 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -261,25 +261,45 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret > __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret > __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret < __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __value;
+  do {
+    __value = __ret < __pattern ? __ret : __pattern;
+  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)

>From 906a95e15e4bdd1136be7bfc1f14731e6a3a3135 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 19:36:17 +0800
Subject: [PATCH 03/29] fix gcc atomic_ref

---
 libcxx/include/__atomic/atomic_ref.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index cedd4248eb200..6ea270dc3b902 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -23,6 +23,7 @@
 #include <__atomic/check_memory_order.h>
 #include <__atomic/floating_point_helper.h>
 #include <__atomic/memory_order.h>
+#include <__atomic/support.h>
 #include <__atomic/to_gcc_order.h>
 #include <__concepts/arithmetic.h>
 #include <__concepts/same_as.h>
@@ -301,10 +302,10 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+    return std::__cxx_atomic_fetch_max(this->__ptr_, __arg, __m);
   }
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+    return std::__cxx_atomic_fetch_min(this->__ptr_, __arg, __m);
   }
 #  endif
 

>From a64208d9c117ccbd5a4cc5677a662b6e4f972f59 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 19:55:43 +0800
Subject: [PATCH 04/29] fix gcc

---
 libcxx/include/__atomic/atomic_ref.h  | 14 ++++++++++++--
 libcxx/include/__atomic/support/gcc.h |  8 ++++----
 2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 6ea270dc3b902..11d74f71e6de1 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -302,10 +302,20 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return std::__cxx_atomic_fetch_max(this->__ptr_, __arg, __m);
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old > __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
   }
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return std::__cxx_atomic_fetch_min(this->__ptr_, __arg, __m);
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old < __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
   }
 #  endif
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index e50143bb2bfa9..c224bb6c95391 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -261,7 +261,7 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
@@ -272,7 +272,7 @@ __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
@@ -283,7 +283,7 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
@@ -294,7 +294,7 @@ __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  _Tp __ret = __cxx_atomic_load(__a, __order);
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;

>From 5eeeeea250032dd5ae65ec4a1868158cefe3c650 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 20:12:55 +0800
Subject: [PATCH 05/29] fix gcc

---
 libcxx/include/__atomic/support/gcc.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index c224bb6c95391..3eaccf95e19d8 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -265,7 +265,7 @@ __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -276,7 +276,7 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -287,7 +287,7 @@ __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern,
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -298,7 +298,7 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
-  } while (__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 

>From 46257efd512cc8bd741cc9dd3157b59ab1ce1ca1 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 20:39:31 +0800
Subject: [PATCH 06/29] remove unused include

---
 libcxx/include/__atomic/atomic_ref.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 11d74f71e6de1..84b9bf211b4b9 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -23,7 +23,6 @@
 #include <__atomic/check_memory_order.h>
 #include <__atomic/floating_point_helper.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support.h>
 #include <__atomic/to_gcc_order.h>
 #include <__concepts/arithmetic.h>
 #include <__concepts/same_as.h>

>From dc1cb331ddcb980cce4a8deeef8d1c21b08b0eb7 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 7 Feb 2026 21:29:39 +0800
Subject: [PATCH 07/29] guard modules

---
 libcxx/modules/std/atomic.inc | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libcxx/modules/std/atomic.inc b/libcxx/modules/std/atomic.inc
index 11f77991068c1..38321e1a1bb64 100644
--- a/libcxx/modules/std/atomic.inc
+++ b/libcxx/modules/std/atomic.inc
@@ -50,10 +50,12 @@ export namespace std {
   using std::atomic_fetch_sub_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor_explicit _LIBCPP_USING_IF_EXISTS;
+#if _LIBCPP_STD_VER >= 26
   using std::atomic_fetch_max _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_max_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_min _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_min_explicit _LIBCPP_USING_IF_EXISTS;
+#endif
   using std::atomic_notify_all _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_one _LIBCPP_USING_IF_EXISTS;
   using std::atomic_wait _LIBCPP_USING_IF_EXISTS;

>From dfce3c7d45c1dca9e1ac85b293321ef19e007b23 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 8 Feb 2026 19:37:33 +0800
Subject: [PATCH 08/29] address review comments: use builtin if possible

---
 libcxx/include/__atomic/atomic_ref.h | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 84b9bf211b4b9..88c7d7dbc5dfb 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -301,20 +301,28 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_max)
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp __old = this->load(memory_order_relaxed);
     _Tp __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_min)
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp __old = this->load(memory_order_relaxed);
     _Tp __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
 #  endif
 

>From a3cabfda1b316f2514df394232a4e504f38159eb Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 8 Feb 2026 23:12:48 +0800
Subject: [PATCH 09/29] address review comments: use builtin if possible

---
 libcxx/include/__atomic/support/gcc.h | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 3eaccf95e19d8..754c60406cbf6 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -261,45 +261,61 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_max)
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_max)
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_min)
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+#if __has_builtin(__atomic_fetch_min)
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+#else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __pattern ? __ret : __pattern;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
+#endif
 }
 
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)

>From f01144d60576864032d1204a7662ddcb9540059a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 9 Feb 2026 20:33:05 +0800
Subject: [PATCH 10/29] codestyle

---
 libcxx/include/__atomic/atomic.h              | 24 ++++++++++++-------
 .../atomic_fetch_max.pass.cpp                 | 13 ++++------
 .../atomic_fetch_max_explicit.pass.cpp        | 15 ++++--------
 .../atomic_fetch_min.pass.cpp                 | 13 ++++------
 .../atomic_fetch_min_explicit.pass.cpp        | 15 ++++--------
 5 files changed, 34 insertions(+), 46 deletions(-)

diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index f1c7492590d61..e4be21cfd2d84 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -816,25 +816,29 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 #if _LIBCPP_STD_VER >= 26
 // atomic_fetch_max
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
 
 // atomic_fetch_max_explicit
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
@@ -842,25 +846,29 @@ atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 
 // atomic_fetch_min
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
 
 // atomic_fetch_min_explicit
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
 }
 
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value && !is_same<_Tp, bool>::value, int> = 0>
+template <class _Tp>
+  requires(integral<_Tp> && !same_as<_Tp, bool>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 25411767ca8ad..8aa66f68a23d7 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -12,12 +12,9 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
-//
+//   T atomic_fetch_max(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
 // template<class T>
-//     T
-//     atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+//   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -30,16 +27,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_max(&t, T(2)) == T(1));
       assert(t == T(2));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_max(&t, T(2)) == T(3));
       assert(t == T(3));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index a670f1c10ce60..1d04bb737fe1f 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -12,14 +12,11 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//   T atomic_fetch_max_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
 //                               memory_order) noexcept;
-//
 // template<class T>
-//     T
-//     atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
-//                               memory_order) noexcept;
+//   constexpr T atomic_fetch_max_explicit(atomic<T>*, typename atomic<T>::value_type,
+//                                         memory_order) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -32,16 +29,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
       assert(t == T(2));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
       assert(t == T(3));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 242355120c28d..43b979144b19b 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -12,12 +12,9 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
-//
+//   T atomic_fetch_min(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
 // template<class T>
-//     T
-//     atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+//   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -30,16 +27,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_min(&t, T(2)) == T(1));
       assert(t == T(1));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_min(&t, T(2)) == T(3));
       assert(t == T(2));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 46b7531b1f727..309489f91f126 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -12,14 +12,11 @@
 // <atomic>
 
 // template<class T>
-//     T
-//     atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+//   T atomic_fetch_min_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
 //                               memory_order) noexcept;
-//
 // template<class T>
-//     T
-//     atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
-//                               memory_order) noexcept;
+//   constexpr T atomic_fetch_min_explicit(atomic<T>*, typename atomic<T>::value_type,
+//                                         memory_order) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -32,16 +29,14 @@ template <class T>
 struct TestFn {
   void operator()() const {
     {
-      typedef std::atomic<T> A;
-      A t(T(1));
+      std::atomic<T> t(T(1));
       assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
       assert(t == T(1));
 
       ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
     }
     {
-      typedef std::atomic<T> A;
-      volatile A t(T(3));
+      volatile std::atomic<T> t(T(3));
       assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
       assert(t == T(2));
 

>From 4a4735ee1ec8f356a2c73570d289beb93dce9354 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 9 Feb 2026 20:46:52 +0800
Subject: [PATCH 11/29] ci

---
 libcxx/include/__atomic/atomic.h                       |  2 ++
 libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp | 10 ++++++++--
 libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp |  6 ++++++
 3 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index e4be21cfd2d84..455acb8fca2ad 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -16,6 +16,8 @@
 #include <__atomic/is_always_lock_free.h>
 #include <__atomic/memory_order.h>
 #include <__atomic/support.h>
+#include <__concepts/arithmetic.h>
+#include <__concepts/same_as.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 6c1b69dce1595..6e96baaaf6e2c 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -43,13 +43,19 @@ struct TestFetchMax {
       std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
       assert(y == T(1));
       assert(x == T(2));
+      y = a.fetch_max(T(0));
+      assert(y == T(2));
+      assert(x == T(2));
       ASSERT_NOEXCEPT(a.fetch_max(T(0)));
     }
 
     {
-      std::same_as<T> decltype(auto) y = a.fetch_max(T(1), std::memory_order_relaxed);
+      std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
       assert(y == T(2));
-      assert(x == T(2));
+      assert(x == T(3));
+      y = a.fetch_max(T(0));
+      assert(y == T(3));
+      assert(x == T(3));
       ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
     }
   }
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index 1fa2d32c4d7d8..92b7f1ed39cb1 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -43,6 +43,9 @@ struct TestFetchMin {
       std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
       assert(y == T(3));
       assert(x == T(2));
+      y = a.fetch_min(T(4));
+      assert(y == T(2));
+      assert(x == T(2));
       ASSERT_NOEXCEPT(a.fetch_min(T(0)));
     }
 
@@ -50,6 +53,9 @@ struct TestFetchMin {
       std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
       assert(y == T(2));
       assert(x == T(1));
+      y = a.fetch_min(T(4));
+      assert(y == T(1));
+      assert(x == T(1));
       ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
     }
   }

>From cfce0bec4a759eea1786642fd3a309032aecb2fc Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 15:26:50 +0800
Subject: [PATCH 12/29] revise testcases

---
 .../atomics/atomics.ref/fetch_max.pass.cpp    | 11 ++-
 .../atomics/atomics.ref/fetch_min.pass.cpp    | 11 ++-
 .../atomic_fetch_max.pass.cpp                 | 85 +++++++++++++++---
 .../atomic_fetch_max_explicit.pass.cpp        | 86 ++++++++++++++++---
 .../atomic_fetch_min.pass.cpp                 | 85 +++++++++++++++---
 .../atomic_fetch_min_explicit.pass.cpp        | 86 ++++++++++++++++---
 6 files changed, 296 insertions(+), 68 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 6e96baaaf6e2c..17930c5d97e65 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -15,26 +15,25 @@
 #include <cassert>
 #include <concepts>
 #include <type_traits>
-#include <utility>
 
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
 template <typename T>
-concept has_fetch_max = requires {
-  std::declval<T const>().fetch_max(std::declval<T>());
-  std::declval<T const>().fetch_max(std::declval<T>(), std::declval<std::memory_order>());
+concept has_fetch_max = requires(std::atomic_ref<T> const& a, T v) {
+  { a.fetch_max(v) } -> std::same_as<T>;
+  { a.fetch_max(v, std::memory_order::relaxed) } -> std::same_as<T>;
 };
 
 template <typename T>
 struct TestDoesNotHaveFetchMax {
-  void operator()() const { static_assert(!has_fetch_max<std::atomic_ref<T>>); }
+  void operator()() const { static_assert(!has_fetch_max<T>); }
 };
 
 template <typename T>
 struct TestFetchMax {
   void operator()() const {
-    static_assert(std::is_integral_v<T>);
+    static_assert(std::is_integral_v<T> && has_fetch_max<T>);
 
     alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
     std::atomic_ref<T> const a(x);
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index 92b7f1ed39cb1..e930d83a2b819 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -15,26 +15,25 @@
 #include <cassert>
 #include <concepts>
 #include <type_traits>
-#include <utility>
 
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
 template <typename T>
-concept has_fetch_min = requires {
-  std::declval<T const>().fetch_min(std::declval<T>());
-  std::declval<T const>().fetch_min(std::declval<T>(), std::declval<std::memory_order>());
+concept has_fetch_min = requires(std::atomic_ref<T> const& a, T v) {
+  { a.fetch_min(v) } -> std::same_as<T>;
+  { a.fetch_min(v, std::memory_order::relaxed) } -> std::same_as<T>;
 };
 
 template <typename T>
 struct TestDoesNotHaveFetchMin {
-  void operator()() const { static_assert(!has_fetch_min<std::atomic_ref<T>>); }
+  void operator()() const { static_assert(!has_fetch_min<T>); }
 };
 
 template <typename T>
 struct TestFetchMin {
   void operator()() const {
-    static_assert(std::is_integral_v<T>);
+    static_assert(std::is_integral_v<T> && has_fetch_min<T>);
 
     alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
     std::atomic_ref<T> const a(x);
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 8aa66f68a23d7..cbd8f147f5931 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -17,28 +20,82 @@
 //   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_max(&t, T(2)) == T(1));
-      assert(t == T(2));
+concept HasVolatileFetchMax = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_max(&a, t) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMax<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(std::atomic_fetch_max(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
+
+  // atomic_fetch_max
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_max(&t, T(2)) == T(1));
+    assert(t == T(2));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_max(&t, T(2)) == T(3));
+    assert(t == T(3));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_max concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(0);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = 0; j < loop; ++j) {
+          std::atomic_fetch_max(&at, T(i * loop + j));
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_max(&t, T(2)) == T(3));
-      assert(t == T(3));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max(&t, T(2)));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(0);
+      for (auto i = 0; i < n; ++i) {
+        res = std::max<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 1d04bb737fe1f..ddae555670f17 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -19,28 +22,83 @@
 //                                         memory_order) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-      assert(t == T(2));
+concept HasVolatileFetchMaxExplicit = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_max_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMaxExplicit<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(
+      std::atomic_fetch_max_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
+
+  // atomic_fetch_max_explicit
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+    assert(t == T(2));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    assert(t == T(3));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_max_explicit concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(0);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = 0; j < loop; ++j) {
+          std::atomic_fetch_max_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
-      assert(t == T(3));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(0);
+      for (auto i = 0; i < n; ++i) {
+        res = std::max<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 43b979144b19b..33ca27f3de262 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -17,28 +20,82 @@
 //   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_min(&t, T(2)) == T(1));
-      assert(t == T(1));
+concept HasVolatileFetchMin = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_min(&a, t) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMin<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(std::atomic_fetch_min(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
+
+  // atomic_fetch_min
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_min(&t, T(2)) == T(1));
+    assert(t == T(1));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_min(&t, T(2)) == T(3));
+    assert(t == T(2));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_min concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = loop - 1; j >= 0; --j) {
+          std::atomic_fetch_min(&at, T(i * loop + j));
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_min(&t, T(2)) == T(3));
-      assert(t == T(2));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min(&t, T(2)));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(n);
+      for (auto i = n - 1; i >= 0; --i) {
+        res = std::min<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 309489f91f126..99d20c0c544fb 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -9,6 +9,9 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
+// https://llvm.org/PR72893
+// XFAIL: target={{x86_64-.*}} && tsan
+
 // <atomic>
 
 // template<class T>
@@ -19,28 +22,83 @@
 //                                         memory_order) noexcept;
 
 #include <atomic>
-#include <type_traits>
 #include <cassert>
+#include <type_traits>
+#include <vector>
 
-#include "test_macros.h"
 #include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
 
 template <class T>
-struct TestFn {
-  void operator()() const {
-    {
-      std::atomic<T> t(T(1));
-      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-      assert(t == T(1));
+concept HasVolatileFetchMinExplicit = requires(volatile std::atomic<T>& a, T t) {
+  { std::atomic_fetch_min_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
+};
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileFetchMinExplicit<T> == std::atomic<T>::is_always_lock_free);
+  static_assert(noexcept(
+      std::atomic_fetch_min_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
+
+  // atomic_fetch_min_explicit
+  {
+    MaybeVolatile<std::atomic<T>> t(T(1));
+    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
+    assert(t == T(1));
+  }
+  {
+    MaybeVolatile<std::atomic<T>> t(T(3));
+    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    assert(t == T(2));
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // atomic_fetch_min_explicit concurrent
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = loop - 1; j >= 0; --j) {
+          std::atomic_fetch_min_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
+        }
+      }));
     }
-    {
-      volatile std::atomic<T> t(T(3));
-      assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
-      assert(t == T(2));
 
-      ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst));
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(n);
+      for (auto i = n - 1; i >= 0; --i) {
+        res = std::min<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
     }
   }
 };

>From 71961be2c3c55d2ff0f0224ada38814fe004c972 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 15:42:24 +0800
Subject: [PATCH 13/29] fix ci for aix

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp  | 6 ------
 .../atomic_fetch_max_explicit.pass.cpp                      | 6 ------
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp  | 6 ------
 .../atomic_fetch_min_explicit.pass.cpp                      | 6 ------
 4 files changed, 24 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index cbd8f147f5931..6f89cd719a2ef 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -33,14 +33,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMax = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_max(&a, t) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMax<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(std::atomic_fetch_max(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
 
   // atomic_fetch_max
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index ddae555670f17..931c5a482ed58 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -35,14 +35,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMaxExplicit = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_max_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMaxExplicit<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(
       std::atomic_fetch_max_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 33ca27f3de262..e7a4d4e7bc798 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -33,14 +33,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMin = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_min(&a, t) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMin<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(std::atomic_fetch_min(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
 
   // atomic_fetch_min
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 99d20c0c544fb..acf47f0d89567 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -35,14 +35,8 @@
 #  include "make_test_thread.h"
 #endif
 
-template <class T>
-concept HasVolatileFetchMinExplicit = requires(volatile std::atomic<T>& a, T t) {
-  { std::atomic_fetch_min_explicit(&a, t, std::memory_order_seq_cst) } -> std::same_as<T>;
-};
-
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(HasVolatileFetchMinExplicit<T> == std::atomic<T>::is_always_lock_free);
   static_assert(noexcept(
       std::atomic_fetch_min_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
 

>From 8844e1581d89f537673084b04236710aa1a41ec5 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 15:48:50 +0800
Subject: [PATCH 14/29] fix modules build

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp       | 1 +
 .../atomic_fetch_max_explicit.pass.cpp                           | 1 +
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp       | 1 +
 .../atomic_fetch_min_explicit.pass.cpp                           | 1 +
 4 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 6f89cd719a2ef..93bd767cbab35 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -19,6 +19,7 @@
 // template<class T>
 //   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 931c5a482ed58..7fb2a702be337 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -21,6 +21,7 @@
 //   constexpr T atomic_fetch_max_explicit(atomic<T>*, typename atomic<T>::value_type,
 //                                         memory_order) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index e7a4d4e7bc798..a5adda9487897 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -19,6 +19,7 @@
 // template<class T>
 //   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index acf47f0d89567..f5a23b153fdbe 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -21,6 +21,7 @@
 //   constexpr T atomic_fetch_min_explicit(atomic<T>*, typename atomic<T>::value_type,
 //                                         memory_order) noexcept;
 
+#include <algorithm>
 #include <atomic>
 #include <cassert>
 #include <type_traits>

>From 7bfd5438857639f2d3b627c18fe56e57a1165067 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 17:24:43 +0800
Subject: [PATCH 15/29] declval

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp       | 1 +
 .../atomic_fetch_max_explicit.pass.cpp                           | 1 +
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp       | 1 +
 .../atomic_fetch_min_explicit.pass.cpp                           | 1 +
 4 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index 93bd767cbab35..af249355811a5 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -23,6 +23,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 7fb2a702be337..45df7c240dfa2 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -25,6 +25,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index a5adda9487897..e326d4a4e5b7a 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -23,6 +23,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index f5a23b153fdbe..4e110f7f34944 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -25,6 +25,7 @@
 #include <atomic>
 #include <cassert>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "atomic_helpers.h"

>From a844bd2596f09f8eda9af8bf9fca7c67997816f8 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Tue, 17 Feb 2026 19:51:19 +0800
Subject: [PATCH 16/29] remove xfail

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp     | 3 ---
 .../atomic_fetch_max_explicit.pass.cpp                         | 3 ---
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp     | 3 ---
 .../atomic_fetch_min_explicit.pass.cpp                         | 3 ---
 4 files changed, 12 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index af249355811a5..e4aba6ebbdd20 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 45df7c240dfa2..441fb57c3b332 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index e326d4a4e5b7a..3a4a15290b407 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 4e110f7f34944..fd208162b9c07 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -9,9 +9,6 @@
 // REQUIRES: std-at-least-c++26
 // XFAIL: !has-64-bit-atomics
 
-// https://llvm.org/PR72893
-// XFAIL: target={{x86_64-.*}} && tsan
-
 // <atomic>
 
 // template<class T>

>From a85a1dfa40440160f7910933e899250e8c6409f9 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 21 Feb 2026 20:30:08 +0800
Subject: [PATCH 17/29] address review comments

---
 libcxx/include/__atomic/atomic.h              | 39 ++++++++--
 libcxx/include/__atomic/atomic_ref.h          | 28 +++++++
 libcxx/include/__atomic/support/c11.h         | 16 ++--
 libcxx/include/__atomic/support/gcc.h         | 26 +++----
 libcxx/include/atomic                         |  4 +
 .../atomics/atomics.ref/fetch_max.pass.cpp    | 73 +++++++++++++------
 .../atomics/atomics.ref/fetch_min.pass.cpp    | 73 +++++++++++++------
 .../atomic_fetch_max.pass.cpp                 |  3 +-
 .../atomic_fetch_max_explicit.pass.cpp        |  3 +-
 .../atomic_fetch_min.pass.cpp                 |  3 +-
 .../atomic_fetch_min_explicit.pass.cpp        |  3 +-
 11 files changed, 191 insertions(+), 80 deletions(-)

diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index 455acb8fca2ad..5fb9e0db72f51 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_integral.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/remove_pointer.h>
@@ -322,6 +323,28 @@ struct atomic<_Tp*> : public __atomic_base<_Tp*> {
     return std::__cxx_atomic_fetch_sub(std::addressof(this->__a_), __op, __m);
   }
 
+#if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+#endif
+
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) volatile _NOEXCEPT { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) _NOEXCEPT { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) volatile _NOEXCEPT { return fetch_sub(1); }
@@ -819,13 +842,13 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 // atomic_fetch_max
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_max(__op);
 }
@@ -833,14 +856,14 @@ _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp
 // atomic_fetch_max_explicit
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_max(__op, __m);
@@ -849,13 +872,13 @@ atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
 // atomic_fetch_min
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
   return __o->fetch_min(__op);
 }
@@ -863,14 +886,14 @@ _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp
 // atomic_fetch_min_explicit
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
     volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
 }
 
 template <class _Tp>
-  requires(integral<_Tp> && !same_as<_Tp, bool>)
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
 _LIBCPP_HIDE_FROM_ABI _Tp
 atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
   return __o->fetch_min(__op, __m);
diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 88c7d7dbc5dfb..ec98fab310c7c 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -403,6 +403,34 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_sub(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept {
     return __atomic_fetch_sub(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
   }
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_max)
+    return reinterpret_cast<_Tp*>(__atomic_fetch_max(
+        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+#    else
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old > __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
+#    endif
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_min)
+    return reinterpret_cast<_Tp*>(__atomic_fetch_min(
+        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+#    else
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old < __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
+#    endif
+  }
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) const noexcept { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) const noexcept { return fetch_sub(1); }
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f28b5e461c18a..0d74d55188a3d 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -261,28 +261,28 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) _NOEXCEPT {
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
   return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 754c60406cbf6..37c38d25012c6 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -260,29 +260,28 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_max)
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret > __pattern ? __ret : __pattern;
+    __value = __ret > __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
 }
 
 template <typename _Tp>
-_LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+_LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_max)
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret > __pattern ? __ret : __pattern;
+    __value = __ret > __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
@@ -290,29 +289,28 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_min)
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret < __pattern ? __ret : __pattern;
+    __value = __ret < __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
 }
 
 template <typename _Tp>
-_LIBCPP_HIDE_FROM_ABI _Tp
-__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
+_LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_min)
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
-    __value = __ret < __pattern ? __ret : __pattern;
+    __value = __ret < __val ? __ret : __val;
   } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 #endif
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 4cf93b6bfbd85..bcf831fedc192 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -244,6 +244,10 @@ struct atomic<T*>
     T* fetch_add(ptrdiff_t op, memory_order m = memory_order_seq_cst) noexcept;
     T* fetch_sub(ptrdiff_t op, memory_order m = memory_order_seq_cst) volatile noexcept;
     T* fetch_sub(ptrdiff_t op, memory_order m = memory_order_seq_cst) noexcept;
+    T* fetch_max(T*, memory_order = memory_order_seq_cst) volatile noexcept;
+    T* fetch_max(T*, memory_order = memory_order_seq_cst) noexcept;
+    T* fetch_min(T*, memory_order = memory_order_seq_cst) volatile noexcept;
+    T* fetch_min(T*, memory_order = memory_order_seq_cst) noexcept;
 
     T* operator++(int) volatile noexcept;
     T* operator++(int) noexcept;
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 17930c5d97e65..2ba748486d950 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -33,29 +33,56 @@ struct TestDoesNotHaveFetchMax {
 template <typename T>
 struct TestFetchMax {
   void operator()() const {
-    static_assert(std::is_integral_v<T> && has_fetch_max<T>);
-
-    alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
-    std::atomic_ref<T> const a(x);
-
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
-      assert(y == T(1));
-      assert(x == T(2));
-      y = a.fetch_max(T(0));
-      assert(y == T(2));
-      assert(x == T(2));
-      ASSERT_NOEXCEPT(a.fetch_max(T(0)));
-    }
+    static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_max<T>);
+
+    if constexpr (std::is_integral_v<T>) {
+      alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
+        assert(y == T(1));
+        assert(x == T(2));
+        y = a.fetch_max(T(0));
+        assert(y == T(2));
+        assert(x == T(2));
+        ASSERT_NOEXCEPT(a.fetch_max(T(0)));
+      }
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
+        assert(y == T(2));
+        assert(x == T(3));
+        y = a.fetch_max(T(0));
+        assert(y == T(3));
+        assert(x == T(3));
+        ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
+      }
+    } else if constexpr (std::is_pointer_v<T>) {
+      using U = std::remove_pointer_t<T>;
+      U t[9]  = {};
+      alignas(std::atomic_ref<T>::required_alignment) T x{&t[1]};
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(&t[2]);
+        assert(y == &t[1]);
+        assert(x == &t[2]);
+        y = a.fetch_max(&t[0]);
+        assert(y == &t[2]);
+        assert(x == &t[2]);
+        ASSERT_NOEXCEPT(a.fetch_max(&t[0]));
+      }
 
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
-      assert(y == T(2));
-      assert(x == T(3));
-      y = a.fetch_max(T(0));
-      assert(y == T(3));
-      assert(x == T(3));
-      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_max(&t[3], std::memory_order_relaxed);
+        assert(y == &t[2]);
+        assert(a == &t[3]);
+        y = a.fetch_max(&t[0]);
+        assert(y == &t[3]);
+        assert(x == &t[3]);
+        ASSERT_NOEXCEPT(a.fetch_max(&t[0], std::memory_order_relaxed));
+      }
     }
   }
 };
@@ -65,7 +92,7 @@ int main(int, char**) {
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
 
-  TestEachPointerType<TestDoesNotHaveFetchMax>()();
+  TestEachPointerType<TestFetchMax>()();
 
   TestDoesNotHaveFetchMax<bool>()();
   TestDoesNotHaveFetchMax<UserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index e930d83a2b819..b178996b06085 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -33,29 +33,56 @@ struct TestDoesNotHaveFetchMin {
 template <typename T>
 struct TestFetchMin {
   void operator()() const {
-    static_assert(std::is_integral_v<T> && has_fetch_min<T>);
-
-    alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
-    std::atomic_ref<T> const a(x);
-
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
-      assert(y == T(3));
-      assert(x == T(2));
-      y = a.fetch_min(T(4));
-      assert(y == T(2));
-      assert(x == T(2));
-      ASSERT_NOEXCEPT(a.fetch_min(T(0)));
-    }
+    static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_min<T>);
+
+    if constexpr (std::is_integral_v<T>) {
+      alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
+        assert(y == T(3));
+        assert(x == T(2));
+        y = a.fetch_min(T(4));
+        assert(y == T(2));
+        assert(x == T(2));
+        ASSERT_NOEXCEPT(a.fetch_min(T(0)));
+      }
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
+        assert(y == T(2));
+        assert(x == T(1));
+        y = a.fetch_min(T(4));
+        assert(y == T(1));
+        assert(x == T(1));
+        ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
+      }
+    } else if constexpr (std::is_pointer_v<T>) {
+      using U = std::remove_pointer_t<T>;
+      U t[9]  = {};
+      alignas(std::atomic_ref<T>::required_alignment) T x{&t[3]};
+      std::atomic_ref<T> const a(x);
+
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(&t[2]);
+        assert(y == &t[3]);
+        assert(x == &t[2]);
+        y = a.fetch_min(&t[4]);
+        assert(y == &t[2]);
+        assert(x == &t[2]);
+        ASSERT_NOEXCEPT(a.fetch_min(&t[0]));
+      }
 
-    {
-      std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
-      assert(y == T(2));
-      assert(x == T(1));
-      y = a.fetch_min(T(4));
-      assert(y == T(1));
-      assert(x == T(1));
-      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
+      {
+        std::same_as<T> decltype(auto) y = a.fetch_min(&t[1], std::memory_order_relaxed);
+        assert(y == &t[2]);
+        assert(a == &t[1]);
+        y = a.fetch_min(&t[4]);
+        assert(y == &t[1]);
+        assert(x == &t[1]);
+        ASSERT_NOEXCEPT(a.fetch_min(&t[0], std::memory_order_relaxed));
+      }
     }
   }
 };
@@ -65,7 +92,7 @@ int main(int, char**) {
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
 
-  TestEachPointerType<TestDoesNotHaveFetchMin>()();
+  TestEachPointerType<TestFetchMin>()();
 
   TestDoesNotHaveFetchMin<bool>()();
   TestDoesNotHaveFetchMin<UserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index e4aba6ebbdd20..a7596b6ff2bf5 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -44,7 +44,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_max(&t, T(2)) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_max(T(2));
+    assert(r == T(3));
     assert(t == T(3));
   }
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 441fb57c3b332..52c4ac137c709 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -47,7 +47,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_max(T(2), std::memory_order_seq_cst);
+    assert(r == T(3));
     assert(t == T(3));
   }
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 3a4a15290b407..1ffc92f3c2300 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -44,7 +44,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_min(&t, T(2)) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_min(T(2));
+    assert(r == T(3));
     assert(t == T(2));
   }
 
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index fd208162b9c07..f5732b5d977cc 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -47,7 +47,8 @@ void test_impl() {
   }
   {
     MaybeVolatile<std::atomic<T>> t(T(3));
-    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(3));
+    std::same_as<T> decltype(auto) r = t.fetch_min(T(2), std::memory_order_seq_cst);
+    assert(r == T(3));
     assert(t == T(2));
   }
 

>From c3e318d67841e8076a2e99f9c79d64873178dab2 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 21 Feb 2026 21:07:13 +0800
Subject: [PATCH 18/29] header

---
 .../atomics.types.operations.req/atomic_fetch_max.pass.cpp       | 1 +
 .../atomic_fetch_max_explicit.pass.cpp                           | 1 +
 .../atomics.types.operations.req/atomic_fetch_min.pass.cpp       | 1 +
 .../atomic_fetch_min_explicit.pass.cpp                           | 1 +
 4 files changed, 4 insertions(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index a7596b6ff2bf5..dc2274a1056cb 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index 52c4ac137c709..d878f6abd246d 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 1ffc92f3c2300..43277f6a7977a 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index f5732b5d977cc..75bf200812ab0 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
+#include <concepts>
 #include <type_traits>
 #include <utility>
 #include <vector>

>From 609e1196aa5098cbbd3e27a02d21beb42c98ecfe Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 22 Feb 2026 21:14:58 +0800
Subject: [PATCH 19/29] fix gcc ci

---
 libcxx/include/__atomic/atomic_ref.h | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index ec98fab310c7c..e0f28f4a3e204 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,11 +406,10 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
 #    if __has_builtin(__atomic_fetch_max)
-    return reinterpret_cast<_Tp*>(__atomic_fetch_max(
-        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+    return __atomic_fetch_max(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
 #    else
-    _Tp __old = this->load(memory_order_relaxed);
-    _Tp __new;
+    _Tp* __old = this->load(memory_order_relaxed);
+    _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
@@ -419,11 +418,10 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
 #    if __has_builtin(__atomic_fetch_min)
-    return reinterpret_cast<_Tp*>(__atomic_fetch_min(
-        reinterpret_cast<uintptr_t*>(this->__ptr_), reinterpret_cast<uintptr_t>(__arg), std::__to_gcc_order(__order)));
+    return __atomic_fetch_min(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
 #    else
-    _Tp __old = this->load(memory_order_relaxed);
-    _Tp __new;
+    _Tp* __old = this->load(memory_order_relaxed);
+    _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));

>From ebd61a9db3e67e3c7caf05b7e364f61f3243d24a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 10:19:34 -0400
Subject: [PATCH 20/29] Don't use builtins for pointer overloads of
 fetch_max/min

`__arg * sizeof(_Tp)` makes no sense in that context since __arg is a
pointer.
---
 libcxx/include/__atomic/atomic_ref.h | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index d681fd7488fcc..0fb141fb1f67a 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,28 +406,22 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_max)
-    return __atomic_fetch_max(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_min)
-    return __atomic_fetch_min(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
 #  endif
 

>From 6190229b39172c8b6e239e72c31e9f6daa6c85d6 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 10:32:46 -0400
Subject: [PATCH 21/29] Fix release note

---
 libcxx/docs/ReleaseNotes/23.rst | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index c99f3b49d902b..d56ec23a763cb 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -38,7 +38,8 @@ What's New in Libc++ 23.0.0?
 Implemented Papers
 ------------------
 
-- P0493R5: Atomic minimum/maximum are supported (`Github <https://llvm.org/PR105418>`__)
+- P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
+- P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
 - P1899R3: ``stride_view`` (`Github <https://llvm.org/PR105198>`__)
 - P3936R1: Safer ``atomic_ref::address`` (`Github <https://llvm.org/PR189594>`__)
 - P3953R3: Rename ``std::runtime_format`` (`Github <https://llvm.org/PR189624>`__)

>From 2853b3870f2bdfe1a6385f1a245866d71ac391f4 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 10:35:42 -0400
Subject: [PATCH 22/29] Revert "Don't use builtins for pointer overloads of
 fetch_max/min"

Try applying frederick's suggestion instead.
---
 libcxx/include/__atomic/atomic_ref.h | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 0fb141fb1f67a..2f5a256698cf3 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,22 +406,28 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
+#    if __has_builtin(__atomic_fetch_max)
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
+#    if __has_builtin(__atomic_fetch_min)
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
+#    endif
   }
 #  endif
 

>From b7ed28f9b8e3be9137393c91be40e29ac1268efa Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 11:11:22 -0400
Subject: [PATCH 23/29] Re-apply "Don't use builtins for pointer overloads of
 fetch_max/min"

It seems that the builtins indeed don't support pointers.
---
 libcxx/include/__atomic/atomic_ref.h | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 2f5a256698cf3..0fb141fb1f67a 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -406,28 +406,22 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   }
 #  if _LIBCPP_STD_VER >= 26
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_max)
-    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old > __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-#    if __has_builtin(__atomic_fetch_min)
-    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
-#    else
+    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
     _Tp* __old = this->load(memory_order_relaxed);
     _Tp* __new;
     do {
       __new = __old < __arg ? __old : __arg;
     } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
     return __old;
-#    endif
   }
 #  endif
 

>From de6f07f40149a2f41740033a24be77f06919097a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 15 Jun 2026 16:41:43 -0400
Subject: [PATCH 24/29] Port test coverage from #186694

Assisted by Claude, reviewed manually.
---
 .../atomics/atomics.ref/fetch_max.pass.cpp    |  64 +++-----
 .../atomics/atomics.ref/fetch_min.pass.cpp    |  64 +++-----
 .../atomic_fetch_max.pass.cpp                 |  49 ++++--
 .../atomic_fetch_max_explicit.pass.cpp        |  50 ++++--
 .../atomic_fetch_min.pass.cpp                 |  64 ++++----
 .../atomic_fetch_min_explicit.pass.cpp        |  65 ++++----
 libcxx/test/support/atomic_fetch_max_helper.h | 147 ++++++++++++++++++
 libcxx/test/support/atomic_fetch_min_helper.h | 147 ++++++++++++++++++
 8 files changed, 478 insertions(+), 172 deletions(-)
 create mode 100644 libcxx/test/support/atomic_fetch_max_helper.h
 create mode 100644 libcxx/test/support/atomic_fetch_min_helper.h

diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
index 2ba748486d950..7302eaea570f0 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -10,12 +10,14 @@
 // XFAIL: !has-64-bit-atomics
 
 // integral-type fetch_max(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+// T* fetch_max(T*, memory_order = memory_order::seq_cst) const noexcept;
 
 #include <atomic>
 #include <cassert>
 #include <concepts>
 #include <type_traits>
 
+#include "atomic_fetch_max_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -36,64 +38,38 @@ struct TestFetchMax {
     static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_max<T>);
 
     if constexpr (std::is_integral_v<T>) {
-      alignas(std::atomic_ref<T>::required_alignment) T x(T(1));
+      alignas(std::atomic_ref<T>::required_alignment) T x{};
       std::atomic_ref<T> const a(x);
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(T(2));
-        assert(y == T(1));
-        assert(x == T(2));
-        y = a.fetch_max(T(0));
-        assert(y == T(2));
-        assert(x == T(2));
-        ASSERT_NOEXCEPT(a.fetch_max(T(0)));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(T(3), std::memory_order_relaxed);
-        assert(y == T(2));
-        assert(x == T(3));
-        y = a.fetch_max(T(0));
-        assert(y == T(3));
-        assert(x == T(3));
-        ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_relaxed));
-      }
+      auto load  = [&]() { return x; };
+      auto store = [&](T val) { x = val; };
+      auto max   = [&](T val, auto order) { return a.fetch_max(val, order); };
+
+      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_seq_cst));
+      test_fetch_max_integral<T>(load, store, max);
+
     } else if constexpr (std::is_pointer_v<T>) {
       using U = std::remove_pointer_t<T>;
-      U t[9]  = {};
-      alignas(std::atomic_ref<T>::required_alignment) T x{&t[1]};
-      std::atomic_ref<T> const a(x);
+      U arr[5]{};
+      alignas(std::atomic_ref<T>::required_alignment) T p{};
+      std::atomic_ref<T> const a(p);
+
+      auto load  = [&]() { return p; };
+      auto store = [&](T val) { p = val; };
+      auto max   = [&](T val, auto order) { return a.fetch_max(val, order); };
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(&t[2]);
-        assert(y == &t[1]);
-        assert(x == &t[2]);
-        y = a.fetch_max(&t[0]);
-        assert(y == &t[2]);
-        assert(x == &t[2]);
-        ASSERT_NOEXCEPT(a.fetch_max(&t[0]));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_max(&t[3], std::memory_order_relaxed);
-        assert(y == &t[2]);
-        assert(a == &t[3]);
-        y = a.fetch_max(&t[0]);
-        assert(y == &t[3]);
-        assert(x == &t[3]);
-        ASSERT_NOEXCEPT(a.fetch_max(&t[0], std::memory_order_relaxed));
-      }
+      ASSERT_NOEXCEPT(a.fetch_max(&arr[0], std::memory_order_seq_cst));
+      test_fetch_max_pointer<U>(&arr[0], &arr[2], &arr[4], load, store, max);
     }
   }
 };
 
 int main(int, char**) {
   TestEachIntegralType<TestFetchMax>()();
+  TestEachPointerType<TestFetchMax>()();
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
 
-  TestEachPointerType<TestFetchMax>()();
-
   TestDoesNotHaveFetchMax<bool>()();
   TestDoesNotHaveFetchMax<UserAtomicType>()();
   TestDoesNotHaveFetchMax<LargeUserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
index b178996b06085..e7f36363ae3a7 100644
--- a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -10,12 +10,14 @@
 // XFAIL: !has-64-bit-atomics
 
 // integral-type fetch_min(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+// T* fetch_min(T*, memory_order = memory_order::seq_cst) const noexcept;
 
 #include <atomic>
 #include <cassert>
 #include <concepts>
 #include <type_traits>
 
+#include "atomic_fetch_min_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -36,64 +38,38 @@ struct TestFetchMin {
     static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_min<T>);
 
     if constexpr (std::is_integral_v<T>) {
-      alignas(std::atomic_ref<T>::required_alignment) T x(T(3));
+      alignas(std::atomic_ref<T>::required_alignment) T x{};
       std::atomic_ref<T> const a(x);
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(T(2));
-        assert(y == T(3));
-        assert(x == T(2));
-        y = a.fetch_min(T(4));
-        assert(y == T(2));
-        assert(x == T(2));
-        ASSERT_NOEXCEPT(a.fetch_min(T(0)));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(T(1), std::memory_order_relaxed);
-        assert(y == T(2));
-        assert(x == T(1));
-        y = a.fetch_min(T(4));
-        assert(y == T(1));
-        assert(x == T(1));
-        ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_relaxed));
-      }
+      auto load  = [&]() { return x; };
+      auto store = [&](T val) { x = val; };
+      auto min   = [&](T val, auto order) { return a.fetch_min(val, order); };
+
+      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_seq_cst));
+      test_fetch_min_integral<T>(load, store, min);
+
     } else if constexpr (std::is_pointer_v<T>) {
       using U = std::remove_pointer_t<T>;
-      U t[9]  = {};
-      alignas(std::atomic_ref<T>::required_alignment) T x{&t[3]};
-      std::atomic_ref<T> const a(x);
+      U arr[5]{};
+      alignas(std::atomic_ref<T>::required_alignment) T p{};
+      std::atomic_ref<T> const a(p);
+
+      auto load  = [&]() { return p; };
+      auto store = [&](T val) { p = val; };
+      auto min   = [&](T val, auto order) { return a.fetch_min(val, order); };
 
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(&t[2]);
-        assert(y == &t[3]);
-        assert(x == &t[2]);
-        y = a.fetch_min(&t[4]);
-        assert(y == &t[2]);
-        assert(x == &t[2]);
-        ASSERT_NOEXCEPT(a.fetch_min(&t[0]));
-      }
-
-      {
-        std::same_as<T> decltype(auto) y = a.fetch_min(&t[1], std::memory_order_relaxed);
-        assert(y == &t[2]);
-        assert(a == &t[1]);
-        y = a.fetch_min(&t[4]);
-        assert(y == &t[1]);
-        assert(x == &t[1]);
-        ASSERT_NOEXCEPT(a.fetch_min(&t[0], std::memory_order_relaxed));
-      }
+      ASSERT_NOEXCEPT(a.fetch_min(&arr[0], std::memory_order_seq_cst));
+      test_fetch_min_pointer<U>(&arr[0], &arr[2], &arr[4], load, store, min);
     }
   }
 };
 
 int main(int, char**) {
   TestEachIntegralType<TestFetchMin>()();
+  TestEachPointerType<TestFetchMin>()();
 
   TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
 
-  TestEachPointerType<TestFetchMin>()();
-
   TestDoesNotHaveFetchMin<bool>()();
   TestDoesNotHaveFetchMin<UserAtomicType>()();
   TestDoesNotHaveFetchMin<LargeUserAtomicType>()();
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
index dc2274a1056cb..7b2a5715ddddd 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -19,11 +19,10 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_max_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -35,23 +34,18 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(std::atomic_fetch_max(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
-
-  // atomic_fetch_max
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_max(&t, T(2)) == T(1));
-    assert(t == T(2));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_max(T(2));
-    assert(r == T(3));
-    assert(t == T(3));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto max   = [&](T val, auto) { return std::atomic_fetch_max(&a, val); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_max(&a, T(0)));
+    test_fetch_max_integral<T>(load, store, max);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_max concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_max should leave
+  // the atomic at the global maximum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
@@ -95,8 +89,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto max   = [&](T* val, auto) { return std::atomic_fetch_max(&a, val); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_max(&a, p0));
+  test_fetch_max_pointer<T>(p0, p2, p4, load, store, max);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
index d878f6abd246d..e811f114708c8 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -21,11 +21,10 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_max_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -37,24 +36,18 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(
-      std::atomic_fetch_max_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
-
-  // atomic_fetch_max_explicit
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_max_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-    assert(t == T(2));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_max(T(2), std::memory_order_seq_cst);
-    assert(r == T(3));
-    assert(t == T(3));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto max   = [&](T val, auto order) { return std::atomic_fetch_max_explicit(&a, val, order); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&a, T(0), std::memory_order_seq_cst));
+    test_fetch_max_integral<T>(load, store, max);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_max_explicit concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_max_explicit should
+  // leave the atomic at the global maximum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
@@ -98,8 +91,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto max   = [&](T* val, auto order) { return std::atomic_fetch_max_explicit(&a, val, order); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&a, p0, std::memory_order_seq_cst));
+  test_fetch_max_pointer<T>(p0, p2, p4, load, store, max);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
index 43277f6a7977a..df40cc7a92c7b 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -19,11 +19,11 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
+#include <limits>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_min_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -35,34 +35,29 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(std::atomic_fetch_min(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0))));
-
-  // atomic_fetch_min
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_min(&t, T(2)) == T(1));
-    assert(t == T(1));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_min(T(2));
-    assert(r == T(3));
-    assert(t == T(2));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto min   = [&](T val, auto) { return std::atomic_fetch_min(&a, val); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_min(&a, T(0)));
+    test_fetch_min_integral<T>(load, store, min);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_min concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_min should leave
+  // the atomic at the global minimum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
 
-    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+    MaybeVolatile<std::atomic<T>> at(std::numeric_limits<T>::max());
 
     std::vector<std::thread> threads;
     threads.reserve(number_of_threads);
     for (auto i = 0; i < number_of_threads; ++i) {
       threads.push_back(support::make_test_thread([&at, i]() {
-        for (auto j = loop - 1; j >= 0; --j) {
+        for (auto j = 0; j < loop; ++j) {
           std::atomic_fetch_min(&at, T(i * loop + j));
         }
       }));
@@ -72,15 +67,7 @@ void test_impl() {
       thread.join();
     }
 
-    const auto times = [](int n) {
-      T res(n);
-      for (auto i = n - 1; i >= 0; --i) {
-        res = std::min<T>(res, i);
-      }
-      return res;
-    };
-
-    assert(at.load() == times(number_of_threads * loop));
+    assert(at.load() == T(0));
   }
 #endif
 }
@@ -95,8 +82,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto min   = [&](T* val, auto) { return std::atomic_fetch_min(&a, val); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_min(&a, p0));
+  test_fetch_min_pointer<T>(p0, p2, p4, load, store, min);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
index 75bf200812ab0..2295b942b86de 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -21,11 +21,11 @@
 #include <algorithm>
 #include <atomic>
 #include <cassert>
-#include <concepts>
+#include <limits>
 #include <type_traits>
-#include <utility>
 #include <vector>
 
+#include "atomic_fetch_min_helper.h"
 #include "atomic_helpers.h"
 #include "test_macros.h"
 
@@ -37,35 +37,29 @@
 
 template <class T, template <class> class MaybeVolatile = std::type_identity_t>
 void test_impl() {
-  static_assert(noexcept(
-      std::atomic_fetch_min_explicit(std::declval<MaybeVolatile<std::atomic<T>>*>(), T(0), std::memory_order_seq_cst)));
-
-  // atomic_fetch_min_explicit
-  {
-    MaybeVolatile<std::atomic<T>> t(T(1));
-    assert(std::atomic_fetch_min_explicit(&t, T(2), std::memory_order_seq_cst) == T(1));
-    assert(t == T(1));
-  }
   {
-    MaybeVolatile<std::atomic<T>> t(T(3));
-    std::same_as<T> decltype(auto) r = t.fetch_min(T(2), std::memory_order_seq_cst);
-    assert(r == T(3));
-    assert(t == T(2));
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto min   = [&](T val, auto order) { return std::atomic_fetch_min_explicit(&a, val, order); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&a, T(0), std::memory_order_seq_cst));
+    test_fetch_min_integral<T>(load, store, min);
   }
 
 #ifndef TEST_HAS_NO_THREADS
-  // atomic_fetch_min_explicit concurrent
+  // Concurrent stress test: many threads calling atomic_fetch_min_explicit should
+  // leave the atomic at the global minimum value seen.
   {
     constexpr auto number_of_threads = 4;
     constexpr auto loop              = 30;
 
-    MaybeVolatile<std::atomic<T>> at(number_of_threads * loop);
+    MaybeVolatile<std::atomic<T>> at(std::numeric_limits<T>::max());
 
     std::vector<std::thread> threads;
     threads.reserve(number_of_threads);
     for (auto i = 0; i < number_of_threads; ++i) {
       threads.push_back(support::make_test_thread([&at, i]() {
-        for (auto j = loop - 1; j >= 0; --j) {
+        for (auto j = 0; j < loop; ++j) {
           std::atomic_fetch_min_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
         }
       }));
@@ -75,15 +69,7 @@ void test_impl() {
       thread.join();
     }
 
-    const auto times = [](int n) {
-      T res(n);
-      for (auto i = n - 1; i >= 0; --i) {
-        res = std::min<T>(res, i);
-      }
-      return res;
-    };
-
-    assert(at.load() == times(number_of_threads * loop));
+    assert(at.load() == T(0));
   }
 #endif
 }
@@ -98,8 +84,33 @@ struct TestFn {
   }
 };
 
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto min   = [&](T* val, auto order) { return std::atomic_fetch_min_explicit(&a, val, order); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&a, p0, std::memory_order_seq_cst));
+  test_fetch_min_pointer<T>(p0, p2, p4, load, store, min);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
 int main(int, char**) {
   TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
 
   return 0;
 }
diff --git a/libcxx/test/support/atomic_fetch_max_helper.h b/libcxx/test/support/atomic_fetch_max_helper.h
new file mode 100644
index 0000000000000..ff4bd7aa38912
--- /dev/null
+++ b/libcxx/test/support/atomic_fetch_max_helper.h
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
+#define TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
+
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Test fetch_max for integral types
+// LoadOp: () -> T - reads current value
+// StoreOp: (T) -> void - stores a value
+// MaxOp: (T, memory_order) -> T - performs fetch_max operation
+template <class T, class LoadOp, class StoreOp, class MaxOp>
+void test_fetch_max_integral(LoadOp load, StoreOp store, MaxOp max) {
+  // Test basic fetch_max (update to larger value)
+  {
+    store(T(10));
+    T old = max(T(20), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(20));
+  }
+
+  // Test with smaller value (no change)
+  {
+    store(T(10));
+    T old = max(T(5), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(10));
+  }
+
+  // Test different memory orderings
+  {
+    store(T(15));
+    T old = max(T(25), std::memory_order_seq_cst);
+    assert(old == T(15));
+    assert(load() == T(25));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(25), std::memory_order_relaxed);
+    assert(old == T(10));
+    assert(load() == T(25));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(30), std::memory_order_acquire);
+    assert(old == T(10));
+    assert(load() == T(30));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(15), std::memory_order_release);
+    assert(old == T(10));
+    assert(load() == T(15));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(22), std::memory_order_acq_rel);
+    assert(old == T(10));
+    assert(load() == T(22));
+  }
+
+  {
+    store(T(15));
+    T old = max(T(35), std::memory_order_relaxed);
+    assert(old == T(15));
+    assert(load() == T(35));
+  }
+
+  // Test return type
+  {
+    store(T(10));
+    static_assert(std::is_same_v<decltype(max(T(20), std::memory_order_seq_cst)), T>);
+  }
+}
+
+// Test fetch_max for pointer types
+// LoadOp: () -> T* - reads current pointer value
+// StoreOp: (T*) -> void - stores a pointer value
+// MaxOp: (T*, memory_order) -> T* - performs fetch_max operation
+template <class T, class LoadOp, class StoreOp, class MaxOp>
+void test_fetch_max_pointer(T* p0, T* p2, T* p4, LoadOp load, StoreOp store, MaxOp max) {
+  // Test basic fetch_max (update to larger pointer)
+  {
+    store(p2);
+    T* old = max(p4, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p4);
+  }
+
+  // Test with smaller pointer (no change)
+  {
+    store(p2);
+    T* old = max(p0, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p2);
+  }
+
+  // Test different memory orderings
+  {
+    store(p0);
+    T* old = max(p2, std::memory_order_seq_cst);
+    assert(old == p0);
+    assert(load() == p2);
+  }
+
+  {
+    store(p0);
+    T* old = max(p2, std::memory_order_relaxed);
+    assert(old == p0);
+    assert(load() == p2);
+  }
+
+  {
+    store(p0);
+    T* old = max(p4, std::memory_order_acquire);
+    assert(old == p0);
+    assert(load() == p4);
+  }
+
+  {
+    store(p0);
+    T* old = max(p4, std::memory_order_release);
+    assert(old == p0);
+    assert(load() == p4);
+  }
+
+  // Test return type
+  {
+    store(p2);
+    static_assert(std::is_same_v<decltype(max(p4, std::memory_order_seq_cst)), T*>);
+  }
+}
+
+#endif // TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
diff --git a/libcxx/test/support/atomic_fetch_min_helper.h b/libcxx/test/support/atomic_fetch_min_helper.h
new file mode 100644
index 0000000000000..20a3558296118
--- /dev/null
+++ b/libcxx/test/support/atomic_fetch_min_helper.h
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H
+#define TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H
+
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Test fetch_min for integral types
+// LoadOp: () -> T - reads current value
+// StoreOp: (T) -> void - stores a value
+// MinOp: (T, memory_order) -> T - performs fetch_min operation
+template <class T, class LoadOp, class StoreOp, class MinOp>
+void test_fetch_min_integral(LoadOp load, StoreOp store, MinOp min) {
+  // Test basic fetch_min (update to smaller value)
+  {
+    store(T(10));
+    T old = min(T(5), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(5));
+  }
+
+  // Test with larger value (no change)
+  {
+    store(T(10));
+    T old = min(T(20), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(10));
+  }
+
+  // Test different memory orderings
+  {
+    store(T(15));
+    T old = min(T(8), std::memory_order_seq_cst);
+    assert(old == T(15));
+    assert(load() == T(8));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(3), std::memory_order_relaxed);
+    assert(old == T(10));
+    assert(load() == T(3));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(2), std::memory_order_acquire);
+    assert(old == T(10));
+    assert(load() == T(2));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(7), std::memory_order_release);
+    assert(old == T(10));
+    assert(load() == T(7));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(4), std::memory_order_acq_rel);
+    assert(old == T(10));
+    assert(load() == T(4));
+  }
+
+  {
+    store(T(15));
+    T old = min(T(6), std::memory_order_relaxed);
+    assert(old == T(15));
+    assert(load() == T(6));
+  }
+
+  // Test return type
+  {
+    store(T(10));
+    static_assert(std::is_same_v<decltype(min(T(5), std::memory_order_seq_cst)), T>);
+  }
+}
+
+// Test fetch_min for pointer types
+// LoadOp: () -> T* - reads current pointer value
+// StoreOp: (T*) -> void - stores a pointer value
+// MinOp: (T*, memory_order) -> T* - performs fetch_min operation
+template <class T, class LoadOp, class StoreOp, class MinOp>
+void test_fetch_min_pointer(T* p0, T* p2, T* p4, LoadOp load, StoreOp store, MinOp min) {
+  // Test basic fetch_min (update to smaller pointer)
+  {
+    store(p2);
+    T* old = min(p0, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p0);
+  }
+
+  // Test with larger pointer (no change)
+  {
+    store(p2);
+    T* old = min(p4, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p2);
+  }
+
+  // Test different memory orderings
+  {
+    store(p4);
+    T* old = min(p2, std::memory_order_seq_cst);
+    assert(old == p4);
+    assert(load() == p2);
+  }
+
+  {
+    store(p4);
+    T* old = min(p2, std::memory_order_relaxed);
+    assert(old == p4);
+    assert(load() == p2);
+  }
+
+  {
+    store(p4);
+    T* old = min(p0, std::memory_order_acquire);
+    assert(old == p4);
+    assert(load() == p0);
+  }
+
+  {
+    store(p4);
+    T* old = min(p0, std::memory_order_release);
+    assert(old == p4);
+    assert(load() == p0);
+  }
+
+  // Test return type
+  {
+    store(p2);
+    static_assert(std::is_same_v<decltype(min(p0, std::memory_order_seq_cst)), T*>);
+  }
+}
+
+#endif // TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H

>From f8b41b52f67d6ca6dd03617efb6d56ec1c20c6ae Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 16 Jun 2026 07:25:18 -0400
Subject: [PATCH 25/29] Use a CAS loop for pointer types in
 __cxx_atomic_fetch_{max,min} on c11.h

Clang's __c11_atomic_fetch_max/min builtins reject pointer arguments
("address argument to atomic operation must be a pointer to atomic integer
or supported floating point type"). The four __cxx_atomic_fetch_{max,min}
overloads in support/c11.h were unconditionally calling those builtins, so
std::atomic<T*>::fetch_max/min failed to compile on every Clang-based
config. support/gcc.h already had this dispatch via #if __has_builtin(...)
falling back to a CAS loop, which is why generic-gcc was passing.

Dispatch on is_pointer<_Tp>::value: pointer types use a CAS loop matching
gcc.h's body, others keep calling the builtin.

Assisted by Claude
---
 libcxx/include/__atomic/support/c11.h | 56 +++++++++++++++++++++++----
 1 file changed, 48 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0d74d55188a3d..603ffc690b104 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -13,6 +13,7 @@
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/remove_const.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -259,30 +260,69 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
       std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
 }
 
+// Clang's __c11_atomic_fetch_max/min builtins do not accept pointer
+// arguments, so dispatch to a CAS loop for pointer types.
+// TODO: Use the builtin for pointer types once Clang accepts them.
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret > __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_max(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_max(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret > __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_max(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret < __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_min(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  return __c11_atomic_fetch_min(
-      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  if constexpr (is_pointer<_Tp>::value) {
+    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+    _Tp __value;
+    do {
+      __value = __ret < __val ? __ret : __val;
+    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+    return __ret;
+  } else {
+    return __c11_atomic_fetch_min(
+        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+  }
 }
 
 _LIBCPP_END_NAMESPACE_STD

>From 77d8902e33364fe5887036f6dc36550094841d7b Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 16 Jun 2026 10:06:33 -0400
Subject: [PATCH 26/29] Switch __cxx_atomic_fetch_{max,min} pointer dispatch to
 SFINAE for C++03 compat

I know they're not needed outside of C++26, but this header should be
usable in all standard modes in principle.
---
 libcxx/include/__atomic/support/c11.h | 103 ++++++++++++++------------
 1 file changed, 55 insertions(+), 48 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 603ffc690b104..58a97fcfb3261 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -13,6 +13,7 @@
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/remove_const.h>
 
@@ -263,66 +264,72 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 // Clang's __c11_atomic_fetch_max/min builtins do not accept pointer
 // arguments, so dispatch to a CAS loop for pointer types.
 // TODO: Use the builtin for pointer types once Clang accepts them.
-template <class _Tp>
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret > __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_max(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret > __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+}
+
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret > __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_max(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret > __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret < __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_min(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret < __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+}
+
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  if constexpr (is_pointer<_Tp>::value) {
-    _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
-    _Tp __value;
-    do {
-      __value = __ret < __val ? __ret : __val;
-    } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
-    return __ret;
-  } else {
-    return __c11_atomic_fetch_min(
-        std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
-  }
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret < __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
 }
 
 _LIBCPP_END_NAMESPACE_STD

>From 269691062efd541709388ab2b330e91b9b94cf91 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 26 Jun 2026 21:35:32 +0800
Subject: [PATCH 27/29] eliminate adl warning

---
 libcxx/include/__atomic/support/c11.h | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 58a97fcfb3261..899502422570a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -273,11 +273,12 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, mem
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -290,11 +291,12 @@ __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret > __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -307,11 +309,12 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, mem
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 
@@ -324,11 +327,12 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order
 template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
-  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
   do {
     __value = __ret < __val ? __ret : __val;
-  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
   return __ret;
 }
 

>From 8e4a7ab9ec21ca5484aa4309b0f3d9943d5ec9d8 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 20:13:01 +0800
Subject: [PATCH 28/29] release notes 23 -> 24

---
 libcxx/docs/ReleaseNotes/23.rst | 1 -
 libcxx/docs/ReleaseNotes/24.rst | 1 +
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e806502decc0e..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -39,7 +39,6 @@ Implemented Papers
 ------------------
 
 - P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
-- P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
 - P1885R12: Naming Text Encodings to Demystify Them (`Github <https://llvm.org/PR105373>`__)
 - P1899R3: ``stride_view`` (`Github <https://llvm.org/PR105198>`__)
 - P2862R1: ``text_encoding::name()`` should never return null values (`Github <https://llvm.org/PR118371>`__)
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..70c704ff9d326 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
 
 Improvements and New Features
 -----------------------------

>From 609355a88e8c3dd9e24b1f2185204b60dae3ba37 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 20:24:06 +0800
Subject: [PATCH 29/29] release notes 23 -> 24

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 49bbcdd613efd..5e10789011f72 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -53,7 +53,7 @@
 "`P3107R5 <https://wg21.link/P3107R5>`__","Permit an efficient implementation of ``std::print``","2024-03 (Tokyo)","","","`#105414 <https://github.com/llvm/llvm-project/issues/105414>`__",""
 "`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)"
 "`P2845R8 <https://wg21.link/P2845R8>`__","Formatting of ``std::filesystem::path``","2024-03 (Tokyo)","","","`#105416 <https://github.com/llvm/llvm-project/issues/105416>`__",""
-"`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","|Complete|","23","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
+"`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","|Complete|","24","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
 "`P2542R8 <https://wg21.link/P2542R8>`__","``views::concat``","2024-03 (Tokyo)","|Complete|","23","`#105419 <https://github.com/llvm/llvm-project/issues/105419>`__",""
 "`P2591R5 <https://wg21.link/P2591R5>`__","Concatenation of strings and string views","2024-03 (Tokyo)","|Complete|","19","`#105420 <https://github.com/llvm/llvm-project/issues/105420>`__",""
 "`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","`#105421 <https://github.com/llvm/llvm-project/issues/105421>`__",""


From libcxx-commits at lists.llvm.org  Mon Jul 20 05:26:21 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Mon, 20 Jul 2026 05:26:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0493R5: Atomic
	minimum/maximum (PR #180333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/180333@github.com>
Message-ID: <6a5e13ed.f1c1923d.2d2b59.951b@mx.google.com>


c8ef wrote:

The release notes and implemented versions now point at the newly created 24.

https://github.com/llvm/llvm-project/pull/180333

From libcxx-commits at lists.llvm.org  Mon Jul 20 05:35:30 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 05:35:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] re-order availability
 mapping to `_LIBCPP_INTRODUCED_IN_LLVM_{N}` (PR #210694)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210694@github.com>
Message-ID: <6a5e1612.9edb5c9d.280be3.f0b1@mx.google.com>


https://github.com/Zingam approved this pull request.


https://github.com/llvm/llvm-project/pull/210694

From libcxx-commits at lists.llvm.org  Mon Jul 20 05:45:03 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 05:45:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a5e184f.7d702ed2.13d102.19cb@mx.google.com>


================
@@ -82,7 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
----------------
Zingam wrote:

Some tests are relying on these these constructors being public. Updates are necessary to restore the tests after making these constructors private.

> Also, I don't think this PR should be of WIP status for so long. IIUC we just need to add some `static_assert(!std::is_constructible_v<...>);` and possibly `static_assert(!std::is_convertible_v<...>);` to verify that we've achieved (some aspects of) the intent of P3059R2.

That's a good point.

https://github.com/llvm/llvm-project/pull/193891

From libcxx-commits at lists.llvm.org  Mon Jul 20 05:47:11 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 20 Jul 2026 05:47:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format a few
 `elements_view` tests (PR #210687)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210687@github.com>
Message-ID: <6a5e18cf.d681cab9.169270.0e69@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/210687

From libcxx-commits at lists.llvm.org  Mon Jul 20 05:53:17 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 05:53:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a5e1a3d.3cce8983.3cf542.9f43@mx.google.com>


================
@@ -82,7 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
----------------
Zingam wrote:

@frederick-vs-ja What do you think about adding these test cases (`static_assert`-s) in a follow-up as this PR is getting quite a lot of changes already? (That would have been a nice case for stacked-PRs as it used to be but I wouldn't bother attempting these in GH right now)

https://github.com/llvm/llvm-project/pull/193891

From libcxx-commits at lists.llvm.org  Mon Jul 20 05:56:02 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 05:56:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format a couple of
 `elements_view` tests (PR #210687)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210687@github.com>
Message-ID: <6a5e1ae2.e55dc341.1c6a0.d09c@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/210687

From libcxx-commits at lists.llvm.org  Mon Jul 20 05:57:32 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 05:57:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize to_chars integral
 overloads for base 2 and 16 (PR #210659)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210659@github.com>
Message-ID: <6a5e1b3c.d2cb4adc.ccd80.cb11@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/210659

>From 1e1f53b4adc43cd47ff8267712bebdceea1df8ba Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 18 Jul 2026 13:03:43 +0200
Subject: [PATCH] [libc++] Optimize to_chars integral overloads

---
 libcxx/include/__charconv/to_chars_integral.h | 90 +++++++++++++++++++
 libcxx/test/support/charconv_test_helpers.h   |  2 -
 2 files changed, 90 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__charconv/to_chars_integral.h b/libcxx/include/__charconv/to_chars_integral.h
index 6d425139260b6..c53374badaef9 100644
--- a/libcxx/include/__charconv/to_chars_integral.h
+++ b/libcxx/include/__charconv/to_chars_integral.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___CHARCONV_TO_CHARS_INTEGRAL_H
 
 #include <__algorithm/copy_n.h>
+#include <__algorithm/simd_utils.h>
 #include <__assert>
 #include <__bit/countl.h>
 #include <__charconv/tables.h>
@@ -22,12 +23,14 @@
 #include <__system_error/errc.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_integral.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_signed.h>
 #include <__type_traits/make_32_64_or_128_bit.h>
 #include <__type_traits/make_unsigned.h>
 #include <__utility/unreachable.h>
+#include <array>
 #include <cstdint>
 #include <limits>
 
@@ -107,6 +110,29 @@ __to_chars_integral(char* __first, char* __last, _Tp __value, int __base) {
 
 namespace __itoa {
 
+template <size_t _Duplicate, size_t _IndexCount>
+constexpr array<size_t, _IndexCount * _Duplicate> __build_indices() {
+  array<size_t, _IndexCount * _Duplicate> __ret;
+  for (size_t __i = 0; __i != _IndexCount; ++__i) {
+    for (size_t __k = 0; __k != _Duplicate; ++__k)
+      __ret[__i * _Duplicate + __k] = _IndexCount - __i - 1;
+  }
+  return __ret;
+}
+
+_LIBCPP_DIAGNOSTIC_PUSH
+// TODO: remove this once all supported compilers diagnose functions correctly
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
+// This is marked `always_inline` because it interacts with simd vectors
+template <size_t _Duplicate, size_t _VecSize>
+[[__gnu__::__always_inline__]] auto __duplicate_vector_entries(__simd_vector<char, _VecSize> __vals) {
+  static constexpr auto __indices = __itoa::__build_indices<_Duplicate, _VecSize>();
+  return [&]<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+    return __builtin_shufflevector(__vals, __vals, __indices[_Indices]...);
+  }(make_index_sequence<_Duplicate * _VecSize>());
+}
+_LIBCPP_DIAGNOSTIC_POP
+
 template <unsigned _Base>
 struct _LIBCPP_HIDDEN __integral;
 
@@ -122,6 +148,35 @@ struct _LIBCPP_HIDDEN __integral<2> {
   template <typename _Tp>
   _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
   __to_chars(char* __first, char* __last, _Tp __value) {
+#if _LIBCPP_VECTORIZE_ALGORITHMS && __has_builtin(__builtin_masked_store)
+    if (!__libcpp_is_constant_evaluated() && __last - __first >= numeric_limits<_Tp>::digits) {
+      // Move the to-be-converted bits into the high bits, so that they are printed at the start.
+      auto __char_count = __width(__value);
+      auto __shift      = std::__countl_zero(__value | 1);
+      __value <<= __shift;
+
+      // Move the value into a vector and chop it up into its constituent bytes
+      auto __chopped    = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
+
+      // Duplicate values so we can extract the appropriate bits in multiple positions
+      auto __characters = __itoa::__duplicate_vector_entries<8>(__chopped);
+
+      // This is marked `always_inline` because it interacts with simd vectors
+      auto __shifts     = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+        return __simd_vector<char, 8 * sizeof(_Tp)>{(7 - _Indices % 8)...};
+      }(make_index_sequence<8 * sizeof(_Tp)>());
+
+      // Check the appropriate bit for the position and set the character to 0 or 1 depending on whether it's set.
+      __characters = (__characters & __shifts) == 0 ? '0' : '1';
+
+      // Store all the characters, no matter whether they're part of the value or not. This is only safe if the buffer
+      // we've been given is large enough.
+      // TODO: Generate a mask on platforms which have native masked stores and use this code path unconditionally.
+      __builtin_masked_store(__simd_vector<bool, 8 * sizeof(_Tp)>(true), __characters, __first);
+      return {__first + __char_count, errc(0)};
+    }
+#endif
+
     ptrdiff_t __cap = __last - __first;
     int __n         = __width(__value);
     if (__n > __cap)
@@ -192,6 +247,41 @@ struct _LIBCPP_HIDDEN __integral<16> {
   template <typename _Tp>
   _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
   __to_chars(char* __first, char* __last, _Tp __value) {
+#if _LIBCPP_VECTORIZE_ALGORITHMS && __has_builtin(__builtin_masked_store)
+    if (!__libcpp_is_constant_evaluated() && __last - __first >= (numeric_limits<_Tp>::digits / 4)) {
+      // Lambdas are marked as `always_inline` because they return vectors, which aren't ABI-stable
+
+      // Move the to-be-converted bits into the high bits, so that they are printed at the start. Note that the shift is
+      // 4-bit aligned, so that the value of the characters doesn't change.
+      auto __char_count = __width(__value);
+      auto __shift      = std::__countl_zero(__value | 1) & ~3;
+      __value <<= __shift;
+
+      // Move the value into a vector and chop it up into its constituent bytes
+      auto __chopped    = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
+
+      // Duplicate values so we can extract the appropriate bits in multiple positions
+      auto __characters = __itoa::__duplicate_vector_entries<2>(__chopped);
+
+      // This is marked `always_inline` because it interacts with simd vectors
+      auto __shifts     = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+        return __simd_vector<char, 2 * sizeof(_Tp)>{(_Indices % 2 == 0 ? 4 : 0)...};
+      }(make_index_sequence<2 * sizeof(_Tp)>());
+
+      // Extract the bits we want for a given offset
+      __characters = (__characters >> __shifts) & 15;
+
+      // Convert the value into the hexadecimal character
+      __characters = (__characters >= 10 ? char('a' - 10) : '0') + __characters;
+
+      // Store all the characters, no matter whether they're part of the value or not. This is only safe if the buffer
+      // we've been given is large enough.
+      // TODO: Generate a mask on platforms which have native masked stores and use this code path unconditionally.
+      __builtin_masked_store(__simd_vector<bool, 2 * sizeof(_Tp)>(true), __characters, __first);
+      return {__first + __char_count, errc(0)};
+    }
+#endif
+
     ptrdiff_t __cap = __last - __first;
     int __n         = __width(__value);
     if (__n > __cap)
diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index fcae09478457b..f6b60a2c8fe4d 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -113,8 +113,6 @@ struct to_chars_test_base
         std::iota(buf, buf + sizeof(buf), static_cast<unsigned char>(1));
         r = std::to_chars(buf, buf + sizeof(buf), v, args...);
         assert(r.ec == std::errc{});
-        for (std::size_t i = r.ptr - buf; i < sizeof(buf); ++i)
-            assert(static_cast<unsigned char>(buf[i]) == i + 1);
         *r.ptr = '\0';
 
 #ifndef TEST_HAS_NO_INT128


From libcxx-commits at lists.llvm.org  Mon Jul 20 05:58:24 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 05:58:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a5e1b70.4a342cbc.3a2f36.8add@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/193891

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:04:28 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 06:04:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a5e1cdc.8e75e0cb.f69c7.14b3@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/193891

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:10:25 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:10:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] re-order availability
 mapping to `_LIBCPP_INTRODUCED_IN_LLVM_{N}` (PR #210694)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210694@github.com>
Message-ID: <6a5e1e41.507fc713.239434.f066@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/210694

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:10:35 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:10:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Re-order availability
 mapping to `_LIBCPP_INTRODUCED_IN_LLVM_{N}` (PR #210694)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210694@github.com>
Message-ID: <6a5e1e4b.e55dc341.1c6a0.e4c9@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/210694

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:12:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:12:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][docs] Avoid talking about other
 implementations on DR status (PR #210637)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210637@github.com>
Message-ID: <6a5e1ebb.510f576d.2f694c.7b85@mx.google.com>


https://github.com/ldionne approved this pull request.

I think this makes sense -- we shouldn't try to track other implementation's DR status in our docs, it will just get stale and it's not authoritative anyway.

https://github.com/llvm/llvm-project/pull/210637

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:13:54 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 06:13:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 03b7806 - [libc++][test] Fix build
 failure from typo in #171785 (#210552)
Message-ID: <6a5e1f12.1905e68e.30d957.d411@mx.google.com>


Author: Thurston Dang
Date: 2026-07-20T09:13:49-04:00
New Revision: 03b780679bdb5152aef0182dfc8b2f1d182a94e0

URL: https://github.com/llvm/llvm-project/commit/03b780679bdb5152aef0182dfc8b2f1d182a94e0
DIFF: https://github.com/llvm/llvm-project/commit/03b780679bdb5152aef0182dfc8b2f1d182a94e0.diff

LOG: [libc++][test] Fix build failure from typo in #171785 (#210552)

If `__STDCPP_DEFAULT_NEW_ALIGNMENT__` is not defined, these tests will
fail to build due to a typo that was added in #171785.

Added: 
    

Modified: 
    libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
    libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
index 367a58897cec0..f2208f5ae6934 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
@@ -18,7 +18,7 @@
 #ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
 static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
 #else
-static const std::size_t alginment = 8;
+static const std::size_t alignment = 8;
 #endif
 
 int main(int, char**) {

diff  --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
index 15736e4a1ceed..72d217d5611b9 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -20,7 +20,7 @@
 #ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
 static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
 #else
-static const std::size_t alginment = 8;
+static const std::size_t alignment = 8;
 #endif
 
 template <class = int>


        

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:13:57 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:13:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx][test] Fix build failure from
	typo in #171785 (PR #210552)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210552@github.com>
Message-ID: <6a5e1f15.d76d81af.23b347.0dac@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/210552

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:16:03 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:16:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some std::minmax_element
	benchmarks (PR #210272)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210272@github.com>
Message-ID: <6a5e1f93.e624eb8d.4a8a7.2a31@mx.google.com>


https://github.com/ldionne commented:

I think we should land https://github.com/llvm/llvm-project/pull/209590 instead.

https://github.com/llvm/llvm-project/pull/210272

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:17:48 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Mon, 20 Jul 2026 06:17:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
 classification / comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a5e1ffc.e624eb8d.4a8a7.80b3@mx.google.com>


================
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
----------------
lknknm wrote:

Confirmed Windows tests are passing.

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:19:34 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:19:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210360@github.com>
Message-ID: <6a5e2066.824e11a6.9c376.a729@mx.google.com>


================
@@ -562,11 +562,10 @@ void locale::facet::__on_zero_shared() noexcept { delete this; }
 
 // locale::id
 
-constinit int32_t locale::id::__next_id = 0;
-
 long locale::id::__get() {
-  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&__next_id, 1); });
-  return __id_ - 1;
+  constinit static atomic<int32_t> next_id = 0;
----------------
ldionne wrote:

Aren't we going to start hitting the same problem we did in https://github.com/llvm/llvm-project/pull/198994 ?

https://github.com/llvm/llvm-project/pull/210360

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:19:34 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:19:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210360@github.com>
Message-ID: <6a5e2066.e5907290.281687.d1cf@mx.google.com>


https://github.com/ldionne requested changes to this pull request.


https://github.com/llvm/llvm-project/pull/210360

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:22:36 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 06:22:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210360@github.com>
Message-ID: <6a5e211c.4987c784.2c60a9.fe26@mx.google.com>


================
@@ -562,11 +562,10 @@ void locale::facet::__on_zero_shared() noexcept { delete this; }
 
 // locale::id
 
-constinit int32_t locale::id::__next_id = 0;
-
 long locale::id::__get() {
-  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&__next_id, 1); });
-  return __id_ - 1;
+  constinit static atomic<int32_t> next_id = 0;
----------------
philnik777 wrote:

I don't see how. I can leave it as `__libcpp_atomic_add` if you like, but I'm not removing any conditional compilation here.

https://github.com/llvm/llvm-project/pull/210360

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:24:33 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:24:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5e2191.824e11a6.9c376.38af@mx.google.com>


================
@@ -1359,13 +1359,13 @@ public:
   template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
   append(_ForwardIterator __first, _ForwardIterator __last) {
-    size_type __sz  = size();
-    size_type __cap = capacity();
-    size_type __n   = static_cast<size_type>(std::distance(__first, __last));
----------------
ldionne wrote:

I think this actually makes a lot of sense. It seems very realistic to me that computing the distance between two iterators could be more expensive than checking whether the iterator is at the end. Hence, checking `first == last` explicitly instead of inferring it from a potentially much more complex computation `distance(first, last) == 0` makes sense to me.

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:24:33 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:24:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5e2191.c4d3d80a.849c3.9187@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:25:11 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:25:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5e21b7.160324b0.1f653e.31fe@mx.google.com>


ldionne wrote:

/libcxx bot benchmark libcxx/test/benchmarks/containers/string.bench.cpp

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:27:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:27:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5e223f.1905e68e.30d957.7493@mx.google.com>


ldionne wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/containers/string.bench.cpp

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:29:18 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:29:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0493R5: Atomic
	minimum/maximum (PR #180333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/180333@github.com>
Message-ID: <6a5e22ae.507fc713.239434.6947@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/180333

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:32:34 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:32:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::swap of locales
	(PR #209760)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209760@github.com>
Message-ID: <6a5e2372.885dd770.25203b.8069@mx.google.com>


================
@@ -143,8 +143,14 @@ private:
   friend bool has_facet(const locale&) _NOEXCEPT;
   template <class _Facet>
   friend const _Facet& use_facet(const locale&);
+
+  friend void swap(locale&, locale&);
 };
 
+inline void swap(locale& __lhs, locale& __rhs) {
----------------
ldionne wrote:

Sorry, I might be missing something extremely obvious but how is the existence of a move constructor observable here, since there's already a copy constructor?

https://github.com/llvm/llvm-project/pull/209760

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:35:46 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:35:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][utils] Fix LIT `%if`
	substitution support (PR #209358)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209358@github.com>
Message-ID: <6a5e2432.d9789fc0.98a70.6a61@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/209358

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:36:48 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 06:36:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] a861703 - [libc++][utils] Fix LIT `%if`
 substitution support (#209358)
Message-ID: <6a5e2470.0c106a4a.26cedc.237d@mx.google.com>


Author: Hubert Tong
Date: 2026-07-20T09:36:43-04:00
New Revision: a8617034c4a7cc733676a31b736d3118fec34259

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

LOG: [libc++][utils] Fix LIT `%if` substitution support (#209358)

Update the applySubstitutions call in `libcxx/utils/libcxx/test/format.py` to match the
change to `llvm/utils/lit/lit/TestRunner.py` done in 1041a9642ba0.

Added: 
    libcxx/test/selftest/if-else.sh.cpp

Modified: 
    libcxx/utils/libcxx/test/format.py

Removed: 
    


################################################################################
diff  --git a/libcxx/test/selftest/if-else.sh.cpp b/libcxx/test/selftest/if-else.sh.cpp
new file mode 100644
index 0000000000000..06b09ac9067a5
--- /dev/null
+++ b/libcxx/test/selftest/if-else.sh.cpp
@@ -0,0 +1,11 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Make sure that LIT `%if` substitutions are aware of the available features.
+
+// RUN: not %if target={{.+}} %{ not %} echo "hello world"

diff  --git a/libcxx/utils/libcxx/test/format.py b/libcxx/utils/libcxx/test/format.py
index 49cbe8a8db618..e3e81fd1cf36f 100644
--- a/libcxx/utils/libcxx/test/format.py
+++ b/libcxx/utils/libcxx/test/format.py
@@ -205,8 +205,12 @@ def parseScript(test, preamble):
         )
 
     # Perform substitutions in the script itself.
+    conditions = {feature: True for feature in test.config.available_features}
     script = lit.TestRunner.applySubstitutions(
-        script, substitutions, recursion_limit=test.config.recursiveExpansionLimit
+        script,
+        substitutions,
+        conditions,
+        recursion_limit=test.config.recursiveExpansionLimit,
     )
 
     return script


        

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:36:52 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:36:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][utils] Fix LIT `%if`
	substitution support (PR #209358)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209358@github.com>
Message-ID: <6a5e2474.c6d84bbd.13aacc.ef7c@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/209358

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:36:52 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 06:36:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::swap of locales
	(PR #209760)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209760@github.com>
Message-ID: <6a5e2474.39be5633.24511e.893a@mx.google.com>


================
@@ -143,8 +143,14 @@ private:
   friend bool has_facet(const locale&) _NOEXCEPT;
   template <class _Facet>
   friend const _Facet& use_facet(const locale&);
+
+  friend void swap(locale&, locale&);
 };
 
+inline void swap(locale& __lhs, locale& __rhs) {
----------------
philnik777 wrote:

By behaving different from a copy. You're essentially allowed to move as many times as you want from a locale. It may be questionable code, but it's definitely correct currently.

https://github.com/llvm/llvm-project/pull/209760

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:37:51 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:37:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Remove atomic workaround (PR
	#208049)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208049@github.com>
Message-ID: <6a5e24af.ec9c0add.1d27a8.8b88@mx.google.com>


ldionne wrote:

Moving to draft since this is an experiment PR

https://github.com/llvm/llvm-project/pull/208049

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:37:54 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:37:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Remove atomic workaround (PR
	#208049)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208049@github.com>
Message-ID: <6a5e24b2.0c106a4a.26cedc.2bcf@mx.google.com>


https://github.com/ldionne converted_to_draft https://github.com/llvm/llvm-project/pull/208049

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:38:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 06:38:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] faf7261 - [libc++] Bring std::{any, all,
 none}_of tests up to current standards (#209266)
Message-ID: <6a5e24c2.ec9c0add.1d27a8.8da3@mx.google.com>


Author: Louis Dionne
Date: 2026-07-20T09:38:04-04:00
New Revision: faf7261308da236627ae8f813d17d6e1ef1e9d68

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

LOG: [libc++] Bring std::{any,all,none}_of tests up to current standards (#209266)

The tests for these algorithms were minimal, didn't test all iterator
categories, and the constexpr coverage didn't follow our current
practice. This patch brings all three tests (which are very similar) up
to our current standards.

Assisted by Claude

Added: 
    

Modified: 
    libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/all_of.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/any_of.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/none_of.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/all_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/all_of.pass.cpp
index f9377fb10277f..acbdeda0f82e6 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/all_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.all_of/all_of.pass.cpp
@@ -9,55 +9,90 @@
 // <algorithm>
 
 // template <class InputIterator, class Predicate>
-//     constexpr bool       // constexpr after C++17
-//   all_of(InputIterator first, InputIterator last, Predicate pred);
+// constexpr bool all_of(InputIterator first, InputIterator last, Predicate pred); // constexpr since C++20
 
 #include <algorithm>
 #include <cassert>
-#include <iterator>
 
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "type_algorithms.h"
 
-struct test1
-{
-    TEST_CONSTEXPR bool operator()(const int& i) const
-    {
-        return i % 2 == 0;
-    }
+struct is_odd {
+  TEST_CONSTEXPR bool operator()(const int& i) const { return i % 2 != 0; }
+};
+
+struct counting_predicate {
+  int* times_applied;
+  bool result;
+  TEST_CONSTEXPR counting_predicate(int* counter, bool r) : times_applied(counter), result(r) {}
+  TEST_CONSTEXPR_CXX14 bool operator()(int) const {
+    ++*times_applied;
+    return result;
+  }
 };
 
-#if TEST_STD_VER > 17
-TEST_CONSTEXPR bool test_constexpr() {
-    int ia[] = {2, 4, 6, 8};
-    int ib[] = {2, 4, 5, 8};
-    return  std::all_of(std::begin(ia), std::end(ia), test1())
-        && !std::all_of(std::begin(ib), std::end(ib), test1())
-        ;
+struct Test {
+  template <class Iter>
+  TEST_CONSTEXPR_CXX20 void operator()() {
+    { // an empty range vacuously satisfies the predicate
+      int a[] = {2, 4, 6, 8};
+      // The return type is always `bool`, regardless of the iterator category.
+      ASSERT_SAME_TYPE(bool, decltype(std::all_of(Iter(a), Iter(a), is_odd())));
+      assert(std::all_of(Iter(a), Iter(a), is_odd()));
+    }
+
+    { // every element satisfies the predicate
+      int a[] = {1, 3, 5, 7};
+      assert(std::all_of(Iter(a), Iter(a + 4), is_odd()));
+    }
+
+    { // no element satisfies the predicate
+      int a[] = {2, 4, 6, 8};
+      assert(!std::all_of(Iter(a), Iter(a + 4), is_odd()));
+    }
+
+    { // the only non-satisfying element is the first one
+      int a[] = {2, 1, 3, 5};
+      assert(!std::all_of(Iter(a), Iter(a + 4), is_odd()));
     }
-#endif
 
-int main(int, char**)
-{
-    {
-        int ia[] = {2, 4, 6, 8};
-        const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-        assert(std::all_of(cpp17_input_iterator<const int*>(ia),
-                           cpp17_input_iterator<const int*>(ia + sa), test1()) == true);
-        assert(std::all_of(cpp17_input_iterator<const int*>(ia),
-                           cpp17_input_iterator<const int*>(ia), test1()) == true);
+    { // the only non-satisfying element is in the middle
+      int a[] = {1, 3, 4, 5};
+      assert(!std::all_of(Iter(a), Iter(a + 4), is_odd()));
     }
-    {
-        const int ia[] = {2, 4, 5, 8};
-        const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-        assert(std::all_of(cpp17_input_iterator<const int*>(ia),
-                           cpp17_input_iterator<const int*>(ia + sa), test1()) == false);
-        assert(std::all_of(cpp17_input_iterator<const int*>(ia),
-                           cpp17_input_iterator<const int*>(ia), test1()) == true);
+
+    { // the only non-satisfying element is the last one
+      int a[] = {1, 3, 5, 8};
+      assert(!std::all_of(Iter(a), Iter(a + 4), is_odd()));
     }
 
-#if TEST_STD_VER > 17
-    static_assert(test_constexpr());
+    { // a single-element range, satisfying and not satisfying the predicate
+      int satisfies     = 1;
+      int not_satisfies = 2;
+      assert(std::all_of(Iter(&satisfies), Iter(&satisfies + 1), is_odd()));
+      assert(!std::all_of(Iter(&not_satisfies), Iter(&not_satisfies + 1), is_odd()));
+    }
+  }
+};
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  types::for_each(types::cpp17_input_iterator_list<int*>(), Test());
+
+  { // the predicate is applied at most `last - first` times (complexity requirement)
+    int a[]     = {1, 2, 3, 4, 5};
+    int applied = 0;
+    assert(std::all_of(a, a + 5, counting_predicate(&applied, true)));
+    assert(applied == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 20
+  static_assert(test());
 #endif
 
   return 0;

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/any_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/any_of.pass.cpp
index c0b659304db8b..6eb815d4bf652 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/any_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.any_of/any_of.pass.cpp
@@ -9,63 +9,90 @@
 // <algorithm>
 
 // template <class InputIterator, class Predicate>
-//     constexpr bool       // constexpr after C++17
-//   any_of(InputIterator first, InputIterator last, Predicate pred);
+// constexpr bool any_of(InputIterator first, InputIterator last, Predicate pred); // constexpr since C++20
 
 #include <algorithm>
 #include <cassert>
-#include <iterator>
 
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "type_algorithms.h"
 
-struct test1
-{
-    TEST_CONSTEXPR bool operator()(const int& i) const
-    {
-        return i % 2 == 0;
-    }
+struct is_odd {
+  TEST_CONSTEXPR bool operator()(const int& i) const { return i % 2 != 0; }
+};
+
+struct counting_predicate {
+  int* times_applied;
+  bool result;
+  TEST_CONSTEXPR counting_predicate(int* counter, bool r) : times_applied(counter), result(r) {}
+  TEST_CONSTEXPR_CXX14 bool operator()(int) const {
+    ++*times_applied;
+    return result;
+  }
 };
 
-#if TEST_STD_VER > 17
-TEST_CONSTEXPR bool test_constexpr() {
-    int ia[] = {2, 4, 6, 8};
-    int ib[] = {1, 3, 5, 7};
-    return  std::any_of(std::begin(ia), std::end(ia), test1())
-        && !std::any_of(std::begin(ib), std::end(ib), test1())
-        ;
+struct Test {
+  template <class Iter>
+  TEST_CONSTEXPR_CXX20 void operator()() {
+    { // an empty range never contains a matching element
+      int a[] = {1, 2, 3, 4};
+      // The return type is always `bool`, regardless of the iterator category.
+      ASSERT_SAME_TYPE(bool, decltype(std::any_of(Iter(a), Iter(a), is_odd())));
+      assert(!std::any_of(Iter(a), Iter(a), is_odd()));
     }
-#endif
 
-int main(int, char**)
-{
-    {
-        int ia[] = {2, 4, 6, 8};
-        const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-        assert(std::any_of(cpp17_input_iterator<const int*>(ia),
-                           cpp17_input_iterator<const int*>(ia + sa), test1()) == true);
-        assert(std::any_of(cpp17_input_iterator<const int*>(ia),
-                           cpp17_input_iterator<const int*>(ia), test1()) == false);
+    { // no element matches the predicate
+      int a[] = {2, 4, 6, 8};
+      assert(!std::any_of(Iter(a), Iter(a + 4), is_odd()));
     }
-    {
-        const int ia[] = {2, 4, 5, 8};
-        const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-        assert(std::any_of(cpp17_input_iterator<const int*>(ia),
-                           cpp17_input_iterator<const int*>(ia + sa), test1()) == true);
-        assert(std::any_of(cpp17_input_iterator<const int*>(ia),
-                           cpp17_input_iterator<const int*>(ia), test1()) == false);
+
+    { // every element matches the predicate
+      int a[] = {1, 3, 5, 7};
+      assert(std::any_of(Iter(a), Iter(a + 4), is_odd()));
     }
-    {
-        const int ia[] = {1, 3, 5, 7};
-        const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-        assert(std::any_of(cpp17_input_iterator<const int*>(ia),
-                           cpp17_input_iterator<const int*>(ia + sa), test1()) == false);
-        assert(std::any_of(cpp17_input_iterator<const int*>(ia),
-                           cpp17_input_iterator<const int*>(ia), test1()) == false);
+
+    { // the only matching element is the first one
+      int a[] = {1, 2, 4, 6};
+      assert(std::any_of(Iter(a), Iter(a + 4), is_odd()));
+    }
+
+    { // the only matching element is in the middle
+      int a[] = {2, 4, 5, 8};
+      assert(std::any_of(Iter(a), Iter(a + 4), is_odd()));
     }
 
-#if TEST_STD_VER > 17
-    static_assert(test_constexpr());
+    { // the only matching element is the last one
+      int a[] = {2, 4, 6, 7};
+      assert(std::any_of(Iter(a), Iter(a + 4), is_odd()));
+    }
+
+    { // a single-element range, with and without a match
+      int match    = 1;
+      int no_match = 2;
+      assert(std::any_of(Iter(&match), Iter(&match + 1), is_odd()));
+      assert(!std::any_of(Iter(&no_match), Iter(&no_match + 1), is_odd()));
+    }
+  }
+};
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  types::for_each(types::cpp17_input_iterator_list<int*>(), Test());
+
+  { // the predicate is applied at most `last - first` times (complexity requirement)
+    int a[]     = {1, 2, 3, 4, 5};
+    int applied = 0;
+    assert(!std::any_of(a, a + 5, counting_predicate(&applied, false)));
+    assert(applied == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 20
+  static_assert(test());
 #endif
 
   return 0;

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/none_of.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/none_of.pass.cpp
index 8d63ecb6c17a5..4a1c5c5bd5c4d 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/none_of.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.none_of/none_of.pass.cpp
@@ -9,63 +9,90 @@
 // <algorithm>
 
 // template <class InputIterator, class Predicate>
-//   bool
-//   none_of(InputIterator first, InputIterator last, Predicate pred);
+// constexpr bool none_of(InputIterator first, InputIterator last, Predicate pred); // constexpr since C++20
 
 #include <algorithm>
 #include <cassert>
-#include <iterator>
 
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "type_algorithms.h"
 
-struct test1
-{
-    TEST_CONSTEXPR bool operator()(const int& i) const
-    {
-        return i % 2 == 0;
-    }
+struct is_odd {
+  TEST_CONSTEXPR bool operator()(const int& i) const { return i % 2 != 0; }
+};
+
+struct counting_predicate {
+  int* times_applied;
+  bool result;
+  TEST_CONSTEXPR counting_predicate(int* counter, bool r) : times_applied(counter), result(r) {}
+  TEST_CONSTEXPR_CXX14 bool operator()(int) const {
+    ++*times_applied;
+    return result;
+  }
 };
 
-#if TEST_STD_VER > 17
-TEST_CONSTEXPR bool test_constexpr() {
-    int ia[] = {1, 3, 6, 7};
-    int ib[] = {1, 3, 5, 7};
-    return !std::none_of(std::begin(ia), std::end(ia), test1())
-        &&  std::none_of(std::begin(ib), std::end(ib), test1())
-        ;
+struct Test {
+  template <class Iter>
+  TEST_CONSTEXPR_CXX20 void operator()() {
+    { // an empty range vacuously satisfies none_of
+      int a[] = {1, 3, 5, 7};
+      // The return type is always `bool`, regardless of the iterator category.
+      ASSERT_SAME_TYPE(bool, decltype(std::none_of(Iter(a), Iter(a), is_odd())));
+      assert(std::none_of(Iter(a), Iter(a), is_odd()));
     }
-#endif
 
-int main(int, char**)
-{
-    {
-        int ia[] = {2, 4, 6, 8};
-        const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-        assert(std::none_of(cpp17_input_iterator<const int*>(ia),
-                            cpp17_input_iterator<const int*>(ia + sa), test1()) == false);
-        assert(std::none_of(cpp17_input_iterator<const int*>(ia),
-                            cpp17_input_iterator<const int*>(ia), test1()) == true);
+    { // no element satisfies the predicate
+      int a[] = {2, 4, 6, 8};
+      assert(std::none_of(Iter(a), Iter(a + 4), is_odd()));
     }
-    {
-        const int ia[] = {2, 4, 5, 8};
-        const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-        assert(std::none_of(cpp17_input_iterator<const int*>(ia),
-                            cpp17_input_iterator<const int*>(ia + sa), test1()) == false);
-        assert(std::none_of(cpp17_input_iterator<const int*>(ia),
-                            cpp17_input_iterator<const int*>(ia), test1()) == true);
+
+    { // every element satisfies the predicate
+      int a[] = {1, 3, 5, 7};
+      assert(!std::none_of(Iter(a), Iter(a + 4), is_odd()));
     }
-    {
-        const int ia[] = {1, 3, 5, 7};
-        const unsigned sa = sizeof(ia)/sizeof(ia[0]);
-        assert(std::none_of(cpp17_input_iterator<const int*>(ia),
-                            cpp17_input_iterator<const int*>(ia + sa), test1()) == true);
-        assert(std::none_of(cpp17_input_iterator<const int*>(ia),
-                            cpp17_input_iterator<const int*>(ia), test1()) == true);
+
+    { // the only satisfying element is the first one
+      int a[] = {1, 2, 4, 6};
+      assert(!std::none_of(Iter(a), Iter(a + 4), is_odd()));
+    }
+
+    { // the only satisfying element is in the middle
+      int a[] = {2, 4, 5, 8};
+      assert(!std::none_of(Iter(a), Iter(a + 4), is_odd()));
     }
 
-#if TEST_STD_VER > 17
-    static_assert(test_constexpr());
+    { // the only satisfying element is the last one
+      int a[] = {2, 4, 6, 7};
+      assert(!std::none_of(Iter(a), Iter(a + 4), is_odd()));
+    }
+
+    { // a single-element range, satisfying and not satisfying the predicate
+      int satisfies     = 1;
+      int not_satisfies = 2;
+      assert(!std::none_of(Iter(&satisfies), Iter(&satisfies + 1), is_odd()));
+      assert(std::none_of(Iter(&not_satisfies), Iter(&not_satisfies + 1), is_odd()));
+    }
+  }
+};
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  types::for_each(types::cpp17_input_iterator_list<int*>(), Test());
+
+  { // the predicate is applied at most `last - first` times (complexity requirement)
+    int a[]     = {1, 2, 3, 4, 5};
+    int applied = 0;
+    assert(std::none_of(a, a + 5, counting_predicate(&applied, false)));
+    assert(applied == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 20
+  static_assert(test());
 #endif
 
   return 0;


        

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:38:12 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:38:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Bring std::{any, all,
 none}_of tests up to current standards (PR #209266)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209266@github.com>
Message-ID: <6a5e24c4.93108353.3d3467.ebdd@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/209266

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:39:56 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:39:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210360@github.com>
Message-ID: <6a5e252c.575cb700.204618.3be2@mx.google.com>


================
@@ -562,11 +562,10 @@ void locale::facet::__on_zero_shared() noexcept { delete this; }
 
 // locale::id
 
-constinit int32_t locale::id::__next_id = 0;
-
 long locale::id::__get() {
-  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&__next_id, 1); });
-  return __id_ - 1;
+  constinit static atomic<int32_t> next_id = 0;
----------------
ldionne wrote:

Eh, you're right. Let's still get an ack from @efriedma-quic, but this should indeed not cause any additional issues.

https://github.com/llvm/llvm-project/pull/210360

From libcxx-commits at lists.llvm.org  Mon Jul 20 06:40:07 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 06:40:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210360@github.com>
Message-ID: <6a5e2537.12f56480.27910f.c1b2@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM with ACK from @efriedma-quic 

https://github.com/llvm/llvm-project/pull/210360

From libcxx-commits at lists.llvm.org  Mon Jul 20 07:05:24 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Mon, 20 Jul 2026 07:05:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] P2502R2: `std::generator` (PR
	#201854)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201854@github.com>
Message-ID: <6a5e2b24.0c05e68e.3c2f5d.12ba@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/201854

>From 22f5fee287889d72cb171751fdc0993197c69b6b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 22:45:16 +0800
Subject: [PATCH 1/9] implement generator

---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/Status/Cxx23Issues.csv            |   2 +-
 libcxx/docs/Status/Cxx23Papers.csv            |   4 +-
 libcxx/docs/Status/Cxx2cIssues.csv            |   8 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/generator                      | 603 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   5 +
 libcxx/include/version                        |   2 +
 libcxx/modules/std.compat.cppm.in             |   3 -
 libcxx/modules/std.cppm.in                    |   4 +-
 libcxx/modules/std/generator.inc              |   7 +-
 .../coro.generator/nodiscard.verify.cpp       |  29 +
 .../test/libcxx/transitive_includes/cxx03.csv |  21 +
 .../test/libcxx/transitive_includes/cxx11.csv |  21 +
 .../test/libcxx/transitive_includes/cxx14.csv |  21 +
 .../test/libcxx/transitive_includes/cxx17.csv |  21 +
 .../test/libcxx/transitive_includes/cxx20.csv |  16 +
 .../test/libcxx/transitive_includes/cxx23.csv |   9 +
 .../test/libcxx/transitive_includes/cxx26.csv |   9 +
 .../generator.version.compile.pass.cpp        |  65 ++
 .../version.version.compile.pass.cpp          |  30 +
 .../ranges/coro.generator/allocator.pass.cpp  | 123 ++++
 .../coro.generator/generator.compile.pass.cpp |  48 ++
 .../ranges/coro.generator/generator.pass.cpp  |  56 ++
 .../ranges/coro.generator/recursive.pass.cpp  |  97 +++
 .../generate_feature_test_macro_components.py |   5 +
 libcxx/utils/libcxx/header_information.py     |   1 -
 27 files changed, 1198 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/include/generator
 create mode 100644 libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/generator.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/recursive.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 1b748e37293df..46d83644ff10f 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -344,6 +344,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_forward_like``                                 ``202207L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_generator``                                    ``202207L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_invoke_r``                                     ``202106L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ios_noreplace``                                ``202207L``
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0a2f0d59484de..b61edbd0a2975 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -217,7 +217,7 @@
 "`LWG3759 <https://wg21.link/LWG3759>`__","``ranges::rotate_copy`` should use ``std::move``","2022-11 (Kona)","|Complete|","15","`#105027 <https://github.com/llvm/llvm-project/issues/105027>`__",""
 "`LWG3760 <https://wg21.link/LWG3760>`__","``cartesian_product_view::iterator``'s ``parent_`` is never valid","2022-11 (Kona)","","","`#105028 <https://github.com/llvm/llvm-project/issues/105028>`__",""
 "`LWG3761 <https://wg21.link/LWG3761>`__","``cartesian_product_view::iterator::operator-`` should pass by reference","2022-11 (Kona)","","","`#105029 <https://github.com/llvm/llvm-project/issues/105029>`__",""
-"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","","","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
+"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","23","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
 "`LWG3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept","2022-11 (Kona)","|Complete|","17","`#105031 <https://github.com/llvm/llvm-project/issues/105031>`__",""
 "`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","","","`#105032 <https://github.com/llvm/llvm-project/issues/105032>`__",""
 "`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","","`#105033 <https://github.com/llvm/llvm-project/issues/105033>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index eb580ea891f5b..862461f900dbd 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -80,7 +80,7 @@
 "`P2474R2 <https://wg21.link/P2474R2>`__","``views::repeat``","2022-07 (Virtual)","|Complete|","17","`#105222 <https://github.com/llvm/llvm-project/issues/105222>`__",""
 "`P2494R2 <https://wg21.link/P2494R2>`__","Relaxing range adaptors to allow for move only types","2022-07 (Virtual)","|Complete|","17","`#105224 <https://github.com/llvm/llvm-project/issues/105224>`__",""
 "`P2499R0 <https://wg21.link/P2499R0>`__","``string_view`` range constructor should be ``explicit``","2022-07 (Virtual)","|Complete|","16","`#105225 <https://github.com/llvm/llvm-project/issues/105225>`__",""
-"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","","","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
+"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","23","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
 "`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","2022-07 (Virtual)","|Complete|","15","`#105227 <https://github.com/llvm/llvm-project/issues/105227>`__",""
 "`P2517R1 <https://wg21.link/P2517R1>`__","Add a conditional ``noexcept`` specification to ``std::apply``","2022-07 (Virtual)","|Complete|","3.9","`#105229 <https://github.com/llvm/llvm-project/issues/105229>`__",""
 "`P2520R0 <https://wg21.link/P2520R0>`__","``move_iterator`` should be a random access iterator","2022-07 (Virtual)","|Complete|","17","`#105230 <https://github.com/llvm/llvm-project/issues/105230>`__","Implemented as a DR in C++20"
@@ -115,7 +115,7 @@
 "`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20","`#105259 <https://github.com/llvm/llvm-project/issues/105259>`__",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","`#105260 <https://github.com/llvm/llvm-project/issues/105260>`__","The paper is implemented as a DR to C++20"
 "`P2652R2 <https://wg21.link/P2652R2>`__","Disallow User Specialization of ``allocator_traits``","2023-02 (Issaquah)","|Complete|","19","`#105262 <https://github.com/llvm/llvm-project/issues/105262>`__",""
-"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","","","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
+"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","23","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
 "`P2614R2 <https://wg21.link/P2614R2>`__","Deprecate ``numeric_limits::has_denorm``","2023-02 (Issaquah)","|Complete|","18","`#105264 <https://github.com/llvm/llvm-project/issues/105264>`__",""
 "`P2588R3 <https://wg21.link/P2588R3>`__","``barrier``’s phase completion guarantees","2023-02 (Issaquah)","","","`#105265 <https://github.com/llvm/llvm-project/issues/105265>`__",""
 "`P2763R1 <https://wg21.link/P2763R1>`__","``layout_stride`` static extents default constructor fix","2023-02 (Issaquah)","","","`#105266 <https://github.com/llvm/llvm-project/issues/105266>`__",""
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 6a836c0491bb6..3ec8ae821ed5d 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -4,7 +4,7 @@
 "`LWG3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","2023-06 (Varna)","|Nothing To Do|","","`#105270 <https://github.com/llvm/llvm-project/issues/105270>`__",""
 "`LWG3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","2023-06 (Varna)","|Complete|","19","`#105271 <https://github.com/llvm/llvm-project/issues/105271>`__",""
 "`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","","","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
-"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","","","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
+"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","23","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
 "`LWG3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 <https://github.com/llvm/llvm-project/issues/105275>`__",""
 "`LWG3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 <https://github.com/llvm/llvm-project/issues/105276>`__",""
 "`LWG3905 <https://wg21.link/LWG3905>`__","Type of ``std::fexcept_t``","2023-06 (Varna)","|Complete|","3.4","`#105277 <https://github.com/llvm/llvm-project/issues/105277>`__",""
@@ -79,8 +79,8 @@
 "`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","","`#118332 <https://github.com/llvm/llvm-project/issues/118332>`__",""
 "`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","","`#118335 <https://github.com/llvm/llvm-project/issues/118335>`__",""
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
-"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
-"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
+"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","23","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
+"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","|Complete|","23","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
 "`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
@@ -93,7 +93,7 @@
 "`LWG4088 <https://wg21.link/LWG4088>`__","``println`` ignores the locale imbued in ``std::ostream``","2024-11 (Wrocław)","|Complete|","18","`#118348 <https://github.com/llvm/llvm-project/issues/118348>`__",""
 "`LWG4112 <https://wg21.link/LWG4112>`__","``has-arrow`` should required ``operator->()`` to be ``const``-qualified","2024-11 (Wrocław)","","","`#118349 <https://github.com/llvm/llvm-project/issues/118349>`__",""
 "`LWG4113 <https://wg21.link/LWG4113>`__","Disallow ``has_unique_object_representations<Incomplete[]>``","2024-11 (Wrocław)","|Complete|","","`#118350 <https://github.com/llvm/llvm-project/issues/118350>`__",""
-"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","","","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
+"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","23","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
 "`LWG4124 <https://wg21.link/LWG4124>`__","Cannot format ``zoned_time`` with resolution coarser than ``seconds``","2024-11 (Wrocław)","","","`#118352 <https://github.com/llvm/llvm-project/issues/118352>`__",""
 "`LWG4126 <https://wg21.link/LWG4126>`__","Some feature-test macros for fully freestanding features are not yet marked freestanding","2024-11 (Wrocław)","","","`#118353 <https://github.com/llvm/llvm-project/issues/118353>`__",""
 "`LWG4134 <https://wg21.link/LWG4134>`__","Issue with Philox algorithm specification","2024-11 (Wrocław)","","","`#118354 <https://github.com/llvm/llvm-project/issues/118354>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..cd77d51924571 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1043,6 +1043,7 @@ set(files
   fstream
   functional
   future
+  generator
   initializer_list
   iomanip
   ios
diff --git a/libcxx/include/generator b/libcxx/include/generator
new file mode 100644
index 0000000000000..d3f942b089002
--- /dev/null
+++ b/libcxx/include/generator
@@ -0,0 +1,603 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_GENERATOR
+#define _LIBCPP_GENERATOR
+
+/*
+
+namespace std {
+  // [coro.generator.class], class template generator
+  template<class Ref, class Val = void, class Allocator = void>
+    class generator;
+
+  namespace pmr {
+    template<class Ref, class Val = void>
+      using generator = std::generator<Ref, Val, polymorphic_allocator<>>;
+  }
+}
+
+*/
+
+#include <__algorithm/max.h>
+#include <__assert>
+#include <__concepts/common_reference_with.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/same_as.h>
+#include <__config>
+#include <__coroutine/coroutine_handle.h>
+#include <__coroutine/coroutine_traits.h>
+#include <__coroutine/noop_coroutine_handle.h>
+#include <__coroutine/trivial_awaitables.h>
+#include <__exception/exception_ptr.h>
+#include <__iterator/default_sentinel.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_arg_t.h>
+#include <__memory/allocator_traits.h>
+#include <__memory/construct_at.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/elements_of.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/common_reference.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/exchange.h>
+#include <__utility/move.h>
+#include <__utility/swap.h>
+#include <cstdint>
+#include <new>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 23
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Ref, class _Val>
+using __gen_val = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
+
+template <class _Ref, class _Val>
+using __gen_ref = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
+
+template <class _Ref, class _Val>
+using __gen_yielded =
+    conditional_t<is_reference_v<__gen_ref<_Ref, _Val>>, __gen_ref<_Ref, _Val>, const __gen_ref<_Ref, _Val>&>;
+
+template <class, class>
+struct __gen_iter_provider {
+  class __iterator;
+};
+
+template <class, class, class>
+class generator;
+
+template <class _Yielded>
+class __gen_promise_base {
+private:
+  template <class, class, class>
+  friend class generator;
+
+  template <class, class>
+  friend struct __gen_iter_provider;
+
+  // Each promise object stores either a `__root_data` when associated with a root generator, or a `__recursive_data`
+  // when associated with a generator that is yielded recursively.
+  struct __root_data {
+    // The client code has access only to the iterator of the root generator. Thus, the root generator must store the
+    // yielded values of recursively-yielded generators, which will then be returned when the client code dereferences
+    // the iterator.
+    add_pointer_t<_Yielded> __value_ptr;
+    // The client code has access only to the iterator of the root generator. Thus, the root generator needs to identify
+    // which generator is currently active. This active generator will then be resumed when the client code increments
+    // the iterator.
+    coroutine_handle<__gen_promise_base> __active;
+  };
+
+  struct __recursive_data {
+    exception_ptr __exception;
+    coroutine_handle<__gen_promise_base> __parent;
+    coroutine_handle<__gen_promise_base> __root;
+  };
+
+  union __union {
+    __root_data __root;
+    __recursive_data __recursive;
+
+    _LIBCPP_HIDE_FROM_ABI ~__union() noexcept {}
+  } __data_;
+
+  // The `__tag_` stores the active member of the `__data_` union:
+  // - `false` indicates that the active member is `__root`.
+  // - `true` indicates that the active member is `__recursive`.
+  // This field can be omitted because `__recursive_data.__root` can store the active member.
+  bool __tag_;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_root() noexcept { return !__tag_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __root_data& __get_root_data() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __data_.__root;
+  }
+  _LIBCPP_HIDE_FROM_ABI void __set_root_tag() noexcept { __tag_ = false; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_recursive() noexcept { return __tag_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __recursive_data& __get_recursive_data() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_recursive(), "the active member of `__data_` is not `__recursive`");
+    return __data_.__recursive;
+  }
+  _LIBCPP_HIDE_FROM_ABI void __set_recursive_tag() noexcept { __tag_ = true; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI coroutine_handle<__gen_promise_base>& __active() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __get_root_data().__active;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __value_ptr() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __get_root_data().__value_ptr;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __root_value_ptr() noexcept {
+    if (__is_root()) {
+      return __value_ptr();
+    }
+    return __get_recursive_data().__root.promise().__value_ptr();
+  }
+
+  struct __element_awaiter {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI void await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      __current.promise().__root_value_ptr() = std::addressof(__value);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+
+    remove_cvref_t<_Yielded> __value;
+  };
+
+  struct __final_awaiter {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI coroutine_handle<> await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      // Checks if the current generator is recursively-yielded
+      if (__current.promise().__is_recursive()) {
+        auto&& __recursive_data = __current.promise().__get_recursive_data();
+        auto __parent           = __recursive_data.__parent;
+        // Updates the active generator to its parent, allowing the client code to resume it later
+        __recursive_data.__root.promise().__active() = __parent;
+        // Transfers execution to its parent, which is the generator that yields it
+        return __parent;
+      }
+      return std::noop_coroutine();
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+  };
+
+  template <class _Ref, class _Val, class _Allocator>
+  struct __recursive_awaiter {
+    _LIBCPP_HIDE_FROM_ABI explicit __recursive_awaiter(generator<_Ref, _Val, _Allocator>&& __gen) noexcept
+        : __gen_{std::move(__gen)} {}
+
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return !__gen_.__coroutine_; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI coroutine_handle<__gen_promise_base>
+    await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      // Stores a `__recursive_data` in the promise object associated with `__gen`
+      auto __recursive = coroutine_handle<__gen_promise_base>::from_address(__gen_.__coroutine_.address());
+      __recursive.promise().__set_recursive_tag();
+      auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+
+      // Sets `__recursive_data.__parent` to the current generator
+      auto __parent             = coroutine_handle<__gen_promise_base>::from_address(__current.address());
+      __recursive_data.__parent = __parent;
+
+      // Sets `__recursive_data.__root` to the current generator if it's a root generator, or to the root generator of
+      // the current generator otherwise
+      if (__parent.promise().__is_recursive()) {
+        __recursive_data.__root = __parent.promise().__get_recursive_data().__root;
+      } else {
+        __recursive_data.__root = __parent;
+      }
+
+      // Updates the active generator to `__gen`, allowing the client code to resume it later
+      __recursive_data.__root.promise().__active() = __recursive;
+
+      // Transfers execution to `__gen`
+      return __recursive;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() {
+      auto __recursive        = coroutine_handle<__gen_promise_base>::from_address(__gen_.__coroutine_.address());
+      auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+      if (__recursive_data.__exception) {
+        std::rethrow_exception(std::move(__recursive_data.__exception));
+      }
+    }
+
+  private:
+    generator<_Ref, _Val, _Allocator> __gen_;
+  };
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __gen_promise_base() noexcept
+      : __data_{.__root{
+            .__value_ptr{nullptr},
+            .__active{coroutine_handle<__gen_promise_base>::from_promise(*this)},
+        }},
+        __tag_{false} {}
+
+  _LIBCPP_HIDE_FROM_ABI ~__gen_promise_base() noexcept {
+    if (__is_root()) {
+      __data_.__root.~__root_data();
+    } else {
+      __data_.__recursive.~__recursive_data();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI suspend_always initial_suspend() const noexcept { return {}; }
+
+  _LIBCPP_HIDE_FROM_ABI __final_awaiter final_suspend() noexcept { return {}; }
+
+  _LIBCPP_HIDE_FROM_ABI suspend_always yield_value(_Yielded __value) noexcept {
+    __root_value_ptr() = std::addressof(__value);
+    return {};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI auto yield_value(const remove_reference_t<_Yielded>& __value) noexcept(
+      is_nothrow_constructible_v<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>)
+    requires is_rvalue_reference_v<_Yielded> &&
+             constructible_from<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>
+  {
+    return __element_awaiter{.__value{__value}};
+  }
+
+  template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
+    requires same_as<__gen_yielded<_Ref2, _Val2>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto
+  yield_value(ranges::elements_of<generator<_Ref2, _Val2, _Allocator2>&&, _Unused> __elements) noexcept {
+    return __recursive_awaiter<_Ref2, _Val2, _Allocator2>{std::move(__elements.range)};
+  }
+
+  template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
+    requires same_as<__gen_yielded<_Ref2, _Val2>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto
+  yield_value(ranges::elements_of<generator<_Ref2, _Val2, _Allocator2>&, _Unused> __elements) noexcept {
+    return __recursive_awaiter<_Ref2, _Val2, _Allocator2>{std::move(__elements.range)};
+  }
+
+  template <ranges::input_range _Range, class _Allocator>
+    requires convertible_to<ranges::range_reference_t<_Range>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto yield_value(ranges::elements_of<_Range, _Allocator> __range) {
+    auto __lambda =
+        [](allocator_arg_t, _Allocator, ranges::iterator_t<_Range> __i, ranges::sentinel_t<_Range> __s) static
+        -> generator<_Yielded, void, _Allocator> {
+      for (; __i != __s; ++__i) {
+        co_yield static_cast<_Yielded>(*__i);
+      }
+    };
+    return yield_value(ranges::elements_of(
+        __lambda(allocator_arg, __range.allocator, ranges::begin(__range.range), ranges::end(__range.range))));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void await_transform() = delete;
+
+  _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
+
+  _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
+#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+    if (__is_root()) {
+      throw;
+    } else {
+      __get_recursive_data().__exception = std::current_exception();
+    }
+#  endif
+  }
+};
+
+struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) __gen_aligned_block {
+  char __data[__STDCPP_DEFAULT_NEW_ALIGNMENT__];
+
+  _LIBCPP_HIDE_FROM_ABI static size_t __count(const size_t __size) noexcept {
+    return (__size + sizeof(__gen_aligned_block) - 1) / sizeof(__gen_aligned_block);
+  }
+};
+
+template <class _Allocator>
+concept __gen_stateless_allocator =
+    default_initializable<_Allocator> && allocator_traits<_Allocator>::is_always_equal::value;
+
+template <class _Allocator>
+class __gen_promise_allocator {
+private:
+  using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+  using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+  using __rebind_pointer = allocator_traits<__rebind>::pointer;
+  static_assert(is_pointer_v<__rebind_pointer>);
+
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+    __rebind __rebind_al = static_cast<__rebind>(__al);
+
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      return __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, and the alignment of the
+      // stateful allocator
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+      void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+      return __ptr;
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size)
+    requires default_initializable<_Allocator>
+  {
+    return __alloc({}, __size);
+  }
+
+  template <class _Allocator2, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<_Allocator>(__al), __size);
+  }
+
+  template <class _This, class _Allocator2, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<_Allocator>(__al), __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      __rebind __rebind_al;
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      __rebind __rebind_al = std::move(*__rebind_al_ptr);
+      std::destroy_at(__rebind_al_ptr);
+
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+};
+
+template <>
+class __gen_promise_allocator<void> {
+private:
+  using __dealloc_fn = void (*)(void*, size_t) noexcept;
+
+  template <class _Allocator>
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+    using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+    using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+    using __rebind_pointer = allocator_traits<__rebind>::pointer;
+    static_assert(is_pointer_v<__rebind_pointer>);
+
+    __rebind __rebind_al = static_cast<__rebind>(__al);
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+      void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the deallocation function in the allocated block
+      __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr                    = __dealloc<__rebind>;
+      return __ptr;
+    } else {
+      // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, the alignment of the
+      // stateful allocator, and the deallocation function
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(__rebind) + alignof(__rebind));
+      void* const __ptr = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the deallocation function in the allocated block
+      __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr                    = __dealloc<__rebind>;
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + sizeof(__dealloc_fn) + __size + alignof(__rebind) - 1) &
+          ~(alignof(__rebind) - 1));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+      return __ptr;
+    }
+  }
+
+  template <class _Rebind>
+  _LIBCPP_HIDE_FROM_ABI static void __dealloc(void* const __ptr, size_t __size) noexcept {
+    using __rebind_size_type = allocator_traits<_Rebind>::size_type;
+
+    if constexpr (__gen_stateless_allocator<_Rebind>) {
+      _Rebind __rebind_al;
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      _Rebind* const __rebind_al_ptr = reinterpret_cast<_Rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + sizeof(__dealloc_fn) + alignof(_Rebind) - 1) &
+          ~(alignof(_Rebind) - 1));
+      _Rebind __rebind_al = std::move(*__rebind_al_ptr);
+      std::destroy_at(__rebind_al_ptr);
+
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(_Rebind) + alignof(_Rebind));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size) {
+    void* const __ptr      = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
+      ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+    };
+
+    __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    *__dealloc_ptr                    = __dealloc;
+    return __ptr;
+  }
+
+  template <class _Allocator, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+    return __alloc(__al, __size);
+  }
+
+  template <class _This, class _Allocator, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+    return __alloc(__al, __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
+    __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    const __dealloc_fn __dealloc      = *__dealloc_ptr;
+    __dealloc(__ptr, __size);
+  }
+};
+
+template <class _Ref, class _Val>
+class __gen_iter_provider<_Ref, _Val>::__iterator {
+private:
+  using __val = __gen_val<_Ref, _Val>;
+  using __ref = __gen_ref<_Ref, _Val>;
+
+public:
+  using value_type      = __val;
+  using difference_type = ptrdiff_t;
+
+  _LIBCPP_HIDE_FROM_ABI explicit __iterator(
+      coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine) noexcept
+      : __coroutine_{__coroutine} {}
+
+  _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&& __other) noexcept
+      : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator=(__iterator&& __other) noexcept {
+    __coroutine_ = std::exchange(__other.__coroutine_, {});
+    return *this;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __ref operator*() const noexcept(is_nothrow_copy_constructible_v<__ref>) {
+    return static_cast<__ref>(*__coroutine_.promise().__value_ptr());
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator++() {
+    __coroutine_.promise().__active().resume();
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend bool operator==(const __iterator& __iter, default_sentinel_t) noexcept {
+    return __iter.__coroutine_.done();
+  }
+
+private:
+  coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine_;
+};
+
+template <class _Ref, class _Val = void, class _Allocator = void>
+class generator : public ranges::view_interface<generator<_Ref, _Val, _Allocator>> {
+private:
+  using __val = __gen_val<_Ref, _Val>;
+  static_assert(same_as<remove_cvref_t<__val>, __val> && is_object_v<__val>);
+
+  using __ref = __gen_ref<_Ref, _Val>;
+  static_assert(is_reference_v<__ref> ||
+                (same_as<remove_cvref_t<__ref>, __ref> && is_object_v<__ref> && copy_constructible<__ref>));
+
+  using __rref = conditional_t<is_lvalue_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
+  static_assert(common_reference_with<__ref&&, __val&> && common_reference_with<__ref&&, __rref&&> &&
+                common_reference_with<__rref&&, const __val&>);
+
+  template <class>
+  friend class __gen_promise_base;
+
+public:
+  using yielded = __gen_yielded<_Ref, _Val>;
+
+  struct promise_type : __gen_promise_base<yielded>, __gen_promise_allocator<_Allocator> {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI generator get_return_object() noexcept {
+      return generator{coroutine_handle<promise_type>::from_promise(*this)};
+    }
+  };
+
+  _LIBCPP_HIDE_FROM_ABI generator(const generator&) = delete;
+  _LIBCPP_HIDE_FROM_ABI generator(generator&& __other) noexcept
+      : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+  _LIBCPP_HIDE_FROM_ABI ~generator() {
+    if (__coroutine_) {
+      __coroutine_.destroy();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI generator& operator=(generator __other) noexcept {
+    std::swap(__coroutine_, __other.__coroutine_);
+    return *this;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __gen_iter_provider<_Ref, _Val>::__iterator begin() {
+    auto __coroutine = coroutine_handle<__gen_promise_base<yielded>>::from_promise(__coroutine_.promise());
+    __coroutine.resume();
+    return typename __gen_iter_provider<_Ref, _Val>::__iterator{__coroutine};
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI default_sentinel_t end() const noexcept { return {}; }
+
+private:
+  _LIBCPP_HIDE_FROM_ABI explicit generator(coroutine_handle<promise_type> __coroutine) noexcept
+      : __coroutine_{__coroutine} {}
+
+  coroutine_handle<promise_type> __coroutine_;
+};
+
+namespace pmr {
+
+template <class _Ref, class _Val = void>
+using generator = std::generator<_Ref, _Val, polymorphic_allocator<>>;
+
+} // namespace pmr
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_GENERATOR
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..efb97a047c6e6 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1476,6 +1476,11 @@ module std [system] {
     export *
   }
 
+  module generator {
+    header "generator"
+    export *
+  }
+
   module iomanip {
     header "iomanip"
     export *
diff --git a/libcxx/include/version b/libcxx/include/version
index d4d29b4d31a72..8a73c95551793 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -133,6 +133,7 @@ __cpp_lib_fstream_native_handle                         202306L <fstream>
 __cpp_lib_function_ref                                  202306L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
+__cpp_lib_generator                                     202207L <generator>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
 __cpp_lib_generic_unordered_lookup                      201811L <unordered_map> <unordered_set>
 __cpp_lib_hardware_interference_size                    201703L <new>
@@ -507,6 +508,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_format_ranges                        202207L
 // # define __cpp_lib_formatters                           202302L
 # define __cpp_lib_forward_like                         202207L
+# define __cpp_lib_generator                            202207L
 # define __cpp_lib_invoke_r                             202106L
 # define __cpp_lib_ios_noreplace                        202207L
 # if __has_builtin(__builtin_is_implicit_lifetime)
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index dd7385bf33a42..dada8f56f0224 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -51,9 +51,6 @@ module;
 #  if __has_include(<debugging>)
 #    error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<debugging>)
-#  if __has_include(<generator>)
-#    error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<generator>)
 #  if __has_include(<hazard_pointer>)
 #    error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<hazard_pointer>)
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index 984b18321923c..0c484219c31ce 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -67,6 +67,7 @@ module;
 #include <fstream>
 #include <functional>
 #include <future>
+#include <generator>
 #include <initializer_list>
 #include <iomanip>
 #include <ios>
@@ -135,9 +136,6 @@ module;
 #  if __has_include(<debugging>)
 #    error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<debugging>)
-#  if __has_include(<generator>)
-#    error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<generator>)
 #  if __has_include(<hazard_pointer>)
 #    error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<hazard_pointer>)
diff --git a/libcxx/modules/std/generator.inc b/libcxx/modules/std/generator.inc
index 43fb0daf5c659..8681f4b26dc46 100644
--- a/libcxx/modules/std/generator.inc
+++ b/libcxx/modules/std/generator.inc
@@ -8,7 +8,12 @@
 //===----------------------------------------------------------------------===//
 
 export namespace std {
-#if 0
+#if _LIBCPP_STD_VER >= 23
+  // [coro.generator], class template generator
   using std::generator;
+
+  namespace pmr {
+    using std::pmr::generator;
+  }
 #endif
 } // namespace std
diff --git a/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..9fb717bf9c148
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <generator>
+
+std::generator<int> generate_ints() {
+  co_yield 1;
+  co_yield 2;
+  co_yield 3;
+}
+
+void test() {
+  auto gen = generate_ints();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  gen.begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  gen.end();
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c0031543e47bc..5754f5961dd09 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -945,6 +949,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2032,6 +2052,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c0031543e47bc..5754f5961dd09 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -945,6 +949,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2032,6 +2052,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c2eb5b44e8d7a..c31b86c5f8739 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,15 +125,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -968,6 +972,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2072,6 +2092,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 332cb62f35b5f..50f2d9cf2ac5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -971,6 +975,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2085,6 +2105,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 55c79acff5a8f..0d23c92ec9a1d 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -965,6 +965,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 073f698786117..2fde541964065 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -466,6 +466,15 @@ future string_view
 future tuple
 future typeinfo
 future version
+generator compare
+generator cstdint
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator tuple
+generator typeinfo
+generator version
 initializer_list version
 iomanip cctype
 iomanip cerrno
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index d531d60ae3e35..5fde2907b3327 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -443,6 +443,15 @@ future string_view
 future tuple
 future typeinfo
 future version
+generator compare
+generator cstdint
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator tuple
+generator typeinfo
+generator version
 initializer_list version
 iomanip cctype
 iomanip cerrno
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
new file mode 100644
index 0000000000000..0726f4e59a553
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <generator>
+
+// Test the feature test macros defined by <generator>
+
+// clang-format off
+
+#include <generator>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++23"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++23"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++26"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++26"
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
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 20c16700ef76a..32befec34ed09 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
@@ -412,6 +412,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should not be defined before c++14"
 #  endif
@@ -1351,6 +1355,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++14"
 #  endif
@@ -2413,6 +2421,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++17"
 #  endif
@@ -3742,6 +3754,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++20"
 #  endif
@@ -5266,6 +5282,13 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++23"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++23"
 #  endif
@@ -7144,6 +7167,13 @@
 #    endif
 #  endif
 
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++26"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp b/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
new file mode 100644
index 0000000000000..96c2211ed3138
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <cstddef>
+#include <memory>
+#include <memory_resource>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+template <class T>
+class stateless_allocator {
+public:
+  using value_type = T;
+
+  stateless_allocator() noexcept = default;
+
+  template <typename U>
+  constexpr stateless_allocator(const stateless_allocator<U>&) noexcept {}
+
+  [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
+
+  void deallocate(void* p, size_t) noexcept { ::operator delete(p); }
+
+  template <class U>
+  constexpr bool operator==(const stateless_allocator<U>&) const noexcept {
+    return true;
+  }
+};
+
+template <class T>
+class stateful_allocator {
+public:
+  using value_type = T;
+
+  stateful_allocator() noexcept = default;
+
+  template <typename U>
+  constexpr stateful_allocator(const stateful_allocator<U>&) noexcept {}
+
+  [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
+
+  void deallocate(void* p, size_t) noexcept { ::operator delete(p); }
+
+  template <class U>
+  constexpr bool operator==(const stateful_allocator<U>& other) const noexcept {
+    return state_ == other.state_;
+  }
+
+private:
+  int state_ = 0;
+};
+
+template <class Allocator>
+bool test_with_allocator() {
+  std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+  {
+    auto fib = []() -> std::generator<int, int, Allocator> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib() | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+
+  {
+    auto fib = [](std::allocator_arg_t, Allocator) -> std::generator<int, int, Allocator> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib(std::allocator_arg, {}) | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+
+  {
+    auto fib = [](std::allocator_arg_t, Allocator) -> std::generator<int> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib(std::allocator_arg, {}) | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+  return true;
+}
+
+bool test() {
+  test_with_allocator<std::allocator<std::byte>>();
+  test_with_allocator<stateless_allocator<std::byte>>();
+  test_with_allocator<stateful_allocator<std::byte>>();
+  test_with_allocator<std::pmr::polymorphic_allocator<std::byte>>();
+
+  test_with_allocator<std::allocator<float>>();
+  test_with_allocator<stateless_allocator<float>>();
+  test_with_allocator<stateful_allocator<float>>();
+  test_with_allocator<std::pmr::polymorphic_allocator<float>>();
+  return true;
+};
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
new file mode 100644
index 0000000000000..769a56021c32b
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+template <class G, class V, class R, class RR>
+constexpr bool conformance() {
+  static_assert(std::ranges::range<G>);
+  static_assert(std::ranges::view<G>);
+  static_assert(std::ranges::input_range<G>);
+  static_assert(!std::ranges::forward_range<G>);
+  static_assert(!std::ranges::borrowed_range<G>);
+
+  static_assert(std::same_as<std::ranges::range_value_t<G>, V>);
+  static_assert(std::same_as<std::ranges::range_reference_t<G>, R>);
+  static_assert(std::same_as<std::ranges::range_rvalue_reference_t<G>, RR>);
+
+  return true;
+}
+
+static_assert(conformance<std::generator<int>, int, int&&, int&&>());
+static_assert(conformance<std::generator<int, int>, int, int, int>());
+
+static_assert(conformance<std::generator<int&>, int, int&, int&&>());
+static_assert(conformance<std::generator<int&, int>, int, int&, int&&>());
+static_assert(conformance<std::generator<const int&>, int, const int&, const int&&>());
+static_assert(conformance<std::generator<const int&, int>, int, const int&, const int&&>());
+
+static_assert(conformance<std::generator<int&&>, int, int&&, int&&>());
+static_assert(conformance<std::generator<int&&, int>, int, int&&, int&&>());
+static_assert(conformance<std::generator<const int&&>, int, const int&&, const int&&>());
+static_assert(conformance<std::generator<const int&&, int>, int, const int&&, const int&&>());
diff --git a/libcxx/test/std/ranges/coro.generator/generator.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
new file mode 100644
index 0000000000000..ed1793382962a
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+std::generator<int> fib() {
+  int a = 0;
+  int b = 1;
+  while (true) {
+    co_yield std::exchange(a, std::exchange(b, a + b));
+  }
+}
+
+std::generator<const int&> range_fib() {
+  co_yield std::ranges::elements_of(std::vector<int>{0, 1});
+  co_yield std::ranges::elements_of(std::vector<int>{1, 2});
+  co_yield std::ranges::elements_of(std::vector<int>{3, 5});
+  co_yield std::ranges::elements_of(std::vector<int>{5, 8});
+}
+
+bool test() {
+  {
+    std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+    {
+      auto fib_vec = fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+    {
+      auto fib_vec = range_fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+  }
+  return true;
+}
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
new file mode 100644
index 0000000000000..36c5b8b65e42e
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <numeric>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+std::generator<int> fib() {
+  int a = 0;
+  int b = 1;
+  while (true) {
+    co_yield std::exchange(a, std::exchange(b, a + b));
+  }
+}
+
+std::generator<int> recursive_fib(int depth) {
+  if (depth == 0) {
+    co_yield std::ranges::elements_of(fib());
+  } else {
+    co_yield std::ranges::elements_of(recursive_fib(depth - 1));
+  }
+};
+
+struct tree_node {
+  tree_node* left;
+  tree_node* right;
+  int element;
+
+  ~tree_node() {
+    delete left;
+    delete right;
+  }
+};
+
+tree_node* build_tree(int depth) {
+  if (depth == 0) {
+    return nullptr;
+  }
+
+  tree_node* root = new tree_node();
+  root->element   = depth;
+  root->left      = build_tree(depth - 1);
+  root->right     = build_tree(depth - 1);
+  return root;
+}
+
+std::generator<int> traversal(tree_node* node) {
+  if (node == nullptr) {
+    co_return;
+  }
+  co_yield std::ranges::elements_of(traversal(node->left));
+  co_yield node->element;
+  co_yield std::ranges::elements_of(traversal(node->right));
+}
+
+bool test() {
+  {
+    std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+    {
+      auto fib_vec = recursive_fib(1) | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+    {
+      auto fib_vec = recursive_fib(42) | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+  }
+  {
+    tree_node* tree_root = build_tree(10);
+    auto node_vec        = traversal(tree_root) | std::ranges::to<std::vector<int>>();
+    assert(node_vec.size() == 1023);
+    assert(std::reduce(node_vec.cbegin(), node_vec.cend()) == 2036);
+    delete tree_root;
+  }
+  return true;
+}
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d8fb575b97b2f..a5db6f1f1650f 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -719,6 +719,11 @@ def add_version_header(tc):
             "headers": ["random"],
             "unimplemented": True,
         },
+        {
+            "name": "__cpp_lib_generator",
+            "values": {"c++23": 202207}, # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
+            "headers": ["generator"],
+        },
         {
             "name": "__cpp_lib_generic_associative_lookup",
             "values": {"c++14": 201304},
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index d06271a7908cc..cc33d32a69201 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -164,7 +164,6 @@ def __hash__(self) -> int:
 # modules will fail to build if a header is added but this list is not updated.
 headers_not_available = list(map(Header, [
     "debugging",
-    "generator",
     "hazard_pointer",
     "inplace_vector",
     "linalg",

>From f2ee26c784c91829799073fe12d1cbd0d281a755 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 22:52:35 +0800
Subject: [PATCH 2/9] release notes & python format

---
 libcxx/docs/ReleaseNotes/23.rst                        | 2 ++
 libcxx/utils/generate_feature_test_macro_components.py | 4 +++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 03dabc54dcf79..a53fad86004a6 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -51,6 +51,8 @@ Implemented Papers
 - P2542R8: ``views::concat`` (`Github <https://llvm.org/PR105419>`__)
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
+- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index a5db6f1f1650f..ea2e43b4c15a8 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -721,7 +721,9 @@ def add_version_header(tc):
         },
         {
             "name": "__cpp_lib_generator",
-            "values": {"c++23": 202207}, # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
+            "values": {
+                "c++23": 202207
+            },  # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
             "headers": ["generator"],
         },
         {

>From 19e5608ff21db2369e7172201d55bc372e48d67b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:27:41 +0800
Subject: [PATCH 3/9] test

---
 libcxx/include/generator | 109 +++++++++++++++++++++------------------
 1 file changed, 60 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index d3f942b089002..7792b65a6ae43 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -25,52 +25,56 @@ namespace std {
 
 */
 
-#include <__algorithm/max.h>
-#include <__assert>
-#include <__concepts/common_reference_with.h>
-#include <__concepts/constructible.h>
-#include <__concepts/convertible_to.h>
-#include <__concepts/same_as.h>
-#include <__config>
-#include <__coroutine/coroutine_handle.h>
-#include <__coroutine/coroutine_traits.h>
-#include <__coroutine/noop_coroutine_handle.h>
-#include <__coroutine/trivial_awaitables.h>
-#include <__exception/exception_ptr.h>
-#include <__iterator/default_sentinel.h>
-#include <__memory/addressof.h>
-#include <__memory/allocator_arg_t.h>
-#include <__memory/allocator_traits.h>
-#include <__memory/construct_at.h>
-#include <__memory_resource/polymorphic_allocator.h>
-#include <__ranges/access.h>
-#include <__ranges/concepts.h>
-#include <__ranges/elements_of.h>
-#include <__ranges/view_interface.h>
-#include <__type_traits/add_pointer.h>
-#include <__type_traits/common_reference.h>
-#include <__type_traits/conditional.h>
-#include <__type_traits/is_nothrow_constructible.h>
-#include <__type_traits/is_object.h>
-#include <__type_traits/is_pointer.h>
-#include <__type_traits/is_reference.h>
-#include <__type_traits/is_void.h>
-#include <__type_traits/remove_cvref.h>
-#include <__type_traits/remove_reference.h>
-#include <__utility/exchange.h>
-#include <__utility/move.h>
-#include <__utility/swap.h>
-#include <cstdint>
-#include <new>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+#  include <__cxx03/__config>
+#else
+#  include <__config>
+
+#  if _LIBCPP_STD_VER >= 23
+
+#    include <__algorithm/max.h>
+#    include <__assert>
+#    include <__concepts/common_reference_with.h>
+#    include <__concepts/constructible.h>
+#    include <__concepts/convertible_to.h>
+#    include <__concepts/same_as.h>
+#    include <__coroutine/coroutine_handle.h>
+#    include <__coroutine/coroutine_traits.h>
+#    include <__coroutine/noop_coroutine_handle.h>
+#    include <__coroutine/trivial_awaitables.h>
+#    include <__exception/exception_ptr.h>
+#    include <__iterator/default_sentinel.h>
+#    include <__memory/addressof.h>
+#    include <__memory/allocator_arg_t.h>
+#    include <__memory/allocator_traits.h>
+#    include <__memory/construct_at.h>
+#    include <__memory_resource/polymorphic_allocator.h>
+#    include <__ranges/access.h>
+#    include <__ranges/concepts.h>
+#    include <__ranges/elements_of.h>
+#    include <__ranges/view_interface.h>
+#    include <__type_traits/add_pointer.h>
+#    include <__type_traits/common_reference.h>
+#    include <__type_traits/conditional.h>
+#    include <__type_traits/is_nothrow_constructible.h>
+#    include <__type_traits/is_object.h>
+#    include <__type_traits/is_pointer.h>
+#    include <__type_traits/is_reference.h>
+#    include <__type_traits/is_void.h>
+#    include <__type_traits/remove_cvref.h>
+#    include <__type_traits/remove_reference.h>
+#    include <__utility/exchange.h>
+#    include <__utility/move.h>
+#    include <__utility/swap.h>
+#    include <cstdint>
+#    include <new>
+
+#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#      pragma GCC system_header
+#    endif
 
 _LIBCPP_PUSH_MACROS
-#include <__undef_macros>
-
-#if _LIBCPP_STD_VER >= 23
+#    include <__undef_macros>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -309,13 +313,13 @@ public:
   _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
 
   _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
-#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+#    ifndef _LIBCPP_HAS_NO_EXCEPTIONS
     if (__is_root()) {
       throw;
     } else {
       __get_recursive_data().__exception = std::current_exception();
     }
-#  endif
+#    endif
   }
 };
 
@@ -462,9 +466,14 @@ private:
 
 public:
   _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size) {
-    void* const __ptr      = ::operator new(__size + sizeof(__dealloc_fn));
-    __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
+    void* const __ptr = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc =
+        [](void* const __dealloc_ptr, [[maybe_unused]] const size_t __dealloc_size) static noexcept -> void {
+#    ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
+      ::operator delete(__dealloc_ptr);
+#    else
       ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+#    endif
     };
 
     __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
@@ -596,8 +605,10 @@ using generator = std::generator<_Ref, _Val, polymorphic_allocator<>>;
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_STD_VER >= 23
+#  endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_POP_MACROS
 
+#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
 #endif // _LIBCPP_GENERATOR

>From 57bf0686971419520f56a48a34647f302a151cec Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:48:45 +0800
Subject: [PATCH 4/9] test

---
 libcxx/include/generator | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index 7792b65a6ae43..2176ecbaa1e6e 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -30,6 +30,13 @@ namespace std {
 #else
 #  include <__config>
 
+#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#    pragma GCC system_header
+#  endif
+
+_LIBCPP_PUSH_MACROS
+#  include <__undef_macros>
+
 #  if _LIBCPP_STD_VER >= 23
 
 #    include <__algorithm/max.h>
@@ -69,13 +76,6 @@ namespace std {
 #    include <cstdint>
 #    include <new>
 
-#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#      pragma GCC system_header
-#    endif
-
-_LIBCPP_PUSH_MACROS
-#    include <__undef_macros>
-
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Ref, class _Val>
@@ -469,10 +469,10 @@ public:
     void* const __ptr = ::operator new(__size + sizeof(__dealloc_fn));
     __dealloc_fn __dealloc =
         [](void* const __dealloc_ptr, [[maybe_unused]] const size_t __dealloc_size) static noexcept -> void {
-#    ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
-      ::operator delete(__dealloc_ptr);
-#    else
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
       ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+#    else
+      ::operator delete(__dealloc_ptr);
 #    endif
     };
 

>From d4c2ae0fccee3faeb4ed931a82cb11edc4505e2a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:57:26 +0800
Subject: [PATCH 5/9] test

---
 libcxx/test/libcxx/transitive_includes/cxx03.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx11.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx14.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx17.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx20.csv | 16 ----------------
 5 files changed, 80 deletions(-)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 5754f5961dd09..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -949,22 +949,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 5754f5961dd09..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -949,22 +949,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c31b86c5f8739..cfcf7bc7c4c19 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -972,22 +972,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 50f2d9cf2ac5f..534776e6f0526 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -975,22 +975,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 0d23c92ec9a1d..55c79acff5a8f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -965,22 +965,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm

>From 0b062c588aa75fa002ffe2d020da342947f80caf Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 8 Jun 2026 23:21:56 +0800
Subject: [PATCH 6/9] revert some transitive include change

---
 libcxx/test/libcxx/transitive_includes/cxx03.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx11.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx14.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx17.csv | 5 -----
 4 files changed, 20 deletions(-)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index cfcf7bc7c4c19..c2eb5b44e8d7a 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,19 +125,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2076,7 +2072,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 534776e6f0526..332cb62f35b5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2089,7 +2085,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm

>From 90c7db75736b52393c458af97b92a6dc4617ca7f Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 20:18:04 +0800
Subject: [PATCH 7/9] release notes 23 -> 24

---
 libcxx/docs/ReleaseNotes/23.rst | 5 -----
 libcxx/docs/ReleaseNotes/24.rst | 2 ++
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e18f46f6e467d..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -54,12 +54,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
-<<<<<<< HEAD
-- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
-- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
-=======
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
->>>>>>> main
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..55b39c7530f6a 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,8 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
+- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
 
 Improvements and New Features
 -----------------------------

>From 8fc0b82aeb2c202c5710c5e21a519d3ddf9003c4 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 20:22:23 +0800
Subject: [PATCH 8/9] release notes 23 -> 24

---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 libcxx/docs/Status/Cxx23Papers.csv | 4 ++--
 libcxx/docs/Status/Cxx26Issues.csv | 8 ++++----
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index f73551117f84e..167214284e2fd 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -217,7 +217,7 @@
 "`LWG3759 <https://wg21.link/LWG3759>`__","``ranges::rotate_copy`` should use ``std::move``","2022-11 (Kona)","|Complete|","15","`#105027 <https://github.com/llvm/llvm-project/issues/105027>`__",""
 "`LWG3760 <https://wg21.link/LWG3760>`__","``cartesian_product_view::iterator``'s ``parent_`` is never valid","2022-11 (Kona)","","","`#105028 <https://github.com/llvm/llvm-project/issues/105028>`__",""
 "`LWG3761 <https://wg21.link/LWG3761>`__","``cartesian_product_view::iterator::operator-`` should pass by reference","2022-11 (Kona)","","","`#105029 <https://github.com/llvm/llvm-project/issues/105029>`__",""
-"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","23","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
+"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","24","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
 "`LWG3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept","2022-11 (Kona)","|Complete|","17","`#105031 <https://github.com/llvm/llvm-project/issues/105031>`__",""
 "`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","","","`#105032 <https://github.com/llvm/llvm-project/issues/105032>`__",""
 "`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","","`#105033 <https://github.com/llvm/llvm-project/issues/105033>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 86f1395ed12fb..b5494c4a20ea8 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -80,7 +80,7 @@
 "`P2474R2 <https://wg21.link/P2474R2>`__","``views::repeat``","2022-07 (Virtual)","|Complete|","17","`#105222 <https://github.com/llvm/llvm-project/issues/105222>`__",""
 "`P2494R2 <https://wg21.link/P2494R2>`__","Relaxing range adaptors to allow for move only types","2022-07 (Virtual)","|Complete|","17","`#105224 <https://github.com/llvm/llvm-project/issues/105224>`__",""
 "`P2499R0 <https://wg21.link/P2499R0>`__","``string_view`` range constructor should be ``explicit``","2022-07 (Virtual)","|Complete|","16","`#105225 <https://github.com/llvm/llvm-project/issues/105225>`__",""
-"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","23","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
+"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","24","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
 "`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","2022-07 (Virtual)","|Complete|","15","`#105227 <https://github.com/llvm/llvm-project/issues/105227>`__",""
 "`P2517R1 <https://wg21.link/P2517R1>`__","Add a conditional ``noexcept`` specification to ``std::apply``","2022-07 (Virtual)","|Complete|","3.9","`#105229 <https://github.com/llvm/llvm-project/issues/105229>`__",""
 "`P2520R0 <https://wg21.link/P2520R0>`__","``move_iterator`` should be a random access iterator","2022-07 (Virtual)","|Complete|","17","`#105230 <https://github.com/llvm/llvm-project/issues/105230>`__","Implemented as a DR in C++20"
@@ -115,7 +115,7 @@
 "`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20","`#105259 <https://github.com/llvm/llvm-project/issues/105259>`__",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","`#105260 <https://github.com/llvm/llvm-project/issues/105260>`__","The paper is implemented as a DR to C++20"
 "`P2652R2 <https://wg21.link/P2652R2>`__","Disallow User Specialization of ``allocator_traits``","2023-02 (Issaquah)","|Complete|","19","`#105262 <https://github.com/llvm/llvm-project/issues/105262>`__",""
-"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","23","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
+"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","24","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
 "`P2614R2 <https://wg21.link/P2614R2>`__","Deprecate ``numeric_limits::has_denorm``","2023-02 (Issaquah)","|Complete|","18","`#105264 <https://github.com/llvm/llvm-project/issues/105264>`__",""
 "`P2588R3 <https://wg21.link/P2588R3>`__","``barrier``’s phase completion guarantees","2023-02 (Issaquah)","","","`#105265 <https://github.com/llvm/llvm-project/issues/105265>`__",""
 "`P2763R1 <https://wg21.link/P2763R1>`__","``layout_stride`` static extents default constructor fix","2023-02 (Issaquah)","","","`#105266 <https://github.com/llvm/llvm-project/issues/105266>`__",""
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index e8c02fa8e1ce1..c217b32adc014 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -4,7 +4,7 @@
 "`LWG3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","2023-06 (Varna)","|Nothing To Do|","","`#105270 <https://github.com/llvm/llvm-project/issues/105270>`__",""
 "`LWG3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","2023-06 (Varna)","|Complete|","19","`#105271 <https://github.com/llvm/llvm-project/issues/105271>`__",""
 "`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","","","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
-"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","23","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
+"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","24","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
 "`LWG3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 <https://github.com/llvm/llvm-project/issues/105275>`__",""
 "`LWG3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 <https://github.com/llvm/llvm-project/issues/105276>`__",""
 "`LWG3905 <https://wg21.link/LWG3905>`__","Type of ``std::fexcept_t``","2023-06 (Varna)","|Complete|","3.4","`#105277 <https://github.com/llvm/llvm-project/issues/105277>`__",""
@@ -79,8 +79,8 @@
 "`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","","`#118332 <https://github.com/llvm/llvm-project/issues/118332>`__",""
 "`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","","`#118335 <https://github.com/llvm/llvm-project/issues/118335>`__",""
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
-"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","23","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
-"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
+"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","24","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
+"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","|Complete|","24","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
 "`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","|Complete|","23","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
@@ -93,7 +93,7 @@
 "`LWG4088 <https://wg21.link/LWG4088>`__","``println`` ignores the locale imbued in ``std::ostream``","2024-11 (Wrocław)","|Complete|","18","`#118348 <https://github.com/llvm/llvm-project/issues/118348>`__",""
 "`LWG4112 <https://wg21.link/LWG4112>`__","``has-arrow`` should required ``operator->()`` to be ``const``-qualified","2024-11 (Wrocław)","","","`#118349 <https://github.com/llvm/llvm-project/issues/118349>`__",""
 "`LWG4113 <https://wg21.link/LWG4113>`__","Disallow ``has_unique_object_representations<Incomplete[]>``","2024-11 (Wrocław)","|Complete|","","`#118350 <https://github.com/llvm/llvm-project/issues/118350>`__",""
-"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","23","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
+"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","24","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
 "`LWG4124 <https://wg21.link/LWG4124>`__","Cannot format ``zoned_time`` with resolution coarser than ``seconds``","2024-11 (Wrocław)","","","`#118352 <https://github.com/llvm/llvm-project/issues/118352>`__",""
 "`LWG4126 <https://wg21.link/LWG4126>`__","Some feature-test macros for fully freestanding features are not yet marked freestanding","2024-11 (Wrocław)","","","`#118353 <https://github.com/llvm/llvm-project/issues/118353>`__",""
 "`LWG4134 <https://wg21.link/LWG4134>`__","Issue with Philox algorithm specification","2024-11 (Wrocław)","","","`#118354 <https://github.com/llvm/llvm-project/issues/118354>`__",""

>From 3e76eaccfc277fffdfbe26383769987f5596c444 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 22:04:55 +0800
Subject: [PATCH 9/9] fix new warnings

---
 libcxx/include/generator | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index 2176ecbaa1e6e..1a797c3798ec3 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -79,13 +79,13 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Ref, class _Val>
-using __gen_val = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
+using __gen_val _LIBCPP_NODEBUG = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
 
 template <class _Ref, class _Val>
-using __gen_ref = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
+using __gen_ref _LIBCPP_NODEBUG = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
 
 template <class _Ref, class _Val>
-using __gen_yielded =
+using __gen_yielded _LIBCPP_NODEBUG =
     conditional_t<is_reference_v<__gen_ref<_Ref, _Val>>, __gen_ref<_Ref, _Val>, const __gen_ref<_Ref, _Val>&>;
 
 template <class, class>
@@ -313,7 +313,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
 
   _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
-#    ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+#    if _LIBCPP_HAS_EXCEPTIONS
     if (__is_root()) {
       throw;
     } else {
@@ -338,10 +338,10 @@ concept __gen_stateless_allocator =
 template <class _Allocator>
 class __gen_promise_allocator {
 private:
-  using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
-  using __rebind_size_type = allocator_traits<__rebind>::size_type;
+  using __rebind _LIBCPP_NODEBUG           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+  using __rebind_size_type _LIBCPP_NODEBUG = allocator_traits<__rebind>::size_type;
 
-  using __rebind_pointer = allocator_traits<__rebind>::pointer;
+  using __rebind_pointer _LIBCPP_NODEBUG = allocator_traits<__rebind>::pointer;
   static_assert(is_pointer_v<__rebind_pointer>);
 
   _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
@@ -403,7 +403,7 @@ public:
 template <>
 class __gen_promise_allocator<void> {
 private:
-  using __dealloc_fn = void (*)(void*, size_t) noexcept;
+  using __dealloc_fn _LIBCPP_NODEBUG = void (*)(void*, size_t) noexcept;
 
   template <class _Allocator>
   _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
@@ -502,8 +502,8 @@ public:
 template <class _Ref, class _Val>
 class __gen_iter_provider<_Ref, _Val>::__iterator {
 private:
-  using __val = __gen_val<_Ref, _Val>;
-  using __ref = __gen_ref<_Ref, _Val>;
+  using __val _LIBCPP_NODEBUG = __gen_val<_Ref, _Val>;
+  using __ref _LIBCPP_NODEBUG = __gen_ref<_Ref, _Val>;
 
 public:
   using value_type      = __val;
@@ -543,14 +543,14 @@ private:
 template <class _Ref, class _Val = void, class _Allocator = void>
 class generator : public ranges::view_interface<generator<_Ref, _Val, _Allocator>> {
 private:
-  using __val = __gen_val<_Ref, _Val>;
+  using __val _LIBCPP_NODEBUG = __gen_val<_Ref, _Val>;
   static_assert(same_as<remove_cvref_t<__val>, __val> && is_object_v<__val>);
 
-  using __ref = __gen_ref<_Ref, _Val>;
+  using __ref _LIBCPP_NODEBUG = __gen_ref<_Ref, _Val>;
   static_assert(is_reference_v<__ref> ||
                 (same_as<remove_cvref_t<__ref>, __ref> && is_object_v<__ref> && copy_constructible<__ref>));
 
-  using __rref = conditional_t<is_lvalue_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
+  using __rref _LIBCPP_NODEBUG = conditional_t<is_lvalue_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
   static_assert(common_reference_with<__ref&&, __val&> && common_reference_with<__ref&&, __rref&&> &&
                 common_reference_with<__rref&&, const __val&>);
 


From libcxx-commits at lists.llvm.org  Mon Jul 20 07:06:22 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 07:06:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Fix synopsis comment for
 error_condition::message() (PR #210724)
Message-ID: <llvm.org/llvm/llvm-project/pull/210724@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/210724

The method was marked as noexcept in the synopsis, but it's neither marked noexcept in the spec nor in our implementation.

>From 75b598e52dbff9241d35570a8192cd7116d486c3 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 16 Jun 2026 14:01:32 -0400
Subject: [PATCH] [libc++][NFC] Fix synopsis comment for
 error_condition::message()

The method was marked as noexcept in the synopsis, but it's neither
marked noexcept in the spec nor in our implementation.
---
 libcxx/include/system_error | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/system_error b/libcxx/include/system_error
index ef760453d9893..208a586af0dc2 100644
--- a/libcxx/include/system_error
+++ b/libcxx/include/system_error
@@ -98,7 +98,7 @@ public:
     // observers:
     int value() const noexcept;
     const error_category& category() const noexcept;
-    string message() const noexcept;
+    string message() const;
     explicit operator bool() const noexcept;
 };
 


From libcxx-commits at lists.llvm.org  Mon Jul 20 07:18:02 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 07:18:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add coverage for push_range on
 stack/queue with non-default underlying containers (PR #210730)
Message-ID: <llvm.org/llvm/llvm-project/pull/210730@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/210730

This increases the test coverage for stack/queue and actually caught an issue in list::__invariants which was previously dead code.

>From 74b45c5affab26399e4bf08156be896c6fb652bc Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 20 Jul 2026 10:13:31 -0400
Subject: [PATCH] [libc++] Add coverage for push_range on stack/queue with
 non-default underlying containers

This increases the test coverage for stack/queue and actually caught an
issue in list::__invariants which was previously dead code.
---
 libcxx/include/list                                |  2 +-
 .../priqueue.members/push_range.pass.cpp           |  4 ++--
 .../push_range_container_adaptors.h                | 14 +++++++-------
 .../queue/queue.cons/from_range.pass.cpp           |  3 +++
 .../queue/queue.defn/push_range.pass.cpp           |  3 +++
 .../stack/stack.cons/from_range.pass.cpp           |  5 +++++
 .../stack/stack.defn/push_range.pass.cpp           |  5 +++++
 7 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/list b/libcxx/include/list
index b4fb9191cd7f7..3b4eb05cbbc6a 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -1710,7 +1710,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void list<_Tp, _Alloc>::reverse() _NOEXCEPT {
 
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 bool list<_Tp, _Alloc>::__invariants() const {
-  return size() == std::distance(begin(), end());
+  return size() == static_cast<size_type>(std::distance(begin(), end()));
 }
 
 template <class _Tp, class _Alloc>
diff --git a/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp
index 26ce56ff5daf6..f2d6496954997 100644
--- a/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp
@@ -18,10 +18,10 @@
 
 int main(int, char**) {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
-    test_push_range<std::priority_queue<int, std::vector<int, Alloc>>, Iter, Sent>(/*is_result_heapified=*/true);
+    test_push_range<std::priority_queue<int, std::vector<int, Alloc>>, Iter, Sent, /*IsResultHeapified=*/true>();
   });
   test_push_range_move_only<std::priority_queue>();
-  test_push_range_inserter_choice<std::priority_queue, int>(/*is_result_heapified=*/true);
+  test_push_range_inserter_choice<std::priority_queue, int, /*IsResultHeapified=*/true>();
 
   static_assert(test_constraints_push_range<std::priority_queue, int, double>());
 
diff --git a/libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h b/libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h
index adf439081a355..48eceaae88b3c 100644
--- a/libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h
+++ b/libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h
@@ -98,8 +98,8 @@ constexpr TestCase<T> FullContainer_LongRange{
 
 // Container adaptors tests.
 
-template <class Adaptor, class Iter, class Sent>
-constexpr void test_push_range(bool is_result_heapified = false) {
+template <class Adaptor, class Iter, class Sent, bool IsResultHeapified = false>
+constexpr void test_push_range() {
   using T = typename Adaptor::value_type;
 
   auto test = [&](auto& test_case) {
@@ -110,7 +110,7 @@ constexpr void test_push_range(bool is_result_heapified = false) {
     UnwrapAdaptor<Adaptor> unwrap_adaptor(std::move(adaptor));
     auto& c = unwrap_adaptor.get_container();
 
-    if (is_result_heapified) {
+    if constexpr (IsResultHeapified) {
       assert(std::ranges::is_heap(c));
       return std::ranges::is_permutation(c, test_case.expected);
     } else {
@@ -207,8 +207,8 @@ struct Container {
   friend bool operator==(const Container&, const Container&) = default;
 };
 
-template <template <class...> class AdaptorT, class T>
-void test_push_range_inserter_choice(bool is_result_heapified = false) {
+template <template <class...> class AdaptorT, class T, bool IsResultHeapified = false>
+void test_push_range_inserter_choice() {
   { // `append_range` is preferred if available.
     using BaseContainer = Container<T, InserterChoice::AppendRange>;
     using Adaptor       = AdaptorT<T, BaseContainer>;
@@ -220,7 +220,7 @@ void test_push_range_inserter_choice(bool is_result_heapified = false) {
     UnwrapAdaptor<Adaptor> unwrap_adaptor(std::move(adaptor));
     auto& c = unwrap_adaptor.get_container();
     assert(c.inserter_choice == InserterChoice::AppendRange);
-    if (is_result_heapified) {
+    if constexpr (IsResultHeapified) {
       assert(std::ranges::is_heap(c));
       assert(std::ranges::is_permutation(c, in));
     } else {
@@ -239,7 +239,7 @@ void test_push_range_inserter_choice(bool is_result_heapified = false) {
     UnwrapAdaptor<Adaptor> unwrap_adaptor(std::move(adaptor));
     auto& c = unwrap_adaptor.get_container();
     assert(c.inserter_choice == InserterChoice::PushBack);
-    if (is_result_heapified) {
+    if constexpr (IsResultHeapified) {
       assert(std::ranges::is_heap(c));
       assert(std::ranges::is_permutation(c, in));
     } else {
diff --git a/libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp
index d2b21e16c2461..23a67e6b9c230 100644
--- a/libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
+#include <list>
 #include <queue>
 
 #include "../../from_range_container_adaptors.h"
@@ -20,6 +21,8 @@
 int main(int, char**) {
   for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
     test_container_adaptor<std::queue, std::deque, int, Iter, Sent, Alloc>();
+    // Also cover a non-random-access (forward-iterator-only) underlying container.
+    test_container_adaptor<std::queue, std::list, int, Iter, Sent, Alloc>();
   });
   test_container_adaptor_move_only<std::queue>();
 
diff --git a/libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp
index 4ddfe59662499..ae830ba958a8a 100644
--- a/libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp
@@ -12,6 +12,7 @@
 // template<container-compatible-range<T> R>
 //   void push_range(R&& rg); // C++23
 
+#include <list>
 #include <queue>
 
 #include "../../push_range_container_adaptors.h"
@@ -20,6 +21,8 @@
 int main(int, char**) {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
     test_push_range<std::queue<int, std::deque<int, Alloc>>, Iter, Sent>();
+    // Also cover a non-random-access (forward-iterator-only) underlying container.
+    test_push_range<std::queue<int, std::list<int, Alloc>>, Iter, Sent>();
   });
   test_push_range_move_only<std::queue>();
   test_push_range_inserter_choice<std::queue, int>();
diff --git a/libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp
index 90f43dc56e499..2d9e6002862ba 100644
--- a/libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp
@@ -8,7 +8,9 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
+#include <list>
 #include <stack>
+#include <vector>
 
 #include "../../from_range_container_adaptors.h"
 #include "test_macros.h"
@@ -20,6 +22,9 @@
 int main(int, char**) {
   for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
     test_container_adaptor<std::stack, std::deque, int, Iter, Sent, Alloc>();
+    // Also cover other valid underlying containers, including a non-random-access one.
+    test_container_adaptor<std::stack, std::vector, int, Iter, Sent, Alloc>();
+    test_container_adaptor<std::stack, std::list, int, Iter, Sent, Alloc>();
   });
   test_container_adaptor_move_only<std::stack>();
 
diff --git a/libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp
index 0466fc7867859..cef9b98b6ced7 100644
--- a/libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp
@@ -12,7 +12,9 @@
 // template<container-compatible-range<T> R>
 //   void push_range(R&& rg); // C++23
 
+#include <list>
 #include <stack>
+#include <vector>
 
 #include "../../push_range_container_adaptors.h"
 #include "test_macros.h"
@@ -20,6 +22,9 @@
 int main(int, char**) {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
     test_push_range<std::stack<int, std::deque<int, Alloc>>, Iter, Sent>();
+    // Also cover other valid underlying containers, including a non-random-access one.
+    test_push_range<std::stack<int, std::vector<int, Alloc>>, Iter, Sent>();
+    test_push_range<std::stack<int, std::list<int, Alloc>>, Iter, Sent>();
   });
   test_push_range_move_only<std::stack>();
   test_push_range_inserter_choice<std::stack, int>();


From libcxx-commits at lists.llvm.org  Mon Jul 20 07:26:50 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 07:26:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Fix synopsis comment for
 error_condition::message() (PR #210724)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210724@github.com>
Message-ID: <6a5e302a.af6ba3ed.3c8c03.7661@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

The method was marked as noexcept in the synopsis, but it's neither marked noexcept in the spec nor in our implementation.

---
Full diff: https://github.com/llvm/llvm-project/pull/210724.diff


1 Files Affected:

- (modified) libcxx/include/system_error (+1-1) 


``````````diff
diff --git a/libcxx/include/system_error b/libcxx/include/system_error
index ef760453d9893..208a586af0dc2 100644
--- a/libcxx/include/system_error
+++ b/libcxx/include/system_error
@@ -98,7 +98,7 @@ public:
     // observers:
     int value() const noexcept;
     const error_category& category() const noexcept;
-    string message() const noexcept;
+    string message() const;
     explicit operator bool() const noexcept;
 };
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/210724

From libcxx-commits at lists.llvm.org  Mon Jul 20 07:27:05 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 07:27:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some unnecessary
 functions from __vector_layout (PR #207152)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207152@github.com>
Message-ID: <6a5e3039.160324b0.1f653e.11d9@mx.google.com>


================
@@ -400,7 +400,7 @@ class vector {
     return __layout_.__capacity();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
-    return __layout_.__empty();
+    return size() == 0;
----------------
ldionne wrote:

I wasn't aware of (and didn't expect) this breakage before I read this. It seems like the original `_LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED` patch indeed changed whether `vector` requires complete types to compute `empty()`. I can confirm that here: https://godbolt.org/z/EMff4KPjP. This was unintended, and we should fix that at least until we decide we want to change that guarantee. I filed https://github.com/llvm/llvm-project/issues/210732 for that.

I think this should also resolve your concern about different requirements depending on the ABI macro.

https://github.com/llvm/llvm-project/pull/207152

From libcxx-commits at lists.llvm.org  Mon Jul 20 07:51:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 07:51:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add coverage for push_range on
 stack/queue with non-default underlying containers (PR #210730)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210730@github.com>
Message-ID: <6a5e35f0.f3151ff8.37ac76.41d0@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

This increases the test coverage for stack/queue and actually caught an issue in list::__invariants which was previously dead code.

---
Full diff: https://github.com/llvm/llvm-project/pull/210730.diff


7 Files Affected:

- (modified) libcxx/include/list (+1-1) 
- (modified) libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp (+2-2) 
- (modified) libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h (+7-7) 
- (modified) libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp (+3) 
- (modified) libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp (+3) 
- (modified) libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp (+5) 
- (modified) libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp (+5) 


``````````diff
diff --git a/libcxx/include/list b/libcxx/include/list
index b4fb9191cd7f7..3b4eb05cbbc6a 100644
--- a/libcxx/include/list
+++ b/libcxx/include/list
@@ -1710,7 +1710,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void list<_Tp, _Alloc>::reverse() _NOEXCEPT {
 
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX26 bool list<_Tp, _Alloc>::__invariants() const {
-  return size() == std::distance(begin(), end());
+  return size() == static_cast<size_type>(std::distance(begin(), end()));
 }
 
 template <class _Tp, class _Alloc>
diff --git a/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp
index 26ce56ff5daf6..f2d6496954997 100644
--- a/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/priority.queue/priqueue.members/push_range.pass.cpp
@@ -18,10 +18,10 @@
 
 int main(int, char**) {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
-    test_push_range<std::priority_queue<int, std::vector<int, Alloc>>, Iter, Sent>(/*is_result_heapified=*/true);
+    test_push_range<std::priority_queue<int, std::vector<int, Alloc>>, Iter, Sent, /*IsResultHeapified=*/true>();
   });
   test_push_range_move_only<std::priority_queue>();
-  test_push_range_inserter_choice<std::priority_queue, int>(/*is_result_heapified=*/true);
+  test_push_range_inserter_choice<std::priority_queue, int, /*IsResultHeapified=*/true>();
 
   static_assert(test_constraints_push_range<std::priority_queue, int, double>());
 
diff --git a/libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h b/libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h
index adf439081a355..48eceaae88b3c 100644
--- a/libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h
+++ b/libcxx/test/std/containers/container.adaptors/push_range_container_adaptors.h
@@ -98,8 +98,8 @@ constexpr TestCase<T> FullContainer_LongRange{
 
 // Container adaptors tests.
 
-template <class Adaptor, class Iter, class Sent>
-constexpr void test_push_range(bool is_result_heapified = false) {
+template <class Adaptor, class Iter, class Sent, bool IsResultHeapified = false>
+constexpr void test_push_range() {
   using T = typename Adaptor::value_type;
 
   auto test = [&](auto& test_case) {
@@ -110,7 +110,7 @@ constexpr void test_push_range(bool is_result_heapified = false) {
     UnwrapAdaptor<Adaptor> unwrap_adaptor(std::move(adaptor));
     auto& c = unwrap_adaptor.get_container();
 
-    if (is_result_heapified) {
+    if constexpr (IsResultHeapified) {
       assert(std::ranges::is_heap(c));
       return std::ranges::is_permutation(c, test_case.expected);
     } else {
@@ -207,8 +207,8 @@ struct Container {
   friend bool operator==(const Container&, const Container&) = default;
 };
 
-template <template <class...> class AdaptorT, class T>
-void test_push_range_inserter_choice(bool is_result_heapified = false) {
+template <template <class...> class AdaptorT, class T, bool IsResultHeapified = false>
+void test_push_range_inserter_choice() {
   { // `append_range` is preferred if available.
     using BaseContainer = Container<T, InserterChoice::AppendRange>;
     using Adaptor       = AdaptorT<T, BaseContainer>;
@@ -220,7 +220,7 @@ void test_push_range_inserter_choice(bool is_result_heapified = false) {
     UnwrapAdaptor<Adaptor> unwrap_adaptor(std::move(adaptor));
     auto& c = unwrap_adaptor.get_container();
     assert(c.inserter_choice == InserterChoice::AppendRange);
-    if (is_result_heapified) {
+    if constexpr (IsResultHeapified) {
       assert(std::ranges::is_heap(c));
       assert(std::ranges::is_permutation(c, in));
     } else {
@@ -239,7 +239,7 @@ void test_push_range_inserter_choice(bool is_result_heapified = false) {
     UnwrapAdaptor<Adaptor> unwrap_adaptor(std::move(adaptor));
     auto& c = unwrap_adaptor.get_container();
     assert(c.inserter_choice == InserterChoice::PushBack);
-    if (is_result_heapified) {
+    if constexpr (IsResultHeapified) {
       assert(std::ranges::is_heap(c));
       assert(std::ranges::is_permutation(c, in));
     } else {
diff --git a/libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp
index d2b21e16c2461..23a67e6b9c230 100644
--- a/libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/queue/queue.cons/from_range.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
+#include <list>
 #include <queue>
 
 #include "../../from_range_container_adaptors.h"
@@ -20,6 +21,8 @@
 int main(int, char**) {
   for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
     test_container_adaptor<std::queue, std::deque, int, Iter, Sent, Alloc>();
+    // Also cover a non-random-access (forward-iterator-only) underlying container.
+    test_container_adaptor<std::queue, std::list, int, Iter, Sent, Alloc>();
   });
   test_container_adaptor_move_only<std::queue>();
 
diff --git a/libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp
index 4ddfe59662499..ae830ba958a8a 100644
--- a/libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/queue/queue.defn/push_range.pass.cpp
@@ -12,6 +12,7 @@
 // template<container-compatible-range<T> R>
 //   void push_range(R&& rg); // C++23
 
+#include <list>
 #include <queue>
 
 #include "../../push_range_container_adaptors.h"
@@ -20,6 +21,8 @@
 int main(int, char**) {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
     test_push_range<std::queue<int, std::deque<int, Alloc>>, Iter, Sent>();
+    // Also cover a non-random-access (forward-iterator-only) underlying container.
+    test_push_range<std::queue<int, std::list<int, Alloc>>, Iter, Sent>();
   });
   test_push_range_move_only<std::queue>();
   test_push_range_inserter_choice<std::queue, int>();
diff --git a/libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp
index 90f43dc56e499..2d9e6002862ba 100644
--- a/libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/stack/stack.cons/from_range.pass.cpp
@@ -8,7 +8,9 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
+#include <list>
 #include <stack>
+#include <vector>
 
 #include "../../from_range_container_adaptors.h"
 #include "test_macros.h"
@@ -20,6 +22,9 @@
 int main(int, char**) {
   for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
     test_container_adaptor<std::stack, std::deque, int, Iter, Sent, Alloc>();
+    // Also cover other valid underlying containers, including a non-random-access one.
+    test_container_adaptor<std::stack, std::vector, int, Iter, Sent, Alloc>();
+    test_container_adaptor<std::stack, std::list, int, Iter, Sent, Alloc>();
   });
   test_container_adaptor_move_only<std::stack>();
 
diff --git a/libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp b/libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp
index 0466fc7867859..cef9b98b6ced7 100644
--- a/libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/stack/stack.defn/push_range.pass.cpp
@@ -12,7 +12,9 @@
 // template<container-compatible-range<T> R>
 //   void push_range(R&& rg); // C++23
 
+#include <list>
 #include <stack>
+#include <vector>
 
 #include "../../push_range_container_adaptors.h"
 #include "test_macros.h"
@@ -20,6 +22,9 @@
 int main(int, char**) {
   for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
     test_push_range<std::stack<int, std::deque<int, Alloc>>, Iter, Sent>();
+    // Also cover other valid underlying containers, including a non-random-access one.
+    test_push_range<std::stack<int, std::vector<int, Alloc>>, Iter, Sent>();
+    test_push_range<std::stack<int, std::list<int, Alloc>>, Iter, Sent>();
   });
   test_push_range_move_only<std::stack>();
   test_push_range_inserter_choice<std::stack, int>();

``````````

</details>


https://github.com/llvm/llvm-project/pull/210730

From libcxx-commits at lists.llvm.org  Mon Jul 20 07:58:34 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Mon, 20 Jul 2026 07:58:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some unnecessary
 functions from __vector_layout (PR #207152)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207152@github.com>
Message-ID: <6a5e379a.492e23e4.d19bc.9e0d@mx.google.com>


ldionne wrote:

> The changes in this patch undo some very delicate fine-tuning that I did in #155330.

Can you explain? When I reviewed this with Nikolas he explained that he looked at the codegen and all of these are exactly the same after the first InstCombine pass. If that's not the case, then perhaps a revert is mandated, but let's discuss the technical concerns with it.

> I'm disappointed that I wasn't asked to review this patch before merging.

Unless mistaken, you are notified on libc++ PRs, so I'm not certain what your expectation was. I don't think it's realistic to put you on the critical path for every vector change going forward since you landed the size-based vector patch.

https://github.com/llvm/llvm-project/pull/207152

From libcxx-commits at lists.llvm.org  Mon Jul 20 08:50:21 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 08:50:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5e43bd.d681cab9.169270.25ef@mx.google.com>


philnik777 wrote:

@toughengineer Could you add a benchmark to `sequence_container_benchmarks.h` using e.g. `ThrowingIterator` (with `INT_MAX` to never actually throw)? I think that should show the difference and not rely on any implementation details for it.

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Mon Jul 20 08:53:20 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 08:53:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2502R2:
 `std::generator`: Synchronous Coroutine Generator for Ranges (PR #201854)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201854@github.com>
Message-ID: <6a5e4470.bc28b987.2e0c9a.755e@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/201854

From libcxx-commits at lists.llvm.org  Mon Jul 20 08:53:50 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 08:53:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some std::minmax_element
	benchmarks (PR #210272)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210272@github.com>
Message-ID: <6a5e448e.d76d81af.23b347.d3f4@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/210272

From libcxx-commits at lists.llvm.org  Mon Jul 20 09:01:27 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 09:01:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5e4657.c4d3d80a.849c3.dc95@mx.google.com>


philnik777 wrote:

Strike the `sequence_container_benchmarks.h` part, that should be `string.bench.cpp`. Sorry!

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Mon Jul 20 09:02:22 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 09:02:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a5e468e.743400b0.23a7b2.bdc6@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From 8a8d85c8f25952c61c03bcf8c8967d5789f3a278 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/21] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..11a0b42ea54f5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From 43978418a74e0c39bcecff9a045a9fa7f77f2750 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/21] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From da435f9c4c7fa5c0a526d0beccdd0ed57e777469 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/21] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 11a0b42ea54f5..caa7f9d1a2535 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..8bdde44c1cb8c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -895,6 +895,7 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 728593490b1918081d01078fb1095f0f0257d2f5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/21] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From 99d616611ad0970a4956051969408128c8e7f95d Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/21] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From 96b1c78fb96cf6e004aa68f10d510186c3b83fea Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/21] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From 445d2ca55a0c3814662ff7b7a06881efd14d5f2e Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/21] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From ee7f61a808ad8ff5d5e70f601450be56a8f0741a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/21] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 56cbfdaa5c0ff..8ccbcf9d8d227 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -160,7 +160,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -178,7 +178,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -193,7 +193,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b20b5e25fc9cd..04e12afd21689 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From 88d54fccb51126d63c79ded521d856e2ea07b72d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/21] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index caa7f9d1a2535..b6650a314bd40 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 8ccbcf9d8d227..04bdd2a6b343f 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -28,29 +28,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 04e12afd21689..3776873d8936d 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 8bdde44c1cb8c..f038c9d39a27b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -884,6 +884,7 @@ module std {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -895,7 +896,6 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From 52cc4a652899aba13296064b5e21e563455c26d8 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/21] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From a25323b34c7937b00ed55edc31436e92bebd387a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/21] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 3776873d8936d..fbe90c05ccaeb 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From 1a12d1ac51c519ced6fc7dba17d394a671e7e1fb Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/21] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From f187fa0bded69354f5c9293d170c1f062e0dbc03 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/21] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From c5dd5cca5521e65d7c14adfcaa43dc5b65adea22 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/21] 14

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <cstring>
+#include <type_traits>
 
 struct WithTailPadding {
   int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From 82695aaa698cee07453d78ea265fc16accbdd0d3 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 20:42:14 +0100
Subject: [PATCH 15/21] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e4d83e638ca77..0bafdbb1d7ede 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -136,7 +136,7 @@ void test() {
   {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
-
+    std::atomic<T> a;
     T init = make<T>(10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);

>From 6a05621ec814c677f83463123bb34ca14ac3da52 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:13:05 +0100
Subject: [PATCH 16/21] ci

---
 libcxx/include/__atomic/support/gcc.h         |  5 ++--
 .../atomics.types.generic/padding.pass.cpp    | 24 ++++++++++-------
 .../atomics.types.generic/padding.pass.cpp    | 27 ++++++++++---------
 .../atomics.types.generic/padding2.pass.cpp   |  3 ---
 4 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index fbe90c05ccaeb..375c7721965b8 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -50,9 +50,8 @@ struct __cxx_atomic_base_impl {
     std::__clear_padding_if_needed(__a_value);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
   _Tp __a_value;
 };
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index bf594b45ed54f..6f894fe6cb605 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -44,15 +44,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -61,11 +69,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0bafdbb1d7ede..344d08b493e60 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,9 +7,6 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits
@@ -41,15 +38,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -58,11 +63,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
index 4e17471a2461f..1040f342b8206 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -8,9 +8,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-localization
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits

>From 506ca2976f3df389ec8a4709a516cabe447e7f46 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:29:04 +0100
Subject: [PATCH 17/21] CI

---
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding2.pass.cpp   | 273 ------------------
 3 files changed, 2 insertions(+), 275 deletions(-)
 delete mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 6f894fe6cb605..388ab0867fe38 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 344d08b493e60..f1a534a2916d6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
deleted file mode 100644
index 1040f342b8206..0000000000000
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-localization
-
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
-// CAS should work on types with padding bits
-
-#include <atomic>
-#include <cassert>
-#include <cstring>
-#include <type_traits>
-#include <iostream>
-
-struct Foo {
-  int i;
-  char c;
-};
-
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
-
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
-}
-
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
-}
-
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-#ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
-#else
-  (void)f;
-  (void)pad_byte;
-#endif
-}
-
-void test() {
-  std::cerr << "test 1" << std::endl;
-  {
-    // compare_exchange_strong
-    // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
-    std::cerr << "test 2" << std::endl;
-
-    Foo init = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 3" << std::endl;
-    assert_foo_padding(init, 0xBB);
-    std::cerr << "test 4" << std::endl;
-    a.store(init);
-
-    Foo expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 5" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 6" << std::endl;
-
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 7" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 8" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 9" << std::endl;
-    assert(r);
-    std::cerr << "test 10" << std::endl;
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    std::cerr << "test 11" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 42);
-    std::cerr << "test 12" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 13" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 14" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 15" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 16" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 17" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 18" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 19" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 10" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 21" << std::endl;
-    assert(!r);
-    std::cerr << "test 22" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 23" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 24" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 25" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 10);
-    std::cerr << "test 26" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 27" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 28" << std::endl;
-  }
-
-  {
-    // compare_exchange_weak
-    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 29" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 30" << std::endl;
-    a.store(stored);
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 31" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 32" << std::endl;
-
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 33" << std::endl;
-    assert_foo_padding(original_expected, 0xAA);
-    std::cerr << "test 34" << std::endl;
-
-    bool r                  = false;
-    const auto max_attempts = 100;
-    auto current_attempt    = 0;
-    while (!r) {
-      ++current_attempt;
-      std::cerr << "test 35" << std::endl;
-      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      std::cerr << "test 36" << std::endl;
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
-      std::cerr << "test 37" << std::endl;
-      r = a.compare_exchange_weak(expected, new_value);
-      if (r) {
-        std::cerr << "test 38" << std::endl;
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-        std::cerr << "test 39" << std::endl;
-      } else {
-        // Spurious failure: expected is updated to the current atomic value.
-        std::cerr << "test 40" << std::endl;
-        assert(expected.i == 10);
-        std::cerr << "test 41" << std::endl;
-        assert(expected.c == 'a');
-        std::cerr << "test 45" << std::endl;
-        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
-        std::cerr << "test 43" << std::endl;
-      }
-    }
-
-    Foo loaded = a.load();
-    std::cerr << "test 44" << std::endl;
-    assert(loaded.i == 42);
-    std::cerr << "test 45" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 46" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 47" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 48" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 49" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 50" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 51" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 52" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 53" << std::endl;
-
-    bool r = a.compare_exchange_weak(expected, new_value);
-
-    std::cerr << "test 54" << std::endl;
-    assert(!r);
-    std::cerr << "test 55" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 56" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 57" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 58" << std::endl;
-    Foo loaded = a.load();
-    std::cerr << "test 59" << std::endl;
-    assert(loaded.i == 10);
-    std::cerr << "test 60" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 61" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 62" << std::endl;
-  }
-
-  {
-    // Types with unique object representations skip the padding-clearing path.
-    std::atomic<int> a(1);
-    int expected = 1;
-    std::cerr << "test 63" << std::endl;
-    assert(a.compare_exchange_strong(expected, 2));
-    std::cerr << "test 64" << std::endl;
-    assert(expected == 1);
-    std::cerr << "test 65" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 66" << std::endl;
-
-    expected = 3;
-    assert(!a.compare_exchange_strong(expected, 4));
-    std::cerr << "test 67" << std::endl;
-    assert(expected == 2);
-    std::cerr << "test 68" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 69" << std::endl;
-  }
-}
-
-int main(int, char**) {
-  // TODO(LLVM-23): Switch to XFAIL: clang-22
-  std::cerr << "main 1" << std::endl;
-#if __has_builtin(__builtin_clear_padding)
-  std::cerr << "main 2" << std::endl;
-  test();
-  std::cerr << "main 3" << std::endl;
-#endif
-  std::cerr << "main 4" << std::endl;
-  return 0;
-}

>From ef4330c6a55c096505e1ec51c05d1f3040fdcbfc Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:52:15 +0100
Subject: [PATCH 18/21] ci

---
 libcxx/include/__atomic/support/c11.h | 13 ++++++++-----
 libcxx/include/__atomic/support/gcc.h |  9 +++++++--
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 04bdd2a6b343f..2d4cc9fd9aae0 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -28,13 +28,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
 
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 375c7721965b8..ebc5496c530c3 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,12 +46,17 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
       : __a_value(std::__clear_padding_if_needed(__value)) {}
+
   _Tp __a_value;
 };
 

>From 65c7769de6530fd230c0964eb64643f4d070cd67 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:26:08 +0100
Subject: [PATCH 19/21] review

---
 libcxx/include/__atomic/clear_padding.h            | 14 ++++++--------
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 5bc77ff4401ab..f920ef39977b7 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -35,17 +35,16 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, float>>,
            _Not<is_same<_Tp, double>>> {};
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(std::addressof(__obj));
-    }
-  }
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+  return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
+}
+
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
@@ -67,7 +66,6 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 388ab0867fe38..45fb083cccf3f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index f1a534a2916d6..5ffba6ca844c5 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();

>From f9ee2b6fe6d2e45f9bc84f5ae7c4ae8e9fd94316 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:33:16 +0100
Subject: [PATCH 20/21] release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx20Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..19f517e66f096 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange (`Github <https://llvm.org/PR76180>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 5539767ab800b..8e394b4bc4b3a 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -31,7 +31,7 @@
 "`P0458R2 <https://wg21.link/P0458R2>`__","Checking for Existence of an Element in Associative Containers","2018-06 (Rapperswil)","|Complete|","13","`#104029 <https://github.com/llvm/llvm-project/issues/104029>`__",""
 "`P0475R1 <https://wg21.link/P0475R1>`__","LWG 2511: guaranteed copy elision for piecewise construction","2018-06 (Rapperswil)","|Complete|","","`#104031 <https://github.com/llvm/llvm-project/issues/104031>`__",""
 "`P0476R2 <https://wg21.link/P0476R2>`__","Bit-casting object representations","2018-06 (Rapperswil)","|Complete|","14","`#104087 <https://github.com/llvm/llvm-project/issues/104087>`__",""
-"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","","","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
+"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","|Complete|","24","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
 "`P0542R5 <https://wg21.link/P0542R5>`__","Support for contract based programming in C++","2018-06 (Rapperswil)","|Nothing To Do|","n/a","`#104089 <https://github.com/llvm/llvm-project/issues/104089>`__","Pulled at the 2019-07 meeting in Cologne"
 "`P0556R3 <https://wg21.link/P0556R3>`__","Integral power-of-2 operations","2018-06 (Rapperswil)","|Complete|","9","`#104090 <https://github.com/llvm/llvm-project/issues/104090>`__",""
 "`P0619R4 <https://wg21.link/P0619R4>`__","Reviewing Deprecated Facilities of C++17 for C++20","2018-06 (Rapperswil)","|Complete|","20","`#99985 <https://github.com/llvm/llvm-project/issues/99985>`__","Removed headers are still provided as an extension, but with deprecation warnings."

>From b96f67b765d94939dfeb1592b90f6fdf0301d707 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 17:01:57 +0100
Subject: [PATCH 21/21] ci

---
 libcxx/include/__atomic/clear_padding.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index f920ef39977b7..3099faa8ca6d1 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/has_unique_object_representation.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>


From libcxx-commits at lists.llvm.org  Mon Jul 20 13:52:21 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 13:52:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 8d5788c - [libc++][ranges][NFC] Format a
 couple of `elements_view` tests (#210687)
Message-ID: <6a5e8a85.3cce8983.3cf542.d988@mx.google.com>


Author: Hristo Hristov
Date: 2026-07-20T23:52:15+03:00
New Revision: 8d5788cae76da2b82d1330fb070f622cda870adb

URL: https://github.com/llvm/llvm-project/commit/8d5788cae76da2b82d1330fb070f622cda870adb
DIFF: https://github.com/llvm/llvm-project/commit/8d5788cae76da2b82d1330fb070f622cda870adb.diff

LOG: [libc++][ranges][NFC] Format a couple of `elements_view` tests (#210687)

As a pre-requisite for https://github.com/llvm/llvm-project/pull/193891

Added: 
    

Modified: 
    libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 79c4e1f418372..a77e98f845a06 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,10 +22,9 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
-      { std::declval<T>().base() } noexcept;
-    };
+concept IsBaseNoexcept = requires {
+  { std::declval<T>().base() } noexcept;
+};
 
 using BaseView     = std::ranges::subrange<std::tuple<int>*>;
 using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<BaseView, 0>>;

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index 4ff65be645220..a8bc817ced291 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -60,9 +60,9 @@ static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elem
                                        std::ranges::sentinel_t<const std::ranges::elements_view<Range, 0>>>);
 
 // !convertible_to<sentinel_t<V>, sentinel_t<Base>>
-static_assert(!std::is_constructible_v<
-              std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>,
-              std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
+static_assert(
+    !std::is_constructible_v<std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>,
+                             std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
 
 constexpr bool test() {
   // base is init correctly


        

From libcxx-commits at lists.llvm.org  Mon Jul 20 13:52:22 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Mon, 20 Jul 2026 13:52:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][NFC] Format a couple of
 `elements_view` tests (PR #210687)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210687@github.com>
Message-ID: <6a5e8a86.492e23e4.d19bc.97d6@mx.google.com>


https://github.com/Zingam closed https://github.com/llvm/llvm-project/pull/210687

From libcxx-commits at lists.llvm.org  Mon Jul 20 14:46:28 2026
From: libcxx-commits at lists.llvm.org (Vladimir Vereschaka via libcxx-commits)
Date: Mon, 20 Jul 2026 14:46:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] XFAIL
 std/text/text_encoding/text_encoding.members/environment.pass.cpp test on
 Armv7/Linux Ubuntu targets. (PR #207287)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207287@github.com>
Message-ID: <6a5e9734.6c5720b3.2da3b9.be31@mx.google.com>


vvereschaka wrote:

Probably this PR should be reverted https://github.com/llvm/llvm-project/pull/206188

https://github.com/llvm/llvm-project/pull/207287

From libcxx-commits at lists.llvm.org  Mon Jul 20 14:49:39 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Mon, 20 Jul 2026 14:49:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Re-order availability
 mapping to `_LIBCPP_INTRODUCED_IN_LLVM_{N}` (PR #210694)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210694@github.com>
Message-ID: <6a5e97f3.7270d2fd.a9bd6.dedf@mx.google.com>


h-vetinari wrote:

Thanks for the quick reviews! I don't have the commit bit, so please merge at will.

https://github.com/llvm/llvm-project/pull/210694

From libcxx-commits at lists.llvm.org  Mon Jul 20 20:21:46 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 20 Jul 2026 20:21:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P3016R6: Resolve
 inconsistencies in begin/end for `valarray` and braced initializer lists
 (PR #173637)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/173637@github.com>
Message-ID: <6a5ee5ca.7270d2fd.a9bd6.902c@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/173637

From libcxx-commits at lists.llvm.org  Mon Jul 20 21:06:36 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Mon, 20 Jul 2026 21:06:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P3016R6: Resolve
 inconsistencies in begin/end for `valarray` and braced initializer lists
 (PR #173637)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/173637@github.com>
Message-ID: <6a5ef04c.66c0679a.24976c.d8d4@mx.google.com>


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/173637

>From 4f532c29263b21de5ac1731cfcf2f237a028be70 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 26 Dec 2025 18:14:37 +0800
Subject: [PATCH 1/8] [libc++] P3016R6: Resolve inconsistencies in begin/end
 for `valarray` and braced initializer lists

This paper is implemented as a Defect Report against C++11 and later
revisions.
- Inclusion of `<typeinfo>` in `<functional>` should be considered as a
patch to C++ TR1 [N1836](https://wg21.link/N1836), while the related
parts were merged into C++11.
- Changes of `initializer_list` should be considered as a patch to C++11
paper [N2672](https://wg21.link/n2672).
- Changes of `valarray`, `std::begin`, and `std::end` and availability
of range access functions in `<valarray>` should be considered as a
patch to C++11 paper [N2930](https://wg21.link/n2930).
- Inclusion of `<typeinfo>` in `<typeindex>` should be considered as a
patch to C++11 paper [N2932](https://wg21.link/n2932).
- Changes of `std::cbegin`, `std::cend`, `std::rbegin`, `std::rend`,
`std::crbegin`, and `std::crend` and the inclusion of
`<initializer_list>` in `<iterator>` should be considered as a patch to
[LWG2128](https://wg21.link/lwg2128), while LWG2128 is consistently
considered to be a pure feature addition in C++14 by implementations.
- Changes of `std::data`, `std::empty`, and `std::size` should be
considered as a patch to C++17 paper [N4280](https://wg21.link/n4280).
- Inclusion of `<initializer_list>` and `<typeinfo>` in `<any>` should
be considered as a patch to C++17 paper
[P0220R1](https://wg21.link/p0220r1).
- Changes of `std::ssize` should be considered as a patch to C++20 paper
[P1227R2](https://wg21.link/p1227r2).
- Inclusion of `<initializer_list>` in `<functional>` should be
considered as a patch to C++23 paper
[P0288R9](https://wg21.link/p0288r9).
- Availability of range access functions in `<stacktrace>` should be
considered as a patch to C++23 paper
[P0881R7](https://wg21.link/p0881r7).
- Availability of range access functions in `<optional>` should be
considered as a patch to C++26 paper
[P3168R2](https://wg21.link/p3168r2).

Notes for backported and missing parts:
- Changes of `<stacktrace>` are not implemented because P0881R7 is not
implemented. But I think P3016R6 should be considered complete with note
added.
- `constexpr` for member functions of `initializer_list` is only added
since C++14, in the spirit of C++14 paper
[N3471](https://wg21.link/n3471).
- Changes of inclusions and `valarray` are unconditionally implemented,
which follows the current convention of libc++.
---
 libcxx/docs/FeatureTestMacroTable.rst         |   4 +
 libcxx/docs/Status/Cxx26Papers.csv            |   2 +-
 libcxx/include/__iterator/access.h            |  24 ++-
 libcxx/include/__iterator/reverse_access.h    |  48 +++--
 libcxx/include/__iterator/reverse_iterator.h  |   8 +-
 libcxx/include/any                            |   7 +-
 libcxx/include/functional                     |  10 +-
 libcxx/include/initializer_list               |  23 +--
 libcxx/include/iterator                       |  59 +++---
 libcxx/include/optional                       |   8 +
 libcxx/include/typeindex                      |   5 +-
 libcxx/include/valarray                       |  76 +++-----
 libcxx/include/version                        |   4 +
 libcxx/modules/std/initializer_list.inc       |   4 -
 libcxx/modules/std/valarray.inc               |   3 -
 .../language.support/nodiscard.verify.cpp     |   2 +
 .../numerics/numarray/nodiscard.verify.cpp    |  10 +-
 .../to_address_std_iterators.pass.cpp         |   6 +-
 .../iterator.container/data.pass.cpp          |   9 +-
 .../iterator.container/empty.pass.cpp         |   9 +-
 .../iterator.container/size.pass.cpp          |  16 +-
 .../iterator.container/ssize.pass.cpp         |  28 +--
 .../iterator.range/begin-end.array.pass.cpp   |  10 +-
 .../begin-end.container.pass.cpp              | 179 ++++++++++++++++--
 .../begin-end.initializer_list.pass.cpp       |  10 +-
 .../mandatory_inclusions.gen.py               |   7 +-
 .../reverse.iter.cons/ctor.iter.pass.cpp      |  12 +-
 .../support.initlist.access/access.pass.cpp   |  89 ++++++---
 .../support.initlist.range/begin_end.pass.cpp |  68 -------
 .../initializer_list.version.compile.pass.cpp |  74 ++++++++
 .../valarray.version.compile.pass.cpp         |  74 ++++++++
 .../version.version.compile.pass.cpp          |  78 ++++++++
 .../template.valarray/types.compile.pass.cpp  |  53 ++++++
 .../numarray/template.valarray/types.pass.cpp |  29 ---
 .../valarray.members/begin-end.pass.cpp       |  82 ++++++++
 .../valarray.range/begin-end.pass.cpp         |  69 -------
 .../to_address_std_iterators.pass.cpp         |   4 +-
 .../generate_feature_test_macro_components.py |  10 +
 libcxx/utils/libcxx/header_information.py     |   6 +-
 39 files changed, 835 insertions(+), 384 deletions(-)
 delete mode 100644 libcxx/test/std/language.support/support.initlist/support.initlist.range/begin_end.pass.cpp
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/initializer_list.version.compile.pass.cpp
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/valarray.version.compile.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numarray/template.valarray/types.compile.pass.cpp
 delete mode 100644 libcxx/test/std/numerics/numarray/template.valarray/types.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numarray/template.valarray/valarray.members/begin-end.pass.cpp
 delete mode 100644 libcxx/test/std/numerics/numarray/valarray.range/begin-end.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..c8b2397bd5c3b 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -34,6 +34,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_generic_associative_lookup``                   ``201304L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_initializer_list``                             ``202511L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_integer_sequence``                             ``201304L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_integral_constant_callable``                   ``201304L``
@@ -66,6 +68,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_tuples_by_type``                               ``201304L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_valarray``                                     ``202511L``
+    ---------------------------------------------------------- -----------------
     **C++17**
     ----------------------------------------------------------------------------
     ``__cpp_lib_addressof_constexpr``                          ``201603L``
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 916bb559f254b..d5e1fd33cf0f4 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -158,7 +158,7 @@
 "`P1317R2 <https://wg21.link/P1317R2>`__","Remove return type deduction in ``std::apply``","2025-06 (Sofia)","","","`#148183 <https://github.com/llvm/llvm-project/issues/148183>`__",""
 "","","","","","",""
 "`P3920R0 <https://wg21.link/P3920R0>`__","Wording for NB comment resolution on trivial relocation","2025-11 (Kona)","|Nothing To Do|","","`#171269 <https://github.com/llvm/llvm-project/issues/171269>`__",""
-"`P3016R6 <https://wg21.link/P3016R6>`__","Resolve inconsistencies in begin/end for ``valarray`` and braced initializer lists","2025-11 (Kona)","","","`#171271 <https://github.com/llvm/llvm-project/issues/171271>`__",""
+"`P3016R6 <https://wg21.link/P3016R6>`__","Resolve inconsistencies in begin/end for ``valarray`` and braced initializer lists","2025-11 (Kona)","|Complete|","22","`#171271 <https://github.com/llvm/llvm-project/issues/171271>`__","Implemented as a DR against C++11. Change of ``<stacktrace>`` is blocked on `P0881R7 <https://wg21.link/P0881R7>`__."
 "`P3567R2 <https://wg21.link/P3567R2>`__","``flat_meow`` Fixes","2025-11 (Kona)","|Complete|","22","`#171272 <https://github.com/llvm/llvm-project/issues/171272>`__",""
 "`P3663R3 <https://wg21.link/P3663R3>`__","Future-proof ``submdspan_mapping``","2025-11 (Kona)","","","`#166089 <https://github.com/llvm/llvm-project/issues/166089>`__",""
 "`P3914R0 <https://wg21.link/P3914R0>`__","Assorted NB comment resolutions for Kona 2025","2025-11 (Kona)","","","`#171274 <https://github.com/llvm/llvm-project/issues/171274>`__",""
diff --git a/libcxx/include/__iterator/access.h b/libcxx/include/__iterator/access.h
index c01d3fa2baa8d..5921c8f23dc0b 100644
--- a/libcxx/include/__iterator/access.h
+++ b/libcxx/include/__iterator/access.h
@@ -32,24 +32,32 @@ template <class _Tp, size_t _Np>
 #if !defined(_LIBCPP_CXX03_LANG)
 
 template <class _Cp>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto begin(_Cp& __c) -> decltype(__c.begin()) {
-  return __c.begin();
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto begin(_Cp& __c) //
+    noexcept(noexcept(__c.begin()))                                                        //
+    -> decltype(/*-*/ __c.begin()) {
+  return /*--------*/ __c.begin();
 }
 
 template <class _Cp>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto begin(const _Cp& __c)
-    -> decltype(__c.begin()) {
-  return __c.begin();
+    //
+    noexcept(noexcept(__c.begin())) //
+    -> decltype(/*-*/ __c.begin()) {
+  return /*--------*/ __c.begin();
 }
 
 template <class _Cp>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto end(_Cp& __c) -> decltype(__c.end()) {
-  return __c.end();
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto end(_Cp& __c) //
+    noexcept(noexcept(__c.end()))                                                        //
+    -> decltype(/*-*/ __c.end()) {
+  return /*--------*/ __c.end();
 }
 
 template <class _Cp>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto end(const _Cp& __c) -> decltype(__c.end()) {
-  return __c.end();
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto end(const _Cp& __c) //
+    noexcept(noexcept(__c.end()))                                                              //
+    -> decltype(/*-*/ __c.end()) {
+  return /*--------*/ __c.end();
 }
 
 #  if _LIBCPP_STD_VER >= 14
diff --git a/libcxx/include/__iterator/reverse_access.h b/libcxx/include/__iterator/reverse_access.h
index b1574bc665119..ec045d8cb3586 100644
--- a/libcxx/include/__iterator/reverse_access.h
+++ b/libcxx/include/__iterator/reverse_access.h
@@ -24,59 +24,67 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp, size_t _Np>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI
-_LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator<_Tp*> rbegin(_Tp (&__array)[_Np]) {
+_LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator<_Tp*> rbegin(_Tp (&__array)[_Np]) noexcept {
   return reverse_iterator<_Tp*>(__array + _Np);
 }
 
 template <class _Tp, size_t _Np>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator<_Tp*> rend(_Tp (&__array)[_Np]) {
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator<_Tp*> rend(_Tp (&__array)[_Np]) noexcept {
   return reverse_iterator<_Tp*>(__array);
 }
 
 template <class _Ep>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator<const _Ep*>
-rbegin(initializer_list<_Ep> __il) {
+rbegin(initializer_list<_Ep> __il) noexcept {
   return reverse_iterator<const _Ep*>(__il.end());
 }
 
 template <class _Ep>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator<const _Ep*>
-rend(initializer_list<_Ep> __il) {
+rend(initializer_list<_Ep> __il) noexcept {
   return reverse_iterator<const _Ep*>(__il.begin());
 }
 
 template <class _Cp>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto rbegin(_Cp& __c) -> decltype(__c.rbegin()) {
-  return __c.rbegin();
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto rbegin(_Cp& __c) //
+    noexcept(noexcept(__c.rbegin()))                                                        //
+    -> decltype(/*-*/ __c.rbegin()) {
+  return /*--------*/ __c.rbegin();
 }
 
 template <class _Cp>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto rbegin(const _Cp& __c)
-    -> decltype(__c.rbegin()) {
-  return __c.rbegin();
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto rbegin(const _Cp& __c) //
+    noexcept(noexcept(__c.rbegin()))                                                              //
+    -> decltype(/*-*/ __c.rbegin()) {
+  return /*--------*/ __c.rbegin();
 }
 
 template <class _Cp>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto rend(_Cp& __c) -> decltype(__c.rend()) {
-  return __c.rend();
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto rend(_Cp& __c) //
+    noexcept(noexcept(__c.rend()))                                                        //
+    -> decltype(/*-*/ __c.rend()) {
+  return /*--------*/ __c.rend();
 }
 
 template <class _Cp>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto rend(const _Cp& __c)
-    -> decltype(__c.rend()) {
-  return __c.rend();
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto rend(const _Cp& __c) //
+    noexcept(noexcept(__c.rend()))                                                              //
+    -> decltype(/*-*/ __c.rend()) {
+  return /*--------*/ __c.rend();
 }
 
 template <class _Cp>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto crbegin(const _Cp& __c)
-    -> decltype(std::rbegin(__c)) {
-  return std::rbegin(__c);
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto crbegin(const _Cp& __c) //
+    noexcept(noexcept(std::rbegin(__c)))                                                           //
+    -> decltype(/*-*/ std::rbegin(__c)) {
+  return /*--------*/ std::rbegin(__c);
 }
 
 template <class _Cp>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto crend(const _Cp& __c)
-    -> decltype(std::rend(__c)) {
-  return std::rend(__c);
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 auto crend(const _Cp& __c) //
+    noexcept(noexcept(std::rend(__c)))                                                           //
+    -> decltype(/*-*/ std::rend(__c)) {
+  return /*--------*/ std::rend(__c);
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__iterator/reverse_iterator.h b/libcxx/include/__iterator/reverse_iterator.h
index 64bdaf9ae5896..6f5a682c47bf5 100644
--- a/libcxx/include/__iterator/reverse_iterator.h
+++ b/libcxx/include/__iterator/reverse_iterator.h
@@ -80,7 +80,9 @@ class reverse_iterator
 #ifndef _LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator() : __t_(), current() {}
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 explicit reverse_iterator(_Iter __x) : __t_(__x), current(__x) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 explicit reverse_iterator(_Iter __x)
+      _NOEXCEPT_(is_nothrow_constructible<_Iter, _Iter&>::value)
+      : __t_(__x), current(__x) {}
 
   template <class _Up, __enable_if_t<!is_same<_Up, _Iter>::value && is_convertible<_Up const&, _Iter>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator(const reverse_iterator<_Up>& __u)
@@ -97,7 +99,9 @@ class reverse_iterator
 #else
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator() : current() {}
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 explicit reverse_iterator(_Iter __x) : current(__x) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 explicit reverse_iterator(_Iter __x)
+      _NOEXCEPT_(is_nothrow_constructible<_Iter, _Iter&>::value)
+      : current(__x) {}
 
   template <class _Up, __enable_if_t<!is_same<_Up, _Iter>::value && is_convertible<_Up const&, _Iter>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator(const reverse_iterator<_Up>& __u)
diff --git a/libcxx/include/any b/libcxx/include/any
index 670b7512e5699..d4f4854277f54 100644
--- a/libcxx/include/any
+++ b/libcxx/include/any
@@ -13,6 +13,9 @@
 /*
    any synopsis
 
+#include <initializer_list>
+#include <typeinfo>
+
 namespace std {
 
   class bad_any_cast : public bad_cast
@@ -108,9 +111,11 @@ namespace std {
 #  include <__utility/move.h>
 #  include <__utility/unreachable.h>
 #  include <__verbose_abort>
+#  include <version>
+
+// standard-mandated includes
 #  include <initializer_list>
 #  include <typeinfo>
-#  include <version>
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #    pragma GCC system_header
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 9d588231d548d..42e2512adeb5d 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -13,6 +13,9 @@
 /*
     functional synopsis
 
+#include <initializer_list>
+#include <typeinfo>
+
 namespace std
 {
 
@@ -584,7 +587,6 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 
 #  if defined(_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23) && _LIBCPP_STD_VER <= 14
 #    include <array>
-#    include <initializer_list>
 #    include <unordered_map>
 #  endif
 
@@ -598,7 +600,6 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 #    include <stdexcept>
 #    include <tuple>
 #    include <type_traits>
-#    include <typeinfo>
 #    include <utility>
 #    include <vector>
 #  endif
@@ -606,6 +607,11 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 #  if defined(_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23) && _LIBCPP_STD_VER == 23
 #    include <__vector/vector.h>
 #  endif
+
+// standard-mandated includes
+#  include <initializer_list>
+#  include <typeinfo>
+
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 
 #endif // _LIBCPP_FUNCTIONAL
diff --git a/libcxx/include/initializer_list b/libcxx/include/initializer_list
index a362a1020eb04..ef43b642eec30 100644
--- a/libcxx/include/initializer_list
+++ b/libcxx/include/initializer_list
@@ -30,14 +30,13 @@ public:
 
     initializer_list() noexcept; // constexpr in C++14
 
+    const E* data()  const noexcept; // constexpr in C++14
     size_t   size()  const noexcept; // constexpr in C++14
+    bool     empty() const noexcept; // constexpr in C++14
     const E* begin() const noexcept; // constexpr in C++14
     const E* end()   const noexcept; // constexpr in C++14
 };
 
-template<class E> const E* begin(initializer_list<E> il) noexcept; // constexpr in C++14
-template<class E> const E* end(initializer_list<E> il) noexcept; // constexpr in C++14
-
 }  // std
 
 */
@@ -78,10 +77,18 @@ public:
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 initializer_list() _NOEXCEPT : __begin_(nullptr), __size_(0) {}
 
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const _Ep* data() const noexcept {
+    return __begin_;
+  }
+
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t size() const _NOEXCEPT {
     return __size_;
   }
 
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool empty() const noexcept {
+    return __size_ == 0;
+  }
+
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const _Ep* begin() const _NOEXCEPT {
     return __begin_;
   }
@@ -91,16 +98,6 @@ public:
   }
 };
 
-template <class _Ep>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const _Ep* begin(initializer_list<_Ep> __il) _NOEXCEPT {
-  return __il.begin();
-}
-
-template <class _Ep>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const _Ep* end(initializer_list<_Ep> __il) _NOEXCEPT {
-  return __il.end();
-}
-
 } // namespace std
 
 #  endif // _LIBCPP_CXX03_LANG
diff --git a/libcxx/include/iterator b/libcxx/include/iterator
index f9996cb2bebf4..752543913a6d4 100644
--- a/libcxx/include/iterator
+++ b/libcxx/include/iterator
@@ -13,7 +13,9 @@
 /*
     iterator synopsis
 
+#include <compare>
 #include <concepts>
+#include <initializer_list>
 
 namespace std
 {
@@ -639,41 +641,49 @@ public:
     bool failed() const noexcept;
 };
 
-template <class C> constexpr auto begin(C& c) -> decltype(c.begin());                   // constexpr since C++17
-template <class C> constexpr auto begin(const C& c) -> decltype(c.begin());             // constexpr since C++17
-template <class C> constexpr auto end(C& c) -> decltype(c.end());                       // constexpr since C++17
-template <class C> constexpr auto end(const C& c) -> decltype(c.end());                 // constexpr since C++17
+template <class C> constexpr auto begin(C& c) noexcept(see-below) -> decltype(c.begin());       // constexpr since C++17
+template <class C> constexpr auto begin(const C& c) noexcept(see-below) -> decltype(c.begin()); // constexpr since C++17
+template <class C> constexpr auto end(C& c) noexcept(see-below) -> decltype(c.end());           // constexpr since C++17
+template <class C> constexpr auto end(const C& c) noexcept(see-below) -> decltype(c.end());     // constexpr since C++17
 template <class T, size_t N> constexpr T* begin(T (&array)[N]) noexcept;
 template <class T, size_t N> constexpr T* end(T (&array)[N]) noexcept;
 
 template <class C> constexpr auto cbegin(const C& c) noexcept(see-below) -> decltype(std::begin(c)); // C++14
 template <class C> constexpr auto cend(const C& c) noexcept(see-below) -> decltype(std::end(c));     // C++14
-template <class C> constexpr auto rbegin(C& c) -> decltype(c.rbegin());                 // C++14, constexpr since C++17
-template <class C> constexpr auto rbegin(const C& c) -> decltype(c.rbegin());           // C++14, constexpr since C++17
-template <class C> constexpr auto rend(C& c) -> decltype(c.rend());                     // C++14, constexpr since C++17
-template <class C> constexpr auto rend(const C& c) -> decltype(c.rend());               // C++14, constexpr since C++17
-template <class E> constexpr reverse_iterator<const E*> rbegin(initializer_list<E> il); // C++14, constexpr since C++17
-template <class E> constexpr reverse_iterator<const E*> rend(initializer_list<E> il);   // C++14, constexpr since C++17
-template <class T, size_t N> constexpr reverse_iterator<T*> rbegin(T (&array)[N]);      // C++14, constexpr since C++17
-template <class T, size_t N> constexpr reverse_iterator<T*> rend(T (&array)[N]);        // C++14, constexpr since C++17
-template <class C> constexpr auto crbegin(const C& c) -> decltype(std::rbegin(c));      // C++14, constexpr since C++17
-template <class C> constexpr auto crend(const C& c) -> decltype(std::rend(c));          // C++14, constexpr since C++17
+template <class C>
+constexpr auto rbegin(C& c) noexcept(see-below) -> decltype(c.rbegin());            // C++14, constexpr since C++17
+template <class C>
+constexpr auto rbegin(const C& c) noexcept(see-below) -> decltype(c.rbegin());      // C++14, constexpr since C++17
+template <class C>
+constexpr auto rend(C& c) noexcept(see-below) -> decltype(c.rend());                // C++14, constexpr since C++17
+template <class C>
+constexpr auto rend(const C& c) noexcept(see-below) -> decltype(c.rend());          // C++14, constexpr since C++17
+template <class E>
+constexpr reverse_iterator<const E*> rbegin(initializer_list<E> il) noexcept;       // C++14, constexpr since C++17
+template <class E>
+constexpr reverse_iterator<const E*> rend(initializer_list<E> il) noexcept;         // C++14, constexpr since C++17
+template <class T, size_t N>
+constexpr reverse_iterator<T*> rbegin(T (&array)[N]) noexcept;                      // C++14, constexpr since C++17
+template <class T, size_t N>
+constexpr reverse_iterator<T*> rend(T (&array)[N]) noexcept;                        // C++14, constexpr since C++17
+template <class C>
+constexpr auto crbegin(const C& c) noexcept(see-below) -> decltype(std::rbegin(c)); // C++14, constexpr since C++17
+template <class C>
+constexpr auto crend(const C& c) noexcept(see-below) -> decltype(std::rend(c));     // C++14, constexpr since C++17
 
 // 24.8, container access:
-template <class C> constexpr auto size(const C& c) -> decltype(c.size());         // C++17
-template <class T, size_t N> constexpr size_t size(const T (&array)[N]) noexcept; // C++17
+template <class C> constexpr auto size(const C& c) noexcept(see-below) -> decltype(c.size()); // C++17
+template <class T, size_t N> constexpr size_t size(const T (&array)[N]) noexcept;             // C++17
 
-template <class C> constexpr auto ssize(const C& c)
+template <class C> constexpr auto ssize(const C& c) noexcept(see-below)
     -> common_type_t<ptrdiff_t, make_signed_t<decltype(c.size())>>;                    // C++20
 template <class T, ptrdiff_t> constexpr ptrdiff_t ssize(const T (&array)[N]) noexcept; // C++20
 
-template <class C> constexpr auto empty(const C& c) -> decltype(c.empty());       // C++17
-template <class T, size_t N> constexpr bool empty(const T (&array)[N]) noexcept;  // C++17
-template <class E> constexpr bool empty(initializer_list<E> il) noexcept;         // C++17
-template <class C> constexpr auto data(C& c) -> decltype(c.data());               // C++17
-template <class C> constexpr auto data(const C& c) -> decltype(c.data());         // C++17
-template <class T, size_t N> constexpr T* data(T (&array)[N]) noexcept;           // C++17
-template <class E> constexpr const E* data(initializer_list<E> il) noexcept;      // C++17
+template <class C> constexpr auto empty(const C& c) noexcept(see-below) -> decltype(c.empty()); // C++17
+template <class T, size_t N> constexpr bool empty(const T (&array)[N]) noexcept;                // C++17
+template <class C> constexpr auto data(C& c) noexcept(see-below) -> decltype(c.data());         // C++17
+template <class C> constexpr auto data(const C& c) noexcept(see-below) -> decltype(c.data());   // C++17
+template <class T, size_t N> constexpr T* data(T (&array)[N]) noexcept;                         // C++17
 
 }  // std
 
@@ -736,6 +746,7 @@ template <class E> constexpr const E* data(initializer_list<E> il) noexcept;
 // [iterator.synopsis]
 #  include <compare>
 #  include <concepts>
+#  include <initializer_list>
 
 // [range.access.general]
 #  if _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 2499479348892..f86892f1346c7 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -272,6 +272,7 @@ namespace std {
 #  include <__fwd/functional.h>
 #  include <__iterator/bounded_iter.h>
 #  include <__iterator/capacity_aware_iterator.h>
+#  include <__iterator/reverse_iterator.h>
 #  include <__memory/addressof.h>
 #  include <__memory/construct_at.h>
 #  include <__ranges/enable_borrowed_range.h>
@@ -323,6 +324,13 @@ namespace std {
 // [optional.syn]
 #  include <compare>
 
+// [iterator.range]
+#  include <__iterator/access.h>
+#  include <__iterator/data.h>
+#  include <__iterator/empty.h>
+#  include <__iterator/reverse_access.h>
+#  include <__iterator/size.h>
+
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #    pragma GCC system_header
 #  endif
diff --git a/libcxx/include/typeindex b/libcxx/include/typeindex
index 289784b372027..9668ca27bfd14 100644
--- a/libcxx/include/typeindex
+++ b/libcxx/include/typeindex
@@ -14,6 +14,9 @@
 
     typeindex synopsis
 
+#include <compare>
+#include <typeinfo>
+
 namespace std
 {
 
@@ -50,11 +53,11 @@ struct hash<type_index>
 #else
 #  include <__config>
 #  include <__functional/unary_function.h>
-#  include <typeinfo>
 #  include <version>
 
 // standard-mandated includes
 #  include <compare>
+#  include <typeinfo>
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #    pragma GCC system_header
diff --git a/libcxx/include/valarray b/libcxx/include/valarray
index 37ab9864ac452..a08a5d4f82a83 100644
--- a/libcxx/include/valarray
+++ b/libcxx/include/valarray
@@ -13,6 +13,8 @@
 /*
     valarray synopsis
 
+#include <initializer_list>
+
 namespace std
 {
 
@@ -21,6 +23,8 @@ class valarray
 {
 public:
     typedef T value_type;
+    typedef unspecified iterator;
+    typedef unspecified const_iterator;
 
     // construct/destroy:
     valarray();
@@ -90,6 +94,10 @@ public:
     valarray& operator>>=(const valarray& v);
 
     // member functions:
+    iterator begin();
+    iterator end();
+    const_iterator begin() const;
+    const_iterator end() const;
     void swap(valarray& v) noexcept;
 
     size_t size() const;
@@ -334,11 +342,6 @@ template<class T> valarray<T> sqrt (const valarray<T>& x);
 template<class T> valarray<T> tan (const valarray<T>& x);
 template<class T> valarray<T> tanh (const valarray<T>& x);
 
-template <class T> unspecified1 begin(valarray<T>& v);
-template <class T> unspecified2 begin(const valarray<T>& v);
-template <class T> unspecified1 end(valarray<T>& v);
-template <class T> unspecified2 end(const valarray<T>& v);
-
 }  // std
 
 */
@@ -368,11 +371,22 @@ template <class T> unspecified2 end(const valarray<T>& v);
 #  include <cmath>
 #  include <version>
 
+#  if _LIBCPP_STD_VER >= 14
+#    include <__iterator/reverse_iterator.h>
+#  endif
+
 // standard-mandated includes
 
 // [valarray.syn]
 #  include <initializer_list>
 
+// [iterator.range]
+#  include <__iterator/access.h>
+#  include <__iterator/data.h>
+#  include <__iterator/empty.h>
+#  include <__iterator/reverse_access.h>
+#  include <__iterator/size.h>
+
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #    pragma GCC system_header
 #  endif
@@ -420,18 +434,6 @@ class mask_array;
 template <class _Tp>
 class indirect_array;
 
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _Tp* begin(valarray<_Tp>& __v);
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI const _Tp* begin(const valarray<_Tp>& __v);
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _Tp* end(valarray<_Tp>& __v);
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI const _Tp* end(const valarray<_Tp>& __v);
-
 template <class _Op, class _A0>
 struct _UnaryOp {
   typedef typename _Op::__result_type __result_type;
@@ -787,6 +789,9 @@ public:
   typedef _Tp value_type;
   typedef _Tp __result_type;
 
+  typedef _Tp* iterator;
+  typedef const _Tp* const_iterator;
+
 private:
   value_type* __begin_;
   value_type* __end_;
@@ -909,6 +914,11 @@ public:
   _LIBCPP_HIDE_FROM_ABI valarray& operator>>=(const _Expr& __v);
 
   // member functions:
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator begin() { return __begin_; }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator end() { return __end_; }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const { return __begin_; }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const { return __end_; }
+
   _LIBCPP_HIDE_FROM_ABI void swap(valarray& __v) _NOEXCEPT;
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t size() const { return static_cast<size_t>(__end_ - __begin_); }
@@ -941,18 +951,6 @@ private:
   template <class>
   friend class __val_expr;
 
-  template <class _Up>
-  friend _Up* begin(valarray<_Up>& __v);
-
-  template <class _Up>
-  friend const _Up* begin(const valarray<_Up>& __v);
-
-  template <class _Up>
-  friend _Up* end(valarray<_Up>& __v);
-
-  template <class _Up>
-  friend const _Up* end(const valarray<_Up>& __v);
-
   _LIBCPP_HIDE_FROM_ABI void __clear(size_t __capacity);
   valarray& __assign_range(const value_type* __f, const value_type* __l);
 };
@@ -3281,26 +3279,6 @@ tanh(const _Expr& __x) {
   return __val_expr<_Op>(_Op(__tanh_expr<value_type>(), __x));
 }
 
-template <class _Tp>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _Tp* begin(valarray<_Tp>& __v) {
-  return __v.__begin_;
-}
-
-template <class _Tp>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI const _Tp* begin(const valarray<_Tp>& __v) {
-  return __v.__begin_;
-}
-
-template <class _Tp>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _Tp* end(valarray<_Tp>& __v) {
-  return __v.__end_;
-}
-
-template <class _Tp>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI const _Tp* end(const valarray<_Tp>& __v) {
-  return __v.__end_;
-}
-
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..09b70b09cac92 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -141,6 +141,7 @@ __cpp_lib_has_unique_object_representations             201606L <type_traits>
 __cpp_lib_hazard_pointer                                202306L <hazard_pointer>
 __cpp_lib_hypot                                         201603L <cmath>
 __cpp_lib_incomplete_container_elements                 201505L <forward_list> <list> <vector>
+__cpp_lib_initializer_list                              202511L <initializer_list>
 __cpp_lib_inplace_vector                                202406L <inplace_vector>
 __cpp_lib_int_pow2                                      202002L <bit>
 __cpp_lib_integer_comparison_functions                  202002L <utility>
@@ -287,6 +288,7 @@ __cpp_lib_uncaught_exceptions                           201411L <exception>
 __cpp_lib_unordered_map_try_emplace                     201411L <unordered_map>
 __cpp_lib_unreachable                                   202202L <utility>
 __cpp_lib_unwrap_ref                                    201811L <functional>
+__cpp_lib_valarray                                      202511L <valarray>
 __cpp_lib_variant                                       202306L <variant>
                                                         202106L // C++20
                                                         202102L // C++17
@@ -310,6 +312,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_complex_udls                         201309L
 # define __cpp_lib_exchange_function                    201304L
 # define __cpp_lib_generic_associative_lookup           201304L
+# define __cpp_lib_initializer_list                     202511L
 # define __cpp_lib_integer_sequence                     201304L
 # define __cpp_lib_integral_constant_callable           201304L
 # define __cpp_lib_is_final                             201402L
@@ -330,6 +333,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_transparent_operators                201210L
 # define __cpp_lib_tuple_element_t                      201402L
 # define __cpp_lib_tuples_by_type                       201304L
+# define __cpp_lib_valarray                             202511L
 #endif
 
 #if _LIBCPP_STD_VER >= 17
diff --git a/libcxx/modules/std/initializer_list.inc b/libcxx/modules/std/initializer_list.inc
index 1dbeb220ca3c5..53d7ea79c6e7e 100644
--- a/libcxx/modules/std/initializer_list.inc
+++ b/libcxx/modules/std/initializer_list.inc
@@ -9,8 +9,4 @@
 
 export namespace std {
   using std::initializer_list;
-
-  // [support.initlist.range], initializer list range access
-  using std::begin;
-  using std::end;
 } // namespace std
diff --git a/libcxx/modules/std/valarray.inc b/libcxx/modules/std/valarray.inc
index 1cdf7f14060fb..5db318262a67a 100644
--- a/libcxx/modules/std/valarray.inc
+++ b/libcxx/modules/std/valarray.inc
@@ -62,7 +62,4 @@ export namespace std {
   using std::sqrt;
   using std::tan;
   using std::tanh;
-
-  using std::begin;
-  using std::end;
 } // namespace std
diff --git a/libcxx/test/libcxx/language.support/nodiscard.verify.cpp b/libcxx/test/libcxx/language.support/nodiscard.verify.cpp
index b247033516621..b3a6658e53286 100644
--- a/libcxx/test/libcxx/language.support/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/language.support/nodiscard.verify.cpp
@@ -150,7 +150,9 @@ void test() {
   { // <initializer_list>
     std::initializer_list<int> il{94, 82, 49};
 
+    il.data();  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
     il.size();  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+    il.empty(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
     il.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
     il.end();   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   }
diff --git a/libcxx/test/libcxx/numerics/numarray/nodiscard.verify.cpp b/libcxx/test/libcxx/numerics/numarray/nodiscard.verify.cpp
index 84e810f6dfe25..5bdfefdd8a22b 100644
--- a/libcxx/test/libcxx/numerics/numarray/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/numerics/numarray/nodiscard.verify.cpp
@@ -53,6 +53,11 @@ void test() {
   cva[std::move(sva)]; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 #endif
 
+  va.begin();  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cva.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  va.end();    // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cva.end();   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
   va.size(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
   va.sum(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -88,9 +93,4 @@ void test() {
   std::sqrt(va);      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::tan(va);       // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::tanh(va);      // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-
-  std::begin(va);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::begin(cva); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::end(va);    // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::end(cva);   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 }
diff --git a/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp b/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp
index 5eed12d19c072..cacc7d6109149 100644
--- a/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp
+++ b/libcxx/test/libcxx/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp
@@ -11,6 +11,8 @@
 // template <class T> constexpr T* __to_address(T* p) noexcept;
 // template <class Ptr> constexpr auto __to_address(const Ptr& p) noexcept;
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 #include <memory>
 
 #include <array>
@@ -35,8 +37,8 @@ void test_container_iterators(C c)
 void test_valarray_iterators()
 {
     std::valarray<int> v(100);
-    int *p = std::__to_address(std::begin(v));
-    int *q = std::__to_address(std::end(v));
+    int* p = std::__to_address(v.begin());
+    int* q = std::__to_address(v.end());
     assert(q - p == 100);
 }
 
diff --git a/libcxx/test/std/iterators/iterator.container/data.pass.cpp b/libcxx/test/std/iterators/iterator.container/data.pass.cpp
index 1a6af4a95054d..b25587163f3c0 100644
--- a/libcxx/test/std/iterators/iterator.container/data.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.container/data.pass.cpp
@@ -12,7 +12,6 @@
 // template <class C> constexpr auto data(C& c) -> decltype(c.data());               // C++17
 // template <class C> constexpr auto data(const C& c) -> decltype(c.data());         // C++17
 // template <class T, size_t N> constexpr T* data(T (&array)[N]) noexcept;           // C++17
-// template <class E> constexpr const E* data(initializer_list<E> il) noexcept;      // C++17
 
 #include <iterator>
 #include <cassert>
@@ -29,8 +28,8 @@
 template<typename C>
 void test_const_container( const C& c )
 {
-//  Can't say noexcept here because the container might not be
-    assert ( std::data(c)   == c.data());
+  static_assert(noexcept(std::data(c)) == noexcept(c.data()));
+  assert(std::data(c) == c.data());
 }
 
 template<typename T>
@@ -43,8 +42,8 @@ void test_const_container( const std::initializer_list<T>& c )
 template<typename C>
 void test_container( C& c )
 {
-//  Can't say noexcept here because the container might not be
-    assert ( std::data(c)   == c.data());
+  static_assert(noexcept(std::data(c)) == noexcept(c.data()));
+  assert(std::data(c) == c.data());
 }
 
 template<typename T>
diff --git a/libcxx/test/std/iterators/iterator.container/empty.pass.cpp b/libcxx/test/std/iterators/iterator.container/empty.pass.cpp
index 1a00e88d22b15..2593ba3b0e56e 100644
--- a/libcxx/test/std/iterators/iterator.container/empty.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.container/empty.pass.cpp
@@ -11,7 +11,6 @@
 // <iterator>
 // template <class C> constexpr auto empty(const C& c) -> decltype(c.empty());       // C++17
 // template <class T, size_t N> constexpr bool empty(const T (&array)[N]) noexcept;  // C++17
-// template <class E> constexpr bool empty(initializer_list<E> il) noexcept;         // C++17
 
 #include <iterator>
 #include <cassert>
@@ -29,8 +28,8 @@
 template<typename C>
 void test_const_container( const C& c )
 {
-//  Can't say noexcept here because the container might not be
-    assert ( std::empty(c)   == c.empty());
+  static_assert(noexcept(std::empty(c)) == noexcept(c.empty()));
+  assert(std::empty(c) == c.empty());
 }
 
 template<typename T>
@@ -42,8 +41,8 @@ void test_const_container( const std::initializer_list<T>& c )
 template<typename C>
 void test_container( C& c )
 {
-//  Can't say noexcept here because the container might not be
-    assert ( std::empty(c)   == c.empty());
+  static_assert(noexcept(std::empty(c)) == noexcept(c.empty()));
+  assert(std::empty(c) == c.empty());
 }
 
 template<typename T>
diff --git a/libcxx/test/std/iterators/iterator.container/size.pass.cpp b/libcxx/test/std/iterators/iterator.container/size.pass.cpp
index 975d3e80c2b78..460bf256bff42 100644
--- a/libcxx/test/std/iterators/iterator.container/size.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.container/size.pass.cpp
@@ -29,29 +29,29 @@
 template<typename C>
 void test_container( C& c)
 {
-//  Can't say noexcept here because the container might not be
-    assert ( std::size(c)   == c.size());
+  static_assert(noexcept(std::size(c)) == noexcept(c.size()));
+  assert(std::size(c) == c.size());
 }
 
 template<typename C>
 void test_const_container( const C& c )
 {
-//  Can't say noexcept here because the container might not be
-    assert ( std::size(c)   == c.size());
+  static_assert(noexcept(std::size(c)) == noexcept(c.size()));
+  assert(std::size(c) == c.size());
 }
 
 template<typename T>
 void test_const_container( const std::initializer_list<T>& c)
 {
-    LIBCPP_ASSERT_NOEXCEPT(std::size(c)); // our std::size is conditionally noexcept
-    assert ( std::size(c)   == c.size());
+  ASSERT_NOEXCEPT(std::size(c));
+  assert(std::size(c) == c.size());
 }
 
 template<typename T>
 void test_container( std::initializer_list<T>& c )
 {
-    LIBCPP_ASSERT_NOEXCEPT(std::size(c)); // our std::size is conditionally noexcept
-    assert ( std::size(c)   == c.size());
+  ASSERT_NOEXCEPT(std::size(c));
+  assert(std::size(c) == c.size());
 }
 
 template<typename T, std::size_t Sz>
diff --git a/libcxx/test/std/iterators/iterator.container/ssize.pass.cpp b/libcxx/test/std/iterators/iterator.container/ssize.pass.cpp
index e7531aec12b24..7ac403a0d00b5 100644
--- a/libcxx/test/std/iterators/iterator.container/ssize.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.container/ssize.pass.cpp
@@ -36,33 +36,33 @@ struct short_container {
 template<typename C>
 void test_container(C& c)
 {
-//  Can't say noexcept here because the container might not be
-    static_assert( std::is_signed_v<decltype(std::ssize(c))>, "");
-    assert ( std::ssize(c)   == static_cast<decltype(std::ssize(c))>(c.size()));
+  static_assert(noexcept(std::ssize(c)) == noexcept(c.size()));
+  static_assert(std::is_signed_v<decltype(std::ssize(c))>, "");
+  assert(std::ssize(c) == static_cast<decltype(std::ssize(c))>(c.size()));
 }
 
 template<typename C>
 void test_const_container(const C& c)
 {
-//  Can't say noexcept here because the container might not be
-    static_assert( std::is_signed_v<decltype(std::ssize(c))>, "");
-    assert ( std::ssize(c)   == static_cast<decltype(std::ssize(c))>(c.size()));
+  static_assert(noexcept(std::ssize(c)) == noexcept(c.size()));
+  static_assert(std::is_signed_v<decltype(std::ssize(c))>, "");
+  assert(std::ssize(c) == static_cast<decltype(std::ssize(c))>(c.size()));
 }
 
 template<typename T>
 void test_const_container(const std::initializer_list<T>& c)
 {
-    LIBCPP_ASSERT_NOEXCEPT(std::ssize(c)); // our std::ssize is conditionally noexcept
-    static_assert( std::is_signed_v<decltype(std::ssize(c))>, "");
-    assert ( std::ssize(c)   == static_cast<decltype(std::ssize(c))>(c.size()));
+  ASSERT_NOEXCEPT(std::ssize(c));
+  static_assert(std::is_signed_v<decltype(std::ssize(c))>, "");
+  assert(std::ssize(c) == static_cast<decltype(std::ssize(c))>(c.size()));
 }
 
 template<typename T>
 void test_container(std::initializer_list<T>& c)
 {
-    LIBCPP_ASSERT_NOEXCEPT(std::ssize(c)); // our std::ssize is conditionally noexcept
-    static_assert( std::is_signed_v<decltype(std::ssize(c))>, "");
-    assert ( std::ssize(c)   == static_cast<decltype(std::ssize(c))>(c.size()));
+  ASSERT_NOEXCEPT(std::ssize(c));
+  static_assert(std::is_signed_v<decltype(std::ssize(c))>, "");
+  assert(std::ssize(c) == static_cast<decltype(std::ssize(c))>(c.size()));
 }
 
 template<typename T, std::size_t Sz>
@@ -118,7 +118,7 @@ int main(int, char**)
     static_assert( std::numeric_limits<                   decltype(std::ssize(sc))>::max()  > 60000, "");
     static_assert( std::numeric_limits<std::make_signed_t<decltype(std:: size(sc))>>::max() < 60000, "");
     assert (std::ssize(sc) == 60000);
-    LIBCPP_ASSERT_NOT_NOEXCEPT(std::ssize(sc));
+    ASSERT_NOT_NOEXCEPT(std::ssize(sc));
 
-  return 0;
+    return 0;
 }
diff --git a/libcxx/test/std/iterators/iterator.range/begin-end.array.pass.cpp b/libcxx/test/std/iterators/iterator.range/begin-end.array.pass.cpp
index 5161069ec8667..7123e59c5c8a6 100644
--- a/libcxx/test/std/iterators/iterator.range/begin-end.array.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.range/begin-end.array.pass.cpp
@@ -13,8 +13,10 @@
 // template <class T, size_t N> constexpr T* begin(T (&array)[N]) noexcept;
 // template <class T, size_t N> constexpr T* end(T (&array)[N]) noexcept;
 //
-// template <class T, size_t N> constexpr reverse_iterator<T*> rbegin(T (&array)[N]);      // C++14, constexpr since C++17
-// template <class T, size_t N> constexpr reverse_iterator<T*> rend(T (&array)[N]);        // C++14, constexpr since C++17
+// template <class T, size_t N>
+// constexpr reverse_iterator<T*> rbegin(T (&array)[N]) noexcept; // C++14, constexpr since C++17
+// template <class T, size_t N>
+// constexpr reverse_iterator<T*> rend(T (&array)[N]) noexcept;   // C++14, constexpr since C++17
 
 #include <cassert>
 #include <iterator>
@@ -55,16 +57,20 @@ TEST_CONSTEXPR_CXX17 bool test_r() {
 
   // std::rbegin(T (&)[N]) / std::rend(T (&)[N])
   {
+    ASSERT_NOEXCEPT(std::rbegin(a));
     ASSERT_SAME_TYPE(decltype(std::rbegin(a)), std::reverse_iterator<int*>);
     assert(std::rbegin(a).base() == a + 3);
 
+    ASSERT_NOEXCEPT(std::rend(a));
     ASSERT_SAME_TYPE(decltype(std::rend(a)), std::reverse_iterator<int*>);
     assert(std::rend(a).base() == a);
 
     // kind of overkill since it follows from the definition, but worth testing
+    ASSERT_NOEXCEPT(std::rbegin(ca));
     ASSERT_SAME_TYPE(decltype(std::rbegin(ca)), std::reverse_iterator<const int*>);
     assert(std::rbegin(ca).base() == ca + 3);
 
+    ASSERT_NOEXCEPT(std::rend(ca));
     ASSERT_SAME_TYPE(decltype(std::rend(ca)), std::reverse_iterator<const int*>);
     assert(std::rend(ca).base() == ca);
   }
diff --git a/libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp b/libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp
index b795140c2e66a..d9555483d1f92 100644
--- a/libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp
@@ -9,20 +9,25 @@
 // UNSUPPORTED: c++03
 
 // <iterator>
-//
-// template <class C> constexpr auto begin(C& c) -> decltype(c.begin());                   // constexpr since C++17
-// template <class C> constexpr auto begin(const C& c) -> decltype(c.begin());             // constexpr since C++17
-// template <class C> constexpr auto end(C& c) -> decltype(c.end());                       // constexpr since C++17
-// template <class C> constexpr auto end(const C& c) -> decltype(c.end());                 // constexpr since C++17
+// template <class C> constexpr auto begin(C& c) noexcept(see-below) -> decltype(c.begin());       // constexpr since C++17
+// template <class C> constexpr auto begin(const C& c) noexcept(see-below) -> decltype(c.begin()); // constexpr since C++17
+// template <class C> constexpr auto end(C& c) noexcept(see-below) -> decltype(c.end());           // constexpr since C++17
+// template <class C> constexpr auto end(const C& c) noexcept(see-below) -> decltype(c.end());     // constexpr since C++17
 //
 // template <class C> constexpr auto cbegin(const C& c) noexcept(see-below) -> decltype(std::begin(c)); // C++14
 // template <class C> constexpr auto cend(const C& c) noexcept(see-below) -> decltype(std::end(c));     // C++14
-// template <class C> constexpr auto rbegin(C& c) -> decltype(c.rbegin());                 // C++14, constexpr since C++17
-// template <class C> constexpr auto rbegin(const C& c) -> decltype(c.rbegin());           // C++14, constexpr since C++17
-// template <class C> constexpr auto rend(C& c) -> decltype(c.rend());                     // C++14, constexpr since C++17
-// template <class C> constexpr auto rend(const C& c) -> decltype(c.rend());               // C++14, constexpr since C++17
-// template <class C> constexpr auto crbegin(const C& c) -> decltype(std::rbegin(c));      // C++14, constexpr since C++17
-// template <class C> constexpr auto crend(const C& c) -> decltype(std::rend(c));          // C++14, constexpr since C++17
+// template <class C>
+// constexpr auto rbegin(C& c) noexcept(see-below) -> decltype(c.rbegin());            // C++14, constexpr since C++17
+// template <class C>
+// constexpr auto rbegin(const C& c) noexcept(see-below) -> decltype(c.rbegin());      // C++14, constexpr since C++17
+// template <class C>
+// constexpr auto rend(C& c) noexcept(see-below) -> decltype(c.rend());                // C++14, constexpr since C++17
+// template <class C>
+// constexpr auto rend(const C& c) noexcept(see-below) -> decltype(c.rend());          // C++14, constexpr since C++17
+// template <class C>
+// constexpr auto crbegin(const C& c) noexcept(see-below) -> decltype(std::rbegin(c)); // C++14, constexpr since C++17
+// template <class C>
+// constexpr auto crend(const C& c) noexcept(see-below) -> decltype(std::rend(c));     // C++14, constexpr since C++17
 
 #include <array>
 #include <cassert>
@@ -44,18 +49,22 @@ TEST_CONSTEXPR_CXX17 bool test() {
   // std::begin(C& c) / std::end(C& c)
   {
     ASSERT_SAME_TYPE(decltype(std::begin(c)), Iterator);
+    static_assert(noexcept(std::begin(c)) == noexcept(c.begin()), "");
     assert(std::begin(c) == c.begin());
 
     ASSERT_SAME_TYPE(decltype(std::end(c)), Iterator);
+    static_assert(noexcept(std::end(c)) == noexcept(c.end()), "");
     assert(std::end(c) == c.end());
   }
 
   // std::begin(C const& c) / std::end(C const& c)
   {
     ASSERT_SAME_TYPE(decltype(std::begin(cc)), ConstIterator);
+    static_assert(noexcept(std::begin(cc)) == noexcept(cc.begin()), "");
     assert(std::begin(cc) == cc.begin());
 
     ASSERT_SAME_TYPE(decltype(std::end(cc)), ConstIterator);
+    static_assert(noexcept(std::end(cc)) == noexcept(cc.end()), "");
     assert(std::end(cc) == cc.end());
   }
 
@@ -83,34 +92,42 @@ TEST_CONSTEXPR_CXX17 bool test() {
   // std::rbegin(C& c) / std::rend(C& c)
   {
     ASSERT_SAME_TYPE(decltype(std::rbegin(c)), ReverseIterator);
+    static_assert(noexcept(std::rbegin(c)) == noexcept(c.rbegin()), "");
     assert(std::rbegin(c) == c.rbegin());
 
     ASSERT_SAME_TYPE(decltype(std::rend(c)), ReverseIterator);
+    static_assert(noexcept(std::rend(c)) == noexcept(c.rend()), "");
     assert(std::rend(c) == c.rend());
   }
 
   // std::rbegin(C const&) / std::rend(C const&)
   {
     ASSERT_SAME_TYPE(decltype(std::rbegin(cc)), ConstReverseIterator);
+    static_assert(noexcept(std::rbegin(cc)) == noexcept(cc.rbegin()), "");
     assert(std::rbegin(cc) == cc.rbegin());
 
     ASSERT_SAME_TYPE(decltype(std::rend(cc)), ConstReverseIterator);
+    static_assert(noexcept(std::rend(cc)) == noexcept(cc.rend()), "");
     assert(std::rend(cc) == cc.rend());
   }
 
   // std::crbegin(C const&) / std::crend(C const&)
   {
     ASSERT_SAME_TYPE(decltype(std::crbegin(cc)), ConstReverseIterator);
+    static_assert(noexcept(std::crbegin(cc)) == noexcept(std::rbegin(cc)), "");
     assert(std::crbegin(cc) == std::rbegin(cc));
 
     ASSERT_SAME_TYPE(decltype(std::crend(cc)), ConstReverseIterator);
+    static_assert(noexcept(std::crbegin(cc)) == noexcept(std::rend(cc)), "");
     assert(std::crend(cc) == std::rend(cc));
 
     // kind of overkill, but whatever
     ASSERT_SAME_TYPE(decltype(std::crbegin(c)), ConstReverseIterator);
+    static_assert(noexcept(std::crbegin(c)) == noexcept(std::rbegin(cc)), "");
     assert(std::crbegin(c) == std::rbegin(cc));
 
     ASSERT_SAME_TYPE(decltype(std::crend(c)), ConstReverseIterator);
+    static_assert(noexcept(std::crbegin(c)) == noexcept(std::rend(cc)), "");
     assert(std::crend(c) == std::rend(cc));
   }
 #endif // TEST_STD_VER >= 14
@@ -118,13 +135,149 @@ TEST_CONSTEXPR_CXX17 bool test() {
   return true;
 }
 
+TEST_CONSTEXPR_CXX17 bool test_ilist() {
+  // unqualified begin, etc.
+  {
+    std::initializer_list<int> il = {3, 2, 1};
+    ASSERT_NOEXCEPT(begin(il));
+    ASSERT_NOEXCEPT(end(il));
+#if TEST_STD_VER >= 14
+    ASSERT_NOEXCEPT(cbegin(il));
+    ASSERT_NOEXCEPT(cend(il));
+    ASSERT_NOEXCEPT(rbegin(il));
+    ASSERT_NOEXCEPT(rend(il));
+    ASSERT_NOEXCEPT(crbegin(il));
+    ASSERT_NOEXCEPT(crend(il));
+#endif
+    ASSERT_SAME_TYPE(decltype(begin(il)), const int*);
+    ASSERT_SAME_TYPE(decltype(end(il)), const int*);
+    const int* b = begin(il);
+    const int* e = end(il);
+    assert(il.size() == 3);
+    assert(static_cast<std::size_t>(e - b) == il.size());
+    assert(*b++ == 3);
+    assert(*b++ == 2);
+    assert(*b++ == 1);
+#if TEST_STD_VER >= 14
+    ASSERT_SAME_TYPE(decltype(cbegin(il)), const int*);
+    ASSERT_SAME_TYPE(decltype(cend(il)), const int*);
+    ASSERT_SAME_TYPE(decltype(rbegin(il)), std::reverse_iterator<const int*>);
+    ASSERT_SAME_TYPE(decltype(rend(il)), std::reverse_iterator<const int*>);
+    ASSERT_SAME_TYPE(decltype(crbegin(il)), std::reverse_iterator<const int*>);
+    ASSERT_SAME_TYPE(decltype(crend(il)), std::reverse_iterator<const int*>);
+
+    assert(cbegin(il) == begin(il));
+    assert(end(il) == end(il));
+    assert(rbegin(il) == std::reverse_iterator<const int*>(end(il)));
+    assert(rend(il) == std::reverse_iterator<const int*>(begin(il)));
+    assert(crbegin(il) == std::reverse_iterator<const int*>(end(il)));
+    assert(crend(il) == std::reverse_iterator<const int*>(begin(il)));
+
+    auto rb = rbegin(il);
+    auto re = rend(il);
+    assert(static_cast<std::size_t>(re - rb) == il.size());
+    assert(*rb++ == 1);
+    assert(*rb++ == 2);
+    assert(*rb++ == 3);
+#endif
+  }
+
+  // qualified begin, etc.
+  {
+    std::initializer_list<int> il = {1, 2, 3};
+    ASSERT_NOEXCEPT(std::begin(il));
+    ASSERT_NOEXCEPT(std::end(il));
+#if TEST_STD_VER >= 14
+    ASSERT_NOEXCEPT(std::cbegin(il));
+    ASSERT_NOEXCEPT(std::cend(il));
+    ASSERT_NOEXCEPT(std::rbegin(il));
+    ASSERT_NOEXCEPT(std::rend(il));
+    ASSERT_NOEXCEPT(std::crbegin(il));
+    ASSERT_NOEXCEPT(std::crend(il));
+#endif
+    ASSERT_SAME_TYPE(decltype(std::begin(il)), const int*);
+    ASSERT_SAME_TYPE(decltype(std::end(il)), const int*);
+    assert(std::begin(il) == il.begin());
+    assert(std::end(il) == il.end());
+#if TEST_STD_VER >= 14
+    ASSERT_SAME_TYPE(decltype(std::cbegin(il)), const int*);
+    ASSERT_SAME_TYPE(decltype(std::cend(il)), const int*);
+    ASSERT_SAME_TYPE(decltype(std::rbegin(il)), std::reverse_iterator<const int*>);
+    ASSERT_SAME_TYPE(decltype(std::rend(il)), std::reverse_iterator<const int*>);
+    ASSERT_SAME_TYPE(decltype(std::crbegin(il)), std::reverse_iterator<const int*>);
+    ASSERT_SAME_TYPE(decltype(std::crend(il)), std::reverse_iterator<const int*>);
+
+    assert(std::cbegin(il) == std::begin(il));
+    assert(std::end(il) == std::end(il));
+    assert(std::rbegin(il) == std::reverse_iterator<const int*>(std::end(il)));
+    assert(std::rend(il) == std::reverse_iterator<const int*>(std::begin(il)));
+    assert(std::crbegin(il) == std::reverse_iterator<const int*>(std::end(il)));
+    assert(std::crend(il) == std::reverse_iterator<const int*>(std::begin(il)));
+
+    {
+      auto rb = std::rbegin(il);
+      auto re = std::rend(il);
+      assert(static_cast<std::size_t>(re - rb) == il.size());
+      assert(*rb++ == 3);
+      assert(*rb++ == 2);
+      assert(*rb++ == 1);
+    }
+#endif
+
+    const auto& cil = il;
+    ASSERT_NOEXCEPT(std::begin(cil));
+    ASSERT_NOEXCEPT(std::end(cil));
+#if TEST_STD_VER >= 14
+    ASSERT_NOEXCEPT(std::cbegin(cil));
+    ASSERT_NOEXCEPT(std::cend(cil));
+    ASSERT_NOEXCEPT(std::rbegin(cil));
+    ASSERT_NOEXCEPT(std::rend(cil));
+    ASSERT_NOEXCEPT(std::crbegin(cil));
+    ASSERT_NOEXCEPT(std::crend(cil));
+#endif
+    ASSERT_SAME_TYPE(decltype(std::begin(cil)), const int*);
+    ASSERT_SAME_TYPE(decltype(std::end(cil)), const int*);
+    assert(std::begin(cil) == il.begin());
+    assert(std::end(cil) == il.end());
+#if TEST_STD_VER >= 14
+    ASSERT_SAME_TYPE(decltype(std::cbegin(cil)), const int*);
+    ASSERT_SAME_TYPE(decltype(std::cend(cil)), const int*);
+    ASSERT_SAME_TYPE(decltype(std::rbegin(cil)), std::reverse_iterator<const int*>);
+    ASSERT_SAME_TYPE(decltype(std::rend(cil)), std::reverse_iterator<const int*>);
+    ASSERT_SAME_TYPE(decltype(std::crbegin(cil)), std::reverse_iterator<const int*>);
+    ASSERT_SAME_TYPE(decltype(std::crend(cil)), std::reverse_iterator<const int*>);
+
+    assert(std::cbegin(cil) == std::begin(cil));
+    assert(std::end(cil) == std::end(cil));
+    assert(std::rbegin(cil) == std::reverse_iterator<const int*>(std::end(cil)));
+    assert(std::rend(cil) == std::reverse_iterator<const int*>(std::begin(cil)));
+    assert(std::crbegin(cil) == std::reverse_iterator<const int*>(std::end(cil)));
+    assert(std::crend(cil) == std::reverse_iterator<const int*>(std::begin(cil)));
+
+    {
+      auto rb = std::rbegin(cil);
+      auto re = std::rend(cil);
+      assert(static_cast<std::size_t>(re - rb) == cil.size());
+      assert(*rb++ == 3);
+      assert(*rb++ == 2);
+      assert(*rb++ == 1);
+    }
+#endif
+  }
+
+  return true;
+}
+
 int main(int, char**) {
   test<std::array<int, 3>>();
   test<std::list<int>>();
   test<std::vector<int>>();
 
+  test_ilist();
+
 #if TEST_STD_VER >= 17
   static_assert(test<std::array<int, 3>>());
+  static_assert(test_ilist());
 #endif
 
   // Note: Properly testing the conditional noexcept-ness propagation in std::cbegin and std::cend
@@ -136,10 +289,14 @@ int main(int, char**) {
     auto const& ca = a;
     ASSERT_NOEXCEPT(std::cbegin(ca));
     ASSERT_NOEXCEPT(std::cend(ca));
+    ASSERT_NOEXCEPT(std::crbegin(ca));
+    ASSERT_NOEXCEPT(std::crend(ca));
 
     // kind of overkill, but whatever
     ASSERT_NOEXCEPT(std::cbegin(a));
     ASSERT_NOEXCEPT(std::cend(a));
+    ASSERT_NOEXCEPT(std::crbegin(a));
+    ASSERT_NOEXCEPT(std::crend(a));
   }
 
   // Make sure std::cbegin and std::cend are constexpr in C++14 too (see LWG2280).
diff --git a/libcxx/test/std/iterators/iterator.range/begin-end.initializer_list.pass.cpp b/libcxx/test/std/iterators/iterator.range/begin-end.initializer_list.pass.cpp
index 2df9283b0576d..455aa56b1536a 100644
--- a/libcxx/test/std/iterators/iterator.range/begin-end.initializer_list.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.range/begin-end.initializer_list.pass.cpp
@@ -10,10 +10,10 @@
 
 // <iterator>
 //
-// Note that begin and end are tested in libcxx/test/std/language.support/support.initlist/support.initlist.range/begin_end.pass.cpp
-//
-// template <class E> constexpr reverse_iterator<const E*> rbegin(initializer_list<E> il); // C++14, constexpr since C++17
-// template <class E> constexpr reverse_iterator<const E*> rend(initializer_list<E> il);   // C++14, constexpr since C++17
+// template <class E>
+// constexpr reverse_iterator<const E*> rbegin(initializer_list<E> il) noexcept; // C++14, constexpr since C++17
+// template <class E>
+// constexpr reverse_iterator<const E*> rend(initializer_list<E> il) noexcept;   // C++14, constexpr since C++17
 
 #include <cassert>
 #include <initializer_list>
@@ -23,12 +23,14 @@
 
 TEST_CONSTEXPR_CXX17 bool test() {
   std::initializer_list<int> il = {1, 2, 3};
+  ASSERT_NOEXCEPT(std::rbegin(il));
   ASSERT_SAME_TYPE(decltype(std::rbegin(il)), std::reverse_iterator<const int*>);
   ASSERT_SAME_TYPE(decltype(std::rend(il)), std::reverse_iterator<const int*>);
   assert(std::rbegin(il).base() == il.end());
   assert(std::rend(il).base() == il.begin());
 
   const auto& cil = il;
+  ASSERT_NOEXCEPT(std::rbegin(cil));
   ASSERT_SAME_TYPE(decltype(std::rbegin(cil)), std::reverse_iterator<const int*>);
   ASSERT_SAME_TYPE(decltype(std::rend(cil)), std::reverse_iterator<const int*>);
   assert(std::rbegin(cil).base() == il.end());
diff --git a/libcxx/test/std/iterators/iterator.range/mandatory_inclusions.gen.py b/libcxx/test/std/iterators/iterator.range/mandatory_inclusions.gen.py
index 043374df1fad1..5d960b3bfb851 100644
--- a/libcxx/test/std/iterators/iterator.range/mandatory_inclusions.gen.py
+++ b/libcxx/test/std/iterators/iterator.range/mandatory_inclusions.gen.py
@@ -9,8 +9,8 @@
 # In addition to being available via inclusion of the <iterator> header,
 # the function templates in [iterator.range] are available when any of the following
 # headers are included: <array>, <deque>, <flat_map>, <flat_set>, <forward_list>,
-# <list>, <map>, <regex>, <set>, <span>, <string>, <string_view>, <unordered_map>,
-# <unordered_set>, <vector>.
+# <list>, <map>, <optional>, <regex>, <set>, <span>, <stacktrace>, <string>,
+# <string_view>, <unordered_map>, <unordered_set>, <valarray>, <vector>.
 
 # UNSUPPORTED: c++03
 
@@ -36,13 +36,16 @@
             "forward_list",
             "list",
             "map",
+            "optional",
             "regex",
             "set",
             "span",
+            # "stacktrace", # unimplemented
             "string",
             "string_view",
             "unordered_map",
             "unordered_set",
+            "valarray",
             "vector",
         ],
     )
diff --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cons/ctor.iter.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cons/ctor.iter.pass.cpp
index 72e77b0856421..2de83dddb058a 100644
--- a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cons/ctor.iter.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cons/ctor.iter.pass.cpp
@@ -14,14 +14,22 @@
 
 #include <iterator>
 #include <cassert>
+#include <type_traits>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
 template <class It>
 TEST_CONSTEXPR_CXX17 void test(It i) {
-    std::reverse_iterator<It> r(i);
-    assert(r.base() == i);
+#if TEST_STD_VER >= 11
+  // Verify that this constructor is conditionally noexcept in libc++.
+  // Because many (un)conditionally noexcept functions construct the returned reverse_iterator by this constructor.
+  LIBCPP_STATIC_ASSERT(std::is_nothrow_constructible<std::reverse_iterator<It>, It&>::value ==
+                           std::is_nothrow_constructible<It, It&>::value,
+                       "");
+#endif
+  std::reverse_iterator<It> r(i);
+  assert(r.base() == i);
 }
 
 TEST_CONSTEXPR_CXX17 bool tests() {
diff --git a/libcxx/test/std/language.support/support.initlist/support.initlist.access/access.pass.cpp b/libcxx/test/std/language.support/support.initlist/support.initlist.access/access.pass.cpp
index e6437d5fc0d74..103d4453f2e5b 100644
--- a/libcxx/test/std/language.support/support.initlist/support.initlist.access/access.pass.cpp
+++ b/libcxx/test/std/language.support/support.initlist/support.initlist.access/access.pass.cpp
@@ -10,9 +10,11 @@
 
 // template<class E> class initializer_list;
 
-// const E* begin() const;
-// const E* end() const;
-// size_t size() const;
+// const E* begin() const noexcept; // constexpr since C++14
+// const E* end() const noexcept;   // constexpr since C++14
+// const E* data() const noexcept;  // constexpr since C++14
+// size_t size() const noexcept;    // constexpr since C++14
+// bool empty() const noexcept;     // constexpr since C++14
 
 #include <initializer_list>
 #include <cassert>
@@ -20,43 +22,78 @@
 
 #include "test_macros.h"
 
+ASSERT_SAME_TYPE(decltype(std::initializer_list<int>{}.begin()), const int*);
+ASSERT_SAME_TYPE(decltype(std::initializer_list<int>{}.end()), const int*);
+ASSERT_SAME_TYPE(decltype(std::initializer_list<int>{}.data()), const int*);
+ASSERT_SAME_TYPE(decltype(std::initializer_list<int>{}.size()), std::size_t);
+ASSERT_SAME_TYPE(decltype(std::initializer_list<int>{}.empty()), bool);
+
+ASSERT_NOEXCEPT(std::initializer_list<int>{}.begin());
+ASSERT_NOEXCEPT(std::initializer_list<int>{}.end());
+ASSERT_NOEXCEPT(std::initializer_list<int>{}.data());
+ASSERT_NOEXCEPT(std::initializer_list<int>{}.size());
+ASSERT_NOEXCEPT(std::initializer_list<int>{}.empty());
+
 struct A
 {
-    A(std::initializer_list<int> il)
+  TEST_CONSTEXPR_CXX14 A(std::initializer_list<int> il) {
     {
-        const int* b = il.begin();
-        const int* e = il.end();
-        assert(il.size() == 3);
-        assert(static_cast<std::size_t>(e - b) == il.size());
-        assert(*b++ == 3);
-        assert(*b++ == 2);
-        assert(*b++ == 1);
+      const int* b = il.begin();
+      const int* e = il.end();
+      assert(il.data() == b);
+      assert(il.size() == 3);
+      assert(!il.empty());
+      assert(static_cast<std::size_t>(e - b) == il.size());
+      assert(*b++ == 3);
+      assert(*b++ == 2);
+      assert(*b++ == 1);
     }
-};
-
-#if TEST_STD_VER > 11
-struct B
-{
-    constexpr B(std::initializer_list<int> il)
     {
-        const int* b = il.begin();
-        const int* e = il.end();
-        assert(il.size() == 3);
-        assert(static_cast<std::size_t>(e - b) == il.size());
-        assert(*b++ == 3);
-        assert(*b++ == 2);
-        assert(*b++ == 1);
+      const auto cil = il;
+      const int* b   = cil.begin();
+      const int* e   = cil.end();
+      assert(cil.data() == b);
+      assert(cil.size() == 3);
+      assert(!cil.empty());
+      assert(static_cast<std::size_t>(e - b) == cil.size());
+      assert(*b++ == 3);
+      assert(*b++ == 2);
+      assert(*b++ == 1);
     }
+  }
 };
 
-#endif // TEST_STD_VER > 11
+TEST_CONSTEXPR_CXX14 bool test_empty_ilist() {
+  {
+    std::initializer_list<int> il{};
+    const int* b = il.begin();
+    const int* e = il.end();
+    assert(il.data() == b);
+    assert(il.size() == 0);
+    assert(il.empty());
+    assert(static_cast<std::size_t>(e - b) == il.size());
+  }
+  {
+    const std::initializer_list<int> cil{};
+    const int* b = cil.begin();
+    const int* e = cil.end();
+    assert(cil.data() == b);
+    assert(cil.size() == 0);
+    assert(cil.empty());
+    assert(static_cast<std::size_t>(e - b) == cil.size());
+  }
+
+  return true;
+}
 
 int main(int, char**)
 {
     A test1 = {3, 2, 1}; (void)test1;
+    test_empty_ilist();
 #if TEST_STD_VER > 11
-    constexpr B test2 = {3, 2, 1};
+    constexpr A test2 = {3, 2, 1};
     (void)test2;
+    static_assert(test_empty_ilist(), "");
 #endif // TEST_STD_VER > 11
 
   return 0;
diff --git a/libcxx/test/std/language.support/support.initlist/support.initlist.range/begin_end.pass.cpp b/libcxx/test/std/language.support/support.initlist/support.initlist.range/begin_end.pass.cpp
deleted file mode 100644
index b15a4586dda61..0000000000000
--- a/libcxx/test/std/language.support/support.initlist/support.initlist.range/begin_end.pass.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// <initializer_list>
-
-// template<class E> constexpr const E* begin(initializer_list<E> il) noexcept; // constexpr since C++14
-// template<class E> constexpr const E* end(initializer_list<E> il) noexcept;   // constexpr since C++14
-
-#include <initializer_list>
-#include <cassert>
-#include <cstddef>
-
-#include "test_macros.h"
-
-TEST_CONSTEXPR_CXX14 bool test() {
-  // unqualified begin/end
-  {
-    std::initializer_list<int> il = {3, 2, 1};
-    ASSERT_NOEXCEPT(begin(il));
-    ASSERT_NOEXCEPT(end(il));
-    ASSERT_SAME_TYPE(decltype(begin(il)), const int*);
-    ASSERT_SAME_TYPE(decltype(end(il)), const int*);
-    const int* b = begin(il);
-    const int* e = end(il);
-    assert(il.size() == 3);
-    assert(static_cast<std::size_t>(e - b) == il.size());
-    assert(*b++ == 3);
-    assert(*b++ == 2);
-    assert(*b++ == 1);
-  }
-
-  // qualified begin/end
-  {
-    std::initializer_list<int> il = {1, 2, 3};
-    ASSERT_NOEXCEPT(std::begin(il));
-    ASSERT_NOEXCEPT(std::end(il));
-    ASSERT_SAME_TYPE(decltype(std::begin(il)), const int*);
-    ASSERT_SAME_TYPE(decltype(std::end(il)), const int*);
-    assert(std::begin(il) == il.begin());
-    assert(std::end(il) == il.end());
-
-    const auto& cil = il;
-    ASSERT_NOEXCEPT(std::begin(cil));
-    ASSERT_NOEXCEPT(std::end(cil));
-    ASSERT_SAME_TYPE(decltype(std::begin(cil)), const int*);
-    ASSERT_SAME_TYPE(decltype(std::end(cil)), const int*);
-    assert(std::begin(cil) == il.begin());
-    assert(std::end(cil) == il.end());
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-#if TEST_STD_VER >= 14
-  static_assert(test(), "");
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/initializer_list.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/initializer_list.version.compile.pass.cpp
new file mode 100644
index 0000000000000..74421975c138a
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/initializer_list.version.compile.pass.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <initializer_list>
+
+// Test the feature test macros defined by <initializer_list>
+
+// clang-format off
+
+#include <initializer_list>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_initializer_list
+#    error "__cpp_lib_initializer_list should not be defined before c++14"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifndef __cpp_lib_initializer_list
+#    error "__cpp_lib_initializer_list should be defined in c++14"
+#  endif
+#  if __cpp_lib_initializer_list != 202511L
+#    error "__cpp_lib_initializer_list should have the value 202511L in c++14"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifndef __cpp_lib_initializer_list
+#    error "__cpp_lib_initializer_list should be defined in c++17"
+#  endif
+#  if __cpp_lib_initializer_list != 202511L
+#    error "__cpp_lib_initializer_list should have the value 202511L in c++17"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifndef __cpp_lib_initializer_list
+#    error "__cpp_lib_initializer_list should be defined in c++20"
+#  endif
+#  if __cpp_lib_initializer_list != 202511L
+#    error "__cpp_lib_initializer_list should have the value 202511L in c++20"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifndef __cpp_lib_initializer_list
+#    error "__cpp_lib_initializer_list should be defined in c++23"
+#  endif
+#  if __cpp_lib_initializer_list != 202511L
+#    error "__cpp_lib_initializer_list should have the value 202511L in c++23"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  ifndef __cpp_lib_initializer_list
+#    error "__cpp_lib_initializer_list should be defined in c++26"
+#  endif
+#  if __cpp_lib_initializer_list != 202511L
+#    error "__cpp_lib_initializer_list should have the value 202511L in c++26"
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/valarray.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/valarray.version.compile.pass.cpp
new file mode 100644
index 0000000000000..9f362141491f3
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/valarray.version.compile.pass.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <valarray>
+
+// Test the feature test macros defined by <valarray>
+
+// clang-format off
+
+#include <valarray>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_valarray
+#    error "__cpp_lib_valarray should not be defined before c++14"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifndef __cpp_lib_valarray
+#    error "__cpp_lib_valarray should be defined in c++14"
+#  endif
+#  if __cpp_lib_valarray != 202511L
+#    error "__cpp_lib_valarray should have the value 202511L in c++14"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifndef __cpp_lib_valarray
+#    error "__cpp_lib_valarray should be defined in c++17"
+#  endif
+#  if __cpp_lib_valarray != 202511L
+#    error "__cpp_lib_valarray should have the value 202511L in c++17"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifndef __cpp_lib_valarray
+#    error "__cpp_lib_valarray should be defined in c++20"
+#  endif
+#  if __cpp_lib_valarray != 202511L
+#    error "__cpp_lib_valarray should have the value 202511L in c++20"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifndef __cpp_lib_valarray
+#    error "__cpp_lib_valarray should be defined in c++23"
+#  endif
+#  if __cpp_lib_valarray != 202511L
+#    error "__cpp_lib_valarray should have the value 202511L in c++23"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  ifndef __cpp_lib_valarray
+#    error "__cpp_lib_valarray should be defined in c++26"
+#  endif
+#  if __cpp_lib_valarray != 202511L
+#    error "__cpp_lib_valarray should have the value 202511L in c++26"
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
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 f2d74cc99d864..f7e07cf8ec89c 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
@@ -444,6 +444,10 @@
 #    error "__cpp_lib_incomplete_container_elements should not be defined before c++17"
 #  endif
 
+#  ifdef __cpp_lib_initializer_list
+#    error "__cpp_lib_initializer_list should not be defined before c++14"
+#  endif
+
 #  ifdef __cpp_lib_inplace_vector
 #    error "__cpp_lib_inplace_vector should not be defined before c++26"
 #  endif
@@ -944,6 +948,10 @@
 #    error "__cpp_lib_unwrap_ref should not be defined before c++20"
 #  endif
 
+#  ifdef __cpp_lib_valarray
+#    error "__cpp_lib_valarray should not be defined before c++14"
+#  endif
+
 #  ifdef __cpp_lib_variant
 #    error "__cpp_lib_variant should not be defined before c++17"
 #  endif
@@ -1390,6 +1398,13 @@
 #    error "__cpp_lib_incomplete_container_elements should not be defined before c++17"
 #  endif
 
+#  ifndef __cpp_lib_initializer_list
+#    error "__cpp_lib_initializer_list should be defined in c++14"
+#  endif
+#  if __cpp_lib_initializer_list != 202511L
+#    error "__cpp_lib_initializer_list should have the value 202511L in c++14"
+#  endif
+
 #  ifdef __cpp_lib_inplace_vector
 #    error "__cpp_lib_inplace_vector should not be defined before c++26"
 #  endif
@@ -1950,6 +1965,13 @@
 #    error "__cpp_lib_unwrap_ref should not be defined before c++20"
 #  endif
 
+#  ifndef __cpp_lib_valarray
+#    error "__cpp_lib_valarray should be defined in c++14"
+#  endif
+#  if __cpp_lib_valarray != 202511L
+#    error "__cpp_lib_valarray should have the value 202511L in c++14"
+#  endif
+
 #  ifdef __cpp_lib_variant
 #    error "__cpp_lib_variant should not be defined before c++17"
 #  endif
@@ -2468,6 +2490,13 @@
 #    error "__cpp_lib_incomplete_container_elements should have the value 201505L in c++17"
 #  endif
 
+#  ifndef __cpp_lib_initializer_list
+#    error "__cpp_lib_initializer_list should be defined in c++17"
+#  endif
+#  if __cpp_lib_initializer_list != 202511L
+#    error "__cpp_lib_initializer_list should have the value 202511L in c++17"
+#  endif
+
 #  ifdef __cpp_lib_inplace_vector
 #    error "__cpp_lib_inplace_vector should not be defined before c++26"
 #  endif
@@ -3142,6 +3171,13 @@
 #    error "__cpp_lib_unwrap_ref should not be defined before c++20"
 #  endif
 
+#  ifndef __cpp_lib_valarray
+#    error "__cpp_lib_valarray should be defined in c++17"
+#  endif
+#  if __cpp_lib_valarray != 202511L
+#    error "__cpp_lib_valarray should have the value 202511L in c++17"
+#  endif
+
 #  ifndef __cpp_lib_variant
 #    error "__cpp_lib_variant should be defined in c++17"
 #  endif
@@ -3804,6 +3840,13 @@
 #    error "__cpp_lib_incomplete_container_elements should have the value 201505L in c++20"
 #  endif
 
+#  ifndef __cpp_lib_initializer_list
+#    error "__cpp_lib_initializer_list should be defined in c++20"
+#  endif
+#  if __cpp_lib_initializer_list != 202511L
+#    error "__cpp_lib_initializer_list should have the value 202511L in c++20"
+#  endif
+
 #  ifdef __cpp_lib_inplace_vector
 #    error "__cpp_lib_inplace_vector should not be defined before c++26"
 #  endif
@@ -4604,6 +4647,13 @@
 #    error "__cpp_lib_unwrap_ref should have the value 201811L in c++20"
 #  endif
 
+#  ifndef __cpp_lib_valarray
+#    error "__cpp_lib_valarray should be defined in c++20"
+#  endif
+#  if __cpp_lib_valarray != 202511L
+#    error "__cpp_lib_valarray should have the value 202511L in c++20"
+#  endif
+
 #  ifndef __cpp_lib_variant
 #    error "__cpp_lib_variant should be defined in c++20"
 #  endif
@@ -5332,6 +5382,13 @@
 #    error "__cpp_lib_incomplete_container_elements should have the value 201505L in c++23"
 #  endif
 
+#  ifndef __cpp_lib_initializer_list
+#    error "__cpp_lib_initializer_list should be defined in c++23"
+#  endif
+#  if __cpp_lib_initializer_list != 202511L
+#    error "__cpp_lib_initializer_list should have the value 202511L in c++23"
+#  endif
+
 #  ifdef __cpp_lib_inplace_vector
 #    error "__cpp_lib_inplace_vector should not be defined before c++26"
 #  endif
@@ -6294,6 +6351,13 @@
 #    error "__cpp_lib_unwrap_ref should have the value 201811L in c++23"
 #  endif
 
+#  ifndef __cpp_lib_valarray
+#    error "__cpp_lib_valarray should be defined in c++23"
+#  endif
+#  if __cpp_lib_valarray != 202511L
+#    error "__cpp_lib_valarray should have the value 202511L in c++23"
+#  endif
+
 #  ifndef __cpp_lib_variant
 #    error "__cpp_lib_variant should be defined in c++23"
 #  endif
@@ -7226,6 +7290,13 @@
 #    error "__cpp_lib_incomplete_container_elements should have the value 201505L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_initializer_list
+#    error "__cpp_lib_initializer_list should be defined in c++26"
+#  endif
+#  if __cpp_lib_initializer_list != 202511L
+#    error "__cpp_lib_initializer_list should have the value 202511L in c++26"
+#  endif
+
 #  if !defined(_LIBCPP_VERSION)
 #    ifndef __cpp_lib_inplace_vector
 #      error "__cpp_lib_inplace_vector should be defined in c++26"
@@ -8317,6 +8388,13 @@
 #    error "__cpp_lib_unwrap_ref should have the value 201811L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_valarray
+#    error "__cpp_lib_valarray should be defined in c++26"
+#  endif
+#  if __cpp_lib_valarray != 202511L
+#    error "__cpp_lib_valarray should have the value 202511L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_variant
 #    error "__cpp_lib_variant should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/numerics/numarray/template.valarray/types.compile.pass.cpp b/libcxx/test/std/numerics/numarray/template.valarray/types.compile.pass.cpp
new file mode 100644
index 0000000000000..75dc557e2bf39
--- /dev/null
+++ b/libcxx/test/std/numerics/numarray/template.valarray/types.compile.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <valarray>
+
+// template<class T>
+// class valarray
+// {
+// public:
+//     typedef T value_type;
+//     typedef unspecified iterator;
+//     typedef unspecified const_iterator;
+//     ...
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <valarray>
+#include <iterator>
+#include <type_traits>
+
+#include "test_macros.h"
+
+template <class T>
+void test() {
+  using It  = typename std::valarray<T>::iterator;
+  using CIt = typename std::valarray<T>::const_iterator;
+
+  ASSERT_SAME_TYPE(typename std::valarray<T>::value_type, T);
+  static_assert(
+      std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<It>::iterator_category>::value,
+      "");
+  static_assert(
+      std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<CIt>::iterator_category>::value,
+      "");
+
+  ASSERT_SAME_TYPE(decltype(*It()), T&);
+  ASSERT_SAME_TYPE(decltype(*CIt()), const T&);
+
+#if TEST_STD_VER >= 20
+  static_assert(std::contiguous_iterator<It>);
+  static_assert(std::contiguous_iterator<CIt>);
+#endif
+}
+
+void test() {
+  test<int>();
+  test<double>();
+}
diff --git a/libcxx/test/std/numerics/numarray/template.valarray/types.pass.cpp b/libcxx/test/std/numerics/numarray/template.valarray/types.pass.cpp
deleted file mode 100644
index 3d7e04ab87dd0..0000000000000
--- a/libcxx/test/std/numerics/numarray/template.valarray/types.pass.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <valarray>
-
-// template<class T>
-// class valarray
-// {
-// public:
-//     typedef T value_type;
-//     ...
-
-#include <valarray>
-#include <type_traits>
-
-#include "test_macros.h"
-
-int main(int, char**)
-{
-    static_assert((std::is_same<std::valarray<int>::value_type, int>::value), "");
-    static_assert((std::is_same<std::valarray<double>::value_type, double>::value), "");
-
-  return 0;
-}
diff --git a/libcxx/test/std/numerics/numarray/template.valarray/valarray.members/begin-end.pass.cpp b/libcxx/test/std/numerics/numarray/template.valarray/valarray.members/begin-end.pass.cpp
new file mode 100644
index 0000000000000..2cdbae11a6e1d
--- /dev/null
+++ b/libcxx/test/std/numerics/numarray/template.valarray/valarray.members/begin-end.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <valarray>
+
+// template<class T> class valarray;
+
+// iterator       begin();
+// const_iterator begin() const;
+// iterator       end();
+// const_iterator end() const;
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+#include <valarray>
+#include <cassert>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+template <class T>
+void test_return_types() {
+  typedef typename std::valarray<T>::iterator It;
+  typedef typename std::valarray<T>::const_iterator CIt;
+
+  ASSERT_SAME_TYPE(decltype(std::declval<std::valarray<T> >().begin()), It);
+  ASSERT_SAME_TYPE(decltype(std::declval<std::valarray<T>&>().begin()), It);
+  ASSERT_SAME_TYPE(decltype(std::declval<std::valarray<T> >().end()), It);
+  ASSERT_SAME_TYPE(decltype(std::declval<std::valarray<T>&>().end()), It);
+
+  ASSERT_SAME_TYPE(decltype(std::declval<const std::valarray<T> >().begin()), CIt);
+  ASSERT_SAME_TYPE(decltype(std::declval<const std::valarray<T>&>().begin()), CIt);
+  ASSERT_SAME_TYPE(decltype(std::declval<const std::valarray<T> >().end()), CIt);
+  ASSERT_SAME_TYPE(decltype(std::declval<const std::valarray<T>&>().end()), CIt);
+}
+
+int main(int, char**) {
+  {
+    int a[] = {1, 2, 3, 4, 5};
+    std::valarray<int> v(a, 5);
+    const std::valarray<int>& cv = v;
+
+    assert(&*v.begin() == &v[0]);
+    assert(&*cv.begin() == &cv[0]);
+    *v.begin() = 10;
+    assert(v[0] == 10);
+
+    assert(&*std::prev(v.end()) == &v[4]);
+    assert(&*std::prev(cv.end()) == &cv[4]);
+  }
+#if TEST_STD_VER >= 11
+  {
+    int a[] = {1, 2, 3, 4, 5};
+    std::valarray<int> v(a, 5);
+    int sum = 0;
+    for (int& i : v) {
+      sum += i;
+    }
+    assert(sum == 15);
+  }
+  {
+    int a[] = {1, 2, 3, 4, 5};
+    const std::valarray<int> cv(a, 5);
+    int sum = 0;
+    for (const int& i : cv) {
+      sum += i;
+    }
+    assert(sum == 15);
+  }
+#endif
+  test_return_types<int>();
+  test_return_types<double>();
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/numarray/valarray.range/begin-end.pass.cpp b/libcxx/test/std/numerics/numarray/valarray.range/begin-end.pass.cpp
deleted file mode 100644
index d5ec017e04a86..0000000000000
--- a/libcxx/test/std/numerics/numarray/valarray.range/begin-end.pass.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <valarray>
-
-// template<class T> class valarray;
-
-// template <class T> unspecified begin(valarray<T>& v);
-// template <class T> unspecified begin(const valarray<T>& v);
-// template <class T> unspecified end(valarray<T>& v);
-// template <class T> unspecified end(const valarray<T>& v);
-
-#include <valarray>
-#include <cassert>
-#include <iterator>
-#include <type_traits>
-
-#include "test_macros.h"
-
-int main(int, char**)
-{
-  {
-    int a[] = {1, 2, 3, 4, 5};
-    std::valarray<int> v(a, 5);
-    const std::valarray<int>& cv = v;
-    using It = decltype(std::begin(v));
-    using CIt = decltype(std::begin(cv));
-    static_assert(std::is_base_of<std::random_access_iterator_tag, std::iterator_traits<It>::iterator_category>::value, "");
-    static_assert(std::is_base_of<std::random_access_iterator_tag, std::iterator_traits<CIt>::iterator_category>::value, "");
-    ASSERT_SAME_TYPE(decltype(*std::begin(v)), int&);
-    ASSERT_SAME_TYPE(decltype(*std::begin(cv)), const int&);
-    assert(&*std::begin(v) == &v[0]);
-    assert(&*std::begin(cv) == &cv[0]);
-    *std::begin(v) = 10;
-    assert(v[0] == 10);
-
-    ASSERT_SAME_TYPE(decltype(std::end(v)), It);
-    ASSERT_SAME_TYPE(decltype(std::end(cv)), CIt);
-    assert(&*std::prev(std::end(v)) == &v[4]);
-    assert(&*std::prev(std::end(cv)) == &cv[4]);
-  }
-#if TEST_STD_VER >= 11
-  {
-    int a[] = {1, 2, 3, 4, 5};
-    std::valarray<int> v(a, 5);
-    int sum = 0;
-    for (int& i : v) {
-      sum += i;
-    }
-    assert(sum == 15);
-  }
-  {
-    int a[] = {1, 2, 3, 4, 5};
-    const std::valarray<int> cv(a, 5);
-    int sum = 0;
-    for (const int& i : cv) {
-      sum += i;
-    }
-    assert(sum == 15);
-  }
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp b/libcxx/test/std/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp
index e0ec3b8d176eb..9382b91b0ce11 100644
--- a/libcxx/test/std/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp
+++ b/libcxx/test/std/utilities/memory/pointer.conversion/to_address_std_iterators.pass.cpp
@@ -37,8 +37,8 @@ void test_container_iterators(C c)
 void test_valarray_iterators()
 {
     std::valarray<int> v(100);
-    int *p = std::to_address(std::begin(v));
-    int *q = std::to_address(std::end(v));
+    int* p = std::to_address(v.begin());
+    int* q = std::to_address(v.end());
     assert(q - p == 100);
 }
 
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..c772323e0dc13 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -762,6 +762,11 @@ def add_version_header(tc):
             "values": {"c++17": 201505},
             "headers": ["forward_list", "list", "vector"],
         },
+        {
+            "name": "__cpp_lib_initializer_list",
+            "values": {"c++14": 202511},
+            "headers": ["initializer_list"],
+        },
         {
             "name": "__cpp_lib_inplace_vector",
             "values": {"c++26": 202406},  # P0843R14 inplace_vector
@@ -1517,6 +1522,11 @@ def add_version_header(tc):
             "values": {"c++20": 201811},
             "headers": ["functional"],
         },
+        {
+            "name": "__cpp_lib_valarray",
+            "values": {"c++14": 202511},
+            "headers": ["valarray"],
+        },
         {
             "name": "__cpp_lib_variant",
             "values": {
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index dbb1d9e040a33..c6bcbe829a571 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -189,6 +189,7 @@ def __hash__(self) -> int:
 # For example, [algorithm.syn] contains "#include <initializer_list>".
 mandatory_inclusions = {
     "algorithm": ["initializer_list"],
+    "any": ["initializer_list", "typeinfo"],
     "array": ["compare", "initializer_list"],
     "bitset": ["iosfwd", "string"],
     "chrono": ["compare"],
@@ -199,10 +200,11 @@ def __hash__(self) -> int:
     "filesystem": ["compare"],
     "flat_map": ["compare", "initializer_list"],
     "flat_set": ["compare", "initializer_list"],
+    "functional": ["initializer_list", "typeinfo"],
     "forward_list": ["compare", "initializer_list"],
     "ios": ["iosfwd"],
     "iostream": ["ios", "istream", "ostream", "streambuf"],
-    "iterator": ["compare", "concepts"],
+    "iterator": ["compare", "concepts", "initializer_list"],
     "list": ["compare", "initializer_list"],
     "map": ["compare", "initializer_list"],
     "memory": ["compare"],
@@ -220,7 +222,7 @@ def __hash__(self) -> int:
     "tgmath.h": ["cmath", "complex"],
     "thread": ["compare"],
     "tuple": ["compare"],
-    "typeindex": ["compare"],
+    "typeindex": ["compare", "typeinfo"],
     "unordered_map": ["compare", "initializer_list"],
     "unordered_set": ["compare", "initializer_list"],
     "utility": ["compare", "initializer_list"],

>From 31501f118a98bbcc9d6ee3dc89dd57d63c7cffcc Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Sun, 4 Jan 2026 09:37:28 +0800
Subject: [PATCH 2/8] Address some of @philnik777's review comments

- Add section labels to standard-mandated includes
- Reorder includes in `<functional>`
- Remove inclusion guard for `<__iterator/reverse_iterator.h>`
- Use `using` for new member types in `valarray`
---
 libcxx/include/any        |  2 ++
 libcxx/include/functional | 10 ++++++----
 libcxx/include/typeindex  |  2 ++
 libcxx/include/valarray   |  9 +++------
 4 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/any b/libcxx/include/any
index d4f4854277f54..bff6a5a526e7a 100644
--- a/libcxx/include/any
+++ b/libcxx/include/any
@@ -114,6 +114,8 @@ namespace std {
 #  include <version>
 
 // standard-mandated includes
+
+// [any.synop]
 #  include <initializer_list>
 #  include <typeinfo>
 
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 42e2512adeb5d..796125acf07e1 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -576,6 +576,12 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 
 #  include <version>
 
+// standard-mandated includes
+
+// [functional.syn]
+#  include <initializer_list>
+#  include <typeinfo>
+
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #    pragma GCC system_header
 #  endif
@@ -608,10 +614,6 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 #    include <__vector/vector.h>
 #  endif
 
-// standard-mandated includes
-#  include <initializer_list>
-#  include <typeinfo>
-
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 
 #endif // _LIBCPP_FUNCTIONAL
diff --git a/libcxx/include/typeindex b/libcxx/include/typeindex
index 9668ca27bfd14..ccb832faa16fe 100644
--- a/libcxx/include/typeindex
+++ b/libcxx/include/typeindex
@@ -56,6 +56,8 @@ struct hash<type_index>
 #  include <version>
 
 // standard-mandated includes
+
+// [type.index.synopsis]
 #  include <compare>
 #  include <typeinfo>
 
diff --git a/libcxx/include/valarray b/libcxx/include/valarray
index a08a5d4f82a83..4ae7f339efd3f 100644
--- a/libcxx/include/valarray
+++ b/libcxx/include/valarray
@@ -360,6 +360,7 @@ template<class T> valarray<T> tanh (const valarray<T>& x);
 #  include <__config>
 #  include <__cstddef/ptrdiff_t.h>
 #  include <__functional/operations.h>
+#  include <__iterator/reverse_iterator.h>
 #  include <__memory/addressof.h>
 #  include <__memory/allocator.h>
 #  include <__memory/uninitialized_algorithms.h>
@@ -371,10 +372,6 @@ template<class T> valarray<T> tanh (const valarray<T>& x);
 #  include <cmath>
 #  include <version>
 
-#  if _LIBCPP_STD_VER >= 14
-#    include <__iterator/reverse_iterator.h>
-#  endif
-
 // standard-mandated includes
 
 // [valarray.syn]
@@ -789,8 +786,8 @@ public:
   typedef _Tp value_type;
   typedef _Tp __result_type;
 
-  typedef _Tp* iterator;
-  typedef const _Tp* const_iterator;
+  using iteraor        = _Tp*;
+  using const_iterator = const _Tp*;
 
 private:
   value_type* __begin_;

>From 51511b7cfede5a4b02a57aa37ca7fec65fe1ad54 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Sun, 4 Jan 2026 09:46:16 +0800
Subject: [PATCH 3/8] Fix typo in `valarray`

---
 libcxx/include/valarray | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/valarray b/libcxx/include/valarray
index 4ae7f339efd3f..397017aa9f0d7 100644
--- a/libcxx/include/valarray
+++ b/libcxx/include/valarray
@@ -786,7 +786,7 @@ public:
   typedef _Tp value_type;
   typedef _Tp __result_type;
 
-  using iteraor        = _Tp*;
+  using iterator       = _Tp*;
   using const_iterator = const _Tp*;
 
 private:

>From d831bb00a1884d93c751a8a62981a94f18760ad9 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 6 Jan 2026 13:37:43 +0800
Subject: [PATCH 4/8] Revert drive-by exception specification strengthening

---
 libcxx/include/__iterator/reverse_iterator.h         |  8 ++------
 .../reverse.iter.cons/ctor.iter.pass.cpp             | 12 ++----------
 2 files changed, 4 insertions(+), 16 deletions(-)

diff --git a/libcxx/include/__iterator/reverse_iterator.h b/libcxx/include/__iterator/reverse_iterator.h
index 6f5a682c47bf5..64bdaf9ae5896 100644
--- a/libcxx/include/__iterator/reverse_iterator.h
+++ b/libcxx/include/__iterator/reverse_iterator.h
@@ -80,9 +80,7 @@ class reverse_iterator
 #ifndef _LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator() : __t_(), current() {}
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 explicit reverse_iterator(_Iter __x)
-      _NOEXCEPT_(is_nothrow_constructible<_Iter, _Iter&>::value)
-      : __t_(__x), current(__x) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 explicit reverse_iterator(_Iter __x) : __t_(__x), current(__x) {}
 
   template <class _Up, __enable_if_t<!is_same<_Up, _Iter>::value && is_convertible<_Up const&, _Iter>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator(const reverse_iterator<_Up>& __u)
@@ -99,9 +97,7 @@ class reverse_iterator
 #else
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator() : current() {}
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 explicit reverse_iterator(_Iter __x)
-      _NOEXCEPT_(is_nothrow_constructible<_Iter, _Iter&>::value)
-      : current(__x) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 explicit reverse_iterator(_Iter __x) : current(__x) {}
 
   template <class _Up, __enable_if_t<!is_same<_Up, _Iter>::value && is_convertible<_Up const&, _Iter>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator(const reverse_iterator<_Up>& __u)
diff --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cons/ctor.iter.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cons/ctor.iter.pass.cpp
index 2de83dddb058a..72e77b0856421 100644
--- a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cons/ctor.iter.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cons/ctor.iter.pass.cpp
@@ -14,22 +14,14 @@
 
 #include <iterator>
 #include <cassert>
-#include <type_traits>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
 template <class It>
 TEST_CONSTEXPR_CXX17 void test(It i) {
-#if TEST_STD_VER >= 11
-  // Verify that this constructor is conditionally noexcept in libc++.
-  // Because many (un)conditionally noexcept functions construct the returned reverse_iterator by this constructor.
-  LIBCPP_STATIC_ASSERT(std::is_nothrow_constructible<std::reverse_iterator<It>, It&>::value ==
-                           std::is_nothrow_constructible<It, It&>::value,
-                       "");
-#endif
-  std::reverse_iterator<It> r(i);
-  assert(r.base() == i);
+    std::reverse_iterator<It> r(i);
+    assert(r.base() == i);
 }
 
 TEST_CONSTEXPR_CXX17 bool tests() {

>From cb1982fca699f2a80bed6f11c8766ea4fa3ec27f Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 6 Jan 2026 13:38:20 +0800
Subject: [PATCH 5/8] Make `reverse_access` export `reverse_iterator`

---
 libcxx/include/module.modulemap.in | 5 ++++-
 libcxx/include/optional            | 1 -
 libcxx/include/valarray            | 1 -
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..dc951739ea36a 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1559,7 +1559,10 @@ module std {
     module projected                  { header "__iterator/projected.h" }
     module ranges_iterator_traits     { header "__iterator/ranges_iterator_traits.h" }
     module readable_traits            { header "__iterator/readable_traits.h" }
-    module reverse_access             { header "__iterator/reverse_access.h" }
+    module reverse_access {
+      header "__iterator/reverse_access.h"
+      export std.iterator.reverse_iterator
+    }
     module reverse_iterator           { header "__iterator/reverse_iterator.h" }
     module segmented_iterator         { header "__iterator/segmented_iterator.h" }
     module size                       { header "__iterator/size.h" }
diff --git a/libcxx/include/optional b/libcxx/include/optional
index f86892f1346c7..9d23aeccb77ec 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -272,7 +272,6 @@ namespace std {
 #  include <__fwd/functional.h>
 #  include <__iterator/bounded_iter.h>
 #  include <__iterator/capacity_aware_iterator.h>
-#  include <__iterator/reverse_iterator.h>
 #  include <__memory/addressof.h>
 #  include <__memory/construct_at.h>
 #  include <__ranges/enable_borrowed_range.h>
diff --git a/libcxx/include/valarray b/libcxx/include/valarray
index 397017aa9f0d7..47e8ec25bcfd2 100644
--- a/libcxx/include/valarray
+++ b/libcxx/include/valarray
@@ -360,7 +360,6 @@ template<class T> valarray<T> tanh (const valarray<T>& x);
 #  include <__config>
 #  include <__cstddef/ptrdiff_t.h>
 #  include <__functional/operations.h>
-#  include <__iterator/reverse_iterator.h>
 #  include <__memory/addressof.h>
 #  include <__memory/allocator.h>
 #  include <__memory/uninitialized_algorithms.h>

>From 6601b331e0af83d7de0439a329aa882422f52228 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 16 Jan 2026 16:23:20 +0800
Subject: [PATCH 6/8] Retarget LLVM 23

---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index d5e1fd33cf0f4..475d8f57b4ed3 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -158,7 +158,7 @@
 "`P1317R2 <https://wg21.link/P1317R2>`__","Remove return type deduction in ``std::apply``","2025-06 (Sofia)","","","`#148183 <https://github.com/llvm/llvm-project/issues/148183>`__",""
 "","","","","","",""
 "`P3920R0 <https://wg21.link/P3920R0>`__","Wording for NB comment resolution on trivial relocation","2025-11 (Kona)","|Nothing To Do|","","`#171269 <https://github.com/llvm/llvm-project/issues/171269>`__",""
-"`P3016R6 <https://wg21.link/P3016R6>`__","Resolve inconsistencies in begin/end for ``valarray`` and braced initializer lists","2025-11 (Kona)","|Complete|","22","`#171271 <https://github.com/llvm/llvm-project/issues/171271>`__","Implemented as a DR against C++11. Change of ``<stacktrace>`` is blocked on `P0881R7 <https://wg21.link/P0881R7>`__."
+"`P3016R6 <https://wg21.link/P3016R6>`__","Resolve inconsistencies in begin/end for ``valarray`` and braced initializer lists","2025-11 (Kona)","|Complete|","23","`#171271 <https://github.com/llvm/llvm-project/issues/171271>`__","Implemented as a DR against C++11. Change of ``<stacktrace>`` is blocked on `P0881R7 <https://wg21.link/P0881R7>`__."
 "`P3567R2 <https://wg21.link/P3567R2>`__","``flat_meow`` Fixes","2025-11 (Kona)","|Complete|","22","`#171272 <https://github.com/llvm/llvm-project/issues/171272>`__",""
 "`P3663R3 <https://wg21.link/P3663R3>`__","Future-proof ``submdspan_mapping``","2025-11 (Kona)","","","`#166089 <https://github.com/llvm/llvm-project/issues/166089>`__",""
 "`P3914R0 <https://wg21.link/P3914R0>`__","Assorted NB comment resolutions for Kona 2025","2025-11 (Kona)","","","`#171274 <https://github.com/llvm/llvm-project/issues/171274>`__",""

>From 37aa00f61628f34b1e7ccc1a36a9dbecc9485ac9 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 22 May 2026 12:09:27 +0800
Subject: [PATCH 7/8] clang-format

---
 libcxx/include/__iterator/reverse_access.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/reverse_access.h b/libcxx/include/__iterator/reverse_access.h
index ec045d8cb3586..a54b467fc2353 100644
--- a/libcxx/include/__iterator/reverse_access.h
+++ b/libcxx/include/__iterator/reverse_access.h
@@ -29,7 +29,8 @@ _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator<_Tp*> rbegin(_Tp (&__array)[_Np])
 }
 
 template <class _Tp, size_t _Np>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator<_Tp*> rend(_Tp (&__array)[_Np]) noexcept {
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator<_Tp*>
+rend(_Tp (&__array)[_Np]) noexcept {
   return reverse_iterator<_Tp*>(__array);
 }
 

>From c6c510fcbb6a8099ad94bf32baa8ab1f7978975e Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 21 Jul 2026 12:03:34 +0800
Subject: [PATCH 8/8] Retarget LLVM 24

---
 libcxx/docs/ReleaseNotes/24.rst    | 3 +++
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..ecda55497f970 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,9 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P3016R6: Resolve inconsistencies in begin/end for ``valarray`` and braced initializer lists
+  (`Github <https://llvm.org/PR171271>`__)
+
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 475d8f57b4ed3..cf2ce6809a646 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -158,7 +158,7 @@
 "`P1317R2 <https://wg21.link/P1317R2>`__","Remove return type deduction in ``std::apply``","2025-06 (Sofia)","","","`#148183 <https://github.com/llvm/llvm-project/issues/148183>`__",""
 "","","","","","",""
 "`P3920R0 <https://wg21.link/P3920R0>`__","Wording for NB comment resolution on trivial relocation","2025-11 (Kona)","|Nothing To Do|","","`#171269 <https://github.com/llvm/llvm-project/issues/171269>`__",""
-"`P3016R6 <https://wg21.link/P3016R6>`__","Resolve inconsistencies in begin/end for ``valarray`` and braced initializer lists","2025-11 (Kona)","|Complete|","23","`#171271 <https://github.com/llvm/llvm-project/issues/171271>`__","Implemented as a DR against C++11. Change of ``<stacktrace>`` is blocked on `P0881R7 <https://wg21.link/P0881R7>`__."
+"`P3016R6 <https://wg21.link/P3016R6>`__","Resolve inconsistencies in begin/end for ``valarray`` and braced initializer lists","2025-11 (Kona)","|Complete|","24","`#171271 <https://github.com/llvm/llvm-project/issues/171271>`__","Implemented as a DR against C++11. Change of ``<stacktrace>`` is blocked on `P0881R7 <https://wg21.link/P0881R7>`__."
 "`P3567R2 <https://wg21.link/P3567R2>`__","``flat_meow`` Fixes","2025-11 (Kona)","|Complete|","22","`#171272 <https://github.com/llvm/llvm-project/issues/171272>`__",""
 "`P3663R3 <https://wg21.link/P3663R3>`__","Future-proof ``submdspan_mapping``","2025-11 (Kona)","","","`#166089 <https://github.com/llvm/llvm-project/issues/166089>`__",""
 "`P3914R0 <https://wg21.link/P3914R0>`__","Assorted NB comment resolutions for Kona 2025","2025-11 (Kona)","","","`#171274 <https://github.com/llvm/llvm-project/issues/171274>`__",""


From libcxx-commits at lists.llvm.org  Mon Jul 20 21:56:18 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Mon, 20 Jul 2026 21:56:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a5efbf2.9edb5c9d.280be3.1586@mx.google.com>


https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/207471

>From f1f732efd7ce293a06aea8d331858a686d24cdb6 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 3 Jul 2026 17:38:47 -0400
Subject: [PATCH 1/4] Inline text_encoding alias offset table std::count to 883

- Add count test to verify the exact number of aliases our table
  contains
---
 libcxx/include/text_encoding                  |  4 +-
 .../text/text_encoding/aliases_count.pass.cpp | 41 +++++++++++++++++++
 .../text_encoding.ctor/id.pass.cpp            |  2 +-
 .../text_encoding.ctor/string_view.pass.cpp   |  2 +-
 .../text_encoding.eq/equal.id.pass.cpp        |  2 +-
 .../text_encoding.aliases_view/empty.pass.cpp |  2 +-
 .../operator-bool.pass.cpp                    |  2 +-
 .../test_text_encoding.h                      |  1 +
 8 files changed, 49 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
 rename libcxx/test/{std/text/text_encoding => support}/test_text_encoding.h (99%)

diff --git a/libcxx/include/text_encoding b/libcxx/include/text_encoding
index da39be92c3dfa..d6063482be74d 100644
--- a/libcxx/include/text_encoding
+++ b/libcxx/include/text_encoding
@@ -792,8 +792,8 @@ private:
       "11\0CP50220\0csCP50220\0";
 
   struct __offset_table {
-    constexpr static unsigned long long __num_aliases =
-        std::count(__aliases_string, __aliases_string + sizeof(__aliases_string), '\0') + 1;
+    constexpr static unsigned long long __num_aliases = 884;
+    // +2 for id::unknown, id::other sentinels
     unsigned short __table[__num_aliases];
   };
 
diff --git a/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp b/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
new file mode 100644
index 0000000000000..365c9fe3b9ea3
--- /dev/null
+++ b/libcxx/test/libcxx/text/text_encoding/aliases_count.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
+//
+//===----------------------------------------------------------------------===//
+
+// <text_encoding>
+
+// REQUIRES: std-at-least-c++26
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=40000000
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=1000000000
+
+// Our table has 882 aliases exactly, test to make sure that number matches with the total alias count
+// Internally, there are 884 entries with 2 reserved for id::unknown and id::other
+#include <cassert>
+#include <ranges>
+#include <text_encoding>
+
+#include "test_text_encoding.h"
+
+constexpr bool test() {
+  long long sum = 0;
+  for (auto& enc : unique_encoding_data) {
+    std::text_encoding te{std::text_encoding::id(enc.mib)};
+
+    sum += std::ranges::size(te.aliases());
+  }
+
+  assert(sum == 882);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp
index bc3d729e770e3..12c85e6994252 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.ctor/id.pass.cpp
@@ -19,7 +19,7 @@
 #include <text_encoding>
 #include <type_traits>
 
-#include "../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp
index c3202f78642f2..032a4d3c08557 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.ctor/string_view.pass.cpp
@@ -19,7 +19,7 @@
 #include <text_encoding>
 #include <type_traits>
 
-#include "../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp
index 79de614299fa0..c50f539d1527a 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.eq/equal.id.pass.cpp
@@ -16,7 +16,7 @@
 #include <text_encoding>
 
 #include "test_macros.h"
-#include "../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp
index 367f9216ef5ff..3008e2d27927f 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/empty.pass.cpp
@@ -16,7 +16,7 @@
 #include <ranges>
 #include <text_encoding>
 
-#include "../../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp
index 9d63ff4cdfccd..f122ee5c88e0b 100644
--- a/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp
+++ b/libcxx/test/std/text/text_encoding/text_encoding.members/text_encoding.aliases_view/operator-bool.pass.cpp
@@ -16,7 +16,7 @@
 #include <ranges>
 #include <text_encoding>
 
-#include "../../test_text_encoding.h"
+#include "test_text_encoding.h"
 
 using id = std::text_encoding::id;
 
diff --git a/libcxx/test/std/text/text_encoding/test_text_encoding.h b/libcxx/test/support/test_text_encoding.h
similarity index 99%
rename from libcxx/test/std/text/text_encoding/test_text_encoding.h
rename to libcxx/test/support/test_text_encoding.h
index e18bc3aaeea15..5cef8b8ab3d0a 100644
--- a/libcxx/test/std/text/text_encoding/test_text_encoding.h
+++ b/libcxx/test/support/test_text_encoding.h
@@ -295,6 +295,7 @@ constexpr inline enc_data all_encoding_data[] = {
     {3, "csASCII"},
     {3, "iso-ir-6"},
     {3, "us"},
+    {3, "ASCII"}, // extension to match libstdc++
     {4, "ISO-8859-1"},
     {4, "ISO_8859-1:1987"},
     {4, "CP819"},

>From 19e8c4af3cb765190b989ba756fbd84eb3830857 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sun, 5 Jul 2026 14:54:20 -0400
Subject: [PATCH 2/4] Address comments

---
 libcxx/include/text_encoding                              | 8 +++-----
 .../test/libcxx/text/text_encoding/aliases_count.pass.cpp | 6 ++++--
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/text_encoding b/libcxx/include/text_encoding
index d6063482be74d..6035927091493 100644
--- a/libcxx/include/text_encoding
+++ b/libcxx/include/text_encoding
@@ -68,7 +68,6 @@ struct text_encoding
 
 #  if _LIBCPP_STD_VER >= 26
 
-#    include <__algorithm/count.h>
 #    include <__algorithm/find_if.h>
 #    include <__algorithm/lower_bound.h>
 #    include <__cstddef/ptrdiff_t.h>
@@ -623,6 +622,8 @@ public:
   static bool environment_is() = delete;
 #    endif
 
+  constexpr static unsigned long long __num_aliases = 884;
+
 private:
   constexpr const __te_data& __get() const { return __entries[__encoding_idx_]; }
 
@@ -792,8 +793,6 @@ private:
       "11\0CP50220\0csCP50220\0";
 
   struct __offset_table {
-    constexpr static unsigned long long __num_aliases = 884;
-    // +2 for id::unknown, id::other sentinels
     unsigned short __table[__num_aliases];
   };
 
@@ -805,8 +804,7 @@ private:
 
     unsigned long long __idx = 3;
 
-    for (unsigned short __pos = 0; __pos < sizeof(__aliases_string) - 1 && __idx < __offset_table::__num_aliases;
-         __pos++) {
+    for (unsigned short __pos = 0; __pos < sizeof(__aliases_string) - 1 && __idx < __num_aliases; __pos++) {
       if (__aliases_string[__pos] == '\0') {
         __aliases.__table[__idx++] = __pos + 1;
       }
diff --git a/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp b/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
index 365c9fe3b9ea3..c5ca1188a0312 100644
--- a/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
+++ b/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
@@ -13,7 +13,9 @@
 // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=1000000000
 
 // Our table has 882 aliases exactly, test to make sure that number matches with the total alias count
-// Internally, there are 884 entries with 2 reserved for id::unknown and id::other
+// Internally, there are a total of 884 entries in our offset table, 882 for the actual aliases,
+// and +2 reserved as sentinels for id::unknown and id::other
+
 #include <cassert>
 #include <ranges>
 #include <text_encoding>
@@ -28,7 +30,7 @@ constexpr bool test() {
     sum += std::ranges::size(te.aliases());
   }
 
-  assert(sum == 882);
+  assert(sum == std::text_encoding::__num_aliases - 2);
 
   return true;
 }

>From 8e2c2419d3f3d001db7b55aec2347ef1c4d2dbe4 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 7 Jul 2026 00:06:38 -0400
Subject: [PATCH 3/4] Remove extension, check count

---
 .../test/libcxx/text/text_encoding/aliases_count.pass.cpp  | 7 +++----
 libcxx/test/support/test_text_encoding.h                   | 1 -
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp b/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
index c5ca1188a0312..f63ae503b9de9 100644
--- a/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
+++ b/libcxx/test/libcxx/text/text_encoding/aliases_count.pass.cpp
@@ -12,10 +12,7 @@
 // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=40000000
 // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=1000000000
 
-// Our table has 882 aliases exactly, test to make sure that number matches with the total alias count
-// Internally, there are a total of 884 entries in our offset table, 882 for the actual aliases,
-// and +2 reserved as sentinels for id::unknown and id::other
-
+// We implement 882 aliases, test to make sure that number matches with the total alias count.
 #include <cassert>
 #include <ranges>
 #include <text_encoding>
@@ -30,6 +27,8 @@ constexpr bool test() {
     sum += std::ranges::size(te.aliases());
   }
 
+  // +2 reserved as sentinels for id::unknown and id::other
+  // Meaning, our offset table actually contains 884 entries.
   assert(sum == std::text_encoding::__num_aliases - 2);
 
   return true;
diff --git a/libcxx/test/support/test_text_encoding.h b/libcxx/test/support/test_text_encoding.h
index 5cef8b8ab3d0a..e18bc3aaeea15 100644
--- a/libcxx/test/support/test_text_encoding.h
+++ b/libcxx/test/support/test_text_encoding.h
@@ -295,7 +295,6 @@ constexpr inline enc_data all_encoding_data[] = {
     {3, "csASCII"},
     {3, "iso-ir-6"},
     {3, "us"},
-    {3, "ASCII"}, // extension to match libstdc++
     {4, "ISO-8859-1"},
     {4, "ISO_8859-1:1987"},
     {4, "CP819"},

>From c425a63e756d25716d439ffc2558bc069431038d Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 21 Jul 2026 00:56:01 -0400
Subject: [PATCH 4/4] Add test header to module map

---
 libcxx/test/support/module.modulemap | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/support/module.modulemap b/libcxx/test/support/module.modulemap
index 0af147d75ee5d..2a6597e25cf87 100644
--- a/libcxx/test/support/module.modulemap
+++ b/libcxx/test/support/module.modulemap
@@ -8,4 +8,5 @@ module test {
   module test_allocator         { header "test_allocator.h" }
   module test_iterators         { header "test_iterators.h" }
   module type_algorithms        { header "type_algorithms.h" }
+  module test_text_encoding     { header "test_text_encoding.h" }
 }


From libcxx-commits at lists.llvm.org  Mon Jul 20 23:51:25 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 23:51:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a5f16ed.e27fddb3.ef63c.d9f7@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Mon Jul 20 23:51:25 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 23:51:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a5f16ed.101bc966.1fd28c.5963@mx.google.com>


https://github.com/philnik777 approved this pull request.

LGTM % nit.

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Mon Jul 20 23:51:26 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Mon, 20 Jul 2026 23:51:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a5f16ee.6b2684af.21659c.c23b@mx.google.com>


================
@@ -8,4 +8,5 @@ module test {
   module test_allocator         { header "test_allocator.h" }
   module test_iterators         { header "test_iterators.h" }
   module type_algorithms        { header "type_algorithms.h" }
+  module test_text_encoding     { header "test_text_encoding.h" }
----------------
philnik777 wrote:

please put it in lexicographical order.

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Tue Jul 21 02:06:19 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 02:06:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Include sizes in string
	benchmark names (PR #210795)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210795@github.com>
Message-ID: <6a5f368b.e55dc341.1c6a0.4e8a@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/210795

From libcxx-commits at lists.llvm.org  Tue Jul 21 02:08:09 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 02:08:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f36f9.b4a331f0.d9742.7d03@mx.google.com>


philnik777 wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/containers/string.bench.cpp

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Tue Jul 21 02:09:09 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 02:09:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210360@github.com>
Message-ID: <6a5f3735.1728eaf3.2a2f6.5da5@mx.google.com>


================
@@ -562,11 +562,10 @@ void locale::facet::__on_zero_shared() noexcept { delete this; }
 
 // locale::id
 
-constinit int32_t locale::id::__next_id = 0;
-
 long locale::id::__get() {
-  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&__next_id, 1); });
-  return __id_ - 1;
+  constinit static atomic<int32_t> next_id = 0;
----------------
philnik777 wrote:

Urgh, that's some horrible naming and confusing semantics. Switching back to the original version for now, but this should definitely be cleaned up.

https://github.com/llvm/llvm-project/pull/210360

From libcxx-commits at lists.llvm.org  Tue Jul 21 02:14:32 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 02:14:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210360@github.com>
Message-ID: <6a5f3878.bd93e731.30f155.fc28@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/210360

>From 9e5261a7aa9df5aa29b8bb34a9915111736745d5 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 17 Jul 2026 17:31:21 +0200
Subject: [PATCH] [libc++] Remove locale::id::__next_id from the ABI

---
 libcxx/include/__locale                                      | 2 --
 ...apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...ux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...erpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...pc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...ux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...nown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...wn-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 1 -
 libcxx/src/locale.cpp                                        | 5 ++---
 11 files changed, 2 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index bb8f8e7bbf5d9..f878aa48071ca 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -162,8 +162,6 @@ class _LIBCPP_EXPORTED_FROM_ABI locale::id {
   once_flag __flag_;
   int32_t __id_;
 
-  static int32_t __next_id;
-
 public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR id() : __id_(0) {}
   void operator=(const id&) = delete;
diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 2a419e8c8af05..fad3441d59ab7 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1730,7 +1730,6 @@
 {'is_defined': True, 'name': '__ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__16locale2id9__next_idE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale3allE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4noneE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4timeE', 'size': 0, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 414a239757e1c..08ee6a6a515c9 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1368,7 +1368,6 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16gslice6__initEj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 590c0de8ce20f..dd79e25d66915 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -699,7 +699,6 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
-{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index f4f4ab99ad866..e2cc8a0f106f1 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -699,7 +699,6 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
-{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 92a37db24ac5f..be1d6382a7983 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1719,7 +1719,6 @@
 {'is_defined': True, 'name': '__ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__16locale2id9__next_idE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale3allE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4noneE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4timeE', 'size': 0, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index e1b1e110318fa..3f618601469dd 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1368,7 +1368,6 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 11cc8f180e824..159e974775034 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1380,7 +1380,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..46094de75b221 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1379,7 +1379,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 2b69d6dc89138..df53f1b42fbec 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1350,7 +1350,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index b0a69ab74f94c..40613e72b1a9d 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -562,10 +562,9 @@ void locale::facet::__on_zero_shared() noexcept { delete this; }
 
 // locale::id
 
-constinit int32_t locale::id::__next_id = 0;
-
 long locale::id::__get() {
-  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&__next_id, 1); });
+  constinit static int32_t next_id = 0;
+  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&next_id, 1); });
   return __id_ - 1;
 }
 


From libcxx-commits at lists.llvm.org  Tue Jul 21 02:18:42 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 02:18:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] reduced the number of multiplication (PR
	#154503)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/154503@github.com>
Message-ID: <6a5f3972.c4d3d80a.849c3.23c3@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/154503

From libcxx-commits at lists.llvm.org  Tue Jul 21 02:18:42 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 02:18:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] reduced the number of multiplication (PR
	#154503)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/154503@github.com>
Message-ID: <6a5f3972.1e1ac41d.3969c3.c9a4@mx.google.com>


philnik777 wrote:

I don't think this actually makes any difference. The compiler is perfectly capably of optimizing divisions with constants, probably producing the exact same sequence of instructions in the end with either version. This one is just way less readable. Closing, since there hasn't been any activity.

https://github.com/llvm/llvm-project/pull/154503

From libcxx-commits at lists.llvm.org  Tue Jul 21 02:20:42 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 02:20:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize to_chars integral
 overloads for base 2 and 16 (PR #210659)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210659@github.com>
Message-ID: <6a5f39ea.1728eaf3.2a2f6.5c73@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/210659

>From b6f0d385fcc77c1841d7e5d30c940f6ebeae7c33 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 18 Jul 2026 13:03:43 +0200
Subject: [PATCH] [libc++] Optimize to_chars integral overloads

---
 libcxx/include/__charconv/to_chars_integral.h | 94 +++++++++++++++++++
 libcxx/test/support/charconv_test_helpers.h   |  2 -
 2 files changed, 94 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__charconv/to_chars_integral.h b/libcxx/include/__charconv/to_chars_integral.h
index 6d425139260b6..1b7649ab1638a 100644
--- a/libcxx/include/__charconv/to_chars_integral.h
+++ b/libcxx/include/__charconv/to_chars_integral.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___CHARCONV_TO_CHARS_INTEGRAL_H
 
 #include <__algorithm/copy_n.h>
+#include <__algorithm/simd_utils.h>
 #include <__assert>
 #include <__bit/countl.h>
 #include <__charconv/tables.h>
@@ -22,12 +23,14 @@
 #include <__system_error/errc.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_integral.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_signed.h>
 #include <__type_traits/make_32_64_or_128_bit.h>
 #include <__type_traits/make_unsigned.h>
 #include <__utility/unreachable.h>
+#include <array>
 #include <cstdint>
 #include <limits>
 
@@ -107,6 +110,29 @@ __to_chars_integral(char* __first, char* __last, _Tp __value, int __base) {
 
 namespace __itoa {
 
+template <size_t _Duplicate, size_t _IndexCount>
+constexpr array<size_t, _IndexCount * _Duplicate> __build_indices() {
+  array<size_t, _IndexCount * _Duplicate> __ret;
+  for (size_t __i = 0; __i != _IndexCount; ++__i) {
+    for (size_t __k = 0; __k != _Duplicate; ++__k)
+      __ret[__i * _Duplicate + __k] = _IndexCount - __i - 1;
+  }
+  return __ret;
+}
+
+_LIBCPP_DIAGNOSTIC_PUSH
+// TODO: remove this once all supported compilers diagnose functions correctly
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
+// This is marked `always_inline` because it interacts with simd vectors
+template <size_t _Duplicate, size_t _VecSize>
+[[__gnu__::__always_inline__]] auto __duplicate_vector_entries(__simd_vector<char, _VecSize> __vals) {
+  static constexpr auto __indices = __itoa::__build_indices<_Duplicate, _VecSize>();
+  return [&]<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+    return __builtin_shufflevector(__vals, __vals, __indices[_Indices]...);
+  }(make_index_sequence<_Duplicate * _VecSize>());
+}
+_LIBCPP_DIAGNOSTIC_POP
+
 template <unsigned _Base>
 struct _LIBCPP_HIDDEN __integral;
 
@@ -122,6 +148,39 @@ struct _LIBCPP_HIDDEN __integral<2> {
   template <typename _Tp>
   _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
   __to_chars(char* __first, char* __last, _Tp __value) {
+#if _LIBCPP_VECTORIZE_ALGORITHMS && __has_builtin(__builtin_masked_store)
+    if (!__libcpp_is_constant_evaluated() && __last - __first >= numeric_limits<_Tp>::digits) {
+      // Move the to-be-converted bits into the high bits, so that they are printed at the start.
+      auto __char_count = __width(__value);
+      auto __shift      = std::__countl_zero(__value | 1);
+      __value <<= __shift;
+
+      // Move the value into a vector and chop it up into its constituent bytes
+      auto __chopped    = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
+
+      // Duplicate values so we can extract the appropriate bits in multiple positions
+      auto __characters = __itoa::__duplicate_vector_entries<8>(__chopped);
+
+      // This is marked `always_inline` because it interacts with simd vectors
+      _LIBCPP_DIAGNOSTIC_PUSH
+      // TODO: remove this once all supported compilers diagnose functions correctly
+      _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
+      auto __shifts = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+        return __simd_vector<char, 8 * sizeof(_Tp)>{(7 - _Indices % 8)...};
+      }(make_index_sequence<8 * sizeof(_Tp)>());
+      _LIBCPP_DIAGNOSTIC_POP
+
+      // Check the appropriate bit for the position and set the character to 0 or 1 depending on whether it's set.
+      __characters = (__characters & __shifts) == 0 ? '0' : '1';
+
+      // Store all the characters, no matter whether they're part of the value or not. This is only safe if the buffer
+      // we've been given is large enough.
+      // TODO: Generate a mask on platforms which have native masked stores and use this code path unconditionally.
+      __builtin_masked_store(__simd_vector<bool, 8 * sizeof(_Tp)>(true), __characters, __first);
+      return {__first + __char_count, errc(0)};
+    }
+#endif
+
     ptrdiff_t __cap = __last - __first;
     int __n         = __width(__value);
     if (__n > __cap)
@@ -192,6 +251,41 @@ struct _LIBCPP_HIDDEN __integral<16> {
   template <typename _Tp>
   _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
   __to_chars(char* __first, char* __last, _Tp __value) {
+#if _LIBCPP_VECTORIZE_ALGORITHMS && __has_builtin(__builtin_masked_store)
+    if (!__libcpp_is_constant_evaluated() && __last - __first >= (numeric_limits<_Tp>::digits / 4)) {
+      // Lambdas are marked as `always_inline` because they return vectors, which aren't ABI-stable
+
+      // Move the to-be-converted bits into the high bits, so that they are printed at the start. Note that the shift is
+      // 4-bit aligned, so that the value of the characters doesn't change.
+      auto __char_count = __width(__value);
+      auto __shift      = std::__countl_zero(__value | 1) & ~3;
+      __value <<= __shift;
+
+      // Move the value into a vector and chop it up into its constituent bytes
+      auto __chopped    = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
+
+      // Duplicate values so we can extract the appropriate bits in multiple positions
+      auto __characters = __itoa::__duplicate_vector_entries<2>(__chopped);
+
+      // This is marked `always_inline` because it interacts with simd vectors
+      auto __shifts     = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+        return __simd_vector<char, 2 * sizeof(_Tp)>{(_Indices % 2 == 0 ? 4 : 0)...};
+      }(make_index_sequence<2 * sizeof(_Tp)>());
+
+      // Extract the bits we want for a given offset
+      __characters = (__characters >> __shifts) & 15;
+
+      // Convert the value into the hexadecimal character
+      __characters = (__characters >= 10 ? char('a' - 10) : '0') + __characters;
+
+      // Store all the characters, no matter whether they're part of the value or not. This is only safe if the buffer
+      // we've been given is large enough.
+      // TODO: Generate a mask on platforms which have native masked stores and use this code path unconditionally.
+      __builtin_masked_store(__simd_vector<bool, 2 * sizeof(_Tp)>(true), __characters, __first);
+      return {__first + __char_count, errc(0)};
+    }
+#endif
+
     ptrdiff_t __cap = __last - __first;
     int __n         = __width(__value);
     if (__n > __cap)
diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index fcae09478457b..f6b60a2c8fe4d 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -113,8 +113,6 @@ struct to_chars_test_base
         std::iota(buf, buf + sizeof(buf), static_cast<unsigned char>(1));
         r = std::to_chars(buf, buf + sizeof(buf), v, args...);
         assert(r.ec == std::errc{});
-        for (std::size_t i = r.ptr - buf; i < sizeof(buf); ++i)
-            assert(static_cast<unsigned char>(buf[i]) == i + 1);
         *r.ptr = '\0';
 
 #ifndef TEST_HAS_NO_INT128


From libcxx-commits at lists.llvm.org  Tue Jul 21 02:29:36 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 21 Jul 2026 02:29:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5f3c00.1728eaf3.2a2f6.dc7d@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210550

>From 23293789342393617472800afc72656011d5334f Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:13:24 +0200
Subject: [PATCH 1/7] apply: changes from proposal and mark as Complete

---
 libcxx/docs/Status/Cxx23Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 91c4dc462c0c9..0e13c6dc62bfe 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..bbdd30e56730f 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
     }
   }
 

>From cbed72094e96ce8a7756aa588be4dc25f608635b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:33:56 +0200
Subject: [PATCH 2/7] clang-format

---
 libcxx/include/__iterator/distance.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index bbdd30e56730f..e0a2590253c71 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,7 +86,8 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>>
+  operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {

>From a265367976e6651929998984e3284210eab11585 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 23:58:32 +0200
Subject: [PATCH 3/7] update: comments in tests

---
 .../range.iter.ops.distance/iterator_sentinel.pass.cpp          | 2 +-
 .../range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index c34d59e405a9e..1888f606465b2 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(I&& first, S last); // TODO: update when LWG3664 is resolved
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <array>
 #include <cassert>
diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
index 164101c2bf5a8..bac0ffba1e7f1 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
@@ -13,7 +13,7 @@
 //     constexpr iter_difference_t<I> ranges::distance(I first, S last);
 //
 // template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<I> ranges::distance(const I& first, S last);
+//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
 
 #include <cassert>
 #include <iterator>

>From 9452d4068636d3a8c8d31b5602b9446ffd7f7fe4 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 19 Jul 2026 00:11:01 +0200
Subject: [PATCH 4/7] add version number

---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0e13c6dc62bfe..617c89508b7a3 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","24","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""

>From c8e9bf8199acfc396e09077e01eb52c240fa2636 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 19 Jul 2026 13:01:31 +0200
Subject: [PATCH 5/7] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 617c89508b7a3..4b80d5ebf3623 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -249,7 +249,7 @@
 "`LWG2295 <https://wg21.link/LWG2295>`__","Locale name when the provided ``Facet`` is a ``nullptr``","2023-02 (Issaquah)","","","`#105063 <https://github.com/llvm/llvm-project/issues/105063>`__",""
 "`LWG3032 <https://wg21.link/LWG3032>`__","``ValueSwappable`` requirement missing for ``push_heap`` and ``make_heap``","2023-02 (Issaquah)","","","`#105064 <https://github.com/llvm/llvm-project/issues/105064>`__",""
 "`LWG3085 <https://wg21.link/LWG3085>`__","``char_traits::copy`` precondition too weak","2023-02 (Issaquah)","","","`#105065 <https://github.com/llvm/llvm-project/issues/105065>`__",""
-"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","24","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
+"`LWG3664 <https://wg21.link/LWG3664>`__","`LWG 3392 <https://wg21.link/LWG3392>`__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","|Complete|","14","`#105066 <https://github.com/llvm/llvm-project/issues/105066>`__",""
 "`LWG3720 <https://wg21.link/LWG3720>`__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","`#105067 <https://github.com/llvm/llvm-project/issues/105067>`__",""
 "`LWG3756 <https://wg21.link/LWG3756>`__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","`#105068 <https://github.com/llvm/llvm-project/issues/105068>`__",""
 "`LWG3769 <https://wg21.link/LWG3769>`__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","`#105069 <https://github.com/llvm/llvm-project/issues/105069>`__",""

>From 934ee224bb2c04a091a0b64c48f1f2c950e597e2 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sun, 19 Jul 2026 17:53:08 +0200
Subject: [PATCH 6/7] port lwg3664 tests to iterator_sentinel.pass.cpp

---
 .../iterator_sentinel.pass.cpp                | 39 ++++++++++
 .../range.iter.ops.distance/lwg3664.pass.cpp  | 73 -------------------
 2 files changed, 39 insertions(+), 73 deletions(-)
 delete mode 100644 libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index 1888f606465b2..30507c59495ea 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -183,6 +183,22 @@ constexpr void test_stride_counting() {
   assert(std::ranges::distance(view.begin(), view.end()) == n);
 }
 
+template <class It>
+struct EvilSentinel {
+  It p_;
+  friend constexpr bool operator==(EvilSentinel s, It p) { return s.p_ == p; }
+  friend constexpr auto operator-(EvilSentinel s, It p) { return s.p_ - p; }
+  friend constexpr auto operator-(It p, EvilSentinel s) { return p - s.p_; }
+  friend constexpr void operator-(EvilSentinel s, int (&)[3])        = delete;
+  friend constexpr void operator-(EvilSentinel s, int (&&)[3])       = delete;
+  friend constexpr void operator-(EvilSentinel s, const int (&)[3])  = delete;
+  friend constexpr void operator-(EvilSentinel s, const int (&&)[3]) = delete;
+};
+static_assert(std::sized_sentinel_for<EvilSentinel<int*>, int*>);
+static_assert(!std::sized_sentinel_for<EvilSentinel<int*>, const int*>);
+static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, int*>);
+static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, const int*>);
+
 constexpr bool test() {
   {
     int a[] = {1, 2, 3};
@@ -271,6 +287,29 @@ constexpr bool test() {
   if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
     test_deque();
 
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, a + 3) == 3);
+    assert(std::ranges::distance(a, a) == 0);
+    assert(std::ranges::distance(a + 3, a) == -3);
+  }
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
+  }
+  {
+    const int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
deleted file mode 100644
index bac0ffba1e7f1..0000000000000
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/lwg3664.pass.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// template<class I, sentinel_for<I> S>
-//   requires (!sized_sentinel_for<S, I>)
-//     constexpr iter_difference_t<I> ranges::distance(I first, S last);
-//
-// template<class I, sized_sentinel_for<decay_t<I>> S>
-//   constexpr iter_difference_t<decay_t<I>> ranges::distance(I&& first, S last);
-
-#include <cassert>
-#include <iterator>
-#include <type_traits>
-#include <utility>
-
-#include "test_iterators.h"
-
-template<class It>
-struct EvilSentinel {
-  It p_;
-  friend constexpr bool operator==(EvilSentinel s, It p) { return s.p_ == p; }
-  friend constexpr auto operator-(EvilSentinel s, It p) { return s.p_ - p; }
-  friend constexpr auto operator-(It p, EvilSentinel s) { return p - s.p_; }
-  friend constexpr void operator-(EvilSentinel s, int(&)[3]) = delete;
-  friend constexpr void operator-(EvilSentinel s, int(&&)[3]) = delete;
-  friend constexpr void operator-(EvilSentinel s, const int(&)[3]) = delete;
-  friend constexpr void operator-(EvilSentinel s, const int(&&)[3]) = delete;
-};
-static_assert( std::sized_sentinel_for<EvilSentinel<int*>, int*>);
-static_assert(!std::sized_sentinel_for<EvilSentinel<int*>, const int*>);
-static_assert( std::sized_sentinel_for<EvilSentinel<const int*>, int*>);
-static_assert( std::sized_sentinel_for<EvilSentinel<const int*>, const int*>);
-
-constexpr bool test() {
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, a + 3) == 3);
-    assert(std::ranges::distance(a, a) == 0);
-    assert(std::ranges::distance(a + 3, a) == -3);
-  }
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a+3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
-    assert(std::ranges::distance(a+3, EvilSentinel<int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a+3}) == 3);
-  }
-  {
-    const int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a+3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
-    assert(std::ranges::distance(a+3, EvilSentinel<const int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a+3}) == 3);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int(&)[3], EvilSentinel<int*>>);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int(&&)[3], EvilSentinel<int*>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 09717349d634bce08c26b9e3ca3cb9d927f6a80a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 11:29:21 +0200
Subject: [PATCH 7/7] revert: initial changes

---
 libcxx/include/__iterator/distance.h | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index e0a2590253c71..c7c4ff22d6e9f 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -86,12 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>>
-  operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - static_cast<decay_t<_Ip>>(__first);
+      return __last - decay_t<_Ip>(__first);
     }
   }
 


From libcxx-commits at lists.llvm.org  Tue Jul 21 02:31:13 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 21 Jul 2026 02:31:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5f3c61.13be6df8.22eac4.7114@mx.google.com>


================
@@ -86,11 +86,11 @@ struct __distance {
   }
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<decay_t<_Ip>> operator()(_Ip&& __first, _Sp __last) const {
     if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
----------------
lknknm wrote:

I just reverted the changes. I kept the tests unified inside `range.iter.ops.distance/iterator_sentinel.pass.cpp`.

https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Tue Jul 21 02:31:20 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 21 Jul 2026 02:31:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5f3c68.1905e68e.30d957.2ef2@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Tue Jul 21 02:32:41 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 21 Jul 2026 02:32:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5f3cb9.b1416da1.3242c8.1408@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Tue Jul 21 03:02:27 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 03:02:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize to_chars integral
 overloads for base 2 and 16 (PR #210659)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210659@github.com>
Message-ID: <6a5f43b3.d0917374.1888be.e517@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/210659

>From 19263161d93d3e32926cdb7f2126f99cff036e6a Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 18 Jul 2026 13:03:43 +0200
Subject: [PATCH] [libc++] Optimize to_chars integral overloads

---
 libcxx/include/__charconv/to_chars_integral.h | 98 +++++++++++++++++++
 libcxx/test/support/charconv_test_helpers.h   |  2 -
 2 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__charconv/to_chars_integral.h b/libcxx/include/__charconv/to_chars_integral.h
index 6d425139260b6..f88f6b0f53c44 100644
--- a/libcxx/include/__charconv/to_chars_integral.h
+++ b/libcxx/include/__charconv/to_chars_integral.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___CHARCONV_TO_CHARS_INTEGRAL_H
 
 #include <__algorithm/copy_n.h>
+#include <__algorithm/simd_utils.h>
 #include <__assert>
 #include <__bit/countl.h>
 #include <__charconv/tables.h>
@@ -22,12 +23,14 @@
 #include <__system_error/errc.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_integral.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_signed.h>
 #include <__type_traits/make_32_64_or_128_bit.h>
 #include <__type_traits/make_unsigned.h>
 #include <__utility/unreachable.h>
+#include <array>
 #include <cstdint>
 #include <limits>
 
@@ -107,6 +110,29 @@ __to_chars_integral(char* __first, char* __last, _Tp __value, int __base) {
 
 namespace __itoa {
 
+template <size_t _Duplicate, size_t _IndexCount>
+constexpr array<size_t, _IndexCount * _Duplicate> __build_indices() {
+  array<size_t, _IndexCount * _Duplicate> __ret;
+  for (size_t __i = 0; __i != _IndexCount; ++__i) {
+    for (size_t __k = 0; __k != _Duplicate; ++__k)
+      __ret[__i * _Duplicate + __k] = _IndexCount - __i - 1;
+  }
+  return __ret;
+}
+
+_LIBCPP_DIAGNOSTIC_PUSH
+// TODO: remove this once all supported compilers diagnose functions correctly
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
+// This is marked `always_inline` because it interacts with simd vectors
+template <size_t _Duplicate, size_t _VecSize>
+[[__gnu__::__always_inline__]] auto __duplicate_vector_entries(__simd_vector<char, _VecSize> __vals) {
+  static constexpr auto __indices = __itoa::__build_indices<_Duplicate, _VecSize>();
+  return [&]<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+    return __builtin_shufflevector(__vals, __vals, __indices[_Indices]...);
+  }(make_index_sequence<_Duplicate * _VecSize>());
+}
+_LIBCPP_DIAGNOSTIC_POP
+
 template <unsigned _Base>
 struct _LIBCPP_HIDDEN __integral;
 
@@ -122,6 +148,39 @@ struct _LIBCPP_HIDDEN __integral<2> {
   template <typename _Tp>
   _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
   __to_chars(char* __first, char* __last, _Tp __value) {
+#if _LIBCPP_VECTORIZE_ALGORITHMS && __has_builtin(__builtin_masked_store)
+    if (!__libcpp_is_constant_evaluated() && __last - __first >= numeric_limits<_Tp>::digits) {
+      // Move the to-be-converted bits into the high bits, so that they are printed at the start.
+      auto __char_count = __width(__value);
+      auto __shift      = std::__countl_zero(__value | 1);
+      __value <<= __shift;
+
+      // Move the value into a vector and chop it up into its constituent bytes
+      auto __chopped    = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
+
+      // Duplicate values so we can extract the appropriate bits in multiple positions
+      auto __characters = __itoa::__duplicate_vector_entries<8>(__chopped);
+
+      // This is marked `always_inline` because it interacts with simd vectors
+      _LIBCPP_DIAGNOSTIC_PUSH
+      // TODO: remove this once all supported compilers diagnose functions correctly
+      _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
+      auto __shifts = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+        return __simd_vector<char, 8 * sizeof(_Tp)>{(7 - _Indices % 8)...};
+      }(make_index_sequence<8 * sizeof(_Tp)>());
+      _LIBCPP_DIAGNOSTIC_POP
+
+      // Check the appropriate bit for the position and set the character to 0 or 1 depending on whether it's set.
+      __characters = (__characters & __shifts) == 0 ? '0' : '1';
+
+      // Store all the characters, no matter whether they're part of the value or not. This is only safe if the buffer
+      // we've been given is large enough.
+      // TODO: Generate a mask on platforms which have native masked stores and use this code path unconditionally.
+      __builtin_masked_store(__simd_vector<bool, 8 * sizeof(_Tp)>(true), __characters, __first);
+      return {__first + __char_count, errc(0)};
+    }
+#endif
+
     ptrdiff_t __cap = __last - __first;
     int __n         = __width(__value);
     if (__n > __cap)
@@ -192,6 +251,45 @@ struct _LIBCPP_HIDDEN __integral<16> {
   template <typename _Tp>
   _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI static __to_chars_result
   __to_chars(char* __first, char* __last, _Tp __value) {
+#if _LIBCPP_VECTORIZE_ALGORITHMS && __has_builtin(__builtin_masked_store)
+    if (!__libcpp_is_constant_evaluated() && __last - __first >= (numeric_limits<_Tp>::digits / 4)) {
+      // Lambdas are marked as `always_inline` because they return vectors, which aren't ABI-stable
+
+      // Move the to-be-converted bits into the high bits, so that they are printed at the start. Note that the shift is
+      // 4-bit aligned, so that the value of the characters doesn't change.
+      auto __char_count = __width(__value);
+      auto __shift      = std::__countl_zero(__value | 1) & ~3;
+      __value <<= __shift;
+
+      // Move the value into a vector and chop it up into its constituent bytes
+      auto __chopped    = __simd_vector<char, sizeof(_Tp)>(__simd_vector<_Tp, 1>(__value));
+
+      // Duplicate values so we can extract the appropriate bits in multiple positions
+      auto __characters = __itoa::__duplicate_vector_entries<2>(__chopped);
+
+      // This is marked `always_inline` because it interacts with simd vectors
+      _LIBCPP_DIAGNOSTIC_PUSH
+      // TODO: remove this once all supported compilers diagnose functions correctly
+      _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
+      auto __shifts     = []<size_t... _Indices> [[__gnu__::__always_inline__]] (index_sequence<_Indices...>) {
+        return __simd_vector<char, 2 * sizeof(_Tp)>{(_Indices % 2 == 0 ? 4 : 0)...};
+      }(make_index_sequence<2 * sizeof(_Tp)>());
+      _LIBCPP_DIAGNOSTIC_POP
+
+      // Extract the bits we want for a given offset
+      __characters = (__characters >> __shifts) & 15;
+
+      // Convert the value into the hexadecimal character
+      __characters = (__characters >= 10 ? char('a' - 10) : '0') + __characters;
+
+      // Store all the characters, no matter whether they're part of the value or not. This is only safe if the buffer
+      // we've been given is large enough.
+      // TODO: Generate a mask on platforms which have native masked stores and use this code path unconditionally.
+      __builtin_masked_store(__simd_vector<bool, 2 * sizeof(_Tp)>(true), __characters, __first);
+      return {__first + __char_count, errc(0)};
+    }
+#endif
+
     ptrdiff_t __cap = __last - __first;
     int __n         = __width(__value);
     if (__n > __cap)
diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index fcae09478457b..f6b60a2c8fe4d 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -113,8 +113,6 @@ struct to_chars_test_base
         std::iota(buf, buf + sizeof(buf), static_cast<unsigned char>(1));
         r = std::to_chars(buf, buf + sizeof(buf), v, args...);
         assert(r.ec == std::errc{});
-        for (std::size_t i = r.ptr - buf; i < sizeof(buf); ++i)
-            assert(static_cast<unsigned char>(buf[i]) == i + 1);
         *r.ptr = '\0';
 
 #ifndef TEST_HAS_NO_INT128


From libcxx-commits at lists.llvm.org  Tue Jul 21 03:12:01 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Tue, 21 Jul 2026 03:12:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Mark LWG3664 as resolved
	(PR #210550)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210550@github.com>
Message-ID: <6a5f45f1.9edb5c9d.280be3.a46e@mx.google.com>


https://github.com/frederick-vs-ja approved this pull request.


https://github.com/llvm/llvm-project/pull/210550

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:16:32 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 04:16:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
Message-ID: <llvm.org/llvm/llvm-project/pull/210951@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/210951

After #206453 we don't correctly handle `unget()` anymore. This fixes the issue by updating the internal buffer to contain the tail of the read data.


>From 24cc1dfc9e792231143e8e5f8cf0826794aa9b27 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 21 Jul 2026 13:14:55 +0200
Subject: [PATCH] [libc++] Fix ungetc failing after xsgetn

---
 libcxx/include/fstream                        | 77 +++++++++++++------
 .../fstreams/filebuf.members/test.dat         |  1 +
 .../filebuf.members/xsgetn.buffer.pass.cpp    | 59 ++++++++++++++
 .../fstreams/ifstream.members/xsgetn.pass.cpp | 14 ++++
 4 files changed, 126 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
 create mode 100644 libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..bd94bb4bf4b90 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -311,6 +311,8 @@ protected:
 
   _LIBCPP_HIDE_FROM_ABI_VIRTUAL streamsize xsgetn(char_type* __str, streamsize __len) override {
     if (__file_ && __always_noconv_) {
+      char_type __1buf;
+      __read_guard __guard(__1buf, *this);
       const streamsize __n = std::min(this->egptr() - this->gptr(), __len);
       if (__n != 0) {
         traits_type::copy(__str, this->gptr(), __n);
@@ -319,9 +321,16 @@ protected:
       const streamsize __remainder    = __len - __n;
       const streamsize __buffer_space = this->egptr() - this->eback();
 
-      if (__remainder >= __buffer_space)
-        return std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
-      else if (__remainder > 0)
+      if (__remainder >= __buffer_space) {
+        auto __res = std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
+
+        // Copy the buffer-sized tail into the buffer so that `unget`ting works correctly
+        auto __buf_size = std::min<size_t>(__res, this->egptr() - this->eback());
+        traits_type::copy(this->eback(), __str + __res - __buf_size, __buf_size);
+        this->setg(this->eback(), this->eback() + __buf_size, this->eback() + __buf_size);
+
+        return __res;
+      } else if (__remainder > 0)
         return basic_streambuf<_CharT, _Traits>::xsgetn(__str + __n, __remainder) + __n;
       return __n;
     }
@@ -383,7 +392,41 @@ private:
   bool __owns_ib_;
   bool __always_noconv_;
 
-  bool __read_mode();
+  struct [[__nodiscard__]] __read_guard {
+    basic_filebuf& __self_;
+    size_t __unget_size_;
+    char_type& __1buf_;
+
+    __read_guard(char_type& __1buf, basic_filebuf& __self) : __self_(__self), __1buf_(__1buf) {
+      if (__self.__cm_ & ios_base::in) {
+        __unget_size_ = std::min<size_t>((__self.egptr() - __self.eback()) / 2, 4);
+      } else {
+        __self.setp(nullptr, nullptr);
+        if (__self.__always_noconv_) {
+          __self.setg(reinterpret_cast<char_type*>(__self.__extbuf_),
+                      reinterpret_cast<char_type*>(__self.__extbuf_) + __self.__ebs_,
+                      reinterpret_cast<char_type*>(__self.__extbuf_) + __self.__ebs_);
+        } else {
+          __self.setg(__self.__intbuf_, __self.__intbuf_ + __self.__ibs_, __self.__intbuf_ + __self.__ibs_);
+        }
+        __self.__cm_ = ios_base::in;
+        __unget_size_ = 0;
+      }
+
+      if (__self.gptr() == nullptr)
+        __self.setg(std::addressof(__1buf_), std::addressof(__1buf_) + 1, std::addressof(__1buf_) + 1);
+    }
+
+    size_t __unget_size() {
+      return __unget_size_;
+    }
+
+    ~__read_guard() {
+      if (__self_.eback() == std::addressof(__1buf_))
+        __self_.setg(nullptr, nullptr, nullptr);
+    }
+  };
+
   void __write_mode();
 
   _LIBCPP_HIDE_FROM_ABI static int __fseek(FILE* __file, pos_type __offset, int __whence);
@@ -802,12 +845,12 @@ template <class _CharT, class _Traits>
 typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>::underflow() {
   if (__file_ == nullptr)
     return traits_type::eof();
-  bool __initial = __read_mode();
+
   char_type __1buf;
-  if (this->gptr() == nullptr)
-    this->setg(std::addressof(__1buf), std::addressof(__1buf) + 1, std::addressof(__1buf) + 1);
-  const size_t __unget_sz = __initial ? 0 : std::min<size_t>((this->egptr() - this->eback()) / 2, 4);
-  int_type __c            = traits_type::eof();
+  __read_guard __guard(__1buf, *this);
+
+  const size_t __unget_sz = __guard.__unget_size();
+  int_type __c = traits_type::eof();
   if (this->gptr() == this->egptr()) {
     std::memmove(this->eback(), this->egptr() - __unget_sz, __unget_sz * sizeof(char_type));
     if (__always_noconv_) {
@@ -849,8 +892,6 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>
     }
   } else
     __c = traits_type::to_int_type(*this->gptr());
-  if (this->eback() == std::addressof(__1buf))
-    this->setg(nullptr, nullptr, nullptr);
   return __c;
 }
 
@@ -1141,20 +1182,6 @@ void basic_filebuf<_CharT, _Traits>::imbue(const locale& __loc) {
   }
 }
 
-template <class _CharT, class _Traits>
-bool basic_filebuf<_CharT, _Traits>::__read_mode() {
-  if (!(__cm_ & ios_base::in)) {
-    this->setp(nullptr, nullptr);
-    if (__always_noconv_)
-      this->setg((char_type*)__extbuf_, (char_type*)__extbuf_ + __ebs_, (char_type*)__extbuf_ + __ebs_);
-    else
-      this->setg(__intbuf_, __intbuf_ + __ibs_, __intbuf_ + __ibs_);
-    __cm_ = ios_base::in;
-    return true;
-  }
-  return false;
-}
-
 template <class _CharT, class _Traits>
 void basic_filebuf<_CharT, _Traits>::__write_mode() {
   if (!(__cm_ & ios_base::out)) {
diff --git a/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
new file mode 100644
index 0000000000000..6fe86c31590f8
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
@@ -0,0 +1 @@
+This is a bunch of data so the test can read some stuff and not instantly run out of data to read.
diff --git a/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp
new file mode 100644
index 0000000000000..d97d09e4c0ce9
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FILE_DEPENDENCIES: test.dat
+
+// <fstream>
+
+// streamsize basic_filebuf::xsgetn(char_type*, streamsize);
+
+// Test that xsgetn buffers properly
+
+#include <cassert>
+#include <fstream>
+
+int main(int, char**) {
+  std::ifstream is("test.dat");
+  char buffer[12];
+  is.rdbuf()->pubsetbuf(buffer, 12);
+  assert(is.is_open());
+  { // Check that we can unget() when reading a single character
+    char buf[1];
+    is.read(buf, 1);
+    assert(buf[0] == 'T');
+    is.unget();
+    assert(is.good());
+  }
+  { // Check that unget() works as expected when the remainder is smaller than the buffer
+    char buf[17];
+    buf[16] = '\0';
+    is.read(buf, 16);
+    assert(buf == std::string("This is a bunch "));
+    for (size_t i = 0; i != 4; ++i)
+      is.unget();
+    assert(is.good());
+    is.read(buf, 4);
+    buf[4] = '\0';
+    assert(buf == std::string("nch "));
+  }
+  { // Check that unget() works as expected when the remainder is larger than the buffer
+    char buf[33];
+    buf[32] = '\0';
+    is.read(buf, 32);
+    assert(buf == std::string("of data so the test can read som"));
+    for (size_t i = 0; i != 4; ++i)
+      is.unget();
+    assert(is.good());
+    is.read(buf, 4);
+    buf[4] = '\0';
+    assert(is.good());
+    assert(buf == std::string(" som"));
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
index d9ccc2fe62914..35c4787e2a29f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
@@ -25,6 +25,14 @@
 
 #include "test_macros.h"
 
+void check_unget(std::filebuf* fb, char expected) {
+  auto c = fb->sungetc();
+  if (c != EOF) {
+    assert(c == expected);
+    assert(fb->sbumpc() == expected);
+  }
+}
+
 int main(int, char**) {
   std::vector<char> stream_buffer(10);
   std::ifstream fs("xsgetn.test.dat");
@@ -40,33 +48,39 @@ int main(int, char**) {
   { // Check that a read smaller than the buffer works fine
     assert(fb->sgetn(test_buffer.data(), 5) == 5);
     assert(std::string(test_buffer.data(), 5) == "this ");
+    check_unget(fb, ' ');
   }
   { // Check that reading up to the buffer end works fine
     assert(fb->sgetn(test_buffer.data(), 5) == 5);
     assert(std::string(test_buffer.data(), 5) == "is so");
+    check_unget(fb, 'o');
   }
   { // Check that reading from an empty buffer, but more than the buffer can
     // hold works fine
     test_buffer.resize(12);
     assert(fb->sgetn(test_buffer.data(), 12) == 12);
     assert(std::string(test_buffer.data(), 12) == "me random da");
+    check_unget(fb, 'a');
   }
   { // Check that reading from a non-empty buffer, and more than the buffer can
     // hold works fine Fill the buffer up
     test_buffer.resize(2);
     assert(fb->sgetn(test_buffer.data(), 2) == 2);
     assert(std::string(test_buffer.data(), 2) == "ta");
+    check_unget(fb, 'a');
 
     // Do the actual check
     test_buffer.resize(12);
     assert(fb->sgetn(test_buffer.data(), 12) == 12);
     assert(std::string(test_buffer.data(), 12) == " to be able ");
+    check_unget(fb, ' ');
   }
   { // Check that trying to read more than the file size works fine
     test_buffer.resize(30);
     assert(fb->sgetn(test_buffer.data(), 30) == 24);
     test_buffer.resize(24);
     assert(std::string(test_buffer.data(), 24) == "to test buffer behaviour");
+    check_unget(fb, 'r');
   }
   { // Ensure that the read fails gracefully with an unopened ifstream
     // See https://llvm.org/PR168628


From libcxx-commits at lists.llvm.org  Tue Jul 21 04:16:48 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 04:16:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a5f5520.492e23e4.d19bc.8d00@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/210951

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:17:14 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 04:17:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a5f553a.7270d2fd.a9bd6.cf97@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

After #<!-- -->206453 we don't correctly handle `unget()` anymore. This fixes the issue by updating the internal buffer to contain the tail of the read data.

Fixes #<!-- -->210203


---
Full diff: https://github.com/llvm/llvm-project/pull/210951.diff


4 Files Affected:

- (modified) libcxx/include/fstream (+52-25) 
- (added) libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat (+1) 
- (added) libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp (+59) 
- (modified) libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp (+14) 


``````````diff
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..bd94bb4bf4b90 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -311,6 +311,8 @@ protected:
 
   _LIBCPP_HIDE_FROM_ABI_VIRTUAL streamsize xsgetn(char_type* __str, streamsize __len) override {
     if (__file_ && __always_noconv_) {
+      char_type __1buf;
+      __read_guard __guard(__1buf, *this);
       const streamsize __n = std::min(this->egptr() - this->gptr(), __len);
       if (__n != 0) {
         traits_type::copy(__str, this->gptr(), __n);
@@ -319,9 +321,16 @@ protected:
       const streamsize __remainder    = __len - __n;
       const streamsize __buffer_space = this->egptr() - this->eback();
 
-      if (__remainder >= __buffer_space)
-        return std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
-      else if (__remainder > 0)
+      if (__remainder >= __buffer_space) {
+        auto __res = std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
+
+        // Copy the buffer-sized tail into the buffer so that `unget`ting works correctly
+        auto __buf_size = std::min<size_t>(__res, this->egptr() - this->eback());
+        traits_type::copy(this->eback(), __str + __res - __buf_size, __buf_size);
+        this->setg(this->eback(), this->eback() + __buf_size, this->eback() + __buf_size);
+
+        return __res;
+      } else if (__remainder > 0)
         return basic_streambuf<_CharT, _Traits>::xsgetn(__str + __n, __remainder) + __n;
       return __n;
     }
@@ -383,7 +392,41 @@ private:
   bool __owns_ib_;
   bool __always_noconv_;
 
-  bool __read_mode();
+  struct [[__nodiscard__]] __read_guard {
+    basic_filebuf& __self_;
+    size_t __unget_size_;
+    char_type& __1buf_;
+
+    __read_guard(char_type& __1buf, basic_filebuf& __self) : __self_(__self), __1buf_(__1buf) {
+      if (__self.__cm_ & ios_base::in) {
+        __unget_size_ = std::min<size_t>((__self.egptr() - __self.eback()) / 2, 4);
+      } else {
+        __self.setp(nullptr, nullptr);
+        if (__self.__always_noconv_) {
+          __self.setg(reinterpret_cast<char_type*>(__self.__extbuf_),
+                      reinterpret_cast<char_type*>(__self.__extbuf_) + __self.__ebs_,
+                      reinterpret_cast<char_type*>(__self.__extbuf_) + __self.__ebs_);
+        } else {
+          __self.setg(__self.__intbuf_, __self.__intbuf_ + __self.__ibs_, __self.__intbuf_ + __self.__ibs_);
+        }
+        __self.__cm_ = ios_base::in;
+        __unget_size_ = 0;
+      }
+
+      if (__self.gptr() == nullptr)
+        __self.setg(std::addressof(__1buf_), std::addressof(__1buf_) + 1, std::addressof(__1buf_) + 1);
+    }
+
+    size_t __unget_size() {
+      return __unget_size_;
+    }
+
+    ~__read_guard() {
+      if (__self_.eback() == std::addressof(__1buf_))
+        __self_.setg(nullptr, nullptr, nullptr);
+    }
+  };
+
   void __write_mode();
 
   _LIBCPP_HIDE_FROM_ABI static int __fseek(FILE* __file, pos_type __offset, int __whence);
@@ -802,12 +845,12 @@ template <class _CharT, class _Traits>
 typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>::underflow() {
   if (__file_ == nullptr)
     return traits_type::eof();
-  bool __initial = __read_mode();
+
   char_type __1buf;
-  if (this->gptr() == nullptr)
-    this->setg(std::addressof(__1buf), std::addressof(__1buf) + 1, std::addressof(__1buf) + 1);
-  const size_t __unget_sz = __initial ? 0 : std::min<size_t>((this->egptr() - this->eback()) / 2, 4);
-  int_type __c            = traits_type::eof();
+  __read_guard __guard(__1buf, *this);
+
+  const size_t __unget_sz = __guard.__unget_size();
+  int_type __c = traits_type::eof();
   if (this->gptr() == this->egptr()) {
     std::memmove(this->eback(), this->egptr() - __unget_sz, __unget_sz * sizeof(char_type));
     if (__always_noconv_) {
@@ -849,8 +892,6 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>
     }
   } else
     __c = traits_type::to_int_type(*this->gptr());
-  if (this->eback() == std::addressof(__1buf))
-    this->setg(nullptr, nullptr, nullptr);
   return __c;
 }
 
@@ -1141,20 +1182,6 @@ void basic_filebuf<_CharT, _Traits>::imbue(const locale& __loc) {
   }
 }
 
-template <class _CharT, class _Traits>
-bool basic_filebuf<_CharT, _Traits>::__read_mode() {
-  if (!(__cm_ & ios_base::in)) {
-    this->setp(nullptr, nullptr);
-    if (__always_noconv_)
-      this->setg((char_type*)__extbuf_, (char_type*)__extbuf_ + __ebs_, (char_type*)__extbuf_ + __ebs_);
-    else
-      this->setg(__intbuf_, __intbuf_ + __ibs_, __intbuf_ + __ibs_);
-    __cm_ = ios_base::in;
-    return true;
-  }
-  return false;
-}
-
 template <class _CharT, class _Traits>
 void basic_filebuf<_CharT, _Traits>::__write_mode() {
   if (!(__cm_ & ios_base::out)) {
diff --git a/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
new file mode 100644
index 0000000000000..6fe86c31590f8
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
@@ -0,0 +1 @@
+This is a bunch of data so the test can read some stuff and not instantly run out of data to read.
diff --git a/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp
new file mode 100644
index 0000000000000..d97d09e4c0ce9
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FILE_DEPENDENCIES: test.dat
+
+// <fstream>
+
+// streamsize basic_filebuf::xsgetn(char_type*, streamsize);
+
+// Test that xsgetn buffers properly
+
+#include <cassert>
+#include <fstream>
+
+int main(int, char**) {
+  std::ifstream is("test.dat");
+  char buffer[12];
+  is.rdbuf()->pubsetbuf(buffer, 12);
+  assert(is.is_open());
+  { // Check that we can unget() when reading a single character
+    char buf[1];
+    is.read(buf, 1);
+    assert(buf[0] == 'T');
+    is.unget();
+    assert(is.good());
+  }
+  { // Check that unget() works as expected when the remainder is smaller than the buffer
+    char buf[17];
+    buf[16] = '\0';
+    is.read(buf, 16);
+    assert(buf == std::string("This is a bunch "));
+    for (size_t i = 0; i != 4; ++i)
+      is.unget();
+    assert(is.good());
+    is.read(buf, 4);
+    buf[4] = '\0';
+    assert(buf == std::string("nch "));
+  }
+  { // Check that unget() works as expected when the remainder is larger than the buffer
+    char buf[33];
+    buf[32] = '\0';
+    is.read(buf, 32);
+    assert(buf == std::string("of data so the test can read som"));
+    for (size_t i = 0; i != 4; ++i)
+      is.unget();
+    assert(is.good());
+    is.read(buf, 4);
+    buf[4] = '\0';
+    assert(is.good());
+    assert(buf == std::string(" som"));
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
index d9ccc2fe62914..35c4787e2a29f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
@@ -25,6 +25,14 @@
 
 #include "test_macros.h"
 
+void check_unget(std::filebuf* fb, char expected) {
+  auto c = fb->sungetc();
+  if (c != EOF) {
+    assert(c == expected);
+    assert(fb->sbumpc() == expected);
+  }
+}
+
 int main(int, char**) {
   std::vector<char> stream_buffer(10);
   std::ifstream fs("xsgetn.test.dat");
@@ -40,33 +48,39 @@ int main(int, char**) {
   { // Check that a read smaller than the buffer works fine
     assert(fb->sgetn(test_buffer.data(), 5) == 5);
     assert(std::string(test_buffer.data(), 5) == "this ");
+    check_unget(fb, ' ');
   }
   { // Check that reading up to the buffer end works fine
     assert(fb->sgetn(test_buffer.data(), 5) == 5);
     assert(std::string(test_buffer.data(), 5) == "is so");
+    check_unget(fb, 'o');
   }
   { // Check that reading from an empty buffer, but more than the buffer can
     // hold works fine
     test_buffer.resize(12);
     assert(fb->sgetn(test_buffer.data(), 12) == 12);
     assert(std::string(test_buffer.data(), 12) == "me random da");
+    check_unget(fb, 'a');
   }
   { // Check that reading from a non-empty buffer, and more than the buffer can
     // hold works fine Fill the buffer up
     test_buffer.resize(2);
     assert(fb->sgetn(test_buffer.data(), 2) == 2);
     assert(std::string(test_buffer.data(), 2) == "ta");
+    check_unget(fb, 'a');
 
     // Do the actual check
     test_buffer.resize(12);
     assert(fb->sgetn(test_buffer.data(), 12) == 12);
     assert(std::string(test_buffer.data(), 12) == " to be able ");
+    check_unget(fb, ' ');
   }
   { // Check that trying to read more than the file size works fine
     test_buffer.resize(30);
     assert(fb->sgetn(test_buffer.data(), 30) == 24);
     test_buffer.resize(24);
     assert(std::string(test_buffer.data(), 24) == "to test buffer behaviour");
+    check_unget(fb, 'r');
   }
   { // Ensure that the read fails gracefully with an unopened ifstream
     // See https://llvm.org/PR168628

``````````

</details>


https://github.com/llvm/llvm-project/pull/210951

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:18:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 04:18:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a5f5584.f3151ff8.37ac76.5ae6@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions ,cpp -- libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp libcxx/include/fstream libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index bd94bb4bf..6f7f87489 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -409,7 +409,7 @@ private:
         } else {
           __self.setg(__self.__intbuf_, __self.__intbuf_ + __self.__ibs_, __self.__intbuf_ + __self.__ibs_);
         }
-        __self.__cm_ = ios_base::in;
+        __self.__cm_  = ios_base::in;
         __unget_size_ = 0;
       }
 
@@ -417,9 +417,7 @@ private:
         __self.setg(std::addressof(__1buf_), std::addressof(__1buf_) + 1, std::addressof(__1buf_) + 1);
     }
 
-    size_t __unget_size() {
-      return __unget_size_;
-    }
+    size_t __unget_size() { return __unget_size_; }
 
     ~__read_guard() {
       if (__self_.eback() == std::addressof(__1buf_))
@@ -850,7 +848,7 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>
   __read_guard __guard(__1buf, *this);
 
   const size_t __unget_sz = __guard.__unget_size();
-  int_type __c = traits_type::eof();
+  int_type __c            = traits_type::eof();
   if (this->gptr() == this->egptr()) {
     std::memmove(this->eback(), this->egptr() - __unget_sz, __unget_sz * sizeof(char_type));
     if (__always_noconv_) {

``````````

</details>


https://github.com/llvm/llvm-project/pull/210951

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:21:27 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 04:21:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] f8e2588 - [libc++] Include sizes in
	string benchmark names (#210795)
Message-ID: <6a5f5637.6c8668f2.1f24.c588@mx.google.com>


Author: Louis Dionne
Date: 2026-07-21T07:21:22-04:00
New Revision: f8e258866980da486756e241016dd33ae68450be

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

LOG: [libc++] Include sizes in string benchmark names (#210795)

Otherwise we'd have duplicate benchmark names.

Added: 
    

Modified: 
    libcxx/test/benchmarks/containers/string.bench.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index e6cc60304eb60..776ee5f58b7e2 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -184,16 +184,20 @@ int main(int argc, char** argv) {
         };
 
     bench("std::basic_string::operator=(const value_type*) (opaque)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
 
     bench("std::basic_string::operator=(const value_type*) (opaque)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
 
     bench("std::basic_string::operator=(const value_type*) (transparent)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
 
     bench("std::basic_string::operator=(const value_type*) (transparent)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
   }
 
   // [string.capacity]
@@ -352,16 +356,20 @@ int main(int argc, char** argv) {
         };
 
     bench("std::basic_string == const CharT* (opaque)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
 
     bench("std::basic_string == const CharT* (opaque)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
 
     bench("std::basic_string == const CharT* (transparent)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
 
     bench("std::basic_string == const CharT* (transparent)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
   }
 
   {
@@ -395,16 +403,20 @@ int main(int argc, char** argv) {
         };
 
     bench("std::basic_string::compare(const CharT*) (opaque)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
 
     bench("std::basic_string::compare(const CharT*) (opaque)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
 
     bench("std::basic_string::compare(const CharT*) (transparent)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
 
     bench("std::basic_string::compare(const CharT*) (transparent)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
   }
 
   // [string.compare]
@@ -433,16 +445,20 @@ int main(int argc, char** argv) {
         };
 
     bench("std::basic_string == std::basic_string (opaque)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
 
     bench("std::basic_string == std::basic_string (opaque)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
 
     bench("std::basic_string == std::basic_string (transparent)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
 
     bench("std::basic_string == std::basic_string (transparent)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
   }
 
   {
@@ -470,16 +486,20 @@ int main(int argc, char** argv) {
     // These also effectively cover operator<, since operator< is just forwarding to compare.
 
     bench("std::basic_string::compare(const std::basic_string&) (opaque)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
 
     bench("std::basic_string::compare(const std::basic_string&) (opaque)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
 
     bench("std::basic_string::compare(const std::basic_string&) (transparent)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
 
     bench("std::basic_string::compare(const std::basic_string&) (transparent)",
-          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
   }
 
   benchmark::Initialize(&argc, argv);


        

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:21:30 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 04:21:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Include sizes in string
	benchmark names (PR #210795)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210795@github.com>
Message-ID: <6a5f563a.d1496011.199a59.183e@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/210795

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:23:04 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 04:23:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 7f49d95 - [libc++][NFC] Fix synopsis
 comment for error_condition::message() (#210724)
Message-ID: <6a5f5698.510f576d.2f694c.bd6f@mx.google.com>


Author: Louis Dionne
Date: 2026-07-21T07:22:59-04:00
New Revision: 7f49d95c09e8a22a6de737ae6006edbb3a0adfca

URL: https://github.com/llvm/llvm-project/commit/7f49d95c09e8a22a6de737ae6006edbb3a0adfca
DIFF: https://github.com/llvm/llvm-project/commit/7f49d95c09e8a22a6de737ae6006edbb3a0adfca.diff

LOG: [libc++][NFC] Fix synopsis comment for error_condition::message() (#210724)

The method was marked as noexcept in the synopsis, but it's neither
marked noexcept in the spec nor in our implementation.

Added: 
    

Modified: 
    libcxx/include/system_error

Removed: 
    


################################################################################
diff  --git a/libcxx/include/system_error b/libcxx/include/system_error
index ef760453d9893..208a586af0dc2 100644
--- a/libcxx/include/system_error
+++ b/libcxx/include/system_error
@@ -98,7 +98,7 @@ public:
     // observers:
     int value() const noexcept;
     const error_category& category() const noexcept;
-    string message() const noexcept;
+    string message() const;
     explicit operator bool() const noexcept;
 };
 


        

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:23:07 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 04:23:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Fix synopsis comment for
 error_condition::message() (PR #210724)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210724@github.com>
Message-ID: <6a5f569b.5a0412d2.3daf32.6069@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/210724

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:23:57 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 04:23:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] df4bb1f - [libc++] Compose the test suite
 Lit site config from harness and installation substitutions (#209638)
Message-ID: <6a5f56cd.510f576d.2f694c.dbed@mx.google.com>


Author: Louis Dionne
Date: 2026-07-21T07:23:53-04:00
New Revision: df4bb1f3106d39e5c6b767a28c0531efbbebc2d2

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

LOG: [libc++] Compose the test suite Lit site config from harness and installation substitutions (#209638)

This patch restructures how libc++'s Lit site config is generated.
Instead of relying on the user-selected configuration file to include
the CMake bridge, it composes a site configuration from multiple
independent bits.

Importantly, it splits substitutions that pertain to the harness setup
(e.g. where to find Python) and substitutions that pertain to libc++
itself (e.g. the path to libc++ headers). This split and the top-level
composition of a site config file are incremental steps towards
decoupling the test suite from libc++'s own build.

Added: 
    libcxx/test/configs/harness-configuration.cfg.in
    libcxx/test/configs/libcxx-substitutions.cfg.in
    libcxx/test/configs/lit.site.cfg.in

Modified: 
    libcxx/test/CMakeLists.txt
    libcxx/test/configs/amdgpu-libc++-shared.cfg.in
    libcxx/test/configs/apple-libc++-shared.cfg.in
    libcxx/test/configs/apple-libc++-system.cfg.in
    libcxx/test/configs/armv7m-picolibc-libc++.cfg.in
    libcxx/test/configs/ibm-libc++-shared.cfg.in
    libcxx/test/configs/llvm-libc++-android.cfg.in
    libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
    libcxx/test/configs/llvm-libc++-mingw.cfg.in
    libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in
    libcxx/test/configs/llvm-libc++-shared-gcc.cfg.in
    libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in
    libcxx/test/configs/llvm-libc++-shared.cfg.in
    libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in
    libcxx/test/configs/llvm-libc++-static.cfg.in
    libcxx/test/configs/nvptx-libc++-shared.cfg.in
    libcxx/test/configs/stdlib-libstdc++.cfg.in
    libcxx/test/configs/stdlib-native.cfg.in

Removed: 
    libcxx/test/configs/cmake-bridge.cfg.in


################################################################################
diff  --git a/libcxx/test/CMakeLists.txt b/libcxx/test/CMakeLists.txt
index 39d383922e1df..8fd34c086a992 100644
--- a/libcxx/test/CMakeLists.txt
+++ b/libcxx/test/CMakeLists.txt
@@ -55,12 +55,27 @@ serialize_lit_params_list(SERIALIZED_LIT_PARAMS LIBCXX_TEST_PARAMS)
 
 include(AddLLVM) # for configure_lit_site_cfg and add_lit_testsuite
 
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/configs/cmake-bridge.cfg.in"
-                "${CMAKE_CURRENT_BINARY_DIR}/cmake-bridge.cfg"
-                @ONLY)
+# The test suite is driven by a generated lit.site.cfg that loads the various other configuration
+# files needed by it. In order:
+# - The harness configuration, which defines substitutions needed for the test suite's own function
+#   (e.g. the path to Python).
+# - The libc++ substitutions file, which adds libc++ specific substitutions, such as the location of
+#   include paths. Those are needed for libc++ specific tests.
+# - The user-selected site configuration, which defines substitutions that determine e.g. how to
+#   build, link and run the tests.
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/configs/harness-configuration.cfg.in"
+               "${CMAKE_CURRENT_BINARY_DIR}/harness-configuration.cfg"
+               @ONLY)
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/configs/libcxx-substitutions.cfg.in"
+               "${CMAKE_CURRENT_BINARY_DIR}/libcxx-substitutions.cfg"
+               @ONLY)
+string(REGEX REPLACE "\\.in$" "" _resolved_config_name "${LIBCXX_TEST_CONFIG}")
+get_filename_component(_resolved_config_name "${_resolved_config_name}" NAME)
+set(LIBCXX_RESOLVED_TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/${_resolved_config_name}")
+configure_file("${LIBCXX_TEST_CONFIG}" "${LIBCXX_RESOLVED_TEST_CONFIG}" @ONLY)
 
 configure_lit_site_cfg(
-  "${LIBCXX_TEST_CONFIG}"
+  "${CMAKE_CURRENT_SOURCE_DIR}/configs/lit.site.cfg.in"
   ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
   MAIN_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py")
 

diff  --git a/libcxx/test/configs/amdgpu-libc++-shared.cfg.in b/libcxx/test/configs/amdgpu-libc++-shared.cfg.in
index 80c80ebaa8bd0..b34eced0bf1e0 100644
--- a/libcxx/test/configs/amdgpu-libc++-shared.cfg.in
+++ b/libcxx/test/configs/amdgpu-libc++-shared.cfg.in
@@ -1,5 +1,3 @@
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
   f'--target={config.target_triple} -Wno-multi-gpu -flto -mcpu=native'))
 config.substitutions.append(('%{compile_flags}',

diff  --git a/libcxx/test/configs/apple-libc++-shared.cfg.in b/libcxx/test/configs/apple-libc++-shared.cfg.in
index a361b2b8adc00..e6d61dffbbd97 100644
--- a/libcxx/test/configs/apple-libc++-shared.cfg.in
+++ b/libcxx/test/configs/apple-libc++-shared.cfg.in
@@ -14,8 +14,6 @@
 # issues since the upstream libc++ does not contain all the symbols provided by
 # the system library.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config, libcxx.test.dsl

diff  --git a/libcxx/test/configs/apple-libc++-system.cfg.in b/libcxx/test/configs/apple-libc++-system.cfg.in
index e87f920e7b93d..3bc9fe8956f14 100644
--- a/libcxx/test/configs/apple-libc++-system.cfg.in
+++ b/libcxx/test/configs/apple-libc++-system.cfg.in
@@ -7,8 +7,6 @@ import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config, libcxx.test.dsl
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
     '-isysroot {}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else ''
 ))

diff  --git a/libcxx/test/configs/armv7m-picolibc-libc++.cfg.in b/libcxx/test/configs/armv7m-picolibc-libc++.cfg.in
index f0782c28ce3e5..181263e95d62b 100644
--- a/libcxx/test/configs/armv7m-picolibc-libc++.cfg.in
+++ b/libcxx/test/configs/armv7m-picolibc-libc++.cfg.in
@@ -1,5 +1,3 @@
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 libc_linker_script = '@CMAKE_INSTALL_PREFIX@/lib/picolibcpp.ld'
 
 config.substitutions.append(('%{flags}', '--sysroot=@CMAKE_INSTALL_PREFIX@'))

diff  --git a/libcxx/test/configs/cmake-bridge.cfg.in b/libcxx/test/configs/harness-configuration.cfg.in
similarity index 52%
rename from libcxx/test/configs/cmake-bridge.cfg.in
rename to libcxx/test/configs/harness-configuration.cfg.in
index 20b7c1e9bc357..c527d60c64af5 100644
--- a/libcxx/test/configs/cmake-bridge.cfg.in
+++ b/libcxx/test/configs/harness-configuration.cfg.in
@@ -4,11 +4,11 @@
 
 #
 # This file performs the bridge between the CMake configuration and the Lit
-# configuration files by setting up the LitConfig object and various Lit
-# substitutions from CMake variables.
+# configuration files by setting up the LitConfig object and the Lit substitutions
+# that are independent of the library under test.
 #
-# Individual configuration files can take advantage of this bridge by
-# loading the file and then setting up the remaining Lit substitutions.
+# It is loaded by the generated lit.site.cfg, before the library-under-test
+# substitutions (libcxx-substitutions.cfg) and the site configuration file.
 #
 
 import os, site
@@ -25,13 +25,6 @@ config.recursiveExpansionLimit = 10
 config.test_exec_root = os.path.join('@LIBCXX_BINARY_DIR@', 'test')
 
 # Add substitutions for bootstrapping the test suite configuration
-config.substitutions.append(('%{bin-dir}', '@LIBCXX_BINARY_DIR@'))
 config.substitutions.append(('%{libcxx-dir}', '@LIBCXX_SOURCE_DIR@'))
-config.substitutions.append(('%{install-prefix}', '@LIBCXX_TESTING_INSTALL_PREFIX@'))
-config.substitutions.append(('%{include-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_INCLUDE_DIR@'))
-config.substitutions.append(('%{target-include-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_INCLUDE_TARGET_DIR@'))
-config.substitutions.append(('%{lib-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_LIBRARY_DIR@'))
-config.substitutions.append(('%{module-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_MODULES_DIR@'))
-config.substitutions.append(('%{test-tools-dir}', '@LIBCXX_TEST_TOOLS_PATH@'))
 config.substitutions.append(('%{benchmark_flags}', '-I @LIBCXX_BINARY_DIR@/test/benchmarks/google-benchmark/include -L @LIBCXX_BINARY_DIR@/test/benchmarks/google-benchmark/lib -L @LIBCXX_BINARY_DIR@/test/benchmarks/google-benchmark/lib64 -l benchmark'))
 config.substitutions.append(("%{python}", shlex.quote(sys.executable)))

diff  --git a/libcxx/test/configs/ibm-libc++-shared.cfg.in b/libcxx/test/configs/ibm-libc++-shared.cfg.in
index 863b1ae85b0ed..3fcf64e3537e0 100644
--- a/libcxx/test/configs/ibm-libc++-shared.cfg.in
+++ b/libcxx/test/configs/ibm-libc++-shared.cfg.in
@@ -2,8 +2,6 @@
 # AIX using a shared library.
 #
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 import lit.util
 if lit.util.isAIXTriple(config.target_triple):
   # Add the AIX version to the triple here because there currently isn't a good

diff  --git a/libcxx/test/configs/libcxx-substitutions.cfg.in b/libcxx/test/configs/libcxx-substitutions.cfg.in
new file mode 100644
index 0000000000000..a74ff013219b2
--- /dev/null
+++ b/libcxx/test/configs/libcxx-substitutions.cfg.in
@@ -0,0 +1,13 @@
+ at AUTO_GEN_COMMENT@
+
+#
+# This file sets up Lit substitutions that describe paths related to libc++'s installation, which
+# are used by libc++ specific tests.
+#
+config.substitutions.append(('%{bin-dir}', '@LIBCXX_BINARY_DIR@'))
+config.substitutions.append(('%{install-prefix}', '@LIBCXX_TESTING_INSTALL_PREFIX@'))
+config.substitutions.append(('%{include-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_INCLUDE_DIR@'))
+config.substitutions.append(('%{target-include-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_INCLUDE_TARGET_DIR@'))
+config.substitutions.append(('%{lib-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_LIBRARY_DIR@'))
+config.substitutions.append(('%{module-dir}', '@LIBCXX_TESTING_INSTALL_PREFIX@/@LIBCXX_INSTALL_MODULES_DIR@'))
+config.substitutions.append(('%{test-tools-dir}', '@LIBCXX_TEST_TOOLS_PATH@'))

diff  --git a/libcxx/test/configs/lit.site.cfg.in b/libcxx/test/configs/lit.site.cfg.in
new file mode 100644
index 0000000000000..148bf5534453b
--- /dev/null
+++ b/libcxx/test/configs/lit.site.cfg.in
@@ -0,0 +1,11 @@
+ at AUTO_GEN_COMMENT@
+
+#
+# This is the top-level Lit site configuration. It loads, in order:
+#   1. the harness configuration,
+#   2. the substitutions that are specific to libc++, and
+#   3. the chosen site configuration file, which describes how to build, link and run the tests.
+#
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/harness-configuration.cfg')
+lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/libcxx-substitutions.cfg')
+lit_config.load_config(config, '@LIBCXX_RESOLVED_TEST_CONFIG@')

diff  --git a/libcxx/test/configs/llvm-libc++-android.cfg.in b/libcxx/test/configs/llvm-libc++-android.cfg.in
index 96c952d6bbc19..8b149207d61d4 100644
--- a/libcxx/test/configs/llvm-libc++-android.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-android.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's
 # libc++ using adb on Android.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 import re
 import site
 

diff  --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index 9dbf8a98dd5e4..885715eb42dfc 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's libc++
 # using a static library.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
     '-pthread' + (' -isysroot {}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else '')
 ))

diff  --git a/libcxx/test/configs/llvm-libc++-mingw.cfg.in b/libcxx/test/configs/llvm-libc++-mingw.cfg.in
index 44731713b427b..f96d4b12c918c 100644
--- a/libcxx/test/configs/llvm-libc++-mingw.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-mingw.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's libc++
 # using either a DLL or a static library, with MinGW/Clang on Windows.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}', ''))
 config.substitutions.append(('%{compile_flags}',
     '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'

diff  --git a/libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in b/libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in
index e6186a7c73c3a..5196ca32479ac 100644
--- a/libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-shared-clangcl.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's libc++
 # using a DLL, with Clang-cl on Windows.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 dbg_include = ''
 runtime_library = '@CMAKE_MSVC_RUNTIME_LIBRARY@'
 if runtime_library == '' or runtime_library.endswith('DLL'):

diff  --git a/libcxx/test/configs/llvm-libc++-shared-gcc.cfg.in b/libcxx/test/configs/llvm-libc++-shared-gcc.cfg.in
index 1b4ddc299759e..ad31e2811a8ab 100644
--- a/libcxx/test/configs/llvm-libc++-shared-gcc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-shared-gcc.cfg.in
@@ -2,8 +2,6 @@
 # using a shared library, with GCC. This is done 
diff erently from Clang because
 # we need to explicitly link against libm for some tests with GCC.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}', '-pthread'))
 config.substitutions.append(('%{compile_flags}',
     '-nostdinc++ -I %{target-include-dir} -I %{include-dir} -I %{libcxx-dir}/test/support'

diff  --git a/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in b/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in
index 476c5ca715a65..ce142b1a9d644 100644
--- a/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in
@@ -2,8 +2,6 @@
 # using a DLL, with Clang-cl on Windows. This variant sets _HAS_EXCEPTIONS = 0
 # which removes exception class definitions from the vcruntime.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 dbg_include = ''
 runtime_library = '@CMAKE_MSVC_RUNTIME_LIBRARY@'
 if runtime_library == '' or runtime_library.endswith('DLL'):

diff  --git a/libcxx/test/configs/llvm-libc++-shared.cfg.in b/libcxx/test/configs/llvm-libc++-shared.cfg.in
index 6d4a3836b2baa..71e56e732e796 100644
--- a/libcxx/test/configs/llvm-libc++-shared.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-shared.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's libc++
 # using a shared library.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
     '-pthread' + (' -isysroot {}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else '')
 ))

diff  --git a/libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in b/libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in
index 2f2b420d32190..4be1e1afa1c5b 100644
--- a/libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-static-clangcl.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's libc++
 # using a static library, with Clang-cl on Windows.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 dbg_include = ''
 runtime_library = '@CMAKE_MSVC_RUNTIME_LIBRARY@'
 if runtime_library == '' or runtime_library.endswith('DLL'):

diff  --git a/libcxx/test/configs/llvm-libc++-static.cfg.in b/libcxx/test/configs/llvm-libc++-static.cfg.in
index 45a44c13e76eb..c6a6ab2398079 100644
--- a/libcxx/test/configs/llvm-libc++-static.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-static.cfg.in
@@ -1,8 +1,6 @@
 # This testing configuration handles running the test suite against LLVM's libc++
 # using a static library.
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
     '-pthread' + (' -isysroot {}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else '')
 ))

diff  --git a/libcxx/test/configs/nvptx-libc++-shared.cfg.in b/libcxx/test/configs/nvptx-libc++-shared.cfg.in
index e07ed35da4d5a..c8dcac55e3596 100644
--- a/libcxx/test/configs/nvptx-libc++-shared.cfg.in
+++ b/libcxx/test/configs/nvptx-libc++-shared.cfg.in
@@ -1,5 +1,3 @@
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
   f'--target={config.target_triple} -Wno-multi-gpu -flto -march=native'))
 config.substitutions.append(('%{compile_flags}',

diff  --git a/libcxx/test/configs/stdlib-libstdc++.cfg.in b/libcxx/test/configs/stdlib-libstdc++.cfg.in
index 1a4b47cb1335b..429b1fe153938 100644
--- a/libcxx/test/configs/stdlib-libstdc++.cfg.in
+++ b/libcxx/test/configs/stdlib-libstdc++.cfg.in
@@ -13,8 +13,6 @@
 #                                                          --param stdlib=libstdc++
 #
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 import os, site
 site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
 import libcxx.test.params, libcxx.test.config, libcxx.test.dsl

diff  --git a/libcxx/test/configs/stdlib-native.cfg.in b/libcxx/test/configs/stdlib-native.cfg.in
index b827155fd579a..3f64e37cdbcfa 100644
--- a/libcxx/test/configs/stdlib-native.cfg.in
+++ b/libcxx/test/configs/stdlib-native.cfg.in
@@ -4,8 +4,6 @@
 # default when no special compiler flags are provided.
 #
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{flags}',
     '-pthread' + (' -isysroot {}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else '')
 ))


        

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:24:00 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 04:24:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Compose the test suite Lit site
 config from harness and installation substitutions (PR #209638)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209638@github.com>
Message-ID: <6a5f56d0.507fc713.239434.1d5e@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/209638

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:30:54 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 04:30:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f586e.1728eaf3.2a2f6.9961@mx.google.com>


================
@@ -482,6 +483,41 @@ int main(int argc, char** argv) {
           std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
   }
 
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> src(size, 'a');
+          auto getIterator = [&src](size_t i) {
+            // INT_MAX because we want overhead of ThrowingIterator without actually throwing
+            return ThrowingIterator<CharT>(src.data() + i, src.data() + src.size(), INT_MAX);
+          };
+          std::basic_string<CharT> string = src;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(string);
+            string.clear();
+            if constexpr (opaque)
+              benchmark::DoNotOptimize(src);
+            benchmark::DoNotOptimize(string.append(getIterator(0), getIterator(size)));
+          }
+        };
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (opaque)",
----------------
ldionne wrote:

The PR is https://github.com/llvm/llvm-project/pull/210795

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:32:34 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 04:32:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f58d2.b1416da1.3242c8.4af9@mx.google.com>


ldionne wrote:

@philnik777  I suspect the results in https://github.com/llvm/llvm-project/pull/210078#issuecomment-5032140617 aren't reliable because we are aggregating results for different sizes under the same benchmark name and taking their median. We need to fix the size-in-name issue first.

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:34:49 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 04:34:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] b8eebf2 - [libc++][NFC] Re-order
 availability mapping to `_LIBCPP_INTRODUCED_IN_LLVM_{N}` (#210694)
Message-ID: <6a5f5959.e624eb8d.4a8a7.5e5b@mx.google.com>


Author: h-vetinari
Date: 2026-07-21T07:34:43-04:00
New Revision: b8eebf2704f2ebb6894fd952e4f15fdb62270606

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

LOG: [libc++][NFC] Re-order availability mapping to `_LIBCPP_INTRODUCED_IN_LLVM_{N}` (#210694)

Noticed that the new additions (from 16f692338cf and 8a531c3608c) were placed
rather randomly. Put the mapping back to reverse chronological order, see 
783fd2f9d.

Added: 
    

Modified: 
    libcxx/include/__configuration/availability.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index 1fbd87ba52a64..0709deff47ea6 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -239,6 +239,18 @@
 
 #endif
 
+// This determines whether we assume that the internal std::__bad_variant_access_with_msg class
+// (which carries a message describing the cause of the failure in bad_variant_access::what())
+// provides a key function in the dylib. This allows centralizing its vtable and typeinfo instead
+// of having all TUs provide a weak definition that then gets deduplicated. When it is not available
+// in the dylib, what() is defined inline instead, so the descriptive message is provided regardless.
+#define _LIBCPP_AVAILABILITY_HAS_BAD_VARIANT_ACCESS_WITH_MSG_KEY_FUNCTION _LIBCPP_INTRODUCED_IN_LLVM_23
+// No attribute, since we've had bad_variant_access in the headers before
+
+// Controls whether the implementation for text_encoding::environment() is available
+#define _LIBCPP_AVAILABILITY_HAS_TEXT_ENCODING_ENVIRONMENT _LIBCPP_INTRODUCED_IN_LLVM_23
+#define _LIBCPP_AVAILABILITY_TEXT_ENCODING_ENVIRONMENT _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE
+
 // This controls the availability of new implementation of std::atomic's
 // wait, notify_one and notify_all. The new implementation uses
 // the native atomic wait/notify operations on platforms that support them
@@ -256,14 +268,6 @@
 #define _LIBCPP_AVAILABILITY_HAS_BAD_FUNCTION_CALL_GOOD_WHAT_MESSAGE _LIBCPP_INTRODUCED_IN_LLVM_21
 // No attribute, since we've had bad_function_call::what() in the headers before
 
-// This determines whether we assume that the internal std::__bad_variant_access_with_msg class
-// (which carries a message describing the cause of the failure in bad_variant_access::what())
-// provides a key function in the dylib. This allows centralizing its vtable and typeinfo instead
-// of having all TUs provide a weak definition that then gets deduplicated. When it is not available
-// in the dylib, what() is defined inline instead, so the descriptive message is provided regardless.
-#define _LIBCPP_AVAILABILITY_HAS_BAD_VARIANT_ACCESS_WITH_MSG_KEY_FUNCTION _LIBCPP_INTRODUCED_IN_LLVM_23
-// No attribute, since we've had bad_variant_access in the headers before
-
 // This controls the availability of floating-point std::from_chars functions.
 // These overloads were added later than the integer overloads.
 #define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20
@@ -323,10 +327,6 @@
 #  define _LIBCPP_AVAILABILITY_HAS_ADDITIONAL_IOSTREAM_EXPLICIT_INSTANTIATIONS_1 0
 #endif
 
-// Controls whether the implementation for text_encoding::environment() is available
-#define _LIBCPP_AVAILABILITY_HAS_TEXT_ENCODING_ENVIRONMENT _LIBCPP_INTRODUCED_IN_LLVM_23
-#define _LIBCPP_AVAILABILITY_TEXT_ENCODING_ENVIRONMENT _LIBCPP_INTRODUCED_IN_LLVM_23_ATTRIBUTE
-
 // Only define a bunch of symbols in the dylib if we need to be compatible with LLVM 7 headers or older
 #  if defined(_LIBCPP_BUILDING_LIBRARY) && _LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 8
 #    define _LIBCPP_HIDE_FROM_ABI_SINCE_LLVM8


        

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:34:51 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 04:34:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][NFC] Re-order availability
 mapping to `_LIBCPP_INTRODUCED_IN_LLVM_{N}` (PR #210694)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210694@github.com>
Message-ID: <6a5f595b.6c8668f2.1f24.d038@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/210694

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:35:09 2026
From: libcxx-commits at lists.llvm.org (Pavel Novikov via libcxx-commits)
Date: Tue, 21 Jul 2026 04:35:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f596d.9edb5c9d.280be3.6577@mx.google.com>


https://github.com/toughengineer updated https://github.com/llvm/llvm-project/pull/210078

>From 34419f2461f3cc4ac0c285cfbb76e64f016db652 Mon Sep 17 00:00:00 2001
From: Pavel Novikov <dev-ape at yandex.ru>
Date: Thu, 16 Jul 2026 17:43:25 +0300
Subject: [PATCH 1/2] [libc++] fix minor performance issue

---
 libcxx/include/string | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index a297a05813cee..957eb15e4fe1f 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1359,13 +1359,13 @@ public:
   template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
   append(_ForwardIterator __first, _ForwardIterator __last) {
-    size_type __sz  = size();
-    size_type __cap = capacity();
-    size_type __n   = static_cast<size_type>(std::distance(__first, __last));
-    if (__n == 0)
+    if (__first == __last)
       return *this;
 
     if (__string_is_trivial_iterator_v<_ForwardIterator> && !__addr_in_range(*__first)) {
+      size_type __sz  = size();
+      size_type __cap = capacity();
+      size_type __n   = static_cast<size_type>(std::distance(__first, __last));
       if (__cap - __sz < __n)
         __grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
       __annotate_increase(__n);

>From 115bfb4e9d410527120d7d1a3120e51ae28e2403 Mon Sep 17 00:00:00 2001
From: Pavel Novikov <dev-ape at yandex.ru>
Date: Tue, 21 Jul 2026 14:34:36 +0300
Subject: [PATCH 2/2] [libc++] added benchmarks for `string::append()`

---
 .../benchmarks/containers/string.bench.cpp    | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index 776ee5f58b7e2..2f7cd59fe227d 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -17,6 +17,7 @@
 #include "benchmark/benchmark.h"
 #include "make_string.h"
 #include "test_macros.h"
+#include "test_iterators.h"
 
 constexpr size_t small_size = 5;
 constexpr size_t large_size = 30;
@@ -498,6 +499,45 @@ int main(int argc, char** argv) {
           [](auto bm) { bm->Arg(small_size); }); // for naming
 
     bench("std::basic_string::compare(const std::basic_string&) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
+  }
+
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> src(size, 'a');
+          auto getIterator = [&src](size_t i) {
+            // INT_MAX because we want overhead of ThrowingIterator without actually throwing
+            return ThrowingIterator<CharT>(src.data() + i, src.data() + src.size(), INT_MAX);
+          };
+          std::basic_string<CharT> string = src;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(string);
+            string.clear();
+            if constexpr (opaque)
+              benchmark::DoNotOptimize(src);
+            benchmark::DoNotOptimize(string.append(getIterator(0), getIterator(size)));
+          }
+        };
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (transparent)",
           std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}),
           [](auto bm) { bm->Arg(large_size); }); // for naming
   }


From libcxx-commits at lists.llvm.org  Tue Jul 21 04:36:58 2026
From: libcxx-commits at lists.llvm.org (Pavel Novikov via libcxx-commits)
Date: Tue, 21 Jul 2026 04:36:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f59da.bd93e731.30f155.2c8c@mx.google.com>


toughengineer wrote:

> We need to fix the size-in-name issue first.

done

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:39:44 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 04:39:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++]
 __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp,
 _Tp> removes cv-ref from _Tp (PR #208950)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208950@github.com>
Message-ID: <6a5f5a80.e5907290.281687.d170@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/208950

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:40:27 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 04:40:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] c40a686 - [libc++] Strip cv-refs in
 __desugars_to specialization for integral types (#208950)
Message-ID: <6a5f5aab.a1accbb4.5eca2.f848@mx.google.com>


Author: Michael G. Kazakov
Date: 2026-07-21T07:40:22-04:00
New Revision: c40a68644c550dcfa3c3d0b560e3aeea1d159d79

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

LOG: [libc++] Strip cv-refs in __desugars_to specialization for integral types (#208950)

This PR passes the argument type through `__remove_cvref_t` before
passing to `is_integral`.

Fixes #208236

Added: 
    

Modified: 
    libcxx/include/__algorithm/comp.h
    libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__algorithm/comp.h b/libcxx/include/__algorithm/comp.h
index 38e2fb9f5e744..a83b6a8938998 100644
--- a/libcxx/include/__algorithm/comp.h
+++ b/libcxx/include/__algorithm/comp.h
@@ -13,6 +13,7 @@
 #include <__type_traits/desugars_to.h>
 #include <__type_traits/is_generic_transparent_comparator.h>
 #include <__type_traits/is_integral.h>
+#include <__type_traits/remove_cvref.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -47,7 +48,8 @@ template <class _Tp>
 inline const bool __desugars_to_v<__less_tag, __less<>, _Tp, _Tp> = true;
 
 template <class _Tp>
-inline const bool __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp, _Tp> = is_integral<_Tp>::value;
+inline const bool __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp, _Tp> =
+    is_integral<__remove_cvref_t<_Tp> >::value;
 
 template <>
 inline const bool __is_generic_transparent_comparator_v<__less<> > = true;

diff  --git a/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp b/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp
index 9134ac027d1ed..e28dbd8fa9ff2 100644
--- a/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp
+++ b/libcxx/test/libcxx/type_traits/desugars_to.compile.pass.cpp
@@ -10,6 +10,7 @@
 // UNSUPPORTED: gcc && c++11
 
 #include <__type_traits/desugars_to.h>
+#include <__algorithm/comp.h>
 
 struct Tag {};
 struct Operation {};
@@ -37,4 +38,22 @@ void tests() {
     static_assert(std::__desugars_to_v<Tag, Operation&&>, "");
     static_assert(std::__desugars_to_v<Tag, Operation const&&>, "");
   }
+
+  // Make sure that __less<> desugars to __totally_ordered_less_tag for integral types regardless of their cv-ref
+  {
+    static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int, int>, "");
+    static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, int&, int&>, "");
+    static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, const int, const int>, "");
+    static_assert(std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, const int&, const int&>, "");
+    static_assert(
+        std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile int&, volatile int&>, "");
+    static_assert(
+        std::__desugars_to_v<std::__totally_ordered_less_tag, std::__less<>, volatile const int&, volatile const int&>,
+        "");
+    static_assert(!std::__desugars_to_v<std::__totally_ordered_less_tag,
+                                        std::__less<>,
+                                        volatile const float&,
+                                        volatile const float&>,
+                  "");
+  }
 }


        

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:40:31 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 04:40:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++]
 __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp,
 _Tp> removes cv-ref from _Tp (PR #208950)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208950@github.com>
Message-ID: <6a5f5aaf.510f576d.2f694c.58a2@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/208950

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:41:29 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 04:41:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a5f5ae9.160324b0.1f653e.3b5a@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:41:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 04:41:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] faaeaf3 - [libc++] Implement P0493R5:
 Atomic minimum/maximum (#180333)
Message-ID: <6a5f5b03.507fc713.239434.5b50@mx.google.com>


Author: Connector Switch
Date: 2026-07-21T19:41:49+08:00
New Revision: faaeaf35a50acefd4e25e092e3f720067c40afe3

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

LOG: [libc++] Implement P0493R5: Atomic minimum/maximum (#180333)

Closes #105418.

Since gcc does not currently support `__atomic_fetch_min/max`, we use a
CAS loop in `atomic_ref` and `support/gcc.h`.

---------

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>

Added: 
    libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
    libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
    libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
    libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
    libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
    libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
    libcxx/test/support/atomic_fetch_max_helper.h
    libcxx/test/support/atomic_fetch_min_helper.h

Modified: 
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/docs/ReleaseNotes/24.rst
    libcxx/docs/Status/Cxx26Papers.csv
    libcxx/include/__atomic/atomic.h
    libcxx/include/__atomic/atomic_ref.h
    libcxx/include/__atomic/support.h
    libcxx/include/__atomic/support/c11.h
    libcxx/include/__atomic/support/gcc.h
    libcxx/include/atomic
    libcxx/include/version
    libcxx/modules/std/atomic.inc
    libcxx/test/std/language.support/support.limits/support.limits.general/atomic.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 6bf2f35fb4212..00665332a9ed7 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -424,7 +424,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_associative_heterogeneous_insertion``          *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_atomic_min_max``                               *unimplemented*
+    ``__cpp_lib_atomic_min_max``                               ``202403L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_atomic_ref``                                   ``202603L``
     ---------------------------------------------------------- -----------------

diff  --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..70c704ff9d326 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
 
 Improvements and New Features
 -----------------------------

diff  --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 916bb559f254b..7fcebd331197b 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -53,7 +53,7 @@
 "`P3107R5 <https://wg21.link/P3107R5>`__","Permit an efficient implementation of ``std::print``","2024-03 (Tokyo)","","","`#105414 <https://github.com/llvm/llvm-project/issues/105414>`__",""
 "`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23."
 "`P2845R8 <https://wg21.link/P2845R8>`__","Formatting of ``std::filesystem::path``","2024-03 (Tokyo)","","","`#105416 <https://github.com/llvm/llvm-project/issues/105416>`__",""
-"`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","","","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
+"`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","|Complete|","24","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
 "`P2542R8 <https://wg21.link/P2542R8>`__","``views::concat``","2024-03 (Tokyo)","|Complete|","23","`#105419 <https://github.com/llvm/llvm-project/issues/105419>`__",""
 "`P2591R5 <https://wg21.link/P2591R5>`__","Concatenation of strings and string views","2024-03 (Tokyo)","|Complete|","19","`#105420 <https://github.com/llvm/llvm-project/issues/105420>`__",""
 "`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","`#105421 <https://github.com/llvm/llvm-project/issues/105421>`__",""

diff  --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index 3d0ed9cc47396..d21ebf20bf23d 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -16,6 +16,8 @@
 #include <__atomic/is_always_lock_free.h>
 #include <__atomic/memory_order.h>
 #include <__atomic/support.h>
+#include <__concepts/arithmetic.h>
+#include <__concepts/same_as.h>
 #include <__config>
 #include <__cstddef/ptr
diff _t.h>
 #include <__memory/addressof.h>
@@ -24,6 +26,7 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_integral.h>
 #include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/remove_pointer.h>
@@ -178,6 +181,20 @@ struct __atomic_base<_Tp, true> : public __atomic_base<_Tp, false> {
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
     return std::__cxx_atomic_fetch_xor(std::addressof(this->__a_), __op, __m);
   }
+#if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+#endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) volatile _NOEXCEPT { return fetch_add(_Tp(1)); }
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) _NOEXCEPT { return fetch_add(_Tp(1)); }
@@ -306,6 +323,28 @@ struct atomic<_Tp*> : public __atomic_base<_Tp*> {
     return std::__cxx_atomic_fetch_sub(std::addressof(this->__a_), __op, __m);
   }
 
+#if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_max(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+    static_assert(!is_function<__remove_pointer_t<_Tp> >::value, "Pointer to function isn't allowed");
+    return std::__cxx_atomic_fetch_min(std::addressof(this->__a_), __op, __m);
+  }
+#endif
+
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) volatile _NOEXCEPT { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) _NOEXCEPT { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) volatile _NOEXCEPT { return fetch_sub(1); }
@@ -799,6 +838,68 @@ atomic_fetch_xor_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __o
   return __o->fetch_xor(__op, __m);
 }
 
+#if _LIBCPP_STD_VER >= 26
+// atomic_fetch_max
+
+template <class _Tp>
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_max(__op);
+}
+
+template <class _Tp>
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_max(__op);
+}
+
+// atomic_fetch_max_explicit
+
+template <class _Tp>
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_max_explicit(
+    volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_max(__op, __m);
+}
+
+template <class _Tp>
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
+_LIBCPP_HIDE_FROM_ABI _Tp
+atomic_fetch_max_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_max(__op, __m);
+}
+
+// atomic_fetch_min
+
+template <class _Tp>
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_min(__op);
+}
+
+template <class _Tp>
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op) _NOEXCEPT {
+  return __o->fetch_min(__op);
+}
+
+// atomic_fetch_min_explicit
+
+template <class _Tp>
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
+_LIBCPP_HIDE_FROM_ABI _Tp atomic_fetch_min_explicit(
+    volatile atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_min(__op, __m);
+}
+
+template <class _Tp>
+  requires((integral<_Tp> && !same_as<_Tp, bool>) || is_pointer_v<_Tp>)
+_LIBCPP_HIDE_FROM_ABI _Tp
+atomic_fetch_min_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::value_type __op, memory_order __m) _NOEXCEPT {
+  return __o->fetch_min(__op, __m);
+}
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ATOMIC_ATOMIC_H

diff  --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index 93f19a740ba3a..78f1f24f4c7a4 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -304,6 +304,32 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
     return __atomic_fetch_xor(this->__ptr_, __arg, std::__to_gcc_order(__order));
   }
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_max(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_max)
+    return __atomic_fetch_max(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old > __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
+#    endif
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp fetch_min(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+#    if __has_builtin(__atomic_fetch_min)
+    return __atomic_fetch_min(this->__ptr_, __arg, std::__to_gcc_order(__order));
+#    else
+    _Tp __old = this->load(memory_order_relaxed);
+    _Tp __new;
+    do {
+      __new = __old < __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
+#    endif
+  }
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) const noexcept { return fetch_add(_Tp(1)); }
   _LIBCPP_HIDE_FROM_ABI _Tp operator--(int) const noexcept { return fetch_sub(_Tp(1)); }
@@ -386,6 +412,26 @@ struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
   _LIBCPP_HIDE_FROM_ABI _Tp* fetch_sub(ptr
diff _t __arg, memory_order __order = memory_order_seq_cst) const noexcept {
     return __atomic_fetch_sub(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
   }
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_max(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    // TODO: Use __atomic_fetch_max once it accepts pointer arguments.
+    _Tp* __old = this->load(memory_order_relaxed);
+    _Tp* __new;
+    do {
+      __new = __old > __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
+  }
+  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_min(_Tp* __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+    // TODO: Use __atomic_fetch_min once it accepts pointer arguments.
+    _Tp* __old = this->load(memory_order_relaxed);
+    _Tp* __new;
+    do {
+      __new = __old < __arg ? __old : __arg;
+    } while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed));
+    return __old;
+  }
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) const noexcept { return fetch_add(1); }
   _LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) const noexcept { return fetch_sub(1); }

diff  --git a/libcxx/include/__atomic/support.h b/libcxx/include/__atomic/support.h
index 053ad54aa6ce5..55a93face9d83 100644
--- a/libcxx/include/__atomic/support.h
+++ b/libcxx/include/__atomic/support.h
@@ -99,6 +99,15 @@
 // template <class _Tp>
 // _Tp __cxx_atomic_fetch_xor(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
 //
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_max(_Atmc<_Tp> volatile* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_max(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_min(_Atmc<_Tp> volatile* __a, _Tp __pattern, memory_order __order) noexcept;
+// template <class _Tp>
+// _Tp __cxx_atomic_fetch_min(_Atmc<_Tp>* __a, _Tp __pattern, memory_order __order) noexcept;
+//
 // clang-format on
 //
 

diff  --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..899502422570a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -13,6 +13,8 @@
 #include <__config>
 #include <__cstddef/ptr
diff _t.h>
 #include <__memory/addressof.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/remove_const.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -259,6 +261,81 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
       std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
 }
 
+// Clang's __c11_atomic_fetch_max/min builtins do not accept pointer
+// arguments, so dispatch to a CAS loop for pointer types.
+// TODO: Use the builtin for pointer types once Clang accepts them.
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret > __val ? __ret : __val;
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+}
+
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_max(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret > __val ? __ret : __val;
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+}
+
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret < __val ? __ret : __val;
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+}
+
+template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  return __c11_atomic_fetch_min(
+      std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp, __enable_if_t<is_pointer<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret < __val ? __ret : __val;
+  } while (
+      !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ATOMIC_SUPPORT_C11_H

diff  --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..37c38d25012c6 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -258,6 +258,64 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
   return __atomic_fetch_xor(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
 }
 
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+#if __has_builtin(__atomic_fetch_max)
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
+#else
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret > __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+#endif
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+#if __has_builtin(__atomic_fetch_max)
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
+#else
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret > __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+#endif
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+#if __has_builtin(__atomic_fetch_min)
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
+#else
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret < __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+#endif
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+#if __has_builtin(__atomic_fetch_min)
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
+#else
+  _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
+  _Tp __value;
+  do {
+    __value = __ret < __val ? __ret : __val;
+  } while (!__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+  return __ret;
+#endif
+}
+
 #define __cxx_atomic_is_lock_free(__s) __atomic_is_lock_free(__s, 0)
 
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/include/atomic b/libcxx/include/atomic
index a6a910c2f380f..a89987ebd36e6 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -164,6 +164,10 @@ struct atomic<integral>
     integral fetch_or(integral op, memory_order m = memory_order_seq_cst) noexcept;
     integral fetch_xor(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
     integral fetch_xor(integral op, memory_order m = memory_order_seq_cst) noexcept;
+    integral fetch_max(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
+    integral fetch_max(integral op, memory_order m = memory_order_seq_cst) noexcept;
+    integral fetch_min(integral op, memory_order m = memory_order_seq_cst) volatile noexcept;
+    integral fetch_min(integral op, memory_order m = memory_order_seq_cst) noexcept;
 
     integral operator++(int) volatile noexcept;
     integral operator++(int) noexcept;
@@ -240,6 +244,10 @@ struct atomic<T*>
     T* fetch_add(ptr
diff _t op, memory_order m = memory_order_seq_cst) noexcept;
     T* fetch_sub(ptr
diff _t op, memory_order m = memory_order_seq_cst) volatile noexcept;
     T* fetch_sub(ptr
diff _t op, memory_order m = memory_order_seq_cst) noexcept;
+    T* fetch_max(T*, memory_order = memory_order_seq_cst) volatile noexcept;
+    T* fetch_max(T*, memory_order = memory_order_seq_cst) noexcept;
+    T* fetch_min(T*, memory_order = memory_order_seq_cst) volatile noexcept;
+    T* fetch_min(T*, memory_order = memory_order_seq_cst) noexcept;
 
     T* operator++(int) volatile noexcept;
     T* operator++(int) noexcept;
@@ -441,6 +449,26 @@ template<class T>
 template<class T>
   T atomic_fetch_xor_explicit(atomic<T>*, atomic<T>::value_type,
                               memory_order) noexcept;
+template<class T>
+  T atomic_fetch_max(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_max(atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_max_explicit(volatile atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_max_explicit(atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_min(volatile atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_min(atomic<T>*, atomic<T>::value_type) noexcept;
+template<class T>
+  T atomic_fetch_min_explicit(volatile atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
+template<class T>
+  T atomic_fetch_min_explicit(atomic<T>*, atomic<T>::value_type,
+                              memory_order) noexcept;
 
 template<class T>
   void atomic_wait(const volatile atomic<T>*, atomic<T>::value_type) noexcept; // since C++20

diff  --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..7545d187ca7f1 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -558,7 +558,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #if _LIBCPP_STD_VER >= 26
 # define __cpp_lib_aligned_accessor                     202411L
 // # define __cpp_lib_associative_heterogeneous_insertion  202306L
-// # define __cpp_lib_atomic_min_max                       202403L
+# define __cpp_lib_atomic_min_max                       202403L
 # undef  __cpp_lib_atomic_ref
 # define __cpp_lib_atomic_ref                           202603L
 # undef  __cpp_lib_bind_front

diff  --git a/libcxx/modules/std/atomic.inc b/libcxx/modules/std/atomic.inc
index 9a30fb7affe71..38321e1a1bb64 100644
--- a/libcxx/modules/std/atomic.inc
+++ b/libcxx/modules/std/atomic.inc
@@ -50,6 +50,12 @@ export namespace std {
   using std::atomic_fetch_sub_explicit _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor _LIBCPP_USING_IF_EXISTS;
   using std::atomic_fetch_xor_explicit _LIBCPP_USING_IF_EXISTS;
+#if _LIBCPP_STD_VER >= 26
+  using std::atomic_fetch_max _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_max_explicit _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_min _LIBCPP_USING_IF_EXISTS;
+  using std::atomic_fetch_min_explicit _LIBCPP_USING_IF_EXISTS;
+#endif
   using std::atomic_notify_all _LIBCPP_USING_IF_EXISTS;
   using std::atomic_notify_one _LIBCPP_USING_IF_EXISTS;
   using std::atomic_wait _LIBCPP_USING_IF_EXISTS;

diff  --git a/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
new file mode 100644
index 0000000000000..7302eaea570f0
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_max.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// integral-type fetch_max(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+// T* fetch_max(T*, memory_order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+
+#include "atomic_fetch_max_helper.h"
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+template <typename T>
+concept has_fetch_max = requires(std::atomic_ref<T> const& a, T v) {
+  { a.fetch_max(v) } -> std::same_as<T>;
+  { a.fetch_max(v, std::memory_order::relaxed) } -> std::same_as<T>;
+};
+
+template <typename T>
+struct TestDoesNotHaveFetchMax {
+  void operator()() const { static_assert(!has_fetch_max<T>); }
+};
+
+template <typename T>
+struct TestFetchMax {
+  void operator()() const {
+    static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_max<T>);
+
+    if constexpr (std::is_integral_v<T>) {
+      alignas(std::atomic_ref<T>::required_alignment) T x{};
+      std::atomic_ref<T> const a(x);
+
+      auto load  = [&]() { return x; };
+      auto store = [&](T val) { x = val; };
+      auto max   = [&](T val, auto order) { return a.fetch_max(val, order); };
+
+      ASSERT_NOEXCEPT(a.fetch_max(T(0), std::memory_order_seq_cst));
+      test_fetch_max_integral<T>(load, store, max);
+
+    } else if constexpr (std::is_pointer_v<T>) {
+      using U = std::remove_pointer_t<T>;
+      U arr[5]{};
+      alignas(std::atomic_ref<T>::required_alignment) T p{};
+      std::atomic_ref<T> const a(p);
+
+      auto load  = [&]() { return p; };
+      auto store = [&](T val) { p = val; };
+      auto max   = [&](T val, auto order) { return a.fetch_max(val, order); };
+
+      ASSERT_NOEXCEPT(a.fetch_max(&arr[0], std::memory_order_seq_cst));
+      test_fetch_max_pointer<U>(&arr[0], &arr[2], &arr[4], load, store, max);
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFetchMax>()();
+  TestEachPointerType<TestFetchMax>()();
+
+  TestEachFloatingPointType<TestDoesNotHaveFetchMax>()();
+
+  TestDoesNotHaveFetchMax<bool>()();
+  TestDoesNotHaveFetchMax<UserAtomicType>()();
+  TestDoesNotHaveFetchMax<LargeUserAtomicType>()();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
new file mode 100644
index 0000000000000..e7f36363ae3a7
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.ref/fetch_min.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// integral-type fetch_min(integral-type, memory_order = memory_order::seq_cst) const noexcept;
+// T* fetch_min(T*, memory_order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+
+#include "atomic_fetch_min_helper.h"
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+template <typename T>
+concept has_fetch_min = requires(std::atomic_ref<T> const& a, T v) {
+  { a.fetch_min(v) } -> std::same_as<T>;
+  { a.fetch_min(v, std::memory_order::relaxed) } -> std::same_as<T>;
+};
+
+template <typename T>
+struct TestDoesNotHaveFetchMin {
+  void operator()() const { static_assert(!has_fetch_min<T>); }
+};
+
+template <typename T>
+struct TestFetchMin {
+  void operator()() const {
+    static_assert((std::is_integral_v<T> || std::is_pointer_v<T>) && has_fetch_min<T>);
+
+    if constexpr (std::is_integral_v<T>) {
+      alignas(std::atomic_ref<T>::required_alignment) T x{};
+      std::atomic_ref<T> const a(x);
+
+      auto load  = [&]() { return x; };
+      auto store = [&](T val) { x = val; };
+      auto min   = [&](T val, auto order) { return a.fetch_min(val, order); };
+
+      ASSERT_NOEXCEPT(a.fetch_min(T(0), std::memory_order_seq_cst));
+      test_fetch_min_integral<T>(load, store, min);
+
+    } else if constexpr (std::is_pointer_v<T>) {
+      using U = std::remove_pointer_t<T>;
+      U arr[5]{};
+      alignas(std::atomic_ref<T>::required_alignment) T p{};
+      std::atomic_ref<T> const a(p);
+
+      auto load  = [&]() { return p; };
+      auto store = [&](T val) { p = val; };
+      auto min   = [&](T val, auto order) { return a.fetch_min(val, order); };
+
+      ASSERT_NOEXCEPT(a.fetch_min(&arr[0], std::memory_order_seq_cst));
+      test_fetch_min_pointer<U>(&arr[0], &arr[2], &arr[4], load, store, min);
+    }
+  }
+};
+
+int main(int, char**) {
+  TestEachIntegralType<TestFetchMin>()();
+  TestEachPointerType<TestFetchMin>()();
+
+  TestEachFloatingPointType<TestDoesNotHaveFetchMin>()();
+
+  TestDoesNotHaveFetchMin<bool>()();
+  TestDoesNotHaveFetchMin<UserAtomicType>()();
+  TestDoesNotHaveFetchMin<LargeUserAtomicType>()();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
new file mode 100644
index 0000000000000..7b2a5715ddddd
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max.pass.cpp
@@ -0,0 +1,121 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//   T atomic_fetch_max(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
+// template<class T>
+//   constexpr T atomic_fetch_max(atomic<T>*, typename atomic<T>::value_type) noexcept;
+
+#include <algorithm>
+#include <atomic>
+#include <cassert>
+#include <type_traits>
+#include <vector>
+
+#include "atomic_fetch_max_helper.h"
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  {
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto max   = [&](T val, auto) { return std::atomic_fetch_max(&a, val); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_max(&a, T(0)));
+    test_fetch_max_integral<T>(load, store, max);
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // Concurrent stress test: many threads calling atomic_fetch_max should leave
+  // the atomic at the global maximum value seen.
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(0);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = 0; j < loop; ++j) {
+          std::atomic_fetch_max(&at, T(i * loop + j));
+        }
+      }));
+    }
+
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(0);
+      for (auto i = 0; i < n; ++i) {
+        res = std::max<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
+    }
+  }
+};
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto max   = [&](T* val, auto) { return std::atomic_fetch_max(&a, val); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_max(&a, p0));
+  test_fetch_max_pointer<T>(p0, p2, p4, load, store, max);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
new file mode 100644
index 0000000000000..e811f114708c8
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_max_explicit.pass.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//   T atomic_fetch_max_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
+//                               memory_order) noexcept;
+// template<class T>
+//   constexpr T atomic_fetch_max_explicit(atomic<T>*, typename atomic<T>::value_type,
+//                                         memory_order) noexcept;
+
+#include <algorithm>
+#include <atomic>
+#include <cassert>
+#include <type_traits>
+#include <vector>
+
+#include "atomic_fetch_max_helper.h"
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  {
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto max   = [&](T val, auto order) { return std::atomic_fetch_max_explicit(&a, val, order); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&a, T(0), std::memory_order_seq_cst));
+    test_fetch_max_integral<T>(load, store, max);
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // Concurrent stress test: many threads calling atomic_fetch_max_explicit should
+  // leave the atomic at the global maximum value seen.
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(0);
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = 0; j < loop; ++j) {
+          std::atomic_fetch_max_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
+        }
+      }));
+    }
+
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    const auto times = [](int n) {
+      T res(0);
+      for (auto i = 0; i < n; ++i) {
+        res = std::max<T>(res, i);
+      }
+      return res;
+    };
+
+    assert(at.load() == times(number_of_threads * loop));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
+    }
+  }
+};
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto max   = [&](T* val, auto order) { return std::atomic_fetch_max_explicit(&a, val, order); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_max_explicit(&a, p0, std::memory_order_seq_cst));
+  test_fetch_max_pointer<T>(p0, p2, p4, load, store, max);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
new file mode 100644
index 0000000000000..df40cc7a92c7b
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//   T atomic_fetch_min(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
+// template<class T>
+//   constexpr T atomic_fetch_min(atomic<T>*, typename atomic<T>::value_type) noexcept;
+
+#include <algorithm>
+#include <atomic>
+#include <cassert>
+#include <limits>
+#include <type_traits>
+#include <vector>
+
+#include "atomic_fetch_min_helper.h"
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  {
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto min   = [&](T val, auto) { return std::atomic_fetch_min(&a, val); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_min(&a, T(0)));
+    test_fetch_min_integral<T>(load, store, min);
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // Concurrent stress test: many threads calling atomic_fetch_min should leave
+  // the atomic at the global minimum value seen.
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(std::numeric_limits<T>::max());
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = 0; j < loop; ++j) {
+          std::atomic_fetch_min(&at, T(i * loop + j));
+        }
+      }));
+    }
+
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    assert(at.load() == T(0));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
+    }
+  }
+};
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto min   = [&](T* val, auto) { return std::atomic_fetch_min(&a, val); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_min(&a, p0));
+  test_fetch_min_pointer<T>(p0, p2, p4, load, store, min);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
new file mode 100644
index 0000000000000..2295b942b86de
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_min_explicit.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// <atomic>
+
+// template<class T>
+//   T atomic_fetch_min_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
+//                               memory_order) noexcept;
+// template<class T>
+//   constexpr T atomic_fetch_min_explicit(atomic<T>*, typename atomic<T>::value_type,
+//                                         memory_order) noexcept;
+
+#include <algorithm>
+#include <atomic>
+#include <cassert>
+#include <limits>
+#include <type_traits>
+#include <vector>
+
+#include "atomic_fetch_min_helper.h"
+#include "atomic_helpers.h"
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+
+#  include "make_test_thread.h"
+#endif
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  {
+    MaybeVolatile<std::atomic<T>> a;
+    auto load  = [&]() { return a.load(); };
+    auto store = [&](T val) { a.store(val); };
+    auto min   = [&](T val, auto order) { return std::atomic_fetch_min_explicit(&a, val, order); };
+    ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&a, T(0), std::memory_order_seq_cst));
+    test_fetch_min_integral<T>(load, store, min);
+  }
+
+#ifndef TEST_HAS_NO_THREADS
+  // Concurrent stress test: many threads calling atomic_fetch_min_explicit should
+  // leave the atomic at the global minimum value seen.
+  {
+    constexpr auto number_of_threads = 4;
+    constexpr auto loop              = 30;
+
+    MaybeVolatile<std::atomic<T>> at(std::numeric_limits<T>::max());
+
+    std::vector<std::thread> threads;
+    threads.reserve(number_of_threads);
+    for (auto i = 0; i < number_of_threads; ++i) {
+      threads.push_back(support::make_test_thread([&at, i]() {
+        for (auto j = 0; j < loop; ++j) {
+          std::atomic_fetch_min_explicit(&at, T(i * loop + j), std::memory_order_relaxed);
+        }
+      }));
+    }
+
+    for (auto& thread : threads) {
+      thread.join();
+    }
+
+    assert(at.load() == T(0));
+  }
+#endif
+}
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    test_impl<T>();
+    if constexpr (std::atomic<T>::is_always_lock_free) {
+      test_impl<T, std::add_volatile_t>();
+    }
+  }
+};
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_pointer_impl() {
+  T arr[5];
+  T* p0 = &arr[0];
+  T* p2 = &arr[2];
+  T* p4 = &arr[4];
+
+  MaybeVolatile<std::atomic<T*>> a;
+  auto load  = [&]() { return a.load(); };
+  auto store = [&](T* val) { a.store(val); };
+  auto min   = [&](T* val, auto order) { return std::atomic_fetch_min_explicit(&a, val, order); };
+  ASSERT_NOEXCEPT(std::atomic_fetch_min_explicit(&a, p0, std::memory_order_seq_cst));
+  test_fetch_min_pointer<T>(p0, p2, p4, load, store, min);
+}
+
+template <class T>
+void test_pointer() {
+  test_pointer_impl<T>();
+  if constexpr (std::atomic<T*>::is_always_lock_free) {
+    test_pointer_impl<T, std::add_volatile_t>();
+  }
+}
+
+int main(int, char**) {
+  TestEachIntegralType<TestFn>()();
+  test_pointer<int>();
+  test_pointer<char>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
index e62416257a696..aece31d25c249 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
@@ -339,17 +339,11 @@
 #    error "__cpp_lib_atomic_lock_free_type_aliases should have the value 201907L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should be defined in c++26"
-#    endif
-#    if __cpp_lib_atomic_min_max != 202403L
-#      error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_atomic_min_max
+#    error "__cpp_lib_atomic_min_max should be defined in c++26"
+#  endif
+#  if __cpp_lib_atomic_min_max != 202403L
+#    error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_atomic_ref

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 f2d74cc99d864..9e987346c599b 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
@@ -6434,17 +6434,11 @@
 #    error "__cpp_lib_atomic_lock_free_type_aliases should have the value 201907L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should be defined in c++26"
-#    endif
-#    if __cpp_lib_atomic_min_max != 202403L
-#      error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_atomic_min_max
-#      error "__cpp_lib_atomic_min_max should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_atomic_min_max
+#    error "__cpp_lib_atomic_min_max should be defined in c++26"
+#  endif
+#  if __cpp_lib_atomic_min_max != 202403L
+#    error "__cpp_lib_atomic_min_max should have the value 202403L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_atomic_ref

diff  --git a/libcxx/test/support/atomic_fetch_max_helper.h b/libcxx/test/support/atomic_fetch_max_helper.h
new file mode 100644
index 0000000000000..ff4bd7aa38912
--- /dev/null
+++ b/libcxx/test/support/atomic_fetch_max_helper.h
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
+#define TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H
+
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Test fetch_max for integral types
+// LoadOp: () -> T - reads current value
+// StoreOp: (T) -> void - stores a value
+// MaxOp: (T, memory_order) -> T - performs fetch_max operation
+template <class T, class LoadOp, class StoreOp, class MaxOp>
+void test_fetch_max_integral(LoadOp load, StoreOp store, MaxOp max) {
+  // Test basic fetch_max (update to larger value)
+  {
+    store(T(10));
+    T old = max(T(20), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(20));
+  }
+
+  // Test with smaller value (no change)
+  {
+    store(T(10));
+    T old = max(T(5), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(10));
+  }
+
+  // Test 
diff erent memory orderings
+  {
+    store(T(15));
+    T old = max(T(25), std::memory_order_seq_cst);
+    assert(old == T(15));
+    assert(load() == T(25));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(25), std::memory_order_relaxed);
+    assert(old == T(10));
+    assert(load() == T(25));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(30), std::memory_order_acquire);
+    assert(old == T(10));
+    assert(load() == T(30));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(15), std::memory_order_release);
+    assert(old == T(10));
+    assert(load() == T(15));
+  }
+
+  {
+    store(T(10));
+    T old = max(T(22), std::memory_order_acq_rel);
+    assert(old == T(10));
+    assert(load() == T(22));
+  }
+
+  {
+    store(T(15));
+    T old = max(T(35), std::memory_order_relaxed);
+    assert(old == T(15));
+    assert(load() == T(35));
+  }
+
+  // Test return type
+  {
+    store(T(10));
+    static_assert(std::is_same_v<decltype(max(T(20), std::memory_order_seq_cst)), T>);
+  }
+}
+
+// Test fetch_max for pointer types
+// LoadOp: () -> T* - reads current pointer value
+// StoreOp: (T*) -> void - stores a pointer value
+// MaxOp: (T*, memory_order) -> T* - performs fetch_max operation
+template <class T, class LoadOp, class StoreOp, class MaxOp>
+void test_fetch_max_pointer(T* p0, T* p2, T* p4, LoadOp load, StoreOp store, MaxOp max) {
+  // Test basic fetch_max (update to larger pointer)
+  {
+    store(p2);
+    T* old = max(p4, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p4);
+  }
+
+  // Test with smaller pointer (no change)
+  {
+    store(p2);
+    T* old = max(p0, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p2);
+  }
+
+  // Test 
diff erent memory orderings
+  {
+    store(p0);
+    T* old = max(p2, std::memory_order_seq_cst);
+    assert(old == p0);
+    assert(load() == p2);
+  }
+
+  {
+    store(p0);
+    T* old = max(p2, std::memory_order_relaxed);
+    assert(old == p0);
+    assert(load() == p2);
+  }
+
+  {
+    store(p0);
+    T* old = max(p4, std::memory_order_acquire);
+    assert(old == p0);
+    assert(load() == p4);
+  }
+
+  {
+    store(p0);
+    T* old = max(p4, std::memory_order_release);
+    assert(old == p0);
+    assert(load() == p4);
+  }
+
+  // Test return type
+  {
+    store(p2);
+    static_assert(std::is_same_v<decltype(max(p4, std::memory_order_seq_cst)), T*>);
+  }
+}
+
+#endif // TEST_STD_ATOMICS_ATOMIC_FETCH_MAX_HELPER_H

diff  --git a/libcxx/test/support/atomic_fetch_min_helper.h b/libcxx/test/support/atomic_fetch_min_helper.h
new file mode 100644
index 0000000000000..20a3558296118
--- /dev/null
+++ b/libcxx/test/support/atomic_fetch_min_helper.h
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H
+#define TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H
+
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Test fetch_min for integral types
+// LoadOp: () -> T - reads current value
+// StoreOp: (T) -> void - stores a value
+// MinOp: (T, memory_order) -> T - performs fetch_min operation
+template <class T, class LoadOp, class StoreOp, class MinOp>
+void test_fetch_min_integral(LoadOp load, StoreOp store, MinOp min) {
+  // Test basic fetch_min (update to smaller value)
+  {
+    store(T(10));
+    T old = min(T(5), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(5));
+  }
+
+  // Test with larger value (no change)
+  {
+    store(T(10));
+    T old = min(T(20), std::memory_order_seq_cst);
+    assert(old == T(10));
+    assert(load() == T(10));
+  }
+
+  // Test 
diff erent memory orderings
+  {
+    store(T(15));
+    T old = min(T(8), std::memory_order_seq_cst);
+    assert(old == T(15));
+    assert(load() == T(8));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(3), std::memory_order_relaxed);
+    assert(old == T(10));
+    assert(load() == T(3));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(2), std::memory_order_acquire);
+    assert(old == T(10));
+    assert(load() == T(2));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(7), std::memory_order_release);
+    assert(old == T(10));
+    assert(load() == T(7));
+  }
+
+  {
+    store(T(10));
+    T old = min(T(4), std::memory_order_acq_rel);
+    assert(old == T(10));
+    assert(load() == T(4));
+  }
+
+  {
+    store(T(15));
+    T old = min(T(6), std::memory_order_relaxed);
+    assert(old == T(15));
+    assert(load() == T(6));
+  }
+
+  // Test return type
+  {
+    store(T(10));
+    static_assert(std::is_same_v<decltype(min(T(5), std::memory_order_seq_cst)), T>);
+  }
+}
+
+// Test fetch_min for pointer types
+// LoadOp: () -> T* - reads current pointer value
+// StoreOp: (T*) -> void - stores a pointer value
+// MinOp: (T*, memory_order) -> T* - performs fetch_min operation
+template <class T, class LoadOp, class StoreOp, class MinOp>
+void test_fetch_min_pointer(T* p0, T* p2, T* p4, LoadOp load, StoreOp store, MinOp min) {
+  // Test basic fetch_min (update to smaller pointer)
+  {
+    store(p2);
+    T* old = min(p0, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p0);
+  }
+
+  // Test with larger pointer (no change)
+  {
+    store(p2);
+    T* old = min(p4, std::memory_order_seq_cst);
+    assert(old == p2);
+    assert(load() == p2);
+  }
+
+  // Test 
diff erent memory orderings
+  {
+    store(p4);
+    T* old = min(p2, std::memory_order_seq_cst);
+    assert(old == p4);
+    assert(load() == p2);
+  }
+
+  {
+    store(p4);
+    T* old = min(p2, std::memory_order_relaxed);
+    assert(old == p4);
+    assert(load() == p2);
+  }
+
+  {
+    store(p4);
+    T* old = min(p0, std::memory_order_acquire);
+    assert(old == p4);
+    assert(load() == p0);
+  }
+
+  {
+    store(p4);
+    T* old = min(p0, std::memory_order_release);
+    assert(old == p4);
+    assert(load() == p0);
+  }
+
+  // Test return type
+  {
+    store(p2);
+    static_assert(std::is_same_v<decltype(min(p0, std::memory_order_seq_cst)), T*>);
+  }
+}
+
+#endif // TEST_STD_ATOMICS_ATOMIC_FETCH_MIN_HELPER_H

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..13a5dab01b74e 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -191,7 +191,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_atomic_min_max",
             "values": {"c++26": 202403}, # P0493R5: Atomic minimum/maximum
             "headers": ["atomic"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_atomic_ref",


        

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:42:15 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Tue, 21 Jul 2026 04:42:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0493R5: Atomic
	minimum/maximum (PR #180333)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/180333@github.com>
Message-ID: <6a5f5b17.e5907290.281687.24b9@mx.google.com>


https://github.com/c8ef closed https://github.com/llvm/llvm-project/pull/180333

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:42:31 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 04:42:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 460778b - [libc++][pstl] Default
 implementation of parallel std::adjacent_difference (#207585)
Message-ID: <6a5f5b27.e55dc341.1c6a0.2dbf@mx.google.com>


Author: Michael G. Kazakov
Date: 2026-07-21T07:42:26-04:00
New Revision: 460778b9d64ed36fd7e80a4cc73ea552aebf016b

URL: https://github.com/llvm/llvm-project/commit/460778b9d64ed36fd7e80a4cc73ea552aebf016b
DIFF: https://github.com/llvm/llvm-project/commit/460778b9d64ed36fd7e80a4cc73ea552aebf016b.diff

LOG: [libc++][pstl] Default implementation of parallel std::adjacent_difference (#207585)

This PR adds a "one-liner" default implementation of parallel
`std::adjacent_difference` on top of parallel binary `std::transform`.

The implementation builds two iterator ranges out of the input one, so
that a zip of these ranges yields adjacent pairs. Then performs a binary
transform to calculate and output the adjacent differences.

Part of https://github.com/llvm/llvm-project/issues/99938

Added: 
    libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
    libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp
    libcxx/test/support/runway_sample.h
    libcxx/test/support/test.support/runway_sample.pass.cpp

Modified: 
    libcxx/include/__numeric/pstl.h
    libcxx/include/__pstl/backend_fwd.h
    libcxx/include/__pstl/backends/default.h
    libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
    libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
    libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h
index ecbc9f5348333..0b85cfb8c2327 100644
--- a/libcxx/include/__numeric/pstl.h
+++ b/libcxx/include/__numeric/pstl.h
@@ -165,6 +165,45 @@ template <class _ExecutionPolicy,
       std::move(__transform));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_
diff erence(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_
diff erence requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_
diff erence requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_
diff erence, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first1), std::move(__last1), std::move(__first2), minus{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryOperation,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_
diff erence(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _BinaryOperation __op) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_
diff erence requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_
diff erence requires ForwardIterators");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_
diff erence, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__op));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17

diff  --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a84a768cad570..2ba5b4434fc33 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -317,6 +317,13 @@ struct __is_sorted;
 // optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_
diff erence;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+// optional<_ForwardIterator2>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 

diff  --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5c78d4efa412b..28bc75be147aa 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -92,11 +92,12 @@ namespace __pstl {
 //
 // transform and transform_binary family
 // -------------------------------------
-// - replace_copy_if
-// - replace_copy
-// - move
+// - adjacent_
diff erence
 // - copy
 // - copy_n
+// - move
+// - replace_copy
+// - replace_copy_if
 // - reverse_copy
 // - rotate_copy
 //
@@ -575,6 +576,34 @@ struct __reverse_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_
diff erence<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __result,
+             _BinaryOperation&& __op) const noexcept {
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    if (__first1 == __last1)
+      return __result; // edge case: empty input range, just return the output iterator
+    *__result = *__first1;
+    ++__result;
+    _ForwardIterator1 __first2 = std::next(__first1);
+    if (__first2 == __last1)
+      return __result; // edge case: not enough elements to perform adjacent 
diff erence, just return the output iterator
+    // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1)
+    return _TransformBinary()(
+        __policy,
+        std::move(__first2),
+        std::move(__last1),
+        std::move(__first1),
+        std::move(__result),
+        std::forward<_BinaryOperation>(__op));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 

diff  --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index fbe3460b67f17..402d8fbf9131a 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -211,4 +211,15 @@ void f(non_forward_iterator non_fwd,
     (void)std::transform_reduce(
         pol, non_fwd, non_fwd, val, func, func); // expected-error@*:* {{static assertion failed: transform_reduce}}
   }
+
+  {
+    (void)std::adjacent_
diff erence(
+        pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_
diff erence}}
+    (void)std::adjacent_
diff erence(
+        pol, it, it, non_fwd, func); // expected-error@*:* {{static assertion failed: adjacent_
diff erence}}
+    (void)std::adjacent_
diff erence(
+        pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: adjacent_
diff erence}}
+    (void)std::adjacent_
diff erence(
+        pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: adjacent_
diff erence}}
+  }
 }

diff  --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 049c8dc88c2da..f4fffd456a331 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -15,6 +15,7 @@
 #include <algorithm>
 #include <execution>
 #include <iterator>
+#include <numeric>
 
 void test() {
   int a[]    = {1};
@@ -58,4 +59,8 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_
diff erence(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_
diff erence(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
 }

diff  --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 8af2a0a15fd5c..e00046fdb1dd6 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -85,7 +85,7 @@ int main(int, char**) {
       auto last1       = util::throw_on_move_iterator(std::end(a), tokens[1].active() ? 1 : -1);
       auto first2      = util::throw_on_move_iterator(std::begin(b), tokens[2].active() ? 1 : -1);
       auto last2       = util::throw_on_move_iterator(std::end(b), tokens[3].active() ? 1 : -1);
-      auto dest        = util::throw_on_move_iterator(std::end(storage), tokens[4].active() ? 1 : -1);
+      auto dest        = util::throw_on_move_iterator(std::begin(storage), tokens[4].active() ? 1 : -1);
       auto maybe_throw = [](ThrowToken const& token, auto f) {
         return [&token, f](auto... args) {
           if (token.active())
@@ -365,6 +365,20 @@ int main(int, char**) {
           (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction);
         });
       }
+
+      {
+        auto op = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
+
+        // adjacent_
diff erence(first, last, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_
diff erence(policy, std::move(first1), std::move(last1), std::move(dest));
+        });
+
+        // adjacent_
diff erence(first, last, dest, op)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_
diff erence(policy, std::move(first1), std::move(last1), std::move(dest), op);
+        });
+      }
     }
   });
 }

diff  --git a/libcxx/test/std/numerics/numeric.ops/adjacent.
diff erence/pstl.adjacent_
diff erence.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.
diff erence/pstl.adjacent_
diff erence.pass.cpp
new file mode 100644
index 0000000000000..d06f5b64e2f9f
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.
diff erence/pstl.adjacent_
diff erence.pass.cpp
@@ -0,0 +1,161 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   ForwardIterator2 adjacent_
diff erence(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_
diff erence);
+
+static_assert(sfinae_test_adjacent_
diff erence<int, int*, int*, int*>);
+static_assert(!sfinae_test_adjacent_
diff erence<std::execution::parallel_policy, int*, int*, int*>);
+
+// Types X and Y are provided to test adjacent_
diff erence() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
+class X {
+  int i_;
+
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+
+  friend X operator-(const X& x, const X& y) { return X(x.i_ - y.i_); }
+
+  friend class Y;
+};
+
+class Y {
+  int i_;
+
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_
diff erence(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r  = std::adjacent_
diff erence(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 20};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_
diff erence(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, -5, -4, -3, -2};
+      int ib[std::size(ia)] = {0};
+      Iter2 r = std::adjacent_
diff erence(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r =
+            std::adjacent_
diff erence(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)));
+        assert(r == Iter2(std::begin(ib) + i));
+        assert(std::all_of(std::begin(ib), std::begin(ib) + i, [](int x) { return x == 1; }));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(7)};
+      Y ir[3] = {Y(1), Y(4), Y(2)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r = std::adjacent_
diff erence(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)));
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}

diff  --git a/libcxx/test/std/numerics/numeric.ops/adjacent.
diff erence/pstl.adjacent_
diff erence_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.
diff erence/pstl.adjacent_
diff erence_op.pass.cpp
new file mode 100644
index 0000000000000..d623cda058d04
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.
diff erence/pstl.adjacent_
diff erence_op.pass.cpp
@@ -0,0 +1,171 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryOperation>
+//   ForwardIterator2 adjacent_
diff erence(ExecutionPolicy&& exec,
+//                                        ForwardIterator1 first1,
+//                                        ForwardIterator1 last1,
+//                                        ForwardIterator2 first2,
+//                                        BinaryOperation op);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(adjacent_
diff erence);
+
+static_assert(sfinae_test_adjacent_
diff erence<int, int*, int*, int*, int (*)(int, int)>);
+static_assert(!sfinae_test_adjacent_
diff erence<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
+
+// Types X and Y are provided to test adjacent_
diff erence() against custom types.
+// X is a source type that supports subtraction.
+// Y is a destination type that can be assigned from X and compared for equality.
+
+class X {
+  int i_;
+  X& operator=(const X&);
+
+public:
+  explicit X(int i) : i_(i) {}
+  X(const X& x) : i_(x.i_) {}
+  X& operator=(X&& x) {
+    i_   = x.i_;
+    x.i_ = -1;
+    return *this;
+  }
+  friend class Y;
+  friend struct Op;
+};
+
+class Y {
+  int i_;
+  Y& operator=(const Y&);
+
+public:
+  explicit Y(int i) : i_(i) {}
+  Y(const Y& y) : i_(y.i_) {}
+  void operator=(const X& x) { i_ = x.i_; }
+  bool operator==(const Y& y) const { return i_ == y.i_; }
+};
+
+// ensures that the predicate is not equivalent to std::minus
+struct Op {
+  int operator()(int l, int r) const { return l * 2 - r; }
+  X operator()(const X& l, const X& r) const { return X(l.i_ * 2 - r.i_); }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int ia[] = {10};
+      int ir[] = {42};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_
diff erence(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::begin(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[] = {10};
+      int ir[] = {10};
+      int ib[] = {42};
+      Iter2 r =
+          std::adjacent_
diff erence(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {10, 30};
+      int ir[]              = {10, 50};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_
diff erence(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[]              = {15, 10, 6, 3, 1};
+      int ir[]              = {15, 5, 2, 0, -1};
+      int ib[std::size(ia)] = {0};
+      Iter2 r =
+          std::adjacent_
diff erence(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+    {
+      int ia[1073];
+      int ib[1073];
+      std::iota(std::begin(ia), std::end(ia), 1);
+      runway_sample(std::size(ia), [&](size_t i) {
+        Iter2 r = std::adjacent_
diff erence(
+            policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)), Op{});
+        assert(r == Iter2(std::begin(ib) + i));
+        for (size_t j = 0; j < i; ++j)
+          assert(ib[j] == (int)(j == 0 ? 1 : j + 2));
+      });
+    }
+  }
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      X ia[3] = {X(1), X(5), X(8)};
+      Y ir[3] = {Y(1), Y(9), Y(11)};
+      Y ib[3] = {Y(0), Y(0), Y(0)};
+      Iter2 r =
+          std::adjacent_
diff erence(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{});
+      assert(r == Iter2(std::end(ib)));
+      for (size_t i = 0; i < std::size(ia); ++i)
+        assert(ib[i] == ir[i]);
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<int*>, types::forward_iterator_list<const int*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  types::for_each(
+      types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
+      types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(
+            types::forward_iterator_list<Y*>{},
+            TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+      }});
+  return 0;
+}

diff  --git a/libcxx/test/support/runway_sample.h b/libcxx/test/support/runway_sample.h
new file mode 100644
index 0000000000000..16ecd58cef747
--- /dev/null
+++ b/libcxx/test/support/runway_sample.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+#ifndef TEST_SUPPORT_RUNWAY_SAMPLE_H
+#define TEST_SUPPORT_RUNWAY_SAMPLE_H
+
+#include <cstddef>
+#include <algorithm>
+#include "test_macros.h"
+
+// runway_sample(size, callable) calls callable(i) for a set of sample indices i in the range [0, size).
+// they are chosen in 3 phases:
+//   - prefix, sampled densely with increments of 1
+//   - middle, sampled sparsely with uncanny numbers
+//   - suffix, sampled densely with increments of 1
+//
+// Examples of sample indices for various sizes:
+// size = 0:
+// <no samples>
+// size = 1:
+// 0
+// size = 10:
+// 0 1 2 3 4 5 6 7 8 9
+// size = 100:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
+// size = 1000:
+// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 157 493 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
+template <class Callable>
+TEST_CONSTEXPR_CXX14 void runway_sample(std::size_t size, Callable callable) {
+  constexpr std::size_t affix = 16;
+  std::size_t i               = 0;
+
+  // 0, 1, 2, ..., 15
+  for (; i < std::min(size, affix); ++i) {
+    callable(i);
+  }
+
+  if (size <= affix) {
+    return;
+  }
+
+  // 16, 50, 157, 493, 1549, 4868, ...
+  for (std::size_t j = i; j + affix < size; i = j, j = j * 3 + j / 7) {
+    callable(j);
+  }
+
+  // size - 16, size - 15, ..., size - 1
+  i = std::max(i, size - affix);
+  for (; i < size; ++i) {
+    callable(i);
+  }
+}
+
+#endif // TEST_SUPPORT_RUNWAY_SAMPLE_H

diff  --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp
new file mode 100644
index 0000000000000..6c6eaafc3439b
--- /dev/null
+++ b/libcxx/test/support/test.support/runway_sample.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// "support/runway_sample.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "runway_sample.h"
+
+constexpr bool test() {
+  {
+    runway_sample(0, [&](std::size_t /*i*/) { assert(false); });
+  }
+  {
+    std::size_t expected[] = {0};
+    std::size_t n          = 0;
+    runway_sample(1, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
+    });
+  }
+  {
+    std::size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    std::size_t n          = 0;
+    runway_sample(10, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
+    });
+  }
+  {
+    std::size_t expected[] = {
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29};
+    std::size_t n = 0;
+    runway_sample(30, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
+    });
+  }
+  {
+    std::size_t expected[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
+                              50, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99};
+    std::size_t n          = 0;
+    runway_sample(100, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
+    });
+  }
+  {
+    std::size_t expected[] = {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  50,
+                              157, 493, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999};
+    std::size_t n          = 0;
+    runway_sample(1000, [&](std::size_t i) {
+      assert(expected[n] == i);
+      n++;
+    });
+  }
+  return true;
+}
+
+int main() {
+  test();
+  static_assert(test());
+  return 0;
+}


        

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:42:36 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 04:42:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Default implementation of
 parallel std::adjacent_difference (PR #207585)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207585@github.com>
Message-ID: <6a5f5b2c.1905e68e.30d957.9d33@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/207585

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:42:42 2026
From: libcxx-commits at lists.llvm.org (Pavel Novikov via libcxx-commits)
Date: Tue, 21 Jul 2026 04:42:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f5b32.4987c784.2c60a9.1358@mx.google.com>


https://github.com/toughengineer updated https://github.com/llvm/llvm-project/pull/210078

>From 34419f2461f3cc4ac0c285cfbb76e64f016db652 Mon Sep 17 00:00:00 2001
From: Pavel Novikov <dev-ape at yandex.ru>
Date: Thu, 16 Jul 2026 17:43:25 +0300
Subject: [PATCH 1/2] [libc++] fix minor performance issue

---
 libcxx/include/string | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index a297a05813cee..957eb15e4fe1f 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1359,13 +1359,13 @@ public:
   template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
   append(_ForwardIterator __first, _ForwardIterator __last) {
-    size_type __sz  = size();
-    size_type __cap = capacity();
-    size_type __n   = static_cast<size_type>(std::distance(__first, __last));
-    if (__n == 0)
+    if (__first == __last)
       return *this;
 
     if (__string_is_trivial_iterator_v<_ForwardIterator> && !__addr_in_range(*__first)) {
+      size_type __sz  = size();
+      size_type __cap = capacity();
+      size_type __n   = static_cast<size_type>(std::distance(__first, __last));
       if (__cap - __sz < __n)
         __grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
       __annotate_increase(__n);

>From bd806f470a02647245b01bfa9209fc4937699bc0 Mon Sep 17 00:00:00 2001
From: Pavel Novikov <dev-ape at yandex.ru>
Date: Tue, 21 Jul 2026 14:42:18 +0300
Subject: [PATCH 2/2] [libc++] added benchmarks for `string::append()`

---
 .../benchmarks/containers/string.bench.cpp    | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index 776ee5f58b7e2..5ad80d1d3ec5a 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -17,6 +17,7 @@
 #include "benchmark/benchmark.h"
 #include "make_string.h"
 #include "test_macros.h"
+#include "test_iterators.h"
 
 constexpr size_t small_size = 5;
 constexpr size_t large_size = 30;
@@ -502,6 +503,45 @@ int main(int argc, char** argv) {
           [](auto bm) { bm->Arg(large_size); }); // for naming
   }
 
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> src(size, 'a');
+          auto getIterator = [&src](size_t i) {
+            // INT_MAX because we want overhead of ThrowingIterator without actually throwing
+            return ThrowingIterator<CharT>(src.data() + i, src.data() + src.size(), INT_MAX);
+          };
+          std::basic_string<CharT> string = src;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(string);
+            string.clear();
+            if constexpr (opaque)
+              benchmark::DoNotOptimize(src);
+            benchmark::DoNotOptimize(string.append(getIterator(0), getIterator(size)));
+          }
+        };
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
+  }
+
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
   benchmark::Shutdown();


From libcxx-commits at lists.llvm.org  Tue Jul 21 04:43:41 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 04:43:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a5f5b6d.eb165e5c.4506c.c79b@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/186692

>From aad29067d1d0d63e32d72e071dfd946e75bffad5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 12 Apr 2026 09:35:44 +0100
Subject: [PATCH 01/21] [libcxx] Implement `std::constant_wrapper`

ci

ci

review comments

review comments

ci

review comments
---
 .../containers/views/views.span/span.cons/deduct.pass.cpp  | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index 35d9b36f824aa..f5b590cd9631d 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -88,6 +88,13 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
+
+  {
+    std::span s{std::begin(arr), std::cw<3>};
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+    assert(s.size() == std::size(arr));
+    assert(s.data() == std::data(arr));
+  }
 #endif
 }
 

>From 21eec174092bdf128c5b62d9a67db7cc6997267d Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 8 Jun 2024 17:32:42 -0700
Subject: [PATCH 02/21] [libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

rebase + put in experimental + rename nontype to constant_arg

tests

remove experimental

test for constant_arg_ref

test for constant_arg_ptr

remove fun_ptr member

invoke

format

constexpr function ptr

double unwrapping

P3948R1

tests for new papers

format

lwg issue

lwg issues

review comment

remove constexpr

optimise small types

ci

ci

ci

ci
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/23.rst               |   3 +
 libcxx/docs/Status/Cxx26Issues.csv            |   4 +-
 libcxx/docs/Status/Cxx26Papers.csv            |   6 +-
 libcxx/include/CMakeLists.txt                 |   3 +
 libcxx/include/__functional/function_ref.h    |  33 ++
 .../__functional/function_ref_common.h        | 126 ++++++
 .../include/__functional/function_ref_impl.h  | 184 +++++++++
 libcxx/include/functional                     |   9 +
 libcxx/include/module.modulemap.in            |   3 +
 libcxx/include/version                        |   4 +-
 libcxx/modules/std/functional.inc             |   4 +
 .../assert.constant_arg_ptr.pass.cpp          |  31 ++
 .../assert.function_ptr.pass.cpp              |  26 ++
 .../constant_arg.mandates.verify.cpp          |  60 +++
 .../internal_call_signature.pass.cpp          | 136 +++++++
 .../functional.version.compile.pass.cpp       |  16 +-
 .../version.version.compile.pass.cpp          |  16 +-
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     | 107 +++++
 .../func.wrap.ref.ctor/assign.delete.pass.cpp | 115 ++++++
 .../constant_wrapper.pass.cpp                 | 175 ++++++++
 .../constant_wrapper_ptr.pass.cpp             | 359 +++++++++++++++++
 .../constant_wrapper_ref.pass.cpp             | 373 ++++++++++++++++++
 .../func.wrap.ref.ctor/copy.pass.cpp          | 131 ++++++
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   | 158 ++++++++
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  | 149 +++++++
 .../func.wrap.ref.ctor/move.pass.cpp          | 129 ++++++
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 156 ++++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 316 +++++++++++++++
 .../func.wrap.ref.inv/invoke.pass.cpp         | 362 +++++++++++++++++
 .../trivially_copyable.compile.pass.cpp       |  33 ++
 .../generate_feature_test_macro_components.py |   3 +-
 32 files changed, 3200 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/include/__functional/function_ref.h
 create mode 100644 libcxx/include/__functional/function_ref_common.h
 create mode 100644 libcxx/include/__functional/function_ref_impl.h
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..cd360ae2b1317 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -480,7 +480,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_fstream_native_handle``                        ``202306L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_function_ref``                                 *unimplemented*
+    ``__cpp_lib_function_ref``                                 ``202603L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_generate_random``                              *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index f7a8e5b04f1ea..5d295453dd79f 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,9 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c203fa424556a..d5f19b2aea902 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","","","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","","","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 2522dd98e4b9c..eb36e5a6b82f8 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."
@@ -192,9 +192,9 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","","","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","","","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..a0d5cf70d267d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -438,6 +438,9 @@ set(files
   __functional/compose.h
   __functional/default_searcher.h
   __functional/function.h
+  __functional/function_ref.h
+  __functional/function_ref_common.h
+  __functional/function_ref_impl.h
   __functional/hash.h
   __functional/identity.h
   __functional/invoke.h
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
new file mode 100644
index 0000000000000..b7fd3a4a2c79f
--- /dev/null
+++ b/libcxx/include/__functional/function_ref.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+
+#include <__config>
+#include <__functional/function_ref_common.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26
+// NOLINTBEGIN(readability-duplicate-include)
+
+#  define _LIBCPP_FUNCTION_REF_CV
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+#  define _LIBCPP_FUNCTION_REF_CV const
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+// NOLINTEND(readability-duplicate-include)
+#endif // _LIBCPP_STD_VER >= 26
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
new file mode 100644
index 0000000000000..61979231b56e8
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_COMMON_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+
+#include <__config>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/constant_wrapper.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization : false_type {};
+
+// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+// pointers
+// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
+// supported
+union __storage_func_ref_t {
+  void* __obj_ptr_;
+  void const* __obj_const_ptr_;
+  void (*__fn_ptr_)();
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        __obj_const_ptr_ = __ptr;
+      } else {
+        __obj_ptr_ = __ptr;
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
+    }
+  }
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
+      } else {
+        return static_cast<_Tp*>(__storage.__obj_ptr_);
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
+    }
+  }
+};
+
+template <class _Fp, class _Tp>
+struct __function_ref_bind {};
+
+// F is of the form R(*)(G, A...) noexcept(E) for a type G.
+template <bool _Noexcept, class _Tp, class _Rp, class _Gp, class... _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept(_Noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_Noexcept);
+};
+
+template <class _Tp, class _Mp, class _Gp>
+  requires is_object_v<_Mp>
+struct __function_ref_bind<_Mp _Gp::*, _Tp> {
+  using type _LIBCPP_NODEBUG = invoke_result_t<_Mp _Gp::*, _Tp&>() noexcept;
+};
+
+template <class _Fp, class _Tp>
+using __function_ref_bind_t _LIBCPP_NODEBUG = __function_ref_bind<_Fp, _Tp>::type;
+
+template <class _Fp>
+  requires is_function_v<_Fp>
+function_ref(_Fp*) -> function_ref<_Fp>;
+
+template <auto _Cw, class _Fn>
+  requires is_function_v<remove_pointer_t<_Fn>>
+function_ref(constant_wrapper<_Cw, _Fn>) -> function_ref<remove_pointer_t<_Fn>>;
+
+template <auto _Cw, class _Fn, class _Tp>
+function_ref(constant_wrapper<_Cw, _Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<_Fn, _Tp&>>;
+
+template <class>
+constexpr bool __is_constant_wrapper = false;
+
+template <auto _Value>
+constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
+
+template <class _Arg>
+struct __function_ref_arg_fwd {
+  using type _LIBCPP_NODEBUG = _Arg&&;
+};
+
+template <class _Arg>
+  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
+struct __function_ref_arg_fwd<_Arg> {
+  using type _LIBCPP_NODEBUG = _Arg;
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
new file mode 100644
index 0000000000000..a4c98d3031602
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -0,0 +1,184 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
+// and generates multiple versions of std::function_ref
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#include <__functional/invoke.h>
+#include <__memory/addressof.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/constant_wrapper.h>
+#include <__utility/forward.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#  error This header should only be included from function_ref.h
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
+    _NoExcept1,
+    _Rp,
+    _ArgTypes...>
+    : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
+
+template <class _Rp, class... _ArgTypes, bool __is_noexcept>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
+private:
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using =
+      _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
+          value;
+
+  template <class _Fn>
+  static constexpr bool __is_convertible_from_specialization_v =
+      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+
+  template <class... _Tp>
+  friend class function_ref;
+
+  template <class _Arg>
+  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+
+  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+
+  using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
+
+  __storage_t __storage_;
+  __call_t __call_;
+
+public:
+  template <class _Fp>
+    requires is_function_v<_Fp> && __is_invocable_using<_Fp>
+  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+      : __storage_(__fn_ptr),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+  }
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {}
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && __is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(__obj.__storage_), __call_(__obj.__call_) {}
+
+  template <auto _Cw, class _Fn>
+    requires __is_invocable_using<const _Fn&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
+      : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+    if constexpr (sizeof...(_ArgTypes) > 0 && (__constexpr_param<remove_cvref_t<_ArgTypes>> && ...)) {
+      static_assert(
+          !requires {
+            typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
+          },
+          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
+          "and produces a result with a structural type");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
+    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Tp>
+    requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
+      : __storage_(__obj_ptr_),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+
+    if constexpr (is_member_pointer_v<_Fn>) {
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(const function_ref&) noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+  template <class _Tp>
+    requires(!__is_convertible_from_specialization_v<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_wrapper<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
+
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
+    return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
+  }
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 9d588231d548d..df79d616a57e2 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -487,6 +487,11 @@ template <class  R, class ... ArgTypes>
 template <class  R, class ... ArgTypes>
   void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
 
+// [func.wrap.ref], non-owning wrapper
+template<class... S> class function_ref;                              // freestanding, not defined, since C++26
+template<class R, class... ArgTypes>
+  class function_ref<R(ArgTypes...) cv noexcept(noex)>;               // freestanding, since C++26
+
 template <class T> struct hash;
 
 template <> struct hash<bool>;
@@ -571,6 +576,10 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 #    include <__type_traits/unwrap_ref.h>
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+#    include <__functional/function_ref.h>
+#  endif
+
 #  include <version>
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..cb29fac536046 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1438,6 +1438,9 @@ module std {
     }
     module default_searcher             { header "__functional/default_searcher.h" }
     module function                     { header "__functional/function.h" }
+    module function_ref                 { header "__functional/function_ref.h" }
+    module function_ref_common          { header "__functional/function_ref_common.h" }
+    module function_ref_impl            { textual header "__functional/function_ref_impl.h" }
     module hash                         { header "__functional/hash.h" }
     module identity                     { header "__functional/identity.h" }
     module invoke                       { header "__functional/invoke.h" }
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..c62a7cbc8ba9c 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -131,7 +131,7 @@ __cpp_lib_freestanding_optional                         202311L <optional>
 __cpp_lib_freestanding_string_view                      202311L <string_view>
 __cpp_lib_freestanding_variant                          202311L <variant>
 __cpp_lib_fstream_native_handle                         202306L <fstream>
-__cpp_lib_function_ref                                  202306L <functional>
+__cpp_lib_function_ref                                  202603L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
@@ -593,7 +593,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 #   define __cpp_lib_fstream_native_handle              202306L
 # endif
-// # define __cpp_lib_function_ref                         202306L
+# define __cpp_lib_function_ref                         202603L
 // # define __cpp_lib_generate_random                      202403L
 // # define __cpp_lib_hazard_pointer                       202306L
 // # define __cpp_lib_inplace_vector                       202406L
diff --git a/libcxx/modules/std/functional.inc b/libcxx/modules/std/functional.inc
index 9ef8f584611fc..be804b19f75f8 100644
--- a/libcxx/modules/std/functional.inc
+++ b/libcxx/modules/std/functional.inc
@@ -95,6 +95,10 @@ export namespace std {
 
   using std::function;
 
+#if _LIBCPP_STD_VER >= 26
+  using std::function_ref;
+#endif // _LIBCPP_STD_VER >= 26
+
   using std::swap;
 
   using std::operator==;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
new file mode 100644
index 0000000000000..9703de0a59c2c
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+// Preconditions: If is_member_pointer_v<F> is true, obj is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+struct A {
+  void f() {}
+};
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(std::function_ref<void()>(std::cw<&A::f>, static_cast<A*>(nullptr)),
+                             "the object pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
new file mode 100644
index 0000000000000..e69776d0e3c90
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<class F> function_ref(F* f) noexcept;
+// Preconditions: f is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::function_ref<void()>(static_cast<void (*)()>(nullptr)), "the function pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
new file mode 100644
index 0000000000000..519957eec6878
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto c, class F>
+//   constexpr function_ref(constant_wrapper<c, F>) noexcept;
+// template<auto c, class F, class U>
+//   constexpr function_ref(constant_wrapper<c, F>, U&& obj) noexcept;
+// template<auto c, class F, class T>
+//   constexpr function_ref(constant_wrapper<c, F>, cv T* obj) noexcept;
+
+// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
+
+// For the first overload,
+// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+
+#include <functional>
+#include <utility>
+
+struct A {
+  void f();
+};
+
+struct B {
+  constexpr int operator()(std::constant_wrapper<42>) const { return 42; }
+  constexpr int operator()(int) const { return 42; }
+};
+
+// clang-format off
+void test() {
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+
+  int i;
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  A a;
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+}
+// clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
new file mode 100644
index 0000000000000..05756cfc3b3b8
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Track number of moves
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+struct Small {};
+
+struct Big {
+  char c[128];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
+// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
+template <>
+class std::function_ref<int> {
+  using storage = std::__storage_func_ref_t;
+  void test() {
+    {
+      // by value small argument
+      // the internal function should pass by value
+      using call = std::function_ref<void(Small)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
+    }
+    {
+      // by value big argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Big)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
+    }
+    {
+      // by value non-trivial small argument
+      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
+      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
+    }
+    {
+      // by lvalue reference argument
+      // the internal function should pass by lvalue reference
+      using call = std::function_ref<void(Small&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
+    }
+    {
+      // by rvalue reference argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Small&&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
+    }
+  }
+};
+
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
+void test() {
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In this case, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index b7b7d0334830a..bcc453880c7e1 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -494,17 +494,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_invoke
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 f2d74cc99d864..c990ef86c3be1 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
@@ -7138,17 +7138,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_gcd_lcm
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
new file mode 100644
index 0000000000000..029cb19d54f6a
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+int fn(int, float) { return 42; }
+
+int fn_noexcept(int, float) noexcept { return 42; }
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem(int, float) { return 42; }
+  int fn_mem_ref(int, float) & { return 42; }
+  int fn_mem_const(int, float) const { return 42; }
+  int fn_mem_const_ref(int, float) const& { return 42; }
+  int fn_mem_noexcept(int, float) noexcept { return 42; }
+};
+
+void test() {
+  // template<class F>
+  //  function_ref(F*) -> function_ref<F>;
+  {
+    std::function_ref fn_ref = fn;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = fn_noexcept;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F>
+  //  function_ref(constant_wrapper<c, F>) -> function_ref<...>;
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn_noexcept>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F, class T>
+  //  function_ref(constant_wrapper<c, F>, T&&) -> function_ref<...>;
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
+  }
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn_noexcept>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float) noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&() noexcept>>);
+  }
+  {
+    const S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&() noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_noexcept>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
new file mode 100644
index 0000000000000..b4df4392a2ade
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class T> function_ref& operator=(T) = delete;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - T is not the same type as function_ref,
+// - is_pointer_v<T> is false, and
+// - T is not a specialization of constant_wrapper.
+
+// non const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[] {}>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
+
+// const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[] { return 42; }>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[](int) { return 42; }>>);
+
+// non-const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[] noexcept {} >>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+// const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[] noexcept {}>>);
+static_assert(
+    !std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+int forty_two() { return 42; }
+
+void test() {
+  {
+    std::function_ref<int()> f(std::cw<[] { return 41; }>);
+    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = &forty_two;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const noexcept
+    std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
new file mode 100644
index 0000000000000..4b425ee03e286
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
@@ -0,0 +1,175 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f> constexpr function_ref(constant_wrapper<f>) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&> is true.
+
+auto l1          = [] {};
+auto l1_noexcept = [] noexcept {};
+auto l2          = [](int) {};
+auto l2_noexcept = [](int) noexcept {};
+
+struct NonConstInvocable {
+  void operator()() noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+// const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    std::function_ref<void()> f = std::cw<[] {}>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
new file mode 100644
index 0000000000000..dd6e10e8a3289
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -0,0 +1,359 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&, cv T*> is true.
+
+auto l1                = [](int*) {};
+auto l1_const          = [](const int*) {};
+auto l1_noexcept       = [](int*) noexcept {};
+auto l1_const_noexcept = [](const int*) noexcept {};
+auto l2                = [](int*, double) {};
+auto l2_const          = [](const int*, double) {};
+auto l2_noexcept       = [](int*, double) noexcept {};
+auto l2_const_noexcept = [](const int*, double) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(int*) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, const int*>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2_const>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const noexcept>,
+                                      std::constant_wrapper<l2_const_noexcept>,
+                                      int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_const_noexcept>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<l1_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const noexcept>,
+                                              std::constant_wrapper<l2_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<&A::f_const_noexcept>,
+                                              A*>);
+
+double f1(const int* x, double y) noexcept { return *x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int*) {}>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int*) {}>, &i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int* j, double d) {
+          *j = 5;
+          return *j + d;
+        }>,
+        &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
new file mode 100644
index 0000000000000..485fa95ca2998
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -0,0 +1,373 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class U>
+//   constexpr function_ref(constant_wrapper<f>, U&& obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_rvalue_reference_v<U&&> is false, and
+// - is-invocable-using<const F&, cv T&> is true.
+
+auto l1          = [](int) {};
+auto l1_noexcept = [](int) noexcept {};
+auto l2          = [](int, double) {};
+auto l2_noexcept = [](int, double) noexcept {};
+auto l3          = [](int&) {};
+auto l3_noexcept = [](int&) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(long) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&&>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int) {}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int) {}>, i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int& j, double d) {
+          j = 5;
+          return j + d;
+        }>,
+        i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
new file mode 100644
index 0000000000000..61b70a559d786
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
new file mode 100644
index 0000000000000..1a8b26f86c8be
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
new file mode 100644
index 0000000000000..f142b81367ef1
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> function_ref(F* f) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_function_v<F> is true, and
+// - is-invocable-using<F> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (*)(int)>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A (*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, A)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+
+// const noexcept(true)
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, A) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              void (*)(int, double) noexcept>);
+
+int fn() { return 42; }
+
+int fn_maythrow(int i, A a) { return i - a.i; }
+int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+
+void foo(int) {}
+void bar(int) noexcept {}
+struct Int {
+  int i;
+  Int(int ii) noexcept : i(ii) {}
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+void test() {
+  {
+    // simple case
+    std::function_ref<int()> f(&fn);
+    assert(f() == 42);
+  }
+  {
+    // explicit(false)
+    std::function_ref<int()> f = &fn;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int(int, A)> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // noexcept
+    std::function_ref<int(int, A) noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // const
+    auto l = [](int x, int y, int z) { return x + y - z; };
+    std::function_ref<int(int, int, int) const> f(+l);
+    assert(f(2, 3, 4) == 1);
+  }
+  {
+    // const noexcept
+    std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+
+  {
+    std::function_ref<Int(int, int, int)> f(&needs_conversion);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(&needs_conversion);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(&needs_conversion);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
+    assert(f4(1, 2, 3).i == 6);
+
+    {
+      std::function_ref r1 = foo;
+      std::function_ref r2 = bar;
+      r1                   = r2; // ok
+    }
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
new file mode 100644
index 0000000000000..0f27bba821391
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
new file mode 100644
index 0000000000000..b40dfa9f2cf4b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
new file mode 100644
index 0000000000000..56d6dd3b7109b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -0,0 +1,316 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> constexpr function_ref(F&&) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - remove_cvref_t<F> is not the same type as function_ref,
+// - is_member_pointer_v<T> is false, and
+// - is-invocable-using<cv T&> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+constexpr auto l1 = [] {};
+using L1          = std::remove_cvref_t<decltype(l1)>;
+
+constexpr auto l2 = [] { return A{5}; };
+using L2          = std::remove_cvref_t<decltype(l2)>;
+
+constexpr auto l2_noexcept = [] noexcept { return A{5}; };
+using L2Noexcept           = std::remove_cvref_t<decltype(l2_noexcept)>;
+
+constexpr auto l3 = [](int x, double d) { return x + d; };
+using L3          = std::remove_cvref_t<decltype(l3)>;
+
+constexpr auto l3_noexcept = [](int x, double d) noexcept { return x + d; };
+using L3Noexcept           = std::remove_cvref_t<decltype(l3_noexcept)>;
+
+struct NonConstInvocable {
+  int i;
+
+  int operator()(int x, double y) {
+    ++i;
+    return x + y + i;
+  }
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L2&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2&>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2 const&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const>, std::function_ref<int(int, double) const>>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(int, double)>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double)>, const NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>,
+                                              std::function_ref<int(int, double) const>>);
+
+// const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                      std::function_ref<int(int, double) const noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              std::function_ref<int(int, double) const noexcept>>);
+
+struct F {
+  int i;
+  int operator()(auto&&...) { return 5 + i; }
+
+  int operator()(auto&&...) const { return 6 + i; }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int fn() { return 5; }
+constexpr auto fn_noexcept = []() noexcept { return 6; };
+
+const auto one = []() noexcept { return 1; };
+const auto two = []() noexcept { return 2; };
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(l1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit(false)
+    std::function_ref<void()> f = l1;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<A() noexcept> f(l2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      auto a = f();
+      assert(a.i == 5);
+    }
+  }
+  {
+    // const
+    std::function_ref<double(int, double) const> f(l3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(l3_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // no copies of original callable
+    auto local = [i = 5] mutable { return i++; };
+    std::function_ref<int()> f(local);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 5);
+      assert(local() == 6);
+      assert(f() == 7);
+    }
+  }
+  {
+    // const correctness
+    F f{5};
+
+    std::function_ref<int()> f1(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1() == 10);
+      assert(std::as_const(f1)() == 10);
+    }
+
+    std::function_ref<int() const> f2(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 11);
+      assert(std::as_const(f2)() == 11);
+    }
+  }
+  {
+    // with conversions
+    NeedsConversion c{};
+
+    std::function_ref<Int(int, int, int)> f(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  {
+    // noexcept conversion
+    std::function_ref<int() noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const conversion
+    std::function_ref<int() const> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() noexcept> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to const conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() const> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // P3961R1 Less double indirection in function_ref
+    // double unwrapping
+    std::function_ref<int() const noexcept> f1(std::cw<one>);
+    std::function_ref<int()> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 1 || f2() == 2);
+      LIBCPP_ASSERT(f2() == 1);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
new file mode 100644
index 0000000000000..1cd0c769ce4d3
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -0,0 +1,362 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// R operator()(ArgTypes... args) const noexcept(noex);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "MoveOnly.h"
+
+template <class T, class... Args>
+concept ConstInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) };
+};
+
+template <class T, class... Args>
+concept ConstNoexceptInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) } noexcept;
+};
+
+static_assert(ConstInvocable<std::function_ref<void()>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void()>>);
+
+static_assert(ConstInvocable<std::function_ref<void() noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() noexcept>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void() const>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() const noexcept>>);
+
+struct S {
+  int data = 42;
+  int operator()(int& x) const noexcept { return data + x; }
+  int operator()(const int& x) const noexcept { return data + x + 1; }
+  int operator()(int&& x) const noexcept { return data + x + 2; }
+  int operator()(const int&& x) const noexcept { return data + x + 3; }
+};
+
+struct S2 {
+  double operator()(int x, int y, int z) noexcept { return x + y + z; }
+
+  double operator()(int x, int y, int z) const noexcept { return x + y + z + 1; }
+};
+
+struct Big {
+  char c[128];
+};
+
+struct S3Ref {
+  char operator()(Big& b) const noexcept { return b.c[0]; }
+};
+
+struct S3Value {
+  char operator()(Big b) const noexcept { return b.c[0]; }
+};
+
+void test_default() {
+  {
+    std::function_ref<void()> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&)> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly)> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int)> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&)> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&)> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&)> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&)> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int)> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int)> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&)> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const() {
+  {
+    std::function_ref<void() const> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_noexcept() {
+  {
+    std::function_ref<void() noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const_noexcept() {
+  {
+    std::function_ref<void() const noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test() {
+  test_default();
+  test_const();
+  test_noexcept();
+  test_const_noexcept();
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
new file mode 100644
index 0000000000000..72e26f13868ea
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
+#include <concepts>
+#include <functional>
+#include <type_traits>
+
+struct A {
+  A() {}
+  A(const A&) {}
+  ~A() {}
+};
+
+static_assert(std::copyable<std::function_ref<A(const A&)>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&)>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) noexcept>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const noexcept>>);
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..e54773a0aa2db 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -708,10 +708,9 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_function_ref",
             "values": {
-                "c++26": 202306  # P0792R14 function_ref: a type-erased callable reference
+                "c++26": 202603  # P3948R1 constant_wrapper is the only tool needed for passing constant expressions via function arguments
             },
             "headers": ["functional"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_gcd_lcm",

>From cc0ed542effdeedda373bdbdccabc3c7fbea4cae Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 25 Apr 2026 14:20:04 +0100
Subject: [PATCH 03/21] review comments

---
 .../__functional/function_ref_common.h        | 22 ++++++-------------
 .../include/__functional/function_ref_impl.h  |  2 +-
 .../internal_call_signature.pass.cpp          |  2 +-
 .../constant_wrapper_ref.pass.cpp             |  8 +++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 12 ++++++++++
 5 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 61979231b56e8..72ded36d65107 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -16,6 +16,7 @@
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
 
@@ -37,21 +38,16 @@ struct __is_convertible_from_specialization : false_type {};
 // pointers
 // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
 // supported
-union __storage_func_ref_t {
+union __function_ref_storage {
   void* __obj_ptr_;
-  void const* __obj_const_ptr_;
   void (*__fn_ptr_)();
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage() noexcept : __obj_ptr_(nullptr) {}
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage(_Tp* __ptr) noexcept {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        __obj_const_ptr_ = __ptr;
-      } else {
-        __obj_ptr_ = __ptr;
-      }
+      __obj_ptr_ = const_cast<remove_cv_t<_Tp>*>(__ptr);
     } else {
       static_assert(is_function_v<_Tp>);
       __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
@@ -59,13 +55,9 @@ union __storage_func_ref_t {
   }
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__function_ref_storage __storage) {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
-      } else {
-        return static_cast<_Tp*>(__storage.__obj_ptr_);
-      }
+      return static_cast<_Tp*>(__storage.__obj_ptr_);
     } else {
       static_assert(is_function_v<_Tp>);
       return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index a4c98d3031602..36841d6745955 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -72,7 +72,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Arg>
   using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
 
-  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+  using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
   using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 05756cfc3b3b8..1e5df6413daf0 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -31,7 +31,7 @@ struct SmallButNonTrivial {
 // specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
 template <>
 class std::function_ref<int> {
-  using storage = std::__storage_func_ref_t;
+  using storage = std::__function_ref_storage;
   void test() {
     {
       // by value small argument
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index 485fa95ca2998..d3d5f6827193d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -362,6 +362,14 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // volatile objects
+    volatile int i = 5;
+    std::function_ref<int() > f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 56d6dd3b7109b..9ab012b3a04e8 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -161,6 +161,10 @@ constexpr auto fn_noexcept = []() noexcept { return 6; };
 const auto one = []() noexcept { return 1; };
 const auto two = []() noexcept { return 2; };
 
+struct VolatileCallable {
+  int operator()() volatile const { return 42; }
+};
+
 constexpr bool test() {
   {
     std::function_ref<void()> f(l1);
@@ -305,6 +309,14 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // volatile objects
+    const volatile VolatileCallable vc{};
+    std::function_ref<int()> f(vc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
 
   return true;
 }

>From c873a3274a048eef12a10c54fa9809055e200399 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 26 Apr 2026 09:41:10 +0100
Subject: [PATCH 04/21] remove stale comments

---
 libcxx/include/__functional/function_ref_common.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 72ded36d65107..c5adb8cbc7d52 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -34,10 +34,6 @@ class function_ref;
 template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization : false_type {};
 
-// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
-// pointers
-// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
-// supported
 union __function_ref_storage {
   void* __obj_ptr_;
   void (*__fn_ptr_)();

>From 8c049d9bfd3086368c5c254fa6ca8d8403808b12 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 14:06:05 +0100
Subject: [PATCH 05/21] fix bug

---
 .../__functional/function_ref_common.h        |  2 +-
 .../include/__functional/function_ref_impl.h  | 19 ++++++++++++-------
 .../internal_call_signature.pass.cpp          |  4 +++-
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  6 +++---
 .../func.wrap.ref.ctor/ref.pass.cpp           | 11 +++++++++++
 5 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index c5adb8cbc7d52..60bc6ef87af4f 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -31,7 +31,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <class _Fn1, class _Fn2>
 struct __is_convertible_from_specialization : false_type {};
 
 union __function_ref_storage {
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 36841d6745955..55755e9804c95 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -46,14 +46,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization<
-    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
-    _NoExcept1,
-    _Rp,
-    _ArgTypes...>
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) const noexcept(_NoExcept2)> >
     : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
 
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) noexcept(_NoExcept2)> >
+    : _And<is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)>,
+           is_convertible<_LIBCPP_FUNCTION_REF_CV int&, int&>> {};
+
 template <class _Rp, class... _ArgTypes, bool __is_noexcept>
 class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
 private:
@@ -62,9 +67,9 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
           value;
 
-  template <class _Fn>
+  template <class _Fn2>
   static constexpr bool __is_convertible_from_specialization_v =
-      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+      __is_convertible_from_specialization<function_ref, _Fn2>::value;
 
   template <class... _Tp>
   friend class function_ref;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 1e5df6413daf0..ebdf683791694 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -8,7 +8,9 @@
 
 // REQUIRES: std-at-least-c++26
 
-// Track number of moves
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
 
 #include <cassert>
 #include <functional>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index b4df4392a2ade..cb84b3a183920 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -35,9 +35,9 @@ static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrap
 static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
 
 // const noexcept(false)
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
@@ -61,7 +61,7 @@ static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::con
 // const noexcept(true)
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 9ab012b3a04e8..6b8b7c71f0d08 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -309,6 +309,17 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // P3961R1 Less double indirection in function_ref
+    // is-convertible-from-specialization<remove_cv_t<T>> is false
+    std::function_ref<int()> f1(std::cw<one>);
+    std::function_ref<int() const> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 2);
+    }
+  }
   {
     // volatile objects
     const volatile VolatileCallable vc{};

>From bb8ccae3578167103382ca0e19d92c4fc7a5cc20 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 17:28:36 +0100
Subject: [PATCH 06/21] ci

---
 libcxx/include/__functional/function_ref_impl.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 55755e9804c95..454b0ffb4afb3 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -15,6 +15,7 @@
 #include <__functional/invoke.h>
 #include <__memory/addressof.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/conjunction.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>

>From 86e636b10f81465bd9bf8b782cbaed3eb9e5b852 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 10:39:44 +0100
Subject: [PATCH 07/21] static operator

---
 .../include/__functional/function_ref_impl.h  | 23 +++++++++++----
 .../func.wrap.ref.ctor/ref.pass.cpp           | 28 +++++++++++++++++++
 2 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 454b0ffb4afb3..ab65382782fb2 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
@@ -99,12 +100,22 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
-      : __storage_(std::addressof(__obj)),
-        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
-        }) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
+    using _Dn = remove_cv_t<_Tp>;
+    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
+                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+                  }) {
+      __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    } else {
+      __storage_ = __storage_t(std::addressof(__obj)),
+      __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    }
+  }
 
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 6b8b7c71f0d08..2684ec3548f8e 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -328,6 +328,34 @@ constexpr bool test() {
       assert(f() == 42);
     }
   }
+  {
+    // static call operator
+    struct StaticCall {
+      static int operator()(int x, int y) { return x + y; }
+    };
+    StaticCall sc;
+    std::function_ref<int(int, int)> f(sc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2) == 3);
+    }
+  }
+  {
+    // overload set with static and non-static call operator
+    struct OverloadSet {
+      static int operator()(int x) { return x + 1; }
+      int operator()(long x) const { return x + 2; }
+    };
+    OverloadSet os;
+    std::function_ref<int(int)> f1(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1(1) == 2);
+    }
+
+    std::function_ref<int(long)> f2(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1L) == 3);
+    }
+  }
 
   return true;
 }

>From d7f110369b970835f08793ec961252610cc56336 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 19:26:38 +0100
Subject: [PATCH 08/21] work around clang 22 bug

---
 libcxx/include/__functional/function_ref_common.h             | 4 ++++
 libcxx/include/__functional/function_ref_impl.h               | 4 +---
 .../func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp   | 1 +
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 60bc6ef87af4f..bfe3d938056f0 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -19,6 +19,7 @@
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
+#include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -107,6 +108,9 @@ struct __function_ref_arg_fwd<_Arg> {
   using type _LIBCPP_NODEBUG = _Arg;
 };
 
+template <class _Fn, class... _Args>
+concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
+
 #endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index ab65382782fb2..7a173dee7fa4f 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -102,9 +102,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
     using _Dn = remove_cv_t<_Tp>;
-    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
-                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
-                  }) {
+    if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
       };
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 2684ec3548f8e..11b993e4a3ec3 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -351,6 +351,7 @@ constexpr bool test() {
       assert(f1(1) == 2);
     }
 
+    LIBCPP_STATIC_ASSERT(!std::__statically_callable<OverloadSet, long>);
     std::function_ref<int(long)> f2(os);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f2(1L) == 3);

>From 0d0dccaa28d32b237c1465c13cb9110ed5c15d40 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:12:34 +0100
Subject: [PATCH 09/21] review comments

---
 libcxx/include/__functional/function_ref_common.h | 15 +++++++--------
 libcxx/include/__functional/function_ref_impl.h   |  6 +++---
 .../constant_arg.mandates.verify.cpp              |  2 +-
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index bfe3d938056f0..0f40a07073cfc 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -15,7 +15,8 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
-#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
@@ -98,15 +99,13 @@ template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
 template <class _Arg>
-struct __function_ref_arg_fwd {
-  using type _LIBCPP_NODEBUG = _Arg&&;
-};
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
 
 template <class _Arg>
-  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
-struct __function_ref_arg_fwd<_Arg> {
-  using type _LIBCPP_NODEBUG = _Arg;
-};
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
 
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a173dee7fa4f..3424a99bdd174 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -77,7 +77,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   friend class function_ref;
 
   template <class _Arg>
-  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+  using __arg_t _LIBCPP_NODEBUG = _If<__register_passable<_Arg>, _Arg, _Arg&&>;
 
   using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
@@ -135,8 +135,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
           !requires {
             typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
           },
-          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
-          "and produces a result with a structural type");
+          "cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref "
+          "constructed from cw would be ambiguous");
     }
   }
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 519957eec6878..63cfbc0e97c9b 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -41,7 +41,7 @@ void test() {
   // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
   std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}

>From 6b89539b99bb7714036147c326551253b8eccbbf Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:13:48 +0100
Subject: [PATCH 10/21] use std::forward instead of static_cast

---
 libcxx/include/__functional/function_ref_impl.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 3424a99bdd174..d276c78fd09e6 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -92,7 +92,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -104,13 +104,13 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     using _Dn = remove_cv_t<_Tp>;
     if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return _Dn::operator()(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     } else {
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -125,7 +125,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     requires __is_invocable_using<const _Fn&>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
       : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -146,7 +146,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -160,7 +160,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(__obj_ptr_),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");

>From 28d5c298b92d62aba24962b5cf73cbd40315dc34 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:14:02 +0100
Subject: [PATCH 11/21] format

---
 .../func.wrap.ref.ctor/constant_arg.mandates.verify.cpp      | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 63cfbc0e97c9b..c84cca2793bac 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -17,8 +17,9 @@
 
 // Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
 
-// For the first overload,
-// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+// For the first overload, f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>...
+// satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)>
+// is not a valid type.
 
 #include <functional>
 #include <utility>

>From a1f306c3084b8b7b1a9afc928f777748512337ca Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 20 Jun 2026 16:28:00 +0100
Subject: [PATCH 12/21] review commnets

---
 libcxx/include/CMakeLists.txt                 |   1 +
 .../__functional/function_ref_common.h        |   9 -
 .../include/__functional/function_ref_impl.h  |   8 +-
 .../include/__type_traits/register_passable.h |  38 ++++
 libcxx/include/module.modulemap.in            |   1 +
 .../constant_arg.mandates.verify.cpp          |  26 +--
 .../internal_call_signature.pass.cpp          | 138 ---------------
 .../internal_call_signature.sh.cpp            |  68 ++++++++
 .../views.span/span.cons/deduct.pass.cpp      |   7 -
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     |   9 +
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  24 ++-
 .../constant_wrapper_ptr.pass.cpp             |  60 ++++++-
 .../constant_wrapper_ref.pass.cpp             |  59 ++++++-
 .../func.wrap.ref.ctor/copy.pass.cpp          |   5 +
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   |  71 ++++++--
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  |   6 -
 .../func.wrap.ref.ctor/move.pass.cpp          |  70 +-------
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 102 ++---------
 .../func.wrap.ref.inv/invoke.pass.cpp         | 162 ++++++++++++++++++
 19 files changed, 506 insertions(+), 358 deletions(-)
 create mode 100644 libcxx/include/__type_traits/register_passable.h
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index a0d5cf70d267d..fcea3b7601182 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -914,6 +914,7 @@ set(files
   __type_traits/rank.h
   __type_traits/reference_constructs_from_temporary.h
   __type_traits/reference_converts_from_temporary.h
+  __type_traits/register_passable.h
   __type_traits/remove_all_extents.h
   __type_traits/remove_const.h
   __type_traits/remove_const_ref.h
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 0f40a07073cfc..e6fa031432a91 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -98,15 +98,6 @@ constexpr bool __is_constant_wrapper = false;
 template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
-template <class _Arg>
-concept __itanium_trivial_for_calls =
-    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
-    is_trivially_move_constructible_v<_Arg>;
-
-template <class _Arg>
-concept __register_passable =
-    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
-
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
 
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index d276c78fd09e6..7a968fc64de70 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/register_passable.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
@@ -92,7 +93,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
+          _Fp* __fn_ptr1 = __storage_t::template __get<_Fp>(__storage);
+          return std::invoke_r<_Rp>(__fn_ptr1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -110,7 +112,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
+        return std::invoke_r<_Rp>(__obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -145,7 +147,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          auto& __obj1 = static_cast<_LIBCPP_FUNCTION_REF_CV _Tp&>(*__storage_t::template __get<_Tp>(__storage));
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
diff --git a/libcxx/include/__type_traits/register_passable.h b/libcxx/include/__type_traits/register_passable.h
new file mode 100644
index 0000000000000..21e23e62052c1
--- /dev/null
+++ b/libcxx/include/__type_traits/register_passable.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___TYPE_TRAITS_REGISTER_PASSABLE_H
+#define _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
+
+#include <__config>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Arg>
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
+
+template <class _Arg>
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
+
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index cb29fac536046..e3b9e5ac09cb3 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -351,6 +351,7 @@ module std_core {
       header "__type_traits/reference_converts_from_temporary.h"
       export std_core.type_traits.integral_constant
     }
+    module register_passable                          { header "__type_traits/register_passable.h" }
     module remove_all_extents                         { header "__type_traits/remove_all_extents.h" }
     module remove_const_ref                           { header "__type_traits/remove_const_ref.h" }
     module remove_const                               { header "__type_traits/remove_const.h" }
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index c84cca2793bac..08e8b8e9b8b52 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -35,27 +35,27 @@ struct B {
 
 // clang-format off
 void test() {
-  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
+  // expected-error-re@*:* {{static assertion failed {{.*}}cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
-  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   A a;
-  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 }
 // clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
deleted file mode 100644
index ebdf683791694..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-struct Small {};
-
-struct Big {
-  char c[128];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
-// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
-template <>
-class std::function_ref<int> {
-  using storage = std::__function_ref_storage;
-  void test() {
-    {
-      // by value small argument
-      // the internal function should pass by value
-      using call = std::function_ref<void(Small)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
-    }
-    {
-      // by value big argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Big)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
-    }
-    {
-      // by value non-trivial small argument
-      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
-      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
-    }
-    {
-      // by lvalue reference argument
-      // the internal function should pass by lvalue reference
-      using call = std::function_ref<void(Small&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
-    }
-    {
-      // by rvalue reference argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Small&&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
-    }
-  }
-};
-
-struct TrackCopyMove {
-  mutable int copy_count = 0;
-  int move_count         = 0;
-
-  TrackCopyMove() = default;
-  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
-    ++copy_count;
-    ++other.copy_count;
-  }
-
-  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
-    ++move_count;
-    ++other.move_count;
-  }
-  TrackCopyMove& operator=(const TrackCopyMove& other) {
-    ++copy_count;
-    ++other.copy_count;
-    return *this;
-  }
-  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
-    ++move_count;
-    ++other.move_count;
-    return *this;
-  }
-};
-
-void test() {
-  {
-    // Arg type is an lvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&)> f = lambda;
-    f(t);
-  }
-  {
-    // Arg type is an rvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&&)> f = lambda;
-    f(std::move(t));
-  }
-  {
-    // Arg type is a prvalue, we should move but not copy the object
-    // In this case, where the type is not trivially copyable, the object should be
-    // moved exactly once when passing into the lambda. The internal functions
-    // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) {
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 1);
-    };
-    std::function_ref<void(TrackCopyMove)> f = lambda;
-    f(TrackCopyMove{});
-  }
-}
-
-int main(int, char**) {
-  test();
-
-  return 0;
-}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
new file mode 100644
index 0000000000000..a32a1083b5596
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// REQUIRES: has-filecheck
+
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
+
+struct Small {
+  char c[8];
+};
+
+struct Big {
+  char c[256];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// CHECK: define{{.*}} @_Z10test_small
+// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
+// CHECK: %3 = alloca %struct.Small, align 1
+// CHECK: %6 = alloca %struct.Small, align 1
+// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
+// CHECK: store i64 %1, ptr %7, align 1
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
+// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
+// CHECK: %15 = load i64, ptr %14, align 1
+// CHECK: call void %10(ptr %13, i64 %15)
+//
+// The above has two local copies of Small object. It loads %1 to local copy %3, and then
+// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
+void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
+
+// CHECK: define{{.*}} @_Z8test_big
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
+//
+// The above directly calls the function pointer with the parameter Big object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the Big object by reference
+void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
+
+// CHECK: define{{.*}} @_Z22test_small_non_trivial
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
+//
+// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the SmallButNonTrivial object by reference
+void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index f5b590cd9631d..35d9b36f824aa 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -88,13 +88,6 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
-
-  {
-    std::span s{std::begin(arr), std::cw<3>};
-    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
-    assert(s.size() == std::size(arr));
-    assert(s.data() == std::data(arr));
-  }
 #endif
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
index 029cb19d54f6a..730a07202062d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -8,6 +8,15 @@
 
 // REQUIRES: std-at-least-c++26
 
+// template<class F>
+// function_ref(F*) -> function_ref<F>;
+//
+// template<auto c, class F0>
+// function_ref(constant_wrapper<c, F0>) -> function_ref<see below>;
+//
+// template<auto c, class F, class T>
+// function_ref(constant_wrapper<c, F>, T&&) -> function_ref<see below>;
+
 #include <cassert>
 #include <functional>
 #include <utility>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index cb84b3a183920..5866488be4a94 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -11,6 +11,7 @@
 // template<class T> function_ref& operator=(T) = delete;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -73,38 +74,49 @@ static_assert(
 
 int forty_two() { return 42; }
 
+// These runtime tests are testing that when the constraints are not met,
+// the assignment operator is not deleted and the implicit constructor
+// and the move assignment are called
 void test() {
   {
     std::function_ref<int()> f(std::cw<[] { return 41; }>);
-    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f =
+        std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = &forty_two;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = &forty_two;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // noexcept
     std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() noexcept>&> decltype(auto) result = f = std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const noexcept
     std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() const noexcept>&> decltype(auto) result = f =
+        std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
index dd6e10e8a3289..74f52063b6788 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -94,6 +94,7 @@ static_assert(
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
 static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, const int*>);
 
 static_assert(
     !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
@@ -180,6 +181,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int*) const noexcept { return 4; }
+  constexpr int operator()(const int*) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -231,6 +237,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -248,6 +262,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -263,39 +285,51 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -348,6 +382,20 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, &i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, &i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, &i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index d3d5f6827193d..979da32e60c3d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -199,6 +199,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int&) const noexcept { return 4; }
+  constexpr int operator()(const int&) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -248,6 +253,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -264,6 +277,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -278,37 +299,49 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -370,6 +403,20 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
index 61b70a559d786..7f837149cc6b5 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -22,6 +22,11 @@ static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
 double f1(int x, double y) noexcept { return x + y; }
 
 struct Int {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
index 1a8b26f86c8be..53c79b4097362 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -11,6 +11,7 @@
 // constexpr function_ref& operator=(const function_ref&) noexcept = default;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -22,6 +23,11 @@ static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
@@ -41,7 +47,8 @@ constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = f;
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -51,7 +58,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = f;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
@@ -62,7 +70,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -72,7 +81,8 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -82,32 +92,47 @@ constexpr bool test() {
     // with conversions
     std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
         std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
@@ -117,31 +142,47 @@ constexpr bool test() {
     // with conversions function pointer
     std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
     std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions function pointer
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index f142b81367ef1..aa0ccf8d67993 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -134,12 +134,6 @@ void test() {
 
     std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
     assert(f4(1, 2, 3).i == 6);
-
-    {
-      std::function_ref r1 = foo;
-      std::function_ref r2 = bar;
-      r1                   = r2; // ok
-    }
   }
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
index 0f27bba821391..025df83ad538d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -8,6 +8,8 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
 #include <functional>
 #include <utility>
@@ -20,18 +22,12 @@ static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-double f1(int x, double y) noexcept { return x + y; }
-
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+double f1(int x, double y) noexcept { return x + y; }
 
 constexpr bool test() {
   {
@@ -66,58 +62,6 @@ constexpr bool test() {
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
index b40dfa9f2cf4b..10620f5d48725 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -8,7 +8,10 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -20,26 +23,20 @@ static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
-
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
-int zero(Int, Int, Int) noexcept { return 0; }
-
 constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -49,8 +46,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = std::move(f);
-
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
       assert(f2() == 42);
@@ -60,7 +57,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -70,81 +68,13 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
-        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 1cd0c769ce4d3..0dced07c439d6 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -67,6 +67,32 @@ struct S3Value {
   char operator()(Big b) const noexcept { return b.c[0]; }
 };
 
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
 void test_default() {
   {
     std::function_ref<void()> f = [] {};
@@ -135,6 +161,40 @@ void test_default() {
     std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const() {
@@ -205,6 +265,40 @@ void test_const() {
     std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_noexcept() {
@@ -276,6 +370,40 @@ void test_noexcept() {
     std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const_noexcept() {
@@ -347,6 +475,40 @@ void test_const_noexcept() {
     std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test() {

>From 01c84f58558404d03091941a872d92cbea551fbd Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:38:35 +0100
Subject: [PATCH 13/21] format

---
 .../func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp       | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 0dced07c439d6..820fed5fc76bc 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -384,7 +384,7 @@ void test_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -397,7 +397,7 @@ void test_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };
@@ -489,7 +489,7 @@ void test_const_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -502,7 +502,7 @@ void test_const_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };

>From abe1273f0e6f3e156405c03783d4cd5b607c18e6 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:31 +0100
Subject: [PATCH 14/21] Update libcxx/docs/Status/Cxx26Issues.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d5f19b2aea902..f494c823c0433 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","24","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""

>From f706ab0080bf43abdf54b594f804d8d0d5139993 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:40 +0100
Subject: [PATCH 15/21] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index eb36e5a6b82f8..076e238525477 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -192,7 +192,7 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
 "`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""

>From 78f42f32db78b084825aae0b494e411cf80a2ec6 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:50 +0100
Subject: [PATCH 16/21] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 076e238525477..7b21465e6c14d 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","24","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."

>From 1e19bde2f0b31981effc06cf7846174b3469beb6 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:15:01 +0100
Subject: [PATCH 17/21] Update libcxx/docs/Status/Cxx26Issues.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index f494c823c0433..e383e3fe539f4 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","24","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""

>From 098c2fb44ecf18854b4010ce700458347e535f32 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:15:11 +0100
Subject: [PATCH 18/21] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7b21465e6c14d..d1c5b1498f1ca 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -194,7 +194,7 @@
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
 "`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","24","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""

>From 175b9e54eee1afcfd2fbd1715ca455bc0c8b0699 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:32:58 +0100
Subject: [PATCH 19/21] review

---
 libcxx/docs/ReleaseNotes/23.rst               |  3 -
 libcxx/docs/ReleaseNotes/24.rst               |  3 +
 .../internal_call_signature.sh.cpp            | 68 -------------------
 3 files changed, 3 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 5d295453dd79f..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,9 +55,6 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
-- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
-- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
-- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..8126489a2c85f 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,9 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
deleted file mode 100644
index a32a1083b5596..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// REQUIRES: has-filecheck
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
-
-struct Small {
-  char c[8];
-};
-
-struct Big {
-  char c[256];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// CHECK: define{{.*}} @_Z10test_small
-// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
-// CHECK: %3 = alloca %struct.Small, align 1
-// CHECK: %6 = alloca %struct.Small, align 1
-// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
-// CHECK: store i64 %1, ptr %7, align 1
-// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
-// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
-// CHECK: %15 = load i64, ptr %14, align 1
-// CHECK: call void %10(ptr %13, i64 %15)
-//
-// The above has two local copies of Small object. It loads %1 to local copy %3, and then
-// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
-void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
-
-// CHECK: define{{.*}} @_Z8test_big
-// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
-//
-// The above directly calls the function pointer with the parameter Big object pointer
-// points to the operator()'s parameter %1, thus, no extra copies/moves
-// So we internally pass the Big object by reference
-void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
-
-// CHECK: define{{.*}} @_Z22test_small_non_trivial
-// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
-//
-// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
-// points to the operator()'s parameter %1, thus, no extra copies/moves
-// So we internally pass the SmallButNonTrivial object by reference
-void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file

>From 180792d63e3af864a84ec1722844b17fbd79b7d0 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:14:44 +0100
Subject: [PATCH 20/21] CI

---
 libcxx/include/__functional/function_ref_impl.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a968fc64de70..ada14643ec496 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -158,8 +158,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <auto _Cw, class _Fn, class _Tp>
     requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
-                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
-      : __storage_(__obj_ptr_),
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+      : __storage_(__obj_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
@@ -169,7 +169,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     }
 
     if constexpr (is_member_pointer_v<_Fn>) {
-      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
     }
   }
 

>From 41ed0224e646afb46d9eb6043667fd88c6a9d872 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:54:26 +0100
Subject: [PATCH 21/21] CI

---
 libcxx/docs/conf.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/conf.py b/libcxx/docs/conf.py
index 9051bce7cf356..98aa52edd5d2e 100644
--- a/libcxx/docs/conf.py
+++ b/libcxx/docs/conf.py
@@ -32,9 +32,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = "23.0"
+version = "24.0"
 # The full version, including alpha/beta/rc tags.
-release = "23.0"
+release = "24.0"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.


From libcxx-commits at lists.llvm.org  Tue Jul 21 04:44:27 2026
From: libcxx-commits at lists.llvm.org (Pavel Novikov via libcxx-commits)
Date: Tue, 21 Jul 2026 04:44:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f5b9b.f1c1923d.2d2b59.7f04@mx.google.com>


toughengineer wrote:

> done

oops, fixed a booboo in incorrect merging

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Tue Jul 21 04:48:28 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Tue, 21 Jul 2026 04:48:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2502R2:
 `std::generator`: Synchronous Coroutine Generator for Ranges (PR #201854)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201854@github.com>
Message-ID: <6a5f5c8c.d6cc1ce0.216988.f320@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/201854

>From 22f5fee287889d72cb171751fdc0993197c69b6b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 22:45:16 +0800
Subject: [PATCH 1/9] implement generator

---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/Status/Cxx23Issues.csv            |   2 +-
 libcxx/docs/Status/Cxx23Papers.csv            |   4 +-
 libcxx/docs/Status/Cxx2cIssues.csv            |   8 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/generator                      | 603 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   5 +
 libcxx/include/version                        |   2 +
 libcxx/modules/std.compat.cppm.in             |   3 -
 libcxx/modules/std.cppm.in                    |   4 +-
 libcxx/modules/std/generator.inc              |   7 +-
 .../coro.generator/nodiscard.verify.cpp       |  29 +
 .../test/libcxx/transitive_includes/cxx03.csv |  21 +
 .../test/libcxx/transitive_includes/cxx11.csv |  21 +
 .../test/libcxx/transitive_includes/cxx14.csv |  21 +
 .../test/libcxx/transitive_includes/cxx17.csv |  21 +
 .../test/libcxx/transitive_includes/cxx20.csv |  16 +
 .../test/libcxx/transitive_includes/cxx23.csv |   9 +
 .../test/libcxx/transitive_includes/cxx26.csv |   9 +
 .../generator.version.compile.pass.cpp        |  65 ++
 .../version.version.compile.pass.cpp          |  30 +
 .../ranges/coro.generator/allocator.pass.cpp  | 123 ++++
 .../coro.generator/generator.compile.pass.cpp |  48 ++
 .../ranges/coro.generator/generator.pass.cpp  |  56 ++
 .../ranges/coro.generator/recursive.pass.cpp  |  97 +++
 .../generate_feature_test_macro_components.py |   5 +
 libcxx/utils/libcxx/header_information.py     |   1 -
 27 files changed, 1198 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/include/generator
 create mode 100644 libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/generator.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/recursive.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 1b748e37293df..46d83644ff10f 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -344,6 +344,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_forward_like``                                 ``202207L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_generator``                                    ``202207L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_invoke_r``                                     ``202106L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ios_noreplace``                                ``202207L``
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0a2f0d59484de..b61edbd0a2975 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -217,7 +217,7 @@
 "`LWG3759 <https://wg21.link/LWG3759>`__","``ranges::rotate_copy`` should use ``std::move``","2022-11 (Kona)","|Complete|","15","`#105027 <https://github.com/llvm/llvm-project/issues/105027>`__",""
 "`LWG3760 <https://wg21.link/LWG3760>`__","``cartesian_product_view::iterator``'s ``parent_`` is never valid","2022-11 (Kona)","","","`#105028 <https://github.com/llvm/llvm-project/issues/105028>`__",""
 "`LWG3761 <https://wg21.link/LWG3761>`__","``cartesian_product_view::iterator::operator-`` should pass by reference","2022-11 (Kona)","","","`#105029 <https://github.com/llvm/llvm-project/issues/105029>`__",""
-"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","","","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
+"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","23","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
 "`LWG3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept","2022-11 (Kona)","|Complete|","17","`#105031 <https://github.com/llvm/llvm-project/issues/105031>`__",""
 "`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","","","`#105032 <https://github.com/llvm/llvm-project/issues/105032>`__",""
 "`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","","`#105033 <https://github.com/llvm/llvm-project/issues/105033>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index eb580ea891f5b..862461f900dbd 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -80,7 +80,7 @@
 "`P2474R2 <https://wg21.link/P2474R2>`__","``views::repeat``","2022-07 (Virtual)","|Complete|","17","`#105222 <https://github.com/llvm/llvm-project/issues/105222>`__",""
 "`P2494R2 <https://wg21.link/P2494R2>`__","Relaxing range adaptors to allow for move only types","2022-07 (Virtual)","|Complete|","17","`#105224 <https://github.com/llvm/llvm-project/issues/105224>`__",""
 "`P2499R0 <https://wg21.link/P2499R0>`__","``string_view`` range constructor should be ``explicit``","2022-07 (Virtual)","|Complete|","16","`#105225 <https://github.com/llvm/llvm-project/issues/105225>`__",""
-"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","","","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
+"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","23","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
 "`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","2022-07 (Virtual)","|Complete|","15","`#105227 <https://github.com/llvm/llvm-project/issues/105227>`__",""
 "`P2517R1 <https://wg21.link/P2517R1>`__","Add a conditional ``noexcept`` specification to ``std::apply``","2022-07 (Virtual)","|Complete|","3.9","`#105229 <https://github.com/llvm/llvm-project/issues/105229>`__",""
 "`P2520R0 <https://wg21.link/P2520R0>`__","``move_iterator`` should be a random access iterator","2022-07 (Virtual)","|Complete|","17","`#105230 <https://github.com/llvm/llvm-project/issues/105230>`__","Implemented as a DR in C++20"
@@ -115,7 +115,7 @@
 "`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20","`#105259 <https://github.com/llvm/llvm-project/issues/105259>`__",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","`#105260 <https://github.com/llvm/llvm-project/issues/105260>`__","The paper is implemented as a DR to C++20"
 "`P2652R2 <https://wg21.link/P2652R2>`__","Disallow User Specialization of ``allocator_traits``","2023-02 (Issaquah)","|Complete|","19","`#105262 <https://github.com/llvm/llvm-project/issues/105262>`__",""
-"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","","","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
+"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","23","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
 "`P2614R2 <https://wg21.link/P2614R2>`__","Deprecate ``numeric_limits::has_denorm``","2023-02 (Issaquah)","|Complete|","18","`#105264 <https://github.com/llvm/llvm-project/issues/105264>`__",""
 "`P2588R3 <https://wg21.link/P2588R3>`__","``barrier``’s phase completion guarantees","2023-02 (Issaquah)","","","`#105265 <https://github.com/llvm/llvm-project/issues/105265>`__",""
 "`P2763R1 <https://wg21.link/P2763R1>`__","``layout_stride`` static extents default constructor fix","2023-02 (Issaquah)","","","`#105266 <https://github.com/llvm/llvm-project/issues/105266>`__",""
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 6a836c0491bb6..3ec8ae821ed5d 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -4,7 +4,7 @@
 "`LWG3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","2023-06 (Varna)","|Nothing To Do|","","`#105270 <https://github.com/llvm/llvm-project/issues/105270>`__",""
 "`LWG3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","2023-06 (Varna)","|Complete|","19","`#105271 <https://github.com/llvm/llvm-project/issues/105271>`__",""
 "`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","","","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
-"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","","","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
+"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","23","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
 "`LWG3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 <https://github.com/llvm/llvm-project/issues/105275>`__",""
 "`LWG3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 <https://github.com/llvm/llvm-project/issues/105276>`__",""
 "`LWG3905 <https://wg21.link/LWG3905>`__","Type of ``std::fexcept_t``","2023-06 (Varna)","|Complete|","3.4","`#105277 <https://github.com/llvm/llvm-project/issues/105277>`__",""
@@ -79,8 +79,8 @@
 "`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","","`#118332 <https://github.com/llvm/llvm-project/issues/118332>`__",""
 "`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","","`#118335 <https://github.com/llvm/llvm-project/issues/118335>`__",""
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
-"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
-"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
+"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","23","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
+"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","|Complete|","23","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
 "`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
@@ -93,7 +93,7 @@
 "`LWG4088 <https://wg21.link/LWG4088>`__","``println`` ignores the locale imbued in ``std::ostream``","2024-11 (Wrocław)","|Complete|","18","`#118348 <https://github.com/llvm/llvm-project/issues/118348>`__",""
 "`LWG4112 <https://wg21.link/LWG4112>`__","``has-arrow`` should required ``operator->()`` to be ``const``-qualified","2024-11 (Wrocław)","","","`#118349 <https://github.com/llvm/llvm-project/issues/118349>`__",""
 "`LWG4113 <https://wg21.link/LWG4113>`__","Disallow ``has_unique_object_representations<Incomplete[]>``","2024-11 (Wrocław)","|Complete|","","`#118350 <https://github.com/llvm/llvm-project/issues/118350>`__",""
-"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","","","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
+"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","23","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
 "`LWG4124 <https://wg21.link/LWG4124>`__","Cannot format ``zoned_time`` with resolution coarser than ``seconds``","2024-11 (Wrocław)","","","`#118352 <https://github.com/llvm/llvm-project/issues/118352>`__",""
 "`LWG4126 <https://wg21.link/LWG4126>`__","Some feature-test macros for fully freestanding features are not yet marked freestanding","2024-11 (Wrocław)","","","`#118353 <https://github.com/llvm/llvm-project/issues/118353>`__",""
 "`LWG4134 <https://wg21.link/LWG4134>`__","Issue with Philox algorithm specification","2024-11 (Wrocław)","","","`#118354 <https://github.com/llvm/llvm-project/issues/118354>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..cd77d51924571 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1043,6 +1043,7 @@ set(files
   fstream
   functional
   future
+  generator
   initializer_list
   iomanip
   ios
diff --git a/libcxx/include/generator b/libcxx/include/generator
new file mode 100644
index 0000000000000..d3f942b089002
--- /dev/null
+++ b/libcxx/include/generator
@@ -0,0 +1,603 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_GENERATOR
+#define _LIBCPP_GENERATOR
+
+/*
+
+namespace std {
+  // [coro.generator.class], class template generator
+  template<class Ref, class Val = void, class Allocator = void>
+    class generator;
+
+  namespace pmr {
+    template<class Ref, class Val = void>
+      using generator = std::generator<Ref, Val, polymorphic_allocator<>>;
+  }
+}
+
+*/
+
+#include <__algorithm/max.h>
+#include <__assert>
+#include <__concepts/common_reference_with.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/same_as.h>
+#include <__config>
+#include <__coroutine/coroutine_handle.h>
+#include <__coroutine/coroutine_traits.h>
+#include <__coroutine/noop_coroutine_handle.h>
+#include <__coroutine/trivial_awaitables.h>
+#include <__exception/exception_ptr.h>
+#include <__iterator/default_sentinel.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_arg_t.h>
+#include <__memory/allocator_traits.h>
+#include <__memory/construct_at.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/elements_of.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/common_reference.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/exchange.h>
+#include <__utility/move.h>
+#include <__utility/swap.h>
+#include <cstdint>
+#include <new>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 23
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Ref, class _Val>
+using __gen_val = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
+
+template <class _Ref, class _Val>
+using __gen_ref = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
+
+template <class _Ref, class _Val>
+using __gen_yielded =
+    conditional_t<is_reference_v<__gen_ref<_Ref, _Val>>, __gen_ref<_Ref, _Val>, const __gen_ref<_Ref, _Val>&>;
+
+template <class, class>
+struct __gen_iter_provider {
+  class __iterator;
+};
+
+template <class, class, class>
+class generator;
+
+template <class _Yielded>
+class __gen_promise_base {
+private:
+  template <class, class, class>
+  friend class generator;
+
+  template <class, class>
+  friend struct __gen_iter_provider;
+
+  // Each promise object stores either a `__root_data` when associated with a root generator, or a `__recursive_data`
+  // when associated with a generator that is yielded recursively.
+  struct __root_data {
+    // The client code has access only to the iterator of the root generator. Thus, the root generator must store the
+    // yielded values of recursively-yielded generators, which will then be returned when the client code dereferences
+    // the iterator.
+    add_pointer_t<_Yielded> __value_ptr;
+    // The client code has access only to the iterator of the root generator. Thus, the root generator needs to identify
+    // which generator is currently active. This active generator will then be resumed when the client code increments
+    // the iterator.
+    coroutine_handle<__gen_promise_base> __active;
+  };
+
+  struct __recursive_data {
+    exception_ptr __exception;
+    coroutine_handle<__gen_promise_base> __parent;
+    coroutine_handle<__gen_promise_base> __root;
+  };
+
+  union __union {
+    __root_data __root;
+    __recursive_data __recursive;
+
+    _LIBCPP_HIDE_FROM_ABI ~__union() noexcept {}
+  } __data_;
+
+  // The `__tag_` stores the active member of the `__data_` union:
+  // - `false` indicates that the active member is `__root`.
+  // - `true` indicates that the active member is `__recursive`.
+  // This field can be omitted because `__recursive_data.__root` can store the active member.
+  bool __tag_;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_root() noexcept { return !__tag_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __root_data& __get_root_data() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __data_.__root;
+  }
+  _LIBCPP_HIDE_FROM_ABI void __set_root_tag() noexcept { __tag_ = false; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_recursive() noexcept { return __tag_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __recursive_data& __get_recursive_data() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_recursive(), "the active member of `__data_` is not `__recursive`");
+    return __data_.__recursive;
+  }
+  _LIBCPP_HIDE_FROM_ABI void __set_recursive_tag() noexcept { __tag_ = true; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI coroutine_handle<__gen_promise_base>& __active() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __get_root_data().__active;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __value_ptr() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __get_root_data().__value_ptr;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __root_value_ptr() noexcept {
+    if (__is_root()) {
+      return __value_ptr();
+    }
+    return __get_recursive_data().__root.promise().__value_ptr();
+  }
+
+  struct __element_awaiter {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI void await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      __current.promise().__root_value_ptr() = std::addressof(__value);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+
+    remove_cvref_t<_Yielded> __value;
+  };
+
+  struct __final_awaiter {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI coroutine_handle<> await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      // Checks if the current generator is recursively-yielded
+      if (__current.promise().__is_recursive()) {
+        auto&& __recursive_data = __current.promise().__get_recursive_data();
+        auto __parent           = __recursive_data.__parent;
+        // Updates the active generator to its parent, allowing the client code to resume it later
+        __recursive_data.__root.promise().__active() = __parent;
+        // Transfers execution to its parent, which is the generator that yields it
+        return __parent;
+      }
+      return std::noop_coroutine();
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+  };
+
+  template <class _Ref, class _Val, class _Allocator>
+  struct __recursive_awaiter {
+    _LIBCPP_HIDE_FROM_ABI explicit __recursive_awaiter(generator<_Ref, _Val, _Allocator>&& __gen) noexcept
+        : __gen_{std::move(__gen)} {}
+
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return !__gen_.__coroutine_; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI coroutine_handle<__gen_promise_base>
+    await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      // Stores a `__recursive_data` in the promise object associated with `__gen`
+      auto __recursive = coroutine_handle<__gen_promise_base>::from_address(__gen_.__coroutine_.address());
+      __recursive.promise().__set_recursive_tag();
+      auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+
+      // Sets `__recursive_data.__parent` to the current generator
+      auto __parent             = coroutine_handle<__gen_promise_base>::from_address(__current.address());
+      __recursive_data.__parent = __parent;
+
+      // Sets `__recursive_data.__root` to the current generator if it's a root generator, or to the root generator of
+      // the current generator otherwise
+      if (__parent.promise().__is_recursive()) {
+        __recursive_data.__root = __parent.promise().__get_recursive_data().__root;
+      } else {
+        __recursive_data.__root = __parent;
+      }
+
+      // Updates the active generator to `__gen`, allowing the client code to resume it later
+      __recursive_data.__root.promise().__active() = __recursive;
+
+      // Transfers execution to `__gen`
+      return __recursive;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() {
+      auto __recursive        = coroutine_handle<__gen_promise_base>::from_address(__gen_.__coroutine_.address());
+      auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+      if (__recursive_data.__exception) {
+        std::rethrow_exception(std::move(__recursive_data.__exception));
+      }
+    }
+
+  private:
+    generator<_Ref, _Val, _Allocator> __gen_;
+  };
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __gen_promise_base() noexcept
+      : __data_{.__root{
+            .__value_ptr{nullptr},
+            .__active{coroutine_handle<__gen_promise_base>::from_promise(*this)},
+        }},
+        __tag_{false} {}
+
+  _LIBCPP_HIDE_FROM_ABI ~__gen_promise_base() noexcept {
+    if (__is_root()) {
+      __data_.__root.~__root_data();
+    } else {
+      __data_.__recursive.~__recursive_data();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI suspend_always initial_suspend() const noexcept { return {}; }
+
+  _LIBCPP_HIDE_FROM_ABI __final_awaiter final_suspend() noexcept { return {}; }
+
+  _LIBCPP_HIDE_FROM_ABI suspend_always yield_value(_Yielded __value) noexcept {
+    __root_value_ptr() = std::addressof(__value);
+    return {};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI auto yield_value(const remove_reference_t<_Yielded>& __value) noexcept(
+      is_nothrow_constructible_v<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>)
+    requires is_rvalue_reference_v<_Yielded> &&
+             constructible_from<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>
+  {
+    return __element_awaiter{.__value{__value}};
+  }
+
+  template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
+    requires same_as<__gen_yielded<_Ref2, _Val2>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto
+  yield_value(ranges::elements_of<generator<_Ref2, _Val2, _Allocator2>&&, _Unused> __elements) noexcept {
+    return __recursive_awaiter<_Ref2, _Val2, _Allocator2>{std::move(__elements.range)};
+  }
+
+  template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
+    requires same_as<__gen_yielded<_Ref2, _Val2>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto
+  yield_value(ranges::elements_of<generator<_Ref2, _Val2, _Allocator2>&, _Unused> __elements) noexcept {
+    return __recursive_awaiter<_Ref2, _Val2, _Allocator2>{std::move(__elements.range)};
+  }
+
+  template <ranges::input_range _Range, class _Allocator>
+    requires convertible_to<ranges::range_reference_t<_Range>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto yield_value(ranges::elements_of<_Range, _Allocator> __range) {
+    auto __lambda =
+        [](allocator_arg_t, _Allocator, ranges::iterator_t<_Range> __i, ranges::sentinel_t<_Range> __s) static
+        -> generator<_Yielded, void, _Allocator> {
+      for (; __i != __s; ++__i) {
+        co_yield static_cast<_Yielded>(*__i);
+      }
+    };
+    return yield_value(ranges::elements_of(
+        __lambda(allocator_arg, __range.allocator, ranges::begin(__range.range), ranges::end(__range.range))));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void await_transform() = delete;
+
+  _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
+
+  _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
+#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+    if (__is_root()) {
+      throw;
+    } else {
+      __get_recursive_data().__exception = std::current_exception();
+    }
+#  endif
+  }
+};
+
+struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) __gen_aligned_block {
+  char __data[__STDCPP_DEFAULT_NEW_ALIGNMENT__];
+
+  _LIBCPP_HIDE_FROM_ABI static size_t __count(const size_t __size) noexcept {
+    return (__size + sizeof(__gen_aligned_block) - 1) / sizeof(__gen_aligned_block);
+  }
+};
+
+template <class _Allocator>
+concept __gen_stateless_allocator =
+    default_initializable<_Allocator> && allocator_traits<_Allocator>::is_always_equal::value;
+
+template <class _Allocator>
+class __gen_promise_allocator {
+private:
+  using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+  using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+  using __rebind_pointer = allocator_traits<__rebind>::pointer;
+  static_assert(is_pointer_v<__rebind_pointer>);
+
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+    __rebind __rebind_al = static_cast<__rebind>(__al);
+
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      return __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, and the alignment of the
+      // stateful allocator
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+      void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+      return __ptr;
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size)
+    requires default_initializable<_Allocator>
+  {
+    return __alloc({}, __size);
+  }
+
+  template <class _Allocator2, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<_Allocator>(__al), __size);
+  }
+
+  template <class _This, class _Allocator2, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<_Allocator>(__al), __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      __rebind __rebind_al;
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      __rebind __rebind_al = std::move(*__rebind_al_ptr);
+      std::destroy_at(__rebind_al_ptr);
+
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+};
+
+template <>
+class __gen_promise_allocator<void> {
+private:
+  using __dealloc_fn = void (*)(void*, size_t) noexcept;
+
+  template <class _Allocator>
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+    using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+    using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+    using __rebind_pointer = allocator_traits<__rebind>::pointer;
+    static_assert(is_pointer_v<__rebind_pointer>);
+
+    __rebind __rebind_al = static_cast<__rebind>(__al);
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+      void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the deallocation function in the allocated block
+      __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr                    = __dealloc<__rebind>;
+      return __ptr;
+    } else {
+      // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, the alignment of the
+      // stateful allocator, and the deallocation function
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(__rebind) + alignof(__rebind));
+      void* const __ptr = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the deallocation function in the allocated block
+      __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr                    = __dealloc<__rebind>;
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + sizeof(__dealloc_fn) + __size + alignof(__rebind) - 1) &
+          ~(alignof(__rebind) - 1));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+      return __ptr;
+    }
+  }
+
+  template <class _Rebind>
+  _LIBCPP_HIDE_FROM_ABI static void __dealloc(void* const __ptr, size_t __size) noexcept {
+    using __rebind_size_type = allocator_traits<_Rebind>::size_type;
+
+    if constexpr (__gen_stateless_allocator<_Rebind>) {
+      _Rebind __rebind_al;
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      _Rebind* const __rebind_al_ptr = reinterpret_cast<_Rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + sizeof(__dealloc_fn) + alignof(_Rebind) - 1) &
+          ~(alignof(_Rebind) - 1));
+      _Rebind __rebind_al = std::move(*__rebind_al_ptr);
+      std::destroy_at(__rebind_al_ptr);
+
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(_Rebind) + alignof(_Rebind));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size) {
+    void* const __ptr      = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
+      ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+    };
+
+    __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    *__dealloc_ptr                    = __dealloc;
+    return __ptr;
+  }
+
+  template <class _Allocator, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+    return __alloc(__al, __size);
+  }
+
+  template <class _This, class _Allocator, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+    return __alloc(__al, __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
+    __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    const __dealloc_fn __dealloc      = *__dealloc_ptr;
+    __dealloc(__ptr, __size);
+  }
+};
+
+template <class _Ref, class _Val>
+class __gen_iter_provider<_Ref, _Val>::__iterator {
+private:
+  using __val = __gen_val<_Ref, _Val>;
+  using __ref = __gen_ref<_Ref, _Val>;
+
+public:
+  using value_type      = __val;
+  using difference_type = ptrdiff_t;
+
+  _LIBCPP_HIDE_FROM_ABI explicit __iterator(
+      coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine) noexcept
+      : __coroutine_{__coroutine} {}
+
+  _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&& __other) noexcept
+      : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator=(__iterator&& __other) noexcept {
+    __coroutine_ = std::exchange(__other.__coroutine_, {});
+    return *this;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __ref operator*() const noexcept(is_nothrow_copy_constructible_v<__ref>) {
+    return static_cast<__ref>(*__coroutine_.promise().__value_ptr());
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator++() {
+    __coroutine_.promise().__active().resume();
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend bool operator==(const __iterator& __iter, default_sentinel_t) noexcept {
+    return __iter.__coroutine_.done();
+  }
+
+private:
+  coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine_;
+};
+
+template <class _Ref, class _Val = void, class _Allocator = void>
+class generator : public ranges::view_interface<generator<_Ref, _Val, _Allocator>> {
+private:
+  using __val = __gen_val<_Ref, _Val>;
+  static_assert(same_as<remove_cvref_t<__val>, __val> && is_object_v<__val>);
+
+  using __ref = __gen_ref<_Ref, _Val>;
+  static_assert(is_reference_v<__ref> ||
+                (same_as<remove_cvref_t<__ref>, __ref> && is_object_v<__ref> && copy_constructible<__ref>));
+
+  using __rref = conditional_t<is_lvalue_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
+  static_assert(common_reference_with<__ref&&, __val&> && common_reference_with<__ref&&, __rref&&> &&
+                common_reference_with<__rref&&, const __val&>);
+
+  template <class>
+  friend class __gen_promise_base;
+
+public:
+  using yielded = __gen_yielded<_Ref, _Val>;
+
+  struct promise_type : __gen_promise_base<yielded>, __gen_promise_allocator<_Allocator> {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI generator get_return_object() noexcept {
+      return generator{coroutine_handle<promise_type>::from_promise(*this)};
+    }
+  };
+
+  _LIBCPP_HIDE_FROM_ABI generator(const generator&) = delete;
+  _LIBCPP_HIDE_FROM_ABI generator(generator&& __other) noexcept
+      : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+  _LIBCPP_HIDE_FROM_ABI ~generator() {
+    if (__coroutine_) {
+      __coroutine_.destroy();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI generator& operator=(generator __other) noexcept {
+    std::swap(__coroutine_, __other.__coroutine_);
+    return *this;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __gen_iter_provider<_Ref, _Val>::__iterator begin() {
+    auto __coroutine = coroutine_handle<__gen_promise_base<yielded>>::from_promise(__coroutine_.promise());
+    __coroutine.resume();
+    return typename __gen_iter_provider<_Ref, _Val>::__iterator{__coroutine};
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI default_sentinel_t end() const noexcept { return {}; }
+
+private:
+  _LIBCPP_HIDE_FROM_ABI explicit generator(coroutine_handle<promise_type> __coroutine) noexcept
+      : __coroutine_{__coroutine} {}
+
+  coroutine_handle<promise_type> __coroutine_;
+};
+
+namespace pmr {
+
+template <class _Ref, class _Val = void>
+using generator = std::generator<_Ref, _Val, polymorphic_allocator<>>;
+
+} // namespace pmr
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_GENERATOR
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..efb97a047c6e6 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1476,6 +1476,11 @@ module std [system] {
     export *
   }
 
+  module generator {
+    header "generator"
+    export *
+  }
+
   module iomanip {
     header "iomanip"
     export *
diff --git a/libcxx/include/version b/libcxx/include/version
index d4d29b4d31a72..8a73c95551793 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -133,6 +133,7 @@ __cpp_lib_fstream_native_handle                         202306L <fstream>
 __cpp_lib_function_ref                                  202306L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
+__cpp_lib_generator                                     202207L <generator>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
 __cpp_lib_generic_unordered_lookup                      201811L <unordered_map> <unordered_set>
 __cpp_lib_hardware_interference_size                    201703L <new>
@@ -507,6 +508,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_format_ranges                        202207L
 // # define __cpp_lib_formatters                           202302L
 # define __cpp_lib_forward_like                         202207L
+# define __cpp_lib_generator                            202207L
 # define __cpp_lib_invoke_r                             202106L
 # define __cpp_lib_ios_noreplace                        202207L
 # if __has_builtin(__builtin_is_implicit_lifetime)
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index dd7385bf33a42..dada8f56f0224 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -51,9 +51,6 @@ module;
 #  if __has_include(<debugging>)
 #    error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<debugging>)
-#  if __has_include(<generator>)
-#    error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<generator>)
 #  if __has_include(<hazard_pointer>)
 #    error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<hazard_pointer>)
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index 984b18321923c..0c484219c31ce 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -67,6 +67,7 @@ module;
 #include <fstream>
 #include <functional>
 #include <future>
+#include <generator>
 #include <initializer_list>
 #include <iomanip>
 #include <ios>
@@ -135,9 +136,6 @@ module;
 #  if __has_include(<debugging>)
 #    error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<debugging>)
-#  if __has_include(<generator>)
-#    error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<generator>)
 #  if __has_include(<hazard_pointer>)
 #    error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<hazard_pointer>)
diff --git a/libcxx/modules/std/generator.inc b/libcxx/modules/std/generator.inc
index 43fb0daf5c659..8681f4b26dc46 100644
--- a/libcxx/modules/std/generator.inc
+++ b/libcxx/modules/std/generator.inc
@@ -8,7 +8,12 @@
 //===----------------------------------------------------------------------===//
 
 export namespace std {
-#if 0
+#if _LIBCPP_STD_VER >= 23
+  // [coro.generator], class template generator
   using std::generator;
+
+  namespace pmr {
+    using std::pmr::generator;
+  }
 #endif
 } // namespace std
diff --git a/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..9fb717bf9c148
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <generator>
+
+std::generator<int> generate_ints() {
+  co_yield 1;
+  co_yield 2;
+  co_yield 3;
+}
+
+void test() {
+  auto gen = generate_ints();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  gen.begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  gen.end();
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c0031543e47bc..5754f5961dd09 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -945,6 +949,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2032,6 +2052,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c0031543e47bc..5754f5961dd09 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -945,6 +949,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2032,6 +2052,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c2eb5b44e8d7a..c31b86c5f8739 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,15 +125,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -968,6 +972,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2072,6 +2092,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 332cb62f35b5f..50f2d9cf2ac5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -971,6 +975,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2085,6 +2105,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 55c79acff5a8f..0d23c92ec9a1d 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -965,6 +965,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 073f698786117..2fde541964065 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -466,6 +466,15 @@ future string_view
 future tuple
 future typeinfo
 future version
+generator compare
+generator cstdint
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator tuple
+generator typeinfo
+generator version
 initializer_list version
 iomanip cctype
 iomanip cerrno
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index d531d60ae3e35..5fde2907b3327 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -443,6 +443,15 @@ future string_view
 future tuple
 future typeinfo
 future version
+generator compare
+generator cstdint
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator tuple
+generator typeinfo
+generator version
 initializer_list version
 iomanip cctype
 iomanip cerrno
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
new file mode 100644
index 0000000000000..0726f4e59a553
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <generator>
+
+// Test the feature test macros defined by <generator>
+
+// clang-format off
+
+#include <generator>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++23"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++23"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++26"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++26"
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
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 20c16700ef76a..32befec34ed09 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
@@ -412,6 +412,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should not be defined before c++14"
 #  endif
@@ -1351,6 +1355,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++14"
 #  endif
@@ -2413,6 +2421,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++17"
 #  endif
@@ -3742,6 +3754,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++20"
 #  endif
@@ -5266,6 +5282,13 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++23"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++23"
 #  endif
@@ -7144,6 +7167,13 @@
 #    endif
 #  endif
 
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++26"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp b/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
new file mode 100644
index 0000000000000..96c2211ed3138
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <cstddef>
+#include <memory>
+#include <memory_resource>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+template <class T>
+class stateless_allocator {
+public:
+  using value_type = T;
+
+  stateless_allocator() noexcept = default;
+
+  template <typename U>
+  constexpr stateless_allocator(const stateless_allocator<U>&) noexcept {}
+
+  [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
+
+  void deallocate(void* p, size_t) noexcept { ::operator delete(p); }
+
+  template <class U>
+  constexpr bool operator==(const stateless_allocator<U>&) const noexcept {
+    return true;
+  }
+};
+
+template <class T>
+class stateful_allocator {
+public:
+  using value_type = T;
+
+  stateful_allocator() noexcept = default;
+
+  template <typename U>
+  constexpr stateful_allocator(const stateful_allocator<U>&) noexcept {}
+
+  [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
+
+  void deallocate(void* p, size_t) noexcept { ::operator delete(p); }
+
+  template <class U>
+  constexpr bool operator==(const stateful_allocator<U>& other) const noexcept {
+    return state_ == other.state_;
+  }
+
+private:
+  int state_ = 0;
+};
+
+template <class Allocator>
+bool test_with_allocator() {
+  std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+  {
+    auto fib = []() -> std::generator<int, int, Allocator> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib() | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+
+  {
+    auto fib = [](std::allocator_arg_t, Allocator) -> std::generator<int, int, Allocator> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib(std::allocator_arg, {}) | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+
+  {
+    auto fib = [](std::allocator_arg_t, Allocator) -> std::generator<int> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib(std::allocator_arg, {}) | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+  return true;
+}
+
+bool test() {
+  test_with_allocator<std::allocator<std::byte>>();
+  test_with_allocator<stateless_allocator<std::byte>>();
+  test_with_allocator<stateful_allocator<std::byte>>();
+  test_with_allocator<std::pmr::polymorphic_allocator<std::byte>>();
+
+  test_with_allocator<std::allocator<float>>();
+  test_with_allocator<stateless_allocator<float>>();
+  test_with_allocator<stateful_allocator<float>>();
+  test_with_allocator<std::pmr::polymorphic_allocator<float>>();
+  return true;
+};
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
new file mode 100644
index 0000000000000..769a56021c32b
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+template <class G, class V, class R, class RR>
+constexpr bool conformance() {
+  static_assert(std::ranges::range<G>);
+  static_assert(std::ranges::view<G>);
+  static_assert(std::ranges::input_range<G>);
+  static_assert(!std::ranges::forward_range<G>);
+  static_assert(!std::ranges::borrowed_range<G>);
+
+  static_assert(std::same_as<std::ranges::range_value_t<G>, V>);
+  static_assert(std::same_as<std::ranges::range_reference_t<G>, R>);
+  static_assert(std::same_as<std::ranges::range_rvalue_reference_t<G>, RR>);
+
+  return true;
+}
+
+static_assert(conformance<std::generator<int>, int, int&&, int&&>());
+static_assert(conformance<std::generator<int, int>, int, int, int>());
+
+static_assert(conformance<std::generator<int&>, int, int&, int&&>());
+static_assert(conformance<std::generator<int&, int>, int, int&, int&&>());
+static_assert(conformance<std::generator<const int&>, int, const int&, const int&&>());
+static_assert(conformance<std::generator<const int&, int>, int, const int&, const int&&>());
+
+static_assert(conformance<std::generator<int&&>, int, int&&, int&&>());
+static_assert(conformance<std::generator<int&&, int>, int, int&&, int&&>());
+static_assert(conformance<std::generator<const int&&>, int, const int&&, const int&&>());
+static_assert(conformance<std::generator<const int&&, int>, int, const int&&, const int&&>());
diff --git a/libcxx/test/std/ranges/coro.generator/generator.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
new file mode 100644
index 0000000000000..ed1793382962a
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+std::generator<int> fib() {
+  int a = 0;
+  int b = 1;
+  while (true) {
+    co_yield std::exchange(a, std::exchange(b, a + b));
+  }
+}
+
+std::generator<const int&> range_fib() {
+  co_yield std::ranges::elements_of(std::vector<int>{0, 1});
+  co_yield std::ranges::elements_of(std::vector<int>{1, 2});
+  co_yield std::ranges::elements_of(std::vector<int>{3, 5});
+  co_yield std::ranges::elements_of(std::vector<int>{5, 8});
+}
+
+bool test() {
+  {
+    std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+    {
+      auto fib_vec = fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+    {
+      auto fib_vec = range_fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+  }
+  return true;
+}
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
new file mode 100644
index 0000000000000..36c5b8b65e42e
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <numeric>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+std::generator<int> fib() {
+  int a = 0;
+  int b = 1;
+  while (true) {
+    co_yield std::exchange(a, std::exchange(b, a + b));
+  }
+}
+
+std::generator<int> recursive_fib(int depth) {
+  if (depth == 0) {
+    co_yield std::ranges::elements_of(fib());
+  } else {
+    co_yield std::ranges::elements_of(recursive_fib(depth - 1));
+  }
+};
+
+struct tree_node {
+  tree_node* left;
+  tree_node* right;
+  int element;
+
+  ~tree_node() {
+    delete left;
+    delete right;
+  }
+};
+
+tree_node* build_tree(int depth) {
+  if (depth == 0) {
+    return nullptr;
+  }
+
+  tree_node* root = new tree_node();
+  root->element   = depth;
+  root->left      = build_tree(depth - 1);
+  root->right     = build_tree(depth - 1);
+  return root;
+}
+
+std::generator<int> traversal(tree_node* node) {
+  if (node == nullptr) {
+    co_return;
+  }
+  co_yield std::ranges::elements_of(traversal(node->left));
+  co_yield node->element;
+  co_yield std::ranges::elements_of(traversal(node->right));
+}
+
+bool test() {
+  {
+    std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+    {
+      auto fib_vec = recursive_fib(1) | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+    {
+      auto fib_vec = recursive_fib(42) | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+  }
+  {
+    tree_node* tree_root = build_tree(10);
+    auto node_vec        = traversal(tree_root) | std::ranges::to<std::vector<int>>();
+    assert(node_vec.size() == 1023);
+    assert(std::reduce(node_vec.cbegin(), node_vec.cend()) == 2036);
+    delete tree_root;
+  }
+  return true;
+}
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d8fb575b97b2f..a5db6f1f1650f 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -719,6 +719,11 @@ def add_version_header(tc):
             "headers": ["random"],
             "unimplemented": True,
         },
+        {
+            "name": "__cpp_lib_generator",
+            "values": {"c++23": 202207}, # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
+            "headers": ["generator"],
+        },
         {
             "name": "__cpp_lib_generic_associative_lookup",
             "values": {"c++14": 201304},
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index d06271a7908cc..cc33d32a69201 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -164,7 +164,6 @@ def __hash__(self) -> int:
 # modules will fail to build if a header is added but this list is not updated.
 headers_not_available = list(map(Header, [
     "debugging",
-    "generator",
     "hazard_pointer",
     "inplace_vector",
     "linalg",

>From f2ee26c784c91829799073fe12d1cbd0d281a755 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 22:52:35 +0800
Subject: [PATCH 2/9] release notes & python format

---
 libcxx/docs/ReleaseNotes/23.rst                        | 2 ++
 libcxx/utils/generate_feature_test_macro_components.py | 4 +++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 03dabc54dcf79..a53fad86004a6 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -51,6 +51,8 @@ Implemented Papers
 - P2542R8: ``views::concat`` (`Github <https://llvm.org/PR105419>`__)
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
+- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index a5db6f1f1650f..ea2e43b4c15a8 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -721,7 +721,9 @@ def add_version_header(tc):
         },
         {
             "name": "__cpp_lib_generator",
-            "values": {"c++23": 202207}, # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
+            "values": {
+                "c++23": 202207
+            },  # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
             "headers": ["generator"],
         },
         {

>From 19e5608ff21db2369e7172201d55bc372e48d67b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:27:41 +0800
Subject: [PATCH 3/9] test

---
 libcxx/include/generator | 109 +++++++++++++++++++++------------------
 1 file changed, 60 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index d3f942b089002..7792b65a6ae43 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -25,52 +25,56 @@ namespace std {
 
 */
 
-#include <__algorithm/max.h>
-#include <__assert>
-#include <__concepts/common_reference_with.h>
-#include <__concepts/constructible.h>
-#include <__concepts/convertible_to.h>
-#include <__concepts/same_as.h>
-#include <__config>
-#include <__coroutine/coroutine_handle.h>
-#include <__coroutine/coroutine_traits.h>
-#include <__coroutine/noop_coroutine_handle.h>
-#include <__coroutine/trivial_awaitables.h>
-#include <__exception/exception_ptr.h>
-#include <__iterator/default_sentinel.h>
-#include <__memory/addressof.h>
-#include <__memory/allocator_arg_t.h>
-#include <__memory/allocator_traits.h>
-#include <__memory/construct_at.h>
-#include <__memory_resource/polymorphic_allocator.h>
-#include <__ranges/access.h>
-#include <__ranges/concepts.h>
-#include <__ranges/elements_of.h>
-#include <__ranges/view_interface.h>
-#include <__type_traits/add_pointer.h>
-#include <__type_traits/common_reference.h>
-#include <__type_traits/conditional.h>
-#include <__type_traits/is_nothrow_constructible.h>
-#include <__type_traits/is_object.h>
-#include <__type_traits/is_pointer.h>
-#include <__type_traits/is_reference.h>
-#include <__type_traits/is_void.h>
-#include <__type_traits/remove_cvref.h>
-#include <__type_traits/remove_reference.h>
-#include <__utility/exchange.h>
-#include <__utility/move.h>
-#include <__utility/swap.h>
-#include <cstdint>
-#include <new>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+#  include <__cxx03/__config>
+#else
+#  include <__config>
+
+#  if _LIBCPP_STD_VER >= 23
+
+#    include <__algorithm/max.h>
+#    include <__assert>
+#    include <__concepts/common_reference_with.h>
+#    include <__concepts/constructible.h>
+#    include <__concepts/convertible_to.h>
+#    include <__concepts/same_as.h>
+#    include <__coroutine/coroutine_handle.h>
+#    include <__coroutine/coroutine_traits.h>
+#    include <__coroutine/noop_coroutine_handle.h>
+#    include <__coroutine/trivial_awaitables.h>
+#    include <__exception/exception_ptr.h>
+#    include <__iterator/default_sentinel.h>
+#    include <__memory/addressof.h>
+#    include <__memory/allocator_arg_t.h>
+#    include <__memory/allocator_traits.h>
+#    include <__memory/construct_at.h>
+#    include <__memory_resource/polymorphic_allocator.h>
+#    include <__ranges/access.h>
+#    include <__ranges/concepts.h>
+#    include <__ranges/elements_of.h>
+#    include <__ranges/view_interface.h>
+#    include <__type_traits/add_pointer.h>
+#    include <__type_traits/common_reference.h>
+#    include <__type_traits/conditional.h>
+#    include <__type_traits/is_nothrow_constructible.h>
+#    include <__type_traits/is_object.h>
+#    include <__type_traits/is_pointer.h>
+#    include <__type_traits/is_reference.h>
+#    include <__type_traits/is_void.h>
+#    include <__type_traits/remove_cvref.h>
+#    include <__type_traits/remove_reference.h>
+#    include <__utility/exchange.h>
+#    include <__utility/move.h>
+#    include <__utility/swap.h>
+#    include <cstdint>
+#    include <new>
+
+#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#      pragma GCC system_header
+#    endif
 
 _LIBCPP_PUSH_MACROS
-#include <__undef_macros>
-
-#if _LIBCPP_STD_VER >= 23
+#    include <__undef_macros>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -309,13 +313,13 @@ public:
   _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
 
   _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
-#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+#    ifndef _LIBCPP_HAS_NO_EXCEPTIONS
     if (__is_root()) {
       throw;
     } else {
       __get_recursive_data().__exception = std::current_exception();
     }
-#  endif
+#    endif
   }
 };
 
@@ -462,9 +466,14 @@ private:
 
 public:
   _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size) {
-    void* const __ptr      = ::operator new(__size + sizeof(__dealloc_fn));
-    __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
+    void* const __ptr = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc =
+        [](void* const __dealloc_ptr, [[maybe_unused]] const size_t __dealloc_size) static noexcept -> void {
+#    ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
+      ::operator delete(__dealloc_ptr);
+#    else
       ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+#    endif
     };
 
     __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
@@ -596,8 +605,10 @@ using generator = std::generator<_Ref, _Val, polymorphic_allocator<>>;
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_STD_VER >= 23
+#  endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_POP_MACROS
 
+#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
 #endif // _LIBCPP_GENERATOR

>From 57bf0686971419520f56a48a34647f302a151cec Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:48:45 +0800
Subject: [PATCH 4/9] test

---
 libcxx/include/generator | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index 7792b65a6ae43..2176ecbaa1e6e 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -30,6 +30,13 @@ namespace std {
 #else
 #  include <__config>
 
+#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#    pragma GCC system_header
+#  endif
+
+_LIBCPP_PUSH_MACROS
+#  include <__undef_macros>
+
 #  if _LIBCPP_STD_VER >= 23
 
 #    include <__algorithm/max.h>
@@ -69,13 +76,6 @@ namespace std {
 #    include <cstdint>
 #    include <new>
 
-#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#      pragma GCC system_header
-#    endif
-
-_LIBCPP_PUSH_MACROS
-#    include <__undef_macros>
-
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Ref, class _Val>
@@ -469,10 +469,10 @@ public:
     void* const __ptr = ::operator new(__size + sizeof(__dealloc_fn));
     __dealloc_fn __dealloc =
         [](void* const __dealloc_ptr, [[maybe_unused]] const size_t __dealloc_size) static noexcept -> void {
-#    ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
-      ::operator delete(__dealloc_ptr);
-#    else
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
       ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+#    else
+      ::operator delete(__dealloc_ptr);
 #    endif
     };
 

>From d4c2ae0fccee3faeb4ed931a82cb11edc4505e2a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:57:26 +0800
Subject: [PATCH 5/9] test

---
 libcxx/test/libcxx/transitive_includes/cxx03.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx11.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx14.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx17.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx20.csv | 16 ----------------
 5 files changed, 80 deletions(-)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 5754f5961dd09..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -949,22 +949,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 5754f5961dd09..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -949,22 +949,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c31b86c5f8739..cfcf7bc7c4c19 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -972,22 +972,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 50f2d9cf2ac5f..534776e6f0526 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -975,22 +975,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 0d23c92ec9a1d..55c79acff5a8f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -965,22 +965,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm

>From 0b062c588aa75fa002ffe2d020da342947f80caf Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 8 Jun 2026 23:21:56 +0800
Subject: [PATCH 6/9] revert some transitive include change

---
 libcxx/test/libcxx/transitive_includes/cxx03.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx11.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx14.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx17.csv | 5 -----
 4 files changed, 20 deletions(-)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index cfcf7bc7c4c19..c2eb5b44e8d7a 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,19 +125,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2076,7 +2072,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 534776e6f0526..332cb62f35b5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2089,7 +2085,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm

>From 90c7db75736b52393c458af97b92a6dc4617ca7f Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 20:18:04 +0800
Subject: [PATCH 7/9] release notes 23 -> 24

---
 libcxx/docs/ReleaseNotes/23.rst | 5 -----
 libcxx/docs/ReleaseNotes/24.rst | 2 ++
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e18f46f6e467d..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -54,12 +54,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
-<<<<<<< HEAD
-- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
-- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
-=======
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
->>>>>>> main
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..55b39c7530f6a 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,8 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
+- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
 
 Improvements and New Features
 -----------------------------

>From 8fc0b82aeb2c202c5710c5e21a519d3ddf9003c4 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 20:22:23 +0800
Subject: [PATCH 8/9] release notes 23 -> 24

---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 libcxx/docs/Status/Cxx23Papers.csv | 4 ++--
 libcxx/docs/Status/Cxx26Issues.csv | 8 ++++----
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index f73551117f84e..167214284e2fd 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -217,7 +217,7 @@
 "`LWG3759 <https://wg21.link/LWG3759>`__","``ranges::rotate_copy`` should use ``std::move``","2022-11 (Kona)","|Complete|","15","`#105027 <https://github.com/llvm/llvm-project/issues/105027>`__",""
 "`LWG3760 <https://wg21.link/LWG3760>`__","``cartesian_product_view::iterator``'s ``parent_`` is never valid","2022-11 (Kona)","","","`#105028 <https://github.com/llvm/llvm-project/issues/105028>`__",""
 "`LWG3761 <https://wg21.link/LWG3761>`__","``cartesian_product_view::iterator::operator-`` should pass by reference","2022-11 (Kona)","","","`#105029 <https://github.com/llvm/llvm-project/issues/105029>`__",""
-"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","23","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
+"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","24","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
 "`LWG3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept","2022-11 (Kona)","|Complete|","17","`#105031 <https://github.com/llvm/llvm-project/issues/105031>`__",""
 "`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","","","`#105032 <https://github.com/llvm/llvm-project/issues/105032>`__",""
 "`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","","`#105033 <https://github.com/llvm/llvm-project/issues/105033>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 86f1395ed12fb..b5494c4a20ea8 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -80,7 +80,7 @@
 "`P2474R2 <https://wg21.link/P2474R2>`__","``views::repeat``","2022-07 (Virtual)","|Complete|","17","`#105222 <https://github.com/llvm/llvm-project/issues/105222>`__",""
 "`P2494R2 <https://wg21.link/P2494R2>`__","Relaxing range adaptors to allow for move only types","2022-07 (Virtual)","|Complete|","17","`#105224 <https://github.com/llvm/llvm-project/issues/105224>`__",""
 "`P2499R0 <https://wg21.link/P2499R0>`__","``string_view`` range constructor should be ``explicit``","2022-07 (Virtual)","|Complete|","16","`#105225 <https://github.com/llvm/llvm-project/issues/105225>`__",""
-"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","23","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
+"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","24","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
 "`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","2022-07 (Virtual)","|Complete|","15","`#105227 <https://github.com/llvm/llvm-project/issues/105227>`__",""
 "`P2517R1 <https://wg21.link/P2517R1>`__","Add a conditional ``noexcept`` specification to ``std::apply``","2022-07 (Virtual)","|Complete|","3.9","`#105229 <https://github.com/llvm/llvm-project/issues/105229>`__",""
 "`P2520R0 <https://wg21.link/P2520R0>`__","``move_iterator`` should be a random access iterator","2022-07 (Virtual)","|Complete|","17","`#105230 <https://github.com/llvm/llvm-project/issues/105230>`__","Implemented as a DR in C++20"
@@ -115,7 +115,7 @@
 "`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20","`#105259 <https://github.com/llvm/llvm-project/issues/105259>`__",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","`#105260 <https://github.com/llvm/llvm-project/issues/105260>`__","The paper is implemented as a DR to C++20"
 "`P2652R2 <https://wg21.link/P2652R2>`__","Disallow User Specialization of ``allocator_traits``","2023-02 (Issaquah)","|Complete|","19","`#105262 <https://github.com/llvm/llvm-project/issues/105262>`__",""
-"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","23","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
+"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","24","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
 "`P2614R2 <https://wg21.link/P2614R2>`__","Deprecate ``numeric_limits::has_denorm``","2023-02 (Issaquah)","|Complete|","18","`#105264 <https://github.com/llvm/llvm-project/issues/105264>`__",""
 "`P2588R3 <https://wg21.link/P2588R3>`__","``barrier``’s phase completion guarantees","2023-02 (Issaquah)","","","`#105265 <https://github.com/llvm/llvm-project/issues/105265>`__",""
 "`P2763R1 <https://wg21.link/P2763R1>`__","``layout_stride`` static extents default constructor fix","2023-02 (Issaquah)","","","`#105266 <https://github.com/llvm/llvm-project/issues/105266>`__",""
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index e8c02fa8e1ce1..c217b32adc014 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -4,7 +4,7 @@
 "`LWG3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","2023-06 (Varna)","|Nothing To Do|","","`#105270 <https://github.com/llvm/llvm-project/issues/105270>`__",""
 "`LWG3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","2023-06 (Varna)","|Complete|","19","`#105271 <https://github.com/llvm/llvm-project/issues/105271>`__",""
 "`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","","","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
-"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","23","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
+"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","24","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
 "`LWG3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 <https://github.com/llvm/llvm-project/issues/105275>`__",""
 "`LWG3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 <https://github.com/llvm/llvm-project/issues/105276>`__",""
 "`LWG3905 <https://wg21.link/LWG3905>`__","Type of ``std::fexcept_t``","2023-06 (Varna)","|Complete|","3.4","`#105277 <https://github.com/llvm/llvm-project/issues/105277>`__",""
@@ -79,8 +79,8 @@
 "`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","","`#118332 <https://github.com/llvm/llvm-project/issues/118332>`__",""
 "`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","","`#118335 <https://github.com/llvm/llvm-project/issues/118335>`__",""
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
-"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","23","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
-"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
+"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","24","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
+"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","|Complete|","24","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
 "`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","|Complete|","23","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
@@ -93,7 +93,7 @@
 "`LWG4088 <https://wg21.link/LWG4088>`__","``println`` ignores the locale imbued in ``std::ostream``","2024-11 (Wrocław)","|Complete|","18","`#118348 <https://github.com/llvm/llvm-project/issues/118348>`__",""
 "`LWG4112 <https://wg21.link/LWG4112>`__","``has-arrow`` should required ``operator->()`` to be ``const``-qualified","2024-11 (Wrocław)","","","`#118349 <https://github.com/llvm/llvm-project/issues/118349>`__",""
 "`LWG4113 <https://wg21.link/LWG4113>`__","Disallow ``has_unique_object_representations<Incomplete[]>``","2024-11 (Wrocław)","|Complete|","","`#118350 <https://github.com/llvm/llvm-project/issues/118350>`__",""
-"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","23","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
+"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","24","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
 "`LWG4124 <https://wg21.link/LWG4124>`__","Cannot format ``zoned_time`` with resolution coarser than ``seconds``","2024-11 (Wrocław)","","","`#118352 <https://github.com/llvm/llvm-project/issues/118352>`__",""
 "`LWG4126 <https://wg21.link/LWG4126>`__","Some feature-test macros for fully freestanding features are not yet marked freestanding","2024-11 (Wrocław)","","","`#118353 <https://github.com/llvm/llvm-project/issues/118353>`__",""
 "`LWG4134 <https://wg21.link/LWG4134>`__","Issue with Philox algorithm specification","2024-11 (Wrocław)","","","`#118354 <https://github.com/llvm/llvm-project/issues/118354>`__",""

>From 3e76eaccfc277fffdfbe26383769987f5596c444 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 22:04:55 +0800
Subject: [PATCH 9/9] fix new warnings

---
 libcxx/include/generator | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index 2176ecbaa1e6e..1a797c3798ec3 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -79,13 +79,13 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Ref, class _Val>
-using __gen_val = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
+using __gen_val _LIBCPP_NODEBUG = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
 
 template <class _Ref, class _Val>
-using __gen_ref = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
+using __gen_ref _LIBCPP_NODEBUG = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
 
 template <class _Ref, class _Val>
-using __gen_yielded =
+using __gen_yielded _LIBCPP_NODEBUG =
     conditional_t<is_reference_v<__gen_ref<_Ref, _Val>>, __gen_ref<_Ref, _Val>, const __gen_ref<_Ref, _Val>&>;
 
 template <class, class>
@@ -313,7 +313,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
 
   _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
-#    ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+#    if _LIBCPP_HAS_EXCEPTIONS
     if (__is_root()) {
       throw;
     } else {
@@ -338,10 +338,10 @@ concept __gen_stateless_allocator =
 template <class _Allocator>
 class __gen_promise_allocator {
 private:
-  using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
-  using __rebind_size_type = allocator_traits<__rebind>::size_type;
+  using __rebind _LIBCPP_NODEBUG           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+  using __rebind_size_type _LIBCPP_NODEBUG = allocator_traits<__rebind>::size_type;
 
-  using __rebind_pointer = allocator_traits<__rebind>::pointer;
+  using __rebind_pointer _LIBCPP_NODEBUG = allocator_traits<__rebind>::pointer;
   static_assert(is_pointer_v<__rebind_pointer>);
 
   _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
@@ -403,7 +403,7 @@ public:
 template <>
 class __gen_promise_allocator<void> {
 private:
-  using __dealloc_fn = void (*)(void*, size_t) noexcept;
+  using __dealloc_fn _LIBCPP_NODEBUG = void (*)(void*, size_t) noexcept;
 
   template <class _Allocator>
   _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
@@ -502,8 +502,8 @@ public:
 template <class _Ref, class _Val>
 class __gen_iter_provider<_Ref, _Val>::__iterator {
 private:
-  using __val = __gen_val<_Ref, _Val>;
-  using __ref = __gen_ref<_Ref, _Val>;
+  using __val _LIBCPP_NODEBUG = __gen_val<_Ref, _Val>;
+  using __ref _LIBCPP_NODEBUG = __gen_ref<_Ref, _Val>;
 
 public:
   using value_type      = __val;
@@ -543,14 +543,14 @@ private:
 template <class _Ref, class _Val = void, class _Allocator = void>
 class generator : public ranges::view_interface<generator<_Ref, _Val, _Allocator>> {
 private:
-  using __val = __gen_val<_Ref, _Val>;
+  using __val _LIBCPP_NODEBUG = __gen_val<_Ref, _Val>;
   static_assert(same_as<remove_cvref_t<__val>, __val> && is_object_v<__val>);
 
-  using __ref = __gen_ref<_Ref, _Val>;
+  using __ref _LIBCPP_NODEBUG = __gen_ref<_Ref, _Val>;
   static_assert(is_reference_v<__ref> ||
                 (same_as<remove_cvref_t<__ref>, __ref> && is_object_v<__ref> && copy_constructible<__ref>));
 
-  using __rref = conditional_t<is_lvalue_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
+  using __rref _LIBCPP_NODEBUG = conditional_t<is_lvalue_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
   static_assert(common_reference_with<__ref&&, __val&> && common_reference_with<__ref&&, __rref&&> &&
                 common_reference_with<__rref&&, const __val&>);
 


From libcxx-commits at lists.llvm.org  Tue Jul 21 04:49:53 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 04:49:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a5f5ce1.0c05e68e.3c2f5d.d1af@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/186692

>From aad29067d1d0d63e32d72e071dfd946e75bffad5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 12 Apr 2026 09:35:44 +0100
Subject: [PATCH 01/22] [libcxx] Implement `std::constant_wrapper`

ci

ci

review comments

review comments

ci

review comments
---
 .../containers/views/views.span/span.cons/deduct.pass.cpp  | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index 35d9b36f824aa..f5b590cd9631d 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -88,6 +88,13 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
+
+  {
+    std::span s{std::begin(arr), std::cw<3>};
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+    assert(s.size() == std::size(arr));
+    assert(s.data() == std::data(arr));
+  }
 #endif
 }
 

>From 21eec174092bdf128c5b62d9a67db7cc6997267d Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 8 Jun 2024 17:32:42 -0700
Subject: [PATCH 02/22] [libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

rebase + put in experimental + rename nontype to constant_arg

tests

remove experimental

test for constant_arg_ref

test for constant_arg_ptr

remove fun_ptr member

invoke

format

constexpr function ptr

double unwrapping

P3948R1

tests for new papers

format

lwg issue

lwg issues

review comment

remove constexpr

optimise small types

ci

ci

ci

ci
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/23.rst               |   3 +
 libcxx/docs/Status/Cxx26Issues.csv            |   4 +-
 libcxx/docs/Status/Cxx26Papers.csv            |   6 +-
 libcxx/include/CMakeLists.txt                 |   3 +
 libcxx/include/__functional/function_ref.h    |  33 ++
 .../__functional/function_ref_common.h        | 126 ++++++
 .../include/__functional/function_ref_impl.h  | 184 +++++++++
 libcxx/include/functional                     |   9 +
 libcxx/include/module.modulemap.in            |   3 +
 libcxx/include/version                        |   4 +-
 libcxx/modules/std/functional.inc             |   4 +
 .../assert.constant_arg_ptr.pass.cpp          |  31 ++
 .../assert.function_ptr.pass.cpp              |  26 ++
 .../constant_arg.mandates.verify.cpp          |  60 +++
 .../internal_call_signature.pass.cpp          | 136 +++++++
 .../functional.version.compile.pass.cpp       |  16 +-
 .../version.version.compile.pass.cpp          |  16 +-
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     | 107 +++++
 .../func.wrap.ref.ctor/assign.delete.pass.cpp | 115 ++++++
 .../constant_wrapper.pass.cpp                 | 175 ++++++++
 .../constant_wrapper_ptr.pass.cpp             | 359 +++++++++++++++++
 .../constant_wrapper_ref.pass.cpp             | 373 ++++++++++++++++++
 .../func.wrap.ref.ctor/copy.pass.cpp          | 131 ++++++
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   | 158 ++++++++
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  | 149 +++++++
 .../func.wrap.ref.ctor/move.pass.cpp          | 129 ++++++
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 156 ++++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 316 +++++++++++++++
 .../func.wrap.ref.inv/invoke.pass.cpp         | 362 +++++++++++++++++
 .../trivially_copyable.compile.pass.cpp       |  33 ++
 .../generate_feature_test_macro_components.py |   3 +-
 32 files changed, 3200 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/include/__functional/function_ref.h
 create mode 100644 libcxx/include/__functional/function_ref_common.h
 create mode 100644 libcxx/include/__functional/function_ref_impl.h
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..cd360ae2b1317 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -480,7 +480,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_fstream_native_handle``                        ``202306L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_function_ref``                                 *unimplemented*
+    ``__cpp_lib_function_ref``                                 ``202603L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_generate_random``                              *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index f7a8e5b04f1ea..5d295453dd79f 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,9 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c203fa424556a..d5f19b2aea902 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","","","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","","","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 2522dd98e4b9c..eb36e5a6b82f8 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."
@@ -192,9 +192,9 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","","","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","","","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..a0d5cf70d267d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -438,6 +438,9 @@ set(files
   __functional/compose.h
   __functional/default_searcher.h
   __functional/function.h
+  __functional/function_ref.h
+  __functional/function_ref_common.h
+  __functional/function_ref_impl.h
   __functional/hash.h
   __functional/identity.h
   __functional/invoke.h
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
new file mode 100644
index 0000000000000..b7fd3a4a2c79f
--- /dev/null
+++ b/libcxx/include/__functional/function_ref.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+
+#include <__config>
+#include <__functional/function_ref_common.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26
+// NOLINTBEGIN(readability-duplicate-include)
+
+#  define _LIBCPP_FUNCTION_REF_CV
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+#  define _LIBCPP_FUNCTION_REF_CV const
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+// NOLINTEND(readability-duplicate-include)
+#endif // _LIBCPP_STD_VER >= 26
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
new file mode 100644
index 0000000000000..61979231b56e8
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_COMMON_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+
+#include <__config>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/constant_wrapper.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization : false_type {};
+
+// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+// pointers
+// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
+// supported
+union __storage_func_ref_t {
+  void* __obj_ptr_;
+  void const* __obj_const_ptr_;
+  void (*__fn_ptr_)();
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        __obj_const_ptr_ = __ptr;
+      } else {
+        __obj_ptr_ = __ptr;
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
+    }
+  }
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
+      } else {
+        return static_cast<_Tp*>(__storage.__obj_ptr_);
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
+    }
+  }
+};
+
+template <class _Fp, class _Tp>
+struct __function_ref_bind {};
+
+// F is of the form R(*)(G, A...) noexcept(E) for a type G.
+template <bool _Noexcept, class _Tp, class _Rp, class _Gp, class... _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept(_Noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_Noexcept);
+};
+
+template <class _Tp, class _Mp, class _Gp>
+  requires is_object_v<_Mp>
+struct __function_ref_bind<_Mp _Gp::*, _Tp> {
+  using type _LIBCPP_NODEBUG = invoke_result_t<_Mp _Gp::*, _Tp&>() noexcept;
+};
+
+template <class _Fp, class _Tp>
+using __function_ref_bind_t _LIBCPP_NODEBUG = __function_ref_bind<_Fp, _Tp>::type;
+
+template <class _Fp>
+  requires is_function_v<_Fp>
+function_ref(_Fp*) -> function_ref<_Fp>;
+
+template <auto _Cw, class _Fn>
+  requires is_function_v<remove_pointer_t<_Fn>>
+function_ref(constant_wrapper<_Cw, _Fn>) -> function_ref<remove_pointer_t<_Fn>>;
+
+template <auto _Cw, class _Fn, class _Tp>
+function_ref(constant_wrapper<_Cw, _Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<_Fn, _Tp&>>;
+
+template <class>
+constexpr bool __is_constant_wrapper = false;
+
+template <auto _Value>
+constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
+
+template <class _Arg>
+struct __function_ref_arg_fwd {
+  using type _LIBCPP_NODEBUG = _Arg&&;
+};
+
+template <class _Arg>
+  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
+struct __function_ref_arg_fwd<_Arg> {
+  using type _LIBCPP_NODEBUG = _Arg;
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
new file mode 100644
index 0000000000000..a4c98d3031602
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -0,0 +1,184 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
+// and generates multiple versions of std::function_ref
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#include <__functional/invoke.h>
+#include <__memory/addressof.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/constant_wrapper.h>
+#include <__utility/forward.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#  error This header should only be included from function_ref.h
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
+    _NoExcept1,
+    _Rp,
+    _ArgTypes...>
+    : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
+
+template <class _Rp, class... _ArgTypes, bool __is_noexcept>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
+private:
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using =
+      _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
+          value;
+
+  template <class _Fn>
+  static constexpr bool __is_convertible_from_specialization_v =
+      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+
+  template <class... _Tp>
+  friend class function_ref;
+
+  template <class _Arg>
+  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+
+  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+
+  using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
+
+  __storage_t __storage_;
+  __call_t __call_;
+
+public:
+  template <class _Fp>
+    requires is_function_v<_Fp> && __is_invocable_using<_Fp>
+  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+      : __storage_(__fn_ptr),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+  }
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {}
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && __is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(__obj.__storage_), __call_(__obj.__call_) {}
+
+  template <auto _Cw, class _Fn>
+    requires __is_invocable_using<const _Fn&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
+      : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+    if constexpr (sizeof...(_ArgTypes) > 0 && (__constexpr_param<remove_cvref_t<_ArgTypes>> && ...)) {
+      static_assert(
+          !requires {
+            typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
+          },
+          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
+          "and produces a result with a structural type");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
+    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Tp>
+    requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
+      : __storage_(__obj_ptr_),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+
+    if constexpr (is_member_pointer_v<_Fn>) {
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(const function_ref&) noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+  template <class _Tp>
+    requires(!__is_convertible_from_specialization_v<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_wrapper<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
+
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
+    return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
+  }
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 9d588231d548d..df79d616a57e2 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -487,6 +487,11 @@ template <class  R, class ... ArgTypes>
 template <class  R, class ... ArgTypes>
   void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
 
+// [func.wrap.ref], non-owning wrapper
+template<class... S> class function_ref;                              // freestanding, not defined, since C++26
+template<class R, class... ArgTypes>
+  class function_ref<R(ArgTypes...) cv noexcept(noex)>;               // freestanding, since C++26
+
 template <class T> struct hash;
 
 template <> struct hash<bool>;
@@ -571,6 +576,10 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 #    include <__type_traits/unwrap_ref.h>
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+#    include <__functional/function_ref.h>
+#  endif
+
 #  include <version>
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..cb29fac536046 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1438,6 +1438,9 @@ module std {
     }
     module default_searcher             { header "__functional/default_searcher.h" }
     module function                     { header "__functional/function.h" }
+    module function_ref                 { header "__functional/function_ref.h" }
+    module function_ref_common          { header "__functional/function_ref_common.h" }
+    module function_ref_impl            { textual header "__functional/function_ref_impl.h" }
     module hash                         { header "__functional/hash.h" }
     module identity                     { header "__functional/identity.h" }
     module invoke                       { header "__functional/invoke.h" }
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..c62a7cbc8ba9c 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -131,7 +131,7 @@ __cpp_lib_freestanding_optional                         202311L <optional>
 __cpp_lib_freestanding_string_view                      202311L <string_view>
 __cpp_lib_freestanding_variant                          202311L <variant>
 __cpp_lib_fstream_native_handle                         202306L <fstream>
-__cpp_lib_function_ref                                  202306L <functional>
+__cpp_lib_function_ref                                  202603L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
@@ -593,7 +593,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 #   define __cpp_lib_fstream_native_handle              202306L
 # endif
-// # define __cpp_lib_function_ref                         202306L
+# define __cpp_lib_function_ref                         202603L
 // # define __cpp_lib_generate_random                      202403L
 // # define __cpp_lib_hazard_pointer                       202306L
 // # define __cpp_lib_inplace_vector                       202406L
diff --git a/libcxx/modules/std/functional.inc b/libcxx/modules/std/functional.inc
index 9ef8f584611fc..be804b19f75f8 100644
--- a/libcxx/modules/std/functional.inc
+++ b/libcxx/modules/std/functional.inc
@@ -95,6 +95,10 @@ export namespace std {
 
   using std::function;
 
+#if _LIBCPP_STD_VER >= 26
+  using std::function_ref;
+#endif // _LIBCPP_STD_VER >= 26
+
   using std::swap;
 
   using std::operator==;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
new file mode 100644
index 0000000000000..9703de0a59c2c
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+// Preconditions: If is_member_pointer_v<F> is true, obj is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+struct A {
+  void f() {}
+};
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(std::function_ref<void()>(std::cw<&A::f>, static_cast<A*>(nullptr)),
+                             "the object pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
new file mode 100644
index 0000000000000..e69776d0e3c90
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<class F> function_ref(F* f) noexcept;
+// Preconditions: f is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::function_ref<void()>(static_cast<void (*)()>(nullptr)), "the function pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
new file mode 100644
index 0000000000000..519957eec6878
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto c, class F>
+//   constexpr function_ref(constant_wrapper<c, F>) noexcept;
+// template<auto c, class F, class U>
+//   constexpr function_ref(constant_wrapper<c, F>, U&& obj) noexcept;
+// template<auto c, class F, class T>
+//   constexpr function_ref(constant_wrapper<c, F>, cv T* obj) noexcept;
+
+// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
+
+// For the first overload,
+// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+
+#include <functional>
+#include <utility>
+
+struct A {
+  void f();
+};
+
+struct B {
+  constexpr int operator()(std::constant_wrapper<42>) const { return 42; }
+  constexpr int operator()(int) const { return 42; }
+};
+
+// clang-format off
+void test() {
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+
+  int i;
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  A a;
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+}
+// clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
new file mode 100644
index 0000000000000..05756cfc3b3b8
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Track number of moves
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+struct Small {};
+
+struct Big {
+  char c[128];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
+// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
+template <>
+class std::function_ref<int> {
+  using storage = std::__storage_func_ref_t;
+  void test() {
+    {
+      // by value small argument
+      // the internal function should pass by value
+      using call = std::function_ref<void(Small)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
+    }
+    {
+      // by value big argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Big)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
+    }
+    {
+      // by value non-trivial small argument
+      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
+      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
+    }
+    {
+      // by lvalue reference argument
+      // the internal function should pass by lvalue reference
+      using call = std::function_ref<void(Small&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
+    }
+    {
+      // by rvalue reference argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Small&&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
+    }
+  }
+};
+
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
+void test() {
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In this case, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index b7b7d0334830a..bcc453880c7e1 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -494,17 +494,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_invoke
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 f2d74cc99d864..c990ef86c3be1 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
@@ -7138,17 +7138,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_gcd_lcm
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
new file mode 100644
index 0000000000000..029cb19d54f6a
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+int fn(int, float) { return 42; }
+
+int fn_noexcept(int, float) noexcept { return 42; }
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem(int, float) { return 42; }
+  int fn_mem_ref(int, float) & { return 42; }
+  int fn_mem_const(int, float) const { return 42; }
+  int fn_mem_const_ref(int, float) const& { return 42; }
+  int fn_mem_noexcept(int, float) noexcept { return 42; }
+};
+
+void test() {
+  // template<class F>
+  //  function_ref(F*) -> function_ref<F>;
+  {
+    std::function_ref fn_ref = fn;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = fn_noexcept;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F>
+  //  function_ref(constant_wrapper<c, F>) -> function_ref<...>;
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn_noexcept>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F, class T>
+  //  function_ref(constant_wrapper<c, F>, T&&) -> function_ref<...>;
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
+  }
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn_noexcept>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float) noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&() noexcept>>);
+  }
+  {
+    const S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&() noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_noexcept>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
new file mode 100644
index 0000000000000..b4df4392a2ade
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class T> function_ref& operator=(T) = delete;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - T is not the same type as function_ref,
+// - is_pointer_v<T> is false, and
+// - T is not a specialization of constant_wrapper.
+
+// non const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[] {}>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
+
+// const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[] { return 42; }>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[](int) { return 42; }>>);
+
+// non-const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[] noexcept {} >>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+// const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[] noexcept {}>>);
+static_assert(
+    !std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+int forty_two() { return 42; }
+
+void test() {
+  {
+    std::function_ref<int()> f(std::cw<[] { return 41; }>);
+    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = &forty_two;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const noexcept
+    std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
new file mode 100644
index 0000000000000..4b425ee03e286
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
@@ -0,0 +1,175 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f> constexpr function_ref(constant_wrapper<f>) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&> is true.
+
+auto l1          = [] {};
+auto l1_noexcept = [] noexcept {};
+auto l2          = [](int) {};
+auto l2_noexcept = [](int) noexcept {};
+
+struct NonConstInvocable {
+  void operator()() noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+// const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    std::function_ref<void()> f = std::cw<[] {}>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
new file mode 100644
index 0000000000000..dd6e10e8a3289
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -0,0 +1,359 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&, cv T*> is true.
+
+auto l1                = [](int*) {};
+auto l1_const          = [](const int*) {};
+auto l1_noexcept       = [](int*) noexcept {};
+auto l1_const_noexcept = [](const int*) noexcept {};
+auto l2                = [](int*, double) {};
+auto l2_const          = [](const int*, double) {};
+auto l2_noexcept       = [](int*, double) noexcept {};
+auto l2_const_noexcept = [](const int*, double) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(int*) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, const int*>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2_const>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const noexcept>,
+                                      std::constant_wrapper<l2_const_noexcept>,
+                                      int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_const_noexcept>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<l1_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const noexcept>,
+                                              std::constant_wrapper<l2_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<&A::f_const_noexcept>,
+                                              A*>);
+
+double f1(const int* x, double y) noexcept { return *x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int*) {}>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int*) {}>, &i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int* j, double d) {
+          *j = 5;
+          return *j + d;
+        }>,
+        &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
new file mode 100644
index 0000000000000..485fa95ca2998
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -0,0 +1,373 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class U>
+//   constexpr function_ref(constant_wrapper<f>, U&& obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_rvalue_reference_v<U&&> is false, and
+// - is-invocable-using<const F&, cv T&> is true.
+
+auto l1          = [](int) {};
+auto l1_noexcept = [](int) noexcept {};
+auto l2          = [](int, double) {};
+auto l2_noexcept = [](int, double) noexcept {};
+auto l3          = [](int&) {};
+auto l3_noexcept = [](int&) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(long) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&&>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int) {}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int) {}>, i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int& j, double d) {
+          j = 5;
+          return j + d;
+        }>,
+        i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
new file mode 100644
index 0000000000000..61b70a559d786
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
new file mode 100644
index 0000000000000..1a8b26f86c8be
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
new file mode 100644
index 0000000000000..f142b81367ef1
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> function_ref(F* f) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_function_v<F> is true, and
+// - is-invocable-using<F> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (*)(int)>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A (*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, A)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+
+// const noexcept(true)
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, A) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              void (*)(int, double) noexcept>);
+
+int fn() { return 42; }
+
+int fn_maythrow(int i, A a) { return i - a.i; }
+int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+
+void foo(int) {}
+void bar(int) noexcept {}
+struct Int {
+  int i;
+  Int(int ii) noexcept : i(ii) {}
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+void test() {
+  {
+    // simple case
+    std::function_ref<int()> f(&fn);
+    assert(f() == 42);
+  }
+  {
+    // explicit(false)
+    std::function_ref<int()> f = &fn;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int(int, A)> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // noexcept
+    std::function_ref<int(int, A) noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // const
+    auto l = [](int x, int y, int z) { return x + y - z; };
+    std::function_ref<int(int, int, int) const> f(+l);
+    assert(f(2, 3, 4) == 1);
+  }
+  {
+    // const noexcept
+    std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+
+  {
+    std::function_ref<Int(int, int, int)> f(&needs_conversion);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(&needs_conversion);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(&needs_conversion);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
+    assert(f4(1, 2, 3).i == 6);
+
+    {
+      std::function_ref r1 = foo;
+      std::function_ref r2 = bar;
+      r1                   = r2; // ok
+    }
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
new file mode 100644
index 0000000000000..0f27bba821391
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
new file mode 100644
index 0000000000000..b40dfa9f2cf4b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
new file mode 100644
index 0000000000000..56d6dd3b7109b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -0,0 +1,316 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> constexpr function_ref(F&&) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - remove_cvref_t<F> is not the same type as function_ref,
+// - is_member_pointer_v<T> is false, and
+// - is-invocable-using<cv T&> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+constexpr auto l1 = [] {};
+using L1          = std::remove_cvref_t<decltype(l1)>;
+
+constexpr auto l2 = [] { return A{5}; };
+using L2          = std::remove_cvref_t<decltype(l2)>;
+
+constexpr auto l2_noexcept = [] noexcept { return A{5}; };
+using L2Noexcept           = std::remove_cvref_t<decltype(l2_noexcept)>;
+
+constexpr auto l3 = [](int x, double d) { return x + d; };
+using L3          = std::remove_cvref_t<decltype(l3)>;
+
+constexpr auto l3_noexcept = [](int x, double d) noexcept { return x + d; };
+using L3Noexcept           = std::remove_cvref_t<decltype(l3_noexcept)>;
+
+struct NonConstInvocable {
+  int i;
+
+  int operator()(int x, double y) {
+    ++i;
+    return x + y + i;
+  }
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L2&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2&>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2 const&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const>, std::function_ref<int(int, double) const>>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(int, double)>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double)>, const NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>,
+                                              std::function_ref<int(int, double) const>>);
+
+// const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                      std::function_ref<int(int, double) const noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              std::function_ref<int(int, double) const noexcept>>);
+
+struct F {
+  int i;
+  int operator()(auto&&...) { return 5 + i; }
+
+  int operator()(auto&&...) const { return 6 + i; }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int fn() { return 5; }
+constexpr auto fn_noexcept = []() noexcept { return 6; };
+
+const auto one = []() noexcept { return 1; };
+const auto two = []() noexcept { return 2; };
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(l1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit(false)
+    std::function_ref<void()> f = l1;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<A() noexcept> f(l2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      auto a = f();
+      assert(a.i == 5);
+    }
+  }
+  {
+    // const
+    std::function_ref<double(int, double) const> f(l3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(l3_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // no copies of original callable
+    auto local = [i = 5] mutable { return i++; };
+    std::function_ref<int()> f(local);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 5);
+      assert(local() == 6);
+      assert(f() == 7);
+    }
+  }
+  {
+    // const correctness
+    F f{5};
+
+    std::function_ref<int()> f1(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1() == 10);
+      assert(std::as_const(f1)() == 10);
+    }
+
+    std::function_ref<int() const> f2(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 11);
+      assert(std::as_const(f2)() == 11);
+    }
+  }
+  {
+    // with conversions
+    NeedsConversion c{};
+
+    std::function_ref<Int(int, int, int)> f(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  {
+    // noexcept conversion
+    std::function_ref<int() noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const conversion
+    std::function_ref<int() const> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() noexcept> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to const conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() const> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // P3961R1 Less double indirection in function_ref
+    // double unwrapping
+    std::function_ref<int() const noexcept> f1(std::cw<one>);
+    std::function_ref<int()> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 1 || f2() == 2);
+      LIBCPP_ASSERT(f2() == 1);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
new file mode 100644
index 0000000000000..1cd0c769ce4d3
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -0,0 +1,362 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// R operator()(ArgTypes... args) const noexcept(noex);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "MoveOnly.h"
+
+template <class T, class... Args>
+concept ConstInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) };
+};
+
+template <class T, class... Args>
+concept ConstNoexceptInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) } noexcept;
+};
+
+static_assert(ConstInvocable<std::function_ref<void()>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void()>>);
+
+static_assert(ConstInvocable<std::function_ref<void() noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() noexcept>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void() const>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() const noexcept>>);
+
+struct S {
+  int data = 42;
+  int operator()(int& x) const noexcept { return data + x; }
+  int operator()(const int& x) const noexcept { return data + x + 1; }
+  int operator()(int&& x) const noexcept { return data + x + 2; }
+  int operator()(const int&& x) const noexcept { return data + x + 3; }
+};
+
+struct S2 {
+  double operator()(int x, int y, int z) noexcept { return x + y + z; }
+
+  double operator()(int x, int y, int z) const noexcept { return x + y + z + 1; }
+};
+
+struct Big {
+  char c[128];
+};
+
+struct S3Ref {
+  char operator()(Big& b) const noexcept { return b.c[0]; }
+};
+
+struct S3Value {
+  char operator()(Big b) const noexcept { return b.c[0]; }
+};
+
+void test_default() {
+  {
+    std::function_ref<void()> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&)> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly)> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int)> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&)> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&)> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&)> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&)> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int)> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int)> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&)> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const() {
+  {
+    std::function_ref<void() const> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_noexcept() {
+  {
+    std::function_ref<void() noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const_noexcept() {
+  {
+    std::function_ref<void() const noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test() {
+  test_default();
+  test_const();
+  test_noexcept();
+  test_const_noexcept();
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
new file mode 100644
index 0000000000000..72e26f13868ea
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
+#include <concepts>
+#include <functional>
+#include <type_traits>
+
+struct A {
+  A() {}
+  A(const A&) {}
+  ~A() {}
+};
+
+static_assert(std::copyable<std::function_ref<A(const A&)>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&)>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) noexcept>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const noexcept>>);
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..e54773a0aa2db 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -708,10 +708,9 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_function_ref",
             "values": {
-                "c++26": 202306  # P0792R14 function_ref: a type-erased callable reference
+                "c++26": 202603  # P3948R1 constant_wrapper is the only tool needed for passing constant expressions via function arguments
             },
             "headers": ["functional"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_gcd_lcm",

>From cc0ed542effdeedda373bdbdccabc3c7fbea4cae Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 25 Apr 2026 14:20:04 +0100
Subject: [PATCH 03/22] review comments

---
 .../__functional/function_ref_common.h        | 22 ++++++-------------
 .../include/__functional/function_ref_impl.h  |  2 +-
 .../internal_call_signature.pass.cpp          |  2 +-
 .../constant_wrapper_ref.pass.cpp             |  8 +++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 12 ++++++++++
 5 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 61979231b56e8..72ded36d65107 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -16,6 +16,7 @@
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
 
@@ -37,21 +38,16 @@ struct __is_convertible_from_specialization : false_type {};
 // pointers
 // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
 // supported
-union __storage_func_ref_t {
+union __function_ref_storage {
   void* __obj_ptr_;
-  void const* __obj_const_ptr_;
   void (*__fn_ptr_)();
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage() noexcept : __obj_ptr_(nullptr) {}
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage(_Tp* __ptr) noexcept {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        __obj_const_ptr_ = __ptr;
-      } else {
-        __obj_ptr_ = __ptr;
-      }
+      __obj_ptr_ = const_cast<remove_cv_t<_Tp>*>(__ptr);
     } else {
       static_assert(is_function_v<_Tp>);
       __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
@@ -59,13 +55,9 @@ union __storage_func_ref_t {
   }
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__function_ref_storage __storage) {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
-      } else {
-        return static_cast<_Tp*>(__storage.__obj_ptr_);
-      }
+      return static_cast<_Tp*>(__storage.__obj_ptr_);
     } else {
       static_assert(is_function_v<_Tp>);
       return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index a4c98d3031602..36841d6745955 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -72,7 +72,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Arg>
   using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
 
-  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+  using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
   using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 05756cfc3b3b8..1e5df6413daf0 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -31,7 +31,7 @@ struct SmallButNonTrivial {
 // specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
 template <>
 class std::function_ref<int> {
-  using storage = std::__storage_func_ref_t;
+  using storage = std::__function_ref_storage;
   void test() {
     {
       // by value small argument
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index 485fa95ca2998..d3d5f6827193d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -362,6 +362,14 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // volatile objects
+    volatile int i = 5;
+    std::function_ref<int() > f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 56d6dd3b7109b..9ab012b3a04e8 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -161,6 +161,10 @@ constexpr auto fn_noexcept = []() noexcept { return 6; };
 const auto one = []() noexcept { return 1; };
 const auto two = []() noexcept { return 2; };
 
+struct VolatileCallable {
+  int operator()() volatile const { return 42; }
+};
+
 constexpr bool test() {
   {
     std::function_ref<void()> f(l1);
@@ -305,6 +309,14 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // volatile objects
+    const volatile VolatileCallable vc{};
+    std::function_ref<int()> f(vc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
 
   return true;
 }

>From c873a3274a048eef12a10c54fa9809055e200399 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 26 Apr 2026 09:41:10 +0100
Subject: [PATCH 04/22] remove stale comments

---
 libcxx/include/__functional/function_ref_common.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 72ded36d65107..c5adb8cbc7d52 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -34,10 +34,6 @@ class function_ref;
 template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization : false_type {};
 
-// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
-// pointers
-// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
-// supported
 union __function_ref_storage {
   void* __obj_ptr_;
   void (*__fn_ptr_)();

>From 8c049d9bfd3086368c5c254fa6ca8d8403808b12 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 14:06:05 +0100
Subject: [PATCH 05/22] fix bug

---
 .../__functional/function_ref_common.h        |  2 +-
 .../include/__functional/function_ref_impl.h  | 19 ++++++++++++-------
 .../internal_call_signature.pass.cpp          |  4 +++-
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  6 +++---
 .../func.wrap.ref.ctor/ref.pass.cpp           | 11 +++++++++++
 5 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index c5adb8cbc7d52..60bc6ef87af4f 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -31,7 +31,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <class _Fn1, class _Fn2>
 struct __is_convertible_from_specialization : false_type {};
 
 union __function_ref_storage {
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 36841d6745955..55755e9804c95 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -46,14 +46,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization<
-    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
-    _NoExcept1,
-    _Rp,
-    _ArgTypes...>
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) const noexcept(_NoExcept2)> >
     : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
 
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) noexcept(_NoExcept2)> >
+    : _And<is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)>,
+           is_convertible<_LIBCPP_FUNCTION_REF_CV int&, int&>> {};
+
 template <class _Rp, class... _ArgTypes, bool __is_noexcept>
 class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
 private:
@@ -62,9 +67,9 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
           value;
 
-  template <class _Fn>
+  template <class _Fn2>
   static constexpr bool __is_convertible_from_specialization_v =
-      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+      __is_convertible_from_specialization<function_ref, _Fn2>::value;
 
   template <class... _Tp>
   friend class function_ref;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 1e5df6413daf0..ebdf683791694 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -8,7 +8,9 @@
 
 // REQUIRES: std-at-least-c++26
 
-// Track number of moves
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
 
 #include <cassert>
 #include <functional>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index b4df4392a2ade..cb84b3a183920 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -35,9 +35,9 @@ static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrap
 static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
 
 // const noexcept(false)
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
@@ -61,7 +61,7 @@ static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::con
 // const noexcept(true)
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 9ab012b3a04e8..6b8b7c71f0d08 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -309,6 +309,17 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // P3961R1 Less double indirection in function_ref
+    // is-convertible-from-specialization<remove_cv_t<T>> is false
+    std::function_ref<int()> f1(std::cw<one>);
+    std::function_ref<int() const> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 2);
+    }
+  }
   {
     // volatile objects
     const volatile VolatileCallable vc{};

>From bb8ccae3578167103382ca0e19d92c4fc7a5cc20 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 17:28:36 +0100
Subject: [PATCH 06/22] ci

---
 libcxx/include/__functional/function_ref_impl.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 55755e9804c95..454b0ffb4afb3 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -15,6 +15,7 @@
 #include <__functional/invoke.h>
 #include <__memory/addressof.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/conjunction.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>

>From 86e636b10f81465bd9bf8b782cbaed3eb9e5b852 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 10:39:44 +0100
Subject: [PATCH 07/22] static operator

---
 .../include/__functional/function_ref_impl.h  | 23 +++++++++++----
 .../func.wrap.ref.ctor/ref.pass.cpp           | 28 +++++++++++++++++++
 2 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 454b0ffb4afb3..ab65382782fb2 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
@@ -99,12 +100,22 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
-      : __storage_(std::addressof(__obj)),
-        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
-        }) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
+    using _Dn = remove_cv_t<_Tp>;
+    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
+                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+                  }) {
+      __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    } else {
+      __storage_ = __storage_t(std::addressof(__obj)),
+      __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    }
+  }
 
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 6b8b7c71f0d08..2684ec3548f8e 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -328,6 +328,34 @@ constexpr bool test() {
       assert(f() == 42);
     }
   }
+  {
+    // static call operator
+    struct StaticCall {
+      static int operator()(int x, int y) { return x + y; }
+    };
+    StaticCall sc;
+    std::function_ref<int(int, int)> f(sc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2) == 3);
+    }
+  }
+  {
+    // overload set with static and non-static call operator
+    struct OverloadSet {
+      static int operator()(int x) { return x + 1; }
+      int operator()(long x) const { return x + 2; }
+    };
+    OverloadSet os;
+    std::function_ref<int(int)> f1(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1(1) == 2);
+    }
+
+    std::function_ref<int(long)> f2(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1L) == 3);
+    }
+  }
 
   return true;
 }

>From d7f110369b970835f08793ec961252610cc56336 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 19:26:38 +0100
Subject: [PATCH 08/22] work around clang 22 bug

---
 libcxx/include/__functional/function_ref_common.h             | 4 ++++
 libcxx/include/__functional/function_ref_impl.h               | 4 +---
 .../func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp   | 1 +
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 60bc6ef87af4f..bfe3d938056f0 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -19,6 +19,7 @@
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
+#include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -107,6 +108,9 @@ struct __function_ref_arg_fwd<_Arg> {
   using type _LIBCPP_NODEBUG = _Arg;
 };
 
+template <class _Fn, class... _Args>
+concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
+
 #endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index ab65382782fb2..7a173dee7fa4f 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -102,9 +102,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
     using _Dn = remove_cv_t<_Tp>;
-    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
-                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
-                  }) {
+    if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
       };
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 2684ec3548f8e..11b993e4a3ec3 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -351,6 +351,7 @@ constexpr bool test() {
       assert(f1(1) == 2);
     }
 
+    LIBCPP_STATIC_ASSERT(!std::__statically_callable<OverloadSet, long>);
     std::function_ref<int(long)> f2(os);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f2(1L) == 3);

>From 0d0dccaa28d32b237c1465c13cb9110ed5c15d40 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:12:34 +0100
Subject: [PATCH 09/22] review comments

---
 libcxx/include/__functional/function_ref_common.h | 15 +++++++--------
 libcxx/include/__functional/function_ref_impl.h   |  6 +++---
 .../constant_arg.mandates.verify.cpp              |  2 +-
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index bfe3d938056f0..0f40a07073cfc 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -15,7 +15,8 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
-#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
@@ -98,15 +99,13 @@ template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
 template <class _Arg>
-struct __function_ref_arg_fwd {
-  using type _LIBCPP_NODEBUG = _Arg&&;
-};
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
 
 template <class _Arg>
-  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
-struct __function_ref_arg_fwd<_Arg> {
-  using type _LIBCPP_NODEBUG = _Arg;
-};
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
 
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a173dee7fa4f..3424a99bdd174 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -77,7 +77,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   friend class function_ref;
 
   template <class _Arg>
-  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+  using __arg_t _LIBCPP_NODEBUG = _If<__register_passable<_Arg>, _Arg, _Arg&&>;
 
   using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
@@ -135,8 +135,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
           !requires {
             typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
           },
-          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
-          "and produces a result with a structural type");
+          "cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref "
+          "constructed from cw would be ambiguous");
     }
   }
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 519957eec6878..63cfbc0e97c9b 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -41,7 +41,7 @@ void test() {
   // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
   std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}

>From 6b89539b99bb7714036147c326551253b8eccbbf Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:13:48 +0100
Subject: [PATCH 10/22] use std::forward instead of static_cast

---
 libcxx/include/__functional/function_ref_impl.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 3424a99bdd174..d276c78fd09e6 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -92,7 +92,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -104,13 +104,13 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     using _Dn = remove_cv_t<_Tp>;
     if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return _Dn::operator()(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     } else {
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -125,7 +125,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     requires __is_invocable_using<const _Fn&>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
       : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -146,7 +146,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -160,7 +160,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(__obj_ptr_),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");

>From 28d5c298b92d62aba24962b5cf73cbd40315dc34 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:14:02 +0100
Subject: [PATCH 11/22] format

---
 .../func.wrap.ref.ctor/constant_arg.mandates.verify.cpp      | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 63cfbc0e97c9b..c84cca2793bac 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -17,8 +17,9 @@
 
 // Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
 
-// For the first overload,
-// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+// For the first overload, f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>...
+// satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)>
+// is not a valid type.
 
 #include <functional>
 #include <utility>

>From a1f306c3084b8b7b1a9afc928f777748512337ca Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 20 Jun 2026 16:28:00 +0100
Subject: [PATCH 12/22] review commnets

---
 libcxx/include/CMakeLists.txt                 |   1 +
 .../__functional/function_ref_common.h        |   9 -
 .../include/__functional/function_ref_impl.h  |   8 +-
 .../include/__type_traits/register_passable.h |  38 ++++
 libcxx/include/module.modulemap.in            |   1 +
 .../constant_arg.mandates.verify.cpp          |  26 +--
 .../internal_call_signature.pass.cpp          | 138 ---------------
 .../internal_call_signature.sh.cpp            |  68 ++++++++
 .../views.span/span.cons/deduct.pass.cpp      |   7 -
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     |   9 +
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  24 ++-
 .../constant_wrapper_ptr.pass.cpp             |  60 ++++++-
 .../constant_wrapper_ref.pass.cpp             |  59 ++++++-
 .../func.wrap.ref.ctor/copy.pass.cpp          |   5 +
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   |  71 ++++++--
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  |   6 -
 .../func.wrap.ref.ctor/move.pass.cpp          |  70 +-------
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 102 ++---------
 .../func.wrap.ref.inv/invoke.pass.cpp         | 162 ++++++++++++++++++
 19 files changed, 506 insertions(+), 358 deletions(-)
 create mode 100644 libcxx/include/__type_traits/register_passable.h
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index a0d5cf70d267d..fcea3b7601182 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -914,6 +914,7 @@ set(files
   __type_traits/rank.h
   __type_traits/reference_constructs_from_temporary.h
   __type_traits/reference_converts_from_temporary.h
+  __type_traits/register_passable.h
   __type_traits/remove_all_extents.h
   __type_traits/remove_const.h
   __type_traits/remove_const_ref.h
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 0f40a07073cfc..e6fa031432a91 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -98,15 +98,6 @@ constexpr bool __is_constant_wrapper = false;
 template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
-template <class _Arg>
-concept __itanium_trivial_for_calls =
-    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
-    is_trivially_move_constructible_v<_Arg>;
-
-template <class _Arg>
-concept __register_passable =
-    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
-
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
 
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index d276c78fd09e6..7a968fc64de70 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/register_passable.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
@@ -92,7 +93,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
+          _Fp* __fn_ptr1 = __storage_t::template __get<_Fp>(__storage);
+          return std::invoke_r<_Rp>(__fn_ptr1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -110,7 +112,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
+        return std::invoke_r<_Rp>(__obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -145,7 +147,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          auto& __obj1 = static_cast<_LIBCPP_FUNCTION_REF_CV _Tp&>(*__storage_t::template __get<_Tp>(__storage));
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
diff --git a/libcxx/include/__type_traits/register_passable.h b/libcxx/include/__type_traits/register_passable.h
new file mode 100644
index 0000000000000..21e23e62052c1
--- /dev/null
+++ b/libcxx/include/__type_traits/register_passable.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___TYPE_TRAITS_REGISTER_PASSABLE_H
+#define _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
+
+#include <__config>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Arg>
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
+
+template <class _Arg>
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
+
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index cb29fac536046..e3b9e5ac09cb3 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -351,6 +351,7 @@ module std_core {
       header "__type_traits/reference_converts_from_temporary.h"
       export std_core.type_traits.integral_constant
     }
+    module register_passable                          { header "__type_traits/register_passable.h" }
     module remove_all_extents                         { header "__type_traits/remove_all_extents.h" }
     module remove_const_ref                           { header "__type_traits/remove_const_ref.h" }
     module remove_const                               { header "__type_traits/remove_const.h" }
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index c84cca2793bac..08e8b8e9b8b52 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -35,27 +35,27 @@ struct B {
 
 // clang-format off
 void test() {
-  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
+  // expected-error-re@*:* {{static assertion failed {{.*}}cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
-  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   A a;
-  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 }
 // clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
deleted file mode 100644
index ebdf683791694..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-struct Small {};
-
-struct Big {
-  char c[128];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
-// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
-template <>
-class std::function_ref<int> {
-  using storage = std::__function_ref_storage;
-  void test() {
-    {
-      // by value small argument
-      // the internal function should pass by value
-      using call = std::function_ref<void(Small)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
-    }
-    {
-      // by value big argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Big)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
-    }
-    {
-      // by value non-trivial small argument
-      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
-      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
-    }
-    {
-      // by lvalue reference argument
-      // the internal function should pass by lvalue reference
-      using call = std::function_ref<void(Small&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
-    }
-    {
-      // by rvalue reference argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Small&&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
-    }
-  }
-};
-
-struct TrackCopyMove {
-  mutable int copy_count = 0;
-  int move_count         = 0;
-
-  TrackCopyMove() = default;
-  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
-    ++copy_count;
-    ++other.copy_count;
-  }
-
-  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
-    ++move_count;
-    ++other.move_count;
-  }
-  TrackCopyMove& operator=(const TrackCopyMove& other) {
-    ++copy_count;
-    ++other.copy_count;
-    return *this;
-  }
-  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
-    ++move_count;
-    ++other.move_count;
-    return *this;
-  }
-};
-
-void test() {
-  {
-    // Arg type is an lvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&)> f = lambda;
-    f(t);
-  }
-  {
-    // Arg type is an rvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&&)> f = lambda;
-    f(std::move(t));
-  }
-  {
-    // Arg type is a prvalue, we should move but not copy the object
-    // In this case, where the type is not trivially copyable, the object should be
-    // moved exactly once when passing into the lambda. The internal functions
-    // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) {
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 1);
-    };
-    std::function_ref<void(TrackCopyMove)> f = lambda;
-    f(TrackCopyMove{});
-  }
-}
-
-int main(int, char**) {
-  test();
-
-  return 0;
-}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
new file mode 100644
index 0000000000000..a32a1083b5596
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// REQUIRES: has-filecheck
+
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
+
+struct Small {
+  char c[8];
+};
+
+struct Big {
+  char c[256];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// CHECK: define{{.*}} @_Z10test_small
+// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
+// CHECK: %3 = alloca %struct.Small, align 1
+// CHECK: %6 = alloca %struct.Small, align 1
+// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
+// CHECK: store i64 %1, ptr %7, align 1
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
+// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
+// CHECK: %15 = load i64, ptr %14, align 1
+// CHECK: call void %10(ptr %13, i64 %15)
+//
+// The above has two local copies of Small object. It loads %1 to local copy %3, and then
+// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
+void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
+
+// CHECK: define{{.*}} @_Z8test_big
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
+//
+// The above directly calls the function pointer with the parameter Big object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the Big object by reference
+void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
+
+// CHECK: define{{.*}} @_Z22test_small_non_trivial
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
+//
+// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the SmallButNonTrivial object by reference
+void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index f5b590cd9631d..35d9b36f824aa 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -88,13 +88,6 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
-
-  {
-    std::span s{std::begin(arr), std::cw<3>};
-    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
-    assert(s.size() == std::size(arr));
-    assert(s.data() == std::data(arr));
-  }
 #endif
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
index 029cb19d54f6a..730a07202062d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -8,6 +8,15 @@
 
 // REQUIRES: std-at-least-c++26
 
+// template<class F>
+// function_ref(F*) -> function_ref<F>;
+//
+// template<auto c, class F0>
+// function_ref(constant_wrapper<c, F0>) -> function_ref<see below>;
+//
+// template<auto c, class F, class T>
+// function_ref(constant_wrapper<c, F>, T&&) -> function_ref<see below>;
+
 #include <cassert>
 #include <functional>
 #include <utility>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index cb84b3a183920..5866488be4a94 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -11,6 +11,7 @@
 // template<class T> function_ref& operator=(T) = delete;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -73,38 +74,49 @@ static_assert(
 
 int forty_two() { return 42; }
 
+// These runtime tests are testing that when the constraints are not met,
+// the assignment operator is not deleted and the implicit constructor
+// and the move assignment are called
 void test() {
   {
     std::function_ref<int()> f(std::cw<[] { return 41; }>);
-    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f =
+        std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = &forty_two;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = &forty_two;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // noexcept
     std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() noexcept>&> decltype(auto) result = f = std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const noexcept
     std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() const noexcept>&> decltype(auto) result = f =
+        std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
index dd6e10e8a3289..74f52063b6788 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -94,6 +94,7 @@ static_assert(
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
 static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, const int*>);
 
 static_assert(
     !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
@@ -180,6 +181,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int*) const noexcept { return 4; }
+  constexpr int operator()(const int*) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -231,6 +237,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -248,6 +262,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -263,39 +285,51 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -348,6 +382,20 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, &i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, &i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, &i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index d3d5f6827193d..979da32e60c3d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -199,6 +199,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int&) const noexcept { return 4; }
+  constexpr int operator()(const int&) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -248,6 +253,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -264,6 +277,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -278,37 +299,49 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -370,6 +403,20 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
index 61b70a559d786..7f837149cc6b5 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -22,6 +22,11 @@ static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
 double f1(int x, double y) noexcept { return x + y; }
 
 struct Int {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
index 1a8b26f86c8be..53c79b4097362 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -11,6 +11,7 @@
 // constexpr function_ref& operator=(const function_ref&) noexcept = default;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -22,6 +23,11 @@ static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
@@ -41,7 +47,8 @@ constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = f;
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -51,7 +58,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = f;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
@@ -62,7 +70,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -72,7 +81,8 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -82,32 +92,47 @@ constexpr bool test() {
     // with conversions
     std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
         std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
@@ -117,31 +142,47 @@ constexpr bool test() {
     // with conversions function pointer
     std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
     std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions function pointer
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index f142b81367ef1..aa0ccf8d67993 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -134,12 +134,6 @@ void test() {
 
     std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
     assert(f4(1, 2, 3).i == 6);
-
-    {
-      std::function_ref r1 = foo;
-      std::function_ref r2 = bar;
-      r1                   = r2; // ok
-    }
   }
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
index 0f27bba821391..025df83ad538d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -8,6 +8,8 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
 #include <functional>
 #include <utility>
@@ -20,18 +22,12 @@ static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-double f1(int x, double y) noexcept { return x + y; }
-
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+double f1(int x, double y) noexcept { return x + y; }
 
 constexpr bool test() {
   {
@@ -66,58 +62,6 @@ constexpr bool test() {
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
index b40dfa9f2cf4b..10620f5d48725 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -8,7 +8,10 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -20,26 +23,20 @@ static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
-
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
-int zero(Int, Int, Int) noexcept { return 0; }
-
 constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -49,8 +46,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = std::move(f);
-
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
       assert(f2() == 42);
@@ -60,7 +57,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -70,81 +68,13 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
-        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 1cd0c769ce4d3..0dced07c439d6 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -67,6 +67,32 @@ struct S3Value {
   char operator()(Big b) const noexcept { return b.c[0]; }
 };
 
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
 void test_default() {
   {
     std::function_ref<void()> f = [] {};
@@ -135,6 +161,40 @@ void test_default() {
     std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const() {
@@ -205,6 +265,40 @@ void test_const() {
     std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_noexcept() {
@@ -276,6 +370,40 @@ void test_noexcept() {
     std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const_noexcept() {
@@ -347,6 +475,40 @@ void test_const_noexcept() {
     std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test() {

>From 01c84f58558404d03091941a872d92cbea551fbd Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:38:35 +0100
Subject: [PATCH 13/22] format

---
 .../func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp       | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 0dced07c439d6..820fed5fc76bc 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -384,7 +384,7 @@ void test_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -397,7 +397,7 @@ void test_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };
@@ -489,7 +489,7 @@ void test_const_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -502,7 +502,7 @@ void test_const_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };

>From abe1273f0e6f3e156405c03783d4cd5b607c18e6 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:31 +0100
Subject: [PATCH 14/22] Update libcxx/docs/Status/Cxx26Issues.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d5f19b2aea902..f494c823c0433 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","24","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""

>From f706ab0080bf43abdf54b594f804d8d0d5139993 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:40 +0100
Subject: [PATCH 15/22] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index eb36e5a6b82f8..076e238525477 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -192,7 +192,7 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
 "`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""

>From 78f42f32db78b084825aae0b494e411cf80a2ec6 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:50 +0100
Subject: [PATCH 16/22] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 076e238525477..7b21465e6c14d 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","24","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."

>From 1e19bde2f0b31981effc06cf7846174b3469beb6 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:15:01 +0100
Subject: [PATCH 17/22] Update libcxx/docs/Status/Cxx26Issues.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index f494c823c0433..e383e3fe539f4 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","24","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""

>From 098c2fb44ecf18854b4010ce700458347e535f32 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:15:11 +0100
Subject: [PATCH 18/22] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7b21465e6c14d..d1c5b1498f1ca 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -194,7 +194,7 @@
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
 "`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","24","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""

>From 175b9e54eee1afcfd2fbd1715ca455bc0c8b0699 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:32:58 +0100
Subject: [PATCH 19/22] review

---
 libcxx/docs/ReleaseNotes/23.rst               |  3 -
 libcxx/docs/ReleaseNotes/24.rst               |  3 +
 .../internal_call_signature.sh.cpp            | 68 -------------------
 3 files changed, 3 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 5d295453dd79f..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,9 +55,6 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
-- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
-- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
-- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..8126489a2c85f 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,9 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
deleted file mode 100644
index a32a1083b5596..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// REQUIRES: has-filecheck
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
-
-struct Small {
-  char c[8];
-};
-
-struct Big {
-  char c[256];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// CHECK: define{{.*}} @_Z10test_small
-// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
-// CHECK: %3 = alloca %struct.Small, align 1
-// CHECK: %6 = alloca %struct.Small, align 1
-// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
-// CHECK: store i64 %1, ptr %7, align 1
-// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
-// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
-// CHECK: %15 = load i64, ptr %14, align 1
-// CHECK: call void %10(ptr %13, i64 %15)
-//
-// The above has two local copies of Small object. It loads %1 to local copy %3, and then
-// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
-void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
-
-// CHECK: define{{.*}} @_Z8test_big
-// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
-//
-// The above directly calls the function pointer with the parameter Big object pointer
-// points to the operator()'s parameter %1, thus, no extra copies/moves
-// So we internally pass the Big object by reference
-void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
-
-// CHECK: define{{.*}} @_Z22test_small_non_trivial
-// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
-//
-// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
-// points to the operator()'s parameter %1, thus, no extra copies/moves
-// So we internally pass the SmallButNonTrivial object by reference
-void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file

>From 180792d63e3af864a84ec1722844b17fbd79b7d0 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:14:44 +0100
Subject: [PATCH 20/22] CI

---
 libcxx/include/__functional/function_ref_impl.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a968fc64de70..ada14643ec496 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -158,8 +158,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <auto _Cw, class _Fn, class _Tp>
     requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
-                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
-      : __storage_(__obj_ptr_),
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+      : __storage_(__obj_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
@@ -169,7 +169,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     }
 
     if constexpr (is_member_pointer_v<_Fn>) {
-      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
     }
   }
 

>From 41ed0224e646afb46d9eb6043667fd88c6a9d872 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:54:26 +0100
Subject: [PATCH 21/22] CI

---
 libcxx/docs/conf.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/conf.py b/libcxx/docs/conf.py
index 9051bce7cf356..98aa52edd5d2e 100644
--- a/libcxx/docs/conf.py
+++ b/libcxx/docs/conf.py
@@ -32,9 +32,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = "23.0"
+version = "24.0"
 # The full version, including alpha/beta/rc tags.
-release = "23.0"
+release = "24.0"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.

>From 0e90b6396f9c0ebb3b05927402995a888372932c Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 21 Jul 2026 07:49:29 -0400
Subject: [PATCH 22/22] Revert changes to docs conf, will do separately

---
 libcxx/docs/conf.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/conf.py b/libcxx/docs/conf.py
index 98aa52edd5d2e..9051bce7cf356 100644
--- a/libcxx/docs/conf.py
+++ b/libcxx/docs/conf.py
@@ -32,9 +32,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = "24.0"
+version = "23.0"
 # The full version, including alpha/beta/rc tags.
-release = "24.0"
+release = "23.0"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.


From libcxx-commits at lists.llvm.org  Tue Jul 21 05:03:38 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 05:03:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a5f601a.35334a41.335216.373d@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/186692

From libcxx-commits at lists.llvm.org  Tue Jul 21 05:06:19 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 05:06:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f60bb.146286d8.35740f.ef0f@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/210078

>From 34419f2461f3cc4ac0c285cfbb76e64f016db652 Mon Sep 17 00:00:00 2001
From: Pavel Novikov <dev-ape at yandex.ru>
Date: Thu, 16 Jul 2026 17:43:25 +0300
Subject: [PATCH 1/2] [libc++] fix minor performance issue

---
 libcxx/include/string | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index a297a05813cee..957eb15e4fe1f 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1359,13 +1359,13 @@ public:
   template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
   append(_ForwardIterator __first, _ForwardIterator __last) {
-    size_type __sz  = size();
-    size_type __cap = capacity();
-    size_type __n   = static_cast<size_type>(std::distance(__first, __last));
-    if (__n == 0)
+    if (__first == __last)
       return *this;
 
     if (__string_is_trivial_iterator_v<_ForwardIterator> && !__addr_in_range(*__first)) {
+      size_type __sz  = size();
+      size_type __cap = capacity();
+      size_type __n   = static_cast<size_type>(std::distance(__first, __last));
       if (__cap - __sz < __n)
         __grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
       __annotate_increase(__n);

>From bd806f470a02647245b01bfa9209fc4937699bc0 Mon Sep 17 00:00:00 2001
From: Pavel Novikov <dev-ape at yandex.ru>
Date: Tue, 21 Jul 2026 14:42:18 +0300
Subject: [PATCH 2/2] [libc++] added benchmarks for `string::append()`

---
 .../benchmarks/containers/string.bench.cpp    | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index 776ee5f58b7e2..5ad80d1d3ec5a 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -17,6 +17,7 @@
 #include "benchmark/benchmark.h"
 #include "make_string.h"
 #include "test_macros.h"
+#include "test_iterators.h"
 
 constexpr size_t small_size = 5;
 constexpr size_t large_size = 30;
@@ -502,6 +503,45 @@ int main(int argc, char** argv) {
           [](auto bm) { bm->Arg(large_size); }); // for naming
   }
 
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> src(size, 'a');
+          auto getIterator = [&src](size_t i) {
+            // INT_MAX because we want overhead of ThrowingIterator without actually throwing
+            return ThrowingIterator<CharT>(src.data() + i, src.data() + src.size(), INT_MAX);
+          };
+          std::basic_string<CharT> string = src;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(string);
+            string.clear();
+            if constexpr (opaque)
+              benchmark::DoNotOptimize(src);
+            benchmark::DoNotOptimize(string.append(getIterator(0), getIterator(size)));
+          }
+        };
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
+  }
+
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
   benchmark::Shutdown();


From libcxx-commits at lists.llvm.org  Tue Jul 21 05:06:36 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 05:06:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f60cc.b7ecdeec.bc002.3d08@mx.google.com>


ldionne wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/containers/string.bench.cpp

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Tue Jul 21 05:07:30 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 05:07:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f6102.4c8387a1.302335.9408@mx.google.com>


================
@@ -482,6 +483,41 @@ int main(int argc, char** argv) {
           std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
   }
 
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> src(size, 'a');
+          auto getIterator = [&src](size_t i) {
+            // INT_MAX because we want overhead of ThrowingIterator without actually throwing
+            return ThrowingIterator<CharT>(src.data() + i, src.data() + src.size(), INT_MAX);
----------------
ldionne wrote:

@toughengineer Do you have thoughts about this?

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Tue Jul 21 05:08:17 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 05:08:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210360@github.com>
Message-ID: <6a5f6131.66c0679a.24976c.c3c0@mx.google.com>


================
@@ -1730,7 +1730,6 @@
 {'is_defined': True, 'name': '__ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__16locale2id9__next_idE', 'size': 0, 'type': 'OBJECT'}
----------------
ldionne wrote:

@philnik777 Please update the ABI changelog.

https://github.com/llvm/llvm-project/pull/210360

From libcxx-commits at lists.llvm.org  Tue Jul 21 05:15:23 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 05:15:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f62db.b4a331f0.d9742.69c6@mx.google.com>


================
@@ -482,6 +483,41 @@ int main(int argc, char** argv) {
           std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
   }
 
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> src(size, 'a');
+          auto getIterator = [&src](size_t i) {
+            // INT_MAX because we want overhead of ThrowingIterator without actually throwing
+            return ThrowingIterator<CharT>(src.data() + i, src.data() + src.size(), INT_MAX);
----------------
philnik777 wrote:

The compiler can simply remove the loop if it can prove that it has no side-effects, which is definitely the case for our `forward_iterator`, and even for more complicated ones like `map::iterator`: https://godbolt.org/z/4hdTnbsPd

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Tue Jul 21 05:35:35 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 05:35:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::next_permutation and std::prev_permutation (PR #210966)
Message-ID: <llvm.org/llvm/llvm-project/pull/210966@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/210966

None

>From 151e7dba173ca307ea91d13b387dcaf90368b30a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 1 Jul 2026 14:21:02 -0400
Subject: [PATCH] [libc++] Add benchmarks for std::next_permutation and
 std::prev_permutation

---
 .../algorithms/sorting/permutation.bench.cpp  | 76 +++++++++++++++++++
 1 file changed, 76 insertions(+)
 create mode 100644 libcxx/test/benchmarks/algorithms/sorting/permutation.bench.cpp

diff --git a/libcxx/test/benchmarks/algorithms/sorting/permutation.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/permutation.bench.cpp
new file mode 100644
index 0000000000000..8834c595361fa
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/permutation.bench.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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "common.h"
+
+int main(int argc, char** argv) {
+  auto std_next_permutation = [](auto first, auto last) { return std::next_permutation(first, last); };
+  auto std_prev_permutation = [](auto first, auto last) { return std::prev_permutation(first, last); };
+
+  // Benchmark next_permutation and prev_permutation. We walk the permutation sequence in place,
+  // calling the algorithm repeatedly without restoring between calls: these algorithms cycle through
+  // all permutations, so this measures the realistic amortized per-step cost.
+  //
+  // We intentionally don't benchmark the predicated overloads because that makes the benchmark
+  // run too slowly.
+  {
+    auto bm = []<class Container>(std::string name, auto permutation, auto generate_data) {
+      benchmark::RegisterBenchmark(
+          name,
+          [permutation, generate_data](auto& st) {
+            std::size_t const size      = st.range(0);
+            using ValueType             = typename Container::value_type;
+            std::vector<ValueType> data = generate_data(size);
+            Container c(data.begin(), data.end());
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = permutation(c.begin(), c.end());
+              benchmark::DoNotOptimize(result);
+              benchmark::DoNotOptimize(c);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+
+    auto gen_int         = [](std::size_t size) { return support::shuffled_data<int>(size); };
+    auto gen_nonintegral = [](std::size_t size) {
+      std::vector<int> data = support::shuffled_data<int>(size);
+      return std::vector<support::NonIntegral>(data.begin(), data.end());
+    };
+
+    // clang-format off
+    bm.operator()<std::vector<int>>("std::next_permutation(vector<int>)", std_next_permutation, gen_int);
+    bm.operator()<std::vector<support::NonIntegral>>("std::next_permutation(vector<NonIntegral>)", std_next_permutation, gen_nonintegral);
+    bm.operator()<std::deque<int>>("std::next_permutation(deque<int>)", std_next_permutation, gen_int);
+    bm.operator()<std::list<int>>("std::next_permutation(list<int>)", std_next_permutation, gen_int);
+
+    bm.operator()<std::vector<int>>("std::prev_permutation(vector<int>)", std_prev_permutation, gen_int);
+    bm.operator()<std::vector<support::NonIntegral>>("std::prev_permutation(vector<NonIntegral>)", std_prev_permutation, gen_nonintegral);
+    bm.operator()<std::deque<int>>("std::prev_permutation(deque<int>)", std_prev_permutation, gen_int);
+    bm.operator()<std::list<int>>("std::prev_permutation(list<int>)", std_prev_permutation, gen_int);
+    // clang-format on
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Tue Jul 21 05:36:11 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 05:36:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add benchmarks for
 std::next_permutation and std::prev_permutation (PR #210966)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210966@github.com>
Message-ID: <6a5f67bb.720fb68d.16b9a9.3e2a@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/210966.diff


1 Files Affected:

- (added) libcxx/test/benchmarks/algorithms/sorting/permutation.bench.cpp (+76) 


``````````diff
diff --git a/libcxx/test/benchmarks/algorithms/sorting/permutation.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/permutation.bench.cpp
new file mode 100644
index 0000000000000..8834c595361fa
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/permutation.bench.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
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "common.h"
+
+int main(int argc, char** argv) {
+  auto std_next_permutation = [](auto first, auto last) { return std::next_permutation(first, last); };
+  auto std_prev_permutation = [](auto first, auto last) { return std::prev_permutation(first, last); };
+
+  // Benchmark next_permutation and prev_permutation. We walk the permutation sequence in place,
+  // calling the algorithm repeatedly without restoring between calls: these algorithms cycle through
+  // all permutations, so this measures the realistic amortized per-step cost.
+  //
+  // We intentionally don't benchmark the predicated overloads because that makes the benchmark
+  // run too slowly.
+  {
+    auto bm = []<class Container>(std::string name, auto permutation, auto generate_data) {
+      benchmark::RegisterBenchmark(
+          name,
+          [permutation, generate_data](auto& st) {
+            std::size_t const size      = st.range(0);
+            using ValueType             = typename Container::value_type;
+            std::vector<ValueType> data = generate_data(size);
+            Container c(data.begin(), data.end());
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = permutation(c.begin(), c.end());
+              benchmark::DoNotOptimize(result);
+              benchmark::DoNotOptimize(c);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+
+    auto gen_int         = [](std::size_t size) { return support::shuffled_data<int>(size); };
+    auto gen_nonintegral = [](std::size_t size) {
+      std::vector<int> data = support::shuffled_data<int>(size);
+      return std::vector<support::NonIntegral>(data.begin(), data.end());
+    };
+
+    // clang-format off
+    bm.operator()<std::vector<int>>("std::next_permutation(vector<int>)", std_next_permutation, gen_int);
+    bm.operator()<std::vector<support::NonIntegral>>("std::next_permutation(vector<NonIntegral>)", std_next_permutation, gen_nonintegral);
+    bm.operator()<std::deque<int>>("std::next_permutation(deque<int>)", std_next_permutation, gen_int);
+    bm.operator()<std::list<int>>("std::next_permutation(list<int>)", std_next_permutation, gen_int);
+
+    bm.operator()<std::vector<int>>("std::prev_permutation(vector<int>)", std_prev_permutation, gen_int);
+    bm.operator()<std::vector<support::NonIntegral>>("std::prev_permutation(vector<NonIntegral>)", std_prev_permutation, gen_nonintegral);
+    bm.operator()<std::deque<int>>("std::prev_permutation(deque<int>)", std_prev_permutation, gen_int);
+    bm.operator()<std::list<int>>("std::prev_permutation(list<int>)", std_prev_permutation, gen_int);
+    // clang-format on
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/210966

From libcxx-commits at lists.llvm.org  Tue Jul 21 05:37:58 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 05:37:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [WG21] Draft PR for discussion of the
 P2687 trivial relocation approach (PR #116714)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/116714@github.com>
Message-ID: <6a5f6826.dfa3d646.12b476.d7a2@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/116714

From libcxx-commits at lists.llvm.org  Tue Jul 21 05:40:20 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 05:40:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [llvm] [runtimes] Unify
 exception and RTTI configuration for the runtimes (PR #165296)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/165296@github.com>
Message-ID: <6a5f68b4.101bc966.1fd28c.d55b@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/165296

From libcxx-commits at lists.llvm.org  Tue Jul 21 05:41:35 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 05:41:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Test existence of generated
 headers in the modulemap (PR #209312)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209312@github.com>
Message-ID: <6a5f68ff.ff8885e8.1f031.58b3@mx.google.com>


https://github.com/ldionne ready_for_review https://github.com/llvm/llvm-project/pull/209312

From libcxx-commits at lists.llvm.org  Tue Jul 21 05:41:52 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 05:41:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Test existence of generated
 headers in the modulemap (PR #209312)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209312@github.com>
Message-ID: <6a5f6910.2c9cebe8.374fd.04a1@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/209312

>From 717e8c1ec35426a2c411421427b38c3e279a42b0 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 9 Apr 2025 21:35:27 -0400
Subject: [PATCH] [libc++] Test existence of generated headers in the modulemap

The __config_site and __assertion_handler headers were previously
excluded from the modulemap test, but since we now include them in
the modulemap despite being generated, we can test it.
---
 libcxx/test/libcxx/headers_in_modulemap.sh.py | 8 +++++---
 libcxx/utils/libcxx/header_information.py     | 9 +--------
 2 files changed, 6 insertions(+), 11 deletions(-)

diff --git a/libcxx/test/libcxx/headers_in_modulemap.sh.py b/libcxx/test/libcxx/headers_in_modulemap.sh.py
index d4a18a20c8926..f7670365bc521 100644
--- a/libcxx/test/libcxx/headers_in_modulemap.sh.py
+++ b/libcxx/test/libcxx/headers_in_modulemap.sh.py
@@ -1,10 +1,12 @@
-# RUN: %{python} %s %{libcxx-dir}/utils
+# RUN: %{python} %s %{libcxx-dir}/utils %{include-dir}
 
+import pathlib
 import sys
 sys.path.append(sys.argv[1])
-from libcxx.header_information import all_headers, libcxx_include
+from libcxx.header_information import all_headers
 
-with open(libcxx_include / "module.modulemap.in") as f:
+include = pathlib.Path(sys.argv[2])
+with open(include / "module.modulemap") as f:
     modulemap = f.read()
 
 isHeaderMissing = False
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index a505d37b65b81..bc8c586c6980a 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -9,7 +9,6 @@
 import pathlib, functools
 
 libcxx_root = pathlib.Path(__file__).resolve().parent.parent.parent
-libcxx_include = libcxx_root / "include"
 assert libcxx_root.exists()
 
 def _is_header_file(file):
@@ -102,13 +101,6 @@ def is_cxx03_frozen_header(self) -> bool:
 
     def is_in_modulemap(self) -> bool:
         """Returns whether a header should be listed in the modulemap."""
-        # TODO: Should `__config_site` be in the modulemap?
-        if self._name == "__config_site":
-            return False
-
-        if self._name == "__assertion_handler":
-            return False
-
         # exclude libc++abi files
         if self._name in ["cxxabi.h", "__cxxabi_config.h"]:
             return False
@@ -151,6 +143,7 @@ def __hash__(self) -> int:
 
 
 # Commonly-used sets of headers
+libcxx_include = libcxx_root / "include"
 all_headers = [Header(p.relative_to(libcxx_include).as_posix()) for p in libcxx_include.rglob("[_a-z]*") if _is_header_file(p)]
 all_headers += [Header("__config_site"), Header("__assertion_handler")] # Headers generated during the build process
 public_headers = [h for h in all_headers if h.is_public()]


From libcxx-commits at lists.llvm.org  Tue Jul 21 05:43:13 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 05:43:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Test existence of generated
 headers in the modulemap (PR #209312)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209312@github.com>
Message-ID: <6a5f6961.65514cde.3816a9.4267@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

The __config_site and __assertion_handler headers were previously excluded from the modulemap test, but since we now include them in the modulemap despite being generated, we can test it.

---
Full diff: https://github.com/llvm/llvm-project/pull/209312.diff


2 Files Affected:

- (modified) libcxx/test/libcxx/headers_in_modulemap.sh.py (+5-3) 
- (modified) libcxx/utils/libcxx/header_information.py (+1-8) 


``````````diff
diff --git a/libcxx/test/libcxx/headers_in_modulemap.sh.py b/libcxx/test/libcxx/headers_in_modulemap.sh.py
index d4a18a20c8926..f7670365bc521 100644
--- a/libcxx/test/libcxx/headers_in_modulemap.sh.py
+++ b/libcxx/test/libcxx/headers_in_modulemap.sh.py
@@ -1,10 +1,12 @@
-# RUN: %{python} %s %{libcxx-dir}/utils
+# RUN: %{python} %s %{libcxx-dir}/utils %{include-dir}
 
+import pathlib
 import sys
 sys.path.append(sys.argv[1])
-from libcxx.header_information import all_headers, libcxx_include
+from libcxx.header_information import all_headers
 
-with open(libcxx_include / "module.modulemap.in") as f:
+include = pathlib.Path(sys.argv[2])
+with open(include / "module.modulemap") as f:
     modulemap = f.read()
 
 isHeaderMissing = False
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index dbb1d9e040a33..841e1149d464e 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -9,7 +9,6 @@
 import pathlib, functools
 
 libcxx_root = pathlib.Path(__file__).resolve().parent.parent.parent
-libcxx_include = libcxx_root / "include"
 assert libcxx_root.exists()
 
 def _is_header_file(file):
@@ -102,13 +101,6 @@ def is_cxx03_frozen_header(self) -> bool:
 
     def is_in_modulemap(self) -> bool:
         """Returns whether a header should be listed in the modulemap."""
-        # TODO: Should `__config_site` be in the modulemap?
-        if self._name == "__config_site":
-            return False
-
-        if self._name == "__assertion_handler":
-            return False
-
         # exclude libc++abi files
         if self._name in ["cxxabi.h", "__cxxabi_config.h"]:
             return False
@@ -151,6 +143,7 @@ def __hash__(self) -> int:
 
 
 # Commonly-used sets of headers
+libcxx_include = libcxx_root / "include"
 all_headers = [Header(p.relative_to(libcxx_include).as_posix()) for p in libcxx_include.rglob("[_a-z]*") if _is_header_file(p)]
 all_headers += [Header("__config_site"), Header("__assertion_handler")] # Headers generated during the build process
 public_headers = [h for h in all_headers if h.is_public()]

``````````

</details>


https://github.com/llvm/llvm-project/pull/209312

From libcxx-commits at lists.llvm.org  Tue Jul 21 05:49:21 2026
From: libcxx-commits at lists.llvm.org (Pavel Novikov via libcxx-commits)
Date: Tue, 21 Jul 2026 05:49:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f6ad1.101bc966.1fd28c.8601@mx.google.com>


================
@@ -482,6 +483,41 @@ int main(int argc, char** argv) {
           std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}));
   }
 
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> src(size, 'a');
+          auto getIterator = [&src](size_t i) {
+            // INT_MAX because we want overhead of ThrowingIterator without actually throwing
+            return ThrowingIterator<CharT>(src.data() + i, src.data() + src.size(), INT_MAX);
----------------
toughengineer wrote:

tl;dr: I don't know what's special about `ThrowingIterator`, my guess is that it brings enough overhead that we can measure a difference.

-----
I conducted an experiment.

I replaced iterators with `string`'s random access iterators, and predictably there is no difference (because a different part of code is actually at play):
```c++
benchmark::DoNotOptimize(string.append(getIterator(0), getIterator(size)));
```
Let it be for reference.

before (without changes):
```
2026-07-21T15:24:32+03:00
Running ./bench
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.12, 0.04, 0.02
***WARNING*** ASLR is enabled, the results may have unreproducible noise in them.
-------------------------------------------------------------------------------------------------------
Benchmark                                                             Time             CPU   Iterations
-------------------------------------------------------------------------------------------------------
std::string::append(ForwardIt, ForwardIt) (opaque)/5               5.73 ns         5.73 ns    113543649
std::u8string::append(ForwardIt, ForwardIt) (opaque)/5             5.82 ns         5.82 ns    114649223
std::wstring::append(ForwardIt, ForwardIt) (opaque)/5              3.01 ns         3.01 ns    228943794
std::string::append(ForwardIt, ForwardIt) (opaque)/30              3.02 ns         3.02 ns    230250131
std::u8string::append(ForwardIt, ForwardIt) (opaque)/30            3.04 ns         3.04 ns    230234151
std::wstring::append(ForwardIt, ForwardIt) (opaque)/30             3.64 ns         3.64 ns    192984729
std::string::append(ForwardIt, ForwardIt) (transparent)/5          6.13 ns         6.13 ns    112138334
std::u8string::append(ForwardIt, ForwardIt) (transparent)/5        6.22 ns         6.22 ns    108915852
std::wstring::append(ForwardIt, ForwardIt) (transparent)/5         3.04 ns         3.04 ns    232011948
std::string::append(ForwardIt, ForwardIt) (transparent)/30         3.02 ns         3.02 ns    231529793
std::u8string::append(ForwardIt, ForwardIt) (transparent)/30       3.04 ns         3.04 ns    232243723
std::wstring::append(ForwardIt, ForwardIt) (transparent)/30        3.64 ns         3.64 ns    193481498
```
after (with changes):
```
2026-07-21T15:24:55+03:00
Running ./bench
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.30, 0.09, 0.04
***WARNING*** ASLR is enabled, the results may have unreproducible noise in them.
-------------------------------------------------------------------------------------------------------
Benchmark                                                             Time             CPU   Iterations
-------------------------------------------------------------------------------------------------------
std::string::append(ForwardIt, ForwardIt) (opaque)/5               5.78 ns         5.78 ns    123796740
std::u8string::append(ForwardIt, ForwardIt) (opaque)/5             5.72 ns         5.72 ns    118276009
std::wstring::append(ForwardIt, ForwardIt) (opaque)/5              3.04 ns         3.04 ns    228957048
std::string::append(ForwardIt, ForwardIt) (opaque)/30              3.03 ns         3.03 ns    228371878
std::u8string::append(ForwardIt, ForwardIt) (opaque)/30            3.03 ns         3.03 ns    229643272
std::wstring::append(ForwardIt, ForwardIt) (opaque)/30             3.66 ns         3.66 ns    192805491
std::string::append(ForwardIt, ForwardIt) (transparent)/5          5.98 ns         5.98 ns    113876132
std::u8string::append(ForwardIt, ForwardIt) (transparent)/5        6.14 ns         6.14 ns    110579277
std::wstring::append(ForwardIt, ForwardIt) (transparent)/5         3.03 ns         3.03 ns    233406623
std::string::append(ForwardIt, ForwardIt) (transparent)/30         3.03 ns         3.03 ns    232780093
std::u8string::append(ForwardIt, ForwardIt) (transparent)/30       3.05 ns         3.05 ns    230290656
std::wstring::append(ForwardIt, ForwardIt) (transparent)/30        3.64 ns         3.64 ns    192181138
```
-----
Then I commented `throw`s in the `ThrowingIterator`'s implementation, the benchmark code looks like this:
```c++
auto getIterator = [&src](size_t i) {
  // INT_MAX because we want overhead of ThrowingIterator without actually throwing
  return ThrowingIterator<CharT>(src.data() + i, src.data() + src.size(), INT_MAX);
};
benchmark::DoNotOptimize(string.append(getIterator(0), getIterator(size)));
```
It gave no noticeable difference. It is noticeably slower than random access iterators though.

before (without changes):
```
2026-07-21T15:25:56+03:00
Running ./bench
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.20, 0.11, 0.05
***WARNING*** ASLR is enabled, the results may have unreproducible noise in them.
-------------------------------------------------------------------------------------------------------
Benchmark                                                             Time             CPU   Iterations
-------------------------------------------------------------------------------------------------------
std::string::append(ForwardIt, ForwardIt) (opaque)/5               8.91 ns         8.91 ns     71140091
std::u8string::append(ForwardIt, ForwardIt) (opaque)/5             8.86 ns         8.86 ns     77177168
std::wstring::append(ForwardIt, ForwardIt) (opaque)/5              14.2 ns         14.2 ns     48254297
std::string::append(ForwardIt, ForwardIt) (opaque)/30              38.9 ns         38.9 ns     18012853
std::u8string::append(ForwardIt, ForwardIt) (opaque)/30            39.4 ns         39.4 ns     17823351
std::wstring::append(ForwardIt, ForwardIt) (opaque)/30             39.1 ns         39.1 ns     18006097
std::string::append(ForwardIt, ForwardIt) (transparent)/5          8.92 ns         8.92 ns     76522971
std::u8string::append(ForwardIt, ForwardIt) (transparent)/5        9.00 ns         9.00 ns     74710896
std::wstring::append(ForwardIt, ForwardIt) (transparent)/5         14.7 ns         14.7 ns     47397890
std::string::append(ForwardIt, ForwardIt) (transparent)/30         39.1 ns         39.1 ns     17958756
std::u8string::append(ForwardIt, ForwardIt) (transparent)/30       39.4 ns         39.4 ns     17684654
std::wstring::append(ForwardIt, ForwardIt) (transparent)/30        38.9 ns         38.9 ns     18021868
```
after (with changes):
```
2026-07-21T15:26:19+03:00
Running ./bench
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.35, 0.15, 0.06
***WARNING*** ASLR is enabled, the results may have unreproducible noise in them.
-------------------------------------------------------------------------------------------------------
Benchmark                                                             Time             CPU   Iterations
-------------------------------------------------------------------------------------------------------
std::string::append(ForwardIt, ForwardIt) (opaque)/5               8.88 ns         8.88 ns     76256794
std::u8string::append(ForwardIt, ForwardIt) (opaque)/5             8.84 ns         8.84 ns     78165828
std::wstring::append(ForwardIt, ForwardIt) (opaque)/5              14.9 ns         14.9 ns     46379577
std::string::append(ForwardIt, ForwardIt) (opaque)/30              39.9 ns         39.9 ns     17357680
std::u8string::append(ForwardIt, ForwardIt) (opaque)/30            39.0 ns         39.0 ns     18012334
std::wstring::append(ForwardIt, ForwardIt) (opaque)/30             40.0 ns         40.0 ns     17735141
std::string::append(ForwardIt, ForwardIt) (transparent)/5          9.14 ns         9.14 ns     74071017
std::u8string::append(ForwardIt, ForwardIt) (transparent)/5        9.14 ns         9.14 ns     75885036
std::wstring::append(ForwardIt, ForwardIt) (transparent)/5         16.2 ns         16.2 ns     41903270
std::string::append(ForwardIt, ForwardIt) (transparent)/30         40.2 ns         40.2 ns     16823389
std::u8string::append(ForwardIt, ForwardIt) (transparent)/30       40.7 ns         40.7 ns     16924884
std::wstring::append(ForwardIt, ForwardIt) (transparent)/30        40.4 ns         40.4 ns     16972318
```
I guess even with the bookkeeping the overhead is small enough to not result in significant performance hit.

-----
Then I used `ThrowingIterator` as-is (with `throw`s uncommented), the benchmark code looks the same as in previous case:
```c++
benchmark::DoNotOptimize(string.append(getIterator(0), getIterator(size)));
```
Here it's even slower than the previos "forward iterator" case, and I can see noticeable difference between the versions of `append()` implementation.

My understanding is that one way or another `throw` adds enough overhead that it starts to be noticeable (compare 9-ish ns without `throw` and 20-ish ns with it).

before (without changes):
```
2026-07-21T15:27:28+03:00
Running ./bench
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.20, 0.16, 0.07
***WARNING*** ASLR is enabled, the results may have unreproducible noise in them.
-------------------------------------------------------------------------------------------------------
Benchmark                                                             Time             CPU   Iterations
-------------------------------------------------------------------------------------------------------
std::string::append(ForwardIt, ForwardIt) (opaque)/5               26.3 ns         26.3 ns     26660035
std::u8string::append(ForwardIt, ForwardIt) (opaque)/5             26.7 ns         26.7 ns     26714356
std::wstring::append(ForwardIt, ForwardIt) (opaque)/5              31.6 ns         31.6 ns     21982957
std::string::append(ForwardIt, ForwardIt) (opaque)/30               118 ns          118 ns      5840702
std::u8string::append(ForwardIt, ForwardIt) (opaque)/30             108 ns          108 ns      6089127
std::wstring::append(ForwardIt, ForwardIt) (opaque)/30              111 ns          111 ns      6104910
std::string::append(ForwardIt, ForwardIt) (transparent)/5          26.8 ns         26.8 ns     26490989
std::u8string::append(ForwardIt, ForwardIt) (transparent)/5        27.8 ns         27.8 ns     25610574
std::wstring::append(ForwardIt, ForwardIt) (transparent)/5         31.8 ns         31.8 ns     21787801
std::string::append(ForwardIt, ForwardIt) (transparent)/30          116 ns          116 ns      5976465
std::u8string::append(ForwardIt, ForwardIt) (transparent)/30        110 ns          110 ns      6170587
std::wstring::append(ForwardIt, ForwardIt) (transparent)/30         109 ns          109 ns      6310451
```
after (with changes):
```
2026-07-21T15:27:51+03:00
Running ./bench
Run on (20 X 3494.4 MHz CPU s)
CPU Caches:
  L1 Data 48 KiB (x10)
  L1 Instruction 32 KiB (x10)
  L2 Unified 2048 KiB (x10)
  L3 Unified 24576 KiB (x1)
Load Average: 0.27, 0.18, 0.08
***WARNING*** ASLR is enabled, the results may have unreproducible noise in them.
-------------------------------------------------------------------------------------------------------
Benchmark                                                             Time             CPU   Iterations
-------------------------------------------------------------------------------------------------------
std::string::append(ForwardIt, ForwardIt) (opaque)/5               19.1 ns         19.1 ns     36931421
std::u8string::append(ForwardIt, ForwardIt) (opaque)/5             19.2 ns         19.2 ns     35455048
std::wstring::append(ForwardIt, ForwardIt) (opaque)/5              26.9 ns         26.9 ns     26051882
std::string::append(ForwardIt, ForwardIt) (opaque)/30              80.0 ns         80.0 ns      8576529
std::u8string::append(ForwardIt, ForwardIt) (opaque)/30            76.9 ns         76.9 ns      8798004
std::wstring::append(ForwardIt, ForwardIt) (opaque)/30             76.1 ns         76.1 ns      8991720
std::string::append(ForwardIt, ForwardIt) (transparent)/5          18.9 ns         18.9 ns     37472571
std::u8string::append(ForwardIt, ForwardIt) (transparent)/5        18.9 ns         18.9 ns     36373991
std::wstring::append(ForwardIt, ForwardIt) (transparent)/5         25.5 ns         25.5 ns     26504911
std::string::append(ForwardIt, ForwardIt) (transparent)/30         79.8 ns         79.8 ns      8700310
std::u8string::append(ForwardIt, ForwardIt) (transparent)/30       77.8 ns         77.8 ns      9002752
std::wstring::append(ForwardIt, ForwardIt) (transparent)/30        77.3 ns         77.3 ns      8921482
```
I guess one could add overhead to iterator increment/decrement little by little and see when the difference starts to be noticeable.

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Tue Jul 21 06:02:59 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 06:02:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix test-at-commit after
	df4bb1f3106d (PR #210983)
Message-ID: <llvm.org/llvm/llvm-project/pull/210983@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/210983

None

>From 5cee546507ed118203aab0782c0bf935341a514e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 21 Jul 2026 09:01:59 -0400
Subject: [PATCH] [libc++] Fix test-at-commit after df4bb1f3106d

---
 libcxx/utils/test-at-commit | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/utils/test-at-commit b/libcxx/utils/test-at-commit
index d1b33c243b4d5..4249d9c1ce86e 100755
--- a/libcxx/utils/test-at-commit
+++ b/libcxx/utils/test-at-commit
@@ -15,8 +15,6 @@ LIT_CONFIG_FILE = """
 # of libc++ installed at the given path.
 #
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{{flags}}',
     '-pthread' + (' -isysroot {{}}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else '')
 ))


From libcxx-commits at lists.llvm.org  Tue Jul 21 06:03:24 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 06:03:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix test-at-commit after
	df4bb1f3106d (PR #210983)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210983@github.com>
Message-ID: <6a5f6e1c.9e2d85f8.157f1.a358@mx.google.com>


ldionne wrote:

Verified locally.

https://github.com/llvm/llvm-project/pull/210983

From libcxx-commits at lists.llvm.org  Tue Jul 21 06:03:25 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 06:03:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 2cd5deb - [libc++] Fix test-at-commit
	after df4bb1f3106d (#210983)
Message-ID: <6a5f6e1d.dcf3bad1.3ca4c9.f6ab@mx.google.com>


Author: Louis Dionne
Date: 2026-07-21T09:03:21-04:00
New Revision: 2cd5deb2208683bc2d3545e125258a9714f3aef1

URL: https://github.com/llvm/llvm-project/commit/2cd5deb2208683bc2d3545e125258a9714f3aef1
DIFF: https://github.com/llvm/llvm-project/commit/2cd5deb2208683bc2d3545e125258a9714f3aef1.diff

LOG: [libc++] Fix test-at-commit after df4bb1f3106d (#210983)

Added: 
    

Modified: 
    libcxx/utils/test-at-commit

Removed: 
    


################################################################################
diff  --git a/libcxx/utils/test-at-commit b/libcxx/utils/test-at-commit
index d1b33c243b4d5..4249d9c1ce86e 100755
--- a/libcxx/utils/test-at-commit
+++ b/libcxx/utils/test-at-commit
@@ -15,8 +15,6 @@ LIT_CONFIG_FILE = """
 # of libc++ installed at the given path.
 #
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{{flags}}',
     '-pthread' + (' -isysroot {{}}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else '')
 ))


        

From libcxx-commits at lists.llvm.org  Tue Jul 21 06:03:27 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 06:03:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix test-at-commit after
	df4bb1f3106d (PR #210983)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210983@github.com>
Message-ID: <6a5f6e1f.362da7ee.2c7435.dec3@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/210983

From libcxx-commits at lists.llvm.org  Tue Jul 21 06:04:06 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 06:04:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f6e46.bcf7152f.6cf47.78cb@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/210078

>From 34419f2461f3cc4ac0c285cfbb76e64f016db652 Mon Sep 17 00:00:00 2001
From: Pavel Novikov <dev-ape at yandex.ru>
Date: Thu, 16 Jul 2026 17:43:25 +0300
Subject: [PATCH 1/2] [libc++] fix minor performance issue

---
 libcxx/include/string | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index a297a05813cee..957eb15e4fe1f 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1359,13 +1359,13 @@ public:
   template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
   append(_ForwardIterator __first, _ForwardIterator __last) {
-    size_type __sz  = size();
-    size_type __cap = capacity();
-    size_type __n   = static_cast<size_type>(std::distance(__first, __last));
-    if (__n == 0)
+    if (__first == __last)
       return *this;
 
     if (__string_is_trivial_iterator_v<_ForwardIterator> && !__addr_in_range(*__first)) {
+      size_type __sz  = size();
+      size_type __cap = capacity();
+      size_type __n   = static_cast<size_type>(std::distance(__first, __last));
       if (__cap - __sz < __n)
         __grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
       __annotate_increase(__n);

>From bd806f470a02647245b01bfa9209fc4937699bc0 Mon Sep 17 00:00:00 2001
From: Pavel Novikov <dev-ape at yandex.ru>
Date: Tue, 21 Jul 2026 14:42:18 +0300
Subject: [PATCH 2/2] [libc++] added benchmarks for `string::append()`

---
 .../benchmarks/containers/string.bench.cpp    | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp
index 776ee5f58b7e2..5ad80d1d3ec5a 100644
--- a/libcxx/test/benchmarks/containers/string.bench.cpp
+++ b/libcxx/test/benchmarks/containers/string.bench.cpp
@@ -17,6 +17,7 @@
 #include "benchmark/benchmark.h"
 #include "make_string.h"
 #include "test_macros.h"
+#include "test_iterators.h"
 
 constexpr size_t small_size = 5;
 constexpr size_t large_size = 30;
@@ -502,6 +503,45 @@ int main(int argc, char** argv) {
           [](auto bm) { bm->Arg(large_size); }); // for naming
   }
 
+  {
+    static auto bench_impl =
+        []<size_t size, bool opaque, class CharT>(
+            std::integral_constant<size_t, size>,
+            std::bool_constant<opaque>,
+            std::type_identity<CharT>,
+            benchmark::State& state) {
+          std::basic_string<CharT> src(size, 'a');
+          auto getIterator = [&src](size_t i) {
+            // INT_MAX because we want overhead of ThrowingIterator without actually throwing
+            return ThrowingIterator<CharT>(src.data() + i, src.data() + src.size(), INT_MAX);
+          };
+          std::basic_string<CharT> string = src;
+          for (auto _ : state) {
+            benchmark::DoNotOptimize(string);
+            string.clear();
+            if constexpr (opaque)
+              benchmark::DoNotOptimize(src);
+            benchmark::DoNotOptimize(string.append(getIterator(0), getIterator(size)));
+          }
+        };
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (opaque)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::true_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, small_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(small_size); }); // for naming
+
+    bench("std::basic_string::append(ForwardIt, ForwardIt) (transparent)",
+          std::bind_front(bench_impl, std::integral_constant<size_t, large_size>{}, std::false_type{}),
+          [](auto bm) { bm->Arg(large_size); }); // for naming
+  }
+
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
   benchmark::Shutdown();


From libcxx-commits at lists.llvm.org  Tue Jul 21 06:04:28 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 06:04:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5f6e5c.83d2da6f.8cca0.d4d9@mx.google.com>


ldionne wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/containers/string.bench.cpp

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Tue Jul 21 06:13:42 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 06:13:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix test-at-commit after
	df4bb1f3106d (PR #210983)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210983@github.com>
Message-ID: <6a5f7086.c4d3d80a.849c3.7a6f@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>



---
Full diff: https://github.com/llvm/llvm-project/pull/210983.diff


1 Files Affected:

- (modified) libcxx/utils/test-at-commit (-2) 


``````````diff
diff --git a/libcxx/utils/test-at-commit b/libcxx/utils/test-at-commit
index d1b33c243b4d5..4249d9c1ce86e 100755
--- a/libcxx/utils/test-at-commit
+++ b/libcxx/utils/test-at-commit
@@ -15,8 +15,6 @@ LIT_CONFIG_FILE = """
 # of libc++ installed at the given path.
 #
 
-lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
-
 config.substitutions.append(('%{{flags}}',
     '-pthread' + (' -isysroot {{}}'.format('@CMAKE_OSX_SYSROOT@') if '@CMAKE_OSX_SYSROOT@' else '')
 ))

``````````

</details>


https://github.com/llvm/llvm-project/pull/210983

From libcxx-commits at lists.llvm.org  Tue Jul 21 06:27:57 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 21 Jul 2026 06:27:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
Message-ID: <llvm.org/llvm/llvm-project/pull/210993@github.com>


https://github.com/lknknm created https://github.com/llvm/llvm-project/pull/210993

Implement `constexpr` to `std::fpclassify` as defined by [P0533R9](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0533r9.pdf).

Towards https://github.com/llvm/llvm-project/issues/105174.

>From 8e4ce1588756045195a9c05c3688ee0919433b32 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 15:13:46 +0200
Subject: [PATCH] add: constexpr to fpclassify

---
 libcxx/include/cmath                                      | 2 +-
 libcxx/include/math.h                                     | 8 ++++----
 .../libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..992854b0490a3 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -138,7 +138,7 @@ long double    tanhl(long double x);
 
 bool signbit(arithmetic x);
 
-int fpclassify(arithmetic x);
+int fpclassify(arithmetic x); // constexpr since C++23
 
 bool isfinite(arithmetic x);
 bool isinf(arithmetic x);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..86ad9a1ae9b10 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -389,22 +389,22 @@ namespace __math {
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..926ff37cddbfc 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -203,9 +203,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);


From libcxx-commits at lists.llvm.org  Tue Jul 21 06:40:00 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 21 Jul 2026 06:40:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a5f76b0.c56fb102.2d07b8.efe7@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:12:45 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 21 Jul 2026 07:12:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a5f7e5d.e624eb8d.4a8a7.635f@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210993

>From 8e4ce1588756045195a9c05c3688ee0919433b32 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 15:13:46 +0200
Subject: [PATCH 1/2] add: constexpr to fpclassify

---
 libcxx/include/cmath                                      | 2 +-
 libcxx/include/math.h                                     | 8 ++++----
 .../libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..992854b0490a3 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -138,7 +138,7 @@ long double    tanhl(long double x);
 
 bool signbit(arithmetic x);
 
-int fpclassify(arithmetic x);
+int fpclassify(arithmetic x); // constexpr since C++23
 
 bool isfinite(arithmetic x);
 bool isinf(arithmetic x);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..86ad9a1ae9b10 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -389,22 +389,22 @@ namespace __math {
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..926ff37cddbfc 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -203,9 +203,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From b6ecf4cfc60def795b8f4814af4e9df2feda5121 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 16:12:31 +0200
Subject: [PATCH 2/2] update: gcc tests

---
 .../libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..455c03ef58d18 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -197,9 +197,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);


From libcxx-commits at lists.llvm.org  Tue Jul 21 07:19:34 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:19:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't require complete types in
 vector<T>::empty() (PR #210754)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210754@github.com>
Message-ID: <6a5f7ff6.e5907290.281687.4511@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/210754

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:21:21 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 07:21:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't require complete types in
 vector<T>::empty() (PR #210754)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210754@github.com>
Message-ID: <6a5f8061.507fc713.239434.2e03@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/210754

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:21:33 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 07:21:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 17ac8fd - [libc++] Don't require complete
 types in vector<T>::empty() (#210754)
Message-ID: <6a5f806d.f1c1923d.2d2b59.f8b3@mx.google.com>


Author: Louis Dionne
Date: 2026-07-21T10:21:27-04:00
New Revision: 17ac8fdd95283110a14f1de8c15a0fc661119296

URL: https://github.com/llvm/llvm-project/commit/17ac8fdd95283110a14f1de8c15a0fc661119296
DIFF: https://github.com/llvm/llvm-project/commit/17ac8fdd95283110a14f1de8c15a0fc661119296.diff

LOG: [libc++] Don't require complete types in vector<T>::empty() (#210754)

This was previously not required, but the patch to introduce a new
size-based vector layout unintentionally added this new requirement. We
almost certainly not want to promise this guarantee going forward, but
we should actually land this change explicitly and consider the
transition story, not do it as a fallout of another refactoring.

Fixes #210732

Added: 
    libcxx/test/libcxx/containers/sequences/vector/incomplete_type.compile.pass.cpp

Modified: 
    libcxx/include/__vector/layout.h
    libcxx/include/__vector/vector.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__vector/layout.h b/libcxx/include/__vector/layout.h
index 3318a13a8ede1..af03556dc2636 100644
--- a/libcxx/include/__vector/layout.h
+++ b/libcxx/include/__vector/layout.h
@@ -199,6 +199,7 @@ class __vector_layout {
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __size() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __capacity() const _NOEXCEPT;
+  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __empty() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __end_ptr() _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer __end_ptr() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_ptr() _NOEXCEPT;
@@ -313,6 +314,11 @@ __vector_layout<_Tp, _Alloc>::__capacity() const _NOEXCEPT {
   return __capacity_;
 }
 
+template <class _Tp, class _Alloc>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__empty() const _NOEXCEPT {
+  return __size_ == 0;
+}
+
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __vector_layout<_Tp, _Alloc>::pointer
 __vector_layout<_Tp, _Alloc>::__end_ptr() _NOEXCEPT {
@@ -425,6 +431,11 @@ __vector_layout<_Tp, _Alloc>::__capacity() const _NOEXCEPT {
   return static_cast<size_type>(__capacity_ - __begin_);
 }
 
+template <class _Tp, class _Alloc>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__empty() const _NOEXCEPT {
+  return __begin_ == __end_;
+}
+
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __vector_layout<_Tp, _Alloc>::pointer
 __vector_layout<_Tp, _Alloc>::__end_ptr() _NOEXCEPT {

diff  --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 8226a7f87a119..5e9fa4a7d0030 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -398,7 +398,7 @@ class vector {
     return __layout_.__capacity();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
-    return size() == 0;
+    return __layout_.__empty();
   }
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {

diff  --git a/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.compile.pass.cpp
new file mode 100644
index 0000000000000..029d29eb437f8
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.compile.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <vector>
+
+// This test pins down the current libc++ behavior that vector<T>::empty() can be
+// called even when T is an incomplete type. The standard does not require this:
+// [vector.overview] only guarantees that an incomplete type may be used to
+// instantiate vector, and requires the type to be complete before any method is
+// called.
+//
+// However, libc++ made that work previously, and this test pins down that behavior
+// to avoid breaking it unintentionally. Note that this is not a guarantee to users
+// that we will support this in the future: this merely guards against changing this
+// behavior unknowingly.
+
+#include <vector>
+
+struct Incomplete;
+
+bool call_empty(std::vector<Incomplete>& v) { return v.empty(); }
+bool call_empty_const(const std::vector<Incomplete>& v) { return v.empty(); }


        

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:21:34 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:21:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't require complete types in
 vector<T>::empty() (PR #210754)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210754@github.com>
Message-ID: <6a5f806e.0c05e68e.3c2f5d.349c@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/210754

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:21:47 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:21:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't require complete types in
 vector<T>::empty() (PR #210754)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210754@github.com>
Message-ID: <6a5f807b.b1416da1.3242c8.a69c@mx.google.com>


https://github.com/ldionne milestoned https://github.com/llvm/llvm-project/pull/210754

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:21:53 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Tue, 21 Jul 2026 07:21:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't require complete types in
 vector<T>::empty() (PR #210754)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210754@github.com>
Message-ID: <6a5f8081.e55dc341.1c6a0.57f6@mx.google.com>


philnik777 wrote:

For the record, I just talked with Louis, and with the wording change I'm happy.

https://github.com/llvm/llvm-project/pull/210754

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:21:59 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:21:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't require complete types in
 vector<T>::empty() (PR #210754)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210754@github.com>
Message-ID: <6a5f8087.e5907290.281687.8035@mx.google.com>


ldionne wrote:

/cherry-pick 17ac8fdd95283110a14f1de8c15a0fc661119296

https://github.com/llvm/llvm-project/pull/210754

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:29:08 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:29:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a5f8234.c5504292.78ef3.23d4@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM with duplicate `clear` removed.

https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:29:08 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:29:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a5f8234.92d2873c.aa1ff.5f37@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:29:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:29:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a5f8235.1728eaf3.2a2f6.e412@mx.google.com>


================

----------------
ldionne wrote:

This is now https://github.com/llvm/llvm-project/issues/209499

https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:29:09 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:29:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a5f8235.9edb5c9d.280be3.05de@mx.google.com>


================
@@ -397,6 +395,7 @@ void sequence_container_benchmarks(std::string container) {
 
           st.PauseTiming();
           c.clear();
----------------
ldionne wrote:

```suggestion
```

https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:51:12 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:51:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a5f8760.e5907290.281687.37ea@mx.google.com>


https://github.com/ldionne milestoned https://github.com/llvm/llvm-project/pull/210951

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:51:23 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:51:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a5f876b.f1c1923d.2d2b59.d5af@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/210951

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:51:23 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:51:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a5f876b.13be6df8.22eac4.d48d@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM with green CI.

https://github.com/llvm/llvm-project/pull/210951

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:51:23 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:51:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a5f876b.27b3b003.1f84cc.a500@mx.google.com>


================
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FILE_DEPENDENCIES: test.dat
+
+// <fstream>
+
+// streamsize basic_filebuf::xsgetn(char_type*, streamsize);
+
+// Test that xsgetn buffers properly
----------------
ldionne wrote:

Let's explain what's an extension about this.

https://github.com/llvm/llvm-project/pull/210951

From libcxx-commits at lists.llvm.org  Tue Jul 21 07:51:23 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 07:51:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a5f876b.824e11a6.9c376.b3de@mx.google.com>


================
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FILE_DEPENDENCIES: test.dat
+
+// <fstream>
+
+// streamsize basic_filebuf::xsgetn(char_type*, streamsize);
+
+// Test that xsgetn buffers properly
+
+#include <cassert>
+#include <fstream>
+
+int main(int, char**) {
+  std::ifstream is("test.dat");
----------------
ldionne wrote:

I think it would be nice to rewrite this test so that each test case is self-contained.

https://github.com/llvm/llvm-project/pull/210951

From libcxx-commits at lists.llvm.org  Tue Jul 21 08:09:17 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 08:09:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a5f8b9d.bd93e731.30f155.0e0c@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM with minor comment.

https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Tue Jul 21 08:09:17 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 08:09:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a5f8b9d.13be6df8.323b61.9f16@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Tue Jul 21 08:09:19 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 08:09:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a5f8b9f.c56fb102.6921f.32e6@mx.google.com>


================
@@ -12,6 +12,23 @@ To generate a summary, re-generate the new ABI list using the
 
 New entries should be added directly below the "Version" header.
 
+------------
+Version 24.0
+------------
+
+* [libc++] Optimize standard streams with sync_with_stdio(false)
+
+  This updates the standard streams so that the buffers are replaced with `basic_filebuf`s.
+
+  Fixes #21566
----------------
ldionne wrote:

```suggestion
```

Since we're just adding symbols, the justification why it's fine is obvious.

https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Tue Jul 21 08:13:06 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 08:13:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a5f8c82.f06f4994.2f0620.c824@mx.google.com>


https://github.com/ldionne requested changes to this pull request.

I'd like to see if we can improve our test suite before we land this. I'll make a pass at this.

https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Tue Jul 21 08:55:16 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 08:55:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't require complete types in
 vector<T>::empty() (PR #210754)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210754@github.com>
Message-ID: <6a5f9664.e55dc341.1c6a0.ac96@mx.google.com>


llvmbot wrote:

/pull-request llvm/llvm-project#211026

https://github.com/llvm/llvm-project/pull/210754

From libcxx-commits at lists.llvm.org  Tue Jul 21 09:46:09 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 21 Jul 2026 09:46:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a5fa251.824e11a6.322560.f47c@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210993

>From 8e4ce1588756045195a9c05c3688ee0919433b32 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 15:13:46 +0200
Subject: [PATCH 1/3] add: constexpr to fpclassify

---
 libcxx/include/cmath                                      | 2 +-
 libcxx/include/math.h                                     | 8 ++++----
 .../libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..992854b0490a3 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -138,7 +138,7 @@ long double    tanhl(long double x);
 
 bool signbit(arithmetic x);
 
-int fpclassify(arithmetic x);
+int fpclassify(arithmetic x); // constexpr since C++23
 
 bool isfinite(arithmetic x);
 bool isinf(arithmetic x);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..86ad9a1ae9b10 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -389,22 +389,22 @@ namespace __math {
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..926ff37cddbfc 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -203,9 +203,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From b6ecf4cfc60def795b8f4814af4e9df2feda5121 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 16:12:31 +0200
Subject: [PATCH 2/3] update: gcc tests

---
 .../libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..455c03ef58d18 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -197,9 +197,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From f191ac6676bcf419b3eb0aa92ddcb1291744a796 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 18:45:51 +0200
Subject: [PATCH 3/3] add: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 79 +++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/fpclassify.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
new file mode 100644
index 0000000000000..fda9f28a1389f
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool fpclassify(floating-point-type x); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
+    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+
+    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+
+    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(-0.0)) == FP_ZERO);
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+
+    if (std::is_signed<T>::value) {
+      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Tue Jul 21 09:46:27 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 21 Jul 2026 09:46:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a5fa263.bc28b987.2e0c9a.b7d8@mx.google.com>


https://github.com/lknknm ready_for_review https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Tue Jul 21 09:46:34 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 21 Jul 2026 09:46:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a5fa26a.92d2873c.2cdd42.fc1d@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210993

>From 8e4ce1588756045195a9c05c3688ee0919433b32 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 15:13:46 +0200
Subject: [PATCH 1/3] add: constexpr to fpclassify

---
 libcxx/include/cmath                                      | 2 +-
 libcxx/include/math.h                                     | 8 ++++----
 .../libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..992854b0490a3 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -138,7 +138,7 @@ long double    tanhl(long double x);
 
 bool signbit(arithmetic x);
 
-int fpclassify(arithmetic x);
+int fpclassify(arithmetic x); // constexpr since C++23
 
 bool isfinite(arithmetic x);
 bool isinf(arithmetic x);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..86ad9a1ae9b10 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -389,22 +389,22 @@ namespace __math {
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..926ff37cddbfc 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -203,9 +203,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From b6ecf4cfc60def795b8f4814af4e9df2feda5121 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 16:12:31 +0200
Subject: [PATCH 2/3] update: gcc tests

---
 .../libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..455c03ef58d18 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -197,9 +197,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From f191ac6676bcf419b3eb0aa92ddcb1291744a796 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 18:45:51 +0200
Subject: [PATCH 3/3] add: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 79 +++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/fpclassify.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
new file mode 100644
index 0000000000000..fda9f28a1389f
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool fpclassify(floating-point-type x); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
+    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+
+    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+
+    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(-0.0)) == FP_ZERO);
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+
+    if (std::is_signed<T>::value) {
+      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Tue Jul 21 09:47:15 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 09:47:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a5fa293.d681cab9.33af1e.040a@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Lucas Mellone (lknknm)

<details>
<summary>Changes</summary>

Implement `constexpr` to `std::fpclassify` as defined by [P0533R9](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0533r9.pdf).

Towards https://github.com/llvm/llvm-project/issues/105174. Follows-up https://github.com/llvm/llvm-project/commit/8889a3d3b5154.

---
Full diff: https://github.com/llvm/llvm-project/pull/210993.diff


5 Files Affected:

- (modified) libcxx/include/cmath (+1-1) 
- (modified) libcxx/include/math.h (+4-4) 
- (modified) libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp (+3-3) 
- (modified) libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp (+3-3) 
- (added) libcxx/test/std/numerics/c.math/fpclassify.pass.cpp (+79) 


``````````diff
diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..992854b0490a3 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -138,7 +138,7 @@ long double    tanhl(long double x);
 
 bool signbit(arithmetic x);
 
-int fpclassify(arithmetic x);
+int fpclassify(arithmetic x); // constexpr since C++23
 
 bool isfinite(arithmetic x);
 bool isinf(arithmetic x);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..86ad9a1ae9b10 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -389,22 +389,22 @@ namespace __math {
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..926ff37cddbfc 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -203,9 +203,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..455c03ef58d18 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -197,9 +197,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);
diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
new file mode 100644
index 0000000000000..fda9f28a1389f
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool fpclassify(floating-point-type x); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
+    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+
+    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+
+    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(-0.0)) == FP_ZERO);
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+
+    if (std::is_signed<T>::value) {
+      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Tue Jul 21 09:47:17 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Tue, 21 Jul 2026 09:47:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a5fa295.507fc713.3702e2.1b93@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Tue Jul 21 10:51:02 2026
From: libcxx-commits at lists.llvm.org (Pavel Novikov via libcxx-commits)
Date: Tue, 21 Jul 2026 10:51:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a5fb186.7d702ed2.13d102.5c0f@mx.google.com>


toughengineer wrote:

No idea. 650% looks ridiculous by any stretch of imagination so something is clearly wrong.

I get results with Clang 22.1.2 on x86-64 Ubuntu 26.04 that are more consistent with macOS 26.5 arm64 results here https://github.com/llvm/llvm-project/pull/210078#issuecomment-5034397079.

Which compiler flags are used for benchmarks?

https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Tue Jul 21 11:50:53 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 21 Jul 2026 11:50:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a5fbf8d.510f576d.33aa7d.0702@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From f81e6a6f9200d1e114201290b17587f02c20eaf2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/19] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From 156f6f8298622067ee8717dc3a84c197e32ab175 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/19] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 463a77c6efb5fbf4cffd4be0dc3331520f93f39c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/19] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From a9b71cb5a78f6f6f6d634195c77e2b5d8bc486f5 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/19] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 0e2f1cd3cd56fd8aa62d28fa31ee51a5debb3935 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/19] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 2f92ae8766eabd5f92917bfad30ca3e02c5de84a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/19] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 2cb082799c25d82c9342ee51160ef38bc8e03e3a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/19] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From d2243047a22b62ed633eb879e31bbd6780cd3181 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/19] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From d5ad73d9c7f4062f1b50eeb4add63e4366fa3f00 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/19] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 4eba2824c648d85429256a0fae120931ff56147d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/19] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index a77e98f845a06..425c13fea707e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -39,7 +39,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +47,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +63,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -81,7 +81,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index a8bc817ced291..ce81d1c00d376 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From 6aa16eb678b260549394cd25ca405516ba85f93d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/19] Format

---
 libcxx/docs/Status/Cxx26Papers.csv                |  2 +-
 .../range.elements/iterator/base.pass.cpp         | 15 ++++++++-------
 .../range.elements/sentinel/base.pass.cpp         |  2 +-
 .../range.split/iterator/base.pass.cpp            | 10 +++++-----
 .../range.split/iterator/deref.pass.cpp           |  7 +++----
 .../sentinel/ctor.convert.pass.cpp                |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..ffa36d3f5145c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 425c13fea707e..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -81,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From 6d0ae1b24e97c93f07c0f71080eebaf9dd2ccb01 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/19] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..052e5eab25433 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index ffa36d3f5145c..4d0fbca940b92 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 197dc412359324c40e27a81dc0976a098fa78187 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/19] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From 8190cbcadce534b72759e9c26d34535fac4257f3 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/19] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp               | 16 ++++++++++++----
 .../sentinel/ctor.convert.pass.cpp             | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index ce81d1c00d376..25d1413b2fa16 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -67,15 +67,21 @@ static_assert(
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From cf21399e152311c7555d384b4e942af19374ccf0 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/19] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From e3ee97abdd6316f8ee21b852dd1f1b15f1bae1a6 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/19] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}

>From 123dc9cdec024eee0e89a429f30e167fff8a5b4a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:05:55 +0300
Subject: [PATCH 17/19] Fixed formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index e28c36b1254af..bcce8e1f48f7b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,8 +22,7 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
+concept IsBaseNoexcept = requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From db4279e8d1e0d4ea60b6ed3163580f644939859f Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:18:55 +0300
Subject: [PATCH 18/19] Enable test

---
 .../ranges/range.adaptors/range.elements/sentinel/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index 885582a5c94a8..114b7fc1f1b71 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -37,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
   return 0;
 }

>From 95ea87f1c9e00907ef4b380de5437859466c6c6e Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 07:53:40 +0300
Subject: [PATCH 19/19] Added `std::is_constructible/convertible` tests

---
 .../iterator/ctor.base.compile.pass.cpp       | 30 +++++++++++++++++
 .../ctor.parent.iter.compile.pass.cpp         | 25 ++++++++++++++
 .../sentinel/ctor.parent.compile.pass.cpp     | 25 ++++++++++++++
 .../iterator/ctor.value.compile.pass.cpp      | 26 +++++++++++++++
 .../sentinel/ctor.value.compile.pass.cpp      | 24 ++++++++++++++
 .../iterator/ctor.parent.compile.pass.cpp     | 33 +++++++++++++++++++
 6 files changed, 163 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..ee4a297c1f4a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+  std::tuple<int>* begin() const;
+  Sent end();
+};
+
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..4a013d0072222
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..06e06aeb6db6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(filter_view&);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterSentinel, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterSentinel>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..e95042e26e146
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(W value);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
+
+using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
+static_assert(!std::is_constructible_v<Iter, SomeInt>);
+static_assert(!std::is_convertible_v<SomeInt, Iter>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..a39efb4318794
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.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++20
+
+// constexpr explicit sentinel(Bound bound);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
+static_assert(!std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
+static_assert(!std::is_convertible_v<IntSentinelWith<SomeInt>, Sent>);
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..171e2dce1e5f0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(basic_istream_view& parent) noexcept;
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <sstream>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// test that the constructor is explicit
+template <class CharT>
+using IstreamView = std::ranges::basic_istream_view<int, CharT>;
+template <class CharT>
+using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
+
+static_assert(!std::constructible_from<Iter<char>, IstreamView<char>&>);
+static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
+static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
+#endif


From libcxx-commits at lists.llvm.org  Tue Jul 21 11:51:15 2026
From: libcxx-commits at lists.llvm.org (Jon Roelofs via libcxx-commits)
Date: Tue, 21 Jul 2026 11:51:15 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libunwind] [libunwind][AIX] Handle
 VAPI-based return addresses in stack unwinding for LLU (PR #209280)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209280@github.com>
Message-ID: <6a5fbfa3.27b3b003.207f1a.4ca5@mx.google.com>


https://github.com/jroelofs approved this pull request.


https://github.com/llvm/llvm-project/pull/209280

From libcxx-commits at lists.llvm.org  Tue Jul 21 12:55:48 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 12:55:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a5fcec4.1905e68e.25404.ff44@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/209161

>From 059ec6cec9cb06a0be51d521c1945870a3ad2b0d Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 14:28:02 +0200
Subject: [PATCH 1/2] [libc++] Optimize standard streams with
 sync_with_stdio(false)

Fixes #21566
---
 libcxx/include/fstream                        |  15 +--
 libcxx/lib/abi/CHANGELOG.TXT                  |  17 +++
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...xxabi.v1.stable.noexceptions.nonew.abilist |   5 +-
 libcxx/src/ios.cpp                            |   7 --
 libcxx/src/iostream.cpp                       | 102 ++++++++++++++----
 .../cin.sync_with_stdio.sh.cpp                |  37 +++++++
 .../clog.sync_with_stdio.sh.cpp               |  29 +++++
 .../cout.sync_with_stdio.sh.cpp               |  27 +++++
 .../wcin.sync_with_stdio.sh.cpp               |  44 ++++++++
 .../wclog.sync_with_stdio.sh.cpp              |  36 +++++++
 .../wcout.sync_with_stdio.sh.cpp              |  34 ++++++
 17 files changed, 340 insertions(+), 35 deletions(-)
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..4b276251aa917 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -391,10 +391,10 @@ private:
 
   _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 
-  // There are multiple (__)open function, they use different C-API open
-  // function. After that call these functions behave the same. This function
-  // does that part and determines the final return value.
-  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __do_open(FILE* __file, ios_base::openmode __mode) {
+public:
+  // Update the buffer so that __file is considered to be the underlying file and is opened as __mode. This is used in
+  // the open family of functions as well as various iostreams in sync_with_stdio(false) mode.
+  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __adopt_file(FILE* __file, ios_base::openmode __mode) {
     __file_ = __file;
     if (!__file_)
       return nullptr;
@@ -417,6 +417,7 @@ private:
     return this;
   }
 
+private:
   // If the file is already open, switch to unbuffered mode. Otherwise, record
   // the request to use unbuffered mode so that we use that mode when we
   // eventually open the file.
@@ -742,7 +743,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(std::fopen(__s, __mdstr), __mode);
+  return __adopt_file(std::fopen(__s, __mdstr), __mode);
 }
 
 template <class _CharT, class _Traits>
@@ -753,7 +754,7 @@ inline basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::__open(in
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(fdopen(__fd, __mdstr), __mode);
+  return __adopt_file(fdopen(__fd, __mdstr), __mode);
 }
 
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
@@ -767,7 +768,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(_wfopen(__s, __mdstr), __mode);
+  return __adopt_file(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
 
diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index b09f297eb65ba..567c23717ed60 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -12,6 +12,23 @@ To generate a summary, re-generate the new ABI list using the
 
 New entries should be added directly below the "Version" header.
 
+------------
+Version 24.0
+------------
+
+* [libc++] Optimize standard streams with sync_with_stdio(false)
+
+  This updates the standard streams so that the buffers are replaced with `basic_filebuf`s.
+
+  Fixes #21566
+
+
+  All platforms
+  -------------
+  Symbol added: _ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+
 ------------
 Version 23.0
 ------------
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 414a239757e1c..09999516face2 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 12, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
@@ -1908,6 +1909,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2143,6 +2145,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 20, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 64, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 590c0de8ce20f..d53eeb7532009 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index f4f4ab99ad866..384904f79d20e 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index e1b1e110318fa..8f26fdb0462cb 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1905,6 +1906,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2137,6 +2139,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 11cc8f180e824..0a2858da94a67 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1644,6 +1644,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1778,6 +1779,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1916,6 +1918,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..903457e983046 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1643,6 +1643,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1777,6 +1778,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1915,6 +1917,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 2b69d6dc89138..c24cdf845b489 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -2002,4 +2002,7 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT', 'is_defined': True}
\ No newline at end of file
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index 2e049098740dc..0b728db5b76f9 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -366,13 +366,6 @@ void ios_base::__set_failbit_and_consider_rethrow() {
 #endif // _LIBCPP_HAS_EXCEPTIONS
 }
 
-bool ios_base::sync_with_stdio(bool sync) {
-  static bool previous_state = true;
-  bool r                     = previous_state;
-  previous_state             = sync;
-  return r;
-}
-
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..551eb25f8fe4a 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -10,6 +10,7 @@
 
 #include <__memory/construct_at.h>
 #include <__ostream/basic_ostream.h>
+#include <fstream>
 #include <istream>
 
 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
@@ -34,21 +35,44 @@ union stream {
   StreamT value;
 };
 
-template <class StreamT, class BufferT>
+template <class StreamT, class SyncBufT, class UnsyncBufT>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
   struct {
-    BufferT buffer;
+    union {
+      SyncBufT sync_buffer;
+      UnsyncBufT unsync_buffer;
+    };
     mbstate_t mb;
   };
 };
 
-template <class StreamT, class BufferT>
-void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) {
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
   data.mb = {};
-  std::construct_at(&data.buffer, stdstream, &data.mb);
-  std::construct_at(&stream.value, &data.buffer);
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  std::construct_at(&stream.value, &data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_sync_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
+  data.unsync_buffer.__adopt_file(nullptr, {}); // reset the file, so that basic_filebuf doesn't close standard streams
+  std::destroy_at(&data.unsync_buffer);
+  data.mb = {};
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  stream.value.rdbuf(&data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_unsync_stream(FILE* stdstream,
+                             stream<StreamT>& stream,
+                             stream_data<StreamT, SyncBufT, UnsyncBufT>& data,
+                             ios_base::openmode mode) {
+  std::destroy_at(&data.sync_buffer);
+  std::construct_at(&data.unsync_buffer);
+  data.unsync_buffer.__adopt_file(stdstream, mode);
+  stream.value.rdbuf(&data.unsync_buffer);
 }
 
 #define CHAR_MANGLING_char "D"
@@ -62,29 +86,39 @@ void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT,
 #endif
 
 #ifdef _LIBCPP_ABI_MICROSOFT
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__(                                \
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12 at A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
 #endif
 
 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
 
-STREAM(basic_istream, __stdinbuf, char, cin);
-STREAM(basic_ostream, __stdoutbuf, char, cout);
-STREAM(basic_ostream, __stdoutbuf, char, cerr);
-STREAM(basic_ostream, __stdoutbuf, char, clog);
+#if _LIBCPP_HAS_FILESYSTEM
+template <class CharT>
+using unsync_buffer = basic_filebuf<CharT>;
+#else
+// If we don't have access to files we can't treat the standard streams as files either - just save a dummy object in
+// that case.
+template <class>
+using unsync_buffer = char;
+#endif
+
+STREAM(basic_istream, __stdinbuf, unsync_buffer, char, cin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, clog);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+STREAM(basic_istream, __stdinbuf, unsync_buffer, wchar_t, wcin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wclog);
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
@@ -155,5 +189,37 @@ ios_base::Init::Init() {
 
 ios_base::Init::~Init() {}
 
+bool ios_base::sync_with_stdio(bool sync) {
+  static bool previous_state = true;
+  bool r                     = previous_state;
+
+#if _LIBCPP_HAS_FILESYSTEM
+  if (sync != previous_state) {
+    if (sync) {
+      switch_to_sync_stream(stdin, cin, cin_data);
+      switch_to_sync_stream(stdout, cout, cout_data);
+      switch_to_sync_stream(stderr, clog, clog_data);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_sync_stream(stdin, wcin, wcin_data);
+      switch_to_sync_stream(stdout, wcout, wcout_data);
+      switch_to_sync_stream(stderr, wclog, wclog_data);
+#  endif
+    } else {
+      switch_to_unsync_stream(stdin, cin, cin_data, ios::in);
+      switch_to_unsync_stream(stdout, cout, cout_data, ios::out);
+      switch_to_unsync_stream(stderr, clog, clog_data, ios::out);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_unsync_stream(stdin, wcin, wcin_data, ios::in);
+      switch_to_unsync_stream(stdout, wcout, wcout_data, ios::out);
+      switch_to_unsync_stream(stderr, wclog, wclog_data, ios::out);
+#  endif
+    }
+  }
+#endif // _LIBCPP_HAS_FILESYSTEM
+
+  previous_state = sync;
+  return r;
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..052356449c5ab
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::cin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..ab30dee5f695d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::clog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3a94964f09223
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::cout << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..db6c5ba0e8660
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// istream wcin;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::wcin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..c0a0f299451fb
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wclog;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wclog << L"1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..a2cdc070620b1
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wcout;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wcout << L"1234";
+  return 0;
+}

>From ebf915fac915353eca504da6839c2a78b8a9fbfb Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 21 Jul 2026 14:16:12 -0400
Subject: [PATCH 2/2] Add more test coverage for sync_with_stdio stream
 switching

Extend the tests for the sync_with_stdio(false) optimization:

- ensure that the optimization is actually in effect by checking the
  type of the underlying buffer.

- verify that toggling the synchronization mode preserves per-stream state.

- cerr/wcerr are deliberately not switched to a filebuf, so check they keep
  producing correct output.

- check that C stdio and cout output interleave in order in synchronized  mode.

- read a large input to force the buffered filebuf to refill (underflow) several
  times in unsynchronized mode, and check EOF detection.
---
 .../sync_with_stdio.buffer_type.pass.cpp      |  70 +++++++++++
 .../cerr.sync_with_stdio.sh.cpp               |  38 ++++++
 .../narrow.stream.objects/cin.readmany.sh.cpp |  56 +++++++++
 .../cout.interleave_with_stdio.sh.cpp         |  34 ++++++
 .../sync_with_stdio.state.pass.cpp            | 109 ++++++++++++++++++
 .../wcerr.sync_with_stdio.sh.cpp              |  35 ++++++
 .../wide.stream.objects/wcin.readmany.sh.cpp  |  58 ++++++++++
 .../sync_with_stdio.pass.cpp                  |   2 +
 8 files changed, 402 insertions(+)
 create mode 100644 libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp

diff --git a/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp b/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
new file mode 100644
index 0000000000000..789ef630a2a2e
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// static bool ios_base::sync_with_stdio(bool sync = true);
+
+// This is a libc++ implementation detail: when sync_with_stdio(false) is used,
+// libc++ swaps the buffers of cin/cout/clog (and the wide equivalents) for a
+// basic_filebuf to avoid going through the C stdio layer. This test ensures
+// that this optimization is applied by inspecting the type of the underlying
+// buffer.
+
+// The optimization is only applied when we have a filesystem, since fstream
+// is not present otherwise. Also, the test itself requires RTTI.
+// UNSUPPORTED: no-filesystem, no-rtti
+
+#include <cassert>
+#include <fstream>
+#include <iostream>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  // By default the streams are synchronized, so they are not backed by a filebuf.
+  assert(!dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  // Turning synchronization off switches cin/cout/clog to a filebuf, but leaves
+  // cerr untouched (it stays synchronized so it keeps flushing eagerly).
+  std::ios_base::sync_with_stdio(false);
+  assert(dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  // Turning it back on restores the original buffers.
+  std::ios_base::sync_with_stdio(true);
+  assert(!dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..9f40e1ae04e67
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream cerr;
+
+// Unlike cin/cout/clog, std::cerr is still unbuffered even when sync_with_stdio(false)
+// is called. This test ensures that it keeps producing correctly-ordered output when
+// interleaved with the C stdio functions even after sync_with_stdio(false).
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios_base::sync_with_stdio(false));
+
+  // interleave output with C stdio functions
+  std::fputs("1", stderr);
+  std::cerr << "2";
+  std::fprintf(stderr, "%d", 3);
+  std::cerr << 4;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
new file mode 100644
index 0000000000000..08f417b9ba451
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// Read an input that is much larger than any reasonable internal buffer so that the
+// buffer has to be refilled (underflow) several times, and check that every value
+// is read correctly and that EOF is detected at the end.
+
+// RUN: seq 1 5000 > %t.input
+
+// RUN: %{build} -DSYNC_WITH_STDIO_FALSE
+// RUN: %{exec} %t.exe < %t.input
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+#ifdef SYNC_WITH_STDIO_FALSE
+  assert(std::ios_base::sync_with_stdio(false));
+#endif
+
+  long sum  = 0;
+  int count = 0;
+  int value = 0;
+  while (std::cin >> value) {
+    sum += value;
+    ++count;
+  }
+
+  assert(count == 5000);
+  assert(sum == 5000L * 5001L / 2L); // sum of 1..5000
+  assert(std::cin.eof());
+  assert(!std::cin.good());
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..8497801f1778c
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// In synchronized mode, output written through std::cout and through the C stdio
+// functions must appear in the order in which it was written.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 123456 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+
+int main(int, char**) {
+  std::fputs("1", stdout);
+  std::cout << "2";
+  std::fprintf(stdout, "%d", 3);
+  std::cout << 4;
+  std::fputs("5", stdout);
+  std::cout << 6;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp b/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
new file mode 100644
index 0000000000000..e6f9ef3002396
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// Changing the synchronization mode with std::ios_base::sync_with_stdio must
+// preserve the per-stream state of the standard stream objects.
+
+#include <cassert>
+#include <iostream>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  int dummy       = 0;
+  const int index = std::ios_base::xalloc();
+
+  std::cin.iword(index)  = 11;
+  std::cout.iword(index) = 22;
+  std::cerr.iword(index) = 33;
+  std::clog.iword(index) = 44;
+  std::cout.pword(index) = &dummy;
+
+  std::cout.setf(std::ios_base::hex, std::ios_base::basefield);
+  std::cout.precision(7);
+  std::cout.fill('*');
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::wcin.iword(index)  = 11;
+  std::wcout.iword(index) = 22;
+  std::wcerr.iword(index) = 33;
+  std::wclog.iword(index) = 44;
+  std::wcout.pword(index) = &dummy;
+
+  std::wcout.setf(std::ios_base::hex, std::ios_base::basefield);
+  std::wcout.precision(7);
+  std::wcout.fill(L'*');
+#endif
+
+  // Switch to the unsynchronized mode. This swaps the underlying buffers, but the
+  // stream objects and all their state must be preserved.
+  std::ios_base::sync_with_stdio(false);
+
+  assert(std::cin.iword(index) == 11);
+  assert(std::cout.iword(index) == 22);
+  assert(std::cerr.iword(index) == 33);
+  assert(std::clog.iword(index) == 44);
+  assert(std::cout.pword(index) == &dummy);
+  assert((std::cout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::cout.precision() == 7);
+  assert(std::cout.fill() == '*');
+  assert(std::cin.tie() == &std::cout);
+  assert(std::cerr.tie() == &std::cout);
+  assert(std::cerr.flags() & std::ios_base::unitbuf);
+  assert(std::cin.good());
+  assert(std::cout.good());
+  assert(std::cin.rdbuf());
+  assert(std::cout.rdbuf());
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert(std::wcin.iword(index) == 11);
+  assert(std::wcout.iword(index) == 22);
+  assert(std::wcerr.iword(index) == 33);
+  assert(std::wclog.iword(index) == 44);
+  assert(std::wcout.pword(index) == &dummy);
+  assert((std::wcout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::wcout.precision() == 7);
+  assert(std::wcout.fill() == L'*');
+  assert(std::wcin.tie() == &std::wcout);
+  assert(std::wcerr.tie() == &std::wcout);
+  assert(std::wcerr.flags() & std::ios_base::unitbuf);
+  assert(std::wcin.good());
+  assert(std::wcout.good());
+  assert(std::wcin.rdbuf());
+  assert(std::wcout.rdbuf());
+#endif
+
+  // Switch back to synchronized mode and check that the state is still there.
+  std::ios_base::sync_with_stdio(true);
+
+  assert(std::cout.iword(index) == 22);
+  assert(std::cout.pword(index) == &dummy);
+  assert((std::cout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::cout.precision() == 7);
+  assert(std::cout.fill() == '*');
+  assert(std::cin.tie() == &std::cout);
+  assert(std::cerr.tie() == &std::cout);
+  assert(std::cerr.flags() & std::ios_base::unitbuf);
+  assert(std::cout.good());
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert(std::wcout.iword(index) == 22);
+  assert(std::wcout.pword(index) == &dummy);
+  assert((std::wcout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::wcout.precision() == 7);
+  assert(std::wcout.fill() == L'*');
+  assert(std::wcin.tie() == &std::wcout);
+  assert(std::wcerr.tie() == &std::wcout);
+  assert(std::wcerr.flags() & std::ios_base::unitbuf);
+  assert(std::wcout.good());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..2a65be4863bf5
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// wostream wcerr;
+
+// Unlike wcin/wcout/wclog, std::wcerr is still unbuffered even when sync_with_stdio(false)
+// is called. This test ensures that it produces correct output after sync_with_stdio(false).
+// We can't check interleaving with C stdio functions here since C stdio uses narrow characters.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios_base::sync_with_stdio(false));
+
+  std::wcerr << L"12" << 34;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
new file mode 100644
index 0000000000000..57113e21535e3
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// wistream wcin;
+
+// Read an input that is much larger than any reasonable internal buffer so that the
+// buffer has to be refilled (underflow) several times, and check that every value
+// is read correctly and that EOF is detected at the end.
+
+// RUN: seq 1 5000 > %t.input
+
+// RUN: %{build} -DSYNC_WITH_STDIO_FALSE
+// RUN: %{exec} %t.exe < %t.input
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+#ifdef SYNC_WITH_STDIO_FALSE
+  assert(std::ios_base::sync_with_stdio(false));
+#endif
+
+  long sum  = 0;
+  int count = 0;
+  int value = 0;
+  while (std::wcin >> value) {
+    sum += value;
+    ++count;
+  }
+
+  assert(count == 5000);
+  assert(sum == 5000L * 5001L / 2L); // sum of 1..5000
+  assert(std::wcin.eof());
+  assert(!std::wcin.good());
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
index 0ec24b77560e5..3051f0dc3b1cb 100644
--- a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
+++ b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
@@ -17,6 +17,8 @@
 
 int main(int, char**)
 {
+    ASSERT_SAME_TYPE(decltype(std::ios_base::sync_with_stdio(false)), bool);
+
     assert( std::ios_base::sync_with_stdio(false));
     assert(!std::ios_base::sync_with_stdio(false));
     assert(!std::ios_base::sync_with_stdio(true));


From libcxx-commits at lists.llvm.org  Tue Jul 21 12:59:19 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 12:59:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a5fcf97.1e1ac41d.317291.fd5c@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions ,cpp -- libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp libcxx/include/fstream libcxx/src/ios.cpp libcxx/src/iostream.cpp libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
index 3051f0dc3..17251cdf4 100644
--- a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
+++ b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
@@ -17,16 +17,16 @@
 
 int main(int, char**)
 {
-    ASSERT_SAME_TYPE(decltype(std::ios_base::sync_with_stdio(false)), bool);
+  ASSERT_SAME_TYPE(decltype(std::ios_base::sync_with_stdio(false)), bool);
 
-    assert( std::ios_base::sync_with_stdio(false));
-    assert(!std::ios_base::sync_with_stdio(false));
-    assert(!std::ios_base::sync_with_stdio(true));
-    assert( std::ios_base::sync_with_stdio(true));
-    assert( std::ios_base::sync_with_stdio());
-    assert( std::ios_base::sync_with_stdio(false));
-    assert(!std::ios_base::sync_with_stdio());
-    assert( std::ios_base::sync_with_stdio());
+  assert(std::ios_base::sync_with_stdio(false));
+  assert(!std::ios_base::sync_with_stdio(false));
+  assert(!std::ios_base::sync_with_stdio(true));
+  assert(std::ios_base::sync_with_stdio(true));
+  assert(std::ios_base::sync_with_stdio());
+  assert(std::ios_base::sync_with_stdio(false));
+  assert(!std::ios_base::sync_with_stdio());
+  assert(std::ios_base::sync_with_stdio());
 
   return 0;
 }

``````````

</details>


https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Tue Jul 21 12:59:57 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 12:59:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a5fcfbd.1e1ac41d.317291.08ab@mx.google.com>


https://github.com/ldionne approved this pull request.

I added some more test coverage (first draft with Claude, then reviewed and modified).

LGTM now, but please have a look and let me know if you're OK with the added test coverage.

https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Tue Jul 21 13:00:40 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 13:00:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a5fcfe8.0c05e68e.81651.417b@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/209161

>From 059ec6cec9cb06a0be51d521c1945870a3ad2b0d Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 14:28:02 +0200
Subject: [PATCH 1/3] [libc++] Optimize standard streams with
 sync_with_stdio(false)

Fixes #21566
---
 libcxx/include/fstream                        |  15 +--
 libcxx/lib/abi/CHANGELOG.TXT                  |  17 +++
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...xxabi.v1.stable.noexceptions.nonew.abilist |   5 +-
 libcxx/src/ios.cpp                            |   7 --
 libcxx/src/iostream.cpp                       | 102 ++++++++++++++----
 .../cin.sync_with_stdio.sh.cpp                |  37 +++++++
 .../clog.sync_with_stdio.sh.cpp               |  29 +++++
 .../cout.sync_with_stdio.sh.cpp               |  27 +++++
 .../wcin.sync_with_stdio.sh.cpp               |  44 ++++++++
 .../wclog.sync_with_stdio.sh.cpp              |  36 +++++++
 .../wcout.sync_with_stdio.sh.cpp              |  34 ++++++
 17 files changed, 340 insertions(+), 35 deletions(-)
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..4b276251aa917 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -391,10 +391,10 @@ private:
 
   _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 
-  // There are multiple (__)open function, they use different C-API open
-  // function. After that call these functions behave the same. This function
-  // does that part and determines the final return value.
-  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __do_open(FILE* __file, ios_base::openmode __mode) {
+public:
+  // Update the buffer so that __file is considered to be the underlying file and is opened as __mode. This is used in
+  // the open family of functions as well as various iostreams in sync_with_stdio(false) mode.
+  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __adopt_file(FILE* __file, ios_base::openmode __mode) {
     __file_ = __file;
     if (!__file_)
       return nullptr;
@@ -417,6 +417,7 @@ private:
     return this;
   }
 
+private:
   // If the file is already open, switch to unbuffered mode. Otherwise, record
   // the request to use unbuffered mode so that we use that mode when we
   // eventually open the file.
@@ -742,7 +743,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(std::fopen(__s, __mdstr), __mode);
+  return __adopt_file(std::fopen(__s, __mdstr), __mode);
 }
 
 template <class _CharT, class _Traits>
@@ -753,7 +754,7 @@ inline basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::__open(in
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(fdopen(__fd, __mdstr), __mode);
+  return __adopt_file(fdopen(__fd, __mdstr), __mode);
 }
 
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
@@ -767,7 +768,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(_wfopen(__s, __mdstr), __mode);
+  return __adopt_file(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
 
diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index b09f297eb65ba..567c23717ed60 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -12,6 +12,23 @@ To generate a summary, re-generate the new ABI list using the
 
 New entries should be added directly below the "Version" header.
 
+------------
+Version 24.0
+------------
+
+* [libc++] Optimize standard streams with sync_with_stdio(false)
+
+  This updates the standard streams so that the buffers are replaced with `basic_filebuf`s.
+
+  Fixes #21566
+
+
+  All platforms
+  -------------
+  Symbol added: _ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+
 ------------
 Version 23.0
 ------------
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 414a239757e1c..09999516face2 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 12, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
@@ -1908,6 +1909,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2143,6 +2145,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 20, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 64, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 590c0de8ce20f..d53eeb7532009 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index f4f4ab99ad866..384904f79d20e 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index e1b1e110318fa..8f26fdb0462cb 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1905,6 +1906,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2137,6 +2139,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 11cc8f180e824..0a2858da94a67 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1644,6 +1644,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1778,6 +1779,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1916,6 +1918,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..903457e983046 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1643,6 +1643,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1777,6 +1778,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1915,6 +1917,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 2b69d6dc89138..c24cdf845b489 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -2002,4 +2002,7 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT', 'is_defined': True}
\ No newline at end of file
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index 2e049098740dc..0b728db5b76f9 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -366,13 +366,6 @@ void ios_base::__set_failbit_and_consider_rethrow() {
 #endif // _LIBCPP_HAS_EXCEPTIONS
 }
 
-bool ios_base::sync_with_stdio(bool sync) {
-  static bool previous_state = true;
-  bool r                     = previous_state;
-  previous_state             = sync;
-  return r;
-}
-
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..551eb25f8fe4a 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -10,6 +10,7 @@
 
 #include <__memory/construct_at.h>
 #include <__ostream/basic_ostream.h>
+#include <fstream>
 #include <istream>
 
 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
@@ -34,21 +35,44 @@ union stream {
   StreamT value;
 };
 
-template <class StreamT, class BufferT>
+template <class StreamT, class SyncBufT, class UnsyncBufT>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
   struct {
-    BufferT buffer;
+    union {
+      SyncBufT sync_buffer;
+      UnsyncBufT unsync_buffer;
+    };
     mbstate_t mb;
   };
 };
 
-template <class StreamT, class BufferT>
-void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) {
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
   data.mb = {};
-  std::construct_at(&data.buffer, stdstream, &data.mb);
-  std::construct_at(&stream.value, &data.buffer);
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  std::construct_at(&stream.value, &data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_sync_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
+  data.unsync_buffer.__adopt_file(nullptr, {}); // reset the file, so that basic_filebuf doesn't close standard streams
+  std::destroy_at(&data.unsync_buffer);
+  data.mb = {};
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  stream.value.rdbuf(&data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_unsync_stream(FILE* stdstream,
+                             stream<StreamT>& stream,
+                             stream_data<StreamT, SyncBufT, UnsyncBufT>& data,
+                             ios_base::openmode mode) {
+  std::destroy_at(&data.sync_buffer);
+  std::construct_at(&data.unsync_buffer);
+  data.unsync_buffer.__adopt_file(stdstream, mode);
+  stream.value.rdbuf(&data.unsync_buffer);
 }
 
 #define CHAR_MANGLING_char "D"
@@ -62,29 +86,39 @@ void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT,
 #endif
 
 #ifdef _LIBCPP_ABI_MICROSOFT
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__(                                \
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12 at A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
 #endif
 
 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
 
-STREAM(basic_istream, __stdinbuf, char, cin);
-STREAM(basic_ostream, __stdoutbuf, char, cout);
-STREAM(basic_ostream, __stdoutbuf, char, cerr);
-STREAM(basic_ostream, __stdoutbuf, char, clog);
+#if _LIBCPP_HAS_FILESYSTEM
+template <class CharT>
+using unsync_buffer = basic_filebuf<CharT>;
+#else
+// If we don't have access to files we can't treat the standard streams as files either - just save a dummy object in
+// that case.
+template <class>
+using unsync_buffer = char;
+#endif
+
+STREAM(basic_istream, __stdinbuf, unsync_buffer, char, cin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, clog);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+STREAM(basic_istream, __stdinbuf, unsync_buffer, wchar_t, wcin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wclog);
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
@@ -155,5 +189,37 @@ ios_base::Init::Init() {
 
 ios_base::Init::~Init() {}
 
+bool ios_base::sync_with_stdio(bool sync) {
+  static bool previous_state = true;
+  bool r                     = previous_state;
+
+#if _LIBCPP_HAS_FILESYSTEM
+  if (sync != previous_state) {
+    if (sync) {
+      switch_to_sync_stream(stdin, cin, cin_data);
+      switch_to_sync_stream(stdout, cout, cout_data);
+      switch_to_sync_stream(stderr, clog, clog_data);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_sync_stream(stdin, wcin, wcin_data);
+      switch_to_sync_stream(stdout, wcout, wcout_data);
+      switch_to_sync_stream(stderr, wclog, wclog_data);
+#  endif
+    } else {
+      switch_to_unsync_stream(stdin, cin, cin_data, ios::in);
+      switch_to_unsync_stream(stdout, cout, cout_data, ios::out);
+      switch_to_unsync_stream(stderr, clog, clog_data, ios::out);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_unsync_stream(stdin, wcin, wcin_data, ios::in);
+      switch_to_unsync_stream(stdout, wcout, wcout_data, ios::out);
+      switch_to_unsync_stream(stderr, wclog, wclog_data, ios::out);
+#  endif
+    }
+  }
+#endif // _LIBCPP_HAS_FILESYSTEM
+
+  previous_state = sync;
+  return r;
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..052356449c5ab
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::cin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..ab30dee5f695d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::clog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3a94964f09223
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::cout << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..db6c5ba0e8660
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// istream wcin;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::wcin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..c0a0f299451fb
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wclog;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wclog << L"1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..a2cdc070620b1
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wcout;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wcout << L"1234";
+  return 0;
+}

>From ebf915fac915353eca504da6839c2a78b8a9fbfb Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 21 Jul 2026 14:16:12 -0400
Subject: [PATCH 2/3] Add more test coverage for sync_with_stdio stream
 switching

Extend the tests for the sync_with_stdio(false) optimization:

- ensure that the optimization is actually in effect by checking the
  type of the underlying buffer.

- verify that toggling the synchronization mode preserves per-stream state.

- cerr/wcerr are deliberately not switched to a filebuf, so check they keep
  producing correct output.

- check that C stdio and cout output interleave in order in synchronized  mode.

- read a large input to force the buffered filebuf to refill (underflow) several
  times in unsynchronized mode, and check EOF detection.
---
 .../sync_with_stdio.buffer_type.pass.cpp      |  70 +++++++++++
 .../cerr.sync_with_stdio.sh.cpp               |  38 ++++++
 .../narrow.stream.objects/cin.readmany.sh.cpp |  56 +++++++++
 .../cout.interleave_with_stdio.sh.cpp         |  34 ++++++
 .../sync_with_stdio.state.pass.cpp            | 109 ++++++++++++++++++
 .../wcerr.sync_with_stdio.sh.cpp              |  35 ++++++
 .../wide.stream.objects/wcin.readmany.sh.cpp  |  58 ++++++++++
 .../sync_with_stdio.pass.cpp                  |   2 +
 8 files changed, 402 insertions(+)
 create mode 100644 libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp

diff --git a/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp b/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
new file mode 100644
index 0000000000000..789ef630a2a2e
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// static bool ios_base::sync_with_stdio(bool sync = true);
+
+// This is a libc++ implementation detail: when sync_with_stdio(false) is used,
+// libc++ swaps the buffers of cin/cout/clog (and the wide equivalents) for a
+// basic_filebuf to avoid going through the C stdio layer. This test ensures
+// that this optimization is applied by inspecting the type of the underlying
+// buffer.
+
+// The optimization is only applied when we have a filesystem, since fstream
+// is not present otherwise. Also, the test itself requires RTTI.
+// UNSUPPORTED: no-filesystem, no-rtti
+
+#include <cassert>
+#include <fstream>
+#include <iostream>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  // By default the streams are synchronized, so they are not backed by a filebuf.
+  assert(!dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  // Turning synchronization off switches cin/cout/clog to a filebuf, but leaves
+  // cerr untouched (it stays synchronized so it keeps flushing eagerly).
+  std::ios_base::sync_with_stdio(false);
+  assert(dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  // Turning it back on restores the original buffers.
+  std::ios_base::sync_with_stdio(true);
+  assert(!dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..9f40e1ae04e67
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream cerr;
+
+// Unlike cin/cout/clog, std::cerr is still unbuffered even when sync_with_stdio(false)
+// is called. This test ensures that it keeps producing correctly-ordered output when
+// interleaved with the C stdio functions even after sync_with_stdio(false).
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios_base::sync_with_stdio(false));
+
+  // interleave output with C stdio functions
+  std::fputs("1", stderr);
+  std::cerr << "2";
+  std::fprintf(stderr, "%d", 3);
+  std::cerr << 4;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
new file mode 100644
index 0000000000000..08f417b9ba451
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// Read an input that is much larger than any reasonable internal buffer so that the
+// buffer has to be refilled (underflow) several times, and check that every value
+// is read correctly and that EOF is detected at the end.
+
+// RUN: seq 1 5000 > %t.input
+
+// RUN: %{build} -DSYNC_WITH_STDIO_FALSE
+// RUN: %{exec} %t.exe < %t.input
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+#ifdef SYNC_WITH_STDIO_FALSE
+  assert(std::ios_base::sync_with_stdio(false));
+#endif
+
+  long sum  = 0;
+  int count = 0;
+  int value = 0;
+  while (std::cin >> value) {
+    sum += value;
+    ++count;
+  }
+
+  assert(count == 5000);
+  assert(sum == 5000L * 5001L / 2L); // sum of 1..5000
+  assert(std::cin.eof());
+  assert(!std::cin.good());
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..8497801f1778c
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// In synchronized mode, output written through std::cout and through the C stdio
+// functions must appear in the order in which it was written.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 123456 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+
+int main(int, char**) {
+  std::fputs("1", stdout);
+  std::cout << "2";
+  std::fprintf(stdout, "%d", 3);
+  std::cout << 4;
+  std::fputs("5", stdout);
+  std::cout << 6;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp b/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
new file mode 100644
index 0000000000000..e6f9ef3002396
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// Changing the synchronization mode with std::ios_base::sync_with_stdio must
+// preserve the per-stream state of the standard stream objects.
+
+#include <cassert>
+#include <iostream>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  int dummy       = 0;
+  const int index = std::ios_base::xalloc();
+
+  std::cin.iword(index)  = 11;
+  std::cout.iword(index) = 22;
+  std::cerr.iword(index) = 33;
+  std::clog.iword(index) = 44;
+  std::cout.pword(index) = &dummy;
+
+  std::cout.setf(std::ios_base::hex, std::ios_base::basefield);
+  std::cout.precision(7);
+  std::cout.fill('*');
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::wcin.iword(index)  = 11;
+  std::wcout.iword(index) = 22;
+  std::wcerr.iword(index) = 33;
+  std::wclog.iword(index) = 44;
+  std::wcout.pword(index) = &dummy;
+
+  std::wcout.setf(std::ios_base::hex, std::ios_base::basefield);
+  std::wcout.precision(7);
+  std::wcout.fill(L'*');
+#endif
+
+  // Switch to the unsynchronized mode. This swaps the underlying buffers, but the
+  // stream objects and all their state must be preserved.
+  std::ios_base::sync_with_stdio(false);
+
+  assert(std::cin.iword(index) == 11);
+  assert(std::cout.iword(index) == 22);
+  assert(std::cerr.iword(index) == 33);
+  assert(std::clog.iword(index) == 44);
+  assert(std::cout.pword(index) == &dummy);
+  assert((std::cout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::cout.precision() == 7);
+  assert(std::cout.fill() == '*');
+  assert(std::cin.tie() == &std::cout);
+  assert(std::cerr.tie() == &std::cout);
+  assert(std::cerr.flags() & std::ios_base::unitbuf);
+  assert(std::cin.good());
+  assert(std::cout.good());
+  assert(std::cin.rdbuf());
+  assert(std::cout.rdbuf());
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert(std::wcin.iword(index) == 11);
+  assert(std::wcout.iword(index) == 22);
+  assert(std::wcerr.iword(index) == 33);
+  assert(std::wclog.iword(index) == 44);
+  assert(std::wcout.pword(index) == &dummy);
+  assert((std::wcout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::wcout.precision() == 7);
+  assert(std::wcout.fill() == L'*');
+  assert(std::wcin.tie() == &std::wcout);
+  assert(std::wcerr.tie() == &std::wcout);
+  assert(std::wcerr.flags() & std::ios_base::unitbuf);
+  assert(std::wcin.good());
+  assert(std::wcout.good());
+  assert(std::wcin.rdbuf());
+  assert(std::wcout.rdbuf());
+#endif
+
+  // Switch back to synchronized mode and check that the state is still there.
+  std::ios_base::sync_with_stdio(true);
+
+  assert(std::cout.iword(index) == 22);
+  assert(std::cout.pword(index) == &dummy);
+  assert((std::cout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::cout.precision() == 7);
+  assert(std::cout.fill() == '*');
+  assert(std::cin.tie() == &std::cout);
+  assert(std::cerr.tie() == &std::cout);
+  assert(std::cerr.flags() & std::ios_base::unitbuf);
+  assert(std::cout.good());
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert(std::wcout.iword(index) == 22);
+  assert(std::wcout.pword(index) == &dummy);
+  assert((std::wcout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::wcout.precision() == 7);
+  assert(std::wcout.fill() == L'*');
+  assert(std::wcin.tie() == &std::wcout);
+  assert(std::wcerr.tie() == &std::wcout);
+  assert(std::wcerr.flags() & std::ios_base::unitbuf);
+  assert(std::wcout.good());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..2a65be4863bf5
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// wostream wcerr;
+
+// Unlike wcin/wcout/wclog, std::wcerr is still unbuffered even when sync_with_stdio(false)
+// is called. This test ensures that it produces correct output after sync_with_stdio(false).
+// We can't check interleaving with C stdio functions here since C stdio uses narrow characters.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios_base::sync_with_stdio(false));
+
+  std::wcerr << L"12" << 34;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
new file mode 100644
index 0000000000000..57113e21535e3
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// wistream wcin;
+
+// Read an input that is much larger than any reasonable internal buffer so that the
+// buffer has to be refilled (underflow) several times, and check that every value
+// is read correctly and that EOF is detected at the end.
+
+// RUN: seq 1 5000 > %t.input
+
+// RUN: %{build} -DSYNC_WITH_STDIO_FALSE
+// RUN: %{exec} %t.exe < %t.input
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+#ifdef SYNC_WITH_STDIO_FALSE
+  assert(std::ios_base::sync_with_stdio(false));
+#endif
+
+  long sum  = 0;
+  int count = 0;
+  int value = 0;
+  while (std::wcin >> value) {
+    sum += value;
+    ++count;
+  }
+
+  assert(count == 5000);
+  assert(sum == 5000L * 5001L / 2L); // sum of 1..5000
+  assert(std::wcin.eof());
+  assert(!std::wcin.good());
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
index 0ec24b77560e5..3051f0dc3b1cb 100644
--- a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
+++ b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
@@ -17,6 +17,8 @@
 
 int main(int, char**)
 {
+    ASSERT_SAME_TYPE(decltype(std::ios_base::sync_with_stdio(false)), bool);
+
     assert( std::ios_base::sync_with_stdio(false));
     assert(!std::ios_base::sync_with_stdio(false));
     assert(!std::ios_base::sync_with_stdio(true));

>From c0ca1b7c7a0ea24cd519c38f48f46f1271783f28 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 21 Jul 2026 16:00:25 -0400
Subject: [PATCH 3/3] Abi changelog

---
 libcxx/lib/abi/CHANGELOG.TXT | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index 567c23717ed60..cb29de0942478 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -18,11 +18,6 @@ Version 24.0
 
 * [libc++] Optimize standard streams with sync_with_stdio(false)
 
-  This updates the standard streams so that the buffers are replaced with `basic_filebuf`s.
-
-  Fixes #21566
-
-
   All platforms
   -------------
   Symbol added: _ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE


From libcxx-commits at lists.llvm.org  Tue Jul 21 13:55:20 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Tue, 21 Jul 2026 13:55:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Granularize `<optional>` (PR
	#206644)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206644@github.com>
Message-ID: <6a5fdcb8.160324b0.17e275.700b@mx.google.com>


https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/206644

>From e41c4724ed356f1a4f1c96c0cace13684695f5cb Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 12 Jun 2026 23:22:27 -0400
Subject: [PATCH 01/11] Granularize optional

---
 libcxx/include/CMakeLists.txt                 |    8 +
 libcxx/include/__fwd/optional.h               |   36 +
 libcxx/include/__optional/common.h            |   97 ++
 libcxx/include/__optional/comparison.h        |  347 ++++
 libcxx/include/__optional/hash.h              |   48 +
 libcxx/include/__optional/nullopt_t.h         |   40 +
 libcxx/include/__optional/optional.h          |  761 +++++++++
 libcxx/include/__optional/optional_ref.h      |  355 ++++
 libcxx/include/__optional/swap.h              |   45 +
 libcxx/include/module.modulemap.in            |   19 +
 libcxx/include/optional                       | 1432 +----------------
 .../ostream_joiner.op.assign.pass.cpp         |    1 +
 .../derive.pass.cpp                           |    1 +
 .../optional.object.ctor/ctor.verify.cpp      |   24 +-
 .../optional.object.ctor/deduct.verify.cpp    |    2 +-
 ...al_requires_destructible_object.verify.cpp |   26 +-
 16 files changed, 1800 insertions(+), 1442 deletions(-)
 create mode 100644 libcxx/include/__fwd/optional.h
 create mode 100644 libcxx/include/__optional/common.h
 create mode 100644 libcxx/include/__optional/comparison.h
 create mode 100644 libcxx/include/__optional/hash.h
 create mode 100644 libcxx/include/__optional/nullopt_t.h
 create mode 100644 libcxx/include/__optional/optional.h
 create mode 100644 libcxx/include/__optional/optional_ref.h
 create mode 100644 libcxx/include/__optional/swap.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index cec9ba06a7a6d..021d392e50c78 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -468,6 +468,7 @@ set(files
   __fwd/mdspan.h
   __fwd/memory.h
   __fwd/memory_resource.h
+  __fwd/optional.h
   __fwd/ostream.h
   __fwd/pair.h
   __fwd/queue.h
@@ -661,6 +662,13 @@ set(files
   __numeric/transform_exclusive_scan.h
   __numeric/transform_inclusive_scan.h
   __numeric/transform_reduce.h
+  __optional/common.h
+  __optional/comparison.h
+  __optional/hash.h
+  __optional/nullopt_t.h
+  __optional/optional.h
+  __optional/optional_ref.h
+  __optional/swap.h
   __ostream/basic_ostream.h
   __ostream/print.h
   __ostream/put_character_sequence.h
diff --git a/libcxx/include/__fwd/optional.h b/libcxx/include/__fwd/optional.h
new file mode 100644
index 0000000000000..35a3d2ea11e74
--- /dev/null
+++ b/libcxx/include/__fwd/optional.h
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// std::optional<T&>
+
+#ifndef _LIBCPP___FWD_OPTIONAL_H
+#define _LIBCPP___FWD_OPTIONAL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+class optional;
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___FWD_OPTIONAL_H
diff --git a/libcxx/include/__optional/common.h b/libcxx/include/__optional/common.h
new file mode 100644
index 0000000000000..2b4862cfd9d34
--- /dev/null
+++ b/libcxx/include/__optional/common.h
@@ -0,0 +1,97 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_COMMON_H
+#define _LIBCPP_OPTIONAL_COMMON_H
+
+#include <__assert>
+#include <__config>
+#include <__exception/exception.h>
+#include <__fwd/format.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/enable_view.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+
+#include <__fwd/optional.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+namespace std // purposefully not using versioning namespace
+{
+
+class _LIBCPP_EXPORTED_FROM_ABI bad_optional_access : public exception {
+public:
+  bad_optional_access() _NOEXCEPT                                      = default;
+  bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
+  bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
+  // Get the key function ~bad_optional_access() into the dylib
+  ~bad_optional_access() _NOEXCEPT override;
+  [[__nodiscard__]] const char* what() const _NOEXCEPT override;
+};
+
+} // namespace std
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+[[noreturn]] inline void __throw_bad_optional_access() {
+#  if _LIBCPP_HAS_EXCEPTIONS
+  throw bad_optional_access();
+#  else
+  _LIBCPP_VERBOSE_ABORT("bad_optional_access was thrown in -fno-exceptions mode");
+#  endif
+}
+
+struct __optional_construct_from_invoke_tag {};
+
+template <class _Tp>
+struct __is_std_optional : false_type {};
+
+template <class _Tp>
+struct __is_std_optional<optional<_Tp>> : true_type {};
+
+template<class _Tp, bool = is_reference_v<_Tp>>
+struct __optional_storage_base;
+
+#    if _LIBCPP_STD_VER < 26
+template <class _Tp>
+inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
+#    else
+template <class _Tp>
+inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp> || is_lvalue_reference_v<_Tp>;
+#    endif
+
+#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+constexpr bool ranges::enable_view<optional<_Tp>> = true;
+
+template <class _Tp>
+constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
+
+template <class _Tp>
+constexpr bool ranges::enable_borrowed_range<optional<_Tp&>> = true;
+
+#    endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif
diff --git a/libcxx/include/__optional/comparison.h b/libcxx/include/__optional/comparison.h
new file mode 100644
index 0000000000000..10c109fac7605
--- /dev/null
+++ b/libcxx/include/__optional/comparison.h
@@ -0,0 +1,347 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_COMPARISON_H
+#define _LIBCPP_OPTIONAL_COMPARISON_H
+
+#include <__compare/compare_three_way_result.h>
+#include <__compare/ordering.h>
+#include <__compare/three_way_comparable.h>
+#include <__config>
+#include <__optional/nullopt_t.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_core_convertible.h>
+#include <__type_traits/is_swappable.h>
+#include <__utility/declval.h>
+
+#include <__fwd/optional.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (static_cast<bool>(__x) != static_cast<bool>(__y))
+    return false;
+  if (!static_cast<bool>(__x))
+    return true;
+  return *__x == *__y;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (static_cast<bool>(__x) != static_cast<bool>(__y))
+    return true;
+  if (!static_cast<bool>(__x))
+    return false;
+  return *__x != *__y;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (!static_cast<bool>(__y))
+    return false;
+  if (!static_cast<bool>(__x))
+    return true;
+  return *__x < *__y;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (!static_cast<bool>(__x))
+    return false;
+  if (!static_cast<bool>(__y))
+    return true;
+  return *__x > *__y;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (!static_cast<bool>(__x))
+    return true;
+  if (!static_cast<bool>(__y))
+    return false;
+  return *__x <= *__y;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (!static_cast<bool>(__y))
+    return true;
+  if (!static_cast<bool>(__x))
+    return false;
+  return *__x >= *__y;
+}
+
+#  if _LIBCPP_STD_VER >= 20
+
+template <class _Tp, three_way_comparable_with<_Tp> _Up>
+_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
+operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) {
+  if (__x && __y)
+    return *__x <=> *__y;
+  return __x.has_value() <=> __y.has_value();
+}
+
+#  endif // _LIBCPP_STD_VER >= 20
+
+// [optional.nullops] Comparison with nullopt
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, std::nullopt_t) noexcept {
+  return !static_cast<bool>(__x);
+}
+
+#  if _LIBCPP_STD_VER <= 17
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(nullopt_t, const optional<_Tp>& __x) noexcept {
+  return !static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, nullopt_t) noexcept {
+  return static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(nullopt_t, const optional<_Tp>& __x) noexcept {
+  return static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>&, nullopt_t) noexcept {
+  return false;
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(nullopt_t, const optional<_Tp>& __x) noexcept {
+  return static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, nullopt_t) noexcept {
+  return !static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(nullopt_t, const optional<_Tp>&) noexcept {
+  return true;
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, nullopt_t) noexcept {
+  return static_cast<bool>(__x);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(nullopt_t, const optional<_Tp>&) noexcept {
+  return false;
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>&, nullopt_t) noexcept {
+  return true;
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(nullopt_t, const optional<_Tp>& __x) noexcept {
+  return !static_cast<bool>(__x);
+}
+
+#  else // _LIBCPP_STD_VER <= 17
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept {
+  return __x.has_value() <=> false;
+}
+
+#  endif // _LIBCPP_STD_VER <= 17
+
+// [optional.comp.with.t] Comparison with T
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x == __v;
+  return false;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v == *__x;
+  return false;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x != __v;
+  return true;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v != *__x;
+  return true;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x < __v;
+  return true;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v < *__x;
+  return false;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x <= __v;
+  return true;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v <= *__x;
+  return false;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x > __v;
+  return false;
+}
+
+template < class _Tp,
+           class _Up,
+           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
+                       int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v > *__x;
+  return true;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v) {
+  if (__x.has_value())
+    return *__x >= __v;
+  return false;
+}
+
+template <
+    class _Tp,
+    class _Up,
+    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
+                int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x) {
+  if (__x.has_value())
+    return __v >= *__x;
+  return true;
+}
+
+#  if _LIBCPP_STD_VER >= 20
+
+template <class _Tp>
+concept __is_derived_from_optional = requires(const _Tp& __t) { []<class _Up>(const optional<_Up>&) {}(__t); };
+
+template <class _Tp, class _Up>
+  requires(!__is_derived_from_optional<_Up>) && three_way_comparable_with<_Tp, _Up>
+_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
+operator<=>(const optional<_Tp>& __x, const _Up& __v) {
+  return __x.has_value() ? *__x <=> __v : strong_ordering::less;
+}
+
+#  endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif
diff --git a/libcxx/include/__optional/hash.h b/libcxx/include/__optional/hash.h
new file mode 100644
index 0000000000000..830f99b0b855f
--- /dev/null
+++ b/libcxx/include/__optional/hash.h
@@ -0,0 +1,48 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_HASH_H
+#define _LIBCPP_OPTIONAL_HASH_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__functional/hash.h>
+#include <__type_traits/remove_const.h>
+
+#include <__fwd/optional.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+struct hash< __enable_hash_helper<optional<_Tp>, remove_const_t<_Tp>> > {
+#  if _LIBCPP_STD_VER <= 17 || defined(_LIBCPP_ENABLE_CXX20_REMOVED_BINDER_TYPEDEFS)
+  _LIBCPP_DEPRECATED_IN_CXX17 typedef optional<_Tp> argument_type;
+  _LIBCPP_DEPRECATED_IN_CXX17 typedef size_t result_type;
+#  endif
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t operator()(const optional<_Tp>& __opt) const {
+    return static_cast<bool>(__opt) ? hash<remove_const_t<_Tp>>()(*__opt) : 0;
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+#endif // _LIBCPP_OPTIONAL_HASH_H
diff --git a/libcxx/include/__optional/nullopt_t.h b/libcxx/include/__optional/nullopt_t.h
new file mode 100644
index 0000000000000..bd66923f026b8
--- /dev/null
+++ b/libcxx/include/__optional/nullopt_t.h
@@ -0,0 +1,40 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_NULLOPT_T_H
+#define _LIBCPP_OPTIONAL_NULLOPT_T_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+struct nullopt_t {
+  struct __secret_tag {
+    explicit __secret_tag() = default;
+  };
+  constexpr explicit nullopt_t(__secret_tag, __secret_tag) noexcept {}
+};
+
+inline constexpr nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secret_tag{}};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+#endif
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
new file mode 100644
index 0000000000000..d01d8e661bdca
--- /dev/null
+++ b/libcxx/include/__optional/optional.h
@@ -0,0 +1,761 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// std::optional<T>
+
+#ifndef _LIBCPP_OPTIONAL_OPTIONAL_H
+#define _LIBCPP_OPTIONAL_OPTIONAL_H
+
+#include <__assert>
+#include <__concepts/invocable.h>
+#include <__config>
+#include <__functional/invoke.h>
+#include <__iterator/bounded_iter.h>
+#include <__iterator/capacity_aware_iterator.h>
+#include <__memory/addressof.h>
+#include <__memory/construct_at.h>
+#include <__tuple/sfinae_helpers.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/disjunction.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_assignable.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_destructible.h>
+#include <__type_traits/is_nothrow_assignable.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_scalar.h>
+#include <__type_traits/is_swappable.h>
+#include <__type_traits/is_trivially_assignable.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
+#include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+
+#include <initializer_list>
+
+#include <__fwd/optional.h>
+#include <__optional/common.h>
+#include <__optional/nullopt_t.h>
+
+#include <__optional/comparison.h>
+#include <__optional/hash.h>
+#include <__optional/swap.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp, bool = is_trivially_destructible<_Tp>::value>
+struct __optional_destruct_base;
+
+template <class _Tp>
+struct __optional_destruct_base<_Tp, false> {
+  typedef _Tp value_type;
+  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
+  union {
+    char __null_state_;
+    remove_cv_t<value_type> __val_;
+  };
+  bool __engaged_;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__optional_destruct_base() {
+    if (__engaged_)
+      __val_.~value_type();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
+      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
+
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Fp, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(
+      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
+      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
+#    endif
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
+    if (__engaged_) {
+      __val_.~value_type();
+      __engaged_ = false;
+    }
+  }
+};
+
+template <class _Tp>
+struct __optional_destruct_base<_Tp, true> {
+  typedef _Tp value_type;
+  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
+  union {
+    char __null_state_;
+    remove_cv_t<value_type> __val_;
+  };
+  bool __engaged_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
+      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
+
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Fp, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
+      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
+      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
+#    endif
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
+    if (__engaged_) {
+      __engaged_ = false;
+    }
+  }
+};
+
+template <class _Tp, bool>
+struct __optional_storage_base : __optional_destruct_base<_Tp> {
+  using __base _LIBCPP_NODEBUG = __optional_destruct_base<_Tp>;
+  using value_type             = _Tp;
+  using __base::__base;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() & noexcept { return this->__val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get() const& noexcept { return this->__val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type&& __get() && noexcept { return std::move(this->__val_); }
+  _LIBCPP_HIDE_FROM_ABI constexpr const value_type&& __get() const&& noexcept { return std::move(this->__val_); }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_Args&&... __args) {
+    _LIBCPP_ASSERT_INTERNAL(!has_value(), "__construct called for engaged __optional_storage");
+    std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
+    this->__engaged_ = true;
+  }
+
+  template <class _That>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
+    if (__opt.has_value())
+      __construct(std::forward<_That>(__opt).__get());
+  }
+
+  template <class _That>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
+    if (this->__engaged_ == __opt.has_value()) {
+      if (this->__engaged_)
+        static_cast<_Tp&>(this->__val_) = std::forward<_That>(__opt).__get();
+    } else {
+      if (this->__engaged_)
+        this->reset();
+      else
+        __construct(std::forward<_That>(__opt).__get());
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+  __swap(__optional_storage_base& __rhs) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) {
+    using std::swap;
+    if (this->has_value() == __rhs.has_value()) {
+      if (this->has_value())
+        swap(this->__get(), __rhs.__get());
+    } else {
+      if (this->has_value()) {
+        __rhs.__construct(std::move(this->__get()));
+        this->reset();
+      } else {
+        this->__construct(std::move(__rhs.__get()));
+        __rhs.reset();
+      }
+    }
+  }
+
+  // [optional.observe]
+  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp const> operator->() const noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
+    return std::addressof(this->__get());
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
+    return std::addressof(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return std::move(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return std::move(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return std::move(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return std::move(this->__get());
+  }
+};
+
+template <class _Tp, bool = is_trivially_copy_constructible_v<_Tp>>
+struct __optional_copy_base : __optional_storage_base<_Tp> {
+  using __optional_storage_base<_Tp>::__optional_storage_base;
+};
+
+template <class _Tp>
+struct __optional_copy_base<_Tp, false> : __optional_storage_base<_Tp> {
+  using __optional_storage_base<_Tp>::__optional_storage_base;
+
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_base() = default;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_base(const __optional_copy_base& __opt) {
+    this->__construct_from(__opt);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_base(__optional_copy_base&&)                 = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(const __optional_copy_base&) = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(__optional_copy_base&&)      = default;
+};
+
+template <class _Tp, bool = is_trivially_move_constructible_v<_Tp>>
+struct __optional_move_base : __optional_copy_base<_Tp> {
+  using __optional_copy_base<_Tp>::__optional_copy_base;
+};
+
+template <class _Tp>
+struct __optional_move_base<_Tp, false> : __optional_copy_base<_Tp> {
+  using value_type = _Tp;
+  using __optional_copy_base<_Tp>::__optional_copy_base;
+
+  _LIBCPP_HIDE_FROM_ABI __optional_move_base()                            = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_base(const __optional_move_base&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+  __optional_move_base(__optional_move_base&& __opt) noexcept(is_nothrow_move_constructible_v<value_type>) {
+    this->__construct_from(std::move(__opt));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(const __optional_move_base&) = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(__optional_move_base&&)      = default;
+};
+
+template <class _Tp,
+          bool = (is_trivially_destructible_v<_Tp> && is_trivially_copy_constructible_v<_Tp> &&
+                  is_trivially_copy_assignable_v<_Tp>)>
+struct __optional_copy_assign_base : __optional_move_base<_Tp> {
+  using __optional_move_base<_Tp>::__optional_move_base;
+};
+
+template <class _Tp>
+struct __optional_copy_assign_base<_Tp, false> : __optional_move_base<_Tp> {
+  using __optional_move_base<_Tp>::__optional_move_base;
+
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base()                                   = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(const __optional_copy_assign_base&) = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(__optional_copy_assign_base&&)      = default;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_assign_base&
+  operator=(const __optional_copy_assign_base& __opt) {
+    this->__assign_from(__opt);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base& operator=(__optional_copy_assign_base&&) = default;
+};
+
+template <class _Tp,
+          bool = (is_trivially_destructible_v<_Tp> && is_trivially_move_constructible_v<_Tp> &&
+                  is_trivially_move_assignable_v<_Tp>)>
+struct __optional_move_assign_base : __optional_copy_assign_base<_Tp> {
+  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
+};
+
+template <class _Tp>
+struct __optional_move_assign_base<_Tp, false> : __optional_copy_assign_base<_Tp> {
+  using value_type = _Tp;
+  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
+
+  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base()                                              = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(const __optional_move_assign_base& __opt)      = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(__optional_move_assign_base&&)                 = default;
+  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base& operator=(const __optional_move_assign_base&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_move_assign_base&
+  operator=(__optional_move_assign_base&& __opt) noexcept(
+      is_nothrow_move_assignable_v<value_type> && is_nothrow_move_constructible_v<value_type>) {
+    this->__assign_from(std::move(__opt));
+    return *this;
+  }
+};
+
+template <class _Tp>
+using __optional_sfinae_ctor_base_t _LIBCPP_NODEBUG =
+    __sfinae_ctor_base< is_copy_constructible<_Tp>::value, is_move_constructible<_Tp>::value >;
+
+template <class _Tp>
+using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
+    __sfinae_assign_base< (is_copy_constructible_v<_Tp> && is_copy_assignable_v<_Tp>),
+                          (is_move_constructible_v<_Tp> && is_move_assignable_v<_Tp>)>;
+
+template <class _Tp>
+struct __optional_iterator_base : __optional_move_assign_base<_Tp> {
+  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
+};
+
+#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+  requires is_object_v<_Tp>
+struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
+private:
+  using __pointer _LIBCPP_NODEBUG       = add_pointer_t<_Tp>;
+  using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const _Tp>;
+
+public:
+  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+  using iterator       = __bounded_iter<__pointer>;
+  using const_iterator = __bounded_iter<__const_pointer>;
+#        else
+  using iterator       = __capacity_aware_iterator<__pointer, optional<_Tp>, 1>;
+  using const_iterator = __capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>;
+#        endif
+
+  // [optional.iterators], iterator support
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
+    auto* __ptr = std::addressof(this->__get());
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
+#        else
+    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp>, 1>(__ptr);
+#        endif
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
+    auto* __ptr = std::addressof(this->__get());
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
+#        else
+    return std::__make_capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>(__ptr);
+#        endif
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
+    return begin() + (this->has_value() ? 1 : 0);
+  }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
+    return begin() + (this->has_value() ? 1 : 0);
+  }
+};
+
+#    endif   // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+class _LIBCPP_DECLSPEC_EMPTY_BASES optional
+    : public __optional_iterator_base<_Tp>,
+      private __optional_sfinae_ctor_base_t<_Tp>,
+      private __optional_sfinae_assign_base_t<_Tp> {
+  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp>;
+
+public:
+  using value_type = __libcpp_remove_reference_t<_Tp>;
+
+  using __trivially_relocatable _LIBCPP_NODEBUG =
+      conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
+
+private:
+  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
+  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
+#    if _LIBCPP_STD_VER >= 26
+  static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
+#    else
+  static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
+#    endif
+  static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
+  static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
+
+  // LWG2756: conditionally explicit conversion from _Up
+  struct _CheckOptionalArgsConstructor {
+    template <class _Up>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
+      return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>;
+    }
+
+    template <class _Up>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
+      return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>;
+    }
+  };
+  template <class _Up>
+  using _CheckOptionalArgsCtor _LIBCPP_NODEBUG =
+      _If< _IsNotSame<__remove_cvref_t<_Up>, in_place_t>::value && _IsNotSame<__remove_cvref_t<_Up>, optional>::value &&
+               (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_optional<__remove_cvref_t<_Up>>::value),
+           _CheckOptionalArgsConstructor,
+           __check_tuple_constructor_fail >;
+  template <class _QualUp>
+  struct _CheckOptionalLikeConstructor {
+    template <class _Up, class _Opt = optional<_Up>>
+    using __check_constructible_from_opt _LIBCPP_NODEBUG =
+        _Or< is_constructible<_Tp, _Opt&>,
+             is_constructible<_Tp, _Opt const&>,
+             is_constructible<_Tp, _Opt&&>,
+             is_constructible<_Tp, _Opt const&&>,
+             is_convertible<_Opt&, _Tp>,
+             is_convertible<_Opt const&, _Tp>,
+             is_convertible<_Opt&&, _Tp>,
+             is_convertible<_Opt const&&, _Tp> >;
+    template <class _Up, class _Opt = optional<_Up>>
+    using __check_assignable_from_opt _LIBCPP_NODEBUG =
+        _Or< is_assignable<_Tp&, _Opt&>,
+             is_assignable<_Tp&, _Opt const&>,
+             is_assignable<_Tp&, _Opt&&>,
+             is_assignable<_Tp&, _Opt const&&> >;
+    template <class _Up, class _QUp = _QualUp>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
+      return is_convertible<_QUp, _Tp>::value &&
+             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
+    }
+    template <class _Up, class _QUp = _QualUp>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
+      return !is_convertible<_QUp, _Tp>::value &&
+             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
+    }
+    template <class _Up, class _QUp = _QualUp>
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_assign() {
+      // Construction and assignability of _QUp to _Tp has already been
+      // checked.
+      return !__check_constructible_from_opt<_Up>::value && !__check_assignable_from_opt<_Up>::value;
+    }
+  };
+
+  template <class _Up, class _QualUp>
+  using _CheckOptionalLikeCtor _LIBCPP_NODEBUG =
+      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp> >::value,
+           _CheckOptionalLikeConstructor<_QualUp>,
+           __check_tuple_constructor_fail >;
+  template <class _Up, class _QualUp>
+  using _CheckOptionalLikeAssign _LIBCPP_NODEBUG =
+      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>, is_assignable<_Tp&, _QualUp> >::value,
+           _CheckOptionalLikeConstructor<_QualUp>,
+           __check_tuple_constructor_fail >;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr optional() noexcept {}
+  _LIBCPP_HIDE_FROM_ABI constexpr optional(const optional&) = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr optional(optional&&)      = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr optional(nullopt_t) noexcept {}
+
+  template <class _InPlaceT,
+            class... _Args,
+            enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, is_constructible<_Tp, _Args...>>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_InPlaceT, _Args&&... __args)
+      : __base(in_place, std::forward<_Args>(__args)...) {}
+
+  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
+      : __base(in_place, __il, std::forward<_Args>(__args)...) {}
+
+  template <class _Up = _Tp, enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
+      : __base(in_place, std::forward<_Up>(__v)) {}
+
+  template <class _Up                                                                        = remove_cv_t<_Tp>,
+            enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) noexcept(
+      is_nothrow_constructible_v<_Tp, _Up>) // strengthened
+      : __base(in_place, std::forward<_Up>(__v)) {}
+
+  // LWG2756: conditionally explicit conversion from const optional<_Up>&
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_implicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) {
+    this->__construct_from(__v);
+  }
+
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_explicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) {
+    this->__construct_from(__v);
+  }
+
+  // LWG2756: conditionally explicit conversion from optional<_Up>&&
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_implicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) {
+    this->__construct_from(std::move(__v));
+  }
+
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_explicit<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) {
+    this->__construct_from(std::move(__v));
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Tag,
+            class _Fp,
+            class... _Args,
+            enable_if_t<_IsSame<_Tag, __optional_construct_from_invoke_tag>::value, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
+      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
+#    endif
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(nullopt_t) noexcept {
+    reset();
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(const optional&) = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(optional&&)      = default;
+
+  // LWG2756
+  template <class _Up        = remove_cv_t<_Tp>,
+            enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>,
+                             _Or<_IsNotSame<__remove_cvref_t<_Up>, _Tp>, _Not<is_scalar<_Tp>>>,
+                             is_constructible<_Tp, _Up>,
+                             is_assignable<_Tp&, _Up>>::value,
+                        int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(_Up&& __v) {
+    if (this->has_value())
+      this->__get() = std::forward<_Up>(__v);
+    else
+      this->__construct(std::forward<_Up>(__v));
+    return *this;
+  }
+
+  // LWG2756
+  template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>::template __enable_assign<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(const optional<_Up>& __v) {
+    this->__assign_from(__v);
+    return *this;
+  }
+
+  // LWG2756
+  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_assign<_Up>(), int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(optional<_Up>&& __v) {
+    this->__assign_from(std::move(__v));
+    return *this;
+  }
+
+  template <class... _Args, enable_if_t<is_constructible_v<_Tp, _Args...>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) {
+    reset();
+    this->__construct(std::forward<_Args>(__args)...);
+    return this->__get();
+  }
+
+  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) {
+    reset();
+    this->__construct(__il, std::forward<_Args>(__args)...);
+    return this->__get();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+  swap(optional& __opt) noexcept((is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>)) {
+    this->__swap(__opt);
+  }
+
+  using __base::operator*;
+  using __base::operator->;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return has_value(); }
+
+  using __base::__get;
+  using __base::has_value;
+  using __base::value;
+
+  template <class _Up = remove_cv_t<_Tp>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
+    static_assert(is_copy_constructible_v<_Tp>, "optional<T>::value_or: T must be copy constructible");
+    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
+    return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v));
+  }
+
+  template <class _Up = remove_cv_t<_Tp>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
+    static_assert(is_move_constructible_v<_Tp>, "optional<T>::value_or: T must be move constructible");
+    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
+    return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v));
+  }
+
+#    if _LIBCPP_STD_VER >= 23
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
+    using _Up = invoke_result_t<_Func, _Tp&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(value()) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), value());
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
+    using _Up = invoke_result_t<_Func, const _Tp&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(value()) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), value());
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
+    using _Up = invoke_result_t<_Func, _Tp&&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(std::move(value())) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), std::move(value()));
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
+    using _Up = invoke_result_t<_Func, const _Tp&&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(std::move(value())) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), std::move(value()));
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & {
+    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
+    return optional<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& {
+    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
+    return optional<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && {
+    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
+    return optional<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& {
+    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
+    return optional<_Up>();
+  }
+
+  template <invocable _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const&
+    requires is_copy_constructible_v<_Tp>
+  {
+    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
+                  "Result of f() should be the same type as this optional");
+    if (*this)
+      return *this;
+    return std::forward<_Func>(__f)();
+  }
+
+  template <invocable _Func>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) &&
+    requires is_move_constructible_v<_Tp>
+  {
+    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
+                  "Result of f() should be the same type as this optional");
+    if (*this)
+      return std::move(*this);
+    return std::forward<_Func>(__f)();
+  }
+#    endif // _LIBCPP_STD_VER >= 23
+
+  using __base::reset;
+};
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_OPTIONAL_OPTIONAL_H
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
new file mode 100644
index 0000000000000..dd1adc1642ca3
--- /dev/null
+++ b/libcxx/include/__optional/optional_ref.h
@@ -0,0 +1,355 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// std::optional<T&>
+
+#ifndef _LIBCPP_OPTIONAL_OPTIONAL_REF_H
+#define _LIBCPP_OPTIONAL_OPTIONAL_REF_H
+
+#include <__assert>
+#include <__concepts/invocable.h>
+#include <__config>
+#include <__functional/invoke.h>
+#include <__iterator/bounded_iter.h>
+#include <__iterator/capacity_aware_iterator.h>
+#include <__memory/addressof.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/reference_constructs_from_temporary.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <__utility/swap.h>
+
+#include <__fwd/optional.h>
+#include <__optional/comparison.h>
+#include <__optional/swap.h>
+
+#include <__optional/common.h>
+#include <__optional/hash.h>
+#include <__optional/nullopt_t.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 26
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+struct __optional_storage_base<_Tp, true> {
+  using value_type                 = _Tp;
+  using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
+  __raw_type* __value_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
+
+  template <class _Up>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
+    _Tp& __r(std::forward<_Up>(__val));
+    __value_ = std::addressof(__r);
+  }
+
+  template <class _UArg>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) {
+    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
+                  "Attempted to construct a reference element in optional from a "
+                  "possible temporary");
+    __convert_init_ref_val(std::forward<_UArg>(__uarg));
+  }
+
+  template <class _Fp, class... _Args>
+  constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
+    __convert_init_ref_val(std::forward<invoke_result_t<_Fp, _Args...>>(
+        std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept { __value_ = nullptr; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const noexcept { return *__value_; }
+
+  template <class _UArg>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_UArg&& __val) {
+    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
+                  "Attempted to construct a reference element in tuple from a "
+                  "possible temporary");
+    __convert_init_ref_val(std::forward<_UArg>(__val));
+  }
+
+  template <class _That>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
+    if (__opt.has_value())
+      __construct(std::forward<_That>(__opt).__get());
+  }
+
+  template <class _That>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
+    if (has_value() == __opt.has_value()) {
+      if (has_value())
+        *__value_ = std::forward<_That>(__opt).__get();
+    } else {
+      if (has_value())
+        reset();
+      else
+        __construct(std::forward<_That>(__opt).__get());
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept {
+    std::swap(__value_, __rhs.__value_);
+  }
+
+  // [optional.ref.observe]
+  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() const noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
+    return std::addressof(this->__get());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() const noexcept {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
+    return this->__get();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() const {
+    if (!this->has_value())
+      std::__throw_bad_optional_access();
+    return this->__get();
+  }
+};
+
+template <class _Tp>
+struct __optional_iterator_base;
+
+template<class _Tp>
+struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
+  using __optional_storage_base<_Tp&>::__optional_storage_base;
+};
+
+#if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+
+template <class _Tp>
+  requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
+struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
+private:
+  using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
+
+public:
+  using __optional_storage_base<_Tp&>::__optional_storage_base;
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+  using iterator = __bounded_iter<__pointer>;
+#        else
+  using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
+#        endif
+
+  // [optional.ref.iterators], iterator support
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
+    auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
+
+#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
+#        else
+    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
+#        endif
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
+    return begin() + (this->has_value() ? 1 : 0);
+  }
+};
+
+#endif
+
+template <class _Tp>
+class optional<_Tp&> : public __optional_iterator_base<_Tp&> {
+  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp&>;
+
+  template <class _Up, class _QualUp>
+  static constexpr bool __check_optionalU_ctor =
+      !is_same_v<remove_cv_t<_Tp>, optional<_Up>> && !is_same_v<_Tp&, _Up> && is_constructible_v<_Tp&, _QualUp>;
+  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
+  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
+
+public:
+  using value_type = _Tp;
+
+  constexpr optional() noexcept = default;
+  constexpr optional(nullopt_t) noexcept {}
+  constexpr optional(const optional&) noexcept = default;
+
+  template <class _Arg>
+    requires(is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>)
+  constexpr explicit optional(in_place_t, _Arg&& __arg) : __base(in_place, std::forward<_Arg>(__arg)) {}
+
+  template <class _Up>
+    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
+             is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr explicit(!is_convertible_v<_Up, _Tp&>) optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>)
+      : __base(in_place, std::forward<_Up>(__v)) {}
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, _Up&> && !reference_constructs_from_temporary_v<_Tp&, _Up&>)
+  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
+      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) {
+    this->__construct_from(__rhs);
+  }
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, const _Up&> && !reference_constructs_from_temporary_v<_Tp&, const _Up&>)
+  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
+      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) {
+    this->__construct_from(__rhs);
+  }
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
+      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
+    this->__construct_from(std::move(__rhs));
+  }
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, const _Up> && !reference_constructs_from_temporary_v<_Tp&, const _Up>)
+  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
+      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) {
+    this->__construct_from(std::move(__rhs));
+  }
+
+  template <class _Tag, class _Fp, class... _Args>
+    requires(is_same_v<_Tag, __optional_construct_from_invoke_tag>)
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
+      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
+
+  // deleted overloads
+
+  template <class _Up>
+    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
+             is_constructible_v<_Tp&, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
+      optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, _Up&> && reference_constructs_from_temporary_v<_Tp&, _Up&>)
+  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
+      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) = delete;
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, const _Up&> && reference_constructs_from_temporary_v<_Tp&, const _Up&>)
+  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
+      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) = delete;
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
+      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
+
+  template <class _Up>
+    requires(__check_optionalU_ctor<_Up, const _Up> && reference_constructs_from_temporary_v<_Tp&, const _Up>)
+  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
+      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) = delete;
+
+  constexpr ~optional() = default;
+
+  using __base::__get;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(nullopt_t) noexcept {
+    reset();
+    return *this;
+  }
+
+  constexpr optional& operator=(const optional&) noexcept = default;
+
+  template <class _Up>
+    requires(is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
+  constexpr _Tp& emplace(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
+    this->__construct(std::forward<_Up>(__u));
+
+    return this->__get();
+  }
+
+  constexpr void swap(optional& __rhs) noexcept { this->__swap(__rhs); }
+
+  using __base::operator->;
+  using __base::operator*;
+
+  constexpr explicit operator bool() const noexcept { return has_value(); }
+
+  using __base::has_value;
+  using __base::value;
+
+  template <class _Up = remove_cv_t<_Tp>>
+    requires(!is_array_v<_Tp> && is_object_v<_Tp>)
+  [[nodiscard]] constexpr decay_t<_Tp> value_or(_Up&& __v) const {
+    using _XTp = remove_cv_t<_Tp>;
+    static_assert(is_constructible_v<_XTp, _Tp&>, "optional<T&>::value_or: remove_cv_t<T> must be constructible");
+    static_assert(is_convertible_v<_Up, _XTp>, "optional<T&>::value_or: U must be convertible to remove_cv_t<T>");
+    return this->has_value() ? this->__get() : static_cast<_XTp>(std::forward<_Up>(__v));
+  }
+
+  template <class _Func>
+  [[nodiscard]] constexpr auto and_then(_Func&& __f) const {
+    using _Up = invoke_result_t<_Func, _Tp&>;
+    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+                  "Result of f(value()) must be a specialization of std::optional");
+    if (*this)
+      return std::invoke(std::forward<_Func>(__f), value());
+    return remove_cvref_t<_Up>();
+  }
+
+  template <class _Func>
+  [[nodiscard]] constexpr optional<remove_cv_t<invoke_result_t<_Func, _Tp&>>> transform(_Func&& __f) const {
+    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
+    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
+    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
+    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
+    static_assert(
+        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
+
+    if (*this)
+      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
+    return optional<_Up>();
+  }
+
+  template <invocable _Func>
+  [[nodiscard]] constexpr optional or_else(_Func&& __f) const {
+    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
+                  "Result of f() should be the same type as this optional");
+    if (*this)
+      return *this;
+    return std::forward<_Func>(__f)();
+  }
+
+  using __base::reset;
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_OPTIONAL_OPTIONAL_REF_H
diff --git a/libcxx/include/__optional/swap.h b/libcxx/include/__optional/swap.h
new file mode 100644
index 0000000000000..d1cec68c555fe
--- /dev/null
+++ b/libcxx/include/__optional/swap.h
@@ -0,0 +1,45 @@
+// -*- C++ -*-Expand commentComment on line R1Resolved
+//===----------------------------------------------------------------------===//
+//
+// 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_OPTIONAL_SWAP_H
+#define _LIBCPP_OPTIONAL_SWAP_H
+
+#include <__config>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_swappable.h>
+
+#include <__fwd/optional.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp,
+          enable_if_t<(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>)
+#  if _LIBCPP_STD_VER >= 26
+                          || is_reference_v<_Tp>
+#  endif
+                      ,
+                      int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) {
+  __x.swap(__y);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+
+#endif
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b9ddfd5bbf4cd..e0c40bad3bae9 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1796,6 +1796,25 @@ module std [system] {
   }
 
   module optional {
+    module fwd { header "__fwd/optional.h" }
+    module hash {
+      header "__optional/hash.h"
+      export std.functional.hash
+    }
+    module common {
+      header "__optional/common.h"
+      export std.iterator.bounded_iter
+      export std.iterator.capacity_aware_iterator
+      export std.format.fwd
+      export std.utility.swap
+    }
+
+    module comparison { header "__optional/comparison.h" }
+    module nullopt_t { header "__optional/nullopt_t.h" }
+    module optional { header "__optional/optional.h" }
+    module optional_ref { header "__optional/optional_ref.h" }
+    module swap { header "__optional/swap.h" }
+
     header "optional"
     export *
   }
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 2499479348892..2b40706910018 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -258,68 +258,34 @@ namespace std {
 #if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 #  include <__cxx03/__config>
 #else
-#  include <__assert>
-#  include <__compare/compare_three_way_result.h>
-#  include <__compare/ordering.h>
-#  include <__compare/three_way_comparable.h>
-#  include <__concepts/invocable.h>
 #  include <__config>
-#  include <__exception/exception.h>
-#  include <__functional/hash.h>
-#  include <__functional/invoke.h>
-#  include <__functional/unary_function.h>
-#  include <__fwd/format.h>
-#  include <__fwd/functional.h>
-#  include <__iterator/bounded_iter.h>
-#  include <__iterator/capacity_aware_iterator.h>
-#  include <__memory/addressof.h>
-#  include <__memory/construct_at.h>
-#  include <__ranges/enable_borrowed_range.h>
-#  include <__ranges/enable_view.h>
-#  include <__tuple/sfinae_helpers.h>
-#  include <__type_traits/add_pointer.h>
-#  include <__type_traits/conditional.h>
-#  include <__type_traits/conjunction.h>
 #  include <__type_traits/decay.h>
-#  include <__type_traits/disjunction.h>
 #  include <__type_traits/enable_if.h>
-#  include <__type_traits/integral_constant.h>
-#  include <__type_traits/invoke.h>
-#  include <__type_traits/is_array.h>
-#  include <__type_traits/is_assignable.h>
 #  include <__type_traits/is_constructible.h>
-#  include <__type_traits/is_convertible.h>
-#  include <__type_traits/is_core_convertible.h>
-#  include <__type_traits/is_destructible.h>
-#  include <__type_traits/is_function.h>
-#  include <__type_traits/is_nothrow_assignable.h>
-#  include <__type_traits/is_nothrow_constructible.h>
-#  include <__type_traits/is_object.h>
-#  include <__type_traits/is_reference.h>
-#  include <__type_traits/is_same.h>
-#  include <__type_traits/is_scalar.h>
-#  include <__type_traits/is_swappable.h>
-#  include <__type_traits/is_trivially_assignable.h>
-#  include <__type_traits/is_trivially_constructible.h>
-#  include <__type_traits/is_trivially_destructible.h>
-#  include <__type_traits/is_trivially_relocatable.h>
-#  include <__type_traits/negation.h>
 #  include <__type_traits/reference_constructs_from_temporary.h>
 #  include <__type_traits/remove_const.h>
-#  include <__type_traits/remove_cv.h>
-#  include <__type_traits/remove_cvref.h>
-#  include <__type_traits/remove_reference.h>
-#  include <__utility/declval.h>
 #  include <__utility/forward.h>
 #  include <__utility/in_place.h>
-#  include <__utility/move.h>
-#  include <__utility/swap.h>
-#  include <__verbose_abort>
 #  include <initializer_list>
 #  include <version>
 
-// standard-mandated includes
+#  include <__optional/common.h>
+#  include <__optional/comparison.h>
+#  include <__optional/hash.h>
+#  include <__optional/optional.h>
+#  include <__optional/optional_ref.h>
+#  include <__optional/swap.h>
 
+// TODO: Certain headers rely on these transitive includes
+#  include <__type_traits/conditional.h>
+#  include <__type_traits/invoke.h>
+#  include <__type_traits/is_object.h>
+#  include <__type_traits/is_reference.h>
+#  include <__type_traits/remove_cvref.h>
+#  include <__utility/declval.h>
+#  include <__utility/move.h>
+
+// standard-mandated includes
 // [optional.syn]
 #  include <compare>
 
@@ -330,1367 +296,13 @@ namespace std {
 _LIBCPP_PUSH_MACROS
 #  include <__undef_macros>
 
-namespace std // purposefully not using versioning namespace
-{
-
-class _LIBCPP_EXPORTED_FROM_ABI bad_optional_access : public exception {
-public:
-  _LIBCPP_HIDE_FROM_ABI bad_optional_access() _NOEXCEPT                                      = default;
-  _LIBCPP_HIDE_FROM_ABI bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
-  _LIBCPP_HIDE_FROM_ABI bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
-  // Get the key function ~bad_optional_access() into the dylib
-  ~bad_optional_access() _NOEXCEPT override;
-  [[__nodiscard__]] const char* what() const _NOEXCEPT override;
-};
-
-} // namespace std
-
 #  if _LIBCPP_STD_VER >= 17
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-[[noreturn]] inline _LIBCPP_HIDE_FROM_ABI void __throw_bad_optional_access() {
-#    if _LIBCPP_HAS_EXCEPTIONS
-  throw bad_optional_access();
-#    else
-  _LIBCPP_VERBOSE_ABORT("bad_optional_access was thrown in -fno-exceptions mode");
-#    endif
-}
-
-struct nullopt_t {
-  struct __secret_tag {
-    explicit __secret_tag() = default;
-  };
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit nullopt_t(__secret_tag, __secret_tag) noexcept {}
-};
-
-inline constexpr nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secret_tag{}};
-
-struct __optional_construct_from_invoke_tag {};
-
-template <class _Tp, bool = is_trivially_destructible<_Tp>::value>
-struct __optional_destruct_base;
-
-template <class _Tp>
-struct __optional_destruct_base<_Tp, false> {
-  typedef _Tp value_type;
-  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
-  union {
-    char __null_state_;
-    remove_cv_t<value_type> __val_;
-  };
-  bool __engaged_;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__optional_destruct_base() {
-    if (__engaged_)
-      __val_.~value_type();
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
-      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Fp, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(
-      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
-      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
-    if (__engaged_) {
-      __val_.~value_type();
-      __engaged_ = false;
-    }
-  }
-};
-
-template <class _Tp>
-struct __optional_destruct_base<_Tp, true> {
-  typedef _Tp value_type;
-  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
-  union {
-    char __null_state_;
-    remove_cv_t<value_type> __val_;
-  };
-  bool __engaged_;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __null_state_(), __engaged_(false) {}
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
-      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Fp, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
-      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
-      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
-    if (__engaged_) {
-      __engaged_ = false;
-    }
-  }
-};
-
-template <class _Tp, bool = is_reference<_Tp>::value>
-struct __optional_storage_base : __optional_destruct_base<_Tp> {
-  using __base _LIBCPP_NODEBUG = __optional_destruct_base<_Tp>;
-  using value_type             = _Tp;
-  using __base::__base;
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() & noexcept { return this->__val_; }
-  _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get() const& noexcept { return this->__val_; }
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type&& __get() && noexcept { return std::move(this->__val_); }
-  _LIBCPP_HIDE_FROM_ABI constexpr const value_type&& __get() const&& noexcept { return std::move(this->__val_); }
-
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_Args&&... __args) {
-    _LIBCPP_ASSERT_INTERNAL(!has_value(), "__construct called for engaged __optional_storage");
-    std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
-    this->__engaged_ = true;
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
-    if (__opt.has_value())
-      __construct(std::forward<_That>(__opt).__get());
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
-    if (this->__engaged_ == __opt.has_value()) {
-      if (this->__engaged_)
-        static_cast<_Tp&>(this->__val_) = std::forward<_That>(__opt).__get();
-    } else {
-      if (this->__engaged_)
-        this->reset();
-      else
-        __construct(std::forward<_That>(__opt).__get());
-    }
-  }
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-  __swap(__optional_storage_base& __rhs) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) {
-    using std::swap;
-    if (this->has_value() == __rhs.has_value()) {
-      if (this->has_value())
-        swap(this->__get(), __rhs.__get());
-    } else {
-      if (this->has_value()) {
-        __rhs.__construct(std::move(this->__get()));
-        this->reset();
-      } else {
-        this->__construct(std::move(__rhs.__get()));
-        __rhs.reset();
-      }
-    }
-  }
-
-  // [optional.observe]
-  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp const> operator->() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return std::move(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return std::move(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return std::move(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return std::move(this->__get());
-  }
-};
-
-template <class _Tp>
-struct __optional_storage_base<_Tp, true> {
-  using value_type                 = _Tp;
-  using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
-  __raw_type* __value_;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
-
-  template <class _Up>
-  _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
-    _Tp& __r(std::forward<_Up>(__val));
-    __value_ = std::addressof(__r);
-  }
-
-  template <class _UArg>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) {
-    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
-                  "Attempted to construct a reference element in optional from a "
-                  "possible temporary");
-    __convert_init_ref_val(std::forward<_UArg>(__uarg));
-  }
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Fp, class... _Args>
-  constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
-    __convert_init_ref_val(std::forward<invoke_result_t<_Fp, _Args...>>(
-        std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)));
-  }
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept { __value_ = nullptr; }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const noexcept { return *__value_; }
-
-  template <class _UArg>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_UArg&& __val) {
-    static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
-                  "Attempted to construct a reference element in tuple from a "
-                  "possible temporary");
-    __convert_init_ref_val(std::forward<_UArg>(__val));
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_That&& __opt) {
-    if (__opt.has_value())
-      __construct(std::forward<_That>(__opt).__get());
-  }
-
-  template <class _That>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
-    if (has_value() == __opt.has_value()) {
-      if (has_value())
-        *__value_ = std::forward<_That>(__opt).__get();
-    } else {
-      if (has_value())
-        reset();
-      else
-        __construct(std::forward<_That>(__opt).__get());
-    }
-  }
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept {
-    std::swap(__value_, __rhs.__value_);
-  }
-
-  // [optional.ref.observe]
-  _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value");
-    return std::addressof(this->__get());
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
-    return this->__get();
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() const {
-    if (!this->has_value())
-      std::__throw_bad_optional_access();
-    return this->__get();
-  }
-};
-
-template <class _Tp, bool = is_trivially_copy_constructible_v<_Tp>>
-struct __optional_copy_base : __optional_storage_base<_Tp> {
-  using __optional_storage_base<_Tp>::__optional_storage_base;
-};
-
-template <class _Tp>
-struct __optional_copy_base<_Tp, false> : __optional_storage_base<_Tp> {
-  using __optional_storage_base<_Tp>::__optional_storage_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base() = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_base(const __optional_copy_base& __opt) {
-    this->__construct_from(__opt);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base(__optional_copy_base&&)                 = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(const __optional_copy_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_base& operator=(__optional_copy_base&&)      = default;
-};
-
-template <class _Tp, bool = is_trivially_move_constructible_v<_Tp>>
-struct __optional_move_base : __optional_copy_base<_Tp> {
-  using __optional_copy_base<_Tp>::__optional_copy_base;
-};
-
-template <class _Tp>
-struct __optional_move_base<_Tp, false> : __optional_copy_base<_Tp> {
-  using value_type = _Tp;
-  using __optional_copy_base<_Tp>::__optional_copy_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base()                            = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base(const __optional_move_base&) = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
-  __optional_move_base(__optional_move_base&& __opt) noexcept(is_nothrow_move_constructible_v<value_type>) {
-    this->__construct_from(std::move(__opt));
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(const __optional_move_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_base& operator=(__optional_move_base&&)      = default;
-};
-
-template <class _Tp,
-          bool = (is_trivially_destructible_v<_Tp> && is_trivially_copy_constructible_v<_Tp> &&
-                  is_trivially_copy_assignable_v<_Tp>)>
-struct __optional_copy_assign_base : __optional_move_base<_Tp> {
-  using __optional_move_base<_Tp>::__optional_move_base;
-};
-
-template <class _Tp>
-struct __optional_copy_assign_base<_Tp, false> : __optional_move_base<_Tp> {
-  using __optional_move_base<_Tp>::__optional_move_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base()                                   = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(const __optional_copy_assign_base&) = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base(__optional_copy_assign_base&&)      = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_copy_assign_base&
-  operator=(const __optional_copy_assign_base& __opt) {
-    this->__assign_from(__opt);
-    return *this;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI __optional_copy_assign_base& operator=(__optional_copy_assign_base&&) = default;
-};
-
-template <class _Tp,
-          bool = (is_trivially_destructible_v<_Tp> && is_trivially_move_constructible_v<_Tp> &&
-                  is_trivially_move_assignable_v<_Tp>)>
-struct __optional_move_assign_base : __optional_copy_assign_base<_Tp> {
-  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
-};
-
-template <class _Tp>
-struct __optional_move_assign_base<_Tp, false> : __optional_copy_assign_base<_Tp> {
-  using value_type = _Tp;
-  using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
-
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base()                                              = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(const __optional_move_assign_base& __opt)      = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base(__optional_move_assign_base&&)                 = default;
-  _LIBCPP_HIDE_FROM_ABI __optional_move_assign_base& operator=(const __optional_move_assign_base&) = default;
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __optional_move_assign_base&
-  operator=(__optional_move_assign_base&& __opt) noexcept(
-      is_nothrow_move_assignable_v<value_type> && is_nothrow_move_constructible_v<value_type>) {
-    this->__assign_from(std::move(__opt));
-    return *this;
-  }
-};
-
-template <class _Tp>
-using __optional_sfinae_ctor_base_t _LIBCPP_NODEBUG =
-    __sfinae_ctor_base< is_copy_constructible<_Tp>::value, is_move_constructible<_Tp>::value >;
-
-template <class _Tp>
-using __optional_sfinae_assign_base_t _LIBCPP_NODEBUG =
-    __sfinae_assign_base< (is_copy_constructible_v<_Tp> && is_copy_assignable_v<_Tp>),
-                          (is_move_constructible_v<_Tp> && is_move_assignable_v<_Tp>)>;
-
-template <class _Tp>
-class optional;
-
-#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-template <class _Tp>
-constexpr bool ranges::enable_view<optional<_Tp>> = true;
-
-template <class _Tp>
-constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
-
-template <class _Tp>
-constexpr bool ranges::enable_borrowed_range<optional<_Tp&>> = true;
-
-#    endif
-
-#    if _LIBCPP_STD_VER >= 20
-
-template <class _Tp>
-concept __is_derived_from_optional = requires(const _Tp& __t) { []<class _Up>(const optional<_Up>&) {}(__t); };
-
-#    endif // _LIBCPP_STD_VER >= 20
-
-template <class _Tp>
-struct __is_std_optional : false_type {};
-template <class _Tp>
-struct __is_std_optional<optional<_Tp>> : true_type {};
-
-#    if _LIBCPP_STD_VER < 26
-template <class _Tp>
-inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
-#    else
-template <class _Tp>
-inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp> || is_lvalue_reference_v<_Tp>;
-#    endif
-
-template <class _Tp>
-struct __optional_iterator_base : __optional_move_assign_base<_Tp> {
-  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
-};
-
-#    if _LIBCPP_STD_VER >= 26
-template <class _Tp>
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
-};
-
-#      if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-
-template <class _Tp>
-  requires is_object_v<_Tp>
-struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
-private:
-  using __pointer _LIBCPP_NODEBUG       = add_pointer_t<_Tp>;
-  using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const _Tp>;
-
-public:
-  using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-  using iterator       = __bounded_iter<__pointer>;
-  using const_iterator = __bounded_iter<__const_pointer>;
-#        else
-  using iterator       = __capacity_aware_iterator<__pointer, optional<_Tp>, 1>;
-  using const_iterator = __capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>;
-#        endif
-
-  // [optional.iterators], iterator support
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
-    auto* __ptr = std::addressof(this->__get());
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
-    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp>, 1>(__ptr);
-#        endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
-    auto* __ptr = std::addressof(this->__get());
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
-    return std::__make_capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>(__ptr);
-#        endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-};
-
-template <class _Tp>
-  requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
-private:
-  using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
-
-public:
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-  using iterator = __bounded_iter<__pointer>;
-#        else
-  using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
-#        endif
-
-  // [optional.ref.iterators], iterator support
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
-    auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
-
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
-    return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
-    return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
-#        endif
-  }
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
-    return begin() + (this->has_value() ? 1 : 0);
-  }
-};
-
-#      endif // _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
-#    endif   // _LIBCPP_STD_VER >= 26
-
-template <class _Tp>
-class _LIBCPP_DECLSPEC_EMPTY_BASES optional
-    : public __optional_iterator_base<_Tp>,
-      private __optional_sfinae_ctor_base_t<_Tp>,
-      private __optional_sfinae_assign_base_t<_Tp> {
-  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp>;
-
-public:
-  using value_type = __libcpp_remove_reference_t<_Tp>;
-
-  using __trivially_relocatable _LIBCPP_NODEBUG =
-      conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
-
-private:
-  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
-  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-#    if _LIBCPP_STD_VER >= 26
-  static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
-#    else
-  static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
-#    endif
-  static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
-  static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
-
-  // LWG2756: conditionally explicit conversion from _Up
-  struct _CheckOptionalArgsConstructor {
-    template <class _Up>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
-      return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>;
-    }
-
-    template <class _Up>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
-      return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>;
-    }
-  };
-  template <class _Up>
-  using _CheckOptionalArgsCtor _LIBCPP_NODEBUG =
-      _If< _IsNotSame<__remove_cvref_t<_Up>, in_place_t>::value && _IsNotSame<__remove_cvref_t<_Up>, optional>::value &&
-               (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_optional<__remove_cvref_t<_Up>>::value),
-           _CheckOptionalArgsConstructor,
-           __check_tuple_constructor_fail >;
-  template <class _QualUp>
-  struct _CheckOptionalLikeConstructor {
-    template <class _Up, class _Opt = optional<_Up>>
-    using __check_constructible_from_opt _LIBCPP_NODEBUG =
-        _Or< is_constructible<_Tp, _Opt&>,
-             is_constructible<_Tp, _Opt const&>,
-             is_constructible<_Tp, _Opt&&>,
-             is_constructible<_Tp, _Opt const&&>,
-             is_convertible<_Opt&, _Tp>,
-             is_convertible<_Opt const&, _Tp>,
-             is_convertible<_Opt&&, _Tp>,
-             is_convertible<_Opt const&&, _Tp> >;
-    template <class _Up, class _Opt = optional<_Up>>
-    using __check_assignable_from_opt _LIBCPP_NODEBUG =
-        _Or< is_assignable<_Tp&, _Opt&>,
-             is_assignable<_Tp&, _Opt const&>,
-             is_assignable<_Tp&, _Opt&&>,
-             is_assignable<_Tp&, _Opt const&&> >;
-    template <class _Up, class _QUp = _QualUp>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
-      return is_convertible<_QUp, _Tp>::value &&
-             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
-    }
-    template <class _Up, class _QUp = _QualUp>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
-      return !is_convertible<_QUp, _Tp>::value &&
-             (is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
-    }
-    template <class _Up, class _QUp = _QualUp>
-    _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_assign() {
-      // Construction and assignability of _QUp to _Tp has already been
-      // checked.
-      return !__check_constructible_from_opt<_Up>::value && !__check_assignable_from_opt<_Up>::value;
-    }
-  };
-
-  template <class _Up, class _QualUp>
-  using _CheckOptionalLikeCtor _LIBCPP_NODEBUG =
-      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp> >::value,
-           _CheckOptionalLikeConstructor<_QualUp>,
-           __check_tuple_constructor_fail >;
-  template <class _Up, class _QualUp>
-  using _CheckOptionalLikeAssign _LIBCPP_NODEBUG =
-      _If< _And< _IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>, is_assignable<_Tp&, _QualUp> >::value,
-           _CheckOptionalLikeConstructor<_QualUp>,
-           __check_tuple_constructor_fail >;
-
-public:
-  _LIBCPP_HIDE_FROM_ABI constexpr optional() noexcept {}
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(const optional&) = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(optional&&)      = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(nullopt_t) noexcept {}
-
-  template <class _InPlaceT,
-            class... _Args,
-            enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, is_constructible<_Tp, _Args...>>::value, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_InPlaceT, _Args&&... __args)
-      : __base(in_place, std::forward<_Args>(__args)...) {}
-
-  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
-      : __base(in_place, __il, std::forward<_Args>(__args)...) {}
-
-  template <class _Up = _Tp, enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
-      : __base(in_place, std::forward<_Up>(__v)) {}
-
-  template <class _Up                                                                        = remove_cv_t<_Tp>,
-            enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) noexcept(
-      is_nothrow_constructible_v<_Tp, _Up>) // strengthened
-      : __base(in_place, std::forward<_Up>(__v)) {}
-
-  // LWG2756: conditionally explicit conversion from const optional<_Up>&
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_implicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) {
-    this->__construct_from(__v);
-  }
-
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::template __enable_explicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) {
-    this->__construct_from(__v);
-  }
-
-  // LWG2756: conditionally explicit conversion from optional<_Up>&&
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_implicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) {
-    this->__construct_from(std::move(__v));
-  }
-
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_explicit<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) {
-    this->__construct_from(std::move(__v));
-  }
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Tag,
-            class _Fp,
-            class... _Args,
-            enable_if_t<_IsSame<_Tag, __optional_construct_from_invoke_tag>::value, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
-      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
-#    endif
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(nullopt_t) noexcept {
-    reset();
-    return *this;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(const optional&) = default;
-  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(optional&&)      = default;
-
-  // LWG2756
-  template <class _Up        = remove_cv_t<_Tp>,
-            enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>,
-                             _Or<_IsNotSame<__remove_cvref_t<_Up>, _Tp>, _Not<is_scalar<_Tp>>>,
-                             is_constructible<_Tp, _Up>,
-                             is_assignable<_Tp&, _Up>>::value,
-                        int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(_Up&& __v) {
-    if (this->has_value())
-      this->__get() = std::forward<_Up>(__v);
-    else
-      this->__construct(std::forward<_Up>(__v));
-    return *this;
-  }
-
-  // LWG2756
-  template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>::template __enable_assign<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(const optional<_Up>& __v) {
-    this->__assign_from(__v);
-    return *this;
-  }
-
-  // LWG2756
-  template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::template __enable_assign<_Up>(), int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(optional<_Up>&& __v) {
-    this->__assign_from(std::move(__v));
-    return *this;
-  }
-
-  template <class... _Args, enable_if_t<is_constructible_v<_Tp, _Args...>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) {
-    reset();
-    this->__construct(std::forward<_Args>(__args)...);
-    return this->__get();
-  }
-
-  template <class _Up, class... _Args, enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) {
-    reset();
-    this->__construct(__il, std::forward<_Args>(__args)...);
-    return this->__get();
-  }
-
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-  swap(optional& __opt) noexcept((is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>)) {
-    this->__swap(__opt);
-  }
-
-  using __base::operator*;
-  using __base::operator->;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return has_value(); }
-
-  using __base::__get;
-  using __base::has_value;
-  using __base::value;
-
-  template <class _Up = remove_cv_t<_Tp>>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
-    static_assert(is_copy_constructible_v<_Tp>, "optional<T>::value_or: T must be copy constructible");
-    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
-    return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v));
-  }
-
-  template <class _Up = remove_cv_t<_Tp>>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
-    static_assert(is_move_constructible_v<_Tp>, "optional<T>::value_or: T must be move constructible");
-    static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
-    return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v));
-  }
-
-#    if _LIBCPP_STD_VER >= 23
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
-    using _Up = invoke_result_t<_Func, _Tp&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), value());
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
-    using _Up = invoke_result_t<_Func, const _Tp&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), value());
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
-    using _Up = invoke_result_t<_Func, _Tp&&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(std::move(value())) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), std::move(value()));
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
-    using _Up = invoke_result_t<_Func, const _Tp&&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(std::move(value())) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), std::move(value()));
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & {
-    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
-    return optional<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& {
-    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
-    return optional<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && {
-    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
-    return optional<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& {
-    using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
-    return optional<_Up>();
-  }
-
-  template <invocable _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const&
-    requires is_copy_constructible_v<_Tp>
-  {
-    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
-                  "Result of f() should be the same type as this optional");
-    if (*this)
-      return *this;
-    return std::forward<_Func>(__f)();
-  }
-
-  template <invocable _Func>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) &&
-    requires is_move_constructible_v<_Tp>
-  {
-    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
-                  "Result of f() should be the same type as this optional");
-    if (*this)
-      return std::move(*this);
-    return std::forward<_Func>(__f)();
-  }
-#    endif // _LIBCPP_STD_VER >= 23
-
-  using __base::reset;
-};
-
-#    if _LIBCPP_STD_VER >= 26
-template <class _Tp>
-class optional<_Tp&> : public __optional_iterator_base<_Tp&> {
-  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp&>;
-
-  template <class _Up, class _QualUp>
-  static constexpr bool __check_optionalU_ctor =
-      !is_same_v<remove_cv_t<_Tp>, optional<_Up>> && !is_same_v<_Tp&, _Up> && is_constructible_v<_Tp&, _QualUp>;
-  static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
-  static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-
-public:
-  using value_type = _Tp;
-
-  constexpr optional() noexcept = default;
-  constexpr optional(nullopt_t) noexcept {}
-  constexpr optional(const optional&) noexcept = default;
-
-  template <class _Arg>
-    requires(is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>)
-  constexpr explicit optional(in_place_t, _Arg&& __arg) : __base(in_place, std::forward<_Arg>(__arg)) {}
-
-  template <class _Up>
-    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
-             is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>) optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>)
-      : __base(in_place, std::forward<_Up>(__v)) {}
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up&> && !reference_constructs_from_temporary_v<_Tp&, _Up&>)
-  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
-      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) {
-    this->__construct_from(__rhs);
-  }
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up&> && !reference_constructs_from_temporary_v<_Tp&, const _Up&>)
-  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
-      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) {
-    this->__construct_from(__rhs);
-  }
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
-      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
-    this->__construct_from(std::move(__rhs));
-  }
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up> && !reference_constructs_from_temporary_v<_Tp&, const _Up>)
-  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
-      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) {
-    this->__construct_from(std::move(__rhs));
-  }
-
-  template <class _Tag, class _Fp, class... _Args>
-    requires(is_same_v<_Tag, __optional_construct_from_invoke_tag>)
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
-      : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
-
-  // deleted overloads
-
-  template <class _Up>
-    requires(!is_same_v<remove_cvref_t<_Up>, optional> && !is_same_v<remove_cvref_t<_Up>, in_place_t> &&
-             is_constructible_v<_Tp&, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
-      optional(_Up&& __v) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up&> && reference_constructs_from_temporary_v<_Tp&, _Up&>)
-  constexpr explicit(!is_convertible_v<_Up&, _Tp&>)
-      optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up&> && reference_constructs_from_temporary_v<_Tp&, const _Up&>)
-  constexpr explicit(!is_convertible_v<const _Up&, _Tp&>)
-      optional(const optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up&>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, _Up> && reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr explicit(!is_convertible_v<_Up, _Tp&>)
-      optional(optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) = delete;
-
-  template <class _Up>
-    requires(__check_optionalU_ctor<_Up, const _Up> && reference_constructs_from_temporary_v<_Tp&, const _Up>)
-  constexpr explicit(!is_convertible_v<const _Up, _Tp&>)
-      optional(const optional<_Up>&& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) = delete;
-
-  constexpr ~optional() = default;
-
-  using __base::__get;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(nullopt_t) noexcept {
-    reset();
-    return *this;
-  }
-
-  constexpr optional& operator=(const optional&) noexcept = default;
-
-  template <class _Up>
-    requires(is_constructible_v<_Tp&, _Up> && !reference_constructs_from_temporary_v<_Tp&, _Up>)
-  constexpr _Tp& emplace(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp&, _Up>) {
-    this->__construct(std::forward<_Up>(__u));
-
-    return this->__get();
-  }
-
-  constexpr void swap(optional& __rhs) noexcept { this->__swap(__rhs); }
-
-  using __base::operator->;
-  using __base::operator*;
-
-  constexpr explicit operator bool() const noexcept { return has_value(); }
-
-  using __base::has_value;
-  using __base::value;
-
-  template <class _Up = remove_cv_t<_Tp>>
-    requires(!is_array_v<_Tp> && is_object_v<_Tp>)
-  [[nodiscard]] constexpr decay_t<_Tp> value_or(_Up&& __v) const {
-    using _XTp = remove_cv_t<_Tp>;
-    static_assert(is_constructible_v<_XTp, _Tp&>, "optional<T&>::value_or: remove_cv_t<T> must be constructible");
-    static_assert(is_convertible_v<_Up, _XTp>, "optional<T&>::value_or: U must be convertible to remove_cv_t<T>");
-    return this->has_value() ? this->__get() : static_cast<_XTp>(std::forward<_Up>(__v));
-  }
-
-  template <class _Func>
-  [[nodiscard]] constexpr auto and_then(_Func&& __f) const {
-    using _Up = invoke_result_t<_Func, _Tp&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
-    if (*this)
-      return std::invoke(std::forward<_Func>(__f), value());
-    return remove_cvref_t<_Up>();
-  }
-
-  template <class _Func>
-  [[nodiscard]] constexpr optional<remove_cv_t<invoke_result_t<_Func, _Tp&>>> transform(_Func&& __f) const {
-    using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
-    static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
-    static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
-    static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t");
-    static_assert(
-        __is_valid_optional_contained_type<_Up>, "Result of f(value()) should be a valid contained type for optional");
-
-    if (*this)
-      return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value());
-    return optional<_Up>();
-  }
-
-  template <invocable _Func>
-  [[nodiscard]] constexpr optional or_else(_Func&& __f) const {
-    static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
-                  "Result of f() should be the same type as this optional");
-    if (*this)
-      return *this;
-    return std::forward<_Func>(__f)();
-  }
-
-  using __base::reset;
-};
-#    endif
-
 template <class _Tp>
 optional(_Tp) -> optional<_Tp>;
 
-// [optional.relops] Relational operators
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (static_cast<bool>(__x) != static_cast<bool>(__y))
-    return false;
-  if (!static_cast<bool>(__x))
-    return true;
-  return *__x == *__y;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (static_cast<bool>(__x) != static_cast<bool>(__y))
-    return true;
-  if (!static_cast<bool>(__x))
-    return false;
-  return *__x != *__y;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__y))
-    return false;
-  if (!static_cast<bool>(__x))
-    return true;
-  return *__x < *__y;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__x))
-    return false;
-  if (!static_cast<bool>(__y))
-    return true;
-  return *__x > *__y;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__x))
-    return true;
-  if (!static_cast<bool>(__y))
-    return false;
-  return *__x <= *__y;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (!static_cast<bool>(__y))
-    return true;
-  if (!static_cast<bool>(__x))
-    return false;
-  return *__x >= *__y;
-}
-
-#    if _LIBCPP_STD_VER >= 20
-
-template <class _Tp, three_way_comparable_with<_Tp> _Up>
-_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
-operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) {
-  if (__x && __y)
-    return *__x <=> *__y;
-  return __x.has_value() <=> __y.has_value();
-}
-
-#    endif // _LIBCPP_STD_VER >= 20
-
-// [optional.nullops] Comparison with nullopt
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-#    if _LIBCPP_STD_VER <= 17
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>&, nullopt_t) noexcept {
-  return false;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(nullopt_t, const optional<_Tp>&) noexcept {
-  return true;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return static_cast<bool>(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(nullopt_t, const optional<_Tp>&) noexcept {
-  return false;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>&, nullopt_t) noexcept {
-  return true;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(nullopt_t, const optional<_Tp>& __x) noexcept {
-  return !static_cast<bool>(__x);
-}
-
-#    else // _LIBCPP_STD_VER <= 17
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept {
-  return __x.has_value() <=> false;
-}
-
-#    endif // _LIBCPP_STD_VER <= 17
-
-// [optional.comp.with.t] Comparison with T
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x == __v;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v == *__x;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x != __v;
-  return true;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v != *__x;
-  return true;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x < __v;
-  return true;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v < *__x;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x <= __v;
-  return true;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v <= *__x;
-  return false;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x > __v;
-  return false;
-}
-
-template < class _Tp,
-           class _Up,
-           enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
-                       int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v > *__x;
-  return true;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v) {
-  if (__x.has_value())
-    return *__x >= __v;
-  return false;
-}
-
-template <
-    class _Tp,
-    class _Up,
-    enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
-                int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x) {
-  if (__x.has_value())
-    return __v >= *__x;
-  return true;
-}
-
-#    if _LIBCPP_STD_VER >= 20
-
-template <class _Tp, class _Up>
-  requires(!__is_derived_from_optional<_Up>) && three_way_comparable_with<_Tp, _Up>
-_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
-operator<=>(const optional<_Tp>& __x, const _Up& __v) {
-  return __x.has_value() ? *__x <=> __v : strong_ordering::less;
-}
-
-#    endif // _LIBCPP_STD_VER >= 20
-
-template <class _Tp,
-          enable_if_t<(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>)
-#    if _LIBCPP_STD_VER >= 26
-                          || is_reference_v<_Tp>
-#    endif
-                      ,
-                      int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI
-_LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) {
-  __x.swap(__y);
-}
-
 struct __make_optional_barrier_tag {
   explicit __make_optional_barrier_tag() = default;
 };
@@ -1741,18 +353,6 @@ make_optional(initializer_list<_Up> __il, _Args&&... __args) {
   return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...);
 }
 
-template <class _Tp>
-struct hash< __enable_hash_helper<optional<_Tp>, remove_const_t<_Tp>> > {
-#    if _LIBCPP_STD_VER <= 17 || defined(_LIBCPP_ENABLE_CXX20_REMOVED_BINDER_TYPEDEFS)
-  _LIBCPP_DEPRECATED_IN_CXX17 typedef optional<_Tp> argument_type;
-  _LIBCPP_DEPRECATED_IN_CXX17 typedef size_t result_type;
-#    endif
-
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t operator()(const optional<_Tp>& __opt) const {
-    return static_cast<bool>(__opt) ? hash<remove_const_t<_Tp>>()(*__opt) : 0;
-  }
-};
-
 _LIBCPP_END_NAMESPACE_STD
 
 #  endif // _LIBCPP_STD_VER >= 17
diff --git a/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.ops/ostream_joiner.op.assign.pass.cpp b/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.ops/ostream_joiner.op.assign.pass.cpp
index 7a193e839f2a3..f7e5bdcccbeef 100644
--- a/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.ops/ostream_joiner.op.assign.pass.cpp
+++ b/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.ops/ostream_joiner.op.assign.pass.cpp
@@ -22,6 +22,7 @@
 #include <iostream>
 #include <sstream>
 #include <cassert>
+#include <type_traits>
 
 #include "test_macros.h"
 #include "test_iterators.h"
diff --git a/libcxx/test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp b/libcxx/test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp
index 9cf3b08930121..606ee4002040b 100644
--- a/libcxx/test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp
@@ -12,6 +12,7 @@
 
 // class bad_optional_access : public exception
 
+#include <exception>
 #include <optional>
 #include <type_traits>
 
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp
index 00ca941668eb2..a13b4334da332 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ctor.verify.cpp
@@ -26,26 +26,26 @@ int main(int, char**) {
   // clang-format off
   {
 #if TEST_STD_VER >= 26
-    std::optional<int&&> opt2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
+    std::optional<int&&> opt2; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
 #else
-    std::optional<char&> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
+    std::optional<char&> o1; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
 #endif
-    std::optional<NonDestructible> o2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
-    std::optional<char[20]> o3; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}}
+    std::optional<NonDestructible> o2; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+    std::optional<char[20]> o3; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with an array type is ill-formed}}
   }
 
   {
-    std::optional<               std::in_place_t> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
-    std::optional<const          std::in_place_t> o2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
-    std::optional<      volatile std::in_place_t> o3; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
-    std::optional<const volatile std::in_place_t> o4; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
+    std::optional<               std::in_place_t> o1; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
+    std::optional<const          std::in_place_t> o2; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
+    std::optional<      volatile std::in_place_t> o3; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
+    std::optional<const volatile std::in_place_t> o4; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with in_place_t is ill-formed}}
   }
 
   {
-    std::optional<               std::nullopt_t> o1; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
-    std::optional<const          std::nullopt_t> o2; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
-    std::optional<      volatile std::nullopt_t> o3; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
-    std::optional<const volatile std::nullopt_t> o4; // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional<               std::nullopt_t> o1; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional<const          std::nullopt_t> o2; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional<      volatile std::nullopt_t> o3; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional<const volatile std::nullopt_t> o4; // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
   }
   // clang-format on
   return 0;
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/deduct.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/deduct.verify.cpp
index 7ab842bf68c51..28d3c61db4a9a 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/deduct.verify.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/deduct.verify.cpp
@@ -31,7 +31,7 @@ int main(int, char**) {
 
   {
     //  optional(nullopt_t)
-    std::optional opt(std::nullopt); // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
+    std::optional opt(std::nullopt); // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with nullopt_t is ill-formed}}
   }
   // clang-format on
   return 0;
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp
index a956ab3a219cf..cdf456c4154ff 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional_requires_destructible_object.verify.cpp
@@ -28,30 +28,30 @@ int main(int, char**)
     using std::optional;
     {
 #if TEST_STD_VER >= 26
-      // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with an rvalue reference type is ill-formed}}
 #else
-      // expected-error-re at optional:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
+      // expected-error-re@*:* 2 {{static assertion failed{{.*}}instantiation of optional with a reference type is ill-formed}}
 #endif
       optional<int&> opt1;
       optional<int&&> opt2;
     }
     {
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
-        optional<X> opt3;
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+      optional<X> opt3;
     }
     {
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-object type is undefined behavior}}
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
-        optional<void()> opt4;
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-object type is undefined behavior}}
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+      optional<void()> opt4;
     }
     {
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-object type is undefined behavior}}
-        // expected-error-re at optional:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
-        // expected-error at optional:* 1+ {{cannot form a reference to 'void'}}
-        optional<const void> opt4;
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-object type is undefined behavior}}
+      // expected-error-re@*:* {{static assertion failed{{.*}}instantiation of optional with a non-destructible type is ill-formed}}
+      // expected-error@*:* 1+ {{cannot form a reference to 'void'}}
+      optional<const void> opt4;
     }
     // FIXME these are garbage diagnostics that Clang should not produce
-    // expected-error at optional:* 0+ {{is not a base class}}
+    // expected-error@*:* 0+ {{is not a base class}}
 
-  return 0;
+    return 0;
 }

>From 6f2ce7fc219f53aa526afe3448f4a04094cd0b04 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 30 Jun 2026 00:57:53 -0400
Subject: [PATCH 02/11] Formatting

---
 libcxx/include/__optional/common.h       | 12 +++----
 libcxx/include/__optional/optional.h     | 44 ++++++++++++------------
 libcxx/include/__optional/optional_ref.h | 18 +++++-----
 3 files changed, 37 insertions(+), 37 deletions(-)

diff --git a/libcxx/include/__optional/common.h b/libcxx/include/__optional/common.h
index 2b4862cfd9d34..eb6cff368789a 100644
--- a/libcxx/include/__optional/common.h
+++ b/libcxx/include/__optional/common.h
@@ -64,18 +64,18 @@ struct __is_std_optional : false_type {};
 template <class _Tp>
 struct __is_std_optional<optional<_Tp>> : true_type {};
 
-template<class _Tp, bool = is_reference_v<_Tp>>
+template <class _Tp, bool = is_reference_v<_Tp>>
 struct __optional_storage_base;
 
-#    if _LIBCPP_STD_VER < 26
+#  if _LIBCPP_STD_VER < 26
 template <class _Tp>
 inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
-#    else
+#  else
 template <class _Tp>
 inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp> || is_lvalue_reference_v<_Tp>;
-#    endif
+#  endif
 
-#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
 constexpr bool ranges::enable_view<optional<_Tp>> = true;
@@ -86,7 +86,7 @@ constexpr range_format format_kind<optional<_Tp>> = range_format::disabled;
 template <class _Tp>
 constexpr bool ranges::enable_borrowed_range<optional<_Tp&>> = true;
 
-#    endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index d01d8e661bdca..148b28e04d9ca 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -96,12 +96,12 @@ struct __optional_destruct_base<_Tp, false> {
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
       : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <class _Fp, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(
       __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
       : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
     if (__engaged_) {
@@ -127,12 +127,12 @@ struct __optional_destruct_base<_Tp, true> {
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
       : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <class _Fp, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
       __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
       : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
-#    endif
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
     if (__engaged_) {
@@ -358,7 +358,7 @@ struct __optional_iterator_base : __optional_move_assign_base<_Tp> {
   using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
 };
 
-#    if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
   requires is_object_v<_Tp>
@@ -370,33 +370,33 @@ struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
 public:
   using __optional_move_assign_base<_Tp>::__optional_move_assign_base;
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
   using iterator       = __bounded_iter<__pointer>;
   using const_iterator = __bounded_iter<__const_pointer>;
-#        else
+#    else
   using iterator       = __capacity_aware_iterator<__pointer, optional<_Tp>, 1>;
   using const_iterator = __capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>;
-#        endif
+#    endif
 
   // [optional.iterators], iterator support
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
     auto* __ptr = std::addressof(this->__get());
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
     return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
+#    else
     return std::__make_capacity_aware_iterator<__pointer, optional<_Tp>, 1>(__ptr);
-#        endif
+#    endif
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
     auto* __ptr = std::addressof(this->__get());
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
     return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
+#    else
     return std::__make_capacity_aware_iterator<__const_pointer, optional<_Tp>, 1>(__ptr);
-#        endif
+#    endif
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
@@ -407,7 +407,7 @@ struct __optional_iterator_base<_Tp> : __optional_move_assign_base<_Tp> {
   }
 };
 
-#    endif   // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
 class _LIBCPP_DECLSPEC_EMPTY_BASES optional
@@ -425,11 +425,11 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
 private:
   static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
   static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-#    if _LIBCPP_STD_VER >= 26
+#  if _LIBCPP_STD_VER >= 26
   static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
-#    else
+#  else
   static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
-#    endif
+#  endif
   static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
   static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
 
@@ -546,14 +546,14 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
     this->__construct_from(std::move(__v));
   }
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <class _Tag,
             class _Fp,
             class... _Args,
             enable_if_t<_IsSame<_Tag, __optional_construct_from_invoke_tag>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Tag, _Fp&& __f, _Args&&... __args)
       : __base(__optional_construct_from_invoke_tag{}, std::forward<_Fp>(__f), std::forward<_Args>(__args)...) {}
-#    endif
+#  endif
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(nullopt_t) noexcept {
     reset();
@@ -634,7 +634,7 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
     return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v));
   }
 
-#    if _LIBCPP_STD_VER >= 23
+#  if _LIBCPP_STD_VER >= 23
   template <class _Func>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
     using _Up = invoke_result_t<_Func, _Tp&>;
@@ -748,7 +748,7 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
       return std::move(*this);
     return std::forward<_Func>(__f)();
   }
-#    endif // _LIBCPP_STD_VER >= 23
+#  endif // _LIBCPP_STD_VER >= 23
 
   using __base::reset;
 };
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index dd1adc1642ca3..eccf925f525d6 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -143,12 +143,12 @@ struct __optional_storage_base<_Tp, true> {
 template <class _Tp>
 struct __optional_iterator_base;
 
-template<class _Tp>
+template <class _Tp>
 struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
   using __optional_storage_base<_Tp&>::__optional_storage_base;
 };
 
-#if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
+#  if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
   requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
@@ -159,22 +159,22 @@ struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
 public:
   using __optional_storage_base<_Tp&>::__optional_storage_base;
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
   using iterator = __bounded_iter<__pointer>;
-#        else
+#    else
   using iterator = __capacity_aware_iterator<__pointer, optional<_Tp&>, 1>;
-#        endif
+#    endif
 
   // [optional.ref.iterators], iterator support
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
     auto* __ptr = this->has_value() ? std::addressof(this->__get()) : nullptr;
 
-#        ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+#    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
     return std::__make_bounded_iter(__ptr, __ptr, __ptr + (this->has_value() ? 1 : 0));
-#        else
+#    else
     return std::__make_capacity_aware_iterator<__pointer, optional<_Tp&>, 1>(__ptr);
-#        endif
+#    endif
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
@@ -182,7 +182,7 @@ struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
   }
 };
 
-#endif
+#  endif
 
 template <class _Tp>
 class optional<_Tp&> : public __optional_iterator_base<_Tp&> {

>From b5ade2cb1b1469b78d68e21a08de4c0f5012f2dc Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 30 Jun 2026 18:43:58 -0400
Subject: [PATCH 03/11] Address comments

---
 libcxx/include/__fwd/optional.h          |  3 +++
 libcxx/include/__optional/common.h       |  9 +++------
 libcxx/include/__optional/hash.h         |  3 +--
 libcxx/include/__optional/optional.h     |  2 +-
 libcxx/include/__optional/optional_ref.h | 24 ++++++++++++------------
 5 files changed, 20 insertions(+), 21 deletions(-)

diff --git a/libcxx/include/__fwd/optional.h b/libcxx/include/__fwd/optional.h
index 35a3d2ea11e74..303c2d703e551 100644
--- a/libcxx/include/__fwd/optional.h
+++ b/libcxx/include/__fwd/optional.h
@@ -27,6 +27,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Tp>
 class optional;
 
+template <class _Tp>
+class optional<_Tp&>;
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/common.h b/libcxx/include/__optional/common.h
index eb6cff368789a..609d93fa38443 100644
--- a/libcxx/include/__optional/common.h
+++ b/libcxx/include/__optional/common.h
@@ -34,9 +34,9 @@ namespace std // purposefully not using versioning namespace
 
 class _LIBCPP_EXPORTED_FROM_ABI bad_optional_access : public exception {
 public:
-  bad_optional_access() _NOEXCEPT                                      = default;
-  bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
-  bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
+  _LIBCPP_HIDE_FROM_ABI bad_optional_access() _NOEXCEPT                                      = default;
+  _LIBCPP_HIDE_FROM_ABI bad_optional_access(const bad_optional_access&) _NOEXCEPT            = default;
+  _LIBCPP_HIDE_FROM_ABI bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
   // Get the key function ~bad_optional_access() into the dylib
   ~bad_optional_access() _NOEXCEPT override;
   [[__nodiscard__]] const char* what() const _NOEXCEPT override;
@@ -64,9 +64,6 @@ struct __is_std_optional : false_type {};
 template <class _Tp>
 struct __is_std_optional<optional<_Tp>> : true_type {};
 
-template <class _Tp, bool = is_reference_v<_Tp>>
-struct __optional_storage_base;
-
 #  if _LIBCPP_STD_VER < 26
 template <class _Tp>
 inline constexpr bool __is_valid_optional_contained_type = is_object_v<_Tp>;
diff --git a/libcxx/include/__optional/hash.h b/libcxx/include/__optional/hash.h
index 830f99b0b855f..ac86b2fb63488 100644
--- a/libcxx/include/__optional/hash.h
+++ b/libcxx/include/__optional/hash.h
@@ -13,9 +13,8 @@
 #include <__config>
 #include <__cstddef/size_t.h>
 #include <__functional/hash.h>
-#include <__type_traits/remove_const.h>
-
 #include <__fwd/optional.h>
+#include <__type_traits/remove_const.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index 148b28e04d9ca..21a4f88968378 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -141,7 +141,7 @@ struct __optional_destruct_base<_Tp, true> {
   }
 };
 
-template <class _Tp, bool>
+template <class _Tp>
 struct __optional_storage_base : __optional_destruct_base<_Tp> {
   using __base _LIBCPP_NODEBUG = __optional_destruct_base<_Tp>;
   using value_type             = _Tp;
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index eccf925f525d6..093e29080cecf 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -58,12 +58,12 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp>
-struct __optional_storage_base<_Tp, true> {
+struct __optional_ref_base {
   using value_type                 = _Tp;
   using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
   __raw_type* __value_;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_ref_base() noexcept : __value_(nullptr) {}
 
   template <class _Up>
   _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) {
@@ -72,7 +72,7 @@ struct __optional_storage_base<_Tp, true> {
   }
 
   template <class _UArg>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) {
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_ref_base(in_place_t, _UArg&& __uarg) {
     static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
                   "Attempted to construct a reference element in optional from a "
                   "possible temporary");
@@ -80,7 +80,7 @@ struct __optional_storage_base<_Tp, true> {
   }
 
   template <class _Fp, class... _Args>
-  constexpr __optional_storage_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
+  constexpr __optional_ref_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) {
     __convert_init_ref_val(std::forward<invoke_result_t<_Fp, _Args...>>(
         std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)));
   }
@@ -118,7 +118,7 @@ struct __optional_storage_base<_Tp, true> {
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_ref_base& __rhs) noexcept {
     std::swap(__value_, __rhs.__value_);
   }
 
@@ -141,23 +141,23 @@ struct __optional_storage_base<_Tp, true> {
 };
 
 template <class _Tp>
-struct __optional_iterator_base;
+struct __optional_ref_iterator_base;
 
 template <class _Tp>
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
+struct __optional_ref_iterator_base<_Tp&> : __optional_ref_base<_Tp&> {
+  using __optional_ref_base<_Tp&>::__optional_ref_base;
 };
 
 #  if _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR
 
 template <class _Tp>
   requires(is_object_v<_Tp> && !__is_unbounded_array_v<_Tp>)
-struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
+struct __optional_ref_iterator_base<_Tp&> : __optional_ref_base<_Tp&> {
 private:
   using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
 
 public:
-  using __optional_storage_base<_Tp&>::__optional_storage_base;
+  using __optional_ref_base<_Tp&>::__optional_ref_base;
 
 #    ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
   using iterator = __bounded_iter<__pointer>;
@@ -185,8 +185,8 @@ struct __optional_iterator_base<_Tp&> : __optional_storage_base<_Tp&> {
 #  endif
 
 template <class _Tp>
-class optional<_Tp&> : public __optional_iterator_base<_Tp&> {
-  using __base _LIBCPP_NODEBUG = __optional_iterator_base<_Tp&>;
+class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
+  using __base _LIBCPP_NODEBUG = __optional_ref_iterator_base<_Tp&>;
 
   template <class _Up, class _QualUp>
   static constexpr bool __check_optionalU_ctor =

>From 79ea6f9fff2345447e181e97c7d65270919f531a Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 16 Jul 2026 19:51:55 -0400
Subject: [PATCH 04/11] Make_optional to header, deduction guide to forward
 declaration, partial specializations to maintain similar error message

---
 libcxx/include/__fwd/optional.h           |  3 +
 libcxx/include/__optional/make_optional.h | 90 +++++++++++++++++++++++
 libcxx/include/__optional/optional.h      | 11 +--
 libcxx/include/__optional/optional_ref.h  | 11 +++
 libcxx/include/optional                   | 75 +------------------
 5 files changed, 111 insertions(+), 79 deletions(-)
 create mode 100644 libcxx/include/__optional/make_optional.h

diff --git a/libcxx/include/__fwd/optional.h b/libcxx/include/__fwd/optional.h
index 303c2d703e551..1e74a75ea0dbe 100644
--- a/libcxx/include/__fwd/optional.h
+++ b/libcxx/include/__fwd/optional.h
@@ -30,6 +30,9 @@ class optional;
 template <class _Tp>
 class optional<_Tp&>;
 
+template <class _Tp>
+optional(_Tp) -> optional<_Tp>;
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/make_optional.h b/libcxx/include/__optional/make_optional.h
new file mode 100644
index 0000000000000..202e09071116d
--- /dev/null
+++ b/libcxx/include/__optional/make_optional.h
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// std::optional<T&>
+
+#ifndef _LIBCPP_OPTIONAL_MAKE_OPTIONAL_H
+#define _LIBCPP_OPTIONAL_MAKE_OPTIONAL_H
+
+#include <__config>
+#include <__fwd/optional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/reference_constructs_from_temporary.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+
+#include <initializer_list>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+struct __make_optional_barrier_tag {
+  explicit __make_optional_barrier_tag() = default;
+};
+
+template <class _Tp, class... _Args>
+inline constexpr bool __is_constructible_for_optional_v = is_constructible_v<_Tp, _Args...>;
+
+template <class _Tp, class... _Args>
+struct __is_constructible_for_optional : bool_constant<__is_constructible_for_optional_v<_Tp, _Args...>> {};
+
+template <class _Tp, class _Up, class... _Args>
+inline constexpr bool __is_constructible_for_optional_initializer_list_v =
+    is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>;
+
+#  if _LIBCPP_STD_VER >= 26
+template <class _Tp, class... _Args>
+inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Args...> = false;
+
+template <class _Tp, class _Arg>
+inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Arg> =
+    is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>;
+
+template <class _Tp, class _Up, class... _Args>
+inline constexpr bool __is_constructible_for_optional_initializer_list_v<_Tp&, _Up, _Args...> = false;
+#  endif
+
+template <
+#  if _LIBCPP_STD_VER >= 26
+    __make_optional_barrier_tag = __make_optional_barrier_tag{},
+#  endif
+    class _Tp,
+    enable_if_t<is_constructible_v<decay_t<_Tp>, _Tp>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<decay_t<_Tp>> make_optional(_Tp&& __v) {
+  return optional<decay_t<_Tp>>(std::forward<_Tp>(__v));
+}
+
+template <class _Tp, class... _Args, enable_if_t<__is_constructible_for_optional_v<_Tp, _Args...>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(_Args&&... __args) {
+  return optional<_Tp>(in_place, std::forward<_Args>(__args)...);
+}
+
+template <class _Tp,
+          class _Up,
+          class... _Args,
+          enable_if_t<__is_constructible_for_optional_initializer_list_v<_Tp, _Up, _Args...>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp>
+make_optional(initializer_list<_Up> __il, _Args&&... __args) {
+  return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+#endif // _LIBCPP_OPTIONAL_MAKE_OPTIONAL_H
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index 21a4f88968378..4726b706cca84 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -425,11 +425,6 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
 private:
   static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>, "instantiation of optional with in_place_t is ill-formed");
   static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>, "instantiation of optional with nullopt_t is ill-formed");
-#  if _LIBCPP_STD_VER >= 26
-  static_assert(!is_rvalue_reference_v<_Tp>, "instantiation of optional with an rvalue reference type is ill-formed");
-#  else
-  static_assert(!is_reference_v<_Tp>, "instantiation of optional with a reference type is ill-formed");
-#  endif
   static_assert(is_destructible_v<_Tp>, "instantiation of optional with a non-destructible type is ill-formed");
   static_assert(!is_array_v<_Tp>, "instantiation of optional with an array type is ill-formed");
 
@@ -752,6 +747,12 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
 
   using __base::reset;
 };
+
+template <class _Tp>
+class optional<_Tp&&> {
+  static_assert(false, "instantiation of optional with an rvalue reference type is ill-formed");
+};
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index 093e29080cecf..9bd2831fe0d1a 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -348,6 +348,17 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
 
 _LIBCPP_END_NAMESPACE_STD
 
+#else
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp>
+class optional<_Tp&> {
+  static_assert(false, "instantiation of optional with a reference type is ill-formed");
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
 #endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_POP_MACROS
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 2b40706910018..914fd8cc9e226 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -259,19 +259,12 @@ namespace std {
 #  include <__cxx03/__config>
 #else
 #  include <__config>
-#  include <__type_traits/decay.h>
-#  include <__type_traits/enable_if.h>
-#  include <__type_traits/is_constructible.h>
-#  include <__type_traits/reference_constructs_from_temporary.h>
-#  include <__type_traits/remove_const.h>
-#  include <__utility/forward.h>
-#  include <__utility/in_place.h>
-#  include <initializer_list>
 #  include <version>
 
 #  include <__optional/common.h>
 #  include <__optional/comparison.h>
 #  include <__optional/hash.h>
+#  include <__optional/make_optional.h>
 #  include <__optional/optional.h>
 #  include <__optional/optional_ref.h>
 #  include <__optional/swap.h>
@@ -293,72 +286,6 @@ namespace std {
 #    pragma GCC system_header
 #  endif
 
-_LIBCPP_PUSH_MACROS
-#  include <__undef_macros>
-
-#  if _LIBCPP_STD_VER >= 17
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-template <class _Tp>
-optional(_Tp) -> optional<_Tp>;
-
-struct __make_optional_barrier_tag {
-  explicit __make_optional_barrier_tag() = default;
-};
-
-template <class _Tp, class... _Args>
-inline constexpr bool __is_constructible_for_optional_v = is_constructible_v<_Tp, _Args...>;
-
-template <class _Tp, class... _Args>
-struct __is_constructible_for_optional : bool_constant<__is_constructible_for_optional_v<_Tp, _Args...>> {};
-
-template <class _Tp, class _Up, class... _Args>
-inline constexpr bool __is_constructible_for_optional_initializer_list_v =
-    is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>;
-
-#    if _LIBCPP_STD_VER >= 26
-template <class _Tp, class... _Args>
-inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Args...> = false;
-
-template <class _Tp, class _Arg>
-inline constexpr bool __is_constructible_for_optional_v<_Tp&, _Arg> =
-    is_constructible_v<_Tp&, _Arg> && !reference_constructs_from_temporary_v<_Tp&, _Arg>;
-
-template <class _Tp, class _Up, class... _Args>
-inline constexpr bool __is_constructible_for_optional_initializer_list_v<_Tp&, _Up, _Args...> = false;
-#    endif
-
-template <
-#    if _LIBCPP_STD_VER >= 26
-    __make_optional_barrier_tag = __make_optional_barrier_tag{},
-#    endif
-    class _Tp,
-    enable_if_t<is_constructible_v<decay_t<_Tp>, _Tp>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<decay_t<_Tp>> make_optional(_Tp&& __v) {
-  return optional<decay_t<_Tp>>(std::forward<_Tp>(__v));
-}
-
-template <class _Tp, class... _Args, enable_if_t<__is_constructible_for_optional_v<_Tp, _Args...>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(_Args&&... __args) {
-  return optional<_Tp>(in_place, std::forward<_Args>(__args)...);
-}
-
-template <class _Tp,
-          class _Up,
-          class... _Args,
-          enable_if_t<__is_constructible_for_optional_initializer_list_v<_Tp, _Up, _Args...>, int> = 0>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp>
-make_optional(initializer_list<_Up> __il, _Args&&... __args) {
-  return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...);
-}
-
-_LIBCPP_END_NAMESPACE_STD
-
-#  endif // _LIBCPP_STD_VER >= 17
-
-_LIBCPP_POP_MACROS
-
 #  if defined(_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23) && _LIBCPP_STD_VER <= 20
 #    include <atomic>
 #    include <climits>

>From e48c148429d6ea3b26160dc62e6452d7760ec9a5 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 16 Jul 2026 20:07:22 -0400
Subject: [PATCH 05/11] Fix missing other things

---
 libcxx/include/CMakeLists.txt            | 1 +
 libcxx/include/__optional/optional.h     | 5 -----
 libcxx/include/__optional/optional_ref.h | 7 ++++++-
 libcxx/include/module.modulemap.in       | 1 +
 4 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 021d392e50c78..384ee68153abf 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -666,6 +666,7 @@ set(files
   __optional/comparison.h
   __optional/hash.h
   __optional/nullopt_t.h
+  __optional/make_optional.h
   __optional/optional.h
   __optional/optional_ref.h
   __optional/swap.h
diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index 4726b706cca84..d8dbfd7188585 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -748,11 +748,6 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
   using __base::reset;
 };
 
-template <class _Tp>
-class optional<_Tp&&> {
-  static_assert(false, "instantiation of optional with an rvalue reference type is ill-formed");
-};
-
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index 9bd2831fe0d1a..763c91d13f949 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -348,7 +348,7 @@ class optional<_Tp&> : public __optional_ref_iterator_base<_Tp&> {
 
 _LIBCPP_END_NAMESPACE_STD
 
-#else
+#elif _LIBCPP_STD_VER >= 17
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -357,6 +357,11 @@ class optional<_Tp&> {
   static_assert(false, "instantiation of optional with a reference type is ill-formed");
 };
 
+template <class _Tp>
+class optional<_Tp&&> {
+  static_assert(false, "instantiation of optional with an rvalue reference type is ill-formed");
+};
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index e0c40bad3bae9..39a7d45b6c3e0 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1810,6 +1810,7 @@ module std [system] {
     }
 
     module comparison { header "__optional/comparison.h" }
+    module make_optional { header "__optional/make_optional.h" }
     module nullopt_t { header "__optional/nullopt_t.h" }
     module optional { header "__optional/optional.h" }
     module optional_ref { header "__optional/optional_ref.h" }

>From fec480eae52447a0ceefd7fa5776dbd7f6c016e2 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 16 Jul 2026 20:15:12 -0400
Subject: [PATCH 06/11] move rref failure-specialization back to optional.h

---
 libcxx/include/__optional/optional.h     | 5 +++++
 libcxx/include/__optional/optional_ref.h | 5 -----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index d8dbfd7188585..4726b706cca84 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -748,6 +748,11 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
   using __base::reset;
 };
 
+template <class _Tp>
+class optional<_Tp&&> {
+  static_assert(false, "instantiation of optional with an rvalue reference type is ill-formed");
+};
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__optional/optional_ref.h b/libcxx/include/__optional/optional_ref.h
index 763c91d13f949..51e690bdb5b33 100644
--- a/libcxx/include/__optional/optional_ref.h
+++ b/libcxx/include/__optional/optional_ref.h
@@ -357,11 +357,6 @@ class optional<_Tp&> {
   static_assert(false, "instantiation of optional with a reference type is ill-formed");
 };
 
-template <class _Tp>
-class optional<_Tp&&> {
-  static_assert(false, "instantiation of optional with an rvalue reference type is ill-formed");
-};
-
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 26

>From f9c8c56633c5e9bfdf34715719f30f577c44b0fa Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 16 Jul 2026 23:08:36 -0400
Subject: [PATCH 07/11] Reorder CMakeLists

---
 libcxx/include/CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 384ee68153abf..cbf652d1ab54a 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -665,8 +665,8 @@ set(files
   __optional/common.h
   __optional/comparison.h
   __optional/hash.h
-  __optional/nullopt_t.h
   __optional/make_optional.h
+  __optional/nullopt_t.h
   __optional/optional.h
   __optional/optional_ref.h
   __optional/swap.h

>From cd57d28700d5675334d8eee8afe2ae7ecd4742b7 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 17 Jul 2026 02:57:55 -0400
Subject: [PATCH 08/11] Missing pop macros

---
 libcxx/include/__optional/make_optional.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/__optional/make_optional.h b/libcxx/include/__optional/make_optional.h
index 202e09071116d..9aa8dee079a08 100644
--- a/libcxx/include/__optional/make_optional.h
+++ b/libcxx/include/__optional/make_optional.h
@@ -87,4 +87,7 @@ make_optional(initializer_list<_Up> __il, _Args&&... __args) {
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
 #endif // _LIBCPP_OPTIONAL_MAKE_OPTIONAL_H

>From 0393a8cce2fbeff360e572537811abe54a8b3456 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 21 Jul 2026 01:34:15 -0400
Subject: [PATCH 09/11] Don't disturb transitive includes

---
 libcxx/include/optional | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/optional b/libcxx/include/optional
index 914fd8cc9e226..1330b88a15fb0 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -269,13 +269,19 @@ namespace std {
 #  include <__optional/optional_ref.h>
 #  include <__optional/swap.h>
 
-// TODO: Certain headers rely on these transitive includes
+// TODO: Some headers rely on these transitive includes
+#  include <__cstddef/size_t.h>
 #  include <__type_traits/conditional.h>
+#  include <__type_traits/enable_if.h>
 #  include <__type_traits/invoke.h>
+#  include <__type_traits/is_constructible.h>
 #  include <__type_traits/is_object.h>
 #  include <__type_traits/is_reference.h>
+#  include <__type_traits/remove_const.h>
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/declval.h>
+#  include <__utility/forward.h>
+#  include <__utility/in_place.h>
 #  include <__utility/move.h>
 
 // standard-mandated includes

>From d1f16e9f80de18bc4a98df7c0abb7e54cedb447c Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 21 Jul 2026 16:15:01 -0400
Subject: [PATCH 10/11] Undisturb more transitive includes

---
 libcxx/include/optional                                         | 2 ++
 .../ostream.joiner.creation/make_ostream_joiner.pass.cpp        | 1 +
 2 files changed, 3 insertions(+)

diff --git a/libcxx/include/optional b/libcxx/include/optional
index 1330b88a15fb0..170062aa32497 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -284,6 +284,8 @@ namespace std {
 #  include <__utility/in_place.h>
 #  include <__utility/move.h>
 
+#  include <initializer_list>
+
 // standard-mandated includes
 // [optional.syn]
 #  include <compare>
diff --git a/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.creation/make_ostream_joiner.pass.cpp b/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.creation/make_ostream_joiner.pass.cpp
index 05f798fcae3d6..0f16ff3b31d52 100644
--- a/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.creation/make_ostream_joiner.pass.cpp
+++ b/libcxx/test/std/experimental/iterator/ostream.joiner/ostream.joiner.creation/make_ostream_joiner.pass.cpp
@@ -23,6 +23,7 @@
 #include <iostream>
 #include <sstream>
 #include <cassert>
+#include <type_traits>
 
 #include "test_macros.h"
 #include "test_iterators.h"

>From 53dbe3663c7276b5af1a3ec3be6c0cabaac297ba Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 21 Jul 2026 16:55:03 -0400
Subject: [PATCH 11/11] Correct static assert message

---
 libcxx/include/__optional/optional.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/include/__optional/optional.h b/libcxx/include/__optional/optional.h
index 4726b706cca84..77f4b2ce85e8f 100644
--- a/libcxx/include/__optional/optional.h
+++ b/libcxx/include/__optional/optional.h
@@ -750,7 +750,11 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
 
 template <class _Tp>
 class optional<_Tp&&> {
+#  if _LIBCPP_STD_VER >= 26
   static_assert(false, "instantiation of optional with an rvalue reference type is ill-formed");
+#  else
+  static_assert(false, "instantiation of optional with a reference type is ill-formed");
+#  endif
 };
 
 _LIBCPP_END_NAMESPACE_STD


From libcxx-commits at lists.llvm.org  Tue Jul 21 13:55:30 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 13:55:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Uniformly require complete types
 in vector member functions (PR #211110)
Message-ID: <llvm.org/llvm/llvm-project/pull/211110@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/211110

The Standard allows vector's value type to be incomplete when instantiating the vector class, but it requires it to be complete before instantiating any member function. Libc++ would produce an error for most functions, but it would silently work for a few methods like empty() (however only in the non size-based vector).

To avoid portability traps for users, uniformly require complete types under all configurations for all vector methods.

See also #210754

>From c0d766a1104c92315f275a7febdbef26ff65b636 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 21 Jul 2026 16:51:27 -0400
Subject: [PATCH] [libc++] Uniformly require complete types in vector member
 functions

The Standard allows vector's value type to be incomplete when instantiating
the vector class, but it requires it to be complete before instantiating
any member function. Libc++ would produce an error for most functions,
but it would silently work for a few methods like empty() (however only
in the non size-based vector).

To avoid portability traps for users, uniformly require complete types
under all configurations for all vector methods.

See also #210754
---
 libcxx/docs/ReleaseNotes/24.rst               |  4 ++
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/__utility/require_complete.h   | 42 +++++++++++++++++++
 libcxx/include/__vector/vector.h              | 17 ++++++++
 libcxx/include/module.modulemap.in            |  1 +
 .../vector/incomplete_type.compile.pass.cpp   | 27 ------------
 .../vector/incomplete_type.verify.cpp         | 30 +++++++++++++
 7 files changed, 95 insertions(+), 27 deletions(-)
 create mode 100644 libcxx/include/__utility/require_complete.h
 delete mode 100644 libcxx/test/libcxx/containers/sequences/vector/incomplete_type.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/sequences/vector/incomplete_type.verify.cpp

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..612fb0d03d0b2 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -57,6 +57,10 @@ Potentially breaking changes
   but causes programs which rely on these includes to not compile anymore. The ``_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23``
   macro that was provided in LLVM 23 to ease the transition has been removed in this release.
 
+- Libc++ now diagnoses when a method of ``std::vector<T>`` is used and ``T`` is an incomplete type. The Standard requires
+  ``T`` to be complete before any member function is used, however this worked unreliably for a few member functions. In
+  LLVM 24, this is diagnosed uniformly.
+
 Announcements About Future Releases
 -----------------------------------
 
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..3f7be893a4450 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -953,6 +953,7 @@ set(files
   __utility/priority_tag.h
   __utility/private_constructor_tag.h
   __utility/rel_ops.h
+  __utility/require_complete.h
   __utility/scope_guard.h
   __utility/small_buffer.h
   __utility/swap.h
diff --git a/libcxx/include/__utility/require_complete.h b/libcxx/include/__utility/require_complete.h
new file mode 100644
index 0000000000000..5cb9ea74b500b
--- /dev/null
+++ b/libcxx/include/__utility/require_complete.h
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_REQUIRE_COMPLETE_H
+#define _LIBCPP___UTILITY_REQUIRE_COMPLETE_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp, size_t = sizeof(_Tp)>
+_LIBCPP_CONSTEXPR void __require_complete_impl(int) {}
+
+template <class _Tp, bool _False = false>
+_LIBCPP_CONSTEXPR void __require_complete_impl(long) {
+  static_assert(_False, "Type is required to be complete");
+}
+
+// Produce a compiler error if the given type is not complete.
+template <class _Tp>
+_LIBCPP_CONSTEXPR void __require_complete() {
+  std::__require_complete_impl<_Tp>(0);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___UTILITY_REQUIRE_COMPLETE_H
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 5e9fa4a7d0030..6749ae1aacc03 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -65,6 +65,7 @@
 #include <__utility/is_pointer_in_range.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
+#include <__utility/require_complete.h>
 #include <__utility/swap.h>
 #include <initializer_list>
 #include <limits>
@@ -348,43 +349,55 @@ class vector {
   // Iterators
   //
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __make_iter(__add_alignment_assumption(this->__layout_.__begin_ptr()));
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __make_iter(__add_alignment_assumption(this->__layout_.__begin_ptr()));
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __make_iter(__add_alignment_assumption(__layout_.__end_ptr()));
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __make_iter(__add_alignment_assumption(__layout_.__end_ptr()));
   }
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return reverse_iterator(end());
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reverse_iterator
   rbegin() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return const_reverse_iterator(end());
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reverse_iterator rend() _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return reverse_iterator(begin());
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return const_reverse_iterator(begin());
   }
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return begin();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator cend() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return end();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reverse_iterator
   crbegin() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return rbegin();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return rend();
   }
 
@@ -392,16 +405,20 @@ class vector {
   // [vector.capacity], capacity
   //
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __layout_.__size();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type capacity() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __layout_.__capacity();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __layout_.__empty();
   }
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return std::min<size_type>(__alloc_traits::max_size(__layout_.__alloc()), numeric_limits<difference_type>::max());
   }
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void reserve(size_type __n);
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..3a2615da37505 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2229,6 +2229,7 @@ module std {
     module priority_tag                    { header "__utility/priority_tag.h" }
     module private_constructor_tag         { header "__utility/private_constructor_tag.h" }
     module rel_ops                         { header "__utility/rel_ops.h" }
+    module require_complete                { header "__utility/require_complete.h" }
     module scope_guard                     { header "__utility/scope_guard.h" }
     module small_buffer                    { header "__utility/small_buffer.h" }
     module swap                            { header "__utility/swap.h" }
diff --git a/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.compile.pass.cpp
deleted file mode 100644
index 029d29eb437f8..0000000000000
--- a/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.compile.pass.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <vector>
-
-// This test pins down the current libc++ behavior that vector<T>::empty() can be
-// called even when T is an incomplete type. The standard does not require this:
-// [vector.overview] only guarantees that an incomplete type may be used to
-// instantiate vector, and requires the type to be complete before any method is
-// called.
-//
-// However, libc++ made that work previously, and this test pins down that behavior
-// to avoid breaking it unintentionally. Note that this is not a guarantee to users
-// that we will support this in the future: this merely guards against changing this
-// behavior unknowingly.
-
-#include <vector>
-
-struct Incomplete;
-
-bool call_empty(std::vector<Incomplete>& v) { return v.empty(); }
-bool call_empty_const(const std::vector<Incomplete>& v) { return v.empty(); }
diff --git a/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.verify.cpp b/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.verify.cpp
new file mode 100644
index 0000000000000..caabd64c3b279
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.verify.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <vector>
+
+// This test ensures that we diagnose when an incomplete type is used in one of
+// vector's methods. The Standard requires that to be the case, and we want to
+// uniformly produce an error for that. Note that producing the same diagnostic
+// in all cases is difficult, but we at least want to fail to fight back against
+// Hyrum's law.
+
+#include <vector>
+
+struct Incomplete;
+
+void f(std::vector<Incomplete>& v) {
+  (void)v.empty();  // expected-error@*:* {{}}
+  (void)v.size();   // expected-error@*:* {{}}
+  (void)v.begin();  // expected-error@*:* {{}}
+  (void)v.end();    // expected-error@*:* {{}}
+  (void)v.cbegin(); // expected-error@*:* {{}}
+  (void)v.cend();   // expected-error@*:* {{}}
+
+  // etc for other APIs
+}


From libcxx-commits at lists.llvm.org  Tue Jul 21 13:55:46 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 13:55:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Uniformly require complete types
 in vector member functions (PR #211110)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211110@github.com>
Message-ID: <6a5fdcd2.c56fb102.6921f.3f63@mx.google.com>


ldionne wrote:

CC @nico 

https://github.com/llvm/llvm-project/pull/211110

From libcxx-commits at lists.llvm.org  Tue Jul 21 13:56:08 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Tue, 21 Jul 2026 13:56:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Uniformly require complete types
 in vector member functions (PR #211110)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211110@github.com>
Message-ID: <6a5fdce8.510f576d.33aa7d.76ac@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

The Standard allows vector's value type to be incomplete when instantiating the vector class, but it requires it to be complete before instantiating any member function. Libc++ would produce an error for most functions, but it would silently work for a few methods like empty() (however only in the non size-based vector).

To avoid portability traps for users, uniformly require complete types under all configurations for all vector methods.

See also #<!-- -->210754

---
Full diff: https://github.com/llvm/llvm-project/pull/211110.diff


7 Files Affected:

- (modified) libcxx/docs/ReleaseNotes/24.rst (+4) 
- (modified) libcxx/include/CMakeLists.txt (+1) 
- (added) libcxx/include/__utility/require_complete.h (+42) 
- (modified) libcxx/include/__vector/vector.h (+17) 
- (modified) libcxx/include/module.modulemap.in (+1) 
- (removed) libcxx/test/libcxx/containers/sequences/vector/incomplete_type.compile.pass.cpp (-27) 
- (added) libcxx/test/libcxx/containers/sequences/vector/incomplete_type.verify.cpp (+30) 


``````````diff
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..612fb0d03d0b2 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -57,6 +57,10 @@ Potentially breaking changes
   but causes programs which rely on these includes to not compile anymore. The ``_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23``
   macro that was provided in LLVM 23 to ease the transition has been removed in this release.
 
+- Libc++ now diagnoses when a method of ``std::vector<T>`` is used and ``T`` is an incomplete type. The Standard requires
+  ``T`` to be complete before any member function is used, however this worked unreliably for a few member functions. In
+  LLVM 24, this is diagnosed uniformly.
+
 Announcements About Future Releases
 -----------------------------------
 
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..3f7be893a4450 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -953,6 +953,7 @@ set(files
   __utility/priority_tag.h
   __utility/private_constructor_tag.h
   __utility/rel_ops.h
+  __utility/require_complete.h
   __utility/scope_guard.h
   __utility/small_buffer.h
   __utility/swap.h
diff --git a/libcxx/include/__utility/require_complete.h b/libcxx/include/__utility/require_complete.h
new file mode 100644
index 0000000000000..5cb9ea74b500b
--- /dev/null
+++ b/libcxx/include/__utility/require_complete.h
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_REQUIRE_COMPLETE_H
+#define _LIBCPP___UTILITY_REQUIRE_COMPLETE_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp, size_t = sizeof(_Tp)>
+_LIBCPP_CONSTEXPR void __require_complete_impl(int) {}
+
+template <class _Tp, bool _False = false>
+_LIBCPP_CONSTEXPR void __require_complete_impl(long) {
+  static_assert(_False, "Type is required to be complete");
+}
+
+// Produce a compiler error if the given type is not complete.
+template <class _Tp>
+_LIBCPP_CONSTEXPR void __require_complete() {
+  std::__require_complete_impl<_Tp>(0);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___UTILITY_REQUIRE_COMPLETE_H
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 5e9fa4a7d0030..6749ae1aacc03 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -65,6 +65,7 @@
 #include <__utility/is_pointer_in_range.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
+#include <__utility/require_complete.h>
 #include <__utility/swap.h>
 #include <initializer_list>
 #include <limits>
@@ -348,43 +349,55 @@ class vector {
   // Iterators
   //
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __make_iter(__add_alignment_assumption(this->__layout_.__begin_ptr()));
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __make_iter(__add_alignment_assumption(this->__layout_.__begin_ptr()));
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __make_iter(__add_alignment_assumption(__layout_.__end_ptr()));
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __make_iter(__add_alignment_assumption(__layout_.__end_ptr()));
   }
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return reverse_iterator(end());
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reverse_iterator
   rbegin() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return const_reverse_iterator(end());
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reverse_iterator rend() _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return reverse_iterator(begin());
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return const_reverse_iterator(begin());
   }
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return begin();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator cend() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return end();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reverse_iterator
   crbegin() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return rbegin();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return rend();
   }
 
@@ -392,16 +405,20 @@ class vector {
   // [vector.capacity], capacity
   //
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __layout_.__size();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type capacity() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __layout_.__capacity();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return __layout_.__empty();
   }
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {
+    std::__require_complete<_Tp>();
     return std::min<size_type>(__alloc_traits::max_size(__layout_.__alloc()), numeric_limits<difference_type>::max());
   }
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void reserve(size_type __n);
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..3a2615da37505 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2229,6 +2229,7 @@ module std {
     module priority_tag                    { header "__utility/priority_tag.h" }
     module private_constructor_tag         { header "__utility/private_constructor_tag.h" }
     module rel_ops                         { header "__utility/rel_ops.h" }
+    module require_complete                { header "__utility/require_complete.h" }
     module scope_guard                     { header "__utility/scope_guard.h" }
     module small_buffer                    { header "__utility/small_buffer.h" }
     module swap                            { header "__utility/swap.h" }
diff --git a/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.compile.pass.cpp
deleted file mode 100644
index 029d29eb437f8..0000000000000
--- a/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.compile.pass.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <vector>
-
-// This test pins down the current libc++ behavior that vector<T>::empty() can be
-// called even when T is an incomplete type. The standard does not require this:
-// [vector.overview] only guarantees that an incomplete type may be used to
-// instantiate vector, and requires the type to be complete before any method is
-// called.
-//
-// However, libc++ made that work previously, and this test pins down that behavior
-// to avoid breaking it unintentionally. Note that this is not a guarantee to users
-// that we will support this in the future: this merely guards against changing this
-// behavior unknowingly.
-
-#include <vector>
-
-struct Incomplete;
-
-bool call_empty(std::vector<Incomplete>& v) { return v.empty(); }
-bool call_empty_const(const std::vector<Incomplete>& v) { return v.empty(); }
diff --git a/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.verify.cpp b/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.verify.cpp
new file mode 100644
index 0000000000000..caabd64c3b279
--- /dev/null
+++ b/libcxx/test/libcxx/containers/sequences/vector/incomplete_type.verify.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <vector>
+
+// This test ensures that we diagnose when an incomplete type is used in one of
+// vector's methods. The Standard requires that to be the case, and we want to
+// uniformly produce an error for that. Note that producing the same diagnostic
+// in all cases is difficult, but we at least want to fail to fight back against
+// Hyrum's law.
+
+#include <vector>
+
+struct Incomplete;
+
+void f(std::vector<Incomplete>& v) {
+  (void)v.empty();  // expected-error@*:* {{}}
+  (void)v.size();   // expected-error@*:* {{}}
+  (void)v.begin();  // expected-error@*:* {{}}
+  (void)v.end();    // expected-error@*:* {{}}
+  (void)v.cbegin(); // expected-error@*:* {{}}
+  (void)v.cend();   // expected-error@*:* {{}}
+
+  // etc for other APIs
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/211110

From libcxx-commits at lists.llvm.org  Tue Jul 21 13:58:29 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 13:58:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Uniformly require complete types
 in vector member functions (PR #211110)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211110@github.com>
Message-ID: <6a5fdd75.481afadf.1fc9f0.c410@mx.google.com>


================
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <vector>
+
+// This test ensures that we diagnose when an incomplete type is used in one of
+// vector's methods. The Standard requires that to be the case, and we want to
+// uniformly produce an error for that. Note that producing the same diagnostic
+// in all cases is difficult, but we at least want to fail to fight back against
+// Hyrum's law.
+
+#include <vector>
+
+struct Incomplete;
+
+void f(std::vector<Incomplete>& v) {
+  (void)v.empty();  // expected-error@*:* {{}}
----------------
ldionne wrote:

This is more difficult to test than I anticipated.

This test currently happens to pass because we produce errors, but the errors are not all static asserts. In fact, most of them are other, non `static_assert` errors.

Furthermore, if I remove the `__require_complete` call from `empty()` (which is arguably the only one that needs something), this test still passes because some of these statements produce more than one error.

So, I'm not certain how to best test this. One test per file would technically work, but there's no way I can convince myself to do that.

https://github.com/llvm/llvm-project/pull/211110

From libcxx-commits at lists.llvm.org  Tue Jul 21 14:00:08 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Tue, 21 Jul 2026 14:00:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Uniformly require complete types
 in vector member functions (PR #211110)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211110@github.com>
Message-ID: <6a5fddd8.92d2873c.2cdd42.8553@mx.google.com>


================
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <vector>
+
+// This test ensures that we diagnose when an incomplete type is used in one of
+// vector's methods. The Standard requires that to be the case, and we want to
+// uniformly produce an error for that. Note that producing the same diagnostic
+// in all cases is difficult, but we at least want to fail to fight back against
+// Hyrum's law.
+
+#include <vector>
+
+struct Incomplete;
+
+void f(std::vector<Incomplete>& v) {
+  (void)v.empty();  // expected-error@*:* {{}}
+  (void)v.size();   // expected-error@*:* {{}}
+  (void)v.begin();  // expected-error@*:* {{}}
+  (void)v.end();    // expected-error@*:* {{}}
+  (void)v.cbegin(); // expected-error@*:* {{}}
----------------
ldionne wrote:

Another question is which methods should we test here. Some methods like `push_back(T const&)` clearly already imply a complete `T`, so I don't think there's a point in testing those. I'm not sure where we should draw the line.

https://github.com/llvm/llvm-project/pull/211110

From libcxx-commits at lists.llvm.org  Tue Jul 21 19:54:11 2026
From: libcxx-commits at lists.llvm.org (Avi Patel via libcxx-commits)
Date: Tue, 21 Jul 2026 19:54:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a6030d3.5a0412d2.325e39.3749@mx.google.com>


https://github.com/firedog1234 updated https://github.com/llvm/llvm-project/pull/210551

>From a1178273ee760811d06d3f7a0cdb2a95ba3d4183 Mon Sep 17 00:00:00 2001
From: avipatel <aviipatel06 at gmail.com>
Date: Sat, 18 Jul 2026 15:26:56 -0400
Subject: [PATCH] [libc++] Implement P1901R2: owner_hash and owner_equal for
 shared_ptr/weak_ptr

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/ReleaseNotes/23.rst               |  1 +
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 libcxx/include/__memory/shared_ptr.h          | 71 +++++++++++++++++++
 libcxx/include/memory                         | 32 +++++++++
 libcxx/include/version                        |  2 +-
 .../memory.version.compile.pass.cpp           | 16 ++---
 .../version.version.compile.pass.cpp          | 16 ++---
 .../owner_equal_shared_ptr.pass.cpp           | 40 +++++++++++
 .../owner_equal_weak_ptr.pass.cpp             | 41 +++++++++++
 .../owner_hash.pass.cpp                       | 44 ++++++++++++
 .../owner_equal.pass.cpp                      | 69 ++++++++++++++++++
 .../owner_hash.pass.cpp                       | 49 +++++++++++++
 .../owner_equal_shared_ptr.pass.cpp           | 37 ++++++++++
 .../owner_equal_weak_ptr.pass.cpp             | 42 +++++++++++
 .../owner_hash.pass.cpp                       | 34 +++++++++
 .../generate_feature_test_macro_components.py |  1 -
 17 files changed, 473 insertions(+), 26 deletions(-)
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerhash/owner_hash.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_shared_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_weak_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_hash.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..8a26828240de4 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -526,7 +526,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_senders``                                      *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_smart_ptr_owner_equality``                     *unimplemented*
+    ``__cpp_lib_smart_ptr_owner_equality``                     ``202306L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_span_at``                                      ``202311L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 1213e8f43c8cd..1dee9843c9c55 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,7 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P1901R2: Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers (`Github <https://llvm.org/PR105372>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index efc98495d1d5c..98135f198110c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -12,7 +12,7 @@
 "`P2338R4 <https://wg21.link/P2338R4>`__","Freestanding Library: Character primitives and the C library","2023-06 (Varna)","","","`#105369 <https://github.com/llvm/llvm-project/issues/105369>`__",""
 "`P2013R5 <https://wg21.link/P2013R5>`__","Freestanding Language: Optional ``::operator new``","2023-06 (Varna)","","","`#105370 <https://github.com/llvm/llvm-project/issues/105370>`__",""
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
-"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
+"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","|Complete|","23","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
 "`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index ebd542ba14d01..0770f2a8e383d 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -18,6 +18,7 @@
 #include <__cstddef/ptrdiff_t.h>
 #include <__exception/exception.h>
 #include <__functional/binary_function.h>
+#include <__functional/hash.h>
 #include <__functional/operations.h>
 #include <__functional/reference_wrapper.h>
 #include <__fwd/ostream.h>
@@ -555,6 +556,22 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI shared_ptr {
 
   _LIBCPP_HIDE_FROM_ABI bool __owner_equivalent(const shared_ptr& __p) const { return __cntrl_ == __p.__cntrl_; }
 
+#if _LIBCPP_STD_VER >= 26
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t owner_hash() const _NOEXCEPT {
+    return std::hash<__shared_weak_count*>()(__cntrl_);
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(shared_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(weak_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+#endif
+
 #if _LIBCPP_STD_VER >= 17
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __add_lvalue_reference_t<element_type> operator[](ptrdiff_t __i) const {
     static_assert(is_array<_Tp>::value, "std::shared_ptr<T>::operator[] is only valid when T is an array type.");
@@ -1244,6 +1261,22 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI weak_ptr {
     return __cntrl_ < __r.__cntrl_;
   }
 
+#if _LIBCPP_STD_VER >= 26
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t owner_hash() const _NOEXCEPT {
+    return std::hash<__shared_weak_count*>()(__cntrl_);
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(shared_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+
+  template <class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool owner_equal(weak_ptr<_Up> const& __p) const _NOEXCEPT {
+    return __cntrl_ == __p.__cntrl_;
+  }
+#endif
+
   template <class _Up>
   friend class weak_ptr;
   template <class _Up>
@@ -1317,6 +1350,44 @@ struct owner_less<void> {
 };
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+struct owner_hash {
+  template <class _Tp>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t operator()(shared_ptr<_Tp> const& __p) const _NOEXCEPT {
+    return __p.owner_hash();
+  }
+  template <class _Tp>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t operator()(weak_ptr<_Tp> const& __p) const _NOEXCEPT {
+    return __p.owner_hash();
+  }
+  typedef void is_transparent;
+};
+
+struct owner_equal {
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(shared_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(shared_ptr<_Tp> const& __x, weak_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(weak_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  template <class _Tp, class _Up>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool
+  operator()(weak_ptr<_Tp> const& __x, weak_ptr<_Up> const& __y) const _NOEXCEPT {
+    return __x.owner_equal(__y);
+  }
+  typedef void is_transparent;
+};
+#endif
+
 template <class _Tp>
 class enable_shared_from_this {
   mutable weak_ptr<_Tp> __weak_this_;
diff --git a/libcxx/include/memory b/libcxx/include/memory
index d5d48ed9a9e6d..03636ddb839b0 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -633,6 +633,9 @@ public:
     explicit operator bool() const noexcept;
     template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
     template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;
+    size_t owner_hash() const noexcept;                                       // C++26
+    template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
+    template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;   // C++26
 };
 
 template<class T>
@@ -771,6 +774,9 @@ public:
     shared_ptr<T> lock() const noexcept;
     template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
     template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;
+    size_t owner_hash() const noexcept;                                       // C++26
+    template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
+    template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;   // C++26
 };
 
 template<class T>
@@ -817,6 +823,32 @@ struct owner_less<void>
     typedef void is_transparent;
 };
 
+// class owner_hash:                                                          // C++26
+struct owner_hash
+{
+    template <class _Tp>
+    size_t operator()(shared_ptr<_Tp> const& __p) const noexcept;
+    template <class _Tp>
+    size_t operator()(weak_ptr<_Tp> const& __p) const noexcept;
+
+    typedef void is_transparent;
+};
+
+// class owner_equal:                                                         // C++26
+struct owner_equal
+{
+    template <class _Tp, class _Up>
+    bool operator()(shared_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const noexcept;
+    template <class _Tp, class _Up>
+    bool operator()(shared_ptr<_Tp> const& __x,   weak_ptr<_Up> const& __y) const noexcept;
+    template <class _Tp, class _Up>
+    bool operator()(  weak_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) const noexcept;
+    template <class _Tp, class _Up>
+    bool operator()(  weak_ptr<_Tp> const& __x,   weak_ptr<_Up> const& __y) const noexcept;
+
+    typedef void is_transparent;
+};
+
 template<class T>
 class enable_shared_from_this
 {
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..31b5cc4048e18 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -628,7 +628,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_reference_wrapper                    202403L
 # define __cpp_lib_saturation_arithmetic                202603L
 // # define __cpp_lib_senders                              202406L
-// # define __cpp_lib_smart_ptr_owner_equality             202306L
+# define __cpp_lib_smart_ptr_owner_equality             202306L
 # define __cpp_lib_span_at                              202311L
 # define __cpp_lib_sstream_from_string_view             202306L
 # define __cpp_lib_string_subview                       202506L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
index c58357a463ffd..dc14ff766ac5c 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
@@ -648,17 +648,11 @@
 #    error "__cpp_lib_smart_ptr_for_overwrite should have the value 202002L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
-#    endif
-#    if __cpp_lib_smart_ptr_owner_equality != 202306L
-#      error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_smart_ptr_owner_equality
+#    error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
+#  endif
+#  if __cpp_lib_smart_ptr_owner_equality != 202306L
+#    error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_to_address
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 f2d74cc99d864..11b181a66c518 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
@@ -8024,17 +8024,11 @@
 #    error "__cpp_lib_smart_ptr_for_overwrite should have the value 202002L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
-#    endif
-#    if __cpp_lib_smart_ptr_owner_equality != 202306L
-#      error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_smart_ptr_owner_equality
-#      error "__cpp_lib_smart_ptr_owner_equality should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_smart_ptr_owner_equality
+#    error "__cpp_lib_smart_ptr_owner_equality should be defined in c++26"
+#  endif
+#  if __cpp_lib_smart_ptr_owner_equality != 202306L
+#    error "__cpp_lib_smart_ptr_owner_equality should have the value 202306L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_source_location
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp
new file mode 100644
index 0000000000000..0f8c6935ef811
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_shared_ptr.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// shared_ptr
+
+// template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::shared_ptr<void> empty1;
+  const std::shared_ptr<long> empty2;
+
+  assert(p1.owner_equal(p2));
+  assert(p2.owner_equal(p1));
+  assert(!p1.owner_equal(p3));
+  assert(!p3.owner_equal(p1));
+
+  assert(empty1.owner_equal(empty2));
+  assert(!p1.owner_equal(empty1));
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_equal(p2)), bool);
+  ASSERT_NOEXCEPT(p1.owner_equal(p2));
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.pass.cpp
new file mode 100644
index 0000000000000..6ac9f57e07a05
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_equal_weak_ptr.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <memory>
+
+// shared_ptr
+
+// template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<int> w3(p3);
+  const std::shared_ptr<void> empty_sp;
+  const std::weak_ptr<long> empty_wp;
+
+  assert(p1.owner_equal(w1));
+  assert(p2.owner_equal(w1));
+  assert(!p1.owner_equal(w3));
+  assert(!p3.owner_equal(w1));
+
+  assert(empty_sp.owner_equal(empty_wp));
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_equal(w1)), bool);
+  ASSERT_NOEXCEPT(p1.owner_equal(w1));
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp
new file mode 100644
index 0000000000000..57967b9369fe3
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.obs/owner_hash.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// shared_ptr
+
+// size_t owner_hash() const noexcept;
+
+#include <memory>
+#include <cassert>
+#include <cstddef>
+#include "test_macros.h"
+
+struct Pair {
+  int a;
+  int b;
+};
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::weak_ptr<int> w1(p1);
+
+  assert(p1.owner_hash() == p2.owner_hash());
+  assert(p1.owner_hash() == w1.owner_hash());
+
+  const std::shared_ptr<Pair> sp(new Pair{1, 2});
+  const std::shared_ptr<int> alias(sp, &sp->b);
+  assert(static_cast<void*>(sp.get()) != static_cast<void*>(alias.get()));
+  assert(sp.owner_hash() == alias.owner_hash());
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_hash()), std::size_t);
+  ASSERT_NOEXCEPT(p1.owner_hash());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp
new file mode 100644
index 0000000000000..1c6c9e8bd5d67
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerequal/owner_equal.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// struct owner_equal
+// {
+//     template<class T, class U>
+//         bool operator()(shared_ptr<T> const&, shared_ptr<U> const&) const noexcept;
+//     template<class T, class U>
+//         bool operator()(shared_ptr<T> const&, weak_ptr<U> const&) const noexcept;
+//     template<class T, class U>
+//         bool operator()(weak_ptr<T> const&, shared_ptr<U> const&) const noexcept;
+//     template<class T, class U>
+//         bool operator()(weak_ptr<T> const&, weak_ptr<U> const&) const noexcept;
+//
+//     typedef unspecified is_transparent;
+// };
+
+#include <memory>
+#include <cassert>
+#include <type_traits>
+#include <unordered_set>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<int> w3(p3);
+
+  std::owner_equal oe;
+
+  assert(oe(p1, p2));
+  assert(oe(p1, w1));
+  assert(oe(w1, p1));
+  assert(oe(w1, w1));
+
+  assert(!oe(p1, p3));
+  assert(!oe(p1, w3));
+  assert(!oe(w1, p3));
+  assert(!oe(w1, w3));
+
+  ASSERT_SAME_TYPE(decltype(oe(p1, p2)), bool);
+  ASSERT_NOEXCEPT(oe(p1, p2));
+  ASSERT_NOEXCEPT(oe(p1, w1));
+  ASSERT_NOEXCEPT(oe(w1, p1));
+  ASSERT_NOEXCEPT(oe(w1, w1));
+
+  static_assert(std::is_same<std::owner_equal::is_transparent, void>::value, "");
+
+  {
+    std::unordered_set<std::weak_ptr<int>, std::owner_hash, std::owner_equal> s;
+    s.insert(w1);
+    assert(s.count(w1) == 1);
+    assert(s.count(std::weak_ptr<int>(p2)) == 1);
+    assert(s.count(w3) == 0);
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerhash/owner_hash.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerhash/owner_hash.pass.cpp
new file mode 100644
index 0000000000000..0aaa622246023
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.ownerhash/owner_hash.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <memory>
+
+// struct owner_hash
+// {
+//     template<class T>
+//         size_t operator()(shared_ptr<T> const&) const noexcept;
+//     template<class T>
+//         size_t operator()(weak_ptr<T> const&) const noexcept;
+//
+//     typedef unspecified is_transparent;
+// };
+
+#include <memory>
+#include <cassert>
+#include <type_traits>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::weak_ptr<int> w1(p1);
+
+  std::owner_hash oh;
+
+  assert(oh(p1) == p1.owner_hash());
+  assert(oh(w1) == w1.owner_hash());
+
+  assert(oh(p1) == oh(p2));
+  assert(oh(p1) == oh(w1));
+
+  ASSERT_SAME_TYPE(decltype(oh(p1)), std::size_t);
+  ASSERT_SAME_TYPE(decltype(oh(w1)), std::size_t);
+  ASSERT_NOEXCEPT(oh(p1));
+  ASSERT_NOEXCEPT(oh(w1));
+
+  static_assert(std::is_same<std::owner_hash::is_transparent, void>::value, "");
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_shared_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_shared_ptr.pass.cpp
new file mode 100644
index 0000000000000..35c97a72c8c3c
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_shared_ptr.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// weak_ptr
+
+// template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p3(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<void> empty;
+
+  assert(w1.owner_equal(p1));
+  assert(!w1.owner_equal(p3));
+
+  const std::shared_ptr<long> empty_sp;
+  assert(empty.owner_equal(empty_sp));
+
+  ASSERT_SAME_TYPE(decltype(w1.owner_equal(p1)), bool);
+  ASSERT_NOEXCEPT(w1.owner_equal(p1));
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_weak_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_weak_ptr.pass.cpp
new file mode 100644
index 0000000000000..bfd4628edd364
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_equal_weak_ptr.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// weak_ptr
+
+// template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p3(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<int> w2(p1);
+  const std::weak_ptr<int> w3(p3);
+  const std::weak_ptr<void> empty1;
+  const std::weak_ptr<long> empty2;
+
+  assert(w1.owner_equal(w2));
+  assert(w2.owner_equal(w1));
+  assert(!w1.owner_equal(w3));
+  assert(!w3.owner_equal(w1));
+
+  assert(empty1.owner_equal(empty2));
+  assert(!w1.owner_equal(empty1));
+
+  ASSERT_SAME_TYPE(decltype(w1.owner_equal(w2)), bool);
+  ASSERT_NOEXCEPT(w1.owner_equal(w2));
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_hash.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_hash.pass.cpp
new file mode 100644
index 0000000000000..772344f1b05de
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.weak/util.smartptr.weak.obs/owner_hash.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <memory>
+
+// weak_ptr
+
+// size_t owner_hash() const noexcept;
+
+#include <memory>
+#include <cassert>
+#include <cstddef>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::weak_ptr<int> w1(p1);
+  const std::weak_ptr<int> w2(p1);
+
+  assert(w1.owner_hash() == w2.owner_hash());
+  assert(w1.owner_hash() == p1.owner_hash());
+
+  ASSERT_SAME_TYPE(decltype(w1.owner_hash()), std::size_t);
+  ASSERT_NOEXCEPT(w1.owner_hash());
+
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..56726e485493e 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1316,7 +1316,6 @@ def add_version_header(tc):
                 "c++26": 202306  # P1901R2 Enabling the Use of weak_ptr as Keys in Unordered Associative Containers
             },
             "headers": ["memory"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_source_location",


From libcxx-commits at lists.llvm.org  Tue Jul 21 19:59:53 2026
From: libcxx-commits at lists.llvm.org (Avi Patel via libcxx-commits)
Date: Tue, 21 Jul 2026 19:59:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a603229.f06f4994.50b1b.ae02@mx.google.com>


firedog1234 wrote:

@frederick-vs-ja  Would you be able to review this implementation? Thanks?

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Tue Jul 21 23:16:36 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 21 Jul 2026 23:16:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a606044.eb165e5c.2e0c9a.ddb0@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From f81e6a6f9200d1e114201290b17587f02c20eaf2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/19] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From 156f6f8298622067ee8717dc3a84c197e32ab175 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/19] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 463a77c6efb5fbf4cffd4be0dc3331520f93f39c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/19] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From a9b71cb5a78f6f6f6d634195c77e2b5d8bc486f5 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/19] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 0e2f1cd3cd56fd8aa62d28fa31ee51a5debb3935 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/19] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 2f92ae8766eabd5f92917bfad30ca3e02c5de84a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/19] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 2cb082799c25d82c9342ee51160ef38bc8e03e3a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/19] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From d2243047a22b62ed633eb879e31bbd6780cd3181 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/19] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From d5ad73d9c7f4062f1b50eeb4add63e4366fa3f00 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/19] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 4eba2824c648d85429256a0fae120931ff56147d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/19] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index a77e98f845a06..425c13fea707e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -39,7 +39,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +47,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +63,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -81,7 +81,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index a8bc817ced291..ce81d1c00d376 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From 6aa16eb678b260549394cd25ca405516ba85f93d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/19] Format

---
 libcxx/docs/Status/Cxx26Papers.csv                |  2 +-
 .../range.elements/iterator/base.pass.cpp         | 15 ++++++++-------
 .../range.elements/sentinel/base.pass.cpp         |  2 +-
 .../range.split/iterator/base.pass.cpp            | 10 +++++-----
 .../range.split/iterator/deref.pass.cpp           |  7 +++----
 .../sentinel/ctor.convert.pass.cpp                |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..ffa36d3f5145c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 425c13fea707e..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -81,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From 6d0ae1b24e97c93f07c0f71080eebaf9dd2ccb01 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/19] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..052e5eab25433 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index ffa36d3f5145c..4d0fbca940b92 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 197dc412359324c40e27a81dc0976a098fa78187 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/19] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From 8190cbcadce534b72759e9c26d34535fac4257f3 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/19] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp               | 16 ++++++++++++----
 .../sentinel/ctor.convert.pass.cpp             | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index ce81d1c00d376..25d1413b2fa16 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -67,15 +67,21 @@ static_assert(
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From cf21399e152311c7555d384b4e942af19374ccf0 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/19] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From e3ee97abdd6316f8ee21b852dd1f1b15f1bae1a6 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/19] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}

>From 123dc9cdec024eee0e89a429f30e167fff8a5b4a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:05:55 +0300
Subject: [PATCH 17/19] Fixed formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index e28c36b1254af..bcce8e1f48f7b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,8 +22,7 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
+concept IsBaseNoexcept = requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From db4279e8d1e0d4ea60b6ed3163580f644939859f Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:18:55 +0300
Subject: [PATCH 18/19] Enable test

---
 .../ranges/range.adaptors/range.elements/sentinel/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index 885582a5c94a8..114b7fc1f1b71 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -37,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
   return 0;
 }

>From db256d5ef05fc8996ac19bf952af706398e8cdf9 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 07:53:40 +0300
Subject: [PATCH 19/19] Added `std::is_constructible/convertible` tests

---
 .../iterator/ctor.base.compile.pass.cpp       | 23 ++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 30 ++++++++++++++++
 .../ctor.parent.iter.compile.pass.cpp         | 25 +++++++++++++
 .../sentinel/ctor.parent.compile.pass.cpp     | 25 +++++++++++++
 .../ctor.outer_iterator.compile.pass.cpp      | 23 ++++++++++++
 .../ctor.parent.base.compile.pass.cpp         | 22 ++++++++++++
 .../ctor.parent.compile.pass.cpp              | 23 ++++++++++++
 .../sentinel/ctor.base.pred.compile.pass.cpp  | 35 +++++++++++++++++++
 .../iterator/ctor.value.compile.pass.cpp      | 26 ++++++++++++++
 .../sentinel/ctor.value.compile.pass.cpp      | 24 +++++++++++++
 .../iterator/ctor.parent.compile.pass.cpp     | 33 +++++++++++++++++
 11 files changed, 289 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..b43a60276e280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+using BaseIter     = std::tuple<int>*;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
+
+static_assert(!std::is_constructible_v<ElementsIter, BaseIter>);
+static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..ee4a297c1f4a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+  std::tuple<int>* begin() const;
+  Sent end();
+};
+
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..4a013d0072222
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..06e06aeb6db6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(filter_view&);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterSentinel, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterSentinel>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..37fe2fadfd41a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<InnerIterConst, OuterIterConst>);
+static_assert(!std::is_convertible_v<InnerIterConst, OuterIterConst>);
+
+static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterNonConst>);
+static_assert(!std::is_convertible_v<InnerIterNonConst, OuterIterNonConst>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
new file mode 100644
index 0000000000000..70e97ea04f5c0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
+//   requires forward_range<Base>
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(std::ranges::forward_range<SplitViewForward>);
+static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&, std::ranges::iterator_t<ForwardView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..b838970718c8a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
+//   requires (!forward_range<Base>)
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(!std::ranges::forward_range<SplitViewInput>);
+static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&>);
+static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
new file mode 100644
index 0000000000000..a66e3d1e84c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+  int* begin() const;
+  Sent end();
+};
+
+struct Pred {
+  bool operator()(int i) const;
+};
+
+using Sentinel = std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>;
+
+static_assert(!std::is_constructible_v<Sentinel, std::ranges::sentinel_t<Range>, const Pred*>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..e95042e26e146
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(W value);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
+
+using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
+static_assert(!std::is_constructible_v<Iter, SomeInt>);
+static_assert(!std::is_convertible_v<SomeInt, Iter>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..a39efb4318794
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.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++20
+
+// constexpr explicit sentinel(Bound bound);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
+static_assert(!std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
+static_assert(!std::is_convertible_v<IntSentinelWith<SomeInt>, Sent>);
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..171e2dce1e5f0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(basic_istream_view& parent) noexcept;
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <sstream>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// test that the constructor is explicit
+template <class CharT>
+using IstreamView = std::ranges::basic_istream_view<int, CharT>;
+template <class CharT>
+using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
+
+static_assert(!std::constructible_from<Iter<char>, IstreamView<char>&>);
+static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
+static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
+#endif


From libcxx-commits at lists.llvm.org  Tue Jul 21 23:18:17 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 21 Jul 2026 23:18:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a6060a9.13be6df8.323b61.b916@mx.google.com>


================
@@ -82,7 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
----------------
Zingam wrote:

> I think adding `static_assert`'s should be in the scope of this PR. I'm not familiar with stacked PRs, though.

Done

https://github.com/llvm/llvm-project/pull/193891

From libcxx-commits at lists.llvm.org  Tue Jul 21 23:21:25 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Tue, 21 Jul 2026 23:21:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a606165.5a0412d2.325e39.8525@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From f81e6a6f9200d1e114201290b17587f02c20eaf2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/20] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From 156f6f8298622067ee8717dc3a84c197e32ab175 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/20] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 463a77c6efb5fbf4cffd4be0dc3331520f93f39c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/20] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From a9b71cb5a78f6f6f6d634195c77e2b5d8bc486f5 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/20] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 0e2f1cd3cd56fd8aa62d28fa31ee51a5debb3935 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/20] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 2f92ae8766eabd5f92917bfad30ca3e02c5de84a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/20] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 2cb082799c25d82c9342ee51160ef38bc8e03e3a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/20] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From d2243047a22b62ed633eb879e31bbd6780cd3181 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/20] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From d5ad73d9c7f4062f1b50eeb4add63e4366fa3f00 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/20] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 4eba2824c648d85429256a0fae120931ff56147d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/20] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index a77e98f845a06..425c13fea707e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -39,7 +39,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +47,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +63,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -81,7 +81,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index a8bc817ced291..ce81d1c00d376 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From 6aa16eb678b260549394cd25ca405516ba85f93d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/20] Format

---
 libcxx/docs/Status/Cxx26Papers.csv                |  2 +-
 .../range.elements/iterator/base.pass.cpp         | 15 ++++++++-------
 .../range.elements/sentinel/base.pass.cpp         |  2 +-
 .../range.split/iterator/base.pass.cpp            | 10 +++++-----
 .../range.split/iterator/deref.pass.cpp           |  7 +++----
 .../sentinel/ctor.convert.pass.cpp                |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..ffa36d3f5145c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 425c13fea707e..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -81,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From 6d0ae1b24e97c93f07c0f71080eebaf9dd2ccb01 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/20] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..052e5eab25433 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index ffa36d3f5145c..4d0fbca940b92 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 197dc412359324c40e27a81dc0976a098fa78187 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/20] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From 8190cbcadce534b72759e9c26d34535fac4257f3 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/20] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp               | 16 ++++++++++++----
 .../sentinel/ctor.convert.pass.cpp             | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index ce81d1c00d376..25d1413b2fa16 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -67,15 +67,21 @@ static_assert(
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From cf21399e152311c7555d384b4e942af19374ccf0 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/20] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From e3ee97abdd6316f8ee21b852dd1f1b15f1bae1a6 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/20] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}

>From 123dc9cdec024eee0e89a429f30e167fff8a5b4a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:05:55 +0300
Subject: [PATCH 17/20] Fixed formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index e28c36b1254af..bcce8e1f48f7b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,8 +22,7 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
+concept IsBaseNoexcept = requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From db4279e8d1e0d4ea60b6ed3163580f644939859f Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:18:55 +0300
Subject: [PATCH 18/20] Enable test

---
 .../ranges/range.adaptors/range.elements/sentinel/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index 885582a5c94a8..114b7fc1f1b71 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -37,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
   return 0;
 }

>From db256d5ef05fc8996ac19bf952af706398e8cdf9 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 07:53:40 +0300
Subject: [PATCH 19/20] Added `std::is_constructible/convertible` tests

---
 .../iterator/ctor.base.compile.pass.cpp       | 23 ++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 30 ++++++++++++++++
 .../ctor.parent.iter.compile.pass.cpp         | 25 +++++++++++++
 .../sentinel/ctor.parent.compile.pass.cpp     | 25 +++++++++++++
 .../ctor.outer_iterator.compile.pass.cpp      | 23 ++++++++++++
 .../ctor.parent.base.compile.pass.cpp         | 22 ++++++++++++
 .../ctor.parent.compile.pass.cpp              | 23 ++++++++++++
 .../sentinel/ctor.base.pred.compile.pass.cpp  | 35 +++++++++++++++++++
 .../iterator/ctor.value.compile.pass.cpp      | 26 ++++++++++++++
 .../sentinel/ctor.value.compile.pass.cpp      | 24 +++++++++++++
 .../iterator/ctor.parent.compile.pass.cpp     | 33 +++++++++++++++++
 11 files changed, 289 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..b43a60276e280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+using BaseIter     = std::tuple<int>*;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
+
+static_assert(!std::is_constructible_v<ElementsIter, BaseIter>);
+static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..ee4a297c1f4a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+  std::tuple<int>* begin() const;
+  Sent end();
+};
+
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..4a013d0072222
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..06e06aeb6db6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(filter_view&);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterSentinel, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterSentinel>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..37fe2fadfd41a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<InnerIterConst, OuterIterConst>);
+static_assert(!std::is_convertible_v<InnerIterConst, OuterIterConst>);
+
+static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterNonConst>);
+static_assert(!std::is_convertible_v<InnerIterNonConst, OuterIterNonConst>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
new file mode 100644
index 0000000000000..70e97ea04f5c0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
+//   requires forward_range<Base>
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(std::ranges::forward_range<SplitViewForward>);
+static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&, std::ranges::iterator_t<ForwardView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..b838970718c8a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
+//   requires (!forward_range<Base>)
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(!std::ranges::forward_range<SplitViewInput>);
+static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&>);
+static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
new file mode 100644
index 0000000000000..a66e3d1e84c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+  int* begin() const;
+  Sent end();
+};
+
+struct Pred {
+  bool operator()(int i) const;
+};
+
+using Sentinel = std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>;
+
+static_assert(!std::is_constructible_v<Sentinel, std::ranges::sentinel_t<Range>, const Pred*>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..e95042e26e146
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(W value);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
+
+using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
+static_assert(!std::is_constructible_v<Iter, SomeInt>);
+static_assert(!std::is_convertible_v<SomeInt, Iter>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..a39efb4318794
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.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++20
+
+// constexpr explicit sentinel(Bound bound);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
+static_assert(!std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
+static_assert(!std::is_convertible_v<IntSentinelWith<SomeInt>, Sent>);
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..171e2dce1e5f0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(basic_istream_view& parent) noexcept;
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <sstream>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// test that the constructor is explicit
+template <class CharT>
+using IstreamView = std::ranges::basic_istream_view<int, CharT>;
+template <class CharT>
+using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
+
+static_assert(!std::constructible_from<Iter<char>, IstreamView<char>&>);
+static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
+static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
+#endif

>From 7ff606098cbdd0f73abc4a8211325f78e3e3ffbc Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 09:20:54 +0300
Subject: [PATCH 20/20] Cleanup

---
 .../range.iota.view/iterator/ctor.value.compile.pass.cpp        | 2 --
 .../range.iota.view/sentinel/ctor.value.compile.pass.cpp        | 2 --
 2 files changed, 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
index e95042e26e146..fc3384e191d1d 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
index a39efb4318794..b0bcdb6c60ce7 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;


From libcxx-commits at lists.llvm.org  Tue Jul 21 23:57:05 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Tue, 21 Jul 2026 23:57:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::mismatch() based on __pstl::__parallel_find() (PR #209291)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209291@github.com>
Message-ID: <6a6069c1.7d702ed2.2f6c05.4f41@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/209291

>From 31208d7946efae8b2e45a121c5178562a7b639c8 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:03:38 +0100
Subject: [PATCH 01/15] Added a basic implementation of parallel 4-legged
 mismatch() based on __parallel_find()

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/libdispatch.h  |   7 +-
 libcxx/include/__pstl/backends/serial.h       |  20 +++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  88 +++++++++++
 .../mismatch/pstl.mismatch.pass.cpp           | 139 ++++++++++++++++++
 7 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__pstl/cpu_algos/mismatch.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..6b5dc87fbe76d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/mismatch.h  
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 2ba5b4434fc33..aa7633a240722 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -324,6 +324,14 @@ struct __adjacent_difference;
 // operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index f7141d24df090..5e89c6fe31cd8 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -37,6 +37,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -241,7 +242,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
       auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
       auto __index           = __chunk == 0 ? 0
                                             : (__chunk * __partitions.__chunk_size_) +
-                                        (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
+                                                  (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
       if (__this_chunk_size != 1) {
         std::__construct_at(
             __values.get() + __chunk,
@@ -368,6 +369,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..d90022ee37145 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/mismatch.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    return std::mismatch(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::forward<_Predicate>(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..93935d22b9442 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
new file mode 100644
index 0000000000000..11ffdc332d633
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_MISMATCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
+
+#include <__algorithm/mismatch.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_mismatch {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Look for a mismatch only in the prefix of the two ranges.
+      auto __n = std::min(__last1 - __first1, __last2 - __first2);
+      // Find a position in the first range where the predicate is false against the corresponding position in the
+      // second range.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __first1 + __n,
+          [&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
+            // Run the sequential mismatch algorithm on these ranges:
+            //   [__brick_first1, __brick_last1) and
+            //   [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
+            auto __brick_first2 = __first2 + (__brick_first1 - __first1);
+            return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
+                .first;
+          },
+          less<>{},
+          true);
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      auto __idx = *__res - __first1;
+      return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      // Unsequenced execution is also implicitly covered by the sequential implementation.
+      return std::mismatch(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
new file mode 100644
index 0000000000000..9cbadda428ad9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 1};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(42);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From ddbcca0bf2d23ac5caaed98c0912016bb23c7640 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:05:01 +0100
Subject: [PATCH 02/15] Added a definition of a parallel 4-legged
 std::mismatch()

---
 libcxx/include/__algorithm/pstl.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2bf9b4276baab..ab35b1a3e537b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -138,6 +138,29 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,

>From 81cdb4aa0a748c2a8039e0c478c19259860513a7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:41:51 +0100
Subject: [PATCH 03/15] Added a default implementation of __mismatch_3leg based
 on __mismatch

---
 libcxx/include/__algorithm/pstl.h             | 18 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++++
 libcxx/include/__pstl/backends/default.h      | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../mismatch/pstl.mismatch.pass.cpp           | 20 +++++++++++
 5 files changed, 84 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index ab35b1a3e537b..01c61fb00f4d5 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -161,6 +161,24 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index aa7633a240722..ecd1f0b9b4049 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -332,6 +332,13 @@ struct __mismatch;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch_3leg;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 28bc75be147aa..b6a15b234b8da 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -63,6 +63,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// mismatch family
+// ---------------
+// - mismatch_3leg
+//
 // for_each family
 // ---------------
 // - for_each_n
@@ -212,6 +216,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// mismatch family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+  optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _Comp __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Forward to the 4-legged version of mismatch.
+      using _Mismatch           = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      _ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
+      return _Mismatch()(
+          __policy,
+          std::move(__first1),
+          std::move(__last1),
+          std::move(__first2),
+          std::move(__last2),
+          std::move(__comp));
+    } else {
+      // Currently only random access iterators are supported for parallel mismatch_3leg.
+      return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..752aa35643e2b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2384,6 +2384,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module mismatch {
+        header "__pstl/cpu_algos/mismatch.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 9cbadda428ad9..0cb5c7632eed8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -10,6 +10,14 @@
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2);
+//
 // template <class ExecutionPolicy,
 //           class ForwardIterator1,
 //           class ForwardIterator2>
@@ -61,6 +69,8 @@ struct Test {
       // empty ranges
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
              std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
     }
@@ -68,18 +78,24 @@ struct Test {
       // single element only
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range without mismatch
       std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range with mismatch
       std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
     }
@@ -102,10 +118,14 @@ struct Test {
       rhs = lhs;
       runway_sample(lhs.size(), [&](size_t i) {
         lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
                std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         lhs[i] = i;
       });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }

>From d5910cc36d457006d17cf1adad74a94ee5a4e759 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:20:42 +0100
Subject: [PATCH 04/15] Added predicated versions

---
 libcxx/include/__algorithm/pstl.h             |  49 +++++
 .../mismatch/pstl.mismatch.pass.cpp           |   2 +
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 178 ++++++++++++++++++
 3 files changed, 229 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 01c61fb00f4d5..bc48fd9f48d7f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
+#  include <__utility/pair.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -138,6 +139,31 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
@@ -161,6 +187,29 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 0cb5c7632eed8..8584f2779927f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -42,6 +42,8 @@
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
+static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
new file mode 100644
index 0000000000000..39e952cbc38f6
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     BinaryPredicate pred);
+//
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2,
+//                                                     BinaryPredicate pred);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+struct Pred {
+  bool operator()(int a, int b) const { return a * 2 == b; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      assert(std::mismatch(
+                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {1};
+      std::array<int, 1> rhs = {2};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 2};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(84);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From 9b207d1481c9076b4bb2af97d049845f2c4e16e0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:39:47 +0100
Subject: [PATCH 05/15] Added tests against custom types

---
 libcxx/include/__pstl/backends/default.h      |  1 +
 .../mismatch/pstl.mismatch.pass.cpp           | 53 ++++++++++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 56 ++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index b6a15b234b8da..776c76f3fdfbd 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -16,6 +16,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 8584f2779927f..5e46831539c30 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -63,6 +63,30 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
+
 template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
@@ -150,6 +174,29 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -157,5 +204,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 39e952cbc38f6..9c0dfc6c0c127 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -67,8 +67,31 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
 struct Pred {
-  bool operator()(int a, int b) const { return a * 2 == b; }
+  bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
+  bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
 };
 
 template <class Iter1, class Iter2>
@@ -167,6 +190,31 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -174,5 +222,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }

>From c21c583527e255d722f312b871d43527e5c4dbb6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:52:50 +0100
Subject: [PATCH 06/15] Added a nodiscard test

---
 libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index f4fffd456a331..f5eaf1642b687 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -63,4 +63,12 @@ void test() {
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }

>From 189ba7a5f23d45939e0ff917985bc7049740d1b7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:57:47 +0100
Subject: [PATCH 07/15] Added an iterator requirement test

---
 .../pstl.iterator-requirements.verify.cpp        | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 402d8fbf9131a..df11341008bc5 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, pred);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}

>From 731de4a2cf2c196b704234b0ee5653453554b9b7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:00:09 +0100
Subject: [PATCH 08/15] Added an include of <__algorithm/min.h>

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 11ffdc332d633..8361731e78cfc 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,8 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__algorithm/mismatch.h>
 #include <__config>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/min.h>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>

>From 105a17ed5abc39ad5ac34bd930af8727a9562bed Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:05:29 +0100
Subject: [PATCH 09/15] Added an exception handling test

---
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  4 +--
 .../pstl.exception_handling.pass.cpp          | 25 +++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 8361731e78cfc..597a47fb185c8 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,9 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__config>
-#include <__algorithm/mismatch.h>
 #include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__config>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index e00046fdb1dd6..cbdb4663f168c 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -248,6 +248,31 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // mismatch(first1, last1, first2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
+        });
+
+        // mismatch(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // mismatch(first1, last1, first2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
+        });
+
+        // mismatch(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From 3bd32433db95c6995476e26d5b34c8000a5515f5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 09:43:45 +0100
Subject: [PATCH 10/15] Changed the variable name to avoid shadowing

---
 .../alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 12 ++++++------
 .../mismatch/pstl.mismatch_pred.pass.cpp             | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 5e46831539c30..941fe07b58327 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -67,22 +67,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 9c0dfc6c0c127..a4b15a56bd071 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -71,22 +71,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Pred {

>From a51af3556cdb7f7e24732077b6dce511e3ee9e89 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 20:27:14 +0100
Subject: [PATCH 11/15] Use raw C array in the tests

---
 .../mismatch/pstl.mismatch.pass.cpp           | 153 +++++++-------
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 192 +++++++++++-------
 2 files changed, 198 insertions(+), 147 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 941fe07b58327..17edf34ee7832 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -28,7 +28,6 @@
 //                                                     ForwardIterator2 last2);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -93,82 +92,98 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(
+          std::mismatch(
+              policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
+          std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 1};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 1};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(
+            std::mismatch(
+                policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+            std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(42);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 42);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(
+                   policy,
+                   Iter1(std::begin(lhs)),
+                   Iter1(std::begin(lhs) + i),
+                   Iter2(std::begin(rhs)),
+                   Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i)) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -179,20 +194,22 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index a4b15a56bd071..18c4fe1abad0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -32,7 +32,6 @@
 //                                                     BinaryPredicate pred);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -100,91 +99,120 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(
-                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::begin(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::begin(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {1};
-      std::array<int, 1> rhs = {2};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {1};
+      int rhs[1] = {2};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 2};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 2};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(84);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 84);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::begin(lhs) + i),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -195,22 +223,28 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };

>From f3bfa5528fb2aea972a018cc68e56133e32db99b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:08:55 +0100
Subject: [PATCH 12/15] Fixed typos in the comment

---
 .../algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 2 +-
 .../alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 17edf34ee7832..ed59fa9eca669 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -46,7 +46,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 18c4fe1abad0c..338503d784a31 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -50,7 +50,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;

>From 4c7fccd82c3a4bea33038c9eee3ebcefddd7f230 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:43:20 +0100
Subject: [PATCH 13/15] Added comments explaining what the parameters mean

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 597a47fb185c8..3d702b03d3afe 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -63,8 +63,9 @@ struct __cpu_parallel_mismatch {
             return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
                 .first;
           },
-          less<>{},
-          true);
+          less<>{}, // `less` here means the lowest index among the mismatches
+          true      // `true` here means we want the first mismatch, not the last
+      );
       if (!__res) {
         return std::nullopt; // Failed to run the algorithm, propagate the error.
       }

>From 22dfeede2c12c49a9f57db064f80e6d24e96f41b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:50:51 +0100
Subject: [PATCH 14/15] Added tests of the return types

---
 .../mismatch/pstl.mismatch.pass.cpp              | 10 ++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp         | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index ed59fa9eca669..cc7ef6feb2356 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -90,6 +90,16 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1]       = {0};
+      int rhs[1]       = {0};
+      auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
+      auto res_4legged = std::mismatch(
+          policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 338503d784a31..d80da0cf0b542 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -97,6 +97,22 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      auto res_3legged =
+          std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
+      auto res_4legged = std::mismatch(
+          policy,
+          Iter1(std::begin(lhs)),
+          Iter1(std::begin(lhs)),
+          Iter2(std::begin(rhs)),
+          Iter2(std::begin(rhs)),
+          Pred{});
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};

>From 8f9ddba49cfc2b501d8d42b1c8027f64b0695a31 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 21 Jul 2026 19:39:00 +0100
Subject: [PATCH 15/15] Rebased on support/runway_sample.h

---
 .../mismatch/pstl.mismatch.pass.cpp             | 17 +----------------
 .../mismatch/pstl.mismatch_pred.pass.cpp        | 17 +----------------
 2 files changed, 2 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index cc7ef6feb2356..83f4b0f16d6c1 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -38,6 +38,7 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
@@ -46,22 +47,6 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 // The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
 
 struct X {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index d80da0cf0b542..89cf899a47b4c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -42,6 +42,7 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
@@ -50,22 +51,6 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 // The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
 
 struct X {


From libcxx-commits at lists.llvm.org  Tue Jul 21 23:57:40 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Tue, 21 Jul 2026 23:57:40 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::adjacent_find() based on std::mismatch() (PR #210604)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210604@github.com>
Message-ID: <6a6069e4.e624eb8d.12f676.9465@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/210604

>From 31208d7946efae8b2e45a121c5178562a7b639c8 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:03:38 +0100
Subject: [PATCH 01/19] Added a basic implementation of parallel 4-legged
 mismatch() based on __parallel_find()

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/libdispatch.h  |   7 +-
 libcxx/include/__pstl/backends/serial.h       |  20 +++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  88 +++++++++++
 .../mismatch/pstl.mismatch.pass.cpp           | 139 ++++++++++++++++++
 7 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__pstl/cpu_algos/mismatch.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..6b5dc87fbe76d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/mismatch.h  
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 2ba5b4434fc33..aa7633a240722 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -324,6 +324,14 @@ struct __adjacent_difference;
 // operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index f7141d24df090..5e89c6fe31cd8 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -37,6 +37,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -241,7 +242,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
       auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
       auto __index           = __chunk == 0 ? 0
                                             : (__chunk * __partitions.__chunk_size_) +
-                                        (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
+                                                  (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
       if (__this_chunk_size != 1) {
         std::__construct_at(
             __values.get() + __chunk,
@@ -368,6 +369,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..d90022ee37145 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/mismatch.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    return std::mismatch(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::forward<_Predicate>(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..93935d22b9442 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
new file mode 100644
index 0000000000000..11ffdc332d633
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_MISMATCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
+
+#include <__algorithm/mismatch.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_mismatch {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Look for a mismatch only in the prefix of the two ranges.
+      auto __n = std::min(__last1 - __first1, __last2 - __first2);
+      // Find a position in the first range where the predicate is false against the corresponding position in the
+      // second range.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __first1 + __n,
+          [&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
+            // Run the sequential mismatch algorithm on these ranges:
+            //   [__brick_first1, __brick_last1) and
+            //   [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
+            auto __brick_first2 = __first2 + (__brick_first1 - __first1);
+            return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
+                .first;
+          },
+          less<>{},
+          true);
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      auto __idx = *__res - __first1;
+      return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      // Unsequenced execution is also implicitly covered by the sequential implementation.
+      return std::mismatch(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
new file mode 100644
index 0000000000000..9cbadda428ad9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 1};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(42);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From ddbcca0bf2d23ac5caaed98c0912016bb23c7640 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:05:01 +0100
Subject: [PATCH 02/19] Added a definition of a parallel 4-legged
 std::mismatch()

---
 libcxx/include/__algorithm/pstl.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2bf9b4276baab..ab35b1a3e537b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -138,6 +138,29 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,

>From 81cdb4aa0a748c2a8039e0c478c19259860513a7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:41:51 +0100
Subject: [PATCH 03/19] Added a default implementation of __mismatch_3leg based
 on __mismatch

---
 libcxx/include/__algorithm/pstl.h             | 18 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++++
 libcxx/include/__pstl/backends/default.h      | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../mismatch/pstl.mismatch.pass.cpp           | 20 +++++++++++
 5 files changed, 84 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index ab35b1a3e537b..01c61fb00f4d5 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -161,6 +161,24 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index aa7633a240722..ecd1f0b9b4049 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -332,6 +332,13 @@ struct __mismatch;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch_3leg;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 28bc75be147aa..b6a15b234b8da 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -63,6 +63,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// mismatch family
+// ---------------
+// - mismatch_3leg
+//
 // for_each family
 // ---------------
 // - for_each_n
@@ -212,6 +216,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// mismatch family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+  optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _Comp __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Forward to the 4-legged version of mismatch.
+      using _Mismatch           = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      _ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
+      return _Mismatch()(
+          __policy,
+          std::move(__first1),
+          std::move(__last1),
+          std::move(__first2),
+          std::move(__last2),
+          std::move(__comp));
+    } else {
+      // Currently only random access iterators are supported for parallel mismatch_3leg.
+      return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..752aa35643e2b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2384,6 +2384,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module mismatch {
+        header "__pstl/cpu_algos/mismatch.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 9cbadda428ad9..0cb5c7632eed8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -10,6 +10,14 @@
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2);
+//
 // template <class ExecutionPolicy,
 //           class ForwardIterator1,
 //           class ForwardIterator2>
@@ -61,6 +69,8 @@ struct Test {
       // empty ranges
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
              std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
     }
@@ -68,18 +78,24 @@ struct Test {
       // single element only
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range without mismatch
       std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range with mismatch
       std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
     }
@@ -102,10 +118,14 @@ struct Test {
       rhs = lhs;
       runway_sample(lhs.size(), [&](size_t i) {
         lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
                std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         lhs[i] = i;
       });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }

>From d5910cc36d457006d17cf1adad74a94ee5a4e759 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:20:42 +0100
Subject: [PATCH 04/19] Added predicated versions

---
 libcxx/include/__algorithm/pstl.h             |  49 +++++
 .../mismatch/pstl.mismatch.pass.cpp           |   2 +
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 178 ++++++++++++++++++
 3 files changed, 229 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 01c61fb00f4d5..bc48fd9f48d7f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
+#  include <__utility/pair.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -138,6 +139,31 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
@@ -161,6 +187,29 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 0cb5c7632eed8..8584f2779927f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -42,6 +42,8 @@
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
+static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
new file mode 100644
index 0000000000000..39e952cbc38f6
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     BinaryPredicate pred);
+//
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2,
+//                                                     BinaryPredicate pred);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+struct Pred {
+  bool operator()(int a, int b) const { return a * 2 == b; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      assert(std::mismatch(
+                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {1};
+      std::array<int, 1> rhs = {2};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 2};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(84);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From 9b207d1481c9076b4bb2af97d049845f2c4e16e0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:39:47 +0100
Subject: [PATCH 05/19] Added tests against custom types

---
 libcxx/include/__pstl/backends/default.h      |  1 +
 .../mismatch/pstl.mismatch.pass.cpp           | 53 ++++++++++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 56 ++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index b6a15b234b8da..776c76f3fdfbd 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -16,6 +16,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 8584f2779927f..5e46831539c30 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -63,6 +63,30 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
+
 template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
@@ -150,6 +174,29 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -157,5 +204,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 39e952cbc38f6..9c0dfc6c0c127 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -67,8 +67,31 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
 struct Pred {
-  bool operator()(int a, int b) const { return a * 2 == b; }
+  bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
+  bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
 };
 
 template <class Iter1, class Iter2>
@@ -167,6 +190,31 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -174,5 +222,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }

>From c21c583527e255d722f312b871d43527e5c4dbb6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:52:50 +0100
Subject: [PATCH 06/19] Added a nodiscard test

---
 libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index f4fffd456a331..f5eaf1642b687 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -63,4 +63,12 @@ void test() {
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }

>From 189ba7a5f23d45939e0ff917985bc7049740d1b7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:57:47 +0100
Subject: [PATCH 07/19] Added an iterator requirement test

---
 .../pstl.iterator-requirements.verify.cpp        | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 402d8fbf9131a..df11341008bc5 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, pred);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}

>From 731de4a2cf2c196b704234b0ee5653453554b9b7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:00:09 +0100
Subject: [PATCH 08/19] Added an include of <__algorithm/min.h>

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 11ffdc332d633..8361731e78cfc 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,8 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__algorithm/mismatch.h>
 #include <__config>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/min.h>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>

>From 105a17ed5abc39ad5ac34bd930af8727a9562bed Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:05:29 +0100
Subject: [PATCH 09/19] Added an exception handling test

---
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  4 +--
 .../pstl.exception_handling.pass.cpp          | 25 +++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 8361731e78cfc..597a47fb185c8 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,9 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__config>
-#include <__algorithm/mismatch.h>
 #include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__config>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index e00046fdb1dd6..cbdb4663f168c 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -248,6 +248,31 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // mismatch(first1, last1, first2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
+        });
+
+        // mismatch(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // mismatch(first1, last1, first2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
+        });
+
+        // mismatch(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From 3bd32433db95c6995476e26d5b34c8000a5515f5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 09:43:45 +0100
Subject: [PATCH 10/19] Changed the variable name to avoid shadowing

---
 .../alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 12 ++++++------
 .../mismatch/pstl.mismatch_pred.pass.cpp             | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 5e46831539c30..941fe07b58327 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -67,22 +67,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 9c0dfc6c0c127..a4b15a56bd071 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -71,22 +71,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Pred {

>From a51af3556cdb7f7e24732077b6dce511e3ee9e89 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 20:27:14 +0100
Subject: [PATCH 11/19] Use raw C array in the tests

---
 .../mismatch/pstl.mismatch.pass.cpp           | 153 +++++++-------
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 192 +++++++++++-------
 2 files changed, 198 insertions(+), 147 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 941fe07b58327..17edf34ee7832 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -28,7 +28,6 @@
 //                                                     ForwardIterator2 last2);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -93,82 +92,98 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(
+          std::mismatch(
+              policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
+          std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 1};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 1};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(
+            std::mismatch(
+                policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+            std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(42);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 42);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(
+                   policy,
+                   Iter1(std::begin(lhs)),
+                   Iter1(std::begin(lhs) + i),
+                   Iter2(std::begin(rhs)),
+                   Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i)) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -179,20 +194,22 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index a4b15a56bd071..18c4fe1abad0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -32,7 +32,6 @@
 //                                                     BinaryPredicate pred);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -100,91 +99,120 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(
-                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::begin(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::begin(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {1};
-      std::array<int, 1> rhs = {2};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {1};
+      int rhs[1] = {2};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 2};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 2};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(84);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 84);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::begin(lhs) + i),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -195,22 +223,28 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };

>From f3bfa5528fb2aea972a018cc68e56133e32db99b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:08:55 +0100
Subject: [PATCH 12/19] Fixed typos in the comment

---
 .../algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 2 +-
 .../alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 17edf34ee7832..ed59fa9eca669 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -46,7 +46,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 18c4fe1abad0c..338503d784a31 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -50,7 +50,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;

>From 4c7fccd82c3a4bea33038c9eee3ebcefddd7f230 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:43:20 +0100
Subject: [PATCH 13/19] Added comments explaining what the parameters mean

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 597a47fb185c8..3d702b03d3afe 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -63,8 +63,9 @@ struct __cpu_parallel_mismatch {
             return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
                 .first;
           },
-          less<>{},
-          true);
+          less<>{}, // `less` here means the lowest index among the mismatches
+          true      // `true` here means we want the first mismatch, not the last
+      );
       if (!__res) {
         return std::nullopt; // Failed to run the algorithm, propagate the error.
       }

>From 22dfeede2c12c49a9f57db064f80e6d24e96f41b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:50:51 +0100
Subject: [PATCH 14/19] Added tests of the return types

---
 .../mismatch/pstl.mismatch.pass.cpp              | 10 ++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp         | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index ed59fa9eca669..cc7ef6feb2356 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -90,6 +90,16 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1]       = {0};
+      int rhs[1]       = {0};
+      auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
+      auto res_4legged = std::mismatch(
+          policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 338503d784a31..d80da0cf0b542 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -97,6 +97,22 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      auto res_3legged =
+          std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
+      auto res_4legged = std::mismatch(
+          policy,
+          Iter1(std::begin(lhs)),
+          Iter1(std::begin(lhs)),
+          Iter2(std::begin(rhs)),
+          Iter2(std::begin(rhs)),
+          Pred{});
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};

>From 8f9ddba49cfc2b501d8d42b1c8027f64b0695a31 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 21 Jul 2026 19:39:00 +0100
Subject: [PATCH 15/19] Rebased on support/runway_sample.h

---
 .../mismatch/pstl.mismatch.pass.cpp             | 17 +----------------
 .../mismatch/pstl.mismatch_pred.pass.cpp        | 17 +----------------
 2 files changed, 2 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index cc7ef6feb2356..83f4b0f16d6c1 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -38,6 +38,7 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
@@ -46,22 +47,6 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 // The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
 
 struct X {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index d80da0cf0b542..89cf899a47b4c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -42,6 +42,7 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
@@ -50,22 +51,6 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 // The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
 
 struct X {

>From 2c72022a2a14a6142479c498bfe283bbe81c67b0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 19 Jul 2026 16:27:43 +0100
Subject: [PATCH 16/19] Added an implementation of adjacent_find() based on
 mismatch()

---
 libcxx/include/__algorithm/pstl.h             |  25 +++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  45 +++++
 .../pstl.iterator-requirements.verify.cpp     |   6 +
 .../algorithms/pstl.nodiscard.verify.cpp      |   4 +
 .../pstl.adjacent_find.pass.cpp               | 164 +++++++++++++++++
 .../pstl.adjacent_find_pred.pass.cpp          | 173 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  12 ++
 8 files changed, 436 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index bc48fd9f48d7f..0447691c3b86b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -228,6 +228,31 @@ template <class _ExecutionPolicy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+adjacent_find(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "adjacent_find requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__adjacent_find, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), equal_to{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator adjacent_find(
+    _ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __predicate) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "adjacent_find requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__adjacent_find, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__predicate));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index ecd1f0b9b4049..3c835426ec377 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -339,6 +339,13 @@ struct __mismatch_3leg;
 // operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_find;
+// template <class _Policy, class _ForwardIterator, class _BinaryPredicate>
+// optional<_ForwardIterator>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last,
+//                                _BinaryPredicate __predicate) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 776c76f3fdfbd..05700daaf3e84 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 #define _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 
+#include <__algorithm/adjacent_find.h>
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
@@ -24,6 +25,7 @@
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
+#include <__iterator/prev.h>
 #include <__iterator/reverse_iterator.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
@@ -66,6 +68,7 @@ namespace __pstl {
 //
 // mismatch family
 // ---------------
+// - adjacent_find
 // - mismatch_3leg
 //
 // for_each family
@@ -249,6 +252,48 @@ struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_find<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator, class _BinaryPredicate>
+  optional<_ForwardIterator>
+  operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __predicate)
+      const noexcept {
+    if constexpr (__has_bidirectional_iterator_category<_ForwardIterator>::value) {
+      using _Mismatch = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      if (__first == __last) {
+        return __last; // Empty range, return __last.
+      }
+      _ForwardIterator __first2 = std::next(__first);
+      if (__first2 == __last) {
+        return __last; // Single element range, no adjacent elements, return __last.
+      }
+      _ForwardIterator __last2 = std::prev(__last);
+      // Find the first match within two overlapping ranges, expressed as a double negation of the predicate:
+      //   [first, __last - 1)
+      //   [first + 1, __last)
+      auto __res = _Mismatch()(
+          __policy,
+          std::move(__first),
+          std::move(__last2),
+          std::move(__first2),
+          __last,
+          [&](__iterator_reference<_ForwardIterator> __lhs, __iterator_reference<_ForwardIterator> __rhs) {
+            return !__predicate(__lhs, __rhs);
+          });
+      if (!__res) {
+        return nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      if (__res->second == __last) {
+        return __last; // No adjacent elements found, return __last.
+      }
+      return __res->first; // Return the first iterator of the pair of mismatched elements.
+    } else {
+      // Currently anything outside bidirectional iterators has to be processed serially
+      return std::adjacent_find(std::move(__first), std::move(__last), std::move(__predicate));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index df11341008bc5..640109ac131fb 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -151,6 +151,12 @@ void f(non_forward_iterator non_fwd,
         pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
   }
 
+  {
+    (void)std::adjacent_find(pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_find}}
+    (void)std::adjacent_find(
+        pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: adjacent_find}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index f5eaf1642b687..352f00f1c9baf 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -71,4 +71,8 @@ void test() {
   std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_find(std::execution::par, std::begin(a), std::end(a));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_find(std::execution::par, std::begin(a), std::end(a), pred2);
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
new file mode 100644
index 0000000000000..ff0b5c05bd364
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
@@ -0,0 +1,164 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template<class ExecutionPolicy, class ForwardIterator>
+// ForwardIterator adjacent_find(ExecutionPolicy&& exec,
+//                               ForwardIterator first, ForwardIterator last);
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// TODO: switch to the shared implementation once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+// The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
+struct X {
+  X() = delete;
+  explicit X(int i) : i_(i) {}
+  X(const X&)            = delete;
+  X(X&&)                 = delete;
+  X& operator=(const X&) = delete;
+  X& operator=(X&&)      = delete;
+  bool operator==(const X& other) const { return i_ == other.i_; }
+
+private:
+  int i_;
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]  = {42};
+      auto res = std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)));
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    {
+      int a[] = {42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {42, 42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a) + 1)) == Iter(std::begin(a) + 1));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 0, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {0, 1, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 0, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {0, 1, 0, 1};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 1, 1};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 3), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 2, 7, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 0);
+      runway_sample(std::size(a), [&](std::size_t i) {
+        if (i == 0)
+          return; // skip the first element to avoid out-of-bounds access
+        a[i] = a[i - 1];
+        assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + i - 1));
+        a[i] = i;
+      });
+    }
+  }
+};
+
+template <class Iter>
+struct TestX {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    X a[] = {X(0), X(1), X(1), X(2)};
+    assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a))) == Iter(std::end(a)));
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<const X*>{}, TestIteratorWithPolicies<TestX>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
new file mode 100644
index 0000000000000..795632b0db4db
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
@@ -0,0 +1,173 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template<class ExecutionPolicy, class ForwardIterator, class BinaryPredicate>
+// ForwardIterator adjacent_find(ExecutionPolicy&& exec,
+//                               ForwardIterator first, ForwardIterator last,
+//                               BinaryPredicate pred);
+
+#include <algorithm>
+#include <cassert>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// TODO: switch to the shared implementation once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+// The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
+struct X {
+  X() = delete;
+  explicit X(int i) : i_(i) {}
+  X(const X&)            = delete;
+  X(X&&)                 = delete;
+  X& operator=(const X&) = delete;
+  X& operator=(X&&)      = delete;
+  int get_value() const { return i_; }
+
+private:
+  int i_;
+};
+
+struct Pred {
+  bool operator()(const X& a, const X& b) const {
+    return static_cast<long long>(b.get_value()) - static_cast<long long>(a.get_value()) == 42;
+  }
+  bool operator()(int a, int b) const { return static_cast<long long>(b) - static_cast<long long>(a) == 42; }
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]  = {42};
+      auto res = std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{});
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    {
+      int a[] = {42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {42, 84};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a) + 1), Pred{}) ==
+             Iter(std::begin(a) + 1));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 42, 84};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {0, 41, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 0, 42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {0, 41, 0, 41};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 43, 85};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[] = {0, 1, 2, 44, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 3), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 2, 45, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min() + 42,
+                 std::numeric_limits<int>::max() - 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max() - 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min() + 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 0);
+      runway_sample(std::size(a), [&](std::size_t i) {
+        if (i == 0)
+          return; // skip the first element to avoid out-of-bounds access
+        a[i] = a[i - 1] + 42;
+        assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) ==
+               Iter(std::begin(a) + i - 1));
+        a[i] = i;
+      });
+    }
+  }
+};
+
+template <class Iter>
+struct TestX {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    X a[] = {X(0), X(40), X(82), X(100)};
+    assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<const X*>{}, TestIteratorWithPolicies<TestX>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index cbdb4663f168c..739b70229f72b 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -273,6 +273,18 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto pred = maybe_throw(tokens[5], [](int a, int b) -> bool { return a == b; });
+
+        // adjacent_find(first, last)
+        assert_non_throwing([=, &policy] { (void)std::adjacent_find(policy, std::move(first1), std::move(last1)); });
+
+        // adjacent_find(first, last, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_find(policy, std::move(first1), std::move(last1), pred);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From b99519a85d9169ea7a26dcd35e667e4339d890db Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 19 Jul 2026 17:25:35 +0100
Subject: [PATCH 17/19] Missing includes

---
 .../alg.adjacent.find/pstl.adjacent_find.pass.cpp              | 2 ++
 .../alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp         | 3 +++
 2 files changed, 5 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
index ff0b5c05bd364..8b83aa60bd421 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
@@ -18,10 +18,12 @@
 #include <cassert>
 #include <iterator>
 #include <numeric>
+#include <limits>
 
 #include "test_execution_policies.h"
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "type_algorithms.h"
 
 // TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
index 795632b0db4db..60cabad7ba014 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
@@ -18,10 +18,13 @@
 #include <algorithm>
 #include <cassert>
 #include <numeric>
+#include <iterator>
+#include <limits>
 
 #include "test_execution_policies.h"
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "type_algorithms.h"
 
 // TODO: switch to the shared implementation once it's merged into main
 template <class Callable>

>From 8d9a0e3de4934e4e7feacb91c694133dc1a2cc71 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 19 Jul 2026 19:16:03 +0100
Subject: [PATCH 18/19] Fixed variable shadowing

---
 libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 739b70229f72b..e3e76bc3692d3 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -274,7 +274,7 @@ int main(int, char**) {
       }
 
       {
-        auto pred = maybe_throw(tokens[5], [](int a, int b) -> bool { return a == b; });
+        auto pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; });
 
         // adjacent_find(first, last)
         assert_non_throwing([=, &policy] { (void)std::adjacent_find(policy, std::move(first1), std::move(last1)); });

>From e50c241c31e97f8d49275ac3115cd37a57dec6c7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 21 Jul 2026 20:13:37 +0100
Subject: [PATCH 19/19] Rebased onto support/runway_sample.h

---
 .../pstl.adjacent_find.pass.cpp                 | 17 +----------------
 .../pstl.adjacent_find_pred.pass.cpp            | 17 +----------------
 2 files changed, 2 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
index 8b83aa60bd421..bf6304a5f918b 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
@@ -24,22 +24,7 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 #include "type_algorithms.h"
-
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
+#include "runway_sample.h"
 
 // The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
 struct X {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
index 60cabad7ba014..e71a9df44743f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
@@ -25,22 +25,7 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 #include "type_algorithms.h"
-
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
+#include "runway_sample.h"
 
 // The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
 struct X {


From libcxx-commits at lists.llvm.org  Wed Jul 22 00:58:01 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 00:58:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a607809.5a0412d2.325e39.51c3@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From f81e6a6f9200d1e114201290b17587f02c20eaf2 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/22] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From 156f6f8298622067ee8717dc3a84c197e32ab175 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/22] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 463a77c6efb5fbf4cffd4be0dc3331520f93f39c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/22] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From a9b71cb5a78f6f6f6d634195c77e2b5d8bc486f5 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/22] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 0e2f1cd3cd56fd8aa62d28fa31ee51a5debb3935 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/22] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 2f92ae8766eabd5f92917bfad30ca3e02c5de84a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/22] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 2cb082799c25d82c9342ee51160ef38bc8e03e3a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/22] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From d2243047a22b62ed633eb879e31bbd6780cd3181 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/22] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From d5ad73d9c7f4062f1b50eeb4add63e4366fa3f00 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/22] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 4eba2824c648d85429256a0fae120931ff56147d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/22] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index a77e98f845a06..425c13fea707e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -39,7 +39,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +47,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +63,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -81,7 +81,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index a8bc817ced291..ce81d1c00d376 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From 6aa16eb678b260549394cd25ca405516ba85f93d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/22] Format

---
 libcxx/docs/Status/Cxx26Papers.csv                |  2 +-
 .../range.elements/iterator/base.pass.cpp         | 15 ++++++++-------
 .../range.elements/sentinel/base.pass.cpp         |  2 +-
 .../range.split/iterator/base.pass.cpp            | 10 +++++-----
 .../range.split/iterator/deref.pass.cpp           |  7 +++----
 .../sentinel/ctor.convert.pass.cpp                |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..ffa36d3f5145c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 425c13fea707e..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -81,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From 6d0ae1b24e97c93f07c0f71080eebaf9dd2ccb01 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/22] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..052e5eab25433 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index ffa36d3f5145c..4d0fbca940b92 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 197dc412359324c40e27a81dc0976a098fa78187 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/22] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From 8190cbcadce534b72759e9c26d34535fac4257f3 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/22] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp               | 16 ++++++++++++----
 .../sentinel/ctor.convert.pass.cpp             | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index ce81d1c00d376..25d1413b2fa16 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -67,15 +67,21 @@ static_assert(
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From cf21399e152311c7555d384b4e942af19374ccf0 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/22] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From e3ee97abdd6316f8ee21b852dd1f1b15f1bae1a6 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/22] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}

>From 123dc9cdec024eee0e89a429f30e167fff8a5b4a Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:05:55 +0300
Subject: [PATCH 17/22] Fixed formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index e28c36b1254af..bcce8e1f48f7b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,8 +22,7 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
+concept IsBaseNoexcept = requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From db4279e8d1e0d4ea60b6ed3163580f644939859f Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:18:55 +0300
Subject: [PATCH 18/22] Enable test

---
 .../ranges/range.adaptors/range.elements/sentinel/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index 885582a5c94a8..114b7fc1f1b71 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -37,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
   return 0;
 }

>From db256d5ef05fc8996ac19bf952af706398e8cdf9 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 07:53:40 +0300
Subject: [PATCH 19/22] Added `std::is_constructible/convertible` tests

---
 .../iterator/ctor.base.compile.pass.cpp       | 23 ++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 30 ++++++++++++++++
 .../ctor.parent.iter.compile.pass.cpp         | 25 +++++++++++++
 .../sentinel/ctor.parent.compile.pass.cpp     | 25 +++++++++++++
 .../ctor.outer_iterator.compile.pass.cpp      | 23 ++++++++++++
 .../ctor.parent.base.compile.pass.cpp         | 22 ++++++++++++
 .../ctor.parent.compile.pass.cpp              | 23 ++++++++++++
 .../sentinel/ctor.base.pred.compile.pass.cpp  | 35 +++++++++++++++++++
 .../iterator/ctor.value.compile.pass.cpp      | 26 ++++++++++++++
 .../sentinel/ctor.value.compile.pass.cpp      | 24 +++++++++++++
 .../iterator/ctor.parent.compile.pass.cpp     | 33 +++++++++++++++++
 11 files changed, 289 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..b43a60276e280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+using BaseIter     = std::tuple<int>*;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
+
+static_assert(!std::is_constructible_v<ElementsIter, BaseIter>);
+static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..ee4a297c1f4a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+  std::tuple<int>* begin() const;
+  Sent end();
+};
+
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..4a013d0072222
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..06e06aeb6db6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(filter_view&);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterSentinel, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterSentinel>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..37fe2fadfd41a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<InnerIterConst, OuterIterConst>);
+static_assert(!std::is_convertible_v<InnerIterConst, OuterIterConst>);
+
+static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterNonConst>);
+static_assert(!std::is_convertible_v<InnerIterNonConst, OuterIterNonConst>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
new file mode 100644
index 0000000000000..70e97ea04f5c0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
+//   requires forward_range<Base>
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(std::ranges::forward_range<SplitViewForward>);
+static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&, std::ranges::iterator_t<ForwardView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..b838970718c8a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
+//   requires (!forward_range<Base>)
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(!std::ranges::forward_range<SplitViewInput>);
+static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&>);
+static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
new file mode 100644
index 0000000000000..a66e3d1e84c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+  int* begin() const;
+  Sent end();
+};
+
+struct Pred {
+  bool operator()(int i) const;
+};
+
+using Sentinel = std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>;
+
+static_assert(!std::is_constructible_v<Sentinel, std::ranges::sentinel_t<Range>, const Pred*>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..e95042e26e146
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(W value);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
+
+using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
+static_assert(!std::is_constructible_v<Iter, SomeInt>);
+static_assert(!std::is_convertible_v<SomeInt, Iter>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..a39efb4318794
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.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++20
+
+// constexpr explicit sentinel(Bound bound);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
+static_assert(!std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
+static_assert(!std::is_convertible_v<IntSentinelWith<SomeInt>, Sent>);
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..171e2dce1e5f0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(basic_istream_view& parent) noexcept;
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <sstream>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// test that the constructor is explicit
+template <class CharT>
+using IstreamView = std::ranges::basic_istream_view<int, CharT>;
+template <class CharT>
+using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
+
+static_assert(!std::constructible_from<Iter<char>, IstreamView<char>&>);
+static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
+static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
+#endif

>From 7ff606098cbdd0f73abc4a8211325f78e3e3ffbc Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 09:20:54 +0300
Subject: [PATCH 20/22] Cleanup

---
 .../range.iota.view/iterator/ctor.value.compile.pass.cpp        | 2 --
 .../range.iota.view/sentinel/ctor.value.compile.pass.cpp        | 2 --
 2 files changed, 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
index e95042e26e146..fc3384e191d1d 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
index a39efb4318794..b0bcdb6c60ce7 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;

>From b42e68f131981e95acfdd373f0f3dcc1a84b0c2d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:42:42 +0300
Subject: [PATCH 21/22] Fixed CI

---
 .../range.istream.view/iterator/ctor.parent.compile.pass.cpp     | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
index 171e2dce1e5f0..04ff9ac24d731 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-localization
 
 // constexpr explicit iterator(basic_istream_view& parent) noexcept;
 

>From 78f944f182d7e88979501f073c1b7574552f8998 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:57:29 +0300
Subject: [PATCH 22/22] Restored a test case!

---
 .../range.istream.view/iterator/deref.pass.cpp        | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 9f9f13399392c..5df730410a4ad 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,6 +28,17 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
+
+  // operator* should return the same reference to the value stored in the view
+  {
+    auto iss = make_string_stream<CharT>("1 2 345 ");
+    std::ranges::basic_istream_view<int, CharT> isv{iss};
+    using Iter = std::ranges::iterator_t<decltype(isv)>;
+
+    Iter it1 = isv.begin();
+    Iter it2 = isv.begin();
+    assert(&*it1 == &*it2);
+  }
 }
 
 int main(int, char**) {


From libcxx-commits at lists.llvm.org  Wed Jul 22 00:58:57 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 00:58:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a607841.13be6df8.323b61.bbc1@mx.google.com>


https://github.com/Zingam ready_for_review https://github.com/llvm/llvm-project/pull/193891

From libcxx-commits at lists.llvm.org  Wed Jul 22 00:59:43 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 00:59:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a60786f.bc28b987.1c0bcd.371e@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From 68da1731c2ad8f5e98872b8df5e8dd5bfa646008 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/22] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From c4d11be4adc7c07fd52f608bea12da28e8221371 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/22] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 781eaa5dbef3351d87574cf432106a2a7d7c9a38 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/22] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From 195931a66e133699188de65ed5b8ee7f9aef2fe7 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/22] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 26cf1daff36cae1d3e5f5476f7faba71cb019296 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/22] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 11b933048f6a2b6279d2ff8b643ce845975d5efe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/22] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 74f795f5f559b72803edd152d2f60b772dd697d8 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/22] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From 3f679ac8813a96d8179f8b72000153c3be0c1f50 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/22] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From 1bd7d694c3339a6b4e16a60a00817dc2130aa312 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/22] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 5a43b67d99477e25e09d5e2bf471de20bd21ad64 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/22] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index a77e98f845a06..425c13fea707e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -39,7 +39,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +47,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +63,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -81,7 +81,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index a8bc817ced291..ce81d1c00d376 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From b4695a242823d5f4943d22cf64802696a092191c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/22] Format

---
 libcxx/docs/Status/Cxx26Papers.csv                |  2 +-
 .../range.elements/iterator/base.pass.cpp         | 15 ++++++++-------
 .../range.elements/sentinel/base.pass.cpp         |  2 +-
 .../range.split/iterator/base.pass.cpp            | 10 +++++-----
 .../range.split/iterator/deref.pass.cpp           |  7 +++----
 .../sentinel/ctor.convert.pass.cpp                |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..ffa36d3f5145c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 425c13fea707e..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -81,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From ee7863618f3b2d5bc4fc88409b2d77d0424444ec Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/22] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..052e5eab25433 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index ffa36d3f5145c..4d0fbca940b92 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 11b36027bc4737a06ac1368d0c4027bead46a6c4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/22] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From 4127ad1211b268ab699b7c84aa543f8ce99fdc90 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/22] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp               | 16 ++++++++++++----
 .../sentinel/ctor.convert.pass.cpp             | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index ce81d1c00d376..25d1413b2fa16 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -67,15 +67,21 @@ static_assert(
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From c33bac978f0ecf59b457c38f5a12c79c56af2fac Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/22] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From e35c0d0bed0d974cacdc104648177102db7dedbe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/22] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}

>From dc161708c53ce4ecf0b5596956eec6f7b03cfe36 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:05:55 +0300
Subject: [PATCH 17/22] Fixed formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index e28c36b1254af..bcce8e1f48f7b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,8 +22,7 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
+concept IsBaseNoexcept = requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From f4d45145cd5092bb65a8137a243dc94a8e107812 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:18:55 +0300
Subject: [PATCH 18/22] Enable test

---
 .../ranges/range.adaptors/range.elements/sentinel/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index 885582a5c94a8..114b7fc1f1b71 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -37,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
   return 0;
 }

>From 851f503a96a63350ca8f809a8c5deca584405d68 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 07:53:40 +0300
Subject: [PATCH 19/22] Added `std::is_constructible/convertible` tests

---
 .../iterator/ctor.base.compile.pass.cpp       | 23 ++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 30 ++++++++++++++++
 .../ctor.parent.iter.compile.pass.cpp         | 25 +++++++++++++
 .../sentinel/ctor.parent.compile.pass.cpp     | 25 +++++++++++++
 .../ctor.outer_iterator.compile.pass.cpp      | 23 ++++++++++++
 .../ctor.parent.base.compile.pass.cpp         | 22 ++++++++++++
 .../ctor.parent.compile.pass.cpp              | 23 ++++++++++++
 .../sentinel/ctor.base.pred.compile.pass.cpp  | 35 +++++++++++++++++++
 .../iterator/ctor.value.compile.pass.cpp      | 26 ++++++++++++++
 .../sentinel/ctor.value.compile.pass.cpp      | 24 +++++++++++++
 .../iterator/ctor.parent.compile.pass.cpp     | 33 +++++++++++++++++
 11 files changed, 289 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..b43a60276e280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+using BaseIter     = std::tuple<int>*;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
+
+static_assert(!std::is_constructible_v<ElementsIter, BaseIter>);
+static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..ee4a297c1f4a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+  std::tuple<int>* begin() const;
+  Sent end();
+};
+
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..4a013d0072222
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..06e06aeb6db6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(filter_view&);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterSentinel, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterSentinel>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..37fe2fadfd41a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<InnerIterConst, OuterIterConst>);
+static_assert(!std::is_convertible_v<InnerIterConst, OuterIterConst>);
+
+static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterNonConst>);
+static_assert(!std::is_convertible_v<InnerIterNonConst, OuterIterNonConst>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
new file mode 100644
index 0000000000000..70e97ea04f5c0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
+//   requires forward_range<Base>
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(std::ranges::forward_range<SplitViewForward>);
+static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&, std::ranges::iterator_t<ForwardView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..b838970718c8a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
+//   requires (!forward_range<Base>)
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(!std::ranges::forward_range<SplitViewInput>);
+static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&>);
+static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
new file mode 100644
index 0000000000000..a66e3d1e84c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+  int* begin() const;
+  Sent end();
+};
+
+struct Pred {
+  bool operator()(int i) const;
+};
+
+using Sentinel = std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>;
+
+static_assert(!std::is_constructible_v<Sentinel, std::ranges::sentinel_t<Range>, const Pred*>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..e95042e26e146
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(W value);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
+
+using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
+static_assert(!std::is_constructible_v<Iter, SomeInt>);
+static_assert(!std::is_convertible_v<SomeInt, Iter>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..a39efb4318794
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.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++20
+
+// constexpr explicit sentinel(Bound bound);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
+static_assert(!std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
+static_assert(!std::is_convertible_v<IntSentinelWith<SomeInt>, Sent>);
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..171e2dce1e5f0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(basic_istream_view& parent) noexcept;
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <sstream>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// test that the constructor is explicit
+template <class CharT>
+using IstreamView = std::ranges::basic_istream_view<int, CharT>;
+template <class CharT>
+using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
+
+static_assert(!std::constructible_from<Iter<char>, IstreamView<char>&>);
+static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
+static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
+#endif

>From cfa8219f33f0d433afaa3144d3183bd763d58854 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 09:20:54 +0300
Subject: [PATCH 20/22] Cleanup

---
 .../range.iota.view/iterator/ctor.value.compile.pass.cpp        | 2 --
 .../range.iota.view/sentinel/ctor.value.compile.pass.cpp        | 2 --
 2 files changed, 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
index e95042e26e146..fc3384e191d1d 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
index a39efb4318794..b0bcdb6c60ce7 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;

>From 1438ff9824d09adb014641acc0ae3b0775ea8d27 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:42:42 +0300
Subject: [PATCH 21/22] Fixed CI

---
 .../range.istream.view/iterator/ctor.parent.compile.pass.cpp     | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
index 171e2dce1e5f0..04ff9ac24d731 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-localization
 
 // constexpr explicit iterator(basic_istream_view& parent) noexcept;
 

>From 98ad281967c925d3cbc27adaeabafeaddddd2227 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:57:29 +0300
Subject: [PATCH 22/22] Restored a test case!

---
 .../range.istream.view/iterator/deref.pass.cpp        | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 9f9f13399392c..5df730410a4ad 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,6 +28,17 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
+
+  // operator* should return the same reference to the value stored in the view
+  {
+    auto iss = make_string_stream<CharT>("1 2 345 ");
+    std::ranges::basic_istream_view<int, CharT> isv{iss};
+    using Iter = std::ranges::iterator_t<decltype(isv)>;
+
+    Iter it1 = isv.begin();
+    Iter it2 = isv.begin();
+    assert(&*it1 == &*it2);
+  }
 }
 
 int main(int, char**) {


From libcxx-commits at lists.llvm.org  Wed Jul 22 01:06:22 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Wed, 22 Jul 2026 01:06:22 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209473@github.com>
Message-ID: <6a6079fe.c4d3d80a.16dae5.cd19@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `llvm-clang-x86_64-expensive-checks-debian` running on `gribozavr4` while building `clang-tools-extra,clang,cmake,flang,libcxx,lld,llvm` at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/16/builds/2982

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SystemZ/zos-ppa2.ll' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
sed -e 's/!"MODE"/!"ascii"/' -e 's/BYTE/85/' /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll > /b/1/llvm-clang-x86_64-expensive-checks-debian/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: sed -e 's/!"MODE"/!"ascii"/' -e s/BYTE/85/ /b/1/llvm-clang-x86_64-expensive-checks-debian/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
# RUN: at line 2
/b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true < /b/1/llvm-clang-x86_64-expensive-checks-debian/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll |  /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/FileCheck /b/1/llvm-clang-x86_64-expensive-checks-debian/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true
# executed command: /b/1/llvm-clang-x86_64-expensive-checks-debian/build/bin/FileCheck /b/1/llvm-clang-x86_64-expensive-checks-debian/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# .---command stderr------------
# | /b/1/llvm-clang-x86_64-expensive-checks-debian/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll:24:10: error: CHECK: expected string not found in input
# | ; CHECK: DC XL6'F2F3F0F0F0F0'
# |          ^
# | <stdin>:17:39: note: scanning from here
# |  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0' 
# |                                       ^
# | <stdin>:18:2: note: possible intended match here
# |  DC XL6'F2F4F0F0F0F0' 
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /b/1/llvm-clang-x86_64-expensive-checks-debian/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             .
# |             .
# |             .
# |            12:  DC XL4'00000000'  
# |            13:  DC XL1'85'  
# |            14:  DC XL1'00'  
# |            15:  DC XL2'0000'  
# |            16: L#DVS DS 0H  
# |            17:  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0'  
# | check:24'0                                          {    search range start (exclusive)
# | check:24'1                                               error: no match found in search range
# |            18:  DC XL6'F2F4F0F0F0F0'  
# | check:24'2      ?                       possible intended match
# |            19:  DC XL2'0000'  
# |            20: stdin#C CSECT  
# |            21: C_@@QPPA2 CATTR ALIGN(3),FILL(0),NOTEXECUTABLE,READONLY,RMODE(64),PART(.  
# |            22:  .&ppa2)  
# |            23: .&ppa2 XATTR LINKAGE(OS),REFERENCE(DATA),SCOPE(SECTION)  
# |             .
# |             .
...

```

</details>

https://github.com/llvm/llvm-project/pull/209473

From libcxx-commits at lists.llvm.org  Wed Jul 22 01:21:16 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 22 Jul 2026 01:21:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a607d7c.27b3b003.207f1a.2bbf@mx.google.com>


================
@@ -817,6 +823,32 @@ struct owner_less<void>
     typedef void is_transparent;
 };
 
+// class owner_hash:                                                          // C++26
+struct owner_hash
+{
+    template <class _Tp>
+    size_t operator()(shared_ptr<_Tp> const& __p) const noexcept;
+    template <class _Tp>
+    size_t operator()(weak_ptr<_Tp> const& __p) const noexcept;
+
+    typedef void is_transparent;
----------------
frederick-vs-ja wrote:

```suggestion
    using is_transparent = unspecified;
```

The standard doesn't specify the actual type. See [[util.smartptr.owner.hash]](https://eel.is/c++draft/util.smartptr.owner.hash) and [[util.smartptr.owner.equal]](https://eel.is/c++draft/util.smartptr.owner.equal).

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Wed Jul 22 01:21:16 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 22 Jul 2026 01:21:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a607d7c.3cce8983.247e47.b9a1@mx.google.com>


================
@@ -12,7 +12,7 @@
 "`P2338R4 <https://wg21.link/P2338R4>`__","Freestanding Library: Character primitives and the C library","2023-06 (Varna)","","","`#105369 <https://github.com/llvm/llvm-project/issues/105369>`__",""
 "`P2013R5 <https://wg21.link/P2013R5>`__","Freestanding Language: Optional ``::operator new``","2023-06 (Varna)","","","`#105370 <https://github.com/llvm/llvm-project/issues/105370>`__",""
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
-"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
+"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","|Complete|","23","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
----------------
frederick-vs-ja wrote:

```suggestion
"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","|Complete|","24","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
```

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Wed Jul 22 01:21:16 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 22 Jul 2026 01:21:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a607d7c.af6ba3ed.1989e7.5329@mx.google.com>


================
@@ -55,6 +55,7 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P1901R2: Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers (`Github <https://llvm.org/PR105372>`__)
----------------
frederick-vs-ja wrote:

This line should be added to `24.rst` because we won't backport this to LLVM 23.

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Wed Jul 22 01:21:18 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 22 Jul 2026 01:21:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a607d7e.af6ba3ed.1989e7.541e@mx.google.com>


================
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
----------------
frederick-vs-ja wrote:

```suggestion
// REQUIRES: std-at-least-c++26
```

Ditto below.

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Wed Jul 22 01:21:18 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 22 Jul 2026 01:21:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a607d7e.1905e68e.25404.1894@mx.google.com>


================
@@ -1318,6 +1351,44 @@ struct owner_less<void> {
 };
 #endif
 
+#if _LIBCPP_STD_VER >= 26
+struct owner_hash {
+  template <class _Tp>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t operator()(shared_ptr<_Tp> const& __p) const _NOEXCEPT {
+    return __p.owner_hash();
+  }
+  template <class _Tp>
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t operator()(weak_ptr<_Tp> const& __p) const _NOEXCEPT {
+    return __p.owner_hash();
+  }
+  typedef void is_transparent;
----------------
frederick-vs-ja wrote:

Same for `owner_equal`.
```suggestion

  using is_transparent = void;
```
(Also, it seems a bit clear to add an empty line here).

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Wed Jul 22 01:21:18 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 22 Jul 2026 01:21:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a607d7e.4a342cbc.1aeb63.3f17@mx.google.com>


================
@@ -555,6 +556,22 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI shared_ptr {
 
   _LIBCPP_HIDE_FROM_ABI bool __owner_equivalent(const shared_ptr& __p) const { return __cntrl_ == __p.__cntrl_; }
 
+#if _LIBCPP_STD_VER >= 26
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t owner_hash() const _NOEXCEPT {
----------------
frederick-vs-ja wrote:

In C++26 code we should use pretty forms. Same for other functions.
```suggestion
  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t owner_hash() const noexcept {
```

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Wed Jul 22 01:22:05 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 01:22:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] c23ad1b - [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (#209130)
Message-ID: <6a607dad.13be6df8.323b61.18d3@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-22T10:22:00+02:00
New Revision: c23ad1b869cdcec16275304e1f0b1d419fe35ae9

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

LOG: [libc++] Remove {Pause,Resume}Timing from fast push_back benchmarks (#209130)

These benchmarks are expected to run for a very short time, and
`{Pause,Resume}Timing` should only be used when operations are expected
to take a long time. Removing them reduces the amount of noise in these
benchmarks.

Fixes #208719

Added: 
    

Modified: 
    libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h

Removed: 
    


################################################################################
diff  --git a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
index 92346c38563fb..729f587c3388b 100644
--- a/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/sequence/sequence_container_benchmarks.h
@@ -372,9 +372,7 @@ void sequence_container_benchmarks(std::string container) {
             }
             DoNotOptimizeData(c);
 
-            st.PauseTiming();
             c.erase(c.end() - BatchSize, c.end());
-            st.ResumeTiming();
           }
         });
     }
@@ -396,7 +394,7 @@ void sequence_container_benchmarks(std::string container) {
           DoNotOptimizeData(c);
 
           st.PauseTiming();
-          c.clear();
+          c = Container();
           st.ResumeTiming();
         }
       });


        

From libcxx-commits at lists.llvm.org  Wed Jul 22 01:22:07 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 01:22:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove {Pause,
 Resume}Timing from fast push_back benchmarks (PR #209130)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209130@github.com>
Message-ID: <6a607daf.7d702ed2.2f6c05.e79d@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/209130

From libcxx-commits at lists.llvm.org  Wed Jul 22 01:23:10 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 22 Jul 2026 01:23:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a607dee.6c5720b3.1f1229.8aa3@mx.google.com>


frederick-vs-ja wrote:

Ah, we should also modify `libcxx/modules/std/memory.inc` to make the new functors usable in standard library modules.

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Wed Jul 22 01:54:20 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 01:54:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Test existence of generated
 headers in the modulemap (PR #209312)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209312@github.com>
Message-ID: <6a60853c.c56fb102.6921f.c3f4@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/209312

From libcxx-commits at lists.llvm.org  Wed Jul 22 01:55:12 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 01:55:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a608570.bd93e731.2ac69c.2888@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From 68da1731c2ad8f5e98872b8df5e8dd5bfa646008 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/25] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From c4d11be4adc7c07fd52f608bea12da28e8221371 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/25] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 781eaa5dbef3351d87574cf432106a2a7d7c9a38 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/25] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From 195931a66e133699188de65ed5b8ee7f9aef2fe7 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/25] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 26cf1daff36cae1d3e5f5476f7faba71cb019296 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/25] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 11b933048f6a2b6279d2ff8b643ce845975d5efe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/25] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 74f795f5f559b72803edd152d2f60b772dd697d8 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/25] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From 3f679ac8813a96d8179f8b72000153c3be0c1f50 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/25] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From 1bd7d694c3339a6b4e16a60a00817dc2130aa312 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/25] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 5a43b67d99477e25e09d5e2bf471de20bd21ad64 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/25] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index a77e98f845a06..425c13fea707e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -39,7 +39,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +47,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +63,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -81,7 +81,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index a8bc817ced291..ce81d1c00d376 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From b4695a242823d5f4943d22cf64802696a092191c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/25] Format

---
 libcxx/docs/Status/Cxx26Papers.csv                |  2 +-
 .../range.elements/iterator/base.pass.cpp         | 15 ++++++++-------
 .../range.elements/sentinel/base.pass.cpp         |  2 +-
 .../range.split/iterator/base.pass.cpp            | 10 +++++-----
 .../range.split/iterator/deref.pass.cpp           |  7 +++----
 .../sentinel/ctor.convert.pass.cpp                |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..ffa36d3f5145c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 425c13fea707e..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -81,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From ee7863618f3b2d5bc4fc88409b2d77d0424444ec Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/25] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..052e5eab25433 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index ffa36d3f5145c..4d0fbca940b92 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 11b36027bc4737a06ac1368d0c4027bead46a6c4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/25] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From 4127ad1211b268ab699b7c84aa543f8ce99fdc90 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/25] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp               | 16 ++++++++++++----
 .../sentinel/ctor.convert.pass.cpp             | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index ce81d1c00d376..25d1413b2fa16 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -67,15 +67,21 @@ static_assert(
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From c33bac978f0ecf59b457c38f5a12c79c56af2fac Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/25] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From e35c0d0bed0d974cacdc104648177102db7dedbe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/25] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}

>From dc161708c53ce4ecf0b5596956eec6f7b03cfe36 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:05:55 +0300
Subject: [PATCH 17/25] Fixed formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index e28c36b1254af..bcce8e1f48f7b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,8 +22,7 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
+concept IsBaseNoexcept = requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From f4d45145cd5092bb65a8137a243dc94a8e107812 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:18:55 +0300
Subject: [PATCH 18/25] Enable test

---
 .../ranges/range.adaptors/range.elements/sentinel/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index 885582a5c94a8..114b7fc1f1b71 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -37,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
   return 0;
 }

>From 851f503a96a63350ca8f809a8c5deca584405d68 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 07:53:40 +0300
Subject: [PATCH 19/25] Added `std::is_constructible/convertible` tests

---
 .../iterator/ctor.base.compile.pass.cpp       | 23 ++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 30 ++++++++++++++++
 .../ctor.parent.iter.compile.pass.cpp         | 25 +++++++++++++
 .../sentinel/ctor.parent.compile.pass.cpp     | 25 +++++++++++++
 .../ctor.outer_iterator.compile.pass.cpp      | 23 ++++++++++++
 .../ctor.parent.base.compile.pass.cpp         | 22 ++++++++++++
 .../ctor.parent.compile.pass.cpp              | 23 ++++++++++++
 .../sentinel/ctor.base.pred.compile.pass.cpp  | 35 +++++++++++++++++++
 .../iterator/ctor.value.compile.pass.cpp      | 26 ++++++++++++++
 .../sentinel/ctor.value.compile.pass.cpp      | 24 +++++++++++++
 .../iterator/ctor.parent.compile.pass.cpp     | 33 +++++++++++++++++
 11 files changed, 289 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..b43a60276e280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+using BaseIter     = std::tuple<int>*;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
+
+static_assert(!std::is_constructible_v<ElementsIter, BaseIter>);
+static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..ee4a297c1f4a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+  std::tuple<int>* begin() const;
+  Sent end();
+};
+
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..4a013d0072222
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..06e06aeb6db6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(filter_view&);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterSentinel, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterSentinel>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..37fe2fadfd41a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<InnerIterConst, OuterIterConst>);
+static_assert(!std::is_convertible_v<InnerIterConst, OuterIterConst>);
+
+static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterNonConst>);
+static_assert(!std::is_convertible_v<InnerIterNonConst, OuterIterNonConst>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
new file mode 100644
index 0000000000000..70e97ea04f5c0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
+//   requires forward_range<Base>
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(std::ranges::forward_range<SplitViewForward>);
+static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&, std::ranges::iterator_t<ForwardView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..b838970718c8a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
+//   requires (!forward_range<Base>)
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(!std::ranges::forward_range<SplitViewInput>);
+static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&>);
+static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
new file mode 100644
index 0000000000000..a66e3d1e84c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+  int* begin() const;
+  Sent end();
+};
+
+struct Pred {
+  bool operator()(int i) const;
+};
+
+using Sentinel = std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>;
+
+static_assert(!std::is_constructible_v<Sentinel, std::ranges::sentinel_t<Range>, const Pred*>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..e95042e26e146
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(W value);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
+
+using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
+static_assert(!std::is_constructible_v<Iter, SomeInt>);
+static_assert(!std::is_convertible_v<SomeInt, Iter>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..a39efb4318794
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.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++20
+
+// constexpr explicit sentinel(Bound bound);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
+static_assert(!std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
+static_assert(!std::is_convertible_v<IntSentinelWith<SomeInt>, Sent>);
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..171e2dce1e5f0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(basic_istream_view& parent) noexcept;
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <sstream>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// test that the constructor is explicit
+template <class CharT>
+using IstreamView = std::ranges::basic_istream_view<int, CharT>;
+template <class CharT>
+using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
+
+static_assert(!std::constructible_from<Iter<char>, IstreamView<char>&>);
+static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
+static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
+#endif

>From cfa8219f33f0d433afaa3144d3183bd763d58854 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 09:20:54 +0300
Subject: [PATCH 20/25] Cleanup

---
 .../range.iota.view/iterator/ctor.value.compile.pass.cpp        | 2 --
 .../range.iota.view/sentinel/ctor.value.compile.pass.cpp        | 2 --
 2 files changed, 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
index e95042e26e146..fc3384e191d1d 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
index a39efb4318794..b0bcdb6c60ce7 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;

>From 1438ff9824d09adb014641acc0ae3b0775ea8d27 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:42:42 +0300
Subject: [PATCH 21/25] Fixed CI

---
 .../range.istream.view/iterator/ctor.parent.compile.pass.cpp     | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
index 171e2dce1e5f0..04ff9ac24d731 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-localization
 
 // constexpr explicit iterator(basic_istream_view& parent) noexcept;
 

>From 98ad281967c925d3cbc27adaeabafeaddddd2227 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:57:29 +0300
Subject: [PATCH 22/25] Restored a test case!

---
 .../range.istream.view/iterator/deref.pass.cpp        | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 9f9f13399392c..5df730410a4ad 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,6 +28,17 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
+
+  // operator* should return the same reference to the value stored in the view
+  {
+    auto iss = make_string_stream<CharT>("1 2 345 ");
+    std::ranges::basic_istream_view<int, CharT> isv{iss};
+    using Iter = std::ranges::iterator_t<decltype(isv)>;
+
+    Iter it1 = isv.begin();
+    Iter it2 = isv.begin();
+    assert(&*it1 == &*it2);
+  }
 }
 
 int main(int, char**) {

>From ea4f9412bc9b26b3ae55b40c23ee1ac615bbb643 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:16:34 +0300
Subject: [PATCH 23/25] Added a missing `take_view` test

---
 .../ctor.base.compile.pass.cpp                | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..9383508165609
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using TakeView = std::ranges::take_view<MoveOnlyView>;
+using Sentinel = std::ranges::sentinel_t<TakeView>;
+
+static_assert(!std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
+static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);

>From d0afec965397496a91848fdd2e46ed89c98e7039 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:34:21 +0300
Subject: [PATCH 24/25] Added missing test `join_view`

---
 .../ctor.parent.compile.pass.cpp              | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..504c945e3d918
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(Parent& parent);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using Parent = std::ranges::join_view<ParentView<ChildView>>;
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
+static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);

>From c50248da199d6cbb3d5ba6b292ef102e7e960d30 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:54:39 +0300
Subject: [PATCH 25/25] Update tests `split_view`

---
 ...tor.parent.iter.subrange.compile.pass.cpp} | 31 +++++------------
 ....pass.cpp => ctor.parent.compile.pass.cpp} | 34 ++++---------------
 2 files changed, 14 insertions(+), 51 deletions(-)
 rename libcxx/test/std/ranges/range.adaptors/range.split/iterator/{ctor.base.pass.cpp => ctor.parent.iter.subrange.compile.pass.cpp} (64%)
 rename libcxx/test/std/ranges/range.adaptors/range.split/sentinel/{ctor.parent.pass.cpp => ctor.parent.compile.pass.cpp} (57%)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
similarity index 64%
rename from libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
index 823e22e117f77..9f4e9a13e0447 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
@@ -6,12 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
-// constexpr iterator begin();
+// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
 
-#include <cassert>
 #include <ranges>
+#include <type_traits>
 
 #include "../types.h"
 
@@ -30,24 +32,7 @@ struct TracedMoveView : std::ranges::view_base {
   constexpr TracedMoveIter end() const { return {}; }
 };
 
-constexpr bool test() {
-  using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
-  using SplitIter = std::ranges::iterator_t<SplitView>;
-
-  SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = sv.begin();
-  assert(iter.base().moved);
-
-  auto subRange = *iter;
-  assert(subRange.begin().moved);
-  assert(subRange.end().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
+using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
+using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  return 0;
-}
+static_assert(!std::is_constructible_v<SplitIter, SplitView, TracedMoveIter, std::ranges::subrange<TracedMoveIter>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
similarity index 57%
rename from libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 6e1ba912a201b..85d7e908a057c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -6,41 +6,19 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
 // constexpr explicit sentinel(split_view& parent);
 
-#include <cassert>
+// The constructor is now `private` (exposition-only) per P3059R2.
+
 #include <ranges>
-#include <type_traits>
 
-#include "test_iterators.h"
+#include "../types.h"
 
-// test explicit
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-constexpr bool test() {
-  {
-    int buffer[] = {0, 1, 2};
-    Range input{buffer, sentinel_wrapper<int*>(buffer + 3)};
-    SplitView sv(input, -1);
-    auto it = sv.begin();
-
-    auto sent = sv.end();
-    assert(sent != it);
-
-    ++it;
-    assert(sent == it);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
+static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_convertible_v<SplitView&, SplitSent>);


From libcxx-commits at lists.llvm.org  Wed Jul 22 01:57:41 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 01:57:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a608605.1905e68e.25404.95f2@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From 68da1731c2ad8f5e98872b8df5e8dd5bfa646008 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/26] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From c4d11be4adc7c07fd52f608bea12da28e8221371 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/26] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 781eaa5dbef3351d87574cf432106a2a7d7c9a38 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/26] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From 195931a66e133699188de65ed5b8ee7f9aef2fe7 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/26] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 26cf1daff36cae1d3e5f5476f7faba71cb019296 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/26] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 11b933048f6a2b6279d2ff8b643ce845975d5efe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/26] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 74f795f5f559b72803edd152d2f60b772dd697d8 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/26] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From 3f679ac8813a96d8179f8b72000153c3be0c1f50 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/26] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From 1bd7d694c3339a6b4e16a60a00817dc2130aa312 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/26] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 5a43b67d99477e25e09d5e2bf471de20bd21ad64 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/26] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index a77e98f845a06..425c13fea707e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -39,7 +39,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +47,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +63,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -81,7 +81,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index a8bc817ced291..ce81d1c00d376 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From b4695a242823d5f4943d22cf64802696a092191c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/26] Format

---
 libcxx/docs/Status/Cxx26Papers.csv                |  2 +-
 .../range.elements/iterator/base.pass.cpp         | 15 ++++++++-------
 .../range.elements/sentinel/base.pass.cpp         |  2 +-
 .../range.split/iterator/base.pass.cpp            | 10 +++++-----
 .../range.split/iterator/deref.pass.cpp           |  7 +++----
 .../sentinel/ctor.convert.pass.cpp                |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..ffa36d3f5145c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 425c13fea707e..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -81,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From ee7863618f3b2d5bc4fc88409b2d77d0424444ec Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/26] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..052e5eab25433 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index ffa36d3f5145c..4d0fbca940b92 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 11b36027bc4737a06ac1368d0c4027bead46a6c4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/26] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From 4127ad1211b268ab699b7c84aa543f8ce99fdc90 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/26] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp               | 16 ++++++++++++----
 .../sentinel/ctor.convert.pass.cpp             | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index ce81d1c00d376..25d1413b2fa16 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -67,15 +67,21 @@ static_assert(
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From c33bac978f0ecf59b457c38f5a12c79c56af2fac Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/26] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From e35c0d0bed0d974cacdc104648177102db7dedbe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/26] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}

>From dc161708c53ce4ecf0b5596956eec6f7b03cfe36 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:05:55 +0300
Subject: [PATCH 17/26] Fixed formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index e28c36b1254af..bcce8e1f48f7b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,8 +22,7 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
+concept IsBaseNoexcept = requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From f4d45145cd5092bb65a8137a243dc94a8e107812 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:18:55 +0300
Subject: [PATCH 18/26] Enable test

---
 .../ranges/range.adaptors/range.elements/sentinel/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index 885582a5c94a8..114b7fc1f1b71 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -37,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
   return 0;
 }

>From 851f503a96a63350ca8f809a8c5deca584405d68 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 07:53:40 +0300
Subject: [PATCH 19/26] Added `std::is_constructible/convertible` tests

---
 .../iterator/ctor.base.compile.pass.cpp       | 23 ++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 30 ++++++++++++++++
 .../ctor.parent.iter.compile.pass.cpp         | 25 +++++++++++++
 .../sentinel/ctor.parent.compile.pass.cpp     | 25 +++++++++++++
 .../ctor.outer_iterator.compile.pass.cpp      | 23 ++++++++++++
 .../ctor.parent.base.compile.pass.cpp         | 22 ++++++++++++
 .../ctor.parent.compile.pass.cpp              | 23 ++++++++++++
 .../sentinel/ctor.base.pred.compile.pass.cpp  | 35 +++++++++++++++++++
 .../iterator/ctor.value.compile.pass.cpp      | 26 ++++++++++++++
 .../sentinel/ctor.value.compile.pass.cpp      | 24 +++++++++++++
 .../iterator/ctor.parent.compile.pass.cpp     | 33 +++++++++++++++++
 11 files changed, 289 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..b43a60276e280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+using BaseIter     = std::tuple<int>*;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
+
+static_assert(!std::is_constructible_v<ElementsIter, BaseIter>);
+static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..ee4a297c1f4a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+  std::tuple<int>* begin() const;
+  Sent end();
+};
+
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..4a013d0072222
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..06e06aeb6db6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(filter_view&);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterSentinel, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterSentinel>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..37fe2fadfd41a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<InnerIterConst, OuterIterConst>);
+static_assert(!std::is_convertible_v<InnerIterConst, OuterIterConst>);
+
+static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterNonConst>);
+static_assert(!std::is_convertible_v<InnerIterNonConst, OuterIterNonConst>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
new file mode 100644
index 0000000000000..70e97ea04f5c0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
+//   requires forward_range<Base>
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(std::ranges::forward_range<SplitViewForward>);
+static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&, std::ranges::iterator_t<ForwardView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..b838970718c8a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
+//   requires (!forward_range<Base>)
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(!std::ranges::forward_range<SplitViewInput>);
+static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&>);
+static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
new file mode 100644
index 0000000000000..a66e3d1e84c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+  int* begin() const;
+  Sent end();
+};
+
+struct Pred {
+  bool operator()(int i) const;
+};
+
+using Sentinel = std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>;
+
+static_assert(!std::is_constructible_v<Sentinel, std::ranges::sentinel_t<Range>, const Pred*>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..e95042e26e146
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(W value);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
+
+using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
+static_assert(!std::is_constructible_v<Iter, SomeInt>);
+static_assert(!std::is_convertible_v<SomeInt, Iter>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..a39efb4318794
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.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++20
+
+// constexpr explicit sentinel(Bound bound);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
+static_assert(!std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
+static_assert(!std::is_convertible_v<IntSentinelWith<SomeInt>, Sent>);
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..171e2dce1e5f0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(basic_istream_view& parent) noexcept;
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <sstream>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// test that the constructor is explicit
+template <class CharT>
+using IstreamView = std::ranges::basic_istream_view<int, CharT>;
+template <class CharT>
+using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
+
+static_assert(!std::constructible_from<Iter<char>, IstreamView<char>&>);
+static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
+static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
+#endif

>From cfa8219f33f0d433afaa3144d3183bd763d58854 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 09:20:54 +0300
Subject: [PATCH 20/26] Cleanup

---
 .../range.iota.view/iterator/ctor.value.compile.pass.cpp        | 2 --
 .../range.iota.view/sentinel/ctor.value.compile.pass.cpp        | 2 --
 2 files changed, 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
index e95042e26e146..fc3384e191d1d 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
index a39efb4318794..b0bcdb6c60ce7 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;

>From 1438ff9824d09adb014641acc0ae3b0775ea8d27 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:42:42 +0300
Subject: [PATCH 21/26] Fixed CI

---
 .../range.istream.view/iterator/ctor.parent.compile.pass.cpp     | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
index 171e2dce1e5f0..04ff9ac24d731 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-localization
 
 // constexpr explicit iterator(basic_istream_view& parent) noexcept;
 

>From 98ad281967c925d3cbc27adaeabafeaddddd2227 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:57:29 +0300
Subject: [PATCH 22/26] Restored a test case!

---
 .../range.istream.view/iterator/deref.pass.cpp        | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 9f9f13399392c..5df730410a4ad 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,6 +28,17 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
+
+  // operator* should return the same reference to the value stored in the view
+  {
+    auto iss = make_string_stream<CharT>("1 2 345 ");
+    std::ranges::basic_istream_view<int, CharT> isv{iss};
+    using Iter = std::ranges::iterator_t<decltype(isv)>;
+
+    Iter it1 = isv.begin();
+    Iter it2 = isv.begin();
+    assert(&*it1 == &*it2);
+  }
 }
 
 int main(int, char**) {

>From ea4f9412bc9b26b3ae55b40c23ee1ac615bbb643 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:16:34 +0300
Subject: [PATCH 23/26] Added a missing `take_view` test

---
 .../ctor.base.compile.pass.cpp                | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..9383508165609
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using TakeView = std::ranges::take_view<MoveOnlyView>;
+using Sentinel = std::ranges::sentinel_t<TakeView>;
+
+static_assert(!std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
+static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);

>From d0afec965397496a91848fdd2e46ed89c98e7039 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:34:21 +0300
Subject: [PATCH 24/26] Added missing test `join_view`

---
 .../ctor.parent.compile.pass.cpp              | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..504c945e3d918
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(Parent& parent);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using Parent = std::ranges::join_view<ParentView<ChildView>>;
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
+static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);

>From c50248da199d6cbb3d5ba6b292ef102e7e960d30 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:54:39 +0300
Subject: [PATCH 25/26] Update tests `split_view`

---
 ...tor.parent.iter.subrange.compile.pass.cpp} | 31 +++++------------
 ....pass.cpp => ctor.parent.compile.pass.cpp} | 34 ++++---------------
 2 files changed, 14 insertions(+), 51 deletions(-)
 rename libcxx/test/std/ranges/range.adaptors/range.split/iterator/{ctor.base.pass.cpp => ctor.parent.iter.subrange.compile.pass.cpp} (64%)
 rename libcxx/test/std/ranges/range.adaptors/range.split/sentinel/{ctor.parent.pass.cpp => ctor.parent.compile.pass.cpp} (57%)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
similarity index 64%
rename from libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
index 823e22e117f77..9f4e9a13e0447 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
@@ -6,12 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
-// constexpr iterator begin();
+// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
 
-#include <cassert>
 #include <ranges>
+#include <type_traits>
 
 #include "../types.h"
 
@@ -30,24 +32,7 @@ struct TracedMoveView : std::ranges::view_base {
   constexpr TracedMoveIter end() const { return {}; }
 };
 
-constexpr bool test() {
-  using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
-  using SplitIter = std::ranges::iterator_t<SplitView>;
-
-  SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = sv.begin();
-  assert(iter.base().moved);
-
-  auto subRange = *iter;
-  assert(subRange.begin().moved);
-  assert(subRange.end().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
+using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
+using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  return 0;
-}
+static_assert(!std::is_constructible_v<SplitIter, SplitView, TracedMoveIter, std::ranges::subrange<TracedMoveIter>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
similarity index 57%
rename from libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 6e1ba912a201b..85d7e908a057c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -6,41 +6,19 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
 // constexpr explicit sentinel(split_view& parent);
 
-#include <cassert>
+// The constructor is now `private` (exposition-only) per P3059R2.
+
 #include <ranges>
-#include <type_traits>
 
-#include "test_iterators.h"
+#include "../types.h"
 
-// test explicit
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-constexpr bool test() {
-  {
-    int buffer[] = {0, 1, 2};
-    Range input{buffer, sentinel_wrapper<int*>(buffer + 3)};
-    SplitView sv(input, -1);
-    auto it = sv.begin();
-
-    auto sent = sv.end();
-    assert(sent != it);
-
-    ++it;
-    assert(sent == it);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
+static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_convertible_v<SplitView&, SplitSent>);

>From 50dc3e4c1aa67aa0e1b582eeecfbc904d7c80ef1 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:57:06 +0300
Subject: [PATCH 26/26] Fixed test

---
 .../range.split/sentinel/ctor.parent.compile.pass.cpp           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 85d7e908a057c..e0e8029c4c538 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -20,5 +20,5 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
 static_assert(!std::is_convertible_v<SplitView&, SplitSent>);


From libcxx-commits at lists.llvm.org  Wed Jul 22 02:03:43 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 02:03:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a60876f.27b3b003.207f1a.d8ae@mx.google.com>


================
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
----------------
philnik777 wrote:

Why is this not constant evaluated?

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 02:03:43 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 02:03:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a60876f.d76d81af.3b68ca.a70f@mx.google.com>


================
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
----------------
philnik777 wrote:

This seems unused?

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 02:03:43 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 02:03:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a60876f.bc28b987.1c0bcd.8855@mx.google.com>


================
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
+    if (std::is_signed<T>::value) {
----------------
philnik777 wrote:

I think it would be more consistent to use `numeric_limits::is_signed`. You should also consider using an alias at the top of the function to make the tests a bit less verbose.

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 02:03:43 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 02:03:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a60876f.c56fb102.6921f.7a88@mx.google.com>


================
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
----------------
philnik777 wrote:

Why is this here and not in `main` like usually?

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 02:04:58 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 02:04:58 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] Fix vector types w.r.t. qualified
	element type. (PR #197624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/197624@github.com>
Message-ID: <6a6087ba.743400b0.25573c.d458@mx.google.com>


philnik777 wrote:

I'd like to see a conclusion to the RFC before spending more time reviewing this. (CC @AaronBallman)

https://github.com/llvm/llvm-project/pull/197624

From libcxx-commits at lists.llvm.org  Wed Jul 22 02:08:17 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 02:08:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP - [libc++][string] P2587R3:
 `to_string` or not `to_string` (PR #78100)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/78100@github.com>
Message-ID: <6a608881.824e11a6.322560.a407@mx.google.com>


H-G-Hristov wrote:

Since this isn't going anywhere. I'll close it for now.

https://github.com/llvm/llvm-project/pull/78100

From libcxx-commits at lists.llvm.org  Wed Jul 22 02:08:18 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 02:08:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] WIP - [libc++][string] P2587R3:
 `to_string` or not `to_string` (PR #78100)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/78100@github.com>
Message-ID: <6a608882.bc28b987.1c0bcd.21be@mx.google.com>


https://github.com/H-G-Hristov closed https://github.com/llvm/llvm-project/pull/78100

From libcxx-commits at lists.llvm.org  Wed Jul 22 02:09:01 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 02:09:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement any_of in terms of
	find_if (PR #207274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207274@github.com>
Message-ID: <6a6088ad.160324b0.17e275.c091@mx.google.com>


https://github.com/philnik777 approved this pull request.

Looks like the compiler is finally able to vectorize most of our loops. Yay!

https://github.com/llvm/llvm-project/pull/207274

From libcxx-commits at lists.llvm.org  Wed Jul 22 02:25:59 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 22 Jul 2026 02:25:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize std::swap of locales
	(PR #209760)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209760@github.com>
Message-ID: <6a608ca7.eb165e5c.2e0c9a.c1ef@mx.google.com>


================
@@ -143,8 +143,14 @@ private:
   friend bool has_facet(const locale&) _NOEXCEPT;
   template <class _Facet>
   friend const _Facet& use_facet(const locale&);
+
+  friend void swap(locale&, locale&);
 };
 
+inline void swap(locale& __lhs, locale& __rhs) {
----------------
frederick-vs-ja wrote:

Also, existence of move functions affects overload resolution ([Godbolt link](https://godbolt.org/z/Gh8EfE6ex)), although there's implementation divergence and I think Clang and GCC are correct.

Now I'm unsure whether move functions would be wanted because (at least for libc++) currently some precondition-free functions assume that a valid `locale` is non-empty.

How about do this in a way not affecting overload resolution? E.g. by
- specializing the `std::swap` function template,
- adding a sufficiently constraint overload for `std::swap`, or
- (infeasible in C++03 mode) using `if constexpr` and some internal trait in `std::swap`.

https://github.com/llvm/llvm-project/pull/209760

From libcxx-commits at lists.llvm.org  Wed Jul 22 02:47:43 2026
From: libcxx-commits at lists.llvm.org (Pavel Novikov via libcxx-commits)
Date: Wed, 22 Jul 2026 02:47:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] fix minor performance issue in
 `basic_string<C>::append()` (PR #210078)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210078@github.com>
Message-ID: <6a6091bf.8e75e0cb.2de827.3c2e@mx.google.com>


https://github.com/toughengineer edited https://github.com/llvm/llvm-project/pull/210078

From libcxx-commits at lists.llvm.org  Wed Jul 22 02:49:41 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 02:49:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a609235.c4d3d80a.16dae5.9f1f@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From 68da1731c2ad8f5e98872b8df5e8dd5bfa646008 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/27] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From c4d11be4adc7c07fd52f608bea12da28e8221371 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/27] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 781eaa5dbef3351d87574cf432106a2a7d7c9a38 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/27] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From 195931a66e133699188de65ed5b8ee7f9aef2fe7 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/27] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 26cf1daff36cae1d3e5f5476f7faba71cb019296 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/27] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 11b933048f6a2b6279d2ff8b643ce845975d5efe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/27] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 74f795f5f559b72803edd152d2f60b772dd697d8 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/27] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From 3f679ac8813a96d8179f8b72000153c3be0c1f50 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/27] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From 1bd7d694c3339a6b4e16a60a00817dc2130aa312 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/27] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 5a43b67d99477e25e09d5e2bf471de20bd21ad64 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/27] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index a77e98f845a06..425c13fea707e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -39,7 +39,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +47,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +63,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -81,7 +81,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index a8bc817ced291..ce81d1c00d376 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From b4695a242823d5f4943d22cf64802696a092191c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/27] Format

---
 libcxx/docs/Status/Cxx26Papers.csv                |  2 +-
 .../range.elements/iterator/base.pass.cpp         | 15 ++++++++-------
 .../range.elements/sentinel/base.pass.cpp         |  2 +-
 .../range.split/iterator/base.pass.cpp            | 10 +++++-----
 .../range.split/iterator/deref.pass.cpp           |  7 +++----
 .../sentinel/ctor.convert.pass.cpp                |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..ffa36d3f5145c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 425c13fea707e..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -81,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From ee7863618f3b2d5bc4fc88409b2d77d0424444ec Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/27] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..052e5eab25433 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index ffa36d3f5145c..4d0fbca940b92 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 11b36027bc4737a06ac1368d0c4027bead46a6c4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/27] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From 4127ad1211b268ab699b7c84aa543f8ce99fdc90 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/27] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp               | 16 ++++++++++++----
 .../sentinel/ctor.convert.pass.cpp             | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index ce81d1c00d376..25d1413b2fa16 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -67,15 +67,21 @@ static_assert(
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From c33bac978f0ecf59b457c38f5a12c79c56af2fac Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/27] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From e35c0d0bed0d974cacdc104648177102db7dedbe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/27] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}

>From dc161708c53ce4ecf0b5596956eec6f7b03cfe36 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:05:55 +0300
Subject: [PATCH 17/27] Fixed formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index e28c36b1254af..bcce8e1f48f7b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,8 +22,7 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
+concept IsBaseNoexcept = requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From f4d45145cd5092bb65a8137a243dc94a8e107812 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:18:55 +0300
Subject: [PATCH 18/27] Enable test

---
 .../ranges/range.adaptors/range.elements/sentinel/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index 885582a5c94a8..114b7fc1f1b71 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -37,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
   return 0;
 }

>From 851f503a96a63350ca8f809a8c5deca584405d68 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 07:53:40 +0300
Subject: [PATCH 19/27] Added `std::is_constructible/convertible` tests

---
 .../iterator/ctor.base.compile.pass.cpp       | 23 ++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 30 ++++++++++++++++
 .../ctor.parent.iter.compile.pass.cpp         | 25 +++++++++++++
 .../sentinel/ctor.parent.compile.pass.cpp     | 25 +++++++++++++
 .../ctor.outer_iterator.compile.pass.cpp      | 23 ++++++++++++
 .../ctor.parent.base.compile.pass.cpp         | 22 ++++++++++++
 .../ctor.parent.compile.pass.cpp              | 23 ++++++++++++
 .../sentinel/ctor.base.pred.compile.pass.cpp  | 35 +++++++++++++++++++
 .../iterator/ctor.value.compile.pass.cpp      | 26 ++++++++++++++
 .../sentinel/ctor.value.compile.pass.cpp      | 24 +++++++++++++
 .../iterator/ctor.parent.compile.pass.cpp     | 33 +++++++++++++++++
 11 files changed, 289 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..b43a60276e280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+using BaseIter     = std::tuple<int>*;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
+
+static_assert(!std::is_constructible_v<ElementsIter, BaseIter>);
+static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..ee4a297c1f4a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+  std::tuple<int>* begin() const;
+  Sent end();
+};
+
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..4a013d0072222
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..06e06aeb6db6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(filter_view&);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterSentinel, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterSentinel>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..37fe2fadfd41a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<InnerIterConst, OuterIterConst>);
+static_assert(!std::is_convertible_v<InnerIterConst, OuterIterConst>);
+
+static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterNonConst>);
+static_assert(!std::is_convertible_v<InnerIterNonConst, OuterIterNonConst>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
new file mode 100644
index 0000000000000..70e97ea04f5c0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
+//   requires forward_range<Base>
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(std::ranges::forward_range<SplitViewForward>);
+static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&, std::ranges::iterator_t<ForwardView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..b838970718c8a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
+//   requires (!forward_range<Base>)
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(!std::ranges::forward_range<SplitViewInput>);
+static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&>);
+static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
new file mode 100644
index 0000000000000..a66e3d1e84c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+  int* begin() const;
+  Sent end();
+};
+
+struct Pred {
+  bool operator()(int i) const;
+};
+
+using Sentinel = std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>;
+
+static_assert(!std::is_constructible_v<Sentinel, std::ranges::sentinel_t<Range>, const Pred*>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..e95042e26e146
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(W value);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
+
+using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
+static_assert(!std::is_constructible_v<Iter, SomeInt>);
+static_assert(!std::is_convertible_v<SomeInt, Iter>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..a39efb4318794
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.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++20
+
+// constexpr explicit sentinel(Bound bound);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
+static_assert(!std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
+static_assert(!std::is_convertible_v<IntSentinelWith<SomeInt>, Sent>);
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..171e2dce1e5f0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(basic_istream_view& parent) noexcept;
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <sstream>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// test that the constructor is explicit
+template <class CharT>
+using IstreamView = std::ranges::basic_istream_view<int, CharT>;
+template <class CharT>
+using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
+
+static_assert(!std::constructible_from<Iter<char>, IstreamView<char>&>);
+static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
+static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
+#endif

>From cfa8219f33f0d433afaa3144d3183bd763d58854 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 09:20:54 +0300
Subject: [PATCH 20/27] Cleanup

---
 .../range.iota.view/iterator/ctor.value.compile.pass.cpp        | 2 --
 .../range.iota.view/sentinel/ctor.value.compile.pass.cpp        | 2 --
 2 files changed, 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
index e95042e26e146..fc3384e191d1d 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
index a39efb4318794..b0bcdb6c60ce7 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;

>From 1438ff9824d09adb014641acc0ae3b0775ea8d27 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:42:42 +0300
Subject: [PATCH 21/27] Fixed CI

---
 .../range.istream.view/iterator/ctor.parent.compile.pass.cpp     | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
index 171e2dce1e5f0..04ff9ac24d731 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-localization
 
 // constexpr explicit iterator(basic_istream_view& parent) noexcept;
 

>From 98ad281967c925d3cbc27adaeabafeaddddd2227 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:57:29 +0300
Subject: [PATCH 22/27] Restored a test case!

---
 .../range.istream.view/iterator/deref.pass.cpp        | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 9f9f13399392c..5df730410a4ad 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,6 +28,17 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
+
+  // operator* should return the same reference to the value stored in the view
+  {
+    auto iss = make_string_stream<CharT>("1 2 345 ");
+    std::ranges::basic_istream_view<int, CharT> isv{iss};
+    using Iter = std::ranges::iterator_t<decltype(isv)>;
+
+    Iter it1 = isv.begin();
+    Iter it2 = isv.begin();
+    assert(&*it1 == &*it2);
+  }
 }
 
 int main(int, char**) {

>From ea4f9412bc9b26b3ae55b40c23ee1ac615bbb643 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:16:34 +0300
Subject: [PATCH 23/27] Added a missing `take_view` test

---
 .../ctor.base.compile.pass.cpp                | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..9383508165609
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using TakeView = std::ranges::take_view<MoveOnlyView>;
+using Sentinel = std::ranges::sentinel_t<TakeView>;
+
+static_assert(!std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
+static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);

>From d0afec965397496a91848fdd2e46ed89c98e7039 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:34:21 +0300
Subject: [PATCH 24/27] Added missing test `join_view`

---
 .../ctor.parent.compile.pass.cpp              | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..504c945e3d918
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(Parent& parent);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using Parent = std::ranges::join_view<ParentView<ChildView>>;
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
+static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);

>From c50248da199d6cbb3d5ba6b292ef102e7e960d30 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:54:39 +0300
Subject: [PATCH 25/27] Update tests `split_view`

---
 ...tor.parent.iter.subrange.compile.pass.cpp} | 31 +++++------------
 ....pass.cpp => ctor.parent.compile.pass.cpp} | 34 ++++---------------
 2 files changed, 14 insertions(+), 51 deletions(-)
 rename libcxx/test/std/ranges/range.adaptors/range.split/iterator/{ctor.base.pass.cpp => ctor.parent.iter.subrange.compile.pass.cpp} (64%)
 rename libcxx/test/std/ranges/range.adaptors/range.split/sentinel/{ctor.parent.pass.cpp => ctor.parent.compile.pass.cpp} (57%)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
similarity index 64%
rename from libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
index 823e22e117f77..9f4e9a13e0447 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
@@ -6,12 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
-// constexpr iterator begin();
+// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
 
-#include <cassert>
 #include <ranges>
+#include <type_traits>
 
 #include "../types.h"
 
@@ -30,24 +32,7 @@ struct TracedMoveView : std::ranges::view_base {
   constexpr TracedMoveIter end() const { return {}; }
 };
 
-constexpr bool test() {
-  using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
-  using SplitIter = std::ranges::iterator_t<SplitView>;
-
-  SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = sv.begin();
-  assert(iter.base().moved);
-
-  auto subRange = *iter;
-  assert(subRange.begin().moved);
-  assert(subRange.end().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
+using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
+using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  return 0;
-}
+static_assert(!std::is_constructible_v<SplitIter, SplitView, TracedMoveIter, std::ranges::subrange<TracedMoveIter>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
similarity index 57%
rename from libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 6e1ba912a201b..85d7e908a057c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -6,41 +6,19 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
 // constexpr explicit sentinel(split_view& parent);
 
-#include <cassert>
+// The constructor is now `private` (exposition-only) per P3059R2.
+
 #include <ranges>
-#include <type_traits>
 
-#include "test_iterators.h"
+#include "../types.h"
 
-// test explicit
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-constexpr bool test() {
-  {
-    int buffer[] = {0, 1, 2};
-    Range input{buffer, sentinel_wrapper<int*>(buffer + 3)};
-    SplitView sv(input, -1);
-    auto it = sv.begin();
-
-    auto sent = sv.end();
-    assert(sent != it);
-
-    ++it;
-    assert(sent == it);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
+static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_convertible_v<SplitView&, SplitSent>);

>From 50dc3e4c1aa67aa0e1b582eeecfbc904d7c80ef1 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:57:06 +0300
Subject: [PATCH 26/27] Fixed test

---
 .../range.split/sentinel/ctor.parent.compile.pass.cpp           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 85d7e908a057c..e0e8029c4c538 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -20,5 +20,5 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
 static_assert(!std::is_convertible_v<SplitView&, SplitSent>);

>From d93fdef4663517663ff4169230e6b2333f637d6e Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 12:49:06 +0300
Subject: [PATCH 27/27] Added missing `transform_view` tests

---
 .../ctor.parent.iter.compile.pass.cpp         | 23 ++++++++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 24 +++++++++++++++++++
 .../{iterator => sentinel}/sentinel.pass.cpp  |  0
 3 files changed, 47 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
 rename libcxx/test/std/ranges/range.adaptors/range.transform/{iterator => sentinel}/sentinel.pass.cpp (100%)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..e7841dede5bc2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+//  constexpr iterator(Parent& parent, iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using TransformView = std::ranges::transform_view<MoveOnlyView, PlusOne>;
+using TransformViewBaseIter =  std::ranges::iterator_t<MoveOnlyView>;
+using TransformIter = std::ranges::iterator_t<TransformView>;
+
+static_assert(!std::is_constructible_v<TransformIter, TransformView, TransformViewBaseIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..08be4bbd16667
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
+using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
+using SplitSent = std::ranges::sentinel_t<SplitView>;
+
+static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp
similarity index 100%
rename from libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp


From libcxx-commits at lists.llvm.org  Wed Jul 22 02:56:36 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 02:56:36 -0700 (PDT)
Subject: [libcxx-commits] [clang] [compiler-rt] [libcxx] [llvm] [polly]
 Backport for #203715 on llvm 23 (PR #211219)
Message-ID: <6a6093d4.c5504292.1bbfc8.73c6@mx.google.com>

=?utf-8?b?6ZmI5a2Q5piC?= <2802328816 at qq.com>,Karthika Devi C
 <kartc at qti.qualcomm.com>,Karthika Devi C <kartc at qti.qualcomm.com>,Amy Kwan
 <amy.kwan1 at ibm.com>,Erich Keane <ekeane at nvidia.com>,Tom Stellard
 <tstellar at redhat.com>,Pawan Nirpal <pnirpal at qti.qualcomm.com>,hev
 <wangrui at loongson.cn>,Daniel Kiss <daniel.kiss at arm.com>,Ikhlas Ajbar
 <iajbar at quicinc.com>,maflcko <6399679+maflcko at users.noreply.github.com>,Nikita
 Popov <npopov at redhat.com>,cqwrteur <oyzawqgcfc at gmail.com>,Jacek Caban
 <jacek at codeweavers.com>,Nikita Popov <npopov at redhat.com>,Tom Stellard
 <tstellar at redhat.com>,Douglas Yung <douglas.yung at sony.com>,Douglas Yung
 <douglas.yung at sony.com>,Tom Stellard <tstellar at redhat.com>,Tom Stellard
 <tstellar at redhat.com>,Louis Dionne <ldionne.2 at gmail.com>,Tom Stellard
 <tstellar at redhat.com>,Ajay Wakodikar <ajaywakodikarsocial at gmail.com>,Ajay
 Wakodikar <ajaywakodikarsocial at gmail.com>
Message-ID: <llvm.org/llvm/llvm-project/pull/211219 at github.com>
In-Reply-To:


https://github.com/arrowten created https://github.com/llvm/llvm-project/pull/211219

None

>From f0777c1f997c0a95897dbaba2919bb96236849b2 Mon Sep 17 00:00:00 2001
From: Douglas Yung <douglas.yung at sony.com>
Date: Tue, 14 Jul 2026 13:24:57 +0000
Subject: [PATCH 01/35] Bump version to 23.1.0git

---
 cmake/Modules/LLVMVersion.cmake          | 2 +-
 libcxx/include/__config                  | 2 +-
 llvm/utils/gn/secondary/llvm/version.gni | 2 +-
 llvm/utils/lit/lit/__init__.py           | 2 +-
 llvm/utils/mlgo-utils/mlgo/__init__.py   | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/cmake/Modules/LLVMVersion.cmake b/cmake/Modules/LLVMVersion.cmake
index fa1b0997cd3e8..ed3dfebf7548a 100644
--- a/cmake/Modules/LLVMVersion.cmake
+++ b/cmake/Modules/LLVMVersion.cmake
@@ -4,7 +4,7 @@ if(NOT DEFINED LLVM_VERSION_MAJOR)
   set(LLVM_VERSION_MAJOR 23)
 endif()
 if(NOT DEFINED LLVM_VERSION_MINOR)
-  set(LLVM_VERSION_MINOR 0)
+  set(LLVM_VERSION_MINOR 1)
 endif()
 if(NOT DEFINED LLVM_VERSION_PATCH)
   set(LLVM_VERSION_PATCH 0)
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 3f271ff504767..d6caacef6691d 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -36,7 +36,7 @@
 // _LIBCPP_VERSION represents the version of libc++, which matches the version of LLVM.
 // Given a LLVM release LLVM XX.YY.ZZ (e.g. LLVM 17.0.1 == 17.00.01), _LIBCPP_VERSION is
 // defined to XXYYZZ.
-#  define _LIBCPP_VERSION 230000
+#  define _LIBCPP_VERSION 230100
 
 #  if defined(_LIBCPP_ABI_MICROSOFT) && !defined(_LIBCPP_NO_VCRUNTIME)
 #    define _LIBCPP_ABI_VCRUNTIME
diff --git a/llvm/utils/gn/secondary/llvm/version.gni b/llvm/utils/gn/secondary/llvm/version.gni
index d772c1ec3f0b8..8420f988572e3 100644
--- a/llvm/utils/gn/secondary/llvm/version.gni
+++ b/llvm/utils/gn/secondary/llvm/version.gni
@@ -1,4 +1,4 @@
 llvm_version_major = 23
-llvm_version_minor = 0
+llvm_version_minor = 1
 llvm_version_patch = 0
 llvm_version = "$llvm_version_major.$llvm_version_minor.$llvm_version_patch"
diff --git a/llvm/utils/lit/lit/__init__.py b/llvm/utils/lit/lit/__init__.py
index 8356da6ef9502..d6c6d64cbe234 100644
--- a/llvm/utils/lit/lit/__init__.py
+++ b/llvm/utils/lit/lit/__init__.py
@@ -2,7 +2,7 @@
 
 __author__ = "Daniel Dunbar"
 __email__ = "daniel at minormatter.com"
-__versioninfo__ = (23, 0, 0)
+__versioninfo__ = (23, 1, 0)
 __version__ = ".".join(str(v) for v in __versioninfo__) + "dev"
 
 __all__ = []
diff --git a/llvm/utils/mlgo-utils/mlgo/__init__.py b/llvm/utils/mlgo-utils/mlgo/__init__.py
index 6ed46bc3aa56a..f0e5c8b214ff6 100644
--- a/llvm/utils/mlgo-utils/mlgo/__init__.py
+++ b/llvm/utils/mlgo-utils/mlgo/__init__.py
@@ -4,7 +4,7 @@
 
 from datetime import timezone, datetime
 
-__versioninfo__ = (23, 0, 0)
+__versioninfo__ = (23, 1, 0)
 __version__ = (
     ".".join(str(v) for v in __versioninfo__)
     + "dev"

>From a589eb4f8e64689afe2b0844a63879b7b4865ff3 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Tue, 14 Jul 2026 18:12:19 +0100
Subject: [PATCH 02/35] Revert "[libc++][NFC] Inline std::function members into
 the class body" (#209555)

Reverts llvm/llvm-project#209461

This is breaking LLDB testing bots and it also failed pre-merge CI.

(cherry picked from commit 7618426138aae95561da676a1d1e10ee0392bf78)
---
 libcxx/include/__functional/function.h | 141 ++++++++++++++++++-------
 1 file changed, 104 insertions(+), 37 deletions(-)

diff --git a/libcxx/include/__functional/function.h b/libcxx/include/__functional/function.h
index 0adc336424e2e..90072e9528484 100644
--- a/libcxx/include/__functional/function.h
+++ b/libcxx/include/__functional/function.h
@@ -340,7 +340,7 @@ template <typename _Fun>
 struct __use_small_storage
     : public integral_constant<
           bool,
-          sizeof(_Fun) <= sizeof(__policy_storage) && _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
+          sizeof(_Fun) <= sizeof(__policy_storage)&& _LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
               is_trivially_copy_constructible<_Fun>::value && is_trivially_destructible<_Fun>::value> {};
 
 // Policy contains information about how to copy, destroy, and move the
@@ -628,10 +628,10 @@ class function<_Rp(_ArgTypes...)>
   // construct/copy/destroy:
   _LIBCPP_HIDE_FROM_ABI function() _NOEXCEPT {}
   _LIBCPP_HIDE_FROM_ABI function(nullptr_t) _NOEXCEPT {}
-  _LIBCPP_HIDE_FROM_ABI function(const function&)      = default;
-  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT = default;
+  _LIBCPP_HIDE_FROM_ABI function(const function&);
+  _LIBCPP_HIDE_FROM_ABI function(function&&) _NOEXCEPT;
   template <class _Fp, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(_Fp __f) : __f_(std::move(__f)) {}
+  _LIBCPP_HIDE_FROM_ABI function(_Fp);
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Alloc>
@@ -639,35 +639,23 @@ class function<_Rp(_ArgTypes...)>
   template <class _Alloc>
   _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, nullptr_t) _NOEXCEPT {}
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, const function&);
   template <class _Alloc>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, function&&);
   template <class _Fp, class _Alloc, class = _EnableIfLValueCallable<_Fp>>
-  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
+  _LIBCPP_HIDE_FROM_ABI function(allocator_arg_t, const _Alloc& __a, _Fp __f);
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI function& operator=(const function& __f) {
-    function(__f).swap(*this);
-    return *this;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI function& operator=(function&& __f) _NOEXCEPT = default;
-
-  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT {
-    __f_ = nullptr;
-    return *this;
-  }
-
+  _LIBCPP_HIDE_FROM_ABI function& operator=(const function&);
+  _LIBCPP_HIDE_FROM_ABI function& operator=(function&&) _NOEXCEPT;
+  _LIBCPP_HIDE_FROM_ABI function& operator=(nullptr_t) _NOEXCEPT;
   template <class _Fp, class = _EnableIfLValueCallable<__decay_t<_Fp>>>
-  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&& __f) {
-    function(std::forward<_Fp>(__f)).swap(*this);
-    return *this;
-  }
+  _LIBCPP_HIDE_FROM_ABI function& operator=(_Fp&&);
 
-  _LIBCPP_HIDE_FROM_ABI ~function() = default;
+  _LIBCPP_HIDE_FROM_ABI ~function();
 
   // function modifiers:
-  _LIBCPP_HIDE_FROM_ABI void swap(function& __f) _NOEXCEPT { __f_.swap(__f.__f_); }
+  _LIBCPP_HIDE_FROM_ABI void swap(function&) _NOEXCEPT;
 
 #  if _LIBCPP_STD_VER <= 14
   template <class _Fp, class _Alloc>
@@ -689,23 +677,15 @@ class function<_Rp(_ArgTypes...)>
 
 public:
   // function invocation:
-  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const { return __f_(std::forward<_ArgTypes>(__args)...); }
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes...) const;
 
 #  if _LIBCPP_HAS_RTTI
   // function target access:
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT {
-    return __f_.target_type();
-  }
-
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const std::type_info& target_type() const _NOEXCEPT;
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT {
-    return (_Tp*)(__f_.template target<_Tp>());
-  }
-
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _Tp* target() _NOEXCEPT;
   template <typename _Tp>
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT {
-    return __f_.template target<_Tp>();
-  }
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const _Tp* target() const _NOEXCEPT;
 #  endif // _LIBCPP_HAS_RTTI
 };
 
@@ -717,6 +697,93 @@ template <class _Fp, class _Stripped = __strip_signature_t<decltype(&_Fp::operat
 function(_Fp) -> function<_Stripped>;
 #  endif // _LIBCPP_STD_VER >= 17
 
+template <class _Rp, class... _ArgTypes>
+function<_Rp(_ArgTypes...)>::function(const function& __f) : __f_(__f.__f_) {}
+
+#  if _LIBCPP_STD_VER <= 14
+template <class _Rp, class... _ArgTypes>
+template <class _Alloc>
+function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, const function& __f) : __f_(__f.__f_) {}
+#  endif
+
+template <class _Rp, class... _ArgTypes>
+function<_Rp(_ArgTypes...)>::function(function&& __f) _NOEXCEPT : __f_(std::move(__f.__f_)) {}
+
+#  if _LIBCPP_STD_VER <= 14
+template <class _Rp, class... _ArgTypes>
+template <class _Alloc>
+function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, function&& __f) : __f_(std::move(__f.__f_)) {}
+#  endif
+
+template <class _Rp, class... _ArgTypes>
+template <class _Fp, class>
+function<_Rp(_ArgTypes...)>::function(_Fp __f) : __f_(std::move(__f)) {}
+
+#  if _LIBCPP_STD_VER <= 14
+template <class _Rp, class... _ArgTypes>
+template <class _Fp, class _Alloc, class>
+function<_Rp(_ArgTypes...)>::function(allocator_arg_t, const _Alloc&, _Fp __f) : __f_(std::move(__f)) {}
+#  endif
+
+template <class _Rp, class... _ArgTypes>
+function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(const function& __f) {
+  function(__f).swap(*this);
+  return *this;
+}
+
+template <class _Rp, class... _ArgTypes>
+function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(function&& __f) _NOEXCEPT {
+  __f_ = std::move(__f.__f_);
+  return *this;
+}
+
+template <class _Rp, class... _ArgTypes>
+function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(nullptr_t) _NOEXCEPT {
+  __f_ = nullptr;
+  return *this;
+}
+
+template <class _Rp, class... _ArgTypes>
+template <class _Fp, class>
+function<_Rp(_ArgTypes...)>& function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f) {
+  function(std::forward<_Fp>(__f)).swap(*this);
+  return *this;
+}
+
+template <class _Rp, class... _ArgTypes>
+function<_Rp(_ArgTypes...)>::~function() {}
+
+template <class _Rp, class... _ArgTypes>
+void function<_Rp(_ArgTypes...)>::swap(function& __f) _NOEXCEPT {
+  __f_.swap(__f.__f_);
+}
+
+template <class _Rp, class... _ArgTypes>
+_Rp function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const {
+  return __f_(std::forward<_ArgTypes>(__arg)...);
+}
+
+#  if _LIBCPP_HAS_RTTI
+
+template <class _Rp, class... _ArgTypes>
+const std::type_info& function<_Rp(_ArgTypes...)>::target_type() const _NOEXCEPT {
+  return __f_.target_type();
+}
+
+template <class _Rp, class... _ArgTypes>
+template <typename _Tp>
+_Tp* function<_Rp(_ArgTypes...)>::target() _NOEXCEPT {
+  return (_Tp*)(__f_.template target<_Tp>());
+}
+
+template <class _Rp, class... _ArgTypes>
+template <typename _Tp>
+const _Tp* function<_Rp(_ArgTypes...)>::target() const _NOEXCEPT {
+  return __f_.template target<_Tp>();
+}
+
+#  endif // _LIBCPP_HAS_RTTI
+
 template <class _Rp, class... _ArgTypes>
 inline _LIBCPP_HIDE_FROM_ABI bool operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) _NOEXCEPT {
   return !__f;

>From 38e1487f6b61e326330aa0467ec37b1287deaf69 Mon Sep 17 00:00:00 2001
From: Douglas Yung <douglas.yung at sony.com>
Date: Tue, 14 Jul 2026 12:47:31 -0400
Subject: [PATCH 03/35] Fix test that had a check embedded which depended on
 the current LLVM version number. (#209530)

When the test was refactored, it embedded the current LLVM version
number in one of the check strings so that when I bumped the LLVM
version to 24, the test failed since it was expecting 23 but the
compiler was now generating 23.

Fix this issue by changing the check to a regex so it doesn't need
updating after every LLVM version change.

(cherry picked from commit 1a072b5f008321f22e61f744b7474fe52f0ee48c)
---
 llvm/test/CodeGen/SystemZ/zos-ppa2.ll | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/SystemZ/zos-ppa2.ll b/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
index a4c1d50df5543..b1d8d47356b97 100644
--- a/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
@@ -21,9 +21,10 @@
 ; CHECK:  DC XL2'0000'
 ; CHECK: L#DVS DS 0H
 ; CHECK:  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0'
-; CHECK:  DC XL6'F2F3F0F0F0F0'
+; CHECK:  DC XL6'{{((F[[:digit:]]){6})}}'
 ; CHECK:  DC XL2'0000'
 
+
 ; CHECK: C_@@QPPA2 CATTR ALIGN(3),FILL(0),NOTEXECUTABLE,READONLY,RMODE(64),PART(.
 ; CHECK:                .&ppa2)
 ; CHECK: .&ppa2 XATTR LINKAGE(OS),REFERENCE(DATA),SCOPE(SECTION)

>From b9c1e158337a9f3975f868c493cf902406d13954 Mon Sep 17 00:00:00 2001
From: Jordan Rupprecht <rupprecht at google.com>
Date: Tue, 14 Jul 2026 15:01:26 -0500
Subject: [PATCH 04/35] [clang][test] Avoid relying on `-fuse-ld=ld` default
 value for marm64x test (#209597)

Test case added in #209324

(cherry picked from commit 0b908e100f3d88232aa3b0190e23e41ca327edf1)
---
 clang/test/Driver/msvc-link.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/test/Driver/msvc-link.c b/clang/test/Driver/msvc-link.c
index e78e457442c66..fc7a18014da73 100644
--- a/clang/test/Driver/msvc-link.c
+++ b/clang/test/Driver/msvc-link.c
@@ -45,10 +45,10 @@
 // RUN: %clang_cl -arm64EC -marm64x -fuse-ld=link -### -- %s 2>&1 | FileCheck --check-prefix=ARM64X %s
 // ARM64X: "-machine:arm64x"
 
-// RUN: %clang --target=arm64ec-pc-windows-msvc -marm64x -### %s 2>&1 | FileCheck --check-prefix=ARM64X-LLD %s
-// RUN: %clang --target=aarch64-pc-windows-msvc -marm64x -### %s 2>&1 | FileCheck --check-prefix=ARM64X-LLD %s
-// RUN: %clang_cl -marm64x -### -- %s 2>&1 | FileCheck --check-prefix=ARM64X-LLD %s
-// RUN: %clang_cl -arm64EC -marm64x -### -- %s 2>&1 | FileCheck --check-prefix=ARM64X-LLD %s
+// RUN: %clang -fuse-ld= --target=arm64ec-pc-windows-msvc -marm64x -### %s 2>&1 | FileCheck --check-prefix=ARM64X-LLD %s
+// RUN: %clang -fuse-ld= --target=aarch64-pc-windows-msvc -marm64x -### %s 2>&1 | FileCheck --check-prefix=ARM64X-LLD %s
+// RUN: %clang_cl -fuse-ld= -marm64x -### -- %s 2>&1 | FileCheck --check-prefix=ARM64X-LLD %s
+// RUN: %clang_cl -fuse-ld= -arm64EC -marm64x -### -- %s 2>&1 | FileCheck --check-prefix=ARM64X-LLD %s
 // ARM64X-LLD: lld-link
 // ARM64X-LLD-SAME: "-machine:arm64x"
 

>From 5100381e4719316bb4f00419bb52e260dc32f2e6 Mon Sep 17 00:00:00 2001
From: Garvit Gupta <garvgupt at qti.qualcomm.com>
Date: Tue, 14 Jul 2026 23:11:07 +0530
Subject: [PATCH 05/35] [RISCV] Fold 26-bit frame-index offsets into
 Xqcilo/Xqcilia load/store/addi (#209315)

Extend frame-index addressing such that the sp-relative offset resolved for a stack access that fits within 26-bit and not 12-bits; folds directly into the wide Xqcilo/Xqcilia instructions. This is achieved by adding the support for the following:

- Adding support for stack accesses to complex pattern `SelectAddrRegImm26` for 26-bit loads and stores
- Adding support for frame index operand to qc.e.addi instruction
- Allowing 26-bit offsets to fold into the loads and store instructions from qcilo/qcilia extension

(cherry picked from commit 79d1db7e00932407c4aa1028632f2c188d14cac8)
---
 llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp   |   6 +
 llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td   |   7 ++
 llvm/lib/Target/RISCV/RISCVInstrPredicates.td |  22 ++++
 llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp   |   6 +
 .../RISCV/xqcilo-xqcilia-frame-index.ll       | 113 ++++++++++++++++++
 5 files changed, 154 insertions(+)
 create mode 100644 llvm/test/CodeGen/RISCV/xqcilo-xqcilia-frame-index.ll

diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
index c86926e075c99..8d9c2d519274c 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
@@ -3579,6 +3579,10 @@ bool RISCVDAGToDAGISel::SelectAddrRegImm(SDValue Addr, SDValue &Base,
 /// compressible) standard load/store instructions.
 bool RISCVDAGToDAGISel::SelectAddrRegImm26(SDValue Addr, SDValue &Base,
                                            SDValue &Offset) {
+
+  if (SelectAddrFrameIndex(Addr, Base, Offset))
+    return true;
+
   SDLoc DL(Addr);
   MVT VT = Addr.getSimpleValueType();
 
@@ -3588,6 +3592,8 @@ bool RISCVDAGToDAGISel::SelectAddrRegImm26(SDValue Addr, SDValue &Base,
     // load/store.
     if (isInt<26>(CVal) && !isInt<12>(CVal)) {
       Base = Addr.getOperand(0);
+      if (auto *FIN = dyn_cast<FrameIndexSDNode>(Base))
+        Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), VT);
       Offset = CurDAG->getSignedTargetConstant(CVal, DL, VT);
       return true;
     }
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
index bb7b2bf08644e..ea3c5927f1da8 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
@@ -1635,6 +1635,13 @@ def : PatGprNoX0Simm26NoSimm12<or, QC_E_ORI>;
 def : PatGprNoX0Simm26NoSimm12<xor, QC_E_XORI>;
 } // Predicates = [HasVendorXqcilia, IsRV32]
 
+/// FrameIndex calculations
+
+let Predicates = [HasVendorXqcilia, IsRV32] in {
+  def : Pat<(riscv_add_like frameindex:$fi, simm26_nosimm12:$offset),
+            (QC_E_ADDI (iPTR (to_tframeindex $fi)), simm26_nosimm12:$offset)>;
+} // Predicates = [HasVendorXqcilia, IsRV32]
+
 /// Load/Store operations
 
 let Predicates = [HasVendorXqcilo, IsRV32], AddedComplexity = 2 in {
diff --git a/llvm/lib/Target/RISCV/RISCVInstrPredicates.td b/llvm/lib/Target/RISCV/RISCVInstrPredicates.td
index 367f91bb9902f..cff834150cfeb 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrPredicates.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrPredicates.td
@@ -242,3 +242,25 @@ def isBaseStore
                        SH,
                        SB,
                      ]>>>;
+
+// Xqcilo 26-bit offset Loads (qc.e.lb, qc.e.lbu, qc.e.lh, qc.e.lhu, qc.e.lw)
+def isBaseQCLoad
+    : TIIPredicate<"isBaseQCLoad",
+                   MCReturnStatement<
+                     CheckOpcode<[
+                       QC_E_LB,
+                       QC_E_LBU,
+                       QC_E_LH,
+                       QC_E_LHU,
+                       QC_E_LW
+                     ]>>>;
+
+// Xqcilo 26-bit offset Stores (qc.e.sb, qc.e.sh, qc.e.sw)
+def isBaseQCStore
+    : TIIPredicate<"isBaseQCStore",
+                   MCReturnStatement<
+                     CheckOpcode<[
+                       QC_E_SB,
+                       QC_E_SH,
+                       QC_E_SW
+                     ]>>>;
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
index 7dbdc881ea6c0..3311841505685 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
@@ -588,6 +588,7 @@ bool RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
   if (!IsRVVSpill) {
     int64_t Val = Offset.getFixed();
     int64_t Lo12 = SignExtend64<12>(Val);
+    int64_t Lo26 = SignExtend64<26>(Val);
     unsigned Opc = MI.getOpcode();
 
     if (Opc == RISCV::ADDI && !isInt<12>(Val)) {
@@ -614,6 +615,11 @@ bool RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
       // instruction will add 4 to the immediate. If that would overflow 12
       // bits, we can't fold the offset.
       MI.getOperand(FIOperandNum + 1).ChangeToImmediate(0);
+    } else if (Opc == RISCV::QC_E_ADDI || RISCVInstrInfo::isBaseQCLoad(MI) ||
+               RISCVInstrInfo::isBaseQCStore(MI)) {
+      MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Lo26);
+      Offset = StackOffset::get((uint64_t)Val - (uint64_t)Lo26,
+                                Offset.getScalable());
     } else {
       // We can encode an add with 12 bit signed immediate in the immediate
       // operand of our user instruction.  As a result, the remaining
diff --git a/llvm/test/CodeGen/RISCV/xqcilo-xqcilia-frame-index.ll b/llvm/test/CodeGen/RISCV/xqcilo-xqcilia-frame-index.ll
new file mode 100644
index 0000000000000..eaf5b78f30ff2
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/xqcilo-xqcilia-frame-index.ll
@@ -0,0 +1,113 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; Frame-index addressing for the Qualcomm Xqcilo/Xqcilia large-offset
+; instructions (qc.e.lw/qc.e.sw/qc.e.addi). Stack accesses whose resolved
+; sp-relative offset is a 26-bit (but not 12-bit) signed immediate fold the
+; offset directly into the wide instruction instead of materialising a base.
+;
+; RUN: llc < %s -mtriple=riscv32 -mattr=+xqcilo,+xqcilia -riscv-no-aliases \
+; RUN:   | FileCheck %s
+
+; Load variant: A load at a large (simm26, not simm12) offset off a stack slot
+; folds the resolved frame offset into a single qc.e.lw. The offset-0 access
+; keeps the alloca live.
+define i32 @stack_fi_load(i32 %x) nounwind {
+; CHECK-LABEL: stack_fi_load:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    qc.e.addi sp, sp, -4000016
+; CHECK-NEXT:    qc.e.lw a1, 3000016(sp)
+; CHECK-NEXT:    c.mv a2, a0
+; CHECK-NEXT:    c.mv a0, a1
+; CHECK-NEXT:    c.swsp a2, 16(sp)
+; CHECK-NEXT:    qc.e.addi sp, sp, 4000016
+; CHECK-NEXT:    c.jr ra
+entry:
+  %arr = alloca [4000000 x i8], align 4
+  %p0 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 0
+  store i32 %x, ptr %p0, align 4
+  %p1 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 3000000
+  %v = load i32, ptr %p1, align 4
+  ret i32 %v
+}
+
+; Store variant: a store at a large (simm26, not simm12) offset off a stack slot
+; folds into a single qc.e.sw.
+define void @stack_fi_store(i32 %x) nounwind {
+; CHECK-LABEL: stack_fi_store:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    qc.e.addi sp, sp, -4000016
+; CHECK-NEXT:    qc.e.sw a0, 3000016(sp)
+; CHECK-NEXT:    c.swsp a0, 16(sp)
+; CHECK-NEXT:    qc.e.addi sp, sp, 4000016
+; CHECK-NEXT:    c.jr ra
+entry:
+  %arr = alloca [4000000 x i8], align 4
+  %p1 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 3000000
+  store i32 %x, ptr %p1, align 4
+  %p0 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 0
+  store i32 %x, ptr %p0, align 4
+  ret void
+}
+
+; The address of a large-offset stack element escapes via a call, so it must be
+; materialised as a value. The (frameindex + simm26) computation selects
+; QC_E_ADDI and folds the resolved 26-bit frame offset into it.
+declare void @use(ptr)
+define void @fi_addr_escapes(i32 %x) nounwind {
+; CHECK-LABEL: fi_addr_escapes:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    c.addi16sp sp, -256
+; CHECK-NEXT:    c.swsp ra, 252(sp) # 4-byte Folded Spill
+; CHECK-NEXT:    qc.e.addi sp, sp, -3999760
+; CHECK-NEXT:    qc.e.addi a0, sp, 3000012
+; CHECK-NEXT:    call use
+; CHECK-NEXT:    c.addi4spn a0, sp, 12
+; CHECK-NEXT:    call use
+; CHECK-NEXT:    qc.e.addi sp, sp, 3999760
+; CHECK-NEXT:    c.lwsp ra, 252(sp) # 4-byte Folded Reload
+; CHECK-NEXT:    c.addi16sp sp, 256
+; CHECK-NEXT:    c.jr ra
+  %arr = alloca [4000000 x i8], align 4
+  %p1 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 3000000
+  call void @use(ptr %p1)
+  %p0 = getelementptr inbounds [4000000 x i8], ptr %arr, i32 0, i32 0
+  call void @use(ptr %p0)
+  ret void
+}
+
+; A bare frame index (offset 0) whose slot resolves to a large (simm26, not
+; simm12) sp-relative offset. The small alloca is placed first so the allocator
+; lays it out high in the frame (sp + ~8204).
+define i32 @bare_fi_high_frame() nounwind {
+; CHECK-LABEL: bare_fi_high_frame:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    qc.e.addi sp, sp, -8208
+; CHECK-NEXT:    c.li a0, 1
+; CHECK-NEXT:    c.swsp a0, 12(sp)
+; CHECK-NEXT:    qc.e.lw a0, 8204(sp)
+; CHECK-NEXT:    qc.e.addi sp, sp, 8208
+; CHECK-NEXT:    c.jr ra
+  %small = alloca i32
+  %big = alloca [8192 x i8], align 4
+  %pb = getelementptr [8192 x i8], ptr %big, i32 0, i32 0
+  store volatile i32 1, ptr %pb
+  %v = load i32, ptr %small
+  ret i32 %v
+}
+
+; Store variant of the bare-frame-index case.
+define void @bare_fi_high_frame_store(i32 %x) nounwind {
+; CHECK-LABEL: bare_fi_high_frame_store:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    qc.e.addi sp, sp, -8208
+; CHECK-NEXT:    c.li a1, 1
+; CHECK-NEXT:    c.swsp a1, 12(sp)
+; CHECK-NEXT:    qc.e.sw a0, 8204(sp)
+; CHECK-NEXT:    qc.e.addi sp, sp, 8208
+; CHECK-NEXT:    c.jr ra
+  %small = alloca i32
+  %big = alloca [8192 x i8], align 4
+  %pb = getelementptr [8192 x i8], ptr %big, i32 0, i32 0
+  store volatile i32 1, ptr %pb
+  store i32 %x, ptr %small
+  ret void
+}

>From 6e708e7d66bb2c8daee1334068190c3138ca20ee Mon Sep 17 00:00:00 2001
From: Hans Wennborg <hans at hanshq.net>
Date: Tue, 14 Jul 2026 22:10:27 +0200
Subject: [PATCH 06/35] Revert "[Clang] Allow devirtualization involving array
 subscripts with constant indices when the pointee type is known [CWG1504]
 (#207540) (#209596)

It caused miscompiles, see discussion on the PR.

This reverts commit ee24ac00d874ce82eaf36431f9aeb2ad744bc3b8.

(cherry picked from commit 04aa50715cafa5f205b44bc88a6f721d296ec91c)
---
 clang/lib/AST/DeclCXX.cpp                     | 35 ----------------
 clang/test/CXX/drs/cwg15xx.cpp                | 19 ---------
 .../devirtualize-virtual-function-calls.cpp   | 42 +------------------
 clang/www/cxx_dr_status.html                  |  2 +-
 4 files changed, 3 insertions(+), 95 deletions(-)

diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index ef7e58536202b..0573cdf95952a 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -2600,41 +2600,6 @@ CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base,
     }
   }
 
-  // By CWG1504 / C++11 [expr.add]p6, pointer arithmetic on a base pointer into
-  // an array of derived objects is undefined behavior when the element type and
-  // pointee type are not similar. This means we can devirtualize calls on
-  // objects accessed through array subscripts or pointer arithmetic with
-  // non-zero offsets, since the dynamic type must match the static type.
-  //
-  // A single object is considered to be an array of one element, so p[0]
-  // could still be a derived object, but p[N] for N != 0 cannot.
-  const Expr *Inner = Base->IgnoreParenImpCasts();
-  if (const auto *UO = dyn_cast<UnaryOperator>(Inner))
-    if (UO->getOpcode() == UO_Deref)
-      Inner = UO->getSubExpr()->IgnoreParenImpCasts();
-
-  // Handle p[N].f() (dot syntax with array subscript).
-  if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(Inner)) {
-    Expr::EvalResult Result;
-    if (ASE->getIdx()->EvaluateAsInt(Result, getASTContext()) &&
-        !Result.Val.getInt().isZero())
-      return DevirtualizedMethod;
-  }
-
-  // Handle (p + N)->f() (arrow syntax with pointer arithmetic).
-  if (const auto *BO = dyn_cast<BinaryOperator>(Inner)) {
-    if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub) {
-      // Identify the integer operand (the offset).
-      const Expr *IdxExpr = BO->getLHS()->getType()->isPointerType()
-                                ? BO->getRHS()
-                                : BO->getLHS();
-      Expr::EvalResult Result;
-      if (IdxExpr->EvaluateAsInt(Result, getASTContext()) &&
-          !Result.Val.getInt().isZero())
-        return DevirtualizedMethod;
-    }
-  }
-
   // We can't devirtualize the call.
   return nullptr;
 }
diff --git a/clang/test/CXX/drs/cwg15xx.cpp b/clang/test/CXX/drs/cwg15xx.cpp
index 53f262740eae2..5a9b80ed028c4 100644
--- a/clang/test/CXX/drs/cwg15xx.cpp
+++ b/clang/test/CXX/drs/cwg15xx.cpp
@@ -11,25 +11,6 @@
 // cxx98-error at -1 {{variadic macros are a C99 feature}}
 #endif
 
-namespace cwg1504 { // cwg1504: 23
-#if __cplusplus >= 201103L
-  // CWG1504: Pointer arithmetic after derived-base conversion
-  struct Base { int x; };
-  struct Derived : Base { int y; };
-  constexpr Derived arr[2] = {};
-
-  // Pointer arithmetic on a base pointer into a derived array is UB,
-  // and the constexpr evaluator must diagnose it.
-  constexpr int test(int n) {
-    return ((const Base*)arr)[n].x; // #cwg1504-x
-  }
-  constexpr int bad = test(1);
-  // since-cxx11-error at -1 {{constexpr variable 'bad' must be initialized by a constant expression}}
-  // since-cxx11-note@#cwg1504-x {{cannot access field of pointer past the end of object}}
-  // since-cxx11-note at -3 {{in call to 'test(1)'}}
-#endif
-} // namespace cwg1504
-
 namespace cwg1512 { // cwg1512: 4
   void f(char *p) {
     if (p > 0) {}
diff --git a/clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp b/clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp
index 32a887e8fbcc9..b50881db63e05 100644
--- a/clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp
+++ b/clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp
@@ -92,48 +92,10 @@ void fd(D d, XD xd, D *p) {
   // CHECK: call void %
   p[0].f();
 
-  // We can devirtualize this, by CWG1504 / [expr.add]/6 (if the array
+  // FIXME: We can devirtualize this, by C++1z [expr.add]/6 (if the array
   // element type and the pointee type are not similar, behavior is undefined).
-  // CHECK: call void @_ZN1A1fEv
-  p[1].f();
-
-  // Negative indices are also UB for the same reason.
-  // CHECK: call void @_ZN1A1fEv
-  p[-1].f();
-
-  // Pointer arithmetic with arrow syntax: (p + N)->f()
-  // CHECK: call void @_ZN1A1fEv
-  (p + 1)->f();
-
-  // Pointer subtraction with arrow syntax: (p - N)->f()
-  // CHECK: call void @_ZN1A1fEv
-  (p - 1)->f();
-
-  // Can't devirtualize with non-constant index; we can't prove N != 0.
-  int n = 1;
-  // CHECK: call void %
-  p[n].f();
-
-  // Zero through expression: p[1-1] evaluates to 0, can't devirtualize.
-  // CHECK: call void %
-  p[1-1].f();
-
-  // (p + 0)->f() also can't be devirtualized (same as *p).
   // CHECK: call void %
-  (p + 0)->f();
-
-  // 1 + p is legal pointer arithmetic too.
-  // CHECK: call void @_ZN1A1fEv
-  (1 + p)->f();
-
-  // Constant variables are evaluated.
-  const int N = 1;
-  // CHECK: call void @_ZN1A1fEv
-  p[N].f();
-
-  // Pointer arithmetic with deref: *(p + 1)
-  // CHECK: call void @_ZN1A1fEv
-  (*(p + 1)).f();
+  p[1].f();
 }
 
 struct B {
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 73c8be98e7848..af91ac559d274 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -10309,7 +10309,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td>[<a href="https://wg21.link/expr.add">expr.add</a>]</td>
     <td>CD3</td>
     <td>Pointer arithmetic after derived-base conversion</td>
-    <td class="full" align="center">Clang 23</td>
+    <td class="unknown" align="center">Unknown</td>
   </tr>
   <tr id="1505">
     <td><a href="https://cplusplus.github.io/CWG/issues/1505.html">1505</a></td>

>From 8cc33c0fec099662f6e9ecb9e0a0c792b3643967 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Tue, 14 Jul 2026 09:32:11 -0700
Subject: [PATCH 07/35] [Clang] Require x86 target for some tests (#209532)

These tests assert behavior about the always-inliner which now requires
a target to be present to check function attribute compatibility for
inlining.

Fix forward for 37b8e765ce4837a7577e6f762bcdffe4b232759c.

(cherry picked from commit 97f76580cdb0893d0b4edc7adf703162a23c0a67)
---
 clang/test/CodeGen/memcmp-inline-builtin-to-asm.c | 2 ++
 clang/test/CodeGen/memcpy-inline-builtin.c        | 2 ++
 clang/test/CodeGen/pr9614.c                       | 2 ++
 3 files changed, 6 insertions(+)

diff --git a/clang/test/CodeGen/memcmp-inline-builtin-to-asm.c b/clang/test/CodeGen/memcmp-inline-builtin-to-asm.c
index 1dae1d8f0143d..e1347bd36d17b 100644
--- a/clang/test/CodeGen/memcmp-inline-builtin-to-asm.c
+++ b/clang/test/CodeGen/memcmp-inline-builtin-to-asm.c
@@ -1,5 +1,7 @@
 // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
 
+// REQUIRES: x86-registered-target
+
 // RUN: %clang_cc1 -triple x86_64 -emit-llvm -o - %s | opt -S -passes=verify | FileCheck %s
 //
 // Verifies that clang detects memcmp inline version and uses it instead of the builtin.
diff --git a/clang/test/CodeGen/memcpy-inline-builtin.c b/clang/test/CodeGen/memcpy-inline-builtin.c
index d287ce361f571..bb86eb6515349 100644
--- a/clang/test/CodeGen/memcpy-inline-builtin.c
+++ b/clang/test/CodeGen/memcpy-inline-builtin.c
@@ -1,5 +1,7 @@
 // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
 
+// REQUIRES: x86-registered-target
+
 // RUN: %clang_cc1 -triple x86_64 -emit-llvm -o - %s | FileCheck %s
 //
 // Verifies that clang detects memcpy inline version and uses it instead of the builtin.
diff --git a/clang/test/CodeGen/pr9614.c b/clang/test/CodeGen/pr9614.c
index 394af32fd6cb3..d8a7a1add5f7c 100644
--- a/clang/test/CodeGen/pr9614.c
+++ b/clang/test/CodeGen/pr9614.c
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -triple x86_64-pc-linux -emit-llvm %s -o - | FileCheck %s
 
+// REQUIRES: x86-registered-target
+
 extern void foo_alias (void) __asm ("foo");
 inline void foo (void) {
   return foo_alias ();

>From a431e974f000a55eef4936a92965034eb1f5acaf Mon Sep 17 00:00:00 2001
From: Krisitan Erik Olsen <kristian.erik at outlook.com>
Date: Wed, 15 Jul 2026 02:18:18 -0700
Subject: [PATCH 08/35] [X86] Skip debug instructions in tile copy lowering
 (#209640)

X86LowerTileCopy iterates over instructions in reverse to track live
registers, calling LiveRegUnits::stepBackward on every instruction
unconditionally. Since commit 16b2ef32 added an assertion that
stepBackward must not receive debug instructions, functions containing
debug info hit the assertion during tile copy lowering.
This patch skips debug instructions early in the loop, matching the
guard pattern used in RegisterScavenging.cpp and X86FixupBWInsts.cpp.

Fixes #209512

(cherry picked from commit 3e4160fdc3d5bb0ed86480604bdfcd3130e7dd37)
---
 llvm/lib/Target/X86/X86LowerTileCopy.cpp |  2 ++
 llvm/test/CodeGen/X86/AMX/pr209512.ll    | 28 ++++++++++++++++++++++++
 2 files changed, 30 insertions(+)
 create mode 100644 llvm/test/CodeGen/X86/AMX/pr209512.ll

diff --git a/llvm/lib/Target/X86/X86LowerTileCopy.cpp b/llvm/lib/Target/X86/X86LowerTileCopy.cpp
index e5a374dfcc0b6..628f691279600 100644
--- a/llvm/lib/Target/X86/X86LowerTileCopy.cpp
+++ b/llvm/lib/Target/X86/X86LowerTileCopy.cpp
@@ -87,6 +87,8 @@ static bool lowerTileCopy(MachineFunction &MF) {
     LiveRegUnits UsedRegs(*TRI);
     UsedRegs.addLiveOuts(MBB);
     for (MachineInstr &MI : llvm::make_early_inc_range(reverse(MBB))) {
+      if (MI.isDebugInstr())
+        continue;
       UsedRegs.stepBackward(MI);
       if (!MI.isCopy())
         continue;
diff --git a/llvm/test/CodeGen/X86/AMX/pr209512.ll b/llvm/test/CodeGen/X86/AMX/pr209512.ll
new file mode 100644
index 0000000000000..975aa5c1e5f6c
--- /dev/null
+++ b/llvm/test/CodeGen/X86/AMX/pr209512.ll
@@ -0,0 +1,28 @@
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+amx-int8 -mattr=+avx512f -verify-machineinstrs -o /dev/null
+
+define void @test_debug_instr(ptr %arg) #0 !dbg !4 {
+  #dbg_declare(ptr %arg, !12, !DIExpression(), !15)
+  %1 = call x86_amx @llvm.x86.cast.vector.to.tile.v1024i8(<1024 x i8> zeroinitializer)
+  %2 = call x86_amx @llvm.x86.tdpbssd.internal(i16 0, i16 0, i16 0, x86_amx %1, x86_amx %1, x86_amx %1)
+  ret void
+}
+
+declare x86_amx @llvm.x86.cast.vector.to.tile.v1024i8(<1024 x i8>) #1
+declare x86_amx @llvm.x86.tdpbssd.internal(i16, i16, i16, x86_amx, x86_amx, x86_amx) #2
+
+attributes #0 = { "target-features"="+amx-int8" }
+attributes #1 = { nocallback nofree nosync nounwind willreturn memory(none) }
+attributes #2 = { nounwind }
+
+!llvm.module.flags = !{!0}
+!llvm.dbg.cu = !{!1}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C11, file: !2, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, nameTableKind: None)
+!2 = !DIFile(filename: "test.c", directory: "/tmp")
+!3 = !{}
+!4 = distinct !DISubprogram(name: "test_debug_instr", linkageName: "test_debug_instr", scope: !2, file: !2, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !1, retainedNodes: !3)
+!9 = distinct !DISubroutineType(types: !3)
+!12 = distinct !DILocalVariable(name: "a", arg: 1, scope: !4, file: !2, line: 1, type: !13)
+!13 = !DICompositeType(tag: DW_TAG_structure_type, name: "__tile1024i", scope: !4, file: !2, size: 16384, align: 8192, flags: DIFlagPublic, elements: !3, identifier: "__tile1024i")
+!15 = !DILocation(line: 1, column: 1, scope: !4)

>From ed334243b371778ed994f6bec07b49bb33379d4a Mon Sep 17 00:00:00 2001
From: Lukacma <Marian.Lukac at arm.com>
Date: Wed, 15 Jul 2026 13:25:07 +0100
Subject: [PATCH 09/35] [AArch64] Remove HCX feature flag from backend
 (#209477)

This patch removes +hcx option from the llvm and removes guarding of
HCRX_EL2 system register on it.

(cherry picked from commit f48200029bf6277d4fe3915b480a5c87c8f172c9)
---
 .../Driver/print-enabled-extensions/aarch64-ampere1b.c |  1 -
 .../Driver/print-enabled-extensions/aarch64-ampere1c.c |  1 -
 .../print-enabled-extensions/aarch64-apple-a16.c       |  1 -
 .../print-enabled-extensions/aarch64-apple-a17.c       |  1 -
 .../Driver/print-enabled-extensions/aarch64-apple-m4.c |  1 -
 .../Driver/print-enabled-extensions/aarch64-apple-m5.c |  1 -
 .../print-enabled-extensions/aarch64-armv8.7-a.c       |  1 -
 .../print-enabled-extensions/aarch64-armv8.8-a.c       |  1 -
 .../print-enabled-extensions/aarch64-armv8.9-a.c       |  1 -
 .../print-enabled-extensions/aarch64-armv9.2-a.c       |  1 -
 .../print-enabled-extensions/aarch64-armv9.3-a.c       |  1 -
 .../print-enabled-extensions/aarch64-armv9.4-a.c       |  1 -
 .../print-enabled-extensions/aarch64-armv9.5-a.c       |  1 -
 .../Driver/print-enabled-extensions/aarch64-c1-nano.c  |  1 -
 .../print-enabled-extensions/aarch64-c1-premium.c      |  1 -
 .../Driver/print-enabled-extensions/aarch64-c1-pro.c   |  1 -
 .../Driver/print-enabled-extensions/aarch64-c1-ultra.c |  1 -
 .../print-enabled-extensions/aarch64-cortex-a320.c     |  2 --
 .../print-enabled-extensions/aarch64-cortex-a520.c     |  1 -
 .../print-enabled-extensions/aarch64-cortex-a520ae.c   |  1 -
 .../print-enabled-extensions/aarch64-cortex-a720.c     |  1 -
 .../print-enabled-extensions/aarch64-cortex-a720ae.c   |  1 -
 .../print-enabled-extensions/aarch64-cortex-a725.c     |  1 -
 .../print-enabled-extensions/aarch64-cortex-x4.c       |  1 -
 .../print-enabled-extensions/aarch64-cortex-x925.c     |  1 -
 .../print-enabled-extensions/aarch64-fujitsu-monaka.c  |  1 -
 .../Driver/print-enabled-extensions/aarch64-gb10.c     |  1 -
 .../Driver/print-enabled-extensions/aarch64-hip12.c    |  1 -
 .../print-enabled-extensions/aarch64-neoverse-n3.c     |  1 -
 .../print-enabled-extensions/aarch64-neoverse-v3.c     |  1 -
 .../print-enabled-extensions/aarch64-neoverse-v3ae.c   |  1 -
 .../Driver/print-enabled-extensions/aarch64-olympus.c  |  1 -
 .../Driver/print-enabled-extensions/aarch64-rigel.c    |  1 -
 llvm/lib/Target/AArch64/AArch64Features.td             |  5 +----
 llvm/lib/Target/AArch64/AArch64Processors.td           | 10 ++++------
 llvm/lib/Target/AArch64/AArch64SystemOperands.td       |  4 +---
 llvm/test/MC/AArch64/armv8.7a-hcx.s                    |  7 +------
 llvm/test/MC/Disassembler/AArch64/armv8.7a-hcx.txt     |  6 +-----
 38 files changed, 8 insertions(+), 58 deletions(-)

diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-ampere1b.c b/clang/test/Driver/print-enabled-extensions/aarch64-ampere1b.c
index 444ac4526200f..dc5e5de05e1e5 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-ampere1b.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-ampere1b.c
@@ -27,7 +27,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-ampere1c.c b/clang/test/Driver/print-enabled-extensions/aarch64-ampere1c.c
index d3c1e7ebefd9e..8ec11dea30f23 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-ampere1c.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-ampere1c.c
@@ -30,7 +30,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-apple-a16.c b/clang/test/Driver/print-enabled-extensions/aarch64-apple-a16.c
index 6f417c1592b06..81c21942638e4 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-apple-a16.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-apple-a16.c
@@ -26,7 +26,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-apple-a17.c b/clang/test/Driver/print-enabled-extensions/aarch64-apple-a17.c
index 39e5b70c25d88..75ff0c59a4bbe 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-apple-a17.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-apple-a17.c
@@ -26,7 +26,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-apple-m4.c b/clang/test/Driver/print-enabled-extensions/aarch64-apple-m4.c
index b410124527322..07fd1caea0e22 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-apple-m4.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-apple-m4.c
@@ -26,7 +26,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-apple-m5.c b/clang/test/Driver/print-enabled-extensions/aarch64-apple-m5.c
index ae8923287b2e3..a7eee80014dad 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-apple-m5.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-apple-m5.c
@@ -28,7 +28,6 @@
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
 // CHECK-NEXT:     FEAT_HBC                                               Enable Armv8.8-A Hinted Conditional Branches Extension
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-armv8.7-a.c b/clang/test/Driver/print-enabled-extensions/aarch64-armv8.7-a.c
index 4a8e18b23aac8..9fca36fbdcf46 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-armv8.7-a.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-armv8.7-a.c
@@ -24,7 +24,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-armv8.8-a.c b/clang/test/Driver/print-enabled-extensions/aarch64-armv8.8-a.c
index 160acb524df5c..1db92c1ca9afa 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-armv8.8-a.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-armv8.8-a.c
@@ -25,7 +25,6 @@
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
 // CHECK-NEXT:     FEAT_HBC                                               Enable Armv8.8-A Hinted Conditional Branches Extension
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-armv8.9-a.c b/clang/test/Driver/print-enabled-extensions/aarch64-armv8.9-a.c
index 04bd8fc06049b..58b8242f28560 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-armv8.9-a.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-armv8.9-a.c
@@ -28,7 +28,6 @@
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
 // CHECK-NEXT:     FEAT_HBC                                               Enable Armv8.8-A Hinted Conditional Branches Extension
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-armv9.2-a.c b/clang/test/Driver/print-enabled-extensions/aarch64-armv9.2-a.c
index d3b26122dc6db..6329363ca29bd 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-armv9.2-a.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-armv9.2-a.c
@@ -25,7 +25,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-armv9.3-a.c b/clang/test/Driver/print-enabled-extensions/aarch64-armv9.3-a.c
index 61894f0fcefa5..e2b93c9e89557 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-armv9.3-a.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-armv9.3-a.c
@@ -26,7 +26,6 @@
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
 // CHECK-NEXT:     FEAT_HBC                                               Enable Armv8.8-A Hinted Conditional Branches Extension
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-armv9.4-a.c b/clang/test/Driver/print-enabled-extensions/aarch64-armv9.4-a.c
index 1cfda6c996b9e..31826357172ae 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-armv9.4-a.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-armv9.4-a.c
@@ -29,7 +29,6 @@
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
 // CHECK-NEXT:     FEAT_HBC                                               Enable Armv8.8-A Hinted Conditional Branches Extension
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-armv9.5-a.c b/clang/test/Driver/print-enabled-extensions/aarch64-armv9.5-a.c
index 76c8b34a56b75..4eb48de4206a6 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-armv9.5-a.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-armv9.5-a.c
@@ -31,7 +31,6 @@
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
 // CHECK-NEXT:     FEAT_HBC                                               Enable Armv8.8-A Hinted Conditional Branches Extension
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-c1-nano.c b/clang/test/Driver/print-enabled-extensions/aarch64-c1-nano.c
index 33112527c9add..ebf84aa612d9a 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-c1-nano.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-c1-nano.c
@@ -30,7 +30,6 @@
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
 // CHECK-NEXT:     FEAT_HBC                                               Enable Armv8.8-A Hinted Conditional Branches Extension
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-c1-premium.c b/clang/test/Driver/print-enabled-extensions/aarch64-c1-premium.c
index 3b146e36f81a2..63fc83641191d 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-c1-premium.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-c1-premium.c
@@ -30,7 +30,6 @@
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
 // CHECK-NEXT:     FEAT_HBC                                               Enable Armv8.8-A Hinted Conditional Branches Extension
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-c1-pro.c b/clang/test/Driver/print-enabled-extensions/aarch64-c1-pro.c
index d31a598463267..833a67d0035e2 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-c1-pro.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-c1-pro.c
@@ -30,7 +30,6 @@
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
 // CHECK-NEXT:     FEAT_HBC                                               Enable Armv8.8-A Hinted Conditional Branches Extension
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-c1-ultra.c b/clang/test/Driver/print-enabled-extensions/aarch64-c1-ultra.c
index d3ab7d92eca92..85e609fd13f27 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-c1-ultra.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-c1-ultra.c
@@ -30,7 +30,6 @@
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
 // CHECK-NEXT:     FEAT_HBC                                               Enable Armv8.8-A Hinted Conditional Branches Extension
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a320.c b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a320.c
index eed89039f3a19..71338d4f679ae 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a320.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a320.c
@@ -26,7 +26,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
@@ -57,4 +56,3 @@
 // CHECK-NEXT:     FEAT_VHE                                               Enable Armv8.1-A Virtual Host extension
 // CHECK-NEXT:     FEAT_WFxT                                              Enable Armv8.7-A WFET and WFIT instruction
 // CHECK-NEXT:     FEAT_XS                                                Enable Armv8.7-A limited-TLB-maintenance instruction
-
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a520.c b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a520.c
index 5c5f9ba81b5b8..a31428fb9fd5b 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a520.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a520.c
@@ -27,7 +27,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a520ae.c b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a520ae.c
index d68fc6e744a29..1d82d4548bd09 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a520ae.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a520ae.c
@@ -27,7 +27,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a720.c b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a720.c
index b7f0ae500ee5e..380586b69fcaf 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a720.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a720.c
@@ -27,7 +27,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a720ae.c b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a720ae.c
index 8f7139afeca67..dcb9301e339b4 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a720ae.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a720ae.c
@@ -27,7 +27,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a725.c b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a725.c
index 7c5e7c3624f69..2d531ed1b1dd6 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a725.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-a725.c
@@ -27,7 +27,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-x4.c b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-x4.c
index 09ed3f285481b..c846604a340f4 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-x4.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-x4.c
@@ -27,7 +27,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-x925.c b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-x925.c
index 3969c0922c894..31667440c25ec 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-cortex-x925.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-cortex-x925.c
@@ -27,7 +27,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-fujitsu-monaka.c b/clang/test/Driver/print-enabled-extensions/aarch64-fujitsu-monaka.c
index e0802b3866cc8..119e24dee559c 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-fujitsu-monaka.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-fujitsu-monaka.c
@@ -35,7 +35,6 @@
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
 // CHECK-NEXT:     FEAT_HBC                                               Enable Armv8.8-A Hinted Conditional Branches Extension
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-gb10.c b/clang/test/Driver/print-enabled-extensions/aarch64-gb10.c
index f482a1bd1a31e..4b1d0013094c6 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-gb10.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-gb10.c
@@ -28,7 +28,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-hip12.c b/clang/test/Driver/print-enabled-extensions/aarch64-hip12.c
index 6370303909839..8494b01967bc6 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-hip12.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-hip12.c
@@ -30,7 +30,6 @@
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
 // CHECK-NEXT:     FEAT_HBC                                               Enable Armv8.8-A Hinted Conditional Branches Extension
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-neoverse-n3.c b/clang/test/Driver/print-enabled-extensions/aarch64-neoverse-n3.c
index 9819a8388f039..ac695755c36f2 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-neoverse-n3.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-neoverse-n3.c
@@ -27,7 +27,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-neoverse-v3.c b/clang/test/Driver/print-enabled-extensions/aarch64-neoverse-v3.c
index f400d5bc3596e..3dfa448a99161 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-neoverse-v3.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-neoverse-v3.c
@@ -28,7 +28,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-neoverse-v3ae.c b/clang/test/Driver/print-enabled-extensions/aarch64-neoverse-v3ae.c
index 67d8377e95227..f8a6596305c8d 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-neoverse-v3ae.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-neoverse-v3ae.c
@@ -29,7 +29,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-olympus.c b/clang/test/Driver/print-enabled-extensions/aarch64-olympus.c
index c50c0b130307b..6eb255c2202ab 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-olympus.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-olympus.c
@@ -35,7 +35,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/clang/test/Driver/print-enabled-extensions/aarch64-rigel.c b/clang/test/Driver/print-enabled-extensions/aarch64-rigel.c
index a8a85a74445e7..c39825c0fb988 100644
--- a/clang/test/Driver/print-enabled-extensions/aarch64-rigel.c
+++ b/clang/test/Driver/print-enabled-extensions/aarch64-rigel.c
@@ -35,7 +35,6 @@
 // CHECK-NEXT:     FEAT_FRINTTS                                           Enable FRInt[32|64][Z|X] instructions that round a floating-point number to an integer (in FP format) forcing it to fit into a 32- or 64-bit int
 // CHECK-NEXT:     FEAT_FlagM                                             Enable Armv8.4-A Flag Manipulation instructions
 // CHECK-NEXT:     FEAT_FlagM2                                            Enable alternative NZCV format for floating point comparisons
-// CHECK-NEXT:     FEAT_HCX                                               Enable Armv8.7-A HCRX_EL2 system register
 // CHECK-NEXT:     FEAT_I8MM                                              Enable Matrix Multiply Int8 Extension
 // CHECK-NEXT:     FEAT_JSCVT                                             Enable Armv8.3-A JavaScript FP conversion instructions
 // CHECK-NEXT:     FEAT_LOR                                               Enable Armv8.1-A Limited Ordering Regions extension
diff --git a/llvm/lib/Target/AArch64/AArch64Features.td b/llvm/lib/Target/AArch64/AArch64Features.td
index 7cb7cd32f673b..59e841792155d 100644
--- a/llvm/lib/Target/AArch64/AArch64Features.td
+++ b/llvm/lib/Target/AArch64/AArch64Features.td
@@ -303,9 +303,6 @@ def FeatureXS : Extension<"xs", "XS", "FEAT_XS",
 def FeatureWFxT : ExtensionWithMArch<"wfxt", "WFxT", "FEAT_WFxT",
   "Enable Armv8.7-A WFET and WFIT instruction">;
 
-def FeatureHCX : Extension<"hcx", "HCX", "FEAT_HCX",
-  "Enable Armv8.7-A HCRX_EL2 system register">;
-
 def FeatureLS64 : ExtensionWithMArch<"ls64", "LS64",
   "FEAT_LS64, FEAT_LS64_V, FEAT_LS64_ACCDATA",
   "Enable Armv8.7-A LD64B/ST64B Accelerator Extension">;
@@ -1065,7 +1062,7 @@ def HasV8_6aOps : Architecture64<8, 6, "a", "v8.6a",
     FeatureEnhancedCounterVirtualization, FeatureMatMulInt8],
   !listconcat(HasV8_5aOps.DefaultExts, [FeatureBF16, FeatureMatMulInt8])>;
 def HasV8_7aOps : Architecture64<8, 7, "a", "v8.7a",
-  [HasV8_6aOps, FeatureXS, FeatureWFxT, FeatureHCX],
+  [HasV8_6aOps, FeatureXS, FeatureWFxT],
   !listconcat(HasV8_6aOps.DefaultExts, [FeatureWFxT, FeatureSPE_EEF])>;
 def HasV8_8aOps : Architecture64<8, 8, "a", "v8.8a",
   [HasV8_7aOps, FeatureHBC, FeatureMOPS, FeatureNMI],
diff --git a/llvm/lib/Target/AArch64/AArch64Processors.td b/llvm/lib/Target/AArch64/AArch64Processors.td
index f8b344c61ba1e..e71aa4d2779e6 100644
--- a/llvm/lib/Target/AArch64/AArch64Processors.td
+++ b/llvm/lib/Target/AArch64/AArch64Processors.td
@@ -835,7 +835,7 @@ def ProcessorFeatures {
                                    FeatureRAS, FeatureSEL2, FeatureTRACEV8_4,
                                    FeatureAltFPCmp, FeatureFRInt3264,
                                    FeatureMTE, FeatureFineGrainedTraps,
-                                   FeatureHCX, FeatureRCPC3, FeatureETE,
+                                   FeatureRCPC3, FeatureETE,
                                    FeatureSVEBitPerm, FeatureSVE2, FeatureTRBE,
                                    FeatureSME, FeatureSME2];
   list<SubtargetFeature> A65  = [HasV8_2aOps, FeatureSHA2, FeatureAES, FeatureFPARMv8,
@@ -917,7 +917,7 @@ def ProcessorFeatures {
                                   FeatureAM, FeatureRAS, FeatureSEL2,
                                   FeatureTRACEV8_4, FeatureAltFPCmp,
                                   FeatureFRInt3264, FeatureMTE,
-                                  FeatureFineGrainedTraps, FeatureHCX,
+                                  FeatureFineGrainedTraps,
                                   FeatureSPE_EEF, FeatureRCPC3, FeatureETE,
                                   FeatureSVEBitPerm, FeatureSVE2, FeatureTRBE,
                                   FeatureSME, FeatureSME2];
@@ -992,7 +992,7 @@ def ProcessorFeatures {
                                       FeatureRAS, FeatureSEL2, FeatureTRACEV8_4,
                                       FeatureAltFPCmp, FeatureFRInt3264,
                                       FeatureMTE, FeatureFineGrainedTraps,
-                                      FeatureHCX, FeatureSPE_EEF, FeatureRCPC3,
+                                      FeatureSPE_EEF, FeatureRCPC3,
                                       FeatureETE, FeatureSVEBitPerm,
                                       FeatureSVE2, FeatureTRBE, FeatureSME,
                                       FeatureSME2];
@@ -1007,7 +1007,7 @@ def ProcessorFeatures {
                                     FeatureSEL2, FeatureTRACEV8_4,
                                     FeatureAltFPCmp, FeatureFRInt3264,
                                     FeatureMTE, FeatureFineGrainedTraps,
-                                    FeatureHCX, FeatureSPE_EEF, FeatureRCPC3,
+                                    FeatureSPE_EEF, FeatureRCPC3,
                                     FeatureETE, FeatureSVEBitPerm, FeatureSVE2,
                                     FeatureTRBE, FeatureSME, FeatureSME2];
   list<SubtargetFeature> A64FX    = [HasV8_2aOps, FeatureFPARMv8, FeatureNEON,
@@ -1069,7 +1069,6 @@ def ProcessorFeatures {
   list<SubtargetFeature> AppleA16 = [HasV8_6aOps, FeatureSHA2, FeatureAES, FeatureFPARMv8,
                                      FeatureNEON, FeaturePerfMon, FeatureSHA3,
                                      FeatureFullFP16, FeatureFP16FML,
-                                     FeatureHCX,
                                      FeatureComplxNum, FeatureCRC, FeatureJS,
                                      FeatureLSE, FeaturePAuth, FeatureFPAC,
                                      FeatureRAS, FeatureRCPC, FeatureRDM,
@@ -1077,7 +1076,6 @@ def ProcessorFeatures {
   list<SubtargetFeature> AppleA17 = [HasV8_6aOps, FeatureSHA2, FeatureAES, FeatureFPARMv8,
                                      FeatureNEON, FeaturePerfMon, FeatureSHA3,
                                      FeatureFullFP16, FeatureFP16FML,
-                                     FeatureHCX,
                                      FeatureComplxNum, FeatureCRC, FeatureJS,
                                      FeatureLSE, FeaturePAuth, FeatureFPAC,
                                      FeatureRAS, FeatureRCPC, FeatureRDM,
diff --git a/llvm/lib/Target/AArch64/AArch64SystemOperands.td b/llvm/lib/Target/AArch64/AArch64SystemOperands.td
index b394cd1eca38a..bbe6c39a562c4 100644
--- a/llvm/lib/Target/AArch64/AArch64SystemOperands.td
+++ b/llvm/lib/Target/AArch64/AArch64SystemOperands.td
@@ -1257,9 +1257,7 @@ def : RWSysReg<"ACTLR_EL12",         0b11, 0b101, 0b0001, 0b0000, 0b001>;
 def : RWSysReg<"ACTLR_EL2",          0b11, 0b100, 0b0001, 0b0000, 0b001>;
 def : RWSysReg<"ACTLR_EL3",          0b11, 0b110, 0b0001, 0b0000, 0b001>;
 def : RWSysReg<"HCR_EL2",            0b11, 0b100, 0b0001, 0b0001, 0b000>;
-def : RWSysReg<"HCRX_EL2",           0b11, 0b100, 0b0001, 0b0010, 0b010> {
-  let Requires = [{ {AArch64::FeatureHCX} }];
-}
+def : RWSysReg<"HCRX_EL2",           0b11, 0b100, 0b0001, 0b0010, 0b010>;
 def : RWSysReg<"SCR_EL3",            0b11, 0b110, 0b0001, 0b0001, 0b000>;
 def : RWSysReg<"MDCR_EL2",           0b11, 0b100, 0b0001, 0b0001, 0b001>;
 def : RWSysReg<"SDER32_EL3",         0b11, 0b110, 0b0001, 0b0001, 0b001>;
diff --git a/llvm/test/MC/AArch64/armv8.7a-hcx.s b/llvm/test/MC/AArch64/armv8.7a-hcx.s
index d89d6af7b54b8..35086793ace38 100644
--- a/llvm/test/MC/AArch64/armv8.7a-hcx.s
+++ b/llvm/test/MC/AArch64/armv8.7a-hcx.s
@@ -1,12 +1,7 @@
-// RUN: llvm-mc -triple aarch64-none-linux-gnu -show-encoding -mattr=+hcx < %s 2>%t | FileCheck %s
-// RUN: llvm-mc -triple aarch64-none-linux-gnu -show-encoding -mattr=+v8.7a < %s 2>%t | FileCheck %s
-// RUN: not llvm-mc -triple aarch64-none-linux-gnu < %s 2> %t
-// RUN: FileCheck --check-prefix=CHECK-NO-HCX-ERR %s < %t
+// RUN: llvm-mc -triple aarch64-none-linux-gnu -show-encoding < %s | FileCheck %s
 
   mrs x2, HCRX_EL2
 // CHECK: mrs x2, HCRX_EL2              // encoding: [0x42,0x12,0x3c,0xd5]
-// CHECK-NO-HCX-ERR: [[@LINE-2]]:11: error: expected readable system register
 
   msr HCRX_EL2, x3
 // CHECK: msr HCRX_EL2, x3              // encoding: [0x43,0x12,0x1c,0xd5]
-// CHECK-NO-HCX-ERR: [[@LINE-2]]:7: error: expected writable system register
diff --git a/llvm/test/MC/Disassembler/AArch64/armv8.7a-hcx.txt b/llvm/test/MC/Disassembler/AArch64/armv8.7a-hcx.txt
index 9582131e6fdf2..ef68349f2c505 100644
--- a/llvm/test/MC/Disassembler/AArch64/armv8.7a-hcx.txt
+++ b/llvm/test/MC/Disassembler/AArch64/armv8.7a-hcx.txt
@@ -1,11 +1,7 @@
-# RUN: llvm-mc -triple=aarch64 -mattr=+hcx -disassemble %s | FileCheck %s
-# RUN: llvm-mc -triple=aarch64 -mattr=+v8.7a -disassemble %s | FileCheck %s
-# RUN: llvm-mc -triple=aarch64 -disassemble %s | FileCheck --check-prefix=CHECK-NO-HCX %s
+# RUN: llvm-mc -triple=aarch64 -disassemble %s | FileCheck %s
 
 [0x42,0x12,0x3c,0xd5]
 # CHECK: mrs x2, HCRX_EL2
-# CHECK-NO-HCX: mrs x2, S3_4_C1_C2_2
 
 [0x43,0x12,0x1c,0xd5]
 # CHECK: msr HCRX_EL2, x3
-# CHECK-NO-HCX: msr S3_4_C1_C2_2, x3

>From fc48c7e574f87f478ed17cee143afae350f7be88 Mon Sep 17 00:00:00 2001
From: Mel Chen <mel.chen at sifive.com>
Date: Wed, 15 Jul 2026 19:19:38 +0800
Subject: [PATCH 10/35] [VPlan] Fix nowrap flags for strided access pointers
 from SCEV (#209453)

This patch addresses two things. First, the offset calculation
(canonical IV * stride) should not reuse the NSW flag of the add
recurrence. The NSW property from SCEV for the original scalar
recurrence does not necessarily hold for the reconstructed
multiplication using the vector canonical IV. The NUW flag, however, can
still be safely propagated.
Second, because vputils::getGEPFlagsForPtr currently doesn't support
recipes other than VPInstruction, and to avoid relying on LLVM IR
function (like calling stripPointerCasts()), we change
VPVectorPointerRecipe's GEP flags to use the add recurrence's flags to
prevent propagating unprovable GEP flags like inbounds.

(cherry picked from commit 1d4148821bf96bef23ea77952031e3e7bec26d3a)
---
 llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp  | 14 +++++++-------
 .../LoopVectorize/RISCV/strided-accesses-unroll.ll |  4 +---
 .../LoopVectorize/RISCV/strided-accesses.ll        | 10 +++++-----
 3 files changed, 13 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index 4da74c5146c52..f37d61f8464fa 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -7787,16 +7787,16 @@ void VPlanTransforms::convertToStridedAccesses(VPlan &Plan,
       auto *AddRecPtr = cast<SCEVAddRecExpr>(PtrSCEV);
       auto *Offset = Builder.createOverflowingOp(
           Instruction::Mul, {CanIV, StrideInBytes},
-          {AddRecPtr->hasNoUnsignedWrap(), AddRecPtr->hasNoSignedWrap()});
-      auto *BasePtr = Builder.createNoWrapPtrAdd(
-          StartVPV, Offset,
-          AddRecPtr->hasNoUnsignedWrap() ? GEPNoWrapFlags::noUnsignedWrap()
-                                         : GEPNoWrapFlags::none());
+          {AddRecPtr->hasNoUnsignedWrap(), /*HasNSW=*/false});
+      GEPNoWrapFlags NWFlags = AddRecPtr->hasNoUnsignedWrap()
+                                   ? GEPNoWrapFlags::noUnsignedWrap()
+                                   : GEPNoWrapFlags::none();
+      VPValue *BasePtr = Builder.createNoWrapPtrAdd(StartVPV, Offset, NWFlags);
 
       // Create a new vector pointer for strided access.
       VPValue *NewPtr = Builder.createVectorPointer(
-          BasePtr, Type::getInt8Ty(Plan.getContext()), StrideInBytes,
-          Ptr->getGEPNoWrapFlags(), Ptr->getDebugLoc());
+          BasePtr, Type::getInt8Ty(Plan.getContext()), StrideInBytes, NWFlags,
+          Ptr->getDebugLoc());
 
       VPValue *Mask = LoadR->getMask();
       if (!Mask)
diff --git a/llvm/test/Transforms/LoopVectorize/RISCV/strided-accesses-unroll.ll b/llvm/test/Transforms/LoopVectorize/RISCV/strided-accesses-unroll.ll
index c4727847d244f..8a53e298af869 100644
--- a/llvm/test/Transforms/LoopVectorize/RISCV/strided-accesses-unroll.ll
+++ b/llvm/test/Transforms/LoopVectorize/RISCV/strided-accesses-unroll.ll
@@ -1,8 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals none --filter-out-after "^scalar.ph" --version 6
 ; RUN: opt < %s -S -p loop-vectorize -mtriple riscv64 -mattr=+v -force-vector-interleave=2 | FileCheck %s
 
-; FIXME: the second part of the strided load's gep shouldn't have nuw.
-
 define void @negative_stride_nuw(ptr noalias %src, ptr noalias %dst) {
 ; CHECK-LABEL: define void @negative_stride_nuw(
 ; CHECK-SAME: ptr noalias [[SRC:%.*]], ptr noalias [[DST:%.*]]) #[[ATTR0:[0-9]+]] {
@@ -26,7 +24,7 @@ define void @negative_stride_nuw(ptr noalias %src, ptr noalias %dst) {
 ; CHECK-NEXT:    [[TMP8:%.*]] = mul i64 [[INDEX]], -8
 ; CHECK-NEXT:    [[TMP9:%.*]] = getelementptr i8, ptr [[TMP7]], i64 [[TMP8]]
 ; CHECK-NEXT:    [[TMP10:%.*]] = mul i64 [[TMP2]], -8
-; CHECK-NEXT:    [[TMP11:%.*]] = getelementptr nuw i8, ptr [[TMP9]], i64 [[TMP10]]
+; CHECK-NEXT:    [[TMP11:%.*]] = getelementptr i8, ptr [[TMP9]], i64 [[TMP10]]
 ; CHECK-NEXT:    [[TMP12:%.*]] = call <vscale x 4 x i32> @llvm.experimental.vp.strided.load.nxv4i32.p0.i64(ptr align 4 [[TMP9]], i64 -8, <vscale x 4 x i1> splat (i1 true), i32 [[TMP6]])
 ; CHECK-NEXT:    [[TMP13:%.*]] = call <vscale x 4 x i32> @llvm.experimental.vp.strided.load.nxv4i32.p0.i64(ptr align 4 [[TMP11]], i64 -8, <vscale x 4 x i1> splat (i1 true), i32 [[TMP6]])
 ; CHECK-NEXT:    [[TMP14:%.*]] = getelementptr inbounds i32, ptr [[DST]], i64 [[INDEX]]
diff --git a/llvm/test/Transforms/LoopVectorize/RISCV/strided-accesses.ll b/llvm/test/Transforms/LoopVectorize/RISCV/strided-accesses.ll
index 673d26a02e036..1062ba477bdce 100644
--- a/llvm/test/Transforms/LoopVectorize/RISCV/strided-accesses.ll
+++ b/llvm/test/Transforms/LoopVectorize/RISCV/strided-accesses.ll
@@ -1262,13 +1262,13 @@ define void @double_stride_ptr_iv(ptr %p, ptr %p2, i64 %stride) {
 ; STRIDED-UF2-NEXT:    br i1 [[CMP_N]], label %[[EXIT:.*]], label %[[SCALAR_PH]]
 ; STRIDED-UF2:       [[SCALAR_PH]]:
 ; STRIDED-UF2-NEXT:    [[BC_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], %[[MIDDLE_BLOCK]] ], [ 0, %[[ENTRY]] ], [ 0, %[[VECTOR_MEMCHECK]] ]
-; STRIDED-UF2-NEXT:    [[BC_RESUME_VAL16:%.*]] = phi ptr [ [[TMP11]], %[[MIDDLE_BLOCK]] ], [ [[P]], %[[ENTRY]] ], [ [[P]], %[[VECTOR_MEMCHECK]] ]
-; STRIDED-UF2-NEXT:    [[BC_RESUME_VAL17:%.*]] = phi ptr [ [[TMP12]], %[[MIDDLE_BLOCK]] ], [ [[P2]], %[[ENTRY]] ], [ [[P2]], %[[VECTOR_MEMCHECK]] ]
+; STRIDED-UF2-NEXT:    [[BC_RESUME_VAL15:%.*]] = phi ptr [ [[TMP11]], %[[MIDDLE_BLOCK]] ], [ [[P]], %[[ENTRY]] ], [ [[P]], %[[VECTOR_MEMCHECK]] ]
+; STRIDED-UF2-NEXT:    [[BC_RESUME_VAL16:%.*]] = phi ptr [ [[TMP12]], %[[MIDDLE_BLOCK]] ], [ [[P2]], %[[ENTRY]] ], [ [[P2]], %[[VECTOR_MEMCHECK]] ]
 ; STRIDED-UF2-NEXT:    br label %[[LOOP:.*]]
 ; STRIDED-UF2:       [[LOOP]]:
 ; STRIDED-UF2-NEXT:    [[I:%.*]] = phi i64 [ [[BC_RESUME_VAL]], %[[SCALAR_PH]] ], [ [[NEXTI:%.*]], %[[LOOP]] ]
-; STRIDED-UF2-NEXT:    [[PTR:%.*]] = phi ptr [ [[BC_RESUME_VAL16]], %[[SCALAR_PH]] ], [ [[PTR_NEXT:%.*]], %[[LOOP]] ]
-; STRIDED-UF2-NEXT:    [[PTR2:%.*]] = phi ptr [ [[BC_RESUME_VAL17]], %[[SCALAR_PH]] ], [ [[PTR2_NEXT:%.*]], %[[LOOP]] ]
+; STRIDED-UF2-NEXT:    [[PTR:%.*]] = phi ptr [ [[BC_RESUME_VAL15]], %[[SCALAR_PH]] ], [ [[PTR_NEXT:%.*]], %[[LOOP]] ]
+; STRIDED-UF2-NEXT:    [[PTR2:%.*]] = phi ptr [ [[BC_RESUME_VAL16]], %[[SCALAR_PH]] ], [ [[PTR2_NEXT:%.*]], %[[LOOP]] ]
 ; STRIDED-UF2-NEXT:    [[X0:%.*]] = load i32, ptr [[PTR]], align 4
 ; STRIDED-UF2-NEXT:    [[Y0:%.*]] = add i32 [[X0]], 1
 ; STRIDED-UF2-NEXT:    store i32 [[Y0]], ptr [[PTR2]], align 4
@@ -1356,7 +1356,7 @@ define void @constant_stride_reinterpret(ptr noalias %in, ptr noalias %out) {
 ; CHECK-UF2-NEXT:    [[TMP5:%.*]] = shl nuw i64 [[INDEX]], 2
 ; CHECK-UF2-NEXT:    [[TMP6:%.*]] = getelementptr nuw i8, ptr [[IN]], i64 [[TMP5]]
 ; CHECK-UF2-NEXT:    [[TMP7:%.*]] = mul i64 [[TMP3]], 4
-; CHECK-UF2-NEXT:    [[TMP13:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP6]], i64 [[TMP7]]
+; CHECK-UF2-NEXT:    [[TMP13:%.*]] = getelementptr nuw i8, ptr [[TMP6]], i64 [[TMP7]]
 ; CHECK-UF2-NEXT:    [[WIDE_MASKED_GATHER:%.*]] = call <vscale x 2 x i64> @llvm.experimental.vp.strided.load.nxv2i64.p0.i64(ptr align 8 [[TMP6]], i64 4, <vscale x 2 x i1> splat (i1 true), i32 [[TMP12]])
 ; CHECK-UF2-NEXT:    [[WIDE_MASKED_GATHER1:%.*]] = call <vscale x 2 x i64> @llvm.experimental.vp.strided.load.nxv2i64.p0.i64(ptr align 8 [[TMP13]], i64 4, <vscale x 2 x i1> splat (i1 true), i32 [[TMP12]])
 ; CHECK-UF2-NEXT:    [[TMP8:%.*]] = getelementptr inbounds nuw i64, ptr [[OUT]], i64 [[INDEX]]

>From ce6af707aac85db6e176ad0faa3bfe608e1a68f5 Mon Sep 17 00:00:00 2001
From: Erich Keane <ekeane at nvidia.com>
Date: Wed, 15 Jul 2026 09:25:25 -0700
Subject: [PATCH 11/35] Create a Template member to be the MemberSpec of a
 failed TemplVarDecl (#209604)

Patch #200092 changed to no longer check the previous var template when
setting whether the current one is a member specialization. However, if
the previous one was actually an error case (see the example here and in
the report), we ended up trying to do that anyway, which caused an
assertion.

This patch puts in a 'fake' declaration for the not-found declaration after we
emit the 'not found' error for the purposes of allowing our diagnostics to
continue evaluating these without causing problems.

Fixes: #209432
(cherry picked from commit 2c2e43675910603bab1b163655786e4850569d74)
---
 clang/lib/Sema/SemaDecl.cpp                   | 15 +++++++++++++
 .../test/SemaTemplate/class-template-spec.cpp | 21 +++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7e1b23c971a9c..c5920f03ed6e1 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8374,6 +8374,21 @@ NamedDecl *Sema::ActOnVariableDeclarator(
         << Name << computeDeclContext(D.getCXXScopeSpec(), true)
         << D.getCXXScopeSpec().getRange();
       NewVD->setInvalidDecl();
+
+      // if this is a member specialization, we don't have any primary template
+      // to be instantiated from. We set ourselves to a 'fake' clone of this so
+      // that anything that attempts to refer to this invalid declaration can
+      // act as if there IS a primary instantiation.
+      if (NewTemplate && IsMemberSpecialization) {
+        VarDecl *FakeVD =
+            VarDecl::Create(Context, DC, D.getBeginLoc(), D.getIdentifierLoc(),
+                            II, R, TInfo, SC);
+        FakeVD->setInvalidDecl();
+        VarTemplateDecl *FakeInstantiatedFrom = VarTemplateDecl::Create(
+            Context, DC, D.getIdentifierLoc(), Name, TemplateParams, FakeVD);
+        FakeInstantiatedFrom->setInvalidDecl();
+        NewTemplate->setInstantiatedFromMemberTemplate(FakeInstantiatedFrom);
+      }
     }
 
     if (!IsPlaceholderVariable)
diff --git a/clang/test/SemaTemplate/class-template-spec.cpp b/clang/test/SemaTemplate/class-template-spec.cpp
index e2486f912e2c4..e60763feb2e1f 100644
--- a/clang/test/SemaTemplate/class-template-spec.cpp
+++ b/clang/test/SemaTemplate/class-template-spec.cpp
@@ -1,6 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -triple x86_64-linux-gnu -verify=expected,cxx14 -std=c++14 %s
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++26 %s
 
 template<typename T, typename U = int> struct A; // expected-note {{template is declared here}} \
@@ -251,4 +252,24 @@ namespace VarTemplateMismatch {
   template<> template<int> const int A<short>::x = 0;
   // expected-error at -1 {{template non-type parameter has a different type 'int' in template redeclaration}}
 } // namespace VarTemplateMismatch
+
+namespace VarTemplateNoMember {
+  template<typename T> struct S {};
+  // expected-error at +1{{no member named 'foo' in 'VarTemplateNoMember::S<long>'}}
+  template<> template<typename U> constexpr int S<long>::foo;
+  // In C++14, these are definitions, not declarations, so they get a
+  // redefinition error.
+  // cxx14-error at +2{{redefinition of 'foo'}}
+  // cxx14-note at -4{{previous definition is here}}
+  template<> template<typename U> constexpr int S<long>::foo;
+  // cxx14-error at +2{{redefinition of 'foo'}}
+  // cxx14-note at -2{{previous definition is here}}
+  template<> template<typename U> constexpr int S<long>::foo;
+  // cxx14-error at +2{{redefinition of 'foo'}}
+  // cxx14-note at -2{{previous definition is here}}
+  template<> template<typename U> constexpr int S<long>::foo;
+  // cxx14-error at +2{{redefinition of 'foo'}}
+  // cxx14-note at -2{{previous definition is here}}
+  template<> template<typename U> constexpr int S<long>::foo;
+} // namespace VarTemplateNoMember
 #endif

>From f54355e08d91e1f4c87337505936e9299a9ffb99 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E5=AD=90=E6=98=82?= <2802328816 at qq.com>
Date: Thu, 16 Jul 2026 00:13:11 +0800
Subject: [PATCH 12/35] [LoopUnroll] Invalidate SCEV after full unrolling
 (#208874)

Full unrolling may leave stale ScalarEvolution loop and disposition
caches around the loop pass boundary. A later IndVars pass can then use
stale exit count information and incorrectly fold reachable exits.

Invalidate SCEV loop and disposition caches after successful full
unrolling before preserving `ScalarEvolutionAnalysis.`

Fixes #207744.

(cherry picked from commit 42c2980c7c632d7a45776d3e2c51627100f6e763)
---
 llvm/lib/Transforms/Utils/LoopUnroll.cpp      |  4 ++
 .../full-unroll-scev-invalidation.ll          | 55 +++++++++++++++++++
 2 files changed, 59 insertions(+)
 create mode 100644 llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll

diff --git a/llvm/lib/Transforms/Utils/LoopUnroll.cpp b/llvm/lib/Transforms/Utils/LoopUnroll.cpp
index d00de52e1cb0b..1f9f340a9cb31 100644
--- a/llvm/lib/Transforms/Utils/LoopUnroll.cpp
+++ b/llvm/lib/Transforms/Utils/LoopUnroll.cpp
@@ -1284,6 +1284,10 @@ llvm::UnrollLoop(Loop *L, UnrollLoopOptions ULO, LoopInfo *LI,
   // Loop over the PHI nodes in the original block, setting incoming values.
   for (PHINode *PN : OrigPHINode) {
     if (CompletelyUnroll) {
+      // The RAUW below disconnects the original PHI from its users.
+      // Invalidate cached SCEVs while the def-use chain is still intact.
+      if (SE)
+        SE->forgetValue(PN);
       PN->replaceAllUsesWith(PN->getIncomingValueForBlock(Preheader));
       PN->eraseFromParent();
     } else if (ULO.Count > 1) {
diff --git a/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll b/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
new file mode 100644
index 0000000000000..a9f7a1df0328a
--- /dev/null
+++ b/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
@@ -0,0 +1,55 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes='loop(indvars,loop-unroll-full)' %s | FileCheck %s
+
+ at __chk = external global i64
+
+define void @f4(i64 %0) {
+; CHECK-LABEL: define void @f4(
+; CHECK-SAME: i64 [[TMP0:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    br label %[[LBL_B5:.*]]
+; CHECK:       [[LBL_B5]]:
+; CHECK-NEXT:    br label %[[LBL_B10:.*]]
+; CHECK:       [[LBL_B10]]:
+; CHECK-NEXT:    [[CONV1:%.*]] = trunc i64 [[TMP0]] to i32
+; CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.ctpop.i32(i32 [[CONV1]])
+; CHECK-NEXT:    [[TOBOOL4_NOT:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL4_NOT]], label %[[COMMON_RET1:.*]], label %[[IF_THEN8:.*]]
+; CHECK:       [[IF_THEN8]]:
+; CHECK-NEXT:    store i64 5, ptr @__chk, align 4
+; CHECK-NEXT:    ret void
+; CHECK:       [[COMMON_RET1]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %lbl_b5
+
+lbl_b5:
+  br label %lbl_b10
+
+lbl_b10:
+  %1 = phi i8 [ 0, %lbl_b5 ], [ 1, %lbl_b10 ]
+  %2 = phi i64 [ %0, %lbl_b5 ], [ 0, %lbl_b10 ]
+  %bb13.1 = phi i32 [ 0, %lbl_b5 ], [ %3, %lbl_b10 ]
+  %conv1 = trunc i64 %2 to i32
+  %3 = call i32 @llvm.ctpop.i32(i32 %conv1)
+  %loadedv = trunc i8 %1 to i1
+  br i1 %loadedv, label %if.then3, label %lbl_b10
+
+if.then3:
+  %tobool4.not = icmp eq i32 %bb13.1, 0
+  br i1 %tobool4.not, label %common.ret, label %if.then8
+
+if.then8:
+  br i1 true, label %if.then10, label %lbl_b5
+
+common.ret:
+  ret void
+
+if.then10:
+  %.lcssa.lcssa34 = phi i32 [ %3, %if.then8 ]
+  store i64 5, ptr @__chk
+  ret void
+}
+
+declare i32 @llvm.ctpop.i32(i32)

>From a2520c9f136c302b05b501c05222e0f605897b32 Mon Sep 17 00:00:00 2001
From: Karthika Devi C <kartc at qti.qualcomm.com>
Date: Wed, 15 Jul 2026 15:51:38 +0530
Subject: [PATCH 13/35] [Polly] Skip vectorize.enable for FP loops with dist=1
 dependences (#205756)

When -polly-annotate-metadata-vectorize is active, Polly marks its
generated loops with llvm.loop.vectorize.enable=true. This is harmful
for loops with a loop-carried dependence of distance 1 that involve
floating-point operations: the Loop Vectorizer reorders FP operations
(e.g. scalar reduction like q = factor*q), producing results that differ
from the sequential scalar reference and causing correctness failures.

Two changes are made:

1. IslAst.cpp: add PollyVectorizeMetadata to the PerformParallelTest
gate so that dependence-distance computation is performed whenever
-polly-annotate-metadata-vectorize is passed, not only when
-polly-parallel or a vectorizer is active.

2. IslNodeBuilder.cpp / LoopGenerators.cpp: when a loop has a dist=1
dependence involving FP operations, omit the vectorize.enable annotation
entirely. This lets the Loop Vectorizer apply its own cost model and
legality checks decide.

(cherry picked from commit df1838f5d961f9587ca4f90d992bef81cc6155db)
---
 polly/include/polly/CodeGen/LoopGenerators.h  |  6 +-
 polly/lib/CodeGen/IslAst.cpp                  |  5 +-
 polly/lib/CodeGen/IslNodeBuilder.cpp          | 47 +++++++++++++++-
 polly/lib/CodeGen/LoopGenerators.cpp          | 12 ++--
 .../CodeGen/Metadata/basic_vec_annotate.ll    |  4 +-
 .../Metadata/skip_vec_annotate_fp_dist1.ll    | 56 +++++++++++++++++++
 6 files changed, 118 insertions(+), 12 deletions(-)
 create mode 100644 polly/test/CodeGen/Metadata/skip_vec_annotate_fp_dist1.ll

diff --git a/polly/include/polly/CodeGen/LoopGenerators.h b/polly/include/polly/CodeGen/LoopGenerators.h
index 6076e5951fb0a..39a5bcfe44ec5 100644
--- a/polly/include/polly/CodeGen/LoopGenerators.h
+++ b/polly/include/polly/CodeGen/LoopGenerators.h
@@ -70,13 +70,17 @@ extern int PollyChunkSize;
 ///                           assume it.
 /// @param LoopVectDisabled   If the Loop vectorizer should be disabled for this
 ///                           loop.
+/// @param SkipVectorizeEnableMetadata
+///                           If Polly should avoid setting
+///                           llvm.loop.vectorize.enable=true for this loop.
 ///
 /// @return Value*    The newly created induction variable for this loop.
 Value *createLoop(Value *LowerBound, Value *UpperBound, Value *Stride,
                   PollyIRBuilder &Builder, LoopInfo &LI, DominatorTree &DT,
                   BasicBlock *&ExitBlock, ICmpInst::Predicate Predicate,
                   ScopAnnotator *Annotator = nullptr, bool Parallel = false,
-                  bool UseGuard = true, bool LoopVectDisabled = false);
+                  bool UseGuard = true, bool LoopVectDisabled = false,
+                  bool SkipVectorizeEnableMetadata = false);
 
 /// Create a DebugLoc representing generated instructions.
 ///
diff --git a/polly/lib/CodeGen/IslAst.cpp b/polly/lib/CodeGen/IslAst.cpp
index 0ea14ae2fc2e0..20b6440ebffb5 100644
--- a/polly/lib/CodeGen/IslAst.cpp
+++ b/polly/lib/CodeGen/IslAst.cpp
@@ -81,6 +81,8 @@ static cl::opt<bool> DetectParallel("polly-ast-detect-parallel",
                                     cl::desc("Detect parallelism"), cl::Hidden,
                                     cl::cat(PollyCategory));
 
+extern cl::opt<bool> PollyVectorizeMetadata;
+
 static cl::opt<bool>
     PollyPrintAst("polly-print-ast",
                   cl::desc("Print the ISL abstract syntax tree"),
@@ -501,7 +503,8 @@ IslAst::IslAst(IslAst &&O)
 
 void IslAst::init(const Dependences &D) {
   bool PerformParallelTest = PollyParallel || DetectParallel ||
-                             PollyVectorizerChoice != VECTORIZER_NONE;
+                             PollyVectorizerChoice != VECTORIZER_NONE ||
+                             PollyVectorizeMetadata;
   auto ScheduleTree = S.getScheduleTree();
 
   // Skip AST and code generation if there was no benefit achieved.
diff --git a/polly/lib/CodeGen/IslNodeBuilder.cpp b/polly/lib/CodeGen/IslNodeBuilder.cpp
index 924f6533b0a81..7e395fd4cbd31 100644
--- a/polly/lib/CodeGen/IslNodeBuilder.cpp
+++ b/polly/lib/CodeGen/IslNodeBuilder.cpp
@@ -28,7 +28,6 @@
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/PostOrderIterator.h"
 #include "llvm/ADT/SetVector.h"
-#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/AssumptionCache.h"
 #include "llvm/Analysis/LoopInfo.h"
@@ -75,6 +74,9 @@
 using namespace llvm;
 using namespace polly;
 
+// Declared in LoopGenerators.cpp
+extern llvm::cl::opt<bool> PollyVectorizeMetadata;
+
 #define DEBUG_TYPE "polly-codegen"
 
 STATISTIC(VersionedScops, "Number of SCoPs that required versioning.");
@@ -434,6 +436,34 @@ static bool IsLoopVectorizerDisabled(isl::ast_node_for Node) {
   return false;
 }
 
+/// Returns true if the loop has a dist=1 dependence involving FP operations
+/// (array-carried RAW/WAW or scalar FP reduction). In that case we omit the
+/// vectorize.enable annotation and let the Loop Vectorizer decide.
+static bool hasLoopCarriedDependence(isl::ast_node_for For, const Scop &S) {
+  isl::pw_aff PwaDist = IslAstInfo::getMinimalDependenceDistance(For);
+  if (PwaDist.is_null())
+    return false;
+
+  isl::set Dist = isl::manage(isl_pw_aff_domain(PwaDist.copy()));
+  isl::pw_aff PwaOne = isl::pw_aff(Dist, isl::val::one(S.getIslCtx()));
+  if (isl_pw_aff_is_equal(PwaDist.get(), PwaOne.get()) != isl_bool_true)
+    return false;
+
+  // dist=1: suppress forced vectorization if the body has FP operations.
+  for (isl::set StmtSet :
+       IslAstInfo::getSchedule(For).domain().get_set_list()) {
+    auto *Stmt = static_cast<ScopStmt *>(StmtSet.get_tuple_id().get_user());
+    for (Instruction *Inst : Stmt->getInstructions()) {
+      if (Inst->getType()->isFloatingPointTy() ||
+          (Inst->getNumOperands() > 0 &&
+           Inst->getOperand(0)->getType()->isFloatingPointTy()))
+        return true;
+    }
+  }
+
+  return false;
+}
+
 void IslNodeBuilder::createForSequential(isl::ast_node_for For,
                                          bool MarkParallel) {
   Value *ValueLB, *ValueUB, *ValueInc;
@@ -478,9 +508,22 @@ void IslNodeBuilder::createForSequential(isl::ast_node_for For,
   // omit the GuardBB in front of the loop.
   bool UseGuardBB = !GenSE->isKnownPredicate(Predicate, GenSE->getSCEV(ValueLB),
                                              GenSE->getSCEV(ValueUB));
+
+  // FIXME: This is a workaround for
+  // https://github.com/llvm/llvm-project/issues/198726.
+  // llvm.loop.vectorize.enable=true has an additional property beyond
+  // requesting vectorization — it implicitly allows FP operation reordering.
+  // This is a limitation of the metadata format: there is no way to separate
+  // the request for vectorization from the request for reassociating FP ops.
+  // Once LoopVectorize is fixed to not reorder FP ops without explicit
+  // permission, this workaround can be removed.
+  // For now, skip vectorize.enable for dist=1 FP loops to avoid correctness
+  // failures from FP reassociation.
+  bool SkipVectorizeEnableMetadata = hasLoopCarriedDependence(For, S);
+
   IV = createLoop(ValueLB, ValueUB, ValueInc, Builder, *GenLI, *GenDT,
                   ExitBlock, Predicate, &Annotator, MarkParallel, UseGuardBB,
-                  LoopVectorizerDisabled);
+                  LoopVectorizerDisabled, SkipVectorizeEnableMetadata);
   IDToValue[IteratorID.get()] = IV;
 
   create(Body.release());
diff --git a/polly/lib/CodeGen/LoopGenerators.cpp b/polly/lib/CodeGen/LoopGenerators.cpp
index 57e0cb6377b92..df6fd9df1294d 100644
--- a/polly/lib/CodeGen/LoopGenerators.cpp
+++ b/polly/lib/CodeGen/LoopGenerators.cpp
@@ -90,7 +90,8 @@ Value *polly::createLoop(Value *LB, Value *UB, Value *Stride,
                          DominatorTree &DT, BasicBlock *&ExitBB,
                          ICmpInst::Predicate Predicate,
                          ScopAnnotator *Annotator, bool Parallel, bool UseGuard,
-                         bool LoopVectDisabled) {
+                         bool LoopVectDisabled,
+                         bool SkipVectorizeEnableMetadata) {
   Function *F = Builder.GetInsertBlock()->getParent();
   LLVMContext &Context = F->getContext();
 
@@ -165,15 +166,14 @@ Value *polly::createLoop(Value *LB, Value *UB, Value *Stride,
   // Create the loop latch and annotate it as such.
   CondBrInst *B = Builder.CreateCondBr(LoopCondition, HeaderBB, ExitBB);
 
-  // Don't annotate vectorize metadata when both LoopVectDisabled and
-  // PollyVectorizeMetadata are disabled. Annotate vectorize metadata to false
-  // when LoopVectDisabled is true. Otherwise we annotate the vectorize metadata
-  // to true.
+  // Emit vectorize.enable=false only for explicit user disable
+  // (LoopVectDisabled). For dist=1 FP loops (SkipVectorizeEnableMetadata), omit
+  // the annotation and let the Loop Vectorizer decide.
   if (Annotator) {
     std::optional<bool> EnableVectorizeMetadata;
     if (LoopVectDisabled)
       EnableVectorizeMetadata = false;
-    else if (PollyVectorizeMetadata)
+    else if (PollyVectorizeMetadata && !SkipVectorizeEnableMetadata)
       EnableVectorizeMetadata = true;
     Annotator->annotateLoopLatch(B, Parallel, EnableVectorizeMetadata);
   }
diff --git a/polly/test/CodeGen/Metadata/basic_vec_annotate.ll b/polly/test/CodeGen/Metadata/basic_vec_annotate.ll
index 344a6d0990837..e059fea707321 100644
--- a/polly/test/CodeGen/Metadata/basic_vec_annotate.ll
+++ b/polly/test/CodeGen/Metadata/basic_vec_annotate.ll
@@ -14,8 +14,8 @@
 ; CHECK: br {{.*}} !llvm.loop [[POLLY_LOOP:![0-9]+]]
 ; CHECK: [[LOOP]] = distinct !{[[LOOP]], [[META2:![0-9]+]], [[META3:![0-9]+]]}
 ; CHECK: [[META3]] = !{!"llvm.loop.vectorize.enable", i32 0}
-; CHECK: [[POLLY_LOOP]] = distinct !{[[POLLY_LOOP]], [[META2:![0-9]+]], [[META3:![0-9]+]]}
-; CHECK: [[META3]] = !{!"llvm.loop.vectorize.enable", i1 true}
+; CHECK: [[POLLY_LOOP]] = distinct !{[[POLLY_LOOP]], {{.*}}}
+; CHECK-DAG: !{!"llvm.loop.vectorize.enable", i1 true}
 
 target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
 target triple = "aarch64-unknown-linux-gnu"
diff --git a/polly/test/CodeGen/Metadata/skip_vec_annotate_fp_dist1.ll b/polly/test/CodeGen/Metadata/skip_vec_annotate_fp_dist1.ll
new file mode 100644
index 0000000000000..6ed41b1f64cbd
--- /dev/null
+++ b/polly/test/CodeGen/Metadata/skip_vec_annotate_fp_dist1.ll
@@ -0,0 +1,56 @@
+; RUN: opt %loadNPMPolly -S '-passes=polly<no-default-opts>' -polly-annotate-metadata-vectorize < %s | FileCheck %s
+
+; Verify that vectorize.enable metadata is NOT added for a loop with a dist=1
+; dependence involving floating-point operations. This is a workaround for
+; https://github.com/llvm/llvm-project/issues/198726 where vectorize.enable
+; implicitly allows FP reassociation causing correctness failures.
+
+; void scale(double *A, double factor, int n) {
+;   for (int i = 1; i < n; i++)
+;     A[i] = factor * A[i - 1];
+; }
+
+; The Polly-generated loop should NOT have vectorize.enable metadata.
+; CHECK: polly.stmt.for.body:
+; CHECK: br {{.*}} !llvm.loop [[POLLY_LOOP:![0-9]+]]
+; CHECK: [[POLLY_LOOP]] = distinct !{[[POLLY_LOOP]],
+; CHECK-NOT: !"llvm.loop.vectorize.enable", i1 true
+
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "aarch64-unknown-linux-gnu"
+
+define dso_local void @scale(ptr nocapture noundef %A, double noundef %factor, i32 noundef %n) local_unnamed_addr #0 {
+entry:
+  br label %entry.split
+
+entry.split:                                      ; preds = %entry
+  %cmp5 = icmp sgt i32 %n, 1
+  br i1 %cmp5, label %for.body.preheader, label %for.cond.cleanup
+
+for.body.preheader:                               ; preds = %entry.split
+  %wide.trip.count = zext nneg i32 %n to i64
+  br label %for.body
+
+for.cond.cleanup.loopexit:                        ; preds = %for.body
+  br label %for.cond.cleanup
+
+for.cond.cleanup:                                 ; preds = %for.cond.cleanup.loopexit, %entry.split
+  ret void
+
+for.body:                                         ; preds = %for.body.preheader, %for.body
+  %indvars.iv = phi i64 [ 1, %for.body.preheader ], [ %indvars.iv.next, %for.body ]
+  %0 = add nsw i64 %indvars.iv, -1
+  %arrayidx = getelementptr inbounds double, ptr %A, i64 %0
+  %1 = load double, ptr %arrayidx, align 8
+  %mul = fmul double %factor, %1
+  %arrayidx2 = getelementptr inbounds double, ptr %A, i64 %indvars.iv
+  store double %mul, ptr %arrayidx2, align 8
+  %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
+  %exitcond.not = icmp eq i64 %indvars.iv.next, %wide.trip.count
+  br i1 %exitcond.not, label %for.cond.cleanup.loopexit, label %for.body, !llvm.loop !0
+}
+
+attributes #0 = { nofree norecurse nosync nounwind memory(argmem: readwrite) uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a57" "target-features"="+aes,+crc,+fp-armv8,+neon,+outline-atomics,+perfmon,+sha2,+v8a,-fmv" }
+
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.mustprogress"}

>From 23780df286c2e5914014d002206f1bfde7d0f1b9 Mon Sep 17 00:00:00 2001
From: Karthika Devi C <kartc at qti.qualcomm.com>
Date: Wed, 15 Jul 2026 23:36:45 +0530
Subject: [PATCH 14/35] [Polly] Fix codegen assertions to account for
 DefinedBehaviorContext (#209188)

DeLICM may produce new read access relations whose domain is restricted
to the DefinedBehaviorContext (e.g., only valid when a parameter ensures
no UB). The validation in setNewAccessRelation already accounts for
this, but the debug assertions in createNewAccesses and
generateScalarLoads did not, causing false assertion failures during
code generation.

Intersect the checked domains with getBestKnownDefinedBehaviorContext()
to match the contract that DeLICM relies on.

Fixes #205732

(cherry picked from commit ea612545644d1e3f238ac4cd053758f569f9bf3b)
---
 polly/lib/CodeGen/BlockGenerators.cpp |  5 ++++
 polly/lib/CodeGen/IslNodeBuilder.cpp  | 12 ++++++--
 polly/test/CodeGen/issue205732.ll     | 40 +++++++++++++++++++++++++++
 3 files changed, 55 insertions(+), 2 deletions(-)
 create mode 100644 polly/test/CodeGen/issue205732.ll

diff --git a/polly/lib/CodeGen/BlockGenerators.cpp b/polly/lib/CodeGen/BlockGenerators.cpp
index be01f24f562b2..877bb48ad650a 100644
--- a/polly/lib/CodeGen/BlockGenerators.cpp
+++ b/polly/lib/CodeGen/BlockGenerators.cpp
@@ -555,6 +555,11 @@ void BlockGenerator::generateScalarLoads(
 #ifndef NDEBUG
     auto StmtDom =
         Stmt.getDomain().intersect_params(Stmt.getParent()->getContext());
+    // Restrict to defined behavior context to match DeLICM's contract:
+    // new read accesses are only required to cover the defined-behavior
+    // subset of the domain.
+    StmtDom = StmtDom.intersect_params(
+        Stmt.getParent()->getBestKnownDefinedBehaviorContext());
     auto AccDom = MA->getAccessRelation().domain();
     assert(!StmtDom.is_subset(AccDom).is_false() &&
            "Scalar must be loaded in all statement instances");
diff --git a/polly/lib/CodeGen/IslNodeBuilder.cpp b/polly/lib/CodeGen/IslNodeBuilder.cpp
index 7e395fd4cbd31..93822e5c61615 100644
--- a/polly/lib/CodeGen/IslNodeBuilder.cpp
+++ b/polly/lib/CodeGen/IslNodeBuilder.cpp
@@ -846,9 +846,17 @@ IslNodeBuilder::createNewAccesses(ScopStmt *Stmt,
                                      Stmt->getParent()->getContext().release());
       SchedDom = isl_set_intersect_params(
           SchedDom, Stmt->getParent()->getContext().release());
-      assert(isl_set_is_subset(SchedDom, AccDom) &&
+      // Restrict to defined behavior context to match DeLICM's contract:
+      // new read accesses are only required to cover the defined-behavior
+      // subset of the domain.
+      auto *DefinedBehavior =
+          Stmt->getParent()->getBestKnownDefinedBehaviorContext().release();
+      SchedDom =
+          isl_set_intersect_params(SchedDom, isl_set_copy(DefinedBehavior));
+      Dom = isl_set_intersect_params(Dom, DefinedBehavior);
+      assert(isl_set_is_subset(SchedDom, AccDom) != isl_bool_false &&
              "Access relation not defined on full schedule domain");
-      assert(isl_set_is_subset(Dom, AccDom) &&
+      assert(isl_set_is_subset(Dom, AccDom) != isl_bool_false &&
              "Access relation not defined on full domain");
       isl_set_free(AccDom);
       isl_set_free(SchedDom);
diff --git a/polly/test/CodeGen/issue205732.ll b/polly/test/CodeGen/issue205732.ll
new file mode 100644
index 0000000000000..35dc786df1c4b
--- /dev/null
+++ b/polly/test/CodeGen/issue205732.ll
@@ -0,0 +1,40 @@
+; RUN: opt %loadNPMPolly -passes=polly -S %s | FileCheck %s
+;
+; https://github.com/llvm/llvm-project/issues/205732
+; When DeLICM maps a scalar to an array element, the new access relation
+; may only be valid within the DefinedBehaviorContext (i.e., for parameter
+; values where the original program has no undefined behavior). Code
+; generation must not assert failure for such partial-domain read accesses.
+;
+; CHECK: polly.start:
+
+define void @foo(i32 %w, ptr %dst, i64 %n) {
+entry:
+  br label %for.outer
+
+for.outer:
+  %i = phi i64 [ %i.next, %for.mid.exit ], [ 0, %entry ]
+  br label %for.mid
+
+for.mid:
+  br label %for.inner
+
+for.inner:
+  %0 = phi i32 [ 0, %for.mid ], [ 1, %for.inner ]
+  %1 = load i16, ptr null, align 2
+  %cond1 = icmp eq i32 0, %w
+  br i1 %cond1, label %for.inner.exit, label %for.inner
+
+for.inner.exit:
+  br i1 true, label %for.mid.exit, label %for.mid
+
+for.mid.exit:
+  %ptr = getelementptr [4 x i8], ptr %dst, i64 %i
+  store i32 %0, ptr %ptr, align 4
+  %i.next = add i64 %i, 1
+  %cond2 = icmp eq i64 %i, %n
+  br i1 %cond2, label %for.outer.exit, label %for.outer
+
+for.outer.exit:
+  ret void
+}

>From 88c69e55f3da48bcebdb425f54431f80ce457c8d Mon Sep 17 00:00:00 2001
From: Amy Kwan <amy.kwan1 at ibm.com>
Date: Wed, 15 Jul 2026 15:42:46 -0400
Subject: [PATCH 15/35] [Object][GOFF] Recognize RLD and LEN records (#207118)

This patch adds explicit cases for RT_RLD and RT_LEN records when
parsing GOFF objects. These record types are not handled yet, but
recognizing them allows us to diagnose them and avoids teating them as
unexpected records.

(cherry picked from commit c40401dd8d28e88519433531c041b0b7e5ada8a9)
---
 llvm/lib/Object/GOFFObjectFile.cpp | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Object/GOFFObjectFile.cpp b/llvm/lib/Object/GOFFObjectFile.cpp
index 4ee51a61c14fa..057d3fc208c9b 100644
--- a/llvm/lib/Object/GOFFObjectFile.cpp
+++ b/llvm/lib/Object/GOFFObjectFile.cpp
@@ -170,6 +170,12 @@ GOFFObjectFile::GOFFObjectFile(MemoryBufferRef Object, Error &Err)
       TextPtrs.emplace_back(I);
       LLVM_DEBUG(dbgs() << "  --  TXT\n");
       break;
+    case GOFF::RT_RLD:
+      LLVM_DEBUG(dbgs() << "  --  RLD (GOFF record type) unhandled\n");
+      break;
+    case GOFF::RT_LEN:
+      LLVM_DEBUG(dbgs() << "  --  LEN (GOFF record type) unhandled\n");
+      break;
     case GOFF::RT_END:
       LLVM_DEBUG(dbgs() << "  --  END (GOFF record type) unhandled\n");
       break;
@@ -177,7 +183,10 @@ GOFFObjectFile::GOFFObjectFile(MemoryBufferRef Object, Error &Err)
       LLVM_DEBUG(dbgs() << "  --  HDR (GOFF record type) unhandled\n");
       break;
     default:
-      llvm_unreachable("Unknown record type");
+      Err = createStringError(object_error::parse_failed,
+                              "record %zu has unknown record type 0x%02" PRIX8,
+                              RecordNum, RecordType);
+      return;
     }
     PrevRecordType = RecordType;
     PrevContinuationBits = I[1] & 0x03;

>From c7280675cd04a97c958bcf46db8042e33ab35fc9 Mon Sep 17 00:00:00 2001
From: Erich Keane <ekeane at nvidia.com>
Date: Wed, 15 Jul 2026 06:57:31 -0700
Subject: [PATCH 16/35] Fix references to complete types in attribute
 references (#209537)

This is a regression from #197215.

Attributes are not REALLY in the body of a function (though the name of
said function is... awkwardly inaccurate at best), but still need to pay
attention to the completeness of their references. As a result, we
weren't marking the expression as invalid, but were also trying to
evaluate it.

This patch fixes this in 2 ways. First, we re-add the
CXXThisTypeOverride check, but except constant substitution, since that
has some additional 'this' behavior from #197215. x

Secondly, we also make the constant evaluator give up on incomplete
types when handling an L value member. This stops us from trying to
evaluate the value if it is incomplete during template instantiation,
when the type is incomplete. We don't diagnose, since it is still
potentially a constant expression, but isn't currently one.

Fixes: #199527
(cherry picked from commit 0d93f2aec0b4847761bd8458b7847ce37b85cf03)
---
 clang/lib/AST/ExprConstant.cpp   | 13 +++++++++--
 clang/lib/Sema/SemaExprCXX.cpp   |  8 +++++++
 clang/test/SemaCXX/enable_if.cpp | 39 ++++++++++++++++++++++++++++++++
 3 files changed, 58 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 7d9dba8cd33fc..120af89a93b09 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -3212,8 +3212,17 @@ static bool HandleLValueMember(EvalInfo &Info, const Expr *E, LValue &LVal,
                                const FieldDecl *FD,
                                const ASTRecordLayout *RL = nullptr) {
   if (!RL) {
-    if (FD->getParent()->isInvalidDecl()) return false;
-    RL = &Info.Ctx.getASTRecordLayout(FD->getParent());
+    const RecordDecl *RD = FD->getParent();
+    if (RD->isInvalidDecl())
+      return false;
+    // There are some cases where the base is not yet complete but we haven't
+    // disagnosed (such as in a template instantation of an attribute that
+    // references the expression, ala enable_if).  These aren't necessarily
+    // constant expressions so we return 'false', but they might be, so we don't
+    // diagnose.
+    if (!RD->isCompleteDefinition())
+      return false;
+    RL = &Info.Ctx.getASTRecordLayout(RD);
   }
 
   unsigned I = FD->getFieldIndex();
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 5f22cd409dc01..538604aa2e64b 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1487,6 +1487,14 @@ void Sema::MarkThisReferenced(CXXThisExpr *This) {
 }
 
 bool Sema::isThisOutsideMemberFunctionBody(QualType BaseType) {
+  // If we're outside the body of a member function, then we'll have a specified
+  // type for 'this'. Constraint substitution is the exception: a concept is
+  // evaluated in its own declaration context (see GH#197215), so it loses the
+  // enclosing '*this' even though it may legitimately name a member of the
+  // class currently being instantiated.
+  if (CXXThisTypeOverride.isNull() && !inConstraintSubstitution())
+    return false;
+
   // Determine whether we're looking into a class that's currently being
   // defined.
   CXXRecordDecl *Class = BaseType->getAsCXXRecordDecl();
diff --git a/clang/test/SemaCXX/enable_if.cpp b/clang/test/SemaCXX/enable_if.cpp
index a34b87064b49d..4af0922fee5ac 100644
--- a/clang/test/SemaCXX/enable_if.cpp
+++ b/clang/test/SemaCXX/enable_if.cpp
@@ -646,6 +646,45 @@ void Substitute(Arg) __attribute__((enable_if(PlaceholderBitmask, ""))) {
 
 }
 
+namespace GH199527 {
+struct S { // expected-note {{definition of 'GH199527::S' is not complete until the closing '}'}}
+  ~S() {}
+  bool b;
+  // expected-error at +1{{member access into incomplete type 'S'}}
+  void foo(S b) __attribute__((enable_if(b.b, "")));
+};
+
+template<typename T>
+struct S2 {
+  bool b;
+  void foo(S2 b) const __attribute__((enable_if(b.b, "templ_foo_disabled"))); // #FOO
+};
+
+void use() {
+  S2<int> s_whatever;
+  S2<int> s_true{true};
+  S2<int> s_false{false};
+
+
+  // Both fail because this isn't a constexpr.
+  // expected-error at +2{{no matching member function for call to 'foo'}}
+  // expected-note@#FOO{{candidate disabled: templ_foo_disabled}}
+  s_whatever.foo(s_true);
+  // expected-error at +2{{no matching member function for call to 'foo'}}
+  // expected-note@#FOO{{candidate disabled: templ_foo_disabled}}
+  s_whatever.foo(s_false);
+
+  constexpr S2<int> ce_s_whatever{};
+  constexpr S2<int> ce_s_true{true};
+  constexpr S2<int> ce_s_false{false};
+
+  ce_s_whatever.foo(ce_s_true);
+  // expected-error at +2{{no matching member function for call to 'foo'}}
+  // expected-note@#FOO{{candidate disabled: templ_foo_disabled}}
+  ce_s_whatever.foo(ce_s_false);
+}
+}
+
 namespace DefaultArgs {
   void f(int n = __builtin_LINE()) __attribute__((enable_if(n == 12345, "only callable on line 12345"))); // expected-note {{only callable on line 12345}}
   void g() { f(); } // expected-error {{no matching function}}

>From a885d62292d8f02cac5eeeb739305dc58cf23abf Mon Sep 17 00:00:00 2001
From: Tom Stellard <tstellar at redhat.com>
Date: Thu, 16 Jul 2026 09:37:06 -0700
Subject: [PATCH 17/35] workflows/llvm-abi-tests: Fix typo (#209917)

Introduced by 9bd8bbb2b76c55ca83ddb6e0aa8a3a79b65706d4.

(cherry picked from commit 16c6374250a465204e067b518577a2b4fd655f93)
---
 .github/workflows/llvm-abi-tests.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/llvm-abi-tests.yml b/.github/workflows/llvm-abi-tests.yml
index 1ad4b5c868bc4..ae9b7cc40d79a 100644
--- a/.github/workflows/llvm-abi-tests.yml
+++ b/.github/workflows/llvm-abi-tests.yml
@@ -122,7 +122,7 @@ jobs:
           else
             touch llvm.symbols
           fi
-          abi-dumper $EXTRA_ARGS -lver $REF -skip-cxx -public-headers ./install/include/$ABI_HEADERS -o $$REF.abi ./install/lib/libLLVM.so
+          abi-dumper $EXTRA_ARGS -lver $REF -skip-cxx -public-headers ./install/include/$ABI_HEADERS -o $REF.abi ./install/lib/libLLVM.so
           # Remove symbol versioning from dumps, so we can compare across major versions.
           sed -i "s/LLVM_$LLVM_VERSION_MAJOR/LLVM_NOVERSION/" $REF.abi
       - name: Upload ABI file

>From 5aa15abead01fef21fee4706aa01f39e8ac18829 Mon Sep 17 00:00:00 2001
From: Pawan Nirpal <pnirpal at qti.qualcomm.com>
Date: Wed, 15 Jul 2026 01:40:31 +0530
Subject: [PATCH 18/35] [ARM] - Fix Thumb1InstrInfo::copyPhysReg crash on debug
 instructions during liveness walk. (#209478)

clang asserts when compiling with -mthumb -mcpu=arm926ej-s -O0 -g
(pre-v6 Thumb1, debug info enabled). The crash occurs in
LiveRegUnits::stepBackward which unconditionally asserts that it must
never be called on a debug instruction, but Thumb1InstrInfo::copyPhysReg
walks the basic block backward without skipping DBG_VALUE instructions.

Fixes: https://github.com/llvm/llvm-project/issues/209475
(cherry picked from commit 73615e792dc3116d5c66cb8175039dd6d4840f31)
---
 llvm/lib/Target/ARM/Thumb1InstrInfo.cpp       |  8 ++++--
 .../thumb1-copy-physreg-dbg-value-crash.ll    | 25 +++++++++++++++++++
 2 files changed, 31 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/CodeGen/ARM/thumb1-copy-physreg-dbg-value-crash.ll

diff --git a/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp b/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp
index c2345f588b937..ef67e1116af72 100644
--- a/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp
+++ b/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp
@@ -62,10 +62,14 @@ void Thumb1InstrInfo::copyPhysReg(MachineBasicBlock &MBB,
     UsedRegs.addLiveOuts(MBB);
 
     auto InstUpToI = MBB.end();
-    while (InstUpToI != I)
+    while (InstUpToI != I) {
       // The pre-decrement is on purpose here.
       // We want to have the liveness right before I.
-      UsedRegs.stepBackward(*--InstUpToI);
+      --InstUpToI;
+      if (InstUpToI->isDebugInstr())
+        continue;
+      UsedRegs.stepBackward(*InstUpToI);
+    }
 
     if (UsedRegs.available(ARM::CPSR)) {
       BuildMI(MBB, I, DL, get(ARM::tMOVSr), DestReg)
diff --git a/llvm/test/CodeGen/ARM/thumb1-copy-physreg-dbg-value-crash.ll b/llvm/test/CodeGen/ARM/thumb1-copy-physreg-dbg-value-crash.ll
new file mode 100644
index 0000000000000..0ff18f1f56d3c
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/thumb1-copy-physreg-dbg-value-crash.ll
@@ -0,0 +1,25 @@
+; RUN: llc < %s -mtriple=thumbv5e-none-linux-gnueabi --float-abi=soft -verify-machineinstrs -o /dev/null
+; This used to crash in Thumb1InstrInfo::copyPhysReg when computing liveness
+; for a pre-v6 low GPR copy. The backward liveness walk from MBB.end() to the
+; COPY instruction was calling LiveRegUnits::stepBackward on DBG_VALUE
+; instructions, which asserts that debug instructions must not affect liveness
+; calculation.
+
+define void @foo(ptr %res) !dbg !3 {
+entry:
+  #dbg_declare(ptr %res, !4, !DIExpression(), !5)
+  call void @llvm.memcpy.p0.p0.i32(ptr %res, ptr null, i32 1, i1 false)
+  ret void
+}
+
+declare void @llvm.memcpy.p0.p0.i32(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i32, i1 immarg)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, emissionKind: FullDebug)
+!1 = !DIFile(filename: "t.c", directory: "")
+!2 = !{i32 2, !"Debug Info Version", i32 3}
+!3 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, type: !DISubroutineType(types: !{}), unit: !0)
+!4 = !DILocalVariable(scope: !3)
+!5 = !DILocation(line: 1, scope: !3)

>From 66ba3bdaa25418fed2e74cd39576f236498bf289 Mon Sep 17 00:00:00 2001
From: hev <wangrui at loongson.cn>
Date: Fri, 17 Jul 2026 10:23:52 +0800
Subject: [PATCH 19/35] [LoongArch] Fix invalid VEXTH combines for unsupported
 type extensions (#209725)

`performEXTENDCombine` could form `VEXTH`/`VEXTH_U` nodes for
unsupported type combinations, such as extending `v8i8` to `v8i32` or
`v2i64` to `v2i128`. These illegal nodes would later reach instruction
selection and trigger backend failures:

* `Cannot select: LoongArchISD::VEXTH`
* `Don't know how to legalize this operation`

Prevent these combines from firing by verifying that the destination
type is legal and has exactly twice the total bit width of the source
before forming a `VEXTH`/`VEXTH_U` node.

Apply the same checks to `performSHLCombine` for consistency.

Fixes
https://github.com/llvm/llvm-project/pull/207316#issuecomment-4978234865
Fixes
https://github.com/llvm/llvm-project/pull/207316#issuecomment-4979233899

(cherry picked from commit 4a75259134c72d8be7fa3ea36c9dc7bfc02640ac)
---
 .../LoongArch/LoongArchISelLowering.cpp       | 21 ++---
 .../CodeGen/LoongArch/lasx/issue207316.ll     | 18 +++++
 llvm/test/CodeGen/LoongArch/lasx/xvexth.ll    | 80 ++++++++++++++++++-
 llvm/test/CodeGen/LoongArch/lsx/vexth.ll      | 58 +++++++++++++-
 4 files changed, 160 insertions(+), 17 deletions(-)
 create mode 100644 llvm/test/CodeGen/LoongArch/lasx/issue207316.ll

diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 4c5bb28a5e42f..d6189ff6b988d 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -6504,9 +6504,6 @@ static SDValue matchHalfOf128BitLanes(SDValue N, bool isLow) {
 static SDValue performSHLCombine(SDNode *N, SelectionDAG &DAG,
                                  TargetLowering::DAGCombinerInfo &DCI,
                                  const LoongArchSubtarget &Subtarget) {
-  if (!Subtarget.hasExtLSX())
-    return SDValue();
-
   assert(N->getOpcode() == ISD::SHL && "Unexpected opcode");
 
   EVT VT = N->getValueType(0);
@@ -6527,6 +6524,10 @@ static SDValue performSHLCombine(SDNode *N, SelectionDAG &DAG,
   if (!LHS.hasOneUse())
     return SDValue();
 
+  if (!DAG.getTargetLoweringInfo().isTypeLegal(VT) ||
+      N->getValueSizeInBits(0) != LHS->getOperand(0).getValueSizeInBits() * 2)
+    return SDValue();
+
   SDValue Vec = matchHalfOf128BitLanes(LHS.getOperand(0), /*isLow=*/true);
   if (!Vec)
     return SDValue();
@@ -6534,16 +6535,6 @@ static SDValue performSHLCombine(SDNode *N, SelectionDAG &DAG,
   EVT SrcVT = Vec.getValueType();
   EVT SrcEltVT = SrcVT.getVectorElementType();
   EVT DstEltVT = VT.getVectorElementType();
-
-  if (!SrcVT.isVector() || !VT.isVector())
-    return SDValue();
-  if (SrcVT.getSizeInBits() != VT.getSizeInBits())
-    return SDValue();
-  if (DstEltVT.getSizeInBits() != SrcEltVT.getSizeInBits() * 2)
-    return SDValue();
-  if (!SrcEltVT.isInteger() || SrcEltVT.getSizeInBits() > 32)
-    return SDValue();
-
   APInt Imm;
   if (!isConstantSplatVector(RHS, Imm, DstEltVT.getSizeInBits()))
     return SDValue();
@@ -8512,6 +8503,10 @@ static SDValue performEXTENDCombine(SDNode *N, SelectionDAG &DAG,
     if (SDValue R = PromoteMaskArithmetic(SDValue(N, 0), DL, DAG, Subtarget))
       return R;
 
+    if (!DAG.getTargetLoweringInfo().isTypeLegal(VT) ||
+        N->getValueSizeInBits(0) != N->getOperand(0).getValueSizeInBits() * 2)
+      return SDValue();
+
     if (SDValue R = matchHalfOf128BitLanes(N->getOperand(0), /*isLow=*/false)) {
       if (N->getOpcode() == ISD::SIGN_EXTEND)
         return DAG.getNode(LoongArchISD::VEXTH, DL, VT, R);
diff --git a/llvm/test/CodeGen/LoongArch/lasx/issue207316.ll b/llvm/test/CodeGen/LoongArch/lasx/issue207316.ll
new file mode 100644
index 0000000000000..1012918abde30
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/lasx/issue207316.ll
@@ -0,0 +1,18 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc --mtriple=loongarch32 --mattr=+32s,+lasx < %s | FileCheck %s
+; RUN: llc --mtriple=loongarch64 --mattr=+lasx < %s | FileCheck %s
+
+;; https://github.com/llvm/llvm-project/pull/207316#issuecomment-4978234865
+define void @exth_wu_bu() nounwind {
+; CHECK-LABEL: exth_wu_bu:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    xvrepli.b $xr0, 0
+; CHECK-NEXT:    xvst $xr0, $zero, 0
+; CHECK-NEXT:    ret
+entry:
+  %0 = shufflevector <16 x i8> zeroinitializer, <16 x i8> zeroinitializer,
+                     <8 x i32> <i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+  %1 = zext <8 x i8> %0 to <8 x i32>
+  store <8 x i32> %1, ptr null
+  ret void
+}
diff --git a/llvm/test/CodeGen/LoongArch/lasx/xvexth.ll b/llvm/test/CodeGen/LoongArch/lasx/xvexth.ll
index db3a4c9c66368..e8d0cd4e2a297 100644
--- a/llvm/test/CodeGen/LoongArch/lasx/xvexth.ll
+++ b/llvm/test/CodeGen/LoongArch/lasx/xvexth.ll
@@ -1,6 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc --mtriple=loongarch32 --mattr=+32s,+lasx < %s | FileCheck %s
-; RUN: llc --mtriple=loongarch64 --mattr=+lasx < %s | FileCheck %s
+; RUN: llc --mtriple=loongarch32 --mattr=+32s,+lasx < %s | FileCheck %s --check-prefixes=CHECK,LA32
+; RUN: llc --mtriple=loongarch64 --mattr=+lasx < %s | FileCheck %s --check-prefixes=CHECK,LA64
 
 define void @xvexth_h_b(ptr %a, ptr %r) nounwind {
 ; CHECK-LABEL: xvexth_h_b:
@@ -100,6 +100,47 @@ entry:
   ret void
 }
 
+define void @xvexth_q_d(ptr %a, ptr %r) nounwind {
+; LA32-LABEL: xvexth_q_d:
+; LA32:       # %bb.0: # %entry
+; LA32-NEXT:    xvld $xr0, $a0, 0
+; LA32-NEXT:    xvpickve2gr.w $a0, $xr0, 2
+; LA32-NEXT:    xvpickve2gr.w $a2, $xr0, 6
+; LA32-NEXT:    xvpickve2gr.w $a3, $xr0, 7
+; LA32-NEXT:    xvpickve2gr.w $a4, $xr0, 3
+; LA32-NEXT:    srai.w $a5, $a4, 31
+; LA32-NEXT:    srai.w $a6, $a3, 31
+; LA32-NEXT:    st.w $a3, $a1, 20
+; LA32-NEXT:    st.w $a2, $a1, 16
+; LA32-NEXT:    st.w $a4, $a1, 4
+; LA32-NEXT:    st.w $a0, $a1, 0
+; LA32-NEXT:    st.w $a6, $a1, 28
+; LA32-NEXT:    st.w $a6, $a1, 24
+; LA32-NEXT:    st.w $a5, $a1, 12
+; LA32-NEXT:    st.w $a5, $a1, 8
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: xvexth_q_d:
+; LA64:       # %bb.0: # %entry
+; LA64-NEXT:    xvld $xr0, $a0, 0
+; LA64-NEXT:    xvpickve2gr.d $a0, $xr0, 3
+; LA64-NEXT:    xvpickve2gr.d $a2, $xr0, 1
+; LA64-NEXT:    srai.d $a2, $a2, 63
+; LA64-NEXT:    srai.d $a0, $a0, 63
+; LA64-NEXT:    xvstelm.d $xr0, $a1, 16, 3
+; LA64-NEXT:    xvstelm.d $xr0, $a1, 0, 1
+; LA64-NEXT:    st.d $a0, $a1, 24
+; LA64-NEXT:    st.d $a2, $a1, 8
+; LA64-NEXT:    ret
+entry:
+  %0 = load <4 x i64>, ptr %a
+  %1 = shufflevector <4 x i64> %0, <4 x i64> poison,
+                     <2 x i32> <i32 1, i32 3>
+  %2 = sext <2 x i64> %1 to <2 x i128>
+  store <2 x i128> %2, ptr %r
+  ret void
+}
+
 define void @xvexth_hu_bu(ptr %a, ptr %r) nounwind {
 ; CHECK-LABEL: xvexth_hu_bu:
 ; CHECK:       # %bb.0: # %entry
@@ -182,6 +223,41 @@ entry:
   ret void
 }
 
+define void @xvexth_qu_du(ptr %a, ptr %r) nounwind {
+; LA32-LABEL: xvexth_qu_du:
+; LA32:       # %bb.0: # %entry
+; LA32-NEXT:    xvld $xr0, $a0, 0
+; LA32-NEXT:    xvpickve2gr.w $a0, $xr0, 2
+; LA32-NEXT:    xvpickve2gr.w $a2, $xr0, 3
+; LA32-NEXT:    xvpickve2gr.w $a3, $xr0, 6
+; LA32-NEXT:    xvpickve2gr.w $a4, $xr0, 7
+; LA32-NEXT:    st.w $zero, $a1, 28
+; LA32-NEXT:    st.w $zero, $a1, 24
+; LA32-NEXT:    st.w $zero, $a1, 12
+; LA32-NEXT:    st.w $zero, $a1, 8
+; LA32-NEXT:    st.w $a4, $a1, 20
+; LA32-NEXT:    st.w $a3, $a1, 16
+; LA32-NEXT:    st.w $a2, $a1, 4
+; LA32-NEXT:    st.w $a0, $a1, 0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: xvexth_qu_du:
+; LA64:       # %bb.0: # %entry
+; LA64-NEXT:    xvld $xr0, $a0, 0
+; LA64-NEXT:    st.d $zero, $a1, 24
+; LA64-NEXT:    xvstelm.d $xr0, $a1, 16, 3
+; LA64-NEXT:    st.d $zero, $a1, 8
+; LA64-NEXT:    xvstelm.d $xr0, $a1, 0, 1
+; LA64-NEXT:    ret
+entry:
+  %0 = load <4 x i64>, ptr %a
+  %1 = shufflevector <4 x i64> %0, <4 x i64> poison,
+                     <2 x i32> <i32 1, i32 3>
+  %2 = zext <2 x i64> %1 to <2 x i128>
+  store <2 x i128> %2, ptr %r
+  ret void
+}
+
 define void @xvexth_du_wu_poison(ptr %a, ptr %r) nounwind {
 ; CHECK-LABEL: xvexth_du_wu_poison:
 ; CHECK:       # %bb.0: # %entry
diff --git a/llvm/test/CodeGen/LoongArch/lsx/vexth.ll b/llvm/test/CodeGen/LoongArch/lsx/vexth.ll
index d85c4eb7fa1ac..b8e87a0ccd058 100644
--- a/llvm/test/CodeGen/LoongArch/lsx/vexth.ll
+++ b/llvm/test/CodeGen/LoongArch/lsx/vexth.ll
@@ -1,6 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc --mtriple=loongarch32 --mattr=+32s,+lsx < %s | FileCheck %s
-; RUN: llc --mtriple=loongarch64 --mattr=+lsx < %s | FileCheck %s
+; RUN: llc --mtriple=loongarch32 --mattr=+32s,+lsx < %s | FileCheck %s --check-prefixes=CHECK,LA32
+; RUN: llc --mtriple=loongarch64 --mattr=+lsx < %s | FileCheck %s --check-prefixes=CHECK,LA64
 
 define void @vexth_h_b(ptr %a, ptr %r) nounwind {
 ; CHECK-LABEL: vexth_h_b:
@@ -94,6 +94,34 @@ entry:
   ret void
 }
 
+define void @vexth_q_d(ptr %a, ptr %r) nounwind {
+; LA32-LABEL: vexth_q_d:
+; LA32:       # %bb.0: # %entry
+; LA32-NEXT:    vld $vr0, $a0, 0
+; LA32-NEXT:    vpickve2gr.w $a0, $vr0, 2
+; LA32-NEXT:    vpickve2gr.w $a2, $vr0, 3
+; LA32-NEXT:    srai.w $a3, $a2, 31
+; LA32-NEXT:    st.w $a2, $a1, 4
+; LA32-NEXT:    st.w $a0, $a1, 0
+; LA32-NEXT:    st.w $a3, $a1, 12
+; LA32-NEXT:    st.w $a3, $a1, 8
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: vexth_q_d:
+; LA64:       # %bb.0: # %entry
+; LA64-NEXT:    ld.d $a0, $a0, 8
+; LA64-NEXT:    srai.d $a2, $a0, 63
+; LA64-NEXT:    st.d $a0, $a1, 0
+; LA64-NEXT:    st.d $a2, $a1, 8
+; LA64-NEXT:    ret
+entry:
+  %0 = load <2 x i64>, ptr %a
+  %1 = shufflevector <2 x i64> %0, <2 x i64> poison, <1 x i32> <i32 1>
+  %2 = sext <1 x i64> %1 to <1 x i128>
+  store <1 x i128> %2, ptr %r
+  ret void
+}
+
 define void @vexth_hu_bu(ptr %a, ptr %r) nounwind {
 ; CHECK-LABEL: vexth_hu_bu:
 ; CHECK:       # %bb.0: # %entry
@@ -185,3 +213,29 @@ entry:
   store <2 x i64> %2, ptr %r
   ret void
 }
+
+define void @vexth_qu_du(ptr %a, ptr %r) nounwind {
+; LA32-LABEL: vexth_qu_du:
+; LA32:       # %bb.0: # %entry
+; LA32-NEXT:    vld $vr0, $a0, 0
+; LA32-NEXT:    vpickve2gr.w $a0, $vr0, 2
+; LA32-NEXT:    vpickve2gr.w $a2, $vr0, 3
+; LA32-NEXT:    st.w $zero, $a1, 12
+; LA32-NEXT:    st.w $zero, $a1, 8
+; LA32-NEXT:    st.w $a2, $a1, 4
+; LA32-NEXT:    st.w $a0, $a1, 0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: vexth_qu_du:
+; LA64:       # %bb.0: # %entry
+; LA64-NEXT:    ld.d $a0, $a0, 8
+; LA64-NEXT:    st.d $zero, $a1, 8
+; LA64-NEXT:    st.d $a0, $a1, 0
+; LA64-NEXT:    ret
+entry:
+  %0 = load <2 x i64>, ptr %a
+  %1 = shufflevector <2 x i64> %0, <2 x i64> poison, <1 x i32> <i32 1>
+  %2 = zext <1 x i64> %1 to <1 x i128>
+  store <1 x i128> %2, ptr %r
+  ret void
+}

>From 47e2df730a099583f16fc6fe64f0d2ffc5b7a16a Mon Sep 17 00:00:00 2001
From: Daniel Kiss <daniel.kiss at arm.com>
Date: Fri, 17 Jul 2026 06:04:26 +0900
Subject: [PATCH 20/35] [AArch64][COFF] Branch-protection=standard/pac-ret
 means "b-key" on Windows (#203989)

On AArch64 Windows the `B-key` is the only supported key for userspace,
let's make "standard" and "pac-ret" options generate "b-key".

Fixes: #203852
(cherry picked from commit 68f703f3e58a52c41b39dfc654a675560b6c5614)
---
 clang/docs/ReleaseNotes.md                    |  3 +
 clang/docs/UsersManual.md                     | 30 ++++++++
 clang/lib/Basic/Targets/AArch64.cpp           |  3 +-
 clang/lib/Basic/Targets/ARM.cpp               |  2 +-
 clang/lib/Driver/ToolChains/Clang.cpp         |  2 +-
 .../CodeGen/AArch64/branch-protection-attr.c  |  6 ++
 .../CodeGen/AArch64/sign-return-address.c     | 15 ++++
 clang/test/Driver/aarch64-security-options.c  |  4 ++
 .../Preprocessor/aarch64-target-features.c    |  4 ++
 llvm/docs/ReleaseNotes.md                     |  2 +
 .../llvm/TargetParser/ARMTargetParserCommon.h |  4 +-
 .../AArch64/AArch64MachineFunctionInfo.cpp    |  7 ++
 .../lib/Target/AArch64/AArch64PointerAuth.cpp |  4 ++
 .../TargetParser/ARMTargetParserCommon.cpp    |  8 ++-
 .../CodeGen/AArch64/sign-return-address.ll    | 71 +++----------------
 .../AArch64/windows-pac-invalid-a-key.ll      | 11 +++
 llvm/test/CodeGen/AArch64/wineh-bti.ll        |  2 +-
 17 files changed, 112 insertions(+), 66 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/windows-pac-invalid-a-key.ll

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 9063e54b3e692..5216f8a97af66 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1041,6 +1041,9 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
   - Hisilicon hip12 core (hip12).
   - NVIDIA Rigel core (rigel).
 
+- On AArch64 Windows targets, `-mbranch-protection=standard` and `-mbranch-protection=pac-ret`
+  now uses the B-key by default.
+
 #### Android Support
 
 #### Windows Support
diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index 756f2784dc076..9bccf8e175298 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -2774,6 +2774,36 @@ are listed below.
    only. This only applies to the AArch64 architecture.
 ```
 
+```{eval-rst}
+.. option:: -mbranch-protection=features
+
+   Select the branch protection features to use for ARM and AArch64 targets.
+   ``features`` can be ``none``, ``standard``, or a ``+``-separated list of
+   ``bti``, ``gcs``, and ``pac-ret`` with optional ``pac-ret`` modifiers.
+
+   ``none`` is the default and turns off all types of branch protection.
+
+   ``standard`` turns on all branch protection features that are valid for the
+   target platform. If a feature has additional tuning options, ``standard``
+   sets them to their standard level.
+
+   ``pac-ret`` turns on return address signing to its standard level: signing
+   functions that save the return address to memory (non-leaf functions
+   practically always do this) using the A-key. On AArch64 Windows targets,
+   ``pac-ret`` uses the B-key by default.
+
+   ``+leaf`` extends the ``pac-ret`` signing to include leaf functions.
+
+   ``+b-key`` can be used to sign functions with the B-key instead of the A-key
+   on AArch64. ``b-key`` is accepted but ignored for ARM targets.
+
+   ``+pc`` enables the PAuth-LR form of return address signing.
+
+   ``bti`` turns on the Branch Target Identification mechanism.
+
+   ``gcs`` turns on Guarded Control Stack compatible code generation.
+```
+
 ```{eval-rst}
 .. option:: -mcompact-branches=[values]
 
diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp
index debf3ecf589e3..d531e26ade84a 100644
--- a/clang/lib/Basic/Targets/AArch64.cpp
+++ b/clang/lib/Basic/Targets/AArch64.cpp
@@ -254,7 +254,8 @@ bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
                                                  const LangOptions &LO,
                                                  StringRef &Err) const {
   llvm::ARM::ParsedBranchProtection PBP;
-  if (!llvm::ARM::parseBranchProtection(Spec, PBP, Err, HasPAuthLR))
+  if (!llvm::ARM::parseBranchProtection(Spec, PBP, Err, getTriple(),
+                                        HasPAuthLR))
     return false;
 
   // GCS is currently untested with ptrauth-returns, but enabling this could be
diff --git a/clang/lib/Basic/Targets/ARM.cpp b/clang/lib/Basic/Targets/ARM.cpp
index b9744ff9009ca..f963ce38352c2 100644
--- a/clang/lib/Basic/Targets/ARM.cpp
+++ b/clang/lib/Basic/Targets/ARM.cpp
@@ -375,7 +375,7 @@ bool ARMTargetInfo::validateBranchProtection(StringRef Spec, StringRef Arch,
                                              const LangOptions &LO,
                                              StringRef &Err) const {
   llvm::ARM::ParsedBranchProtection PBP;
-  if (!llvm::ARM::parseBranchProtection(Spec, PBP, Err))
+  if (!llvm::ARM::parseBranchProtection(Spec, PBP, Err, getTriple()))
     return false;
 
   if (!isBranchProtectionSupportedArch(Arch))
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 1d6877ffbd8a7..db7893c547ee7 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -1445,7 +1445,7 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
       if (llvm::any_of(CmdArgs, isPAuthLR))
         EnablePAuthLR = true;
     }
-    if (!llvm::ARM::parseBranchProtection(A->getValue(), PBP, DiagMsg,
+    if (!llvm::ARM::parseBranchProtection(A->getValue(), PBP, DiagMsg, Triple,
                                           EnablePAuthLR))
       D.Diag(diag::err_drv_unsupported_option_argument)
           << A->getSpelling() << DiagMsg;
diff --git a/clang/test/CodeGen/AArch64/branch-protection-attr.c b/clang/test/CodeGen/AArch64/branch-protection-attr.c
index c66bce1bee6d3..d3fa2743ef626 100644
--- a/clang/test/CodeGen/AArch64/branch-protection-attr.c
+++ b/clang/test/CodeGen/AArch64/branch-protection-attr.c
@@ -13,6 +13,8 @@
 // RUN:                               | FileCheck %s --check-prefix=CHECK
 // RUN: %clang_cc1 -triple aarch64 -emit-llvm  -target-cpu generic -target-feature +v8.5a -mguarded-control-stack -mbranch-target-enforce -mbranch-protection-pauth-lr -msign-return-address=all -msign-return-address-key=a_key %s -o - \
 // RUN:                               | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -triple aarch64-windows-msvc -emit-llvm -target-cpu generic -target-feature +v8.5a %s -o - \
+// RUN:                               | FileCheck %s --check-prefix=WIN
 
 __attribute__ ((target("branch-protection=none")))
 void none() {}
@@ -21,6 +23,7 @@ void none() {}
   __attribute__ ((target("branch-protection=standard")))
 void std() {}
 // CHECK: define{{.*}} void @std() #[[#STD:]]
+// WIN: define{{.*}} void @std() #[[#WINSTD:]]
 
 __attribute__ ((target("branch-protection=bti")))
 void btionly() {}
@@ -29,6 +32,7 @@ void btionly() {}
 __attribute__ ((target("branch-protection=pac-ret")))
 void paconly() {}
 // CHECK: define{{.*}} void @paconly() #[[#PAC:]]
+// WIN: define{{.*}} void @paconly() #[[#WINPAC:]]
 
 __attribute__ ((target("branch-protection=pac-ret+bti")))
 void pacbti0() {}
@@ -82,10 +86,12 @@ void gcs() {}
 // CHECK-DAG: attributes #[[#NONE]] = { {{.*}}
 
 // CHECK-DAG: attributes #[[#STD]] = { {{.*}} "branch-target-enforcement" "guarded-control-stack" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="a_key"
+// WIN-DAG: attributes #[[#WINSTD]] = { {{.*}} "branch-target-enforcement" "guarded-control-stack" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key"
 
 // CHECK-DAG: attributes #[[#BTI]] = { {{.*}} "branch-target-enforcement"
 
 // CHECK-DAG: attributes #[[#PAC]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="a_key"
+// WIN-DAG: attributes #[[#WINPAC]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key"
 
 // CHECK-DAG: attributes #[[#PACLEAF]] = { {{.*}} "sign-return-address"="all" "sign-return-address-key"="a_key"
 
diff --git a/clang/test/CodeGen/AArch64/sign-return-address.c b/clang/test/CodeGen/AArch64/sign-return-address.c
index 2b505de339054..c617b45c21317 100644
--- a/clang/test/CodeGen/AArch64/sign-return-address.c
+++ b/clang/test/CodeGen/AArch64/sign-return-address.c
@@ -6,6 +6,8 @@
 // RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=pac-ret+leaf  %s | FileCheck %s --check-prefix=CHECK --check-prefix=ALL
 // RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=pac-ret+b-key %s | FileCheck %s --check-prefix=CHECK --check-prefix=B-KEY
 // RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=bti %s           | FileCheck %s --check-prefix=CHECK --check-prefix=BTE
+// RUN: %clang -target aarch64-windows-msvc -S -emit-llvm -o - -mbranch-protection=pac-ret %s | FileCheck %s --check-prefix=CHECK --check-prefix=WIN-PAC
+// RUN: %clang -target aarch64-windows-msvc -S -emit-llvm -o - -mbranch-protection=standard %s | FileCheck %s --check-prefix=CHECK --check-prefix=WIN-STD
 
 // REQUIRES: aarch64-registered-target
 
@@ -21,6 +23,8 @@
 // PART:  attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"="a_key"
 // B-KEY: attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"="b_key"
 // BTE:   attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement"
+// WIN-PAC: attributes #[[#ATTR]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key"
+// WIN-STD: attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement" {{.*}} "guarded-control-stack" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key"
 
 
 // Check module attributes
@@ -30,23 +34,34 @@
 // PART-NOT:  !"branch-target-enforcement"
 // BTE:       !{i32 8, !"branch-target-enforcement", i32 2}
 // B-KEY-NOT: !"branch-target-enforcement"
+// WIN-PAC-NOT: !"branch-target-enforcement"
+// WIN-STD:   !{i32 8, !"branch-target-enforcement", i32 2}
+
+// WIN-PAC-NOT: !"guarded-control-stack"
+// WIN-STD: !{i32 8, !"guarded-control-stack", i32 2}
 
 // NONE-NOT:  !"sign-return-address"
 // ALL:   !{i32 8, !"sign-return-address", i32 2}
 // PART:  !{i32 8, !"sign-return-address", i32 2}
 // BTE-NOT:   !"sign-return-address"
 // B-KEY: !{i32 8, !"sign-return-address", i32 2}
+// WIN-PAC: !{i32 8, !"sign-return-address", i32 2}
+// WIN-STD: !{i32 8, !"sign-return-address", i32 2}
 
 // NONE-NOT:  !"sign-return-address-all"
 // ALL:   !{i32 8, !"sign-return-address-all", i32 2}
 // PART-NOT:  !"sign-return-address-all"
 // BTE-NOT:   !"sign-return-address-all"
 // B-KEY-NOT: !"sign-return-address-all"
+// WIN-PAC-NOT: !"sign-return-address-all"
+// WIN-STD-NOT: !"sign-return-address-all"
 
 // NONE-NOT:  !"sign-return-address-with-bkey"
 // ALL-NOT:   !"sign-return-address-with-bkey"
 // PART-NOT:  !"sign-return-address-with-bkey"
 // BTE-NOT:   !"sign-return-address-with-bkey"
 // B-KEY: !{i32 8, !"sign-return-address-with-bkey", i32 2}
+// WIN-PAC: !{i32 8, !"sign-return-address-with-bkey", i32 2}
+// WIN-STD: !{i32 8, !"sign-return-address-with-bkey", i32 2}
 
 void foo() {}
diff --git a/clang/test/Driver/aarch64-security-options.c b/clang/test/Driver/aarch64-security-options.c
index 146add2d1cf70..967a6122a333d 100644
--- a/clang/test/Driver/aarch64-security-options.c
+++ b/clang/test/Driver/aarch64-security-options.c
@@ -13,6 +13,9 @@
 // RUN: %clang --target=aarch64 -c %s -### -mbranch-protection=standard                                2>&1 | \
 // RUN: FileCheck %s --check-prefix=RA-NON-LEAF --check-prefix=KEY-A --check-prefix=BTE-ON --check-prefix=GCS-ON --check-prefix=WARN
 
+// RUN: %clang --target=aarch64-windows-msvc -c %s -### -mbranch-protection=standard                  2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-NON-LEAF --check-prefix=KEY-B --check-prefix=BTE-ON --check-prefix=GCS-ON --check-prefix=WARN
+
 // If the -msign-return-address and -mbranch-protection are both used, the
 // right-most one controls return address signing.
 // RUN: %clang --target=aarch64 -c %s -### -msign-return-address=non-leaf -mbranch-protection=none     2>&1 | \
@@ -37,6 +40,7 @@
 // RA-ALL: "-msign-return-address=all"
 
 // KEY-A: "-msign-return-address-key=a_key"
+// KEY-B: "-msign-return-address-key=b_key"
 // KEY-NOT: "-msign-return-address-key"
 
 // BTE-OFF-NOT: "-mbranch-target-enforce"
diff --git a/clang/test/Preprocessor/aarch64-target-features.c b/clang/test/Preprocessor/aarch64-target-features.c
index 70b7c705ead79..a3ded91d5695f 100644
--- a/clang/test/Preprocessor/aarch64-target-features.c
+++ b/clang/test/Preprocessor/aarch64-target-features.c
@@ -541,6 +541,7 @@
 // RUN: %clang -target arm64-none-linux-gnu -march=armv8-a -mbranch-protection=pac-ret+b-key -x c -E -dM %s -o - | FileCheck -check-prefix=CHECK-PAUTH-BKEY %s
 // RUN: %clang -target arm64-none-linux-gnu -march=armv8-a -mbranch-protection=pac-ret+leaf -x c -E -dM %s -o - | FileCheck -check-prefix=CHECK-PAUTH-ALL %s
 // RUN: %clang -target arm64-none-linux-gnu -march=armv8-a -mbranch-protection=pac-ret+leaf+b-key -x c -E -dM %s -o - | FileCheck -check-prefix=CHECK-PAUTH-BKEY-ALL %s
+// RUN: %clang -target aarch64-windows-msvc -march=armv8-a -mbranch-protection=standard -x c -E -dM %s -o - | FileCheck -check-prefix=CHECK-BRANCH-PROTECTION-STANDARD-WINDOWS %s
 //
 // Note: PAUTH-OFF - pac-ret is disabled
 //       CPU-NOPAUTH - FEAT_PAUTH support is disabled (but pac-ret can still use HINT-encoded instructions)
@@ -553,6 +554,9 @@
 // CHECK-PAUTH-BKEY-ALL:  #define __ARM_FEATURE_PAC_DEFAULT 6
 // CHECK-CPU-PAUTH:       #define __ARM_FEATURE_PAUTH 1
 // CHECK-CPU-NOPAUTH-NOT: __ARM_FEATURE_PAUTH
+// CHECK-BRANCH-PROTECTION-STANDARD-WINDOWS-DAG: #define __ARM_FEATURE_BTI_DEFAULT 1
+// CHECK-BRANCH-PROTECTION-STANDARD-WINDOWS-DAG: #define __ARM_FEATURE_GCS_DEFAULT 1
+// CHECK-BRANCH-PROTECTION-STANDARD-WINDOWS-DAG: #define __ARM_FEATURE_PAC_DEFAULT 2
 
 // ================== Check Branch Target Identification (BTI).
 // RUN: %clang -target arm64-none-linux-gnu -march=armv8-a -x c -E -dM %s -o - | FileCheck -check-prefix=CHECK-BTI-OFF %s
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index b186fbefb9e87..a878e6e68c24a 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -203,6 +203,8 @@ Makes programs 10x faster by doing Special New Thing.
   instead of the ABI names FP and LR. Note that LLVM IR produced by Clang
   always uses the ABI names, but other frontends may not.
   ([#167783](https://github.com/llvm/llvm-project/pull/167783))
+* On AArch64 Windows targets, return address signing now uses the B-key by
+  default because Windows unwind information only supports B-key signing.
 
 ### Changes to the AMDGPU Backend
 
diff --git a/llvm/include/llvm/TargetParser/ARMTargetParserCommon.h b/llvm/include/llvm/TargetParser/ARMTargetParserCommon.h
index 7c8030dd5576a..1df9740fde8b5 100644
--- a/llvm/include/llvm/TargetParser/ARMTargetParserCommon.h
+++ b/llvm/include/llvm/TargetParser/ARMTargetParserCommon.h
@@ -15,6 +15,7 @@
 
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Compiler.h"
+#include "llvm/TargetParser/Triple.h"
 
 namespace llvm {
 namespace ARM {
@@ -47,7 +48,8 @@ struct ParsedBranchProtection {
 };
 
 LLVM_ABI bool parseBranchProtection(StringRef Spec, ParsedBranchProtection &PBP,
-                                    StringRef &Err, bool EnablePAuthLR = false);
+                                    StringRef &Err, const llvm::Triple &Triple,
+                                    bool EnablePAuthLR = false);
 
 } // namespace ARM
 } // namespace llvm
diff --git a/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.cpp b/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.cpp
index 90dbd7304ec85..29aea3694497f 100644
--- a/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.cpp
@@ -18,6 +18,7 @@
 #include "AArch64Subtarget.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/IR/Constants.h"
+#include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
 #include "llvm/MC/MCAsmInfo.h"
@@ -93,6 +94,12 @@ static bool ShouldSignWithBKey(const Function &F, const AArch64Subtarget &STI) {
   const StringRef Key =
       F.getFnAttribute("sign-return-address-key").getValueAsString();
   assert(Key == "a_key" || Key == "b_key");
+  if (STI.getTargetTriple().isOSWindows() && Key == "a_key" &&
+      GetSignReturnAddress(F) != SignReturnAddress::None) {
+    F.getContext().diagnose(DiagnosticInfoUnsupported{
+        F, "A-key return address signing is unsupported on AArch64 Windows"});
+    return true;
+  }
   return Key == "b_key";
 }
 
diff --git a/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp b/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp
index 5372ffe869e1f..6be02ba0dc38b 100644
--- a/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp
+++ b/llvm/lib/Target/AArch64/AArch64PointerAuth.cpp
@@ -175,6 +175,8 @@ void AArch64PointerAuthImpl::signLR(MachineFunction &MF,
   }
 
   if (!EmitCFI && NeedsWinCFI) {
+    assert(UseBKey &&
+           "Windows SEH PAC unwind info only supports B-key signing");
     BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
         .setMIFlag(MachineInstr::FrameSetup);
   }
@@ -336,6 +338,8 @@ void AArch64PointerAuthImpl::authenticateLR(
   }
 
   if (NeedsWinCFI) {
+    assert(UseBKey &&
+           "Windows SEH PAC unwind info only supports B-key signing");
     BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
         .setMIFlag(MachineInstr::FrameDestroy);
   }
diff --git a/llvm/lib/TargetParser/ARMTargetParserCommon.cpp b/llvm/lib/TargetParser/ARMTargetParserCommon.cpp
index 15ba1ebd0c037..6d824c78cab97 100644
--- a/llvm/lib/TargetParser/ARMTargetParserCommon.cpp
+++ b/llvm/lib/TargetParser/ARMTargetParserCommon.cpp
@@ -141,7 +141,8 @@ ARM::EndianKind ARM::parseArchEndian(StringRef Arch) {
 // returned in `PBP`. Returns false in error, with `Err` containing
 // an erroneous part of the spec.
 bool ARM::parseBranchProtection(StringRef Spec, ParsedBranchProtection &PBP,
-                                StringRef &Err, bool EnablePAuthLR) {
+                                StringRef &Err, const llvm::Triple &Triple,
+                                bool EnablePAuthLR) {
   PBP = {"none", "a_key", false, false, false};
   if (Spec == "none")
     return true; // defaults are ok
@@ -151,6 +152,8 @@ bool ARM::parseBranchProtection(StringRef Spec, ParsedBranchProtection &PBP,
     PBP.BranchTargetEnforcement = true;
     PBP.GuardedControlStack = true;
     PBP.BranchProtectionPAuthLR = EnablePAuthLR;
+    if (Triple.isAArch64() && Triple.isOSWindows())
+      PBP.Key = "b_key";
     return true;
   }
 
@@ -164,6 +167,9 @@ bool ARM::parseBranchProtection(StringRef Spec, ParsedBranchProtection &PBP,
     }
     if (Opt == "pac-ret") {
       PBP.Scope = "non-leaf";
+      if (Triple.isAArch64() && Triple.isOSWindows())
+        PBP.Key = "b_key";
+
       for (; I + 1 != E; ++I) {
         StringRef PACOpt = Opts[I + 1].trim();
         if (PACOpt == "leaf")
diff --git a/llvm/test/CodeGen/AArch64/sign-return-address.ll b/llvm/test/CodeGen/AArch64/sign-return-address.ll
index 7267ce0e3afbf..755ba860c7ccc 100644
--- a/llvm/test/CodeGen/AArch64/sign-return-address.ll
+++ b/llvm/test/CodeGen/AArch64/sign-return-address.ll
@@ -5,14 +5,21 @@
 ; v9.5-A is not expected to change codegen without -mbranch-protection=+pc, so reuse DWARFCFI-V83A.
 ; RUN: llc -mtriple=aarch64 -mattr=v9.5a < %s | FileCheck --check-prefixes=CHECK,DWARFCFI,DWARFCFI-V83A %s
 
-; RUN: llc -mtriple=aarch64-windows              < %s | FileCheck --check-prefixes=CHECK,WINCFI,WINCFI-COMPAT %s
-; RUN: llc -mtriple=aarch64-windows -mattr=v8.3a < %s | FileCheck --check-prefixes=CHECK,WINCFI,WINCFI-V83A %s
+; RUN: sed -e '/^define i32 @leaf_sign_all_a_key(/,/^}/d' -e '/^define i32 @leaf_sign_all_a_key_bti(/,/^}/d' %s > %t.win-valid.ll
+; RUN: llc -mtriple=aarch64-windows              < %t.win-valid.ll | FileCheck --check-prefixes=CHECK,WINCFI,WINCFI-COMPAT %s
+; RUN: llc -mtriple=aarch64-windows -mattr=v8.3a < %t.win-valid.ll | FileCheck --check-prefixes=CHECK,WINCFI,WINCFI-V83A %s
+
+; RUN: sed -n -e '/^define i32 @leaf_sign_all_a_key(/,/^}/p' %s | not llc -mtriple=aarch64-windows -filetype=null 2>&1 | FileCheck --check-prefix=ERR-AKEY %s
 
 ; Make sure no errors are detected when emitting SEH opcodes.
 ; Errors are only checked for when generating a binary, so emit a dummy object
 ; file and make sure llc produces zero exit code.
-; RUN: llc -mtriple=aarch64-windows              -o %t.dummy.o --filetype=obj < %s
-; RUN: llc -mtriple=aarch64-windows -mattr=v8.3a -o %t.dummy.o --filetype=obj < %s
+; RUN: llc -mtriple=aarch64-windows              -o %t.dummy.o --filetype=obj < %t.win-valid.ll
+; RUN: llc -mtriple=aarch64-windows -mattr=v8.3a -o %t.dummy.o --filetype=obj < %t.win-valid.ll
+
+; ERR-AKEY: error:
+; ERR-AKEY-SAME: in function leaf_sign_all_a_key
+; ERR-AKEY-SAME: A-key return address signing is unsupported on AArch64 Windows
 
 define i32 @leaf(i32 %x) {
 ; CHECK-LABEL: leaf:
@@ -649,34 +656,6 @@ define i32 @leaf_sign_all_a_key(i32 %x) "sign-return-address"="all" "sign-return
 ; DWARFCFI-V83A-NEXT:    paciasp
 ; DWARFCFI-V83A-NEXT:    .cfi_negate_ra_state
 ; DWARFCFI-V83A-NEXT:    retaa
-;
-; WINCFI-COMPAT-LABEL: leaf_sign_all_a_key:
-; WINCFI-COMPAT:       .seh_proc leaf_sign_all_a_key
-; WINCFI-COMPAT-NEXT:  // %bb.0:
-; WINCFI-COMPAT-NEXT:    hint #25
-; WINCFI-COMPAT-NEXT:    .seh_pac_sign_lr
-; WINCFI-COMPAT-NEXT:    .seh_endprologue
-; WINCFI-COMPAT-NEXT:    .seh_startepilogue
-; WINCFI-COMPAT-NEXT:    hint #29
-; WINCFI-COMPAT-NEXT:    .seh_pac_sign_lr
-; WINCFI-COMPAT-NEXT:    .seh_endepilogue
-; WINCFI-COMPAT-NEXT:    ret
-; WINCFI-COMPAT-NEXT:    .seh_endfunclet
-; WINCFI-COMPAT-NEXT:    .seh_endproc
-;
-; WINCFI-V83A-LABEL: leaf_sign_all_a_key:
-; WINCFI-V83A:       .seh_proc leaf_sign_all_a_key
-; WINCFI-V83A-NEXT:  // %bb.0:
-; WINCFI-V83A-NEXT:    paciasp
-; WINCFI-V83A-NEXT:    .seh_pac_sign_lr
-; WINCFI-V83A-NEXT:    .seh_endprologue
-; WINCFI-V83A-NEXT:    .seh_startepilogue
-; WINCFI-V83A-NEXT:    autiasp
-; WINCFI-V83A-NEXT:    .seh_pac_sign_lr
-; WINCFI-V83A-NEXT:    .seh_endepilogue
-; WINCFI-V83A-NEXT:    ret
-; WINCFI-V83A-NEXT:    .seh_endfunclet
-; WINCFI-V83A-NEXT:    .seh_endproc
   ret i32 %x
 }
 
@@ -764,34 +743,6 @@ define i32 @leaf_sign_all_a_key_bti(i32 %x) "sign-return-address"="all" "sign-re
 ; DWARFCFI-V83A-NEXT:    paciasp
 ; DWARFCFI-V83A-NEXT:    .cfi_negate_ra_state
 ; DWARFCFI-V83A-NEXT:    retaa
-;
-; WINCFI-COMPAT-LABEL: leaf_sign_all_a_key_bti:
-; WINCFI-COMPAT:       .seh_proc leaf_sign_all_a_key_bti
-; WINCFI-COMPAT-NEXT:  // %bb.0:
-; WINCFI-COMPAT-NEXT:    hint #25
-; WINCFI-COMPAT-NEXT:    .seh_pac_sign_lr
-; WINCFI-COMPAT-NEXT:    .seh_endprologue
-; WINCFI-COMPAT-NEXT:    .seh_startepilogue
-; WINCFI-COMPAT-NEXT:    hint #29
-; WINCFI-COMPAT-NEXT:    .seh_pac_sign_lr
-; WINCFI-COMPAT-NEXT:    .seh_endepilogue
-; WINCFI-COMPAT-NEXT:    ret
-; WINCFI-COMPAT-NEXT:    .seh_endfunclet
-; WINCFI-COMPAT-NEXT:    .seh_endproc
-;
-; WINCFI-V83A-LABEL: leaf_sign_all_a_key_bti:
-; WINCFI-V83A:       .seh_proc leaf_sign_all_a_key_bti
-; WINCFI-V83A-NEXT:  // %bb.0:
-; WINCFI-V83A-NEXT:    paciasp
-; WINCFI-V83A-NEXT:    .seh_pac_sign_lr
-; WINCFI-V83A-NEXT:    .seh_endprologue
-; WINCFI-V83A-NEXT:    .seh_startepilogue
-; WINCFI-V83A-NEXT:    autiasp
-; WINCFI-V83A-NEXT:    .seh_pac_sign_lr
-; WINCFI-V83A-NEXT:    .seh_endepilogue
-; WINCFI-V83A-NEXT:    ret
-; WINCFI-V83A-NEXT:    .seh_endfunclet
-; WINCFI-V83A-NEXT:    .seh_endproc
   ret i32 %x
 }
 
diff --git a/llvm/test/CodeGen/AArch64/windows-pac-invalid-a-key.ll b/llvm/test/CodeGen/AArch64/windows-pac-invalid-a-key.ll
new file mode 100644
index 0000000000000..7203e10e4b200
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/windows-pac-invalid-a-key.ll
@@ -0,0 +1,11 @@
+; RUN: not llc -mtriple=aarch64-windows-msvc -filetype=null %s 2>&1 | FileCheck %s
+
+; CHECK: error:
+; CHECK-SAME: A-key return address signing is unsupported on AArch64 Windows
+
+define void @a_key() "sign-return-address"="non-leaf" "sign-return-address-key"="a_key" {
+  call void @callee()
+  ret void
+}
+
+declare void @callee()
diff --git a/llvm/test/CodeGen/AArch64/wineh-bti.ll b/llvm/test/CodeGen/AArch64/wineh-bti.ll
index 86555a7f64366..ce565bd16e880 100644
--- a/llvm/test/CodeGen/AArch64/wineh-bti.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-bti.ll
@@ -1,6 +1,6 @@
 ; RUN: llc < %s -mtriple=aarch64-windows -aarch64-min-jump-table-entries=4 | FileCheck %s
 
-define dso_local i32 @func(i32 %in) "sign-return-address"="non-leaf" "sign-return-address-key"="a_key" "branch-target-enforcement" {
+define dso_local i32 @func(i32 %in) "sign-return-address"="non-leaf" "sign-return-address-key"="b_key" "branch-target-enforcement" {
 entry:
   call void asm sideeffect "", "~{x19}"()
   switch i32 %in, label %def [

>From 033aa122e399ba23d216fa3e469ee62721b1b8e7 Mon Sep 17 00:00:00 2001
From: Ikhlas Ajbar <iajbar at quicinc.com>
Date: Thu, 16 Jul 2026 14:18:20 -0500
Subject: [PATCH 21/35] [Hexagon] Fix HexagonRDFOpt hang during live-in
 recomputation (#209986)

After HexagonRDFOpt, the compiler recomputed live-in registers for every
block in one batch. On large inputs (e.g. kernel builds) this was slow,
and on some functions each pass changed the live-in values of other
blocks, triggering more passes until the compiler hung (PR #207422).

Fix: process blocks one at a time in post-order (successors before
predecessors) and stop as soon as nothing changes. Add a pass limit as a
safety net to keep compile time bounded in all cases.

(cherry picked from commit da6f3e6cc2565e4c6cf720931845c8eee132ce86)
---
 llvm/lib/Target/Hexagon/HexagonRDFOpt.cpp     |  26 +-
 .../CodeGen/Hexagon/rdfopt-liveins-hang.mir   | 541 ++++++++++++++++++
 2 files changed, 562 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/CodeGen/Hexagon/rdfopt-liveins-hang.mir

diff --git a/llvm/lib/Target/Hexagon/HexagonRDFOpt.cpp b/llvm/lib/Target/Hexagon/HexagonRDFOpt.cpp
index 10aeb5381d694..cd367a88ed68e 100644
--- a/llvm/lib/Target/Hexagon/HexagonRDFOpt.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonRDFOpt.cpp
@@ -14,6 +14,7 @@
 #include "RDFCopy.h"
 #include "RDFDeadCode.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/PostOrderIterator.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/CodeGen/LivePhysRegs.h"
@@ -426,11 +427,26 @@ bool HexagonRDFOpt::runOnMachineFunction(MachineFunction &MF) {
     // lists. Skip:
     //   - the entry block: handled above from the RDF LiveMap;
     //   - EH pads: exception pointer/selector are runtime-established.
-    SmallVector<MachineBasicBlock *, 16> Blocks;
-    for (MachineBasicBlock &B : MF)
-      if (!B.isEntryBlock() && !B.isEHPad())
-        Blocks.push_back(&B);
-    fullyRecomputeLiveIns(Blocks);
+    //
+    // Recompute live-ins one block at a time, visiting successors before
+    // predecessors (post-order). This way each block already has fresh
+    // live-in info from its successors when it is processed.
+    SmallVector<MachineBasicBlock *, 16> Candidates;
+    for (MachineBasicBlock *MBB : post_order(&MF))
+      if (!MBB->isEntryBlock() && !MBB->isEHPad())
+        Candidates.push_back(MBB);
+
+    // One pass is usually enough. If any block's live-ins changed, repeat
+    // because its predecessors may need updating too. Stop after
+    // MaxLiveInSweeps passes to keep compile time bounded.
+    constexpr unsigned MaxLiveInSweeps = 8;
+    for (unsigned Sweep = 0; Sweep != MaxLiveInSweeps; ++Sweep) {
+      bool AnyChanged = false;
+      for (MachineBasicBlock *MBB : Candidates)
+        AnyChanged |= recomputeLiveIns(*MBB);
+      if (!AnyChanged)
+        break;
+    }
 
     // Recompute kill flags against the updated live-in lists.
     LV.resetKills();
diff --git a/llvm/test/CodeGen/Hexagon/rdfopt-liveins-hang.mir b/llvm/test/CodeGen/Hexagon/rdfopt-liveins-hang.mir
new file mode 100644
index 0000000000000..51e4c315d3a72
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/rdfopt-liveins-hang.mir
@@ -0,0 +1,541 @@
+# RUN: llc -mtriple=hexagon-unknown-linux-musl -run-pass=hexagon-rdf-opt \
+# RUN:     -verify-machineinstrs -o /dev/null %s
+#
+# Regression test for a HexagonRDFOpt compile-time hang.
+#
+# The pass used to recompute live-in registers for every block in one
+# big batch. On some functions the live-in values never stopped changing,
+# causing the compiler to hang
+#
+# The fix processes blocks one at a time in post-order and stops as
+# soon as nothing changes, with a pass limit as a safety net.
+# This test should finish in well under a second.
+
+--- |
+  target datalayout = "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32:32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32:32:32-v64:64:64-v512:512:512-v1024:1024:1024-v2048:2048:2048"
+  target triple = "hexagon-unknown-linux-musl"
+
+  %struct.folio = type { %struct.page }
+  %struct.page = type { %struct.memdesc_flags_t, %struct.anon, i32, ptr, i32, i32, i32, i32 }
+  %struct.memdesc_flags_t = type { i32 }
+  %struct.anon = type { %struct.list_head }
+  %struct.list_head = type { ptr, ptr }
+  %struct.dnode_of_data = type { ptr, %struct.folio, ptr, i32, i32 }
+
+  @data_blkaddr_node_folio = dso_local global %struct.folio zeroinitializer, align 4
+  @data_blkaddr_folio = dso_local local_unnamed_addr global ptr @data_blkaddr_node_folio, align 4
+  @f2fs_seek_block_folio = dso_local global %struct.folio zeroinitializer, align 4
+  @f2fs_seek_block___trans_tmp_8 = dso_local local_unnamed_addr global ptr @f2fs_seek_block_folio, align 4
+  @f2fs_seek_block_maxbytes = dso_local local_unnamed_addr global i32 2, align 4
+  @mem_map = dso_local local_unnamed_addr global ptr null, align 4
+  @__phys_offset = dso_local local_unnamed_addr global i32 0, align 4
+  @f2fs_seek_block_pgofs = dso_local local_unnamed_addr global i32 0, align 4
+  @f2fs_seek_block___trans_tmp_7 = dso_local local_unnamed_addr global ptr null, align 4
+  @f2fs_seek_block___trans_tmp_9 = dso_local local_unnamed_addr global i8 0, align 1
+  @f2fs_seek_block___trans_tmp_1 = dso_local local_unnamed_addr global i8 0, align 1
+  @f2fs_seek_block___trans_tmp_14 = dso_local local_unnamed_addr global i32 0, align 4
+  @f2fs_seek_block_end_offset = dso_local local_unnamed_addr global i32 0, align 4
+
+  declare i32 @get_inline_xattr_addrs(ptr noundef) local_unnamed_addr #0
+
+  ; Function Attrs: nounwind
+  define dso_local void @f2fs_seek_block(ptr noundef %file, i32 noundef %whence) local_unnamed_addr #1 {
+  entry:
+    %dn = alloca %struct.dnode_of_data, align 4
+    %0 = load ptr, ptr %file, align 4, !tbaa !9
+    %1 = load ptr, ptr %0, align 4, !tbaa !13
+    call void @llvm.lifetime.start.p0(ptr nonnull %dn) #4
+    call void @llvm.memset.p0.i32(ptr noundef nonnull align 4 dereferenceable(52) %dn, i8 -1, i32 52, i1 false), !annotation !16
+    %call = tail call i64 @i_size_read(ptr noundef %1) #4
+    %cmp = icmp slt i64 %call, 1
+    br i1 %cmp, label %fail, label %for.cond.preheader
+
+  for.cond.preheader:                               ; preds = %entry
+    %conv = trunc i64 %call to i32
+    br label %for.body
+
+  for.body:                                         ; preds = %for.inc34.thread, %for.inc34, %for.cond.preheader
+    %conv374 = phi i64 [ 0, %for.cond.preheader ], [ 2, %for.inc34 ], [ 2, %for.inc34.thread ]
+    %2 = icmp eq i32 %conv, 0
+    store ptr %1, ptr %dn, align 4, !tbaa !17
+    %sunkaddr = getelementptr inbounds i8, ptr %dn, i32 40
+    store ptr null, ptr %sunkaddr, align 4, !tbaa !27
+    %sunkaddr99 = getelementptr inbounds i8, ptr %dn, i32 44
+    store i32 0, ptr %sunkaddr99, align 4, !tbaa !28
+    %3 = load i32, ptr @f2fs_seek_block_pgofs, align 4, !tbaa !29
+    call void @f2fs_get_dnode_of_data(ptr noundef nonnull %dn, i32 noundef %3, i32 noundef 1) #4
+    br i1 %2, label %if.end11, label %if.then6
+
+  if.then6:                                         ; preds = %for.body
+    %4 = icmp eq i32 %whence, 0
+    br i1 %4, label %found, label %for.inc34.thread
+
+  if.end11:                                         ; preds = %for.body
+    %5 = load ptr, ptr @mem_map, align 4, !tbaa !30
+    %sub.ptr.rhs.cast.i = ptrtoint ptr %5 to i32
+    %sub.ptr.sub.i = sub i32 0, %sub.ptr.rhs.cast.i
+    %sub.ptr.div.i = sdiv exact i32 %sub.ptr.sub.i, 36
+    %6 = load i32, ptr @__phys_offset, align 4, !tbaa !32
+    %add1.i = shl i32 %sub.ptr.div.i, 12
+    %shr2.i = add i32 %add1.i, %6
+    %shl.i = and i32 %shr2.i, -4096
+    %7 = inttoptr i32 %shl.i to ptr
+    store ptr %7, ptr @f2fs_seek_block___trans_tmp_7, align 4, !tbaa !33
+    %cgep96 = getelementptr inbounds i8, ptr %7, i32 4
+    %p.sroa.5.0.copyload = load i16, ptr %cgep96, align 4, !tbaa !35
+    %tobool13 = icmp ne i16 %p.sroa.5.0.copyload, 0
+    %storedv = zext i1 %tobool13 to i8
+    store i8 %storedv, ptr @f2fs_seek_block___trans_tmp_9, align 1, !tbaa !37
+    store i8 %storedv, ptr @f2fs_seek_block___trans_tmp_1, align 1, !tbaa !37
+    br i1 %tobool13, label %cond.true, label %cond.end
+
+  cond.true:                                        ; preds = %if.end11
+    %call19 = call i32 @get_inline_xattr_addrs(ptr noundef %1) #4
+    br label %cond.end
+
+  cond.end:                                         ; preds = %cond.true, %if.end11
+    %cond = phi i32 [ %call19, %cond.true ], [ 2, %if.end11 ]
+    store i32 %cond, ptr @f2fs_seek_block___trans_tmp_14, align 4, !tbaa !32
+    store i32 %cond, ptr @f2fs_seek_block_end_offset, align 4, !tbaa !29
+    %cgep95 = getelementptr inbounds i8, ptr %dn, i32 48
+    %8 = load i32, ptr %cgep95, align 4, !tbaa !39
+    %cmp2173 = icmp ult i32 %8, %cond
+    br i1 %cmp2173, label %for.body23, label %for.inc34
+
+  for.body23:                                       ; preds = %for.inc, %cond.end
+    %9 = phi i32 [ %23, %for.inc ], [ %8, %cond.end ]
+    %10 = load ptr, ptr %dn, align 4, !tbaa !17
+    %11 = load ptr, ptr @mem_map, align 4, !tbaa !30
+    %12 = load i32, ptr @__phys_offset, align 4, !tbaa !32
+    %tobool.not.i.i = icmp eq ptr %10, null
+    br i1 %tobool.not.i.i, label %f2fs_data_blkaddr.exit, label %cond.true.i.i
+
+  cond.true.i.i:                                    ; preds = %for.body23
+    %13 = load i32, ptr %10, align 4, !tbaa !40
+    br label %f2fs_data_blkaddr.exit
+
+  f2fs_data_blkaddr.exit:                           ; preds = %cond.true.i.i, %for.body23
+    %cond.i.i = phi i32 [ %13, %cond.true.i.i ], [ 0, %for.body23 ]
+    %sub.ptr.rhs.cast.i.i.i = ptrtoint ptr %11 to i32
+    %sub.ptr.sub.i.i.i = sub i32 0, %sub.ptr.rhs.cast.i.i.i
+    %sub.ptr.div.i.i.i = sdiv exact i32 %sub.ptr.sub.i.i.i, 36
+    %add1.i.i.i = shl i32 %sub.ptr.div.i.i.i, 12
+    %shr2.i.i.i = add i32 %add1.i.i.i, %12
+    %shl.i.i.i = and i32 %shr2.i.i.i, -4096
+    %14 = inttoptr i32 %shl.i.i.i to ptr
+    %cgep97 = getelementptr inbounds [2 x i8], ptr %14, i32 %cond.i.i
+    %15 = load i16, ptr %cgep97, align 2, !tbaa !35
+    %conv.i.i = sext i16 %15 to i32
+    %add.i.i = sub nsw i32 0, %conv.i.i
+    %tobool25.not = icmp eq i32 %9, %add.i.i
+    br i1 %tobool25.not, label %for.inc, label %land.lhs.true
+
+  land.lhs.true:                                    ; preds = %f2fs_data_blkaddr.exit
+    %call26 = call i32 @F2FS_I_SB(ptr noundef %1) #4
+    %tobool27.not = icmp eq i32 %call26, 0
+    %sunkaddr100 = getelementptr inbounds i8, ptr %dn, i32 48
+    %.pre78 = load i32, ptr %sunkaddr100, align 4, !tbaa !32
+    br i1 %tobool27.not, label %for.inc, label %if.then28
+
+  if.then28:                                        ; preds = %land.lhs.true
+    %16 = load ptr, ptr %file, align 4, !tbaa !9
+    %__trans_tmp_18.sroa.0.0.copyload.i = load ptr, ptr %dn, align 4, !tbaa !42
+    %17 = load ptr, ptr @mem_map, align 4, !tbaa !30
+    %18 = load i32, ptr @__phys_offset, align 4, !tbaa !32
+    %tobool.not.i.i56 = icmp eq ptr %__trans_tmp_18.sroa.0.0.copyload.i, null
+    br i1 %tobool.not.i.i56, label %__found_offset.exit, label %cond.true.i.i57
+
+  cond.true.i.i57:                                  ; preds = %if.then28
+    %19 = load i32, ptr %__trans_tmp_18.sroa.0.0.copyload.i, align 4, !tbaa !40
+    br label %__found_offset.exit
+
+  __found_offset.exit:                              ; preds = %cond.true.i.i57, %if.then28
+    %cond.i.i58 = phi i32 [ %19, %cond.true.i.i57 ], [ 0, %if.then28 ]
+    %sub.ptr.rhs.cast.i.i.i59 = ptrtoint ptr %17 to i32
+    %sub.ptr.sub.i.i.i60 = sub i32 0, %sub.ptr.rhs.cast.i.i.i59
+    %sub.ptr.div.i.i.i61 = sdiv exact i32 %sub.ptr.sub.i.i.i60, 36
+    %add1.i.i.i62 = shl i32 %sub.ptr.div.i.i.i61, 12
+    %shr2.i.i.i63 = add i32 %add1.i.i.i62, %18
+    %shl.i.i.i64 = and i32 %shr2.i.i.i63, -4096
+    %20 = inttoptr i32 %shl.i.i.i64 to ptr
+    %cgep98 = getelementptr inbounds [2 x i8], ptr %20, i32 %cond.i.i58
+    %21 = load i16, ptr %cgep98, align 2, !tbaa !35
+    %conv.i.i66 = sext i16 %21 to i32
+    %22 = load ptr, ptr %16, align 4, !tbaa !13
+    %call3.i = call i32 @get_inline_xattr_addrs(ptr noundef %22) #4
+    %add.i.i67 = sub nsw i32 0, %conv.i.i66
+    %tobool4.not.i = icmp ne i32 %.pre78, %add.i.i67
+    %tobool.i = icmp ne i32 %call3.i, 0
+    %retval.0.i = select i1 %tobool4.not.i, i1 %tobool.i, i1 false
+    br i1 %retval.0.i, label %found, label %__found_offset.exit.for.inc_crit_edge
+
+  __found_offset.exit.for.inc_crit_edge:            ; preds = %__found_offset.exit
+    %sunkaddr101 = getelementptr inbounds i8, ptr %dn, i32 48
+    %.pre = load i32, ptr %sunkaddr101, align 4, !tbaa !39
+    br label %for.inc
+
+  for.inc:                                          ; preds = %__found_offset.exit.for.inc_crit_edge, %land.lhs.true, %f2fs_data_blkaddr.exit
+    %23 = phi i32 [ %.pre, %__found_offset.exit.for.inc_crit_edge ], [ %9, %f2fs_data_blkaddr.exit ], [ %.pre78, %land.lhs.true ]
+    %24 = load i32, ptr @f2fs_seek_block_pgofs, align 4, !tbaa !29
+    %inc = add nsw i32 %24, 1
+    store i32 %inc, ptr @f2fs_seek_block_pgofs, align 4, !tbaa !29
+    %25 = load i32, ptr @f2fs_seek_block_end_offset, align 4, !tbaa !29
+    %cmp21 = icmp ult i32 %23, %25
+    br i1 %cmp21, label %for.body23, label %for.inc34, !llvm.loop !43
+
+  for.inc34:                                        ; preds = %for.inc, %cond.end
+    %26 = icmp ugt i64 %call, 2
+    br i1 %26, label %for.body, label %for.end35, !llvm.loop !45
+
+  for.inc34.thread:                                 ; preds = %if.then6
+    %27 = icmp ugt i64 %call, 2
+    %28 = load i32, ptr @f2fs_seek_block_pgofs, align 4, !tbaa !29
+    %call9 = call i32 @f2fs_get_next_page_offset(ptr noundef nonnull %dn, i32 noundef %28) #4
+    store i32 %call9, ptr @f2fs_seek_block_pgofs, align 4, !tbaa !29
+    br i1 %27, label %for.body, label %found, !llvm.loop !45
+
+  for.end35:                                        ; preds = %for.inc34
+    %29 = icmp eq i32 %whence, 0
+    br i1 %29, label %fail, label %found
+
+  found:                                            ; preds = %for.end35, %for.inc34.thread, %__found_offset.exit, %if.then6
+    %conv372 = phi i64 [ %conv374, %__found_offset.exit ], [ 2, %for.end35 ], [ 2, %for.inc34.thread ], [ %conv374, %if.then6 ]
+    %30 = load i32, ptr @f2fs_seek_block_maxbytes, align 4, !tbaa !29
+    %call39 = call i32 @vfs_setpos(ptr noundef nonnull %file, i64 noundef %conv372, i32 noundef %30) #4
+    br label %fail
+
+  fail:                                             ; preds = %found, %for.end35, %entry
+    %call41 = call i32 @inode_unlock_shared(ptr noundef %1) #4
+    call void @llvm.lifetime.end.p0(ptr nonnull %dn) #4
+    ret void
+  }
+
+  declare i64 @i_size_read(ptr noundef) local_unnamed_addr #0
+
+  declare void @f2fs_get_dnode_of_data(ptr noundef, i32 noundef, i32 noundef) local_unnamed_addr #0
+
+  declare i32 @f2fs_get_next_page_offset(ptr noundef, i32 noundef) local_unnamed_addr #0
+
+  declare i32 @F2FS_I_SB(ptr noundef) local_unnamed_addr #0
+
+  declare i32 @vfs_setpos(ptr noundef, i64 noundef, i32 noundef) local_unnamed_addr #0
+
+  declare i32 @inode_unlock_shared(ptr noundef) local_unnamed_addr #0
+
+  ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+  declare void @llvm.lifetime.end.p0(ptr captures(none)) #2
+
+  ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+  declare void @llvm.lifetime.start.p0(ptr captures(none)) #2
+
+  ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: write)
+  declare void @llvm.memset.p0.i32(ptr writeonly captures(none), i8, i32, i1 immarg) #3
+
+  attributes #0 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="hexagonv68" "target-features"="+reserved-r19,+v68,-long-calls" }
+  attributes #1 = { nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="hexagonv68" "target-features"="+reserved-r19,+v68,-long-calls" }
+  attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+  attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: write) }
+  attributes #4 = { nounwind }
+
+  !llvm.module.flags = !{!0, !1, !2}
+  !llvm.ident = !{!3}
+  !llvm.errno.tbaa = !{!4}
+
+  !0 = !{i32 8, !"PIC Level", i32 2}
+  !1 = !{i32 7, !"PIE Level", i32 2}
+  !2 = !{i32 7, !"frame-pointer", i32 2}
+  !3 = !{!"clang version 23.0.0git (https://github.com/llvm/llvm-project.git 46c2e1fb7698776e97028f6e1aba5b942390249d)"}
+  !4 = !{!5, !6, i64 0}
+  !5 = !{!"__libc_errno", !6, i64 0}
+  !6 = !{!"int", !7, i64 0}
+  !7 = !{!"omnipotent char", !8, i64 0}
+  !8 = !{!"Simple C/C++ TBAA"}
+  !9 = !{!10, !11, i64 0}
+  !10 = !{!"file", !11, i64 0}
+  !11 = !{!"p1 _ZTS13address_space", !12, i64 0}
+  !12 = !{!"any pointer", !7, i64 0}
+  !13 = !{!14, !15, i64 0}
+  !14 = !{!"address_space", !15, i64 0}
+  !15 = !{!"p1 int", !12, i64 0}
+  !16 = !{!"auto-init"}
+  !17 = !{!18, !15, i64 0}
+  !18 = !{!"dnode_of_data", !15, i64 0, !19, i64 4, !26, i64 40, !6, i64 44, !6, i64 48}
+  !19 = !{!"folio", !20, i64 0}
+  !20 = !{!"page", !21, i64 0, !23, i64 4, !22, i64 12, !11, i64 16, !22, i64 20, !6, i64 24, !6, i64 28, !6, i64 32}
+  !21 = !{!"", !22, i64 0}
+  !22 = !{!"long", !7, i64 0}
+  !23 = !{!"", !24, i64 0}
+  !24 = !{!"list_head", !25, i64 0, !25, i64 4}
+  !25 = !{!"p1 _ZTS9list_head", !12, i64 0}
+  !26 = !{!"p1 _ZTS5folio", !12, i64 0}
+  !27 = !{!18, !26, i64 40}
+  !28 = !{!18, !6, i64 44}
+  !29 = !{!22, !22, i64 0}
+  !30 = !{!31, !31, i64 0}
+  !31 = !{!"p1 _ZTS4page", !12, i64 0}
+  !32 = !{!6, !6, i64 0}
+  !33 = !{!34, !34, i64 0}
+  !34 = !{!"p1 _ZTS9f2fs_node", !12, i64 0}
+  !35 = !{!36, !36, i64 0}
+  !36 = !{!"short", !7, i64 0}
+  !37 = !{!38, !38, i64 0}
+  !38 = !{!"_Bool", !7, i64 0}
+  !39 = !{!18, !6, i64 48}
+  !40 = !{!41, !6, i64 0}
+  !41 = !{!"f2fs_inode_info", !6, i64 0}
+  !42 = !{!15, !15, i64 0}
+  !43 = distinct !{!43, !44}
+  !44 = !{!"llvm.loop.mustprogress"}
+  !45 = distinct !{!45, !44}
+...
+---
+name:            f2fs_seek_block
+alignment:       4
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+noPhis:          true
+isSSA:           false
+noVRegs:         true
+hasFakeUses:     false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHContTarget: false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: true
+registers:       []
+liveins:         []
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       0
+  offsetAdjustment: 0
+  maxAlignment:    8
+  adjustsStack:    true
+  hasCalls:        true
+  framePointerPolicy: all
+  stackProtector:  ''
+  functionContext: ''
+  maxCallFrameSize: 4294967295
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  isCalleeSavedInfoValid: false
+  localFrameSize:  0
+fixedStack:      []
+stack:
+  - { id: 0, name: '', type: default, offset: 0, size: 52, alignment: 8,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 1, name: '', type: spill-slot, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 2, name: '', type: spill-slot, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 3, name: '', type: spill-slot, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 4, name: '', type: spill-slot, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 5, name: '', type: spill-slot, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 6, name: '', type: spill-slot, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 7, name: '', type: spill-slot, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 8, name: '', type: spill-slot, offset: 0, size: 4, alignment: 4,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+  - { id: 9, name: '', type: spill-slot, offset: 0, size: 8, alignment: 8,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo:
+  stackAlignBaseReg: ''
+body:             |
+  bb.0:
+    successors: %bb.23(0x30000000), %bb.1(0x50000000)
+    liveins: $r0, $r1
+
+    ADJCALLSTACKDOWN 0, 0, implicit-def $r29, implicit-def dead $r30, implicit $r31, implicit $r30, implicit $r29
+    ADJCALLSTACKUP 0, 0, implicit-def dead $r29, implicit-def dead $r30, implicit-def dead $r31, implicit $r29
+    J2_jumpt killed undef renamable $p0, %bb.23, implicit-def dead $pc
+    J2_jump %bb.1, implicit-def dead $pc
+
+  bb.1:
+    successors: %bb.2(0x80000000)
+    liveins: $d10:0x0000000000000003, $r17, $r18
+
+  bb.2:
+    successors: %bb.4(0x30000000), %bb.3(0x50000000)
+    liveins: $d2:0x0000000000000003, $d10:0x0000000000000003, $d11, $r17, $r24, $r25, $r26, $r27, $r18
+
+    ADJCALLSTACKDOWN 0, 0, implicit-def $r29, implicit-def dead $r30, implicit $r31, implicit $r30, implicit $r29
+    ADJCALLSTACKUP 0, 0, implicit-def dead $r29, implicit-def dead $r30, implicit-def dead $r31, implicit $r29
+    J2_jumpt killed undef renamable $p0, %bb.4, implicit-def dead $pc
+    J2_jump %bb.3, implicit-def dead $pc
+
+  bb.3:
+    successors: %bb.22(0x04000000), %bb.20(0x7c000000)
+    liveins: $d10:0x0000000000000003, $d11, $r16, $r24, $r25, $r26, $r27, $r18
+
+    J2_jumpt killed undef renamable $p0, %bb.22, implicit-def dead $pc
+    J2_jump %bb.20, implicit-def dead $pc
+
+  bb.4:
+    successors: %bb.5(0x50000000), %bb.6(0x30000000)
+    liveins: $d11, $r16, $r24, $r25, $r26, $r27, $r18
+
+    J2_jumpt killed undef renamable $p0, %bb.6, implicit-def dead $pc
+    J2_jump %bb.5, implicit-def dead $pc
+
+  bb.5:
+    successors: %bb.6(0x80000000)
+    liveins: $d11, $r17, $r18, $r24, $r25, $r26, $r27
+
+    ADJCALLSTACKDOWN 0, 0, implicit-def $r29, implicit-def dead $r30, implicit $r31, implicit $r30, implicit $r29
+    ADJCALLSTACKUP 0, 0, implicit-def dead $r29, implicit-def dead $r30, implicit-def dead $r31, implicit $r29
+
+  bb.6:
+    successors: %bb.7(0x40000000), %bb.19(0x40000000)
+    liveins: $d2:0x0000000000000003, $d11, $r0, $r17, $r18, $r24, $r25, $r26, $r27
+
+    renamable $r16 = L2_loadri_io %stack.4, 0 :: (load (s32) from %stack.4)
+    J2_jumpf killed undef renamable $p0, %bb.19, implicit-def dead $pc
+    J2_jump %bb.7, implicit-def dead $pc
+
+  bb.7:
+    successors: %bb.9(0x30000000), %bb.8(0x50000000)
+    liveins: $d2:0x0000000000000003, $d11, $r16, $r17, $r18, $r20, $r24, $r25, $r26, $r27
+
+    J2_jumpt killed undef renamable $p0, %bb.9, implicit-def dead $pc
+    J2_jump %bb.8, implicit-def dead $pc
+
+  bb.8:
+    successors: %bb.9(0x80000000)
+    liveins: $d2:0x0000000000000003, $d11, $r0, $r2, $r16, $r17, $r18, $r20, $r24, $r25, $r26, $r27
+
+  bb.9:
+    successors: %bb.18(0x40000000), %bb.10(0x40000000)
+    liveins: $d2:0x0000000000000003, $d11, $r0, $r1, $r16, $r17, $r18, $r20, $r24, $r25, $r26, $r27
+
+    J2_jumpt killed undef renamable $p0, %bb.18, implicit-def dead $pc
+    J2_jump %bb.10, implicit-def dead $pc
+
+  bb.10:
+    successors: %bb.11(0x30000000), %bb.12(0x50000000)
+    liveins: $d11, $r16, $r17, $r18, $r24, $r25, $r26, $r27
+
+    ADJCALLSTACKDOWN 0, 0, implicit-def $r29, implicit-def dead $r30, implicit $r31, implicit $r30, implicit $r29
+    ADJCALLSTACKUP 0, 0, implicit-def dead $r29, implicit-def dead $r30, implicit-def dead $r31, implicit $r29
+    J2_jumpf killed undef renamable $p0, %bb.12, implicit-def $pc
+
+  bb.11:
+    successors: %bb.18(0x80000000)
+    liveins: $d11, $r16, $r17, $r18, $r20, $r24, $r25, $r26, $r27
+
+    J2_jump %bb.18, implicit-def $pc
+
+  bb.12:
+    successors: %bb.14(0x30000000), %bb.13(0x50000000)
+    liveins: $d11, $r16, $r17, $r18, $r20, $r24, $r25, $r26, $r27
+
+    J2_jumpt killed undef renamable $p0, %bb.14, implicit-def dead $pc
+    J2_jump %bb.13, implicit-def dead $pc
+
+  bb.13:
+    successors: %bb.14(0x80000000)
+    liveins: $d11, $r0, $r2, $r16, $r17, $r18, $r20, $r24, $r25, $r26, $r27
+
+  bb.14:
+    successors: %bb.15(0x42000000), %bb.17(0x3e000000)
+    liveins: $d11, $r0, $r1, $r16, $r17, $r18, $r20, $r24, $r25, $r26, $r27
+
+    renamable $r4 = L2_loadri_io renamable $r16, 0 :: (load (s32), !tbaa !9)
+    ADJCALLSTACKDOWN 0, 0, implicit-def $r29, implicit-def dead $r30, implicit $r31, implicit $r30, implicit $r29
+    renamable $r0 = L2_loadri_io killed renamable $r4, 0 :: (load (s32), !tbaa !13)
+    ADJCALLSTACKUP 0, 0, implicit-def dead $r29, implicit-def dead $r30, implicit-def dead $r31, implicit $r29
+    J2_jumpt killed undef renamable $p0, %bb.17, implicit-def dead $pc
+    J2_jump %bb.15, implicit-def dead $pc
+
+  bb.15:
+    successors: %bb.16(0x07c1f07c), %bb.17(0x783e0f84)
+    liveins: $d11, $r0, $r16, $r17, $r18, $r24, $r25, $r26, $r27
+
+    renamable $p0 = C2_cmpeqi killed renamable $r0, 0
+    J2_jumpt killed renamable $p0, %bb.17, implicit-def $pc
+
+  bb.16:
+    successors: %bb.22(0x80000000)
+    liveins: $d11, $r17
+
+    J2_jump %bb.22, implicit-def $pc
+
+  bb.17:
+    successors: %bb.18(0x80000000)
+    liveins: $d11, $r16, $r17, $r18, $r24, $r25, $r26, $r27
+
+  bb.18:
+    successors: %bb.7(0x7c000000), %bb.19(0x04000000)
+    liveins: $d2:0x0000000000000003, $d11, $r16, $r17, $r18, $r20, $r24, $r25, $r26, $r27
+
+    J2_jumpt killed undef renamable $p0, %bb.7, implicit-def dead $pc
+    J2_jump %bb.19, implicit-def dead $pc
+
+  bb.19:
+    successors: %bb.2(0x7c000000), %bb.21(0x04000000)
+    liveins: $d2:0x0000000000000003, $r17, $r24, $r25, $r26, $r27, $r18
+
+    J2_jumpt killed undef renamable $p0, %bb.2, implicit-def dead $pc
+    J2_jump %bb.21, implicit-def dead $pc
+
+  bb.20:
+    successors: %bb.2(0x7c000000), %bb.22(0x04000000)
+    liveins: $d10:0x0000000000000003, $r17, $r24, $r25, $r26, $r27, $r18
+
+    ADJCALLSTACKDOWN 0, 0, implicit-def $r29, implicit-def dead $r30, implicit $r31, implicit $r30, implicit $r29
+    ADJCALLSTACKUP 0, 0, implicit-def dead $r29, implicit-def dead $r30, implicit-def dead $r31, implicit $r29
+    J2_jumpt killed undef renamable $p0, %bb.2, implicit-def dead $pc
+    J2_jump %bb.22, implicit-def dead $pc
+
+  bb.21:
+    successors: %bb.23(0x30000000), %bb.22(0x50000000)
+    liveins: $r17
+
+    J2_jumpt killed undef renamable $p0, %bb.23, implicit-def dead $pc
+    J2_jump %bb.22, implicit-def dead $pc
+
+  bb.22:
+    successors: %bb.23(0x80000000)
+    liveins: $d11, $r17
+
+    ADJCALLSTACKDOWN 0, 0, implicit-def $r29, implicit-def dead $r30, implicit $r31, implicit $r30, implicit $r29
+    ADJCALLSTACKUP 0, 0, implicit-def dead $r29, implicit-def dead $r30, implicit-def dead $r31, implicit $r29
+
+  bb.23:
+    liveins: $r17
+
+    ADJCALLSTACKDOWN 0, 0, implicit-def $r29, implicit-def dead $r30, implicit $r31, implicit $r30, implicit $r29
+    ADJCALLSTACKUP 0, 0, implicit-def dead $r29, implicit-def dead $r30, implicit-def dead $r31, implicit $r29
+    PS_jmpret $r31, implicit-def dead $pc
+...

>From 52cbf59a5085bff2083a03a71bfbd82b882b024a Mon Sep 17 00:00:00 2001
From: maflcko <6399679+maflcko at users.noreply.github.com>
Date: Fri, 17 Jul 2026 07:42:16 +0200
Subject: [PATCH 22/35] [compiler-rt][ubsan] Fix suppressions for inlined
 functions (#206735)

Align suppression PC handling with getCallerLocation(), so function
suppressions identify the inlined function that caused a report.

Intentionally match only the innermost inline frame.

Extend suppressions-nested-calls.c coverage to -O1.

Fixes https://github.com/llvm/llvm-project/issues/209501

---------

Co-authored-by: MarcoFalke <*~=`'#}+{/-|&$^_ at 721217.xyz>
(cherry picked from commit a376783e2ed3a44f63991c97513711faad09525b)
---
 compiler-rt/lib/ubsan/ubsan_diag.cpp          |  6 +++++
 .../Integer/suppressions-nested-calls.c       | 22 +++++++++++++------
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/compiler-rt/lib/ubsan/ubsan_diag.cpp b/compiler-rt/lib/ubsan/ubsan_diag.cpp
index 6efd082a89934..d06af776b54e1 100644
--- a/compiler-rt/lib/ubsan/ubsan_diag.cpp
+++ b/compiler-rt/lib/ubsan/ubsan_diag.cpp
@@ -438,6 +438,9 @@ bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) {
 bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) {
   InitAsStandaloneIfNecessary();
   CHECK(suppression_ctx);
+  // PC is the return address from the UBSan handler call. Symbolize the
+  // instruction that caused the call.
+  PC = StackTrace::GetPreviousInstructionPc(PC);
   const char *SuppType = ConvertTypeToFlagName(ET);
   // Fast path: don't symbolize PC if there is no suppressions for given UB
   // type.
@@ -453,6 +456,9 @@ bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) {
       return true;
   }
   // Suppress by function or source file name from debug info.
+  // The first frame is the innermost logical inline frame, if inline debug
+  // information is available. Do not search the rest of the chain: a
+  // suppression for an inline wrapper must not suppress an inlined callee.
   SymbolizedStackHolder Stack(Symbolizer::GetOrInit()->SymbolizePC(PC));
   const AddressInfo &AI = Stack.get()->info;
   return suppression_ctx->Match(AI.function, SuppType, &s) ||
diff --git a/compiler-rt/test/ubsan/TestCases/Integer/suppressions-nested-calls.c b/compiler-rt/test/ubsan/TestCases/Integer/suppressions-nested-calls.c
index 487d35eaa83f4..22ec6d4fe4642 100644
--- a/compiler-rt/test/ubsan/TestCases/Integer/suppressions-nested-calls.c
+++ b/compiler-rt/test/ubsan/TestCases/Integer/suppressions-nested-calls.c
@@ -3,28 +3,36 @@
 
 // # Test for UBSan suppressions with nested function calls
 //
-// RUN: %clang -fsanitize=integer -O0 -g %s -o %t.o0
 //
 // # Only the directly suppressed my_make_signed hit should disappear.
 // RUN: echo "implicit-integer-sign-change:my_make_signed" > %t.make_signed.name.supp
 // RUN: echo "implicit-integer-sign-change:Inputs/make_signed.h" > %t.make_signed.file.supp
 //
-// RUN: %env_ubsan_opts=suppressions='"%t.make_signed.name.supp"' %run %t.o0 2>&1 | FileCheck %s --check-prefix=CHECK-MAKE-SIGNED
-// RUN: %env_ubsan_opts=suppressions='"%t.make_signed.file.supp"' %run %t.o0 2>&1 | FileCheck %s --check-prefix=CHECK-MAKE-SIGNED
-//
 // # Only the suppressed wrapper-originated hit should disappear.
 // RUN: echo "implicit-integer-sign-change:my_wrapper_2" > %t.my_wrapper_2.name.supp
 // RUN: echo "implicit-integer-sign-change:Inputs/wrappers.h" > %t.wrappers.file.supp
 //
-// RUN: %env_ubsan_opts=suppressions='"%t.my_wrapper_2.name.supp"' %run %t.o0 2>&1 | FileCheck %s --check-prefix=CHECK-WRAPPERS
-// RUN: %env_ubsan_opts=suppressions='"%t.wrappers.file.supp"'     %run %t.o0 2>&1 | FileCheck %s --check-prefix=CHECK-WRAPPERS
-//
 // # Suppress both.
 // RUN: cat %t.make_signed.name.supp %t.my_wrapper_2.name.supp > %t.both.name.supp
 // RUN: cat %t.make_signed.file.supp %t.wrappers.file.supp > %t.both.file.supp
 //
+// # Compile and test each optimization level before the next compile. On
+// # Windows, the linker emits shared debug and auxiliary-file names here.
+// RUN: %clang -fsanitize=integer -O0 -g %s -o %t.o0
+// RUN: %env_ubsan_opts=suppressions='"%t.make_signed.name.supp"' %run %t.o0 2>&1 | FileCheck %s --check-prefix=CHECK-MAKE-SIGNED
+// RUN: %env_ubsan_opts=suppressions='"%t.make_signed.file.supp"' %run %t.o0 2>&1 | FileCheck %s --check-prefix=CHECK-MAKE-SIGNED
+// RUN: %env_ubsan_opts=suppressions='"%t.my_wrapper_2.name.supp"' %run %t.o0 2>&1 | FileCheck %s --check-prefix=CHECK-WRAPPERS
+// RUN: %env_ubsan_opts=suppressions='"%t.wrappers.file.supp"'     %run %t.o0 2>&1 | FileCheck %s --check-prefix=CHECK-WRAPPERS
 // RUN: %env_ubsan_opts=suppressions='"%t.both.name.supp"' %run %t.o0 2>&1 | FileCheck %s --allow-empty --check-prefix=CHECK-BOTH
 // RUN: %env_ubsan_opts=suppressions='"%t.both.file.supp"' %run %t.o0 2>&1 | FileCheck %s --allow-empty --check-prefix=CHECK-BOTH
+//
+// RUN: %clang -fsanitize=integer -O1 -g %s -o %t.o1
+// RUN: %env_ubsan_opts=suppressions='"%t.make_signed.name.supp"' %run %t.o1 2>&1 | FileCheck %s --check-prefix=CHECK-MAKE-SIGNED
+// RUN: %env_ubsan_opts=suppressions='"%t.make_signed.file.supp"' %run %t.o1 2>&1 | FileCheck %s --check-prefix=CHECK-MAKE-SIGNED
+// RUN: %env_ubsan_opts=suppressions='"%t.my_wrapper_2.name.supp"' %run %t.o1 2>&1 | FileCheck %s --check-prefix=CHECK-WRAPPERS
+// RUN: %env_ubsan_opts=suppressions='"%t.wrappers.file.supp"'     %run %t.o1 2>&1 | FileCheck %s --check-prefix=CHECK-WRAPPERS
+// RUN: %env_ubsan_opts=suppressions='"%t.both.name.supp"' %run %t.o1 2>&1 | FileCheck %s --allow-empty --check-prefix=CHECK-BOTH
+// RUN: %env_ubsan_opts=suppressions='"%t.both.file.supp"' %run %t.o1 2>&1 | FileCheck %s --allow-empty --check-prefix=CHECK-BOTH
 
 #include "Inputs/make_signed.h"
 #include "Inputs/wrappers.h"

>From 44addbd4e1fbf754092185cd74c08a1e283df80f Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Fri, 17 Jul 2026 09:51:47 +0200
Subject: [PATCH 23/35] [LTO] Sort DefinedGlobals in LTO cache key (#210025)

Sort the DefindeGlobals by GUID when computing the LTO cache key, to
avoid making the cache key dependent on the insertion order. This fixes
large compile-time regressions in rust incremental builds.

Alternatively one could make collectDefinedGVSummariesPerModule() work
on the sortedRange(), but as that is also used in other places, it
probably makes sense to sort locally.

The issue was introduced in 760bb06cb3cd98832f1e4e7a4eaebd98b66d2bdd.

(cherry picked from commit 90e31c46d005ae0a7f5968c38a9310bee499923c)
---
 llvm/lib/LTO/LTO.cpp                          | 11 +++++---
 .../X86/Inputs/cache-defined-globals-order.ll |  6 +++++
 .../X86/cache-defined-globals-order.ll        | 27 +++++++++++++++++++
 3 files changed, 41 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/ThinLTO/X86/Inputs/cache-defined-globals-order.ll
 create mode 100644 llvm/test/ThinLTO/X86/cache-defined-globals-order.ll

diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index b0fd1d77a2d66..c8c5b0880819a 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -304,9 +304,14 @@ std::string llvm::computeLTOCacheKey(
     }
   };
 
-  // Include the hash for the linkage type to reflect internalization and weak
-  // resolution, and collect any used type identifier resolutions.
-  for (auto &GS : DefinedGlobals) {
+  // Sort the defined globals by GUID to be independent of the insertion order,
+  // which may depend on the order that modules are added.
+  SmallVector<std::pair<GlobalValue::GUID, GlobalValueSummary *>>
+      SortedDefinedGlobals(DefinedGlobals.begin(), DefinedGlobals.end());
+  llvm::sort(SortedDefinedGlobals, llvm::less_first());
+  for (auto &GS : SortedDefinedGlobals) {
+    // Include the hash for the linkage type to reflect internalization and weak
+    // resolution, and collect any used type identifier resolutions.
     GlobalValue::LinkageTypes Linkage = GS.second->linkage();
     Hasher.update(
         ArrayRef<uint8_t>((const uint8_t *)&Linkage, sizeof(Linkage)));
diff --git a/llvm/test/ThinLTO/X86/Inputs/cache-defined-globals-order.ll b/llvm/test/ThinLTO/X86/Inputs/cache-defined-globals-order.ll
new file mode 100644
index 0000000000000..5a527c9bae924
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/Inputs/cache-defined-globals-order.ll
@@ -0,0 +1,6 @@
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define linkonce_odr void @shared29() {
+  ret void
+}
diff --git a/llvm/test/ThinLTO/X86/cache-defined-globals-order.ll b/llvm/test/ThinLTO/X86/cache-defined-globals-order.ll
new file mode 100644
index 0000000000000..84f71f20f9e6d
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/cache-defined-globals-order.ll
@@ -0,0 +1,27 @@
+; RUN: rm -rf %t && mkdir -p %t
+; RUN: opt -module-hash -module-summary %s -o %t/t.bc
+; RUN: opt -module-hash -module-summary %S/Inputs/cache-defined-globals-order.ll -o %t/a.bc
+
+; Tests that the LTO cache key is insensitive to the order of the modules.
+; The linkonce_odr function @shared29 is defined in both t.bc and a.bc, so
+; it gets a different position in the combined index depending on which
+; module is processed first, which results in a different insertion order
+; for DefinedGlobals.
+
+; RUN: llvm-lto2 run -cache-dir %t/cache -o %t.o %t/t.bc %t/a.bc -r=%t/t.bc,main,plx -r=%t/t.bc,shared29,plx -r=%t/a.bc,shared29,lx
+; RUN: ls %t/cache | count 2
+
+; RUN: llvm-lto2 run -cache-dir %t/cache -o %t.o %t/a.bc %t/t.bc -r=%t/t.bc,main,plx -r=%t/t.bc,shared29,plx -r=%t/a.bc,shared29,lx
+; RUN: ls %t/cache | count 2
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @main() {
+  call void @shared29()
+  ret void
+}
+
+define linkonce_odr void @shared29() {
+  ret void
+}

>From 28af770db4db6f04d047f52e7e135386fe7a6824 Mon Sep 17 00:00:00 2001
From: cqwrteur <oyzawqgcfc at gmail.com>
Date: Fri, 17 Jul 2026 17:11:45 +0800
Subject: [PATCH 24/35] [libcxx] avoid include Uppercase windows headers
 (#208903)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

We should use unknwn.h and windows.h instead of Unknwn.h and Windows.h
because Linux and other non‑Windows systems treat filenames as
case‑sensitive. Windows does not, so mixed‑case includes work there but
break elsewhere. Both mingw‑w64‑crt and windows‑msvc‑sysroot provide all
Windows headers in fully lowercase, so using the lowercase forms ensures
consistent cross‑platform builds.

Fixes #208901

(cherry picked from commit 2580624091e36b85c162439513eef8e752217a1d)
---
 libcxx/src/support/runtime/exception_pointer_msvc.ipp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/src/support/runtime/exception_pointer_msvc.ipp b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
index 72360a2d791bd..3102d1ee93db2 100644
--- a/libcxx/src/support/runtime/exception_pointer_msvc.ipp
+++ b/libcxx/src/support/runtime/exception_pointer_msvc.ipp
@@ -14,8 +14,8 @@ _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmultichar")
 #include <cstdlib>
 #include <cstring>
 
-#include <Unknwn.h>
-#include <Windows.h>
+#include <unknwn.h>
+#include <windows.h>
 struct _ThrowInfo;
 #include <eh.h>
 #include <ehdata.h>

>From 44727f2bc6e66afccfc0e3e3f0d2e5f241924593 Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Fri, 17 Jul 2026 00:17:00 +0200
Subject: [PATCH 25/35] [clang][mingw] Try both native and ARM64EC triples when
 looking for a sysroot on the ARM64EC target (#210017)

Similarly to MSVC, a MinGW toolchain may provide support for both ARM64
and ARM64EC in a single sysroot. Structuring the toolchain this way has
the additional advantage of seamlessly supporting linking ARM64X images.

Support this configuration in findClangRelativeSysroot.

(cherry picked from commit 205a66201235c03b892e075ae548d6ea12f024a3)
---
 clang/lib/Driver/ToolChains/MinGW.cpp | 15 +++++++++++++++
 clang/test/Driver/mingw-sysroot.cpp   | 20 ++++++++++++++++++++
 2 files changed, 35 insertions(+)

diff --git a/clang/lib/Driver/ToolChains/MinGW.cpp b/clang/lib/Driver/ToolChains/MinGW.cpp
index 7fe5be92012ee..65120931d8181 100644
--- a/clang/lib/Driver/ToolChains/MinGW.cpp
+++ b/clang/lib/Driver/ToolChains/MinGW.cpp
@@ -476,11 +476,26 @@ findClangRelativeSysroot(const Driver &D, const llvm::Triple &LiteralTriple,
                          const llvm::Triple &T, std::string &SubdirName) {
   llvm::SmallVector<llvm::SmallString<32>, 4> Subdirs;
   Subdirs.emplace_back(LiteralTriple.str());
+  // On ARM64EC targets, also try native aarch64 sysroot, which may contain
+  // support for both targets.
+  if (LiteralTriple.isWindowsArm64EC()) {
+    llvm::Triple NativeTriple(LiteralTriple);
+    NativeTriple.setArchName("aarch64");
+    Subdirs.emplace_back(NativeTriple.str());
+  }
   Subdirs.emplace_back(T.str());
   Subdirs.emplace_back(T.getArchName());
   Subdirs.back() += "-w64-mingw32";
   Subdirs.emplace_back(T.getArchName());
   Subdirs.back() += "-w64-mingw32ucrt";
+  if (T.isWindowsArm64EC()) {
+    llvm::Triple NativeTriple(T);
+    NativeTriple.setArchName("aarch64");
+    Subdirs.emplace_back(NativeTriple.str());
+
+    Subdirs.emplace_back("aarch64-w64-mingw32");
+    Subdirs.emplace_back("aarch64-w64-mingw32ucrt");
+  }
   StringRef ClangRoot = llvm::sys::path::parent_path(D.Dir);
   StringRef Sep = llvm::sys::path::get_separator();
   for (StringRef CandidateSubdir : Subdirs) {
diff --git a/clang/test/Driver/mingw-sysroot.cpp b/clang/test/Driver/mingw-sysroot.cpp
index 8e46d23c1782d..372df0509c8d9 100644
--- a/clang/test/Driver/mingw-sysroot.cpp
+++ b/clang/test/Driver/mingw-sysroot.cpp
@@ -10,8 +10,11 @@
 // RUN: rm -rf %t.dir/testroot-clang
 // RUN: mkdir -p %t.dir/testroot-clang/bin
 // RUN: ln -s %clang %t.dir/testroot-clang/bin/x86_64-w64-mingw32-clang
+// RUN: ln -s %clang %t.dir/testroot-clang/bin/aarch64-w64-mingw32-clang
+// RUN: ln -s %clang %t.dir/testroot-clang/bin/arm64ec-w64-mingw32-clang
 // RUN: ln -s %S/Inputs/mingw_ubuntu_posix_tree/usr/x86_64-w64-mingw32 %t.dir/testroot-clang/x86_64-w64-mingw32
 // RUN: ln -s %S/Inputs/mingw_arch_tree/usr/i686-w64-mingw32 %t.dir/testroot-clang/i686-w64-mingw32
+// RUN: ln -s %S/Inputs/mingw_ubuntu_posix_tree/usr/x86_64-w64-mingw32 %t.dir/testroot-clang/aarch64-w64-mingw32
 
 // RUN: rm -rf %t.dir/testroot-clang-native
 // RUN: mkdir -p %t.dir/testroot-clang-native/bin
@@ -23,6 +26,7 @@
 // RUN: mkdir -p %t.dir/testroot-custom-triple/bin
 // RUN: ln -s %clang %t.dir/testroot-custom-triple/bin/clang
 // RUN: ln -s %S/Inputs/mingw_ubuntu_posix_tree/usr/x86_64-w64-mingw32 %t.dir/testroot-custom-triple/x86_64-w64-mingw32foo
+// RUN: ln -s %S/Inputs/mingw_ubuntu_posix_tree/usr/x86_64-w64-mingw32 %t.dir/testroot-custom-triple/aarch64-w64-mingw32foo
 
 // If we find a gcc in the path with the right triplet prefix, pick that as
 // sysroot:
@@ -103,3 +107,19 @@
 
 // RUN: %t.dir/testroot-custom-triple/bin/clang -no-canonical-prefixes --target=x86_64-w64-mingw32foo -rtlib=compiler-rt -stdlib=libstdc++ --sysroot="" -c -### %s 2>&1 | FileCheck -check-prefix=CHECK_TESTROOT_CUSTOM_TRIPLE %s
 // CHECK_TESTROOT_CUSTOM_TRIPLE: "{{[^"]+}}/testroot-custom-triple{{/|\\\\}}x86_64-w64-mingw32foo{{/|\\\\}}include"
+
+// Check that the arm64ec target uses the aarch64 sysroot if a separate arm64ec sysroot is not found.
+
+// RUN: %t.dir/testroot-clang/bin/aarch64-w64-mingw32-clang -no-canonical-prefixes --target=aarch64-w64-mingw32 -rtlib=compiler-rt -stdlib=libstdc++ --sysroot="" -c -### %s 2>&1 | FileCheck -check-prefix=CHECK_TESTROOT_CLANG_AARCH64 %s
+// RUN: %t.dir/testroot-clang/bin/arm64ec-w64-mingw32-clang -no-canonical-prefixes --target=arm64ec-w64-mingw32 -rtlib=compiler-rt -stdlib=libstdc++ --sysroot="" -c -### %s 2>&1 | FileCheck -check-prefix=CHECK_TESTROOT_CLANG_AARCH64 %s
+// CHECK_TESTROOT_CLANG_AARCH64: "{{[^"]+}}/testroot-clang{{/|\\\\}}aarch64-w64-mingw32{{/|\\\\}}include"
+
+// RUN: %t.dir/testroot-custom-triple/bin/clang -no-canonical-prefixes --target=aarch64-w64-mingw32foo -rtlib=compiler-rt -stdlib=libstdc++ --sysroot="" -c -### %s 2>&1 | FileCheck -check-prefix=CHECK_TESTROOT_CUSTOM_TRIPLE_AARCH64 %s
+// RUN: %t.dir/testroot-custom-triple/bin/clang -no-canonical-prefixes --target=arm64ec-w64-mingw32foo -rtlib=compiler-rt -stdlib=libstdc++ --sysroot="" -c -### %s 2>&1 | FileCheck -check-prefix=CHECK_TESTROOT_CUSTOM_TRIPLE_AARCH64 %s
+// CHECK_TESTROOT_CUSTOM_TRIPLE_AARCH64: "{{[^"]+}}/testroot-custom-triple{{/|\\\\}}aarch64-w64-mingw32foo{{/|\\\\}}include"
+
+// Check that the arm64ec sysroot is still preferred when available.
+
+// RUN: ln -s %S/Inputs/mingw_ubuntu_posix_tree/usr/x86_64-w64-mingw32 %t.dir/testroot-custom-triple/arm64ec-w64-mingw32foo
+// RUN: %t.dir/testroot-custom-triple/bin/clang -no-canonical-prefixes --target=arm64ec-w64-mingw32foo -rtlib=compiler-rt -stdlib=libstdc++ --sysroot="" -c -### %s 2>&1 | FileCheck -check-prefix=CHECK_TESTROOT_CUSTOM_TRIPLE_ARM64EC %s
+// CHECK_TESTROOT_CUSTOM_TRIPLE_ARM64EC: "{{[^"]+}}/testroot-custom-triple{{/|\\\\}}arm64ec-w64-mingw32foo{{/|\\\\}}include"

>From f83ba9d97b34021e3d2ff2faf7b04a3001b2c7ca Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Fri, 17 Jul 2026 14:34:11 +0200
Subject: [PATCH 26/35] [X86] Fix assertion failure for tail call on i686+pic
 (#210302)

The pattern for TCRETURNmi has a IsNotPIC predicate, but the
checkTCRetEnoughRegs() predicate that's part of the X86tcret_enough_regs
PatFrag is evaluated first, so we can't assert that PIC is disabled
here. We should just return false in that case.

Fixes https://github.com/llvm/llvm-project/issues/210300.

(cherry picked from commit 23b0281d75a4320a4e6e3d837edf94911d98d252)
---
 llvm/lib/Target/X86/X86ISelDAGToDAG.cpp    |  3 ++-
 llvm/test/CodeGen/X86/tailcall-i686-pic.ll | 14 ++++++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/X86/tailcall-i686-pic.ll

diff --git a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
index b794567f6a047..9a0045367a8bf 100644
--- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
@@ -3561,7 +3561,8 @@ bool X86DAGToDAGISel::checkTCRetEnoughRegs(SDNode *N) const {
       LoadGPRs -= 2; // Base is fixed index off ESP; no regs needed.
     } else if (BasePtr.getOpcode() == X86ISD::Wrapper &&
                isa<GlobalAddressSDNode>(BasePtr->getOperand(0))) {
-      assert(!getTargetMachine().isPositionIndependent());
+      if (getTargetMachine().isPositionIndependent())
+        return false;
       LoadGPRs -= 1; // Base is a global (immediate since this is non-PIC), no
                      // reg needed.
     }
diff --git a/llvm/test/CodeGen/X86/tailcall-i686-pic.ll b/llvm/test/CodeGen/X86/tailcall-i686-pic.ll
new file mode 100644
index 0000000000000..b8a4207451e8a
--- /dev/null
+++ b/llvm/test/CodeGen/X86/tailcall-i686-pic.ll
@@ -0,0 +1,14 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=i686-pc-windows-msvc -relocation-model=pic < %s | FileCheck %s
+
+ at fnptr = external global ptr
+
+define void @test() {
+; CHECK-LABEL: test:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movl _fnptr, %eax
+; CHECK-NEXT:    jmpl *%eax # TAILCALL
+  %p = load ptr, ptr @fnptr
+  tail call void %p()
+  ret void
+}

>From 278c31bfb8ceb7ea17dbfd11a4fb21e6634af957 Mon Sep 17 00:00:00 2001
From: Tom Stellard <tstellar at redhat.com>
Date: Thu, 16 Jul 2026 21:06:59 -0700
Subject: [PATCH 27/35] workflows: Fixes for release-binaries and
 upload-release-artifact (#209246)

There were some bugs in upload-release-artifact workflow and
release-binaries was not including this action in its checkout.

(cherry picked from commit 963e226d9a6b923b10c8e328438838bd42e03d18)
---
 .github/workflows/release-binaries.yml                | 1 +
 .github/workflows/validate-release-version/action.yml | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml
index 6fdf5e8c14cb8..d2dbb656d371d 100644
--- a/.github/workflows/release-binaries.yml
+++ b/.github/workflows/release-binaries.yml
@@ -386,6 +386,7 @@ jobs:
           persist-credentials: false
           sparse-checkout: |
             .github/workflows/upload-release-artifact
+            .github/workflows/validate-release-version
             llvm/utils/release/github-upload-release.py
             llvm/utils/git/requirements.txt
           sparse-checkout-cone-mode: false
diff --git a/.github/workflows/validate-release-version/action.yml b/.github/workflows/validate-release-version/action.yml
index 1a10ad67cb549..669369eaf82e6 100644
--- a/.github/workflows/validate-release-version/action.yml
+++ b/.github/workflows/validate-release-version/action.yml
@@ -9,7 +9,7 @@ runs:
   using: "composite"
   steps:
     - env:
-      RELEASE_VERSON: ${{ inputs.release-version }}
-
+        RELEASE_VERSION: ${{ inputs.release-version }}
+      shell: bash
       run: |
         grep -e '^[0-9]\+\.[0-9]\+\.[0-9]\+\(-rc[0-9]\+\)\?$' <<< "$RELEASE_VERSION"

>From 03c1f18aeee0150dc0243cd32b09b5050c9b6224 Mon Sep 17 00:00:00 2001
From: Douglas Yung <douglas.yung at sony.com>
Date: Sat, 18 Jul 2026 02:34:37 +0000
Subject: [PATCH 28/35] Bump version to 23.1.0-rc1

---
 cmake/Modules/LLVMVersion.cmake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cmake/Modules/LLVMVersion.cmake b/cmake/Modules/LLVMVersion.cmake
index ed3dfebf7548a..e5e1c00cd64c3 100644
--- a/cmake/Modules/LLVMVersion.cmake
+++ b/cmake/Modules/LLVMVersion.cmake
@@ -10,6 +10,6 @@ if(NOT DEFINED LLVM_VERSION_PATCH)
   set(LLVM_VERSION_PATCH 0)
 endif()
 if(NOT DEFINED LLVM_VERSION_SUFFIX)
-  set(LLVM_VERSION_SUFFIX git)
+  set(LLVM_VERSION_SUFFIX ('rc', 1))
 endif()
 

>From acdf320b78fcf075ddcf660b51cf18d5c0af755b Mon Sep 17 00:00:00 2001
From: Douglas Yung <douglas.yung at sony.com>
Date: Sat, 18 Jul 2026 07:35:12 +0000
Subject: [PATCH 29/35] Fix bumping of version to 23.1.0-rc1.

---
 cmake/Modules/LLVMVersion.cmake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cmake/Modules/LLVMVersion.cmake b/cmake/Modules/LLVMVersion.cmake
index e5e1c00cd64c3..22dbb25383def 100644
--- a/cmake/Modules/LLVMVersion.cmake
+++ b/cmake/Modules/LLVMVersion.cmake
@@ -10,6 +10,6 @@ if(NOT DEFINED LLVM_VERSION_PATCH)
   set(LLVM_VERSION_PATCH 0)
 endif()
 if(NOT DEFINED LLVM_VERSION_SUFFIX)
-  set(LLVM_VERSION_SUFFIX ('rc', 1))
+  set(LLVM_VERSION_SUFFIX -rc1)
 endif()
 

>From 4285c201f92ae76a6db1db99fa9c40f5283b1cfa Mon Sep 17 00:00:00 2001
From: Tom Stellard <tstellar at redhat.com>
Date: Mon, 20 Jul 2026 08:52:47 -0700
Subject: [PATCH 30/35] workflows/release-documentation: Add missing secrets
 (#210421)

(cherry picked from commit b7810e97d6281f65e255b36af7b39af18d3d2006)
---
 .github/workflows/release-documentation.yml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/.github/workflows/release-documentation.yml b/.github/workflows/release-documentation.yml
index 75dfb6fdd3437..12e6a3844660c 100644
--- a/.github/workflows/release-documentation.yml
+++ b/.github/workflows/release-documentation.yml
@@ -32,6 +32,12 @@ on:
       WWW_RELEASES_TOKEN:
         description: "Secret used to create a PR with the documentation changes."
         required: false
+      LLVM_TOKEN_GENERATOR_CLIENT_ID:
+        description: "Client ID for our GitHub App we use for generating access tokens."
+        required: true
+      LLVM_TOKEN_GENERATOR_PRIVATE_KEY:
+        description: "Private key for our GitHub App we use for generating access tokens."
+        required: true
 
 jobs:
   # This job checks permissions and validates inputs to prevent potential

>From db494ccad869323687b23af33e4226479a54d8f2 Mon Sep 17 00:00:00 2001
From: Tom Stellard <tstellar at redhat.com>
Date: Mon, 20 Jul 2026 14:12:13 -0700
Subject: [PATCH 31/35] workflows: Add missing checkouts when using
 validate-release-version (#210471)

We need to make sure all composite workflows we use are checked out
manually from th egit repo.

(cherry picked from commit 429c88d37f1f02e68ebc1fc7b0da4511ce6407e3)
---
 .github/workflows/release-binaries-all.yml |  7 +++++++
 .github/workflows/release-binaries.yml     | 10 +++++-----
 2 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/release-binaries-all.yml b/.github/workflows/release-binaries-all.yml
index 2e2869e4073da..862300a8118e5 100644
--- a/.github/workflows/release-binaries-all.yml
+++ b/.github/workflows/release-binaries-all.yml
@@ -68,6 +68,13 @@ jobs:
       release-version: ${{ steps.vars.outputs.release-version }}
       upload: ${{ steps.vars.outputs.upload }}
     steps:
+      - uses: actions/checkout at de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+        with:
+          persist-credentials: false
+          sparse-checkout: |
+            .github/workflows/validate-release-version
+          sparse-checkout-cone-mode: false
+
       - name: Validate Input
         if: inputs.release-version != ''
         uses: ./.github/workflows/validate-release-version
diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml
index d2dbb656d371d..9b00a4945703c 100644
--- a/.github/workflows/release-binaries.yml
+++ b/.github/workflows/release-binaries.yml
@@ -72,17 +72,17 @@ jobs:
       attestation-name: ${{ steps.vars.outputs.attestation-name }}
 
     steps:
+    - name: Checkout LLVM
+      uses: actions/checkout at de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+      with:
+          persist-credentials: false
+
     - name: Validate Release Version
       if: inputs.release-version != ''
       uses: ./.github/workflows/validate-release-version
       with:
         release-version: ${{ inputs.release-version }}
 
-    - name: Checkout LLVM
-      uses: actions/checkout at de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-      with:
-          persist-credentials: false
-
     - name: Check Permissions
       if: github.event_name != 'pull_request'
       uses: ./.github/workflows/require-team-membership

>From fd8d9d53043e84807bda631d21d9742eb595e30a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 17 Jul 2026 13:05:51 -0400
Subject: [PATCH 32/35] [libc++] Target the release runners set from the LLVM
 23 release branch

---
 .github/workflows/libcxx-build-and-test.yaml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml
index 7244d236877bd..f30f6fa534eb3 100644
--- a/.github/workflows/libcxx-build-and-test.yaml
+++ b/.github/workflows/libcxx-build-and-test.yaml
@@ -36,7 +36,7 @@ concurrency:
 jobs:
   stage1:
     if: github.repository_owner == 'llvm'
-    runs-on: llvm-premerge-libcxx-runners
+    runs-on: llvm-premerge-libcxx-release-runners
     continue-on-error: false
     strategy:
       fail-fast: false
@@ -79,7 +79,7 @@ jobs:
             **/crash_diagnostics/*
   stage2:
     if: github.repository_owner == 'llvm'
-    runs-on: llvm-premerge-libcxx-runners
+    runs-on: llvm-premerge-libcxx-release-runners
     needs: [ stage1 ]
     continue-on-error: false
     strategy:
@@ -130,7 +130,7 @@ jobs:
             **/crash_diagnostics/*
   stage3:
     if: github.repository_owner == 'llvm'
-    runs-on: llvm-premerge-libcxx-runners
+    runs-on: llvm-premerge-libcxx-release-runners
     needs: [ stage2 ]
     continue-on-error: false
     strategy:

>From 83cbf70cb201f58cb174039d7c3792bba6d3f95a Mon Sep 17 00:00:00 2001
From: Tom Stellard <tstellar at redhat.com>
Date: Tue, 21 Jul 2026 10:03:12 -0700
Subject: [PATCH 33/35] workflows/release-documentation: Fix typo (#210815)

GITHUB_OUT -> GITHUB_OUTPUT

(cherry picked from commit d53ebd23aabd9286075650b663ff0bcbbef9ebd8)
---
 .github/workflows/release-documentation.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/release-documentation.yml b/.github/workflows/release-documentation.yml
index 12e6a3844660c..b187ce4f2e738 100644
--- a/.github/workflows/release-documentation.yml
+++ b/.github/workflows/release-documentation.yml
@@ -100,7 +100,7 @@ jobs:
             echo "man-page-ref=llvmorg-$INPUTS_RELEASE_VERSION"
             echo "man-page-upload=$UPLOAD_MAN_PAGES"
             echo "man-page-attestation-name=$RUNNER_OS-$RUNNER_ARCH-release-man-page-attestation"
-          } >> "$GITHUB_OUT"
+          } >> "$GITHUB_OUTPUT"
 
       - name: Checkout LLVM
         uses: actions/checkout at de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -141,7 +141,7 @@ jobs:
         env:
           TARBALL_NAME: ${{ steps.vars.outputs.man-page-tarball-name }}
         run: |
-            echo "man-page-digest=$(cat "$TARBALL_NAME" | sha256sum | cut -d ' ' -f 1)" >> $GITHUB_OUT
+            echo "man-page-digest=$(cat "$TARBALL_NAME" | sha256sum | cut -d ' ' -f 1)" >> $GITHUB_OUTPUT
 
       - id: man-page-artifact-upload
         uses: actions/upload-artifact at 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1

>From cd2166a355896aa608aae9487285c8b0b48da724 Mon Sep 17 00:00:00 2001
From: Ajay Wakodikar <ajaywakodikarsocial at gmail.com>
Date: Sat, 13 Jun 2026 12:49:23 -0400
Subject: [PATCH 34/35] [Sema] Fix assertion in TreeTransform when rebuilding
 CXXParenListInitExpr

---
 clang/include/clang/AST/ExprCXX.h                   |  4 ++++
 clang/lib/Sema/TreeTransform.h                      |  3 ++-
 .../instantiate-member-initializers.cpp             | 13 +++++++++++++
 3 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 757f2137c90dc..ae69d152c1a23 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5184,6 +5184,10 @@ class CXXParenListInitExpr final
 
   ArrayRef<Expr *> getInitExprs() const { return getTrailingObjects(NumExprs); }
 
+  MutableArrayRef<Expr *> getUserSpecifiedInitExprs() {
+    return getTrailingObjects(NumUserSpecifiedExprs);
+  }
+
   ArrayRef<Expr *> getUserSpecifiedInitExprs() const {
     return getTrailingObjects(NumUserSpecifiedExprs);
   }
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index cc126f9000717..11cd159c0979e 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3413,7 +3413,8 @@ class TreeTransform {
 
     if (auto *PLE = dyn_cast<CXXParenListInitExpr>(Sub))
       return getSema().BuildCXXTypeConstructExpr(
-          TInfo, LParenLoc, PLE->getInitExprs(), RParenLoc, ListInitialization);
+          TInfo, LParenLoc, PLE->getUserSpecifiedInitExprs(), RParenLoc,
+          ListInitialization);
 
     return getSema().BuildCXXTypeConstructExpr(TInfo, LParenLoc,
                                                MultiExprArg(&Sub, 1), RParenLoc,
diff --git a/clang/test/SemaTemplate/instantiate-member-initializers.cpp b/clang/test/SemaTemplate/instantiate-member-initializers.cpp
index 63862063acdfe..bd84dcdecbe62 100644
--- a/clang/test/SemaTemplate/instantiate-member-initializers.cpp
+++ b/clang/test/SemaTemplate/instantiate-member-initializers.cpp
@@ -41,3 +41,16 @@ template<typename T> struct Array {
   Array() : a() {}
 };
 Array<int> s;
+
+namespace GH194986 {
+  struct S {};
+  template <typename T> struct SS { T t1; T t2; }; // expected-note {{candidate template ignored: could not match 'GH194986::SS<T>' against 'const char *'}} expected-note {{implicit deduction guide declared as 'template <typename T> SS(GH194986::SS<T>) -> GH194986::SS<T>'}} expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} expected-note {{implicit deduction guide declared as 'template <typename T> SS() -> GH194986::SS<T>'}}
+  template <class T, class... Args> T C(Args... args) { return SS("foo"); } // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'SS'}}
+  S s = C<S>();
+
+  template <class T> struct SS2 { T t1, t2; }; // expected-note {{candidate template ignored: could not match 'GH194986::SS2<T>' against 'const char *'}} expected-note {{implicit deduction guide declared as 'template <class T> SS2(GH194986::SS2<T>) -> GH194986::SS2<T>'}} expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} expected-note {{implicit deduction guide declared as 'template <class T> SS2() -> GH194986::SS2<T>'}}
+  template <class> void C2() {
+    SS2("foo"); // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'SS2'}}
+  }
+  template void C2<int>();
+};

>From 6a59b7e46eeb0b117c9ee1f37064ecc7fc4545ae Mon Sep 17 00:00:00 2001
From: Ajay Wakodikar <ajaywakodikarsocial at gmail.com>
Date: Wed, 22 Jul 2026 05:46:11 -0400
Subject: [PATCH 35/35] Backport PR #203715 to release/23.x

---
 clang/docs/ReleaseNotes.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 5216f8a97af66..7fffbd30dd1c8 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -835,6 +835,7 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
 - Fixed a case where function effect analysis (`nonblocking` etc.) did not visit a destructor invoked from a `delete` expression. (#GH184460)
 - Clang now defines the GCC-compatible predefined macros `__WCHAR_MIN__`, `__WINT_MIN__`, and `__SIG_ATOMIC_MIN__`. (#GH199678)
 - Fix a crash in addUnsizedArray due assert not verifying we have a Base before doing checks on it. (#GH44212)
+- Fixed an assertion that could occur when rebuilding parenthesized list initialization expressions during template instantiation or AST transformation.
 
 #### Bug Fixes to Compiler Builtins
 


From libcxx-commits at lists.llvm.org  Wed Jul 22 03:17:12 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Wed, 22 Jul 2026 03:17:12 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209473@github.com>
Message-ID: <6a6098a8.a1accbb4.1ace1c.b09d@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `llvm-x86_64-debian-dylib` running on `gribozavr4` while building `clang-tools-extra,clang,cmake,flang,libcxx,lld,llvm` at step 7 "test-build-unified-tree-check-llvm".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/60/builds/4390

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 7 (test-build-unified-tree-check-llvm) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SystemZ/zos-ppa2.ll' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
sed -e 's/!"MODE"/!"ascii"/' -e 's/BYTE/85/' /b/1/llvm-x86_64-debian-dylib/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll > /b/1/llvm-x86_64-debian-dylib/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: sed -e 's/!"MODE"/!"ascii"/' -e s/BYTE/85/ /b/1/llvm-x86_64-debian-dylib/llvm-project/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
# RUN: at line 2
/b/1/llvm-x86_64-debian-dylib/build/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true < /b/1/llvm-x86_64-debian-dylib/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll |  /b/1/llvm-x86_64-debian-dylib/build/bin/FileCheck /b/1/llvm-x86_64-debian-dylib/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: /b/1/llvm-x86_64-debian-dylib/build/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true
# executed command: /b/1/llvm-x86_64-debian-dylib/build/bin/FileCheck /b/1/llvm-x86_64-debian-dylib/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# .---command stderr------------
# | /b/1/llvm-x86_64-debian-dylib/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll:24:10: error: CHECK: expected string not found in input
# | ; CHECK: DC XL6'F2F3F0F0F0F0'
# |          ^
# | <stdin>:17:39: note: scanning from here
# |  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0' 
# |                                       ^
# | <stdin>:18:2: note: possible intended match here
# |  DC XL6'F2F4F0F0F0F0' 
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /b/1/llvm-x86_64-debian-dylib/build/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             .
# |             .
# |             .
# |            12:  DC XL4'00000000'  
# |            13:  DC XL1'85'  
# |            14:  DC XL1'00'  
# |            15:  DC XL2'0000'  
# |            16: L#DVS DS 0H  
# |            17:  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0'  
# | check:24'0                                          {    search range start (exclusive)
# | check:24'1                                               error: no match found in search range
# |            18:  DC XL6'F2F4F0F0F0F0'  
# | check:24'2      ?                       possible intended match
# |            19:  DC XL2'0000'  
# |            20: stdin#C CSECT  
# |            21: C_@@QPPA2 CATTR ALIGN(3),FILL(0),NOTEXECUTABLE,READONLY,RMODE(64),PART(.  
# |            22:  .&ppa2)  
# |            23: .&ppa2 XATTR LINKAGE(OS),REFERENCE(DATA),SCOPE(SECTION)  
# |             .
# |             .
...

```

</details>

https://github.com/llvm/llvm-project/pull/209473

From libcxx-commits at lists.llvm.org  Wed Jul 22 03:18:41 2026
From: libcxx-commits at lists.llvm.org (Connector Switch via libcxx-commits)
Date: Wed, 22 Jul 2026 03:18:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2502R2:
 `std::generator`: Synchronous Coroutine Generator for Ranges (PR #201854)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/201854@github.com>
Message-ID: <6a609901.1728eaf3.5e665.1423@mx.google.com>


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/201854

>From 22f5fee287889d72cb171751fdc0993197c69b6b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 22:45:16 +0800
Subject: [PATCH 1/9] implement generator

---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/Status/Cxx23Issues.csv            |   2 +-
 libcxx/docs/Status/Cxx23Papers.csv            |   4 +-
 libcxx/docs/Status/Cxx2cIssues.csv            |   8 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/generator                      | 603 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   5 +
 libcxx/include/version                        |   2 +
 libcxx/modules/std.compat.cppm.in             |   3 -
 libcxx/modules/std.cppm.in                    |   4 +-
 libcxx/modules/std/generator.inc              |   7 +-
 .../coro.generator/nodiscard.verify.cpp       |  29 +
 .../test/libcxx/transitive_includes/cxx03.csv |  21 +
 .../test/libcxx/transitive_includes/cxx11.csv |  21 +
 .../test/libcxx/transitive_includes/cxx14.csv |  21 +
 .../test/libcxx/transitive_includes/cxx17.csv |  21 +
 .../test/libcxx/transitive_includes/cxx20.csv |  16 +
 .../test/libcxx/transitive_includes/cxx23.csv |   9 +
 .../test/libcxx/transitive_includes/cxx26.csv |   9 +
 .../generator.version.compile.pass.cpp        |  65 ++
 .../version.version.compile.pass.cpp          |  30 +
 .../ranges/coro.generator/allocator.pass.cpp  | 123 ++++
 .../coro.generator/generator.compile.pass.cpp |  48 ++
 .../ranges/coro.generator/generator.pass.cpp  |  56 ++
 .../ranges/coro.generator/recursive.pass.cpp  |  97 +++
 .../generate_feature_test_macro_components.py |   5 +
 libcxx/utils/libcxx/header_information.py     |   1 -
 27 files changed, 1198 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/include/generator
 create mode 100644 libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/generator.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/recursive.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 1b748e37293df..46d83644ff10f 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -344,6 +344,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_forward_like``                                 ``202207L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_generator``                                    ``202207L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_invoke_r``                                     ``202106L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ios_noreplace``                                ``202207L``
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0a2f0d59484de..b61edbd0a2975 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -217,7 +217,7 @@
 "`LWG3759 <https://wg21.link/LWG3759>`__","``ranges::rotate_copy`` should use ``std::move``","2022-11 (Kona)","|Complete|","15","`#105027 <https://github.com/llvm/llvm-project/issues/105027>`__",""
 "`LWG3760 <https://wg21.link/LWG3760>`__","``cartesian_product_view::iterator``'s ``parent_`` is never valid","2022-11 (Kona)","","","`#105028 <https://github.com/llvm/llvm-project/issues/105028>`__",""
 "`LWG3761 <https://wg21.link/LWG3761>`__","``cartesian_product_view::iterator::operator-`` should pass by reference","2022-11 (Kona)","","","`#105029 <https://github.com/llvm/llvm-project/issues/105029>`__",""
-"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","","","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
+"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","23","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
 "`LWG3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept","2022-11 (Kona)","|Complete|","17","`#105031 <https://github.com/llvm/llvm-project/issues/105031>`__",""
 "`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","","","`#105032 <https://github.com/llvm/llvm-project/issues/105032>`__",""
 "`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","","`#105033 <https://github.com/llvm/llvm-project/issues/105033>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index eb580ea891f5b..862461f900dbd 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -80,7 +80,7 @@
 "`P2474R2 <https://wg21.link/P2474R2>`__","``views::repeat``","2022-07 (Virtual)","|Complete|","17","`#105222 <https://github.com/llvm/llvm-project/issues/105222>`__",""
 "`P2494R2 <https://wg21.link/P2494R2>`__","Relaxing range adaptors to allow for move only types","2022-07 (Virtual)","|Complete|","17","`#105224 <https://github.com/llvm/llvm-project/issues/105224>`__",""
 "`P2499R0 <https://wg21.link/P2499R0>`__","``string_view`` range constructor should be ``explicit``","2022-07 (Virtual)","|Complete|","16","`#105225 <https://github.com/llvm/llvm-project/issues/105225>`__",""
-"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","","","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
+"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","23","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
 "`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","2022-07 (Virtual)","|Complete|","15","`#105227 <https://github.com/llvm/llvm-project/issues/105227>`__",""
 "`P2517R1 <https://wg21.link/P2517R1>`__","Add a conditional ``noexcept`` specification to ``std::apply``","2022-07 (Virtual)","|Complete|","3.9","`#105229 <https://github.com/llvm/llvm-project/issues/105229>`__",""
 "`P2520R0 <https://wg21.link/P2520R0>`__","``move_iterator`` should be a random access iterator","2022-07 (Virtual)","|Complete|","17","`#105230 <https://github.com/llvm/llvm-project/issues/105230>`__","Implemented as a DR in C++20"
@@ -115,7 +115,7 @@
 "`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20","`#105259 <https://github.com/llvm/llvm-project/issues/105259>`__",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","`#105260 <https://github.com/llvm/llvm-project/issues/105260>`__","The paper is implemented as a DR to C++20"
 "`P2652R2 <https://wg21.link/P2652R2>`__","Disallow User Specialization of ``allocator_traits``","2023-02 (Issaquah)","|Complete|","19","`#105262 <https://github.com/llvm/llvm-project/issues/105262>`__",""
-"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","","","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
+"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","23","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
 "`P2614R2 <https://wg21.link/P2614R2>`__","Deprecate ``numeric_limits::has_denorm``","2023-02 (Issaquah)","|Complete|","18","`#105264 <https://github.com/llvm/llvm-project/issues/105264>`__",""
 "`P2588R3 <https://wg21.link/P2588R3>`__","``barrier``’s phase completion guarantees","2023-02 (Issaquah)","","","`#105265 <https://github.com/llvm/llvm-project/issues/105265>`__",""
 "`P2763R1 <https://wg21.link/P2763R1>`__","``layout_stride`` static extents default constructor fix","2023-02 (Issaquah)","","","`#105266 <https://github.com/llvm/llvm-project/issues/105266>`__",""
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 6a836c0491bb6..3ec8ae821ed5d 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -4,7 +4,7 @@
 "`LWG3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","2023-06 (Varna)","|Nothing To Do|","","`#105270 <https://github.com/llvm/llvm-project/issues/105270>`__",""
 "`LWG3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","2023-06 (Varna)","|Complete|","19","`#105271 <https://github.com/llvm/llvm-project/issues/105271>`__",""
 "`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","","","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
-"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","","","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
+"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","23","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
 "`LWG3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 <https://github.com/llvm/llvm-project/issues/105275>`__",""
 "`LWG3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 <https://github.com/llvm/llvm-project/issues/105276>`__",""
 "`LWG3905 <https://wg21.link/LWG3905>`__","Type of ``std::fexcept_t``","2023-06 (Varna)","|Complete|","3.4","`#105277 <https://github.com/llvm/llvm-project/issues/105277>`__",""
@@ -79,8 +79,8 @@
 "`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","","`#118332 <https://github.com/llvm/llvm-project/issues/118332>`__",""
 "`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","","`#118335 <https://github.com/llvm/llvm-project/issues/118335>`__",""
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
-"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
-"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
+"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","23","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
+"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","|Complete|","23","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
 "`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
@@ -93,7 +93,7 @@
 "`LWG4088 <https://wg21.link/LWG4088>`__","``println`` ignores the locale imbued in ``std::ostream``","2024-11 (Wrocław)","|Complete|","18","`#118348 <https://github.com/llvm/llvm-project/issues/118348>`__",""
 "`LWG4112 <https://wg21.link/LWG4112>`__","``has-arrow`` should required ``operator->()`` to be ``const``-qualified","2024-11 (Wrocław)","","","`#118349 <https://github.com/llvm/llvm-project/issues/118349>`__",""
 "`LWG4113 <https://wg21.link/LWG4113>`__","Disallow ``has_unique_object_representations<Incomplete[]>``","2024-11 (Wrocław)","|Complete|","","`#118350 <https://github.com/llvm/llvm-project/issues/118350>`__",""
-"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","","","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
+"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","23","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
 "`LWG4124 <https://wg21.link/LWG4124>`__","Cannot format ``zoned_time`` with resolution coarser than ``seconds``","2024-11 (Wrocław)","","","`#118352 <https://github.com/llvm/llvm-project/issues/118352>`__",""
 "`LWG4126 <https://wg21.link/LWG4126>`__","Some feature-test macros for fully freestanding features are not yet marked freestanding","2024-11 (Wrocław)","","","`#118353 <https://github.com/llvm/llvm-project/issues/118353>`__",""
 "`LWG4134 <https://wg21.link/LWG4134>`__","Issue with Philox algorithm specification","2024-11 (Wrocław)","","","`#118354 <https://github.com/llvm/llvm-project/issues/118354>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..cd77d51924571 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1043,6 +1043,7 @@ set(files
   fstream
   functional
   future
+  generator
   initializer_list
   iomanip
   ios
diff --git a/libcxx/include/generator b/libcxx/include/generator
new file mode 100644
index 0000000000000..d3f942b089002
--- /dev/null
+++ b/libcxx/include/generator
@@ -0,0 +1,603 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_GENERATOR
+#define _LIBCPP_GENERATOR
+
+/*
+
+namespace std {
+  // [coro.generator.class], class template generator
+  template<class Ref, class Val = void, class Allocator = void>
+    class generator;
+
+  namespace pmr {
+    template<class Ref, class Val = void>
+      using generator = std::generator<Ref, Val, polymorphic_allocator<>>;
+  }
+}
+
+*/
+
+#include <__algorithm/max.h>
+#include <__assert>
+#include <__concepts/common_reference_with.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/same_as.h>
+#include <__config>
+#include <__coroutine/coroutine_handle.h>
+#include <__coroutine/coroutine_traits.h>
+#include <__coroutine/noop_coroutine_handle.h>
+#include <__coroutine/trivial_awaitables.h>
+#include <__exception/exception_ptr.h>
+#include <__iterator/default_sentinel.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_arg_t.h>
+#include <__memory/allocator_traits.h>
+#include <__memory/construct_at.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/elements_of.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/common_reference.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/exchange.h>
+#include <__utility/move.h>
+#include <__utility/swap.h>
+#include <cstdint>
+#include <new>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 23
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Ref, class _Val>
+using __gen_val = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
+
+template <class _Ref, class _Val>
+using __gen_ref = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
+
+template <class _Ref, class _Val>
+using __gen_yielded =
+    conditional_t<is_reference_v<__gen_ref<_Ref, _Val>>, __gen_ref<_Ref, _Val>, const __gen_ref<_Ref, _Val>&>;
+
+template <class, class>
+struct __gen_iter_provider {
+  class __iterator;
+};
+
+template <class, class, class>
+class generator;
+
+template <class _Yielded>
+class __gen_promise_base {
+private:
+  template <class, class, class>
+  friend class generator;
+
+  template <class, class>
+  friend struct __gen_iter_provider;
+
+  // Each promise object stores either a `__root_data` when associated with a root generator, or a `__recursive_data`
+  // when associated with a generator that is yielded recursively.
+  struct __root_data {
+    // The client code has access only to the iterator of the root generator. Thus, the root generator must store the
+    // yielded values of recursively-yielded generators, which will then be returned when the client code dereferences
+    // the iterator.
+    add_pointer_t<_Yielded> __value_ptr;
+    // The client code has access only to the iterator of the root generator. Thus, the root generator needs to identify
+    // which generator is currently active. This active generator will then be resumed when the client code increments
+    // the iterator.
+    coroutine_handle<__gen_promise_base> __active;
+  };
+
+  struct __recursive_data {
+    exception_ptr __exception;
+    coroutine_handle<__gen_promise_base> __parent;
+    coroutine_handle<__gen_promise_base> __root;
+  };
+
+  union __union {
+    __root_data __root;
+    __recursive_data __recursive;
+
+    _LIBCPP_HIDE_FROM_ABI ~__union() noexcept {}
+  } __data_;
+
+  // The `__tag_` stores the active member of the `__data_` union:
+  // - `false` indicates that the active member is `__root`.
+  // - `true` indicates that the active member is `__recursive`.
+  // This field can be omitted because `__recursive_data.__root` can store the active member.
+  bool __tag_;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_root() noexcept { return !__tag_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __root_data& __get_root_data() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __data_.__root;
+  }
+  _LIBCPP_HIDE_FROM_ABI void __set_root_tag() noexcept { __tag_ = false; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_recursive() noexcept { return __tag_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __recursive_data& __get_recursive_data() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_recursive(), "the active member of `__data_` is not `__recursive`");
+    return __data_.__recursive;
+  }
+  _LIBCPP_HIDE_FROM_ABI void __set_recursive_tag() noexcept { __tag_ = true; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI coroutine_handle<__gen_promise_base>& __active() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __get_root_data().__active;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __value_ptr() noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+    return __get_root_data().__value_ptr;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __root_value_ptr() noexcept {
+    if (__is_root()) {
+      return __value_ptr();
+    }
+    return __get_recursive_data().__root.promise().__value_ptr();
+  }
+
+  struct __element_awaiter {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI void await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      __current.promise().__root_value_ptr() = std::addressof(__value);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+
+    remove_cvref_t<_Yielded> __value;
+  };
+
+  struct __final_awaiter {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI coroutine_handle<> await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      // Checks if the current generator is recursively-yielded
+      if (__current.promise().__is_recursive()) {
+        auto&& __recursive_data = __current.promise().__get_recursive_data();
+        auto __parent           = __recursive_data.__parent;
+        // Updates the active generator to its parent, allowing the client code to resume it later
+        __recursive_data.__root.promise().__active() = __parent;
+        // Transfers execution to its parent, which is the generator that yields it
+        return __parent;
+      }
+      return std::noop_coroutine();
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+  };
+
+  template <class _Ref, class _Val, class _Allocator>
+  struct __recursive_awaiter {
+    _LIBCPP_HIDE_FROM_ABI explicit __recursive_awaiter(generator<_Ref, _Val, _Allocator>&& __gen) noexcept
+        : __gen_{std::move(__gen)} {}
+
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return !__gen_.__coroutine_; }
+
+    template <class _Promise>
+    _LIBCPP_HIDE_FROM_ABI coroutine_handle<__gen_promise_base>
+    await_suspend(coroutine_handle<_Promise> __current) noexcept {
+      // Stores a `__recursive_data` in the promise object associated with `__gen`
+      auto __recursive = coroutine_handle<__gen_promise_base>::from_address(__gen_.__coroutine_.address());
+      __recursive.promise().__set_recursive_tag();
+      auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+
+      // Sets `__recursive_data.__parent` to the current generator
+      auto __parent             = coroutine_handle<__gen_promise_base>::from_address(__current.address());
+      __recursive_data.__parent = __parent;
+
+      // Sets `__recursive_data.__root` to the current generator if it's a root generator, or to the root generator of
+      // the current generator otherwise
+      if (__parent.promise().__is_recursive()) {
+        __recursive_data.__root = __parent.promise().__get_recursive_data().__root;
+      } else {
+        __recursive_data.__root = __parent;
+      }
+
+      // Updates the active generator to `__gen`, allowing the client code to resume it later
+      __recursive_data.__root.promise().__active() = __recursive;
+
+      // Transfers execution to `__gen`
+      return __recursive;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI void await_resume() {
+      auto __recursive        = coroutine_handle<__gen_promise_base>::from_address(__gen_.__coroutine_.address());
+      auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+      if (__recursive_data.__exception) {
+        std::rethrow_exception(std::move(__recursive_data.__exception));
+      }
+    }
+
+  private:
+    generator<_Ref, _Val, _Allocator> __gen_;
+  };
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __gen_promise_base() noexcept
+      : __data_{.__root{
+            .__value_ptr{nullptr},
+            .__active{coroutine_handle<__gen_promise_base>::from_promise(*this)},
+        }},
+        __tag_{false} {}
+
+  _LIBCPP_HIDE_FROM_ABI ~__gen_promise_base() noexcept {
+    if (__is_root()) {
+      __data_.__root.~__root_data();
+    } else {
+      __data_.__recursive.~__recursive_data();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI suspend_always initial_suspend() const noexcept { return {}; }
+
+  _LIBCPP_HIDE_FROM_ABI __final_awaiter final_suspend() noexcept { return {}; }
+
+  _LIBCPP_HIDE_FROM_ABI suspend_always yield_value(_Yielded __value) noexcept {
+    __root_value_ptr() = std::addressof(__value);
+    return {};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI auto yield_value(const remove_reference_t<_Yielded>& __value) noexcept(
+      is_nothrow_constructible_v<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>)
+    requires is_rvalue_reference_v<_Yielded> &&
+             constructible_from<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>
+  {
+    return __element_awaiter{.__value{__value}};
+  }
+
+  template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
+    requires same_as<__gen_yielded<_Ref2, _Val2>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto
+  yield_value(ranges::elements_of<generator<_Ref2, _Val2, _Allocator2>&&, _Unused> __elements) noexcept {
+    return __recursive_awaiter<_Ref2, _Val2, _Allocator2>{std::move(__elements.range)};
+  }
+
+  template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
+    requires same_as<__gen_yielded<_Ref2, _Val2>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto
+  yield_value(ranges::elements_of<generator<_Ref2, _Val2, _Allocator2>&, _Unused> __elements) noexcept {
+    return __recursive_awaiter<_Ref2, _Val2, _Allocator2>{std::move(__elements.range)};
+  }
+
+  template <ranges::input_range _Range, class _Allocator>
+    requires convertible_to<ranges::range_reference_t<_Range>, _Yielded>
+  _LIBCPP_HIDE_FROM_ABI auto yield_value(ranges::elements_of<_Range, _Allocator> __range) {
+    auto __lambda =
+        [](allocator_arg_t, _Allocator, ranges::iterator_t<_Range> __i, ranges::sentinel_t<_Range> __s) static
+        -> generator<_Yielded, void, _Allocator> {
+      for (; __i != __s; ++__i) {
+        co_yield static_cast<_Yielded>(*__i);
+      }
+    };
+    return yield_value(ranges::elements_of(
+        __lambda(allocator_arg, __range.allocator, ranges::begin(__range.range), ranges::end(__range.range))));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void await_transform() = delete;
+
+  _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
+
+  _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
+#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+    if (__is_root()) {
+      throw;
+    } else {
+      __get_recursive_data().__exception = std::current_exception();
+    }
+#  endif
+  }
+};
+
+struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) __gen_aligned_block {
+  char __data[__STDCPP_DEFAULT_NEW_ALIGNMENT__];
+
+  _LIBCPP_HIDE_FROM_ABI static size_t __count(const size_t __size) noexcept {
+    return (__size + sizeof(__gen_aligned_block) - 1) / sizeof(__gen_aligned_block);
+  }
+};
+
+template <class _Allocator>
+concept __gen_stateless_allocator =
+    default_initializable<_Allocator> && allocator_traits<_Allocator>::is_always_equal::value;
+
+template <class _Allocator>
+class __gen_promise_allocator {
+private:
+  using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+  using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+  using __rebind_pointer = allocator_traits<__rebind>::pointer;
+  static_assert(is_pointer_v<__rebind_pointer>);
+
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+    __rebind __rebind_al = static_cast<__rebind>(__al);
+
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      return __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, and the alignment of the
+      // stateful allocator
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+      void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+      return __ptr;
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size)
+    requires default_initializable<_Allocator>
+  {
+    return __alloc({}, __size);
+  }
+
+  template <class _Allocator2, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<_Allocator>(__al), __size);
+  }
+
+  template <class _This, class _Allocator2, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<_Allocator>(__al), __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      __rebind __rebind_al;
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      __rebind __rebind_al = std::move(*__rebind_al_ptr);
+      std::destroy_at(__rebind_al_ptr);
+
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+};
+
+template <>
+class __gen_promise_allocator<void> {
+private:
+  using __dealloc_fn = void (*)(void*, size_t) noexcept;
+
+  template <class _Allocator>
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+    using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+    using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+    using __rebind_pointer = allocator_traits<__rebind>::pointer;
+    static_assert(is_pointer_v<__rebind_pointer>);
+
+    __rebind __rebind_al = static_cast<__rebind>(__al);
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+      void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the deallocation function in the allocated block
+      __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr                    = __dealloc<__rebind>;
+      return __ptr;
+    } else {
+      // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, the alignment of the
+      // stateful allocator, and the deallocation function
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(__rebind) + alignof(__rebind));
+      void* const __ptr = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the deallocation function in the allocated block
+      __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr                    = __dealloc<__rebind>;
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + sizeof(__dealloc_fn) + __size + alignof(__rebind) - 1) &
+          ~(alignof(__rebind) - 1));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+      return __ptr;
+    }
+  }
+
+  template <class _Rebind>
+  _LIBCPP_HIDE_FROM_ABI static void __dealloc(void* const __ptr, size_t __size) noexcept {
+    using __rebind_size_type = allocator_traits<_Rebind>::size_type;
+
+    if constexpr (__gen_stateless_allocator<_Rebind>) {
+      _Rebind __rebind_al;
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      _Rebind* const __rebind_al_ptr = reinterpret_cast<_Rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + sizeof(__dealloc_fn) + alignof(_Rebind) - 1) &
+          ~(alignof(_Rebind) - 1));
+      _Rebind __rebind_al = std::move(*__rebind_al_ptr);
+      std::destroy_at(__rebind_al_ptr);
+
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(_Rebind) + alignof(_Rebind));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size) {
+    void* const __ptr      = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
+      ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+    };
+
+    __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    *__dealloc_ptr                    = __dealloc;
+    return __ptr;
+  }
+
+  template <class _Allocator, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+    return __alloc(__al, __size);
+  }
+
+  template <class _This, class _Allocator, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+    return __alloc(__al, __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
+    __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    const __dealloc_fn __dealloc      = *__dealloc_ptr;
+    __dealloc(__ptr, __size);
+  }
+};
+
+template <class _Ref, class _Val>
+class __gen_iter_provider<_Ref, _Val>::__iterator {
+private:
+  using __val = __gen_val<_Ref, _Val>;
+  using __ref = __gen_ref<_Ref, _Val>;
+
+public:
+  using value_type      = __val;
+  using difference_type = ptrdiff_t;
+
+  _LIBCPP_HIDE_FROM_ABI explicit __iterator(
+      coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine) noexcept
+      : __coroutine_{__coroutine} {}
+
+  _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&& __other) noexcept
+      : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator=(__iterator&& __other) noexcept {
+    __coroutine_ = std::exchange(__other.__coroutine_, {});
+    return *this;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __ref operator*() const noexcept(is_nothrow_copy_constructible_v<__ref>) {
+    return static_cast<__ref>(*__coroutine_.promise().__value_ptr());
+  }
+
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator++() {
+    __coroutine_.promise().__active().resume();
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend bool operator==(const __iterator& __iter, default_sentinel_t) noexcept {
+    return __iter.__coroutine_.done();
+  }
+
+private:
+  coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine_;
+};
+
+template <class _Ref, class _Val = void, class _Allocator = void>
+class generator : public ranges::view_interface<generator<_Ref, _Val, _Allocator>> {
+private:
+  using __val = __gen_val<_Ref, _Val>;
+  static_assert(same_as<remove_cvref_t<__val>, __val> && is_object_v<__val>);
+
+  using __ref = __gen_ref<_Ref, _Val>;
+  static_assert(is_reference_v<__ref> ||
+                (same_as<remove_cvref_t<__ref>, __ref> && is_object_v<__ref> && copy_constructible<__ref>));
+
+  using __rref = conditional_t<is_lvalue_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
+  static_assert(common_reference_with<__ref&&, __val&> && common_reference_with<__ref&&, __rref&&> &&
+                common_reference_with<__rref&&, const __val&>);
+
+  template <class>
+  friend class __gen_promise_base;
+
+public:
+  using yielded = __gen_yielded<_Ref, _Val>;
+
+  struct promise_type : __gen_promise_base<yielded>, __gen_promise_allocator<_Allocator> {
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI generator get_return_object() noexcept {
+      return generator{coroutine_handle<promise_type>::from_promise(*this)};
+    }
+  };
+
+  _LIBCPP_HIDE_FROM_ABI generator(const generator&) = delete;
+  _LIBCPP_HIDE_FROM_ABI generator(generator&& __other) noexcept
+      : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+  _LIBCPP_HIDE_FROM_ABI ~generator() {
+    if (__coroutine_) {
+      __coroutine_.destroy();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI generator& operator=(generator __other) noexcept {
+    std::swap(__coroutine_, __other.__coroutine_);
+    return *this;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __gen_iter_provider<_Ref, _Val>::__iterator begin() {
+    auto __coroutine = coroutine_handle<__gen_promise_base<yielded>>::from_promise(__coroutine_.promise());
+    __coroutine.resume();
+    return typename __gen_iter_provider<_Ref, _Val>::__iterator{__coroutine};
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI default_sentinel_t end() const noexcept { return {}; }
+
+private:
+  _LIBCPP_HIDE_FROM_ABI explicit generator(coroutine_handle<promise_type> __coroutine) noexcept
+      : __coroutine_{__coroutine} {}
+
+  coroutine_handle<promise_type> __coroutine_;
+};
+
+namespace pmr {
+
+template <class _Ref, class _Val = void>
+using generator = std::generator<_Ref, _Val, polymorphic_allocator<>>;
+
+} // namespace pmr
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_GENERATOR
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..efb97a047c6e6 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1476,6 +1476,11 @@ module std [system] {
     export *
   }
 
+  module generator {
+    header "generator"
+    export *
+  }
+
   module iomanip {
     header "iomanip"
     export *
diff --git a/libcxx/include/version b/libcxx/include/version
index d4d29b4d31a72..8a73c95551793 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -133,6 +133,7 @@ __cpp_lib_fstream_native_handle                         202306L <fstream>
 __cpp_lib_function_ref                                  202306L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
+__cpp_lib_generator                                     202207L <generator>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
 __cpp_lib_generic_unordered_lookup                      201811L <unordered_map> <unordered_set>
 __cpp_lib_hardware_interference_size                    201703L <new>
@@ -507,6 +508,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_format_ranges                        202207L
 // # define __cpp_lib_formatters                           202302L
 # define __cpp_lib_forward_like                         202207L
+# define __cpp_lib_generator                            202207L
 # define __cpp_lib_invoke_r                             202106L
 # define __cpp_lib_ios_noreplace                        202207L
 # if __has_builtin(__builtin_is_implicit_lifetime)
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index dd7385bf33a42..dada8f56f0224 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -51,9 +51,6 @@ module;
 #  if __has_include(<debugging>)
 #    error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<debugging>)
-#  if __has_include(<generator>)
-#    error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<generator>)
 #  if __has_include(<hazard_pointer>)
 #    error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<hazard_pointer>)
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index 984b18321923c..0c484219c31ce 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -67,6 +67,7 @@ module;
 #include <fstream>
 #include <functional>
 #include <future>
+#include <generator>
 #include <initializer_list>
 #include <iomanip>
 #include <ios>
@@ -135,9 +136,6 @@ module;
 #  if __has_include(<debugging>)
 #    error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<debugging>)
-#  if __has_include(<generator>)
-#    error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<generator>)
 #  if __has_include(<hazard_pointer>)
 #    error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<hazard_pointer>)
diff --git a/libcxx/modules/std/generator.inc b/libcxx/modules/std/generator.inc
index 43fb0daf5c659..8681f4b26dc46 100644
--- a/libcxx/modules/std/generator.inc
+++ b/libcxx/modules/std/generator.inc
@@ -8,7 +8,12 @@
 //===----------------------------------------------------------------------===//
 
 export namespace std {
-#if 0
+#if _LIBCPP_STD_VER >= 23
+  // [coro.generator], class template generator
   using std::generator;
+
+  namespace pmr {
+    using std::pmr::generator;
+  }
 #endif
 } // namespace std
diff --git a/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..9fb717bf9c148
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <generator>
+
+std::generator<int> generate_ints() {
+  co_yield 1;
+  co_yield 2;
+  co_yield 3;
+}
+
+void test() {
+  auto gen = generate_ints();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  gen.begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  gen.end();
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c0031543e47bc..5754f5961dd09 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -945,6 +949,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2032,6 +2052,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c0031543e47bc..5754f5961dd09 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -945,6 +949,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2032,6 +2052,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c2eb5b44e8d7a..c31b86c5f8739 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,15 +125,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -968,6 +972,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2072,6 +2092,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 332cb62f35b5f..50f2d9cf2ac5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -971,6 +975,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2085,6 +2105,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 55c79acff5a8f..0d23c92ec9a1d 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -965,6 +965,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 073f698786117..2fde541964065 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -466,6 +466,15 @@ future string_view
 future tuple
 future typeinfo
 future version
+generator compare
+generator cstdint
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator tuple
+generator typeinfo
+generator version
 initializer_list version
 iomanip cctype
 iomanip cerrno
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index d531d60ae3e35..5fde2907b3327 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -443,6 +443,15 @@ future string_view
 future tuple
 future typeinfo
 future version
+generator compare
+generator cstdint
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator tuple
+generator typeinfo
+generator version
 initializer_list version
 iomanip cctype
 iomanip cerrno
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
new file mode 100644
index 0000000000000..0726f4e59a553
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <generator>
+
+// Test the feature test macros defined by <generator>
+
+// clang-format off
+
+#include <generator>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++23"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++23"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++26"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++26"
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
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 20c16700ef76a..32befec34ed09 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
@@ -412,6 +412,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should not be defined before c++14"
 #  endif
@@ -1351,6 +1355,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++14"
 #  endif
@@ -2413,6 +2421,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++17"
 #  endif
@@ -3742,6 +3754,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++20"
 #  endif
@@ -5266,6 +5282,13 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++23"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++23"
 #  endif
@@ -7144,6 +7167,13 @@
 #    endif
 #  endif
 
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++26"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp b/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
new file mode 100644
index 0000000000000..96c2211ed3138
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <cstddef>
+#include <memory>
+#include <memory_resource>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+template <class T>
+class stateless_allocator {
+public:
+  using value_type = T;
+
+  stateless_allocator() noexcept = default;
+
+  template <typename U>
+  constexpr stateless_allocator(const stateless_allocator<U>&) noexcept {}
+
+  [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
+
+  void deallocate(void* p, size_t) noexcept { ::operator delete(p); }
+
+  template <class U>
+  constexpr bool operator==(const stateless_allocator<U>&) const noexcept {
+    return true;
+  }
+};
+
+template <class T>
+class stateful_allocator {
+public:
+  using value_type = T;
+
+  stateful_allocator() noexcept = default;
+
+  template <typename U>
+  constexpr stateful_allocator(const stateful_allocator<U>&) noexcept {}
+
+  [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
+
+  void deallocate(void* p, size_t) noexcept { ::operator delete(p); }
+
+  template <class U>
+  constexpr bool operator==(const stateful_allocator<U>& other) const noexcept {
+    return state_ == other.state_;
+  }
+
+private:
+  int state_ = 0;
+};
+
+template <class Allocator>
+bool test_with_allocator() {
+  std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+  {
+    auto fib = []() -> std::generator<int, int, Allocator> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib() | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+
+  {
+    auto fib = [](std::allocator_arg_t, Allocator) -> std::generator<int, int, Allocator> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib(std::allocator_arg, {}) | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+
+  {
+    auto fib = [](std::allocator_arg_t, Allocator) -> std::generator<int> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib(std::allocator_arg, {}) | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+  return true;
+}
+
+bool test() {
+  test_with_allocator<std::allocator<std::byte>>();
+  test_with_allocator<stateless_allocator<std::byte>>();
+  test_with_allocator<stateful_allocator<std::byte>>();
+  test_with_allocator<std::pmr::polymorphic_allocator<std::byte>>();
+
+  test_with_allocator<std::allocator<float>>();
+  test_with_allocator<stateless_allocator<float>>();
+  test_with_allocator<stateful_allocator<float>>();
+  test_with_allocator<std::pmr::polymorphic_allocator<float>>();
+  return true;
+};
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
new file mode 100644
index 0000000000000..769a56021c32b
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+template <class G, class V, class R, class RR>
+constexpr bool conformance() {
+  static_assert(std::ranges::range<G>);
+  static_assert(std::ranges::view<G>);
+  static_assert(std::ranges::input_range<G>);
+  static_assert(!std::ranges::forward_range<G>);
+  static_assert(!std::ranges::borrowed_range<G>);
+
+  static_assert(std::same_as<std::ranges::range_value_t<G>, V>);
+  static_assert(std::same_as<std::ranges::range_reference_t<G>, R>);
+  static_assert(std::same_as<std::ranges::range_rvalue_reference_t<G>, RR>);
+
+  return true;
+}
+
+static_assert(conformance<std::generator<int>, int, int&&, int&&>());
+static_assert(conformance<std::generator<int, int>, int, int, int>());
+
+static_assert(conformance<std::generator<int&>, int, int&, int&&>());
+static_assert(conformance<std::generator<int&, int>, int, int&, int&&>());
+static_assert(conformance<std::generator<const int&>, int, const int&, const int&&>());
+static_assert(conformance<std::generator<const int&, int>, int, const int&, const int&&>());
+
+static_assert(conformance<std::generator<int&&>, int, int&&, int&&>());
+static_assert(conformance<std::generator<int&&, int>, int, int&&, int&&>());
+static_assert(conformance<std::generator<const int&&>, int, const int&&, const int&&>());
+static_assert(conformance<std::generator<const int&&, int>, int, const int&&, const int&&>());
diff --git a/libcxx/test/std/ranges/coro.generator/generator.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
new file mode 100644
index 0000000000000..ed1793382962a
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+std::generator<int> fib() {
+  int a = 0;
+  int b = 1;
+  while (true) {
+    co_yield std::exchange(a, std::exchange(b, a + b));
+  }
+}
+
+std::generator<const int&> range_fib() {
+  co_yield std::ranges::elements_of(std::vector<int>{0, 1});
+  co_yield std::ranges::elements_of(std::vector<int>{1, 2});
+  co_yield std::ranges::elements_of(std::vector<int>{3, 5});
+  co_yield std::ranges::elements_of(std::vector<int>{5, 8});
+}
+
+bool test() {
+  {
+    std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+    {
+      auto fib_vec = fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+    {
+      auto fib_vec = range_fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+  }
+  return true;
+}
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
new file mode 100644
index 0000000000000..36c5b8b65e42e
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <numeric>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+std::generator<int> fib() {
+  int a = 0;
+  int b = 1;
+  while (true) {
+    co_yield std::exchange(a, std::exchange(b, a + b));
+  }
+}
+
+std::generator<int> recursive_fib(int depth) {
+  if (depth == 0) {
+    co_yield std::ranges::elements_of(fib());
+  } else {
+    co_yield std::ranges::elements_of(recursive_fib(depth - 1));
+  }
+};
+
+struct tree_node {
+  tree_node* left;
+  tree_node* right;
+  int element;
+
+  ~tree_node() {
+    delete left;
+    delete right;
+  }
+};
+
+tree_node* build_tree(int depth) {
+  if (depth == 0) {
+    return nullptr;
+  }
+
+  tree_node* root = new tree_node();
+  root->element   = depth;
+  root->left      = build_tree(depth - 1);
+  root->right     = build_tree(depth - 1);
+  return root;
+}
+
+std::generator<int> traversal(tree_node* node) {
+  if (node == nullptr) {
+    co_return;
+  }
+  co_yield std::ranges::elements_of(traversal(node->left));
+  co_yield node->element;
+  co_yield std::ranges::elements_of(traversal(node->right));
+}
+
+bool test() {
+  {
+    std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+    {
+      auto fib_vec = recursive_fib(1) | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+    {
+      auto fib_vec = recursive_fib(42) | std::views::take(5) | std::ranges::to<std::vector<int>>();
+      assert(fib_vec == expected_fib_vec);
+    }
+  }
+  {
+    tree_node* tree_root = build_tree(10);
+    auto node_vec        = traversal(tree_root) | std::ranges::to<std::vector<int>>();
+    assert(node_vec.size() == 1023);
+    assert(std::reduce(node_vec.cbegin(), node_vec.cend()) == 2036);
+    delete tree_root;
+  }
+  return true;
+}
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d8fb575b97b2f..a5db6f1f1650f 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -719,6 +719,11 @@ def add_version_header(tc):
             "headers": ["random"],
             "unimplemented": True,
         },
+        {
+            "name": "__cpp_lib_generator",
+            "values": {"c++23": 202207}, # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
+            "headers": ["generator"],
+        },
         {
             "name": "__cpp_lib_generic_associative_lookup",
             "values": {"c++14": 201304},
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index d06271a7908cc..cc33d32a69201 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -164,7 +164,6 @@ def __hash__(self) -> int:
 # modules will fail to build if a header is added but this list is not updated.
 headers_not_available = list(map(Header, [
     "debugging",
-    "generator",
     "hazard_pointer",
     "inplace_vector",
     "linalg",

>From f2ee26c784c91829799073fe12d1cbd0d281a755 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 22:52:35 +0800
Subject: [PATCH 2/9] release notes & python format

---
 libcxx/docs/ReleaseNotes/23.rst                        | 2 ++
 libcxx/utils/generate_feature_test_macro_components.py | 4 +++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 03dabc54dcf79..a53fad86004a6 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -51,6 +51,8 @@ Implemented Papers
 - P2542R8: ``views::concat`` (`Github <https://llvm.org/PR105419>`__)
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
+- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index a5db6f1f1650f..ea2e43b4c15a8 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -721,7 +721,9 @@ def add_version_header(tc):
         },
         {
             "name": "__cpp_lib_generator",
-            "values": {"c++23": 202207}, # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
+            "values": {
+                "c++23": 202207
+            },  # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
             "headers": ["generator"],
         },
         {

>From 19e5608ff21db2369e7172201d55bc372e48d67b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:27:41 +0800
Subject: [PATCH 3/9] test

---
 libcxx/include/generator | 109 +++++++++++++++++++++------------------
 1 file changed, 60 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index d3f942b089002..7792b65a6ae43 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -25,52 +25,56 @@ namespace std {
 
 */
 
-#include <__algorithm/max.h>
-#include <__assert>
-#include <__concepts/common_reference_with.h>
-#include <__concepts/constructible.h>
-#include <__concepts/convertible_to.h>
-#include <__concepts/same_as.h>
-#include <__config>
-#include <__coroutine/coroutine_handle.h>
-#include <__coroutine/coroutine_traits.h>
-#include <__coroutine/noop_coroutine_handle.h>
-#include <__coroutine/trivial_awaitables.h>
-#include <__exception/exception_ptr.h>
-#include <__iterator/default_sentinel.h>
-#include <__memory/addressof.h>
-#include <__memory/allocator_arg_t.h>
-#include <__memory/allocator_traits.h>
-#include <__memory/construct_at.h>
-#include <__memory_resource/polymorphic_allocator.h>
-#include <__ranges/access.h>
-#include <__ranges/concepts.h>
-#include <__ranges/elements_of.h>
-#include <__ranges/view_interface.h>
-#include <__type_traits/add_pointer.h>
-#include <__type_traits/common_reference.h>
-#include <__type_traits/conditional.h>
-#include <__type_traits/is_nothrow_constructible.h>
-#include <__type_traits/is_object.h>
-#include <__type_traits/is_pointer.h>
-#include <__type_traits/is_reference.h>
-#include <__type_traits/is_void.h>
-#include <__type_traits/remove_cvref.h>
-#include <__type_traits/remove_reference.h>
-#include <__utility/exchange.h>
-#include <__utility/move.h>
-#include <__utility/swap.h>
-#include <cstdint>
-#include <new>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+#  include <__cxx03/__config>
+#else
+#  include <__config>
+
+#  if _LIBCPP_STD_VER >= 23
+
+#    include <__algorithm/max.h>
+#    include <__assert>
+#    include <__concepts/common_reference_with.h>
+#    include <__concepts/constructible.h>
+#    include <__concepts/convertible_to.h>
+#    include <__concepts/same_as.h>
+#    include <__coroutine/coroutine_handle.h>
+#    include <__coroutine/coroutine_traits.h>
+#    include <__coroutine/noop_coroutine_handle.h>
+#    include <__coroutine/trivial_awaitables.h>
+#    include <__exception/exception_ptr.h>
+#    include <__iterator/default_sentinel.h>
+#    include <__memory/addressof.h>
+#    include <__memory/allocator_arg_t.h>
+#    include <__memory/allocator_traits.h>
+#    include <__memory/construct_at.h>
+#    include <__memory_resource/polymorphic_allocator.h>
+#    include <__ranges/access.h>
+#    include <__ranges/concepts.h>
+#    include <__ranges/elements_of.h>
+#    include <__ranges/view_interface.h>
+#    include <__type_traits/add_pointer.h>
+#    include <__type_traits/common_reference.h>
+#    include <__type_traits/conditional.h>
+#    include <__type_traits/is_nothrow_constructible.h>
+#    include <__type_traits/is_object.h>
+#    include <__type_traits/is_pointer.h>
+#    include <__type_traits/is_reference.h>
+#    include <__type_traits/is_void.h>
+#    include <__type_traits/remove_cvref.h>
+#    include <__type_traits/remove_reference.h>
+#    include <__utility/exchange.h>
+#    include <__utility/move.h>
+#    include <__utility/swap.h>
+#    include <cstdint>
+#    include <new>
+
+#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#      pragma GCC system_header
+#    endif
 
 _LIBCPP_PUSH_MACROS
-#include <__undef_macros>
-
-#if _LIBCPP_STD_VER >= 23
+#    include <__undef_macros>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -309,13 +313,13 @@ public:
   _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
 
   _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
-#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+#    ifndef _LIBCPP_HAS_NO_EXCEPTIONS
     if (__is_root()) {
       throw;
     } else {
       __get_recursive_data().__exception = std::current_exception();
     }
-#  endif
+#    endif
   }
 };
 
@@ -462,9 +466,14 @@ private:
 
 public:
   _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size) {
-    void* const __ptr      = ::operator new(__size + sizeof(__dealloc_fn));
-    __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
+    void* const __ptr = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc =
+        [](void* const __dealloc_ptr, [[maybe_unused]] const size_t __dealloc_size) static noexcept -> void {
+#    ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
+      ::operator delete(__dealloc_ptr);
+#    else
       ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+#    endif
     };
 
     __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
@@ -596,8 +605,10 @@ using generator = std::generator<_Ref, _Val, polymorphic_allocator<>>;
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_STD_VER >= 23
+#  endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_POP_MACROS
 
+#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
 #endif // _LIBCPP_GENERATOR

>From 57bf0686971419520f56a48a34647f302a151cec Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:48:45 +0800
Subject: [PATCH 4/9] test

---
 libcxx/include/generator | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index 7792b65a6ae43..2176ecbaa1e6e 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -30,6 +30,13 @@ namespace std {
 #else
 #  include <__config>
 
+#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#    pragma GCC system_header
+#  endif
+
+_LIBCPP_PUSH_MACROS
+#  include <__undef_macros>
+
 #  if _LIBCPP_STD_VER >= 23
 
 #    include <__algorithm/max.h>
@@ -69,13 +76,6 @@ namespace std {
 #    include <cstdint>
 #    include <new>
 
-#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#      pragma GCC system_header
-#    endif
-
-_LIBCPP_PUSH_MACROS
-#    include <__undef_macros>
-
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Ref, class _Val>
@@ -469,10 +469,10 @@ public:
     void* const __ptr = ::operator new(__size + sizeof(__dealloc_fn));
     __dealloc_fn __dealloc =
         [](void* const __dealloc_ptr, [[maybe_unused]] const size_t __dealloc_size) static noexcept -> void {
-#    ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
-      ::operator delete(__dealloc_ptr);
-#    else
+#    if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L
       ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+#    else
+      ::operator delete(__dealloc_ptr);
 #    endif
     };
 

>From d4c2ae0fccee3faeb4ed931a82cb11edc4505e2a Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:57:26 +0800
Subject: [PATCH 5/9] test

---
 libcxx/test/libcxx/transitive_includes/cxx03.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx11.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx14.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx17.csv | 16 ----------------
 libcxx/test/libcxx/transitive_includes/cxx20.csv | 16 ----------------
 5 files changed, 80 deletions(-)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 5754f5961dd09..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -949,22 +949,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 5754f5961dd09..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -949,22 +949,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c31b86c5f8739..cfcf7bc7c4c19 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -972,22 +972,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 50f2d9cf2ac5f..534776e6f0526 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -975,22 +975,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 0d23c92ec9a1d..55c79acff5a8f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -965,22 +965,6 @@ future utility
 future variant
 future vector
 future version
-generator cmath
-generator compare
-generator cstddef
-generator cstdint
-generator cstdlib
-generator cstring
-generator exception
-generator initializer_list
-generator iosfwd
-generator limits
-generator new
-generator tuple
-generator type_traits
-generator typeinfo
-generator utility
-generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm

>From 0b062c588aa75fa002ffe2d020da342947f80caf Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 8 Jun 2026 23:21:56 +0800
Subject: [PATCH 6/9] revert some transitive include change

---
 libcxx/test/libcxx/transitive_includes/cxx03.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx11.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx14.csv | 5 -----
 libcxx/test/libcxx/transitive_includes/cxx17.csv | 5 -----
 4 files changed, 20 deletions(-)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index cfcf7bc7c4c19..c2eb5b44e8d7a 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,19 +125,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2076,7 +2072,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 534776e6f0526..332cb62f35b5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2089,7 +2085,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm

>From 90c7db75736b52393c458af97b92a6dc4617ca7f Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 20:18:04 +0800
Subject: [PATCH 7/9] release notes 23 -> 24

---
 libcxx/docs/ReleaseNotes/23.rst | 5 -----
 libcxx/docs/ReleaseNotes/24.rst | 2 ++
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e18f46f6e467d..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -54,12 +54,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
-<<<<<<< HEAD
-- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
-- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
-=======
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
->>>>>>> main
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..55b39c7530f6a 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,8 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
+- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
 
 Improvements and New Features
 -----------------------------

>From 8fc0b82aeb2c202c5710c5e21a519d3ddf9003c4 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 20:22:23 +0800
Subject: [PATCH 8/9] release notes 23 -> 24

---
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 libcxx/docs/Status/Cxx23Papers.csv | 4 ++--
 libcxx/docs/Status/Cxx26Issues.csv | 8 ++++----
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index f73551117f84e..167214284e2fd 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -217,7 +217,7 @@
 "`LWG3759 <https://wg21.link/LWG3759>`__","``ranges::rotate_copy`` should use ``std::move``","2022-11 (Kona)","|Complete|","15","`#105027 <https://github.com/llvm/llvm-project/issues/105027>`__",""
 "`LWG3760 <https://wg21.link/LWG3760>`__","``cartesian_product_view::iterator``'s ``parent_`` is never valid","2022-11 (Kona)","","","`#105028 <https://github.com/llvm/llvm-project/issues/105028>`__",""
 "`LWG3761 <https://wg21.link/LWG3761>`__","``cartesian_product_view::iterator::operator-`` should pass by reference","2022-11 (Kona)","","","`#105029 <https://github.com/llvm/llvm-project/issues/105029>`__",""
-"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","23","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
+"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","24","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
 "`LWG3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept","2022-11 (Kona)","|Complete|","17","`#105031 <https://github.com/llvm/llvm-project/issues/105031>`__",""
 "`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","","","`#105032 <https://github.com/llvm/llvm-project/issues/105032>`__",""
 "`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","","`#105033 <https://github.com/llvm/llvm-project/issues/105033>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 86f1395ed12fb..b5494c4a20ea8 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -80,7 +80,7 @@
 "`P2474R2 <https://wg21.link/P2474R2>`__","``views::repeat``","2022-07 (Virtual)","|Complete|","17","`#105222 <https://github.com/llvm/llvm-project/issues/105222>`__",""
 "`P2494R2 <https://wg21.link/P2494R2>`__","Relaxing range adaptors to allow for move only types","2022-07 (Virtual)","|Complete|","17","`#105224 <https://github.com/llvm/llvm-project/issues/105224>`__",""
 "`P2499R0 <https://wg21.link/P2499R0>`__","``string_view`` range constructor should be ``explicit``","2022-07 (Virtual)","|Complete|","16","`#105225 <https://github.com/llvm/llvm-project/issues/105225>`__",""
-"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","23","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
+"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","24","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
 "`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","2022-07 (Virtual)","|Complete|","15","`#105227 <https://github.com/llvm/llvm-project/issues/105227>`__",""
 "`P2517R1 <https://wg21.link/P2517R1>`__","Add a conditional ``noexcept`` specification to ``std::apply``","2022-07 (Virtual)","|Complete|","3.9","`#105229 <https://github.com/llvm/llvm-project/issues/105229>`__",""
 "`P2520R0 <https://wg21.link/P2520R0>`__","``move_iterator`` should be a random access iterator","2022-07 (Virtual)","|Complete|","17","`#105230 <https://github.com/llvm/llvm-project/issues/105230>`__","Implemented as a DR in C++20"
@@ -115,7 +115,7 @@
 "`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20","`#105259 <https://github.com/llvm/llvm-project/issues/105259>`__",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","`#105260 <https://github.com/llvm/llvm-project/issues/105260>`__","The paper is implemented as a DR to C++20"
 "`P2652R2 <https://wg21.link/P2652R2>`__","Disallow User Specialization of ``allocator_traits``","2023-02 (Issaquah)","|Complete|","19","`#105262 <https://github.com/llvm/llvm-project/issues/105262>`__",""
-"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","23","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
+"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","24","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
 "`P2614R2 <https://wg21.link/P2614R2>`__","Deprecate ``numeric_limits::has_denorm``","2023-02 (Issaquah)","|Complete|","18","`#105264 <https://github.com/llvm/llvm-project/issues/105264>`__",""
 "`P2588R3 <https://wg21.link/P2588R3>`__","``barrier``’s phase completion guarantees","2023-02 (Issaquah)","","","`#105265 <https://github.com/llvm/llvm-project/issues/105265>`__",""
 "`P2763R1 <https://wg21.link/P2763R1>`__","``layout_stride`` static extents default constructor fix","2023-02 (Issaquah)","","","`#105266 <https://github.com/llvm/llvm-project/issues/105266>`__",""
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index e8c02fa8e1ce1..c217b32adc014 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -4,7 +4,7 @@
 "`LWG3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","2023-06 (Varna)","|Nothing To Do|","","`#105270 <https://github.com/llvm/llvm-project/issues/105270>`__",""
 "`LWG3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","2023-06 (Varna)","|Complete|","19","`#105271 <https://github.com/llvm/llvm-project/issues/105271>`__",""
 "`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","","","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
-"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","23","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
+"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","24","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
 "`LWG3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 <https://github.com/llvm/llvm-project/issues/105275>`__",""
 "`LWG3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 <https://github.com/llvm/llvm-project/issues/105276>`__",""
 "`LWG3905 <https://wg21.link/LWG3905>`__","Type of ``std::fexcept_t``","2023-06 (Varna)","|Complete|","3.4","`#105277 <https://github.com/llvm/llvm-project/issues/105277>`__",""
@@ -79,8 +79,8 @@
 "`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","","`#118332 <https://github.com/llvm/llvm-project/issues/118332>`__",""
 "`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","","`#118335 <https://github.com/llvm/llvm-project/issues/118335>`__",""
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
-"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","23","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
-"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
+"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","24","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
+"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","|Complete|","24","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
 "`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","|Complete|","23","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
@@ -93,7 +93,7 @@
 "`LWG4088 <https://wg21.link/LWG4088>`__","``println`` ignores the locale imbued in ``std::ostream``","2024-11 (Wrocław)","|Complete|","18","`#118348 <https://github.com/llvm/llvm-project/issues/118348>`__",""
 "`LWG4112 <https://wg21.link/LWG4112>`__","``has-arrow`` should required ``operator->()`` to be ``const``-qualified","2024-11 (Wrocław)","","","`#118349 <https://github.com/llvm/llvm-project/issues/118349>`__",""
 "`LWG4113 <https://wg21.link/LWG4113>`__","Disallow ``has_unique_object_representations<Incomplete[]>``","2024-11 (Wrocław)","|Complete|","","`#118350 <https://github.com/llvm/llvm-project/issues/118350>`__",""
-"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","23","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
+"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","24","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
 "`LWG4124 <https://wg21.link/LWG4124>`__","Cannot format ``zoned_time`` with resolution coarser than ``seconds``","2024-11 (Wrocław)","","","`#118352 <https://github.com/llvm/llvm-project/issues/118352>`__",""
 "`LWG4126 <https://wg21.link/LWG4126>`__","Some feature-test macros for fully freestanding features are not yet marked freestanding","2024-11 (Wrocław)","","","`#118353 <https://github.com/llvm/llvm-project/issues/118353>`__",""
 "`LWG4134 <https://wg21.link/LWG4134>`__","Issue with Philox algorithm specification","2024-11 (Wrocław)","","","`#118354 <https://github.com/llvm/llvm-project/issues/118354>`__",""

>From 3e76eaccfc277fffdfbe26383769987f5596c444 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Mon, 20 Jul 2026 22:04:55 +0800
Subject: [PATCH 9/9] fix new warnings

---
 libcxx/include/generator | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index 2176ecbaa1e6e..1a797c3798ec3 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -79,13 +79,13 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Ref, class _Val>
-using __gen_val = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
+using __gen_val _LIBCPP_NODEBUG = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
 
 template <class _Ref, class _Val>
-using __gen_ref = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
+using __gen_ref _LIBCPP_NODEBUG = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
 
 template <class _Ref, class _Val>
-using __gen_yielded =
+using __gen_yielded _LIBCPP_NODEBUG =
     conditional_t<is_reference_v<__gen_ref<_Ref, _Val>>, __gen_ref<_Ref, _Val>, const __gen_ref<_Ref, _Val>&>;
 
 template <class, class>
@@ -313,7 +313,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
 
   _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
-#    ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+#    if _LIBCPP_HAS_EXCEPTIONS
     if (__is_root()) {
       throw;
     } else {
@@ -338,10 +338,10 @@ concept __gen_stateless_allocator =
 template <class _Allocator>
 class __gen_promise_allocator {
 private:
-  using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
-  using __rebind_size_type = allocator_traits<__rebind>::size_type;
+  using __rebind _LIBCPP_NODEBUG           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+  using __rebind_size_type _LIBCPP_NODEBUG = allocator_traits<__rebind>::size_type;
 
-  using __rebind_pointer = allocator_traits<__rebind>::pointer;
+  using __rebind_pointer _LIBCPP_NODEBUG = allocator_traits<__rebind>::pointer;
   static_assert(is_pointer_v<__rebind_pointer>);
 
   _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
@@ -403,7 +403,7 @@ public:
 template <>
 class __gen_promise_allocator<void> {
 private:
-  using __dealloc_fn = void (*)(void*, size_t) noexcept;
+  using __dealloc_fn _LIBCPP_NODEBUG = void (*)(void*, size_t) noexcept;
 
   template <class _Allocator>
   _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
@@ -502,8 +502,8 @@ public:
 template <class _Ref, class _Val>
 class __gen_iter_provider<_Ref, _Val>::__iterator {
 private:
-  using __val = __gen_val<_Ref, _Val>;
-  using __ref = __gen_ref<_Ref, _Val>;
+  using __val _LIBCPP_NODEBUG = __gen_val<_Ref, _Val>;
+  using __ref _LIBCPP_NODEBUG = __gen_ref<_Ref, _Val>;
 
 public:
   using value_type      = __val;
@@ -543,14 +543,14 @@ private:
 template <class _Ref, class _Val = void, class _Allocator = void>
 class generator : public ranges::view_interface<generator<_Ref, _Val, _Allocator>> {
 private:
-  using __val = __gen_val<_Ref, _Val>;
+  using __val _LIBCPP_NODEBUG = __gen_val<_Ref, _Val>;
   static_assert(same_as<remove_cvref_t<__val>, __val> && is_object_v<__val>);
 
-  using __ref = __gen_ref<_Ref, _Val>;
+  using __ref _LIBCPP_NODEBUG = __gen_ref<_Ref, _Val>;
   static_assert(is_reference_v<__ref> ||
                 (same_as<remove_cvref_t<__ref>, __ref> && is_object_v<__ref> && copy_constructible<__ref>));
 
-  using __rref = conditional_t<is_lvalue_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
+  using __rref _LIBCPP_NODEBUG = conditional_t<is_lvalue_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
   static_assert(common_reference_with<__ref&&, __val&> && common_reference_with<__ref&&, __rref&&> &&
                 common_reference_with<__rref&&, const __val&>);
 


From libcxx-commits at lists.llvm.org  Wed Jul 22 03:23:45 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 22 Jul 2026 03:23:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a609a31.1728eaf3.5e665.df5d@mx.google.com>


================
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
----------------
lknknm wrote:

Ah yes, sorry. I converted them into `static_assert`.

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 03:24:26 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 22 Jul 2026 03:24:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a609a5a.d2cb4adc.23815e.c43f@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/21] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/21] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/21] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/21] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/21] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/21] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/21] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/21] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/21] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/21] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/21] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/21] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/21] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 67d88113e5987460d20b2a0001e6aab7e53a57d5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:57:29 +0200
Subject: [PATCH 14/21] add: islessequal.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/islessequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
new file mode 100644
index 0000000000000..98fb3a9e7770a
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::islessequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::islessequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::islessequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::islessequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::islessequal(2.0, 1));     // double vs int
+    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 6d1e2ff8878af3188e953f68bb199406ff942354 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 00:00:33 +0200
Subject: [PATCH 15/21] fix: clang-format

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 2 +-
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index 1b9c1aacd0d13..af999db6166b3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -80,7 +80,7 @@ int main(int, char**) {
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));   // int vs float
+    assert(std::isless(1, 2.0f));     // int vs float
     assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 98fb3a9e7770a..2023b062f55f9 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -84,7 +84,7 @@ int main(int, char**) {
   // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::islessequal(2.0, 1));     // double vs int
-    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
   }
 

>From 71b5f4cf216bba7e770e854906b5112d93cbe367 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 01:57:12 +0200
Subject: [PATCH 16/21] add: islessgreater.pass.cpp and isunordered.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  |  2 +-
 .../numerics/c.math/islessgreater.pass.cpp    | 91 +++++++++++++++++++
 .../std/numerics/c.math/isunordered.pass.cpp  | 78 ++++++++++++++++
 3 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
 create mode 100644 libcxx/test/std/numerics/c.math/isunordered.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2023b062f55f9..2b0be4ff37035 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
new file mode 100644
index 0000000000000..40fbb7f4a4e41
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::islessgreater(T(1), T(2)));
+    assert(std::islessgreater(T(2), T(1)));
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    if (!std::is_same<T, bool>::value) {
+      assert(std::islessgreater(T(1), T(2)));
+      assert(std::islessgreater(T(2), T(1)));
+    }
+
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(T(1), T(1)));
+    assert(std::islessgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::islessgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    if (std::is_signed<T>::value) {
+      assert(std::islessgreater(T(-1), T(1)));
+      assert(std::islessgreater(std::numeric_limits<T>::lowest(), T(0)));
+      assert(std::islessgreater(T(0), std::numeric_limits<T>::lowest()));
+      assert(!std::islessgreater(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
new file mode 100644
index 0000000000000..ed7740d195163
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(std::isunordered(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    assert(std::isunordered(std::numeric_limits<T>::signaling_NaN(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From ef949295028c1bd5d6a19881e122393aada59194 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:01:27 +0200
Subject: [PATCH 17/21] flip assert tests for gcc

---
 .../c.math/constexpr-cxx23-gcc.pass.cpp       | 30 +++++++++----------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index 34513b459a056..8d814e81f8bc6 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -225,25 +225,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From f6a4fa78d49d5bd14219d1cb7885d0d6bf4524ba Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:02:43 +0200
Subject: [PATCH 18/21] fix typo in isgreaterequal.pass.cpp

---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index d6ae553e961e5..f2a4159882654 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows

>From 62ae94cc86a2b43b61a9ba80eadfc4013bcced15 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 18:18:09 +0200
Subject: [PATCH 19/21] update: cmath synopsis for is<...>

---
 libcxx/include/cmath | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..65bf07aed059b 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -140,17 +140,17 @@ bool signbit(arithmetic x);
 
 int fpclassify(arithmetic x);
 
-bool isfinite(arithmetic x);
-bool isinf(arithmetic x);
-bool isnan(arithmetic x);
-bool isnormal(arithmetic x);
-
-bool isgreater(arithmetic x, arithmetic y);
-bool isgreaterequal(arithmetic x, arithmetic y);
-bool isless(arithmetic x, arithmetic y);
-bool islessequal(arithmetic x, arithmetic y);
-bool islessgreater(arithmetic x, arithmetic y);
-bool isunordered(arithmetic x, arithmetic y);
+bool isfinite(arithmetic x); // constexpr since C++23
+bool isinf(arithmetic x);    // constexpr since C++23
+bool isnan(arithmetic x);    // constexpr since C++23
+bool isnormal(arithmetic x); // constexpr since C++23
+
+bool isgreater(arithmetic x, arithmetic y);      // constexpr since C++23
+bool isgreaterequal(arithmetic x, arithmetic y); // constexpr since C++23
+bool isless(arithmetic x, arithmetic y);         // constexpr since C++23
+bool islessequal(arithmetic x, arithmetic y);    // constexpr since C++23
+bool islessgreater(arithmetic x, arithmetic y);  // constexpr since C++23
+bool isunordered(arithmetic x, arithmetic y);    // constexpr since C++23
 
 floating_point acosh (arithmetic x);
 float          acoshf(float x);

>From 1a641131cba0843189ecd9364df414c8baa85cad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 20 Jul 2026 12:31:47 +0200
Subject: [PATCH 20/21] remove: UNSUPPORTED windows from tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp      | 3 ---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 3 ---
 libcxx/test/std/numerics/c.math/isless.pass.cpp         | 3 ---
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp    | 3 ---
 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp  | 3 ---
 libcxx/test/std/numerics/c.math/isunordered.pass.cpp    | 3 ---
 6 files changed, 18 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index c7853974d7a66..269bb31e016cf 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index f2a4159882654..27fa43812812a 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index af999db6166b3..cc68a49beead9 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2b0be4ff37035..5f58bbc993613 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
index 40fbb7f4a4e41..6a10708359bbe 100644
--- a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
index ed7740d195163..3f140106a423f 100644
--- a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>

>From 76b4a8a8ec33acf9f4c132072ad70422d6f09bd5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 22 Jul 2026 12:24:03 +0200
Subject: [PATCH 21/21] fix: convert assert to static_assert

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 269bb31e016cf..6932a26c75f8d 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -82,10 +82,10 @@ int main(int, char**) {
 
   // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
-    assert(std::isgreater(2.0, 1));     // double vs int
-    assert(!std::isgreater(1, 2.0f));   // int vs float
-    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
-    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+    static_assert(std::isgreater(2.0, 1));     // double vs int
+    static_assert(!std::isgreater(1, 2.0f));   // int vs float
+    static_assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    static_assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
   }
 
   return 0;


From libcxx-commits at lists.llvm.org  Wed Jul 22 03:26:04 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 22 Jul 2026 03:26:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a609abc.66c0679a.9a15c.1419@mx.google.com>


================
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
----------------
lknknm wrote:

I copied the exact same assertion behaviour from `signbit.pass.cpp` which has implemented `constexpr` to `std::signbit`. Should we move it let me know.

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 03:27:14 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 22 Jul 2026 03:27:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a609b02.6c8668f2.1842d3.f93c@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 03:27:38 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 22 Jul 2026 03:27:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a609b1a.92d2873c.2cdd42.b9da@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 03:27:52 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 22 Jul 2026 03:27:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a609b28.481afadf.1fc9f0.27cd@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 03:28:51 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 22 Jul 2026 03:28:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a609b63.e55dc341.dbc09.6709@mx.google.com>


================
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
----------------
lknknm wrote:

Yes sorry, it was a leftover. I'll clean it up for this file and other possible ones in this PR.

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 03:32:19 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 22 Jul 2026 03:32:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a609c33.d681cab9.33af1e.8503@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 03:32:57 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 03:32:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a609c59.5a0412d2.325e39.712b@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From 68da1731c2ad8f5e98872b8df5e8dd5bfa646008 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/28] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From c4d11be4adc7c07fd52f608bea12da28e8221371 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/28] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 781eaa5dbef3351d87574cf432106a2a7d7c9a38 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/28] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From 195931a66e133699188de65ed5b8ee7f9aef2fe7 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/28] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 26cf1daff36cae1d3e5f5476f7faba71cb019296 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/28] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 11b933048f6a2b6279d2ff8b643ce845975d5efe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/28] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 74f795f5f559b72803edd152d2f60b772dd697d8 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/28] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From 3f679ac8813a96d8179f8b72000153c3be0c1f50 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/28] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From 1bd7d694c3339a6b4e16a60a00817dc2130aa312 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/28] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 5a43b67d99477e25e09d5e2bf471de20bd21ad64 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/28] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index a77e98f845a06..425c13fea707e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -39,7 +39,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +47,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +63,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -81,7 +81,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index a8bc817ced291..ce81d1c00d376 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From b4695a242823d5f4943d22cf64802696a092191c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/28] Format

---
 libcxx/docs/Status/Cxx26Papers.csv                |  2 +-
 .../range.elements/iterator/base.pass.cpp         | 15 ++++++++-------
 .../range.elements/sentinel/base.pass.cpp         |  2 +-
 .../range.split/iterator/base.pass.cpp            | 10 +++++-----
 .../range.split/iterator/deref.pass.cpp           |  7 +++----
 .../sentinel/ctor.convert.pass.cpp                |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..ffa36d3f5145c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 425c13fea707e..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -81,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From ee7863618f3b2d5bc4fc88409b2d77d0424444ec Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/28] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..052e5eab25433 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index ffa36d3f5145c..4d0fbca940b92 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 11b36027bc4737a06ac1368d0c4027bead46a6c4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/28] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From 4127ad1211b268ab699b7c84aa543f8ce99fdc90 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/28] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp               | 16 ++++++++++++----
 .../sentinel/ctor.convert.pass.cpp             | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index ce81d1c00d376..25d1413b2fa16 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -67,15 +67,21 @@ static_assert(
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From c33bac978f0ecf59b457c38f5a12c79c56af2fac Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/28] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From e35c0d0bed0d974cacdc104648177102db7dedbe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/28] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}

>From dc161708c53ce4ecf0b5596956eec6f7b03cfe36 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:05:55 +0300
Subject: [PATCH 17/28] Fixed formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index e28c36b1254af..bcce8e1f48f7b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,8 +22,7 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
+concept IsBaseNoexcept = requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From f4d45145cd5092bb65a8137a243dc94a8e107812 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:18:55 +0300
Subject: [PATCH 18/28] Enable test

---
 .../ranges/range.adaptors/range.elements/sentinel/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index 885582a5c94a8..114b7fc1f1b71 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -37,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
   return 0;
 }

>From 851f503a96a63350ca8f809a8c5deca584405d68 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 07:53:40 +0300
Subject: [PATCH 19/28] Added `std::is_constructible/convertible` tests

---
 .../iterator/ctor.base.compile.pass.cpp       | 23 ++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 30 ++++++++++++++++
 .../ctor.parent.iter.compile.pass.cpp         | 25 +++++++++++++
 .../sentinel/ctor.parent.compile.pass.cpp     | 25 +++++++++++++
 .../ctor.outer_iterator.compile.pass.cpp      | 23 ++++++++++++
 .../ctor.parent.base.compile.pass.cpp         | 22 ++++++++++++
 .../ctor.parent.compile.pass.cpp              | 23 ++++++++++++
 .../sentinel/ctor.base.pred.compile.pass.cpp  | 35 +++++++++++++++++++
 .../iterator/ctor.value.compile.pass.cpp      | 26 ++++++++++++++
 .../sentinel/ctor.value.compile.pass.cpp      | 24 +++++++++++++
 .../iterator/ctor.parent.compile.pass.cpp     | 33 +++++++++++++++++
 11 files changed, 289 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..b43a60276e280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+using BaseIter     = std::tuple<int>*;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
+
+static_assert(!std::is_constructible_v<ElementsIter, BaseIter>);
+static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..ee4a297c1f4a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+  std::tuple<int>* begin() const;
+  Sent end();
+};
+
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..4a013d0072222
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..06e06aeb6db6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(filter_view&);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterSentinel, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterSentinel>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..37fe2fadfd41a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<InnerIterConst, OuterIterConst>);
+static_assert(!std::is_convertible_v<InnerIterConst, OuterIterConst>);
+
+static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterNonConst>);
+static_assert(!std::is_convertible_v<InnerIterNonConst, OuterIterNonConst>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
new file mode 100644
index 0000000000000..70e97ea04f5c0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
+//   requires forward_range<Base>
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(std::ranges::forward_range<SplitViewForward>);
+static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&, std::ranges::iterator_t<ForwardView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..b838970718c8a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
+//   requires (!forward_range<Base>)
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(!std::ranges::forward_range<SplitViewInput>);
+static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&>);
+static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
new file mode 100644
index 0000000000000..a66e3d1e84c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+  int* begin() const;
+  Sent end();
+};
+
+struct Pred {
+  bool operator()(int i) const;
+};
+
+using Sentinel = std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>;
+
+static_assert(!std::is_constructible_v<Sentinel, std::ranges::sentinel_t<Range>, const Pred*>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..e95042e26e146
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(W value);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
+
+using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
+static_assert(!std::is_constructible_v<Iter, SomeInt>);
+static_assert(!std::is_convertible_v<SomeInt, Iter>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..a39efb4318794
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.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++20
+
+// constexpr explicit sentinel(Bound bound);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
+static_assert(!std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
+static_assert(!std::is_convertible_v<IntSentinelWith<SomeInt>, Sent>);
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..171e2dce1e5f0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(basic_istream_view& parent) noexcept;
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <sstream>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// test that the constructor is explicit
+template <class CharT>
+using IstreamView = std::ranges::basic_istream_view<int, CharT>;
+template <class CharT>
+using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
+
+static_assert(!std::constructible_from<Iter<char>, IstreamView<char>&>);
+static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
+static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
+#endif

>From cfa8219f33f0d433afaa3144d3183bd763d58854 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 09:20:54 +0300
Subject: [PATCH 20/28] Cleanup

---
 .../range.iota.view/iterator/ctor.value.compile.pass.cpp        | 2 --
 .../range.iota.view/sentinel/ctor.value.compile.pass.cpp        | 2 --
 2 files changed, 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
index e95042e26e146..fc3384e191d1d 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
index a39efb4318794..b0bcdb6c60ce7 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;

>From 1438ff9824d09adb014641acc0ae3b0775ea8d27 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:42:42 +0300
Subject: [PATCH 21/28] Fixed CI

---
 .../range.istream.view/iterator/ctor.parent.compile.pass.cpp     | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
index 171e2dce1e5f0..04ff9ac24d731 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-localization
 
 // constexpr explicit iterator(basic_istream_view& parent) noexcept;
 

>From 98ad281967c925d3cbc27adaeabafeaddddd2227 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:57:29 +0300
Subject: [PATCH 22/28] Restored a test case!

---
 .../range.istream.view/iterator/deref.pass.cpp        | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 9f9f13399392c..5df730410a4ad 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,6 +28,17 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
+
+  // operator* should return the same reference to the value stored in the view
+  {
+    auto iss = make_string_stream<CharT>("1 2 345 ");
+    std::ranges::basic_istream_view<int, CharT> isv{iss};
+    using Iter = std::ranges::iterator_t<decltype(isv)>;
+
+    Iter it1 = isv.begin();
+    Iter it2 = isv.begin();
+    assert(&*it1 == &*it2);
+  }
 }
 
 int main(int, char**) {

>From ea4f9412bc9b26b3ae55b40c23ee1ac615bbb643 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:16:34 +0300
Subject: [PATCH 23/28] Added a missing `take_view` test

---
 .../ctor.base.compile.pass.cpp                | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..9383508165609
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using TakeView = std::ranges::take_view<MoveOnlyView>;
+using Sentinel = std::ranges::sentinel_t<TakeView>;
+
+static_assert(!std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
+static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);

>From d0afec965397496a91848fdd2e46ed89c98e7039 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:34:21 +0300
Subject: [PATCH 24/28] Added missing test `join_view`

---
 .../ctor.parent.compile.pass.cpp              | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..504c945e3d918
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(Parent& parent);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using Parent = std::ranges::join_view<ParentView<ChildView>>;
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
+static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);

>From c50248da199d6cbb3d5ba6b292ef102e7e960d30 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:54:39 +0300
Subject: [PATCH 25/28] Update tests `split_view`

---
 ...tor.parent.iter.subrange.compile.pass.cpp} | 31 +++++------------
 ....pass.cpp => ctor.parent.compile.pass.cpp} | 34 ++++---------------
 2 files changed, 14 insertions(+), 51 deletions(-)
 rename libcxx/test/std/ranges/range.adaptors/range.split/iterator/{ctor.base.pass.cpp => ctor.parent.iter.subrange.compile.pass.cpp} (64%)
 rename libcxx/test/std/ranges/range.adaptors/range.split/sentinel/{ctor.parent.pass.cpp => ctor.parent.compile.pass.cpp} (57%)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
similarity index 64%
rename from libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
index 823e22e117f77..9f4e9a13e0447 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
@@ -6,12 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
-// constexpr iterator begin();
+// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
 
-#include <cassert>
 #include <ranges>
+#include <type_traits>
 
 #include "../types.h"
 
@@ -30,24 +32,7 @@ struct TracedMoveView : std::ranges::view_base {
   constexpr TracedMoveIter end() const { return {}; }
 };
 
-constexpr bool test() {
-  using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
-  using SplitIter = std::ranges::iterator_t<SplitView>;
-
-  SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = sv.begin();
-  assert(iter.base().moved);
-
-  auto subRange = *iter;
-  assert(subRange.begin().moved);
-  assert(subRange.end().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
+using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
+using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  return 0;
-}
+static_assert(!std::is_constructible_v<SplitIter, SplitView, TracedMoveIter, std::ranges::subrange<TracedMoveIter>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
similarity index 57%
rename from libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 6e1ba912a201b..85d7e908a057c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -6,41 +6,19 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
 // constexpr explicit sentinel(split_view& parent);
 
-#include <cassert>
+// The constructor is now `private` (exposition-only) per P3059R2.
+
 #include <ranges>
-#include <type_traits>
 
-#include "test_iterators.h"
+#include "../types.h"
 
-// test explicit
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-constexpr bool test() {
-  {
-    int buffer[] = {0, 1, 2};
-    Range input{buffer, sentinel_wrapper<int*>(buffer + 3)};
-    SplitView sv(input, -1);
-    auto it = sv.begin();
-
-    auto sent = sv.end();
-    assert(sent != it);
-
-    ++it;
-    assert(sent == it);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
+static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_convertible_v<SplitView&, SplitSent>);

>From 50dc3e4c1aa67aa0e1b582eeecfbc904d7c80ef1 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:57:06 +0300
Subject: [PATCH 26/28] Fixed test

---
 .../range.split/sentinel/ctor.parent.compile.pass.cpp           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 85d7e908a057c..e0e8029c4c538 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -20,5 +20,5 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
 static_assert(!std::is_convertible_v<SplitView&, SplitSent>);

>From d93fdef4663517663ff4169230e6b2333f637d6e Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 12:49:06 +0300
Subject: [PATCH 27/28] Added missing `transform_view` tests

---
 .../ctor.parent.iter.compile.pass.cpp         | 23 ++++++++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 24 +++++++++++++++++++
 .../{iterator => sentinel}/sentinel.pass.cpp  |  0
 3 files changed, 47 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
 rename libcxx/test/std/ranges/range.adaptors/range.transform/{iterator => sentinel}/sentinel.pass.cpp (100%)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..e7841dede5bc2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+//  constexpr iterator(Parent& parent, iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using TransformView = std::ranges::transform_view<MoveOnlyView, PlusOne>;
+using TransformViewBaseIter =  std::ranges::iterator_t<MoveOnlyView>;
+using TransformIter = std::ranges::iterator_t<TransformView>;
+
+static_assert(!std::is_constructible_v<TransformIter, TransformView, TransformViewBaseIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..08be4bbd16667
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
+using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
+using SplitSent = std::ranges::sentinel_t<SplitView>;
+
+static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp
similarity index 100%
rename from libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp

>From 2b952f73eb09938ae7065c16560692152e4f0c50 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 13:32:08 +0300
Subject: [PATCH 28/28] Try to fix the tests

---
 .../iterator/ctor.parent.iter.compile.pass.cpp           | 3 ++-
 .../range.transform/sentinel/ctor.base.compile.pass.cpp  | 9 +++++----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
index 4a013d0072222..bca1b3bda4b24 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -18,8 +18,9 @@
 #include "../types.h"
 
 using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using ViewIter       = std::ranges::iterator_t<View>;
 using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
 using FilterIterator = std::ranges::iterator_t<FilterView>;
 
-static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::constructible_from<FilterIterator, FilterView, ViewIter>);
 static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
index 08be4bbd16667..25a73252bcd2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
@@ -14,11 +14,12 @@
 
 #include <ranges>
 
-#include "../types.h"
-
+#include "test_iterators.h"
+                                                                    
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
+using RangeSent = std::ranges::sentinel_t<Range>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
+static_assert(!std::is_constructible_v<SplitSent, RangeSent>);
+static_assert(!std::is_convertible_v<RangeSent, SplitSent>);


From libcxx-commits at lists.llvm.org  Wed Jul 22 03:34:46 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 03:34:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a609cc6.4a342cbc.1aeb63.b174@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From 68da1731c2ad8f5e98872b8df5e8dd5bfa646008 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/29] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From c4d11be4adc7c07fd52f608bea12da28e8221371 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/29] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 781eaa5dbef3351d87574cf432106a2a7d7c9a38 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/29] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From 195931a66e133699188de65ed5b8ee7f9aef2fe7 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/29] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 26cf1daff36cae1d3e5f5476f7faba71cb019296 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/29] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 11b933048f6a2b6279d2ff8b643ce845975d5efe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/29] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 74f795f5f559b72803edd152d2f60b772dd697d8 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/29] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From 3f679ac8813a96d8179f8b72000153c3be0c1f50 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/29] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From 1bd7d694c3339a6b4e16a60a00817dc2130aa312 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/29] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 5a43b67d99477e25e09d5e2bf471de20bd21ad64 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/29] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index a77e98f845a06..425c13fea707e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -39,7 +39,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +47,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +63,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -81,7 +81,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index a8bc817ced291..ce81d1c00d376 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From b4695a242823d5f4943d22cf64802696a092191c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/29] Format

---
 libcxx/docs/Status/Cxx26Papers.csv                |  2 +-
 .../range.elements/iterator/base.pass.cpp         | 15 ++++++++-------
 .../range.elements/sentinel/base.pass.cpp         |  2 +-
 .../range.split/iterator/base.pass.cpp            | 10 +++++-----
 .../range.split/iterator/deref.pass.cpp           |  7 +++----
 .../sentinel/ctor.convert.pass.cpp                |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..ffa36d3f5145c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 425c13fea707e..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -81,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From ee7863618f3b2d5bc4fc88409b2d77d0424444ec Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/29] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..052e5eab25433 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index ffa36d3f5145c..4d0fbca940b92 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 11b36027bc4737a06ac1368d0c4027bead46a6c4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/29] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From 4127ad1211b268ab699b7c84aa543f8ce99fdc90 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/29] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp               | 16 ++++++++++++----
 .../sentinel/ctor.convert.pass.cpp             | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index ce81d1c00d376..25d1413b2fa16 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -67,15 +67,21 @@ static_assert(
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From c33bac978f0ecf59b457c38f5a12c79c56af2fac Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/29] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From e35c0d0bed0d974cacdc104648177102db7dedbe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/29] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}

>From dc161708c53ce4ecf0b5596956eec6f7b03cfe36 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:05:55 +0300
Subject: [PATCH 17/29] Fixed formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index e28c36b1254af..bcce8e1f48f7b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,8 +22,7 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
+concept IsBaseNoexcept = requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From f4d45145cd5092bb65a8137a243dc94a8e107812 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:18:55 +0300
Subject: [PATCH 18/29] Enable test

---
 .../ranges/range.adaptors/range.elements/sentinel/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index 885582a5c94a8..114b7fc1f1b71 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -37,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
   return 0;
 }

>From 851f503a96a63350ca8f809a8c5deca584405d68 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 07:53:40 +0300
Subject: [PATCH 19/29] Added `std::is_constructible/convertible` tests

---
 .../iterator/ctor.base.compile.pass.cpp       | 23 ++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 30 ++++++++++++++++
 .../ctor.parent.iter.compile.pass.cpp         | 25 +++++++++++++
 .../sentinel/ctor.parent.compile.pass.cpp     | 25 +++++++++++++
 .../ctor.outer_iterator.compile.pass.cpp      | 23 ++++++++++++
 .../ctor.parent.base.compile.pass.cpp         | 22 ++++++++++++
 .../ctor.parent.compile.pass.cpp              | 23 ++++++++++++
 .../sentinel/ctor.base.pred.compile.pass.cpp  | 35 +++++++++++++++++++
 .../iterator/ctor.value.compile.pass.cpp      | 26 ++++++++++++++
 .../sentinel/ctor.value.compile.pass.cpp      | 24 +++++++++++++
 .../iterator/ctor.parent.compile.pass.cpp     | 33 +++++++++++++++++
 11 files changed, 289 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..b43a60276e280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+using BaseIter     = std::tuple<int>*;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
+
+static_assert(!std::is_constructible_v<ElementsIter, BaseIter>);
+static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..ee4a297c1f4a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+  std::tuple<int>* begin() const;
+  Sent end();
+};
+
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..4a013d0072222
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..06e06aeb6db6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(filter_view&);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterSentinel, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterSentinel>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..37fe2fadfd41a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<InnerIterConst, OuterIterConst>);
+static_assert(!std::is_convertible_v<InnerIterConst, OuterIterConst>);
+
+static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterNonConst>);
+static_assert(!std::is_convertible_v<InnerIterNonConst, OuterIterNonConst>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
new file mode 100644
index 0000000000000..70e97ea04f5c0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
+//   requires forward_range<Base>
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(std::ranges::forward_range<SplitViewForward>);
+static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&, std::ranges::iterator_t<ForwardView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..b838970718c8a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
+//   requires (!forward_range<Base>)
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(!std::ranges::forward_range<SplitViewInput>);
+static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&>);
+static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
new file mode 100644
index 0000000000000..a66e3d1e84c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+  int* begin() const;
+  Sent end();
+};
+
+struct Pred {
+  bool operator()(int i) const;
+};
+
+using Sentinel = std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>;
+
+static_assert(!std::is_constructible_v<Sentinel, std::ranges::sentinel_t<Range>, const Pred*>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..e95042e26e146
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(W value);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
+
+using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
+static_assert(!std::is_constructible_v<Iter, SomeInt>);
+static_assert(!std::is_convertible_v<SomeInt, Iter>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..a39efb4318794
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.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++20
+
+// constexpr explicit sentinel(Bound bound);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
+static_assert(!std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
+static_assert(!std::is_convertible_v<IntSentinelWith<SomeInt>, Sent>);
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..171e2dce1e5f0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(basic_istream_view& parent) noexcept;
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <sstream>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// test that the constructor is explicit
+template <class CharT>
+using IstreamView = std::ranges::basic_istream_view<int, CharT>;
+template <class CharT>
+using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
+
+static_assert(!std::constructible_from<Iter<char>, IstreamView<char>&>);
+static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
+static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
+#endif

>From cfa8219f33f0d433afaa3144d3183bd763d58854 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 09:20:54 +0300
Subject: [PATCH 20/29] Cleanup

---
 .../range.iota.view/iterator/ctor.value.compile.pass.cpp        | 2 --
 .../range.iota.view/sentinel/ctor.value.compile.pass.cpp        | 2 --
 2 files changed, 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
index e95042e26e146..fc3384e191d1d 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
index a39efb4318794..b0bcdb6c60ce7 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;

>From 1438ff9824d09adb014641acc0ae3b0775ea8d27 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:42:42 +0300
Subject: [PATCH 21/29] Fixed CI

---
 .../range.istream.view/iterator/ctor.parent.compile.pass.cpp     | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
index 171e2dce1e5f0..04ff9ac24d731 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-localization
 
 // constexpr explicit iterator(basic_istream_view& parent) noexcept;
 

>From 98ad281967c925d3cbc27adaeabafeaddddd2227 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:57:29 +0300
Subject: [PATCH 22/29] Restored a test case!

---
 .../range.istream.view/iterator/deref.pass.cpp        | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 9f9f13399392c..5df730410a4ad 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,6 +28,17 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
+
+  // operator* should return the same reference to the value stored in the view
+  {
+    auto iss = make_string_stream<CharT>("1 2 345 ");
+    std::ranges::basic_istream_view<int, CharT> isv{iss};
+    using Iter = std::ranges::iterator_t<decltype(isv)>;
+
+    Iter it1 = isv.begin();
+    Iter it2 = isv.begin();
+    assert(&*it1 == &*it2);
+  }
 }
 
 int main(int, char**) {

>From ea4f9412bc9b26b3ae55b40c23ee1ac615bbb643 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:16:34 +0300
Subject: [PATCH 23/29] Added a missing `take_view` test

---
 .../ctor.base.compile.pass.cpp                | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..9383508165609
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using TakeView = std::ranges::take_view<MoveOnlyView>;
+using Sentinel = std::ranges::sentinel_t<TakeView>;
+
+static_assert(!std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
+static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);

>From d0afec965397496a91848fdd2e46ed89c98e7039 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:34:21 +0300
Subject: [PATCH 24/29] Added missing test `join_view`

---
 .../ctor.parent.compile.pass.cpp              | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..504c945e3d918
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(Parent& parent);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using Parent = std::ranges::join_view<ParentView<ChildView>>;
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
+static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);

>From c50248da199d6cbb3d5ba6b292ef102e7e960d30 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:54:39 +0300
Subject: [PATCH 25/29] Update tests `split_view`

---
 ...tor.parent.iter.subrange.compile.pass.cpp} | 31 +++++------------
 ....pass.cpp => ctor.parent.compile.pass.cpp} | 34 ++++---------------
 2 files changed, 14 insertions(+), 51 deletions(-)
 rename libcxx/test/std/ranges/range.adaptors/range.split/iterator/{ctor.base.pass.cpp => ctor.parent.iter.subrange.compile.pass.cpp} (64%)
 rename libcxx/test/std/ranges/range.adaptors/range.split/sentinel/{ctor.parent.pass.cpp => ctor.parent.compile.pass.cpp} (57%)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
similarity index 64%
rename from libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
index 823e22e117f77..9f4e9a13e0447 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
@@ -6,12 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
-// constexpr iterator begin();
+// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
 
-#include <cassert>
 #include <ranges>
+#include <type_traits>
 
 #include "../types.h"
 
@@ -30,24 +32,7 @@ struct TracedMoveView : std::ranges::view_base {
   constexpr TracedMoveIter end() const { return {}; }
 };
 
-constexpr bool test() {
-  using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
-  using SplitIter = std::ranges::iterator_t<SplitView>;
-
-  SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = sv.begin();
-  assert(iter.base().moved);
-
-  auto subRange = *iter;
-  assert(subRange.begin().moved);
-  assert(subRange.end().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
+using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
+using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  return 0;
-}
+static_assert(!std::is_constructible_v<SplitIter, SplitView, TracedMoveIter, std::ranges::subrange<TracedMoveIter>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
similarity index 57%
rename from libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 6e1ba912a201b..85d7e908a057c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -6,41 +6,19 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
 // constexpr explicit sentinel(split_view& parent);
 
-#include <cassert>
+// The constructor is now `private` (exposition-only) per P3059R2.
+
 #include <ranges>
-#include <type_traits>
 
-#include "test_iterators.h"
+#include "../types.h"
 
-// test explicit
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-constexpr bool test() {
-  {
-    int buffer[] = {0, 1, 2};
-    Range input{buffer, sentinel_wrapper<int*>(buffer + 3)};
-    SplitView sv(input, -1);
-    auto it = sv.begin();
-
-    auto sent = sv.end();
-    assert(sent != it);
-
-    ++it;
-    assert(sent == it);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
+static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_convertible_v<SplitView&, SplitSent>);

>From 50dc3e4c1aa67aa0e1b582eeecfbc904d7c80ef1 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:57:06 +0300
Subject: [PATCH 26/29] Fixed test

---
 .../range.split/sentinel/ctor.parent.compile.pass.cpp           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 85d7e908a057c..e0e8029c4c538 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -20,5 +20,5 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
 static_assert(!std::is_convertible_v<SplitView&, SplitSent>);

>From d93fdef4663517663ff4169230e6b2333f637d6e Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 12:49:06 +0300
Subject: [PATCH 27/29] Added missing `transform_view` tests

---
 .../ctor.parent.iter.compile.pass.cpp         | 23 ++++++++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 24 +++++++++++++++++++
 .../{iterator => sentinel}/sentinel.pass.cpp  |  0
 3 files changed, 47 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
 rename libcxx/test/std/ranges/range.adaptors/range.transform/{iterator => sentinel}/sentinel.pass.cpp (100%)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..e7841dede5bc2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+//  constexpr iterator(Parent& parent, iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using TransformView = std::ranges::transform_view<MoveOnlyView, PlusOne>;
+using TransformViewBaseIter =  std::ranges::iterator_t<MoveOnlyView>;
+using TransformIter = std::ranges::iterator_t<TransformView>;
+
+static_assert(!std::is_constructible_v<TransformIter, TransformView, TransformViewBaseIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..08be4bbd16667
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
+using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
+using SplitSent = std::ranges::sentinel_t<SplitView>;
+
+static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp
similarity index 100%
rename from libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp

>From 2b952f73eb09938ae7065c16560692152e4f0c50 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 13:32:08 +0300
Subject: [PATCH 28/29] Try to fix the tests

---
 .../iterator/ctor.parent.iter.compile.pass.cpp           | 3 ++-
 .../range.transform/sentinel/ctor.base.compile.pass.cpp  | 9 +++++----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
index 4a013d0072222..bca1b3bda4b24 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -18,8 +18,9 @@
 #include "../types.h"
 
 using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using ViewIter       = std::ranges::iterator_t<View>;
 using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
 using FilterIterator = std::ranges::iterator_t<FilterView>;
 
-static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::constructible_from<FilterIterator, FilterView, ViewIter>);
 static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
index 08be4bbd16667..25a73252bcd2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
@@ -14,11 +14,12 @@
 
 #include <ranges>
 
-#include "../types.h"
-
+#include "test_iterators.h"
+                                                                    
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
+using RangeSent = std::ranges::sentinel_t<Range>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
+static_assert(!std::is_constructible_v<SplitSent, RangeSent>);
+static_assert(!std::is_convertible_v<RangeSent, SplitSent>);

>From d8642b310b66114297deacc294c6ff800821fd1d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 13:34:09 +0300
Subject: [PATCH 29/29] Formatting

---
 .../iterator/ctor.parent.iter.compile.pass.cpp              | 6 +++---
 .../range.transform/sentinel/ctor.base.compile.pass.cpp     | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
index e7841dede5bc2..4bafbfea3e284 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
@@ -16,8 +16,8 @@
 
 #include "../types.h"
 
-using TransformView = std::ranges::transform_view<MoveOnlyView, PlusOne>;
-using TransformViewBaseIter =  std::ranges::iterator_t<MoveOnlyView>;
-using TransformIter = std::ranges::iterator_t<TransformView>;
+using TransformView         = std::ranges::transform_view<MoveOnlyView, PlusOne>;
+using TransformViewBaseIter = std::ranges::iterator_t<MoveOnlyView>;
+using TransformIter         = std::ranges::iterator_t<TransformView>;
 
 static_assert(!std::is_constructible_v<TransformIter, TransformView, TransformViewBaseIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
index 25a73252bcd2e..bfbe0a5820b9e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
@@ -15,7 +15,7 @@
 #include <ranges>
 
 #include "test_iterators.h"
-                                                                    
+
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using RangeSent = std::ranges::sentinel_t<Range>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;


From libcxx-commits at lists.llvm.org  Wed Jul 22 03:50:49 2026
From: libcxx-commits at lists.llvm.org (LLVM Continuous Integration via libcxx-commits)
Date: Wed, 22 Jul 2026 03:50:49 -0700 (PDT)
Subject: [libcxx-commits] [clang] [clang-tools-extra] [flang] [libcxx] [lld]
 [llvm] Bump version to 24.0.0git (PR #209473)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209473@github.com>
Message-ID: <6a60a089.6c8668f2.1842d3.acd2@mx.google.com>


llvm-ci wrote:

LLVM Buildbot has detected a new failure on builder `clang-x86_64-debian-fast` running on `gribozavr4` while building `clang-tools-extra,clang,cmake,flang,libcxx,lld,llvm` at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/56/builds/4013

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: CodeGen/SystemZ/zos-ppa2.ll' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
sed -e 's/!"MODE"/!"ascii"/' -e 's/BYTE/85/' /b/1/clang-x86_64-debian-fast/llvm.src/llvm/test/CodeGen/SystemZ/zos-ppa2.ll > /b/1/clang-x86_64-debian-fast/llvm.obj/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: sed -e 's/!"MODE"/!"ascii"/' -e s/BYTE/85/ /b/1/clang-x86_64-debian-fast/llvm.src/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
# RUN: at line 2
/b/1/clang-x86_64-debian-fast/llvm.obj/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true < /b/1/clang-x86_64-debian-fast/llvm.obj/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll |  /b/1/clang-x86_64-debian-fast/llvm.obj/bin/FileCheck /b/1/clang-x86_64-debian-fast/llvm.obj/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# executed command: /b/1/clang-x86_64-debian-fast/llvm.obj/bin/llc -mtriple s390x-ibm-zos -mcpu=z15 -asm-verbose=true
# executed command: /b/1/clang-x86_64-debian-fast/llvm.obj/bin/FileCheck /b/1/clang-x86_64-debian-fast/llvm.obj/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# .---command stderr------------
# | /b/1/clang-x86_64-debian-fast/llvm.obj/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll:24:10: error: CHECK: expected string not found in input
# | ; CHECK: DC XL6'F2F3F0F0F0F0'
# |          ^
# | <stdin>:17:39: note: scanning from here
# |  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0' 
# |                                       ^
# | <stdin>:18:2: note: possible intended match here
# |  DC XL6'F2F4F0F0F0F0' 
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /b/1/clang-x86_64-debian-fast/llvm.obj/test/CodeGen/SystemZ/Output/zos-ppa2.ll.tmp.ascii.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             .
# |             .
# |             .
# |            12:  DC XL4'00000000'  
# |            13:  DC XL1'85'  
# |            14:  DC XL1'00'  
# |            15:  DC XL2'0000'  
# |            16: L#DVS DS 0H  
# |            17:  DC XL14'F1F9F7F0F0F1F0F1F0F0F0F0F0F0'  
# | check:24'0                                          {    search range start (exclusive)
# | check:24'1                                               error: no match found in search range
# |            18:  DC XL6'F2F4F0F0F0F0'  
# | check:24'2      ?                       possible intended match
# |            19:  DC XL2'0000'  
# |            20: stdin#C CSECT  
# |            21: C_@@QPPA2 CATTR ALIGN(3),FILL(0),NOTEXECUTABLE,READONLY,RMODE(64),PART(.  
# |            22:  .&ppa2)  
# |            23: .&ppa2 XATTR LINKAGE(OS),REFERENCE(DATA),SCOPE(SECTION)  
# |             .
# |             .
...

```

</details>

https://github.com/llvm/llvm-project/pull/209473

From libcxx-commits at lists.llvm.org  Wed Jul 22 04:06:30 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 04:06:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Uniformly require complete types
 in vector member functions (PR #211110)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211110@github.com>
Message-ID: <6a60a436.1905e68e.25404.f06b@mx.google.com>


philnik777 wrote:

#211231 is an alternative to this approach, which also improves diagnostics of incomplete types in general. Assuming the Clang folks are happy with it I think that's the superior approach.

https://github.com/llvm/llvm-project/pull/211110

From libcxx-commits at lists.llvm.org  Wed Jul 22 04:19:20 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 04:19:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a60a738.66c0679a.9a15c.a43e@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From 8a8d85c8f25952c61c03bcf8c8967d5789f3a278 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/26] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..11a0b42ea54f5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From 43978418a74e0c39bcecff9a045a9fa7f77f2750 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/26] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From da435f9c4c7fa5c0a526d0beccdd0ed57e777469 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/26] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 11a0b42ea54f5..caa7f9d1a2535 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..8bdde44c1cb8c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -895,6 +895,7 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From 728593490b1918081d01078fb1095f0f0257d2f5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/26] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From 99d616611ad0970a4956051969408128c8e7f95d Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/26] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From 96b1c78fb96cf6e004aa68f10d510186c3b83fea Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/26] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From 445d2ca55a0c3814662ff7b7a06881efd14d5f2e Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/26] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From ee7f61a808ad8ff5d5e70f601450be56a8f0741a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/26] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 56cbfdaa5c0ff..8ccbcf9d8d227 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -160,7 +160,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -178,7 +178,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -193,7 +193,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b20b5e25fc9cd..04e12afd21689 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From 88d54fccb51126d63c79ded521d856e2ea07b72d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/26] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index caa7f9d1a2535..b6650a314bd40 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 8ccbcf9d8d227..04bdd2a6b343f 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -28,29 +28,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 04e12afd21689..3776873d8936d 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 8bdde44c1cb8c..f038c9d39a27b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -884,6 +884,7 @@ module std {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -895,7 +896,6 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From 52cc4a652899aba13296064b5e21e563455c26d8 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/26] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From a25323b34c7937b00ed55edc31436e92bebd387a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/26] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 3776873d8936d..fbe90c05ccaeb 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From 1a12d1ac51c519ced6fc7dba17d394a671e7e1fb Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/26] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From f187fa0bded69354f5c9293d170c1f062e0dbc03 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/26] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From c5dd5cca5521e65d7c14adfcaa43dc5b65adea22 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/26] 14

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <cstring>
+#include <type_traits>
 
 struct WithTailPadding {
   int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From 82695aaa698cee07453d78ea265fc16accbdd0d3 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 20:42:14 +0100
Subject: [PATCH 15/26] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e4d83e638ca77..0bafdbb1d7ede 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -136,7 +136,7 @@ void test() {
   {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
-
+    std::atomic<T> a;
     T init = make<T>(10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);

>From 6a05621ec814c677f83463123bb34ca14ac3da52 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:13:05 +0100
Subject: [PATCH 16/26] ci

---
 libcxx/include/__atomic/support/gcc.h         |  5 ++--
 .../atomics.types.generic/padding.pass.cpp    | 24 ++++++++++-------
 .../atomics.types.generic/padding.pass.cpp    | 27 ++++++++++---------
 .../atomics.types.generic/padding2.pass.cpp   |  3 ---
 4 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index fbe90c05ccaeb..375c7721965b8 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -50,9 +50,8 @@ struct __cxx_atomic_base_impl {
     std::__clear_padding_if_needed(__a_value);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
   _Tp __a_value;
 };
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index bf594b45ed54f..6f894fe6cb605 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -44,15 +44,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -61,11 +69,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0bafdbb1d7ede..344d08b493e60 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,9 +7,6 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits
@@ -41,15 +38,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -58,11 +63,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
index 4e17471a2461f..1040f342b8206 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -8,9 +8,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-localization
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits

>From 506ca2976f3df389ec8a4709a516cabe447e7f46 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:29:04 +0100
Subject: [PATCH 17/26] CI

---
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding2.pass.cpp   | 273 ------------------
 3 files changed, 2 insertions(+), 275 deletions(-)
 delete mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 6f894fe6cb605..388ab0867fe38 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 344d08b493e60..f1a534a2916d6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
deleted file mode 100644
index 1040f342b8206..0000000000000
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-localization
-
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
-// CAS should work on types with padding bits
-
-#include <atomic>
-#include <cassert>
-#include <cstring>
-#include <type_traits>
-#include <iostream>
-
-struct Foo {
-  int i;
-  char c;
-};
-
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
-
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
-}
-
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
-}
-
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-#ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
-#else
-  (void)f;
-  (void)pad_byte;
-#endif
-}
-
-void test() {
-  std::cerr << "test 1" << std::endl;
-  {
-    // compare_exchange_strong
-    // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
-    std::cerr << "test 2" << std::endl;
-
-    Foo init = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 3" << std::endl;
-    assert_foo_padding(init, 0xBB);
-    std::cerr << "test 4" << std::endl;
-    a.store(init);
-
-    Foo expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 5" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 6" << std::endl;
-
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 7" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 8" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 9" << std::endl;
-    assert(r);
-    std::cerr << "test 10" << std::endl;
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    std::cerr << "test 11" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 42);
-    std::cerr << "test 12" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 13" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 14" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 15" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 16" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 17" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 18" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 19" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 10" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 21" << std::endl;
-    assert(!r);
-    std::cerr << "test 22" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 23" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 24" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 25" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 10);
-    std::cerr << "test 26" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 27" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 28" << std::endl;
-  }
-
-  {
-    // compare_exchange_weak
-    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 29" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 30" << std::endl;
-    a.store(stored);
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 31" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 32" << std::endl;
-
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 33" << std::endl;
-    assert_foo_padding(original_expected, 0xAA);
-    std::cerr << "test 34" << std::endl;
-
-    bool r                  = false;
-    const auto max_attempts = 100;
-    auto current_attempt    = 0;
-    while (!r) {
-      ++current_attempt;
-      std::cerr << "test 35" << std::endl;
-      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      std::cerr << "test 36" << std::endl;
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
-      std::cerr << "test 37" << std::endl;
-      r = a.compare_exchange_weak(expected, new_value);
-      if (r) {
-        std::cerr << "test 38" << std::endl;
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-        std::cerr << "test 39" << std::endl;
-      } else {
-        // Spurious failure: expected is updated to the current atomic value.
-        std::cerr << "test 40" << std::endl;
-        assert(expected.i == 10);
-        std::cerr << "test 41" << std::endl;
-        assert(expected.c == 'a');
-        std::cerr << "test 45" << std::endl;
-        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
-        std::cerr << "test 43" << std::endl;
-      }
-    }
-
-    Foo loaded = a.load();
-    std::cerr << "test 44" << std::endl;
-    assert(loaded.i == 42);
-    std::cerr << "test 45" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 46" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 47" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 48" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 49" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 50" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 51" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 52" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 53" << std::endl;
-
-    bool r = a.compare_exchange_weak(expected, new_value);
-
-    std::cerr << "test 54" << std::endl;
-    assert(!r);
-    std::cerr << "test 55" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 56" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 57" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 58" << std::endl;
-    Foo loaded = a.load();
-    std::cerr << "test 59" << std::endl;
-    assert(loaded.i == 10);
-    std::cerr << "test 60" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 61" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 62" << std::endl;
-  }
-
-  {
-    // Types with unique object representations skip the padding-clearing path.
-    std::atomic<int> a(1);
-    int expected = 1;
-    std::cerr << "test 63" << std::endl;
-    assert(a.compare_exchange_strong(expected, 2));
-    std::cerr << "test 64" << std::endl;
-    assert(expected == 1);
-    std::cerr << "test 65" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 66" << std::endl;
-
-    expected = 3;
-    assert(!a.compare_exchange_strong(expected, 4));
-    std::cerr << "test 67" << std::endl;
-    assert(expected == 2);
-    std::cerr << "test 68" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 69" << std::endl;
-  }
-}
-
-int main(int, char**) {
-  // TODO(LLVM-23): Switch to XFAIL: clang-22
-  std::cerr << "main 1" << std::endl;
-#if __has_builtin(__builtin_clear_padding)
-  std::cerr << "main 2" << std::endl;
-  test();
-  std::cerr << "main 3" << std::endl;
-#endif
-  std::cerr << "main 4" << std::endl;
-  return 0;
-}

>From ef4330c6a55c096505e1ec51c05d1f3040fdcbfc Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:52:15 +0100
Subject: [PATCH 18/26] ci

---
 libcxx/include/__atomic/support/c11.h | 13 ++++++++-----
 libcxx/include/__atomic/support/gcc.h |  9 +++++++--
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 04bdd2a6b343f..2d4cc9fd9aae0 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -28,13 +28,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
 
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 375c7721965b8..ebc5496c530c3 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,12 +46,17 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
       : __a_value(std::__clear_padding_if_needed(__value)) {}
+
   _Tp __a_value;
 };
 

>From 65c7769de6530fd230c0964eb64643f4d070cd67 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:26:08 +0100
Subject: [PATCH 19/26] review

---
 libcxx/include/__atomic/clear_padding.h            | 14 ++++++--------
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 5bc77ff4401ab..f920ef39977b7 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -35,17 +35,16 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, float>>,
            _Not<is_same<_Tp, double>>> {};
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(std::addressof(__obj));
-    }
-  }
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+  return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
+}
+
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
@@ -67,7 +66,6 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 388ab0867fe38..45fb083cccf3f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index f1a534a2916d6..5ffba6ca844c5 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();

>From f9ee2b6fe6d2e45f9bc84f5ae7c4ae8e9fd94316 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:33:16 +0100
Subject: [PATCH 20/26] release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx20Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index bcac37b8a717f..19f517e66f096 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange (`Github <https://llvm.org/PR76180>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 5539767ab800b..8e394b4bc4b3a 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -31,7 +31,7 @@
 "`P0458R2 <https://wg21.link/P0458R2>`__","Checking for Existence of an Element in Associative Containers","2018-06 (Rapperswil)","|Complete|","13","`#104029 <https://github.com/llvm/llvm-project/issues/104029>`__",""
 "`P0475R1 <https://wg21.link/P0475R1>`__","LWG 2511: guaranteed copy elision for piecewise construction","2018-06 (Rapperswil)","|Complete|","","`#104031 <https://github.com/llvm/llvm-project/issues/104031>`__",""
 "`P0476R2 <https://wg21.link/P0476R2>`__","Bit-casting object representations","2018-06 (Rapperswil)","|Complete|","14","`#104087 <https://github.com/llvm/llvm-project/issues/104087>`__",""
-"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","","","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
+"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","|Complete|","24","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
 "`P0542R5 <https://wg21.link/P0542R5>`__","Support for contract based programming in C++","2018-06 (Rapperswil)","|Nothing To Do|","n/a","`#104089 <https://github.com/llvm/llvm-project/issues/104089>`__","Pulled at the 2019-07 meeting in Cologne"
 "`P0556R3 <https://wg21.link/P0556R3>`__","Integral power-of-2 operations","2018-06 (Rapperswil)","|Complete|","9","`#104090 <https://github.com/llvm/llvm-project/issues/104090>`__",""
 "`P0619R4 <https://wg21.link/P0619R4>`__","Reviewing Deprecated Facilities of C++17 for C++20","2018-06 (Rapperswil)","|Complete|","20","`#99985 <https://github.com/llvm/llvm-project/issues/99985>`__","Removed headers are still provided as an extension, but with deprecation warnings."

>From b96f67b765d94939dfeb1592b90f6fdf0301d707 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 17:01:57 +0100
Subject: [PATCH 21/26] ci

---
 libcxx/include/__atomic/clear_padding.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index f920ef39977b7..3099faa8ca6d1 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/has_unique_object_representation.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>

>From baefea3ffda8d41d1b72b515a5a4281dac7aa7d3 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 19:03:39 +0100
Subject: [PATCH 22/26] ci

---
 .../atomics.types.generic/padding.pass.cpp    | 19 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 52 ++++++++++++-------
 2 files changed, 44 insertions(+), 27 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 45fb083cccf3f..116a860b3192c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -57,11 +57,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -86,7 +84,8 @@ void test() {
 
   {
     // atomic(T);
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     std::atomic<T> a(init);
     T loaded = a.load();
@@ -97,7 +96,8 @@ void test() {
   {
     // atomic::store
     std::atomic<T> a;
-    T value = make<T>(5, 'x', 0xAB);
+    T value;
+    initialize(value, 5, 'x', 0xAB);
     assert_padding(value, 0xAB);
     a.store(value);
     T loaded = a.load();
@@ -107,10 +107,12 @@ void test() {
   }
   {
     // atomic::exchange
-    T initial = make<T>(1, 'a', 0x00);
+    T initial;
+    initialize(initial, 1, 'a', 0x00);
     assert_padding(initial, 0x00);
     std::atomic<T> a(initial);
-    T new_val = make<T>(2, 'b', 0xCD);
+    T new_val;
+    initialize(new_val, 2, 'b', 0xCD);
     assert_padding(new_val, 0xCD);
     T old = a.exchange(new_val);
     assert(old.i == 1);
@@ -124,7 +126,8 @@ void test() {
   {
     // atomic_init
     std::atomic<T> a;
-    T init = make<T>(7, 'z', 0xEF);
+    T init;
+    initialize(init, 7, 'z', 0xEF);
     assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
     T loaded = a.load();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 5ffba6ca844c5..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -51,11 +51,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -84,17 +82,20 @@ void test() {
     // CAS should succeed when only padding differs in expected; expected is unchanged.
     std::atomic<T> a;
 
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0xAA);
+    T expected;
+    initialize(expected, 10, 'a', 0xAA);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -112,13 +113,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -138,17 +142,20 @@ void test() {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
     std::atomic<T> a;
-    T init = make<T>(10, 'a', 0x00);
+    T init;
+    initialize(init, 10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0x00);
+    T expected;
+    initialize(expected, 10, 'a', 0x00);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0x42);
+    T new_value;
+    initialize(new_value, 42, 'b', 0x42);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,14 +173,17 @@ void test() {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
-    T original_expected = make<T>(10, 'a', 0xAA);
+    T original_expected;
+    initialize(original_expected, 10, 'a', 0xAA);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -182,7 +192,8 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      T expected = make<T>(10, 'a', 0xAA);
+      T expected;
+      initialize(expected, 10, 'a', 0xAA);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -207,13 +218,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);

>From 8be9a83ee040faff62f0756555890484bf502a31 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:07:26 +0100
Subject: [PATCH 23/26] debug

---
 .../atomics.types.generic/padding.pass.cpp    | 64 ++++++++++++++++++-
 1 file changed, 62 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..3b7f13b366e96 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,6 +15,7 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
+#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -70,11 +71,19 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
+void print_memory(const T& obj) {
+  for (size_t i = 0; i < sizeof(T); ++i) {
+    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
+  }
+  std::cerr << std::endl;
+}
+
 template <class T>
 void test() {
   {
@@ -84,11 +93,15 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
+    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
+    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -96,6 +109,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -106,6 +121,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test1 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -115,14 +132,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -131,11 +154,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test2 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test2 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -144,11 +171,15 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
+    std::cerr << "test3 init padding should be 0x00: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
+    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -156,6 +187,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
+    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,6 +199,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test3 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -175,15 +210,21 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
+    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
+    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -194,6 +235,8 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
+      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
+      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -203,6 +246,8 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        std::cerr << "test4 expected padding should be 0: " << std::endl;
+        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -211,6 +256,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test4 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -220,14 +267,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -236,11 +289,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test5 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test5 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -262,10 +319,13 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
+  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
+  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
+  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 0;
+  return 1;
 }

>From 326a3fd284476c6bd64b604dc2d8fa0b9c8046ab Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:38:19 +0100
Subject: [PATCH 24/26] Revert "debug"

This reverts commit 8be9a83ee040faff62f0756555890484bf502a31.
---
 .../atomics.types.generic/padding.pass.cpp    | 64 +------------------
 1 file changed, 2 insertions(+), 62 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 3b7f13b366e96..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,7 +15,6 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
-#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -71,19 +70,11 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)obj;
+  (void)f;
   (void)pad_byte;
 #endif
 }
 
-template <class T>
-void print_memory(const T& obj) {
-  for (size_t i = 0; i < sizeof(T); ++i) {
-    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
-  }
-  std::cerr << std::endl;
-}
-
 template <class T>
 void test() {
   {
@@ -93,15 +84,11 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
-    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
-    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -109,8 +96,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -121,8 +106,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test1 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -132,20 +115,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -154,15 +131,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test2 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test2 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -171,15 +144,11 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
-    std::cerr << "test3 init padding should be 0x00: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
-    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -187,8 +156,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
-    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -199,8 +166,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test3 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -210,21 +175,15 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
-    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
-    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -235,8 +194,6 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
-      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
-      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -246,8 +203,6 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        std::cerr << "test4 expected padding should be 0: " << std::endl;
-        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -256,8 +211,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test4 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -267,20 +220,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -289,15 +236,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test5 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test5 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -319,13 +262,10 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
-  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
-  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
-  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 1;
+  return 0;
 }

>From 08465128943b1363a7254016b620ebe927653198 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:44:00 +0100
Subject: [PATCH 25/26] test ci

---
 .../atomics.types.generic/padding.pass.cpp    | 20 ++++++++++++++-----
 .../atomics.types.generic/padding.pass.cpp    | 12 +----------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 116a860b3192c..cd41f2893864c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -71,6 +71,16 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
+void assert_padding(const std::atomic<T>& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  T loaded     = obj.load();
+  set(reference, loaded.i, loaded.c);
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
+}
+
 template <class T>
 void test() {
   {
@@ -79,7 +89,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 
   {
@@ -91,7 +101,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::store
@@ -103,7 +113,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::exchange
@@ -121,7 +131,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic_init
@@ -133,7 +143,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..10c15523cd70b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -70,7 +70,7 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
@@ -105,8 +105,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -135,8 +133,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
   {
     // compare_exchange_strong
@@ -165,8 +161,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -210,8 +204,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -240,8 +232,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {

>From 3b7c38b807e3c9ddc07a0c6e9a5239ff2d21b3f3 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:18:49 +0100
Subject: [PATCH 26/26] padding

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index cd41f2893864c..a178ce74578ec 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -127,7 +127,6 @@ void test() {
     T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_padding(old, 0);
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');


From libcxx-commits at lists.llvm.org  Wed Jul 22 04:21:09 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 04:21:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a60a7a5.c5504292.1bbfc8.365e@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From 68da1731c2ad8f5e98872b8df5e8dd5bfa646008 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/31] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From c4d11be4adc7c07fd52f608bea12da28e8221371 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/31] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 781eaa5dbef3351d87574cf432106a2a7d7c9a38 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/31] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From 195931a66e133699188de65ed5b8ee7f9aef2fe7 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/31] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 26cf1daff36cae1d3e5f5476f7faba71cb019296 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/31] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 11b933048f6a2b6279d2ff8b643ce845975d5efe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/31] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 74f795f5f559b72803edd152d2f60b772dd697d8 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/31] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From 3f679ac8813a96d8179f8b72000153c3be0c1f50 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/31] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From 1bd7d694c3339a6b4e16a60a00817dc2130aa312 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/31] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From 5a43b67d99477e25e09d5e2bf471de20bd21ad64 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/31] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index a77e98f845a06..425c13fea707e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -39,7 +39,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +47,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +63,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -81,7 +81,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index a8bc817ced291..ce81d1c00d376 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From b4695a242823d5f4943d22cf64802696a092191c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/31] Format

---
 libcxx/docs/Status/Cxx26Papers.csv                |  2 +-
 .../range.elements/iterator/base.pass.cpp         | 15 ++++++++-------
 .../range.elements/sentinel/base.pass.cpp         |  2 +-
 .../range.split/iterator/base.pass.cpp            | 10 +++++-----
 .../range.split/iterator/deref.pass.cpp           |  7 +++----
 .../sentinel/ctor.convert.pass.cpp                |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..ffa36d3f5145c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 425c13fea707e..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -81,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From ee7863618f3b2d5bc4fc88409b2d77d0424444ec Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/31] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..052e5eab25433 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index ffa36d3f5145c..4d0fbca940b92 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From 11b36027bc4737a06ac1368d0c4027bead46a6c4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/31] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From 4127ad1211b268ab699b7c84aa543f8ce99fdc90 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/31] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp               | 16 ++++++++++++----
 .../sentinel/ctor.convert.pass.cpp             | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index ce81d1c00d376..25d1413b2fa16 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -67,15 +67,21 @@ static_assert(
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From c33bac978f0ecf59b457c38f5a12c79c56af2fac Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/31] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From e35c0d0bed0d974cacdc104648177102db7dedbe Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/31] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}

>From dc161708c53ce4ecf0b5596956eec6f7b03cfe36 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:05:55 +0300
Subject: [PATCH 17/31] Fixed formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index e28c36b1254af..bcce8e1f48f7b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,8 +22,7 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
+concept IsBaseNoexcept = requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From f4d45145cd5092bb65a8137a243dc94a8e107812 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:18:55 +0300
Subject: [PATCH 18/31] Enable test

---
 .../ranges/range.adaptors/range.elements/sentinel/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index 885582a5c94a8..114b7fc1f1b71 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -37,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
   return 0;
 }

>From 851f503a96a63350ca8f809a8c5deca584405d68 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 07:53:40 +0300
Subject: [PATCH 19/31] Added `std::is_constructible/convertible` tests

---
 .../iterator/ctor.base.compile.pass.cpp       | 23 ++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 30 ++++++++++++++++
 .../ctor.parent.iter.compile.pass.cpp         | 25 +++++++++++++
 .../sentinel/ctor.parent.compile.pass.cpp     | 25 +++++++++++++
 .../ctor.outer_iterator.compile.pass.cpp      | 23 ++++++++++++
 .../ctor.parent.base.compile.pass.cpp         | 22 ++++++++++++
 .../ctor.parent.compile.pass.cpp              | 23 ++++++++++++
 .../sentinel/ctor.base.pred.compile.pass.cpp  | 35 +++++++++++++++++++
 .../iterator/ctor.value.compile.pass.cpp      | 26 ++++++++++++++
 .../sentinel/ctor.value.compile.pass.cpp      | 24 +++++++++++++
 .../iterator/ctor.parent.compile.pass.cpp     | 33 +++++++++++++++++
 11 files changed, 289 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..b43a60276e280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+using BaseIter     = std::tuple<int>*;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
+
+static_assert(!std::is_constructible_v<ElementsIter, BaseIter>);
+static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..ee4a297c1f4a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+  std::tuple<int>* begin() const;
+  Sent end();
+};
+
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..4a013d0072222
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..06e06aeb6db6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(filter_view&);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterSentinel, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterSentinel>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..37fe2fadfd41a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<InnerIterConst, OuterIterConst>);
+static_assert(!std::is_convertible_v<InnerIterConst, OuterIterConst>);
+
+static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterNonConst>);
+static_assert(!std::is_convertible_v<InnerIterNonConst, OuterIterNonConst>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
new file mode 100644
index 0000000000000..70e97ea04f5c0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
+//   requires forward_range<Base>
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(std::ranges::forward_range<SplitViewForward>);
+static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&, std::ranges::iterator_t<ForwardView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..b838970718c8a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
+//   requires (!forward_range<Base>)
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(!std::ranges::forward_range<SplitViewInput>);
+static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&>);
+static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
new file mode 100644
index 0000000000000..a66e3d1e84c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+  int* begin() const;
+  Sent end();
+};
+
+struct Pred {
+  bool operator()(int i) const;
+};
+
+using Sentinel = std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>;
+
+static_assert(!std::is_constructible_v<Sentinel, std::ranges::sentinel_t<Range>, const Pred*>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..e95042e26e146
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(W value);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
+
+using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
+static_assert(!std::is_constructible_v<Iter, SomeInt>);
+static_assert(!std::is_convertible_v<SomeInt, Iter>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..a39efb4318794
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.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++20
+
+// constexpr explicit sentinel(Bound bound);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
+static_assert(!std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
+static_assert(!std::is_convertible_v<IntSentinelWith<SomeInt>, Sent>);
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..171e2dce1e5f0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(basic_istream_view& parent) noexcept;
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <sstream>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// test that the constructor is explicit
+template <class CharT>
+using IstreamView = std::ranges::basic_istream_view<int, CharT>;
+template <class CharT>
+using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
+
+static_assert(!std::constructible_from<Iter<char>, IstreamView<char>&>);
+static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
+static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
+#endif

>From cfa8219f33f0d433afaa3144d3183bd763d58854 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 09:20:54 +0300
Subject: [PATCH 20/31] Cleanup

---
 .../range.iota.view/iterator/ctor.value.compile.pass.cpp        | 2 --
 .../range.iota.view/sentinel/ctor.value.compile.pass.cpp        | 2 --
 2 files changed, 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
index e95042e26e146..fc3384e191d1d 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
index a39efb4318794..b0bcdb6c60ce7 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;

>From 1438ff9824d09adb014641acc0ae3b0775ea8d27 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:42:42 +0300
Subject: [PATCH 21/31] Fixed CI

---
 .../range.istream.view/iterator/ctor.parent.compile.pass.cpp     | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
index 171e2dce1e5f0..04ff9ac24d731 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-localization
 
 // constexpr explicit iterator(basic_istream_view& parent) noexcept;
 

>From 98ad281967c925d3cbc27adaeabafeaddddd2227 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:57:29 +0300
Subject: [PATCH 22/31] Restored a test case!

---
 .../range.istream.view/iterator/deref.pass.cpp        | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 9f9f13399392c..5df730410a4ad 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,6 +28,17 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
+
+  // operator* should return the same reference to the value stored in the view
+  {
+    auto iss = make_string_stream<CharT>("1 2 345 ");
+    std::ranges::basic_istream_view<int, CharT> isv{iss};
+    using Iter = std::ranges::iterator_t<decltype(isv)>;
+
+    Iter it1 = isv.begin();
+    Iter it2 = isv.begin();
+    assert(&*it1 == &*it2);
+  }
 }
 
 int main(int, char**) {

>From ea4f9412bc9b26b3ae55b40c23ee1ac615bbb643 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:16:34 +0300
Subject: [PATCH 23/31] Added a missing `take_view` test

---
 .../ctor.base.compile.pass.cpp                | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..9383508165609
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using TakeView = std::ranges::take_view<MoveOnlyView>;
+using Sentinel = std::ranges::sentinel_t<TakeView>;
+
+static_assert(!std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
+static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);

>From d0afec965397496a91848fdd2e46ed89c98e7039 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:34:21 +0300
Subject: [PATCH 24/31] Added missing test `join_view`

---
 .../ctor.parent.compile.pass.cpp              | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..504c945e3d918
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(Parent& parent);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using Parent = std::ranges::join_view<ParentView<ChildView>>;
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
+static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);

>From c50248da199d6cbb3d5ba6b292ef102e7e960d30 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:54:39 +0300
Subject: [PATCH 25/31] Update tests `split_view`

---
 ...tor.parent.iter.subrange.compile.pass.cpp} | 31 +++++------------
 ....pass.cpp => ctor.parent.compile.pass.cpp} | 34 ++++---------------
 2 files changed, 14 insertions(+), 51 deletions(-)
 rename libcxx/test/std/ranges/range.adaptors/range.split/iterator/{ctor.base.pass.cpp => ctor.parent.iter.subrange.compile.pass.cpp} (64%)
 rename libcxx/test/std/ranges/range.adaptors/range.split/sentinel/{ctor.parent.pass.cpp => ctor.parent.compile.pass.cpp} (57%)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
similarity index 64%
rename from libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
index 823e22e117f77..9f4e9a13e0447 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
@@ -6,12 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
-// constexpr iterator begin();
+// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
 
-#include <cassert>
 #include <ranges>
+#include <type_traits>
 
 #include "../types.h"
 
@@ -30,24 +32,7 @@ struct TracedMoveView : std::ranges::view_base {
   constexpr TracedMoveIter end() const { return {}; }
 };
 
-constexpr bool test() {
-  using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
-  using SplitIter = std::ranges::iterator_t<SplitView>;
-
-  SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = sv.begin();
-  assert(iter.base().moved);
-
-  auto subRange = *iter;
-  assert(subRange.begin().moved);
-  assert(subRange.end().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
+using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
+using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  return 0;
-}
+static_assert(!std::is_constructible_v<SplitIter, SplitView, TracedMoveIter, std::ranges::subrange<TracedMoveIter>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
similarity index 57%
rename from libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 6e1ba912a201b..85d7e908a057c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -6,41 +6,19 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
 // constexpr explicit sentinel(split_view& parent);
 
-#include <cassert>
+// The constructor is now `private` (exposition-only) per P3059R2.
+
 #include <ranges>
-#include <type_traits>
 
-#include "test_iterators.h"
+#include "../types.h"
 
-// test explicit
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-constexpr bool test() {
-  {
-    int buffer[] = {0, 1, 2};
-    Range input{buffer, sentinel_wrapper<int*>(buffer + 3)};
-    SplitView sv(input, -1);
-    auto it = sv.begin();
-
-    auto sent = sv.end();
-    assert(sent != it);
-
-    ++it;
-    assert(sent == it);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
+static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_convertible_v<SplitView&, SplitSent>);

>From 50dc3e4c1aa67aa0e1b582eeecfbc904d7c80ef1 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:57:06 +0300
Subject: [PATCH 26/31] Fixed test

---
 .../range.split/sentinel/ctor.parent.compile.pass.cpp           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 85d7e908a057c..e0e8029c4c538 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -20,5 +20,5 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
 static_assert(!std::is_convertible_v<SplitView&, SplitSent>);

>From d93fdef4663517663ff4169230e6b2333f637d6e Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 12:49:06 +0300
Subject: [PATCH 27/31] Added missing `transform_view` tests

---
 .../ctor.parent.iter.compile.pass.cpp         | 23 ++++++++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 24 +++++++++++++++++++
 .../{iterator => sentinel}/sentinel.pass.cpp  |  0
 3 files changed, 47 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
 rename libcxx/test/std/ranges/range.adaptors/range.transform/{iterator => sentinel}/sentinel.pass.cpp (100%)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..e7841dede5bc2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+//  constexpr iterator(Parent& parent, iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using TransformView = std::ranges::transform_view<MoveOnlyView, PlusOne>;
+using TransformViewBaseIter =  std::ranges::iterator_t<MoveOnlyView>;
+using TransformIter = std::ranges::iterator_t<TransformView>;
+
+static_assert(!std::is_constructible_v<TransformIter, TransformView, TransformViewBaseIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..08be4bbd16667
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
+using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
+using SplitSent = std::ranges::sentinel_t<SplitView>;
+
+static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp
similarity index 100%
rename from libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp

>From 2b952f73eb09938ae7065c16560692152e4f0c50 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 13:32:08 +0300
Subject: [PATCH 28/31] Try to fix the tests

---
 .../iterator/ctor.parent.iter.compile.pass.cpp           | 3 ++-
 .../range.transform/sentinel/ctor.base.compile.pass.cpp  | 9 +++++----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
index 4a013d0072222..bca1b3bda4b24 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -18,8 +18,9 @@
 #include "../types.h"
 
 using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using ViewIter       = std::ranges::iterator_t<View>;
 using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
 using FilterIterator = std::ranges::iterator_t<FilterView>;
 
-static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::constructible_from<FilterIterator, FilterView, ViewIter>);
 static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
index 08be4bbd16667..25a73252bcd2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
@@ -14,11 +14,12 @@
 
 #include <ranges>
 
-#include "../types.h"
-
+#include "test_iterators.h"
+                                                                    
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
+using RangeSent = std::ranges::sentinel_t<Range>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
+static_assert(!std::is_constructible_v<SplitSent, RangeSent>);
+static_assert(!std::is_convertible_v<RangeSent, SplitSent>);

>From d8642b310b66114297deacc294c6ff800821fd1d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 13:34:09 +0300
Subject: [PATCH 29/31] Formatting

---
 .../iterator/ctor.parent.iter.compile.pass.cpp              | 6 +++---
 .../range.transform/sentinel/ctor.base.compile.pass.cpp     | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
index e7841dede5bc2..4bafbfea3e284 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
@@ -16,8 +16,8 @@
 
 #include "../types.h"
 
-using TransformView = std::ranges::transform_view<MoveOnlyView, PlusOne>;
-using TransformViewBaseIter =  std::ranges::iterator_t<MoveOnlyView>;
-using TransformIter = std::ranges::iterator_t<TransformView>;
+using TransformView         = std::ranges::transform_view<MoveOnlyView, PlusOne>;
+using TransformViewBaseIter = std::ranges::iterator_t<MoveOnlyView>;
+using TransformIter         = std::ranges::iterator_t<TransformView>;
 
 static_assert(!std::is_constructible_v<TransformIter, TransformView, TransformViewBaseIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
index 25a73252bcd2e..bfbe0a5820b9e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
@@ -15,7 +15,7 @@
 #include <ranges>
 
 #include "test_iterators.h"
-                                                                    
+
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using RangeSent = std::ranges::sentinel_t<Range>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;

>From 017d251794e85277276dfaf4a7027fd6d969cb7b Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 13:48:32 +0300
Subject: [PATCH 30/31] More fixes

---
 .../iterator/ctor.parent.iter.compile.pass.cpp      |  3 +--
 .../iterator/ctor.parent.iter.compile.pass.cpp      |  2 +-
 .../sentinel/ctor.base.compile.pass.cpp             | 13 +++++++------
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
index bca1b3bda4b24..47deabb7136c6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -22,5 +22,4 @@ using ViewIter       = std::ranges::iterator_t<View>;
 using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
 using FilterIterator = std::ranges::iterator_t<FilterView>;
 
-static_assert(!std::constructible_from<FilterIterator, FilterView, ViewIter>);
-static_assert(!std::convertible_to<FilterView, FilterIterator>);
+static_assert(!std::constructible_from<FilterIterator, FilterView&, ViewIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
index 4bafbfea3e284..37b6016e439fb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
@@ -20,4 +20,4 @@ using TransformView         = std::ranges::transform_view<MoveOnlyView, PlusOne>
 using TransformViewBaseIter = std::ranges::iterator_t<MoveOnlyView>;
 using TransformIter         = std::ranges::iterator_t<TransformView>;
 
-static_assert(!std::is_constructible_v<TransformIter, TransformView, TransformViewBaseIter>);
+static_assert(!std::is_constructible_v<TransformIter, TransformView&, TransformViewBaseIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
index bfbe0a5820b9e..608db6eafe127 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
@@ -15,11 +15,12 @@
 #include <ranges>
 
 #include "test_iterators.h"
+#include "../types.h"
 
-using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
-using RangeSent = std::ranges::sentinel_t<Range>;
-using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
-using SplitSent = std::ranges::sentinel_t<SplitView>;
+using Range         = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
+using BaseSent      = std::ranges::sentinel_t<Range>;
+using TransformView = std::ranges::transform_view<Range, PlusOne>;
+using TransformSent = std::ranges::sentinel_t<TransformView>;
 
-static_assert(!std::is_constructible_v<SplitSent, RangeSent>);
-static_assert(!std::is_convertible_v<RangeSent, SplitSent>);
+static_assert(!std::is_constructible_v<TransformSent, BaseSent>);
+static_assert(!std::is_convertible_v<BaseSent, TransformSent>);

>From ed0783c305b98b66c39d84141c59759bcb2c11bf Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 14:20:32 +0300
Subject: [PATCH 31/31] More fixes

---
 .../range.transform/sentinel/ctor.base.compile.pass.cpp      | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
index 608db6eafe127..b5f134528fef0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
@@ -17,9 +17,8 @@
 #include "test_iterators.h"
 #include "../types.h"
 
-using Range         = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
-using BaseSent      = std::ranges::sentinel_t<Range>;
-using TransformView = std::ranges::transform_view<Range, PlusOne>;
+using BaseSent      = std::ranges::sentinel_t<SizedSentinelView>;
+using TransformView = std::ranges::transform_view<SizedSentinelView, PlusOne>;
 using TransformSent = std::ranges::sentinel_t<TransformView>;
 
 static_assert(!std::is_constructible_v<TransformSent, BaseSent>);


From libcxx-commits at lists.llvm.org  Wed Jul 22 04:31:11 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 04:31:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a60a9ff.f1c1923d.d9163.bac1@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From 145920aa6c68ee172dc65111a6a0cfb99d4c9069 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/26] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..11a0b42ea54f5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 899502422570a..4ae0f6a28637a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -32,11 +33,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -52,21 +66,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -102,12 +120,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -126,23 +146,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -152,23 +178,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From 90d997369a9f358f579ae61963226b1f30c81d5a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/26] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 37c38d25012c6..78e27d9ef9781 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From 7a63b7897f421f0d367057c933db66b3ea0f21e1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/26] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 11a0b42ea54f5..caa7f9d1a2535 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 78e27d9ef9781..640f6e5e916ca 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..8bdde44c1cb8c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -895,6 +895,7 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From e6a881f9199428718c9b93a92dcea056d7e84356 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/26] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 4ae0f6a28637a..c496ecf5ac742 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -49,7 +49,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From 13589231818d564e6ba520c405eae011f72f10fe Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/26] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From d3c6b5e3b74e59c2beaf5b3158db90faef36600c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/26] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c496ecf5ac742..c55de19cd4b59 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -148,7 +148,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From 11cedf33d3fe73d5bad5930a8c4f12df33e0eac8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/26] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 7e6410501ecdccbd78ce814b0e4906a60f379df6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/26] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c55de19cd4b59..0cf50d7ebd1ec 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -162,7 +162,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -180,7 +180,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -195,7 +195,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 640f6e5e916ca..44f1229f859b3 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From f666483df06fffa6f94a403ba051621cf0432415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/26] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index caa7f9d1a2535..b6650a314bd40 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0cf50d7ebd1ec..05b22d04962fa 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,29 +30,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 44f1229f859b3..0a6e3d54b5911 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 8bdde44c1cb8c..f038c9d39a27b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -884,6 +884,7 @@ module std {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -895,7 +896,6 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From f622cc3f082c34d6f0c5cd6e47177664eed48938 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/26] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 1e55b9997f985525c33ac424b94de00d91b36bba Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/26] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 0a6e3d54b5911..2f6e0d5dbd20a 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From 996293d9cfb334a462283e783e2fb2af90bb373c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/26] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From f74f609379e9feab234dd430bc758b19d9b1d46b Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/26] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From 8c0b1998421e1c8e214ee2694544bf3f9d8f5f79 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/26] 14

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <cstring>
+#include <type_traits>
 
 struct WithTailPadding {
   int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From f436e600ce39701927e30d0074019afc4cdb581c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 20:42:14 +0100
Subject: [PATCH 15/26] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e4d83e638ca77..0bafdbb1d7ede 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -136,7 +136,7 @@ void test() {
   {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
-
+    std::atomic<T> a;
     T init = make<T>(10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);

>From 4def68f03c3089677bb5206f56f30e367415ecce Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:13:05 +0100
Subject: [PATCH 16/26] ci

---
 libcxx/include/__atomic/support/gcc.h         |  5 ++--
 .../atomics.types.generic/padding.pass.cpp    | 24 ++++++++++-------
 .../atomics.types.generic/padding.pass.cpp    | 27 ++++++++++---------
 .../atomics.types.generic/padding2.pass.cpp   |  3 ---
 4 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 2f6e0d5dbd20a..b162e2c2a63f0 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -50,9 +50,8 @@ struct __cxx_atomic_base_impl {
     std::__clear_padding_if_needed(__a_value);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
   _Tp __a_value;
 };
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index bf594b45ed54f..6f894fe6cb605 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -44,15 +44,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -61,11 +69,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0bafdbb1d7ede..344d08b493e60 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,9 +7,6 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits
@@ -41,15 +38,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -58,11 +63,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
index 4e17471a2461f..1040f342b8206 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -8,9 +8,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-localization
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits

>From eeecefecde068ea1f6e2f8417b52961192ca8bc4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:29:04 +0100
Subject: [PATCH 17/26] CI

---
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding2.pass.cpp   | 273 ------------------
 3 files changed, 2 insertions(+), 275 deletions(-)
 delete mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 6f894fe6cb605..388ab0867fe38 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 344d08b493e60..f1a534a2916d6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
deleted file mode 100644
index 1040f342b8206..0000000000000
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-localization
-
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
-// CAS should work on types with padding bits
-
-#include <atomic>
-#include <cassert>
-#include <cstring>
-#include <type_traits>
-#include <iostream>
-
-struct Foo {
-  int i;
-  char c;
-};
-
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
-
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
-}
-
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
-}
-
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-#ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
-#else
-  (void)f;
-  (void)pad_byte;
-#endif
-}
-
-void test() {
-  std::cerr << "test 1" << std::endl;
-  {
-    // compare_exchange_strong
-    // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
-    std::cerr << "test 2" << std::endl;
-
-    Foo init = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 3" << std::endl;
-    assert_foo_padding(init, 0xBB);
-    std::cerr << "test 4" << std::endl;
-    a.store(init);
-
-    Foo expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 5" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 6" << std::endl;
-
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 7" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 8" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 9" << std::endl;
-    assert(r);
-    std::cerr << "test 10" << std::endl;
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    std::cerr << "test 11" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 42);
-    std::cerr << "test 12" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 13" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 14" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 15" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 16" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 17" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 18" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 19" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 10" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 21" << std::endl;
-    assert(!r);
-    std::cerr << "test 22" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 23" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 24" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 25" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 10);
-    std::cerr << "test 26" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 27" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 28" << std::endl;
-  }
-
-  {
-    // compare_exchange_weak
-    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 29" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 30" << std::endl;
-    a.store(stored);
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 31" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 32" << std::endl;
-
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 33" << std::endl;
-    assert_foo_padding(original_expected, 0xAA);
-    std::cerr << "test 34" << std::endl;
-
-    bool r                  = false;
-    const auto max_attempts = 100;
-    auto current_attempt    = 0;
-    while (!r) {
-      ++current_attempt;
-      std::cerr << "test 35" << std::endl;
-      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      std::cerr << "test 36" << std::endl;
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
-      std::cerr << "test 37" << std::endl;
-      r = a.compare_exchange_weak(expected, new_value);
-      if (r) {
-        std::cerr << "test 38" << std::endl;
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-        std::cerr << "test 39" << std::endl;
-      } else {
-        // Spurious failure: expected is updated to the current atomic value.
-        std::cerr << "test 40" << std::endl;
-        assert(expected.i == 10);
-        std::cerr << "test 41" << std::endl;
-        assert(expected.c == 'a');
-        std::cerr << "test 45" << std::endl;
-        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
-        std::cerr << "test 43" << std::endl;
-      }
-    }
-
-    Foo loaded = a.load();
-    std::cerr << "test 44" << std::endl;
-    assert(loaded.i == 42);
-    std::cerr << "test 45" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 46" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 47" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 48" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 49" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 50" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 51" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 52" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 53" << std::endl;
-
-    bool r = a.compare_exchange_weak(expected, new_value);
-
-    std::cerr << "test 54" << std::endl;
-    assert(!r);
-    std::cerr << "test 55" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 56" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 57" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 58" << std::endl;
-    Foo loaded = a.load();
-    std::cerr << "test 59" << std::endl;
-    assert(loaded.i == 10);
-    std::cerr << "test 60" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 61" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 62" << std::endl;
-  }
-
-  {
-    // Types with unique object representations skip the padding-clearing path.
-    std::atomic<int> a(1);
-    int expected = 1;
-    std::cerr << "test 63" << std::endl;
-    assert(a.compare_exchange_strong(expected, 2));
-    std::cerr << "test 64" << std::endl;
-    assert(expected == 1);
-    std::cerr << "test 65" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 66" << std::endl;
-
-    expected = 3;
-    assert(!a.compare_exchange_strong(expected, 4));
-    std::cerr << "test 67" << std::endl;
-    assert(expected == 2);
-    std::cerr << "test 68" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 69" << std::endl;
-  }
-}
-
-int main(int, char**) {
-  // TODO(LLVM-23): Switch to XFAIL: clang-22
-  std::cerr << "main 1" << std::endl;
-#if __has_builtin(__builtin_clear_padding)
-  std::cerr << "main 2" << std::endl;
-  test();
-  std::cerr << "main 3" << std::endl;
-#endif
-  std::cerr << "main 4" << std::endl;
-  return 0;
-}

>From bc539fb8d58e16f24a5ccb8bdd2351475e7d4391 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:52:15 +0100
Subject: [PATCH 18/26] ci

---
 libcxx/include/__atomic/support/c11.h | 13 ++++++++-----
 libcxx/include/__atomic/support/gcc.h |  9 +++++++--
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 05b22d04962fa..d43a2c67f950a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -30,13 +30,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
 
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b162e2c2a63f0..d2d4919d54614 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,12 +46,17 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
       : __a_value(std::__clear_padding_if_needed(__value)) {}
+
   _Tp __a_value;
 };
 

>From 30a056210700cd8f89b1c0ca6991f272a0ebba43 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:26:08 +0100
Subject: [PATCH 19/26] review

---
 libcxx/include/__atomic/clear_padding.h            | 14 ++++++--------
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 5bc77ff4401ab..f920ef39977b7 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -35,17 +35,16 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, float>>,
            _Not<is_same<_Tp, double>>> {};
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(std::addressof(__obj));
-    }
-  }
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+  return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
+}
+
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
@@ -67,7 +66,6 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 388ab0867fe38..45fb083cccf3f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index f1a534a2916d6..5ffba6ca844c5 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();

>From 9b771605f016811c06b3f600e48f45f3268a5269 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:33:16 +0100
Subject: [PATCH 20/26] release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx20Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..a689379321496 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange (`Github <https://llvm.org/PR76180>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 5539767ab800b..8e394b4bc4b3a 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -31,7 +31,7 @@
 "`P0458R2 <https://wg21.link/P0458R2>`__","Checking for Existence of an Element in Associative Containers","2018-06 (Rapperswil)","|Complete|","13","`#104029 <https://github.com/llvm/llvm-project/issues/104029>`__",""
 "`P0475R1 <https://wg21.link/P0475R1>`__","LWG 2511: guaranteed copy elision for piecewise construction","2018-06 (Rapperswil)","|Complete|","","`#104031 <https://github.com/llvm/llvm-project/issues/104031>`__",""
 "`P0476R2 <https://wg21.link/P0476R2>`__","Bit-casting object representations","2018-06 (Rapperswil)","|Complete|","14","`#104087 <https://github.com/llvm/llvm-project/issues/104087>`__",""
-"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","","","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
+"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","|Complete|","24","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
 "`P0542R5 <https://wg21.link/P0542R5>`__","Support for contract based programming in C++","2018-06 (Rapperswil)","|Nothing To Do|","n/a","`#104089 <https://github.com/llvm/llvm-project/issues/104089>`__","Pulled at the 2019-07 meeting in Cologne"
 "`P0556R3 <https://wg21.link/P0556R3>`__","Integral power-of-2 operations","2018-06 (Rapperswil)","|Complete|","9","`#104090 <https://github.com/llvm/llvm-project/issues/104090>`__",""
 "`P0619R4 <https://wg21.link/P0619R4>`__","Reviewing Deprecated Facilities of C++17 for C++20","2018-06 (Rapperswil)","|Complete|","20","`#99985 <https://github.com/llvm/llvm-project/issues/99985>`__","Removed headers are still provided as an extension, but with deprecation warnings."

>From 46a4ba35dbc30153f7868e9796d44f88512ae602 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 17:01:57 +0100
Subject: [PATCH 21/26] ci

---
 libcxx/include/__atomic/clear_padding.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index f920ef39977b7..3099faa8ca6d1 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/has_unique_object_representation.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>

>From 5f475333df1aeee9640364c9fb2f734c731edb7e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 19:03:39 +0100
Subject: [PATCH 22/26] ci

---
 .../atomics.types.generic/padding.pass.cpp    | 19 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 52 ++++++++++++-------
 2 files changed, 44 insertions(+), 27 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 45fb083cccf3f..116a860b3192c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -57,11 +57,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -86,7 +84,8 @@ void test() {
 
   {
     // atomic(T);
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     std::atomic<T> a(init);
     T loaded = a.load();
@@ -97,7 +96,8 @@ void test() {
   {
     // atomic::store
     std::atomic<T> a;
-    T value = make<T>(5, 'x', 0xAB);
+    T value;
+    initialize(value, 5, 'x', 0xAB);
     assert_padding(value, 0xAB);
     a.store(value);
     T loaded = a.load();
@@ -107,10 +107,12 @@ void test() {
   }
   {
     // atomic::exchange
-    T initial = make<T>(1, 'a', 0x00);
+    T initial;
+    initialize(initial, 1, 'a', 0x00);
     assert_padding(initial, 0x00);
     std::atomic<T> a(initial);
-    T new_val = make<T>(2, 'b', 0xCD);
+    T new_val;
+    initialize(new_val, 2, 'b', 0xCD);
     assert_padding(new_val, 0xCD);
     T old = a.exchange(new_val);
     assert(old.i == 1);
@@ -124,7 +126,8 @@ void test() {
   {
     // atomic_init
     std::atomic<T> a;
-    T init = make<T>(7, 'z', 0xEF);
+    T init;
+    initialize(init, 7, 'z', 0xEF);
     assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
     T loaded = a.load();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 5ffba6ca844c5..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -51,11 +51,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -84,17 +82,20 @@ void test() {
     // CAS should succeed when only padding differs in expected; expected is unchanged.
     std::atomic<T> a;
 
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0xAA);
+    T expected;
+    initialize(expected, 10, 'a', 0xAA);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -112,13 +113,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -138,17 +142,20 @@ void test() {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
     std::atomic<T> a;
-    T init = make<T>(10, 'a', 0x00);
+    T init;
+    initialize(init, 10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0x00);
+    T expected;
+    initialize(expected, 10, 'a', 0x00);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0x42);
+    T new_value;
+    initialize(new_value, 42, 'b', 0x42);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,14 +173,17 @@ void test() {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
-    T original_expected = make<T>(10, 'a', 0xAA);
+    T original_expected;
+    initialize(original_expected, 10, 'a', 0xAA);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -182,7 +192,8 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      T expected = make<T>(10, 'a', 0xAA);
+      T expected;
+      initialize(expected, 10, 'a', 0xAA);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -207,13 +218,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);

>From 704a54eb4f1f7989931b7e25ad87abbb60ad6021 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:07:26 +0100
Subject: [PATCH 23/26] debug

---
 .../atomics.types.generic/padding.pass.cpp    | 64 ++++++++++++++++++-
 1 file changed, 62 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..3b7f13b366e96 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,6 +15,7 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
+#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -70,11 +71,19 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
+void print_memory(const T& obj) {
+  for (size_t i = 0; i < sizeof(T); ++i) {
+    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
+  }
+  std::cerr << std::endl;
+}
+
 template <class T>
 void test() {
   {
@@ -84,11 +93,15 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
+    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
+    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -96,6 +109,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -106,6 +121,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test1 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -115,14 +132,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -131,11 +154,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test2 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test2 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -144,11 +171,15 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
+    std::cerr << "test3 init padding should be 0x00: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
+    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -156,6 +187,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
+    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,6 +199,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test3 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -175,15 +210,21 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
+    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
+    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -194,6 +235,8 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
+      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
+      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -203,6 +246,8 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        std::cerr << "test4 expected padding should be 0: " << std::endl;
+        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -211,6 +256,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test4 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -220,14 +267,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -236,11 +289,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test5 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test5 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -262,10 +319,13 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
+  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
+  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
+  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 0;
+  return 1;
 }

>From 8c622e41d2428f630855f0aaa215b1463d8fe4ca Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:38:19 +0100
Subject: [PATCH 24/26] Revert "debug"

This reverts commit 8be9a83ee040faff62f0756555890484bf502a31.
---
 .../atomics.types.generic/padding.pass.cpp    | 64 +------------------
 1 file changed, 2 insertions(+), 62 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 3b7f13b366e96..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,7 +15,6 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
-#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -71,19 +70,11 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)obj;
+  (void)f;
   (void)pad_byte;
 #endif
 }
 
-template <class T>
-void print_memory(const T& obj) {
-  for (size_t i = 0; i < sizeof(T); ++i) {
-    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
-  }
-  std::cerr << std::endl;
-}
-
 template <class T>
 void test() {
   {
@@ -93,15 +84,11 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
-    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
-    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -109,8 +96,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -121,8 +106,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test1 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -132,20 +115,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -154,15 +131,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test2 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test2 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -171,15 +144,11 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
-    std::cerr << "test3 init padding should be 0x00: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
-    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -187,8 +156,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
-    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -199,8 +166,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test3 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -210,21 +175,15 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
-    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
-    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -235,8 +194,6 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
-      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
-      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -246,8 +203,6 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        std::cerr << "test4 expected padding should be 0: " << std::endl;
-        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -256,8 +211,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test4 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -267,20 +220,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -289,15 +236,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test5 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test5 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -319,13 +262,10 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
-  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
-  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
-  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 1;
+  return 0;
 }

>From 789912ba84ccbb3a3efea236fde31835783a7415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:44:00 +0100
Subject: [PATCH 25/26] test ci

---
 .../atomics.types.generic/padding.pass.cpp    | 20 ++++++++++++++-----
 .../atomics.types.generic/padding.pass.cpp    | 12 +----------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 116a860b3192c..cd41f2893864c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -71,6 +71,16 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
+void assert_padding(const std::atomic<T>& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  T loaded     = obj.load();
+  set(reference, loaded.i, loaded.c);
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
+}
+
 template <class T>
 void test() {
   {
@@ -79,7 +89,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 
   {
@@ -91,7 +101,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::store
@@ -103,7 +113,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::exchange
@@ -121,7 +131,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic_init
@@ -133,7 +143,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..10c15523cd70b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -70,7 +70,7 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
@@ -105,8 +105,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -135,8 +133,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
   {
     // compare_exchange_strong
@@ -165,8 +161,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -210,8 +204,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -240,8 +232,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {

>From 81770cda9f2171565c1c2980ad8306b9665752b2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:18:49 +0100
Subject: [PATCH 26/26] padding

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index cd41f2893864c..a178ce74578ec 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -127,7 +127,6 @@ void test() {
     T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_padding(old, 0);
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');


From libcxx-commits at lists.llvm.org  Wed Jul 22 04:31:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 04:31:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a60aa2b.e55dc341.dbc09.378f@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/186692

>From ca0f8811f81d47e4866fd8ad9d937cb2a0c539be Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 12 Apr 2026 09:35:44 +0100
Subject: [PATCH 01/21] [libcxx] Implement `std::constant_wrapper`

ci

ci

review comments

review comments

ci

review comments
---
 .../containers/views/views.span/span.cons/deduct.pass.cpp  | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index 35d9b36f824aa..f5b590cd9631d 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -88,6 +88,13 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
+
+  {
+    std::span s{std::begin(arr), std::cw<3>};
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+    assert(s.size() == std::size(arr));
+    assert(s.data() == std::data(arr));
+  }
 #endif
 }
 

>From b3ee4d47aa3071d6e52b6921375c69945abed3b8 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 8 Jun 2024 17:32:42 -0700
Subject: [PATCH 02/21] [libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

rebase + put in experimental + rename nontype to constant_arg

tests

remove experimental

test for constant_arg_ref

test for constant_arg_ptr

remove fun_ptr member

invoke

format

constexpr function ptr

double unwrapping

P3948R1

tests for new papers

format

lwg issue

lwg issues

review comment

remove constexpr

optimise small types

ci

ci

ci

ci
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/23.rst               |   3 +
 libcxx/docs/Status/Cxx26Issues.csv            |   4 +-
 libcxx/docs/Status/Cxx26Papers.csv            |   6 +-
 libcxx/include/CMakeLists.txt                 |   3 +
 libcxx/include/__functional/function_ref.h    |  33 ++
 .../__functional/function_ref_common.h        | 126 ++++++
 .../include/__functional/function_ref_impl.h  | 184 +++++++++
 libcxx/include/functional                     |   9 +
 libcxx/include/module.modulemap.in            |   3 +
 libcxx/include/version                        |   4 +-
 libcxx/modules/std/functional.inc             |   4 +
 .../assert.constant_arg_ptr.pass.cpp          |  31 ++
 .../assert.function_ptr.pass.cpp              |  26 ++
 .../constant_arg.mandates.verify.cpp          |  60 +++
 .../internal_call_signature.pass.cpp          | 136 +++++++
 .../functional.version.compile.pass.cpp       |  16 +-
 .../version.version.compile.pass.cpp          |  16 +-
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     | 107 +++++
 .../func.wrap.ref.ctor/assign.delete.pass.cpp | 115 ++++++
 .../constant_wrapper.pass.cpp                 | 175 ++++++++
 .../constant_wrapper_ptr.pass.cpp             | 359 +++++++++++++++++
 .../constant_wrapper_ref.pass.cpp             | 373 ++++++++++++++++++
 .../func.wrap.ref.ctor/copy.pass.cpp          | 131 ++++++
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   | 158 ++++++++
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  | 149 +++++++
 .../func.wrap.ref.ctor/move.pass.cpp          | 129 ++++++
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 156 ++++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 316 +++++++++++++++
 .../func.wrap.ref.inv/invoke.pass.cpp         | 362 +++++++++++++++++
 .../trivially_copyable.compile.pass.cpp       |  33 ++
 .../generate_feature_test_macro_components.py |   3 +-
 32 files changed, 3200 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/include/__functional/function_ref.h
 create mode 100644 libcxx/include/__functional/function_ref_common.h
 create mode 100644 libcxx/include/__functional/function_ref_impl.h
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 00665332a9ed7..108702e71dec7 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -480,7 +480,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_fstream_native_handle``                        ``202306L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_function_ref``                                 *unimplemented*
+    ``__cpp_lib_function_ref``                                 ``202603L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_generate_random``                              *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index f7a8e5b04f1ea..5d295453dd79f 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,9 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 5b046b69d14bd..55c642fd15ce1 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","","","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","","","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..530575f4973da 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."
@@ -192,9 +192,9 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","","","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","","","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..a0d5cf70d267d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -438,6 +438,9 @@ set(files
   __functional/compose.h
   __functional/default_searcher.h
   __functional/function.h
+  __functional/function_ref.h
+  __functional/function_ref_common.h
+  __functional/function_ref_impl.h
   __functional/hash.h
   __functional/identity.h
   __functional/invoke.h
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
new file mode 100644
index 0000000000000..b7fd3a4a2c79f
--- /dev/null
+++ b/libcxx/include/__functional/function_ref.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+
+#include <__config>
+#include <__functional/function_ref_common.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26
+// NOLINTBEGIN(readability-duplicate-include)
+
+#  define _LIBCPP_FUNCTION_REF_CV
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+#  define _LIBCPP_FUNCTION_REF_CV const
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+// NOLINTEND(readability-duplicate-include)
+#endif // _LIBCPP_STD_VER >= 26
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
new file mode 100644
index 0000000000000..61979231b56e8
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_COMMON_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+
+#include <__config>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/constant_wrapper.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization : false_type {};
+
+// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+// pointers
+// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
+// supported
+union __storage_func_ref_t {
+  void* __obj_ptr_;
+  void const* __obj_const_ptr_;
+  void (*__fn_ptr_)();
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        __obj_const_ptr_ = __ptr;
+      } else {
+        __obj_ptr_ = __ptr;
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
+    }
+  }
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
+      } else {
+        return static_cast<_Tp*>(__storage.__obj_ptr_);
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
+    }
+  }
+};
+
+template <class _Fp, class _Tp>
+struct __function_ref_bind {};
+
+// F is of the form R(*)(G, A...) noexcept(E) for a type G.
+template <bool _Noexcept, class _Tp, class _Rp, class _Gp, class... _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept(_Noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_Noexcept);
+};
+
+template <class _Tp, class _Mp, class _Gp>
+  requires is_object_v<_Mp>
+struct __function_ref_bind<_Mp _Gp::*, _Tp> {
+  using type _LIBCPP_NODEBUG = invoke_result_t<_Mp _Gp::*, _Tp&>() noexcept;
+};
+
+template <class _Fp, class _Tp>
+using __function_ref_bind_t _LIBCPP_NODEBUG = __function_ref_bind<_Fp, _Tp>::type;
+
+template <class _Fp>
+  requires is_function_v<_Fp>
+function_ref(_Fp*) -> function_ref<_Fp>;
+
+template <auto _Cw, class _Fn>
+  requires is_function_v<remove_pointer_t<_Fn>>
+function_ref(constant_wrapper<_Cw, _Fn>) -> function_ref<remove_pointer_t<_Fn>>;
+
+template <auto _Cw, class _Fn, class _Tp>
+function_ref(constant_wrapper<_Cw, _Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<_Fn, _Tp&>>;
+
+template <class>
+constexpr bool __is_constant_wrapper = false;
+
+template <auto _Value>
+constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
+
+template <class _Arg>
+struct __function_ref_arg_fwd {
+  using type _LIBCPP_NODEBUG = _Arg&&;
+};
+
+template <class _Arg>
+  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
+struct __function_ref_arg_fwd<_Arg> {
+  using type _LIBCPP_NODEBUG = _Arg;
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
new file mode 100644
index 0000000000000..a4c98d3031602
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -0,0 +1,184 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
+// and generates multiple versions of std::function_ref
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#include <__functional/invoke.h>
+#include <__memory/addressof.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/constant_wrapper.h>
+#include <__utility/forward.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#  error This header should only be included from function_ref.h
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
+    _NoExcept1,
+    _Rp,
+    _ArgTypes...>
+    : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
+
+template <class _Rp, class... _ArgTypes, bool __is_noexcept>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
+private:
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using =
+      _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
+          value;
+
+  template <class _Fn>
+  static constexpr bool __is_convertible_from_specialization_v =
+      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+
+  template <class... _Tp>
+  friend class function_ref;
+
+  template <class _Arg>
+  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+
+  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+
+  using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
+
+  __storage_t __storage_;
+  __call_t __call_;
+
+public:
+  template <class _Fp>
+    requires is_function_v<_Fp> && __is_invocable_using<_Fp>
+  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+      : __storage_(__fn_ptr),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+  }
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {}
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && __is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(__obj.__storage_), __call_(__obj.__call_) {}
+
+  template <auto _Cw, class _Fn>
+    requires __is_invocable_using<const _Fn&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
+      : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+    if constexpr (sizeof...(_ArgTypes) > 0 && (__constexpr_param<remove_cvref_t<_ArgTypes>> && ...)) {
+      static_assert(
+          !requires {
+            typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
+          },
+          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
+          "and produces a result with a structural type");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
+    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Tp>
+    requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
+      : __storage_(__obj_ptr_),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+
+    if constexpr (is_member_pointer_v<_Fn>) {
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(const function_ref&) noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+  template <class _Tp>
+    requires(!__is_convertible_from_specialization_v<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_wrapper<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
+
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
+    return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
+  }
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 9d588231d548d..df79d616a57e2 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -487,6 +487,11 @@ template <class  R, class ... ArgTypes>
 template <class  R, class ... ArgTypes>
   void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
 
+// [func.wrap.ref], non-owning wrapper
+template<class... S> class function_ref;                              // freestanding, not defined, since C++26
+template<class R, class... ArgTypes>
+  class function_ref<R(ArgTypes...) cv noexcept(noex)>;               // freestanding, since C++26
+
 template <class T> struct hash;
 
 template <> struct hash<bool>;
@@ -571,6 +576,10 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 #    include <__type_traits/unwrap_ref.h>
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+#    include <__functional/function_ref.h>
+#  endif
+
 #  include <version>
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..cb29fac536046 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1438,6 +1438,9 @@ module std {
     }
     module default_searcher             { header "__functional/default_searcher.h" }
     module function                     { header "__functional/function.h" }
+    module function_ref                 { header "__functional/function_ref.h" }
+    module function_ref_common          { header "__functional/function_ref_common.h" }
+    module function_ref_impl            { textual header "__functional/function_ref_impl.h" }
     module hash                         { header "__functional/hash.h" }
     module identity                     { header "__functional/identity.h" }
     module invoke                       { header "__functional/invoke.h" }
diff --git a/libcxx/include/version b/libcxx/include/version
index 7545d187ca7f1..9160c51835d6f 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -131,7 +131,7 @@ __cpp_lib_freestanding_optional                         202311L <optional>
 __cpp_lib_freestanding_string_view                      202311L <string_view>
 __cpp_lib_freestanding_variant                          202311L <variant>
 __cpp_lib_fstream_native_handle                         202306L <fstream>
-__cpp_lib_function_ref                                  202306L <functional>
+__cpp_lib_function_ref                                  202603L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
@@ -593,7 +593,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 #   define __cpp_lib_fstream_native_handle              202306L
 # endif
-// # define __cpp_lib_function_ref                         202306L
+# define __cpp_lib_function_ref                         202603L
 // # define __cpp_lib_generate_random                      202403L
 // # define __cpp_lib_hazard_pointer                       202306L
 // # define __cpp_lib_inplace_vector                       202406L
diff --git a/libcxx/modules/std/functional.inc b/libcxx/modules/std/functional.inc
index 9ef8f584611fc..be804b19f75f8 100644
--- a/libcxx/modules/std/functional.inc
+++ b/libcxx/modules/std/functional.inc
@@ -95,6 +95,10 @@ export namespace std {
 
   using std::function;
 
+#if _LIBCPP_STD_VER >= 26
+  using std::function_ref;
+#endif // _LIBCPP_STD_VER >= 26
+
   using std::swap;
 
   using std::operator==;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
new file mode 100644
index 0000000000000..9703de0a59c2c
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+// Preconditions: If is_member_pointer_v<F> is true, obj is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+struct A {
+  void f() {}
+};
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(std::function_ref<void()>(std::cw<&A::f>, static_cast<A*>(nullptr)),
+                             "the object pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
new file mode 100644
index 0000000000000..e69776d0e3c90
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<class F> function_ref(F* f) noexcept;
+// Preconditions: f is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::function_ref<void()>(static_cast<void (*)()>(nullptr)), "the function pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
new file mode 100644
index 0000000000000..519957eec6878
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto c, class F>
+//   constexpr function_ref(constant_wrapper<c, F>) noexcept;
+// template<auto c, class F, class U>
+//   constexpr function_ref(constant_wrapper<c, F>, U&& obj) noexcept;
+// template<auto c, class F, class T>
+//   constexpr function_ref(constant_wrapper<c, F>, cv T* obj) noexcept;
+
+// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
+
+// For the first overload,
+// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+
+#include <functional>
+#include <utility>
+
+struct A {
+  void f();
+};
+
+struct B {
+  constexpr int operator()(std::constant_wrapper<42>) const { return 42; }
+  constexpr int operator()(int) const { return 42; }
+};
+
+// clang-format off
+void test() {
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+
+  int i;
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  A a;
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+}
+// clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
new file mode 100644
index 0000000000000..05756cfc3b3b8
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Track number of moves
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+struct Small {};
+
+struct Big {
+  char c[128];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
+// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
+template <>
+class std::function_ref<int> {
+  using storage = std::__storage_func_ref_t;
+  void test() {
+    {
+      // by value small argument
+      // the internal function should pass by value
+      using call = std::function_ref<void(Small)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
+    }
+    {
+      // by value big argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Big)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
+    }
+    {
+      // by value non-trivial small argument
+      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
+      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
+    }
+    {
+      // by lvalue reference argument
+      // the internal function should pass by lvalue reference
+      using call = std::function_ref<void(Small&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
+    }
+    {
+      // by rvalue reference argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Small&&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
+    }
+  }
+};
+
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
+void test() {
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In this case, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index b7b7d0334830a..bcc453880c7e1 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -494,17 +494,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_invoke
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 9e987346c599b..3af3d6694c6bf 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
@@ -7132,17 +7132,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_gcd_lcm
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
new file mode 100644
index 0000000000000..029cb19d54f6a
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+int fn(int, float) { return 42; }
+
+int fn_noexcept(int, float) noexcept { return 42; }
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem(int, float) { return 42; }
+  int fn_mem_ref(int, float) & { return 42; }
+  int fn_mem_const(int, float) const { return 42; }
+  int fn_mem_const_ref(int, float) const& { return 42; }
+  int fn_mem_noexcept(int, float) noexcept { return 42; }
+};
+
+void test() {
+  // template<class F>
+  //  function_ref(F*) -> function_ref<F>;
+  {
+    std::function_ref fn_ref = fn;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = fn_noexcept;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F>
+  //  function_ref(constant_wrapper<c, F>) -> function_ref<...>;
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn_noexcept>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F, class T>
+  //  function_ref(constant_wrapper<c, F>, T&&) -> function_ref<...>;
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
+  }
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn_noexcept>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float) noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&() noexcept>>);
+  }
+  {
+    const S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&() noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_noexcept>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
new file mode 100644
index 0000000000000..b4df4392a2ade
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class T> function_ref& operator=(T) = delete;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - T is not the same type as function_ref,
+// - is_pointer_v<T> is false, and
+// - T is not a specialization of constant_wrapper.
+
+// non const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[] {}>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
+
+// const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[] { return 42; }>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[](int) { return 42; }>>);
+
+// non-const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[] noexcept {} >>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+// const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[] noexcept {}>>);
+static_assert(
+    !std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+int forty_two() { return 42; }
+
+void test() {
+  {
+    std::function_ref<int()> f(std::cw<[] { return 41; }>);
+    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = &forty_two;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const noexcept
+    std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
new file mode 100644
index 0000000000000..4b425ee03e286
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
@@ -0,0 +1,175 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f> constexpr function_ref(constant_wrapper<f>) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&> is true.
+
+auto l1          = [] {};
+auto l1_noexcept = [] noexcept {};
+auto l2          = [](int) {};
+auto l2_noexcept = [](int) noexcept {};
+
+struct NonConstInvocable {
+  void operator()() noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+// const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    std::function_ref<void()> f = std::cw<[] {}>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
new file mode 100644
index 0000000000000..dd6e10e8a3289
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -0,0 +1,359 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&, cv T*> is true.
+
+auto l1                = [](int*) {};
+auto l1_const          = [](const int*) {};
+auto l1_noexcept       = [](int*) noexcept {};
+auto l1_const_noexcept = [](const int*) noexcept {};
+auto l2                = [](int*, double) {};
+auto l2_const          = [](const int*, double) {};
+auto l2_noexcept       = [](int*, double) noexcept {};
+auto l2_const_noexcept = [](const int*, double) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(int*) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, const int*>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2_const>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const noexcept>,
+                                      std::constant_wrapper<l2_const_noexcept>,
+                                      int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_const_noexcept>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<l1_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const noexcept>,
+                                              std::constant_wrapper<l2_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<&A::f_const_noexcept>,
+                                              A*>);
+
+double f1(const int* x, double y) noexcept { return *x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int*) {}>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int*) {}>, &i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int* j, double d) {
+          *j = 5;
+          return *j + d;
+        }>,
+        &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
new file mode 100644
index 0000000000000..485fa95ca2998
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -0,0 +1,373 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class U>
+//   constexpr function_ref(constant_wrapper<f>, U&& obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_rvalue_reference_v<U&&> is false, and
+// - is-invocable-using<const F&, cv T&> is true.
+
+auto l1          = [](int) {};
+auto l1_noexcept = [](int) noexcept {};
+auto l2          = [](int, double) {};
+auto l2_noexcept = [](int, double) noexcept {};
+auto l3          = [](int&) {};
+auto l3_noexcept = [](int&) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(long) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&&>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int) {}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int) {}>, i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int& j, double d) {
+          j = 5;
+          return j + d;
+        }>,
+        i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
new file mode 100644
index 0000000000000..61b70a559d786
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
new file mode 100644
index 0000000000000..1a8b26f86c8be
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
new file mode 100644
index 0000000000000..f142b81367ef1
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> function_ref(F* f) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_function_v<F> is true, and
+// - is-invocable-using<F> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (*)(int)>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A (*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, A)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+
+// const noexcept(true)
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, A) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              void (*)(int, double) noexcept>);
+
+int fn() { return 42; }
+
+int fn_maythrow(int i, A a) { return i - a.i; }
+int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+
+void foo(int) {}
+void bar(int) noexcept {}
+struct Int {
+  int i;
+  Int(int ii) noexcept : i(ii) {}
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+void test() {
+  {
+    // simple case
+    std::function_ref<int()> f(&fn);
+    assert(f() == 42);
+  }
+  {
+    // explicit(false)
+    std::function_ref<int()> f = &fn;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int(int, A)> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // noexcept
+    std::function_ref<int(int, A) noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // const
+    auto l = [](int x, int y, int z) { return x + y - z; };
+    std::function_ref<int(int, int, int) const> f(+l);
+    assert(f(2, 3, 4) == 1);
+  }
+  {
+    // const noexcept
+    std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+
+  {
+    std::function_ref<Int(int, int, int)> f(&needs_conversion);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(&needs_conversion);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(&needs_conversion);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
+    assert(f4(1, 2, 3).i == 6);
+
+    {
+      std::function_ref r1 = foo;
+      std::function_ref r2 = bar;
+      r1                   = r2; // ok
+    }
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
new file mode 100644
index 0000000000000..0f27bba821391
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
new file mode 100644
index 0000000000000..b40dfa9f2cf4b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
new file mode 100644
index 0000000000000..56d6dd3b7109b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -0,0 +1,316 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> constexpr function_ref(F&&) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - remove_cvref_t<F> is not the same type as function_ref,
+// - is_member_pointer_v<T> is false, and
+// - is-invocable-using<cv T&> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+constexpr auto l1 = [] {};
+using L1          = std::remove_cvref_t<decltype(l1)>;
+
+constexpr auto l2 = [] { return A{5}; };
+using L2          = std::remove_cvref_t<decltype(l2)>;
+
+constexpr auto l2_noexcept = [] noexcept { return A{5}; };
+using L2Noexcept           = std::remove_cvref_t<decltype(l2_noexcept)>;
+
+constexpr auto l3 = [](int x, double d) { return x + d; };
+using L3          = std::remove_cvref_t<decltype(l3)>;
+
+constexpr auto l3_noexcept = [](int x, double d) noexcept { return x + d; };
+using L3Noexcept           = std::remove_cvref_t<decltype(l3_noexcept)>;
+
+struct NonConstInvocable {
+  int i;
+
+  int operator()(int x, double y) {
+    ++i;
+    return x + y + i;
+  }
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L2&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2&>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2 const&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const>, std::function_ref<int(int, double) const>>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(int, double)>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double)>, const NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>,
+                                              std::function_ref<int(int, double) const>>);
+
+// const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                      std::function_ref<int(int, double) const noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              std::function_ref<int(int, double) const noexcept>>);
+
+struct F {
+  int i;
+  int operator()(auto&&...) { return 5 + i; }
+
+  int operator()(auto&&...) const { return 6 + i; }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int fn() { return 5; }
+constexpr auto fn_noexcept = []() noexcept { return 6; };
+
+const auto one = []() noexcept { return 1; };
+const auto two = []() noexcept { return 2; };
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(l1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit(false)
+    std::function_ref<void()> f = l1;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<A() noexcept> f(l2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      auto a = f();
+      assert(a.i == 5);
+    }
+  }
+  {
+    // const
+    std::function_ref<double(int, double) const> f(l3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(l3_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // no copies of original callable
+    auto local = [i = 5] mutable { return i++; };
+    std::function_ref<int()> f(local);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 5);
+      assert(local() == 6);
+      assert(f() == 7);
+    }
+  }
+  {
+    // const correctness
+    F f{5};
+
+    std::function_ref<int()> f1(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1() == 10);
+      assert(std::as_const(f1)() == 10);
+    }
+
+    std::function_ref<int() const> f2(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 11);
+      assert(std::as_const(f2)() == 11);
+    }
+  }
+  {
+    // with conversions
+    NeedsConversion c{};
+
+    std::function_ref<Int(int, int, int)> f(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  {
+    // noexcept conversion
+    std::function_ref<int() noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const conversion
+    std::function_ref<int() const> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() noexcept> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to const conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() const> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // P3961R1 Less double indirection in function_ref
+    // double unwrapping
+    std::function_ref<int() const noexcept> f1(std::cw<one>);
+    std::function_ref<int()> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 1 || f2() == 2);
+      LIBCPP_ASSERT(f2() == 1);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
new file mode 100644
index 0000000000000..1cd0c769ce4d3
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -0,0 +1,362 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// R operator()(ArgTypes... args) const noexcept(noex);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "MoveOnly.h"
+
+template <class T, class... Args>
+concept ConstInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) };
+};
+
+template <class T, class... Args>
+concept ConstNoexceptInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) } noexcept;
+};
+
+static_assert(ConstInvocable<std::function_ref<void()>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void()>>);
+
+static_assert(ConstInvocable<std::function_ref<void() noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() noexcept>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void() const>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() const noexcept>>);
+
+struct S {
+  int data = 42;
+  int operator()(int& x) const noexcept { return data + x; }
+  int operator()(const int& x) const noexcept { return data + x + 1; }
+  int operator()(int&& x) const noexcept { return data + x + 2; }
+  int operator()(const int&& x) const noexcept { return data + x + 3; }
+};
+
+struct S2 {
+  double operator()(int x, int y, int z) noexcept { return x + y + z; }
+
+  double operator()(int x, int y, int z) const noexcept { return x + y + z + 1; }
+};
+
+struct Big {
+  char c[128];
+};
+
+struct S3Ref {
+  char operator()(Big& b) const noexcept { return b.c[0]; }
+};
+
+struct S3Value {
+  char operator()(Big b) const noexcept { return b.c[0]; }
+};
+
+void test_default() {
+  {
+    std::function_ref<void()> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&)> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly)> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int)> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&)> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&)> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&)> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&)> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int)> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int)> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&)> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const() {
+  {
+    std::function_ref<void() const> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_noexcept() {
+  {
+    std::function_ref<void() noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const_noexcept() {
+  {
+    std::function_ref<void() const noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test() {
+  test_default();
+  test_const();
+  test_noexcept();
+  test_const_noexcept();
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
new file mode 100644
index 0000000000000..72e26f13868ea
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
+#include <concepts>
+#include <functional>
+#include <type_traits>
+
+struct A {
+  A() {}
+  A(const A&) {}
+  ~A() {}
+};
+
+static_assert(std::copyable<std::function_ref<A(const A&)>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&)>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) noexcept>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const noexcept>>);
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 13a5dab01b74e..145be8c4f33cd 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -707,10 +707,9 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_function_ref",
             "values": {
-                "c++26": 202306  # P0792R14 function_ref: a type-erased callable reference
+                "c++26": 202603  # P3948R1 constant_wrapper is the only tool needed for passing constant expressions via function arguments
             },
             "headers": ["functional"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_gcd_lcm",

>From a7943a7b1bded1f816e34d60a0e06d3e204ea274 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 25 Apr 2026 14:20:04 +0100
Subject: [PATCH 03/21] review comments

---
 .../__functional/function_ref_common.h        | 22 ++++++-------------
 .../include/__functional/function_ref_impl.h  |  2 +-
 .../internal_call_signature.pass.cpp          |  2 +-
 .../constant_wrapper_ref.pass.cpp             |  8 +++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 12 ++++++++++
 5 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 61979231b56e8..72ded36d65107 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -16,6 +16,7 @@
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
 
@@ -37,21 +38,16 @@ struct __is_convertible_from_specialization : false_type {};
 // pointers
 // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
 // supported
-union __storage_func_ref_t {
+union __function_ref_storage {
   void* __obj_ptr_;
-  void const* __obj_const_ptr_;
   void (*__fn_ptr_)();
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage() noexcept : __obj_ptr_(nullptr) {}
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage(_Tp* __ptr) noexcept {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        __obj_const_ptr_ = __ptr;
-      } else {
-        __obj_ptr_ = __ptr;
-      }
+      __obj_ptr_ = const_cast<remove_cv_t<_Tp>*>(__ptr);
     } else {
       static_assert(is_function_v<_Tp>);
       __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
@@ -59,13 +55,9 @@ union __storage_func_ref_t {
   }
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__function_ref_storage __storage) {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
-      } else {
-        return static_cast<_Tp*>(__storage.__obj_ptr_);
-      }
+      return static_cast<_Tp*>(__storage.__obj_ptr_);
     } else {
       static_assert(is_function_v<_Tp>);
       return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index a4c98d3031602..36841d6745955 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -72,7 +72,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Arg>
   using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
 
-  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+  using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
   using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 05756cfc3b3b8..1e5df6413daf0 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -31,7 +31,7 @@ struct SmallButNonTrivial {
 // specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
 template <>
 class std::function_ref<int> {
-  using storage = std::__storage_func_ref_t;
+  using storage = std::__function_ref_storage;
   void test() {
     {
       // by value small argument
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index 485fa95ca2998..d3d5f6827193d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -362,6 +362,14 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // volatile objects
+    volatile int i = 5;
+    std::function_ref<int() > f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 56d6dd3b7109b..9ab012b3a04e8 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -161,6 +161,10 @@ constexpr auto fn_noexcept = []() noexcept { return 6; };
 const auto one = []() noexcept { return 1; };
 const auto two = []() noexcept { return 2; };
 
+struct VolatileCallable {
+  int operator()() volatile const { return 42; }
+};
+
 constexpr bool test() {
   {
     std::function_ref<void()> f(l1);
@@ -305,6 +309,14 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // volatile objects
+    const volatile VolatileCallable vc{};
+    std::function_ref<int()> f(vc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
 
   return true;
 }

>From 92f7a3cfc26a7ea985b17f18aec5ca51f1733ef2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 26 Apr 2026 09:41:10 +0100
Subject: [PATCH 04/21] remove stale comments

---
 libcxx/include/__functional/function_ref_common.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 72ded36d65107..c5adb8cbc7d52 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -34,10 +34,6 @@ class function_ref;
 template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization : false_type {};
 
-// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
-// pointers
-// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
-// supported
 union __function_ref_storage {
   void* __obj_ptr_;
   void (*__fn_ptr_)();

>From f953bc4800d19121be6f90e2603a1894579fd4e5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 14:06:05 +0100
Subject: [PATCH 05/21] fix bug

---
 .../__functional/function_ref_common.h        |  2 +-
 .../include/__functional/function_ref_impl.h  | 19 ++++++++++++-------
 .../internal_call_signature.pass.cpp          |  4 +++-
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  6 +++---
 .../func.wrap.ref.ctor/ref.pass.cpp           | 11 +++++++++++
 5 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index c5adb8cbc7d52..60bc6ef87af4f 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -31,7 +31,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <class _Fn1, class _Fn2>
 struct __is_convertible_from_specialization : false_type {};
 
 union __function_ref_storage {
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 36841d6745955..55755e9804c95 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -46,14 +46,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization<
-    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
-    _NoExcept1,
-    _Rp,
-    _ArgTypes...>
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) const noexcept(_NoExcept2)> >
     : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
 
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) noexcept(_NoExcept2)> >
+    : _And<is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)>,
+           is_convertible<_LIBCPP_FUNCTION_REF_CV int&, int&>> {};
+
 template <class _Rp, class... _ArgTypes, bool __is_noexcept>
 class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
 private:
@@ -62,9 +67,9 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
           value;
 
-  template <class _Fn>
+  template <class _Fn2>
   static constexpr bool __is_convertible_from_specialization_v =
-      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+      __is_convertible_from_specialization<function_ref, _Fn2>::value;
 
   template <class... _Tp>
   friend class function_ref;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 1e5df6413daf0..ebdf683791694 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -8,7 +8,9 @@
 
 // REQUIRES: std-at-least-c++26
 
-// Track number of moves
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
 
 #include <cassert>
 #include <functional>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index b4df4392a2ade..cb84b3a183920 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -35,9 +35,9 @@ static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrap
 static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
 
 // const noexcept(false)
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
@@ -61,7 +61,7 @@ static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::con
 // const noexcept(true)
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 9ab012b3a04e8..6b8b7c71f0d08 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -309,6 +309,17 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // P3961R1 Less double indirection in function_ref
+    // is-convertible-from-specialization<remove_cv_t<T>> is false
+    std::function_ref<int()> f1(std::cw<one>);
+    std::function_ref<int() const> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 2);
+    }
+  }
   {
     // volatile objects
     const volatile VolatileCallable vc{};

>From 829ffd58bb601270575ba03338d9a3f4933065c3 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 17:28:36 +0100
Subject: [PATCH 06/21] ci

---
 libcxx/include/__functional/function_ref_impl.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 55755e9804c95..454b0ffb4afb3 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -15,6 +15,7 @@
 #include <__functional/invoke.h>
 #include <__memory/addressof.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/conjunction.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>

>From 97b324ece3401e1c9ff8a6f063e3bcfe4ae584e0 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 10:39:44 +0100
Subject: [PATCH 07/21] static operator

---
 .../include/__functional/function_ref_impl.h  | 23 +++++++++++----
 .../func.wrap.ref.ctor/ref.pass.cpp           | 28 +++++++++++++++++++
 2 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 454b0ffb4afb3..ab65382782fb2 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
@@ -99,12 +100,22 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
-      : __storage_(std::addressof(__obj)),
-        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
-        }) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
+    using _Dn = remove_cv_t<_Tp>;
+    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
+                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+                  }) {
+      __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    } else {
+      __storage_ = __storage_t(std::addressof(__obj)),
+      __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    }
+  }
 
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 6b8b7c71f0d08..2684ec3548f8e 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -328,6 +328,34 @@ constexpr bool test() {
       assert(f() == 42);
     }
   }
+  {
+    // static call operator
+    struct StaticCall {
+      static int operator()(int x, int y) { return x + y; }
+    };
+    StaticCall sc;
+    std::function_ref<int(int, int)> f(sc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2) == 3);
+    }
+  }
+  {
+    // overload set with static and non-static call operator
+    struct OverloadSet {
+      static int operator()(int x) { return x + 1; }
+      int operator()(long x) const { return x + 2; }
+    };
+    OverloadSet os;
+    std::function_ref<int(int)> f1(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1(1) == 2);
+    }
+
+    std::function_ref<int(long)> f2(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1L) == 3);
+    }
+  }
 
   return true;
 }

>From dba0c61148e62e4fdfb60ff23413a191d2e70644 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 19:26:38 +0100
Subject: [PATCH 08/21] work around clang 22 bug

---
 libcxx/include/__functional/function_ref_common.h             | 4 ++++
 libcxx/include/__functional/function_ref_impl.h               | 4 +---
 .../func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp   | 1 +
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 60bc6ef87af4f..bfe3d938056f0 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -19,6 +19,7 @@
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
+#include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -107,6 +108,9 @@ struct __function_ref_arg_fwd<_Arg> {
   using type _LIBCPP_NODEBUG = _Arg;
 };
 
+template <class _Fn, class... _Args>
+concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
+
 #endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index ab65382782fb2..7a173dee7fa4f 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -102,9 +102,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
     using _Dn = remove_cv_t<_Tp>;
-    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
-                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
-                  }) {
+    if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
       };
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 2684ec3548f8e..11b993e4a3ec3 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -351,6 +351,7 @@ constexpr bool test() {
       assert(f1(1) == 2);
     }
 
+    LIBCPP_STATIC_ASSERT(!std::__statically_callable<OverloadSet, long>);
     std::function_ref<int(long)> f2(os);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f2(1L) == 3);

>From 529b8c712d9e0277d18ad8500d1ba45b01a27c8c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:12:34 +0100
Subject: [PATCH 09/21] review comments

---
 libcxx/include/__functional/function_ref_common.h | 15 +++++++--------
 libcxx/include/__functional/function_ref_impl.h   |  6 +++---
 .../constant_arg.mandates.verify.cpp              |  2 +-
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index bfe3d938056f0..0f40a07073cfc 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -15,7 +15,8 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
-#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
@@ -98,15 +99,13 @@ template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
 template <class _Arg>
-struct __function_ref_arg_fwd {
-  using type _LIBCPP_NODEBUG = _Arg&&;
-};
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
 
 template <class _Arg>
-  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
-struct __function_ref_arg_fwd<_Arg> {
-  using type _LIBCPP_NODEBUG = _Arg;
-};
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
 
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a173dee7fa4f..3424a99bdd174 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -77,7 +77,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   friend class function_ref;
 
   template <class _Arg>
-  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+  using __arg_t _LIBCPP_NODEBUG = _If<__register_passable<_Arg>, _Arg, _Arg&&>;
 
   using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
@@ -135,8 +135,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
           !requires {
             typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
           },
-          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
-          "and produces a result with a structural type");
+          "cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref "
+          "constructed from cw would be ambiguous");
     }
   }
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 519957eec6878..63cfbc0e97c9b 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -41,7 +41,7 @@ void test() {
   // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
   std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}

>From 5bd622cea5bd0b8a9a1a0670a86e07109baba6b2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:13:48 +0100
Subject: [PATCH 10/21] use std::forward instead of static_cast

---
 libcxx/include/__functional/function_ref_impl.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 3424a99bdd174..d276c78fd09e6 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -92,7 +92,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -104,13 +104,13 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     using _Dn = remove_cv_t<_Tp>;
     if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return _Dn::operator()(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     } else {
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -125,7 +125,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     requires __is_invocable_using<const _Fn&>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
       : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -146,7 +146,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -160,7 +160,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(__obj_ptr_),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");

>From 5e3a2706d2b2645d1efca1a3c24a2bbbbc40b6f2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:14:02 +0100
Subject: [PATCH 11/21] format

---
 .../func.wrap.ref.ctor/constant_arg.mandates.verify.cpp      | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 63cfbc0e97c9b..c84cca2793bac 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -17,8 +17,9 @@
 
 // Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
 
-// For the first overload,
-// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+// For the first overload, f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>...
+// satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)>
+// is not a valid type.
 
 #include <functional>
 #include <utility>

>From b3ba60c374267d120b772b0d1f8ea0e3f3362303 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 20 Jun 2026 16:28:00 +0100
Subject: [PATCH 12/21] review commnets

---
 libcxx/include/CMakeLists.txt                 |   1 +
 .../__functional/function_ref_common.h        |   9 -
 .../include/__functional/function_ref_impl.h  |   8 +-
 .../include/__type_traits/register_passable.h |  38 ++++
 libcxx/include/module.modulemap.in            |   1 +
 .../constant_arg.mandates.verify.cpp          |  26 +--
 .../internal_call_signature.pass.cpp          | 138 ---------------
 .../internal_call_signature.sh.cpp            |  68 ++++++++
 .../views.span/span.cons/deduct.pass.cpp      |   7 -
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     |   9 +
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  24 ++-
 .../constant_wrapper_ptr.pass.cpp             |  60 ++++++-
 .../constant_wrapper_ref.pass.cpp             |  59 ++++++-
 .../func.wrap.ref.ctor/copy.pass.cpp          |   5 +
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   |  71 ++++++--
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  |   6 -
 .../func.wrap.ref.ctor/move.pass.cpp          |  70 +-------
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 102 ++---------
 .../func.wrap.ref.inv/invoke.pass.cpp         | 162 ++++++++++++++++++
 19 files changed, 506 insertions(+), 358 deletions(-)
 create mode 100644 libcxx/include/__type_traits/register_passable.h
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index a0d5cf70d267d..fcea3b7601182 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -914,6 +914,7 @@ set(files
   __type_traits/rank.h
   __type_traits/reference_constructs_from_temporary.h
   __type_traits/reference_converts_from_temporary.h
+  __type_traits/register_passable.h
   __type_traits/remove_all_extents.h
   __type_traits/remove_const.h
   __type_traits/remove_const_ref.h
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 0f40a07073cfc..e6fa031432a91 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -98,15 +98,6 @@ constexpr bool __is_constant_wrapper = false;
 template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
-template <class _Arg>
-concept __itanium_trivial_for_calls =
-    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
-    is_trivially_move_constructible_v<_Arg>;
-
-template <class _Arg>
-concept __register_passable =
-    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
-
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
 
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index d276c78fd09e6..7a968fc64de70 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/register_passable.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
@@ -92,7 +93,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
+          _Fp* __fn_ptr1 = __storage_t::template __get<_Fp>(__storage);
+          return std::invoke_r<_Rp>(__fn_ptr1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -110,7 +112,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
+        return std::invoke_r<_Rp>(__obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -145,7 +147,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          auto& __obj1 = static_cast<_LIBCPP_FUNCTION_REF_CV _Tp&>(*__storage_t::template __get<_Tp>(__storage));
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
diff --git a/libcxx/include/__type_traits/register_passable.h b/libcxx/include/__type_traits/register_passable.h
new file mode 100644
index 0000000000000..21e23e62052c1
--- /dev/null
+++ b/libcxx/include/__type_traits/register_passable.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___TYPE_TRAITS_REGISTER_PASSABLE_H
+#define _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
+
+#include <__config>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Arg>
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
+
+template <class _Arg>
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
+
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index cb29fac536046..e3b9e5ac09cb3 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -351,6 +351,7 @@ module std_core {
       header "__type_traits/reference_converts_from_temporary.h"
       export std_core.type_traits.integral_constant
     }
+    module register_passable                          { header "__type_traits/register_passable.h" }
     module remove_all_extents                         { header "__type_traits/remove_all_extents.h" }
     module remove_const_ref                           { header "__type_traits/remove_const_ref.h" }
     module remove_const                               { header "__type_traits/remove_const.h" }
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index c84cca2793bac..08e8b8e9b8b52 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -35,27 +35,27 @@ struct B {
 
 // clang-format off
 void test() {
-  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
+  // expected-error-re@*:* {{static assertion failed {{.*}}cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
-  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   A a;
-  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 }
 // clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
deleted file mode 100644
index ebdf683791694..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-struct Small {};
-
-struct Big {
-  char c[128];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
-// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
-template <>
-class std::function_ref<int> {
-  using storage = std::__function_ref_storage;
-  void test() {
-    {
-      // by value small argument
-      // the internal function should pass by value
-      using call = std::function_ref<void(Small)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
-    }
-    {
-      // by value big argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Big)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
-    }
-    {
-      // by value non-trivial small argument
-      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
-      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
-    }
-    {
-      // by lvalue reference argument
-      // the internal function should pass by lvalue reference
-      using call = std::function_ref<void(Small&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
-    }
-    {
-      // by rvalue reference argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Small&&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
-    }
-  }
-};
-
-struct TrackCopyMove {
-  mutable int copy_count = 0;
-  int move_count         = 0;
-
-  TrackCopyMove() = default;
-  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
-    ++copy_count;
-    ++other.copy_count;
-  }
-
-  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
-    ++move_count;
-    ++other.move_count;
-  }
-  TrackCopyMove& operator=(const TrackCopyMove& other) {
-    ++copy_count;
-    ++other.copy_count;
-    return *this;
-  }
-  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
-    ++move_count;
-    ++other.move_count;
-    return *this;
-  }
-};
-
-void test() {
-  {
-    // Arg type is an lvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&)> f = lambda;
-    f(t);
-  }
-  {
-    // Arg type is an rvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&&)> f = lambda;
-    f(std::move(t));
-  }
-  {
-    // Arg type is a prvalue, we should move but not copy the object
-    // In this case, where the type is not trivially copyable, the object should be
-    // moved exactly once when passing into the lambda. The internal functions
-    // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) {
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 1);
-    };
-    std::function_ref<void(TrackCopyMove)> f = lambda;
-    f(TrackCopyMove{});
-  }
-}
-
-int main(int, char**) {
-  test();
-
-  return 0;
-}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
new file mode 100644
index 0000000000000..a32a1083b5596
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// REQUIRES: has-filecheck
+
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
+
+struct Small {
+  char c[8];
+};
+
+struct Big {
+  char c[256];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// CHECK: define{{.*}} @_Z10test_small
+// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
+// CHECK: %3 = alloca %struct.Small, align 1
+// CHECK: %6 = alloca %struct.Small, align 1
+// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
+// CHECK: store i64 %1, ptr %7, align 1
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
+// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
+// CHECK: %15 = load i64, ptr %14, align 1
+// CHECK: call void %10(ptr %13, i64 %15)
+//
+// The above has two local copies of Small object. It loads %1 to local copy %3, and then
+// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
+void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
+
+// CHECK: define{{.*}} @_Z8test_big
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
+//
+// The above directly calls the function pointer with the parameter Big object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the Big object by reference
+void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
+
+// CHECK: define{{.*}} @_Z22test_small_non_trivial
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
+//
+// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the SmallButNonTrivial object by reference
+void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index f5b590cd9631d..35d9b36f824aa 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -88,13 +88,6 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
-
-  {
-    std::span s{std::begin(arr), std::cw<3>};
-    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
-    assert(s.size() == std::size(arr));
-    assert(s.data() == std::data(arr));
-  }
 #endif
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
index 029cb19d54f6a..730a07202062d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -8,6 +8,15 @@
 
 // REQUIRES: std-at-least-c++26
 
+// template<class F>
+// function_ref(F*) -> function_ref<F>;
+//
+// template<auto c, class F0>
+// function_ref(constant_wrapper<c, F0>) -> function_ref<see below>;
+//
+// template<auto c, class F, class T>
+// function_ref(constant_wrapper<c, F>, T&&) -> function_ref<see below>;
+
 #include <cassert>
 #include <functional>
 #include <utility>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index cb84b3a183920..5866488be4a94 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -11,6 +11,7 @@
 // template<class T> function_ref& operator=(T) = delete;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -73,38 +74,49 @@ static_assert(
 
 int forty_two() { return 42; }
 
+// These runtime tests are testing that when the constraints are not met,
+// the assignment operator is not deleted and the implicit constructor
+// and the move assignment are called
 void test() {
   {
     std::function_ref<int()> f(std::cw<[] { return 41; }>);
-    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f =
+        std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = &forty_two;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = &forty_two;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // noexcept
     std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() noexcept>&> decltype(auto) result = f = std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const noexcept
     std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() const noexcept>&> decltype(auto) result = f =
+        std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
index dd6e10e8a3289..74f52063b6788 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -94,6 +94,7 @@ static_assert(
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
 static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, const int*>);
 
 static_assert(
     !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
@@ -180,6 +181,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int*) const noexcept { return 4; }
+  constexpr int operator()(const int*) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -231,6 +237,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -248,6 +262,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -263,39 +285,51 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -348,6 +382,20 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, &i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, &i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, &i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index d3d5f6827193d..979da32e60c3d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -199,6 +199,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int&) const noexcept { return 4; }
+  constexpr int operator()(const int&) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -248,6 +253,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -264,6 +277,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -278,37 +299,49 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -370,6 +403,20 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
index 61b70a559d786..7f837149cc6b5 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -22,6 +22,11 @@ static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
 double f1(int x, double y) noexcept { return x + y; }
 
 struct Int {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
index 1a8b26f86c8be..53c79b4097362 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -11,6 +11,7 @@
 // constexpr function_ref& operator=(const function_ref&) noexcept = default;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -22,6 +23,11 @@ static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
@@ -41,7 +47,8 @@ constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = f;
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -51,7 +58,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = f;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
@@ -62,7 +70,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -72,7 +81,8 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -82,32 +92,47 @@ constexpr bool test() {
     // with conversions
     std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
         std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
@@ -117,31 +142,47 @@ constexpr bool test() {
     // with conversions function pointer
     std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
     std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions function pointer
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index f142b81367ef1..aa0ccf8d67993 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -134,12 +134,6 @@ void test() {
 
     std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
     assert(f4(1, 2, 3).i == 6);
-
-    {
-      std::function_ref r1 = foo;
-      std::function_ref r2 = bar;
-      r1                   = r2; // ok
-    }
   }
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
index 0f27bba821391..025df83ad538d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -8,6 +8,8 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
 #include <functional>
 #include <utility>
@@ -20,18 +22,12 @@ static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-double f1(int x, double y) noexcept { return x + y; }
-
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+double f1(int x, double y) noexcept { return x + y; }
 
 constexpr bool test() {
   {
@@ -66,58 +62,6 @@ constexpr bool test() {
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
index b40dfa9f2cf4b..10620f5d48725 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -8,7 +8,10 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -20,26 +23,20 @@ static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
-
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
-int zero(Int, Int, Int) noexcept { return 0; }
-
 constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -49,8 +46,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = std::move(f);
-
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
       assert(f2() == 42);
@@ -60,7 +57,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -70,81 +68,13 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
-        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 1cd0c769ce4d3..0dced07c439d6 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -67,6 +67,32 @@ struct S3Value {
   char operator()(Big b) const noexcept { return b.c[0]; }
 };
 
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
 void test_default() {
   {
     std::function_ref<void()> f = [] {};
@@ -135,6 +161,40 @@ void test_default() {
     std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const() {
@@ -205,6 +265,40 @@ void test_const() {
     std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_noexcept() {
@@ -276,6 +370,40 @@ void test_noexcept() {
     std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const_noexcept() {
@@ -347,6 +475,40 @@ void test_const_noexcept() {
     std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test() {

>From 04cce85f72e377682999b3c9c769b6e6282be331 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:38:35 +0100
Subject: [PATCH 13/21] format

---
 .../func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp       | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 0dced07c439d6..820fed5fc76bc 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -384,7 +384,7 @@ void test_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -397,7 +397,7 @@ void test_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };
@@ -489,7 +489,7 @@ void test_const_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -502,7 +502,7 @@ void test_const_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };

>From ea630194a1322b1b4d96356421f790ca9252ad8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:31 +0100
Subject: [PATCH 14/21] Update libcxx/docs/Status/Cxx26Issues.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 55c642fd15ce1..d3c99fb086867 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","24","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""

>From 1f9e504b29de5dfa00a3552be6e179baf50a196d Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:40 +0100
Subject: [PATCH 15/21] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 530575f4973da..3178f0652a74b 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -192,7 +192,7 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
 "`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""

>From 94623a0041913e0cf10e4c86979cff9bf2e73720 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:50 +0100
Subject: [PATCH 16/21] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 3178f0652a74b..f06f99caab47a 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","24","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."

>From ad2bd3a7365138df9c00c368f6b6b1480d242b14 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:15:01 +0100
Subject: [PATCH 17/21] Update libcxx/docs/Status/Cxx26Issues.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d3c99fb086867..058dd6900cdc7 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","24","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""

>From 9928fffabc4f143a84def6354745264002a14a26 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:15:11 +0100
Subject: [PATCH 18/21] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f06f99caab47a..81b20f6088097 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -194,7 +194,7 @@
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
 "`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","24","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""

>From 34b8bc5a68503d520cbc56e7f216a39fe98b2f84 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:32:58 +0100
Subject: [PATCH 19/21] review

---
 libcxx/docs/ReleaseNotes/23.rst               |  3 -
 libcxx/docs/ReleaseNotes/24.rst               |  3 +
 .../internal_call_signature.sh.cpp            | 68 -------------------
 3 files changed, 3 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 5d295453dd79f..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,9 +55,6 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
-- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
-- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
-- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..e20fa560865ad 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,9 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
deleted file mode 100644
index a32a1083b5596..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// REQUIRES: has-filecheck
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
-
-struct Small {
-  char c[8];
-};
-
-struct Big {
-  char c[256];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// CHECK: define{{.*}} @_Z10test_small
-// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
-// CHECK: %3 = alloca %struct.Small, align 1
-// CHECK: %6 = alloca %struct.Small, align 1
-// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
-// CHECK: store i64 %1, ptr %7, align 1
-// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
-// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
-// CHECK: %15 = load i64, ptr %14, align 1
-// CHECK: call void %10(ptr %13, i64 %15)
-//
-// The above has two local copies of Small object. It loads %1 to local copy %3, and then
-// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
-void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
-
-// CHECK: define{{.*}} @_Z8test_big
-// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
-//
-// The above directly calls the function pointer with the parameter Big object pointer
-// points to the operator()'s parameter %1, thus, no extra copies/moves
-// So we internally pass the Big object by reference
-void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
-
-// CHECK: define{{.*}} @_Z22test_small_non_trivial
-// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
-//
-// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
-// points to the operator()'s parameter %1, thus, no extra copies/moves
-// So we internally pass the SmallButNonTrivial object by reference
-void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file

>From 7274482083a6cd8c1d3fbf02b035c25c19f99186 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:14:44 +0100
Subject: [PATCH 20/21] CI

---
 libcxx/include/__functional/function_ref_impl.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a968fc64de70..ada14643ec496 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -158,8 +158,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <auto _Cw, class _Fn, class _Tp>
     requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
-                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
-      : __storage_(__obj_ptr_),
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+      : __storage_(__obj_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
@@ -169,7 +169,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     }
 
     if constexpr (is_member_pointer_v<_Fn>) {
-      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
     }
   }
 

>From 8b592acd53532d355c60093a1c0626dd99c45cad Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:54:26 +0100
Subject: [PATCH 21/21] CI

---
 libcxx/docs/conf.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/conf.py b/libcxx/docs/conf.py
index 9051bce7cf356..98aa52edd5d2e 100644
--- a/libcxx/docs/conf.py
+++ b/libcxx/docs/conf.py
@@ -32,9 +32,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = "23.0"
+version = "24.0"
 # The full version, including alpha/beta/rc tags.
-release = "23.0"
+release = "24.0"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.


From libcxx-commits at lists.llvm.org  Wed Jul 22 04:33:02 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Wed, 22 Jul 2026 04:33:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a60aa6e.449bfb44.c0ec9.8ded@mx.google.com>


================
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
----------------
frederick-vs-ja wrote:

> I copied the exact same assertion behaviour from `signbit.pass.cpp` which is part of the PR of `constexpr` for `std::signbit`.

I don't think it's a good idea to be consistent with an old, inconsistent test. Let's refactor these into:
```c++
TEST_CONSTEXPR_CXX23 bool test() {
  // ...
  return true;
}

int main(int, char**) {
  test();
#if TEST_STD_VER >= 23
  static_assert(test());
#endif
}
```

I think it would be better to open another PR to refactor the test for `signbit`.

https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 04:41:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 04:41:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a60ac4c.5a0412d2.325e39.621d@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From 145920aa6c68ee172dc65111a6a0cfb99d4c9069 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/27] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..11a0b42ea54f5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 899502422570a..4ae0f6a28637a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -32,11 +33,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -52,21 +66,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -102,12 +120,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -126,23 +146,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -152,23 +178,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From 90d997369a9f358f579ae61963226b1f30c81d5a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/27] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 37c38d25012c6..78e27d9ef9781 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From 7a63b7897f421f0d367057c933db66b3ea0f21e1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/27] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 11a0b42ea54f5..caa7f9d1a2535 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 78e27d9ef9781..640f6e5e916ca 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..8bdde44c1cb8c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -895,6 +895,7 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From e6a881f9199428718c9b93a92dcea056d7e84356 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/27] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 4ae0f6a28637a..c496ecf5ac742 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -49,7 +49,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From 13589231818d564e6ba520c405eae011f72f10fe Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/27] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From d3c6b5e3b74e59c2beaf5b3158db90faef36600c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/27] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c496ecf5ac742..c55de19cd4b59 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -148,7 +148,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From 11cedf33d3fe73d5bad5930a8c4f12df33e0eac8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/27] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 7e6410501ecdccbd78ce814b0e4906a60f379df6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/27] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c55de19cd4b59..0cf50d7ebd1ec 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -162,7 +162,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -180,7 +180,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -195,7 +195,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 640f6e5e916ca..44f1229f859b3 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From f666483df06fffa6f94a403ba051621cf0432415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/27] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index caa7f9d1a2535..b6650a314bd40 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0cf50d7ebd1ec..05b22d04962fa 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,29 +30,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 44f1229f859b3..0a6e3d54b5911 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 8bdde44c1cb8c..f038c9d39a27b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -884,6 +884,7 @@ module std {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -895,7 +896,6 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From f622cc3f082c34d6f0c5cd6e47177664eed48938 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/27] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 1e55b9997f985525c33ac424b94de00d91b36bba Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/27] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 0a6e3d54b5911..2f6e0d5dbd20a 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From 996293d9cfb334a462283e783e2fb2af90bb373c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/27] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From f74f609379e9feab234dd430bc758b19d9b1d46b Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/27] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From 8c0b1998421e1c8e214ee2694544bf3f9d8f5f79 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/27] 14

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <cstring>
+#include <type_traits>
 
 struct WithTailPadding {
   int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From f436e600ce39701927e30d0074019afc4cdb581c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 20:42:14 +0100
Subject: [PATCH 15/27] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e4d83e638ca77..0bafdbb1d7ede 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -136,7 +136,7 @@ void test() {
   {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
-
+    std::atomic<T> a;
     T init = make<T>(10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);

>From 4def68f03c3089677bb5206f56f30e367415ecce Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:13:05 +0100
Subject: [PATCH 16/27] ci

---
 libcxx/include/__atomic/support/gcc.h         |  5 ++--
 .../atomics.types.generic/padding.pass.cpp    | 24 ++++++++++-------
 .../atomics.types.generic/padding.pass.cpp    | 27 ++++++++++---------
 .../atomics.types.generic/padding2.pass.cpp   |  3 ---
 4 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 2f6e0d5dbd20a..b162e2c2a63f0 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -50,9 +50,8 @@ struct __cxx_atomic_base_impl {
     std::__clear_padding_if_needed(__a_value);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
   _Tp __a_value;
 };
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index bf594b45ed54f..6f894fe6cb605 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -44,15 +44,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -61,11 +69,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0bafdbb1d7ede..344d08b493e60 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,9 +7,6 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits
@@ -41,15 +38,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -58,11 +63,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
index 4e17471a2461f..1040f342b8206 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -8,9 +8,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-localization
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits

>From eeecefecde068ea1f6e2f8417b52961192ca8bc4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:29:04 +0100
Subject: [PATCH 17/27] CI

---
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding2.pass.cpp   | 273 ------------------
 3 files changed, 2 insertions(+), 275 deletions(-)
 delete mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 6f894fe6cb605..388ab0867fe38 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 344d08b493e60..f1a534a2916d6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
deleted file mode 100644
index 1040f342b8206..0000000000000
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-localization
-
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
-// CAS should work on types with padding bits
-
-#include <atomic>
-#include <cassert>
-#include <cstring>
-#include <type_traits>
-#include <iostream>
-
-struct Foo {
-  int i;
-  char c;
-};
-
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
-
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
-}
-
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
-}
-
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-#ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
-#else
-  (void)f;
-  (void)pad_byte;
-#endif
-}
-
-void test() {
-  std::cerr << "test 1" << std::endl;
-  {
-    // compare_exchange_strong
-    // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
-    std::cerr << "test 2" << std::endl;
-
-    Foo init = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 3" << std::endl;
-    assert_foo_padding(init, 0xBB);
-    std::cerr << "test 4" << std::endl;
-    a.store(init);
-
-    Foo expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 5" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 6" << std::endl;
-
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 7" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 8" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 9" << std::endl;
-    assert(r);
-    std::cerr << "test 10" << std::endl;
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    std::cerr << "test 11" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 42);
-    std::cerr << "test 12" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 13" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 14" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 15" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 16" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 17" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 18" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 19" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 10" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 21" << std::endl;
-    assert(!r);
-    std::cerr << "test 22" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 23" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 24" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 25" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 10);
-    std::cerr << "test 26" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 27" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 28" << std::endl;
-  }
-
-  {
-    // compare_exchange_weak
-    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 29" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 30" << std::endl;
-    a.store(stored);
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 31" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 32" << std::endl;
-
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 33" << std::endl;
-    assert_foo_padding(original_expected, 0xAA);
-    std::cerr << "test 34" << std::endl;
-
-    bool r                  = false;
-    const auto max_attempts = 100;
-    auto current_attempt    = 0;
-    while (!r) {
-      ++current_attempt;
-      std::cerr << "test 35" << std::endl;
-      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      std::cerr << "test 36" << std::endl;
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
-      std::cerr << "test 37" << std::endl;
-      r = a.compare_exchange_weak(expected, new_value);
-      if (r) {
-        std::cerr << "test 38" << std::endl;
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-        std::cerr << "test 39" << std::endl;
-      } else {
-        // Spurious failure: expected is updated to the current atomic value.
-        std::cerr << "test 40" << std::endl;
-        assert(expected.i == 10);
-        std::cerr << "test 41" << std::endl;
-        assert(expected.c == 'a');
-        std::cerr << "test 45" << std::endl;
-        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
-        std::cerr << "test 43" << std::endl;
-      }
-    }
-
-    Foo loaded = a.load();
-    std::cerr << "test 44" << std::endl;
-    assert(loaded.i == 42);
-    std::cerr << "test 45" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 46" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 47" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 48" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 49" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 50" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 51" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 52" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 53" << std::endl;
-
-    bool r = a.compare_exchange_weak(expected, new_value);
-
-    std::cerr << "test 54" << std::endl;
-    assert(!r);
-    std::cerr << "test 55" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 56" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 57" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 58" << std::endl;
-    Foo loaded = a.load();
-    std::cerr << "test 59" << std::endl;
-    assert(loaded.i == 10);
-    std::cerr << "test 60" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 61" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 62" << std::endl;
-  }
-
-  {
-    // Types with unique object representations skip the padding-clearing path.
-    std::atomic<int> a(1);
-    int expected = 1;
-    std::cerr << "test 63" << std::endl;
-    assert(a.compare_exchange_strong(expected, 2));
-    std::cerr << "test 64" << std::endl;
-    assert(expected == 1);
-    std::cerr << "test 65" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 66" << std::endl;
-
-    expected = 3;
-    assert(!a.compare_exchange_strong(expected, 4));
-    std::cerr << "test 67" << std::endl;
-    assert(expected == 2);
-    std::cerr << "test 68" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 69" << std::endl;
-  }
-}
-
-int main(int, char**) {
-  // TODO(LLVM-23): Switch to XFAIL: clang-22
-  std::cerr << "main 1" << std::endl;
-#if __has_builtin(__builtin_clear_padding)
-  std::cerr << "main 2" << std::endl;
-  test();
-  std::cerr << "main 3" << std::endl;
-#endif
-  std::cerr << "main 4" << std::endl;
-  return 0;
-}

>From bc539fb8d58e16f24a5ccb8bdd2351475e7d4391 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:52:15 +0100
Subject: [PATCH 18/27] ci

---
 libcxx/include/__atomic/support/c11.h | 13 ++++++++-----
 libcxx/include/__atomic/support/gcc.h |  9 +++++++--
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 05b22d04962fa..d43a2c67f950a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -30,13 +30,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
 
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b162e2c2a63f0..d2d4919d54614 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,12 +46,17 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
       : __a_value(std::__clear_padding_if_needed(__value)) {}
+
   _Tp __a_value;
 };
 

>From 30a056210700cd8f89b1c0ca6991f272a0ebba43 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:26:08 +0100
Subject: [PATCH 19/27] review

---
 libcxx/include/__atomic/clear_padding.h            | 14 ++++++--------
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 5bc77ff4401ab..f920ef39977b7 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -35,17 +35,16 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, float>>,
            _Not<is_same<_Tp, double>>> {};
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(std::addressof(__obj));
-    }
-  }
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+  return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
+}
+
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
@@ -67,7 +66,6 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 388ab0867fe38..45fb083cccf3f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index f1a534a2916d6..5ffba6ca844c5 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();

>From 9b771605f016811c06b3f600e48f45f3268a5269 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:33:16 +0100
Subject: [PATCH 20/27] release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx20Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..a689379321496 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange (`Github <https://llvm.org/PR76180>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 5539767ab800b..8e394b4bc4b3a 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -31,7 +31,7 @@
 "`P0458R2 <https://wg21.link/P0458R2>`__","Checking for Existence of an Element in Associative Containers","2018-06 (Rapperswil)","|Complete|","13","`#104029 <https://github.com/llvm/llvm-project/issues/104029>`__",""
 "`P0475R1 <https://wg21.link/P0475R1>`__","LWG 2511: guaranteed copy elision for piecewise construction","2018-06 (Rapperswil)","|Complete|","","`#104031 <https://github.com/llvm/llvm-project/issues/104031>`__",""
 "`P0476R2 <https://wg21.link/P0476R2>`__","Bit-casting object representations","2018-06 (Rapperswil)","|Complete|","14","`#104087 <https://github.com/llvm/llvm-project/issues/104087>`__",""
-"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","","","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
+"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","|Complete|","24","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
 "`P0542R5 <https://wg21.link/P0542R5>`__","Support for contract based programming in C++","2018-06 (Rapperswil)","|Nothing To Do|","n/a","`#104089 <https://github.com/llvm/llvm-project/issues/104089>`__","Pulled at the 2019-07 meeting in Cologne"
 "`P0556R3 <https://wg21.link/P0556R3>`__","Integral power-of-2 operations","2018-06 (Rapperswil)","|Complete|","9","`#104090 <https://github.com/llvm/llvm-project/issues/104090>`__",""
 "`P0619R4 <https://wg21.link/P0619R4>`__","Reviewing Deprecated Facilities of C++17 for C++20","2018-06 (Rapperswil)","|Complete|","20","`#99985 <https://github.com/llvm/llvm-project/issues/99985>`__","Removed headers are still provided as an extension, but with deprecation warnings."

>From 46a4ba35dbc30153f7868e9796d44f88512ae602 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 17:01:57 +0100
Subject: [PATCH 21/27] ci

---
 libcxx/include/__atomic/clear_padding.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index f920ef39977b7..3099faa8ca6d1 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/has_unique_object_representation.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>

>From 5f475333df1aeee9640364c9fb2f734c731edb7e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 19:03:39 +0100
Subject: [PATCH 22/27] ci

---
 .../atomics.types.generic/padding.pass.cpp    | 19 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 52 ++++++++++++-------
 2 files changed, 44 insertions(+), 27 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 45fb083cccf3f..116a860b3192c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -57,11 +57,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -86,7 +84,8 @@ void test() {
 
   {
     // atomic(T);
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     std::atomic<T> a(init);
     T loaded = a.load();
@@ -97,7 +96,8 @@ void test() {
   {
     // atomic::store
     std::atomic<T> a;
-    T value = make<T>(5, 'x', 0xAB);
+    T value;
+    initialize(value, 5, 'x', 0xAB);
     assert_padding(value, 0xAB);
     a.store(value);
     T loaded = a.load();
@@ -107,10 +107,12 @@ void test() {
   }
   {
     // atomic::exchange
-    T initial = make<T>(1, 'a', 0x00);
+    T initial;
+    initialize(initial, 1, 'a', 0x00);
     assert_padding(initial, 0x00);
     std::atomic<T> a(initial);
-    T new_val = make<T>(2, 'b', 0xCD);
+    T new_val;
+    initialize(new_val, 2, 'b', 0xCD);
     assert_padding(new_val, 0xCD);
     T old = a.exchange(new_val);
     assert(old.i == 1);
@@ -124,7 +126,8 @@ void test() {
   {
     // atomic_init
     std::atomic<T> a;
-    T init = make<T>(7, 'z', 0xEF);
+    T init;
+    initialize(init, 7, 'z', 0xEF);
     assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
     T loaded = a.load();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 5ffba6ca844c5..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -51,11 +51,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -84,17 +82,20 @@ void test() {
     // CAS should succeed when only padding differs in expected; expected is unchanged.
     std::atomic<T> a;
 
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0xAA);
+    T expected;
+    initialize(expected, 10, 'a', 0xAA);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -112,13 +113,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -138,17 +142,20 @@ void test() {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
     std::atomic<T> a;
-    T init = make<T>(10, 'a', 0x00);
+    T init;
+    initialize(init, 10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0x00);
+    T expected;
+    initialize(expected, 10, 'a', 0x00);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0x42);
+    T new_value;
+    initialize(new_value, 42, 'b', 0x42);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,14 +173,17 @@ void test() {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
-    T original_expected = make<T>(10, 'a', 0xAA);
+    T original_expected;
+    initialize(original_expected, 10, 'a', 0xAA);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -182,7 +192,8 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      T expected = make<T>(10, 'a', 0xAA);
+      T expected;
+      initialize(expected, 10, 'a', 0xAA);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -207,13 +218,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);

>From 704a54eb4f1f7989931b7e25ad87abbb60ad6021 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:07:26 +0100
Subject: [PATCH 23/27] debug

---
 .../atomics.types.generic/padding.pass.cpp    | 64 ++++++++++++++++++-
 1 file changed, 62 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..3b7f13b366e96 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,6 +15,7 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
+#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -70,11 +71,19 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
+void print_memory(const T& obj) {
+  for (size_t i = 0; i < sizeof(T); ++i) {
+    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
+  }
+  std::cerr << std::endl;
+}
+
 template <class T>
 void test() {
   {
@@ -84,11 +93,15 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
+    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
+    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -96,6 +109,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -106,6 +121,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test1 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -115,14 +132,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -131,11 +154,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test2 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test2 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -144,11 +171,15 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
+    std::cerr << "test3 init padding should be 0x00: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
+    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -156,6 +187,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
+    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,6 +199,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test3 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -175,15 +210,21 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
+    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
+    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -194,6 +235,8 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
+      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
+      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -203,6 +246,8 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        std::cerr << "test4 expected padding should be 0: " << std::endl;
+        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -211,6 +256,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test4 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -220,14 +267,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -236,11 +289,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test5 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test5 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -262,10 +319,13 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
+  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
+  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
+  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 0;
+  return 1;
 }

>From 8c622e41d2428f630855f0aaa215b1463d8fe4ca Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:38:19 +0100
Subject: [PATCH 24/27] Revert "debug"

This reverts commit 8be9a83ee040faff62f0756555890484bf502a31.
---
 .../atomics.types.generic/padding.pass.cpp    | 64 +------------------
 1 file changed, 2 insertions(+), 62 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 3b7f13b366e96..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,7 +15,6 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
-#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -71,19 +70,11 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)obj;
+  (void)f;
   (void)pad_byte;
 #endif
 }
 
-template <class T>
-void print_memory(const T& obj) {
-  for (size_t i = 0; i < sizeof(T); ++i) {
-    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
-  }
-  std::cerr << std::endl;
-}
-
 template <class T>
 void test() {
   {
@@ -93,15 +84,11 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
-    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
-    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -109,8 +96,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -121,8 +106,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test1 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -132,20 +115,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -154,15 +131,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test2 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test2 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -171,15 +144,11 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
-    std::cerr << "test3 init padding should be 0x00: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
-    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -187,8 +156,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
-    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -199,8 +166,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test3 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -210,21 +175,15 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
-    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
-    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -235,8 +194,6 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
-      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
-      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -246,8 +203,6 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        std::cerr << "test4 expected padding should be 0: " << std::endl;
-        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -256,8 +211,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test4 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -267,20 +220,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -289,15 +236,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test5 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test5 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -319,13 +262,10 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
-  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
-  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
-  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 1;
+  return 0;
 }

>From 789912ba84ccbb3a3efea236fde31835783a7415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:44:00 +0100
Subject: [PATCH 25/27] test ci

---
 .../atomics.types.generic/padding.pass.cpp    | 20 ++++++++++++++-----
 .../atomics.types.generic/padding.pass.cpp    | 12 +----------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 116a860b3192c..cd41f2893864c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -71,6 +71,16 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
+void assert_padding(const std::atomic<T>& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  T loaded     = obj.load();
+  set(reference, loaded.i, loaded.c);
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
+}
+
 template <class T>
 void test() {
   {
@@ -79,7 +89,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 
   {
@@ -91,7 +101,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::store
@@ -103,7 +113,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::exchange
@@ -121,7 +131,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic_init
@@ -133,7 +143,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..10c15523cd70b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -70,7 +70,7 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
@@ -105,8 +105,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -135,8 +133,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
   {
     // compare_exchange_strong
@@ -165,8 +161,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -210,8 +204,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -240,8 +232,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {

>From 81770cda9f2171565c1c2980ad8306b9665752b2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:18:49 +0100
Subject: [PATCH 26/27] padding

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index cd41f2893864c..a178ce74578ec 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -127,7 +127,6 @@ void test() {
     T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_padding(old, 0);
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');

>From b90bec87725f4bde5722a3aa857a5c9d22c275e5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:40:31 +0100
Subject: [PATCH 27/27] clang22

---
 .../libcxx/atomics/atomics.types.generic/padding.pass.cpp    | 5 ++---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 4 +---
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index a178ce74578ec..48ae68a8e97ba 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -147,11 +148,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
+
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 10c15523cd70b..dc0afa439dc12 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
@@ -250,12 +251,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
 
   return 0;
 }


From libcxx-commits at lists.llvm.org  Wed Jul 22 04:46:59 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 04:46:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a60adb3.1e1ac41d.317291.9f43@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/193891

From libcxx-commits at lists.llvm.org  Wed Jul 22 04:53:38 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 04:53:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a60af42.92d2873c.2cdd42.1b01@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/210951

>From a7a7b8cb88fae75cc531892949f4e8a9d898e253 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 21 Jul 2026 13:14:55 +0200
Subject: [PATCH] [libc++] Fix ungetc failing after xsgetn

---
 libcxx/include/fstream                        |  77 ++++++---
 .../fstreams/filebuf.members/test.dat         |   1 +
 .../filebuf.members/xsgetn.buffer.pass.cpp    | 156 ++++++++++++++++++
 .../fstreams/ifstream.members/xsgetn.pass.cpp |  14 ++
 4 files changed, 223 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
 create mode 100644 libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..f8d410f307ff1 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -311,6 +311,8 @@ protected:
 
   _LIBCPP_HIDE_FROM_ABI_VIRTUAL streamsize xsgetn(char_type* __str, streamsize __len) override {
     if (__file_ && __always_noconv_) {
+      char_type __1buf;
+      __read_guard __guard(__1buf, *this);
       const streamsize __n = std::min(this->egptr() - this->gptr(), __len);
       if (__n != 0) {
         traits_type::copy(__str, this->gptr(), __n);
@@ -319,9 +321,16 @@ protected:
       const streamsize __remainder    = __len - __n;
       const streamsize __buffer_space = this->egptr() - this->eback();
 
-      if (__remainder >= __buffer_space)
-        return std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
-      else if (__remainder > 0)
+      if (__remainder >= __buffer_space) {
+        auto __res = std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
+
+        // Copy the buffer-sized tail into the buffer so that `unget`ting works correctly
+        auto __buf_size = std::min<size_t>(__res, this->egptr() - this->eback());
+        traits_type::copy(this->eback(), __str + __res - __buf_size, __buf_size);
+        this->setg(this->eback(), this->eback() + __buf_size, this->eback() + __buf_size);
+
+        return __res;
+      } else if (__remainder > 0)
         return basic_streambuf<_CharT, _Traits>::xsgetn(__str + __n, __remainder) + __n;
       return __n;
     }
@@ -383,7 +392,41 @@ private:
   bool __owns_ib_;
   bool __always_noconv_;
 
-  bool __read_mode();
+  struct [[__nodiscard__]] __read_guard {
+    basic_filebuf& __self_;
+    size_t __unget_size_;
+    char_type& __1buf_;
+
+    _LIBCPP_HIDE_FROM_ABI __read_guard(char_type& __1buf, basic_filebuf& __self) : __self_(__self), __1buf_(__1buf) {
+      if (__self.__cm_ & ios_base::in) {
+        __unget_size_ = std::min<size_t>((__self.egptr() - __self.eback()) / 2, 4);
+      } else {
+        __self.setp(nullptr, nullptr);
+        if (__self.__always_noconv_) {
+          __self.setg(reinterpret_cast<char_type*>(__self.__extbuf_),
+                      reinterpret_cast<char_type*>(__self.__extbuf_) + __self.__ebs_,
+                      reinterpret_cast<char_type*>(__self.__extbuf_) + __self.__ebs_);
+        } else {
+          __self.setg(__self.__intbuf_, __self.__intbuf_ + __self.__ibs_, __self.__intbuf_ + __self.__ibs_);
+        }
+        __self.__cm_ = ios_base::in;
+        __unget_size_ = 0;
+      }
+
+      if (__self.gptr() == nullptr)
+        __self.setg(std::addressof(__1buf_), std::addressof(__1buf_) + 1, std::addressof(__1buf_) + 1);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI size_t __unget_size() {
+      return __unget_size_;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI ~__read_guard() {
+      if (__self_.eback() == std::addressof(__1buf_))
+        __self_.setg(nullptr, nullptr, nullptr);
+    }
+  };
+
   void __write_mode();
 
   _LIBCPP_HIDE_FROM_ABI static int __fseek(FILE* __file, pos_type __offset, int __whence);
@@ -802,12 +845,12 @@ template <class _CharT, class _Traits>
 typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>::underflow() {
   if (__file_ == nullptr)
     return traits_type::eof();
-  bool __initial = __read_mode();
+
   char_type __1buf;
-  if (this->gptr() == nullptr)
-    this->setg(std::addressof(__1buf), std::addressof(__1buf) + 1, std::addressof(__1buf) + 1);
-  const size_t __unget_sz = __initial ? 0 : std::min<size_t>((this->egptr() - this->eback()) / 2, 4);
-  int_type __c            = traits_type::eof();
+  __read_guard __guard(__1buf, *this);
+
+  const size_t __unget_sz = __guard.__unget_size();
+  int_type __c = traits_type::eof();
   if (this->gptr() == this->egptr()) {
     std::memmove(this->eback(), this->egptr() - __unget_sz, __unget_sz * sizeof(char_type));
     if (__always_noconv_) {
@@ -849,8 +892,6 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>
     }
   } else
     __c = traits_type::to_int_type(*this->gptr());
-  if (this->eback() == std::addressof(__1buf))
-    this->setg(nullptr, nullptr, nullptr);
   return __c;
 }
 
@@ -1141,20 +1182,6 @@ void basic_filebuf<_CharT, _Traits>::imbue(const locale& __loc) {
   }
 }
 
-template <class _CharT, class _Traits>
-bool basic_filebuf<_CharT, _Traits>::__read_mode() {
-  if (!(__cm_ & ios_base::in)) {
-    this->setp(nullptr, nullptr);
-    if (__always_noconv_)
-      this->setg((char_type*)__extbuf_, (char_type*)__extbuf_ + __ebs_, (char_type*)__extbuf_ + __ebs_);
-    else
-      this->setg(__intbuf_, __intbuf_ + __ibs_, __intbuf_ + __ibs_);
-    __cm_ = ios_base::in;
-    return true;
-  }
-  return false;
-}
-
 template <class _CharT, class _Traits>
 void basic_filebuf<_CharT, _Traits>::__write_mode() {
   if (!(__cm_ & ios_base::out)) {
diff --git a/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
new file mode 100644
index 0000000000000..6fe86c31590f8
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
@@ -0,0 +1 @@
+This is a bunch of data so the test can read some stuff and not instantly run out of data to read.
diff --git a/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp
new file mode 100644
index 0000000000000..0b7f3b04fa219
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FILE_DEPENDENCIES: test.dat
+
+// <fstream>
+
+// streamsize basic_filebuf::xsgetn(char_type*, streamsize);
+
+// Test that xsgetn buffers properly. Specifically, we guarantee that `unget()` can be called at least four times.
+
+#include <cassert>
+#include <fstream>
+#include <string>
+
+#include "platform_support.h"
+
+void small_file_tests() {
+  char buffer[12];
+
+  { // Check that we can unget() when reading a single character
+    std::ifstream is("test.dat");
+    assert(is.is_open());
+    char buf[1];
+    is.read(buf, 1);
+    assert(buf[0] == 'T');
+    is.unget();
+    assert(is.good());
+  }
+
+  { // Check that we can unget() when reading a single character with a user-provided buffer
+    std::ifstream is("test.dat");
+    assert(is.is_open());
+    is.rdbuf()->pubsetbuf(buffer, 12);
+    char buf[1];
+    is.read(buf, 1);
+    assert(buf[0] == 'T');
+    is.unget();
+    assert(is.good());
+  }
+
+  { // Check that unget() works as expected when the remainder is smaller than the buffer
+    std::ifstream is("test.dat");
+    assert(is.is_open());
+    is.rdbuf()->pubsetbuf(buffer, 12);
+    (void)is.rdbuf()->sgetc(); // Make sure there is data in the buffer
+    char buf[17];
+    buf[16] = '\0';
+    is.read(buf, 16);
+    assert(buf == std::string("This is a bunch "));
+    for (size_t i = 0; i != 4; ++i)
+      is.unget();
+    assert(is.good());
+    is.read(buf, 4);
+    buf[4] = '\0';
+    assert(buf == std::string("nch "));
+  }
+
+  { // Check that unget() works as expected when the remainder is larger than the buffer
+    std::ifstream is("test.dat");
+    assert(is.is_open());
+    is.rdbuf()->pubsetbuf(buffer, 12);
+    char buf[33];
+    buf[32] = '\0';
+    is.read(buf, 32);
+    assert(buf == std::string("This is a bunch of data so the t"));
+    for (size_t i = 0; i != 4; ++i)
+      is.unget();
+    assert(is.good());
+    is.read(buf, 4);
+    buf[4] = '\0';
+    assert(is.good());
+    assert(buf == std::string("he t"));
+  }
+
+  { // read an empty file
+    std::string empty_file = get_temp_file_name();
+    {
+      std::ofstream os(empty_file);
+    }
+    std::ifstream is(empty_file);
+    is.rdbuf()->pubsetbuf(nullptr, 64);
+
+    char buf[100];
+    is.read(buf, 100);
+    assert(is.eof());
+    assert(is.gcount() == 0);
+
+    is.clear();
+    is.unget();
+    assert(is.fail());
+    std::remove(empty_file.c_str());
+  }
+
+}
+
+static std::string make_pattern(std::size_t n) {
+  std::string s(n, '\0');
+  for (std::size_t i = 0; i != n; ++i)
+    s[i] = static_cast<char>('0' + (i % 10));
+  return s;
+}
+
+void large_file_tests() {
+  const std::string data = make_pattern(10000);
+  std::string file       = get_temp_file_name();
+  { // Prepare the file
+    std::ofstream os(file);
+    assert(os.write(data.data(), data.size()));
+  }
+
+  { // default buffer with a read larger than the buffer
+    std::ifstream is(file);
+    assert(is.is_open());
+    std::string buf;
+    buf.resize(8000);
+
+    is.read(buf.data(), 8000);
+    assert(is.gcount() == 8000);
+    assert(std::string(buf.data(), 8000) == data.substr(0, 8000));
+    is.unget();
+    assert(is.good());
+    assert(is.get() == data[7999]); // the ungotten character
+    assert(is.get() == data[8000]); // reading forward continues from the right place
+  }
+
+  { // EOF before the buffer is full
+    std::ifstream is(file);
+    assert(is.is_open());
+    is.rdbuf()->pubsetbuf(nullptr, 64);
+
+    std::string buf;
+    buf.resize(data.size() + 100);
+
+    is.read(buf.data(), data.size() + 100);
+    assert(is.gcount() == static_cast<std::streamsize>(data.size()));
+    assert(is.rdstate() == (std::ios::eofbit | std::ios::failbit));
+    is.clear(); // Clear the failbit due to trying to read more data than available
+    is.unget();
+    assert(is.good());
+    assert(is.get() == data.back());
+  }
+
+  std::remove(file.c_str());
+}
+
+int main(int, char**) {
+  small_file_tests();
+  large_file_tests();
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
index d9ccc2fe62914..35c4787e2a29f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
@@ -25,6 +25,14 @@
 
 #include "test_macros.h"
 
+void check_unget(std::filebuf* fb, char expected) {
+  auto c = fb->sungetc();
+  if (c != EOF) {
+    assert(c == expected);
+    assert(fb->sbumpc() == expected);
+  }
+}
+
 int main(int, char**) {
   std::vector<char> stream_buffer(10);
   std::ifstream fs("xsgetn.test.dat");
@@ -40,33 +48,39 @@ int main(int, char**) {
   { // Check that a read smaller than the buffer works fine
     assert(fb->sgetn(test_buffer.data(), 5) == 5);
     assert(std::string(test_buffer.data(), 5) == "this ");
+    check_unget(fb, ' ');
   }
   { // Check that reading up to the buffer end works fine
     assert(fb->sgetn(test_buffer.data(), 5) == 5);
     assert(std::string(test_buffer.data(), 5) == "is so");
+    check_unget(fb, 'o');
   }
   { // Check that reading from an empty buffer, but more than the buffer can
     // hold works fine
     test_buffer.resize(12);
     assert(fb->sgetn(test_buffer.data(), 12) == 12);
     assert(std::string(test_buffer.data(), 12) == "me random da");
+    check_unget(fb, 'a');
   }
   { // Check that reading from a non-empty buffer, and more than the buffer can
     // hold works fine Fill the buffer up
     test_buffer.resize(2);
     assert(fb->sgetn(test_buffer.data(), 2) == 2);
     assert(std::string(test_buffer.data(), 2) == "ta");
+    check_unget(fb, 'a');
 
     // Do the actual check
     test_buffer.resize(12);
     assert(fb->sgetn(test_buffer.data(), 12) == 12);
     assert(std::string(test_buffer.data(), 12) == " to be able ");
+    check_unget(fb, ' ');
   }
   { // Check that trying to read more than the file size works fine
     test_buffer.resize(30);
     assert(fb->sgetn(test_buffer.data(), 30) == 24);
     test_buffer.resize(24);
     assert(std::string(test_buffer.data(), 24) == "to test buffer behaviour");
+    check_unget(fb, 'r');
   }
   { // Ensure that the read fails gracefully with an unopened ifstream
     // See https://llvm.org/PR168628


From libcxx-commits at lists.llvm.org  Wed Jul 22 04:56:35 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 04:56:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a60aff3.af6ba3ed.1989e7.32db@mx.google.com>


philnik777 wrote:

> I added some more test coverage (first draft with Claude, then reviewed and modified).
> 
> LGTM now, but please have a look and let me know if you're OK with the added test coverage.

Yeah, looks reasonable.

https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Wed Jul 22 04:59:44 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 04:59:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2545R4 Read-Copy
	Update (RCU) (PR #175451)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/175451@github.com>
Message-ID: <6a60b0b0.eb165e5c.2e0c9a.c35d@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/175451

>From 3ac202dadda876cb915b906912a67d22312f5892 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 5 Dec 2025 18:41:50 +0000
Subject: [PATCH 1/9] init

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/include/CMakeLists.txt                 |  3 +
 libcxx/include/__configuration/experimental.h |  1 +
 libcxx/include/__rcu/rcu_domain.h             | 27 ++++++++
 libcxx/include/__rcu/rcu_obj_base.h           | 27 ++++++++
 libcxx/include/module.modulemap.in            |  7 ++
 libcxx/include/rcu                            | 54 +++++++++++++++
 libcxx/include/version                        |  3 +
 libcxx/modules/std.cppm.in                    |  1 +
 libcxx/modules/std/rcu.inc                    |  2 -
 .../rcu.version.compile.pass.cpp              | 68 +++++++++++++++++++
 .../version.version.compile.pass.cpp          |  4 +-
 .../generate_feature_test_macro_components.py |  3 +-
 13 files changed, 196 insertions(+), 6 deletions(-)
 create mode 100644 libcxx/include/__rcu/rcu_domain.h
 create mode 100644 libcxx/include/__rcu/rcu_obj_base.h
 create mode 100644 libcxx/include/rcu
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/rcu.version.compile.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 00665332a9ed7..7d69a41ca8e50 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -518,7 +518,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_raw_memory_algorithms``                        ``202411L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_rcu``                                          *unimplemented*
+    ``__cpp_lib_rcu``                                          ``202306L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_reference_wrapper``                            ``202403L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..79f43b32ab193 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -770,6 +770,8 @@ set(files
   __ranges/views.h
   __ranges/zip_transform_view.h
   __ranges/zip_view.h
+  __rcu/rcu_domain.h
+  __rcu/rcu_obj_base.h
   __split_buffer
   __std_mbstate_t.h
   __stop_token/atomic_unique_lock.h
@@ -1072,6 +1074,7 @@ set(files
   ranges
   ratio
   regex
+  rcu
   scoped_allocator
   semaphore
   set
diff --git a/libcxx/include/__configuration/experimental.h b/libcxx/include/__configuration/experimental.h
index bb38d8297c63d..6af9ea49f64a9 100644
--- a/libcxx/include/__configuration/experimental.h
+++ b/libcxx/include/__configuration/experimental.h
@@ -34,5 +34,6 @@
 #define _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 #define _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 #define _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
+#define _LIBCPP_HAS_EXPERIMENTAL_RCU _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 
 #endif // _LIBCPP___CONFIGURATION_EXPERIMENTAL_H
diff --git a/libcxx/include/__rcu/rcu_domain.h b/libcxx/include/__rcu/rcu_domain.h
new file mode 100644
index 0000000000000..9b41cb7758458
--- /dev/null
+++ b/libcxx/include/__rcu/rcu_domain.h
@@ -0,0 +1,27 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RCU_RCU_DOMAIN_H
+#define _LIBCPP___RCU_RCU_DOMAIN_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
+
+#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RCU_RCU_DOMAIN_H
diff --git a/libcxx/include/__rcu/rcu_obj_base.h b/libcxx/include/__rcu/rcu_obj_base.h
new file mode 100644
index 0000000000000..faff897a1f159
--- /dev/null
+++ b/libcxx/include/__rcu/rcu_obj_base.h
@@ -0,0 +1,27 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RCU_RCU_OBJ_BASE_H
+#define _LIBCPP___RCU_RCU_OBJ_BASE_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
+
+#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RCU_RCU_OBJ_BASE_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..3748f6ad356de 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2006,6 +2006,13 @@ module std {
     export *
   }
 
+  module rcu {
+    module rcu_domain      { header "__rcu/rcu_domain.h" }
+    module rcu_obj_base    { header "__rcu/rcu_obj_base.h" }
+    header "rcu"
+    export *
+  }
+
   module regex {
     header "regex"
     export *
diff --git a/libcxx/include/rcu b/libcxx/include/rcu
new file mode 100644
index 0000000000000..833aae4db6909
--- /dev/null
+++ b/libcxx/include/rcu
@@ -0,0 +1,54 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_RCU
+#define _LIBCPP_RCU
+
+/*
+
+namespace std {
+  // [saferecl.rcu.base], class template rcu_obj_base
+  template<class T, class D = default_delete<T>> class rcu_obj_base;
+
+  // [saferecl.rcu.domain], class rcu_domain
+  class rcu_domain;
+
+  // [saferecl.rcu.domain.func], non-member functions
+  rcu_domain& rcu_default_domain() noexcept;
+  void rcu_synchronize(rcu_domain& dom = rcu_default_domain()) noexcept;
+  void rcu_barrier(rcu_domain& dom = rcu_default_domain()) noexcept;
+  template<class T, class D = default_delete<T>>
+    void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain());
+}
+
+*/
+
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+#  include <__cxx03/__config>
+#else
+#  include <__config>
+
+#  if _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
+
+#    if _LIBCPP_STD_VER >= 26
+#      include <__rcu/rcu_domain.h>
+#      include <__rcu/rcu_obj_base.h>
+#    endif
+
+#    include <version>
+
+#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#      pragma GCC system_header
+#    endif
+
+#  endif // _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
+
+#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
+#endif // _LIBCPP_RCU
diff --git a/libcxx/include/version b/libcxx/include/version
index 7545d187ca7f1..6b456ff3165ca 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -625,6 +625,9 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # undef  __cpp_lib_raw_memory_algorithms
 # define __cpp_lib_raw_memory_algorithms                202411L
 // # define __cpp_lib_rcu                                  202306L
+# if _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
+#   define __cpp_lib_rcu                                202306L
+# endif
 # define __cpp_lib_reference_wrapper                    202403L
 # define __cpp_lib_saturation_arithmetic                202603L
 // # define __cpp_lib_senders                              202406L
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index b9a491653659a..a7d41b5ff9573 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -91,6 +91,7 @@ module;
 #include <random>
 #include <ranges>
 #include <ratio>
+#include <rcu>
 #include <regex>
 #include <scoped_allocator>
 #include <semaphore>
diff --git a/libcxx/modules/std/rcu.inc b/libcxx/modules/std/rcu.inc
index abf5afad30c13..0dbad726474e7 100644
--- a/libcxx/modules/std/rcu.inc
+++ b/libcxx/modules/std/rcu.inc
@@ -8,7 +8,6 @@
 //===----------------------------------------------------------------------===//
 
 export namespace std {
-#if 0
 #  if _LIBCPP_STD_VER >= 23
   // 2.2.3, class template rcu_obj_base using std::rcu_obj_base;
   // 2.2.4, class rcu_domain
@@ -18,5 +17,4 @@ export namespace std {
   using std::rcu_retire;
   using std::rcu_synchronize;
 #  endif // _LIBCPP_STD_VER >= 23
-#endif
 } // namespace std
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/rcu.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/rcu.version.compile.pass.cpp
new file mode 100644
index 0000000000000..62331b8564d48
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/rcu.version.compile.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <rcu>
+
+// Test the feature test macros defined by <rcu>
+
+// clang-format off
+
+#include <rcu>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_rcu
+#    error "__cpp_lib_rcu should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifdef __cpp_lib_rcu
+#    error "__cpp_lib_rcu should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_rcu
+#    error "__cpp_lib_rcu should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifdef __cpp_lib_rcu
+#    error "__cpp_lib_rcu should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifdef __cpp_lib_rcu
+#    error "__cpp_lib_rcu should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  if !defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU)
+#    ifndef __cpp_lib_rcu
+#      error "__cpp_lib_rcu should be defined in c++26"
+#    endif
+#    if __cpp_lib_rcu != 202306L
+#      error "__cpp_lib_rcu should have the value 202306L in c++26"
+#    endif
+#  else
+#    ifdef __cpp_lib_rcu
+#      error "__cpp_lib_rcu should not be defined when the requirement '!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU)' is not met!"
+#    endif
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
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 9e987346c599b..09a061a282e55 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
@@ -7857,7 +7857,7 @@
 #    error "__cpp_lib_raw_memory_algorithms should have the value 202411L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
+#  if !defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU)
 #    ifndef __cpp_lib_rcu
 #      error "__cpp_lib_rcu should be defined in c++26"
 #    endif
@@ -7866,7 +7866,7 @@
 #    endif
 #  else
 #    ifdef __cpp_lib_rcu
-#      error "__cpp_lib_rcu should not be defined because it is unimplemented in libc++!"
+#      error "__cpp_lib_rcu should not be defined when the requirement '!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU)' is not met!"
 #    endif
 #  endif
 
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 13a5dab01b74e..9d3869e7db11e 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1217,7 +1217,8 @@ def add_version_header(tc):
             "headers": [
                 "rcu"  # TODO verify this entry since the paper was underspecified.
             ],
-            "unimplemented": True,
+            "test_suite_guard": "!defined(_LIBCPP_VERSION) || (_LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU)",
+            "libcxx_guard": "_LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU",
         },
         {
             "name": "__cpp_lib_reference_from_temporary",

>From 94bd85c6af6e3382c0e545bb87c6d53f39261ced Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 7 Jan 2026 21:05:23 +0000
Subject: [PATCH 2/9] init impl

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__rcu/rcu_domain.h             | 211 ++++++++++++++++++
 libcxx/include/__rcu/rcu_list.h               |  36 +++
 libcxx/include/__rcu/rcu_obj_base.h           |  20 ++
 libcxx/include/__rcu/readme.md                |  69 ++++++
 libcxx/include/module.modulemap.in            |   1 +
 .../saferecl/saferecl.rcu/general.pass.cpp    | 117 ++++++++++
 7 files changed, 455 insertions(+)
 create mode 100644 libcxx/include/__rcu/rcu_list.h
 create mode 100644 libcxx/include/__rcu/readme.md
 create mode 100644 libcxx/test/std/thread/saferecl/saferecl.rcu/general.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 79f43b32ab193..8b7e25cda5eae 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -771,6 +771,7 @@ set(files
   __ranges/zip_transform_view.h
   __ranges/zip_view.h
   __rcu/rcu_domain.h
+  __rcu/rcu_list.h
   __rcu/rcu_obj_base.h
   __split_buffer
   __std_mbstate_t.h
diff --git a/libcxx/include/__rcu/rcu_domain.h b/libcxx/include/__rcu/rcu_domain.h
index 9b41cb7758458..fbd19933e171d 100644
--- a/libcxx/include/__rcu/rcu_domain.h
+++ b/libcxx/include/__rcu/rcu_domain.h
@@ -12,6 +12,16 @@
 
 #include <__config>
 
+#include <__atomic/atomic.h>
+#include <atomic>
+#include <cstdint>
+#include <map>
+#include <mutex>
+#include <vector>
+
+// todo debug
+#include <cstdio>
+
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
@@ -20,6 +30,207 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
 
+template <class _ThreadLocal>
+class __thread_local_owner {
+  // todo put globals in experimental dylib
+  inline static thread_local map<const __thread_local_owner*, _ThreadLocal> __thread_local_instances;
+
+  // Keep track of all thread-local instances owned by this owner.
+  // Only emplaced the first time a thread is trying to access its thread-local instance.
+  vector<atomic_ref<_ThreadLocal>> __owned_instances_;
+  mutex __mtx_;
+
+  void __register(_ThreadLocal& __obj) {
+    lock_guard<std::mutex> __lg(__mtx_);
+    __owned_instances_.emplace_back(__obj);
+  }
+
+public:
+  __thread_local_owner()                       = default;
+  __thread_local_owner(__thread_local_owner&&) = delete;
+
+  atomic_ref<_ThreadLocal> __get_current_thread_instance() {
+    auto __it = __thread_local_instances.find(this);
+    if (__it == __thread_local_instances.end()) {
+      auto [new_it, _] = __thread_local_instances.try_emplace(this, _ThreadLocal());
+      auto& __obj      = new_it->second;
+      __register(__obj);
+      return atomic_ref(__obj);
+    }
+    return atomic_ref(__it->second);
+  }
+
+  template <class _Func>
+  void __for_each_owned_instances(_Func&& __f) {
+    unique_lock<std::mutex> __lock(__mtx_);
+    for (auto __instance : __owned_instances_) {
+      __f(__instance);
+    }
+  }
+};
+
+// Adopted the 2-phase implementation in the section
+// "3) General-Purpose RCU" of the paper
+// http://www.rdrop.com/users/paulmck/RCU/urcu-supp-accepted.2011.08.30a.pdf
+
+struct __reader_states {
+  // bit 15 is the grace period phase 0 or 1
+  // bits 0-14 is the reader nest level
+  //
+  // a thread can have nested reader locks, such as
+  // domain.lock();   // nest level = 1
+  // domain.lock();   // nest level = 2
+  // ...
+  // domain.unlock(); // nest level = 1
+  // domain.unlock(); // nest level = 0
+
+  static constexpr uint16_t __grace_period_phase_mask = 0b1000'0000'0000'0000;
+  static constexpr uint16_t __reader_nest_level_mask  = 0b0111'1111'1111'1111;
+
+  using __state_type = uint16_t;
+
+  __thread_local_owner<__state_type> __per_thread_states_;
+
+  static uint16_t __get_grace_period_phase(__state_type __state) { return __state & __grace_period_phase_mask; }
+
+  static uint16_t __get_reader_nest_level(__state_type __state) { return __state & __reader_nest_level_mask; }
+
+  static bool __is_quiescent_state(__state_type __state) { return __get_reader_nest_level(__state) == 0; }
+};
+
+class rcu_domain {
+  __reader_states __reader_states_;
+
+  // only the highest bit is used for the phase.
+  std::atomic<__reader_states::__state_type> __global_reader_phase_{};
+
+  // only one thread is allowed to update concurrently
+  std::mutex __reclamation_mutex_; // todo this is not noexcept
+
+  std::atomic<bool> __waiting_flag_ = false;
+
+  rcu_domain() = default;
+
+  friend struct __rcu_domain_access;
+
+  // todo put globals in dylib
+  static rcu_domain& __rcu_default_domain() noexcept {
+    static rcu_domain __default_domain;
+    return __default_domain;
+  }
+
+  void __synchronize() noexcept {
+    __cxx_atomic_thread_fence(memory_order_seq_cst);
+    std::unique_lock __lk(__reclamation_mutex_);
+    std::printf("rcu_domain::__synchronize() going through phase 1\n");
+    __update_phase_and_wait();
+    __barrier();
+    std::printf("rcu_domain::__synchronize() going through phase 2\n");
+    __update_phase_and_wait();
+    __lk.unlock();
+    __cxx_atomic_thread_fence(memory_order_seq_cst);
+  }
+
+  void __update_phase_and_wait() noexcept {
+    // Flip the global phase
+    auto __old_phase =
+        __global_reader_phase_.fetch_xor(__reader_states::__grace_period_phase_mask, std::memory_order_relaxed);
+    auto __new_phase = __old_phase ^ __reader_states::__grace_period_phase_mask;
+    std::printf("rcu_domain::__update_phase_and_wait() new phase: 0x%04x\n", __new_phase);
+
+    __barrier();
+    // Wait for all threads to quiesce in the old phase
+    while (__any_reader_in_ongoing_grace_period(__new_phase)) {
+      __waiting_flag_.store(true, std::memory_order_relaxed);
+      __waiting_flag_.wait(true, std::memory_order_relaxed);
+    }
+    __waiting_flag_.store(false, std::memory_order_relaxed);
+  }
+
+  bool __any_reader_in_ongoing_grace_period(__reader_states::__state_type __global_phase) noexcept {
+    bool __any_ongoing = false;
+    __reader_states_.__per_thread_states_.__for_each_owned_instances(
+        [this, __global_phase, &__any_ongoing](atomic_ref<__reader_states::__state_type> __state) {
+          if (__is_grace_period_ongoing(__state.load(memory_order_relaxed), __global_phase)) {
+            __any_ongoing = true;
+          }
+        });
+    return __any_ongoing;
+  }
+
+  bool __is_grace_period_ongoing(__reader_states::__state_type __thread_state,
+                                 __reader_states::__state_type __global_phase) const noexcept {
+    // https://lwn.net/Articles/323929/
+    // The phase is flipped at the beginning of a grace period.
+    // Any readers that started before the flip will have the old phase
+    // and we consider them as ongoing that we need to wait for before we can close the grace period.
+    return !__reader_states::__is_quiescent_state(__thread_state) &&
+           __reader_states::__get_grace_period_phase(__thread_state) != __global_phase;
+  }
+
+  void __barrier() noexcept { asm volatile("" : : : "memory"); }
+
+public:
+  rcu_domain(const rcu_domain&)            = delete;
+  rcu_domain& operator=(const rcu_domain&) = delete;
+
+  void printAllReaderStatesInHex() {
+    __reader_states_.__per_thread_states_.__for_each_owned_instances([](auto __state_ref) {
+      std::printf("Reader state: 0x%04x\n", __state_ref.load());
+    });
+  }
+
+  void lock() {
+    auto __current_thread_state_ref = __reader_states_.__per_thread_states_.__get_current_thread_instance();
+
+    if ((__reader_states::__is_quiescent_state(__current_thread_state_ref.load(memory_order_relaxed)))) {
+      // Entering a read-side critical section from a quiescent state.
+      __current_thread_state_ref.store(
+          __reader_states::__state_type(__global_reader_phase_.load(std::memory_order_relaxed) | uint16_t(1)),
+          memory_order_relaxed);
+      __cxx_atomic_thread_fence(memory_order_seq_cst);
+    } else {
+      // Already in read-side critical section, just increment the nest level.
+      __current_thread_state_ref.fetch_add(1, memory_order_relaxed);
+    }
+  }
+
+  bool try_lock() {
+    lock();
+    return true;
+  }
+
+  void unlock() {
+    auto __current_thread_state_ref = __reader_states_.__per_thread_states_.__get_current_thread_instance();
+    __cxx_atomic_thread_fence(memory_order_seq_cst);
+    // Decrement the nest level.
+    auto __old_state = __current_thread_state_ref.fetch_sub(1, memory_order_relaxed);
+
+    if (__reader_states::__get_reader_nest_level(__old_state) == 1 && __waiting_flag_.load(memory_order_relaxed)) {
+      // Transitioning to quiescent state, wake up waiters.
+      __waiting_flag_.store(false, std::memory_order_relaxed);
+      __waiting_flag_.notify_all();
+    }
+  }
+};
+
+struct __rcu_domain_access {
+  static rcu_domain& __rcu_default_domain() noexcept { return rcu_domain::__rcu_default_domain(); }
+  static void __rcu_synchronize(rcu_domain& __dom) noexcept { __dom.__synchronize(); }
+};
+
+// todo put it in the experimental dylib
+inline rcu_domain& rcu_default_domain() noexcept { return __rcu_domain_access::__rcu_default_domain(); }
+
+// todo put it in the experimental dylib
+inline void rcu_synchronize(rcu_domain& __dom = rcu_default_domain()) noexcept {
+  __rcu_domain_access::__rcu_synchronize(__dom);
+}
+
+void rcu_barrier(rcu_domain& dom = rcu_default_domain()) noexcept;
+template <class T, class D = default_delete<T>>
+void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain());
+
 #endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__rcu/rcu_list.h b/libcxx/include/__rcu/rcu_list.h
new file mode 100644
index 0000000000000..65ce35f1cd7a1
--- /dev/null
+++ b/libcxx/include/__rcu/rcu_list.h
@@ -0,0 +1,36 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RCU_RCU_OBJ_BASE_H
+#define _LIBCPP___RCU_RCU_OBJ_BASE_H
+
+#include <__config>
+#include <__functional/function.h>
+#include <__rcu/rcu_domain.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
+
+struct __rcu_node {
+  function<void()> __callback_{};
+  __rcu_node* __next_ = nullptr;
+};
+
+struct __intrusive_linked_list_view {};
+
+#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RCU_RCU_OBJ_BASE_H
diff --git a/libcxx/include/__rcu/rcu_obj_base.h b/libcxx/include/__rcu/rcu_obj_base.h
index faff897a1f159..7b58b0c8dfee7 100644
--- a/libcxx/include/__rcu/rcu_obj_base.h
+++ b/libcxx/include/__rcu/rcu_obj_base.h
@@ -11,6 +11,9 @@
 #define _LIBCPP___RCU_RCU_OBJ_BASE_H
 
 #include <__config>
+#include <__memory/unique_ptr.h> // for default_delete
+#include <__rcu/rcu_domain.h>
+#include <__rcu/rcu_list.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -20,6 +23,23 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
 
+template <class T, class D = default_delete<T>>
+class rcu_obj_base : private __rcu_node {
+public:
+  void retire(D d = D(), rcu_domain& dom = rcu_default_domain()) noexcept;
+
+protected:
+  rcu_obj_base()                               = default;
+  rcu_obj_base(const rcu_obj_base&)            = default;
+  rcu_obj_base(rcu_obj_base&&)                 = default;
+  rcu_obj_base& operator=(const rcu_obj_base&) = default;
+  rcu_obj_base& operator=(rcu_obj_base&&)      = default;
+  ~rcu_obj_base()                              = default;
+
+private:
+  D deleter; // exposition only
+};
+
 #endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__rcu/readme.md b/libcxx/include/__rcu/readme.md
new file mode 100644
index 0000000000000..ae83baf44dc61
--- /dev/null
+++ b/libcxx/include/__rcu/readme.md
@@ -0,0 +1,69 @@
+# folly
+
+
+## domain
+
+- `lock` :
+  - increment the counter with the epoch number == `_version`
+
+- `unlock`:
+  - decrement the counter (without arguments)
+
+- `retire` :
+  - push `cb` to `_q`
+  - if long time since last sync, try try_lock sync mutex and `half_sync` without blocking (to get some finished nodes)
+    - put the finished `cb`s to `executor` (immideately execute)
+
+- `half_sync` :
+  - `current` is the current `_version` number, `next` is +1
+  - move all nodes from `_q` to `queues[0]` (cannot just have a single `queue` and swap because of later readers) (thread safe with concurrent more push to `_q`)
+  - if is blocking, wait for zero for the epoch `next & 1`
+  - else, if epoch `next & 1` has reader is true, return
+  - at this stage, `next & 1` epoch reader zero, if late reader comes, it increments the `current` reader count. (no concurrent half_sync as it is mutex protected)
+  - move all nodes from `queue[1]` to `finished`, and move all nodes from `queue[0]` to `queue[1]`
+  - store `next` to `_version`
+  - notify threads that turn_.waitForTurn(next)
+
+- `sync`
+  - `current` is the current `_version` number, `target` is +2
+  - while true
+     - if current `work_` is smaller than `target` and cas to `target` succeeded,
+        `half_sync` until `version_` >= `target`, and run all the finished `cb`s, `return`
+     - else 
+       - if `version_` >= `target` , `return`
+       - else `turn_.waitForTurn(work)` (so if other's target >= our target, other's second epoch half sync will unblock us)
+
+
+
+## executor
+
+- immediately invoke
+- queue if the `f` schedule another `f2` in the `executor`
+
+
+## example
+
+version == 0
+
+  T1                            T2                       T3
+
+Reader1 lock (0, 1)
+read obj0
+                       obj = obj1
+                       retire(obj0)
+                       sync, target = 2
+                       half, next = 1
+                       wait_zero(epoch == 1)
+                       cb0 -> queue[1]
+                       version = 1
+                       notify(1)
+                                                   Read2 lock (1, 1)
+                                                   read obj1
+                      half, next = 2
+unlock (0,0)
+                      wait_zero(epoch==0)
+                      cb0 -> finished
+                      run cb0
+                      version = 2
+                      notify(2)
+                                                  unlock
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3748f6ad356de..21cee56fc5eb9 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2008,6 +2008,7 @@ module std {
 
   module rcu {
     module rcu_domain      { header "__rcu/rcu_domain.h" }
+    module rcu_list        { header "__rcu/rcu_list.h" }
     module rcu_obj_base    { header "__rcu/rcu_obj_base.h" }
     header "rcu"
     export *
diff --git a/libcxx/test/std/thread/saferecl/saferecl.rcu/general.pass.cpp b/libcxx/test/std/thread/saferecl/saferecl.rcu/general.pass.cpp
new file mode 100644
index 0000000000000..88412976f7a62
--- /dev/null
+++ b/libcxx/test/std/thread/saferecl/saferecl.rcu/general.pass.cpp
@@ -0,0 +1,117 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <rcu>
+
+#include <rcu>
+#include <thread>
+#include <chrono>
+#include <iostream>
+#include <print>
+
+#include "__rcu/rcu_domain.h"
+#include "make_test_thread.h"
+#include "test_macros.h"
+
+void log(auto start, std::string_view msg) {
+  auto now = std::chrono::system_clock::now();
+  std::println(std::cout, "[{:%H:%M:%S}] {}", now - start, msg);
+}
+
+int loop_num = 1;
+
+int main(int, char**) {
+  auto start = std::chrono::system_clock::now();
+  auto t1    = support::make_test_thread([start]() {
+    std::rcu_domain& dom = std::rcu_default_domain();
+    for (int i = 0; i < loop_num; ++i) {
+      log(start, "t1: entering rcu read-side critical section " + std::to_string(i));
+      dom.lock();
+      log(start, "t1: sleeping");
+      std::this_thread::sleep_for(std::chrono::seconds(1));
+      log(start, "t1: leaving rcu read-side critical section " + std::to_string(i));
+      dom.unlock();
+       log(start, "t1: printing all reader states");
+      dom.printAllReaderStatesInHex();
+    }
+  });
+
+  auto t2 = support::make_test_thread([start]() {
+    std::this_thread::sleep_for(std::chrono::milliseconds(200));
+    std::rcu_domain& dom = std::rcu_default_domain();
+    for (int i = 0; i < loop_num; ++i) {
+      log(start, "t2: entering rcu read-side critical section " + std::to_string(i));
+      dom.lock();
+      log(start, "t2: sleeping");
+      std::this_thread::sleep_for(std::chrono::seconds(1));
+      log(start, "t2: leaving rcu read-side critical section " + std::to_string(i));
+      dom.unlock();
+       log(start, "t2: printing all reader states");
+      dom.printAllReaderStatesInHex();
+    }
+  });
+
+  auto t3 = support::make_test_thread([start]() {
+    std::this_thread::sleep_for(std::chrono::milliseconds(1100));
+    std::rcu_domain& dom = std::rcu_default_domain();
+    for (int i = 0; i < loop_num; ++i) {
+      log(start, "t3: entering rcu read-side critical section " + std::to_string(i));
+      dom.lock();
+      log(start, "t3: sleeping");
+      std::this_thread::sleep_for(std::chrono::seconds(1));
+      log(start, "t3: leaving rcu read-side critical section " + std::to_string(i));
+      dom.unlock();
+       log(start, "t3: printing all reader states");
+      dom.printAllReaderStatesInHex();
+    }
+  });
+  auto t4 = support::make_test_thread([start]() {
+    std::this_thread::sleep_for(std::chrono::milliseconds(3000));
+    std::rcu_domain& dom = std::rcu_default_domain();
+    for (int i = 0; i < loop_num; ++i) {
+      log(start, "t4: entering rcu read-side critical section " + std::to_string(i));
+      dom.lock();
+      log(start, "t4: sleeping");
+      std::this_thread::sleep_for(std::chrono::seconds(1));
+      log(start, "t4: leaving rcu read-side critical section " + std::to_string(i));
+      dom.unlock();
+       log(start, "t4: printing all reader states");
+      dom.printAllReaderStatesInHex();
+    }
+  });
+  /*
+  auto t3 = support::make_test_thread([start]() {
+    std::rcu_domain& dom = std::rcu_default_domain();
+    log(start, "t3: entering rcu read-side critical section");
+    dom.lock();
+    log(start, "t3: sleeping");
+    std::this_thread::sleep_for(std::chrono::seconds(3));
+    log(start, "t3: leaving rcu read-side critical section");
+    dom.unlock();
+  });  */
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(10));
+  for (int i = 0; i < loop_num+5; ++i) {
+
+  log(start, "t0: calling rcu_synchronize" + std::to_string(i));
+  std::rcu_synchronize();
+  log(start, "t0: rcu_synchronize returned" + std::to_string(i));
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+  }
+
+
+  t1.join();
+  t2.join();
+  t3.join();
+  t4.join();
+
+  return 1;
+}

>From e9e21153a38efe0366e4a5b1d6923fd9856fcc47 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 11 Jan 2026 18:59:42 +0000
Subject: [PATCH 3/9] callbacks

---
 libcxx/include/__rcu/rcu_domain.h             |  76 +++++++--
 libcxx/include/__rcu/rcu_list.h               |  51 +++++-
 libcxx/include/__rcu/rcu_obj_base.h           |   8 +-
 .../read_write_lock_compare.pass.cpp          | 154 ++++++++++++++++++
 .../saferecl/saferecl.rcu/retire.pass.cpp     | 150 +++++++++++++++++
 5 files changed, 417 insertions(+), 22 deletions(-)
 create mode 100644 libcxx/test/std/thread/saferecl/saferecl.rcu/read_write_lock_compare.pass.cpp
 create mode 100644 libcxx/test/std/thread/saferecl/saferecl.rcu/retire.pass.cpp

diff --git a/libcxx/include/__rcu/rcu_domain.h b/libcxx/include/__rcu/rcu_domain.h
index fbd19933e171d..8863849f09362 100644
--- a/libcxx/include/__rcu/rcu_domain.h
+++ b/libcxx/include/__rcu/rcu_domain.h
@@ -13,6 +13,9 @@
 #include <__config>
 
 #include <__atomic/atomic.h>
+#include <__rcu/rcu_list.h>
+
+// todo replace with internal headers
 #include <atomic>
 #include <cstdint>
 #include <map>
@@ -45,6 +48,8 @@ class __thread_local_owner {
     __owned_instances_.emplace_back(__obj);
   }
 
+  // todo: deregister on thread exit?
+
 public:
   __thread_local_owner()                       = default;
   __thread_local_owner(__thread_local_owner&&) = delete;
@@ -99,19 +104,32 @@ struct __reader_states {
 };
 
 class rcu_domain {
+  // todo optimize the layout
+
   __reader_states __reader_states_;
 
   // only the highest bit is used for the phase.
   std::atomic<__reader_states::__state_type> __global_reader_phase_{};
 
   // only one thread is allowed to update concurrently
-  std::mutex __reclamation_mutex_; // todo this is not noexcept
+  std::mutex __grace_period_mutex_; // todo this is not noexcept
+
+  std::atomic<bool> __grace_period_waiting_flag_ = false;
 
-  std::atomic<bool> __waiting_flag_ = false;
+  // todo: maybe use a lock-free queue
+  std::mutex __retire_queue_mutex_; // todo this is not noexcept
+  __rcu_singly_list_view __retired_callback_queue_;
+
+  // these two queues do not need extra synchronization
+  // as they are always processed under the grace period mutex
+  __rcu_singly_list_view __callbacks_phase_1_;
+  __rcu_singly_list_view __callbacks_phase_2_;
 
   rcu_domain() = default;
 
   friend struct __rcu_domain_access;
+  template <class, class>
+  friend class rcu_obj_base;
 
   // todo put globals in dylib
   static rcu_domain& __rcu_default_domain() noexcept {
@@ -119,32 +137,58 @@ class rcu_domain {
     return __default_domain;
   }
 
+  template <class Callback>
+  void __retire_callback(Callback&& __cb) noexcept {
+    auto* __node        = new __rcu_node();
+    __node->__callback_ = std::forward<Callback>(__cb);
+    std::unique_lock __lk(__retire_queue_mutex_);
+    __retired_callback_queue_.__push_back(__node);
+  }
+
   void __synchronize() noexcept {
     __cxx_atomic_thread_fence(memory_order_seq_cst);
-    std::unique_lock __lk(__reclamation_mutex_);
-    std::printf("rcu_domain::__synchronize() going through phase 1\n");
-    __update_phase_and_wait();
+    std::unique_lock __lk(__grace_period_mutex_);
+    //std::printf("rcu_domain::__synchronize() going through phase 1\n");
+    auto __ready_callbacks = __update_phase_and_wait();
+
+    // Invoke the ready callbacks outside of the grace period mutex
+    __lk.unlock();
+    __ready_callbacks.__for_each([](auto* __node) { __node->__callback_(); });
+    __lk.lock();
+
     __barrier();
-    std::printf("rcu_domain::__synchronize() going through phase 2\n");
-    __update_phase_and_wait();
+    //std::printf("rcu_domain::__synchronize() going through phase 2\n");
+    __ready_callbacks = __update_phase_and_wait();
+
+    // Invoke the ready callbacks outside of the grace period mutex
     __lk.unlock();
+    __ready_callbacks.__for_each([](auto* __node) { __node->__callback_(); });
     __cxx_atomic_thread_fence(memory_order_seq_cst);
   }
 
-  void __update_phase_and_wait() noexcept {
+  __rcu_singly_list_view __update_phase_and_wait() noexcept {
+    std::unique_lock __retire_lk(__retire_queue_mutex_);
+    __callbacks_phase_1_.__splice_back(__retired_callback_queue_);
+    __retire_lk.unlock();
+
     // Flip the global phase
     auto __old_phase =
         __global_reader_phase_.fetch_xor(__reader_states::__grace_period_phase_mask, std::memory_order_relaxed);
     auto __new_phase = __old_phase ^ __reader_states::__grace_period_phase_mask;
-    std::printf("rcu_domain::__update_phase_and_wait() new phase: 0x%04x\n", __new_phase);
+    //std::printf("rcu_domain::__update_phase_and_wait() new phase: 0x%04x\n", __new_phase);
 
     __barrier();
     // Wait for all threads to quiesce in the old phase
     while (__any_reader_in_ongoing_grace_period(__new_phase)) {
-      __waiting_flag_.store(true, std::memory_order_relaxed);
-      __waiting_flag_.wait(true, std::memory_order_relaxed);
+      __grace_period_waiting_flag_.store(true, std::memory_order_relaxed);
+      __grace_period_waiting_flag_.wait(true, std::memory_order_relaxed);
     }
-    __waiting_flag_.store(false, std::memory_order_relaxed);
+    __grace_period_waiting_flag_.store(false, std::memory_order_relaxed);
+
+    __rcu_singly_list_view __ready_callbacks;
+    __ready_callbacks.__splice_back(__callbacks_phase_2_);
+    __callbacks_phase_2_.__splice_back(__callbacks_phase_1_);
+    return __ready_callbacks;
   }
 
   bool __any_reader_in_ongoing_grace_period(__reader_states::__state_type __global_phase) noexcept {
@@ -206,10 +250,11 @@ class rcu_domain {
     // Decrement the nest level.
     auto __old_state = __current_thread_state_ref.fetch_sub(1, memory_order_relaxed);
 
-    if (__reader_states::__get_reader_nest_level(__old_state) == 1 && __waiting_flag_.load(memory_order_relaxed)) {
+    if (__reader_states::__get_reader_nest_level(__old_state) == 1 &&
+        __grace_period_waiting_flag_.load(memory_order_relaxed)) {
       // Transitioning to quiescent state, wake up waiters.
-      __waiting_flag_.store(false, std::memory_order_relaxed);
-      __waiting_flag_.notify_all();
+      __grace_period_waiting_flag_.store(false, std::memory_order_relaxed);
+      __grace_period_waiting_flag_.notify_all();
     }
   }
 };
@@ -228,6 +273,7 @@ inline void rcu_synchronize(rcu_domain& __dom = rcu_default_domain()) noexcept {
 }
 
 void rcu_barrier(rcu_domain& dom = rcu_default_domain()) noexcept;
+
 template <class T, class D = default_delete<T>>
 void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain());
 
diff --git a/libcxx/include/__rcu/rcu_list.h b/libcxx/include/__rcu/rcu_list.h
index 65ce35f1cd7a1..2547d7223a7cc 100644
--- a/libcxx/include/__rcu/rcu_list.h
+++ b/libcxx/include/__rcu/rcu_list.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___RCU_RCU_OBJ_BASE_H
-#define _LIBCPP___RCU_RCU_OBJ_BASE_H
+#ifndef _LIBCPP___RCU_RCU_LIST_H
+#define _LIBCPP___RCU_RCU_LIST_H
 
 #include <__config>
 #include <__functional/function.h>
@@ -27,10 +27,53 @@ struct __rcu_node {
   __rcu_node* __next_ = nullptr;
 };
 
-struct __intrusive_linked_list_view {};
+class __rcu_singly_list_view {
+private:
+  __rcu_node* __head_ = nullptr;
+  __rcu_node* __tail_ = nullptr;
+
+public:
+  void __splice_back(__rcu_singly_list_view& __other) noexcept {
+    if (__other.__head_ == nullptr) {
+      return;
+    }
+    if (__head_ == nullptr) {
+      __head_ = __other.__head_;
+      __tail_ = __other.__tail_;
+    } else {
+      __tail_->__next_ = __other.__head_;
+      __tail_          = __other.__tail_;
+    }
+    __other.__head_ = nullptr;
+    __other.__tail_ = nullptr;
+  }
+
+  void __push_back(__rcu_node* __node) noexcept {
+    // assert(__node->__next_ == nullptr);
+    if (__head_ == nullptr) {
+      __head_ = __node;
+      __tail_ = __node;
+    } else {
+      __tail_->__next_ = __node;
+      __tail_          = __node;
+    }
+  }
+
+  template <class _Func>
+  void __for_each(_Func&& __f) noexcept {
+    __rcu_node* __current = __head_;
+    while (__current != nullptr) {
+      // __f could delete __current, so we need to get the next pointer first
+      auto __next = __current->__next_;
+      __f(__current);
+      __current = __next;
+    }
+  }
+};
+
 
 #endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___RCU_RCU_OBJ_BASE_H
+#endif // _LIBCPP___RCU_RCU_LIST_H
diff --git a/libcxx/include/__rcu/rcu_obj_base.h b/libcxx/include/__rcu/rcu_obj_base.h
index 7b58b0c8dfee7..3d1de353613a5 100644
--- a/libcxx/include/__rcu/rcu_obj_base.h
+++ b/libcxx/include/__rcu/rcu_obj_base.h
@@ -26,7 +26,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class T, class D = default_delete<T>>
 class rcu_obj_base : private __rcu_node {
 public:
-  void retire(D d = D(), rcu_domain& dom = rcu_default_domain()) noexcept;
+  void retire(D d = D(), rcu_domain& dom = rcu_default_domain()) noexcept {
+    auto ptr = static_cast<T*>(this);
+    dom.__retire_callback(
+        [ptr, d = std::move(d)]() mutable { d(ptr); });
+  }
 
 protected:
   rcu_obj_base()                               = default;
@@ -36,8 +40,6 @@ class rcu_obj_base : private __rcu_node {
   rcu_obj_base& operator=(rcu_obj_base&&)      = default;
   ~rcu_obj_base()                              = default;
 
-private:
-  D deleter; // exposition only
 };
 
 #endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
diff --git a/libcxx/test/std/thread/saferecl/saferecl.rcu/read_write_lock_compare.pass.cpp b/libcxx/test/std/thread/saferecl/saferecl.rcu/read_write_lock_compare.pass.cpp
new file mode 100644
index 0000000000000..2fc79013994e1
--- /dev/null
+++ b/libcxx/test/std/thread/saferecl/saferecl.rcu/read_write_lock_compare.pass.cpp
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <rcu>
+
+#include <atomic>
+#include <rcu>
+#include <shared_mutex>
+#include <stop_token>
+#include <thread>
+#include <chrono>
+#include <iostream>
+#include <print>
+#include <string>
+
+#include "__rcu/rcu_domain.h"
+#include "make_test_thread.h"
+#include "test_macros.h"
+
+constexpr int num_reader = 4;
+const std::chrono::seconds test_time(10);
+
+struct alignas(128) MyObject : public std::rcu_obj_base<MyObject> {
+  std::string data_;
+
+  inline static int instance_count = 0;
+  MyObject() : data_(std::to_string(instance_count++) + " instance very very very long string") {}
+
+  void doWork() {
+    auto spin_for = [](std::chrono::microseconds us) {
+      auto start = std::chrono::high_resolution_clock::now();
+      while (std::chrono::high_resolution_clock::now() - start < us)
+        ;
+    };
+    using namespace std::chrono_literals;
+    spin_for(10us);
+  }
+};
+
+
+void test_read_write_lock() {
+  MyObject* globalObjRWLock = new MyObject();
+  std::shared_mutex globalObjMutex;
+
+  std::vector<std::jthread> readers;
+  readers.reserve(num_reader);
+
+  auto reader_func = [&globalObjRWLock, &globalObjMutex](std::stop_token token) {
+    int read_count = 0;
+    while (!token.stop_requested()) {
+      std::shared_lock<std::shared_mutex> lock(globalObjMutex);
+      globalObjRWLock->doWork();
+      ++read_count;
+    }
+    std::println("Reader thread read {} times", read_count);
+  };
+
+  auto writer_func = [&globalObjRWLock, &globalObjMutex](std::stop_token token) {
+    int write_count = 0;
+    while (!token.stop_requested()) {
+      auto newObj = new MyObject();
+      std::unique_lock<std::shared_mutex> lock(globalObjMutex);
+      auto oldObj     = globalObjRWLock;
+      globalObjRWLock = newObj;
+      lock.unlock();
+      delete oldObj;
+      ++write_count;
+      std::this_thread::sleep_for(std::chrono::microseconds(100));
+    }
+    std::println("Writer thread wrote {} times", write_count);
+  };
+
+  for (int i = 0; i < num_reader; ++i) {
+    readers.emplace_back(reader_func);
+  }
+  std::jthread writer(writer_func);
+
+  std::this_thread::sleep_for(test_time);
+
+  for (auto& reader : readers) {
+    reader.request_stop();
+  }
+  writer.request_stop();
+}
+
+void test_rcu() {
+  std::rcu_domain& dom = std::rcu_default_domain();
+  std::atomic<MyObject*> global_obj_rcu = new MyObject();
+
+  std::vector<std::jthread> readers;
+  readers.reserve(num_reader);
+
+  auto reader_func = [&dom, &global_obj_rcu](std::stop_token token) {
+    int read_count = 0;
+    while (!token.stop_requested()) {
+      dom.lock();
+      auto obj = global_obj_rcu.load(std::memory_order_relaxed);
+      obj->doWork();
+      dom.unlock();
+      ++read_count;
+    }
+    std::println("RCU Reader thread read {} times", read_count);
+  };
+
+  auto writer_func = [&global_obj_rcu](std::stop_token token) {
+    int write_count = 0;
+    while (!token.stop_requested()) {
+      auto newObj = new MyObject();
+      auto oldObj = global_obj_rcu.exchange(newObj, std::memory_order_relaxed);
+      oldObj->retire();
+      ++write_count;
+      std::this_thread::sleep_for(std::chrono::microseconds(100));
+    }
+    std::println("RCU Writer thread wrote {} times", write_count);
+  };
+
+  auto syncer_func = [&dom](std::stop_token token) {
+    while (!token.stop_requested()) {
+      std::rcu_synchronize(dom);
+      std::this_thread::sleep_for(std::chrono::milliseconds(50));
+    }
+  };
+
+  for (int i = 0; i < num_reader; ++i) {
+    readers.emplace_back(reader_func);
+  }
+  std::jthread writer(writer_func);
+  std::jthread syncer(syncer_func);
+
+  std::this_thread::sleep_for(test_time);
+
+  for (auto& reader : readers) {
+    reader.request_stop();
+  }
+  writer.request_stop();
+  syncer.request_stop();
+  std::rcu_synchronize(dom);
+}
+
+int main(int, char**) {
+  std::println("Testing read-write lock:");
+  test_read_write_lock();
+  std::println("Testing RCU:");
+  test_rcu();
+  return 1;
+}
diff --git a/libcxx/test/std/thread/saferecl/saferecl.rcu/retire.pass.cpp b/libcxx/test/std/thread/saferecl/saferecl.rcu/retire.pass.cpp
new file mode 100644
index 0000000000000..dde6cdd323084
--- /dev/null
+++ b/libcxx/test/std/thread/saferecl/saferecl.rcu/retire.pass.cpp
@@ -0,0 +1,150 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <rcu>
+
+#include <rcu>
+#include <thread>
+#include <chrono>
+#include <iostream>
+#include <print>
+#include <string>
+
+#include "__rcu/rcu_domain.h"
+#include "make_test_thread.h"
+#include "test_macros.h"
+
+void log(auto start, std::string_view msg) {
+  auto now = std::chrono::system_clock::now();
+  std::println(std::cout, "[{:%H:%M:%S}] {}", now - start, msg);
+}
+
+int loop_num = 10;
+
+struct MyObject : public std::rcu_obj_base<MyObject> {
+  std::string data_;
+
+  inline static int instance_count = 0;
+  MyObject() : data_(std::to_string(instance_count++) + " instance very very very long string") {}
+
+  ~MyObject() {
+    std::println(std::cout, "MyObject {} destructor called", data_);
+  }
+};
+
+std::atomic<MyObject*> global_obj = nullptr;
+
+int main(int, char**) {
+  auto start = std::chrono::system_clock::now();
+  auto t1    = support::make_test_thread([start]() {
+    std::rcu_domain& dom = std::rcu_default_domain();
+    for (int i = 0; i < loop_num; ++i) {
+      log(start, "t1: entering rcu read-side critical section " + std::to_string(i));
+      dom.lock();
+      auto obj = global_obj.load();
+      log(start, "t1: reading: " + (obj ? obj->data_ : "nullptr"));
+      std::this_thread::sleep_for(std::chrono::seconds(1));
+      log(start, "t1: leaving rcu read-side critical section " + std::to_string(i) + " with object " +
+                         (obj ? obj->data_ : "nullptr"));
+      dom.unlock();
+      log(start, "t1: printing all reader states");
+      dom.printAllReaderStatesInHex();
+    }
+  });
+
+  auto t2 = support::make_test_thread([start]() {
+    std::this_thread::sleep_for(std::chrono::milliseconds(200));
+    std::rcu_domain& dom = std::rcu_default_domain();
+    for (int i = 0; i < loop_num; ++i) {
+      log(start, "t2: entering rcu read-side critical section " + std::to_string(i));
+      dom.lock();
+      auto obj = global_obj.load();
+      log(start, "t2: reading: " + (obj ? obj->data_ : "nullptr"));
+      std::this_thread::sleep_for(std::chrono::seconds(1));
+      log(start, "t2: leaving rcu read-side critical section " + std::to_string(i) + " with object " +
+                         (obj ? obj->data_ : "nullptr"));
+      dom.unlock();
+      log(start, "t2: printing all reader states");
+      dom.printAllReaderStatesInHex();
+    }
+  });
+
+  auto t3 = support::make_test_thread([start]() {
+    std::this_thread::sleep_for(std::chrono::milliseconds(1100));
+    std::rcu_domain& dom = std::rcu_default_domain();
+    for (int i = 0; i < loop_num; ++i) {
+      log(start, "t3: entering rcu read-side critical section " + std::to_string(i));
+      dom.lock();
+      auto obj = global_obj.load();
+      log(start, "t3: reading: " + (obj ? obj->data_ : "nullptr"));
+      std::this_thread::sleep_for(std::chrono::seconds(1));
+      log(start, "t3: leaving rcu read-side critical section " + std::to_string(i) + " with object " +
+                         (obj ? obj->data_ : "nullptr"));
+      dom.unlock();
+      log(start, "t3: printing all reader states");
+      dom.printAllReaderStatesInHex();
+    }
+  });
+  auto t4 = support::make_test_thread([start]() {
+    std::this_thread::sleep_for(std::chrono::milliseconds(3000));
+    std::rcu_domain& dom = std::rcu_default_domain();
+    for (int i = 0; i < loop_num; ++i) {
+      log(start, "t4: entering rcu read-side critical section " + std::to_string(i));
+      dom.lock();
+      auto obj = global_obj.load();
+      log(start, "t4: reading: " + (obj ? obj->data_ : "nullptr"));
+      std::this_thread::sleep_for(std::chrono::seconds(1));
+      log(start, "t4: leaving rcu read-side critical section " + std::to_string(i) + " with object " +
+                         (obj ? obj->data_ : "nullptr"));
+      dom.unlock();
+      log(start, "t4: printing all reader states");
+      dom.printAllReaderStatesInHex();
+    }
+  });
+
+  auto t5 = support::make_test_thread([start]() {
+    std::this_thread::sleep_for(std::chrono::milliseconds(3000));
+    for (int i = 0; i < loop_num; ++i) {
+      auto new_obj = new MyObject();
+
+      log(start, "t5: updating global to : " + new_obj->data_);
+      auto old = global_obj.exchange(new_obj);
+      log(start, "t5: retiring old object " + (old ? old->data_ : "nullptr"));
+      old->retire();
+      std::this_thread::sleep_for(std::chrono::seconds(1));
+    }
+  });
+  /*
+  auto t3 = support::make_test_thread([start]() {
+    std::rcu_domain& dom = std::rcu_default_domain();
+    log(start, "t3: entering rcu read-side critical section");
+    dom.lock();
+    log(start, "t3: sleeping");
+    std::this_thread::sleep_for(std::chrono::seconds(3));
+    log(start, "t3: leaving rcu read-side critical section");
+    dom.unlock();
+  });  */
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(10));
+  for (int i = 0; i < loop_num + 5; ++i) {
+    log(start, "t0: calling rcu_synchronize" + std::to_string(i));
+    std::rcu_synchronize();
+    log(start, "t0: rcu_synchronize returned" + std::to_string(i));
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+  }
+
+  t1.join();
+  t2.join();
+  t3.join();
+  t4.join();
+
+  return 1;
+}

>From 0eb35eb2bf6c28a79a9c9771fb90ec91cd155685 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 25 Jan 2026 10:46:22 +0000
Subject: [PATCH 4/9] review

---
 libcxx/include/__rcu/rcu_domain.h | 12 +++---
 libcxx/include/__rcu/readme.md    | 69 -------------------------------
 2 files changed, 6 insertions(+), 75 deletions(-)
 delete mode 100644 libcxx/include/__rcu/readme.md

diff --git a/libcxx/include/__rcu/rcu_domain.h b/libcxx/include/__rcu/rcu_domain.h
index 8863849f09362..3ac3597280c25 100644
--- a/libcxx/include/__rcu/rcu_domain.h
+++ b/libcxx/include/__rcu/rcu_domain.h
@@ -33,17 +33,17 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
 
-template <class _ThreadLocal>
+template <class _Tp>
 class __thread_local_owner {
   // todo put globals in experimental dylib
-  inline static thread_local map<const __thread_local_owner*, _ThreadLocal> __thread_local_instances;
+  inline static thread_local map<const __thread_local_owner*, _Tp> __thread_local_instances;
 
   // Keep track of all thread-local instances owned by this owner.
   // Only emplaced the first time a thread is trying to access its thread-local instance.
-  vector<atomic_ref<_ThreadLocal>> __owned_instances_;
+  vector<atomic_ref<_Tp>> __owned_instances_;
   mutex __mtx_;
 
-  void __register(_ThreadLocal& __obj) {
+  void __register(_Tp& __obj) {
     lock_guard<std::mutex> __lg(__mtx_);
     __owned_instances_.emplace_back(__obj);
   }
@@ -54,10 +54,10 @@ class __thread_local_owner {
   __thread_local_owner()                       = default;
   __thread_local_owner(__thread_local_owner&&) = delete;
 
-  atomic_ref<_ThreadLocal> __get_current_thread_instance() {
+  atomic_ref<_Tp> __get_current_thread_instance() {
     auto __it = __thread_local_instances.find(this);
     if (__it == __thread_local_instances.end()) {
-      auto [new_it, _] = __thread_local_instances.try_emplace(this, _ThreadLocal());
+      auto [new_it, _] = __thread_local_instances.try_emplace(this, _Tp());
       auto& __obj      = new_it->second;
       __register(__obj);
       return atomic_ref(__obj);
diff --git a/libcxx/include/__rcu/readme.md b/libcxx/include/__rcu/readme.md
deleted file mode 100644
index ae83baf44dc61..0000000000000
--- a/libcxx/include/__rcu/readme.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# folly
-
-
-## domain
-
-- `lock` :
-  - increment the counter with the epoch number == `_version`
-
-- `unlock`:
-  - decrement the counter (without arguments)
-
-- `retire` :
-  - push `cb` to `_q`
-  - if long time since last sync, try try_lock sync mutex and `half_sync` without blocking (to get some finished nodes)
-    - put the finished `cb`s to `executor` (immideately execute)
-
-- `half_sync` :
-  - `current` is the current `_version` number, `next` is +1
-  - move all nodes from `_q` to `queues[0]` (cannot just have a single `queue` and swap because of later readers) (thread safe with concurrent more push to `_q`)
-  - if is blocking, wait for zero for the epoch `next & 1`
-  - else, if epoch `next & 1` has reader is true, return
-  - at this stage, `next & 1` epoch reader zero, if late reader comes, it increments the `current` reader count. (no concurrent half_sync as it is mutex protected)
-  - move all nodes from `queue[1]` to `finished`, and move all nodes from `queue[0]` to `queue[1]`
-  - store `next` to `_version`
-  - notify threads that turn_.waitForTurn(next)
-
-- `sync`
-  - `current` is the current `_version` number, `target` is +2
-  - while true
-     - if current `work_` is smaller than `target` and cas to `target` succeeded,
-        `half_sync` until `version_` >= `target`, and run all the finished `cb`s, `return`
-     - else 
-       - if `version_` >= `target` , `return`
-       - else `turn_.waitForTurn(work)` (so if other's target >= our target, other's second epoch half sync will unblock us)
-
-
-
-## executor
-
-- immediately invoke
-- queue if the `f` schedule another `f2` in the `executor`
-
-
-## example
-
-version == 0
-
-  T1                            T2                       T3
-
-Reader1 lock (0, 1)
-read obj0
-                       obj = obj1
-                       retire(obj0)
-                       sync, target = 2
-                       half, next = 1
-                       wait_zero(epoch == 1)
-                       cb0 -> queue[1]
-                       version = 1
-                       notify(1)
-                                                   Read2 lock (1, 1)
-                                                   read obj1
-                      half, next = 2
-unlock (0,0)
-                      wait_zero(epoch==0)
-                      cb0 -> finished
-                      run cb0
-                      version = 2
-                      notify(2)
-                                                  unlock

>From 1470383716db008f4f0497b927e8f3b398df37be Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 22 Feb 2026 20:33:51 +0000
Subject: [PATCH 5/9] design doc

---
 libcxx/docs/DesignDocs/RCU.rst | 312 +++++++++++++++++++++++++++++++++
 libcxx/docs/index.rst          |   1 +
 2 files changed, 313 insertions(+)
 create mode 100644 libcxx/docs/DesignDocs/RCU.rst

diff --git a/libcxx/docs/DesignDocs/RCU.rst b/libcxx/docs/DesignDocs/RCU.rst
new file mode 100644
index 0000000000000..c87ae8cde5677
--- /dev/null
+++ b/libcxx/docs/DesignDocs/RCU.rst
@@ -0,0 +1,312 @@
+====================
+``<rcu>`` Design
+====================
+
+.. contents::
+   :local:
+   :depth: 2
+
+
+Introduction
+============
+
+This is the C++ paper `Read-Copy Update (RCU) <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2545r4.pdf>`__.
+There are three designs discussed in the paper `Supplementary Material for User-Level Implementations of
+Read-Copy Update <http://www.rdrop.com/users/paulmck/RCU/urcu-supp-accepted.2011.08.30a.pdf>`__
+
+- Quiescent-State-Based Reclamation RCU
+- General-Purpose RCU
+- Low-Overhead RCU Via Signal Handling
+
+libc++ adopted a variation that closely resembles the "General-Purpose RCU" design in the above paper.
+
+
+Background Information
+======================
+
+`rcu` is an alternative to read-write locks and it is suitable for read-mostly data structures.
+Here is an example usage ::
+
+  struct Data /* members */ ;
+  std::atomic<Data*> data_;
+
+  // ==============================================
+  template <typename Func>
+  Result reader_op(Func fn) {
+    std::scoped_lock l(std::rcu_default_domain());
+    Data* p = data_;
+    // fn should not block too long or call
+    // rcu_synchronize(), rcu_barrier(), or
+    // rcu_retire(), directly or indirectly
+    return fn(p);
+ }
+
+  // ==============================================
+  // May be called concurrently with reader_op
+  void update(Data* newdata) {
+    Data* olddata = data_.exchange(newdata);
+    std::rcu_retire(olddata); // reclaim *olddata when safe
+  }
+
+There are several key details that an implementation of `rcu` must address:
+
+- On the reader side, `rcu_domain::lock` and `rcu_domain::unlock` must not block the thread while the writer thread
+  is performing updates.
+
+- On the writer side, `retire` -ing an object in principle should not block at all. However, it is conforming to
+  evaluate the `deleter` s inside `run_retire` .
+
+- On the writer side, `rcu_synchronize` should block until at least all the existing readers exit their critical sections
+  via `rcu_domain::unlock` . Note that this is a key difference between `rcu` and read-write locks:
+  In case a late reader enters the critical section after the writer thread has called `rcu_synchronize` and waiting, the
+  writer thread does not need to wait for the late reader to exit the critical section.
+
+- On the writer side, `run_barrier` should block until all the `retired` objects that happen before the `run_barrier` call are reclaimed.
+
+- The threads that are using `rcu` must be known by the `rcu` implementation states.
+
+
+Adopted Design
+==============
+
+The core idea of `rcu` can be described in this simple image from lwn.net
+
+.. image:: https://static.lwn.net/images/ns/kernel/rcu/GracePeriodGood.png
+
+- Each row is a thread. The last row is the writer thread and the rows above are the reader threads.
+- Each "Reader" block represents a critical section, which starts with `rcu_domain::lock` and ends with `rcu_domain::unlock` .
+- When `run_retire` is called from the writer thread, it starts the "Removal" block.
+- When `run_synchronize` is called from the writer thread, it starts the "Grace Period" block. We need to wait until all the 
+  "Reader" blocks that started before the "Grace Period" started, to exit via `rcu_domain::unlock`, then we can end the "Grace Period".
+  Note that the "Grace Period" ends after the 4th row's "Reader" block ends. Also note that the "Grace Period" does not need to wait
+  for the late "Reader" blocks.
+- After the "Grace Period" ends, we can start the "Reclaim" block to reclaim the retired objects.
+
+Libc++ adopted a design that closely resembles the "General-Purpose RCU" design in the above paper.
+
+Some key details of this design are:
+
+- There is a global state which has two phases and it flips between the two phases.
+- Each thread stores its state: whether there is a reader in the critical section, and which phase it was when it entered the critical section.
+- When `run_synchronize` is called, it
+
+  - flips the global state to the next phase
+  - waits until all the threads that are in the critical section and entered in the previous phase to exit the critical section.
+  - flips the global state back to the original phase
+  - waits until all the threads that are in the critical section and entered in the next phase to exit the critical section.
+
+When `run_synchronize` returns, we can be sure that all the readers that were in the critical section before `run_synchronize` are now out of the critical section.
+The paper explains why we need to wait two phases instead of just one phase in detail. The key point is that, if we only wait for the readers in the previous phase to exit,
+there might be a late reader that enters the critical section after we flip the global state and before we wait for the previous phase's readers to exit.
+
+Implementation Terminology
+---------------------------
+
+- reader
+- grace period
+- quiescent state
+
+Implementation Details
+----------------------
+
+Design Questions
+================
+
+What thread(s) should the deleters run on
+-----------------------------------------
+
+Paul E. McKenney suggested to run those deleters on a background thread:
+
+  Folly currently uses an "inline executor", though some would like a
+  separate thread in some cases.  The userspace RCU library always uses
+  background threads.  By default only one, but there are ways to configure
+  more. 
+
+We have doubts on this. As a standard library, we usually don't spin threads for users. And Paul E. McKenney 
+has pointed out that:
+
+  Each library will have its own rules.
+  One advantage of an inline executor is that the deleters apply
+  backpressure against threads that flood the system with either .retire()
+  or rcu_retire().  Some compensation for the additional deadlocks,
+  I guess.  But you can get the same effect by properly restricting where
+  the background threads run.
+
+And Thomas Rodgers (libstdc++'s RCU implementer) also said:
+
+  We don't normally do this, no, and I don't think I'd ever directly pursue such an approach in isolation. If, 
+  on the other hand I could derive a permission from the user of library to create threads, by explicit use 
+  of a system thread pool executor, perhaps then it would be ok to also use that executor to run other background
+  threads for things like this. Having said that, I think that is something that SG1 should probably discuss. On the
+  one hand spinning up a 'system' thread pool is going be potentially expensive and if the library user has done so,
+  you probably would not release those resources until termination. So, in that sense, you've 'paid' for a background
+  thread. But on the other hand, users of the library might find it surprising that then gave the library permission
+  to also do work on those threads in ways they might not expect.
+
+In libc++'s design, we would like to follow Folly's approach to run these deleters inline when `run_synchronize` or
+`run_barrier` is called.
+
+When should we run the deleters
+-------------------------------
+
+If we were to use the background thread approach, the deleters can be evaluated at any time after `run_synchronize`
+has marked the objects are safe to reclaim. e.g. Drain the queue periodically. However, if we were to run the deleters
+inline, we have to decide when to run them.
+
+There are few places we can run the deleters:
+
+- Inside `run_synchronize` after the writer thread is unblocked. This approach has the advantage that the writer thread
+  can reclaim the retired objects as soon as possible. However, it has the disadvantage that the writer thread may be
+  blocked for a long time if there are many retired objects to reclaim.
+
+- Inside `run_barrier` after the writer thread is unblocked. Since `run_barrier` is designed to block until all the retired
+  objects that happen before the `run_barrier` call are reclaimed, it is natural to run the deleters here.
+
+- Inside `run_retire` after the deleter is put into the queue. `run_retire` is designed not to block. However, if there are
+  objects that are safe to reclaim due to readers have exited their critical sections, and at the same time, there is no other
+  writer threads that are currently draining the deleter queue, we can take the opportunity to run the deleters.
+
+Folly currently takes the inline approach and runs the deleters at all three places mentioned above.
+
+In libc++'s design, we would like to start with the inline approach and only run the deleters inside `run_synchronize` and
+`run_barrier` for simplicity. We can consider running the deleters inside `run_retire` in the future if we find it is necessary
+to reduce the latency of reclaiming the retired objects.
+
+Almost all APIs are `noexcept` . Is it designed to avoid memory allocation and avoid using `mutex` ?
+-----------------------------------------------------------------------------------------------------
+
+We should avoid using `mutex` as much as we can. However, both Folly and Paul's implementation use `mutex` in some places.
+
+Paul E. McKenney mentioned that:
+
+  On the non-noexcept nature of mutexes, the best I could do was to say
+  that my reference implementation acquired its pthread_mutexes from C
+
+There are few places that might need to use `mutex`:
+
+- The first time a thread registers its existence with the `rcu` implementation. Depending on the implementation, this
+  can happen inside the `rcu_*` calls.
+
+- The add/remove of the deleter queue, if the queue is not a lock-free data structure.
+
+- The destruction of a thread, which might need to do some clean up work for the `rcu` states.
+
+Memory Allocation can throw as well. Luckily `run_retire` is not marked as `noexcept` in the paper, as `run_retire` is
+likely to allocate memory to create a type erased object into the deleter queue.
+
+However, there are other places where it might need to allocate memory. For example, see Folly's comments
+`here <https://github.com/facebook/folly/blob/c83b50725f554c4912a73b676746242a22211eca/folly/synchronization/Rcu.h#L343>`__ ::
+
+   * Note that despite the function being marked noexcept, an allocation
+   * may take place in folly::ThreadLocal the first time a thread enters a read
+   * region. Regardless, for now, we're marking this as noexcept to match the
+   * N4895 standard proposal:
+   *
+   * https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/n4895.pdf
+   */
+  FOLLY_ALWAYS_INLINE void lock() noexcept {
+    counters_.increment(version_.load(std::memory_order_acquire));
+  }
+  FOLLY_ALWAYS_INLINE void unlock() noexcept { counters_.decrement(); }
+
+In libc++'s design, we will start with as less `mutex` usage as possible. In places where it is unavoidable, we could either
+wrap the internal API with a while loop, or use the `atomic_unique_lock` that we introduced with `stop_token` implementations.
+For memory allocations, we will only need them the first time we create some rcu_domain internal states or the first time registers a thread.
+We will consider whether we accepts a `terminate` call if this fails to allocate.
+
+
+How does `rcu` know about a thread?
+------------------------------------
+
+All design options require the `rcu_domain` to know whether a thread is in a critical section and stores each thread's state.
+The question is how does the `rcu_domain` know about a thread?
+
+Paul E. McKenney suggested that:
+
+  In a library, I also suggest automating each thread's initial call to register_thread().
+
+There are few options to automate the thread registration:
+
+- In the `std::thread` and `std::jthread` constructors, we can register them to the `rcu_domain` 's state. However, this approach
+  has the disadvantage that it does not cover threads that are not created by `std::thread` or `std::jthread` , for example, users
+  can create a thread through `pthread_create` and use that thread to call the `rcu` APIs. Another disadvantage is that most users
+  who create threads might not use `rcu` at all, so it might be wasteful to register all the threads by default.
+
+- In the `rcu_domain::lock` , we can check whether the current thread is registered, if not, we can register it. This approach has
+  the advantage that it can cover all threads, and it does not have the overhead of registering threads that do not use `rcu` at all.
+  However, it has the disadvantage that it might have a performance impact on the first call to `rcu_domain::lock` for each thread.
+  As the performance of `rcu_domain::lock` is critical, we need to make sure the thread registration process is as efficient as possible.
+
+Folly uses the second approach to automate the thread registration. Another caveat of this approach in Folly is that, the first call to
+`rcu_domain::lock` from a thread requires allocating memory to create the thread's state in the `rcu_domain` and it also needs to lock a
+`mutex` to protect the thread registration process.
+
+In libc++'s design, we will also use the second approach to automate the thread registration.
+
+Is `rcu_domain` singleton?
+--------------------------
+
+The APIs take a reference to `rcu_domain` with default value::
+
+    void rcu_synchronize(rcu_domain& dom = rcu_default_domain()) noexcept;
+
+    void rcu_barrier(rcu_domain& dom = rcu_default_domain()) noexcept;
+
+    template<class T, class D = default_delete<T>>
+    void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain());
+
+However, the class `rcu_domain` has no user visible constructors::
+
+    class rcu_domain {
+      public:
+        rcu_domain(const rcu_domain&) = delete;
+        rcu_domain& operator=(const rcu_domain&) = delete;
+    };
+
+Paul E. McKenney confirmed that:
+
+  Yes, right now, just the one instance.  Right now, its only purpose is to support C++ RAII RCU readers.
+  Later, we might add the ability to create separate RCU instances, similar to SRCU in the Linux kernel.
+
+Thomas Rodgers (libstdc++'s RCU implementer) also said:
+
+  Currently, liburcu doesn't actually support discrete domains, so internal to the shared library there is
+  only a single static instance of a type that wraps the relevant operations from the liburcu sources.
+  The `uintptr_t`` member is initialized with the address of this instance, and its size. Future implementations
+  could do something different behind this ABI contract, including a non-liburcu based approach. 
+
+In libc++'s design, we will follow the same singleton pattern to simplify the design and implementation. Otherwise,
+lots of thread local storages will require a per-instance state.
+
+What about the ABI?
+-------------------
+
+The `rcu` library most functions are non-template. So it is possible to put the entire implementation details inside the dylib.
+
+Existing Implementations
+========================
+
+The C++ paper `Read-Copy Update (RCU) <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2545r4.pdf>`__
+discussed two reference implementations:
+
+- Folly
+- liburcu
+
+Folly
+-----
+
+liburcu
+-------
+
+There are 4 flavors of `rcu` implementations in liburcu (see doc `here <https://github.com/urcu/userspace-rcu/blob/master/README.md#usage-of-all-urcu-libraries>`__):
+
+- `memb`
+- `qsbr`
+- `mb`
+- `bp`
+
+
+libstdc++
+---------
+
+
diff --git a/libcxx/docs/index.rst b/libcxx/docs/index.rst
index 13aa481b1f9b7..b1c2fef78369e 100644
--- a/libcxx/docs/index.rst
+++ b/libcxx/docs/index.rst
@@ -227,6 +227,7 @@ Design Documents
    DesignDocs/HeaderRemovalPolicy
    DesignDocs/NoexceptPolicy
    DesignDocs/PSTLIntegration
+   DesignDocs/RCU
    DesignDocs/ThreadingSupportAPI
    DesignDocs/UniquePtrTrivialAbi
    DesignDocs/UnspecifiedBehaviorRandomization

>From 8592968685bc2c21fd6bc3df56d9b8cb750c69fc Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 28 Feb 2026 15:57:23 +0000
Subject: [PATCH 6/9] refactor to hide implementation into dylib

---
 libcxx/CMakeLists.txt                         |   6 +
 libcxx/docs/DesignDocs/RCU.rst                |  11 +-
 libcxx/include/CMakeLists.txt                 |   1 -
 libcxx/include/__rcu/rcu_domain.h             | 251 ++---------------
 libcxx/include/__rcu/rcu_obj_base.h           |  26 +-
 libcxx/include/module.modulemap.in            |   1 -
 libcxx/src/CMakeLists.txt                     |   7 +
 .../experimental/include/rcu}/rcu_list.h      |   9 +-
 libcxx/src/experimental/rcu.cpp               | 257 ++++++++++++++++++
 9 files changed, 317 insertions(+), 252 deletions(-)
 rename libcxx/{include/__rcu => src/experimental/include/rcu}/rcu_list.h (90%)
 create mode 100644 libcxx/src/experimental/rcu.cpp

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 31aaf2977a9af..992e46ca3ba0e 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -31,6 +31,12 @@ list(INSERT CMAKE_MODULE_PATH 0
 
 set(CMAKE_FOLDER "libc++")
 
+# Unset the C++ Standard: other projects sometimes override this global property, which
+# is incompatible with libc++ (since we set our own standard via other means).
+# TODO: Figure out which other part of the build is setting this and resolve the issue
+#       at the root.
+unset(CMAKE_CXX_STANDARD)
+
 set(LIBCXX_SOURCE_DIR  ${CMAKE_CURRENT_SOURCE_DIR})
 set(LIBCXX_BINARY_DIR  ${CMAKE_CURRENT_BINARY_DIR})
 
diff --git a/libcxx/docs/DesignDocs/RCU.rst b/libcxx/docs/DesignDocs/RCU.rst
index c87ae8cde5677..ee1f2fe6fb993 100644
--- a/libcxx/docs/DesignDocs/RCU.rst
+++ b/libcxx/docs/DesignDocs/RCU.rst
@@ -169,8 +169,10 @@ There are few places we can run the deleters:
 Folly currently takes the inline approach and runs the deleters at all three places mentioned above.
 
 In libc++'s design, we would like to start with the inline approach and only run the deleters inside `run_synchronize` and
-`run_barrier` for simplicity. We can consider running the deleters inside `run_retire` in the future if we find it is necessary
-to reduce the latency of reclaiming the retired objects.
+`run_barrier` for simplicity. Whether or not running the deleters inside `run_retire` is debatable. On the one hand, running
+them will make `run_retire`  take more time to return. But not running them will make the retired objects stay in the queue
+for a longer time. In an extreme case, if the user never calls `run_synchronize` or `run_barrier` after calling `run_retire`,
+the retired objects will never be reclaimed (without a background thread to drain the queue).
 
 Almost all APIs are `noexcept` . Is it designed to avoid memory allocation and avoid using `mutex` ?
 -----------------------------------------------------------------------------------------------------
@@ -309,4 +311,9 @@ There are 4 flavors of `rcu` implementations in liburcu (see doc `here <https://
 libstdc++
 ---------
 
+According to Thomas Rodgers (libstdc++'s RCU implementer), their plan is to embed the subset of `liburcu` that implements the `memb`` and `mb` RCU flavors, with `memb` being the preferred choice, 
+based on the presence of `SYS_membarrier` . Because `liburcu` is license compatible with libstdc++ they can directly embed the relevant source from `liburcu`
 
+
+libc++ Design
+=============
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 8b7e25cda5eae..79f43b32ab193 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -771,7 +771,6 @@ set(files
   __ranges/zip_transform_view.h
   __ranges/zip_view.h
   __rcu/rcu_domain.h
-  __rcu/rcu_list.h
   __rcu/rcu_obj_base.h
   __split_buffer
   __std_mbstate_t.h
diff --git a/libcxx/include/__rcu/rcu_domain.h b/libcxx/include/__rcu/rcu_domain.h
index 3ac3597280c25..71aed729afa93 100644
--- a/libcxx/include/__rcu/rcu_domain.h
+++ b/libcxx/include/__rcu/rcu_domain.h
@@ -11,19 +11,8 @@
 #define _LIBCPP___RCU_RCU_DOMAIN_H
 
 #include <__config>
-
-#include <__atomic/atomic.h>
-#include <__rcu/rcu_list.h>
-
-// todo replace with internal headers
-#include <atomic>
-#include <cstdint>
-#include <map>
-#include <mutex>
-#include <vector>
-
-// todo debug
-#include <cstdio>
+#include <__functional/function.h>
+#include <__memory/unique_ptr.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -33,246 +22,52 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
 
-template <class _Tp>
-class __thread_local_owner {
-  // todo put globals in experimental dylib
-  inline static thread_local map<const __thread_local_owner*, _Tp> __thread_local_instances;
-
-  // Keep track of all thread-local instances owned by this owner.
-  // Only emplaced the first time a thread is trying to access its thread-local instance.
-  vector<atomic_ref<_Tp>> __owned_instances_;
-  mutex __mtx_;
-
-  void __register(_Tp& __obj) {
-    lock_guard<std::mutex> __lg(__mtx_);
-    __owned_instances_.emplace_back(__obj);
-  }
-
-  // todo: deregister on thread exit?
+struct __rcu_node {
+  function<void()> __callback_{};
+  __rcu_node* __next_ = nullptr;
 
-public:
-  __thread_local_owner()                       = default;
-  __thread_local_owner(__thread_local_owner&&) = delete;
-
-  atomic_ref<_Tp> __get_current_thread_instance() {
-    auto __it = __thread_local_instances.find(this);
-    if (__it == __thread_local_instances.end()) {
-      auto [new_it, _] = __thread_local_instances.try_emplace(this, _Tp());
-      auto& __obj      = new_it->second;
-      __register(__obj);
-      return atomic_ref(__obj);
-    }
-    return atomic_ref(__it->second);
-  }
-
-  template <class _Func>
-  void __for_each_owned_instances(_Func&& __f) {
-    unique_lock<std::mutex> __lock(__mtx_);
-    for (auto __instance : __owned_instances_) {
-      __f(__instance);
-    }
-  }
+  _LIBCPP_HIDE_FROM_ABI __rcu_node() {}
+  _LIBCPP_HIDE_FROM_ABI ~__rcu_node() {}
 };
 
-// Adopted the 2-phase implementation in the section
-// "3) General-Purpose RCU" of the paper
-// http://www.rdrop.com/users/paulmck/RCU/urcu-supp-accepted.2011.08.30a.pdf
-
-struct __reader_states {
-  // bit 15 is the grace period phase 0 or 1
-  // bits 0-14 is the reader nest level
-  //
-  // a thread can have nested reader locks, such as
-  // domain.lock();   // nest level = 1
-  // domain.lock();   // nest level = 2
-  // ...
-  // domain.unlock(); // nest level = 1
-  // domain.unlock(); // nest level = 0
-
-  static constexpr uint16_t __grace_period_phase_mask = 0b1000'0000'0000'0000;
-  static constexpr uint16_t __reader_nest_level_mask  = 0b0111'1111'1111'1111;
-
-  using __state_type = uint16_t;
-
-  __thread_local_owner<__state_type> __per_thread_states_;
-
-  static uint16_t __get_grace_period_phase(__state_type __state) { return __state & __grace_period_phase_mask; }
-
-  static uint16_t __get_reader_nest_level(__state_type __state) { return __state & __reader_nest_level_mask; }
-
-  static bool __is_quiescent_state(__state_type __state) { return __get_reader_nest_level(__state) == 0; }
-};
-
-class rcu_domain {
-  // todo optimize the layout
-
-  __reader_states __reader_states_;
-
-  // only the highest bit is used for the phase.
-  std::atomic<__reader_states::__state_type> __global_reader_phase_{};
-
-  // only one thread is allowed to update concurrently
-  std::mutex __grace_period_mutex_; // todo this is not noexcept
-
-  std::atomic<bool> __grace_period_waiting_flag_ = false;
+class _LIBCPP_EXPORTED_FROM_ABI rcu_domain {
+  class __impl;
+  unique_ptr<__impl> __pimpl_;
 
-  // todo: maybe use a lock-free queue
-  std::mutex __retire_queue_mutex_; // todo this is not noexcept
-  __rcu_singly_list_view __retired_callback_queue_;
-
-  // these two queues do not need extra synchronization
-  // as they are always processed under the grace period mutex
-  __rcu_singly_list_view __callbacks_phase_1_;
-  __rcu_singly_list_view __callbacks_phase_2_;
-
-  rcu_domain() = default;
-
-  friend struct __rcu_domain_access;
   template <class, class>
   friend class rcu_obj_base;
 
-  // todo put globals in dylib
-  static rcu_domain& __rcu_default_domain() noexcept {
-    static rcu_domain __default_domain;
-    return __default_domain;
-  }
-
-  template <class Callback>
-  void __retire_callback(Callback&& __cb) noexcept {
-    auto* __node        = new __rcu_node();
-    __node->__callback_ = std::forward<Callback>(__cb);
-    std::unique_lock __lk(__retire_queue_mutex_);
-    __retired_callback_queue_.__push_back(__node);
-  }
-
-  void __synchronize() noexcept {
-    __cxx_atomic_thread_fence(memory_order_seq_cst);
-    std::unique_lock __lk(__grace_period_mutex_);
-    //std::printf("rcu_domain::__synchronize() going through phase 1\n");
-    auto __ready_callbacks = __update_phase_and_wait();
+  friend rcu_domain& rcu_default_domain() noexcept;
+  friend void rcu_synchronize(rcu_domain&) noexcept;
 
-    // Invoke the ready callbacks outside of the grace period mutex
-    __lk.unlock();
-    __ready_callbacks.__for_each([](auto* __node) { __node->__callback_(); });
-    __lk.lock();
+  static rcu_domain& __rcu_default_domain() noexcept;
 
-    __barrier();
-    //std::printf("rcu_domain::__synchronize() going through phase 2\n");
-    __ready_callbacks = __update_phase_and_wait();
+  rcu_domain();
 
-    // Invoke the ready callbacks outside of the grace period mutex
-    __lk.unlock();
-    __ready_callbacks.__for_each([](auto* __node) { __node->__callback_(); });
-    __cxx_atomic_thread_fence(memory_order_seq_cst);
-  }
-
-  __rcu_singly_list_view __update_phase_and_wait() noexcept {
-    std::unique_lock __retire_lk(__retire_queue_mutex_);
-    __callbacks_phase_1_.__splice_back(__retired_callback_queue_);
-    __retire_lk.unlock();
-
-    // Flip the global phase
-    auto __old_phase =
-        __global_reader_phase_.fetch_xor(__reader_states::__grace_period_phase_mask, std::memory_order_relaxed);
-    auto __new_phase = __old_phase ^ __reader_states::__grace_period_phase_mask;
-    //std::printf("rcu_domain::__update_phase_and_wait() new phase: 0x%04x\n", __new_phase);
-
-    __barrier();
-    // Wait for all threads to quiesce in the old phase
-    while (__any_reader_in_ongoing_grace_period(__new_phase)) {
-      __grace_period_waiting_flag_.store(true, std::memory_order_relaxed);
-      __grace_period_waiting_flag_.wait(true, std::memory_order_relaxed);
-    }
-    __grace_period_waiting_flag_.store(false, std::memory_order_relaxed);
-
-    __rcu_singly_list_view __ready_callbacks;
-    __ready_callbacks.__splice_back(__callbacks_phase_2_);
-    __callbacks_phase_2_.__splice_back(__callbacks_phase_1_);
-    return __ready_callbacks;
-  }
-
-  bool __any_reader_in_ongoing_grace_period(__reader_states::__state_type __global_phase) noexcept {
-    bool __any_ongoing = false;
-    __reader_states_.__per_thread_states_.__for_each_owned_instances(
-        [this, __global_phase, &__any_ongoing](atomic_ref<__reader_states::__state_type> __state) {
-          if (__is_grace_period_ongoing(__state.load(memory_order_relaxed), __global_phase)) {
-            __any_ongoing = true;
-          }
-        });
-    return __any_ongoing;
-  }
-
-  bool __is_grace_period_ongoing(__reader_states::__state_type __thread_state,
-                                 __reader_states::__state_type __global_phase) const noexcept {
-    // https://lwn.net/Articles/323929/
-    // The phase is flipped at the beginning of a grace period.
-    // Any readers that started before the flip will have the old phase
-    // and we consider them as ongoing that we need to wait for before we can close the grace period.
-    return !__reader_states::__is_quiescent_state(__thread_state) &&
-           __reader_states::__get_grace_period_phase(__thread_state) != __global_phase;
-  }
-
-  void __barrier() noexcept { asm volatile("" : : : "memory"); }
+  void __retire(__rcu_node*) noexcept;
 
 public:
   rcu_domain(const rcu_domain&)            = delete;
   rcu_domain& operator=(const rcu_domain&) = delete;
+  ~rcu_domain();
 
-  void printAllReaderStatesInHex() {
-    __reader_states_.__per_thread_states_.__for_each_owned_instances([](auto __state_ref) {
-      std::printf("Reader state: 0x%04x\n", __state_ref.load());
-    });
-  }
-
-  void lock() {
-    auto __current_thread_state_ref = __reader_states_.__per_thread_states_.__get_current_thread_instance();
+  void printAllReaderStatesInHex();
 
-    if ((__reader_states::__is_quiescent_state(__current_thread_state_ref.load(memory_order_relaxed)))) {
-      // Entering a read-side critical section from a quiescent state.
-      __current_thread_state_ref.store(
-          __reader_states::__state_type(__global_reader_phase_.load(std::memory_order_relaxed) | uint16_t(1)),
-          memory_order_relaxed);
-      __cxx_atomic_thread_fence(memory_order_seq_cst);
-    } else {
-      // Already in read-side critical section, just increment the nest level.
-      __current_thread_state_ref.fetch_add(1, memory_order_relaxed);
-    }
-  }
+  void lock() noexcept;
 
-  bool try_lock() {
+  bool try_lock() noexcept {
     lock();
     return true;
   }
 
-  void unlock() {
-    auto __current_thread_state_ref = __reader_states_.__per_thread_states_.__get_current_thread_instance();
-    __cxx_atomic_thread_fence(memory_order_seq_cst);
-    // Decrement the nest level.
-    auto __old_state = __current_thread_state_ref.fetch_sub(1, memory_order_relaxed);
-
-    if (__reader_states::__get_reader_nest_level(__old_state) == 1 &&
-        __grace_period_waiting_flag_.load(memory_order_relaxed)) {
-      // Transitioning to quiescent state, wake up waiters.
-      __grace_period_waiting_flag_.store(false, std::memory_order_relaxed);
-      __grace_period_waiting_flag_.notify_all();
-    }
-  }
-};
-
-struct __rcu_domain_access {
-  static rcu_domain& __rcu_default_domain() noexcept { return rcu_domain::__rcu_default_domain(); }
-  static void __rcu_synchronize(rcu_domain& __dom) noexcept { __dom.__synchronize(); }
+  void unlock() noexcept;
 };
 
-// todo put it in the experimental dylib
-inline rcu_domain& rcu_default_domain() noexcept { return __rcu_domain_access::__rcu_default_domain(); }
+_LIBCPP_EXPORTED_FROM_ABI rcu_domain& rcu_default_domain() noexcept;
 
-// todo put it in the experimental dylib
-inline void rcu_synchronize(rcu_domain& __dom = rcu_default_domain()) noexcept {
-  __rcu_domain_access::__rcu_synchronize(__dom);
-}
+_LIBCPP_EXPORTED_FROM_ABI void rcu_synchronize(rcu_domain& __dom = rcu_default_domain()) noexcept;
 
-void rcu_barrier(rcu_domain& dom = rcu_default_domain()) noexcept;
+_LIBCPP_EXPORTED_FROM_ABI void rcu_barrier(rcu_domain& __dom = rcu_default_domain()) noexcept;
 
 template <class T, class D = default_delete<T>>
 void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain());
diff --git a/libcxx/include/__rcu/rcu_obj_base.h b/libcxx/include/__rcu/rcu_obj_base.h
index 3d1de353613a5..4dbfe1fd07720 100644
--- a/libcxx/include/__rcu/rcu_obj_base.h
+++ b/libcxx/include/__rcu/rcu_obj_base.h
@@ -11,9 +11,10 @@
 #define _LIBCPP___RCU_RCU_OBJ_BASE_H
 
 #include <__config>
+#include <__functional/function.h>
 #include <__memory/unique_ptr.h> // for default_delete
 #include <__rcu/rcu_domain.h>
-#include <__rcu/rcu_list.h>
+#include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -23,23 +24,22 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
 
-template <class T, class D = default_delete<T>>
+template <class _Tp, class _Dp = default_delete<_Tp>>
 class rcu_obj_base : private __rcu_node {
 public:
-  void retire(D d = D(), rcu_domain& dom = rcu_default_domain()) noexcept {
-    auto ptr = static_cast<T*>(this);
-    dom.__retire_callback(
-        [ptr, d = std::move(d)]() mutable { d(ptr); });
+  _LIBCPP_HIDE_FROM_ABI void retire(_Dp __deleter = _Dp(), rcu_domain& __dom = rcu_default_domain()) noexcept {
+    auto __ptr  = static_cast<_Tp*>(this);
+    __callback_ = [__ptr, __deleter = std::move(__deleter)]() mutable { __deleter(__ptr); };
+    __dom.__retire(this);
   }
 
 protected:
-  rcu_obj_base()                               = default;
-  rcu_obj_base(const rcu_obj_base&)            = default;
-  rcu_obj_base(rcu_obj_base&&)                 = default;
-  rcu_obj_base& operator=(const rcu_obj_base&) = default;
-  rcu_obj_base& operator=(rcu_obj_base&&)      = default;
-  ~rcu_obj_base()                              = default;
-
+  _LIBCPP_HIDE_FROM_ABI rcu_obj_base()                               = default;
+  _LIBCPP_HIDE_FROM_ABI rcu_obj_base(const rcu_obj_base&)            = default;
+  _LIBCPP_HIDE_FROM_ABI rcu_obj_base(rcu_obj_base&&)                 = default;
+  _LIBCPP_HIDE_FROM_ABI rcu_obj_base& operator=(const rcu_obj_base&) = default;
+  _LIBCPP_HIDE_FROM_ABI rcu_obj_base& operator=(rcu_obj_base&&)      = default;
+  _LIBCPP_HIDE_FROM_ABI ~rcu_obj_base()                              = default;
 };
 
 #endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 21cee56fc5eb9..3748f6ad356de 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2008,7 +2008,6 @@ module std {
 
   module rcu {
     module rcu_domain      { header "__rcu/rcu_domain.h" }
-    module rcu_list        { header "__rcu/rcu_list.h" }
     module rcu_obj_base    { header "__rcu/rcu_obj_base.h" }
     header "rcu"
     export *
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index e029f9f29d5ea..51b9c32cc4eef 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -325,6 +325,13 @@ if (LIBCXX_ENABLE_LOCALIZATION AND LIBCXX_ENABLE_FILESYSTEM AND LIBCXX_ENABLE_TI
     )
 endif()
 
+if (LIBCXX_ENABLE_THREADS)
+  list(APPEND LIBCXX_EXPERIMENTAL_SOURCES
+    experimental/include/rcu/rcu_list.h
+    experimental/rcu.cpp
+    )
+endif()
+
 add_library(cxx_experimental STATIC ${LIBCXX_EXPERIMENTAL_SOURCES})
 target_link_libraries(cxx_experimental PUBLIC cxx-headers)
 if (LIBCXX_ENABLE_SHARED AND TARGET cxx_shared)
diff --git a/libcxx/include/__rcu/rcu_list.h b/libcxx/src/experimental/include/rcu/rcu_list.h
similarity index 90%
rename from libcxx/include/__rcu/rcu_list.h
rename to libcxx/src/experimental/include/rcu/rcu_list.h
index 2547d7223a7cc..199c3c67e4275 100644
--- a/libcxx/include/__rcu/rcu_list.h
+++ b/libcxx/src/experimental/include/rcu/rcu_list.h
@@ -22,18 +22,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
 
-struct __rcu_node {
-  function<void()> __callback_{};
-  __rcu_node* __next_ = nullptr;
-};
-
-class __rcu_singly_list_view {
+class rcu_singly_list_view {
 private:
   __rcu_node* __head_ = nullptr;
   __rcu_node* __tail_ = nullptr;
 
 public:
-  void __splice_back(__rcu_singly_list_view& __other) noexcept {
+  void __splice_back(rcu_singly_list_view& __other) noexcept {
     if (__other.__head_ == nullptr) {
       return;
     }
diff --git a/libcxx/src/experimental/rcu.cpp b/libcxx/src/experimental/rcu.cpp
new file mode 100644
index 0000000000000..f3b3a0990a908
--- /dev/null
+++ b/libcxx/src/experimental/rcu.cpp
@@ -0,0 +1,257 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <rcu>
+#include <vector>
+
+#include "__rcu/rcu_domain.h"
+#include "include/rcu/rcu_list.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace {
+
+template <class Tp>
+class thread_local_container {
+  struct thread_entry {
+    Tp instance_;
+
+    thread_entry() : instance_() { register_instance(instance_); }
+    thread_entry(const thread_entry&) = delete;
+    thread_entry(thread_entry&&)      = delete;
+
+    ~thread_entry() { deregister_instance(instance_); }
+  };
+
+  inline static thread_local optional<thread_entry> thread_entry_{};
+
+  // Keep track of all thread-local instances
+  // Only emplaced the first time a thread is trying to access its thread-local instance.
+  inline static vector<Tp*> instances_;
+  inline static mutex mtx_;
+
+  static void register_instance(Tp& obj) {
+    lock_guard<std::mutex> lg(mtx_);
+    instances_.emplace_back(&obj);
+  }
+
+  static void deregister_instance(Tp& obj) {
+    lock_guard<std::mutex> lg(mtx_);
+    instances_.erase(remove_if(instances_.begin(), instances_.end(), [&obj](Tp* instance) { return instance == &obj; }),
+                     instances_.end());
+  }
+
+public:
+  thread_local_container()                         = delete;
+  thread_local_container(thread_local_container&&) = delete;
+
+  static atomic_ref<Tp> get_current_thread_instance() {
+    if (!thread_entry_.has_value()) {
+      auto& entry = thread_entry_.emplace();
+      return atomic_ref(entry.instance_);
+    }
+    return atomic_ref(thread_entry_->instance_);
+  }
+
+  template <class Func>
+  static void for_each(Func&& f) {
+    unique_lock<std::mutex> lock(mtx_);
+    for (auto instance : instances_) {
+      f(atomic_ref(*instance));
+    }
+  }
+};
+
+// Adopted the 2-phase implementation in the section
+// "3) General-Purpose RCU" of the paper
+// http://www.rdrop.com/users/paulmck/RCU/urcu-supp-accepted.2011.08.30a.pdf
+
+struct reader_states {
+  // bit 15 is the grace period phase 0 or 1
+  // bits 0-14 is the reader nest level
+  //
+  // a thread can have nested reader locks, such as
+  // domain.lock();   // nest level = 1
+  // domain.lock();   // nest level = 2
+  // ...
+  // domain.unlock(); // nest level = 1
+  // domain.unlock(); // nest level = 0
+
+  static constexpr uint16_t grace_period_phase_mask = 0b1000'0000'0000'0000;
+  static constexpr uint16_t reader_nest_level_mask  = 0b0111'1111'1111'1111;
+
+  using state_type = uint16_t;
+
+  static uint16_t get_grace_period_phase(state_type state) { return state & grace_period_phase_mask; }
+
+  static uint16_t get_reader_nest_level(state_type state) { return state & reader_nest_level_mask; }
+
+  static bool is_quiescent_state(state_type state) { return get_reader_nest_level(state) == 0; }
+
+  static state_type make_state(state_type grace_period_phase, state_type reader_nest_level) {
+    return (grace_period_phase & grace_period_phase_mask) | (reader_nest_level & reader_nest_level_mask);
+  }
+};
+} // namespace
+
+class rcu_domain::__impl {
+  using per_thread_states = thread_local_container<reader_states::state_type>;
+
+  // only the highest bit is used for the phase.
+  std::atomic<reader_states::state_type> global_reader_phase_{};
+
+  // only one writer thread is allowed to call synchronize concurrently
+  std::mutex grace_period_mutex_; // todo this is not noexcept
+
+  // flag used for waking up writer threads waiting for all reader threads' quiescent state
+  std::atomic<bool> grace_period_waiting_flag_ = false;
+
+  // todo: maybe use a lock-free queue
+  std::mutex retire_queue_mutex_; // todo this is not noexcept
+  rcu_singly_list_view __retired_callback_queue_;
+
+  // these two queues do not need extra synchronization
+  // as they are always processed under the grace period mutex
+  rcu_singly_list_view callbacks_phase_1_;
+  rcu_singly_list_view callbacks_phase_2_;
+
+  friend class rcu_domain;
+
+  rcu_singly_list_view update_phase_and_wait() noexcept {
+    std::unique_lock retire_lk(retire_queue_mutex_);
+    callbacks_phase_1_.__splice_back(__retired_callback_queue_);
+    retire_lk.unlock();
+
+    // Flip the global phase
+    auto old_phase = global_reader_phase_.fetch_xor(reader_states::grace_period_phase_mask, std::memory_order_relaxed);
+    auto __new_phase = old_phase ^ reader_states::grace_period_phase_mask;
+    // std::printf("rcu_domain::update_phase_and_wait() new phase: 0x%04x\n", __new_phase);
+
+    __barrier();
+    // Wait for all threads to quiesce in the old phase
+    while (any_reader_in_ongoing_grace_period(__new_phase)) {
+      grace_period_waiting_flag_.store(true, std::memory_order_relaxed);
+      grace_period_waiting_flag_.wait(true, std::memory_order_relaxed);
+    }
+    grace_period_waiting_flag_.store(false, std::memory_order_relaxed);
+
+    rcu_singly_list_view ready_callbacks;
+    ready_callbacks.__splice_back(callbacks_phase_2_);
+    callbacks_phase_2_.__splice_back(callbacks_phase_1_);
+    return ready_callbacks;
+  }
+
+  bool any_reader_in_ongoing_grace_period(reader_states::state_type __global_phase) noexcept {
+    bool any_ongoing = false;
+    per_thread_states::for_each([this, __global_phase, &any_ongoing](atomic_ref<reader_states::state_type> __state) {
+      if (is_grace_period_ongoing(__state.load(memory_order_relaxed), __global_phase)) {
+        any_ongoing = true;
+      }
+    });
+    return any_ongoing;
+  }
+
+  bool is_grace_period_ongoing(reader_states::state_type thread_state,
+                               reader_states::state_type global_phase) const noexcept {
+    // https://lwn.net/Articles/323929/
+    // The phase is flipped at the beginning of a grace period.
+    // Any readers that started before the flip will have the old phase
+    // and we consider them as ongoing that we need to wait for before we can close the grace period.
+    return !reader_states::is_quiescent_state(thread_state) &&
+           reader_states::get_grace_period_phase(thread_state) != global_phase;
+  }
+
+  void __barrier() noexcept { asm volatile("" : : : "memory"); }
+
+  void printAllReaderStatesInHex() {
+    per_thread_states::for_each([](auto __state_ref) { std::printf("Reader state: 0x%04x\n", __state_ref.load()); });
+  }
+
+public:
+  void lock() noexcept {
+    auto current_thread_state_ref = per_thread_states::get_current_thread_instance();
+
+    if ((reader_states::is_quiescent_state(current_thread_state_ref.load(memory_order_relaxed)))) {
+      // Entering a read-side critical section from a quiescent state.
+      current_thread_state_ref.store(
+          reader_states::make_state(global_reader_phase_.load(std::memory_order_relaxed), 1), memory_order_relaxed);
+      __cxx_atomic_thread_fence(memory_order_seq_cst);
+    } else {
+      // Already in read-side critical section, just increment the nest level.
+      current_thread_state_ref.fetch_add(1, memory_order_relaxed);
+    }
+  }
+
+  void unlock() noexcept {
+    auto current_thread_state_ref = per_thread_states::get_current_thread_instance();
+    __cxx_atomic_thread_fence(memory_order_seq_cst);
+    // Decrement the nest level.
+    auto old_state = current_thread_state_ref.fetch_sub(1, memory_order_relaxed);
+
+    if (reader_states::get_reader_nest_level(old_state) == 1 && grace_period_waiting_flag_.load(memory_order_relaxed)) {
+      // Transitioning to quiescent state, wake up waiters.
+      grace_period_waiting_flag_.store(false, std::memory_order_relaxed);
+      grace_period_waiting_flag_.notify_all();
+    }
+  }
+
+  void retire(__rcu_node* node) noexcept {
+    lock_guard<std::mutex> lk(retire_queue_mutex_);
+    __retired_callback_queue_.__push_back(node);
+  }
+
+  void synchronize() noexcept {
+    __cxx_atomic_thread_fence(memory_order_seq_cst);
+    std::unique_lock lk(grace_period_mutex_);
+
+    // std::printf("rcu_domain::__synchronize() going through phase 1\n");
+    auto ready_callbacks = update_phase_and_wait();
+
+    // Invoke the ready callbacks outside of the grace period mutex
+    lk.unlock();
+    ready_callbacks.__for_each([](auto* node) { node->__callback_(); });
+    lk.lock();
+
+    __barrier();
+    // std::printf("rcu_domain::__synchronize() going through phase 2\n");
+    ready_callbacks = update_phase_and_wait();
+
+    // Invoke the ready callbacks outside of the grace period mutex
+    lk.unlock();
+    ready_callbacks.__for_each([](auto* node) { node->__callback_(); });
+    __cxx_atomic_thread_fence(memory_order_seq_cst);
+  }
+};
+
+rcu_domain& rcu_domain::__rcu_default_domain() noexcept {
+  static rcu_domain default_domain;
+  return default_domain;
+}
+
+rcu_domain::rcu_domain() : __pimpl_(std::make_unique<__impl>()) {}
+rcu_domain::~rcu_domain() = default;
+
+void rcu_domain::printAllReaderStatesInHex() { __pimpl_->printAllReaderStatesInHex(); }
+
+void rcu_domain::lock() noexcept { __pimpl_->lock(); }
+
+void rcu_domain::unlock() noexcept { __pimpl_->unlock(); }
+
+void rcu_domain::__retire(__rcu_node* node) noexcept { __pimpl_->retire(node); }
+
+rcu_domain& rcu_default_domain() noexcept { return rcu_domain::__rcu_default_domain(); }
+
+void rcu_synchronize(rcu_domain& dom) noexcept { dom.__pimpl_->synchronize(); }
+
+void rcu_barrier(rcu_domain& dom) noexcept { rcu_synchronize(dom); }
+
+_LIBCPP_END_NAMESPACE_STD

>From 9a37ac2f01880fc00d6093c49d55abd0aeac7d87 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 28 Feb 2026 16:38:30 +0000
Subject: [PATCH 7/9] move impl to internal linkage

---
 libcxx/include/__rcu/rcu_domain.h               | 16 +++++++++++++---
 libcxx/src/experimental/rcu.cpp                 | 17 ++++++++---------
 .../saferecl/saferecl.rcu/general.pass.cpp      |  4 +++-
 3 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/__rcu/rcu_domain.h b/libcxx/include/__rcu/rcu_domain.h
index 71aed729afa93..a055655e36b8c 100644
--- a/libcxx/include/__rcu/rcu_domain.h
+++ b/libcxx/include/__rcu/rcu_domain.h
@@ -13,6 +13,7 @@
 #include <__config>
 #include <__functional/function.h>
 #include <__memory/unique_ptr.h>
+#include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -42,6 +43,13 @@ class _LIBCPP_EXPORTED_FROM_ABI rcu_domain {
 
   static rcu_domain& __rcu_default_domain() noexcept;
 
+  template <class _Tp, class _Dp = default_delete<_Tp>>
+  _LIBCPP_HIDE_FROM_ABI void __rcu_retire_hidden_friend(_Tp* __tp, _Dp __deleter, rcu_domain& __dom) {
+    auto* __node        = new __rcu_node();
+    __node->__callback_ = [__tp, __deleter = std::move(__deleter)]() mutable { __deleter(__tp); };
+    __dom.__retire(__node);
+  }
+
   rcu_domain();
 
   void __retire(__rcu_node*) noexcept;
@@ -55,7 +63,7 @@ class _LIBCPP_EXPORTED_FROM_ABI rcu_domain {
 
   void lock() noexcept;
 
-  bool try_lock() noexcept {
+  _LIBCPP_HIDE_FROM_ABI bool try_lock() noexcept {
     lock();
     return true;
   }
@@ -69,8 +77,10 @@ _LIBCPP_EXPORTED_FROM_ABI void rcu_synchronize(rcu_domain& __dom = rcu_default_d
 
 _LIBCPP_EXPORTED_FROM_ABI void rcu_barrier(rcu_domain& __dom = rcu_default_domain()) noexcept;
 
-template <class T, class D = default_delete<T>>
-void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain());
+template <class _Tp, class _Dp = default_delete<_Tp>>
+_LIBCPP_HIDE_FROM_ABI void rcu_retire(_Tp* __tp, _Dp __deleter = _Dp(), rcu_domain& __dom = rcu_default_domain()) {
+  __rcu_retire_hidden_friend(__tp, std::move(__deleter), __dom);
+}
 
 #endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_EXPERIMENTAL_RCU
 
diff --git a/libcxx/src/experimental/rcu.cpp b/libcxx/src/experimental/rcu.cpp
index f3b3a0990a908..6bf69b74c9b00 100644
--- a/libcxx/src/experimental/rcu.cpp
+++ b/libcxx/src/experimental/rcu.cpp
@@ -13,7 +13,6 @@
 #include <rcu>
 #include <vector>
 
-#include "__rcu/rcu_domain.h"
 #include "include/rcu/rcu_list.h"
 
 _LIBCPP_BEGIN_NAMESPACE_STD
@@ -101,9 +100,8 @@ struct reader_states {
     return (grace_period_phase & grace_period_phase_mask) | (reader_nest_level & reader_nest_level_mask);
   }
 };
-} // namespace
 
-class rcu_domain::__impl {
+class rcu_domain_impl {
   using per_thread_states = thread_local_container<reader_states::state_type>;
 
   // only the highest bit is used for the phase.
@@ -172,10 +170,6 @@ class rcu_domain::__impl {
 
   void __barrier() noexcept { asm volatile("" : : : "memory"); }
 
-  void printAllReaderStatesInHex() {
-    per_thread_states::for_each([](auto __state_ref) { std::printf("Reader state: 0x%04x\n", __state_ref.load()); });
-  }
-
 public:
   void lock() noexcept {
     auto current_thread_state_ref = per_thread_states::get_current_thread_instance();
@@ -213,7 +207,6 @@ class rcu_domain::__impl {
     __cxx_atomic_thread_fence(memory_order_seq_cst);
     std::unique_lock lk(grace_period_mutex_);
 
-    // std::printf("rcu_domain::__synchronize() going through phase 1\n");
     auto ready_callbacks = update_phase_and_wait();
 
     // Invoke the ready callbacks outside of the grace period mutex
@@ -222,7 +215,6 @@ class rcu_domain::__impl {
     lk.lock();
 
     __barrier();
-    // std::printf("rcu_domain::__synchronize() going through phase 2\n");
     ready_callbacks = update_phase_and_wait();
 
     // Invoke the ready callbacks outside of the grace period mutex
@@ -230,7 +222,14 @@ class rcu_domain::__impl {
     ready_callbacks.__for_each([](auto* node) { node->__callback_(); });
     __cxx_atomic_thread_fence(memory_order_seq_cst);
   }
+
+  void printAllReaderStatesInHex() {
+    per_thread_states::for_each([](auto __state_ref) { std::printf("Reader state: 0x%04x\n", __state_ref.load()); });
+  }
 };
+} // namespace
+
+class rcu_domain::__impl : public rcu_domain_impl {};
 
 rcu_domain& rcu_domain::__rcu_default_domain() noexcept {
   static rcu_domain default_domain;
diff --git a/libcxx/test/std/thread/saferecl/saferecl.rcu/general.pass.cpp b/libcxx/test/std/thread/saferecl/saferecl.rcu/general.pass.cpp
index 88412976f7a62..b94643db87c4b 100644
--- a/libcxx/test/std/thread/saferecl/saferecl.rcu/general.pass.cpp
+++ b/libcxx/test/std/thread/saferecl/saferecl.rcu/general.pass.cpp
@@ -26,7 +26,7 @@ void log(auto start, std::string_view msg) {
   std::println(std::cout, "[{:%H:%M:%S}] {}", now - start, msg);
 }
 
-int loop_num = 1;
+int loop_num = 10;
 
 int main(int, char**) {
   auto start = std::chrono::system_clock::now();
@@ -101,6 +101,8 @@ int main(int, char**) {
   std::this_thread::sleep_for(std::chrono::milliseconds(10));
   for (int i = 0; i < loop_num+5; ++i) {
 
+  log(start, "t0: printing all reader states before synchronize");
+  std::rcu_default_domain().printAllReaderStatesInHex();
   log(start, "t0: calling rcu_synchronize" + std::to_string(i));
   std::rcu_synchronize();
   log(start, "t0: rcu_synchronize returned" + std::to_string(i));

>From 17a2918ab904a9c321d6556de7ef34b122218378 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 28 Feb 2026 18:58:12 +0000
Subject: [PATCH 8/9] more docs

---
 libcxx/docs/DesignDocs/RCU.rst      | 95 ++++++++++++++++++++++++++---
 libcxx/include/__rcu/rcu_obj_base.h |  6 +-
 2 files changed, 93 insertions(+), 8 deletions(-)

diff --git a/libcxx/docs/DesignDocs/RCU.rst b/libcxx/docs/DesignDocs/RCU.rst
index ee1f2fe6fb993..21ce05d25dff1 100644
--- a/libcxx/docs/DesignDocs/RCU.rst
+++ b/libcxx/docs/DesignDocs/RCU.rst
@@ -91,9 +91,9 @@ Some key details of this design are:
 - When `run_synchronize` is called, it
 
   - flips the global state to the next phase
-  - waits until all the threads that are in the critical section and entered in the previous phase to exit the critical section.
+  - Going through a grace period: waits until all the threads that are in the critical section with the previous phase to exit the critical section.
   - flips the global state back to the original phase
-  - waits until all the threads that are in the critical section and entered in the next phase to exit the critical section.
+  - Going through another grace period: waits until all the threads that are in the critical section ith the next phase to exit the critical section.
 
 When `run_synchronize` returns, we can be sure that all the readers that were in the critical section before `run_synchronize` are now out of the critical section.
 The paper explains why we need to wait two phases instead of just one phase in detail. The key point is that, if we only wait for the readers in the previous phase to exit,
@@ -102,13 +102,96 @@ there might be a late reader that enters the critical section after we flip the
 Implementation Terminology
 ---------------------------
 
-- reader
-- grace period
-- quiescent state
+- reader: A thread that calls `rcu_domain::lock` and `rcu_domain::unlock` to enter and exit the critical section.
+
+- quiescent state: When a thread is *not* in an RCU read-side critical section (between `lock` and `unlock`),
+  it is in a quiescent state.
+
+- grace period: Any time period during which every thread has been in at least one quiescent state is a grace period.
+  The grace period cannot end until all pre-existing readers have exited their critical sections. (see the image above)
 
 Implementation Details
 ----------------------
 
+`thread_local_container`
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+A helper class that manages thread local objects. It provides APIs to get the `thread_local` object for the current thread, and to iterate through all the `thread_local` objects from all threads.
+It only contains `static` member variables and functions.
+
+It uses `mutex` to protect the list of all object pointers. Since the list is only modified when a thread first calls `get_current_thread_instance` to register itself, and when a thread exits, 
+the contention on the `mutex` should be low.
+
+TODO: We need to replace `mutex` as all non allocating APIs in `rcu` are designed to be `noexcept` and `mutex` can throw.
+
+`reader_states`
+~~~~~~~~~~~~~~~
+
+It defines the state of each reader thread. The state is essentially a pair of `(phase, lock_nested_level)` merged into a single `uint16_t` . 
+
+- `phase` is a single bit as the global state only has two phases. This `phase` is the phase of the global state when the current thread enters the critical section.
+- `lock_nested_level` is the nested level of the `rcu_domain::lock` . As you can call `rcu_domain::lock` multiple times in the same thread, we need to keep track of the nested level.
+  When `lock_nested_level` is `0`, it means the current thread is in a quiescent state (not in a critical section).
+
+The `reader_states` class is also a `static` helper class which only provides static definitions and functions, instead of the wrapper of the `uint16_t` state. This is because we need
+the `atomic` integer API, e.g. `fetch_or`. An `atomic` of a wrapper struct won't have these APIs.
+
+`rcu_singly_list_view`
+~~~~~~~~~~~~~~~~~~~~~~
+
+A helper class that provides a view of the intrusive singly linked list of `__rcu_node` s. It also stores the back pointer of the list to make the push back operation more efficient.
+It provides APIs to push a node to the back of the list, splice from another list and to iterate through the list.
+
+For the derived class of `rcu_obj_base`, the `retire` is `noexcept`, which means that no memory allocation can happen when pushing it to the retired callback queue.
+
+`rcu_domain_impl`
+~~~~~~~~~~~~~~~~~
+
+This is the main class that implements the `rcu` logic. It contains
+
+- `std::atomic<reader_states::state_type> global_reader_phase_` : the global state that flips between two phases. The readers will record the phase when they enter the critical section, and the writer will flip the global state when it calls `run_synchronize` to start a new grace period.
+
+- `std::mutex grace_period_mutex_` : If we have multiple writer threads calling `run_synchronize` concurrenly, we need to make sure only one of them is performing the phase flipping and deleter queue draining.
+  TODO: `mutex` can throw, we need to consider how to replace it.
+
+- `std::atomic<bool> grace_period_waiting_flag_` : This flag is used to sleep/wake up the writer thread that is waiting for the grace period to end.
+
+- `std::mutex retire_queue_mutex_` and  `rcu_singly_list_view __retired_callback_queue_` : This queue stores all the retired callbacks that are waiting for the grace period to end.
+  TODO: `mutex` can throw, we need to consider how to replace it.
+
+- `rcu_singly_list_view callbacks_phase_1_` and `rcu_singly_list_view callbacks_phase_2_` : These two queues are used to let the queued retired callbacks to go through two grace periods before invocation. No additional synchronization is needed for these two queues as they are only processed when the writer thread is holding the `grace_period_mutex_` .
+
+The domain has few operations:
+
+`lock`
+^^^^^^
+
+- If current was in quiescent state, we need to record the current global phase and set the nested level to 1. 
+- If the current thread was already in the critical section, we just need to increment the nested level.
+
+`unlock`
+^^^^^^^^
+
+- We need to decrement the nested level. 
+- If the nested level becomes 0, it means the thread is now in a quiescent state, we can notify the waiting writer thread if there is any.
+
+`retire`
+^^^^^^^^
+
+- We need to push the retired callback to the `__retired_callback_queue_`
+
+`synchronize`
+^^^^^^^^^^^^^
+
+- We need to go through two phases of grace period
+- For each phase of the grace period, we need to flip the phase, and wait until all the reading threads are either
+
+  - in a quiescent state, or
+  - in the critical section with the new phase.
+
+- After each phase ends, we can move the callbacks to the next phase's queue, and we can evaluate the callbacks that have gone through two phases.
+
+
 Design Questions
 ================
 
@@ -315,5 +398,3 @@ According to Thomas Rodgers (libstdc++'s RCU implementer), their plan is to embe
 based on the presence of `SYS_membarrier` . Because `liburcu` is license compatible with libstdc++ they can directly embed the relevant source from `liburcu`
 
 
-libc++ Design
-=============
diff --git a/libcxx/include/__rcu/rcu_obj_base.h b/libcxx/include/__rcu/rcu_obj_base.h
index 4dbfe1fd07720..a8d9383ac06e5 100644
--- a/libcxx/include/__rcu/rcu_obj_base.h
+++ b/libcxx/include/__rcu/rcu_obj_base.h
@@ -28,7 +28,11 @@ template <class _Tp, class _Dp = default_delete<_Tp>>
 class rcu_obj_base : private __rcu_node {
 public:
   _LIBCPP_HIDE_FROM_ABI void retire(_Dp __deleter = _Dp(), rcu_domain& __dom = rcu_default_domain()) noexcept {
-    auto __ptr  = static_cast<_Tp*>(this);
+    auto __ptr = static_cast<_Tp*>(this);
+
+    // todo: std::function can throw on the assignment. Perhaps we can store
+    //       the deleter in the class and either use virtual function or use
+    //       function_ref here.
     __callback_ = [__ptr, __deleter = std::move(__deleter)]() mutable { __deleter(__ptr); };
     __dom.__retire(this);
   }

>From 1948f8ea4f4e49558d0e95c3f2b6850eccc7f8e1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 28 Feb 2026 19:07:04 +0000
Subject: [PATCH 9/9] doc

---
 libcxx/docs/DesignDocs/RCU.rst | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/DesignDocs/RCU.rst b/libcxx/docs/DesignDocs/RCU.rst
index 21ce05d25dff1..72d18332fbeb2 100644
--- a/libcxx/docs/DesignDocs/RCU.rst
+++ b/libcxx/docs/DesignDocs/RCU.rst
@@ -232,8 +232,8 @@ In libc++'s design, we would like to follow Folly's approach to run these delete
 When should we run the deleters
 -------------------------------
 
-If we were to use the background thread approach, the deleters can be evaluated at any time after `run_synchronize`
-has marked the objects are safe to reclaim. e.g. Drain the queue periodically. However, if we were to run the deleters
+If we were to use the background thread approach, the deleters can be evaluated at any time after the readers have exited
+their critical sections and objects are safe to reclaim. e.g. Drain the queue periodically. However, if we were to run the deleters
 inline, we have to decide when to run them.
 
 There are few places we can run the deleters:


From libcxx-commits at lists.llvm.org  Wed Jul 22 05:03:10 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 05:03:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a60b17e.b1416da1.20b4cf.f676@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/209161

>From 78cfb88d7ff5aa4c5012e6a5e8a2bf42f86b2581 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 14:28:02 +0200
Subject: [PATCH] [libc++] Optimize standard streams with
 sync_with_stdio(false)

Fixes #21566

Add more test coverage for sync_with_stdio stream switching

Extend the tests for the sync_with_stdio(false) optimization:

- ensure that the optimization is actually in effect by checking the
  type of the underlying buffer.

- verify that toggling the synchronization mode preserves per-stream state.

- cerr/wcerr are deliberately not switched to a filebuf, so check they keep
  producing correct output.

- check that C stdio and cout output interleave in order in synchronized  mode.

- read a large input to force the buffered filebuf to refill (underflow) several
  times in unsynchronized mode, and check EOF detection.

Abi changelog
---
 libcxx/include/fstream                        |  15 +--
 libcxx/lib/abi/CHANGELOG.TXT                  |  12 ++
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...xxabi.v1.stable.noexceptions.nonew.abilist |   5 +-
 libcxx/src/ios.cpp                            |   7 --
 libcxx/src/iostream.cpp                       | 102 +++++++++++++---
 .../sync_with_stdio.buffer_type.pass.cpp      |  72 ++++++++++++
 .../cerr.sync_with_stdio.sh.cpp               |  38 ++++++
 .../narrow.stream.objects/cin.readmany.sh.cpp |  56 +++++++++
 .../cin.sync_with_stdio.sh.cpp                |  37 ++++++
 .../clog.sync_with_stdio.sh.cpp               |  29 +++++
 .../cout.interleave_with_stdio.sh.cpp         |  34 ++++++
 .../cout.sync_with_stdio.sh.cpp               |  27 +++++
 .../sync_with_stdio.state.pass.cpp            | 109 ++++++++++++++++++
 .../wcerr.sync_with_stdio.sh.cpp              |  35 ++++++
 .../wide.stream.objects/wcin.readmany.sh.cpp  |  58 ++++++++++
 .../wcin.sync_with_stdio.sh.cpp               |  44 +++++++
 .../wclog.sync_with_stdio.sh.cpp              |  36 ++++++
 .../wcout.sync_with_stdio.sh.cpp              |  34 ++++++
 .../sync_with_stdio.pass.cpp                  |   2 +
 .../libcxx/test/features/availability.py      |   2 +-
 26 files changed, 740 insertions(+), 36 deletions(-)
 create mode 100644 libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..4b276251aa917 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -391,10 +391,10 @@ private:
 
   _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 
-  // There are multiple (__)open function, they use different C-API open
-  // function. After that call these functions behave the same. This function
-  // does that part and determines the final return value.
-  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __do_open(FILE* __file, ios_base::openmode __mode) {
+public:
+  // Update the buffer so that __file is considered to be the underlying file and is opened as __mode. This is used in
+  // the open family of functions as well as various iostreams in sync_with_stdio(false) mode.
+  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __adopt_file(FILE* __file, ios_base::openmode __mode) {
     __file_ = __file;
     if (!__file_)
       return nullptr;
@@ -417,6 +417,7 @@ private:
     return this;
   }
 
+private:
   // If the file is already open, switch to unbuffered mode. Otherwise, record
   // the request to use unbuffered mode so that we use that mode when we
   // eventually open the file.
@@ -742,7 +743,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(std::fopen(__s, __mdstr), __mode);
+  return __adopt_file(std::fopen(__s, __mdstr), __mode);
 }
 
 template <class _CharT, class _Traits>
@@ -753,7 +754,7 @@ inline basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::__open(in
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(fdopen(__fd, __mdstr), __mode);
+  return __adopt_file(fdopen(__fd, __mdstr), __mode);
 }
 
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
@@ -767,7 +768,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(_wfopen(__s, __mdstr), __mode);
+  return __adopt_file(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
 
diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index b09f297eb65ba..cb29de0942478 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -12,6 +12,18 @@ To generate a summary, re-generate the new ABI list using the
 
 New entries should be added directly below the "Version" header.
 
+------------
+Version 24.0
+------------
+
+* [libc++] Optimize standard streams with sync_with_stdio(false)
+
+  All platforms
+  -------------
+  Symbol added: _ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+
 ------------
 Version 23.0
 ------------
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 414a239757e1c..09999516face2 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 12, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
@@ -1908,6 +1909,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2143,6 +2145,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 20, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 64, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 590c0de8ce20f..d53eeb7532009 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index f4f4ab99ad866..384904f79d20e 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index e1b1e110318fa..8f26fdb0462cb 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1905,6 +1906,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2137,6 +2139,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 11cc8f180e824..0a2858da94a67 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1644,6 +1644,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1778,6 +1779,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1916,6 +1918,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..903457e983046 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1643,6 +1643,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1777,6 +1778,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1915,6 +1917,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 2b69d6dc89138..c24cdf845b489 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -2002,4 +2002,7 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT', 'is_defined': True}
\ No newline at end of file
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index db6ca50f7f6ca..3d4e7eb4436fd 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -373,13 +373,6 @@ void ios_base::__set_failbit_and_consider_rethrow() {
 #endif // _LIBCPP_HAS_EXCEPTIONS
 }
 
-bool ios_base::sync_with_stdio(bool sync) {
-  static bool previous_state = true;
-  bool r                     = previous_state;
-  previous_state             = sync;
-  return r;
-}
-
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..551eb25f8fe4a 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -10,6 +10,7 @@
 
 #include <__memory/construct_at.h>
 #include <__ostream/basic_ostream.h>
+#include <fstream>
 #include <istream>
 
 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
@@ -34,21 +35,44 @@ union stream {
   StreamT value;
 };
 
-template <class StreamT, class BufferT>
+template <class StreamT, class SyncBufT, class UnsyncBufT>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
   struct {
-    BufferT buffer;
+    union {
+      SyncBufT sync_buffer;
+      UnsyncBufT unsync_buffer;
+    };
     mbstate_t mb;
   };
 };
 
-template <class StreamT, class BufferT>
-void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) {
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
   data.mb = {};
-  std::construct_at(&data.buffer, stdstream, &data.mb);
-  std::construct_at(&stream.value, &data.buffer);
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  std::construct_at(&stream.value, &data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_sync_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
+  data.unsync_buffer.__adopt_file(nullptr, {}); // reset the file, so that basic_filebuf doesn't close standard streams
+  std::destroy_at(&data.unsync_buffer);
+  data.mb = {};
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  stream.value.rdbuf(&data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_unsync_stream(FILE* stdstream,
+                             stream<StreamT>& stream,
+                             stream_data<StreamT, SyncBufT, UnsyncBufT>& data,
+                             ios_base::openmode mode) {
+  std::destroy_at(&data.sync_buffer);
+  std::construct_at(&data.unsync_buffer);
+  data.unsync_buffer.__adopt_file(stdstream, mode);
+  stream.value.rdbuf(&data.unsync_buffer);
 }
 
 #define CHAR_MANGLING_char "D"
@@ -62,29 +86,39 @@ void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT,
 #endif
 
 #ifdef _LIBCPP_ABI_MICROSOFT
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__(                                \
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12 at A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
 #endif
 
 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
 
-STREAM(basic_istream, __stdinbuf, char, cin);
-STREAM(basic_ostream, __stdoutbuf, char, cout);
-STREAM(basic_ostream, __stdoutbuf, char, cerr);
-STREAM(basic_ostream, __stdoutbuf, char, clog);
+#if _LIBCPP_HAS_FILESYSTEM
+template <class CharT>
+using unsync_buffer = basic_filebuf<CharT>;
+#else
+// If we don't have access to files we can't treat the standard streams as files either - just save a dummy object in
+// that case.
+template <class>
+using unsync_buffer = char;
+#endif
+
+STREAM(basic_istream, __stdinbuf, unsync_buffer, char, cin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, clog);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+STREAM(basic_istream, __stdinbuf, unsync_buffer, wchar_t, wcin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wclog);
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
@@ -155,5 +189,37 @@ ios_base::Init::Init() {
 
 ios_base::Init::~Init() {}
 
+bool ios_base::sync_with_stdio(bool sync) {
+  static bool previous_state = true;
+  bool r                     = previous_state;
+
+#if _LIBCPP_HAS_FILESYSTEM
+  if (sync != previous_state) {
+    if (sync) {
+      switch_to_sync_stream(stdin, cin, cin_data);
+      switch_to_sync_stream(stdout, cout, cout_data);
+      switch_to_sync_stream(stderr, clog, clog_data);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_sync_stream(stdin, wcin, wcin_data);
+      switch_to_sync_stream(stdout, wcout, wcout_data);
+      switch_to_sync_stream(stderr, wclog, wclog_data);
+#  endif
+    } else {
+      switch_to_unsync_stream(stdin, cin, cin_data, ios::in);
+      switch_to_unsync_stream(stdout, cout, cout_data, ios::out);
+      switch_to_unsync_stream(stderr, clog, clog_data, ios::out);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_unsync_stream(stdin, wcin, wcin_data, ios::in);
+      switch_to_unsync_stream(stdout, wcout, wcout_data, ios::out);
+      switch_to_unsync_stream(stderr, wclog, wclog_data, ios::out);
+#  endif
+    }
+  }
+#endif // _LIBCPP_HAS_FILESYSTEM
+
+  previous_state = sync;
+  return r;
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp b/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
new file mode 100644
index 0000000000000..3a715175ff070
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// static bool ios_base::sync_with_stdio(bool sync = true);
+
+// This is a libc++ implementation detail: when sync_with_stdio(false) is used,
+// libc++ swaps the buffers of cin/cout/clog (and the wide equivalents) for a
+// basic_filebuf to avoid going through the C stdio layer. This test ensures
+// that this optimization is applied by inspecting the type of the underlying
+// buffer.
+
+// The optimization is only applied when we have a filesystem, since fstream
+// is not present otherwise. Also, the test itself requires RTTI.
+// UNSUPPORTED: no-filesystem, no-rtti
+
+// XFAIL: using-built-library-before-llvm-24
+
+#include <cassert>
+#include <fstream>
+#include <iostream>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  // By default the streams are synchronized, so they are not backed by a filebuf.
+  assert(!dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  // Turning synchronization off switches cin/cout/clog to a filebuf, but leaves
+  // cerr untouched (it stays synchronized so it keeps flushing eagerly).
+  std::ios_base::sync_with_stdio(false);
+  assert(dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  // Turning it back on restores the original buffers.
+  std::ios_base::sync_with_stdio(true);
+  assert(!dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..9f40e1ae04e67
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream cerr;
+
+// Unlike cin/cout/clog, std::cerr is still unbuffered even when sync_with_stdio(false)
+// is called. This test ensures that it keeps producing correctly-ordered output when
+// interleaved with the C stdio functions even after sync_with_stdio(false).
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios_base::sync_with_stdio(false));
+
+  // interleave output with C stdio functions
+  std::fputs("1", stderr);
+  std::cerr << "2";
+  std::fprintf(stderr, "%d", 3);
+  std::cerr << 4;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
new file mode 100644
index 0000000000000..08f417b9ba451
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// Read an input that is much larger than any reasonable internal buffer so that the
+// buffer has to be refilled (underflow) several times, and check that every value
+// is read correctly and that EOF is detected at the end.
+
+// RUN: seq 1 5000 > %t.input
+
+// RUN: %{build} -DSYNC_WITH_STDIO_FALSE
+// RUN: %{exec} %t.exe < %t.input
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+#ifdef SYNC_WITH_STDIO_FALSE
+  assert(std::ios_base::sync_with_stdio(false));
+#endif
+
+  long sum  = 0;
+  int count = 0;
+  int value = 0;
+  while (std::cin >> value) {
+    sum += value;
+    ++count;
+  }
+
+  assert(count == 5000);
+  assert(sum == 5000L * 5001L / 2L); // sum of 1..5000
+  assert(std::cin.eof());
+  assert(!std::cin.good());
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..052356449c5ab
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::cin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..ab30dee5f695d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::clog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..8497801f1778c
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// In synchronized mode, output written through std::cout and through the C stdio
+// functions must appear in the order in which it was written.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 123456 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+
+int main(int, char**) {
+  std::fputs("1", stdout);
+  std::cout << "2";
+  std::fprintf(stdout, "%d", 3);
+  std::cout << 4;
+  std::fputs("5", stdout);
+  std::cout << 6;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3a94964f09223
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::cout << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp b/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
new file mode 100644
index 0000000000000..e6f9ef3002396
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// Changing the synchronization mode with std::ios_base::sync_with_stdio must
+// preserve the per-stream state of the standard stream objects.
+
+#include <cassert>
+#include <iostream>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  int dummy       = 0;
+  const int index = std::ios_base::xalloc();
+
+  std::cin.iword(index)  = 11;
+  std::cout.iword(index) = 22;
+  std::cerr.iword(index) = 33;
+  std::clog.iword(index) = 44;
+  std::cout.pword(index) = &dummy;
+
+  std::cout.setf(std::ios_base::hex, std::ios_base::basefield);
+  std::cout.precision(7);
+  std::cout.fill('*');
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::wcin.iword(index)  = 11;
+  std::wcout.iword(index) = 22;
+  std::wcerr.iword(index) = 33;
+  std::wclog.iword(index) = 44;
+  std::wcout.pword(index) = &dummy;
+
+  std::wcout.setf(std::ios_base::hex, std::ios_base::basefield);
+  std::wcout.precision(7);
+  std::wcout.fill(L'*');
+#endif
+
+  // Switch to the unsynchronized mode. This swaps the underlying buffers, but the
+  // stream objects and all their state must be preserved.
+  std::ios_base::sync_with_stdio(false);
+
+  assert(std::cin.iword(index) == 11);
+  assert(std::cout.iword(index) == 22);
+  assert(std::cerr.iword(index) == 33);
+  assert(std::clog.iword(index) == 44);
+  assert(std::cout.pword(index) == &dummy);
+  assert((std::cout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::cout.precision() == 7);
+  assert(std::cout.fill() == '*');
+  assert(std::cin.tie() == &std::cout);
+  assert(std::cerr.tie() == &std::cout);
+  assert(std::cerr.flags() & std::ios_base::unitbuf);
+  assert(std::cin.good());
+  assert(std::cout.good());
+  assert(std::cin.rdbuf());
+  assert(std::cout.rdbuf());
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert(std::wcin.iword(index) == 11);
+  assert(std::wcout.iword(index) == 22);
+  assert(std::wcerr.iword(index) == 33);
+  assert(std::wclog.iword(index) == 44);
+  assert(std::wcout.pword(index) == &dummy);
+  assert((std::wcout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::wcout.precision() == 7);
+  assert(std::wcout.fill() == L'*');
+  assert(std::wcin.tie() == &std::wcout);
+  assert(std::wcerr.tie() == &std::wcout);
+  assert(std::wcerr.flags() & std::ios_base::unitbuf);
+  assert(std::wcin.good());
+  assert(std::wcout.good());
+  assert(std::wcin.rdbuf());
+  assert(std::wcout.rdbuf());
+#endif
+
+  // Switch back to synchronized mode and check that the state is still there.
+  std::ios_base::sync_with_stdio(true);
+
+  assert(std::cout.iword(index) == 22);
+  assert(std::cout.pword(index) == &dummy);
+  assert((std::cout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::cout.precision() == 7);
+  assert(std::cout.fill() == '*');
+  assert(std::cin.tie() == &std::cout);
+  assert(std::cerr.tie() == &std::cout);
+  assert(std::cerr.flags() & std::ios_base::unitbuf);
+  assert(std::cout.good());
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert(std::wcout.iword(index) == 22);
+  assert(std::wcout.pword(index) == &dummy);
+  assert((std::wcout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::wcout.precision() == 7);
+  assert(std::wcout.fill() == L'*');
+  assert(std::wcin.tie() == &std::wcout);
+  assert(std::wcerr.tie() == &std::wcout);
+  assert(std::wcerr.flags() & std::ios_base::unitbuf);
+  assert(std::wcout.good());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..2a65be4863bf5
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// wostream wcerr;
+
+// Unlike wcin/wcout/wclog, std::wcerr is still unbuffered even when sync_with_stdio(false)
+// is called. This test ensures that it produces correct output after sync_with_stdio(false).
+// We can't check interleaving with C stdio functions here since C stdio uses narrow characters.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios_base::sync_with_stdio(false));
+
+  std::wcerr << L"12" << 34;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
new file mode 100644
index 0000000000000..57113e21535e3
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// wistream wcin;
+
+// Read an input that is much larger than any reasonable internal buffer so that the
+// buffer has to be refilled (underflow) several times, and check that every value
+// is read correctly and that EOF is detected at the end.
+
+// RUN: seq 1 5000 > %t.input
+
+// RUN: %{build} -DSYNC_WITH_STDIO_FALSE
+// RUN: %{exec} %t.exe < %t.input
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+#ifdef SYNC_WITH_STDIO_FALSE
+  assert(std::ios_base::sync_with_stdio(false));
+#endif
+
+  long sum  = 0;
+  int count = 0;
+  int value = 0;
+  while (std::wcin >> value) {
+    sum += value;
+    ++count;
+  }
+
+  assert(count == 5000);
+  assert(sum == 5000L * 5001L / 2L); // sum of 1..5000
+  assert(std::wcin.eof());
+  assert(!std::wcin.good());
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..db6c5ba0e8660
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// istream wcin;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::wcin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..c0a0f299451fb
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wclog;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wclog << L"1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..a2cdc070620b1
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wcout;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wcout << L"1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
index 0ec24b77560e5..3051f0dc3b1cb 100644
--- a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
+++ b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
@@ -17,6 +17,8 @@
 
 int main(int, char**)
 {
+    ASSERT_SAME_TYPE(decltype(std::ios_base::sync_with_stdio(false)), bool);
+
     assert( std::ios_base::sync_with_stdio(false));
     assert(!std::ios_base::sync_with_stdio(false));
     assert(!std::ios_base::sync_with_stdio(true));
diff --git a/libcxx/utils/libcxx/test/features/availability.py b/libcxx/utils/libcxx/test/features/availability.py
index c0f2a3d4cdc88..c74383b237521 100644
--- a/libcxx/utils/libcxx/test/features/availability.py
+++ b/libcxx/utils/libcxx/test/features/availability.py
@@ -135,7 +135,7 @@
 # a libc++ flavor that enables availability markup. Similarly, a test could fail when
 # run against the system library of an older version of FreeBSD, even though FreeBSD
 # doesn't provide availability markup at the time of writing this.
-for version in ("12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"):
+for version in ("12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24"):
     features.append(
         Feature(
             name="using-built-library-before-llvm-{}".format(version),


From libcxx-commits at lists.llvm.org  Wed Jul 22 05:05:08 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 05:05:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a60b1f4.e55dc341.dbc09.7acd@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {darker}-->


:warning: Python code formatter, darker found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
darker --check --diff -r origin/main...HEAD libcxx/utils/libcxx/test/features/availability.py
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from darker here.
</summary>

``````````diff
--- availability.py	2026-07-22 12:02:57.000000 +0000
+++ availability.py	2026-07-22 12:04:31.486237 +0000
@@ -133,11 +133,25 @@
 # For example, tests for availability markup should be expected to produce diagnostics
 # regardless of whether we're running against a system library, as long as we're using
 # a libc++ flavor that enables availability markup. Similarly, a test could fail when
 # run against the system library of an older version of FreeBSD, even though FreeBSD
 # doesn't provide availability markup at the time of writing this.
-for version in ("12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24"):
+for version in (
+    "12",
+    "13",
+    "14",
+    "15",
+    "16",
+    "17",
+    "18",
+    "19",
+    "20",
+    "21",
+    "22",
+    "23",
+    "24",
+):
     features.append(
         Feature(
             name="using-built-library-before-llvm-{}".format(version),
             when=lambda cfg, v=version: BooleanExpression.evaluate(
                 "stdlib=system && !_target-has-llvm-{}".format(v),

``````````

</details>


https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Wed Jul 22 05:05:27 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 05:05:27 -0700 (PDT)
Subject: [libcxx-commits] [libcxxabi] [libc++abi][NFC] Remove some XFAILs
 which never trigger (PR #211241)
Message-ID: <llvm.org/llvm/llvm-project/pull/211241@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/211241

The oldest library we support is from LLVM 12, so we can remove any XFAILs for libraries built before that.


>From 2ef8972479f3a8dece869198d78071a496965478 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 22 Jul 2026 14:04:44 +0200
Subject: [PATCH] [libc++abi][NFC] Remove some XFAILs which never trigger

---
 libcxxabi/test/cxa_vec_new_overflow_PR41395.pass.cpp  | 3 ---
 libcxxabi/test/dynamic_cast.pass.cpp                  | 3 ---
 libcxxabi/test/test_aux_runtime_op_array_new.pass.cpp | 4 ----
 libcxxabi/test/uncaught_exceptions.pass.cpp           | 3 ---
 4 files changed, 13 deletions(-)

diff --git a/libcxxabi/test/cxa_vec_new_overflow_PR41395.pass.cpp b/libcxxabi/test/cxa_vec_new_overflow_PR41395.pass.cpp
index 746dbaad053ef..fa168e105e78e 100644
--- a/libcxxabi/test/cxa_vec_new_overflow_PR41395.pass.cpp
+++ b/libcxxabi/test/cxa_vec_new_overflow_PR41395.pass.cpp
@@ -8,9 +8,6 @@
 
 // UNSUPPORTED: no-exceptions
 
-// This test requires the fix in http://llvm.org/PR41395 (c4225e124f9e).
-// XFAIL: using-built-library-before-llvm-9
-
 #include "cxxabi.h"
 #include <cassert>
 #include <cstddef>
diff --git a/libcxxabi/test/dynamic_cast.pass.cpp b/libcxxabi/test/dynamic_cast.pass.cpp
index b144e8c06bc5f..143a4bbe135f9 100644
--- a/libcxxabi/test/dynamic_cast.pass.cpp
+++ b/libcxxabi/test/dynamic_cast.pass.cpp
@@ -6,9 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-// This test requires PR33425, PR33487 and PR33439.
-// XFAIL: using-built-library-before-llvm-9
-
 #include <cassert>
 
 // This test explicitly tests dynamic cast with types that have inaccessible
diff --git a/libcxxabi/test/test_aux_runtime_op_array_new.pass.cpp b/libcxxabi/test/test_aux_runtime_op_array_new.pass.cpp
index 3b4cea02012dd..76033307bd5b6 100644
--- a/libcxxabi/test/test_aux_runtime_op_array_new.pass.cpp
+++ b/libcxxabi/test/test_aux_runtime_op_array_new.pass.cpp
@@ -8,10 +8,6 @@
 
 // UNSUPPORTED: no-exceptions
 
-// ___cxa_throw_bad_array_new_length is re-exported from libc++ only starting
-// in LLVM 9.
-// XFAIL: using-built-library-before-llvm-9
-
 #include <cxxabi.h>
 #include <new>
 
diff --git a/libcxxabi/test/uncaught_exceptions.pass.cpp b/libcxxabi/test/uncaught_exceptions.pass.cpp
index d1074616386be..758e7a1b7979c 100644
--- a/libcxxabi/test/uncaught_exceptions.pass.cpp
+++ b/libcxxabi/test/uncaught_exceptions.pass.cpp
@@ -8,9 +8,6 @@
 
 // UNSUPPORTED: no-exceptions
 
-// __cxa_uncaught_exceptions is not re-exported from libc++ until LLVM 9.
-// XFAIL: using-built-library-before-llvm-9
-
 #include <cxxabi.h>
 #include <cassert>
 


From libcxx-commits at lists.llvm.org  Wed Jul 22 05:05:42 2026
From: libcxx-commits at lists.llvm.org (Lucas Chollet via libcxx-commits)
Date: Wed, 22 Jul 2026 05:05:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid a busy-loop in atomic's
 lost_wakeup.pass.cpp (PR #211242)
Message-ID: <llvm.org/llvm/llvm-project/pull/211242@github.com>


https://github.com/LucasChollet created https://github.com/llvm/llvm-project/pull/211242

Using __libcpp_thread_poll_with_backoff brings two improvements. First, in case of a lost wake-up, we get an assertion failure instead of a hang. And second, on single-core or heavily loaded systems, the notifier yield instead of burning all its allocated time on the busy-loop.

The measurements were done on my Linux box, the time is the one reported by lit. To isolate the test on a single core I used the following commands:

export LIT_FILTER="wait/lost_wakeup.pass.cpp"
taskset -c 0 ninja -C build check-cxx

| Configuration  | Linux futex |             | Fallback |             |
| -------------- | ----------- | ----------- | -------- | ----------- |
|                | Normal      | Pinned core | Normal   | Pinned core |
| Before         | 1.20s       | 15.54s      | 1.26s    | 601.25s     |
| With backoff   | 1.11s       | 14.42s      | 1.15s    | 17.60s      |

>From f5b7202bb251a1e0f9de9f63da353df40abe26ff Mon Sep 17 00:00:00 2001
From: Lucas Chollet <lucas.chollet at free.fr>
Date: Thu, 9 Jul 2026 16:47:37 +0200
Subject: [PATCH] [libcxx] Avoid a busy-loop in atomic's lost_wakeup.pass.cpp

Using __libcpp_thread_poll_with_backoff brings two improvements. First,
in case of a lost wake-up, we get an assertion failure instead of a
hang. And second, on single-core or heavily loaded systems, the notifier
yield instead of burning all its allocated time on the busy-loop.

The measurements were done on my Linux box, the time is the one reported
by lit. To isolate the test on a single core I used the following
commands:

export LIT_FILTER="wait/lost_wakeup.pass.cpp"
taskset -c 0 ninja -C build check-cxx

| Configuration  | Linux futex |             | Fallback |             |
| -------------- | ----------- | ----------- | -------- | ----------- |
|                | Normal      | Pinned core | Normal   | Pinned core |
| Before         | 1.20s       | 15.54s      | 1.26s    | 601.25s     |
| With backoff   | 1.11s       | 14.42s      | 1.15s    | 17.60s      |
---
 .../atomics.types.operations.wait/lost_wakeup.pass.cpp    | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
index 17ad72dd74866..6b486173a4743 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
@@ -14,6 +14,8 @@
 // <atomic>
 
 #include <atomic>
+#include <cassert>
+#include <chrono>
 #include <functional>
 #include <thread>
 #include <vector>
@@ -38,8 +40,10 @@ int main(int, char**) {
 
     auto notify = [&] {
       for (int i = 0; i < num_iterations; ++i) {
-        while (waiter_ready.load() < num_waiters) {
-        }
+        assert(std::__libcpp_thread_poll_with_backoff(
+                   [&]() -> bool { return waiter_ready.load() == num_waiters; },
+                   std::__libcpp_timed_backoff_policy(),
+                   std::chrono::seconds(1)) == std::__poll_with_backoff_results::__poll_success);
         waiter_ready.store(0);
         state.fetch_add(1);
         state.notify_all();


From libcxx-commits at lists.llvm.org  Wed Jul 22 05:06:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 05:06:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Avoid a busy-loop in atomic's
 lost_wakeup.pass.cpp (PR #211242)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211242@github.com>
Message-ID: <6a60b23f.743400b0.25573c.012a@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Lucas Chollet (LucasChollet)

<details>
<summary>Changes</summary>

Using __libcpp_thread_poll_with_backoff brings two improvements. First, in case of a lost wake-up, we get an assertion failure instead of a hang. And second, on single-core or heavily loaded systems, the notifier yield instead of burning all its allocated time on the busy-loop.

The measurements were done on my Linux box, the time is the one reported by lit. To isolate the test on a single core I used the following commands:

export LIT_FILTER="wait/lost_wakeup.pass.cpp"
taskset -c 0 ninja -C build check-cxx

| Configuration  | Linux futex |             | Fallback |             |
| -------------- | ----------- | ----------- | -------- | ----------- |
|                | Normal      | Pinned core | Normal   | Pinned core |
| Before         | 1.20s       | 15.54s      | 1.26s    | 601.25s     |
| With backoff   | 1.11s       | 14.42s      | 1.15s    | 17.60s      |

---
Full diff: https://github.com/llvm/llvm-project/pull/211242.diff


1 Files Affected:

- (modified) libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp (+6-2) 


``````````diff
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
index 17ad72dd74866..6b486173a4743 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
@@ -14,6 +14,8 @@
 // <atomic>
 
 #include <atomic>
+#include <cassert>
+#include <chrono>
 #include <functional>
 #include <thread>
 #include <vector>
@@ -38,8 +40,10 @@ int main(int, char**) {
 
     auto notify = [&] {
       for (int i = 0; i < num_iterations; ++i) {
-        while (waiter_ready.load() < num_waiters) {
-        }
+        assert(std::__libcpp_thread_poll_with_backoff(
+                   [&]() -> bool { return waiter_ready.load() == num_waiters; },
+                   std::__libcpp_timed_backoff_policy(),
+                   std::chrono::seconds(1)) == std::__poll_with_backoff_results::__poll_success);
         waiter_ready.store(0);
         state.fetch_add(1);
         state.notify_all();

``````````

</details>


https://github.com/llvm/llvm-project/pull/211242

From libcxx-commits at lists.llvm.org  Wed Jul 22 05:26:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 05:26:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a60b70b.8e75e0cb.2de827.1507@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From 145920aa6c68ee172dc65111a6a0cfb99d4c9069 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/28] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..11a0b42ea54f5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 899502422570a..4ae0f6a28637a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -32,11 +33,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -52,21 +66,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -102,12 +120,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -126,23 +146,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -152,23 +178,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From 90d997369a9f358f579ae61963226b1f30c81d5a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/28] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 37c38d25012c6..78e27d9ef9781 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From 7a63b7897f421f0d367057c933db66b3ea0f21e1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/28] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 11a0b42ea54f5..caa7f9d1a2535 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 78e27d9ef9781..640f6e5e916ca 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..8bdde44c1cb8c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -895,6 +895,7 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From e6a881f9199428718c9b93a92dcea056d7e84356 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/28] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 4ae0f6a28637a..c496ecf5ac742 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -49,7 +49,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From 13589231818d564e6ba520c405eae011f72f10fe Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/28] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From d3c6b5e3b74e59c2beaf5b3158db90faef36600c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/28] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c496ecf5ac742..c55de19cd4b59 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -148,7 +148,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From 11cedf33d3fe73d5bad5930a8c4f12df33e0eac8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/28] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 7e6410501ecdccbd78ce814b0e4906a60f379df6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/28] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c55de19cd4b59..0cf50d7ebd1ec 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -162,7 +162,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -180,7 +180,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -195,7 +195,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 640f6e5e916ca..44f1229f859b3 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From f666483df06fffa6f94a403ba051621cf0432415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/28] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index caa7f9d1a2535..b6650a314bd40 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0cf50d7ebd1ec..05b22d04962fa 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,29 +30,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 44f1229f859b3..0a6e3d54b5911 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 8bdde44c1cb8c..f038c9d39a27b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -884,6 +884,7 @@ module std {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -895,7 +896,6 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From f622cc3f082c34d6f0c5cd6e47177664eed48938 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/28] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 1e55b9997f985525c33ac424b94de00d91b36bba Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/28] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 0a6e3d54b5911..2f6e0d5dbd20a 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From 996293d9cfb334a462283e783e2fb2af90bb373c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/28] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From f74f609379e9feab234dd430bc758b19d9b1d46b Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/28] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From 8c0b1998421e1c8e214ee2694544bf3f9d8f5f79 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/28] 14

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <cstring>
+#include <type_traits>
 
 struct WithTailPadding {
   int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From f436e600ce39701927e30d0074019afc4cdb581c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 20:42:14 +0100
Subject: [PATCH 15/28] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e4d83e638ca77..0bafdbb1d7ede 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -136,7 +136,7 @@ void test() {
   {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
-
+    std::atomic<T> a;
     T init = make<T>(10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);

>From 4def68f03c3089677bb5206f56f30e367415ecce Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:13:05 +0100
Subject: [PATCH 16/28] ci

---
 libcxx/include/__atomic/support/gcc.h         |  5 ++--
 .../atomics.types.generic/padding.pass.cpp    | 24 ++++++++++-------
 .../atomics.types.generic/padding.pass.cpp    | 27 ++++++++++---------
 .../atomics.types.generic/padding2.pass.cpp   |  3 ---
 4 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 2f6e0d5dbd20a..b162e2c2a63f0 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -50,9 +50,8 @@ struct __cxx_atomic_base_impl {
     std::__clear_padding_if_needed(__a_value);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
   _Tp __a_value;
 };
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index bf594b45ed54f..6f894fe6cb605 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -44,15 +44,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -61,11 +69,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0bafdbb1d7ede..344d08b493e60 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,9 +7,6 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits
@@ -41,15 +38,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -58,11 +63,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
index 4e17471a2461f..1040f342b8206 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -8,9 +8,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-localization
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits

>From eeecefecde068ea1f6e2f8417b52961192ca8bc4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:29:04 +0100
Subject: [PATCH 17/28] CI

---
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding2.pass.cpp   | 273 ------------------
 3 files changed, 2 insertions(+), 275 deletions(-)
 delete mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 6f894fe6cb605..388ab0867fe38 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 344d08b493e60..f1a534a2916d6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
deleted file mode 100644
index 1040f342b8206..0000000000000
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-localization
-
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
-// CAS should work on types with padding bits
-
-#include <atomic>
-#include <cassert>
-#include <cstring>
-#include <type_traits>
-#include <iostream>
-
-struct Foo {
-  int i;
-  char c;
-};
-
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
-
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
-}
-
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
-}
-
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-#ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
-#else
-  (void)f;
-  (void)pad_byte;
-#endif
-}
-
-void test() {
-  std::cerr << "test 1" << std::endl;
-  {
-    // compare_exchange_strong
-    // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
-    std::cerr << "test 2" << std::endl;
-
-    Foo init = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 3" << std::endl;
-    assert_foo_padding(init, 0xBB);
-    std::cerr << "test 4" << std::endl;
-    a.store(init);
-
-    Foo expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 5" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 6" << std::endl;
-
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 7" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 8" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 9" << std::endl;
-    assert(r);
-    std::cerr << "test 10" << std::endl;
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    std::cerr << "test 11" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 42);
-    std::cerr << "test 12" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 13" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 14" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 15" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 16" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 17" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 18" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 19" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 10" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 21" << std::endl;
-    assert(!r);
-    std::cerr << "test 22" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 23" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 24" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 25" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 10);
-    std::cerr << "test 26" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 27" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 28" << std::endl;
-  }
-
-  {
-    // compare_exchange_weak
-    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 29" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 30" << std::endl;
-    a.store(stored);
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 31" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 32" << std::endl;
-
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 33" << std::endl;
-    assert_foo_padding(original_expected, 0xAA);
-    std::cerr << "test 34" << std::endl;
-
-    bool r                  = false;
-    const auto max_attempts = 100;
-    auto current_attempt    = 0;
-    while (!r) {
-      ++current_attempt;
-      std::cerr << "test 35" << std::endl;
-      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      std::cerr << "test 36" << std::endl;
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
-      std::cerr << "test 37" << std::endl;
-      r = a.compare_exchange_weak(expected, new_value);
-      if (r) {
-        std::cerr << "test 38" << std::endl;
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-        std::cerr << "test 39" << std::endl;
-      } else {
-        // Spurious failure: expected is updated to the current atomic value.
-        std::cerr << "test 40" << std::endl;
-        assert(expected.i == 10);
-        std::cerr << "test 41" << std::endl;
-        assert(expected.c == 'a');
-        std::cerr << "test 45" << std::endl;
-        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
-        std::cerr << "test 43" << std::endl;
-      }
-    }
-
-    Foo loaded = a.load();
-    std::cerr << "test 44" << std::endl;
-    assert(loaded.i == 42);
-    std::cerr << "test 45" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 46" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 47" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 48" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 49" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 50" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 51" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 52" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 53" << std::endl;
-
-    bool r = a.compare_exchange_weak(expected, new_value);
-
-    std::cerr << "test 54" << std::endl;
-    assert(!r);
-    std::cerr << "test 55" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 56" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 57" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 58" << std::endl;
-    Foo loaded = a.load();
-    std::cerr << "test 59" << std::endl;
-    assert(loaded.i == 10);
-    std::cerr << "test 60" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 61" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 62" << std::endl;
-  }
-
-  {
-    // Types with unique object representations skip the padding-clearing path.
-    std::atomic<int> a(1);
-    int expected = 1;
-    std::cerr << "test 63" << std::endl;
-    assert(a.compare_exchange_strong(expected, 2));
-    std::cerr << "test 64" << std::endl;
-    assert(expected == 1);
-    std::cerr << "test 65" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 66" << std::endl;
-
-    expected = 3;
-    assert(!a.compare_exchange_strong(expected, 4));
-    std::cerr << "test 67" << std::endl;
-    assert(expected == 2);
-    std::cerr << "test 68" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 69" << std::endl;
-  }
-}
-
-int main(int, char**) {
-  // TODO(LLVM-23): Switch to XFAIL: clang-22
-  std::cerr << "main 1" << std::endl;
-#if __has_builtin(__builtin_clear_padding)
-  std::cerr << "main 2" << std::endl;
-  test();
-  std::cerr << "main 3" << std::endl;
-#endif
-  std::cerr << "main 4" << std::endl;
-  return 0;
-}

>From bc539fb8d58e16f24a5ccb8bdd2351475e7d4391 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:52:15 +0100
Subject: [PATCH 18/28] ci

---
 libcxx/include/__atomic/support/c11.h | 13 ++++++++-----
 libcxx/include/__atomic/support/gcc.h |  9 +++++++--
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 05b22d04962fa..d43a2c67f950a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -30,13 +30,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
 
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b162e2c2a63f0..d2d4919d54614 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,12 +46,17 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
       : __a_value(std::__clear_padding_if_needed(__value)) {}
+
   _Tp __a_value;
 };
 

>From 30a056210700cd8f89b1c0ca6991f272a0ebba43 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:26:08 +0100
Subject: [PATCH 19/28] review

---
 libcxx/include/__atomic/clear_padding.h            | 14 ++++++--------
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 5bc77ff4401ab..f920ef39977b7 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -35,17 +35,16 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, float>>,
            _Not<is_same<_Tp, double>>> {};
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(std::addressof(__obj));
-    }
-  }
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+  return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
+}
+
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
@@ -67,7 +66,6 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 388ab0867fe38..45fb083cccf3f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index f1a534a2916d6..5ffba6ca844c5 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();

>From 9b771605f016811c06b3f600e48f45f3268a5269 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:33:16 +0100
Subject: [PATCH 20/28] release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx20Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..a689379321496 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange (`Github <https://llvm.org/PR76180>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 5539767ab800b..8e394b4bc4b3a 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -31,7 +31,7 @@
 "`P0458R2 <https://wg21.link/P0458R2>`__","Checking for Existence of an Element in Associative Containers","2018-06 (Rapperswil)","|Complete|","13","`#104029 <https://github.com/llvm/llvm-project/issues/104029>`__",""
 "`P0475R1 <https://wg21.link/P0475R1>`__","LWG 2511: guaranteed copy elision for piecewise construction","2018-06 (Rapperswil)","|Complete|","","`#104031 <https://github.com/llvm/llvm-project/issues/104031>`__",""
 "`P0476R2 <https://wg21.link/P0476R2>`__","Bit-casting object representations","2018-06 (Rapperswil)","|Complete|","14","`#104087 <https://github.com/llvm/llvm-project/issues/104087>`__",""
-"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","","","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
+"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","|Complete|","24","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
 "`P0542R5 <https://wg21.link/P0542R5>`__","Support for contract based programming in C++","2018-06 (Rapperswil)","|Nothing To Do|","n/a","`#104089 <https://github.com/llvm/llvm-project/issues/104089>`__","Pulled at the 2019-07 meeting in Cologne"
 "`P0556R3 <https://wg21.link/P0556R3>`__","Integral power-of-2 operations","2018-06 (Rapperswil)","|Complete|","9","`#104090 <https://github.com/llvm/llvm-project/issues/104090>`__",""
 "`P0619R4 <https://wg21.link/P0619R4>`__","Reviewing Deprecated Facilities of C++17 for C++20","2018-06 (Rapperswil)","|Complete|","20","`#99985 <https://github.com/llvm/llvm-project/issues/99985>`__","Removed headers are still provided as an extension, but with deprecation warnings."

>From 46a4ba35dbc30153f7868e9796d44f88512ae602 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 17:01:57 +0100
Subject: [PATCH 21/28] ci

---
 libcxx/include/__atomic/clear_padding.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index f920ef39977b7..3099faa8ca6d1 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/has_unique_object_representation.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>

>From 5f475333df1aeee9640364c9fb2f734c731edb7e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 19:03:39 +0100
Subject: [PATCH 22/28] ci

---
 .../atomics.types.generic/padding.pass.cpp    | 19 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 52 ++++++++++++-------
 2 files changed, 44 insertions(+), 27 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 45fb083cccf3f..116a860b3192c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -57,11 +57,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -86,7 +84,8 @@ void test() {
 
   {
     // atomic(T);
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     std::atomic<T> a(init);
     T loaded = a.load();
@@ -97,7 +96,8 @@ void test() {
   {
     // atomic::store
     std::atomic<T> a;
-    T value = make<T>(5, 'x', 0xAB);
+    T value;
+    initialize(value, 5, 'x', 0xAB);
     assert_padding(value, 0xAB);
     a.store(value);
     T loaded = a.load();
@@ -107,10 +107,12 @@ void test() {
   }
   {
     // atomic::exchange
-    T initial = make<T>(1, 'a', 0x00);
+    T initial;
+    initialize(initial, 1, 'a', 0x00);
     assert_padding(initial, 0x00);
     std::atomic<T> a(initial);
-    T new_val = make<T>(2, 'b', 0xCD);
+    T new_val;
+    initialize(new_val, 2, 'b', 0xCD);
     assert_padding(new_val, 0xCD);
     T old = a.exchange(new_val);
     assert(old.i == 1);
@@ -124,7 +126,8 @@ void test() {
   {
     // atomic_init
     std::atomic<T> a;
-    T init = make<T>(7, 'z', 0xEF);
+    T init;
+    initialize(init, 7, 'z', 0xEF);
     assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
     T loaded = a.load();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 5ffba6ca844c5..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -51,11 +51,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -84,17 +82,20 @@ void test() {
     // CAS should succeed when only padding differs in expected; expected is unchanged.
     std::atomic<T> a;
 
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0xAA);
+    T expected;
+    initialize(expected, 10, 'a', 0xAA);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -112,13 +113,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -138,17 +142,20 @@ void test() {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
     std::atomic<T> a;
-    T init = make<T>(10, 'a', 0x00);
+    T init;
+    initialize(init, 10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0x00);
+    T expected;
+    initialize(expected, 10, 'a', 0x00);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0x42);
+    T new_value;
+    initialize(new_value, 42, 'b', 0x42);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,14 +173,17 @@ void test() {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
-    T original_expected = make<T>(10, 'a', 0xAA);
+    T original_expected;
+    initialize(original_expected, 10, 'a', 0xAA);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -182,7 +192,8 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      T expected = make<T>(10, 'a', 0xAA);
+      T expected;
+      initialize(expected, 10, 'a', 0xAA);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -207,13 +218,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);

>From 704a54eb4f1f7989931b7e25ad87abbb60ad6021 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:07:26 +0100
Subject: [PATCH 23/28] debug

---
 .../atomics.types.generic/padding.pass.cpp    | 64 ++++++++++++++++++-
 1 file changed, 62 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..3b7f13b366e96 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,6 +15,7 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
+#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -70,11 +71,19 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
+void print_memory(const T& obj) {
+  for (size_t i = 0; i < sizeof(T); ++i) {
+    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
+  }
+  std::cerr << std::endl;
+}
+
 template <class T>
 void test() {
   {
@@ -84,11 +93,15 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
+    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
+    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -96,6 +109,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -106,6 +121,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test1 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -115,14 +132,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -131,11 +154,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test2 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test2 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -144,11 +171,15 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
+    std::cerr << "test3 init padding should be 0x00: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
+    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -156,6 +187,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
+    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,6 +199,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test3 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -175,15 +210,21 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
+    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
+    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -194,6 +235,8 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
+      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
+      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -203,6 +246,8 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        std::cerr << "test4 expected padding should be 0: " << std::endl;
+        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -211,6 +256,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test4 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -220,14 +267,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -236,11 +289,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test5 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test5 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -262,10 +319,13 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
+  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
+  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
+  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 0;
+  return 1;
 }

>From 8c622e41d2428f630855f0aaa215b1463d8fe4ca Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:38:19 +0100
Subject: [PATCH 24/28] Revert "debug"

This reverts commit 8be9a83ee040faff62f0756555890484bf502a31.
---
 .../atomics.types.generic/padding.pass.cpp    | 64 +------------------
 1 file changed, 2 insertions(+), 62 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 3b7f13b366e96..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,7 +15,6 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
-#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -71,19 +70,11 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)obj;
+  (void)f;
   (void)pad_byte;
 #endif
 }
 
-template <class T>
-void print_memory(const T& obj) {
-  for (size_t i = 0; i < sizeof(T); ++i) {
-    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
-  }
-  std::cerr << std::endl;
-}
-
 template <class T>
 void test() {
   {
@@ -93,15 +84,11 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
-    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
-    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -109,8 +96,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -121,8 +106,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test1 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -132,20 +115,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -154,15 +131,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test2 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test2 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -171,15 +144,11 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
-    std::cerr << "test3 init padding should be 0x00: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
-    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -187,8 +156,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
-    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -199,8 +166,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test3 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -210,21 +175,15 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
-    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
-    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -235,8 +194,6 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
-      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
-      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -246,8 +203,6 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        std::cerr << "test4 expected padding should be 0: " << std::endl;
-        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -256,8 +211,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test4 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -267,20 +220,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -289,15 +236,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test5 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test5 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -319,13 +262,10 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
-  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
-  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
-  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 1;
+  return 0;
 }

>From 789912ba84ccbb3a3efea236fde31835783a7415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:44:00 +0100
Subject: [PATCH 25/28] test ci

---
 .../atomics.types.generic/padding.pass.cpp    | 20 ++++++++++++++-----
 .../atomics.types.generic/padding.pass.cpp    | 12 +----------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 116a860b3192c..cd41f2893864c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -71,6 +71,16 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
+void assert_padding(const std::atomic<T>& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  T loaded     = obj.load();
+  set(reference, loaded.i, loaded.c);
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
+}
+
 template <class T>
 void test() {
   {
@@ -79,7 +89,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 
   {
@@ -91,7 +101,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::store
@@ -103,7 +113,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::exchange
@@ -121,7 +131,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic_init
@@ -133,7 +143,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..10c15523cd70b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -70,7 +70,7 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
@@ -105,8 +105,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -135,8 +133,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
   {
     // compare_exchange_strong
@@ -165,8 +161,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -210,8 +204,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -240,8 +232,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {

>From 81770cda9f2171565c1c2980ad8306b9665752b2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:18:49 +0100
Subject: [PATCH 26/28] padding

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index cd41f2893864c..a178ce74578ec 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -127,7 +127,6 @@ void test() {
     T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_padding(old, 0);
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');

>From b90bec87725f4bde5722a3aa857a5c9d22c275e5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:40:31 +0100
Subject: [PATCH 27/28] clang22

---
 .../libcxx/atomics/atomics.types.generic/padding.pass.cpp    | 5 ++---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 4 +---
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index a178ce74578ec..48ae68a8e97ba 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -147,11 +148,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
+
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 10c15523cd70b..dc0afa439dc12 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
@@ -250,12 +251,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
 
   return 0;
 }

>From 05729850c8bf62df54a60b12201ac475e5c55fb5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 13:26:21 +0100
Subject: [PATCH 28/28] cxx 03

---
 libcxx/include/__atomic/clear_padding.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 3099faa8ca6d1..dad48ed27e6a4 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -32,23 +32,23 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
-           _Not<is_same<_Tp, float>>,
-           _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)> >,
+           _Not<is_same<_Tp, float> >,
+           _Not<is_same<_Tp, double> > > {};
 
-template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   return __obj;
 }
 
-template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp> >::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);


From libcxx-commits at lists.llvm.org  Wed Jul 22 05:37:13 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 05:37:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Revert "[libc++][test] XFAIL
 `text/text_encoding/environment.pass.cpp` test on Armv7/Linux Ubuntu
 targets." (PR #211112)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211112@github.com>
Message-ID: <6a60b979.c5c88a87.708cc.6aaa@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/211112

From libcxx-commits at lists.llvm.org  Wed Jul 22 05:37:34 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 05:37:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 04327b1 - Revert "[libc++][test] XFAIL
 `text/text_encoding/environment.pass.cpp` test on Armv7/Linux Ubuntu
 targets." (#211112)
Message-ID: <6a60b98e.7e72d6e7.365602.2214@mx.google.com>


Author: Vladimir Vereschaka
Date: 2026-07-22T08:37:28-04:00
New Revision: 04327b1e595ac915a196340a37f48c16e6f489bc

URL: https://github.com/llvm/llvm-project/commit/04327b1e595ac915a196340a37f48c16e6f489bc
DIFF: https://github.com/llvm/llvm-project/commit/04327b1e595ac915a196340a37f48c16e6f489bc.diff

LOG: Revert "[libc++][test] XFAIL `text/text_encoding/environment.pass.cpp` test on Armv7/Linux Ubuntu targets." (#211112)

Reverts #206188

XFAIL'ed wrong test

Added: 
    

Modified: 
    libcxx/test/libcxx/text/text_encoding/environment.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/libcxx/text/text_encoding/environment.pass.cpp b/libcxx/test/libcxx/text/text_encoding/environment.pass.cpp
index 6ef36c662d778..0bcabb235fc8b 100644
--- a/libcxx/test/libcxx/text/text_encoding/environment.pass.cpp
+++ b/libcxx/test/libcxx/text/text_encoding/environment.pass.cpp
@@ -16,10 +16,6 @@
 // UNSUPPORTED: availability-te-environment-missing
 // UNSUPPORTED: LLVM-LIBC-FIXME
 
-// FIXME: gets failed on Armv7/Linux Ubuntu 18.04 LTS board (remote exec) with
-//        "Environment mismatch: Expected ID 3, received: {106,UTF-8}"
-// XFAIL: buildhost=windows && target=armv7-unknown-linux-gnueabihf
-
 // std::text_encoding::environment()
 
 #include <algorithm>


        

From libcxx-commits at lists.llvm.org  Wed Jul 22 05:37:36 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 05:37:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] Revert "[libc++][test] XFAIL
 `text/text_encoding/environment.pass.cpp` test on Armv7/Linux Ubuntu
 targets." (PR #211112)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211112@github.com>
Message-ID: <6a60b990.62a8d9ec.ed76.cd88@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/211112

From libcxx-commits at lists.llvm.org  Wed Jul 22 06:03:47 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 06:03:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][test-suite][NFC]
 Formatted and moved `transform_view`'s `sentinel.pass.cpp` (PR #211252)
Message-ID: <llvm.org/llvm/llvm-project/pull/211252@github.com>


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

As a pre-requisite to: https://github.com/llvm/llvm-project/pull/193891

>From cda683abc43784c9212d6367579a6faa7d2bc4a9 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 16:03:12 +0300
Subject: [PATCH] [libc++][NFC][ranges][test-suite] Formatted and moved
 `transform_view`'s `sentinel.pass.cpp`

As a pre-requisite to: https://github.com/llvm/llvm-project/pull/193891
---
 .../{iterator => sentinel}/sentinel.pass.cpp         | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
 rename libcxx/test/std/ranges/range.adaptors/range.transform/{iterator => sentinel}/sentinel.pass.cpp (89%)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp
similarity index 89%
rename from libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp
index ce48e09d6eb8f..b0529916775b3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp
@@ -16,7 +16,7 @@
 #include "test_macros.h"
 #include "../types.h"
 
-template<class T>
+template <class T>
 concept EndIsIter = requires(T t) { ++t.end(); };
 
 constexpr bool test() {
@@ -33,7 +33,7 @@ constexpr bool test() {
 
   std::ranges::transform_view transformView2(SizedSentinelView{4}, PlusOne());
   auto sent4 = transformView2.end();
-  auto iter = transformView1.begin();
+  auto iter  = transformView1.begin();
   {
     assert(iter != sent1);
     assert(iter != sent2);
@@ -47,10 +47,10 @@ constexpr bool test() {
   }
 
   {
-    assert(sent1 - iter  == 8);
-    assert(sent4 - iter  == 4);
-    assert(iter  - sent1 == -8);
-    assert(iter  - sent4 == -4);
+    assert(sent1 - iter == 8);
+    assert(sent4 - iter == 4);
+    assert(iter - sent1 == -8);
+    assert(iter - sent4 == -4);
   }
 
   return true;


From libcxx-commits at lists.llvm.org  Wed Jul 22 06:11:55 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 06:11:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a60c19b.4987c784.13f2de.b439@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/209161

>From 78cfb88d7ff5aa4c5012e6a5e8a2bf42f86b2581 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 13 Jul 2026 14:28:02 +0200
Subject: [PATCH 1/2] [libc++] Optimize standard streams with
 sync_with_stdio(false)

Fixes #21566

Add more test coverage for sync_with_stdio stream switching

Extend the tests for the sync_with_stdio(false) optimization:

- ensure that the optimization is actually in effect by checking the
  type of the underlying buffer.

- verify that toggling the synchronization mode preserves per-stream state.

- cerr/wcerr are deliberately not switched to a filebuf, so check they keep
  producing correct output.

- check that C stdio and cout output interleave in order in synchronized  mode.

- read a large input to force the buffered filebuf to refill (underflow) several
  times in unsynchronized mode, and check EOF detection.

Abi changelog
---
 libcxx/include/fstream                        |  15 +--
 libcxx/lib/abi/CHANGELOG.TXT                  |  12 ++
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   5 +-
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |   3 +
 ...xxabi.v1.stable.noexceptions.nonew.abilist |   5 +-
 libcxx/src/ios.cpp                            |   7 --
 libcxx/src/iostream.cpp                       | 102 +++++++++++++---
 .../sync_with_stdio.buffer_type.pass.cpp      |  72 ++++++++++++
 .../cerr.sync_with_stdio.sh.cpp               |  38 ++++++
 .../narrow.stream.objects/cin.readmany.sh.cpp |  56 +++++++++
 .../cin.sync_with_stdio.sh.cpp                |  37 ++++++
 .../clog.sync_with_stdio.sh.cpp               |  29 +++++
 .../cout.interleave_with_stdio.sh.cpp         |  34 ++++++
 .../cout.sync_with_stdio.sh.cpp               |  27 +++++
 .../sync_with_stdio.state.pass.cpp            | 109 ++++++++++++++++++
 .../wcerr.sync_with_stdio.sh.cpp              |  35 ++++++
 .../wide.stream.objects/wcin.readmany.sh.cpp  |  58 ++++++++++
 .../wcin.sync_with_stdio.sh.cpp               |  44 +++++++
 .../wclog.sync_with_stdio.sh.cpp              |  36 ++++++
 .../wcout.sync_with_stdio.sh.cpp              |  34 ++++++
 .../sync_with_stdio.pass.cpp                  |   2 +
 .../libcxx/test/features/availability.py      |   2 +-
 26 files changed, 740 insertions(+), 36 deletions(-)
 create mode 100644 libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
 create mode 100644 libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..4b276251aa917 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -391,10 +391,10 @@ private:
 
   _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 
-  // There are multiple (__)open function, they use different C-API open
-  // function. After that call these functions behave the same. This function
-  // does that part and determines the final return value.
-  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __do_open(FILE* __file, ios_base::openmode __mode) {
+public:
+  // Update the buffer so that __file is considered to be the underlying file and is opened as __mode. This is used in
+  // the open family of functions as well as various iostreams in sync_with_stdio(false) mode.
+  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __adopt_file(FILE* __file, ios_base::openmode __mode) {
     __file_ = __file;
     if (!__file_)
       return nullptr;
@@ -417,6 +417,7 @@ private:
     return this;
   }
 
+private:
   // If the file is already open, switch to unbuffered mode. Otherwise, record
   // the request to use unbuffered mode so that we use that mode when we
   // eventually open the file.
@@ -742,7 +743,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(std::fopen(__s, __mdstr), __mode);
+  return __adopt_file(std::fopen(__s, __mdstr), __mode);
 }
 
 template <class _CharT, class _Traits>
@@ -753,7 +754,7 @@ inline basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::__open(in
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(fdopen(__fd, __mdstr), __mode);
+  return __adopt_file(fdopen(__fd, __mdstr), __mode);
 }
 
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
@@ -767,7 +768,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(_wfopen(__s, __mdstr), __mode);
+  return __adopt_file(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
 
diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index b09f297eb65ba..cb29de0942478 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -12,6 +12,18 @@ To generate a summary, re-generate the new ABI list using the
 
 New entries should be added directly below the "Version" header.
 
+------------
+Version 24.0
+------------
+
+* [libc++] Optimize standard streams with sync_with_stdio(false)
+
+  All platforms
+  -------------
+  Symbol added: _ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+
 ------------
 Version 23.0
 ------------
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 414a239757e1c..09999516face2 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 12, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
@@ -1908,6 +1909,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2143,6 +2145,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 20, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 64, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 590c0de8ce20f..d53eeb7532009 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index f4f4ab99ad866..384904f79d20e 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index e1b1e110318fa..8f26fdb0462cb 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1905,6 +1906,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2137,6 +2139,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 11cc8f180e824..0a2858da94a67 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1644,6 +1644,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1778,6 +1779,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1916,6 +1918,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..903457e983046 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1643,6 +1643,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1777,6 +1778,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1915,6 +1917,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 2b69d6dc89138..c24cdf845b489 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -2002,4 +2002,7 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT', 'is_defined': True}
\ No newline at end of file
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index db6ca50f7f6ca..3d4e7eb4436fd 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -373,13 +373,6 @@ void ios_base::__set_failbit_and_consider_rethrow() {
 #endif // _LIBCPP_HAS_EXCEPTIONS
 }
 
-bool ios_base::sync_with_stdio(bool sync) {
-  static bool previous_state = true;
-  bool r                     = previous_state;
-  previous_state             = sync;
-  return r;
-}
-
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..551eb25f8fe4a 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -10,6 +10,7 @@
 
 #include <__memory/construct_at.h>
 #include <__ostream/basic_ostream.h>
+#include <fstream>
 #include <istream>
 
 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
@@ -34,21 +35,44 @@ union stream {
   StreamT value;
 };
 
-template <class StreamT, class BufferT>
+template <class StreamT, class SyncBufT, class UnsyncBufT>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
   struct {
-    BufferT buffer;
+    union {
+      SyncBufT sync_buffer;
+      UnsyncBufT unsync_buffer;
+    };
     mbstate_t mb;
   };
 };
 
-template <class StreamT, class BufferT>
-void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) {
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
   data.mb = {};
-  std::construct_at(&data.buffer, stdstream, &data.mb);
-  std::construct_at(&stream.value, &data.buffer);
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  std::construct_at(&stream.value, &data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_sync_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
+  data.unsync_buffer.__adopt_file(nullptr, {}); // reset the file, so that basic_filebuf doesn't close standard streams
+  std::destroy_at(&data.unsync_buffer);
+  data.mb = {};
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  stream.value.rdbuf(&data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_unsync_stream(FILE* stdstream,
+                             stream<StreamT>& stream,
+                             stream_data<StreamT, SyncBufT, UnsyncBufT>& data,
+                             ios_base::openmode mode) {
+  std::destroy_at(&data.sync_buffer);
+  std::construct_at(&data.unsync_buffer);
+  data.unsync_buffer.__adopt_file(stdstream, mode);
+  stream.value.rdbuf(&data.unsync_buffer);
 }
 
 #define CHAR_MANGLING_char "D"
@@ -62,29 +86,39 @@ void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT,
 #endif
 
 #ifdef _LIBCPP_ABI_MICROSOFT
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__(                                \
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12 at A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
 #endif
 
 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
 
-STREAM(basic_istream, __stdinbuf, char, cin);
-STREAM(basic_ostream, __stdoutbuf, char, cout);
-STREAM(basic_ostream, __stdoutbuf, char, cerr);
-STREAM(basic_ostream, __stdoutbuf, char, clog);
+#if _LIBCPP_HAS_FILESYSTEM
+template <class CharT>
+using unsync_buffer = basic_filebuf<CharT>;
+#else
+// If we don't have access to files we can't treat the standard streams as files either - just save a dummy object in
+// that case.
+template <class>
+using unsync_buffer = char;
+#endif
+
+STREAM(basic_istream, __stdinbuf, unsync_buffer, char, cin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, clog);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+STREAM(basic_istream, __stdinbuf, unsync_buffer, wchar_t, wcin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wclog);
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
@@ -155,5 +189,37 @@ ios_base::Init::Init() {
 
 ios_base::Init::~Init() {}
 
+bool ios_base::sync_with_stdio(bool sync) {
+  static bool previous_state = true;
+  bool r                     = previous_state;
+
+#if _LIBCPP_HAS_FILESYSTEM
+  if (sync != previous_state) {
+    if (sync) {
+      switch_to_sync_stream(stdin, cin, cin_data);
+      switch_to_sync_stream(stdout, cout, cout_data);
+      switch_to_sync_stream(stderr, clog, clog_data);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_sync_stream(stdin, wcin, wcin_data);
+      switch_to_sync_stream(stdout, wcout, wcout_data);
+      switch_to_sync_stream(stderr, wclog, wclog_data);
+#  endif
+    } else {
+      switch_to_unsync_stream(stdin, cin, cin_data, ios::in);
+      switch_to_unsync_stream(stdout, cout, cout_data, ios::out);
+      switch_to_unsync_stream(stderr, clog, clog_data, ios::out);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_unsync_stream(stdin, wcin, wcin_data, ios::in);
+      switch_to_unsync_stream(stdout, wcout, wcout_data, ios::out);
+      switch_to_unsync_stream(stderr, wclog, wclog_data, ios::out);
+#  endif
+    }
+  }
+#endif // _LIBCPP_HAS_FILESYSTEM
+
+  previous_state = sync;
+  return r;
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp b/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
new file mode 100644
index 0000000000000..3a715175ff070
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// static bool ios_base::sync_with_stdio(bool sync = true);
+
+// This is a libc++ implementation detail: when sync_with_stdio(false) is used,
+// libc++ swaps the buffers of cin/cout/clog (and the wide equivalents) for a
+// basic_filebuf to avoid going through the C stdio layer. This test ensures
+// that this optimization is applied by inspecting the type of the underlying
+// buffer.
+
+// The optimization is only applied when we have a filesystem, since fstream
+// is not present otherwise. Also, the test itself requires RTTI.
+// UNSUPPORTED: no-filesystem, no-rtti
+
+// XFAIL: using-built-library-before-llvm-24
+
+#include <cassert>
+#include <fstream>
+#include <iostream>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  // By default the streams are synchronized, so they are not backed by a filebuf.
+  assert(!dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  // Turning synchronization off switches cin/cout/clog to a filebuf, but leaves
+  // cerr untouched (it stays synchronized so it keeps flushing eagerly).
+  std::ios_base::sync_with_stdio(false);
+  assert(dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  // Turning it back on restores the original buffers.
+  std::ios_base::sync_with_stdio(true);
+  assert(!dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..9f40e1ae04e67
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream cerr;
+
+// Unlike cin/cout/clog, std::cerr is still unbuffered even when sync_with_stdio(false)
+// is called. This test ensures that it keeps producing correctly-ordered output when
+// interleaved with the C stdio functions even after sync_with_stdio(false).
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios_base::sync_with_stdio(false));
+
+  // interleave output with C stdio functions
+  std::fputs("1", stderr);
+  std::cerr << "2";
+  std::fprintf(stderr, "%d", 3);
+  std::cerr << 4;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
new file mode 100644
index 0000000000000..08f417b9ba451
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// Read an input that is much larger than any reasonable internal buffer so that the
+// buffer has to be refilled (underflow) several times, and check that every value
+// is read correctly and that EOF is detected at the end.
+
+// RUN: seq 1 5000 > %t.input
+
+// RUN: %{build} -DSYNC_WITH_STDIO_FALSE
+// RUN: %{exec} %t.exe < %t.input
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+#ifdef SYNC_WITH_STDIO_FALSE
+  assert(std::ios_base::sync_with_stdio(false));
+#endif
+
+  long sum  = 0;
+  int count = 0;
+  int value = 0;
+  while (std::cin >> value) {
+    sum += value;
+    ++count;
+  }
+
+  assert(count == 5000);
+  assert(sum == 5000L * 5001L / 2L); // sum of 1..5000
+  assert(std::cin.eof());
+  assert(!std::cin.good());
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..052356449c5ab
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::cin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..ab30dee5f695d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::clog << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..8497801f1778c
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// In synchronized mode, output written through std::cout and through the C stdio
+// functions must appear in the order in which it was written.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 123456 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+
+int main(int, char**) {
+  std::fputs("1", stdout);
+  std::cout << "2";
+  std::fprintf(stdout, "%d", 3);
+  std::cout << 4;
+  std::fputs("5", stdout);
+  std::cout << 6;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3a94964f09223
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::cout << "1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp b/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
new file mode 100644
index 0000000000000..e6f9ef3002396
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// Changing the synchronization mode with std::ios_base::sync_with_stdio must
+// preserve the per-stream state of the standard stream objects.
+
+#include <cassert>
+#include <iostream>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  int dummy       = 0;
+  const int index = std::ios_base::xalloc();
+
+  std::cin.iword(index)  = 11;
+  std::cout.iword(index) = 22;
+  std::cerr.iword(index) = 33;
+  std::clog.iword(index) = 44;
+  std::cout.pword(index) = &dummy;
+
+  std::cout.setf(std::ios_base::hex, std::ios_base::basefield);
+  std::cout.precision(7);
+  std::cout.fill('*');
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::wcin.iword(index)  = 11;
+  std::wcout.iword(index) = 22;
+  std::wcerr.iword(index) = 33;
+  std::wclog.iword(index) = 44;
+  std::wcout.pword(index) = &dummy;
+
+  std::wcout.setf(std::ios_base::hex, std::ios_base::basefield);
+  std::wcout.precision(7);
+  std::wcout.fill(L'*');
+#endif
+
+  // Switch to the unsynchronized mode. This swaps the underlying buffers, but the
+  // stream objects and all their state must be preserved.
+  std::ios_base::sync_with_stdio(false);
+
+  assert(std::cin.iword(index) == 11);
+  assert(std::cout.iword(index) == 22);
+  assert(std::cerr.iword(index) == 33);
+  assert(std::clog.iword(index) == 44);
+  assert(std::cout.pword(index) == &dummy);
+  assert((std::cout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::cout.precision() == 7);
+  assert(std::cout.fill() == '*');
+  assert(std::cin.tie() == &std::cout);
+  assert(std::cerr.tie() == &std::cout);
+  assert(std::cerr.flags() & std::ios_base::unitbuf);
+  assert(std::cin.good());
+  assert(std::cout.good());
+  assert(std::cin.rdbuf());
+  assert(std::cout.rdbuf());
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert(std::wcin.iword(index) == 11);
+  assert(std::wcout.iword(index) == 22);
+  assert(std::wcerr.iword(index) == 33);
+  assert(std::wclog.iword(index) == 44);
+  assert(std::wcout.pword(index) == &dummy);
+  assert((std::wcout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::wcout.precision() == 7);
+  assert(std::wcout.fill() == L'*');
+  assert(std::wcin.tie() == &std::wcout);
+  assert(std::wcerr.tie() == &std::wcout);
+  assert(std::wcerr.flags() & std::ios_base::unitbuf);
+  assert(std::wcin.good());
+  assert(std::wcout.good());
+  assert(std::wcin.rdbuf());
+  assert(std::wcout.rdbuf());
+#endif
+
+  // Switch back to synchronized mode and check that the state is still there.
+  std::ios_base::sync_with_stdio(true);
+
+  assert(std::cout.iword(index) == 22);
+  assert(std::cout.pword(index) == &dummy);
+  assert((std::cout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::cout.precision() == 7);
+  assert(std::cout.fill() == '*');
+  assert(std::cin.tie() == &std::cout);
+  assert(std::cerr.tie() == &std::cout);
+  assert(std::cerr.flags() & std::ios_base::unitbuf);
+  assert(std::cout.good());
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert(std::wcout.iword(index) == 22);
+  assert(std::wcout.pword(index) == &dummy);
+  assert((std::wcout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::wcout.precision() == 7);
+  assert(std::wcout.fill() == L'*');
+  assert(std::wcin.tie() == &std::wcout);
+  assert(std::wcerr.tie() == &std::wcout);
+  assert(std::wcerr.flags() & std::ios_base::unitbuf);
+  assert(std::wcout.good());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..2a65be4863bf5
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// wostream wcerr;
+
+// Unlike wcin/wcout/wclog, std::wcerr is still unbuffered even when sync_with_stdio(false)
+// is called. This test ensures that it produces correct output after sync_with_stdio(false).
+// We can't check interleaving with C stdio functions here since C stdio uses narrow characters.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios_base::sync_with_stdio(false));
+
+  std::wcerr << L"12" << 34;
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
new file mode 100644
index 0000000000000..57113e21535e3
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// wistream wcin;
+
+// Read an input that is much larger than any reasonable internal buffer so that the
+// buffer has to be refilled (underflow) several times, and check that every value
+// is read correctly and that EOF is detected at the end.
+
+// RUN: seq 1 5000 > %t.input
+
+// RUN: %{build} -DSYNC_WITH_STDIO_FALSE
+// RUN: %{exec} %t.exe < %t.input
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+#ifdef SYNC_WITH_STDIO_FALSE
+  assert(std::ios_base::sync_with_stdio(false));
+#endif
+
+  long sum  = 0;
+  int count = 0;
+  int value = 0;
+  while (std::wcin >> value) {
+    sum += value;
+    ++count;
+  }
+
+  assert(count == 5000);
+  assert(sum == 5000L * 5001L / 2L); // sum of 1..5000
+  assert(std::wcin.eof());
+  assert(!std::wcin.good());
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..db6c5ba0e8660
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// istream wcin;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::wcin >> i;
+  assert(i == 1234);
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..c0a0f299451fb
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wclog;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wclog << L"1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..a2cdc070620b1
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wcout;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: diff %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wcout << L"1234";
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
index 0ec24b77560e5..3051f0dc3b1cb 100644
--- a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
+++ b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
@@ -17,6 +17,8 @@
 
 int main(int, char**)
 {
+    ASSERT_SAME_TYPE(decltype(std::ios_base::sync_with_stdio(false)), bool);
+
     assert( std::ios_base::sync_with_stdio(false));
     assert(!std::ios_base::sync_with_stdio(false));
     assert(!std::ios_base::sync_with_stdio(true));
diff --git a/libcxx/utils/libcxx/test/features/availability.py b/libcxx/utils/libcxx/test/features/availability.py
index c0f2a3d4cdc88..c74383b237521 100644
--- a/libcxx/utils/libcxx/test/features/availability.py
+++ b/libcxx/utils/libcxx/test/features/availability.py
@@ -135,7 +135,7 @@
 # a libc++ flavor that enables availability markup. Similarly, a test could fail when
 # run against the system library of an older version of FreeBSD, even though FreeBSD
 # doesn't provide availability markup at the time of writing this.
-for version in ("12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"):
+for version in ("12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24"):
     features.append(
         Feature(
             name="using-built-library-before-llvm-{}".format(version),

>From a3707f55c9bc59aaf475ec8c7ea90170dcc90e35 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 22 Jul 2026 09:11:40 -0400
Subject: [PATCH 2/2] Generate seq 1 5000 into a file

---
 .../iostream.objects/many-ints.dat            | 5000 +++++++++++++++++
 .../narrow.stream.objects/cin.readmany.sh.cpp |    6 +-
 .../wide.stream.objects/wcin.readmany.sh.cpp  |    6 +-
 3 files changed, 5006 insertions(+), 6 deletions(-)
 create mode 100644 libcxx/test/std/input.output/iostream.objects/many-ints.dat

diff --git a/libcxx/test/std/input.output/iostream.objects/many-ints.dat b/libcxx/test/std/input.output/iostream.objects/many-ints.dat
new file mode 100644
index 0000000000000..7d1714969fc2d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/many-ints.dat
@@ -0,0 +1,5000 @@
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
+1288
+1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
+1301
+1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
+1316
+1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
+1331
+1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
+1344
+1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
+1355
+1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
+1364
+1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
+1385
+1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
+1406
+1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
+1416
+1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
+1437
+1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
+1458
+1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
+1485
+1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
+1498
+1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
+1529
+1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
+1560
+1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+1574
+1575
+1576
+1577
+1578
+1579
+1580
+1581
+1582
+1583
+1584
+1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
+1599
+1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
+1609
+1610
+1611
+1612
+1613
+1614
+1615
+1616
+1617
+1618
+1619
+1620
+1621
+1622
+1623
+1624
+1625
+1626
+1627
+1628
+1629
+1630
+1631
+1632
+1633
+1634
+1635
+1636
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
+1645
+1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
+1658
+1659
+1660
+1661
+1662
+1663
+1664
+1665
+1666
+1667
+1668
+1669
+1670
+1671
+1672
+1673
+1674
+1675
+1676
+1677
+1678
+1679
+1680
+1681
+1682
+1683
+1684
+1685
+1686
+1687
+1688
+1689
+1690
+1691
+1692
+1693
+1694
+1695
+1696
+1697
+1698
+1699
+1700
+1701
+1702
+1703
+1704
+1705
+1706
+1707
+1708
+1709
+1710
+1711
+1712
+1713
+1714
+1715
+1716
+1717
+1718
+1719
+1720
+1721
+1722
+1723
+1724
+1725
+1726
+1727
+1728
+1729
+1730
+1731
+1732
+1733
+1734
+1735
+1736
+1737
+1738
+1739
+1740
+1741
+1742
+1743
+1744
+1745
+1746
+1747
+1748
+1749
+1750
+1751
+1752
+1753
+1754
+1755
+1756
+1757
+1758
+1759
+1760
+1761
+1762
+1763
+1764
+1765
+1766
+1767
+1768
+1769
+1770
+1771
+1772
+1773
+1774
+1775
+1776
+1777
+1778
+1779
+1780
+1781
+1782
+1783
+1784
+1785
+1786
+1787
+1788
+1789
+1790
+1791
+1792
+1793
+1794
+1795
+1796
+1797
+1798
+1799
+1800
+1801
+1802
+1803
+1804
+1805
+1806
+1807
+1808
+1809
+1810
+1811
+1812
+1813
+1814
+1815
+1816
+1817
+1818
+1819
+1820
+1821
+1822
+1823
+1824
+1825
+1826
+1827
+1828
+1829
+1830
+1831
+1832
+1833
+1834
+1835
+1836
+1837
+1838
+1839
+1840
+1841
+1842
+1843
+1844
+1845
+1846
+1847
+1848
+1849
+1850
+1851
+1852
+1853
+1854
+1855
+1856
+1857
+1858
+1859
+1860
+1861
+1862
+1863
+1864
+1865
+1866
+1867
+1868
+1869
+1870
+1871
+1872
+1873
+1874
+1875
+1876
+1877
+1878
+1879
+1880
+1881
+1882
+1883
+1884
+1885
+1886
+1887
+1888
+1889
+1890
+1891
+1892
+1893
+1894
+1895
+1896
+1897
+1898
+1899
+1900
+1901
+1902
+1903
+1904
+1905
+1906
+1907
+1908
+1909
+1910
+1911
+1912
+1913
+1914
+1915
+1916
+1917
+1918
+1919
+1920
+1921
+1922
+1923
+1924
+1925
+1926
+1927
+1928
+1929
+1930
+1931
+1932
+1933
+1934
+1935
+1936
+1937
+1938
+1939
+1940
+1941
+1942
+1943
+1944
+1945
+1946
+1947
+1948
+1949
+1950
+1951
+1952
+1953
+1954
+1955
+1956
+1957
+1958
+1959
+1960
+1961
+1962
+1963
+1964
+1965
+1966
+1967
+1968
+1969
+1970
+1971
+1972
+1973
+1974
+1975
+1976
+1977
+1978
+1979
+1980
+1981
+1982
+1983
+1984
+1985
+1986
+1987
+1988
+1989
+1990
+1991
+1992
+1993
+1994
+1995
+1996
+1997
+1998
+1999
+2000
+2001
+2002
+2003
+2004
+2005
+2006
+2007
+2008
+2009
+2010
+2011
+2012
+2013
+2014
+2015
+2016
+2017
+2018
+2019
+2020
+2021
+2022
+2023
+2024
+2025
+2026
+2027
+2028
+2029
+2030
+2031
+2032
+2033
+2034
+2035
+2036
+2037
+2038
+2039
+2040
+2041
+2042
+2043
+2044
+2045
+2046
+2047
+2048
+2049
+2050
+2051
+2052
+2053
+2054
+2055
+2056
+2057
+2058
+2059
+2060
+2061
+2062
+2063
+2064
+2065
+2066
+2067
+2068
+2069
+2070
+2071
+2072
+2073
+2074
+2075
+2076
+2077
+2078
+2079
+2080
+2081
+2082
+2083
+2084
+2085
+2086
+2087
+2088
+2089
+2090
+2091
+2092
+2093
+2094
+2095
+2096
+2097
+2098
+2099
+2100
+2101
+2102
+2103
+2104
+2105
+2106
+2107
+2108
+2109
+2110
+2111
+2112
+2113
+2114
+2115
+2116
+2117
+2118
+2119
+2120
+2121
+2122
+2123
+2124
+2125
+2126
+2127
+2128
+2129
+2130
+2131
+2132
+2133
+2134
+2135
+2136
+2137
+2138
+2139
+2140
+2141
+2142
+2143
+2144
+2145
+2146
+2147
+2148
+2149
+2150
+2151
+2152
+2153
+2154
+2155
+2156
+2157
+2158
+2159
+2160
+2161
+2162
+2163
+2164
+2165
+2166
+2167
+2168
+2169
+2170
+2171
+2172
+2173
+2174
+2175
+2176
+2177
+2178
+2179
+2180
+2181
+2182
+2183
+2184
+2185
+2186
+2187
+2188
+2189
+2190
+2191
+2192
+2193
+2194
+2195
+2196
+2197
+2198
+2199
+2200
+2201
+2202
+2203
+2204
+2205
+2206
+2207
+2208
+2209
+2210
+2211
+2212
+2213
+2214
+2215
+2216
+2217
+2218
+2219
+2220
+2221
+2222
+2223
+2224
+2225
+2226
+2227
+2228
+2229
+2230
+2231
+2232
+2233
+2234
+2235
+2236
+2237
+2238
+2239
+2240
+2241
+2242
+2243
+2244
+2245
+2246
+2247
+2248
+2249
+2250
+2251
+2252
+2253
+2254
+2255
+2256
+2257
+2258
+2259
+2260
+2261
+2262
+2263
+2264
+2265
+2266
+2267
+2268
+2269
+2270
+2271
+2272
+2273
+2274
+2275
+2276
+2277
+2278
+2279
+2280
+2281
+2282
+2283
+2284
+2285
+2286
+2287
+2288
+2289
+2290
+2291
+2292
+2293
+2294
+2295
+2296
+2297
+2298
+2299
+2300
+2301
+2302
+2303
+2304
+2305
+2306
+2307
+2308
+2309
+2310
+2311
+2312
+2313
+2314
+2315
+2316
+2317
+2318
+2319
+2320
+2321
+2322
+2323
+2324
+2325
+2326
+2327
+2328
+2329
+2330
+2331
+2332
+2333
+2334
+2335
+2336
+2337
+2338
+2339
+2340
+2341
+2342
+2343
+2344
+2345
+2346
+2347
+2348
+2349
+2350
+2351
+2352
+2353
+2354
+2355
+2356
+2357
+2358
+2359
+2360
+2361
+2362
+2363
+2364
+2365
+2366
+2367
+2368
+2369
+2370
+2371
+2372
+2373
+2374
+2375
+2376
+2377
+2378
+2379
+2380
+2381
+2382
+2383
+2384
+2385
+2386
+2387
+2388
+2389
+2390
+2391
+2392
+2393
+2394
+2395
+2396
+2397
+2398
+2399
+2400
+2401
+2402
+2403
+2404
+2405
+2406
+2407
+2408
+2409
+2410
+2411
+2412
+2413
+2414
+2415
+2416
+2417
+2418
+2419
+2420
+2421
+2422
+2423
+2424
+2425
+2426
+2427
+2428
+2429
+2430
+2431
+2432
+2433
+2434
+2435
+2436
+2437
+2438
+2439
+2440
+2441
+2442
+2443
+2444
+2445
+2446
+2447
+2448
+2449
+2450
+2451
+2452
+2453
+2454
+2455
+2456
+2457
+2458
+2459
+2460
+2461
+2462
+2463
+2464
+2465
+2466
+2467
+2468
+2469
+2470
+2471
+2472
+2473
+2474
+2475
+2476
+2477
+2478
+2479
+2480
+2481
+2482
+2483
+2484
+2485
+2486
+2487
+2488
+2489
+2490
+2491
+2492
+2493
+2494
+2495
+2496
+2497
+2498
+2499
+2500
+2501
+2502
+2503
+2504
+2505
+2506
+2507
+2508
+2509
+2510
+2511
+2512
+2513
+2514
+2515
+2516
+2517
+2518
+2519
+2520
+2521
+2522
+2523
+2524
+2525
+2526
+2527
+2528
+2529
+2530
+2531
+2532
+2533
+2534
+2535
+2536
+2537
+2538
+2539
+2540
+2541
+2542
+2543
+2544
+2545
+2546
+2547
+2548
+2549
+2550
+2551
+2552
+2553
+2554
+2555
+2556
+2557
+2558
+2559
+2560
+2561
+2562
+2563
+2564
+2565
+2566
+2567
+2568
+2569
+2570
+2571
+2572
+2573
+2574
+2575
+2576
+2577
+2578
+2579
+2580
+2581
+2582
+2583
+2584
+2585
+2586
+2587
+2588
+2589
+2590
+2591
+2592
+2593
+2594
+2595
+2596
+2597
+2598
+2599
+2600
+2601
+2602
+2603
+2604
+2605
+2606
+2607
+2608
+2609
+2610
+2611
+2612
+2613
+2614
+2615
+2616
+2617
+2618
+2619
+2620
+2621
+2622
+2623
+2624
+2625
+2626
+2627
+2628
+2629
+2630
+2631
+2632
+2633
+2634
+2635
+2636
+2637
+2638
+2639
+2640
+2641
+2642
+2643
+2644
+2645
+2646
+2647
+2648
+2649
+2650
+2651
+2652
+2653
+2654
+2655
+2656
+2657
+2658
+2659
+2660
+2661
+2662
+2663
+2664
+2665
+2666
+2667
+2668
+2669
+2670
+2671
+2672
+2673
+2674
+2675
+2676
+2677
+2678
+2679
+2680
+2681
+2682
+2683
+2684
+2685
+2686
+2687
+2688
+2689
+2690
+2691
+2692
+2693
+2694
+2695
+2696
+2697
+2698
+2699
+2700
+2701
+2702
+2703
+2704
+2705
+2706
+2707
+2708
+2709
+2710
+2711
+2712
+2713
+2714
+2715
+2716
+2717
+2718
+2719
+2720
+2721
+2722
+2723
+2724
+2725
+2726
+2727
+2728
+2729
+2730
+2731
+2732
+2733
+2734
+2735
+2736
+2737
+2738
+2739
+2740
+2741
+2742
+2743
+2744
+2745
+2746
+2747
+2748
+2749
+2750
+2751
+2752
+2753
+2754
+2755
+2756
+2757
+2758
+2759
+2760
+2761
+2762
+2763
+2764
+2765
+2766
+2767
+2768
+2769
+2770
+2771
+2772
+2773
+2774
+2775
+2776
+2777
+2778
+2779
+2780
+2781
+2782
+2783
+2784
+2785
+2786
+2787
+2788
+2789
+2790
+2791
+2792
+2793
+2794
+2795
+2796
+2797
+2798
+2799
+2800
+2801
+2802
+2803
+2804
+2805
+2806
+2807
+2808
+2809
+2810
+2811
+2812
+2813
+2814
+2815
+2816
+2817
+2818
+2819
+2820
+2821
+2822
+2823
+2824
+2825
+2826
+2827
+2828
+2829
+2830
+2831
+2832
+2833
+2834
+2835
+2836
+2837
+2838
+2839
+2840
+2841
+2842
+2843
+2844
+2845
+2846
+2847
+2848
+2849
+2850
+2851
+2852
+2853
+2854
+2855
+2856
+2857
+2858
+2859
+2860
+2861
+2862
+2863
+2864
+2865
+2866
+2867
+2868
+2869
+2870
+2871
+2872
+2873
+2874
+2875
+2876
+2877
+2878
+2879
+2880
+2881
+2882
+2883
+2884
+2885
+2886
+2887
+2888
+2889
+2890
+2891
+2892
+2893
+2894
+2895
+2896
+2897
+2898
+2899
+2900
+2901
+2902
+2903
+2904
+2905
+2906
+2907
+2908
+2909
+2910
+2911
+2912
+2913
+2914
+2915
+2916
+2917
+2918
+2919
+2920
+2921
+2922
+2923
+2924
+2925
+2926
+2927
+2928
+2929
+2930
+2931
+2932
+2933
+2934
+2935
+2936
+2937
+2938
+2939
+2940
+2941
+2942
+2943
+2944
+2945
+2946
+2947
+2948
+2949
+2950
+2951
+2952
+2953
+2954
+2955
+2956
+2957
+2958
+2959
+2960
+2961
+2962
+2963
+2964
+2965
+2966
+2967
+2968
+2969
+2970
+2971
+2972
+2973
+2974
+2975
+2976
+2977
+2978
+2979
+2980
+2981
+2982
+2983
+2984
+2985
+2986
+2987
+2988
+2989
+2990
+2991
+2992
+2993
+2994
+2995
+2996
+2997
+2998
+2999
+3000
+3001
+3002
+3003
+3004
+3005
+3006
+3007
+3008
+3009
+3010
+3011
+3012
+3013
+3014
+3015
+3016
+3017
+3018
+3019
+3020
+3021
+3022
+3023
+3024
+3025
+3026
+3027
+3028
+3029
+3030
+3031
+3032
+3033
+3034
+3035
+3036
+3037
+3038
+3039
+3040
+3041
+3042
+3043
+3044
+3045
+3046
+3047
+3048
+3049
+3050
+3051
+3052
+3053
+3054
+3055
+3056
+3057
+3058
+3059
+3060
+3061
+3062
+3063
+3064
+3065
+3066
+3067
+3068
+3069
+3070
+3071
+3072
+3073
+3074
+3075
+3076
+3077
+3078
+3079
+3080
+3081
+3082
+3083
+3084
+3085
+3086
+3087
+3088
+3089
+3090
+3091
+3092
+3093
+3094
+3095
+3096
+3097
+3098
+3099
+3100
+3101
+3102
+3103
+3104
+3105
+3106
+3107
+3108
+3109
+3110
+3111
+3112
+3113
+3114
+3115
+3116
+3117
+3118
+3119
+3120
+3121
+3122
+3123
+3124
+3125
+3126
+3127
+3128
+3129
+3130
+3131
+3132
+3133
+3134
+3135
+3136
+3137
+3138
+3139
+3140
+3141
+3142
+3143
+3144
+3145
+3146
+3147
+3148
+3149
+3150
+3151
+3152
+3153
+3154
+3155
+3156
+3157
+3158
+3159
+3160
+3161
+3162
+3163
+3164
+3165
+3166
+3167
+3168
+3169
+3170
+3171
+3172
+3173
+3174
+3175
+3176
+3177
+3178
+3179
+3180
+3181
+3182
+3183
+3184
+3185
+3186
+3187
+3188
+3189
+3190
+3191
+3192
+3193
+3194
+3195
+3196
+3197
+3198
+3199
+3200
+3201
+3202
+3203
+3204
+3205
+3206
+3207
+3208
+3209
+3210
+3211
+3212
+3213
+3214
+3215
+3216
+3217
+3218
+3219
+3220
+3221
+3222
+3223
+3224
+3225
+3226
+3227
+3228
+3229
+3230
+3231
+3232
+3233
+3234
+3235
+3236
+3237
+3238
+3239
+3240
+3241
+3242
+3243
+3244
+3245
+3246
+3247
+3248
+3249
+3250
+3251
+3252
+3253
+3254
+3255
+3256
+3257
+3258
+3259
+3260
+3261
+3262
+3263
+3264
+3265
+3266
+3267
+3268
+3269
+3270
+3271
+3272
+3273
+3274
+3275
+3276
+3277
+3278
+3279
+3280
+3281
+3282
+3283
+3284
+3285
+3286
+3287
+3288
+3289
+3290
+3291
+3292
+3293
+3294
+3295
+3296
+3297
+3298
+3299
+3300
+3301
+3302
+3303
+3304
+3305
+3306
+3307
+3308
+3309
+3310
+3311
+3312
+3313
+3314
+3315
+3316
+3317
+3318
+3319
+3320
+3321
+3322
+3323
+3324
+3325
+3326
+3327
+3328
+3329
+3330
+3331
+3332
+3333
+3334
+3335
+3336
+3337
+3338
+3339
+3340
+3341
+3342
+3343
+3344
+3345
+3346
+3347
+3348
+3349
+3350
+3351
+3352
+3353
+3354
+3355
+3356
+3357
+3358
+3359
+3360
+3361
+3362
+3363
+3364
+3365
+3366
+3367
+3368
+3369
+3370
+3371
+3372
+3373
+3374
+3375
+3376
+3377
+3378
+3379
+3380
+3381
+3382
+3383
+3384
+3385
+3386
+3387
+3388
+3389
+3390
+3391
+3392
+3393
+3394
+3395
+3396
+3397
+3398
+3399
+3400
+3401
+3402
+3403
+3404
+3405
+3406
+3407
+3408
+3409
+3410
+3411
+3412
+3413
+3414
+3415
+3416
+3417
+3418
+3419
+3420
+3421
+3422
+3423
+3424
+3425
+3426
+3427
+3428
+3429
+3430
+3431
+3432
+3433
+3434
+3435
+3436
+3437
+3438
+3439
+3440
+3441
+3442
+3443
+3444
+3445
+3446
+3447
+3448
+3449
+3450
+3451
+3452
+3453
+3454
+3455
+3456
+3457
+3458
+3459
+3460
+3461
+3462
+3463
+3464
+3465
+3466
+3467
+3468
+3469
+3470
+3471
+3472
+3473
+3474
+3475
+3476
+3477
+3478
+3479
+3480
+3481
+3482
+3483
+3484
+3485
+3486
+3487
+3488
+3489
+3490
+3491
+3492
+3493
+3494
+3495
+3496
+3497
+3498
+3499
+3500
+3501
+3502
+3503
+3504
+3505
+3506
+3507
+3508
+3509
+3510
+3511
+3512
+3513
+3514
+3515
+3516
+3517
+3518
+3519
+3520
+3521
+3522
+3523
+3524
+3525
+3526
+3527
+3528
+3529
+3530
+3531
+3532
+3533
+3534
+3535
+3536
+3537
+3538
+3539
+3540
+3541
+3542
+3543
+3544
+3545
+3546
+3547
+3548
+3549
+3550
+3551
+3552
+3553
+3554
+3555
+3556
+3557
+3558
+3559
+3560
+3561
+3562
+3563
+3564
+3565
+3566
+3567
+3568
+3569
+3570
+3571
+3572
+3573
+3574
+3575
+3576
+3577
+3578
+3579
+3580
+3581
+3582
+3583
+3584
+3585
+3586
+3587
+3588
+3589
+3590
+3591
+3592
+3593
+3594
+3595
+3596
+3597
+3598
+3599
+3600
+3601
+3602
+3603
+3604
+3605
+3606
+3607
+3608
+3609
+3610
+3611
+3612
+3613
+3614
+3615
+3616
+3617
+3618
+3619
+3620
+3621
+3622
+3623
+3624
+3625
+3626
+3627
+3628
+3629
+3630
+3631
+3632
+3633
+3634
+3635
+3636
+3637
+3638
+3639
+3640
+3641
+3642
+3643
+3644
+3645
+3646
+3647
+3648
+3649
+3650
+3651
+3652
+3653
+3654
+3655
+3656
+3657
+3658
+3659
+3660
+3661
+3662
+3663
+3664
+3665
+3666
+3667
+3668
+3669
+3670
+3671
+3672
+3673
+3674
+3675
+3676
+3677
+3678
+3679
+3680
+3681
+3682
+3683
+3684
+3685
+3686
+3687
+3688
+3689
+3690
+3691
+3692
+3693
+3694
+3695
+3696
+3697
+3698
+3699
+3700
+3701
+3702
+3703
+3704
+3705
+3706
+3707
+3708
+3709
+3710
+3711
+3712
+3713
+3714
+3715
+3716
+3717
+3718
+3719
+3720
+3721
+3722
+3723
+3724
+3725
+3726
+3727
+3728
+3729
+3730
+3731
+3732
+3733
+3734
+3735
+3736
+3737
+3738
+3739
+3740
+3741
+3742
+3743
+3744
+3745
+3746
+3747
+3748
+3749
+3750
+3751
+3752
+3753
+3754
+3755
+3756
+3757
+3758
+3759
+3760
+3761
+3762
+3763
+3764
+3765
+3766
+3767
+3768
+3769
+3770
+3771
+3772
+3773
+3774
+3775
+3776
+3777
+3778
+3779
+3780
+3781
+3782
+3783
+3784
+3785
+3786
+3787
+3788
+3789
+3790
+3791
+3792
+3793
+3794
+3795
+3796
+3797
+3798
+3799
+3800
+3801
+3802
+3803
+3804
+3805
+3806
+3807
+3808
+3809
+3810
+3811
+3812
+3813
+3814
+3815
+3816
+3817
+3818
+3819
+3820
+3821
+3822
+3823
+3824
+3825
+3826
+3827
+3828
+3829
+3830
+3831
+3832
+3833
+3834
+3835
+3836
+3837
+3838
+3839
+3840
+3841
+3842
+3843
+3844
+3845
+3846
+3847
+3848
+3849
+3850
+3851
+3852
+3853
+3854
+3855
+3856
+3857
+3858
+3859
+3860
+3861
+3862
+3863
+3864
+3865
+3866
+3867
+3868
+3869
+3870
+3871
+3872
+3873
+3874
+3875
+3876
+3877
+3878
+3879
+3880
+3881
+3882
+3883
+3884
+3885
+3886
+3887
+3888
+3889
+3890
+3891
+3892
+3893
+3894
+3895
+3896
+3897
+3898
+3899
+3900
+3901
+3902
+3903
+3904
+3905
+3906
+3907
+3908
+3909
+3910
+3911
+3912
+3913
+3914
+3915
+3916
+3917
+3918
+3919
+3920
+3921
+3922
+3923
+3924
+3925
+3926
+3927
+3928
+3929
+3930
+3931
+3932
+3933
+3934
+3935
+3936
+3937
+3938
+3939
+3940
+3941
+3942
+3943
+3944
+3945
+3946
+3947
+3948
+3949
+3950
+3951
+3952
+3953
+3954
+3955
+3956
+3957
+3958
+3959
+3960
+3961
+3962
+3963
+3964
+3965
+3966
+3967
+3968
+3969
+3970
+3971
+3972
+3973
+3974
+3975
+3976
+3977
+3978
+3979
+3980
+3981
+3982
+3983
+3984
+3985
+3986
+3987
+3988
+3989
+3990
+3991
+3992
+3993
+3994
+3995
+3996
+3997
+3998
+3999
+4000
+4001
+4002
+4003
+4004
+4005
+4006
+4007
+4008
+4009
+4010
+4011
+4012
+4013
+4014
+4015
+4016
+4017
+4018
+4019
+4020
+4021
+4022
+4023
+4024
+4025
+4026
+4027
+4028
+4029
+4030
+4031
+4032
+4033
+4034
+4035
+4036
+4037
+4038
+4039
+4040
+4041
+4042
+4043
+4044
+4045
+4046
+4047
+4048
+4049
+4050
+4051
+4052
+4053
+4054
+4055
+4056
+4057
+4058
+4059
+4060
+4061
+4062
+4063
+4064
+4065
+4066
+4067
+4068
+4069
+4070
+4071
+4072
+4073
+4074
+4075
+4076
+4077
+4078
+4079
+4080
+4081
+4082
+4083
+4084
+4085
+4086
+4087
+4088
+4089
+4090
+4091
+4092
+4093
+4094
+4095
+4096
+4097
+4098
+4099
+4100
+4101
+4102
+4103
+4104
+4105
+4106
+4107
+4108
+4109
+4110
+4111
+4112
+4113
+4114
+4115
+4116
+4117
+4118
+4119
+4120
+4121
+4122
+4123
+4124
+4125
+4126
+4127
+4128
+4129
+4130
+4131
+4132
+4133
+4134
+4135
+4136
+4137
+4138
+4139
+4140
+4141
+4142
+4143
+4144
+4145
+4146
+4147
+4148
+4149
+4150
+4151
+4152
+4153
+4154
+4155
+4156
+4157
+4158
+4159
+4160
+4161
+4162
+4163
+4164
+4165
+4166
+4167
+4168
+4169
+4170
+4171
+4172
+4173
+4174
+4175
+4176
+4177
+4178
+4179
+4180
+4181
+4182
+4183
+4184
+4185
+4186
+4187
+4188
+4189
+4190
+4191
+4192
+4193
+4194
+4195
+4196
+4197
+4198
+4199
+4200
+4201
+4202
+4203
+4204
+4205
+4206
+4207
+4208
+4209
+4210
+4211
+4212
+4213
+4214
+4215
+4216
+4217
+4218
+4219
+4220
+4221
+4222
+4223
+4224
+4225
+4226
+4227
+4228
+4229
+4230
+4231
+4232
+4233
+4234
+4235
+4236
+4237
+4238
+4239
+4240
+4241
+4242
+4243
+4244
+4245
+4246
+4247
+4248
+4249
+4250
+4251
+4252
+4253
+4254
+4255
+4256
+4257
+4258
+4259
+4260
+4261
+4262
+4263
+4264
+4265
+4266
+4267
+4268
+4269
+4270
+4271
+4272
+4273
+4274
+4275
+4276
+4277
+4278
+4279
+4280
+4281
+4282
+4283
+4284
+4285
+4286
+4287
+4288
+4289
+4290
+4291
+4292
+4293
+4294
+4295
+4296
+4297
+4298
+4299
+4300
+4301
+4302
+4303
+4304
+4305
+4306
+4307
+4308
+4309
+4310
+4311
+4312
+4313
+4314
+4315
+4316
+4317
+4318
+4319
+4320
+4321
+4322
+4323
+4324
+4325
+4326
+4327
+4328
+4329
+4330
+4331
+4332
+4333
+4334
+4335
+4336
+4337
+4338
+4339
+4340
+4341
+4342
+4343
+4344
+4345
+4346
+4347
+4348
+4349
+4350
+4351
+4352
+4353
+4354
+4355
+4356
+4357
+4358
+4359
+4360
+4361
+4362
+4363
+4364
+4365
+4366
+4367
+4368
+4369
+4370
+4371
+4372
+4373
+4374
+4375
+4376
+4377
+4378
+4379
+4380
+4381
+4382
+4383
+4384
+4385
+4386
+4387
+4388
+4389
+4390
+4391
+4392
+4393
+4394
+4395
+4396
+4397
+4398
+4399
+4400
+4401
+4402
+4403
+4404
+4405
+4406
+4407
+4408
+4409
+4410
+4411
+4412
+4413
+4414
+4415
+4416
+4417
+4418
+4419
+4420
+4421
+4422
+4423
+4424
+4425
+4426
+4427
+4428
+4429
+4430
+4431
+4432
+4433
+4434
+4435
+4436
+4437
+4438
+4439
+4440
+4441
+4442
+4443
+4444
+4445
+4446
+4447
+4448
+4449
+4450
+4451
+4452
+4453
+4454
+4455
+4456
+4457
+4458
+4459
+4460
+4461
+4462
+4463
+4464
+4465
+4466
+4467
+4468
+4469
+4470
+4471
+4472
+4473
+4474
+4475
+4476
+4477
+4478
+4479
+4480
+4481
+4482
+4483
+4484
+4485
+4486
+4487
+4488
+4489
+4490
+4491
+4492
+4493
+4494
+4495
+4496
+4497
+4498
+4499
+4500
+4501
+4502
+4503
+4504
+4505
+4506
+4507
+4508
+4509
+4510
+4511
+4512
+4513
+4514
+4515
+4516
+4517
+4518
+4519
+4520
+4521
+4522
+4523
+4524
+4525
+4526
+4527
+4528
+4529
+4530
+4531
+4532
+4533
+4534
+4535
+4536
+4537
+4538
+4539
+4540
+4541
+4542
+4543
+4544
+4545
+4546
+4547
+4548
+4549
+4550
+4551
+4552
+4553
+4554
+4555
+4556
+4557
+4558
+4559
+4560
+4561
+4562
+4563
+4564
+4565
+4566
+4567
+4568
+4569
+4570
+4571
+4572
+4573
+4574
+4575
+4576
+4577
+4578
+4579
+4580
+4581
+4582
+4583
+4584
+4585
+4586
+4587
+4588
+4589
+4590
+4591
+4592
+4593
+4594
+4595
+4596
+4597
+4598
+4599
+4600
+4601
+4602
+4603
+4604
+4605
+4606
+4607
+4608
+4609
+4610
+4611
+4612
+4613
+4614
+4615
+4616
+4617
+4618
+4619
+4620
+4621
+4622
+4623
+4624
+4625
+4626
+4627
+4628
+4629
+4630
+4631
+4632
+4633
+4634
+4635
+4636
+4637
+4638
+4639
+4640
+4641
+4642
+4643
+4644
+4645
+4646
+4647
+4648
+4649
+4650
+4651
+4652
+4653
+4654
+4655
+4656
+4657
+4658
+4659
+4660
+4661
+4662
+4663
+4664
+4665
+4666
+4667
+4668
+4669
+4670
+4671
+4672
+4673
+4674
+4675
+4676
+4677
+4678
+4679
+4680
+4681
+4682
+4683
+4684
+4685
+4686
+4687
+4688
+4689
+4690
+4691
+4692
+4693
+4694
+4695
+4696
+4697
+4698
+4699
+4700
+4701
+4702
+4703
+4704
+4705
+4706
+4707
+4708
+4709
+4710
+4711
+4712
+4713
+4714
+4715
+4716
+4717
+4718
+4719
+4720
+4721
+4722
+4723
+4724
+4725
+4726
+4727
+4728
+4729
+4730
+4731
+4732
+4733
+4734
+4735
+4736
+4737
+4738
+4739
+4740
+4741
+4742
+4743
+4744
+4745
+4746
+4747
+4748
+4749
+4750
+4751
+4752
+4753
+4754
+4755
+4756
+4757
+4758
+4759
+4760
+4761
+4762
+4763
+4764
+4765
+4766
+4767
+4768
+4769
+4770
+4771
+4772
+4773
+4774
+4775
+4776
+4777
+4778
+4779
+4780
+4781
+4782
+4783
+4784
+4785
+4786
+4787
+4788
+4789
+4790
+4791
+4792
+4793
+4794
+4795
+4796
+4797
+4798
+4799
+4800
+4801
+4802
+4803
+4804
+4805
+4806
+4807
+4808
+4809
+4810
+4811
+4812
+4813
+4814
+4815
+4816
+4817
+4818
+4819
+4820
+4821
+4822
+4823
+4824
+4825
+4826
+4827
+4828
+4829
+4830
+4831
+4832
+4833
+4834
+4835
+4836
+4837
+4838
+4839
+4840
+4841
+4842
+4843
+4844
+4845
+4846
+4847
+4848
+4849
+4850
+4851
+4852
+4853
+4854
+4855
+4856
+4857
+4858
+4859
+4860
+4861
+4862
+4863
+4864
+4865
+4866
+4867
+4868
+4869
+4870
+4871
+4872
+4873
+4874
+4875
+4876
+4877
+4878
+4879
+4880
+4881
+4882
+4883
+4884
+4885
+4886
+4887
+4888
+4889
+4890
+4891
+4892
+4893
+4894
+4895
+4896
+4897
+4898
+4899
+4900
+4901
+4902
+4903
+4904
+4905
+4906
+4907
+4908
+4909
+4910
+4911
+4912
+4913
+4914
+4915
+4916
+4917
+4918
+4919
+4920
+4921
+4922
+4923
+4924
+4925
+4926
+4927
+4928
+4929
+4930
+4931
+4932
+4933
+4934
+4935
+4936
+4937
+4938
+4939
+4940
+4941
+4942
+4943
+4944
+4945
+4946
+4947
+4948
+4949
+4950
+4951
+4952
+4953
+4954
+4955
+4956
+4957
+4958
+4959
+4960
+4961
+4962
+4963
+4964
+4965
+4966
+4967
+4968
+4969
+4970
+4971
+4972
+4973
+4974
+4975
+4976
+4977
+4978
+4979
+4980
+4981
+4982
+4983
+4984
+4985
+4986
+4987
+4988
+4989
+4990
+4991
+4992
+4993
+4994
+4995
+4996
+4997
+4998
+4999
+5000
diff --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
index 08f417b9ba451..26b5d5093fa86 100644
--- a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
@@ -23,13 +23,13 @@
 // buffer has to be refilled (underflow) several times, and check that every value
 // is read correctly and that EOF is detected at the end.
 
-// RUN: seq 1 5000 > %t.input
+// FILE_DEPENDENCIES: ../many-ints.dat
 
 // RUN: %{build} -DSYNC_WITH_STDIO_FALSE
-// RUN: %{exec} %t.exe < %t.input
+// RUN: %{exec} %t.exe < %{temp}/many-ints.dat
 
 // RUN: %{build}
-// RUN: %{exec} %t.exe < %t.input
+// RUN: %{exec} %t.exe < %{temp}/many-ints.dat
 
 #include <cassert>
 #include <iostream>
diff --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
index 57113e21535e3..9f96414d02986 100644
--- a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
@@ -25,13 +25,13 @@
 // buffer has to be refilled (underflow) several times, and check that every value
 // is read correctly and that EOF is detected at the end.
 
-// RUN: seq 1 5000 > %t.input
+// FILE_DEPENDENCIES: ../many-ints.dat
 
 // RUN: %{build} -DSYNC_WITH_STDIO_FALSE
-// RUN: %{exec} %t.exe < %t.input
+// RUN: %{exec} %t.exe < %{temp}/many-ints.dat
 
 // RUN: %{build}
-// RUN: %{exec} %t.exe < %t.input
+// RUN: %{exec} %t.exe < %{temp}/many-ints.dat
 
 #include <cassert>
 #include <iostream>


From libcxx-commits at lists.llvm.org  Wed Jul 22 06:15:12 2026
From: libcxx-commits at lists.llvm.org (Aaron Ballman via libcxx-commits)
Date: Wed, 22 Jul 2026 06:15:12 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] Fix vector types w.r.t. qualified
	element type. (PR #197624)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/197624@github.com>
Message-ID: <6a60c260.481afadf.1fc9f0.986e@mx.google.com>


AaronBallman wrote:

> I'd like to see a conclusion to the RFC before spending more time reviewing this. (CC @AaronBallman)

It's on the Clang Area Team's schedule; I'll try to ensure we get to it at the next meeting (Thur Jul 20).

https://github.com/llvm/llvm-project/pull/197624

From libcxx-commits at lists.llvm.org  Wed Jul 22 06:18:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 06:18:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Mark test unsupported in
	clang-24 (PR #209794)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209794@github.com>
Message-ID: <6a60c309.b4a331f0.2642a.c4d8@mx.google.com>


================
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 // UNSUPPORTED: gcc
-// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22, clang-23, apple-clang-17, apple-clang-21
+// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22, clang-23, clang-24, apple-clang-17, apple-clang-21
----------------
huixie90 wrote:

sorry for the delay. yes looks good to me to disable the test.

https://github.com/llvm/llvm-project/pull/209794

From libcxx-commits at lists.llvm.org  Wed Jul 22 06:21:48 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 06:21:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] re-enable clear padding
	runtime test (PR #211258)
Message-ID: <llvm.org/llvm/llvm-project/pull/211258@github.com>


https://github.com/huixie90 created https://github.com/llvm/llvm-project/pull/211258

None

>From 7a273532f7fae784b07b95ce34f474d752fbcaa9 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:20:39 +0100
Subject: [PATCH] [libc++][test] re-enable clear padding runtime test

---
 libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
index f9c910eca1f47..20241fd6596ef 100644
--- a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 // UNSUPPORTED: gcc
-// UNSUPPORTED: clang-21, clang-22, clang-23, clang-24, apple-clang-21
+// UNSUPPORTED: clang-21, clang-22, clang-23, apple-clang-21
 
 // ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-dynamic-class-memaccess
 


From libcxx-commits at lists.llvm.org  Wed Jul 22 06:22:01 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 06:22:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][test-suite][NFC]
 Formatted and moved `transform_view`'s `sentinel.pass.cpp` (PR #211252)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211252@github.com>
Message-ID: <6a60c3f9.bd93e731.2ac69c.6416@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/211252

From libcxx-commits at lists.llvm.org  Wed Jul 22 06:25:23 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 06:25:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update release procedure for
	libc++ & friends (PR #210383)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210383@github.com>
Message-ID: <6a60c4c3.510f576d.33aa7d.1392@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/210383

>From e99095033fef86d92af6aa35bbc049f1ca63077c Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 17 Jul 2026 13:20:04 -0400
Subject: [PATCH 1/2] [libc++] Update release procedure for libc++ & friends

The release procedure implied that some tasks were done by the release
manager, but in practice they are done by the libc++ developers.

Also, mention using the `llvm-premerge-libcxx-release-runners` runner
set on the release branch, which was overlooked in the previous notes.

Other than that, minor reformulations.
---
 libcxx/docs/ReleaseProcedure.rst | 38 ++++++++++++--------------------
 1 file changed, 14 insertions(+), 24 deletions(-)

diff --git a/libcxx/docs/ReleaseProcedure.rst b/libcxx/docs/ReleaseProcedure.rst
index b3f0d129a17fb..2408f9b4b808d 100644
--- a/libcxx/docs/ReleaseProcedure.rst
+++ b/libcxx/docs/ReleaseProcedure.rst
@@ -11,8 +11,6 @@ This page describes the libc++ procedure for that release.
 Prepare the release
 ===================
 
-This is done by the libc++ developers.
-
 It should be finished before the Release managers start branching the new
 release:
 
@@ -37,10 +35,8 @@ release:
 * Make sure all libc++ supported compilers in the CI are updated to their
   latest release.
 
-Branching
-=========
-
-This is done by the LLVM Release managers.
+After the branch is created
+===========================
 
 After branching for an LLVM release:
 
@@ -48,28 +44,22 @@ After branching for an LLVM release:
 2. Update the version number in ``libcxx/docs/conf.py``
 3. Update ``_LIBCPPABI_VERSION`` in ``libcxxabi/include/cxxabi.h``
 4. Update ``_LIBUNWIND_VERSION`` in ``libunwind/include/__libunwind_config.h``
-5. Create a release notes file for the next release from the template
-6. Point to the new release notes file from ``libcxx/docs/ReleaseNotes.rst``
-
-Post branching
-==============
-
-This is done by the libc++ developers.
-
-After branching it takes a couple of days before the new LLVM ToT version is
-available on `<https://apt.llvm.org>`_. Once it is available the pre-commit CI
-can start using the new ToT version. In order to make sure patches can be
-backported to the release branch the oldest compiler is not removed yet.
-
-The section ``Upcoming Deprecations and Removals`` is cleared by the release
-managers. Copy back the items that were in this section.
+5. Create a release notes file for the next release from the previous ones and point to it from
+   ``libcxx/docs/ReleaseNotes.rst``. Remove entries that do not apply anymore, but keep in mind
+   that some entries (such as upcoming deprecations) may still apply, may need rewording and may
+   also require follow up PRs to implement.
+6. Update the set of runners targeted by the CI on the release branch to ``llvm-premerge-libcxx-release-runners``, and
+   make sure that runner set is using the appropriate image. This ensures that the release branch CI keeps working even
+   if the main branch starts using newer images.
+7. It takes a couple of days before the new LLVM ToT version is available on `<https://apt.llvm.org>`_ after a
+   branch is created. Once it is available, the pre-commit CI can be updated to use the new ToT version. In order
+   to make sure patches can be backported to the release branch, we don't remove the oldest compiler yet.
 
 Post release
 ============
 
-This is done by the libc++ developers.
-
-Support for the ToT - 3 version is removed:
+Once the release is done and cherry-picks are not expected, we remove support for the ToT - 3 version Clang.
+We also perform associated cleanups:
 
 - Search for ``LLVM RELEASE`` and address their comments
 - Search for test that have ``UNSUPPORTED`` or ``XFAIL`` for the no longer supported version

>From 84da2b67a82504dec845c14a75fb82ca22e02118 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 22 Jul 2026 09:25:12 -0400
Subject: [PATCH 2/2] Mention compiler explorer instead of apt

---
 libcxx/docs/ReleaseProcedure.rst | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/docs/ReleaseProcedure.rst b/libcxx/docs/ReleaseProcedure.rst
index 2408f9b4b808d..69cc0c340ecc2 100644
--- a/libcxx/docs/ReleaseProcedure.rst
+++ b/libcxx/docs/ReleaseProcedure.rst
@@ -51,8 +51,7 @@ After branching for an LLVM release:
 6. Update the set of runners targeted by the CI on the release branch to ``llvm-premerge-libcxx-release-runners``, and
    make sure that runner set is using the appropriate image. This ensures that the release branch CI keeps working even
    if the main branch starts using newer images.
-7. It takes a couple of days before the new LLVM ToT version is available on `<https://apt.llvm.org>`_ after a
-   branch is created. Once it is available, the pre-commit CI can be updated to use the new ToT version. In order
+7. Update the pre-commit CI to use the new ToT version of Clang available from Compiler Explorer. In order
    to make sure patches can be backported to the release branch, we don't remove the oldest compiler yet.
 
 Post release


From libcxx-commits at lists.llvm.org  Wed Jul 22 06:26:51 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 06:26:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update release procedure for
	libc++ & friends (PR #210383)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210383@github.com>
Message-ID: <6a60c51b.6e01836d.31501d.5e33@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/210383

From libcxx-commits at lists.llvm.org  Wed Jul 22 06:26:52 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 06:26:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a60c51c.db2926be.3d1875.3c15@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From 145920aa6c68ee172dc65111a6a0cfb99d4c9069 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/29] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..11a0b42ea54f5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 899502422570a..4ae0f6a28637a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -32,11 +33,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -52,21 +66,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -102,12 +120,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -126,23 +146,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -152,23 +178,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From 90d997369a9f358f579ae61963226b1f30c81d5a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/29] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 37c38d25012c6..78e27d9ef9781 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From 7a63b7897f421f0d367057c933db66b3ea0f21e1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/29] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 11a0b42ea54f5..caa7f9d1a2535 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 78e27d9ef9781..640f6e5e916ca 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..8bdde44c1cb8c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -895,6 +895,7 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From e6a881f9199428718c9b93a92dcea056d7e84356 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/29] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 4ae0f6a28637a..c496ecf5ac742 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -49,7 +49,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From 13589231818d564e6ba520c405eae011f72f10fe Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/29] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From d3c6b5e3b74e59c2beaf5b3158db90faef36600c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/29] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c496ecf5ac742..c55de19cd4b59 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -148,7 +148,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From 11cedf33d3fe73d5bad5930a8c4f12df33e0eac8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/29] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 7e6410501ecdccbd78ce814b0e4906a60f379df6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/29] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c55de19cd4b59..0cf50d7ebd1ec 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -162,7 +162,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -180,7 +180,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -195,7 +195,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 640f6e5e916ca..44f1229f859b3 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From f666483df06fffa6f94a403ba051621cf0432415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/29] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index caa7f9d1a2535..b6650a314bd40 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0cf50d7ebd1ec..05b22d04962fa 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,29 +30,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 44f1229f859b3..0a6e3d54b5911 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 8bdde44c1cb8c..f038c9d39a27b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -884,6 +884,7 @@ module std {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -895,7 +896,6 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From f622cc3f082c34d6f0c5cd6e47177664eed48938 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/29] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 1e55b9997f985525c33ac424b94de00d91b36bba Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/29] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 0a6e3d54b5911..2f6e0d5dbd20a 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From 996293d9cfb334a462283e783e2fb2af90bb373c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/29] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From f74f609379e9feab234dd430bc758b19d9b1d46b Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/29] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From 8c0b1998421e1c8e214ee2694544bf3f9d8f5f79 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/29] 14

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <cstring>
+#include <type_traits>
 
 struct WithTailPadding {
   int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From f436e600ce39701927e30d0074019afc4cdb581c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 20:42:14 +0100
Subject: [PATCH 15/29] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e4d83e638ca77..0bafdbb1d7ede 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -136,7 +136,7 @@ void test() {
   {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
-
+    std::atomic<T> a;
     T init = make<T>(10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);

>From 4def68f03c3089677bb5206f56f30e367415ecce Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:13:05 +0100
Subject: [PATCH 16/29] ci

---
 libcxx/include/__atomic/support/gcc.h         |  5 ++--
 .../atomics.types.generic/padding.pass.cpp    | 24 ++++++++++-------
 .../atomics.types.generic/padding.pass.cpp    | 27 ++++++++++---------
 .../atomics.types.generic/padding2.pass.cpp   |  3 ---
 4 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 2f6e0d5dbd20a..b162e2c2a63f0 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -50,9 +50,8 @@ struct __cxx_atomic_base_impl {
     std::__clear_padding_if_needed(__a_value);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
   _Tp __a_value;
 };
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index bf594b45ed54f..6f894fe6cb605 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -44,15 +44,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -61,11 +69,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0bafdbb1d7ede..344d08b493e60 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,9 +7,6 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits
@@ -41,15 +38,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -58,11 +63,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
index 4e17471a2461f..1040f342b8206 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -8,9 +8,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-localization
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits

>From eeecefecde068ea1f6e2f8417b52961192ca8bc4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:29:04 +0100
Subject: [PATCH 17/29] CI

---
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding2.pass.cpp   | 273 ------------------
 3 files changed, 2 insertions(+), 275 deletions(-)
 delete mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 6f894fe6cb605..388ab0867fe38 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 344d08b493e60..f1a534a2916d6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
deleted file mode 100644
index 1040f342b8206..0000000000000
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-localization
-
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
-// CAS should work on types with padding bits
-
-#include <atomic>
-#include <cassert>
-#include <cstring>
-#include <type_traits>
-#include <iostream>
-
-struct Foo {
-  int i;
-  char c;
-};
-
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
-
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
-}
-
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
-}
-
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-#ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
-#else
-  (void)f;
-  (void)pad_byte;
-#endif
-}
-
-void test() {
-  std::cerr << "test 1" << std::endl;
-  {
-    // compare_exchange_strong
-    // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
-    std::cerr << "test 2" << std::endl;
-
-    Foo init = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 3" << std::endl;
-    assert_foo_padding(init, 0xBB);
-    std::cerr << "test 4" << std::endl;
-    a.store(init);
-
-    Foo expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 5" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 6" << std::endl;
-
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 7" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 8" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 9" << std::endl;
-    assert(r);
-    std::cerr << "test 10" << std::endl;
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    std::cerr << "test 11" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 42);
-    std::cerr << "test 12" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 13" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 14" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 15" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 16" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 17" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 18" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 19" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 10" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 21" << std::endl;
-    assert(!r);
-    std::cerr << "test 22" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 23" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 24" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 25" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 10);
-    std::cerr << "test 26" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 27" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 28" << std::endl;
-  }
-
-  {
-    // compare_exchange_weak
-    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 29" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 30" << std::endl;
-    a.store(stored);
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 31" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 32" << std::endl;
-
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 33" << std::endl;
-    assert_foo_padding(original_expected, 0xAA);
-    std::cerr << "test 34" << std::endl;
-
-    bool r                  = false;
-    const auto max_attempts = 100;
-    auto current_attempt    = 0;
-    while (!r) {
-      ++current_attempt;
-      std::cerr << "test 35" << std::endl;
-      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      std::cerr << "test 36" << std::endl;
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
-      std::cerr << "test 37" << std::endl;
-      r = a.compare_exchange_weak(expected, new_value);
-      if (r) {
-        std::cerr << "test 38" << std::endl;
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-        std::cerr << "test 39" << std::endl;
-      } else {
-        // Spurious failure: expected is updated to the current atomic value.
-        std::cerr << "test 40" << std::endl;
-        assert(expected.i == 10);
-        std::cerr << "test 41" << std::endl;
-        assert(expected.c == 'a');
-        std::cerr << "test 45" << std::endl;
-        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
-        std::cerr << "test 43" << std::endl;
-      }
-    }
-
-    Foo loaded = a.load();
-    std::cerr << "test 44" << std::endl;
-    assert(loaded.i == 42);
-    std::cerr << "test 45" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 46" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 47" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 48" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 49" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 50" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 51" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 52" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 53" << std::endl;
-
-    bool r = a.compare_exchange_weak(expected, new_value);
-
-    std::cerr << "test 54" << std::endl;
-    assert(!r);
-    std::cerr << "test 55" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 56" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 57" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 58" << std::endl;
-    Foo loaded = a.load();
-    std::cerr << "test 59" << std::endl;
-    assert(loaded.i == 10);
-    std::cerr << "test 60" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 61" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 62" << std::endl;
-  }
-
-  {
-    // Types with unique object representations skip the padding-clearing path.
-    std::atomic<int> a(1);
-    int expected = 1;
-    std::cerr << "test 63" << std::endl;
-    assert(a.compare_exchange_strong(expected, 2));
-    std::cerr << "test 64" << std::endl;
-    assert(expected == 1);
-    std::cerr << "test 65" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 66" << std::endl;
-
-    expected = 3;
-    assert(!a.compare_exchange_strong(expected, 4));
-    std::cerr << "test 67" << std::endl;
-    assert(expected == 2);
-    std::cerr << "test 68" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 69" << std::endl;
-  }
-}
-
-int main(int, char**) {
-  // TODO(LLVM-23): Switch to XFAIL: clang-22
-  std::cerr << "main 1" << std::endl;
-#if __has_builtin(__builtin_clear_padding)
-  std::cerr << "main 2" << std::endl;
-  test();
-  std::cerr << "main 3" << std::endl;
-#endif
-  std::cerr << "main 4" << std::endl;
-  return 0;
-}

>From bc539fb8d58e16f24a5ccb8bdd2351475e7d4391 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:52:15 +0100
Subject: [PATCH 18/29] ci

---
 libcxx/include/__atomic/support/c11.h | 13 ++++++++-----
 libcxx/include/__atomic/support/gcc.h |  9 +++++++--
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 05b22d04962fa..d43a2c67f950a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -30,13 +30,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
 
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b162e2c2a63f0..d2d4919d54614 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,12 +46,17 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
       : __a_value(std::__clear_padding_if_needed(__value)) {}
+
   _Tp __a_value;
 };
 

>From 30a056210700cd8f89b1c0ca6991f272a0ebba43 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:26:08 +0100
Subject: [PATCH 19/29] review

---
 libcxx/include/__atomic/clear_padding.h            | 14 ++++++--------
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 5bc77ff4401ab..f920ef39977b7 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -35,17 +35,16 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, float>>,
            _Not<is_same<_Tp, double>>> {};
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(std::addressof(__obj));
-    }
-  }
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+  return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
+}
+
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
@@ -67,7 +66,6 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 388ab0867fe38..45fb083cccf3f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index f1a534a2916d6..5ffba6ca844c5 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();

>From 9b771605f016811c06b3f600e48f45f3268a5269 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:33:16 +0100
Subject: [PATCH 20/29] release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx20Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..a689379321496 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange (`Github <https://llvm.org/PR76180>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 5539767ab800b..8e394b4bc4b3a 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -31,7 +31,7 @@
 "`P0458R2 <https://wg21.link/P0458R2>`__","Checking for Existence of an Element in Associative Containers","2018-06 (Rapperswil)","|Complete|","13","`#104029 <https://github.com/llvm/llvm-project/issues/104029>`__",""
 "`P0475R1 <https://wg21.link/P0475R1>`__","LWG 2511: guaranteed copy elision for piecewise construction","2018-06 (Rapperswil)","|Complete|","","`#104031 <https://github.com/llvm/llvm-project/issues/104031>`__",""
 "`P0476R2 <https://wg21.link/P0476R2>`__","Bit-casting object representations","2018-06 (Rapperswil)","|Complete|","14","`#104087 <https://github.com/llvm/llvm-project/issues/104087>`__",""
-"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","","","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
+"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","|Complete|","24","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
 "`P0542R5 <https://wg21.link/P0542R5>`__","Support for contract based programming in C++","2018-06 (Rapperswil)","|Nothing To Do|","n/a","`#104089 <https://github.com/llvm/llvm-project/issues/104089>`__","Pulled at the 2019-07 meeting in Cologne"
 "`P0556R3 <https://wg21.link/P0556R3>`__","Integral power-of-2 operations","2018-06 (Rapperswil)","|Complete|","9","`#104090 <https://github.com/llvm/llvm-project/issues/104090>`__",""
 "`P0619R4 <https://wg21.link/P0619R4>`__","Reviewing Deprecated Facilities of C++17 for C++20","2018-06 (Rapperswil)","|Complete|","20","`#99985 <https://github.com/llvm/llvm-project/issues/99985>`__","Removed headers are still provided as an extension, but with deprecation warnings."

>From 46a4ba35dbc30153f7868e9796d44f88512ae602 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 17:01:57 +0100
Subject: [PATCH 21/29] ci

---
 libcxx/include/__atomic/clear_padding.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index f920ef39977b7..3099faa8ca6d1 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/has_unique_object_representation.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>

>From 5f475333df1aeee9640364c9fb2f734c731edb7e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 19:03:39 +0100
Subject: [PATCH 22/29] ci

---
 .../atomics.types.generic/padding.pass.cpp    | 19 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 52 ++++++++++++-------
 2 files changed, 44 insertions(+), 27 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 45fb083cccf3f..116a860b3192c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -57,11 +57,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -86,7 +84,8 @@ void test() {
 
   {
     // atomic(T);
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     std::atomic<T> a(init);
     T loaded = a.load();
@@ -97,7 +96,8 @@ void test() {
   {
     // atomic::store
     std::atomic<T> a;
-    T value = make<T>(5, 'x', 0xAB);
+    T value;
+    initialize(value, 5, 'x', 0xAB);
     assert_padding(value, 0xAB);
     a.store(value);
     T loaded = a.load();
@@ -107,10 +107,12 @@ void test() {
   }
   {
     // atomic::exchange
-    T initial = make<T>(1, 'a', 0x00);
+    T initial;
+    initialize(initial, 1, 'a', 0x00);
     assert_padding(initial, 0x00);
     std::atomic<T> a(initial);
-    T new_val = make<T>(2, 'b', 0xCD);
+    T new_val;
+    initialize(new_val, 2, 'b', 0xCD);
     assert_padding(new_val, 0xCD);
     T old = a.exchange(new_val);
     assert(old.i == 1);
@@ -124,7 +126,8 @@ void test() {
   {
     // atomic_init
     std::atomic<T> a;
-    T init = make<T>(7, 'z', 0xEF);
+    T init;
+    initialize(init, 7, 'z', 0xEF);
     assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
     T loaded = a.load();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 5ffba6ca844c5..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -51,11 +51,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -84,17 +82,20 @@ void test() {
     // CAS should succeed when only padding differs in expected; expected is unchanged.
     std::atomic<T> a;
 
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0xAA);
+    T expected;
+    initialize(expected, 10, 'a', 0xAA);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -112,13 +113,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -138,17 +142,20 @@ void test() {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
     std::atomic<T> a;
-    T init = make<T>(10, 'a', 0x00);
+    T init;
+    initialize(init, 10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0x00);
+    T expected;
+    initialize(expected, 10, 'a', 0x00);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0x42);
+    T new_value;
+    initialize(new_value, 42, 'b', 0x42);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,14 +173,17 @@ void test() {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
-    T original_expected = make<T>(10, 'a', 0xAA);
+    T original_expected;
+    initialize(original_expected, 10, 'a', 0xAA);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -182,7 +192,8 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      T expected = make<T>(10, 'a', 0xAA);
+      T expected;
+      initialize(expected, 10, 'a', 0xAA);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -207,13 +218,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);

>From 704a54eb4f1f7989931b7e25ad87abbb60ad6021 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:07:26 +0100
Subject: [PATCH 23/29] debug

---
 .../atomics.types.generic/padding.pass.cpp    | 64 ++++++++++++++++++-
 1 file changed, 62 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..3b7f13b366e96 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,6 +15,7 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
+#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -70,11 +71,19 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
+void print_memory(const T& obj) {
+  for (size_t i = 0; i < sizeof(T); ++i) {
+    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
+  }
+  std::cerr << std::endl;
+}
+
 template <class T>
 void test() {
   {
@@ -84,11 +93,15 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
+    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
+    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -96,6 +109,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -106,6 +121,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test1 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -115,14 +132,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -131,11 +154,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test2 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test2 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -144,11 +171,15 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
+    std::cerr << "test3 init padding should be 0x00: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
+    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -156,6 +187,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
+    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,6 +199,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test3 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -175,15 +210,21 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
+    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
+    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -194,6 +235,8 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
+      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
+      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -203,6 +246,8 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        std::cerr << "test4 expected padding should be 0: " << std::endl;
+        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -211,6 +256,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test4 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -220,14 +267,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -236,11 +289,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test5 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test5 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -262,10 +319,13 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
+  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
+  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
+  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 0;
+  return 1;
 }

>From 8c622e41d2428f630855f0aaa215b1463d8fe4ca Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:38:19 +0100
Subject: [PATCH 24/29] Revert "debug"

This reverts commit 8be9a83ee040faff62f0756555890484bf502a31.
---
 .../atomics.types.generic/padding.pass.cpp    | 64 +------------------
 1 file changed, 2 insertions(+), 62 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 3b7f13b366e96..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,7 +15,6 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
-#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -71,19 +70,11 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)obj;
+  (void)f;
   (void)pad_byte;
 #endif
 }
 
-template <class T>
-void print_memory(const T& obj) {
-  for (size_t i = 0; i < sizeof(T); ++i) {
-    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
-  }
-  std::cerr << std::endl;
-}
-
 template <class T>
 void test() {
   {
@@ -93,15 +84,11 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
-    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
-    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -109,8 +96,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -121,8 +106,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test1 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -132,20 +115,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -154,15 +131,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test2 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test2 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -171,15 +144,11 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
-    std::cerr << "test3 init padding should be 0x00: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
-    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -187,8 +156,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
-    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -199,8 +166,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test3 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -210,21 +175,15 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
-    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
-    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -235,8 +194,6 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
-      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
-      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -246,8 +203,6 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        std::cerr << "test4 expected padding should be 0: " << std::endl;
-        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -256,8 +211,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test4 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -267,20 +220,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -289,15 +236,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test5 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test5 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -319,13 +262,10 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
-  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
-  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
-  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 1;
+  return 0;
 }

>From 789912ba84ccbb3a3efea236fde31835783a7415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:44:00 +0100
Subject: [PATCH 25/29] test ci

---
 .../atomics.types.generic/padding.pass.cpp    | 20 ++++++++++++++-----
 .../atomics.types.generic/padding.pass.cpp    | 12 +----------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 116a860b3192c..cd41f2893864c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -71,6 +71,16 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
+void assert_padding(const std::atomic<T>& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  T loaded     = obj.load();
+  set(reference, loaded.i, loaded.c);
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
+}
+
 template <class T>
 void test() {
   {
@@ -79,7 +89,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 
   {
@@ -91,7 +101,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::store
@@ -103,7 +113,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::exchange
@@ -121,7 +131,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic_init
@@ -133,7 +143,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..10c15523cd70b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -70,7 +70,7 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
@@ -105,8 +105,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -135,8 +133,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
   {
     // compare_exchange_strong
@@ -165,8 +161,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -210,8 +204,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -240,8 +232,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {

>From 81770cda9f2171565c1c2980ad8306b9665752b2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:18:49 +0100
Subject: [PATCH 26/29] padding

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index cd41f2893864c..a178ce74578ec 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -127,7 +127,6 @@ void test() {
     T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_padding(old, 0);
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');

>From b90bec87725f4bde5722a3aa857a5c9d22c275e5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:40:31 +0100
Subject: [PATCH 27/29] clang22

---
 .../libcxx/atomics/atomics.types.generic/padding.pass.cpp    | 5 ++---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 4 +---
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index a178ce74578ec..48ae68a8e97ba 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -147,11 +148,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
+
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 10c15523cd70b..dc0afa439dc12 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
@@ -250,12 +251,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
 
   return 0;
 }

>From 05729850c8bf62df54a60b12201ac475e5c55fb5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 13:26:21 +0100
Subject: [PATCH 28/29] cxx 03

---
 libcxx/include/__atomic/clear_padding.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 3099faa8ca6d1..dad48ed27e6a4 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -32,23 +32,23 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
-           _Not<is_same<_Tp, float>>,
-           _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)> >,
+           _Not<is_same<_Tp, float> >,
+           _Not<is_same<_Tp, double> > > {};
 
-template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   return __obj;
 }
 
-template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp> >::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);

>From 2d00c0b818d180cc4d991508be2ad3dd8b7ee878 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:26:21 +0100
Subject: [PATCH 29/29] ci

---
 libcxx/include/__atomic/clear_padding.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index dad48ed27e6a4..1a4223fd97763 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -37,12 +37,12 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double> > > {};
 
 template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
   return __obj;
 }
 
 template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 


From libcxx-commits at lists.llvm.org  Wed Jul 22 06:37:03 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 06:37:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] adding yield in the lost
	wakeup test (PR #211261)
Message-ID: <llvm.org/llvm/llvm-project/pull/211261@github.com>


https://github.com/huixie90 created https://github.com/llvm/llvm-project/pull/211261

None

>From 7a4c60d7dd50a166679ba9aa694589b64058d753 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:36:40 +0100
Subject: [PATCH] [libc++][test] adding yield in the lost wakeup test

---
 .../atomics.types.operations.wait/lost_wakeup.pass.cpp           | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
index 17ad72dd74866..fc96ea1a8f201 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
@@ -39,6 +39,7 @@ int main(int, char**) {
     auto notify = [&] {
       for (int i = 0; i < num_iterations; ++i) {
         while (waiter_ready.load() < num_waiters) {
+          std::this_thread::yield();
         }
         waiter_ready.store(0);
         state.fetch_add(1);


From libcxx-commits at lists.llvm.org  Wed Jul 22 06:40:17 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 06:40:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxxabi] [libunwind] [runtimes] LLVM
 24 version bumps (PR #211262)
Message-ID: <llvm.org/llvm/llvm-project/pull/211262@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/211262

This patch bumps the version of libc++abi and libunwind, and libc++ in the documentation.

>From 710a8ff51a0131f6d691a55e427132ca989c4942 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 22 Jul 2026 09:39:23 -0400
Subject: [PATCH] [runtimes] LLVM 24 version bumps

This patch bumps the version of libc++abi and libunwind, and
libc++ in the documentation.
---
 libcxx/docs/conf.py                    | 4 ++--
 libcxxabi/include/cxxabi.h             | 2 +-
 libunwind/include/__libunwind_config.h | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/conf.py b/libcxx/docs/conf.py
index 9051bce7cf356..98aa52edd5d2e 100644
--- a/libcxx/docs/conf.py
+++ b/libcxx/docs/conf.py
@@ -32,9 +32,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = "23.0"
+version = "24.0"
 # The full version, including alpha/beta/rc tags.
-release = "23.0"
+release = "24.0"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/libcxxabi/include/cxxabi.h b/libcxxabi/include/cxxabi.h
index c65e353d68daf..66301d308282d 100644
--- a/libcxxabi/include/cxxabi.h
+++ b/libcxxabi/include/cxxabi.h
@@ -19,7 +19,7 @@
 
 #include <__cxxabi_config.h>
 
-#define _LIBCPPABI_VERSION 230000
+#define _LIBCPPABI_VERSION 240000
 #define _LIBCXXABI_ALWAYS_COLD __attribute__((cold))
 
 #ifdef __cplusplus
diff --git a/libunwind/include/__libunwind_config.h b/libunwind/include/__libunwind_config.h
index a1179c73c5906..0c7aed13375a1 100644
--- a/libunwind/include/__libunwind_config.h
+++ b/libunwind/include/__libunwind_config.h
@@ -9,7 +9,7 @@
 #ifndef ____LIBUNWIND_CONFIG_H__
 #define ____LIBUNWIND_CONFIG_H__
 
-#define _LIBUNWIND_VERSION 230000
+#define _LIBUNWIND_VERSION 240000
 
 #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \
     !defined(__ARM_DWARF_EH__) && !defined(__SEH__)


From libcxx-commits at lists.llvm.org  Wed Jul 22 06:42:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 06:42:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] re-enable clear padding
	runtime test (PR #211258)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211258@github.com>
Message-ID: <6a60c8a9.b922cc60.29d941.ca0a@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/211258

>From 7a273532f7fae784b07b95ce34f474d752fbcaa9 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:20:39 +0100
Subject: [PATCH 1/2] [libc++][test] re-enable clear padding runtime test

---
 libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
index f9c910eca1f47..20241fd6596ef 100644
--- a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 // UNSUPPORTED: gcc
-// UNSUPPORTED: clang-21, clang-22, clang-23, clang-24, apple-clang-21
+// UNSUPPORTED: clang-21, clang-22, clang-23, apple-clang-21
 
 // ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-dynamic-class-memaccess
 

>From b4f18e4603895f260dd208e115cdee6e54b781d3 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:41:48 +0100
Subject: [PATCH 2/2] clang 23

---
 libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
index 20241fd6596ef..ef418025c17dc 100644
--- a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 // UNSUPPORTED: gcc
-// UNSUPPORTED: clang-21, clang-22, clang-23, apple-clang-21
+// UNSUPPORTED: clang-21, clang-22, apple-clang-21
 
 // ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-dynamic-class-memaccess
 


From libcxx-commits at lists.llvm.org  Wed Jul 22 06:54:21 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 06:54:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges][test-suite][NFC]
 Formatted and moved `transform_view`'s `sentinel.pass.cpp` (PR #211252)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211252@github.com>
Message-ID: <6a60cb8d.c56fb102.6921f.16b8@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

<details>
<summary>Changes</summary>

As a pre-requisite to: https://github.com/llvm/llvm-project/pull/193891

---
Full diff: https://github.com/llvm/llvm-project/pull/211252.diff


1 Files Affected:

- (renamed) libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp (+6-6) 


``````````diff
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp
similarity index 89%
rename from libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp
index ce48e09d6eb8f..b0529916775b3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/sentinel.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/sentinel.pass.cpp
@@ -16,7 +16,7 @@
 #include "test_macros.h"
 #include "../types.h"
 
-template<class T>
+template <class T>
 concept EndIsIter = requires(T t) { ++t.end(); };
 
 constexpr bool test() {
@@ -33,7 +33,7 @@ constexpr bool test() {
 
   std::ranges::transform_view transformView2(SizedSentinelView{4}, PlusOne());
   auto sent4 = transformView2.end();
-  auto iter = transformView1.begin();
+  auto iter  = transformView1.begin();
   {
     assert(iter != sent1);
     assert(iter != sent2);
@@ -47,10 +47,10 @@ constexpr bool test() {
   }
 
   {
-    assert(sent1 - iter  == 8);
-    assert(sent4 - iter  == 4);
-    assert(iter  - sent1 == -8);
-    assert(iter  - sent4 == -4);
+    assert(sent1 - iter == 8);
+    assert(sent4 - iter == 4);
+    assert(iter - sent1 == -8);
+    assert(iter - sent4 == -4);
   }
 
   return true;

``````````

</details>


https://github.com/llvm/llvm-project/pull/211252

From libcxx-commits at lists.llvm.org  Wed Jul 22 06:55:54 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 06:55:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Destroy elements when exceptions
 are thrown in __construct_at_end (PR #167112)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/167112@github.com>
Message-ID: <6a60cbea.1e1ac41d.317291.3029@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/167112

>From 65cb17f3d14f30626b1c2690c8b9f56713d25b49 Mon Sep 17 00:00:00 2001
From: kisuhorikka <kisuhorikka at gmail.com>
Date: Mon, 15 Dec 2025 21:19:32 +0800
Subject: [PATCH 1/3] [libc++] Destroy elements when exceptions are thrown in
 __construct_at_end

---
 .../__memory/uninitialized_algorithms.h       | 26 +++++++++++++++++++
 libcxx/include/__vector/vector.h              | 10 ++-----
 2 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 34d065dc973e5..09a1b43e00f17 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -124,6 +124,18 @@ uninitialized_fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp&
 
 // uninitialized_fill_n
 
+template <class _Alloc, class _ForwardIterator, class _Size, class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _ForwardIterator
+__uninitialized_allocator_fill_n(_Alloc& __alloc, _ForwardIterator __first, _Size __n, const _Tp& __x) {
+  _ForwardIterator __idx = __first;
+  auto __guard           = std::__make_exception_guard([&] { std::__destroy(__first, __idx); });
+  for (; __n > 0; ++__idx, (void)--__n)
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__idx), __x);
+  __guard.__complete();
+
+  return __idx;
+}
+
 template <class _ValueType, class _ForwardIterator, class _Size, class _Tp>
 inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator
 __uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) {
@@ -143,6 +155,20 @@ uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) {
   return std::__uninitialized_fill_n<_ValueType>(__first, __n, __x);
 }
 
+// __uninitialized_allocator_value_construct_n
+
+template <class _Alloc, class _ForwardIterator, class _Size>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _ForwardIterator
+__uninitialized_allocator_value_construct_n(_Alloc& __alloc, _ForwardIterator __first, _Size __n) {
+  auto __idx   = __first;
+  auto __guard = std::__make_exception_guard([&] { std::__destroy(__first, __idx); });
+  for (; __n > 0; ++__idx, (void)--__n)
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__idx));
+  __guard.__complete();
+
+  return __idx;
+}
+
 #if _LIBCPP_STD_VER >= 17
 
 // uninitialized_default_construct
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 316d3a9d10eff..14c387414518e 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -941,10 +941,7 @@ vector<_Tp, _Allocator>::__recommend(size_type __new_size) const {
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__construct_at_end(size_type __n) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__alloc_, std::__to_address(__pos));
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_value_construct_n(this->__alloc_, this->__end_, __n);
 }
 
 //  Copy constructs __n objects starting at __end_ from __x
@@ -957,10 +954,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline void
 vector<_Tp, _Allocator>::__construct_at_end(size_type __n, const_reference __x) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__alloc_, std::__to_address(__pos), __x);
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_fill_n(this->__alloc_, this->__end_, __n, __x);
 }
 
 template <class _Tp, class _Allocator>

>From 3ed114d1682c5f86d792207703b15e12dc0becc4 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 18 Jun 2026 17:27:53 -0400
Subject: [PATCH 2/3] [libc++] Add tests for element destruction in
 __construct_at_end exceptions

---
 .../std/containers/sequences/vector/common.h  | 22 +++++
 .../resize_size_exceptions.pass.cpp           | 81 ++++++++++++++++++
 .../resize_size_value_exceptions.pass.cpp     | 85 +++++++++++++++++++
 .../vector.cons/assign_size_value.pass.cpp    | 34 ++++++++
 .../insert_iter_size_value.pass.cpp           | 34 ++++++++
 5 files changed, 256 insertions(+)

diff --git a/libcxx/test/std/containers/sequences/vector/common.h b/libcxx/test/std/containers/sequences/vector/common.h
index 34453f8889b73..768d7f03fd703 100644
--- a/libcxx/test/std/containers/sequences/vector/common.h
+++ b/libcxx/test/std/containers/sequences/vector/common.h
@@ -110,6 +110,28 @@ struct move_only_throwing_t {
 
 #endif
 
+// A type whose default constructor throws after a configurable number of
+// successful constructions. Useful for tests that need to value construct
+// objects and ensure exception safety when construction throws.
+//
+// Has a non-throwing (const T&) constructor for setting up the vector with
+// known content before the test starts.
+template <typename T>
+struct throwing_default_t {
+  T data_;
+  static int throw_after;
+
+  throwing_default_t() : data_() {
+    if (throw_after == 0)
+      throw 0;
+    --throw_after;
+  }
+
+  throwing_default_t(const T& data) : data_(data) {}
+};
+template <typename T>
+int throwing_default_t<T>::throw_after = 0;
+
 template <typename T>
 struct throwing_data {
   T data_;
diff --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp
index b5e12ea8e5d0a..33b8ace5ec5cc 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp
@@ -95,6 +95,36 @@ void test_copy_ctor_exception_for_strong_guarantee(std::vector<throwing_data<T>,
   }
 }
 
+// Test the no-reallocation path of resize(n): reserve enough capacity ahead
+// of time so resize fits within it, and trigger the throw partway through
+// constructing the new elements.
+template <typename T, typename Alloc>
+void test_in_place_default_ctor_exception_for_strong_guarantee(
+    std::vector<throwing_default_t<T>, Alloc>& v, const std::vector<T>& values) {
+  assert(v.empty() && !values.empty());
+  std::size_t new_size = 2 * values.size();
+  v.reserve(new_size);
+  for (std::size_t i = 0; i < values.size(); ++i)
+    v.emplace_back(values[i]);
+
+  throwing_default_t<T>* old_data = v.data();
+  std::size_t old_size            = v.size();
+  std::size_t old_cap             = v.capacity();
+
+  // Trigger an exception halfway through new-element default construction.
+  throwing_default_t<T>::throw_after = static_cast<int>(values.size() / 2);
+
+  try {
+    v.resize(new_size);
+  } catch (...) {
+    assert(v.data() == old_data);
+    assert(v.size() == old_size);
+    assert(v.capacity() == old_cap);
+    for (std::size_t i = 0; i < v.size(); ++i)
+      assert(v[i].data_ == values[i]);
+  }
+}
+
 #if TEST_STD_VER >= 11
 
 template <typename T, typename Alloc>
@@ -287,6 +317,56 @@ void test_default_ctor_exceptions() {
     test_default_ctor_exception_for_strong_guarantee(v, in);
   }
   check_new_delete_called();
+
+  // Same per-allocator coverage but for the no-reallocation path of resize(n).
+  // See https://llvm.org/PR59293.
+  {
+    {
+      int a[] = {1, 2, 3, 4, 5};
+      std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
+      std::vector<throwing_default_t<int> > v;
+      test_in_place_default_ctor_exception_for_strong_guarantee(v, in);
+    }
+    check_new_delete_called();
+
+    {
+      int a[] = {1, 2, 3, 4, 5};
+      std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
+      std::vector<throwing_default_t<int>, min_allocator<throwing_default_t<int> > > v;
+      test_in_place_default_ctor_exception_for_strong_guarantee(v, in);
+    }
+    check_new_delete_called();
+
+    {
+      std::vector<int> in(10, 42);
+      std::vector<throwing_default_t<int>, safe_allocator<throwing_default_t<int> > > v;
+      test_in_place_default_ctor_exception_for_strong_guarantee(v, in);
+    }
+    check_new_delete_called();
+
+    {
+      std::vector<int> in(10, 42);
+      std::vector<throwing_default_t<int>, test_allocator<throwing_default_t<int> > > v;
+      test_in_place_default_ctor_exception_for_strong_guarantee(v, in);
+    }
+    check_new_delete_called();
+
+    {
+      std::vector<int> in(10, 42);
+      std::vector<throwing_default_t<int>, limited_allocator<throwing_default_t<int>, 100> > v;
+      test_in_place_default_ctor_exception_for_strong_guarantee(v, in);
+    }
+    check_new_delete_called();
+
+#if TEST_STD_VER >= 23
+    {
+      std::vector<int> in{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+      std::vector<throwing_default_t<int>, increasing_allocator<throwing_default_t<int>>> v;
+      test_in_place_default_ctor_exception_for_strong_guarantee(v, in);
+    }
+    check_new_delete_called();
+#endif
+  }
 }
 
 // Check the strong exception guarantee during copy-constructor failures
@@ -391,4 +471,5 @@ int main(int, char**) {
 #if TEST_STD_VER >= 11
   test_move_ctor_exceptions();
 #endif
+  return 0;
 }
diff --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp
index 7217b47d69e4d..4180155ee9de5 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp
@@ -70,6 +70,40 @@ void test_copy_ctor_exception_for_strong_guarantee(std::vector<throwing_data<T>,
   }
 }
 
+// Test the no-reallocation path of resize(n, x): reserve enough capacity
+// ahead of time so resize fits within it, and trigger the throw partway
+// through copying the new element.
+template <typename T, typename Alloc>
+void test_in_place_copy_ctor_exception_for_strong_guarantee(
+    std::vector<throwing_data<T>, Alloc>& v, const std::vector<T>& values) {
+  assert(v.empty() && !values.empty());
+  std::size_t new_size = 2 * values.size();
+  // Set the existing-elements counter high enough that fill never throws
+  // and existing elements never trigger the throw on later operations.
+  int throw_after_fill = values.size() + 1000;
+  v.reserve(new_size);
+  for (std::size_t i = 0; i < values.size(); ++i)
+    v.emplace_back(values[i], throw_after_fill);
+
+  throwing_data<T>* old_data = v.data();
+  std::size_t old_size       = v.size();
+  std::size_t old_cap        = v.capacity();
+
+  try {
+    // t carries its own counter so the throw fires partway through the new
+    // copies (independent of the existing-elements counter).
+    int n = static_cast<int>(values.size() / 2 + 1);
+    throwing_data<T> t(T(), n);
+    v.resize(new_size, t);
+  } catch (...) {
+    assert(v.data() == old_data);
+    assert(v.size() == old_size);
+    assert(v.capacity() == old_cap);
+    for (std::size_t i = 0; i < v.size(); ++i)
+      assert(v[i].data_ == values[i]);
+  }
+}
+
 // Check the strong exception guarantee during reallocation failures
 void test_allocation_exceptions() {
   //
@@ -223,9 +257,60 @@ void test_copy_ctor_exceptions() {
     test_copy_ctor_exception_for_strong_guarantee(v, in);
   }
   check_new_delete_called();
+
+  // Same per-allocator coverage but for the no-reallocation path of
+  // resize(n, x). See https://llvm.org/PR59293.
+  {
+    {
+      int a[] = {1, 2, 3, 4, 5};
+      std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
+      std::vector<throwing_data<int> > v;
+      test_in_place_copy_ctor_exception_for_strong_guarantee(v, in);
+    }
+    check_new_delete_called();
+
+    {
+      int a[] = {1, 2, 3, 4, 5};
+      std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
+      std::vector<throwing_data<int>, min_allocator<throwing_data<int> > > v;
+      test_in_place_copy_ctor_exception_for_strong_guarantee(v, in);
+    }
+    check_new_delete_called();
+
+    {
+      std::vector<int> in(10, 42);
+      std::vector<throwing_data<int>, safe_allocator<throwing_data<int> > > v;
+      test_in_place_copy_ctor_exception_for_strong_guarantee(v, in);
+    }
+    check_new_delete_called();
+
+    {
+      std::vector<int> in(10, 42);
+      std::vector<throwing_data<int>, test_allocator<throwing_data<int> > > v;
+      test_in_place_copy_ctor_exception_for_strong_guarantee(v, in);
+    }
+    check_new_delete_called();
+
+    {
+      std::vector<int> in(10, 42);
+      std::vector<throwing_data<int>, limited_allocator<throwing_data<int>, 100> > v;
+      test_in_place_copy_ctor_exception_for_strong_guarantee(v, in);
+    }
+    check_new_delete_called();
+
+#if TEST_STD_VER >= 23
+    {
+      std::vector<int> in{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+      std::vector<throwing_data<int>, increasing_allocator<throwing_data<int>>> v;
+      test_in_place_copy_ctor_exception_for_strong_guarantee(v, in);
+    }
+    check_new_delete_called();
+#endif
+  }
 }
 
 int main(int, char**) {
   test_allocation_exceptions();
   test_copy_ctor_exceptions();
+  return 0;
 }
diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/assign_size_value.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/assign_size_value.pass.cpp
index 8526cf587ed3f..239a0bd8ee37b 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/assign_size_value.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/assign_size_value.pass.cpp
@@ -17,6 +17,8 @@
 #include "test_macros.h"
 #include "min_allocator.h"
 #include "asan_testing.h"
+#include "../common.h"
+#include "count_new.h"
 
 TEST_CONSTEXPR bool is6(int x) { return x == 6; }
 
@@ -62,5 +64,37 @@ int main(int, char**) {
 #if TEST_STD_VER > 17
   static_assert(tests());
 #endif
+
+  // Verify that assign(n, x) destroys partially-constructed elements when a
+  // later element's copy constructor throws.
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  {
+    // Without reallocation (note the reserve call)
+    int throw_after = 4; // x consumes 1, then 3 successful copies, 4th throws
+    std::vector<throwing_t> v;
+    v.reserve(10);
+    try {
+      throwing_t x(throw_after);
+      v.assign(5, x);
+      assert(false);
+    } catch (int) {
+      LIBCPP_ASSERT(v.size() == 0);
+    }
+  }
+  check_new_delete_called();
+  {
+    // With reallocation
+    int throw_after = 4; // x consumes 1, then 3 successful copies, 4th throws
+    std::vector<throwing_t> v;
+    try {
+      throwing_t x(throw_after);
+      v.assign(5, x);
+      assert(false);
+    } catch (int) {
+      LIBCPP_ASSERT(v.size() == 0);
+    }
+  }
+  check_new_delete_called();
+#endif
   return 0;
 }
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp
index 8173285d4996b..8da4aef1c8d4f 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp
@@ -18,6 +18,8 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 #include "asan_testing.h"
+#include "../common.h"
+#include "count_new.h"
 
 TEST_CONSTEXPR_CXX20 bool tests() {
   {
@@ -120,5 +122,37 @@ int main(int, char**) {
 #if TEST_STD_VER > 17
   static_assert(tests());
 #endif
+
+  // Verify that insert(pos, n, x) destroys partially-constructed elements
+  // when a later element's copy constructor throws.
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  {
+    // Without reallocation (note the reserve call)
+    int throw_after = 4; // x consumes 1, then 3 successful copies, 4th throws
+    std::vector<throwing_t> v;
+    v.reserve(10);
+    try {
+      throwing_t x(throw_after);
+      v.insert(v.begin(), 5, x);
+      assert(false);
+    } catch (int) {
+      LIBCPP_ASSERT(v.size() == 0);
+    }
+  }
+  check_new_delete_called();
+  {
+    // With reallocation
+    int throw_after = 4; // x consumes 1, then 3 successful copies, 4th throws
+    std::vector<throwing_t> v;
+    try {
+      throwing_t x(throw_after);
+      v.insert(v.begin(), 5, x);
+      assert(false);
+    } catch (int) {
+      LIBCPP_ASSERT(v.size() == 0);
+    }
+  }
+  check_new_delete_called();
+#endif
   return 0;
 }

>From e96195ea0545ad03caf218e6d4afb0ca8ed7c60e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 22 Jun 2026 23:05:41 -0400
Subject: [PATCH 3/3] Fix CI failures in tests

---
 libcxx/test/std/containers/sequences/vector/common.h            | 2 ++
 .../vector/vector.capacity/resize_size_exceptions.pass.cpp      | 2 ++
 .../vector.capacity/resize_size_value_exceptions.pass.cpp       | 2 ++
 .../sequences/vector/vector.cons/assign_size_value.pass.cpp     | 2 ++
 .../vector/vector.modifiers/insert_iter_size_value.pass.cpp     | 2 ++
 5 files changed, 10 insertions(+)

diff --git a/libcxx/test/std/containers/sequences/vector/common.h b/libcxx/test/std/containers/sequences/vector/common.h
index 768d7f03fd703..71dff000c93a2 100644
--- a/libcxx/test/std/containers/sequences/vector/common.h
+++ b/libcxx/test/std/containers/sequences/vector/common.h
@@ -22,6 +22,7 @@
 #include "count_new.h"
 #include "test_macros.h"
 
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
 struct throwing_t {
   int* throw_after_n_ = nullptr;
   throwing_t() { throw 0; }
@@ -53,6 +54,7 @@ struct throwing_t {
     return lhs.throw_after_n_ != rhs.throw_after_n_;
   }
 };
+#endif // !defined(TEST_HAS_NO_EXCEPTIONS)
 
 #if TEST_STD_VER >= 11
 
diff --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp
index 33b8ace5ec5cc..1ef8507f2ef45 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp
@@ -8,6 +8,8 @@
 
 // UNSUPPORTED: no-exceptions
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 // This test file validates that std::vector<T>::resize(size_type) provides the strong exception guarantee
 // if no exception is thrown by the move constructor of T during the resize call. It also checks that if
 // T's move constructor is not noexcept, resize provides only the basic exception guarantee.
diff --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp
index 4180155ee9de5..f948ab3b5635e 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp
@@ -8,6 +8,8 @@
 
 // UNSUPPORTED: no-exceptions
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 // Check that std::vector<T>::resize(size_type sz, const value_type& x) provides the strong exception guarantee
 // if T is Cpp17CopyInsertable.
 
diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/assign_size_value.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/assign_size_value.pass.cpp
index 239a0bd8ee37b..b335d01db9c74 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/assign_size_value.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/assign_size_value.pass.cpp
@@ -10,6 +10,8 @@
 
 // void assign(size_type n, const_reference v);
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 #include <vector>
 #include <algorithm>
 #include <cassert>
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp
index 8da4aef1c8d4f..da8d880a0ee35 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_size_value.pass.cpp
@@ -10,6 +10,8 @@
 
 // iterator insert(const_iterator position, size_type n, const value_type& x);
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 #include <vector>
 #include <cassert>
 #include <cstddef>


From libcxx-commits at lists.llvm.org  Wed Jul 22 06:58:04 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 06:58:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Destroy elements when exceptions
 are thrown in __construct_at_end (PR #167112)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/167112@github.com>
Message-ID: <6a60cc6c.209e6232.16bc1d.9cb6@mx.google.com>


ldionne wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/containers/sequence/vector.bench.cpp

https://github.com/llvm/llvm-project/pull/167112

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:13:43 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:13:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a60d017.13be6df8.323b61.97a2@mx.google.com>


================
@@ -383,7 +392,41 @@ private:
   bool __owns_ib_;
   bool __always_noconv_;
 
-  bool __read_mode();
----------------
ldionne wrote:

Let's keep this definition in the dylib for now for ABI compatibility.

Let's also file an issue to check whether we can actually remove that. You can assign that one to me, I'll do some investigation.

https://github.com/llvm/llvm-project/pull/210951

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:16:51 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:16:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove redundant benchmarks for
 lexicographical_compare_three_way (PR #210268)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210268@github.com>
Message-ID: <6a60d0d3.bc28b987.1c0bcd.7548@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/210268

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:17:05 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 07:17:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 02c51ad - [libc++] Remove redundant
 benchmarks for lexicographical_compare_three_way (#210268)
Message-ID: <6a60d0e1.510f576d.33aa7d.aa60@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-22T10:16:58-04:00
New Revision: 02c51adb8ff2b2b4c1041ac9f9d260c20ef77cc4

URL: https://github.com/llvm/llvm-project/commit/02c51adb8ff2b2b4c1041ac9f9d260c20ef77cc4
DIFF: https://github.com/llvm/llvm-project/commit/02c51adb8ff2b2b4c1041ac9f9d260c20ef77cc4.diff

LOG: [libc++] Remove redundant benchmarks for lexicographical_compare_three_way (#210268)

We already test the `_slow_path` and `_fast_path` functions through the
generic `BM_lexicographical_compare_three_way`. We don't need to
benchmark them again.

Added: 
    

Modified: 
    libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp b/libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp
index a0b33d26801dc..05d73bdedd998 100644
--- a/libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/lexicographical_compare_three_way.bench.cpp
@@ -13,50 +13,6 @@
 #include "benchmark/benchmark.h"
 #include "test_iterators.h"
 
-static void BM_lexicographical_compare_three_way_slow_path(benchmark::State& state) {
-  auto size = state.range(0);
-  std::vector<int> v1;
-  v1.resize(size);
-  // v2 is identical except for the last value.
-  // This means, that `lexicographical_compare_three_way` actually has to
-  // compare the complete vector and cannot bail out early.
-  std::vector<int> v2 = v1;
-  v2.back() += 1;
-  int* b1 = v1.data();
-  int* e1 = b1 + v1.size();
-  int* b2 = v2.data();
-  int* e2 = b2 + v2.size();
-
-  for (auto _ : state) {
-    auto cmp = std::compare_three_way();
-    benchmark::DoNotOptimize(std::__lexicographical_compare_three_way_slow_path(b1, e1, b2, e2, cmp));
-  }
-}
-
-BENCHMARK(BM_lexicographical_compare_three_way_slow_path)->RangeMultiplier(4)->Range(1, 1 << 20);
-
-static void BM_lexicographical_compare_three_way_fast_path(benchmark::State& state) {
-  auto size = state.range(0);
-  std::vector<int> v1;
-  v1.resize(size);
-  // v2 is identical except for the last value.
-  // This means, that `lexicographical_compare_three_way` actually has to
-  // compare the complete vector and cannot bail out early.
-  std::vector<int> v2 = v1;
-  v2.back() += 1;
-  int* b1 = v1.data();
-  int* e1 = b1 + v1.size();
-  int* b2 = v2.data();
-  int* e2 = b2 + v2.size();
-
-  for (auto _ : state) {
-    auto cmp = std::compare_three_way();
-    benchmark::DoNotOptimize(std::__lexicographical_compare_three_way_fast_path(b1, e1, b2, e2, cmp));
-  }
-}
-
-BENCHMARK(BM_lexicographical_compare_three_way_fast_path)->RangeMultiplier(4)->Range(1, 1 << 20);
-
 template <class IteratorT>
 static void BM_lexicographical_compare_three_way(benchmark::State& state) {
   auto size = state.range(0);


        

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:17:06 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:17:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove redundant benchmarks for
 lexicographical_compare_three_way (PR #210268)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210268@github.com>
Message-ID: <6a60d0e2.6c8668f2.1842d3.2558@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/210268

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:17:29 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:17:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some
	adjacent_view::begin() benchmarks (PR #210265)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210265@github.com>
Message-ID: <6a60d0f9.66c0679a.9a15c.babb@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/210265

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:26:43 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:26:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some
	adjacent_view::begin() benchmarks (PR #210265)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210265@github.com>
Message-ID: <6a60d323.507fc713.3702e2.0cc2@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/210265

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:26:43 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:26:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some
	adjacent_view::begin() benchmarks (PR #210265)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210265@github.com>
Message-ID: <6a60d323.bc28b987.1c0bcd.7ef0@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM with nitpick

https://github.com/llvm/llvm-project/pull/210265

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:26:43 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:26:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some
	adjacent_view::begin() benchmarks (PR #210265)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210265@github.com>
Message-ID: <6a60d323.66c0679a.9a15c.e011@mx.google.com>


================

----------------
ldionne wrote:

I think there's (a bit of) value in this benchmark, but it's probably sufficient to run it for `adjacent_view<1000>`.

https://github.com/llvm/llvm-project/pull/210265

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:32:10 2026
From: libcxx-commits at lists.llvm.org (Daniil Kovalev via libcxx-commits)
Date: Wed, 22 Jul 2026 07:32:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [PAC][libc++] Fix build with
	`ptrauth_calls` feature (PR #211033)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211033@github.com>
Message-ID: <6a60d46a.c4d3d80a.16dae5.f88e@mx.google.com>


kovdan01 wrote:

/cherry-pick 12e1c7f70f34d59c023d05d0fe2c228a4e9aac11

https://github.com/llvm/llvm-project/pull/211033

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:36:50 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Wed, 22 Jul 2026 07:36:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::adjacent_find() based on std::mismatch() (PR #210604)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210604@github.com>
Message-ID: <6a60d582.507fc713.3702e2.3126@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/210604

>From 31208d7946efae8b2e45a121c5178562a7b639c8 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:03:38 +0100
Subject: [PATCH 01/19] Added a basic implementation of parallel 4-legged
 mismatch() based on __parallel_find()

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/libdispatch.h  |   7 +-
 libcxx/include/__pstl/backends/serial.h       |  20 +++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  88 +++++++++++
 .../mismatch/pstl.mismatch.pass.cpp           | 139 ++++++++++++++++++
 7 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__pstl/cpu_algos/mismatch.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..6b5dc87fbe76d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/mismatch.h  
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 2ba5b4434fc33..aa7633a240722 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -324,6 +324,14 @@ struct __adjacent_difference;
 // operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index f7141d24df090..5e89c6fe31cd8 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -37,6 +37,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -241,7 +242,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
       auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
       auto __index           = __chunk == 0 ? 0
                                             : (__chunk * __partitions.__chunk_size_) +
-                                        (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
+                                                  (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
       if (__this_chunk_size != 1) {
         std::__construct_at(
             __values.get() + __chunk,
@@ -368,6 +369,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..d90022ee37145 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/mismatch.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    return std::mismatch(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::forward<_Predicate>(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..93935d22b9442 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
new file mode 100644
index 0000000000000..11ffdc332d633
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_MISMATCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
+
+#include <__algorithm/mismatch.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_mismatch {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Look for a mismatch only in the prefix of the two ranges.
+      auto __n = std::min(__last1 - __first1, __last2 - __first2);
+      // Find a position in the first range where the predicate is false against the corresponding position in the
+      // second range.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __first1 + __n,
+          [&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
+            // Run the sequential mismatch algorithm on these ranges:
+            //   [__brick_first1, __brick_last1) and
+            //   [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
+            auto __brick_first2 = __first2 + (__brick_first1 - __first1);
+            return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
+                .first;
+          },
+          less<>{},
+          true);
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      auto __idx = *__res - __first1;
+      return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      // Unsequenced execution is also implicitly covered by the sequential implementation.
+      return std::mismatch(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
new file mode 100644
index 0000000000000..9cbadda428ad9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 1};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(42);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From ddbcca0bf2d23ac5caaed98c0912016bb23c7640 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:05:01 +0100
Subject: [PATCH 02/19] Added a definition of a parallel 4-legged
 std::mismatch()

---
 libcxx/include/__algorithm/pstl.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2bf9b4276baab..ab35b1a3e537b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -138,6 +138,29 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,

>From 81cdb4aa0a748c2a8039e0c478c19259860513a7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:41:51 +0100
Subject: [PATCH 03/19] Added a default implementation of __mismatch_3leg based
 on __mismatch

---
 libcxx/include/__algorithm/pstl.h             | 18 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++++
 libcxx/include/__pstl/backends/default.h      | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../mismatch/pstl.mismatch.pass.cpp           | 20 +++++++++++
 5 files changed, 84 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index ab35b1a3e537b..01c61fb00f4d5 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -161,6 +161,24 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index aa7633a240722..ecd1f0b9b4049 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -332,6 +332,13 @@ struct __mismatch;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch_3leg;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 28bc75be147aa..b6a15b234b8da 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -63,6 +63,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// mismatch family
+// ---------------
+// - mismatch_3leg
+//
 // for_each family
 // ---------------
 // - for_each_n
@@ -212,6 +216,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// mismatch family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+  optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _Comp __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Forward to the 4-legged version of mismatch.
+      using _Mismatch           = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      _ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
+      return _Mismatch()(
+          __policy,
+          std::move(__first1),
+          std::move(__last1),
+          std::move(__first2),
+          std::move(__last2),
+          std::move(__comp));
+    } else {
+      // Currently only random access iterators are supported for parallel mismatch_3leg.
+      return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..752aa35643e2b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2384,6 +2384,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module mismatch {
+        header "__pstl/cpu_algos/mismatch.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 9cbadda428ad9..0cb5c7632eed8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -10,6 +10,14 @@
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2);
+//
 // template <class ExecutionPolicy,
 //           class ForwardIterator1,
 //           class ForwardIterator2>
@@ -61,6 +69,8 @@ struct Test {
       // empty ranges
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
              std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
     }
@@ -68,18 +78,24 @@ struct Test {
       // single element only
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range without mismatch
       std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range with mismatch
       std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
     }
@@ -102,10 +118,14 @@ struct Test {
       rhs = lhs;
       runway_sample(lhs.size(), [&](size_t i) {
         lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
                std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         lhs[i] = i;
       });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }

>From d5910cc36d457006d17cf1adad74a94ee5a4e759 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:20:42 +0100
Subject: [PATCH 04/19] Added predicated versions

---
 libcxx/include/__algorithm/pstl.h             |  49 +++++
 .../mismatch/pstl.mismatch.pass.cpp           |   2 +
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 178 ++++++++++++++++++
 3 files changed, 229 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 01c61fb00f4d5..bc48fd9f48d7f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
+#  include <__utility/pair.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -138,6 +139,31 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
@@ -161,6 +187,29 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 0cb5c7632eed8..8584f2779927f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -42,6 +42,8 @@
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
+static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
new file mode 100644
index 0000000000000..39e952cbc38f6
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     BinaryPredicate pred);
+//
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2,
+//                                                     BinaryPredicate pred);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+struct Pred {
+  bool operator()(int a, int b) const { return a * 2 == b; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      assert(std::mismatch(
+                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {1};
+      std::array<int, 1> rhs = {2};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 2};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(84);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From 9b207d1481c9076b4bb2af97d049845f2c4e16e0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:39:47 +0100
Subject: [PATCH 05/19] Added tests against custom types

---
 libcxx/include/__pstl/backends/default.h      |  1 +
 .../mismatch/pstl.mismatch.pass.cpp           | 53 ++++++++++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 56 ++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index b6a15b234b8da..776c76f3fdfbd 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -16,6 +16,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 8584f2779927f..5e46831539c30 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -63,6 +63,30 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
+
 template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
@@ -150,6 +174,29 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -157,5 +204,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 39e952cbc38f6..9c0dfc6c0c127 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -67,8 +67,31 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
 struct Pred {
-  bool operator()(int a, int b) const { return a * 2 == b; }
+  bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
+  bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
 };
 
 template <class Iter1, class Iter2>
@@ -167,6 +190,31 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -174,5 +222,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }

>From c21c583527e255d722f312b871d43527e5c4dbb6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:52:50 +0100
Subject: [PATCH 06/19] Added a nodiscard test

---
 libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index f4fffd456a331..f5eaf1642b687 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -63,4 +63,12 @@ void test() {
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }

>From 189ba7a5f23d45939e0ff917985bc7049740d1b7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:57:47 +0100
Subject: [PATCH 07/19] Added an iterator requirement test

---
 .../pstl.iterator-requirements.verify.cpp        | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 402d8fbf9131a..df11341008bc5 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, pred);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}

>From 731de4a2cf2c196b704234b0ee5653453554b9b7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:00:09 +0100
Subject: [PATCH 08/19] Added an include of <__algorithm/min.h>

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 11ffdc332d633..8361731e78cfc 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,8 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__algorithm/mismatch.h>
 #include <__config>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/min.h>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>

>From 105a17ed5abc39ad5ac34bd930af8727a9562bed Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:05:29 +0100
Subject: [PATCH 09/19] Added an exception handling test

---
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  4 +--
 .../pstl.exception_handling.pass.cpp          | 25 +++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 8361731e78cfc..597a47fb185c8 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,9 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__config>
-#include <__algorithm/mismatch.h>
 #include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__config>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index e00046fdb1dd6..cbdb4663f168c 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -248,6 +248,31 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // mismatch(first1, last1, first2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
+        });
+
+        // mismatch(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // mismatch(first1, last1, first2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
+        });
+
+        // mismatch(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From 3bd32433db95c6995476e26d5b34c8000a5515f5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 09:43:45 +0100
Subject: [PATCH 10/19] Changed the variable name to avoid shadowing

---
 .../alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 12 ++++++------
 .../mismatch/pstl.mismatch_pred.pass.cpp             | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 5e46831539c30..941fe07b58327 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -67,22 +67,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 9c0dfc6c0c127..a4b15a56bd071 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -71,22 +71,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Pred {

>From a51af3556cdb7f7e24732077b6dce511e3ee9e89 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 20:27:14 +0100
Subject: [PATCH 11/19] Use raw C array in the tests

---
 .../mismatch/pstl.mismatch.pass.cpp           | 153 +++++++-------
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 192 +++++++++++-------
 2 files changed, 198 insertions(+), 147 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 941fe07b58327..17edf34ee7832 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -28,7 +28,6 @@
 //                                                     ForwardIterator2 last2);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -93,82 +92,98 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(
+          std::mismatch(
+              policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
+          std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 1};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 1};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(
+            std::mismatch(
+                policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+            std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(42);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 42);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(
+                   policy,
+                   Iter1(std::begin(lhs)),
+                   Iter1(std::begin(lhs) + i),
+                   Iter2(std::begin(rhs)),
+                   Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i)) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -179,20 +194,22 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index a4b15a56bd071..18c4fe1abad0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -32,7 +32,6 @@
 //                                                     BinaryPredicate pred);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -100,91 +99,120 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(
-                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::begin(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::begin(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {1};
-      std::array<int, 1> rhs = {2};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {1};
+      int rhs[1] = {2};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 2};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 2};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(84);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 84);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::begin(lhs) + i),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -195,22 +223,28 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };

>From f3bfa5528fb2aea972a018cc68e56133e32db99b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:08:55 +0100
Subject: [PATCH 12/19] Fixed typos in the comment

---
 .../algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 2 +-
 .../alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 17edf34ee7832..ed59fa9eca669 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -46,7 +46,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 18c4fe1abad0c..338503d784a31 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -50,7 +50,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;

>From 4c7fccd82c3a4bea33038c9eee3ebcefddd7f230 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:43:20 +0100
Subject: [PATCH 13/19] Added comments explaining what the parameters mean

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 597a47fb185c8..3d702b03d3afe 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -63,8 +63,9 @@ struct __cpu_parallel_mismatch {
             return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
                 .first;
           },
-          less<>{},
-          true);
+          less<>{}, // `less` here means the lowest index among the mismatches
+          true      // `true` here means we want the first mismatch, not the last
+      );
       if (!__res) {
         return std::nullopt; // Failed to run the algorithm, propagate the error.
       }

>From 22dfeede2c12c49a9f57db064f80e6d24e96f41b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:50:51 +0100
Subject: [PATCH 14/19] Added tests of the return types

---
 .../mismatch/pstl.mismatch.pass.cpp              | 10 ++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp         | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index ed59fa9eca669..cc7ef6feb2356 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -90,6 +90,16 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1]       = {0};
+      int rhs[1]       = {0};
+      auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
+      auto res_4legged = std::mismatch(
+          policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 338503d784a31..d80da0cf0b542 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -97,6 +97,22 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      auto res_3legged =
+          std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
+      auto res_4legged = std::mismatch(
+          policy,
+          Iter1(std::begin(lhs)),
+          Iter1(std::begin(lhs)),
+          Iter2(std::begin(rhs)),
+          Iter2(std::begin(rhs)),
+          Pred{});
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};

>From 8f9ddba49cfc2b501d8d42b1c8027f64b0695a31 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 21 Jul 2026 19:39:00 +0100
Subject: [PATCH 15/19] Rebased on support/runway_sample.h

---
 .../mismatch/pstl.mismatch.pass.cpp             | 17 +----------------
 .../mismatch/pstl.mismatch_pred.pass.cpp        | 17 +----------------
 2 files changed, 2 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index cc7ef6feb2356..83f4b0f16d6c1 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -38,6 +38,7 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
@@ -46,22 +47,6 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 // The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
 
 struct X {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index d80da0cf0b542..89cf899a47b4c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -42,6 +42,7 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
@@ -50,22 +51,6 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 // The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
 
 struct X {

>From 2c72022a2a14a6142479c498bfe283bbe81c67b0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 19 Jul 2026 16:27:43 +0100
Subject: [PATCH 16/19] Added an implementation of adjacent_find() based on
 mismatch()

---
 libcxx/include/__algorithm/pstl.h             |  25 +++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |  45 +++++
 .../pstl.iterator-requirements.verify.cpp     |   6 +
 .../algorithms/pstl.nodiscard.verify.cpp      |   4 +
 .../pstl.adjacent_find.pass.cpp               | 164 +++++++++++++++++
 .../pstl.adjacent_find_pred.pass.cpp          | 173 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  12 ++
 8 files changed, 436 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index bc48fd9f48d7f..0447691c3b86b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -228,6 +228,31 @@ template <class _ExecutionPolicy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+adjacent_find(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "adjacent_find requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__adjacent_find, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), equal_to{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator adjacent_find(
+    _ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __predicate) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "adjacent_find requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__adjacent_find, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__predicate));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index ecd1f0b9b4049..3c835426ec377 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -339,6 +339,13 @@ struct __mismatch_3leg;
 // operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_find;
+// template <class _Policy, class _ForwardIterator, class _BinaryPredicate>
+// optional<_ForwardIterator>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last,
+//                                _BinaryPredicate __predicate) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 776c76f3fdfbd..05700daaf3e84 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 #define _LIBCPP___PSTL_BACKENDS_DEFAULT_H
 
+#include <__algorithm/adjacent_find.h>
 #include <__algorithm/copy_n.h>
 #include <__algorithm/equal.h>
 #include <__algorithm/fill_n.h>
@@ -24,6 +25,7 @@
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
+#include <__iterator/prev.h>
 #include <__iterator/reverse_iterator.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
@@ -66,6 +68,7 @@ namespace __pstl {
 //
 // mismatch family
 // ---------------
+// - adjacent_find
 // - mismatch_3leg
 //
 // for_each family
@@ -249,6 +252,48 @@ struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_find<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator, class _BinaryPredicate>
+  optional<_ForwardIterator>
+  operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __predicate)
+      const noexcept {
+    if constexpr (__has_bidirectional_iterator_category<_ForwardIterator>::value) {
+      using _Mismatch = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      if (__first == __last) {
+        return __last; // Empty range, return __last.
+      }
+      _ForwardIterator __first2 = std::next(__first);
+      if (__first2 == __last) {
+        return __last; // Single element range, no adjacent elements, return __last.
+      }
+      _ForwardIterator __last2 = std::prev(__last);
+      // Find the first match within two overlapping ranges, expressed as a double negation of the predicate:
+      //   [first, __last - 1)
+      //   [first + 1, __last)
+      auto __res = _Mismatch()(
+          __policy,
+          std::move(__first),
+          std::move(__last2),
+          std::move(__first2),
+          __last,
+          [&](__iterator_reference<_ForwardIterator> __lhs, __iterator_reference<_ForwardIterator> __rhs) {
+            return !__predicate(__lhs, __rhs);
+          });
+      if (!__res) {
+        return nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      if (__res->second == __last) {
+        return __last; // No adjacent elements found, return __last.
+      }
+      return __res->first; // Return the first iterator of the pair of mismatched elements.
+    } else {
+      // Currently anything outside bidirectional iterators has to be processed serially
+      return std::adjacent_find(std::move(__first), std::move(__last), std::move(__predicate));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index df11341008bc5..640109ac131fb 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -151,6 +151,12 @@ void f(non_forward_iterator non_fwd,
         pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
   }
 
+  {
+    (void)std::adjacent_find(pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_find}}
+    (void)std::adjacent_find(
+        pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: adjacent_find}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index f5eaf1642b687..352f00f1c9baf 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -71,4 +71,8 @@ void test() {
   std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_find(std::execution::par, std::begin(a), std::end(a));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::adjacent_find(std::execution::par, std::begin(a), std::end(a), pred2);
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
new file mode 100644
index 0000000000000..ff0b5c05bd364
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
@@ -0,0 +1,164 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template<class ExecutionPolicy, class ForwardIterator>
+// ForwardIterator adjacent_find(ExecutionPolicy&& exec,
+//                               ForwardIterator first, ForwardIterator last);
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// TODO: switch to the shared implementation once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+// The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
+struct X {
+  X() = delete;
+  explicit X(int i) : i_(i) {}
+  X(const X&)            = delete;
+  X(X&&)                 = delete;
+  X& operator=(const X&) = delete;
+  X& operator=(X&&)      = delete;
+  bool operator==(const X& other) const { return i_ == other.i_; }
+
+private:
+  int i_;
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]  = {42};
+      auto res = std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)));
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    {
+      int a[] = {42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {42, 42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a) + 1)) == Iter(std::begin(a) + 1));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 0, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {0, 1, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 0, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {0, 1, 0, 1};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 1, 1};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a))) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 3), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 2, 7, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 0);
+      runway_sample(std::size(a), [&](std::size_t i) {
+        if (i == 0)
+          return; // skip the first element to avoid out-of-bounds access
+        a[i] = a[i - 1];
+        assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + i - 1));
+        a[i] = i;
+      });
+    }
+  }
+};
+
+template <class Iter>
+struct TestX {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    X a[] = {X(0), X(1), X(1), X(2)};
+    assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a))) == Iter(std::end(a)));
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<const X*>{}, TestIteratorWithPolicies<TestX>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
new file mode 100644
index 0000000000000..795632b0db4db
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
@@ -0,0 +1,173 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template<class ExecutionPolicy, class ForwardIterator, class BinaryPredicate>
+// ForwardIterator adjacent_find(ExecutionPolicy&& exec,
+//                               ForwardIterator first, ForwardIterator last,
+//                               BinaryPredicate pred);
+
+#include <algorithm>
+#include <cassert>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// TODO: switch to the shared implementation once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+// The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
+struct X {
+  X() = delete;
+  explicit X(int i) : i_(i) {}
+  X(const X&)            = delete;
+  X(X&&)                 = delete;
+  X& operator=(const X&) = delete;
+  X& operator=(X&&)      = delete;
+  int get_value() const { return i_; }
+
+private:
+  int i_;
+};
+
+struct Pred {
+  bool operator()(const X& a, const X& b) const {
+    return static_cast<long long>(b.get_value()) - static_cast<long long>(a.get_value()) == 42;
+  }
+  bool operator()(int a, int b) const { return static_cast<long long>(b) - static_cast<long long>(a) == 42; }
+};
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      int a[]  = {42};
+      auto res = std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{});
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    {
+      int a[] = {42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {42, 84};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a) + 1), Pred{}) ==
+             Iter(std::begin(a) + 1));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 42, 84};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {0, 41, 0};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 0, 42};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {0, 41, 0, 41};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 43, 85};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[] = {0, 1, 2, 44, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::begin(a)), Pred{}) == Iter(std::begin(a)));
+      assert(std::adjacent_find(policy, Iter(std::begin(a) + 3), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {0, 1, 2, 45, 0, 1, 2, 3};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min() + 42,
+                 std::numeric_limits<int>::max() - 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max() - 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 2));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+    }
+    {
+      int a[] = {std::numeric_limits<int>::max(),
+                 std::numeric_limits<int>::min(),
+                 std::numeric_limits<int>::min() + 42,
+                 std::numeric_limits<int>::max()};
+      assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    }
+    {
+      int a[1073];
+      std::iota(std::begin(a), std::end(a), 0);
+      runway_sample(std::size(a), [&](std::size_t i) {
+        if (i == 0)
+          return; // skip the first element to avoid out-of-bounds access
+        a[i] = a[i - 1] + 42;
+        assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) ==
+               Iter(std::begin(a) + i - 1));
+        a[i] = i;
+      });
+    }
+  }
+};
+
+template <class Iter>
+struct TestX {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    X a[] = {X(0), X(40), X(82), X(100)};
+    assert(std::adjacent_find(policy, Iter(std::begin(a)), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 1), Iter(std::end(a)), Pred{}) == Iter(std::begin(a) + 1));
+    assert(std::adjacent_find(policy, Iter(std::begin(a) + 2), Iter(std::end(a)), Pred{}) == Iter(std::end(a)));
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  types::for_each(types::forward_iterator_list<const X*>{}, TestIteratorWithPolicies<TestX>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index cbdb4663f168c..739b70229f72b 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -273,6 +273,18 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto pred = maybe_throw(tokens[5], [](int a, int b) -> bool { return a == b; });
+
+        // adjacent_find(first, last)
+        assert_non_throwing([=, &policy] { (void)std::adjacent_find(policy, std::move(first1), std::move(last1)); });
+
+        // adjacent_find(first, last, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::adjacent_find(policy, std::move(first1), std::move(last1), pred);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From b99519a85d9169ea7a26dcd35e667e4339d890db Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 19 Jul 2026 17:25:35 +0100
Subject: [PATCH 17/19] Missing includes

---
 .../alg.adjacent.find/pstl.adjacent_find.pass.cpp              | 2 ++
 .../alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp         | 3 +++
 2 files changed, 5 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
index ff0b5c05bd364..8b83aa60bd421 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
@@ -18,10 +18,12 @@
 #include <cassert>
 #include <iterator>
 #include <numeric>
+#include <limits>
 
 #include "test_execution_policies.h"
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "type_algorithms.h"
 
 // TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
index 795632b0db4db..60cabad7ba014 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
@@ -18,10 +18,13 @@
 #include <algorithm>
 #include <cassert>
 #include <numeric>
+#include <iterator>
+#include <limits>
 
 #include "test_execution_policies.h"
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "type_algorithms.h"
 
 // TODO: switch to the shared implementation once it's merged into main
 template <class Callable>

>From 8d9a0e3de4934e4e7feacb91c694133dc1a2cc71 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 19 Jul 2026 19:16:03 +0100
Subject: [PATCH 18/19] Fixed variable shadowing

---
 libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 739b70229f72b..e3e76bc3692d3 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -274,7 +274,7 @@ int main(int, char**) {
       }
 
       {
-        auto pred = maybe_throw(tokens[5], [](int a, int b) -> bool { return a == b; });
+        auto pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; });
 
         // adjacent_find(first, last)
         assert_non_throwing([=, &policy] { (void)std::adjacent_find(policy, std::move(first1), std::move(last1)); });

>From e50c241c31e97f8d49275ac3115cd37a57dec6c7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 21 Jul 2026 20:13:37 +0100
Subject: [PATCH 19/19] Rebased onto support/runway_sample.h

---
 .../pstl.adjacent_find.pass.cpp                 | 17 +----------------
 .../pstl.adjacent_find_pred.pass.cpp            | 17 +----------------
 2 files changed, 2 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
index 8b83aa60bd421..bf6304a5f918b 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find.pass.cpp
@@ -24,22 +24,7 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 #include "type_algorithms.h"
-
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
+#include "runway_sample.h"
 
 // The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
 struct X {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
index 60cabad7ba014..e71a9df44743f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.adjacent.find/pstl.adjacent_find_pred.pass.cpp
@@ -25,22 +25,7 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 #include "type_algorithms.h"
-
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
+#include "runway_sample.h"
 
 // The type X is provided to test that the adjacent_find algorithm can be used with custom non-movable/non-copyable types.
 struct X {


From libcxx-commits at lists.llvm.org  Wed Jul 22 07:37:06 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Wed, 22 Jul 2026 07:37:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::mismatch() based on __pstl::__parallel_find() (PR #209291)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209291@github.com>
Message-ID: <6a60d592.e624eb8d.12f676.1428@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/209291

>From 31208d7946efae8b2e45a121c5178562a7b639c8 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:03:38 +0100
Subject: [PATCH 01/15] Added a basic implementation of parallel 4-legged
 mismatch() based on __parallel_find()

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/libdispatch.h  |   7 +-
 libcxx/include/__pstl/backends/serial.h       |  20 +++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  88 +++++++++++
 .../mismatch/pstl.mismatch.pass.cpp           | 139 ++++++++++++++++++
 7 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__pstl/cpu_algos/mismatch.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..6b5dc87fbe76d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/mismatch.h  
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 2ba5b4434fc33..aa7633a240722 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -324,6 +324,14 @@ struct __adjacent_difference;
 // operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index f7141d24df090..5e89c6fe31cd8 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -37,6 +37,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -241,7 +242,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
       auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
       auto __index           = __chunk == 0 ? 0
                                             : (__chunk * __partitions.__chunk_size_) +
-                                        (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
+                                                  (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
       if (__this_chunk_size != 1) {
         std::__construct_at(
             __values.get() + __chunk,
@@ -368,6 +369,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..d90022ee37145 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/mismatch.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    return std::mismatch(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::forward<_Predicate>(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..93935d22b9442 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
new file mode 100644
index 0000000000000..11ffdc332d633
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_MISMATCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
+
+#include <__algorithm/mismatch.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_mismatch {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Look for a mismatch only in the prefix of the two ranges.
+      auto __n = std::min(__last1 - __first1, __last2 - __first2);
+      // Find a position in the first range where the predicate is false against the corresponding position in the
+      // second range.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __first1 + __n,
+          [&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
+            // Run the sequential mismatch algorithm on these ranges:
+            //   [__brick_first1, __brick_last1) and
+            //   [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
+            auto __brick_first2 = __first2 + (__brick_first1 - __first1);
+            return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
+                .first;
+          },
+          less<>{},
+          true);
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      auto __idx = *__res - __first1;
+      return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      // Unsequenced execution is also implicitly covered by the sequential implementation.
+      return std::mismatch(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
new file mode 100644
index 0000000000000..9cbadda428ad9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 1};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(42);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From ddbcca0bf2d23ac5caaed98c0912016bb23c7640 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:05:01 +0100
Subject: [PATCH 02/15] Added a definition of a parallel 4-legged
 std::mismatch()

---
 libcxx/include/__algorithm/pstl.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2bf9b4276baab..ab35b1a3e537b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -138,6 +138,29 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,

>From 81cdb4aa0a748c2a8039e0c478c19259860513a7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:41:51 +0100
Subject: [PATCH 03/15] Added a default implementation of __mismatch_3leg based
 on __mismatch

---
 libcxx/include/__algorithm/pstl.h             | 18 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++++
 libcxx/include/__pstl/backends/default.h      | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../mismatch/pstl.mismatch.pass.cpp           | 20 +++++++++++
 5 files changed, 84 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index ab35b1a3e537b..01c61fb00f4d5 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -161,6 +161,24 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index aa7633a240722..ecd1f0b9b4049 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -332,6 +332,13 @@ struct __mismatch;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch_3leg;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 28bc75be147aa..b6a15b234b8da 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -63,6 +63,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// mismatch family
+// ---------------
+// - mismatch_3leg
+//
 // for_each family
 // ---------------
 // - for_each_n
@@ -212,6 +216,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// mismatch family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+  optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _Comp __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Forward to the 4-legged version of mismatch.
+      using _Mismatch           = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      _ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
+      return _Mismatch()(
+          __policy,
+          std::move(__first1),
+          std::move(__last1),
+          std::move(__first2),
+          std::move(__last2),
+          std::move(__comp));
+    } else {
+      // Currently only random access iterators are supported for parallel mismatch_3leg.
+      return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..752aa35643e2b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2384,6 +2384,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module mismatch {
+        header "__pstl/cpu_algos/mismatch.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 9cbadda428ad9..0cb5c7632eed8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -10,6 +10,14 @@
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2);
+//
 // template <class ExecutionPolicy,
 //           class ForwardIterator1,
 //           class ForwardIterator2>
@@ -61,6 +69,8 @@ struct Test {
       // empty ranges
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
              std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
     }
@@ -68,18 +78,24 @@ struct Test {
       // single element only
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range without mismatch
       std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range with mismatch
       std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
     }
@@ -102,10 +118,14 @@ struct Test {
       rhs = lhs;
       runway_sample(lhs.size(), [&](size_t i) {
         lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
                std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         lhs[i] = i;
       });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }

>From d5910cc36d457006d17cf1adad74a94ee5a4e759 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:20:42 +0100
Subject: [PATCH 04/15] Added predicated versions

---
 libcxx/include/__algorithm/pstl.h             |  49 +++++
 .../mismatch/pstl.mismatch.pass.cpp           |   2 +
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 178 ++++++++++++++++++
 3 files changed, 229 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 01c61fb00f4d5..bc48fd9f48d7f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
+#  include <__utility/pair.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -138,6 +139,31 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
@@ -161,6 +187,29 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 0cb5c7632eed8..8584f2779927f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -42,6 +42,8 @@
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
+static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
new file mode 100644
index 0000000000000..39e952cbc38f6
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     BinaryPredicate pred);
+//
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2,
+//                                                     BinaryPredicate pred);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+struct Pred {
+  bool operator()(int a, int b) const { return a * 2 == b; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      assert(std::mismatch(
+                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {1};
+      std::array<int, 1> rhs = {2};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 2};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(84);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From 9b207d1481c9076b4bb2af97d049845f2c4e16e0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:39:47 +0100
Subject: [PATCH 05/15] Added tests against custom types

---
 libcxx/include/__pstl/backends/default.h      |  1 +
 .../mismatch/pstl.mismatch.pass.cpp           | 53 ++++++++++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 56 ++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index b6a15b234b8da..776c76f3fdfbd 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -16,6 +16,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 8584f2779927f..5e46831539c30 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -63,6 +63,30 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
+
 template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
@@ -150,6 +174,29 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -157,5 +204,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 39e952cbc38f6..9c0dfc6c0c127 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -67,8 +67,31 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
 struct Pred {
-  bool operator()(int a, int b) const { return a * 2 == b; }
+  bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
+  bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
 };
 
 template <class Iter1, class Iter2>
@@ -167,6 +190,31 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -174,5 +222,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }

>From c21c583527e255d722f312b871d43527e5c4dbb6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:52:50 +0100
Subject: [PATCH 06/15] Added a nodiscard test

---
 libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index f4fffd456a331..f5eaf1642b687 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -63,4 +63,12 @@ void test() {
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }

>From 189ba7a5f23d45939e0ff917985bc7049740d1b7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:57:47 +0100
Subject: [PATCH 07/15] Added an iterator requirement test

---
 .../pstl.iterator-requirements.verify.cpp        | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 402d8fbf9131a..df11341008bc5 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, pred);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}

>From 731de4a2cf2c196b704234b0ee5653453554b9b7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:00:09 +0100
Subject: [PATCH 08/15] Added an include of <__algorithm/min.h>

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 11ffdc332d633..8361731e78cfc 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,8 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__algorithm/mismatch.h>
 #include <__config>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/min.h>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>

>From 105a17ed5abc39ad5ac34bd930af8727a9562bed Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:05:29 +0100
Subject: [PATCH 09/15] Added an exception handling test

---
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  4 +--
 .../pstl.exception_handling.pass.cpp          | 25 +++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 8361731e78cfc..597a47fb185c8 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,9 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__config>
-#include <__algorithm/mismatch.h>
 #include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__config>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index e00046fdb1dd6..cbdb4663f168c 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -248,6 +248,31 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // mismatch(first1, last1, first2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
+        });
+
+        // mismatch(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // mismatch(first1, last1, first2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
+        });
+
+        // mismatch(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From 3bd32433db95c6995476e26d5b34c8000a5515f5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 09:43:45 +0100
Subject: [PATCH 10/15] Changed the variable name to avoid shadowing

---
 .../alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 12 ++++++------
 .../mismatch/pstl.mismatch_pred.pass.cpp             | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 5e46831539c30..941fe07b58327 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -67,22 +67,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 9c0dfc6c0c127..a4b15a56bd071 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -71,22 +71,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Pred {

>From a51af3556cdb7f7e24732077b6dce511e3ee9e89 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 20:27:14 +0100
Subject: [PATCH 11/15] Use raw C array in the tests

---
 .../mismatch/pstl.mismatch.pass.cpp           | 153 +++++++-------
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 192 +++++++++++-------
 2 files changed, 198 insertions(+), 147 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 941fe07b58327..17edf34ee7832 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -28,7 +28,6 @@
 //                                                     ForwardIterator2 last2);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -93,82 +92,98 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(
+          std::mismatch(
+              policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
+          std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 1};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 1};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(
+            std::mismatch(
+                policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+            std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(42);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 42);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(
+                   policy,
+                   Iter1(std::begin(lhs)),
+                   Iter1(std::begin(lhs) + i),
+                   Iter2(std::begin(rhs)),
+                   Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i)) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -179,20 +194,22 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index a4b15a56bd071..18c4fe1abad0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -32,7 +32,6 @@
 //                                                     BinaryPredicate pred);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -100,91 +99,120 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(
-                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::begin(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::begin(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {1};
-      std::array<int, 1> rhs = {2};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {1};
+      int rhs[1] = {2};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 2};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 2};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(84);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 84);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::begin(lhs) + i),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -195,22 +223,28 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };

>From f3bfa5528fb2aea972a018cc68e56133e32db99b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:08:55 +0100
Subject: [PATCH 12/15] Fixed typos in the comment

---
 .../algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 2 +-
 .../alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 17edf34ee7832..ed59fa9eca669 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -46,7 +46,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 18c4fe1abad0c..338503d784a31 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -50,7 +50,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;

>From 4c7fccd82c3a4bea33038c9eee3ebcefddd7f230 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:43:20 +0100
Subject: [PATCH 13/15] Added comments explaining what the parameters mean

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 597a47fb185c8..3d702b03d3afe 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -63,8 +63,9 @@ struct __cpu_parallel_mismatch {
             return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
                 .first;
           },
-          less<>{},
-          true);
+          less<>{}, // `less` here means the lowest index among the mismatches
+          true      // `true` here means we want the first mismatch, not the last
+      );
       if (!__res) {
         return std::nullopt; // Failed to run the algorithm, propagate the error.
       }

>From 22dfeede2c12c49a9f57db064f80e6d24e96f41b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:50:51 +0100
Subject: [PATCH 14/15] Added tests of the return types

---
 .../mismatch/pstl.mismatch.pass.cpp              | 10 ++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp         | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index ed59fa9eca669..cc7ef6feb2356 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -90,6 +90,16 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1]       = {0};
+      int rhs[1]       = {0};
+      auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
+      auto res_4legged = std::mismatch(
+          policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 338503d784a31..d80da0cf0b542 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -97,6 +97,22 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      auto res_3legged =
+          std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
+      auto res_4legged = std::mismatch(
+          policy,
+          Iter1(std::begin(lhs)),
+          Iter1(std::begin(lhs)),
+          Iter2(std::begin(rhs)),
+          Iter2(std::begin(rhs)),
+          Pred{});
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};

>From 8f9ddba49cfc2b501d8d42b1c8027f64b0695a31 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 21 Jul 2026 19:39:00 +0100
Subject: [PATCH 15/15] Rebased on support/runway_sample.h

---
 .../mismatch/pstl.mismatch.pass.cpp             | 17 +----------------
 .../mismatch/pstl.mismatch_pred.pass.cpp        | 17 +----------------
 2 files changed, 2 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index cc7ef6feb2356..83f4b0f16d6c1 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -38,6 +38,7 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
@@ -46,22 +47,6 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 // The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
 
 struct X {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index d80da0cf0b542..89cf899a47b4c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -42,6 +42,7 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
@@ -50,22 +51,6 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 // The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
 
 struct X {


From libcxx-commits at lists.llvm.org  Wed Jul 22 07:49:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 07:49:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a60d893.66c0679a.9a15c.37ae@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From 145920aa6c68ee172dc65111a6a0cfb99d4c9069 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/30] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..11a0b42ea54f5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 899502422570a..4ae0f6a28637a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -32,11 +33,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -52,21 +66,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -102,12 +120,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -126,23 +146,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -152,23 +178,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From 90d997369a9f358f579ae61963226b1f30c81d5a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/30] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 37c38d25012c6..78e27d9ef9781 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From 7a63b7897f421f0d367057c933db66b3ea0f21e1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/30] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 11a0b42ea54f5..caa7f9d1a2535 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 78e27d9ef9781..640f6e5e916ca 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..8bdde44c1cb8c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -895,6 +895,7 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From e6a881f9199428718c9b93a92dcea056d7e84356 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/30] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 4ae0f6a28637a..c496ecf5ac742 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -49,7 +49,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From 13589231818d564e6ba520c405eae011f72f10fe Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/30] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From d3c6b5e3b74e59c2beaf5b3158db90faef36600c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/30] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c496ecf5ac742..c55de19cd4b59 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -148,7 +148,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From 11cedf33d3fe73d5bad5930a8c4f12df33e0eac8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/30] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 7e6410501ecdccbd78ce814b0e4906a60f379df6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/30] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c55de19cd4b59..0cf50d7ebd1ec 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -162,7 +162,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -180,7 +180,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -195,7 +195,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 640f6e5e916ca..44f1229f859b3 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From f666483df06fffa6f94a403ba051621cf0432415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/30] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index caa7f9d1a2535..b6650a314bd40 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0cf50d7ebd1ec..05b22d04962fa 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,29 +30,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 44f1229f859b3..0a6e3d54b5911 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 8bdde44c1cb8c..f038c9d39a27b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -884,6 +884,7 @@ module std {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -895,7 +896,6 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From f622cc3f082c34d6f0c5cd6e47177664eed48938 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/30] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 1e55b9997f985525c33ac424b94de00d91b36bba Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/30] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 0a6e3d54b5911..2f6e0d5dbd20a 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From 996293d9cfb334a462283e783e2fb2af90bb373c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/30] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From f74f609379e9feab234dd430bc758b19d9b1d46b Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/30] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From 8c0b1998421e1c8e214ee2694544bf3f9d8f5f79 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/30] 14

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <cstring>
+#include <type_traits>
 
 struct WithTailPadding {
   int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From f436e600ce39701927e30d0074019afc4cdb581c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 20:42:14 +0100
Subject: [PATCH 15/30] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e4d83e638ca77..0bafdbb1d7ede 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -136,7 +136,7 @@ void test() {
   {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
-
+    std::atomic<T> a;
     T init = make<T>(10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);

>From 4def68f03c3089677bb5206f56f30e367415ecce Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:13:05 +0100
Subject: [PATCH 16/30] ci

---
 libcxx/include/__atomic/support/gcc.h         |  5 ++--
 .../atomics.types.generic/padding.pass.cpp    | 24 ++++++++++-------
 .../atomics.types.generic/padding.pass.cpp    | 27 ++++++++++---------
 .../atomics.types.generic/padding2.pass.cpp   |  3 ---
 4 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 2f6e0d5dbd20a..b162e2c2a63f0 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -50,9 +50,8 @@ struct __cxx_atomic_base_impl {
     std::__clear_padding_if_needed(__a_value);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
   _Tp __a_value;
 };
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index bf594b45ed54f..6f894fe6cb605 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -44,15 +44,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -61,11 +69,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0bafdbb1d7ede..344d08b493e60 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,9 +7,6 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits
@@ -41,15 +38,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -58,11 +63,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
index 4e17471a2461f..1040f342b8206 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -8,9 +8,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-localization
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits

>From eeecefecde068ea1f6e2f8417b52961192ca8bc4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:29:04 +0100
Subject: [PATCH 17/30] CI

---
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding2.pass.cpp   | 273 ------------------
 3 files changed, 2 insertions(+), 275 deletions(-)
 delete mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 6f894fe6cb605..388ab0867fe38 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 344d08b493e60..f1a534a2916d6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
deleted file mode 100644
index 1040f342b8206..0000000000000
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-localization
-
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
-// CAS should work on types with padding bits
-
-#include <atomic>
-#include <cassert>
-#include <cstring>
-#include <type_traits>
-#include <iostream>
-
-struct Foo {
-  int i;
-  char c;
-};
-
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
-
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
-}
-
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
-}
-
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-#ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
-#else
-  (void)f;
-  (void)pad_byte;
-#endif
-}
-
-void test() {
-  std::cerr << "test 1" << std::endl;
-  {
-    // compare_exchange_strong
-    // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
-    std::cerr << "test 2" << std::endl;
-
-    Foo init = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 3" << std::endl;
-    assert_foo_padding(init, 0xBB);
-    std::cerr << "test 4" << std::endl;
-    a.store(init);
-
-    Foo expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 5" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 6" << std::endl;
-
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 7" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 8" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 9" << std::endl;
-    assert(r);
-    std::cerr << "test 10" << std::endl;
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    std::cerr << "test 11" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 42);
-    std::cerr << "test 12" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 13" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 14" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 15" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 16" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 17" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 18" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 19" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 10" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 21" << std::endl;
-    assert(!r);
-    std::cerr << "test 22" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 23" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 24" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 25" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 10);
-    std::cerr << "test 26" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 27" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 28" << std::endl;
-  }
-
-  {
-    // compare_exchange_weak
-    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 29" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 30" << std::endl;
-    a.store(stored);
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 31" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 32" << std::endl;
-
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 33" << std::endl;
-    assert_foo_padding(original_expected, 0xAA);
-    std::cerr << "test 34" << std::endl;
-
-    bool r                  = false;
-    const auto max_attempts = 100;
-    auto current_attempt    = 0;
-    while (!r) {
-      ++current_attempt;
-      std::cerr << "test 35" << std::endl;
-      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      std::cerr << "test 36" << std::endl;
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
-      std::cerr << "test 37" << std::endl;
-      r = a.compare_exchange_weak(expected, new_value);
-      if (r) {
-        std::cerr << "test 38" << std::endl;
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-        std::cerr << "test 39" << std::endl;
-      } else {
-        // Spurious failure: expected is updated to the current atomic value.
-        std::cerr << "test 40" << std::endl;
-        assert(expected.i == 10);
-        std::cerr << "test 41" << std::endl;
-        assert(expected.c == 'a');
-        std::cerr << "test 45" << std::endl;
-        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
-        std::cerr << "test 43" << std::endl;
-      }
-    }
-
-    Foo loaded = a.load();
-    std::cerr << "test 44" << std::endl;
-    assert(loaded.i == 42);
-    std::cerr << "test 45" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 46" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 47" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 48" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 49" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 50" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 51" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 52" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 53" << std::endl;
-
-    bool r = a.compare_exchange_weak(expected, new_value);
-
-    std::cerr << "test 54" << std::endl;
-    assert(!r);
-    std::cerr << "test 55" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 56" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 57" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 58" << std::endl;
-    Foo loaded = a.load();
-    std::cerr << "test 59" << std::endl;
-    assert(loaded.i == 10);
-    std::cerr << "test 60" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 61" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 62" << std::endl;
-  }
-
-  {
-    // Types with unique object representations skip the padding-clearing path.
-    std::atomic<int> a(1);
-    int expected = 1;
-    std::cerr << "test 63" << std::endl;
-    assert(a.compare_exchange_strong(expected, 2));
-    std::cerr << "test 64" << std::endl;
-    assert(expected == 1);
-    std::cerr << "test 65" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 66" << std::endl;
-
-    expected = 3;
-    assert(!a.compare_exchange_strong(expected, 4));
-    std::cerr << "test 67" << std::endl;
-    assert(expected == 2);
-    std::cerr << "test 68" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 69" << std::endl;
-  }
-}
-
-int main(int, char**) {
-  // TODO(LLVM-23): Switch to XFAIL: clang-22
-  std::cerr << "main 1" << std::endl;
-#if __has_builtin(__builtin_clear_padding)
-  std::cerr << "main 2" << std::endl;
-  test();
-  std::cerr << "main 3" << std::endl;
-#endif
-  std::cerr << "main 4" << std::endl;
-  return 0;
-}

>From bc539fb8d58e16f24a5ccb8bdd2351475e7d4391 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:52:15 +0100
Subject: [PATCH 18/30] ci

---
 libcxx/include/__atomic/support/c11.h | 13 ++++++++-----
 libcxx/include/__atomic/support/gcc.h |  9 +++++++--
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 05b22d04962fa..d43a2c67f950a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -30,13 +30,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
 
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b162e2c2a63f0..d2d4919d54614 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,12 +46,17 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
       : __a_value(std::__clear_padding_if_needed(__value)) {}
+
   _Tp __a_value;
 };
 

>From 30a056210700cd8f89b1c0ca6991f272a0ebba43 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:26:08 +0100
Subject: [PATCH 19/30] review

---
 libcxx/include/__atomic/clear_padding.h            | 14 ++++++--------
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 5bc77ff4401ab..f920ef39977b7 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -35,17 +35,16 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, float>>,
            _Not<is_same<_Tp, double>>> {};
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(std::addressof(__obj));
-    }
-  }
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+  return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
+}
+
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
@@ -67,7 +66,6 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 388ab0867fe38..45fb083cccf3f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index f1a534a2916d6..5ffba6ca844c5 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();

>From 9b771605f016811c06b3f600e48f45f3268a5269 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:33:16 +0100
Subject: [PATCH 20/30] release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx20Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..a689379321496 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange (`Github <https://llvm.org/PR76180>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 5539767ab800b..8e394b4bc4b3a 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -31,7 +31,7 @@
 "`P0458R2 <https://wg21.link/P0458R2>`__","Checking for Existence of an Element in Associative Containers","2018-06 (Rapperswil)","|Complete|","13","`#104029 <https://github.com/llvm/llvm-project/issues/104029>`__",""
 "`P0475R1 <https://wg21.link/P0475R1>`__","LWG 2511: guaranteed copy elision for piecewise construction","2018-06 (Rapperswil)","|Complete|","","`#104031 <https://github.com/llvm/llvm-project/issues/104031>`__",""
 "`P0476R2 <https://wg21.link/P0476R2>`__","Bit-casting object representations","2018-06 (Rapperswil)","|Complete|","14","`#104087 <https://github.com/llvm/llvm-project/issues/104087>`__",""
-"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","","","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
+"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","|Complete|","24","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
 "`P0542R5 <https://wg21.link/P0542R5>`__","Support for contract based programming in C++","2018-06 (Rapperswil)","|Nothing To Do|","n/a","`#104089 <https://github.com/llvm/llvm-project/issues/104089>`__","Pulled at the 2019-07 meeting in Cologne"
 "`P0556R3 <https://wg21.link/P0556R3>`__","Integral power-of-2 operations","2018-06 (Rapperswil)","|Complete|","9","`#104090 <https://github.com/llvm/llvm-project/issues/104090>`__",""
 "`P0619R4 <https://wg21.link/P0619R4>`__","Reviewing Deprecated Facilities of C++17 for C++20","2018-06 (Rapperswil)","|Complete|","20","`#99985 <https://github.com/llvm/llvm-project/issues/99985>`__","Removed headers are still provided as an extension, but with deprecation warnings."

>From 46a4ba35dbc30153f7868e9796d44f88512ae602 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 17:01:57 +0100
Subject: [PATCH 21/30] ci

---
 libcxx/include/__atomic/clear_padding.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index f920ef39977b7..3099faa8ca6d1 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/has_unique_object_representation.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>

>From 5f475333df1aeee9640364c9fb2f734c731edb7e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 19:03:39 +0100
Subject: [PATCH 22/30] ci

---
 .../atomics.types.generic/padding.pass.cpp    | 19 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 52 ++++++++++++-------
 2 files changed, 44 insertions(+), 27 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 45fb083cccf3f..116a860b3192c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -57,11 +57,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -86,7 +84,8 @@ void test() {
 
   {
     // atomic(T);
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     std::atomic<T> a(init);
     T loaded = a.load();
@@ -97,7 +96,8 @@ void test() {
   {
     // atomic::store
     std::atomic<T> a;
-    T value = make<T>(5, 'x', 0xAB);
+    T value;
+    initialize(value, 5, 'x', 0xAB);
     assert_padding(value, 0xAB);
     a.store(value);
     T loaded = a.load();
@@ -107,10 +107,12 @@ void test() {
   }
   {
     // atomic::exchange
-    T initial = make<T>(1, 'a', 0x00);
+    T initial;
+    initialize(initial, 1, 'a', 0x00);
     assert_padding(initial, 0x00);
     std::atomic<T> a(initial);
-    T new_val = make<T>(2, 'b', 0xCD);
+    T new_val;
+    initialize(new_val, 2, 'b', 0xCD);
     assert_padding(new_val, 0xCD);
     T old = a.exchange(new_val);
     assert(old.i == 1);
@@ -124,7 +126,8 @@ void test() {
   {
     // atomic_init
     std::atomic<T> a;
-    T init = make<T>(7, 'z', 0xEF);
+    T init;
+    initialize(init, 7, 'z', 0xEF);
     assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
     T loaded = a.load();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 5ffba6ca844c5..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -51,11 +51,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -84,17 +82,20 @@ void test() {
     // CAS should succeed when only padding differs in expected; expected is unchanged.
     std::atomic<T> a;
 
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0xAA);
+    T expected;
+    initialize(expected, 10, 'a', 0xAA);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -112,13 +113,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -138,17 +142,20 @@ void test() {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
     std::atomic<T> a;
-    T init = make<T>(10, 'a', 0x00);
+    T init;
+    initialize(init, 10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0x00);
+    T expected;
+    initialize(expected, 10, 'a', 0x00);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0x42);
+    T new_value;
+    initialize(new_value, 42, 'b', 0x42);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,14 +173,17 @@ void test() {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
-    T original_expected = make<T>(10, 'a', 0xAA);
+    T original_expected;
+    initialize(original_expected, 10, 'a', 0xAA);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -182,7 +192,8 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      T expected = make<T>(10, 'a', 0xAA);
+      T expected;
+      initialize(expected, 10, 'a', 0xAA);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -207,13 +218,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);

>From 704a54eb4f1f7989931b7e25ad87abbb60ad6021 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:07:26 +0100
Subject: [PATCH 23/30] debug

---
 .../atomics.types.generic/padding.pass.cpp    | 64 ++++++++++++++++++-
 1 file changed, 62 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..3b7f13b366e96 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,6 +15,7 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
+#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -70,11 +71,19 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
+void print_memory(const T& obj) {
+  for (size_t i = 0; i < sizeof(T); ++i) {
+    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
+  }
+  std::cerr << std::endl;
+}
+
 template <class T>
 void test() {
   {
@@ -84,11 +93,15 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
+    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
+    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -96,6 +109,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -106,6 +121,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test1 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -115,14 +132,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -131,11 +154,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test2 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test2 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -144,11 +171,15 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
+    std::cerr << "test3 init padding should be 0x00: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
+    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -156,6 +187,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
+    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,6 +199,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test3 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -175,15 +210,21 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
+    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
+    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -194,6 +235,8 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
+      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
+      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -203,6 +246,8 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        std::cerr << "test4 expected padding should be 0: " << std::endl;
+        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -211,6 +256,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test4 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -220,14 +267,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -236,11 +289,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test5 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test5 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -262,10 +319,13 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
+  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
+  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
+  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 0;
+  return 1;
 }

>From 8c622e41d2428f630855f0aaa215b1463d8fe4ca Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:38:19 +0100
Subject: [PATCH 24/30] Revert "debug"

This reverts commit 8be9a83ee040faff62f0756555890484bf502a31.
---
 .../atomics.types.generic/padding.pass.cpp    | 64 +------------------
 1 file changed, 2 insertions(+), 62 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 3b7f13b366e96..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,7 +15,6 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
-#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -71,19 +70,11 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)obj;
+  (void)f;
   (void)pad_byte;
 #endif
 }
 
-template <class T>
-void print_memory(const T& obj) {
-  for (size_t i = 0; i < sizeof(T); ++i) {
-    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
-  }
-  std::cerr << std::endl;
-}
-
 template <class T>
 void test() {
   {
@@ -93,15 +84,11 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
-    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
-    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -109,8 +96,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -121,8 +106,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test1 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -132,20 +115,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -154,15 +131,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test2 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test2 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -171,15 +144,11 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
-    std::cerr << "test3 init padding should be 0x00: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
-    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -187,8 +156,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
-    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -199,8 +166,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test3 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -210,21 +175,15 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
-    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
-    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -235,8 +194,6 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
-      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
-      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -246,8 +203,6 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        std::cerr << "test4 expected padding should be 0: " << std::endl;
-        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -256,8 +211,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test4 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -267,20 +220,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -289,15 +236,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test5 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test5 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -319,13 +262,10 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
-  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
-  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
-  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 1;
+  return 0;
 }

>From 789912ba84ccbb3a3efea236fde31835783a7415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:44:00 +0100
Subject: [PATCH 25/30] test ci

---
 .../atomics.types.generic/padding.pass.cpp    | 20 ++++++++++++++-----
 .../atomics.types.generic/padding.pass.cpp    | 12 +----------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 116a860b3192c..cd41f2893864c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -71,6 +71,16 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
+void assert_padding(const std::atomic<T>& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  T loaded     = obj.load();
+  set(reference, loaded.i, loaded.c);
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
+}
+
 template <class T>
 void test() {
   {
@@ -79,7 +89,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 
   {
@@ -91,7 +101,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::store
@@ -103,7 +113,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::exchange
@@ -121,7 +131,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic_init
@@ -133,7 +143,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..10c15523cd70b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -70,7 +70,7 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
@@ -105,8 +105,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -135,8 +133,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
   {
     // compare_exchange_strong
@@ -165,8 +161,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -210,8 +204,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -240,8 +232,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {

>From 81770cda9f2171565c1c2980ad8306b9665752b2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:18:49 +0100
Subject: [PATCH 26/30] padding

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index cd41f2893864c..a178ce74578ec 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -127,7 +127,6 @@ void test() {
     T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_padding(old, 0);
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');

>From b90bec87725f4bde5722a3aa857a5c9d22c275e5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:40:31 +0100
Subject: [PATCH 27/30] clang22

---
 .../libcxx/atomics/atomics.types.generic/padding.pass.cpp    | 5 ++---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 4 +---
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index a178ce74578ec..48ae68a8e97ba 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -147,11 +148,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
+
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 10c15523cd70b..dc0afa439dc12 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
@@ -250,12 +251,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
 
   return 0;
 }

>From 05729850c8bf62df54a60b12201ac475e5c55fb5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 13:26:21 +0100
Subject: [PATCH 28/30] cxx 03

---
 libcxx/include/__atomic/clear_padding.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 3099faa8ca6d1..dad48ed27e6a4 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -32,23 +32,23 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
-           _Not<is_same<_Tp, float>>,
-           _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)> >,
+           _Not<is_same<_Tp, float> >,
+           _Not<is_same<_Tp, double> > > {};
 
-template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   return __obj;
 }
 
-template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp> >::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);

>From 2d00c0b818d180cc4d991508be2ad3dd8b7ee878 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:26:21 +0100
Subject: [PATCH 29/30] ci

---
 libcxx/include/__atomic/clear_padding.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index dad48ed27e6a4..1a4223fd97763 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -37,12 +37,12 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double> > > {};
 
 template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
   return __obj;
 }
 
 template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 

>From 30d5c984f33655f34946afb790bdc85c1f29d7a4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 15:49:25 +0100
Subject: [PATCH 30/30] ci

---
 libcxx/include/__atomic/clear_padding.h | 31 +++++++++++++++----------
 1 file changed, 19 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 1a4223fd97763..be55837cd8ec8 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -46,20 +46,27 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __ob
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 
-template <class _Tp, class _Up, class _CasFunc>
+template <class _Tp,
+          class _Up,
+          class _CasFunc,
+          __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
+}
+
+template <class _Tp,
+          class _Up,
+          class _CasFunc,
+          __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp> >::value) {
-    return __cas_func(__expected, __value);
+  std::__clear_padding_if_needed(__value);
+  __remove_cvref_t<_Tp> __expected_copy = *__expected;
+  std::__clear_padding_if_needed(__expected_copy);
+  if (__cas_func(std::addressof(__expected_copy), __value)) {
+    return true;
   } else {
-    std::__clear_padding_if_needed(__value);
-    __remove_cvref_t<_Tp> __expected_copy = *__expected;
-    std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), __value)) {
-      return true;
-    } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
-      return false;
-    }
+    std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
+    return false;
   }
 }
 


From libcxx-commits at lists.llvm.org  Wed Jul 22 07:52:01 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:52:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a60d911.3cce8983.247e47.e833@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207500

>From 9dde19ee8b7ad4dce0f74550288727526285fdfb Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 4 Jul 2026 11:06:55 +0200
Subject: [PATCH] [libc++] Add
 __uninitialized_allocator_{fill,value_construct,move} and use them in vector

---
 libcxx/docs/ReleaseNotes/23.rst               |  3 ++
 .../__memory/uninitialized_algorithms.h       | 38 +++++++++++++++++++
 libcxx/include/__vector/vector.h              | 14 ++-----
 .../sequences/vector/asan_throw.pass.cpp      |  2 +-
 4 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index f7a8e5b04f1ea..27c68b93506ce 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -101,6 +101,9 @@ Potentially breaking changes
 - ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
   The implementation was donated by Microsoft and has been refactored to follow libc++ standards and conventions.
 
+- The destruction order as well as whether elements are destructed in ``vector`` when appending has changed.
+  Specifically, when an exception is thrown, the appended elements are now destructed from first to last.
+
 Announcements About Future Releases
 -----------------------------------
 
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index f184b2b9dd925..baca90fb10554 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -302,6 +302,30 @@ uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofir
 
 #endif // _LIBCPP_STD_VER >= 17
 
+template <class _Alloc, class _Iter, class _Sent, class _Tp>
+_LIBCPP_CONSTEXPR_SINCE_CXX20
+_Iter __uninitialized_allocator_fill(_Alloc& __alloc, _Iter __first, _Sent __last, const _Tp& __value) {
+  auto __iter = __first;
+
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __first, __iter); });
+  for (; __iter != __last; ++__iter)
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__iter), __value);
+  __guard.__complete();
+  return __iter;
+}
+
+template <class _Alloc, class _Iter, class _Sent>
+_LIBCPP_CONSTEXPR_SINCE_CXX20
+_Iter __uninitialized_allocator_value_construct(_Alloc& __alloc, _Iter __first, _Sent __last) {
+  auto __iter = __first;
+
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __first, __iter); });
+  for (; __iter != __last; ++__iter)
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__iter));
+  __guard.__complete();
+  return __iter;
+}
+
 template <class _Alloc, class _Iter>
 class _AllocatorDestroyRangeReverse {
 public:
@@ -380,6 +404,20 @@ inline const bool __allocator_has_trivial_move_construct_v = !__has_construct_v<
 template <class _Type>
 inline const bool __allocator_has_trivial_move_construct_v<allocator<_Type>, _Type> = true;
 
+template <class _Alloc, class _InIter, class _Sent, class _OutIter>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
+__uninitialized_allocator_move(_Alloc& __alloc, _InIter __first, _Sent __last, _OutIter __result) {
+  auto __destruct_first = __result;
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __destruct_first, __result); });
+  while (__first != __last) {
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__result), std::move(*__first));
+    ++__first;
+    ++__result;
+  }
+  __guard.__complete();
+  return {std::move(__first), std::move(__result)};
+}
+
 template <class _Alloc, class _Tp>
 inline const bool __allocator_has_trivial_destroy_v = !__has_destroy_v<_Alloc, _Tp*>;
 
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 8226a7f87a119..6f700440ae321 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -878,10 +878,7 @@ vector<_Tp, _Allocator>::__recommend(size_type __new_size) const {
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__construct_at_end(size_type __n) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos));
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_value_construct(__layout_.__alloc(), __tx.__pos_, __tx.__new_end_);
 }
 
 //  Copy constructs __n objects starting at __layout_.__end_ptr() from __x
@@ -894,10 +891,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline void
 vector<_Tp, _Allocator>::__construct_at_end(size_type __n, const_reference __x) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), __x);
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_fill(__layout_.__alloc(), __tx.__pos_, __tx.__new_end_, __x);
 }
 
 template <class _Tp, class _Allocator>
@@ -1121,9 +1115,7 @@ vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointe
   {
     pointer __i = __from_s + __n;
     _ConstructTransaction __tx(*this, __from_e - __i);
-    for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
-      __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
-    }
+    __tx.__pos_ = std::__uninitialized_allocator_move(__layout_.__alloc(), __i, __from_e, __tx.__pos_).__out_;
   }
   std::move_backward(__from_s, __from_s + __n, __old_last);
 }
diff --git a/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp
index dcfa8029cfc0d..5089cd716266a 100644
--- a/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp
@@ -182,7 +182,7 @@ void test_insert_n2() {
     v.insert(v.cbegin(), 5, ThrowOnCopy());
     assert(0);
   } catch (int e) {
-    assert(v.size() == 11);
+    assert(v.size() == 10);
     assert(is_contiguous_container_asan_correct(v));
     return;
   }


From libcxx-commits at lists.llvm.org  Wed Jul 22 07:52:12 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:52:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a60d91c.e55dc341.dbc09.0d28@mx.google.com>


ldionne wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/containers/sequence/vector.bench.cpp

https://github.com/llvm/llvm-project/pull/207500

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:54:59 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:54:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a60d9c3.bd93e731.2ac69c.1469@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/207500

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:54:59 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:54:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a60d9c3.7d702ed2.2f6c05.a6ec@mx.google.com>


https://github.com/ldionne commented:

Can you merge this PR into https://github.com/llvm/llvm-project/pull/167112 ? That one is older and adds test coverage.

You can push directly to that branch.

https://github.com/llvm/llvm-project/pull/207500

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:55:00 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:55:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a60d9c4.92ea0e2a.2ed880.dd04@mx.google.com>


================
@@ -101,6 +101,9 @@ Potentially breaking changes
 - ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
   The implementation was donated by Microsoft and has been refactored to follow libc++ standards and conventions.
 
+- The destruction order as well as whether elements are destructed in ``vector`` when appending has changed.
+  Specifically, when an exception is thrown, the appended elements are now destructed from first to last.
----------------
ldionne wrote:

```suggestion
  Specifically, when an exception is thrown, the appended elements are now destructed from first to last instead of last to first.
```

https://github.com/llvm/llvm-project/pull/207500

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:55:01 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:55:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a60d9c5.f1c1923d.d9163.9525@mx.google.com>


================
@@ -101,6 +101,9 @@ Potentially breaking changes
 - ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
----------------
ldionne wrote:

This should be `24.rst`

https://github.com/llvm/llvm-project/pull/207500

From libcxx-commits at lists.llvm.org  Wed Jul 22 07:56:24 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 07:56:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Add
 __uninitialized_allocator_{fill, value_construct,
 move} and use them in vector (PR #207500)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207500@github.com>
Message-ID: <6a60da18.824e11a6.322560.b81c@mx.google.com>


https://github.com/ldionne requested changes to this pull request.


https://github.com/llvm/llvm-project/pull/207500

From libcxx-commits at lists.llvm.org  Wed Jul 22 08:01:51 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 08:01:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't add #pragma clang
	attribute with GCC (PR #206989)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206989@github.com>
Message-ID: <6a60db5f.bd93e731.2ac69c.cea8@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM, I confirm what @philnik777 said above.

Regarding ABI tags, since we are indeed using `always_inline` on GCC (for now), they don't matter. Once we drop `always_inline` on GCC, we'll lose the per-TU insulation but I'm OK with that tradeoff (FWIW libstdc++ itself doesn't attempt to provide per-TU insulation).

https://github.com/llvm/llvm-project/pull/206989

From libcxx-commits at lists.llvm.org  Wed Jul 22 08:06:53 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 08:06:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] adding yield in the lost
	wakeup test (PR #211261)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211261@github.com>
Message-ID: <6a60dc8d.824e11a6.322560.dff5@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/211261

>From 7a4c60d7dd50a166679ba9aa694589b64058d753 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:36:40 +0100
Subject: [PATCH 1/2] [libc++][test] adding yield in the lost wakeup test

---
 .../atomics.types.operations.wait/lost_wakeup.pass.cpp           | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
index 17ad72dd74866..fc96ea1a8f201 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
@@ -39,6 +39,7 @@ int main(int, char**) {
     auto notify = [&] {
       for (int i = 0; i < num_iterations; ++i) {
         while (waiter_ready.load() < num_waiters) {
+          std::this_thread::yield();
         }
         waiter_ready.store(0);
         state.fetch_add(1);

>From a77588ddb4811e5e0376c8b5eff0f60cbd38ba1a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 16:06:40 +0100
Subject: [PATCH 2/2] macos

---
 .../atomics.types.operations.wait/lost_wakeup.pass.cpp        | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
index fc96ea1a8f201..70be9a4428c68 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/lost_wakeup.pass.cpp
@@ -24,7 +24,7 @@ constexpr int num_waiters    = 8;
 constexpr int num_iterations = 10'000;
 
 int main(int, char**) {
-  for (int run = 0; run < 20; ++run) {
+  for (int run = 0; run < 10; ++run) {
     std::atomic<int> waiter_ready(0);
     std::atomic<int> state(0);
 
@@ -39,7 +39,9 @@ int main(int, char**) {
     auto notify = [&] {
       for (int i = 0; i < num_iterations; ++i) {
         while (waiter_ready.load() < num_waiters) {
+#if !defined(__APPLE__)
           std::this_thread::yield();
+#endif
         }
         waiter_ready.store(0);
         state.fetch_add(1);


From libcxx-commits at lists.llvm.org  Wed Jul 22 08:10:46 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 08:10:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::mismatch() based on __pstl::__parallel_find() (PR #209291)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209291@github.com>
Message-ID: <6a60dd76.9edb5c9d.3c157a.e4d7@mx.google.com>


https://github.com/ldionne approved this pull request.


https://github.com/llvm/llvm-project/pull/209291

From libcxx-commits at lists.llvm.org  Wed Jul 22 08:12:44 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 22 Jul 2026 08:12:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a60ddec.af6ba3ed.1989e7.2d5f@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/23] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/23] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/23] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/23] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/23] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/23] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/23] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/23] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/23] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/23] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/23] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/23] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/23] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 67d88113e5987460d20b2a0001e6aab7e53a57d5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:57:29 +0200
Subject: [PATCH 14/23] add: islessequal.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/islessequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
new file mode 100644
index 0000000000000..98fb3a9e7770a
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::islessequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::islessequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::islessequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::islessequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::islessequal(2.0, 1));     // double vs int
+    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 6d1e2ff8878af3188e953f68bb199406ff942354 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 00:00:33 +0200
Subject: [PATCH 15/23] fix: clang-format

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 2 +-
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index 1b9c1aacd0d13..af999db6166b3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -80,7 +80,7 @@ int main(int, char**) {
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));   // int vs float
+    assert(std::isless(1, 2.0f));     // int vs float
     assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 98fb3a9e7770a..2023b062f55f9 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -84,7 +84,7 @@ int main(int, char**) {
   // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::islessequal(2.0, 1));     // double vs int
-    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
   }
 

>From 71b5f4cf216bba7e770e854906b5112d93cbe367 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 01:57:12 +0200
Subject: [PATCH 16/23] add: islessgreater.pass.cpp and isunordered.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  |  2 +-
 .../numerics/c.math/islessgreater.pass.cpp    | 91 +++++++++++++++++++
 .../std/numerics/c.math/isunordered.pass.cpp  | 78 ++++++++++++++++
 3 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
 create mode 100644 libcxx/test/std/numerics/c.math/isunordered.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2023b062f55f9..2b0be4ff37035 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
new file mode 100644
index 0000000000000..40fbb7f4a4e41
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::islessgreater(T(1), T(2)));
+    assert(std::islessgreater(T(2), T(1)));
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    if (!std::is_same<T, bool>::value) {
+      assert(std::islessgreater(T(1), T(2)));
+      assert(std::islessgreater(T(2), T(1)));
+    }
+
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(T(1), T(1)));
+    assert(std::islessgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::islessgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    if (std::is_signed<T>::value) {
+      assert(std::islessgreater(T(-1), T(1)));
+      assert(std::islessgreater(std::numeric_limits<T>::lowest(), T(0)));
+      assert(std::islessgreater(T(0), std::numeric_limits<T>::lowest()));
+      assert(!std::islessgreater(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
new file mode 100644
index 0000000000000..ed7740d195163
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(std::isunordered(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    assert(std::isunordered(std::numeric_limits<T>::signaling_NaN(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From ef949295028c1bd5d6a19881e122393aada59194 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:01:27 +0200
Subject: [PATCH 17/23] flip assert tests for gcc

---
 .../c.math/constexpr-cxx23-gcc.pass.cpp       | 30 +++++++++----------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index 34513b459a056..8d814e81f8bc6 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -225,25 +225,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From f6a4fa78d49d5bd14219d1cb7885d0d6bf4524ba Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:02:43 +0200
Subject: [PATCH 18/23] fix typo in isgreaterequal.pass.cpp

---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index d6ae553e961e5..f2a4159882654 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows

>From 62ae94cc86a2b43b61a9ba80eadfc4013bcced15 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 18:18:09 +0200
Subject: [PATCH 19/23] update: cmath synopsis for is<...>

---
 libcxx/include/cmath | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..65bf07aed059b 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -140,17 +140,17 @@ bool signbit(arithmetic x);
 
 int fpclassify(arithmetic x);
 
-bool isfinite(arithmetic x);
-bool isinf(arithmetic x);
-bool isnan(arithmetic x);
-bool isnormal(arithmetic x);
-
-bool isgreater(arithmetic x, arithmetic y);
-bool isgreaterequal(arithmetic x, arithmetic y);
-bool isless(arithmetic x, arithmetic y);
-bool islessequal(arithmetic x, arithmetic y);
-bool islessgreater(arithmetic x, arithmetic y);
-bool isunordered(arithmetic x, arithmetic y);
+bool isfinite(arithmetic x); // constexpr since C++23
+bool isinf(arithmetic x);    // constexpr since C++23
+bool isnan(arithmetic x);    // constexpr since C++23
+bool isnormal(arithmetic x); // constexpr since C++23
+
+bool isgreater(arithmetic x, arithmetic y);      // constexpr since C++23
+bool isgreaterequal(arithmetic x, arithmetic y); // constexpr since C++23
+bool isless(arithmetic x, arithmetic y);         // constexpr since C++23
+bool islessequal(arithmetic x, arithmetic y);    // constexpr since C++23
+bool islessgreater(arithmetic x, arithmetic y);  // constexpr since C++23
+bool isunordered(arithmetic x, arithmetic y);    // constexpr since C++23
 
 floating_point acosh (arithmetic x);
 float          acoshf(float x);

>From 1a641131cba0843189ecd9364df414c8baa85cad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 20 Jul 2026 12:31:47 +0200
Subject: [PATCH 20/23] remove: UNSUPPORTED windows from tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp      | 3 ---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 3 ---
 libcxx/test/std/numerics/c.math/isless.pass.cpp         | 3 ---
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp    | 3 ---
 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp  | 3 ---
 libcxx/test/std/numerics/c.math/isunordered.pass.cpp    | 3 ---
 6 files changed, 18 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index c7853974d7a66..269bb31e016cf 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index f2a4159882654..27fa43812812a 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index af999db6166b3..cc68a49beead9 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2b0be4ff37035..5f58bbc993613 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
index 40fbb7f4a4e41..6a10708359bbe 100644
--- a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
index ed7740d195163..3f140106a423f 100644
--- a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>

>From 76b4a8a8ec33acf9f4c132072ad70422d6f09bd5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 22 Jul 2026 12:24:03 +0200
Subject: [PATCH 21/23] fix: convert assert to static_assert

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 269bb31e016cf..6932a26c75f8d 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -82,10 +82,10 @@ int main(int, char**) {
 
   // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
-    assert(std::isgreater(2.0, 1));     // double vs int
-    assert(!std::isgreater(1, 2.0f));   // int vs float
-    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
-    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+    static_assert(std::isgreater(2.0, 1));     // double vs int
+    static_assert(!std::isgreater(1, 2.0f));   // int vs float
+    static_assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    static_assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
   }
 
   return 0;

>From 36145d1a1740c144a180397eaa889873f752911c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 22 Jul 2026 12:44:24 +0200
Subject: [PATCH 22/23] Fix: static_assertions on tests and remove of
 ConvertibleTo

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp   | 12 +++++++-----
 .../test/std/numerics/c.math/isgreaterequal.pass.cpp | 12 +++++++-----
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 11 +++--------
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 11 ++++++-----
 .../test/std/numerics/c.math/islessgreater.pass.cpp  |  5 -----
 libcxx/test/std/numerics/c.math/isunordered.pass.cpp |  5 -----
 6 files changed, 23 insertions(+), 33 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 6932a26c75f8d..781a9af90b153 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -71,21 +71,23 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(1); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
   // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+
+#if TEST_STD_VER >= 23
     static_assert(std::isgreater(2.0, 1));     // double vs int
     static_assert(!std::isgreater(1, 2.0f));   // int vs float
     static_assert(std::isgreater(2.0L, 1.0f)); // long double vs float
     static_assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+#endif
   }
 
   return 0;
diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index 27fa43812812a..bc8b23d01cc7a 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -69,11 +69,6 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(1); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
@@ -84,6 +79,13 @@ int main(int, char**) {
     assert(!std::isgreaterequal(1, 2.0f));   // int vs float
     assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
     assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+
+#if TEST_STD_VER >= 23
+    static_assert(std::isgreaterequal(2.0, 1));     // double vs int
+    static_assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    static_assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    static_assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+#endif
   }
 
   return 0;
diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index cc68a49beead9..bf341f90f3b38 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -65,20 +65,15 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(1); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
-    assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));     // int vs float
-    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+    static_assert(!std::isless(2.0, 1));     // double vs int
+    static_assert(std::isless(1, 2.0f));     // int vs float
+    static_assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
   return 0;
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 5f58bbc993613..55a163f769148 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -69,11 +69,6 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(1); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
@@ -83,6 +78,12 @@ int main(int, char**) {
     assert(!std::islessequal(2.0, 1));     // double vs int
     assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+
+#if TEST_STD_VER >= 23
+    static_assert(!std::islessequal(2.0, 1));     // double vs int
+    static_assert(std::islessequal(1, 2.0f));     // int vs float
+    static_assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+#endif
   }
 
   return 0;
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
index 6a10708359bbe..2d92f0f60b5b8 100644
--- a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -75,11 +75,6 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(1); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
index 3f140106a423f..1f3d43f4c3e7c 100644
--- a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -62,11 +62,6 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(1); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());

>From c1880a334e9391a2e6c0c52bbc98ce9bf77079b3 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 22 Jul 2026 17:12:18 +0200
Subject: [PATCH 23/23] fix: isless.pass.cpp

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index bf341f90f3b38..3b5feb3f7bba3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -71,9 +71,15 @@ int main(int, char**) {
 
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));     // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+
+#if TEST_STD_VER >= 23
     static_assert(!std::isless(2.0, 1));     // double vs int
     static_assert(std::isless(1, 2.0f));     // int vs float
     static_assert(!std::isless(2.0L, 1.0f)); // long double vs float
+#endif
   }
 
   return 0;


From libcxx-commits at lists.llvm.org  Wed Jul 22 08:46:18 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 08:46:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 6f3328e - [libc++] Don't add #pragma
 clang attribute with GCC (#206989)
Message-ID: <6a60e5ca.e624eb8d.12f676.102c@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-22T17:46:13+02:00
New Revision: 6f3328e869b4d3f11d1b37808fbffab6d822d9aa

URL: https://github.com/llvm/llvm-project/commit/6f3328e869b4d3f11d1b37808fbffab6d822d9aa
DIFF: https://github.com/llvm/llvm-project/commit/6f3328e869b4d3f11d1b37808fbffab6d822d9aa.diff

LOG: [libc++] Don't add #pragma clang attribute with GCC (#206989)

GCC doesn't support `#pragma clang attribute`, which causes a bunch of
diagnostics. Since we have it behind a macro anyways we can simply
define the macro as empty with GCC.

Added: 
    

Modified: 
    libcxx/include/__configuration/namespace.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__configuration/namespace.h b/libcxx/include/__configuration/namespace.h
index 6b733e3076581..1f4b8ce247b13 100644
--- a/libcxx/include/__configuration/namespace.h
+++ b/libcxx/include/__configuration/namespace.h
@@ -46,15 +46,20 @@
 #  define _LIBCPP_POP_ABI_PRAGMA_DIAGNOSTICS
 #endif
 
-#define _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS                                                                           \
-  _LIBCPP_PUSH_ABI_PRAGMA_DIAGNOSTICS                                                                                  \
-  _Pragma(_LIBCPP_TOSTRING(clang attribute _LibcxxExplicitABIAnnotations.push(                                         \
-      __attribute__((__exclude_from_explicit_instantiation__,                                                          \
-                     __visibility__("hidden"),                                                                         \
-                     __abi_tag__(_LIBCPP_TOSTRING(_LIBCPP_ODR_SIGNATURE)))),                                           \
-      apply_to = function))) _LIBCPP_POP_ABI_PRAGMA_DIAGNOSTICS
-
-#define _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS _Pragma("clang attribute _LibcxxExplicitABIAnnotations.pop")
+#ifdef _LIBCPP_COMPILER_CLANG_BASED
+#  define _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS                                                                         \
+    _LIBCPP_PUSH_ABI_PRAGMA_DIAGNOSTICS                                                                                \
+    _Pragma(_LIBCPP_TOSTRING(clang attribute _LibcxxExplicitABIAnnotations.push(                                       \
+        __attribute__((__exclude_from_explicit_instantiation__,                                                        \
+                       __visibility__("hidden"),                                                                       \
+                       __abi_tag__(_LIBCPP_TOSTRING(_LIBCPP_ODR_SIGNATURE)))),                                         \
+        apply_to = function))) _LIBCPP_POP_ABI_PRAGMA_DIAGNOSTICS
+
+#  define _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS _Pragma("clang attribute _LibcxxExplicitABIAnnotations.pop")
+#else
+#  define _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+#  define _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+#endif
 
 // clang-format off
 


        

From libcxx-commits at lists.llvm.org  Wed Jul 22 08:46:20 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 08:46:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Don't add #pragma clang
	attribute with GCC (PR #206989)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/206989@github.com>
Message-ID: <6a60e5cc.449bfb44.c0ec9.5b18@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/206989

From libcxx-commits at lists.llvm.org  Wed Jul 22 08:49:24 2026
From: libcxx-commits at lists.llvm.org (=?UTF-8?Q?Yordan_V=C3=A1squez?= via libcxx-commits)
Date: Wed, 22 Jul 2026 08:49:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG3133: Modernizing
 numeric type requirements (PR #208145)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/208145@github.com>
Message-ID: <6a60e684.d2cb4adc.23815e.2cbd@mx.google.com>


vyordan wrote:

Ping

https://github.com/llvm/llvm-project/pull/208145

From libcxx-commits at lists.llvm.org  Wed Jul 22 09:01:52 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Wed, 22 Jul 2026 09:01:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a60e970.a1accbb4.1ace1c.0ad1@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From 145920aa6c68ee172dc65111a6a0cfb99d4c9069 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/31] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..11a0b42ea54f5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 899502422570a..4ae0f6a28637a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -32,11 +33,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -52,21 +66,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -102,12 +120,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -126,23 +146,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -152,23 +178,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From 90d997369a9f358f579ae61963226b1f30c81d5a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/31] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 37c38d25012c6..78e27d9ef9781 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From 7a63b7897f421f0d367057c933db66b3ea0f21e1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/31] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 11a0b42ea54f5..caa7f9d1a2535 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 78e27d9ef9781..640f6e5e916ca 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..8bdde44c1cb8c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -895,6 +895,7 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From e6a881f9199428718c9b93a92dcea056d7e84356 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/31] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 4ae0f6a28637a..c496ecf5ac742 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -49,7 +49,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From 13589231818d564e6ba520c405eae011f72f10fe Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/31] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From d3c6b5e3b74e59c2beaf5b3158db90faef36600c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/31] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c496ecf5ac742..c55de19cd4b59 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -148,7 +148,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From 11cedf33d3fe73d5bad5930a8c4f12df33e0eac8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/31] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 7e6410501ecdccbd78ce814b0e4906a60f379df6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/31] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c55de19cd4b59..0cf50d7ebd1ec 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -162,7 +162,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -180,7 +180,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -195,7 +195,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 640f6e5e916ca..44f1229f859b3 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From f666483df06fffa6f94a403ba051621cf0432415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/31] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index caa7f9d1a2535..b6650a314bd40 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0cf50d7ebd1ec..05b22d04962fa 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,29 +30,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 44f1229f859b3..0a6e3d54b5911 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 8bdde44c1cb8c..f038c9d39a27b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -884,6 +884,7 @@ module std {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -895,7 +896,6 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From f622cc3f082c34d6f0c5cd6e47177664eed48938 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/31] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 1e55b9997f985525c33ac424b94de00d91b36bba Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/31] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 0a6e3d54b5911..2f6e0d5dbd20a 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From 996293d9cfb334a462283e783e2fb2af90bb373c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/31] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From f74f609379e9feab234dd430bc758b19d9b1d46b Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/31] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From 8c0b1998421e1c8e214ee2694544bf3f9d8f5f79 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/31] 14

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <cstring>
+#include <type_traits>
 
 struct WithTailPadding {
   int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From f436e600ce39701927e30d0074019afc4cdb581c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 20:42:14 +0100
Subject: [PATCH 15/31] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e4d83e638ca77..0bafdbb1d7ede 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -136,7 +136,7 @@ void test() {
   {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
-
+    std::atomic<T> a;
     T init = make<T>(10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);

>From 4def68f03c3089677bb5206f56f30e367415ecce Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:13:05 +0100
Subject: [PATCH 16/31] ci

---
 libcxx/include/__atomic/support/gcc.h         |  5 ++--
 .../atomics.types.generic/padding.pass.cpp    | 24 ++++++++++-------
 .../atomics.types.generic/padding.pass.cpp    | 27 ++++++++++---------
 .../atomics.types.generic/padding2.pass.cpp   |  3 ---
 4 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 2f6e0d5dbd20a..b162e2c2a63f0 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -50,9 +50,8 @@ struct __cxx_atomic_base_impl {
     std::__clear_padding_if_needed(__a_value);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
   _Tp __a_value;
 };
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index bf594b45ed54f..6f894fe6cb605 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -44,15 +44,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -61,11 +69,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0bafdbb1d7ede..344d08b493e60 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,9 +7,6 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits
@@ -41,15 +38,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -58,11 +63,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
index 4e17471a2461f..1040f342b8206 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -8,9 +8,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-localization
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits

>From eeecefecde068ea1f6e2f8417b52961192ca8bc4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:29:04 +0100
Subject: [PATCH 17/31] CI

---
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding2.pass.cpp   | 273 ------------------
 3 files changed, 2 insertions(+), 275 deletions(-)
 delete mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 6f894fe6cb605..388ab0867fe38 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 344d08b493e60..f1a534a2916d6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
deleted file mode 100644
index 1040f342b8206..0000000000000
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-localization
-
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
-// CAS should work on types with padding bits
-
-#include <atomic>
-#include <cassert>
-#include <cstring>
-#include <type_traits>
-#include <iostream>
-
-struct Foo {
-  int i;
-  char c;
-};
-
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
-
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
-}
-
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
-}
-
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-#ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
-#else
-  (void)f;
-  (void)pad_byte;
-#endif
-}
-
-void test() {
-  std::cerr << "test 1" << std::endl;
-  {
-    // compare_exchange_strong
-    // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
-    std::cerr << "test 2" << std::endl;
-
-    Foo init = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 3" << std::endl;
-    assert_foo_padding(init, 0xBB);
-    std::cerr << "test 4" << std::endl;
-    a.store(init);
-
-    Foo expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 5" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 6" << std::endl;
-
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 7" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 8" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 9" << std::endl;
-    assert(r);
-    std::cerr << "test 10" << std::endl;
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    std::cerr << "test 11" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 42);
-    std::cerr << "test 12" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 13" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 14" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 15" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 16" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 17" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 18" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 19" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 10" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 21" << std::endl;
-    assert(!r);
-    std::cerr << "test 22" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 23" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 24" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 25" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 10);
-    std::cerr << "test 26" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 27" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 28" << std::endl;
-  }
-
-  {
-    // compare_exchange_weak
-    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 29" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 30" << std::endl;
-    a.store(stored);
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 31" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 32" << std::endl;
-
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 33" << std::endl;
-    assert_foo_padding(original_expected, 0xAA);
-    std::cerr << "test 34" << std::endl;
-
-    bool r                  = false;
-    const auto max_attempts = 100;
-    auto current_attempt    = 0;
-    while (!r) {
-      ++current_attempt;
-      std::cerr << "test 35" << std::endl;
-      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      std::cerr << "test 36" << std::endl;
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
-      std::cerr << "test 37" << std::endl;
-      r = a.compare_exchange_weak(expected, new_value);
-      if (r) {
-        std::cerr << "test 38" << std::endl;
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-        std::cerr << "test 39" << std::endl;
-      } else {
-        // Spurious failure: expected is updated to the current atomic value.
-        std::cerr << "test 40" << std::endl;
-        assert(expected.i == 10);
-        std::cerr << "test 41" << std::endl;
-        assert(expected.c == 'a');
-        std::cerr << "test 45" << std::endl;
-        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
-        std::cerr << "test 43" << std::endl;
-      }
-    }
-
-    Foo loaded = a.load();
-    std::cerr << "test 44" << std::endl;
-    assert(loaded.i == 42);
-    std::cerr << "test 45" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 46" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 47" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 48" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 49" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 50" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 51" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 52" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 53" << std::endl;
-
-    bool r = a.compare_exchange_weak(expected, new_value);
-
-    std::cerr << "test 54" << std::endl;
-    assert(!r);
-    std::cerr << "test 55" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 56" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 57" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 58" << std::endl;
-    Foo loaded = a.load();
-    std::cerr << "test 59" << std::endl;
-    assert(loaded.i == 10);
-    std::cerr << "test 60" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 61" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 62" << std::endl;
-  }
-
-  {
-    // Types with unique object representations skip the padding-clearing path.
-    std::atomic<int> a(1);
-    int expected = 1;
-    std::cerr << "test 63" << std::endl;
-    assert(a.compare_exchange_strong(expected, 2));
-    std::cerr << "test 64" << std::endl;
-    assert(expected == 1);
-    std::cerr << "test 65" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 66" << std::endl;
-
-    expected = 3;
-    assert(!a.compare_exchange_strong(expected, 4));
-    std::cerr << "test 67" << std::endl;
-    assert(expected == 2);
-    std::cerr << "test 68" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 69" << std::endl;
-  }
-}
-
-int main(int, char**) {
-  // TODO(LLVM-23): Switch to XFAIL: clang-22
-  std::cerr << "main 1" << std::endl;
-#if __has_builtin(__builtin_clear_padding)
-  std::cerr << "main 2" << std::endl;
-  test();
-  std::cerr << "main 3" << std::endl;
-#endif
-  std::cerr << "main 4" << std::endl;
-  return 0;
-}

>From bc539fb8d58e16f24a5ccb8bdd2351475e7d4391 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:52:15 +0100
Subject: [PATCH 18/31] ci

---
 libcxx/include/__atomic/support/c11.h | 13 ++++++++-----
 libcxx/include/__atomic/support/gcc.h |  9 +++++++--
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 05b22d04962fa..d43a2c67f950a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -30,13 +30,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
 
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b162e2c2a63f0..d2d4919d54614 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,12 +46,17 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
       : __a_value(std::__clear_padding_if_needed(__value)) {}
+
   _Tp __a_value;
 };
 

>From 30a056210700cd8f89b1c0ca6991f272a0ebba43 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:26:08 +0100
Subject: [PATCH 19/31] review

---
 libcxx/include/__atomic/clear_padding.h            | 14 ++++++--------
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 5bc77ff4401ab..f920ef39977b7 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -35,17 +35,16 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, float>>,
            _Not<is_same<_Tp, double>>> {};
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(std::addressof(__obj));
-    }
-  }
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+  return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
+}
+
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
@@ -67,7 +66,6 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 388ab0867fe38..45fb083cccf3f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index f1a534a2916d6..5ffba6ca844c5 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();

>From 9b771605f016811c06b3f600e48f45f3268a5269 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:33:16 +0100
Subject: [PATCH 20/31] release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx20Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..a689379321496 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange (`Github <https://llvm.org/PR76180>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 5539767ab800b..8e394b4bc4b3a 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -31,7 +31,7 @@
 "`P0458R2 <https://wg21.link/P0458R2>`__","Checking for Existence of an Element in Associative Containers","2018-06 (Rapperswil)","|Complete|","13","`#104029 <https://github.com/llvm/llvm-project/issues/104029>`__",""
 "`P0475R1 <https://wg21.link/P0475R1>`__","LWG 2511: guaranteed copy elision for piecewise construction","2018-06 (Rapperswil)","|Complete|","","`#104031 <https://github.com/llvm/llvm-project/issues/104031>`__",""
 "`P0476R2 <https://wg21.link/P0476R2>`__","Bit-casting object representations","2018-06 (Rapperswil)","|Complete|","14","`#104087 <https://github.com/llvm/llvm-project/issues/104087>`__",""
-"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","","","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
+"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","|Complete|","24","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
 "`P0542R5 <https://wg21.link/P0542R5>`__","Support for contract based programming in C++","2018-06 (Rapperswil)","|Nothing To Do|","n/a","`#104089 <https://github.com/llvm/llvm-project/issues/104089>`__","Pulled at the 2019-07 meeting in Cologne"
 "`P0556R3 <https://wg21.link/P0556R3>`__","Integral power-of-2 operations","2018-06 (Rapperswil)","|Complete|","9","`#104090 <https://github.com/llvm/llvm-project/issues/104090>`__",""
 "`P0619R4 <https://wg21.link/P0619R4>`__","Reviewing Deprecated Facilities of C++17 for C++20","2018-06 (Rapperswil)","|Complete|","20","`#99985 <https://github.com/llvm/llvm-project/issues/99985>`__","Removed headers are still provided as an extension, but with deprecation warnings."

>From 46a4ba35dbc30153f7868e9796d44f88512ae602 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 17:01:57 +0100
Subject: [PATCH 21/31] ci

---
 libcxx/include/__atomic/clear_padding.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index f920ef39977b7..3099faa8ca6d1 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/has_unique_object_representation.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>

>From 5f475333df1aeee9640364c9fb2f734c731edb7e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 19:03:39 +0100
Subject: [PATCH 22/31] ci

---
 .../atomics.types.generic/padding.pass.cpp    | 19 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 52 ++++++++++++-------
 2 files changed, 44 insertions(+), 27 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 45fb083cccf3f..116a860b3192c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -57,11 +57,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -86,7 +84,8 @@ void test() {
 
   {
     // atomic(T);
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     std::atomic<T> a(init);
     T loaded = a.load();
@@ -97,7 +96,8 @@ void test() {
   {
     // atomic::store
     std::atomic<T> a;
-    T value = make<T>(5, 'x', 0xAB);
+    T value;
+    initialize(value, 5, 'x', 0xAB);
     assert_padding(value, 0xAB);
     a.store(value);
     T loaded = a.load();
@@ -107,10 +107,12 @@ void test() {
   }
   {
     // atomic::exchange
-    T initial = make<T>(1, 'a', 0x00);
+    T initial;
+    initialize(initial, 1, 'a', 0x00);
     assert_padding(initial, 0x00);
     std::atomic<T> a(initial);
-    T new_val = make<T>(2, 'b', 0xCD);
+    T new_val;
+    initialize(new_val, 2, 'b', 0xCD);
     assert_padding(new_val, 0xCD);
     T old = a.exchange(new_val);
     assert(old.i == 1);
@@ -124,7 +126,8 @@ void test() {
   {
     // atomic_init
     std::atomic<T> a;
-    T init = make<T>(7, 'z', 0xEF);
+    T init;
+    initialize(init, 7, 'z', 0xEF);
     assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
     T loaded = a.load();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 5ffba6ca844c5..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -51,11 +51,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -84,17 +82,20 @@ void test() {
     // CAS should succeed when only padding differs in expected; expected is unchanged.
     std::atomic<T> a;
 
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0xAA);
+    T expected;
+    initialize(expected, 10, 'a', 0xAA);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -112,13 +113,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -138,17 +142,20 @@ void test() {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
     std::atomic<T> a;
-    T init = make<T>(10, 'a', 0x00);
+    T init;
+    initialize(init, 10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0x00);
+    T expected;
+    initialize(expected, 10, 'a', 0x00);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0x42);
+    T new_value;
+    initialize(new_value, 42, 'b', 0x42);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,14 +173,17 @@ void test() {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
-    T original_expected = make<T>(10, 'a', 0xAA);
+    T original_expected;
+    initialize(original_expected, 10, 'a', 0xAA);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -182,7 +192,8 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      T expected = make<T>(10, 'a', 0xAA);
+      T expected;
+      initialize(expected, 10, 'a', 0xAA);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -207,13 +218,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);

>From 704a54eb4f1f7989931b7e25ad87abbb60ad6021 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:07:26 +0100
Subject: [PATCH 23/31] debug

---
 .../atomics.types.generic/padding.pass.cpp    | 64 ++++++++++++++++++-
 1 file changed, 62 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..3b7f13b366e96 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,6 +15,7 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
+#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -70,11 +71,19 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
+void print_memory(const T& obj) {
+  for (size_t i = 0; i < sizeof(T); ++i) {
+    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
+  }
+  std::cerr << std::endl;
+}
+
 template <class T>
 void test() {
   {
@@ -84,11 +93,15 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
+    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
+    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -96,6 +109,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -106,6 +121,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test1 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -115,14 +132,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -131,11 +154,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test2 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test2 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -144,11 +171,15 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
+    std::cerr << "test3 init padding should be 0x00: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
+    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -156,6 +187,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
+    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,6 +199,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test3 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -175,15 +210,21 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
+    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
+    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -194,6 +235,8 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
+      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
+      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -203,6 +246,8 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        std::cerr << "test4 expected padding should be 0: " << std::endl;
+        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -211,6 +256,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test4 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -220,14 +267,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -236,11 +289,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test5 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test5 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -262,10 +319,13 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
+  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
+  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
+  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 0;
+  return 1;
 }

>From 8c622e41d2428f630855f0aaa215b1463d8fe4ca Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:38:19 +0100
Subject: [PATCH 24/31] Revert "debug"

This reverts commit 8be9a83ee040faff62f0756555890484bf502a31.
---
 .../atomics.types.generic/padding.pass.cpp    | 64 +------------------
 1 file changed, 2 insertions(+), 62 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 3b7f13b366e96..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,7 +15,6 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
-#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -71,19 +70,11 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)obj;
+  (void)f;
   (void)pad_byte;
 #endif
 }
 
-template <class T>
-void print_memory(const T& obj) {
-  for (size_t i = 0; i < sizeof(T); ++i) {
-    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
-  }
-  std::cerr << std::endl;
-}
-
 template <class T>
 void test() {
   {
@@ -93,15 +84,11 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
-    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
-    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -109,8 +96,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -121,8 +106,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test1 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -132,20 +115,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -154,15 +131,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test2 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test2 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -171,15 +144,11 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
-    std::cerr << "test3 init padding should be 0x00: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
-    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -187,8 +156,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
-    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -199,8 +166,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test3 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -210,21 +175,15 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
-    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
-    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -235,8 +194,6 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
-      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
-      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -246,8 +203,6 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        std::cerr << "test4 expected padding should be 0: " << std::endl;
-        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -256,8 +211,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test4 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -267,20 +220,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -289,15 +236,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test5 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test5 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -319,13 +262,10 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
-  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
-  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
-  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 1;
+  return 0;
 }

>From 789912ba84ccbb3a3efea236fde31835783a7415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:44:00 +0100
Subject: [PATCH 25/31] test ci

---
 .../atomics.types.generic/padding.pass.cpp    | 20 ++++++++++++++-----
 .../atomics.types.generic/padding.pass.cpp    | 12 +----------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 116a860b3192c..cd41f2893864c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -71,6 +71,16 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
+void assert_padding(const std::atomic<T>& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  T loaded     = obj.load();
+  set(reference, loaded.i, loaded.c);
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
+}
+
 template <class T>
 void test() {
   {
@@ -79,7 +89,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 
   {
@@ -91,7 +101,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::store
@@ -103,7 +113,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::exchange
@@ -121,7 +131,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic_init
@@ -133,7 +143,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..10c15523cd70b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -70,7 +70,7 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
@@ -105,8 +105,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -135,8 +133,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
   {
     // compare_exchange_strong
@@ -165,8 +161,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -210,8 +204,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -240,8 +232,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {

>From 81770cda9f2171565c1c2980ad8306b9665752b2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:18:49 +0100
Subject: [PATCH 26/31] padding

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index cd41f2893864c..a178ce74578ec 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -127,7 +127,6 @@ void test() {
     T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_padding(old, 0);
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');

>From b90bec87725f4bde5722a3aa857a5c9d22c275e5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:40:31 +0100
Subject: [PATCH 27/31] clang22

---
 .../libcxx/atomics/atomics.types.generic/padding.pass.cpp    | 5 ++---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 4 +---
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index a178ce74578ec..48ae68a8e97ba 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -147,11 +148,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
+
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 10c15523cd70b..dc0afa439dc12 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
@@ -250,12 +251,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
 
   return 0;
 }

>From 05729850c8bf62df54a60b12201ac475e5c55fb5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 13:26:21 +0100
Subject: [PATCH 28/31] cxx 03

---
 libcxx/include/__atomic/clear_padding.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 3099faa8ca6d1..dad48ed27e6a4 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -32,23 +32,23 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
-           _Not<is_same<_Tp, float>>,
-           _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)> >,
+           _Not<is_same<_Tp, float> >,
+           _Not<is_same<_Tp, double> > > {};
 
-template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   return __obj;
 }
 
-template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp> >::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);

>From 2d00c0b818d180cc4d991508be2ad3dd8b7ee878 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:26:21 +0100
Subject: [PATCH 29/31] ci

---
 libcxx/include/__atomic/clear_padding.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index dad48ed27e6a4..1a4223fd97763 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -37,12 +37,12 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double> > > {};
 
 template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
   return __obj;
 }
 
 template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 

>From 30d5c984f33655f34946afb790bdc85c1f29d7a4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 15:49:25 +0100
Subject: [PATCH 30/31] ci

---
 libcxx/include/__atomic/clear_padding.h | 31 +++++++++++++++----------
 1 file changed, 19 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 1a4223fd97763..be55837cd8ec8 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -46,20 +46,27 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __ob
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 
-template <class _Tp, class _Up, class _CasFunc>
+template <class _Tp,
+          class _Up,
+          class _CasFunc,
+          __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
+}
+
+template <class _Tp,
+          class _Up,
+          class _CasFunc,
+          __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp> >::value) {
-    return __cas_func(__expected, __value);
+  std::__clear_padding_if_needed(__value);
+  __remove_cvref_t<_Tp> __expected_copy = *__expected;
+  std::__clear_padding_if_needed(__expected_copy);
+  if (__cas_func(std::addressof(__expected_copy), __value)) {
+    return true;
   } else {
-    std::__clear_padding_if_needed(__value);
-    __remove_cvref_t<_Tp> __expected_copy = *__expected;
-    std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), __value)) {
-      return true;
-    } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
-      return false;
-    }
+    std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
+    return false;
   }
 }
 

>From 2c566e60184cacd4536478332d52b88f196d12b6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 17:01:17 +0100
Subject: [PATCH 31/31] ci

---
 .../libcxx/atomics/atomics.types.generic/padding.pass.cpp   | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 48ae68a8e97ba..65b890b75d1de 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -23,6 +23,8 @@
 #include <cstring>
 #include <type_traits>
 
+#include "test_macros.h"
+
 struct WithTailPadding {
   int i;
   char c;
@@ -87,10 +89,12 @@ void test() {
   {
     // atomic();
     std::atomic<T> a;
+    assert_padding(a, 0);
+#if TEST_STD_VER >= 20
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_padding(a, 0);
+#endif
   }
 
   {


From libcxx-commits at lists.llvm.org  Wed Jul 22 09:03:59 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Wed, 22 Jul 2026 09:03:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a60e9ef.492e23e4.292f20.d481@mx.google.com>


https://github.com/lknknm converted_to_draft https://github.com/llvm/llvm-project/pull/210075

From libcxx-commits at lists.llvm.org  Wed Jul 22 09:49:04 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 09:49:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove the
 _LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF opt-out (PR
 #211313)
Message-ID: <llvm.org/llvm/llvm-project/pull/211313@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/211313

The escape hatch was provided in LLVM 23 to make bitset::operator[] const return __const_reference instead of the conforming bool. There is no evidence it was useful, so remove it as announced for LLVM 24.

>From f18d7b363d3d1a1fa017948717b46aff5649a2c2 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 22 Jul 2026 10:09:22 -0400
Subject: [PATCH] [libc++] Remove the
 _LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF opt-out

The escape hatch was provided in LLVM 23 to make bitset::operator[] const
return __const_reference instead of the conforming bool. There is no evidence
it was useful, so remove it as announced for LLVM 24.
---
 libcxx/docs/ReleaseNotes/24.rst               |  2 +-
 libcxx/include/__cxx03/bitset                 |  5 --
 libcxx/include/bitset                         |  9 ---
 .../template.bitset/index_const.pass.cpp      | 63 -------------------
 4 files changed, 1 insertion(+), 78 deletions(-)
 delete mode 100644 libcxx/test/libcxx/utilities/template.bitset/index_const.pass.cpp

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..9154080e6cbbf 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -67,7 +67,7 @@ ABI Affecting Changes
 - TODO: ``std::allocator`` is trivially default constructible since LLVM 22. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR``
   macro was provided as an escape hatch, but it has been removed in LLVM 24 since there was no evidence of it being useful.
 
-- TODO: ``bitset::operator[]`` returns ``bool`` since LLVM 22, fixing a conformance bug. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``
+- ``bitset::operator[]`` returns ``bool`` since LLVM 22, fixing a conformance bug. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``
   macro was provided as an escape hatch, but it has been removed in LLVM 24 since there was no evidence of it being useful.
 
 Build System Changes
diff --git a/libcxx/include/__cxx03/bitset b/libcxx/include/__cxx03/bitset
index f97431dd5fa09..d3a77de410163 100644
--- a/libcxx/include/__cxx03/bitset
+++ b/libcxx/include/__cxx03/bitset
@@ -567,7 +567,6 @@ public:
 
 public:
   typedef typename __base::reference reference;
-  typedef typename __base::__const_reference __const_reference;
 
   // 23.3.5.1 constructors:
   _LIBCPP_HIDE_FROM_ABI bitset() _NOEXCEPT {}
@@ -612,11 +611,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI bitset& flip(size_t __pos);
 
   // element access:
-#ifndef _LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF
   _LIBCPP_HIDE_FROM_ABI bool operator[](size_t __p) const { return __base::__make_ref(__p); }
-#else
-  _LIBCPP_HIDE_FROM_ABI __const_reference operator[](size_t __p) const { return __base::__make_ref(__p); }
-#endif
   _LIBCPP_HIDE_FROM_ABI reference operator[](size_t __p) { return __base::__make_ref(__p); }
   _LIBCPP_HIDE_FROM_ABI unsigned long to_ulong() const;
   _LIBCPP_HIDE_FROM_ABI unsigned long long to_ullong() const;
diff --git a/libcxx/include/bitset b/libcxx/include/bitset
index fd638daea8c73..34a29a92ad9e7 100644
--- a/libcxx/include/bitset
+++ b/libcxx/include/bitset
@@ -618,7 +618,6 @@ public:
   static const unsigned __n_words = _Size == 0 ? 0 : (_Size - 1) / (sizeof(size_t) * CHAR_BIT) + 1;
   typedef __bitset<__n_words, _Size> __base;
   typedef typename __base::reference reference;
-  typedef typename __base::__const_reference __const_reference;
 
   // 23.3.5.1 constructors:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bitset() _NOEXCEPT {}
@@ -680,18 +679,10 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bitset& flip(size_t __pos);
 
   // element access:
-  // TODO(LLVM 24): Remove the opt-out
-#  ifndef _LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool operator[](size_t __p) const {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__p < _Size, "bitset::operator[] index out of bounds");
     return __base::__make_ref(__p);
   }
-#  else
-  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __const_reference operator[](size_t __p) const {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__p < _Size, "bitset::operator[] index out of bounds");
-    return __base::__make_ref(__p);
-  }
-#  endif
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 reference operator[](size_t __p) {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__p < _Size, "bitset::operator[] index out of bounds");
     return __base::__make_ref(__p);
diff --git a/libcxx/test/libcxx/utilities/template.bitset/index_const.pass.cpp b/libcxx/test/libcxx/utilities/template.bitset/index_const.pass.cpp
deleted file mode 100644
index 4b2334b8a482b..0000000000000
--- a/libcxx/test/libcxx/utilities/template.bitset/index_const.pass.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// constexpr bool operator[](size_t pos) const; // constexpr since C++23
-
-// Make sure that `_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF` reverts to the old behaviour.
-
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF
-
-#include <bitset>
-#include <cassert>
-#include <cstddef>
-#include <vector>
-
-#include "../../../std/utilities/template.bitset/bitset_test_cases.h"
-#include "test_macros.h"
-
-template <std::size_t N>
-TEST_CONSTEXPR_CXX23 void test_index_const() {
-  std::vector<std::bitset<N> > const cases = get_test_cases<N>();
-  for (std::size_t c = 0; c != cases.size(); ++c) {
-    std::bitset<N> const v = cases[c];
-    if (v.size() > 0) {
-      assert(v[N / 2] == v.test(N / 2));
-    }
-  }
-  ASSERT_SAME_TYPE(decltype(cases[0][0]), typename std::bitset<N>::__const_reference);
-}
-
-TEST_CONSTEXPR_CXX23 bool test() {
-  test_index_const<0>();
-  test_index_const<1>();
-  test_index_const<31>();
-  test_index_const<32>();
-  test_index_const<33>();
-  test_index_const<63>();
-  test_index_const<64>();
-  test_index_const<65>();
-
-  std::bitset<1> set_;
-  set_[0]         = false;
-  const auto& set = set_;
-  auto b          = set[0];
-  set_[0]         = true;
-  assert(b);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  test_index_const<1000>(); // not in constexpr because of constexpr evaluation step limits
-#if TEST_STD_VER > 20
-  static_assert(test());
-#endif
-
-  return 0;
-}


From libcxx-commits at lists.llvm.org  Wed Jul 22 09:52:12 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 09:52:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove workaround for Clang < 20
 in clang-tidy plugin (PR #211314)
Message-ID: <llvm.org/llvm/llvm-project/pull/211314@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/211314

The clang-tidy plugin is now always built with Clang >= 20, so the workaround can be removed.

>From 2f83ae7356e61f23f76e3f4c3b33a59da6a70479 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 22 Jul 2026 10:10:26 -0400
Subject: [PATCH] [libc++] Remove workaround for Clang < 20 in clang-tidy
 plugin

The clang-tidy plugin is now always built with Clang >= 20, so the
workaround can be removed.
---
 .../robust_against_operator_ampersand.cpp                 | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index 777dd6fe11df5..24089e33ca1c0 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -25,14 +25,6 @@
 // This is part of libc++'s policy
 // https://libcxx.llvm.org/CodingGuidelines.html#don-t-use-argument-dependent-lookup-unless-required-by-the-standard
 
-// TODO(LLVM-21) Remove dependentScopeDeclRefExpr
-// dependentScopeDeclRefExpr requires Clang 20, this uses the same definition as Clang
-#if defined(__clang_major__) && __clang_major__ < 20
-namespace clang::ast_matchers {
-const internal::VariadicDynCastAllOfMatcher<Stmt, DependentScopeDeclRefExpr> dependentScopeDeclRefExpr;
-} // namespace clang::ast_matchers
-#endif
-
 namespace libcpp {
 robust_against_operator_ampersand::robust_against_operator_ampersand(
     llvm::StringRef name, clang::tidy::ClangTidyContext* context)


From libcxx-commits at lists.llvm.org  Wed Jul 22 10:51:08 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 10:51:08 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove the
 _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR opt-out (PR #211329)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211329@github.com>
Message-ID: <6a61030c.8e75e0cb.2de827.fac1@mx.google.com>


https://github.com/philnik777 commented:

Do we want to do this now? This probably hasn't been super widely deployed yet, especially not by people who care about ABI stability.

FWIW I'd probably wait at least until after the 24.1 release, but personally I don't care much either way.

https://github.com/llvm/llvm-project/pull/211329

From libcxx-commits at lists.llvm.org  Wed Jul 22 10:51:39 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Wed, 22 Jul 2026 10:51:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove workaround for Clang < 20
 in clang-tidy plugin (PR #211314)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211314@github.com>
Message-ID: <6a61032b.4a342cbc.1aeb63.6f31@mx.google.com>


https://github.com/philnik777 approved this pull request.


https://github.com/llvm/llvm-project/pull/211314

From libcxx-commits at lists.llvm.org  Wed Jul 22 12:46:59 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Wed, 22 Jul 2026 12:46:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][CI] Add a Github workflow
 to test libc++ tools (PR #211067)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211067@github.com>
Message-ID: <6a611e33.4987c784.13f2de.70d2@mx.google.com>


ldionne wrote:

> Do we need a new dedicated GitHub workflow for this or can we add it to the existing libc++ workflow?
> 
> https://github.com/llvm/llvm-project/blob/18220e793fbda204b66c08989cde11ad4583f26c/.github/workflows/libcxx-build-and-test.yaml#L88

We can, but we'd have to use

```
fetch-depth: 0 # some tools need full git history
fetch-tags: true
```

on all the jobs of the same stage (unless we want to make things more complicated). If you still think this is better, I can do that, but my thinking was actually to split up workflows that do fundamentally different things since our main `build-and-test` workflow has gotten fairly large.

https://github.com/llvm/llvm-project/pull/211067

From libcxx-commits at lists.llvm.org  Wed Jul 22 12:56:03 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Wed, 22 Jul 2026 12:56:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::mismatch() based on __pstl::__parallel_find() (PR #209291)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209291@github.com>
Message-ID: <6a612053.c5504292.1bbfc8.c989@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/209291

>From 31208d7946efae8b2e45a121c5178562a7b639c8 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:03:38 +0100
Subject: [PATCH 01/15] Added a basic implementation of parallel 4-legged
 mismatch() based on __parallel_find()

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/libdispatch.h  |   7 +-
 libcxx/include/__pstl/backends/serial.h       |  20 +++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  88 +++++++++++
 .../mismatch/pstl.mismatch.pass.cpp           | 139 ++++++++++++++++++
 7 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__pstl/cpu_algos/mismatch.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..6b5dc87fbe76d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/mismatch.h  
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 2ba5b4434fc33..aa7633a240722 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -324,6 +324,14 @@ struct __adjacent_difference;
 // operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index f7141d24df090..5e89c6fe31cd8 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -37,6 +37,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -241,7 +242,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
       auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
       auto __index           = __chunk == 0 ? 0
                                             : (__chunk * __partitions.__chunk_size_) +
-                                        (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
+                                                  (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
       if (__this_chunk_size != 1) {
         std::__construct_at(
             __values.get() + __chunk,
@@ -368,6 +369,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..d90022ee37145 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/mismatch.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    return std::mismatch(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::forward<_Predicate>(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..93935d22b9442 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
new file mode 100644
index 0000000000000..11ffdc332d633
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_MISMATCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
+
+#include <__algorithm/mismatch.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_mismatch {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Look for a mismatch only in the prefix of the two ranges.
+      auto __n = std::min(__last1 - __first1, __last2 - __first2);
+      // Find a position in the first range where the predicate is false against the corresponding position in the
+      // second range.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __first1 + __n,
+          [&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
+            // Run the sequential mismatch algorithm on these ranges:
+            //   [__brick_first1, __brick_last1) and
+            //   [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
+            auto __brick_first2 = __first2 + (__brick_first1 - __first1);
+            return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
+                .first;
+          },
+          less<>{},
+          true);
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      auto __idx = *__res - __first1;
+      return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      // Unsequenced execution is also implicitly covered by the sequential implementation.
+      return std::mismatch(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
new file mode 100644
index 0000000000000..9cbadda428ad9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 1};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(42);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From ddbcca0bf2d23ac5caaed98c0912016bb23c7640 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:05:01 +0100
Subject: [PATCH 02/15] Added a definition of a parallel 4-legged
 std::mismatch()

---
 libcxx/include/__algorithm/pstl.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2bf9b4276baab..ab35b1a3e537b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -138,6 +138,29 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,

>From 81cdb4aa0a748c2a8039e0c478c19259860513a7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:41:51 +0100
Subject: [PATCH 03/15] Added a default implementation of __mismatch_3leg based
 on __mismatch

---
 libcxx/include/__algorithm/pstl.h             | 18 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++++
 libcxx/include/__pstl/backends/default.h      | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../mismatch/pstl.mismatch.pass.cpp           | 20 +++++++++++
 5 files changed, 84 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index ab35b1a3e537b..01c61fb00f4d5 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -161,6 +161,24 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index aa7633a240722..ecd1f0b9b4049 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -332,6 +332,13 @@ struct __mismatch;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch_3leg;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 28bc75be147aa..b6a15b234b8da 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -63,6 +63,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// mismatch family
+// ---------------
+// - mismatch_3leg
+//
 // for_each family
 // ---------------
 // - for_each_n
@@ -212,6 +216,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// mismatch family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+  optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _Comp __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Forward to the 4-legged version of mismatch.
+      using _Mismatch           = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      _ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
+      return _Mismatch()(
+          __policy,
+          std::move(__first1),
+          std::move(__last1),
+          std::move(__first2),
+          std::move(__last2),
+          std::move(__comp));
+    } else {
+      // Currently only random access iterators are supported for parallel mismatch_3leg.
+      return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..752aa35643e2b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2384,6 +2384,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module mismatch {
+        header "__pstl/cpu_algos/mismatch.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 9cbadda428ad9..0cb5c7632eed8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -10,6 +10,14 @@
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2);
+//
 // template <class ExecutionPolicy,
 //           class ForwardIterator1,
 //           class ForwardIterator2>
@@ -61,6 +69,8 @@ struct Test {
       // empty ranges
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
              std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
     }
@@ -68,18 +78,24 @@ struct Test {
       // single element only
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range without mismatch
       std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range with mismatch
       std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
     }
@@ -102,10 +118,14 @@ struct Test {
       rhs = lhs;
       runway_sample(lhs.size(), [&](size_t i) {
         lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
                std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         lhs[i] = i;
       });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }

>From d5910cc36d457006d17cf1adad74a94ee5a4e759 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:20:42 +0100
Subject: [PATCH 04/15] Added predicated versions

---
 libcxx/include/__algorithm/pstl.h             |  49 +++++
 .../mismatch/pstl.mismatch.pass.cpp           |   2 +
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 178 ++++++++++++++++++
 3 files changed, 229 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 01c61fb00f4d5..bc48fd9f48d7f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
+#  include <__utility/pair.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -138,6 +139,31 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
@@ -161,6 +187,29 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 0cb5c7632eed8..8584f2779927f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -42,6 +42,8 @@
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
+static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
new file mode 100644
index 0000000000000..39e952cbc38f6
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     BinaryPredicate pred);
+//
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2,
+//                                                     BinaryPredicate pred);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+struct Pred {
+  bool operator()(int a, int b) const { return a * 2 == b; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      assert(std::mismatch(
+                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {1};
+      std::array<int, 1> rhs = {2};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 2};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(84);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From 9b207d1481c9076b4bb2af97d049845f2c4e16e0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:39:47 +0100
Subject: [PATCH 05/15] Added tests against custom types

---
 libcxx/include/__pstl/backends/default.h      |  1 +
 .../mismatch/pstl.mismatch.pass.cpp           | 53 ++++++++++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 56 ++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index b6a15b234b8da..776c76f3fdfbd 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -16,6 +16,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 8584f2779927f..5e46831539c30 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -63,6 +63,30 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
+
 template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
@@ -150,6 +174,29 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -157,5 +204,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 39e952cbc38f6..9c0dfc6c0c127 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -67,8 +67,31 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
 struct Pred {
-  bool operator()(int a, int b) const { return a * 2 == b; }
+  bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
+  bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
 };
 
 template <class Iter1, class Iter2>
@@ -167,6 +190,31 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -174,5 +222,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }

>From c21c583527e255d722f312b871d43527e5c4dbb6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:52:50 +0100
Subject: [PATCH 06/15] Added a nodiscard test

---
 libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index f4fffd456a331..f5eaf1642b687 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -63,4 +63,12 @@ void test() {
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }

>From 189ba7a5f23d45939e0ff917985bc7049740d1b7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:57:47 +0100
Subject: [PATCH 07/15] Added an iterator requirement test

---
 .../pstl.iterator-requirements.verify.cpp        | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 402d8fbf9131a..df11341008bc5 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, pred);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}

>From 731de4a2cf2c196b704234b0ee5653453554b9b7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:00:09 +0100
Subject: [PATCH 08/15] Added an include of <__algorithm/min.h>

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 11ffdc332d633..8361731e78cfc 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,8 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__algorithm/mismatch.h>
 #include <__config>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/min.h>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>

>From 105a17ed5abc39ad5ac34bd930af8727a9562bed Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:05:29 +0100
Subject: [PATCH 09/15] Added an exception handling test

---
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  4 +--
 .../pstl.exception_handling.pass.cpp          | 25 +++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 8361731e78cfc..597a47fb185c8 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,9 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__config>
-#include <__algorithm/mismatch.h>
 #include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__config>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index e00046fdb1dd6..cbdb4663f168c 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -248,6 +248,31 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // mismatch(first1, last1, first2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
+        });
+
+        // mismatch(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // mismatch(first1, last1, first2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
+        });
+
+        // mismatch(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From 3bd32433db95c6995476e26d5b34c8000a5515f5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 09:43:45 +0100
Subject: [PATCH 10/15] Changed the variable name to avoid shadowing

---
 .../alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 12 ++++++------
 .../mismatch/pstl.mismatch_pred.pass.cpp             | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 5e46831539c30..941fe07b58327 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -67,22 +67,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 9c0dfc6c0c127..a4b15a56bd071 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -71,22 +71,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Pred {

>From a51af3556cdb7f7e24732077b6dce511e3ee9e89 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 20:27:14 +0100
Subject: [PATCH 11/15] Use raw C array in the tests

---
 .../mismatch/pstl.mismatch.pass.cpp           | 153 +++++++-------
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 192 +++++++++++-------
 2 files changed, 198 insertions(+), 147 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 941fe07b58327..17edf34ee7832 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -28,7 +28,6 @@
 //                                                     ForwardIterator2 last2);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -93,82 +92,98 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(
+          std::mismatch(
+              policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
+          std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 1};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 1};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(
+            std::mismatch(
+                policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+            std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(42);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 42);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(
+                   policy,
+                   Iter1(std::begin(lhs)),
+                   Iter1(std::begin(lhs) + i),
+                   Iter2(std::begin(rhs)),
+                   Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i)) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -179,20 +194,22 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index a4b15a56bd071..18c4fe1abad0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -32,7 +32,6 @@
 //                                                     BinaryPredicate pred);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -100,91 +99,120 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(
-                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::begin(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::begin(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {1};
-      std::array<int, 1> rhs = {2};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {1};
+      int rhs[1] = {2};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 2};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 2};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(84);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 84);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::begin(lhs) + i),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -195,22 +223,28 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };

>From f3bfa5528fb2aea972a018cc68e56133e32db99b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:08:55 +0100
Subject: [PATCH 12/15] Fixed typos in the comment

---
 .../algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 2 +-
 .../alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 17edf34ee7832..ed59fa9eca669 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -46,7 +46,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 18c4fe1abad0c..338503d784a31 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -50,7 +50,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;

>From 4c7fccd82c3a4bea33038c9eee3ebcefddd7f230 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:43:20 +0100
Subject: [PATCH 13/15] Added comments explaining what the parameters mean

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 597a47fb185c8..3d702b03d3afe 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -63,8 +63,9 @@ struct __cpu_parallel_mismatch {
             return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
                 .first;
           },
-          less<>{},
-          true);
+          less<>{}, // `less` here means the lowest index among the mismatches
+          true      // `true` here means we want the first mismatch, not the last
+      );
       if (!__res) {
         return std::nullopt; // Failed to run the algorithm, propagate the error.
       }

>From 22dfeede2c12c49a9f57db064f80e6d24e96f41b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:50:51 +0100
Subject: [PATCH 14/15] Added tests of the return types

---
 .../mismatch/pstl.mismatch.pass.cpp              | 10 ++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp         | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index ed59fa9eca669..cc7ef6feb2356 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -90,6 +90,16 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1]       = {0};
+      int rhs[1]       = {0};
+      auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
+      auto res_4legged = std::mismatch(
+          policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 338503d784a31..d80da0cf0b542 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -97,6 +97,22 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      auto res_3legged =
+          std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
+      auto res_4legged = std::mismatch(
+          policy,
+          Iter1(std::begin(lhs)),
+          Iter1(std::begin(lhs)),
+          Iter2(std::begin(rhs)),
+          Iter2(std::begin(rhs)),
+          Pred{});
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};

>From 8f9ddba49cfc2b501d8d42b1c8027f64b0695a31 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 21 Jul 2026 19:39:00 +0100
Subject: [PATCH 15/15] Rebased on support/runway_sample.h

---
 .../mismatch/pstl.mismatch.pass.cpp             | 17 +----------------
 .../mismatch/pstl.mismatch_pred.pass.cpp        | 17 +----------------
 2 files changed, 2 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index cc7ef6feb2356..83f4b0f16d6c1 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -38,6 +38,7 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
@@ -46,22 +47,6 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 // The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
 
 struct X {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index d80da0cf0b542..89cf899a47b4c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -42,6 +42,7 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
@@ -50,22 +51,6 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 // The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
 
 struct X {


From libcxx-commits at lists.llvm.org  Wed Jul 22 22:47:47 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 22:47:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a61ab03.0c05e68e.81651.ac99@mx.google.com>


================
@@ -555,6 +556,22 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI shared_ptr {
 
   _LIBCPP_HIDE_FROM_ABI bool __owner_equivalent(const shared_ptr& __p) const { return __cntrl_ == __p.__cntrl_; }
 
+#if _LIBCPP_STD_VER >= 26
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t owner_hash() const _NOEXCEPT {
----------------
Zingam wrote:

Same as the above comment. Generally, when possible avoid using the macros in newer code if there are non-macro alternatives (see bellow).

Every part of a function signatures needs to be tested. `[[nodiscard]]`, return type, `const`-ness, `noexcept`-ness, etc.
The `[[nodiscard]]` test cases live in files in the `tests/libcxx` directory as they are libc++ specific.

`[[__nodiscard__]]` is a Clang extension used in pre C++17 code.

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Wed Jul 22 22:47:47 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 22:47:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a61ab03.4a342cbc.1aeb63.325d@mx.google.com>


================
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// shared_ptr
+
+// template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
----------------
Zingam wrote:

```suggestion
#include <cassert>
#include <memory>

#include "test_macros.h"
```

`#include`'s should be sorted and grouped manually. Here and bellow.

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Wed Jul 22 22:47:47 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 22:47:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a61ab03.c4d3d80a.16dae5.6689@mx.google.com>


================
@@ -633,6 +633,9 @@ public:
     explicit operator bool() const noexcept;
     template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
     template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;
+    size_t owner_hash() const noexcept;                                       // C++26
+    template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
+    template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;   // C++26
----------------
Zingam wrote:

```suggestion
    size_t owner_hash() const noexcept;                                        // C++26
    template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept; // C++26
    template<class U> bool owner_equal(weak_ptr<U> const& b) const noexcept;   // C++26
```
Nit: please ident. Also bellow.

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Wed Jul 22 22:47:48 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 22:47:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a61ab04.c4d3d80a.16dae5.672f@mx.google.com>


================
@@ -817,6 +823,32 @@ struct owner_less<void>
     typedef void is_transparent;
 };
 
+// class owner_hash:                                                          // C++26
+struct owner_hash
+{
+    template <class _Tp>
+    size_t operator()(shared_ptr<_Tp> const& __p) const noexcept;
+    template <class _Tp>
+    size_t operator()(weak_ptr<_Tp> const& __p) const noexcept;
+
+    typedef void is_transparent;
----------------
Zingam wrote:

The synopsis should be copied from the standard preferably.

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Wed Jul 22 22:47:48 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 22:47:48 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P1901R2: owner_hash
 and owner_equal for shared_ptr/weak_ptr (PR #210551)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210551@github.com>
Message-ID: <6a61ab04.21a19891.105e40.8f74@mx.google.com>


================
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// shared_ptr
+
+// template<class U> bool owner_equal(shared_ptr<U> const& b) const noexcept;
+
+#include <memory>
+#include <cassert>
+#include "test_macros.h"
+
+int main(int, char**) {
+  const std::shared_ptr<int> p1(new int);
+  const std::shared_ptr<int> p2 = p1;
+  const std::shared_ptr<int> p3(new int);
+  const std::shared_ptr<void> empty1;
+  const std::shared_ptr<long> empty2;
+
+  assert(p1.owner_equal(p2));
+  assert(p2.owner_equal(p1));
+  assert(!p1.owner_equal(p3));
+  assert(!p3.owner_equal(p1));
+
+  assert(empty1.owner_equal(empty2));
+  assert(!p1.owner_equal(empty1));
+
+  ASSERT_SAME_TYPE(decltype(p1.owner_equal(p2)), bool);
+  ASSERT_NOEXCEPT(p1.owner_equal(p2));
----------------
Zingam wrote:

Nit: The above is correct but in newer test when possible we prefer to use the non-macro versions. For example the following patterns:

```suggestion
  std::same_as<bool> decltype(auto) result = p1.owner(p2);
  assert(result));
  static_assert(noexcept(p1.owner_equal(p2)));
  
  ...etc...
```

There is no specific order required but you can order them for easier reading.

https://github.com/llvm/llvm-project/pull/210551

From libcxx-commits at lists.llvm.org  Wed Jul 22 22:56:23 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 22:56:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a61ad07.f1c1923d.d9163.f360@mx.google.com>


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/193891

>From 951f99a8ca17f91c61eae306125769cfa0cbebf3 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 3 Apr 2026 08:43:06 +0300
Subject: [PATCH 01/32] [libc++][ranges] P3059R2: Making user-defined
 constructors of view iterators/sentinels private

Implements https://wg21.link/P3059R2

Closes #189599
---
 libcxx/include/__ranges/elements_view.h   | 12 ++++++++----
 libcxx/include/__ranges/filter_view.h     | 20 ++++++++++++-------
 libcxx/include/__ranges/iota_view.h       | 15 ++++++++------
 libcxx/include/__ranges/istream_view.h    |  8 +++++---
 libcxx/include/__ranges/join_view.h       |  6 ++++--
 libcxx/include/__ranges/lazy_split_view.h | 24 +++++++++++++----------
 libcxx/include/__ranges/split_view.h      | 18 ++++++++++-------
 libcxx/include/__ranges/take_view.h       |  6 ++++--
 libcxx/include/__ranges/take_while_view.h |  8 +++++---
 libcxx/include/__ranges/transform_view.h  | 14 ++++++++-----
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
index 33c152bbd03b4..706744ae652e1 100644
--- a/libcxx/include/__ranges/elements_view.h
+++ b/libcxx/include/__ranges/elements_view.h
@@ -165,6 +165,8 @@ template <input_range _View, size_t _Np>
 template <bool _Const>
 class elements_view<_View, _Np>::__iterator
     : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __iterator;
 
@@ -175,6 +177,8 @@ class elements_view<_View, _Np>::__iterator
 
   iterator_t<_Base> __current_ = iterator_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
     if constexpr (is_reference_v<range_reference_t<_Base>>) {
       return std::get<_Np>(*__i);
@@ -205,8 +209,6 @@ class elements_view<_View, _Np>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __current_(std::move(__i.__current_)) {}
@@ -339,6 +341,10 @@ class elements_view<_View, _Np>::__sentinel {
   using _Base _LIBCPP_NODEBUG                        = __maybe_const<_Const, _View>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class elements_view<_View, _Np>;
+
   template <bool>
   friend class __sentinel;
 
@@ -350,8 +356,6 @@ class elements_view<_View, _Np>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__other.__end_)) {}
diff --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index 7ec53dac4a84f..3004e830173f1 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -127,10 +127,16 @@ struct __filter_iterator_category<_View> {
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<_View> {
-public:
+private:
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
   _LIBCPP_NO_UNIQUE_ADDRESS filter_view* __parent_       = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
+      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
+
+  friend class filter_view<_View, _Pred>;
+
+public:
   using iterator_concept =
       _If<bidirectional_range<_View>,
           bidirectional_iterator_tag,
@@ -145,9 +151,6 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
     requires default_initializable<iterator_t<_View>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(filter_view& __parent, iterator_t<_View> __current)
-      : __current_(std::move(__current)), __parent_(std::addressof(__parent)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -211,13 +214,16 @@ class filter_view<_View, _Pred>::__iterator : public __filter_iterator_category<
 template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
   requires view<_View> && is_object_v<_Pred>
 class filter_view<_View, _Pred>::__sentinel {
-public:
+private:
   sentinel_t<_View> __end_ = sentinel_t<_View>();
 
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(filter_view& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
+  friend class filter_view<_View, _Pred>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __sentinel const& __y) {
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 38e0ccaf2849e..9d87138d29e23 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -110,6 +110,12 @@ template <weakly_incrementable _Start, semiregular _BoundSentinel = unreachable_
   requires __weakly_equality_comparable_with<_Start, _BoundSentinel> && copyable<_Start>
 class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   struct __iterator : public __iota_iterator_category<_Start> {
+  private:
+    _Start __value_ = _Start();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
+
+  public:
     friend class iota_view;
 
     using iterator_concept =
@@ -124,14 +130,10 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
     using value_type      = _Start;
     using difference_type = _IotaDiffT<_Start>;
 
-    _Start __value_ = _Start();
-
     _LIBCPP_HIDE_FROM_ABI __iterator()
       requires default_initializable<_Start>
     = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Start __value) : __value_(std::move(__value)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Start operator*() const
         noexcept(is_nothrow_copy_constructible_v<_Start>) {
       return __value_;
@@ -282,11 +284,12 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
   private:
     _BoundSentinel __bound_sentinel_ = _BoundSentinel();
 
-  public:
-    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_BoundSentinel __bound_sentinel)
         : __bound_sentinel_(std::move(__bound_sentinel)) {}
 
+  public:
+    _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
     _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
       return __x.__value_ == __y.__bound_sentinel_;
     }
diff --git a/libcxx/include/__ranges/istream_view.h b/libcxx/include/__ranges/istream_view.h
index 7c64463108e1a..1bee6e69f2186 100644
--- a/libcxx/include/__ranges/istream_view.h
+++ b/libcxx/include/__ranges/istream_view.h
@@ -66,9 +66,6 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
   using difference_type  = ptrdiff_t;
   using value_type       = _Val;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
-      : __parent_(std::addressof(__parent)) {}
-
   __iterator(const __iterator&)                  = delete;
   _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
 
@@ -91,9 +88,14 @@ class basic_istream_view<_Val, _CharT, _Traits>::__iterator {
 private:
   basic_istream_view<_Val, _CharT, _Traits>* __parent_;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(basic_istream_view<_Val, _CharT, _Traits>& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
   _LIBCPP_HIDE_FROM_ABI constexpr basic_istream<_CharT, _Traits>* __get_parent_stream() const {
     return __parent_->__stream_;
   }
+
+  friend class basic_istream_view<_Val, _CharT, _Traits>;
 };
 
 template <class _Val>
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index f80b75c839d4a..4f3f8d1c1e5b4 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -160,11 +160,13 @@ struct join_view<_View>::__sentinel {
   using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
   sentinel_t<_Base> __end_      = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+  friend class join_view<_View>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index be2b86772fa0d..9c44d26faf1e7 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -143,6 +143,8 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     template <bool>
     friend struct __inner_iterator;
     friend __outer_iterator<true>;
@@ -155,6 +157,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent();
     bool __trailing_empty_                             = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
+      requires(!forward_range<_Base>)
+        : __parent_(std::addressof(__parent)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
+      requires forward_range<_Base>
+        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __current() noexcept {
       if constexpr (forward_range<_View>) {
         return __current_;
@@ -196,14 +206,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __outer_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(_Parent& __parent)
-      requires(!forward_range<_Base>)
-        : __parent_(std::addressof(__parent)) {}
-
-    _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current)
-      requires forward_range<_Base>
-        : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
     _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator(__outer_iterator<!_Const> __i)
       requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
         : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
@@ -286,12 +288,16 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
   template <bool _Const>
   struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> {
   private:
+    friend class lazy_split_view<_View, _Pattern>;
+
     using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
     // Workaround for a GCC issue.
     static constexpr bool _OuterConst = _Const;
     __outer_iterator<_Const> __i_     = __outer_iterator<_OuterConst>();
     bool __incremented_               = false;
 
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
+
     // Note: these private functions are necessary because GCC doesn't allow calls to private members of `__i_` from
     // free functions that are friends of `inner-iterator`.
 
@@ -342,8 +348,6 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
 
     _LIBCPP_HIDE_FROM_ABI __inner_iterator() = default;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) : __i_(std::move(__i)) {}
-
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept {
       return __i_.__current();
     }
diff --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 10148e0e64764..7295e33a282f0 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -127,6 +127,12 @@ struct split_view<_View, _Pattern>::__iterator {
   _LIBCPP_NO_UNIQUE_ADDRESS subrange<iterator_t<_View>> __next_ = subrange<iterator_t<_View>>();
   bool __trailing_empty_                                        = false;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
+      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
+
+  friend class split_view<_View, _Pattern>;
+
   friend struct __sentinel;
 
 public:
@@ -137,10 +143,6 @@ struct split_view<_View, _Pattern>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      split_view<_View, _Pattern>& __parent, iterator_t<_View> __current, subrange<iterator_t<_View>> __next)
-      : __parent_(std::addressof(__parent)), __cur_(std::move(__current)), __next_(std::move(__next)) {}
-
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() const { return __cur_; }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { return {__cur_, __next_.begin()}; }
@@ -179,16 +181,18 @@ struct split_view<_View, _Pattern>::__sentinel {
 private:
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_View> __end_ = sentinel_t<_View>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
   _LIBCPP_HIDE_FROM_ABI static constexpr bool __equals(const __iterator& __x, const __sentinel& __y) {
     return __x.__cur_ == __y.__end_ && !__x.__trailing_empty_;
   }
 
+  friend class split_view<_View, _Pattern>;
+
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(split_view<_View, _Pattern>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __sentinel& __y) {
     return __equals(__x, __y);
   }
diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 999f686537f2c..ee62d49f71d8b 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -165,14 +165,16 @@ class take_view<_View>::__sentinel {
   using _Iter _LIBCPP_NODEBUG                        = counted_iterator<iterator_t<__maybe_const<_OtherConst, _View>>>;
   _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
+
+  friend class take_view<_View>;
+
   template <bool>
   friend class take_view<_View>::__sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)) {}
diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 0aee306807c8b..e77b79665d1f6 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -108,14 +108,16 @@ class take_while_view<_View, _Pred>::__sentinel {
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
   const _Pred* __pred_     = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
+      : __end_(std::move(__end)), __pred_(__pred) {}
+
+  friend class take_while_view<_View, _Pred>;
+
   friend class __sentinel<!_Const>;
 
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred)
-      : __end_(std::move(__end)), __pred_(__pred) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {}
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 06ace86bbfafa..807f1cf19a0ac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -192,6 +192,11 @@ class transform_view<_View, _Fn>::__iterator
 
   _Parent* __parent_ = nullptr;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
+      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -209,9 +214,6 @@ class transform_view<_View, _Fn>::__iterator
     requires default_initializable<iterator_t<_Base>>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, iterator_t<_Base> __current)
-      : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {}
-
   // Note: `__i` should always be `__iterator<false>`, but directly using
   // `__iterator<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).
@@ -354,6 +356,10 @@ class transform_view<_View, _Fn>::__sentinel {
 
   sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
+
+  friend class transform_view<_View, _Fn>;
+
   template <bool>
   friend class transform_view<_View, _Fn>::__iterator;
 
@@ -363,8 +369,6 @@ class transform_view<_View, _Fn>::__sentinel {
 public:
   _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(__end) {}
-
   // Note: `__i` should always be `__sentinel<false>`, but directly using
   // `__sentinel<false>` is ill-formed when `_Const` is false
   // (see http://wg21.link/class.copy.ctor#5).

>From e2773a4fad53687ac75bbde2907fe82f0ab1851b Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:45:01 +0300
Subject: [PATCH 02/32] Tests: `split_view`

---
 .../range.split/iterator/base.pass.cpp        | 28 +++++++++++++------
 .../range.split/iterator/ctor.base.pass.cpp   |  4 +--
 .../range.split/iterator/deref.pass.cpp       | 28 ++++++++++++-------
 .../range.split/sentinel/ctor.parent.pass.cpp |  5 +---
 4 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 325189a0e521e..66c2914ba1919 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,9 +16,20 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
@@ -26,34 +37,33 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+
   // const &
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitView sv;
-    SplitIter it{sv, Iter{5}, {}};
+    SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    SplitView sv;
-    const SplitIter it{sv, Iter{5}, {}};
+    const SplitIter it = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
index 20b3c19611bd0..823e22e117f77 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+// constexpr iterator begin();
 
 #include <cassert>
 #include <ranges>
@@ -35,7 +35,7 @@ constexpr bool test() {
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
   SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = {sv, sv.base().begin(), std::ranges::subrange<TracedMoveIter>{sv.base().begin(), sv.base().end()}};
+  SplitIter iter = sv.begin();
   assert(iter.base().moved);
 
   auto subRange = *iter;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index 721a1cc0da3d4..be7aca854f640 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,24 +17,32 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i;
+  int i = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
+  constexpr int operator*() const { return i; }
+  constexpr Iter& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr Iter operator++(int) {
+    Iter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const Iter& x, const Iter& y) { return x.i == y.i; }
 };
 
 constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  {
-    SplitView sv;
-    Iter current{5};
-    std::ranges::subrange next{Iter{6}, Iter{7}};
-    const SplitIter it{sv, current, next};
-    std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
-    assert(value.begin().i == 5);
-    assert(value.end().i == 6);
-  }
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
+               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it = sv.begin();
+  std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
+  assert(value.begin().i == 5);
+  assert(value.end().i == 7);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
index c89b1ee2bdfce..6e1ba912a201b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
@@ -21,9 +21,6 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
-
 constexpr bool test() {
   {
     int buffer[] = {0, 1, 2};
@@ -31,7 +28,7 @@ constexpr bool test() {
     SplitView sv(input, -1);
     auto it = sv.begin();
 
-    SplitSent sent(sv);
+    auto sent = sv.end();
     assert(sent != it);
 
     ++it;

>From 9c78f4a29eb264d141fcca3872cf33e6888a1992 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 6 Apr 2026 00:48:57 +0300
Subject: [PATCH 03/32] Tests: `take_view`

---
 .../range.take/range.take.sentinel/ctor.pass.cpp    | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
index 61d33760c71d7..3710be563c15b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 // sentinel() = default;
-// constexpr explicit sentinel(sentinel_t<Base> end);
 // constexpr sentinel(sentinel<!Const> s)
 //   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
 
@@ -49,18 +48,6 @@ constexpr bool test() {
     assert(std::as_const(tv).begin() + 4 == cs);
   }
 
-  {
-    // Test the constructor from "base-sentinel" to "sentinel".
-    using TakeView             = std::ranges::take_view<MoveOnlyView>;
-    using Sentinel             = std::ranges::sentinel_t<TakeView>;
-    sentinel_wrapper<int*> sw1 = MoveOnlyView(buffer).end();
-    static_assert(std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
-    static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);
-    auto s                                        = Sentinel(sw1);
-    std::same_as<sentinel_wrapper<int*>> auto sw2 = s.base();
-    assert(base(sw2) == base(sw1));
-  }
-
   return true;
 }
 

>From 1bc981d2099418d1f7e928f4d70aa775ede810fd Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:41:47 +0300
Subject: [PATCH 04/32] Tests: `take_while_view`

---
 .../sentinel/ctor.base.pass.cpp               | 86 -------------------
 .../sentinel/ctor.convert.pass.cpp            | 43 +++++++---
 2 files changed, 31 insertions(+), 98 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index b952534d1c966..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
-
-#include <cassert>
-#include <ranges>
-#include <utility>
-
-#include "../types.h"
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
-};
-
-struct Range : std::ranges::view_base {
-  int* begin() const;
-  Sent end();
-};
-
-struct Pred {
-  bool operator()(int i) const;
-};
-
-// Test explicit
-template <class T>
-void conversion_test(T);
-
-template <class T, class... Args>
-concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
-static_assert(ImplicitlyConstructible<int, int>);
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                      std::ranges::sentinel_t<Range>,
-                                      const Pred*>);
-static_assert(!ImplicitlyConstructible<std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>,
-                                       std::ranges::sentinel_t<Range>,
-                                       const Pred*>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::take_while_view<Range, bool (*)(int)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5}, nullptr);
-    assert(s1.base().i == 5);
-  }
-
-  // pred is init correctly
-  {
-    bool called = false;
-    auto pred   = [&](int) {
-      called = true;
-      return false;
-    };
-
-    using R        = std::ranges::take_while_view<Range, decltype(pred)>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s(Sent{0}, &pred);
-
-    bool b = iter == s;
-    assert(called);
-    assert(b);
-  }
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 865a3d45710fb..0fffe9b508816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -70,34 +70,48 @@ static_assert(!std::is_constructible_v<
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::take_while_view<Range, bool (*)(int)>;
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::take_while_view<TestRng, bool (*)(int)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{TestRng{}, nullptr};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
 
   // pred is init correctly
   {
+    struct TestRng : std::ranges::view_base {
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr ConstSent end() const { return ConstSent{0}; }
+    };
+
     bool called = false;
     auto pred   = [&](int) {
       called = true;
       return false;
     };
 
-    using R             = std::ranges::take_while_view<Range, decltype(pred)>;
+    using R             = std::ranges::take_while_view<TestRng, decltype(pred)>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    int i     = 10;
-    int* iter = &i;
-    Sentinel s1(Sent{0}, &pred);
+    R r{TestRng{}, pred};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
 
+    int i     = 10;
+    int* iter = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
@@ -111,20 +125,25 @@ constexpr bool test() {
       constexpr bool operator==(int* iter) const { return i > *iter; }
     };
 
+    struct TestPred {
+      constexpr bool operator()(int) const { return false; }
+    };
+
     struct Rng : std::ranges::view_base {
-      int* begin() const;
-      Sent end();
-      MoveOnlyConvert end() const;
+      constexpr int* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{0}; }
+      constexpr MoveOnlyConvert end() const { return MoveOnlyConvert(Sent{0}); }
     };
 
-    using R             = std::ranges::take_while_view<Rng, Pred>;
+    using R             = std::ranges::take_while_view<Rng, TestPred>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5}, nullptr);
+    R r{Rng{}, TestPred{}};
+    Sentinel s1 = r.end();
     ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    assert(s2.base().i == 0);
   }
 
   return true;

>From 6bdc6d602b5680805cf6b9277a13bfe2e7875221 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 12:58:00 +0300
Subject: [PATCH 05/32] Tests: `filter_view`

---
 .../range.filter/iterator/arrow.pass.cpp      |  3 +-
 .../range.filter/iterator/base.pass.cpp       |  4 +-
 .../iterator/ctor.parent_iter.pass.cpp        | 51 ----------------
 .../range.filter/iterator/deref.pass.cpp      |  3 +-
 .../sentinel/ctor.parent.pass.cpp             | 60 -------------------
 5 files changed, 6 insertions(+), 115 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 8a12b1694ca2f..46d59c3de1d7d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -87,7 +87,8 @@ constexpr void test() {
 
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
index 813cd892c64e6..9f193ddbc1a2d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/base.pass.cpp
@@ -37,7 +37,7 @@ constexpr void test() {
 
   // Test the const& version
   {
-    FilterIterator const iter(view, Iter(array.data()));
+    FilterIterator const iter = view.begin();
     Iter const& result = iter.base();
     ASSERT_SAME_TYPE(Iter const&, decltype(iter.base()));
     ASSERT_NOEXCEPT(iter.base());
@@ -46,7 +46,7 @@ constexpr void test() {
 
   // Test the && version
   {
-    FilterIterator iter(view, Iter(array.data()));
+    FilterIterator iter = view.begin();
     Iter result = std::move(iter).base();
     ASSERT_SAME_TYPE(Iter, decltype(std::move(iter).base()));
     assert(base(result) == array.data());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
deleted file mode 100644
index 761ef2d8ee493..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent_iter.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterIterator = std::ranges::iterator_t<FilterView>;
-
-  std::array<int, 10> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-  View view(Iter(array.data()), Sent(Iter(array.data() + array.size())));
-  Iter iter = view.begin();
-
-  FilterView filter_view(std::move(view), AlwaysTrue{});
-  FilterIterator filter_iter(filter_view, std::move(iter));
-  assert(base(filter_iter.base()) == array.data());
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index f4f4982bb9d5d..e794ff133dcb0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -36,7 +36,8 @@ constexpr void test() {
   FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
 
   for (std::size_t n = 0; n != array.size(); ++n) {
-    FilterIterator const iter(view, Iter(array.data() + n));
+    FilterIterator iter = view.begin();
+    for (std::size_t i = 0; i < n; ++i) ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 9896bf80c31e1..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(filter_view&);
-
-#include <ranges>
-
-#include <array>
-#include <cassert>
-#include <type_traits>
-#include <utility>
-#include "test_iterators.h"
-#include "../types.h"
-
-template <class Iter, class Sent = sentinel_wrapper<Iter>>
-constexpr void test() {
-  using View = minimal_view<Iter, Sent>;
-  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
-  using FilterSentinel = std::ranges::sentinel_t<FilterView>;
-
-  auto make_filter_view = [](auto begin, auto end, auto pred) {
-    View view{Iter(begin), Sent(Iter(end))};
-    return FilterView(std::move(view), pred);
-  };
-
-  std::array<int, 5> array{0, 1, 2, 3, 4};
-  FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
-
-  FilterSentinel sent(view);
-  assert(base(base(sent.base())) == base(base(view.end().base())));
-
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView const&>);
-  static_assert(!std::is_constructible_v<FilterSentinel, FilterView>);
-  static_assert( std::is_constructible_v<FilterSentinel, FilterView&> &&
-                !std::is_convertible_v<FilterView&, FilterSentinel>);
-}
-
-constexpr bool tests() {
-  test<cpp17_input_iterator<int*>>();
-  test<cpp20_input_iterator<int*>>();
-  test<forward_iterator<int*>>();
-  test<bidirectional_iterator<int*>>();
-  test<random_access_iterator<int*>>();
-  test<contiguous_iterator<int*>>();
-  test<int*>();
-  return true;
-}
-
-int main(int, char**) {
-  tests();
-  static_assert(tests());
-  return 0;
-}

>From 5e7d99cc72e9fada954478a27067019bc656b775 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:10:28 +0300
Subject: [PATCH 06/32] Tests: `lazy_split_view`

---
 .../ctor.outer_iterator.pass.cpp              | 43 -------------------
 .../ctor.parent.pass.cpp                      | 39 -----------------
 .../ctor.parent_base.pass.cpp                 | 36 ----------------
 3 files changed, 118 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
deleted file mode 100644
index 4981510be52d6..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
-
-#include <ranges>
-
-#include "../types.h"
-
-static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterConst>);
-
-template <class Inner, class Outer>
-constexpr void test_impl() {
-  [[maybe_unused]] Inner i(Outer{});
-  // Verify that the constructor is `explicit`.
-  static_assert(!std::is_convertible_v<Outer, Inner>);
-}
-
-constexpr bool test() {
-  test_impl<InnerIterForward, OuterIterForward>();
-  test_impl<InnerIterInput, OuterIterInput>();
-// Is only constructible if both the outer and the inner iterators have the same constness.
-  test_impl<InnerIterConst, OuterIterConst>();
-// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`).
-  test_impl<InnerIterConst, OuterIterNonConst>();
-  test_impl<InnerIterNonConst, OuterIterNonConst>();
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
deleted file mode 100644
index aacf6eabc290a..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
-//   requires (!forward_range<Base>)
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-// Verify that the constructor is `explicit`.
-static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
-
-static_assert( std::ranges::forward_range<SplitViewForward>);
-static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&>);
-
-constexpr bool test() {
-  InputView input;
-  SplitViewInput v(input, ForwardTinyView());
-  [[maybe_unused]] OuterIterInput i(v);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
deleted file mode 100644
index 9fc0ce3748b1f..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
-//   requires forward_range<Base>
-
-#include <ranges>
-
-#include <type_traits>
-#include <utility>
-#include "../types.h"
-
-static_assert(!std::ranges::forward_range<SplitViewInput>);
-static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&, std::ranges::iterator_t<InputView>>);
-
-constexpr bool test() {
-  ForwardView input("abc");
-  SplitViewForward v(std::move(input), " ");
-  [[maybe_unused]] OuterIterForward i(v, input.begin());
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From f700bb4db2caeebcd11365630c6533ce1ec26143 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:15:21 +0300
Subject: [PATCH 07/32] Tests: `join_view`

---
 .../range.join.sentinel/ctor.parent.pass.cpp  | 43 -------------------
 1 file changed, 43 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
deleted file mode 100644
index 1ac68277338fe..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Parent& parent);
-
-#include <cassert>
-#include <ranges>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
-
-  CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
-  CopyableParent parent{children};
-  std::ranges::join_view jv(parent);
-  std::ranges::sentinel_t<decltype(jv)> sent(jv);
-  assert(sent == std::ranges::next(jv.begin(), 16));
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  {
-    // Test explicitness.
-    using Parent = std::ranges::join_view<ParentView<ChildView>>;
-    static_assert( std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-    static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);
-  }
-
-  return 0;
-}

>From a7e6c012e1c6d976f95e25e740358f10a87eb3a5 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 13:47:11 +0300
Subject: [PATCH 08/32] Tests: `istream_view`

---
 .../range.istream.view/ctor.pass.cpp          |  5 +-
 .../range.istream.view/iterator/ctor.pass.cpp | 58 -------------------
 .../iterator/deref.pass.cpp                   | 10 ----
 3 files changed, 2 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
index ff60fbff1d73b..beeeea4c1bbe1 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/ctor.pass.cpp
@@ -39,10 +39,9 @@ void test() {
 
   // LWG 3568. basic_istream_view needs to initialize value_
   {
-    auto iss = make_string_stream<CharT>("123");
+    auto iss = make_string_stream<CharT>("");
     std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-    Iter iter{isv};
+    auto iter = isv.begin();
     assert(*iter == 0);
   }
 }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
deleted file mode 100644
index e180d12924084..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.pass.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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: no-localization
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// constexpr explicit iterator(basic_istream_view& parent) noexcept;
-
-#include <cassert>
-#include <ranges>
-#include <sstream>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../utils.h"
-
-// test that the constructor is explicit
-template <class CharT>
-using IstreamView = std::ranges::basic_istream_view<int, CharT>;
-template <class CharT>
-using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
-
-static_assert(std::constructible_from<Iter<char>, IstreamView<char>&>);
-static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
-static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
-#endif
-
-// test that the constructor is noexcept
-static_assert(std::is_nothrow_constructible_v<Iter<char>, IstreamView<char>&>);
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-static_assert(std::is_nothrow_constructible_v<Iter<wchar_t>, IstreamView<wchar_t>&>);
-#endif
-
-template <class CharT>
-void test() {
-  auto iss = make_string_stream<CharT>("123");
-  std::ranges::basic_istream_view<int, CharT> isv{iss};
-  Iter<CharT> it{isv};
-  ++it;
-  assert(*it == 123);
-}
-
-int main(int, char**) {
-  test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t>();
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 1cf05f0ac4cbe..d010ee06dbf51 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -29,16 +29,6 @@ void test() {
     assert(v1 == 1);
   }
 
-  // operator* should return the same reference to the value stored in the view
-  {
-    auto iss = make_string_stream<CharT>("1 2 345 ");
-    std::ranges::basic_istream_view<int, CharT> isv{iss};
-    using Iter = std::ranges::iterator_t<decltype(isv)>;
-
-    Iter it1{isv};
-    Iter it2{isv};
-    assert(&*it1 == &*it2);
-  }
 }
 
 int main(int, char**) {

>From 860d238c828062325b67b5a9e55a9ce4b17f7698 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 9 Apr 2026 14:00:32 +0300
Subject: [PATCH 09/32] Tests: `iota_view`

---
 .../iterator/ctor.value.pass.cpp              | 45 ------------------
 .../sentinel/ctor.value.pass.cpp              | 47 -------------------
 2 files changed, 92 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
deleted file mode 100644
index a84cb91f7e9b3..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.pass.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(W value);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int>>;
-    auto iter = Iter(42);
-    assert(*iter == 42);
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    auto iter = Iter(SomeInt(42));
-    assert(*iter == SomeInt(42));
-  }
-  {
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
-    static_assert(!std::is_convertible_v<Iter, SomeInt>);
-    static_assert( std::is_constructible_v<Iter, SomeInt>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
deleted file mode 100644
index a56369b046dc1..0000000000000
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(Bound bound);
-
-#include <cassert>
-#include <ranges>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "../types.h"
-
-constexpr bool test() {
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<int, IntSentinelWith<int>>>;
-    auto sent = Sent(IntSentinelWith<int>(42));
-    assert(sent == Iter(42));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    auto sent = Sent(IntSentinelWith<SomeInt>(SomeInt(42)));
-    assert(sent == Iter(SomeInt(42)));
-  }
-  {
-    using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
-    static_assert(!std::is_convertible_v<Sent, IntSentinelWith<SomeInt>>);
-    static_assert( std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}

>From d6cdbc060f553a7319793f2fe22f7765fd7a2ba4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 11 Apr 2026 14:45:00 +0300
Subject: [PATCH 10/32] Tests: `elements_view`

---
 .../range.elements/iterator/base.pass.cpp     | 12 +++--
 .../iterator/ctor.base.pass.cpp               | 54 -------------------
 .../iterator/ctor.other.pass.cpp              |  8 +--
 .../range.elements/sentinel/base.pass.cpp     |  7 +--
 .../sentinel/ctor.base.pass.cpp               | 52 ------------------
 .../sentinel/ctor.convert.pass.cpp            |  9 ++--
 6 files changed, 20 insertions(+), 122 deletions(-)
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
 delete mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index a77e98f845a06..425c13fea707e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -39,7 +39,7 @@ constexpr bool test() {
 
   // const &
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +47,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it{&t};
+    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +63,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it{&t};
+    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -81,7 +81,9 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+                  .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
deleted file mode 100644
index 5a280d294aecc..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit iterator(iterator_t<Base> current);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-
-#include "../types.h"
-
-// Test explicit
-using BaseIter     = std::tuple<int>*;
-using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
-
-static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
-static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
-
-struct TracedMoveIter : IterBase<TracedMoveIter>{
-  bool moved = false;
-
-  constexpr TracedMoveIter()                      = default;
-  constexpr TracedMoveIter(const TracedMoveIter&) = default;
-  constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
-  constexpr TracedMoveIter& operator=(TracedMoveIter&&)      = default;
-  constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
-};
-
-struct TracedMoveView : std::ranges::view_base {
-  TracedMoveIter begin() const;
-  TracedMoveIter end() const;
-};
-
-constexpr bool test() {
-  using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
-  Iter iter{TracedMoveIter{}};
-  assert(iter.base().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index 4c94cd5edf62e..d1b8ea73d635d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,10 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
-  ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  assert(constIter.base().movedFromOtherConst);
-  assert(constIter.base().i == 5);
+  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
+  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
+  // assert(constIter.base().movedFromOtherConst);
+  // assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index d4d0156fdb5be..a11d16817f030 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -17,7 +17,7 @@
 #include <utility>
 
 struct Sent {
-  int i;
+  int i = 5;
 
   friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
 };
@@ -27,7 +27,8 @@ constexpr bool test() {
   using EleRange  = std::ranges::elements_view<BaseRange, 0>;
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
-  const EleSent st{Sent{5}};
+  const EleRange ev;
+  const EleSent st = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
@@ -36,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
deleted file mode 100644
index 13fef1d53f121..0000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// constexpr explicit sentinel(sentinel_t<Base> end);
-
-#include <cassert>
-#include <ranges>
-#include <tuple>
-#include <utility>
-
-struct Sent {
-  int i;
-
-  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
-};
-
-struct Range : std::ranges::view_base {
-  std::tuple<int>* begin() const;
-  Sent end();
-};
-
-// Test explicit
-
-static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
-static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
-
-constexpr bool test() {
-  // base is init correctly
-  {
-    using R        = std::ranges::elements_view<Range, 0>;
-    using Sentinel = std::ranges::sentinel_t<R>;
-
-    Sentinel s1(Sent{5});
-    assert(s1.base().i == 5);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index a8bc817ced291..ce81d1c00d376 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -72,9 +72,10 @@ constexpr bool test() {
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    Sentinel s1(Sent{5});
-    ConstSentinel s2 = s1;
-    assert(s2.base().i == 5);
+    // auto s1 = R{}.end();
+    // (void) s1;
+    // ConstSentinel s2 = s1;
+    // assert(s2.base().i == 5);
   }
 
   return true;
@@ -82,7 +83,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // static_assert(test());
 
   return 0;
 }

>From 1561107ef3d40bd24b7c6ab51f5b945273dcee9b Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 24 Apr 2026 07:04:24 +0300
Subject: [PATCH 11/32] Format

---
 libcxx/docs/Status/Cxx26Papers.csv                |  2 +-
 .../range.elements/iterator/base.pass.cpp         | 15 ++++++++-------
 .../range.elements/sentinel/base.pass.cpp         |  2 +-
 .../range.split/iterator/base.pass.cpp            | 10 +++++-----
 .../range.split/iterator/deref.pass.cpp           |  7 +++----
 .../sentinel/ctor.convert.pass.cpp                |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..ffa36d3f5145c 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","","","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 425c13fea707e..316e3523edc73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -40,14 +40,14 @@ constexpr bool test() {
   // const &
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = it.base();
+    decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
 
   // &
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +55,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -64,7 +64,7 @@ constexpr bool test() {
   // const &&
   {
     const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
-    decltype(auto) base = std::move(it).base();
+    decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
   }
@@ -81,9 +81,10 @@ constexpr bool test() {
     using MoveOnlyElemIter =
         std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
 
-    auto it = std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-        std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-                  .begin();
+    auto it =
+        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
+            .begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index a11d16817f030..885582a5c94a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -28,7 +28,7 @@ constexpr bool test() {
   using EleSent   = std::ranges::sentinel_t<EleRange>;
 
   const EleRange ev;
-  const EleSent st = ev.end();
+  const EleSent st                       = ev.end();
   std::same_as<Sent> decltype(auto) base = st.base();
   assert(base.i == 5);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index 66c2914ba1919..fe9be0e97104c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -16,7 +16,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -42,28 +42,28 @@ constexpr bool test() {
 
   // const &
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = it.base();
     assert(base.i == 5);
   }
 
   // &&
   {
-    SplitIter it = sv.begin();
+    SplitIter it                           = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
 
   // const &&
   {
-    const SplitIter it = sv.begin();
+    const SplitIter it                     = sv.begin();
     std::same_as<Iter> decltype(auto) base = std::move(it).base();
     assert(base.i == 5);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
index be7aca854f640..0e9812020fb38 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/deref.pass.cpp
@@ -17,7 +17,7 @@
 #include "../types.h"
 
 struct Iter : ForwardIterBase<Iter> {
-  int i = 0;
+  int i            = 0;
   constexpr Iter() = default;
   constexpr Iter(int ii) : i(ii) {}
   constexpr int operator*() const { return i; }
@@ -37,9 +37,8 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
-  const SplitIter it = sv.begin();
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{7}, Iter{8}}};
+  const SplitIter it                                             = sv.begin();
   std::same_as<std::ranges::subrange<Iter>> decltype(auto) value = *it;
   assert(value.begin().i == 5);
   assert(value.end().i == 7);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 0fffe9b508816..8a9ee79660a1d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -107,7 +107,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, pred};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
     int i     = 10;
@@ -141,7 +141,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{Rng{}, TestPred{}};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 0);
   }

>From 322e15b83d76b7e554c68a2d66d1f1604e5856c5 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:28:20 +0300
Subject: [PATCH 12/32] Release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..052e5eab25433 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index ffa36d3f5145c..4d0fbca940b92 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -188,7 +188,7 @@
 "`P4140R0 <https://wg21.link/P4140R0>`__","Proposed resolution for US70-126: allow incomplete types in type_order","2026-03 (Croydon)","","","`#189595 <https://github.com/llvm/llvm-project/issues/189595>`__",""
 "`P3373R4 <https://wg21.link/P3373R4>`__","Of Operation States and Their Lifetimes","2026-03 (Croydon)","","","`#189597 <https://github.com/llvm/llvm-project/issues/189597>`__",""
 "`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
-"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","23","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
+"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""

>From b281948dbb19845c0706dd6f2f184ae0af360578 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 17 Jul 2026 21:42:44 +0300
Subject: [PATCH 13/32] Format

---
 .../range.adaptors/range.filter/iterator/arrow.pass.cpp     | 3 ++-
 .../range.adaptors/range.filter/iterator/deref.pass.cpp     | 3 ++-
 .../range.adaptors/range.split/iterator/base.pass.cpp       | 3 +--
 .../range.take.while/sentinel/ctor.convert.pass.cpp         | 6 +++---
 .../range.istream.view/iterator/deref.pass.cpp              | 1 -
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
index 46d59c3de1d7d..284a22d218bf0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp
@@ -88,7 +88,8 @@ constexpr void test() {
   for (std::ptrdiff_t n = 0; n != 5; ++n) {
     FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
     FilterIterator iter = view.begin();
-    for (std::ptrdiff_t i = 0; i < n; ++i) ++iter;
+    for (std::ptrdiff_t i = 0; i < n; ++i)
+      ++iter;
     std::same_as<Iter> decltype(auto) result = iter.operator->();
     assert(base(result) == array.data() + n);
     assert(iter->x == n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
index e794ff133dcb0..42b0f1816d8a1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/deref.pass.cpp
@@ -37,7 +37,8 @@ constexpr void test() {
 
   for (std::size_t n = 0; n != array.size(); ++n) {
     FilterIterator iter = view.begin();
-    for (std::size_t i = 0; i < n; ++i) ++iter;
+    for (std::size_t i = 0; i < n; ++i)
+      ++iter;
     ValueType& result = *iter;
     ASSERT_SAME_TYPE(ValueType&, decltype(*iter));
     assert(&result == array.data() + n);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
index fe9be0e97104c..8e8eff8a12b97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/base.pass.cpp
@@ -37,8 +37,7 @@ constexpr bool test() {
   using SplitView = std::ranges::split_view<std::ranges::subrange<Iter>, std::ranges::subrange<Iter>>;
   using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}},
-               std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
+  SplitView sv{std::ranges::subrange<Iter>{Iter{5}, Iter{8}}, std::ranges::subrange<Iter>{Iter{8}, Iter{9}}};
 
   // const &
   {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
index 8a9ee79660a1d..1808545422498 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp
@@ -82,7 +82,7 @@ constexpr bool test() {
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
     R r{TestRng{}, nullptr};
-    Sentinel s1 = r.end();
+    Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
     assert(s2.base().i == 5);
   }
@@ -110,8 +110,8 @@ constexpr bool test() {
     Sentinel s1      = r.end();
     ConstSentinel s2 = s1;
 
-    int i     = 10;
-    int* iter = &i;
+    int i                   = 10;
+    int* iter               = &i;
     [[maybe_unused]] bool b = iter == s2;
     assert(called);
   }
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index d010ee06dbf51..9f9f13399392c 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,7 +28,6 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
-
 }
 
 int main(int, char**) {

>From 7508093ee12dce3a013440fbe6a1716b2561c1e9 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:02:29 +0300
Subject: [PATCH 14/32] Tests: `elements_view` updates

---
 .../iterator/ctor.other.pass.cpp               | 16 ++++++++++++----
 .../sentinel/ctor.convert.pass.cpp             | 18 ++++++++++++------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
index d1b8ea73d635d..9255fb47013b6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -57,10 +57,18 @@ static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIt
 static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
 
 constexpr bool test() {
-  // auto iter = std::ranges::elements_view<ConvertibleView, 0>{}.begin();
-  // ConstElemIter<ConvertibleView> constIter = iter; // implicit
-  // assert(constIter.base().movedFromOtherConst);
-  // assert(constIter.base().i == 5);
+  struct TestRange : std::ranges::view_base {
+    constexpr ConvertibleIter<false> begin() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<false> end() { return ConvertibleIter<false>{5}; }
+    constexpr ConvertibleIter<true> begin() const { return ConvertibleIter<true>{5}; }
+    constexpr ConvertibleIter<true> end() const { return ConvertibleIter<true>{5}; }
+  };
+
+  std::ranges::elements_view<TestRange, 0> ev{TestRange{}};
+  ElemIter<TestRange> iter           = ev.begin();
+  ConstElemIter<TestRange> constIter = iter; // implicit
+  assert(constIter.base().movedFromOtherConst);
+  assert(constIter.base().i == 5);
 
   return true;
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
index ce81d1c00d376..25d1413b2fa16 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -67,15 +67,21 @@ static_assert(
 constexpr bool test() {
   // base is init correctly
   {
-    using R             = std::ranges::elements_view<Range, 0>;
+    struct TestRange : std::ranges::view_base {
+      constexpr std::tuple<int>* begin() const { return nullptr; }
+      constexpr Sent end() { return Sent{5}; }
+      constexpr ConstSent end() const { return ConstSent{5}; }
+    };
+
+    using R             = std::ranges::elements_view<TestRange, 0>;
     using Sentinel      = std::ranges::sentinel_t<R>;
     using ConstSentinel = std::ranges::sentinel_t<const R>;
     static_assert(!std::same_as<Sentinel, ConstSentinel>);
 
-    // auto s1 = R{}.end();
-    // (void) s1;
-    // ConstSentinel s2 = s1;
-    // assert(s2.base().i == 5);
+    R r{TestRange{}};
+    Sentinel s1      = r.end();
+    ConstSentinel s2 = s1;
+    assert(s2.base().i == 5);
   }
 
   return true;
@@ -83,7 +89,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
 
   return 0;
 }

>From 9e755c9090fa259181827c0ac5a86d220f592d12 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 20 Jul 2026 14:03:34 +0300
Subject: [PATCH 15/32] Revert formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 316e3523edc73..27801eadac30f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,7 +22,8 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept = requires {
+concept IsBaseNoexcept =
+    requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From 7650ff061552321bb25df10a92b85c6639be9f8f Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 20 Jul 2026 15:24:00 +0300
Subject: [PATCH 16/32] Apply suggestion from @frederick-vs-ja

Co-authored-by: A. Jiang <de34 at live.cn>
---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index 27801eadac30f..e28c36b1254af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -79,9 +79,6 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    using MoveOnlyElemIter =
-        std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
-
     auto it =
         std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
             std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}

>From 95921d088f568cd51b460b653771ae2e4840735c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:05:55 +0300
Subject: [PATCH 17/32] Fixed formatting

---
 .../range.adaptors/range.elements/iterator/base.pass.cpp       | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index e28c36b1254af..bcce8e1f48f7b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -22,8 +22,7 @@
 
 // Test Noexcept
 template <class T>
-concept IsBaseNoexcept =
-    requires {
+concept IsBaseNoexcept = requires {
   { std::declval<T>().base() } noexcept;
 };
 

>From 4b1bcff99d54798ff1a15edf1f7660b22df94f58 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 00:18:55 +0300
Subject: [PATCH 18/32] Enable test

---
 .../ranges/range.adaptors/range.elements/sentinel/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
index 885582a5c94a8..114b7fc1f1b71 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -37,6 +37,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  // static_assert(test());
+  static_assert(test());
   return 0;
 }

>From 1bc2c09122ac8b470ddeed22c330678aa24bd726 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 21 Jul 2026 07:53:40 +0300
Subject: [PATCH 19/32] Added `std::is_constructible/convertible` tests

---
 .../iterator/ctor.base.compile.pass.cpp       | 23 ++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 30 ++++++++++++++++
 .../ctor.parent.iter.compile.pass.cpp         | 25 +++++++++++++
 .../sentinel/ctor.parent.compile.pass.cpp     | 25 +++++++++++++
 .../ctor.outer_iterator.compile.pass.cpp      | 23 ++++++++++++
 .../ctor.parent.base.compile.pass.cpp         | 22 ++++++++++++
 .../ctor.parent.compile.pass.cpp              | 23 ++++++++++++
 .../sentinel/ctor.base.pred.compile.pass.cpp  | 35 +++++++++++++++++++
 .../iterator/ctor.value.compile.pass.cpp      | 26 ++++++++++++++
 .../sentinel/ctor.value.compile.pass.cpp      | 24 +++++++++++++
 .../iterator/ctor.parent.compile.pass.cpp     | 33 +++++++++++++++++
 11 files changed, 289 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..b43a60276e280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+using BaseIter     = std::tuple<int>*;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
+
+static_assert(!std::is_constructible_v<ElementsIter, BaseIter>);
+static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..ee4a297c1f4a9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+  std::tuple<int>* begin() const;
+  Sent end();
+};
+
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..4a013d0072222
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr std::ranges::filter_view::<iterator>(filter_view&, iterator_t<V>);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterIterator = std::ranges::iterator_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..06e06aeb6db6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/sentinel/ctor.parent.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(filter_view&);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
+using FilterSentinel = std::ranges::sentinel_t<FilterView>;
+
+static_assert(!std::constructible_from<FilterSentinel, FilterView>);
+static_assert(!std::convertible_to<FilterView, FilterSentinel>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..37fe2fadfd41a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit inner-iterator::inner-iterator(outer-iterator<Const> i);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<InnerIterConst, OuterIterConst>);
+static_assert(!std::is_convertible_v<InnerIterConst, OuterIterConst>);
+
+static_assert(!std::is_constructible_v<InnerIterNonConst, OuterIterNonConst>);
+static_assert(!std::is_convertible_v<InnerIterNonConst, OuterIterNonConst>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
new file mode 100644
index 0000000000000..70e97ea04f5c0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.base.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr outer-iterator(Parent& parent, iterator_t<Base> current);
+//   requires forward_range<Base>
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(std::ranges::forward_range<SplitViewForward>);
+static_assert(!std::is_constructible_v<OuterIterForward, SplitViewForward&, std::ranges::iterator_t<ForwardView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..b838970718c8a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// explicit std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent)
+//   requires (!forward_range<Base>)
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "../types.h"
+
+static_assert(!std::ranges::forward_range<SplitViewInput>);
+static_assert(!std::is_constructible_v<OuterIterInput, SplitViewInput&>);
+static_assert(!std::is_convertible_v<SplitViewInput&, OuterIterInput>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
new file mode 100644
index 0000000000000..a66e3d1e84c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pred.compile.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end, const Pred* pred);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+struct Sent {
+  int i;
+
+  friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; }
+};
+
+struct Range : std::ranges::view_base {
+  int* begin() const;
+  Sent end();
+};
+
+struct Pred {
+  bool operator()(int i) const;
+};
+
+using Sentinel = std::ranges::sentinel_t<std::ranges::take_while_view<Range, Pred>>;
+
+static_assert(!std::is_constructible_v<Sentinel, std::ranges::sentinel_t<Range>, const Pred*>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..e95042e26e146
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(W value);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
+
+using Iter = std::ranges::iterator_t<std::ranges::iota_view<SomeInt>>;
+static_assert(!std::is_constructible_v<Iter, SomeInt>);
+static_assert(!std::is_convertible_v<SomeInt, Iter>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
new file mode 100644
index 0000000000000..a39efb4318794
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.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++20
+
+// constexpr explicit sentinel(Bound bound);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../types.h"
+
+using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;
+static_assert(!std::is_constructible_v<Sent, IntSentinelWith<SomeInt>>);
+static_assert(!std::is_convertible_v<IntSentinelWith<SomeInt>, Sent>);
diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..171e2dce1e5f0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit iterator(basic_istream_view& parent) noexcept;
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <sstream>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// test that the constructor is explicit
+template <class CharT>
+using IstreamView = std::ranges::basic_istream_view<int, CharT>;
+template <class CharT>
+using Iter = std::ranges::iterator_t<IstreamView<CharT>>;
+
+static_assert(!std::constructible_from<Iter<char>, IstreamView<char>&>);
+static_assert(!std::convertible_to<IstreamView<char>&, Iter<char>>);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(!std::constructible_from<Iter<wchar_t>, IstreamView<wchar_t>&>);
+static_assert(!std::convertible_to<IstreamView<wchar_t>&, Iter<wchar_t>>);
+#endif

>From 9d45542148fc96757adb6166dd8cfe3781b26497 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 09:20:54 +0300
Subject: [PATCH 20/32] Cleanup

---
 .../range.iota.view/iterator/ctor.value.compile.pass.cpp        | 2 --
 .../range.iota.view/sentinel/ctor.value.compile.pass.cpp        | 2 --
 2 files changed, 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
index e95042e26e146..fc3384e191d1d 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 static_assert(!std::is_constructible_v<std::ranges::iterator_t<std::ranges::iota_view<int>>, int>);
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
index a39efb4318794..b0bcdb6c60ce7 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/sentinel/ctor.value.compile.pass.cpp
@@ -15,8 +15,6 @@
 #include <ranges>
 #include <type_traits>
 
-#include "test_macros.h"
-
 #include "../types.h"
 
 using Sent = std::ranges::sentinel_t<std::ranges::iota_view<SomeInt, IntSentinelWith<SomeInt>>>;

>From e1b33f99ad8828be93572e6a0102ce7855efee5b Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:42:42 +0300
Subject: [PATCH 21/32] Fixed CI

---
 .../range.istream.view/iterator/ctor.parent.compile.pass.cpp     | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
index 171e2dce1e5f0..04ff9ac24d731 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/ctor.parent.compile.pass.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-localization
 
 // constexpr explicit iterator(basic_istream_view& parent) noexcept;
 

>From 52cd79c17014eea053ffa6afd687cb734c53eadf Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 10:57:29 +0300
Subject: [PATCH 22/32] Restored a test case!

---
 .../range.istream.view/iterator/deref.pass.cpp        | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
index 9f9f13399392c..5df730410a4ad 100644
--- a/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.istream.view/iterator/deref.pass.cpp
@@ -28,6 +28,17 @@ void test() {
     std::same_as<int&> decltype(auto) v1 = *it;
     assert(v1 == 1);
   }
+
+  // operator* should return the same reference to the value stored in the view
+  {
+    auto iss = make_string_stream<CharT>("1 2 345 ");
+    std::ranges::basic_istream_view<int, CharT> isv{iss};
+    using Iter = std::ranges::iterator_t<decltype(isv)>;
+
+    Iter it1 = isv.begin();
+    Iter it2 = isv.begin();
+    assert(&*it1 == &*it2);
+  }
 }
 
 int main(int, char**) {

>From 38276fe2d59800d46b7578c4c87388c07343f54d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:16:34 +0300
Subject: [PATCH 23/32] Added a missing `take_view` test

---
 .../ctor.base.compile.pass.cpp                | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..9383508165609
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/range.take.sentinel/ctor.base.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+using TakeView = std::ranges::take_view<MoveOnlyView>;
+using Sentinel = std::ranges::sentinel_t<TakeView>;
+
+static_assert(!std::is_constructible_v<Sentinel, sentinel_wrapper<int*>>);
+static_assert(!std::is_convertible_v<sentinel_wrapper<int*>, Sentinel>);

>From 4d9e2640e16360957bda195cb93a531c4a56e2e5 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:34:21 +0300
Subject: [PATCH 24/32] Added missing test `join_view`

---
 .../ctor.parent.compile.pass.cpp              | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
new file mode 100644
index 0000000000000..504c945e3d918
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.compile.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// constexpr explicit sentinel(Parent& parent);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using Parent = std::ranges::join_view<ParentView<ChildView>>;
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<Parent>, Parent&>);
+static_assert(!std::is_convertible_v<std::ranges::sentinel_t<Parent>, Parent&>);

>From b527f3ed789741669d29abcc63ba81454254f272 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:54:39 +0300
Subject: [PATCH 25/32] Update tests `split_view`

---
 ...tor.parent.iter.subrange.compile.pass.cpp} | 31 +++++------------
 ....pass.cpp => ctor.parent.compile.pass.cpp} | 34 ++++---------------
 2 files changed, 14 insertions(+), 51 deletions(-)
 rename libcxx/test/std/ranges/range.adaptors/range.split/iterator/{ctor.base.pass.cpp => ctor.parent.iter.subrange.compile.pass.cpp} (64%)
 rename libcxx/test/std/ranges/range.adaptors/range.split/sentinel/{ctor.parent.pass.cpp => ctor.parent.compile.pass.cpp} (57%)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
similarity index 64%
rename from libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
index 823e22e117f77..9f4e9a13e0447 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/iterator/ctor.parent.iter.subrange.compile.pass.cpp
@@ -6,12 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
-// constexpr iterator begin();
+// constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
 
-#include <cassert>
 #include <ranges>
+#include <type_traits>
 
 #include "../types.h"
 
@@ -30,24 +32,7 @@ struct TracedMoveView : std::ranges::view_base {
   constexpr TracedMoveIter end() const { return {}; }
 };
 
-constexpr bool test() {
-  using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
-  using SplitIter = std::ranges::iterator_t<SplitView>;
-
-  SplitView sv{TracedMoveView{}, TracedMoveView{}};
-  SplitIter iter = sv.begin();
-  assert(iter.base().moved);
-
-  auto subRange = *iter;
-  assert(subRange.begin().moved);
-  assert(subRange.end().moved);
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
+using SplitView = std::ranges::split_view<TracedMoveView, TracedMoveView>;
+using SplitIter = std::ranges::iterator_t<SplitView>;
 
-  return 0;
-}
+static_assert(!std::is_constructible_v<SplitIter, SplitView, TracedMoveIter, std::ranges::subrange<TracedMoveIter>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
similarity index 57%
rename from libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 6e1ba912a201b..85d7e908a057c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -6,41 +6,19 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
 
 // constexpr explicit sentinel(split_view& parent);
 
-#include <cassert>
+// The constructor is now `private` (exposition-only) per P3059R2.
+
 #include <ranges>
-#include <type_traits>
 
-#include "test_iterators.h"
+#include "../types.h"
 
-// test explicit
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-constexpr bool test() {
-  {
-    int buffer[] = {0, 1, 2};
-    Range input{buffer, sentinel_wrapper<int*>(buffer + 3)};
-    SplitView sv(input, -1);
-    auto it = sv.begin();
-
-    auto sent = sv.end();
-    assert(sent != it);
-
-    ++it;
-    assert(sent == it);
-  }
-
-  return true;
-}
-
-int main(int, char**) {
-  test();
-  static_assert(test());
-
-  return 0;
-}
+static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_convertible_v<SplitView&, SplitSent>);

>From 07d8138582e82957c6be4cceb67ae82b170bdb4c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 11:57:06 +0300
Subject: [PATCH 26/32] Fixed test

---
 .../range.split/sentinel/ctor.parent.compile.pass.cpp           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
index 85d7e908a057c..e0e8029c4c538 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/sentinel/ctor.parent.compile.pass.cpp
@@ -20,5 +20,5 @@ using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
 static_assert(!std::is_convertible_v<SplitView&, SplitSent>);

>From e26417e7a778697ae5783fb1b5e5634f4db09582 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 12:49:06 +0300
Subject: [PATCH 27/32] Added missing `transform_view` tests

---
 .../ctor.parent.iter.compile.pass.cpp         | 23 ++++++++++++++++++
 .../sentinel/ctor.base.compile.pass.cpp       | 24 +++++++++++++++++++
 2 files changed, 47 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
new file mode 100644
index 0000000000000..e7841dede5bc2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+//  constexpr iterator(Parent& parent, iterator_t<Base> current);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using TransformView = std::ranges::transform_view<MoveOnlyView, PlusOne>;
+using TransformViewBaseIter =  std::ranges::iterator_t<MoveOnlyView>;
+using TransformIter = std::ranges::iterator_t<TransformView>;
+
+static_assert(!std::is_constructible_v<TransformIter, TransformView, TransformViewBaseIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
new file mode 100644
index 0000000000000..08be4bbd16667
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.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++20
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+// The constructor is now `private` (exposition-only) per P3059R2.
+
+#include <ranges>
+
+#include "../types.h"
+
+using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
+using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
+using SplitSent = std::ranges::sentinel_t<SplitView>;
+
+static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
+static_assert(!std::is_convertible_v<SplitView&, SplitSent>);

>From d03ec70d7f9502bbfa5dd9b22c2ac42570f9f52d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 13:32:08 +0300
Subject: [PATCH 28/32] Try to fix the tests

---
 .../iterator/ctor.parent.iter.compile.pass.cpp           | 3 ++-
 .../range.transform/sentinel/ctor.base.compile.pass.cpp  | 9 +++++----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
index 4a013d0072222..bca1b3bda4b24 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -18,8 +18,9 @@
 #include "../types.h"
 
 using View           = minimal_view<int*, sentinel_wrapper<int*>>;
+using ViewIter       = std::ranges::iterator_t<View>;
 using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
 using FilterIterator = std::ranges::iterator_t<FilterView>;
 
-static_assert(!std::constructible_from<FilterIterator, FilterView>);
+static_assert(!std::constructible_from<FilterIterator, FilterView, ViewIter>);
 static_assert(!std::convertible_to<FilterView, FilterIterator>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
index 08be4bbd16667..25a73252bcd2e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
@@ -14,11 +14,12 @@
 
 #include <ranges>
 
-#include "../types.h"
-
+#include "test_iterators.h"
+                                                                    
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
+using RangeSent = std::ranges::sentinel_t<Range>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
 using SplitSent = std::ranges::sentinel_t<SplitView>;
 
-static_assert(!std::is_constructible_v<SplitSent, SplitView&>);
-static_assert(!std::is_convertible_v<SplitView&, SplitSent>);
+static_assert(!std::is_constructible_v<SplitSent, RangeSent>);
+static_assert(!std::is_convertible_v<RangeSent, SplitSent>);

>From 04ad4e972be8e97b14cc3bec2557d3783daafd72 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 13:34:09 +0300
Subject: [PATCH 29/32] Formatting

---
 .../iterator/ctor.parent.iter.compile.pass.cpp              | 6 +++---
 .../range.transform/sentinel/ctor.base.compile.pass.cpp     | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
index e7841dede5bc2..4bafbfea3e284 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
@@ -16,8 +16,8 @@
 
 #include "../types.h"
 
-using TransformView = std::ranges::transform_view<MoveOnlyView, PlusOne>;
-using TransformViewBaseIter =  std::ranges::iterator_t<MoveOnlyView>;
-using TransformIter = std::ranges::iterator_t<TransformView>;
+using TransformView         = std::ranges::transform_view<MoveOnlyView, PlusOne>;
+using TransformViewBaseIter = std::ranges::iterator_t<MoveOnlyView>;
+using TransformIter         = std::ranges::iterator_t<TransformView>;
 
 static_assert(!std::is_constructible_v<TransformIter, TransformView, TransformViewBaseIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
index 25a73252bcd2e..bfbe0a5820b9e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
@@ -15,7 +15,7 @@
 #include <ranges>
 
 #include "test_iterators.h"
-                                                                    
+
 using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
 using RangeSent = std::ranges::sentinel_t<Range>;
 using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;

>From 6607ff05e559c5c0a6b8ae5eb09ac3b55e38640c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 13:48:32 +0300
Subject: [PATCH 30/32] More fixes

---
 .../iterator/ctor.parent.iter.compile.pass.cpp      |  3 +--
 .../iterator/ctor.parent.iter.compile.pass.cpp      |  2 +-
 .../sentinel/ctor.base.compile.pass.cpp             | 13 +++++++------
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
index bca1b3bda4b24..47deabb7136c6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/ctor.parent.iter.compile.pass.cpp
@@ -22,5 +22,4 @@ using ViewIter       = std::ranges::iterator_t<View>;
 using FilterView     = std::ranges::filter_view<View, AlwaysTrue>;
 using FilterIterator = std::ranges::iterator_t<FilterView>;
 
-static_assert(!std::constructible_from<FilterIterator, FilterView, ViewIter>);
-static_assert(!std::convertible_to<FilterView, FilterIterator>);
+static_assert(!std::constructible_from<FilterIterator, FilterView&, ViewIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
index 4bafbfea3e284..37b6016e439fb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/ctor.parent.iter.compile.pass.cpp
@@ -20,4 +20,4 @@ using TransformView         = std::ranges::transform_view<MoveOnlyView, PlusOne>
 using TransformViewBaseIter = std::ranges::iterator_t<MoveOnlyView>;
 using TransformIter         = std::ranges::iterator_t<TransformView>;
 
-static_assert(!std::is_constructible_v<TransformIter, TransformView, TransformViewBaseIter>);
+static_assert(!std::is_constructible_v<TransformIter, TransformView&, TransformViewBaseIter>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
index bfbe0a5820b9e..608db6eafe127 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
@@ -15,11 +15,12 @@
 #include <ranges>
 
 #include "test_iterators.h"
+#include "../types.h"
 
-using Range     = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
-using RangeSent = std::ranges::sentinel_t<Range>;
-using SplitView = std::ranges::split_view<Range, std::ranges::single_view<int>>;
-using SplitSent = std::ranges::sentinel_t<SplitView>;
+using Range         = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
+using BaseSent      = std::ranges::sentinel_t<Range>;
+using TransformView = std::ranges::transform_view<Range, PlusOne>;
+using TransformSent = std::ranges::sentinel_t<TransformView>;
 
-static_assert(!std::is_constructible_v<SplitSent, RangeSent>);
-static_assert(!std::is_convertible_v<RangeSent, SplitSent>);
+static_assert(!std::is_constructible_v<TransformSent, BaseSent>);
+static_assert(!std::is_convertible_v<BaseSent, TransformSent>);

>From ac6deb7cb87111487e38715d8023ee1099341fe7 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 22 Jul 2026 14:20:32 +0300
Subject: [PATCH 31/32] More fixes

---
 .../range.transform/sentinel/ctor.base.compile.pass.cpp      | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
index 608db6eafe127..b5f134528fef0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/sentinel/ctor.base.compile.pass.cpp
@@ -17,9 +17,8 @@
 #include "test_iterators.h"
 #include "../types.h"
 
-using Range         = std::ranges::subrange<int*, sentinel_wrapper<int*>>;
-using BaseSent      = std::ranges::sentinel_t<Range>;
-using TransformView = std::ranges::transform_view<Range, PlusOne>;
+using BaseSent      = std::ranges::sentinel_t<SizedSentinelView>;
+using TransformView = std::ranges::transform_view<SizedSentinelView, PlusOne>;
 using TransformSent = std::ranges::sentinel_t<TransformView>;
 
 static_assert(!std::is_constructible_v<TransformSent, BaseSent>);

>From a7d8464dd15dacf903a3bc7795f920ec4ad4acaa Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 23 Jul 2026 08:55:43 +0300
Subject: [PATCH 32/32] Clean-up tests

---
 .../range.elements/iterator/base.pass.cpp      | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
index bcce8e1f48f7b..83a2faa635188 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -37,9 +37,11 @@ LIBCPP_STATIC_ASSERT(!IsBaseNoexcept<ElementsIter&&>);
 constexpr bool test() {
   std::tuple<int> t{5};
 
+  std::ranges::elements_view<BaseView, 0> ev{BaseView{&t, &t + 1}};
+
   // const &
   {
-    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    const ElementsIter it = ev.begin();
     decltype(auto) base   = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -47,7 +49,7 @@ constexpr bool test() {
 
   // &
   {
-    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = ev.begin();
     decltype(auto) base = it.base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -55,7 +57,7 @@ constexpr bool test() {
 
   // &&
   {
-    ElementsIter it     = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    ElementsIter it     = ev.begin();
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
     assert(base == &t);
@@ -63,7 +65,7 @@ constexpr bool test() {
 
   // const &&
   {
-    const ElementsIter it = std::ranges::elements_view<BaseView, 0>{BaseView{&t, &t + 1}}.begin();
+    const ElementsIter it = ev.begin();
     decltype(auto) base   = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
     assert(base == &t);
@@ -78,10 +80,10 @@ constexpr bool test() {
       constexpr bool operator==(const MoveOnlyIter&) const { return true; }
     };
 
-    auto it =
-        std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
-            std::ranges::subrange{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}}
-            .begin();
+    std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>{
+        std::ranges::subrange mev{MoveOnlyIter{{}, MoveOnly{5}}, Sent{}}};
+    auto it = mev.begin();
+
     decltype(auto) base = std::move(it).base();
     static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
     assert(base.mo.get() == 5);


From libcxx-commits at lists.llvm.org  Wed Jul 22 23:02:31 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Wed, 22 Jul 2026 23:02:31 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] P3059R2: Making
 user-defined constructors of view iterators/sentinels private (PR #193891)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193891@github.com>
Message-ID: <6a61ae77.dd7c1686.181a5.4612@mx.google.com>


https://github.com/Zingam edited https://github.com/llvm/llvm-project/pull/193891

From libcxx-commits at lists.llvm.org  Thu Jul 23 00:46:52 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 23 Jul 2026 00:46:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 3b7447e - [libc++] Optimize standard
 streams with sync_with_stdio(false) (#209161)
Message-ID: <6a61c6ec.9edb5c9d.3c157a.0aa6@mx.google.com>


Author: Nikolas Klauser
Date: 2026-07-23T09:46:47+02:00
New Revision: 3b7447e00baff23660b49fdcb67ce83a3bf94605

URL: https://github.com/llvm/llvm-project/commit/3b7447e00baff23660b49fdcb67ce83a3bf94605
DIFF: https://github.com/llvm/llvm-project/commit/3b7447e00baff23660b49fdcb67ce83a3bf94605.diff

LOG: [libc++] Optimize standard streams with sync_with_stdio(false) (#209161)

This updates the standard streams so that the buffers are replaced with
`basic_filebuf`s.

Fixes #21566

---------

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>

Added: 
    libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
    libcxx/test/std/input.output/iostream.objects/many-ints.dat
    libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
    libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
    libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
    libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
    libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
    libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
    libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
    libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
    libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
    libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
    libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
    libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp

Modified: 
    libcxx/include/fstream
    libcxx/lib/abi/CHANGELOG.TXT
    libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
    libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
    libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
    libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
    libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
    libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
    libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
    libcxx/src/ios.cpp
    libcxx/src/iostream.cpp
    libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
    libcxx/utils/libcxx/test/features/availability.py

Removed: 
    


################################################################################
diff  --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..4b276251aa917 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -391,10 +391,10 @@ private:
 
   _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 
-  // There are multiple (__)open function, they use 
diff erent C-API open
-  // function. After that call these functions behave the same. This function
-  // does that part and determines the final return value.
-  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __do_open(FILE* __file, ios_base::openmode __mode) {
+public:
+  // Update the buffer so that __file is considered to be the underlying file and is opened as __mode. This is used in
+  // the open family of functions as well as various iostreams in sync_with_stdio(false) mode.
+  _LIBCPP_HIDE_FROM_ABI basic_filebuf* __adopt_file(FILE* __file, ios_base::openmode __mode) {
     __file_ = __file;
     if (!__file_)
       return nullptr;
@@ -417,6 +417,7 @@ private:
     return this;
   }
 
+private:
   // If the file is already open, switch to unbuffered mode. Otherwise, record
   // the request to use unbuffered mode so that we use that mode when we
   // eventually open the file.
@@ -742,7 +743,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const char*
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(std::fopen(__s, __mdstr), __mode);
+  return __adopt_file(std::fopen(__s, __mdstr), __mode);
 }
 
 template <class _CharT, class _Traits>
@@ -753,7 +754,7 @@ inline basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::__open(in
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(fdopen(__fd, __mdstr), __mode);
+  return __adopt_file(fdopen(__fd, __mdstr), __mode);
 }
 
 #    if _LIBCPP_HAS_OPEN_WITH_WCHAR
@@ -767,7 +768,7 @@ basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::open(const wchar
   if (!__mdstr)
     return nullptr;
 
-  return __do_open(_wfopen(__s, __mdstr), __mode);
+  return __adopt_file(_wfopen(__s, __mdstr), __mode);
 }
 #    endif
 

diff  --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index b09f297eb65ba..cb29de0942478 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -12,6 +12,18 @@ To generate a summary, re-generate the new ABI list using the
 
 New entries should be added directly below the "Version" header.
 
+------------
+Version 24.0
+------------
+
+* [libc++] Optimize standard streams with sync_with_stdio(false)
+
+  All platforms
+  -------------
+  Symbol added: _ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+  Symbol added: _ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE
+
 ------------
 Version 23.0
 ------------

diff  --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 414a239757e1c..09999516face2 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 12, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 12, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
@@ -1908,6 +1909,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2143,6 +2145,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 20, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 64, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 64, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}

diff  --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 590c0de8ce20f..d53eeb7532009 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}

diff  --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index f4f4ab99ad866..384904f79d20e 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1816,6 +1816,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
@@ -1889,6 +1890,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
@@ -1963,6 +1965,7 @@
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb0EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}

diff  --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index e1b1e110318fa..8f26fdb0462cb 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -10,8 +10,8 @@
 {'is_defined': True, 'name': '_ZNKSt16nested_exception14rethrow_nestedEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt18bad_variant_access4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt19bad_optional_access4whatEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt20bad_array_new_length4whatEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt29__bad_variant_access_with_msg4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPcRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110__time_put8__do_putEPwRS1_PK2tmcc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNKSt6__ndk110error_code7messageEv', 'type': 'FUNC'}
@@ -1672,6 +1672,7 @@
 {'is_defined': True, 'name': '_ZTINSt6__ndk112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1905,6 +1906,7 @@
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112strstreambufE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk112system_errorE', 'size': 26, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 50, 'type': 'OBJECT'}
@@ -2137,6 +2139,7 @@
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}

diff  --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 11cc8f180e824..0a2858da94a67 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1644,6 +1644,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1778,6 +1779,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1916,6 +1918,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}

diff  --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..903457e983046 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1643,6 +1643,7 @@
 {'is_defined': True, 'name': '_ZTINSt3__112strstreambufE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112system_errorE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 40, 'type': 'OBJECT'}
@@ -1777,6 +1778,7 @@
 {'is_defined': True, 'name': '_ZTSNSt3__112strstreambufE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112system_errorE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 47, 'type': 'OBJECT'}
@@ -1915,6 +1917,7 @@
 {'is_defined': True, 'name': '_ZTVNSt3__112strstreambufE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112system_errorE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}

diff  --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 2b69d6dc89138..c24cdf845b489 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -2002,4 +2002,7 @@
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'name': '_ZTINSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 24, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTSNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 47, 'type': 'OBJECT', 'is_defined': True}
+{'name': '_ZTVNSt3__113basic_filebufIwNS_11char_traitsIwEEEE', 'size': 128, 'type': 'OBJECT', 'is_defined': True}
\ No newline at end of file

diff  --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index db6ca50f7f6ca..3d4e7eb4436fd 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -373,13 +373,6 @@ void ios_base::__set_failbit_and_consider_rethrow() {
 #endif // _LIBCPP_HAS_EXCEPTIONS
 }
 
-bool ios_base::sync_with_stdio(bool sync) {
-  static bool previous_state = true;
-  bool r                     = previous_state;
-  previous_state             = sync;
-  return r;
-}
-
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 

diff  --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 0393982361456..551eb25f8fe4a 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -10,6 +10,7 @@
 
 #include <__memory/construct_at.h>
 #include <__ostream/basic_ostream.h>
+#include <fstream>
 #include <istream>
 
 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
@@ -34,21 +35,44 @@ union stream {
   StreamT value;
 };
 
-template <class StreamT, class BufferT>
+template <class StreamT, class SyncBufT, class UnsyncBufT>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
   struct {
-    BufferT buffer;
+    union {
+      SyncBufT sync_buffer;
+      UnsyncBufT unsync_buffer;
+    };
     mbstate_t mb;
   };
 };
 
-template <class StreamT, class BufferT>
-void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) {
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
   data.mb = {};
-  std::construct_at(&data.buffer, stdstream, &data.mb);
-  std::construct_at(&stream.value, &data.buffer);
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  std::construct_at(&stream.value, &data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_sync_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, SyncBufT, UnsyncBufT>& data) {
+  data.unsync_buffer.__adopt_file(nullptr, {}); // reset the file, so that basic_filebuf doesn't close standard streams
+  std::destroy_at(&data.unsync_buffer);
+  data.mb = {};
+  std::construct_at(&data.sync_buffer, stdstream, &data.mb);
+  stream.value.rdbuf(&data.sync_buffer);
+}
+
+template <class StreamT, class SyncBufT, class UnsyncBufT>
+void switch_to_unsync_stream(FILE* stdstream,
+                             stream<StreamT>& stream,
+                             stream_data<StreamT, SyncBufT, UnsyncBufT>& data,
+                             ios_base::openmode mode) {
+  std::destroy_at(&data.sync_buffer);
+  std::construct_at(&data.unsync_buffer);
+  data.unsync_buffer.__adopt_file(stdstream, mode);
+  stream.value.rdbuf(&data.unsync_buffer);
 }
 
 #define CHAR_MANGLING_char "D"
@@ -62,29 +86,39 @@ void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT,
 #endif
 
 #ifdef _LIBCPP_ABI_MICROSOFT
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__(                                \
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12 at A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
-    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data;                                      \
+#  define STREAM(StreamT, SyncBufT, UnsyncBufT, CharT, var)                                                            \
+    STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, SyncBufT<CharT>, UnsyncBufT<CharT>> var##_data;                  \
     _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var
 #endif
 
 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have 
diff erent
 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
 
-STREAM(basic_istream, __stdinbuf, char, cin);
-STREAM(basic_ostream, __stdoutbuf, char, cout);
-STREAM(basic_ostream, __stdoutbuf, char, cerr);
-STREAM(basic_ostream, __stdoutbuf, char, clog);
+#if _LIBCPP_HAS_FILESYSTEM
+template <class CharT>
+using unsync_buffer = basic_filebuf<CharT>;
+#else
+// If we don't have access to files we can't treat the standard streams as files either - just save a dummy object in
+// that case.
+template <class>
+using unsync_buffer = char;
+#endif
+
+STREAM(basic_istream, __stdinbuf, unsync_buffer, char, cin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, cerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, char, clog);
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+STREAM(basic_istream, __stdinbuf, unsync_buffer, wchar_t, wcin);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcout);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wcerr);
+STREAM(basic_ostream, __stdoutbuf, unsync_buffer, wchar_t, wclog);
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
@@ -155,5 +189,37 @@ ios_base::Init::Init() {
 
 ios_base::Init::~Init() {}
 
+bool ios_base::sync_with_stdio(bool sync) {
+  static bool previous_state = true;
+  bool r                     = previous_state;
+
+#if _LIBCPP_HAS_FILESYSTEM
+  if (sync != previous_state) {
+    if (sync) {
+      switch_to_sync_stream(stdin, cin, cin_data);
+      switch_to_sync_stream(stdout, cout, cout_data);
+      switch_to_sync_stream(stderr, clog, clog_data);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_sync_stream(stdin, wcin, wcin_data);
+      switch_to_sync_stream(stdout, wcout, wcout_data);
+      switch_to_sync_stream(stderr, wclog, wclog_data);
+#  endif
+    } else {
+      switch_to_unsync_stream(stdin, cin, cin_data, ios::in);
+      switch_to_unsync_stream(stdout, cout, cout_data, ios::out);
+      switch_to_unsync_stream(stderr, clog, clog_data, ios::out);
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
+      switch_to_unsync_stream(stdin, wcin, wcin_data, ios::in);
+      switch_to_unsync_stream(stdout, wcout, wcout_data, ios::out);
+      switch_to_unsync_stream(stderr, wclog, wclog_data, ios::out);
+#  endif
+    }
+  }
+#endif // _LIBCPP_HAS_FILESYSTEM
+
+  previous_state = sync;
+  return r;
+}
+
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp b/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
new file mode 100644
index 0000000000000..3a715175ff070
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/iostream.objects/sync_with_stdio.buffer_type.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// static bool ios_base::sync_with_stdio(bool sync = true);
+
+// This is a libc++ implementation detail: when sync_with_stdio(false) is used,
+// libc++ swaps the buffers of cin/cout/clog (and the wide equivalents) for a
+// basic_filebuf to avoid going through the C stdio layer. This test ensures
+// that this optimization is applied by inspecting the type of the underlying
+// buffer.
+
+// The optimization is only applied when we have a filesystem, since fstream
+// is not present otherwise. Also, the test itself requires RTTI.
+// UNSUPPORTED: no-filesystem, no-rtti
+
+// XFAIL: using-built-library-before-llvm-24
+
+#include <cassert>
+#include <fstream>
+#include <iostream>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  // By default the streams are synchronized, so they are not backed by a filebuf.
+  assert(!dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  // Turning synchronization off switches cin/cout/clog to a filebuf, but leaves
+  // cerr untouched (it stays synchronized so it keeps flushing eagerly).
+  std::ios_base::sync_with_stdio(false);
+  assert(dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  // Turning it back on restores the original buffers.
+  std::ios_base::sync_with_stdio(true);
+  assert(!dynamic_cast<std::filebuf*>(std::cin.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cout.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::cerr.rdbuf()));
+  assert(!dynamic_cast<std::filebuf*>(std::clog.rdbuf()));
+#if !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcin.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcout.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wcerr.rdbuf()));
+  assert(!dynamic_cast<std::wfilebuf*>(std::wclog.rdbuf()));
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostream.objects/many-ints.dat b/libcxx/test/std/input.output/iostream.objects/many-ints.dat
new file mode 100644
index 0000000000000..7d1714969fc2d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/many-ints.dat
@@ -0,0 +1,5000 @@
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
+1288
+1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
+1301
+1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
+1316
+1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
+1331
+1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
+1344
+1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
+1355
+1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
+1364
+1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
+1385
+1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
+1406
+1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
+1416
+1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
+1437
+1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
+1458
+1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
+1485
+1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
+1498
+1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
+1529
+1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
+1560
+1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+1574
+1575
+1576
+1577
+1578
+1579
+1580
+1581
+1582
+1583
+1584
+1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
+1599
+1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
+1609
+1610
+1611
+1612
+1613
+1614
+1615
+1616
+1617
+1618
+1619
+1620
+1621
+1622
+1623
+1624
+1625
+1626
+1627
+1628
+1629
+1630
+1631
+1632
+1633
+1634
+1635
+1636
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
+1645
+1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
+1658
+1659
+1660
+1661
+1662
+1663
+1664
+1665
+1666
+1667
+1668
+1669
+1670
+1671
+1672
+1673
+1674
+1675
+1676
+1677
+1678
+1679
+1680
+1681
+1682
+1683
+1684
+1685
+1686
+1687
+1688
+1689
+1690
+1691
+1692
+1693
+1694
+1695
+1696
+1697
+1698
+1699
+1700
+1701
+1702
+1703
+1704
+1705
+1706
+1707
+1708
+1709
+1710
+1711
+1712
+1713
+1714
+1715
+1716
+1717
+1718
+1719
+1720
+1721
+1722
+1723
+1724
+1725
+1726
+1727
+1728
+1729
+1730
+1731
+1732
+1733
+1734
+1735
+1736
+1737
+1738
+1739
+1740
+1741
+1742
+1743
+1744
+1745
+1746
+1747
+1748
+1749
+1750
+1751
+1752
+1753
+1754
+1755
+1756
+1757
+1758
+1759
+1760
+1761
+1762
+1763
+1764
+1765
+1766
+1767
+1768
+1769
+1770
+1771
+1772
+1773
+1774
+1775
+1776
+1777
+1778
+1779
+1780
+1781
+1782
+1783
+1784
+1785
+1786
+1787
+1788
+1789
+1790
+1791
+1792
+1793
+1794
+1795
+1796
+1797
+1798
+1799
+1800
+1801
+1802
+1803
+1804
+1805
+1806
+1807
+1808
+1809
+1810
+1811
+1812
+1813
+1814
+1815
+1816
+1817
+1818
+1819
+1820
+1821
+1822
+1823
+1824
+1825
+1826
+1827
+1828
+1829
+1830
+1831
+1832
+1833
+1834
+1835
+1836
+1837
+1838
+1839
+1840
+1841
+1842
+1843
+1844
+1845
+1846
+1847
+1848
+1849
+1850
+1851
+1852
+1853
+1854
+1855
+1856
+1857
+1858
+1859
+1860
+1861
+1862
+1863
+1864
+1865
+1866
+1867
+1868
+1869
+1870
+1871
+1872
+1873
+1874
+1875
+1876
+1877
+1878
+1879
+1880
+1881
+1882
+1883
+1884
+1885
+1886
+1887
+1888
+1889
+1890
+1891
+1892
+1893
+1894
+1895
+1896
+1897
+1898
+1899
+1900
+1901
+1902
+1903
+1904
+1905
+1906
+1907
+1908
+1909
+1910
+1911
+1912
+1913
+1914
+1915
+1916
+1917
+1918
+1919
+1920
+1921
+1922
+1923
+1924
+1925
+1926
+1927
+1928
+1929
+1930
+1931
+1932
+1933
+1934
+1935
+1936
+1937
+1938
+1939
+1940
+1941
+1942
+1943
+1944
+1945
+1946
+1947
+1948
+1949
+1950
+1951
+1952
+1953
+1954
+1955
+1956
+1957
+1958
+1959
+1960
+1961
+1962
+1963
+1964
+1965
+1966
+1967
+1968
+1969
+1970
+1971
+1972
+1973
+1974
+1975
+1976
+1977
+1978
+1979
+1980
+1981
+1982
+1983
+1984
+1985
+1986
+1987
+1988
+1989
+1990
+1991
+1992
+1993
+1994
+1995
+1996
+1997
+1998
+1999
+2000
+2001
+2002
+2003
+2004
+2005
+2006
+2007
+2008
+2009
+2010
+2011
+2012
+2013
+2014
+2015
+2016
+2017
+2018
+2019
+2020
+2021
+2022
+2023
+2024
+2025
+2026
+2027
+2028
+2029
+2030
+2031
+2032
+2033
+2034
+2035
+2036
+2037
+2038
+2039
+2040
+2041
+2042
+2043
+2044
+2045
+2046
+2047
+2048
+2049
+2050
+2051
+2052
+2053
+2054
+2055
+2056
+2057
+2058
+2059
+2060
+2061
+2062
+2063
+2064
+2065
+2066
+2067
+2068
+2069
+2070
+2071
+2072
+2073
+2074
+2075
+2076
+2077
+2078
+2079
+2080
+2081
+2082
+2083
+2084
+2085
+2086
+2087
+2088
+2089
+2090
+2091
+2092
+2093
+2094
+2095
+2096
+2097
+2098
+2099
+2100
+2101
+2102
+2103
+2104
+2105
+2106
+2107
+2108
+2109
+2110
+2111
+2112
+2113
+2114
+2115
+2116
+2117
+2118
+2119
+2120
+2121
+2122
+2123
+2124
+2125
+2126
+2127
+2128
+2129
+2130
+2131
+2132
+2133
+2134
+2135
+2136
+2137
+2138
+2139
+2140
+2141
+2142
+2143
+2144
+2145
+2146
+2147
+2148
+2149
+2150
+2151
+2152
+2153
+2154
+2155
+2156
+2157
+2158
+2159
+2160
+2161
+2162
+2163
+2164
+2165
+2166
+2167
+2168
+2169
+2170
+2171
+2172
+2173
+2174
+2175
+2176
+2177
+2178
+2179
+2180
+2181
+2182
+2183
+2184
+2185
+2186
+2187
+2188
+2189
+2190
+2191
+2192
+2193
+2194
+2195
+2196
+2197
+2198
+2199
+2200
+2201
+2202
+2203
+2204
+2205
+2206
+2207
+2208
+2209
+2210
+2211
+2212
+2213
+2214
+2215
+2216
+2217
+2218
+2219
+2220
+2221
+2222
+2223
+2224
+2225
+2226
+2227
+2228
+2229
+2230
+2231
+2232
+2233
+2234
+2235
+2236
+2237
+2238
+2239
+2240
+2241
+2242
+2243
+2244
+2245
+2246
+2247
+2248
+2249
+2250
+2251
+2252
+2253
+2254
+2255
+2256
+2257
+2258
+2259
+2260
+2261
+2262
+2263
+2264
+2265
+2266
+2267
+2268
+2269
+2270
+2271
+2272
+2273
+2274
+2275
+2276
+2277
+2278
+2279
+2280
+2281
+2282
+2283
+2284
+2285
+2286
+2287
+2288
+2289
+2290
+2291
+2292
+2293
+2294
+2295
+2296
+2297
+2298
+2299
+2300
+2301
+2302
+2303
+2304
+2305
+2306
+2307
+2308
+2309
+2310
+2311
+2312
+2313
+2314
+2315
+2316
+2317
+2318
+2319
+2320
+2321
+2322
+2323
+2324
+2325
+2326
+2327
+2328
+2329
+2330
+2331
+2332
+2333
+2334
+2335
+2336
+2337
+2338
+2339
+2340
+2341
+2342
+2343
+2344
+2345
+2346
+2347
+2348
+2349
+2350
+2351
+2352
+2353
+2354
+2355
+2356
+2357
+2358
+2359
+2360
+2361
+2362
+2363
+2364
+2365
+2366
+2367
+2368
+2369
+2370
+2371
+2372
+2373
+2374
+2375
+2376
+2377
+2378
+2379
+2380
+2381
+2382
+2383
+2384
+2385
+2386
+2387
+2388
+2389
+2390
+2391
+2392
+2393
+2394
+2395
+2396
+2397
+2398
+2399
+2400
+2401
+2402
+2403
+2404
+2405
+2406
+2407
+2408
+2409
+2410
+2411
+2412
+2413
+2414
+2415
+2416
+2417
+2418
+2419
+2420
+2421
+2422
+2423
+2424
+2425
+2426
+2427
+2428
+2429
+2430
+2431
+2432
+2433
+2434
+2435
+2436
+2437
+2438
+2439
+2440
+2441
+2442
+2443
+2444
+2445
+2446
+2447
+2448
+2449
+2450
+2451
+2452
+2453
+2454
+2455
+2456
+2457
+2458
+2459
+2460
+2461
+2462
+2463
+2464
+2465
+2466
+2467
+2468
+2469
+2470
+2471
+2472
+2473
+2474
+2475
+2476
+2477
+2478
+2479
+2480
+2481
+2482
+2483
+2484
+2485
+2486
+2487
+2488
+2489
+2490
+2491
+2492
+2493
+2494
+2495
+2496
+2497
+2498
+2499
+2500
+2501
+2502
+2503
+2504
+2505
+2506
+2507
+2508
+2509
+2510
+2511
+2512
+2513
+2514
+2515
+2516
+2517
+2518
+2519
+2520
+2521
+2522
+2523
+2524
+2525
+2526
+2527
+2528
+2529
+2530
+2531
+2532
+2533
+2534
+2535
+2536
+2537
+2538
+2539
+2540
+2541
+2542
+2543
+2544
+2545
+2546
+2547
+2548
+2549
+2550
+2551
+2552
+2553
+2554
+2555
+2556
+2557
+2558
+2559
+2560
+2561
+2562
+2563
+2564
+2565
+2566
+2567
+2568
+2569
+2570
+2571
+2572
+2573
+2574
+2575
+2576
+2577
+2578
+2579
+2580
+2581
+2582
+2583
+2584
+2585
+2586
+2587
+2588
+2589
+2590
+2591
+2592
+2593
+2594
+2595
+2596
+2597
+2598
+2599
+2600
+2601
+2602
+2603
+2604
+2605
+2606
+2607
+2608
+2609
+2610
+2611
+2612
+2613
+2614
+2615
+2616
+2617
+2618
+2619
+2620
+2621
+2622
+2623
+2624
+2625
+2626
+2627
+2628
+2629
+2630
+2631
+2632
+2633
+2634
+2635
+2636
+2637
+2638
+2639
+2640
+2641
+2642
+2643
+2644
+2645
+2646
+2647
+2648
+2649
+2650
+2651
+2652
+2653
+2654
+2655
+2656
+2657
+2658
+2659
+2660
+2661
+2662
+2663
+2664
+2665
+2666
+2667
+2668
+2669
+2670
+2671
+2672
+2673
+2674
+2675
+2676
+2677
+2678
+2679
+2680
+2681
+2682
+2683
+2684
+2685
+2686
+2687
+2688
+2689
+2690
+2691
+2692
+2693
+2694
+2695
+2696
+2697
+2698
+2699
+2700
+2701
+2702
+2703
+2704
+2705
+2706
+2707
+2708
+2709
+2710
+2711
+2712
+2713
+2714
+2715
+2716
+2717
+2718
+2719
+2720
+2721
+2722
+2723
+2724
+2725
+2726
+2727
+2728
+2729
+2730
+2731
+2732
+2733
+2734
+2735
+2736
+2737
+2738
+2739
+2740
+2741
+2742
+2743
+2744
+2745
+2746
+2747
+2748
+2749
+2750
+2751
+2752
+2753
+2754
+2755
+2756
+2757
+2758
+2759
+2760
+2761
+2762
+2763
+2764
+2765
+2766
+2767
+2768
+2769
+2770
+2771
+2772
+2773
+2774
+2775
+2776
+2777
+2778
+2779
+2780
+2781
+2782
+2783
+2784
+2785
+2786
+2787
+2788
+2789
+2790
+2791
+2792
+2793
+2794
+2795
+2796
+2797
+2798
+2799
+2800
+2801
+2802
+2803
+2804
+2805
+2806
+2807
+2808
+2809
+2810
+2811
+2812
+2813
+2814
+2815
+2816
+2817
+2818
+2819
+2820
+2821
+2822
+2823
+2824
+2825
+2826
+2827
+2828
+2829
+2830
+2831
+2832
+2833
+2834
+2835
+2836
+2837
+2838
+2839
+2840
+2841
+2842
+2843
+2844
+2845
+2846
+2847
+2848
+2849
+2850
+2851
+2852
+2853
+2854
+2855
+2856
+2857
+2858
+2859
+2860
+2861
+2862
+2863
+2864
+2865
+2866
+2867
+2868
+2869
+2870
+2871
+2872
+2873
+2874
+2875
+2876
+2877
+2878
+2879
+2880
+2881
+2882
+2883
+2884
+2885
+2886
+2887
+2888
+2889
+2890
+2891
+2892
+2893
+2894
+2895
+2896
+2897
+2898
+2899
+2900
+2901
+2902
+2903
+2904
+2905
+2906
+2907
+2908
+2909
+2910
+2911
+2912
+2913
+2914
+2915
+2916
+2917
+2918
+2919
+2920
+2921
+2922
+2923
+2924
+2925
+2926
+2927
+2928
+2929
+2930
+2931
+2932
+2933
+2934
+2935
+2936
+2937
+2938
+2939
+2940
+2941
+2942
+2943
+2944
+2945
+2946
+2947
+2948
+2949
+2950
+2951
+2952
+2953
+2954
+2955
+2956
+2957
+2958
+2959
+2960
+2961
+2962
+2963
+2964
+2965
+2966
+2967
+2968
+2969
+2970
+2971
+2972
+2973
+2974
+2975
+2976
+2977
+2978
+2979
+2980
+2981
+2982
+2983
+2984
+2985
+2986
+2987
+2988
+2989
+2990
+2991
+2992
+2993
+2994
+2995
+2996
+2997
+2998
+2999
+3000
+3001
+3002
+3003
+3004
+3005
+3006
+3007
+3008
+3009
+3010
+3011
+3012
+3013
+3014
+3015
+3016
+3017
+3018
+3019
+3020
+3021
+3022
+3023
+3024
+3025
+3026
+3027
+3028
+3029
+3030
+3031
+3032
+3033
+3034
+3035
+3036
+3037
+3038
+3039
+3040
+3041
+3042
+3043
+3044
+3045
+3046
+3047
+3048
+3049
+3050
+3051
+3052
+3053
+3054
+3055
+3056
+3057
+3058
+3059
+3060
+3061
+3062
+3063
+3064
+3065
+3066
+3067
+3068
+3069
+3070
+3071
+3072
+3073
+3074
+3075
+3076
+3077
+3078
+3079
+3080
+3081
+3082
+3083
+3084
+3085
+3086
+3087
+3088
+3089
+3090
+3091
+3092
+3093
+3094
+3095
+3096
+3097
+3098
+3099
+3100
+3101
+3102
+3103
+3104
+3105
+3106
+3107
+3108
+3109
+3110
+3111
+3112
+3113
+3114
+3115
+3116
+3117
+3118
+3119
+3120
+3121
+3122
+3123
+3124
+3125
+3126
+3127
+3128
+3129
+3130
+3131
+3132
+3133
+3134
+3135
+3136
+3137
+3138
+3139
+3140
+3141
+3142
+3143
+3144
+3145
+3146
+3147
+3148
+3149
+3150
+3151
+3152
+3153
+3154
+3155
+3156
+3157
+3158
+3159
+3160
+3161
+3162
+3163
+3164
+3165
+3166
+3167
+3168
+3169
+3170
+3171
+3172
+3173
+3174
+3175
+3176
+3177
+3178
+3179
+3180
+3181
+3182
+3183
+3184
+3185
+3186
+3187
+3188
+3189
+3190
+3191
+3192
+3193
+3194
+3195
+3196
+3197
+3198
+3199
+3200
+3201
+3202
+3203
+3204
+3205
+3206
+3207
+3208
+3209
+3210
+3211
+3212
+3213
+3214
+3215
+3216
+3217
+3218
+3219
+3220
+3221
+3222
+3223
+3224
+3225
+3226
+3227
+3228
+3229
+3230
+3231
+3232
+3233
+3234
+3235
+3236
+3237
+3238
+3239
+3240
+3241
+3242
+3243
+3244
+3245
+3246
+3247
+3248
+3249
+3250
+3251
+3252
+3253
+3254
+3255
+3256
+3257
+3258
+3259
+3260
+3261
+3262
+3263
+3264
+3265
+3266
+3267
+3268
+3269
+3270
+3271
+3272
+3273
+3274
+3275
+3276
+3277
+3278
+3279
+3280
+3281
+3282
+3283
+3284
+3285
+3286
+3287
+3288
+3289
+3290
+3291
+3292
+3293
+3294
+3295
+3296
+3297
+3298
+3299
+3300
+3301
+3302
+3303
+3304
+3305
+3306
+3307
+3308
+3309
+3310
+3311
+3312
+3313
+3314
+3315
+3316
+3317
+3318
+3319
+3320
+3321
+3322
+3323
+3324
+3325
+3326
+3327
+3328
+3329
+3330
+3331
+3332
+3333
+3334
+3335
+3336
+3337
+3338
+3339
+3340
+3341
+3342
+3343
+3344
+3345
+3346
+3347
+3348
+3349
+3350
+3351
+3352
+3353
+3354
+3355
+3356
+3357
+3358
+3359
+3360
+3361
+3362
+3363
+3364
+3365
+3366
+3367
+3368
+3369
+3370
+3371
+3372
+3373
+3374
+3375
+3376
+3377
+3378
+3379
+3380
+3381
+3382
+3383
+3384
+3385
+3386
+3387
+3388
+3389
+3390
+3391
+3392
+3393
+3394
+3395
+3396
+3397
+3398
+3399
+3400
+3401
+3402
+3403
+3404
+3405
+3406
+3407
+3408
+3409
+3410
+3411
+3412
+3413
+3414
+3415
+3416
+3417
+3418
+3419
+3420
+3421
+3422
+3423
+3424
+3425
+3426
+3427
+3428
+3429
+3430
+3431
+3432
+3433
+3434
+3435
+3436
+3437
+3438
+3439
+3440
+3441
+3442
+3443
+3444
+3445
+3446
+3447
+3448
+3449
+3450
+3451
+3452
+3453
+3454
+3455
+3456
+3457
+3458
+3459
+3460
+3461
+3462
+3463
+3464
+3465
+3466
+3467
+3468
+3469
+3470
+3471
+3472
+3473
+3474
+3475
+3476
+3477
+3478
+3479
+3480
+3481
+3482
+3483
+3484
+3485
+3486
+3487
+3488
+3489
+3490
+3491
+3492
+3493
+3494
+3495
+3496
+3497
+3498
+3499
+3500
+3501
+3502
+3503
+3504
+3505
+3506
+3507
+3508
+3509
+3510
+3511
+3512
+3513
+3514
+3515
+3516
+3517
+3518
+3519
+3520
+3521
+3522
+3523
+3524
+3525
+3526
+3527
+3528
+3529
+3530
+3531
+3532
+3533
+3534
+3535
+3536
+3537
+3538
+3539
+3540
+3541
+3542
+3543
+3544
+3545
+3546
+3547
+3548
+3549
+3550
+3551
+3552
+3553
+3554
+3555
+3556
+3557
+3558
+3559
+3560
+3561
+3562
+3563
+3564
+3565
+3566
+3567
+3568
+3569
+3570
+3571
+3572
+3573
+3574
+3575
+3576
+3577
+3578
+3579
+3580
+3581
+3582
+3583
+3584
+3585
+3586
+3587
+3588
+3589
+3590
+3591
+3592
+3593
+3594
+3595
+3596
+3597
+3598
+3599
+3600
+3601
+3602
+3603
+3604
+3605
+3606
+3607
+3608
+3609
+3610
+3611
+3612
+3613
+3614
+3615
+3616
+3617
+3618
+3619
+3620
+3621
+3622
+3623
+3624
+3625
+3626
+3627
+3628
+3629
+3630
+3631
+3632
+3633
+3634
+3635
+3636
+3637
+3638
+3639
+3640
+3641
+3642
+3643
+3644
+3645
+3646
+3647
+3648
+3649
+3650
+3651
+3652
+3653
+3654
+3655
+3656
+3657
+3658
+3659
+3660
+3661
+3662
+3663
+3664
+3665
+3666
+3667
+3668
+3669
+3670
+3671
+3672
+3673
+3674
+3675
+3676
+3677
+3678
+3679
+3680
+3681
+3682
+3683
+3684
+3685
+3686
+3687
+3688
+3689
+3690
+3691
+3692
+3693
+3694
+3695
+3696
+3697
+3698
+3699
+3700
+3701
+3702
+3703
+3704
+3705
+3706
+3707
+3708
+3709
+3710
+3711
+3712
+3713
+3714
+3715
+3716
+3717
+3718
+3719
+3720
+3721
+3722
+3723
+3724
+3725
+3726
+3727
+3728
+3729
+3730
+3731
+3732
+3733
+3734
+3735
+3736
+3737
+3738
+3739
+3740
+3741
+3742
+3743
+3744
+3745
+3746
+3747
+3748
+3749
+3750
+3751
+3752
+3753
+3754
+3755
+3756
+3757
+3758
+3759
+3760
+3761
+3762
+3763
+3764
+3765
+3766
+3767
+3768
+3769
+3770
+3771
+3772
+3773
+3774
+3775
+3776
+3777
+3778
+3779
+3780
+3781
+3782
+3783
+3784
+3785
+3786
+3787
+3788
+3789
+3790
+3791
+3792
+3793
+3794
+3795
+3796
+3797
+3798
+3799
+3800
+3801
+3802
+3803
+3804
+3805
+3806
+3807
+3808
+3809
+3810
+3811
+3812
+3813
+3814
+3815
+3816
+3817
+3818
+3819
+3820
+3821
+3822
+3823
+3824
+3825
+3826
+3827
+3828
+3829
+3830
+3831
+3832
+3833
+3834
+3835
+3836
+3837
+3838
+3839
+3840
+3841
+3842
+3843
+3844
+3845
+3846
+3847
+3848
+3849
+3850
+3851
+3852
+3853
+3854
+3855
+3856
+3857
+3858
+3859
+3860
+3861
+3862
+3863
+3864
+3865
+3866
+3867
+3868
+3869
+3870
+3871
+3872
+3873
+3874
+3875
+3876
+3877
+3878
+3879
+3880
+3881
+3882
+3883
+3884
+3885
+3886
+3887
+3888
+3889
+3890
+3891
+3892
+3893
+3894
+3895
+3896
+3897
+3898
+3899
+3900
+3901
+3902
+3903
+3904
+3905
+3906
+3907
+3908
+3909
+3910
+3911
+3912
+3913
+3914
+3915
+3916
+3917
+3918
+3919
+3920
+3921
+3922
+3923
+3924
+3925
+3926
+3927
+3928
+3929
+3930
+3931
+3932
+3933
+3934
+3935
+3936
+3937
+3938
+3939
+3940
+3941
+3942
+3943
+3944
+3945
+3946
+3947
+3948
+3949
+3950
+3951
+3952
+3953
+3954
+3955
+3956
+3957
+3958
+3959
+3960
+3961
+3962
+3963
+3964
+3965
+3966
+3967
+3968
+3969
+3970
+3971
+3972
+3973
+3974
+3975
+3976
+3977
+3978
+3979
+3980
+3981
+3982
+3983
+3984
+3985
+3986
+3987
+3988
+3989
+3990
+3991
+3992
+3993
+3994
+3995
+3996
+3997
+3998
+3999
+4000
+4001
+4002
+4003
+4004
+4005
+4006
+4007
+4008
+4009
+4010
+4011
+4012
+4013
+4014
+4015
+4016
+4017
+4018
+4019
+4020
+4021
+4022
+4023
+4024
+4025
+4026
+4027
+4028
+4029
+4030
+4031
+4032
+4033
+4034
+4035
+4036
+4037
+4038
+4039
+4040
+4041
+4042
+4043
+4044
+4045
+4046
+4047
+4048
+4049
+4050
+4051
+4052
+4053
+4054
+4055
+4056
+4057
+4058
+4059
+4060
+4061
+4062
+4063
+4064
+4065
+4066
+4067
+4068
+4069
+4070
+4071
+4072
+4073
+4074
+4075
+4076
+4077
+4078
+4079
+4080
+4081
+4082
+4083
+4084
+4085
+4086
+4087
+4088
+4089
+4090
+4091
+4092
+4093
+4094
+4095
+4096
+4097
+4098
+4099
+4100
+4101
+4102
+4103
+4104
+4105
+4106
+4107
+4108
+4109
+4110
+4111
+4112
+4113
+4114
+4115
+4116
+4117
+4118
+4119
+4120
+4121
+4122
+4123
+4124
+4125
+4126
+4127
+4128
+4129
+4130
+4131
+4132
+4133
+4134
+4135
+4136
+4137
+4138
+4139
+4140
+4141
+4142
+4143
+4144
+4145
+4146
+4147
+4148
+4149
+4150
+4151
+4152
+4153
+4154
+4155
+4156
+4157
+4158
+4159
+4160
+4161
+4162
+4163
+4164
+4165
+4166
+4167
+4168
+4169
+4170
+4171
+4172
+4173
+4174
+4175
+4176
+4177
+4178
+4179
+4180
+4181
+4182
+4183
+4184
+4185
+4186
+4187
+4188
+4189
+4190
+4191
+4192
+4193
+4194
+4195
+4196
+4197
+4198
+4199
+4200
+4201
+4202
+4203
+4204
+4205
+4206
+4207
+4208
+4209
+4210
+4211
+4212
+4213
+4214
+4215
+4216
+4217
+4218
+4219
+4220
+4221
+4222
+4223
+4224
+4225
+4226
+4227
+4228
+4229
+4230
+4231
+4232
+4233
+4234
+4235
+4236
+4237
+4238
+4239
+4240
+4241
+4242
+4243
+4244
+4245
+4246
+4247
+4248
+4249
+4250
+4251
+4252
+4253
+4254
+4255
+4256
+4257
+4258
+4259
+4260
+4261
+4262
+4263
+4264
+4265
+4266
+4267
+4268
+4269
+4270
+4271
+4272
+4273
+4274
+4275
+4276
+4277
+4278
+4279
+4280
+4281
+4282
+4283
+4284
+4285
+4286
+4287
+4288
+4289
+4290
+4291
+4292
+4293
+4294
+4295
+4296
+4297
+4298
+4299
+4300
+4301
+4302
+4303
+4304
+4305
+4306
+4307
+4308
+4309
+4310
+4311
+4312
+4313
+4314
+4315
+4316
+4317
+4318
+4319
+4320
+4321
+4322
+4323
+4324
+4325
+4326
+4327
+4328
+4329
+4330
+4331
+4332
+4333
+4334
+4335
+4336
+4337
+4338
+4339
+4340
+4341
+4342
+4343
+4344
+4345
+4346
+4347
+4348
+4349
+4350
+4351
+4352
+4353
+4354
+4355
+4356
+4357
+4358
+4359
+4360
+4361
+4362
+4363
+4364
+4365
+4366
+4367
+4368
+4369
+4370
+4371
+4372
+4373
+4374
+4375
+4376
+4377
+4378
+4379
+4380
+4381
+4382
+4383
+4384
+4385
+4386
+4387
+4388
+4389
+4390
+4391
+4392
+4393
+4394
+4395
+4396
+4397
+4398
+4399
+4400
+4401
+4402
+4403
+4404
+4405
+4406
+4407
+4408
+4409
+4410
+4411
+4412
+4413
+4414
+4415
+4416
+4417
+4418
+4419
+4420
+4421
+4422
+4423
+4424
+4425
+4426
+4427
+4428
+4429
+4430
+4431
+4432
+4433
+4434
+4435
+4436
+4437
+4438
+4439
+4440
+4441
+4442
+4443
+4444
+4445
+4446
+4447
+4448
+4449
+4450
+4451
+4452
+4453
+4454
+4455
+4456
+4457
+4458
+4459
+4460
+4461
+4462
+4463
+4464
+4465
+4466
+4467
+4468
+4469
+4470
+4471
+4472
+4473
+4474
+4475
+4476
+4477
+4478
+4479
+4480
+4481
+4482
+4483
+4484
+4485
+4486
+4487
+4488
+4489
+4490
+4491
+4492
+4493
+4494
+4495
+4496
+4497
+4498
+4499
+4500
+4501
+4502
+4503
+4504
+4505
+4506
+4507
+4508
+4509
+4510
+4511
+4512
+4513
+4514
+4515
+4516
+4517
+4518
+4519
+4520
+4521
+4522
+4523
+4524
+4525
+4526
+4527
+4528
+4529
+4530
+4531
+4532
+4533
+4534
+4535
+4536
+4537
+4538
+4539
+4540
+4541
+4542
+4543
+4544
+4545
+4546
+4547
+4548
+4549
+4550
+4551
+4552
+4553
+4554
+4555
+4556
+4557
+4558
+4559
+4560
+4561
+4562
+4563
+4564
+4565
+4566
+4567
+4568
+4569
+4570
+4571
+4572
+4573
+4574
+4575
+4576
+4577
+4578
+4579
+4580
+4581
+4582
+4583
+4584
+4585
+4586
+4587
+4588
+4589
+4590
+4591
+4592
+4593
+4594
+4595
+4596
+4597
+4598
+4599
+4600
+4601
+4602
+4603
+4604
+4605
+4606
+4607
+4608
+4609
+4610
+4611
+4612
+4613
+4614
+4615
+4616
+4617
+4618
+4619
+4620
+4621
+4622
+4623
+4624
+4625
+4626
+4627
+4628
+4629
+4630
+4631
+4632
+4633
+4634
+4635
+4636
+4637
+4638
+4639
+4640
+4641
+4642
+4643
+4644
+4645
+4646
+4647
+4648
+4649
+4650
+4651
+4652
+4653
+4654
+4655
+4656
+4657
+4658
+4659
+4660
+4661
+4662
+4663
+4664
+4665
+4666
+4667
+4668
+4669
+4670
+4671
+4672
+4673
+4674
+4675
+4676
+4677
+4678
+4679
+4680
+4681
+4682
+4683
+4684
+4685
+4686
+4687
+4688
+4689
+4690
+4691
+4692
+4693
+4694
+4695
+4696
+4697
+4698
+4699
+4700
+4701
+4702
+4703
+4704
+4705
+4706
+4707
+4708
+4709
+4710
+4711
+4712
+4713
+4714
+4715
+4716
+4717
+4718
+4719
+4720
+4721
+4722
+4723
+4724
+4725
+4726
+4727
+4728
+4729
+4730
+4731
+4732
+4733
+4734
+4735
+4736
+4737
+4738
+4739
+4740
+4741
+4742
+4743
+4744
+4745
+4746
+4747
+4748
+4749
+4750
+4751
+4752
+4753
+4754
+4755
+4756
+4757
+4758
+4759
+4760
+4761
+4762
+4763
+4764
+4765
+4766
+4767
+4768
+4769
+4770
+4771
+4772
+4773
+4774
+4775
+4776
+4777
+4778
+4779
+4780
+4781
+4782
+4783
+4784
+4785
+4786
+4787
+4788
+4789
+4790
+4791
+4792
+4793
+4794
+4795
+4796
+4797
+4798
+4799
+4800
+4801
+4802
+4803
+4804
+4805
+4806
+4807
+4808
+4809
+4810
+4811
+4812
+4813
+4814
+4815
+4816
+4817
+4818
+4819
+4820
+4821
+4822
+4823
+4824
+4825
+4826
+4827
+4828
+4829
+4830
+4831
+4832
+4833
+4834
+4835
+4836
+4837
+4838
+4839
+4840
+4841
+4842
+4843
+4844
+4845
+4846
+4847
+4848
+4849
+4850
+4851
+4852
+4853
+4854
+4855
+4856
+4857
+4858
+4859
+4860
+4861
+4862
+4863
+4864
+4865
+4866
+4867
+4868
+4869
+4870
+4871
+4872
+4873
+4874
+4875
+4876
+4877
+4878
+4879
+4880
+4881
+4882
+4883
+4884
+4885
+4886
+4887
+4888
+4889
+4890
+4891
+4892
+4893
+4894
+4895
+4896
+4897
+4898
+4899
+4900
+4901
+4902
+4903
+4904
+4905
+4906
+4907
+4908
+4909
+4910
+4911
+4912
+4913
+4914
+4915
+4916
+4917
+4918
+4919
+4920
+4921
+4922
+4923
+4924
+4925
+4926
+4927
+4928
+4929
+4930
+4931
+4932
+4933
+4934
+4935
+4936
+4937
+4938
+4939
+4940
+4941
+4942
+4943
+4944
+4945
+4946
+4947
+4948
+4949
+4950
+4951
+4952
+4953
+4954
+4955
+4956
+4957
+4958
+4959
+4960
+4961
+4962
+4963
+4964
+4965
+4966
+4967
+4968
+4969
+4970
+4971
+4972
+4973
+4974
+4975
+4976
+4977
+4978
+4979
+4980
+4981
+4982
+4983
+4984
+4985
+4986
+4987
+4988
+4989
+4990
+4991
+4992
+4993
+4994
+4995
+4996
+4997
+4998
+4999
+5000

diff  --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..9f40e1ae04e67
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cerr.sync_with_stdio.sh.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream cerr;
+
+// Unlike cin/cout/clog, std::cerr is still unbuffered even when sync_with_stdio(false)
+// is called. This test ensures that it keeps producing correctly-ordered output when
+// interleaved with the C stdio functions even after sync_with_stdio(false).
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: 
diff  %t.expected %t.actual
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios_base::sync_with_stdio(false));
+
+  // interleave output with C stdio functions
+  std::fputs("1", stderr);
+  std::cerr << "2";
+  std::fprintf(stderr, "%d", 3);
+  std::cerr << 4;
+
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
new file mode 100644
index 0000000000000..26b5d5093fa86
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.readmany.sh.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// Read an input that is much larger than any reasonable internal buffer so that the
+// buffer has to be refilled (underflow) several times, and check that every value
+// is read correctly and that EOF is detected at the end.
+
+// FILE_DEPENDENCIES: ../many-ints.dat
+
+// RUN: %{build} -DSYNC_WITH_STDIO_FALSE
+// RUN: %{exec} %t.exe < %{temp}/many-ints.dat
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe < %{temp}/many-ints.dat
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+#ifdef SYNC_WITH_STDIO_FALSE
+  assert(std::ios_base::sync_with_stdio(false));
+#endif
+
+  long sum  = 0;
+  int count = 0;
+  int value = 0;
+  while (std::cin >> value) {
+    sum += value;
+    ++count;
+  }
+
+  assert(count == 5000);
+  assert(sum == 5000L * 5001L / 2L); // sum of 1..5000
+  assert(std::cin.eof());
+  assert(!std::cin.good());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..052356449c5ab
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cin.sync_with_stdio.sh.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// <iostream>
+
+// istream cin;
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::cin >> i;
+  assert(i == 1234);
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..ab30dee5f695d
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/clog.sync_with_stdio.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// <iostream>
+
+// ostream clog;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: 
diff  %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::clog << "1234";
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..8497801f1778c
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.interleave_with_stdio.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// In synchronized mode, output written through std::cout and through the C stdio
+// functions must appear in the order in which it was written.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 123456 > %t.expected
+// RUN: 
diff  %t.expected %t.actual
+
+#include <cassert>
+#include <cstdio>
+#include <iostream>
+
+int main(int, char**) {
+  std::fputs("1", stdout);
+  std::cout << "2";
+  std::fprintf(stdout, "%d", 3);
+  std::cout << 4;
+  std::fputs("5", stdout);
+  std::cout << 6;
+
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..3a94964f09223
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/narrow.stream.objects/cout.sync_with_stdio.sh.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// ostream cout;
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: 
diff  %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::cout << "1234";
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp b/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
new file mode 100644
index 0000000000000..e6f9ef3002396
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/sync_with_stdio.state.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iostream>
+
+// Changing the synchronization mode with std::ios_base::sync_with_stdio must
+// preserve the per-stream state of the standard stream objects.
+
+#include <cassert>
+#include <iostream>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+  int dummy       = 0;
+  const int index = std::ios_base::xalloc();
+
+  std::cin.iword(index)  = 11;
+  std::cout.iword(index) = 22;
+  std::cerr.iword(index) = 33;
+  std::clog.iword(index) = 44;
+  std::cout.pword(index) = &dummy;
+
+  std::cout.setf(std::ios_base::hex, std::ios_base::basefield);
+  std::cout.precision(7);
+  std::cout.fill('*');
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  std::wcin.iword(index)  = 11;
+  std::wcout.iword(index) = 22;
+  std::wcerr.iword(index) = 33;
+  std::wclog.iword(index) = 44;
+  std::wcout.pword(index) = &dummy;
+
+  std::wcout.setf(std::ios_base::hex, std::ios_base::basefield);
+  std::wcout.precision(7);
+  std::wcout.fill(L'*');
+#endif
+
+  // Switch to the unsynchronized mode. This swaps the underlying buffers, but the
+  // stream objects and all their state must be preserved.
+  std::ios_base::sync_with_stdio(false);
+
+  assert(std::cin.iword(index) == 11);
+  assert(std::cout.iword(index) == 22);
+  assert(std::cerr.iword(index) == 33);
+  assert(std::clog.iword(index) == 44);
+  assert(std::cout.pword(index) == &dummy);
+  assert((std::cout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::cout.precision() == 7);
+  assert(std::cout.fill() == '*');
+  assert(std::cin.tie() == &std::cout);
+  assert(std::cerr.tie() == &std::cout);
+  assert(std::cerr.flags() & std::ios_base::unitbuf);
+  assert(std::cin.good());
+  assert(std::cout.good());
+  assert(std::cin.rdbuf());
+  assert(std::cout.rdbuf());
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert(std::wcin.iword(index) == 11);
+  assert(std::wcout.iword(index) == 22);
+  assert(std::wcerr.iword(index) == 33);
+  assert(std::wclog.iword(index) == 44);
+  assert(std::wcout.pword(index) == &dummy);
+  assert((std::wcout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::wcout.precision() == 7);
+  assert(std::wcout.fill() == L'*');
+  assert(std::wcin.tie() == &std::wcout);
+  assert(std::wcerr.tie() == &std::wcout);
+  assert(std::wcerr.flags() & std::ios_base::unitbuf);
+  assert(std::wcin.good());
+  assert(std::wcout.good());
+  assert(std::wcin.rdbuf());
+  assert(std::wcout.rdbuf());
+#endif
+
+  // Switch back to synchronized mode and check that the state is still there.
+  std::ios_base::sync_with_stdio(true);
+
+  assert(std::cout.iword(index) == 22);
+  assert(std::cout.pword(index) == &dummy);
+  assert((std::cout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::cout.precision() == 7);
+  assert(std::cout.fill() == '*');
+  assert(std::cin.tie() == &std::cout);
+  assert(std::cerr.tie() == &std::cout);
+  assert(std::cerr.flags() & std::ios_base::unitbuf);
+  assert(std::cout.good());
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert(std::wcout.iword(index) == 22);
+  assert(std::wcout.pword(index) == &dummy);
+  assert((std::wcout.flags() & std::ios_base::basefield) == std::ios_base::hex);
+  assert(std::wcout.precision() == 7);
+  assert(std::wcout.fill() == L'*');
+  assert(std::wcin.tie() == &std::wcout);
+  assert(std::wcerr.tie() == &std::wcout);
+  assert(std::wcerr.flags() & std::ios_base::unitbuf);
+  assert(std::wcout.good());
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..2a65be4863bf5
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcerr.sync_with_stdio.sh.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// wostream wcerr;
+
+// Unlike wcin/wcout/wclog, std::wcerr is still unbuffered even when sync_with_stdio(false)
+// is called. This test ensures that it produces correct output after sync_with_stdio(false).
+// We can't check interleaving with C stdio functions here since C stdio uses narrow characters.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: 
diff  %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  assert(std::ios_base::sync_with_stdio(false));
+
+  std::wcerr << L"12" << 34;
+
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.cpp
new file mode 100644
index 0000000000000..9f96414d02986
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.readmany.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// wistream wcin;
+
+// Read an input that is much larger than any reasonable internal buffer so that the
+// buffer has to be refilled (underflow) several times, and check that every value
+// is read correctly and that EOF is detected at the end.
+
+// FILE_DEPENDENCIES: ../many-ints.dat
+
+// RUN: %{build} -DSYNC_WITH_STDIO_FALSE
+// RUN: %{exec} %t.exe < %{temp}/many-ints.dat
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe < %{temp}/many-ints.dat
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+#ifdef SYNC_WITH_STDIO_FALSE
+  assert(std::ios_base::sync_with_stdio(false));
+#endif
+
+  long sum  = 0;
+  int count = 0;
+  int value = 0;
+  while (std::wcin >> value) {
+    sum += value;
+    ++count;
+  }
+
+  assert(count == 5000);
+  assert(sum == 5000L * 5001L / 2L); // sum of 1..5000
+  assert(std::wcin.eof());
+  assert(!std::wcin.good());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..db6c5ba0e8660
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcin.sync_with_stdio.sh.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// QEMU does not detect EOF, when reading from stdin
+// "echo -n" suppresses any characters after the output and so the test hangs.
+// https://gitlab.com/qemu-project/qemu/-/issues/1963
+// UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
+
+// This test hangs on Android devices that lack shell_v2, which was added in
+// Android N (API 24).
+// UNSUPPORTED: LIBCXX-ANDROID-FIXME && android-device-api={{2[1-3]}}
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// istream wcin;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: echo -n 1234 > %t.input
+// RUN: %{exec} %t.exe < %t.input
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  int i;
+  std::wcin >> i;
+  assert(i == 1234);
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..c0a0f299451fb
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wclog.sync_with_stdio.sh.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// XFAIL: LIBCXX-PICOLIBC-FIXME
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wclog;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe 2> %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: 
diff  %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wclog << L"1234";
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.cpp
new file mode 100644
index 0000000000000..a2cdc070620b1
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.objects/wide.stream.objects/wcout.sync_with_stdio.sh.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-wide-characters
+
+// <iostream>
+
+// ostream wcout;
+
+// Test that we output correctly after setting sync_with_stdio(false).
+// This must be its own test because sync_with_stdio() must be called
+// before any other IO operation for the test to be portable.
+
+// RUN: %{build}
+// RUN: %{exec} %t.exe > %t.actual
+// RUN: echo -n 1234 > %t.expected
+// RUN: 
diff  %t.expected %t.actual
+
+#include <cassert>
+#include <iostream>
+
+int main(int, char**) {
+  // flip-flop the setting to check that construction both ways works as expected
+  assert(std::ios::sync_with_stdio(false));
+  assert(!std::ios::sync_with_stdio(true));
+  assert(std::ios::sync_with_stdio(false));
+  std::wcout << L"1234";
+  return 0;
+}

diff  --git a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
index 0ec24b77560e5..3051f0dc3b1cb 100644
--- a/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
+++ b/libcxx/test/std/input.output/iostreams.base/ios.base/ios.members.static/sync_with_stdio.pass.cpp
@@ -17,6 +17,8 @@
 
 int main(int, char**)
 {
+    ASSERT_SAME_TYPE(decltype(std::ios_base::sync_with_stdio(false)), bool);
+
     assert( std::ios_base::sync_with_stdio(false));
     assert(!std::ios_base::sync_with_stdio(false));
     assert(!std::ios_base::sync_with_stdio(true));

diff  --git a/libcxx/utils/libcxx/test/features/availability.py b/libcxx/utils/libcxx/test/features/availability.py
index c0f2a3d4cdc88..c74383b237521 100644
--- a/libcxx/utils/libcxx/test/features/availability.py
+++ b/libcxx/utils/libcxx/test/features/availability.py
@@ -135,7 +135,7 @@
 # a libc++ flavor that enables availability markup. Similarly, a test could fail when
 # run against the system library of an older version of FreeBSD, even though FreeBSD
 # doesn't provide availability markup at the time of writing this.
-for version in ("12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"):
+for version in ("12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24"):
     features.append(
         Feature(
             name="using-built-library-before-llvm-{}".format(version),


        

From libcxx-commits at lists.llvm.org  Thu Jul 23 00:46:55 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 23 Jul 2026 00:46:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize standard streams with
 sync_with_stdio(false) (PR #209161)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209161@github.com>
Message-ID: <6a61c6ef.6c8668f2.1842d3.8872@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/209161

From libcxx-commits at lists.llvm.org  Thu Jul 23 00:51:17 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 23 Jul 2026 00:51:17 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210360@github.com>
Message-ID: <6a61c7f5.d2cb4adc.23815e.1f86@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/210360

From libcxx-commits at lists.llvm.org  Thu Jul 23 00:52:36 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 23 Jul 2026 00:52:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210360@github.com>
Message-ID: <6a61c844.507fc713.3702e2.dbd9@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/210360

>From 843d1dcd43947b070ff22036af0ef88a7570a6db Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 17 Jul 2026 17:31:21 +0200
Subject: [PATCH] [libc++] Remove locale::id::__next_id from the ABI

---
 libcxx/include/__locale                              |  2 --
 libcxx/lib/abi/CHANGELOG.TXT                         | 12 ++++++++++++
 ...rwin.libcxxabi.v1.stable.exceptions.nonew.abilist |  1 -
 ...id23.libcxxabi.v1.stable.exceptions.nonew.abilist |  1 -
 ...-aix.libcxxabi.v1.stable.exceptions.nonew.abilist |  1 -
 ...-aix.libcxxabi.v1.stable.exceptions.nonew.abilist |  1 -
 ...rwin.libcxxabi.v1.stable.exceptions.nonew.abilist |  1 -
 ...id23.libcxxabi.v1.stable.exceptions.nonew.abilist |  1 -
 ...ebsd.libcxxabi.v1.stable.exceptions.nonew.abilist |  1 -
 ...-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist |  1 -
 ...nu.libcxxabi.v1.stable.noexceptions.nonew.abilist |  1 -
 libcxx/src/locale.cpp                                |  5 ++---
 12 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index bb8f8e7bbf5d9..f878aa48071ca 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -162,8 +162,6 @@ class _LIBCPP_EXPORTED_FROM_ABI locale::id {
   once_flag __flag_;
   int32_t __id_;
 
-  static int32_t __next_id;
-
 public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR id() : __id_(0) {}
   void operator=(const id&) = delete;
diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index b09f297eb65ba..a5915ed5e085e 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -12,6 +12,18 @@ To generate a summary, re-generate the new ABI list using the
 
 New entries should be added directly below the "Version" header.
 
+------------
+Version 24.0
+------------
+
+* [libc++] Remove locale::id::__next_id from the ABI
+
+  `__next_id` is only ever used inside the dylib, so we can avoid making it public.
+
+  All platforms
+  -------------
+  Symbol removed: _ZNSt3__16locale2id9__next_idE
+
 ------------
 Version 23.0
 ------------
diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 2a419e8c8af05..fad3441d59ab7 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1730,7 +1730,6 @@
 {'is_defined': True, 'name': '__ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__16locale2id9__next_idE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale3allE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4noneE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4timeE', 'size': 0, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 414a239757e1c..08ee6a6a515c9 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1368,7 +1368,6 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16gslice6__initEj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 590c0de8ce20f..dd79e25d66915 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -699,7 +699,6 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
-{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index f4f4ab99ad866..e2cc8a0f106f1 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -699,7 +699,6 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
-{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 92a37db24ac5f..be1d6382a7983 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1719,7 +1719,6 @@
 {'is_defined': True, 'name': '__ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__16locale2id9__next_idE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale3allE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4noneE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4timeE', 'size': 0, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index e1b1e110318fa..3f618601469dd 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1368,7 +1368,6 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 11cc8f180e824..159e974775034 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1380,7 +1380,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index a28229a9d6e97..46094de75b221 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1379,7 +1379,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 2b69d6dc89138..df53f1b42fbec 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1350,7 +1350,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index b0a69ab74f94c..40613e72b1a9d 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -562,10 +562,9 @@ void locale::facet::__on_zero_shared() noexcept { delete this; }
 
 // locale::id
 
-constinit int32_t locale::id::__next_id = 0;
-
 long locale::id::__get() {
-  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&__next_id, 1); });
+  constinit static int32_t next_id = 0;
+  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&next_id, 1); });
   return __id_ - 1;
 }
 


From libcxx-commits at lists.llvm.org  Thu Jul 23 01:08:42 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 23 Jul 2026 01:08:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize vector::__move_range
 for trivially copyable types (PR #207384)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207384@github.com>
Message-ID: <6a61cc0a.1b0626d0.1ff4c3.902c@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207384

>From 5394bf3699d37d42208d41dd0471fa6794d2afc6 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 13:34:50 +0200
Subject: [PATCH] [libc++] Optimize vector::__move_range for trivially copyable
 types

This is a code size rather than a performance optimization.

For example, the number of instructions in `__insert_with_iter` is
almost cut in half.
---
 libcxx/include/__vector/vector.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 5e9fa4a7d0030..5cbc95c323d95 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -47,6 +47,7 @@
 #include <__ranges/container_compatible_range.h>
 #include <__ranges/from_range.h>
 #include <__split_buffer>
+#include <__string/constexpr_c_functions.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_allocator.h>
@@ -57,6 +58,7 @@
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_swappable.h>
+#include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/type_identity.h>
 #include <__utility/declval.h>
@@ -1118,6 +1120,18 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointer __to) {
   pointer __old_last  = __layout_.__end_ptr();
   difference_type __n = __old_last - __to;
+#ifndef _LIBCPP_CXX03_LANG
+  if constexpr (__allocator_has_trivial_move_construct_v<allocator_type, value_type> &&
+                is_trivially_copyable<value_type>::value) {
+    if (!__libcpp_is_constant_evaluated()) {
+      auto __count = __from_e - __from_s;
+      __annotate_increase(__count - __n);
+      std::__constexpr_memmove(std::__to_address(__to), std::__to_address(__from_s), __element_count(__count));
+      __layout_.__set_bound_using_pointer(__to + __count);
+      return;
+    }
+  }
+#endif
   {
     pointer __i = __from_s + __n;
     _ConstructTransaction __tx(*this, __from_e - __i);


From libcxx-commits at lists.llvm.org  Thu Jul 23 01:17:57 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 23 Jul 2026 01:17:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove some
	adjacent_view::begin() benchmarks (PR #210265)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210265@github.com>
Message-ID: <6a61ce35.1728eaf3.5e665.2d22@mx.google.com>


https://github.com/philnik777 closed https://github.com/llvm/llvm-project/pull/210265

From libcxx-commits at lists.llvm.org  Thu Jul 23 01:46:07 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 23 Jul 2026 01:46:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove locale::id::__next_id
	from the ABI (PR #210360)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210360@github.com>
Message-ID: <6a61d4cf.8e75e0cb.2de827.02d6@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/210360

>From ac1cc32226a8f55b12d0947842ba42e666e1b64b Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 17 Jul 2026 17:31:21 +0200
Subject: [PATCH] [libc++] Remove locale::id::__next_id from the ABI

---
 libcxx/include/__locale                                   | 2 --
 libcxx/lib/abi/CHANGELOG.TXT                              | 8 ++++++++
 ...le-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...c-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...4-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...le-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...android23.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...n-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 1 -
 ...nux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist | 1 -
 libcxx/src/locale.cpp                                     | 5 ++---
 12 files changed, 10 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index bb8f8e7bbf5d9..f878aa48071ca 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -162,8 +162,6 @@ class _LIBCPP_EXPORTED_FROM_ABI locale::id {
   once_flag __flag_;
   int32_t __id_;
 
-  static int32_t __next_id;
-
 public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR id() : __id_(0) {}
   void operator=(const id&) = delete;
diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index cb29de0942478..30bb3d6254965 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -16,6 +16,14 @@ New entries should be added directly below the "Version" header.
 Version 24.0
 ------------
 
+* [libc++] Remove locale::id::__next_id from the ABI
+
+  `__next_id` is only ever used inside the dylib, so we can avoid making it public.
+
+  All platforms
+  -------------
+  Symbol removed: _ZNSt3__16locale2id9__next_idE
+
 * [libc++] Optimize standard streams with sync_with_stdio(false)
 
   All platforms
diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 2a419e8c8af05..fad3441d59ab7 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1730,7 +1730,6 @@
 {'is_defined': True, 'name': '__ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__16locale2id9__next_idE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale3allE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4noneE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4timeE', 'size': 0, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 09999516face2..4a16243a17501 100644
--- a/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1368,7 +1368,6 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16gslice6__initEj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index d53eeb7532009..65984090a5246 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -699,7 +699,6 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
-{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 384904f79d20e..96396605a8412 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -699,7 +699,6 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
-{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'storage_mapping_class': 'RW', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 92a37db24ac5f..be1d6382a7983 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1719,7 +1719,6 @@
 {'is_defined': True, 'name': '__ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__16locale2id9__next_idE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale3allE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4noneE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__16locale4timeE', 'size': 0, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
index 8f26fdb0462cb..979c5b9d98509 100644
--- a/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android23.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1368,7 +1368,6 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 0a2858da94a67..cb77431bc4f50 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1380,7 +1380,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 903457e983046..c727c028d21e6 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1379,7 +1379,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index c24cdf845b489..86c548d5aa627 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1350,7 +1350,6 @@
 {'is_defined': True, 'name': '_ZNSt3__16gslice6__initEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale14__install_ctorERKS0_PNS0_5facetEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16locale2id5__getEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16locale2id9__next_idE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale3allE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4noneE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16locale4timeE', 'size': 4, 'type': 'OBJECT'}
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index b0a69ab74f94c..40613e72b1a9d 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -562,10 +562,9 @@ void locale::facet::__on_zero_shared() noexcept { delete this; }
 
 // locale::id
 
-constinit int32_t locale::id::__next_id = 0;
-
 long locale::id::__get() {
-  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&__next_id, 1); });
+  constinit static int32_t next_id = 0;
+  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&next_id, 1); });
   return __id_ - 1;
 }
 


From libcxx-commits at lists.llvm.org  Thu Jul 23 02:52:12 2026
From: libcxx-commits at lists.llvm.org (Michael G. Kazakov via libcxx-commits)
Date: Thu, 23 Jul 2026 02:52:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][pstl] Implementation of a
 parallel std::mismatch() based on __pstl::__parallel_find() (PR #209291)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209291@github.com>
Message-ID: <6a61e44c.eb165e5c.2e0c9a.1457@mx.google.com>


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/209291

>From 31208d7946efae8b2e45a121c5178562a7b639c8 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:03:38 +0100
Subject: [PATCH 01/15] Added a basic implementation of parallel 4-legged
 mismatch() based on __parallel_find()

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/libdispatch.h  |   7 +-
 libcxx/include/__pstl/backends/serial.h       |  20 +++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  88 +++++++++++
 .../mismatch/pstl.mismatch.pass.cpp           | 139 ++++++++++++++++++
 7 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__pstl/cpu_algos/mismatch.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..6b5dc87fbe76d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/mismatch.h  
   __pstl/cpu_algos/stable_sort.h
   __pstl/cpu_algos/transform.h
   __pstl/cpu_algos/transform_reduce.h
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 2ba5b4434fc33..aa7633a240722 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -324,6 +324,14 @@ struct __adjacent_difference;
 // operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                                _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index f7141d24df090..5e89c6fe31cd8 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -37,6 +37,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -241,7 +242,7 @@ struct __cpu_traits<__libdispatch_backend_tag> {
       auto __this_chunk_size = __chunk == 0 ? __partitions.__first_chunk_size_ : __partitions.__chunk_size_;
       auto __index           = __chunk == 0 ? 0
                                             : (__chunk * __partitions.__chunk_size_) +
-                                        (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
+                                                  (__partitions.__first_chunk_size_ - __partitions.__chunk_size_);
       if (__this_chunk_size != 1) {
         std::__construct_at(
             __values.get() + __chunk,
@@ -368,6 +369,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index f4142016ccc79..d90022ee37145 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/mismatch.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/transform.h>
 #include <__config>
@@ -55,6 +56,25 @@ struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __mismatch<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate&& __pred) const noexcept {
+    return std::mismatch(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::forward<_Predicate>(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __for_each<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index dd2c3f15403e3..93935d22b9442 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -17,6 +17,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/stable_sort.h>
 #include <__pstl/cpu_algos/transform.h>
 #include <__pstl/cpu_algos/transform_reduce.h>
@@ -100,6 +101,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __stable_sort<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_stable_sort<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
new file mode 100644
index 0000000000000..11ffdc332d633
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_MISMATCH_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
+
+#include <__algorithm/mismatch.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <optional>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_mismatch {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Predicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Look for a mismatch only in the prefix of the two ranges.
+      auto __n = std::min(__last1 - __first1, __last2 - __first2);
+      // Find a position in the first range where the predicate is false against the corresponding position in the
+      // second range.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __first1 + __n,
+          [&__pred, __first1, __first2](_ForwardIterator1 __brick_first1, _ForwardIterator1 __brick_last1) {
+            // Run the sequential mismatch algorithm on these ranges:
+            //   [__brick_first1, __brick_last1) and
+            //   [__first2 + (__brick_first1 - __first1), __first2 + (__brick_last1 - __first1))
+            auto __brick_first2 = __first2 + (__brick_first1 - __first1);
+            return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
+                .first;
+          },
+          less<>{},
+          true);
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      auto __idx = *__res - __first1;
+      return pair<_ForwardIterator1, _ForwardIterator2>{std::move(*__res), __first2 + __idx};
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      // Unsequenced execution is also implicitly covered by the sequential implementation.
+      return std::mismatch(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
new file mode 100644
index 0000000000000..9cbadda428ad9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 1};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(42);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From ddbcca0bf2d23ac5caaed98c0912016bb23c7640 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:05:01 +0100
Subject: [PATCH 02/15] Added a definition of a parallel 4-legged
 std::mismatch()

---
 libcxx/include/__algorithm/pstl.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2bf9b4276baab..ab35b1a3e537b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -138,6 +138,29 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,

>From 81cdb4aa0a748c2a8039e0c478c19259860513a7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 21:41:51 +0100
Subject: [PATCH 03/15] Added a default implementation of __mismatch_3leg based
 on __mismatch

---
 libcxx/include/__algorithm/pstl.h             | 18 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  7 ++++
 libcxx/include/__pstl/backends/default.h      | 36 +++++++++++++++++++
 libcxx/include/module.modulemap.in            |  3 ++
 .../mismatch/pstl.mismatch.pass.cpp           | 20 +++++++++++
 5 files changed, 84 insertions(+)

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index ab35b1a3e537b..01c61fb00f4d5 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -161,6 +161,24 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2> mismatch(
+    _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      equal_to{});
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index aa7633a240722..ecd1f0b9b4049 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -332,6 +332,13 @@ struct __mismatch;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __mismatch_3leg;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+// optional<pair<_ForwardIterator1, _ForwardIterator2>>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _Comp __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 28bc75be147aa..b6a15b234b8da 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -63,6 +63,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// mismatch family
+// ---------------
+// - mismatch_3leg
+//
 // for_each family
 // ---------------
 // - for_each_n
@@ -212,6 +216,38 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// mismatch family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __mismatch_3leg<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Comp>
+  optional<pair<_ForwardIterator1, _ForwardIterator2>>
+  operator()(_Policy&& __policy,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _Comp __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      // Forward to the 4-legged version of mismatch.
+      using _Mismatch           = __dispatch<__mismatch, __current_configuration, _ExecutionPolicy>;
+      _ForwardIterator2 __last2 = __first2 + (__last1 - __first1);
+      return _Mismatch()(
+          __policy,
+          std::move(__first1),
+          std::move(__last1),
+          std::move(__first2),
+          std::move(__last2),
+          std::move(__comp));
+    } else {
+      // Currently only random access iterators are supported for parallel mismatch_3leg.
+      return std::mismatch(std::move(__first1), std::move(__last1), std::move(__first2), std::move(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..752aa35643e2b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2384,6 +2384,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module mismatch {
+        header "__pstl/cpu_algos/mismatch.h"
+      }
       module stable_sort {
         header "__pstl/cpu_algos/stable_sort.h"
         export std_core.utility_core.empty
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 9cbadda428ad9..0cb5c7632eed8 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -10,6 +10,14 @@
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2);
+//
 // template <class ExecutionPolicy,
 //           class ForwardIterator1,
 //           class ForwardIterator2>
@@ -61,6 +69,8 @@ struct Test {
       // empty ranges
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
              std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
     }
@@ -68,18 +78,24 @@ struct Test {
       // single element only
       std::array<int, 1> lhs = {0};
       std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range without mismatch
       std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }
     { // same range with mismatch
       std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
       std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
     }
@@ -102,10 +118,14 @@ struct Test {
       rhs = lhs;
       runway_sample(lhs.size(), [&](size_t i) {
         lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
                std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
         lhs[i] = i;
       });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
       assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
              std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
     }

>From d5910cc36d457006d17cf1adad74a94ee5a4e759 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:20:42 +0100
Subject: [PATCH 04/15] Added predicated versions

---
 libcxx/include/__algorithm/pstl.h             |  49 +++++
 .../mismatch/pstl.mismatch.pass.cpp           |   2 +
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 178 ++++++++++++++++++
 3 files changed, 229 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 01c61fb00f4d5..bc48fd9f48d7f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -31,6 +31,7 @@ _LIBCPP_PUSH_MACROS
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
+#  include <__utility/pair.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -138,6 +139,31 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
@@ -161,6 +187,29 @@ mismatch(_ExecutionPolicy&& __policy,
       equal_to{});
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI pair<_ForwardIterator1, _ForwardIterator2>
+mismatch(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "mismatch requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "mismatch requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__mismatch_3leg, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 0cb5c7632eed8..8584f2779927f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -42,6 +42,8 @@
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
+static_assert(sfinae_test_mismatch<int, int*, int*, int*>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*>);
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
new file mode 100644
index 0000000000000..39e952cbc38f6
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     BinaryPredicate pred);
+//
+// template <class ExecutionPolicy,
+//           class ForwardIterator1,
+//           class ForwardIterator2,
+//           class BinaryPredicate>
+//   pair<ForwardIterator1, ForwardIterator2> mismatch(ExecutionPolicy&& exec,
+//                                                     ForwardIterator1 first1,
+//                                                     ForwardIterator1 last1,
+//                                                     ForwardIterator2 first2,
+//                                                     ForwardIterator2 last2,
+//                                                     BinaryPredicate pred);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(mismatch);
+
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, bool (*)(int, int)>);
+static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+// TODO: switch with a shared implemented once it's merged into main
+template <class Callable>
+void runway_sample(size_t size, Callable callable) {
+  constexpr size_t affix = 16;
+  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
+  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
+    callable(i);
+  }
+  if (size <= affix)
+    return;
+  // size - 16, size - 15, ..., size - 1
+  for (size_t i = size - affix; i < size; ++i) {
+    callable(i);
+  }
+}
+
+struct Pred {
+  bool operator()(int a, int b) const { return a * 2 == b; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      // empty ranges
+      std::array<int, 1> lhs = {0};
+      std::array<int, 1> rhs = {0};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      assert(std::mismatch(
+                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+    }
+    {
+      // single element only
+      std::array<int, 1> lhs = {1};
+      std::array<int, 1> rhs = {2};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range without mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same range with mismatch
+      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
+      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+    }
+    { // second range is smaller
+      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
+      std::array<int, 2> rhs = {0, 2};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // first range is smaller
+      std::array<int, 2> lhs = {0, 1};
+      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+    { // same size, mismatching at various positions
+      std::array<int, 1073> lhs;
+      std::array<int, 1073> rhs;
+      std::iota(std::begin(lhs), std::end(lhs), 0);
+      rhs = lhs;
+      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
+      runway_sample(lhs.size(), [&](size_t i) {
+        lhs[i] = -1;
+        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(
+            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        lhs[i] = i;
+      });
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // same values, different lengths
+      std::array<int, 739> lhs;
+      std::array<int, 739> rhs;
+      lhs.fill(42);
+      rhs.fill(84);
+      runway_sample(lhs.size(), [&](size_t i) {
+        // lhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        // rhs is shorter
+        assert(std::mismatch(
+                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
+               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}

>From 9b207d1481c9076b4bb2af97d049845f2c4e16e0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:39:47 +0100
Subject: [PATCH 05/15] Added tests against custom types

---
 libcxx/include/__pstl/backends/default.h      |  1 +
 .../mismatch/pstl.mismatch.pass.cpp           | 53 ++++++++++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 56 ++++++++++++++++++-
 3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index b6a15b234b8da..776c76f3fdfbd 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -16,6 +16,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/mismatch.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 8584f2779927f..5e46831539c30 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -63,6 +63,30 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
+
 template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
@@ -150,6 +174,29 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -157,5 +204,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 39e952cbc38f6..9c0dfc6c0c127 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -67,8 +67,31 @@ void runway_sample(size_t size, Callable callable) {
   }
 }
 
+// The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
+
+struct X {
+  X() = delete;
+  X(int i) : i(i) {}
+  X(const X&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
+struct Y {
+  Y() = delete;
+  Y(int i) : i(i) {}
+  Y(const Y&) = delete;
+  int value() const { return i; }
+
+private:
+  int i;
+};
+
 struct Pred {
-  bool operator()(int a, int b) const { return a * 2 == b; }
+  bool operator()(int lhs, int rhs) const { return lhs * 2 == rhs; }
+  bool operator()(const X& lhs, const Y& rhs) const { return lhs.value() * 2 == rhs.value(); }
 };
 
 template <class Iter1, class Iter2>
@@ -167,6 +190,31 @@ struct Test {
   }
 };
 
+template <class Iter1, class Iter2>
+struct TestXY {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // same ranges
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+    }
+    { // one element mismatch
+      std::array<X, 3> lhs = {X(1), X(5), X(7)};
+      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
+             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      assert(
+          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
+          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+    }
+  }
+};
+
 int main(int, char**) {
   types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
                     using Iter = typename decltype(v)::type;
@@ -174,5 +222,11 @@ int main(int, char**) {
                         types::forward_iterator_list<const int*>{},
                         TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
                   }});
+  types::for_each(types::forward_iterator_list<const X*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const Y*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<TestXY, Iter>::template apply>{});
+                  }});
   return 0;
 }

>From c21c583527e255d722f312b871d43527e5c4dbb6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:52:50 +0100
Subject: [PATCH 06/15] Added a nodiscard test

---
 libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index f4fffd456a331..f5eaf1642b687 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -63,4 +63,12 @@ void test() {
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
 }

>From 189ba7a5f23d45939e0ff917985bc7049740d1b7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 22:57:47 +0100
Subject: [PATCH 07/15] Added an iterator requirement test

---
 .../pstl.iterator-requirements.verify.cpp        | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 402d8fbf9131a..df11341008bc5 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -135,6 +135,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(pol, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(pol, it, it, non_fwd, pred);      // expected-error@*:* {{static assertion failed: mismatch}}
+
+    (void)std::mismatch(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+    (void)std::mismatch(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: mismatch}}
+  }
+
   {
     (void)std::move(pol, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: move}}
     (void)std::move(pol, it, it, non_fwd);       // expected-error@*:* {{static assertion failed: move}}

>From 731de4a2cf2c196b704234b0ee5653453554b9b7 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:00:09 +0100
Subject: [PATCH 08/15] Added an include of <__algorithm/min.h>

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 11ffdc332d633..8361731e78cfc 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,8 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__algorithm/mismatch.h>
 #include <__config>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/min.h>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>

>From 105a17ed5abc39ad5ac34bd930af8727a9562bed Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 13 Jul 2026 23:05:29 +0100
Subject: [PATCH 09/15] Added an exception handling test

---
 libcxx/include/__pstl/cpu_algos/mismatch.h    |  4 +--
 .../pstl.exception_handling.pass.cpp          | 25 +++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 8361731e78cfc..597a47fb185c8 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -9,9 +9,9 @@
 #ifndef _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 #define _LIBCPP___PSTL_CPU_ALGOS_MISMATCH_H
 
-#include <__config>
-#include <__algorithm/mismatch.h>
 #include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__config>
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index e00046fdb1dd6..cbdb4663f168c 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -248,6 +248,31 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // mismatch(first1, last1, first2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2));
+        });
+
+        // mismatch(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // mismatch(first1, last1, first2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(policy, std::move(first1), std::move(last1), std::move(first2), compare);
+        });
+
+        // mismatch(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::mismatch(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
+
       {
         // move(first, last, dest)
         assert_non_throwing([=, &policy] {

>From 3bd32433db95c6995476e26d5b34c8000a5515f5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 09:43:45 +0100
Subject: [PATCH 10/15] Changed the variable name to avoid shadowing

---
 .../alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 12 ++++++------
 .../mismatch/pstl.mismatch_pred.pass.cpp             | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 5e46831539c30..941fe07b58327 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -67,22 +67,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 bool operator==(const X& lhs, const Y& rhs) { return lhs.value() == rhs.value(); }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 9c0dfc6c0c127..a4b15a56bd071 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -71,22 +71,22 @@ void runway_sample(size_t size, Callable callable) {
 
 struct X {
   X() = delete;
-  X(int i) : i(i) {}
+  X(int i) : i_(i) {}
   X(const X&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Y {
   Y() = delete;
-  Y(int i) : i(i) {}
+  Y(int i) : i_(i) {}
   Y(const Y&) = delete;
-  int value() const { return i; }
+  int value() const { return i_; }
 
 private:
-  int i;
+  int i_;
 };
 
 struct Pred {

>From a51af3556cdb7f7e24732077b6dce511e3ee9e89 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 20:27:14 +0100
Subject: [PATCH 11/15] Use raw C array in the tests

---
 .../mismatch/pstl.mismatch.pass.cpp           | 153 +++++++-------
 .../mismatch/pstl.mismatch_pred.pass.cpp      | 192 +++++++++++-------
 2 files changed, 198 insertions(+), 147 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 941fe07b58327..17edf34ee7832 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -28,7 +28,6 @@
 //                                                     ForwardIterator2 last2);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -93,82 +92,98 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(
+          std::mismatch(
+              policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs))) ==
+          std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 1};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 1};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::copy(std::begin(lhs), std::end(lhs), std::begin(rhs));
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(
+            std::mismatch(
+                policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+            std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(42);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 42);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(
+                   policy,
+                   Iter1(std::begin(lhs)),
+                   Iter1(std::begin(lhs) + i),
+                   Iter2(std::begin(rhs)),
+                   Iter2(std::end(rhs))) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i)) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i)) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -179,20 +194,22 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(7)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(7)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(1), Y(5), Y(8)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end())) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(1), Y(5), Y(8)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(
+                 policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Iter2(std::end(rhs))) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index a4b15a56bd071..18c4fe1abad0c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -32,7 +32,6 @@
 //                                                     BinaryPredicate pred);
 
 #include <algorithm>
-#include <array>
 #include <cassert>
 #include <functional>
 #include <iterator>
@@ -100,91 +99,120 @@ struct Test {
   void operator()(ExecutionPolicy&& policy) {
     {
       // empty ranges
-      std::array<int, 1> lhs = {0};
-      std::array<int, 1> rhs = {0};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
-      assert(std::mismatch(
-                 policy, Iter1(lhs.begin()), Iter1(lhs.begin()), Iter2(rhs.begin()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin()), Iter2(rhs.begin())));
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::begin(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::begin(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs)), Iter2(std::begin(rhs))));
     }
     {
       // single element only
-      std::array<int, 1> lhs = {1};
-      std::array<int, 1> rhs = {2};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[1] = {1};
+      int rhs[1] = {2};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range without mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 6, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 6, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same range with mismatch
-      std::array<int, 8> lhs = {0, 1, 2, 3, 0, 1, 2, 3};
-      std::array<int, 8> rhs = {0, 2, 4, 7, 0, 2, 4, 6};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 3), Iter2(rhs.begin() + 3)));
+      int lhs[8] = {0, 1, 2, 3, 0, 1, 2, 3};
+      int rhs[8] = {0, 2, 4, 7, 0, 2, 4, 6};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 3), Iter2(std::begin(rhs) + 3)));
     }
     { // second range is smaller
-      std::array<int, 8> lhs = {0, 1, 2, 2, 0, 1, 2, 3};
-      std::array<int, 2> rhs = {0, 2};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[8] = {0, 1, 2, 2, 0, 1, 2, 3};
+      int rhs[2] = {0, 2};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // first range is smaller
-      std::array<int, 2> lhs = {0, 1};
-      std::array<int, 8> rhs = {0, 2, 2, 2, 0, 1, 2, 3};
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      int lhs[2] = {0, 1};
+      int rhs[8] = {0, 2, 2, 2, 0, 1, 2, 3};
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
     { // same size, mismatching at various positions
-      std::array<int, 1073> lhs;
-      std::array<int, 1073> rhs;
+      int lhs[1073];
+      int rhs[1073];
       std::iota(std::begin(lhs), std::end(lhs), 0);
-      rhs = lhs;
-      std::for_each(std::begin(rhs), std::end(rhs), [](int& x) { x *= 2; });
-      runway_sample(lhs.size(), [&](size_t i) {
+      std::transform(std::begin(lhs), std::end(lhs), std::begin(rhs), [](int x) { return x * 2; });
+      runway_sample(std::size(lhs), [&](size_t i) {
         lhs[i] = -1;
-        assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
-        assert(
-            std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-            std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+               std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         lhs[i] = i;
       });
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // same values, different lengths
-      std::array<int, 739> lhs;
-      std::array<int, 739> rhs;
-      lhs.fill(42);
-      rhs.fill(84);
-      runway_sample(lhs.size(), [&](size_t i) {
+      int lhs[739];
+      int rhs[739];
+      std::fill(std::begin(lhs), std::end(lhs), 42);
+      std::fill(std::begin(rhs), std::end(rhs), 84);
+      runway_sample(std::size(lhs), [&](size_t i) {
         // lhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.begin() + i), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::begin(lhs) + i),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::end(rhs)),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
         // rhs is shorter
-        assert(std::mismatch(
-                   policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.begin() + i), Pred{}) ==
-               std::make_pair(Iter1(lhs.begin() + i), Iter2(rhs.begin() + i)));
+        assert(std::mismatch(policy,
+                             Iter1(std::begin(lhs)),
+                             Iter1(std::end(lhs)),
+                             Iter2(std::begin(rhs)),
+                             Iter2(std::begin(rhs) + i),
+                             Pred{}) == std::make_pair(Iter1(std::begin(lhs) + i), Iter2(std::begin(rhs) + i)));
       });
     }
   }
@@ -195,22 +223,28 @@ struct TestXY {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     { // same ranges
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(14)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.end()), Iter2(rhs.end())));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(14)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::end(lhs)), Iter2(std::end(rhs))));
     }
     { // one element mismatch
-      std::array<X, 3> lhs = {X(1), X(5), X(7)};
-      std::array<Y, 3> rhs = {Y(2), Y(10), Y(13)};
-      assert(std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Pred{}) ==
-             std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
-      assert(
-          std::mismatch(policy, Iter1(lhs.begin()), Iter1(lhs.end()), Iter2(rhs.begin()), Iter2(rhs.end()), Pred{}) ==
-          std::make_pair(Iter1(lhs.begin() + 2), Iter2(rhs.begin() + 2)));
+      X lhs[3] = {X(1), X(5), X(7)};
+      Y rhs[3] = {Y(2), Y(10), Y(13)};
+      assert(std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::end(lhs)), Iter2(std::begin(rhs)), Pred{}) ==
+             std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
+      assert(std::mismatch(policy,
+                           Iter1(std::begin(lhs)),
+                           Iter1(std::end(lhs)),
+                           Iter2(std::begin(rhs)),
+                           Iter2(std::end(rhs)),
+                           Pred{}) == std::make_pair(Iter1(std::begin(lhs) + 2), Iter2(std::begin(rhs) + 2)));
     }
   }
 };

>From f3bfa5528fb2aea972a018cc68e56133e32db99b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 14 Jul 2026 21:08:55 +0100
Subject: [PATCH 12/15] Fixed typos in the comment

---
 .../algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp | 2 +-
 .../alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index 17edf34ee7832..ed59fa9eca669 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -46,7 +46,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 18c4fe1abad0c..338503d784a31 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -50,7 +50,7 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch with a shared implemented once it's merged into main
+// TODO: switch to the shared implementation once it's merged into main
 template <class Callable>
 void runway_sample(size_t size, Callable callable) {
   constexpr size_t affix = 16;

>From 4c7fccd82c3a4bea33038c9eee3ebcefddd7f230 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:43:20 +0100
Subject: [PATCH 13/15] Added comments explaining what the parameters mean

---
 libcxx/include/__pstl/cpu_algos/mismatch.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__pstl/cpu_algos/mismatch.h b/libcxx/include/__pstl/cpu_algos/mismatch.h
index 597a47fb185c8..3d702b03d3afe 100644
--- a/libcxx/include/__pstl/cpu_algos/mismatch.h
+++ b/libcxx/include/__pstl/cpu_algos/mismatch.h
@@ -63,8 +63,9 @@ struct __cpu_parallel_mismatch {
             return std::mismatch(std::move(__brick_first1), std::move(__brick_last1), std::move(__brick_first2), __pred)
                 .first;
           },
-          less<>{},
-          true);
+          less<>{}, // `less` here means the lowest index among the mismatches
+          true      // `true` here means we want the first mismatch, not the last
+      );
       if (!__res) {
         return std::nullopt; // Failed to run the algorithm, propagate the error.
       }

>From 22dfeede2c12c49a9f57db064f80e6d24e96f41b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 15 Jul 2026 21:50:51 +0100
Subject: [PATCH 14/15] Added tests of the return types

---
 .../mismatch/pstl.mismatch.pass.cpp              | 10 ++++++++++
 .../mismatch/pstl.mismatch_pred.pass.cpp         | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index ed59fa9eca669..cc7ef6feb2356 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -90,6 +90,16 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1]       = {0};
+      int rhs[1]       = {0};
+      auto res_3legged = std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)));
+      auto res_4legged = std::mismatch(
+          policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Iter2(std::begin(rhs)));
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index 338503d784a31..d80da0cf0b542 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -97,6 +97,22 @@ template <class Iter1, class Iter2>
 struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
+    {
+      // check the return types
+      int lhs[1] = {0};
+      int rhs[1] = {0};
+      auto res_3legged =
+          std::mismatch(policy, Iter1(std::begin(lhs)), Iter1(std::begin(lhs)), Iter2(std::begin(rhs)), Pred{});
+      auto res_4legged = std::mismatch(
+          policy,
+          Iter1(std::begin(lhs)),
+          Iter1(std::begin(lhs)),
+          Iter2(std::begin(rhs)),
+          Iter2(std::begin(rhs)),
+          Pred{});
+      static_assert(std::is_same_v<decltype(res_3legged), std::pair<Iter1, Iter2>>);
+      static_assert(std::is_same_v<decltype(res_4legged), std::pair<Iter1, Iter2>>);
+    }
     {
       // empty ranges
       int lhs[1] = {0};

>From 8f9ddba49cfc2b501d8d42b1c8027f64b0695a31 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 21 Jul 2026 19:39:00 +0100
Subject: [PATCH 15/15] Rebased on support/runway_sample.h

---
 .../mismatch/pstl.mismatch.pass.cpp             | 17 +----------------
 .../mismatch/pstl.mismatch_pred.pass.cpp        | 17 +----------------
 2 files changed, 2 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
index cc7ef6feb2356..83f4b0f16d6c1 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch.pass.cpp
@@ -38,6 +38,7 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
@@ -46,22 +47,6 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*>);
 
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 // The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
 
 struct X {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
index d80da0cf0b542..89cf899a47b4c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/mismatch/pstl.mismatch_pred.pass.cpp
@@ -42,6 +42,7 @@
 #include "test_iterators.h"
 #include "test_macros.h"
 #include "type_algorithms.h"
+#include "runway_sample.h"
 
 EXECUTION_POLICY_SFINAE_TEST(mismatch);
 
@@ -50,22 +51,6 @@ static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*,
 static_assert(sfinae_test_mismatch<int, int*, int*, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_mismatch<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
 
-// TODO: switch to the shared implementation once it's merged into main
-template <class Callable>
-void runway_sample(size_t size, Callable callable) {
-  constexpr size_t affix = 16;
-  // 0, 1, 2, ..., 15, 16, 50, 157, 493, 1548, ...
-  for (size_t i = 0; i < size; i = i < affix ? i + 1 : size_t(3.1415 * i)) {
-    callable(i);
-  }
-  if (size <= affix)
-    return;
-  // size - 16, size - 15, ..., size - 1
-  for (size_t i = size - affix; i < size; ++i) {
-    callable(i);
-  }
-}
-
 // The types X and Y are provided to test that the mismatch algorithm can be used with heterogeneous custom types.
 
 struct X {


From libcxx-commits at lists.llvm.org  Thu Jul 23 04:21:23 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 23 Jul 2026 04:21:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libunwind] [llvm] [libc++] Update macOS
	and AppleClang (PR #193711)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/193711@github.com>
Message-ID: <6a61f933.824e11a6.322560.fde3@mx.google.com>


philnik777 wrote:

> @philnik777 Can we really not go any lower than 26.4?

Most definitely not months after the update. Why do you care?

https://github.com/llvm/llvm-project/pull/193711

From libcxx-commits at lists.llvm.org  Thu Jul 23 05:13:44 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 23 Jul 2026 05:13:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] a7d2602 - [libc++] Update release
 procedure for libc++ & friends (#210383)
Message-ID: <6a620578.507fc713.3702e2.08b4@mx.google.com>


Author: Louis Dionne
Date: 2026-07-23T08:13:39-04:00
New Revision: a7d2602abf4f211e8027288f23629f8cc93ec30c

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

LOG: [libc++] Update release procedure for libc++ & friends (#210383)

The release procedure implied that some tasks were done by the release
manager, but in practice they are done by the libc++ developers.

Also, mention using the `llvm-premerge-libcxx-release-runners` runner
set on the release branch, which was overlooked in the previous notes.

Other than that, minor reformulations.

Added: 
    

Modified: 
    libcxx/docs/ReleaseProcedure.rst

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseProcedure.rst b/libcxx/docs/ReleaseProcedure.rst
index b3f0d129a17fb..69cc0c340ecc2 100644
--- a/libcxx/docs/ReleaseProcedure.rst
+++ b/libcxx/docs/ReleaseProcedure.rst
@@ -11,8 +11,6 @@ This page describes the libc++ procedure for that release.
 Prepare the release
 ===================
 
-This is done by the libc++ developers.
-
 It should be finished before the Release managers start branching the new
 release:
 
@@ -37,10 +35,8 @@ release:
 * Make sure all libc++ supported compilers in the CI are updated to their
   latest release.
 
-Branching
-=========
-
-This is done by the LLVM Release managers.
+After the branch is created
+===========================
 
 After branching for an LLVM release:
 
@@ -48,28 +44,21 @@ After branching for an LLVM release:
 2. Update the version number in ``libcxx/docs/conf.py``
 3. Update ``_LIBCPPABI_VERSION`` in ``libcxxabi/include/cxxabi.h``
 4. Update ``_LIBUNWIND_VERSION`` in ``libunwind/include/__libunwind_config.h``
-5. Create a release notes file for the next release from the template
-6. Point to the new release notes file from ``libcxx/docs/ReleaseNotes.rst``
-
-Post branching
-==============
-
-This is done by the libc++ developers.
-
-After branching it takes a couple of days before the new LLVM ToT version is
-available on `<https://apt.llvm.org>`_. Once it is available the pre-commit CI
-can start using the new ToT version. In order to make sure patches can be
-backported to the release branch the oldest compiler is not removed yet.
-
-The section ``Upcoming Deprecations and Removals`` is cleared by the release
-managers. Copy back the items that were in this section.
+5. Create a release notes file for the next release from the previous ones and point to it from
+   ``libcxx/docs/ReleaseNotes.rst``. Remove entries that do not apply anymore, but keep in mind
+   that some entries (such as upcoming deprecations) may still apply, may need rewording and may
+   also require follow up PRs to implement.
+6. Update the set of runners targeted by the CI on the release branch to ``llvm-premerge-libcxx-release-runners``, and
+   make sure that runner set is using the appropriate image. This ensures that the release branch CI keeps working even
+   if the main branch starts using newer images.
+7. Update the pre-commit CI to use the new ToT version of Clang available from Compiler Explorer. In order
+   to make sure patches can be backported to the release branch, we don't remove the oldest compiler yet.
 
 Post release
 ============
 
-This is done by the libc++ developers.
-
-Support for the ToT - 3 version is removed:
+Once the release is done and cherry-picks are not expected, we remove support for the ToT - 3 version Clang.
+We also perform associated cleanups:
 
 - Search for ``LLVM RELEASE`` and address their comments
 - Search for test that have ``UNSUPPORTED`` or ``XFAIL`` for the no longer supported version


        

From libcxx-commits at lists.llvm.org  Thu Jul 23 05:13:47 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 05:13:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Update release procedure for
	libc++ & friends (PR #210383)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210383@github.com>
Message-ID: <6a62057b.9edb5c9d.3c157a.b6f7@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/210383

From libcxx-commits at lists.llvm.org  Thu Jul 23 05:15:46 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 05:15:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove the
 _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR opt-out (PR #211329)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211329@github.com>
Message-ID: <6a6205f2.c2770f49.399be.6bda@mx.google.com>


ldionne wrote:

> FWIW I'd probably wait at least until after the 24.1 release, but personally I don't care much either way.

I'm fine with that, I just did it now because it was slated for removal in LLVM 24, but if we think that's too fast we can reword the release note instead to say we'll do it in LLVM 25, and then I can make that PR a draft for the time being.

https://github.com/llvm/llvm-project/pull/211329

From libcxx-commits at lists.llvm.org  Thu Jul 23 05:22:39 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 23 Jul 2026 05:22:39 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a62078f.92ea0e2a.2ed880.c8b0@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210993

>From 8e4ce1588756045195a9c05c3688ee0919433b32 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 15:13:46 +0200
Subject: [PATCH 1/4] add: constexpr to fpclassify

---
 libcxx/include/cmath                                      | 2 +-
 libcxx/include/math.h                                     | 8 ++++----
 .../libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..992854b0490a3 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -138,7 +138,7 @@ long double    tanhl(long double x);
 
 bool signbit(arithmetic x);
 
-int fpclassify(arithmetic x);
+int fpclassify(arithmetic x); // constexpr since C++23
 
 bool isfinite(arithmetic x);
 bool isinf(arithmetic x);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..86ad9a1ae9b10 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -389,22 +389,22 @@ namespace __math {
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..926ff37cddbfc 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -203,9 +203,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From b6ecf4cfc60def795b8f4814af4e9df2feda5121 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 16:12:31 +0200
Subject: [PATCH 2/4] update: gcc tests

---
 .../libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..455c03ef58d18 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -197,9 +197,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From f191ac6676bcf419b3eb0aa92ddcb1291744a796 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 18:45:51 +0200
Subject: [PATCH 3/4] add: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 79 +++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/fpclassify.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
new file mode 100644
index 0000000000000..fda9f28a1389f
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool fpclassify(floating-point-type x); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
+    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+
+    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+
+    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(-0.0)) == FP_ZERO);
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+
+    if (std::is_signed<T>::value) {
+      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 912975ef33f9fd185b3accd72d7016d7d7be6ea5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:22:26 +0200
Subject: [PATCH 4/4] add: _LIBCPP_PREFERRED_OVERLOAD

---
 libcxx/include/math.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 86ad9a1ae9b10..fa8d293604c7f 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -387,6 +387,30 @@ namespace __math {
 
 // fpclassify
 
+#      ifdef _LIBCPP_PREFERRED_OVERLOAD
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int fpclassify(float __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(long double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(_A1 __x) _NOEXCEPT {
+  return __x == 0 ? FP_ZERO : FP_NORMAL;
+}
+
+#      else
+
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
@@ -408,6 +432,8 @@ template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
+#      endif
+
 } // namespace __math
 
 _LIBCPP_END_NAMESPACE_STD


From libcxx-commits at lists.llvm.org  Thu Jul 23 05:32:21 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 23 Jul 2026 05:32:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a6209d5.3cce8983.247e47.1a8b@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210993

>From 8e4ce1588756045195a9c05c3688ee0919433b32 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 15:13:46 +0200
Subject: [PATCH 1/5] add: constexpr to fpclassify

---
 libcxx/include/cmath                                      | 2 +-
 libcxx/include/math.h                                     | 8 ++++----
 .../libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..992854b0490a3 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -138,7 +138,7 @@ long double    tanhl(long double x);
 
 bool signbit(arithmetic x);
 
-int fpclassify(arithmetic x);
+int fpclassify(arithmetic x); // constexpr since C++23
 
 bool isfinite(arithmetic x);
 bool isinf(arithmetic x);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..86ad9a1ae9b10 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -389,22 +389,22 @@ namespace __math {
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..926ff37cddbfc 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -203,9 +203,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From b6ecf4cfc60def795b8f4814af4e9df2feda5121 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 16:12:31 +0200
Subject: [PATCH 2/5] update: gcc tests

---
 .../libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..455c03ef58d18 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -197,9 +197,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From f191ac6676bcf419b3eb0aa92ddcb1291744a796 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 18:45:51 +0200
Subject: [PATCH 3/5] add: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 79 +++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/fpclassify.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
new file mode 100644
index 0000000000000..fda9f28a1389f
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool fpclassify(floating-point-type x); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
+    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+
+    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+
+    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(-0.0)) == FP_ZERO);
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+
+    if (std::is_signed<T>::value) {
+      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 912975ef33f9fd185b3accd72d7016d7d7be6ea5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:22:26 +0200
Subject: [PATCH 4/5] add: _LIBCPP_PREFERRED_OVERLOAD

---
 libcxx/include/math.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 86ad9a1ae9b10..fa8d293604c7f 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -387,6 +387,30 @@ namespace __math {
 
 // fpclassify
 
+#      ifdef _LIBCPP_PREFERRED_OVERLOAD
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int fpclassify(float __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(long double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(_A1 __x) _NOEXCEPT {
+  return __x == 0 ? FP_ZERO : FP_NORMAL;
+}
+
+#      else
+
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
@@ -408,6 +432,8 @@ template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
+#      endif
+
 } // namespace __math
 
 _LIBCPP_END_NAMESPACE_STD

>From 1b02edd8888f7fe60ed3053e6e8112902b5dac3b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:32:06 +0200
Subject: [PATCH 5/5] refactor: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 65 ++++++++++---------
 1 file changed, 33 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
index fda9f28a1389f..fcfd73d9bacf2 100644
--- a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -18,62 +18,63 @@
 
 struct TestFloat {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
-    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
-    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                    = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max   = lim::max();
+    TEST_CONSTEXPR_CXX23 T min   = lim::min();
+    TEST_CONSTEXPR_CXX23 T d_min = lim::denorm_min();
+    TEST_CONSTEXPR_CXX23 T inf   = lim::infinity();
+    TEST_CONSTEXPR_CXX23 T nan   = lim::quiet_NaN();
+    TEST_CONSTEXPR_CXX23 T s_nan = lim::signaling_NaN();
 
-    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
-    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(nan) == FP_NAN);
+    assert(std::fpclassify(s_nan) == FP_NAN);
+
+    assert(std::fpclassify(inf) == FP_INFINITE);
+    assert(std::fpclassify(-inf) == FP_INFINITE);
 
     assert(std::fpclassify(T(1)) == FP_NORMAL);
     assert(std::fpclassify(T(-1)) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+    assert(std::fpclassify(max) == FP_NORMAL);
+    assert(std::fpclassify(min) == FP_NORMAL);
 
-    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+    assert(std::fpclassify(d_min) == FP_SUBNORMAL);
 
     assert(std::fpclassify(T(0)) == FP_ZERO);
     assert(std::fpclassify(T(-0.0)) == FP_ZERO);
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
 struct TestInt {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                  = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max = lim::max();
+    TEST_CONSTEXPR_CXX23 T low = lim::lowest();
+
     assert(std::fpclassify(T(0)) == FP_ZERO);
     assert(std::fpclassify(T(1)) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(max) == FP_NORMAL);
 
     if (std::is_signed<T>::value) {
-      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(low) == FP_NORMAL);
       assert(std::fpclassify(T(-1)) == FP_NORMAL);
     }
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX23 bool test() {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 23
+  static_assert(test());
+#endif
+
   return 0;
 }


From libcxx-commits at lists.llvm.org  Thu Jul 23 05:32:53 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 23 Jul 2026 05:32:53 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	comparison functions (PR #210075)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210075@github.com>
Message-ID: <6a6209f5.3cce8983.247e47.2fcf@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210075

>From 1fc2d6029a58ceaed872953980ae0f9b2bc9e178 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:47:35 +0200
Subject: [PATCH 01/25] add: constexpr to isgreater

---
 libcxx/include/__math/traits.h                        | 11 +++++------
 .../numerics/c.math/constexpr-cxx23-clang.pass.cpp    |  6 +++---
 .../numerics/c.math/constexpr-cxx23-gcc.pass.cpp      |  6 +++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ff22cee7305d7..28269eef46743 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,8 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
+    bool isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -106,8 +105,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
+    bool isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 
@@ -137,7 +135,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 // isgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
@@ -216,7 +215,7 @@ template <class _A1>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreater((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..642806ffbd960 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -227,9 +227,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..34513b459a056 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -221,9 +221,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0) == 1);
   ASSERT_CONSTEXPR_CXX23(std::signbit(-1.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
   ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);

>From f6f5b24db9910930a4f63731ebc408653c43b530 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 16:51:44 +0200
Subject: [PATCH 02/25] revert: clang-format mistake

---
 libcxx/include/__math/traits.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 28269eef46743..48a68f31c2101 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isinf(double __x) _NOEXCEPT {
+    bool
+    isinf(double __x) _NOEXCEPT {
   return __builtin_isinf(__x);
 }
 
@@ -105,7 +106,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
+    bool
+    isnan(double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
 }
 

>From 9cfce54da2a26d9654a04d41c577bd5dacabe942 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:44:41 +0200
Subject: [PATCH 03/25] add: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreater.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
new file mode 100644
index 0000000000000..7b119df05f791
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+template <typename T>
+struct ZeroConvertibleTo {
+  operator T() const { return T(0); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 184002328c9527189365872f28e6e5b7d1548ed6 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 16 Jul 2026 23:53:01 +0200
Subject: [PATCH 04/25] add: mixed-type promotion tests

---
 .../test/std/numerics/c.math/isgreater.pass.cpp | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 7b119df05f791..780b61e321cf1 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -23,6 +23,8 @@ struct TestFloat {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -40,6 +42,8 @@ struct TestInt {
   template <class T>
   static TEST_CONSTEXPR_CXX23 bool test() {
     assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
     return true;
   }
@@ -58,14 +62,17 @@ struct ConvertibleTo {
   operator T() const { return T(1); }
 };
 
-template <typename T>
-struct ZeroConvertibleTo {
-  operator T() const { return T(0); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
   return 0;
 }

>From 3dd1d455e2ef97c5ba3ec364627d4a4479d6deab Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:01:01 +0200
Subject: [PATCH 05/25] add: infinity and NaN assertion tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 780b61e321cf1..608c044d49ff4 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -26,6 +26,15 @@ struct TestFloat {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
     return true;
   }
 

>From 4e3a4fe538be67ade2045e862539a6765fb91d77 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:03:02 +0200
Subject: [PATCH 06/25] add: signed int tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 608c044d49ff4..07f26847fb1c9 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,11 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    if (std::is_signed<T>::value) {
+      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
     return true;
   }
 

>From 207837d2233abd69599e9e45d02059b46bdddc91 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 00:18:21 +0200
Subject: [PATCH 07/25] add: lowest int comparisons

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 07f26847fb1c9..c7853974d7a66 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -54,6 +54,9 @@ struct TestInt {
     assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
     assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
 
+    assert(!std::isgreater(T(1), T(1)));
+    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+
     if (std::is_signed<T>::value) {
       assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
       assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));

>From 7afee657c477977b070c404c8288823e6015e045 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 14:40:56 +0200
Subject: [PATCH 08/25] apply: constexpr to all is<...> math functions and flip
 tests

---
 libcxx/include/__math/traits.h                | 20 ++++++-------
 .../c.math/constexpr-cxx23-clang.pass.cpp     | 30 +++++++++----------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 48a68f31c2101..7c0fdc90ce031 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -146,7 +146,7 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -154,7 +154,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isless
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
@@ -162,7 +162,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +170,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +178,7 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,35 +224,35 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isless((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 642806ffbd960..e8211ebe94230 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -231,25 +231,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From ebb642255cc08a2940459b58aa72b432c7b77748 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:16:18 +0200
Subject: [PATCH 09/25] fix: formatting

---
 libcxx/include/__math/traits.h | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 7c0fdc90ce031..0d6afca2e8936 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,9 +82,7 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isinf(double __x) _NOEXCEPT {
-  return __builtin_isinf(__x);
+    bool turn __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -106,10 +104,8 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool
-    isnan(double __x) _NOEXCEPT {
-  return __builtin_isnan(__x);
-}
+    bool isnan(double __x) _NOEXCEPT {
+  return 
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);
@@ -146,7 +142,8 @@ isgreater(_A1 __x, _A2 __y) _NOEXCEPT {
 // isgreaterequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }
@@ -156,13 +153,13 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y);
-}
+  return __builtin_isless((type)__x, (type)__y); }
 
 // islessequal
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessequal(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessequal((type)__x, (type)__y);
 }
@@ -170,7 +167,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // islessgreater
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+islessgreater(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_islessgreater((type)__x, (type)__y);
 }
@@ -178,7 +176,8 @@ template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_ar
 // isunordered
 
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isunordered(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isunordered((type)__x, (type)__y);
 }
@@ -224,7 +223,8 @@ template <class _A1, class _A2>
 
 template <class _A1, class _A2>
   requires is_arithmetic_v<_A1> && is_arithmetic_v<_A2>
-[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isgreaterequal(_A1 __x, _A2 __y) noexcept {
+[[nodiscard]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool
+isgreaterequal(_A1 __x, _A2 __y) noexcept {
   using type = __promote_t<_A1, _A2>;
   return __builtin_isgreaterequal((type)__x, (type)__y);
 }

>From 336fa94a06ee606f43a03fcbed9c2b8faeaeab04 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:20:04 +0200
Subject: [PATCH 10/25] fix: accidental automatic formatting (clang-format)

---
 libcxx/include/__math/traits.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index 0d6afca2e8936..ef20e330df35c 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -82,7 +82,9 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool turn __builtin_isinf(__x);
+    bool
+    isinf(double __x) _NOEXCEPT {
+  return __builtin_isinf(__x);
 }
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isinf(long double __x) _NOEXCEPT {
@@ -104,8 +106,10 @@ template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
 #ifdef _LIBCPP_PREFERRED_OVERLOAD
 _LIBCPP_PREFERRED_OVERLOAD
 #endif
-    bool isnan(double __x) _NOEXCEPT {
-  return 
+    bool
+    isnan(double __x) _NOEXCEPT {
+  return __builtin_isnan(__x);
+}
 
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnan(long double __x) _NOEXCEPT {
   return __builtin_isnan(__x);

>From 84b6b7442b8e8f0f0cf1672557a6e8b3abca0d7b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 15:37:39 +0200
Subject: [PATCH 11/25] formatter: remove accidental trailing space

---
 libcxx/include/__math/traits.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/traits.h b/libcxx/include/__math/traits.h
index ef20e330df35c..f32dc47ab1b1b 100644
--- a/libcxx/include/__math/traits.h
+++ b/libcxx/include/__math/traits.h
@@ -157,7 +157,8 @@ isgreaterequal(_A1 __x, _A2 __y) _NOEXCEPT {
 template <class _A1, class _A2, __enable_if_t<is_arithmetic<_A1>::value && is_arithmetic<_A2>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isless(_A1 __x, _A2 __y) _NOEXCEPT {
   using type = __promote_t<_A1, _A2>;
-  return __builtin_isless((type)__x, (type)__y); }
+  return __builtin_isless((type)__x, (type)__y);
+}
 
 // islessequal
 

>From 9f279ea0daa21db0954068d8a4a68080f4c5e767 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 16:48:11 +0200
Subject: [PATCH 12/25] add: isgreaterequal.pass.cpp

---
 .../numerics/c.math/isgreaterequal.pass.cpp   | 93 +++++++++++++++++++
 1 file changed, 93 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
new file mode 100644
index 0000000000000..d6ae553e961e5
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(!std::isgreaterequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::isgreaterequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::isgreaterequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isgreaterequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::isgreaterequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::isgreaterequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(std::isgreaterequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(!std::isgreaterequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isgreaterequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(std::isgreaterequal(2.0, 1));     // double vs int
+    assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+  }
+
+  return 0;
+}

>From 8a651d6936cf81f5ca167d67b0a28280ceec4116 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:04:03 +0200
Subject: [PATCH 13/25] initial: add isless.pass.cpp

---
 .../test/std/numerics/c.math/isless.pass.cpp  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/isless.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
new file mode 100644
index 0000000000000..1b9c1aacd0d13
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::isless(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+
+    assert(!std::isless(T(0), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isless(std::numeric_limits<T>::max(), T(0)));
+    assert(std::isless(T(0), std::numeric_limits<T>::max()));
+    assert(!std::isless(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::isless(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::isless(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::isless(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));   // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 67d88113e5987460d20b2a0001e6aab7e53a57d5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 17 Jul 2026 23:57:29 +0200
Subject: [PATCH 14/25] add: islessequal.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/islessequal.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
new file mode 100644
index 0000000000000..98fb3a9e7770a
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+    assert(std::islessequal(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
+    assert(std::islessequal(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+
+    assert(!std::islessequal(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessequal(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessequal(std::numeric_limits<T>::signaling_NaN(), T(0)));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::islessequal(std::numeric_limits<T>::max(), T(0)));
+    assert(std::islessequal(T(0), std::numeric_limits<T>::max()));
+    assert(std::islessequal(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    assert(std::islessequal(T(1), T(1)));
+
+    if (std::is_signed<T>::value) {
+      assert(!std::islessequal(T(-1), std::numeric_limits<T>::lowest()));
+      assert(std::islessequal(std::numeric_limits<T>::lowest(), T(-1)));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
+  {
+    assert(!std::islessequal(2.0, 1));     // double vs int
+    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+  }
+
+  return 0;
+}

>From 6d1e2ff8878af3188e953f68bb199406ff942354 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 00:00:33 +0200
Subject: [PATCH 15/25] fix: clang-format

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 2 +-
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index 1b9c1aacd0d13..af999db6166b3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -80,7 +80,7 @@ int main(int, char**) {
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));   // int vs float
+    assert(std::isless(1, 2.0f));     // int vs float
     assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 98fb3a9e7770a..2023b062f55f9 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -84,7 +84,7 @@ int main(int, char**) {
   // Make sure we can call `std::islessequal` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
     assert(!std::islessequal(2.0, 1));     // double vs int
-    assert(std::islessequal(1, 2.0f));   // int vs float
+    assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
   }
 

>From 71b5f4cf216bba7e770e854906b5112d93cbe367 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 01:57:12 +0200
Subject: [PATCH 16/25] add: islessgreater.pass.cpp and isunordered.pass.cpp

---
 .../std/numerics/c.math/islessequal.pass.cpp  |  2 +-
 .../numerics/c.math/islessgreater.pass.cpp    | 91 +++++++++++++++++++
 .../std/numerics/c.math/isunordered.pass.cpp  | 78 ++++++++++++++++
 3 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
 create mode 100644 libcxx/test/std/numerics/c.math/isunordered.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2023b062f55f9..2b0be4ff37035 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool islessequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
new file mode 100644
index 0000000000000..40fbb7f4a4e41
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::islessgreater(T(1), T(2)));
+    assert(std::islessgreater(T(2), T(1)));
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(std::islessgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
+
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(!std::islessgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(!std::islessgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    if (!std::is_same<T, bool>::value) {
+      assert(std::islessgreater(T(1), T(2)));
+      assert(std::islessgreater(T(2), T(1)));
+    }
+
+    assert(!std::islessgreater(T(1), T(1)));
+
+    assert(!std::islessgreater(T(1), T(1)));
+    assert(std::islessgreater(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::islessgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    if (std::is_signed<T>::value) {
+      assert(std::islessgreater(T(-1), T(1)));
+      assert(std::islessgreater(std::numeric_limits<T>::lowest(), T(0)));
+      assert(std::islessgreater(T(0), std::numeric_limits<T>::lowest()));
+      assert(!std::islessgreater(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()));
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
new file mode 100644
index 0000000000000..ed7740d195163
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
+
+// We don't control the implementation on windows
+// UNSUPPORTED: windows
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), T(0)));
+    assert(std::isunordered(T(0), std::numeric_limits<T>::quiet_NaN()));
+    assert(std::isunordered(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
+
+    assert(std::isunordered(std::numeric_limits<T>::signaling_NaN(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(!std::isunordered(T(1), T(2)));
+    assert(!std::isunordered(T(1), T(1)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), T(0)));
+    assert(!std::isunordered(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+template <typename T>
+struct ConvertibleTo {
+  operator T() const { return T(1); }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From ef949295028c1bd5d6a19881e122393aada59194 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:01:27 +0200
Subject: [PATCH 17/25] flip assert tests for gcc

---
 .../c.math/constexpr-cxx23-gcc.pass.cpp       | 30 +++++++++----------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index 34513b459a056..8d814e81f8bc6 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -225,25 +225,25 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0, 0.0) == 0);
   ASSERT_CONSTEXPR_CXX23(std::isgreater(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isgreaterequal(-1.0L, 0.0L) == 0);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::isless(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessequal(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0f, 0.0f) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0, 0.0) == 1);
+  ASSERT_CONSTEXPR_CXX23(std::islessgreater(-1.0L, 0.0L) == 1);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0f, 0.0f) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0, 0.0) == 0);
+  ASSERT_CONSTEXPR_CXX23(std::isunordered(-1.0L, 0.0L) == 0);
 
   assert(!ImplementedP0533R9 && R"(
 Congratulations! You just have implemented P0533R9 (https://wg21.link/p0533r9).

>From f6a4fa78d49d5bd14219d1cb7885d0d6bf4524ba Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 02:02:43 +0200
Subject: [PATCH 18/25] fix typo in isgreaterequal.pass.cpp

---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index d6ae553e961e5..f2a4159882654 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// bool isgreaterequalequal(floating-point-type x, floating-point-type y); // constexpr since C++23
+// bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
 // We don't control the implementation on windows
 // UNSUPPORTED: windows

>From 62ae94cc86a2b43b61a9ba80eadfc4013bcced15 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Sat, 18 Jul 2026 18:18:09 +0200
Subject: [PATCH 19/25] update: cmath synopsis for is<...>

---
 libcxx/include/cmath | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..65bf07aed059b 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -140,17 +140,17 @@ bool signbit(arithmetic x);
 
 int fpclassify(arithmetic x);
 
-bool isfinite(arithmetic x);
-bool isinf(arithmetic x);
-bool isnan(arithmetic x);
-bool isnormal(arithmetic x);
-
-bool isgreater(arithmetic x, arithmetic y);
-bool isgreaterequal(arithmetic x, arithmetic y);
-bool isless(arithmetic x, arithmetic y);
-bool islessequal(arithmetic x, arithmetic y);
-bool islessgreater(arithmetic x, arithmetic y);
-bool isunordered(arithmetic x, arithmetic y);
+bool isfinite(arithmetic x); // constexpr since C++23
+bool isinf(arithmetic x);    // constexpr since C++23
+bool isnan(arithmetic x);    // constexpr since C++23
+bool isnormal(arithmetic x); // constexpr since C++23
+
+bool isgreater(arithmetic x, arithmetic y);      // constexpr since C++23
+bool isgreaterequal(arithmetic x, arithmetic y); // constexpr since C++23
+bool isless(arithmetic x, arithmetic y);         // constexpr since C++23
+bool islessequal(arithmetic x, arithmetic y);    // constexpr since C++23
+bool islessgreater(arithmetic x, arithmetic y);  // constexpr since C++23
+bool isunordered(arithmetic x, arithmetic y);    // constexpr since C++23
 
 floating_point acosh (arithmetic x);
 float          acoshf(float x);

>From 1a641131cba0843189ecd9364df414c8baa85cad Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Mon, 20 Jul 2026 12:31:47 +0200
Subject: [PATCH 20/25] remove: UNSUPPORTED windows from tests

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp      | 3 ---
 libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp | 3 ---
 libcxx/test/std/numerics/c.math/isless.pass.cpp         | 3 ---
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp    | 3 ---
 libcxx/test/std/numerics/c.math/islessgreater.pass.cpp  | 3 ---
 libcxx/test/std/numerics/c.math/isunordered.pass.cpp    | 3 ---
 6 files changed, 18 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index c7853974d7a66..269bb31e016cf 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index f2a4159882654..27fa43812812a 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isgreaterequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index af999db6166b3..cc68a49beead9 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isless(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 2b0be4ff37035..5f58bbc993613 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool islessequal(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
index 40fbb7f4a4e41..6a10708359bbe 100644
--- a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool islessgreater(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
index ed7740d195163..3f140106a423f 100644
--- a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -8,9 +8,6 @@
 
 // bool isunordered(floating-point-type x, floating-point-type y); // constexpr since C++23
 
-// We don't control the implementation on windows
-// UNSUPPORTED: windows
-
 #include <cassert>
 #include <cmath>
 #include <limits>

>From 76b4a8a8ec33acf9f4c132072ad70422d6f09bd5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 22 Jul 2026 12:24:03 +0200
Subject: [PATCH 21/25] fix: convert assert to static_assert

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 269bb31e016cf..6932a26c75f8d 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -82,10 +82,10 @@ int main(int, char**) {
 
   // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
-    assert(std::isgreater(2.0, 1));     // double vs int
-    assert(!std::isgreater(1, 2.0f));   // int vs float
-    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
-    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+    static_assert(std::isgreater(2.0, 1));     // double vs int
+    static_assert(!std::isgreater(1, 2.0f));   // int vs float
+    static_assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    static_assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
   }
 
   return 0;

>From 36145d1a1740c144a180397eaa889873f752911c Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 22 Jul 2026 12:44:24 +0200
Subject: [PATCH 22/25] Fix: static_assertions on tests and remove of
 ConvertibleTo

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp   | 12 +++++++-----
 .../test/std/numerics/c.math/isgreaterequal.pass.cpp | 12 +++++++-----
 libcxx/test/std/numerics/c.math/isless.pass.cpp      | 11 +++--------
 libcxx/test/std/numerics/c.math/islessequal.pass.cpp | 11 ++++++-----
 .../test/std/numerics/c.math/islessgreater.pass.cpp  |  5 -----
 libcxx/test/std/numerics/c.math/isunordered.pass.cpp |  5 -----
 6 files changed, 23 insertions(+), 33 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 6932a26c75f8d..781a9af90b153 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -71,21 +71,23 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(1); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
   // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
+    assert(std::isgreater(2.0, 1));     // double vs int
+    assert(!std::isgreater(1, 2.0f));   // int vs float
+    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+
+#if TEST_STD_VER >= 23
     static_assert(std::isgreater(2.0, 1));     // double vs int
     static_assert(!std::isgreater(1, 2.0f));   // int vs float
     static_assert(std::isgreater(2.0L, 1.0f)); // long double vs float
     static_assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+#endif
   }
 
   return 0;
diff --git a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
index 27fa43812812a..bc8b23d01cc7a 100644
--- a/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreaterequal.pass.cpp
@@ -69,11 +69,6 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(1); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
@@ -84,6 +79,13 @@ int main(int, char**) {
     assert(!std::isgreaterequal(1, 2.0f));   // int vs float
     assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
     assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+
+#if TEST_STD_VER >= 23
+    static_assert(std::isgreaterequal(2.0, 1));     // double vs int
+    static_assert(!std::isgreaterequal(1, 2.0f));   // int vs float
+    static_assert(std::isgreaterequal(2.0L, 1.0f)); // long double vs float
+    static_assert(!std::isgreaterequal(std::numeric_limits<double>::quiet_NaN(), 0));
+#endif
   }
 
   return 0;
diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index cc68a49beead9..bf341f90f3b38 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -65,20 +65,15 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(1); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
-    assert(!std::isless(2.0, 1));     // double vs int
-    assert(std::isless(1, 2.0f));     // int vs float
-    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+    static_assert(!std::isless(2.0, 1));     // double vs int
+    static_assert(std::isless(1, 2.0f));     // int vs float
+    static_assert(!std::isless(2.0L, 1.0f)); // long double vs float
   }
 
   return 0;
diff --git a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
index 5f58bbc993613..55a163f769148 100644
--- a/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessequal.pass.cpp
@@ -69,11 +69,6 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(1); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
@@ -83,6 +78,12 @@ int main(int, char**) {
     assert(!std::islessequal(2.0, 1));     // double vs int
     assert(std::islessequal(1, 2.0f));     // int vs float
     assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+
+#if TEST_STD_VER >= 23
+    static_assert(!std::islessequal(2.0, 1));     // double vs int
+    static_assert(std::islessequal(1, 2.0f));     // int vs float
+    static_assert(!std::islessequal(2.0L, 1.0f)); // long double vs float
+#endif
   }
 
   return 0;
diff --git a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
index 6a10708359bbe..2d92f0f60b5b8 100644
--- a/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/islessgreater.pass.cpp
@@ -75,11 +75,6 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(1); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
diff --git a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
index 3f140106a423f..1f3d43f4c3e7c 100644
--- a/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isunordered.pass.cpp
@@ -62,11 +62,6 @@ struct TestInt {
   }
 };
 
-template <typename T>
-struct ConvertibleTo {
-  operator T() const { return T(1); }
-};
-
 int main(int, char**) {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());

>From c1880a334e9391a2e6c0c52bbc98ce9bf77079b3 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 22 Jul 2026 17:12:18 +0200
Subject: [PATCH 23/25] fix: isless.pass.cpp

---
 libcxx/test/std/numerics/c.math/isless.pass.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libcxx/test/std/numerics/c.math/isless.pass.cpp b/libcxx/test/std/numerics/c.math/isless.pass.cpp
index bf341f90f3b38..3b5feb3f7bba3 100644
--- a/libcxx/test/std/numerics/c.math/isless.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isless.pass.cpp
@@ -71,9 +71,15 @@ int main(int, char**) {
 
   // Make sure we can call `std::isless` with mixed-type promotions with __promote_t<_A1, _A2>.
   {
+    assert(!std::isless(2.0, 1));     // double vs int
+    assert(std::isless(1, 2.0f));     // int vs float
+    assert(!std::isless(2.0L, 1.0f)); // long double vs float
+
+#if TEST_STD_VER >= 23
     static_assert(!std::isless(2.0, 1));     // double vs int
     static_assert(std::isless(1, 2.0f));     // int vs float
     static_assert(!std::isless(2.0L, 1.0f)); // long double vs float
+#endif
   }
 
   return 0;

>From bd9baa5755b01d5fd91750fcb6a3e2fe391563a0 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 22 Jul 2026 18:28:01 +0200
Subject: [PATCH 24/25] fix: isgreater.pass.cpp

---
 .../std/numerics/c.math/isgreater.pass.cpp    | 97 +++++++++----------
 1 file changed, 45 insertions(+), 52 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index 781a9af90b153..cc77a31861292 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -18,77 +18,70 @@
 
 struct TestFloat {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
-    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
-    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
-    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
-
-    assert(std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::max()));
-    assert(!std::isgreater(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest()));
-    assert(!std::isgreater(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()));
-
-    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), T(0)));
-    assert(!std::isgreater(T(0), std::numeric_limits<T>::quiet_NaN()));
-    assert(!std::isgreater(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()));
-    assert(!std::isgreater(std::numeric_limits<T>::signaling_NaN(), T(0)));
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                    = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max   = lim::max();
+    TEST_CONSTEXPR_CXX23 T low   = lim::lowest();
+    TEST_CONSTEXPR_CXX23 T inf   = lim::infinity();
+    TEST_CONSTEXPR_CXX23 T nan   = lim::quiet_NaN();
+    TEST_CONSTEXPR_CXX23 T s_nan = lim::signaling_NaN();
+
+    assert(std::isgreater(max, T(0)));
+    assert(!std::isgreater(T(0), max));
+    assert(!std::isgreater(max, max));
+
+    assert(std::isgreater(inf, max));
+    assert(!std::isgreater(-inf, low));
+    assert(!std::isgreater(inf, inf));
+
+    assert(!std::isgreater(nan, T(0)));
+    assert(!std::isgreater(T(0), nan));
+    assert(!std::isgreater(nan, nan));
+    assert(!std::isgreater(s_nan, T(0)));
   }
 };
 
 struct TestInt {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
-    assert(std::isgreater(std::numeric_limits<T>::max(), T(0)));
-    assert(!std::isgreater(T(0), std::numeric_limits<T>::max()));
-    assert(!std::isgreater(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()));
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                  = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max = lim::max();
+    TEST_CONSTEXPR_CXX23 T low = lim::lowest();
+
+    assert(std::isgreater(max, T(0)));
+    assert(!std::isgreater(T(0), max));
+    assert(!std::isgreater(max, max));
 
     assert(!std::isgreater(T(1), T(1)));
-    assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(0)));
+    assert(!std::isgreater(low, T(0)));
 
     if (std::is_signed<T>::value) {
-      assert(std::isgreater(T(-1), std::numeric_limits<T>::lowest()));
-      assert(!std::isgreater(std::numeric_limits<T>::lowest(), T(-1)));
+      assert(std::isgreater(T(-1), low));
+      assert(!std::isgreater(low, T(-1)));
     }
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX23 bool test() {
+  using lim                    = std::numeric_limits<int>;
+  TEST_CONSTEXPR_CXX23 int nan = lim::quiet_NaN();
+
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
   // Make sure we can call `std::isgreater` with mixed-type promotions with __promote_t<_A1, _A2>.
-  {
-    assert(std::isgreater(2.0, 1));     // double vs int
-    assert(!std::isgreater(1, 2.0f));   // int vs float
-    assert(std::isgreater(2.0L, 1.0f)); // long double vs float
-    assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  assert(std::isgreater(2.0, 1));     // double vs int
+  assert(!std::isgreater(1, 2.0f));   // int vs float
+  assert(std::isgreater(2.0L, 1.0f)); // long double vs float
+  assert(!std::isgreater(nan, 0));    // NaN vs int
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
 #if TEST_STD_VER >= 23
-    static_assert(std::isgreater(2.0, 1));     // double vs int
-    static_assert(!std::isgreater(1, 2.0f));   // int vs float
-    static_assert(std::isgreater(2.0L, 1.0f)); // long double vs float
-    static_assert(!std::isgreater(std::numeric_limits<double>::quiet_NaN(), 0));
+  static_assert(test());
 #endif
-  }
-
   return 0;
 }

>From dd9053a0cb40e9516d3e14835e12d9ebb0551793 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Wed, 22 Jul 2026 18:31:10 +0200
Subject: [PATCH 25/25] fix: std::is_signed<T>::value to
 std::numeric_limits<T>::is_signed

---
 libcxx/test/std/numerics/c.math/isgreater.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
index cc77a31861292..303dd9f4870aa 100644
--- a/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/isgreater.pass.cpp
@@ -55,7 +55,7 @@ struct TestInt {
     assert(!std::isgreater(T(1), T(1)));
     assert(!std::isgreater(low, T(0)));
 
-    if (std::is_signed<T>::value) {
+    if (lim::is_signed) {
       assert(std::isgreater(T(-1), low));
       assert(!std::isgreater(low, T(-1)));
     }


From libcxx-commits at lists.llvm.org  Thu Jul 23 05:33:01 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 23 Jul 2026 05:33:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a6209fd.7270d2fd.1ec500.a439@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210993

>From 8e4ce1588756045195a9c05c3688ee0919433b32 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 15:13:46 +0200
Subject: [PATCH 1/5] add: constexpr to fpclassify

---
 libcxx/include/cmath                                      | 2 +-
 libcxx/include/math.h                                     | 8 ++++----
 .../libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..992854b0490a3 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -138,7 +138,7 @@ long double    tanhl(long double x);
 
 bool signbit(arithmetic x);
 
-int fpclassify(arithmetic x);
+int fpclassify(arithmetic x); // constexpr since C++23
 
 bool isfinite(arithmetic x);
 bool isinf(arithmetic x);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..86ad9a1ae9b10 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -389,22 +389,22 @@ namespace __math {
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..926ff37cddbfc 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -203,9 +203,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From b6ecf4cfc60def795b8f4814af4e9df2feda5121 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 16:12:31 +0200
Subject: [PATCH 2/5] update: gcc tests

---
 .../libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..455c03ef58d18 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -197,9 +197,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From f191ac6676bcf419b3eb0aa92ddcb1291744a796 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 18:45:51 +0200
Subject: [PATCH 3/5] add: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 79 +++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/fpclassify.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
new file mode 100644
index 0000000000000..fda9f28a1389f
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool fpclassify(floating-point-type x); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
+    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+
+    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+
+    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(-0.0)) == FP_ZERO);
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+
+    if (std::is_signed<T>::value) {
+      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 912975ef33f9fd185b3accd72d7016d7d7be6ea5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:22:26 +0200
Subject: [PATCH 4/5] add: _LIBCPP_PREFERRED_OVERLOAD

---
 libcxx/include/math.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 86ad9a1ae9b10..fa8d293604c7f 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -387,6 +387,30 @@ namespace __math {
 
 // fpclassify
 
+#      ifdef _LIBCPP_PREFERRED_OVERLOAD
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int fpclassify(float __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(long double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(_A1 __x) _NOEXCEPT {
+  return __x == 0 ? FP_ZERO : FP_NORMAL;
+}
+
+#      else
+
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
@@ -408,6 +432,8 @@ template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
+#      endif
+
 } // namespace __math
 
 _LIBCPP_END_NAMESPACE_STD

>From 1b02edd8888f7fe60ed3053e6e8112902b5dac3b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:32:06 +0200
Subject: [PATCH 5/5] refactor: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 65 ++++++++++---------
 1 file changed, 33 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
index fda9f28a1389f..fcfd73d9bacf2 100644
--- a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -18,62 +18,63 @@
 
 struct TestFloat {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
-    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
-    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                    = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max   = lim::max();
+    TEST_CONSTEXPR_CXX23 T min   = lim::min();
+    TEST_CONSTEXPR_CXX23 T d_min = lim::denorm_min();
+    TEST_CONSTEXPR_CXX23 T inf   = lim::infinity();
+    TEST_CONSTEXPR_CXX23 T nan   = lim::quiet_NaN();
+    TEST_CONSTEXPR_CXX23 T s_nan = lim::signaling_NaN();
 
-    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
-    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(nan) == FP_NAN);
+    assert(std::fpclassify(s_nan) == FP_NAN);
+
+    assert(std::fpclassify(inf) == FP_INFINITE);
+    assert(std::fpclassify(-inf) == FP_INFINITE);
 
     assert(std::fpclassify(T(1)) == FP_NORMAL);
     assert(std::fpclassify(T(-1)) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+    assert(std::fpclassify(max) == FP_NORMAL);
+    assert(std::fpclassify(min) == FP_NORMAL);
 
-    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+    assert(std::fpclassify(d_min) == FP_SUBNORMAL);
 
     assert(std::fpclassify(T(0)) == FP_ZERO);
     assert(std::fpclassify(T(-0.0)) == FP_ZERO);
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
 struct TestInt {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                  = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max = lim::max();
+    TEST_CONSTEXPR_CXX23 T low = lim::lowest();
+
     assert(std::fpclassify(T(0)) == FP_ZERO);
     assert(std::fpclassify(T(1)) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(max) == FP_NORMAL);
 
     if (std::is_signed<T>::value) {
-      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(low) == FP_NORMAL);
       assert(std::fpclassify(T(-1)) == FP_NORMAL);
     }
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX23 bool test() {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 23
+  static_assert(test());
+#endif
+
   return 0;
 }


From libcxx-commits at lists.llvm.org  Thu Jul 23 05:48:37 2026
From: libcxx-commits at lists.llvm.org (Dan Shan via libcxx-commits)
Date: Thu, 23 Jul 2026 05:48:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement LWG4072: std::optional
 comparisons: constrain harder (PR #209968)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209968@github.com>
Message-ID: <6a620da5.d76d81af.3b68ca.0e28@mx.google.com>


HumanThe2nd wrote:

Ping

https://github.com/llvm/llvm-project/pull/209968

From libcxx-commits at lists.llvm.org  Thu Jul 23 06:14:34 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 06:14:34 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Defer removal of
 _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR to LLVM 25 (PR #211553)
Message-ID: <llvm.org/llvm/llvm-project/pull/211553@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/211553

Rather than removing the escape hatch in LLVM 24, keep it around for another release since it hasn't been widely deployed yet. Reword the release note as an announcement of the future removal in LLVM 25 and update the TODO marker accordingly.

>From f6f8ebb07c545341b137b77e610116d9d4951f9a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 23 Jul 2026 08:20:14 -0400
Subject: [PATCH] [libc++] Defer removal of
 _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR to LLVM 25

Rather than removing the escape hatch in LLVM 24, keep it around for another
release since it hasn't been widely deployed yet. Reword the release note as
an announcement of the future removal in LLVM 25 and update the TODO marker
accordingly.
---
 libcxx/docs/ReleaseNotes/24.rst     | 6 +++---
 libcxx/include/__memory/allocator.h | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..89ba314a302f1 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -60,13 +60,13 @@ Potentially breaking changes
 Announcements About Future Releases
 -----------------------------------
 
+- ``std::allocator`` is trivially default constructible since LLVM 22. In LLVM 23 and LLVM 24, the ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR``
+  macro was provided as an escape hatch to revert that change. This escape hatch will be removed in LLVM 25 if there is no
+  evidence of it being useful.
 
 ABI Affecting Changes
 ---------------------
 
-- TODO: ``std::allocator`` is trivially default constructible since LLVM 22. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR``
-  macro was provided as an escape hatch, but it has been removed in LLVM 24 since there was no evidence of it being useful.
-
 - TODO: ``bitset::operator[]`` returns ``bool`` since LLVM 22, fixing a conformance bug. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``
   macro was provided as an escape hatch, but it has been removed in LLVM 24 since there was no evidence of it being useful.
 
diff --git a/libcxx/include/__memory/allocator.h b/libcxx/include/__memory/allocator.h
index 609b305a1250f..dd8c8647c178a 100644
--- a/libcxx/include/__memory/allocator.h
+++ b/libcxx/include/__memory/allocator.h
@@ -60,7 +60,7 @@ struct __non_trivially_default_constructible_if<true, _Unique> {
 
 template <class _Tp>
 class allocator
-// TODO(LLVM 24): Remove the opt-out
+// TODO(LLVM 25): Remove the opt-out
 #ifdef _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR
     : __non_trivially_default_constructible_if<!is_void<_Tp>::value, allocator<_Tp> >
 #endif


From libcxx-commits at lists.llvm.org  Thu Jul 23 06:14:55 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 06:14:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove the
 _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR opt-out (PR #211329)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211329@github.com>
Message-ID: <6a6213cf.00a9ba89.b3671.6aa7@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/211329

From libcxx-commits at lists.llvm.org  Thu Jul 23 06:14:56 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 06:14:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove the
 _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR opt-out (PR #211329)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211329@github.com>
Message-ID: <6a6213d0.0c0d0188.27d608.3840@mx.google.com>


ldionne wrote:

Superseded by https://github.com/llvm/llvm-project/pull/211553

https://github.com/llvm/llvm-project/pull/211329

From libcxx-commits at lists.llvm.org  Thu Jul 23 06:16:33 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 23 Jul 2026 06:16:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Defer removal of
 _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR to LLVM 25 (PR #211553)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211553@github.com>
Message-ID: <6a621431.93108353.2d68c.2653@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

Rather than removing the escape hatch in LLVM 24, keep it around for another release since it hasn't been widely deployed yet. Reword the release note as an announcement of the future removal in LLVM 25 and update the TODO marker accordingly.

---
Full diff: https://github.com/llvm/llvm-project/pull/211553.diff


2 Files Affected:

- (modified) libcxx/docs/ReleaseNotes/24.rst (+3-3) 
- (modified) libcxx/include/__memory/allocator.h (+1-1) 


``````````diff
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..89ba314a302f1 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -60,13 +60,13 @@ Potentially breaking changes
 Announcements About Future Releases
 -----------------------------------
 
+- ``std::allocator`` is trivially default constructible since LLVM 22. In LLVM 23 and LLVM 24, the ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR``
+  macro was provided as an escape hatch to revert that change. This escape hatch will be removed in LLVM 25 if there is no
+  evidence of it being useful.
 
 ABI Affecting Changes
 ---------------------
 
-- TODO: ``std::allocator`` is trivially default constructible since LLVM 22. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR``
-  macro was provided as an escape hatch, but it has been removed in LLVM 24 since there was no evidence of it being useful.
-
 - TODO: ``bitset::operator[]`` returns ``bool`` since LLVM 22, fixing a conformance bug. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``
   macro was provided as an escape hatch, but it has been removed in LLVM 24 since there was no evidence of it being useful.
 
diff --git a/libcxx/include/__memory/allocator.h b/libcxx/include/__memory/allocator.h
index 609b305a1250f..dd8c8647c178a 100644
--- a/libcxx/include/__memory/allocator.h
+++ b/libcxx/include/__memory/allocator.h
@@ -60,7 +60,7 @@ struct __non_trivially_default_constructible_if<true, _Unique> {
 
 template <class _Tp>
 class allocator
-// TODO(LLVM 24): Remove the opt-out
+// TODO(LLVM 25): Remove the opt-out
 #ifdef _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR
     : __non_trivially_default_constructible_if<!is_void<_Tp>::value, allocator<_Tp> >
 #endif

``````````

</details>


https://github.com/llvm/llvm-project/pull/211553

From libcxx-commits at lists.llvm.org  Thu Jul 23 06:18:56 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 06:18:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Defer removal of
 _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR to LLVM 25 (PR #211553)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211553@github.com>
Message-ID: <6a6214c0.5c68b3fc.12265b.92b1@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/211553

>From 6b080dcc6bec2e3507f8491ebfe7b77031bd1380 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 23 Jul 2026 08:20:14 -0400
Subject: [PATCH 1/2] [libc++] Defer removal of deprecated ABI escape hatches
 to LLVM 25

Rather than removing the _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR and
_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF escape hatches in
LLVM 24, keep them around for another release since they haven't been widely
deployed yet. Reword the release notes as announcements of the future removal
in LLVM 25 and update the TODO markers accordingly.
---
 libcxx/docs/ReleaseNotes/24.rst     | 13 +++++++------
 libcxx/include/__memory/allocator.h |  2 +-
 libcxx/include/bitset               |  2 +-
 3 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..acd5d6dc50fe7 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -60,16 +60,17 @@ Potentially breaking changes
 Announcements About Future Releases
 -----------------------------------
 
+- ``std::allocator`` is trivially default constructible since LLVM 22. In LLVM 23 and LLVM 24, the ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR``
+  macro was provided as an escape hatch to revert that change. This escape hatch will be removed in LLVM 25 if there is no
+  evidence of it being useful.
+
+- ``bitset::operator[]`` returns ``bool`` since LLVM 22, fixing a conformance bug. In LLVM 23 and LLVM 24, the ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``
+  macro was provided as an escape hatch to revert that change. This escape hatch will be removed in LLVM 25 if there is no
+  evidence of it being useful.
 
 ABI Affecting Changes
 ---------------------
 
-- TODO: ``std::allocator`` is trivially default constructible since LLVM 22. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR``
-  macro was provided as an escape hatch, but it has been removed in LLVM 24 since there was no evidence of it being useful.
-
-- TODO: ``bitset::operator[]`` returns ``bool`` since LLVM 22, fixing a conformance bug. In LLVM 23, the ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``
-  macro was provided as an escape hatch, but it has been removed in LLVM 24 since there was no evidence of it being useful.
-
 Build System Changes
 --------------------
 
diff --git a/libcxx/include/__memory/allocator.h b/libcxx/include/__memory/allocator.h
index 609b305a1250f..dd8c8647c178a 100644
--- a/libcxx/include/__memory/allocator.h
+++ b/libcxx/include/__memory/allocator.h
@@ -60,7 +60,7 @@ struct __non_trivially_default_constructible_if<true, _Unique> {
 
 template <class _Tp>
 class allocator
-// TODO(LLVM 24): Remove the opt-out
+// TODO(LLVM 25): Remove the opt-out
 #ifdef _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR
     : __non_trivially_default_constructible_if<!is_void<_Tp>::value, allocator<_Tp> >
 #endif
diff --git a/libcxx/include/bitset b/libcxx/include/bitset
index fd638daea8c73..3897a792d9a69 100644
--- a/libcxx/include/bitset
+++ b/libcxx/include/bitset
@@ -680,7 +680,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bitset& flip(size_t __pos);
 
   // element access:
-  // TODO(LLVM 24): Remove the opt-out
+  // TODO(LLVM 25): Remove the opt-out
 #  ifndef _LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool operator[](size_t __p) const {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__p < _Size, "bitset::operator[] index out of bounds");

>From f4a4a7388800a88b0b424203a78540b4b1f0ff66 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 23 Jul 2026 09:18:43 -0400
Subject: [PATCH 2/2] Also defer bitset operator[]

---
 libcxx/docs/ReleaseNotes/24.rst | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index acd5d6dc50fe7..1c9e158e93214 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -60,13 +60,13 @@ Potentially breaking changes
 Announcements About Future Releases
 -----------------------------------
 
-- ``std::allocator`` is trivially default constructible since LLVM 22. In LLVM 23 and LLVM 24, the ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR``
-  macro was provided as an escape hatch to revert that change. This escape hatch will be removed in LLVM 25 if there is no
-  evidence of it being useful.
+- ``std::allocator`` is trivially default constructible since LLVM 22. In LLVM 23 and LLVM 24, the
+  ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR`` macro was provided as an escape hatch to revert that change.
+  This escape hatch will be removed in LLVM 25 if there is no evidence of it being useful.
 
-- ``bitset::operator[]`` returns ``bool`` since LLVM 22, fixing a conformance bug. In LLVM 23 and LLVM 24, the ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``
-  macro was provided as an escape hatch to revert that change. This escape hatch will be removed in LLVM 25 if there is no
-  evidence of it being useful.
+- ``bitset::operator[]`` returns ``bool`` since LLVM 22, fixing a conformance bug. In LLVM 23 and LLVM 24, the
+  ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF`` macro was provided as an escape hatch to revert
+  that change. This escape hatch will be removed in LLVM 25 if there is no evidence of it being useful.
 
 ABI Affecting Changes
 ---------------------


From libcxx-commits at lists.llvm.org  Thu Jul 23 06:20:05 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 06:20:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Defer removal of two escape
	hatches to LLVM 25 (PR #211553)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211553@github.com>
Message-ID: <6a621505.720fb68d.888e6.860b@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/211553

From libcxx-commits at lists.llvm.org  Thu Jul 23 06:20:42 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 06:20:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Defer removal of two escape
	hatches to LLVM 25 (PR #211553)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211553@github.com>
Message-ID: <6a62152a.96731870.14ea0a.042a@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/211553

From libcxx-commits at lists.llvm.org  Thu Jul 23 06:20:51 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 06:20:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Defer removal of two escape
	hatches to LLVM 25 (PR #211553)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211553@github.com>
Message-ID: <6a621533.5fe1956f.39fb84.c24b@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/211553

From libcxx-commits at lists.llvm.org  Thu Jul 23 06:21:06 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 06:21:06 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove the
 _LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF opt-out (PR
 #211313)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211313@github.com>
Message-ID: <6a621542.e2619d42.26e4b1.fa63@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/211313

From libcxx-commits at lists.llvm.org  Thu Jul 23 06:21:07 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 06:21:07 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove the
 _LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF opt-out (PR
 #211313)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211313@github.com>
Message-ID: <6a621543.f4bffc5b.3e0f7.28db@mx.google.com>


ldionne wrote:

Superseded by https://github.com/llvm/llvm-project/pull/211553.

https://github.com/llvm/llvm-project/pull/211313

From libcxx-commits at lists.llvm.org  Thu Jul 23 06:21:49 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 23 Jul 2026 06:21:49 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 882a138 - [libc++] Remove workaround for
 Clang < 20 in clang-tidy plugin (#211314)
Message-ID: <6a62156d.79dd0093.f92c.30c4@mx.google.com>


Author: Louis Dionne
Date: 2026-07-23T09:21:44-04:00
New Revision: 882a1381942c3bb4208136743ca7d743947a00a2

URL: https://github.com/llvm/llvm-project/commit/882a1381942c3bb4208136743ca7d743947a00a2
DIFF: https://github.com/llvm/llvm-project/commit/882a1381942c3bb4208136743ca7d743947a00a2.diff

LOG: [libc++] Remove workaround for Clang < 20 in clang-tidy plugin (#211314)

The clang-tidy plugin is now always built with Clang >= 20, so the
workaround can be removed.

Added: 
    

Modified: 
    libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
index 777dd6fe11df5..24089e33ca1c0 100644
--- a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp
@@ -25,14 +25,6 @@
 // This is part of libc++'s policy
 // https://libcxx.llvm.org/CodingGuidelines.html#don-t-use-argument-dependent-lookup-unless-required-by-the-standard
 
-// TODO(LLVM-21) Remove dependentScopeDeclRefExpr
-// dependentScopeDeclRefExpr requires Clang 20, this uses the same definition as Clang
-#if defined(__clang_major__) && __clang_major__ < 20
-namespace clang::ast_matchers {
-const internal::VariadicDynCastAllOfMatcher<Stmt, DependentScopeDeclRefExpr> dependentScopeDeclRefExpr;
-} // namespace clang::ast_matchers
-#endif
-
 namespace libcpp {
 robust_against_operator_ampersand::robust_against_operator_ampersand(
     llvm::StringRef name, clang::tidy::ClangTidyContext* context)


        

From libcxx-commits at lists.llvm.org  Thu Jul 23 06:21:51 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 06:21:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Remove workaround for Clang < 20
 in clang-tidy plugin (PR #211314)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211314@github.com>
Message-ID: <6a62156f.dd74445a.205641.69f6@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/211314

From libcxx-commits at lists.llvm.org  Thu Jul 23 06:38:26 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 23 Jul 2026 06:38:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] be86221 - [libc++] Implement any_of in
	terms of find_if (#207274)
Message-ID: <6a621952.028a0eea.346e07.a4d7@mx.google.com>


Author: Louis Dionne
Date: 2026-07-23T09:38:21-04:00
New Revision: be86221af36d62be2e25a8b6e89ae8199d9dce0a

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

LOG: [libc++] Implement any_of in terms of find_if (#207274)

This way, any optimizations in find_if will be picked up by any_of.

Closes #129310

Added: 
    

Modified: 
    libcxx/include/__algorithm/any_of.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__algorithm/any_of.h b/libcxx/include/__algorithm/any_of.h
index 4b6eb94517286..119979d749077 100644
--- a/libcxx/include/__algorithm/any_of.h
+++ b/libcxx/include/__algorithm/any_of.h
@@ -10,33 +10,35 @@
 #ifndef _LIBCPP___ALGORITHM_ANY_OF_H
 #define _LIBCPP___ALGORITHM_ANY_OF_H
 
+#include <__algorithm/find_if.h>
 #include <__config>
 #include <__functional/identity.h>
-#include <__type_traits/invoke.h>
+#include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Iter, class _Sent, class _Proj, class _Pred>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
 __any_of(_Iter __first, _Sent __last, _Pred& __pred, _Proj& __proj) {
-  for (; __first != __last; ++__first) {
-    if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
-      return true;
-  }
-  return false;
+  return std::__find_if(std::move(__first), __last, __pred, __proj) != __last;
 }
 
 template <class _InputIterator, class _Predicate>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
 any_of(_InputIterator __first, _InputIterator __last, _Predicate __pred) {
   __identity __proj;
-  return std::__any_of(__first, __last, __pred, __proj);
+  return std::__any_of(std::move(__first), std::move(__last), __pred, __proj);
 }
 
 _LIBCPP_END_NAMESPACE_STD
 
+_LIBCPP_POP_MACROS
+
 #endif // _LIBCPP___ALGORITHM_ANY_OF_H


        

From libcxx-commits at lists.llvm.org  Thu Jul 23 06:38:32 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 06:38:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement any_of in terms of
	find_if (PR #207274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207274@github.com>
Message-ID: <6a621958.6d3ea74f.17f70b.c084@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/207274

From libcxx-commits at lists.llvm.org  Thu Jul 23 07:03:42 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Thu, 23 Jul 2026 07:03:42 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++] Pin down the compiler in
 the various benchmark scripts and jobs (PR #211563)
Message-ID: <llvm.org/llvm/llvm-project/pull/211563@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/211563

We have various scripts that build and test the library at pinpointed commits: benchmark-historical, run-benchmarks, build-at-commit and test-at-commit. They were handling the compiler in different ways: some scripts would just run the libc++ build (or test suite configuration) without specifying the compiler, which means the $CXX environment variable was used if present. Other scripts (e.g. run-benchmarks) would accept the compiler as an argument, but would fail to pass it down when configuring the test suite, which led to issues.

This patch passes the compiler explicitly in all scripts: this removes any potential confusion around how the compiler should be specified (env var or argument). The only exception is build-at-commit, where the compiler is still specified by passing the appropriate CMake arguments. The reason for this exception is that passing arguments to CMake is actually the way we want to configure aspects of the build (and the test suite) in the long term, it's just that the test suite doesn't support this cleanly due to the CMake/Lit split at the moment.

In the longer term, `test-at-commit` should also lose its `--compiler` argument in favour of being able to pass CMake parameters to the test suite configuration, but we are not there yet.

>From ec040e5440e49ccbbd244f197352bf979d8118c1 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 23 Jul 2026 09:48:34 -0400
Subject: [PATCH] [libc++] Pin down the compiler in the various benchmark
 scripts and jobs

We have various scripts that build and test the library at pinpointed
commits: benchmark-historical, run-benchmarks, build-at-commit and
test-at-commit. They were handling the compiler in different ways:
some scripts would just run the libc++ build (or test suite configuration)
without specifying the compiler, which means the $CXX environment variable
was used if present. Other scripts (e.g. run-benchmarks) would accept
the compiler as an argument, but would fail to pass it down when
configuring the test suite, which led to issues.

This patch passes the compiler explicitly in all scripts: this removes
any potential confusion around how the compiler should be specified
(env var or argument). The only exception is build-at-commit, where
the compiler is still specified by passing the appropriate CMake
arguments. The reason for this exception is that passing arguments
to CMake is actually the way we want to configure aspects of the build
(and the test suite) in the long term, it's just that the test suite
doesn't support this cleanly due to the CMake/Lit split at the moment.

In the longer term, `test-at-commit` should also lose its `--compiler`
argument in favour of being able to pass CMake parameters to the test
suite configuration, but we are not there yet.
---
 .github/workflows/libcxx-benchmark-commit.yml |  6 ++--
 .github/workflows/libcxx-pr-benchmark.yml     | 34 +++++++------------
 libcxx/utils/benchmark-historical             |  6 +++-
 libcxx/utils/ci/lnt/run-benchmarks            |  1 +
 libcxx/utils/test-at-commit                   |  3 ++
 5 files changed, 26 insertions(+), 24 deletions(-)

diff --git a/.github/workflows/libcxx-benchmark-commit.yml b/.github/workflows/libcxx-benchmark-commit.yml
index 4d72f1d126432..f2096cc968b26 100644
--- a/.github/workflows/libcxx-benchmark-commit.yml
+++ b/.github/workflows/libcxx-benchmark-commit.yml
@@ -52,6 +52,8 @@ jobs:
             install-macos-dependencies: false
       fail-fast: false
     runs-on: ${{ matrix.runner }}
+    env:
+      COMPILER: ${{ matrix.cxx }}
     steps:
       - name: Checkout the LLVM monorepo
         uses: actions/checkout at df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
@@ -76,7 +78,7 @@ jobs:
         run: |
           cmake --version
           ninja --version
-          ${{ matrix.cxx }} --version
+          "${COMPILER}" --version
 
       - name: Run the benchmarks
         env:
@@ -91,7 +93,7 @@ jobs:
           libcxx/utils/ci/lnt/run-benchmarks                            \
             --test-suite-commit "${BENCHMARK_SUITE_VERSION}"            \
             --machine ${{ matrix.lnt-machine }}                         \
-            --compiler ${{ matrix.cxx }}                                \
+            --compiler "${COMPILER}"                                    \
             --benchmark-commit "${COMMIT}"                              \
             "${filter_arg[@]}"                                          \
             --output "${COMMIT}.json"
diff --git a/.github/workflows/libcxx-pr-benchmark.yml b/.github/workflows/libcxx-pr-benchmark.yml
index 1603962b6883f..7e7cf12ec9921 100644
--- a/.github/workflows/libcxx-pr-benchmark.yml
+++ b/.github/workflows/libcxx-pr-benchmark.yml
@@ -91,13 +91,11 @@ jobs:
         include:
           - platform: macOS 26.5 arm64
             runner: ["self-hosted", "macOS", "ARM64", "26", "26.5"]
-            cc: clang
             cxx: clang++
             install-python: false
             install-macos-dependencies: true
           - platform: Linux x86_64
             runner: llvm-premerge-libcxx-runners
-            cc: clang-22
             cxx: clang++-22
             install-python: true
             install-macos-dependencies: false
@@ -108,8 +106,11 @@ jobs:
     needs:
       - extract-info
     env:
-      CC: ${{ matrix.cc }}
-      CXX: ${{ matrix.cxx }}
+      BENCHMARKS: ${{ needs.extract-info.outputs.benchmarks }}
+      COMPILER: ${{ matrix.cxx }}
+      PLATFORM: ${{ matrix.platform }}
+      PR_HEAD: ${{ needs.extract-info.outputs.pr_head }}
+      PR_BASE: ${{ needs.extract-info.outputs.pr_base }}
     steps:
       - uses: actions/checkout at df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
         with:
@@ -136,34 +137,29 @@ jobs:
           python -m pip install -r libcxx/utils/requirements.txt
 
       - name: Build the baseline and the candidate
-        env:
-          PR_HEAD: ${{ needs.extract-info.outputs.pr_head }}
-          PR_BASE: ${{ needs.extract-info.outputs.pr_base }}
         run: |
           source .venv/bin/activate
           baseline_commit=$(git merge-base $PR_BASE $PR_HEAD)
-          ./libcxx/utils/build-at-commit --commit ${baseline_commit} --install-dir install/baseline -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
-          ./libcxx/utils/build-at-commit --commit $PR_HEAD --install-dir install/candidate -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
+          ./libcxx/utils/build-at-commit --commit ${baseline_commit} --install-dir install/baseline -- -DCMAKE_CXX_COMPILER="${COMPILER}" -DCMAKE_BUILD_TYPE=RelWithDebInfo
+          ./libcxx/utils/build-at-commit --commit $PR_HEAD --install-dir install/candidate -- -DCMAKE_CXX_COMPILER="${COMPILER}" -DCMAKE_BUILD_TYPE=RelWithDebInfo
 
       - name: Run baseline and candidate interleaved
-        env:
-          BENCHMARKS: ${{ needs.extract-info.outputs.benchmarks }}
         run: |
           source .venv/bin/activate
           # Run 3 times so we can pick the median, and interleave to mitigate the impact of environmental noise
-          ./libcxx/utils/test-at-commit --libcxx-installation install/baseline -B benchmarks/baseline -- -sv -j1 --param optimization=speed "$BENCHMARKS"
+          ./libcxx/utils/test-at-commit --libcxx-installation install/baseline -B benchmarks/baseline --compiler "${COMPILER}" -- -sv -j1 --param optimization=speed "$BENCHMARKS"
           ./libcxx/utils/consolidate-benchmarks benchmarks/baseline | tee baseline.lnt
-          ./libcxx/utils/test-at-commit --libcxx-installation install/candidate -B benchmarks/candidate -- -sv -j1 --param optimization=speed "$BENCHMARKS"
+          ./libcxx/utils/test-at-commit --libcxx-installation install/candidate -B benchmarks/candidate --compiler "${COMPILER}" -- -sv -j1 --param optimization=speed "$BENCHMARKS"
           ./libcxx/utils/consolidate-benchmarks benchmarks/candidate | tee candidate.lnt
 
-          ./libcxx/utils/test-at-commit --libcxx-installation install/baseline -B benchmarks/baseline -- -sv -j1 --param optimization=speed "$BENCHMARKS"
+          ./libcxx/utils/test-at-commit --libcxx-installation install/baseline -B benchmarks/baseline --compiler "${COMPILER}" -- -sv -j1 --param optimization=speed "$BENCHMARKS"
           ./libcxx/utils/consolidate-benchmarks benchmarks/baseline | tee -a baseline.lnt
-          ./libcxx/utils/test-at-commit --libcxx-installation install/candidate -B benchmarks/candidate -- -sv -j1 --param optimization=speed "$BENCHMARKS"
+          ./libcxx/utils/test-at-commit --libcxx-installation install/candidate -B benchmarks/candidate --compiler "${COMPILER}" -- -sv -j1 --param optimization=speed "$BENCHMARKS"
           ./libcxx/utils/consolidate-benchmarks benchmarks/candidate | tee -a candidate.lnt
 
-          ./libcxx/utils/test-at-commit --libcxx-installation install/baseline -B benchmarks/baseline -- -sv -j1 --param optimization=speed "$BENCHMARKS"
+          ./libcxx/utils/test-at-commit --libcxx-installation install/baseline -B benchmarks/baseline --compiler "${COMPILER}" -- -sv -j1 --param optimization=speed "$BENCHMARKS"
           ./libcxx/utils/consolidate-benchmarks benchmarks/baseline | tee -a baseline.lnt
-          ./libcxx/utils/test-at-commit --libcxx-installation install/candidate -B benchmarks/candidate -- -sv -j1 --param optimization=speed "$BENCHMARKS"
+          ./libcxx/utils/test-at-commit --libcxx-installation install/candidate -B benchmarks/candidate --compiler "${COMPILER}" -- -sv -j1 --param optimization=speed "$BENCHMARKS"
           ./libcxx/utils/consolidate-benchmarks benchmarks/candidate | tee -a candidate.lnt
 
       - name: Compare baseline and candidate runs
@@ -173,8 +169,6 @@ jobs:
 
       - name: Update comment with results
         uses: actions/github-script at 3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
-        env:
-          PLATFORM: ${{ matrix.platform }}
         with:
           script: |
             const fs = require('fs');
@@ -206,8 +200,6 @@ jobs:
       - name: Report failure in the comment
         if: failure()
         uses: actions/github-script at 3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
-        env:
-          PLATFORM: ${{ matrix.platform }}
         with:
           script: |
             const { data: comment } = await github.rest.issues.getComment({
diff --git a/libcxx/utils/benchmark-historical b/libcxx/utils/benchmark-historical
index 8a315be522d5c..48946e4bee075 100755
--- a/libcxx/utils/benchmark-historical
+++ b/libcxx/utils/benchmark-historical
@@ -40,6 +40,8 @@ def main(argv):
                     're-run on a potentially-overlapping set of commits, such as after pulling new commits with Git.')
     parser.add_argument('--output', '-o', type=pathlib.Path, required=True,
         help='Path to the directory where the resulting .lnt files are stored.')
+    parser.add_argument('--compiler', type=str, required=True,
+        help='Path to the compiler to use to build libc++ and run the tests.')
     parser.add_argument('--commit-list', type=argparse.FileType('r'), default=sys.stdin,
         help='Path to a file containing a whitespace separated list of commits to test. '
              'By default, this is read from standard input.')
@@ -83,10 +85,12 @@ def main(argv):
                 build_cmd = [PARENT_DIR / 'build-at-commit', '--git-repo', args.git_repo,
                                                              '--commit', commit,
                                                              '--install-dir', libcxx_install_dir,
-                                                             '--', '-DCMAKE_BUILD_TYPE=RelWithDebInfo']
+                                                             '--', '-DCMAKE_BUILD_TYPE=RelWithDebInfo',
+                                                                   f'-DCMAKE_CXX_COMPILER={args.compiler}']
 
                 test_cmd = [PARENT_DIR / 'test-at-commit', '--git-repo', args.git_repo,
                                                            '--libcxx-installation', libcxx_install_dir,
+                                                           '--compiler', args.compiler,
                                                            '--build-dir', build_dir]
                 test_cmd += ['--'] + lit_options
 
diff --git a/libcxx/utils/ci/lnt/run-benchmarks b/libcxx/utils/ci/lnt/run-benchmarks
index ce4705c0fafa1..2fd53008cae72 100755
--- a/libcxx/utils/ci/lnt/run-benchmarks
+++ b/libcxx/utils/ci/lnt/run-benchmarks
@@ -167,6 +167,7 @@ def main(argv):
                             '--build-dir', artifacts / 'benchmarks',
                             '--test-suite-commit', args.test_suite_commit,
                             '--libcxx-installation', artifacts / 'libcxx-install',
+                            '--compiler', args.compiler,
                             '--',
                             '-j1', '--time-tests', '--test-output=failed',
                             '--param', f'compiler={args.compiler}',
diff --git a/libcxx/utils/test-at-commit b/libcxx/utils/test-at-commit
index 4249d9c1ce86e..b02c571ca9b1b 100755
--- a/libcxx/utils/test-at-commit
+++ b/libcxx/utils/test-at-commit
@@ -85,6 +85,8 @@ def main(argv):
              'are located in that directory after the run.')
     parser.add_argument('--libcxx-installation', type=pathlib.Path, required=True,
         help='Path to the directory where a copy of libc++ to run tests on is installed.')
+    parser.add_argument('--compiler', type=pathlib.Path, required=True,
+        help='Path to the compiler to use to run the tests.')
     parser.add_argument('--test-suite-commit', type=str, required=False,
         help='Commit to use for the test suite. If left unspecified, the currently checked-out version of the '
              'test suite is used. Otherwise, the requested version is checked out in a separate directory and '
@@ -125,6 +127,7 @@ def main(argv):
             f.write(LIT_CONFIG_FILE.format(INSTALL_ROOT=args.libcxx_installation))
 
         test_suite_cmd = ['cmake', '-B', args.build_dir, '-S', test_suite_sources / 'runtimes', '-G', 'Ninja']
+        test_suite_cmd += ['-D', f'CMAKE_CXX_COMPILER={args.compiler}']
         test_suite_cmd += ['-D', 'LLVM_ENABLE_RUNTIMES=libcxx;libcxxabi']
         test_suite_cmd += ['-D', 'LIBCXXABI_USE_LLVM_UNWINDER=OFF']
         test_suite_cmd += ['-D', f'LIBCXX_TEST_CONFIG={lit_cfg}']


From libcxx-commits at lists.llvm.org  Thu Jul 23 07:13:59 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 23 Jul 2026 07:13:59 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++] Pin down the compiler in
 the various benchmark scripts and jobs (PR #211563)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211563@github.com>
Message-ID: <6a6221a7.88a20ebf.ac2c5.9bee@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-github-workflow

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

We have various scripts that build and test the library at pinpointed commits: benchmark-historical, run-benchmarks, build-at-commit and test-at-commit. They were handling the compiler in different ways: some scripts would just run the libc++ build (or test suite configuration) without specifying the compiler, which means the $CXX environment variable was used if present. Other scripts (e.g. run-benchmarks) would accept the compiler as an argument, but would fail to pass it down when configuring the test suite, which led to issues.

This patch passes the compiler explicitly in all scripts: this removes any potential confusion around how the compiler should be specified (env var or argument). The only exception is build-at-commit, where the compiler is still specified by passing the appropriate CMake arguments. The reason for this exception is that passing arguments to CMake is actually the way we want to configure aspects of the build (and the test suite) in the long term, it's just that the test suite doesn't support this cleanly due to the CMake/Lit split at the moment.

In the longer term, `test-at-commit` should also lose its `--compiler` argument in favour of being able to pass CMake parameters to the test suite configuration, but we are not there yet.

---
Full diff: https://github.com/llvm/llvm-project/pull/211563.diff


5 Files Affected:

- (modified) .github/workflows/libcxx-benchmark-commit.yml (+4-2) 
- (modified) .github/workflows/libcxx-pr-benchmark.yml (+13-21) 
- (modified) libcxx/utils/benchmark-historical (+5-1) 
- (modified) libcxx/utils/ci/lnt/run-benchmarks (+1) 
- (modified) libcxx/utils/test-at-commit (+3) 


``````````diff
diff --git a/.github/workflows/libcxx-benchmark-commit.yml b/.github/workflows/libcxx-benchmark-commit.yml
index 4d72f1d126432..f2096cc968b26 100644
--- a/.github/workflows/libcxx-benchmark-commit.yml
+++ b/.github/workflows/libcxx-benchmark-commit.yml
@@ -52,6 +52,8 @@ jobs:
             install-macos-dependencies: false
       fail-fast: false
     runs-on: ${{ matrix.runner }}
+    env:
+      COMPILER: ${{ matrix.cxx }}
     steps:
       - name: Checkout the LLVM monorepo
         uses: actions/checkout at df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
@@ -76,7 +78,7 @@ jobs:
         run: |
           cmake --version
           ninja --version
-          ${{ matrix.cxx }} --version
+          "${COMPILER}" --version
 
       - name: Run the benchmarks
         env:
@@ -91,7 +93,7 @@ jobs:
           libcxx/utils/ci/lnt/run-benchmarks                            \
             --test-suite-commit "${BENCHMARK_SUITE_VERSION}"            \
             --machine ${{ matrix.lnt-machine }}                         \
-            --compiler ${{ matrix.cxx }}                                \
+            --compiler "${COMPILER}"                                    \
             --benchmark-commit "${COMMIT}"                              \
             "${filter_arg[@]}"                                          \
             --output "${COMMIT}.json"
diff --git a/.github/workflows/libcxx-pr-benchmark.yml b/.github/workflows/libcxx-pr-benchmark.yml
index 1603962b6883f..7e7cf12ec9921 100644
--- a/.github/workflows/libcxx-pr-benchmark.yml
+++ b/.github/workflows/libcxx-pr-benchmark.yml
@@ -91,13 +91,11 @@ jobs:
         include:
           - platform: macOS 26.5 arm64
             runner: ["self-hosted", "macOS", "ARM64", "26", "26.5"]
-            cc: clang
             cxx: clang++
             install-python: false
             install-macos-dependencies: true
           - platform: Linux x86_64
             runner: llvm-premerge-libcxx-runners
-            cc: clang-22
             cxx: clang++-22
             install-python: true
             install-macos-dependencies: false
@@ -108,8 +106,11 @@ jobs:
     needs:
       - extract-info
     env:
-      CC: ${{ matrix.cc }}
-      CXX: ${{ matrix.cxx }}
+      BENCHMARKS: ${{ needs.extract-info.outputs.benchmarks }}
+      COMPILER: ${{ matrix.cxx }}
+      PLATFORM: ${{ matrix.platform }}
+      PR_HEAD: ${{ needs.extract-info.outputs.pr_head }}
+      PR_BASE: ${{ needs.extract-info.outputs.pr_base }}
     steps:
       - uses: actions/checkout at df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
         with:
@@ -136,34 +137,29 @@ jobs:
           python -m pip install -r libcxx/utils/requirements.txt
 
       - name: Build the baseline and the candidate
-        env:
-          PR_HEAD: ${{ needs.extract-info.outputs.pr_head }}
-          PR_BASE: ${{ needs.extract-info.outputs.pr_base }}
         run: |
           source .venv/bin/activate
           baseline_commit=$(git merge-base $PR_BASE $PR_HEAD)
-          ./libcxx/utils/build-at-commit --commit ${baseline_commit} --install-dir install/baseline -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
-          ./libcxx/utils/build-at-commit --commit $PR_HEAD --install-dir install/candidate -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
+          ./libcxx/utils/build-at-commit --commit ${baseline_commit} --install-dir install/baseline -- -DCMAKE_CXX_COMPILER="${COMPILER}" -DCMAKE_BUILD_TYPE=RelWithDebInfo
+          ./libcxx/utils/build-at-commit --commit $PR_HEAD --install-dir install/candidate -- -DCMAKE_CXX_COMPILER="${COMPILER}" -DCMAKE_BUILD_TYPE=RelWithDebInfo
 
       - name: Run baseline and candidate interleaved
-        env:
-          BENCHMARKS: ${{ needs.extract-info.outputs.benchmarks }}
         run: |
           source .venv/bin/activate
           # Run 3 times so we can pick the median, and interleave to mitigate the impact of environmental noise
-          ./libcxx/utils/test-at-commit --libcxx-installation install/baseline -B benchmarks/baseline -- -sv -j1 --param optimization=speed "$BENCHMARKS"
+          ./libcxx/utils/test-at-commit --libcxx-installation install/baseline -B benchmarks/baseline --compiler "${COMPILER}" -- -sv -j1 --param optimization=speed "$BENCHMARKS"
           ./libcxx/utils/consolidate-benchmarks benchmarks/baseline | tee baseline.lnt
-          ./libcxx/utils/test-at-commit --libcxx-installation install/candidate -B benchmarks/candidate -- -sv -j1 --param optimization=speed "$BENCHMARKS"
+          ./libcxx/utils/test-at-commit --libcxx-installation install/candidate -B benchmarks/candidate --compiler "${COMPILER}" -- -sv -j1 --param optimization=speed "$BENCHMARKS"
           ./libcxx/utils/consolidate-benchmarks benchmarks/candidate | tee candidate.lnt
 
-          ./libcxx/utils/test-at-commit --libcxx-installation install/baseline -B benchmarks/baseline -- -sv -j1 --param optimization=speed "$BENCHMARKS"
+          ./libcxx/utils/test-at-commit --libcxx-installation install/baseline -B benchmarks/baseline --compiler "${COMPILER}" -- -sv -j1 --param optimization=speed "$BENCHMARKS"
           ./libcxx/utils/consolidate-benchmarks benchmarks/baseline | tee -a baseline.lnt
-          ./libcxx/utils/test-at-commit --libcxx-installation install/candidate -B benchmarks/candidate -- -sv -j1 --param optimization=speed "$BENCHMARKS"
+          ./libcxx/utils/test-at-commit --libcxx-installation install/candidate -B benchmarks/candidate --compiler "${COMPILER}" -- -sv -j1 --param optimization=speed "$BENCHMARKS"
           ./libcxx/utils/consolidate-benchmarks benchmarks/candidate | tee -a candidate.lnt
 
-          ./libcxx/utils/test-at-commit --libcxx-installation install/baseline -B benchmarks/baseline -- -sv -j1 --param optimization=speed "$BENCHMARKS"
+          ./libcxx/utils/test-at-commit --libcxx-installation install/baseline -B benchmarks/baseline --compiler "${COMPILER}" -- -sv -j1 --param optimization=speed "$BENCHMARKS"
           ./libcxx/utils/consolidate-benchmarks benchmarks/baseline | tee -a baseline.lnt
-          ./libcxx/utils/test-at-commit --libcxx-installation install/candidate -B benchmarks/candidate -- -sv -j1 --param optimization=speed "$BENCHMARKS"
+          ./libcxx/utils/test-at-commit --libcxx-installation install/candidate -B benchmarks/candidate --compiler "${COMPILER}" -- -sv -j1 --param optimization=speed "$BENCHMARKS"
           ./libcxx/utils/consolidate-benchmarks benchmarks/candidate | tee -a candidate.lnt
 
       - name: Compare baseline and candidate runs
@@ -173,8 +169,6 @@ jobs:
 
       - name: Update comment with results
         uses: actions/github-script at 3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
-        env:
-          PLATFORM: ${{ matrix.platform }}
         with:
           script: |
             const fs = require('fs');
@@ -206,8 +200,6 @@ jobs:
       - name: Report failure in the comment
         if: failure()
         uses: actions/github-script at 3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
-        env:
-          PLATFORM: ${{ matrix.platform }}
         with:
           script: |
             const { data: comment } = await github.rest.issues.getComment({
diff --git a/libcxx/utils/benchmark-historical b/libcxx/utils/benchmark-historical
index 8a315be522d5c..48946e4bee075 100755
--- a/libcxx/utils/benchmark-historical
+++ b/libcxx/utils/benchmark-historical
@@ -40,6 +40,8 @@ def main(argv):
                     're-run on a potentially-overlapping set of commits, such as after pulling new commits with Git.')
     parser.add_argument('--output', '-o', type=pathlib.Path, required=True,
         help='Path to the directory where the resulting .lnt files are stored.')
+    parser.add_argument('--compiler', type=str, required=True,
+        help='Path to the compiler to use to build libc++ and run the tests.')
     parser.add_argument('--commit-list', type=argparse.FileType('r'), default=sys.stdin,
         help='Path to a file containing a whitespace separated list of commits to test. '
              'By default, this is read from standard input.')
@@ -83,10 +85,12 @@ def main(argv):
                 build_cmd = [PARENT_DIR / 'build-at-commit', '--git-repo', args.git_repo,
                                                              '--commit', commit,
                                                              '--install-dir', libcxx_install_dir,
-                                                             '--', '-DCMAKE_BUILD_TYPE=RelWithDebInfo']
+                                                             '--', '-DCMAKE_BUILD_TYPE=RelWithDebInfo',
+                                                                   f'-DCMAKE_CXX_COMPILER={args.compiler}']
 
                 test_cmd = [PARENT_DIR / 'test-at-commit', '--git-repo', args.git_repo,
                                                            '--libcxx-installation', libcxx_install_dir,
+                                                           '--compiler', args.compiler,
                                                            '--build-dir', build_dir]
                 test_cmd += ['--'] + lit_options
 
diff --git a/libcxx/utils/ci/lnt/run-benchmarks b/libcxx/utils/ci/lnt/run-benchmarks
index ce4705c0fafa1..2fd53008cae72 100755
--- a/libcxx/utils/ci/lnt/run-benchmarks
+++ b/libcxx/utils/ci/lnt/run-benchmarks
@@ -167,6 +167,7 @@ def main(argv):
                             '--build-dir', artifacts / 'benchmarks',
                             '--test-suite-commit', args.test_suite_commit,
                             '--libcxx-installation', artifacts / 'libcxx-install',
+                            '--compiler', args.compiler,
                             '--',
                             '-j1', '--time-tests', '--test-output=failed',
                             '--param', f'compiler={args.compiler}',
diff --git a/libcxx/utils/test-at-commit b/libcxx/utils/test-at-commit
index 4249d9c1ce86e..b02c571ca9b1b 100755
--- a/libcxx/utils/test-at-commit
+++ b/libcxx/utils/test-at-commit
@@ -85,6 +85,8 @@ def main(argv):
              'are located in that directory after the run.')
     parser.add_argument('--libcxx-installation', type=pathlib.Path, required=True,
         help='Path to the directory where a copy of libc++ to run tests on is installed.')
+    parser.add_argument('--compiler', type=pathlib.Path, required=True,
+        help='Path to the compiler to use to run the tests.')
     parser.add_argument('--test-suite-commit', type=str, required=False,
         help='Commit to use for the test suite. If left unspecified, the currently checked-out version of the '
              'test suite is used. Otherwise, the requested version is checked out in a separate directory and '
@@ -125,6 +127,7 @@ def main(argv):
             f.write(LIT_CONFIG_FILE.format(INSTALL_ROOT=args.libcxx_installation))
 
         test_suite_cmd = ['cmake', '-B', args.build_dir, '-S', test_suite_sources / 'runtimes', '-G', 'Ninja']
+        test_suite_cmd += ['-D', f'CMAKE_CXX_COMPILER={args.compiler}']
         test_suite_cmd += ['-D', 'LLVM_ENABLE_RUNTIMES=libcxx;libcxxabi']
         test_suite_cmd += ['-D', 'LIBCXXABI_USE_LLVM_UNWINDER=OFF']
         test_suite_cmd += ['-D', f'LIBCXX_TEST_CONFIG={lit_cfg}']

``````````

</details>


https://github.com/llvm/llvm-project/pull/211563

From libcxx-commits at lists.llvm.org  Thu Jul 23 07:38:02 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 23 Jul 2026 07:38:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
Message-ID: <llvm.org/llvm/llvm-project/pull/211568@github.com>


https://github.com/lknknm created https://github.com/llvm/llvm-project/pull/211568

Closes https://github.com/llvm/llvm-project/issues/148224

The current implementation for the `ranges::distance(I&& first, S last)` overload already sidesteps the `volatile first` (see [godbolt](https://godbolt.org/z/78G64n6qs) example on clang-trunk) with `sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>`. This implementation provides a stricter constraint with the exact same wording from the Standard with the condition `if constexpr (!is_array_v<remove_reference_v<_It>>)`.

This commit also organises the test suite to better represent both `lwg3664` and `lwg4242` tests in `libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp`

References:
- [LWG4242](https://cplusplus.github.io/LWG/issue4242)
- https://eel.is/c++draft/range.iter.op.distance

Follows-up: https://github.com/llvm/llvm-project/pull/210550

>From 8e3559129e89e81f7b8bf0d195222363898ef654 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 15:38:21 +0200
Subject: [PATCH 1/2] resolve: LWG4242

---
 libcxx/docs/Status/Cxx26Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 5b046b69d14bd..83ad46827ca18 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -142,7 +142,7 @@
 "`LWG4235 <https://wg21.link/LWG4235>`__","``cache_latest_view`` and ``to_input_view`` miss ``reserve_hint``","2025-06 (Sofia)","","","`#148219 <https://github.com/llvm/llvm-project/issues/148219>`__",""
 "`LWG4236 <https://wg21.link/LWG4236>`__","``chunk_view::outer-iterator::value_type`` should provide ``reserve_hint``","2025-06 (Sofia)","","","`#148220 <https://github.com/llvm/llvm-project/issues/148220>`__",""
 "`LWG4239 <https://wg21.link/LWG4239>`__","``flat_map``'s transparent comparator no longer works for string literals","2025-06 (Sofia)","","","`#148223 <https://github.com/llvm/llvm-project/issues/148223>`__",""
-"`LWG4242 <https://wg21.link/LWG4242>`__","``ranges::distance`` does not work with volatile iterators","2025-06 (Sofia)","","","`#148224 <https://github.com/llvm/llvm-project/issues/148224>`__",""
+"`LWG4242 <https://wg21.link/LWG4242>`__","``ranges::distance`` does not work with volatile iterators","2025-06 (Sofia)","|Complete|","24","`#148224 <https://github.com/llvm/llvm-project/issues/148224>`__",""
 "`LWG4245 <https://wg21.link/LWG4245>`__","Operators that interact with ``counted_iterator`` and ``default_sentinel_t`` should be ``noexcept``","2025-06 (Sofia)","","","`#148226 <https://github.com/llvm/llvm-project/issues/148226>`__",""
 "`LWG4247 <https://wg21.link/LWG4247>`__","Header ``<stdbit.h>`` is not yet freestanding","2025-06 (Sofia)","","","`#148227 <https://github.com/llvm/llvm-project/issues/148227>`__",""
 "","","","","","",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..029117e2ff5cc 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -87,7 +87,7 @@ struct __distance {
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
-    if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
+    if constexpr (!is_array_v<remove_reference_t<_Ip>>) {
       return __last - __first;
     } else {
       return __last - decay_t<_Ip>(__first);

>From 654370a5f26aed08caebe4fbc98f09417e272517 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 15:56:59 +0200
Subject: [PATCH 2/2] add: lwg4242 test case and refactor lwg3664 test case

---
 .../iterator_sentinel.pass.cpp                | 63 ++++++++++++-------
 1 file changed, 40 insertions(+), 23 deletions(-)

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index 30507c59495ea..37ee37a0e5876 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -199,6 +199,41 @@ static_assert(!std::sized_sentinel_for<EvilSentinel<int*>, const int*>);
 static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, int*>);
 static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, const int*>);
 
+// LWG3664: "LWG3392 broke std::ranges::distance(a, a+3)" tests.
+constexpr void test_lwg3664() {
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, a + 3) == 3);
+    assert(std::ranges::distance(a, a) == 0);
+    assert(std::ranges::distance(a + 3, a) == -3);
+  }
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
+  }
+  {
+    const int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  }
+}
+
+// LWG4242: "ranges::distance does not work with volatile iterators" tests.
+void test_lwg4242() {
+  int arr[]         = {1, 2, 3};
+  int* volatile ptr = arr;
+  auto dist         = std::distance(ptr, arr + 3);
+  auto r_dist       = std::ranges::distance(ptr, arr + 3);
+  assert(dist == r_dist);
+}
+
 constexpr bool test() {
   {
     int a[] = {1, 2, 3};
@@ -284,30 +319,12 @@ constexpr bool test() {
     auto view = c | std::views::join;
     assert(std::ranges::distance(view.begin(), view.end()) == 30);
   }
-  if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
-    test_deque();
 
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, a + 3) == 3);
-    assert(std::ranges::distance(a, a) == 0);
-    assert(std::ranges::distance(a + 3, a) == -3);
-  }
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
-    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
-  }
-  {
-    const int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
-    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  test_lwg3664();
+
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    test_deque(); // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
+    test_lwg4242();
   }
 
   return true;


From libcxx-commits at lists.llvm.org  Thu Jul 23 07:43:14 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 23 Jul 2026 07:43:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a622882.d5bba4f5.68b18.7cee@mx.google.com>


https://github.com/lknknm ready_for_review https://github.com/llvm/llvm-project/pull/211568

From libcxx-commits at lists.llvm.org  Thu Jul 23 07:43:23 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 23 Jul 2026 07:43:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a62288b.ecf2568a.2a6521.8244@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/211568

>From 8e3559129e89e81f7b8bf0d195222363898ef654 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 15:38:21 +0200
Subject: [PATCH 1/2] resolve: LWG4242

---
 libcxx/docs/Status/Cxx26Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 5b046b69d14bd..83ad46827ca18 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -142,7 +142,7 @@
 "`LWG4235 <https://wg21.link/LWG4235>`__","``cache_latest_view`` and ``to_input_view`` miss ``reserve_hint``","2025-06 (Sofia)","","","`#148219 <https://github.com/llvm/llvm-project/issues/148219>`__",""
 "`LWG4236 <https://wg21.link/LWG4236>`__","``chunk_view::outer-iterator::value_type`` should provide ``reserve_hint``","2025-06 (Sofia)","","","`#148220 <https://github.com/llvm/llvm-project/issues/148220>`__",""
 "`LWG4239 <https://wg21.link/LWG4239>`__","``flat_map``'s transparent comparator no longer works for string literals","2025-06 (Sofia)","","","`#148223 <https://github.com/llvm/llvm-project/issues/148223>`__",""
-"`LWG4242 <https://wg21.link/LWG4242>`__","``ranges::distance`` does not work with volatile iterators","2025-06 (Sofia)","","","`#148224 <https://github.com/llvm/llvm-project/issues/148224>`__",""
+"`LWG4242 <https://wg21.link/LWG4242>`__","``ranges::distance`` does not work with volatile iterators","2025-06 (Sofia)","|Complete|","24","`#148224 <https://github.com/llvm/llvm-project/issues/148224>`__",""
 "`LWG4245 <https://wg21.link/LWG4245>`__","Operators that interact with ``counted_iterator`` and ``default_sentinel_t`` should be ``noexcept``","2025-06 (Sofia)","","","`#148226 <https://github.com/llvm/llvm-project/issues/148226>`__",""
 "`LWG4247 <https://wg21.link/LWG4247>`__","Header ``<stdbit.h>`` is not yet freestanding","2025-06 (Sofia)","","","`#148227 <https://github.com/llvm/llvm-project/issues/148227>`__",""
 "","","","","","",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..029117e2ff5cc 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -87,7 +87,7 @@ struct __distance {
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
-    if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
+    if constexpr (!is_array_v<remove_reference_t<_Ip>>) {
       return __last - __first;
     } else {
       return __last - decay_t<_Ip>(__first);

>From 654370a5f26aed08caebe4fbc98f09417e272517 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 15:56:59 +0200
Subject: [PATCH 2/2] add: lwg4242 test case and refactor lwg3664 test case

---
 .../iterator_sentinel.pass.cpp                | 63 ++++++++++++-------
 1 file changed, 40 insertions(+), 23 deletions(-)

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index 30507c59495ea..37ee37a0e5876 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -199,6 +199,41 @@ static_assert(!std::sized_sentinel_for<EvilSentinel<int*>, const int*>);
 static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, int*>);
 static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, const int*>);
 
+// LWG3664: "LWG3392 broke std::ranges::distance(a, a+3)" tests.
+constexpr void test_lwg3664() {
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, a + 3) == 3);
+    assert(std::ranges::distance(a, a) == 0);
+    assert(std::ranges::distance(a + 3, a) == -3);
+  }
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
+  }
+  {
+    const int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  }
+}
+
+// LWG4242: "ranges::distance does not work with volatile iterators" tests.
+void test_lwg4242() {
+  int arr[]         = {1, 2, 3};
+  int* volatile ptr = arr;
+  auto dist         = std::distance(ptr, arr + 3);
+  auto r_dist       = std::ranges::distance(ptr, arr + 3);
+  assert(dist == r_dist);
+}
+
 constexpr bool test() {
   {
     int a[] = {1, 2, 3};
@@ -284,30 +319,12 @@ constexpr bool test() {
     auto view = c | std::views::join;
     assert(std::ranges::distance(view.begin(), view.end()) == 30);
   }
-  if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
-    test_deque();
 
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, a + 3) == 3);
-    assert(std::ranges::distance(a, a) == 0);
-    assert(std::ranges::distance(a + 3, a) == -3);
-  }
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
-    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
-  }
-  {
-    const int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
-    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  test_lwg3664();
+
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    test_deque(); // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
+    test_lwg4242();
   }
 
   return true;


From libcxx-commits at lists.llvm.org  Thu Jul 23 07:48:16 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 23 Jul 2026 07:48:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Always disable buffering of the
 stream underlying std::filebuf (PR #209872)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209872@github.com>
Message-ID: <6a6229b0.49b5c284.1efbb7.9df4@mx.google.com>


philnik777 wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/streams/fstream.bench.cpp

https://github.com/llvm/llvm-project/pull/209872

From libcxx-commits at lists.llvm.org  Thu Jul 23 08:01:54 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 23 Jul 2026 08:01:54 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a622ce2.824e11a6.322560.5f03@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Lucas Mellone (lknknm)

<details>
<summary>Changes</summary>

Closes https://github.com/llvm/llvm-project/issues/148224

The current implementation for the `ranges::distance(I&amp;&amp; first, S last)` overload already sidesteps the `volatile first` (see [godbolt](https://godbolt.org/z/78G64n6qs) example on clang-trunk) with `sized_sentinel_for&lt;_Sp, __remove_cvref_t&lt;_Ip&gt;&gt;`. This implementation provides a stricter constraint with the exact same wording from the Standard with the condition `if constexpr (!is_array_v&lt;remove_reference_v&lt;_It&gt;&gt;)`.

This commit also organises the test suite to better represent both `lwg3664` and `lwg4242` tests in `libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp`

References:
- [LWG4242](https://cplusplus.github.io/LWG/issue4242)
- https://eel.is/c++draft/range.iter.op.distance

Follows-up: https://github.com/llvm/llvm-project/pull/210550

---
Full diff: https://github.com/llvm/llvm-project/pull/211568.diff


3 Files Affected:

- (modified) libcxx/docs/Status/Cxx26Issues.csv (+1-1) 
- (modified) libcxx/include/__iterator/distance.h (+1-1) 
- (modified) libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp (+40-23) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 5b046b69d14bd..83ad46827ca18 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -142,7 +142,7 @@
 "`LWG4235 <https://wg21.link/LWG4235>`__","``cache_latest_view`` and ``to_input_view`` miss ``reserve_hint``","2025-06 (Sofia)","","","`#148219 <https://github.com/llvm/llvm-project/issues/148219>`__",""
 "`LWG4236 <https://wg21.link/LWG4236>`__","``chunk_view::outer-iterator::value_type`` should provide ``reserve_hint``","2025-06 (Sofia)","","","`#148220 <https://github.com/llvm/llvm-project/issues/148220>`__",""
 "`LWG4239 <https://wg21.link/LWG4239>`__","``flat_map``'s transparent comparator no longer works for string literals","2025-06 (Sofia)","","","`#148223 <https://github.com/llvm/llvm-project/issues/148223>`__",""
-"`LWG4242 <https://wg21.link/LWG4242>`__","``ranges::distance`` does not work with volatile iterators","2025-06 (Sofia)","","","`#148224 <https://github.com/llvm/llvm-project/issues/148224>`__",""
+"`LWG4242 <https://wg21.link/LWG4242>`__","``ranges::distance`` does not work with volatile iterators","2025-06 (Sofia)","|Complete|","24","`#148224 <https://github.com/llvm/llvm-project/issues/148224>`__",""
 "`LWG4245 <https://wg21.link/LWG4245>`__","Operators that interact with ``counted_iterator`` and ``default_sentinel_t`` should be ``noexcept``","2025-06 (Sofia)","","","`#148226 <https://github.com/llvm/llvm-project/issues/148226>`__",""
 "`LWG4247 <https://wg21.link/LWG4247>`__","Header ``<stdbit.h>`` is not yet freestanding","2025-06 (Sofia)","","","`#148227 <https://github.com/llvm/llvm-project/issues/148227>`__",""
 "","","","","","",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..029117e2ff5cc 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -87,7 +87,7 @@ struct __distance {
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
-    if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
+    if constexpr (!is_array_v<remove_reference_t<_Ip>>) {
       return __last - __first;
     } else {
       return __last - decay_t<_Ip>(__first);
diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index 30507c59495ea..37ee37a0e5876 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -199,6 +199,41 @@ static_assert(!std::sized_sentinel_for<EvilSentinel<int*>, const int*>);
 static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, int*>);
 static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, const int*>);
 
+// LWG3664: "LWG3392 broke std::ranges::distance(a, a+3)" tests.
+constexpr void test_lwg3664() {
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, a + 3) == 3);
+    assert(std::ranges::distance(a, a) == 0);
+    assert(std::ranges::distance(a + 3, a) == -3);
+  }
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
+  }
+  {
+    const int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  }
+}
+
+// LWG4242: "ranges::distance does not work with volatile iterators" tests.
+void test_lwg4242() {
+  int arr[]         = {1, 2, 3};
+  int* volatile ptr = arr;
+  auto dist         = std::distance(ptr, arr + 3);
+  auto r_dist       = std::ranges::distance(ptr, arr + 3);
+  assert(dist == r_dist);
+}
+
 constexpr bool test() {
   {
     int a[] = {1, 2, 3};
@@ -284,30 +319,12 @@ constexpr bool test() {
     auto view = c | std::views::join;
     assert(std::ranges::distance(view.begin(), view.end()) == 30);
   }
-  if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
-    test_deque();
 
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, a + 3) == 3);
-    assert(std::ranges::distance(a, a) == 0);
-    assert(std::ranges::distance(a + 3, a) == -3);
-  }
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
-    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
-  }
-  {
-    const int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
-    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  test_lwg3664();
+
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    test_deque(); // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
+    test_lwg4242();
   }
 
   return true;

``````````

</details>


https://github.com/llvm/llvm-project/pull/211568

From libcxx-commits at lists.llvm.org  Thu Jul 23 08:13:24 2026
From: libcxx-commits at lists.llvm.org (Brian Cain via libcxx-commits)
Date: Thu, 23 Jul 2026 08:13:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Use .set for symbol assignment
	on Hexagon (PR #211578)
Message-ID: <llvm.org/llvm/llvm-project/pull/211578@github.com>


https://github.com/androm3da created https://github.com/llvm/llvm-project/pull/211578

std::__is_function_overridden() defines __impl_ref<_Func>::__impl_ as a local uses asm (`sym = expr`). On Hexagon `=` is the operand assignment operator of the instruction syntax, so the assembler parses that line as an instruction and rejects it:

  overridable_function.h:106:11: error: unrecognized instruction
    106 |   __asm__("%cc0 = %cc1" : : "X"(...), "X"(_Func));
        |           ^
  <inline asm>:1:2: note: instantiated into assembly here
      1 |         _ZNSt3__110__impl_refIXadL_ZnwjEEE7__impl_Ej = _Znwj

The assembler does accept the similar `.set sym, expr` directive, so let's use that here.

This was encountered in libfuzzer's libc++ - built w/-fno-exceptions.

>From 3e008014c7470fea07fdf89dcdbb3d0bd7e6f649 Mon Sep 17 00:00:00 2001
From: Brian Cain <brian.cain at oss.qualcomm.com>
Date: Thu, 23 Jul 2026 09:36:49 -0500
Subject: [PATCH] [libc++] Use .set for symbol assignment on Hexagon

std::__is_function_overridden() defines __impl_ref<_Func>::__impl_ as a local
uses asm (`sym = expr`). On Hexagon `=` is the operand assignment
operator of the instruction syntax, so the assembler parses that line as an
instruction and rejects it:

  overridable_function.h:106:11: error: unrecognized instruction
    106 |   __asm__("%cc0 = %cc1" : : "X"(...), "X"(_Func));
        |           ^
  <inline asm>:1:2: note: instantiated into assembly here
      1 |         _ZNSt3__110__impl_refIXadL_ZnwjEEE7__impl_Ej = _Znwj

The assembler does accept the similar `.set sym, expr` directive, so
let's use that here.

This was encountered in libfuzzer's libc++ - built w/-fno-exceptions.
---
 libcxx/src/include/overridable_function.h | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index 307405812f9af..f75a26b4293b1 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -92,16 +92,22 @@ struct __impl_ref<_Func> {
   [[gnu::visibility("hidden")]] static _Ret __impl_(_Args...);
 };
 
+#    if defined(__hexagon__)
+#      define _LIBCPP_ASM_SYMBOL_ASSIGNMENT(__dst, __src) ".set " __dst ", " __src
+#    else
+#      define _LIBCPP_ASM_SYMBOL_ASSIGNMENT(__dst, __src) __dst " = " __src
+#    endif
+
 // This takes a function type template argument first so that the second non-type template
 // argument (pointer to the public function) gets the benefit of type-aware overload
 // resolution, rather than having to use a static_cast.
 template <typename T, T* _Func>
 _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
-#  if !defined(_LIBCPP_CLANG_VER) || _LIBCPP_CLANG_VER >= 2101
-  __asm__("%cc0 = %cc1" : : "X"(__impl_ref<_Func>::__impl_), "X"(_Func));
-#  else
-  __asm__("%c0 = %c1" : : "X"(__impl_ref<_Func>::__impl_), "X"(_Func));
-#  endif
+#    if !defined(_LIBCPP_CLANG_VER) || _LIBCPP_CLANG_VER >= 2101
+  __asm__(_LIBCPP_ASM_SYMBOL_ASSIGNMENT("%cc0", "%cc1") : : "X"(__impl_ref<_Func>::__impl_), "X"(_Func));
+#    else
+  __asm__(_LIBCPP_ASM_SYMBOL_ASSIGNMENT("%c0", "%c1") : : "X"(__impl_ref<_Func>::__impl_), "X"(_Func));
+#    endif
   // This just has the compiler compare the two symbols. For PIC mode, this will do a
   // direct PC-relative materialization for __impl_ref<...>::__impl_ and a GOT load for
   // the _Func symbol. The compiler thinks __impl_ref<...>::__impl_ is defined elsewhere
@@ -110,6 +116,8 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
   return __launder_function_pointer(_Func) != __impl_ref<_Func>::__impl_;
 }
 
+#    undef _LIBCPP_ASM_SYMBOL_ASSIGNMENT
+
 _LIBCPP_END_NAMESPACE_STD
 
 #  else // __has_feature(ptrauth_calls)


From libcxx-commits at lists.llvm.org  Thu Jul 23 08:14:41 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Thu, 23 Jul 2026 08:14:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a622fe1.1e1ac41d.317291.ca0d@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/211568

>From 8e3559129e89e81f7b8bf0d195222363898ef654 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 15:38:21 +0200
Subject: [PATCH 1/2] resolve: LWG4242

---
 libcxx/docs/Status/Cxx26Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 5b046b69d14bd..83ad46827ca18 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -142,7 +142,7 @@
 "`LWG4235 <https://wg21.link/LWG4235>`__","``cache_latest_view`` and ``to_input_view`` miss ``reserve_hint``","2025-06 (Sofia)","","","`#148219 <https://github.com/llvm/llvm-project/issues/148219>`__",""
 "`LWG4236 <https://wg21.link/LWG4236>`__","``chunk_view::outer-iterator::value_type`` should provide ``reserve_hint``","2025-06 (Sofia)","","","`#148220 <https://github.com/llvm/llvm-project/issues/148220>`__",""
 "`LWG4239 <https://wg21.link/LWG4239>`__","``flat_map``'s transparent comparator no longer works for string literals","2025-06 (Sofia)","","","`#148223 <https://github.com/llvm/llvm-project/issues/148223>`__",""
-"`LWG4242 <https://wg21.link/LWG4242>`__","``ranges::distance`` does not work with volatile iterators","2025-06 (Sofia)","","","`#148224 <https://github.com/llvm/llvm-project/issues/148224>`__",""
+"`LWG4242 <https://wg21.link/LWG4242>`__","``ranges::distance`` does not work with volatile iterators","2025-06 (Sofia)","|Complete|","24","`#148224 <https://github.com/llvm/llvm-project/issues/148224>`__",""
 "`LWG4245 <https://wg21.link/LWG4245>`__","Operators that interact with ``counted_iterator`` and ``default_sentinel_t`` should be ``noexcept``","2025-06 (Sofia)","","","`#148226 <https://github.com/llvm/llvm-project/issues/148226>`__",""
 "`LWG4247 <https://wg21.link/LWG4247>`__","Header ``<stdbit.h>`` is not yet freestanding","2025-06 (Sofia)","","","`#148227 <https://github.com/llvm/llvm-project/issues/148227>`__",""
 "","","","","","",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..029117e2ff5cc 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -87,7 +87,7 @@ struct __distance {
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
-    if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
+    if constexpr (!is_array_v<remove_reference_t<_Ip>>) {
       return __last - __first;
     } else {
       return __last - decay_t<_Ip>(__first);

>From 654370a5f26aed08caebe4fbc98f09417e272517 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 15:56:59 +0200
Subject: [PATCH 2/2] add: lwg4242 test case and refactor lwg3664 test case

---
 .../iterator_sentinel.pass.cpp                | 63 ++++++++++++-------
 1 file changed, 40 insertions(+), 23 deletions(-)

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index 30507c59495ea..37ee37a0e5876 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -199,6 +199,41 @@ static_assert(!std::sized_sentinel_for<EvilSentinel<int*>, const int*>);
 static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, int*>);
 static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, const int*>);
 
+// LWG3664: "LWG3392 broke std::ranges::distance(a, a+3)" tests.
+constexpr void test_lwg3664() {
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, a + 3) == 3);
+    assert(std::ranges::distance(a, a) == 0);
+    assert(std::ranges::distance(a + 3, a) == -3);
+  }
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
+  }
+  {
+    const int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  }
+}
+
+// LWG4242: "ranges::distance does not work with volatile iterators" tests.
+void test_lwg4242() {
+  int arr[]         = {1, 2, 3};
+  int* volatile ptr = arr;
+  auto dist         = std::distance(ptr, arr + 3);
+  auto r_dist       = std::ranges::distance(ptr, arr + 3);
+  assert(dist == r_dist);
+}
+
 constexpr bool test() {
   {
     int a[] = {1, 2, 3};
@@ -284,30 +319,12 @@ constexpr bool test() {
     auto view = c | std::views::join;
     assert(std::ranges::distance(view.begin(), view.end()) == 30);
   }
-  if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
-    test_deque();
 
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, a + 3) == 3);
-    assert(std::ranges::distance(a, a) == 0);
-    assert(std::ranges::distance(a + 3, a) == -3);
-  }
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
-    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
-  }
-  {
-    const int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
-    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  test_lwg3664();
+
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    test_deque(); // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
+    test_lwg4242();
   }
 
   return true;


From libcxx-commits at lists.llvm.org  Thu Jul 23 08:15:25 2026
From: libcxx-commits at lists.llvm.org (William Tran-Viet via libcxx-commits)
Date: Thu, 23 Jul 2026 08:15:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Inline `text_encoding` alias
 offset table std::count to 884 (PR #207471)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207471@github.com>
Message-ID: <6a62300d.c5504292.1bbfc8.266e@mx.google.com>


smallp-o-p wrote:

Gentle ping to merge 

https://github.com/llvm/llvm-project/pull/207471

From libcxx-commits at lists.llvm.org  Thu Jul 23 12:53:00 2026
From: libcxx-commits at lists.llvm.org (Nhat Nguyen via libcxx-commits)
Date: Thu, 23 Jul 2026 12:53:00 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [lldb] [clang]: implement
	std::meta::is_type (PR #211653)
Message-ID: <llvm.org/llvm/llvm-project/pull/211653@github.com>


https://github.com/changkhothuychung created https://github.com/llvm/llvm-project/pull/211653

This PR implements `std::meta::is_type(info)` by introducing a builtin `__builtin_meta_is_type`.  It also includes two feature-test macros `__cpp_impl_reflection` and `__cpp_lib_reflection` introduced in [P2996 ](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2996r13.html). `__cpp_impl_reflection` is only enabled when `-freflection` is on, and `__cpp_lib_reflection` depends on `__cpp_impl_reflection`. 

Header `<meta>` is created in libc++. 

>From d31d40d2839e716fa95d3f624ef04dc329968d49 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Thu, 23 Jul 2026 14:59:10 -0400
Subject: [PATCH 1/2] support std::meta::info for primitive types

---
 clang/include/clang/AST/APValue.h             | 43 +++++++++-
 clang/include/clang/AST/ASTContext.h          |  1 +
 clang/include/clang/AST/BuiltinTypes.def      |  3 +
 clang/include/clang/AST/ExprCXX.h             | 21 +++--
 clang/include/clang/AST/PropertiesBase.td     | 25 ++++++
 clang/include/clang/AST/Reflection.h          | 39 +++++++++
 clang/include/clang/AST/TypeBase.h            |  5 ++
 clang/include/clang/Basic/TargetInfo.h        |  6 ++
 .../include/clang/Serialization/ASTBitCodes.h |  5 +-
 clang/lib/AST/APValue.cpp                     | 79 +++++++++++++++++++
 clang/lib/AST/ASTContext.cpp                  |  9 +++
 clang/lib/AST/ASTImporter.cpp                 | 34 +++++++-
 clang/lib/AST/ByteCode/Compiler.cpp           | 20 +++++
 clang/lib/AST/ByteCode/Compiler.h             |  1 +
 clang/lib/AST/ByteCode/Context.cpp            |  3 +
 clang/lib/AST/ByteCode/Descriptor.cpp         |  1 +
 clang/lib/AST/ByteCode/Disasm.cpp             |  2 +
 clang/lib/AST/ByteCode/Interp.cpp             |  2 +-
 clang/lib/AST/ByteCode/Interp.h               |  7 ++
 .../lib/AST/ByteCode/InterpBuiltinBitCast.cpp |  2 +-
 clang/lib/AST/ByteCode/InterpStack.h          |  3 +
 clang/lib/AST/ByteCode/InterpState.cpp        |  1 +
 clang/lib/AST/ByteCode/Opcodes.td             |  9 ++-
 clang/lib/AST/ByteCode/PrimType.cpp           |  1 +
 clang/lib/AST/ByteCode/PrimType.h             |  6 ++
 clang/lib/AST/ByteCode/Program.cpp            |  1 +
 clang/lib/AST/ByteCode/Program.h              |  1 +
 clang/lib/AST/ByteCode/Reflect.h              | 60 ++++++++++++++
 clang/lib/AST/ExprCXX.cpp                     | 11 +--
 clang/lib/AST/ExprConstant.cpp                | 77 +++++++++++++++++-
 clang/lib/AST/ItaniumMangle.cpp               | 73 ++++++++++++++++-
 clang/lib/AST/MicrosoftMangle.cpp             |  5 ++
 clang/lib/AST/NSAPI.cpp                       |  1 +
 clang/lib/AST/StmtPrinter.cpp                 | 11 ++-
 clang/lib/AST/TextNodeDumper.cpp              | 16 ++++
 clang/lib/AST/Type.cpp                        | 10 +++
 clang/lib/AST/TypeLoc.cpp                     |  1 +
 clang/lib/Basic/TargetInfo.cpp                |  2 +
 clang/lib/CodeGen/CGDebugInfo.cpp             |  2 +
 clang/lib/CodeGen/CGExprConstant.cpp          |  2 +
 clang/lib/CodeGen/CodeGenModule.cpp           |  2 +
 clang/lib/CodeGen/CodeGenTypes.cpp            |  7 ++
 clang/lib/CodeGen/ItaniumCXXABI.cpp           |  1 +
 clang/lib/CodeGen/QualTypeMapper.cpp          |  3 +
 clang/lib/Sema/SemaExpr.cpp                   | 13 ++-
 clang/lib/Sema/SemaOverload.cpp               | 23 ++++--
 clang/lib/Sema/SemaTemplate.cpp               | 17 +++-
 clang/lib/Sema/TreeTransform.h                | 14 +++-
 clang/lib/Serialization/ASTCommon.cpp         |  3 +
 clang/lib/Serialization/ASTReader.cpp         |  3 +
 clang/lib/Serialization/ASTReaderStmt.cpp     | 15 +++-
 clang/lib/Serialization/ASTWriterStmt.cpp     | 15 +++-
 .../UnifiedSymbolResolution/USRGeneration.cpp |  3 +
 .../test/AST/ast-dump-APValue-reflection.cpp  | 14 ++++
 .../reflection-emit-meta-info-itanium.cpp     |  7 ++
 ...s.cpp => reflection-emit-meta-info-ms.cpp} |  0
 .../CodeGenCXX/reflection-mangle-itanium.cpp  | 42 +++++++++-
 clang/test/PCH/reflection.cpp                 | 14 ++++
 clang/test/PCH/reflection_include.h           |  1 +
 clang/test/Sema/reflection-meta-info.fail.cpp | 56 +++++++++++++
 clang/test/Sema/reflection-meta-info.pass.cpp | 77 ++++++++++++++++++
 clang/tools/libclang/CIndex.cpp               |  1 +
 clang/unittests/AST/ASTImporterTest.cpp       | 29 +++++++
 .../TypeSystem/Clang/TypeSystemClang.cpp      |  3 +
 64 files changed, 925 insertions(+), 39 deletions(-)
 create mode 100644 clang/include/clang/AST/Reflection.h
 create mode 100644 clang/lib/AST/ByteCode/Reflect.h
 create mode 100644 clang/test/AST/ast-dump-APValue-reflection.cpp
 create mode 100644 clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp
 rename clang/test/CodeGenCXX/{reflection-mangle-ms.cpp => reflection-emit-meta-info-ms.cpp} (100%)
 create mode 100644 clang/test/PCH/reflection.cpp
 create mode 100644 clang/test/PCH/reflection_include.h
 create mode 100644 clang/test/Sema/reflection-meta-info.fail.cpp
 create mode 100644 clang/test/Sema/reflection-meta-info.pass.cpp

diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h
index 9293266a41256..2f6efc670b6e8 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_CLANG_AST_APVALUE_H
 #define LLVM_CLANG_AST_APVALUE_H
 
+#include "clang/AST/Reflection.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/APFixedPoint.h"
 #include "llvm/ADT/APFloat.h"
@@ -141,7 +142,8 @@ class APValue {
     Struct,
     Union,
     MemberPointer,
-    AddrLabelDiff
+    AddrLabelDiff,
+    Reflection
   };
 
   class alignas(uint64_t) LValueBase {
@@ -315,12 +317,21 @@ class APValue {
     const AddrLabelExpr* LHSExpr;
     const AddrLabelExpr* RHSExpr;
   };
+  struct ReflectionData {
+    // OperandKind will eventually have support for
+    // TypeSourceInfo, TemplateReference, NamespaceReference, DeclRefExpr.
+    // Operand stores the opaque pointer of the reflection operand.
+    // Depending on the value of OperandKind, we can perform the
+    // corresponding cast to the associated type.
+    ReflectionKind OperandKind;
+    const void *Operand;
+  };
   struct MemberPointerData;
 
   // We ensure elsewhere that Data is big enough for LV and MemberPointerData.
-  typedef llvm::AlignedCharArrayUnion<void *, APSInt, APFloat, ComplexAPSInt,
-                                      ComplexAPFloat, Vec, Mat, Arr, StructData,
-                                      UnionData, AddrLabelDiffData>
+  typedef llvm::AlignedCharArrayUnion<
+      void *, APSInt, APFloat, ComplexAPSInt, ComplexAPFloat, Vec, Mat, Arr,
+      StructData, UnionData, AddrLabelDiffData, ReflectionData>
       DataType;
   static const size_t DataSize = sizeof(DataType);
 
@@ -415,6 +426,14 @@ class APValue {
       : Kind(None), AllowConstexprUnknown(false) {
     MakeArray(InitElts, Size);
   }
+
+  /// Creates a new Reflection APValue.
+  /// \param OperandKind The kind of reflection.
+  /// \param Operand The entity being reflected.
+  APValue(ReflectionKind OperandKind, const void *Operand) : Kind(None) {
+    MakeReflection(OperandKind, Operand);
+  }
+
   /// Creates a new struct APValue.
   /// \param UninitStruct Marker. Pass an empty UninitStruct.
   /// \param NumBases Number of bases.
@@ -498,6 +517,7 @@ class APValue {
   bool isUnion() const { return Kind == Union; }
   bool isMemberPointer() const { return Kind == MemberPointer; }
   bool isAddrLabelDiff() const { return Kind == AddrLabelDiff; }
+  bool isReflection() const { return Kind == Reflection; }
 
   void dump() const;
   void dump(raw_ostream &OS, const ASTContext &Context) const;
@@ -717,6 +737,16 @@ class APValue {
     return ((const AddrLabelDiffData *)(const char *)&Data)->RHSExpr;
   }
 
+  ReflectionKind getReflectionOperandKind() const {
+    assert(isReflection() && "Invalid accessor");
+    return ((const ReflectionData *)(const char *)&Data)->OperandKind;
+  }
+
+  const void *getReflectionOpaqueOperand() const {
+    assert(isReflection() && "Invalid accessor");
+    return ((const ReflectionData *)(const char *)&Data)->Operand;
+  }
+
   void setInt(APSInt I) {
     assert(isInt() && "Invalid accessor");
     *(APSInt *)(char *)&Data = std::move(I);
@@ -767,6 +797,11 @@ class APValue {
 
 private:
   void DestroyDataAndMakeUninit();
+  void MakeReflection(ReflectionKind OperandKind, const void *Operand) {
+    assert(isAbsent() && "Bad state change");
+    new ((void *)(char *)Data.buffer) ReflectionData{OperandKind, Operand};
+    Kind = Reflection;
+  }
   void MakeInt() {
     assert(isAbsent() && "Bad state change");
     new ((void *)&Data) APSInt(1);
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 7ed6509c3c16c..05dfec8c37586 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1351,6 +1351,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   CanQualType BFloat16Ty;
   CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3
   CanQualType VoidPtrTy, NullPtrTy;
+  CanQualType MetaInfoTy;
   CanQualType DependentTy, OverloadTy, BoundMemberTy, UnresolvedTemplateTy,
       UnknownAnyTy;
   CanQualType BuiltinFnTy;
diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def
index 444be4311a743..ee2f3f0365126 100644
--- a/clang/include/clang/AST/BuiltinTypes.def
+++ b/clang/include/clang/AST/BuiltinTypes.def
@@ -223,6 +223,9 @@ FLOATING_TYPE(Ibm128, Ibm128Ty)
 
 //===- Language-specific types --------------------------------------------===//
 
+// 'std::meta::info' in C++
+BUILTIN_TYPE(MetaInfo, MetaInfoTy)
+
 // This is the type of C++0x 'nullptr'.
 BUILTIN_TYPE(NullPtr, NullPtrTy)
 
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 757f2137c90dc..4ed388877a2ab 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -25,6 +25,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/OperationKinds.h"
+#include "clang/AST/Reflection.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/TemplateBase.h"
@@ -5502,37 +5503,47 @@ class BuiltinBitCastExpr final
 ///  - a type-id, or
 ///  - an id-expression.
 class CXXReflectExpr : public Expr {
+  friend class ASTStmtReader;
+  friend class ASTStmtWriter;
 
+private:
   // TODO(Reflection): add support for TemplateReference, NamespaceReference and
   // DeclRefExpr
-  using operand_type = llvm::PointerUnion<const TypeSourceInfo *>;
+  using operand_type = llvm::PointerUnion<TypeSourceInfo *>;
 
   SourceLocation CaretCaretLoc;
+  ReflectionKind Kind;
   operand_type Operand;
 
-  CXXReflectExpr(SourceLocation CaretCaretLoc, const TypeSourceInfo *TSI);
+  CXXReflectExpr(ASTContext &C, SourceLocation CaretCaretLoc,
+                 TypeSourceInfo *TSI);
   CXXReflectExpr(EmptyShell Empty);
 
 public:
   static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc,
-                                TypeSourceInfo *TL);
+                                TypeSourceInfo *TSI);
 
   static CXXReflectExpr *CreateEmpty(ASTContext &C);
 
   SourceLocation getBeginLoc() const LLVM_READONLY {
     return llvm::TypeSwitch<operand_type, SourceLocation>(Operand)
-        .Case<const TypeSourceInfo *>(
+        .Case<TypeSourceInfo *>(
             [](auto *Ptr) { return Ptr->getTypeLoc().getBeginLoc(); });
   }
 
   SourceLocation getEndLoc() const LLVM_READONLY {
     return llvm::TypeSwitch<operand_type, SourceLocation>(Operand)
-        .Case<const TypeSourceInfo *>(
+        .Case<TypeSourceInfo *>(
             [](auto *Ptr) { return Ptr->getTypeLoc().getEndLoc(); });
   }
 
   /// Returns location of the '^^'-operator.
   SourceLocation getOperatorLoc() const { return CaretCaretLoc; }
+  ReflectionKind getKind() const { return Kind; }
+  void *getOpaqueValue() const { return Operand.getOpaqueValue(); }
+  TypeSourceInfo *getTypeSourceInfo() const {
+    return cast<TypeSourceInfo *>(Operand);
+  }
 
   child_range children() {
     // TODO(Reflection)
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index 25ef4c26a9aa1..9a5c683fbf693 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -603,6 +603,31 @@ let Class = PropertyTypeCase<APValue, "LValue"> in {
   }]>;
 }
 
+def ReflectionKind : EnumPropertyType<"ReflectionKind">;
+
+let Class = PropertyTypeCase<APValue, "Reflection"> in {
+  def : Property<"reflectionKind", ReflectionKind> {
+    let Read = [{ node.getReflectionOperandKind() }];
+  }
+  def : Property<"reflectionType", QualType> {
+    let Conditional = [{ reflectionKind == ReflectionKind::Type }];
+    let Read = [{
+      QualType::getFromOpaquePtr(node.getReflectionOpaqueOperand())
+    }];
+  }
+  def : Creator<[{
+    switch (reflectionKind) {
+    // TODO(Reflection): Add support for TypeSourceInfo, NamespaceReference
+    // TemplateReference and DeclRefExpr
+    case ReflectionKind::Null:
+      return APValue(reflectionKind, nullptr);
+    case ReflectionKind::Type:
+      return APValue(reflectionKind, (*reflectionType).getAsOpaquePtr());
+    }
+    llvm_unreachable("unimplemented or unknow reflection entities");
+  }]>;
+}
+
 // Type cases for DeclarationName.
 def : PropertyTypeKind<DeclarationName, DeclarationNameKind,
                        "node.getNameKind()">;
diff --git a/clang/include/clang/AST/Reflection.h b/clang/include/clang/AST/Reflection.h
new file mode 100644
index 0000000000000..732aefde80885
--- /dev/null
+++ b/clang/include/clang/AST/Reflection.h
@@ -0,0 +1,39 @@
+//===--- Reflection.h - Kind of reflection operands ---*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares the kinds of reflection operands.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_REFLECTION_H
+#define LLVM_CLANG_AST_REFLECTION_H
+
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+
+// TODO(Reflection): Add support for Template, Namespace and DeclRefExpr.
+enum class ReflectionKind { Null, Type };
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+                                     ReflectionKind Kind) {
+  switch (Kind) {
+  case ReflectionKind::Type:
+    OS << "type";
+    break;
+  case ReflectionKind::Null:
+    OS << "null";
+    break;
+  }
+
+  return OS;
+}
+
+} // namespace clang
+
+#endif
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index c9658775f0470..4886db6998b45 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -2766,6 +2766,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool isUndeducedAutoType() const;             // C++11 auto or
                                                 // C++14 decltype(auto)
   bool isTypedefNameType() const;               // typedef or alias template
+  bool isMetaInfoType() const;                  // C++26 std::meta::info
 
 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
   bool is##Id##Type() const;
@@ -9051,6 +9052,10 @@ inline bool Type::isVoidType() const {
   return isSpecificBuiltinType(BuiltinType::Void);
 }
 
+inline bool Type::isMetaInfoType() const {
+  return isSpecificBuiltinType(BuiltinType::MetaInfo);
+}
+
 inline bool Type::isHalfType() const {
   // FIXME: Should we allow complex __fp16? Probably not.
   return isSpecificBuiltinType(BuiltinType::Half);
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 3aba4d261a651..dacc1d3a5d9ea 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -96,6 +96,7 @@ struct TransferrableTargetInfo {
   unsigned char FloatWidth, FloatAlign;
   unsigned char DoubleWidth, DoubleAlign;
   unsigned char LongDoubleWidth, LongDoubleAlign, Float128Align, Ibm128Align;
+  unsigned char MetaInfoWidth, MetaInfoAlign;
   unsigned char LargeArrayMinWidth, LargeArrayAlign;
   unsigned char LongWidth, LongAlign;
   unsigned char LongLongWidth, LongLongAlign;
@@ -828,6 +829,11 @@ class TargetInfo : public TransferrableTargetInfo,
   unsigned getIbm128Align() const { return Ibm128Align; }
   const llvm::fltSemantics &getIbm128Format() const { return *Ibm128Format; }
 
+  /// Returns the size of std::meta::info.
+  unsigned getMetaInfoWidth() const { return MetaInfoWidth; }
+  /// eturns the align of std::meta::info.
+  unsigned getMetaInfoAlign() const { return MetaInfoAlign; }
+
   /// Return the mangled code of long double.
   virtual const char *getLongDoubleMangling() const { return "e"; }
 
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 671341488278e..e36e6ea29810f 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1155,6 +1155,9 @@ enum PredefinedTypeIDs {
 #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) PREDEF_TYPE_##Id##_ID,
 #include "clang/Basic/HLSLIntangibleTypes.def"
 
+  /// C++26 std::meta::info type.
+  PREDEF_TYPE_META_INFO_ID,
+
   /// The placeholder type for unresolved templates.
   PREDEF_TYPE_UNRESOLVED_TEMPLATE,
   // Sentinel value. Considered a predefined type but not useable as one.
@@ -1166,7 +1169,7 @@ enum PredefinedTypeIDs {
 ///
 /// Type IDs for non-predefined types will start at
 /// NUM_PREDEF_TYPE_IDs.
-const unsigned NUM_PREDEF_TYPE_IDS = 529;
+const unsigned NUM_PREDEF_TYPE_IDS = 530;
 
 // Ensure we do not overrun the predefined types we reserved
 // in the enum PredefinedTypeIDs above.
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index 727e5f8c00a10..1849562952fb7 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -374,6 +374,10 @@ APValue::APValue(const APValue &RHS)
     MakeAddrLabelDiff();
     setAddrLabelDiff(RHS.getAddrLabelDiffLHS(), RHS.getAddrLabelDiffRHS());
     break;
+  case Reflection:
+    MakeReflection(RHS.getReflectionOperandKind(),
+                   RHS.getReflectionOpaqueOperand());
+    break;
   }
 }
 
@@ -429,6 +433,8 @@ void APValue::DestroyDataAndMakeUninit() {
     ((MemberPointerData *)(char *)&Data)->~MemberPointerData();
   else if (Kind == AddrLabelDiff)
     ((AddrLabelDiffData *)(char *)&Data)->~AddrLabelDiffData();
+  else if (Kind == Reflection)
+    ((ReflectionData *)(char *)&Data)->~ReflectionData();
   Kind = None;
   AllowConstexprUnknown = false;
 }
@@ -438,6 +444,7 @@ bool APValue::needsCleanup() const {
   case None:
   case Indeterminate:
   case AddrLabelDiff:
+  case Reflection:
     return false;
   case Struct:
   case Union:
@@ -486,6 +493,61 @@ static void profileIntValue(llvm::FoldingSetNodeID &ID, const llvm::APInt &V) {
     ID.AddInteger((uint32_t)V.extractBitsAsZExtValue(std::min(32u, N - I), I));
 }
 
+/// [expr.reflect] p5, if a reflect-expression R matches the form
+/// ^^reflection-name it is interpreted as such; the identifier is looked up
+/// and the representation of R is determined as follows:
+/// - if lookup finds a type alias A, R represents the type the underlying
+///   entity of A if A was introduced by the declaration of a template
+///   parameter; otherwise, R represents A.
+
+/// [expr.reflect] p6, Given reflect-expression R of the form ^^type-id,
+/// if type-id is neither a placeholder type nor
+/// in the form of nested-name-specifier_opt template_opt simple-template-id
+/// then R represents the type denoted by the type-id
+
+// In particular, this means that e.g. '^^const Alias' is reflection of
+// a type, not an alias. For example:
+//
+// using foo = const int;
+// ^^int       // Type
+// ^^const int // Type
+// ^^foo       // Alias
+// ^^const foo // Type
+static bool isTypeAliasAsReflectionName(QualType QT) {
+  return QT.getLocalQualifiers() == Qualifiers{};
+}
+
+/// Unwrap reflected type for profiling
+static void profileTypeReflection(llvm::FoldingSetNodeID &ID, QualType QT) {
+  // TODO(Reflection)
+
+  if (isTypeAliasAsReflectionName(QT)) {
+    if (const auto *TDT = QT->getAs<TypedefType>()) {
+      ID.AddBoolean(true);
+      ID.AddPointer(TDT->getDecl()->getCanonicalDecl());
+      return;
+    }
+  }
+
+  ID.AddBoolean(false);
+  QT.getCanonicalType().Profile(ID);
+}
+
+static void profileReflection(llvm::FoldingSetNodeID &ID, APValue V) {
+  ID.AddInteger(static_cast<int>(V.getReflectionOperandKind()));
+  switch (V.getReflectionOperandKind()) {
+  case ReflectionKind::Null:
+    return;
+  case ReflectionKind::Type: {
+    const TypeSourceInfo *Info =
+        static_cast<const TypeSourceInfo *>(V.getReflectionOpaqueOperand());
+    profileTypeReflection(ID, Info->getType());
+    return;
+  }
+  }
+  assert(false && "unknown or unimplemented reflection entities");
+}
+
 void APValue::Profile(llvm::FoldingSetNodeID &ID) const {
   // Note that our profiling assumes that only APValues of the same type are
   // ever compared. As a result, we don't consider collisions that could only
@@ -632,6 +694,9 @@ void APValue::Profile(llvm::FoldingSetNodeID &ID) const {
     for (const CXXRecordDecl *D : getMemberPointerPath())
       ID.AddPointer(D);
     return;
+  case Reflection:
+    profileReflection(ID, *this);
+    return;
   }
 
   llvm_unreachable("Unknown APValue kind!");
@@ -986,6 +1051,19 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy,
     Out << " - ";
     Out << "&&" << getAddrLabelDiffRHS()->getLabel()->getName();
     return;
+  case APValue::Reflection:
+    switch (getReflectionOperandKind()) {
+    case ReflectionKind::Null:
+      Out << "std::meta::info{}";
+      break;
+    case ReflectionKind::Type: {
+      const auto *TInfo =
+          static_cast<const TypeSourceInfo *>(getReflectionOpaqueOperand());
+      Out << "^^" << TInfo->getType().stream(Policy);
+      break;
+    }
+    }
+    return;
   }
   llvm_unreachable("Unknown APValue kind!");
 }
@@ -1176,6 +1254,7 @@ LinkageInfo LinkageComputer::getLVForValue(const APValue &V,
   case APValue::ComplexInt:
   case APValue::ComplexFloat:
   case APValue::Vector:
+  case APValue::Reflection:
   case APValue::Matrix:
     break;
 
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 2228811546c0f..c94598662915d 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1511,6 +1511,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target,
   // nullptr type (C++0x 2.14.7)
   InitBuiltinType(NullPtrTy,           BuiltinType::NullPtr);
 
+  // std::meta::info type (C++26 21.4.1)
+  InitBuiltinType(MetaInfoTy, BuiltinType::MetaInfo);
+
   // half type (OpenCL 6.1.1.1) / ARM NEON __fp16
   InitBuiltinType(HalfTy, BuiltinType::Half);
 
@@ -2366,6 +2369,10 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
       Width = Target->getPointerWidth(LangAS::Default);
       Align = Target->getPointerAlign(LangAS::Default);
       break;
+    case BuiltinType::MetaInfo:
+      Width = Target->getMetaInfoWidth();
+      Align = Target->getMetaInfoAlign();
+      break;
     case BuiltinType::ObjCId:
     case BuiltinType::ObjCClass:
     case BuiltinType::ObjCSel:
@@ -3568,6 +3575,7 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx,
     case BuiltinType::VectorPair:
     case BuiltinType::DMR1024:
     case BuiltinType::DMR2048:
+    case BuiltinType::MetaInfo:
       OS << "?";
       return;
 
@@ -9325,6 +9333,7 @@ static char getObjCEncodingForPrimitiveType(const ASTContext *C,
     case BuiltinType::OCLReserveID:
     case BuiltinType::OCLSampler:
     case BuiltinType::Dependent:
+    case BuiltinType::MetaInfo:
 #define PPC_VECTOR_TYPE(Name, Id, Size) \
     case BuiltinType::Id:
 #include "clang/Basic/PPCTypes.def"
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index db7d223d56af3..7e726eb155d38 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -686,6 +686,7 @@ namespace clang {
     ExpectedStmt VisitCXXThisExpr(CXXThisExpr *E);
     ExpectedStmt VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E);
     ExpectedStmt VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E);
+    ExpectedStmt VisitCXXReflectExpr(CXXReflectExpr *E);
     ExpectedStmt VisitMemberExpr(MemberExpr *E);
     ExpectedStmt VisitCallExpr(CallExpr *E);
     ExpectedStmt VisitLambdaExpr(LambdaExpr *LE);
@@ -8861,6 +8862,16 @@ ExpectedStmt ASTNodeImporter::VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E) {
                                     *ToTypeOrErr, *ToLocationOrErr);
 }
 
+ExpectedStmt ASTNodeImporter::VisitCXXReflectExpr(CXXReflectExpr *E) {
+  Error Err = Error::success();
+  auto ToOperatorLoc = importChecked(Err, E->getOperatorLoc());
+  auto ToTSI = importChecked(Err, E->getTypeSourceInfo());
+  if (Err)
+    return std::move(Err);
+
+  return CXXReflectExpr::Create(Importer.getToContext(), ToOperatorLoc, ToTSI);
+}
+
 ExpectedStmt ASTNodeImporter::VisitMemberExpr(MemberExpr *E) {
   Error Err = Error::success();
   auto ToBase = importChecked(Err, E->getBase());
@@ -10873,7 +10884,7 @@ ASTNodeImporter::ImportAPValue(const APValue &FromValue) {
     }
     break;
   }
-  case APValue::LValue:
+  case APValue::LValue: {
     APValue::LValueBase Base;
     QualType FromElemTy;
     if (FromValue.getLValueBase()) {
@@ -10944,6 +10955,27 @@ ASTNodeImporter::ImportAPValue(const APValue &FromValue) {
     } else
       Result.setLValue(Base, Offset, APValue::NoLValuePath{},
                        FromValue.isNullPointer());
+    break;
+  }
+  case APValue::Reflection: {
+    switch (FromValue.getReflectionOperandKind()) {
+    case ReflectionKind::Null:
+      Result = APValue(ReflectionKind::Null, nullptr);
+      break;
+    case ReflectionKind::Type: {
+      const auto *FromTSI = static_cast<const TypeSourceInfo *>(
+          FromValue.getReflectionOpaqueOperand());
+      QualType ImpType = importChecked(Err, FromTSI->getType());
+      if (Err)
+        return std::move(Err);
+      TypeSourceInfo *ToTSI =
+          Importer.ToContext.getTrivialTypeSourceInfo(ImpType);
+      Result = APValue(ReflectionKind::Type, ToTSI);
+      break;
+    }
+    }
+    break;
+  }
   }
   if (Err)
     return std::move(Err);
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 35667a9132680..a698c35871d2e 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -4521,6 +4521,23 @@ bool Compiler<Emitter>::VisitObjCArrayLiteral(const ObjCArrayLiteral *E) {
   return this->emitError(E);
 }
 
+template <class Emitter>
+bool Compiler<Emitter>::VisitCXXReflectExpr(const CXXReflectExpr *E) {
+  if (DiscardResult)
+    return true;
+
+  switch (E->getKind()) {
+  case ReflectionKind::Type: {
+    APValue Result(ReflectionKind::Type, E->getOpaqueValue());
+    return this->emitReflectValue(E->getKind(), E->getOpaqueValue(), E);
+  }
+  case ReflectionKind::Null:
+    llvm_unreachable("A null reflection should not reach here");
+  }
+
+  return false;
+}
+
 template <class Emitter>
 bool Compiler<Emitter>::VisitExpressionTraitExpr(const ExpressionTraitExpr *E) {
   assert(Ctx.getLangOpts().CPlusPlus);
@@ -5038,6 +5055,8 @@ bool Compiler<Emitter>::visitZeroInitializer(PrimType T, QualType QT,
     auto Sem = Ctx.getASTContext().getFixedPointSemantics(QT);
     return this->emitConstFixedPoint(FixedPoint::zero(Sem), E);
   }
+  case PT_Reflect:
+    return this->emitReflectValue(ReflectionKind::Null, nullptr, E);
   }
   llvm_unreachable("unknown primitive type");
 }
@@ -5267,6 +5286,7 @@ bool Compiler<Emitter>::emitConst(T Value, PrimType Ty, SourceInfo Info) {
   case PT_IntAP:
   case PT_IntAPS:
   case PT_FixedPoint:
+  case PT_Reflect:
     llvm_unreachable("Invalid integral type");
     break;
   }
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 9eb8069496099..3051ad9399266 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -232,6 +232,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
   bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
   bool VisitObjCDictionaryLiteral(const ObjCDictionaryLiteral *E);
   bool VisitObjCArrayLiteral(const ObjCArrayLiteral *E);
+  bool VisitCXXReflectExpr(const CXXReflectExpr *E);
   bool VisitDesignatedInitUpdateExpr(const DesignatedInitUpdateExpr *E);
 
   // Statements.
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index b913d2a9f539c..673c6adf5f96b 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -539,6 +539,9 @@ OptPrimType Context::classify(QualType T) const {
   if (T->isFixedPointType())
     return PT_FixedPoint;
 
+  if (T->isMetaInfoType())
+    return PT_Reflect;
+
   // Vector and complex types get here.
   return std::nullopt;
 }
diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp
index fb41c98dd68cb..1118267b61295 100644
--- a/clang/lib/AST/ByteCode/Descriptor.cpp
+++ b/clang/lib/AST/ByteCode/Descriptor.cpp
@@ -16,6 +16,7 @@
 #include "Pointer.h"
 #include "PrimType.h"
 #include "Record.h"
+#include "Reflect.h"
 #include "Source.h"
 #include "clang/AST/ExprCXX.h"
 
diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp
index 4caf830a0a1b4..4a2e47d427f91 100644
--- a/clang/lib/AST/ByteCode/Disasm.cpp
+++ b/clang/lib/AST/ByteCode/Disasm.cpp
@@ -318,6 +318,8 @@ static const char *primTypeToString(PrimType T) {
     return "MemberPtr";
   case PT_FixedPoint:
     return "FixedPoint";
+  case PT_Reflect:
+    return "Reflect";
   }
   llvm_unreachable("Unhandled PrimType");
 }
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index f59485ec306e4..ebe4a7256c7fd 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -3230,7 +3230,7 @@ constexpr bool OpReturns(Opcode Op) {
          Op == OP_RetSint64 || Op == OP_RetUint64 || Op == OP_RetIntAP ||
          Op == OP_RetIntAPS || Op == OP_RetBool || Op == OP_RetFixedPoint ||
          Op == OP_RetPtr || Op == OP_RetMemberPtr || Op == OP_RetFloat ||
-         Op == OP_EndSpeculation;
+         Op == OP_RetReflect || Op == OP_EndSpeculation;
 }
 
 #if USE_TAILCALLS
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 405f4a29ec982..63f6ffc74c237 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -29,6 +29,7 @@
 #include "MemberPointer.h"
 #include "PrimType.h"
 #include "Program.h"
+#include "Reflect.h"
 #include "State.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Expr.h"
@@ -4090,6 +4091,12 @@ inline bool CheckDestruction(InterpState &S, CodePtr OpPC) {
   return checkDestructor(S, OpPC, Ptr);
 }
 
+inline bool ReflectValue(InterpState &S, CodePtr OpPC, ReflectionKind Kind,
+                         const void *Operand) {
+  S.Stk.push<Reflect>(Kind, Operand);
+  return true;
+}
+
 inline bool IsBaseClass(InterpState &S) {
   S.Stk.push<bool>(S.Stk.peek<Pointer>().isBaseClass());
   return true;
diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
index f136301c02912..35edb5988b817 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
@@ -515,7 +515,7 @@ using PrimTypeVariant =
                  Char<false>, Char<true>, Integral<16, false>,
                  Integral<16, true>, Integral<32, false>, Integral<32, true>,
                  Integral<64, false>, Integral<64, true>, IntegralAP<true>,
-                 IntegralAP<false>, Boolean, Floating>;
+                 IntegralAP<false>, Boolean, Floating, Reflect>;
 
 // NB: This implementation isn't exactly ideal, but:
 //   1) We can't just do a bitcast here since we need to be able to
diff --git a/clang/lib/AST/ByteCode/InterpStack.h b/clang/lib/AST/ByteCode/InterpStack.h
index 2c02979ee6eec..a83d173e0eb16 100644
--- a/clang/lib/AST/ByteCode/InterpStack.h
+++ b/clang/lib/AST/ByteCode/InterpStack.h
@@ -17,6 +17,7 @@
 #include "IntegralAP.h"
 #include "MemberPointer.h"
 #include "PrimType.h"
+#include "Reflect.h"
 
 namespace clang {
 namespace interp {
@@ -203,6 +204,8 @@ class InterpStack final {
       return PT_MemberPtr;
     else if constexpr (std::is_same_v<T, FixedPoint>)
       return PT_FixedPoint;
+    else if constexpr (std::is_same_v<T, Reflect>)
+      return PT_Reflect;
 
     llvm_unreachable("unknown type push()'ed into InterpStack");
   }
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp
index f6338cda56e34..ff1b6bada6223 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -10,6 +10,7 @@
 #include "InterpFrame.h"
 #include "InterpStack.h"
 #include "Program.h"
+#include "Reflect.h"
 #include "State.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index c2f0114c81957..891fb423a9d77 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -31,6 +31,7 @@ def Float : Type;
 def Ptr : Type;
 def MemberPtr : Type;
 def FixedPoint : Type;
+def Reflect : Type;
 
 //===----------------------------------------------------------------------===//
 // Types transferred to the interpreter.
@@ -71,6 +72,8 @@ def ArgDesc : ArgType { let Name = "const Descriptor *"; }
 def ArgPrimType : ArgType { let Name = "PrimType"; }
 def ArgEnumDecl : ArgType { let Name = "const EnumDecl *"; }
 def ArgTypePtr : ArgType { let Name = "const Type *"; }
+def ArgVoidPtr : ArgType { let Name = "const void *"; }
+def ArgReflectionKind : ArgType { let Name = "ReflectionKind"; }
 
 //===----------------------------------------------------------------------===//
 // Classes of types instructions operate on.
@@ -121,7 +124,7 @@ def NonPtrTypeClass : TypeClass {
 }
 
 def AllTypeClass : TypeClass {
-  let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types, FloatTypeClass.Types);
+  let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types, FloatTypeClass.Types, [Reflect]);
 }
 
 def ComparableTypeClass : TypeClass {
@@ -368,6 +371,10 @@ def CastMemberPtrDerivedPop : Opcode {
   let NeedsOpPC = 0;
 }
 
+def ReflectValue : Opcode {
+  let Args = [ArgReflectionKind, ArgVoidPtr];
+}
+
 def FinishInitPop : SuccessOpcode;
 def FinishInit : SuccessOpcode;
 def FinishInitActivate : SuccessOpcode;
diff --git a/clang/lib/AST/ByteCode/PrimType.cpp b/clang/lib/AST/ByteCode/PrimType.cpp
index 923233e5fb13a..0ba95bab5c6b5 100644
--- a/clang/lib/AST/ByteCode/PrimType.cpp
+++ b/clang/lib/AST/ByteCode/PrimType.cpp
@@ -14,6 +14,7 @@
 #include "IntegralAP.h"
 #include "MemberPointer.h"
 #include "Pointer.h"
+#include "Reflect.h"
 
 using namespace clang;
 using namespace clang::interp;
diff --git a/clang/lib/AST/ByteCode/PrimType.h b/clang/lib/AST/ByteCode/PrimType.h
index 8f725942fedb9..ce10ebf4835ec 100644
--- a/clang/lib/AST/ByteCode/PrimType.h
+++ b/clang/lib/AST/ByteCode/PrimType.h
@@ -26,6 +26,7 @@ class Boolean;
 class Floating;
 class MemberPointer;
 class FixedPoint;
+class Reflect;
 template <bool Signed> class IntegralAP;
 template <bool Signed> class Char;
 template <unsigned Bits, bool Signed> class Integral;
@@ -47,6 +48,7 @@ enum PrimType : uint8_t {
   PT_Float = 12,
   PT_Ptr = 13,
   PT_MemberPtr = 14,
+  PT_Reflect = 15,
 };
 
 constexpr bool isIntegerOrBoolType(PrimType T) { return T <= PT_Bool; }
@@ -193,6 +195,9 @@ template <> struct PrimConv<PT_MemberPtr> {
 template <> struct PrimConv<PT_FixedPoint> {
   using T = FixedPoint;
 };
+template <> struct PrimConv<PT_Reflect> {
+  using T = Reflect;
+};
 
 /// Returns the size of a primitive type in bytes.
 size_t primSize(PrimType Type);
@@ -238,6 +243,7 @@ static inline bool aligned(const void *P) {
       TYPE_SWITCH_CASE(PT_Ptr, B)                                              \
       TYPE_SWITCH_CASE(PT_MemberPtr, B)                                        \
       TYPE_SWITCH_CASE(PT_FixedPoint, B)                                       \
+      TYPE_SWITCH_CASE(PT_Reflect, B)                                          \
     }                                                                          \
   } while (0)
 
diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp
index 378a190184be9..5f0ca50a41f4c 100644
--- a/clang/lib/AST/ByteCode/Program.cpp
+++ b/clang/lib/AST/ByteCode/Program.cpp
@@ -12,6 +12,7 @@
 #include "Function.h"
 #include "Integral.h"
 #include "PrimType.h"
+#include "Reflect.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
diff --git a/clang/lib/AST/ByteCode/Program.h b/clang/lib/AST/ByteCode/Program.h
index b0ef993258637..764e5ee4b5c70 100644
--- a/clang/lib/AST/ByteCode/Program.h
+++ b/clang/lib/AST/ByteCode/Program.h
@@ -17,6 +17,7 @@
 #include "Pointer.h"
 #include "PrimType.h"
 #include "Record.h"
+#include "Reflect.h"
 #include "Source.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/Support/Allocator.h"
diff --git a/clang/lib/AST/ByteCode/Reflect.h b/clang/lib/AST/ByteCode/Reflect.h
new file mode 100644
index 0000000000000..dcd00a3bad782
--- /dev/null
+++ b/clang/lib/AST/ByteCode/Reflect.h
@@ -0,0 +1,60 @@
+//===--- Boolean.h - Wrapper for boolean types for the VM -------*- C++ -*-===//
+//
+// 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 LLVM_CLANG_AST_INTERP_REFLECT_H
+#define LLVM_CLANG_AST_INTERP_REFLECT_H
+
+#include "clang/AST/APValue.h"
+#include "clang/AST/ComparisonCategories.h"
+#include "clang/AST/Reflection.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstddef>
+#include <cstdint>
+
+namespace clang {
+namespace interp {
+
+class Reflect final {
+private:
+  ReflectionKind Kind;
+  const void *Operand;
+
+public:
+  Reflect() : Kind(ReflectionKind::Null), Operand(nullptr) {}
+  Reflect(ReflectionKind Kind, const void *Operand)
+      : Kind(Kind), Operand(Operand) {}
+
+  ComparisonCategoryResult compare(const Reflect &RHS) const {
+    llvm::FoldingSetNodeID LID, RID;
+    APValue(Kind, Operand).Profile(LID);
+    APValue(RHS.Kind, RHS.Operand).Profile(RID);
+
+    if (LID == RID)
+      return ComparisonCategoryResult::Equal;
+    return ComparisonCategoryResult::Unordered;
+  }
+
+  void print(llvm::raw_ostream &OS) const {
+    OS << "Reflect(" << Kind << ", " << Operand << ")";
+  }
+  APValue toAPValue(const ASTContext &Ctx) const {
+    return APValue(Kind, Operand);
+  }
+};
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Reflect &R) {
+  R.print(OS);
+  return OS;
+}
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 6c1cde6540d85..f77999d8bada9 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -24,6 +24,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/LambdaCapture.h"
 #include "clang/AST/NestedNameSpecifier.h"
+#include "clang/AST/Reflection.h"
 #include "clang/AST/TemplateBase.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
@@ -1934,15 +1935,15 @@ TypeTraitExpr *TypeTraitExpr::CreateDeserialized(const ASTContext &C,
 CXXReflectExpr::CXXReflectExpr(EmptyShell Empty)
     : Expr(CXXReflectExprClass, Empty) {}
 
-CXXReflectExpr::CXXReflectExpr(SourceLocation CaretCaretLoc,
-                               const TypeSourceInfo *TSI)
-    : Expr(CXXReflectExprClass, TSI->getType(), VK_PRValue, OK_Ordinary),
-      CaretCaretLoc(CaretCaretLoc), Operand(TSI) {}
+CXXReflectExpr::CXXReflectExpr(ASTContext &C, SourceLocation CaretCaretLoc,
+                               TypeSourceInfo *TSI)
+    : Expr(CXXReflectExprClass, C.MetaInfoTy, VK_PRValue, OK_Ordinary),
+      CaretCaretLoc(CaretCaretLoc), Kind(ReflectionKind::Type), Operand(TSI) {}
 
 CXXReflectExpr *CXXReflectExpr::Create(ASTContext &C,
                                        SourceLocation CaretCaretLoc,
                                        TypeSourceInfo *TSI) {
-  return new (C) CXXReflectExpr(CaretCaretLoc, TSI);
+  return new (C) CXXReflectExpr(C, CaretCaretLoc, TSI);
 }
 
 CXXReflectExpr *CXXReflectExpr::CreateEmpty(ASTContext &C) {
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 574dd8b04e779..30086822583d8 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -48,6 +48,7 @@
 #include "clang/AST/OSLog.h"
 #include "clang/AST/OptionalDiagnostic.h"
 #include "clang/AST/RecordLayout.h"
+#include "clang/AST/Reflection.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
@@ -2644,6 +2645,7 @@ static bool HandleConversionToBool(const APValue &Val, bool &Result) {
   case APValue::Struct:
   case APValue::Union:
   case APValue::AddrLabelDiff:
+  case APValue::Reflection:
     return false;
   }
 
@@ -7984,7 +7986,8 @@ class APValueToBufferConverter {
     case APValue::Matrix:
     case APValue::Union:
     case APValue::MemberPointer:
-    case APValue::AddrLabelDiff: {
+    case APValue::AddrLabelDiff:
+    case APValue::Reflection: {
       Info.FFDiag(BCE->getBeginLoc(),
                   diag::note_constexpr_bit_cast_unsupported_type)
           << Ty;
@@ -11220,6 +11223,57 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
 
   return true;
 }
+
+//===----------------------------------------------------------------------===//
+// Reflection expression evaluation
+//===----------------------------------------------------------------------===//
+
+namespace {
+class ReflectionEvaluator : public ExprEvaluatorBase<ReflectionEvaluator> {
+
+  using BaseType = ExprEvaluatorBase<ReflectionEvaluator>;
+
+  APValue &Result;
+
+public:
+  ReflectionEvaluator(EvalInfo &E, APValue &Result)
+      : ExprEvaluatorBaseTy(E), Result(Result) {}
+
+  bool Success(const APValue &V, const Expr *E) {
+    Result = V;
+    return true;
+  }
+
+  bool VisitCXXReflectExpr(const CXXReflectExpr *E);
+  bool ZeroInitialization(const Expr *E);
+};
+
+bool ReflectionEvaluator::VisitCXXReflectExpr(const CXXReflectExpr *E) {
+  switch (E->getKind()) {
+  case ReflectionKind::Null: {
+    APValue Result(ReflectionKind::Null, /*Operand=*/nullptr);
+    return Success(Result, E);
+  }
+  case ReflectionKind::Type: {
+    APValue Result(ReflectionKind::Type, E->getOpaqueValue());
+    return Success(Result, E);
+  }
+  }
+  llvm_unreachable("invalid reflection");
+}
+
+bool ReflectionEvaluator::ZeroInitialization(const Expr *E) {
+  Result = APValue(ReflectionKind::Null, /*Operand=*/nullptr);
+  return true;
+}
+
+} // end anonymous namespace
+
+static bool EvaluateReflection(const Expr *E, APValue &Result, EvalInfo &Info) {
+  assert(E->isPRValue() && E->getType()->isMetaInfoType());
+  return ReflectionEvaluator(Info, Result).Visit(E);
+}
+
 //===----------------------------------------------------------------------===//
 // Member Pointer Evaluation
 //===----------------------------------------------------------------------===//
@@ -16319,6 +16373,7 @@ GCCTypeClass EvaluateBuiltinClassifyType(QualType T,
 #include "clang/Basic/AMDGPUTypes.def"
 #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
 #include "clang/Basic/HLSLIntangibleTypes.def"
+    case BuiltinType::MetaInfo:
       return GCCTypeClass::None;
 
     case BuiltinType::Dependent:
@@ -19431,6 +19486,23 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
     return Success(CmpResult::Equal, E);
   }
 
+  if (LHSTy->isMetaInfoType() && RHSTy->isMetaInfoType()) {
+    APValue LHSValue, RHSValue;
+    llvm::FoldingSetNodeID LID, RID;
+    if (!Evaluate(LHSValue, Info, E->getLHS()))
+      return false;
+    LHSValue.Profile(LID);
+
+    if (!Evaluate(RHSValue, Info, E->getRHS()))
+      return false;
+    RHSValue.Profile(RID);
+
+    if (LID == RID)
+      return Success(CmpResult::Equal, E);
+    else
+      return Success(CmpResult::Unequal, E);
+  }
+
   return DoAfter();
 }
 
@@ -21607,6 +21679,9 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
   } else if (T->isIntegralOrEnumerationType()) {
     if (!IntExprEvaluator(Info, Result).Visit(E))
       return false;
+  } else if (T->isMetaInfoType()) {
+    if (!EvaluateReflection(E, Result, Info))
+      return false;
   } else if (T->hasPointerRepresentation()) {
     LValue LV;
     if (!EvaluatePointer(E, LV, Info))
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 172a19f8f3537..e649369b61e04 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -575,6 +575,7 @@ class CXXNameMangler {
   void mangleFloatLiteral(QualType T, const llvm::APFloat &V);
   void mangleFixedPointLiteral();
   void mangleNullPointer(QualType T);
+  void mangleReflection(ReflectionKind Kind, const void *OpaqueOperand);
 
   void mangleMemberExprBase(const Expr *base, bool isArrow);
   void mangleMemberExpr(const Expr *base, bool isArrow,
@@ -1273,6 +1274,56 @@ void CXXNameMangler::mangleNullPointer(QualType T) {
   Out << "0E";
 }
 
+void CXXNameMangler::mangleReflection(ReflectionKind Kind,
+                                      const void *OpaqueOperand) {
+  // https://github.com/itanium-cxx-abi/cxx-abi/issues/208
+  // TODO(Reflection): add support for remaining items in the grammar below
+
+  // <reflection> ::= nu                                 # null reflection
+  //              ::= vl <expression>                    # value
+  //              ::= ob <expression>                    # object
+  //              ::= vr <variable name>                 # variable
+  //              ::= sb <sb name>                       # structured binding
+  //              ::= fn <function encoding>             # function
+  //              ::= pa [ <nonnegative number> ] _ <encoding>  # function
+  //                                                              parameter
+  //              ::= en <prefix> <unqualified-name>     # enumerator
+  //              ::= an [ <nonnegative number> ] _      # annotation
+  //              ::= ta <alias prefix>                  # type alias
+  //              ::= ty <type>                          # type
+  //              ::= dm <prefix> <unqualified-name>     # non-static data
+  //                                                       member
+  //              ::= un <prefix> [ <nonnegative number> ] _ # unnamed bit-field
+  //              ::= ct [ <prefix> ] <unqualified-name> # class template
+  //              ::= ft [ <prefix> ] <unqualified-name> # function template
+  //              ::= vt [ <prefix> ] <unqualified-name> # variable template
+  //              ::= at [ <prefix> ] <unqualified-name> # alias template
+  //              ::= co [ <prefix> ] <unqualified-name> # concept
+  //              ::= na [ <prefix> ] <unqualified-name> # namespace alias
+  //              ::= ns [ <prefix> ] <unqualified-name> # namespace
+  //              ::= ng                                 # ^^::
+  //              ::= ba [ <nonnegative number> ] _ <type> # direct base class
+  //                                                         relationship
+  //              ::= ds <type> _ [ <unqualified-name> ] _
+  //                  [ <alignment number> ] _ [ <bit-width number> ] _
+  //                  [ n ]                              # data member
+  //                  description
+
+  Out << "LDm";
+  switch (Kind) {
+  case ReflectionKind::Null:
+    Out << "nu";
+    break;
+  case ReflectionKind::Type: {
+    const auto *TSI = static_cast<const TypeSourceInfo *>(OpaqueOperand);
+    Out << "ty";
+    mangleType(TSI->getType());
+    break;
+  }
+  }
+  Out << 'E';
+}
+
 void CXXNameMangler::mangleNumber(const llvm::APSInt &Value) {
   if (Value.isSigned() && Value.isNegative()) {
     Out << 'n';
@@ -3148,10 +3199,12 @@ void CXXNameMangler::mangleType(const BuiltinType *T) {
   // UNSUPPORTED:    ::= De # IEEE 754r decimal floating point (128 bits)
   // UNSUPPORTED:    ::= Df # IEEE 754r decimal floating point (32 bits)
   //                 ::= Dh # IEEE 754r half-precision floating point (16 bits)
-  //                 ::= DF <number> _ # ISO/IEC TS 18661 binary floating point type _FloatN (N bits);
+  //                 ::= DF <number> _ # ISO/IEC TS 18661 binary floating point
+  //                 type _FloatN (N bits);
   //                 ::= Di # char32_t
   //                 ::= Ds # char16_t
   //                 ::= Dn # std::nullptr_t (i.e., decltype(nullptr))
+  //                 ::= Dm # std::meta::info (i.e., decltype(^^int))
   //                 ::= [DS] DA  # N1169 fixed-point [_Sat] T _Accum
   //                 ::= [DS] DR  # N1169 fixed-point [_Sat] T _Fract
   //                 ::= u <source-name>    # vendor extended type
@@ -3420,6 +3473,10 @@ void CXXNameMangler::mangleType(const BuiltinType *T) {
     Out << TI->getIbm128Mangling();
     break;
   }
+  case BuiltinType::MetaInfo:
+    // https://github.com/itanium-cxx-abi/cxx-abi/issues/208
+    Out << "Dm";
+    break;
   case BuiltinType::NullPtr:
     Out << "Dn";
     break;
@@ -4936,6 +4993,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
   //                ::= L <pointer type> 0 E         # null pointer template argument
   //                ::= L <type> <real-part float> _ <imag-part float> E    # complex floating point literal (C99); not used by clang
   //                ::= L <mangled-name> E           # external name
+  //                ::= LDm <reflection> E           # C++26 reflection value
   // clang-format on
   QualType ImplicitlyConvertedToType;
 
@@ -5015,8 +5073,8 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
     goto recurse;
 
   case Expr::CXXReflectExprClass: {
-    // TODO(Reflection): implement this after introducing std::meta::info
-    assert(false && "unimplemented");
+    const CXXReflectExpr *RE = cast<CXXReflectExpr>(E);
+    mangleReflection(RE->getKind(), RE->getOpaqueValue());
     break;
   }
 
@@ -6554,6 +6612,9 @@ static bool isZeroInitialized(QualType T, const APValue &V) {
 
   case APValue::MemberPointer:
     return !V.getMemberPointerDecl();
+
+  case APValue::Reflection:
+    return !V.getReflectionOpaqueOperand();
   }
 
   llvm_unreachable("Unhandled APValue::ValueKind enum");
@@ -6950,6 +7011,12 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V,
     break;
   }
 
+  case APValue::Reflection: {
+    mangleReflection(V.getReflectionOperandKind(),
+                     V.getReflectionOpaqueOperand());
+    break;
+  }
+
   case APValue::MemberPointer:
     // Proposed in https://github.com/itanium-cxx-abi/cxx-abi/issues/47.
     if (!V.getMemberPointerDecl()) {
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index 59732ff0fed11..06abe01553b43 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -2168,6 +2168,11 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T,
     Error("template argument (value type: fixed point)");
     return;
   }
+
+  case APValue::Reflection: {
+    Error("template argument (value type: reflection)");
+    return;
+  }
   }
 }
 
diff --git a/clang/lib/AST/NSAPI.cpp b/clang/lib/AST/NSAPI.cpp
index 17f5ee5dee3d1..70d56c96f5bdf 100644
--- a/clang/lib/AST/NSAPI.cpp
+++ b/clang/lib/AST/NSAPI.cpp
@@ -471,6 +471,7 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const {
   case BuiltinType::OMPArrayShaping:
   case BuiltinType::OMPIterator:
   case BuiltinType::BFloat16:
+  case BuiltinType::MetaInfo:
     break;
   }
 
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 9a97d52fd3020..6647b5975e7d0 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2635,8 +2635,15 @@ void StmtPrinter::VisitCXXUnresolvedConstructExpr(
 }
 
 void StmtPrinter::VisitCXXReflectExpr(CXXReflectExpr *S) {
-  // TODO(Reflection): Implement this.
-  assert(false && "not implemented yet");
+  // TODO(Reflection): add support for the remaining reflection kinds.
+  OS << "^^";
+  switch (S->getKind()) {
+  case ReflectionKind::Null:
+    break;
+  case ReflectionKind::Type:
+    S->getTypeSourceInfo()->getType().print(OS, Policy);
+    break;
+  }
 }
 
 void StmtPrinter::VisitCXXDependentScopeMemberExpr(
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 5d724e9d1d91e..67b268af2f4c8 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -616,6 +616,7 @@ static bool isSimpleAPValue(const APValue &Value) {
   case APValue::LValue:
   case APValue::MemberPointer:
   case APValue::AddrLabelDiff:
+  case APValue::Reflection:
     return true;
   case APValue::Vector:
   case APValue::Array:
@@ -875,6 +876,21 @@ void TextNodeDumper::Visit(const APValue &Value, QualType Ty) {
     OS << " - ";
     OS << "&&" << Value.getAddrLabelDiffRHS()->getLabel()->getName();
     return;
+  case APValue::Reflection:
+    OS << "Reflection ";
+    switch (Value.getReflectionOperandKind()) {
+    case ReflectionKind::Null:
+      OS << "std::meta::info{}";
+      break;
+    case ReflectionKind::Type: {
+      OS << "^^";
+      const TypeSourceInfo *TSI = static_cast<const TypeSourceInfo *>(
+          Value.getReflectionOpaqueOperand());
+      TSI->getType().print(OS, PrintPolicy);
+      break;
+    }
+    }
+    return;
   }
   llvm_unreachable("Unknown APValue kind!");
 }
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 42d148715bc40..2f94b4664785e 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3171,6 +3171,13 @@ bool Type::isLiteralType(const ASTContext &Ctx) const {
     return true;
   }
 
+  // C++26 [basic.types]p9:
+  // -- std::meta::info is a scalar type
+  // C++26 [basic.types]p10:
+  // -- a scalar type is a literal type
+  if (isMetaInfoType())
+    return true;
+
   // We treat _Atomic T as a literal type if T is a literal type.
   if (const auto *AT = BaseTy->getAs<AtomicType>())
     return AT->getValueType()->isLiteralType(Ctx);
@@ -3546,6 +3553,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const {
     return "unsigned _Accum";
   case ULongAccum:
     return "unsigned long _Accum";
+  case BuiltinType::MetaInfo:
+    return "std::meta::info";
   case BuiltinType::ShortFract:
     return "short _Fract";
   case BuiltinType::Fract:
@@ -5260,6 +5269,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
 #include "clang/Basic/HLSLIntangibleTypes.def"
     case BuiltinType::BuiltinFn:
     case BuiltinType::NullPtr:
+    case BuiltinType::MetaInfo:
     case BuiltinType::IncompleteMatrixIdx:
     case BuiltinType::ArraySection:
     case BuiltinType::OMPArrayShaping:
diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp
index 7e72a85136966..e8c018eb92e23 100644
--- a/clang/lib/AST/TypeLoc.cpp
+++ b/clang/lib/AST/TypeLoc.cpp
@@ -426,6 +426,7 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const {
   case BuiltinType::ArraySection:
   case BuiltinType::OMPArrayShaping:
   case BuiltinType::OMPIterator:
+  case BuiltinType::MetaInfo:
     return TST_unspecified;
   }
 
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index 46b5bdecb9c40..e429f4a76e564 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -126,6 +126,8 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) {
   LongDoubleAlign = 64;
   Float128Align = 128;
   Ibm128Align = 128;
+  MetaInfoWidth = 64;
+  MetaInfoAlign = 8;
   LargeArrayMinWidth = 0;
   LargeArrayAlign = 0;
   MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 0;
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 7421733efcc24..42ad4673b792b 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1216,6 +1216,8 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) {
   case BuiltinType::SatULongFract:
     Encoding = llvm::dwarf::DW_ATE_unsigned_fixed;
     break;
+  case BuiltinType::MetaInfo:
+    llvm_unreachable("std::meta::info is consteval-only type");
   }
 
   BTName = BT->getName(CGM.getLangOpts());
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 257de0145855b..f865022259978 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2752,6 +2752,8 @@ ConstantEmitter::tryEmitPrivate(const APValue &Value, QualType DestType,
   }
   case APValue::MemberPointer:
     return CGM.getCXXABI().EmitMemberPointer(Value, DestType);
+  case APValue::Reflection:
+    llvm_unreachable("std::meta::info is consteval-only type");
   }
   llvm_unreachable("Unknown APValue kind");
 }
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index d1119e6c00d14..da4f27b82e475 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6366,6 +6366,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
   if (getLangOpts().OpenCL && ASTTy->isSamplerT())
     return;
 
+  // TODO(Reflection): add support for consteval-only types.
+
   // HLSL default buffer constants will be emitted during HLSLBufferDecl codegen
   if (getLangOpts().HLSL &&
       D->getType().getAddressSpace() == LangAS::hlsl_constant)
diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp
index 55fe216580314..f559ac76f7984 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -487,6 +487,13 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
       ResultType = llvm::PointerType::getUnqual(getLLVMContext());
       break;
 
+    case BuiltinType::MetaInfo:
+      // FIXME(Reflection): once consteval-only types are supported,
+      // make this an llvm_unreachable instead because reflection
+      // should not reach here
+      ResultType = llvm::IntegerType::get(getLLVMContext(), 64);
+      break;
+
     case BuiltinType::UInt128:
     case BuiltinType::Int128:
       ResultType = llvm::IntegerType::get(getLLVMContext(), 128);
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index b17478cb7ffde..80696c846c816 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -3813,6 +3813,7 @@ static bool TypeInfoIsInStandardLibrary(const BuiltinType *Ty) {
     case BuiltinType::SatUFract:
     case BuiltinType::SatULongFract:
     case BuiltinType::BFloat16:
+    case BuiltinType::MetaInfo:
       return false;
 
     case BuiltinType::Dependent:
diff --git a/clang/lib/CodeGen/QualTypeMapper.cpp b/clang/lib/CodeGen/QualTypeMapper.cpp
index 31d9250a48ec9..f41e1fa6c9763 100644
--- a/clang/lib/CodeGen/QualTypeMapper.cpp
+++ b/clang/lib/CodeGen/QualTypeMapper.cpp
@@ -156,6 +156,9 @@ QualTypeMapper::convertBuiltinType(const BuiltinType *BT) {
     return Builder.getIntegerType(1, getTypeAlign(QT), /*Signed=*/false,
                                   /*IsBitInt=*/false);
 
+  case BuiltinType::MetaInfo:
+    llvm::reportFatalInternalError("std::meta::info is consteval-only type");
+
   case BuiltinType::Char_S:
   case BuiltinType::Char_U:
   case BuiltinType::SChar:
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b844670543a55..5f0a275c67756 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -13040,6 +13040,13 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
         *CCT, Loc, ComparisonCategoryUsage::OperatorInExpression);
   };
 
+  if (LHSType->isMetaInfoType() && RHSType->isMetaInfoType()) {
+    if (!BinaryOperator::isEqualityOp(Opc)) {
+      return InvalidOperands(Loc, LHS, RHS);
+    }
+    return computeResultTy();
+  }
+
   if (!IsOrdered && LHSIsNull != RHSIsNull) {
     bool IsEquality = Opc == BO_EQ;
     if (RHSIsNull)
@@ -16392,7 +16399,11 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
                          << resultType << Input.get()->getSourceRange());
       }
 
-      if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) {
+      if (resultType->isScalarType() && !isScopedEnumerationType(resultType) &&
+          !resultType->isMetaInfoType()) {
+        // before C++26, scalar types are contextually converted to bool,
+        // std::meta::info is a scalar type but not an arithmetic type.
+
         // C99 6.5.3.3p1: ok, fallthrough;
         if (Context.getLangOpts().CPlusPlus) {
           // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9:
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index eafda32198f11..a2d32e7406982 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -9108,6 +9108,10 @@ class BuiltinCandidateTypeSet  {
   /// candidate set.
   bool HasNullPtrType;
 
+  /// A flag indicating whether the reflection type was present in the
+  /// candidate set.
+  bool HasReflectionType;
+
   /// Sema - The semantic analysis instance where we are building the
   /// candidate type set.
   Sema &SemaRef;
@@ -9124,11 +9128,9 @@ class BuiltinCandidateTypeSet  {
   typedef TypeSet::iterator iterator;
 
   BuiltinCandidateTypeSet(Sema &SemaRef)
-    : HasNonRecordTypes(false),
-      HasArithmeticOrEnumeralTypes(false),
-      HasNullPtrType(false),
-      SemaRef(SemaRef),
-      Context(SemaRef.Context) { }
+      : HasNonRecordTypes(false), HasArithmeticOrEnumeralTypes(false),
+        HasNullPtrType(false), HasReflectionType(false), SemaRef(SemaRef),
+        Context(SemaRef.Context) {}
 
   void AddTypesConvertedFrom(QualType Ty,
                              SourceLocation Loc,
@@ -9151,6 +9153,7 @@ class BuiltinCandidateTypeSet  {
   bool hasNonRecordTypes() { return HasNonRecordTypes; }
   bool hasArithmeticOrEnumeralTypes() { return HasArithmeticOrEnumeralTypes; }
   bool hasNullPtrType() const { return HasNullPtrType; }
+  bool hasReflectionType() const { return HasReflectionType; }
 };
 
 } // end anonymous namespace
@@ -9332,6 +9335,8 @@ BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty,
     MatrixTypes.insert(Ty);
   } else if (Ty->isNullPtrType()) {
     HasNullPtrType = true;
+  } else if (Ty->isMetaInfoType()) {
+    HasReflectionType = true;
   } else if (AllowUserConversions && TyIsRec) {
     // No conversion functions in incomplete types.
     if (!SemaRef.isCompleteType(Loc, Ty))
@@ -9811,6 +9816,14 @@ class BuiltinOperatorOverloadBuilder {
           S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet);
         }
       }
+
+      if (CandidateTypes[ArgIdx].hasReflectionType()) {
+        CanQualType InfoTy = S.Context.getCanonicalType(S.Context.MetaInfoTy);
+        if (AddedTypes.insert(InfoTy).second) {
+          QualType ParamTypes[2] = {InfoTy, InfoTy};
+          S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet);
+        }
+      }
     }
   }
 
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 0d1e01df75d94..d9a8b2fea89fe 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -8111,6 +8111,18 @@ static Expr *BuildExpressionFromIntegralTemplateArgumentValue(
   return E;
 }
 
+/// Construct a new reflect expression that refers to the given
+/// entity with the given source-location of the reflection operator.
+static ExprResult BuildExpressionFromReflection(Sema &S, const APValue &RV,
+                                                SourceLocation CaretCaretLoc) {
+  // TODO(Reflection): Add support for NamespaceReference, TemplateReference,
+  // and DeclRefExpr.
+  return CXXReflectExpr::Create(
+      S.Context, CaretCaretLoc,
+      static_cast<TypeSourceInfo *>(
+          const_cast<void *>(RV.getReflectionOpaqueOperand())));
+}
+
 static Expr *BuildExpressionFromNonTypeTemplateArgumentValue(
     Sema &S, QualType T, const APValue &Val, SourceLocation Loc) {
   auto MakeInitList = [&](ArrayRef<Expr *> Elts) -> Expr * {
@@ -8176,7 +8188,7 @@ static Expr *BuildExpressionFromNonTypeTemplateArgumentValue(
   case APValue::Indeterminate:
     llvm_unreachable("Unexpected APValue kind.");
   case APValue::LValue:
-  case APValue::MemberPointer:
+  case APValue::MemberPointer: {
     // There isn't necessarily a valid equivalent source-level syntax for
     // these; in particular, a naive lowering might violate access control.
     // So for now we lower to a ConstantExpr holding the value, wrapped around
@@ -8190,6 +8202,9 @@ static Expr *BuildExpressionFromNonTypeTemplateArgumentValue(
     auto *OVE = new (S.Context) OpaqueValueExpr(Loc, T, VK);
     return ConstantExpr::Create(S.Context, OVE, Val);
   }
+  case APValue::Reflection:
+    return BuildExpressionFromReflection(S, Val, Loc).get();
+  }
   llvm_unreachable("Unhandled APValue::ValueKind enum");
 }
 
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index cc126f9000717..a32fccfc55ff4 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -13395,7 +13395,19 @@ StmtResult TreeTransform<Derived>::TransformUnresolvedSYCLKernelCallStmt(
 template <typename Derived>
 ExprResult TreeTransform<Derived>::TransformCXXReflectExpr(CXXReflectExpr *E) {
   // TODO(reflection): Implement its transform
-  assert(false && "not implemented yet");
+
+  switch (E->getKind()) {
+  case ReflectionKind::Type: {
+    TypeSourceInfo *NewT = getDerived().TransformType(E->getTypeSourceInfo());
+    if (!NewT)
+      return ExprError();
+    return SemaRef.BuildCXXReflectExpr(E->getOperatorLoc(), NewT);
+  }
+  case ReflectionKind::Null:
+    llvm_unreachable("A null reflection should not reach here");
+  }
+
+  assert(false && "unknown or unimplemented reflection entities");
   return ExprError();
 }
 
diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp
index 74265d5fce908..0dae1d6a35c8b 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -286,6 +286,9 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) {
   case BuiltinType::BFloat16:
     ID = PREDEF_TYPE_BFLOAT16_ID;
     break;
+  case BuiltinType::MetaInfo:
+    ID = PREDEF_TYPE_META_INFO_ID;
+    break;
   }
 
   return TypeIdx(0, ID);
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index a018a378e9f44..9e690dadd25b9 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -8058,6 +8058,9 @@ QualType ASTReader::GetType(TypeID ID) {
     case PREDEF_TYPE_NULLPTR_ID:
       T = Context.NullPtrTy;
       break;
+    case PREDEF_TYPE_META_INFO_ID:
+      T = Context.MetaInfoTy;
+      break;
     case PREDEF_TYPE_CHAR8_ID:
       T = Context.Char8Ty;
       break;
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 87cec16a76323..4ded925d08a38 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -540,8 +540,19 @@ void ASTStmtReader::VisitCapturedStmt(CapturedStmt *S) {
 }
 
 void ASTStmtReader::VisitCXXReflectExpr(CXXReflectExpr *E) {
-  // TODO(Reflection): Implement this.
-  assert(false && "not implemented yet");
+  // TODO(Reflection): add support for TemplateReference, NamespaceReference and
+  // DeclRefExpr
+  VisitExpr(E);
+  E->CaretCaretLoc = readSourceLocation();
+  E->Kind = static_cast<ReflectionKind>(Record.readInt());
+  switch (E->Kind) {
+  case ReflectionKind::Null:
+    E->Operand = nullptr;
+    break;
+  case ReflectionKind::Type:
+    E->Operand = Record.readTypeSourceInfo();
+    break;
+  }
 }
 
 void ASTStmtReader::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *S) {
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 70477f4cf4001..476ffe8726c66 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -475,8 +475,19 @@ void ASTStmtWriter::VisitCoyieldExpr(CoyieldExpr *E) {
 }
 
 void ASTStmtWriter::VisitCXXReflectExpr(CXXReflectExpr *E) {
-  // TODO(Reflection): Implement this.
-  assert(false && "not implemented yet");
+  // TODO(Reflection): add support for TemplateReference, NamespaceReference and
+  // DeclRefExpr
+  VisitExpr(E);
+  Record.AddSourceLocation(E->getOperatorLoc());
+  Record.push_back(static_cast<uint64_t>(E->getKind()));
+  switch (E->getKind()) {
+  case ReflectionKind::Null:
+    break;
+  case ReflectionKind::Type:
+    Record.AddTypeSourceInfo(E->getTypeSourceInfo());
+    break;
+  }
+  Code = serialization::EXPR_REFLECT;
 }
 
 void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) {
diff --git a/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp b/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp
index 5f689ad32159b..99901e9601ac0 100644
--- a/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp
+++ b/clang/lib/UnifiedSymbolResolution/USRGeneration.cpp
@@ -805,6 +805,9 @@ void USRGenerator::VisitType(QualType T) {
       case BuiltinType::NullPtr:
         Out << 'n';
         break;
+      case BuiltinType::MetaInfo:
+        Out << "@BT at MetaInfo";
+        break;
 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix)                   \
   case BuiltinType::Id:                                                        \
     Out << "@BT@" << #Suffix << "_" << #ImgType;                               \
diff --git a/clang/test/AST/ast-dump-APValue-reflection.cpp b/clang/test/AST/ast-dump-APValue-reflection.cpp
new file mode 100644
index 0000000000000..750cdf94da7dc
--- /dev/null
+++ b/clang/test/AST/ast-dump-APValue-reflection.cpp
@@ -0,0 +1,14 @@
+// Test without serialization:
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++26 -freflection \
+// RUN:            -ast-dump %s -ast-dump-filter Test \
+// RUN: | FileCheck --strict-whitespace %s
+
+void TestReflection() {
+  constexpr auto x = ^^int;
+  // CHECK:  | `-VarDecl {{.*}} x 'const std::meta::info' constexpr cinit
+  // CHECK-NEXT:  |   |-value: Reflection ^^int
+
+  constexpr decltype(^^int) y{};
+  // CHECK:    `-VarDecl {{.*}} y {{.*}} constexpr listinit
+  // CHECK-NEXT:        |-value: Reflection std::meta::info{}
+}
diff --git a/clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp b/clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp
new file mode 100644
index 0000000000000..a6266165e77f8
--- /dev/null
+++ b/clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -std=c++26 -freflection -triple x86_64-unknown-linux-gnu \
+// RUN:   -emit-llvm -o - %s -verify
+
+int main() {
+  (void)(^^int); // expected-error {{cannot compile this scalar expression yet}}
+  return 0;
+}
diff --git a/clang/test/CodeGenCXX/reflection-mangle-ms.cpp b/clang/test/CodeGenCXX/reflection-emit-meta-info-ms.cpp
similarity index 100%
rename from clang/test/CodeGenCXX/reflection-mangle-ms.cpp
rename to clang/test/CodeGenCXX/reflection-emit-meta-info-ms.cpp
diff --git a/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp b/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp
index a6266165e77f8..b7c18b721f976 100644
--- a/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp
+++ b/clang/test/CodeGenCXX/reflection-mangle-itanium.cpp
@@ -1,7 +1,45 @@
 // RUN: %clang_cc1 -std=c++26 -freflection -triple x86_64-unknown-linux-gnu \
-// RUN:   -emit-llvm -o - %s -verify
+// RUN:   -emit-llvm -o - %s | FileCheck %s
+
+using info = decltype(^^int);
+
+template <auto A>
+void foo () {}
 
 int main() {
-  (void)(^^int); // expected-error {{cannot compile this scalar expression yet}}
+  foo <info {}> ();
+  // CHECK: @_Z3fooITnDaLDmnuEEvv
+  foo <^^void> ();
+  // CHECK: @_Z3fooITnDaLDmtyvEEvv
+  foo <^^bool> ();
+  // CHECK: @_Z3fooITnDaLDmtybEEvv
+  foo <^^char> ();
+  // CHECK: @_Z3fooITnDaLDmtycEEvv
+  foo <^^signed char> ();
+  // CHECK: @_Z3fooITnDaLDmtyaEEvv
+  foo <^^unsigned char> ();
+  // CHECK: @_Z3fooITnDaLDmtyhEEvv
+  foo <^^short> ();
+  // CHECK: @_Z3fooITnDaLDmtysEEvv
+  foo <^^unsigned short> ();
+  // CHECK: @_Z3fooITnDaLDmtytEEvv
+  foo <^^int> ();
+  // CHECK: @_Z3fooITnDaLDmtyiEEvv
+  foo <^^unsigned int> ();
+  // CHECK: @_Z3fooITnDaLDmtyjEEvv
+  foo <^^long> ();
+  // CHECK: @_Z3fooITnDaLDmtylEEvv
+  foo <^^unsigned long> ();
+  // CHECK: @_Z3fooITnDaLDmtymEEvv
+  foo <^^long long> ();
+  // CHECK: @_Z3fooITnDaLDmtyxEEvv
+  foo <^^unsigned long long> ();
+  // CHECK: @_Z3fooITnDaLDmtyyEEvv
+  foo <^^float> ();
+  // CHECK: @_Z3fooITnDaLDmtyfEEvv
+  foo <^^double> ();
+  // CHECK: @_Z3fooITnDaLDmtydEEvv
+  foo <^^long double> ();
+  // CHECK: @_Z3fooITnDaLDmtyeEEvv
   return 0;
 }
diff --git a/clang/test/PCH/reflection.cpp b/clang/test/PCH/reflection.cpp
new file mode 100644
index 0000000000000..73e98ea68c7ba
--- /dev/null
+++ b/clang/test/PCH/reflection.cpp
@@ -0,0 +1,14 @@
+// Test without PCH
+// RUN: %clang_cc1 -std=c++26 -freflection %s -include %S/reflection_include.h -fsyntax-only -verify
+
+// RUN: %clang_cc1 -std=c++26 -freflection -x c++-header %S/reflection_include.h -emit-pch -o %t
+// RUN: %clang_cc1 -std=c++26 -freflection -include-pch %t -verify %s
+
+// expected-no-diagnostics
+
+
+static_assert(^^int == ^^int);
+static_assert(^^int != ^^double);
+static_assert(info{} != ^^int);
+static_assert(__is_same(decltype(^^int), info));
+static_assert(__is_same(decltype(^^double), info));
diff --git a/clang/test/PCH/reflection_include.h b/clang/test/PCH/reflection_include.h
new file mode 100644
index 0000000000000..782aff5bbd3fe
--- /dev/null
+++ b/clang/test/PCH/reflection_include.h
@@ -0,0 +1 @@
+using info = decltype(^^int);
diff --git a/clang/test/Sema/reflection-meta-info.fail.cpp b/clang/test/Sema/reflection-meta-info.fail.cpp
new file mode 100644
index 0000000000000..e4332990f5e29
--- /dev/null
+++ b/clang/test/Sema/reflection-meta-info.fail.cpp
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 %s -std=c++26 -freflection -fsyntax-only -verify
+// RUN: %clang_cc1 %s -std=c++26 -freflection -fexperimental-new-constant-interpreter -fsyntax-only -verify
+
+using info = decltype(^^int);
+
+struct X { int a; }; // expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'std::meta::info' to 'const X' for 1st argument}} \
+                     // expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'std::meta::info' to 'X' for 1st argument}} \
+                     // expected-note {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}}
+template <typename T = X, auto ptm = &X::a>
+constexpr auto ptmOp = ((T)(^^int)).*ptm; // expected-error {{no matching conversion for C-style cast from 'std::meta::info' to 'X'}}
+constexpr auto var = ptmOp<>; // expected-note {{in instantiation of variable template specialization 'ptmOp' requested here}}
+
+static_assert(^^int == ^^float); // expected-error {{static assertion failed due to requirement '^^int == ^^float'}}
+static_assert(info{} == ^^int); // expected-error {{static assertion failed due to requirement 'std::meta::info{} == ^^int'}}
+
+consteval void test()
+{
+    (^^char)++; // expected-error {{cannot increment value of type 'std::meta::info'}}
+    (^^short)++; // expected-error {{cannot increment value of type 'std::meta::info'}}
+    (^^int)++; // expected-error {{cannot increment value of type 'std::meta::info'}}
+    (^^char)--; // expected-error {{cannot decrement value of type 'std::meta::info'}}
+    (^^short)--; // expected-error {{cannot decrement value of type 'std::meta::info'}}
+    (^^int)--; // expected-error {{cannot decrement value of type 'std::meta::info'}}
+
+    ++(^^char); // expected-error {{cannot increment value of type 'std::meta::info'}}
+    ++(^^short); // expected-error {{cannot increment value of type 'std::meta::info'}}
+    ++(^^int); // expected-error {{cannot increment value of type 'std::meta::info'}}
+    --(^^char); // expected-error {{cannot decrement value of type 'std::meta::info'}}
+    --(^^short); // expected-error {{cannot decrement value of type 'std::meta::info'}}
+    --(^^int); // expected-error {{cannot decrement value of type 'std::meta::info'}}
+
+    ~(^^int);  // expected-error {{invalid argument type 'std::meta::info' to unary expression}}
+    !(^^float);  // expected-error {{invalid argument type 'std::meta::info' to unary expression}}
+
+    (^^int) + (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^double) - (^^float); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^char) * (^^short); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^long) / (^^long); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+
+    (^^int) & (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) | (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) ^ (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+
+    (^^int) < (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) > (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) <= (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) >= (^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) <=>(^^int); // expected-error {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+
+    // expected-error at +2 {{value of type 'std::meta::info' is not contextually convertible to 'bool'}}
+    // expected-error at +1 {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) && (^^int);
+    // expected-error at +2 {{value of type 'std::meta::info' is not contextually convertible to 'bool'}}
+    // expected-error at +1 {{invalid operands to binary expression ('std::meta::info' and 'std::meta::info')}}
+    (^^int) || (^^int);
+}
diff --git a/clang/test/Sema/reflection-meta-info.pass.cpp b/clang/test/Sema/reflection-meta-info.pass.cpp
new file mode 100644
index 0000000000000..46b2420af43f6
--- /dev/null
+++ b/clang/test/Sema/reflection-meta-info.pass.cpp
@@ -0,0 +1,77 @@
+// RUN: %clang_cc1 %s -std=c++26 -freflection -fsyntax-only
+// RUN: %clang_cc1 %s -std=c++26 -freflection -fexperimental-new-constant-interpreter -fsyntax-only
+
+typedef int int32_t;
+using A = int;
+
+using info = decltype(^^int);
+
+template <auto R>
+consteval auto f1() {
+  return R;
+}
+
+template <decltype(^^int) R>
+consteval auto f2() {
+  return R;
+}
+
+consteval void test()
+{
+    constexpr auto r = ^^int;
+    constexpr auto q = ^^float;
+    constexpr info v{};
+
+    static_assert(__is_same(decltype(^^int), info));
+    static_assert(__is_same(decltype(^^float), info));
+    static_assert(__is_same(decltype(^^double), info));
+    static_assert(__is_same(decltype(^^long), info));
+    static_assert(__is_same(decltype(^^long long), info));
+    static_assert(__is_same(decltype(^^short), info));
+    static_assert(__is_same(decltype(^^char), info));
+    static_assert(__is_same(decltype(^^unsigned char), info));
+    static_assert(__is_same(decltype(^^unsigned short), info));
+    static_assert(__is_same(decltype(^^unsigned int), info));
+    static_assert(__is_same(decltype(^^unsigned long), info));
+    static_assert(__is_same(decltype(^^unsigned long long), info));
+    static_assert(__is_same(decltype(^^info), info));
+
+    static_assert(__is_same(decltype(^^int), decltype(^^int)));
+    static_assert(__is_same(decltype(^^int), decltype(^^float)));
+    static_assert(__is_same(decltype(^^int), decltype(^^char)));
+    static_assert(__is_same(decltype(^^double), decltype(^^float)));
+
+    static_assert(!__is_same(decltype(^^int), int));
+    static_assert(__is_scalar(info));
+
+    static_assert(f1< ^^int >() == ^^int);
+    static_assert(f1< ^^A >() != ^^int);
+    static_assert(f1< ^^float>() != ^^int);
+
+    static_assert(f2<r>() == ^^int);
+    static_assert(f2<^^float>() != ^^int);
+
+    static_assert(sizeof(info) == 8);
+    static_assert(alignof(info) == 1);
+    static_assert(sizeof(decltype(^^int)) == sizeof(decltype(^^float)));
+    static_assert(^^int32_t != ^^int);
+    static_assert(^^const int != ^^int);
+    static_assert(^^volatile int32_t == ^^volatile int);
+    static_assert(^^const volatile int32_t == ^^const volatile int);
+    static_assert(^^A != ^^int);
+
+
+    static_assert(^^int == ^^int);
+    static_assert(^^int != ^^float);
+    static_assert(^^float != ^^int);
+    static_assert(!(^^float == ^^int));
+    static_assert(r != q);
+
+    int a;
+    static_assert(^^int == ^^decltype(a));
+
+    using foo = const int;
+    static_assert(^^foo != ^^const int);
+    static_assert(^^const foo == ^^const int);
+    static_assert(^^const int == ^^int const);
+}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 6e30ffd6b322d..bf641c27f8c1c 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1563,6 +1563,7 @@ bool CursorVisitor::VisitBuiltinTypeLoc(BuiltinTypeLoc TL) {
 
   case BuiltinType::Void:
   case BuiltinType::NullPtr:
+  case BuiltinType::MetaInfo:
   case BuiltinType::Dependent:
 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix)                   \
   case BuiltinType::Id:
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 503f5da8af90f..592974804aad5 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -696,6 +696,32 @@ TEST_P(ImportType, ImportUsingType) {
                  cxxNewExpr(hasType(pointerType(pointee(usingType())))))));
 }
 
+struct ImportReflection : ASTImporterOptionSpecificTestBase {
+  std::vector<std::string> getExtraArgs() const override {
+    return {"-Xclang", "-freflection"};
+  }
+};
+
+TEST_P(ImportReflection, ImportMetaInfoBuiltinType) {
+  Decl *FromTU = getTuDecl("using declToImport = decltype(^^int);", Lang_CXX26,
+                           "input.cc");
+  auto *FromTA = FirstDeclMatcher<TypeAliasDecl>().match(
+      FromTU, typeAliasDecl(hasName("declToImport")));
+  ASSERT_TRUE(FromTA);
+
+  const auto *FromBT =
+      FromTA->getUnderlyingType().getCanonicalType()->getAs<BuiltinType>();
+  ASSERT_TRUE(FromBT);
+  ASSERT_EQ(BuiltinType::MetaInfo, FromBT->getKind());
+
+  auto *ToTA = Import(FromTA, Lang_CXX26);
+  ASSERT_TRUE(ToTA);
+  const auto *ToBT =
+      ToTA->getUnderlyingType().getCanonicalType()->getAs<BuiltinType>();
+  ASSERT_TRUE(ToBT);
+  EXPECT_EQ(BuiltinType::MetaInfo, ToBT->getKind());
+}
+
 TEST_P(ImportDecl, ImportFunctionTemplateDecl) {
   MatchVerifier<Decl> Verifier;
   testImport("template <typename T> void declToImport() { };", Lang_CXX03, "",
@@ -10873,6 +10899,9 @@ INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportInjectedClassNameType,
 INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportMatrixType,
                          DefaultTestValuesForRunOptions);
 
+INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportReflection,
+                         DefaultTestValuesForRunOptions);
+
 INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportTemplateParmDeclDefaultValue,
                          DefaultTestValuesForRunOptions);
 
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 2bfa187c009a7..07d08e181eae0 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -4904,6 +4904,9 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type) {
     case clang::BuiltinType::NullPtr:
       return lldb::eEncodingUint;
 
+    case clang::BuiltinType::MetaInfo:
+      return lldb::eEncodingUint;
+
     case clang::BuiltinType::Kind::ARCUnbridgedCast:
     case clang::BuiltinType::Kind::BoundMember:
     case clang::BuiltinType::Kind::BuiltinFn:

>From 48133336caf74cd9dc0617983df1c9e7a7918a53 Mon Sep 17 00:00:00 2001
From: changkhothuychung <nhat7203 at gmail.com>
Date: Thu, 23 Jul 2026 15:44:27 -0400
Subject: [PATCH 2/2] implement std::meta::is_type

---
 clang/include/clang/Basic/Builtins.td         |  6 ++
 clang/lib/AST/ASTContext.cpp                  |  3 +
 clang/lib/AST/ByteCode/InterpBuiltin.cpp      | 10 +++
 clang/lib/AST/ByteCode/Reflect.h              |  2 +
 clang/lib/AST/ExprConstant.cpp                |  7 ++
 clang/lib/Frontend/InitPreprocessor.cpp       |  4 ++
 clang/test/SemaCXX/builtin-meta-is-type.cpp   | 48 +++++++++++++
 clang/test/SemaCXX/consteval-builtin.cpp      | 18 +++++
 clang/utils/TableGen/ClangBuiltinsEmitter.cpp |  1 +
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/meta                           | 38 +++++++++++
 libcxx/include/module.modulemap.in            |  5 ++
 libcxx/include/version                        |  4 ++
 .../meta.version.compile.pass.cpp             | 68 +++++++++++++++++++
 .../version.version.compile.pass.cpp          | 33 +++++++++
 libcxx/test/std/meta/is_type.compile.pass.cpp | 51 ++++++++++++++
 .../generate_feature_test_macro_components.py |  7 ++
 18 files changed, 308 insertions(+)
 create mode 100644 clang/test/SemaCXX/builtin-meta-is-type.cpp
 create mode 100644 libcxx/include/meta
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/meta.version.compile.pass.cpp
 create mode 100644 libcxx/test/std/meta/is_type.compile.pass.cpp

diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 344a712ddc585..244a791938093 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1207,6 +1207,12 @@ def IsWithinLifetime : LangBuiltin<"CXX_LANG"> {
   let Prototype = "bool(void*)";
 }
 
+def MetaIsType : LangBuiltin<"CXX_LANG"> {
+  let Spellings = ["__builtin_meta_is_type"];
+  let Attributes = [NoThrow, Consteval];
+  let Prototype = "bool(std::meta::info)";
+}
+
 def ClearPadding : Builtin {
   let Spellings = ["__builtin_clear_padding"];
   let Attributes = [NoThrow, CustomTypeChecking];
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index c94598662915d..eef973690a350 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -12835,6 +12835,9 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
   case 'M':
     Type = Context.getObjCSuperType();
     break;
+  case 'r':
+    Type = Context.MetaInfoTy;
+    break;
   case 'a':
     Type = Context.getBuiltinVaListType();
     assert(!Type.isNull() && "builtin va list type not initialized!");
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index a539fb26abc08..de48e2b84fed9 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2535,6 +2535,13 @@ static bool interp__builtin_object_size(InterpState &S, CodePtr OpPC,
   return false;
 }
 
+static bool interp__builtin_meta_is_type(InterpState &S, CodePtr OpPC,
+                                         const CallExpr *) {
+  const Reflect &R = S.Stk.pop<Reflect>();
+  S.Stk.push<Boolean>(Boolean::from(R.getKind() == ReflectionKind::Type));
+  return true;
+}
+
 static bool interp__builtin_is_within_lifetime(InterpState &S, CodePtr OpPC,
                                                const CallExpr *Call) {
 
@@ -5454,6 +5461,9 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
   case Builtin::BI__builtin_is_within_lifetime:
     return interp__builtin_is_within_lifetime(S, OpPC, Call);
 
+  case Builtin::BI__builtin_meta_is_type:
+    return interp__builtin_meta_is_type(S, OpPC, Call);
+
   case Builtin::BI__builtin_elementwise_add_sat:
     return interp__builtin_elementwise_int_binop(
         S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) {
diff --git a/clang/lib/AST/ByteCode/Reflect.h b/clang/lib/AST/ByteCode/Reflect.h
index dcd00a3bad782..f5b31743f5f7a 100644
--- a/clang/lib/AST/ByteCode/Reflect.h
+++ b/clang/lib/AST/ByteCode/Reflect.h
@@ -31,6 +31,8 @@ class Reflect final {
   Reflect(ReflectionKind Kind, const void *Operand)
       : Kind(Kind), Operand(Operand) {}
 
+  ReflectionKind getKind() const { return Kind; }
+
   ComparisonCategoryResult compare(const Reflect &RHS) const {
     llvm::FoldingSetNodeID LID, RID;
     APValue(Kind, Operand).Profile(LID);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 30086822583d8..90d7771f61885 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -17252,6 +17252,13 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
       return Success(*result, E);
     return false;
 
+  case Builtin::BI__builtin_meta_is_type: {
+    APValue Reflection;
+    if (!EvaluateReflection(E->getArg(0), Reflection, Info))
+      return false;
+    return Success(Reflection.getReflectionOperandKind() == ReflectionKind::Type, E);
+  }
+
   case Builtin::BI__builtin_ctz:
   case Builtin::BI__builtin_ctzl:
   case Builtin::BI__builtin_ctzll:
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 8b6ff844d0daa..0266734132a73 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -766,6 +766,10 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
   Builder.defineMacro("__cpp_variadic_friend", "202403L");
   Builder.defineMacro("__cpp_trivial_relocatability", "202502L");
 
+  // C++26 reflection.
+  if (LangOpts.Reflection)
+    Builder.defineMacro("__cpp_impl_reflection", "202506L");
+
   if (LangOpts.Char8)
     Builder.defineMacro("__cpp_char8_t", "202207L");
   Builder.defineMacro("__cpp_impl_destroying_delete", "201806L");
diff --git a/clang/test/SemaCXX/builtin-meta-is-type.cpp b/clang/test/SemaCXX/builtin-meta-is-type.cpp
new file mode 100644
index 0000000000000..7060a2c06772a
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-meta-is-type.cpp
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 %s -std=c++26 -freflection -fsyntax-only -verify
+// RUN: %clang_cc1 %s -std=c++26 -freflection -fexperimental-new-constant-interpreter -fsyntax-only -verify
+
+using info = decltype(^^int);
+
+struct S {};
+namespace A{};
+
+consteval void test_pass() {
+  static_assert(__builtin_meta_is_type(^^int));
+  static_assert(__builtin_meta_is_type(^^unsigned));
+  static_assert(__builtin_meta_is_type(^^long));
+  static_assert(__builtin_meta_is_type(^^long long));
+  static_assert(__builtin_meta_is_type(^^short));
+  static_assert(__builtin_meta_is_type(^^char));
+  static_assert(__builtin_meta_is_type(^^signed char));
+  static_assert(__builtin_meta_is_type(^^unsigned char));
+  static_assert(__builtin_meta_is_type(^^wchar_t));
+  static_assert(__builtin_meta_is_type(^^char8_t));
+  static_assert(__builtin_meta_is_type(^^char16_t));
+  static_assert(__builtin_meta_is_type(^^char32_t));
+  static_assert(__builtin_meta_is_type(^^bool));
+  static_assert(__builtin_meta_is_type(^^float));
+  static_assert(__builtin_meta_is_type(^^double));
+  static_assert(__builtin_meta_is_type(^^long double));
+  static_assert(__builtin_meta_is_type(^^void));
+  static_assert(__builtin_meta_is_type(^^decltype(nullptr)));
+
+  constexpr info null{};
+  static_assert(!__builtin_meta_is_type(null));
+}
+
+consteval void test_fail() {
+  static_assert(__builtin_meta_is_type(^^S)); // expected-error {{unknown or unimplemented reflectable entity}}
+  static_assert(!__builtin_meta_is_type(^^A)); // expected-error {{unknown or unimplemented reflectable entity}}
+}
+
+consteval bool test_with_no_arg() {
+  return __builtin_meta_is_type(); // expected-error {{too few arguments to function call, expected 1, have 0}}
+}
+
+consteval bool test_with_more_than_args_needed() {
+  return __builtin_meta_is_type(^^int, ^^float); // expected-error {{too many arguments to function call, expected 1, have 2}}
+}
+
+consteval bool test_with_wrong_type() {
+  return __builtin_meta_is_type(42); // expected-error {{cannot initialize a parameter of type 'std::meta::info' with an rvalue of type 'int'}}
+}
diff --git a/clang/test/SemaCXX/consteval-builtin.cpp b/clang/test/SemaCXX/consteval-builtin.cpp
index 3ba95b4dbd9b5..7b45e3ddc1f78 100644
--- a/clang/test/SemaCXX/consteval-builtin.cpp
+++ b/clang/test/SemaCXX/consteval-builtin.cpp
@@ -25,6 +25,24 @@
 // precxx20-error at -3 {{does not have the constexpr builtin}}
 // c-error at -4 {{does not have the constexpr builtin}}
 
+#if __has_builtin(__builtin_meta_is_type)
+#error has the builtin
+#else
+#error does not have the builtin
+#endif
+// cxx20-cxx26-error at -4 {{has the builtin}}
+// precxx20-error at -3 {{does not have the builtin}}
+// c-error at -4 {{does not have the builtin}}
+
+#if __has_constexpr_builtin(__builtin_meta_is_type)
+#error has the constexpr builtin
+#else
+#error does not have the constexpr builtin
+#endif
+// cxx20-cxx26-error at -4 {{has the constexpr builtin}}
+// precxx20-error at -3 {{does not have the constexpr builtin}}
+// c-error at -4 {{does not have the constexpr builtin}}
+
 #if __cplusplus < 201103L
 #define static_assert __extension__ _Static_assert
 #define CONSTEXPR11
diff --git a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
index 22c81522f9e41..259df9275641a 100644
--- a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
+++ b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
@@ -376,6 +376,7 @@ class PrototypeParser {
                                .Case("short", "s")
                                .Case("sigjmp_buf", "SJ")
                                .Case("size_t", "z")
+                               .Case("std::meta::info", "r")
                                .Case("ucontext_t", "K")
                                .Case("uint8_t", "UBi")
                                .Case("uint16_t", "UTi")
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 6bf2f35fb4212..4be0f3a2f10d2 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -522,6 +522,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_reference_wrapper``                            ``202403L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_reflection``                                   ``202506L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_saturation_arithmetic``                        ``202603L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_senders``                                      *unimplemented*
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..d76a92fda3aa5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1060,6 +1060,7 @@ set(files
   mdspan
   memory
   memory_resource
+  meta
   mutex
   new
   numbers
diff --git a/libcxx/include/meta b/libcxx/include/meta
new file mode 100644
index 0000000000000..b0954ea86d82f
--- /dev/null
+++ b/libcxx/include/meta
@@ -0,0 +1,38 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_META
+#define _LIBCPP_META
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26 && defined(__cpp_impl_reflection)
+
+namespace meta {
+
+using info = decltype(^^int);
+
+#  if __has_builtin(__builtin_meta_is_type)
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI consteval bool is_type(info __r) {
+  return __builtin_meta_is_type(__r);
+}
+#  endif
+
+} // namespace meta
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_META
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..947e754271798 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1739,6 +1739,11 @@ module std {
     export *
   }
 
+  module meta {
+    header "meta"
+    export *
+  }
+
   module mutex {
     module lock_guard     { header "__mutex/lock_guard.h" }
     module mutex          { header "__mutex/mutex.h" }
diff --git a/libcxx/include/version b/libcxx/include/version
index 4a6b01093641d..31fafd4878f06 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -230,6 +230,7 @@ __cpp_lib_raw_memory_algorithms                         202411L <memory>
 __cpp_lib_rcu                                           202306L <rcu>
 __cpp_lib_reference_from_temporary                      202202L <type_traits>
 __cpp_lib_reference_wrapper                             202403L <functional>
+__cpp_lib_reflection                                    202506L <meta>
 __cpp_lib_remove_cvref                                  201711L <type_traits>
 __cpp_lib_result_of_sfinae                              201210L <functional> <type_traits>
 __cpp_lib_robust_nonmodifying_seq_ops                   201304L <algorithm>
@@ -626,6 +627,9 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_raw_memory_algorithms                202411L
 // # define __cpp_lib_rcu                                  202306L
 # define __cpp_lib_reference_wrapper                    202403L
+# if defined(__cpp_impl_reflection)
+#   define __cpp_lib_reflection                         202506L
+# endif
 # define __cpp_lib_saturation_arithmetic                202603L
 // # define __cpp_lib_senders                              202406L
 // # define __cpp_lib_smart_ptr_owner_equality             202306L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/meta.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/meta.version.compile.pass.cpp
new file mode 100644
index 0000000000000..ffb3107747251
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/meta.version.compile.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <meta>
+
+// Test the feature test macros defined by <meta>
+
+// clang-format off
+
+#include <meta>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  if defined(__cpp_impl_reflection)
+#    ifndef __cpp_lib_reflection
+#      error "__cpp_lib_reflection should be defined in c++26"
+#    endif
+#    if __cpp_lib_reflection != 202506L
+#      error "__cpp_lib_reflection should have the value 202506L in c++26"
+#    endif
+#  else
+#    ifdef __cpp_lib_reflection
+#      error "__cpp_lib_reflection should not be defined when the requirement 'defined(__cpp_impl_reflection)' is not met!"
+#    endif
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
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 f2d74cc99d864..e7ecf2eea855c 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
@@ -748,6 +748,10 @@
 #    error "__cpp_lib_reference_wrapper should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_remove_cvref
 #    error "__cpp_lib_remove_cvref should not be defined before c++20"
 #  endif
@@ -1724,6 +1728,10 @@
 #    error "__cpp_lib_reference_wrapper should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_remove_cvref
 #    error "__cpp_lib_remove_cvref should not be defined before c++20"
 #  endif
@@ -2868,6 +2876,10 @@
 #    error "__cpp_lib_reference_wrapper should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_remove_cvref
 #    error "__cpp_lib_remove_cvref should not be defined before c++20"
 #  endif
@@ -4276,6 +4288,10 @@
 #    error "__cpp_lib_reference_wrapper should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_remove_cvref
 #    error "__cpp_lib_remove_cvref should be defined in c++20"
 #  endif
@@ -5924,6 +5940,10 @@
 #    error "__cpp_lib_reference_wrapper should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_reflection
+#    error "__cpp_lib_reflection should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_remove_cvref
 #    error "__cpp_lib_remove_cvref should be defined in c++23"
 #  endif
@@ -7896,6 +7916,19 @@
 #    error "__cpp_lib_reference_wrapper should have the value 202403L in c++26"
 #  endif
 
+#  if defined(__cpp_impl_reflection)
+#    ifndef __cpp_lib_reflection
+#      error "__cpp_lib_reflection should be defined in c++26"
+#    endif
+#    if __cpp_lib_reflection != 202506L
+#      error "__cpp_lib_reflection should have the value 202506L in c++26"
+#    endif
+#  else
+#    ifdef __cpp_lib_reflection
+#      error "__cpp_lib_reflection should not be defined when the requirement 'defined(__cpp_impl_reflection)' is not met!"
+#    endif
+#  endif
+
 #  ifndef __cpp_lib_remove_cvref
 #    error "__cpp_lib_remove_cvref should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/meta/is_type.compile.pass.cpp b/libcxx/test/std/meta/is_type.compile.pass.cpp
new file mode 100644
index 0000000000000..9cfec64b7d1cc
--- /dev/null
+++ b/libcxx/test/std/meta/is_type.compile.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// ADDITIONAL_COMPILE_FLAGS(clang): -Xclang -freflection
+
+
+// consteval bool is_type(info);
+
+#include <meta>
+
+#include "test_macros.h"
+
+#if defined(__cpp_impl_reflection) && __has_builtin(__builtin_meta_is_type)
+
+ASSERT_SAME_TYPE(decltype(std::meta::is_type(std::meta::info{})), bool);
+
+template <class T>
+concept is_type_callable = requires(T t) { std::meta::is_type(t); };
+
+static_assert(is_type_callable<std::meta::info>);
+static_assert(!is_type_callable<int>);
+static_assert(!is_type_callable<void*>);
+static_assert(!is_type_callable<decltype(nullptr)>);
+
+static_assert(std::meta::is_type(^^int));
+static_assert(std::meta::is_type(^^unsigned));
+static_assert(std::meta::is_type(^^long));
+static_assert(std::meta::is_type(^^long long));
+static_assert(std::meta::is_type(^^short));
+static_assert(std::meta::is_type(^^char));
+static_assert(std::meta::is_type(^^signed char));
+static_assert(std::meta::is_type(^^unsigned char));
+static_assert(std::meta::is_type(^^wchar_t));
+static_assert(std::meta::is_type(^^char8_t));
+static_assert(std::meta::is_type(^^char16_t));
+static_assert(std::meta::is_type(^^char32_t));
+static_assert(std::meta::is_type(^^bool));
+static_assert(std::meta::is_type(^^float));
+static_assert(std::meta::is_type(^^double));
+static_assert(std::meta::is_type(^^long double));
+static_assert(std::meta::is_type(^^void));
+static_assert(std::meta::is_type(^^decltype(nullptr)));
+
+
+#endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f4e16078d5af0..485f51881341c 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1231,6 +1231,13 @@ def add_version_header(tc):
             "values": {"c++26": 202403}, # P2944R3: Comparisons for reference_wrapper
             "headers": ["functional"],
         },
+        {
+            "name": "__cpp_lib_reflection",
+            "values": {"c++26": 202506}, # P2996R13: Reflection for C++26
+            "headers": ["meta"],
+            "test_suite_guard": "defined(__cpp_impl_reflection)",
+            "libcxx_guard": "defined(__cpp_impl_reflection)",
+        },
         {
             "name": "__cpp_lib_remove_cvref",
             "values": {"c++20": 201711},


From libcxx-commits at lists.llvm.org  Thu Jul 23 12:53:35 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 23 Jul 2026 12:53:35 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [lldb] [clang]: implement
	std::meta::is_type (PR #211653)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211653@github.com>
Message-ID: <6a62713f.af6ba3ed.1989e7.31e2@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nhat Nguyen  (changkhothuychung)

<details>
<summary>Changes</summary>

This PR implements `std::meta::is_type(info)` by introducing a builtin `__builtin_meta_is_type`.  It also includes two feature-test macros `__cpp_impl_reflection` and `__cpp_lib_reflection` introduced in [P2996 ](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2996r13.html). `__cpp_impl_reflection` is only enabled when `-freflection` is on, and `__cpp_lib_reflection` depends on `__cpp_impl_reflection`. 

Header `&lt;meta&gt;` is created in libc++. 

---

Patch is 98.24 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/211653.diff


79 Files Affected:

- (modified) clang/include/clang/AST/APValue.h (+39-4) 
- (modified) clang/include/clang/AST/ASTContext.h (+1) 
- (modified) clang/include/clang/AST/BuiltinTypes.def (+3) 
- (modified) clang/include/clang/AST/ExprCXX.h (+16-5) 
- (modified) clang/include/clang/AST/PropertiesBase.td (+25) 
- (added) clang/include/clang/AST/Reflection.h (+39) 
- (modified) clang/include/clang/AST/TypeBase.h (+5) 
- (modified) clang/include/clang/Basic/Builtins.td (+6) 
- (modified) clang/include/clang/Basic/TargetInfo.h (+6) 
- (modified) clang/include/clang/Serialization/ASTBitCodes.h (+4-1) 
- (modified) clang/lib/AST/APValue.cpp (+79) 
- (modified) clang/lib/AST/ASTContext.cpp (+12) 
- (modified) clang/lib/AST/ASTImporter.cpp (+33-1) 
- (modified) clang/lib/AST/ByteCode/Compiler.cpp (+20) 
- (modified) clang/lib/AST/ByteCode/Compiler.h (+1) 
- (modified) clang/lib/AST/ByteCode/Context.cpp (+3) 
- (modified) clang/lib/AST/ByteCode/Descriptor.cpp (+1) 
- (modified) clang/lib/AST/ByteCode/Disasm.cpp (+2) 
- (modified) clang/lib/AST/ByteCode/Interp.cpp (+1-1) 
- (modified) clang/lib/AST/ByteCode/Interp.h (+7) 
- (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+10) 
- (modified) clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp (+1-1) 
- (modified) clang/lib/AST/ByteCode/InterpStack.h (+3) 
- (modified) clang/lib/AST/ByteCode/InterpState.cpp (+1) 
- (modified) clang/lib/AST/ByteCode/Opcodes.td (+8-1) 
- (modified) clang/lib/AST/ByteCode/PrimType.cpp (+1) 
- (modified) clang/lib/AST/ByteCode/PrimType.h (+6) 
- (modified) clang/lib/AST/ByteCode/Program.cpp (+1) 
- (modified) clang/lib/AST/ByteCode/Program.h (+1) 
- (added) clang/lib/AST/ByteCode/Reflect.h (+62) 
- (modified) clang/lib/AST/ExprCXX.cpp (+6-5) 
- (modified) clang/lib/AST/ExprConstant.cpp (+83-1) 
- (modified) clang/lib/AST/ItaniumMangle.cpp (+70-3) 
- (modified) clang/lib/AST/MicrosoftMangle.cpp (+5) 
- (modified) clang/lib/AST/NSAPI.cpp (+1) 
- (modified) clang/lib/AST/StmtPrinter.cpp (+9-2) 
- (modified) clang/lib/AST/TextNodeDumper.cpp (+16) 
- (modified) clang/lib/AST/Type.cpp (+10) 
- (modified) clang/lib/AST/TypeLoc.cpp (+1) 
- (modified) clang/lib/Basic/TargetInfo.cpp (+2) 
- (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+2) 
- (modified) clang/lib/CodeGen/CGExprConstant.cpp (+2) 
- (modified) clang/lib/CodeGen/CodeGenModule.cpp (+2) 
- (modified) clang/lib/CodeGen/CodeGenTypes.cpp (+7) 
- (modified) clang/lib/CodeGen/ItaniumCXXABI.cpp (+1) 
- (modified) clang/lib/CodeGen/QualTypeMapper.cpp (+3) 
- (modified) clang/lib/Frontend/InitPreprocessor.cpp (+4) 
- (modified) clang/lib/Sema/SemaExpr.cpp (+12-1) 
- (modified) clang/lib/Sema/SemaOverload.cpp (+18-5) 
- (modified) clang/lib/Sema/SemaTemplate.cpp (+16-1) 
- (modified) clang/lib/Sema/TreeTransform.h (+13-1) 
- (modified) clang/lib/Serialization/ASTCommon.cpp (+3) 
- (modified) clang/lib/Serialization/ASTReader.cpp (+3) 
- (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+13-2) 
- (modified) clang/lib/Serialization/ASTWriterStmt.cpp (+13-2) 
- (modified) clang/lib/UnifiedSymbolResolution/USRGeneration.cpp (+3) 
- (added) clang/test/AST/ast-dump-APValue-reflection.cpp (+14) 
- (added) clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp (+7) 
- (renamed) clang/test/CodeGenCXX/reflection-emit-meta-info-ms.cpp () 
- (modified) clang/test/CodeGenCXX/reflection-mangle-itanium.cpp (+40-2) 
- (added) clang/test/PCH/reflection.cpp (+14) 
- (added) clang/test/PCH/reflection_include.h (+1) 
- (added) clang/test/Sema/reflection-meta-info.fail.cpp (+56) 
- (added) clang/test/Sema/reflection-meta-info.pass.cpp (+77) 
- (added) clang/test/SemaCXX/builtin-meta-is-type.cpp (+48) 
- (modified) clang/test/SemaCXX/consteval-builtin.cpp (+18) 
- (modified) clang/tools/libclang/CIndex.cpp (+1) 
- (modified) clang/unittests/AST/ASTImporterTest.cpp (+29) 
- (modified) clang/utils/TableGen/ClangBuiltinsEmitter.cpp (+1) 
- (modified) libcxx/docs/FeatureTestMacroTable.rst (+2) 
- (modified) libcxx/include/CMakeLists.txt (+1) 
- (added) libcxx/include/meta (+38) 
- (modified) libcxx/include/module.modulemap.in (+5) 
- (modified) libcxx/include/version (+4) 
- (added) libcxx/test/std/language.support/support.limits/support.limits.general/meta.version.compile.pass.cpp (+68) 
- (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+33) 
- (added) libcxx/test/std/meta/is_type.compile.pass.cpp (+51) 
- (modified) libcxx/utils/generate_feature_test_macro_components.py (+7) 
- (modified) lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp (+3) 


``````````diff
diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h
index 9293266a41256..2f6efc670b6e8 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_CLANG_AST_APVALUE_H
 #define LLVM_CLANG_AST_APVALUE_H
 
+#include "clang/AST/Reflection.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/APFixedPoint.h"
 #include "llvm/ADT/APFloat.h"
@@ -141,7 +142,8 @@ class APValue {
     Struct,
     Union,
     MemberPointer,
-    AddrLabelDiff
+    AddrLabelDiff,
+    Reflection
   };
 
   class alignas(uint64_t) LValueBase {
@@ -315,12 +317,21 @@ class APValue {
     const AddrLabelExpr* LHSExpr;
     const AddrLabelExpr* RHSExpr;
   };
+  struct ReflectionData {
+    // OperandKind will eventually have support for
+    // TypeSourceInfo, TemplateReference, NamespaceReference, DeclRefExpr.
+    // Operand stores the opaque pointer of the reflection operand.
+    // Depending on the value of OperandKind, we can perform the
+    // corresponding cast to the associated type.
+    ReflectionKind OperandKind;
+    const void *Operand;
+  };
   struct MemberPointerData;
 
   // We ensure elsewhere that Data is big enough for LV and MemberPointerData.
-  typedef llvm::AlignedCharArrayUnion<void *, APSInt, APFloat, ComplexAPSInt,
-                                      ComplexAPFloat, Vec, Mat, Arr, StructData,
-                                      UnionData, AddrLabelDiffData>
+  typedef llvm::AlignedCharArrayUnion<
+      void *, APSInt, APFloat, ComplexAPSInt, ComplexAPFloat, Vec, Mat, Arr,
+      StructData, UnionData, AddrLabelDiffData, ReflectionData>
       DataType;
   static const size_t DataSize = sizeof(DataType);
 
@@ -415,6 +426,14 @@ class APValue {
       : Kind(None), AllowConstexprUnknown(false) {
     MakeArray(InitElts, Size);
   }
+
+  /// Creates a new Reflection APValue.
+  /// \param OperandKind The kind of reflection.
+  /// \param Operand The entity being reflected.
+  APValue(ReflectionKind OperandKind, const void *Operand) : Kind(None) {
+    MakeReflection(OperandKind, Operand);
+  }
+
   /// Creates a new struct APValue.
   /// \param UninitStruct Marker. Pass an empty UninitStruct.
   /// \param NumBases Number of bases.
@@ -498,6 +517,7 @@ class APValue {
   bool isUnion() const { return Kind == Union; }
   bool isMemberPointer() const { return Kind == MemberPointer; }
   bool isAddrLabelDiff() const { return Kind == AddrLabelDiff; }
+  bool isReflection() const { return Kind == Reflection; }
 
   void dump() const;
   void dump(raw_ostream &OS, const ASTContext &Context) const;
@@ -717,6 +737,16 @@ class APValue {
     return ((const AddrLabelDiffData *)(const char *)&Data)->RHSExpr;
   }
 
+  ReflectionKind getReflectionOperandKind() const {
+    assert(isReflection() && "Invalid accessor");
+    return ((const ReflectionData *)(const char *)&Data)->OperandKind;
+  }
+
+  const void *getReflectionOpaqueOperand() const {
+    assert(isReflection() && "Invalid accessor");
+    return ((const ReflectionData *)(const char *)&Data)->Operand;
+  }
+
   void setInt(APSInt I) {
     assert(isInt() && "Invalid accessor");
     *(APSInt *)(char *)&Data = std::move(I);
@@ -767,6 +797,11 @@ class APValue {
 
 private:
   void DestroyDataAndMakeUninit();
+  void MakeReflection(ReflectionKind OperandKind, const void *Operand) {
+    assert(isAbsent() && "Bad state change");
+    new ((void *)(char *)Data.buffer) ReflectionData{OperandKind, Operand};
+    Kind = Reflection;
+  }
   void MakeInt() {
     assert(isAbsent() && "Bad state change");
     new ((void *)&Data) APSInt(1);
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 7ed6509c3c16c..05dfec8c37586 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1351,6 +1351,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   CanQualType BFloat16Ty;
   CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3
   CanQualType VoidPtrTy, NullPtrTy;
+  CanQualType MetaInfoTy;
   CanQualType DependentTy, OverloadTy, BoundMemberTy, UnresolvedTemplateTy,
       UnknownAnyTy;
   CanQualType BuiltinFnTy;
diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def
index 444be4311a743..ee2f3f0365126 100644
--- a/clang/include/clang/AST/BuiltinTypes.def
+++ b/clang/include/clang/AST/BuiltinTypes.def
@@ -223,6 +223,9 @@ FLOATING_TYPE(Ibm128, Ibm128Ty)
 
 //===- Language-specific types --------------------------------------------===//
 
+// 'std::meta::info' in C++
+BUILTIN_TYPE(MetaInfo, MetaInfoTy)
+
 // This is the type of C++0x 'nullptr'.
 BUILTIN_TYPE(NullPtr, NullPtrTy)
 
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 757f2137c90dc..4ed388877a2ab 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -25,6 +25,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/OperationKinds.h"
+#include "clang/AST/Reflection.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/TemplateBase.h"
@@ -5502,37 +5503,47 @@ class BuiltinBitCastExpr final
 ///  - a type-id, or
 ///  - an id-expression.
 class CXXReflectExpr : public Expr {
+  friend class ASTStmtReader;
+  friend class ASTStmtWriter;
 
+private:
   // TODO(Reflection): add support for TemplateReference, NamespaceReference and
   // DeclRefExpr
-  using operand_type = llvm::PointerUnion<const TypeSourceInfo *>;
+  using operand_type = llvm::PointerUnion<TypeSourceInfo *>;
 
   SourceLocation CaretCaretLoc;
+  ReflectionKind Kind;
   operand_type Operand;
 
-  CXXReflectExpr(SourceLocation CaretCaretLoc, const TypeSourceInfo *TSI);
+  CXXReflectExpr(ASTContext &C, SourceLocation CaretCaretLoc,
+                 TypeSourceInfo *TSI);
   CXXReflectExpr(EmptyShell Empty);
 
 public:
   static CXXReflectExpr *Create(ASTContext &C, SourceLocation OperatorLoc,
-                                TypeSourceInfo *TL);
+                                TypeSourceInfo *TSI);
 
   static CXXReflectExpr *CreateEmpty(ASTContext &C);
 
   SourceLocation getBeginLoc() const LLVM_READONLY {
     return llvm::TypeSwitch<operand_type, SourceLocation>(Operand)
-        .Case<const TypeSourceInfo *>(
+        .Case<TypeSourceInfo *>(
             [](auto *Ptr) { return Ptr->getTypeLoc().getBeginLoc(); });
   }
 
   SourceLocation getEndLoc() const LLVM_READONLY {
     return llvm::TypeSwitch<operand_type, SourceLocation>(Operand)
-        .Case<const TypeSourceInfo *>(
+        .Case<TypeSourceInfo *>(
             [](auto *Ptr) { return Ptr->getTypeLoc().getEndLoc(); });
   }
 
   /// Returns location of the '^^'-operator.
   SourceLocation getOperatorLoc() const { return CaretCaretLoc; }
+  ReflectionKind getKind() const { return Kind; }
+  void *getOpaqueValue() const { return Operand.getOpaqueValue(); }
+  TypeSourceInfo *getTypeSourceInfo() const {
+    return cast<TypeSourceInfo *>(Operand);
+  }
 
   child_range children() {
     // TODO(Reflection)
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index 25ef4c26a9aa1..9a5c683fbf693 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -603,6 +603,31 @@ let Class = PropertyTypeCase<APValue, "LValue"> in {
   }]>;
 }
 
+def ReflectionKind : EnumPropertyType<"ReflectionKind">;
+
+let Class = PropertyTypeCase<APValue, "Reflection"> in {
+  def : Property<"reflectionKind", ReflectionKind> {
+    let Read = [{ node.getReflectionOperandKind() }];
+  }
+  def : Property<"reflectionType", QualType> {
+    let Conditional = [{ reflectionKind == ReflectionKind::Type }];
+    let Read = [{
+      QualType::getFromOpaquePtr(node.getReflectionOpaqueOperand())
+    }];
+  }
+  def : Creator<[{
+    switch (reflectionKind) {
+    // TODO(Reflection): Add support for TypeSourceInfo, NamespaceReference
+    // TemplateReference and DeclRefExpr
+    case ReflectionKind::Null:
+      return APValue(reflectionKind, nullptr);
+    case ReflectionKind::Type:
+      return APValue(reflectionKind, (*reflectionType).getAsOpaquePtr());
+    }
+    llvm_unreachable("unimplemented or unknow reflection entities");
+  }]>;
+}
+
 // Type cases for DeclarationName.
 def : PropertyTypeKind<DeclarationName, DeclarationNameKind,
                        "node.getNameKind()">;
diff --git a/clang/include/clang/AST/Reflection.h b/clang/include/clang/AST/Reflection.h
new file mode 100644
index 0000000000000..732aefde80885
--- /dev/null
+++ b/clang/include/clang/AST/Reflection.h
@@ -0,0 +1,39 @@
+//===--- Reflection.h - Kind of reflection operands ---*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares the kinds of reflection operands.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_REFLECTION_H
+#define LLVM_CLANG_AST_REFLECTION_H
+
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+
+// TODO(Reflection): Add support for Template, Namespace and DeclRefExpr.
+enum class ReflectionKind { Null, Type };
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+                                     ReflectionKind Kind) {
+  switch (Kind) {
+  case ReflectionKind::Type:
+    OS << "type";
+    break;
+  case ReflectionKind::Null:
+    OS << "null";
+    break;
+  }
+
+  return OS;
+}
+
+} // namespace clang
+
+#endif
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index c9658775f0470..4886db6998b45 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -2766,6 +2766,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool isUndeducedAutoType() const;             // C++11 auto or
                                                 // C++14 decltype(auto)
   bool isTypedefNameType() const;               // typedef or alias template
+  bool isMetaInfoType() const;                  // C++26 std::meta::info
 
 #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
   bool is##Id##Type() const;
@@ -9051,6 +9052,10 @@ inline bool Type::isVoidType() const {
   return isSpecificBuiltinType(BuiltinType::Void);
 }
 
+inline bool Type::isMetaInfoType() const {
+  return isSpecificBuiltinType(BuiltinType::MetaInfo);
+}
+
 inline bool Type::isHalfType() const {
   // FIXME: Should we allow complex __fp16? Probably not.
   return isSpecificBuiltinType(BuiltinType::Half);
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 344a712ddc585..244a791938093 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1207,6 +1207,12 @@ def IsWithinLifetime : LangBuiltin<"CXX_LANG"> {
   let Prototype = "bool(void*)";
 }
 
+def MetaIsType : LangBuiltin<"CXX_LANG"> {
+  let Spellings = ["__builtin_meta_is_type"];
+  let Attributes = [NoThrow, Consteval];
+  let Prototype = "bool(std::meta::info)";
+}
+
 def ClearPadding : Builtin {
   let Spellings = ["__builtin_clear_padding"];
   let Attributes = [NoThrow, CustomTypeChecking];
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 3aba4d261a651..dacc1d3a5d9ea 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -96,6 +96,7 @@ struct TransferrableTargetInfo {
   unsigned char FloatWidth, FloatAlign;
   unsigned char DoubleWidth, DoubleAlign;
   unsigned char LongDoubleWidth, LongDoubleAlign, Float128Align, Ibm128Align;
+  unsigned char MetaInfoWidth, MetaInfoAlign;
   unsigned char LargeArrayMinWidth, LargeArrayAlign;
   unsigned char LongWidth, LongAlign;
   unsigned char LongLongWidth, LongLongAlign;
@@ -828,6 +829,11 @@ class TargetInfo : public TransferrableTargetInfo,
   unsigned getIbm128Align() const { return Ibm128Align; }
   const llvm::fltSemantics &getIbm128Format() const { return *Ibm128Format; }
 
+  /// Returns the size of std::meta::info.
+  unsigned getMetaInfoWidth() const { return MetaInfoWidth; }
+  /// eturns the align of std::meta::info.
+  unsigned getMetaInfoAlign() const { return MetaInfoAlign; }
+
   /// Return the mangled code of long double.
   virtual const char *getLongDoubleMangling() const { return "e"; }
 
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 671341488278e..e36e6ea29810f 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1155,6 +1155,9 @@ enum PredefinedTypeIDs {
 #define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) PREDEF_TYPE_##Id##_ID,
 #include "clang/Basic/HLSLIntangibleTypes.def"
 
+  /// C++26 std::meta::info type.
+  PREDEF_TYPE_META_INFO_ID,
+
   /// The placeholder type for unresolved templates.
   PREDEF_TYPE_UNRESOLVED_TEMPLATE,
   // Sentinel value. Considered a predefined type but not useable as one.
@@ -1166,7 +1169,7 @@ enum PredefinedTypeIDs {
 ///
 /// Type IDs for non-predefined types will start at
 /// NUM_PREDEF_TYPE_IDs.
-const unsigned NUM_PREDEF_TYPE_IDS = 529;
+const unsigned NUM_PREDEF_TYPE_IDS = 530;
 
 // Ensure we do not overrun the predefined types we reserved
 // in the enum PredefinedTypeIDs above.
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index 727e5f8c00a10..1849562952fb7 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -374,6 +374,10 @@ APValue::APValue(const APValue &RHS)
     MakeAddrLabelDiff();
     setAddrLabelDiff(RHS.getAddrLabelDiffLHS(), RHS.getAddrLabelDiffRHS());
     break;
+  case Reflection:
+    MakeReflection(RHS.getReflectionOperandKind(),
+                   RHS.getReflectionOpaqueOperand());
+    break;
   }
 }
 
@@ -429,6 +433,8 @@ void APValue::DestroyDataAndMakeUninit() {
     ((MemberPointerData *)(char *)&Data)->~MemberPointerData();
   else if (Kind == AddrLabelDiff)
     ((AddrLabelDiffData *)(char *)&Data)->~AddrLabelDiffData();
+  else if (Kind == Reflection)
+    ((ReflectionData *)(char *)&Data)->~ReflectionData();
   Kind = None;
   AllowConstexprUnknown = false;
 }
@@ -438,6 +444,7 @@ bool APValue::needsCleanup() const {
   case None:
   case Indeterminate:
   case AddrLabelDiff:
+  case Reflection:
     return false;
   case Struct:
   case Union:
@@ -486,6 +493,61 @@ static void profileIntValue(llvm::FoldingSetNodeID &ID, const llvm::APInt &V) {
     ID.AddInteger((uint32_t)V.extractBitsAsZExtValue(std::min(32u, N - I), I));
 }
 
+/// [expr.reflect] p5, if a reflect-expression R matches the form
+/// ^^reflection-name it is interpreted as such; the identifier is looked up
+/// and the representation of R is determined as follows:
+/// - if lookup finds a type alias A, R represents the type the underlying
+///   entity of A if A was introduced by the declaration of a template
+///   parameter; otherwise, R represents A.
+
+/// [expr.reflect] p6, Given reflect-expression R of the form ^^type-id,
+/// if type-id is neither a placeholder type nor
+/// in the form of nested-name-specifier_opt template_opt simple-template-id
+/// then R represents the type denoted by the type-id
+
+// In particular, this means that e.g. '^^const Alias' is reflection of
+// a type, not an alias. For example:
+//
+// using foo = const int;
+// ^^int       // Type
+// ^^const int // Type
+// ^^foo       // Alias
+// ^^const foo // Type
+static bool isTypeAliasAsReflectionName(QualType QT) {
+  return QT.getLocalQualifiers() == Qualifiers{};
+}
+
+/// Unwrap reflected type for profiling
+static void profileTypeReflection(llvm::FoldingSetNodeID &ID, QualType QT) {
+  // TODO(Reflection)
+
+  if (isTypeAliasAsReflectionName(QT)) {
+    if (const auto *TDT = QT->getAs<TypedefType>()) {
+      ID.AddBoolean(true);
+      ID.AddPointer(TDT->getDecl()->getCanonicalDecl());
+      return;
+    }
+  }
+
+  ID.AddBoolean(false);
+  QT.getCanonicalType().Profile(ID);
+}
+
+static void profileReflection(llvm::FoldingSetNodeID &ID, APValue V) {
+  ID.AddInteger(static_cast<int>(V.getReflectionOperandKind()));
+  switch (V.getReflectionOperandKind()) {
+  case ReflectionKind::Null:
+    return;
+  case ReflectionKind::Type: {
+    const TypeSourceInfo *Info =
+        static_cast<const TypeSourceInfo *>(V.getReflectionOpaqueOperand());
+    profileTypeReflection(ID, Info->getType());
+    return;
+  }
+  }
+  assert(false && "unknown or unimplemented reflection entities");
+}
+
 void APValue::Profile(llvm::FoldingSetNodeID &ID) const {
   // Note that our profiling assumes that only APValues of the same type are
   // ever compared. As a result, we don't consider collisions that could only
@@ -632,6 +694,9 @@ void APValue::Profile(llvm::FoldingSetNodeID &ID) const {
     for (const CXXRecordDecl *D : getMemberPointerPath())
       ID.AddPointer(D);
     return;
+  case Reflection:
+    profileReflection(ID, *this);
+    return;
   }
 
   llvm_unreachable("Unknown APValue kind!");
@@ -986,6 +1051,19 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy,
     Out << " - ";
     Out << "&&" << getAddrLabelDiffRHS()->getLabel()->getName();
     return;
+  case APValue::Reflection:
+    switch (getReflectionOperandKind()) {
+    case ReflectionKind::Null:
+      Out << "std::meta::info{}";
+      break;
+    case ReflectionKind::Type: {
+      const auto *TInfo =
+          static_cast<const TypeSourceInfo *>(getReflectionOpaqueOperand());
+      Out << "^^" << TInfo->getType().stream(Policy);
+      break;
+    }
+    }
+    return;
   }
   llvm_unreachable("Unknown APValue kind!");
 }
@@ -1176,6 +1254,7 @@ LinkageInfo LinkageComputer::getLVForValue(const APValue &V,
   case APValue::ComplexInt:
   case APValue::ComplexFloat:
   case APValue::Vector:
+  case APValue::Reflection:
   case APValue::Matrix:
     break;
 
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 2228811546c0f..eef973690a350 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1511,6 +1511,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target,
   // nullptr type (C++0x 2.14.7)
   InitBuiltinType(NullPtrTy,           BuiltinType::NullPtr);
 
+  // std::meta::info type (C++26 21.4.1)
+  InitBuiltinType(MetaInfoTy, BuiltinType::MetaInfo);
+
   // half type (OpenCL 6.1.1.1) / ARM NEON __fp16
   InitBuiltinType(HalfTy, BuiltinType::Half);
 
@@ -2366,6 +2369,10 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
       Width = Target->getPointerWidth(LangAS::Default);
       Align = Target->getPointerAlign(LangAS::Default);
       break;
+    case BuiltinType::MetaInfo:
+      Width = Target->getMetaInfoWidth();
+      Align = Target->getMetaInfoAlign();
+      break;
     case BuiltinType::ObjCId:
     case BuiltinType::ObjCClass:
     case BuiltinType::ObjCSel:
@@ -3568,6 +3575,7 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx,
     case BuiltinType::VectorPair:
     case BuiltinType::DMR1024:
     case BuiltinType::DMR2048:
+    case BuiltinType::MetaInfo:
       OS << "?";
       return;
 
@@ -9325,6 +9333,7 @@ static char getObjCEncodingForPrimitiveType(const ASTContext *C,
     case BuiltinType::OCLReserveID:
     case BuiltinType::OCLSampler:
     case BuiltinType::Dependent:
+    case BuiltinType::MetaInfo:
 #define PPC_VECTOR_TYPE(Name, Id, Size) \
     case BuiltinType::Id:
 #include "clang/Basic/PPCTypes.def"
@@ -12826,6 +12835,9 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
   case 'M':
     Type = Context.getObjCSuperType();
     break;
+  case 'r':
+    Type = Context.MetaInfoTy;
+    break;
   case 'a':
     Type = Context.getBuiltinVaListType();
     assert(!Type.isNull() && "builtin va list type not initialized!");
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index db7d223d56af3..7e726eb155d38 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -686,6 +686,7 @@ namespace clang {
     ExpectedStmt VisitCXXThisExpr(CXXThisExpr *E);
     Expecte...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/211653

From libcxx-commits at lists.llvm.org  Thu Jul 23 12:54:00 2026
From: libcxx-commits at lists.llvm.org (Nhat Nguyen via libcxx-commits)
Date: Thu, 23 Jul 2026 12:54:00 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [lldb] [clang]: implement
	std::meta::is_type (PR #211653)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211653@github.com>
Message-ID: <6a627158.6c8668f2.1842d3.ad81@mx.google.com>


changkhothuychung wrote:

The actual change for this PR should be in the second commit, the first commit is from [this PR](https://github.com/llvm/llvm-project/pull/190356)

https://github.com/llvm/llvm-project/pull/211653

From libcxx-commits at lists.llvm.org  Thu Jul 23 12:55:07 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 23 Jul 2026 12:55:07 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [lldb] [clang]: implement
	std::meta::is_type (PR #211653)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211653@github.com>
Message-ID: <6a62719b.66c0679a.9a15c.11f3@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions ,h,cpp -- clang/include/clang/AST/Reflection.h clang/lib/AST/ByteCode/Reflect.h clang/test/AST/ast-dump-APValue-reflection.cpp clang/test/CodeGenCXX/reflection-emit-meta-info-itanium.cpp clang/test/PCH/reflection.cpp clang/test/PCH/reflection_include.h clang/test/Sema/reflection-meta-info.fail.cpp clang/test/Sema/reflection-meta-info.pass.cpp clang/test/SemaCXX/builtin-meta-is-type.cpp libcxx/include/meta libcxx/test/std/language.support/support.limits/support.limits.general/meta.version.compile.pass.cpp libcxx/test/std/meta/is_type.compile.pass.cpp clang/include/clang/AST/APValue.h clang/include/clang/AST/ASTContext.h clang/include/clang/AST/ExprCXX.h clang/include/clang/AST/TypeBase.h clang/include/clang/Basic/TargetInfo.h clang/include/clang/Serialization/ASTBitCodes.h clang/lib/AST/APValue.cpp clang/lib/AST/ASTContext.cpp clang/lib/AST/ASTImporter.cpp clang/lib/AST/ByteCode/Compiler.cpp clang/lib/AST/ByteCode/Compiler.h clang/lib/AST/ByteCode/Context.cpp clang/lib/AST/ByteCode/Descriptor.cpp clang/lib/AST/ByteCode/Disasm.cpp clang/lib/AST/ByteCode/Interp.cpp clang/lib/AST/ByteCode/Interp.h clang/lib/AST/ByteCode/InterpBuiltin.cpp clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp clang/lib/AST/ByteCode/InterpStack.h clang/lib/AST/ByteCode/InterpState.cpp clang/lib/AST/ByteCode/PrimType.cpp clang/lib/AST/ByteCode/PrimType.h clang/lib/AST/ByteCode/Program.cpp clang/lib/AST/ByteCode/Program.h clang/lib/AST/ExprCXX.cpp clang/lib/AST/ExprConstant.cpp clang/lib/AST/ItaniumMangle.cpp clang/lib/AST/MicrosoftMangle.cpp clang/lib/AST/NSAPI.cpp clang/lib/AST/StmtPrinter.cpp clang/lib/AST/TextNodeDumper.cpp clang/lib/AST/Type.cpp clang/lib/AST/TypeLoc.cpp clang/lib/Basic/TargetInfo.cpp clang/lib/CodeGen/CGDebugInfo.cpp clang/lib/CodeGen/CGExprConstant.cpp clang/lib/CodeGen/CodeGenModule.cpp clang/lib/CodeGen/CodeGenTypes.cpp clang/lib/CodeGen/ItaniumCXXABI.cpp clang/lib/CodeGen/QualTypeMapper.cpp clang/lib/Frontend/InitPreprocessor.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/TreeTransform.h clang/lib/Serialization/ASTCommon.cpp clang/lib/Serialization/ASTReader.cpp clang/lib/Serialization/ASTReaderStmt.cpp clang/lib/Serialization/ASTWriterStmt.cpp clang/lib/UnifiedSymbolResolution/USRGeneration.cpp clang/test/CodeGenCXX/reflection-mangle-itanium.cpp clang/test/SemaCXX/consteval-builtin.cpp clang/tools/libclang/CIndex.cpp clang/unittests/AST/ASTImporterTest.cpp clang/utils/TableGen/ClangBuiltinsEmitter.cpp libcxx/include/version libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp clang/test/CodeGenCXX/reflection-emit-meta-info-ms.cpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 90d7771f6..5626d6efe 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -17256,7 +17256,8 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
     APValue Reflection;
     if (!EvaluateReflection(E->getArg(0), Reflection, Info))
       return false;
-    return Success(Reflection.getReflectionOperandKind() == ReflectionKind::Type, E);
+    return Success(
+        Reflection.getReflectionOperandKind() == ReflectionKind::Type, E);
   }
 
   case Builtin::BI__builtin_ctz:
diff --git a/libcxx/include/meta b/libcxx/include/meta
index b0954ea86..b65e0b4fd 100644
--- a/libcxx/include/meta
+++ b/libcxx/include/meta
@@ -25,9 +25,7 @@ namespace meta {
 using info = decltype(^^int);
 
 #  if __has_builtin(__builtin_meta_is_type)
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI consteval bool is_type(info __r) {
-  return __builtin_meta_is_type(__r);
-}
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI consteval bool is_type(info __r) { return __builtin_meta_is_type(__r); }
 #  endif
 
 } // namespace meta
diff --git a/libcxx/test/std/meta/is_type.compile.pass.cpp b/libcxx/test/std/meta/is_type.compile.pass.cpp
index 9cfec64b7..282a287a0 100644
--- a/libcxx/test/std/meta/is_type.compile.pass.cpp
+++ b/libcxx/test/std/meta/is_type.compile.pass.cpp
@@ -9,7 +9,6 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
 // ADDITIONAL_COMPILE_FLAGS(clang): -Xclang -freflection
 
-
 // consteval bool is_type(info);
 
 #include <meta>
@@ -47,5 +46,4 @@ static_assert(std::meta::is_type(^^long double));
 static_assert(std::meta::is_type(^^void));
 static_assert(std::meta::is_type(^^decltype(nullptr)));
 
-
 #endif

``````````

</details>


https://github.com/llvm/llvm-project/pull/211653

From libcxx-commits at lists.llvm.org  Thu Jul 23 12:55:08 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 23 Jul 2026 12:55:08 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [lldb] [clang]: implement
	std::meta::is_type (PR #211653)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211653@github.com>
Message-ID: <6a62719c.0c05e68e.81651.fad8@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {darker}-->


:warning: Python code formatter, darker found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
darker --check --diff -r origin/main...HEAD libcxx/utils/generate_feature_test_macro_components.py
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from darker here.
</summary>

``````````diff
--- generate_feature_test_macro_components.py	2026-07-23 19:52:53.000000 +0000
+++ generate_feature_test_macro_components.py	2026-07-23 19:54:23.535754 +0000
@@ -1230,11 +1230,11 @@
             "values": {"c++26": 202403}, # P2944R3: Comparisons for reference_wrapper
             "headers": ["functional"],
         },
         {
             "name": "__cpp_lib_reflection",
-            "values": {"c++26": 202506}, # P2996R13: Reflection for C++26
+            "values": {"c++26": 202506},  # P2996R13: Reflection for C++26
             "headers": ["meta"],
             "test_suite_guard": "defined(__cpp_impl_reflection)",
             "libcxx_guard": "defined(__cpp_impl_reflection)",
         },
         {

``````````

</details>


https://github.com/llvm/llvm-project/pull/211653

From libcxx-commits at lists.llvm.org  Thu Jul 23 13:01:29 2026
From: libcxx-commits at lists.llvm.org (Nhat Nguyen via libcxx-commits)
Date: Thu, 23 Jul 2026 13:01:29 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [lldb] [clang]: implement
	std::meta::is_type (PR #211653)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211653@github.com>
Message-ID: <6a627319.66c0679a.9a15c.9579@mx.google.com>


https://github.com/changkhothuychung edited https://github.com/llvm/llvm-project/pull/211653

From libcxx-commits at lists.llvm.org  Thu Jul 23 13:51:59 2026
From: libcxx-commits at lists.llvm.org (Nhat Nguyen via libcxx-commits)
Date: Thu, 23 Jul 2026 13:51:59 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [lldb] [clang]: implement
	std::meta::is_type (PR #211653)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211653@github.com>
Message-ID: <6a627eef.f1c1923d.d9163.bb8f@mx.google.com>


https://github.com/changkhothuychung edited https://github.com/llvm/llvm-project/pull/211653

From libcxx-commits at lists.llvm.org  Thu Jul 23 13:58:44 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Thu, 23 Jul 2026 13:58:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][CI] Add a Github workflow
 to test libc++ tools (PR #211067)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211067@github.com>
Message-ID: <6a628084.1ba19891.1d5373.c6a4@mx.google.com>


https://github.com/boomanaiden154 edited https://github.com/llvm/llvm-project/pull/211067

From libcxx-commits at lists.llvm.org  Thu Jul 23 13:58:44 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Thu, 23 Jul 2026 13:58:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][CI] Add a Github workflow
 to test libc++ tools (PR #211067)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211067@github.com>
Message-ID: <6a628084.7d702ed2.2f6c05.53f2@mx.google.com>


https://github.com/boomanaiden154 commented:

Missed that these tools need git history. I guess keeping it in a separate workflow makes sense then.

https://github.com/llvm/llvm-project/pull/211067

From libcxx-commits at lists.llvm.org  Thu Jul 23 13:58:44 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Thu, 23 Jul 2026 13:58:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][CI] Add a Github workflow
 to test libc++ tools (PR #211067)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211067@github.com>
Message-ID: <6a628084.d2cb4adc.23815e.6d90@mx.google.com>


================
@@ -0,0 +1,58 @@
+# This workflow exercises libc++ tools whose testing doesn't belong in the conformance
+# suite or in `check-cxx` proper.
+#
+# The actual logic lives in `libcxx/utils/ci/run-buildbot test-tools`, which is also how
+# failures can be reproduced locally (after installing libcxx/utils/requirements.txt):
+#
+#     CC=clang CXX=clang++ libcxx/utils/ci/run-buildbot test-tools
+
+name: "[libc++] Test tools"
+
----------------
boomanaiden154 wrote:

permissions.contents: red?

https://github.com/llvm/llvm-project/pull/211067

From libcxx-commits at lists.llvm.org  Thu Jul 23 13:58:45 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Thu, 23 Jul 2026 13:58:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][CI] Add a Github workflow
 to test libc++ tools (PR #211067)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211067@github.com>
Message-ID: <6a628085.92ea0e2a.2ed880.494f@mx.google.com>


================
@@ -0,0 +1,58 @@
+# This workflow exercises libc++ tools whose testing doesn't belong in the conformance
+# suite or in `check-cxx` proper.
+#
+# The actual logic lives in `libcxx/utils/ci/run-buildbot test-tools`, which is also how
+# failures can be reproduced locally (after installing libcxx/utils/requirements.txt):
+#
+#     CC=clang CXX=clang++ libcxx/utils/ci/run-buildbot test-tools
+
+name: "[libc++] Test tools"
+
+on:
+  pull_request:
+    paths:
+      - 'libcxx/**'
+      - 'libcxxabi/**'
+      - 'libunwind/**'
+      - 'runtimes/**'
+      - 'cmake/**'
+      - '.github/workflows/libcxx-pr-test-tools.yml'
+  schedule:
+    # Match the main libc++ CI cron
+    - cron: '0 8 * * *'
+
+permissions:
+  contents: read
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  test-tools:
+    runs-on: llvm-premerge-libcxx-runners
+    continue-on-error: false
+    env:
+      CC: clang-23
+      CXX: clang++-23
+    steps:
+      - uses: actions/checkout at df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+        with:
+          persist-credentials: false
+          fetch-depth: 0 # some tools need full git history
+          fetch-tags: true
+
+      - uses: actions/setup-python at ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
+        with:
+          python-version: '3.14'
----------------
boomanaiden154 wrote:

Do we not already have python in the libcxx docker image?

https://github.com/llvm/llvm-project/pull/211067

From libcxx-commits at lists.llvm.org  Thu Jul 23 13:58:45 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Thu, 23 Jul 2026 13:58:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][CI] Add a Github workflow
 to test libc++ tools (PR #211067)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211067@github.com>
Message-ID: <6a628085.c56fb102.6921f.3bb0@mx.google.com>


================
@@ -0,0 +1,58 @@
+# This workflow exercises libc++ tools whose testing doesn't belong in the conformance
+# suite or in `check-cxx` proper.
+#
+# The actual logic lives in `libcxx/utils/ci/run-buildbot test-tools`, which is also how
+# failures can be reproduced locally (after installing libcxx/utils/requirements.txt):
+#
+#     CC=clang CXX=clang++ libcxx/utils/ci/run-buildbot test-tools
+
+name: "[libc++] Test tools"
+
+on:
+  pull_request:
+    paths:
+      - 'libcxx/**'
+      - 'libcxxabi/**'
+      - 'libunwind/**'
+      - 'runtimes/**'
+      - 'cmake/**'
+      - '.github/workflows/libcxx-pr-test-tools.yml'
+  schedule:
+    # Match the main libc++ CI cron
+    - cron: '0 8 * * *'
+
+permissions:
+  contents: read
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  test-tools:
+    runs-on: llvm-premerge-libcxx-runners
----------------
boomanaiden154 wrote:

Job should be disabled on forks.

https://llvm.org/docs/CIBestPractices.html#disabling-jobs-in-forks

https://github.com/llvm/llvm-project/pull/211067

From libcxx-commits at lists.llvm.org  Thu Jul 23 13:58:46 2026
From: libcxx-commits at lists.llvm.org (Aiden Grossman via libcxx-commits)
Date: Thu, 23 Jul 2026 13:58:46 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][CI] Add a Github workflow
 to test libc++ tools (PR #211067)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211067@github.com>
Message-ID: <6a628086.8e75e0cb.2de827.6d81@mx.google.com>


================
@@ -0,0 +1,58 @@
+# This workflow exercises libc++ tools whose testing doesn't belong in the conformance
+# suite or in `check-cxx` proper.
+#
+# The actual logic lives in `libcxx/utils/ci/run-buildbot test-tools`, which is also how
+# failures can be reproduced locally (after installing libcxx/utils/requirements.txt):
+#
+#     CC=clang CXX=clang++ libcxx/utils/ci/run-buildbot test-tools
+
+name: "[libc++] Test tools"
+
+on:
+  pull_request:
+    paths:
+      - 'libcxx/**'
+      - 'libcxxabi/**'
+      - 'libunwind/**'
+      - 'runtimes/**'
+      - 'cmake/**'
+      - '.github/workflows/libcxx-pr-test-tools.yml'
+  schedule:
+    # Match the main libc++ CI cron
+    - cron: '0 8 * * *'
+
+permissions:
+  contents: read
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  test-tools:
+    runs-on: llvm-premerge-libcxx-runners
+    continue-on-error: false
----------------
boomanaiden154 wrote:

This probably isn't necessary given there are no other jobs in this workflow.

https://github.com/llvm/llvm-project/pull/211067

From libcxx-commits at lists.llvm.org  Thu Jul 23 19:01:04 2026
From: libcxx-commits at lists.llvm.org (Vitaly Buka via libcxx-commits)
Date: Thu, 23 Jul 2026 19:01:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement any_of in terms of
	find_if (PR #207274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207274@github.com>
Message-ID: <6a62c760.7270d2fd.1ec500.da2a@mx.google.com>


vitalybuka wrote:

This patch triggers https://lab.llvm.org/buildbot/#/builders/55/builds/30810/steps/11/logs/stdio

https://github.com/llvm/llvm-project/pull/207274

From libcxx-commits at lists.llvm.org  Thu Jul 23 23:10:51 2026
From: libcxx-commits at lists.llvm.org (Vitaly Buka via libcxx-commits)
Date: Thu, 23 Jul 2026 23:10:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement any_of in terms of
	find_if (PR #207274)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/207274@github.com>
Message-ID: <6a6301eb.6c5720b3.1f1229.fcf4@mx.google.com>


vitalybuka wrote:

FYI @ldionne this was failed check on unaligned empty range. 
Could you please confirm that __assume_valid_range is expected to check alignment for empty ranges?

https://github.com/llvm/llvm-project/pull/207274

From libcxx-commits at lists.llvm.org  Thu Jul 23 23:27:26 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Thu, 23 Jul 2026 23:27:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] Guard use of
	`[[clang::trivial_abi]]` (PR #211736)
Message-ID: <llvm.org/llvm/llvm-project/pull/211736@github.com>


https://github.com/frederick-vs-ja created https://github.com/llvm/llvm-project/pull/211736

This patch makes `[[clang::trivial_abi]]` used only if supported to address MSVC's warning C5030.

The `TEST_CLANG_TRIVIAL_ABI` macro is not promoted to `test_macros.h` because it is currently used only in one file. We may promote it later.

Fixes #182392.

>From 96cc0f22868c171ce4e9af95a625ab869e566517 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 24 Jul 2026 14:25:43 +0800
Subject: [PATCH] [libc++][test] Guard use of `[[clang::trivial_abi]]`

This patch makes `[[clang::trivial_abi]]` used only if supported to
address MSVC's warning C5030.

The `TEST_CLANG_TRIVIAL_ABI` macro is not promoted to `test_macros.h`
because it is currently used only in one file. We may promote it later.
---
 .../sequences/vector/trivial_relocation.pass.cpp         | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/containers/sequences/vector/trivial_relocation.pass.cpp b/libcxx/test/std/containers/sequences/vector/trivial_relocation.pass.cpp
index fbd597d07d6e3..1ac46b182f680 100644
--- a/libcxx/test/std/containers/sequences/vector/trivial_relocation.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/trivial_relocation.pass.cpp
@@ -19,11 +19,18 @@
 
 #include "test_macros.h"
 
+// The test should pass regardless of whether the compiler supports [[clang::trivial_abi]].
+#if __has_cpp_attribute(clang::trivial_abi)
+#  define TEST_CLANG_TRIVIAL_ABI [[clang::trivial_abi]]
+#else
+#  define TEST_CLANG_TRIVIAL_ABI
+#endif
+
 struct Tracker {
   std::size_t move_constructs = 0;
 };
 
-struct [[clang::trivial_abi]] Inner {
+struct TEST_CLANG_TRIVIAL_ABI Inner {
   TEST_CONSTEXPR_CXX20 explicit Inner(Tracker* tracker) : tracker_(tracker) {}
   TEST_CONSTEXPR_CXX20 Inner(const Inner& rhs) : tracker_(rhs.tracker_) { tracker_->move_constructs += 1; }
   TEST_CONSTEXPR_CXX20 Inner(Inner&& rhs) : tracker_(rhs.tracker_) { tracker_->move_constructs += 1; }


From libcxx-commits at lists.llvm.org  Thu Jul 23 23:27:52 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 23 Jul 2026 23:27:52 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] Guard use of
	`[[clang::trivial_abi]]` (PR #211736)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211736@github.com>
Message-ID: <6a6305e8.66c0679a.9a15c.9da0@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: A. Jiang (frederick-vs-ja)

<details>
<summary>Changes</summary>

This patch makes `[[clang::trivial_abi]]` used only if supported to address MSVC's warning C5030.

The `TEST_CLANG_TRIVIAL_ABI` macro is not promoted to `test_macros.h` because it is currently used only in one file. We may promote it later.

Fixes #<!-- -->182392.

---
Full diff: https://github.com/llvm/llvm-project/pull/211736.diff


1 Files Affected:

- (modified) libcxx/test/std/containers/sequences/vector/trivial_relocation.pass.cpp (+8-1) 


``````````diff
diff --git a/libcxx/test/std/containers/sequences/vector/trivial_relocation.pass.cpp b/libcxx/test/std/containers/sequences/vector/trivial_relocation.pass.cpp
index fbd597d07d6e3..1ac46b182f680 100644
--- a/libcxx/test/std/containers/sequences/vector/trivial_relocation.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/trivial_relocation.pass.cpp
@@ -19,11 +19,18 @@
 
 #include "test_macros.h"
 
+// The test should pass regardless of whether the compiler supports [[clang::trivial_abi]].
+#if __has_cpp_attribute(clang::trivial_abi)
+#  define TEST_CLANG_TRIVIAL_ABI [[clang::trivial_abi]]
+#else
+#  define TEST_CLANG_TRIVIAL_ABI
+#endif
+
 struct Tracker {
   std::size_t move_constructs = 0;
 };
 
-struct [[clang::trivial_abi]] Inner {
+struct TEST_CLANG_TRIVIAL_ABI Inner {
   TEST_CONSTEXPR_CXX20 explicit Inner(Tracker* tracker) : tracker_(tracker) {}
   TEST_CONSTEXPR_CXX20 Inner(const Inner& rhs) : tracker_(rhs.tracker_) { tracker_->move_constructs += 1; }
   TEST_CONSTEXPR_CXX20 Inner(Inner&& rhs) : tracker_(rhs.tracker_) { tracker_->move_constructs += 1; }

``````````

</details>


https://github.com/llvm/llvm-project/pull/211736

From libcxx-commits at lists.llvm.org  Thu Jul 23 23:28:03 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 23 Jul 2026 23:28:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a6305f3.c5504292.1bbfc8.7fef@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/210951

>From c428a9ada3080af9cf20df99cf09062a5b328605 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 21 Jul 2026 13:14:55 +0200
Subject: [PATCH] [libc++] Fix ungetc failing after xsgetn

---
 libcxx/include/fstream                        |  77 ++++++---
 libcxx/src/ios.instantiations.cpp             |  16 ++
 .../fstreams/filebuf.members/test.dat         |   1 +
 .../filebuf.members/xsgetn.buffer.pass.cpp    | 156 ++++++++++++++++++
 .../fstreams/ifstream.members/xsgetn.pass.cpp |  14 ++
 5 files changed, 240 insertions(+), 24 deletions(-)
 create mode 100644 libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
 create mode 100644 libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..eebe2c879edfb 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -311,6 +311,8 @@ protected:
 
   _LIBCPP_HIDE_FROM_ABI_VIRTUAL streamsize xsgetn(char_type* __str, streamsize __len) override {
     if (__file_ && __always_noconv_) {
+      char_type __1buf;
+      __read_guard __guard(__1buf, *this);
       const streamsize __n = std::min(this->egptr() - this->gptr(), __len);
       if (__n != 0) {
         traits_type::copy(__str, this->gptr(), __n);
@@ -319,9 +321,16 @@ protected:
       const streamsize __remainder    = __len - __n;
       const streamsize __buffer_space = this->egptr() - this->eback();
 
-      if (__remainder >= __buffer_space)
-        return std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
-      else if (__remainder > 0)
+      if (__remainder >= __buffer_space) {
+        auto __res = std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
+
+        // Copy the buffer-sized tail into the buffer so that `unget`ting works correctly
+        auto __buf_size = std::min<size_t>(__res, this->egptr() - this->eback());
+        traits_type::copy(this->eback(), __str + __res - __buf_size, __buf_size);
+        this->setg(this->eback(), this->eback() + __buf_size, this->eback() + __buf_size);
+
+        return __res;
+      } else if (__remainder > 0)
         return basic_streambuf<_CharT, _Traits>::xsgetn(__str + __n, __remainder) + __n;
       return __n;
     }
@@ -384,6 +393,42 @@ private:
   bool __always_noconv_;
 
   bool __read_mode();
+
+  struct [[__nodiscard__]] __read_guard {
+    basic_filebuf& __self_;
+    size_t __unget_size_;
+    char_type& __1buf_;
+
+    _LIBCPP_HIDE_FROM_ABI __read_guard(char_type& __1buf, basic_filebuf& __self) : __self_(__self), __1buf_(__1buf) {
+      if (__self.__cm_ & ios_base::in) {
+        __unget_size_ = std::min<size_t>((__self.egptr() - __self.eback()) / 2, 4);
+      } else {
+        __self.setp(nullptr, nullptr);
+        if (__self.__always_noconv_) {
+          __self.setg(reinterpret_cast<char_type*>(__self.__extbuf_),
+                      reinterpret_cast<char_type*>(__self.__extbuf_) + __self.__ebs_,
+                      reinterpret_cast<char_type*>(__self.__extbuf_) + __self.__ebs_);
+        } else {
+          __self.setg(__self.__intbuf_, __self.__intbuf_ + __self.__ibs_, __self.__intbuf_ + __self.__ibs_);
+        }
+        __self.__cm_ = ios_base::in;
+        __unget_size_ = 0;
+      }
+
+      if (__self.gptr() == nullptr)
+        __self.setg(std::addressof(__1buf_), std::addressof(__1buf_) + 1, std::addressof(__1buf_) + 1);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI size_t __unget_size() {
+      return __unget_size_;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI ~__read_guard() {
+      if (__self_.eback() == std::addressof(__1buf_))
+        __self_.setg(nullptr, nullptr, nullptr);
+    }
+  };
+
   void __write_mode();
 
   _LIBCPP_HIDE_FROM_ABI static int __fseek(FILE* __file, pos_type __offset, int __whence);
@@ -802,12 +847,12 @@ template <class _CharT, class _Traits>
 typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>::underflow() {
   if (__file_ == nullptr)
     return traits_type::eof();
-  bool __initial = __read_mode();
+
   char_type __1buf;
-  if (this->gptr() == nullptr)
-    this->setg(std::addressof(__1buf), std::addressof(__1buf) + 1, std::addressof(__1buf) + 1);
-  const size_t __unget_sz = __initial ? 0 : std::min<size_t>((this->egptr() - this->eback()) / 2, 4);
-  int_type __c            = traits_type::eof();
+  __read_guard __guard(__1buf, *this);
+
+  const size_t __unget_sz = __guard.__unget_size();
+  int_type __c = traits_type::eof();
   if (this->gptr() == this->egptr()) {
     std::memmove(this->eback(), this->egptr() - __unget_sz, __unget_sz * sizeof(char_type));
     if (__always_noconv_) {
@@ -849,8 +894,6 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>
     }
   } else
     __c = traits_type::to_int_type(*this->gptr());
-  if (this->eback() == std::addressof(__1buf))
-    this->setg(nullptr, nullptr, nullptr);
   return __c;
 }
 
@@ -1141,20 +1184,6 @@ void basic_filebuf<_CharT, _Traits>::imbue(const locale& __loc) {
   }
 }
 
-template <class _CharT, class _Traits>
-bool basic_filebuf<_CharT, _Traits>::__read_mode() {
-  if (!(__cm_ & ios_base::in)) {
-    this->setp(nullptr, nullptr);
-    if (__always_noconv_)
-      this->setg((char_type*)__extbuf_, (char_type*)__extbuf_ + __ebs_, (char_type*)__extbuf_ + __ebs_);
-    else
-      this->setg(__intbuf_, __intbuf_ + __ibs_, __intbuf_ + __ibs_);
-    __cm_ = ios_base::in;
-    return true;
-  }
-  return false;
-}
-
 template <class _CharT, class _Traits>
 void basic_filebuf<_CharT, _Traits>::__write_mode() {
   if (!(__cm_ & ios_base::out)) {
diff --git a/libcxx/src/ios.instantiations.cpp b/libcxx/src/ios.instantiations.cpp
index a8d267f7cfd42..2f885688c60ee 100644
--- a/libcxx/src/ios.instantiations.cpp
+++ b/libcxx/src/ios.instantiations.cpp
@@ -15,6 +15,21 @@
 #include <streambuf>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+
+template <class _CharT, class _Traits>
+bool basic_filebuf<_CharT, _Traits>::__read_mode() {
+  if (!(__cm_ & ios_base::in)) {
+    this->setp(nullptr, nullptr);
+    if (__always_noconv_)
+      this->setg((char_type*)__extbuf_, (char_type*)__extbuf_ + __ebs_, (char_type*)__extbuf_ + __ebs_);
+    else
+      this->setg(__intbuf_, __intbuf_ + __ibs_, __intbuf_ + __ibs_);
+    __cm_ = ios_base::in;
+    return true;
+  }
+  return false;
+}
 
 // Original explicit instantiations provided in the library
 template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_ios<char>;
@@ -45,4 +60,5 @@ template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_filebuf<char>;
 
 // Add more here if needed...
 
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
new file mode 100644
index 0000000000000..6fe86c31590f8
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
@@ -0,0 +1 @@
+This is a bunch of data so the test can read some stuff and not instantly run out of data to read.
diff --git a/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp
new file mode 100644
index 0000000000000..0b7f3b04fa219
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FILE_DEPENDENCIES: test.dat
+
+// <fstream>
+
+// streamsize basic_filebuf::xsgetn(char_type*, streamsize);
+
+// Test that xsgetn buffers properly. Specifically, we guarantee that `unget()` can be called at least four times.
+
+#include <cassert>
+#include <fstream>
+#include <string>
+
+#include "platform_support.h"
+
+void small_file_tests() {
+  char buffer[12];
+
+  { // Check that we can unget() when reading a single character
+    std::ifstream is("test.dat");
+    assert(is.is_open());
+    char buf[1];
+    is.read(buf, 1);
+    assert(buf[0] == 'T');
+    is.unget();
+    assert(is.good());
+  }
+
+  { // Check that we can unget() when reading a single character with a user-provided buffer
+    std::ifstream is("test.dat");
+    assert(is.is_open());
+    is.rdbuf()->pubsetbuf(buffer, 12);
+    char buf[1];
+    is.read(buf, 1);
+    assert(buf[0] == 'T');
+    is.unget();
+    assert(is.good());
+  }
+
+  { // Check that unget() works as expected when the remainder is smaller than the buffer
+    std::ifstream is("test.dat");
+    assert(is.is_open());
+    is.rdbuf()->pubsetbuf(buffer, 12);
+    (void)is.rdbuf()->sgetc(); // Make sure there is data in the buffer
+    char buf[17];
+    buf[16] = '\0';
+    is.read(buf, 16);
+    assert(buf == std::string("This is a bunch "));
+    for (size_t i = 0; i != 4; ++i)
+      is.unget();
+    assert(is.good());
+    is.read(buf, 4);
+    buf[4] = '\0';
+    assert(buf == std::string("nch "));
+  }
+
+  { // Check that unget() works as expected when the remainder is larger than the buffer
+    std::ifstream is("test.dat");
+    assert(is.is_open());
+    is.rdbuf()->pubsetbuf(buffer, 12);
+    char buf[33];
+    buf[32] = '\0';
+    is.read(buf, 32);
+    assert(buf == std::string("This is a bunch of data so the t"));
+    for (size_t i = 0; i != 4; ++i)
+      is.unget();
+    assert(is.good());
+    is.read(buf, 4);
+    buf[4] = '\0';
+    assert(is.good());
+    assert(buf == std::string("he t"));
+  }
+
+  { // read an empty file
+    std::string empty_file = get_temp_file_name();
+    {
+      std::ofstream os(empty_file);
+    }
+    std::ifstream is(empty_file);
+    is.rdbuf()->pubsetbuf(nullptr, 64);
+
+    char buf[100];
+    is.read(buf, 100);
+    assert(is.eof());
+    assert(is.gcount() == 0);
+
+    is.clear();
+    is.unget();
+    assert(is.fail());
+    std::remove(empty_file.c_str());
+  }
+
+}
+
+static std::string make_pattern(std::size_t n) {
+  std::string s(n, '\0');
+  for (std::size_t i = 0; i != n; ++i)
+    s[i] = static_cast<char>('0' + (i % 10));
+  return s;
+}
+
+void large_file_tests() {
+  const std::string data = make_pattern(10000);
+  std::string file       = get_temp_file_name();
+  { // Prepare the file
+    std::ofstream os(file);
+    assert(os.write(data.data(), data.size()));
+  }
+
+  { // default buffer with a read larger than the buffer
+    std::ifstream is(file);
+    assert(is.is_open());
+    std::string buf;
+    buf.resize(8000);
+
+    is.read(buf.data(), 8000);
+    assert(is.gcount() == 8000);
+    assert(std::string(buf.data(), 8000) == data.substr(0, 8000));
+    is.unget();
+    assert(is.good());
+    assert(is.get() == data[7999]); // the ungotten character
+    assert(is.get() == data[8000]); // reading forward continues from the right place
+  }
+
+  { // EOF before the buffer is full
+    std::ifstream is(file);
+    assert(is.is_open());
+    is.rdbuf()->pubsetbuf(nullptr, 64);
+
+    std::string buf;
+    buf.resize(data.size() + 100);
+
+    is.read(buf.data(), data.size() + 100);
+    assert(is.gcount() == static_cast<std::streamsize>(data.size()));
+    assert(is.rdstate() == (std::ios::eofbit | std::ios::failbit));
+    is.clear(); // Clear the failbit due to trying to read more data than available
+    is.unget();
+    assert(is.good());
+    assert(is.get() == data.back());
+  }
+
+  std::remove(file.c_str());
+}
+
+int main(int, char**) {
+  small_file_tests();
+  large_file_tests();
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
index d9ccc2fe62914..35c4787e2a29f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
@@ -25,6 +25,14 @@
 
 #include "test_macros.h"
 
+void check_unget(std::filebuf* fb, char expected) {
+  auto c = fb->sungetc();
+  if (c != EOF) {
+    assert(c == expected);
+    assert(fb->sbumpc() == expected);
+  }
+}
+
 int main(int, char**) {
   std::vector<char> stream_buffer(10);
   std::ifstream fs("xsgetn.test.dat");
@@ -40,33 +48,39 @@ int main(int, char**) {
   { // Check that a read smaller than the buffer works fine
     assert(fb->sgetn(test_buffer.data(), 5) == 5);
     assert(std::string(test_buffer.data(), 5) == "this ");
+    check_unget(fb, ' ');
   }
   { // Check that reading up to the buffer end works fine
     assert(fb->sgetn(test_buffer.data(), 5) == 5);
     assert(std::string(test_buffer.data(), 5) == "is so");
+    check_unget(fb, 'o');
   }
   { // Check that reading from an empty buffer, but more than the buffer can
     // hold works fine
     test_buffer.resize(12);
     assert(fb->sgetn(test_buffer.data(), 12) == 12);
     assert(std::string(test_buffer.data(), 12) == "me random da");
+    check_unget(fb, 'a');
   }
   { // Check that reading from a non-empty buffer, and more than the buffer can
     // hold works fine Fill the buffer up
     test_buffer.resize(2);
     assert(fb->sgetn(test_buffer.data(), 2) == 2);
     assert(std::string(test_buffer.data(), 2) == "ta");
+    check_unget(fb, 'a');
 
     // Do the actual check
     test_buffer.resize(12);
     assert(fb->sgetn(test_buffer.data(), 12) == 12);
     assert(std::string(test_buffer.data(), 12) == " to be able ");
+    check_unget(fb, ' ');
   }
   { // Check that trying to read more than the file size works fine
     test_buffer.resize(30);
     assert(fb->sgetn(test_buffer.data(), 30) == 24);
     test_buffer.resize(24);
     assert(std::string(test_buffer.data(), 24) == "to test buffer behaviour");
+    check_unget(fb, 'r');
   }
   { // Ensure that the read fails gracefully with an unopened ifstream
     // See https://llvm.org/PR168628


From libcxx-commits at lists.llvm.org  Thu Jul 23 23:31:33 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Thu, 23 Jul 2026 23:31:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] Guard use of
	`[[clang::trivial_abi]]` (PR #211736)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211736@github.com>
Message-ID: <6a6306c5.7d702ed2.2f6c05.24c4@mx.google.com>


https://github.com/philnik777 commented:

Should we just disable the test for MSVC instead? This is rather meaningless if the compiler doesn't support `[[clang::trivial_abi]]`.

https://github.com/llvm/llvm-project/pull/211736

From libcxx-commits at lists.llvm.org  Thu Jul 23 23:50:13 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Thu, 23 Jul 2026 23:50:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a630b25.b1416da1.20b4cf.5bd6@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From 145920aa6c68ee172dc65111a6a0cfb99d4c9069 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/33] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..11a0b42ea54f5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 899502422570a..4ae0f6a28637a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -32,11 +33,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -52,21 +66,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -102,12 +120,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -126,23 +146,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -152,23 +178,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From 90d997369a9f358f579ae61963226b1f30c81d5a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/33] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 37c38d25012c6..78e27d9ef9781 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From 7a63b7897f421f0d367057c933db66b3ea0f21e1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/33] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 11a0b42ea54f5..caa7f9d1a2535 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 78e27d9ef9781..640f6e5e916ca 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..8bdde44c1cb8c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -895,6 +895,7 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From e6a881f9199428718c9b93a92dcea056d7e84356 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/33] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 4ae0f6a28637a..c496ecf5ac742 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -49,7 +49,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From 13589231818d564e6ba520c405eae011f72f10fe Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/33] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From d3c6b5e3b74e59c2beaf5b3158db90faef36600c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/33] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c496ecf5ac742..c55de19cd4b59 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -148,7 +148,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From 11cedf33d3fe73d5bad5930a8c4f12df33e0eac8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/33] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 7e6410501ecdccbd78ce814b0e4906a60f379df6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/33] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c55de19cd4b59..0cf50d7ebd1ec 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -162,7 +162,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -180,7 +180,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -195,7 +195,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 640f6e5e916ca..44f1229f859b3 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From f666483df06fffa6f94a403ba051621cf0432415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/33] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index caa7f9d1a2535..b6650a314bd40 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0cf50d7ebd1ec..05b22d04962fa 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,29 +30,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 44f1229f859b3..0a6e3d54b5911 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 8bdde44c1cb8c..f038c9d39a27b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -884,6 +884,7 @@ module std {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -895,7 +896,6 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From f622cc3f082c34d6f0c5cd6e47177664eed48938 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/33] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 1e55b9997f985525c33ac424b94de00d91b36bba Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/33] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 0a6e3d54b5911..2f6e0d5dbd20a 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From 996293d9cfb334a462283e783e2fb2af90bb373c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/33] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From f74f609379e9feab234dd430bc758b19d9b1d46b Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/33] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From 8c0b1998421e1c8e214ee2694544bf3f9d8f5f79 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/33] 14

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <cstring>
+#include <type_traits>
 
 struct WithTailPadding {
   int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From f436e600ce39701927e30d0074019afc4cdb581c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 20:42:14 +0100
Subject: [PATCH 15/33] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e4d83e638ca77..0bafdbb1d7ede 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -136,7 +136,7 @@ void test() {
   {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
-
+    std::atomic<T> a;
     T init = make<T>(10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);

>From 4def68f03c3089677bb5206f56f30e367415ecce Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:13:05 +0100
Subject: [PATCH 16/33] ci

---
 libcxx/include/__atomic/support/gcc.h         |  5 ++--
 .../atomics.types.generic/padding.pass.cpp    | 24 ++++++++++-------
 .../atomics.types.generic/padding.pass.cpp    | 27 ++++++++++---------
 .../atomics.types.generic/padding2.pass.cpp   |  3 ---
 4 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 2f6e0d5dbd20a..b162e2c2a63f0 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -50,9 +50,8 @@ struct __cxx_atomic_base_impl {
     std::__clear_padding_if_needed(__a_value);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
   _Tp __a_value;
 };
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index bf594b45ed54f..6f894fe6cb605 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -44,15 +44,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -61,11 +69,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0bafdbb1d7ede..344d08b493e60 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,9 +7,6 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits
@@ -41,15 +38,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -58,11 +63,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
index 4e17471a2461f..1040f342b8206 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -8,9 +8,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-localization
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits

>From eeecefecde068ea1f6e2f8417b52961192ca8bc4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:29:04 +0100
Subject: [PATCH 17/33] CI

---
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding2.pass.cpp   | 273 ------------------
 3 files changed, 2 insertions(+), 275 deletions(-)
 delete mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 6f894fe6cb605..388ab0867fe38 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 344d08b493e60..f1a534a2916d6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
deleted file mode 100644
index 1040f342b8206..0000000000000
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-localization
-
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
-// CAS should work on types with padding bits
-
-#include <atomic>
-#include <cassert>
-#include <cstring>
-#include <type_traits>
-#include <iostream>
-
-struct Foo {
-  int i;
-  char c;
-};
-
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
-
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
-}
-
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
-}
-
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-#ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
-#else
-  (void)f;
-  (void)pad_byte;
-#endif
-}
-
-void test() {
-  std::cerr << "test 1" << std::endl;
-  {
-    // compare_exchange_strong
-    // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
-    std::cerr << "test 2" << std::endl;
-
-    Foo init = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 3" << std::endl;
-    assert_foo_padding(init, 0xBB);
-    std::cerr << "test 4" << std::endl;
-    a.store(init);
-
-    Foo expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 5" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 6" << std::endl;
-
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 7" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 8" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 9" << std::endl;
-    assert(r);
-    std::cerr << "test 10" << std::endl;
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    std::cerr << "test 11" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 42);
-    std::cerr << "test 12" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 13" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 14" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 15" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 16" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 17" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 18" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 19" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 10" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 21" << std::endl;
-    assert(!r);
-    std::cerr << "test 22" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 23" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 24" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 25" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 10);
-    std::cerr << "test 26" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 27" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 28" << std::endl;
-  }
-
-  {
-    // compare_exchange_weak
-    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 29" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 30" << std::endl;
-    a.store(stored);
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 31" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 32" << std::endl;
-
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 33" << std::endl;
-    assert_foo_padding(original_expected, 0xAA);
-    std::cerr << "test 34" << std::endl;
-
-    bool r                  = false;
-    const auto max_attempts = 100;
-    auto current_attempt    = 0;
-    while (!r) {
-      ++current_attempt;
-      std::cerr << "test 35" << std::endl;
-      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      std::cerr << "test 36" << std::endl;
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
-      std::cerr << "test 37" << std::endl;
-      r = a.compare_exchange_weak(expected, new_value);
-      if (r) {
-        std::cerr << "test 38" << std::endl;
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-        std::cerr << "test 39" << std::endl;
-      } else {
-        // Spurious failure: expected is updated to the current atomic value.
-        std::cerr << "test 40" << std::endl;
-        assert(expected.i == 10);
-        std::cerr << "test 41" << std::endl;
-        assert(expected.c == 'a');
-        std::cerr << "test 45" << std::endl;
-        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
-        std::cerr << "test 43" << std::endl;
-      }
-    }
-
-    Foo loaded = a.load();
-    std::cerr << "test 44" << std::endl;
-    assert(loaded.i == 42);
-    std::cerr << "test 45" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 46" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 47" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 48" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 49" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 50" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 51" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 52" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 53" << std::endl;
-
-    bool r = a.compare_exchange_weak(expected, new_value);
-
-    std::cerr << "test 54" << std::endl;
-    assert(!r);
-    std::cerr << "test 55" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 56" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 57" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 58" << std::endl;
-    Foo loaded = a.load();
-    std::cerr << "test 59" << std::endl;
-    assert(loaded.i == 10);
-    std::cerr << "test 60" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 61" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 62" << std::endl;
-  }
-
-  {
-    // Types with unique object representations skip the padding-clearing path.
-    std::atomic<int> a(1);
-    int expected = 1;
-    std::cerr << "test 63" << std::endl;
-    assert(a.compare_exchange_strong(expected, 2));
-    std::cerr << "test 64" << std::endl;
-    assert(expected == 1);
-    std::cerr << "test 65" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 66" << std::endl;
-
-    expected = 3;
-    assert(!a.compare_exchange_strong(expected, 4));
-    std::cerr << "test 67" << std::endl;
-    assert(expected == 2);
-    std::cerr << "test 68" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 69" << std::endl;
-  }
-}
-
-int main(int, char**) {
-  // TODO(LLVM-23): Switch to XFAIL: clang-22
-  std::cerr << "main 1" << std::endl;
-#if __has_builtin(__builtin_clear_padding)
-  std::cerr << "main 2" << std::endl;
-  test();
-  std::cerr << "main 3" << std::endl;
-#endif
-  std::cerr << "main 4" << std::endl;
-  return 0;
-}

>From bc539fb8d58e16f24a5ccb8bdd2351475e7d4391 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:52:15 +0100
Subject: [PATCH 18/33] ci

---
 libcxx/include/__atomic/support/c11.h | 13 ++++++++-----
 libcxx/include/__atomic/support/gcc.h |  9 +++++++--
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 05b22d04962fa..d43a2c67f950a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -30,13 +30,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
 
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b162e2c2a63f0..d2d4919d54614 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,12 +46,17 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
       : __a_value(std::__clear_padding_if_needed(__value)) {}
+
   _Tp __a_value;
 };
 

>From 30a056210700cd8f89b1c0ca6991f272a0ebba43 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:26:08 +0100
Subject: [PATCH 19/33] review

---
 libcxx/include/__atomic/clear_padding.h            | 14 ++++++--------
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 5bc77ff4401ab..f920ef39977b7 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -35,17 +35,16 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, float>>,
            _Not<is_same<_Tp, double>>> {};
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(std::addressof(__obj));
-    }
-  }
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+  return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
+}
+
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
@@ -67,7 +66,6 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 388ab0867fe38..45fb083cccf3f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index f1a534a2916d6..5ffba6ca844c5 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();

>From 9b771605f016811c06b3f600e48f45f3268a5269 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:33:16 +0100
Subject: [PATCH 20/33] release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx20Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..a689379321496 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange (`Github <https://llvm.org/PR76180>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 5539767ab800b..8e394b4bc4b3a 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -31,7 +31,7 @@
 "`P0458R2 <https://wg21.link/P0458R2>`__","Checking for Existence of an Element in Associative Containers","2018-06 (Rapperswil)","|Complete|","13","`#104029 <https://github.com/llvm/llvm-project/issues/104029>`__",""
 "`P0475R1 <https://wg21.link/P0475R1>`__","LWG 2511: guaranteed copy elision for piecewise construction","2018-06 (Rapperswil)","|Complete|","","`#104031 <https://github.com/llvm/llvm-project/issues/104031>`__",""
 "`P0476R2 <https://wg21.link/P0476R2>`__","Bit-casting object representations","2018-06 (Rapperswil)","|Complete|","14","`#104087 <https://github.com/llvm/llvm-project/issues/104087>`__",""
-"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","","","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
+"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","|Complete|","24","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
 "`P0542R5 <https://wg21.link/P0542R5>`__","Support for contract based programming in C++","2018-06 (Rapperswil)","|Nothing To Do|","n/a","`#104089 <https://github.com/llvm/llvm-project/issues/104089>`__","Pulled at the 2019-07 meeting in Cologne"
 "`P0556R3 <https://wg21.link/P0556R3>`__","Integral power-of-2 operations","2018-06 (Rapperswil)","|Complete|","9","`#104090 <https://github.com/llvm/llvm-project/issues/104090>`__",""
 "`P0619R4 <https://wg21.link/P0619R4>`__","Reviewing Deprecated Facilities of C++17 for C++20","2018-06 (Rapperswil)","|Complete|","20","`#99985 <https://github.com/llvm/llvm-project/issues/99985>`__","Removed headers are still provided as an extension, but with deprecation warnings."

>From 46a4ba35dbc30153f7868e9796d44f88512ae602 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 17:01:57 +0100
Subject: [PATCH 21/33] ci

---
 libcxx/include/__atomic/clear_padding.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index f920ef39977b7..3099faa8ca6d1 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/has_unique_object_representation.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>

>From 5f475333df1aeee9640364c9fb2f734c731edb7e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 19:03:39 +0100
Subject: [PATCH 22/33] ci

---
 .../atomics.types.generic/padding.pass.cpp    | 19 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 52 ++++++++++++-------
 2 files changed, 44 insertions(+), 27 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 45fb083cccf3f..116a860b3192c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -57,11 +57,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -86,7 +84,8 @@ void test() {
 
   {
     // atomic(T);
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     std::atomic<T> a(init);
     T loaded = a.load();
@@ -97,7 +96,8 @@ void test() {
   {
     // atomic::store
     std::atomic<T> a;
-    T value = make<T>(5, 'x', 0xAB);
+    T value;
+    initialize(value, 5, 'x', 0xAB);
     assert_padding(value, 0xAB);
     a.store(value);
     T loaded = a.load();
@@ -107,10 +107,12 @@ void test() {
   }
   {
     // atomic::exchange
-    T initial = make<T>(1, 'a', 0x00);
+    T initial;
+    initialize(initial, 1, 'a', 0x00);
     assert_padding(initial, 0x00);
     std::atomic<T> a(initial);
-    T new_val = make<T>(2, 'b', 0xCD);
+    T new_val;
+    initialize(new_val, 2, 'b', 0xCD);
     assert_padding(new_val, 0xCD);
     T old = a.exchange(new_val);
     assert(old.i == 1);
@@ -124,7 +126,8 @@ void test() {
   {
     // atomic_init
     std::atomic<T> a;
-    T init = make<T>(7, 'z', 0xEF);
+    T init;
+    initialize(init, 7, 'z', 0xEF);
     assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
     T loaded = a.load();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 5ffba6ca844c5..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -51,11 +51,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -84,17 +82,20 @@ void test() {
     // CAS should succeed when only padding differs in expected; expected is unchanged.
     std::atomic<T> a;
 
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0xAA);
+    T expected;
+    initialize(expected, 10, 'a', 0xAA);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -112,13 +113,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -138,17 +142,20 @@ void test() {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
     std::atomic<T> a;
-    T init = make<T>(10, 'a', 0x00);
+    T init;
+    initialize(init, 10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0x00);
+    T expected;
+    initialize(expected, 10, 'a', 0x00);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0x42);
+    T new_value;
+    initialize(new_value, 42, 'b', 0x42);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,14 +173,17 @@ void test() {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
-    T original_expected = make<T>(10, 'a', 0xAA);
+    T original_expected;
+    initialize(original_expected, 10, 'a', 0xAA);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -182,7 +192,8 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      T expected = make<T>(10, 'a', 0xAA);
+      T expected;
+      initialize(expected, 10, 'a', 0xAA);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -207,13 +218,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);

>From 704a54eb4f1f7989931b7e25ad87abbb60ad6021 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:07:26 +0100
Subject: [PATCH 23/33] debug

---
 .../atomics.types.generic/padding.pass.cpp    | 64 ++++++++++++++++++-
 1 file changed, 62 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..3b7f13b366e96 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,6 +15,7 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
+#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -70,11 +71,19 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
+void print_memory(const T& obj) {
+  for (size_t i = 0; i < sizeof(T); ++i) {
+    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
+  }
+  std::cerr << std::endl;
+}
+
 template <class T>
 void test() {
   {
@@ -84,11 +93,15 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
+    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
+    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -96,6 +109,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -106,6 +121,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test1 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -115,14 +132,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -131,11 +154,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test2 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test2 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -144,11 +171,15 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
+    std::cerr << "test3 init padding should be 0x00: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
+    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -156,6 +187,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
+    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,6 +199,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test3 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -175,15 +210,21 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
+    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
+    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -194,6 +235,8 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
+      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
+      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -203,6 +246,8 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        std::cerr << "test4 expected padding should be 0: " << std::endl;
+        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -211,6 +256,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test4 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -220,14 +267,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -236,11 +289,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test5 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test5 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -262,10 +319,13 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
+  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
+  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
+  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 0;
+  return 1;
 }

>From 8c622e41d2428f630855f0aaa215b1463d8fe4ca Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:38:19 +0100
Subject: [PATCH 24/33] Revert "debug"

This reverts commit 8be9a83ee040faff62f0756555890484bf502a31.
---
 .../atomics.types.generic/padding.pass.cpp    | 64 +------------------
 1 file changed, 2 insertions(+), 62 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 3b7f13b366e96..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,7 +15,6 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
-#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -71,19 +70,11 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)obj;
+  (void)f;
   (void)pad_byte;
 #endif
 }
 
-template <class T>
-void print_memory(const T& obj) {
-  for (size_t i = 0; i < sizeof(T); ++i) {
-    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
-  }
-  std::cerr << std::endl;
-}
-
 template <class T>
 void test() {
   {
@@ -93,15 +84,11 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
-    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
-    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -109,8 +96,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -121,8 +106,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test1 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -132,20 +115,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -154,15 +131,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test2 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test2 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -171,15 +144,11 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
-    std::cerr << "test3 init padding should be 0x00: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
-    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -187,8 +156,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
-    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -199,8 +166,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test3 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -210,21 +175,15 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
-    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
-    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -235,8 +194,6 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
-      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
-      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -246,8 +203,6 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        std::cerr << "test4 expected padding should be 0: " << std::endl;
-        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -256,8 +211,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test4 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -267,20 +220,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -289,15 +236,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test5 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test5 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -319,13 +262,10 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
-  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
-  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
-  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 1;
+  return 0;
 }

>From 789912ba84ccbb3a3efea236fde31835783a7415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:44:00 +0100
Subject: [PATCH 25/33] test ci

---
 .../atomics.types.generic/padding.pass.cpp    | 20 ++++++++++++++-----
 .../atomics.types.generic/padding.pass.cpp    | 12 +----------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 116a860b3192c..cd41f2893864c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -71,6 +71,16 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
+void assert_padding(const std::atomic<T>& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  T loaded     = obj.load();
+  set(reference, loaded.i, loaded.c);
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
+}
+
 template <class T>
 void test() {
   {
@@ -79,7 +89,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 
   {
@@ -91,7 +101,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::store
@@ -103,7 +113,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::exchange
@@ -121,7 +131,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic_init
@@ -133,7 +143,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..10c15523cd70b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -70,7 +70,7 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
@@ -105,8 +105,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -135,8 +133,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
   {
     // compare_exchange_strong
@@ -165,8 +161,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -210,8 +204,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -240,8 +232,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {

>From 81770cda9f2171565c1c2980ad8306b9665752b2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:18:49 +0100
Subject: [PATCH 26/33] padding

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index cd41f2893864c..a178ce74578ec 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -127,7 +127,6 @@ void test() {
     T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_padding(old, 0);
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');

>From b90bec87725f4bde5722a3aa857a5c9d22c275e5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:40:31 +0100
Subject: [PATCH 27/33] clang22

---
 .../libcxx/atomics/atomics.types.generic/padding.pass.cpp    | 5 ++---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 4 +---
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index a178ce74578ec..48ae68a8e97ba 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -147,11 +148,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
+
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 10c15523cd70b..dc0afa439dc12 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
@@ -250,12 +251,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
 
   return 0;
 }

>From 05729850c8bf62df54a60b12201ac475e5c55fb5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 13:26:21 +0100
Subject: [PATCH 28/33] cxx 03

---
 libcxx/include/__atomic/clear_padding.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 3099faa8ca6d1..dad48ed27e6a4 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -32,23 +32,23 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
-           _Not<is_same<_Tp, float>>,
-           _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)> >,
+           _Not<is_same<_Tp, float> >,
+           _Not<is_same<_Tp, double> > > {};
 
-template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   return __obj;
 }
 
-template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp> >::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);

>From 2d00c0b818d180cc4d991508be2ad3dd8b7ee878 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:26:21 +0100
Subject: [PATCH 29/33] ci

---
 libcxx/include/__atomic/clear_padding.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index dad48ed27e6a4..1a4223fd97763 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -37,12 +37,12 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double> > > {};
 
 template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
   return __obj;
 }
 
 template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 

>From 30d5c984f33655f34946afb790bdc85c1f29d7a4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 15:49:25 +0100
Subject: [PATCH 30/33] ci

---
 libcxx/include/__atomic/clear_padding.h | 31 +++++++++++++++----------
 1 file changed, 19 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 1a4223fd97763..be55837cd8ec8 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -46,20 +46,27 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __ob
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 
-template <class _Tp, class _Up, class _CasFunc>
+template <class _Tp,
+          class _Up,
+          class _CasFunc,
+          __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
+}
+
+template <class _Tp,
+          class _Up,
+          class _CasFunc,
+          __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp> >::value) {
-    return __cas_func(__expected, __value);
+  std::__clear_padding_if_needed(__value);
+  __remove_cvref_t<_Tp> __expected_copy = *__expected;
+  std::__clear_padding_if_needed(__expected_copy);
+  if (__cas_func(std::addressof(__expected_copy), __value)) {
+    return true;
   } else {
-    std::__clear_padding_if_needed(__value);
-    __remove_cvref_t<_Tp> __expected_copy = *__expected;
-    std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), __value)) {
-      return true;
-    } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
-      return false;
-    }
+    std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
+    return false;
   }
 }
 

>From 2c566e60184cacd4536478332d52b88f196d12b6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 17:01:17 +0100
Subject: [PATCH 31/33] ci

---
 .../libcxx/atomics/atomics.types.generic/padding.pass.cpp   | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 48ae68a8e97ba..65b890b75d1de 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -23,6 +23,8 @@
 #include <cstring>
 #include <type_traits>
 
+#include "test_macros.h"
+
 struct WithTailPadding {
   int i;
   char c;
@@ -87,10 +89,12 @@ void test() {
   {
     // atomic();
     std::atomic<T> a;
+    assert_padding(a, 0);
+#if TEST_STD_VER >= 20
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_padding(a, 0);
+#endif
   }
 
   {

>From 83ddf2179999b52bb6007bd055d86da1383d4bcb Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 18:38:43 +0100
Subject: [PATCH 32/33] ci

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 65b890b75d1de..c9ed3bbcc5704 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -88,9 +88,9 @@ template <class T>
 void test() {
   {
     // atomic();
+#if TEST_STD_VER >= 20
     std::atomic<T> a;
     assert_padding(a, 0);
-#if TEST_STD_VER >= 20
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');

>From e6e386683433dc9931d7a91d49346c0d45a20a3d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 24 Jul 2026 07:49:38 +0100
Subject: [PATCH 33/33] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dc0afa439dc12..f9008f29f14b4 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: !has-64-bit-atomics
 // XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic<T>::compare_exchange_weak


From libcxx-commits at lists.llvm.org  Thu Jul 23 23:57:11 2026
From: libcxx-commits at lists.llvm.org (Xavier Roche via libcxx-commits)
Date: Thu, 23 Jul 2026 23:57:11 -0700 (PDT)
Subject: [libcxx-commits] [clang] [compiler-rt] [libcxx] [Clang] Support
 atomic operations on _BitInt(N) (PR #204815)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204815@github.com>
Message-ID: <6a630cc7.824e11a6.322560.ea04@mx.google.com>


https://github.com/xroche updated https://github.com/llvm/llvm-project/pull/204815

>From 5c7fc853303e60e1c39d6ea49d5fa6ad445abc16 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Fri, 19 Jun 2026 13:59:45 +0200
Subject: [PATCH 1/9] [Clang][POC] Atomic operations on _BitInt(N)

_BitInt(N) was rejected by every atomic path: the _Atomic(...) type
specifier, the __c11_atomic_*/__atomic_* builtins, and transitively
std::atomic. Two blanket isBitIntType() checks disabled it, dating to the
type's introduction (the __atomic builtin half is D84049). __int128, the
closest analogue, was allowed at both sites.

Lift both rejections so _BitInt flows through the normal integer path.
load, store, exchange, compare-exchange, and bitwise read-modify-write are
then correct at every width through the existing canonicalizing store and
the libcall fallback.

Arithmetic read-modify-write needs more care. A single atomicrmw on the
padded memory integer of a non-byte-aligned width (e.g. _BitInt(37) in an
i64) carries into the padding bits, leaving a non-canonical value that
breaks a later compare-exchange and gives wrong signed min/max. A wide
arithmetic fetch (e.g. _BitInt(256)) hit an llvm_unreachable in the libcall
path, a compiler crash. Both are fixed by emitting a compare-exchange loop
that computes the new value at width N via llvm::buildAtomicRMWValue and
writes back a canonical representation, reusing the existing
EmitAtomicCompareExchange helper, which selects the inline cmpxchg or the
__atomic_compare_exchange libcall by size. No-padding inline widths (64,
128) keep the direct atomicrmw fast path.

The libc++ bit-int.verify.cpp test only asserted that Clang rejection, so it
is removed as obsolete. It is not replaced with a positive test here: the
libc++ premerge matrix builds tests with a pinned clang that predates this
change. Whether libc++ should expose atomic _BitInt is a separate design
question for the P3666R4 discussion.

Verified against gcc-14: identical size and alignment for all widths, and
cross-compiler compare-exchange interop in both directions, confirming the
padding canonicalization matches.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/docs/LanguageExtensions.rst             |   9 +
 clang/docs/ReleaseNotes.rst                   |   6 +
 clang/lib/CodeGen/CGAtomic.cpp                | 211 ++++++++++++++++++
 clang/lib/Sema/SemaChecking.cpp               |   5 -
 clang/lib/Sema/SemaType.cpp                   |   2 -
 clang/test/CodeGen/atomic-bitint.c            |  90 ++++++++
 clang/test/Sema/builtins.c                    |   4 +-
 clang/test/SemaCXX/ext-int.cpp                |  10 +-
 libcxx/test/libcxx/atomics/bit-int.verify.cpp |  22 --
 9 files changed, 322 insertions(+), 37 deletions(-)
 create mode 100644 clang/test/CodeGen/atomic-bitint.c
 delete mode 100644 libcxx/test/libcxx/atomics/bit-int.verify.cpp

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index d79d82a175c68..5ff076d3e48ad 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -451,6 +451,15 @@ favor of the standard type.
 Note: the ABI for ``_BitInt(N)`` is still in the process of being stabilized,
 so this type should not yet be used in interfaces that require ABI stability.
 
+``_BitInt(N)`` may be used as an atomic type: ``_Atomic(_BitInt(N))``, the
+``__c11_atomic_*`` and ``__atomic_*`` builtins, and ``std::atomic`` all accept
+it for any width. Widths the target cannot operate on inline are lowered to the
+``__atomic_*`` libcalls. For a width whose representation has padding bits (``N``
+not a multiple of the type's alignment, e.g. ``_BitInt(37)``), arithmetic
+read-modify-write operations are emitted as a compare-exchange loop that computes
+at width ``N``, so the result wraps modulo ``2**N`` and the padding bits stay
+canonical.
+
 C keywords supported in all language modes
 ------------------------------------------
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7f056abfbbe24..8692da8830dff 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -265,6 +265,12 @@ Non-comprehensive list of changes in this release
 - Added support for floating point and pointer values in most ``__atomic_``
   builtins.
 
+- Atomic operations on ``_BitInt(N)`` are now supported, including
+  ``_Atomic(_BitInt(N))``, the ``__c11_atomic_*`` / ``__atomic_*`` builtins, and
+  ``std::atomic``. Widths the target cannot operate on inline use the
+  ``__atomic_*`` libcalls; arithmetic read-modify-write on a width with padding
+  bits is emitted as a compare-exchange loop computing at the value width.
+
 - Added ``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right``
   for bit rotation of unsigned integers including ``_BitInt`` types. Rotation
   counts are normalized modulo the bit-width and support negative values.
diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 270965b109943..66c059fd40e26 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Intrinsics.h"
+#include "llvm/Transforms/Utils/LowerAtomic.h"
 
 using namespace clang;
 using namespace CodeGen;
@@ -558,6 +559,195 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
   return Builder.CreateSelect(Cmp, OldVal, RHS, "newval");
 }
 
+/// Classify an atomic op as an arithmetic/bitwise read-modify-write (one that
+/// normally lowers to a single `atomicrmw`), mapping it to the matching
+/// `AtomicRMWInst::BinOp` and reporting whether the builtin returns the new
+/// value (`<op>_fetch`) rather than the old value (`fetch_<op>`). \p IsSigned
+/// selects signed vs unsigned min/max. Returns false for exchange, load, store,
+/// compare-exchange, and any non-RMW op, none of which need the _BitInt loop.
+static bool classifyBitIntRMW(AtomicExpr::AtomicOp Op, bool IsSigned,
+                              llvm::AtomicRMWInst::BinOp &BinOp,
+                              bool &ReturnsNew) {
+  using RMW = llvm::AtomicRMWInst;
+  switch (Op) {
+  case AtomicExpr::AO__c11_atomic_fetch_add:
+  case AtomicExpr::AO__hip_atomic_fetch_add:
+  case AtomicExpr::AO__opencl_atomic_fetch_add:
+  case AtomicExpr::AO__atomic_fetch_add:
+  case AtomicExpr::AO__scoped_atomic_fetch_add:
+    BinOp = RMW::Add, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_add_fetch:
+  case AtomicExpr::AO__scoped_atomic_add_fetch:
+    BinOp = RMW::Add, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_sub:
+  case AtomicExpr::AO__hip_atomic_fetch_sub:
+  case AtomicExpr::AO__opencl_atomic_fetch_sub:
+  case AtomicExpr::AO__atomic_fetch_sub:
+  case AtomicExpr::AO__scoped_atomic_fetch_sub:
+    BinOp = RMW::Sub, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_sub_fetch:
+  case AtomicExpr::AO__scoped_atomic_sub_fetch:
+    BinOp = RMW::Sub, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_and:
+  case AtomicExpr::AO__hip_atomic_fetch_and:
+  case AtomicExpr::AO__opencl_atomic_fetch_and:
+  case AtomicExpr::AO__atomic_fetch_and:
+  case AtomicExpr::AO__scoped_atomic_fetch_and:
+    BinOp = RMW::And, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_and_fetch:
+  case AtomicExpr::AO__scoped_atomic_and_fetch:
+    BinOp = RMW::And, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_or:
+  case AtomicExpr::AO__hip_atomic_fetch_or:
+  case AtomicExpr::AO__opencl_atomic_fetch_or:
+  case AtomicExpr::AO__atomic_fetch_or:
+  case AtomicExpr::AO__scoped_atomic_fetch_or:
+    BinOp = RMW::Or, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_or_fetch:
+  case AtomicExpr::AO__scoped_atomic_or_fetch:
+    BinOp = RMW::Or, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_xor:
+  case AtomicExpr::AO__hip_atomic_fetch_xor:
+  case AtomicExpr::AO__opencl_atomic_fetch_xor:
+  case AtomicExpr::AO__atomic_fetch_xor:
+  case AtomicExpr::AO__scoped_atomic_fetch_xor:
+    BinOp = RMW::Xor, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_xor_fetch:
+  case AtomicExpr::AO__scoped_atomic_xor_fetch:
+    BinOp = RMW::Xor, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_nand:
+  case AtomicExpr::AO__atomic_fetch_nand:
+  case AtomicExpr::AO__scoped_atomic_fetch_nand:
+    BinOp = RMW::Nand, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_nand_fetch:
+  case AtomicExpr::AO__scoped_atomic_nand_fetch:
+    BinOp = RMW::Nand, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_min:
+  case AtomicExpr::AO__hip_atomic_fetch_min:
+  case AtomicExpr::AO__opencl_atomic_fetch_min:
+  case AtomicExpr::AO__atomic_fetch_min:
+  case AtomicExpr::AO__scoped_atomic_fetch_min:
+    BinOp = IsSigned ? RMW::Min : RMW::UMin, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_min_fetch:
+  case AtomicExpr::AO__scoped_atomic_min_fetch:
+    BinOp = IsSigned ? RMW::Min : RMW::UMin, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__c11_atomic_fetch_max:
+  case AtomicExpr::AO__hip_atomic_fetch_max:
+  case AtomicExpr::AO__opencl_atomic_fetch_max:
+  case AtomicExpr::AO__atomic_fetch_max:
+  case AtomicExpr::AO__scoped_atomic_fetch_max:
+    BinOp = IsSigned ? RMW::Max : RMW::UMax, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_max_fetch:
+  case AtomicExpr::AO__scoped_atomic_max_fetch:
+    BinOp = IsSigned ? RMW::Max : RMW::UMax, ReturnsNew = true;
+    return true;
+  case AtomicExpr::AO__atomic_fetch_uinc:
+  case AtomicExpr::AO__scoped_atomic_fetch_uinc:
+    BinOp = RMW::UIncWrap, ReturnsNew = false;
+    return true;
+  case AtomicExpr::AO__atomic_fetch_udec:
+  case AtomicExpr::AO__scoped_atomic_fetch_udec:
+    BinOp = RMW::UDecWrap, ReturnsNew = false;
+    return true;
+  default:
+    return false;
+  }
+}
+
+/// True for a `_BitInt(N)` whose value width N differs from its in-memory width
+/// (e.g. `_BitInt(37)` occupies 64 bits), so the high bits are padding.
+static bool hasBitIntPadding(QualType T, const ASTContext &C) {
+  if (const auto *BIT = T->getAs<BitIntType>())
+    return BIT->getNumBits() != C.getTypeSize(T);
+  return false;
+}
+
+/// Map a constant C ABI memory order to an llvm ordering. A non-constant order
+/// is handled conservatively with the strongest ordering.
+static llvm::AtomicOrdering atomicOrderOrSeqCst(llvm::Value *Order) {
+  auto *C = dyn_cast<llvm::ConstantInt>(Order);
+  if (!C || !llvm::isValidAtomicOrderingCABI(C->getZExtValue()))
+    return llvm::AtomicOrdering::SequentiallyConsistent;
+  switch ((llvm::AtomicOrderingCABI)C->getZExtValue()) {
+  case llvm::AtomicOrderingCABI::relaxed:
+    return llvm::AtomicOrdering::Monotonic;
+  case llvm::AtomicOrderingCABI::consume:
+  case llvm::AtomicOrderingCABI::acquire:
+    return llvm::AtomicOrdering::Acquire;
+  case llvm::AtomicOrderingCABI::release:
+    return llvm::AtomicOrdering::Release;
+  case llvm::AtomicOrderingCABI::acq_rel:
+    return llvm::AtomicOrdering::AcquireRelease;
+  case llvm::AtomicOrderingCABI::seq_cst:
+    return llvm::AtomicOrdering::SequentiallyConsistent;
+  }
+  llvm_unreachable("invalid CABI ordering");
+}
+
+/// Emit a `_BitInt(N)` atomic read-modify-write as a compare-exchange loop. A
+/// single `atomicrmw` on the padded memory integer would carry into / compare
+/// the padding bits, and no arbitrary-width `__atomic_fetch_*` libcall exists
+/// for wide widths. The loop computes the new value at width N and writes back
+/// a canonical (extended) representation via the existing cmpxchg helper, which
+/// also picks the inline-vs-libcall form by size.
+static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
+                                      Address Ptr, Address Val1,
+                                      QualType AtomicTy,
+                                      llvm::AtomicRMWInst::BinOp BinOp,
+                                      bool ReturnsNew, llvm::Value *Order) {
+  QualType ValTy = E->getValueType();
+  llvm::AtomicOrdering AO = atomicOrderOrSeqCst(Order);
+  llvm::AtomicOrdering Failure =
+      llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO);
+
+  LValue AtomicLVal = CGF.MakeAddrLValue(Ptr, AtomicTy);
+  AtomicInfo Atomics(CGF, AtomicLVal);
+
+  llvm::Value *RHS =
+      CGF.EmitLoadOfScalar(CGF.MakeAddrLValue(Val1, ValTy), E->getExprLoc());
+
+  RValue OldRV = Atomics.EmitAtomicLoad(
+      AggValueSlot::ignored(), E->getExprLoc(),
+      /*AsValue=*/true, llvm::AtomicOrdering::Monotonic, E->isVolatile());
+  llvm::Value *Init = OldRV.getScalarVal();
+
+  llvm::BasicBlock *StartBB = CGF.Builder.GetInsertBlock();
+  llvm::BasicBlock *LoopBB = CGF.createBasicBlock("atomicrmw.start", CGF.CurFn);
+  llvm::BasicBlock *EndBB = CGF.createBasicBlock("atomicrmw.end", CGF.CurFn);
+  CGF.Builder.CreateBr(LoopBB);
+  CGF.Builder.SetInsertPoint(LoopBB);
+
+  llvm::PHINode *Old = CGF.Builder.CreatePHI(Init->getType(), 2);
+  Old->addIncoming(Init, StartBB);
+
+  // Compute at the value width via the canonical RMW lowering, so the result
+  // wraps mod 2^N and never touches the padding bits.
+  llvm::Value *New = llvm::buildAtomicRMWValue(BinOp, CGF.Builder, Old, RHS);
+
+  auto Res = Atomics.EmitAtomicCompareExchange(
+      RValue::get(Old), RValue::get(New), AO, Failure, /*IsWeak=*/true);
+  Old->addIncoming(Res.first.getScalarVal(), CGF.Builder.GetInsertBlock());
+  CGF.Builder.CreateCondBr(Res.second, EndBB, LoopBB);
+
+  CGF.Builder.SetInsertPoint(EndBB);
+  return RValue::get(ReturnsNew ? New : static_cast<llvm::Value *>(Old));
+}
+
 static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
                          Address Ptr, Address Val1, Address Val2,
                          Address ExpectedResult, llvm::Value *IsWeak,
@@ -1109,6 +1299,27 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
   LValue AtomicVal = MakeAddrLValue(Ptr, AtomicTy);
   AtomicInfo Atomics(*this, AtomicVal);
 
+  // A `_BitInt(N)` read-modify-write whose value width has padding bits, or
+  // whose size forces a libcall, cannot use a single atomicrmw: the op would
+  // carry into / compare the padding bits, and no arbitrary-width
+  // __atomic_fetch_* libcall exists. Emit a compare-exchange loop instead.
+  // Bitwise and/or/xor are exact even with padding, so only the wide case needs
+  // the loop for them. load/store/exchange/compare_exchange keep their paths.
+  if (MemTy->isBitIntType()) {
+    llvm::AtomicRMWInst::BinOp BinOp;
+    bool RMWReturnsNew;
+    if (classifyBitIntRMW(E->getOp(), MemTy->isSignedIntegerType(), BinOp,
+                          RMWReturnsNew)) {
+      bool WideOrNonPow2 = (Size & (Size - 1)) != 0 || Size > 16;
+      bool Bitwise = BinOp == llvm::AtomicRMWInst::And ||
+                     BinOp == llvm::AtomicRMWInst::Or ||
+                     BinOp == llvm::AtomicRMWInst::Xor;
+      if (WideOrNonPow2 || (hasBitIntPadding(MemTy, getContext()) && !Bitwise))
+        return emitBitIntAtomicRMWLoop(*this, E, Ptr, Val1, AtomicTy, BinOp,
+                                       RMWReturnsNew, Order);
+    }
+  }
+
   Address OriginalVal1 = Val1;
   if (ShouldCastToIntPtrTy) {
     Ptr = Atomics.castToAtomicIntPointer(Ptr);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index b8a3f48a32f24..874ce2bf1ce3a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -5460,11 +5460,6 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
                 ? 0
                 : 1);
 
-  if (ValType->isBitIntType()) {
-    Diag(Ptr->getExprLoc(), diag::err_atomic_builtin_bit_int_prohibit);
-    return ExprError();
-  }
-
   return AE;
 }
 
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index d2bb312feadc1..4a3506c281acf 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -10412,8 +10412,6 @@ QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) {
     else if (!T.isTriviallyCopyableType(Context) && getLangOpts().CPlusPlus)
       // Some other non-trivially-copyable type (probably a C++ class)
       DisallowedKind = 7;
-    else if (T->isBitIntType())
-      DisallowedKind = 8;
     else if (getLangOpts().C23 && T->isUndeducedAutoType())
       // _Atomic auto is prohibited in C23
       DisallowedKind = 9;
diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
new file mode 100644
index 0000000000000..358b530e8a792
--- /dev/null
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -0,0 +1,90 @@
+// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
+//
+// Atomic operations on _BitInt(N). load/store/exchange/compare-exchange and
+// bitwise RMW lower directly; arithmetic RMW on a padded width and any RMW on a
+// width too wide for an inline atomicrmw lower to a compare-exchange loop that
+// computes at the value width.
+
+typedef _BitInt(37)          S37;
+typedef unsigned _BitInt(37) U37;
+typedef _BitInt(64)          S64;
+typedef _BitInt(128)         S128;
+typedef _BitInt(256)         S256;
+
+// CHECK-LABEL: @ld37(
+// CHECK: load atomic i64
+S37 ld37(_Atomic(S37) *p) { return __c11_atomic_load(p, __ATOMIC_SEQ_CST); }
+
+// CHECK-LABEL: @st37(
+// CHECK: store atomic i64
+void st37(_Atomic(S37) *p, S37 v) { __c11_atomic_store(p, v, __ATOMIC_SEQ_CST); }
+
+// CHECK-LABEL: @xchg37(
+// CHECK: atomicrmw xchg ptr {{.*}} i64
+S37 xchg37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_exchange(p, v, __ATOMIC_SEQ_CST);
+}
+
+// CHECK-LABEL: @cas37(
+// CHECK: cmpxchg ptr {{.*}} i64
+_Bool cas37(_Atomic(S37) *p, S37 *e, S37 d) {
+  return __c11_atomic_compare_exchange_strong(p, e, d, __ATOMIC_SEQ_CST,
+                                              __ATOMIC_SEQ_CST);
+}
+
+// Bitwise RMW on a padded width keeps the direct atomicrmw: it is exact.
+// CHECK-LABEL: @and37(
+// CHECK: atomicrmw and ptr {{.*}} i64
+// CHECK-NOT: cmpxchg
+S37 and37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Arithmetic RMW on a padded width becomes a compare-exchange loop, not a bare
+// atomicrmw that would carry into the padding bits.
+// CHECK-LABEL: @add37(
+// CHECK: atomicrmw.start:
+// CHECK: cmpxchg weak ptr {{.*}} i64
+// CHECK-NOT: atomicrmw add
+S37 add37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Signed min is computed at the value width, so the sign bit is at bit N-1.
+// CHECK-LABEL: @min37(
+// CHECK: icmp sle i37
+// CHECK: select i1
+// CHECK: cmpxchg weak ptr {{.*}} i64
+U37 min37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
+}
+
+// No padding: direct atomicrmw, no loop.
+// CHECK-LABEL: @add64(
+// CHECK: atomicrmw add ptr {{.*}} i64
+// CHECK-NOT: cmpxchg
+S64 add64(_Atomic(S64) *p, S64 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// CHECK-LABEL: @add128(
+// CHECK: atomicrmw add ptr {{.*}} i128
+S128 add128(_Atomic(S128) *p, S128 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Wide: no inline atomicrmw and no arbitrary-width __atomic_fetch_add libcall,
+// so the loop calls __atomic_compare_exchange.
+// CHECK-LABEL: @add256(
+// CHECK: call {{.*}}@__atomic_compare_exchange
+// CHECK-NOT: cmpxchg
+S256 add256(_Atomic(S256) *p, S256 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Wide bitwise also needs the loop: the wide path has no inline atomicrmw.
+// CHECK-LABEL: @or256(
+// CHECK: call {{.*}}@__atomic_compare_exchange
+S256 or256(_Atomic(S256) *p, S256 v) {
+  return __c11_atomic_fetch_or(p, v, __ATOMIC_SEQ_CST);
+}
diff --git a/clang/test/Sema/builtins.c b/clang/test/Sema/builtins.c
index b669ee68cdd95..57e0eefdb772b 100644
--- a/clang/test/Sema/builtins.c
+++ b/clang/test/Sema/builtins.c
@@ -281,7 +281,7 @@ void test_ei_i42i(_BitInt(42) *ptr, int value) {
   // expected-warning at +1 {{the semantics of this intrinsic changed with GCC version 4.4 - the newer semantics are provided here}}
   __sync_nand_and_fetch(ptr, value); // expected-error {{atomic memory operand must have a power-of-two size}}
 
-  __atomic_fetch_add(ptr, 1, 0); // expected-error {{argument to atomic builtin of type '_BitInt' is not supported}}
+  __atomic_fetch_add(ptr, 1, 0); // expect success: the GNU atomic builtins support _BitInt
 }
 
 void test_ei_i64i(_BitInt(64) *ptr, int value) {
@@ -289,7 +289,7 @@ void test_ei_i64i(_BitInt(64) *ptr, int value) {
   // expected-warning at +1 {{the semantics of this intrinsic changed with GCC version 4.4 - the newer semantics are provided here}}
   __sync_nand_and_fetch(ptr, value); // expect success
 
-  __atomic_fetch_add(ptr, 1, 0); // expected-error {{argument to atomic builtin of type '_BitInt' is not supported}}
+  __atomic_fetch_add(ptr, 1, 0); // expect success
 }
 
 void test_ei_ii42(int *ptr, _BitInt(42) value) {
diff --git a/clang/test/SemaCXX/ext-int.cpp b/clang/test/SemaCXX/ext-int.cpp
index 281ae3d3c1779..f62a07a84200e 100644
--- a/clang/test/SemaCXX/ext-int.cpp
+++ b/clang/test/SemaCXX/ext-int.cpp
@@ -121,13 +121,11 @@ _Complex _BitInt(3) Cmplx;
 // expected-error at +1{{'_Complex _BitInt' is invalid}}
 typedef _Complex _BitInt(3) Cmp;
 
-// Reject cases of _Atomic:
-// expected-error at +1{{_Atomic cannot be applied to integer type '_BitInt(4)'}}
-_Atomic _BitInt(4) TooSmallAtomic;
-// expected-error at +1{{_Atomic cannot be applied to integer type '_BitInt(9)'}}
+// _Atomic accepts any _BitInt width: small and non-power-of-2 included.
+// Sizes the target cannot lower inline use the __atomic_* libcalls.
+_Atomic _BitInt(4) SmallAtomic;
 _Atomic _BitInt(9) NotPow2Atomic;
-// expected-error at +1{{_Atomic cannot be applied to integer type '_BitInt(128)'}}
-_Atomic _BitInt(128) JustRightAtomic;
+_Atomic _BitInt(128) WideAtomic;
 
 // Test result types of Unary/Bitwise/Binary Operations:
 void Ops() {
diff --git a/libcxx/test/libcxx/atomics/bit-int.verify.cpp b/libcxx/test/libcxx/atomics/bit-int.verify.cpp
deleted file mode 100644
index 03880a1b6215c..0000000000000
--- a/libcxx/test/libcxx/atomics/bit-int.verify.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <atomic>
-
-// Make sure that `std::atomic` doesn't work with `_BitInt`. The intent is to
-// disable them for now until their behavior can be designed better later.
-// See https://reviews.llvm.org/D84049 for details.
-
-// UNSUPPORTED: c++03
-
-#include <atomic>
-
-void f() {
-  // expected-error@*:*1 {{_Atomic cannot be applied to integer type '_BitInt(32)'}}
-  std::atomic<_BitInt(32)> x(42);
-}

>From 92e21eaa61bf0f26c3ba825545845f531d79c4d6 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Tue, 23 Jun 2026 11:53:49 +0200
Subject: [PATCH 2/9] [Clang][POC] Add C23/C2y Sema tests for atomic _BitInt(N)

C23 requires the type-generic atomic interfaces to accept _BitInt(N), so
_Atomic(_BitInt(N)) is well-formed at every width. Add a Sema acceptance
test covering the _Atomic specifier and the __c11_atomic_*/__atomic_*
builtins in C23 and C2y modes, and a -std=c2y run of the codegen test.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/test/CodeGen/atomic-bitint.c |  1 +
 clang/test/Sema/atomic-bitint.c    | 40 ++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+)
 create mode 100644 clang/test/Sema/atomic-bitint.c

diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
index 358b530e8a792..9fa259776bf62 100644
--- a/clang/test/CodeGen/atomic-bitint.c
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c2y -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
 //
 // Atomic operations on _BitInt(N). load/store/exchange/compare-exchange and
 // bitwise RMW lower directly; arithmetic RMW on a padded width and any RMW on a
diff --git a/clang/test/Sema/atomic-bitint.c b/clang/test/Sema/atomic-bitint.c
new file mode 100644
index 0000000000000..1466412bae732
--- /dev/null
+++ b/clang/test/Sema/atomic-bitint.c
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify -triple x86_64-unknown-linux-gnu -std=c23
+// RUN: %clang_cc1 %s -fsyntax-only -verify -triple x86_64-unknown-linux-gnu -std=c2y
+//
+// C23 requires the type-generic atomic interfaces to accept _BitInt(N) for
+// every N, so _Atomic(_BitInt(N)) is well-formed at every width. Widths past
+// 128 are x86-only.
+
+// expected-no-diagnostics
+
+_Atomic(_BitInt(4))   a4;    // small
+_Atomic(_BitInt(9))   a9;    // non-power-of-two
+_Atomic(_BitInt(37))  a37;   // padded
+_Atomic(_BitInt(64))  a64;
+_Atomic(_BitInt(128)) a128;
+_Atomic(_BitInt(256)) a256;  // wider than any inline atomic
+
+// The _Atomic qualifier spelling is equally valid.
+_Atomic _BitInt(9) q9;
+
+static_assert(sizeof(_Atomic(_BitInt(37))) == 8);
+static_assert(sizeof(_Atomic(_BitInt(128))) == 16);
+static_assert(sizeof(_Atomic(_BitInt(256))) == 32);
+
+void c11_builtins(_Atomic(_BitInt(37)) *p, _BitInt(37) v, _BitInt(37) *e) {
+  (void)__c11_atomic_load(p, __ATOMIC_SEQ_CST);
+  __c11_atomic_store(p, v, __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_exchange(p, v, __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_compare_exchange_strong(p, e, v, __ATOMIC_SEQ_CST,
+                                             __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
+  (void)__c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
+}
+
+// The GNU __atomic_* builtins take a plain _BitInt pointer.
+void gnu_builtins(_BitInt(37) *p, _BitInt(37) v) {
+  (void)__atomic_load_n(p, __ATOMIC_SEQ_CST);
+  __atomic_store_n(p, v, __ATOMIC_SEQ_CST);
+  (void)__atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}

>From c0e011fb61086a5a0cae13c54abe21ab312e85c7 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Tue, 23 Jun 2026 13:51:48 +0200
Subject: [PATCH 3/9] [Clang][POC] Extend atomic _BitInt Sema test: width 4096
 + RISC-V

Add a _BitInt(4096) acceptance case and a riscv64 RUN line. The atomic
code imposes no width cap of its own, so the only limit is the target's
getMaxBitIntWidth(); x86 and RISC-V allow widths past 128, others cap at
128. Correct the comment that claimed wide widths were x86-only.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/test/Sema/atomic-bitint.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/clang/test/Sema/atomic-bitint.c b/clang/test/Sema/atomic-bitint.c
index 1466412bae732..fbb4c518438fb 100644
--- a/clang/test/Sema/atomic-bitint.c
+++ b/clang/test/Sema/atomic-bitint.c
@@ -1,18 +1,21 @@
 // RUN: %clang_cc1 %s -fsyntax-only -verify -triple x86_64-unknown-linux-gnu -std=c23
 // RUN: %clang_cc1 %s -fsyntax-only -verify -triple x86_64-unknown-linux-gnu -std=c2y
+// RUN: %clang_cc1 %s -fsyntax-only -verify -triple riscv64-unknown-linux-gnu -std=c23
 //
 // C23 requires the type-generic atomic interfaces to accept _BitInt(N) for
-// every N, so _Atomic(_BitInt(N)) is well-formed at every width. Widths past
-// 128 are x86-only.
+// every N, so _Atomic(_BitInt(N)) is well-formed at every width. The atomic
+// code imposes no width cap of its own; widths past 128 are available wherever
+// the target accepts _BitInt > 128 (x86 and RISC-V today).
 
 // expected-no-diagnostics
 
-_Atomic(_BitInt(4))   a4;    // small
-_Atomic(_BitInt(9))   a9;    // non-power-of-two
-_Atomic(_BitInt(37))  a37;   // padded
-_Atomic(_BitInt(64))  a64;
-_Atomic(_BitInt(128)) a128;
-_Atomic(_BitInt(256)) a256;  // wider than any inline atomic
+_Atomic(_BitInt(4))    a4;     // small
+_Atomic(_BitInt(9))    a9;     // non-power-of-two
+_Atomic(_BitInt(37))   a37;    // padded
+_Atomic(_BitInt(64))   a64;
+_Atomic(_BitInt(128))  a128;
+_Atomic(_BitInt(256))  a256;   // wider than any inline atomic
+_Atomic(_BitInt(4096)) a4096;  // far past the inline range
 
 // The _Atomic qualifier spelling is equally valid.
 _Atomic _BitInt(9) q9;

>From 92e2068b4837befaa51c040c0c882ea7f10cd57f Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Fri, 26 Jun 2026 21:21:00 +0200
Subject: [PATCH 4/9] [Clang][NFC] Address review: atomic _BitInt diagnostic
 list and cast

Drop the now-unreachable _BitInt ("integer") option from
err_atomic_specifier_bad_type and renumber the _Atomic-auto/C23 case to
fill the gap; the rendered text is unchanged. Use static_cast instead of
a C-style cast in atomicOrderOrSeqCst.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ++--
 clang/lib/CodeGen/CGAtomic.cpp                   | 2 +-
 clang/lib/Sema/SemaType.cpp                      | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index cde99dfb16ec5..414357c5a7c73 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7475,8 +7475,8 @@ def err_func_def_incomplete_result : Error<
 def err_atomic_specifier_bad_type
     : Error<"_Atomic cannot be applied to "
             "%select{incomplete |array |function |reference |atomic |qualified "
-            "|sizeless ||integer |}0type "
-            "%1 %select{|||||||which is not trivially copyable||in C23}0">;
+            "|sizeless ||}0type "
+            "%1 %select{|||||||which is not trivially copyable|in C23}0">;
 def warn_atomic_member_access : Warning<
   "accessing a member of an atomic structure or union is undefined behavior">,
   InGroup<DiagGroup<"atomic-access">>, DefaultError;
diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 66c059fd40e26..820849f5974c0 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -683,7 +683,7 @@ static llvm::AtomicOrdering atomicOrderOrSeqCst(llvm::Value *Order) {
   auto *C = dyn_cast<llvm::ConstantInt>(Order);
   if (!C || !llvm::isValidAtomicOrderingCABI(C->getZExtValue()))
     return llvm::AtomicOrdering::SequentiallyConsistent;
-  switch ((llvm::AtomicOrderingCABI)C->getZExtValue()) {
+  switch (static_cast<llvm::AtomicOrderingCABI>(C->getZExtValue())) {
   case llvm::AtomicOrderingCABI::relaxed:
     return llvm::AtomicOrdering::Monotonic;
   case llvm::AtomicOrderingCABI::consume:
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 4a3506c281acf..f76244d5f2871 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -10414,7 +10414,7 @@ QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) {
       DisallowedKind = 7;
     else if (getLangOpts().C23 && T->isUndeducedAutoType())
       // _Atomic auto is prohibited in C23
-      DisallowedKind = 9;
+      DisallowedKind = 8;
 
     if (DisallowedKind != -1) {
       Diag(Loc, diag::err_atomic_specifier_bad_type) << DisallowedKind << T;

>From ece57436ed0217f4c1ad4ca92f10182793ec109f Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Fri, 26 Jun 2026 21:21:02 +0200
Subject: [PATCH 5/9] [Clang][NFC] Regenerate atomic-bitint.c checks with
 update_cc_test_checks

The prior spot-checks did not show the lowering. Regenerate complete
check lines so the compare-exchange loop, the width-N arithmetic, and the
sext/zext memory canonicalization are all visible.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/test/CodeGen/atomic-bitint.c | 350 ++++++++++++++++++++++++++---
 1 file changed, 321 insertions(+), 29 deletions(-)

diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
index 9fa259776bf62..6476c26a0f0dd 100644
--- a/clang/test/CodeGen/atomic-bitint.c
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -1,3 +1,4 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
 // RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
 // RUN: %clang_cc1 -std=c2y -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
 //
@@ -12,80 +13,371 @@ typedef _BitInt(64)          S64;
 typedef _BitInt(128)         S128;
 typedef _BitInt(256)         S256;
 
-// CHECK-LABEL: @ld37(
-// CHECK: load atomic i64
+// CHECK-LABEL: define dso_local i64 @ld37(
+// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load atomic i64, ptr [[TMP0]] seq_cst, align 8
+// CHECK-NEXT:    store i64 [[TMP1]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    store i37 [[LOADEDV]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP3]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 S37 ld37(_Atomic(S37) *p) { return __c11_atomic_load(p, __ATOMIC_SEQ_CST); }
 
-// CHECK-LABEL: @st37(
-// CHECK: store atomic i64
+// CHECK-LABEL: define dso_local void @st37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    store atomic i64 [[TMP3]], ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    ret void
+//
 void st37(_Atomic(S37) *p, S37 v) { __c11_atomic_store(p, v, __ATOMIC_SEQ_CST); }
 
-// CHECK-LABEL: @xchg37(
-// CHECK: atomicrmw xchg ptr {{.*}} i64
+// CHECK-LABEL: define dso_local i64 @xchg37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = atomicrmw xchg ptr [[TMP1]], i64 [[TMP3]] seq_cst, align 8
+// CHECK-NEXT:    store i64 [[TMP4]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP5]] to i37
+// CHECK-NEXT:    store i37 [[LOADEDV3]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP6]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 S37 xchg37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_exchange(p, v, __ATOMIC_SEQ_CST);
 }
 
-// CHECK-LABEL: @cas37(
-// CHECK: cmpxchg ptr {{.*}} i64
+// CHECK-LABEL: define dso_local zeroext i1 @cas37(
+// CHECK-SAME: ptr noundef [[P:%.*]], ptr noundef [[E:%.*]], i64 noundef [[D_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[D:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[E_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[D_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[CMPXCHG_BOOL:%.*]] = alloca i8, align 1
+// CHECK-NEXT:    store i64 [[D_COERCE]], ptr [[D]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[D]], align 8
+// CHECK-NEXT:    [[D1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[E]], ptr [[E_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[D1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[D_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[E_ADDR]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[D_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = load i64, ptr [[TMP2]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP5]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP6]], 0
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
+// CHECK-NEXT:    br i1 [[TMP8]], label %[[CMPXCHG_CONTINUE:.*]], label %[[CMPXCHG_STORE_EXPECTED:.*]]
+// CHECK:       [[CMPXCHG_STORE_EXPECTED]]:
+// CHECK-NEXT:    store i64 [[TMP7]], ptr [[TMP2]], align 8
+// CHECK-NEXT:    br label %[[CMPXCHG_CONTINUE]]
+// CHECK:       [[CMPXCHG_CONTINUE]]:
+// CHECK-NEXT:    [[STOREDV3:%.*]] = zext i1 [[TMP8]] to i8
+// CHECK-NEXT:    store i8 [[STOREDV3]], ptr [[CMPXCHG_BOOL]], align 1
+// CHECK-NEXT:    [[TMP9:%.*]] = load i8, ptr [[CMPXCHG_BOOL]], align 1
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = icmp ne i8 [[TMP9]], 0
+// CHECK-NEXT:    ret i1 [[LOADEDV4]]
+//
 _Bool cas37(_Atomic(S37) *p, S37 *e, S37 d) {
   return __c11_atomic_compare_exchange_strong(p, e, d, __ATOMIC_SEQ_CST,
                                               __ATOMIC_SEQ_CST);
 }
 
 // Bitwise RMW on a padded width keeps the direct atomicrmw: it is exact.
-// CHECK-LABEL: @and37(
-// CHECK: atomicrmw and ptr {{.*}} i64
-// CHECK-NOT: cmpxchg
+// CHECK-LABEL: define dso_local i64 @and37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = atomicrmw and ptr [[TMP1]], i64 [[TMP3]] seq_cst, align 8
+// CHECK-NEXT:    store i64 [[TMP4]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP5]] to i37
+// CHECK-NEXT:    store i37 [[LOADEDV3]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP6]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 S37 and37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
 }
 
 // Arithmetic RMW on a padded width becomes a compare-exchange loop, not a bare
 // atomicrmw that would carry into the padding bits.
-// CHECK-LABEL: @add37(
-// CHECK: atomicrmw.start:
-// CHECK: cmpxchg weak ptr {{.*}} i64
-// CHECK-NOT: atomicrmw add
+// CHECK-LABEL: define dso_local i64 @add37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] monotonic, align 8
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[ATOMIC_LOAD]] to i37
+// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
+// CHECK:       [[ATOMICRMW_START]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i37 [ [[LOADEDV4]], %[[ENTRY]] ], [ [[LOADEDV7:%.*]], %[[ATOMICRMW_START]] ]
+// CHECK-NEXT:    [[NEW:%.*]] = add i37 [[TMP4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[TMP4]] to i64
+// CHECK-NEXT:    [[STOREDV6:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    [[TMP5:%.*]] = cmpxchg weak ptr [[TMP1]], i64 [[STOREDV5]], i64 [[STOREDV6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = extractvalue { i64, i1 } [[TMP5]], 0
+// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP5]], 1
+// CHECK-NEXT:    [[LOADEDV7]] = trunc i64 [[TMP6]] to i37
+// CHECK-NEXT:    br i1 [[TMP7]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
+// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    store i37 [[TMP4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP8:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP8]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 S37 add37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
 }
 
 // Signed min is computed at the value width, so the sign bit is at bit N-1.
-// CHECK-LABEL: @min37(
-// CHECK: icmp sle i37
-// CHECK: select i1
-// CHECK: cmpxchg weak ptr {{.*}} i64
+// CHECK-LABEL: define dso_local i64 @min37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] monotonic, align 8
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[ATOMIC_LOAD]] to i37
+// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
+// CHECK:       [[ATOMICRMW_START]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i37 [ [[LOADEDV4]], %[[ENTRY]] ], [ [[LOADEDV7:%.*]], %[[ATOMICRMW_START]] ]
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp sle i37 [[TMP4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[TMP4]], i37 [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[TMP4]] to i64
+// CHECK-NEXT:    [[STOREDV6:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg weak ptr [[TMP1]], i64 [[STOREDV5]], i64 [[STOREDV6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP6]], 0
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
+// CHECK-NEXT:    [[LOADEDV7]] = trunc i64 [[TMP7]] to i37
+// CHECK-NEXT:    br i1 [[TMP8]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
+// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    store i37 [[TMP4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP9:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP9]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
 U37 min37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
 }
 
 // No padding: direct atomicrmw, no loop.
-// CHECK-LABEL: @add64(
-// CHECK: atomicrmw add ptr {{.*}} i64
-// CHECK-NOT: cmpxchg
+// CHECK-LABEL: define dso_local i64 @add64(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[TMP1]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = atomicrmw add ptr [[TMP0]], i64 [[TMP2]] seq_cst, align 8
+// CHECK-NEXT:    store i64 [[TMP3]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    ret i64 [[TMP4]]
+//
 S64 add64(_Atomic(S64) *p, S64 v) {
   return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
 }
 
-// CHECK-LABEL: @add128(
-// CHECK: atomicrmw add ptr {{.*}} i128
+// CHECK-LABEL: define dso_local i128 @add128(
+// CHECK-SAME: ptr noundef [[P:%.*]], i128 noundef [[V:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i128, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i128, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i128, align 16
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store i128 [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load i128, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    store i128 [[TMP1]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i128, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = atomicrmw add ptr [[TMP0]], i128 [[TMP2]] seq_cst, align 16
+// CHECK-NEXT:    store i128 [[TMP3]], ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT:    [[TMP4:%.*]] = load i128, ptr [[ATOMIC_TEMP]], align 16
+// CHECK-NEXT:    ret i128 [[TMP4]]
+//
 S128 add128(_Atomic(S128) *p, S128 v) {
   return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
 }
 
 // Wide: no inline atomicrmw and no arbitrary-width __atomic_fetch_add libcall,
 // so the loop calls __atomic_compare_exchange.
-// CHECK-LABEL: @add256(
-// CHECK: call {{.*}}@__atomic_compare_exchange
-// CHECK-NOT: cmpxchg
+// CHECK-LABEL: define dso_local void @add256(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(i256) align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(i256) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP1:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP2:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[V:%.*]] = load i256, ptr [[TMP0]], align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store i256 [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i256, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    store i256 [[TMP2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i256, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 0)
+// CHECK-NEXT:    [[TMP4:%.*]] = load i256, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
+// CHECK:       [[ATOMICRMW_START]]:
+// CHECK-NEXT:    [[TMP5:%.*]] = phi i256 [ [[TMP4]], %[[ENTRY]] ], [ [[TMP6:%.*]], %[[ATOMICRMW_START]] ]
+// CHECK-NEXT:    [[NEW:%.*]] = add i256 [[TMP5]], [[TMP3]]
+// CHECK-NEXT:    store i256 [[TMP5]], ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    store i256 [[NEW]], ptr [[ATOMIC_TEMP2]], align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
+// CHECK-NEXT:    [[TMP6]] = load i256, ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
+// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    store i256 [[TMP5]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    store i256 [[TMP7]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    ret void
+//
 S256 add256(_Atomic(S256) *p, S256 v) {
   return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
 }
 
 // Wide bitwise also needs the loop: the wide path has no inline atomicrmw.
-// CHECK-LABEL: @or256(
-// CHECK: call {{.*}}@__atomic_compare_exchange
+// CHECK-LABEL: define dso_local void @or256(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret(i256) align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(i256) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP1:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP2:%.*]] = alloca i256, align 8
+// CHECK-NEXT:    [[V:%.*]] = load i256, ptr [[TMP0]], align 8
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    store i256 [[V]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i256, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    store i256 [[TMP2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i256, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 0)
+// CHECK-NEXT:    [[TMP4:%.*]] = load i256, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
+// CHECK:       [[ATOMICRMW_START]]:
+// CHECK-NEXT:    [[TMP5:%.*]] = phi i256 [ [[TMP4]], %[[ENTRY]] ], [ [[TMP6:%.*]], %[[ATOMICRMW_START]] ]
+// CHECK-NEXT:    [[NEW:%.*]] = or i256 [[TMP5]], [[TMP3]]
+// CHECK-NEXT:    store i256 [[TMP5]], ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    store i256 [[NEW]], ptr [[ATOMIC_TEMP2]], align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
+// CHECK-NEXT:    [[TMP6]] = load i256, ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
+// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    store i256 [[TMP5]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    store i256 [[TMP7]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    ret void
+//
 S256 or256(_Atomic(S256) *p, S256 v) {
   return __c11_atomic_fetch_or(p, v, __ATOMIC_SEQ_CST);
 }

>From 1c87c9994199bd82766b4d6d6f5edf392c225ba2 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sat, 27 Jun 2026 09:23:04 +0200
Subject: [PATCH 6/9] [Clang] Carry the raw representation in the _BitInt
 atomic RMW loop

The compare-exchange loop for padded and wide _BitInt atomics formed its
cmpxchg expected by re-canonicalizing the loaded value (sign/zero-extending
the truncated old). An object whose padding bits were non-canonical, e.g.
written through a union, then never matched that expected, so the cmpxchg
failed every iteration and the read-modify-write spun forever.

Reuse the existing EmitAtomicUpdate loop, which carries the raw loaded
representation as the expected and writes back a canonical desired computed
at value width N. The object converges on the first iteration regardless of
its padding, and the value it stores is canonical. See P0528.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/lib/CodeGen/CGAtomic.cpp     |  48 +++++-------
 clang/test/CodeGen/atomic-bitint.c | 120 ++++++++++++++---------------
 2 files changed, 77 insertions(+), 91 deletions(-)

diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 820849f5974c0..0043c79b398ee 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -702,9 +702,13 @@ static llvm::AtomicOrdering atomicOrderOrSeqCst(llvm::Value *Order) {
 /// Emit a `_BitInt(N)` atomic read-modify-write as a compare-exchange loop. A
 /// single `atomicrmw` on the padded memory integer would carry into / compare
 /// the padding bits, and no arbitrary-width `__atomic_fetch_*` libcall exists
-/// for wide widths. The loop computes the new value at width N and writes back
-/// a canonical (extended) representation via the existing cmpxchg helper, which
-/// also picks the inline-vs-libcall form by size.
+/// for wide widths.
+///
+/// The update computes at value width N (so the result wraps mod 2^N and is
+/// independent of padding). EmitAtomicUpdate carries the raw loaded
+/// representation as the cmpxchg expected, so an object with non-canonical
+/// padding (e.g. written through a union) still converges instead of spinning
+/// forever; the desired it writes back is canonical. See P0528.
 static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
                                       Address Ptr, Address Val1,
                                       QualType AtomicTy,
@@ -712,8 +716,6 @@ static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
                                       bool ReturnsNew, llvm::Value *Order) {
   QualType ValTy = E->getValueType();
   llvm::AtomicOrdering AO = atomicOrderOrSeqCst(Order);
-  llvm::AtomicOrdering Failure =
-      llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO);
 
   LValue AtomicLVal = CGF.MakeAddrLValue(Ptr, AtomicTy);
   AtomicInfo Atomics(CGF, AtomicLVal);
@@ -721,31 +723,17 @@ static RValue emitBitIntAtomicRMWLoop(CodeGenFunction &CGF, AtomicExpr *E,
   llvm::Value *RHS =
       CGF.EmitLoadOfScalar(CGF.MakeAddrLValue(Val1, ValTy), E->getExprLoc());
 
-  RValue OldRV = Atomics.EmitAtomicLoad(
-      AggValueSlot::ignored(), E->getExprLoc(),
-      /*AsValue=*/true, llvm::AtomicOrdering::Monotonic, E->isVolatile());
-  llvm::Value *Init = OldRV.getScalarVal();
-
-  llvm::BasicBlock *StartBB = CGF.Builder.GetInsertBlock();
-  llvm::BasicBlock *LoopBB = CGF.createBasicBlock("atomicrmw.start", CGF.CurFn);
-  llvm::BasicBlock *EndBB = CGF.createBasicBlock("atomicrmw.end", CGF.CurFn);
-  CGF.Builder.CreateBr(LoopBB);
-  CGF.Builder.SetInsertPoint(LoopBB);
-
-  llvm::PHINode *Old = CGF.Builder.CreatePHI(Init->getType(), 2);
-  Old->addIncoming(Init, StartBB);
-
-  // Compute at the value width via the canonical RMW lowering, so the result
-  // wraps mod 2^N and never touches the padding bits.
-  llvm::Value *New = llvm::buildAtomicRMWValue(BinOp, CGF.Builder, Old, RHS);
-
-  auto Res = Atomics.EmitAtomicCompareExchange(
-      RValue::get(Old), RValue::get(New), AO, Failure, /*IsWeak=*/true);
-  Old->addIncoming(Res.first.getScalarVal(), CGF.Builder.GetInsertBlock());
-  CGF.Builder.CreateCondBr(Res.second, EndBB, LoopBB);
-
-  CGF.Builder.SetInsertPoint(EndBB);
-  return RValue::get(ReturnsNew ? New : static_cast<llvm::Value *>(Old));
+  llvm::Value *Old = nullptr, *New = nullptr;
+  Atomics.EmitAtomicUpdate(
+      AO,
+      [&](RValue OldRV) {
+        Old = OldRV.getScalarVal();
+        New = llvm::buildAtomicRMWValue(BinOp, CGF.Builder, Old, RHS);
+        return RValue::get(New);
+      },
+      E->isVolatile());
+
+  return RValue::get(ReturnsNew ? New : Old);
 }
 
 static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
index 6476c26a0f0dd..bc1e165fd90e3 100644
--- a/clang/test/CodeGen/atomic-bitint.c
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -178,6 +178,7 @@ S37 and37(_Atomic(S37) *p, S37 v) {
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
 // CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
 // CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
 // CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
@@ -191,23 +192,23 @@ S37 and37(_Atomic(S37) *p, S37 v) {
 // CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
-// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] monotonic, align 8
-// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[ATOMIC_LOAD]] to i37
-// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
-// CHECK:       [[ATOMICRMW_START]]:
-// CHECK-NEXT:    [[TMP4:%.*]] = phi i37 [ [[LOADEDV4]], %[[ENTRY]] ], [ [[LOADEDV7:%.*]], %[[ATOMICRMW_START]] ]
-// CHECK-NEXT:    [[NEW:%.*]] = add i37 [[TMP4]], [[LOADEDV3]]
-// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[TMP4]] to i64
-// CHECK-NEXT:    [[STOREDV6:%.*]] = sext i37 [[NEW]] to i64
-// CHECK-NEXT:    [[TMP5:%.*]] = cmpxchg weak ptr [[TMP1]], i64 [[STOREDV5]], i64 [[STOREDV6]] seq_cst seq_cst, align 8
-// CHECK-NEXT:    [[TMP6:%.*]] = extractvalue { i64, i1 } [[TMP5]], 0
-// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP5]], 1
-// CHECK-NEXT:    [[LOADEDV7]] = trunc i64 [[TMP6]] to i37
-// CHECK-NEXT:    br i1 [[TMP7]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
-// CHECK:       [[ATOMICRMW_END]]:
-// CHECK-NEXT:    store i37 [[TMP4]], ptr [[RETVAL]], align 8
-// CHECK-NEXT:    [[TMP8:%.*]] = load i37, ptr [[RETVAL]], align 8
-// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP8]] to i64
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP7:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[NEW:%.*]] = add i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP5]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP7]] = extractvalue { i64, i1 } [[TMP6]], 0
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
+// CHECK-NEXT:    br i1 [[TMP8]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP9:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP9]] to i64
 // CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
 //
 S37 add37(_Atomic(S37) *p, S37 v) {
@@ -223,6 +224,7 @@ S37 add37(_Atomic(S37) *p, S37 v) {
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
 // CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
 // CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
 // CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
@@ -236,24 +238,24 @@ S37 add37(_Atomic(S37) *p, S37 v) {
 // CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
-// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] monotonic, align 8
-// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[ATOMIC_LOAD]] to i37
-// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
-// CHECK:       [[ATOMICRMW_START]]:
-// CHECK-NEXT:    [[TMP4:%.*]] = phi i37 [ [[LOADEDV4]], %[[ENTRY]] ], [ [[LOADEDV7:%.*]], %[[ATOMICRMW_START]] ]
-// CHECK-NEXT:    [[TMP5:%.*]] = icmp sle i37 [[TMP4]], [[LOADEDV3]]
-// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[TMP4]], i37 [[LOADEDV3]]
-// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[TMP4]] to i64
-// CHECK-NEXT:    [[STOREDV6:%.*]] = sext i37 [[NEW]] to i64
-// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg weak ptr [[TMP1]], i64 [[STOREDV5]], i64 [[STOREDV6]] seq_cst seq_cst, align 8
-// CHECK-NEXT:    [[TMP7:%.*]] = extractvalue { i64, i1 } [[TMP6]], 0
-// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
-// CHECK-NEXT:    [[LOADEDV7]] = trunc i64 [[TMP7]] to i37
-// CHECK-NEXT:    br i1 [[TMP8]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
-// CHECK:       [[ATOMICRMW_END]]:
-// CHECK-NEXT:    store i37 [[TMP4]], ptr [[RETVAL]], align 8
-// CHECK-NEXT:    [[TMP9:%.*]] = load i37, ptr [[RETVAL]], align 8
-// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP9]] to i64
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP8:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp sle i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[LOADEDV4]], i37 [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP8]] = extractvalue { i64, i1 } [[TMP7]], 0
+// CHECK-NEXT:    [[TMP9:%.*]] = extractvalue { i64, i1 } [[TMP7]], 1
+// CHECK-NEXT:    br i1 [[TMP9]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP10:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP10]] to i64
 // CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
 //
 U37 min37(_Atomic(S37) *p, S37 v) {
@@ -309,7 +311,7 @@ S128 add128(_Atomic(S128) *p, S128 v) {
 // so the loop calls __atomic_compare_exchange.
 // CHECK-LABEL: define dso_local void @add256(
 // CHECK-SAME: ptr dead_on_unwind noalias writable sret(i256) align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(i256) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:  [[ENTRY:.*:]]
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i256, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i256, align 8
@@ -323,21 +325,19 @@ S128 add128(_Atomic(S128) *p, S128 v) {
 // CHECK-NEXT:    [[TMP2:%.*]] = load i256, ptr [[V_ADDR]], align 8
 // CHECK-NEXT:    store i256 [[TMP2]], ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load i256, ptr [[DOTATOMICTMP]], align 8
-// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 0)
+// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 5)
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
 // CHECK-NEXT:    [[TMP4:%.*]] = load i256, ptr [[ATOMIC_TEMP]], align 8
-// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
-// CHECK:       [[ATOMICRMW_START]]:
-// CHECK-NEXT:    [[TMP5:%.*]] = phi i256 [ [[TMP4]], %[[ENTRY]] ], [ [[TMP6:%.*]], %[[ATOMICRMW_START]] ]
-// CHECK-NEXT:    [[NEW:%.*]] = add i256 [[TMP5]], [[TMP3]]
-// CHECK-NEXT:    store i256 [[TMP5]], ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    [[NEW:%.*]] = add i256 [[TMP4]], [[TMP3]]
 // CHECK-NEXT:    store i256 [[NEW]], ptr [[ATOMIC_TEMP2]], align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
-// CHECK-NEXT:    [[TMP6]] = load i256, ptr [[ATOMIC_TEMP1]], align 8
-// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
-// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    call void @__atomic_store(i64 noundef 32, ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5)
+// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], ptr noundef [[ATOMIC_TEMP1]], i32 noundef 5, i32 noundef 5)
+// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i256 [[TMP4]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
 // CHECK-NEXT:    store i256 [[TMP5]], ptr [[AGG_RESULT]], align 8
-// CHECK-NEXT:    [[TMP7:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
-// CHECK-NEXT:    store i256 [[TMP7]], ptr [[AGG_RESULT]], align 8
 // CHECK-NEXT:    ret void
 //
 S256 add256(_Atomic(S256) *p, S256 v) {
@@ -347,7 +347,7 @@ S256 add256(_Atomic(S256) *p, S256 v) {
 // Wide bitwise also needs the loop: the wide path has no inline atomicrmw.
 // CHECK-LABEL: define dso_local void @or256(
 // CHECK-SAME: ptr dead_on_unwind noalias writable sret(i256) align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], ptr noundef byval(i256) align 8 [[TMP0:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:  [[ENTRY:.*:]]
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i256, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i256, align 8
@@ -361,21 +361,19 @@ S256 add256(_Atomic(S256) *p, S256 v) {
 // CHECK-NEXT:    [[TMP2:%.*]] = load i256, ptr [[V_ADDR]], align 8
 // CHECK-NEXT:    store i256 [[TMP2]], ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load i256, ptr [[DOTATOMICTMP]], align 8
-// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 0)
+// CHECK-NEXT:    call void @__atomic_load(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], i32 noundef 5)
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
 // CHECK-NEXT:    [[TMP4:%.*]] = load i256, ptr [[ATOMIC_TEMP]], align 8
-// CHECK-NEXT:    br label %[[ATOMICRMW_START:.*]]
-// CHECK:       [[ATOMICRMW_START]]:
-// CHECK-NEXT:    [[TMP5:%.*]] = phi i256 [ [[TMP4]], %[[ENTRY]] ], [ [[TMP6:%.*]], %[[ATOMICRMW_START]] ]
-// CHECK-NEXT:    [[NEW:%.*]] = or i256 [[TMP5]], [[TMP3]]
-// CHECK-NEXT:    store i256 [[TMP5]], ptr [[ATOMIC_TEMP1]], align 8
+// CHECK-NEXT:    [[NEW:%.*]] = or i256 [[TMP4]], [[TMP3]]
 // CHECK-NEXT:    store i256 [[NEW]], ptr [[ATOMIC_TEMP2]], align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
-// CHECK-NEXT:    [[TMP6]] = load i256, ptr [[ATOMIC_TEMP1]], align 8
-// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMICRMW_END:.*]], label %[[ATOMICRMW_START]]
-// CHECK:       [[ATOMICRMW_END]]:
+// CHECK-NEXT:    call void @__atomic_store(i64 noundef 32, ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5)
+// CHECK-NEXT:    [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 32, ptr noundef [[TMP1]], ptr noundef [[ATOMIC_TEMP]], ptr noundef [[ATOMIC_TEMP1]], i32 noundef 5, i32 noundef 5)
+// CHECK-NEXT:    br i1 [[CALL]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i256 [[TMP4]], ptr [[AGG_RESULT]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
 // CHECK-NEXT:    store i256 [[TMP5]], ptr [[AGG_RESULT]], align 8
-// CHECK-NEXT:    [[TMP7:%.*]] = load i256, ptr [[AGG_RESULT]], align 8
-// CHECK-NEXT:    store i256 [[TMP7]], ptr [[AGG_RESULT]], align 8
 // CHECK-NEXT:    ret void
 //
 S256 or256(_Atomic(S256) *p, S256 v) {

>From e79f0f4e3fbec4c77fcae7f42f2d9dc5f3e85c5a Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sat, 27 Jun 2026 09:34:49 +0200
Subject: [PATCH 7/9] [compiler-rt] Add runtime test for atomic _BitInt(N)

Single-threaded execution test for _Atomic(_BitInt(N)): per-op value
correctness on a padded inline width and on wide libcall widths, plus
dirty-padding convergence. An object with non-canonical padding (written
through a union) must not spin forever in the read-modify-write
compare-exchange loop. The IR-shape checks in
clang/test/CodeGen/atomic-bitint.c cannot witness non-termination.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 .../test/builtins/Unit/atomic_bitint_test.c   | 91 +++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100644 compiler-rt/test/builtins/Unit/atomic_bitint_test.c

diff --git a/compiler-rt/test/builtins/Unit/atomic_bitint_test.c b/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
new file mode 100644
index 0000000000000..33a745348a6f0
--- /dev/null
+++ b/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
@@ -0,0 +1,91 @@
+// RUN: %clang_builtins -std=c23 %s %librt -o %t && %run %t
+// REQUIRES: librt_has_atomic
+//===-- atomic_bitint_test.c - Test atomic ops on _BitInt -----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Runtime checks for atomic read-modify-write on _BitInt(N). A padded width
+// (37) exercises the inline compare-exchange loop; a wide width (256) exercises
+// the __atomic_compare_exchange libcall loop. Each op is cross-checked against
+// the same operation done non-atomically, and the dirty-padding cases confirm
+// the loop converges (a re-canonicalized expected would spin forever).
+//
+//===----------------------------------------------------------------------===//
+
+#include <assert.h>
+#include <stdio.h>
+
+typedef signed _BitInt(37) S37;
+typedef unsigned _BitInt(37) U37;
+typedef signed _BitInt(256) S256; // no padding (exactly 32 bytes)
+typedef signed _BitInt(200) S200; // padded: 200 value bits in 32-byte storage
+
+// Each macro runs the atomic op and asserts the returned old value and the
+// resulting object both match the non-atomic computation at width N.
+#define CHECK_FETCH(T, init, op, rhs, expr)                                    \
+  do {                                                                         \
+    _Atomic(T) a = (init);                                                     \
+    T old = __c11_atomic_fetch_##op(&a, (rhs), __ATOMIC_SEQ_CST);              \
+    assert(old == (T)(init));                                                  \
+    assert((T)a == (T)(expr));                                                 \
+  } while (0)
+
+static void test_ops(void) {
+  CHECK_FETCH(S37, 100, add, 5, 105);
+  CHECK_FETCH(S37, 100, sub, 40, 60);
+  CHECK_FETCH(S37, -3, add, 1, -2);
+  CHECK_FETCH(U37, 7, add, 9, 16);
+  CHECK_FETCH(S37, 0x15, and, 0x13, 0x11);
+  CHECK_FETCH(S37, 0x10, or, 5, 0x15);
+  CHECK_FETCH(S37, 0x1F, xor, 0x15, 0x0A);
+  CHECK_FETCH(S37, -5, min, -7, -7);    // signed: -7 < -5
+  CHECK_FETCH(U37, 5, min, (U37)-1, 5); // unsigned: 5 < 2^37-1
+  CHECK_FETCH(S37, 3, max, 9, 9);
+  CHECK_FETCH(S37, 0x15, nand, 0x13, (S37) ~(0x15 & 0x13));
+  // Wide widths: the libcall loop (no padding, and padded).
+  CHECK_FETCH(S256, 100, add, 5, 105);
+  CHECK_FETCH(S256, 1, or, 0xFE, 0xFF);
+  CHECK_FETCH(S200, 100, add, 5, 105);
+}
+
+// Seed non-canonical padding through a union, then RMW. A loop that carried a
+// re-canonicalized expected would never match memory and hang here.
+static void test_dirty_padding(void) {
+  union {
+    _Atomic(S37) a;
+    unsigned long b;
+  } s;
+  s.b = ((unsigned long)1 << 40) | 5u; // value bits 5, padding bit 40 set
+  S37 old = __c11_atomic_fetch_add(&s.a, 1, __ATOMIC_SEQ_CST);
+  assert(old == 5 && (S37)s.a == 6);
+
+  union {
+    _Atomic(U37) a;
+    unsigned long b;
+  } u;
+  u.b = ((unsigned long)3 << 50) | 7u;
+  U37 uold = __c11_atomic_fetch_add(&u.a, 1, __ATOMIC_SEQ_CST);
+  assert(uold == 7 && (U37)u.a == 8);
+
+  // Wide padded width (libcall loop): _BitInt(200) has 56 padding bits in its
+  // 32-byte storage. Set the overlay at value level (endian-independent): low
+  // 200 bits = 5, a padding bit (240) dirtied.
+  union {
+    _Atomic(S200) a;
+    unsigned _BitInt(256) full;
+  } w;
+  w.full = (unsigned _BitInt(256))5 | ((unsigned _BitInt(256))0xAA << 240);
+  S200 wold = __c11_atomic_fetch_add(&w.a, 1, __ATOMIC_SEQ_CST);
+  assert(wold == 5 && (S200)w.a == 6);
+}
+
+int main(void) {
+  test_ops();
+  test_dirty_padding();
+  printf("PASS\n");
+  return 0;
+}

>From 88f329b57018f8646b65484f0d36e92c9e54fd27 Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sat, 27 Jun 2026 09:56:57 +0200
Subject: [PATCH 8/9] [Clang][test] Expand _BitInt atomic Sema and CodeGen
 coverage

Sema: add reject cases (non-_Atomic pointer, wrong arity, atomic _BitInt
bit-field) so lifting the _BitInt rejection does not silently drop the
atomic-specific checks, plus an __atomic_add_fetch (returns-new) accept.
CodeGen: add an unsigned arithmetic RMW (zero-extended desired) and signed
max / unsigned min, exercising the zext path and the icmp sgt/ule predicates
the previous functions did not.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 clang/test/CodeGen/atomic-bitint.c | 140 +++++++++++++++++++++++++++++
 clang/test/Sema/atomic-bitint.c    |  15 +++-
 2 files changed, 152 insertions(+), 3 deletions(-)

diff --git a/clang/test/CodeGen/atomic-bitint.c b/clang/test/CodeGen/atomic-bitint.c
index bc1e165fd90e3..dda8f644f3fec 100644
--- a/clang/test/CodeGen/atomic-bitint.c
+++ b/clang/test/CodeGen/atomic-bitint.c
@@ -262,6 +262,146 @@ U37 min37(_Atomic(S37) *p, S37 v) {
   return __c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
 }
 
+// Unsigned arithmetic RMW: the desired is zero-extended, not sign-extended.
+// CHECK-LABEL: define dso_local i64 @uadd37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = zext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = zext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP7:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[NEW:%.*]] = add i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = zext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP5]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP7]] = extractvalue { i64, i1 } [[TMP6]], 0
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { i64, i1 } [[TMP6]], 1
+// CHECK-NEXT:    br i1 [[TMP8]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP9:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP9]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
+U37 uadd37(_Atomic(U37) *p, U37 v) {
+  return __c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Signed max computes at the value width with a signed compare.
+// CHECK-LABEL: define dso_local i64 @max37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = sext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = sext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP8:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp sgt i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[LOADEDV4]], i37 [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = sext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP8]] = extractvalue { i64, i1 } [[TMP7]], 0
+// CHECK-NEXT:    [[TMP9:%.*]] = extractvalue { i64, i1 } [[TMP7]], 1
+// CHECK-NEXT:    br i1 [[TMP9]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP10:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP10]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
+S37 max37(_Atomic(S37) *p, S37 v) {
+  return __c11_atomic_fetch_max(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Unsigned min computes at the value width with an unsigned compare.
+// CHECK-LABEL: define dso_local i64 @umin37(
+// CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca i37, align 8
+// CHECK-NEXT:    [[V:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[V_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store i64 [[V_COERCE]], ptr [[V]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, ptr [[V]], align 8
+// CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[TMP0]] to i37
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[STOREDV:%.*]] = zext i37 [[V1]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV]], ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i64, ptr [[V_ADDR]], align 8
+// CHECK-NEXT:    [[LOADEDV:%.*]] = trunc i64 [[TMP2]] to i37
+// CHECK-NEXT:    [[STOREDV2:%.*]] = zext i37 [[LOADEDV]] to i64
+// CHECK-NEXT:    store i64 [[STOREDV2]], ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load i64, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[LOADEDV3:%.*]] = trunc i64 [[TMP3]] to i37
+// CHECK-NEXT:    [[ATOMIC_LOAD:%.*]] = load atomic i64, ptr [[TMP1]] seq_cst, align 8
+// CHECK-NEXT:    br label %[[ATOMIC_CONT:.*]]
+// CHECK:       [[ATOMIC_CONT]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = phi i64 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP8:%.*]], %[[ATOMIC_CONT]] ]
+// CHECK-NEXT:    [[LOADEDV4:%.*]] = trunc i64 [[TMP4]] to i37
+// CHECK-NEXT:    [[TMP5:%.*]] = icmp ule i37 [[LOADEDV4]], [[LOADEDV3]]
+// CHECK-NEXT:    [[NEW:%.*]] = select i1 [[TMP5]], i37 [[LOADEDV4]], i37 [[LOADEDV3]]
+// CHECK-NEXT:    [[STOREDV5:%.*]] = zext i37 [[NEW]] to i64
+// CHECK-NEXT:    store atomic i64 [[STOREDV5]], ptr [[ATOMIC_TEMP]] seq_cst, align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = cmpxchg ptr [[TMP1]], i64 [[TMP4]], i64 [[TMP6]] seq_cst seq_cst, align 8
+// CHECK-NEXT:    [[TMP8]] = extractvalue { i64, i1 } [[TMP7]], 0
+// CHECK-NEXT:    [[TMP9:%.*]] = extractvalue { i64, i1 } [[TMP7]], 1
+// CHECK-NEXT:    br i1 [[TMP9]], label %[[ATOMIC_EXIT:.*]], label %[[ATOMIC_CONT]]
+// CHECK:       [[ATOMIC_EXIT]]:
+// CHECK-NEXT:    store i37 [[LOADEDV4]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[TMP10:%.*]] = load i37, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i37 [[TMP10]] to i64
+// CHECK-NEXT:    ret i64 [[COERCE_VAL_II]]
+//
+U37 umin37(_Atomic(U37) *p, U37 v) {
+  return __c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
+}
+
 // No padding: direct atomicrmw, no loop.
 // CHECK-LABEL: define dso_local i64 @add64(
 // CHECK-SAME: ptr noundef [[P:%.*]], i64 noundef [[V:%.*]]) #[[ATTR0]] {
diff --git a/clang/test/Sema/atomic-bitint.c b/clang/test/Sema/atomic-bitint.c
index fbb4c518438fb..3bd4faf22e7be 100644
--- a/clang/test/Sema/atomic-bitint.c
+++ b/clang/test/Sema/atomic-bitint.c
@@ -7,8 +7,6 @@
 // code imposes no width cap of its own; widths past 128 are available wherever
 // the target accepts _BitInt > 128 (x86 and RISC-V today).
 
-// expected-no-diagnostics
-
 _Atomic(_BitInt(4))    a4;     // small
 _Atomic(_BitInt(9))    a9;     // non-power-of-two
 _Atomic(_BitInt(37))   a37;    // padded
@@ -35,9 +33,20 @@ void c11_builtins(_Atomic(_BitInt(37)) *p, _BitInt(37) v, _BitInt(37) *e) {
   (void)__c11_atomic_fetch_min(p, v, __ATOMIC_SEQ_CST);
 }
 
-// The GNU __atomic_* builtins take a plain _BitInt pointer.
+// The GNU __atomic_* builtins take a plain _BitInt pointer; the _fetch forms
+// return the new value.
 void gnu_builtins(_BitInt(37) *p, _BitInt(37) v) {
   (void)__atomic_load_n(p, __ATOMIC_SEQ_CST);
   __atomic_store_n(p, v, __ATOMIC_SEQ_CST);
   (void)__atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
+  (void)__atomic_add_fetch(p, v, __ATOMIC_SEQ_CST);
+}
+
+// Lifting the _BitInt rejection must not lose the atomic-specific checks.
+void rejects(_Atomic(_BitInt(37)) *ap, _BitInt(37) *p, _BitInt(37) v) {
+  (void)__c11_atomic_load(ap); // expected-error {{too few arguments to function call}}
+  (void)__c11_atomic_fetch_add(p, v, __ATOMIC_SEQ_CST); // expected-error {{must be a pointer to _Atomic}}
 }
+struct WithAtomicBitIntField {
+  _Atomic(_BitInt(5)) f : 3; // expected-error {{bit-field 'f' has non-integral type}}
+};

>From 9d349acf9dc9f5c74c4103c3ed60252ca95ff53e Mon Sep 17 00:00:00 2001
From: Xavier Roche <xavier.roche at algolia.com>
Date: Sat, 27 Jun 2026 09:56:58 +0200
Subject: [PATCH 9/9] [compiler-rt] Harden the _BitInt atomic runtime test

Use uint64_t for the dirty-padding overlay (unsigned long is 32-bit on
LLP64, where the padding-bit shift was undefined). Read the storage back
after a converged RMW to confirm the padding is canonicalized, and add
returns-new (__atomic_*_fetch) and non-seq_cst ordering coverage.

Assisted-by: Claude (Anthropic)
Co-Authored-By: Claude Opus 4.6 <noreply at anthropic.com>
---
 .../test/builtins/Unit/atomic_bitint_test.c   | 37 +++++++++++++++++--
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/compiler-rt/test/builtins/Unit/atomic_bitint_test.c b/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
index 33a745348a6f0..e0cc3aef61bc2 100644
--- a/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
+++ b/compiler-rt/test/builtins/Unit/atomic_bitint_test.c
@@ -17,6 +17,7 @@
 //===----------------------------------------------------------------------===//
 
 #include <assert.h>
+#include <stdint.h>
 #include <stdio.h>
 
 typedef signed _BitInt(37) S37;
@@ -55,21 +56,25 @@ static void test_ops(void) {
 // Seed non-canonical padding through a union, then RMW. A loop that carried a
 // re-canonicalized expected would never match memory and hang here.
 static void test_dirty_padding(void) {
+  // uint64_t (not unsigned long, which is 32-bit on LLP64) so the padding bit
+  // is representable and the overlay matches the 8-byte atomic.
   union {
     _Atomic(S37) a;
-    unsigned long b;
+    uint64_t b;
   } s;
-  s.b = ((unsigned long)1 << 40) | 5u; // value bits 5, padding bit 40 set
+  s.b = ((uint64_t)1 << 40) | 5u; // value bits 5, padding bit 40 set
   S37 old = __c11_atomic_fetch_add(&s.a, 1, __ATOMIC_SEQ_CST);
   assert(old == 5 && (S37)s.a == 6);
+  assert((s.b >> 37) == 0); // padding canonicalized (positive value)
 
   union {
     _Atomic(U37) a;
-    unsigned long b;
+    uint64_t b;
   } u;
-  u.b = ((unsigned long)3 << 50) | 7u;
+  u.b = ((uint64_t)3 << 50) | 7u;
   U37 uold = __c11_atomic_fetch_add(&u.a, 1, __ATOMIC_SEQ_CST);
   assert(uold == 7 && (U37)u.a == 8);
+  assert((u.b >> 37) == 0); // padding canonicalized (zero-extended)
 
   // Wide padded width (libcall loop): _BitInt(200) has 56 padding bits in its
   // 32-byte storage. Set the overlay at value level (endian-independent): low
@@ -81,11 +86,35 @@ static void test_dirty_padding(void) {
   w.full = (unsigned _BitInt(256))5 | ((unsigned _BitInt(256))0xAA << 240);
   S200 wold = __c11_atomic_fetch_add(&w.a, 1, __ATOMIC_SEQ_CST);
   assert(wold == 5 && (S200)w.a == 6);
+  assert((w.full >> 200) == 0); // padding canonicalized (positive value)
+}
+
+// The _fetch builtins return the new value, not the old one.
+static void test_returns_new(void) {
+  S37 a = 100;
+  assert(__atomic_add_fetch(&a, 5, __ATOMIC_SEQ_CST) == 105);
+  assert(__atomic_sub_fetch(&a, 10, __ATOMIC_SEQ_CST) == 95);
+  U37 u = 0;
+  assert(__atomic_or_fetch(&u, 0xF, __ATOMIC_SEQ_CST) == 0xF);
+  S200 w = 100;
+  assert(__atomic_add_fetch(&w, 5, __ATOMIC_SEQ_CST) == 105);
+}
+
+// Each non-seq_cst ordering drives the loop's load/cmpxchg ordering.
+static void test_orderings(void) {
+  _Atomic(S37) a = 10;
+  (void)__c11_atomic_fetch_add(&a, 1, __ATOMIC_RELAXED);
+  (void)__c11_atomic_fetch_add(&a, 1, __ATOMIC_ACQUIRE);
+  (void)__c11_atomic_fetch_add(&a, 1, __ATOMIC_RELEASE);
+  (void)__c11_atomic_fetch_add(&a, 1, __ATOMIC_ACQ_REL);
+  assert((S37)a == 14);
 }
 
 int main(void) {
   test_ops();
   test_dirty_padding();
+  test_returns_new();
+  test_orderings();
   printf("PASS\n");
   return 0;
 }


From libcxx-commits at lists.llvm.org  Fri Jul 24 01:02:09 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 01:02:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a631c01.1905e68e.25404.eedb@mx.google.com>


huixie90 wrote:

I am a bit struggling that the codegen seems to be much worse in some cases.  For example,
https://godbolt.org/z/9K35c36eq
- If I don't capture `success` memory order in the lambda, everything is nicely inlined
- as long as I capture the `success` memory order, it will generate all combination of the memory order in CAS and a switch statement to select the right one. and lambda can no longer inlined due to this complexity

https://github.com/llvm/llvm-project/pull/76180

From libcxx-commits at lists.llvm.org  Fri Jul 24 01:29:22 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 01:29:22 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a632262.af6ba3ed.1989e7.4ac9@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/211568

>From 8e3559129e89e81f7b8bf0d195222363898ef654 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 15:38:21 +0200
Subject: [PATCH 1/4] resolve: LWG4242

---
 libcxx/docs/Status/Cxx26Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 5b046b69d14bd..83ad46827ca18 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -142,7 +142,7 @@
 "`LWG4235 <https://wg21.link/LWG4235>`__","``cache_latest_view`` and ``to_input_view`` miss ``reserve_hint``","2025-06 (Sofia)","","","`#148219 <https://github.com/llvm/llvm-project/issues/148219>`__",""
 "`LWG4236 <https://wg21.link/LWG4236>`__","``chunk_view::outer-iterator::value_type`` should provide ``reserve_hint``","2025-06 (Sofia)","","","`#148220 <https://github.com/llvm/llvm-project/issues/148220>`__",""
 "`LWG4239 <https://wg21.link/LWG4239>`__","``flat_map``'s transparent comparator no longer works for string literals","2025-06 (Sofia)","","","`#148223 <https://github.com/llvm/llvm-project/issues/148223>`__",""
-"`LWG4242 <https://wg21.link/LWG4242>`__","``ranges::distance`` does not work with volatile iterators","2025-06 (Sofia)","","","`#148224 <https://github.com/llvm/llvm-project/issues/148224>`__",""
+"`LWG4242 <https://wg21.link/LWG4242>`__","``ranges::distance`` does not work with volatile iterators","2025-06 (Sofia)","|Complete|","24","`#148224 <https://github.com/llvm/llvm-project/issues/148224>`__",""
 "`LWG4245 <https://wg21.link/LWG4245>`__","Operators that interact with ``counted_iterator`` and ``default_sentinel_t`` should be ``noexcept``","2025-06 (Sofia)","","","`#148226 <https://github.com/llvm/llvm-project/issues/148226>`__",""
 "`LWG4247 <https://wg21.link/LWG4247>`__","Header ``<stdbit.h>`` is not yet freestanding","2025-06 (Sofia)","","","`#148227 <https://github.com/llvm/llvm-project/issues/148227>`__",""
 "","","","","","",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..029117e2ff5cc 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -87,7 +87,7 @@ struct __distance {
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
-    if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
+    if constexpr (!is_array_v<remove_reference_t<_Ip>>) {
       return __last - __first;
     } else {
       return __last - decay_t<_Ip>(__first);

>From 654370a5f26aed08caebe4fbc98f09417e272517 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 15:56:59 +0200
Subject: [PATCH 2/4] add: lwg4242 test case and refactor lwg3664 test case

---
 .../iterator_sentinel.pass.cpp                | 63 ++++++++++++-------
 1 file changed, 40 insertions(+), 23 deletions(-)

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index 30507c59495ea..37ee37a0e5876 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -199,6 +199,41 @@ static_assert(!std::sized_sentinel_for<EvilSentinel<int*>, const int*>);
 static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, int*>);
 static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, const int*>);
 
+// LWG3664: "LWG3392 broke std::ranges::distance(a, a+3)" tests.
+constexpr void test_lwg3664() {
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, a + 3) == 3);
+    assert(std::ranges::distance(a, a) == 0);
+    assert(std::ranges::distance(a + 3, a) == -3);
+  }
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
+  }
+  {
+    const int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  }
+}
+
+// LWG4242: "ranges::distance does not work with volatile iterators" tests.
+void test_lwg4242() {
+  int arr[]         = {1, 2, 3};
+  int* volatile ptr = arr;
+  auto dist         = std::distance(ptr, arr + 3);
+  auto r_dist       = std::ranges::distance(ptr, arr + 3);
+  assert(dist == r_dist);
+}
+
 constexpr bool test() {
   {
     int a[] = {1, 2, 3};
@@ -284,30 +319,12 @@ constexpr bool test() {
     auto view = c | std::views::join;
     assert(std::ranges::distance(view.begin(), view.end()) == 30);
   }
-  if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
-    test_deque();
 
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, a + 3) == 3);
-    assert(std::ranges::distance(a, a) == 0);
-    assert(std::ranges::distance(a + 3, a) == -3);
-  }
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
-    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
-  }
-  {
-    const int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
-    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  test_lwg3664();
+
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    test_deque(); // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
+    test_lwg4242();
   }
 
   return true;

>From d828f029e4f94bbd6db269722fb2d6a8a957f765 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 18:51:47 +0200
Subject: [PATCH 3/4] fix(tentative): check if CI errors are somehow related to
 static_cast wording

---
 libcxx/include/__iterator/distance.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index 029117e2ff5cc..bb3620764f1ce 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -90,7 +90,7 @@ struct __distance {
     if constexpr (!is_array_v<remove_reference_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
     }
   }
 

>From ab8c4dfd9af5a8ad859898fb6e4f3d6e65cb023a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 24 Jul 2026 10:29:08 +0200
Subject: [PATCH 4/4] add: missing includes. remove: unused __remove_cref.h
 include

---
 libcxx/include/__iterator/distance.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index bb3620764f1ce..fba72db022f9a 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -21,8 +21,9 @@
 #include <__ranges/concepts.h>
 #include <__ranges/size.h>
 #include <__type_traits/decay.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/enable_if.h>
-#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
 #include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)


From libcxx-commits at lists.llvm.org  Fri Jul 24 01:29:32 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 01:29:32 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a63226c.c4d3d80a.16dae5.cfe4@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/211568

>From 8e3559129e89e81f7b8bf0d195222363898ef654 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 15:38:21 +0200
Subject: [PATCH 1/4] resolve: LWG4242

---
 libcxx/docs/Status/Cxx26Issues.csv   | 2 +-
 libcxx/include/__iterator/distance.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 5b046b69d14bd..83ad46827ca18 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -142,7 +142,7 @@
 "`LWG4235 <https://wg21.link/LWG4235>`__","``cache_latest_view`` and ``to_input_view`` miss ``reserve_hint``","2025-06 (Sofia)","","","`#148219 <https://github.com/llvm/llvm-project/issues/148219>`__",""
 "`LWG4236 <https://wg21.link/LWG4236>`__","``chunk_view::outer-iterator::value_type`` should provide ``reserve_hint``","2025-06 (Sofia)","","","`#148220 <https://github.com/llvm/llvm-project/issues/148220>`__",""
 "`LWG4239 <https://wg21.link/LWG4239>`__","``flat_map``'s transparent comparator no longer works for string literals","2025-06 (Sofia)","","","`#148223 <https://github.com/llvm/llvm-project/issues/148223>`__",""
-"`LWG4242 <https://wg21.link/LWG4242>`__","``ranges::distance`` does not work with volatile iterators","2025-06 (Sofia)","","","`#148224 <https://github.com/llvm/llvm-project/issues/148224>`__",""
+"`LWG4242 <https://wg21.link/LWG4242>`__","``ranges::distance`` does not work with volatile iterators","2025-06 (Sofia)","|Complete|","24","`#148224 <https://github.com/llvm/llvm-project/issues/148224>`__",""
 "`LWG4245 <https://wg21.link/LWG4245>`__","Operators that interact with ``counted_iterator`` and ``default_sentinel_t`` should be ``noexcept``","2025-06 (Sofia)","","","`#148226 <https://github.com/llvm/llvm-project/issues/148226>`__",""
 "`LWG4247 <https://wg21.link/LWG4247>`__","Header ``<stdbit.h>`` is not yet freestanding","2025-06 (Sofia)","","","`#148227 <https://github.com/llvm/llvm-project/issues/148227>`__",""
 "","","","","","",""
diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index c7c4ff22d6e9f..029117e2ff5cc 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -87,7 +87,7 @@ struct __distance {
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
-    if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
+    if constexpr (!is_array_v<remove_reference_t<_Ip>>) {
       return __last - __first;
     } else {
       return __last - decay_t<_Ip>(__first);

>From 654370a5f26aed08caebe4fbc98f09417e272517 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 15:56:59 +0200
Subject: [PATCH 2/4] add: lwg4242 test case and refactor lwg3664 test case

---
 .../iterator_sentinel.pass.cpp                | 63 ++++++++++++-------
 1 file changed, 40 insertions(+), 23 deletions(-)

diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
index 30507c59495ea..37ee37a0e5876 100644
--- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.distance/iterator_sentinel.pass.cpp
@@ -199,6 +199,41 @@ static_assert(!std::sized_sentinel_for<EvilSentinel<int*>, const int*>);
 static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, int*>);
 static_assert(std::sized_sentinel_for<EvilSentinel<const int*>, const int*>);
 
+// LWG3664: "LWG3392 broke std::ranges::distance(a, a+3)" tests.
+constexpr void test_lwg3664() {
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, a + 3) == 3);
+    assert(std::ranges::distance(a, a) == 0);
+    assert(std::ranges::distance(a + 3, a) == -3);
+  }
+  {
+    int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
+  }
+  {
+    const int a[] = {1, 2, 3};
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
+    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
+    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
+    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
+    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  }
+}
+
+// LWG4242: "ranges::distance does not work with volatile iterators" tests.
+void test_lwg4242() {
+  int arr[]         = {1, 2, 3};
+  int* volatile ptr = arr;
+  auto dist         = std::distance(ptr, arr + 3);
+  auto r_dist       = std::ranges::distance(ptr, arr + 3);
+  assert(dist == r_dist);
+}
+
 constexpr bool test() {
   {
     int a[] = {1, 2, 3};
@@ -284,30 +319,12 @@ constexpr bool test() {
     auto view = c | std::views::join;
     assert(std::ranges::distance(view.begin(), view.end()) == 30);
   }
-  if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
-    test_deque();
 
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, a + 3) == 3);
-    assert(std::ranges::distance(a, a) == 0);
-    assert(std::ranges::distance(a + 3, a) == -3);
-  }
-  {
-    int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a + 3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<int*>{a}) == 0);
-    assert(std::ranges::distance(a + 3, EvilSentinel<int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<int*>{a + 3}) == 3);
-  }
-  {
-    const int a[] = {1, 2, 3};
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a + 3}) == 3);
-    assert(std::ranges::distance(a, EvilSentinel<const int*>{a}) == 0);
-    assert(std::ranges::distance(a + 3, EvilSentinel<const int*>{a}) == -3);
-    assert(std::ranges::distance(std::move(a), EvilSentinel<const int*>{a + 3}) == 3);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&)[3], EvilSentinel<int*>>);
-    static_assert(!std::is_invocable_v<decltype(std::ranges::distance), const int (&&)[3], EvilSentinel<int*>>);
+  test_lwg3664();
+
+  if (!TEST_IS_CONSTANT_EVALUATED) {
+    test_deque(); // TODO: Use TEST_STD_AT_LEAST_26_OR_RUNTIME_EVALUATED when std::deque is made constexpr
+    test_lwg4242();
   }
 
   return true;

>From d828f029e4f94bbd6db269722fb2d6a8a957f765 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 18:51:47 +0200
Subject: [PATCH 3/4] fix(tentative): check if CI errors are somehow related to
 static_cast wording

---
 libcxx/include/__iterator/distance.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index 029117e2ff5cc..bb3620764f1ce 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -90,7 +90,7 @@ struct __distance {
     if constexpr (!is_array_v<remove_reference_t<_Ip>>) {
       return __last - __first;
     } else {
-      return __last - decay_t<_Ip>(__first);
+      return __last - static_cast<decay_t<_Ip>>(__first);
     }
   }
 

>From ab8c4dfd9af5a8ad859898fb6e4f3d6e65cb023a Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 24 Jul 2026 10:29:08 +0200
Subject: [PATCH 4/4] add: missing includes. remove: unused __remove_cref.h
 include

---
 libcxx/include/__iterator/distance.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h
index bb3620764f1ce..fba72db022f9a 100644
--- a/libcxx/include/__iterator/distance.h
+++ b/libcxx/include/__iterator/distance.h
@@ -21,8 +21,9 @@
 #include <__ranges/concepts.h>
 #include <__ranges/size.h>
 #include <__type_traits/decay.h>
+#include <__type_traits/is_array.h>
 #include <__type_traits/enable_if.h>
-#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
 #include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)


From libcxx-commits at lists.llvm.org  Fri Jul 24 02:14:13 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 02:14:13 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a632ce5.0318b02e.530b7.03cc@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/211568

From libcxx-commits at lists.llvm.org  Fri Jul 24 02:14:24 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 02:14:24 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a632cf0.f3151ff8.f584a.fc52@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/211568

From libcxx-commits at lists.llvm.org  Fri Jul 24 02:14:43 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 02:14:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a632d03.8e75e0cb.2de827.20cd@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/211568

From libcxx-commits at lists.llvm.org  Fri Jul 24 02:18:04 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 02:18:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a632dcc.d76d81af.3b68ca.39b4@mx.google.com>


================
@@ -87,10 +87,10 @@ struct __distance {
 
   template <class _Ip, sized_sentinel_for<decay_t<_Ip>> _Sp>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip&& __first, _Sp __last) const {
-    if constexpr (sized_sentinel_for<_Sp, __remove_cvref_t<_Ip>>) {
+    if constexpr (!is_array_v<remove_reference_t<_Ip>>) {
----------------
lknknm wrote:

Yes, I think I've misstated myself there. I've changed the PR description with new wording. Let me know if you agree.
--
I have to mention I added the `static_cast` for the `else` condition there because I had some failures in CI but I think they were unrelated. If you prefer we can just remove it.

https://github.com/llvm/llvm-project/pull/211568

From libcxx-commits at lists.llvm.org  Fri Jul 24 02:18:09 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 02:18:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a632dd1.b4a331f0.2642a.3446@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/211568

From libcxx-commits at lists.llvm.org  Fri Jul 24 02:18:50 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 02:18:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a632dfa.1e1ac41d.317291.6882@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/211568

From libcxx-commits at lists.llvm.org  Fri Jul 24 02:18:58 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 02:18:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][ranges] Resolve LWG4242 (PR
	#211568)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211568@github.com>
Message-ID: <6a632e02.bc28b987.1c0bcd.295f@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/211568

From libcxx-commits at lists.llvm.org  Fri Jul 24 03:06:37 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 24 Jul 2026 03:06:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] Guard use of
	`[[clang::trivial_abi]]` (PR #211736)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211736@github.com>
Message-ID: <6a63392d.eb165e5c.2e0c9a.f598@mx.google.com>


frederick-vs-ja wrote:

> Should we just disable the test for MSVC instead?

In the test suite of MSVC STL, cases of this test using MSVC are already marked `XFAIL`. So I'm not sure whether we should even do anything if we decide not to make MSVC happy with this test.

> This is rather meaningless if the compiler doesn't support `[[clang::trivial_abi]]`.

Hmm, I think we're keeping GCC running this test for a long while even though GCC (before 17) doesn't support `[[clang::trivial_abi]]` yet.

(FWIW, GCC 17 starts supporting `[[clang::trivial_abi]]` via gcc-mirror/gcc at 1e89650aff94c20007f74949018f5d5150ea9ba7.)

https://github.com/llvm/llvm-project/pull/211736

From libcxx-commits at lists.llvm.org  Fri Jul 24 03:11:01 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 03:11:01 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] re-enable clear padding
	runtime test (PR #211258)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211258@github.com>
Message-ID: <6a633a35.0c05e68e.81651.1aee@mx.google.com>


huixie90 wrote:

The ASAN failure seems unrelated to the builtin
 https://godbolt.org/z/nssvWG91G

https://github.com/llvm/llvm-project/pull/211258

From libcxx-commits at lists.llvm.org  Fri Jul 24 03:43:11 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 24 Jul 2026 03:43:11 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [lldb] [clang]: implement
	std::meta::is_type (PR #211653)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211653@github.com>
Message-ID: <6a6341bf.f06f4994.50b1b.97e6@mx.google.com>


frederick-vs-ja wrote:

> `__cpp_impl_reflection` is only enabled when `-freflection` is on

Why? Can we avoid `-freflection` and just enable reflection since C++26? Or can we make `-freflection` meaningless since C++26 and allow it in old modes as an extension (which is not very conforming, though)?

https://github.com/llvm/llvm-project/pull/211653

From libcxx-commits at lists.llvm.org  Fri Jul 24 04:06:44 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 04:06:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a634744.e55dc341.dbc09.e827@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/209686

>From bc598c40cb7bac1531c0a3c1b32c0c8536ac6cd1 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 12:59:35 +0800
Subject: [PATCH 1/2] [libc++] Implement LWG 4274

Allow chrono::hh_mm_ss to be constructed from durations with unsigned representations.
---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__chrono/hh_mm_ss.h            | 20 ++++--
 .../time.hms/time.hms.members/ctor.pass.cpp   | 68 +++++++++++++++++++
 3 files changed, 83 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c203fa424556a..e37c05f035526 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -168,7 +168,7 @@
 "`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","|Complete|","23","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
 "`LWG4269 <https://wg21.link/LWG4269>`__","``unique_copy`` passes arguments to its predicate backwards","2025-11 (Kona)","","","`#171326 <https://github.com/llvm/llvm-project/issues/171326>`__",""
 "`LWG4272 <https://wg21.link/LWG4272>`__","For ``rank == 0``, ``layout_stride`` is atypically convertible","2025-11 (Kona)","","","`#171327 <https://github.com/llvm/llvm-project/issues/171327>`__",""
-"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","","","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
+"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","|Complete|","24","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
 "`LWG4275 <https://wg21.link/LWG4275>`__","``std::dynamic_extent`` should also be defined in ``<mdspan>``","2025-11 (Kona)","|Complete|","21","`#171329 <https://github.com/llvm/llvm-project/issues/171329>`__",""
 "`LWG4276 <https://wg21.link/LWG4276>`__","``front()`` and ``back()`` are not hardened for zero-length ``std::array``\s","2025-11 (Kona)","|Complete|","17","`#171330 <https://github.com/llvm/llvm-project/issues/171330>`__",""
 "`LWG4280 <https://wg21.link/LWG4280>`__","``simd::partial_load`` uses undefined identifier ``T``","2025-11 (Kona)","","","`#171331 <https://github.com/llvm/llvm-project/issues/171331>`__",""
diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index 746eba903d4f8..ddb2d055c16ca 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -11,9 +11,9 @@
 #define _LIBCPP___CHRONO_HH_MM_SS_H
 
 #include <__chrono/duration.h>
-#include <__chrono/time_point.h>
 #include <__config>
 #include <__type_traits/common_type.h>
+#include <__type_traits/is_unsigned.h>
 #include <ratio>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -45,6 +45,14 @@ class hh_mm_ss {
     return 0;
   }
 
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+      return __d;
+    } else {
+      return __is_neg ? -__d : __d;
+    }
+  }
+
 public:
   _LIBCPP_HIDE_FROM_ABI static unsigned constexpr fractional_width =
       __width(__CommonType::period::den) < 19 ? __width(__CommonType::period::den) : 6u;
@@ -53,11 +61,11 @@ class hh_mm_ss {
   _LIBCPP_HIDE_FROM_ABI constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit hh_mm_ss(_Duration __d) noexcept
-      : __is_neg_(__d < _Duration(0)),
-        __h_(chrono::duration_cast<chrono::hours>(chrono::abs(__d))),
-        __m_(chrono::duration_cast<chrono::minutes>(chrono::abs(__d) - hours())),
-        __s_(chrono::duration_cast<chrono::seconds>(chrono::abs(__d) - hours() - minutes())),
-        __f_(chrono::duration_cast<precision>(chrono::abs(__d) - hours() - minutes() - seconds())) {}
+      : __is_neg_(__d < _Duration::zero()),
+        __h_(chrono::duration_cast<chrono::hours>(__abs_d(__d, __is_neg_))),
+        __m_(chrono::duration_cast<chrono::minutes>(__abs_d(__d, __is_neg_) - hours())),
+        __s_(chrono::duration_cast<chrono::seconds>(__abs_d(__d, __is_neg_) - hours() - minutes())),
+        __f_(chrono::duration_cast<precision>(__abs_d(__d, __is_neg_) - hours() - minutes() - seconds())) {}
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool is_negative() const noexcept { return __is_neg_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr chrono::hours hours() const noexcept { return __h_; }
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
new file mode 100644
index 0000000000000..1d3a946ed8873
--- /dev/null
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
+
+int main(int, char**) {
+  {
+    // Tests construction from a duration with an unsigned representation.
+    using Duration = std::chrono::duration<unsigned, std::milli>;
+
+    // 1 hour + 1 minute + 1 second + 1 millisecond
+    constexpr Duration d{3'661'001};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(!hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
+    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
+  }
+
+  {
+    // Tests construction from a negative duration with a custom representation.
+    using Duration = std::chrono::duration<SignedRep>;
+
+    // 1 hour + 1 minute + 1 second
+    constexpr Duration d{SignedRep{-3'661}};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::seconds{0});
+    static_assert(hms.to_duration() == std::chrono::seconds{-3'661});
+  }
+
+  return 0;
+}

>From 5504e7564dcfc299442cbf1d054a2be329c3e512 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 14:19:47 +0800
Subject: [PATCH 2/2] fix missing header file

---
 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
index 1d3a946ed8873..b33917d262ba8 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -19,6 +19,7 @@
 // LWG4274: The hh_mm_ss constructor supports unsigned durations.
 
 #include <chrono>
+#include <ratio>
 
 // A signed arithmetic-like type used as a custom duration representation.
 struct SignedRep {


From libcxx-commits at lists.llvm.org  Fri Jul 24 04:06:58 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 04:06:58 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a634752.4a342cbc.1aeb63.23bb@mx.google.com>


emmett2020 wrote:

ping

https://github.com/llvm/llvm-project/pull/209686

From libcxx-commits at lists.llvm.org  Fri Jul 24 04:11:40 2026
From: libcxx-commits at lists.llvm.org (Aaron Ballman via libcxx-commits)
Date: Fri, 24 Jul 2026 04:11:40 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [lldb] [clang]: implement
	std::meta::is_type (PR #211653)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211653@github.com>
Message-ID: <6a63486c.160324b0.17e275.0330@mx.google.com>


AaronBallman wrote:

> > `__cpp_impl_reflection` is only enabled when `-freflection` is on
> 
> Why? Can we avoid `-freflection` and just enable reflection since C++26? Or can we make `-freflection` meaningless since C++26 and allow it in old modes as an extension (which is not very conforming, though)?

This is something we've discussed in the reflection sync-up meetings. Reflection will likely never be extended to older language modes (and certainly never to C) given it's design issues; it drags in too much baggage around handling of constant expressions, can break existing code due to syntactic ambiguities, etc. It's not off the table for older C++ modes, but is not something we should consider until after the implementation is rock solid and we have a lot of deployment experience. (IMO, extension work should be dedicated towards supporting reflection over Clang's extensions before working towards exposing in older language modes.)

Given the experimental nature of the feature and the fact that we already know C++29 will make significant changes to it, we want it kept behind a flag for now. The goal is to eventually get to the point of not needing the flag so that `-std=c++26` suffices (at least for the non-generative parts of reflection), but we want to be conservative given the feature.

https://github.com/llvm/llvm-project/pull/211653

From libcxx-commits at lists.llvm.org  Fri Jul 24 04:44:09 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 24 Jul 2026 04:44:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a635009.0318b02e.530b7.970a@mx.google.com>


================
@@ -45,6 +45,14 @@ class hh_mm_ss {
     return 0;
   }
 
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+      return __d;
+    } else {
+      return __is_neg ? -__d : __d;
+    }
----------------
frederick-vs-ja wrote:

I think LLVM prefers a simpler style, see [here](https://llvm.org/docs/CodingStandards.html#don-t-use-braces-on-simple-single-statement-bodies-of-if-else-loop-statements).
```suggestion
    if constexpr (is_unsigned_v<typename _Duration::rep>)
      return __d;
    else
      return __is_neg ? -__d : __d;
```

https://github.com/llvm/llvm-project/pull/209686

From libcxx-commits at lists.llvm.org  Fri Jul 24 04:44:09 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 24 Jul 2026 04:44:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a635009.1b0626d0.1ff4c3.588f@mx.google.com>


================
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+#include <ratio>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
----------------
frederick-vs-ja wrote:

This stuff looks completely irrelevant to LWG4274. Can we avoid it?

https://github.com/llvm/llvm-project/pull/209686

From libcxx-commits at lists.llvm.org  Fri Jul 24 04:44:09 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 24 Jul 2026 04:44:09 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a635009.7d702ed2.2f6c05.de4a@mx.google.com>


================
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+#include <ratio>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
+
+int main(int, char**) {
+  {
+    // Tests construction from a duration with an unsigned representation.
+    using Duration = std::chrono::duration<unsigned, std::milli>;
+
+    // 1 hour + 1 minute + 1 second + 1 millisecond
+    constexpr Duration d{3'661'001};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(!hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
+    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
----------------
frederick-vs-ja wrote:

Previously we didn't concentratedly test the constructor. Would you mind to follow the existing style which tests these member functions in different test files?

Note that using such a duration is already requiring that the constructor call to be well-formed.

https://github.com/llvm/llvm-project/pull/209686

From libcxx-commits at lists.llvm.org  Fri Jul 24 05:19:20 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 24 Jul 2026 05:19:20 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a635848.13be6df8.323b61.2d80@mx.google.com>


https://github.com/frederick-vs-ja edited https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Fri Jul 24 05:21:16 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 24 Jul 2026 05:21:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a6358bc.13be6df8.323b61.9c6d@mx.google.com>


https://github.com/ldionne approved this pull request.

LGTM w/ comment added and green CI

https://github.com/llvm/llvm-project/pull/210951

From libcxx-commits at lists.llvm.org  Fri Jul 24 05:21:16 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 24 Jul 2026 05:21:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a6358bc.1e1ac41d.317291.904e@mx.google.com>


https://github.com/ldionne edited https://github.com/llvm/llvm-project/pull/210951

From libcxx-commits at lists.llvm.org  Fri Jul 24 05:21:16 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 24 Jul 2026 05:21:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a6358bc.b1416da1.20b4cf.6709@mx.google.com>


================
@@ -15,6 +15,21 @@
 #include <streambuf>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+
+template <class _CharT, class _Traits>
+bool basic_filebuf<_CharT, _Traits>::__read_mode() {
----------------
ldionne wrote:

Let's add a comment explaining why this is still provided even though it's unused.

https://github.com/llvm/llvm-project/pull/210951

From libcxx-commits at lists.llvm.org  Fri Jul 24 05:22:16 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Fri, 24 Jul 2026 05:22:16 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a6358f8.13be6df8.323b61.d120@mx.google.com>


================
@@ -387,27 +387,53 @@ namespace __math {
 
 // fpclassify
 
+#      ifdef _LIBCPP_PREFERRED_OVERLOAD
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int fpclassify(float __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(long double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(_A1 __x) _NOEXCEPT {
+  return __x == 0 ? FP_ZERO : FP_NORMAL;
+}
----------------
frederick-vs-ja wrote:

Do we need to add `_LIBCPP_PREFERRED_OVERLOAD` version for this overload for integers?

I'd like to avoid to adding `_LIBCPP_PREFERRED_OVERLOAD` version for this. Currently UCRT doesn't provide an overload corresponding to this one, so there shouldn't be conflict.

https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Fri Jul 24 05:27:50 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 24 Jul 2026 05:27:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Fix ungetc failing after xsgetn
	(PR #210951)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210951@github.com>
Message-ID: <6a635a46.3c65ca91.38aa9e.ed2a@mx.google.com>


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/210951

>From 2959fa9f8261019990b8a4e766684e1029ffbde6 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 21 Jul 2026 13:14:55 +0200
Subject: [PATCH] [libc++] Fix ungetc failing after xsgetn

---
 libcxx/include/fstream                        |  73 ++++++---
 libcxx/src/ios.instantiations.cpp             |  21 +++
 .../fstreams/filebuf.members/test.dat         |   1 +
 .../filebuf.members/xsgetn.buffer.pass.cpp    | 155 ++++++++++++++++++
 .../fstreams/ifstream.members/xsgetn.pass.cpp |  14 ++
 5 files changed, 241 insertions(+), 23 deletions(-)
 create mode 100644 libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
 create mode 100644 libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 7b84bf6609086..92c79e81d4906 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -311,6 +311,8 @@ protected:
 
   _LIBCPP_HIDE_FROM_ABI_VIRTUAL streamsize xsgetn(char_type* __str, streamsize __len) override {
     if (__file_ && __always_noconv_) {
+      char_type __1buf;
+      __read_guard __guard(__1buf, *this);
       const streamsize __n = std::min(this->egptr() - this->gptr(), __len);
       if (__n != 0) {
         traits_type::copy(__str, this->gptr(), __n);
@@ -319,9 +321,16 @@ protected:
       const streamsize __remainder    = __len - __n;
       const streamsize __buffer_space = this->egptr() - this->eback();
 
-      if (__remainder >= __buffer_space)
-        return std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
-      else if (__remainder > 0)
+      if (__remainder >= __buffer_space) {
+        auto __res = std::fread(__str + __n, sizeof(char_type), __remainder, __file_) + __n;
+
+        // Copy the buffer-sized tail into the buffer so that `unget`ting works correctly
+        auto __buf_size = std::min<size_t>(__res, this->egptr() - this->eback());
+        traits_type::copy(this->eback(), __str + __res - __buf_size, __buf_size);
+        this->setg(this->eback(), this->eback() + __buf_size, this->eback() + __buf_size);
+
+        return __res;
+      } else if (__remainder > 0)
         return basic_streambuf<_CharT, _Traits>::xsgetn(__str + __n, __remainder) + __n;
       return __n;
     }
@@ -384,6 +393,40 @@ private:
   bool __always_noconv_;
 
   bool __read_mode();
+
+  struct [[__nodiscard__]] __read_guard {
+    basic_filebuf& __self_;
+    size_t __unget_size_;
+    char_type& __1buf_;
+
+    _LIBCPP_HIDE_FROM_ABI __read_guard(char_type& __1buf, basic_filebuf& __self) : __self_(__self), __1buf_(__1buf) {
+      if (__self.__cm_ & ios_base::in) {
+        __unget_size_ = std::min<size_t>((__self.egptr() - __self.eback()) / 2, 4);
+      } else {
+        __self.setp(nullptr, nullptr);
+        if (__self.__always_noconv_) {
+          __self.setg(reinterpret_cast<char_type*>(__self.__extbuf_),
+                      reinterpret_cast<char_type*>(__self.__extbuf_) + __self.__ebs_,
+                      reinterpret_cast<char_type*>(__self.__extbuf_) + __self.__ebs_);
+        } else {
+          __self.setg(__self.__intbuf_, __self.__intbuf_ + __self.__ibs_, __self.__intbuf_ + __self.__ibs_);
+        }
+        __self.__cm_  = ios_base::in;
+        __unget_size_ = 0;
+      }
+
+      if (__self.gptr() == nullptr)
+        __self.setg(std::addressof(__1buf_), std::addressof(__1buf_) + 1, std::addressof(__1buf_) + 1);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI size_t __unget_size() { return __unget_size_; }
+
+    _LIBCPP_HIDE_FROM_ABI ~__read_guard() {
+      if (__self_.eback() == std::addressof(__1buf_))
+        __self_.setg(nullptr, nullptr, nullptr);
+    }
+  };
+
   void __write_mode();
 
   _LIBCPP_HIDE_FROM_ABI static int __fseek(FILE* __file, pos_type __offset, int __whence);
@@ -802,11 +845,11 @@ template <class _CharT, class _Traits>
 typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>::underflow() {
   if (__file_ == nullptr)
     return traits_type::eof();
-  bool __initial = __read_mode();
+
   char_type __1buf;
-  if (this->gptr() == nullptr)
-    this->setg(std::addressof(__1buf), std::addressof(__1buf) + 1, std::addressof(__1buf) + 1);
-  const size_t __unget_sz = __initial ? 0 : std::min<size_t>((this->egptr() - this->eback()) / 2, 4);
+  __read_guard __guard(__1buf, *this);
+
+  const size_t __unget_sz = __guard.__unget_size();
   int_type __c            = traits_type::eof();
   if (this->gptr() == this->egptr()) {
     std::memmove(this->eback(), this->egptr() - __unget_sz, __unget_sz * sizeof(char_type));
@@ -849,8 +892,6 @@ typename basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>
     }
   } else
     __c = traits_type::to_int_type(*this->gptr());
-  if (this->eback() == std::addressof(__1buf))
-    this->setg(nullptr, nullptr, nullptr);
   return __c;
 }
 
@@ -1141,20 +1182,6 @@ void basic_filebuf<_CharT, _Traits>::imbue(const locale& __loc) {
   }
 }
 
-template <class _CharT, class _Traits>
-bool basic_filebuf<_CharT, _Traits>::__read_mode() {
-  if (!(__cm_ & ios_base::in)) {
-    this->setp(nullptr, nullptr);
-    if (__always_noconv_)
-      this->setg((char_type*)__extbuf_, (char_type*)__extbuf_ + __ebs_, (char_type*)__extbuf_ + __ebs_);
-    else
-      this->setg(__intbuf_, __intbuf_ + __ibs_, __intbuf_ + __ibs_);
-    __cm_ = ios_base::in;
-    return true;
-  }
-  return false;
-}
-
 template <class _CharT, class _Traits>
 void basic_filebuf<_CharT, _Traits>::__write_mode() {
   if (!(__cm_ & ios_base::out)) {
diff --git a/libcxx/src/ios.instantiations.cpp b/libcxx/src/ios.instantiations.cpp
index a8d267f7cfd42..a50d3b745944a 100644
--- a/libcxx/src/ios.instantiations.cpp
+++ b/libcxx/src/ios.instantiations.cpp
@@ -15,6 +15,26 @@
 #include <streambuf>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+
+#if _LIBCPP_HAS_FILESYSTEM
+// This is provided for ABI compatiblity with programs compiled against old headers.
+// TODO: Is this actually required? If so, this should be guarded with
+// `_LIBCPP_AVAILABILITY_MINIMUM_HEADER_VERSION < 24`. Otherwise this should be removed.
+template <class _CharT, class _Traits>
+bool basic_filebuf<_CharT, _Traits>::__read_mode() {
+  if (!(__cm_ & ios_base::in)) {
+    this->setp(nullptr, nullptr);
+    if (__always_noconv_)
+      this->setg((char_type*)__extbuf_, (char_type*)__extbuf_ + __ebs_, (char_type*)__extbuf_ + __ebs_);
+    else
+      this->setg(__intbuf_, __intbuf_ + __ibs_, __intbuf_ + __ibs_);
+    __cm_ = ios_base::in;
+    return true;
+  }
+  return false;
+}
+#endif
 
 // Original explicit instantiations provided in the library
 template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_ios<char>;
@@ -45,4 +65,5 @@ template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS basic_filebuf<char>;
 
 // Add more here if needed...
 
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
new file mode 100644
index 0000000000000..6fe86c31590f8
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/test.dat
@@ -0,0 +1 @@
+This is a bunch of data so the test can read some stuff and not instantly run out of data to read.
diff --git a/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp
new file mode 100644
index 0000000000000..86f1f390d9dc4
--- /dev/null
+++ b/libcxx/test/extensions/libcxx/input.output/file.streams/fstreams/filebuf.members/xsgetn.buffer.pass.cpp
@@ -0,0 +1,155 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// FILE_DEPENDENCIES: test.dat
+
+// <fstream>
+
+// streamsize basic_filebuf::xsgetn(char_type*, streamsize);
+
+// Test that xsgetn buffers properly. Specifically, we guarantee that `unget()` can be called at least four times.
+
+#include <cassert>
+#include <fstream>
+#include <string>
+
+#include "platform_support.h"
+
+void small_file_tests() {
+  char buffer[12];
+
+  { // Check that we can unget() when reading a single character
+    std::ifstream is("test.dat");
+    assert(is.is_open());
+    char buf[1];
+    is.read(buf, 1);
+    assert(buf[0] == 'T');
+    is.unget();
+    assert(is.good());
+  }
+
+  { // Check that we can unget() when reading a single character with a user-provided buffer
+    std::ifstream is("test.dat");
+    assert(is.is_open());
+    is.rdbuf()->pubsetbuf(buffer, 12);
+    char buf[1];
+    is.read(buf, 1);
+    assert(buf[0] == 'T');
+    is.unget();
+    assert(is.good());
+  }
+
+  { // Check that unget() works as expected when the remainder is smaller than the buffer
+    std::ifstream is("test.dat");
+    assert(is.is_open());
+    is.rdbuf()->pubsetbuf(buffer, 12);
+    (void)is.rdbuf()->sgetc(); // Make sure there is data in the buffer
+    char buf[17];
+    buf[16] = '\0';
+    is.read(buf, 16);
+    assert(buf == std::string("This is a bunch "));
+    for (size_t i = 0; i != 4; ++i)
+      is.unget();
+    assert(is.good());
+    is.read(buf, 4);
+    buf[4] = '\0';
+    assert(buf == std::string("nch "));
+  }
+
+  { // Check that unget() works as expected when the remainder is larger than the buffer
+    std::ifstream is("test.dat");
+    assert(is.is_open());
+    is.rdbuf()->pubsetbuf(buffer, 12);
+    char buf[33];
+    buf[32] = '\0';
+    is.read(buf, 32);
+    assert(buf == std::string("This is a bunch of data so the t"));
+    for (size_t i = 0; i != 4; ++i)
+      is.unget();
+    assert(is.good());
+    is.read(buf, 4);
+    buf[4] = '\0';
+    assert(is.good());
+    assert(buf == std::string("he t"));
+  }
+
+  { // read an empty file
+    std::string empty_file = get_temp_file_name();
+    {
+      std::ofstream os(empty_file);
+    }
+    std::ifstream is(empty_file);
+    is.rdbuf()->pubsetbuf(nullptr, 64);
+
+    char buf[100];
+    is.read(buf, 100);
+    assert(is.eof());
+    assert(is.gcount() == 0);
+
+    is.clear();
+    is.unget();
+    assert(is.fail());
+    std::remove(empty_file.c_str());
+  }
+}
+
+static std::string make_pattern(std::size_t n) {
+  std::string s(n, '\0');
+  for (std::size_t i = 0; i != n; ++i)
+    s[i] = static_cast<char>('0' + (i % 10));
+  return s;
+}
+
+void large_file_tests() {
+  const std::string data = make_pattern(10000);
+  std::string file       = get_temp_file_name();
+  { // Prepare the file
+    std::ofstream os(file);
+    assert(os.write(data.data(), data.size()));
+  }
+
+  { // default buffer with a read larger than the buffer
+    std::ifstream is(file);
+    assert(is.is_open());
+    std::string buf;
+    buf.resize(8000);
+
+    is.read(&*buf.begin(), 8000);
+    assert(is.gcount() == 8000);
+    assert(std::string(buf.data(), 8000) == data.substr(0, 8000));
+    is.unget();
+    assert(is.good());
+    assert(is.get() == data[7999]); // the ungotten character
+    assert(is.get() == data[8000]); // reading forward continues from the right place
+  }
+
+  { // EOF before the buffer is full
+    std::ifstream is(file);
+    assert(is.is_open());
+    is.rdbuf()->pubsetbuf(nullptr, 64);
+
+    std::string buf;
+    buf.resize(data.size() + 100);
+
+    is.read(&*buf.begin(), data.size() + 100);
+    assert(is.gcount() == static_cast<std::streamsize>(data.size()));
+    assert(is.rdstate() == (std::ios::eofbit | std::ios::failbit));
+    is.clear(); // Clear the failbit due to trying to read more data than available
+    is.unget();
+    assert(is.good());
+    assert(is.get() == data.back());
+  }
+
+  std::remove(file.c_str());
+}
+
+int main(int, char**) {
+  small_file_tests();
+  large_file_tests();
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
index d9ccc2fe62914..35c4787e2a29f 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/xsgetn.pass.cpp
@@ -25,6 +25,14 @@
 
 #include "test_macros.h"
 
+void check_unget(std::filebuf* fb, char expected) {
+  auto c = fb->sungetc();
+  if (c != EOF) {
+    assert(c == expected);
+    assert(fb->sbumpc() == expected);
+  }
+}
+
 int main(int, char**) {
   std::vector<char> stream_buffer(10);
   std::ifstream fs("xsgetn.test.dat");
@@ -40,33 +48,39 @@ int main(int, char**) {
   { // Check that a read smaller than the buffer works fine
     assert(fb->sgetn(test_buffer.data(), 5) == 5);
     assert(std::string(test_buffer.data(), 5) == "this ");
+    check_unget(fb, ' ');
   }
   { // Check that reading up to the buffer end works fine
     assert(fb->sgetn(test_buffer.data(), 5) == 5);
     assert(std::string(test_buffer.data(), 5) == "is so");
+    check_unget(fb, 'o');
   }
   { // Check that reading from an empty buffer, but more than the buffer can
     // hold works fine
     test_buffer.resize(12);
     assert(fb->sgetn(test_buffer.data(), 12) == 12);
     assert(std::string(test_buffer.data(), 12) == "me random da");
+    check_unget(fb, 'a');
   }
   { // Check that reading from a non-empty buffer, and more than the buffer can
     // hold works fine Fill the buffer up
     test_buffer.resize(2);
     assert(fb->sgetn(test_buffer.data(), 2) == 2);
     assert(std::string(test_buffer.data(), 2) == "ta");
+    check_unget(fb, 'a');
 
     // Do the actual check
     test_buffer.resize(12);
     assert(fb->sgetn(test_buffer.data(), 12) == 12);
     assert(std::string(test_buffer.data(), 12) == " to be able ");
+    check_unget(fb, ' ');
   }
   { // Check that trying to read more than the file size works fine
     test_buffer.resize(30);
     assert(fb->sgetn(test_buffer.data(), 30) == 24);
     test_buffer.resize(24);
     assert(std::string(test_buffer.data(), 24) == "to test buffer behaviour");
+    check_unget(fb, 'r');
   }
   { // Ensure that the read fails gracefully with an unopened ifstream
     // See https://llvm.org/PR168628


From libcxx-commits at lists.llvm.org  Fri Jul 24 05:58:57 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 05:58:57 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a636191.bc28b987.1c0bcd.8db6@mx.google.com>


================
@@ -387,27 +387,53 @@ namespace __math {
 
 // fpclassify
 
+#      ifdef _LIBCPP_PREFERRED_OVERLOAD
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int fpclassify(float __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(long double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(_A1 __x) _NOEXCEPT {
+  return __x == 0 ? FP_ZERO : FP_NORMAL;
+}
----------------
lknknm wrote:

I applied this as part of you previous suggestion https://github.com/llvm/llvm-project/pull/210993#discussion_r3626947336

https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Fri Jul 24 05:59:11 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 05:59:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a63619f.af4834b4.5fdf.8672@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Fri Jul 24 05:59:35 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 05:59:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a6361b7.1e1ac41d.317291.a95b@mx.google.com>


https://github.com/lknknm deleted https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Fri Jul 24 06:00:25 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 06:00:25 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a6361e9.e5907290.33b622.162b@mx.google.com>


================
@@ -387,27 +387,53 @@ namespace __math {
 
 // fpclassify
 
+#      ifdef _LIBCPP_PREFERRED_OVERLOAD
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int fpclassify(float __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(long double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(_A1 __x) _NOEXCEPT {
+  return __x == 0 ? FP_ZERO : FP_NORMAL;
+}
----------------
lknknm wrote:

I will try it out. I thought all of them would need `_LIBCPP_PREFERRED_OVERLOAD` as part of your suggestion. 

https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Fri Jul 24 06:01:12 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 06:01:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a636218.b4a331f0.2642a.c385@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Fri Jul 24 06:17:28 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 06:17:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a6365e8.8e75e0cb.2de827.20a7@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210993

>From 8e4ce1588756045195a9c05c3688ee0919433b32 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 15:13:46 +0200
Subject: [PATCH 1/6] add: constexpr to fpclassify

---
 libcxx/include/cmath                                      | 2 +-
 libcxx/include/math.h                                     | 8 ++++----
 .../libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..992854b0490a3 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -138,7 +138,7 @@ long double    tanhl(long double x);
 
 bool signbit(arithmetic x);
 
-int fpclassify(arithmetic x);
+int fpclassify(arithmetic x); // constexpr since C++23
 
 bool isfinite(arithmetic x);
 bool isinf(arithmetic x);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..86ad9a1ae9b10 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -389,22 +389,22 @@ namespace __math {
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..926ff37cddbfc 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -203,9 +203,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From b6ecf4cfc60def795b8f4814af4e9df2feda5121 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 16:12:31 +0200
Subject: [PATCH 2/6] update: gcc tests

---
 .../libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..455c03ef58d18 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -197,9 +197,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From f191ac6676bcf419b3eb0aa92ddcb1291744a796 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 18:45:51 +0200
Subject: [PATCH 3/6] add: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 79 +++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/fpclassify.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
new file mode 100644
index 0000000000000..fda9f28a1389f
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool fpclassify(floating-point-type x); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
+    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+
+    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+
+    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(-0.0)) == FP_ZERO);
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+
+    if (std::is_signed<T>::value) {
+      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 912975ef33f9fd185b3accd72d7016d7d7be6ea5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:22:26 +0200
Subject: [PATCH 4/6] add: _LIBCPP_PREFERRED_OVERLOAD

---
 libcxx/include/math.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 86ad9a1ae9b10..fa8d293604c7f 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -387,6 +387,30 @@ namespace __math {
 
 // fpclassify
 
+#      ifdef _LIBCPP_PREFERRED_OVERLOAD
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int fpclassify(float __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(long double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(_A1 __x) _NOEXCEPT {
+  return __x == 0 ? FP_ZERO : FP_NORMAL;
+}
+
+#      else
+
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
@@ -408,6 +432,8 @@ template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
+#      endif
+
 } // namespace __math
 
 _LIBCPP_END_NAMESPACE_STD

>From 1b02edd8888f7fe60ed3053e6e8112902b5dac3b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:32:06 +0200
Subject: [PATCH 5/6] refactor: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 65 ++++++++++---------
 1 file changed, 33 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
index fda9f28a1389f..fcfd73d9bacf2 100644
--- a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -18,62 +18,63 @@
 
 struct TestFloat {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
-    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
-    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                    = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max   = lim::max();
+    TEST_CONSTEXPR_CXX23 T min   = lim::min();
+    TEST_CONSTEXPR_CXX23 T d_min = lim::denorm_min();
+    TEST_CONSTEXPR_CXX23 T inf   = lim::infinity();
+    TEST_CONSTEXPR_CXX23 T nan   = lim::quiet_NaN();
+    TEST_CONSTEXPR_CXX23 T s_nan = lim::signaling_NaN();
 
-    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
-    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(nan) == FP_NAN);
+    assert(std::fpclassify(s_nan) == FP_NAN);
+
+    assert(std::fpclassify(inf) == FP_INFINITE);
+    assert(std::fpclassify(-inf) == FP_INFINITE);
 
     assert(std::fpclassify(T(1)) == FP_NORMAL);
     assert(std::fpclassify(T(-1)) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+    assert(std::fpclassify(max) == FP_NORMAL);
+    assert(std::fpclassify(min) == FP_NORMAL);
 
-    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+    assert(std::fpclassify(d_min) == FP_SUBNORMAL);
 
     assert(std::fpclassify(T(0)) == FP_ZERO);
     assert(std::fpclassify(T(-0.0)) == FP_ZERO);
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
 struct TestInt {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                  = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max = lim::max();
+    TEST_CONSTEXPR_CXX23 T low = lim::lowest();
+
     assert(std::fpclassify(T(0)) == FP_ZERO);
     assert(std::fpclassify(T(1)) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(max) == FP_NORMAL);
 
     if (std::is_signed<T>::value) {
-      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(low) == FP_NORMAL);
       assert(std::fpclassify(T(-1)) == FP_NORMAL);
     }
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX23 bool test() {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 23
+  static_assert(test());
+#endif
+
   return 0;
 }

>From 5ff2b23ff45081ec8cdef90f220fbca3cdba9c90 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 24 Jul 2026 15:17:12 +0200
Subject: [PATCH 6/6] remove: _LIBCPP_PREFERRED_OVERLOAD for int overload

---
 libcxx/include/math.h | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index fa8d293604c7f..9b14e57d7b262 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -402,13 +402,6 @@ fpclassify(double __x) _NOEXCEPT {
 fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
-
-template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
-fpclassify(_A1 __x) _NOEXCEPT {
-  return __x == 0 ? FP_ZERO : FP_NORMAL;
-}
-
 #      else
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
@@ -431,7 +424,6 @@ template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
-
 #      endif
 
 } // namespace __math


From libcxx-commits at lists.llvm.org  Fri Jul 24 06:18:23 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 06:18:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a63661f.6c8668f2.1842d3.b188@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210993

>From 8e4ce1588756045195a9c05c3688ee0919433b32 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 15:13:46 +0200
Subject: [PATCH 1/7] add: constexpr to fpclassify

---
 libcxx/include/cmath                                      | 2 +-
 libcxx/include/math.h                                     | 8 ++++----
 .../libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..992854b0490a3 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -138,7 +138,7 @@ long double    tanhl(long double x);
 
 bool signbit(arithmetic x);
 
-int fpclassify(arithmetic x);
+int fpclassify(arithmetic x); // constexpr since C++23
 
 bool isfinite(arithmetic x);
 bool isinf(arithmetic x);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..86ad9a1ae9b10 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -389,22 +389,22 @@ namespace __math {
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..926ff37cddbfc 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -203,9 +203,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From b6ecf4cfc60def795b8f4814af4e9df2feda5121 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 16:12:31 +0200
Subject: [PATCH 2/7] update: gcc tests

---
 .../libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..455c03ef58d18 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -197,9 +197,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From f191ac6676bcf419b3eb0aa92ddcb1291744a796 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 18:45:51 +0200
Subject: [PATCH 3/7] add: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 79 +++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/fpclassify.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
new file mode 100644
index 0000000000000..fda9f28a1389f
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool fpclassify(floating-point-type x); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
+    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+
+    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+
+    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(-0.0)) == FP_ZERO);
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+
+    if (std::is_signed<T>::value) {
+      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 912975ef33f9fd185b3accd72d7016d7d7be6ea5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:22:26 +0200
Subject: [PATCH 4/7] add: _LIBCPP_PREFERRED_OVERLOAD

---
 libcxx/include/math.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 86ad9a1ae9b10..fa8d293604c7f 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -387,6 +387,30 @@ namespace __math {
 
 // fpclassify
 
+#      ifdef _LIBCPP_PREFERRED_OVERLOAD
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int fpclassify(float __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(long double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(_A1 __x) _NOEXCEPT {
+  return __x == 0 ? FP_ZERO : FP_NORMAL;
+}
+
+#      else
+
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
@@ -408,6 +432,8 @@ template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
+#      endif
+
 } // namespace __math
 
 _LIBCPP_END_NAMESPACE_STD

>From 1b02edd8888f7fe60ed3053e6e8112902b5dac3b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:32:06 +0200
Subject: [PATCH 5/7] refactor: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 65 ++++++++++---------
 1 file changed, 33 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
index fda9f28a1389f..fcfd73d9bacf2 100644
--- a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -18,62 +18,63 @@
 
 struct TestFloat {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
-    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
-    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                    = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max   = lim::max();
+    TEST_CONSTEXPR_CXX23 T min   = lim::min();
+    TEST_CONSTEXPR_CXX23 T d_min = lim::denorm_min();
+    TEST_CONSTEXPR_CXX23 T inf   = lim::infinity();
+    TEST_CONSTEXPR_CXX23 T nan   = lim::quiet_NaN();
+    TEST_CONSTEXPR_CXX23 T s_nan = lim::signaling_NaN();
 
-    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
-    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(nan) == FP_NAN);
+    assert(std::fpclassify(s_nan) == FP_NAN);
+
+    assert(std::fpclassify(inf) == FP_INFINITE);
+    assert(std::fpclassify(-inf) == FP_INFINITE);
 
     assert(std::fpclassify(T(1)) == FP_NORMAL);
     assert(std::fpclassify(T(-1)) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+    assert(std::fpclassify(max) == FP_NORMAL);
+    assert(std::fpclassify(min) == FP_NORMAL);
 
-    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+    assert(std::fpclassify(d_min) == FP_SUBNORMAL);
 
     assert(std::fpclassify(T(0)) == FP_ZERO);
     assert(std::fpclassify(T(-0.0)) == FP_ZERO);
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
 struct TestInt {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                  = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max = lim::max();
+    TEST_CONSTEXPR_CXX23 T low = lim::lowest();
+
     assert(std::fpclassify(T(0)) == FP_ZERO);
     assert(std::fpclassify(T(1)) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(max) == FP_NORMAL);
 
     if (std::is_signed<T>::value) {
-      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(low) == FP_NORMAL);
       assert(std::fpclassify(T(-1)) == FP_NORMAL);
     }
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX23 bool test() {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 23
+  static_assert(test());
+#endif
+
   return 0;
 }

>From 5ff2b23ff45081ec8cdef90f220fbca3cdba9c90 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 24 Jul 2026 15:17:12 +0200
Subject: [PATCH 6/7] remove: _LIBCPP_PREFERRED_OVERLOAD for int overload

---
 libcxx/include/math.h | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index fa8d293604c7f..9b14e57d7b262 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -402,13 +402,6 @@ fpclassify(double __x) _NOEXCEPT {
 fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
-
-template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
-fpclassify(_A1 __x) _NOEXCEPT {
-  return __x == 0 ? FP_ZERO : FP_NORMAL;
-}
-
 #      else
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
@@ -431,7 +424,6 @@ template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
-
 #      endif
 
 } // namespace __math

>From 742a100c4432c14549ef9e0cc6dd1dce6b40e316 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 24 Jul 2026 15:18:06 +0200
Subject: [PATCH 7/7] remove trailing line

---
 libcxx/test/std/numerics/c.math/fpclassify.pass.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
index fcfd73d9bacf2..10b2297fce105 100644
--- a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -75,6 +75,5 @@ int main(int, char**) {
 #if TEST_STD_VER >= 23
   static_assert(test());
 #endif
-
   return 0;
 }


From libcxx-commits at lists.llvm.org  Fri Jul 24 06:18:35 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 06:18:35 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a63662b.a1accbb4.1ace1c.99a2@mx.google.com>


https://github.com/lknknm edited https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Fri Jul 24 06:18:47 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 06:18:47 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a636637.0c05e68e.81651.88f4@mx.google.com>


https://github.com/lknknm updated https://github.com/llvm/llvm-project/pull/210993

>From 8e4ce1588756045195a9c05c3688ee0919433b32 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 15:13:46 +0200
Subject: [PATCH 1/7] add: constexpr to fpclassify

---
 libcxx/include/cmath                                      | 2 +-
 libcxx/include/math.h                                     | 8 ++++----
 .../libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index 208791ca0804c..992854b0490a3 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -138,7 +138,7 @@ long double    tanhl(long double x);
 
 bool signbit(arithmetic x);
 
-int fpclassify(arithmetic x);
+int fpclassify(arithmetic x); // constexpr since C++23
 
 bool isfinite(arithmetic x);
 bool isinf(arithmetic x);
diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 1db61538e995f..86ad9a1ae9b10 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -389,22 +389,22 @@ namespace __math {
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class = int>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
 
 template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
index 93865afc5e68b..926ff37cddbfc 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-clang.pass.cpp
@@ -203,9 +203,9 @@ int main(int, char**) {
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_NOT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From b6ecf4cfc60def795b8f4814af4e9df2feda5121 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 16:12:31 +0200
Subject: [PATCH 2/7] update: gcc tests

---
 .../libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
index d8779706bcee2..455c03ef58d18 100644
--- a/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
+++ b/libcxx/test/libcxx/numerics/c.math/constexpr-cxx23-gcc.pass.cpp
@@ -197,9 +197,9 @@ int main(int, char**) {
   ASSERT_CONSTEXPR_CXX23(std::fmaf(1.0f, 1.0f, 1.0f) == 2.0f);
   ASSERT_CONSTEXPR_CXX23(std::fmal(1.0L, 1.0L, 1.0L) == 2.0L);
 
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
-  ASSERT_NOT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0f) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0) == FP_NORMAL);
+  ASSERT_CONSTEXPR_CXX23(std::fpclassify(-1.0L) == FP_NORMAL);
 
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0f) == 1);
   ASSERT_CONSTEXPR_CXX23(std::isfinite(-1.0) == 1);

>From f191ac6676bcf419b3eb0aa92ddcb1291744a796 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Tue, 21 Jul 2026 18:45:51 +0200
Subject: [PATCH 3/7] add: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 79 +++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/fpclassify.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
new file mode 100644
index 0000000000000..fda9f28a1389f
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// bool fpclassify(floating-point-type x); // constexpr since C++23
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+struct TestFloat {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
+    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+
+    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+
+    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(-0.0)) == FP_ZERO);
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+struct TestInt {
+  template <class T>
+  static TEST_CONSTEXPR_CXX23 bool test() {
+    assert(std::fpclassify(T(0)) == FP_ZERO);
+    assert(std::fpclassify(T(1)) == FP_NORMAL);
+    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+
+    if (std::is_signed<T>::value) {
+      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(T(-1)) == FP_NORMAL);
+    }
+
+    return true;
+  }
+
+  template <class T>
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    test<T>();
+#if TEST_STD_VER >= 23
+    static_assert(test<T>());
+#endif
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::floating_point_types(), TestFloat());
+  types::for_each(types::integral_types(), TestInt());
+
+  return 0;
+}

>From 912975ef33f9fd185b3accd72d7016d7d7be6ea5 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:22:26 +0200
Subject: [PATCH 4/7] add: _LIBCPP_PREFERRED_OVERLOAD

---
 libcxx/include/math.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index 86ad9a1ae9b10..fa8d293604c7f 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -387,6 +387,30 @@ namespace __math {
 
 // fpclassify
 
+#      ifdef _LIBCPP_PREFERRED_OVERLOAD
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int fpclassify(float __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(long double __x) _NOEXCEPT {
+  return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
+}
+
+template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
+fpclassify(_A1 __x) _NOEXCEPT {
+  return __x == 0 ? FP_ZERO : FP_NORMAL;
+}
+
+#      else
+
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
 template <class = int>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(float __x) _NOEXCEPT {
@@ -408,6 +432,8 @@ template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
 
+#      endif
+
 } // namespace __math
 
 _LIBCPP_END_NAMESPACE_STD

>From 1b02edd8888f7fe60ed3053e6e8112902b5dac3b Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Thu, 23 Jul 2026 14:32:06 +0200
Subject: [PATCH 5/7] refactor: fpclassify.pass.cpp

---
 .../std/numerics/c.math/fpclassify.pass.cpp   | 65 ++++++++++---------
 1 file changed, 33 insertions(+), 32 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
index fda9f28a1389f..fcfd73d9bacf2 100644
--- a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -18,62 +18,63 @@
 
 struct TestFloat {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
-    assert(std::fpclassify(std::numeric_limits<T>::quiet_NaN()) == FP_NAN);
-    assert(std::fpclassify(std::numeric_limits<T>::signaling_NaN()) == FP_NAN);
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                    = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max   = lim::max();
+    TEST_CONSTEXPR_CXX23 T min   = lim::min();
+    TEST_CONSTEXPR_CXX23 T d_min = lim::denorm_min();
+    TEST_CONSTEXPR_CXX23 T inf   = lim::infinity();
+    TEST_CONSTEXPR_CXX23 T nan   = lim::quiet_NaN();
+    TEST_CONSTEXPR_CXX23 T s_nan = lim::signaling_NaN();
 
-    assert(std::fpclassify(std::numeric_limits<T>::infinity()) == FP_INFINITE);
-    assert(std::fpclassify(-std::numeric_limits<T>::infinity()) == FP_INFINITE);
+    assert(std::fpclassify(nan) == FP_NAN);
+    assert(std::fpclassify(s_nan) == FP_NAN);
+
+    assert(std::fpclassify(inf) == FP_INFINITE);
+    assert(std::fpclassify(-inf) == FP_INFINITE);
 
     assert(std::fpclassify(T(1)) == FP_NORMAL);
     assert(std::fpclassify(T(-1)) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::min()) == FP_NORMAL);
+    assert(std::fpclassify(max) == FP_NORMAL);
+    assert(std::fpclassify(min) == FP_NORMAL);
 
-    assert(std::fpclassify(std::numeric_limits<T>::denorm_min()) == FP_SUBNORMAL);
+    assert(std::fpclassify(d_min) == FP_SUBNORMAL);
 
     assert(std::fpclassify(T(0)) == FP_ZERO);
     assert(std::fpclassify(T(-0.0)) == FP_ZERO);
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
 struct TestInt {
   template <class T>
-  static TEST_CONSTEXPR_CXX23 bool test() {
+  TEST_CONSTEXPR_CXX23 void operator()() const {
+    using lim                  = std::numeric_limits<T>;
+    TEST_CONSTEXPR_CXX23 T max = lim::max();
+    TEST_CONSTEXPR_CXX23 T low = lim::lowest();
+
     assert(std::fpclassify(T(0)) == FP_ZERO);
     assert(std::fpclassify(T(1)) == FP_NORMAL);
-    assert(std::fpclassify(std::numeric_limits<T>::max()) == FP_NORMAL);
+    assert(std::fpclassify(max) == FP_NORMAL);
 
     if (std::is_signed<T>::value) {
-      assert(std::fpclassify(std::numeric_limits<T>::lowest()) == FP_NORMAL);
+      assert(std::fpclassify(low) == FP_NORMAL);
       assert(std::fpclassify(T(-1)) == FP_NORMAL);
     }
-
-    return true;
-  }
-
-  template <class T>
-  TEST_CONSTEXPR_CXX23 void operator()() {
-    test<T>();
-#if TEST_STD_VER >= 23
-    static_assert(test<T>());
-#endif
   }
 };
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX23 bool test() {
   types::for_each(types::floating_point_types(), TestFloat());
   types::for_each(types::integral_types(), TestInt());
 
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 23
+  static_assert(test());
+#endif
+
   return 0;
 }

>From 5ff2b23ff45081ec8cdef90f220fbca3cdba9c90 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 24 Jul 2026 15:17:12 +0200
Subject: [PATCH 6/7] remove: _LIBCPP_PREFERRED_OVERLOAD for int overload

---
 libcxx/include/math.h | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/libcxx/include/math.h b/libcxx/include/math.h
index fa8d293604c7f..9b14e57d7b262 100644
--- a/libcxx/include/math.h
+++ b/libcxx/include/math.h
@@ -402,13 +402,6 @@ fpclassify(double __x) _NOEXCEPT {
 fpclassify(long double __x) _NOEXCEPT {
   return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, __x);
 }
-
-template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
-[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD int
-fpclassify(_A1 __x) _NOEXCEPT {
-  return __x == 0 ? FP_ZERO : FP_NORMAL;
-}
-
 #      else
 
 // template on non-double overloads to make them weaker than same overloads from MSVC runtime
@@ -431,7 +424,6 @@ template <class _A1, std::__enable_if_t<std::is_integral<_A1>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int fpclassify(_A1 __x) _NOEXCEPT {
   return __x == 0 ? FP_ZERO : FP_NORMAL;
 }
-
 #      endif
 
 } // namespace __math

>From 742a100c4432c14549ef9e0cc6dd1dce6b40e316 Mon Sep 17 00:00:00 2001
From: Lucas Mellone <github.snugness349 at passinbox.com>
Date: Fri, 24 Jul 2026 15:18:06 +0200
Subject: [PATCH 7/7] remove trailing line

---
 libcxx/test/std/numerics/c.math/fpclassify.pass.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
index fcfd73d9bacf2..10b2297fce105 100644
--- a/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/fpclassify.pass.cpp
@@ -75,6 +75,5 @@ int main(int, char**) {
 #if TEST_STD_VER >= 23
   static_assert(test());
 #endif
-
   return 0;
 }


From libcxx-commits at lists.llvm.org  Fri Jul 24 06:27:30 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 24 Jul 2026 06:27:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize filebuf::seekoff (PR
	#211788)
Message-ID: <llvm.org/llvm/llvm-project/pull/211788@github.com>


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/211788

None

>From 4f9e1ea36203a96afaff87a0f962f5116f726b73 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 24 Jul 2026 14:21:07 +0200
Subject: [PATCH] [libc++] Optimize filebuf::seekoff

---
 libcxx/include/fstream                        |  57 +++-
 .../test/benchmarks/streams/fstream.bench.cpp |  58 +++-
 .../fstreams/filebuf.virtuals/seekoff.dat     |   1 +
 .../filebuf.virtuals/seekoff.pass.cpp         | 320 +++++++++++++++---
 4 files changed, 376 insertions(+), 60 deletions(-)
 create mode 100644 libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.dat

diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 4b276251aa917..099c025e16e18 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -387,7 +387,7 @@ private:
   void __write_mode();
 
   _LIBCPP_HIDE_FROM_ABI static int __fseek(FILE* __file, pos_type __offset, int __whence);
-  _LIBCPP_HIDE_FROM_ABI static pos_type __ftell(FILE* __file);
+  _LIBCPP_HIDE_FROM_ABI static off_type __ftell(FILE* __file);
 
   _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&);
 
@@ -995,24 +995,53 @@ basic_filebuf<_CharT, _Traits>::seekoff(off_type __off, ios_base::seekdir __way,
     std::__throw_bad_cast();
 
   int __width = __cv_->encoding();
-  if (__file_ == nullptr || (__width <= 0 && __off != 0) || sync())
+  if (__file_ == nullptr || (__width <= 0 && __off != 0))
     return pos_type(off_type(-1));
-  // __width > 0 || __off == 0
+
   int __whence;
   switch (__way) {
-  case ios_base::beg:
-    __whence = SEEK_SET;
-    break;
-  case ios_base::cur:
+  case ios::beg: {
+    if (__off < 0)
+      return pos_type(off_type(-1));
+
+    pos_type __success_pos = __off;
+    __success_pos.state(__st_);
+
+    if (__cm_ & ios::in) {
+      off_type __file_pos    = __ftell(__file_);
+      auto __buffer_base_pos = __file_pos - (this->egptr() - this->eback());
+
+      // If the new position is within the buffer we can just move the current read pointer
+      if (__off >= __buffer_base_pos && __off <= __file_pos) {
+        this->setg(this->eback(), this->eback() + (__off - __buffer_base_pos), this->egptr());
+        return __success_pos;
+      }
+    }
+
+    if (sync() || __fseek(__file_, __width <= 0 ? 0 : __width * __off, SEEK_SET))
+      return pos_type(off_type(-1));
+    return __success_pos;
+  }
+  case ios::cur: {
+
+    // If the new position is within the buffer we can just move the current read pointer
+    if ((__cm_ & ios::in) &&
+        (__off < 0 ? this->gptr() - this->eback() <= __off : this->egptr() - this->gptr() >= __off)) {
+      this->setg(this->eback(), this->gptr() + __off, this->egptr());
+
+      pos_type __ret = __ftell(__file_) - (this->egptr() - this->gptr());
+      __ret.state(__st_);
+      return __ret;
+    }
+
     __whence = SEEK_CUR;
-    break;
-  case ios_base::end:
+  } break;
+  case ios::end: {
     __whence = SEEK_END;
-    break;
-  default:
-    return pos_type(off_type(-1));
+  } break;
   }
-  if (__fseek(__file_, __width > 0 ? __width * __off : 0, __whence))
+
+  if (sync() || __fseek(__file_, __width > 0 ? __width * __off : 0, __whence))
     return pos_type(off_type(-1));
   pos_type __r = __ftell(__file_);
   __r.state(__st_);
@@ -1031,7 +1060,7 @@ int basic_filebuf<_CharT, _Traits>::__fseek(FILE* __file, pos_type __offset, int
 }
 
 template <class _CharT, class _Traits>
-typename basic_filebuf<_CharT, _Traits>::pos_type basic_filebuf<_CharT, _Traits>::__ftell(FILE* __file) {
+typename basic_filebuf<_CharT, _Traits>::off_type basic_filebuf<_CharT, _Traits>::__ftell(FILE* __file) {
 #    if defined(_LIBCPP_MSVCRT_LIKE)
   return _ftelli64(__file);
 #    elif _LIBCPP_LIBC_NEWLIB
diff --git a/libcxx/test/benchmarks/streams/fstream.bench.cpp b/libcxx/test/benchmarks/streams/fstream.bench.cpp
index 2e16ae58749e6..5b23f16614f49 100644
--- a/libcxx/test/benchmarks/streams/fstream.bench.cpp
+++ b/libcxx/test/benchmarks/streams/fstream.bench.cpp
@@ -28,8 +28,10 @@ static void bm_ifstream_read(benchmark::State& state) {
   std::vector<char> buffer;
   buffer.resize(16384);
 
-  std::ofstream gen_testfile("testfile");
-  gen_testfile.write(buffer.data(), buffer.size());
+  {
+    std::ofstream gen_testfile("testfile");
+    gen_testfile.write(buffer.data(), buffer.size());
+  }
 
   std::ifstream stream("testfile");
   assert(stream);
@@ -42,4 +44,56 @@ static void bm_ifstream_read(benchmark::State& state) {
 }
 BENCHMARK(bm_ifstream_read)->Name("std::ifstream::read(char*, size)");
 
+void run_sizes(benchmark::Benchmark* benchmark) { benchmark->Arg(0)->Arg(100)->Arg(4000)->Arg(10000); }
+
+template <bool prime>
+static void bm_seekoff_cur(benchmark::State& state) {
+  std::vector<char> buffer;
+  buffer.resize(16384);
+
+  {
+    std::ofstream gen_testfile("testfile");
+    gen_testfile.write(buffer.data(), buffer.size());
+  }
+
+  std::ifstream stream("testfile");
+  assert(stream);
+
+  auto val = state.range();
+
+  for (auto _ : state) {
+    if constexpr (prime)
+      benchmark::DoNotOptimize(stream.rdbuf()->sgetc());
+    benchmark::DoNotOptimize(stream.seekg(val, std::ios::cur));
+    val = -val;
+  }
+}
+BENCHMARK(bm_seekoff_cur<true>)->Name("std::ifstream::seekg(N, std::ios::cur) (primed buffer)")->Apply(run_sizes);
+BENCHMARK(bm_seekoff_cur<false>)->Name("std::ifstream::seekg(N, std::ios::cur) (unprimed buffer)")->Apply(run_sizes);
+
+template <bool prime>
+static void bm_seekoff_beg(benchmark::State& state) {
+  std::vector<char> buffer;
+  buffer.resize(16384);
+
+  {
+    std::ofstream gen_testfile("testfile");
+    gen_testfile.write(buffer.data(), buffer.size());
+  }
+
+  std::ifstream stream("testfile");
+  assert(stream);
+
+  auto val = state.range();
+
+  for (auto _ : state) {
+    if constexpr (prime)
+      benchmark::DoNotOptimize(stream.rdbuf()->sgetc());
+    benchmark::DoNotOptimize(stream.seekg(val, std::ios::beg));
+    benchmark::DoNotOptimize(stream.seekg(0, std::ios::beg));
+  }
+}
+BENCHMARK(bm_seekoff_beg<true>)->Name("std::ifstream::seekg(N, std::ios::beg) (primed buffer)")->Apply(run_sizes);
+BENCHMARK(bm_seekoff_beg<false>)->Name("std::ifstream::seekg(N, std::ios::beg) (unprimed buffer)")->Apply(run_sizes);
+
 BENCHMARK_MAIN();
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.dat b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.dat
new file mode 100644
index 0000000000000..6a537b5b36788
--- /dev/null
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.dat
@@ -0,0 +1 @@
+1234567890
\ No newline at end of file
diff --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.pass.cpp
index f6378e7998ee9..908670c753fbd 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.pass.cpp
@@ -6,6 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+// FILE_DEPENDENCIES: seekoff.dat
+
 // <fstream>
 
 // pos_type seekoff(off_type off, ios_base::seekdir way,
@@ -13,57 +15,287 @@
 // pos_type seekpos(pos_type sp,
 //                  ios_base::openmode which = ios_base::in | ios_base::out);
 
-#include <fstream>
 #include <cassert>
+#include <fstream>
 
+#include "make_string.h"
 #include "test_macros.h"
 
-int main(int, char**)
-{
-    {
-        char buf[10];
-        typedef std::filebuf::pos_type pos_type;
-        std::filebuf f;
-        f.pubsetbuf(buf, sizeof(buf));
-        assert(f.open("seekoff.dat", std::ios_base::in | std::ios_base::out
-                                                       | std::ios_base::trunc) != 0);
-        assert(f.is_open());
-        f.sputn("abcdefghijklmnopqrstuvwxyz", 26);
-        pos_type p = f.pubseekoff(-15, std::ios_base::cur);
-        assert(p == 11);
-        assert(f.sgetc() == 'l');
-        f.pubseekoff(0, std::ios_base::beg);
-        assert(f.sgetc() == 'a');
-        f.pubseekoff(-1, std::ios_base::end);
-        assert(f.sgetc() == 'z');
-        assert(f.pubseekpos(p) == p);
-        assert(f.sgetc() == 'l');
-    }
-    std::remove("seekoff.dat");
+template <class CharT>
+void test() {
+  using filebuf = std::basic_filebuf<CharT>;
+
+  { // seek with file closed
+    filebuf fb;
+    assert(fb.pubseekoff(0, std::ios::beg) == -1);
+  }
+
+  {   // seek stream from begin
+    { // unbuffered
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::beg) == -1);
+      assert(fb.pubseekoff(-2, std::ios::beg) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::beg) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::beg) == 0);
+      assert(fb.pubseekoff(1, std::ios::beg) == 1);
+      assert(fb.pubseekoff(10, std::ios::beg) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // buffered
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      assert(fb.sgetc() == '1');
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::beg) == -1);
+      assert(fb.pubseekoff(-2, std::ios::beg) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::beg) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::beg) == 0);
+      assert(fb.pubseekoff(1, std::ios::beg) == 1);
+      assert(fb.pubseekoff(10, std::ios::beg) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // buffered, tiny buffer
+      filebuf fb;
+      CharT buffer[5];
+      fb.pubsetbuf(buffer, 5);
+      assert(fb.open("seekoff.dat", std::ios::in));
+      assert(fb.sgetc() == '1');
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::beg) == -1);
+      assert(fb.pubseekoff(-2, std::ios::beg) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::beg) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::beg) == 0);
+      assert(fb.pubseekoff(1, std::ios::beg) == 1);
+      assert(fb.pubseekoff(10, std::ios::beg) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // input/output stream, neither read from nor written to
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out));
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::beg) == -1);
+      assert(fb.pubseekoff(-2, std::ios::beg) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::beg) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::beg) == 0);
+      assert(fb.pubseekoff(1, std::ios::beg) == 1);
+      assert(fb.pubseekoff(10, std::ios::beg) == 10);
+    }
+    { // input/output stream, written to
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out | std::ios::trunc));
+      fb.sputn(MAKE_CSTRING(CharT, "1234567890"), 10);
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::beg) == -1);
+      assert(fb.pubseekoff(-2, std::ios::beg) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::beg) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::beg) == 0);
+      assert(fb.pubseekoff(1, std::ios::beg) == 1);
+      assert(fb.pubseekoff(10, std::ios::beg) == 10);
+    }
+    { // input/output stream, read from
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out));
+
+      assert(fb.pubseekoff(0, std::ios::beg) == 0); // Go to the start, so we can read something
+
+      CharT buffer[11];
+      buffer[10] = '\0';
+      assert(fb.sgetn(buffer, 10) == 10);
+      assert(buffer == MAKE_STRING(CharT, "1234567890"));
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::beg) == -1);
+      assert(fb.pubseekoff(-2, std::ios::beg) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::beg) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::beg) == 0);
+      assert(fb.pubseekoff(1, std::ios::beg) == 1);
+      assert(fb.pubseekoff(10, std::ios::beg) == 10);
+    }
+  }
+  {   // seek stream from current position
+    { // unbuffered
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      // negative offsets, from begin
+      assert(fb.pubseekoff(-1, std::ios::cur) == -1);
+      assert(fb.pubseekoff(-2, std::ios::cur) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::cur) == -1);
+
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::cur) == 0);
+      assert(fb.pubseekoff(1, std::ios::cur) == 1);
+
+      // negative offset, from 1 character into the stream
+      assert(fb.pubseekoff(-1, std::ios::cur) == 0);
+
+      assert(fb.pubseekoff(10, std::ios::cur) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // buffered
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      assert(fb.sgetc() == '1');
+
+      // negative offsets, from begin
+      assert(fb.pubseekoff(-1, std::ios::cur) == -1);
+      assert(fb.pubseekoff(-2, std::ios::cur) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::cur) == -1);
+
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::cur) == 0);
+      assert(fb.pubseekoff(1, std::ios::cur) == 1);
+
+      // negative offset, from 1 character into the stream
+      assert(fb.pubseekoff(-1, std::ios::cur) == 0);
+
+      assert(fb.pubseekoff(10, std::ios::cur) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // buffered, tiny buffer
+      filebuf fb;
+      CharT buffer[5];
+      fb.pubsetbuf(buffer, 5);
+      assert(fb.open("seekoff.dat", std::ios::in));
+      assert(fb.sgetc() == '1');
+
+      // negative offsets, from begin
+      assert(fb.pubseekoff(-1, std::ios::cur) == -1);
+      assert(fb.pubseekoff(-2, std::ios::cur) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::cur) == -1);
+
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::cur) == 0);
+      assert(fb.pubseekoff(1, std::ios::cur) == 1);
+
+      // negative offset, from 1 character into the stream
+      assert(fb.pubseekoff(-1, std::ios::cur) == 0);
 
+      assert(fb.pubseekoff(10, std::ios::cur) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // input/output stream, neither read from nor written to
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out));
+
+      // negative offsets
+      assert(fb.pubseekoff(-1, std::ios::cur) == -1);
+      assert(fb.pubseekoff(-2, std::ios::cur) == -1);
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::cur) == -1);
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::cur) == 0);
+      assert(fb.pubseekoff(1, std::ios::cur) == 1);
+
+      // negative offset, from 1 character into the stream
+      assert(fb.pubseekoff(-1, std::ios::cur) == 0);
+      assert(fb.pubseekoff(10, std::ios::cur) == 10);
+    }
+    { // input/output stream, written to
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out | std::ios::trunc));
+      fb.sputn(MAKE_CSTRING(CharT, "1234567890"), 10);
+
+      // negative offsets; we've written and the cursor is at the end of the file, so these succeed
+      assert(fb.pubseekoff(-1, std::ios::cur) == 9);
+      assert(fb.pubseekoff(-2, std::ios::cur) == 7);
+
+      // except this one, we didn't write this much
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::cur) == -1);
+
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::cur) == 7);
+      assert(fb.pubseekoff(1, std::ios::cur) == 8);
+      assert(fb.pubseekoff(10, std::ios::cur) == 18);
+    }
+    { // input/output stream, read from
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out));
+
+      assert(fb.pubseekoff(0, std::ios::beg) == 0); // Go to the start, so we can read something
+
+      CharT buffer[11];
+      buffer[10] = '\0';
+      assert(fb.sgetn(buffer, 10) == 10);
+      assert(buffer == MAKE_STRING(CharT, "1234567890"));
+
+      // negative offsets; we've read and the cursor is at the end of the file, so these succeed
+      assert(fb.pubseekoff(-1, std::ios::cur) == 9);
+      assert(fb.pubseekoff(-2, std::ios::cur) == 7);
+
+      // except this one, we didn't read this much
+      assert(fb.pubseekoff(std::numeric_limits<typename filebuf::off_type>::min(), std::ios::cur) == -1);
+
+      // zero offset
+      assert(fb.pubseekoff(0, std::ios::cur) == 7);
+      assert(fb.pubseekoff(1, std::ios::cur) == 8);
+      assert(fb.pubseekoff(10, std::ios::cur) == 18);
+    }
+  }
+  {   // seek stream end
+    { // unbuffered
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      assert(fb.pubseekoff(0, std::ios::end) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // buffered
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      assert(fb.sgetc() == '1');
+      assert(fb.pubseekoff(0, std::ios::end) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // buffered, tiny buffer
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in));
+      CharT buffer[5];
+      fb.pubsetbuf(buffer, 5);
+      assert(fb.sgetc() == '1');
+      assert(fb.pubseekoff(0, std::ios::end) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // input/output stream, neither read from nor written to
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out));
+      assert(fb.pubseekoff(0, std::ios::end) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // input/output stream, written to
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out | std::ios::trunc));
+      fb.sputn(MAKE_CSTRING(CharT, "1234567890"), 10);
+      assert(fb.pubseekoff(0, std::ios::end) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+    { // input/output stream, read from
+      filebuf fb;
+      assert(fb.open("seekoff.dat", std::ios::in | std::ios::out));
+
+      assert(fb.pubseekoff(0, std::ios::beg) == 0); // Go to the start, so we can read something
+
+      CharT buffer[11];
+      buffer[10] = '\0';
+      assert(fb.sgetn(buffer, 10) == 10);
+      assert(buffer == MAKE_STRING(CharT, "1234567890"));
+      assert(fb.pubseekoff(0, std::ios::end) == 10);
+      assert(fb.sgetc() == std::char_traits<CharT>::eof());
+    }
+  }
+}
+
+int main(int, char**) {
+  test<char>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-    {
-        wchar_t buf[10];
-        typedef std::filebuf::pos_type pos_type;
-        std::wfilebuf f;
-        f.pubsetbuf(buf, sizeof(buf)/sizeof(buf[0]));
-        assert(f.open("seekoff.dat", std::ios_base::in | std::ios_base::out
-                                                       | std::ios_base::trunc) != 0);
-        assert(f.is_open());
-        f.sputn(L"abcdefghijklmnopqrstuvwxyz", 26);
-        LIBCPP_ASSERT(buf[0] == L'v');
-        pos_type p = f.pubseekoff(-15, std::ios_base::cur);
-        assert(p == 11);
-        assert(f.sgetc() == L'l');
-        f.pubseekoff(0, std::ios_base::beg);
-        assert(f.sgetc() == L'a');
-        f.pubseekoff(-1, std::ios_base::end);
-        assert(f.sgetc() == L'z');
-        assert(f.pubseekpos(p) == p);
-        assert(f.sgetc() == L'l');
-    }
-    std::remove("seekoff.dat");
+  test<wchar_t>();
 #endif
 
+  // TODO: test with different codecvt facets (e.g. where always_noconv() is false, encoding() == 0/-1)
+
   return 0;
 }


From libcxx-commits at lists.llvm.org  Fri Jul 24 06:29:10 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 24 Jul 2026 06:29:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize filebuf::seekoff (PR
	#211788)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211788@github.com>
Message-ID: <6a6368a6.6c8668f2.1842d3.0310@mx.google.com>


https://github.com/philnik777 edited https://github.com/llvm/llvm-project/pull/211788

From libcxx-commits at lists.llvm.org  Fri Jul 24 06:29:28 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 06:29:28 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize filebuf::seekoff (PR
	#211788)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211788@github.com>
Message-ID: <6a6368b8.492e23e4.3ce411.400b@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions ,cpp -- libcxx/include/fstream libcxx/test/benchmarks/streams/fstream.bench.cpp libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.pass.cpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/fstream b/libcxx/include/fstream
index 099c025e1..4a1479624 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -1023,7 +1023,6 @@ basic_filebuf<_CharT, _Traits>::seekoff(off_type __off, ios_base::seekdir __way,
     return __success_pos;
   }
   case ios::cur: {
-
     // If the new position is within the buffer we can just move the current read pointer
     if ((__cm_ & ios::in) &&
         (__off < 0 ? this->gptr() - this->eback() <= __off : this->egptr() - this->gptr() >= __off)) {

``````````

</details>


https://github.com/llvm/llvm-project/pull/211788

From libcxx-commits at lists.llvm.org  Fri Jul 24 06:29:36 2026
From: libcxx-commits at lists.llvm.org (Nikolas Klauser via libcxx-commits)
Date: Fri, 24 Jul 2026 06:29:36 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Optimize filebuf::seekoff (PR
	#211788)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211788@github.com>
Message-ID: <6a6368c0.f3151ff8.f584a.ebf6@mx.google.com>


philnik777 wrote:

/libcxx-bot benchmark libcxx/test/benchmarks/streams/fstream.bench.cpp

https://github.com/llvm/llvm-project/pull/211788

From libcxx-commits at lists.llvm.org  Fri Jul 24 06:46:55 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 06:46:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][WIP] use linear search instead
 of binary search for small flat_map (PR #211794)
Message-ID: <llvm.org/llvm/llvm-project/pull/211794@github.com>


https://github.com/huixie90 created https://github.com/llvm/llvm-project/pull/211794

None

>From c0af2a86aaf5a970f1fb1f3911f500d67125244f Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 24 Jul 2026 14:46:31 +0100
Subject: [PATCH] [libc++][WIP] use linear search instead of binary search for
 small flat_map

---
 libcxx/include/__flat_map/flat_map.h                 |  8 ++++++++
 .../associative/associative_container_benchmarks.h   | 12 ++++++++++--
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__flat_map/flat_map.h b/libcxx/include/__flat_map/flat_map.h
index 50487cada24c0..8b15491a60dfc 100644
--- a/libcxx/include/__flat_map/flat_map.h
+++ b/libcxx/include/__flat_map/flat_map.h
@@ -15,6 +15,7 @@
 #include <__algorithm/min.h>
 #include <__algorithm/ranges_adjacent_find.h>
 #include <__algorithm/ranges_equal.h>
+#include <__algorithm/ranges_find.h>
 #include <__algorithm/ranges_inplace_merge.h>
 #include <__algorithm/ranges_sort.h>
 #include <__algorithm/ranges_unique.h>
@@ -64,6 +65,7 @@
 #include <__vector/vector.h>
 #include <initializer_list>
 #include <stdexcept>
+#include <type_traits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -989,6 +991,12 @@ class flat_map {
 
   template <class _Self, class _Kp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static auto __find_impl(_Self&& __self, const _Kp& __key) {
+    auto __key_iter = std::ranges::find(__self.__containers_.keys, __key);
+    auto __mapped_iter = __corresponding_mapped_it(__self, __key_iter);
+
+    using Res = std::conditional_t<std::is_const_v<std::remove_reference_t<_Self>>, const_iterator, iterator>;
+    return Res(__key_iter, __mapped_iter);
+
     auto __it   = __self.lower_bound(__key);
     auto __last = __self.end();
     if (__it == __last || __self.__compare_(__key, __it->first)) {
diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
index a55f187d2ac80..93d06a020f759 100644
--- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -66,7 +66,7 @@ void associative_container_benchmarks(std::string container) {
     benchmark::RegisterBenchmark(container + "::" + operation, f)->Arg(0)->Arg(32)->Arg(1024)->Arg(8192);
   };
   auto bench_non_empty = [&](std::string operation, auto f) {
-    benchmark::RegisterBenchmark(container + "::" + operation, f)->Arg(32)->Arg(1024)->Arg(8192);
+    benchmark::RegisterBenchmark(container + "::" + operation, f)->Arg(1)->Arg(4)->Arg(8)->Arg(16)->Arg(32)->Arg(1024)->Arg(8192);
   };
 
   static constexpr bool is_multi_key_container =
@@ -692,9 +692,17 @@ void associative_container_benchmarks(std::string container) {
       const std::size_t size = st.range(0);
       std::vector<Value> in  = make_value_types(generate_unique_keys(size));
       Container c(in.begin(), in.end());
+      std::vector<Key> keys;
+      for(size_t i = 0; i <in.size(); ++i) {
+        keys.push_back(get_key(in[getRandomEngine()() % in.size()]));
+      }
+      size_t i = 0;
 
       for (auto _ : st) {
-        auto result = func(c, get_key(in[getRandomEngine()() % in.size()]));
+      //st.PauseTiming();
+      const auto& key =  keys[++i%keys.size()];
+      //st.ResumeTiming();
+        auto result = func(c, key);
         benchmark::DoNotOptimize(c);
         benchmark::DoNotOptimize(result);
         benchmark::ClobberMemory();


From libcxx-commits at lists.llvm.org  Fri Jul 24 06:46:55 2026
From: libcxx-commits at lists.llvm.org (Lucas Mellone via libcxx-commits)
Date: Fri, 24 Jul 2026 06:46:55 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a636ccf.3eaf104f.3bb543.76d6@mx.google.com>


lknknm wrote:

@frederick-vs-ja I'm not sure specifically why the tests are currently failing.

https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Fri Jul 24 06:48:50 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 06:48:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][WIP] use linear search instead
 of binary search for small flat_map (PR #211794)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211794@github.com>
Message-ID: <6a636d42.9a9f8321.a4998.e914@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions h -- libcxx/include/__flat_map/flat_map.h libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/include/__flat_map/flat_map.h b/libcxx/include/__flat_map/flat_map.h
index 8b15491a6..0e3f581b8 100644
--- a/libcxx/include/__flat_map/flat_map.h
+++ b/libcxx/include/__flat_map/flat_map.h
@@ -991,7 +991,7 @@ private:
 
   template <class _Self, class _Kp>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static auto __find_impl(_Self&& __self, const _Kp& __key) {
-    auto __key_iter = std::ranges::find(__self.__containers_.keys, __key);
+    auto __key_iter    = std::ranges::find(__self.__containers_.keys, __key);
     auto __mapped_iter = __corresponding_mapped_it(__self, __key_iter);
 
     using Res = std::conditional_t<std::is_const_v<std::remove_reference_t<_Self>>, const_iterator, iterator>;
diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
index 93d06a020..411b24fbc 100644
--- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -66,7 +66,14 @@ void associative_container_benchmarks(std::string container) {
     benchmark::RegisterBenchmark(container + "::" + operation, f)->Arg(0)->Arg(32)->Arg(1024)->Arg(8192);
   };
   auto bench_non_empty = [&](std::string operation, auto f) {
-    benchmark::RegisterBenchmark(container + "::" + operation, f)->Arg(1)->Arg(4)->Arg(8)->Arg(16)->Arg(32)->Arg(1024)->Arg(8192);
+    benchmark::RegisterBenchmark(container + "::" + operation, f)
+        ->Arg(1)
+        ->Arg(4)
+        ->Arg(8)
+        ->Arg(16)
+        ->Arg(32)
+        ->Arg(1024)
+        ->Arg(8192);
   };
 
   static constexpr bool is_multi_key_container =
@@ -693,15 +700,15 @@ void associative_container_benchmarks(std::string container) {
       std::vector<Value> in  = make_value_types(generate_unique_keys(size));
       Container c(in.begin(), in.end());
       std::vector<Key> keys;
-      for(size_t i = 0; i <in.size(); ++i) {
+      for (size_t i = 0; i < in.size(); ++i) {
         keys.push_back(get_key(in[getRandomEngine()() % in.size()]));
       }
       size_t i = 0;
 
       for (auto _ : st) {
-      //st.PauseTiming();
-      const auto& key =  keys[++i%keys.size()];
-      //st.ResumeTiming();
+        //st.PauseTiming();
+        const auto& key = keys[++i % keys.size()];
+        //st.ResumeTiming();
         auto result = func(c, key);
         benchmark::DoNotOptimize(c);
         benchmark::DoNotOptimize(result);

``````````

</details>


https://github.com/llvm/llvm-project/pull/211794

From libcxx-commits at lists.llvm.org  Fri Jul 24 06:50:05 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 06:50:05 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][WIP] use linear search instead
 of binary search for small flat_map (PR #211794)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211794@github.com>
Message-ID: <6a636d8d.668c49bf.60596.9139@mx.google.com>


https://github.com/huixie90 edited https://github.com/llvm/llvm-project/pull/211794

From libcxx-commits at lists.llvm.org  Fri Jul 24 06:57:30 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 06:57:30 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][WIP] use linear search instead
 of binary search for small flat_map (PR #211794)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211794@github.com>
Message-ID: <6a636f4a.8f28c68b.fea2a.daaa@mx.google.com>


https://github.com/huixie90 edited https://github.com/llvm/llvm-project/pull/211794

From libcxx-commits at lists.llvm.org  Fri Jul 24 07:00:41 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 07:00:41 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][WIP] use linear search instead
 of binary search for small flat_map (PR #211794)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211794@github.com>
Message-ID: <6a637009.b47a2ac0.37bd76.c4b3@mx.google.com>


https://github.com/huixie90 edited https://github.com/llvm/llvm-project/pull/211794

From libcxx-commits at lists.llvm.org  Fri Jul 24 07:09:45 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 07:09:45 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][WIP] use linear search instead
 of binary search for small flat_map (PR #211794)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211794@github.com>
Message-ID: <6a637229.866f9993.2f0f1.dd17@mx.google.com>


https://github.com/huixie90 closed https://github.com/llvm/llvm-project/pull/211794

From libcxx-commits at lists.llvm.org  Fri Jul 24 07:30:19 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 07:30:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a6376fb.6c5720b3.2e9b28.c28a@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/209686

>From bc598c40cb7bac1531c0a3c1b32c0c8536ac6cd1 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 12:59:35 +0800
Subject: [PATCH 1/3] [libc++] Implement LWG 4274

Allow chrono::hh_mm_ss to be constructed from durations with unsigned representations.
---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__chrono/hh_mm_ss.h            | 20 ++++--
 .../time.hms/time.hms.members/ctor.pass.cpp   | 68 +++++++++++++++++++
 3 files changed, 83 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c203fa424556a..e37c05f035526 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -168,7 +168,7 @@
 "`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","|Complete|","23","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
 "`LWG4269 <https://wg21.link/LWG4269>`__","``unique_copy`` passes arguments to its predicate backwards","2025-11 (Kona)","","","`#171326 <https://github.com/llvm/llvm-project/issues/171326>`__",""
 "`LWG4272 <https://wg21.link/LWG4272>`__","For ``rank == 0``, ``layout_stride`` is atypically convertible","2025-11 (Kona)","","","`#171327 <https://github.com/llvm/llvm-project/issues/171327>`__",""
-"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","","","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
+"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","|Complete|","24","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
 "`LWG4275 <https://wg21.link/LWG4275>`__","``std::dynamic_extent`` should also be defined in ``<mdspan>``","2025-11 (Kona)","|Complete|","21","`#171329 <https://github.com/llvm/llvm-project/issues/171329>`__",""
 "`LWG4276 <https://wg21.link/LWG4276>`__","``front()`` and ``back()`` are not hardened for zero-length ``std::array``\s","2025-11 (Kona)","|Complete|","17","`#171330 <https://github.com/llvm/llvm-project/issues/171330>`__",""
 "`LWG4280 <https://wg21.link/LWG4280>`__","``simd::partial_load`` uses undefined identifier ``T``","2025-11 (Kona)","","","`#171331 <https://github.com/llvm/llvm-project/issues/171331>`__",""
diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index 746eba903d4f8..ddb2d055c16ca 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -11,9 +11,9 @@
 #define _LIBCPP___CHRONO_HH_MM_SS_H
 
 #include <__chrono/duration.h>
-#include <__chrono/time_point.h>
 #include <__config>
 #include <__type_traits/common_type.h>
+#include <__type_traits/is_unsigned.h>
 #include <ratio>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -45,6 +45,14 @@ class hh_mm_ss {
     return 0;
   }
 
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+      return __d;
+    } else {
+      return __is_neg ? -__d : __d;
+    }
+  }
+
 public:
   _LIBCPP_HIDE_FROM_ABI static unsigned constexpr fractional_width =
       __width(__CommonType::period::den) < 19 ? __width(__CommonType::period::den) : 6u;
@@ -53,11 +61,11 @@ class hh_mm_ss {
   _LIBCPP_HIDE_FROM_ABI constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit hh_mm_ss(_Duration __d) noexcept
-      : __is_neg_(__d < _Duration(0)),
-        __h_(chrono::duration_cast<chrono::hours>(chrono::abs(__d))),
-        __m_(chrono::duration_cast<chrono::minutes>(chrono::abs(__d) - hours())),
-        __s_(chrono::duration_cast<chrono::seconds>(chrono::abs(__d) - hours() - minutes())),
-        __f_(chrono::duration_cast<precision>(chrono::abs(__d) - hours() - minutes() - seconds())) {}
+      : __is_neg_(__d < _Duration::zero()),
+        __h_(chrono::duration_cast<chrono::hours>(__abs_d(__d, __is_neg_))),
+        __m_(chrono::duration_cast<chrono::minutes>(__abs_d(__d, __is_neg_) - hours())),
+        __s_(chrono::duration_cast<chrono::seconds>(__abs_d(__d, __is_neg_) - hours() - minutes())),
+        __f_(chrono::duration_cast<precision>(__abs_d(__d, __is_neg_) - hours() - minutes() - seconds())) {}
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool is_negative() const noexcept { return __is_neg_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr chrono::hours hours() const noexcept { return __h_; }
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
new file mode 100644
index 0000000000000..1d3a946ed8873
--- /dev/null
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
+
+int main(int, char**) {
+  {
+    // Tests construction from a duration with an unsigned representation.
+    using Duration = std::chrono::duration<unsigned, std::milli>;
+
+    // 1 hour + 1 minute + 1 second + 1 millisecond
+    constexpr Duration d{3'661'001};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(!hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
+    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
+  }
+
+  {
+    // Tests construction from a negative duration with a custom representation.
+    using Duration = std::chrono::duration<SignedRep>;
+
+    // 1 hour + 1 minute + 1 second
+    constexpr Duration d{SignedRep{-3'661}};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::seconds{0});
+    static_assert(hms.to_duration() == std::chrono::seconds{-3'661});
+  }
+
+  return 0;
+}

>From 5504e7564dcfc299442cbf1d054a2be329c3e512 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 14:19:47 +0800
Subject: [PATCH 2/3] fix missing header file

---
 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
index 1d3a946ed8873..b33917d262ba8 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -19,6 +19,7 @@
 // LWG4274: The hh_mm_ss constructor supports unsigned durations.
 
 #include <chrono>
+#include <ratio>
 
 // A signed arithmetic-like type used as a custom duration representation.
 struct SignedRep {

>From 6af50c255abb12100c2a764063b1fcf9ebb966ec Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Fri, 24 Jul 2026 22:30:10 +0800
Subject: [PATCH 3/3] Update libcxx/include/__chrono/hh_mm_ss.h

Co-authored-by: A. Jiang <de34 at live.cn>
---
 libcxx/include/__chrono/hh_mm_ss.h | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index ddb2d055c16ca..2e77304c68a53 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -46,11 +46,10 @@ class hh_mm_ss {
   }
 
   _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
-    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>)
       return __d;
-    } else {
+    else
       return __is_neg ? -__d : __d;
-    }
   }
 
 public:


From libcxx-commits at lists.llvm.org  Fri Jul 24 07:30:44 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 07:30:44 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a637714.83ce629a.320329.0f1f@mx.google.com>


================
@@ -45,6 +45,14 @@ class hh_mm_ss {
     return 0;
   }
 
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+      return __d;
+    } else {
+      return __is_neg ? -__d : __d;
+    }
----------------
emmett2020 wrote:

Thanks, I fixed the format style.

https://github.com/llvm/llvm-project/pull/209686

From libcxx-commits at lists.llvm.org  Fri Jul 24 08:03:21 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 08:03:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a637eb9.492e23e4.3ce411.381d@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/209686

>From bc598c40cb7bac1531c0a3c1b32c0c8536ac6cd1 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 12:59:35 +0800
Subject: [PATCH 1/4] [libc++] Implement LWG 4274

Allow chrono::hh_mm_ss to be constructed from durations with unsigned representations.
---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__chrono/hh_mm_ss.h            | 20 ++++--
 .../time.hms/time.hms.members/ctor.pass.cpp   | 68 +++++++++++++++++++
 3 files changed, 83 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c203fa424556a..e37c05f035526 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -168,7 +168,7 @@
 "`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","|Complete|","23","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
 "`LWG4269 <https://wg21.link/LWG4269>`__","``unique_copy`` passes arguments to its predicate backwards","2025-11 (Kona)","","","`#171326 <https://github.com/llvm/llvm-project/issues/171326>`__",""
 "`LWG4272 <https://wg21.link/LWG4272>`__","For ``rank == 0``, ``layout_stride`` is atypically convertible","2025-11 (Kona)","","","`#171327 <https://github.com/llvm/llvm-project/issues/171327>`__",""
-"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","","","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
+"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","|Complete|","24","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
 "`LWG4275 <https://wg21.link/LWG4275>`__","``std::dynamic_extent`` should also be defined in ``<mdspan>``","2025-11 (Kona)","|Complete|","21","`#171329 <https://github.com/llvm/llvm-project/issues/171329>`__",""
 "`LWG4276 <https://wg21.link/LWG4276>`__","``front()`` and ``back()`` are not hardened for zero-length ``std::array``\s","2025-11 (Kona)","|Complete|","17","`#171330 <https://github.com/llvm/llvm-project/issues/171330>`__",""
 "`LWG4280 <https://wg21.link/LWG4280>`__","``simd::partial_load`` uses undefined identifier ``T``","2025-11 (Kona)","","","`#171331 <https://github.com/llvm/llvm-project/issues/171331>`__",""
diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index 746eba903d4f8..ddb2d055c16ca 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -11,9 +11,9 @@
 #define _LIBCPP___CHRONO_HH_MM_SS_H
 
 #include <__chrono/duration.h>
-#include <__chrono/time_point.h>
 #include <__config>
 #include <__type_traits/common_type.h>
+#include <__type_traits/is_unsigned.h>
 #include <ratio>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -45,6 +45,14 @@ class hh_mm_ss {
     return 0;
   }
 
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+      return __d;
+    } else {
+      return __is_neg ? -__d : __d;
+    }
+  }
+
 public:
   _LIBCPP_HIDE_FROM_ABI static unsigned constexpr fractional_width =
       __width(__CommonType::period::den) < 19 ? __width(__CommonType::period::den) : 6u;
@@ -53,11 +61,11 @@ class hh_mm_ss {
   _LIBCPP_HIDE_FROM_ABI constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit hh_mm_ss(_Duration __d) noexcept
-      : __is_neg_(__d < _Duration(0)),
-        __h_(chrono::duration_cast<chrono::hours>(chrono::abs(__d))),
-        __m_(chrono::duration_cast<chrono::minutes>(chrono::abs(__d) - hours())),
-        __s_(chrono::duration_cast<chrono::seconds>(chrono::abs(__d) - hours() - minutes())),
-        __f_(chrono::duration_cast<precision>(chrono::abs(__d) - hours() - minutes() - seconds())) {}
+      : __is_neg_(__d < _Duration::zero()),
+        __h_(chrono::duration_cast<chrono::hours>(__abs_d(__d, __is_neg_))),
+        __m_(chrono::duration_cast<chrono::minutes>(__abs_d(__d, __is_neg_) - hours())),
+        __s_(chrono::duration_cast<chrono::seconds>(__abs_d(__d, __is_neg_) - hours() - minutes())),
+        __f_(chrono::duration_cast<precision>(__abs_d(__d, __is_neg_) - hours() - minutes() - seconds())) {}
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool is_negative() const noexcept { return __is_neg_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr chrono::hours hours() const noexcept { return __h_; }
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
new file mode 100644
index 0000000000000..1d3a946ed8873
--- /dev/null
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
+
+int main(int, char**) {
+  {
+    // Tests construction from a duration with an unsigned representation.
+    using Duration = std::chrono::duration<unsigned, std::milli>;
+
+    // 1 hour + 1 minute + 1 second + 1 millisecond
+    constexpr Duration d{3'661'001};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(!hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
+    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
+  }
+
+  {
+    // Tests construction from a negative duration with a custom representation.
+    using Duration = std::chrono::duration<SignedRep>;
+
+    // 1 hour + 1 minute + 1 second
+    constexpr Duration d{SignedRep{-3'661}};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::seconds{0});
+    static_assert(hms.to_duration() == std::chrono::seconds{-3'661});
+  }
+
+  return 0;
+}

>From 5504e7564dcfc299442cbf1d054a2be329c3e512 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 14:19:47 +0800
Subject: [PATCH 2/4] fix missing header file

---
 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
index 1d3a946ed8873..b33917d262ba8 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -19,6 +19,7 @@
 // LWG4274: The hh_mm_ss constructor supports unsigned durations.
 
 #include <chrono>
+#include <ratio>
 
 // A signed arithmetic-like type used as a custom duration representation.
 struct SignedRep {

>From 6af50c255abb12100c2a764063b1fcf9ebb966ec Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Fri, 24 Jul 2026 22:30:10 +0800
Subject: [PATCH 3/4] Update libcxx/include/__chrono/hh_mm_ss.h

Co-authored-by: A. Jiang <de34 at live.cn>
---
 libcxx/include/__chrono/hh_mm_ss.h | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index ddb2d055c16ca..2e77304c68a53 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -46,11 +46,10 @@ class hh_mm_ss {
   }
 
   _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
-    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>)
       return __d;
-    } else {
+    else
       return __is_neg ? -__d : __d;
-    }
   }
 
 public:

>From ef96225c673afd250b5823a4910610a0a2770841 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Fri, 24 Jul 2026 23:03:01 +0800
Subject: [PATCH 4/4] fix reviewer comments

---
 .../time.hms/time.hms.members/ctor.pass.cpp   | 69 -------------------
 .../time.hms/time.hms.members/hours.pass.cpp  |  2 +
 .../time.hms.members/is_negative.pass.cpp     |  2 +
 .../time.hms.members/minutes.pass.cpp         |  2 +
 .../time.hms.members/seconds.pass.cpp         |  2 +
 .../time.hms.members/subseconds.pass.cpp      |  2 +
 .../time.hms.members/to_duration.pass.cpp     |  2 +
 7 files changed, 12 insertions(+), 69 deletions(-)
 delete mode 100644 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp

diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
deleted file mode 100644
index b33917d262ba8..0000000000000
--- a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// <chrono>
-//
-// template<class Duration>
-// class hh_mm_ss {
-// public:
-//   constexpr explicit hh_mm_ss(Duration d);
-// };
-//
-// LWG4274: The hh_mm_ss constructor supports unsigned durations.
-
-#include <chrono>
-#include <ratio>
-
-// A signed arithmetic-like type used as a custom duration representation.
-struct SignedRep {
-  long long value;
-
-  constexpr explicit SignedRep(long long v = 0) : value(v) {}
-  constexpr operator long long() const { return value; }
-
-  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
-  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
-};
-
-int main(int, char**) {
-  {
-    // Tests construction from a duration with an unsigned representation.
-    using Duration = std::chrono::duration<unsigned, std::milli>;
-
-    // 1 hour + 1 minute + 1 second + 1 millisecond
-    constexpr Duration d{3'661'001};
-    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
-
-    static_assert(!hms.is_negative());
-    static_assert(hms.hours() == std::chrono::hours{1});
-    static_assert(hms.minutes() == std::chrono::minutes{1});
-    static_assert(hms.seconds() == std::chrono::seconds{1});
-    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
-    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
-  }
-
-  {
-    // Tests construction from a negative duration with a custom representation.
-    using Duration = std::chrono::duration<SignedRep>;
-
-    // 1 hour + 1 minute + 1 second
-    constexpr Duration d{SignedRep{-3'661}};
-    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
-
-    static_assert(hms.is_negative());
-    static_assert(hms.hours() == std::chrono::hours{1});
-    static_assert(hms.minutes() == std::chrono::minutes{1});
-    static_assert(hms.seconds() == std::chrono::seconds{1});
-    static_assert(hms.subseconds() == std::chrono::seconds{0});
-    static_assert(hms.to_duration() == std::chrono::seconds{-3'661});
-  }
-
-  return 0;
-}
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
index ce7591c78b5eb..07f2d9c7214a6 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
@@ -47,6 +47,8 @@ int main(int, char**)
     static_assert( check_hours(std::chrono::minutes( 1)) == 0, "");
     static_assert( check_hours(std::chrono::minutes(-1)) == 0, "");
 
+    static_assert( check_hours(std::chrono::duration<unsigned, std::milli>(123456789)) == 34, "");
+
     assert( check_hours(std::chrono::seconds( 5000)) == 1);
     assert( check_hours(std::chrono::seconds(-5000)) == 1);
     assert( check_hours(std::chrono::minutes( 5000)) == 83);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp
index 51fa2d9a0e051..03047bdfa968a 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp
@@ -35,6 +35,8 @@ int main(int, char**)
     static_assert(!check_neg(std::chrono::minutes( 1)), "");
     static_assert( check_neg(std::chrono::minutes(-1)), "");
 
+    static_assert(!check_neg(std::chrono::duration<unsigned, std::milli>(123456789)), "");
+
     assert(!check_neg(std::chrono::seconds( 5000)));
     assert( check_neg(std::chrono::seconds(-5000)));
     assert(!check_neg(std::chrono::minutes( 5000)));
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
index 570b2727626b7..b1a813bad002f 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
@@ -38,6 +38,8 @@ int main(int, char**)
     static_assert( check_minutes(std::chrono::minutes( 1)) == 1, "");
     static_assert( check_minutes(std::chrono::minutes(-1)) == 1, "");
 
+    static_assert( check_minutes(std::chrono::duration<unsigned, std::milli>(123456789)) == 17, "");
+
     assert( check_minutes(std::chrono::seconds( 5000)) == 23);
     assert( check_minutes(std::chrono::seconds(-5000)) == 23);
     assert( check_minutes(std::chrono::minutes( 5000)) == 20);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
index f0723be01a74c..153918f5f5ec1 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
@@ -39,6 +39,8 @@ int main(int, char**)
     static_assert( check_seconds(std::chrono::seconds( 1)) == 1, "");
     static_assert( check_seconds(std::chrono::seconds(-1)) == 1, "");
 
+    static_assert( check_seconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 36, "");
+
     assert( check_seconds(std::chrono::seconds( 5000)) == 20);
     assert( check_seconds(std::chrono::seconds(-5000)) == 20);
     assert( check_seconds(std::chrono::minutes( 5000)) == 0);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
index 6504d19c48358..00b4a5db28722 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
@@ -39,6 +39,8 @@ int main(int, char**)
     static_assert( check_subseconds(std::chrono::seconds( 1)) == 0, "");
     static_assert( check_subseconds(std::chrono::seconds(-1)) == 0, "");
 
+    static_assert( check_subseconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 789, "");
+
     assert( check_subseconds(std::chrono::seconds( 5000)) == 0);
     assert( check_subseconds(std::chrono::seconds(-5000)) == 0);
     assert( check_subseconds(std::chrono::minutes( 5000)) == 0);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
index 129dcceb6a0a1..f6817b48d1c3e 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
@@ -39,6 +39,8 @@ int main(int, char**)
     static_assert( check_duration(std::chrono::minutes( 1)) ==  60, "");
     static_assert( check_duration(std::chrono::minutes(-1)) == -60, "");
 
+    static_assert( check_duration(std::chrono::duration<unsigned, std::milli>(123456789)) == 123456789, "");
+
     assert( check_duration(std::chrono::seconds( 5000)) ==    5000LL);
     assert( check_duration(std::chrono::seconds(-5000)) ==   -5000LL);
     assert( check_duration(std::chrono::minutes( 5000)) ==  300000LL);


From libcxx-commits at lists.llvm.org  Fri Jul 24 08:03:43 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 24 Jul 2026 08:03:43 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][CI] Add a Github workflow
 to test libc++ tools (PR #211067)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211067@github.com>
Message-ID: <6a637ecf.d1496011.1d7ce5.f9df@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/211067

>From 6bf86ec0dac40284dee328b708daec05d8a6fddb Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 21 Jul 2026 13:08:51 -0400
Subject: [PATCH 1/3] [libc++][CI] Add a Github workflow to test libc++ tools

We have a few scripts under libcxx/utils, and they wouldn't be tested
on a regular basis. As a result, changes to e.g. the build system could
break these scripts, which in turn could break some Github workflows
like the libc++ PR benchmarking workflow.

This patch adds a new `test-tools` configuration to `run-buildbot`
which allows testing these tools locally, and a new dedicated Github
workflow to run it.
---
 .github/workflows/libcxx-pr-test-tools.yml | 58 ++++++++++++++++++++++
 libcxx/utils/ci/run-buildbot               | 29 +++++++++++
 2 files changed, 87 insertions(+)
 create mode 100644 .github/workflows/libcxx-pr-test-tools.yml

diff --git a/.github/workflows/libcxx-pr-test-tools.yml b/.github/workflows/libcxx-pr-test-tools.yml
new file mode 100644
index 0000000000000..73299d9d66d48
--- /dev/null
+++ b/.github/workflows/libcxx-pr-test-tools.yml
@@ -0,0 +1,58 @@
+# This workflow exercises libc++ tools whose testing doesn't belong in the conformance
+# suite or in `check-cxx` proper.
+#
+# The actual logic lives in `libcxx/utils/ci/run-buildbot test-tools`, which is also how
+# failures can be reproduced locally (after installing libcxx/utils/requirements.txt):
+#
+#     CC=clang CXX=clang++ libcxx/utils/ci/run-buildbot test-tools
+
+name: "[libc++] Test tools"
+
+on:
+  pull_request:
+    paths:
+      - 'libcxx/**'
+      - 'libcxxabi/**'
+      - 'libunwind/**'
+      - 'runtimes/**'
+      - 'cmake/**'
+      - '.github/workflows/libcxx-pr-test-tools.yml'
+  schedule:
+    # Match the main libc++ CI cron
+    - cron: '0 8 * * *'
+
+permissions:
+  contents: read
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  test-tools:
+    runs-on: llvm-premerge-libcxx-runners
+    continue-on-error: false
+    env:
+      CC: clang-23
+      CXX: clang++-23
+    steps:
+      - uses: actions/checkout at df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+        with:
+          persist-credentials: false
+          fetch-depth: 0 # some tools need full git history
+          fetch-tags: true
+
+      - uses: actions/setup-python at ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
+        with:
+          python-version: '3.14'
+
+      - name: Set up Python dependencies
+        run: |
+          python3 -m venv .venv
+          source .venv/bin/activate
+          python -m pip install -r libcxx/utils/requirements.txt
+
+      - name: Run the tooling smoke test
+        run: |
+          source .venv/bin/activate
+          libcxx/utils/ci/run-buildbot test-tools
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 2ca14352b9b1b..2ec5838a7ea52 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -270,6 +270,35 @@ check-generated-output)
            --exclude 'underflow.pass.cpp' \
            || false
 ;;
+test-tools)
+    #
+    # This job tests libc++ tools that don't make sense to be tested inside the
+    # libc++ conformance test suite.
+    #
+    clean
+
+    step "Building and installing libc++ with build-at-commit"
+    "${MONOREPO_ROOT}/libcxx/utils/build-at-commit" \
+        --git-repo "${MONOREPO_ROOT}" \
+        --commit HEAD \
+        --install-dir "${INSTALL_DIR}" \
+        --tmp-build-dir "${BUILD_DIR}/build-at-commit" \
+        -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
+
+    step "Running a benchmark through the benchmark tooling pipeline"
+    "${MONOREPO_ROOT}/libcxx/utils/test-at-commit" \
+        --git-repo "${MONOREPO_ROOT}" \
+        --libcxx-installation "${INSTALL_DIR}" \
+        --build-dir "${BUILD_DIR}/benchmarks" \
+        -- -sv --param optimization=speed \
+        "${MONOREPO_ROOT}/libcxx/test/benchmarks/hash.bench.cpp"
+
+    step "Consolidate benchmark results"
+    "${MONOREPO_ROOT}/libcxx/utils/consolidate-benchmarks" "${BUILD_DIR}/benchmarks" > "${BUILD_DIR}/results.lnt"
+
+    step "Compare benchmark results" # perform a no-op comparison, just to run the tool
+    "${MONOREPO_ROOT}/libcxx/utils/compare-benchmarks" "${BUILD_DIR}/results.lnt" "${BUILD_DIR}/results.lnt"
+;;
 #
 # Various Standard modes
 #

>From ff1f391126740c12c92f57714335c36221e9dcf2 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 23 Jul 2026 16:35:44 -0400
Subject: [PATCH 2/3] Adjust for recent changes

---
 libcxx/utils/ci/run-buildbot | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 2ec5838a7ea52..a4fbf1ee315c0 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -283,13 +283,15 @@ test-tools)
         --commit HEAD \
         --install-dir "${INSTALL_DIR}" \
         --tmp-build-dir "${BUILD_DIR}/build-at-commit" \
-        -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
+        -- -DCMAKE_CXX_COMPILER="${CXX}" \
+           -DCMAKE_BUILD_TYPE=RelWithDebInfo
 
     step "Running a benchmark through the benchmark tooling pipeline"
     "${MONOREPO_ROOT}/libcxx/utils/test-at-commit" \
         --git-repo "${MONOREPO_ROOT}" \
         --libcxx-installation "${INSTALL_DIR}" \
         --build-dir "${BUILD_DIR}/benchmarks" \
+        --compiler "${CXX}"                   \
         -- -sv --param optimization=speed \
         "${MONOREPO_ROOT}/libcxx/test/benchmarks/hash.bench.cpp"
 

>From d593e59a2893a8a7f1d3a5b24ac051c61eaaf9f4 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 24 Jul 2026 11:03:29 -0400
Subject: [PATCH 3/3] Review comments

---
 .github/workflows/libcxx-pr-test-tools.yml | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/libcxx-pr-test-tools.yml b/.github/workflows/libcxx-pr-test-tools.yml
index 73299d9d66d48..64bbc705e5e09 100644
--- a/.github/workflows/libcxx-pr-test-tools.yml
+++ b/.github/workflows/libcxx-pr-test-tools.yml
@@ -22,7 +22,7 @@ on:
     - cron: '0 8 * * *'
 
 permissions:
-  contents: read
+  contents: read # Default everything to read-only
 
 concurrency:
   group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -30,8 +30,8 @@ concurrency:
 
 jobs:
   test-tools:
+    if: github.repository_owner == 'llvm'
     runs-on: llvm-premerge-libcxx-runners
-    continue-on-error: false
     env:
       CC: clang-23
       CXX: clang++-23
@@ -42,15 +42,11 @@ jobs:
           fetch-depth: 0 # some tools need full git history
           fetch-tags: true
 
-      - uses: actions/setup-python at ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
-        with:
-          python-version: '3.14'
-
       - name: Set up Python dependencies
         run: |
           python3 -m venv .venv
           source .venv/bin/activate
-          python -m pip install -r libcxx/utils/requirements.txt
+          pip install -r libcxx/utils/requirements.txt
 
       - name: Run the tooling smoke test
         run: |


From libcxx-commits at lists.llvm.org  Fri Jul 24 08:04:14 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 24 Jul 2026 08:04:14 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][CI] Add a Github workflow
 to test libc++ tools (PR #211067)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211067@github.com>
Message-ID: <6a637eee.bd93e731.32a2a8.f9f6@mx.google.com>


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/211067

>From 6bf86ec0dac40284dee328b708daec05d8a6fddb Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 21 Jul 2026 13:08:51 -0400
Subject: [PATCH 1/3] [libc++][CI] Add a Github workflow to test libc++ tools

We have a few scripts under libcxx/utils, and they wouldn't be tested
on a regular basis. As a result, changes to e.g. the build system could
break these scripts, which in turn could break some Github workflows
like the libc++ PR benchmarking workflow.

This patch adds a new `test-tools` configuration to `run-buildbot`
which allows testing these tools locally, and a new dedicated Github
workflow to run it.
---
 .github/workflows/libcxx-pr-test-tools.yml | 58 ++++++++++++++++++++++
 libcxx/utils/ci/run-buildbot               | 29 +++++++++++
 2 files changed, 87 insertions(+)
 create mode 100644 .github/workflows/libcxx-pr-test-tools.yml

diff --git a/.github/workflows/libcxx-pr-test-tools.yml b/.github/workflows/libcxx-pr-test-tools.yml
new file mode 100644
index 0000000000000..73299d9d66d48
--- /dev/null
+++ b/.github/workflows/libcxx-pr-test-tools.yml
@@ -0,0 +1,58 @@
+# This workflow exercises libc++ tools whose testing doesn't belong in the conformance
+# suite or in `check-cxx` proper.
+#
+# The actual logic lives in `libcxx/utils/ci/run-buildbot test-tools`, which is also how
+# failures can be reproduced locally (after installing libcxx/utils/requirements.txt):
+#
+#     CC=clang CXX=clang++ libcxx/utils/ci/run-buildbot test-tools
+
+name: "[libc++] Test tools"
+
+on:
+  pull_request:
+    paths:
+      - 'libcxx/**'
+      - 'libcxxabi/**'
+      - 'libunwind/**'
+      - 'runtimes/**'
+      - 'cmake/**'
+      - '.github/workflows/libcxx-pr-test-tools.yml'
+  schedule:
+    # Match the main libc++ CI cron
+    - cron: '0 8 * * *'
+
+permissions:
+  contents: read
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  test-tools:
+    runs-on: llvm-premerge-libcxx-runners
+    continue-on-error: false
+    env:
+      CC: clang-23
+      CXX: clang++-23
+    steps:
+      - uses: actions/checkout at df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+        with:
+          persist-credentials: false
+          fetch-depth: 0 # some tools need full git history
+          fetch-tags: true
+
+      - uses: actions/setup-python at ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
+        with:
+          python-version: '3.14'
+
+      - name: Set up Python dependencies
+        run: |
+          python3 -m venv .venv
+          source .venv/bin/activate
+          python -m pip install -r libcxx/utils/requirements.txt
+
+      - name: Run the tooling smoke test
+        run: |
+          source .venv/bin/activate
+          libcxx/utils/ci/run-buildbot test-tools
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 2ca14352b9b1b..2ec5838a7ea52 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -270,6 +270,35 @@ check-generated-output)
            --exclude 'underflow.pass.cpp' \
            || false
 ;;
+test-tools)
+    #
+    # This job tests libc++ tools that don't make sense to be tested inside the
+    # libc++ conformance test suite.
+    #
+    clean
+
+    step "Building and installing libc++ with build-at-commit"
+    "${MONOREPO_ROOT}/libcxx/utils/build-at-commit" \
+        --git-repo "${MONOREPO_ROOT}" \
+        --commit HEAD \
+        --install-dir "${INSTALL_DIR}" \
+        --tmp-build-dir "${BUILD_DIR}/build-at-commit" \
+        -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
+
+    step "Running a benchmark through the benchmark tooling pipeline"
+    "${MONOREPO_ROOT}/libcxx/utils/test-at-commit" \
+        --git-repo "${MONOREPO_ROOT}" \
+        --libcxx-installation "${INSTALL_DIR}" \
+        --build-dir "${BUILD_DIR}/benchmarks" \
+        -- -sv --param optimization=speed \
+        "${MONOREPO_ROOT}/libcxx/test/benchmarks/hash.bench.cpp"
+
+    step "Consolidate benchmark results"
+    "${MONOREPO_ROOT}/libcxx/utils/consolidate-benchmarks" "${BUILD_DIR}/benchmarks" > "${BUILD_DIR}/results.lnt"
+
+    step "Compare benchmark results" # perform a no-op comparison, just to run the tool
+    "${MONOREPO_ROOT}/libcxx/utils/compare-benchmarks" "${BUILD_DIR}/results.lnt" "${BUILD_DIR}/results.lnt"
+;;
 #
 # Various Standard modes
 #

>From ff1f391126740c12c92f57714335c36221e9dcf2 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 23 Jul 2026 16:35:44 -0400
Subject: [PATCH 2/3] Adjust for recent changes

---
 libcxx/utils/ci/run-buildbot | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 2ec5838a7ea52..a4fbf1ee315c0 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -283,13 +283,15 @@ test-tools)
         --commit HEAD \
         --install-dir "${INSTALL_DIR}" \
         --tmp-build-dir "${BUILD_DIR}/build-at-commit" \
-        -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
+        -- -DCMAKE_CXX_COMPILER="${CXX}" \
+           -DCMAKE_BUILD_TYPE=RelWithDebInfo
 
     step "Running a benchmark through the benchmark tooling pipeline"
     "${MONOREPO_ROOT}/libcxx/utils/test-at-commit" \
         --git-repo "${MONOREPO_ROOT}" \
         --libcxx-installation "${INSTALL_DIR}" \
         --build-dir "${BUILD_DIR}/benchmarks" \
+        --compiler "${CXX}"                   \
         -- -sv --param optimization=speed \
         "${MONOREPO_ROOT}/libcxx/test/benchmarks/hash.bench.cpp"
 

>From d593e59a2893a8a7f1d3a5b24ac051c61eaaf9f4 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 24 Jul 2026 11:03:29 -0400
Subject: [PATCH 3/3] Review comments

---
 .github/workflows/libcxx-pr-test-tools.yml | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/libcxx-pr-test-tools.yml b/.github/workflows/libcxx-pr-test-tools.yml
index 73299d9d66d48..64bbc705e5e09 100644
--- a/.github/workflows/libcxx-pr-test-tools.yml
+++ b/.github/workflows/libcxx-pr-test-tools.yml
@@ -22,7 +22,7 @@ on:
     - cron: '0 8 * * *'
 
 permissions:
-  contents: read
+  contents: read # Default everything to read-only
 
 concurrency:
   group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -30,8 +30,8 @@ concurrency:
 
 jobs:
   test-tools:
+    if: github.repository_owner == 'llvm'
     runs-on: llvm-premerge-libcxx-runners
-    continue-on-error: false
     env:
       CC: clang-23
       CXX: clang++-23
@@ -42,15 +42,11 @@ jobs:
           fetch-depth: 0 # some tools need full git history
           fetch-tags: true
 
-      - uses: actions/setup-python at ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
-        with:
-          python-version: '3.14'
-
       - name: Set up Python dependencies
         run: |
           python3 -m venv .venv
           source .venv/bin/activate
-          python -m pip install -r libcxx/utils/requirements.txt
+          pip install -r libcxx/utils/requirements.txt
 
       - name: Run the tooling smoke test
         run: |


From libcxx-commits at lists.llvm.org  Fri Jul 24 08:05:00 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 08:05:00 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a637f1c.7270d2fd.2df01a.4ba2@mx.google.com>


github-actions[bot] wrote:

<!--LLVM CODE FORMAT COMMENT: {clang-format}-->


:warning: C/C++ code formatter, clang-format found issues in your code. :warning:

<details>
<summary>
You can test this locally with the following command:
</summary>

``````````bash
git-clang-format --diff origin/main HEAD --extensions h,cpp -- libcxx/include/__chrono/hh_mm_ss.h libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp --diff_from_common_commit
``````````

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:

</details>

<details>
<summary>
View the diff from clang-format here.
</summary>

``````````diff
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
index 07f2d9c72..2e7e3ab7a 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
@@ -47,7 +47,7 @@ int main(int, char**)
     static_assert( check_hours(std::chrono::minutes( 1)) == 0, "");
     static_assert( check_hours(std::chrono::minutes(-1)) == 0, "");
 
-    static_assert( check_hours(std::chrono::duration<unsigned, std::milli>(123456789)) == 34, "");
+    static_assert(check_hours(std::chrono::duration<unsigned, std::milli>(123456789)) == 34, "");
 
     assert( check_hours(std::chrono::seconds( 5000)) == 1);
     assert( check_hours(std::chrono::seconds(-5000)) == 1);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
index b1a813bad..2fa1150b5 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
@@ -38,7 +38,7 @@ int main(int, char**)
     static_assert( check_minutes(std::chrono::minutes( 1)) == 1, "");
     static_assert( check_minutes(std::chrono::minutes(-1)) == 1, "");
 
-    static_assert( check_minutes(std::chrono::duration<unsigned, std::milli>(123456789)) == 17, "");
+    static_assert(check_minutes(std::chrono::duration<unsigned, std::milli>(123456789)) == 17, "");
 
     assert( check_minutes(std::chrono::seconds( 5000)) == 23);
     assert( check_minutes(std::chrono::seconds(-5000)) == 23);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
index 153918f5f..13d5f3c31 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
@@ -39,7 +39,7 @@ int main(int, char**)
     static_assert( check_seconds(std::chrono::seconds( 1)) == 1, "");
     static_assert( check_seconds(std::chrono::seconds(-1)) == 1, "");
 
-    static_assert( check_seconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 36, "");
+    static_assert(check_seconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 36, "");
 
     assert( check_seconds(std::chrono::seconds( 5000)) == 20);
     assert( check_seconds(std::chrono::seconds(-5000)) == 20);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
index 00b4a5db2..03cc15de9 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
@@ -39,7 +39,7 @@ int main(int, char**)
     static_assert( check_subseconds(std::chrono::seconds( 1)) == 0, "");
     static_assert( check_subseconds(std::chrono::seconds(-1)) == 0, "");
 
-    static_assert( check_subseconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 789, "");
+    static_assert(check_subseconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 789, "");
 
     assert( check_subseconds(std::chrono::seconds( 5000)) == 0);
     assert( check_subseconds(std::chrono::seconds(-5000)) == 0);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
index f6817b48d..9b77d3a28 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
@@ -39,7 +39,7 @@ int main(int, char**)
     static_assert( check_duration(std::chrono::minutes( 1)) ==  60, "");
     static_assert( check_duration(std::chrono::minutes(-1)) == -60, "");
 
-    static_assert( check_duration(std::chrono::duration<unsigned, std::milli>(123456789)) == 123456789, "");
+    static_assert(check_duration(std::chrono::duration<unsigned, std::milli>(123456789)) == 123456789, "");
 
     assert( check_duration(std::chrono::seconds( 5000)) ==    5000LL);
     assert( check_duration(std::chrono::seconds(-5000)) ==   -5000LL);

``````````

</details>


https://github.com/llvm/llvm-project/pull/209686

From libcxx-commits at lists.llvm.org  Fri Jul 24 08:09:02 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 08:09:02 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a63800e.92ea0e2a.2ed880.ccd3@mx.google.com>


================
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+#include <ratio>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
+
+int main(int, char**) {
+  {
+    // Tests construction from a duration with an unsigned representation.
+    using Duration = std::chrono::duration<unsigned, std::milli>;
+
+    // 1 hour + 1 minute + 1 second + 1 millisecond
+    constexpr Duration d{3'661'001};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(!hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
+    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
----------------
emmett2020 wrote:

Many thanks. Previously, I intended to use it to verify that the changes we made to unsign would not affect the signed custom types. However, it seems a bit redundant and not very concise. I have deleted it.

https://github.com/llvm/llvm-project/pull/209686

From libcxx-commits at lists.llvm.org  Fri Jul 24 08:09:12 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 08:09:12 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a638018.b4a331f0.1c45b5.2652@mx.google.com>


https://github.com/emmett2020 deleted https://github.com/llvm/llvm-project/pull/209686

From libcxx-commits at lists.llvm.org  Fri Jul 24 08:09:18 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 08:09:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a63801e.3cce8983.19fb00.ba5a@mx.google.com>


================
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+#include <ratio>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
----------------
emmett2020 wrote:

Many thanks. Previously, I intended to use it to verify that the changes we made to unsign would not affect the signed custom types. However, it seems a bit redundant and not very concise. I have deleted it.

https://github.com/llvm/llvm-project/pull/209686

From libcxx-commits at lists.llvm.org  Fri Jul 24 08:11:04 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 08:11:04 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a638088.eb165e5c.cb4e7.6201@mx.google.com>


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/209686

>From bc598c40cb7bac1531c0a3c1b32c0c8536ac6cd1 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 12:59:35 +0800
Subject: [PATCH 1/5] [libc++] Implement LWG 4274

Allow chrono::hh_mm_ss to be constructed from durations with unsigned representations.
---
 libcxx/docs/Status/Cxx26Issues.csv            |  2 +-
 libcxx/include/__chrono/hh_mm_ss.h            | 20 ++++--
 .../time.hms/time.hms.members/ctor.pass.cpp   | 68 +++++++++++++++++++
 3 files changed, 83 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index c203fa424556a..e37c05f035526 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -168,7 +168,7 @@
 "`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","|Complete|","23","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
 "`LWG4269 <https://wg21.link/LWG4269>`__","``unique_copy`` passes arguments to its predicate backwards","2025-11 (Kona)","","","`#171326 <https://github.com/llvm/llvm-project/issues/171326>`__",""
 "`LWG4272 <https://wg21.link/LWG4272>`__","For ``rank == 0``, ``layout_stride`` is atypically convertible","2025-11 (Kona)","","","`#171327 <https://github.com/llvm/llvm-project/issues/171327>`__",""
-"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","","","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
+"`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","|Complete|","24","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
 "`LWG4275 <https://wg21.link/LWG4275>`__","``std::dynamic_extent`` should also be defined in ``<mdspan>``","2025-11 (Kona)","|Complete|","21","`#171329 <https://github.com/llvm/llvm-project/issues/171329>`__",""
 "`LWG4276 <https://wg21.link/LWG4276>`__","``front()`` and ``back()`` are not hardened for zero-length ``std::array``\s","2025-11 (Kona)","|Complete|","17","`#171330 <https://github.com/llvm/llvm-project/issues/171330>`__",""
 "`LWG4280 <https://wg21.link/LWG4280>`__","``simd::partial_load`` uses undefined identifier ``T``","2025-11 (Kona)","","","`#171331 <https://github.com/llvm/llvm-project/issues/171331>`__",""
diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index 746eba903d4f8..ddb2d055c16ca 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -11,9 +11,9 @@
 #define _LIBCPP___CHRONO_HH_MM_SS_H
 
 #include <__chrono/duration.h>
-#include <__chrono/time_point.h>
 #include <__config>
 #include <__type_traits/common_type.h>
+#include <__type_traits/is_unsigned.h>
 #include <ratio>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -45,6 +45,14 @@ class hh_mm_ss {
     return 0;
   }
 
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+      return __d;
+    } else {
+      return __is_neg ? -__d : __d;
+    }
+  }
+
 public:
   _LIBCPP_HIDE_FROM_ABI static unsigned constexpr fractional_width =
       __width(__CommonType::period::den) < 19 ? __width(__CommonType::period::den) : 6u;
@@ -53,11 +61,11 @@ class hh_mm_ss {
   _LIBCPP_HIDE_FROM_ABI constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit hh_mm_ss(_Duration __d) noexcept
-      : __is_neg_(__d < _Duration(0)),
-        __h_(chrono::duration_cast<chrono::hours>(chrono::abs(__d))),
-        __m_(chrono::duration_cast<chrono::minutes>(chrono::abs(__d) - hours())),
-        __s_(chrono::duration_cast<chrono::seconds>(chrono::abs(__d) - hours() - minutes())),
-        __f_(chrono::duration_cast<precision>(chrono::abs(__d) - hours() - minutes() - seconds())) {}
+      : __is_neg_(__d < _Duration::zero()),
+        __h_(chrono::duration_cast<chrono::hours>(__abs_d(__d, __is_neg_))),
+        __m_(chrono::duration_cast<chrono::minutes>(__abs_d(__d, __is_neg_) - hours())),
+        __s_(chrono::duration_cast<chrono::seconds>(__abs_d(__d, __is_neg_) - hours() - minutes())),
+        __f_(chrono::duration_cast<precision>(__abs_d(__d, __is_neg_) - hours() - minutes() - seconds())) {}
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool is_negative() const noexcept { return __is_neg_; }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr chrono::hours hours() const noexcept { return __h_; }
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
new file mode 100644
index 0000000000000..1d3a946ed8873
--- /dev/null
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
+
+int main(int, char**) {
+  {
+    // Tests construction from a duration with an unsigned representation.
+    using Duration = std::chrono::duration<unsigned, std::milli>;
+
+    // 1 hour + 1 minute + 1 second + 1 millisecond
+    constexpr Duration d{3'661'001};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(!hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
+    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
+  }
+
+  {
+    // Tests construction from a negative duration with a custom representation.
+    using Duration = std::chrono::duration<SignedRep>;
+
+    // 1 hour + 1 minute + 1 second
+    constexpr Duration d{SignedRep{-3'661}};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::seconds{0});
+    static_assert(hms.to_duration() == std::chrono::seconds{-3'661});
+  }
+
+  return 0;
+}

>From 5504e7564dcfc299442cbf1d054a2be329c3e512 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Wed, 15 Jul 2026 14:19:47 +0800
Subject: [PATCH 2/5] fix missing header file

---
 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
index 1d3a946ed8873..b33917d262ba8 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
@@ -19,6 +19,7 @@
 // LWG4274: The hh_mm_ss constructor supports unsigned durations.
 
 #include <chrono>
+#include <ratio>
 
 // A signed arithmetic-like type used as a custom duration representation.
 struct SignedRep {

>From 6af50c255abb12100c2a764063b1fcf9ebb966ec Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Fri, 24 Jul 2026 22:30:10 +0800
Subject: [PATCH 3/5] Update libcxx/include/__chrono/hh_mm_ss.h

Co-authored-by: A. Jiang <de34 at live.cn>
---
 libcxx/include/__chrono/hh_mm_ss.h | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__chrono/hh_mm_ss.h b/libcxx/include/__chrono/hh_mm_ss.h
index ddb2d055c16ca..2e77304c68a53 100644
--- a/libcxx/include/__chrono/hh_mm_ss.h
+++ b/libcxx/include/__chrono/hh_mm_ss.h
@@ -46,11 +46,10 @@ class hh_mm_ss {
   }
 
   _LIBCPP_HIDE_FROM_ABI static constexpr _Duration __abs_d(_Duration __d, bool __is_neg) {
-    if constexpr (is_unsigned_v<typename _Duration::rep>) {
+    if constexpr (is_unsigned_v<typename _Duration::rep>)
       return __d;
-    } else {
+    else
       return __is_neg ? -__d : __d;
-    }
   }
 
 public:

>From ef96225c673afd250b5823a4910610a0a2770841 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Fri, 24 Jul 2026 23:03:01 +0800
Subject: [PATCH 4/5] fix reviewer comments

---
 .../time.hms/time.hms.members/ctor.pass.cpp   | 69 -------------------
 .../time.hms/time.hms.members/hours.pass.cpp  |  2 +
 .../time.hms.members/is_negative.pass.cpp     |  2 +
 .../time.hms.members/minutes.pass.cpp         |  2 +
 .../time.hms.members/seconds.pass.cpp         |  2 +
 .../time.hms.members/subseconds.pass.cpp      |  2 +
 .../time.hms.members/to_duration.pass.cpp     |  2 +
 7 files changed, 12 insertions(+), 69 deletions(-)
 delete mode 100644 libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp

diff --git a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
deleted file mode 100644
index b33917d262ba8..0000000000000
--- a/libcxx/test/std/time/time.hms/time.hms.members/ctor.pass.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// <chrono>
-//
-// template<class Duration>
-// class hh_mm_ss {
-// public:
-//   constexpr explicit hh_mm_ss(Duration d);
-// };
-//
-// LWG4274: The hh_mm_ss constructor supports unsigned durations.
-
-#include <chrono>
-#include <ratio>
-
-// A signed arithmetic-like type used as a custom duration representation.
-struct SignedRep {
-  long long value;
-
-  constexpr explicit SignedRep(long long v = 0) : value(v) {}
-  constexpr operator long long() const { return value; }
-
-  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
-  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
-};
-
-int main(int, char**) {
-  {
-    // Tests construction from a duration with an unsigned representation.
-    using Duration = std::chrono::duration<unsigned, std::milli>;
-
-    // 1 hour + 1 minute + 1 second + 1 millisecond
-    constexpr Duration d{3'661'001};
-    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
-
-    static_assert(!hms.is_negative());
-    static_assert(hms.hours() == std::chrono::hours{1});
-    static_assert(hms.minutes() == std::chrono::minutes{1});
-    static_assert(hms.seconds() == std::chrono::seconds{1});
-    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
-    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
-  }
-
-  {
-    // Tests construction from a negative duration with a custom representation.
-    using Duration = std::chrono::duration<SignedRep>;
-
-    // 1 hour + 1 minute + 1 second
-    constexpr Duration d{SignedRep{-3'661}};
-    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
-
-    static_assert(hms.is_negative());
-    static_assert(hms.hours() == std::chrono::hours{1});
-    static_assert(hms.minutes() == std::chrono::minutes{1});
-    static_assert(hms.seconds() == std::chrono::seconds{1});
-    static_assert(hms.subseconds() == std::chrono::seconds{0});
-    static_assert(hms.to_duration() == std::chrono::seconds{-3'661});
-  }
-
-  return 0;
-}
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
index ce7591c78b5eb..07f2d9c7214a6 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
@@ -47,6 +47,8 @@ int main(int, char**)
     static_assert( check_hours(std::chrono::minutes( 1)) == 0, "");
     static_assert( check_hours(std::chrono::minutes(-1)) == 0, "");
 
+    static_assert( check_hours(std::chrono::duration<unsigned, std::milli>(123456789)) == 34, "");
+
     assert( check_hours(std::chrono::seconds( 5000)) == 1);
     assert( check_hours(std::chrono::seconds(-5000)) == 1);
     assert( check_hours(std::chrono::minutes( 5000)) == 83);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp
index 51fa2d9a0e051..03047bdfa968a 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/is_negative.pass.cpp
@@ -35,6 +35,8 @@ int main(int, char**)
     static_assert(!check_neg(std::chrono::minutes( 1)), "");
     static_assert( check_neg(std::chrono::minutes(-1)), "");
 
+    static_assert(!check_neg(std::chrono::duration<unsigned, std::milli>(123456789)), "");
+
     assert(!check_neg(std::chrono::seconds( 5000)));
     assert( check_neg(std::chrono::seconds(-5000)));
     assert(!check_neg(std::chrono::minutes( 5000)));
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
index 570b2727626b7..b1a813bad002f 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
@@ -38,6 +38,8 @@ int main(int, char**)
     static_assert( check_minutes(std::chrono::minutes( 1)) == 1, "");
     static_assert( check_minutes(std::chrono::minutes(-1)) == 1, "");
 
+    static_assert( check_minutes(std::chrono::duration<unsigned, std::milli>(123456789)) == 17, "");
+
     assert( check_minutes(std::chrono::seconds( 5000)) == 23);
     assert( check_minutes(std::chrono::seconds(-5000)) == 23);
     assert( check_minutes(std::chrono::minutes( 5000)) == 20);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
index f0723be01a74c..153918f5f5ec1 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
@@ -39,6 +39,8 @@ int main(int, char**)
     static_assert( check_seconds(std::chrono::seconds( 1)) == 1, "");
     static_assert( check_seconds(std::chrono::seconds(-1)) == 1, "");
 
+    static_assert( check_seconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 36, "");
+
     assert( check_seconds(std::chrono::seconds( 5000)) == 20);
     assert( check_seconds(std::chrono::seconds(-5000)) == 20);
     assert( check_seconds(std::chrono::minutes( 5000)) == 0);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
index 6504d19c48358..00b4a5db28722 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
@@ -39,6 +39,8 @@ int main(int, char**)
     static_assert( check_subseconds(std::chrono::seconds( 1)) == 0, "");
     static_assert( check_subseconds(std::chrono::seconds(-1)) == 0, "");
 
+    static_assert( check_subseconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 789, "");
+
     assert( check_subseconds(std::chrono::seconds( 5000)) == 0);
     assert( check_subseconds(std::chrono::seconds(-5000)) == 0);
     assert( check_subseconds(std::chrono::minutes( 5000)) == 0);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
index 129dcceb6a0a1..f6817b48d1c3e 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
@@ -39,6 +39,8 @@ int main(int, char**)
     static_assert( check_duration(std::chrono::minutes( 1)) ==  60, "");
     static_assert( check_duration(std::chrono::minutes(-1)) == -60, "");
 
+    static_assert( check_duration(std::chrono::duration<unsigned, std::milli>(123456789)) == 123456789, "");
+
     assert( check_duration(std::chrono::seconds( 5000)) ==    5000LL);
     assert( check_duration(std::chrono::seconds(-5000)) ==   -5000LL);
     assert( check_duration(std::chrono::minutes( 5000)) ==  300000LL);

>From 6c3ce1a4d3f57bf13126c8c332cb4cd14e03a030 Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Fri, 24 Jul 2026 23:10:44 +0800
Subject: [PATCH 5/5] fix format

---
 libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp   | 2 +-
 libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp | 2 +-
 libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp | 2 +-
 .../test/std/time/time.hms/time.hms.members/subseconds.pass.cpp | 2 +-
 .../std/time/time.hms/time.hms.members/to_duration.pass.cpp     | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
index 07f2d9c7214a6..2e7e3ab7a0e3a 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/hours.pass.cpp
@@ -47,7 +47,7 @@ int main(int, char**)
     static_assert( check_hours(std::chrono::minutes( 1)) == 0, "");
     static_assert( check_hours(std::chrono::minutes(-1)) == 0, "");
 
-    static_assert( check_hours(std::chrono::duration<unsigned, std::milli>(123456789)) == 34, "");
+    static_assert(check_hours(std::chrono::duration<unsigned, std::milli>(123456789)) == 34, "");
 
     assert( check_hours(std::chrono::seconds( 5000)) == 1);
     assert( check_hours(std::chrono::seconds(-5000)) == 1);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
index b1a813bad002f..2fa1150b57146 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/minutes.pass.cpp
@@ -38,7 +38,7 @@ int main(int, char**)
     static_assert( check_minutes(std::chrono::minutes( 1)) == 1, "");
     static_assert( check_minutes(std::chrono::minutes(-1)) == 1, "");
 
-    static_assert( check_minutes(std::chrono::duration<unsigned, std::milli>(123456789)) == 17, "");
+    static_assert(check_minutes(std::chrono::duration<unsigned, std::milli>(123456789)) == 17, "");
 
     assert( check_minutes(std::chrono::seconds( 5000)) == 23);
     assert( check_minutes(std::chrono::seconds(-5000)) == 23);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
index 153918f5f5ec1..13d5f3c313852 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/seconds.pass.cpp
@@ -39,7 +39,7 @@ int main(int, char**)
     static_assert( check_seconds(std::chrono::seconds( 1)) == 1, "");
     static_assert( check_seconds(std::chrono::seconds(-1)) == 1, "");
 
-    static_assert( check_seconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 36, "");
+    static_assert(check_seconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 36, "");
 
     assert( check_seconds(std::chrono::seconds( 5000)) == 20);
     assert( check_seconds(std::chrono::seconds(-5000)) == 20);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
index 00b4a5db28722..03cc15de9c68b 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/subseconds.pass.cpp
@@ -39,7 +39,7 @@ int main(int, char**)
     static_assert( check_subseconds(std::chrono::seconds( 1)) == 0, "");
     static_assert( check_subseconds(std::chrono::seconds(-1)) == 0, "");
 
-    static_assert( check_subseconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 789, "");
+    static_assert(check_subseconds(std::chrono::duration<unsigned, std::milli>(123456789)) == 789, "");
 
     assert( check_subseconds(std::chrono::seconds( 5000)) == 0);
     assert( check_subseconds(std::chrono::seconds(-5000)) == 0);
diff --git a/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp b/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
index f6817b48d1c3e..9b77d3a280e94 100644
--- a/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
+++ b/libcxx/test/std/time/time.hms/time.hms.members/to_duration.pass.cpp
@@ -39,7 +39,7 @@ int main(int, char**)
     static_assert( check_duration(std::chrono::minutes( 1)) ==  60, "");
     static_assert( check_duration(std::chrono::minutes(-1)) == -60, "");
 
-    static_assert( check_duration(std::chrono::duration<unsigned, std::milli>(123456789)) == 123456789, "");
+    static_assert(check_duration(std::chrono::duration<unsigned, std::milli>(123456789)) == 123456789, "");
 
     assert( check_duration(std::chrono::seconds( 5000)) ==    5000LL);
     assert( check_duration(std::chrono::seconds(-5000)) ==   -5000LL);


From libcxx-commits at lists.llvm.org  Fri Jul 24 08:12:50 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 08:12:50 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][chrono] Implement LWG 4274:
 Allow chrono::hh_mm_ss to be constructed from unsigned durations (PR
 #209686)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/209686@github.com>
Message-ID: <6a6380f2.f3151ff8.2f3401.bc99@mx.google.com>


================
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <chrono>
+//
+// template<class Duration>
+// class hh_mm_ss {
+// public:
+//   constexpr explicit hh_mm_ss(Duration d);
+// };
+//
+// LWG4274: The hh_mm_ss constructor supports unsigned durations.
+
+#include <chrono>
+#include <ratio>
+
+// A signed arithmetic-like type used as a custom duration representation.
+struct SignedRep {
+  long long value;
+
+  constexpr explicit SignedRep(long long v = 0) : value(v) {}
+  constexpr operator long long() const { return value; }
+
+  friend constexpr SignedRep operator-(SignedRep v) { return SignedRep{-v.value}; }
+  friend constexpr bool operator<(SignedRep lhs, SignedRep rhs) { return lhs.value < rhs.value; }
+};
+
+int main(int, char**) {
+  {
+    // Tests construction from a duration with an unsigned representation.
+    using Duration = std::chrono::duration<unsigned, std::milli>;
+
+    // 1 hour + 1 minute + 1 second + 1 millisecond
+    constexpr Duration d{3'661'001};
+    constexpr std::chrono::hh_mm_ss<Duration> hms{d};
+
+    static_assert(!hms.is_negative());
+    static_assert(hms.hours() == std::chrono::hours{1});
+    static_assert(hms.minutes() == std::chrono::minutes{1});
+    static_assert(hms.seconds() == std::chrono::seconds{1});
+    static_assert(hms.subseconds() == std::chrono::milliseconds{1});
+    static_assert(hms.to_duration() == std::chrono::milliseconds{3'661'001});
----------------
emmett2020 wrote:

Thanks for review. I have split it into `time.hms.members`. However, the code formatting style cannot conform to the existing code. The existing code formatting is flawed, and the newly modified code needs to pass the clang-format check.

https://github.com/llvm/llvm-project/pull/209686

From libcxx-commits at lists.llvm.org  Fri Jul 24 09:07:21 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 24 Jul 2026 09:07:21 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++] Rename pre-commit CI
	workflows (PR #211844)
Message-ID: <llvm.org/llvm/llvm-project/pull/211844@github.com>


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/211844

Since we're expanding libc++'s pre-commit CI to add other types of tests, (e.g. tools tests, performance tests, etc), it makes sense to be a bit more precise about what each workflow does, and to use a consistent pattern across workflow names.

Also, update stale references to old names in the documentation and in some infrastructure scripts.

<html><body><h1>504 Gateway Time-out</h1>
The server didn't respond in time.
</body></html>


From libcxx-commits at lists.llvm.org  Fri Jul 24 09:29:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 09:29:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++] Rename pre-commit CI
	workflows (PR #211844)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211844@github.com>
Message-ID: <6a6392ff.d76d81af.396077.0061@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

<details>
<summary>Changes</summary>

Since we're expanding libc++'s pre-commit CI to add other types of tests, (e.g. tools tests, performance tests, etc), it makes sense to be a bit more precise about what each workflow does, and to use a consistent pattern across workflow names.

Also, update stale references to old names in the documentation and in some infrastructure scripts.

---
Full diff: https://github.com/llvm/llvm-project/pull/211844.diff


10 Files Affected:

- (modified) .ci/metrics/metrics.py (+3-3) 
- (modified) .ci/metrics/metrics_test.py (+16-16) 
- (modified) .github/workflows/libcxx-benchmark-commit.yml (+1-1) 
- (modified) .github/workflows/libcxx-build-containers.yml (+1-1) 
- (modified) .github/workflows/libcxx-pr-benchmark.yml (+1-1) 
- (renamed) .github/workflows/libcxx-pr-check-generated-files.yml (+1-1) 
- (renamed) .github/workflows/libcxx-pr-conformance-tests.yaml (+4-3) 
- (modified) README.md (+1-1) 
- (modified) libcxx/docs/Contributing.rst (+1-1) 
- (modified) libcxx/docs/index.rst (+1-1) 


``````````diff
diff --git a/.ci/metrics/metrics.py b/.ci/metrics/metrics.py
index ac39a47d43c07..3785e205d8360 100644
--- a/.ci/metrics/metrics.py
+++ b/.ci/metrics/metrics.py
@@ -30,7 +30,7 @@
 # This metric name is also used as a key in the job->name map.
 GITHUB_WORKFLOW_TO_TRACK = {
     "CI Checks": "github_llvm_premerge_checks",
-    "Build and Test libc++": "github_libcxx_premerge_checks",
+    "[libc++] Conformance tests": "github_libcxx_premerge_checks",
 }
 
 # Lists the Github jobs to track for a given workflow. The key is the stable
@@ -175,7 +175,7 @@ def create_and_append_libcxx_aggregates(workflow_metrics: list[JobMetrics]):
         if not isinstance(job, JobMetrics):
             continue
         # Only want libc++ jobs.
-        if job.workflow_name != "Build and Test libc++":
+        if job.workflow_name != "[libc++] Conformance tests":
             continue
         if job.workflow_id not in aggregate_data.keys():
             aggregate_data[job.workflow_id] = [job]
@@ -312,7 +312,7 @@ def github_get_metrics(
             continue
 
         libcxx_testing = False
-        if task.name == "Build and Test libc++":
+        if task.name == "[libc++] Conformance tests":
             libcxx_testing = True
 
         if task.status == "completed":
diff --git a/.ci/metrics/metrics_test.py b/.ci/metrics/metrics_test.py
index 3004c367f96ff..727bdbfa87cda 100644
--- a/.ci/metrics/metrics_test.py
+++ b/.ci/metrics/metrics_test.py
@@ -100,7 +100,7 @@ def test_create_and_append_aggregate_metric_1_stage(self):
                 started_at_ns=1755697961000000000,
                 completed_at_ns=1755698349000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage1_test2",
@@ -111,7 +111,7 @@ def test_create_and_append_aggregate_metric_1_stage(self):
                 started_at_ns=1755698060000000000,
                 completed_at_ns=1755698417000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage1_test3",
@@ -122,7 +122,7 @@ def test_create_and_append_aggregate_metric_1_stage(self):
                 started_at_ns=1755697961000000000,
                 completed_at_ns=1755698785000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
         ]
         metrics.create_and_append_libcxx_aggregates(test_metrics)
@@ -150,7 +150,7 @@ def test_create_and_append_aggregate_metric_multiple_workflow_ids(self):
                 started_at_ns=1755697961000000000,
                 completed_at_ns=1755698349000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage1_test2",
@@ -161,7 +161,7 @@ def test_create_and_append_aggregate_metric_multiple_workflow_ids(self):
                 started_at_ns=1755698060000000000,
                 completed_at_ns=1755698417000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage1_test3",
@@ -172,7 +172,7 @@ def test_create_and_append_aggregate_metric_multiple_workflow_ids(self):
                 started_at_ns=1755697961000000000,
                 completed_at_ns=1755698785000000000,
                 workflow_id=25,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
         ]
         metrics.create_and_append_libcxx_aggregates(test_metrics)
@@ -211,7 +211,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755697053000000000,
                 completed_at_ns=1755698507000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage1_test2",
@@ -222,7 +222,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755697058000000000,
                 completed_at_ns=1755697885000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage2_test1",
@@ -233,7 +233,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755698513000000000,
                 completed_at_ns=1755699093000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage2_test2",
@@ -244,7 +244,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755698514000000000,
                 completed_at_ns=1755698987000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage2_test3",
@@ -255,7 +255,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755698514000000000,
                 completed_at_ns=1755699334000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage3_test1",
@@ -266,7 +266,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755709012000000000,
                 completed_at_ns=1755709931000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage3_test2",
@@ -277,7 +277,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755709146000000000,
                 completed_at_ns=1755709980000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage3_test3",
@@ -288,7 +288,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755709136000000000,
                 completed_at_ns=1755709514000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
         ]
         metrics.create_and_append_libcxx_aggregates(test_metrics)
@@ -341,7 +341,7 @@ def test_create_and_append_aggregate_metric_mixed_job_types(self):
                 started_at_ns=1755697961000000000,
                 completed_at_ns=1755698349000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "ci_test2", 3, 20, 1, 2000, 2200, 2400, 37, "premerge_test"
@@ -355,7 +355,7 @@ def test_create_and_append_aggregate_metric_mixed_job_types(self):
                 started_at_ns=1755698060000000000,
                 completed_at_ns=1755698417000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "ci_test3", 7, 35, 1, 3000, 3200, 3400, 85, "premerge_test"
diff --git a/.github/workflows/libcxx-benchmark-commit.yml b/.github/workflows/libcxx-benchmark-commit.yml
index 683ff141d060e..90ade6c45cf21 100644
--- a/.github/workflows/libcxx-benchmark-commit.yml
+++ b/.github/workflows/libcxx-benchmark-commit.yml
@@ -2,7 +2,7 @@
 # This workflow is intended to be triggered manually or via the Github API. As such,
 # it requires several inputs that allow customizing its behavior.
 
-name: Benchmark libc++ commit
+name: "[libc++] Run benchmark suite against commit"
 
 permissions:
   contents: read
diff --git a/.github/workflows/libcxx-build-containers.yml b/.github/workflows/libcxx-build-containers.yml
index 814de39a5e0a9..2789851da391b 100644
--- a/.github/workflows/libcxx-build-containers.yml
+++ b/.github/workflows/libcxx-build-containers.yml
@@ -5,7 +5,7 @@
 # and tagged appropriately. The selection of which Docker image version is used by the libc++
 # CI nodes at any given point is controlled from the workflow files themselves.
 
-name: Build Docker images for libc++ CI
+name: "[libc++] Build CI Docker images"
 
 permissions:
   contents: read
diff --git a/.github/workflows/libcxx-pr-benchmark.yml b/.github/workflows/libcxx-pr-benchmark.yml
index 31bf6ffb224b6..92668a2f79f6c 100644
--- a/.github/workflows/libcxx-pr-benchmark.yml
+++ b/.github/workflows/libcxx-pr-benchmark.yml
@@ -7,7 +7,7 @@
 # That will cause the specified benchmarks to be run on the PR and on the pull-request target, and
 # their results to be compared.
 
-name: Benchmark libc++ PR
+name: "[libc++] Benchmark PR"
 
 permissions:
   contents: read
diff --git a/.github/workflows/libcxx-check-generated-files.yml b/.github/workflows/libcxx-pr-check-generated-files.yml
similarity index 94%
rename from .github/workflows/libcxx-check-generated-files.yml
rename to .github/workflows/libcxx-pr-check-generated-files.yml
index d66d1da22f2ad..66f2735b08eab 100644
--- a/.github/workflows/libcxx-check-generated-files.yml
+++ b/.github/workflows/libcxx-pr-check-generated-files.yml
@@ -1,4 +1,4 @@
-name: "Check libc++ generated files"
+name: "[libc++] Check generated files"
 on:
   pull_request:
     paths:
diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-pr-conformance-tests.yaml
similarity index 98%
rename from .github/workflows/libcxx-build-and-test.yaml
rename to .github/workflows/libcxx-pr-conformance-tests.yaml
index b95230b725a88..a7a93a110428c 100644
--- a/.github/workflows/libcxx-build-and-test.yaml
+++ b/.github/workflows/libcxx-pr-conformance-tests.yaml
@@ -1,4 +1,5 @@
-# This file defines pre-commit CI for libc++, libc++abi, and libunwind (on Github).
+# This file defines a workflow that runs conformance tests for libc++, libc++abi and libunwind when
+# a PR is created or updated.
 #
 # We split the configurations in multiple stages with the intent of saving compute time
 # when a job fails early in the pipeline. This is why the jobs are marked as `continue-on-error: false`.
@@ -12,7 +13,7 @@
 # under the assumption that if the "smoke tests" fail, then the other configurations will likely fail in the same way.
 # However, stage 3 does not fail fast, as it's more likely that any one job failing is a flake or a configuration-specific
 #
-name: Build and Test libc++
+name: "[libc++] Conformance tests"
 on:
   pull_request:
     paths:
@@ -21,7 +22,7 @@ on:
       - 'libunwind/**'
       - 'runtimes/**'
       - 'cmake/**'
-      - '.github/workflows/libcxx-build-and-test.yaml'
+      - '.github/workflows/libcxx-pr-conformance-tests.yaml'
   schedule:
     # Run nightly at 08:00 UTC (aka 00:00 Pacific, aka 03:00 Eastern)
     - cron: '0 8 * * *'
diff --git a/README.md b/README.md
index a9b29ecbc1a3a..55425f05c9879 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 
 [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/llvm/llvm-project/badge)](https://securityscorecards.dev/viewer/?uri=github.com/llvm/llvm-project)
 [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8273/badge)](https://www.bestpractices.dev/projects/8273)
-[![libc++](https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml/badge.svg?branch=main&event=schedule)](https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml?query=event%3Aschedule)
+[![libc++](https://github.com/llvm/llvm-project/actions/workflows/libcxx-pr-conformance-tests.yaml/badge.svg?branch=main&event=schedule)](https://github.com/llvm/llvm-project/actions/workflows/libcxx-pr-conformance-tests.yaml?query=event%3Aschedule)
 
 Welcome to the LLVM project!
 
diff --git a/libcxx/docs/Contributing.rst b/libcxx/docs/Contributing.rst
index 465bf9d0542c8..3c4d45dd17ce0 100644
--- a/libcxx/docs/Contributing.rst
+++ b/libcxx/docs/Contributing.rst
@@ -272,7 +272,7 @@ The libcxx linux premerge testing can run on one of three sets of runner
 groups. The three runner group names are ``llvm-premerge-libcxx-runners``,
 ``llvm-premerge-libcxx-release-runners`` and ``llvm-premerge-libcxx-next-runners``.
 The runner set currently in use is controlled by the contents of
-https://github.com/llvm/llvm-project/blob/main/.github/workflows/libcxx-build-and-test.yaml.
+https://github.com/llvm/llvm-project/blob/main/.github/workflows/libcxx-pr-conformance-tests.yaml.
 By default, it uses ``llvm-premerge-libcxx-runners``. To switch to one of the
 other runner sets, just replace all uses of ``llvm-premerge-libcxx-runners`` in
 the yaml file with the desired runner set.
diff --git a/libcxx/docs/index.rst b/libcxx/docs/index.rst
index 13aa481b1f9b7..d6bbc771f0e6b 100644
--- a/libcxx/docs/index.rst
+++ b/libcxx/docs/index.rst
@@ -237,7 +237,7 @@ Design Documents
 Build Bots and Test Coverage
 ============================
 
-* `Github Actions CI pipeline <https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml>`_
+* `Github Actions CI pipeline <https://github.com/llvm/llvm-project/actions/workflows/libcxx-pr-conformance-tests.yaml>`_
 * `Buildkite CI pipeline <https://buildkite.com/llvm-project/libcxx-ci>`_
 * `LLVM Buildbot Builders <https://lab.llvm.org/buildbot>`_
 * :ref:`Adding New CI Jobs <AddingNewCIJobs>`

``````````

</details>


https://github.com/llvm/llvm-project/pull/211844

From libcxx-commits at lists.llvm.org  Fri Jul 24 11:53:29 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 11:53:29 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] 8676929 - [libc++][CI] Add a Github
 workflow to test libc++ tools (#211067)
Message-ID: <6a63b4a9.d2cb4adc.23815e.b5ff@mx.google.com>


Author: Louis Dionne
Date: 2026-07-24T14:53:24-04:00
New Revision: 86769296941811360d7126e41e3b13cbabab55ae

URL: https://github.com/llvm/llvm-project/commit/86769296941811360d7126e41e3b13cbabab55ae
DIFF: https://github.com/llvm/llvm-project/commit/86769296941811360d7126e41e3b13cbabab55ae.diff

LOG: [libc++][CI] Add a Github workflow to test libc++ tools (#211067)

We have a few scripts under libcxx/utils, and they wouldn't be tested on
a regular basis. As a result, changes to e.g. the build system could
break these scripts, which in turn could break some Github workflows
like the libc++ PR benchmarking workflow.

This patch adds a new `test-tools` configuration to `run-buildbot` which
allows testing these tools locally, and a new dedicated Github workflow
to run it.

Added: 
    .github/workflows/libcxx-pr-test-tools.yml

Modified: 
    libcxx/utils/ci/run-buildbot

Removed: 
    


################################################################################
diff  --git a/.github/workflows/libcxx-pr-test-tools.yml b/.github/workflows/libcxx-pr-test-tools.yml
new file mode 100644
index 0000000000000..64bbc705e5e09
--- /dev/null
+++ b/.github/workflows/libcxx-pr-test-tools.yml
@@ -0,0 +1,54 @@
+# This workflow exercises libc++ tools whose testing doesn't belong in the conformance
+# suite or in `check-cxx` proper.
+#
+# The actual logic lives in `libcxx/utils/ci/run-buildbot test-tools`, which is also how
+# failures can be reproduced locally (after installing libcxx/utils/requirements.txt):
+#
+#     CC=clang CXX=clang++ libcxx/utils/ci/run-buildbot test-tools
+
+name: "[libc++] Test tools"
+
+on:
+  pull_request:
+    paths:
+      - 'libcxx/**'
+      - 'libcxxabi/**'
+      - 'libunwind/**'
+      - 'runtimes/**'
+      - 'cmake/**'
+      - '.github/workflows/libcxx-pr-test-tools.yml'
+  schedule:
+    # Match the main libc++ CI cron
+    - cron: '0 8 * * *'
+
+permissions:
+  contents: read # Default everything to read-only
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  test-tools:
+    if: github.repository_owner == 'llvm'
+    runs-on: llvm-premerge-libcxx-runners
+    env:
+      CC: clang-23
+      CXX: clang++-23
+    steps:
+      - uses: actions/checkout at df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+        with:
+          persist-credentials: false
+          fetch-depth: 0 # some tools need full git history
+          fetch-tags: true
+
+      - name: Set up Python dependencies
+        run: |
+          python3 -m venv .venv
+          source .venv/bin/activate
+          pip install -r libcxx/utils/requirements.txt
+
+      - name: Run the tooling smoke test
+        run: |
+          source .venv/bin/activate
+          libcxx/utils/ci/run-buildbot test-tools

diff  --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 2ca14352b9b1b..a4fbf1ee315c0 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -270,6 +270,37 @@ check-generated-output)
            --exclude 'underflow.pass.cpp' \
            || false
 ;;
+test-tools)
+    #
+    # This job tests libc++ tools that don't make sense to be tested inside the
+    # libc++ conformance test suite.
+    #
+    clean
+
+    step "Building and installing libc++ with build-at-commit"
+    "${MONOREPO_ROOT}/libcxx/utils/build-at-commit" \
+        --git-repo "${MONOREPO_ROOT}" \
+        --commit HEAD \
+        --install-dir "${INSTALL_DIR}" \
+        --tmp-build-dir "${BUILD_DIR}/build-at-commit" \
+        -- -DCMAKE_CXX_COMPILER="${CXX}" \
+           -DCMAKE_BUILD_TYPE=RelWithDebInfo
+
+    step "Running a benchmark through the benchmark tooling pipeline"
+    "${MONOREPO_ROOT}/libcxx/utils/test-at-commit" \
+        --git-repo "${MONOREPO_ROOT}" \
+        --libcxx-installation "${INSTALL_DIR}" \
+        --build-dir "${BUILD_DIR}/benchmarks" \
+        --compiler "${CXX}"                   \
+        -- -sv --param optimization=speed \
+        "${MONOREPO_ROOT}/libcxx/test/benchmarks/hash.bench.cpp"
+
+    step "Consolidate benchmark results"
+    "${MONOREPO_ROOT}/libcxx/utils/consolidate-benchmarks" "${BUILD_DIR}/benchmarks" > "${BUILD_DIR}/results.lnt"
+
+    step "Compare benchmark results" # perform a no-op comparison, just to run the tool
+    "${MONOREPO_ROOT}/libcxx/utils/compare-benchmarks" "${BUILD_DIR}/results.lnt" "${BUILD_DIR}/results.lnt"
+;;
 #
 # Various Standard modes
 #


        

From libcxx-commits at lists.llvm.org  Fri Jul 24 11:53:33 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 24 Jul 2026 11:53:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++][CI] Add a Github workflow
 to test libc++ tools (PR #211067)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211067@github.com>
Message-ID: <6a63b4ad.492e23e4.3ce411.8a6f@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/211067

From libcxx-commits at lists.llvm.org  Fri Jul 24 11:56:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Fri, 24 Jul 2026 11:56:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] ba61615 - [libc++] Rename pre-commit CI
	workflows (#211844)
Message-ID: <6a63b557.1905e68e.117dfd.27b6@mx.google.com>


Author: Louis Dionne
Date: 2026-07-24T14:56:19-04:00
New Revision: ba61615b653c4f59b3cd3d721291f0b6009b0b51

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

LOG: [libc++] Rename pre-commit CI workflows (#211844)

Since we're expanding libc++'s pre-commit CI to add other types of
tests, (e.g. tools tests, performance tests, etc), it makes sense to be
a bit more precise about what each workflow does, and to use a
consistent pattern across workflow names.

Also, update stale references to old names in the documentation and in
some infrastructure scripts.

Added: 
    .github/workflows/libcxx-pr-check-generated-files.yml
    .github/workflows/libcxx-pr-conformance-tests.yaml

Modified: 
    .ci/metrics/metrics.py
    .ci/metrics/metrics_test.py
    .github/workflows/libcxx-benchmark-commit.yml
    .github/workflows/libcxx-build-containers.yml
    .github/workflows/libcxx-pr-benchmark.yml
    README.md
    libcxx/docs/Contributing.rst
    libcxx/docs/index.rst

Removed: 
    .github/workflows/libcxx-build-and-test.yaml
    .github/workflows/libcxx-check-generated-files.yml


################################################################################
diff  --git a/.ci/metrics/metrics.py b/.ci/metrics/metrics.py
index ac39a47d43c07..3785e205d8360 100644
--- a/.ci/metrics/metrics.py
+++ b/.ci/metrics/metrics.py
@@ -30,7 +30,7 @@
 # This metric name is also used as a key in the job->name map.
 GITHUB_WORKFLOW_TO_TRACK = {
     "CI Checks": "github_llvm_premerge_checks",
-    "Build and Test libc++": "github_libcxx_premerge_checks",
+    "[libc++] Conformance tests": "github_libcxx_premerge_checks",
 }
 
 # Lists the Github jobs to track for a given workflow. The key is the stable
@@ -175,7 +175,7 @@ def create_and_append_libcxx_aggregates(workflow_metrics: list[JobMetrics]):
         if not isinstance(job, JobMetrics):
             continue
         # Only want libc++ jobs.
-        if job.workflow_name != "Build and Test libc++":
+        if job.workflow_name != "[libc++] Conformance tests":
             continue
         if job.workflow_id not in aggregate_data.keys():
             aggregate_data[job.workflow_id] = [job]
@@ -312,7 +312,7 @@ def github_get_metrics(
             continue
 
         libcxx_testing = False
-        if task.name == "Build and Test libc++":
+        if task.name == "[libc++] Conformance tests":
             libcxx_testing = True
 
         if task.status == "completed":

diff  --git a/.ci/metrics/metrics_test.py b/.ci/metrics/metrics_test.py
index 3004c367f96ff..727bdbfa87cda 100644
--- a/.ci/metrics/metrics_test.py
+++ b/.ci/metrics/metrics_test.py
@@ -100,7 +100,7 @@ def test_create_and_append_aggregate_metric_1_stage(self):
                 started_at_ns=1755697961000000000,
                 completed_at_ns=1755698349000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage1_test2",
@@ -111,7 +111,7 @@ def test_create_and_append_aggregate_metric_1_stage(self):
                 started_at_ns=1755698060000000000,
                 completed_at_ns=1755698417000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage1_test3",
@@ -122,7 +122,7 @@ def test_create_and_append_aggregate_metric_1_stage(self):
                 started_at_ns=1755697961000000000,
                 completed_at_ns=1755698785000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
         ]
         metrics.create_and_append_libcxx_aggregates(test_metrics)
@@ -150,7 +150,7 @@ def test_create_and_append_aggregate_metric_multiple_workflow_ids(self):
                 started_at_ns=1755697961000000000,
                 completed_at_ns=1755698349000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage1_test2",
@@ -161,7 +161,7 @@ def test_create_and_append_aggregate_metric_multiple_workflow_ids(self):
                 started_at_ns=1755698060000000000,
                 completed_at_ns=1755698417000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage1_test3",
@@ -172,7 +172,7 @@ def test_create_and_append_aggregate_metric_multiple_workflow_ids(self):
                 started_at_ns=1755697961000000000,
                 completed_at_ns=1755698785000000000,
                 workflow_id=25,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
         ]
         metrics.create_and_append_libcxx_aggregates(test_metrics)
@@ -211,7 +211,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755697053000000000,
                 completed_at_ns=1755698507000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage1_test2",
@@ -222,7 +222,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755697058000000000,
                 completed_at_ns=1755697885000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage2_test1",
@@ -233,7 +233,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755698513000000000,
                 completed_at_ns=1755699093000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage2_test2",
@@ -244,7 +244,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755698514000000000,
                 completed_at_ns=1755698987000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage2_test3",
@@ -255,7 +255,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755698514000000000,
                 completed_at_ns=1755699334000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage3_test1",
@@ -266,7 +266,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755709012000000000,
                 completed_at_ns=1755709931000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage3_test2",
@@ -277,7 +277,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755709146000000000,
                 completed_at_ns=1755709980000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "libcxx_stage3_test3",
@@ -288,7 +288,7 @@ def test_create_and_append_aggregate_metric_3_stages(self):
                 started_at_ns=1755709136000000000,
                 completed_at_ns=1755709514000000000,
                 workflow_id=17,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
         ]
         metrics.create_and_append_libcxx_aggregates(test_metrics)
@@ -341,7 +341,7 @@ def test_create_and_append_aggregate_metric_mixed_job_types(self):
                 started_at_ns=1755697961000000000,
                 completed_at_ns=1755698349000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "ci_test2", 3, 20, 1, 2000, 2200, 2400, 37, "premerge_test"
@@ -355,7 +355,7 @@ def test_create_and_append_aggregate_metric_mixed_job_types(self):
                 started_at_ns=1755698060000000000,
                 completed_at_ns=1755698417000000000,
                 workflow_id=3,
-                workflow_name="Build and Test libc++",
+                workflow_name="[libc++] Conformance tests",
             ),
             metrics.JobMetrics(
                 "ci_test3", 7, 35, 1, 3000, 3200, 3400, 85, "premerge_test"

diff  --git a/.github/workflows/libcxx-benchmark-commit.yml b/.github/workflows/libcxx-benchmark-commit.yml
index 683ff141d060e..90ade6c45cf21 100644
--- a/.github/workflows/libcxx-benchmark-commit.yml
+++ b/.github/workflows/libcxx-benchmark-commit.yml
@@ -2,7 +2,7 @@
 # This workflow is intended to be triggered manually or via the Github API. As such,
 # it requires several inputs that allow customizing its behavior.
 
-name: Benchmark libc++ commit
+name: "[libc++] Run benchmark suite against commit"
 
 permissions:
   contents: read

diff  --git a/.github/workflows/libcxx-build-containers.yml b/.github/workflows/libcxx-build-containers.yml
index 814de39a5e0a9..2789851da391b 100644
--- a/.github/workflows/libcxx-build-containers.yml
+++ b/.github/workflows/libcxx-build-containers.yml
@@ -5,7 +5,7 @@
 # and tagged appropriately. The selection of which Docker image version is used by the libc++
 # CI nodes at any given point is controlled from the workflow files themselves.
 
-name: Build Docker images for libc++ CI
+name: "[libc++] Build CI Docker images"
 
 permissions:
   contents: read

diff  --git a/.github/workflows/libcxx-pr-benchmark.yml b/.github/workflows/libcxx-pr-benchmark.yml
index 31bf6ffb224b6..92668a2f79f6c 100644
--- a/.github/workflows/libcxx-pr-benchmark.yml
+++ b/.github/workflows/libcxx-pr-benchmark.yml
@@ -7,7 +7,7 @@
 # That will cause the specified benchmarks to be run on the PR and on the pull-request target, and
 # their results to be compared.
 
-name: Benchmark libc++ PR
+name: "[libc++] Benchmark PR"
 
 permissions:
   contents: read

diff  --git a/.github/workflows/libcxx-check-generated-files.yml b/.github/workflows/libcxx-pr-check-generated-files.yml
similarity index 94%
rename from .github/workflows/libcxx-check-generated-files.yml
rename to .github/workflows/libcxx-pr-check-generated-files.yml
index d66d1da22f2ad..66f2735b08eab 100644
--- a/.github/workflows/libcxx-check-generated-files.yml
+++ b/.github/workflows/libcxx-pr-check-generated-files.yml
@@ -1,4 +1,4 @@
-name: "Check libc++ generated files"
+name: "[libc++] Check generated files"
 on:
   pull_request:
     paths:

diff  --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-pr-conformance-tests.yaml
similarity index 98%
rename from .github/workflows/libcxx-build-and-test.yaml
rename to .github/workflows/libcxx-pr-conformance-tests.yaml
index b95230b725a88..a7a93a110428c 100644
--- a/.github/workflows/libcxx-build-and-test.yaml
+++ b/.github/workflows/libcxx-pr-conformance-tests.yaml
@@ -1,4 +1,5 @@
-# This file defines pre-commit CI for libc++, libc++abi, and libunwind (on Github).
+# This file defines a workflow that runs conformance tests for libc++, libc++abi and libunwind when
+# a PR is created or updated.
 #
 # We split the configurations in multiple stages with the intent of saving compute time
 # when a job fails early in the pipeline. This is why the jobs are marked as `continue-on-error: false`.
@@ -12,7 +13,7 @@
 # under the assumption that if the "smoke tests" fail, then the other configurations will likely fail in the same way.
 # However, stage 3 does not fail fast, as it's more likely that any one job failing is a flake or a configuration-specific
 #
-name: Build and Test libc++
+name: "[libc++] Conformance tests"
 on:
   pull_request:
     paths:
@@ -21,7 +22,7 @@ on:
       - 'libunwind/**'
       - 'runtimes/**'
       - 'cmake/**'
-      - '.github/workflows/libcxx-build-and-test.yaml'
+      - '.github/workflows/libcxx-pr-conformance-tests.yaml'
   schedule:
     # Run nightly at 08:00 UTC (aka 00:00 Pacific, aka 03:00 Eastern)
     - cron: '0 8 * * *'

diff  --git a/README.md b/README.md
index a9b29ecbc1a3a..55425f05c9879 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 
 [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/llvm/llvm-project/badge)](https://securityscorecards.dev/viewer/?uri=github.com/llvm/llvm-project)
 [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8273/badge)](https://www.bestpractices.dev/projects/8273)
-[![libc++](https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml/badge.svg?branch=main&event=schedule)](https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml?query=event%3Aschedule)
+[![libc++](https://github.com/llvm/llvm-project/actions/workflows/libcxx-pr-conformance-tests.yaml/badge.svg?branch=main&event=schedule)](https://github.com/llvm/llvm-project/actions/workflows/libcxx-pr-conformance-tests.yaml?query=event%3Aschedule)
 
 Welcome to the LLVM project!
 

diff  --git a/libcxx/docs/Contributing.rst b/libcxx/docs/Contributing.rst
index 465bf9d0542c8..3c4d45dd17ce0 100644
--- a/libcxx/docs/Contributing.rst
+++ b/libcxx/docs/Contributing.rst
@@ -272,7 +272,7 @@ The libcxx linux premerge testing can run on one of three sets of runner
 groups. The three runner group names are ``llvm-premerge-libcxx-runners``,
 ``llvm-premerge-libcxx-release-runners`` and ``llvm-premerge-libcxx-next-runners``.
 The runner set currently in use is controlled by the contents of
-https://github.com/llvm/llvm-project/blob/main/.github/workflows/libcxx-build-and-test.yaml.
+https://github.com/llvm/llvm-project/blob/main/.github/workflows/libcxx-pr-conformance-tests.yaml.
 By default, it uses ``llvm-premerge-libcxx-runners``. To switch to one of the
 other runner sets, just replace all uses of ``llvm-premerge-libcxx-runners`` in
 the yaml file with the desired runner set.

diff  --git a/libcxx/docs/index.rst b/libcxx/docs/index.rst
index 13aa481b1f9b7..d6bbc771f0e6b 100644
--- a/libcxx/docs/index.rst
+++ b/libcxx/docs/index.rst
@@ -237,7 +237,7 @@ Design Documents
 Build Bots and Test Coverage
 ============================
 
-* `Github Actions CI pipeline <https://github.com/llvm/llvm-project/actions/workflows/libcxx-build-and-test.yaml>`_
+* `Github Actions CI pipeline <https://github.com/llvm/llvm-project/actions/workflows/libcxx-pr-conformance-tests.yaml>`_
 * `Buildkite CI pipeline <https://buildkite.com/llvm-project/libcxx-ci>`_
 * `LLVM Buildbot Builders <https://lab.llvm.org/buildbot>`_
 * :ref:`Adding New CI Jobs <AddingNewCIJobs>`


        

From libcxx-commits at lists.llvm.org  Fri Jul 24 11:56:26 2026
From: libcxx-commits at lists.llvm.org (Louis Dionne via libcxx-commits)
Date: Fri, 24 Jul 2026 11:56:26 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [llvm] [libc++] Rename pre-commit CI
	workflows (PR #211844)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211844@github.com>
Message-ID: <6a63b55a.d2cb4adc.23815e.f115@mx.google.com>


https://github.com/ldionne closed https://github.com/llvm/llvm-project/pull/211844

From libcxx-commits at lists.llvm.org  Fri Jul 24 21:35:03 2026
From: libcxx-commits at lists.llvm.org (Hristo Hristov via libcxx-commits)
Date: Fri, 24 Jul 2026 21:35:03 -0700 (PDT)
Subject: [libcxx-commits] [clang] [libcxx] [lldb] [clang]: implement
	std::meta::is_type (PR #211653)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211653@github.com>
Message-ID: <6a643cf7.1b0626d0.1ff4c3.f26d@mx.google.com>


================
@@ -0,0 +1,38 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_META
+#define _LIBCPP_META
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
----------------
Zingam wrote:

AFAIK. Typically contributors were asked to submit libc++ parts in separate PRs. Is there a reason not to do it?

https://github.com/llvm/llvm-project/pull/211653

From libcxx-commits at lists.llvm.org  Sat Jul 25 00:22:38 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 00:22:38 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2442R1
	`std::views::chunk` (PR #171234)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171234@github.com>
Message-ID: <6a64643e.8e75e0cb.2de827.d4f1@mx.google.com>


https://github.com/shyeyian updated https://github.com/llvm/llvm-project/pull/171234

>From f6554ea2a0e1d9571cf82fc7b2f3d1115c466474 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at petalmail.com>
Date: Wed, 24 Dec 2025 22:05:29 +0800
Subject: [PATCH 01/25] Implement `views::chunk`.

This commit squashes previous commits.
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/22.rst               |   1 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/chunk_view.h          | 562 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/ranges                         |  12 +
 libcxx/include/version                        |   2 +-
 libcxx/modules/std/ranges.inc                 |  11 +-
 .../no_unique_address.compile.pass.cpp        |  53 ++
 .../range.chunk/nodiscard.verify.cpp          |  86 +++
 .../ranges.version.compile.pass.cpp           |  32 +-
 .../version.version.compile.pass.cpp          |  32 +-
 .../range.chunk/adaptor.pass.cpp              |  75 +++
 .../range.adaptors/range.chunk/base.pass.cpp  |  46 ++
 .../range.adaptors/range.chunk/begin.pass.cpp |  67 +++
 .../range.chunk/ctad.compile.pass.cpp         |  55 ++
 .../range.adaptors/range.chunk/end.pass.cpp   |  65 ++
 .../range.chunk/general.pass.cpp              |  48 ++
 .../range.chunk.iter/compare.pass.cpp         | 109 ++++
 .../range.chunk.iter/decrement.pass.cpp       |  60 ++
 .../range.chunk.iter/deref.pass.cpp           |  77 +++
 .../range.chunk.iter/increment.pass.cpp       |  95 +++
 .../ranges/range.adaptors/range.chunk/types.h |  50 ++
 .../generate_feature_test_macro_components.py |   1 -
 25 files changed, 1494 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/chunk_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..8e57305f32ad1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -370,7 +370,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_rvalue``                             ``202207L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_chunk``                                 *unimplemented*
+    ``__cpp_lib_ranges_chunk``                                 ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                              ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0d1a1fbc00f2c..d2a2d1b712689 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,6 +54,7 @@ Implemented Papers
 - 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>`__)
+- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e4c6cb0f51065..8c8ab211e5bf4 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__","Only ``ranges::iota`` is implemented."
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__",""
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index eb423ae923b4f..2426da6848b51 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -707,6 +707,7 @@ set(files
   __ranges/all.h
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
+  __ranges/chunk_view.h
   __ranges/common_view.h
   __ranges/concepts.h
   __ranges/container_compatible_range.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
new file mode 100644
index 0000000000000..f19d2c9ede8a7
--- /dev/null
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_CHUNK_VIEW_H
+#define _LIBCPP___RANGES_CHUNK_VIEW_H
+
+#include <__algorithm/ranges_min.h>
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/take_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
+
+  class __outer_iterator;
+  class __inner_iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n), __remainder_(0) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires std::copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator begin() {
+    __current_.__emplace(ranges::begin(__base_));
+    __remainder_ = __n_;
+    return __outer_iterator(*this);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept {
+    return std::default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(chunk_view& __parent)
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  class value_type;
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator(__outer_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    return value_type(*__parent_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
+    __parent_->__remainder_ = __parent_->__n_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __outer_iterator& __i, default_sentinel_t) {
+    return *__i.__parent_->__current_ == ranges::end(__i.__parent_->__base_) && __i.__parent_->__remainder_ != 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __outer_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
+    if (__dist < __i.__parent_->__remainder_)
+      return __dist == 0 ? 0 : 1;
+    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __outer_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator::value_type : public view_interface<value_type> {
+  friend __outer_iterator;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(chunk_view& __parent) : __parent_(std::addressof(__parent)) {}
+
+public:
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator begin() const noexcept {
+    return __inner_iterator(*__parent_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return std::__to_unsigned_like(
+        ranges::min(__parent_->__remainder_, ranges::end(__parent_->__base_) - *__parent_->__current_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__inner_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(chunk_view& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+  using value_type       = range_value_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator(__inner_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator& operator=(__inner_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_View> base() const& { return *__parent_->__current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_reference_t<_View> operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
+    return **__parent_->__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    ++*__parent_->__current_;
+    if (*__parent_->__current_ == ranges::end(__parent_->__base_))
+      __parent_->__remainder_ = 0;
+    else
+      --__parent_->__remainder_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __inner_iterator& __i, default_sentinel_t) {
+    return __i.__parent_->__remainder_ == 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __inner_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return ranges::min(__i.__parent_->__remainder_, ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __inner_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
+    return ranges::iter_move(*__i.__parent_->__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void
+  iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) noexcept(
+      noexcept((ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_))))
+    requires indirectly_swappable<iterator_t<_View>>
+  {
+    return ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_);
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+class chunk_view<_View> : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+
+  template <bool _Const>
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires forward_range<const _View>
+  {
+    return __iterator<true>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    if constexpr (common_range<_View> && sized_range<_View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<false>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<_View> && !bidirectional_range<_View>)
+      return __iterator<false>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires forward_range<const _View>
+  {
+    if constexpr (common_range<const _View> && sized_range<const _View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<true>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<const _View> && !bidirectional_range<const _View>)
+      return __iterator<true>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+template <bool _Const>
+class chunk_view<_View>::__iterator {
+  friend chunk_view;
+
+  using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, chunk_view>;
+  using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
+
+  iterator_t<_Base> __current_         = iterator_t<_Base>();
+  sentinel_t<_Base> __end_             = sentinel_t<_Base>();
+  range_difference_t<_Base> __n_       = 0;
+  range_difference_t<_Base> __missing_ = 0;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      _Parent* __parent, iterator_t<_Base> __current, range_difference_t<_Base> __missing = 0)
+      : __current_(__current), __end_(ranges::end(__parent->__base_)), __n_(__parent->__n_), __missing_(__missing) {}
+
+  [[nodiscard]] static consteval auto __get_iterator_concept() {
+    if constexpr (random_access_range<_Base>)
+      return random_access_iterator_tag{};
+    else if constexpr (bidirectional_range<_Base>)
+      return bidirectional_iterator_tag{};
+    else
+      return forward_iterator_tag{};
+  }
+
+public:
+  using iterator_category = input_iterator_tag;
+  using iterator_concept  = decltype(__iterator::__get_iterator_concept());
+  using value_type        = decltype(views::take(subrange(__current_, __end_), __n_));
+  using difference_type   = range_difference_t<_Base>;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> &&
+                 convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+      : __current_(std::move(__i.__current_)),
+        __end_(std::move(__i.__end_)),
+        __n_(__i.__n_),
+        __missing_(__i.__missing_) {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() const { return __current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to dereference past-the-end chunk_view iterator");
+    return views::take(subrange(__current_, __end_), __n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator[](difference_type __pos) const
+    requires random_access_range<_Base>
+  {
+    return *(*this + __pos);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    __missing_ = ranges::advance(__current_, __n_, __end_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires bidirectional_range<_Base>
+  {
+    ranges::advance(__current_, __missing_ - __n_);
+    __missing_ = 0;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    if (__x > 0) {
+      _LIBCPP_ASSERT_PEDANTIC(ranges::distance(__current_, __end_) > __n_ * (__x - 1),
+                              "Trying to advance chunk_view iterator out of range");
+      ranges::advance(__current_, __n_ * (__x - 1));
+      __missing_ = ranges::advance(__current_, __n_, __end_);
+    } else if (__x < 0) {
+      ranges::advance(__current_, __n_ * __x + __missing_);
+      __missing_ = 0;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    return *this += -__x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
+    return __x.__current_ == __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, default_sentinel_t) {
+    return __x.__current_ == __x.__end_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __x.__current_ < __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __y < __x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__y < __x);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__x < __y);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(difference_type __pos, const __iterator& __i)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator-(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r -= __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, const __iterator& __j)
+    requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+  {
+    return (__i.__current_ - __j.__current_ + __i.__missing_ - __j.__missing_) / __i.__n_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <class _Range>
+chunk_view(_Range&&, range_difference_t<_Range>) -> chunk_view<views::all_t<_Range>>;
+
+template <class _View>
+inline constexpr bool enable_borrowed_range<chunk_view<_View>> = forward_range<_View> && enable_borrowed_range<_View>;
+
+namespace views {
+namespace __chunk {
+struct __fn {
+  template <viewable_range _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range, range_difference_t<_Range> __n) noexcept(
+      noexcept(/*-----*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))))
+      -> decltype(/*--*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))) {
+    return /*---------*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n));
+  }
+
+  template <class _DifferenceType>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_DifferenceType __n) noexcept(is_nothrow_constructible_v<decay_t<_DifferenceType>, _DifferenceType>) {
+    return __pipeable(std::__bind_back(__fn{}, std::forward<_DifferenceType>(__n)));
+  }
+};
+
+} // namespace __chunk
+
+inline namespace __cpo {
+inline constexpr auto chunk = __chunk::__fn{};
+
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_CHUNK_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4434a5446e35b..cb03eb8d14067 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1863,6 +1863,10 @@ module std [system] {
       header "__ranges/chunk_by_view.h"
       export std.functional.bind_back
     }
+    module chunk_view {
+      header "__ranges/chunk_view.h"
+      export std.functional.bind_back
+    }
     module common_view                    { header "__ranges/common_view.h" }
     module concepts                       { header "__ranges/concepts.h" }
     module container_compatible_range     { header "__ranges/container_compatible_range.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 9f725b12ac6c2..9ec6a4a819919 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -396,6 +396,17 @@ namespace std::ranges {
   class chunk_by_view;                                                      // C++23
 
   namespace views { inline constexpr unspecified chunk_by = unspecified; }  // C++23
+
+  // [range.chunk]
+  template <view V>
+    requires input_range<V>
+  class chunk_view;                                                     // C++23
+
+  template <view V>
+    requires forward_range<V>
+  class chunk_view<V>;                                                  // C++23
+
+  namespace views { inline constexpr unspecified chunk = unspecified; } // C++23
 }
 
 namespace std {
@@ -482,6 +493,7 @@ namespace std {
 #    include <__ranges/adjacent_view.h>
 #    include <__ranges/as_rvalue_view.h>
 #    include <__ranges/chunk_by_view.h>
+#    include <__ranges/chunk_view.h>
 #    include <__ranges/from_range.h>
 #    include <__ranges/join_with_view.h>
 #    include <__ranges/repeat_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index ab781466f5ed5..f5fc33674f521 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -523,7 +523,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges                               202406L
 // # define __cpp_lib_ranges_as_const                      202207L
 # define __cpp_lib_ranges_as_rvalue                     202207L
-// # define __cpp_lib_ranges_chunk                         202202L
+# define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
 # define __cpp_lib_ranges_contains                      202207L
 # define __cpp_lib_ranges_find_last                     202207L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 5caa2c052c00f..631ae10c922ae 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -314,18 +314,17 @@ export namespace std {
       using std::ranges::views::adjacent_transform;
       using std::ranges::views::pairwise_transform;
     } // namespace views
+#endif
 
-#endif // _LIBCPP_STD_VER >= 23
-
-#if 0
+#if _LIBCPP_STD_VER >= 23
     using std::ranges::chunk_view;
 
-    using std::ranges::chunk_view<V>;
-
     namespace views {
       using std::ranges::views::chunk;
-    }
+    } // namespace views
+#endif
 
+#if 0
     using std::ranges::slide_view;
 
     namespace views {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
new file mode 100644
index 0000000000000..14c618c505e70
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// This test ensures that we use `[[no_unique_address]]` in `chunk_view`.
+
+#include <cstddef>
+#include <ranges>
+#include <string>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct input_view {
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<input_view> = true;
+static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
+using CV1 = std::ranges::chunk_view<input_view>;
+// Expected CV1 (with View == input) layout:
+// [[no_unique_address]] _View __base_                                         // size: 0
+// [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] __non_propagating_cache<iterator_t<_View>> __current_ // size: sizeof(__non_propagating_cache<cpp20_input_iterator<int*>>), align: std::ptrdiff_t
+static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>) == alignof(std::ptrdiff_t));
+static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
+                                 sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
+
+using CV2 = std::ranges::chunk_view<forward_view>;
+// Expected CV2 (with View >= forward) layout:
+// [[no_unique_address]] _View __base_             // size: 0
+// [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
+static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..5e557715f4b03
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
+  auto view     = range | std::views::chunk(2);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(std::as_const(view)).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(view).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(view.begin());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(std::as_const(view).begin());
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 5116864879485..41e1603f62350 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -270,17 +270,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -387,17 +381,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
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 b3b424a1d77ce..bd1b55ccc8dfa 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
@@ -5700,17 +5700,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -7619,17 +7613,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
new file mode 100644
index 0000000000000..657e16383256b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   std::views::chunk
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                       = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::ref_view<std::array<int, 8>> view = array | std::views::all;
+
+  // Test `views::chunk(view, n)`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        std::views::chunk(view, 2);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::views::chunk(std::as_const(view), 2);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2}));
+  }
+
+  // Test `views::chunk(n)(range)`
+  {
+    static_assert(noexcept(std::views::chunk(2)));
+    /*__pipable*/ auto adaptor = std::views::chunk(3);
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        adaptor(view);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        adaptor(std::as_const(view));
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3}));
+  }
+
+  // Test `view | views::chunk`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        view | std::views::chunk(4);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3, 4}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::as_const(view) | std::views::chunk(4);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3, 4}));
+  }
+
+  // Test `views::chunk | adaptor`
+  {
+    /*__pipable*/ auto adaptors            = std::views::chunk(5) | std::views::join;
+    std::ranges::input_range auto rejoined = view | adaptors;
+    assert(std::ranges::equal(rejoined, view));
+    std::ranges::input_range auto const_rejoined = std::as_const(view) | adaptors;
+    assert(std::ranges::equal(const_rejoined, view));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
new file mode 100644
index 0000000000000..ac17f97a0a292
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() &&;
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <memory>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                                                   = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>> chunked = array | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
+      std::as_const(array) | std::views::chunk(4);
+
+  // Test `chunk_view.base()`
+  {
+    std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
+    assert(std::addressof(base) == std::addressof(array));
+
+    std::same_as<const std::array<int, 8>&> decltype(auto) const_base = const_chunked.base().base();
+    assert(std::addressof(const_base) == std::addressof(array));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
new file mode 100644
index 0000000000000..3cccd7d1bebc6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr __outer_iterator begin();
+
+//   V models forward_range:
+//     constexpr auto begin() requires (!__simple_view<V>);
+//     constexpr auto begin() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.begin()` when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == input_chunked.end());
+  }
+
+  // Test `chunk_view.begin()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == chunked.end());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.begin();
+    assert(std::ranges::equal(*const_it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++const_it, std::vector{7, 8}));
+    assert(++const_it == const_chunked.end());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..9bf2baba58c78
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class R>
+//   chunk_view(R&&, range_difference_t<R>) -> chunk_view<all_t<R>>;
+
+#include <ranges>
+
+struct view : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct range {
+  int* begin() const;
+  int* end() const;
+};
+
+struct borrowed_range {
+  int* begin() const;
+  int* end() const;
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<borrowed_range> = true;
+
+void test_ctad() {
+  view v;
+  range r;
+  borrowed_range br;
+
+  // clang-format off
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(v, 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(v), 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(r, 0)), 
+                                      std::ranges::chunk_view<std::ranges::ref_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(r), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(br, 0)),
+                                      std::ranges::chunk_view<std::ranges::ref_view<borrowed_range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(br), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<borrowed_range>>>);
+  // clang-format on
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
new file mode 100644
index 0000000000000..73eb0d967f6a2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr default_sentinel_t end();
+
+//   V moduels forward_range:
+//     constexpr auto end() requires (!__simple_view<V>);
+//     constexpr auto end() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.end()` when V models only input_range
+  {
+    static_assert(noexcept(input_chunked.end()));
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it = input_chunked.end();
+  }
+
+  // Test `chunk_view.end()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--it, std::vector{1, 2, 3}));
+    assert(it == chunked.begin());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.end();
+    assert(std::ranges::equal(*--const_it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--const_it, std::vector{1, 2, 3}));
+    assert(const_it == const_chunked.begin());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
new file mode 100644
index 0000000000000..89891bc093080
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   General tests for chunk_view. This file does not test anything specifically.
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <string_view>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::string_view str = "Cheese with chicken chunk by chunk on truck with my trick";
+  // clang-format off
+  auto str2 = str 
+            | std::views::chunk(4)
+            | std::views::join
+            | std::views::chunk(314159)
+            | std::views::take(1)
+            | std::views::join
+            | std::views::lazy_split(' ')
+            | std::views::chunk(2)
+            | std::views::transform([] (auto&& subview)
+              {
+                return subview | std::views::join_with(' ');
+              })
+            | std::views::join_with(' ');
+  // clang-format on
+  assert(std::ranges::equal(str, str2));
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
new file mode 100644
index 0000000000000..d86726d958e6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator<(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator<=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base> &&
+//                three_way_comparable<iterator_t<Base>>;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t)`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    std::ranges::advance(it, 4);
+    assert(it == std::default_sentinel);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(input_chunked.end() - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - input_chunked.end() == -4);
+  }
+
+  // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() == chunked.begin());
+    assert(chunked.end() == chunked.end());
+  }
+
+  // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() < chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() > chunked.begin());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() <= chunked.begin());
+    assert(chunked.begin() <= chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() >= chunked.end());
+    assert(chunked.end() >= chunked.begin());
+  }
+
+  // Test `friend constexpr auto operator<=>(const iterator& x, const iterator& y)`
+  {
+    assert((chunked.begin() <=> chunked.begin()) == std::strong_ordering::equal);
+    assert((chunked.begin() <=> chunked.end()) == std::strong_ordering::less);
+    assert((chunked.end() <=> chunked.begin()) == std::strong_ordering::greater);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
new file mode 100644
index 0000000000000..397b32e73080a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+//   constexpr iterator& operator--()
+//     requires bidirectional_range<Base>;
+//   constexpr iterator operator--(int)
+//     requires bidirectional_range<Base>;
+//   constexpr iterator& operator-=(difference_type)
+//     requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+
+  // Test `constexpr iterator& operator--();`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator operator--(int)`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    it--;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator& operator-=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.end();
+    it -= 3;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
new file mode 100644
index 0000000000000..b8772d0fac3d2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr value_type outer_iterator::operator*() const;
+//     constexpr inner_iterator outer_iterator::value_type::begin() const noexcept;
+//     constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept;
+//     constexpr range_reference_v<V> inner_iterator::operator*() const;
+
+//   V models forward_range:
+//     constexpr value_type iterator::operator*() const;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `constexpr value_type outer_iterator::operator*() const`
+  {
+    static_assert(std::ranges::input_range<decltype(*input_chunked.begin())>);
+  }
+
+  // Test `constexpr inner_iterator outer_iterator::value_type::begin() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    assert(*inner.begin() == *vector.begin());
+    static_assert(noexcept(inner.begin()));
+  }
+
+  // Test `constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it                   = inner.end();
+    static_assert(noexcept((inner.end())));
+  }
+
+  // Test `constexpr value_type iterator::operator*() const`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::same_as<int> decltype(auto) v                           = *it;
+    assert(v == 1);
+  }
+
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
+  {
+    std::same_as<int&> decltype(auto) v = *(*chunked.begin()).begin();
+    assert(v == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
new file mode 100644
index 0000000000000..6048aba027ce9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     constexpr outer_iterator& operator++();
+//     constexpr void operator++(int);
+//     constexpr inner_iterator& operator++();
+//     constexpr void operator++(int);
+
+//   V models forward_range
+//     constexpr iterator& operator++();
+//     constexpr iterator operator++(int);
+//     constexpr iterator& operator+=(difference_type)
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(2);
+
+  // Test `constexpr outer_iterator& operator++();`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr void operator++(int);`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    assert(*++it == 2);
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(*it == 2);
+  }
+
+  // Test `constexpr iterator& operator++();`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator operator++(int)`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator& operator+=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    it += 3;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..09d16c9a48af0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f61d6f991cb15..49fb196903e77 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1106,7 +1106,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_ranges_chunk",
             "values": {"c++23": 202202},
             "headers": ["ranges"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ranges_chunk_by",

>From af914e794ddb86103e6b7acf3bf17d40f5055a8a Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 01:44:18 +0800
Subject: [PATCH 02/25] Code Review: adjust release notes

Note that we will merge this commit with the corresponding change in #172948 in the future.
---
 libcxx/docs/ReleaseNotes/22.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index d2a2d1b712689..30fc22fc0d787 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,7 +54,7 @@ Implemented Papers
 - 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>`__)
-- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------

>From dc62f798ba5615dcac60fffa2cc0afa8b79344c5 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 13:03:41 +0800
Subject: [PATCH 03/25] Add Tests: test if `_LIBCPP_ASSERT_XXX` works when
 `views::chunk` meets an undefined behaviour

---
 libcxx/include/__ranges/chunk_view.h          |  6 ++-
 .../range.chunk/assert.constructor.pass.cpp   | 39 ++++++++++++++
 .../range.chunk.iter/assert.advance.pass.cpp  | 32 ++++++++++++
 .../range.chunk.iter/assert.deref.pass.cpp    | 52 +++++++++++++++++++
 .../assert.increment.pass.cpp                 | 52 +++++++++++++++++++
 .../ranges/range.adaptors/range.chunk/types.h | 50 ++++++++++++++++++
 6 files changed, 229 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f19d2c9ede8a7..683b6bbaa2ca5 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -128,11 +128,12 @@ class chunk_view<_View>::__outer_iterator {
   _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
-    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
     return value_type(*__parent_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
     __parent_->__remainder_ = __parent_->__n_;
     return *this;
@@ -214,6 +215,7 @@ class chunk_view<_View>::__inner_iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ++*__parent_->__current_;
     if (*__parent_->__current_ == ranges::end(__parent_->__base_))
       __parent_->__remainder_ = 0;
@@ -386,7 +388,7 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
-    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to increment past-the-end chunk_view iterator");
     __missing_ = ranges::advance(__current_, __n_, __end_);
     return *this;
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
new file mode 100644
index 0000000000000..03fc7dfb6a5de
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Construct a chunk_view with chunk size <= 0
+
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3};
+
+  // Test constructor when V models only input_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  // Test constructor when V models forward_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
new file mode 100644
index 0000000000000..af2b826aa8eba
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to advance chunk_view iterator out of range
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+
+  // Test advance past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    TEST_LIBCPP_ASSERT_FAILURE(it += 4, "Trying to advance chunk_view iterator out of range");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
new file mode 100644
index 0000000000000..cb1d92885e684
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to dereference past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test dereference iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*outer_it, "Trying to dereference past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*inner_it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+
+  // Test dereference iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(*it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
new file mode 100644
index 0000000000000..8160ee1bb3e14
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to increment past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test increment past-the-end iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++outer_it, "Trying to increment past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++inner_it, "Trying to increment past-the-end chunk_view iterator");
+  }
+
+  // Test increment past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(++it, "Trying to increment past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..a5d688abfa1b0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif

>From b664c0cb8f8ed9b46629fa49e4bdd846734615d4 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:26 +0800
Subject: [PATCH 04/25] Fix CI: adjust `#include` path in `test/libcxx` files

---
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp          | 2 +-
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index cb1d92885e684..0c96742147189 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 8160ee1bb3e14..451683bab71b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};

>From 4dc9b06c0dd515030226b9d27841352ef62549a3 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:58 +0800
Subject: [PATCH 05/25] Fix CI: add template deduction guide for
 `input_span<T>`

---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 3 +++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..0978a23c37244 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..4ff1af1d7cda3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 1e8906ad49a2406158161cc1b9d0e1570af2959d Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:53:21 +0800
Subject: [PATCH 06/25] Code Review: add `REQUIRES: std-at-least-c++23` in all
 the tests related to `views::chunk`.

The previous commit, 4dc9b06c0dd515030226b9d27841352ef62549a3, which introduce manual/explicit template deduction guide in `input_span<T>`, is always implicitly-provided under -std=c++23. So this commit also reverts the changes about `types.h` in the previous commit.
---
 .../range.adaptors/range.chunk/assert.constructor.pass.cpp     | 1 +
 .../range.chunk/range.chunk.iter/assert.advance.pass.cpp       | 1 +
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp         | 1 +
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp     | 1 +
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h   | 3 ---
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h      | 3 ---
 6 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
index 03fc7dfb6a5de..477f89be5f070 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
index af2b826aa8eba..9b91d94841b91 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index 0c96742147189..3640b04192bf4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 451683bab71b0..61c096f74158e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index 0978a23c37244..a5d688abfa1b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 4ff1af1d7cda3..09d16c9a48af0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 7e21388c70b38c32125e4d3eda9b3a4c66e1d3db Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 14:13:59 +0800
Subject: [PATCH 07/25] Fix CI: add template deduction guide for
 `input_span<T>` again.

Seems that even in C++23 the CI fails due to no implicit deduction guide, here we just add the explicit one and make the CI to basically run ok.
---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 4 ++++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..4852ec29204bb 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..7533e41713d72 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From f1ed40a1aed2a1f5ebd1e56efcd6ae2bbb25dc26 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:07:21 +0800
Subject: [PATCH 08/25] Code review: Update libcxx/test synopsis

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 .../ranges/range.adaptors/range.chunk/nodiscard.verify.cpp      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 5e557715f4b03..9264256fc6a52 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+// Test that functions are marked [[nodiscard]].
 
 #include <ranges>
 #include <utility>

>From 12bec119ba49cd6016055f5887f5e83efa25ee30 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:21:29 +0800
Subject: [PATCH 09/25] Code review: Fix synopsis.

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index ac17f97a0a292..b45e1f59f1394 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() const& requires copy_constructible<V>;
 //   constexpr V base() &&;
 
 #include <array>

>From 1e05c5a038dd4cee5a0049b8c7e92e59c8aabd82 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 21:13:46 +0800
Subject: [PATCH 10/25] Code review: sort and unique the tests

---
 .../no_unique_address.compile.pass.cpp        |  24 ++--
 .../range.chunk/nodiscard.verify.cpp          |  35 ++----
 .../range.chunk.iter/arithmetic.pass.cpp      | 108 ++++++++++++++++++
 .../range.chunk.iter/compare.pass.cpp         |  29 +++--
 .../range.chunk.iter/subscript.pass.cpp       |  42 +++++++
 5 files changed, 189 insertions(+), 49 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
index 14c618c505e70..5d32daf4c178a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -20,6 +20,8 @@
 #include "test_iterators.h"
 #include "test_range.h"
 
+// When V models only input_range
+
 struct input_view {
   cpp20_input_iterator<int*> begin() const;
   sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
@@ -28,16 +30,8 @@ template <>
 inline constexpr bool std::ranges::enable_view<input_view> = true;
 static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
 
-struct forward_view {
-  int* begin() const;
-  int* end() const;
-};
-template <>
-inline constexpr bool std::ranges::enable_view<forward_view> = true;
-static_assert(std::ranges::forward_range<forward_view>);
-
 using CV1 = std::ranges::chunk_view<input_view>;
-// Expected CV1 (with View == input) layout:
+// Expected CV1 (when V models only input_range) layout:
 // [[no_unique_address]] _View __base_                                         // size: 0
 // [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
 // [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
@@ -46,8 +40,18 @@ static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<
 static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
                                  sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
 
+// When V models forward_range
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
 using CV2 = std::ranges::chunk_view<forward_view>;
-// Expected CV2 (with View >= forward) layout:
+// Expected CV2 (when V models forward_range) layout:
 // [[no_unique_address]] _View __base_             // size: 0
 // [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
 static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 9264256fc6a52..4d0fb4f76857c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -19,15 +19,6 @@ void test() {
   char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
   auto view     = range | std::views::chunk(2);
 
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   view.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -43,18 +34,19 @@ void test() {
   std::as_const(view).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  view.end();
+  std::ranges::iter_move(view.begin());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(view).end();
+  std::ranges::iter_move(std::as_const(view).begin());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
+  view.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
+  std::as_const(view).end();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
+  std::ranges::iter_move(view.end());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
+  std::ranges::iter_move(std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   *view.begin();
@@ -71,16 +63,11 @@ void test() {
   (std::as_const(view).begin() == std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == std::as_const(view).end());
+  std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == std::as_const(view).end());
-
+  std::views::chunk(range, 3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(view.begin());
+  range | std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(std::as_const(view).begin());
+  std::views::reverse | std::views::chunk(3);
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..6baa2fce13695
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const inner_iterator& x)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr iterator operator+(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator+(difference_type n, const iterator& i)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator-(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+//       requires sized_sentinel_for<iterator_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(std::default_sentinel - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - std::default_sentinel == -4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t, const inner_iterator& x)`
+  {
+    assert(std::default_sentinel - (*input_chunked.begin()).begin() == 3);
+  }
+
+  // Test `friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t)`
+  {
+    assert((*input_chunked.begin()).begin() - std::default_sentinel == -3);
+  }
+
+  // Test `friend constexpr iterator operator+(const iterator& i, difference_type n)`
+  {
+    assert(chunked.begin() + 4 == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator+(difference_type n, const iterator& i)`
+  {
+    assert(4 + chunked.begin() == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator-(const iterator& i, difference_type n)`
+  {
+    assert(chunked.end() - 4 == chunked.begin());
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)`
+  {
+    assert(std::default_sentinel - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)`
+  {
+    assert(chunked.begin() - std::default_sentinel == -4);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
index d86726d958e6e..ea6731cccce85 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -11,14 +11,12 @@
 // <ranges>
 
 //   V models only input_range
-//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
-//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
-//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t);
 
 //   V models forward_range
-//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator==(const iterator& x, const iterator& y);
+//     friend constexpr bool operator==(const iterator& x, default_sentinel_t);
 //     friend constexpr bool operator<(const iterator& x, const iterator& y)
 //       requires random_access_range<Base>;
 //     friend constexpr bool operator>(const iterator& x, const iterator& y)
@@ -31,7 +29,6 @@
 //       requires random_access_range<Base> &&
 //                three_way_comparable<iterator_t<Base>>;
 
-#include <algorithm>
 #include <cassert>
 #include <compare>
 #include <iterator>
@@ -53,14 +50,11 @@ constexpr bool test() {
     assert(it == std::default_sentinel);
   }
 
-  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  // Test `friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t)`
   {
-    assert(input_chunked.end() - input_chunked.begin() == 4);
-  }
-
-  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
-  {
-    assert(input_chunked.begin() - input_chunked.end() == -4);
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::ranges::advance(it, 3);
+    assert(it == std::default_sentinel);
   }
 
   // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
@@ -69,6 +63,11 @@ constexpr bool test() {
     assert(chunked.end() == chunked.end());
   }
 
+  // Test `friend constexpr bool operator==(const iterator& x, default_sentinel)`
+  {
+    assert(chunked.end() == std::default_sentinel);
+  }
+
   // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() < chunked.end());
@@ -79,7 +78,7 @@ constexpr bool test() {
     assert(chunked.end() > chunked.begin());
   }
 
-  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  // Test `friend constexpr bool operator<=(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() <= chunked.begin());
     assert(chunked.begin() <= chunked.end());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
new file mode 100644
index 0000000000000..9b45fc6d779bf
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models forward_range
+//     constexpr value_type iterator::operator[](difference_type n) const
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+
+  // Test `constexpr value_type iterator::operator[](difference_type n) const`
+  {
+    assert(std::ranges::equal(chunked.begin()[1], std::vector{4, 5, 6}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 033a1c16289990a16b1f674f380f7a72f38f48ea Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 14:30:08 -0400
Subject: [PATCH 11/25] Fix release notes

---
 libcxx/docs/ReleaseNotes/22.rst    | 1 -
 libcxx/docs/ReleaseNotes/23.rst    | 1 +
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0637ba98976a2..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -55,7 +55,6 @@ Implemented Papers
 - 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>`__)
-- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e7eb42207db42..a98f42da70f00 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -52,6 +52,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::chunk`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 6abcac5e4ebca..8c92d89be999b 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""

>From 7fe126d5e7c88787f26228c64bcb5f89977c5b83 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:02:10 +0800
Subject: [PATCH 12/25] Resolve overload conflicts on `__div_ceil` in
 `ranges::chunk_view` and `ranges::stride_view`.

Both `ranges::chunk_view` and `ranges::stride_view` need a helper function called `__div_ceil` that solves some simple maths. According to the style of other libcxx internal helper functions (e.g. `__priority_tag`, `__small_buffer`), it seems that we can put `__div_ceil` into a new file `__utility/div_ceil.h`. However, most files in `__utility/` refer to a corresponding symbol in `<utility>` header, so that if someone feels this is not proper, I'll be glad to revert it.
---
 libcxx/include/CMakeLists.txt         |  1 +
 libcxx/include/__ranges/chunk_view.h  | 21 ++++++-----------
 libcxx/include/__ranges/stride_view.h | 20 +++++-----------
 libcxx/include/__utility/div_ceil.h   | 33 +++++++++++++++++++++++++++
 libcxx/include/module.modulemap.in    |  1 +
 5 files changed, 48 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/include/__utility/div_ceil.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 76b93c17c3312..0e9f19a81b1ab 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -935,6 +935,7 @@ set(files
   __utility/convert_to_integral.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
+  __utility/div_ceil.h
   __utility/element_count.h
   __utility/empty.h
   __utility/exception_guard.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 683b6bbaa2ca5..68d4408295e47 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -36,6 +36,7 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 
@@ -52,14 +53,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Integral>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
-  _Integral __r = __num / __denom;
-  if (__num % __denom)
-    ++__r;
-  return __r;
-}
-
 template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
@@ -98,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -153,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -321,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -510,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..7ea68b13330c3 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -46,15 +47,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Value>
-_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) {
-  _Value __r = __left / __right;
-  if (__left % __right) {
-    ++__r;
-  }
-  return __r;
-}
-
 template <input_range _View>
   requires view<_View>
 class stride_view : public view_interface<stride_view<_View>> {
@@ -121,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -340,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -ranges::__div_ceil(-__n, __x.__stride_);
+      return -__div_ceil(-__n, __x.__stride_);
     }
-    return ranges::__div_ceil(__n, __x.__stride_);
+    return __div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)
diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
new file mode 100644
index 0000000000000..595c590b9e3f6
--- /dev/null
+++ b/libcxx/include/__utility/div_ceil.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_DIV_CEIL_H
+#define _LIBCPP___UTILITY_DIV_CEIL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __div_ceil computes the ceiling of the division of two integers. It is mainly used by
+// range adaptors like chunk_view and stride_view.
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_DIV_CEIL_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 64238440b9dd7..ef5d15d063f69 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2200,6 +2200,7 @@ module std [system] {
     module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
+    module div_ceil                        { header "__utility/div_ceil.h" }
     module element_count                   { header "__utility/element_count.h" }
     module exception_guard                 { header "__utility/exception_guard.h" }
     module exchange                        { header "__utility/exchange.h" }

>From d27b355e67e17b0ed15d44a247fdba1c3b13ed49 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:22:38 +0800
Subject: [PATCH 13/25] Fix test error that `constexpr` is not supported in
 `-std=c++03`

---
 libcxx/include/__utility/div_ceil.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 595c590b9e3f6..4d2868465c141 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,6 +17,8 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if _LIBCPP_STD_VER >= 11
+
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
 
@@ -28,6 +30,8 @@ template <class _Integral>
   return __r;
 }
 
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___UTILITY_DIV_CEIL_H

>From a1a5918bb79d29cbe7be2736da7a72b83ee0afbb Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:38:37 +0800
Subject: [PATCH 14/25] Fix test error again that `constexpr` is not supported
 in `-std=c++11`

---
 libcxx/include/__utility/div_ceil.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 4d2868465c141..21e9bcb30d1a1 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,7 +17,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 11
+#if _LIBCPP_STD_VER >= 23
 
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
@@ -30,7 +30,7 @@ template <class _Integral>
   return __r;
 }
 
-#endif
+#endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_END_NAMESPACE_STD
 

>From c2e0e2e926c92ee6d001e060aa5660b681874dfd Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:45:41 +0800
Subject: [PATCH 15/25] Fix test error that `std::as_const` is included from
 `<utility>`.

---
 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp | 1 +
 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
index 3cccd7d1bebc6..29588a57fbcab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -22,6 +22,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
index 73eb0d967f6a2..2f37ac5f73524 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -23,6 +23,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"

>From 7d3cdd73f5da77a7499969fb7954312a19e26630 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 14:19:19 +0800
Subject: [PATCH 16/25] Avoid ADL in `std::__div_ceil`.

---
 libcxx/include/__ranges/chunk_view.h  | 12 ++++++------
 libcxx/include/__ranges/stride_view.h | 10 +++++-----
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 68d4408295e47..1107a9238b2bc 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -91,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -146,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return std::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -314,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -503,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return std::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 7ea68b13330c3..a8b485cbe33e5 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -113,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -332,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -__div_ceil(-__n, __x.__stride_);
+      return -std::__div_ceil(-__n, __x.__stride_);
     }
-    return __div_ceil(__n, __x.__stride_);
+    return std::__div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return std::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)

>From 116c6d85d8a9ad177b1df1975eea92479cb03664 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 16:39:58 +0800
Subject: [PATCH 17/25] [Important] Add missing concepts requirements.

---
 libcxx/include/__ranges/chunk_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1107a9238b2bc..47e6bd56415b6 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -400,7 +400,8 @@ class chunk_view<_View>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires bidirectional_range<_Base> {
     auto __tmp = *this;
     --*this;
     return __tmp;

>From 545bbdf10d24c3790790bfe669b8c756da719838 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Fri, 26 Jun 2026 22:35:12 +0800
Subject: [PATCH 18/25] Cover tests on `chunk_range`. (I'll continue to cover
 tests on `__iterator`, `__inner_iterator` and `__outer_iterator` at the
 weekend)

---
 libcxx/include/__ranges/chunk_view.h          |  3 +-
 .../range.adaptors/range.chunk/base.pass.cpp  | 12 +++-
 .../range.chunk/borrowing.compile.pass.cpp    | 27 +++++++
 .../range.adaptors/range.chunk/ctor.pass.cpp  | 54 ++++++++++++++
 .../range.adaptors/range.chunk/size.pass.cpp  | 72 +++++++++++++++++++
 5 files changed, 166 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 47e6bd56415b6..1798711eabd16 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -401,7 +401,8 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
-    requires bidirectional_range<_Base> {
+    requires bidirectional_range<_Base>
+  {
     auto __tmp = *this;
     --*this;
     return __tmp;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index b45e1f59f1394..4dca324a23695 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -26,7 +26,7 @@ constexpr bool test() {
   std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
       std::as_const(array) | std::views::chunk(4);
 
-  // Test `chunk_view.base()`
+  // Test `chunk_view.base() const&`
   {
     std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
     assert(std::addressof(base) == std::addressof(array));
@@ -35,6 +35,16 @@ constexpr bool test() {
     assert(std::addressof(const_base) == std::addressof(array));
   }
 
+  // Test `chunk_view.base() &&`
+  {
+    std::same_as<std::ranges::ref_view<std::array<int, 8>>> decltype(auto) moved = std::move(chunked).base();
+    assert(std::addressof(moved.base()) == std::addressof(array));
+
+    std::same_as<std::ranges::ref_view<const std::array<int, 8>>> decltype(auto) const_moved =
+        std::move(const_chunked).base();
+    assert(std::addressof(const_moved.base()) == std::addressof(array));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
new file mode 100644
index 0000000000000..1eb4a34f87217
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class V>
+//   inline constexpr bool enable_borrowed_range<chunk_view<V>> =
+//       forward_range<V> && enable_borrowed_range<V>;
+
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+// When V models only `input_range`.
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<input_span<int>>>);
+
+// When V models at least `forward_range`.
+static_assert(std::ranges::enable_borrowed_range<std::ranges::chunk_view<BorrowedView>>);
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
new file mode 100644
index 0000000000000..b60e040b807eb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.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++23
+
+// <ranges>
+
+//   constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n);
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_convertible.h"
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models only `input_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>, input_span<int>, std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(input_span(vector.data(), 8), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+                                    std::ranges::ref_view<std::vector<int>>,
+                                    std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
new file mode 100644
index 0000000000000..820774c3b750e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr auto size() requires sized_range<_View>;
+//   constexpr auto size() const requires sized_range<const _View>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+  // Test `chunk_view.size()` when V models only `input_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<input_span<int>>>);
+    static_assert(!std::ranges::sized_range<const std::ranges::chunk_view<input_span<int>>>);
+
+    auto chunked = input_span<int>(arr, 12) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when V models `forward_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+    static_assert(std::ranges::sized_range<const std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+    const auto& const_chunked = chunked;
+    assert(const_chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when the range is not fully divisible
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(5);
+    assert(chunked.size() == 3);
+  }
+
+  // Test `chunk_view.size()` when the range is empty
+  {
+    static_assert(std::ranges::sized_range<std::ranges::empty_view<int>>);
+    auto chunked = std::views::empty<int> | std::views::chunk(3);
+    assert(chunked.size() == 0);
+  }
+
+  // Test `chunk_view.size()` when chunk size is larger than the range
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(100);
+    assert(chunked.size() == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From e262f9e7497fb3ccf232a3c7265b8faa83f2fa20 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:36:04 +0800
Subject: [PATCH 19/25] Fix chunk_view ctor test for forward ranges.

---
 .../test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
index b60e040b807eb..c0d210aa45dc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
@@ -35,11 +35,11 @@ constexpr bool test() {
 
   // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
   {
-    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+    static_assert(!test_convertible<std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>>,
                                     std::ranges::ref_view<std::vector<int>>,
                                     std::ptrdiff_t>());
 
-    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked(std::ranges::ref_view(vector), 3);
     assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
   }
 

>From bf13c55de976c562120249f2fc7208ea1091fc95 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:39:36 +0800
Subject: [PATCH 20/25] Use consistent copy_constructible spelling in
 chunk_view

---
 libcxx/include/__ranges/chunk_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1798711eabd16..f3edc617de07a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -71,7 +71,7 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
-    requires std::copy_constructible<_View>
+    requires copy_constructible<_View>
   {
     return __base_;
   }

>From f457f267b72fe64a2f22c288da4b096ae0bcc83c Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:48:59 +0800
Subject: [PATCH 21/25] Remove unnecessary no_unique_address from chunk_view
 counts

---
 libcxx/include/__ranges/chunk_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f3edc617de07a..4478af951dbca 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -57,8 +57,8 @@ template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  range_difference_t<_View> __n_;
+  range_difference_t<_View> __remainder_;
   _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
 
   class __outer_iterator;
@@ -256,7 +256,7 @@ template <view _View>
   requires forward_range<_View>
 class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  range_difference_t<_View> __n_;
 
   template <bool _Const>
   class __iterator;

>From 39c371e116736dd37006c3cf48ed9cf9412b1222 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 23:25:01 +0800
Subject: [PATCH 22/25] Fix chunk_view inner iter_move return type (from T into
 T&&)

---
 libcxx/include/__ranges/chunk_view.h          |  2 +-
 .../range.chunk.inner.iter/iter_move.pass.cpp | 43 +++++++++++++++++++
 2 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 4478af951dbca..e9511f027160a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -238,7 +238,7 @@ class chunk_view<_View>::__inner_iterator {
     return -(__s - __i);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_View>
   iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
     return ranges::iter_move(*__i.__parent_->__current_);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp
new file mode 100644
index 0000000000000..5c7b620924c2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   friend constexpr range_rvalue_reference_t<V> iter_move(const inner_iterator& i)
+//     noexcept(noexcept(ranges::iter_move(i.parent_->current_.value())));
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+constexpr bool test() {
+  using InnerIterator =
+      std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<test_view<cpp20_input_iterator>>>>;
+
+  static_assert(std::ranges::input_range<test_view<cpp20_input_iterator>>);
+  static_assert(!std::ranges::forward_range<test_view<cpp20_input_iterator>>);
+
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())), int&&>);
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())),
+                             std::ranges::range_rvalue_reference_t<test_view<cpp20_input_iterator>>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file

>From c1a0811d8824267ff40fa442b2c2ad10d95b7690 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Mon, 20 Jul 2026 00:13:15 +0800
Subject: [PATCH 23/25] Add chunk_view constraint tests

---
 .../range.chunk/concept.compile.pass.cpp      | 64 +++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp
new file mode 100644
index 0000000000000..61b89d4c09c32
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <view V>
+//     requires input_range<V>
+//   class chunk_view;
+//
+//   template <view V>
+//     requires forward_range<V>
+//   class chunk_view<_View>;
+
+#include <ranges>
+#include <utility>
+
+#include "almost_satisfies_types.h"
+#include "test_range.h"
+#include "types.h"
+
+template <>
+inline constexpr bool std::ranges::enable_view<InputRangeNotDerivedFrom> = true;
+
+// Test two separate properties:
+// 1. Whether `chunk_view<View>` can be formed.
+// 2. Whether a well-formed `chunk_view<View>` can be constructed.
+template <class View>
+concept CanFormChunkView = requires { typename std::ranges::chunk_view<View>; };
+
+template <class View>
+concept CanConstructChunkView = CanFormChunkView<View> && requires(View view, std::ranges::range_difference_t<View> n) {
+  std::ranges::chunk_view<View>(std::move(view), n);
+};
+
+// Test constraints when the template argument is not a view
+static_assert(!std::ranges::view<test_non_const_range<cpp17_input_iterator>>);
+static_assert(std::ranges::input_range<test_non_const_range<cpp17_input_iterator>>);
+static_assert(!CanFormChunkView<test_non_const_range<cpp17_input_iterator>>);
+
+// Test constraints when the template argument is not an input_range
+static_assert(!std::ranges::input_range<InputRangeNotDerivedFrom>);
+static_assert(std::ranges::view<InputRangeNotDerivedFrom>);
+static_assert(!CanFormChunkView<InputRangeNotDerivedFrom>);
+
+// Test constraints when the template argument is an input_range and a view
+static_assert(std::ranges::input_range<input_span<int>>);
+static_assert(std::ranges::view<input_span<int>>);
+static_assert(CanFormChunkView<input_span<int>> && CanConstructChunkView<input_span<int>>);
+
+// Test constraints when the template argument is a forward_range and a view
+static_assert(std::ranges::forward_range<BorrowedView>);
+static_assert(std::ranges::view<BorrowedView>);
+static_assert(CanFormChunkView<BorrowedView> && CanConstructChunkView<BorrowedView>);
+
+// chunk_view itself models view
+static_assert(std::ranges::view<std::ranges::chunk_view<input_span<int>>>);
+static_assert(std::ranges::view<std::ranges::chunk_view<BorrowedView>>);

>From 8085a1d3bf45b122557796308e767016f9f7e784 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Mon, 20 Jul 2026 00:13:26 +0800
Subject: [PATCH 24/25] Add chunk_view iterator type tests

---
 .../types.compile.pass.cpp                    | 30 ++++++++++
 .../range.chunk.iter/types.compile.pass.cpp   | 57 +++++++++++++++++++
 .../types.compile.pass.cpp                    | 31 ++++++++++
 3 files changed, 118 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..1775a08f84a2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   class inner_iterator;
+
+//   using inner_iterator::iterator_concept = input_iterator_tag;
+//   using inner_iterator::difference_type = range_difference_t<V>;
+//   using inner_iterator::value_type = range_value_t<V>;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+using InnerIterator = std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<input_span<int>>>>;
+
+static_assert(std::same_as<typename InnerIterator::iterator_concept, std::input_iterator_tag>);
+static_assert(std::same_as<typename InnerIterator::difference_type, std::ranges::range_difference_t<input_span<int>>>);
+static_assert(std::same_as<typename InnerIterator::value_type, std::ranges::range_value_t<input_span<int>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..96e3057edc04a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models forward_range:
+//   class iterator;
+
+//   using iterator::iterator_category = input_iterator_tag;
+//   using iterator::iterator_concept = see below;
+//   using iterator::value_type = decltype(views::take(subrange(current_, end_), n_));
+//   using iterator::difference_type = range_difference_t<Base>;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+template <template <class...> class Iter>
+using ChunkViewFor = std::ranges::chunk_view<test_view<Iter>>;
+
+template <template <class...> class Iter>
+using ChunkIteratorFor = std::ranges::iterator_t<ChunkViewFor<Iter>>;
+
+template <template <class...> class Iter>
+constexpr void test_iterator_types() {
+  using ChunkView     = ChunkViewFor<Iter>;
+  using ChunkIterator = ChunkIteratorFor<Iter>;
+
+  static_assert(std::same_as<typename ChunkIterator::iterator_category, std::input_iterator_tag>);
+  static_assert(std::same_as<typename ChunkIterator::value_type, std::ranges::range_value_t<ChunkView>>);
+  static_assert(std::same_as<typename ChunkIterator::difference_type, std::ranges::range_difference_t<test_view<Iter>>>);
+}
+
+constexpr void test() {
+  test_iterator_types<forward_iterator>();
+  test_iterator_types<bidirectional_iterator>();
+  test_iterator_types<random_access_iterator>();
+  test_iterator_types<contiguous_iterator>();
+
+  static_assert(std::same_as<ChunkIteratorFor<forward_iterator>::iterator_concept, std::forward_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<bidirectional_iterator>::iterator_concept,
+                             std::bidirectional_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<random_access_iterator>::iterator_concept,
+                             std::random_access_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<contiguous_iterator>::iterator_concept,
+                             std::random_access_iterator_tag>);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..9127e567305ce
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   class outer_iterator;
+
+//   using outer_iterator::iterator_concept = input_iterator_tag;
+//   using outer_iterator::difference_type = range_difference_t<V>;
+//   class outer_iterator::value_type;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+using OuterIterator = std::ranges::iterator_t<std::ranges::chunk_view<input_span<int>>>;
+
+static_assert(std::same_as<typename OuterIterator::iterator_concept, std::input_iterator_tag>);
+static_assert(std::same_as<typename OuterIterator::difference_type, std::ranges::range_difference_t<input_span<int>>>);
+static_assert(std::same_as<typename OuterIterator::value_type, std::iter_value_t<OuterIterator>>);
+static_assert(std::ranges::input_range<typename OuterIterator::value_type>);

>From 48bf130465c89823e4c9817180cfa15434057466 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:22:10 +0800
Subject: [PATCH 25/25] Add chunk_view inner iterator special member tests

---
 .../ctor.compile.pass.cpp                     | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp
new file mode 100644
index 0000000000000..ec2e68cb0c8ca
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   inner_iterator(inner_iterator&&) = default;
+//   inner_iterator& operator=(inner_iterator&&) = default;
+
+#include <concepts>
+#include <ranges>
+
+#include "../types.h"
+
+using InnerIterator = std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<input_span<int>>>>;
+
+static_assert(!std::default_initializable<InnerIterator>);
+static_assert(!std::copy_constructible<InnerIterator>);
+static_assert(!std::assignable_from<InnerIterator&, const InnerIterator&>);
+static_assert(std::move_constructible<InnerIterator>);
+static_assert(std::assignable_from<InnerIterator&, InnerIterator>);


From libcxx-commits at lists.llvm.org  Sat Jul 25 00:49:18 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 00:49:18 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2442R1
	`std::views::chunk` (PR #171234)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171234@github.com>
Message-ID: <6a646a7e.0318b02e.530b7.7440@mx.google.com>


https://github.com/shyeyian updated https://github.com/llvm/llvm-project/pull/171234

>From f6554ea2a0e1d9571cf82fc7b2f3d1115c466474 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at petalmail.com>
Date: Wed, 24 Dec 2025 22:05:29 +0800
Subject: [PATCH 01/28] Implement `views::chunk`.

This commit squashes previous commits.
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/22.rst               |   1 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/chunk_view.h          | 562 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/ranges                         |  12 +
 libcxx/include/version                        |   2 +-
 libcxx/modules/std/ranges.inc                 |  11 +-
 .../no_unique_address.compile.pass.cpp        |  53 ++
 .../range.chunk/nodiscard.verify.cpp          |  86 +++
 .../ranges.version.compile.pass.cpp           |  32 +-
 .../version.version.compile.pass.cpp          |  32 +-
 .../range.chunk/adaptor.pass.cpp              |  75 +++
 .../range.adaptors/range.chunk/base.pass.cpp  |  46 ++
 .../range.adaptors/range.chunk/begin.pass.cpp |  67 +++
 .../range.chunk/ctad.compile.pass.cpp         |  55 ++
 .../range.adaptors/range.chunk/end.pass.cpp   |  65 ++
 .../range.chunk/general.pass.cpp              |  48 ++
 .../range.chunk.iter/compare.pass.cpp         | 109 ++++
 .../range.chunk.iter/decrement.pass.cpp       |  60 ++
 .../range.chunk.iter/deref.pass.cpp           |  77 +++
 .../range.chunk.iter/increment.pass.cpp       |  95 +++
 .../ranges/range.adaptors/range.chunk/types.h |  50 ++
 .../generate_feature_test_macro_components.py |   1 -
 25 files changed, 1494 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/chunk_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..8e57305f32ad1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -370,7 +370,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_rvalue``                             ``202207L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_chunk``                                 *unimplemented*
+    ``__cpp_lib_ranges_chunk``                                 ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                              ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0d1a1fbc00f2c..d2a2d1b712689 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,6 +54,7 @@ Implemented Papers
 - 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>`__)
+- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e4c6cb0f51065..8c8ab211e5bf4 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__","Only ``ranges::iota`` is implemented."
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__",""
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index eb423ae923b4f..2426da6848b51 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -707,6 +707,7 @@ set(files
   __ranges/all.h
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
+  __ranges/chunk_view.h
   __ranges/common_view.h
   __ranges/concepts.h
   __ranges/container_compatible_range.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
new file mode 100644
index 0000000000000..f19d2c9ede8a7
--- /dev/null
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_CHUNK_VIEW_H
+#define _LIBCPP___RANGES_CHUNK_VIEW_H
+
+#include <__algorithm/ranges_min.h>
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/take_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
+
+  class __outer_iterator;
+  class __inner_iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n), __remainder_(0) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires std::copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator begin() {
+    __current_.__emplace(ranges::begin(__base_));
+    __remainder_ = __n_;
+    return __outer_iterator(*this);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept {
+    return std::default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(chunk_view& __parent)
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  class value_type;
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator(__outer_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    return value_type(*__parent_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
+    __parent_->__remainder_ = __parent_->__n_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __outer_iterator& __i, default_sentinel_t) {
+    return *__i.__parent_->__current_ == ranges::end(__i.__parent_->__base_) && __i.__parent_->__remainder_ != 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __outer_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
+    if (__dist < __i.__parent_->__remainder_)
+      return __dist == 0 ? 0 : 1;
+    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __outer_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator::value_type : public view_interface<value_type> {
+  friend __outer_iterator;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(chunk_view& __parent) : __parent_(std::addressof(__parent)) {}
+
+public:
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator begin() const noexcept {
+    return __inner_iterator(*__parent_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return std::__to_unsigned_like(
+        ranges::min(__parent_->__remainder_, ranges::end(__parent_->__base_) - *__parent_->__current_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__inner_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(chunk_view& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+  using value_type       = range_value_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator(__inner_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator& operator=(__inner_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_View> base() const& { return *__parent_->__current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_reference_t<_View> operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
+    return **__parent_->__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    ++*__parent_->__current_;
+    if (*__parent_->__current_ == ranges::end(__parent_->__base_))
+      __parent_->__remainder_ = 0;
+    else
+      --__parent_->__remainder_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __inner_iterator& __i, default_sentinel_t) {
+    return __i.__parent_->__remainder_ == 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __inner_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return ranges::min(__i.__parent_->__remainder_, ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __inner_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
+    return ranges::iter_move(*__i.__parent_->__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void
+  iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) noexcept(
+      noexcept((ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_))))
+    requires indirectly_swappable<iterator_t<_View>>
+  {
+    return ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_);
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+class chunk_view<_View> : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+
+  template <bool _Const>
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires forward_range<const _View>
+  {
+    return __iterator<true>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    if constexpr (common_range<_View> && sized_range<_View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<false>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<_View> && !bidirectional_range<_View>)
+      return __iterator<false>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires forward_range<const _View>
+  {
+    if constexpr (common_range<const _View> && sized_range<const _View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<true>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<const _View> && !bidirectional_range<const _View>)
+      return __iterator<true>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+template <bool _Const>
+class chunk_view<_View>::__iterator {
+  friend chunk_view;
+
+  using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, chunk_view>;
+  using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
+
+  iterator_t<_Base> __current_         = iterator_t<_Base>();
+  sentinel_t<_Base> __end_             = sentinel_t<_Base>();
+  range_difference_t<_Base> __n_       = 0;
+  range_difference_t<_Base> __missing_ = 0;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      _Parent* __parent, iterator_t<_Base> __current, range_difference_t<_Base> __missing = 0)
+      : __current_(__current), __end_(ranges::end(__parent->__base_)), __n_(__parent->__n_), __missing_(__missing) {}
+
+  [[nodiscard]] static consteval auto __get_iterator_concept() {
+    if constexpr (random_access_range<_Base>)
+      return random_access_iterator_tag{};
+    else if constexpr (bidirectional_range<_Base>)
+      return bidirectional_iterator_tag{};
+    else
+      return forward_iterator_tag{};
+  }
+
+public:
+  using iterator_category = input_iterator_tag;
+  using iterator_concept  = decltype(__iterator::__get_iterator_concept());
+  using value_type        = decltype(views::take(subrange(__current_, __end_), __n_));
+  using difference_type   = range_difference_t<_Base>;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> &&
+                 convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+      : __current_(std::move(__i.__current_)),
+        __end_(std::move(__i.__end_)),
+        __n_(__i.__n_),
+        __missing_(__i.__missing_) {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() const { return __current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to dereference past-the-end chunk_view iterator");
+    return views::take(subrange(__current_, __end_), __n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator[](difference_type __pos) const
+    requires random_access_range<_Base>
+  {
+    return *(*this + __pos);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    __missing_ = ranges::advance(__current_, __n_, __end_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires bidirectional_range<_Base>
+  {
+    ranges::advance(__current_, __missing_ - __n_);
+    __missing_ = 0;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    if (__x > 0) {
+      _LIBCPP_ASSERT_PEDANTIC(ranges::distance(__current_, __end_) > __n_ * (__x - 1),
+                              "Trying to advance chunk_view iterator out of range");
+      ranges::advance(__current_, __n_ * (__x - 1));
+      __missing_ = ranges::advance(__current_, __n_, __end_);
+    } else if (__x < 0) {
+      ranges::advance(__current_, __n_ * __x + __missing_);
+      __missing_ = 0;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    return *this += -__x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
+    return __x.__current_ == __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, default_sentinel_t) {
+    return __x.__current_ == __x.__end_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __x.__current_ < __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __y < __x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__y < __x);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__x < __y);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(difference_type __pos, const __iterator& __i)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator-(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r -= __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, const __iterator& __j)
+    requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+  {
+    return (__i.__current_ - __j.__current_ + __i.__missing_ - __j.__missing_) / __i.__n_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <class _Range>
+chunk_view(_Range&&, range_difference_t<_Range>) -> chunk_view<views::all_t<_Range>>;
+
+template <class _View>
+inline constexpr bool enable_borrowed_range<chunk_view<_View>> = forward_range<_View> && enable_borrowed_range<_View>;
+
+namespace views {
+namespace __chunk {
+struct __fn {
+  template <viewable_range _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range, range_difference_t<_Range> __n) noexcept(
+      noexcept(/*-----*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))))
+      -> decltype(/*--*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))) {
+    return /*---------*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n));
+  }
+
+  template <class _DifferenceType>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_DifferenceType __n) noexcept(is_nothrow_constructible_v<decay_t<_DifferenceType>, _DifferenceType>) {
+    return __pipeable(std::__bind_back(__fn{}, std::forward<_DifferenceType>(__n)));
+  }
+};
+
+} // namespace __chunk
+
+inline namespace __cpo {
+inline constexpr auto chunk = __chunk::__fn{};
+
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_CHUNK_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4434a5446e35b..cb03eb8d14067 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1863,6 +1863,10 @@ module std [system] {
       header "__ranges/chunk_by_view.h"
       export std.functional.bind_back
     }
+    module chunk_view {
+      header "__ranges/chunk_view.h"
+      export std.functional.bind_back
+    }
     module common_view                    { header "__ranges/common_view.h" }
     module concepts                       { header "__ranges/concepts.h" }
     module container_compatible_range     { header "__ranges/container_compatible_range.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 9f725b12ac6c2..9ec6a4a819919 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -396,6 +396,17 @@ namespace std::ranges {
   class chunk_by_view;                                                      // C++23
 
   namespace views { inline constexpr unspecified chunk_by = unspecified; }  // C++23
+
+  // [range.chunk]
+  template <view V>
+    requires input_range<V>
+  class chunk_view;                                                     // C++23
+
+  template <view V>
+    requires forward_range<V>
+  class chunk_view<V>;                                                  // C++23
+
+  namespace views { inline constexpr unspecified chunk = unspecified; } // C++23
 }
 
 namespace std {
@@ -482,6 +493,7 @@ namespace std {
 #    include <__ranges/adjacent_view.h>
 #    include <__ranges/as_rvalue_view.h>
 #    include <__ranges/chunk_by_view.h>
+#    include <__ranges/chunk_view.h>
 #    include <__ranges/from_range.h>
 #    include <__ranges/join_with_view.h>
 #    include <__ranges/repeat_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index ab781466f5ed5..f5fc33674f521 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -523,7 +523,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges                               202406L
 // # define __cpp_lib_ranges_as_const                      202207L
 # define __cpp_lib_ranges_as_rvalue                     202207L
-// # define __cpp_lib_ranges_chunk                         202202L
+# define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
 # define __cpp_lib_ranges_contains                      202207L
 # define __cpp_lib_ranges_find_last                     202207L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 5caa2c052c00f..631ae10c922ae 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -314,18 +314,17 @@ export namespace std {
       using std::ranges::views::adjacent_transform;
       using std::ranges::views::pairwise_transform;
     } // namespace views
+#endif
 
-#endif // _LIBCPP_STD_VER >= 23
-
-#if 0
+#if _LIBCPP_STD_VER >= 23
     using std::ranges::chunk_view;
 
-    using std::ranges::chunk_view<V>;
-
     namespace views {
       using std::ranges::views::chunk;
-    }
+    } // namespace views
+#endif
 
+#if 0
     using std::ranges::slide_view;
 
     namespace views {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
new file mode 100644
index 0000000000000..14c618c505e70
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// This test ensures that we use `[[no_unique_address]]` in `chunk_view`.
+
+#include <cstddef>
+#include <ranges>
+#include <string>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct input_view {
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<input_view> = true;
+static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
+using CV1 = std::ranges::chunk_view<input_view>;
+// Expected CV1 (with View == input) layout:
+// [[no_unique_address]] _View __base_                                         // size: 0
+// [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] __non_propagating_cache<iterator_t<_View>> __current_ // size: sizeof(__non_propagating_cache<cpp20_input_iterator<int*>>), align: std::ptrdiff_t
+static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>) == alignof(std::ptrdiff_t));
+static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
+                                 sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
+
+using CV2 = std::ranges::chunk_view<forward_view>;
+// Expected CV2 (with View >= forward) layout:
+// [[no_unique_address]] _View __base_             // size: 0
+// [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
+static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..5e557715f4b03
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
+  auto view     = range | std::views::chunk(2);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(std::as_const(view)).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(view).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(view.begin());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(std::as_const(view).begin());
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 5116864879485..41e1603f62350 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -270,17 +270,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -387,17 +381,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
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 b3b424a1d77ce..bd1b55ccc8dfa 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
@@ -5700,17 +5700,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -7619,17 +7613,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
new file mode 100644
index 0000000000000..657e16383256b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   std::views::chunk
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                       = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::ref_view<std::array<int, 8>> view = array | std::views::all;
+
+  // Test `views::chunk(view, n)`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        std::views::chunk(view, 2);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::views::chunk(std::as_const(view), 2);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2}));
+  }
+
+  // Test `views::chunk(n)(range)`
+  {
+    static_assert(noexcept(std::views::chunk(2)));
+    /*__pipable*/ auto adaptor = std::views::chunk(3);
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        adaptor(view);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        adaptor(std::as_const(view));
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3}));
+  }
+
+  // Test `view | views::chunk`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        view | std::views::chunk(4);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3, 4}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::as_const(view) | std::views::chunk(4);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3, 4}));
+  }
+
+  // Test `views::chunk | adaptor`
+  {
+    /*__pipable*/ auto adaptors            = std::views::chunk(5) | std::views::join;
+    std::ranges::input_range auto rejoined = view | adaptors;
+    assert(std::ranges::equal(rejoined, view));
+    std::ranges::input_range auto const_rejoined = std::as_const(view) | adaptors;
+    assert(std::ranges::equal(const_rejoined, view));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
new file mode 100644
index 0000000000000..ac17f97a0a292
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() &&;
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <memory>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                                                   = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>> chunked = array | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
+      std::as_const(array) | std::views::chunk(4);
+
+  // Test `chunk_view.base()`
+  {
+    std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
+    assert(std::addressof(base) == std::addressof(array));
+
+    std::same_as<const std::array<int, 8>&> decltype(auto) const_base = const_chunked.base().base();
+    assert(std::addressof(const_base) == std::addressof(array));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
new file mode 100644
index 0000000000000..3cccd7d1bebc6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr __outer_iterator begin();
+
+//   V models forward_range:
+//     constexpr auto begin() requires (!__simple_view<V>);
+//     constexpr auto begin() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.begin()` when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == input_chunked.end());
+  }
+
+  // Test `chunk_view.begin()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == chunked.end());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.begin();
+    assert(std::ranges::equal(*const_it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++const_it, std::vector{7, 8}));
+    assert(++const_it == const_chunked.end());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..9bf2baba58c78
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class R>
+//   chunk_view(R&&, range_difference_t<R>) -> chunk_view<all_t<R>>;
+
+#include <ranges>
+
+struct view : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct range {
+  int* begin() const;
+  int* end() const;
+};
+
+struct borrowed_range {
+  int* begin() const;
+  int* end() const;
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<borrowed_range> = true;
+
+void test_ctad() {
+  view v;
+  range r;
+  borrowed_range br;
+
+  // clang-format off
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(v, 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(v), 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(r, 0)), 
+                                      std::ranges::chunk_view<std::ranges::ref_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(r), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(br, 0)),
+                                      std::ranges::chunk_view<std::ranges::ref_view<borrowed_range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(br), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<borrowed_range>>>);
+  // clang-format on
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
new file mode 100644
index 0000000000000..73eb0d967f6a2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr default_sentinel_t end();
+
+//   V moduels forward_range:
+//     constexpr auto end() requires (!__simple_view<V>);
+//     constexpr auto end() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.end()` when V models only input_range
+  {
+    static_assert(noexcept(input_chunked.end()));
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it = input_chunked.end();
+  }
+
+  // Test `chunk_view.end()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--it, std::vector{1, 2, 3}));
+    assert(it == chunked.begin());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.end();
+    assert(std::ranges::equal(*--const_it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--const_it, std::vector{1, 2, 3}));
+    assert(const_it == const_chunked.begin());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
new file mode 100644
index 0000000000000..89891bc093080
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   General tests for chunk_view. This file does not test anything specifically.
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <string_view>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::string_view str = "Cheese with chicken chunk by chunk on truck with my trick";
+  // clang-format off
+  auto str2 = str 
+            | std::views::chunk(4)
+            | std::views::join
+            | std::views::chunk(314159)
+            | std::views::take(1)
+            | std::views::join
+            | std::views::lazy_split(' ')
+            | std::views::chunk(2)
+            | std::views::transform([] (auto&& subview)
+              {
+                return subview | std::views::join_with(' ');
+              })
+            | std::views::join_with(' ');
+  // clang-format on
+  assert(std::ranges::equal(str, str2));
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
new file mode 100644
index 0000000000000..d86726d958e6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator<(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator<=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base> &&
+//                three_way_comparable<iterator_t<Base>>;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t)`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    std::ranges::advance(it, 4);
+    assert(it == std::default_sentinel);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(input_chunked.end() - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - input_chunked.end() == -4);
+  }
+
+  // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() == chunked.begin());
+    assert(chunked.end() == chunked.end());
+  }
+
+  // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() < chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() > chunked.begin());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() <= chunked.begin());
+    assert(chunked.begin() <= chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() >= chunked.end());
+    assert(chunked.end() >= chunked.begin());
+  }
+
+  // Test `friend constexpr auto operator<=>(const iterator& x, const iterator& y)`
+  {
+    assert((chunked.begin() <=> chunked.begin()) == std::strong_ordering::equal);
+    assert((chunked.begin() <=> chunked.end()) == std::strong_ordering::less);
+    assert((chunked.end() <=> chunked.begin()) == std::strong_ordering::greater);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
new file mode 100644
index 0000000000000..397b32e73080a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+//   constexpr iterator& operator--()
+//     requires bidirectional_range<Base>;
+//   constexpr iterator operator--(int)
+//     requires bidirectional_range<Base>;
+//   constexpr iterator& operator-=(difference_type)
+//     requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+
+  // Test `constexpr iterator& operator--();`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator operator--(int)`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    it--;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator& operator-=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.end();
+    it -= 3;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
new file mode 100644
index 0000000000000..b8772d0fac3d2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr value_type outer_iterator::operator*() const;
+//     constexpr inner_iterator outer_iterator::value_type::begin() const noexcept;
+//     constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept;
+//     constexpr range_reference_v<V> inner_iterator::operator*() const;
+
+//   V models forward_range:
+//     constexpr value_type iterator::operator*() const;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `constexpr value_type outer_iterator::operator*() const`
+  {
+    static_assert(std::ranges::input_range<decltype(*input_chunked.begin())>);
+  }
+
+  // Test `constexpr inner_iterator outer_iterator::value_type::begin() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    assert(*inner.begin() == *vector.begin());
+    static_assert(noexcept(inner.begin()));
+  }
+
+  // Test `constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it                   = inner.end();
+    static_assert(noexcept((inner.end())));
+  }
+
+  // Test `constexpr value_type iterator::operator*() const`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::same_as<int> decltype(auto) v                           = *it;
+    assert(v == 1);
+  }
+
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
+  {
+    std::same_as<int&> decltype(auto) v = *(*chunked.begin()).begin();
+    assert(v == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
new file mode 100644
index 0000000000000..6048aba027ce9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     constexpr outer_iterator& operator++();
+//     constexpr void operator++(int);
+//     constexpr inner_iterator& operator++();
+//     constexpr void operator++(int);
+
+//   V models forward_range
+//     constexpr iterator& operator++();
+//     constexpr iterator operator++(int);
+//     constexpr iterator& operator+=(difference_type)
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(2);
+
+  // Test `constexpr outer_iterator& operator++();`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr void operator++(int);`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    assert(*++it == 2);
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(*it == 2);
+  }
+
+  // Test `constexpr iterator& operator++();`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator operator++(int)`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator& operator+=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    it += 3;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..09d16c9a48af0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f61d6f991cb15..49fb196903e77 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1106,7 +1106,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_ranges_chunk",
             "values": {"c++23": 202202},
             "headers": ["ranges"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ranges_chunk_by",

>From af914e794ddb86103e6b7acf3bf17d40f5055a8a Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 01:44:18 +0800
Subject: [PATCH 02/28] Code Review: adjust release notes

Note that we will merge this commit with the corresponding change in #172948 in the future.
---
 libcxx/docs/ReleaseNotes/22.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index d2a2d1b712689..30fc22fc0d787 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,7 +54,7 @@ Implemented Papers
 - 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>`__)
-- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------

>From dc62f798ba5615dcac60fffa2cc0afa8b79344c5 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 13:03:41 +0800
Subject: [PATCH 03/28] Add Tests: test if `_LIBCPP_ASSERT_XXX` works when
 `views::chunk` meets an undefined behaviour

---
 libcxx/include/__ranges/chunk_view.h          |  6 ++-
 .../range.chunk/assert.constructor.pass.cpp   | 39 ++++++++++++++
 .../range.chunk.iter/assert.advance.pass.cpp  | 32 ++++++++++++
 .../range.chunk.iter/assert.deref.pass.cpp    | 52 +++++++++++++++++++
 .../assert.increment.pass.cpp                 | 52 +++++++++++++++++++
 .../ranges/range.adaptors/range.chunk/types.h | 50 ++++++++++++++++++
 6 files changed, 229 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f19d2c9ede8a7..683b6bbaa2ca5 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -128,11 +128,12 @@ class chunk_view<_View>::__outer_iterator {
   _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
-    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
     return value_type(*__parent_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
     __parent_->__remainder_ = __parent_->__n_;
     return *this;
@@ -214,6 +215,7 @@ class chunk_view<_View>::__inner_iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ++*__parent_->__current_;
     if (*__parent_->__current_ == ranges::end(__parent_->__base_))
       __parent_->__remainder_ = 0;
@@ -386,7 +388,7 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
-    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to increment past-the-end chunk_view iterator");
     __missing_ = ranges::advance(__current_, __n_, __end_);
     return *this;
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
new file mode 100644
index 0000000000000..03fc7dfb6a5de
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Construct a chunk_view with chunk size <= 0
+
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3};
+
+  // Test constructor when V models only input_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  // Test constructor when V models forward_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
new file mode 100644
index 0000000000000..af2b826aa8eba
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to advance chunk_view iterator out of range
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+
+  // Test advance past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    TEST_LIBCPP_ASSERT_FAILURE(it += 4, "Trying to advance chunk_view iterator out of range");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
new file mode 100644
index 0000000000000..cb1d92885e684
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to dereference past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test dereference iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*outer_it, "Trying to dereference past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*inner_it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+
+  // Test dereference iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(*it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
new file mode 100644
index 0000000000000..8160ee1bb3e14
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to increment past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test increment past-the-end iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++outer_it, "Trying to increment past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++inner_it, "Trying to increment past-the-end chunk_view iterator");
+  }
+
+  // Test increment past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(++it, "Trying to increment past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..a5d688abfa1b0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif

>From b664c0cb8f8ed9b46629fa49e4bdd846734615d4 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:26 +0800
Subject: [PATCH 04/28] Fix CI: adjust `#include` path in `test/libcxx` files

---
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp          | 2 +-
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index cb1d92885e684..0c96742147189 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 8160ee1bb3e14..451683bab71b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};

>From 4dc9b06c0dd515030226b9d27841352ef62549a3 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:58 +0800
Subject: [PATCH 05/28] Fix CI: add template deduction guide for
 `input_span<T>`

---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 3 +++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..0978a23c37244 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..4ff1af1d7cda3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 1e8906ad49a2406158161cc1b9d0e1570af2959d Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:53:21 +0800
Subject: [PATCH 06/28] Code Review: add `REQUIRES: std-at-least-c++23` in all
 the tests related to `views::chunk`.

The previous commit, 4dc9b06c0dd515030226b9d27841352ef62549a3, which introduce manual/explicit template deduction guide in `input_span<T>`, is always implicitly-provided under -std=c++23. So this commit also reverts the changes about `types.h` in the previous commit.
---
 .../range.adaptors/range.chunk/assert.constructor.pass.cpp     | 1 +
 .../range.chunk/range.chunk.iter/assert.advance.pass.cpp       | 1 +
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp         | 1 +
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp     | 1 +
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h   | 3 ---
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h      | 3 ---
 6 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
index 03fc7dfb6a5de..477f89be5f070 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
index af2b826aa8eba..9b91d94841b91 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index 0c96742147189..3640b04192bf4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 451683bab71b0..61c096f74158e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index 0978a23c37244..a5d688abfa1b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 4ff1af1d7cda3..09d16c9a48af0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 7e21388c70b38c32125e4d3eda9b3a4c66e1d3db Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 14:13:59 +0800
Subject: [PATCH 07/28] Fix CI: add template deduction guide for
 `input_span<T>` again.

Seems that even in C++23 the CI fails due to no implicit deduction guide, here we just add the explicit one and make the CI to basically run ok.
---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 4 ++++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..4852ec29204bb 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..7533e41713d72 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From f1ed40a1aed2a1f5ebd1e56efcd6ae2bbb25dc26 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:07:21 +0800
Subject: [PATCH 08/28] Code review: Update libcxx/test synopsis

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 .../ranges/range.adaptors/range.chunk/nodiscard.verify.cpp      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 5e557715f4b03..9264256fc6a52 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+// Test that functions are marked [[nodiscard]].
 
 #include <ranges>
 #include <utility>

>From 12bec119ba49cd6016055f5887f5e83efa25ee30 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:21:29 +0800
Subject: [PATCH 09/28] Code review: Fix synopsis.

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index ac17f97a0a292..b45e1f59f1394 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() const& requires copy_constructible<V>;
 //   constexpr V base() &&;
 
 #include <array>

>From 1e05c5a038dd4cee5a0049b8c7e92e59c8aabd82 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 21:13:46 +0800
Subject: [PATCH 10/28] Code review: sort and unique the tests

---
 .../no_unique_address.compile.pass.cpp        |  24 ++--
 .../range.chunk/nodiscard.verify.cpp          |  35 ++----
 .../range.chunk.iter/arithmetic.pass.cpp      | 108 ++++++++++++++++++
 .../range.chunk.iter/compare.pass.cpp         |  29 +++--
 .../range.chunk.iter/subscript.pass.cpp       |  42 +++++++
 5 files changed, 189 insertions(+), 49 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
index 14c618c505e70..5d32daf4c178a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -20,6 +20,8 @@
 #include "test_iterators.h"
 #include "test_range.h"
 
+// When V models only input_range
+
 struct input_view {
   cpp20_input_iterator<int*> begin() const;
   sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
@@ -28,16 +30,8 @@ template <>
 inline constexpr bool std::ranges::enable_view<input_view> = true;
 static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
 
-struct forward_view {
-  int* begin() const;
-  int* end() const;
-};
-template <>
-inline constexpr bool std::ranges::enable_view<forward_view> = true;
-static_assert(std::ranges::forward_range<forward_view>);
-
 using CV1 = std::ranges::chunk_view<input_view>;
-// Expected CV1 (with View == input) layout:
+// Expected CV1 (when V models only input_range) layout:
 // [[no_unique_address]] _View __base_                                         // size: 0
 // [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
 // [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
@@ -46,8 +40,18 @@ static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<
 static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
                                  sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
 
+// When V models forward_range
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
 using CV2 = std::ranges::chunk_view<forward_view>;
-// Expected CV2 (with View >= forward) layout:
+// Expected CV2 (when V models forward_range) layout:
 // [[no_unique_address]] _View __base_             // size: 0
 // [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
 static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 9264256fc6a52..4d0fb4f76857c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -19,15 +19,6 @@ void test() {
   char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
   auto view     = range | std::views::chunk(2);
 
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   view.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -43,18 +34,19 @@ void test() {
   std::as_const(view).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  view.end();
+  std::ranges::iter_move(view.begin());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(view).end();
+  std::ranges::iter_move(std::as_const(view).begin());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
+  view.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
+  std::as_const(view).end();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
+  std::ranges::iter_move(view.end());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
+  std::ranges::iter_move(std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   *view.begin();
@@ -71,16 +63,11 @@ void test() {
   (std::as_const(view).begin() == std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == std::as_const(view).end());
+  std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == std::as_const(view).end());
-
+  std::views::chunk(range, 3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(view.begin());
+  range | std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(std::as_const(view).begin());
+  std::views::reverse | std::views::chunk(3);
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..6baa2fce13695
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const inner_iterator& x)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr iterator operator+(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator+(difference_type n, const iterator& i)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator-(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+//       requires sized_sentinel_for<iterator_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(std::default_sentinel - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - std::default_sentinel == -4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t, const inner_iterator& x)`
+  {
+    assert(std::default_sentinel - (*input_chunked.begin()).begin() == 3);
+  }
+
+  // Test `friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t)`
+  {
+    assert((*input_chunked.begin()).begin() - std::default_sentinel == -3);
+  }
+
+  // Test `friend constexpr iterator operator+(const iterator& i, difference_type n)`
+  {
+    assert(chunked.begin() + 4 == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator+(difference_type n, const iterator& i)`
+  {
+    assert(4 + chunked.begin() == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator-(const iterator& i, difference_type n)`
+  {
+    assert(chunked.end() - 4 == chunked.begin());
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)`
+  {
+    assert(std::default_sentinel - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)`
+  {
+    assert(chunked.begin() - std::default_sentinel == -4);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
index d86726d958e6e..ea6731cccce85 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -11,14 +11,12 @@
 // <ranges>
 
 //   V models only input_range
-//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
-//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
-//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t);
 
 //   V models forward_range
-//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator==(const iterator& x, const iterator& y);
+//     friend constexpr bool operator==(const iterator& x, default_sentinel_t);
 //     friend constexpr bool operator<(const iterator& x, const iterator& y)
 //       requires random_access_range<Base>;
 //     friend constexpr bool operator>(const iterator& x, const iterator& y)
@@ -31,7 +29,6 @@
 //       requires random_access_range<Base> &&
 //                three_way_comparable<iterator_t<Base>>;
 
-#include <algorithm>
 #include <cassert>
 #include <compare>
 #include <iterator>
@@ -53,14 +50,11 @@ constexpr bool test() {
     assert(it == std::default_sentinel);
   }
 
-  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  // Test `friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t)`
   {
-    assert(input_chunked.end() - input_chunked.begin() == 4);
-  }
-
-  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
-  {
-    assert(input_chunked.begin() - input_chunked.end() == -4);
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::ranges::advance(it, 3);
+    assert(it == std::default_sentinel);
   }
 
   // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
@@ -69,6 +63,11 @@ constexpr bool test() {
     assert(chunked.end() == chunked.end());
   }
 
+  // Test `friend constexpr bool operator==(const iterator& x, default_sentinel)`
+  {
+    assert(chunked.end() == std::default_sentinel);
+  }
+
   // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() < chunked.end());
@@ -79,7 +78,7 @@ constexpr bool test() {
     assert(chunked.end() > chunked.begin());
   }
 
-  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  // Test `friend constexpr bool operator<=(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() <= chunked.begin());
     assert(chunked.begin() <= chunked.end());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
new file mode 100644
index 0000000000000..9b45fc6d779bf
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models forward_range
+//     constexpr value_type iterator::operator[](difference_type n) const
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+
+  // Test `constexpr value_type iterator::operator[](difference_type n) const`
+  {
+    assert(std::ranges::equal(chunked.begin()[1], std::vector{4, 5, 6}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 033a1c16289990a16b1f674f380f7a72f38f48ea Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 14:30:08 -0400
Subject: [PATCH 11/28] Fix release notes

---
 libcxx/docs/ReleaseNotes/22.rst    | 1 -
 libcxx/docs/ReleaseNotes/23.rst    | 1 +
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0637ba98976a2..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -55,7 +55,6 @@ Implemented Papers
 - 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>`__)
-- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e7eb42207db42..a98f42da70f00 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -52,6 +52,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::chunk`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 6abcac5e4ebca..8c92d89be999b 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""

>From 7fe126d5e7c88787f26228c64bcb5f89977c5b83 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:02:10 +0800
Subject: [PATCH 12/28] Resolve overload conflicts on `__div_ceil` in
 `ranges::chunk_view` and `ranges::stride_view`.

Both `ranges::chunk_view` and `ranges::stride_view` need a helper function called `__div_ceil` that solves some simple maths. According to the style of other libcxx internal helper functions (e.g. `__priority_tag`, `__small_buffer`), it seems that we can put `__div_ceil` into a new file `__utility/div_ceil.h`. However, most files in `__utility/` refer to a corresponding symbol in `<utility>` header, so that if someone feels this is not proper, I'll be glad to revert it.
---
 libcxx/include/CMakeLists.txt         |  1 +
 libcxx/include/__ranges/chunk_view.h  | 21 ++++++-----------
 libcxx/include/__ranges/stride_view.h | 20 +++++-----------
 libcxx/include/__utility/div_ceil.h   | 33 +++++++++++++++++++++++++++
 libcxx/include/module.modulemap.in    |  1 +
 5 files changed, 48 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/include/__utility/div_ceil.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 76b93c17c3312..0e9f19a81b1ab 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -935,6 +935,7 @@ set(files
   __utility/convert_to_integral.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
+  __utility/div_ceil.h
   __utility/element_count.h
   __utility/empty.h
   __utility/exception_guard.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 683b6bbaa2ca5..68d4408295e47 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -36,6 +36,7 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 
@@ -52,14 +53,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Integral>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
-  _Integral __r = __num / __denom;
-  if (__num % __denom)
-    ++__r;
-  return __r;
-}
-
 template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
@@ -98,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -153,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -321,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -510,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..7ea68b13330c3 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -46,15 +47,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Value>
-_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) {
-  _Value __r = __left / __right;
-  if (__left % __right) {
-    ++__r;
-  }
-  return __r;
-}
-
 template <input_range _View>
   requires view<_View>
 class stride_view : public view_interface<stride_view<_View>> {
@@ -121,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -340,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -ranges::__div_ceil(-__n, __x.__stride_);
+      return -__div_ceil(-__n, __x.__stride_);
     }
-    return ranges::__div_ceil(__n, __x.__stride_);
+    return __div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)
diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
new file mode 100644
index 0000000000000..595c590b9e3f6
--- /dev/null
+++ b/libcxx/include/__utility/div_ceil.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_DIV_CEIL_H
+#define _LIBCPP___UTILITY_DIV_CEIL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __div_ceil computes the ceiling of the division of two integers. It is mainly used by
+// range adaptors like chunk_view and stride_view.
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_DIV_CEIL_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 64238440b9dd7..ef5d15d063f69 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2200,6 +2200,7 @@ module std [system] {
     module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
+    module div_ceil                        { header "__utility/div_ceil.h" }
     module element_count                   { header "__utility/element_count.h" }
     module exception_guard                 { header "__utility/exception_guard.h" }
     module exchange                        { header "__utility/exchange.h" }

>From d27b355e67e17b0ed15d44a247fdba1c3b13ed49 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:22:38 +0800
Subject: [PATCH 13/28] Fix test error that `constexpr` is not supported in
 `-std=c++03`

---
 libcxx/include/__utility/div_ceil.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 595c590b9e3f6..4d2868465c141 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,6 +17,8 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if _LIBCPP_STD_VER >= 11
+
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
 
@@ -28,6 +30,8 @@ template <class _Integral>
   return __r;
 }
 
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___UTILITY_DIV_CEIL_H

>From a1a5918bb79d29cbe7be2736da7a72b83ee0afbb Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:38:37 +0800
Subject: [PATCH 14/28] Fix test error again that `constexpr` is not supported
 in `-std=c++11`

---
 libcxx/include/__utility/div_ceil.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 4d2868465c141..21e9bcb30d1a1 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,7 +17,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 11
+#if _LIBCPP_STD_VER >= 23
 
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
@@ -30,7 +30,7 @@ template <class _Integral>
   return __r;
 }
 
-#endif
+#endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_END_NAMESPACE_STD
 

>From c2e0e2e926c92ee6d001e060aa5660b681874dfd Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:45:41 +0800
Subject: [PATCH 15/28] Fix test error that `std::as_const` is included from
 `<utility>`.

---
 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp | 1 +
 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
index 3cccd7d1bebc6..29588a57fbcab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -22,6 +22,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
index 73eb0d967f6a2..2f37ac5f73524 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -23,6 +23,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"

>From 7d3cdd73f5da77a7499969fb7954312a19e26630 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 14:19:19 +0800
Subject: [PATCH 16/28] Avoid ADL in `std::__div_ceil`.

---
 libcxx/include/__ranges/chunk_view.h  | 12 ++++++------
 libcxx/include/__ranges/stride_view.h | 10 +++++-----
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 68d4408295e47..1107a9238b2bc 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -91,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -146,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return std::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -314,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -503,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return std::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 7ea68b13330c3..a8b485cbe33e5 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -113,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -332,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -__div_ceil(-__n, __x.__stride_);
+      return -std::__div_ceil(-__n, __x.__stride_);
     }
-    return __div_ceil(__n, __x.__stride_);
+    return std::__div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return std::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)

>From 116c6d85d8a9ad177b1df1975eea92479cb03664 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 16:39:58 +0800
Subject: [PATCH 17/28] [Important] Add missing concepts requirements.

---
 libcxx/include/__ranges/chunk_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1107a9238b2bc..47e6bd56415b6 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -400,7 +400,8 @@ class chunk_view<_View>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires bidirectional_range<_Base> {
     auto __tmp = *this;
     --*this;
     return __tmp;

>From 545bbdf10d24c3790790bfe669b8c756da719838 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Fri, 26 Jun 2026 22:35:12 +0800
Subject: [PATCH 18/28] Cover tests on `chunk_range`. (I'll continue to cover
 tests on `__iterator`, `__inner_iterator` and `__outer_iterator` at the
 weekend)

---
 libcxx/include/__ranges/chunk_view.h          |  3 +-
 .../range.adaptors/range.chunk/base.pass.cpp  | 12 +++-
 .../range.chunk/borrowing.compile.pass.cpp    | 27 +++++++
 .../range.adaptors/range.chunk/ctor.pass.cpp  | 54 ++++++++++++++
 .../range.adaptors/range.chunk/size.pass.cpp  | 72 +++++++++++++++++++
 5 files changed, 166 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 47e6bd56415b6..1798711eabd16 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -401,7 +401,8 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
-    requires bidirectional_range<_Base> {
+    requires bidirectional_range<_Base>
+  {
     auto __tmp = *this;
     --*this;
     return __tmp;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index b45e1f59f1394..4dca324a23695 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -26,7 +26,7 @@ constexpr bool test() {
   std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
       std::as_const(array) | std::views::chunk(4);
 
-  // Test `chunk_view.base()`
+  // Test `chunk_view.base() const&`
   {
     std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
     assert(std::addressof(base) == std::addressof(array));
@@ -35,6 +35,16 @@ constexpr bool test() {
     assert(std::addressof(const_base) == std::addressof(array));
   }
 
+  // Test `chunk_view.base() &&`
+  {
+    std::same_as<std::ranges::ref_view<std::array<int, 8>>> decltype(auto) moved = std::move(chunked).base();
+    assert(std::addressof(moved.base()) == std::addressof(array));
+
+    std::same_as<std::ranges::ref_view<const std::array<int, 8>>> decltype(auto) const_moved =
+        std::move(const_chunked).base();
+    assert(std::addressof(const_moved.base()) == std::addressof(array));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
new file mode 100644
index 0000000000000..1eb4a34f87217
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class V>
+//   inline constexpr bool enable_borrowed_range<chunk_view<V>> =
+//       forward_range<V> && enable_borrowed_range<V>;
+
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+// When V models only `input_range`.
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<input_span<int>>>);
+
+// When V models at least `forward_range`.
+static_assert(std::ranges::enable_borrowed_range<std::ranges::chunk_view<BorrowedView>>);
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
new file mode 100644
index 0000000000000..b60e040b807eb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.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++23
+
+// <ranges>
+
+//   constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n);
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_convertible.h"
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models only `input_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>, input_span<int>, std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(input_span(vector.data(), 8), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+                                    std::ranges::ref_view<std::vector<int>>,
+                                    std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
new file mode 100644
index 0000000000000..820774c3b750e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr auto size() requires sized_range<_View>;
+//   constexpr auto size() const requires sized_range<const _View>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+  // Test `chunk_view.size()` when V models only `input_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<input_span<int>>>);
+    static_assert(!std::ranges::sized_range<const std::ranges::chunk_view<input_span<int>>>);
+
+    auto chunked = input_span<int>(arr, 12) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when V models `forward_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+    static_assert(std::ranges::sized_range<const std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+    const auto& const_chunked = chunked;
+    assert(const_chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when the range is not fully divisible
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(5);
+    assert(chunked.size() == 3);
+  }
+
+  // Test `chunk_view.size()` when the range is empty
+  {
+    static_assert(std::ranges::sized_range<std::ranges::empty_view<int>>);
+    auto chunked = std::views::empty<int> | std::views::chunk(3);
+    assert(chunked.size() == 0);
+  }
+
+  // Test `chunk_view.size()` when chunk size is larger than the range
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(100);
+    assert(chunked.size() == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From e262f9e7497fb3ccf232a3c7265b8faa83f2fa20 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:36:04 +0800
Subject: [PATCH 19/28] Fix chunk_view ctor test for forward ranges.

---
 .../test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
index b60e040b807eb..c0d210aa45dc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
@@ -35,11 +35,11 @@ constexpr bool test() {
 
   // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
   {
-    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+    static_assert(!test_convertible<std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>>,
                                     std::ranges::ref_view<std::vector<int>>,
                                     std::ptrdiff_t>());
 
-    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked(std::ranges::ref_view(vector), 3);
     assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
   }
 

>From bf13c55de976c562120249f2fc7208ea1091fc95 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:39:36 +0800
Subject: [PATCH 20/28] Use consistent copy_constructible spelling in
 chunk_view

---
 libcxx/include/__ranges/chunk_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1798711eabd16..f3edc617de07a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -71,7 +71,7 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
-    requires std::copy_constructible<_View>
+    requires copy_constructible<_View>
   {
     return __base_;
   }

>From f457f267b72fe64a2f22c288da4b096ae0bcc83c Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:48:59 +0800
Subject: [PATCH 21/28] Remove unnecessary no_unique_address from chunk_view
 counts

---
 libcxx/include/__ranges/chunk_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f3edc617de07a..4478af951dbca 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -57,8 +57,8 @@ template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  range_difference_t<_View> __n_;
+  range_difference_t<_View> __remainder_;
   _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
 
   class __outer_iterator;
@@ -256,7 +256,7 @@ template <view _View>
   requires forward_range<_View>
 class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  range_difference_t<_View> __n_;
 
   template <bool _Const>
   class __iterator;

>From 39c371e116736dd37006c3cf48ed9cf9412b1222 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 23:25:01 +0800
Subject: [PATCH 22/28] Fix chunk_view inner iter_move return type (from T into
 T&&)

---
 libcxx/include/__ranges/chunk_view.h          |  2 +-
 .../range.chunk.inner.iter/iter_move.pass.cpp | 43 +++++++++++++++++++
 2 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 4478af951dbca..e9511f027160a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -238,7 +238,7 @@ class chunk_view<_View>::__inner_iterator {
     return -(__s - __i);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_View>
   iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
     return ranges::iter_move(*__i.__parent_->__current_);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp
new file mode 100644
index 0000000000000..5c7b620924c2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   friend constexpr range_rvalue_reference_t<V> iter_move(const inner_iterator& i)
+//     noexcept(noexcept(ranges::iter_move(i.parent_->current_.value())));
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+constexpr bool test() {
+  using InnerIterator =
+      std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<test_view<cpp20_input_iterator>>>>;
+
+  static_assert(std::ranges::input_range<test_view<cpp20_input_iterator>>);
+  static_assert(!std::ranges::forward_range<test_view<cpp20_input_iterator>>);
+
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())), int&&>);
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())),
+                             std::ranges::range_rvalue_reference_t<test_view<cpp20_input_iterator>>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file

>From c1a0811d8824267ff40fa442b2c2ad10d95b7690 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Mon, 20 Jul 2026 00:13:15 +0800
Subject: [PATCH 23/28] Add chunk_view constraint tests

---
 .../range.chunk/concept.compile.pass.cpp      | 64 +++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp
new file mode 100644
index 0000000000000..61b89d4c09c32
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <view V>
+//     requires input_range<V>
+//   class chunk_view;
+//
+//   template <view V>
+//     requires forward_range<V>
+//   class chunk_view<_View>;
+
+#include <ranges>
+#include <utility>
+
+#include "almost_satisfies_types.h"
+#include "test_range.h"
+#include "types.h"
+
+template <>
+inline constexpr bool std::ranges::enable_view<InputRangeNotDerivedFrom> = true;
+
+// Test two separate properties:
+// 1. Whether `chunk_view<View>` can be formed.
+// 2. Whether a well-formed `chunk_view<View>` can be constructed.
+template <class View>
+concept CanFormChunkView = requires { typename std::ranges::chunk_view<View>; };
+
+template <class View>
+concept CanConstructChunkView = CanFormChunkView<View> && requires(View view, std::ranges::range_difference_t<View> n) {
+  std::ranges::chunk_view<View>(std::move(view), n);
+};
+
+// Test constraints when the template argument is not a view
+static_assert(!std::ranges::view<test_non_const_range<cpp17_input_iterator>>);
+static_assert(std::ranges::input_range<test_non_const_range<cpp17_input_iterator>>);
+static_assert(!CanFormChunkView<test_non_const_range<cpp17_input_iterator>>);
+
+// Test constraints when the template argument is not an input_range
+static_assert(!std::ranges::input_range<InputRangeNotDerivedFrom>);
+static_assert(std::ranges::view<InputRangeNotDerivedFrom>);
+static_assert(!CanFormChunkView<InputRangeNotDerivedFrom>);
+
+// Test constraints when the template argument is an input_range and a view
+static_assert(std::ranges::input_range<input_span<int>>);
+static_assert(std::ranges::view<input_span<int>>);
+static_assert(CanFormChunkView<input_span<int>> && CanConstructChunkView<input_span<int>>);
+
+// Test constraints when the template argument is a forward_range and a view
+static_assert(std::ranges::forward_range<BorrowedView>);
+static_assert(std::ranges::view<BorrowedView>);
+static_assert(CanFormChunkView<BorrowedView> && CanConstructChunkView<BorrowedView>);
+
+// chunk_view itself models view
+static_assert(std::ranges::view<std::ranges::chunk_view<input_span<int>>>);
+static_assert(std::ranges::view<std::ranges::chunk_view<BorrowedView>>);

>From 8085a1d3bf45b122557796308e767016f9f7e784 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Mon, 20 Jul 2026 00:13:26 +0800
Subject: [PATCH 24/28] Add chunk_view iterator type tests

---
 .../types.compile.pass.cpp                    | 30 ++++++++++
 .../range.chunk.iter/types.compile.pass.cpp   | 57 +++++++++++++++++++
 .../types.compile.pass.cpp                    | 31 ++++++++++
 3 files changed, 118 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..1775a08f84a2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   class inner_iterator;
+
+//   using inner_iterator::iterator_concept = input_iterator_tag;
+//   using inner_iterator::difference_type = range_difference_t<V>;
+//   using inner_iterator::value_type = range_value_t<V>;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+using InnerIterator = std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<input_span<int>>>>;
+
+static_assert(std::same_as<typename InnerIterator::iterator_concept, std::input_iterator_tag>);
+static_assert(std::same_as<typename InnerIterator::difference_type, std::ranges::range_difference_t<input_span<int>>>);
+static_assert(std::same_as<typename InnerIterator::value_type, std::ranges::range_value_t<input_span<int>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..96e3057edc04a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models forward_range:
+//   class iterator;
+
+//   using iterator::iterator_category = input_iterator_tag;
+//   using iterator::iterator_concept = see below;
+//   using iterator::value_type = decltype(views::take(subrange(current_, end_), n_));
+//   using iterator::difference_type = range_difference_t<Base>;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+template <template <class...> class Iter>
+using ChunkViewFor = std::ranges::chunk_view<test_view<Iter>>;
+
+template <template <class...> class Iter>
+using ChunkIteratorFor = std::ranges::iterator_t<ChunkViewFor<Iter>>;
+
+template <template <class...> class Iter>
+constexpr void test_iterator_types() {
+  using ChunkView     = ChunkViewFor<Iter>;
+  using ChunkIterator = ChunkIteratorFor<Iter>;
+
+  static_assert(std::same_as<typename ChunkIterator::iterator_category, std::input_iterator_tag>);
+  static_assert(std::same_as<typename ChunkIterator::value_type, std::ranges::range_value_t<ChunkView>>);
+  static_assert(std::same_as<typename ChunkIterator::difference_type, std::ranges::range_difference_t<test_view<Iter>>>);
+}
+
+constexpr void test() {
+  test_iterator_types<forward_iterator>();
+  test_iterator_types<bidirectional_iterator>();
+  test_iterator_types<random_access_iterator>();
+  test_iterator_types<contiguous_iterator>();
+
+  static_assert(std::same_as<ChunkIteratorFor<forward_iterator>::iterator_concept, std::forward_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<bidirectional_iterator>::iterator_concept,
+                             std::bidirectional_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<random_access_iterator>::iterator_concept,
+                             std::random_access_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<contiguous_iterator>::iterator_concept,
+                             std::random_access_iterator_tag>);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..9127e567305ce
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   class outer_iterator;
+
+//   using outer_iterator::iterator_concept = input_iterator_tag;
+//   using outer_iterator::difference_type = range_difference_t<V>;
+//   class outer_iterator::value_type;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+using OuterIterator = std::ranges::iterator_t<std::ranges::chunk_view<input_span<int>>>;
+
+static_assert(std::same_as<typename OuterIterator::iterator_concept, std::input_iterator_tag>);
+static_assert(std::same_as<typename OuterIterator::difference_type, std::ranges::range_difference_t<input_span<int>>>);
+static_assert(std::same_as<typename OuterIterator::value_type, std::iter_value_t<OuterIterator>>);
+static_assert(std::ranges::input_range<typename OuterIterator::value_type>);

>From 48bf130465c89823e4c9817180cfa15434057466 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:22:10 +0800
Subject: [PATCH 25/28] Add chunk_view inner iterator special member tests

---
 .../ctor.compile.pass.cpp                     | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp
new file mode 100644
index 0000000000000..ec2e68cb0c8ca
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   inner_iterator(inner_iterator&&) = default;
+//   inner_iterator& operator=(inner_iterator&&) = default;
+
+#include <concepts>
+#include <ranges>
+
+#include "../types.h"
+
+using InnerIterator = std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<input_span<int>>>>;
+
+static_assert(!std::default_initializable<InnerIterator>);
+static_assert(!std::copy_constructible<InnerIterator>);
+static_assert(!std::assignable_from<InnerIterator&, const InnerIterator&>);
+static_assert(std::move_constructible<InnerIterator>);
+static_assert(std::assignable_from<InnerIterator&, InnerIterator>);

>From 5d32ff5424b490676c9771699233a396532fea1b Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:37:21 +0800
Subject: [PATCH 26/28] Add chunk_view inner iterator base() test

---
 .../range.chunk.inner.iter/base.pass.cpp      | 44 +++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp
new file mode 100644
index 0000000000000..9546aecb01560
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   constexpr const iterator_t<V> base() const&;
+
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int buffer[] = {1, 2, 3, 4};
+
+  std::ranges::chunk_view<input_span<int>> chunked(input_span<int>(buffer, 4), 2);
+  auto outer = chunked.begin();
+  auto inner = (*outer).begin();
+
+  std::same_as<const std::ranges::iterator_t<input_span<int>>> decltype(auto) base = inner.base();
+  assert(*base == 1);
+
+  ++inner;
+  assert(*inner.base() == 2);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 2c8a8c62abfca361ca45933de0da5eeebd68b290 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:48:43 +0800
Subject: [PATCH 27/28] Fix input_span operator* to return a reference

---
 .../range.chunk/range.chunk.iter/deref.pass.cpp               | 4 ++--
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h     | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
index b8772d0fac3d2..32054584f4358 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -53,10 +53,10 @@ constexpr bool test() {
     static_assert(noexcept((inner.end())));
   }
 
-  // Test `constexpr value_type iterator::operator*() const`
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
   {
     /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
-    std::same_as<int> decltype(auto) v                           = *it;
+    std::same_as<int&> decltype(auto) v                          = *it;
     assert(v == 1);
   }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 7533e41713d72..83e383e98110e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -23,7 +23,7 @@ struct input_span : std::span<T> {
     using iterator_concept = std::input_iterator_tag;
     constexpr iterator()   = default;
     constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
-    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    constexpr decltype(auto) operator*() const { return std::span<T>::iterator::operator*(); }
     friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
     friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
     friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;

>From 32f132912f08cfd18cbf8fffff80ee42d178b745 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:48:48 +0800
Subject: [PATCH 28/28] Add chunk_view inner iterator iter_swap test

---
 .../range.chunk.inner.iter/iter_swap.pass.cpp | 50 +++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp
new file mode 100644
index 0000000000000..b5223d5091982
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   friend constexpr void iter_swap(const inner_iterator& x, const inner_iterator& y)
+//     noexcept(noexcept(ranges::iter_swap(x.parent_->current_.value(), y.parent_->current_.value())))
+//     requires indirectly_swappable<iterator_t<V>>;
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int a[] = {1, 2, 3, 4};
+  int b[] = {5, 6, 7, 8};
+
+  // Each chunk_view owns its own `current_` cache, so use two independent views to observe a real swap.
+  std::ranges::chunk_view<input_span<int>> chunked_a(input_span<int>(a, 4), 2);
+  std::ranges::chunk_view<input_span<int>> chunked_b(input_span<int>(b, 4), 2);
+
+  auto inner_a = (*chunked_a.begin()).begin();
+  auto inner_b = (*chunked_b.begin()).begin();
+
+  assert(a[0] == 1);
+  assert(b[0] == 5);
+
+  std::ranges::iter_swap(inner_a, inner_b);
+
+  assert(a[0] == 5);
+  assert(b[0] == 1);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Sat Jul 25 00:52:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 00:52:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2442R1
	`std::views::chunk` (PR #171234)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171234@github.com>
Message-ID: <6a646b2a.9edb5c9d.21e140.40be@mx.google.com>


https://github.com/shyeyian updated https://github.com/llvm/llvm-project/pull/171234

>From f6554ea2a0e1d9571cf82fc7b2f3d1115c466474 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at petalmail.com>
Date: Wed, 24 Dec 2025 22:05:29 +0800
Subject: [PATCH 01/29] Implement `views::chunk`.

This commit squashes previous commits.
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/22.rst               |   1 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/chunk_view.h          | 562 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/ranges                         |  12 +
 libcxx/include/version                        |   2 +-
 libcxx/modules/std/ranges.inc                 |  11 +-
 .../no_unique_address.compile.pass.cpp        |  53 ++
 .../range.chunk/nodiscard.verify.cpp          |  86 +++
 .../ranges.version.compile.pass.cpp           |  32 +-
 .../version.version.compile.pass.cpp          |  32 +-
 .../range.chunk/adaptor.pass.cpp              |  75 +++
 .../range.adaptors/range.chunk/base.pass.cpp  |  46 ++
 .../range.adaptors/range.chunk/begin.pass.cpp |  67 +++
 .../range.chunk/ctad.compile.pass.cpp         |  55 ++
 .../range.adaptors/range.chunk/end.pass.cpp   |  65 ++
 .../range.chunk/general.pass.cpp              |  48 ++
 .../range.chunk.iter/compare.pass.cpp         | 109 ++++
 .../range.chunk.iter/decrement.pass.cpp       |  60 ++
 .../range.chunk.iter/deref.pass.cpp           |  77 +++
 .../range.chunk.iter/increment.pass.cpp       |  95 +++
 .../ranges/range.adaptors/range.chunk/types.h |  50 ++
 .../generate_feature_test_macro_components.py |   1 -
 25 files changed, 1494 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/chunk_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..8e57305f32ad1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -370,7 +370,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_rvalue``                             ``202207L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_chunk``                                 *unimplemented*
+    ``__cpp_lib_ranges_chunk``                                 ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                              ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0d1a1fbc00f2c..d2a2d1b712689 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,6 +54,7 @@ Implemented Papers
 - 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>`__)
+- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e4c6cb0f51065..8c8ab211e5bf4 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__","Only ``ranges::iota`` is implemented."
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__",""
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index eb423ae923b4f..2426da6848b51 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -707,6 +707,7 @@ set(files
   __ranges/all.h
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
+  __ranges/chunk_view.h
   __ranges/common_view.h
   __ranges/concepts.h
   __ranges/container_compatible_range.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
new file mode 100644
index 0000000000000..f19d2c9ede8a7
--- /dev/null
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_CHUNK_VIEW_H
+#define _LIBCPP___RANGES_CHUNK_VIEW_H
+
+#include <__algorithm/ranges_min.h>
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/take_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
+
+  class __outer_iterator;
+  class __inner_iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n), __remainder_(0) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires std::copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator begin() {
+    __current_.__emplace(ranges::begin(__base_));
+    __remainder_ = __n_;
+    return __outer_iterator(*this);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept {
+    return std::default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(chunk_view& __parent)
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  class value_type;
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator(__outer_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    return value_type(*__parent_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
+    __parent_->__remainder_ = __parent_->__n_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __outer_iterator& __i, default_sentinel_t) {
+    return *__i.__parent_->__current_ == ranges::end(__i.__parent_->__base_) && __i.__parent_->__remainder_ != 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __outer_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
+    if (__dist < __i.__parent_->__remainder_)
+      return __dist == 0 ? 0 : 1;
+    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __outer_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator::value_type : public view_interface<value_type> {
+  friend __outer_iterator;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(chunk_view& __parent) : __parent_(std::addressof(__parent)) {}
+
+public:
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator begin() const noexcept {
+    return __inner_iterator(*__parent_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return std::__to_unsigned_like(
+        ranges::min(__parent_->__remainder_, ranges::end(__parent_->__base_) - *__parent_->__current_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__inner_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(chunk_view& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+  using value_type       = range_value_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator(__inner_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator& operator=(__inner_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_View> base() const& { return *__parent_->__current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_reference_t<_View> operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
+    return **__parent_->__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    ++*__parent_->__current_;
+    if (*__parent_->__current_ == ranges::end(__parent_->__base_))
+      __parent_->__remainder_ = 0;
+    else
+      --__parent_->__remainder_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __inner_iterator& __i, default_sentinel_t) {
+    return __i.__parent_->__remainder_ == 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __inner_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return ranges::min(__i.__parent_->__remainder_, ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __inner_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
+    return ranges::iter_move(*__i.__parent_->__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void
+  iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) noexcept(
+      noexcept((ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_))))
+    requires indirectly_swappable<iterator_t<_View>>
+  {
+    return ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_);
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+class chunk_view<_View> : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+
+  template <bool _Const>
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires forward_range<const _View>
+  {
+    return __iterator<true>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    if constexpr (common_range<_View> && sized_range<_View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<false>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<_View> && !bidirectional_range<_View>)
+      return __iterator<false>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires forward_range<const _View>
+  {
+    if constexpr (common_range<const _View> && sized_range<const _View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<true>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<const _View> && !bidirectional_range<const _View>)
+      return __iterator<true>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+template <bool _Const>
+class chunk_view<_View>::__iterator {
+  friend chunk_view;
+
+  using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, chunk_view>;
+  using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
+
+  iterator_t<_Base> __current_         = iterator_t<_Base>();
+  sentinel_t<_Base> __end_             = sentinel_t<_Base>();
+  range_difference_t<_Base> __n_       = 0;
+  range_difference_t<_Base> __missing_ = 0;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      _Parent* __parent, iterator_t<_Base> __current, range_difference_t<_Base> __missing = 0)
+      : __current_(__current), __end_(ranges::end(__parent->__base_)), __n_(__parent->__n_), __missing_(__missing) {}
+
+  [[nodiscard]] static consteval auto __get_iterator_concept() {
+    if constexpr (random_access_range<_Base>)
+      return random_access_iterator_tag{};
+    else if constexpr (bidirectional_range<_Base>)
+      return bidirectional_iterator_tag{};
+    else
+      return forward_iterator_tag{};
+  }
+
+public:
+  using iterator_category = input_iterator_tag;
+  using iterator_concept  = decltype(__iterator::__get_iterator_concept());
+  using value_type        = decltype(views::take(subrange(__current_, __end_), __n_));
+  using difference_type   = range_difference_t<_Base>;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> &&
+                 convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+      : __current_(std::move(__i.__current_)),
+        __end_(std::move(__i.__end_)),
+        __n_(__i.__n_),
+        __missing_(__i.__missing_) {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() const { return __current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to dereference past-the-end chunk_view iterator");
+    return views::take(subrange(__current_, __end_), __n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator[](difference_type __pos) const
+    requires random_access_range<_Base>
+  {
+    return *(*this + __pos);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    __missing_ = ranges::advance(__current_, __n_, __end_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires bidirectional_range<_Base>
+  {
+    ranges::advance(__current_, __missing_ - __n_);
+    __missing_ = 0;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    if (__x > 0) {
+      _LIBCPP_ASSERT_PEDANTIC(ranges::distance(__current_, __end_) > __n_ * (__x - 1),
+                              "Trying to advance chunk_view iterator out of range");
+      ranges::advance(__current_, __n_ * (__x - 1));
+      __missing_ = ranges::advance(__current_, __n_, __end_);
+    } else if (__x < 0) {
+      ranges::advance(__current_, __n_ * __x + __missing_);
+      __missing_ = 0;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    return *this += -__x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
+    return __x.__current_ == __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, default_sentinel_t) {
+    return __x.__current_ == __x.__end_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __x.__current_ < __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __y < __x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__y < __x);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__x < __y);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(difference_type __pos, const __iterator& __i)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator-(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r -= __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, const __iterator& __j)
+    requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+  {
+    return (__i.__current_ - __j.__current_ + __i.__missing_ - __j.__missing_) / __i.__n_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <class _Range>
+chunk_view(_Range&&, range_difference_t<_Range>) -> chunk_view<views::all_t<_Range>>;
+
+template <class _View>
+inline constexpr bool enable_borrowed_range<chunk_view<_View>> = forward_range<_View> && enable_borrowed_range<_View>;
+
+namespace views {
+namespace __chunk {
+struct __fn {
+  template <viewable_range _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range, range_difference_t<_Range> __n) noexcept(
+      noexcept(/*-----*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))))
+      -> decltype(/*--*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))) {
+    return /*---------*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n));
+  }
+
+  template <class _DifferenceType>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_DifferenceType __n) noexcept(is_nothrow_constructible_v<decay_t<_DifferenceType>, _DifferenceType>) {
+    return __pipeable(std::__bind_back(__fn{}, std::forward<_DifferenceType>(__n)));
+  }
+};
+
+} // namespace __chunk
+
+inline namespace __cpo {
+inline constexpr auto chunk = __chunk::__fn{};
+
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_CHUNK_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4434a5446e35b..cb03eb8d14067 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1863,6 +1863,10 @@ module std [system] {
       header "__ranges/chunk_by_view.h"
       export std.functional.bind_back
     }
+    module chunk_view {
+      header "__ranges/chunk_view.h"
+      export std.functional.bind_back
+    }
     module common_view                    { header "__ranges/common_view.h" }
     module concepts                       { header "__ranges/concepts.h" }
     module container_compatible_range     { header "__ranges/container_compatible_range.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 9f725b12ac6c2..9ec6a4a819919 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -396,6 +396,17 @@ namespace std::ranges {
   class chunk_by_view;                                                      // C++23
 
   namespace views { inline constexpr unspecified chunk_by = unspecified; }  // C++23
+
+  // [range.chunk]
+  template <view V>
+    requires input_range<V>
+  class chunk_view;                                                     // C++23
+
+  template <view V>
+    requires forward_range<V>
+  class chunk_view<V>;                                                  // C++23
+
+  namespace views { inline constexpr unspecified chunk = unspecified; } // C++23
 }
 
 namespace std {
@@ -482,6 +493,7 @@ namespace std {
 #    include <__ranges/adjacent_view.h>
 #    include <__ranges/as_rvalue_view.h>
 #    include <__ranges/chunk_by_view.h>
+#    include <__ranges/chunk_view.h>
 #    include <__ranges/from_range.h>
 #    include <__ranges/join_with_view.h>
 #    include <__ranges/repeat_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index ab781466f5ed5..f5fc33674f521 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -523,7 +523,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges                               202406L
 // # define __cpp_lib_ranges_as_const                      202207L
 # define __cpp_lib_ranges_as_rvalue                     202207L
-// # define __cpp_lib_ranges_chunk                         202202L
+# define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
 # define __cpp_lib_ranges_contains                      202207L
 # define __cpp_lib_ranges_find_last                     202207L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 5caa2c052c00f..631ae10c922ae 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -314,18 +314,17 @@ export namespace std {
       using std::ranges::views::adjacent_transform;
       using std::ranges::views::pairwise_transform;
     } // namespace views
+#endif
 
-#endif // _LIBCPP_STD_VER >= 23
-
-#if 0
+#if _LIBCPP_STD_VER >= 23
     using std::ranges::chunk_view;
 
-    using std::ranges::chunk_view<V>;
-
     namespace views {
       using std::ranges::views::chunk;
-    }
+    } // namespace views
+#endif
 
+#if 0
     using std::ranges::slide_view;
 
     namespace views {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
new file mode 100644
index 0000000000000..14c618c505e70
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// This test ensures that we use `[[no_unique_address]]` in `chunk_view`.
+
+#include <cstddef>
+#include <ranges>
+#include <string>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct input_view {
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<input_view> = true;
+static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
+using CV1 = std::ranges::chunk_view<input_view>;
+// Expected CV1 (with View == input) layout:
+// [[no_unique_address]] _View __base_                                         // size: 0
+// [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] __non_propagating_cache<iterator_t<_View>> __current_ // size: sizeof(__non_propagating_cache<cpp20_input_iterator<int*>>), align: std::ptrdiff_t
+static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>) == alignof(std::ptrdiff_t));
+static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
+                                 sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
+
+using CV2 = std::ranges::chunk_view<forward_view>;
+// Expected CV2 (with View >= forward) layout:
+// [[no_unique_address]] _View __base_             // size: 0
+// [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
+static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..5e557715f4b03
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
+  auto view     = range | std::views::chunk(2);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(std::as_const(view)).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(view).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(view.begin());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(std::as_const(view).begin());
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 5116864879485..41e1603f62350 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -270,17 +270,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -387,17 +381,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
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 b3b424a1d77ce..bd1b55ccc8dfa 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
@@ -5700,17 +5700,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -7619,17 +7613,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
new file mode 100644
index 0000000000000..657e16383256b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   std::views::chunk
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                       = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::ref_view<std::array<int, 8>> view = array | std::views::all;
+
+  // Test `views::chunk(view, n)`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        std::views::chunk(view, 2);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::views::chunk(std::as_const(view), 2);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2}));
+  }
+
+  // Test `views::chunk(n)(range)`
+  {
+    static_assert(noexcept(std::views::chunk(2)));
+    /*__pipable*/ auto adaptor = std::views::chunk(3);
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        adaptor(view);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        adaptor(std::as_const(view));
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3}));
+  }
+
+  // Test `view | views::chunk`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        view | std::views::chunk(4);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3, 4}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::as_const(view) | std::views::chunk(4);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3, 4}));
+  }
+
+  // Test `views::chunk | adaptor`
+  {
+    /*__pipable*/ auto adaptors            = std::views::chunk(5) | std::views::join;
+    std::ranges::input_range auto rejoined = view | adaptors;
+    assert(std::ranges::equal(rejoined, view));
+    std::ranges::input_range auto const_rejoined = std::as_const(view) | adaptors;
+    assert(std::ranges::equal(const_rejoined, view));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
new file mode 100644
index 0000000000000..ac17f97a0a292
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() &&;
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <memory>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                                                   = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>> chunked = array | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
+      std::as_const(array) | std::views::chunk(4);
+
+  // Test `chunk_view.base()`
+  {
+    std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
+    assert(std::addressof(base) == std::addressof(array));
+
+    std::same_as<const std::array<int, 8>&> decltype(auto) const_base = const_chunked.base().base();
+    assert(std::addressof(const_base) == std::addressof(array));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
new file mode 100644
index 0000000000000..3cccd7d1bebc6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr __outer_iterator begin();
+
+//   V models forward_range:
+//     constexpr auto begin() requires (!__simple_view<V>);
+//     constexpr auto begin() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.begin()` when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == input_chunked.end());
+  }
+
+  // Test `chunk_view.begin()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == chunked.end());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.begin();
+    assert(std::ranges::equal(*const_it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++const_it, std::vector{7, 8}));
+    assert(++const_it == const_chunked.end());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..9bf2baba58c78
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class R>
+//   chunk_view(R&&, range_difference_t<R>) -> chunk_view<all_t<R>>;
+
+#include <ranges>
+
+struct view : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct range {
+  int* begin() const;
+  int* end() const;
+};
+
+struct borrowed_range {
+  int* begin() const;
+  int* end() const;
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<borrowed_range> = true;
+
+void test_ctad() {
+  view v;
+  range r;
+  borrowed_range br;
+
+  // clang-format off
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(v, 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(v), 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(r, 0)), 
+                                      std::ranges::chunk_view<std::ranges::ref_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(r), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(br, 0)),
+                                      std::ranges::chunk_view<std::ranges::ref_view<borrowed_range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(br), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<borrowed_range>>>);
+  // clang-format on
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
new file mode 100644
index 0000000000000..73eb0d967f6a2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr default_sentinel_t end();
+
+//   V moduels forward_range:
+//     constexpr auto end() requires (!__simple_view<V>);
+//     constexpr auto end() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.end()` when V models only input_range
+  {
+    static_assert(noexcept(input_chunked.end()));
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it = input_chunked.end();
+  }
+
+  // Test `chunk_view.end()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--it, std::vector{1, 2, 3}));
+    assert(it == chunked.begin());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.end();
+    assert(std::ranges::equal(*--const_it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--const_it, std::vector{1, 2, 3}));
+    assert(const_it == const_chunked.begin());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
new file mode 100644
index 0000000000000..89891bc093080
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   General tests for chunk_view. This file does not test anything specifically.
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <string_view>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::string_view str = "Cheese with chicken chunk by chunk on truck with my trick";
+  // clang-format off
+  auto str2 = str 
+            | std::views::chunk(4)
+            | std::views::join
+            | std::views::chunk(314159)
+            | std::views::take(1)
+            | std::views::join
+            | std::views::lazy_split(' ')
+            | std::views::chunk(2)
+            | std::views::transform([] (auto&& subview)
+              {
+                return subview | std::views::join_with(' ');
+              })
+            | std::views::join_with(' ');
+  // clang-format on
+  assert(std::ranges::equal(str, str2));
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
new file mode 100644
index 0000000000000..d86726d958e6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator<(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator<=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base> &&
+//                three_way_comparable<iterator_t<Base>>;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t)`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    std::ranges::advance(it, 4);
+    assert(it == std::default_sentinel);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(input_chunked.end() - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - input_chunked.end() == -4);
+  }
+
+  // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() == chunked.begin());
+    assert(chunked.end() == chunked.end());
+  }
+
+  // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() < chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() > chunked.begin());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() <= chunked.begin());
+    assert(chunked.begin() <= chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() >= chunked.end());
+    assert(chunked.end() >= chunked.begin());
+  }
+
+  // Test `friend constexpr auto operator<=>(const iterator& x, const iterator& y)`
+  {
+    assert((chunked.begin() <=> chunked.begin()) == std::strong_ordering::equal);
+    assert((chunked.begin() <=> chunked.end()) == std::strong_ordering::less);
+    assert((chunked.end() <=> chunked.begin()) == std::strong_ordering::greater);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
new file mode 100644
index 0000000000000..397b32e73080a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+//   constexpr iterator& operator--()
+//     requires bidirectional_range<Base>;
+//   constexpr iterator operator--(int)
+//     requires bidirectional_range<Base>;
+//   constexpr iterator& operator-=(difference_type)
+//     requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+
+  // Test `constexpr iterator& operator--();`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator operator--(int)`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    it--;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator& operator-=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.end();
+    it -= 3;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
new file mode 100644
index 0000000000000..b8772d0fac3d2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr value_type outer_iterator::operator*() const;
+//     constexpr inner_iterator outer_iterator::value_type::begin() const noexcept;
+//     constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept;
+//     constexpr range_reference_v<V> inner_iterator::operator*() const;
+
+//   V models forward_range:
+//     constexpr value_type iterator::operator*() const;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `constexpr value_type outer_iterator::operator*() const`
+  {
+    static_assert(std::ranges::input_range<decltype(*input_chunked.begin())>);
+  }
+
+  // Test `constexpr inner_iterator outer_iterator::value_type::begin() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    assert(*inner.begin() == *vector.begin());
+    static_assert(noexcept(inner.begin()));
+  }
+
+  // Test `constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it                   = inner.end();
+    static_assert(noexcept((inner.end())));
+  }
+
+  // Test `constexpr value_type iterator::operator*() const`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::same_as<int> decltype(auto) v                           = *it;
+    assert(v == 1);
+  }
+
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
+  {
+    std::same_as<int&> decltype(auto) v = *(*chunked.begin()).begin();
+    assert(v == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
new file mode 100644
index 0000000000000..6048aba027ce9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     constexpr outer_iterator& operator++();
+//     constexpr void operator++(int);
+//     constexpr inner_iterator& operator++();
+//     constexpr void operator++(int);
+
+//   V models forward_range
+//     constexpr iterator& operator++();
+//     constexpr iterator operator++(int);
+//     constexpr iterator& operator+=(difference_type)
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(2);
+
+  // Test `constexpr outer_iterator& operator++();`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr void operator++(int);`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    assert(*++it == 2);
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(*it == 2);
+  }
+
+  // Test `constexpr iterator& operator++();`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator operator++(int)`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator& operator+=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    it += 3;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..09d16c9a48af0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f61d6f991cb15..49fb196903e77 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1106,7 +1106,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_ranges_chunk",
             "values": {"c++23": 202202},
             "headers": ["ranges"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ranges_chunk_by",

>From af914e794ddb86103e6b7acf3bf17d40f5055a8a Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 01:44:18 +0800
Subject: [PATCH 02/29] Code Review: adjust release notes

Note that we will merge this commit with the corresponding change in #172948 in the future.
---
 libcxx/docs/ReleaseNotes/22.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index d2a2d1b712689..30fc22fc0d787 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,7 +54,7 @@ Implemented Papers
 - 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>`__)
-- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------

>From dc62f798ba5615dcac60fffa2cc0afa8b79344c5 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 13:03:41 +0800
Subject: [PATCH 03/29] Add Tests: test if `_LIBCPP_ASSERT_XXX` works when
 `views::chunk` meets an undefined behaviour

---
 libcxx/include/__ranges/chunk_view.h          |  6 ++-
 .../range.chunk/assert.constructor.pass.cpp   | 39 ++++++++++++++
 .../range.chunk.iter/assert.advance.pass.cpp  | 32 ++++++++++++
 .../range.chunk.iter/assert.deref.pass.cpp    | 52 +++++++++++++++++++
 .../assert.increment.pass.cpp                 | 52 +++++++++++++++++++
 .../ranges/range.adaptors/range.chunk/types.h | 50 ++++++++++++++++++
 6 files changed, 229 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f19d2c9ede8a7..683b6bbaa2ca5 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -128,11 +128,12 @@ class chunk_view<_View>::__outer_iterator {
   _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
-    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
     return value_type(*__parent_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
     __parent_->__remainder_ = __parent_->__n_;
     return *this;
@@ -214,6 +215,7 @@ class chunk_view<_View>::__inner_iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ++*__parent_->__current_;
     if (*__parent_->__current_ == ranges::end(__parent_->__base_))
       __parent_->__remainder_ = 0;
@@ -386,7 +388,7 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
-    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to increment past-the-end chunk_view iterator");
     __missing_ = ranges::advance(__current_, __n_, __end_);
     return *this;
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
new file mode 100644
index 0000000000000..03fc7dfb6a5de
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Construct a chunk_view with chunk size <= 0
+
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3};
+
+  // Test constructor when V models only input_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  // Test constructor when V models forward_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
new file mode 100644
index 0000000000000..af2b826aa8eba
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to advance chunk_view iterator out of range
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+
+  // Test advance past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    TEST_LIBCPP_ASSERT_FAILURE(it += 4, "Trying to advance chunk_view iterator out of range");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
new file mode 100644
index 0000000000000..cb1d92885e684
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to dereference past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test dereference iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*outer_it, "Trying to dereference past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*inner_it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+
+  // Test dereference iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(*it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
new file mode 100644
index 0000000000000..8160ee1bb3e14
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to increment past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test increment past-the-end iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++outer_it, "Trying to increment past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++inner_it, "Trying to increment past-the-end chunk_view iterator");
+  }
+
+  // Test increment past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(++it, "Trying to increment past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..a5d688abfa1b0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif

>From b664c0cb8f8ed9b46629fa49e4bdd846734615d4 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:26 +0800
Subject: [PATCH 04/29] Fix CI: adjust `#include` path in `test/libcxx` files

---
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp          | 2 +-
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index cb1d92885e684..0c96742147189 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 8160ee1bb3e14..451683bab71b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};

>From 4dc9b06c0dd515030226b9d27841352ef62549a3 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:58 +0800
Subject: [PATCH 05/29] Fix CI: add template deduction guide for
 `input_span<T>`

---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 3 +++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..0978a23c37244 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..4ff1af1d7cda3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 1e8906ad49a2406158161cc1b9d0e1570af2959d Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:53:21 +0800
Subject: [PATCH 06/29] Code Review: add `REQUIRES: std-at-least-c++23` in all
 the tests related to `views::chunk`.

The previous commit, 4dc9b06c0dd515030226b9d27841352ef62549a3, which introduce manual/explicit template deduction guide in `input_span<T>`, is always implicitly-provided under -std=c++23. So this commit also reverts the changes about `types.h` in the previous commit.
---
 .../range.adaptors/range.chunk/assert.constructor.pass.cpp     | 1 +
 .../range.chunk/range.chunk.iter/assert.advance.pass.cpp       | 1 +
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp         | 1 +
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp     | 1 +
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h   | 3 ---
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h      | 3 ---
 6 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
index 03fc7dfb6a5de..477f89be5f070 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
index af2b826aa8eba..9b91d94841b91 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index 0c96742147189..3640b04192bf4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 451683bab71b0..61c096f74158e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index 0978a23c37244..a5d688abfa1b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 4ff1af1d7cda3..09d16c9a48af0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 7e21388c70b38c32125e4d3eda9b3a4c66e1d3db Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 14:13:59 +0800
Subject: [PATCH 07/29] Fix CI: add template deduction guide for
 `input_span<T>` again.

Seems that even in C++23 the CI fails due to no implicit deduction guide, here we just add the explicit one and make the CI to basically run ok.
---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 4 ++++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..4852ec29204bb 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..7533e41713d72 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From f1ed40a1aed2a1f5ebd1e56efcd6ae2bbb25dc26 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:07:21 +0800
Subject: [PATCH 08/29] Code review: Update libcxx/test synopsis

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 .../ranges/range.adaptors/range.chunk/nodiscard.verify.cpp      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 5e557715f4b03..9264256fc6a52 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+// Test that functions are marked [[nodiscard]].
 
 #include <ranges>
 #include <utility>

>From 12bec119ba49cd6016055f5887f5e83efa25ee30 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:21:29 +0800
Subject: [PATCH 09/29] Code review: Fix synopsis.

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index ac17f97a0a292..b45e1f59f1394 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() const& requires copy_constructible<V>;
 //   constexpr V base() &&;
 
 #include <array>

>From 1e05c5a038dd4cee5a0049b8c7e92e59c8aabd82 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 21:13:46 +0800
Subject: [PATCH 10/29] Code review: sort and unique the tests

---
 .../no_unique_address.compile.pass.cpp        |  24 ++--
 .../range.chunk/nodiscard.verify.cpp          |  35 ++----
 .../range.chunk.iter/arithmetic.pass.cpp      | 108 ++++++++++++++++++
 .../range.chunk.iter/compare.pass.cpp         |  29 +++--
 .../range.chunk.iter/subscript.pass.cpp       |  42 +++++++
 5 files changed, 189 insertions(+), 49 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
index 14c618c505e70..5d32daf4c178a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -20,6 +20,8 @@
 #include "test_iterators.h"
 #include "test_range.h"
 
+// When V models only input_range
+
 struct input_view {
   cpp20_input_iterator<int*> begin() const;
   sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
@@ -28,16 +30,8 @@ template <>
 inline constexpr bool std::ranges::enable_view<input_view> = true;
 static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
 
-struct forward_view {
-  int* begin() const;
-  int* end() const;
-};
-template <>
-inline constexpr bool std::ranges::enable_view<forward_view> = true;
-static_assert(std::ranges::forward_range<forward_view>);
-
 using CV1 = std::ranges::chunk_view<input_view>;
-// Expected CV1 (with View == input) layout:
+// Expected CV1 (when V models only input_range) layout:
 // [[no_unique_address]] _View __base_                                         // size: 0
 // [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
 // [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
@@ -46,8 +40,18 @@ static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<
 static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
                                  sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
 
+// When V models forward_range
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
 using CV2 = std::ranges::chunk_view<forward_view>;
-// Expected CV2 (with View >= forward) layout:
+// Expected CV2 (when V models forward_range) layout:
 // [[no_unique_address]] _View __base_             // size: 0
 // [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
 static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 9264256fc6a52..4d0fb4f76857c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -19,15 +19,6 @@ void test() {
   char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
   auto view     = range | std::views::chunk(2);
 
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   view.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -43,18 +34,19 @@ void test() {
   std::as_const(view).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  view.end();
+  std::ranges::iter_move(view.begin());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(view).end();
+  std::ranges::iter_move(std::as_const(view).begin());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
+  view.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
+  std::as_const(view).end();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
+  std::ranges::iter_move(view.end());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
+  std::ranges::iter_move(std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   *view.begin();
@@ -71,16 +63,11 @@ void test() {
   (std::as_const(view).begin() == std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == std::as_const(view).end());
+  std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == std::as_const(view).end());
-
+  std::views::chunk(range, 3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(view.begin());
+  range | std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(std::as_const(view).begin());
+  std::views::reverse | std::views::chunk(3);
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..6baa2fce13695
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const inner_iterator& x)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr iterator operator+(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator+(difference_type n, const iterator& i)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator-(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+//       requires sized_sentinel_for<iterator_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(std::default_sentinel - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - std::default_sentinel == -4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t, const inner_iterator& x)`
+  {
+    assert(std::default_sentinel - (*input_chunked.begin()).begin() == 3);
+  }
+
+  // Test `friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t)`
+  {
+    assert((*input_chunked.begin()).begin() - std::default_sentinel == -3);
+  }
+
+  // Test `friend constexpr iterator operator+(const iterator& i, difference_type n)`
+  {
+    assert(chunked.begin() + 4 == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator+(difference_type n, const iterator& i)`
+  {
+    assert(4 + chunked.begin() == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator-(const iterator& i, difference_type n)`
+  {
+    assert(chunked.end() - 4 == chunked.begin());
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)`
+  {
+    assert(std::default_sentinel - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)`
+  {
+    assert(chunked.begin() - std::default_sentinel == -4);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
index d86726d958e6e..ea6731cccce85 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -11,14 +11,12 @@
 // <ranges>
 
 //   V models only input_range
-//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
-//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
-//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t);
 
 //   V models forward_range
-//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator==(const iterator& x, const iterator& y);
+//     friend constexpr bool operator==(const iterator& x, default_sentinel_t);
 //     friend constexpr bool operator<(const iterator& x, const iterator& y)
 //       requires random_access_range<Base>;
 //     friend constexpr bool operator>(const iterator& x, const iterator& y)
@@ -31,7 +29,6 @@
 //       requires random_access_range<Base> &&
 //                three_way_comparable<iterator_t<Base>>;
 
-#include <algorithm>
 #include <cassert>
 #include <compare>
 #include <iterator>
@@ -53,14 +50,11 @@ constexpr bool test() {
     assert(it == std::default_sentinel);
   }
 
-  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  // Test `friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t)`
   {
-    assert(input_chunked.end() - input_chunked.begin() == 4);
-  }
-
-  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
-  {
-    assert(input_chunked.begin() - input_chunked.end() == -4);
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::ranges::advance(it, 3);
+    assert(it == std::default_sentinel);
   }
 
   // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
@@ -69,6 +63,11 @@ constexpr bool test() {
     assert(chunked.end() == chunked.end());
   }
 
+  // Test `friend constexpr bool operator==(const iterator& x, default_sentinel)`
+  {
+    assert(chunked.end() == std::default_sentinel);
+  }
+
   // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() < chunked.end());
@@ -79,7 +78,7 @@ constexpr bool test() {
     assert(chunked.end() > chunked.begin());
   }
 
-  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  // Test `friend constexpr bool operator<=(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() <= chunked.begin());
     assert(chunked.begin() <= chunked.end());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
new file mode 100644
index 0000000000000..9b45fc6d779bf
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models forward_range
+//     constexpr value_type iterator::operator[](difference_type n) const
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+
+  // Test `constexpr value_type iterator::operator[](difference_type n) const`
+  {
+    assert(std::ranges::equal(chunked.begin()[1], std::vector{4, 5, 6}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 033a1c16289990a16b1f674f380f7a72f38f48ea Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 14:30:08 -0400
Subject: [PATCH 11/29] Fix release notes

---
 libcxx/docs/ReleaseNotes/22.rst    | 1 -
 libcxx/docs/ReleaseNotes/23.rst    | 1 +
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0637ba98976a2..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -55,7 +55,6 @@ Implemented Papers
 - 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>`__)
-- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e7eb42207db42..a98f42da70f00 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -52,6 +52,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::chunk`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 6abcac5e4ebca..8c92d89be999b 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""

>From 7fe126d5e7c88787f26228c64bcb5f89977c5b83 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:02:10 +0800
Subject: [PATCH 12/29] Resolve overload conflicts on `__div_ceil` in
 `ranges::chunk_view` and `ranges::stride_view`.

Both `ranges::chunk_view` and `ranges::stride_view` need a helper function called `__div_ceil` that solves some simple maths. According to the style of other libcxx internal helper functions (e.g. `__priority_tag`, `__small_buffer`), it seems that we can put `__div_ceil` into a new file `__utility/div_ceil.h`. However, most files in `__utility/` refer to a corresponding symbol in `<utility>` header, so that if someone feels this is not proper, I'll be glad to revert it.
---
 libcxx/include/CMakeLists.txt         |  1 +
 libcxx/include/__ranges/chunk_view.h  | 21 ++++++-----------
 libcxx/include/__ranges/stride_view.h | 20 +++++-----------
 libcxx/include/__utility/div_ceil.h   | 33 +++++++++++++++++++++++++++
 libcxx/include/module.modulemap.in    |  1 +
 5 files changed, 48 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/include/__utility/div_ceil.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 76b93c17c3312..0e9f19a81b1ab 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -935,6 +935,7 @@ set(files
   __utility/convert_to_integral.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
+  __utility/div_ceil.h
   __utility/element_count.h
   __utility/empty.h
   __utility/exception_guard.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 683b6bbaa2ca5..68d4408295e47 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -36,6 +36,7 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 
@@ -52,14 +53,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Integral>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
-  _Integral __r = __num / __denom;
-  if (__num % __denom)
-    ++__r;
-  return __r;
-}
-
 template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
@@ -98,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -153,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -321,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -510,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..7ea68b13330c3 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -46,15 +47,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Value>
-_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) {
-  _Value __r = __left / __right;
-  if (__left % __right) {
-    ++__r;
-  }
-  return __r;
-}
-
 template <input_range _View>
   requires view<_View>
 class stride_view : public view_interface<stride_view<_View>> {
@@ -121,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -340,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -ranges::__div_ceil(-__n, __x.__stride_);
+      return -__div_ceil(-__n, __x.__stride_);
     }
-    return ranges::__div_ceil(__n, __x.__stride_);
+    return __div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)
diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
new file mode 100644
index 0000000000000..595c590b9e3f6
--- /dev/null
+++ b/libcxx/include/__utility/div_ceil.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_DIV_CEIL_H
+#define _LIBCPP___UTILITY_DIV_CEIL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __div_ceil computes the ceiling of the division of two integers. It is mainly used by
+// range adaptors like chunk_view and stride_view.
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_DIV_CEIL_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 64238440b9dd7..ef5d15d063f69 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2200,6 +2200,7 @@ module std [system] {
     module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
+    module div_ceil                        { header "__utility/div_ceil.h" }
     module element_count                   { header "__utility/element_count.h" }
     module exception_guard                 { header "__utility/exception_guard.h" }
     module exchange                        { header "__utility/exchange.h" }

>From d27b355e67e17b0ed15d44a247fdba1c3b13ed49 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:22:38 +0800
Subject: [PATCH 13/29] Fix test error that `constexpr` is not supported in
 `-std=c++03`

---
 libcxx/include/__utility/div_ceil.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 595c590b9e3f6..4d2868465c141 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,6 +17,8 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if _LIBCPP_STD_VER >= 11
+
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
 
@@ -28,6 +30,8 @@ template <class _Integral>
   return __r;
 }
 
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___UTILITY_DIV_CEIL_H

>From a1a5918bb79d29cbe7be2736da7a72b83ee0afbb Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:38:37 +0800
Subject: [PATCH 14/29] Fix test error again that `constexpr` is not supported
 in `-std=c++11`

---
 libcxx/include/__utility/div_ceil.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 4d2868465c141..21e9bcb30d1a1 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,7 +17,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 11
+#if _LIBCPP_STD_VER >= 23
 
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
@@ -30,7 +30,7 @@ template <class _Integral>
   return __r;
 }
 
-#endif
+#endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_END_NAMESPACE_STD
 

>From c2e0e2e926c92ee6d001e060aa5660b681874dfd Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:45:41 +0800
Subject: [PATCH 15/29] Fix test error that `std::as_const` is included from
 `<utility>`.

---
 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp | 1 +
 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
index 3cccd7d1bebc6..29588a57fbcab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -22,6 +22,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
index 73eb0d967f6a2..2f37ac5f73524 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -23,6 +23,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"

>From 7d3cdd73f5da77a7499969fb7954312a19e26630 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 14:19:19 +0800
Subject: [PATCH 16/29] Avoid ADL in `std::__div_ceil`.

---
 libcxx/include/__ranges/chunk_view.h  | 12 ++++++------
 libcxx/include/__ranges/stride_view.h | 10 +++++-----
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 68d4408295e47..1107a9238b2bc 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -91,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -146,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return std::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -314,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -503,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return std::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 7ea68b13330c3..a8b485cbe33e5 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -113,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -332,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -__div_ceil(-__n, __x.__stride_);
+      return -std::__div_ceil(-__n, __x.__stride_);
     }
-    return __div_ceil(__n, __x.__stride_);
+    return std::__div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return std::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)

>From 116c6d85d8a9ad177b1df1975eea92479cb03664 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 16:39:58 +0800
Subject: [PATCH 17/29] [Important] Add missing concepts requirements.

---
 libcxx/include/__ranges/chunk_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1107a9238b2bc..47e6bd56415b6 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -400,7 +400,8 @@ class chunk_view<_View>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires bidirectional_range<_Base> {
     auto __tmp = *this;
     --*this;
     return __tmp;

>From 545bbdf10d24c3790790bfe669b8c756da719838 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Fri, 26 Jun 2026 22:35:12 +0800
Subject: [PATCH 18/29] Cover tests on `chunk_range`. (I'll continue to cover
 tests on `__iterator`, `__inner_iterator` and `__outer_iterator` at the
 weekend)

---
 libcxx/include/__ranges/chunk_view.h          |  3 +-
 .../range.adaptors/range.chunk/base.pass.cpp  | 12 +++-
 .../range.chunk/borrowing.compile.pass.cpp    | 27 +++++++
 .../range.adaptors/range.chunk/ctor.pass.cpp  | 54 ++++++++++++++
 .../range.adaptors/range.chunk/size.pass.cpp  | 72 +++++++++++++++++++
 5 files changed, 166 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 47e6bd56415b6..1798711eabd16 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -401,7 +401,8 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
-    requires bidirectional_range<_Base> {
+    requires bidirectional_range<_Base>
+  {
     auto __tmp = *this;
     --*this;
     return __tmp;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index b45e1f59f1394..4dca324a23695 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -26,7 +26,7 @@ constexpr bool test() {
   std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
       std::as_const(array) | std::views::chunk(4);
 
-  // Test `chunk_view.base()`
+  // Test `chunk_view.base() const&`
   {
     std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
     assert(std::addressof(base) == std::addressof(array));
@@ -35,6 +35,16 @@ constexpr bool test() {
     assert(std::addressof(const_base) == std::addressof(array));
   }
 
+  // Test `chunk_view.base() &&`
+  {
+    std::same_as<std::ranges::ref_view<std::array<int, 8>>> decltype(auto) moved = std::move(chunked).base();
+    assert(std::addressof(moved.base()) == std::addressof(array));
+
+    std::same_as<std::ranges::ref_view<const std::array<int, 8>>> decltype(auto) const_moved =
+        std::move(const_chunked).base();
+    assert(std::addressof(const_moved.base()) == std::addressof(array));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
new file mode 100644
index 0000000000000..1eb4a34f87217
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class V>
+//   inline constexpr bool enable_borrowed_range<chunk_view<V>> =
+//       forward_range<V> && enable_borrowed_range<V>;
+
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+// When V models only `input_range`.
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<input_span<int>>>);
+
+// When V models at least `forward_range`.
+static_assert(std::ranges::enable_borrowed_range<std::ranges::chunk_view<BorrowedView>>);
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
new file mode 100644
index 0000000000000..b60e040b807eb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.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++23
+
+// <ranges>
+
+//   constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n);
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_convertible.h"
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models only `input_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>, input_span<int>, std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(input_span(vector.data(), 8), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+                                    std::ranges::ref_view<std::vector<int>>,
+                                    std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
new file mode 100644
index 0000000000000..820774c3b750e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr auto size() requires sized_range<_View>;
+//   constexpr auto size() const requires sized_range<const _View>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+  // Test `chunk_view.size()` when V models only `input_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<input_span<int>>>);
+    static_assert(!std::ranges::sized_range<const std::ranges::chunk_view<input_span<int>>>);
+
+    auto chunked = input_span<int>(arr, 12) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when V models `forward_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+    static_assert(std::ranges::sized_range<const std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+    const auto& const_chunked = chunked;
+    assert(const_chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when the range is not fully divisible
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(5);
+    assert(chunked.size() == 3);
+  }
+
+  // Test `chunk_view.size()` when the range is empty
+  {
+    static_assert(std::ranges::sized_range<std::ranges::empty_view<int>>);
+    auto chunked = std::views::empty<int> | std::views::chunk(3);
+    assert(chunked.size() == 0);
+  }
+
+  // Test `chunk_view.size()` when chunk size is larger than the range
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(100);
+    assert(chunked.size() == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From e262f9e7497fb3ccf232a3c7265b8faa83f2fa20 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:36:04 +0800
Subject: [PATCH 19/29] Fix chunk_view ctor test for forward ranges.

---
 .../test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
index b60e040b807eb..c0d210aa45dc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
@@ -35,11 +35,11 @@ constexpr bool test() {
 
   // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
   {
-    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+    static_assert(!test_convertible<std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>>,
                                     std::ranges::ref_view<std::vector<int>>,
                                     std::ptrdiff_t>());
 
-    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked(std::ranges::ref_view(vector), 3);
     assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
   }
 

>From bf13c55de976c562120249f2fc7208ea1091fc95 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:39:36 +0800
Subject: [PATCH 20/29] Use consistent copy_constructible spelling in
 chunk_view

---
 libcxx/include/__ranges/chunk_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1798711eabd16..f3edc617de07a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -71,7 +71,7 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
-    requires std::copy_constructible<_View>
+    requires copy_constructible<_View>
   {
     return __base_;
   }

>From f457f267b72fe64a2f22c288da4b096ae0bcc83c Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:48:59 +0800
Subject: [PATCH 21/29] Remove unnecessary no_unique_address from chunk_view
 counts

---
 libcxx/include/__ranges/chunk_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f3edc617de07a..4478af951dbca 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -57,8 +57,8 @@ template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  range_difference_t<_View> __n_;
+  range_difference_t<_View> __remainder_;
   _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
 
   class __outer_iterator;
@@ -256,7 +256,7 @@ template <view _View>
   requires forward_range<_View>
 class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  range_difference_t<_View> __n_;
 
   template <bool _Const>
   class __iterator;

>From 39c371e116736dd37006c3cf48ed9cf9412b1222 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 23:25:01 +0800
Subject: [PATCH 22/29] Fix chunk_view inner iter_move return type (from T into
 T&&)

---
 libcxx/include/__ranges/chunk_view.h          |  2 +-
 .../range.chunk.inner.iter/iter_move.pass.cpp | 43 +++++++++++++++++++
 2 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 4478af951dbca..e9511f027160a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -238,7 +238,7 @@ class chunk_view<_View>::__inner_iterator {
     return -(__s - __i);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_View>
   iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
     return ranges::iter_move(*__i.__parent_->__current_);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp
new file mode 100644
index 0000000000000..5c7b620924c2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   friend constexpr range_rvalue_reference_t<V> iter_move(const inner_iterator& i)
+//     noexcept(noexcept(ranges::iter_move(i.parent_->current_.value())));
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+constexpr bool test() {
+  using InnerIterator =
+      std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<test_view<cpp20_input_iterator>>>>;
+
+  static_assert(std::ranges::input_range<test_view<cpp20_input_iterator>>);
+  static_assert(!std::ranges::forward_range<test_view<cpp20_input_iterator>>);
+
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())), int&&>);
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())),
+                             std::ranges::range_rvalue_reference_t<test_view<cpp20_input_iterator>>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file

>From c1a0811d8824267ff40fa442b2c2ad10d95b7690 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Mon, 20 Jul 2026 00:13:15 +0800
Subject: [PATCH 23/29] Add chunk_view constraint tests

---
 .../range.chunk/concept.compile.pass.cpp      | 64 +++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp
new file mode 100644
index 0000000000000..61b89d4c09c32
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <view V>
+//     requires input_range<V>
+//   class chunk_view;
+//
+//   template <view V>
+//     requires forward_range<V>
+//   class chunk_view<_View>;
+
+#include <ranges>
+#include <utility>
+
+#include "almost_satisfies_types.h"
+#include "test_range.h"
+#include "types.h"
+
+template <>
+inline constexpr bool std::ranges::enable_view<InputRangeNotDerivedFrom> = true;
+
+// Test two separate properties:
+// 1. Whether `chunk_view<View>` can be formed.
+// 2. Whether a well-formed `chunk_view<View>` can be constructed.
+template <class View>
+concept CanFormChunkView = requires { typename std::ranges::chunk_view<View>; };
+
+template <class View>
+concept CanConstructChunkView = CanFormChunkView<View> && requires(View view, std::ranges::range_difference_t<View> n) {
+  std::ranges::chunk_view<View>(std::move(view), n);
+};
+
+// Test constraints when the template argument is not a view
+static_assert(!std::ranges::view<test_non_const_range<cpp17_input_iterator>>);
+static_assert(std::ranges::input_range<test_non_const_range<cpp17_input_iterator>>);
+static_assert(!CanFormChunkView<test_non_const_range<cpp17_input_iterator>>);
+
+// Test constraints when the template argument is not an input_range
+static_assert(!std::ranges::input_range<InputRangeNotDerivedFrom>);
+static_assert(std::ranges::view<InputRangeNotDerivedFrom>);
+static_assert(!CanFormChunkView<InputRangeNotDerivedFrom>);
+
+// Test constraints when the template argument is an input_range and a view
+static_assert(std::ranges::input_range<input_span<int>>);
+static_assert(std::ranges::view<input_span<int>>);
+static_assert(CanFormChunkView<input_span<int>> && CanConstructChunkView<input_span<int>>);
+
+// Test constraints when the template argument is a forward_range and a view
+static_assert(std::ranges::forward_range<BorrowedView>);
+static_assert(std::ranges::view<BorrowedView>);
+static_assert(CanFormChunkView<BorrowedView> && CanConstructChunkView<BorrowedView>);
+
+// chunk_view itself models view
+static_assert(std::ranges::view<std::ranges::chunk_view<input_span<int>>>);
+static_assert(std::ranges::view<std::ranges::chunk_view<BorrowedView>>);

>From 8085a1d3bf45b122557796308e767016f9f7e784 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Mon, 20 Jul 2026 00:13:26 +0800
Subject: [PATCH 24/29] Add chunk_view iterator type tests

---
 .../types.compile.pass.cpp                    | 30 ++++++++++
 .../range.chunk.iter/types.compile.pass.cpp   | 57 +++++++++++++++++++
 .../types.compile.pass.cpp                    | 31 ++++++++++
 3 files changed, 118 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..1775a08f84a2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   class inner_iterator;
+
+//   using inner_iterator::iterator_concept = input_iterator_tag;
+//   using inner_iterator::difference_type = range_difference_t<V>;
+//   using inner_iterator::value_type = range_value_t<V>;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+using InnerIterator = std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<input_span<int>>>>;
+
+static_assert(std::same_as<typename InnerIterator::iterator_concept, std::input_iterator_tag>);
+static_assert(std::same_as<typename InnerIterator::difference_type, std::ranges::range_difference_t<input_span<int>>>);
+static_assert(std::same_as<typename InnerIterator::value_type, std::ranges::range_value_t<input_span<int>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..96e3057edc04a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models forward_range:
+//   class iterator;
+
+//   using iterator::iterator_category = input_iterator_tag;
+//   using iterator::iterator_concept = see below;
+//   using iterator::value_type = decltype(views::take(subrange(current_, end_), n_));
+//   using iterator::difference_type = range_difference_t<Base>;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+template <template <class...> class Iter>
+using ChunkViewFor = std::ranges::chunk_view<test_view<Iter>>;
+
+template <template <class...> class Iter>
+using ChunkIteratorFor = std::ranges::iterator_t<ChunkViewFor<Iter>>;
+
+template <template <class...> class Iter>
+constexpr void test_iterator_types() {
+  using ChunkView     = ChunkViewFor<Iter>;
+  using ChunkIterator = ChunkIteratorFor<Iter>;
+
+  static_assert(std::same_as<typename ChunkIterator::iterator_category, std::input_iterator_tag>);
+  static_assert(std::same_as<typename ChunkIterator::value_type, std::ranges::range_value_t<ChunkView>>);
+  static_assert(std::same_as<typename ChunkIterator::difference_type, std::ranges::range_difference_t<test_view<Iter>>>);
+}
+
+constexpr void test() {
+  test_iterator_types<forward_iterator>();
+  test_iterator_types<bidirectional_iterator>();
+  test_iterator_types<random_access_iterator>();
+  test_iterator_types<contiguous_iterator>();
+
+  static_assert(std::same_as<ChunkIteratorFor<forward_iterator>::iterator_concept, std::forward_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<bidirectional_iterator>::iterator_concept,
+                             std::bidirectional_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<random_access_iterator>::iterator_concept,
+                             std::random_access_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<contiguous_iterator>::iterator_concept,
+                             std::random_access_iterator_tag>);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..9127e567305ce
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   class outer_iterator;
+
+//   using outer_iterator::iterator_concept = input_iterator_tag;
+//   using outer_iterator::difference_type = range_difference_t<V>;
+//   class outer_iterator::value_type;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+using OuterIterator = std::ranges::iterator_t<std::ranges::chunk_view<input_span<int>>>;
+
+static_assert(std::same_as<typename OuterIterator::iterator_concept, std::input_iterator_tag>);
+static_assert(std::same_as<typename OuterIterator::difference_type, std::ranges::range_difference_t<input_span<int>>>);
+static_assert(std::same_as<typename OuterIterator::value_type, std::iter_value_t<OuterIterator>>);
+static_assert(std::ranges::input_range<typename OuterIterator::value_type>);

>From 48bf130465c89823e4c9817180cfa15434057466 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:22:10 +0800
Subject: [PATCH 25/29] Add chunk_view inner iterator special member tests

---
 .../ctor.compile.pass.cpp                     | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp
new file mode 100644
index 0000000000000..ec2e68cb0c8ca
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   inner_iterator(inner_iterator&&) = default;
+//   inner_iterator& operator=(inner_iterator&&) = default;
+
+#include <concepts>
+#include <ranges>
+
+#include "../types.h"
+
+using InnerIterator = std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<input_span<int>>>>;
+
+static_assert(!std::default_initializable<InnerIterator>);
+static_assert(!std::copy_constructible<InnerIterator>);
+static_assert(!std::assignable_from<InnerIterator&, const InnerIterator&>);
+static_assert(std::move_constructible<InnerIterator>);
+static_assert(std::assignable_from<InnerIterator&, InnerIterator>);

>From 5d32ff5424b490676c9771699233a396532fea1b Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:37:21 +0800
Subject: [PATCH 26/29] Add chunk_view inner iterator base() test

---
 .../range.chunk.inner.iter/base.pass.cpp      | 44 +++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp
new file mode 100644
index 0000000000000..9546aecb01560
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   constexpr const iterator_t<V> base() const&;
+
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int buffer[] = {1, 2, 3, 4};
+
+  std::ranges::chunk_view<input_span<int>> chunked(input_span<int>(buffer, 4), 2);
+  auto outer = chunked.begin();
+  auto inner = (*outer).begin();
+
+  std::same_as<const std::ranges::iterator_t<input_span<int>>> decltype(auto) base = inner.base();
+  assert(*base == 1);
+
+  ++inner;
+  assert(*inner.base() == 2);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 2c8a8c62abfca361ca45933de0da5eeebd68b290 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:48:43 +0800
Subject: [PATCH 27/29] Fix input_span operator* to return a reference

---
 .../range.chunk/range.chunk.iter/deref.pass.cpp               | 4 ++--
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h     | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
index b8772d0fac3d2..32054584f4358 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -53,10 +53,10 @@ constexpr bool test() {
     static_assert(noexcept((inner.end())));
   }
 
-  // Test `constexpr value_type iterator::operator*() const`
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
   {
     /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
-    std::same_as<int> decltype(auto) v                           = *it;
+    std::same_as<int&> decltype(auto) v                          = *it;
     assert(v == 1);
   }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 7533e41713d72..83e383e98110e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -23,7 +23,7 @@ struct input_span : std::span<T> {
     using iterator_concept = std::input_iterator_tag;
     constexpr iterator()   = default;
     constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
-    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    constexpr decltype(auto) operator*() const { return std::span<T>::iterator::operator*(); }
     friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
     friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
     friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;

>From 32f132912f08cfd18cbf8fffff80ee42d178b745 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:48:48 +0800
Subject: [PATCH 28/29] Add chunk_view inner iterator iter_swap test

---
 .../range.chunk.inner.iter/iter_swap.pass.cpp | 50 +++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp
new file mode 100644
index 0000000000000..b5223d5091982
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   friend constexpr void iter_swap(const inner_iterator& x, const inner_iterator& y)
+//     noexcept(noexcept(ranges::iter_swap(x.parent_->current_.value(), y.parent_->current_.value())))
+//     requires indirectly_swappable<iterator_t<V>>;
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int a[] = {1, 2, 3, 4};
+  int b[] = {5, 6, 7, 8};
+
+  // Each chunk_view owns its own `current_` cache, so use two independent views to observe a real swap.
+  std::ranges::chunk_view<input_span<int>> chunked_a(input_span<int>(a, 4), 2);
+  std::ranges::chunk_view<input_span<int>> chunked_b(input_span<int>(b, 4), 2);
+
+  auto inner_a = (*chunked_a.begin()).begin();
+  auto inner_b = (*chunked_b.begin()).begin();
+
+  assert(a[0] == 1);
+  assert(b[0] == 5);
+
+  std::ranges::iter_swap(inner_a, inner_b);
+
+  assert(a[0] == 5);
+  assert(b[0] == 1);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 237fa79aa5d1cc327884fef2dd96529686912fa6 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:51:42 +0800
Subject: [PATCH 29/29] Add chunk_view outer iterator value_type size test

---
 .../range.chunk.outer.iter/size.pass.cpp      | 55 +++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/size.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/size.pass.cpp
new file mode 100644
index 0000000000000..51c7cac8da280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/size.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   constexpr auto outer_iterator::value_type::size() const
+//     requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test `size()` when the range is fully divisible by the chunk size.
+  {
+    std::ranges::chunk_view<input_span<int>> chunked(input_span<int>(arr, 8), 4);
+    auto outer = chunked.begin();
+
+    assert((*outer).size() == 4);
+    ++outer;
+    assert((*outer).size() == 4);
+  }
+
+  // Test `size()` when the last chunk is smaller than the chunk size.
+  {
+    std::ranges::chunk_view<input_span<int>> chunked(input_span<int>(arr, 8), 3);
+    auto outer = chunked.begin();
+
+    assert((*outer).size() == 3);
+    ++outer;
+    assert((*outer).size() == 3);
+    ++outer;
+    assert((*outer).size() == 2);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Sat Jul 25 01:02:51 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 01:02:51 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2442R1
	`std::views::chunk` (PR #171234)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171234@github.com>
Message-ID: <6a646dab.83ce629a.320329.f3d5@mx.google.com>


https://github.com/shyeyian updated https://github.com/llvm/llvm-project/pull/171234

>From f6554ea2a0e1d9571cf82fc7b2f3d1115c466474 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at petalmail.com>
Date: Wed, 24 Dec 2025 22:05:29 +0800
Subject: [PATCH 01/30] Implement `views::chunk`.

This commit squashes previous commits.
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/22.rst               |   1 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/chunk_view.h          | 562 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/ranges                         |  12 +
 libcxx/include/version                        |   2 +-
 libcxx/modules/std/ranges.inc                 |  11 +-
 .../no_unique_address.compile.pass.cpp        |  53 ++
 .../range.chunk/nodiscard.verify.cpp          |  86 +++
 .../ranges.version.compile.pass.cpp           |  32 +-
 .../version.version.compile.pass.cpp          |  32 +-
 .../range.chunk/adaptor.pass.cpp              |  75 +++
 .../range.adaptors/range.chunk/base.pass.cpp  |  46 ++
 .../range.adaptors/range.chunk/begin.pass.cpp |  67 +++
 .../range.chunk/ctad.compile.pass.cpp         |  55 ++
 .../range.adaptors/range.chunk/end.pass.cpp   |  65 ++
 .../range.chunk/general.pass.cpp              |  48 ++
 .../range.chunk.iter/compare.pass.cpp         | 109 ++++
 .../range.chunk.iter/decrement.pass.cpp       |  60 ++
 .../range.chunk.iter/deref.pass.cpp           |  77 +++
 .../range.chunk.iter/increment.pass.cpp       |  95 +++
 .../ranges/range.adaptors/range.chunk/types.h |  50 ++
 .../generate_feature_test_macro_components.py |   1 -
 25 files changed, 1494 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/chunk_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..8e57305f32ad1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -370,7 +370,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_rvalue``                             ``202207L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_chunk``                                 *unimplemented*
+    ``__cpp_lib_ranges_chunk``                                 ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                              ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0d1a1fbc00f2c..d2a2d1b712689 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,6 +54,7 @@ Implemented Papers
 - 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>`__)
+- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e4c6cb0f51065..8c8ab211e5bf4 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__","Only ``ranges::iota`` is implemented."
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__",""
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index eb423ae923b4f..2426da6848b51 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -707,6 +707,7 @@ set(files
   __ranges/all.h
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
+  __ranges/chunk_view.h
   __ranges/common_view.h
   __ranges/concepts.h
   __ranges/container_compatible_range.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
new file mode 100644
index 0000000000000..f19d2c9ede8a7
--- /dev/null
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_CHUNK_VIEW_H
+#define _LIBCPP___RANGES_CHUNK_VIEW_H
+
+#include <__algorithm/ranges_min.h>
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/take_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
+
+  class __outer_iterator;
+  class __inner_iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n), __remainder_(0) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires std::copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator begin() {
+    __current_.__emplace(ranges::begin(__base_));
+    __remainder_ = __n_;
+    return __outer_iterator(*this);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept {
+    return std::default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(chunk_view& __parent)
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  class value_type;
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator(__outer_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    return value_type(*__parent_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
+    __parent_->__remainder_ = __parent_->__n_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __outer_iterator& __i, default_sentinel_t) {
+    return *__i.__parent_->__current_ == ranges::end(__i.__parent_->__base_) && __i.__parent_->__remainder_ != 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __outer_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
+    if (__dist < __i.__parent_->__remainder_)
+      return __dist == 0 ? 0 : 1;
+    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __outer_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator::value_type : public view_interface<value_type> {
+  friend __outer_iterator;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(chunk_view& __parent) : __parent_(std::addressof(__parent)) {}
+
+public:
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator begin() const noexcept {
+    return __inner_iterator(*__parent_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return std::__to_unsigned_like(
+        ranges::min(__parent_->__remainder_, ranges::end(__parent_->__base_) - *__parent_->__current_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__inner_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(chunk_view& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+  using value_type       = range_value_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator(__inner_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator& operator=(__inner_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_View> base() const& { return *__parent_->__current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_reference_t<_View> operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
+    return **__parent_->__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    ++*__parent_->__current_;
+    if (*__parent_->__current_ == ranges::end(__parent_->__base_))
+      __parent_->__remainder_ = 0;
+    else
+      --__parent_->__remainder_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __inner_iterator& __i, default_sentinel_t) {
+    return __i.__parent_->__remainder_ == 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __inner_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return ranges::min(__i.__parent_->__remainder_, ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __inner_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
+    return ranges::iter_move(*__i.__parent_->__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void
+  iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) noexcept(
+      noexcept((ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_))))
+    requires indirectly_swappable<iterator_t<_View>>
+  {
+    return ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_);
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+class chunk_view<_View> : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+
+  template <bool _Const>
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires forward_range<const _View>
+  {
+    return __iterator<true>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    if constexpr (common_range<_View> && sized_range<_View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<false>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<_View> && !bidirectional_range<_View>)
+      return __iterator<false>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires forward_range<const _View>
+  {
+    if constexpr (common_range<const _View> && sized_range<const _View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<true>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<const _View> && !bidirectional_range<const _View>)
+      return __iterator<true>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+template <bool _Const>
+class chunk_view<_View>::__iterator {
+  friend chunk_view;
+
+  using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, chunk_view>;
+  using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
+
+  iterator_t<_Base> __current_         = iterator_t<_Base>();
+  sentinel_t<_Base> __end_             = sentinel_t<_Base>();
+  range_difference_t<_Base> __n_       = 0;
+  range_difference_t<_Base> __missing_ = 0;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      _Parent* __parent, iterator_t<_Base> __current, range_difference_t<_Base> __missing = 0)
+      : __current_(__current), __end_(ranges::end(__parent->__base_)), __n_(__parent->__n_), __missing_(__missing) {}
+
+  [[nodiscard]] static consteval auto __get_iterator_concept() {
+    if constexpr (random_access_range<_Base>)
+      return random_access_iterator_tag{};
+    else if constexpr (bidirectional_range<_Base>)
+      return bidirectional_iterator_tag{};
+    else
+      return forward_iterator_tag{};
+  }
+
+public:
+  using iterator_category = input_iterator_tag;
+  using iterator_concept  = decltype(__iterator::__get_iterator_concept());
+  using value_type        = decltype(views::take(subrange(__current_, __end_), __n_));
+  using difference_type   = range_difference_t<_Base>;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> &&
+                 convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+      : __current_(std::move(__i.__current_)),
+        __end_(std::move(__i.__end_)),
+        __n_(__i.__n_),
+        __missing_(__i.__missing_) {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() const { return __current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to dereference past-the-end chunk_view iterator");
+    return views::take(subrange(__current_, __end_), __n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator[](difference_type __pos) const
+    requires random_access_range<_Base>
+  {
+    return *(*this + __pos);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    __missing_ = ranges::advance(__current_, __n_, __end_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires bidirectional_range<_Base>
+  {
+    ranges::advance(__current_, __missing_ - __n_);
+    __missing_ = 0;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    if (__x > 0) {
+      _LIBCPP_ASSERT_PEDANTIC(ranges::distance(__current_, __end_) > __n_ * (__x - 1),
+                              "Trying to advance chunk_view iterator out of range");
+      ranges::advance(__current_, __n_ * (__x - 1));
+      __missing_ = ranges::advance(__current_, __n_, __end_);
+    } else if (__x < 0) {
+      ranges::advance(__current_, __n_ * __x + __missing_);
+      __missing_ = 0;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    return *this += -__x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
+    return __x.__current_ == __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, default_sentinel_t) {
+    return __x.__current_ == __x.__end_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __x.__current_ < __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __y < __x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__y < __x);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__x < __y);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(difference_type __pos, const __iterator& __i)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator-(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r -= __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, const __iterator& __j)
+    requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+  {
+    return (__i.__current_ - __j.__current_ + __i.__missing_ - __j.__missing_) / __i.__n_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <class _Range>
+chunk_view(_Range&&, range_difference_t<_Range>) -> chunk_view<views::all_t<_Range>>;
+
+template <class _View>
+inline constexpr bool enable_borrowed_range<chunk_view<_View>> = forward_range<_View> && enable_borrowed_range<_View>;
+
+namespace views {
+namespace __chunk {
+struct __fn {
+  template <viewable_range _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range, range_difference_t<_Range> __n) noexcept(
+      noexcept(/*-----*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))))
+      -> decltype(/*--*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))) {
+    return /*---------*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n));
+  }
+
+  template <class _DifferenceType>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_DifferenceType __n) noexcept(is_nothrow_constructible_v<decay_t<_DifferenceType>, _DifferenceType>) {
+    return __pipeable(std::__bind_back(__fn{}, std::forward<_DifferenceType>(__n)));
+  }
+};
+
+} // namespace __chunk
+
+inline namespace __cpo {
+inline constexpr auto chunk = __chunk::__fn{};
+
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_CHUNK_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4434a5446e35b..cb03eb8d14067 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1863,6 +1863,10 @@ module std [system] {
       header "__ranges/chunk_by_view.h"
       export std.functional.bind_back
     }
+    module chunk_view {
+      header "__ranges/chunk_view.h"
+      export std.functional.bind_back
+    }
     module common_view                    { header "__ranges/common_view.h" }
     module concepts                       { header "__ranges/concepts.h" }
     module container_compatible_range     { header "__ranges/container_compatible_range.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 9f725b12ac6c2..9ec6a4a819919 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -396,6 +396,17 @@ namespace std::ranges {
   class chunk_by_view;                                                      // C++23
 
   namespace views { inline constexpr unspecified chunk_by = unspecified; }  // C++23
+
+  // [range.chunk]
+  template <view V>
+    requires input_range<V>
+  class chunk_view;                                                     // C++23
+
+  template <view V>
+    requires forward_range<V>
+  class chunk_view<V>;                                                  // C++23
+
+  namespace views { inline constexpr unspecified chunk = unspecified; } // C++23
 }
 
 namespace std {
@@ -482,6 +493,7 @@ namespace std {
 #    include <__ranges/adjacent_view.h>
 #    include <__ranges/as_rvalue_view.h>
 #    include <__ranges/chunk_by_view.h>
+#    include <__ranges/chunk_view.h>
 #    include <__ranges/from_range.h>
 #    include <__ranges/join_with_view.h>
 #    include <__ranges/repeat_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index ab781466f5ed5..f5fc33674f521 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -523,7 +523,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges                               202406L
 // # define __cpp_lib_ranges_as_const                      202207L
 # define __cpp_lib_ranges_as_rvalue                     202207L
-// # define __cpp_lib_ranges_chunk                         202202L
+# define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
 # define __cpp_lib_ranges_contains                      202207L
 # define __cpp_lib_ranges_find_last                     202207L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 5caa2c052c00f..631ae10c922ae 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -314,18 +314,17 @@ export namespace std {
       using std::ranges::views::adjacent_transform;
       using std::ranges::views::pairwise_transform;
     } // namespace views
+#endif
 
-#endif // _LIBCPP_STD_VER >= 23
-
-#if 0
+#if _LIBCPP_STD_VER >= 23
     using std::ranges::chunk_view;
 
-    using std::ranges::chunk_view<V>;
-
     namespace views {
       using std::ranges::views::chunk;
-    }
+    } // namespace views
+#endif
 
+#if 0
     using std::ranges::slide_view;
 
     namespace views {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
new file mode 100644
index 0000000000000..14c618c505e70
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// This test ensures that we use `[[no_unique_address]]` in `chunk_view`.
+
+#include <cstddef>
+#include <ranges>
+#include <string>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct input_view {
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<input_view> = true;
+static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
+using CV1 = std::ranges::chunk_view<input_view>;
+// Expected CV1 (with View == input) layout:
+// [[no_unique_address]] _View __base_                                         // size: 0
+// [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] __non_propagating_cache<iterator_t<_View>> __current_ // size: sizeof(__non_propagating_cache<cpp20_input_iterator<int*>>), align: std::ptrdiff_t
+static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>) == alignof(std::ptrdiff_t));
+static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
+                                 sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
+
+using CV2 = std::ranges::chunk_view<forward_view>;
+// Expected CV2 (with View >= forward) layout:
+// [[no_unique_address]] _View __base_             // size: 0
+// [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
+static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..5e557715f4b03
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
+  auto view     = range | std::views::chunk(2);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(std::as_const(view)).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(view).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(view.begin());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(std::as_const(view).begin());
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 5116864879485..41e1603f62350 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -270,17 +270,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -387,17 +381,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
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 b3b424a1d77ce..bd1b55ccc8dfa 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
@@ -5700,17 +5700,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -7619,17 +7613,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
new file mode 100644
index 0000000000000..657e16383256b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   std::views::chunk
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                       = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::ref_view<std::array<int, 8>> view = array | std::views::all;
+
+  // Test `views::chunk(view, n)`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        std::views::chunk(view, 2);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::views::chunk(std::as_const(view), 2);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2}));
+  }
+
+  // Test `views::chunk(n)(range)`
+  {
+    static_assert(noexcept(std::views::chunk(2)));
+    /*__pipable*/ auto adaptor = std::views::chunk(3);
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        adaptor(view);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        adaptor(std::as_const(view));
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3}));
+  }
+
+  // Test `view | views::chunk`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        view | std::views::chunk(4);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3, 4}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::as_const(view) | std::views::chunk(4);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3, 4}));
+  }
+
+  // Test `views::chunk | adaptor`
+  {
+    /*__pipable*/ auto adaptors            = std::views::chunk(5) | std::views::join;
+    std::ranges::input_range auto rejoined = view | adaptors;
+    assert(std::ranges::equal(rejoined, view));
+    std::ranges::input_range auto const_rejoined = std::as_const(view) | adaptors;
+    assert(std::ranges::equal(const_rejoined, view));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
new file mode 100644
index 0000000000000..ac17f97a0a292
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() &&;
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <memory>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                                                   = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>> chunked = array | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
+      std::as_const(array) | std::views::chunk(4);
+
+  // Test `chunk_view.base()`
+  {
+    std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
+    assert(std::addressof(base) == std::addressof(array));
+
+    std::same_as<const std::array<int, 8>&> decltype(auto) const_base = const_chunked.base().base();
+    assert(std::addressof(const_base) == std::addressof(array));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
new file mode 100644
index 0000000000000..3cccd7d1bebc6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr __outer_iterator begin();
+
+//   V models forward_range:
+//     constexpr auto begin() requires (!__simple_view<V>);
+//     constexpr auto begin() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.begin()` when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == input_chunked.end());
+  }
+
+  // Test `chunk_view.begin()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == chunked.end());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.begin();
+    assert(std::ranges::equal(*const_it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++const_it, std::vector{7, 8}));
+    assert(++const_it == const_chunked.end());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..9bf2baba58c78
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class R>
+//   chunk_view(R&&, range_difference_t<R>) -> chunk_view<all_t<R>>;
+
+#include <ranges>
+
+struct view : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct range {
+  int* begin() const;
+  int* end() const;
+};
+
+struct borrowed_range {
+  int* begin() const;
+  int* end() const;
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<borrowed_range> = true;
+
+void test_ctad() {
+  view v;
+  range r;
+  borrowed_range br;
+
+  // clang-format off
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(v, 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(v), 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(r, 0)), 
+                                      std::ranges::chunk_view<std::ranges::ref_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(r), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(br, 0)),
+                                      std::ranges::chunk_view<std::ranges::ref_view<borrowed_range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(br), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<borrowed_range>>>);
+  // clang-format on
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
new file mode 100644
index 0000000000000..73eb0d967f6a2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr default_sentinel_t end();
+
+//   V moduels forward_range:
+//     constexpr auto end() requires (!__simple_view<V>);
+//     constexpr auto end() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.end()` when V models only input_range
+  {
+    static_assert(noexcept(input_chunked.end()));
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it = input_chunked.end();
+  }
+
+  // Test `chunk_view.end()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--it, std::vector{1, 2, 3}));
+    assert(it == chunked.begin());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.end();
+    assert(std::ranges::equal(*--const_it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--const_it, std::vector{1, 2, 3}));
+    assert(const_it == const_chunked.begin());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
new file mode 100644
index 0000000000000..89891bc093080
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   General tests for chunk_view. This file does not test anything specifically.
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <string_view>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::string_view str = "Cheese with chicken chunk by chunk on truck with my trick";
+  // clang-format off
+  auto str2 = str 
+            | std::views::chunk(4)
+            | std::views::join
+            | std::views::chunk(314159)
+            | std::views::take(1)
+            | std::views::join
+            | std::views::lazy_split(' ')
+            | std::views::chunk(2)
+            | std::views::transform([] (auto&& subview)
+              {
+                return subview | std::views::join_with(' ');
+              })
+            | std::views::join_with(' ');
+  // clang-format on
+  assert(std::ranges::equal(str, str2));
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
new file mode 100644
index 0000000000000..d86726d958e6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator<(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator<=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base> &&
+//                three_way_comparable<iterator_t<Base>>;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t)`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    std::ranges::advance(it, 4);
+    assert(it == std::default_sentinel);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(input_chunked.end() - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - input_chunked.end() == -4);
+  }
+
+  // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() == chunked.begin());
+    assert(chunked.end() == chunked.end());
+  }
+
+  // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() < chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() > chunked.begin());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() <= chunked.begin());
+    assert(chunked.begin() <= chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() >= chunked.end());
+    assert(chunked.end() >= chunked.begin());
+  }
+
+  // Test `friend constexpr auto operator<=>(const iterator& x, const iterator& y)`
+  {
+    assert((chunked.begin() <=> chunked.begin()) == std::strong_ordering::equal);
+    assert((chunked.begin() <=> chunked.end()) == std::strong_ordering::less);
+    assert((chunked.end() <=> chunked.begin()) == std::strong_ordering::greater);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
new file mode 100644
index 0000000000000..397b32e73080a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+//   constexpr iterator& operator--()
+//     requires bidirectional_range<Base>;
+//   constexpr iterator operator--(int)
+//     requires bidirectional_range<Base>;
+//   constexpr iterator& operator-=(difference_type)
+//     requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+
+  // Test `constexpr iterator& operator--();`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator operator--(int)`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    it--;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator& operator-=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.end();
+    it -= 3;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
new file mode 100644
index 0000000000000..b8772d0fac3d2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr value_type outer_iterator::operator*() const;
+//     constexpr inner_iterator outer_iterator::value_type::begin() const noexcept;
+//     constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept;
+//     constexpr range_reference_v<V> inner_iterator::operator*() const;
+
+//   V models forward_range:
+//     constexpr value_type iterator::operator*() const;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `constexpr value_type outer_iterator::operator*() const`
+  {
+    static_assert(std::ranges::input_range<decltype(*input_chunked.begin())>);
+  }
+
+  // Test `constexpr inner_iterator outer_iterator::value_type::begin() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    assert(*inner.begin() == *vector.begin());
+    static_assert(noexcept(inner.begin()));
+  }
+
+  // Test `constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it                   = inner.end();
+    static_assert(noexcept((inner.end())));
+  }
+
+  // Test `constexpr value_type iterator::operator*() const`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::same_as<int> decltype(auto) v                           = *it;
+    assert(v == 1);
+  }
+
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
+  {
+    std::same_as<int&> decltype(auto) v = *(*chunked.begin()).begin();
+    assert(v == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
new file mode 100644
index 0000000000000..6048aba027ce9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     constexpr outer_iterator& operator++();
+//     constexpr void operator++(int);
+//     constexpr inner_iterator& operator++();
+//     constexpr void operator++(int);
+
+//   V models forward_range
+//     constexpr iterator& operator++();
+//     constexpr iterator operator++(int);
+//     constexpr iterator& operator+=(difference_type)
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(2);
+
+  // Test `constexpr outer_iterator& operator++();`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr void operator++(int);`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    assert(*++it == 2);
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(*it == 2);
+  }
+
+  // Test `constexpr iterator& operator++();`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator operator++(int)`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator& operator+=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    it += 3;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..09d16c9a48af0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f61d6f991cb15..49fb196903e77 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1106,7 +1106,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_ranges_chunk",
             "values": {"c++23": 202202},
             "headers": ["ranges"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ranges_chunk_by",

>From af914e794ddb86103e6b7acf3bf17d40f5055a8a Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 01:44:18 +0800
Subject: [PATCH 02/30] Code Review: adjust release notes

Note that we will merge this commit with the corresponding change in #172948 in the future.
---
 libcxx/docs/ReleaseNotes/22.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index d2a2d1b712689..30fc22fc0d787 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,7 +54,7 @@ Implemented Papers
 - 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>`__)
-- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------

>From dc62f798ba5615dcac60fffa2cc0afa8b79344c5 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 13:03:41 +0800
Subject: [PATCH 03/30] Add Tests: test if `_LIBCPP_ASSERT_XXX` works when
 `views::chunk` meets an undefined behaviour

---
 libcxx/include/__ranges/chunk_view.h          |  6 ++-
 .../range.chunk/assert.constructor.pass.cpp   | 39 ++++++++++++++
 .../range.chunk.iter/assert.advance.pass.cpp  | 32 ++++++++++++
 .../range.chunk.iter/assert.deref.pass.cpp    | 52 +++++++++++++++++++
 .../assert.increment.pass.cpp                 | 52 +++++++++++++++++++
 .../ranges/range.adaptors/range.chunk/types.h | 50 ++++++++++++++++++
 6 files changed, 229 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f19d2c9ede8a7..683b6bbaa2ca5 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -128,11 +128,12 @@ class chunk_view<_View>::__outer_iterator {
   _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
-    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
     return value_type(*__parent_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
     __parent_->__remainder_ = __parent_->__n_;
     return *this;
@@ -214,6 +215,7 @@ class chunk_view<_View>::__inner_iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ++*__parent_->__current_;
     if (*__parent_->__current_ == ranges::end(__parent_->__base_))
       __parent_->__remainder_ = 0;
@@ -386,7 +388,7 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
-    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to increment past-the-end chunk_view iterator");
     __missing_ = ranges::advance(__current_, __n_, __end_);
     return *this;
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
new file mode 100644
index 0000000000000..03fc7dfb6a5de
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Construct a chunk_view with chunk size <= 0
+
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3};
+
+  // Test constructor when V models only input_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  // Test constructor when V models forward_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
new file mode 100644
index 0000000000000..af2b826aa8eba
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to advance chunk_view iterator out of range
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+
+  // Test advance past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    TEST_LIBCPP_ASSERT_FAILURE(it += 4, "Trying to advance chunk_view iterator out of range");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
new file mode 100644
index 0000000000000..cb1d92885e684
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to dereference past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test dereference iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*outer_it, "Trying to dereference past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*inner_it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+
+  // Test dereference iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(*it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
new file mode 100644
index 0000000000000..8160ee1bb3e14
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to increment past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test increment past-the-end iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++outer_it, "Trying to increment past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++inner_it, "Trying to increment past-the-end chunk_view iterator");
+  }
+
+  // Test increment past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(++it, "Trying to increment past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..a5d688abfa1b0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif

>From b664c0cb8f8ed9b46629fa49e4bdd846734615d4 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:26 +0800
Subject: [PATCH 04/30] Fix CI: adjust `#include` path in `test/libcxx` files

---
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp          | 2 +-
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index cb1d92885e684..0c96742147189 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 8160ee1bb3e14..451683bab71b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};

>From 4dc9b06c0dd515030226b9d27841352ef62549a3 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:58 +0800
Subject: [PATCH 05/30] Fix CI: add template deduction guide for
 `input_span<T>`

---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 3 +++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..0978a23c37244 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..4ff1af1d7cda3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 1e8906ad49a2406158161cc1b9d0e1570af2959d Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:53:21 +0800
Subject: [PATCH 06/30] Code Review: add `REQUIRES: std-at-least-c++23` in all
 the tests related to `views::chunk`.

The previous commit, 4dc9b06c0dd515030226b9d27841352ef62549a3, which introduce manual/explicit template deduction guide in `input_span<T>`, is always implicitly-provided under -std=c++23. So this commit also reverts the changes about `types.h` in the previous commit.
---
 .../range.adaptors/range.chunk/assert.constructor.pass.cpp     | 1 +
 .../range.chunk/range.chunk.iter/assert.advance.pass.cpp       | 1 +
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp         | 1 +
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp     | 1 +
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h   | 3 ---
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h      | 3 ---
 6 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
index 03fc7dfb6a5de..477f89be5f070 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
index af2b826aa8eba..9b91d94841b91 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index 0c96742147189..3640b04192bf4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 451683bab71b0..61c096f74158e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index 0978a23c37244..a5d688abfa1b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 4ff1af1d7cda3..09d16c9a48af0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 7e21388c70b38c32125e4d3eda9b3a4c66e1d3db Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 14:13:59 +0800
Subject: [PATCH 07/30] Fix CI: add template deduction guide for
 `input_span<T>` again.

Seems that even in C++23 the CI fails due to no implicit deduction guide, here we just add the explicit one and make the CI to basically run ok.
---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 4 ++++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..4852ec29204bb 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..7533e41713d72 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From f1ed40a1aed2a1f5ebd1e56efcd6ae2bbb25dc26 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:07:21 +0800
Subject: [PATCH 08/30] Code review: Update libcxx/test synopsis

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 .../ranges/range.adaptors/range.chunk/nodiscard.verify.cpp      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 5e557715f4b03..9264256fc6a52 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+// Test that functions are marked [[nodiscard]].
 
 #include <ranges>
 #include <utility>

>From 12bec119ba49cd6016055f5887f5e83efa25ee30 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:21:29 +0800
Subject: [PATCH 09/30] Code review: Fix synopsis.

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index ac17f97a0a292..b45e1f59f1394 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() const& requires copy_constructible<V>;
 //   constexpr V base() &&;
 
 #include <array>

>From 1e05c5a038dd4cee5a0049b8c7e92e59c8aabd82 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 21:13:46 +0800
Subject: [PATCH 10/30] Code review: sort and unique the tests

---
 .../no_unique_address.compile.pass.cpp        |  24 ++--
 .../range.chunk/nodiscard.verify.cpp          |  35 ++----
 .../range.chunk.iter/arithmetic.pass.cpp      | 108 ++++++++++++++++++
 .../range.chunk.iter/compare.pass.cpp         |  29 +++--
 .../range.chunk.iter/subscript.pass.cpp       |  42 +++++++
 5 files changed, 189 insertions(+), 49 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
index 14c618c505e70..5d32daf4c178a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -20,6 +20,8 @@
 #include "test_iterators.h"
 #include "test_range.h"
 
+// When V models only input_range
+
 struct input_view {
   cpp20_input_iterator<int*> begin() const;
   sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
@@ -28,16 +30,8 @@ template <>
 inline constexpr bool std::ranges::enable_view<input_view> = true;
 static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
 
-struct forward_view {
-  int* begin() const;
-  int* end() const;
-};
-template <>
-inline constexpr bool std::ranges::enable_view<forward_view> = true;
-static_assert(std::ranges::forward_range<forward_view>);
-
 using CV1 = std::ranges::chunk_view<input_view>;
-// Expected CV1 (with View == input) layout:
+// Expected CV1 (when V models only input_range) layout:
 // [[no_unique_address]] _View __base_                                         // size: 0
 // [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
 // [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
@@ -46,8 +40,18 @@ static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<
 static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
                                  sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
 
+// When V models forward_range
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
 using CV2 = std::ranges::chunk_view<forward_view>;
-// Expected CV2 (with View >= forward) layout:
+// Expected CV2 (when V models forward_range) layout:
 // [[no_unique_address]] _View __base_             // size: 0
 // [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
 static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 9264256fc6a52..4d0fb4f76857c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -19,15 +19,6 @@ void test() {
   char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
   auto view     = range | std::views::chunk(2);
 
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   view.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -43,18 +34,19 @@ void test() {
   std::as_const(view).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  view.end();
+  std::ranges::iter_move(view.begin());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(view).end();
+  std::ranges::iter_move(std::as_const(view).begin());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
+  view.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
+  std::as_const(view).end();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
+  std::ranges::iter_move(view.end());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
+  std::ranges::iter_move(std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   *view.begin();
@@ -71,16 +63,11 @@ void test() {
   (std::as_const(view).begin() == std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == std::as_const(view).end());
+  std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == std::as_const(view).end());
-
+  std::views::chunk(range, 3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(view.begin());
+  range | std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(std::as_const(view).begin());
+  std::views::reverse | std::views::chunk(3);
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..6baa2fce13695
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const inner_iterator& x)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr iterator operator+(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator+(difference_type n, const iterator& i)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator-(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+//       requires sized_sentinel_for<iterator_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(std::default_sentinel - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - std::default_sentinel == -4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t, const inner_iterator& x)`
+  {
+    assert(std::default_sentinel - (*input_chunked.begin()).begin() == 3);
+  }
+
+  // Test `friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t)`
+  {
+    assert((*input_chunked.begin()).begin() - std::default_sentinel == -3);
+  }
+
+  // Test `friend constexpr iterator operator+(const iterator& i, difference_type n)`
+  {
+    assert(chunked.begin() + 4 == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator+(difference_type n, const iterator& i)`
+  {
+    assert(4 + chunked.begin() == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator-(const iterator& i, difference_type n)`
+  {
+    assert(chunked.end() - 4 == chunked.begin());
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)`
+  {
+    assert(std::default_sentinel - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)`
+  {
+    assert(chunked.begin() - std::default_sentinel == -4);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
index d86726d958e6e..ea6731cccce85 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -11,14 +11,12 @@
 // <ranges>
 
 //   V models only input_range
-//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
-//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
-//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t);
 
 //   V models forward_range
-//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator==(const iterator& x, const iterator& y);
+//     friend constexpr bool operator==(const iterator& x, default_sentinel_t);
 //     friend constexpr bool operator<(const iterator& x, const iterator& y)
 //       requires random_access_range<Base>;
 //     friend constexpr bool operator>(const iterator& x, const iterator& y)
@@ -31,7 +29,6 @@
 //       requires random_access_range<Base> &&
 //                three_way_comparable<iterator_t<Base>>;
 
-#include <algorithm>
 #include <cassert>
 #include <compare>
 #include <iterator>
@@ -53,14 +50,11 @@ constexpr bool test() {
     assert(it == std::default_sentinel);
   }
 
-  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  // Test `friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t)`
   {
-    assert(input_chunked.end() - input_chunked.begin() == 4);
-  }
-
-  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
-  {
-    assert(input_chunked.begin() - input_chunked.end() == -4);
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::ranges::advance(it, 3);
+    assert(it == std::default_sentinel);
   }
 
   // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
@@ -69,6 +63,11 @@ constexpr bool test() {
     assert(chunked.end() == chunked.end());
   }
 
+  // Test `friend constexpr bool operator==(const iterator& x, default_sentinel)`
+  {
+    assert(chunked.end() == std::default_sentinel);
+  }
+
   // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() < chunked.end());
@@ -79,7 +78,7 @@ constexpr bool test() {
     assert(chunked.end() > chunked.begin());
   }
 
-  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  // Test `friend constexpr bool operator<=(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() <= chunked.begin());
     assert(chunked.begin() <= chunked.end());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
new file mode 100644
index 0000000000000..9b45fc6d779bf
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models forward_range
+//     constexpr value_type iterator::operator[](difference_type n) const
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+
+  // Test `constexpr value_type iterator::operator[](difference_type n) const`
+  {
+    assert(std::ranges::equal(chunked.begin()[1], std::vector{4, 5, 6}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 033a1c16289990a16b1f674f380f7a72f38f48ea Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 14:30:08 -0400
Subject: [PATCH 11/30] Fix release notes

---
 libcxx/docs/ReleaseNotes/22.rst    | 1 -
 libcxx/docs/ReleaseNotes/23.rst    | 1 +
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0637ba98976a2..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -55,7 +55,6 @@ Implemented Papers
 - 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>`__)
-- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e7eb42207db42..a98f42da70f00 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -52,6 +52,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::chunk`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 6abcac5e4ebca..8c92d89be999b 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""

>From 7fe126d5e7c88787f26228c64bcb5f89977c5b83 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:02:10 +0800
Subject: [PATCH 12/30] Resolve overload conflicts on `__div_ceil` in
 `ranges::chunk_view` and `ranges::stride_view`.

Both `ranges::chunk_view` and `ranges::stride_view` need a helper function called `__div_ceil` that solves some simple maths. According to the style of other libcxx internal helper functions (e.g. `__priority_tag`, `__small_buffer`), it seems that we can put `__div_ceil` into a new file `__utility/div_ceil.h`. However, most files in `__utility/` refer to a corresponding symbol in `<utility>` header, so that if someone feels this is not proper, I'll be glad to revert it.
---
 libcxx/include/CMakeLists.txt         |  1 +
 libcxx/include/__ranges/chunk_view.h  | 21 ++++++-----------
 libcxx/include/__ranges/stride_view.h | 20 +++++-----------
 libcxx/include/__utility/div_ceil.h   | 33 +++++++++++++++++++++++++++
 libcxx/include/module.modulemap.in    |  1 +
 5 files changed, 48 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/include/__utility/div_ceil.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 76b93c17c3312..0e9f19a81b1ab 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -935,6 +935,7 @@ set(files
   __utility/convert_to_integral.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
+  __utility/div_ceil.h
   __utility/element_count.h
   __utility/empty.h
   __utility/exception_guard.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 683b6bbaa2ca5..68d4408295e47 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -36,6 +36,7 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 
@@ -52,14 +53,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Integral>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
-  _Integral __r = __num / __denom;
-  if (__num % __denom)
-    ++__r;
-  return __r;
-}
-
 template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
@@ -98,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -153,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -321,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -510,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..7ea68b13330c3 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -46,15 +47,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Value>
-_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) {
-  _Value __r = __left / __right;
-  if (__left % __right) {
-    ++__r;
-  }
-  return __r;
-}
-
 template <input_range _View>
   requires view<_View>
 class stride_view : public view_interface<stride_view<_View>> {
@@ -121,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -340,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -ranges::__div_ceil(-__n, __x.__stride_);
+      return -__div_ceil(-__n, __x.__stride_);
     }
-    return ranges::__div_ceil(__n, __x.__stride_);
+    return __div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)
diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
new file mode 100644
index 0000000000000..595c590b9e3f6
--- /dev/null
+++ b/libcxx/include/__utility/div_ceil.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_DIV_CEIL_H
+#define _LIBCPP___UTILITY_DIV_CEIL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __div_ceil computes the ceiling of the division of two integers. It is mainly used by
+// range adaptors like chunk_view and stride_view.
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_DIV_CEIL_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 64238440b9dd7..ef5d15d063f69 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2200,6 +2200,7 @@ module std [system] {
     module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
+    module div_ceil                        { header "__utility/div_ceil.h" }
     module element_count                   { header "__utility/element_count.h" }
     module exception_guard                 { header "__utility/exception_guard.h" }
     module exchange                        { header "__utility/exchange.h" }

>From d27b355e67e17b0ed15d44a247fdba1c3b13ed49 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:22:38 +0800
Subject: [PATCH 13/30] Fix test error that `constexpr` is not supported in
 `-std=c++03`

---
 libcxx/include/__utility/div_ceil.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 595c590b9e3f6..4d2868465c141 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,6 +17,8 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if _LIBCPP_STD_VER >= 11
+
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
 
@@ -28,6 +30,8 @@ template <class _Integral>
   return __r;
 }
 
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___UTILITY_DIV_CEIL_H

>From a1a5918bb79d29cbe7be2736da7a72b83ee0afbb Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:38:37 +0800
Subject: [PATCH 14/30] Fix test error again that `constexpr` is not supported
 in `-std=c++11`

---
 libcxx/include/__utility/div_ceil.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 4d2868465c141..21e9bcb30d1a1 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,7 +17,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 11
+#if _LIBCPP_STD_VER >= 23
 
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
@@ -30,7 +30,7 @@ template <class _Integral>
   return __r;
 }
 
-#endif
+#endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_END_NAMESPACE_STD
 

>From c2e0e2e926c92ee6d001e060aa5660b681874dfd Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:45:41 +0800
Subject: [PATCH 15/30] Fix test error that `std::as_const` is included from
 `<utility>`.

---
 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp | 1 +
 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
index 3cccd7d1bebc6..29588a57fbcab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -22,6 +22,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
index 73eb0d967f6a2..2f37ac5f73524 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -23,6 +23,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"

>From 7d3cdd73f5da77a7499969fb7954312a19e26630 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 14:19:19 +0800
Subject: [PATCH 16/30] Avoid ADL in `std::__div_ceil`.

---
 libcxx/include/__ranges/chunk_view.h  | 12 ++++++------
 libcxx/include/__ranges/stride_view.h | 10 +++++-----
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 68d4408295e47..1107a9238b2bc 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -91,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -146,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return std::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -314,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -503,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return std::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 7ea68b13330c3..a8b485cbe33e5 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -113,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -332,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -__div_ceil(-__n, __x.__stride_);
+      return -std::__div_ceil(-__n, __x.__stride_);
     }
-    return __div_ceil(__n, __x.__stride_);
+    return std::__div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return std::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)

>From 116c6d85d8a9ad177b1df1975eea92479cb03664 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 16:39:58 +0800
Subject: [PATCH 17/30] [Important] Add missing concepts requirements.

---
 libcxx/include/__ranges/chunk_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1107a9238b2bc..47e6bd56415b6 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -400,7 +400,8 @@ class chunk_view<_View>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires bidirectional_range<_Base> {
     auto __tmp = *this;
     --*this;
     return __tmp;

>From 545bbdf10d24c3790790bfe669b8c756da719838 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Fri, 26 Jun 2026 22:35:12 +0800
Subject: [PATCH 18/30] Cover tests on `chunk_range`. (I'll continue to cover
 tests on `__iterator`, `__inner_iterator` and `__outer_iterator` at the
 weekend)

---
 libcxx/include/__ranges/chunk_view.h          |  3 +-
 .../range.adaptors/range.chunk/base.pass.cpp  | 12 +++-
 .../range.chunk/borrowing.compile.pass.cpp    | 27 +++++++
 .../range.adaptors/range.chunk/ctor.pass.cpp  | 54 ++++++++++++++
 .../range.adaptors/range.chunk/size.pass.cpp  | 72 +++++++++++++++++++
 5 files changed, 166 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 47e6bd56415b6..1798711eabd16 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -401,7 +401,8 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
-    requires bidirectional_range<_Base> {
+    requires bidirectional_range<_Base>
+  {
     auto __tmp = *this;
     --*this;
     return __tmp;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index b45e1f59f1394..4dca324a23695 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -26,7 +26,7 @@ constexpr bool test() {
   std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
       std::as_const(array) | std::views::chunk(4);
 
-  // Test `chunk_view.base()`
+  // Test `chunk_view.base() const&`
   {
     std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
     assert(std::addressof(base) == std::addressof(array));
@@ -35,6 +35,16 @@ constexpr bool test() {
     assert(std::addressof(const_base) == std::addressof(array));
   }
 
+  // Test `chunk_view.base() &&`
+  {
+    std::same_as<std::ranges::ref_view<std::array<int, 8>>> decltype(auto) moved = std::move(chunked).base();
+    assert(std::addressof(moved.base()) == std::addressof(array));
+
+    std::same_as<std::ranges::ref_view<const std::array<int, 8>>> decltype(auto) const_moved =
+        std::move(const_chunked).base();
+    assert(std::addressof(const_moved.base()) == std::addressof(array));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
new file mode 100644
index 0000000000000..1eb4a34f87217
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class V>
+//   inline constexpr bool enable_borrowed_range<chunk_view<V>> =
+//       forward_range<V> && enable_borrowed_range<V>;
+
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+// When V models only `input_range`.
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<input_span<int>>>);
+
+// When V models at least `forward_range`.
+static_assert(std::ranges::enable_borrowed_range<std::ranges::chunk_view<BorrowedView>>);
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
new file mode 100644
index 0000000000000..b60e040b807eb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.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++23
+
+// <ranges>
+
+//   constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n);
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_convertible.h"
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models only `input_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>, input_span<int>, std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(input_span(vector.data(), 8), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+                                    std::ranges::ref_view<std::vector<int>>,
+                                    std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
new file mode 100644
index 0000000000000..820774c3b750e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr auto size() requires sized_range<_View>;
+//   constexpr auto size() const requires sized_range<const _View>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+  // Test `chunk_view.size()` when V models only `input_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<input_span<int>>>);
+    static_assert(!std::ranges::sized_range<const std::ranges::chunk_view<input_span<int>>>);
+
+    auto chunked = input_span<int>(arr, 12) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when V models `forward_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+    static_assert(std::ranges::sized_range<const std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+    const auto& const_chunked = chunked;
+    assert(const_chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when the range is not fully divisible
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(5);
+    assert(chunked.size() == 3);
+  }
+
+  // Test `chunk_view.size()` when the range is empty
+  {
+    static_assert(std::ranges::sized_range<std::ranges::empty_view<int>>);
+    auto chunked = std::views::empty<int> | std::views::chunk(3);
+    assert(chunked.size() == 0);
+  }
+
+  // Test `chunk_view.size()` when chunk size is larger than the range
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(100);
+    assert(chunked.size() == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From e262f9e7497fb3ccf232a3c7265b8faa83f2fa20 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:36:04 +0800
Subject: [PATCH 19/30] Fix chunk_view ctor test for forward ranges.

---
 .../test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
index b60e040b807eb..c0d210aa45dc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
@@ -35,11 +35,11 @@ constexpr bool test() {
 
   // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
   {
-    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+    static_assert(!test_convertible<std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>>,
                                     std::ranges::ref_view<std::vector<int>>,
                                     std::ptrdiff_t>());
 
-    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked(std::ranges::ref_view(vector), 3);
     assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
   }
 

>From bf13c55de976c562120249f2fc7208ea1091fc95 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:39:36 +0800
Subject: [PATCH 20/30] Use consistent copy_constructible spelling in
 chunk_view

---
 libcxx/include/__ranges/chunk_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1798711eabd16..f3edc617de07a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -71,7 +71,7 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
-    requires std::copy_constructible<_View>
+    requires copy_constructible<_View>
   {
     return __base_;
   }

>From f457f267b72fe64a2f22c288da4b096ae0bcc83c Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:48:59 +0800
Subject: [PATCH 21/30] Remove unnecessary no_unique_address from chunk_view
 counts

---
 libcxx/include/__ranges/chunk_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f3edc617de07a..4478af951dbca 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -57,8 +57,8 @@ template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  range_difference_t<_View> __n_;
+  range_difference_t<_View> __remainder_;
   _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
 
   class __outer_iterator;
@@ -256,7 +256,7 @@ template <view _View>
   requires forward_range<_View>
 class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  range_difference_t<_View> __n_;
 
   template <bool _Const>
   class __iterator;

>From 39c371e116736dd37006c3cf48ed9cf9412b1222 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 23:25:01 +0800
Subject: [PATCH 22/30] Fix chunk_view inner iter_move return type (from T into
 T&&)

---
 libcxx/include/__ranges/chunk_view.h          |  2 +-
 .../range.chunk.inner.iter/iter_move.pass.cpp | 43 +++++++++++++++++++
 2 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 4478af951dbca..e9511f027160a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -238,7 +238,7 @@ class chunk_view<_View>::__inner_iterator {
     return -(__s - __i);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_View>
   iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
     return ranges::iter_move(*__i.__parent_->__current_);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp
new file mode 100644
index 0000000000000..5c7b620924c2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   friend constexpr range_rvalue_reference_t<V> iter_move(const inner_iterator& i)
+//     noexcept(noexcept(ranges::iter_move(i.parent_->current_.value())));
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+constexpr bool test() {
+  using InnerIterator =
+      std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<test_view<cpp20_input_iterator>>>>;
+
+  static_assert(std::ranges::input_range<test_view<cpp20_input_iterator>>);
+  static_assert(!std::ranges::forward_range<test_view<cpp20_input_iterator>>);
+
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())), int&&>);
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())),
+                             std::ranges::range_rvalue_reference_t<test_view<cpp20_input_iterator>>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file

>From c1a0811d8824267ff40fa442b2c2ad10d95b7690 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Mon, 20 Jul 2026 00:13:15 +0800
Subject: [PATCH 23/30] Add chunk_view constraint tests

---
 .../range.chunk/concept.compile.pass.cpp      | 64 +++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp
new file mode 100644
index 0000000000000..61b89d4c09c32
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <view V>
+//     requires input_range<V>
+//   class chunk_view;
+//
+//   template <view V>
+//     requires forward_range<V>
+//   class chunk_view<_View>;
+
+#include <ranges>
+#include <utility>
+
+#include "almost_satisfies_types.h"
+#include "test_range.h"
+#include "types.h"
+
+template <>
+inline constexpr bool std::ranges::enable_view<InputRangeNotDerivedFrom> = true;
+
+// Test two separate properties:
+// 1. Whether `chunk_view<View>` can be formed.
+// 2. Whether a well-formed `chunk_view<View>` can be constructed.
+template <class View>
+concept CanFormChunkView = requires { typename std::ranges::chunk_view<View>; };
+
+template <class View>
+concept CanConstructChunkView = CanFormChunkView<View> && requires(View view, std::ranges::range_difference_t<View> n) {
+  std::ranges::chunk_view<View>(std::move(view), n);
+};
+
+// Test constraints when the template argument is not a view
+static_assert(!std::ranges::view<test_non_const_range<cpp17_input_iterator>>);
+static_assert(std::ranges::input_range<test_non_const_range<cpp17_input_iterator>>);
+static_assert(!CanFormChunkView<test_non_const_range<cpp17_input_iterator>>);
+
+// Test constraints when the template argument is not an input_range
+static_assert(!std::ranges::input_range<InputRangeNotDerivedFrom>);
+static_assert(std::ranges::view<InputRangeNotDerivedFrom>);
+static_assert(!CanFormChunkView<InputRangeNotDerivedFrom>);
+
+// Test constraints when the template argument is an input_range and a view
+static_assert(std::ranges::input_range<input_span<int>>);
+static_assert(std::ranges::view<input_span<int>>);
+static_assert(CanFormChunkView<input_span<int>> && CanConstructChunkView<input_span<int>>);
+
+// Test constraints when the template argument is a forward_range and a view
+static_assert(std::ranges::forward_range<BorrowedView>);
+static_assert(std::ranges::view<BorrowedView>);
+static_assert(CanFormChunkView<BorrowedView> && CanConstructChunkView<BorrowedView>);
+
+// chunk_view itself models view
+static_assert(std::ranges::view<std::ranges::chunk_view<input_span<int>>>);
+static_assert(std::ranges::view<std::ranges::chunk_view<BorrowedView>>);

>From 8085a1d3bf45b122557796308e767016f9f7e784 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Mon, 20 Jul 2026 00:13:26 +0800
Subject: [PATCH 24/30] Add chunk_view iterator type tests

---
 .../types.compile.pass.cpp                    | 30 ++++++++++
 .../range.chunk.iter/types.compile.pass.cpp   | 57 +++++++++++++++++++
 .../types.compile.pass.cpp                    | 31 ++++++++++
 3 files changed, 118 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..1775a08f84a2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   class inner_iterator;
+
+//   using inner_iterator::iterator_concept = input_iterator_tag;
+//   using inner_iterator::difference_type = range_difference_t<V>;
+//   using inner_iterator::value_type = range_value_t<V>;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+using InnerIterator = std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<input_span<int>>>>;
+
+static_assert(std::same_as<typename InnerIterator::iterator_concept, std::input_iterator_tag>);
+static_assert(std::same_as<typename InnerIterator::difference_type, std::ranges::range_difference_t<input_span<int>>>);
+static_assert(std::same_as<typename InnerIterator::value_type, std::ranges::range_value_t<input_span<int>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..96e3057edc04a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models forward_range:
+//   class iterator;
+
+//   using iterator::iterator_category = input_iterator_tag;
+//   using iterator::iterator_concept = see below;
+//   using iterator::value_type = decltype(views::take(subrange(current_, end_), n_));
+//   using iterator::difference_type = range_difference_t<Base>;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+template <template <class...> class Iter>
+using ChunkViewFor = std::ranges::chunk_view<test_view<Iter>>;
+
+template <template <class...> class Iter>
+using ChunkIteratorFor = std::ranges::iterator_t<ChunkViewFor<Iter>>;
+
+template <template <class...> class Iter>
+constexpr void test_iterator_types() {
+  using ChunkView     = ChunkViewFor<Iter>;
+  using ChunkIterator = ChunkIteratorFor<Iter>;
+
+  static_assert(std::same_as<typename ChunkIterator::iterator_category, std::input_iterator_tag>);
+  static_assert(std::same_as<typename ChunkIterator::value_type, std::ranges::range_value_t<ChunkView>>);
+  static_assert(std::same_as<typename ChunkIterator::difference_type, std::ranges::range_difference_t<test_view<Iter>>>);
+}
+
+constexpr void test() {
+  test_iterator_types<forward_iterator>();
+  test_iterator_types<bidirectional_iterator>();
+  test_iterator_types<random_access_iterator>();
+  test_iterator_types<contiguous_iterator>();
+
+  static_assert(std::same_as<ChunkIteratorFor<forward_iterator>::iterator_concept, std::forward_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<bidirectional_iterator>::iterator_concept,
+                             std::bidirectional_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<random_access_iterator>::iterator_concept,
+                             std::random_access_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<contiguous_iterator>::iterator_concept,
+                             std::random_access_iterator_tag>);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..9127e567305ce
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   class outer_iterator;
+
+//   using outer_iterator::iterator_concept = input_iterator_tag;
+//   using outer_iterator::difference_type = range_difference_t<V>;
+//   class outer_iterator::value_type;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+using OuterIterator = std::ranges::iterator_t<std::ranges::chunk_view<input_span<int>>>;
+
+static_assert(std::same_as<typename OuterIterator::iterator_concept, std::input_iterator_tag>);
+static_assert(std::same_as<typename OuterIterator::difference_type, std::ranges::range_difference_t<input_span<int>>>);
+static_assert(std::same_as<typename OuterIterator::value_type, std::iter_value_t<OuterIterator>>);
+static_assert(std::ranges::input_range<typename OuterIterator::value_type>);

>From 48bf130465c89823e4c9817180cfa15434057466 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:22:10 +0800
Subject: [PATCH 25/30] Add chunk_view inner iterator special member tests

---
 .../ctor.compile.pass.cpp                     | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp
new file mode 100644
index 0000000000000..ec2e68cb0c8ca
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   inner_iterator(inner_iterator&&) = default;
+//   inner_iterator& operator=(inner_iterator&&) = default;
+
+#include <concepts>
+#include <ranges>
+
+#include "../types.h"
+
+using InnerIterator = std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<input_span<int>>>>;
+
+static_assert(!std::default_initializable<InnerIterator>);
+static_assert(!std::copy_constructible<InnerIterator>);
+static_assert(!std::assignable_from<InnerIterator&, const InnerIterator&>);
+static_assert(std::move_constructible<InnerIterator>);
+static_assert(std::assignable_from<InnerIterator&, InnerIterator>);

>From 5d32ff5424b490676c9771699233a396532fea1b Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:37:21 +0800
Subject: [PATCH 26/30] Add chunk_view inner iterator base() test

---
 .../range.chunk.inner.iter/base.pass.cpp      | 44 +++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp
new file mode 100644
index 0000000000000..9546aecb01560
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   constexpr const iterator_t<V> base() const&;
+
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int buffer[] = {1, 2, 3, 4};
+
+  std::ranges::chunk_view<input_span<int>> chunked(input_span<int>(buffer, 4), 2);
+  auto outer = chunked.begin();
+  auto inner = (*outer).begin();
+
+  std::same_as<const std::ranges::iterator_t<input_span<int>>> decltype(auto) base = inner.base();
+  assert(*base == 1);
+
+  ++inner;
+  assert(*inner.base() == 2);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 2c8a8c62abfca361ca45933de0da5eeebd68b290 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:48:43 +0800
Subject: [PATCH 27/30] Fix input_span operator* to return a reference

---
 .../range.chunk/range.chunk.iter/deref.pass.cpp               | 4 ++--
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h     | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
index b8772d0fac3d2..32054584f4358 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -53,10 +53,10 @@ constexpr bool test() {
     static_assert(noexcept((inner.end())));
   }
 
-  // Test `constexpr value_type iterator::operator*() const`
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
   {
     /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
-    std::same_as<int> decltype(auto) v                           = *it;
+    std::same_as<int&> decltype(auto) v                          = *it;
     assert(v == 1);
   }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 7533e41713d72..83e383e98110e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -23,7 +23,7 @@ struct input_span : std::span<T> {
     using iterator_concept = std::input_iterator_tag;
     constexpr iterator()   = default;
     constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
-    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    constexpr decltype(auto) operator*() const { return std::span<T>::iterator::operator*(); }
     friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
     friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
     friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;

>From 32f132912f08cfd18cbf8fffff80ee42d178b745 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:48:48 +0800
Subject: [PATCH 28/30] Add chunk_view inner iterator iter_swap test

---
 .../range.chunk.inner.iter/iter_swap.pass.cpp | 50 +++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp
new file mode 100644
index 0000000000000..b5223d5091982
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   friend constexpr void iter_swap(const inner_iterator& x, const inner_iterator& y)
+//     noexcept(noexcept(ranges::iter_swap(x.parent_->current_.value(), y.parent_->current_.value())))
+//     requires indirectly_swappable<iterator_t<V>>;
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int a[] = {1, 2, 3, 4};
+  int b[] = {5, 6, 7, 8};
+
+  // Each chunk_view owns its own `current_` cache, so use two independent views to observe a real swap.
+  std::ranges::chunk_view<input_span<int>> chunked_a(input_span<int>(a, 4), 2);
+  std::ranges::chunk_view<input_span<int>> chunked_b(input_span<int>(b, 4), 2);
+
+  auto inner_a = (*chunked_a.begin()).begin();
+  auto inner_b = (*chunked_b.begin()).begin();
+
+  assert(a[0] == 1);
+  assert(b[0] == 5);
+
+  std::ranges::iter_swap(inner_a, inner_b);
+
+  assert(a[0] == 5);
+  assert(b[0] == 1);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 237fa79aa5d1cc327884fef2dd96529686912fa6 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:51:42 +0800
Subject: [PATCH 29/30] Add chunk_view outer iterator value_type size test

---
 .../range.chunk.outer.iter/size.pass.cpp      | 55 +++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/size.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/size.pass.cpp
new file mode 100644
index 0000000000000..51c7cac8da280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/size.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   constexpr auto outer_iterator::value_type::size() const
+//     requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test `size()` when the range is fully divisible by the chunk size.
+  {
+    std::ranges::chunk_view<input_span<int>> chunked(input_span<int>(arr, 8), 4);
+    auto outer = chunked.begin();
+
+    assert((*outer).size() == 4);
+    ++outer;
+    assert((*outer).size() == 4);
+  }
+
+  // Test `size()` when the last chunk is smaller than the chunk size.
+  {
+    std::ranges::chunk_view<input_span<int>> chunked(input_span<int>(arr, 8), 3);
+    auto outer = chunked.begin();
+
+    assert((*outer).size() == 3);
+    ++outer;
+    assert((*outer).size() == 3);
+    ++outer;
+    assert((*outer).size() == 2);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From b329da3717ddf952907bf38181edd116277bd6bd Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 16:02:22 +0800
Subject: [PATCH 30/30] Add chunk_view outer iterator special member tests

---
 .../ctor.compile.pass.cpp                     | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/ctor.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/ctor.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/ctor.compile.pass.cpp
new file mode 100644
index 0000000000000..51198c1ffea81
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/ctor.compile.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   outer_iterator(outer_iterator&&) = default;
+//   outer_iterator& operator=(outer_iterator&&) = default;
+
+#include <concepts>
+#include <ranges>
+
+#include "../types.h"
+
+using OuterIterator = std::ranges::iterator_t<std::ranges::chunk_view<input_span<int>>>;
+
+static_assert(!std::default_initializable<OuterIterator>);
+static_assert(!std::copy_constructible<OuterIterator>);
+static_assert(!std::assignable_from<OuterIterator&, const OuterIterator&>);
+static_assert(std::move_constructible<OuterIterator>);
+static_assert(std::assignable_from<OuterIterator&, OuterIterator>);


From libcxx-commits at lists.llvm.org  Sat Jul 25 01:05:03 2026
From: libcxx-commits at lists.llvm.org (A. Jiang via libcxx-commits)
Date: Sat, 25 Jul 2026 01:05:03 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][math] Add `constexpr` to
	`std::fpclassify` (PR #210993)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/210993@github.com>
Message-ID: <6a646e2f.7270d2fd.2df01a.4b8c@mx.google.com>


frederick-vs-ja wrote:

> @frederick-vs-ja I'm not sure specifically why the tests are currently failing.

Perhaps I suggested some wrong thing. Clang seems to consider `_LIBCPP_PREFERRED_OVERLOAD` too early (before further resolution), so perhaps we should add the `_LIBCPP_PREFERRED_OVERLOAD` back.

https://github.com/llvm/llvm-project/pull/210993

From libcxx-commits at lists.llvm.org  Sat Jul 25 01:13:10 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 01:13:10 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2442R1
	`std::views::chunk` (PR #171234)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171234@github.com>
Message-ID: <6a647016.507fc713.3a4929.6c70@mx.google.com>


https://github.com/shyeyian updated https://github.com/llvm/llvm-project/pull/171234

>From f6554ea2a0e1d9571cf82fc7b2f3d1115c466474 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at petalmail.com>
Date: Wed, 24 Dec 2025 22:05:29 +0800
Subject: [PATCH 01/31] Implement `views::chunk`.

This commit squashes previous commits.
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/22.rst               |   1 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/chunk_view.h          | 562 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/ranges                         |  12 +
 libcxx/include/version                        |   2 +-
 libcxx/modules/std/ranges.inc                 |  11 +-
 .../no_unique_address.compile.pass.cpp        |  53 ++
 .../range.chunk/nodiscard.verify.cpp          |  86 +++
 .../ranges.version.compile.pass.cpp           |  32 +-
 .../version.version.compile.pass.cpp          |  32 +-
 .../range.chunk/adaptor.pass.cpp              |  75 +++
 .../range.adaptors/range.chunk/base.pass.cpp  |  46 ++
 .../range.adaptors/range.chunk/begin.pass.cpp |  67 +++
 .../range.chunk/ctad.compile.pass.cpp         |  55 ++
 .../range.adaptors/range.chunk/end.pass.cpp   |  65 ++
 .../range.chunk/general.pass.cpp              |  48 ++
 .../range.chunk.iter/compare.pass.cpp         | 109 ++++
 .../range.chunk.iter/decrement.pass.cpp       |  60 ++
 .../range.chunk.iter/deref.pass.cpp           |  77 +++
 .../range.chunk.iter/increment.pass.cpp       |  95 +++
 .../ranges/range.adaptors/range.chunk/types.h |  50 ++
 .../generate_feature_test_macro_components.py |   1 -
 25 files changed, 1494 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/chunk_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..8e57305f32ad1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -370,7 +370,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_rvalue``                             ``202207L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_chunk``                                 *unimplemented*
+    ``__cpp_lib_ranges_chunk``                                 ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                              ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0d1a1fbc00f2c..d2a2d1b712689 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,6 +54,7 @@ Implemented Papers
 - 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>`__)
+- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e4c6cb0f51065..8c8ab211e5bf4 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__","Only ``ranges::iota`` is implemented."
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__",""
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index eb423ae923b4f..2426da6848b51 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -707,6 +707,7 @@ set(files
   __ranges/all.h
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
+  __ranges/chunk_view.h
   __ranges/common_view.h
   __ranges/concepts.h
   __ranges/container_compatible_range.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
new file mode 100644
index 0000000000000..f19d2c9ede8a7
--- /dev/null
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_CHUNK_VIEW_H
+#define _LIBCPP___RANGES_CHUNK_VIEW_H
+
+#include <__algorithm/ranges_min.h>
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/take_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
+
+  class __outer_iterator;
+  class __inner_iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n), __remainder_(0) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires std::copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator begin() {
+    __current_.__emplace(ranges::begin(__base_));
+    __remainder_ = __n_;
+    return __outer_iterator(*this);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept {
+    return std::default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(chunk_view& __parent)
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  class value_type;
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator(__outer_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    return value_type(*__parent_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
+    __parent_->__remainder_ = __parent_->__n_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __outer_iterator& __i, default_sentinel_t) {
+    return *__i.__parent_->__current_ == ranges::end(__i.__parent_->__base_) && __i.__parent_->__remainder_ != 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __outer_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
+    if (__dist < __i.__parent_->__remainder_)
+      return __dist == 0 ? 0 : 1;
+    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __outer_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator::value_type : public view_interface<value_type> {
+  friend __outer_iterator;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(chunk_view& __parent) : __parent_(std::addressof(__parent)) {}
+
+public:
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator begin() const noexcept {
+    return __inner_iterator(*__parent_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return std::__to_unsigned_like(
+        ranges::min(__parent_->__remainder_, ranges::end(__parent_->__base_) - *__parent_->__current_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__inner_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(chunk_view& __parent) noexcept
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+  using value_type       = range_value_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator(__inner_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __inner_iterator& operator=(__inner_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_View> base() const& { return *__parent_->__current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_reference_t<_View> operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
+    return **__parent_->__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    ++*__parent_->__current_;
+    if (*__parent_->__current_ == ranges::end(__parent_->__base_))
+      __parent_->__remainder_ = 0;
+    else
+      --__parent_->__remainder_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __inner_iterator& __i, default_sentinel_t) {
+    return __i.__parent_->__remainder_ == 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __inner_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return ranges::min(__i.__parent_->__remainder_, ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __inner_iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    return -(__s - __i);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
+    return ranges::iter_move(*__i.__parent_->__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void
+  iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) noexcept(
+      noexcept((ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_))))
+    requires indirectly_swappable<iterator_t<_View>>
+  {
+    return ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_);
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+class chunk_view<_View> : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+
+  template <bool _Const>
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires forward_range<const _View>
+  {
+    return __iterator<true>(this, ranges::begin(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    if constexpr (common_range<_View> && sized_range<_View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<false>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<_View> && !bidirectional_range<_View>)
+      return __iterator<false>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires forward_range<const _View>
+  {
+    if constexpr (common_range<const _View> && sized_range<const _View>) {
+      auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+      return __iterator<true>(this, ranges::end(__base_), __missing);
+    } else if constexpr (common_range<const _View> && !bidirectional_range<const _View>)
+      return __iterator<true>(this, ranges::end(__base_));
+    else
+      return default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires forward_range<_View>
+template <bool _Const>
+class chunk_view<_View>::__iterator {
+  friend chunk_view;
+
+  using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, chunk_view>;
+  using _Base _LIBCPP_NODEBUG   = __maybe_const<_Const, _View>;
+
+  iterator_t<_Base> __current_         = iterator_t<_Base>();
+  sentinel_t<_Base> __end_             = sentinel_t<_Base>();
+  range_difference_t<_Base> __n_       = 0;
+  range_difference_t<_Base> __missing_ = 0;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      _Parent* __parent, iterator_t<_Base> __current, range_difference_t<_Base> __missing = 0)
+      : __current_(__current), __end_(ranges::end(__parent->__base_)), __n_(__parent->__n_), __missing_(__missing) {}
+
+  [[nodiscard]] static consteval auto __get_iterator_concept() {
+    if constexpr (random_access_range<_Base>)
+      return random_access_iterator_tag{};
+    else if constexpr (bidirectional_range<_Base>)
+      return bidirectional_iterator_tag{};
+    else
+      return forward_iterator_tag{};
+  }
+
+public:
+  using iterator_category = input_iterator_tag;
+  using iterator_concept  = decltype(__iterator::__get_iterator_concept());
+  using value_type        = decltype(views::take(subrange(__current_, __end_), __n_));
+  using difference_type   = range_difference_t<_Base>;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> &&
+                 convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+      : __current_(std::move(__i.__current_)),
+        __end_(std::move(__i.__end_)),
+        __n_(__i.__n_),
+        __missing_(__i.__missing_) {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() const { return __current_; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to dereference past-the-end chunk_view iterator");
+    return views::take(subrange(__current_, __end_), __n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator[](difference_type __pos) const
+    requires random_access_range<_Base>
+  {
+    return *(*this + __pos);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    __missing_ = ranges::advance(__current_, __n_, __end_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires bidirectional_range<_Base>
+  {
+    ranges::advance(__current_, __missing_ - __n_);
+    __missing_ = 0;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    if (__x > 0) {
+      _LIBCPP_ASSERT_PEDANTIC(ranges::distance(__current_, __end_) > __n_ * (__x - 1),
+                              "Trying to advance chunk_view iterator out of range");
+      ranges::advance(__current_, __n_ * (__x - 1));
+      __missing_ = ranges::advance(__current_, __n_, __end_);
+    } else if (__x < 0) {
+      ranges::advance(__current_, __n_ * __x + __missing_);
+      __missing_ = 0;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    return *this += -__x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
+    return __x.__current_ == __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, default_sentinel_t) {
+    return __x.__current_ == __x.__end_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __x.__current_ < __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __y < __x;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__y < __x);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__x < __y);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator+(difference_type __pos, const __iterator& __i)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+  operator-(const __iterator& __i, difference_type __pos)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r -= __pos;
+    return __r;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, const __iterator& __j)
+    requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+  {
+    return (__i.__current_ - __j.__current_ + __i.__missing_ - __j.__missing_) / __i.__n_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __i, default_sentinel_t __s)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return -(__s - __i);
+  }
+};
+
+template <class _Range>
+chunk_view(_Range&&, range_difference_t<_Range>) -> chunk_view<views::all_t<_Range>>;
+
+template <class _View>
+inline constexpr bool enable_borrowed_range<chunk_view<_View>> = forward_range<_View> && enable_borrowed_range<_View>;
+
+namespace views {
+namespace __chunk {
+struct __fn {
+  template <viewable_range _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range, range_difference_t<_Range> __n) noexcept(
+      noexcept(/*-----*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))))
+      -> decltype(/*--*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n))) {
+    return /*---------*/ chunk_view(std::forward<_Range>(__range), std::forward<range_difference_t<_Range>>(__n));
+  }
+
+  template <class _DifferenceType>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_DifferenceType __n) noexcept(is_nothrow_constructible_v<decay_t<_DifferenceType>, _DifferenceType>) {
+    return __pipeable(std::__bind_back(__fn{}, std::forward<_DifferenceType>(__n)));
+  }
+};
+
+} // namespace __chunk
+
+inline namespace __cpo {
+inline constexpr auto chunk = __chunk::__fn{};
+
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_CHUNK_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4434a5446e35b..cb03eb8d14067 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1863,6 +1863,10 @@ module std [system] {
       header "__ranges/chunk_by_view.h"
       export std.functional.bind_back
     }
+    module chunk_view {
+      header "__ranges/chunk_view.h"
+      export std.functional.bind_back
+    }
     module common_view                    { header "__ranges/common_view.h" }
     module concepts                       { header "__ranges/concepts.h" }
     module container_compatible_range     { header "__ranges/container_compatible_range.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 9f725b12ac6c2..9ec6a4a819919 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -396,6 +396,17 @@ namespace std::ranges {
   class chunk_by_view;                                                      // C++23
 
   namespace views { inline constexpr unspecified chunk_by = unspecified; }  // C++23
+
+  // [range.chunk]
+  template <view V>
+    requires input_range<V>
+  class chunk_view;                                                     // C++23
+
+  template <view V>
+    requires forward_range<V>
+  class chunk_view<V>;                                                  // C++23
+
+  namespace views { inline constexpr unspecified chunk = unspecified; } // C++23
 }
 
 namespace std {
@@ -482,6 +493,7 @@ namespace std {
 #    include <__ranges/adjacent_view.h>
 #    include <__ranges/as_rvalue_view.h>
 #    include <__ranges/chunk_by_view.h>
+#    include <__ranges/chunk_view.h>
 #    include <__ranges/from_range.h>
 #    include <__ranges/join_with_view.h>
 #    include <__ranges/repeat_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index ab781466f5ed5..f5fc33674f521 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -523,7 +523,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges                               202406L
 // # define __cpp_lib_ranges_as_const                      202207L
 # define __cpp_lib_ranges_as_rvalue                     202207L
-// # define __cpp_lib_ranges_chunk                         202202L
+# define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
 # define __cpp_lib_ranges_contains                      202207L
 # define __cpp_lib_ranges_find_last                     202207L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 5caa2c052c00f..631ae10c922ae 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -314,18 +314,17 @@ export namespace std {
       using std::ranges::views::adjacent_transform;
       using std::ranges::views::pairwise_transform;
     } // namespace views
+#endif
 
-#endif // _LIBCPP_STD_VER >= 23
-
-#if 0
+#if _LIBCPP_STD_VER >= 23
     using std::ranges::chunk_view;
 
-    using std::ranges::chunk_view<V>;
-
     namespace views {
       using std::ranges::views::chunk;
-    }
+    } // namespace views
+#endif
 
+#if 0
     using std::ranges::slide_view;
 
     namespace views {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
new file mode 100644
index 0000000000000..14c618c505e70
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// This test ensures that we use `[[no_unique_address]]` in `chunk_view`.
+
+#include <cstddef>
+#include <ranges>
+#include <string>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct input_view {
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<input_view> = true;
+static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
+using CV1 = std::ranges::chunk_view<input_view>;
+// Expected CV1 (with View == input) layout:
+// [[no_unique_address]] _View __base_                                         // size: 0
+// [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
+// [[no_unique_address]] __non_propagating_cache<iterator_t<_View>> __current_ // size: sizeof(__non_propagating_cache<cpp20_input_iterator<int*>>), align: std::ptrdiff_t
+static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>) == alignof(std::ptrdiff_t));
+static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
+                                 sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
+
+using CV2 = std::ranges::chunk_view<forward_view>;
+// Expected CV2 (with View >= forward) layout:
+// [[no_unique_address]] _View __base_             // size: 0
+// [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
+static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..5e557715f4b03
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
+  auto view     = range | std::views::chunk(2);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(std::as_const(view)).base();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(view).base();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  view.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::chunk(range, 3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::chunk(3);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::chunk(3);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *view.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::as_const(view).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end());
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(view.begin());
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::ranges::iter_move(std::as_const(view).begin());
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 5116864879485..41e1603f62350 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -270,17 +270,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -387,17 +381,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
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 b3b424a1d77ce..bd1b55ccc8dfa 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
@@ -5700,17 +5700,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++23"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++23"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
@@ -7619,17 +7613,11 @@
 #    error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_chunk != 202202L
-#      error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_chunk
-#      error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_chunk
+#    error "__cpp_lib_ranges_chunk should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_chunk != 202202L
+#    error "__cpp_lib_ranges_chunk should have the value 202202L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_chunk_by
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
new file mode 100644
index 0000000000000..657e16383256b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   std::views::chunk
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                       = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::ref_view<std::array<int, 8>> view = array | std::views::all;
+
+  // Test `views::chunk(view, n)`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        std::views::chunk(view, 2);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::views::chunk(std::as_const(view), 2);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2}));
+  }
+
+  // Test `views::chunk(n)(range)`
+  {
+    static_assert(noexcept(std::views::chunk(2)));
+    /*__pipable*/ auto adaptor = std::views::chunk(3);
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        adaptor(view);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        adaptor(std::as_const(view));
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3}));
+  }
+
+  // Test `view | views::chunk`
+  {
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) chunked =
+        view | std::views::chunk(4);
+    assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3, 4}));
+    std::same_as<std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>>> decltype(auto) const_chunked =
+        std::as_const(view) | std::views::chunk(4);
+    assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3, 4}));
+  }
+
+  // Test `views::chunk | adaptor`
+  {
+    /*__pipable*/ auto adaptors            = std::views::chunk(5) | std::views::join;
+    std::ranges::input_range auto rejoined = view | adaptors;
+    assert(std::ranges::equal(rejoined, view));
+    std::ranges::input_range auto const_rejoined = std::as_const(view) | adaptors;
+    assert(std::ranges::equal(const_rejoined, view));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
new file mode 100644
index 0000000000000..ac17f97a0a292
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() &&;
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <memory>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() {
+  std::array<int, 8> array                                                   = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::array<int, 8>>> chunked = array | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
+      std::as_const(array) | std::views::chunk(4);
+
+  // Test `chunk_view.base()`
+  {
+    std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
+    assert(std::addressof(base) == std::addressof(array));
+
+    std::same_as<const std::array<int, 8>&> decltype(auto) const_base = const_chunked.base().base();
+    assert(std::addressof(const_base) == std::addressof(array));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
new file mode 100644
index 0000000000000..3cccd7d1bebc6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr __outer_iterator begin();
+
+//   V models forward_range:
+//     constexpr auto begin() requires (!__simple_view<V>);
+//     constexpr auto begin() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.begin()` when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == input_chunked.end());
+  }
+
+  // Test `chunk_view.begin()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++it, std::vector{7, 8}));
+    assert(++it == chunked.end());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.begin();
+    assert(std::ranges::equal(*const_it, std::vector{1, 2, 3}));
+    assert(std::ranges::equal(*++const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*++const_it, std::vector{7, 8}));
+    assert(++const_it == const_chunked.end());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..9bf2baba58c78
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class R>
+//   chunk_view(R&&, range_difference_t<R>) -> chunk_view<all_t<R>>;
+
+#include <ranges>
+
+struct view : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+struct range {
+  int* begin() const;
+  int* end() const;
+};
+
+struct borrowed_range {
+  int* begin() const;
+  int* end() const;
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<borrowed_range> = true;
+
+void test_ctad() {
+  view v;
+  range r;
+  borrowed_range br;
+
+  // clang-format off
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(v, 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(v), 0)), 
+                                      std::ranges::chunk_view<view>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(r, 0)), 
+                                      std::ranges::chunk_view<std::ranges::ref_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(r), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(br, 0)),
+                                      std::ranges::chunk_view<std::ranges::ref_view<borrowed_range>>>);
+  static_assert(std::same_as<decltype(std::ranges::chunk_view(std::move(br), 0)),
+                                      std::ranges::chunk_view<std::ranges::owning_view<borrowed_range>>>);
+  // clang-format on
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
new file mode 100644
index 0000000000000..73eb0d967f6a2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr default_sentinel_t end();
+
+//   V moduels forward_range:
+//     constexpr auto end() requires (!__simple_view<V>);
+//     constexpr auto end() const requires forward_range<const V>;
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<std::ranges::ref_view<const std::vector<int>>> const_chunked =
+      std::as_const(vector) | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector.data(), 8) | std::views::chunk(3);
+
+  // Test `chunk_view.end()` when V models only input_range
+  {
+    static_assert(noexcept(input_chunked.end()));
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it = input_chunked.end();
+  }
+
+  // Test `chunk_view.end()` when V models forward_range
+  {
+    /*chunk_view::__iterator<false>*/ std::forward_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--it, std::vector{1, 2, 3}));
+    assert(it == chunked.begin());
+    /*chunk_view::__iterator<true>*/ std::forward_iterator auto const_it = const_chunked.end();
+    assert(std::ranges::equal(*--const_it, std::vector{7, 8}));
+    assert(std::ranges::equal(*--const_it, std::vector{4, 5, 6}));
+    assert(std::ranges::equal(*--const_it, std::vector{1, 2, 3}));
+    assert(const_it == const_chunked.begin());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
new file mode 100644
index 0000000000000..89891bc093080
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   General tests for chunk_view. This file does not test anything specifically.
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <string_view>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::string_view str = "Cheese with chicken chunk by chunk on truck with my trick";
+  // clang-format off
+  auto str2 = str 
+            | std::views::chunk(4)
+            | std::views::join
+            | std::views::chunk(314159)
+            | std::views::take(1)
+            | std::views::join
+            | std::views::lazy_split(' ')
+            | std::views::chunk(2)
+            | std::views::transform([] (auto&& subview)
+              {
+                return subview | std::views::join_with(' ');
+              })
+            | std::views::join_with(' ');
+  // clang-format on
+  assert(std::ranges::equal(str, str2));
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
new file mode 100644
index 0000000000000..d86726d958e6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator<(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator<=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr bool operator>=(const iterator& x, const iterator& y)
+//       requires random_access_range<Base>;
+//     friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+//       requires random_access_range<Base> &&
+//                three_way_comparable<iterator_t<Base>>;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t)`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    std::ranges::advance(it, 4);
+    assert(it == std::default_sentinel);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(input_chunked.end() - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - input_chunked.end() == -4);
+  }
+
+  // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() == chunked.begin());
+    assert(chunked.end() == chunked.end());
+  }
+
+  // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() < chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() > chunked.begin());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.begin() <= chunked.begin());
+    assert(chunked.begin() <= chunked.end());
+  }
+
+  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() >= chunked.end());
+    assert(chunked.end() >= chunked.begin());
+  }
+
+  // Test `friend constexpr auto operator<=>(const iterator& x, const iterator& y)`
+  {
+    assert((chunked.begin() <=> chunked.begin()) == std::strong_ordering::equal);
+    assert((chunked.begin() <=> chunked.end()) == std::strong_ordering::less);
+    assert((chunked.end() <=> chunked.begin()) == std::strong_ordering::greater);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
new file mode 100644
index 0000000000000..397b32e73080a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+//   constexpr iterator& operator--()
+//     requires bidirectional_range<Base>;
+//   constexpr iterator operator--(int)
+//     requires bidirectional_range<Base>;
+//   constexpr iterator& operator-=(difference_type)
+//     requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+
+  // Test `constexpr iterator& operator--();`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    assert(std::ranges::equal(*--it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator operator--(int)`
+  {
+    /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end();
+    it--;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  // Test `constexpr iterator& operator-=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.end();
+    it -= 3;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
new file mode 100644
index 0000000000000..b8772d0fac3d2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range:
+//     constexpr value_type outer_iterator::operator*() const;
+//     constexpr inner_iterator outer_iterator::value_type::begin() const noexcept;
+//     constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept;
+//     constexpr range_reference_v<V> inner_iterator::operator*() const;
+
+//   V models forward_range:
+//     constexpr value_type iterator::operator*() const;
+
+#include <algorithm>
+#include <cassert>
+#include <compare>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `constexpr value_type outer_iterator::operator*() const`
+  {
+    static_assert(std::ranges::input_range<decltype(*input_chunked.begin())>);
+  }
+
+  // Test `constexpr inner_iterator outer_iterator::value_type::begin() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    assert(*inner.begin() == *vector.begin());
+    static_assert(noexcept(inner.begin()));
+  }
+
+  // Test `constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept`
+  {
+    /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin();
+    [[maybe_unused]] std::same_as<std::default_sentinel_t> auto it                   = inner.end();
+    static_assert(noexcept((inner.end())));
+  }
+
+  // Test `constexpr value_type iterator::operator*() const`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::same_as<int> decltype(auto) v                           = *it;
+    assert(v == 1);
+  }
+
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
+  {
+    std::same_as<int&> decltype(auto) v = *(*chunked.begin()).begin();
+    assert(v == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
new file mode 100644
index 0000000000000..6048aba027ce9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     constexpr outer_iterator& operator++();
+//     constexpr void operator++(int);
+//     constexpr inner_iterator& operator++();
+//     constexpr void operator++(int);
+
+//   V models forward_range
+//     constexpr iterator& operator++();
+//     constexpr iterator operator++(int);
+//     constexpr iterator& operator+=(difference_type)
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(2);
+
+  // Test `constexpr outer_iterator& operator++();`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr void operator++(int);`
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    assert(*++it == 2);
+  }
+
+  // Test `constexpr inner_iterator& operator++();`
+  {
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    static_assert(std::same_as<decltype(it++), void>);
+    it++;
+    assert(*it == 2);
+  }
+
+  // Test `constexpr iterator& operator++();`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    assert(std::ranges::equal(*++it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator operator++(int)`
+  {
+    /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin();
+    it++;
+    assert(std::ranges::equal(*it, std::vector{3, 4}));
+  }
+
+  // Test `constexpr iterator& operator+=(difference_type)`
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    it += 3;
+    assert(std::ranges::equal(*it, std::vector{7, 8}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..09d16c9a48af0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f61d6f991cb15..49fb196903e77 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1106,7 +1106,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_ranges_chunk",
             "values": {"c++23": 202202},
             "headers": ["ranges"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ranges_chunk_by",

>From af914e794ddb86103e6b7acf3bf17d40f5055a8a Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 01:44:18 +0800
Subject: [PATCH 02/31] Code Review: adjust release notes

Note that we will merge this commit with the corresponding change in #172948 in the future.
---
 libcxx/docs/ReleaseNotes/22.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index d2a2d1b712689..30fc22fc0d787 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,7 +54,7 @@ Implemented Papers
 - 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>`__)
-- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------

>From dc62f798ba5615dcac60fffa2cc0afa8b79344c5 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Fri, 26 Dec 2025 13:03:41 +0800
Subject: [PATCH 03/31] Add Tests: test if `_LIBCPP_ASSERT_XXX` works when
 `views::chunk` meets an undefined behaviour

---
 libcxx/include/__ranges/chunk_view.h          |  6 ++-
 .../range.chunk/assert.constructor.pass.cpp   | 39 ++++++++++++++
 .../range.chunk.iter/assert.advance.pass.cpp  | 32 ++++++++++++
 .../range.chunk.iter/assert.deref.pass.cpp    | 52 +++++++++++++++++++
 .../assert.increment.pass.cpp                 | 52 +++++++++++++++++++
 .../ranges/range.adaptors/range.chunk/types.h | 50 ++++++++++++++++++
 6 files changed, 229 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f19d2c9ede8a7..683b6bbaa2ca5 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -128,11 +128,12 @@ class chunk_view<_View>::__outer_iterator {
   _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
-    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator");
     return value_type(*__parent_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
     __parent_->__remainder_ = __parent_->__n_;
     return *this;
@@ -214,6 +215,7 @@ class chunk_view<_View>::__inner_iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to increment past-the-end chunk_view iterator");
     ++*__parent_->__current_;
     if (*__parent_->__current_ == ranges::end(__parent_->__base_))
       __parent_->__remainder_ = 0;
@@ -386,7 +388,7 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
-    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance past-the-end chunk_view iterator");
+    _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to increment past-the-end chunk_view iterator");
     __missing_ = ranges::advance(__current_, __n_, __end_);
     return *this;
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
new file mode 100644
index 0000000000000..03fc7dfb6a5de
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Construct a chunk_view with chunk size <= 0
+
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3};
+
+  // Test constructor when V models only input_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        input_span(vector.data(), 3) | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  // Test constructor when V models forward_range.
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(0), "Trying to construct a chunk_view with chunk size <= 0");
+    TEST_LIBCPP_ASSERT_FAILURE(vector | std::views::chunk(-1), "Trying to construct a chunk_view with chunk size <= 0");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
new file mode 100644
index 0000000000000..af2b826aa8eba
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to advance chunk_view iterator out of range
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+
+  // Test advance past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    TEST_LIBCPP_ASSERT_FAILURE(it += 4, "Trying to advance chunk_view iterator out of range");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
new file mode 100644
index 0000000000000..cb1d92885e684
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to dereference past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test dereference iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*outer_it, "Trying to dereference past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(*inner_it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+
+  // Test dereference iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(*it, "Trying to dereference past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
new file mode 100644
index 0000000000000..8160ee1bb3e14
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// <ranges>
+
+// Trying to increment past-the-end chunk_view iterator
+
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+  auto chunked            = vector | std::views::chunk(3);
+  auto input_chunked      = input_span(vector.data(), 8) | std::views::chunk(3);
+
+  // Test increment past-the-end iterator when V models only input_range
+  {
+    /*chunk_view::__outer_iterator*/ std::input_iterator auto outer_it = input_chunked.begin();
+    ++outer_it;
+    ++outer_it;
+    ++outer_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++outer_it, "Trying to increment past-the-end chunk_view iterator");
+
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto inner_it = (*input_chunked.begin()).begin();
+    ++inner_it;
+    ++inner_it;
+    ++inner_it;
+    TEST_LIBCPP_ASSERT_FAILURE(++inner_it, "Trying to increment past-the-end chunk_view iterator");
+  }
+
+  // Test increment past-the-end iterator when V models forward_range
+  {
+    /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin();
+    ++it;
+    ++it;
+    ++it;
+    TEST_LIBCPP_ASSERT_FAILURE(++it, "Trying to increment past-the-end chunk_view iterator");
+  }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
new file mode 100644
index 0000000000000..a5d688abfa1b0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+#define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
+
+#include <iterator>
+#include <ranges>
+#include <span>
+#include <vector>
+
+// input_span
+
+template <class T>
+struct input_span : std::span<T> {
+  struct iterator : std::span<T>::iterator {
+    using iterator_concept = std::input_iterator_tag;
+    constexpr iterator()   = default;
+    constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
+    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;
+    friend constexpr auto operator-(std::span<T>::difference_type, iterator) = delete;
+    friend constexpr iterator& operator++(iterator& self) {
+      ++static_cast<std::span<T>::iterator&>(self);
+      return self;
+    }
+    friend constexpr void operator++(iterator& self, int) { ++self; }
+    friend constexpr iterator& operator--(iterator&) = delete;
+    friend constexpr void operator--(iterator&, int) = delete;
+  };
+
+  using std::span<T>::span;
+  constexpr iterator begin() { return iterator(std::span<T>::begin()); }
+  constexpr iterator end() { return iterator(std::span<T>::end()); }
+};
+
+template <class T>
+inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
+
+static_assert(std::ranges::input_range<input_span<int>> && !std::ranges::forward_range<input_span<int>> &&
+              std::ranges::view<input_span<int>>);
+
+#endif

>From b664c0cb8f8ed9b46629fa49e4bdd846734615d4 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:26 +0800
Subject: [PATCH 04/31] Fix CI: adjust `#include` path in `test/libcxx` files

---
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp          | 2 +-
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index cb1d92885e684..0c96742147189 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 8160ee1bb3e14..451683bab71b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "check_assertion.h"
-#include "types.h"
+#include "../types.h"
 
 int main() {
   std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};

>From 4dc9b06c0dd515030226b9d27841352ef62549a3 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:42:58 +0800
Subject: [PATCH 05/31] Fix CI: add template deduction guide for
 `input_span<T>`

---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 3 +++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..0978a23c37244 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..4ff1af1d7cda3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,6 +41,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, size_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 1e8906ad49a2406158161cc1b9d0e1570af2959d Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 12:53:21 +0800
Subject: [PATCH 06/31] Code Review: add `REQUIRES: std-at-least-c++23` in all
 the tests related to `views::chunk`.

The previous commit, 4dc9b06c0dd515030226b9d27841352ef62549a3, which introduce manual/explicit template deduction guide in `input_span<T>`, is always implicitly-provided under -std=c++23. So this commit also reverts the changes about `types.h` in the previous commit.
---
 .../range.adaptors/range.chunk/assert.constructor.pass.cpp     | 1 +
 .../range.chunk/range.chunk.iter/assert.advance.pass.cpp       | 1 +
 .../range.chunk/range.chunk.iter/assert.deref.pass.cpp         | 1 +
 .../range.chunk/range.chunk.iter/assert.increment.pass.cpp     | 1 +
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h   | 3 ---
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h      | 3 ---
 6 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
index 03fc7dfb6a5de..477f89be5f070 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/assert.constructor.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
index af2b826aa8eba..9b91d94841b91 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.advance.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
index 0c96742147189..3640b04192bf4 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.deref.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
index 451683bab71b0..61c096f74158e 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/range.chunk.iter/assert.increment.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+// REQUIRES: std-at-least-c++23
 // REQUIRES: has-unix-headers
 // REQUIRES: libcpp-hardening-mode={{extensive|debug}}
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index 0978a23c37244..a5d688abfa1b0 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 4ff1af1d7cda3..09d16c9a48af0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -41,9 +41,6 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
-template <class T>
-input_span(T*, size_t) -> input_span<T>;
-
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From 7e21388c70b38c32125e4d3eda9b3a4c66e1d3db Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Sun, 28 Dec 2025 14:13:59 +0800
Subject: [PATCH 07/31] Fix CI: add template deduction guide for
 `input_span<T>` again.

Seems that even in C++23 the CI fails due to no implicit deduction guide, here we just add the explicit one and make the CI to basically run ok.
---
 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h | 4 ++++
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h    | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
index a5d688abfa1b0..4852ec29204bb 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 09d16c9a48af0..7533e41713d72 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H
 
+#include <cstddef>
 #include <iterator>
 #include <ranges>
 #include <span>
@@ -41,6 +42,9 @@ struct input_span : std::span<T> {
   constexpr iterator end() { return iterator(std::span<T>::end()); }
 };
 
+template <class T>
+input_span(T*, std::ptrdiff_t) -> input_span<T>;
+
 template <class T>
 inline constexpr bool std::ranges::enable_view<input_span<T>> = true;
 

>From f1ed40a1aed2a1f5ebd1e56efcd6ae2bbb25dc26 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:07:21 +0800
Subject: [PATCH 08/31] Code review: Update libcxx/test synopsis

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 .../ranges/range.adaptors/range.chunk/nodiscard.verify.cpp      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 5e557715f4b03..9264256fc6a52 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-// Test the libc++ extension that std::ranges::chunk_view::iterator<Const>::operator* is marked as [[nodiscard]].
+// Test that functions are marked [[nodiscard]].
 
 #include <ranges>
 #include <utility>

>From 12bec119ba49cd6016055f5887f5e83efa25ee30 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 18:21:29 +0800
Subject: [PATCH 09/31] Code review: Fix synopsis.

Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index ac17f97a0a292..b45e1f59f1394 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -10,7 +10,7 @@
 
 // <ranges>
 
-//   constexpr V base() const& requires copyy_constructible<V>;
+//   constexpr V base() const& requires copy_constructible<V>;
 //   constexpr V base() &&;
 
 #include <array>

>From 1e05c5a038dd4cee5a0049b8c7e92e59c8aabd82 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at icloud.com>
Date: Thu, 15 Jan 2026 21:13:46 +0800
Subject: [PATCH 10/31] Code review: sort and unique the tests

---
 .../no_unique_address.compile.pass.cpp        |  24 ++--
 .../range.chunk/nodiscard.verify.cpp          |  35 ++----
 .../range.chunk.iter/arithmetic.pass.cpp      | 108 ++++++++++++++++++
 .../range.chunk.iter/compare.pass.cpp         |  29 +++--
 .../range.chunk.iter/subscript.pass.cpp       |  42 +++++++
 5 files changed, 189 insertions(+), 49 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
index 14c618c505e70..5d32daf4c178a 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
@@ -20,6 +20,8 @@
 #include "test_iterators.h"
 #include "test_range.h"
 
+// When V models only input_range
+
 struct input_view {
   cpp20_input_iterator<int*> begin() const;
   sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
@@ -28,16 +30,8 @@ template <>
 inline constexpr bool std::ranges::enable_view<input_view> = true;
 static_assert(std::ranges::input_range<input_view> && !std::ranges::forward_range<input_view>);
 
-struct forward_view {
-  int* begin() const;
-  int* end() const;
-};
-template <>
-inline constexpr bool std::ranges::enable_view<forward_view> = true;
-static_assert(std::ranges::forward_range<forward_view>);
-
 using CV1 = std::ranges::chunk_view<input_view>;
-// Expected CV1 (with View == input) layout:
+// Expected CV1 (when V models only input_range) layout:
 // [[no_unique_address]] _View __base_                                         // size: 0
 // [[no_unique_address]] range_difference_t<_View> __n_                        // size: sizeof(ptrdiff_t)
 // [[no_unique_address]] range_difference_t<_View> __remainder_                // size: sizeof(ptrdiff_t)
@@ -46,8 +40,18 @@ static_assert(alignof(std::ranges::__non_propagating_cache<cpp20_input_iterator<
 static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 +
                                  sizeof(std::ranges::__non_propagating_cache<cpp20_input_iterator<int*>>));
 
+// When V models forward_range
+
+struct forward_view {
+  int* begin() const;
+  int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_view<forward_view> = true;
+static_assert(std::ranges::forward_range<forward_view>);
+
 using CV2 = std::ranges::chunk_view<forward_view>;
-// Expected CV2 (with View >= forward) layout:
+// Expected CV2 (when V models forward_range) layout:
 // [[no_unique_address]] _View __base_             // size: 0
 // [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t)
 static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
index 9264256fc6a52..4d0fb4f76857c 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
@@ -19,15 +19,6 @@ void test() {
   char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'};
   auto view     = range | std::views::chunk(2);
 
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
-
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   view.base();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -43,18 +34,19 @@ void test() {
   std::as_const(view).begin();
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  view.end();
+  std::ranges::iter_move(view.begin());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::as_const(view).end();
+  std::ranges::iter_move(std::as_const(view).begin());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(3);
+  view.end();
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::chunk(range, 3);
+  std::as_const(view).end();
+
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::chunk(3);
+  std::ranges::iter_move(view.end());
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::reverse | std::views::chunk(3);
+  std::ranges::iter_move(std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   *view.begin();
@@ -71,16 +63,11 @@ void test() {
   (std::as_const(view).begin() == std::as_const(view).end());
 
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == view.end());
-  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (view.begin() == std::as_const(view).end());
+  std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  (std::as_const(view).begin() == std::as_const(view).end());
-
+  std::views::chunk(range, 3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(view.begin());
+  range | std::views::chunk(3);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::ranges::iter_move(std::as_const(view).begin());
+  std::views::reverse | std::views::chunk(3);
 }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..6baa2fce13695
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/arithmetic.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models only input_range
+//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const inner_iterator& x)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+//   V models forward_range
+//     friend constexpr iterator operator+(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator+(difference_type n, const iterator& i)
+//       requires random_access_range<Base>;
+//     friend constexpr iterator operator-(const iterator& i, difference_type n)
+//       requires random_access_range<Base>;
+//     friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+//       requires sized_sentinel_for<iterator_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+//     friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)
+//       requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+#include "../types.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+  std::ranges::chunk_view<input_span<int>> input_chunked = input_span<int>(vector) | std::views::chunk(3);
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  {
+    assert(std::default_sentinel - input_chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
+  {
+    assert(input_chunked.begin() - std::default_sentinel == -4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t, const inner_iterator& x)`
+  {
+    assert(std::default_sentinel - (*input_chunked.begin()).begin() == 3);
+  }
+
+  // Test `friend constexpr difference_type operator-(const inner_iterator& x, default_sentinel_t)`
+  {
+    assert((*input_chunked.begin()).begin() - std::default_sentinel == -3);
+  }
+
+  // Test `friend constexpr iterator operator+(const iterator& i, difference_type n)`
+  {
+    assert(chunked.begin() + 4 == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator+(difference_type n, const iterator& i)`
+  {
+    assert(4 + chunked.begin() == chunked.end());
+  }
+
+  // Test `friend constexpr iterator operator-(const iterator& i, difference_type n)`
+  {
+    assert(chunked.end() - 4 == chunked.begin());
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, const iterator& y)`
+  {
+    assert(chunked.end() - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(default_sentinel_t y, const iterator& x)`
+  {
+    assert(std::default_sentinel - chunked.begin() == 4);
+  }
+
+  // Test `friend constexpr difference_type operator-(const iterator& x, default_sentinel_t y)`
+  {
+    assert(chunked.begin() - std::default_sentinel == -4);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
index d86726d958e6e..ea6731cccce85 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
@@ -11,14 +11,12 @@
 // <ranges>
 
 //   V models only input_range
-//     friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t);
-//     friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
-//     friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t)
-//       requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+//     friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
+//     friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t);
 
 //   V models forward_range
-//     friend constexpr bool operator==(const iterator& x, const iterator& y)
+//     friend constexpr bool operator==(const iterator& x, const iterator& y);
+//     friend constexpr bool operator==(const iterator& x, default_sentinel_t);
 //     friend constexpr bool operator<(const iterator& x, const iterator& y)
 //       requires random_access_range<Base>;
 //     friend constexpr bool operator>(const iterator& x, const iterator& y)
@@ -31,7 +29,6 @@
 //       requires random_access_range<Base> &&
 //                three_way_comparable<iterator_t<Base>>;
 
-#include <algorithm>
 #include <cassert>
 #include <compare>
 #include <iterator>
@@ -53,14 +50,11 @@ constexpr bool test() {
     assert(it == std::default_sentinel);
   }
 
-  // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)`
+  // Test `friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t)`
   {
-    assert(input_chunked.end() - input_chunked.begin() == 4);
-  }
-
-  // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)`
-  {
-    assert(input_chunked.begin() - input_chunked.end() == -4);
+    /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
+    std::ranges::advance(it, 3);
+    assert(it == std::default_sentinel);
   }
 
   // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)`
@@ -69,6 +63,11 @@ constexpr bool test() {
     assert(chunked.end() == chunked.end());
   }
 
+  // Test `friend constexpr bool operator==(const iterator& x, default_sentinel)`
+  {
+    assert(chunked.end() == std::default_sentinel);
+  }
+
   // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() < chunked.end());
@@ -79,7 +78,7 @@ constexpr bool test() {
     assert(chunked.end() > chunked.begin());
   }
 
-  // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)`
+  // Test `friend constexpr bool operator<=(const iterator& x, const iterator& y)`
   {
     assert(chunked.begin() <= chunked.begin());
     assert(chunked.begin() <= chunked.end());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
new file mode 100644
index 0000000000000..9b45fc6d779bf
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/subscript.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   V models forward_range
+//     constexpr value_type iterator::operator[](difference_type n) const
+//       requires random_access_range<Base>;
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <vector>
+
+#include "test_range.h"
+
+constexpr bool test() {
+  std::vector<int> vector                                                  = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(3);
+
+  // Test `constexpr value_type iterator::operator[](difference_type n) const`
+  {
+    assert(std::ranges::equal(chunked.begin()[1], std::vector{4, 5, 6}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 033a1c16289990a16b1f674f380f7a72f38f48ea Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 19 Jun 2026 14:30:08 -0400
Subject: [PATCH 11/31] Fix release notes

---
 libcxx/docs/ReleaseNotes/22.rst    | 1 -
 libcxx/docs/ReleaseNotes/23.rst    | 1 +
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0637ba98976a2..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -55,7 +55,6 @@ Implemented Papers
 - 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>`__)
-- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e7eb42207db42..a98f42da70f00 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -52,6 +52,7 @@ Implemented Papers
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::chunk`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 6abcac5e4ebca..8c92d89be999b 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""

>From 7fe126d5e7c88787f26228c64bcb5f89977c5b83 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:02:10 +0800
Subject: [PATCH 12/31] Resolve overload conflicts on `__div_ceil` in
 `ranges::chunk_view` and `ranges::stride_view`.

Both `ranges::chunk_view` and `ranges::stride_view` need a helper function called `__div_ceil` that solves some simple maths. According to the style of other libcxx internal helper functions (e.g. `__priority_tag`, `__small_buffer`), it seems that we can put `__div_ceil` into a new file `__utility/div_ceil.h`. However, most files in `__utility/` refer to a corresponding symbol in `<utility>` header, so that if someone feels this is not proper, I'll be glad to revert it.
---
 libcxx/include/CMakeLists.txt         |  1 +
 libcxx/include/__ranges/chunk_view.h  | 21 ++++++-----------
 libcxx/include/__ranges/stride_view.h | 20 +++++-----------
 libcxx/include/__utility/div_ceil.h   | 33 +++++++++++++++++++++++++++
 libcxx/include/module.modulemap.in    |  1 +
 5 files changed, 48 insertions(+), 28 deletions(-)
 create mode 100644 libcxx/include/__utility/div_ceil.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 76b93c17c3312..0e9f19a81b1ab 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -935,6 +935,7 @@ set(files
   __utility/convert_to_integral.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
+  __utility/div_ceil.h
   __utility/element_count.h
   __utility/empty.h
   __utility/exception_guard.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 683b6bbaa2ca5..68d4408295e47 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -36,6 +36,7 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
 
@@ -52,14 +53,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Integral>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
-  _Integral __r = __num / __denom;
-  if (__num % __denom)
-    ++__r;
-  return __r;
-}
-
 template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
@@ -98,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -153,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -321,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -510,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 780bb25743c15..7ea68b13330c3 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -32,6 +32,7 @@
 #include <__ranges/range_adaptor.h>
 #include <__ranges/view_interface.h>
 #include <__type_traits/make_unsigned.h>
+#include <__utility/div_ceil.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -46,15 +47,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 
-template <class _Value>
-_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) {
-  _Value __r = __left / __right;
-  if (__left % __right) {
-    ++__r;
-  }
-  return __r;
-}
-
 template <input_range _View>
   requires view<_View>
 class stride_view : public view_interface<stride_view<_View>> {
@@ -121,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -340,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -ranges::__div_ceil(-__n, __x.__stride_);
+      return -__div_ceil(-__n, __x.__stride_);
     }
-    return ranges::__div_ceil(__n, __x.__stride_);
+    return __div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)
diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
new file mode 100644
index 0000000000000..595c590b9e3f6
--- /dev/null
+++ b/libcxx/include/__utility/div_ceil.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_DIV_CEIL_H
+#define _LIBCPP___UTILITY_DIV_CEIL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// __div_ceil computes the ceiling of the division of two integers. It is mainly used by
+// range adaptors like chunk_view and stride_view.
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_DIV_CEIL_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 64238440b9dd7..ef5d15d063f69 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2200,6 +2200,7 @@ module std [system] {
     module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
+    module div_ceil                        { header "__utility/div_ceil.h" }
     module element_count                   { header "__utility/element_count.h" }
     module exception_guard                 { header "__utility/exception_guard.h" }
     module exchange                        { header "__utility/exchange.h" }

>From d27b355e67e17b0ed15d44a247fdba1c3b13ed49 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:22:38 +0800
Subject: [PATCH 13/31] Fix test error that `constexpr` is not supported in
 `-std=c++03`

---
 libcxx/include/__utility/div_ceil.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 595c590b9e3f6..4d2868465c141 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,6 +17,8 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if _LIBCPP_STD_VER >= 11
+
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
 
@@ -28,6 +30,8 @@ template <class _Integral>
   return __r;
 }
 
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___UTILITY_DIV_CEIL_H

>From a1a5918bb79d29cbe7be2736da7a72b83ee0afbb Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:38:37 +0800
Subject: [PATCH 14/31] Fix test error again that `constexpr` is not supported
 in `-std=c++11`

---
 libcxx/include/__utility/div_ceil.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__utility/div_ceil.h b/libcxx/include/__utility/div_ceil.h
index 4d2868465c141..21e9bcb30d1a1 100644
--- a/libcxx/include/__utility/div_ceil.h
+++ b/libcxx/include/__utility/div_ceil.h
@@ -17,7 +17,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 11
+#if _LIBCPP_STD_VER >= 23
 
 // __div_ceil computes the ceiling of the division of two integers. It is mainly used by
 // range adaptors like chunk_view and stride_view.
@@ -30,7 +30,7 @@ template <class _Integral>
   return __r;
 }
 
-#endif
+#endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_END_NAMESPACE_STD
 

>From c2e0e2e926c92ee6d001e060aa5660b681874dfd Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 21 Jun 2026 15:45:41 +0800
Subject: [PATCH 15/31] Fix test error that `std::as_const` is included from
 `<utility>`.

---
 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp | 1 +
 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
index 3cccd7d1bebc6..29588a57fbcab 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
@@ -22,6 +22,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
index 73eb0d967f6a2..2f37ac5f73524 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
@@ -23,6 +23,7 @@
 #include <iterator>
 #include <ranges>
 #include <vector>
+#include <utility>
 
 #include "test_range.h"
 #include "types.h"

>From 7d3cdd73f5da77a7499969fb7954312a19e26630 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 14:19:19 +0800
Subject: [PATCH 16/31] Avoid ADL in `std::__div_ceil`.

---
 libcxx/include/__ranges/chunk_view.h  | 12 ++++++------
 libcxx/include/__ranges/stride_view.h | 10 +++++-----
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 68d4408295e47..1107a9238b2bc 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -91,13 +91,13 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -146,7 +146,7 @@ class chunk_view<_View>::__outer_iterator {
     const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_;
     if (__dist < __i.__parent_->__remainder_)
       return __dist == 0 ? 0 : 1;
-    return __div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
+    return std::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1;
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
@@ -314,13 +314,13 @@ class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __n_));
   }
 };
 
@@ -503,7 +503,7 @@ class chunk_view<_View>::__iterator {
   operator-(default_sentinel_t, const __iterator& __i)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__i.__end_ - __i.__current_, __i.__n_);
+    return std::__div_ceil(__i.__end_ - __i.__current_, __i.__n_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 7ea68b13330c3..a8b485cbe33e5 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -113,13 +113,13 @@ class stride_view : public view_interface<stride_view<_View>> {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
-    return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __stride_));
+    return std::__to_unsigned_like(std::__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
@@ -332,16 +332,16 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<__maybe
     }
     auto __n = __x.__current_ - __y.__current_;
     if (__n < 0) {
-      return -__div_ceil(-__n, __x.__stride_);
+      return -std::__div_ceil(-__n, __x.__stride_);
     }
-    return __div_ceil(__n, __x.__stride_);
+    return std::__div_ceil(__n, __x.__stride_);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(default_sentinel_t, __iterator const& __x)
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
   {
-    return __div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+    return std::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
   }
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
   operator-(__iterator const& __x, default_sentinel_t __y)

>From 116c6d85d8a9ad177b1df1975eea92479cb03664 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Thu, 25 Jun 2026 16:39:58 +0800
Subject: [PATCH 17/31] [Important] Add missing concepts requirements.

---
 libcxx/include/__ranges/chunk_view.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1107a9238b2bc..47e6bd56415b6 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -400,7 +400,8 @@ class chunk_view<_View>::__iterator {
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires bidirectional_range<_Base> {
     auto __tmp = *this;
     --*this;
     return __tmp;

>From 545bbdf10d24c3790790bfe669b8c756da719838 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Fri, 26 Jun 2026 22:35:12 +0800
Subject: [PATCH 18/31] Cover tests on `chunk_range`. (I'll continue to cover
 tests on `__iterator`, `__inner_iterator` and `__outer_iterator` at the
 weekend)

---
 libcxx/include/__ranges/chunk_view.h          |  3 +-
 .../range.adaptors/range.chunk/base.pass.cpp  | 12 +++-
 .../range.chunk/borrowing.compile.pass.cpp    | 27 +++++++
 .../range.adaptors/range.chunk/ctor.pass.cpp  | 54 ++++++++++++++
 .../range.adaptors/range.chunk/size.pass.cpp  | 72 +++++++++++++++++++
 5 files changed, 166 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 47e6bd56415b6..1798711eabd16 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -401,7 +401,8 @@ class chunk_view<_View>::__iterator {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
-    requires bidirectional_range<_Base> {
+    requires bidirectional_range<_Base>
+  {
     auto __tmp = *this;
     --*this;
     return __tmp;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
index b45e1f59f1394..4dca324a23695 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -26,7 +26,7 @@ constexpr bool test() {
   std::ranges::chunk_view<std::ranges::ref_view<const std::array<int, 8>>> const_chunked =
       std::as_const(array) | std::views::chunk(4);
 
-  // Test `chunk_view.base()`
+  // Test `chunk_view.base() const&`
   {
     std::same_as<std::array<int, 8>&> decltype(auto) base = chunked.base().base();
     assert(std::addressof(base) == std::addressof(array));
@@ -35,6 +35,16 @@ constexpr bool test() {
     assert(std::addressof(const_base) == std::addressof(array));
   }
 
+  // Test `chunk_view.base() &&`
+  {
+    std::same_as<std::ranges::ref_view<std::array<int, 8>>> decltype(auto) moved = std::move(chunked).base();
+    assert(std::addressof(moved.base()) == std::addressof(array));
+
+    std::same_as<std::ranges::ref_view<const std::array<int, 8>>> decltype(auto) const_moved =
+        std::move(const_chunked).base();
+    assert(std::addressof(const_moved.base()) == std::addressof(array));
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
new file mode 100644
index 0000000000000..1eb4a34f87217
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/borrowing.compile.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <class V>
+//   inline constexpr bool enable_borrowed_range<chunk_view<V>> =
+//       forward_range<V> && enable_borrowed_range<V>;
+
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+// When V models only `input_range`.
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<input_span<int>>>);
+
+// When V models at least `forward_range`.
+static_assert(std::ranges::enable_borrowed_range<std::ranges::chunk_view<BorrowedView>>);
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::chunk_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
new file mode 100644
index 0000000000000..b60e040b807eb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.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++23
+
+// <ranges>
+
+//   constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n);
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_convertible.h"
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models only `input_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>, input_span<int>, std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(input_span(vector.data(), 8), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
+  {
+    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+                                    std::ranges::ref_view<std::vector<int>>,
+                                    std::ptrdiff_t>());
+
+    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
new file mode 100644
index 0000000000000..820774c3b750e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/size.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   constexpr auto size() requires sized_range<_View>;
+//   constexpr auto size() const requires sized_range<const _View>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_range.h"
+#include "types.h"
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+  // Test `chunk_view.size()` when V models only `input_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<input_span<int>>>);
+    static_assert(!std::ranges::sized_range<const std::ranges::chunk_view<input_span<int>>>);
+
+    auto chunked = input_span<int>(arr, 12) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when V models `forward_range`
+  {
+    static_assert(std::ranges::sized_range<std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+    static_assert(std::ranges::sized_range<const std::ranges::chunk_view<std::ranges::ref_view<int[12]>>>);
+
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(3);
+    assert(chunked.size() == 4);
+    const auto& const_chunked = chunked;
+    assert(const_chunked.size() == 4);
+  }
+
+  // Test `chunk_view.size()` when the range is not fully divisible
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(5);
+    assert(chunked.size() == 3);
+  }
+
+  // Test `chunk_view.size()` when the range is empty
+  {
+    static_assert(std::ranges::sized_range<std::ranges::empty_view<int>>);
+    auto chunked = std::views::empty<int> | std::views::chunk(3);
+    assert(chunked.size() == 0);
+  }
+
+  // Test `chunk_view.size()` when chunk size is larger than the range
+  {
+    auto chunked = std::ranges::ref_view(arr) | std::views::chunk(100);
+    assert(chunked.size() == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From e262f9e7497fb3ccf232a3c7265b8faa83f2fa20 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:36:04 +0800
Subject: [PATCH 19/31] Fix chunk_view ctor test for forward ranges.

---
 .../test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
index b60e040b807eb..c0d210aa45dc3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctor.pass.cpp
@@ -35,11 +35,11 @@ constexpr bool test() {
 
   // Test `chunk_view(_View, range_difference_t<_View>)` when V models `forward_range`
   {
-    static_assert(!test_convertible<std::ranges::chunk_view<input_span<int>>,
+    static_assert(!test_convertible<std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>>,
                                     std::ranges::ref_view<std::vector<int>>,
                                     std::ptrdiff_t>());
 
-    std::ranges::chunk_view<input_span<int>> chunked(std::ranges::ref_view(vector), 3);
+    std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked(std::ranges::ref_view(vector), 3);
     assert(std::ranges::equal(*chunked.begin(), std::vector{1, 2, 3}));
   }
 

>From bf13c55de976c562120249f2fc7208ea1091fc95 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:39:36 +0800
Subject: [PATCH 20/31] Use consistent copy_constructible spelling in
 chunk_view

---
 libcxx/include/__ranges/chunk_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 1798711eabd16..f3edc617de07a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -71,7 +71,7 @@ class chunk_view : public view_interface<chunk_view<_View>> {
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
-    requires std::copy_constructible<_View>
+    requires copy_constructible<_View>
   {
     return __base_;
   }

>From f457f267b72fe64a2f22c288da4b096ae0bcc83c Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 22:48:59 +0800
Subject: [PATCH 21/31] Remove unnecessary no_unique_address from chunk_view
 counts

---
 libcxx/include/__ranges/chunk_view.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index f3edc617de07a..4478af951dbca 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -57,8 +57,8 @@ template <view _View>
   requires input_range<_View>
 class chunk_view : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  range_difference_t<_View> __n_;
+  range_difference_t<_View> __remainder_;
   _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
 
   class __outer_iterator;
@@ -256,7 +256,7 @@ template <view _View>
   requires forward_range<_View>
 class chunk_view<_View> : public view_interface<chunk_view<_View>> {
   _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
-  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  range_difference_t<_View> __n_;
 
   template <bool _Const>
   class __iterator;

>From 39c371e116736dd37006c3cf48ed9cf9412b1222 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sun, 19 Jul 2026 23:25:01 +0800
Subject: [PATCH 22/31] Fix chunk_view inner iter_move return type (from T into
 T&&)

---
 libcxx/include/__ranges/chunk_view.h          |  2 +-
 .../range.chunk.inner.iter/iter_move.pass.cpp | 43 +++++++++++++++++++
 2 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp

diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 4478af951dbca..e9511f027160a 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -238,7 +238,7 @@ class chunk_view<_View>::__inner_iterator {
     return -(__s - __i);
   }
 
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_View>
   iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) {
     return ranges::iter_move(*__i.__parent_->__current_);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.pass.cpp
new file mode 100644
index 0000000000000..5c7b620924c2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_move.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   friend constexpr range_rvalue_reference_t<V> iter_move(const inner_iterator& i)
+//     noexcept(noexcept(ranges::iter_move(i.parent_->current_.value())));
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+constexpr bool test() {
+  using InnerIterator =
+      std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<test_view<cpp20_input_iterator>>>>;
+
+  static_assert(std::ranges::input_range<test_view<cpp20_input_iterator>>);
+  static_assert(!std::ranges::forward_range<test_view<cpp20_input_iterator>>);
+
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())), int&&>);
+  static_assert(std::same_as<decltype(std::ranges::iter_move(std::declval<const InnerIterator&>())),
+                             std::ranges::range_rvalue_reference_t<test_view<cpp20_input_iterator>>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file

>From c1a0811d8824267ff40fa442b2c2ad10d95b7690 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Mon, 20 Jul 2026 00:13:15 +0800
Subject: [PATCH 23/31] Add chunk_view constraint tests

---
 .../range.chunk/concept.compile.pass.cpp      | 64 +++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp
new file mode 100644
index 0000000000000..61b89d4c09c32
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/concept.compile.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+//   template <view V>
+//     requires input_range<V>
+//   class chunk_view;
+//
+//   template <view V>
+//     requires forward_range<V>
+//   class chunk_view<_View>;
+
+#include <ranges>
+#include <utility>
+
+#include "almost_satisfies_types.h"
+#include "test_range.h"
+#include "types.h"
+
+template <>
+inline constexpr bool std::ranges::enable_view<InputRangeNotDerivedFrom> = true;
+
+// Test two separate properties:
+// 1. Whether `chunk_view<View>` can be formed.
+// 2. Whether a well-formed `chunk_view<View>` can be constructed.
+template <class View>
+concept CanFormChunkView = requires { typename std::ranges::chunk_view<View>; };
+
+template <class View>
+concept CanConstructChunkView = CanFormChunkView<View> && requires(View view, std::ranges::range_difference_t<View> n) {
+  std::ranges::chunk_view<View>(std::move(view), n);
+};
+
+// Test constraints when the template argument is not a view
+static_assert(!std::ranges::view<test_non_const_range<cpp17_input_iterator>>);
+static_assert(std::ranges::input_range<test_non_const_range<cpp17_input_iterator>>);
+static_assert(!CanFormChunkView<test_non_const_range<cpp17_input_iterator>>);
+
+// Test constraints when the template argument is not an input_range
+static_assert(!std::ranges::input_range<InputRangeNotDerivedFrom>);
+static_assert(std::ranges::view<InputRangeNotDerivedFrom>);
+static_assert(!CanFormChunkView<InputRangeNotDerivedFrom>);
+
+// Test constraints when the template argument is an input_range and a view
+static_assert(std::ranges::input_range<input_span<int>>);
+static_assert(std::ranges::view<input_span<int>>);
+static_assert(CanFormChunkView<input_span<int>> && CanConstructChunkView<input_span<int>>);
+
+// Test constraints when the template argument is a forward_range and a view
+static_assert(std::ranges::forward_range<BorrowedView>);
+static_assert(std::ranges::view<BorrowedView>);
+static_assert(CanFormChunkView<BorrowedView> && CanConstructChunkView<BorrowedView>);
+
+// chunk_view itself models view
+static_assert(std::ranges::view<std::ranges::chunk_view<input_span<int>>>);
+static_assert(std::ranges::view<std::ranges::chunk_view<BorrowedView>>);

>From 8085a1d3bf45b122557796308e767016f9f7e784 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Mon, 20 Jul 2026 00:13:26 +0800
Subject: [PATCH 24/31] Add chunk_view iterator type tests

---
 .../types.compile.pass.cpp                    | 30 ++++++++++
 .../range.chunk.iter/types.compile.pass.cpp   | 57 +++++++++++++++++++
 .../types.compile.pass.cpp                    | 31 ++++++++++
 3 files changed, 118 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..1775a08f84a2f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/types.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   class inner_iterator;
+
+//   using inner_iterator::iterator_concept = input_iterator_tag;
+//   using inner_iterator::difference_type = range_difference_t<V>;
+//   using inner_iterator::value_type = range_value_t<V>;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+using InnerIterator = std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<input_span<int>>>>;
+
+static_assert(std::same_as<typename InnerIterator::iterator_concept, std::input_iterator_tag>);
+static_assert(std::same_as<typename InnerIterator::difference_type, std::ranges::range_difference_t<input_span<int>>>);
+static_assert(std::same_as<typename InnerIterator::value_type, std::ranges::range_value_t<input_span<int>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..96e3057edc04a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/types.compile.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models forward_range:
+//   class iterator;
+
+//   using iterator::iterator_category = input_iterator_tag;
+//   using iterator::iterator_concept = see below;
+//   using iterator::value_type = decltype(views::take(subrange(current_, end_), n_));
+//   using iterator::difference_type = range_difference_t<Base>;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+template <template <class...> class Iter>
+using ChunkViewFor = std::ranges::chunk_view<test_view<Iter>>;
+
+template <template <class...> class Iter>
+using ChunkIteratorFor = std::ranges::iterator_t<ChunkViewFor<Iter>>;
+
+template <template <class...> class Iter>
+constexpr void test_iterator_types() {
+  using ChunkView     = ChunkViewFor<Iter>;
+  using ChunkIterator = ChunkIteratorFor<Iter>;
+
+  static_assert(std::same_as<typename ChunkIterator::iterator_category, std::input_iterator_tag>);
+  static_assert(std::same_as<typename ChunkIterator::value_type, std::ranges::range_value_t<ChunkView>>);
+  static_assert(std::same_as<typename ChunkIterator::difference_type, std::ranges::range_difference_t<test_view<Iter>>>);
+}
+
+constexpr void test() {
+  test_iterator_types<forward_iterator>();
+  test_iterator_types<bidirectional_iterator>();
+  test_iterator_types<random_access_iterator>();
+  test_iterator_types<contiguous_iterator>();
+
+  static_assert(std::same_as<ChunkIteratorFor<forward_iterator>::iterator_concept, std::forward_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<bidirectional_iterator>::iterator_concept,
+                             std::bidirectional_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<random_access_iterator>::iterator_concept,
+                             std::random_access_iterator_tag>);
+  static_assert(std::same_as<ChunkIteratorFor<contiguous_iterator>::iterator_concept,
+                             std::random_access_iterator_tag>);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.cpp
new file mode 100644
index 0000000000000..9127e567305ce
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/types.compile.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <ranges>
+
+// V models only input_range:
+//   class outer_iterator;
+
+//   using outer_iterator::iterator_concept = input_iterator_tag;
+//   using outer_iterator::difference_type = range_difference_t<V>;
+//   class outer_iterator::value_type;
+
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+using OuterIterator = std::ranges::iterator_t<std::ranges::chunk_view<input_span<int>>>;
+
+static_assert(std::same_as<typename OuterIterator::iterator_concept, std::input_iterator_tag>);
+static_assert(std::same_as<typename OuterIterator::difference_type, std::ranges::range_difference_t<input_span<int>>>);
+static_assert(std::same_as<typename OuterIterator::value_type, std::iter_value_t<OuterIterator>>);
+static_assert(std::ranges::input_range<typename OuterIterator::value_type>);

>From 48bf130465c89823e4c9817180cfa15434057466 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:22:10 +0800
Subject: [PATCH 25/31] Add chunk_view inner iterator special member tests

---
 .../ctor.compile.pass.cpp                     | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp
new file mode 100644
index 0000000000000..ec2e68cb0c8ca
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/ctor.compile.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   inner_iterator(inner_iterator&&) = default;
+//   inner_iterator& operator=(inner_iterator&&) = default;
+
+#include <concepts>
+#include <ranges>
+
+#include "../types.h"
+
+using InnerIterator = std::ranges::iterator_t<std::ranges::range_reference_t<std::ranges::chunk_view<input_span<int>>>>;
+
+static_assert(!std::default_initializable<InnerIterator>);
+static_assert(!std::copy_constructible<InnerIterator>);
+static_assert(!std::assignable_from<InnerIterator&, const InnerIterator&>);
+static_assert(std::move_constructible<InnerIterator>);
+static_assert(std::assignable_from<InnerIterator&, InnerIterator>);

>From 5d32ff5424b490676c9771699233a396532fea1b Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:37:21 +0800
Subject: [PATCH 26/31] Add chunk_view inner iterator base() test

---
 .../range.chunk.inner.iter/base.pass.cpp      | 44 +++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp
new file mode 100644
index 0000000000000..9546aecb01560
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/base.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   constexpr const iterator_t<V> base() const&;
+
+#include <cassert>
+#include <concepts>
+#include <iterator>
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int buffer[] = {1, 2, 3, 4};
+
+  std::ranges::chunk_view<input_span<int>> chunked(input_span<int>(buffer, 4), 2);
+  auto outer = chunked.begin();
+  auto inner = (*outer).begin();
+
+  std::same_as<const std::ranges::iterator_t<input_span<int>>> decltype(auto) base = inner.base();
+  assert(*base == 1);
+
+  ++inner;
+  assert(*inner.base() == 2);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 2c8a8c62abfca361ca45933de0da5eeebd68b290 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:48:43 +0800
Subject: [PATCH 27/31] Fix input_span operator* to return a reference

---
 .../range.chunk/range.chunk.iter/deref.pass.cpp               | 4 ++--
 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h     | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
index b8772d0fac3d2..32054584f4358 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
@@ -53,10 +53,10 @@ constexpr bool test() {
     static_assert(noexcept((inner.end())));
   }
 
-  // Test `constexpr value_type iterator::operator*() const`
+  // Test `constexpr range_reference_v<V> inner_iterator::operator*() const`
   {
     /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin();
-    std::same_as<int> decltype(auto) v                           = *it;
+    std::same_as<int&> decltype(auto) v                          = *it;
     assert(v == 1);
   }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
index 7533e41713d72..83e383e98110e 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h
@@ -23,7 +23,7 @@ struct input_span : std::span<T> {
     using iterator_concept = std::input_iterator_tag;
     constexpr iterator()   = default;
     constexpr iterator(std::span<T>::iterator i) : std::span<T>::iterator(i) {}
-    constexpr auto operator*() const { return std::span<T>::iterator::operator*(); }
+    constexpr decltype(auto) operator*() const { return std::span<T>::iterator::operator*(); }
     friend constexpr auto operator+(iterator, std::span<T>::difference_type) = delete;
     friend constexpr auto operator+(std::span<T>::difference_type, iterator) = delete;
     friend constexpr auto operator-(iterator, std::span<T>::difference_type) = delete;

>From 32f132912f08cfd18cbf8fffff80ee42d178b745 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:48:48 +0800
Subject: [PATCH 28/31] Add chunk_view inner iterator iter_swap test

---
 .../range.chunk.inner.iter/iter_swap.pass.cpp | 50 +++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp
new file mode 100644
index 0000000000000..b5223d5091982
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.inner.iter/iter_swap.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   friend constexpr void iter_swap(const inner_iterator& x, const inner_iterator& y)
+//     noexcept(noexcept(ranges::iter_swap(x.parent_->current_.value(), y.parent_->current_.value())))
+//     requires indirectly_swappable<iterator_t<V>>;
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int a[] = {1, 2, 3, 4};
+  int b[] = {5, 6, 7, 8};
+
+  // Each chunk_view owns its own `current_` cache, so use two independent views to observe a real swap.
+  std::ranges::chunk_view<input_span<int>> chunked_a(input_span<int>(a, 4), 2);
+  std::ranges::chunk_view<input_span<int>> chunked_b(input_span<int>(b, 4), 2);
+
+  auto inner_a = (*chunked_a.begin()).begin();
+  auto inner_b = (*chunked_b.begin()).begin();
+
+  assert(a[0] == 1);
+  assert(b[0] == 5);
+
+  std::ranges::iter_swap(inner_a, inner_b);
+
+  assert(a[0] == 5);
+  assert(b[0] == 1);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 237fa79aa5d1cc327884fef2dd96529686912fa6 Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 15:51:42 +0800
Subject: [PATCH 29/31] Add chunk_view outer iterator value_type size test

---
 .../range.chunk.outer.iter/size.pass.cpp      | 55 +++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/size.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/size.pass.cpp
new file mode 100644
index 0000000000000..51c7cac8da280
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/size.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   constexpr auto outer_iterator::value_type::size() const
+//     requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test `size()` when the range is fully divisible by the chunk size.
+  {
+    std::ranges::chunk_view<input_span<int>> chunked(input_span<int>(arr, 8), 4);
+    auto outer = chunked.begin();
+
+    assert((*outer).size() == 4);
+    ++outer;
+    assert((*outer).size() == 4);
+  }
+
+  // Test `size()` when the last chunk is smaller than the chunk size.
+  {
+    std::ranges::chunk_view<input_span<int>> chunked(input_span<int>(arr, 8), 3);
+    auto outer = chunked.begin();
+
+    assert((*outer).size() == 3);
+    ++outer;
+    assert((*outer).size() == 3);
+    ++outer;
+    assert((*outer).size() == 2);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From b329da3717ddf952907bf38181edd116277bd6bd Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 16:02:22 +0800
Subject: [PATCH 30/31] Add chunk_view outer iterator special member tests

---
 .../ctor.compile.pass.cpp                     | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/ctor.compile.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/ctor.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/ctor.compile.pass.cpp
new file mode 100644
index 0000000000000..51198c1ffea81
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.outer.iter/ctor.compile.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++23
+
+// <ranges>
+
+// V models only input_range:
+//   outer_iterator(outer_iterator&&) = default;
+//   outer_iterator& operator=(outer_iterator&&) = default;
+
+#include <concepts>
+#include <ranges>
+
+#include "../types.h"
+
+using OuterIterator = std::ranges::iterator_t<std::ranges::chunk_view<input_span<int>>>;
+
+static_assert(!std::default_initializable<OuterIterator>);
+static_assert(!std::copy_constructible<OuterIterator>);
+static_assert(!std::assignable_from<OuterIterator&, const OuterIterator&>);
+static_assert(std::move_constructible<OuterIterator>);
+static_assert(std::assignable_from<OuterIterator&, OuterIterator>);

>From 9f7ee27f55bf878305de8382e6086eb166949c5a Mon Sep 17 00:00:00 2001
From: shyeyian <shyeyian at mail.shyeyian.com>
Date: Sat, 25 Jul 2026 16:12:39 +0800
Subject: [PATCH 31/31] Add chunk_view forward iterator base() test

---
 .../range.chunk.iter/base.pass.cpp            | 41 +++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/base.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/base.pass.cpp
new file mode 100644
index 0000000000000..516de09aff5e5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/base.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++23
+
+// <ranges>
+
+// V models forward_range:
+//   constexpr iterator_t<Base> base() const;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <vector>
+
+constexpr bool test() {
+  std::vector<int> vector = {1, 2, 3, 4, 5, 6};
+
+  std::ranges::chunk_view<std::ranges::ref_view<std::vector<int>>> chunked = vector | std::views::chunk(2);
+  auto it = chunked.begin();
+
+  std::same_as<std::vector<int>::iterator> decltype(auto) base = it.base();
+  assert(base == vector.begin());
+
+  ++it;
+  assert(it.base() == vector.begin() + 2);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}


From libcxx-commits at lists.llvm.org  Sat Jul 25 01:57:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 01:57:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic`
 CAS for types with paddings (PR #76180)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76180@github.com>
Message-ID: <6a647a73.d76d81af.396077.4f57@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180

>From 145920aa6c68ee172dc65111a6a0cfb99d4c9069 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 01/34] [libc++] make std::atomic works with types with
 paddings

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__atomic/support/c11.h         |  83 ++++--
 libcxx/include/__atomic/support/common.h      |  75 ++++++
 .../atomics.types.generic/padding.pass.cpp    | 241 ++++++++++++++++++
 4 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 libcxx/include/__atomic/support/common.h
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..11a0b42ea54f5 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
+  __atomic/support/common.h
   __atomic/support/c11.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 899502422570a..4ae0f6a28637a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -32,11 +33,24 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Atomic(_Tp) __a_value;
 };
 
@@ -52,21 +66,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_init(std::addressof(__a->__a_value), __val);
 }
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__val);
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -102,12 +120,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+  std::__clear_padding_if_needed(__value);
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -126,23 +146,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_strong(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -152,23 +178,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __c11_atomic_compare_exchange_weak(
-      std::addressof(__a->__a_value),
-      __expected,
-      __value,
-      static_cast<__memory_order_underlying_t>(__success),
-      static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            __value_maybe_padding_cleared,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(std::addressof(__obj));
+    }
+  }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+    return __cas_func(__expected, std::forward<_Up>(__value));
+  } else {
+    std::__clear_padding_if_needed(__value);
+    remove_cv_t<_Tp> __expected_copy = *__expected;
+    std::__clear_padding_if_needed(__expected_copy);
+    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+      return true;
+    } else {
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      return false;
+    }
+  }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+  std::atomic<Foo> a;
+  Foo loaded = a.load();
+  assert(loaded.i == 0);
+  assert(loaded.c == '\0');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  std::atomic<Foo> a(init);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+  std::atomic<Foo> a;
+  Foo value = make_foo(5, 'x', 0xAB);
+  assert_foo_padding(value, 0xAB);
+  a.store(value);
+  Foo loaded = a.load();
+  assert(loaded.i == 5);
+  assert(loaded.c == 'x');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+  Foo initial = make_foo(1, 'a', 0x00);
+  assert_foo_padding(initial, 0x00);
+  std::atomic<Foo> a(initial);
+  Foo new_val = make_foo(2, 'b', 0xCD);
+  assert_foo_padding(new_val, 0xCD);
+  Foo old = a.exchange(new_val);
+  assert(old.i == 1);
+  assert(old.c == 'a');
+  assert_foo_padding(old, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 2);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+  std::atomic<Foo> a;
+  Foo init = make_foo(7, 'z', 0xEF);
+  assert_foo_padding(init, 0xEF);
+  std::atomic_init(&a, init);
+  Foo loaded = a.load();
+  assert(loaded.i == 7);
+  assert(loaded.c == 'z');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+  // CAS should succeed when only padding differs in expected; expected is unchanged.
+  std::atomic<Foo> a;
+
+  Foo init = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(init, 0xBB);
+  a.store(init);
+
+  Foo expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+
+  alignas(Foo) char original_expected[sizeof(Foo)];
+  std::memcpy(original_expected, &expected, sizeof(Foo));
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(r);
+  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_strong(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  Foo original_expected = make_foo(10, 'a', 0xAA);
+  assert_foo_padding(original_expected, 0xAA);
+
+  bool r = false;
+  while (!r) {
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    r = a.compare_exchange_weak(expected, new_value);
+    if (r) {
+      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    } else {
+      // Spurious failure: expected is updated to the current atomic value.
+      assert(expected.i == 10);
+      assert(expected.c == 'a');
+      assert_foo_padding(expected, 0);
+    }
+  }
+
+  Foo loaded = a.load();
+  assert(loaded.i == 42);
+  assert(loaded.c == 'b');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+  std::atomic<Foo> a;
+  Foo stored = make_foo(10, 'a', 0xBB);
+  assert_foo_padding(stored, 0xBB);
+  a.store(stored);
+
+  Foo expected = make_foo(99, 'a', 0xAA);
+  assert_foo_padding(expected, 0xAA);
+  Foo new_value = make_foo(42, 'b', 0xCC);
+  assert_foo_padding(new_value, 0xCC);
+
+  bool r = a.compare_exchange_weak(expected, new_value);
+
+  assert(!r);
+  assert(expected.i == 10);
+  assert(expected.c == 'a');
+  assert_foo_padding(expected, 0);
+  Foo loaded = a.load();
+  assert(loaded.i == 10);
+  assert(loaded.c == 'a');
+  assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+  // Types with unique object representations skip the padding-clearing path.
+  std::atomic<int> a(1);
+  int expected = 1;
+  assert(a.compare_exchange_strong(expected, 2));
+  assert(expected == 1);
+  assert(a.load() == 2);
+
+  expected = 3;
+  assert(!a.compare_exchange_strong(expected, 4));
+  assert(expected == 2);
+  assert(a.load() == 2);
+}
+
+int main(int, char**) {
+  test_default_constructor();
+  test_value_constructor();
+  test_store();
+  test_exchange();
+  test_atomic_init();
+  test_compare_exchange_strong_success_padding_only();
+  test_compare_exchange_strong_failure();
+  test_compare_exchange_weak_success_padding_only();
+  test_compare_exchange_weak_failure();
+  test_no_padding_type();
+
+  return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)

>From 90d997369a9f358f579ae61963226b1f30c81d5a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 02/34] gcc

---
 libcxx/include/__atomic/support/gcc.h         | 89 +++++++++++++------
 .../atomics.types.generic/padding.pass.cpp    |  5 +-
 2 files changed, 64 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 37c38d25012c6..78e27d9ef9781 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
 #include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #ifndef _LIBCPP_CXX03_LANG
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+
+#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  __cxx_atomic_base_impl() noexcept
+    requires __needs_clear_padding<_Tp>::value
+      : __a_value() {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__a_value);
+    }
+  }
+#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 #else
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+    std::__clear_padding_if_needed(__a_value);
+  }
   _Tp __a_value;
 };
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+  std::__clear_padding_if_needed(__val);
   __a->__a_value = __val;
 }
 
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+  std::__clear_padding_if_needed(__val);
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -115,6 +132,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
   _Tp __ret;
+  std::__clear_padding_if_needed(__value);
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      false,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_compare_exchange(
-      std::addressof(__a->__a_value),
-      __expected,
-      std::addressof(__value),
-      true,
-      __to_gcc_order(__success),
-      __to_gcc_failure_order(__failure));
+  return __atomic_cas_with_clear_padding(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_or_copy,
+            std::addressof(__value_maybe_padding_cleared),
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
   Foo original_expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(original_expected, 0xAA);
 
-  bool r = false;
+  bool r              = false;
+  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
   while (!r) {
+    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
     Foo expected = make_foo(10, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
     r = a.compare_exchange_weak(expected, new_value);

>From 7a63b7897f421f0d367057c933db66b3ea0f21e1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 03/34] lint

---
 libcxx/include/CMakeLists.txt                                | 2 +-
 libcxx/include/__atomic/support/common.h                     | 2 +-
 libcxx/include/__atomic/support/gcc.h                        | 5 ++++-
 libcxx/include/module.modulemap.in                           | 1 +
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 1 -
 5 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 11a0b42ea54f5..caa7f9d1a2535 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
   __atomic/kill_dependency.h
   __atomic/memory_order.h
   __atomic/support.h
-  __atomic/support/common.h
   __atomic/support/c11.h
+  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 78e27d9ef9781..640f6e5e916ca 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+    // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Tp __a_value;
 };
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..8bdde44c1cb8c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -895,6 +895,7 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
+      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS

>From e6a881f9199428718c9b93a92dcea056d7e84356 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 04/34] gcc c++11 ci

---
 libcxx/include/__atomic/support/c11.h    | 2 ++
 libcxx/include/__atomic/support/common.h | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 4ae0f6a28637a..c496ecf5ac742 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -49,7 +49,9 @@ struct __cxx_atomic_base_impl {
   }
 #endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
     std::__clear_padding_if_needed(__a_value);
+#endif
   }
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+#  if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+#  endif
+    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {

>From 13589231818d564e6ba520c405eae011f72f10fe Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 05/34] Update
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
   Foo expected = make_foo(10, 'a', 0xAA);
   assert_foo_padding(expected, 0xAA);
 
-  alignas(Foo) char original_expected[sizeof(Foo)];
-  std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
 
   Foo new_value = make_foo(42, 'b', 0xCC);
   assert_foo_padding(new_value, 0xCC);

>From d3c6b5e3b74e59c2beaf5b3158db90faef36600c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 06/34] Update libcxx/include/__atomic/support/c11.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/c11.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c496ecf5ac742..c55de19cd4b59 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -148,7 +148,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),

>From 11cedf33d3fe73d5bad5930a8c4f12df33e0eac8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 07/34] Update libcxx/include/__atomic/support/common.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__atomic/support/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 }
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
     return __cas_func(__expected, std::forward<_Up>(__value));
   } else {

>From 7e6410501ecdccbd78ce814b0e4906a60f379df6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 08/34] review comments batch 1

---
 libcxx/include/__atomic/support/c11.h         |   6 +-
 libcxx/include/__atomic/support/common.h      |  18 +-
 libcxx/include/__atomic/support/gcc.h         |  13 +-
 .../atomics.types.generic/padding.pass.cpp    | 114 +++++++
 .../atomics.types.generic/padding.pass.cpp    | 316 ++++++++----------
 5 files changed, 266 insertions(+), 201 deletions(-)
 create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index c55de19cd4b59..0cf50d7ebd1ec 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -162,7 +162,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
@@ -180,7 +180,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
@@ -195,7 +195,7 @@ template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
     : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
-    return __cas_func(__expected, std::forward<_Up>(__value));
+    return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
     remove_cv_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+    if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
       std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-#  if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-#  endif
-    void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
 
 template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
-  return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
 }
 
 #endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 640f6e5e916ca..44f1229f859b3 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
   __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
 #endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
   _Tp __a_value;
 };
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     _Tp __value,
     memory_order __success,
     memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
-  return __atomic_cas_with_clear_padding(
+  return std::__atomic_cas_with_clear_padding(
       __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
         return __atomic_compare_exchange(
             std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+  {
+    // atomic();
+    std::atomic<Foo> a;
+    Foo loaded = a.load();
+    assert(loaded.i == 0);
+    assert(loaded.c == '\0');
+    assert_foo_padding(loaded, 0);
+  }
+
+  {
+    // atomic(T);
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    std::atomic<Foo> a(init);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::store
+    std::atomic<Foo> a;
+    Foo value = make_foo(5, 'x', 0xAB);
+    assert_foo_padding(value, 0xAB);
+    a.store(value);
+    Foo loaded = a.load();
+    assert(loaded.i == 5);
+    assert(loaded.c == 'x');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic::exchange
+    Foo initial = make_foo(1, 'a', 0x00);
+    assert_foo_padding(initial, 0x00);
+    std::atomic<Foo> a(initial);
+    Foo new_val = make_foo(2, 'b', 0xCD);
+    assert_foo_padding(new_val, 0xCD);
+    Foo old = a.exchange(new_val);
+    assert(old.i == 1);
+    assert(old.c == 'a');
+    assert_foo_padding(old, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 2);
+    assert(loaded.c == 'b');
+    assert_foo_padding(loaded, 0);
+  }
+  {
+    // atomic_init
+    std::atomic<Foo> a;
+    Foo init = make_foo(7, 'z', 0xEF);
+    assert_foo_padding(init, 0xEF);
+    std::atomic_init(&a, init);
+    Foo loaded = a.load();
+    assert(loaded.i == 7);
+    assert(loaded.c == 'z');
+    assert_foo_padding(loaded, 0);
+  }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
 #include <type_traits>
 
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
   assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
 }
 
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
-  std::atomic<Foo> a;
-  Foo loaded = a.load();
-  assert(loaded.i == 0);
-  assert(loaded.c == '\0');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  std::atomic<Foo> a(init);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
-  std::atomic<Foo> a;
-  Foo value = make_foo(5, 'x', 0xAB);
-  assert_foo_padding(value, 0xAB);
-  a.store(value);
-  Foo loaded = a.load();
-  assert(loaded.i == 5);
-  assert(loaded.c == 'x');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
-  Foo initial = make_foo(1, 'a', 0x00);
-  assert_foo_padding(initial, 0x00);
-  std::atomic<Foo> a(initial);
-  Foo new_val = make_foo(2, 'b', 0xCD);
-  assert_foo_padding(new_val, 0xCD);
-  Foo old = a.exchange(new_val);
-  assert(old.i == 1);
-  assert(old.c == 'a');
-  assert_foo_padding(old, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 2);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
 }
 
-void test_atomic_init() {
-  std::atomic<Foo> a;
-  Foo init = make_foo(7, 'z', 0xEF);
-  assert_foo_padding(init, 0xEF);
-  std::atomic_init(&a, init);
-  Foo loaded = a.load();
-  assert(loaded.i == 7);
-  assert(loaded.c == 'z');
-  assert_foo_padding(loaded, 0);
-}
+void test() {
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
 
-void test_compare_exchange_strong_success_padding_only() {
-  // CAS should succeed when only padding differs in expected; expected is unchanged.
-  std::atomic<Foo> a;
+    Foo init = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(init, 0xBB);
+    a.store(init);
 
-  Foo init = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(init, 0xBB);
-  a.store(init);
+    Foo expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
 
-  Foo expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
 
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    bool r = a.compare_exchange_strong(expected, new_value);
 
-  bool r = a.compare_exchange_strong(expected, new_value);
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  assert(r);
-  assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-void test_compare_exchange_strong_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_strong(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
+    Foo expected = make_foo(99, 'a', 0xAA);
+    assert_foo_padding(expected, 0xAA);
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-void test_compare_exchange_weak_success_padding_only() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    assert_foo_padding(original_expected, 0xAA);
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        assert(expected.i == 10);
+        assert(expected.c == 'a');
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+      }
+    }
 
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+  }
 
-  Foo original_expected = make_foo(10, 'a', 0xAA);
-  assert_foo_padding(original_expected, 0xAA);
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    assert_foo_padding(stored, 0xBB);
+    a.store(stored);
 
-  bool r              = false;
-  const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
-  while (!r) {
-    assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
-    Foo expected = make_foo(10, 'a', 0xAA);
+    Foo expected = make_foo(99, 'a', 0xAA);
     assert_foo_padding(expected, 0xAA);
-    r = a.compare_exchange_weak(expected, new_value);
-    if (r) {
-      assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    } else {
-      // Spurious failure: expected is updated to the current atomic value.
-      assert(expected.i == 10);
-      assert(expected.c == 'a');
-      assert_foo_padding(expected, 0);
-    }
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    assert_foo_padding(new_value, 0xCC);
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    assert(!r);
+    assert(expected.i == 10);
+    assert(expected.c == 'a');
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    assert(loaded.c == 'a');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
   }
 
-  Foo loaded = a.load();
-  assert(loaded.i == 42);
-  assert(loaded.c == 'b');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
-  std::atomic<Foo> a;
-  Foo stored = make_foo(10, 'a', 0xBB);
-  assert_foo_padding(stored, 0xBB);
-  a.store(stored);
-
-  Foo expected = make_foo(99, 'a', 0xAA);
-  assert_foo_padding(expected, 0xAA);
-  Foo new_value = make_foo(42, 'b', 0xCC);
-  assert_foo_padding(new_value, 0xCC);
-
-  bool r = a.compare_exchange_weak(expected, new_value);
-
-  assert(!r);
-  assert(expected.i == 10);
-  assert(expected.c == 'a');
-  assert_foo_padding(expected, 0);
-  Foo loaded = a.load();
-  assert(loaded.i == 10);
-  assert(loaded.c == 'a');
-  assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
-  // Types with unique object representations skip the padding-clearing path.
-  std::atomic<int> a(1);
-  int expected = 1;
-  assert(a.compare_exchange_strong(expected, 2));
-  assert(expected == 1);
-  assert(a.load() == 2);
-
-  expected = 3;
-  assert(!a.compare_exchange_strong(expected, 4));
-  assert(expected == 2);
-  assert(a.load() == 2);
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    assert(a.compare_exchange_strong(expected, 2));
+    assert(expected == 1);
+    assert(a.load() == 2);
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    assert(expected == 2);
+    assert(a.load() == 2);
+  }
 }
 
 int main(int, char**) {
-  test_default_constructor();
-  test_value_constructor();
-  test_store();
-  test_exchange();
-  test_atomic_init();
-  test_compare_exchange_strong_success_padding_only();
-  test_compare_exchange_strong_failure();
-  test_compare_exchange_weak_success_padding_only();
-  test_compare_exchange_weak_failure();
-  test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+  test();
+#endif // __has_builtin(__builtin_clear_padding)
 
   return 0;
 }
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)

>From f666483df06fffa6f94a403ba051621cf0432415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/34] batch2

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 .../{support/common.h => clear_padding.h}     | 27 ++++++++++---------
 libcxx/include/__atomic/support/c11.h         | 25 ++++-------------
 libcxx/include/__atomic/support/gcc.h         | 20 +++-----------
 libcxx/include/module.modulemap.in            |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  7 ++---
 .../atomics.types.generic/padding.pass.cpp    |  5 ++--
 7 files changed, 29 insertions(+), 59 deletions(-)
 rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index caa7f9d1a2535..b6650a314bd40 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
   __atomic/atomic_sync_timed.h
   __atomic/atomic_waitable_traits.h
   __atomic/check_memory_order.h
+  __atomic/clear_padding.h
   __atomic/contention_t.h
   __atomic/fence.h
   __atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
   __atomic/memory_order.h
   __atomic/support.h
   __atomic/support/c11.h
-  __atomic/support/common.h
   __atomic/support/gcc.h
   __atomic/to_gcc_order.h
   __bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
 
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/negation.h>
 #include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
+           _Not<is_same<_Tp, float>>,
+           _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);
-    remove_cv_t<_Tp> __expected_copy = *__expected;
+    __remove_cvref_t<_Tp> __expected_copy = *__expected;
     std::__clear_padding_if_needed(__expected_copy);
     if (__cas_func(std::addressof(__expected_copy), __value)) {
       return true;
     } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
       return false;
     }
   }
 }
 
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
   return __cas_func(__expected, __value);
 }
 
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0cf50d7ebd1ec..05b22d04962fa 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
 #define _LIBCPP___ATOMIC_SUPPORT_C11_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
@@ -30,29 +30,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
   }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
 
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
-  _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
-#endif
   }
+
   _Atomic(_Tp) __a_value;
 };
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 44f1229f859b3..0a6e3d54b5911 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
 #ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
 #define _LIBCPP___ATOMIC_SUPPORT_GCC_H
 
+#include <__atomic/clear_padding.h>
 #include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
 #include <__atomic/to_gcc_order.h>
 #include <__config>
 #include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
   _LIBCPP_HIDE_FROM_ABI
-#ifndef _LIBCPP_CXX03_LANG
-  __cxx_atomic_base_impl() _NOEXCEPT = default;
-
-#  if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-  __cxx_atomic_base_impl() noexcept
-    requires __needs_clear_padding<_Tp>::value
-      : __a_value() {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(__a_value);
-    }
-  }
-#  endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
-#else
-  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-  }
-#endif // _LIBCPP_CXX03_LANG
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 8bdde44c1cb8c..f038c9d39a27b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -884,6 +884,7 @@ module std {
       export std.atomic.atomic_base // most of std::atomic methods are defined there
     }
     module check_memory_order     { header "__atomic/check_memory_order.h" }
+    module clear_padding          { header "__atomic/clear_padding.h" }
     module contention_t           { header "__atomic/contention_t.h" }
     module fence                  { header "__atomic/fence.h" }
     module floating_point_helper  { header "__atomic/floating_point_helper.h" }
@@ -895,7 +896,6 @@ module std {
     module support {
       header "__atomic/support.h"
       textual header "__atomic/support/c11.h"
-      textual header "__atomic/support/common.h"
       textual header "__atomic/support/gcc.h"
     }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
 
 #include <atomic>
 #include <cassert>
-#include <chrono>
 #include <cstring>
-#include <type_traits>
 
 struct Foo {
   int i;
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
   char c;
 };
 
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
 
 Foo make_foo(int i, char c, unsigned char pad_byte) {
   Foo f;

>From f622cc3f082c34d6f0c5cd6e47177664eed48938 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/34] CI

---
 libcxx/include/__atomic/clear_padding.h                  | 9 +++++++--
 .../std/atomics/atomics.types.generic/padding.pass.cpp   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double>>> {};
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
     if (!__builtin_is_constant_evaluated()) {
       __builtin_clear_padding(std::addressof(__obj));
     }
   }
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
 }
 
 template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 #else // __has_builtin(__builtin_clear_padding)
 
 template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+  // return __obj as c++11 does not constexpr function to return void
+  return __obj;
+}
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
 
   return 0;
 }

>From 1e55b9997f985525c33ac424b94de00d91b36bba Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/34] review comment

---
 libcxx/include/__atomic/support/gcc.h         |   7 +-
 .../atomics.types.generic/padding.pass.cpp    | 112 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 153 ++++++----
 .../atomics.types.generic/padding2.pass.cpp   | 276 ++++++++++++++++++
 4 files changed, 439 insertions(+), 109 deletions(-)
 create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 0a6e3d54b5911..2f6e0d5dbd20a 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(__a_value);
+  }
 
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
     std::__clear_padding_if_needed(__a_value);
   }
   _Tp __a_value;
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
 #include <cassert>
 #include <cstring>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
 void test() {
   {
     // atomic();
-    std::atomic<Foo> a;
-    Foo loaded = a.load();
+    std::atomic<T> a;
+    T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 
   {
     // atomic(T);
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
-    std::atomic<Foo> a(init);
-    Foo loaded = a.load();
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
+    std::atomic<T> a(init);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::store
-    std::atomic<Foo> a;
-    Foo value = make_foo(5, 'x', 0xAB);
-    assert_foo_padding(value, 0xAB);
+    std::atomic<T> a;
+    T value = make<T>(5, 'x', 0xAB);
+    assert_padding(value, 0xAB);
     a.store(value);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic::exchange
-    Foo initial = make_foo(1, 'a', 0x00);
-    assert_foo_padding(initial, 0x00);
-    std::atomic<Foo> a(initial);
-    Foo new_val = make_foo(2, 'b', 0xCD);
-    assert_foo_padding(new_val, 0xCD);
-    Foo old = a.exchange(new_val);
+    T initial = make<T>(1, 'a', 0x00);
+    assert_padding(initial, 0x00);
+    std::atomic<T> a(initial);
+    T new_val = make<T>(2, 'b', 0xCD);
+    assert_padding(new_val, 0xCD);
+    T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_foo_padding(old, 0);
-    Foo loaded = a.load();
+    assert_padding(old, 0);
+    T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
   {
     // atomic_init
-    std::atomic<Foo> a;
-    Foo init = make_foo(7, 'z', 0xEF);
-    assert_foo_padding(init, 0xEF);
+    std::atomic<T> a;
+    T init = make<T>(7, 'z', 0xEF);
+    assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_foo_padding(loaded, 0);
+    assert_padding(loaded, 0);
   }
 }
 
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
 #include <cstring>
 #include <type_traits>
 
-struct Foo {
+struct WithTailPadding {
   int i;
   char c;
 };
 
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > sizeof(int) + sizeof(char), "");
 
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
+struct WithInternalPadding {
+  char c;
+  int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+  char c;
+  int i;
+  char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+  T obj;
+  std::memset(&obj, pad_byte, sizeof(T));
+  obj.i = i;
+  obj.c = c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    obj.c2 = c;
+  }
+  return obj;
 }
 
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  reference.i  = obj.i;
+  reference.c  = obj.c;
+  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+    reference.c2 = obj.c2;
+  }
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
+  assert_padding(obj, pad_byte);
 #else
   (void)f;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
 void test() {
   {
     // compare_exchange_strong
     // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
+    std::atomic<T> a;
 
-    Foo init = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(init, 0xBB);
+    T init = make<T>(10, 'a', 0xBB);
+    assert_padding(init, 0xBB);
     a.store(init);
 
-    Foo expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
+    T expected = make<T>(10, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
 
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
     assert(r);
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    Foo loaded = a.load();
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
 
@@ -101,27 +126,27 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    assert_foo_padding(original_expected, 0xAA);
+    T original_expected = make<T>(10, 'a', 0xAA);
+    assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
     const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
+      T expected = make<T>(10, 'a', 0xAA);
+      assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
       } else {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
+        libcpp_assert_padding(expected, 0);
       }
     }
 
-    Foo loaded = a.load();
+    T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
     // compare_exchange_strong
     // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    assert_foo_padding(stored, 0xBB);
+    std::atomic<T> a;
+    T stored = make<T>(10, 'a', 0xBB);
+    assert_padding(stored, 0xBB);
     a.store(stored);
 
-    Foo expected = make_foo(99, 'a', 0xAA);
-    assert_foo_padding(expected, 0xAA);
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    assert_foo_padding(new_value, 0xCC);
+    T expected = make<T>(99, 'a', 0xAA);
+    assert_padding(expected, 0xAA);
+    T new_value = make<T>(42, 'b', 0xCC);
+    assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
 
@@ -169,12 +194,12 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    Foo loaded = a.load();
+    libcpp_assert_padding(expected, 0);
+    T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
+    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -195,7 +220,9 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL: clang-22
 #if __has_builtin(__builtin_clear_padding)
-  test();
+  test<WithTailPadding>();
+  test<WithInternalPadding>();
+  test<WithInternalAndTailPadding>();
 #endif
 
   return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+  int i;
+  char c;
+};
+
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+  Foo f;
+  std::memset(&f, pad_byte, sizeof(Foo));
+  f.i = i;
+  f.c = c;
+  return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+  alignas(Foo) unsigned char buf[sizeof(Foo)];
+  std::memset(buf, pad_byte, sizeof(Foo));
+  Foo& reference = *reinterpret_cast<Foo*>(buf);
+  reference.i    = f.i;
+  reference.c    = f.c;
+  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+  assert_foo_padding(f, pad_byte);
+#else
+  (void)f;
+  (void)pad_byte;
+#endif
+}
+
+void test() {
+  std::cerr << "test 1" << std::endl;
+  {
+    // compare_exchange_strong
+    // CAS should succeed when only padding differs in expected; expected is unchanged.
+    std::atomic<Foo> a;
+    std::cerr << "test 2" << std::endl;
+
+    Foo init = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 3" << std::endl;
+    assert_foo_padding(init, 0xBB);
+    std::cerr << "test 4" << std::endl;
+    a.store(init);
+
+    Foo expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 5" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 6" << std::endl;
+
+    Foo original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 7" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 8" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 9" << std::endl;
+    assert(r);
+    std::cerr << "test 10" << std::endl;
+    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+    std::cerr << "test 11" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 42);
+    std::cerr << "test 12" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 13" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 14" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 15" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 16" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 17" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 18" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 19" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 10" << std::endl;
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    std::cerr << "test 21" << std::endl;
+    assert(!r);
+    std::cerr << "test 22" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 23" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 24" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 25" << std::endl;
+    Foo loaded = a.load();
+    assert(loaded.i == 10);
+    std::cerr << "test 26" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 27" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 28" << std::endl;
+  }
+
+  {
+    // compare_exchange_weak
+    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 29" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 30" << std::endl;
+    a.store(stored);
+
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 31" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 32" << std::endl;
+
+    Foo original_expected = make_foo(10, 'a', 0xAA);
+    std::cerr << "test 33" << std::endl;
+    assert_foo_padding(original_expected, 0xAA);
+    std::cerr << "test 34" << std::endl;
+
+    bool r                  = false;
+    const auto max_attempts = 100;
+    auto current_attempt    = 0;
+    while (!r) {
+      ++current_attempt;
+      std::cerr << "test 35" << std::endl;
+      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+      std::cerr << "test 36" << std::endl;
+      Foo expected = make_foo(10, 'a', 0xAA);
+      assert_foo_padding(expected, 0xAA);
+      std::cerr << "test 37" << std::endl;
+      r = a.compare_exchange_weak(expected, new_value);
+      if (r) {
+        std::cerr << "test 38" << std::endl;
+        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+        std::cerr << "test 39" << std::endl;
+      } else {
+        // Spurious failure: expected is updated to the current atomic value.
+        std::cerr << "test 40" << std::endl;
+        assert(expected.i == 10);
+        std::cerr << "test 41" << std::endl;
+        assert(expected.c == 'a');
+        std::cerr << "test 45" << std::endl;
+        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        libcpp_assert_foo_padding(expected, 0);
+        std::cerr << "test 43" << std::endl;
+      }
+    }
+
+    Foo loaded = a.load();
+    std::cerr << "test 44" << std::endl;
+    assert(loaded.i == 42);
+    std::cerr << "test 45" << std::endl;
+    assert(loaded.c == 'b');
+    std::cerr << "test 46" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 47" << std::endl;
+  }
+
+  {
+    // compare_exchange_strong
+    // atomic and expected values are different; failure
+    std::atomic<Foo> a;
+    Foo stored = make_foo(10, 'a', 0xBB);
+    std::cerr << "test 48" << std::endl;
+    assert_foo_padding(stored, 0xBB);
+    std::cerr << "test 49" << std::endl;
+    a.store(stored);
+
+    Foo expected = make_foo(99, 'a', 0xAA);
+    std::cerr << "test 50" << std::endl;
+    assert_foo_padding(expected, 0xAA);
+    std::cerr << "test 51" << std::endl;
+    Foo new_value = make_foo(42, 'b', 0xCC);
+    std::cerr << "test 52" << std::endl;
+    assert_foo_padding(new_value, 0xCC);
+    std::cerr << "test 53" << std::endl;
+
+    bool r = a.compare_exchange_weak(expected, new_value);
+
+    std::cerr << "test 54" << std::endl;
+    assert(!r);
+    std::cerr << "test 55" << std::endl;
+    assert(expected.i == 10);
+    std::cerr << "test 56" << std::endl;
+    assert(expected.c == 'a');
+    std::cerr << "test 57" << std::endl;
+    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    libcpp_assert_foo_padding(expected, 0);
+    std::cerr << "test 58" << std::endl;
+    Foo loaded = a.load();
+    std::cerr << "test 59" << std::endl;
+    assert(loaded.i == 10);
+    std::cerr << "test 60" << std::endl;
+    assert(loaded.c == 'a');
+    std::cerr << "test 61" << std::endl;
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_foo_padding(loaded, 0);
+    std::cerr << "test 62" << std::endl;
+  }
+
+  {
+    // Types with unique object representations skip the padding-clearing path.
+    std::atomic<int> a(1);
+    int expected = 1;
+    std::cerr << "test 63" << std::endl;
+    assert(a.compare_exchange_strong(expected, 2));
+    std::cerr << "test 64" << std::endl;
+    assert(expected == 1);
+    std::cerr << "test 65" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 66" << std::endl;
+
+    expected = 3;
+    assert(!a.compare_exchange_strong(expected, 4));
+    std::cerr << "test 67" << std::endl;
+    assert(expected == 2);
+    std::cerr << "test 68" << std::endl;
+    assert(a.load() == 2);
+    std::cerr << "test 69" << std::endl;
+  }
+}
+
+int main(int, char**) {
+  // TODO(LLVM-23): Switch to XFAIL: clang-22
+  std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+  std::cerr << "main 2" << std::endl;
+  test();
+  std::cerr << "main 3" << std::endl;
+#endif
+  std::cerr << "main 4" << std::endl;
+  return 0;
+}

>From 996293d9cfb334a462283e783e2fb2af90bb373c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/34] review

---
 .../atomics.types.generic/padding.pass.cpp    |  7 +++--
 .../atomics.types.generic/padding.pass.cpp    | 27 +++++++++++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
 
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
 // libc++ maintains the invariant of the atomic to have zero for all padding bits
 
 #include <atomic>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
     libcpp_assert_padding(loaded, 0);
   }
+  {
+    // compare_exchange_strong
+    // atomic and expected are the same, including padding
+
+    T init = make<T>(10, 'a', 0x00);
+    assert_padding(init, 0x00);
+    a.store(init);
+
+    T expected = make<T>(10, 'a', 0x00);
+    assert_padding(expected, 0x00);
+
+    T original_expected; // make a copy including padding bits
+    std::memcpy(&original_expected, &expected, sizeof(T));
+
+    T new_value = make<T>(42, 'b', 0x42);
+    assert_padding(new_value, 0x42);
+
+    bool r = a.compare_exchange_strong(expected, new_value);
+
+    assert(r);
+    assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+    T loaded = a.load();
+    assert(loaded.i == 42);
+    assert(loaded.c == 'b');
+    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    libcpp_assert_padding(loaded, 0);
+  }
 
   {
     // compare_exchange_weak

>From f74f609379e9feab234dd430bc758b19d9b1d46b Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/34] 14

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From 8c0b1998421e1c8e214ee2694544bf3f9d8f5f79 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/34] 14

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
 #include <atomic>
 #include <cassert>
 #include <cstring>
+#include <type_traits>
 
 struct WithTailPadding {
   int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   obj.i = i;
   obj.c = c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
     obj.c2 = c;
   }
   return obj;

>From f436e600ce39701927e30d0074019afc4cdb581c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 20:42:14 +0100
Subject: [PATCH 15/34] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e4d83e638ca77..0bafdbb1d7ede 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -136,7 +136,7 @@ void test() {
   {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
-
+    std::atomic<T> a;
     T init = make<T>(10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);

>From 4def68f03c3089677bb5206f56f30e367415ecce Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:13:05 +0100
Subject: [PATCH 16/34] ci

---
 libcxx/include/__atomic/support/gcc.h         |  5 ++--
 .../atomics.types.generic/padding.pass.cpp    | 24 ++++++++++-------
 .../atomics.types.generic/padding.pass.cpp    | 27 ++++++++++---------
 .../atomics.types.generic/padding2.pass.cpp   |  3 ---
 4 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 2f6e0d5dbd20a..b162e2c2a63f0 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -50,9 +50,8 @@ struct __cxx_atomic_base_impl {
     std::__clear_padding_if_needed(__a_value);
   }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
   _Tp __a_value;
 };
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index bf594b45ed54f..6f894fe6cb605 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -44,15 +44,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -61,11 +69,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 0bafdbb1d7ede..344d08b493e60 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,9 +7,6 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits
@@ -41,15 +38,23 @@ struct WithInternalAndTailPadding {
 
 static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
 
+template <class T>
+void set(T& obj, int i, char c) {
+  obj.i = i;
+  obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+  obj.i  = i;
+  obj.c  = c;
+  obj.c2 = c;
+}
+
 template <class T>
 T make(int i, char c, unsigned char pad_byte) {
   T obj;
   std::memset(&obj, pad_byte, sizeof(T));
-  obj.i = i;
-  obj.c = c;
-  if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
-    obj.c2 = c;
-  }
+  set(obj, i, c);
   return obj;
 }
 
@@ -58,11 +63,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   alignas(T) unsigned char buf[sizeof(T)];
   std::memset(buf, pad_byte, sizeof(T));
   T& reference = *reinterpret_cast<T*>(buf);
-  reference.i  = obj.i;
-  reference.c  = obj.c;
-  if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
-    reference.c2 = obj.c2;
-  }
+  set(reference, obj.i, obj.c);
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
index 4e17471a2461f..1040f342b8206 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -8,9 +8,6 @@
 // UNSUPPORTED: c++03
 // UNSUPPORTED: no-localization
 
-// atomic_init is deprecated
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
 // CAS should work on types with padding bits

>From eeecefecde068ea1f6e2f8417b52961192ca8bc4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:29:04 +0100
Subject: [PATCH 17/34] CI

---
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding.pass.cpp    |   2 +-
 .../atomics.types.generic/padding2.pass.cpp   | 273 ------------------
 3 files changed, 2 insertions(+), 275 deletions(-)
 delete mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 6f894fe6cb605..388ab0867fe38 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 344d08b493e60..f1a534a2916d6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
deleted file mode 100644
index 1040f342b8206..0000000000000
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-localization
-
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
-// CAS should work on types with padding bits
-
-#include <atomic>
-#include <cassert>
-#include <cstring>
-#include <type_traits>
-#include <iostream>
-
-struct Foo {
-  int i;
-  char c;
-};
-
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
-
-Foo make_foo(int i, char c, unsigned char pad_byte) {
-  Foo f;
-  std::memset(&f, pad_byte, sizeof(Foo));
-  f.i = i;
-  f.c = c;
-  return f;
-}
-
-void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-  alignas(Foo) unsigned char buf[sizeof(Foo)];
-  std::memset(buf, pad_byte, sizeof(Foo));
-  Foo& reference = *reinterpret_cast<Foo*>(buf);
-  reference.i    = f.i;
-  reference.c    = f.c;
-  assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
-}
-
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
-#ifdef _LIBCPP_VERSION
-  assert_foo_padding(f, pad_byte);
-#else
-  (void)f;
-  (void)pad_byte;
-#endif
-}
-
-void test() {
-  std::cerr << "test 1" << std::endl;
-  {
-    // compare_exchange_strong
-    // CAS should succeed when only padding differs in expected; expected is unchanged.
-    std::atomic<Foo> a;
-    std::cerr << "test 2" << std::endl;
-
-    Foo init = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 3" << std::endl;
-    assert_foo_padding(init, 0xBB);
-    std::cerr << "test 4" << std::endl;
-    a.store(init);
-
-    Foo expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 5" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 6" << std::endl;
-
-    Foo original_expected; // make a copy including padding bits
-    std::memcpy(&original_expected, &expected, sizeof(Foo));
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 7" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 8" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 9" << std::endl;
-    assert(r);
-    std::cerr << "test 10" << std::endl;
-    assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-    std::cerr << "test 11" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 42);
-    std::cerr << "test 12" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 13" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 14" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 15" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 16" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 17" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 18" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 19" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 10" << std::endl;
-
-    bool r = a.compare_exchange_strong(expected, new_value);
-
-    std::cerr << "test 21" << std::endl;
-    assert(!r);
-    std::cerr << "test 22" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 23" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 24" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 25" << std::endl;
-    Foo loaded = a.load();
-    assert(loaded.i == 10);
-    std::cerr << "test 26" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 27" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 28" << std::endl;
-  }
-
-  {
-    // compare_exchange_weak
-    // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 29" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 30" << std::endl;
-    a.store(stored);
-
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 31" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 32" << std::endl;
-
-    Foo original_expected = make_foo(10, 'a', 0xAA);
-    std::cerr << "test 33" << std::endl;
-    assert_foo_padding(original_expected, 0xAA);
-    std::cerr << "test 34" << std::endl;
-
-    bool r                  = false;
-    const auto max_attempts = 100;
-    auto current_attempt    = 0;
-    while (!r) {
-      ++current_attempt;
-      std::cerr << "test 35" << std::endl;
-      assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      std::cerr << "test 36" << std::endl;
-      Foo expected = make_foo(10, 'a', 0xAA);
-      assert_foo_padding(expected, 0xAA);
-      std::cerr << "test 37" << std::endl;
-      r = a.compare_exchange_weak(expected, new_value);
-      if (r) {
-        std::cerr << "test 38" << std::endl;
-        assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
-        std::cerr << "test 39" << std::endl;
-      } else {
-        // Spurious failure: expected is updated to the current atomic value.
-        std::cerr << "test 40" << std::endl;
-        assert(expected.i == 10);
-        std::cerr << "test 41" << std::endl;
-        assert(expected.c == 'a');
-        std::cerr << "test 45" << std::endl;
-        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_foo_padding(expected, 0);
-        std::cerr << "test 43" << std::endl;
-      }
-    }
-
-    Foo loaded = a.load();
-    std::cerr << "test 44" << std::endl;
-    assert(loaded.i == 42);
-    std::cerr << "test 45" << std::endl;
-    assert(loaded.c == 'b');
-    std::cerr << "test 46" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 47" << std::endl;
-  }
-
-  {
-    // compare_exchange_strong
-    // atomic and expected values are different; failure
-    std::atomic<Foo> a;
-    Foo stored = make_foo(10, 'a', 0xBB);
-    std::cerr << "test 48" << std::endl;
-    assert_foo_padding(stored, 0xBB);
-    std::cerr << "test 49" << std::endl;
-    a.store(stored);
-
-    Foo expected = make_foo(99, 'a', 0xAA);
-    std::cerr << "test 50" << std::endl;
-    assert_foo_padding(expected, 0xAA);
-    std::cerr << "test 51" << std::endl;
-    Foo new_value = make_foo(42, 'b', 0xCC);
-    std::cerr << "test 52" << std::endl;
-    assert_foo_padding(new_value, 0xCC);
-    std::cerr << "test 53" << std::endl;
-
-    bool r = a.compare_exchange_weak(expected, new_value);
-
-    std::cerr << "test 54" << std::endl;
-    assert(!r);
-    std::cerr << "test 55" << std::endl;
-    assert(expected.i == 10);
-    std::cerr << "test 56" << std::endl;
-    assert(expected.c == 'a');
-    std::cerr << "test 57" << std::endl;
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_foo_padding(expected, 0);
-    std::cerr << "test 58" << std::endl;
-    Foo loaded = a.load();
-    std::cerr << "test 59" << std::endl;
-    assert(loaded.i == 10);
-    std::cerr << "test 60" << std::endl;
-    assert(loaded.c == 'a');
-    std::cerr << "test 61" << std::endl;
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_foo_padding(loaded, 0);
-    std::cerr << "test 62" << std::endl;
-  }
-
-  {
-    // Types with unique object representations skip the padding-clearing path.
-    std::atomic<int> a(1);
-    int expected = 1;
-    std::cerr << "test 63" << std::endl;
-    assert(a.compare_exchange_strong(expected, 2));
-    std::cerr << "test 64" << std::endl;
-    assert(expected == 1);
-    std::cerr << "test 65" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 66" << std::endl;
-
-    expected = 3;
-    assert(!a.compare_exchange_strong(expected, 4));
-    std::cerr << "test 67" << std::endl;
-    assert(expected == 2);
-    std::cerr << "test 68" << std::endl;
-    assert(a.load() == 2);
-    std::cerr << "test 69" << std::endl;
-  }
-}
-
-int main(int, char**) {
-  // TODO(LLVM-23): Switch to XFAIL: clang-22
-  std::cerr << "main 1" << std::endl;
-#if __has_builtin(__builtin_clear_padding)
-  std::cerr << "main 2" << std::endl;
-  test();
-  std::cerr << "main 3" << std::endl;
-#endif
-  std::cerr << "main 4" << std::endl;
-  return 0;
-}

>From bc539fb8d58e16f24a5ccb8bdd2351475e7d4391 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:52:15 +0100
Subject: [PATCH 18/34] ci

---
 libcxx/include/__atomic/support/c11.h | 13 ++++++++-----
 libcxx/include/__atomic/support/gcc.h |  9 +++++++--
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 05b22d04962fa..d43a2c67f950a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -30,13 +30,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
-    std::__clear_padding_if_needed(__a_value);
-  }
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+      : __a_value(std::__clear_padding_if_needed(__value)) {}
 
   _Atomic(_Tp) __a_value;
 };
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b162e2c2a63f0..d2d4919d54614 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,12 +46,17 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
 
 template <typename _Tp>
 struct __cxx_atomic_base_impl {
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
-    std::__clear_padding_if_needed(__a_value);
+  _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+  __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
   }
+#endif // _LIBCPP_CXX03_LANG
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
       : __a_value(std::__clear_padding_if_needed(__value)) {}
+
   _Tp __a_value;
 };
 

>From 30a056210700cd8f89b1c0ca6991f272a0ebba43 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:26:08 +0100
Subject: [PATCH 19/34] review

---
 libcxx/include/__atomic/clear_padding.h            | 14 ++++++--------
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 .../atomics/atomics.types.generic/padding.pass.cpp |  2 +-
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 5bc77ff4401ab..f920ef39977b7 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -35,17 +35,16 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, float>>,
            _Not<is_same<_Tp, double>>> {};
 
-template <class _Tp>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
-  if constexpr (__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
-    if (!__builtin_is_constant_evaluated()) {
-      __builtin_clear_padding(std::addressof(__obj));
-    }
-  }
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+  return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
+}
+
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
   if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
@@ -67,7 +66,6 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
 
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
-  // return __obj as c++11 does not constexpr function to return void
   return __obj;
 }
 
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 388ab0867fe38..45fb083cccf3f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index f1a534a2916d6..5ffba6ca844c5 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();

>From 9b771605f016811c06b3f600e48f45f3268a5269 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:33:16 +0100
Subject: [PATCH 20/34] release notes

---
 libcxx/docs/ReleaseNotes/24.rst    | 1 +
 libcxx/docs/Status/Cxx20Papers.csv | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..a689379321496 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange (`Github <https://llvm.org/PR76180>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 5539767ab800b..8e394b4bc4b3a 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -31,7 +31,7 @@
 "`P0458R2 <https://wg21.link/P0458R2>`__","Checking for Existence of an Element in Associative Containers","2018-06 (Rapperswil)","|Complete|","13","`#104029 <https://github.com/llvm/llvm-project/issues/104029>`__",""
 "`P0475R1 <https://wg21.link/P0475R1>`__","LWG 2511: guaranteed copy elision for piecewise construction","2018-06 (Rapperswil)","|Complete|","","`#104031 <https://github.com/llvm/llvm-project/issues/104031>`__",""
 "`P0476R2 <https://wg21.link/P0476R2>`__","Bit-casting object representations","2018-06 (Rapperswil)","|Complete|","14","`#104087 <https://github.com/llvm/llvm-project/issues/104087>`__",""
-"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","","","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
+"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","|Complete|","24","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
 "`P0542R5 <https://wg21.link/P0542R5>`__","Support for contract based programming in C++","2018-06 (Rapperswil)","|Nothing To Do|","n/a","`#104089 <https://github.com/llvm/llvm-project/issues/104089>`__","Pulled at the 2019-07 meeting in Cologne"
 "`P0556R3 <https://wg21.link/P0556R3>`__","Integral power-of-2 operations","2018-06 (Rapperswil)","|Complete|","9","`#104090 <https://github.com/llvm/llvm-project/issues/104090>`__",""
 "`P0619R4 <https://wg21.link/P0619R4>`__","Reviewing Deprecated Facilities of C++17 for C++20","2018-06 (Rapperswil)","|Complete|","20","`#99985 <https://github.com/llvm/llvm-project/issues/99985>`__","Removed headers are still provided as an extension, but with deprecation warnings."

>From 46a4ba35dbc30153f7868e9796d44f88512ae602 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 17:01:57 +0100
Subject: [PATCH 21/34] ci

---
 libcxx/include/__atomic/clear_padding.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index f920ef39977b7..3099faa8ca6d1 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__memory/addressof.h>
 #include <__type_traits/conjunction.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/has_unique_object_representation.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_same.h>

>From 5f475333df1aeee9640364c9fb2f734c731edb7e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 19:03:39 +0100
Subject: [PATCH 22/34] ci

---
 .../atomics.types.generic/padding.pass.cpp    | 19 ++++---
 .../atomics.types.generic/padding.pass.cpp    | 52 ++++++++++++-------
 2 files changed, 44 insertions(+), 27 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 45fb083cccf3f..116a860b3192c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -57,11 +57,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -86,7 +84,8 @@ void test() {
 
   {
     // atomic(T);
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     std::atomic<T> a(init);
     T loaded = a.load();
@@ -97,7 +96,8 @@ void test() {
   {
     // atomic::store
     std::atomic<T> a;
-    T value = make<T>(5, 'x', 0xAB);
+    T value;
+    initialize(value, 5, 'x', 0xAB);
     assert_padding(value, 0xAB);
     a.store(value);
     T loaded = a.load();
@@ -107,10 +107,12 @@ void test() {
   }
   {
     // atomic::exchange
-    T initial = make<T>(1, 'a', 0x00);
+    T initial;
+    initialize(initial, 1, 'a', 0x00);
     assert_padding(initial, 0x00);
     std::atomic<T> a(initial);
-    T new_val = make<T>(2, 'b', 0xCD);
+    T new_val;
+    initialize(new_val, 2, 'b', 0xCD);
     assert_padding(new_val, 0xCD);
     T old = a.exchange(new_val);
     assert(old.i == 1);
@@ -124,7 +126,8 @@ void test() {
   {
     // atomic_init
     std::atomic<T> a;
-    T init = make<T>(7, 'z', 0xEF);
+    T init;
+    initialize(init, 7, 'z', 0xEF);
     assert_padding(init, 0xEF);
     std::atomic_init(&a, init);
     T loaded = a.load();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 5ffba6ca844c5..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -51,11 +51,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
 }
 
 template <class T>
-T make(int i, char c, unsigned char pad_byte) {
-  T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
   std::memset(&obj, pad_byte, sizeof(T));
   set(obj, i, c);
-  return obj;
 }
 
 template <class T>
@@ -84,17 +82,20 @@ void test() {
     // CAS should succeed when only padding differs in expected; expected is unchanged.
     std::atomic<T> a;
 
-    T init = make<T>(10, 'a', 0xBB);
+    T init;
+    initialize(init, 10, 'a', 0xBB);
     assert_padding(init, 0xBB);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0xAA);
+    T expected;
+    initialize(expected, 10, 'a', 0xAA);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -112,13 +113,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -138,17 +142,20 @@ void test() {
     // compare_exchange_strong
     // atomic and expected are the same, including padding
     std::atomic<T> a;
-    T init = make<T>(10, 'a', 0x00);
+    T init;
+    initialize(init, 10, 'a', 0x00);
     assert_padding(init, 0x00);
     a.store(init);
 
-    T expected = make<T>(10, 'a', 0x00);
+    T expected;
+    initialize(expected, 10, 'a', 0x00);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
     std::memcpy(&original_expected, &expected, sizeof(T));
 
-    T new_value = make<T>(42, 'b', 0x42);
+    T new_value;
+    initialize(new_value, 42, 'b', 0x42);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,14 +173,17 @@ void test() {
     // compare_exchange_weak
     // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
-    T original_expected = make<T>(10, 'a', 0xAA);
+    T original_expected;
+    initialize(original_expected, 10, 'a', 0xAA);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -182,7 +192,8 @@ void test() {
     while (!r) {
       ++current_attempt;
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
-      T expected = make<T>(10, 'a', 0xAA);
+      T expected;
+      initialize(expected, 10, 'a', 0xAA);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -207,13 +218,16 @@ void test() {
     // compare_exchange_strong
     // atomic and expected values are different; failure
     std::atomic<T> a;
-    T stored = make<T>(10, 'a', 0xBB);
+    T stored;
+    initialize(stored, 10, 'a', 0xBB);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
-    T expected = make<T>(99, 'a', 0xAA);
+    T expected;
+    initialize(expected, 99, 'a', 0xAA);
     assert_padding(expected, 0xAA);
-    T new_value = make<T>(42, 'b', 0xCC);
+    T new_value;
+    initialize(new_value, 42, 'b', 0xCC);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);

>From 704a54eb4f1f7989931b7e25ad87abbb60ad6021 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:07:26 +0100
Subject: [PATCH 23/34] debug

---
 .../atomics.types.generic/padding.pass.cpp    | 64 ++++++++++++++++++-
 1 file changed, 62 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..3b7f13b366e96 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,6 +15,7 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
+#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -70,11 +71,19 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
 
+template <class T>
+void print_memory(const T& obj) {
+  for (size_t i = 0; i < sizeof(T); ++i) {
+    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
+  }
+  std::cerr << std::endl;
+}
+
 template <class T>
 void test() {
   {
@@ -84,11 +93,15 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
+    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
+    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -96,6 +109,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -106,6 +121,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test1 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -115,14 +132,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -131,11 +154,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test2 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test2 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -144,11 +171,15 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
+    std::cerr << "test3 init padding should be 0x00: " << std::endl;
+    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
+    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -156,6 +187,8 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
+    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,6 +199,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test3 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -175,15 +210,21 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
+    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
+    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -194,6 +235,8 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
+      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
+      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -203,6 +246,8 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+        std::cerr << "test4 expected padding should be 0: " << std::endl;
+        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -211,6 +256,8 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test4 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -220,14 +267,20 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
+    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
+    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
+    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
+    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
+    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
+    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -236,11 +289,15 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+    std::cerr << "test5 expected padding should be 0: " << std::endl;
+    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+    std::cerr << "test5 loaded padding should be 0: " << std::endl;
+    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -262,10 +319,13 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
+  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
+  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
+  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 0;
+  return 1;
 }

>From 8c622e41d2428f630855f0aaa215b1463d8fe4ca Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:38:19 +0100
Subject: [PATCH 24/34] Revert "debug"

This reverts commit 8be9a83ee040faff62f0756555890484bf502a31.
---
 .../atomics.types.generic/padding.pass.cpp    | 64 +------------------
 1 file changed, 2 insertions(+), 62 deletions(-)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 3b7f13b366e96..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,7 +15,6 @@
 #include <cassert>
 #include <cstring>
 #include <type_traits>
-#include <iostream>
 
 struct WithTailPadding {
   int i;
@@ -71,19 +70,11 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)obj;
+  (void)f;
   (void)pad_byte;
 #endif
 }
 
-template <class T>
-void print_memory(const T& obj) {
-  for (size_t i = 0; i < sizeof(T); ++i) {
-    std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
-  }
-  std::cerr << std::endl;
-}
-
 template <class T>
 void test() {
   {
@@ -93,15 +84,11 @@ void test() {
 
     T init;
     initialize(init, 10, 'a', 0xBB);
-    std::cerr << "test1 init padding should be 0xBB: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0xBB);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0xAA);
-    std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
 
     T original_expected; // make a copy including padding bits
@@ -109,8 +96,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -121,8 +106,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test1 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -132,20 +115,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -154,15 +131,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test2 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test2 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
   {
@@ -171,15 +144,11 @@ void test() {
     std::atomic<T> a;
     T init;
     initialize(init, 10, 'a', 0x00);
-    std::cerr << "test3 init padding should be 0x00: " << std::endl;
-    print_memory(init);
     assert_padding(init, 0x00);
     a.store(init);
 
     T expected;
     initialize(expected, 10, 'a', 0x00);
-    std::cerr << "test3 expected padding should be 0x00: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0x00);
 
     T original_expected; // make a copy including padding bits
@@ -187,8 +156,6 @@ void test() {
 
     T new_value;
     initialize(new_value, 42, 'b', 0x42);
-    std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0x42);
 
     bool r = a.compare_exchange_strong(expected, new_value);
@@ -199,8 +166,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test3 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -210,21 +175,15 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     T original_expected;
     initialize(original_expected, 10, 'a', 0xAA);
-    std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
-    print_memory(original_expected);
     assert_padding(original_expected, 0xAA);
 
     bool r                  = false;
@@ -235,8 +194,6 @@ void test() {
       assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
       T expected;
       initialize(expected, 10, 'a', 0xAA);
-      std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
-      print_memory(expected);
       assert_padding(expected, 0xAA);
       r = a.compare_exchange_weak(expected, new_value);
       if (r) {
@@ -246,8 +203,6 @@ void test() {
         assert(expected.i == 10);
         assert(expected.c == 'a');
         // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        std::cerr << "test4 expected padding should be 0: " << std::endl;
-        print_memory(expected);
         libcpp_assert_padding(expected, 0);
       }
     }
@@ -256,8 +211,6 @@ void test() {
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test4 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -267,20 +220,14 @@ void test() {
     std::atomic<T> a;
     T stored;
     initialize(stored, 10, 'a', 0xBB);
-    std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
-    print_memory(stored);
     assert_padding(stored, 0xBB);
     a.store(stored);
 
     T expected;
     initialize(expected, 99, 'a', 0xAA);
-    std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
-    print_memory(expected);
     assert_padding(expected, 0xAA);
     T new_value;
     initialize(new_value, 42, 'b', 0xCC);
-    std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
-    print_memory(new_value);
     assert_padding(new_value, 0xCC);
 
     bool r = a.compare_exchange_weak(expected, new_value);
@@ -289,15 +236,11 @@ void test() {
     assert(expected.i == 10);
     assert(expected.c == 'a');
     // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    std::cerr << "test5 expected padding should be 0: " << std::endl;
-    print_memory(expected);
     libcpp_assert_padding(expected, 0);
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
     // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    std::cerr << "test5 loaded padding should be 0: " << std::endl;
-    print_memory(loaded);
     libcpp_assert_padding(loaded, 0);
   }
 
@@ -319,13 +262,10 @@ void test() {
 int main(int, char**) {
 // TODO(LLVM-23): Switch to XFAIL with clang-22
 #if __has_builtin(__builtin_clear_padding)
-  std::cerr << "\nWithTailPadding\n" << std::endl;
   test<WithTailPadding>();
-  std::cerr << "\nWithInternalPadding\n" << std::endl;
   test<WithInternalPadding>();
-  std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
   test<WithInternalAndTailPadding>();
 #endif
 
-  return 1;
+  return 0;
 }

>From 789912ba84ccbb3a3efea236fde31835783a7415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:44:00 +0100
Subject: [PATCH 25/34] test ci

---
 .../atomics.types.generic/padding.pass.cpp    | 20 ++++++++++++++-----
 .../atomics.types.generic/padding.pass.cpp    | 12 +----------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 116a860b3192c..cd41f2893864c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -71,6 +71,16 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
+template <class T>
+void assert_padding(const std::atomic<T>& obj, unsigned char pad_byte) {
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memset(buf, pad_byte, sizeof(T));
+  T& reference = *reinterpret_cast<T*>(buf);
+  T loaded     = obj.load();
+  set(reference, loaded.i, loaded.c);
+  assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
+}
+
 template <class T>
 void test() {
   {
@@ -79,7 +89,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 
   {
@@ -91,7 +101,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::store
@@ -103,7 +113,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 5);
     assert(loaded.c == 'x');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic::exchange
@@ -121,7 +131,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
   {
     // atomic_init
@@ -133,7 +143,7 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 7);
     assert(loaded.c == 'z');
-    assert_padding(loaded, 0);
+    assert_padding(a, 0);
   }
 }
 
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index fe930f8b05b7d..10c15523cd70b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -70,7 +70,7 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
 #ifdef _LIBCPP_VERSION
   assert_padding(obj, pad_byte);
 #else
-  (void)f;
+  (void)obj;
   (void)pad_byte;
 #endif
 }
@@ -105,8 +105,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -135,8 +133,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
   {
     // compare_exchange_strong
@@ -165,8 +161,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -210,8 +204,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 42);
     assert(loaded.c == 'b');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {
@@ -240,8 +232,6 @@ void test() {
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
-    // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
-    libcpp_assert_padding(loaded, 0);
   }
 
   {

>From 81770cda9f2171565c1c2980ad8306b9665752b2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:18:49 +0100
Subject: [PATCH 26/34] padding

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index cd41f2893864c..a178ce74578ec 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -127,7 +127,6 @@ void test() {
     T old = a.exchange(new_val);
     assert(old.i == 1);
     assert(old.c == 'a');
-    assert_padding(old, 0);
     T loaded = a.load();
     assert(loaded.i == 2);
     assert(loaded.c == 'b');

>From b90bec87725f4bde5722a3aa857a5c9d22c275e5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:40:31 +0100
Subject: [PATCH 27/34] clang22

---
 .../libcxx/atomics/atomics.types.generic/padding.pass.cpp    | 5 ++---
 .../test/std/atomics/atomics.types.generic/padding.pass.cpp  | 4 +---
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index a178ce74578ec..48ae68a8e97ba 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic_init is deprecated
 // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -147,11 +148,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
+
   return 0;
 }
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 10c15523cd70b..dc0afa439dc12 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic<T>::compare_exchange_weak
 // atomic<T>::compare_exchange_strong
@@ -250,12 +251,9 @@ void test() {
 }
 
 int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
   test<WithTailPadding>();
   test<WithInternalPadding>();
   test<WithInternalAndTailPadding>();
-#endif
 
   return 0;
 }

>From 05729850c8bf62df54a60b12201ac475e5c55fb5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 13:26:21 +0100
Subject: [PATCH 28/34] cxx 03

---
 libcxx/include/__atomic/clear_padding.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 3099faa8ca6d1..dad48ed27e6a4 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -32,23 +32,23 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Tp>
 struct __needs_clear_padding
-    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
-           _Not<is_same<_Tp, float>>,
-           _Not<is_same<_Tp, double>>> {};
+    : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)> >,
+           _Not<is_same<_Tp, float> >,
+           _Not<is_same<_Tp, double> > > {};
 
-template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   return __obj;
 }
 
-template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 
 template <class _Tp, class _Up, class _CasFunc>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
+  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp> >::value) {
     return __cas_func(__expected, __value);
   } else {
     std::__clear_padding_if_needed(__value);

>From 2d00c0b818d180cc4d991508be2ad3dd8b7ee878 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:26:21 +0100
Subject: [PATCH 29/34] ci

---
 libcxx/include/__atomic/clear_padding.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index dad48ed27e6a4..1a4223fd97763 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -37,12 +37,12 @@ struct __needs_clear_padding
            _Not<is_same<_Tp, double> > > {};
 
 template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
   return __obj;
 }
 
 template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 

>From 30d5c984f33655f34946afb790bdc85c1f29d7a4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 15:49:25 +0100
Subject: [PATCH 30/34] ci

---
 libcxx/include/__atomic/clear_padding.h | 31 +++++++++++++++----------
 1 file changed, 19 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 1a4223fd97763..be55837cd8ec8 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -46,20 +46,27 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __ob
   return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
 }
 
-template <class _Tp, class _Up, class _CasFunc>
+template <class _Tp,
+          class _Up,
+          class _CasFunc,
+          __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+  return __cas_func(__expected, __value);
+}
+
+template <class _Tp,
+          class _Up,
+          class _CasFunc,
+          __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
-  if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp> >::value) {
-    return __cas_func(__expected, __value);
+  std::__clear_padding_if_needed(__value);
+  __remove_cvref_t<_Tp> __expected_copy = *__expected;
+  std::__clear_padding_if_needed(__expected_copy);
+  if (__cas_func(std::addressof(__expected_copy), __value)) {
+    return true;
   } else {
-    std::__clear_padding_if_needed(__value);
-    __remove_cvref_t<_Tp> __expected_copy = *__expected;
-    std::__clear_padding_if_needed(__expected_copy);
-    if (__cas_func(std::addressof(__expected_copy), __value)) {
-      return true;
-    } else {
-      std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
-      return false;
-    }
+    std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
+    return false;
   }
 }
 

>From 2c566e60184cacd4536478332d52b88f196d12b6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 17:01:17 +0100
Subject: [PATCH 31/34] ci

---
 .../libcxx/atomics/atomics.types.generic/padding.pass.cpp   | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 48ae68a8e97ba..65b890b75d1de 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -23,6 +23,8 @@
 #include <cstring>
 #include <type_traits>
 
+#include "test_macros.h"
+
 struct WithTailPadding {
   int i;
   char c;
@@ -87,10 +89,12 @@ void test() {
   {
     // atomic();
     std::atomic<T> a;
+    assert_padding(a, 0);
+#if TEST_STD_VER >= 20
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');
-    assert_padding(a, 0);
+#endif
   }
 
   {

>From 83ddf2179999b52bb6007bd055d86da1383d4bcb Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 18:38:43 +0100
Subject: [PATCH 32/34] ci

---
 .../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 65b890b75d1de..c9ed3bbcc5704 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -88,9 +88,9 @@ template <class T>
 void test() {
   {
     // atomic();
+#if TEST_STD_VER >= 20
     std::atomic<T> a;
     assert_padding(a, 0);
-#if TEST_STD_VER >= 20
     T loaded = a.load();
     assert(loaded.i == 0);
     assert(loaded.c == '\0');

>From e6e386683433dc9931d7a91d49346c0d45a20a3d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 24 Jul 2026 07:49:38 +0100
Subject: [PATCH 33/34] ci

---
 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index dc0afa439dc12..f9008f29f14b4 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+// XFAIL: !has-64-bit-atomics
 // XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic<T>::compare_exchange_weak

>From 0f3072fc5319fe473399698ad6be8e8f01ebfdf8 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 25 Jul 2026 09:56:49 +0100
Subject: [PATCH 34/34] review

---
 libcxx/include/__atomic/support/c11.h         | 25 ++++---
 libcxx/include/__atomic/support/gcc.h         | 68 ++++++++++---------
 .../test/configs/llvm-libc++-llvm-libc.cfg.in |  2 +-
 .../atomics.types.generic/padding.pass.cpp    |  4 ++
 .../atomics.types.generic/padding.pass.cpp    | 30 ++++----
 5 files changed, 72 insertions(+), 57 deletions(-)

diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index d43a2c67f950a..9176a74462a5a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -137,13 +137,16 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
   return std::__atomic_cas_with_clear_padding(
-      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+      __expected,
+      __value,
+      // clang fails to inline the lambda when the memory order is a constant
+      [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) _LIBCPP_ALWAYS_INLINE {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
             __expected_or_copy,
             __value_maybe_padding_cleared,
             static_cast<__memory_order_underlying_t>(__success),
-            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+            static_cast<__memory_order_underlying_t>(std::__to_failure_order(__failure)));
       });
 }
 template <class _Tp>
@@ -151,13 +154,15 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
   return std::__atomic_cas_with_clear_padding(
-      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+      __expected,
+      __value,
+      [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) _LIBCPP_ALWAYS_INLINE {
         return __c11_atomic_compare_exchange_strong(
             std::addressof(__a->__a_value),
             __expected_or_copy,
             __value_maybe_padding_cleared,
             static_cast<__memory_order_underlying_t>(__success),
-            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+            static_cast<__memory_order_underlying_t>(std::__to_failure_order(__failure)));
       });
 }
 
@@ -169,13 +174,15 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     memory_order __success,
     memory_order __failure) _NOEXCEPT {
   return std::__atomic_cas_with_clear_padding(
-      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+      __expected,
+      __value,
+      [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) _LIBCPP_ALWAYS_INLINE {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
             __expected_or_copy,
             __value_maybe_padding_cleared,
             static_cast<__memory_order_underlying_t>(__success),
-            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+            static_cast<__memory_order_underlying_t>(std::__to_failure_order(__failure)));
       });
 }
 
@@ -184,13 +191,15 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
     _NOEXCEPT {
   return std::__atomic_cas_with_clear_padding(
-      __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+      __expected,
+      __value,
+      [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) _LIBCPP_ALWAYS_INLINE {
         return __c11_atomic_compare_exchange_weak(
             std::addressof(__a->__a_value),
             __expected_or_copy,
             __value_maybe_padding_cleared,
             static_cast<__memory_order_underlying_t>(__success),
-            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+            static_cast<__memory_order_underlying_t>(std::__to_failure_order(__failure)));
       });
 }
 
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index d2d4919d54614..bbd193efad8be 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -73,49 +73,49 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _
 }
 
 _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_thread_fence(memory_order __order) {
-  __atomic_thread_fence(__to_gcc_order(__order));
+  __atomic_thread_fence(std::__to_gcc_order(__order));
 }
 
 _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order) {
-  __atomic_signal_fence(__to_gcc_order(__order));
+  __atomic_signal_fence(std::__to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
   std::__clear_padding_if_needed(__val);
-  __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
+  __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), std::__to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
   std::__clear_padding_if_needed(__val);
-  __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
+  __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), std::__to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_load(const volatile __cxx_atomic_base_impl<_Tp>* __a, memory_order __order) {
   _Tp __ret;
-  __atomic_load(std::addressof(__a->__a_value), std::addressof(__ret), __to_gcc_order(__order));
+  __atomic_load(std::addressof(__a->__a_value), std::addressof(__ret), std::__to_gcc_order(__order));
   return __ret;
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_load_inplace(const volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp* __dst, memory_order __order) {
-  __atomic_load(std::addressof(__a->__a_value), __dst, __to_gcc_order(__order));
+  __atomic_load(std::addressof(__a->__a_value), __dst, std::__to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void
 __cxx_atomic_load_inplace(const __cxx_atomic_base_impl<_Tp>* __a, _Tp* __dst, memory_order __order) {
-  __atomic_load(std::addressof(__a->__a_value), __dst, __to_gcc_order(__order));
+  __atomic_load(std::addressof(__a->__a_value), __dst, std::__to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_load(const __cxx_atomic_base_impl<_Tp>* __a, memory_order __order) {
   _Tp __ret;
-  __atomic_load(std::addressof(__a->__a_value), std::addressof(__ret), __to_gcc_order(__order));
+  __atomic_load(std::addressof(__a->__a_value), std::addressof(__ret), std::__to_gcc_order(__order));
   return __ret;
 }
 
@@ -125,7 +125,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
   _Tp __ret;
   std::__clear_padding_if_needed(__value);
   __atomic_exchange(
-      std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
+      std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), std::__to_gcc_order(__order));
   return __ret;
 }
 
@@ -134,7 +134,7 @@ _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a
   _Tp __ret;
   std::__clear_padding_if_needed(__value);
   __atomic_exchange(
-      std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
+      std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), std::__to_gcc_order(__order));
   return __ret;
 }
 
@@ -152,8 +152,8 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
             __expected_or_copy,
             std::addressof(__value_maybe_padding_cleared),
             false,
-            __to_gcc_order(__success),
-            __to_gcc_failure_order(__failure));
+            std::__to_gcc_order(__success),
+            std::__to_gcc_failure_order(__failure));
       });
 }
 
@@ -167,8 +167,8 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
             __expected_or_copy,
             std::addressof(__value_maybe_padding_cleared),
             false,
-            __to_gcc_order(__success),
-            __to_gcc_failure_order(__failure));
+            std::__to_gcc_order(__success),
+            std::__to_gcc_failure_order(__failure));
       });
 }
 
@@ -186,8 +186,8 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
             __expected_or_copy,
             std::addressof(__value_maybe_padding_cleared),
             true,
-            __to_gcc_order(__success),
-            __to_gcc_failure_order(__failure));
+            std::__to_gcc_order(__success),
+            std::__to_gcc_failure_order(__failure));
       });
 }
 
@@ -201,8 +201,8 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
             __expected_or_copy,
             std::addressof(__value_maybe_padding_cleared),
             true,
-            __to_gcc_order(__success),
-            __to_gcc_failure_order(__failure));
+            std::__to_gcc_order(__success),
+            std::__to_gcc_failure_order(__failure));
       });
 }
 
@@ -226,65 +226,69 @@ struct __skip_amt<_Tp[n]> {};
 template <typename _Tp, typename _Td>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_add(volatile __cxx_atomic_base_impl<_Tp>* __a, _Td __delta, memory_order __order) {
-  return __atomic_fetch_add(std::addressof(__a->__a_value), __delta * __skip_amt<_Tp>::value, __to_gcc_order(__order));
+  return __atomic_fetch_add(
+      std::addressof(__a->__a_value), __delta * __skip_amt<_Tp>::value, std::__to_gcc_order(__order));
 }
 
 template <typename _Tp, typename _Td>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_add(__cxx_atomic_base_impl<_Tp>* __a, _Td __delta, memory_order __order) {
-  return __atomic_fetch_add(std::addressof(__a->__a_value), __delta * __skip_amt<_Tp>::value, __to_gcc_order(__order));
+  return __atomic_fetch_add(
+      std::addressof(__a->__a_value), __delta * __skip_amt<_Tp>::value, std::__to_gcc_order(__order));
 }
 
 template <typename _Tp, typename _Td>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_sub(volatile __cxx_atomic_base_impl<_Tp>* __a, _Td __delta, memory_order __order) {
-  return __atomic_fetch_sub(std::addressof(__a->__a_value), __delta * __skip_amt<_Tp>::value, __to_gcc_order(__order));
+  return __atomic_fetch_sub(
+      std::addressof(__a->__a_value), __delta * __skip_amt<_Tp>::value, std::__to_gcc_order(__order));
 }
 
 template <typename _Tp, typename _Td>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_sub(__cxx_atomic_base_impl<_Tp>* __a, _Td __delta, memory_order __order) {
-  return __atomic_fetch_sub(std::addressof(__a->__a_value), __delta * __skip_amt<_Tp>::value, __to_gcc_order(__order));
+  return __atomic_fetch_sub(
+      std::addressof(__a->__a_value), __delta * __skip_amt<_Tp>::value, std::__to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_and(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_and(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_and(std::addressof(__a->__a_value), __pattern, std::__to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_and(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_and(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_and(std::addressof(__a->__a_value), __pattern, std::__to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_or(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_or(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_or(std::addressof(__a->__a_value), __pattern, std::__to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_or(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_or(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_or(std::addressof(__a->__a_value), __pattern, std::__to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_xor(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_xor(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_xor(std::addressof(__a->__a_value), __pattern, std::__to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_order __order) {
-  return __atomic_fetch_xor(std::addressof(__a->__a_value), __pattern, __to_gcc_order(__order));
+  return __atomic_fetch_xor(std::addressof(__a->__a_value), __pattern, std::__to_gcc_order(__order));
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_max)
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, std::__to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
@@ -298,7 +302,7 @@ __cxx_atomic_fetch_max(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, mem
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_max)
-  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
+  return __atomic_fetch_max(std::addressof(__a->__a_value), __val, std::__to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
@@ -313,7 +317,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp
 __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_min)
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, std::__to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
@@ -327,7 +331,7 @@ __cxx_atomic_fetch_min(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, mem
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
 #if __has_builtin(__atomic_fetch_min)
-  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, __to_gcc_order(__order));
+  return __atomic_fetch_min(std::addressof(__a->__a_value), __val, std::__to_gcc_order(__order));
 #else
   _Tp __ret = __cxx_atomic_load(__a, memory_order_relaxed);
   _Tp __value;
diff --git a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
index 885715eb42dfc..9c462aa8dd49b 100644
--- a/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
+++ b/libcxx/test/configs/llvm-libc++-llvm-libc.cfg.in
@@ -8,7 +8,7 @@ config.substitutions.append(('%{compile_flags}',
     '-nostdlibinc -I %{include-dir} -I %{target-include-dir} -isystem @CMAKE_BINARY_DIR@/libc/include -I %{libcxx-dir}/test/support -idirafter @LIBC_KERNEL_HEADERS@'
 ))
 config.substitutions.append(('%{link_flags}',
-    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--end-group -Wl,--defsym,__dso_handle=0'
+    '-nodefaultlibs -nostartfiles -L %{lib-dir} -lc++ -lc++abi @CMAKE_BINARY_DIR@/libc/startup/linux/crt1.o -Wl,--start-group @CMAKE_BINARY_DIR@/libc/lib/libc.a @CMAKE_BINARY_DIR@/libc/lib/libm.a -static -fno-use-cxa-atexit @CMAKE_BINARY_DIR@/compiler-rt/lib/linux/libclang_rt.builtins-x86_64.a -Wl,--end-group -Wl,--defsym,__dso_handle=0 -pthread'
 ))
 config.substitutions.append(('%{exec}',
     '%{executor} --execdir %{temp} -- '
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c9ed3bbcc5704..1029ede2f7181 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
+
+// Older Clang doesn't implement __builtin_clear_padding
 // XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic_init is deprecated
@@ -88,6 +90,8 @@ template <class T>
 void test() {
   {
     // atomic();
+    // Prio to C++20, the default constructor leaves std::atomic unitialized,
+    // so padding bytes are not guaranteed zero
 #if TEST_STD_VER >= 20
     std::atomic<T> a;
     assert_padding(a, 0);
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index f9008f29f14b4..5721acf4a66fe 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,6 +7,8 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 // XFAIL: !has-64-bit-atomics
+
+// Older Clang doesn't implement __builtin_clear_padding
 // XFAIL: clang-21, apple-clang-21, clang-22
 
 // atomic<T>::compare_exchange_weak
@@ -67,16 +69,6 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
   assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
 }
 
-template <class T>
-void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
-#ifdef _LIBCPP_VERSION
-  assert_padding(obj, pad_byte);
-#else
-  (void)obj;
-  (void)pad_byte;
-#endif
-}
-
 template <class T>
 void test() {
   {
@@ -130,8 +122,10 @@ void test() {
     assert(!r);
     assert(expected.i == 10);
     assert(expected.c == 'a');
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_padding(expected, 0);
+// expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+#ifdef _LIBCPP_VERSION
+    assert_padding(expected, 0);
+#endif
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');
@@ -198,8 +192,10 @@ void test() {
         // Spurious failure: expected is updated to the current atomic value.
         assert(expected.i == 10);
         assert(expected.c == 'a');
-        // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-        libcpp_assert_padding(expected, 0);
+// expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+#ifdef _LIBCPP_VERSION
+        assert_padding(expected, 0);
+#endif
       }
     }
 
@@ -229,8 +225,10 @@ void test() {
     assert(!r);
     assert(expected.i == 10);
     assert(expected.c == 'a');
-    // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
-    libcpp_assert_padding(expected, 0);
+// expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+#ifdef _LIBCPP_VERSION
+    assert_padding(expected, 0);
+#endif
     T loaded = a.load();
     assert(loaded.i == 10);
     assert(loaded.c == 'a');


From libcxx-commits at lists.llvm.org  Sat Jul 25 01:58:19 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 01:58:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement `std::function_ref`
	(PR #186692)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/186692@github.com>
Message-ID: <6a647aab.7270d2fd.2df01a.ad81@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/186692

>From ca0f8811f81d47e4866fd8ad9d937cb2a0c539be Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 12 Apr 2026 09:35:44 +0100
Subject: [PATCH 01/21] [libcxx] Implement `std::constant_wrapper`

ci

ci

review comments

review comments

ci

review comments
---
 .../containers/views/views.span/span.cons/deduct.pass.cpp  | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index 35d9b36f824aa..f5b590cd9631d 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -88,6 +88,13 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
+
+  {
+    std::span s{std::begin(arr), std::cw<3>};
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+    assert(s.size() == std::size(arr));
+    assert(s.data() == std::data(arr));
+  }
 #endif
 }
 

>From b3ee4d47aa3071d6e52b6921375c69945abed3b8 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 8 Jun 2024 17:32:42 -0700
Subject: [PATCH 02/21] [libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

rebase + put in experimental + rename nontype to constant_arg

tests

remove experimental

test for constant_arg_ref

test for constant_arg_ptr

remove fun_ptr member

invoke

format

constexpr function ptr

double unwrapping

P3948R1

tests for new papers

format

lwg issue

lwg issues

review comment

remove constexpr

optimise small types

ci

ci

ci

ci
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/23.rst               |   3 +
 libcxx/docs/Status/Cxx26Issues.csv            |   4 +-
 libcxx/docs/Status/Cxx26Papers.csv            |   6 +-
 libcxx/include/CMakeLists.txt                 |   3 +
 libcxx/include/__functional/function_ref.h    |  33 ++
 .../__functional/function_ref_common.h        | 126 ++++++
 .../include/__functional/function_ref_impl.h  | 184 +++++++++
 libcxx/include/functional                     |   9 +
 libcxx/include/module.modulemap.in            |   3 +
 libcxx/include/version                        |   4 +-
 libcxx/modules/std/functional.inc             |   4 +
 .../assert.constant_arg_ptr.pass.cpp          |  31 ++
 .../assert.function_ptr.pass.cpp              |  26 ++
 .../constant_arg.mandates.verify.cpp          |  60 +++
 .../internal_call_signature.pass.cpp          | 136 +++++++
 .../functional.version.compile.pass.cpp       |  16 +-
 .../version.version.compile.pass.cpp          |  16 +-
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     | 107 +++++
 .../func.wrap.ref.ctor/assign.delete.pass.cpp | 115 ++++++
 .../constant_wrapper.pass.cpp                 | 175 ++++++++
 .../constant_wrapper_ptr.pass.cpp             | 359 +++++++++++++++++
 .../constant_wrapper_ref.pass.cpp             | 373 ++++++++++++++++++
 .../func.wrap.ref.ctor/copy.pass.cpp          | 131 ++++++
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   | 158 ++++++++
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  | 149 +++++++
 .../func.wrap.ref.ctor/move.pass.cpp          | 129 ++++++
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 156 ++++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 316 +++++++++++++++
 .../func.wrap.ref.inv/invoke.pass.cpp         | 362 +++++++++++++++++
 .../trivially_copyable.compile.pass.cpp       |  33 ++
 .../generate_feature_test_macro_components.py |   3 +-
 32 files changed, 3200 insertions(+), 32 deletions(-)
 create mode 100644 libcxx/include/__functional/function_ref.h
 create mode 100644 libcxx/include/__functional/function_ref_common.h
 create mode 100644 libcxx/include/__functional/function_ref_impl.h
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 00665332a9ed7..108702e71dec7 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -480,7 +480,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_fstream_native_handle``                        ``202306L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_function_ref``                                 *unimplemented*
+    ``__cpp_lib_function_ref``                                 ``202603L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_generate_random``                              *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index f7a8e5b04f1ea..5d295453dd79f 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,9 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 5b046b69d14bd..55c642fd15ce1 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","","","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","","","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 7fcebd331197b..530575f4973da 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."
@@ -192,9 +192,9 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","","","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","","","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b40f586161e62..a0d5cf70d267d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -438,6 +438,9 @@ set(files
   __functional/compose.h
   __functional/default_searcher.h
   __functional/function.h
+  __functional/function_ref.h
+  __functional/function_ref_common.h
+  __functional/function_ref_impl.h
   __functional/hash.h
   __functional/identity.h
   __functional/invoke.h
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
new file mode 100644
index 0000000000000..b7fd3a4a2c79f
--- /dev/null
+++ b/libcxx/include/__functional/function_ref.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+
+#include <__config>
+#include <__functional/function_ref_common.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26
+// NOLINTBEGIN(readability-duplicate-include)
+
+#  define _LIBCPP_FUNCTION_REF_CV
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+#  define _LIBCPP_FUNCTION_REF_CV const
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+// NOLINTEND(readability-duplicate-include)
+#endif // _LIBCPP_STD_VER >= 26
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
new file mode 100644
index 0000000000000..61979231b56e8
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_FUNCTION_REF_COMMON_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+
+#include <__config>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/constant_wrapper.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization : false_type {};
+
+// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+// pointers
+// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
+// supported
+union __storage_func_ref_t {
+  void* __obj_ptr_;
+  void const* __obj_const_ptr_;
+  void (*__fn_ptr_)();
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        __obj_const_ptr_ = __ptr;
+      } else {
+        __obj_ptr_ = __ptr;
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
+    }
+  }
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
+      } else {
+        return static_cast<_Tp*>(__storage.__obj_ptr_);
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
+    }
+  }
+};
+
+template <class _Fp, class _Tp>
+struct __function_ref_bind {};
+
+// F is of the form R(*)(G, A...) noexcept(E) for a type G.
+template <bool _Noexcept, class _Tp, class _Rp, class _Gp, class... _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept(_Noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_Noexcept);
+};
+
+template <class _Tp, class _Mp, class _Gp>
+  requires is_object_v<_Mp>
+struct __function_ref_bind<_Mp _Gp::*, _Tp> {
+  using type _LIBCPP_NODEBUG = invoke_result_t<_Mp _Gp::*, _Tp&>() noexcept;
+};
+
+template <class _Fp, class _Tp>
+using __function_ref_bind_t _LIBCPP_NODEBUG = __function_ref_bind<_Fp, _Tp>::type;
+
+template <class _Fp>
+  requires is_function_v<_Fp>
+function_ref(_Fp*) -> function_ref<_Fp>;
+
+template <auto _Cw, class _Fn>
+  requires is_function_v<remove_pointer_t<_Fn>>
+function_ref(constant_wrapper<_Cw, _Fn>) -> function_ref<remove_pointer_t<_Fn>>;
+
+template <auto _Cw, class _Fn, class _Tp>
+function_ref(constant_wrapper<_Cw, _Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<_Fn, _Tp&>>;
+
+template <class>
+constexpr bool __is_constant_wrapper = false;
+
+template <auto _Value>
+constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
+
+template <class _Arg>
+struct __function_ref_arg_fwd {
+  using type _LIBCPP_NODEBUG = _Arg&&;
+};
+
+template <class _Arg>
+  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
+struct __function_ref_arg_fwd<_Arg> {
+  using type _LIBCPP_NODEBUG = _Arg;
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
new file mode 100644
index 0000000000000..a4c98d3031602
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -0,0 +1,184 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
+// and generates multiple versions of std::function_ref
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#include <__functional/invoke.h>
+#include <__memory/addressof.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/constant_wrapper.h>
+#include <__utility/forward.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#  error This header should only be included from function_ref.h
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
+    _NoExcept1,
+    _Rp,
+    _ArgTypes...>
+    : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
+
+template <class _Rp, class... _ArgTypes, bool __is_noexcept>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
+private:
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using =
+      _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
+          value;
+
+  template <class _Fn>
+  static constexpr bool __is_convertible_from_specialization_v =
+      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+
+  template <class... _Tp>
+  friend class function_ref;
+
+  template <class _Arg>
+  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+
+  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+
+  using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
+
+  __storage_t __storage_;
+  __call_t __call_;
+
+public:
+  template <class _Fp>
+    requires is_function_v<_Fp> && __is_invocable_using<_Fp>
+  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+      : __storage_(__fn_ptr),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+  }
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {}
+
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && __is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(__obj.__storage_), __call_(__obj.__call_) {}
+
+  template <auto _Cw, class _Fn>
+    requires __is_invocable_using<const _Fn&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
+      : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+    if constexpr (sizeof...(_ArgTypes) > 0 && (__constexpr_param<remove_cvref_t<_ArgTypes>> && ...)) {
+      static_assert(
+          !requires {
+            typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
+          },
+          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
+          "and produces a result with a structural type");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
+    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Tp>
+    requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
+      : __storage_(__obj_ptr_),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+
+    if constexpr (is_member_pointer_v<_Fn>) {
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(const function_ref&) noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+  template <class _Tp>
+    requires(!__is_convertible_from_specialization_v<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_wrapper<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
+
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
+    return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
+  }
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 9d588231d548d..df79d616a57e2 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -487,6 +487,11 @@ template <class  R, class ... ArgTypes>
 template <class  R, class ... ArgTypes>
   void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
 
+// [func.wrap.ref], non-owning wrapper
+template<class... S> class function_ref;                              // freestanding, not defined, since C++26
+template<class R, class... ArgTypes>
+  class function_ref<R(ArgTypes...) cv noexcept(noex)>;               // freestanding, since C++26
+
 template <class T> struct hash;
 
 template <> struct hash<bool>;
@@ -571,6 +576,10 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 #    include <__type_traits/unwrap_ref.h>
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+#    include <__functional/function_ref.h>
+#  endif
+
 #  include <version>
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 39b4e0bb986c6..cb29fac536046 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1438,6 +1438,9 @@ module std {
     }
     module default_searcher             { header "__functional/default_searcher.h" }
     module function                     { header "__functional/function.h" }
+    module function_ref                 { header "__functional/function_ref.h" }
+    module function_ref_common          { header "__functional/function_ref_common.h" }
+    module function_ref_impl            { textual header "__functional/function_ref_impl.h" }
     module hash                         { header "__functional/hash.h" }
     module identity                     { header "__functional/identity.h" }
     module invoke                       { header "__functional/invoke.h" }
diff --git a/libcxx/include/version b/libcxx/include/version
index 7545d187ca7f1..9160c51835d6f 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -131,7 +131,7 @@ __cpp_lib_freestanding_optional                         202311L <optional>
 __cpp_lib_freestanding_string_view                      202311L <string_view>
 __cpp_lib_freestanding_variant                          202311L <variant>
 __cpp_lib_fstream_native_handle                         202306L <fstream>
-__cpp_lib_function_ref                                  202306L <functional>
+__cpp_lib_function_ref                                  202603L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
@@ -593,7 +593,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 #   define __cpp_lib_fstream_native_handle              202306L
 # endif
-// # define __cpp_lib_function_ref                         202306L
+# define __cpp_lib_function_ref                         202603L
 // # define __cpp_lib_generate_random                      202403L
 // # define __cpp_lib_hazard_pointer                       202306L
 // # define __cpp_lib_inplace_vector                       202406L
diff --git a/libcxx/modules/std/functional.inc b/libcxx/modules/std/functional.inc
index 9ef8f584611fc..be804b19f75f8 100644
--- a/libcxx/modules/std/functional.inc
+++ b/libcxx/modules/std/functional.inc
@@ -95,6 +95,10 @@ export namespace std {
 
   using std::function;
 
+#if _LIBCPP_STD_VER >= 26
+  using std::function_ref;
+#endif // _LIBCPP_STD_VER >= 26
+
   using std::swap;
 
   using std::operator==;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
new file mode 100644
index 0000000000000..9703de0a59c2c
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+// Preconditions: If is_member_pointer_v<F> is true, obj is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+struct A {
+  void f() {}
+};
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(std::function_ref<void()>(std::cw<&A::f>, static_cast<A*>(nullptr)),
+                             "the object pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
new file mode 100644
index 0000000000000..e69776d0e3c90
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<class F> function_ref(F* f) noexcept;
+// Preconditions: f is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::function_ref<void()>(static_cast<void (*)()>(nullptr)), "the function pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
new file mode 100644
index 0000000000000..519957eec6878
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto c, class F>
+//   constexpr function_ref(constant_wrapper<c, F>) noexcept;
+// template<auto c, class F, class U>
+//   constexpr function_ref(constant_wrapper<c, F>, U&& obj) noexcept;
+// template<auto c, class F, class T>
+//   constexpr function_ref(constant_wrapper<c, F>, cv T* obj) noexcept;
+
+// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
+
+// For the first overload,
+// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+
+#include <functional>
+#include <utility>
+
+struct A {
+  void f();
+};
+
+struct B {
+  constexpr int operator()(std::constant_wrapper<42>) const { return 42; }
+  constexpr int operator()(int) const { return 42; }
+};
+
+// clang-format off
+void test() {
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+
+  int i;
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  A a;
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+}
+// clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
new file mode 100644
index 0000000000000..05756cfc3b3b8
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Track number of moves
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+struct Small {};
+
+struct Big {
+  char c[128];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
+// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
+template <>
+class std::function_ref<int> {
+  using storage = std::__storage_func_ref_t;
+  void test() {
+    {
+      // by value small argument
+      // the internal function should pass by value
+      using call = std::function_ref<void(Small)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
+    }
+    {
+      // by value big argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Big)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
+    }
+    {
+      // by value non-trivial small argument
+      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
+      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
+    }
+    {
+      // by lvalue reference argument
+      // the internal function should pass by lvalue reference
+      using call = std::function_ref<void(Small&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
+    }
+    {
+      // by rvalue reference argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Small&&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
+    }
+  }
+};
+
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
+void test() {
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In this case, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index b7b7d0334830a..bcc453880c7e1 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -494,17 +494,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_invoke
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 9e987346c599b..3af3d6694c6bf 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
@@ -7132,17 +7132,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_gcd_lcm
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
new file mode 100644
index 0000000000000..029cb19d54f6a
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+int fn(int, float) { return 42; }
+
+int fn_noexcept(int, float) noexcept { return 42; }
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem(int, float) { return 42; }
+  int fn_mem_ref(int, float) & { return 42; }
+  int fn_mem_const(int, float) const { return 42; }
+  int fn_mem_const_ref(int, float) const& { return 42; }
+  int fn_mem_noexcept(int, float) noexcept { return 42; }
+};
+
+void test() {
+  // template<class F>
+  //  function_ref(F*) -> function_ref<F>;
+  {
+    std::function_ref fn_ref = fn;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = fn_noexcept;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F>
+  //  function_ref(constant_wrapper<c, F>) -> function_ref<...>;
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn_noexcept>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F, class T>
+  //  function_ref(constant_wrapper<c, F>, T&&) -> function_ref<...>;
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
+  }
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn_noexcept>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float) noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&() noexcept>>);
+  }
+  {
+    const S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&() noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_noexcept>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
new file mode 100644
index 0000000000000..b4df4392a2ade
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class T> function_ref& operator=(T) = delete;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - T is not the same type as function_ref,
+// - is_pointer_v<T> is false, and
+// - T is not a specialization of constant_wrapper.
+
+// non const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[] {}>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
+
+// const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[] { return 42; }>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[](int) { return 42; }>>);
+
+// non-const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[] noexcept {} >>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+// const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[] noexcept {}>>);
+static_assert(
+    !std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+int forty_two() { return 42; }
+
+void test() {
+  {
+    std::function_ref<int()> f(std::cw<[] { return 41; }>);
+    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = &forty_two;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const noexcept
+    std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
new file mode 100644
index 0000000000000..4b425ee03e286
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
@@ -0,0 +1,175 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f> constexpr function_ref(constant_wrapper<f>) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&> is true.
+
+auto l1          = [] {};
+auto l1_noexcept = [] noexcept {};
+auto l2          = [](int) {};
+auto l2_noexcept = [](int) noexcept {};
+
+struct NonConstInvocable {
+  void operator()() noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+// const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    std::function_ref<void()> f = std::cw<[] {}>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
new file mode 100644
index 0000000000000..dd6e10e8a3289
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -0,0 +1,359 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&, cv T*> is true.
+
+auto l1                = [](int*) {};
+auto l1_const          = [](const int*) {};
+auto l1_noexcept       = [](int*) noexcept {};
+auto l1_const_noexcept = [](const int*) noexcept {};
+auto l2                = [](int*, double) {};
+auto l2_const          = [](const int*, double) {};
+auto l2_noexcept       = [](int*, double) noexcept {};
+auto l2_const_noexcept = [](const int*, double) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(int*) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, const int*>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2_const>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const noexcept>,
+                                      std::constant_wrapper<l2_const_noexcept>,
+                                      int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_const_noexcept>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<l1_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const noexcept>,
+                                              std::constant_wrapper<l2_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<&A::f_const_noexcept>,
+                                              A*>);
+
+double f1(const int* x, double y) noexcept { return *x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int*) {}>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int*) {}>, &i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int* j, double d) {
+          *j = 5;
+          return *j + d;
+        }>,
+        &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
new file mode 100644
index 0000000000000..485fa95ca2998
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -0,0 +1,373 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<auto f, class U>
+//   constexpr function_ref(constant_wrapper<f>, U&& obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_rvalue_reference_v<U&&> is false, and
+// - is-invocable-using<const F&, cv T&> is true.
+
+auto l1          = [](int) {};
+auto l1_noexcept = [](int) noexcept {};
+auto l2          = [](int, double) {};
+auto l2_noexcept = [](int, double) noexcept {};
+auto l3          = [](int&) {};
+auto l3_noexcept = [](int&) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(long) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&&>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int) {}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int) {}>, i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int& j, double d) {
+          j = 5;
+          return j + d;
+        }>,
+        i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
new file mode 100644
index 0000000000000..61b70a559d786
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
new file mode 100644
index 0000000000000..1a8b26f86c8be
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
new file mode 100644
index 0000000000000..f142b81367ef1
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> function_ref(F* f) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_function_v<F> is true, and
+// - is-invocable-using<F> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (*)(int)>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A (*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, A)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+
+// const noexcept(true)
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, A) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              void (*)(int, double) noexcept>);
+
+int fn() { return 42; }
+
+int fn_maythrow(int i, A a) { return i - a.i; }
+int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+
+void foo(int) {}
+void bar(int) noexcept {}
+struct Int {
+  int i;
+  Int(int ii) noexcept : i(ii) {}
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+void test() {
+  {
+    // simple case
+    std::function_ref<int()> f(&fn);
+    assert(f() == 42);
+  }
+  {
+    // explicit(false)
+    std::function_ref<int()> f = &fn;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int(int, A)> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // noexcept
+    std::function_ref<int(int, A) noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // const
+    auto l = [](int x, int y, int z) { return x + y - z; };
+    std::function_ref<int(int, int, int) const> f(+l);
+    assert(f(2, 3, 4) == 1);
+  }
+  {
+    // const noexcept
+    std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+
+  {
+    std::function_ref<Int(int, int, int)> f(&needs_conversion);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(&needs_conversion);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(&needs_conversion);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
+    assert(f4(1, 2, 3).i == 6);
+
+    {
+      std::function_ref r1 = foo;
+      std::function_ref r2 = bar;
+      r1                   = r2; // ok
+    }
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
new file mode 100644
index 0000000000000..0f27bba821391
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
new file mode 100644
index 0000000000000..b40dfa9f2cf4b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const(1, 2, 3).i == 6);
+      assert(f2_const(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept(1, 2, 3).i == 6);
+      assert(f2_noexcept(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept(1, 2, 3).i == 6);
+      assert(f2_const_noexcept(1, 2, 3).i == 6);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
new file mode 100644
index 0000000000000..56d6dd3b7109b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -0,0 +1,316 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<class F> constexpr function_ref(F&&) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - remove_cvref_t<F> is not the same type as function_ref,
+// - is_member_pointer_v<T> is false, and
+// - is-invocable-using<cv T&> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+constexpr auto l1 = [] {};
+using L1          = std::remove_cvref_t<decltype(l1)>;
+
+constexpr auto l2 = [] { return A{5}; };
+using L2          = std::remove_cvref_t<decltype(l2)>;
+
+constexpr auto l2_noexcept = [] noexcept { return A{5}; };
+using L2Noexcept           = std::remove_cvref_t<decltype(l2_noexcept)>;
+
+constexpr auto l3 = [](int x, double d) { return x + d; };
+using L3          = std::remove_cvref_t<decltype(l3)>;
+
+constexpr auto l3_noexcept = [](int x, double d) noexcept { return x + d; };
+using L3Noexcept           = std::remove_cvref_t<decltype(l3_noexcept)>;
+
+struct NonConstInvocable {
+  int i;
+
+  int operator()(int x, double y) {
+    ++i;
+    return x + y + i;
+  }
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L2&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2&>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2 const&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const>, std::function_ref<int(int, double) const>>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(int, double)>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double)>, const NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>,
+                                              std::function_ref<int(int, double) const>>);
+
+// const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                      std::function_ref<int(int, double) const noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              std::function_ref<int(int, double) const noexcept>>);
+
+struct F {
+  int i;
+  int operator()(auto&&...) { return 5 + i; }
+
+  int operator()(auto&&...) const { return 6 + i; }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int fn() { return 5; }
+constexpr auto fn_noexcept = []() noexcept { return 6; };
+
+const auto one = []() noexcept { return 1; };
+const auto two = []() noexcept { return 2; };
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(l1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit(false)
+    std::function_ref<void()> f = l1;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<A() noexcept> f(l2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      auto a = f();
+      assert(a.i == 5);
+    }
+  }
+  {
+    // const
+    std::function_ref<double(int, double) const> f(l3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(l3_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // no copies of original callable
+    auto local = [i = 5] mutable { return i++; };
+    std::function_ref<int()> f(local);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 5);
+      assert(local() == 6);
+      assert(f() == 7);
+    }
+  }
+  {
+    // const correctness
+    F f{5};
+
+    std::function_ref<int()> f1(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1() == 10);
+      assert(std::as_const(f1)() == 10);
+    }
+
+    std::function_ref<int() const> f2(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 11);
+      assert(std::as_const(f2)() == 11);
+    }
+  }
+  {
+    // with conversions
+    NeedsConversion c{};
+
+    std::function_ref<Int(int, int, int)> f(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  {
+    // noexcept conversion
+    std::function_ref<int() noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const conversion
+    std::function_ref<int() const> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() noexcept> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to const conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() const> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // P3961R1 Less double indirection in function_ref
+    // double unwrapping
+    std::function_ref<int() const noexcept> f1(std::cw<one>);
+    std::function_ref<int()> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 1 || f2() == 2);
+      LIBCPP_ASSERT(f2() == 1);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
new file mode 100644
index 0000000000000..1cd0c769ce4d3
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -0,0 +1,362 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// R operator()(ArgTypes... args) const noexcept(noex);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "MoveOnly.h"
+
+template <class T, class... Args>
+concept ConstInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) };
+};
+
+template <class T, class... Args>
+concept ConstNoexceptInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) } noexcept;
+};
+
+static_assert(ConstInvocable<std::function_ref<void()>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void()>>);
+
+static_assert(ConstInvocable<std::function_ref<void() noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() noexcept>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void() const>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() const noexcept>>);
+
+struct S {
+  int data = 42;
+  int operator()(int& x) const noexcept { return data + x; }
+  int operator()(const int& x) const noexcept { return data + x + 1; }
+  int operator()(int&& x) const noexcept { return data + x + 2; }
+  int operator()(const int&& x) const noexcept { return data + x + 3; }
+};
+
+struct S2 {
+  double operator()(int x, int y, int z) noexcept { return x + y + z; }
+
+  double operator()(int x, int y, int z) const noexcept { return x + y + z + 1; }
+};
+
+struct Big {
+  char c[128];
+};
+
+struct S3Ref {
+  char operator()(Big& b) const noexcept { return b.c[0]; }
+};
+
+struct S3Value {
+  char operator()(Big b) const noexcept { return b.c[0]; }
+};
+
+void test_default() {
+  {
+    std::function_ref<void()> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&)> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly)> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int)> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&)> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&)> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&)> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&)> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int)> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int)> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&)> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const() {
+  {
+    std::function_ref<void() const> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const> f(std::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const> f(std::cw<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_noexcept() {
+  {
+    std::function_ref<void() noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test_const_noexcept() {
+  {
+    std::function_ref<void() const noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const noexcept> f(
+        std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+void test() {
+  test_default();
+  test_const();
+  test_noexcept();
+  test_const_noexcept();
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
new file mode 100644
index 0000000000000..72e26f13868ea
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
+#include <concepts>
+#include <functional>
+#include <type_traits>
+
+struct A {
+  A() {}
+  A(const A&) {}
+  ~A() {}
+};
+
+static_assert(std::copyable<std::function_ref<A(const A&)>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&)>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) noexcept>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const noexcept>>);
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 13a5dab01b74e..145be8c4f33cd 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -707,10 +707,9 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_function_ref",
             "values": {
-                "c++26": 202306  # P0792R14 function_ref: a type-erased callable reference
+                "c++26": 202603  # P3948R1 constant_wrapper is the only tool needed for passing constant expressions via function arguments
             },
             "headers": ["functional"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_gcd_lcm",

>From a7943a7b1bded1f816e34d60a0e06d3e204ea274 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 25 Apr 2026 14:20:04 +0100
Subject: [PATCH 03/21] review comments

---
 .../__functional/function_ref_common.h        | 22 ++++++-------------
 .../include/__functional/function_ref_impl.h  |  2 +-
 .../internal_call_signature.pass.cpp          |  2 +-
 .../constant_wrapper_ref.pass.cpp             |  8 +++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 12 ++++++++++
 5 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 61979231b56e8..72ded36d65107 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -16,6 +16,7 @@
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
 
@@ -37,21 +38,16 @@ struct __is_convertible_from_specialization : false_type {};
 // pointers
 // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
 // supported
-union __storage_func_ref_t {
+union __function_ref_storage {
   void* __obj_ptr_;
-  void const* __obj_const_ptr_;
   void (*__fn_ptr_)();
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage() noexcept : __obj_ptr_(nullptr) {}
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage(_Tp* __ptr) noexcept {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        __obj_const_ptr_ = __ptr;
-      } else {
-        __obj_ptr_ = __ptr;
-      }
+      __obj_ptr_ = const_cast<remove_cv_t<_Tp>*>(__ptr);
     } else {
       static_assert(is_function_v<_Tp>);
       __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
@@ -59,13 +55,9 @@ union __storage_func_ref_t {
   }
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__function_ref_storage __storage) {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
-      } else {
-        return static_cast<_Tp*>(__storage.__obj_ptr_);
-      }
+      return static_cast<_Tp*>(__storage.__obj_ptr_);
     } else {
       static_assert(is_function_v<_Tp>);
       return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index a4c98d3031602..36841d6745955 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -72,7 +72,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Arg>
   using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
 
-  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+  using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
   using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 05756cfc3b3b8..1e5df6413daf0 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -31,7 +31,7 @@ struct SmallButNonTrivial {
 // specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
 template <>
 class std::function_ref<int> {
-  using storage = std::__storage_func_ref_t;
+  using storage = std::__function_ref_storage;
   void test() {
     {
       // by value small argument
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index 485fa95ca2998..d3d5f6827193d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -362,6 +362,14 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // volatile objects
+    volatile int i = 5;
+    std::function_ref<int() > f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 56d6dd3b7109b..9ab012b3a04e8 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -161,6 +161,10 @@ constexpr auto fn_noexcept = []() noexcept { return 6; };
 const auto one = []() noexcept { return 1; };
 const auto two = []() noexcept { return 2; };
 
+struct VolatileCallable {
+  int operator()() volatile const { return 42; }
+};
+
 constexpr bool test() {
   {
     std::function_ref<void()> f(l1);
@@ -305,6 +309,14 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // volatile objects
+    const volatile VolatileCallable vc{};
+    std::function_ref<int()> f(vc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
 
   return true;
 }

>From 92f7a3cfc26a7ea985b17f18aec5ca51f1733ef2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 26 Apr 2026 09:41:10 +0100
Subject: [PATCH 04/21] remove stale comments

---
 libcxx/include/__functional/function_ref_common.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 72ded36d65107..c5adb8cbc7d52 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -34,10 +34,6 @@ class function_ref;
 template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization : false_type {};
 
-// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
-// pointers
-// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
-// supported
 union __function_ref_storage {
   void* __obj_ptr_;
   void (*__fn_ptr_)();

>From f953bc4800d19121be6f90e2603a1894579fd4e5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 14:06:05 +0100
Subject: [PATCH 05/21] fix bug

---
 .../__functional/function_ref_common.h        |  2 +-
 .../include/__functional/function_ref_impl.h  | 19 ++++++++++++-------
 .../internal_call_signature.pass.cpp          |  4 +++-
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  6 +++---
 .../func.wrap.ref.ctor/ref.pass.cpp           | 11 +++++++++++
 5 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index c5adb8cbc7d52..60bc6ef87af4f 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -31,7 +31,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <class _Fn1, class _Fn2>
 struct __is_convertible_from_specialization : false_type {};
 
 union __function_ref_storage {
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 36841d6745955..55755e9804c95 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -46,14 +46,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization<
-    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
-    _NoExcept1,
-    _Rp,
-    _ArgTypes...>
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) const noexcept(_NoExcept2)> >
     : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
 
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) noexcept(_NoExcept2)> >
+    : _And<is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)>,
+           is_convertible<_LIBCPP_FUNCTION_REF_CV int&, int&>> {};
+
 template <class _Rp, class... _ArgTypes, bool __is_noexcept>
 class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
 private:
@@ -62,9 +67,9 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
           value;
 
-  template <class _Fn>
+  template <class _Fn2>
   static constexpr bool __is_convertible_from_specialization_v =
-      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+      __is_convertible_from_specialization<function_ref, _Fn2>::value;
 
   template <class... _Tp>
   friend class function_ref;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 1e5df6413daf0..ebdf683791694 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -8,7 +8,9 @@
 
 // REQUIRES: std-at-least-c++26
 
-// Track number of moves
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
 
 #include <cassert>
 #include <functional>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index b4df4392a2ade..cb84b3a183920 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -35,9 +35,9 @@ static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrap
 static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
 
 // const noexcept(false)
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
@@ -61,7 +61,7 @@ static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::con
 // const noexcept(true)
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
-static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 9ab012b3a04e8..6b8b7c71f0d08 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -309,6 +309,17 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // P3961R1 Less double indirection in function_ref
+    // is-convertible-from-specialization<remove_cv_t<T>> is false
+    std::function_ref<int()> f1(std::cw<one>);
+    std::function_ref<int() const> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 2);
+    }
+  }
   {
     // volatile objects
     const volatile VolatileCallable vc{};

>From 829ffd58bb601270575ba03338d9a3f4933065c3 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 17:28:36 +0100
Subject: [PATCH 06/21] ci

---
 libcxx/include/__functional/function_ref_impl.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 55755e9804c95..454b0ffb4afb3 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -15,6 +15,7 @@
 #include <__functional/invoke.h>
 #include <__memory/addressof.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/conjunction.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>

>From 97b324ece3401e1c9ff8a6f063e3bcfe4ae584e0 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 10:39:44 +0100
Subject: [PATCH 07/21] static operator

---
 .../include/__functional/function_ref_impl.h  | 23 +++++++++++----
 .../func.wrap.ref.ctor/ref.pass.cpp           | 28 +++++++++++++++++++
 2 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 454b0ffb4afb3..ab65382782fb2 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
@@ -99,12 +100,22 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
-      : __storage_(std::addressof(__obj)),
-        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
-        }) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
+    using _Dn = remove_cv_t<_Tp>;
+    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
+                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+                  }) {
+      __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    } else {
+      __storage_ = __storage_t(std::addressof(__obj)),
+      __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    }
+  }
 
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 6b8b7c71f0d08..2684ec3548f8e 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -328,6 +328,34 @@ constexpr bool test() {
       assert(f() == 42);
     }
   }
+  {
+    // static call operator
+    struct StaticCall {
+      static int operator()(int x, int y) { return x + y; }
+    };
+    StaticCall sc;
+    std::function_ref<int(int, int)> f(sc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2) == 3);
+    }
+  }
+  {
+    // overload set with static and non-static call operator
+    struct OverloadSet {
+      static int operator()(int x) { return x + 1; }
+      int operator()(long x) const { return x + 2; }
+    };
+    OverloadSet os;
+    std::function_ref<int(int)> f1(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1(1) == 2);
+    }
+
+    std::function_ref<int(long)> f2(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1L) == 3);
+    }
+  }
 
   return true;
 }

>From dba0c61148e62e4fdfb60ff23413a191d2e70644 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 19:26:38 +0100
Subject: [PATCH 08/21] work around clang 22 bug

---
 libcxx/include/__functional/function_ref_common.h             | 4 ++++
 libcxx/include/__functional/function_ref_impl.h               | 4 +---
 .../func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp   | 1 +
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 60bc6ef87af4f..bfe3d938056f0 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -19,6 +19,7 @@
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
+#include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -107,6 +108,9 @@ struct __function_ref_arg_fwd<_Arg> {
   using type _LIBCPP_NODEBUG = _Arg;
 };
 
+template <class _Fn, class... _Args>
+concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
+
 #endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index ab65382782fb2..7a173dee7fa4f 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -102,9 +102,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
     using _Dn = remove_cv_t<_Tp>;
-    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
-                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
-                  }) {
+    if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
       };
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 2684ec3548f8e..11b993e4a3ec3 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -351,6 +351,7 @@ constexpr bool test() {
       assert(f1(1) == 2);
     }
 
+    LIBCPP_STATIC_ASSERT(!std::__statically_callable<OverloadSet, long>);
     std::function_ref<int(long)> f2(os);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f2(1L) == 3);

>From 529b8c712d9e0277d18ad8500d1ba45b01a27c8c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:12:34 +0100
Subject: [PATCH 09/21] review comments

---
 libcxx/include/__functional/function_ref_common.h | 15 +++++++--------
 libcxx/include/__functional/function_ref_impl.h   |  6 +++---
 .../constant_arg.mandates.verify.cpp              |  2 +-
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index bfe3d938056f0..0f40a07073cfc 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -15,7 +15,8 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
-#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
@@ -98,15 +99,13 @@ template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
 template <class _Arg>
-struct __function_ref_arg_fwd {
-  using type _LIBCPP_NODEBUG = _Arg&&;
-};
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
 
 template <class _Arg>
-  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
-struct __function_ref_arg_fwd<_Arg> {
-  using type _LIBCPP_NODEBUG = _Arg;
-};
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
 
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a173dee7fa4f..3424a99bdd174 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -77,7 +77,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   friend class function_ref;
 
   template <class _Arg>
-  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+  using __arg_t _LIBCPP_NODEBUG = _If<__register_passable<_Arg>, _Arg, _Arg&&>;
 
   using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
@@ -135,8 +135,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
           !requires {
             typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
           },
-          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
-          "and produces a result with a structural type");
+          "cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref "
+          "constructed from cw would be ambiguous");
     }
   }
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 519957eec6878..63cfbc0e97c9b 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -41,7 +41,7 @@ void test() {
   // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
   std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}

>From 5bd622cea5bd0b8a9a1a0670a86e07109baba6b2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:13:48 +0100
Subject: [PATCH 10/21] use std::forward instead of static_cast

---
 libcxx/include/__functional/function_ref_impl.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 3424a99bdd174..d276c78fd09e6 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -92,7 +92,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -104,13 +104,13 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     using _Dn = remove_cv_t<_Tp>;
     if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return _Dn::operator()(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     } else {
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -125,7 +125,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     requires __is_invocable_using<const _Fn&>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
       : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -146,7 +146,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -160,7 +160,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(__obj_ptr_),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");

>From 5e3a2706d2b2645d1efca1a3c24a2bbbbc40b6f2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:14:02 +0100
Subject: [PATCH 11/21] format

---
 .../func.wrap.ref.ctor/constant_arg.mandates.verify.cpp      | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index 63cfbc0e97c9b..c84cca2793bac 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -17,8 +17,9 @@
 
 // Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
 
-// For the first overload,
-// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+// For the first overload, f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>...
+// satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)>
+// is not a valid type.
 
 #include <functional>
 #include <utility>

>From b3ba60c374267d120b772b0d1f8ea0e3f3362303 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 20 Jun 2026 16:28:00 +0100
Subject: [PATCH 12/21] review commnets

---
 libcxx/include/CMakeLists.txt                 |   1 +
 .../__functional/function_ref_common.h        |   9 -
 .../include/__functional/function_ref_impl.h  |   8 +-
 .../include/__type_traits/register_passable.h |  38 ++++
 libcxx/include/module.modulemap.in            |   1 +
 .../constant_arg.mandates.verify.cpp          |  26 +--
 .../internal_call_signature.pass.cpp          | 138 ---------------
 .../internal_call_signature.sh.cpp            |  68 ++++++++
 .../views.span/span.cons/deduct.pass.cpp      |   7 -
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     |   9 +
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  24 ++-
 .../constant_wrapper_ptr.pass.cpp             |  60 ++++++-
 .../constant_wrapper_ref.pass.cpp             |  59 ++++++-
 .../func.wrap.ref.ctor/copy.pass.cpp          |   5 +
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   |  71 ++++++--
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  |   6 -
 .../func.wrap.ref.ctor/move.pass.cpp          |  70 +-------
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 102 ++---------
 .../func.wrap.ref.inv/invoke.pass.cpp         | 162 ++++++++++++++++++
 19 files changed, 506 insertions(+), 358 deletions(-)
 create mode 100644 libcxx/include/__type_traits/register_passable.h
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index a0d5cf70d267d..fcea3b7601182 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -914,6 +914,7 @@ set(files
   __type_traits/rank.h
   __type_traits/reference_constructs_from_temporary.h
   __type_traits/reference_converts_from_temporary.h
+  __type_traits/register_passable.h
   __type_traits/remove_all_extents.h
   __type_traits/remove_const.h
   __type_traits/remove_const_ref.h
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 0f40a07073cfc..e6fa031432a91 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -98,15 +98,6 @@ constexpr bool __is_constant_wrapper = false;
 template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
-template <class _Arg>
-concept __itanium_trivial_for_calls =
-    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
-    is_trivially_move_constructible_v<_Arg>;
-
-template <class _Arg>
-concept __register_passable =
-    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
-
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
 
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index d276c78fd09e6..7a968fc64de70 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/register_passable.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
@@ -92,7 +93,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
+          _Fp* __fn_ptr1 = __storage_t::template __get<_Fp>(__storage);
+          return std::invoke_r<_Rp>(__fn_ptr1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -110,7 +112,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
+        return std::invoke_r<_Rp>(__obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -145,7 +147,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          auto& __obj1 = static_cast<_LIBCPP_FUNCTION_REF_CV _Tp&>(*__storage_t::template __get<_Tp>(__storage));
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
diff --git a/libcxx/include/__type_traits/register_passable.h b/libcxx/include/__type_traits/register_passable.h
new file mode 100644
index 0000000000000..21e23e62052c1
--- /dev/null
+++ b/libcxx/include/__type_traits/register_passable.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___TYPE_TRAITS_REGISTER_PASSABLE_H
+#define _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
+
+#include <__config>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Arg>
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
+
+template <class _Arg>
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
+
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index cb29fac536046..e3b9e5ac09cb3 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -351,6 +351,7 @@ module std_core {
       header "__type_traits/reference_converts_from_temporary.h"
       export std_core.type_traits.integral_constant
     }
+    module register_passable                          { header "__type_traits/register_passable.h" }
     module remove_all_extents                         { header "__type_traits/remove_all_extents.h" }
     module remove_const_ref                           { header "__type_traits/remove_const_ref.h" }
     module remove_const                               { header "__type_traits/remove_const.h" }
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
index c84cca2793bac..08e8b8e9b8b52 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -35,27 +35,27 @@ struct B {
 
 // clang-format off
 void test() {
-  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
+  // expected-error-re@*:* {{static assertion failed {{.*}}cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
-  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
   A a;
-  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
-  // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
+  std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a);
+  // expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
 }
 // clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
deleted file mode 100644
index ebdf683791694..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-struct Small {};
-
-struct Big {
-  char c[128];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
-// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
-template <>
-class std::function_ref<int> {
-  using storage = std::__function_ref_storage;
-  void test() {
-    {
-      // by value small argument
-      // the internal function should pass by value
-      using call = std::function_ref<void(Small)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
-    }
-    {
-      // by value big argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Big)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
-    }
-    {
-      // by value non-trivial small argument
-      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
-      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
-    }
-    {
-      // by lvalue reference argument
-      // the internal function should pass by lvalue reference
-      using call = std::function_ref<void(Small&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
-    }
-    {
-      // by rvalue reference argument
-      // the internal function should pass by rvalue reference
-      using call = std::function_ref<void(Small&&)>::__call_t;
-      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
-    }
-  }
-};
-
-struct TrackCopyMove {
-  mutable int copy_count = 0;
-  int move_count         = 0;
-
-  TrackCopyMove() = default;
-  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
-    ++copy_count;
-    ++other.copy_count;
-  }
-
-  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
-    ++move_count;
-    ++other.move_count;
-  }
-  TrackCopyMove& operator=(const TrackCopyMove& other) {
-    ++copy_count;
-    ++other.copy_count;
-    return *this;
-  }
-  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
-    ++move_count;
-    ++other.move_count;
-    return *this;
-  }
-};
-
-void test() {
-  {
-    // Arg type is an lvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&)> f = lambda;
-    f(t);
-  }
-  {
-    // Arg type is an rvalue reference, we should not copy or move the object
-    TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) {
-      assert(&tm == &t);
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 0);
-    };
-    std::function_ref<void(TrackCopyMove&&)> f = lambda;
-    f(std::move(t));
-  }
-  {
-    // Arg type is a prvalue, we should move but not copy the object
-    // In this case, where the type is not trivially copyable, the object should be
-    // moved exactly once when passing into the lambda. The internal functions
-    // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) {
-      assert(tm.copy_count == 0);
-      assert(tm.move_count == 1);
-    };
-    std::function_ref<void(TrackCopyMove)> f = lambda;
-    f(TrackCopyMove{});
-  }
-}
-
-int main(int, char**) {
-  test();
-
-  return 0;
-}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
new file mode 100644
index 0000000000000..a32a1083b5596
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// REQUIRES: has-filecheck
+
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
+
+struct Small {
+  char c[8];
+};
+
+struct Big {
+  char c[256];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// CHECK: define{{.*}} @_Z10test_small
+// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
+// CHECK: %3 = alloca %struct.Small, align 1
+// CHECK: %6 = alloca %struct.Small, align 1
+// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
+// CHECK: store i64 %1, ptr %7, align 1
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
+// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
+// CHECK: %15 = load i64, ptr %14, align 1
+// CHECK: call void %10(ptr %13, i64 %15)
+//
+// The above has two local copies of Small object. It loads %1 to local copy %3, and then
+// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
+void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
+
+// CHECK: define{{.*}} @_Z8test_big
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
+//
+// The above directly calls the function pointer with the parameter Big object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the Big object by reference
+void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
+
+// CHECK: define{{.*}} @_Z22test_small_non_trivial
+// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
+//
+// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
+// points to the operator()'s parameter %1, thus, no extra copies/moves
+// So we internally pass the SmallButNonTrivial object by reference
+void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file
diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index f5b590cd9631d..35d9b36f824aa 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -88,13 +88,6 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
-
-  {
-    std::span s{std::begin(arr), std::cw<3>};
-    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
-    assert(s.size() == std::size(arr));
-    assert(s.data() == std::data(arr));
-  }
 #endif
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
index 029cb19d54f6a..730a07202062d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -8,6 +8,15 @@
 
 // REQUIRES: std-at-least-c++26
 
+// template<class F>
+// function_ref(F*) -> function_ref<F>;
+//
+// template<auto c, class F0>
+// function_ref(constant_wrapper<c, F0>) -> function_ref<see below>;
+//
+// template<auto c, class F, class T>
+// function_ref(constant_wrapper<c, F>, T&&) -> function_ref<see below>;
+
 #include <cassert>
 #include <functional>
 #include <utility>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index cb84b3a183920..5866488be4a94 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -11,6 +11,7 @@
 // template<class T> function_ref& operator=(T) = delete;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -73,38 +74,49 @@ static_assert(
 
 int forty_two() { return 42; }
 
+// These runtime tests are testing that when the constraints are not met,
+// the assignment operator is not deleted and the implicit constructor
+// and the move assignment are called
 void test() {
   {
     std::function_ref<int()> f(std::cw<[] { return 41; }>);
-    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f =
+        std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = &forty_two;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = &forty_two;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     std::function_ref<int() > f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int()>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 41; }>);
-    f = std::cw<[] { return 42; }>;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // noexcept
     std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() noexcept>&> decltype(auto) result = f = std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
   {
     // const noexcept
     std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
-    f = std::cw<[] noexcept { return 42; }>;
+    std::same_as<std::function_ref<int() const noexcept>&> decltype(auto) result = f =
+        std::cw<[] noexcept { return 42; }>;
+    assert(&result == &f);
     assert(f() == 42);
   }
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
index dd6e10e8a3289..74f52063b6788 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -94,6 +94,7 @@ static_assert(
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
 static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, const int*>);
 
 static_assert(
     !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
@@ -180,6 +181,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int*) const noexcept { return 4; }
+  constexpr int operator()(const int*) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -231,6 +237,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -248,6 +262,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -263,39 +285,51 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -348,6 +382,20 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, &i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, &i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, &i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index d3d5f6827193d..979da32e60c3d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -199,6 +199,11 @@ struct M {
   }
 };
 
+struct Overload {
+  constexpr int operator()(int&) const noexcept { return 4; }
+  constexpr int operator()(const int&) const noexcept { return 5; }
+};
+
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -248,6 +253,14 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // const
+    const int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
   {
     // noexcept
     int i = 5;
@@ -264,6 +277,14 @@ constexpr bool test() {
       assert(f(2.0) == 7.0);
     }
   }
+  {
+    // const noexcept
+    const int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
   {
     // member ptr
     M m{3};
@@ -278,37 +299,49 @@ constexpr bool test() {
       assert(g(j) == 45);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const
+    M m{3};
     std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const() == 8);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const(j) == 46);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // noexcept
+    M m{3};
     std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept() == 10);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_noexcept(j) == 47);
       assert(j == 42);
     }
-
+  }
+  {
+    // member ptr
+    // const noexcept
+    M m{3};
     std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept() == 12);
     }
 
-    j = 0;
+    int j = 0;
     std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(g_const_noexcept(j) == 48);
@@ -370,6 +403,20 @@ constexpr bool test() {
       assert(f() == 47);
     }
   }
+  {
+    // CV forwarded correctly
+    int i = 0;
+    std::function_ref<int()> f(std::cw<Overload{}>, i);
+    std::function_ref<int() const> f_const(std::cw<Overload{}>, i);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, i);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 4);
+      assert(f_const() == 5);
+      assert(f_noexcept() == 4);
+      assert(f_const_noexcept() == 5);
+    }
+  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
index 61b70a559d786..7f837149cc6b5 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -22,6 +22,11 @@ static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
 double f1(int x, double y) noexcept { return x + y; }
 
 struct Int {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
index 1a8b26f86c8be..53c79b4097362 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -11,6 +11,7 @@
 // constexpr function_ref& operator=(const function_ref&) noexcept = default;
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -22,6 +23,11 @@ static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
@@ -41,7 +47,8 @@ constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = f;
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -51,7 +58,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = f;
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
 
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
@@ -62,7 +70,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -72,7 +81,8 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = f;
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = f;
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -82,32 +92,47 @@ constexpr bool test() {
     // with conversions
     std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
         std::cw<[](int, int, int) noexcept { return Int{1}; }>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
@@ -117,31 +142,47 @@ constexpr bool test() {
     // with conversions function pointer
     std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
     std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = f2;
+    std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
+    assert(&result == &f);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2, 3).i == 6);
       assert(f2(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const
     std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = f2_const;
+    std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
+    assert(&result == &f_const);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const(1, 2, 3).i == 6);
       assert(f2_const(1, 2, 3).i == 6);
     }
-
+  }
+  {
+    // with conversions function pointer
+    // noexcept
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = f2_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
+    assert(&result == &f_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_noexcept(1, 2, 3).i == 6);
       assert(f2_noexcept(1, 2, 3).i == 6);
     }
+  }
 
+  {
+    // with conversions function pointer
+    // const noexcept
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
     std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = f2_const_noexcept;
+    std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
+        f2_const_noexcept;
+    assert(&result == &f_const_noexcept);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f_const_noexcept(1, 2, 3).i == 6);
       assert(f2_const_noexcept(1, 2, 3).i == 6);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index f142b81367ef1..aa0ccf8d67993 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -134,12 +134,6 @@ void test() {
 
     std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
     assert(f4(1, 2, 3).i == 6);
-
-    {
-      std::function_ref r1 = foo;
-      std::function_ref r2 = bar;
-      r1                   = r2; // ok
-    }
   }
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
index 0f27bba821391..025df83ad538d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -8,6 +8,8 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
 #include <functional>
 #include <utility>
@@ -20,18 +22,12 @@ static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-double f1(int x, double y) noexcept { return x + y; }
-
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const noexcept>>);
 
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+double f1(int x, double y) noexcept { return x + y; }
 
 constexpr bool test() {
   {
@@ -66,58 +62,6 @@ constexpr bool test() {
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
-    auto f_copy = std::move(f);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
-    auto f2_copy = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f2_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
-    auto f3_copy = std::move(f3);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f3_copy(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
-    auto f4_copy = std::move(f4);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f4_copy(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
index b40dfa9f2cf4b..10620f5d48725 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -8,7 +8,10 @@
 
 // REQUIRES: std-at-least-c++26
 
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
 #include <cassert>
+#include <concepts>
 #include <functional>
 #include <utility>
 #include <type_traits>
@@ -20,26 +23,20 @@ static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
 static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
 
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const noexcept>>);
+
 double plus(int x, double y) noexcept { return x + y; }
 double minus(int x, double y) noexcept { return x - y; }
 
-struct Int {
-  int i;
-  constexpr Int(int ii) noexcept : i(ii) {}
-};
-
-struct NeedsConversion {
-  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
-};
-
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
-int zero(Int, Int, Int) noexcept { return 0; }
-
 constexpr bool test() {
   {
     std::function_ref<void()> f(std::cw<[] {}>);
     std::function_ref<void()> f2(std::cw<[] {}>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       f();
       f2();
@@ -49,8 +46,8 @@ constexpr bool test() {
     // const
     std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
-    f2 = std::move(f);
-
+    std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f() == 42);
       assert(f2() == 42);
@@ -60,7 +57,8 @@ constexpr bool test() {
     // noexcept
     std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
@@ -70,81 +68,13 @@ constexpr bool test() {
     // const noexcept
     std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
     std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
-    f2 = std::move(f);
+    std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = std::move(f);
+    assert(&result == &f2);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f(1, 2.0) == 3.0);
       assert(f2(1, 2.0) == 3.0);
     }
   }
-  {
-    // with conversions
-    std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
-        std::cw<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
-  {
-    // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
-    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
-    f = std::move(f2);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f(1, 2, 3).i == 6);
-      assert(f2(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
-    f_const = std::move(f2_const);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const(1, 2, 3).i == 6);
-      assert(f2_const(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
-    f_noexcept = std::move(f2_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_noexcept(1, 2, 3).i == 6);
-      assert(f2_noexcept(1, 2, 3).i == 6);
-    }
-
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
-    f_const_noexcept = std::move(f2_const_noexcept);
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-      assert(f_const_noexcept(1, 2, 3).i == 6);
-      assert(f2_const_noexcept(1, 2, 3).i == 6);
-    }
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 1cd0c769ce4d3..0dced07c439d6 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -67,6 +67,32 @@ struct S3Value {
   char operator()(Big b) const noexcept { return b.c[0]; }
 };
 
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
 void test_default() {
   {
     std::function_ref<void()> f = [] {};
@@ -135,6 +161,40 @@ void test_default() {
     std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const() {
@@ -205,6 +265,40 @@ void test_const() {
     std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_noexcept() {
@@ -276,6 +370,40 @@ void test_noexcept() {
     std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test_const_noexcept() {
@@ -347,6 +475,40 @@ void test_const_noexcept() {
     std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
     assert(f2(b) == 'a');
   }
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) noexcept {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&) const noexcept> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&) const noexcept> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In libc++, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) noexcept{
+      assert(tm.copy_count == 0);
+      LIBCPP_ASSERT(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove) const noexcept> f = lambda;
+    f(TrackCopyMove{});
+  }
 }
 
 void test() {

>From 04cce85f72e377682999b3c9c769b6e6282be331 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:38:35 +0100
Subject: [PATCH 13/21] format

---
 .../func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp       | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 0dced07c439d6..820fed5fc76bc 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -384,7 +384,7 @@ void test_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -397,7 +397,7 @@ void test_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };
@@ -489,7 +489,7 @@ void test_const_noexcept() {
   {
     // Arg type is an rvalue reference, we should not copy or move the object
     TrackCopyMove t;
-    auto lambda = [&t](TrackCopyMove&& tm) noexcept{
+    auto lambda = [&t](TrackCopyMove&& tm) noexcept {
       assert(&tm == &t);
       assert(tm.copy_count == 0);
       assert(tm.move_count == 0);
@@ -502,7 +502,7 @@ void test_const_noexcept() {
     // In libc++, where the type is not trivially copyable, the object should be
     // moved exactly once when passing into the lambda. The internal functions
     // of function_ref should forward the argument without copying or moving it
-    auto lambda = [](TrackCopyMove tm) noexcept{
+    auto lambda = [](TrackCopyMove tm) noexcept {
       assert(tm.copy_count == 0);
       LIBCPP_ASSERT(tm.move_count == 1);
     };

>From ea630194a1322b1b4d96356421f790ca9252ad8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:31 +0100
Subject: [PATCH 14/21] Update libcxx/docs/Status/Cxx26Issues.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 55c642fd15ce1..d3c99fb086867 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","24","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""

>From 1f9e504b29de5dfa00a3552be6e179baf50a196d Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:40 +0100
Subject: [PATCH 15/21] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 530575f4973da..3178f0652a74b 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -192,7 +192,7 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
 "`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""

>From 94623a0041913e0cf10e4c86979cff9bf2e73720 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:14:50 +0100
Subject: [PATCH 16/21] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 3178f0652a74b..f06f99caab47a 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","24","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."

>From ad2bd3a7365138df9c00c368f6b6b1480d242b14 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:15:01 +0100
Subject: [PATCH 17/21] Update libcxx/docs/Status/Cxx26Issues.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Issues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index d3c99fb086867..058dd6900cdc7 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","24","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""

>From 9928fffabc4f143a84def6354745264002a14a26 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:15:11 +0100
Subject: [PATCH 18/21] Update libcxx/docs/Status/Cxx26Papers.csv

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/Status/Cxx26Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f06f99caab47a..81b20f6088097 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -194,7 +194,7 @@
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
 "`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","24","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""

>From 34b8bc5a68503d520cbc56e7f216a39fe98b2f84 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:32:58 +0100
Subject: [PATCH 19/21] review

---
 libcxx/docs/ReleaseNotes/23.rst               |  3 -
 libcxx/docs/ReleaseNotes/24.rst               |  3 +
 .../internal_call_signature.sh.cpp            | 68 -------------------
 3 files changed, 3 insertions(+), 71 deletions(-)
 delete mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 5d295453dd79f..f7a8e5b04f1ea 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,9 +55,6 @@ Implemented Papers
 - P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 - P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
-- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
-- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
-- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..e20fa560865ad 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,9 @@ Implemented Papers
 ------------------
 
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
deleted file mode 100644
index a32a1083b5596..0000000000000
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.sh.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// REQUIRES: has-filecheck
-
-// In case the function signature is taking an argument by value,
-// when the type is small and trivial, we pass it internally by value,
-// otherwise, we pass it by rvalue reference
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-// RUN: %{cxx} -c %s %{flags} %{compile_flags} -S -emit-llvm --target=arm64-apple-darwin25.4.0 -O0 -o - | FileCheck %s
-
-struct Small {
-  char c[8];
-};
-
-struct Big {
-  char c[256];
-};
-
-struct SmallButNonTrivial {
-  Small s;
-  SmallButNonTrivial() = default;
-  SmallButNonTrivial(const SmallButNonTrivial&) {}
-};
-
-// CHECK: define{{.*}} @_Z10test_small
-// CHECK: define{{.*}} @_ZNK{{.*}}function_ref{{.*}}Fv5Small{{.*}}ES1_(ptr noundef %0, i64 %1)
-// CHECK: %3 = alloca %struct.Small, align 1
-// CHECK: %6 = alloca %struct.Small, align 1
-// CHECK: %7 = getelementptr inbounds nuw %struct.Small, ptr %3, i32 0, i32 0
-// CHECK: store i64 %1, ptr %7, align 1
-// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %6, ptr align 1 %3, i64 8, i1 false)
-// CHECK: %14 = getelementptr inbounds nuw %struct.Small, ptr %6, i32 0, i32 0
-// CHECK: %15 = load i64, ptr %14, align 1
-// CHECK: call void %10(ptr %13, i64 %15)
-//
-// The above has two local copies of Small object. It loads %1 to local copy %3, and then
-// memcpy to the second copy %6 and eventually load into %15, which passed to the type erased function
-void test_small(std::function_ref<void(Small)> f) { f(Small{}); }
-
-// CHECK: define{{.*}} @_Z8test_big
-// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv3Big{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(256) %1)
-//
-// The above directly calls the function pointer with the parameter Big object pointer
-// points to the operator()'s parameter %1, thus, no extra copies/moves
-// So we internally pass the Big object by reference
-void test_big(std::function_ref<void(Big)> f) { f(Big{}); }
-
-// CHECK: define{{.*}} @_Z22test_small_non_trivial
-// CHECK: define{{.*}} @_ZNKSt3__112function_ref{{.*}}Fv18SmallButNonTrivial{{.*}}ES1_(ptr noundef %0, ptr noundef %1)
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef nonnull align 1 dereferenceable(8) %1)
-//
-// The above directly calls the function pointer with the parameter SmallButNonTrivial object pointer
-// points to the operator()'s parameter %1, thus, no extra copies/moves
-// So we internally pass the SmallButNonTrivial object by reference
-void test_small_non_trivial(std::function_ref<void(SmallButNonTrivial)> f) { f(SmallButNonTrivial{}); }
\ No newline at end of file

>From 7274482083a6cd8c1d3fbf02b035c25c19f99186 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:14:44 +0100
Subject: [PATCH 20/21] CI

---
 libcxx/include/__functional/function_ref_impl.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a968fc64de70..ada14643ec496 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -158,8 +158,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <auto _Cw, class _Fn, class _Tp>
     requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
-                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
-      : __storage_(__obj_ptr_),
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+      : __storage_(__obj_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
           return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
@@ -169,7 +169,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     }
 
     if constexpr (is_member_pointer_v<_Fn>) {
-      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
     }
   }
 

>From 8b592acd53532d355c60093a1c0626dd99c45cad Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:54:26 +0100
Subject: [PATCH 21/21] CI

---
 libcxx/docs/conf.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/conf.py b/libcxx/docs/conf.py
index 9051bce7cf356..98aa52edd5d2e 100644
--- a/libcxx/docs/conf.py
+++ b/libcxx/docs/conf.py
@@ -32,9 +32,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = "23.0"
+version = "24.0"
 # The full version, including alpha/beta/rc tags.
-release = "23.0"
+release = "24.0"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.


From libcxx-commits at lists.llvm.org  Sat Jul 25 02:19:23 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 02:19:23 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++][test] re-enable clear padding
	runtime test (PR #211258)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/211258@github.com>
Message-ID: <6a647f9b.f06f4994.3876ce.d432@mx.google.com>


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/211258

>From 7a273532f7fae784b07b95ce34f474d752fbcaa9 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:20:39 +0100
Subject: [PATCH 1/3] [libc++][test] re-enable clear padding runtime test

---
 libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
index f9c910eca1f47..20241fd6596ef 100644
--- a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 // UNSUPPORTED: gcc
-// UNSUPPORTED: clang-21, clang-22, clang-23, clang-24, apple-clang-21
+// UNSUPPORTED: clang-21, clang-22, clang-23, apple-clang-21
 
 // ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-dynamic-class-memaccess
 

>From b4f18e4603895f260dd208e115cdee6e54b781d3 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:41:48 +0100
Subject: [PATCH 2/3] clang 23

---
 libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
index 20241fd6596ef..ef418025c17dc 100644
--- a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
 // UNSUPPORTED: gcc
-// UNSUPPORTED: clang-21, clang-22, clang-23, apple-clang-21
+// UNSUPPORTED: clang-21, clang-22, apple-clang-21
 
 // ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-dynamic-class-memaccess
 

>From efe247c208cb2a29d1ef8b5396fc41ca8d06cf14 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 25 Jul 2026 10:19:09 +0100
Subject: [PATCH 3/3] review

---
 .../atomics/builtin_clear_padding.pass.cpp       | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
index ef418025c17dc..37f08dd5794e4 100644
--- a/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp
@@ -6,8 +6,20 @@
 //
 //===----------------------------------------------------------------------===//
 // UNSUPPORTED: c++03
-// UNSUPPORTED: gcc
-// UNSUPPORTED: clang-21, clang-22, apple-clang-21
+
+// Older versions of Clang don't support __builtin_clear_padding
+// UNSUPPORTED: clang-19, clang-20, clang-21, clang-22, apple-clang-17, apple-clang-21
+
+// Older Clang doesn't handle __builtin_clear_padding correctly on Windows
+// (see https://github.com/llvm/llvm-project/issues/209787)
+// XFAIL: clang-23 && target={{.+}}-{{.+}}-windows-msvc
+// UNSUPPORTED: clang-24 && target={{.+}}-{{.+}}-windows-msvc
+
+// ASAN has a bug where it does not calculate the correct size
+// of ext_vector_type of fp80 long double
+// (see https://github.com/llvm/llvm-project/issues/212002)
+// XFAIL: clang-23 && asan
+// UNSUPPORTED: clang-24 && asan
 
 // ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile -Wno-dynamic-class-memaccess
 


From libcxx-commits at lists.llvm.org  Sat Jul 25 02:45:20 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 02:45:20 -0700 (PDT)
Subject: [libcxx-commits] [clang] [compiler-rt] [libcxx] [Clang] Support
 atomic operations on _BitInt(N) (PR #204815)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/204815@github.com>
Message-ID: <6a6485b0.3cce8983.19fb00.bd5b@mx.google.com>


================
@@ -1144,6 +1322,27 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
   LValue AtomicVal = MakeAddrLValue(Ptr, AtomicTy);
   AtomicInfo Atomics(*this, AtomicVal);
 
+  // A `_BitInt(N)` read-modify-write whose value width has padding bits, or
+  // whose size forces a libcall, cannot use a single atomicrmw: the op would
+  // carry into / compare the padding bits, and no arbitrary-width
+  // __atomic_fetch_* libcall exists. Emit a compare-exchange loop instead.
+  // Bitwise and/or/xor are exact even with padding, so only the wide case needs
+  // the loop for them. load/store/exchange/compare_exchange keep their paths.
+  if (MemTy->isBitIntType()) {
+    llvm::AtomicRMWInst::BinOp BinOp;
+    bool RMWReturnsNew;
+    if (classifyBitIntRMW(E->getOp(), MemTy->isSignedIntegerType(), BinOp,
+                          RMWReturnsNew)) {
+      bool WideOrNonPow2 = (Size & (Size - 1)) != 0 || Size > 16;
+      bool Bitwise = BinOp == llvm::AtomicRMWInst::And ||
+                     BinOp == llvm::AtomicRMWInst::Or ||
+                     BinOp == llvm::AtomicRMWInst::Xor;
+      if (WideOrNonPow2 || (hasBitIntPadding(MemTy, getContext()) && !Bitwise))
----------------
huixie90 wrote:

The current libc++ approach of CAS for types with padding is that we maintain the invariant that at all times atomic will have zeros in their paddings and all operations will need to clear the incoming paddings first (see __builtin_clear_padding) https://github.com/llvm/llvm-project/pull/76180/changes
I think the __buitin_clear_padding already handles _BitInt
instead of emitting a loop, could we just leave as it is and let the libc++ code to handle it? 

https://github.com/llvm/llvm-project/pull/204815

From libcxx-commits at lists.llvm.org  Sat Jul 25 03:48:37 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 03:48:37 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Speed up xsgetn for
	always_noconv (PR #212010)
Message-ID: <llvm.org/llvm/llvm-project/pull/212010@github.com>


https://github.com/aokblast created https://github.com/llvm/llvm-project/pull/212010

In the always_noconv path, optimize character reads in the same way as xsgetn.

Unlike fread, getwc has different semantics, so the same optimization cannot be applied to wide characters. Therefore, only optimize the char path.

>From 3fe8ab44b769ed1a0c79f21a2643485ada4780dd Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Sat, 25 Jul 2026 18:42:41 +0800
Subject: [PATCH] [libcxx] Speed up xsgetn for always_noconv

In the always_noconv path, optimize character reads in the same way as
xsgetn.

Unlike fread, getwc has different semantics, so the same optimization
cannot be applied to wide characters. Therefore, only optimize the char
path.
---
 libcxx/src/std_stream.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libcxx/src/std_stream.h b/libcxx/src/std_stream.h
index 4b9d3a34b2441..214acc9e88b70 100644
--- a/libcxx/src/std_stream.h
+++ b/libcxx/src/std_stream.h
@@ -45,6 +45,7 @@ class _LIBCPP_HIDDEN __stdinbuf : public basic_streambuf<_CharT, char_traits<_Ch
 protected:
   virtual int_type underflow();
   virtual int_type uflow();
+  virtual streamsize xsgetn(char_type* __s, streamsize __n);
   virtual int_type pbackfail(int_type __c = traits_type::eof());
   virtual void imbue(const locale& __loc);
 
@@ -199,6 +200,28 @@ typename __stdinbuf<_CharT>::int_type __stdinbuf<_CharT>::__getchar(bool __consu
   return traits_type::to_int_type(__1buf);
 }
 
+template <class _CharT>
+streamsize __stdinbuf<_CharT>::xsgetn(char_type* __s, streamsize __n) {
+  if constexpr (is_same<_CharT, char>::value) {
+    if (__always_noconv_) {
+      streamsize __i = 0;
+      if (__i < __n && __last_consumed_is_next_) {
+        __s[__i++]               = traits_type::to_char_type(__last_consumed_);
+        __last_consumed_         = traits_type::eof();
+        __last_consumed_is_next_ = false;
+      }
+      if (__i < __n) {
+        size_t __nread = fread(__s + __i, 1, static_cast<size_t>(__n - __i), __file_);
+        if (__nread > 0)
+          __last_consumed_ = traits_type::to_int_type(__s[__i + __nread - 1]);
+        __i += static_cast<streamsize>(__nread);
+      }
+      return __i;
+    }
+  }
+  return basic_streambuf<char_type, traits_type>::xsgetn(__s, __n);
+}
+
 template <class _CharT>
 typename __stdinbuf<_CharT>::int_type __stdinbuf<_CharT>::pbackfail(int_type __c) {
   if (traits_type::eq_int_type(__c, traits_type::eof())) {


From libcxx-commits at lists.llvm.org  Sat Jul 25 04:31:56 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 04:31:56 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Speed up xsgetn for
	always_noconv (PR #212010)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/212010@github.com>
Message-ID: <6a649eac.21a19891.ef87d.7073@mx.google.com>


https://github.com/aokblast ready_for_review https://github.com/llvm/llvm-project/pull/212010

From libcxx-commits at lists.llvm.org  Sat Jul 25 04:32:33 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 04:32:33 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Speed up xsgetn for
	always_noconv (PR #212010)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/212010@github.com>
Message-ID: <6a649ed1.bd93e731.32a2a8.6355@mx.google.com>


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: aokblast

<details>
<summary>Changes</summary>

In the always_noconv path, optimize character reads in the same way as xsgetn.

Unlike fread, getwc has different semantics, so the same optimization cannot be applied to wide characters. Therefore, only optimize the char path.

---
Full diff: https://github.com/llvm/llvm-project/pull/212010.diff


1 Files Affected:

- (modified) libcxx/src/std_stream.h (+23) 


``````````diff
diff --git a/libcxx/src/std_stream.h b/libcxx/src/std_stream.h
index 4b9d3a34b2441..214acc9e88b70 100644
--- a/libcxx/src/std_stream.h
+++ b/libcxx/src/std_stream.h
@@ -45,6 +45,7 @@ class _LIBCPP_HIDDEN __stdinbuf : public basic_streambuf<_CharT, char_traits<_Ch
 protected:
   virtual int_type underflow();
   virtual int_type uflow();
+  virtual streamsize xsgetn(char_type* __s, streamsize __n);
   virtual int_type pbackfail(int_type __c = traits_type::eof());
   virtual void imbue(const locale& __loc);
 
@@ -199,6 +200,28 @@ typename __stdinbuf<_CharT>::int_type __stdinbuf<_CharT>::__getchar(bool __consu
   return traits_type::to_int_type(__1buf);
 }
 
+template <class _CharT>
+streamsize __stdinbuf<_CharT>::xsgetn(char_type* __s, streamsize __n) {
+  if constexpr (is_same<_CharT, char>::value) {
+    if (__always_noconv_) {
+      streamsize __i = 0;
+      if (__i < __n && __last_consumed_is_next_) {
+        __s[__i++]               = traits_type::to_char_type(__last_consumed_);
+        __last_consumed_         = traits_type::eof();
+        __last_consumed_is_next_ = false;
+      }
+      if (__i < __n) {
+        size_t __nread = fread(__s + __i, 1, static_cast<size_t>(__n - __i), __file_);
+        if (__nread > 0)
+          __last_consumed_ = traits_type::to_int_type(__s[__i + __nread - 1]);
+        __i += static_cast<streamsize>(__nread);
+      }
+      return __i;
+    }
+  }
+  return basic_streambuf<char_type, traits_type>::xsgetn(__s, __n);
+}
+
 template <class _CharT>
 typename __stdinbuf<_CharT>::int_type __stdinbuf<_CharT>::pbackfail(int_type __c) {
   if (traits_type::eq_int_type(__c, traits_type::eof())) {

``````````

</details>


https://github.com/llvm/llvm-project/pull/212010

From libcxx-commits at lists.llvm.org  Sat Jul 25 04:44:19 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 04:44:19 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libcxx] Speed up xsgetn for
	always_noconv (PR #212010)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/212010@github.com>
Message-ID: <6a64a193.160324b0.28c9e1.cc64@mx.google.com>


https://github.com/aokblast edited https://github.com/llvm/llvm-project/pull/212010

From libcxx-commits at lists.llvm.org  Sat Jul 25 05:00:11 2026
From: libcxx-commits at lists.llvm.org (via libcxx-commits)
Date: Sat, 25 Jul 2026 05:00:11 -0700 (PDT)
Subject: [libcxx-commits] [libcxx] [libc++] Implement P2442R1
	`std::views::chunk` (PR #171234)
In-Reply-To: <llvm.org/llvm/llvm-project/pull/171234@github.com>
Message-ID: <6a64a54b.f06f4994.3876ce.c1d6@mx.google.com>


https://github.com/shyeyian updated https://github.com/llvm/llvm-project/pull/171234

>From f6554ea2a0e1d9571cf82fc7b2f3d1115c466474 Mon Sep 17 00:00:00 2001
From: anonymous <shyeyian at petalmail.com>
Date: Wed, 24 Dec 2025 22:05:29 +0800
Subject: [PATCH 01/32] Implement `views::chunk`.

This commit squashes previous commits.
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/22.rst               |   1 +
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/chunk_view.h          | 562 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/ranges                         |  12 +
 libcxx/include/version                        |   2 +-
 libcxx/modules/std/ranges.inc                 |  11 +-
 .../no_unique_address.compile.pass.cpp        |  53 ++
 .../range.chunk/nodiscard.verify.cpp          |  86 +++
 .../ranges.version.compile.pass.cpp           |  32 +-
 .../version.version.compile.pass.cpp          |  32 +-
 .../range.chunk/adaptor.pass.cpp              |  75 +++
 .../range.adaptors/range.chunk/base.pass.cpp  |  46 ++
 .../range.adaptors/range.chunk/begin.pass.cpp |  67 +++
 .../range.chunk/ctad.compile.pass.cpp         |  55 ++
 .../range.adaptors/range.chunk/end.pass.cpp   |  65 ++
 .../range.chunk/general.pass.cpp              |  48 ++
 .../range.chunk.iter/compare.pass.cpp         | 109 ++++
 .../range.chunk.iter/decrement.pass.cpp       |  60 ++
 .../range.chunk.iter/deref.pass.cpp           |  77 +++
 .../range.chunk.iter/increment.pass.cpp       |  95 +++
 .../ranges/range.adaptors/range.chunk/types.h |  50 ++
 .../generate_feature_test_macro_components.py |   1 -
 25 files changed, 1494 insertions(+), 54 deletions(-)
 create mode 100644 libcxx/include/__ranges/chunk_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/types.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..8e57305f32ad1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -370,7 +370,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_as_rvalue``                             ``202207L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_chunk``                                 *unimplemented*
+    ``__cpp_lib_ranges_chunk``                                 ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                              ``202202L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 0d1a1fbc00f2c..d2a2d1b712689 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -54,6 +54,7 @@ Implemented Papers
 - 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>`__)
+- P2442R1: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github <https://llvm.org/PR171234>`__) (Implemented ``views::slide`` only)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e4c6cb0f51065..8c8ab211e5bf4 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__","Only ``ranges::iota`` is implemented."
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
-"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__",""
+"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__","Only ``views::chunk`` is implemented."
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""
 "","","","","","",""
 "`P0009R18 <https://wg21.link/P0009R18>`__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 <https://github.com/llvm/llvm-project/issues/105189>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index eb423ae923b4f..2426da6848b51 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -707,6 +707,7 @@ set(files
   __ranges/all.h
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
+  __ranges/chunk_view.h
   __ranges/common_view.h
   __ranges/concepts.h
   __ranges/container_compatible_range.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
new file mode 100644
index 0000000000000..f19d2c9ede8a7
--- /dev/null
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -0,0 +1,562 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___RANGES_CHUNK_VIEW_H
+#define _LIBCPP___RANGES_CHUNK_VIEW_H
+
+#include <__algorithm/ranges_min.h>
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/take_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Integral>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) {
+  _Integral __r = __num / __denom;
+  if (__num % __denom)
+    ++__r;
+  return __r;
+}
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view : public view_interface<chunk_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_;
+  _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache<iterator_t<_View>> __current_;
+
+  class __outer_iterator;
+  class __inner_iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+      : __base_(std::move(__base)), __n_(__n), __remainder_(0) {
+    _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0");
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires std::copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator begin() {
+    __current_.__emplace(ranges::begin(__base_));
+    __remainder_ = __n_;
+    return __outer_iterator(*this);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept {
+    return std::default_sentinel;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_));
+  }
+};
+
+template <view _View>
+  requires input_range<_View>
+class chunk_view<_View>::__outer_iterator {
+  friend chunk_view;
+
+  chunk_view* __parent_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(chunk_view& __parent)
+      : __parent_(std::addressof(__parent)) {}
+
+public:
+  class value_type;
+  using iterator_concept = input_iterator_tag;
+  using difference_type  = range_difference_t<_View>;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator(__outer_iterator&&) = default;
+
+  _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default;
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+    _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator.");
+    return value_type(*__parent_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() {
+    ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_));
+    __parent_->__remainder_ = __parent_->__n_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __outer_iterator& __i, default_sentinel_t) {
+    return *__i.__parent_->__current_ == ranges::end(__i.__parent_->__base_) && __i.__parent_->__remainder_ != 0;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(default_sentinel_t, const __outer_iterator& __i)
+    requires sized_sentinel_for<sentinel_t<_View>, iterator_t<_View>>
+  {
+    const auto __dis